1 /****************************************************************************
3 ** Copyright (C) 2011 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"
45 #include "qdeclarativecomponent_p.h"
46 #include "qdeclarativeexpression_p.h"
47 #include "qdeclarativeengine_p.h"
48 #include "qdeclarativeengine.h"
49 #include "qdeclarativeinfo.h"
50 #include <private/qv4bindings_p.h>
51 #include <private/qv8bindings_p.h>
53 #include <qjsengine.h>
54 #include <QtCore/qvarlengtharray.h>
55 #include <QtCore/qdebug.h>
59 QDeclarativeContextPrivate::QDeclarativeContextPrivate()
60 : data(0), notifyIndex(-1)
65 \class QDeclarativeContext
67 \brief The QDeclarativeContext class defines a context within a QML engine.
70 Contexts allow data to be exposed to the QML components instantiated by the
73 Each QDeclarativeContext contains a set of properties, distinct from its QObject
74 properties, that allow data to be explicitly bound to a context by name. The
75 context properties are defined and updated by calling
76 QDeclarativeContext::setContextProperty(). The following example shows a Qt model
77 being bound to a context and then accessed from a QML file.
80 QDeclarativeEngine engine;
81 QStringListModel modelData;
82 QDeclarativeContext *context = new QDeclarativeContext(engine.rootContext());
83 context->setContextProperty("myModel", &modelData);
85 QDeclarativeComponent component(&engine);
86 component.setData("import QtQuick 1.0\nListView { model: myModel }", QUrl());
87 QObject *window = component.create(context);
90 Note it is the responsibility of the creator to delete any QDeclarativeContext it
91 constructs. If the \c context object in the example is no longer needed when the
92 \c window component instance is destroyed, the \c context must be destroyed explicitly.
93 The simplest way to ensure this is to set \c window as the parent of \c context.
95 To simplify binding and maintaining larger data sets, a context object can be set
96 on a QDeclarativeContext. All the properties of the context object are available
97 by name in the context, as though they were all individually added through calls
98 to QDeclarativeContext::setContextProperty(). Changes to the property's values are
99 detected through the property's notify signal. Setting a context object is both
100 faster and easier than manually adding and maintaing context property values.
102 The following example has the same effect as the previous one, but it uses a context
106 class MyDataSet : ... {
108 Q_PROPERTY(QAbstractItemModel *myModel READ model NOTIFY modelChanged)
113 QDeclarativeEngine engine;
114 QDeclarativeContext *context = new QDeclarativeContext(engine.rootContext());
115 context->setContextObject(&myDataSet);
117 QDeclarativeComponent component(&engine);
118 component.setData("import QtQuick 1.0\nListView { model: myModel }", QUrl());
119 component.create(context);
122 All properties added explicitly by QDeclarativeContext::setContextProperty() take
123 precedence over the context object's properties.
125 \section2 The Context Hierarchy
127 Contexts form a hierarchy. The root of this hierarchy is the QML engine's
128 \l {QDeclarativeEngine::rootContext()}{root context}. Child contexts inherit
129 the context properties of their parents; if a child context sets a context property
130 that already exists in its parent, the new context property overrides that of the
133 The following example defines two contexts - \c context1 and \c context2. The
134 second context overrides the "b" context property inherited from the first with a
138 QDeclarativeEngine engine;
139 QDeclarativeContext *context1 = new QDeclarativeContext(engine.rootContext());
140 QDeclarativeContext *context2 = new QDeclarativeContext(context1);
142 context1->setContextProperty("a", 12);
143 context1->setContextProperty("b", 12);
145 context2->setContextProperty("b", 15);
148 While QML objects instantiated in a context are not strictly owned by that
149 context, their bindings are. If a context is destroyed, the property bindings of
150 outstanding QML objects will stop evaluating.
152 \warning Setting the context object or adding new context properties after an object
153 has been created in that context is an expensive operation (essentially forcing all bindings
154 to reevaluate). Thus whenever possible you should complete "setup" of the context
155 before using it to create any objects.
157 \sa {Using QML Bindings in C++ Applications}
161 QDeclarativeContext::QDeclarativeContext(QDeclarativeEngine *e, bool)
162 : QObject(*(new QDeclarativeContextPrivate))
164 Q_D(QDeclarativeContext);
165 d->data = new QDeclarativeContextData(this);
171 Create a new QDeclarativeContext as a child of \a engine's root context, and the
174 QDeclarativeContext::QDeclarativeContext(QDeclarativeEngine *engine, QObject *parent)
175 : QObject(*(new QDeclarativeContextPrivate), parent)
177 Q_D(QDeclarativeContext);
178 d->data = new QDeclarativeContextData(this);
180 d->data->setParent(engine?QDeclarativeContextData::get(engine->rootContext()):0);
184 Create a new QDeclarativeContext with the given \a parentContext, and the
187 QDeclarativeContext::QDeclarativeContext(QDeclarativeContext *parentContext, QObject *parent)
188 : QObject(*(new QDeclarativeContextPrivate), parent)
190 Q_D(QDeclarativeContext);
191 d->data = new QDeclarativeContextData(this);
193 d->data->setParent(parentContext?QDeclarativeContextData::get(parentContext):0);
199 QDeclarativeContext::QDeclarativeContext(QDeclarativeContextData *data)
200 : QObject(*(new QDeclarativeContextPrivate), 0)
202 Q_D(QDeclarativeContext);
207 Destroys the QDeclarativeContext.
209 Any expressions, or sub-contexts dependent on this context will be
210 invalidated, but not destroyed (unless they are parented to the QDeclarativeContext
213 QDeclarativeContext::~QDeclarativeContext()
215 Q_D(QDeclarativeContext);
217 if (!d->data->isInternal)
222 Returns whether the context is valid.
224 To be valid, a context must have a engine, and it's contextObject(), if any,
225 must not have been deleted.
227 bool QDeclarativeContext::isValid() const
229 Q_D(const QDeclarativeContext);
230 return d->data && d->data->isValid();
234 Return the context's QDeclarativeEngine, or 0 if the context has no QDeclarativeEngine or the
235 QDeclarativeEngine was destroyed.
237 QDeclarativeEngine *QDeclarativeContext::engine() const
239 Q_D(const QDeclarativeContext);
240 return d->data->engine;
244 Return the context's parent QDeclarativeContext, or 0 if this context has no
245 parent or if the parent has been destroyed.
247 QDeclarativeContext *QDeclarativeContext::parentContext() const
249 Q_D(const QDeclarativeContext);
250 return d->data->parent?d->data->parent->asQDeclarativeContext():0;
254 Return the context object, or 0 if there is no context object.
256 QObject *QDeclarativeContext::contextObject() const
258 Q_D(const QDeclarativeContext);
259 return d->data->contextObject;
263 Set the context \a object.
265 void QDeclarativeContext::setContextObject(QObject *object)
267 Q_D(QDeclarativeContext);
269 QDeclarativeContextData *data = d->data;
271 if (data->isInternal) {
272 qWarning("QDeclarativeContext: Cannot set context object for internal context.");
277 qWarning("QDeclarativeContext: Cannot set context object on invalid context.");
281 data->contextObject = object;
285 Set a the \a value of the \a name property on this context.
287 void QDeclarativeContext::setContextProperty(const QString &name, const QVariant &value)
289 Q_D(QDeclarativeContext);
290 if (d->notifyIndex == -1)
291 d->notifyIndex = this->metaObject()->methodCount();
293 QDeclarativeContextData *data = d->data;
295 if (data->isInternal) {
296 qWarning("QDeclarativeContext: Cannot set property on internal context.");
301 qWarning("QDeclarativeContext: Cannot set property on invalid context.");
307 QObject *o = QDeclarativeEnginePrivate::get(data->engine)->toQObject(value, &ok);
309 setContextProperty(name, o);
314 if (!data->propertyNames) data->propertyNames = new QDeclarativeIntegerCache();
316 int idx = data->propertyNames->value(name);
318 data->propertyNames->add(name, data->idValueCount + d->propertyValues.count());
319 d->propertyValues.append(value);
321 data->refreshExpressions();
323 d->propertyValues[idx] = value;
324 QMetaObject::activate(this, idx + d->notifyIndex, 0);
329 Set the \a value of the \a name property on this context.
331 QDeclarativeContext does \bold not take ownership of \a value.
333 void QDeclarativeContext::setContextProperty(const QString &name, QObject *value)
335 Q_D(QDeclarativeContext);
336 if (d->notifyIndex == -1)
337 d->notifyIndex = this->metaObject()->methodCount();
339 QDeclarativeContextData *data = d->data;
341 if (data->isInternal) {
342 qWarning("QDeclarativeContext: Cannot set property on internal context.");
347 qWarning("QDeclarativeContext: Cannot set property on invalid context.");
351 if (!data->propertyNames) data->propertyNames = new QDeclarativeIntegerCache();
352 int idx = data->propertyNames->value(name);
355 data->propertyNames->add(name, data->idValueCount + d->propertyValues.count());
356 d->propertyValues.append(QVariant::fromValue(value));
358 data->refreshExpressions();
360 d->propertyValues[idx] = QVariant::fromValue(value);
361 QMetaObject::activate(this, idx + d->notifyIndex, 0);
366 Returns the value of the \a name property for this context
369 QVariant QDeclarativeContext::contextProperty(const QString &name) const
371 Q_D(const QDeclarativeContext);
375 QDeclarativeContextData *data = d->data;
377 if (data->propertyNames)
378 idx = data->propertyNames->value(name);
381 QByteArray utf8Name = name.toUtf8();
382 if (data->contextObject) {
383 QObject *obj = data->contextObject;
384 QDeclarativePropertyData local;
385 QDeclarativePropertyData *property =
386 QDeclarativePropertyCache::property(data->engine, obj, name, local);
388 if (property) value = obj->metaObject()->property(property->coreIndex).read(obj);
390 if (!value.isValid() && parentContext())
391 value = parentContext()->contextProperty(name);
393 if (idx >= d->propertyValues.count())
394 value = QVariant::fromValue(data->idValues[idx - d->propertyValues.count()].data());
396 value = d->propertyValues[idx];
403 Returns the name of \a object in this context, or an empty string if \a object
404 is not named in the context. Objects are named by setContextProperty(), or by ids in
405 the case of QML created contexts.
407 If the object has multiple names, the first is returned.
409 QString QDeclarativeContext::nameForObject(QObject *object) const
411 Q_D(const QDeclarativeContext);
413 return d->data->findObjectId(object);
417 Resolves the URL \a src relative to the URL of the
418 containing component.
420 \sa QDeclarativeEngine::baseUrl(), setBaseUrl()
422 QUrl QDeclarativeContext::resolvedUrl(const QUrl &src)
424 Q_D(QDeclarativeContext);
425 return d->data->resolvedUrl(src);
428 QUrl QDeclarativeContextData::resolvedUrl(const QUrl &src)
430 QDeclarativeContextData *ctxt = this;
432 if (src.isRelative() && !src.isEmpty()) {
435 if(ctxt->url.isValid())
442 return ctxt->url.resolved(src);
444 return engine->baseUrl().resolved(src);
454 Explicitly sets the url resolvedUrl() will use for relative references to \a baseUrl.
456 Calling this function will override the url of the containing
457 component used by default.
461 void QDeclarativeContext::setBaseUrl(const QUrl &baseUrl)
463 Q_D(QDeclarativeContext);
465 d->data->url = baseUrl;
469 Returns the base url of the component, or the containing component
472 QUrl QDeclarativeContext::baseUrl() const
474 Q_D(const QDeclarativeContext);
475 const QDeclarativeContextData* data = d->data;
476 while (data && data->url.isEmpty())
485 int QDeclarativeContextPrivate::context_count(QDeclarativeListProperty<QObject> *prop)
487 QDeclarativeContext *context = static_cast<QDeclarativeContext*>(prop->object);
488 QDeclarativeContextPrivate *d = QDeclarativeContextPrivate::get(context);
489 int contextProperty = (int)(quintptr)prop->data;
491 if (d->propertyValues.at(contextProperty).userType() != qMetaTypeId<QList<QObject*> >()) {
494 return ((const QList<QObject> *)d->propertyValues.at(contextProperty).constData())->count();
498 QObject *QDeclarativeContextPrivate::context_at(QDeclarativeListProperty<QObject> *prop, int index)
500 QDeclarativeContext *context = static_cast<QDeclarativeContext*>(prop->object);
501 QDeclarativeContextPrivate *d = QDeclarativeContextPrivate::get(context);
502 int contextProperty = (int)(quintptr)prop->data;
504 if (d->propertyValues.at(contextProperty).userType() != qMetaTypeId<QList<QObject*> >()) {
507 return ((const QList<QObject*> *)d->propertyValues.at(contextProperty).constData())->at(index);
512 QDeclarativeContextData::QDeclarativeContextData()
513 : parent(0), engine(0), isInternal(false), ownedByParent(false), isJSContext(false),
514 isPragmaLibraryContext(false), publicContext(0), activeVMEData(0), propertyNames(0), contextObject(0),
515 imports(0), childContexts(0), nextChild(0), prevChild(0), expressions(0), contextObjects(0),
516 contextGuards(0), idValues(0), idValueCount(0), linkedContext(0), componentAttached(0),
517 v4bindings(0), v8bindings(0)
521 QDeclarativeContextData::QDeclarativeContextData(QDeclarativeContext *ctxt)
522 : parent(0), engine(0), isInternal(false), ownedByParent(false), isJSContext(false),
523 isPragmaLibraryContext(false), publicContext(ctxt), activeVMEData(0), propertyNames(0),
524 contextObject(0), imports(0), childContexts(0), nextChild(0), prevChild(0), expressions(0),
525 contextObjects(0), contextGuards(0), idValues(0), idValueCount(0), linkedContext(0),
526 componentAttached(0), v4bindings(0), v8bindings(0)
530 void QDeclarativeContextData::invalidate()
532 while (childContexts) {
533 if (childContexts->ownedByParent) {
534 childContexts->destroy();
536 childContexts->invalidate();
540 while (componentAttached) {
541 QDeclarativeComponentAttached *a = componentAttached;
542 componentAttached = a->next;
543 if (componentAttached) componentAttached->prev = &componentAttached;
548 emit a->destruction();
552 *prevChild = nextChild;
553 if (nextChild) nextChild->prevChild = prevChild;
562 void QDeclarativeContextData::clearContext()
565 while (componentAttached) {
566 QDeclarativeComponentAttached *a = componentAttached;
567 componentAttached = a->next;
568 if (componentAttached) componentAttached->prev = &componentAttached;
573 emit a->destruction();
577 QDeclarativeAbstractExpression *expression = expressions;
579 QDeclarativeAbstractExpression *nextExpression = expression->m_nextExpression;
581 expression->m_context = 0;
582 expression->m_prevExpression = 0;
583 expression->m_nextExpression = 0;
585 expression = nextExpression;
590 void QDeclarativeContextData::destroy()
593 linkedContext->destroy();
595 if (engine) invalidate();
599 while (contextObjects) {
600 QDeclarativeData *co = contextObjects;
601 contextObjects = contextObjects->nextContextObject;
604 co->outerContext = 0;
605 co->nextContextObject = 0;
606 co->prevContextObject = 0;
609 QDeclarativeGuardedContextData *contextGuard = contextGuards;
610 while (contextGuard) {
611 QDeclarativeGuardedContextData *next = contextGuard->m_next;
612 contextGuard->m_next = 0;
613 contextGuard->m_prev = 0;
614 contextGuard->m_contextData = 0;
620 propertyNames->release();
626 v4bindings->release();
629 v8bindings->release();
631 for (int ii = 0; ii < importedScripts.count(); ++ii) {
632 qPersistentDispose(importedScripts[ii]);
638 delete publicContext;
643 void QDeclarativeContextData::setParent(QDeclarativeContextData *p, bool parentTakesOwnership)
648 nextChild = p->childContexts;
649 if (nextChild) nextChild->prevChild = &nextChild;
650 prevChild = &p->childContexts;
651 p->childContexts = this;
652 ownedByParent = parentTakesOwnership;
656 void QDeclarativeContextData::refreshExpressionsRecursive(QDeclarativeAbstractExpression *expression)
658 QDeleteWatcher w(expression);
660 if (expression->m_nextExpression)
661 refreshExpressionsRecursive(expression->m_nextExpression);
664 expression->refresh();
667 void QDeclarativeContextData::refreshExpressionsRecursive()
669 // For efficiency, we try and minimize the number of guards we have to create
670 if (expressions && (nextChild || childContexts)) {
671 QDeclarativeGuardedContextData guard(this);
674 childContexts->refreshExpressionsRecursive();
676 if (guard.isNull()) return;
679 nextChild->refreshExpressionsRecursive();
681 if (guard.isNull()) return;
684 refreshExpressionsRecursive(expressions);
686 } else if (expressions) {
688 refreshExpressionsRecursive(expressions);
690 } else if (nextChild && childContexts) {
692 QDeclarativeGuardedContextData guard(this);
694 childContexts->refreshExpressionsRecursive();
696 if (!guard.isNull() && nextChild)
697 nextChild->refreshExpressionsRecursive();
699 } else if (nextChild) {
701 nextChild->refreshExpressionsRecursive();
703 } else if (childContexts) {
705 childContexts->refreshExpressionsRecursive();
710 // Refreshes all expressions that could possibly depend on this context. Refreshing flushes all
711 // context-tree dependent caches in the expressions, and should occur every time the context tree
712 // *structure* (not values) changes.
713 void QDeclarativeContextData::refreshExpressions()
715 // For efficiency, we try and minimize the number of guards we have to create
716 if (expressions && childContexts) {
717 QDeclarativeGuardedContextData guard(this);
719 childContexts->refreshExpressionsRecursive();
721 if (!guard.isNull() && expressions)
722 refreshExpressionsRecursive(expressions);
724 } else if (expressions) {
726 refreshExpressionsRecursive(expressions);
728 } else if (childContexts) {
730 childContexts->refreshExpressionsRecursive();
735 void QDeclarativeContextData::addObject(QObject *o)
737 QDeclarativeData *data = QDeclarativeData::get(o, true);
739 Q_ASSERT(data->context == 0);
741 data->context = this;
742 data->outerContext = this;
744 data->nextContextObject = contextObjects;
745 if (data->nextContextObject)
746 data->nextContextObject->prevContextObject = &data->nextContextObject;
747 data->prevContextObject = &contextObjects;
748 contextObjects = data;
751 void QDeclarativeContextData::setIdProperty(int idx, QObject *obj)
754 idValues[idx].context = this;
757 void QDeclarativeContextData::setIdPropertyData(QDeclarativeIntegerCache *data)
759 Q_ASSERT(!propertyNames);
760 propertyNames = data;
761 propertyNames->addref();
763 idValueCount = data->count();
764 idValues = new ContextGuard[idValueCount];
767 QString QDeclarativeContextData::findObjectId(const QObject *obj) const
772 for (int ii = 0; ii < idValueCount; ii++) {
773 if (idValues[ii] == obj)
774 return propertyNames->findId(ii);
778 QDeclarativeContextPrivate *p = QDeclarativeContextPrivate::get(publicContext);
779 for (int ii = 0; ii < p->propertyValues.count(); ++ii)
780 if (p->propertyValues.at(ii) == QVariant::fromValue((QObject *)obj))
781 return propertyNames->findId(ii);
785 return linkedContext->findObjectId(obj);
789 QDeclarativeContext *QDeclarativeContextData::asQDeclarativeContext()
792 publicContext = new QDeclarativeContext(this);
793 return publicContext;
796 QDeclarativeContextPrivate *QDeclarativeContextData::asQDeclarativeContextPrivate()
798 return QDeclarativeContextPrivate::get(asQDeclarativeContext());