diff options
author | Alan Alpert <alan.alpert@nokia.com> | 2009-07-03 06:14:22 (GMT) |
---|---|---|
committer | Alan Alpert <alan.alpert@nokia.com> | 2009-07-03 06:14:22 (GMT) |
commit | 26fab96a7827cf7ac250fb434715840195258608 (patch) | |
tree | f43816824fb34db0db29210ac4c1803d1baeff5f /src/declarative/qml | |
parent | c329d989054ba21698a2de9b73a1e005e49ba916 (diff) | |
parent | 6c7b88af807cbd2b4d824b8fca0f199ae1413432 (diff) | |
download | Qt-26fab96a7827cf7ac250fb434715840195258608.zip Qt-26fab96a7827cf7ac250fb434715840195258608.tar.gz Qt-26fab96a7827cf7ac250fb434715840195258608.tar.bz2 |
Merge branch 'kinetic-declarativeui' of git@scm.dev.nokia.troll.no:qt/kinetic into kinetic-declarativeui
Diffstat (limited to 'src/declarative/qml')
-rw-r--r-- | src/declarative/qml/qml.pri | 2 | ||||
-rw-r--r-- | src/declarative/qml/qmlbasicscript.cpp | 32 | ||||
-rw-r--r-- | src/declarative/qml/qmlcompiler.cpp | 65 | ||||
-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 | 529 | ||||
-rw-r--r-- | src/declarative/qml/qmlengine.h | 1 | ||||
-rw-r--r-- | src/declarative/qml/qmlengine_p.h | 76 | ||||
-rw-r--r-- | src/declarative/qml/qmlexpression.cpp | 624 | ||||
-rw-r--r-- | src/declarative/qml/qmlexpression.h | 2 | ||||
-rw-r--r-- | src/declarative/qml/qmlexpression_p.h | 138 | ||||
-rw-r--r-- | src/declarative/qml/qmlmetaproperty.cpp | 3 | ||||
-rw-r--r-- | src/declarative/qml/qmlparser.cpp | 2 | ||||
-rw-r--r-- | src/declarative/qml/qmlparser_p.h | 1 | ||||
-rw-r--r-- | src/declarative/qml/qmlscriptparser.cpp | 15 | ||||
-rw-r--r-- | src/declarative/qml/qmlvme.cpp | 2 | ||||
-rw-r--r-- | src/declarative/qml/qmlvmemetaobject.cpp | 243 | ||||
-rw-r--r-- | src/declarative/qml/qmlvmemetaobject_p.h | 59 | ||||
-rw-r--r-- | src/declarative/qml/rewriter/rewriter.cpp | 9 | ||||
-rw-r--r-- | src/declarative/qml/rewriter/rewriter_p.h | 3 |
20 files changed, 1029 insertions, 782 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/qmlbasicscript.cpp b/src/declarative/qml/qmlbasicscript.cpp index 0cfb587..478491f 100644 --- a/src/declarative/qml/qmlbasicscript.cpp +++ b/src/declarative/qml/qmlbasicscript.cpp @@ -603,7 +603,10 @@ bool QmlBasicScriptCompiler::parseName(AST::Node *node, instr.type = ScriptInstruction::FetchD0Constant; instr.constant.idx = d0Idx; QMetaProperty prop = context->metaObject()->property(d0Idx); - instr.constant.notify = prop.notifySignalIndex(); + if (prop.isConstant()) + instr.constant.notify = 0; + else + instr.constant.notify = prop.notifySignalIndex(); instr.constant.type = prop.userType(); } else if (d1Idx != -1) { @@ -611,7 +614,10 @@ bool QmlBasicScriptCompiler::parseName(AST::Node *node, instr.type = ScriptInstruction::FetchD1Constant; instr.constant.idx = d1Idx; QMetaProperty prop = component->metaObject()->property(d1Idx); - instr.constant.notify = prop.notifySignalIndex(); + if (prop.isConstant()) + instr.constant.notify = 0; + else + instr.constant.notify = prop.notifySignalIndex(); instr.constant.type = prop.userType(); } else { @@ -635,7 +641,10 @@ bool QmlBasicScriptCompiler::parseName(AST::Node *node, instr.type = ScriptInstruction::FetchConstant; instr.constant.idx = idx; QMetaProperty prop = loadedType->metaObject()->property(idx); - instr.constant.notify = prop.notifySignalIndex(); + if (prop.isConstant()) + instr.constant.notify = 0; + else + instr.constant.notify = prop.notifySignalIndex(); instr.constant.type = prop.userType(); } else { int nref = data.count(); @@ -804,7 +813,7 @@ QVariant QmlBasicScript::run(QmlContext *context, void *voidCache, CacheState *c { stack.push(contextPrivate->propertyValues.at(instr.fetch.idx)); enginePrivate->capturedProperties << - QmlEnginePrivate::CapturedProperty(context, contextPrivate->notifyIndex + instr.fetch.idx); + QmlEnginePrivate::CapturedProperty(context, -1, contextPrivate->notifyIndex + instr.fetch.idx); state = Reset; } break; @@ -814,8 +823,9 @@ QVariant QmlBasicScript::run(QmlContext *context, void *voidCache, CacheState *c QObject *obj = contextPrivate->defaultObjects.at(0); stack.push(fetch_value(obj, instr.constant.idx, instr.constant.type)); - enginePrivate->capturedProperties << - QmlEnginePrivate::CapturedProperty(obj, instr.constant.notify); + if (instr.constant.notify != 0) + enginePrivate->capturedProperties << + QmlEnginePrivate::CapturedProperty(obj, instr.constant.idx, instr.constant.notify); state = Reset; } break; @@ -825,8 +835,9 @@ QVariant QmlBasicScript::run(QmlContext *context, void *voidCache, CacheState *c QObject *obj = contextPrivate->defaultObjects.at(1); stack.push(fetch_value(obj, instr.constant.idx, instr.constant.type)); - enginePrivate->capturedProperties << - QmlEnginePrivate::CapturedProperty(obj, instr.constant.notify); + if (instr.constant.notify != 0) + enginePrivate->capturedProperties << + QmlEnginePrivate::CapturedProperty(obj, instr.constant.idx, instr.constant.notify); state = Reset; } break; @@ -837,8 +848,9 @@ QVariant QmlBasicScript::run(QmlContext *context, void *voidCache, CacheState *c QObject *obj = qvariant_cast<QObject *>(o); stack.push(fetch_value(obj, instr.constant.idx, instr.constant.type)); - enginePrivate->capturedProperties << - QmlEnginePrivate::CapturedProperty(obj, instr.constant.notify); + if (instr.constant.notify != 0) + enginePrivate->capturedProperties << + QmlEnginePrivate::CapturedProperty(obj, instr.constant.idx, instr.constant.notify); state = Reset; } break; diff --git a/src/declarative/qml/qmlcompiler.cpp b/src/declarative/qml/qmlcompiler.cpp index aa2cf84..b04c932 100644 --- a/src/declarative/qml/qmlcompiler.cpp +++ b/src/declarative/qml/qmlcompiler.cpp @@ -63,6 +63,7 @@ #include <private/qmlcontext_p.h> #include <private/qmlcomponent_p.h> #include "parser/qmljsast_p.h" +#include <private/qmlvmemetaobject_p.h> #include "qmlscriptparser_p.h" @@ -1406,6 +1407,8 @@ bool QmlCompiler::compileDynamicMeta(QmlParser::Object *obj, int preAlias) obj->dynamicSlots.isEmpty()) return true; + QByteArray dynamicData(sizeof(QmlVMEMetaData), (char)0); + QMetaObjectBuilder builder; if (obj->metatype) builder.setClassName(QByteArray(obj->metatype->className()) + "QML"); @@ -1420,41 +1423,65 @@ bool QmlCompiler::compileDynamicMeta(QmlParser::Object *obj, int preAlias) builder.addClassInfo("DefaultProperty", p.name); QByteArray type; + int propertyType; switch(p.type) { case Object::DynamicProperty::Alias: hasAlias = true; continue; break; case Object::DynamicProperty::Variant: + propertyType = -1; type = "QVariant"; break; case Object::DynamicProperty::Int: + propertyType = QVariant::Int; type = "int"; break; case Object::DynamicProperty::Bool: + propertyType = QVariant::Bool; type = "bool"; break; case Object::DynamicProperty::Real: + propertyType = QVariant::Double; type = "double"; break; case Object::DynamicProperty::String: + propertyType = QVariant::String; type = "QString"; break; case Object::DynamicProperty::Url: + propertyType = QVariant::Url; type = "QUrl"; break; case Object::DynamicProperty::Color: + propertyType = QVariant::Color; type = "QColor"; break; case Object::DynamicProperty::Date: + propertyType = QVariant::Date; type = "QDate"; break; } + ((QmlVMEMetaData *)dynamicData.data())->propertyCount++; + QmlVMEMetaData::PropertyData propertyData = { propertyType }; + dynamicData.append((char *)&propertyData, sizeof(propertyData)); + builder.addSignal(p.name + "Changed()"); builder.addProperty(p.name, type, ii); } + if (preAlias != -1) { + for (int ii = 0; ii < obj->dynamicProperties.count(); ++ii) { + const Object::DynamicProperty &p = obj->dynamicProperties.at(ii); + + if (p.type == Object::DynamicProperty::Alias) { + ((QmlVMEMetaData *)dynamicData.data())->aliasCount++; + compileAlias(builder, dynamicData, obj, p); + } + } + } + for (int ii = 0; ii < obj->dynamicSignals.count(); ++ii) { const Object::DynamicSignal &s = obj->dynamicSignals.at(ii); QByteArray sig(s.name + "("); @@ -1465,38 +1492,30 @@ bool QmlCompiler::compileDynamicMeta(QmlParser::Object *obj, int preAlias) sig.append(")"); QMetaMethodBuilder b = builder.addSignal(sig); b.setParameterNames(s.parameterNames); + ((QmlVMEMetaData *)dynamicData.data())->signalCount++; } int slotStart = obj->dynamicSlots.isEmpty()?-1:output->primitives.count(); for (int ii = 0; ii < obj->dynamicSlots.count(); ++ii) { const Object::DynamicSlot &s = obj->dynamicSlots.at(ii); - builder.addSlot(s.name + "()"); + QByteArray sig(s.name + "("); + for (int jj = 0; jj < s.parameterNames.count(); ++jj) { + if (jj) sig.append(","); + sig.append("QVariant"); + } + sig.append(")"); + QMetaMethodBuilder b = builder.addSlot(sig); + b.setParameterNames(s.parameterNames); + + ((QmlVMEMetaData *)dynamicData.data())->methodCount++; + QmlVMEMetaData::MethodData methodData = { s.parameterNames.count() }; + dynamicData.append((char *)&methodData, sizeof(methodData)); if (preAlias == -1) output->primitives << s.body; } - QByteArray aliasData; - if (preAlias != -1) { - int dynProperties = 0; - QByteArray data; - int propCount = builder.propertyCount(); - int signalCount = builder.methodCount(); - for (int ii = 0; ii < obj->dynamicProperties.count(); ++ii) { - const Object::DynamicProperty &p = obj->dynamicProperties.at(ii); - - if (p.type == Object::DynamicProperty::Alias) { - dynProperties++; - compileAlias(builder, data, obj, p); - } - } - aliasData.append((const char *)&dynProperties, sizeof(int)); - aliasData.append((const char *)&propCount, sizeof(int)); - aliasData.append((const char *)&signalCount, sizeof(int)); - aliasData.append(data); - } - if (obj->metatype) builder.setSuperClass(obj->metatype); @@ -1506,7 +1525,7 @@ bool QmlCompiler::compileDynamicMeta(QmlParser::Object *obj, int preAlias) if (preAlias != -1) { QmlInstruction &store = output->bytecode[preAlias]; - store.storeMeta.aliasData = output->indexForByteArray(aliasData); + store.storeMeta.aliasData = output->indexForByteArray(dynamicData); qFree(output->synthesizedMetaObjects.at(store.storeMeta.data)); output->synthesizedMetaObjects[store.storeMeta.data] = obj->extObjectData; @@ -1516,7 +1535,7 @@ bool QmlCompiler::compileDynamicMeta(QmlParser::Object *obj, int preAlias) store.type = QmlInstruction::StoreMetaObject; store.storeMeta.data = output->synthesizedMetaObjects.count() - 1; store.storeMeta.slotData = slotStart; - store.storeMeta.aliasData = -1; + store.storeMeta.aliasData = output->indexForByteArray(dynamicData); store.line = obj->location.start.line; output->bytecode << store; 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 9b74472..5c9273a 100644 --- a/src/declarative/qml/qmlengine.cpp +++ b/src/declarative/qml/qmlengine.cpp @@ -208,7 +208,7 @@ QmlContext *QmlEnginePrivate::setCurrentBindContext(QmlContext *c) } QmlEnginePrivate::CapturedProperty::CapturedProperty(const QmlMetaProperty &p) -: object(p.object()), notifyIndex(p.property().notifySignalIndex()) +: object(p.object()), coreIndex(p.coreIndex()), notifyIndex(p.property().notifySignalIndex()) { } @@ -357,7 +357,7 @@ bool QmlEnginePrivate::loadCache(QmlBasicScriptNodeCache &cache, const QString & cache.type = QmlBasicScriptNodeCache::Variant; cache.context = context; cache.contextIndex = *iter; - capturedProperties << CapturedProperty(context->q_ptr, *iter + context->notifyIndex); + capturedProperties << CapturedProperty(context->q_ptr, -1, *iter + context->notifyIndex); return true; } @@ -946,457 +946,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) { @@ -1480,7 +1029,7 @@ QScriptValue QmlContextScriptClass::property(const QScriptValue &object, } else { rv = scriptEngine->newVariant(value); } - engine->d_func()->capturedProperties << QmlEnginePrivate::CapturedProperty(bindContext, index + bindContext->d_func()->notifyIndex); + engine->d_func()->capturedProperties << QmlEnginePrivate::CapturedProperty(bindContext, -1, index + bindContext->d_func()->notifyIndex); return rv; } default: @@ -1649,76 +1198,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 f600520..c4259ce 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..25f6edf 100644 --- a/src/declarative/qml/qmlengine_p.h +++ b/src/declarative/qml/qmlengine_p.h @@ -103,11 +103,12 @@ public: QScriptValue propertyObject(const QScriptString &propName, QObject *, uint id = 0); struct CapturedProperty { - CapturedProperty(QObject *o, int n) - : object(o), notifyIndex(n) {} + CapturedProperty(QObject *o, int c, int n) + : object(o), coreIndex(c), notifyIndex(n) {} CapturedProperty(const QmlMetaProperty &); QObject *object; + int coreIndex; int notifyIndex; }; QPODVector<CapturedProperty> capturedProperties; @@ -169,23 +170,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 +231,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..84352b8 --- /dev/null +++ b/src/declarative/qml/qmlexpression.cpp @@ -0,0 +1,624 @@ +/**************************************************************************** +** +** 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 { + const QMetaObject *metaObj = prop.object->metaObject(); + QMetaProperty metaProp = + metaObj->property(prop.coreIndex); + + QString warn = QLatin1String("Expression depends on non-NOTIFYable property: ") + + QLatin1String(metaObj->className()) + + QLatin1String("::") + + QLatin1String(metaProp.name()); + log.addWarning(warn); + } + } + d->addLog(log); + + } else { + bool outputWarningHeader = false; + 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 { + if (!outputWarningHeader) { + outputWarningHeader = true; + qWarning() << "QmlExpression: Expression" << expression() << "depends on non-NOTIFYable properties:"; + } + + const QMetaObject *metaObj = prop.object->metaObject(); + QMetaProperty metaProp = + metaObj->property(prop.coreIndex); + + qWarning().nospace() << " " << metaObj->className() + << "::" << metaProp.name(); + } + } + } + } 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 diff --git a/src/declarative/qml/qmlmetaproperty.cpp b/src/declarative/qml/qmlmetaproperty.cpp index 90acd72..ee24074 100644 --- a/src/declarative/qml/qmlmetaproperty.cpp +++ b/src/declarative/qml/qmlmetaproperty.cpp @@ -882,7 +882,8 @@ bool QmlMetaProperty::hasChangedNotifier() const */ bool QmlMetaProperty::needsChangedNotifier() const { - return type() & Property && !(type() & Attached); + return type() & Property && !(type() & Attached) && + !property().isConstant(); } /*! diff --git a/src/declarative/qml/qmlparser.cpp b/src/declarative/qml/qmlparser.cpp index 2e7eb69..8daab6a 100644 --- a/src/declarative/qml/qmlparser.cpp +++ b/src/declarative/qml/qmlparser.cpp @@ -133,7 +133,7 @@ QmlParser::Object::DynamicSlot::DynamicSlot() } QmlParser::Object::DynamicSlot::DynamicSlot(const DynamicSlot &o) -: name(o.name), body(o.body) +: name(o.name), body(o.body), parameterNames(o.parameterNames) { } diff --git a/src/declarative/qml/qmlparser_p.h b/src/declarative/qml/qmlparser_p.h index 803c73e..7550870 100644 --- a/src/declarative/qml/qmlparser_p.h +++ b/src/declarative/qml/qmlparser_p.h @@ -166,6 +166,7 @@ namespace QmlParser QByteArray name; QString body; + QList<QByteArray> parameterNames; }; // The list of dynamic properties diff --git a/src/declarative/qml/qmlscriptparser.cpp b/src/declarative/qml/qmlscriptparser.cpp index 1c88018..82f6a60 100644 --- a/src/declarative/qml/qmlscriptparser.cpp +++ b/src/declarative/qml/qmlscriptparser.cpp @@ -723,20 +723,19 @@ bool ProcessAST::visit(AST::UiSourceElement *node) if (AST::FunctionDeclaration *funDecl = AST::cast<AST::FunctionDeclaration *>(node->sourceElement)) { - if(funDecl->formals) { - QmlError error; - error.setDescription(QCoreApplication::translate("QmlParser","Slot declarations must be parameterless")); - error.setLine(funDecl->lparenToken.startLine); - error.setColumn(funDecl->lparenToken.startColumn); - _parser->_errors << error; - return false; + Object::DynamicSlot slot; + + AST::FormalParameterList *f = funDecl->formals; + while (f) { + slot.parameterNames << f->name->asString().toUtf8(); + f = f->finish(); } QString body = textAt(funDecl->lbraceToken, funDecl->rbraceToken); - Object::DynamicSlot slot; slot.name = funDecl->name->asString().toUtf8(); slot.body = body; obj->dynamicSlots << slot; + } else { QmlError error; error.setDescription(QCoreApplication::translate("QmlParser","QmlJS declaration outside Script element")); diff --git a/src/declarative/qml/qmlvme.cpp b/src/declarative/qml/qmlvme.cpp index 0d88e02..a11caeb 100644 --- a/src/declarative/qml/qmlvme.cpp +++ b/src/declarative/qml/qmlvme.cpp @@ -239,7 +239,7 @@ QObject *QmlVME::run(QStack<QObject *> &stack, QmlContext *ctxt, QmlCompiledComp case QmlInstruction::StoreMetaObject: { QObject *target = stack.top(); - new QmlVMEMetaObject(target, synthesizedMetaObjects.at(instr.storeMeta.data), &comp->primitives, instr.storeMeta.slotData, (instr.storeMeta.aliasData != -1)?datas.at(instr.storeMeta.aliasData):QByteArray(), comp); + new QmlVMEMetaObject(target, synthesizedMetaObjects.at(instr.storeMeta.data), &comp->primitives, instr.storeMeta.slotData, (const QmlVMEMetaData *)datas.at(instr.storeMeta.aliasData).constData(), comp); } break; diff --git a/src/declarative/qml/qmlvmemetaobject.cpp b/src/declarative/qml/qmlvmemetaobject.cpp index dc06bc5..6d14689 100644 --- a/src/declarative/qml/qmlvmemetaobject.cpp +++ b/src/declarative/qml/qmlvmemetaobject.cpp @@ -55,10 +55,10 @@ QmlVMEMetaObject::QmlVMEMetaObject(QObject *obj, const QMetaObject *other, QList<QString> *strData, int slotData, - const QByteArray &alias, + const QmlVMEMetaData *meta, QmlRefCount *rc) -: object(obj), ref(rc), slotData(strData), slotDataIdx(slotData), parent(0), - aliasData(alias), aliases(0), aliasArray(0) +: object(obj), ref(rc), metaData(meta), slotData(strData), + slotDataIdx(slotData), parent(0) { if (ref) ref->addref(); @@ -71,44 +71,18 @@ QmlVMEMetaObject::QmlVMEMetaObject(QObject *obj, parent = static_cast<QAbstractDynamicMetaObject*>(op->metaObject); op->metaObject = this; - if (!aliasData.isEmpty()) { - aliases = (Aliases *)aliasData.constData(); - aliasArray = (AliasArray *)(aliasData.constData() + 3 * sizeof(int)); - aConnected.resize(aliases->aliasCount); - } + propOffset = QAbstractDynamicMetaObject::propertyOffset(); + methodOffset = QAbstractDynamicMetaObject::methodOffset(); - baseProp = propertyOffset(); - baseSig = methodOffset(); - int propCount = propertyCount() - (aliases?aliases->aliasCount:0); - data = new QVariant[propCount - baseProp]; - vTypes.resize(propCount - baseProp); + data = new QVariant[metaData->propertyCount]; + aConnected.resize(metaData->aliasCount); // ### Optimize - for (int ii = baseProp; ii < propCount; ++ii) { - QMetaProperty prop = property(ii); - if ((int)prop.type() != -1) { - data[ii - baseProp] = QVariant((QVariant::Type)prop.userType()); - } else { - vTypes.setBit(ii - baseProp, true); - } + for (int ii = 0; ii < metaData->propertyCount; ++ii) { + int t = (metaData->propertyData() + ii)->propertyType; + if (t != -1) + data[ii] = QVariant((QVariant::Type)t); } - - baseSlot = -1; - slotCount = 0; - for (int ii = baseSig; ii < methodCount(); ++ii) { - QMetaMethod m = method(ii); - if (m.methodType() == QMetaMethod::Slot) { - if (baseSlot == -1) - baseSlot = ii; - } else { - if (baseSlot != -1) { - slotCount = ii - baseSlot; - break; - } - } - } - if(baseSlot != -1 && !slotCount) - slotCount = methodCount() - baseSlot; } QmlVMEMetaObject::~QmlVMEMetaObject() @@ -120,111 +94,146 @@ QmlVMEMetaObject::~QmlVMEMetaObject() delete [] data; } -int QmlVMEMetaObject::metaCall(QMetaObject::Call c, int id, void **a) +int QmlVMEMetaObject::metaCall(QMetaObject::Call c, int _id, void **a) { + int id = _id; if(c == QMetaObject::ReadProperty || c == QMetaObject::WriteProperty) { - if (id >= baseProp) { - int propId = id - baseProp; - bool needActivate = false; + if (id >= propOffset) { + id -= propOffset; + + if (id < metaData->propertyCount) { + int t = (metaData->propertyData() + id)->propertyType; + bool needActivate = false; + + if (t == -1) { + + if (c == QMetaObject::ReadProperty) { + *reinterpret_cast<QVariant *>(a[0]) = data[id]; + } else if (c == QMetaObject::WriteProperty) { + needActivate = + (data[id] != *reinterpret_cast<QVariant *>(a[0])); + data[id] = *reinterpret_cast<QVariant *>(a[0]); + } + + } else { + + if (c == QMetaObject::ReadProperty) { + switch(t) { + case QVariant::Int: + *reinterpret_cast<int *>(a[0]) = data[id].toInt(); + break; + case QVariant::Bool: + *reinterpret_cast<bool *>(a[0]) = data[id].toBool(); + break; + case QVariant::Double: + *reinterpret_cast<double *>(a[0]) = data[id].toDouble(); + break; + case QVariant::String: + *reinterpret_cast<QString *>(a[0]) = data[id].toString(); + break; + case QVariant::Url: + *reinterpret_cast<QUrl *>(a[0]) = data[id].toUrl(); + break; + case QVariant::Color: + *reinterpret_cast<QColor *>(a[0]) = data[id].value<QColor>(); + break; + case QVariant::Date: + *reinterpret_cast<QDate *>(a[0]) = data[id].toDate(); + break; + default: + break; + } + + } else if (c == QMetaObject::WriteProperty) { + + QVariant value = QVariant((QVariant::Type)data[id].type(), a[0]); + needActivate = (data[id] != value); + data[id] = value; + } + + } + + if (c == QMetaObject::WriteProperty && needActivate) { + activate(object, methodOffset + id, 0); + } + + return -1; + } + + id -= metaData->propertyCount; + + if (id < metaData->aliasCount) { - if (aliases && propId >= aliases->propCount) { QmlContext *ctxt = qmlContext(object); if (!ctxt) return -1; - int aliasId = propId - aliases->propCount; - AliasArray *d = aliasArray + aliasId; + QmlVMEMetaData::AliasData *d = metaData->aliasData() + id; QmlContextPrivate *ctxtPriv = (QmlContextPrivate *)QObjectPrivate::get(ctxt); - QObject *target = *(QObject **)ctxtPriv->propertyValues[d->contextIdx].data(); + QObject *target = + *(QObject **)ctxtPriv->propertyValues[d->contextIdx].data(); if (!target) return -1; - if (c == QMetaObject::ReadProperty && - !aConnected.testBit(aliasId)) { - - int mySigIdx = baseSig + aliasId + aliases->signalOffset; - QMetaObject::connect(ctxt, d->contextIdx + ctxtPriv->notifyIndex, object, mySigIdx); + if (c == QMetaObject::ReadProperty && !aConnected.testBit(id)) { + int sigIdx = methodOffset + id + metaData->propertyCount; + QMetaObject::connect(ctxt, d->contextIdx + ctxtPriv->notifyIndex, object, sigIdx); - QMetaProperty prop = target->metaObject()->property(d->propIdx); + QMetaProperty prop = + target->metaObject()->property(d->propertyIdx); if (prop.hasNotifySignal()) QMetaObject::connect(target, prop.notifySignalIndex(), - object, mySigIdx); - aConnected.setBit(aliasId); - - } - return QMetaObject::metacall(target, c, d->propIdx, a); - - } else if (vTypes.testBit(propId)) { - if (c == QMetaObject::ReadProperty) { - *reinterpret_cast<QVariant *>(a[0]) = data[propId]; - } else if (c == QMetaObject::WriteProperty) { - needActivate = - (data[propId] != *reinterpret_cast<QVariant *>(a[0])); - data[propId] = *reinterpret_cast<QVariant *>(a[0]); + object, sigIdx); + aConnected.setBit(id); } - } else { - if (c == QMetaObject::ReadProperty) { - switch(data[propId].type()) { - case QVariant::Int: - *reinterpret_cast<int *>(a[0]) = data[propId].toInt(); - break; - case QVariant::Bool: - *reinterpret_cast<bool *>(a[0]) = data[propId].toBool(); - break; - case QVariant::Double: - *reinterpret_cast<double *>(a[0]) = data[propId].toDouble(); - break; - case QVariant::String: - *reinterpret_cast<QString *>(a[0]) = data[propId].toString(); - break; - case QVariant::Url: - *reinterpret_cast<QUrl *>(a[0]) = data[propId].toUrl(); - break; - case QVariant::Color: - *reinterpret_cast<QColor *>(a[0]) = data[propId].value<QColor>(); - break; - case QVariant::Date: - *reinterpret_cast<QDate *>(a[0]) = data[propId].toDate(); - break; - default: - qFatal("Unknown type"); - break; - } - } else if (c == QMetaObject::WriteProperty) { + return QMetaObject::metacall(target, c, d->propertyIdx, a); - QVariant value = QVariant((QVariant::Type)data[propId].type(), a[0]); - needActivate = (data[propId] != value); - data[propId] = value; - } } + return -1; - if (c == QMetaObject::WriteProperty && needActivate) { - activate(object, baseSig + propId, 0); - } + } - return id; - } } else if(c == QMetaObject::InvokeMetaMethod) { - if (id >= baseSig && (aliases && id >= baseSig + aliases->signalOffset)) { - QMetaObject::activate(object, id, a); - return id; - } else if (id >= baseSig && (baseSlot == -1 || id < baseSlot)) { - QMetaObject::activate(object, id, a); - return id; - } else if (id >= baseSlot && id < (baseSlot + slotCount)) { - int idx = id - baseSlot + slotDataIdx; - QmlContext *ctxt = qmlContext(object); - QmlExpression expr(ctxt, slotData->at(idx), object); - expr.setTrackChange(false); - expr.value(); - return id; + + if (id >= methodOffset) { + + id -= methodOffset; + int plainSignals = metaData->signalCount + metaData->propertyCount + + metaData->aliasCount; + if (id < plainSignals) { + QMetaObject::activate(object, _id, a); + return -1; + } + + id -= plainSignals; + + if (id < metaData->methodCount) { + QString code = slotData->at(id + slotDataIdx); + QmlContext *ctxt = qmlContext(object); + + if (0 == (metaData->methodData() + id)->parameterCount) { + QmlExpression expr(ctxt, code, object); + expr.setTrackChange(false); + expr.value(); + } else { + QmlContext newCtxt(ctxt); + QMetaMethod m = method(_id); + QList<QByteArray> names = m.parameterNames(); + for (int ii = 0; ii < names.count(); ++ii) + newCtxt.setContextProperty(names.at(ii), *(QVariant *)a[ii + 1]); + QmlExpression expr(&newCtxt, code, object); + expr.setTrackChange(false); + expr.value(); + } + } + return -1; } } if (parent) - return parent->metaCall(c, id, a); + return parent->metaCall(c, _id, a); else - return object->qt_metacall(c, id, a); + return object->qt_metacall(c, _id, a); } QT_END_NAMESPACE diff --git a/src/declarative/qml/qmlvmemetaobject_p.h b/src/declarative/qml/qmlvmemetaobject_p.h index 45fb33d..6421c3f 100644 --- a/src/declarative/qml/qmlvmemetaobject_p.h +++ b/src/declarative/qml/qmlvmemetaobject_p.h @@ -59,11 +59,45 @@ #include <private/qobject_p.h> QT_BEGIN_NAMESPACE + +struct QmlVMEMetaData +{ + short propertyCount; + short aliasCount; + short signalCount; + short methodCount; + + struct AliasData { + int contextIdx; + int propertyIdx; + }; + + struct PropertyData { + int propertyType; + }; + + struct MethodData { + int parameterCount; + }; + + PropertyData *propertyData() const { + return (PropertyData *)(((const char *)this) + sizeof(QmlVMEMetaData)); + } + + AliasData *aliasData() const { + return (AliasData *)(propertyData() + propertyCount); + } + + MethodData *methodData() const { + return (MethodData *)(aliasData() + propertyCount); + } +}; + class QmlRefCount; class QmlVMEMetaObject : public QAbstractDynamicMetaObject { public: - QmlVMEMetaObject(QObject *, const QMetaObject *, QList<QString> *, int slotData, const QByteArray &aliasData, QmlRefCount * = 0); + QmlVMEMetaObject(QObject *, const QMetaObject *, QList<QString> *, int slotData, const QmlVMEMetaData *data, QmlRefCount * = 0); ~QmlVMEMetaObject(); protected: @@ -72,26 +106,19 @@ protected: private: QObject *object; QmlRefCount *ref; - int baseProp; - int baseSig; - int baseSlot; - int slotCount; + + const QmlVMEMetaData *metaData; + int propOffset; + int methodOffset; + QVariant *data; - QBitArray vTypes; QBitArray aConnected; + QList<QString> *slotData; int slotDataIdx; + QAbstractDynamicMetaObject *parent; - QByteArray aliasData; - struct Aliases { - int aliasCount; - int propCount; - int signalOffset; - } *aliases; - struct AliasArray { - int contextIdx; - int propIdx; - } *aliasArray; + }; QT_END_NAMESPACE diff --git a/src/declarative/qml/rewriter/rewriter.cpp b/src/declarative/qml/rewriter/rewriter.cpp index 2ce927c..ed45f16 100644 --- a/src/declarative/qml/rewriter/rewriter.cpp +++ b/src/declarative/qml/rewriter/rewriter.cpp @@ -83,14 +83,19 @@ void Rewriter::moveTextBefore(const AST::SourceLocation &firstLoc, const AST::SourceLocation &lastLoc, const AST::SourceLocation &loc) { - textWriter.move(firstLoc.offset, lastLoc.offset + lastLoc.length - firstLoc.offset, loc.offset); + move(firstLoc.offset, lastLoc.offset + lastLoc.length - firstLoc.offset, loc.offset); } void Rewriter::moveTextAfter(const AST::SourceLocation &firstLoc, const AST::SourceLocation &lastLoc, const AST::SourceLocation &loc) { - textWriter.move(firstLoc.offset, lastLoc.offset + lastLoc.length - firstLoc.offset, loc.offset + loc.length); + move(firstLoc.offset, lastLoc.offset + lastLoc.length - firstLoc.offset, loc.offset + loc.length); +} + +void Rewriter::move(int pos, int length, int to) +{ + textWriter.move(pos, length, to); } QT_END_NAMESPACE diff --git a/src/declarative/qml/rewriter/rewriter_p.h b/src/declarative/qml/rewriter/rewriter_p.h index fcb9ca5..44f3cce 100644 --- a/src/declarative/qml/rewriter/rewriter_p.h +++ b/src/declarative/qml/rewriter/rewriter_p.h @@ -121,7 +121,8 @@ public: // // low-level offset based API // - void replace(int offset, int length, const QString &text); + virtual void replace(int offset, int length, const QString &text); + virtual void move(int pos, int length, int to); void insertText(int offset, const QString &text); void removeText(int offset, int length); |