Initial import from the monolithic Qt.
[profile/ivi/qtdeclarative.git] / src / declarative / qml / qdeclarativebinding.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file.  Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23 **
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
30 **
31 **
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "private/qdeclarativebinding_p.h"
43 #include "private/qdeclarativebinding_p_p.h"
44
45 #include "qdeclarative.h"
46 #include "qdeclarativecontext.h"
47 #include "qdeclarativeinfo.h"
48 #include "private/qdeclarativecontext_p.h"
49 #include "private/qdeclarativecompiler_p.h"
50 #include "private/qdeclarativedata_p.h"
51 #include "private/qdeclarativestringconverters_p.h"
52 #include "private/qdeclarativestate_p_p.h"
53 #include "private/qdeclarativedebugtrace_p.h"
54
55 #include <QVariant>
56 #include <QtCore/qdebug.h>
57
58 QT_BEGIN_NAMESPACE
59
60 QDeclarativeAbstractBinding::QDeclarativeAbstractBinding()
61 : m_object(0), m_propertyIndex(-1), m_mePtr(0), m_prevBinding(0), m_nextBinding(0)
62 {
63 }
64
65 QDeclarativeAbstractBinding::~QDeclarativeAbstractBinding()
66 {
67     Q_ASSERT(m_prevBinding == 0);
68     Q_ASSERT(m_mePtr == 0);
69 }
70
71 /*!
72 Destroy the binding.  Use this instead of calling delete.
73
74 Bindings are free to implement their own memory management, so the delete operator is not 
75 necessarily safe.  The default implementation clears the binding, removes it from the object
76 and calls delete.
77 */
78 void QDeclarativeAbstractBinding::destroy()
79 {
80     removeFromObject();
81     clear();
82
83     delete this;
84 }
85
86 /*!
87 Add this binding to \a object.
88
89 This transfers ownership of the binding to the object, marks the object's property as
90 being bound.  
91
92 However, it does not enable the binding itself or call update() on it.
93 */
94 void QDeclarativeAbstractBinding::addToObject(QObject *object, int index)
95 {
96     Q_ASSERT(object);
97
98     if (m_object == object && m_propertyIndex == index)
99         return;
100
101     removeFromObject();
102
103     Q_ASSERT(!m_prevBinding);
104
105     m_object = object;
106     m_propertyIndex = index;
107
108     QDeclarativeData *data = QDeclarativeData::get(object, true);
109
110     if (index & 0xFF000000) {
111         // Value type
112
113         int coreIndex = index & 0xFFFFFF;
114
115         // Find the value type proxy (if there is one)
116         QDeclarativeValueTypeProxyBinding *proxy = 0;
117         if (data->hasBindingBit(coreIndex)) {
118             QDeclarativeAbstractBinding *b = data->bindings;
119             while (b && b->propertyIndex() != coreIndex)
120                 b = b->m_nextBinding;
121             Q_ASSERT(b && b->bindingType() == QDeclarativeAbstractBinding::ValueTypeProxy);
122             proxy = static_cast<QDeclarativeValueTypeProxyBinding *>(b);
123         }
124
125         if (!proxy) {
126             proxy = new QDeclarativeValueTypeProxyBinding(object, coreIndex);
127             proxy->addToObject(object, coreIndex);
128         }
129
130         m_nextBinding = proxy->m_bindings;
131         if (m_nextBinding) m_nextBinding->m_prevBinding = &m_nextBinding;
132         m_prevBinding = &proxy->m_bindings;
133         proxy->m_bindings = this;
134
135     } else {
136         m_nextBinding = data->bindings;
137         if (m_nextBinding) m_nextBinding->m_prevBinding = &m_nextBinding;
138         m_prevBinding = &data->bindings;
139         data->bindings = this;
140
141         data->setBindingBit(m_object, index);
142     }
143 }
144
145 /*!
146 Remove the binding from the object.
147 */
148 void QDeclarativeAbstractBinding::removeFromObject()
149 {
150     if (m_prevBinding) {
151         int index = propertyIndex();
152
153         *m_prevBinding = m_nextBinding;
154         if (m_nextBinding) m_nextBinding->m_prevBinding = m_prevBinding;
155         m_prevBinding = 0;
156         m_nextBinding = 0;
157
158         if (index & 0xFF000000) {
159             // Value type - we don't remove the proxy from the object.  It will sit their happily
160             // doing nothing until it is removed by a write, a binding change or it is reused
161             // to hold more sub-bindings.
162         } else if (m_object) {
163             QDeclarativeData *data = QDeclarativeData::get(m_object, false);
164             if (data) data->clearBindingBit(index);
165         }
166
167         m_object = 0;
168         m_propertyIndex = -1;
169     }
170 }
171
172 static void bindingDummyDeleter(QDeclarativeAbstractBinding *)
173 {
174 }
175
176 QDeclarativeAbstractBinding::Pointer QDeclarativeAbstractBinding::weakPointer()
177 {
178     if (m_selfPointer.isNull())
179         m_selfPointer = QSharedPointer<QDeclarativeAbstractBinding>(this, bindingDummyDeleter);
180
181     return m_selfPointer.toWeakRef();
182 }
183
184 void QDeclarativeAbstractBinding::clear()
185 {
186     if (m_mePtr) {
187         *m_mePtr = 0;
188         m_mePtr = 0;
189     }
190 }
191
192 QString QDeclarativeAbstractBinding::expression() const
193 {
194     return QLatin1String("<Unknown>");
195 }
196
197 QObject *QDeclarativeAbstractBinding::object() const
198 {
199     return m_object;
200 }
201
202 int QDeclarativeAbstractBinding::propertyIndex() const
203 {
204     return m_propertyIndex;
205 }
206
207 void QDeclarativeAbstractBinding::setEnabled(bool enabled, QDeclarativePropertyPrivate::WriteFlags flags)
208 {
209     if (enabled) update(flags);
210 }
211
212 QDeclarativeBinding::Identifier QDeclarativeBinding::Invalid = -1;
213
214 void QDeclarativeBindingPrivate::refresh()
215 {
216     Q_Q(QDeclarativeBinding);
217     q->update();
218 }
219
220 QDeclarativeBindingPrivate::QDeclarativeBindingPrivate()
221 : updating(false), enabled(false), deleted(0)
222 {
223 }
224
225 QDeclarativeBindingPrivate::~QDeclarativeBindingPrivate()
226 {
227     if (deleted) *deleted = true;
228 }
229
230 QDeclarativeBinding::QDeclarativeBinding(void *data, QDeclarativeRefCount *rc, QObject *obj, 
231                                          QDeclarativeContextData *ctxt, const QString &url, int lineNumber, 
232                                          QObject *parent)
233 : QDeclarativeExpression(ctxt, data, rc, obj, url, lineNumber, *new QDeclarativeBindingPrivate)
234 {
235     setParent(parent);
236     setNotifyOnValueChanged(true);
237 }
238
239 QDeclarativeBinding *
240 QDeclarativeBinding::createBinding(Identifier id, QObject *obj, QDeclarativeContext *ctxt,
241                                    const QString &url, int lineNumber, QObject *parent)
242 {
243     if (id < 0)
244         return 0;
245
246     QDeclarativeContextData *ctxtdata = QDeclarativeContextData::get(ctxt);
247
248     QDeclarativeEnginePrivate *engine = QDeclarativeEnginePrivate::get(qmlEngine(obj));
249     QDeclarativeCompiledData *cdata = 0;
250     QDeclarativeTypeData *typeData = 0;
251     if (engine && ctxtdata && !ctxtdata->url.isEmpty()) {
252         typeData = engine->typeLoader.get(ctxtdata->url);
253         cdata = typeData->compiledData();
254     }
255     QDeclarativeBinding *rv = cdata ? new QDeclarativeBinding((void*)cdata->datas.at(id).constData(), cdata, obj, ctxtdata, url, lineNumber, parent) : 0;
256     if (typeData)
257         typeData->release();
258     return rv;
259 }
260
261 QDeclarativeBinding::QDeclarativeBinding(const QString &str, QObject *obj, QDeclarativeContext *ctxt, 
262                                          QObject *parent)
263 : QDeclarativeExpression(QDeclarativeContextData::get(ctxt), obj, str, *new QDeclarativeBindingPrivate)
264 {
265     setParent(parent);
266     setNotifyOnValueChanged(true);
267 }
268
269 QDeclarativeBinding::QDeclarativeBinding(const QString &str, QObject *obj, QDeclarativeContextData *ctxt, 
270                                          QObject *parent)
271 : QDeclarativeExpression(ctxt, obj, str, *new QDeclarativeBindingPrivate)
272 {
273     setParent(parent);
274     setNotifyOnValueChanged(true);
275 }
276
277 QDeclarativeBinding::QDeclarativeBinding(const QScriptValue &func, QObject *obj, QDeclarativeContextData *ctxt, QObject *parent)
278 : QDeclarativeExpression(ctxt, obj, func, *new QDeclarativeBindingPrivate)
279 {
280     setParent(parent);
281     setNotifyOnValueChanged(true);
282 }
283
284 QDeclarativeBinding::~QDeclarativeBinding()
285 {
286 }
287
288 void QDeclarativeBinding::setTarget(const QDeclarativeProperty &prop)
289 {
290     Q_D(QDeclarativeBinding);
291     d->property = prop;
292
293     update();
294 }
295
296 QDeclarativeProperty QDeclarativeBinding::property() const 
297 {
298    Q_D(const QDeclarativeBinding);
299    return d->property; 
300 }
301
302 void QDeclarativeBinding::setEvaluateFlags(EvaluateFlags flags)
303 {
304     Q_D(QDeclarativeBinding);
305     d->setEvaluateFlags(QDeclarativeQtScriptExpression::EvaluateFlags(static_cast<int>(flags)));
306 }
307
308 QDeclarativeBinding::EvaluateFlags QDeclarativeBinding::evaluateFlags() const
309 {
310     Q_D(const QDeclarativeBinding);
311     return QDeclarativeBinding::EvaluateFlags(static_cast<int>(d->evaluateFlags()));
312 }
313
314
315 class QDeclarativeBindingProfiler {
316 public:
317     QDeclarativeBindingProfiler(QDeclarativeBinding *bind)
318     {
319         QDeclarativeDebugTrace::startRange(QDeclarativeDebugTrace::Binding);
320         QDeclarativeDebugTrace::rangeData(QDeclarativeDebugTrace::Binding, bind->expression());
321         QDeclarativeDebugTrace::rangeLocation(QDeclarativeDebugTrace::Binding, bind->sourceFile(), bind->lineNumber());
322     }
323
324     ~QDeclarativeBindingProfiler()
325     {
326         QDeclarativeDebugTrace::endRange(QDeclarativeDebugTrace::Binding);
327     }
328 };
329
330 void QDeclarativeBinding::update(QDeclarativePropertyPrivate::WriteFlags flags)
331 {
332     Q_D(QDeclarativeBinding);
333
334     if (!d->enabled || !d->context() || !d->context()->isValid()) 
335         return;
336
337     if (!d->updating) {
338         QDeclarativeBindingProfiler prof(this);
339         d->updating = true;
340         bool wasDeleted = false;
341         d->deleted = &wasDeleted;
342
343         if (d->property.propertyType() == qMetaTypeId<QDeclarativeBinding *>()) {
344
345             int idx = d->property.index();
346             Q_ASSERT(idx != -1);
347
348             QDeclarativeBinding *t = this;
349             int status = -1;
350             void *a[] = { &t, 0, &status, &flags };
351             QMetaObject::metacall(d->property.object(),
352                                   QMetaObject::WriteProperty,
353                                   idx, a);
354
355             if (wasDeleted)
356                 return;
357
358         } else {
359             QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(d->context()->engine);
360
361             bool isUndefined = false;
362             QVariant value;
363
364             QScriptValue scriptValue = d->scriptValue(0, &isUndefined);
365             if (wasDeleted)
366                 return;
367
368             if (d->property.propertyTypeCategory() == QDeclarativeProperty::List) {
369                 value = ep->scriptValueToVariant(scriptValue, qMetaTypeId<QList<QObject *> >());
370             } else if (scriptValue.isNull() && 
371                        d->property.propertyTypeCategory() == QDeclarativeProperty::Object) {
372                 value = QVariant::fromValue((QObject *)0);
373             } else {
374                 value = ep->scriptValueToVariant(scriptValue, d->property.propertyType());
375                 if (value.userType() == QMetaType::QObjectStar && !qvariant_cast<QObject*>(value)) {
376                     // If the object is null, we extract the predicted type.  While this isn't
377                     // 100% reliable, in many cases it gives us better error messages if we
378                     // assign this null-object to an incompatible property
379                     int type = ep->objectClass->objectType(scriptValue);
380                     QObject *o = 0;
381                     value = QVariant(type, (void *)&o);
382                 }
383             }
384
385
386             if (d->error.isValid()) {
387
388             } else if (isUndefined && d->property.isResettable()) {
389
390                 d->property.reset();
391
392             } else if (isUndefined && d->property.propertyType() == qMetaTypeId<QVariant>()) {
393
394                 QDeclarativePropertyPrivate::write(d->property, QVariant(), flags);
395
396             } else if (isUndefined) {
397
398                 QUrl url = QUrl(d->url);
399                 int line = d->line;
400                 if (url.isEmpty()) url = QUrl(QLatin1String("<Unknown File>"));
401
402                 d->error.setUrl(url);
403                 d->error.setLine(line);
404                 d->error.setColumn(-1);
405                 d->error.setDescription(QLatin1String("Unable to assign [undefined] to ") +
406                                         QLatin1String(QMetaType::typeName(d->property.propertyType())) +
407                                         QLatin1String(" ") + d->property.name());
408
409             } else if (!scriptValue.isRegExp() && scriptValue.isFunction()) {
410
411                 QUrl url = QUrl(d->url);
412                 int line = d->line;
413                 if (url.isEmpty()) url = QUrl(QLatin1String("<Unknown File>"));
414
415                 d->error.setUrl(url);
416                 d->error.setLine(line);
417                 d->error.setColumn(-1);
418                 d->error.setDescription(QLatin1String("Unable to assign a function to a property."));
419
420             } else if (d->property.object() &&
421                        !QDeclarativePropertyPrivate::write(d->property, value, flags)) {
422
423                 if (wasDeleted)
424                     return;
425
426                 QUrl url = QUrl(d->url);
427                 int line = d->line;
428                 if (url.isEmpty()) url = QUrl(QLatin1String("<Unknown File>"));
429
430                 const char *valueType = 0;
431                 if (value.userType() == QVariant::Invalid) valueType = "null";
432                 else valueType = QMetaType::typeName(value.userType());
433
434                 d->error.setUrl(url);
435                 d->error.setLine(line);
436                 d->error.setColumn(-1);
437                 d->error.setDescription(QLatin1String("Unable to assign ") +
438                                         QLatin1String(valueType) +
439                                         QLatin1String(" to ") +
440                                         QLatin1String(QMetaType::typeName(d->property.propertyType())));
441             }
442
443             if (wasDeleted)
444                 return;
445
446             if (d->error.isValid()) {
447                if (!d->addError(ep)) ep->warning(this->error());
448             } else {
449                 d->removeError();
450             }
451         }
452
453         d->updating = false;
454         d->deleted = 0;
455     } else {
456         qmlInfo(d->property.object()) << tr("Binding loop detected for property \"%1\"").arg(d->property.name());
457     }
458 }
459
460 void QDeclarativeBindingPrivate::emitValueChanged()
461 {
462     Q_Q(QDeclarativeBinding);
463     q->update();
464 }
465
466 void QDeclarativeBinding::setEnabled(bool e, QDeclarativePropertyPrivate::WriteFlags flags)
467 {
468     Q_D(QDeclarativeBinding);
469     d->enabled = e;
470     setNotifyOnValueChanged(e);
471
472     if (e) 
473         update(flags);
474 }
475
476 bool QDeclarativeBinding::enabled() const
477 {
478     Q_D(const QDeclarativeBinding);
479
480     return d->enabled;
481 }
482
483 QString QDeclarativeBinding::expression() const
484 {
485     return QDeclarativeExpression::expression();
486 }
487
488 QDeclarativeValueTypeProxyBinding::QDeclarativeValueTypeProxyBinding(QObject *o, int index)
489 : m_object(o), m_index(index), m_bindings(0)
490 {
491 }
492
493 QDeclarativeValueTypeProxyBinding::~QDeclarativeValueTypeProxyBinding()
494 {
495     while (m_bindings) {
496         QDeclarativeAbstractBinding *binding = m_bindings;
497         binding->setEnabled(false, 0);
498         binding->destroy();
499     }
500 }
501
502 void QDeclarativeValueTypeProxyBinding::setEnabled(bool e, QDeclarativePropertyPrivate::WriteFlags flags)
503 {
504     if (e) {
505         QDeclarativeAbstractBinding *bindings = m_bindings;
506         recursiveEnable(bindings, flags);
507     } else {
508         QDeclarativeAbstractBinding *bindings = m_bindings;
509         recursiveDisable(bindings);
510     }
511 }
512
513 void QDeclarativeValueTypeProxyBinding::recursiveEnable(QDeclarativeAbstractBinding *b, QDeclarativePropertyPrivate::WriteFlags flags)
514 {
515     if (!b)
516         return;
517
518     recursiveEnable(b->m_nextBinding, flags);
519
520     if (b)
521         b->setEnabled(true, flags);
522 }
523
524 void QDeclarativeValueTypeProxyBinding::recursiveDisable(QDeclarativeAbstractBinding *b)
525 {
526     if (!b)
527         return;
528
529     recursiveDisable(b->m_nextBinding);
530
531     if (b)
532         b->setEnabled(false, 0);
533 }
534
535 void QDeclarativeValueTypeProxyBinding::update(QDeclarativePropertyPrivate::WriteFlags)
536 {
537 }
538
539 QDeclarativeAbstractBinding *QDeclarativeValueTypeProxyBinding::binding(int propertyIndex)
540 {
541     QDeclarativeAbstractBinding *binding = m_bindings;
542     
543     while (binding && binding->propertyIndex() != propertyIndex) 
544         binding = binding->m_nextBinding;
545
546     return binding;
547 }
548
549 /*!
550 Removes a collection of bindings, corresponding to the set bits in \a mask.
551 */
552 void QDeclarativeValueTypeProxyBinding::removeBindings(quint32 mask)
553 {
554     QDeclarativeAbstractBinding *binding = m_bindings;
555     while (binding) {
556         if (mask & (1 << (binding->propertyIndex() >> 24))) {
557             QDeclarativeAbstractBinding *remove = binding;
558             binding = remove->m_nextBinding;
559             *remove->m_prevBinding = remove->m_nextBinding;
560             if (remove->m_nextBinding) remove->m_nextBinding->m_prevBinding = remove->m_prevBinding;
561             remove->m_prevBinding = 0;
562             remove->m_nextBinding = 0;
563             remove->destroy();
564         } else {
565             binding = binding->m_nextBinding;
566         }
567     }
568 }
569
570 QT_END_NAMESPACE