summaryrefslogtreecommitdiffstats
path: root/src/declarative/qml/qmlengine.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/declarative/qml/qmlengine.cpp')
-rw-r--r--src/declarative/qml/qmlengine.cpp1295
1 files changed, 1295 insertions, 0 deletions
diff --git a/src/declarative/qml/qmlengine.cpp b/src/declarative/qml/qmlengine.cpp
new file mode 100644
index 0000000..63f45b4
--- /dev/null
+++ b/src/declarative/qml/qmlengine.cpp
@@ -0,0 +1,1295 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+// XXX ;)
+#define private public
+#include <QMetaProperty>
+#undef private
+
+#include <private/qmlengine_p.h>
+#include <private/qmlcontext_p.h>
+
+#ifdef QT_SCRIPTTOOLS_LIB
+#include <QScriptEngineDebugger>
+#endif
+
+#include <QScriptClass>
+#include <QNetworkReply>
+#include <QNetworkRequest>
+#include <QNetworkAccessManager>
+#include <QList>
+#include <QPair>
+#include <QDebug>
+#include <QMetaObject>
+#include "qml.h"
+#include <qfxperf.h>
+#include <QStack>
+#include "private/qmlbasicscript_p.h"
+#include "private/qmlcompiledcomponent_p.h"
+#include "qmlengine.h"
+#include "qmlcontext.h"
+#include "qmlexpression.h"
+#include <QtCore/qthreadstorage.h>
+#include <QtCore/qthread.h>
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qdir.h>
+#include <qmlcomponent.h>
+#include "private/qmlmetaproperty_p.h"
+
+
+QT_BEGIN_NAMESPACE
+
+DEFINE_BOOL_CONFIG_OPTION(bindValueDebug, QML_BINDVALUE_DEBUG);
+#ifdef QT_SCRIPTTOOLS_LIB
+DEFINE_BOOL_CONFIG_OPTION(debuggerEnabled, QML_DEBUGGER);
+#endif
+
+Q_DECLARE_METATYPE(QmlMetaProperty);
+
+QML_DEFINE_TYPE(QObject,Object);
+
+static QScriptValue qmlMetaProperty_emit(QScriptContext *ctx, QScriptEngine *engine)
+{
+ QmlMetaProperty mp = qscriptvalue_cast<QmlMetaProperty>(ctx->thisObject());
+ if(mp.type() & QmlMetaProperty::Signal)
+ mp.emitSignal();
+ return engine->nullValue();
+}
+
+struct StaticQtMetaObject : public QObject
+{
+ static const QMetaObject *get()
+ { return &static_cast<StaticQtMetaObject*> (0)->staticQtMetaObject; }
+};
+
+
+struct QmlEngineStack {
+ QmlEngineStack();
+
+ QStack<QmlEngine *> mainThreadEngines;
+ QThread *mainThread;
+
+ QThreadStorage<QStack<QmlEngine *> *> storage;
+
+ QStack<QmlEngine *> *engines();
+};
+
+Q_GLOBAL_STATIC(QmlEngineStack, engineStack);
+
+QmlEngineStack::QmlEngineStack()
+: mainThread(0)
+{
+}
+
+QStack<QmlEngine *> *QmlEngineStack::engines()
+{
+ if(mainThread== 0) {
+ if(!QCoreApplication::instance())
+ return 0;
+ mainThread = QCoreApplication::instance()->thread();
+ }
+
+ // Note: This is very slightly faster than just using the thread storage
+ // for everything.
+ QStack<QmlEngine *> *engines = 0;
+ if(QThread::currentThread() == mainThread) {
+ engines = &mainThreadEngines;
+ } else {
+ engines = storage.localData();
+ if(!engines) {
+ engines = new QStack<QmlEngine *>;
+ storage.setLocalData(engines);
+ }
+ }
+ return engines;
+}
+
+
+QmlEnginePrivate::QmlEnginePrivate(QmlEngine *e)
+: rootContext(0), currentBindContext(0), currentExpression(0), q(e),
+ rootComponent(0), networkAccessManager(0), typeManager(e)
+{
+ QScriptValue proto = scriptEngine.newObject();
+ proto.setProperty(QLatin1String("emit"),
+ scriptEngine.newFunction(qmlMetaProperty_emit));
+ scriptEngine.setDefaultPrototype(qMetaTypeId<QmlMetaProperty>(), proto);
+
+ QScriptValue qtObject = scriptEngine.newQMetaObject(StaticQtMetaObject::get());
+ scriptEngine.globalObject().setProperty(QLatin1String("Qt"), qtObject);
+}
+
+QmlEnginePrivate::~QmlEnginePrivate()
+{
+ delete rootContext;
+ rootContext = 0;
+ delete contextClass;
+ contextClass = 0;
+ delete objectClass;
+ objectClass = 0;
+ delete networkAccessManager;
+ networkAccessManager = 0;
+}
+
+void QmlEnginePrivate::init()
+{
+ contextClass = new QmlContextScriptClass(q);
+ objectClass = new QmlObjectScriptClass(q);
+ rootContext = new QmlContext(q);
+#ifdef QT_SCRIPTTOOLS_LIB
+ if(debuggerEnabled()){
+ debugger = new QScriptEngineDebugger(q);
+ debugger->attachTo(&scriptEngine);
+ }
+#endif
+}
+
+QmlContext *QmlEnginePrivate::setCurrentBindContext(QmlContext *c)
+{
+ QmlContext *old = currentBindContext;
+ currentBindContext = c;
+ return old;
+}
+
+////////////////////////////////////////////////////////////////////
+typedef QHash<QPair<const QMetaObject *, QString>, bool> FunctionCache;
+Q_GLOBAL_STATIC(FunctionCache, functionCache);
+
+QScriptClass::QueryFlags
+QmlEnginePrivate::queryObject(const QString &propName,
+ uint *id, QObject *obj)
+{
+ QScriptClass::QueryFlags rv = 0;
+
+ QmlMetaProperty prop(obj, propName);
+ if(prop.type() == QmlMetaProperty::Invalid) {
+ QPair<const QMetaObject *, QString> key =
+ qMakePair(obj->metaObject(), propName);
+ bool isFunction = false;
+ if (functionCache()->contains(key)) {
+ isFunction = functionCache()->value(key);
+ } else {
+ QScriptValue sobj = scriptEngine.newQObject(obj);
+ QScriptValue func = sobj.property(propName);
+ isFunction = func.isFunction();
+ functionCache()->insert(key, isFunction);
+ }
+
+ if(isFunction) {
+ *id = QmlScriptClass::FunctionId;
+ rv |= QScriptClass::HandlesReadAccess;
+ }
+ } else {
+ *id = QmlScriptClass::PropertyId;
+ *id |= prop.save();
+
+ rv |= QScriptClass::HandlesReadAccess;
+ if(prop.isWritable())
+ rv |= QScriptClass::HandlesWriteAccess;
+ }
+
+ return rv;
+}
+
+QScriptValue QmlEnginePrivate::propertyObject(const QScriptString &propName,
+ QObject *obj, uint id)
+{
+ if(id == QmlScriptClass::FunctionId) {
+ QScriptValue sobj = scriptEngine.newQObject(obj);
+ QScriptValue func = sobj.property(propName);
+ return func;
+ } else {
+ QmlMetaProperty prop;
+ prop.restore(id, obj);
+ if(!prop.isValid())
+ return QScriptValue();
+
+ if(prop.type() & QmlMetaProperty::Signal) {
+ return scriptEngine.newVariant(qVariantFromValue(prop));
+ } else {
+ QVariant var = prop.read();
+ capturedProperties << prop;
+ QObject *varobj = QmlMetaType::toQObject(var);
+ if(!varobj)
+ varobj = qvariant_cast<QObject *>(var);
+ if(varobj) {
+ return scriptEngine.newObject(objectClass, scriptEngine.newVariant(QVariant::fromValue(varobj)));
+ } else {
+ if (var.type() == QVariant::Bool)
+ return QScriptValue(&scriptEngine, var.toBool());
+ return scriptEngine.newVariant(var);
+ }
+ }
+ }
+
+ return QScriptValue();
+}
+
+void QmlEnginePrivate::contextActivated(QmlContext *)
+{
+ Q_Q(QmlEngine);
+ QmlEngineStack *stack = engineStack();
+ if(!stack)
+ return;
+ QStack<QmlEngine *> *engines = stack->engines();
+ if(engines)
+ engines->push(q);
+}
+
+void QmlEnginePrivate::contextDeactivated(QmlContext *)
+{
+ QmlEngineStack *stack = engineStack();
+ if(!stack)
+ return;
+ QStack<QmlEngine *> *engines = stack->engines();
+ if(engines) {
+ Q_ASSERT(engines->top() == q_func());
+ engines->pop();
+ }
+}
+
+
+////////////////////////////////////////////////////////////////////
+
+bool QmlEnginePrivate::fetchCache(QmlBasicScriptNodeCache &cache, const QString &propName, QObject *obj)
+{
+ QmlMetaProperty prop(obj, propName);
+
+ if(!prop.isValid())
+ return false;
+
+ capturedProperties << prop;
+
+ if(prop.type() & QmlMetaProperty::Attached) {
+
+ cache.object = obj;
+ cache.type = QmlBasicScriptNodeCache::Attached;
+ cache.attached = prop.d->attachedObject();
+ return true;
+
+ } else if(prop.type() & QmlMetaProperty::Property) {
+
+ cache.object = obj;
+ cache.type = QmlBasicScriptNodeCache::Core;
+ cache.core = prop.property().idx + prop.property().mobj->propertyOffset();
+ cache.coreType = prop.propertyType();
+ return true;
+
+ } else if(prop.type() & QmlMetaProperty::SignalProperty) {
+
+ cache.object = obj;
+ cache.type = QmlBasicScriptNodeCache::SignalProperty;
+ cache.core = prop.coreIndex();
+ return true;
+
+ } else if(prop.type() & QmlMetaProperty::Signal) {
+
+ cache.object = obj;
+ cache.type = QmlBasicScriptNodeCache::Signal;
+ cache.core = prop.coreIndex();
+ return true;
+
+ }
+
+ return false;
+}
+
+bool QmlEnginePrivate::loadCache(QmlBasicScriptNodeCache &cache, const QString &propName, QmlContextPrivate *context)
+{
+ while(context) {
+ if(context->variantProperties.contains(propName)) {
+ cache.object = 0;
+ cache.type = QmlBasicScriptNodeCache::Variant;
+ cache.context = context;
+ return true;
+ }
+
+ if(context->properties.contains(propName)) {
+ cache.object = context->properties[propName];
+ cache.type = QmlBasicScriptNodeCache::Explicit;
+ return true;
+ }
+
+ foreach(QObject *obj, context->defaultObjects) {
+ if(fetchCache(cache, propName, obj))
+ return true;
+ }
+
+ if(context->parent)
+ context = context->parent->d_func();
+ else
+ context = 0;
+ }
+ return false;
+}
+
+
+/*!
+ \class QmlEngine
+ \brief The QmlEngine class provides an environment for instantiating QML components.
+ \mainclass
+
+ Each QML component is instantiated in a QmlContext. QmlContext's are
+ essential for passing data to QML components. In QML, contexts are arranged
+ hierarchically and this hierarchy is managed by the QmlEngine.
+
+ Prior to creating any QML components, an application must have created a
+ QmlEngine to gain access to a QML context. The following example shows how
+ to create a simple Text item.
+
+ \code
+ QmlEngine engine;
+ QmlComponent component("<Text text=\"Hello world!\"/>");
+ QFxItem *item = qobject_cast<QFxItem *>(component.create(&engine));
+
+ //add item to view, etc
+ ...
+ \endcode
+
+ In this case, the Text item will be created in the engine's
+ \l {QmlEngine::rootContext()}{root context}.
+
+ \sa QmlComponent QmlContext
+*/
+
+/*!
+ Create a new QmlEngine with the given \a parent.
+*/
+QmlEngine::QmlEngine(QObject *parent)
+: QObject(*new QmlEnginePrivate(this), parent)
+{
+ Q_D(QmlEngine);
+ d->init();
+
+ qRegisterMetaType<QVariant>("QVariant");
+}
+
+/*!
+ Destroys the QmlEngine.
+
+ Any QmlContext's created on this engine will be invalidated, but not
+ destroyed (unless they are parented to the QmlEngine object).
+*/
+QmlEngine::~QmlEngine()
+{
+}
+
+void QmlEngine::clearComponentCache()
+{
+ Q_D(QmlEngine);
+ d->typeManager.clearCache();
+}
+
+/*!
+ Returns the engine's root context.
+
+ The root context is automatically created by the QmlEngine. Data that
+ should be available to all QML component instances instantiated by the
+ engine should be put in the root context.
+
+ Additional data that should only be available to a subset of component
+ instances should be added to sub-contexts parented to the root context.
+*/
+QmlContext *QmlEngine::rootContext()
+{
+ Q_D(QmlEngine);
+ return d->rootContext;
+}
+
+/*!
+ Returns this engine's active context, or 0 if no context is active on this
+ engine.
+
+ Contexts are activated and deactivated by calling QmlContext::activate() and
+ QmlContext::deactivate() respectively.
+
+ Context activation holds no special semantic, other than it allows types
+ instantiated by QML to access "their" context without having it passed as
+ a parameter in their constructor, as shown below.
+ \code
+ class MyClass : ... {
+ ...
+ MyClass() {
+ qWarning() << "I was instantiated in this context:"
+ << QmlContext::activeContext();
+ }
+ };
+ \endcode
+*/
+QmlContext *QmlEngine::activeContext()
+{
+ Q_D(QmlEngine);
+ if(d->currentBindContext)
+ return d->currentBindContext;
+ else
+ return 0;
+}
+
+/*!
+ Sets the mappings from namespace URIs to URL to \a map.
+
+ \sa nameSpacePaths
+*/
+void QmlEngine::setNameSpacePaths(const QMap<QString,QString>& map)
+{
+ Q_D(QmlEngine);
+ d->nameSpacePaths = map;
+}
+
+/*!
+ Adds mappings (given by \a map) from namespace URIs to URL.
+
+ \sa nameSpacePaths
+*/
+void QmlEngine::addNameSpacePaths(const QMap<QString,QString>& map)
+{
+ Q_D(QmlEngine);
+ d->nameSpacePaths.unite(map);
+}
+
+/*!
+ Adds a mapping from namespace URI \a ns to URL \a path.
+
+ \sa nameSpacePaths
+*/
+void QmlEngine::addNameSpacePath(const QString& ns, const QString& path)
+{
+ Q_D(QmlEngine);
+ d->nameSpacePaths.insertMulti(ns,path);
+}
+
+/*!
+ Returns the mapping from namespace URIs to URLs.
+
+ Namespaces in QML allow types to be specified by a URI,
+ using standard XML namespaces:
+
+ \code
+ <Item xmlns:foo="xyz://abc/def">
+ <foo:Bar/>
+ </Item>
+ \endcode
+
+ Actual QML types can be defined in URLs, in which case a mapping
+ may be made from URIs (such as "xyz://abc/def/Bar.qml" above, to
+ URLs (such as "file:///opt/abcdef/Bar.qml"):
+
+ \code
+ engine->addNameSpacePath("xyz://abc/def","file:///opt/abcdef");
+ \endcode
+
+ If only a prefix of the URI is mapped, the path of the URI is
+ mapped similarly to the URL:
+
+ \code
+ engine->addNameSpacePath("xyz://abc","file:///opt/jkl");
+ \endcode
+
+ In the above case, "xyz://abc/def/Bar.qml" would then map to
+ "file:///opt/jkl/def/Bar.qml".
+
+ \sa componentUrl
+*/
+QMap<QString,QString> QmlEngine::nameSpacePaths() const
+{
+ Q_D(const QmlEngine);
+ return d->nameSpacePaths;
+}
+
+/*!
+ Returns the URL for the component source \a src, as mapped
+ by the nameSpacePaths(), resolved relative to \a baseUrl.
+
+ \sa nameSpacePaths
+*/
+QUrl QmlEngine::componentUrl(const QUrl& src, const QUrl& baseUrl) const
+{
+ Q_D(const QmlEngine);
+
+ // Find the most-specific namespace matching src.
+ // For files, multiple paths can be given, the first found is used.
+ QUrl r;
+ QMap<QString, QString>::const_iterator i = d->nameSpacePaths.constBegin();
+ QString rns=QLatin1String(":"); // ns of r, if file found, initial an imposible namespace
+ QString srcstring = src.toString();
+ while (i != d->nameSpacePaths.constEnd()) {
+ QString ns = i.key();
+ QString path = i.value();
+ if (ns != rns) {
+ if (srcstring.startsWith(ns) && (ns.length()==0 || srcstring[ns.length()]==QLatin1Char('/'))) {
+ QString file = ns.length()==0 ? srcstring : srcstring.mid(ns.length()+1);
+ QUrl cr = baseUrl.resolved(QUrl(path + QLatin1String("/") + file));
+ QString lf = cr.toLocalFile();
+ if (lf.isEmpty() || QFile::exists(lf)) {
+ r = cr;
+ rns = ns;
+ }
+ }
+ }
+ ++i;
+ }
+ if (r.isEmpty())
+ r = baseUrl.resolved(src);
+ return r;
+}
+
+/*!
+ Sets the common QNetworkAccessManager, \a network, used by all QML elements instantiated
+ by this engine.
+
+ Any previously set manager is deleted and \a network is owned by the QmlEngine. This
+ method should only be called before any QmlComponents are instantiated.
+*/
+void QmlEngine::setNetworkAccessManager(QNetworkAccessManager *network)
+{
+ Q_D(QmlEngine);
+ delete d->networkAccessManager;
+ d->networkAccessManager = network;
+}
+
+/*!
+ Returns the common QNetworkAccessManager used by all QML elements instantiated by
+ this engine.
+
+ The default implements no caching, cookiejar, etc., just a default
+ QNetworkAccessManager.
+*/
+QNetworkAccessManager *QmlEngine::networkAccessManager() const
+{
+ Q_D(const QmlEngine);
+ if(!d->networkAccessManager)
+ d->networkAccessManager = new QNetworkAccessManager;
+ return d->networkAccessManager;
+}
+
+/*! \internal */
+QScriptEngine *QmlEngine::scriptEngine()
+{
+ Q_D(QmlEngine);
+ return &d->scriptEngine;
+}
+
+/*!
+ Returns the currently active QmlEngine.
+
+ The active engine is the engine associated with the last activated
+ QmlContext. This method is thread-safe - the "active" engine is maintained
+ independently for each thread.
+*/
+QmlEngine *QmlEngine::activeEngine()
+{
+ QmlEngineStack *stack = engineStack();
+ if(!stack) return 0;
+
+ QStack<QmlEngine *> *engines = stack->engines();
+ if(!engines) {
+ qWarning("QmlEngine::activeEngine() cannot be called before the construction of QCoreApplication");
+ return 0;
+ }
+
+ if(engines->isEmpty())
+ return 0;
+ else
+ return engines->top();
+}
+
+
+
+QmlExpressionPrivate::QmlExpressionPrivate(QmlExpression *b)
+: q(b), ctxt(0), sseData(0), proxy(0), me(0), trackChange(false)
+{
+}
+
+QmlExpressionPrivate::QmlExpressionPrivate(QmlExpression *b, void *expr, QmlRefCount *rc)
+: q(b), ctxt(0), sse((const char *)expr, rc), sseData(0), proxy(0), me(0), trackChange(true)
+{
+}
+
+QmlExpressionPrivate::QmlExpressionPrivate(QmlExpression *b, const QString &expr, bool ssecompile)
+: q(b), ctxt(0), expression(expr), sseData(0), proxy(0), me(0), trackChange(true)
+{
+ if(ssecompile) {
+#ifdef Q_ENABLE_PERFORMANCE_LOG
+ QFxPerfTimer<QFxPerf::BindCompile> pt;
+#endif
+ sse.compile(expr.toLatin1());
+ }
+}
+
+QmlExpressionPrivate::~QmlExpressionPrivate()
+{
+ sse.deleteScriptState(sseData);
+ sseData = 0;
+ delete proxy;
+}
+
+/*!
+ Create an invalid QmlExpression.
+
+ As the expression will not have an associated QmlContext, this will be a
+ null expression object and its value will always be an invalid QVariant.
+ */
+QmlExpression::QmlExpression()
+: d(new QmlExpressionPrivate(this))
+{
+}
+
+/*! \internal */
+QmlExpression::QmlExpression(QmlContext *ctxt, void *expr,
+ QmlRefCount *rc, QObject *me)
+: d(new QmlExpressionPrivate(this, expr, rc))
+{
+ d->ctxt = ctxt;
+ d->me = me;
+}
+
+/*! \internal */
+QmlExpression::QmlExpression(QmlContext *ctxt, const QString &expr,
+ QObject *me, bool ssecompile)
+: d(new QmlExpressionPrivate(this, expr, ssecompile))
+{
+ d->ctxt = ctxt;
+ d->me = me;
+}
+
+/*!
+ Create a QmlExpression object.
+
+ The \a expression ECMAScript will be executed in the \a ctxt QmlContext.
+ If specified, the \a scope object's properties will also be in scope during
+ the expression's execution.
+*/
+QmlExpression::QmlExpression(QmlContext *ctxt, const QString &expression,
+ QObject *scope)
+: d(new QmlExpressionPrivate(this, expression, true))
+{
+ d->ctxt = ctxt;
+ d->me = scope;
+}
+
+/*!
+ Destroy the QmlExpression instance.
+*/
+QmlExpression::~QmlExpression()
+{
+ delete d; d = 0;
+}
+
+/*!
+ Returns the QmlEngine this expression is associated with, or 0 if there
+ is no association or the QmlEngine has been destroyed.
+*/
+QmlEngine *QmlExpression::engine() const
+{
+ return d->ctxt->engine();
+}
+
+/*!
+ Returns teh QmlContext this expression is associated with, or 0 if there
+ is no association or the QmlContext has been destroyed.
+*/
+QmlContext *QmlExpression::context() const
+{
+ return d->ctxt;
+}
+
+/*!
+ Returns the expression string.
+*/
+QString QmlExpression::expression() const
+{
+ if(d->sse.isValid())
+ return QLatin1String(d->sse.expression());
+ else
+ return d->expression;
+}
+
+/*!
+ Clear the expression.
+*/
+void QmlExpression::clearExpression()
+{
+ setExpression(QString());
+}
+
+/*!
+ Set the expression to \a expression.
+*/
+void QmlExpression::setExpression(const QString &expression)
+{
+ if(d->sseData) {
+ d->sse.deleteScriptState(d->sseData);
+ d->sseData = 0;
+ }
+
+ delete d->proxy; d->proxy = 0;
+
+ d->expression = expression;
+
+ if(d->expression.isEmpty())
+ d->sse.clear();
+ else
+ d->sse.compile(expression.toLatin1());
+}
+
+/*!
+ Called by QmlExpression each time the expression value changes from the
+ last time it was evaluated. The expression must have been evaluated at
+ least once (by calling QmlExpression::value()) before this callback will
+ be made.
+
+ The default implementation does nothing.
+*/
+void QmlExpression::valueChanged()
+{
+}
+
+Q_DECLARE_METATYPE(QList<QObject *>);
+
+void BindExpressionProxy::changed()
+{
+ e->valueChanged();
+}
+
+/*!
+ Returns the value of the expression, or an invalid QVariant if the
+ expression is invalid or has an error.
+*/
+QVariant QmlExpression::value()
+{
+ if(bindValueDebug())
+ qWarning() << "QmlEngine: Evaluating:" << expression();
+ QVariant rv;
+ if(!d->ctxt || (!d->sse.isValid() && d->expression.isEmpty()))
+ return rv;
+
+#ifdef Q_ENABLE_PERFORMANCE_LOG
+ QFxPerfTimer<QFxPerf::BindValue> perf;
+#endif
+
+ QmlBasicScript::CacheState cacheState = QmlBasicScript::Reset;
+
+ QmlEnginePrivate *ep = engine()->d_func();
+ QmlExpression *lastCurrentExpression = ep->currentExpression;
+ ep->currentExpression = this;
+ if(d->sse.isValid()) {
+#ifdef Q_ENABLE_PERFORMANCE_LOG
+ QFxPerfTimer<QFxPerf::BindValueSSE> perfsse;
+#endif
+
+ context()->d_func()->defaultObjects.insert(context()->d_func()->highPriorityCount, d->me);
+
+ if(!d->sseData)
+ d->sseData = d->sse.newScriptState();
+ rv = d->sse.run(context(), d->sseData, &cacheState);
+
+ context()->d_func()->defaultObjects.removeAt(context()->d_func()->highPriorityCount);
+ } else {
+#ifdef Q_ENABLE_PERFORMANCE_LOG
+ QFxPerfTimer<QFxPerf::BindValueQt> perfqt;
+#endif
+ context()->d_func()->defaultObjects.insert(context()->d_func()->highPriorityCount, d->me);
+
+ QScriptEngine *scriptEngine = engine()->scriptEngine();
+ QScriptValueList oldScopeChain = scriptEngine->currentContext()->scopeChain();
+ for (int i = 0; i < oldScopeChain.size(); ++i) {
+ scriptEngine->currentContext()->popScope();
+ }
+ for (int i = context()->d_func()->scopeChain.size() - 1; i > -1; --i) {
+ scriptEngine->currentContext()->pushScope(context()->d_func()->scopeChain.at(i));
+ }
+ QScriptValue svalue = scriptEngine->evaluate(expression());
+ context()->d_func()->defaultObjects.removeAt(context()->d_func()->highPriorityCount);
+ if(svalue.isArray()) {
+ int length = svalue.property(QLatin1String("length")).toInt32();
+ if(length && svalue.property(0).isObject()) {
+ QList<QObject *> list;
+ for(int ii = 0; ii < length; ++ii) {
+ QScriptValue arrayItem = svalue.property(ii);
+ QObject *d = qvariant_cast<QObject *>(arrayItem.data().toVariant());
+ if(d) {
+ list << d;
+ } else {
+ list << 0;
+ }
+ }
+ rv = QVariant::fromValue(list);
+ }
+ } /*else if (svalue.isVariant()) {
+ rv = svalue.toVariant();
+ }*/ else if (svalue.isObject()) {
+ QScriptValue objValue = svalue.data();
+ if (objValue.isValid())
+ rv = objValue.toVariant();
+ }
+ if(rv.isNull()) {
+ rv = svalue.toVariant();
+ }
+
+ for (int i = 0; i < context()->d_func()->scopeChain.size(); ++i) {
+ scriptEngine->currentContext()->popScope();
+ }
+ for (int i = oldScopeChain.size() - 1; i > -1; --i) {
+ scriptEngine->currentContext()->pushScope(oldScopeChain.at(i));
+ }
+ }
+ ep->currentExpression = lastCurrentExpression;
+
+ if(cacheState != QmlBasicScript::NoChange) {
+ if(cacheState != QmlBasicScript::Incremental && d->proxy) {
+ delete d->proxy;
+ d->proxy = 0;
+ }
+
+ if(trackChange() && ep->capturedProperties.count()) {
+ if(!d->proxy)
+ d->proxy = new BindExpressionProxy(this);
+
+ static int changedIndex = -1;
+ if(changedIndex == -1)
+ changedIndex = BindExpressionProxy::staticMetaObject.indexOfSlot("changed()");
+
+ if(bindValueDebug())
+ qWarning() << " Depends on:";
+
+ for(int ii = 0; ii < ep->capturedProperties.count(); ++ii) {
+ const QmlMetaProperty &prop =
+ ep->capturedProperties.at(ii);
+
+ if(prop.hasChangedNotifier()) {
+ prop.connectNotifier(d->proxy, changedIndex);
+ if(bindValueDebug())
+ qWarning() << " property"
+ << prop.name()
+ << prop.object()
+ << prop.object()->metaObject()->superClass()->className();
+ } else if(bindValueDebug()) {
+ qWarning() << " non-subscribable property"
+ << prop.name()
+ << prop.object()
+ << prop.object()->metaObject()->superClass()->className();
+ }
+ }
+ }
+ }
+ ep->capturedProperties.clear();
+
+ if(bindValueDebug())
+ qWarning() << " Result:" << rv
+ << "(SSE: " << d->sse.isValid() << ")";
+ return rv;
+}
+
+/*!
+ Returns true if the expression results in a constant value.
+ QmlExpression::value() must have been invoked at least once before the
+ return from this method is valid.
+ */
+bool QmlExpression::isConstant() const
+{
+ return d->proxy == 0;
+}
+
+/*!
+ Returns true if the changes are tracked in the expression's value.
+*/
+bool QmlExpression::trackChange() const
+{
+ return d->trackChange;
+}
+
+/*!
+ Set whether changes are tracked in the expression's value to \a trackChange.
+
+ If true, the QmlExpression will monitor properties involved in the
+ expression's evaluation, and call QmlExpression::valueChanged() if they have
+ changed. This allows an application to ensure that any value associated
+ with the result of the expression remains up to date.
+
+ If false, the QmlExpression will not montitor properties involved in the
+ expression's evaluation, and QmlExpression::valueChanged() will never be
+ called. This is more efficient if an application wants a "one off"
+ evaluation of the expression.
+
+ By default, trackChange is true.
+*/
+void QmlExpression::setTrackChange(bool trackChange)
+{
+ d->trackChange = trackChange;
+}
+
+/*!
+ Returns the expression's scope object, if provided, otherwise 0.
+
+ In addition to data provided by the expression's QmlContext, the scope
+ object's properties are also in scope during the expression's evaluation.
+*/
+QObject *QmlExpression::scopeObject() const
+{
+ return d->me;
+}
+
+/*!
+ \class QmlExpression
+ \brief The QmlExpression class evaluates ECMAScript in a QML context.
+*/
+
+/*!
+ \class QmlExpressionObject
+ \brief The QmlExpressionObject class extends QmlExpression with signals and slots.
+
+ To remain as lightweight as possible, QmlExpression does not inherit QObject
+ and consequently cannot use signals or slots. For the cases where this is
+ more convenient in an application, QmlExpressionObject can be used instead.
+
+ QmlExpressionObject behaves identically to QmlExpression, except that the
+ QmlExpressionObject::value() method is a slot, and the
+ QmlExpressionObject::valueChanged() callback is a signal.
+*/
+/*!
+ Create a QmlExpression with the specified \a parent.
+
+ As the expression will not have an associated QmlContext, this will be a
+ null expression object and its value will always be an invalid QVariant.
+*/
+QmlExpressionObject::QmlExpressionObject(QObject *parent)
+: QObject(parent)
+{
+}
+
+/*!
+ Create a QmlExpressionObject with the specified \a parent.
+
+ The \a expression ECMAScript will be executed in the \a ctxt QmlContext.
+ If specified, the \a scope object's properties will also be in scope during
+ the expression's execution.
+*/
+QmlExpressionObject::QmlExpressionObject(QmlContext *ctxt, const QString &expression, QObject *scope, QObject *parent)
+: QObject(parent), QmlExpression(ctxt, expression, scope, true)
+{
+}
+
+/*! \internal */
+QmlExpressionObject::QmlExpressionObject(QmlContext *ctxt, const QString &expr, QObject *scope, bool sse)
+: QmlExpression(ctxt, expr, scope, sse)
+{
+}
+
+/*! \internal */
+QmlExpressionObject::QmlExpressionObject(QmlContext *ctxt, void *d, QmlRefCount *rc, QObject *me)
+: QmlExpression(ctxt, d, rc, me)
+{
+}
+
+/*!
+ Returns the value of the expression, or an invalid QVariant if the
+ expression is invalid or has an error.
+*/
+QVariant QmlExpressionObject::value()
+{
+ return QmlExpression::value();
+}
+
+/*!
+ \fn void QmlExpressionObject::valueChanged()
+
+ Emitted each time the expression value changes from the last time it was
+ evaluated. The expression must have been evaluated at least once (by
+ calling QmlExpressionObject::value()) before this signal will be emitted.
+*/
+
+QmlScriptClass::QmlScriptClass(QmlEngine *bindengine)
+: QScriptClass(bindengine->scriptEngine()), engine(bindengine)
+{
+}
+
+/////////////////////////////////////////////////////////////
+/*
+ The QmlContextScriptClass handles property access for a QmlContext
+ via QtScript.
+ */
+QmlContextScriptClass::QmlContextScriptClass(QmlEngine *bindEngine)
+ : QmlScriptClass(bindEngine)
+{
+}
+
+QmlContextScriptClass::~QmlContextScriptClass()
+{
+}
+
+QScriptClass::QueryFlags
+QmlContextScriptClass::queryProperty(const QScriptValue &object,
+ const QScriptString &name,
+ QueryFlags flags, uint *id)
+{
+ Q_UNUSED(flags);
+#ifdef Q_ENABLE_PERFORMANCE_LOG
+ QFxPerfTimer<QFxPerf::ContextQuery> perf;
+#endif
+ QmlContext *bindContext =
+ static_cast<QmlContext*>(object.data().toQObject());
+ QueryFlags rv = 0;
+
+ QString propName = name.toString();
+
+#ifdef PROPERTY_DEBUG
+ qWarning() << "Query Context:" << propName << bindContext;
+#endif
+
+ *id = InvalidId;
+ if (bindContext->d_func()->variantProperties.contains(propName)) {
+ rv |= HandlesReadAccess;
+ *id = VariantPropertyId;
+ } else if (bindContext->d_func()->properties.contains(propName)) {
+ rv |= HandlesReadAccess;
+ *id = ObjectListPropertyId;
+ }
+
+ for(int ii = 0; !rv && ii < bindContext->d_func()->defaultObjects.count(); ++ii) {
+ rv = engine->d_func()->queryObject(propName, id,
+ bindContext->d_func()->defaultObjects.at(ii));
+ if(rv)
+ *id |= (ii << 24);
+ }
+
+ return rv;
+}
+
+QScriptValue QmlContextScriptClass::property(const QScriptValue &object,
+ const QScriptString &name,
+ uint id)
+{
+#ifdef Q_ENABLE_PERFORMANCE_LOG
+ QFxPerfTimer<QFxPerf::ContextProperty> perf;
+#endif
+ QmlContext *bindContext =
+ static_cast<QmlContext*>(object.data().toQObject());
+
+#ifdef PROPERTY_DEBUG
+ QString propName = name.toString();
+ qWarning() << "Context Property:" << propName << bindContext;
+#endif
+
+ uint basicId = id & QmlScriptClass::ClassIdMask;
+
+ QScriptEngine *scriptEngine = engine->scriptEngine();
+
+ switch (basicId) {
+ case VariantPropertyId:
+ {
+ QString propName = name.toString();
+ QScriptValue rv = scriptEngine->newVariant(bindContext->d_func()->variantProperties[propName]);
+#ifdef PROPERTY_DEBUG
+ qWarning() << "Context Property: Resolved property" << propName
+ << "to context variant property list" << bindContext <<". Value:" << rv.toVariant();
+#endif
+ return rv;
+ }
+ case ObjectListPropertyId:
+ {
+ QString propName = name.toString();
+ QObject *o = bindContext->d_func()->properties[propName];
+ QScriptValue rv = scriptEngine->newObject(engine->d_func()->objectClass, scriptEngine->newVariant(QVariant::fromValue(o)));
+#ifdef PROPERTY_DEBUG
+ qWarning() << "Context Property: Resolved property" << propName
+ << "to context object property list" << bindContext <<". Value:" << rv.toVariant();
+#endif
+ return rv;
+ }
+ default:
+ {
+ int objId = (id & ClassIdSelectorMask) >> 24;
+ QObject *obj = bindContext->d_func()->defaultObjects.at(objId);
+ QScriptValue rv = engine->d_func()->propertyObject(name, obj,
+ id & ~QmlScriptClass::ClassIdSelectorMask);
+ if(rv.isValid()) {
+#ifdef PROPERTY_DEBUG
+ qWarning() << "~Property: Resolved property" << propName
+ << "to context default object" << bindContext << obj <<". Value:" << rv.toVariant();
+#endif
+ return rv;
+ }
+ break;
+ }
+ }
+
+ return QScriptValue();
+}
+
+void QmlContextScriptClass::setProperty(QScriptValue &object,
+ const QScriptString &name,
+ uint id,
+ const QScriptValue &value)
+{
+ Q_UNUSED(name);
+
+#ifdef Q_ENABLE_PERFORMANCE_LOG
+ QFxPerfTimer<QFxPerf::ObjectSetProperty> perf;
+#endif
+ QmlContext *bindContext =
+ static_cast<QmlContext*>(object.data().toQObject());
+
+#ifdef PROPERTY_DEBUG
+ QString propName = name.toString();
+ qWarning() << "Set QmlObject Property" << name.toString() << value.toVariant();
+#endif
+
+ int objIdx = (id & QmlScriptClass::ClassIdSelectorMask) >> 24;
+ QObject *obj = bindContext->d_func()->defaultObjects.at(objIdx);
+
+ QScriptEngine *scriptEngine = engine->scriptEngine();
+ QScriptValue oldact = scriptEngine->currentContext()->activationObject();
+ scriptEngine->currentContext()->setActivationObject(scriptEngine->globalObject());
+
+ QmlMetaProperty prop;
+ prop.restore(id, obj);
+
+ QVariant v;
+ QObject *data = value.data().toQObject();
+ if (data) {
+ v = QVariant::fromValue(data);
+ } else {
+ v = value.toVariant();
+ }
+ prop.write(v);
+
+ scriptEngine->currentContext()->setActivationObject(oldact);
+}
+
+/////////////////////////////////////////////////////////////
+/*
+ The QmlObjectScriptClass handles property access for QObjects
+ via QtScript.
+ */
+QmlObjectScriptClass::QmlObjectScriptClass(QmlEngine *bindEngine)
+ : QmlScriptClass(bindEngine)
+{
+ engine = bindEngine;
+}
+
+QmlObjectScriptClass::~QmlObjectScriptClass()
+{
+}
+
+QScriptClass::QueryFlags QmlObjectScriptClass::queryProperty(const QScriptValue &object,
+ const QScriptString &name,
+ QueryFlags flags, uint *id)
+{
+ Q_UNUSED(flags);
+#ifdef Q_ENABLE_PERFORMANCE_LOG
+ QFxPerfTimer<QFxPerf::ObjectQuery> perf;
+#endif
+ QObject *obj = object.data().toQObject();
+ QueryFlags rv = 0;
+ QString propName = name.toString();
+
+#ifdef PROPERTY_DEBUG
+ qWarning() << "Query QmlObject:" << propName << obj;
+#endif
+
+ if (obj)
+ rv = engine->d_func()->queryObject(propName, id, obj);
+
+ return rv;
+}
+
+QScriptValue QmlObjectScriptClass::property(const QScriptValue &object,
+ const QScriptString &name,
+ uint id)
+{
+#ifdef Q_ENABLE_PERFORMANCE_LOG
+ QFxPerfTimer<QFxPerf::ObjectProperty> perf;
+#endif
+ QObject *obj = object.data().toQObject();
+
+#ifdef PROPERTY_DEBUG
+ QString propName = name.toString();
+ qWarning() << "QmlObject Property:" << propName << obj;
+#endif
+
+ QScriptValue rv = engine->d_func()->propertyObject(name, obj, id);
+ if(rv.isValid()) {
+#ifdef PROPERTY_DEBUG
+ qWarning() << "~Property: Resolved property" << propName
+ << "to object" << obj <<". Value:" << rv.toVariant();
+#endif
+ return rv;
+ }
+
+ return QScriptValue();
+}
+
+void QmlObjectScriptClass::setProperty(QScriptValue &object,
+ const QScriptString &name,
+ uint id,
+ const QScriptValue &value)
+{
+ Q_UNUSED(name);
+
+#ifdef Q_ENABLE_PERFORMANCE_LOG
+ QFxPerfTimer<QFxPerf::ObjectSetProperty> perf;
+#endif
+ QObject *obj = object.data().toQObject();
+
+#ifdef PROPERTY_DEBUG
+ QString propName = name.toString();
+ qWarning() << "Set QmlObject Property" << name.toString() << value.toVariant();
+#endif
+
+ QScriptEngine *scriptEngine = engine->scriptEngine();
+ QScriptValue oldact = scriptEngine->currentContext()->activationObject();
+ scriptEngine->currentContext()->setActivationObject(scriptEngine->globalObject());
+
+ QmlMetaProperty prop;
+ prop.restore(id, obj);
+
+ QVariant v;
+ QObject *data = value.data().toQObject();
+ if (data) {
+ v = QVariant::fromValue(data);
+ } else {
+ v = value.toVariant();
+ }
+ prop.write(v);
+
+ scriptEngine->currentContext()->setActivationObject(oldact);
+}
+
+QT_END_NAMESPACE