1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtQml module of the Qt Toolkit.
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.
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.
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.
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.
40 ****************************************************************************/
42 #include "qqmlcontext.h"
43 #include "qqmlcontext_p.h"
44 #include "qqmlcomponentattached_p.h"
46 #include "qqmlcomponent_p.h"
47 #include "qqmlexpression_p.h"
48 #include "qqmlengine_p.h"
49 #include "qqmlengine.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 QQmlContextPrivate::QQmlContextPrivate()
61 : data(0), notifyIndex(-1)
68 \brief The QQmlContext class defines a context within a QML engine.
71 Contexts allow data to be exposed to the QML components instantiated by the
74 Each QQmlContext 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 QQmlContext::setContextProperty(). The following example shows a Qt model
78 being bound to a context and then accessed from a QML file.
82 QStringListModel modelData;
83 QQmlContext *context = new QQmlContext(engine.rootContext());
84 context->setContextProperty("myModel", &modelData);
86 QQmlComponent component(&engine);
87 component.setData("import QtQuick 2.0\nListView { model: myModel }", QUrl());
88 QObject *window = component.create(context);
91 Note it is the responsibility of the creator to delete any QQmlContext 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 QQmlContext. 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 QQmlContext::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)
115 QQmlContext *context = new QQmlContext(engine.rootContext());
116 context->setContextObject(&myDataSet);
118 QQmlComponent component(&engine);
119 component.setData("import QtQuick 2.0\nListView { model: myModel }", QUrl());
120 component.create(context);
123 All properties added explicitly by QQmlContext::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 {QQmlEngine::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
140 QQmlContext *context1 = new QQmlContext(engine.rootContext());
141 QQmlContext *context2 = new QQmlContext(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 QQmlContext::QQmlContext(QQmlEngine *e, bool)
163 : QObject(*(new QQmlContextPrivate))
166 d->data = new QQmlContextData(this);
172 Create a new QQmlContext as a child of \a engine's root context, and the
175 QQmlContext::QQmlContext(QQmlEngine *engine, QObject *parent)
176 : QObject(*(new QQmlContextPrivate), parent)
179 d->data = new QQmlContextData(this);
181 d->data->setParent(engine?QQmlContextData::get(engine->rootContext()):0);
185 Create a new QQmlContext with the given \a parentContext, and the
188 QQmlContext::QQmlContext(QQmlContext *parentContext, QObject *parent)
189 : QObject(*(new QQmlContextPrivate), parent)
192 d->data = new QQmlContextData(this);
194 d->data->setParent(parentContext?QQmlContextData::get(parentContext):0);
200 QQmlContext::QQmlContext(QQmlContextData *data)
201 : QObject(*(new QQmlContextPrivate), 0)
208 Destroys the QQmlContext.
210 Any expressions, or sub-contexts dependent on this context will be
211 invalidated, but not destroyed (unless they are parented to the QQmlContext
214 QQmlContext::~QQmlContext()
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 QQmlContext::isValid() const
230 Q_D(const QQmlContext);
231 return d->data && d->data->isValid();
235 Return the context's QQmlEngine, or 0 if the context has no QQmlEngine or the
236 QQmlEngine was destroyed.
238 QQmlEngine *QQmlContext::engine() const
240 Q_D(const QQmlContext);
241 return d->data->engine;
245 Return the context's parent QQmlContext, or 0 if this context has no
246 parent or if the parent has been destroyed.
248 QQmlContext *QQmlContext::parentContext() const
250 Q_D(const QQmlContext);
251 return d->data->parent?d->data->parent->asQQmlContext():0;
255 Return the context object, or 0 if there is no context object.
257 QObject *QQmlContext::contextObject() const
259 Q_D(const QQmlContext);
260 return d->data->contextObject;
264 Set the context \a object.
266 void QQmlContext::setContextObject(QObject *object)
270 QQmlContextData *data = d->data;
272 if (data->isInternal) {
273 qWarning("QQmlContext: Cannot set context object for internal context.");
278 qWarning("QQmlContext: 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 QQmlContext::setContextProperty(const QString &name, const QVariant &value)
291 if (d->notifyIndex == -1)
292 d->notifyIndex = this->metaObject()->methodCount();
294 QQmlContextData *data = d->data;
296 if (data->isInternal) {
297 qWarning("QQmlContext: Cannot set property on internal context.");
302 qWarning("QQmlContext: Cannot set property on invalid context.");
308 QObject *o = QQmlEnginePrivate::get(data->engine)->toQObject(value, &ok);
310 setContextProperty(name, o);
315 if (!data->propertyNames) data->propertyNames = new QQmlIntegerCache();
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 QQmlContext does \bold not take ownership of \a value.
334 void QQmlContext::setContextProperty(const QString &name, QObject *value)
337 if (d->notifyIndex == -1)
338 d->notifyIndex = this->metaObject()->methodCount();
340 QQmlContextData *data = d->data;
342 if (data->isInternal) {
343 qWarning("QQmlContext: Cannot set property on internal context.");
348 qWarning("QQmlContext: Cannot set property on invalid context.");
352 if (!data->propertyNames) data->propertyNames = new QQmlIntegerCache();
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 QQmlContext::contextProperty(const QString &name) const
372 Q_D(const QQmlContext);
376 QQmlContextData *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 QQmlPropertyData local;
386 QQmlPropertyData *property =
387 QQmlPropertyCache::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 QQmlContext::nameForObject(QObject *object) const
412 Q_D(const QQmlContext);
414 return d->data->findObjectId(object);
418 Resolves the URL \a src relative to the URL of the
419 containing component.
421 \sa QQmlEngine::baseUrl(), setBaseUrl()
423 QUrl QQmlContext::resolvedUrl(const QUrl &src)
426 return d->data->resolvedUrl(src);
429 QUrl QQmlContextData::resolvedUrl(const QUrl &src)
431 QQmlContextData *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 QQmlContext::setBaseUrl(const QUrl &baseUrl)
466 d->data->url = baseUrl;
467 d->data->urlString = baseUrl.toString();
471 Returns the base url of the component, or the containing component
474 QUrl QQmlContext::baseUrl() const
476 Q_D(const QQmlContext);
477 const QQmlContextData* data = d->data;
478 while (data && data->url.isEmpty())
487 int QQmlContextPrivate::context_count(QQmlListProperty<QObject> *prop)
489 QQmlContext *context = static_cast<QQmlContext*>(prop->object);
490 QQmlContextPrivate *d = QQmlContextPrivate::get(context);
491 int contextProperty = (int)(quintptr)prop->data;
493 if (d->propertyValues.at(contextProperty).userType() != qMetaTypeId<QList<QObject*> >()) {
496 return ((const QList<QObject> *)d->propertyValues.at(contextProperty).constData())->count();
500 QObject *QQmlContextPrivate::context_at(QQmlListProperty<QObject> *prop, int index)
502 QQmlContext *context = static_cast<QQmlContext*>(prop->object);
503 QQmlContextPrivate *d = QQmlContextPrivate::get(context);
504 int contextProperty = (int)(quintptr)prop->data;
506 if (d->propertyValues.at(contextProperty).userType() != qMetaTypeId<QList<QObject*> >()) {
509 return ((const QList<QObject*> *)d->propertyValues.at(contextProperty).constData())->at(index);
514 QQmlContextData::QQmlContextData()
515 : parent(0), engine(0), isInternal(false), ownedByParent(false), isJSContext(false),
516 isPragmaLibraryContext(false), unresolvedNames(false), publicContext(0), activeVMEData(0),
517 propertyNames(0), contextObject(0), imports(0), childContexts(0), nextChild(0), prevChild(0),
518 expressions(0), contextObjects(0), contextGuards(0), idValues(0), idValueCount(0), linkedContext(0),
519 componentAttached(0), v4bindings(0), v8bindings(0)
523 QQmlContextData::QQmlContextData(QQmlContext *ctxt)
524 : parent(0), engine(0), isInternal(false), ownedByParent(false), isJSContext(false),
525 isPragmaLibraryContext(false), unresolvedNames(false), publicContext(ctxt), activeVMEData(0),
526 propertyNames(0), contextObject(0), imports(0), childContexts(0), nextChild(0), prevChild(0),
527 expressions(0), contextObjects(0), contextGuards(0), idValues(0), idValueCount(0), linkedContext(0),
528 componentAttached(0), v4bindings(0), v8bindings(0)
532 void QQmlContextData::invalidate()
534 while (componentAttached) {
535 QQmlComponentAttached *a = componentAttached;
536 componentAttached = a->next;
537 if (componentAttached) componentAttached->prev = &componentAttached;
542 emit a->destruction();
545 while (childContexts) {
546 if (childContexts->ownedByParent) {
547 childContexts->destroy();
549 childContexts->invalidate();
554 *prevChild = nextChild;
555 if (nextChild) nextChild->prevChild = prevChild;
564 void QQmlContextData::clearContext()
567 while (componentAttached) {
568 QQmlComponentAttached *a = componentAttached;
569 componentAttached = a->next;
570 if (componentAttached) componentAttached->prev = &componentAttached;
575 emit a->destruction();
579 QQmlAbstractExpression *expression = expressions;
581 QQmlAbstractExpression *nextExpression = expression->m_nextExpression;
583 expression->m_prevExpression = 0;
584 expression->m_nextExpression = 0;
586 expression->setContext(0);
588 expression = nextExpression;
593 void QQmlContextData::destroy()
596 linkedContext->destroy();
598 if (engine) invalidate();
602 while (contextObjects) {
603 QQmlData *co = contextObjects;
604 contextObjects = contextObjects->nextContextObject;
607 co->outerContext = 0;
608 co->nextContextObject = 0;
609 co->prevContextObject = 0;
612 QQmlGuardedContextData *contextGuard = contextGuards;
613 while (contextGuard) {
614 QQmlGuardedContextData *next = contextGuard->m_next;
615 contextGuard->m_next = 0;
616 contextGuard->m_prev = 0;
617 contextGuard->m_contextData = 0;
623 propertyNames->release();
629 v4bindings->release();
632 v8bindings->release();
634 for (int ii = 0; ii < importedScripts.count(); ++ii) {
635 qPersistentDispose(importedScripts[ii]);
641 delete publicContext;
646 void QQmlContextData::setParent(QQmlContextData *p, bool parentTakesOwnership)
651 nextChild = p->childContexts;
652 if (nextChild) nextChild->prevChild = &nextChild;
653 prevChild = &p->childContexts;
654 p->childContexts = this;
655 ownedByParent = parentTakesOwnership;
659 void QQmlContextData::refreshExpressionsRecursive(QQmlAbstractExpression *expression)
661 QQmlAbstractExpression::DeleteWatcher w(expression);
663 if (expression->m_nextExpression)
664 refreshExpressionsRecursive(expression->m_nextExpression);
667 expression->refresh();
670 static inline bool expressions_to_run(QQmlContextData *ctxt, bool isGlobalRefresh)
672 return ctxt->expressions && (!isGlobalRefresh || ctxt->unresolvedNames);
675 void QQmlContextData::refreshExpressionsRecursive(bool isGlobal)
677 // For efficiency, we try and minimize the number of guards we have to create
678 if (expressions_to_run(this, isGlobal) && (nextChild || childContexts)) {
679 QQmlGuardedContextData guard(this);
682 childContexts->refreshExpressionsRecursive(isGlobal);
684 if (guard.isNull()) return;
687 nextChild->refreshExpressionsRecursive(isGlobal);
689 if (guard.isNull()) return;
691 if (expressions_to_run(this, isGlobal))
692 refreshExpressionsRecursive(expressions);
694 } else if (expressions_to_run(this, isGlobal)) {
696 refreshExpressionsRecursive(expressions);
698 } else if (nextChild && childContexts) {
700 QQmlGuardedContextData guard(this);
702 childContexts->refreshExpressionsRecursive(isGlobal);
704 if (!guard.isNull() && nextChild)
705 nextChild->refreshExpressionsRecursive(isGlobal);
707 } else if (nextChild) {
709 nextChild->refreshExpressionsRecursive(isGlobal);
711 } else if (childContexts) {
713 childContexts->refreshExpressionsRecursive(isGlobal);
718 // Refreshes all expressions that could possibly depend on this context. Refreshing flushes all
719 // context-tree dependent caches in the expressions, and should occur every time the context tree
720 // *structure* (not values) changes.
721 void QQmlContextData::refreshExpressions()
723 bool isGlobal = (parent == 0);
725 // For efficiency, we try and minimize the number of guards we have to create
726 if (expressions_to_run(this, isGlobal) && childContexts) {
727 QQmlGuardedContextData guard(this);
729 childContexts->refreshExpressionsRecursive(isGlobal);
731 if (!guard.isNull() && expressions_to_run(this, isGlobal))
732 refreshExpressionsRecursive(expressions);
734 } else if (expressions_to_run(this, isGlobal)) {
736 refreshExpressionsRecursive(expressions);
738 } else if (childContexts) {
740 childContexts->refreshExpressionsRecursive(isGlobal);
745 void QQmlContextData::addObject(QObject *o)
747 QQmlData *data = QQmlData::get(o, true);
749 Q_ASSERT(data->context == 0);
751 data->context = this;
752 data->outerContext = this;
754 data->nextContextObject = contextObjects;
755 if (data->nextContextObject)
756 data->nextContextObject->prevContextObject = &data->nextContextObject;
757 data->prevContextObject = &contextObjects;
758 contextObjects = data;
761 void QQmlContextData::setIdProperty(int idx, QObject *obj)
764 idValues[idx].context = this;
767 void QQmlContextData::setIdPropertyData(QQmlIntegerCache *data)
769 Q_ASSERT(!propertyNames);
770 propertyNames = data;
771 propertyNames->addref();
773 idValueCount = data->count();
774 idValues = new ContextGuard[idValueCount];
777 QString QQmlContextData::findObjectId(const QObject *obj) const
782 for (int ii = 0; ii < idValueCount; ii++) {
783 if (idValues[ii] == obj)
784 return propertyNames->findId(ii);
788 QQmlContextPrivate *p = QQmlContextPrivate::get(publicContext);
789 for (int ii = 0; ii < p->propertyValues.count(); ++ii)
790 if (p->propertyValues.at(ii) == QVariant::fromValue((QObject *)obj))
791 return propertyNames->findId(ii);
795 return linkedContext->findObjectId(obj);
799 QQmlContext *QQmlContextData::asQQmlContext()
802 publicContext = new QQmlContext(this);
803 return publicContext;
806 QQmlContextPrivate *QQmlContextData::asQQmlContextPrivate()
808 return QQmlContextPrivate::get(asQQmlContext());