1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qdeclarativecontext.h"
43 #include "qdeclarativecontext_p.h"
44 #include "qdeclarativecomponentattached_p.h"
46 #include "qdeclarativecomponent_p.h"
47 #include "qdeclarativeexpression_p.h"
48 #include "qdeclarativeengine_p.h"
49 #include "qdeclarativeengine.h"
50 #include "qdeclarativeinfo.h"
51 #include <private/qv4bindings_p.h>
52 #include <private/qv8bindings_p.h>
54 #include <qjsengine.h>
55 #include <QtCore/qvarlengtharray.h>
56 #include <QtCore/qdebug.h>
60 QDeclarativeContextPrivate::QDeclarativeContextPrivate()
61 : data(0), notifyIndex(-1)
66 \class QDeclarativeContext
68 \brief The QDeclarativeContext class defines a context within a QML engine.
71 Contexts allow data to be exposed to the QML components instantiated by the
74 Each QDeclarativeContext contains a set of properties, distinct from its QObject
75 properties, that allow data to be explicitly bound to a context by name. The
76 context properties are defined and updated by calling
77 QDeclarativeContext::setContextProperty(). The following example shows a Qt model
78 being bound to a context and then accessed from a QML file.
81 QDeclarativeEngine engine;
82 QStringListModel modelData;
83 QDeclarativeContext *context = new QDeclarativeContext(engine.rootContext());
84 context->setContextProperty("myModel", &modelData);
86 QDeclarativeComponent component(&engine);
87 component.setData("import QtQuick 1.0\nListView { model: myModel }", QUrl());
88 QObject *window = component.create(context);
91 Note it is the responsibility of the creator to delete any QDeclarativeContext it
92 constructs. If the \c context object in the example is no longer needed when the
93 \c window component instance is destroyed, the \c context must be destroyed explicitly.
94 The simplest way to ensure this is to set \c window as the parent of \c context.
96 To simplify binding and maintaining larger data sets, a context object can be set
97 on a QDeclarativeContext. All the properties of the context object are available
98 by name in the context, as though they were all individually added through calls
99 to QDeclarativeContext::setContextProperty(). Changes to the property's values are
100 detected through the property's notify signal. Setting a context object is both
101 faster and easier than manually adding and maintaing context property values.
103 The following example has the same effect as the previous one, but it uses a context
107 class MyDataSet : ... {
109 Q_PROPERTY(QAbstractItemModel *myModel READ model NOTIFY modelChanged)
114 QDeclarativeEngine engine;
115 QDeclarativeContext *context = new QDeclarativeContext(engine.rootContext());
116 context->setContextObject(&myDataSet);
118 QDeclarativeComponent component(&engine);
119 component.setData("import QtQuick 1.0\nListView { model: myModel }", QUrl());
120 component.create(context);
123 All properties added explicitly by QDeclarativeContext::setContextProperty() take
124 precedence over the context object's properties.
126 \section2 The Context Hierarchy
128 Contexts form a hierarchy. The root of this hierarchy is the QML engine's
129 \l {QDeclarativeEngine::rootContext()}{root context}. Child contexts inherit
130 the context properties of their parents; if a child context sets a context property
131 that already exists in its parent, the new context property overrides that of the
134 The following example defines two contexts - \c context1 and \c context2. The
135 second context overrides the "b" context property inherited from the first with a
139 QDeclarativeEngine engine;
140 QDeclarativeContext *context1 = new QDeclarativeContext(engine.rootContext());
141 QDeclarativeContext *context2 = new QDeclarativeContext(context1);
143 context1->setContextProperty("a", 12);
144 context1->setContextProperty("b", 12);
146 context2->setContextProperty("b", 15);
149 While QML objects instantiated in a context are not strictly owned by that
150 context, their bindings are. If a context is destroyed, the property bindings of
151 outstanding QML objects will stop evaluating.
153 \warning Setting the context object or adding new context properties after an object
154 has been created in that context is an expensive operation (essentially forcing all bindings
155 to reevaluate). Thus whenever possible you should complete "setup" of the context
156 before using it to create any objects.
158 \sa {Using QML Bindings in C++ Applications}
162 QDeclarativeContext::QDeclarativeContext(QDeclarativeEngine *e, bool)
163 : QObject(*(new QDeclarativeContextPrivate))
165 Q_D(QDeclarativeContext);
166 d->data = new QDeclarativeContextData(this);
172 Create a new QDeclarativeContext as a child of \a engine's root context, and the
175 QDeclarativeContext::QDeclarativeContext(QDeclarativeEngine *engine, QObject *parent)
176 : QObject(*(new QDeclarativeContextPrivate), parent)
178 Q_D(QDeclarativeContext);
179 d->data = new QDeclarativeContextData(this);
181 d->data->setParent(engine?QDeclarativeContextData::get(engine->rootContext()):0);
185 Create a new QDeclarativeContext with the given \a parentContext, and the
188 QDeclarativeContext::QDeclarativeContext(QDeclarativeContext *parentContext, QObject *parent)
189 : QObject(*(new QDeclarativeContextPrivate), parent)
191 Q_D(QDeclarativeContext);
192 d->data = new QDeclarativeContextData(this);
194 d->data->setParent(parentContext?QDeclarativeContextData::get(parentContext):0);
200 QDeclarativeContext::QDeclarativeContext(QDeclarativeContextData *data)
201 : QObject(*(new QDeclarativeContextPrivate), 0)
203 Q_D(QDeclarativeContext);
208 Destroys the QDeclarativeContext.
210 Any expressions, or sub-contexts dependent on this context will be
211 invalidated, but not destroyed (unless they are parented to the QDeclarativeContext
214 QDeclarativeContext::~QDeclarativeContext()
216 Q_D(QDeclarativeContext);
218 if (!d->data->isInternal)
223 Returns whether the context is valid.
225 To be valid, a context must have a engine, and it's contextObject(), if any,
226 must not have been deleted.
228 bool QDeclarativeContext::isValid() const
230 Q_D(const QDeclarativeContext);
231 return d->data && d->data->isValid();
235 Return the context's QDeclarativeEngine, or 0 if the context has no QDeclarativeEngine or the
236 QDeclarativeEngine was destroyed.
238 QDeclarativeEngine *QDeclarativeContext::engine() const
240 Q_D(const QDeclarativeContext);
241 return d->data->engine;
245 Return the context's parent QDeclarativeContext, or 0 if this context has no
246 parent or if the parent has been destroyed.
248 QDeclarativeContext *QDeclarativeContext::parentContext() const
250 Q_D(const QDeclarativeContext);
251 return d->data->parent?d->data->parent->asQDeclarativeContext():0;
255 Return the context object, or 0 if there is no context object.
257 QObject *QDeclarativeContext::contextObject() const
259 Q_D(const QDeclarativeContext);
260 return d->data->contextObject;
264 Set the context \a object.
266 void QDeclarativeContext::setContextObject(QObject *object)
268 Q_D(QDeclarativeContext);
270 QDeclarativeContextData *data = d->data;
272 if (data->isInternal) {
273 qWarning("QDeclarativeContext: Cannot set context object for internal context.");
278 qWarning("QDeclarativeContext: Cannot set context object on invalid context.");
282 data->contextObject = object;
286 Set a the \a value of the \a name property on this context.
288 void QDeclarativeContext::setContextProperty(const QString &name, const QVariant &value)
290 Q_D(QDeclarativeContext);
291 if (d->notifyIndex == -1)
292 d->notifyIndex = this->metaObject()->methodCount();
294 QDeclarativeContextData *data = d->data;
296 if (data->isInternal) {
297 qWarning("QDeclarativeContext: Cannot set property on internal context.");
302 qWarning("QDeclarativeContext: Cannot set property on invalid context.");
308 QObject *o = QDeclarativeEnginePrivate::get(data->engine)->toQObject(value, &ok);
310 setContextProperty(name, o);
315 if (!data->propertyNames) data->propertyNames = new QDeclarativeIntegerCache();
317 int idx = data->propertyNames->value(name);
319 data->propertyNames->add(name, data->idValueCount + d->propertyValues.count());
320 d->propertyValues.append(value);
322 data->refreshExpressions();
324 d->propertyValues[idx] = value;
325 QMetaObject::activate(this, idx + d->notifyIndex, 0);
330 Set the \a value of the \a name property on this context.
332 QDeclarativeContext does \bold not take ownership of \a value.
334 void QDeclarativeContext::setContextProperty(const QString &name, QObject *value)
336 Q_D(QDeclarativeContext);
337 if (d->notifyIndex == -1)
338 d->notifyIndex = this->metaObject()->methodCount();
340 QDeclarativeContextData *data = d->data;
342 if (data->isInternal) {
343 qWarning("QDeclarativeContext: Cannot set property on internal context.");
348 qWarning("QDeclarativeContext: Cannot set property on invalid context.");
352 if (!data->propertyNames) data->propertyNames = new QDeclarativeIntegerCache();
353 int idx = data->propertyNames->value(name);
356 data->propertyNames->add(name, data->idValueCount + d->propertyValues.count());
357 d->propertyValues.append(QVariant::fromValue(value));
359 data->refreshExpressions();
361 d->propertyValues[idx] = QVariant::fromValue(value);
362 QMetaObject::activate(this, idx + d->notifyIndex, 0);
367 Returns the value of the \a name property for this context
370 QVariant QDeclarativeContext::contextProperty(const QString &name) const
372 Q_D(const QDeclarativeContext);
376 QDeclarativeContextData *data = d->data;
378 if (data->propertyNames)
379 idx = data->propertyNames->value(name);
382 QByteArray utf8Name = name.toUtf8();
383 if (data->contextObject) {
384 QObject *obj = data->contextObject;
385 QDeclarativePropertyData local;
386 QDeclarativePropertyData *property =
387 QDeclarativePropertyCache::property(data->engine, obj, name, local);
389 if (property) value = obj->metaObject()->property(property->coreIndex).read(obj);
391 if (!value.isValid() && parentContext())
392 value = parentContext()->contextProperty(name);
394 if (idx >= d->propertyValues.count())
395 value = QVariant::fromValue(data->idValues[idx - d->propertyValues.count()].data());
397 value = d->propertyValues[idx];
404 Returns the name of \a object in this context, or an empty string if \a object
405 is not named in the context. Objects are named by setContextProperty(), or by ids in
406 the case of QML created contexts.
408 If the object has multiple names, the first is returned.
410 QString QDeclarativeContext::nameForObject(QObject *object) const
412 Q_D(const QDeclarativeContext);
414 return d->data->findObjectId(object);
418 Resolves the URL \a src relative to the URL of the
419 containing component.
421 \sa QDeclarativeEngine::baseUrl(), setBaseUrl()
423 QUrl QDeclarativeContext::resolvedUrl(const QUrl &src)
425 Q_D(QDeclarativeContext);
426 return d->data->resolvedUrl(src);
429 QUrl QDeclarativeContextData::resolvedUrl(const QUrl &src)
431 QDeclarativeContextData *ctxt = this;
433 if (src.isRelative() && !src.isEmpty()) {
436 if(ctxt->url.isValid())
443 return ctxt->url.resolved(src);
445 return engine->baseUrl().resolved(src);
455 Explicitly sets the url resolvedUrl() will use for relative references to \a baseUrl.
457 Calling this function will override the url of the containing
458 component used by default.
462 void QDeclarativeContext::setBaseUrl(const QUrl &baseUrl)
464 Q_D(QDeclarativeContext);
466 d->data->url = baseUrl;
470 Returns the base url of the component, or the containing component
473 QUrl QDeclarativeContext::baseUrl() const
475 Q_D(const QDeclarativeContext);
476 const QDeclarativeContextData* data = d->data;
477 while (data && data->url.isEmpty())
486 int QDeclarativeContextPrivate::context_count(QDeclarativeListProperty<QObject> *prop)
488 QDeclarativeContext *context = static_cast<QDeclarativeContext*>(prop->object);
489 QDeclarativeContextPrivate *d = QDeclarativeContextPrivate::get(context);
490 int contextProperty = (int)(quintptr)prop->data;
492 if (d->propertyValues.at(contextProperty).userType() != qMetaTypeId<QList<QObject*> >()) {
495 return ((const QList<QObject> *)d->propertyValues.at(contextProperty).constData())->count();
499 QObject *QDeclarativeContextPrivate::context_at(QDeclarativeListProperty<QObject> *prop, int index)
501 QDeclarativeContext *context = static_cast<QDeclarativeContext*>(prop->object);
502 QDeclarativeContextPrivate *d = QDeclarativeContextPrivate::get(context);
503 int contextProperty = (int)(quintptr)prop->data;
505 if (d->propertyValues.at(contextProperty).userType() != qMetaTypeId<QList<QObject*> >()) {
508 return ((const QList<QObject*> *)d->propertyValues.at(contextProperty).constData())->at(index);
513 QDeclarativeContextData::QDeclarativeContextData()
514 : parent(0), engine(0), isInternal(false), ownedByParent(false), isJSContext(false),
515 isPragmaLibraryContext(false), unresolvedNames(false), publicContext(0), activeVMEData(0),
516 propertyNames(0), contextObject(0), imports(0), childContexts(0), nextChild(0), prevChild(0),
517 expressions(0), contextObjects(0), contextGuards(0), idValues(0), idValueCount(0), linkedContext(0),
518 componentAttached(0), v4bindings(0), v8bindings(0)
522 QDeclarativeContextData::QDeclarativeContextData(QDeclarativeContext *ctxt)
523 : parent(0), engine(0), isInternal(false), ownedByParent(false), isJSContext(false),
524 isPragmaLibraryContext(false), unresolvedNames(false), publicContext(ctxt), activeVMEData(0),
525 propertyNames(0), contextObject(0), imports(0), childContexts(0), nextChild(0), prevChild(0),
526 expressions(0), contextObjects(0), contextGuards(0), idValues(0), idValueCount(0), linkedContext(0),
527 componentAttached(0), v4bindings(0), v8bindings(0)
531 void QDeclarativeContextData::invalidate()
533 while (componentAttached) {
534 QDeclarativeComponentAttached *a = componentAttached;
535 componentAttached = a->next;
536 if (componentAttached) componentAttached->prev = &componentAttached;
541 emit a->destruction();
544 while (childContexts) {
545 if (childContexts->ownedByParent) {
546 childContexts->destroy();
548 childContexts->invalidate();
553 *prevChild = nextChild;
554 if (nextChild) nextChild->prevChild = prevChild;
563 void QDeclarativeContextData::clearContext()
566 while (componentAttached) {
567 QDeclarativeComponentAttached *a = componentAttached;
568 componentAttached = a->next;
569 if (componentAttached) componentAttached->prev = &componentAttached;
574 emit a->destruction();
578 QDeclarativeAbstractExpression *expression = expressions;
580 QDeclarativeAbstractExpression *nextExpression = expression->m_nextExpression;
582 expression->m_context = 0;
583 expression->m_prevExpression = 0;
584 expression->m_nextExpression = 0;
586 expression = nextExpression;
591 void QDeclarativeContextData::destroy()
594 linkedContext->destroy();
596 if (engine) invalidate();
600 while (contextObjects) {
601 QDeclarativeData *co = contextObjects;
602 contextObjects = contextObjects->nextContextObject;
605 co->outerContext = 0;
606 co->nextContextObject = 0;
607 co->prevContextObject = 0;
610 QDeclarativeGuardedContextData *contextGuard = contextGuards;
611 while (contextGuard) {
612 QDeclarativeGuardedContextData *next = contextGuard->m_next;
613 contextGuard->m_next = 0;
614 contextGuard->m_prev = 0;
615 contextGuard->m_contextData = 0;
621 propertyNames->release();
627 v4bindings->release();
630 v8bindings->release();
632 for (int ii = 0; ii < importedScripts.count(); ++ii) {
633 qPersistentDispose(importedScripts[ii]);
639 delete publicContext;
644 void QDeclarativeContextData::setParent(QDeclarativeContextData *p, bool parentTakesOwnership)
649 nextChild = p->childContexts;
650 if (nextChild) nextChild->prevChild = &nextChild;
651 prevChild = &p->childContexts;
652 p->childContexts = this;
653 ownedByParent = parentTakesOwnership;
657 void QDeclarativeContextData::refreshExpressionsRecursive(QDeclarativeAbstractExpression *expression)
659 QDeleteWatcher w(expression);
661 if (expression->m_nextExpression)
662 refreshExpressionsRecursive(expression->m_nextExpression);
665 expression->refresh();
668 static inline bool expressions_to_run(QDeclarativeContextData *ctxt, bool isGlobalRefresh)
670 return ctxt->expressions && (!isGlobalRefresh || ctxt->unresolvedNames);
673 void QDeclarativeContextData::refreshExpressionsRecursive(bool isGlobal)
675 // For efficiency, we try and minimize the number of guards we have to create
676 if (expressions_to_run(this, isGlobal) && (nextChild || childContexts)) {
677 QDeclarativeGuardedContextData guard(this);
680 childContexts->refreshExpressionsRecursive(isGlobal);
682 if (guard.isNull()) return;
685 nextChild->refreshExpressionsRecursive(isGlobal);
687 if (guard.isNull()) return;
689 if (expressions_to_run(this, isGlobal))
690 refreshExpressionsRecursive(expressions);
692 } else if (expressions_to_run(this, isGlobal)) {
694 refreshExpressionsRecursive(expressions);
696 } else if (nextChild && childContexts) {
698 QDeclarativeGuardedContextData guard(this);
700 childContexts->refreshExpressionsRecursive(isGlobal);
702 if (!guard.isNull() && nextChild)
703 nextChild->refreshExpressionsRecursive(isGlobal);
705 } else if (nextChild) {
707 nextChild->refreshExpressionsRecursive(isGlobal);
709 } else if (childContexts) {
711 childContexts->refreshExpressionsRecursive(isGlobal);
716 // Refreshes all expressions that could possibly depend on this context. Refreshing flushes all
717 // context-tree dependent caches in the expressions, and should occur every time the context tree
718 // *structure* (not values) changes.
719 void QDeclarativeContextData::refreshExpressions()
721 bool isGlobal = (parent == 0);
723 // For efficiency, we try and minimize the number of guards we have to create
724 if (expressions_to_run(this, isGlobal) && childContexts) {
725 QDeclarativeGuardedContextData guard(this);
727 childContexts->refreshExpressionsRecursive(isGlobal);
729 if (!guard.isNull() && expressions_to_run(this, isGlobal))
730 refreshExpressionsRecursive(expressions);
732 } else if (expressions_to_run(this, isGlobal)) {
734 refreshExpressionsRecursive(expressions);
736 } else if (childContexts) {
738 childContexts->refreshExpressionsRecursive(isGlobal);
743 void QDeclarativeContextData::addObject(QObject *o)
745 QDeclarativeData *data = QDeclarativeData::get(o, true);
747 Q_ASSERT(data->context == 0);
749 data->context = this;
750 data->outerContext = this;
752 data->nextContextObject = contextObjects;
753 if (data->nextContextObject)
754 data->nextContextObject->prevContextObject = &data->nextContextObject;
755 data->prevContextObject = &contextObjects;
756 contextObjects = data;
759 void QDeclarativeContextData::setIdProperty(int idx, QObject *obj)
762 idValues[idx].context = this;
765 void QDeclarativeContextData::setIdPropertyData(QDeclarativeIntegerCache *data)
767 Q_ASSERT(!propertyNames);
768 propertyNames = data;
769 propertyNames->addref();
771 idValueCount = data->count();
772 idValues = new ContextGuard[idValueCount];
775 QString QDeclarativeContextData::findObjectId(const QObject *obj) const
780 for (int ii = 0; ii < idValueCount; ii++) {
781 if (idValues[ii] == obj)
782 return propertyNames->findId(ii);
786 QDeclarativeContextPrivate *p = QDeclarativeContextPrivate::get(publicContext);
787 for (int ii = 0; ii < p->propertyValues.count(); ++ii)
788 if (p->propertyValues.at(ii) == QVariant::fromValue((QObject *)obj))
789 return propertyNames->findId(ii);
793 return linkedContext->findObjectId(obj);
797 QDeclarativeContext *QDeclarativeContextData::asQDeclarativeContext()
800 publicContext = new QDeclarativeContext(this);
801 return publicContext;
804 QDeclarativeContextPrivate *QDeclarativeContextData::asQDeclarativeContextPrivate()
806 return QDeclarativeContextPrivate::get(asQDeclarativeContext());