Rename QDeclarative symbols to QQuick and QQml
[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 #include "qqmlbinding_p_p.h"
44
45 #include "qqml.h"
46 #include "qqmlcontext.h"
47 #include "qqmlinfo.h"
48 #include "qqmlcompiler_p.h"
49 #include "qqmldata_p.h"
50 #include <private/qqmlprofilerservice_p.h>
51 #include <private/qqmltrace_p.h>
52
53 #include <QVariant>
54 #include <QtCore/qdebug.h>
55
56 QT_BEGIN_NAMESPACE
57
58 QQmlAbstractBinding::QQmlAbstractBinding()
59 : m_prevBinding(0), m_nextBinding(0)
60 {
61 }
62
63 QQmlAbstractBinding::~QQmlAbstractBinding()
64 {
65     Q_ASSERT(m_prevBinding == 0);
66     Q_ASSERT(*m_mePtr == 0);
67 }
68
69 /*!
70 Destroy the binding.  Use this instead of calling delete.
71
72 Bindings are free to implement their own memory management, so the delete operator is not 
73 necessarily safe.  The default implementation clears the binding, removes it from the object
74 and calls delete.
75 */
76 void QQmlAbstractBinding::destroy()
77 {
78     removeFromObject();
79     clear();
80
81     delete this;
82 }
83
84 /*!
85 Add this binding to \a object.
86
87 This transfers ownership of the binding to the object, marks the object's property as
88 being bound.  
89
90 However, it does not enable the binding itself or call update() on it.
91 */
92 void QQmlAbstractBinding::addToObject()
93 {
94     Q_ASSERT(!m_prevBinding);
95
96     QObject *obj = object();
97     Q_ASSERT(obj);
98
99     int index = propertyIndex();
100
101     QQmlData *data = QQmlData::get(obj, true);
102
103     if (index & 0xFF000000) {
104         // Value type
105
106         int coreIndex = index & 0xFFFFFF;
107
108         // Find the value type proxy (if there is one)
109         QQmlValueTypeProxyBinding *proxy = 0;
110         if (data->hasBindingBit(coreIndex)) {
111             QQmlAbstractBinding *b = data->bindings;
112             while (b && b->propertyIndex() != coreIndex)
113                 b = b->m_nextBinding;
114             Q_ASSERT(b && b->bindingType() == QQmlAbstractBinding::ValueTypeProxy);
115             proxy = static_cast<QQmlValueTypeProxyBinding *>(b);
116         }
117
118         if (!proxy) {
119             proxy = new QQmlValueTypeProxyBinding(obj, coreIndex);
120
121             Q_ASSERT(proxy->propertyIndex() == coreIndex);
122             Q_ASSERT(proxy->object() == obj);
123
124             proxy->addToObject();
125         }
126
127         m_nextBinding = proxy->m_bindings;
128         if (m_nextBinding) m_nextBinding->m_prevBinding = &m_nextBinding;
129         m_prevBinding = &proxy->m_bindings;
130         proxy->m_bindings = this;
131
132     } else {
133         m_nextBinding = data->bindings;
134         if (m_nextBinding) m_nextBinding->m_prevBinding = &m_nextBinding;
135         m_prevBinding = &data->bindings;
136         data->bindings = this;
137
138         data->setBindingBit(obj, index);
139     }
140 }
141
142 /*!
143 Remove the binding from the object.
144 */
145 void QQmlAbstractBinding::removeFromObject()
146 {
147     if (m_prevBinding) {
148         int index = propertyIndex();
149
150         *m_prevBinding = m_nextBinding;
151         if (m_nextBinding) m_nextBinding->m_prevBinding = m_prevBinding;
152         m_prevBinding = 0;
153         m_nextBinding = 0;
154
155         if (index & 0xFF000000) {
156             // Value type - we don't remove the proxy from the object.  It will sit their happily
157             // doing nothing until it is removed by a write, a binding change or it is reused
158             // to hold more sub-bindings.
159         } else if (QObject *obj = object()) {
160             QQmlData *data = QQmlData::get(obj, false);
161             if (data) data->clearBindingBit(index);
162         }
163     }
164 }
165
166 static void bindingDummyDeleter(QQmlAbstractBinding *)
167 {
168 }
169
170 QQmlAbstractBinding::Pointer QQmlAbstractBinding::weakPointer()
171 {
172     if (m_mePtr.value().isNull())
173         m_mePtr.value() = QSharedPointer<QQmlAbstractBinding>(this, bindingDummyDeleter);
174
175     return m_mePtr.value().toWeakRef();
176 }
177
178 void QQmlAbstractBinding::clear()
179 {
180     if (!m_mePtr.isNull()) {
181         **m_mePtr = 0;
182         m_mePtr = 0;
183     }
184 }
185
186 void QQmlAbstractBinding::retargetBinding(QObject *, int)
187 {
188     qFatal("QQmlAbstractBinding::retargetBinding() called on illegal binding.");
189 }
190
191 QString QQmlAbstractBinding::expression() const
192 {
193     return QLatin1String("<Unknown>");
194 }
195
196 void QQmlAbstractBinding::setEnabled(bool enabled, QQmlPropertyPrivate::WriteFlags flags)
197 {
198     if (enabled) update(flags);
199 }
200
201 QQmlBinding::Identifier QQmlBinding::Invalid = -1;
202
203 void QQmlBindingPrivate::refresh()
204 {
205     Q_Q(QQmlBinding);
206     q->update();
207 }
208
209 QQmlBindingPrivate::QQmlBindingPrivate()
210 : updating(false), enabled(false), target(), targetProperty(0)
211 {
212 }
213
214 QQmlBindingPrivate::~QQmlBindingPrivate()
215 {
216 }
217
218 QQmlBinding *
219 QQmlBinding::createBinding(Identifier id, QObject *obj, QQmlContext *ctxt,
220                                    const QString &url, int lineNumber, QObject *parent)
221 {
222     if (id < 0)
223         return 0;
224
225     QQmlContextData *ctxtdata = QQmlContextData::get(ctxt);
226
227     QQmlEnginePrivate *engine = QQmlEnginePrivate::get(ctxt->engine());
228     QQmlCompiledData *cdata = 0;
229     QQmlTypeData *typeData = 0;
230     if (engine && ctxtdata && !ctxtdata->url.isEmpty()) {
231         typeData = engine->typeLoader.get(ctxtdata->url);
232         cdata = typeData->compiledData();
233     }
234     QQmlBinding *rv = cdata ? new QQmlBinding(cdata->primitives.at(id), true, obj, ctxtdata, url, lineNumber, 0, parent) : 0;
235     if (cdata)
236         cdata->release();
237     if (typeData)
238         typeData->release();
239     return rv;
240 }
241
242 QQmlBinding::QQmlBinding(const QString &str, QObject *obj, QQmlContext *ctxt, 
243                                          QObject *parent)
244 : QQmlExpression(QQmlContextData::get(ctxt), obj, str, *new QQmlBindingPrivate)
245 {
246     setParent(parent);
247     setNotifyOnValueChanged(true);
248 }
249
250 QQmlBinding::QQmlBinding(const QString &str, QObject *obj, QQmlContextData *ctxt, 
251                                          QObject *parent)
252 : QQmlExpression(ctxt, obj, str, *new QQmlBindingPrivate)
253 {
254     setParent(parent);
255     setNotifyOnValueChanged(true);
256 }
257
258 QQmlBinding::QQmlBinding(const QString &str, bool isRewritten, QObject *obj, 
259                                          QQmlContextData *ctxt, 
260                                          const QString &url, int lineNumber, int columnNumber,
261                                          QObject *parent)
262 : QQmlExpression(ctxt, obj, str, isRewritten, url, lineNumber, columnNumber, *new QQmlBindingPrivate)
263 {
264     setParent(parent);
265     setNotifyOnValueChanged(true);
266 }
267
268 /*!  
269     \internal 
270
271     To avoid exposing v8 in the public API, functionPtr must be a pointer to a v8::Handle<v8::Function>.  
272     For example:
273         v8::Handle<v8::Function> function;
274         new QQmlBinding(&function, scope, ctxt);
275  */
276 QQmlBinding::QQmlBinding(void *functionPtr, QObject *obj, QQmlContextData *ctxt, 
277                                          QObject *parent)
278 : QQmlExpression(ctxt, obj, functionPtr, *new QQmlBindingPrivate)
279 {
280     setParent(parent);
281     setNotifyOnValueChanged(true);
282 }
283
284 QQmlBinding::~QQmlBinding()
285 {
286 }
287
288 void QQmlBinding::setTarget(const QQmlProperty &prop)
289 {
290     Q_D(QQmlBinding);
291     d->property = prop;
292     d->target = d->property.object();
293     d->targetProperty = QQmlPropertyPrivate::get(d->property)->core.encodedIndex();
294
295     update();
296 }
297
298 void QQmlBinding::setTarget(QObject *object,
299                                     const QQmlPropertyData &core,
300                                     QQmlContextData *ctxt)
301 {
302     Q_D(QQmlBinding);
303     d->property = QQmlPropertyPrivate::restore(object, core, ctxt);
304     d->target = d->property.object();
305     d->targetProperty = QQmlPropertyPrivate::get(d->property)->core.encodedIndex();
306
307     update();
308 }
309
310 QQmlProperty QQmlBinding::property() const 
311 {
312    Q_D(const QQmlBinding);
313    return d->property; 
314 }
315
316 void QQmlBinding::setEvaluateFlags(EvaluateFlags flags)
317 {
318     Q_D(QQmlBinding);
319     d->setRequiresThisObject(flags & RequiresThisObject);
320 }
321
322 QQmlBinding::EvaluateFlags QQmlBinding::evaluateFlags() const
323 {
324     Q_D(const QQmlBinding);
325     return d->requiresThisObject()?RequiresThisObject:None;
326 }
327
328 void QQmlBinding::update(QQmlPropertyPrivate::WriteFlags flags)
329 {
330     Q_D(QQmlBinding);
331
332     if (!d->enabled || !d->context() || !d->context()->isValid()) 
333         return;
334
335     QQmlTrace trace("General Binding Update");
336     trace.addDetail("URL", d->url);
337     trace.addDetail("Line", d->line);
338     trace.addDetail("Column", d->columnNumber);
339
340     if (!d->updating) {
341         QQmlBindingProfiler prof(d->url, d->line, d->column);
342         prof.addDetail(expression());
343         d->updating = true;
344
345         QQmlAbstractExpression::DeleteWatcher watcher(d);
346
347         if (d->property.propertyType() == qMetaTypeId<QQmlBinding *>()) {
348
349             int idx = d->property.index();
350             Q_ASSERT(idx != -1);
351
352             QQmlBinding *t = this;
353             int status = -1;
354             void *a[] = { &t, 0, &status, &flags };
355             QMetaObject::metacall(d->property.object(),
356                                   QMetaObject::WriteProperty,
357                                   idx, a);
358
359         } else {
360             QQmlEnginePrivate *ep = QQmlEnginePrivate::get(d->context()->engine);
361             ep->referenceScarceResources(); 
362
363             bool isUndefined = false;
364
365             v8::HandleScope handle_scope;
366             v8::Context::Scope scope(ep->v8engine()->context());
367             v8::Local<v8::Value> result = d->v8value(0, &isUndefined);
368
369             trace.event("writing binding result");
370
371             bool needsErrorData = false;
372             if (!watcher.wasDeleted() && !d->hasError())
373                 needsErrorData = !QQmlPropertyPrivate::writeBinding(d->property, d->context(),
374                                                                             d, result,
375                                                                             isUndefined, flags);
376
377             if (!watcher.wasDeleted()) {
378                
379                 if (needsErrorData) {
380                     QUrl url = QUrl(d->url);
381                     int line = d->line;
382                     if (url.isEmpty()) url = QUrl(QLatin1String("<Unknown File>"));
383
384                     d->delayedError()->error.setUrl(url);
385                     d->delayedError()->error.setLine(line);
386                     d->delayedError()->error.setColumn(-1);
387                 }
388
389                 if (d->hasError()) {
390                     if (!d->delayedError()->addError(ep)) ep->warning(this->error());
391                 } else {
392                     d->clearError();
393                 }
394
395             }
396
397             ep->dereferenceScarceResources(); 
398         }
399
400         if (!watcher.wasDeleted())
401             d->updating = false;
402     } else {
403         QQmlBindingPrivate::printBindingLoopError(d->property);
404     }
405 }
406
407 void QQmlBindingPrivate::printBindingLoopError(QQmlProperty &prop)
408 {
409     qmlInfo(prop.object()) << QQmlBinding::tr("Binding loop detected for property \"%1\"").arg(prop.name());
410 }
411
412 void QQmlBindingPrivate::expressionChanged()
413 {
414     Q_Q(QQmlBinding);
415     q->update();
416 }
417
418 void QQmlBinding::setEnabled(bool e, QQmlPropertyPrivate::WriteFlags flags)
419 {
420     Q_D(QQmlBinding);
421     d->enabled = e;
422     setNotifyOnValueChanged(e);
423
424     if (e) 
425         update(flags);
426 }
427
428 bool QQmlBinding::enabled() const
429 {
430     Q_D(const QQmlBinding);
431
432     return d->enabled;
433 }
434
435 QString QQmlBinding::expression() const
436 {
437     return QQmlExpression::expression();
438 }
439
440 int QQmlBinding::propertyIndex() const
441 {
442     Q_D(const QQmlBinding);
443     return d->targetProperty;
444 }
445
446 QObject *QQmlBinding::object() const
447 {
448     Q_D(const QQmlBinding);
449     return d->target;
450 }
451
452 void QQmlBinding::retargetBinding(QObject *t, int i)
453 {
454     Q_D(QQmlBinding);
455     d->target = t;
456     d->targetProperty = i;
457 }
458
459 QQmlValueTypeProxyBinding::QQmlValueTypeProxyBinding(QObject *o, int index)
460 : m_object(o), m_index(index), m_bindings(0)
461 {
462 }
463
464 QQmlValueTypeProxyBinding::~QQmlValueTypeProxyBinding()
465 {
466     while (m_bindings) {
467         QQmlAbstractBinding *binding = m_bindings;
468         binding->setEnabled(false, 0);
469         binding->destroy();
470     }
471 }
472
473 void QQmlValueTypeProxyBinding::setEnabled(bool e, QQmlPropertyPrivate::WriteFlags flags)
474 {
475     if (e) {
476         QQmlAbstractBinding *bindings = m_bindings;
477         recursiveEnable(bindings, flags);
478     } else {
479         QQmlAbstractBinding *bindings = m_bindings;
480         recursiveDisable(bindings);
481     }
482 }
483
484 void QQmlValueTypeProxyBinding::recursiveEnable(QQmlAbstractBinding *b, QQmlPropertyPrivate::WriteFlags flags)
485 {
486     if (!b)
487         return;
488
489     recursiveEnable(b->m_nextBinding, flags);
490
491     if (b)
492         b->setEnabled(true, flags);
493 }
494
495 void QQmlValueTypeProxyBinding::recursiveDisable(QQmlAbstractBinding *b)
496 {
497     if (!b)
498         return;
499
500     recursiveDisable(b->m_nextBinding);
501
502     if (b)
503         b->setEnabled(false, 0);
504 }
505
506 void QQmlValueTypeProxyBinding::update(QQmlPropertyPrivate::WriteFlags)
507 {
508 }
509
510 QQmlAbstractBinding *QQmlValueTypeProxyBinding::binding(int propertyIndex)
511 {
512     QQmlAbstractBinding *binding = m_bindings;
513     
514     while (binding && binding->propertyIndex() != propertyIndex) 
515         binding = binding->m_nextBinding;
516
517     return binding;
518 }
519
520 /*!
521 Removes a collection of bindings, corresponding to the set bits in \a mask.
522 */
523 void QQmlValueTypeProxyBinding::removeBindings(quint32 mask)
524 {
525     QQmlAbstractBinding *binding = m_bindings;
526     while (binding) {
527         if (mask & (1 << (binding->propertyIndex() >> 24))) {
528             QQmlAbstractBinding *remove = binding;
529             binding = remove->m_nextBinding;
530             *remove->m_prevBinding = remove->m_nextBinding;
531             if (remove->m_nextBinding) remove->m_nextBinding->m_prevBinding = remove->m_prevBinding;
532             remove->m_prevBinding = 0;
533             remove->m_nextBinding = 0;
534             remove->destroy();
535         } else {
536             binding = binding->m_nextBinding;
537         }
538     }
539 }
540
541 int QQmlValueTypeProxyBinding::propertyIndex() const
542 {
543     return m_index;
544 }
545
546 QObject *QQmlValueTypeProxyBinding::object() const
547 {
548     return m_object;
549 }
550
551 QT_END_NAMESPACE