diff options
author | Aaron Kennedy <aaron.kennedy@nokia.com> | 2009-07-03 01:21:30 (GMT) |
---|---|---|
committer | Aaron Kennedy <aaron.kennedy@nokia.com> | 2009-07-03 01:21:30 (GMT) |
commit | 67a9e4fe6e35bf517bd2f99540cf88a8e567b0ca (patch) | |
tree | 9bde22afe3289835d2053a7a025c83a403786407 /src/declarative/qml | |
parent | 1ee2bf9f693799c06f10bd6a02ecc65874fb6622 (diff) | |
download | Qt-67a9e4fe6e35bf517bd2f99540cf88a8e567b0ca.zip Qt-67a9e4fe6e35bf517bd2f99540cf88a8e567b0ca.tar.gz Qt-67a9e4fe6e35bf517bd2f99540cf88a8e567b0ca.tar.bz2 |
Move QmlExpression into its own file
Diffstat (limited to 'src/declarative/qml')
-rw-r--r-- | src/declarative/qml/qml.pri | 2 | ||||
-rw-r--r-- | src/declarative/qml/qmlcontext.cpp | 4 | ||||
-rw-r--r-- | src/declarative/qml/qmlcontext.h | 1 | ||||
-rw-r--r-- | src/declarative/qml/qmlengine.cpp | 523 | ||||
-rw-r--r-- | src/declarative/qml/qmlengine.h | 1 | ||||
-rw-r--r-- | src/declarative/qml/qmlengine_p.h | 71 | ||||
-rw-r--r-- | src/declarative/qml/qmlexpression.cpp | 604 | ||||
-rw-r--r-- | src/declarative/qml/qmlexpression.h | 2 | ||||
-rw-r--r-- | src/declarative/qml/qmlexpression_p.h | 138 |
9 files changed, 749 insertions, 597 deletions
diff --git a/src/declarative/qml/qml.pri b/src/declarative/qml/qml.pri index efe4d3f..fa09935 100644 --- a/src/declarative/qml/qml.pri +++ b/src/declarative/qml/qml.pri @@ -2,6 +2,7 @@ SOURCES += qml/qmlparser.cpp \ qml/qmlinstruction.cpp \ qml/qmlvmemetaobject.cpp \ qml/qmlengine.cpp \ + qml/qmlexpression.cpp \ qml/qmlbindablevalue.cpp \ qml/qmlmetaproperty.cpp \ qml/qmlcomponent.cpp \ @@ -45,6 +46,7 @@ HEADERS += qml/qmlparser_p.h \ qml/qmlvme_p.h \ qml/qmlcompiler_p.h \ qml/qmlengine_p.h \ + qml/qmlexpression_p.h \ qml/qmlprivate.h \ qml/qmldom.h \ qml/qmldom_p.h \ diff --git a/src/declarative/qml/qmlcontext.cpp b/src/declarative/qml/qmlcontext.cpp index e5016f2..b590596 100644 --- a/src/declarative/qml/qmlcontext.cpp +++ b/src/declarative/qml/qmlcontext.cpp @@ -41,12 +41,12 @@ #include <qmlcontext.h> #include <private/qmlcontext_p.h> +#include <private/qmlexpression_p.h> #include <private/qmlengine_p.h> #include <qmlengine.h> #include <qscriptengine.h> #include <QtCore/qvarlengtharray.h> - -#include <qdebug.h> +#include <QtCore/qdebug.h> // 6-bits #define MAXIMUM_DEFAULT_OBJECTS 63 diff --git a/src/declarative/qml/qmlcontext.h b/src/declarative/qml/qmlcontext.h index ce5fe52..f5858cb 100644 --- a/src/declarative/qml/qmlcontext.h +++ b/src/declarative/qml/qmlcontext.h @@ -89,6 +89,7 @@ private: friend class QmlEngine; friend class QmlEnginePrivate; friend class QmlExpression; + friend class QmlExpressionPrivate; friend class QmlBasicScript; friend class QmlContextScriptClass; friend class QmlObjectScriptClass; diff --git a/src/declarative/qml/qmlengine.cpp b/src/declarative/qml/qmlengine.cpp index ad3f4b6..f26e389 100644 --- a/src/declarative/qml/qmlengine.cpp +++ b/src/declarative/qml/qmlengine.cpp @@ -925,457 +925,6 @@ QScriptValue QmlEngine::createQMLObject(QScriptContext *ctxt, QScriptEngine *eng return engine->nullValue(); } -QmlExpressionPrivate::QmlExpressionPrivate(QmlExpression *b) -: q(b), ctxt(0), sseData(0), proxy(0), me(0), trackChange(false), line(-1), id(0), log(0) -{ -} - -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), line(-1), id(0), log(0) -{ -} - -QmlExpressionPrivate::QmlExpressionPrivate(QmlExpression *b, const QString &expr) -: q(b), ctxt(0), expression(expr), sseData(0), proxy(0), me(0), trackChange(true), line(-1), id(0), log(0) -{ -} - -QmlExpressionPrivate::~QmlExpressionPrivate() -{ - sse.deleteScriptState(sseData); - sseData = 0; - delete proxy; - delete log; -} - -/*! - 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; - if(ctxt && ctxt->engine()) - d->id = ctxt->engine()->d_func()->getUniqueId(); - if(ctxt) - ctxt->d_func()->childExpressions.insert(this); - 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)) -{ - d->ctxt = ctxt; - if(ctxt && ctxt->engine()) - d->id = ctxt->engine()->d_func()->getUniqueId(); - if(ctxt) - ctxt->d_func()->childExpressions.insert(this); - d->me = scope; -} - -/*! - Destroy the QmlExpression instance. -*/ -QmlExpression::~QmlExpression() -{ - if (d->ctxt) - d->ctxt->d_func()->childExpressions.remove(this); - 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?d->ctxt->engine():0; -} - -/*! - Returns the 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; - - d->sse.clear(); -} - -/*! - 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() -{ -} - -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() -{ - QVariant rv; - if (!d->ctxt || !engine() || (!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(), d->fileName.toString(), d->line); - if (scriptEngine->hasUncaughtException()) { - if (scriptEngine->uncaughtException().isError()){ - QScriptValue exception = scriptEngine->uncaughtException(); - if (!exception.property(QLatin1String("fileName")).toString().isEmpty()){ - qWarning() << exception.property(QLatin1String("fileName")).toString() - << scriptEngine->uncaughtExceptionLineNumber() - << exception.toString(); - - } else { - qWarning() << exception.toString(); - } - } - } - - 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.isObject() && - !svalue.isNumber() && - !svalue.isString() && - !svalue.isDate() && - !svalue.isError() && - !svalue.isFunction() && - !svalue.isNull() && - !svalue.isQMetaObject() && - !svalue.isQObject() && - !svalue.isRegExp()) { - QScriptValue objValue = svalue.data(); - if (objValue.isValid()) { - QVariant var = objValue.toVariant(); - if (var.userType() >= (int)QVariant::UserType && - QmlMetaType::isObject(var.userType())) - rv = var; - } - } - 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(qmlDebugger()) { - QmlExpressionLog log; - log.setTime(engine()->d_func()->getUniqueId()); - log.setExpression(expression()); - log.setResult(rv); - - for (int ii = 0; ii < ep->capturedProperties.count(); ++ii) { - const QmlEnginePrivate::CapturedProperty &prop = - ep->capturedProperties.at(ii); - - if (prop.notifyIndex != -1) { - QMetaObject::connect(prop.object, prop.notifyIndex, - d->proxy, changedIndex); - } else { - // ### FIXME - //QString warn = QLatin1String("Expression depends on property without a NOTIFY signal: [") + QLatin1String(prop.object->metaObject()->className()) + QLatin1String("].") + prop.name; - //log.addWarning(warn); - } - } - d->addLog(log); - - } else { - for (int ii = 0; ii < ep->capturedProperties.count(); ++ii) { - const QmlEnginePrivate::CapturedProperty &prop = - ep->capturedProperties.at(ii); - - if (prop.notifyIndex != -1) - QMetaObject::connect(prop.object, prop.notifyIndex, - d->proxy, changedIndex); - } - } - } else { - QmlExpressionLog log; - log.setTime(engine()->d_func()->getUniqueId()); - log.setExpression(expression()); - log.setResult(rv); - d->addLog(log); - } - - } else { - if(qmlDebugger()) { - QmlExpressionLog log; - log.setTime(engine()->d_func()->getUniqueId()); - log.setExpression(expression()); - log.setResult(rv); - d->addLog(log); - } - } - - ep->capturedProperties.clear(); - - 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; -} - -/*! - Set the location of this expression to \a line of \a fileName. This information - is used by the script engine. -*/ -void QmlExpression::setSourceLocation(const QUrl &fileName, int line) -{ - d->fileName = fileName; - d->line = line; -} - -/*! - 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; -} - -/*! - \internal -*/ -quint32 QmlExpression::id() const -{ - return d->id; -} - -/*! - \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) -{ -} - -/*! \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) { @@ -1628,76 +1177,4 @@ void QmlObjectScriptClass::setProperty(QScriptValue &object, scriptEngine->currentContext()->setActivationObject(oldact); } -void QmlExpressionPrivate::addLog(const QmlExpressionLog &l) -{ - if (!log) - log = new QList<QmlExpressionLog>(); - log->append(l); -} - -QmlExpressionLog::QmlExpressionLog() -{ -} - -QmlExpressionLog::QmlExpressionLog(const QmlExpressionLog &o) -: m_time(o.m_time), - m_expression(o.m_expression), - m_result(o.m_result), - m_warnings(o.m_warnings) -{ -} - -QmlExpressionLog::~QmlExpressionLog() -{ -} - -QmlExpressionLog &QmlExpressionLog::operator=(const QmlExpressionLog &o) -{ - m_time = o.m_time; - m_expression = o.m_expression; - m_result = o.m_result; - m_warnings = o.m_warnings; - return *this; -} - -void QmlExpressionLog::setTime(quint32 time) -{ - m_time = time; -} - -quint32 QmlExpressionLog::time() const -{ - return m_time; -} - -QString QmlExpressionLog::expression() const -{ - return m_expression; -} - -void QmlExpressionLog::setExpression(const QString &e) -{ - m_expression = e; -} - -QStringList QmlExpressionLog::warnings() const -{ - return m_warnings; -} - -void QmlExpressionLog::addWarning(const QString &w) -{ - m_warnings << w; -} - -QVariant QmlExpressionLog::result() const -{ - return m_result; -} - -void QmlExpressionLog::setResult(const QVariant &r) -{ - m_result = r; -} - QT_END_NAMESPACE diff --git a/src/declarative/qml/qmlengine.h b/src/declarative/qml/qmlengine.h index f114379..42359e1 100644 --- a/src/declarative/qml/qmlengine.h +++ b/src/declarative/qml/qmlengine.h @@ -104,6 +104,7 @@ private: friend class QmlContext; friend class QmlContextPrivate; friend class QmlExpression; + friend class QmlExpressionPrivate; friend class QmlBasicScript; friend class QmlVME; friend class QmlComponent; diff --git a/src/declarative/qml/qmlengine_p.h b/src/declarative/qml/qmlengine_p.h index 93ae704..e91c4ee 100644 --- a/src/declarative/qml/qmlengine_p.h +++ b/src/declarative/qml/qmlengine_p.h @@ -169,23 +169,6 @@ public: } }; - -class BindExpressionProxy : public QObject -{ -Q_OBJECT -public: - BindExpressionProxy(QmlExpression *be) - :e(be) - { - } - -private: - QmlExpression *e; - -private Q_SLOTS: - void changed(); -}; - class QmlScriptClass : public QScriptClass { public: @@ -247,60 +230,6 @@ public: const QScriptValue &value); }; -class QmlExpressionLog -{ -public: - QmlExpressionLog(); - QmlExpressionLog(const QmlExpressionLog &); - ~QmlExpressionLog(); - - QmlExpressionLog &operator=(const QmlExpressionLog &); - - void setTime(quint32); - quint32 time() const; - - QString expression() const; - void setExpression(const QString &); - - QStringList warnings() const; - void addWarning(const QString &); - - QVariant result() const; - void setResult(const QVariant &); - -private: - quint32 m_time; - QString m_expression; - QVariant m_result; - QStringList m_warnings; -}; - -class QmlExpressionPrivate -{ -public: - QmlExpressionPrivate(QmlExpression *); - QmlExpressionPrivate(QmlExpression *, const QString &expr); - QmlExpressionPrivate(QmlExpression *, void *expr, QmlRefCount *rc); - ~QmlExpressionPrivate(); - - QmlExpression *q; - QmlContext *ctxt; - QString expression; - QmlBasicScript sse; - void *sseData; - BindExpressionProxy *proxy; - QObject *me; - bool trackChange; - - QUrl fileName; - int line; - - quint32 id; - - void addLog(const QmlExpressionLog &); - QList<QmlExpressionLog> *log; -}; - QT_END_NAMESPACE #endif // QMLENGINE_P_H diff --git a/src/declarative/qml/qmlexpression.cpp b/src/declarative/qml/qmlexpression.cpp new file mode 100644 index 0000000..1e7b149 --- /dev/null +++ b/src/declarative/qml/qmlexpression.cpp @@ -0,0 +1,604 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#include "qmlexpression.h" +#include "qmlexpression_p.h" +#include "qmlengine_p.h" +#include "qmlcontext_p.h" +#include "QtCore/qdebug.h" + +Q_DECLARE_METATYPE(QList<QObject *>); + +QT_BEGIN_NAMESPACE + +DEFINE_BOOL_CONFIG_OPTION(qmlDebugger, QML_DEBUGGER) + +QmlExpressionPrivate::QmlExpressionPrivate(QmlExpression *b) +: q(b), ctxt(0), sseData(0), proxy(0), me(0), trackChange(false), line(-1), id(0), log(0) +{ +} + +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), line(-1), id(0), log(0) +{ +} + +QmlExpressionPrivate::QmlExpressionPrivate(QmlExpression *b, const QString &expr) +: q(b), ctxt(0), expression(expr), sseData(0), proxy(0), me(0), trackChange(true), line(-1), id(0), log(0) +{ +} + +QmlExpressionPrivate::~QmlExpressionPrivate() +{ + sse.deleteScriptState(sseData); + sseData = 0; + delete proxy; + delete log; +} + +/*! + 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; + if(ctxt && ctxt->engine()) + d->id = ctxt->engine()->d_func()->getUniqueId(); + if(ctxt) + ctxt->d_func()->childExpressions.insert(this); + 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)) +{ + d->ctxt = ctxt; + if(ctxt && ctxt->engine()) + d->id = ctxt->engine()->d_func()->getUniqueId(); + if(ctxt) + ctxt->d_func()->childExpressions.insert(this); + d->me = scope; +} + +/*! + Destroy the QmlExpression instance. +*/ +QmlExpression::~QmlExpression() +{ + if (d->ctxt) + d->ctxt->d_func()->childExpressions.remove(this); + 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?d->ctxt->engine():0; +} + +/*! + Returns the 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; + + d->sse.clear(); +} + +/*! + 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() +{ +} + +QVariant QmlExpressionPrivate::evalSSE(QmlBasicScript::CacheState &cacheState) +{ +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxPerfTimer<QFxPerf::BindValueSSE> perfsse; +#endif + + QmlContextPrivate *ctxtPriv = ctxt->d_func(); + if (me) + ctxtPriv->defaultObjects.insert(ctxtPriv->highPriorityCount , me); + + if (!sseData) + sseData = sse.newScriptState(); + QVariant rv = sse.run(ctxt, sseData, &cacheState); + + if (me) + ctxtPriv->defaultObjects.removeAt(ctxtPriv->highPriorityCount); + + return rv; +} + +QVariant QmlExpressionPrivate::evalQtScript() +{ +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxPerfTimer<QFxPerf::BindValueQt> perfqt; +#endif + + QmlContextPrivate *ctxtPriv = ctxt->d_func(); + QmlEngine *engine = ctxt->engine(); + + if (me) + ctxtPriv->defaultObjects.insert(ctxtPriv->highPriorityCount, me); + + QScriptEngine *scriptEngine = engine->scriptEngine(); + QScriptValueList oldScopeChain = + scriptEngine->currentContext()->scopeChain(); + + for (int i = 0; i < oldScopeChain.size(); ++i) + scriptEngine->currentContext()->popScope(); + for (int i = ctxtPriv->scopeChain.size() - 1; i > -1; --i) + scriptEngine->currentContext()->pushScope(ctxtPriv->scopeChain.at(i)); + + QScriptValue svalue = + scriptEngine->evaluate(expression, fileName.toString(), line); + + if (scriptEngine->hasUncaughtException()) { + if (scriptEngine->uncaughtException().isError()){ + QScriptValue exception = scriptEngine->uncaughtException(); + QLatin1String fileNameProp("fileName"); + if (!exception.property(fileNameProp).toString().isEmpty()){ + qWarning() << exception.property(fileNameProp).toString() + << scriptEngine->uncaughtExceptionLineNumber() + << exception.toString(); + } else { + qWarning() << exception.toString(); + } + } + } + + if (me) + ctxtPriv->defaultObjects.removeAt(ctxtPriv->highPriorityCount); + + QVariant rv; + + 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.isObject() && + !svalue.isNumber() && + !svalue.isString() && + !svalue.isDate() && + !svalue.isError() && + !svalue.isFunction() && + !svalue.isNull() && + !svalue.isQMetaObject() && + !svalue.isQObject() && + !svalue.isRegExp()) { + QScriptValue objValue = svalue.data(); + if (objValue.isValid()) { + QVariant var = objValue.toVariant(); + if (var.userType() >= (int)QVariant::UserType && + QmlMetaType::isObject(var.userType())) + rv = var; + } + } + if (rv.isNull()) + rv = svalue.toVariant(); + + for (int i = 0; i < ctxtPriv->scopeChain.size(); ++i) + scriptEngine->currentContext()->popScope(); + for (int i = oldScopeChain.size() - 1; i > -1; --i) + scriptEngine->currentContext()->pushScope(oldScopeChain.at(i)); + + return rv; +} + +/*! + Returns the value of the expression, or an invalid QVariant if the + expression is invalid or has an error. +*/ +QVariant QmlExpression::value() +{ + QVariant rv; + if (!d->ctxt || !engine() || (!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()) { + rv = d->evalSSE(cacheState); + } else { + rv = d->evalQtScript(); + } + + 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 QmlExpressionBindProxy(this); + + static int changedIndex = -1; + if (changedIndex == -1) + changedIndex = QmlExpressionBindProxy::staticMetaObject.indexOfSlot("changed()"); + + if(qmlDebugger()) { + QmlExpressionLog log; + log.setTime(engine()->d_func()->getUniqueId()); + log.setExpression(expression()); + log.setResult(rv); + + for (int ii = 0; ii < ep->capturedProperties.count(); ++ii) { + const QmlEnginePrivate::CapturedProperty &prop = + ep->capturedProperties.at(ii); + + if (prop.notifyIndex != -1) { + QMetaObject::connect(prop.object, prop.notifyIndex, + d->proxy, changedIndex); + } else { + // ### FIXME + //QString warn = QLatin1String("Expression depends on property without a NOTIFY signal: [") + QLatin1String(prop.object->metaObject()->className()) + QLatin1String("].") + prop.name; + //log.addWarning(warn); + } + } + d->addLog(log); + + } else { + for (int ii = 0; ii < ep->capturedProperties.count(); ++ii) { + const QmlEnginePrivate::CapturedProperty &prop = + ep->capturedProperties.at(ii); + + if (prop.notifyIndex != -1) + QMetaObject::connect(prop.object, prop.notifyIndex, + d->proxy, changedIndex); + } + } + } else { + QmlExpressionLog log; + log.setTime(engine()->d_func()->getUniqueId()); + log.setExpression(expression()); + log.setResult(rv); + d->addLog(log); + } + + } else { + if(qmlDebugger()) { + QmlExpressionLog log; + log.setTime(engine()->d_func()->getUniqueId()); + log.setExpression(expression()); + log.setResult(rv); + d->addLog(log); + } + } + + ep->capturedProperties.clear(); + + 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; +} + +/*! + Set the location of this expression to \a line of \a fileName. This information + is used by the script engine. +*/ +void QmlExpression::setSourceLocation(const QUrl &fileName, int line) +{ + d->fileName = fileName; + d->line = line; +} + +/*! + 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; +} + +/*! + \internal +*/ +quint32 QmlExpression::id() const +{ + return d->id; +} + +/*! + \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) +{ +} + +/*! \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. +*/ + +void QmlExpressionPrivate::addLog(const QmlExpressionLog &l) +{ + if (!log) + log = new QList<QmlExpressionLog>(); + log->append(l); +} + +QmlExpressionLog::QmlExpressionLog() +{ +} + +QmlExpressionLog::QmlExpressionLog(const QmlExpressionLog &o) +: m_time(o.m_time), + m_expression(o.m_expression), + m_result(o.m_result), + m_warnings(o.m_warnings) +{ +} + +QmlExpressionLog::~QmlExpressionLog() +{ +} + +QmlExpressionLog &QmlExpressionLog::operator=(const QmlExpressionLog &o) +{ + m_time = o.m_time; + m_expression = o.m_expression; + m_result = o.m_result; + m_warnings = o.m_warnings; + return *this; +} + +void QmlExpressionLog::setTime(quint32 time) +{ + m_time = time; +} + +quint32 QmlExpressionLog::time() const +{ + return m_time; +} + +QString QmlExpressionLog::expression() const +{ + return m_expression; +} + +void QmlExpressionLog::setExpression(const QString &e) +{ + m_expression = e; +} + +QStringList QmlExpressionLog::warnings() const +{ + return m_warnings; +} + +void QmlExpressionLog::addWarning(const QString &w) +{ + m_warnings << w; +} + +QVariant QmlExpressionLog::result() const +{ + return m_result; +} + +void QmlExpressionLog::setResult(const QVariant &r) +{ + m_result = r; +} + + +QT_END_NAMESPACE + diff --git a/src/declarative/qml/qmlexpression.h b/src/declarative/qml/qmlexpression.h index e8cac7a..3d8f8df 100644 --- a/src/declarative/qml/qmlexpression.h +++ b/src/declarative/qml/qmlexpression.h @@ -86,7 +86,7 @@ protected: virtual void valueChanged(); private: - friend class BindExpressionProxy; + friend class QmlExpressionBindProxy; friend class QmlDebugger; friend class QmlContext; QmlExpressionPrivate *d; diff --git a/src/declarative/qml/qmlexpression_p.h b/src/declarative/qml/qmlexpression_p.h new file mode 100644 index 0000000..5883125 --- /dev/null +++ b/src/declarative/qml/qmlexpression_p.h @@ -0,0 +1,138 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QMLEXPRESSION_P_H +#define QMLEXPRESSION_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qmlbasicscript_p.h" +#include "qmlexpression.h" + +QT_BEGIN_NAMESPACE + +class QmlExpression; +class QString; +class QmlExpressionLog; +class QmlExpressionBindProxy; +class QmlExpressionPrivate +{ +public: + QmlExpressionPrivate(QmlExpression *); + QmlExpressionPrivate(QmlExpression *, const QString &expr); + QmlExpressionPrivate(QmlExpression *, void *expr, QmlRefCount *rc); + ~QmlExpressionPrivate(); + + QmlExpression *q; + QmlContext *ctxt; + QString expression; + QmlBasicScript sse; + void *sseData; + QmlExpressionBindProxy *proxy; + QObject *me; + bool trackChange; + + QUrl fileName; + int line; + + quint32 id; + + void addLog(const QmlExpressionLog &); + QList<QmlExpressionLog> *log; + + QVariant evalSSE(QmlBasicScript::CacheState &cacheState); + QVariant evalQtScript(); +}; + +class QmlExpressionBindProxy : public QObject +{ +Q_OBJECT +public: + QmlExpressionBindProxy(QmlExpression *be) + :e(be) { } + +private: + QmlExpression *e; + +private Q_SLOTS: + void changed() { e->valueChanged(); } +}; + +class QmlExpressionLog +{ +public: + QmlExpressionLog(); + QmlExpressionLog(const QmlExpressionLog &); + ~QmlExpressionLog(); + + QmlExpressionLog &operator=(const QmlExpressionLog &); + + void setTime(quint32); + quint32 time() const; + + QString expression() const; + void setExpression(const QString &); + + QStringList warnings() const; + void addWarning(const QString &); + + QVariant result() const; + void setResult(const QVariant &); + +private: + quint32 m_time; + QString m_expression; + QVariant m_result; + QStringList m_warnings; +}; + +QT_END_NAMESPACE + +#endif // QMLEXPRESSION_P_H |