diff options
20 files changed, 1241 insertions, 922 deletions
diff --git a/src/declarative/graphicsitems/qdeclarativeitem.cpp b/src/declarative/graphicsitems/qdeclarativeitem.cpp index 928c504..723ed34 100644 --- a/src/declarative/graphicsitems/qdeclarativeitem.cpp +++ b/src/declarative/graphicsitems/qdeclarativeitem.cpp @@ -444,7 +444,7 @@ void QDeclarativeItemKeyFilter::componentComplete() If an item has been set for a direction and the KeyNavigation attached property receives the corresponding key press and release events, the events will be accepted by - KeyNaviagtion and will not propagate any further. + KeyNavigation and will not propagate any further. \sa {Keys}{Keys attached property} */ diff --git a/src/declarative/graphicsitems/qdeclarativepath.cpp b/src/declarative/graphicsitems/qdeclarativepath.cpp index e2042fc..e867a52 100644 --- a/src/declarative/graphicsitems/qdeclarativepath.cpp +++ b/src/declarative/graphicsitems/qdeclarativepath.cpp @@ -117,6 +117,7 @@ void QDeclarativePath::setStartX(qreal x) return; d->startX = x; emit startXChanged(); + processPath(); } qreal QDeclarativePath::startY() const @@ -132,6 +133,7 @@ void QDeclarativePath::setStartY(qreal y) return; d->startY = y; emit startYChanged(); + processPath(); } /*! @@ -220,6 +222,9 @@ void QDeclarativePath::processPath() { Q_D(QDeclarativePath); + if (!d->componentComplete) + return; + d->_pointCache.clear(); d->_attributePoints.clear(); d->_path = QPainterPath(); @@ -284,10 +289,18 @@ void QDeclarativePath::processPath() emit changed(); } +void QDeclarativePath::classBegin() +{ + Q_D(QDeclarativePath); + d->componentComplete = false; +} + void QDeclarativePath::componentComplete() { Q_D(QDeclarativePath); QSet<QString> attrs; + d->componentComplete = true; + // First gather up all the attributes foreach (QDeclarativePathElement *pathElement, d->_pathElements) { if (QDeclarativePathAttribute *attribute = diff --git a/src/declarative/graphicsitems/qdeclarativepath_p.h b/src/declarative/graphicsitems/qdeclarativepath_p.h index d7cfca1..17a2ea3 100644 --- a/src/declarative/graphicsitems/qdeclarativepath_p.h +++ b/src/declarative/graphicsitems/qdeclarativepath_p.h @@ -224,6 +224,7 @@ Q_SIGNALS: protected: virtual void componentComplete(); + virtual void classBegin(); private Q_SLOTS: void processPath(); diff --git a/src/declarative/graphicsitems/qdeclarativepath_p_p.h b/src/declarative/graphicsitems/qdeclarativepath_p_p.h index e82bcf5..994090e 100644 --- a/src/declarative/graphicsitems/qdeclarativepath_p_p.h +++ b/src/declarative/graphicsitems/qdeclarativepath_p_p.h @@ -65,7 +65,7 @@ class QDeclarativePathPrivate : public QObjectPrivate Q_DECLARE_PUBLIC(QDeclarativePath) public: - QDeclarativePathPrivate() : startX(0), startY(0), closed(false) { } + QDeclarativePathPrivate() : startX(0), startY(0), closed(false), componentComplete(true) { } QPainterPath _path; QList<QDeclarativePathElement*> _pathElements; @@ -75,6 +75,7 @@ public: int startX; int startY; bool closed; + bool componentComplete; }; QT_END_NAMESPACE diff --git a/src/declarative/qml/qdeclarativecompiledbindings.cpp b/src/declarative/qml/qdeclarativecompiledbindings.cpp index 6596aba..05b7dc6 100644 --- a/src/declarative/qml/qdeclarativecompiledbindings.cpp +++ b/src/declarative/qml/qdeclarativecompiledbindings.cpp @@ -572,7 +572,7 @@ struct QDeclarativeBindingCompilerPrivate QDeclarativeParser::Object *component; QDeclarativeParser::Property *destination; QHash<QString, QDeclarativeParser::Object *> ids; - QDeclarativeEnginePrivate::Imports imports; + QDeclarativeImports imports; QDeclarativeEnginePrivate *engine; QString contextName() const { return QLatin1String("$$$SCOPE_") + QString::number((intptr_t)context, 16); } @@ -1795,8 +1795,8 @@ bool QDeclarativeBindingCompilerPrivate::parseName(AST::Node *node, Result &type if (nameParts.at(ii + 1).at(0).isUpper()) return false; - QDeclarativeEnginePrivate::ImportedNamespace *ns = 0; - if (!engine->resolveType(imports, name.toUtf8(), &attachType, 0, 0, 0, &ns)) + QDeclarativeImportedNamespace *ns = 0; + if (!engine->importDatabase.resolveType(imports, name.toUtf8(), &attachType, 0, 0, 0, &ns)) return false; if (ns || !attachType || !attachType->attachedPropertiesType()) return false; diff --git a/src/declarative/qml/qdeclarativecompiledbindings_p.h b/src/declarative/qml/qdeclarativecompiledbindings_p.h index a17bc84..29a1092 100644 --- a/src/declarative/qml/qdeclarativecompiledbindings_p.h +++ b/src/declarative/qml/qdeclarativecompiledbindings_p.h @@ -77,7 +77,7 @@ public: QDeclarativeParser::Property *property; QDeclarativeParser::Variant expression; QHash<QString, QDeclarativeParser::Object *> ids; - QDeclarativeEnginePrivate::Imports imports; + QDeclarativeImports imports; }; // -1 on failure, otherwise the binding index to use diff --git a/src/declarative/qml/qdeclarativecompiler.cpp b/src/declarative/qml/qdeclarativecompiler.cpp index f64efcb..6d420a7 100644 --- a/src/declarative/qml/qdeclarativecompiler.cpp +++ b/src/declarative/qml/qdeclarativecompiler.cpp @@ -607,6 +607,7 @@ bool QDeclarativeCompiler::compile(QDeclarativeEngine *engine, Q_ASSERT(root); this->engine = engine; + this->enginePrivate = QDeclarativeEnginePrivate::get(engine); this->unit = unit; this->unitRoot = root; compileTree(root); @@ -624,6 +625,7 @@ bool QDeclarativeCompiler::compile(QDeclarativeEngine *engine, savedCompileStates.clear(); output = 0; this->engine = 0; + this->enginePrivate = 0; this->unit = 0; this->unitRoot = 0; @@ -704,7 +706,7 @@ void QDeclarativeCompiler::compileTree(Object *tree) output->root = &output->rootData; } if (!tree->metadata.isEmpty()) - QDeclarativeEnginePrivate::get(engine)->registerCompositeType(output); + enginePrivate->registerCompositeType(output); } static bool ValuePtrLessThan(const Value *t1, const Value *t2) @@ -939,7 +941,7 @@ void QDeclarativeCompiler::genObject(QDeclarativeParser::Object *obj) if (tr.component) propertyCache = QDeclarativeComponentPrivate::get(tr.component)->cc->rootPropertyCache->copy(); else - propertyCache = QDeclarativeEnginePrivate::get(engine)->cache(obj->metaObject()->superClass())->copy(); + propertyCache = enginePrivate->cache(obj->metaObject()->superClass())->copy(); propertyCache->append(engine, obj->metaObject(), QDeclarativePropertyCache::Data::NoFlags, QDeclarativePropertyCache::Data::IsVMEFunction); @@ -955,7 +957,7 @@ void QDeclarativeCompiler::genObject(QDeclarativeParser::Object *obj) if (tr.component) output->rootPropertyCache = QDeclarativeComponentPrivate::get(tr.component)->cc->rootPropertyCache; else - output->rootPropertyCache = QDeclarativeEnginePrivate::get(engine)->cache(obj->metaObject()); + output->rootPropertyCache = enginePrivate->cache(obj->metaObject()); output->rootPropertyCache->addref(); } @@ -1366,9 +1368,9 @@ bool QDeclarativeCompiler::buildProperty(QDeclarativeParser::Property *prop, } QDeclarativeType *type = 0; - QDeclarativeEnginePrivate::ImportedNamespace *typeNamespace = 0; - QDeclarativeEnginePrivate::get(engine)->resolveType(unit->imports, prop->name, - &type, 0, 0, 0, &typeNamespace); + QDeclarativeImportedNamespace *typeNamespace = 0; + enginePrivate->importDatabase.resolveType(unit->imports, prop->name, + &type, 0, 0, 0, &typeNamespace); if (typeNamespace) { // ### We might need to indicate that this property is a namespace @@ -1443,7 +1445,7 @@ bool QDeclarativeCompiler::buildProperty(QDeclarativeParser::Property *prop, COMPILE_CHECK(buildGroupedProperty(prop, obj, ctxt)); - } else if (QDeclarativeEnginePrivate::get(engine)->isList(prop->type)) { + } else if (enginePrivate->isList(prop->type)) { COMPILE_CHECK(buildListProperty(prop, obj, ctxt)); @@ -1460,11 +1462,10 @@ bool QDeclarativeCompiler::buildProperty(QDeclarativeParser::Property *prop, return true; } -bool -QDeclarativeCompiler::buildPropertyInNamespace(QDeclarativeEnginePrivate::ImportedNamespace *ns, - QDeclarativeParser::Property *nsProp, - QDeclarativeParser::Object *obj, - const BindingContext &ctxt) +bool QDeclarativeCompiler::buildPropertyInNamespace(QDeclarativeImportedNamespace *ns, + QDeclarativeParser::Property *nsProp, + QDeclarativeParser::Object *obj, + const BindingContext &ctxt) { if (!nsProp->value) COMPILE_EXCEPTION(nsProp, tr("Invalid use of namespace")); @@ -1477,8 +1478,7 @@ QDeclarativeCompiler::buildPropertyInNamespace(QDeclarativeEnginePrivate::Import // Setup attached property data QDeclarativeType *type = 0; - QDeclarativeEnginePrivate::get(engine)->resolveTypeInNamespace(ns, prop->name, - &type, 0, 0, 0); + enginePrivate->importDatabase.resolveTypeInNamespace(ns, prop->name, &type, 0, 0, 0); if (!type || !type->attachedPropertiesType()) COMPILE_EXCEPTION(prop, tr("Non-existent attached object")); @@ -1499,7 +1499,7 @@ QDeclarativeCompiler::buildPropertyInNamespace(QDeclarativeEnginePrivate::Import void QDeclarativeCompiler::genValueProperty(QDeclarativeParser::Property *prop, QDeclarativeParser::Object *obj) { - if (QDeclarativeEnginePrivate::get(engine)->isList(prop->type)) { + if (enginePrivate->isList(prop->type)) { genListProperty(prop, obj); } else { genPropertyAssignment(prop, obj); @@ -1509,7 +1509,7 @@ void QDeclarativeCompiler::genValueProperty(QDeclarativeParser::Property *prop, void QDeclarativeCompiler::genListProperty(QDeclarativeParser::Property *prop, QDeclarativeParser::Object *obj) { - int listType = QDeclarativeEnginePrivate::get(engine)->listType(prop->type); + int listType = enginePrivate->listType(prop->type); QDeclarativeInstruction fetch; fetch.type = QDeclarativeInstruction::FetchQList; @@ -1763,8 +1763,7 @@ bool QDeclarativeCompiler::buildGroupedProperty(QDeclarativeParser::Property *pr } else { // Load the nested property's meta type - prop->value->metatype = - QDeclarativeEnginePrivate::get(engine)->metaObjectForType(prop->type); + prop->value->metatype = enginePrivate->metaObjectForType(prop->type); if (!prop->value->metatype) COMPILE_EXCEPTION(prop, tr("Invalid grouped property access")); @@ -1843,13 +1842,13 @@ bool QDeclarativeCompiler::buildListProperty(QDeclarativeParser::Property *prop, QDeclarativeParser::Object *obj, const BindingContext &ctxt) { - Q_ASSERT(QDeclarativeEnginePrivate::get(engine)->isList(prop->type)); + Q_ASSERT(enginePrivate->isList(prop->type)); int t = prop->type; obj->addValueProperty(prop); - int listType = QDeclarativeEnginePrivate::get(engine)->listType(t); + int listType = enginePrivate->listType(t); bool listTypeIsInterface = QDeclarativeMetaType::isInterface(listType); bool assignedBinding = false; @@ -1964,8 +1963,7 @@ bool QDeclarativeCompiler::buildPropertyObjectAssignment(QDeclarativeParser::Pro // We want to raw metaObject here as the raw metaobject is the // actual property type before we applied any extensions that might // effect the properties on the type, but don't effect assignability - const QMetaObject *propertyMetaObject = - QDeclarativeEnginePrivate::get(engine)->rawMetaObjectForType(prop->type); + const QMetaObject *propertyMetaObject = enginePrivate->rawMetaObjectForType(prop->type); // Will be true if the assgned type inherits propertyMetaObject bool isAssignable = false; @@ -2107,8 +2105,8 @@ bool QDeclarativeCompiler::testQualifiedEnumAssignment(const QMetaProperty &prop QString typeName = parts.at(0); QDeclarativeType *type = 0; - QDeclarativeEnginePrivate::get(engine)->resolveType(unit->imports, typeName.toUtf8(), - &type, 0, 0, 0, 0); + enginePrivate->importDatabase.resolveType(unit->imports, typeName.toUtf8(), + &type, 0, 0, 0, 0); if (!type || obj->typeName != type->qmlTypeName()) return true; @@ -2135,7 +2133,7 @@ int QDeclarativeCompiler::evaluateEnum(const QByteArray& script) const int dot = script.indexOf('.'); if (dot > 0) { QDeclarativeType *type = 0; - QDeclarativeEnginePrivate::get(engine)->resolveType(unit->imports, script.left(dot), &type, 0, 0, 0, 0); + enginePrivate->importDatabase.resolveType(unit->imports, script.left(dot), &type, 0, 0, 0, 0); if (!type) return -1; const QMetaObject *mo = type->metaObject(); @@ -2289,13 +2287,12 @@ bool QDeclarativeCompiler::buildDynamicMeta(QDeclarativeParser::Object *obj, Dyn QByteArray customTypeName; QDeclarativeType *qmltype = 0; QUrl url; - QDeclarativeEnginePrivate *priv = QDeclarativeEnginePrivate::get(engine); - if (!priv->resolveType(unit->imports, p.customType, &qmltype, - &url, 0, 0, 0)) + if (!enginePrivate->importDatabase.resolveType(unit->imports, p.customType, &qmltype, + &url, 0, 0, 0)) COMPILE_EXCEPTION(&p, tr("Invalid property type")); if (!qmltype) { - QDeclarativeCompositeTypeData *tdata = priv->typeManager.get(url); + QDeclarativeCompositeTypeData *tdata = enginePrivate->typeManager.get(url); Q_ASSERT(tdata); Q_ASSERT(tdata->status == QDeclarativeCompositeTypeData::Complete); @@ -2467,7 +2464,7 @@ bool QDeclarativeCompiler::checkValidId(QDeclarativeParser::Value *v, const QStr } - if (QDeclarativeEnginePrivate::get(engine)->globalClass->illegalNames().contains(val)) + if (enginePrivate->globalClass->illegalNames().contains(val)) COMPILE_EXCEPTION(v, tr( "ID illegally masks global JavaScript property")); return true; @@ -2660,8 +2657,8 @@ int QDeclarativeCompiler::genValueTypeData(QDeclarativeParser::Property *valueTy { QByteArray data = QDeclarativePropertyPrivate::saveValueType(prop->parent->metaObject(), prop->index, - QDeclarativeEnginePrivate::get(engine)->valueTypes[prop->type]->metaObject(), - valueTypeProp->index); + enginePrivate->valueTypes[prop->type]->metaObject(), + valueTypeProp->index); // valueTypeProp->index, valueTypeProp->type); return output->indexForByteArray(data); @@ -2697,7 +2694,7 @@ bool QDeclarativeCompiler::completeComponentBuild() expr.expression = binding.expression; expr.imports = unit->imports; - int index = bindingCompiler.compile(expr, QDeclarativeEnginePrivate::get(engine)); + int index = bindingCompiler.compile(expr, enginePrivate); if (index != -1) { binding.dataType = BindingReference::Experimental; binding.compiledIndex = index; @@ -2805,7 +2802,7 @@ void QDeclarativeCompiler::dumpStats() bool QDeclarativeCompiler::canCoerce(int to, QDeclarativeParser::Object *from) { const QMetaObject *toMo = - QDeclarativeEnginePrivate::get(engine)->rawMetaObjectForType(to); + enginePrivate->rawMetaObjectForType(to); const QMetaObject *fromMo = from->metaObject(); while (fromMo) { diff --git a/src/declarative/qml/qdeclarativecompiler_p.h b/src/declarative/qml/qdeclarativecompiler_p.h index cd612d8..908c703 100644 --- a/src/declarative/qml/qdeclarativecompiler_p.h +++ b/src/declarative/qml/qdeclarativecompiler_p.h @@ -191,7 +191,7 @@ private: const BindingContext &); bool buildProperty(QDeclarativeParser::Property *prop, QDeclarativeParser::Object *obj, const BindingContext &); - bool buildPropertyInNamespace(QDeclarativeEnginePrivate::ImportedNamespace *ns, + bool buildPropertyInNamespace(QDeclarativeImportedNamespace *ns, QDeclarativeParser::Property *prop, QDeclarativeParser::Object *obj, const BindingContext &); @@ -335,6 +335,7 @@ private: QList<QDeclarativeError> exceptions; QDeclarativeCompiledData *output; QDeclarativeEngine *engine; + QDeclarativeEnginePrivate *enginePrivate; QDeclarativeParser::Object *unitRoot; QDeclarativeCompositeTypeData *unit; }; diff --git a/src/declarative/qml/qdeclarativecompositetypedata_p.h b/src/declarative/qml/qdeclarativecompositetypedata_p.h index 47cb3b3..a0e4cc2 100644 --- a/src/declarative/qml/qdeclarativecompositetypedata_p.h +++ b/src/declarative/qml/qdeclarativecompositetypedata_p.h @@ -83,7 +83,7 @@ public: QList<QDeclarativeError> errors; - QDeclarativeEnginePrivate::Imports imports; + QDeclarativeImports imports; QList<QDeclarativeCompositeTypeData *> dependants; diff --git a/src/declarative/qml/qdeclarativecompositetypemanager.cpp b/src/declarative/qml/qdeclarativecompositetypemanager.cpp index 0eb7e1b..0ea198d 100644 --- a/src/declarative/qml/qdeclarativecompositetypemanager.cpp +++ b/src/declarative/qml/qdeclarativecompositetypemanager.cpp @@ -525,16 +525,15 @@ void QDeclarativeCompositeTypeManager::checkComplete(QDeclarativeCompositeTypeDa int QDeclarativeCompositeTypeManager::resolveTypes(QDeclarativeCompositeTypeData *unit) { // not called until all resources are loaded (they include import URLs) - int waiting = 0; + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine); + QDeclarativeImportDatabase &importDatabase = ep->importDatabase; - /* - For local urls, add an implicit import "." as first (most overridden) lookup. This will also trigger - the loading of the qmldir and the import of any native types from available plugins. - */ + // For local urls, add an implicit import "." as first (most overridden) lookup. + // This will also trigger the loading of the qmldir and the import of any native + // types from available plugins. { - QDeclarativeDirComponents qmldircomponentsnetwork; if (QDeclarativeCompositeTypeResource *resource = resources.value(unit->imports.baseUrl().resolved(QUrl(QLatin1String("./qmldir"))))) { @@ -544,14 +543,9 @@ int QDeclarativeCompositeTypeManager::resolveTypes(QDeclarativeCompositeTypeData qmldircomponentsnetwork = parser.components(); } - QDeclarativeEnginePrivate::get(engine)-> - addToImport(&unit->imports, - qmldircomponentsnetwork, - QLatin1String("."), - QString(), - -1, -1, - QDeclarativeScriptParser::Import::File, - 0); // error ignored (just means no fallback) + importDatabase.addToImport(&unit->imports, qmldircomponentsnetwork, QLatin1String("."), + QString(), -1, -1, QDeclarativeScriptParser::Import::File, + 0); // error ignored (just means no fallback) } @@ -588,9 +582,8 @@ int QDeclarativeCompositeTypeManager::resolveTypes(QDeclarativeCompositeTypeData } QString errorString; - if (!QDeclarativeEnginePrivate::get(engine)-> - addToImport(&unit->imports, qmldircomponentsnetwork, imp.uri, imp.qualifier, vmaj, vmin, imp.type, &errorString)) - { + if (!importDatabase.addToImport(&unit->imports, qmldircomponentsnetwork, imp.uri, imp.qualifier, + vmaj, vmin, imp.type, &errorString)) { QDeclarativeError error; error.setUrl(unit->imports.baseUrl()); error.setDescription(errorString); @@ -616,11 +609,10 @@ int QDeclarativeCompositeTypeManager::resolveTypes(QDeclarativeCompositeTypeData QUrl url; int majorVersion; int minorVersion; - QDeclarativeEnginePrivate::ImportedNamespace *typeNamespace = 0; + QDeclarativeImportedNamespace *typeNamespace = 0; QString errorString; - if (!QDeclarativeEnginePrivate::get(engine)->resolveType(unit->imports, typeName, &ref.type, &url, &majorVersion, &minorVersion, &typeNamespace, &errorString) - || typeNamespace) - { + if (!importDatabase.resolveType(unit->imports, typeName, &ref.type, &url, &majorVersion, &minorVersion, + &typeNamespace, &errorString) || typeNamespace) { // Known to not be a type: // - known to be a namespace (Namespace {}) // - type with unknown namespace (UnknownNamespace.SomeType {}) diff --git a/src/declarative/qml/qdeclarativeengine.cpp b/src/declarative/qml/qdeclarativeengine.cpp index e097edc..a22011a 100644 --- a/src/declarative/qml/qdeclarativeengine.cpp +++ b/src/declarative/qml/qdeclarativeengine.cpp @@ -111,9 +111,6 @@ Q_DECLARE_METATYPE(QDeclarativeProperty) QT_BEGIN_NAMESPACE -DEFINE_BOOL_CONFIG_OPTION(qmlImportTrace, QML_IMPORT_TRACE) -DEFINE_BOOL_CONFIG_OPTION(qmlCheckTypes, QML_CHECK_TYPES) - /*! \qmlclass QtObject QObject \since 4.7 @@ -158,7 +155,7 @@ QDeclarativeEnginePrivate::QDeclarativeEnginePrivate(QDeclarativeEngine *e) objectClass(0), valueTypeClass(0), globalClass(0), cleanup(0), erroredBindings(0), inProgressCreations(0), scriptEngine(this), workerScriptEngine(0), componentAttached(0), inBeginCreate(false), networkAccessManager(0), networkAccessManagerFactory(0), - typeManager(e), uniqueId(1) + typeManager(e), importDatabase(e), uniqueId(1) { if (!qt_QmlQtModule_registered) { qt_QmlQtModule_registered = true; @@ -168,27 +165,6 @@ QDeclarativeEnginePrivate::QDeclarativeEnginePrivate(QDeclarativeEngine *e) QDeclarativeValueTypeFactory::registerValueTypes(); } globalClass = new QDeclarativeGlobalScriptClass(&scriptEngine); - - // env import paths - QByteArray envImportPath = qgetenv("QML_IMPORT_PATH"); - if (!envImportPath.isEmpty()) { -#if defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN) - QLatin1Char pathSep(';'); -#else - QLatin1Char pathSep(':'); -#endif - foreach (const QString &path, QString::fromLatin1(envImportPath).split(pathSep, QString::SkipEmptyParts)) { - QString canonicalPath = QDir(path).canonicalPath(); - if (!canonicalPath.isEmpty() && !fileImportPath.contains(canonicalPath)) - fileImportPath.append(canonicalPath); - } - } - QString builtinPath = QLibraryInfo::location(QLibraryInfo::ImportsPath); - if (!builtinPath.isEmpty()) - fileImportPath += builtinPath; - - filePluginPath += QLatin1String("."); - } QUrl QDeclarativeScriptEngine::resolvedUrl(QScriptContext *context, const QUrl& url) @@ -345,9 +321,6 @@ void QDeclarativeEnginePrivate::clear(SimpleList<QDeclarativeParserStatus> &pss) } Q_GLOBAL_STATIC(QDeclarativeEngineDebugServer, qmlEngineDebugServer); -typedef QMap<QString, QString> StringStringMap; -Q_GLOBAL_STATIC(StringStringMap, qmlEnginePluginsWithRegisteredTypes); // stores the uri - void QDeclarativePrivate::qdeclarativeelement_destructor(QObject *o) { @@ -1526,520 +1499,6 @@ QVariant QDeclarativeEnginePrivate::scriptValueToVariant(const QScriptValue &val return val.toVariant(); } -// XXX this beyonds in QUrl::toLocalFile() -// WARNING, there is a copy of this function in qdeclarativecompositetypemanager.cpp -static QString toLocalFileOrQrc(const QUrl& url) -{ - if (url.scheme() == QLatin1String("qrc")) { - if (url.authority().isEmpty()) - return QLatin1Char(':') + url.path(); - return QString(); - } - return url.toLocalFile(); -} - -///////////////////////////////////////////////////////////// -struct QDeclarativeEnginePrivate::ImportedNamespace { - QStringList uris; - QStringList urls; - QList<int> majversions; - QList<int> minversions; - QList<bool> isLibrary; - QList<QDeclarativeDirComponents> qmlDirComponents; - - - bool find_helper(int i, const QByteArray& type, int *vmajor, int *vminor, - QDeclarativeType** type_return, QUrl* url_return, - QUrl *base = 0, bool *typeRecursionDetected = 0) - { - int vmaj = majversions.at(i); - int vmin = minversions.at(i); - - QByteArray qt = uris.at(i).toUtf8(); - qt += '/'; - qt += type; - - QDeclarativeType *t = QDeclarativeMetaType::qmlType(qt,vmaj,vmin); - if (t) { - if (vmajor) *vmajor = vmaj; - if (vminor) *vminor = vmin; - if (type_return) - *type_return = t; - return true; - } - - QUrl url = QUrl(urls.at(i) + QLatin1Char('/') + QString::fromUtf8(type) + QLatin1String(".qml")); - QDeclarativeDirComponents qmldircomponents = qmlDirComponents.at(i); - - bool typeWasDeclaredInQmldir = false; - if (!qmldircomponents.isEmpty()) { - const QString typeName = QString::fromUtf8(type); - foreach (const QDeclarativeDirParser::Component &c, qmldircomponents) { - if (c.typeName == typeName) { - typeWasDeclaredInQmldir = true; - - // importing version -1 means import ALL versions - if ((vmaj == -1) || (c.majorVersion < vmaj || (c.majorVersion == vmaj && vmin >= c.minorVersion))) { - QUrl candidate = url.resolved(QUrl(c.fileName)); - if (c.internal && base) { - if (base->resolved(QUrl(c.fileName)) != candidate) - continue; // failed attempt to access an internal type - } - if (base && *base == candidate) { - if (typeRecursionDetected) - *typeRecursionDetected = true; - continue; // no recursion - } - if (url_return) - *url_return = candidate; - return true; - } - } - } - } - - if (!typeWasDeclaredInQmldir && !isLibrary.at(i)) { - // XXX search non-files too! (eg. zip files, see QT-524) - QFileInfo f(toLocalFileOrQrc(url)); - if (f.exists()) { - if (base && *base == url) { // no recursion - if (typeRecursionDetected) - *typeRecursionDetected = true; - } else { - if (url_return) - *url_return = url; - return true; - } - } - } - return false; - } - - bool find(const QByteArray& type, int *vmajor, int *vminor, QDeclarativeType** type_return, - QUrl* url_return, QUrl *base = 0, QString *errorString = 0) - { - bool typeRecursionDetected = false; - for (int i=0; i<urls.count(); ++i) { - if (find_helper(i, type, vmajor, vminor, type_return, url_return, base, &typeRecursionDetected)) { - if (qmlCheckTypes()) { - // check for type clashes - for (int j = i+1; j<urls.count(); ++j) { - if (find_helper(j, type, vmajor, vminor, 0, 0, base)) { - if (errorString) { - QString u1 = urls.at(i); - QString u2 = urls.at(j); - if (base) { - QString b = base->toString(); - int slash = b.lastIndexOf(QLatin1Char('/')); - if (slash >= 0) { - b = b.left(slash+1); - QString l = b.left(slash); - if (u1.startsWith(b)) - u1 = u1.mid(b.count()); - else if (u1 == l) - u1 = QDeclarativeEngine::tr("local directory"); - if (u2.startsWith(b)) - u2 = u2.mid(b.count()); - else if (u2 == l) - u2 = QDeclarativeEngine::tr("local directory"); - } - } - - if (u1 != u2) - *errorString - = QDeclarativeEngine::tr("is ambiguous. Found in %1 and in %2") - .arg(u1).arg(u2); - else - *errorString - = QDeclarativeEngine::tr("is ambiguous. Found in %1 in version %2.%3 and %4.%5") - .arg(u1) - .arg(majversions.at(i)).arg(minversions.at(i)) - .arg(majversions.at(j)).arg(minversions.at(j)); - } - return false; - } - } - } - return true; - } - } - if (errorString) { - if (typeRecursionDetected) - *errorString = QDeclarativeEngine::tr("is instantiated recursively"); - else - *errorString = QDeclarativeEngine::tr("is not a type"); - } - return false; - } -}; - -static bool greaterThan(const QString &s1, const QString &s2) -{ - return s1 > s2; -} - -class QDeclarativeImportsPrivate { -public: - QDeclarativeImportsPrivate(); - ~QDeclarativeImportsPrivate(); - - bool importExtension(const QString &absoluteFilePath, const QString &uri, - QDeclarativeEngine *engine, QDeclarativeDirComponents* components, - QString *errorString); - - QString resolvedUri(const QString &dir_arg, QDeclarativeEngine *engine); - bool add(const QUrl& base, const QDeclarativeDirComponents &qmldircomponentsnetwork, - const QString& uri_arg, const QString& prefix, - int vmaj, int vmin, QDeclarativeScriptParser::Import::Type importType, - QDeclarativeEngine *engine, QString *errorString); - bool find(const QByteArray& type, int *vmajor, int *vminor, - QDeclarativeType** type_return, QUrl* url_return, QString *errorString); - - QDeclarativeEnginePrivate::ImportedNamespace *findNamespace(const QString& type); - - QUrl base; - int ref; - -private: - friend struct QDeclarativeEnginePrivate::Imports; - - QSet<QString> qmlDirFilesForWhichPluginsHaveBeenLoaded; - QDeclarativeEnginePrivate::ImportedNamespace unqualifiedset; - QHash<QString,QDeclarativeEnginePrivate::ImportedNamespace* > set; -}; - -QDeclarativeEnginePrivate::Imports::Imports(const Imports ©) -: d(copy.d) -{ - ++d->ref; -} - -QDeclarativeEnginePrivate::Imports & -QDeclarativeEnginePrivate::Imports::operator =(const Imports ©) -{ - ++copy.d->ref; - if (--d->ref == 0) - delete d; - d = copy.d; - return *this; -} - -QDeclarativeEnginePrivate::Imports::Imports() -: d(new QDeclarativeImportsPrivate) -{ -} - -QDeclarativeEnginePrivate::Imports::~Imports() -{ - if (--d->ref == 0) - delete d; -} - -QDeclarativeImportsPrivate::QDeclarativeImportsPrivate() -: ref(1) -{ -} - -QDeclarativeImportsPrivate::~QDeclarativeImportsPrivate() -{ - foreach (QDeclarativeEnginePrivate::ImportedNamespace* s, set.values()) - delete s; -} - -bool QDeclarativeImportsPrivate::importExtension(const QString &absoluteFilePath, const QString &uri, QDeclarativeEngine *engine, - QDeclarativeDirComponents* components, QString *errorString) -{ - QFile file(absoluteFilePath); - QString filecontent; - if (file.open(QFile::ReadOnly)) { - filecontent = QString::fromUtf8(file.readAll()); - if (qmlImportTrace()) - qDebug() << "QDeclarativeEngine::add: loaded" << absoluteFilePath; - } else { - if (errorString) - *errorString = QDeclarativeEngine::tr("module \"%1\" definition \"%2\" not readable").arg(uri).arg(absoluteFilePath); - return false; - } - QDir dir = QFileInfo(file).dir(); - - QDeclarativeDirParser qmldirParser; - qmldirParser.setSource(filecontent); - qmldirParser.parse(); - - if (! qmlDirFilesForWhichPluginsHaveBeenLoaded.contains(absoluteFilePath)) { - qmlDirFilesForWhichPluginsHaveBeenLoaded.insert(absoluteFilePath); - - - foreach (const QDeclarativeDirParser::Plugin &plugin, qmldirParser.plugins()) { - - QString resolvedFilePath = - QDeclarativeEnginePrivate::get(engine) - ->resolvePlugin(dir, plugin.path, - plugin.name); - - if (!resolvedFilePath.isEmpty()) { - if (!engine->importPlugin(resolvedFilePath, uri, errorString)) { - if (errorString) - *errorString = QDeclarativeEngine::tr("plugin cannot be loaded for module \"%1\": %2").arg(uri).arg(*errorString); - return false; - } - } else { - if (errorString) - *errorString = QDeclarativeEngine::tr("module \"%1\" plugin \"%2\" not found").arg(uri).arg(plugin.name); - return false; - } - } - } - - if (components) - *components = qmldirParser.components(); - - return true; -} - -QString QDeclarativeImportsPrivate::resolvedUri(const QString &dir_arg, QDeclarativeEngine *engine) -{ - QString dir = dir_arg; - if (dir.endsWith(QLatin1Char('/')) || dir.endsWith(QLatin1Char('\\'))) - dir.chop(1); - - QStringList paths = QDeclarativeEnginePrivate::get(engine)->fileImportPath; - qSort(paths.begin(), paths.end(), greaterThan); // Ensure subdirs preceed their parents. - - QString stableRelativePath = dir; - foreach( QString path, paths) { - if (dir.startsWith(path)) { - stableRelativePath = dir.mid(path.length()+1); - break; - } - } - stableRelativePath.replace(QLatin1Char('/'), QLatin1Char('.')); - stableRelativePath.replace(QLatin1Char('\\'), QLatin1Char('.')); - return stableRelativePath; -} - -bool QDeclarativeImportsPrivate::add(const QUrl& base, const QDeclarativeDirComponents &qmldircomponentsnetwork, const QString& uri_arg, - const QString& prefix, int vmaj, int vmin, QDeclarativeScriptParser::Import::Type importType, - QDeclarativeEngine *engine, QString *errorString) -{ - QDeclarativeDirComponents qmldircomponents = qmldircomponentsnetwork; - QString uri = uri_arg; - QDeclarativeEnginePrivate::ImportedNamespace *s; - if (prefix.isEmpty()) { - s = &unqualifiedset; - } else { - s = set.value(prefix); - if (!s) - set.insert(prefix,(s=new QDeclarativeEnginePrivate::ImportedNamespace)); - } - - - - QString url = uri; - if (importType == QDeclarativeScriptParser::Import::Library) { - url.replace(QLatin1Char('.'), QLatin1Char('/')); - bool found = false; - QString dir; - - - foreach (const QString &p, - QDeclarativeEnginePrivate::get(engine)->fileImportPath) { - dir = p+QLatin1Char('/')+url; - - QFileInfo fi(dir+QLatin1String("/qmldir")); - const QString absoluteFilePath = fi.absoluteFilePath(); - - if (fi.isFile()) { - found = true; - - url = QUrl::fromLocalFile(fi.absolutePath()).toString(); - uri = resolvedUri(dir, engine); - if (!importExtension(absoluteFilePath, uri, engine, &qmldircomponents, errorString)) - return false; - break; - } - } - - if (!found) { - found = QDeclarativeMetaType::isModule(uri.toUtf8(), vmaj, vmin); - if (!found) { - if (errorString) { - bool anyversion = QDeclarativeMetaType::isModule(uri.toUtf8(), -1, -1); - if (anyversion) - *errorString = QDeclarativeEngine::tr("module \"%1\" version %2.%3 is not installed").arg(uri_arg).arg(vmaj).arg(vmin); - else - *errorString = QDeclarativeEngine::tr("module \"%1\" is not installed").arg(uri_arg); - } - return false; - } - } - } else { - - if (importType == QDeclarativeScriptParser::Import::File && qmldircomponents.isEmpty()) { - QUrl importUrl = base.resolved(QUrl(uri + QLatin1String("/qmldir"))); - QString localFileOrQrc = toLocalFileOrQrc(importUrl); - if (!localFileOrQrc.isEmpty()) { - QString dir = toLocalFileOrQrc(base.resolved(QUrl(uri))); - if (dir.isEmpty() || !QDir().exists(dir)) { - if (errorString) - *errorString = QDeclarativeEngine::tr("\"%1\": no such directory").arg(uri_arg); - return false; // local import dirs must exist - } - uri = resolvedUri(toLocalFileOrQrc(base.resolved(QUrl(uri))), engine); - if (uri.endsWith(QLatin1Char('/'))) - uri.chop(1); - if (QFile::exists(localFileOrQrc)) { - if (!importExtension(localFileOrQrc,uri,engine,&qmldircomponents,errorString)) - return false; - } - } else { - if (prefix.isEmpty()) { - // directory must at least exist for valid import - QString localFileOrQrc = toLocalFileOrQrc(base.resolved(QUrl(uri))); - if (localFileOrQrc.isEmpty() || !QDir().exists(localFileOrQrc)) { - if (errorString) { - if (localFileOrQrc.isEmpty()) - *errorString = QDeclarativeEngine::tr("import \"%1\" has no qmldir and no namespace").arg(uri); - else - *errorString = QDeclarativeEngine::tr("\"%1\": no such directory").arg(uri); - } - return false; - } - } - } - } - - url = base.resolved(QUrl(url)).toString(); - if (url.endsWith(QLatin1Char('/'))) - url.chop(1); - } - - if (vmaj > -1 && vmin > -1 && !qmldircomponents.isEmpty()) { - QList<QDeclarativeDirParser::Component>::ConstIterator it = qmldircomponents.begin(); - for (; it != qmldircomponents.end(); ++it) { - if (it->majorVersion > vmaj || (it->majorVersion == vmaj && it->minorVersion >= vmin)) - break; - } - if (it == qmldircomponents.end()) { - *errorString = QDeclarativeEngine::tr("module \"%1\" version %2.%3 is not installed").arg(uri_arg).arg(vmaj).arg(vmin); - return false; - } - } - - s->uris.prepend(uri); - s->urls.prepend(url); - s->majversions.prepend(vmaj); - s->minversions.prepend(vmin); - s->isLibrary.prepend(importType == QDeclarativeScriptParser::Import::Library); - s->qmlDirComponents.prepend(qmldircomponents); - return true; -} - -bool QDeclarativeImportsPrivate::find(const QByteArray& type, int *vmajor, int *vminor, QDeclarativeType** type_return, - QUrl* url_return, QString *errorString) -{ - QDeclarativeEnginePrivate::ImportedNamespace *s = 0; - int slash = type.indexOf('/'); - if (slash >= 0) { - QString namespaceName = QString::fromUtf8(type.left(slash)); - s = set.value(namespaceName); - if (!s) { - if (errorString) - *errorString = QDeclarativeEngine::tr("- %1 is not a namespace").arg(namespaceName); - return false; - } - int nslash = type.indexOf('/',slash+1); - if (nslash > 0) { - if (errorString) - *errorString = QDeclarativeEngine::tr("- nested namespaces not allowed"); - return false; - } - } else { - s = &unqualifiedset; - } - QByteArray unqualifiedtype = slash < 0 ? type : type.mid(slash+1); // common-case opt (QString::mid works fine, but slower) - if (s) { - if (s->find(unqualifiedtype,vmajor,vminor,type_return,url_return, &base, errorString)) - return true; - if (s->urls.count() == 1 && !s->isLibrary[0] && url_return && s != &unqualifiedset) { - // qualified, and only 1 url - *url_return = QUrl(s->urls[0]+QLatin1Char('/')).resolved(QUrl(QString::fromUtf8(unqualifiedtype) + QLatin1String(".qml"))); - return true; - } - } - - return false; -} - -QDeclarativeEnginePrivate::ImportedNamespace *QDeclarativeImportsPrivate::findNamespace(const QString& type) -{ - return set.value(type); -} - -static QDeclarativeTypeNameCache *cacheForNamespace(QDeclarativeEngine *engine, const QDeclarativeEnginePrivate::ImportedNamespace &set, QDeclarativeTypeNameCache *cache) -{ - if (!cache) - cache = new QDeclarativeTypeNameCache(engine); - - QList<QDeclarativeType *> types = QDeclarativeMetaType::qmlTypes(); - - for (int ii = 0; ii < set.uris.count(); ++ii) { - QByteArray base = set.uris.at(ii).toUtf8() + '/'; - int major = set.majversions.at(ii); - int minor = set.minversions.at(ii); - - foreach (QDeclarativeType *type, types) { - if (type->qmlTypeName().startsWith(base) && - type->qmlTypeName().lastIndexOf('/') == (base.length() - 1) && - type->availableInVersion(major,minor)) - { - QString name = QString::fromUtf8(type->qmlTypeName().mid(base.length())); - - cache->add(name, type); - } - } - } - - return cache; -} - -void QDeclarativeEnginePrivate::Imports::cache(QDeclarativeTypeNameCache *cache, QDeclarativeEngine *engine) const -{ - const QDeclarativeEnginePrivate::ImportedNamespace &set = d->unqualifiedset; - - for (QHash<QString,QDeclarativeEnginePrivate::ImportedNamespace* >::ConstIterator iter = d->set.begin(); - iter != d->set.end(); ++iter) { - - QDeclarativeTypeNameCache::Data *d = cache->data(iter.key()); - if (d) { - if (!d->typeNamespace) - cacheForNamespace(engine, *(*iter), d->typeNamespace); - } else { - QDeclarativeTypeNameCache *nc = cacheForNamespace(engine, *(*iter), 0); - cache->add(iter.key(), nc); - nc->release(); - } - } - - cacheForNamespace(engine, set, cache); -} - -/*! - Sets the base URL to be used for all relative file imports added. -*/ -void QDeclarativeEnginePrivate::Imports::setBaseUrl(const QUrl& url) -{ - d->base = url; -} - -/*! - Returns the base URL to be used for all relative file imports added. -*/ -QUrl QDeclarativeEnginePrivate::Imports::baseUrl() const -{ - return d->base; -} - /*! Adds \a path as a directory where the engine searches for installed modules in a URL-based directory structure. @@ -2050,16 +1509,8 @@ QUrl QDeclarativeEnginePrivate::Imports::baseUrl() const */ void QDeclarativeEngine::addImportPath(const QString& path) { - if (qmlImportTrace()) - qDebug() << "QDeclarativeEngine::addImportPath" << path; Q_D(QDeclarativeEngine); - QUrl url = QUrl(path); - if (url.isRelative() || url.scheme() == QString::fromLocal8Bit("file")) { - QDir dir = QDir(path); - d->fileImportPath.prepend(dir.canonicalPath()); - } else { - d->fileImportPath.prepend(path); - } + d->importDatabase.addImportPath(path); } /*! @@ -2080,7 +1531,7 @@ void QDeclarativeEngine::addImportPath(const QString& path) QStringList QDeclarativeEngine::importPathList() const { Q_D(const QDeclarativeEngine); - return d->fileImportPath; + return d->importDatabase.importPathList(); } /*! @@ -2095,7 +1546,7 @@ QStringList QDeclarativeEngine::importPathList() const void QDeclarativeEngine::setImportPathList(const QStringList &paths) { Q_D(QDeclarativeEngine); - d->fileImportPath = paths; + d->importDatabase.setImportPathList(paths); } @@ -2112,16 +1563,8 @@ void QDeclarativeEngine::setImportPathList(const QStringList &paths) */ void QDeclarativeEngine::addPluginPath(const QString& path) { - if (qmlImportTrace()) - qDebug() << "QDeclarativeEngine::addPluginPath" << path; Q_D(QDeclarativeEngine); - QUrl url = QUrl(path); - if (url.isRelative() || url.scheme() == QString::fromLocal8Bit("file")) { - QDir dir = QDir(path); - d->filePluginPath.prepend(dir.canonicalPath()); - } else { - d->filePluginPath.prepend(path); - } + d->importDatabase.addPluginPath(path); } @@ -2137,7 +1580,7 @@ void QDeclarativeEngine::addPluginPath(const QString& path) QStringList QDeclarativeEngine::pluginPathList() const { Q_D(const QDeclarativeEngine); - return d->filePluginPath; + return d->importDatabase.pluginPathList(); } /*! @@ -2153,7 +1596,7 @@ QStringList QDeclarativeEngine::pluginPathList() const void QDeclarativeEngine::setPluginPathList(const QStringList &paths) { Q_D(QDeclarativeEngine); - d->filePluginPath = paths; + d->importDatabase.setPluginPathList(paths); } @@ -2167,55 +1610,8 @@ void QDeclarativeEngine::setPluginPathList(const QStringList &paths) */ bool QDeclarativeEngine::importPlugin(const QString &filePath, const QString &uri, QString *errorString) { - if (qmlImportTrace()) - qDebug() << "QDeclarativeEngine::importPlugin" << uri << "from" << filePath; - QFileInfo fileInfo(filePath); - const QString absoluteFilePath = fileInfo.absoluteFilePath(); - - QDeclarativeEnginePrivate *d = QDeclarativeEnginePrivate::get(this); - bool engineInitialized = d->initializedPlugins.contains(absoluteFilePath); - bool typesRegistered = qmlEnginePluginsWithRegisteredTypes()->contains(absoluteFilePath); - - if (typesRegistered) { - Q_ASSERT_X(qmlEnginePluginsWithRegisteredTypes()->value(absoluteFilePath) == uri, - "QDeclarativeEngine::importExtension", - "Internal error: Plugin imported previously with different uri"); - } - - if (!engineInitialized || !typesRegistered) { - QPluginLoader loader(absoluteFilePath); - - if (!loader.load()) { - if (errorString) - *errorString = loader.errorString(); - return false; - } - - if (QDeclarativeExtensionInterface *iface = qobject_cast<QDeclarativeExtensionInterface *>(loader.instance())) { - - const QByteArray bytes = uri.toUtf8(); - const char *moduleId = bytes.constData(); - if (!typesRegistered) { - - // ### this code should probably be protected with a mutex. - qmlEnginePluginsWithRegisteredTypes()->insert(absoluteFilePath, uri); - iface->registerTypes(moduleId); - } - if (!engineInitialized) { - // things on the engine (eg. adding new global objects) have to be done for every engine. - - // protect against double initialization - d->initializedPlugins.insert(absoluteFilePath); - iface->initializeEngine(this, moduleId); - } - } else { - if (errorString) - *errorString = loader.errorString(); - return false; - } - } - - return true; + Q_D(QDeclarativeEngine); + return d->importDatabase.importPlugin(filePath, uri, errorString); } /*! @@ -2247,208 +1643,6 @@ QString QDeclarativeEngine::offlineStoragePath() const return d->scriptEngine.offlineStoragePath; } -/*! - \internal - - Returns the result of the merge of \a baseName with \a path, \a suffixes, and \a prefix. - The \a prefix must contain the dot. - - \a qmldirPath is the location of the qmldir file. - */ -QString QDeclarativeEnginePrivate::resolvePlugin(const QDir &qmldirPath, const QString &qmldirPluginPath, const QString &baseName, - const QStringList &suffixes, - const QString &prefix) -{ - QStringList searchPaths = filePluginPath; - bool qmldirPluginPathIsRelative = QDir::isRelativePath(qmldirPluginPath); - if (!qmldirPluginPathIsRelative) - searchPaths.prepend(qmldirPluginPath); - - foreach (const QString &pluginPath, searchPaths) { - - QString resolvedPath; - - if (pluginPath == QLatin1String(".")) { - if (qmldirPluginPathIsRelative) - resolvedPath = qmldirPath.absoluteFilePath(qmldirPluginPath); - else - resolvedPath = qmldirPath.absolutePath(); - } else { - resolvedPath = pluginPath; - } - - // hack for resources, should probably go away - if (resolvedPath.startsWith(QLatin1Char(':'))) - resolvedPath = QCoreApplication::applicationDirPath(); - - QDir dir(resolvedPath); - foreach (const QString &suffix, suffixes) { - QString pluginFileName = prefix; - - pluginFileName += baseName; - pluginFileName += suffix; - - QFileInfo fileInfo(dir, pluginFileName); - - if (fileInfo.exists()) - return fileInfo.absoluteFilePath(); - } - } - - if (qmlImportTrace()) - qDebug() << "QDeclarativeEngine::resolvePlugin: Could not resolve plugin" << baseName << "in" << qmldirPath.absolutePath(); - return QString(); -} - -/*! - \internal - - Returns the result of the merge of \a baseName with \a dir and the platform suffix. - - \table - \header \i Platform \i Valid suffixes - \row \i Windows \i \c .dll - \row \i Unix/Linux \i \c .so - \row \i AIX \i \c .a - \row \i HP-UX \i \c .sl, \c .so (HP-UXi) - \row \i Mac OS X \i \c .dylib, \c .bundle, \c .so - \row \i Symbian \i \c .dll - \endtable - - Version number on unix are ignored. -*/ -QString QDeclarativeEnginePrivate::resolvePlugin(const QDir &qmldirPath, const QString &qmldirPluginPath, const QString &baseName) -{ -#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) - return resolvePlugin(qmldirPath, qmldirPluginPath, baseName, - QStringList() -# ifdef QT_DEBUG - << QLatin1String("d.dll") // try a qmake-style debug build first -# endif - << QLatin1String(".dll")); -#elif defined(Q_OS_SYMBIAN) - return resolvePlugin(qmldirPath, qmldirPluginPath, baseName, - QStringList() - << QLatin1String(".dll") - << QLatin1String(".qtplugin")); -#else - -# if defined(Q_OS_DARWIN) - - return resolvePlugin(qmldirPath, qmldirPluginPath, baseName, - QStringList() -# ifdef QT_DEBUG - << QLatin1String("_debug.dylib") // try a qmake-style debug build first - << QLatin1String(".dylib") -# else - << QLatin1String(".dylib") - << QLatin1String("_debug.dylib") // try a qmake-style debug build after -# endif - << QLatin1String(".so") - << QLatin1String(".bundle"), - QLatin1String("lib")); -# else // Generic Unix - QStringList validSuffixList; - -# if defined(Q_OS_HPUX) -/* - See "HP-UX Linker and Libraries User's Guide", section "Link-time Differences between PA-RISC and IPF": - "In PA-RISC (PA-32 and PA-64) shared libraries are suffixed with .sl. In IPF (32-bit and 64-bit), - the shared libraries are suffixed with .so. For compatibility, the IPF linker also supports the .sl suffix." - */ - validSuffixList << QLatin1String(".sl"); -# if defined __ia64 - validSuffixList << QLatin1String(".so"); -# endif -# elif defined(Q_OS_AIX) - validSuffixList << QLatin1String(".a") << QLatin1String(".so"); -# elif defined(Q_OS_UNIX) - validSuffixList << QLatin1String(".so"); -# endif - - // Examples of valid library names: - // libfoo.so - - return resolvePlugin(qmldirPath, qmldirPluginPath, baseName, validSuffixList, QLatin1String("lib")); -# endif - -#endif -} - -/*! - \internal - - Adds information to \a imports such that subsequent calls to resolveType() - will resolve types qualified by \a prefix by considering types found at the given \a uri. - - The uri is either a directory (if importType is FileImport), or a URI resolved using paths - added via addImportPath() (if importType is LibraryImport). - - The \a prefix may be empty, in which case the import location is considered for - unqualified types. - - The base URL must already have been set with Import::setBaseUrl(). -*/ -bool QDeclarativeEnginePrivate::addToImport(Imports* imports, const QDeclarativeDirComponents &qmldircomponentsnetwork, const QString& uri, const QString& prefix, int vmaj, int vmin, QDeclarativeScriptParser::Import::Type importType, QString *errorString) const -{ - QDeclarativeEngine *engine = QDeclarativeEnginePrivate::get(const_cast<QDeclarativeEnginePrivate *>(this)); - if (qmlImportTrace()) - qDebug().nospace() << "QDeclarativeEngine::addToImport " << imports << " " << uri << " " << vmaj << '.' << vmin << " " << (importType==QDeclarativeScriptParser::Import::Library? "Library" : "File") << " as " << prefix; - bool ok = imports->d->add(imports->d->base,qmldircomponentsnetwork, uri,prefix,vmaj,vmin,importType, engine, errorString); - return ok; -} - -/*! - \internal - - Using the given \a imports, the given (namespace qualified) \a type is resolved to either - an ImportedNamespace stored at \a ns_return, - a QDeclarativeType stored at \a type_return, or - a component located at \a url_return. - - If any return pointer is 0, the corresponding search is not done. - - \sa addToImport() -*/ -bool QDeclarativeEnginePrivate::resolveType(const Imports& imports, const QByteArray& type, QDeclarativeType** type_return, QUrl* url_return, int *vmaj, int *vmin, ImportedNamespace** ns_return, QString *errorString) const -{ - ImportedNamespace* ns = imports.d->findNamespace(QString::fromUtf8(type)); - if (ns) { - if (ns_return) - *ns_return = ns; - return true; - } - if (type_return || url_return) { - if (imports.d->find(type,vmaj,vmin,type_return,url_return, errorString)) { - if (qmlImportTrace()) { - if (type_return && *type_return && url_return && !url_return->isEmpty()) - qDebug() << "QDeclarativeEngine::resolveType" << type << '=' << (*type_return)->typeName() << *url_return; - if (type_return && *type_return) - qDebug() << "QDeclarativeEngine::resolveType" << type << '=' << (*type_return)->typeName(); - if (url_return && !url_return->isEmpty()) - qDebug() << "QDeclarativeEngine::resolveType" << type << '=' << *url_return; - } - return true; - } - } - return false; -} - -/*! - \internal - - Searching \e only in the namespace \a ns (previously returned in a call to - resolveType(), \a type is found and returned to either - a QDeclarativeType stored at \a type_return, or - a component located at \a url_return. - - If either return pointer is 0, the corresponding search is not done. -*/ -void QDeclarativeEnginePrivate::resolveTypeInNamespace(ImportedNamespace* ns, const QByteArray& type, QDeclarativeType** type_return, QUrl* url_return, int *vmaj, int *vmin ) const -{ - ns->find(type,vmaj,vmin,type_return,url_return); -} - static void voidptr_destructor(void *v) { void **ptr = (void **)v; diff --git a/src/declarative/qml/qdeclarativeengine_p.h b/src/declarative/qml/qdeclarativeengine_p.h index b669f30..45656e8 100644 --- a/src/declarative/qml/qdeclarativeengine_p.h +++ b/src/declarative/qml/qdeclarativeengine_p.h @@ -57,6 +57,7 @@ #include "private/qdeclarativeclassfactory_p.h" #include "private/qdeclarativecompositetypemanager_p.h" +#include "private/qdeclarativeimport_p.h" #include "private/qpodvector_p.h" #include "qdeclarative.h" #include "private/qdeclarativevaluetype_p.h" @@ -164,7 +165,6 @@ public: bool outputWarningsToStdErr; - struct ImportedNamespace; QDeclarativeContextScriptClass *contextClass; QDeclarativeContextData *sharedContext; QObject *sharedScope; @@ -237,8 +237,8 @@ public: mutable QMutex mutex; QDeclarativeCompositeTypeManager typeManager; - QStringList fileImportPath; - QStringList filePluginPath; + QDeclarativeImportDatabase importDatabase; + QString offlineStoragePath; mutable quint32 uniqueId; @@ -252,46 +252,6 @@ public: inline QDeclarativePropertyCache *cache(QObject *obj); inline QDeclarativePropertyCache *cache(const QMetaObject *); - // ### This whole class is embarrassing - struct Imports { - Imports(); - ~Imports(); - Imports(const Imports ©); - Imports &operator =(const Imports ©); - - void setBaseUrl(const QUrl& url); - QUrl baseUrl() const; - - void cache(QDeclarativeTypeNameCache *cache, QDeclarativeEngine *) const; - - private: - friend class QDeclarativeEnginePrivate; - QDeclarativeImportsPrivate *d; - }; - - - QSet<QString> initializedPlugins; - - QString resolvePlugin(const QDir &qmldirPath, const QString &qmldirPluginPath, const QString &baseName, - const QStringList &suffixes, - const QString &prefix = QString()); - QString resolvePlugin(const QDir &qmldirPath, const QString &qmldirPluginPath, const QString &baseName); - - - bool addToImport(Imports*, const QDeclarativeDirComponents &qmldircomponentsnetwork, - const QString& uri, const QString& prefix, int vmaj, int vmin, - QDeclarativeScriptParser::Import::Type importType, - QString *errorString) const; - bool resolveType(const Imports&, const QByteArray& type, - QDeclarativeType** type_return, QUrl* url_return, - int *version_major, int *version_minor, - ImportedNamespace** ns_return, - QString *errorString = 0) const; - void resolveTypeInNamespace(ImportedNamespace*, const QByteArray& type, - QDeclarativeType** type_return, QUrl* url_return, - int *version_major, int *version_minor ) const; - - void registerCompositeType(QDeclarativeCompiledData *); bool isQObject(int); diff --git a/src/declarative/qml/qdeclarativeexpression.cpp b/src/declarative/qml/qdeclarativeexpression.cpp index f561a7e..5ceb918 100644 --- a/src/declarative/qml/qdeclarativeexpression.cpp +++ b/src/declarative/qml/qdeclarativeexpression.cpp @@ -57,10 +57,12 @@ QT_BEGIN_NAMESPACE bool QDeclarativeDelayedError::addError(QDeclarativeEnginePrivate *e) { - if (!e || prevError) return false; + if (!e) return false; if (e->inProgressCreations == 0) return false; // Not in construction + if (prevError) return true; // Already in error chain + prevError = &e->erroredBindings; nextError = e->erroredBindings; e->erroredBindings = this; diff --git a/src/declarative/qml/qdeclarativeimport.cpp b/src/declarative/qml/qdeclarativeimport.cpp new file mode 100644 index 0000000..65d42a1 --- /dev/null +++ b/src/declarative/qml/qdeclarativeimport.cpp @@ -0,0 +1,925 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (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 Technology Preview License Agreement accompanying +** this package. +** +** 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.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativeimport_p.h" + +#include <QtCore/qdebug.h> +#include <QtCore/qdir.h> +#include <QtCore/qfileinfo.h> +#include <QtCore/qpluginloader.h> +#include <QtCore/qlibraryinfo.h> +#include <QtDeclarative/qdeclarativeextensioninterface.h> +#include <private/qdeclarativeglobal_p.h> +#include <private/qdeclarativetypenamecache_p.h> + +QT_BEGIN_NAMESPACE + +DEFINE_BOOL_CONFIG_OPTION(qmlImportTrace, QML_IMPORT_TRACE) +DEFINE_BOOL_CONFIG_OPTION(qmlCheckTypes, QML_CHECK_TYPES) + +static QString toLocalFileOrQrc(const QUrl& url) +{ + if (url.scheme() == QLatin1String("qrc")) { + if (url.authority().isEmpty()) + return QLatin1Char(':') + url.path(); + return QString(); + } + return url.toLocalFile(); +} + +static bool greaterThan(const QString &s1, const QString &s2) +{ + return s1 > s2; +} + +typedef QMap<QString, QString> StringStringMap; +Q_GLOBAL_STATIC(StringStringMap, qmlEnginePluginsWithRegisteredTypes); // stores the uri + +class QDeclarativeImportedNamespace +{ +public: + QStringList uris; + QStringList urls; + QList<int> majversions; + QList<int> minversions; + QList<bool> isLibrary; + QList<QDeclarativeDirComponents> qmlDirComponents; + + + bool find_helper(int i, const QByteArray& type, int *vmajor, int *vminor, + QDeclarativeType** type_return, QUrl* url_return, + QUrl *base = 0, bool *typeRecursionDetected = 0); + bool find(const QByteArray& type, int *vmajor, int *vminor, QDeclarativeType** type_return, + QUrl* url_return, QUrl *base = 0, QString *errorString = 0); +}; + +class QDeclarativeImportsPrivate { +public: + QDeclarativeImportsPrivate(); + ~QDeclarativeImportsPrivate(); + + bool importExtension(const QString &absoluteFilePath, const QString &uri, + QDeclarativeImportDatabase *database, QDeclarativeDirComponents* components, + QString *errorString); + + QString resolvedUri(const QString &dir_arg, QDeclarativeImportDatabase *database); + bool add(const QDeclarativeDirComponents &qmldircomponentsnetwork, + const QString& uri_arg, const QString& prefix, + int vmaj, int vmin, QDeclarativeScriptParser::Import::Type importType, + QDeclarativeImportDatabase *database, QString *errorString); + bool find(const QByteArray& type, int *vmajor, int *vminor, + QDeclarativeType** type_return, QUrl* url_return, QString *errorString); + + QDeclarativeImportedNamespace *findNamespace(const QString& type); + + QUrl base; + int ref; + + QSet<QString> qmlDirFilesForWhichPluginsHaveBeenLoaded; + QDeclarativeImportedNamespace unqualifiedset; + QHash<QString,QDeclarativeImportedNamespace* > set; +}; + +QDeclarativeImports::QDeclarativeImports(const QDeclarativeImports ©) +: d(copy.d) +{ + ++d->ref; +} + +QDeclarativeImports & +QDeclarativeImports::operator =(const QDeclarativeImports ©) +{ + ++copy.d->ref; + if (--d->ref == 0) + delete d; + d = copy.d; + return *this; +} + +QDeclarativeImports::QDeclarativeImports() +: d(new QDeclarativeImportsPrivate) +{ +} + +QDeclarativeImports::~QDeclarativeImports() +{ + if (--d->ref == 0) + delete d; +} + +/*! + Sets the base URL to be used for all relative file imports added. +*/ +void QDeclarativeImports::setBaseUrl(const QUrl& url) +{ + d->base = url; +} + +/*! + Returns the base URL to be used for all relative file imports added. +*/ +QUrl QDeclarativeImports::baseUrl() const +{ + return d->base; +} + +static QDeclarativeTypeNameCache * +cacheForNamespace(QDeclarativeEngine *engine, const QDeclarativeImportedNamespace &set, + QDeclarativeTypeNameCache *cache) +{ + if (!cache) + cache = new QDeclarativeTypeNameCache(engine); + + QList<QDeclarativeType *> types = QDeclarativeMetaType::qmlTypes(); + + for (int ii = 0; ii < set.uris.count(); ++ii) { + QByteArray base = set.uris.at(ii).toUtf8() + '/'; + int major = set.majversions.at(ii); + int minor = set.minversions.at(ii); + + foreach (QDeclarativeType *type, types) { + if (type->qmlTypeName().startsWith(base) && + type->qmlTypeName().lastIndexOf('/') == (base.length() - 1) && + type->availableInVersion(major,minor)) + { + QString name = QString::fromUtf8(type->qmlTypeName().mid(base.length())); + + cache->add(name, type); + } + } + } + + return cache; +} + +void QDeclarativeImports::cache(QDeclarativeTypeNameCache *cache, QDeclarativeEngine *engine) const +{ + const QDeclarativeImportedNamespace &set = d->unqualifiedset; + + for (QHash<QString,QDeclarativeImportedNamespace* >::ConstIterator iter = d->set.begin(); + iter != d->set.end(); ++iter) { + + QDeclarativeTypeNameCache::Data *d = cache->data(iter.key()); + if (d) { + if (!d->typeNamespace) + cacheForNamespace(engine, *(*iter), d->typeNamespace); + } else { + QDeclarativeTypeNameCache *nc = cacheForNamespace(engine, *(*iter), 0); + cache->add(iter.key(), nc); + nc->release(); + } + } + + cacheForNamespace(engine, set, cache); +} +bool QDeclarativeImportedNamespace::find_helper(int i, const QByteArray& type, int *vmajor, int *vminor, + QDeclarativeType** type_return, QUrl* url_return, + QUrl *base, bool *typeRecursionDetected) +{ + int vmaj = majversions.at(i); + int vmin = minversions.at(i); + + QByteArray qt = uris.at(i).toUtf8(); + qt += '/'; + qt += type; + + QDeclarativeType *t = QDeclarativeMetaType::qmlType(qt,vmaj,vmin); + if (t) { + if (vmajor) *vmajor = vmaj; + if (vminor) *vminor = vmin; + if (type_return) + *type_return = t; + return true; + } + + QUrl url = QUrl(urls.at(i) + QLatin1Char('/') + QString::fromUtf8(type) + QLatin1String(".qml")); + QDeclarativeDirComponents qmldircomponents = qmlDirComponents.at(i); + + bool typeWasDeclaredInQmldir = false; + if (!qmldircomponents.isEmpty()) { + const QString typeName = QString::fromUtf8(type); + foreach (const QDeclarativeDirParser::Component &c, qmldircomponents) { + if (c.typeName == typeName) { + typeWasDeclaredInQmldir = true; + + // importing version -1 means import ALL versions + if ((vmaj == -1) || (c.majorVersion < vmaj || (c.majorVersion == vmaj && vmin >= c.minorVersion))) { + QUrl candidate = url.resolved(QUrl(c.fileName)); + if (c.internal && base) { + if (base->resolved(QUrl(c.fileName)) != candidate) + continue; // failed attempt to access an internal type + } + if (base && *base == candidate) { + if (typeRecursionDetected) + *typeRecursionDetected = true; + continue; // no recursion + } + if (url_return) + *url_return = candidate; + return true; + } + } + } + } + + if (!typeWasDeclaredInQmldir && !isLibrary.at(i)) { + // XXX search non-files too! (eg. zip files, see QT-524) + QFileInfo f(toLocalFileOrQrc(url)); + if (f.exists()) { + if (base && *base == url) { // no recursion + if (typeRecursionDetected) + *typeRecursionDetected = true; + } else { + if (url_return) + *url_return = url; + return true; + } + } + } + return false; +} + +QDeclarativeImportsPrivate::QDeclarativeImportsPrivate() +: ref(1) +{ +} + +QDeclarativeImportsPrivate::~QDeclarativeImportsPrivate() +{ + foreach (QDeclarativeImportedNamespace* s, set.values()) + delete s; +} + +bool QDeclarativeImportsPrivate::importExtension(const QString &absoluteFilePath, const QString &uri, + QDeclarativeImportDatabase *database, + QDeclarativeDirComponents* components, QString *errorString) +{ + QFile file(absoluteFilePath); + QString filecontent; + if (file.open(QFile::ReadOnly)) { + filecontent = QString::fromUtf8(file.readAll()); + if (qmlImportTrace()) + qDebug() << "QDeclarativeImportDatabase::add: loaded" << absoluteFilePath; + } else { + if (errorString) + *errorString = QDeclarativeImportDatabase::tr("module \"%1\" definition \"%2\" not readable").arg(uri).arg(absoluteFilePath); + return false; + } + QDir dir = QFileInfo(file).dir(); + + QDeclarativeDirParser qmldirParser; + qmldirParser.setSource(filecontent); + qmldirParser.parse(); + + if (! qmlDirFilesForWhichPluginsHaveBeenLoaded.contains(absoluteFilePath)) { + qmlDirFilesForWhichPluginsHaveBeenLoaded.insert(absoluteFilePath); + + + foreach (const QDeclarativeDirParser::Plugin &plugin, qmldirParser.plugins()) { + + QString resolvedFilePath = database->resolvePlugin(dir, plugin.path, plugin.name); + + if (!resolvedFilePath.isEmpty()) { + if (!database->importPlugin(resolvedFilePath, uri, errorString)) { + if (errorString) + *errorString = QDeclarativeImportDatabase::tr("plugin cannot be loaded for module \"%1\": %2").arg(uri).arg(*errorString); + return false; + } + } else { + if (errorString) + *errorString = QDeclarativeImportDatabase::tr("module \"%1\" plugin \"%2\" not found").arg(uri).arg(plugin.name); + return false; + } + } + } + + if (components) + *components = qmldirParser.components(); + + return true; +} + +QString QDeclarativeImportsPrivate::resolvedUri(const QString &dir_arg, QDeclarativeImportDatabase *database) +{ + QString dir = dir_arg; + if (dir.endsWith(QLatin1Char('/')) || dir.endsWith(QLatin1Char('\\'))) + dir.chop(1); + + QStringList paths = database->fileImportPath; + qSort(paths.begin(), paths.end(), greaterThan); // Ensure subdirs preceed their parents. + + QString stableRelativePath = dir; + foreach( QString path, paths) { + if (dir.startsWith(path)) { + stableRelativePath = dir.mid(path.length()+1); + break; + } + } + stableRelativePath.replace(QLatin1Char('/'), QLatin1Char('.')); + stableRelativePath.replace(QLatin1Char('\\'), QLatin1Char('.')); + return stableRelativePath; +} + +bool QDeclarativeImportsPrivate::add(const QDeclarativeDirComponents &qmldircomponentsnetwork, + const QString& uri_arg, const QString& prefix, int vmaj, int vmin, + QDeclarativeScriptParser::Import::Type importType, + QDeclarativeImportDatabase *database, QString *errorString) +{ + QDeclarativeDirComponents qmldircomponents = qmldircomponentsnetwork; + QString uri = uri_arg; + QDeclarativeImportedNamespace *s; + if (prefix.isEmpty()) { + s = &unqualifiedset; + } else { + s = set.value(prefix); + if (!s) + set.insert(prefix,(s=new QDeclarativeImportedNamespace)); + } + + QString url = uri; + if (importType == QDeclarativeScriptParser::Import::Library) { + url.replace(QLatin1Char('.'), QLatin1Char('/')); + bool found = false; + QString dir; + + + foreach (const QString &p, database->fileImportPath) { + dir = p+QLatin1Char('/')+url; + + QFileInfo fi(dir+QLatin1String("/qmldir")); + const QString absoluteFilePath = fi.absoluteFilePath(); + + if (fi.isFile()) { + found = true; + + url = QUrl::fromLocalFile(fi.absolutePath()).toString(); + uri = resolvedUri(dir, database); + if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, errorString)) + return false; + break; + } + } + + if (!found) { + found = QDeclarativeMetaType::isModule(uri.toUtf8(), vmaj, vmin); + if (!found) { + if (errorString) { + bool anyversion = QDeclarativeMetaType::isModule(uri.toUtf8(), -1, -1); + if (anyversion) + *errorString = QDeclarativeImportDatabase::tr("module \"%1\" version %2.%3 is not installed").arg(uri_arg).arg(vmaj).arg(vmin); + else + *errorString = QDeclarativeImportDatabase::tr("module \"%1\" is not installed").arg(uri_arg); + } + return false; + } + } + } else { + + if (importType == QDeclarativeScriptParser::Import::File && qmldircomponents.isEmpty()) { + QUrl importUrl = base.resolved(QUrl(uri + QLatin1String("/qmldir"))); + QString localFileOrQrc = toLocalFileOrQrc(importUrl); + if (!localFileOrQrc.isEmpty()) { + QString dir = toLocalFileOrQrc(base.resolved(QUrl(uri))); + if (dir.isEmpty() || !QDir().exists(dir)) { + if (errorString) + *errorString = QDeclarativeImportDatabase::tr("\"%1\": no such directory").arg(uri_arg); + return false; // local import dirs must exist + } + uri = resolvedUri(toLocalFileOrQrc(base.resolved(QUrl(uri))), database); + if (uri.endsWith(QLatin1Char('/'))) + uri.chop(1); + if (QFile::exists(localFileOrQrc)) { + if (!importExtension(localFileOrQrc,uri,database,&qmldircomponents,errorString)) + return false; + } + } else { + if (prefix.isEmpty()) { + // directory must at least exist for valid import + QString localFileOrQrc = toLocalFileOrQrc(base.resolved(QUrl(uri))); + if (localFileOrQrc.isEmpty() || !QDir().exists(localFileOrQrc)) { + if (errorString) { + if (localFileOrQrc.isEmpty()) + *errorString = QDeclarativeImportDatabase::tr("import \"%1\" has no qmldir and no namespace").arg(uri); + else + *errorString = QDeclarativeImportDatabase::tr("\"%1\": no such directory").arg(uri); + } + return false; + } + } + } + } + + url = base.resolved(QUrl(url)).toString(); + if (url.endsWith(QLatin1Char('/'))) + url.chop(1); + } + + if (vmaj > -1 && vmin > -1 && !qmldircomponents.isEmpty()) { + QList<QDeclarativeDirParser::Component>::ConstIterator it = qmldircomponents.begin(); + for (; it != qmldircomponents.end(); ++it) { + if (it->majorVersion > vmaj || (it->majorVersion == vmaj && it->minorVersion >= vmin)) + break; + } + if (it == qmldircomponents.end()) { + *errorString = QDeclarativeImportDatabase::tr("module \"%1\" version %2.%3 is not installed").arg(uri_arg).arg(vmaj).arg(vmin); + return false; + } + } + + s->uris.prepend(uri); + s->urls.prepend(url); + s->majversions.prepend(vmaj); + s->minversions.prepend(vmin); + s->isLibrary.prepend(importType == QDeclarativeScriptParser::Import::Library); + s->qmlDirComponents.prepend(qmldircomponents); + return true; +} + +bool QDeclarativeImportsPrivate::find(const QByteArray& type, int *vmajor, int *vminor, QDeclarativeType** type_return, + QUrl* url_return, QString *errorString) +{ + QDeclarativeImportedNamespace *s = 0; + int slash = type.indexOf('/'); + if (slash >= 0) { + QString namespaceName = QString::fromUtf8(type.left(slash)); + s = set.value(namespaceName); + if (!s) { + if (errorString) + *errorString = QDeclarativeImportDatabase::tr("- %1 is not a namespace").arg(namespaceName); + return false; + } + int nslash = type.indexOf('/',slash+1); + if (nslash > 0) { + if (errorString) + *errorString = QDeclarativeImportDatabase::tr("- nested namespaces not allowed"); + return false; + } + } else { + s = &unqualifiedset; + } + QByteArray unqualifiedtype = slash < 0 ? type : type.mid(slash+1); // common-case opt (QString::mid works fine, but slower) + if (s) { + if (s->find(unqualifiedtype,vmajor,vminor,type_return,url_return, &base, errorString)) + return true; + if (s->urls.count() == 1 && !s->isLibrary[0] && url_return && s != &unqualifiedset) { + // qualified, and only 1 url + *url_return = QUrl(s->urls[0]+QLatin1Char('/')).resolved(QUrl(QString::fromUtf8(unqualifiedtype) + QLatin1String(".qml"))); + return true; + } + } + + return false; +} + +QDeclarativeImportedNamespace *QDeclarativeImportsPrivate::findNamespace(const QString& type) +{ + return set.value(type); +} + +bool QDeclarativeImportedNamespace::find(const QByteArray& type, int *vmajor, int *vminor, QDeclarativeType** type_return, + QUrl* url_return, QUrl *base, QString *errorString) +{ + bool typeRecursionDetected = false; + for (int i=0; i<urls.count(); ++i) { + if (find_helper(i, type, vmajor, vminor, type_return, url_return, base, &typeRecursionDetected)) { + if (qmlCheckTypes()) { + // check for type clashes + for (int j = i+1; j<urls.count(); ++j) { + if (find_helper(j, type, vmajor, vminor, 0, 0, base)) { + if (errorString) { + QString u1 = urls.at(i); + QString u2 = urls.at(j); + if (base) { + QString b = base->toString(); + int slash = b.lastIndexOf(QLatin1Char('/')); + if (slash >= 0) { + b = b.left(slash+1); + QString l = b.left(slash); + if (u1.startsWith(b)) + u1 = u1.mid(b.count()); + else if (u1 == l) + u1 = QDeclarativeImportDatabase::tr("local directory"); + if (u2.startsWith(b)) + u2 = u2.mid(b.count()); + else if (u2 == l) + u2 = QDeclarativeImportDatabase::tr("local directory"); + } + } + + if (u1 != u2) + *errorString + = QDeclarativeImportDatabase::tr("is ambiguous. Found in %1 and in %2") + .arg(u1).arg(u2); + else + *errorString + = QDeclarativeImportDatabase::tr("is ambiguous. Found in %1 in version %2.%3 and %4.%5") + .arg(u1) + .arg(majversions.at(i)).arg(minversions.at(i)) + .arg(majversions.at(j)).arg(minversions.at(j)); + } + return false; + } + } + } + return true; + } + } + if (errorString) { + if (typeRecursionDetected) + *errorString = QDeclarativeImportDatabase::tr("is instantiated recursively"); + else + *errorString = QDeclarativeImportDatabase::tr("is not a type"); + } + return false; +} + +QDeclarativeImportDatabase::QDeclarativeImportDatabase(QDeclarativeEngine *e) +: engine(e) +{ + filePluginPath << QLatin1String("."); + + QString builtinPath = QLibraryInfo::location(QLibraryInfo::ImportsPath); + if (!builtinPath.isEmpty()) + addImportPath(builtinPath); + + // env import paths + QByteArray envImportPath = qgetenv("QML_IMPORT_PATH"); + if (!envImportPath.isEmpty()) { +#if defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN) + QLatin1Char pathSep(';'); +#else + QLatin1Char pathSep(':'); +#endif + QStringList paths = QString::fromLatin1(envImportPath).split(pathSep, QString::SkipEmptyParts); + for (int ii = paths.count() - 1; ii >= 0; --ii) + addImportPath(paths.at(ii)); + } +} + +QDeclarativeImportDatabase::~QDeclarativeImportDatabase() +{ +} + +/*! + \internal + + Adds information to \a imports such that subsequent calls to resolveType() + will resolve types qualified by \a prefix by considering types found at the given \a uri. + + The uri is either a directory (if importType is FileImport), or a URI resolved using paths + added via addImportPath() (if importType is LibraryImport). + + The \a prefix may be empty, in which case the import location is considered for + unqualified types. + + The base URL must already have been set with Import::setBaseUrl(). +*/ +bool QDeclarativeImportDatabase::addToImport(QDeclarativeImports* imports, + const QDeclarativeDirComponents &qmldircomponentsnetwork, + const QString& uri, const QString& prefix, int vmaj, int vmin, + QDeclarativeScriptParser::Import::Type importType, + QString *errorString) +{ + if (qmlImportTrace()) + qDebug().nospace() << "QDeclarativeImportDatabase::addToImport " << imports << " " << uri << " " + << vmaj << '.' << vmin << " " + << (importType==QDeclarativeScriptParser::Import::Library? "Library" : "File") + << " as " << prefix; + + bool ok = imports->d->add(qmldircomponentsnetwork, uri, prefix, vmaj, vmin, importType, this, errorString); + return ok; +} + +/*! + \internal + + Using the given \a imports, the given (namespace qualified) \a type is resolved to either + a QDeclarativeImportedNamespace stored at \a ns_return, + a QDeclarativeType stored at \a type_return, or + a component located at \a url_return. + + If any return pointer is 0, the corresponding search is not done. + + \sa addToImport() +*/ +bool QDeclarativeImportDatabase::resolveType(const QDeclarativeImports& imports, const QByteArray& type, + QDeclarativeType** type_return, QUrl* url_return, int *vmaj, int *vmin, + QDeclarativeImportedNamespace** ns_return, QString *errorString) const +{ + QDeclarativeImportedNamespace* ns = imports.d->findNamespace(QString::fromUtf8(type)); + if (ns) { + if (ns_return) + *ns_return = ns; + return true; + } + if (type_return || url_return) { + if (imports.d->find(type,vmaj,vmin,type_return,url_return, errorString)) { + if (qmlImportTrace()) { + if (type_return && *type_return && url_return && !url_return->isEmpty()) + qDebug() << "QDeclarativeImportDatabase::resolveType" << type << '=' << (*type_return)->typeName() << *url_return; + if (type_return && *type_return) + qDebug() << "QDeclarativeImportDatabase::resolveType" << type << '=' << (*type_return)->typeName(); + if (url_return && !url_return->isEmpty()) + qDebug() << "QDeclarativeImportDatabase::resolveType" << type << '=' << *url_return; + } + return true; + } + } + return false; +} + +/*! + \internal + + Searching \e only in the namespace \a ns (previously returned in a call to + resolveType(), \a type is found and returned to either + a QDeclarativeType stored at \a type_return, or + a component located at \a url_return. + + If either return pointer is 0, the corresponding search is not done. +*/ +void QDeclarativeImportDatabase::resolveTypeInNamespace(QDeclarativeImportedNamespace* ns, const QByteArray& type, + QDeclarativeType** type_return, QUrl* url_return, + int *vmaj, int *vmin) const +{ + ns->find(type,vmaj,vmin,type_return,url_return); +} + +/*! + \internal + + Returns the result of the merge of \a baseName with \a path, \a suffixes, and \a prefix. + The \a prefix must contain the dot. + + \a qmldirPath is the location of the qmldir file. + */ +QString QDeclarativeImportDatabase::resolvePlugin(const QDir &qmldirPath, const QString &qmldirPluginPath, + const QString &baseName, const QStringList &suffixes, + const QString &prefix) +{ + QStringList searchPaths = filePluginPath; + bool qmldirPluginPathIsRelative = QDir::isRelativePath(qmldirPluginPath); + if (!qmldirPluginPathIsRelative) + searchPaths.prepend(qmldirPluginPath); + + foreach (const QString &pluginPath, searchPaths) { + + QString resolvedPath; + + if (pluginPath == QLatin1String(".")) { + if (qmldirPluginPathIsRelative) + resolvedPath = qmldirPath.absoluteFilePath(qmldirPluginPath); + else + resolvedPath = qmldirPath.absolutePath(); + } else { + resolvedPath = pluginPath; + } + + // hack for resources, should probably go away + if (resolvedPath.startsWith(QLatin1Char(':'))) + resolvedPath = QCoreApplication::applicationDirPath(); + + QDir dir(resolvedPath); + foreach (const QString &suffix, suffixes) { + QString pluginFileName = prefix; + + pluginFileName += baseName; + pluginFileName += suffix; + + QFileInfo fileInfo(dir, pluginFileName); + + if (fileInfo.exists()) + return fileInfo.absoluteFilePath(); + } + } + + if (qmlImportTrace()) + qDebug() << "QDeclarativeImportDatabase::resolvePlugin: Could not resolve plugin" << baseName + << "in" << qmldirPath.absolutePath(); + + return QString(); +} + +/*! + \internal + + Returns the result of the merge of \a baseName with \a dir and the platform suffix. + + \table + \header \i Platform \i Valid suffixes + \row \i Windows \i \c .dll + \row \i Unix/Linux \i \c .so + \row \i AIX \i \c .a + \row \i HP-UX \i \c .sl, \c .so (HP-UXi) + \row \i Mac OS X \i \c .dylib, \c .bundle, \c .so + \row \i Symbian \i \c .dll + \endtable + + Version number on unix are ignored. +*/ +QString QDeclarativeImportDatabase::resolvePlugin(const QDir &qmldirPath, const QString &qmldirPluginPath, + const QString &baseName) +{ +#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) + return resolvePlugin(qmldirPath, qmldirPluginPath, baseName, + QStringList() +# ifdef QT_DEBUG + << QLatin1String("d.dll") // try a qmake-style debug build first +# endif + << QLatin1String(".dll")); +#elif defined(Q_OS_SYMBIAN) + return resolvePlugin(qmldirPath, qmldirPluginPath, baseName, + QStringList() + << QLatin1String(".dll") + << QLatin1String(".qtplugin")); +#else + +# if defined(Q_OS_DARWIN) + + return resolvePlugin(qmldirPath, qmldirPluginPath, baseName, + QStringList() +# ifdef QT_DEBUG + << QLatin1String("_debug.dylib") // try a qmake-style debug build first + << QLatin1String(".dylib") +# else + << QLatin1String(".dylib") + << QLatin1String("_debug.dylib") // try a qmake-style debug build after +# endif + << QLatin1String(".so") + << QLatin1String(".bundle"), + QLatin1String("lib")); +# else // Generic Unix + QStringList validSuffixList; + +# if defined(Q_OS_HPUX) +/* + See "HP-UX Linker and Libraries User's Guide", section "Link-time Differences between PA-RISC and IPF": + "In PA-RISC (PA-32 and PA-64) shared libraries are suffixed with .sl. In IPF (32-bit and 64-bit), + the shared libraries are suffixed with .so. For compatibility, the IPF linker also supports the .sl suffix." + */ + validSuffixList << QLatin1String(".sl"); +# if defined __ia64 + validSuffixList << QLatin1String(".so"); +# endif +# elif defined(Q_OS_AIX) + validSuffixList << QLatin1String(".a") << QLatin1String(".so"); +# elif defined(Q_OS_UNIX) + validSuffixList << QLatin1String(".so"); +# endif + + // Examples of valid library names: + // libfoo.so + + return resolvePlugin(qmldirPath, qmldirPluginPath, baseName, validSuffixList, QLatin1String("lib")); +# endif + +#endif +} + +QStringList QDeclarativeImportDatabase::pluginPathList() const +{ + return filePluginPath; +} + +void QDeclarativeImportDatabase::setPluginPathList(const QStringList &paths) +{ + filePluginPath = paths; +} + +void QDeclarativeImportDatabase::addPluginPath(const QString& path) +{ + if (qmlImportTrace()) + qDebug() << "QDeclarativeImportDatabase::addPluginPath" << path; + + QUrl url = QUrl(path); + if (url.isRelative() || url.scheme() == QString::fromLocal8Bit("file")) { + QDir dir = QDir(path); + filePluginPath.prepend(dir.canonicalPath()); + } else { + filePluginPath.prepend(path); + } +} + +void QDeclarativeImportDatabase::addImportPath(const QString& path) +{ + if (qmlImportTrace()) + qDebug() << "QDeclarativeImportDatabase::addImportPath" << path; + + QUrl url = QUrl(path); + QString cPath; + + if (url.isRelative() || url.scheme() == QString::fromLocal8Bit("file")) { + QDir dir = QDir(path); + cPath = dir.canonicalPath(); + } else { + cPath = path; + } + + if (!fileImportPath.contains(cPath)) + fileImportPath.prepend(cPath); +} + +QStringList QDeclarativeImportDatabase::importPathList() const +{ + return fileImportPath; +} + +void QDeclarativeImportDatabase::setImportPathList(const QStringList &paths) +{ + fileImportPath = paths; +} + + +bool QDeclarativeImportDatabase::importPlugin(const QString &filePath, const QString &uri, QString *errorString) +{ + if (qmlImportTrace()) + qDebug() << "QDeclarativeImportDatabase::importPlugin" << uri << "from" << filePath; + + QFileInfo fileInfo(filePath); + const QString absoluteFilePath = fileInfo.absoluteFilePath(); + + bool engineInitialized = initializedPlugins.contains(absoluteFilePath); + bool typesRegistered = qmlEnginePluginsWithRegisteredTypes()->contains(absoluteFilePath); + + if (typesRegistered) { + Q_ASSERT_X(qmlEnginePluginsWithRegisteredTypes()->value(absoluteFilePath) == uri, + "QDeclarativeImportDatabase::importExtension", + "Internal error: Plugin imported previously with different uri"); + } + + if (!engineInitialized || !typesRegistered) { + QPluginLoader loader(absoluteFilePath); + + if (!loader.load()) { + if (errorString) + *errorString = loader.errorString(); + return false; + } + + if (QDeclarativeExtensionInterface *iface = qobject_cast<QDeclarativeExtensionInterface *>(loader.instance())) { + + const QByteArray bytes = uri.toUtf8(); + const char *moduleId = bytes.constData(); + if (!typesRegistered) { + + // ### this code should probably be protected with a mutex. + qmlEnginePluginsWithRegisteredTypes()->insert(absoluteFilePath, uri); + iface->registerTypes(moduleId); + } + if (!engineInitialized) { + // things on the engine (eg. adding new global objects) have to be done for every engine. + + // protect against double initialization + initializedPlugins.insert(absoluteFilePath); + iface->initializeEngine(engine, moduleId); + } + } else { + if (errorString) + *errorString = loader.errorString(); + return false; + } + } + + return true; +} + + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qdeclarativeimport_p.h b/src/declarative/qml/qdeclarativeimport_p.h new file mode 100644 index 0000000..62b0517 --- /dev/null +++ b/src/declarative/qml/qdeclarativeimport_p.h @@ -0,0 +1,139 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (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 Technology Preview License Agreement accompanying +** this package. +** +** 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.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEIMPORT_P_H +#define QDECLARATIVEIMPORT_P_H + +#include <QtCore/qurl.h> +#include <QtCore/qcoreapplication.h> +#include <QtCore/qset.h> +#include <private/qdeclarativedirparser_p.h> +#include <private/qdeclarativescriptparser_p.h> +#include <private/qdeclarativemetatype_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. +// + +QT_BEGIN_NAMESPACE + +class QDeclarativeTypeNameCache; +class QDeclarativeEngine; +class QDir; + +class QDeclarativeImportedNamespace; +class QDeclarativeImportsPrivate; +class QDeclarativeImports +{ +public: + QDeclarativeImports(); + QDeclarativeImports(const QDeclarativeImports &); + ~QDeclarativeImports(); + QDeclarativeImports &operator=(const QDeclarativeImports &); + + void setBaseUrl(const QUrl &url); + QUrl baseUrl() const; + + void cache(QDeclarativeTypeNameCache *cache, QDeclarativeEngine *) const; +private: + friend class QDeclarativeImportDatabase; + QDeclarativeImportsPrivate *d; +}; + +class QDeclarativeImportDatabase +{ + Q_DECLARE_TR_FUNCTIONS(QDeclarativeImportDatabase) +public: + QDeclarativeImportDatabase(QDeclarativeEngine *); + ~QDeclarativeImportDatabase(); + + bool importPlugin(const QString &filePath, const QString &uri, QString *errorString); + + QStringList importPathList() const; + void setImportPathList(const QStringList &paths); + void addImportPath(const QString& dir); + + QStringList pluginPathList() const; + void setPluginPathList(const QStringList &paths); + void addPluginPath(const QString& path); + + + bool addToImport(QDeclarativeImports*, const QDeclarativeDirComponents &qmldircomponentsnetwork, + const QString& uri, const QString& prefix, int vmaj, int vmin, + QDeclarativeScriptParser::Import::Type importType, + QString *errorString); + bool resolveType(const QDeclarativeImports&, const QByteArray& type, + QDeclarativeType** type_return, QUrl* url_return, + int *version_major, int *version_minor, + QDeclarativeImportedNamespace** ns_return, + QString *errorString = 0) const; + void resolveTypeInNamespace(QDeclarativeImportedNamespace*, const QByteArray& type, + QDeclarativeType** type_return, QUrl* url_return, + int *version_major, int *version_minor ) const; + + +private: + friend class QDeclarativeImportsPrivate; + QString resolvePlugin(const QDir &qmldirPath, const QString &qmldirPluginPath, + const QString &baseName, const QStringList &suffixes, + const QString &prefix = QString()); + QString resolvePlugin(const QDir &qmldirPath, const QString &qmldirPluginPath, + const QString &baseName); + + + QStringList filePluginPath; + QStringList fileImportPath; + + QSet<QString> initializedPlugins; + QDeclarativeEngine *engine; +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVEIMPORT_P_H + diff --git a/src/declarative/qml/qml.pri b/src/declarative/qml/qml.pri index 3848593..dab9767 100644 --- a/src/declarative/qml/qml.pri +++ b/src/declarative/qml/qml.pri @@ -54,6 +54,7 @@ SOURCES += \ $$PWD/qdeclarativenetworkaccessmanagerfactory.cpp \ $$PWD/qdeclarativedirparser.cpp \ $$PWD/qdeclarativeextensionplugin.cpp \ + $$PWD/qdeclarativeimport.cpp \ $$PWD/qdeclarativelist.cpp HEADERS += \ @@ -128,6 +129,7 @@ HEADERS += \ $$PWD/qdeclarativenetworkaccessmanagerfactory.h \ $$PWD/qdeclarativedirparser_p.h \ $$PWD/qdeclarativeextensioninterface.h \ + $$PWD/qdeclarativeimport_p.h \ $$PWD/qdeclarativeextensionplugin.h QT += sql diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/transientErrors.2.qml b/tests/auto/declarative/qdeclarativeecmascript/data/transientErrors.2.qml new file mode 100644 index 0000000..a36b4c0 --- /dev/null +++ b/tests/auto/declarative/qdeclarativeecmascript/data/transientErrors.2.qml @@ -0,0 +1,14 @@ +import Qt 4.7 + +QtObject { + id: root + + property variant a: 10 + property int x: 10 + property int test: a.x + + Component.onCompleted: { + a = 11; + a = root; + } +} diff --git a/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp b/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp index 491a736..6cde46b 100644 --- a/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp +++ b/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp @@ -1149,6 +1149,7 @@ static void transientErrorsMsgHandler(QtMsgType, const char *) // Check that transient binding errors are not displayed void tst_qdeclarativeecmascript::transientErrors() { + { QDeclarativeComponent component(&engine, TEST_FILE("transientErrors.qml")); transientErrorsMsgCount = 0; @@ -1160,6 +1161,22 @@ void tst_qdeclarativeecmascript::transientErrors() qInstallMsgHandler(old); QCOMPARE(transientErrorsMsgCount, 0); + } + + // One binding erroring multiple times, but then resolving + { + QDeclarativeComponent component(&engine, TEST_FILE("transientErrors.2.qml")); + + transientErrorsMsgCount = 0; + QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler); + + QObject *object = component.create(); + QVERIFY(object != 0); + + qInstallMsgHandler(old); + + QCOMPARE(transientErrorsMsgCount, 0); + } } // Check that errors during shutdown are minimized diff --git a/tests/auto/declarative/qdeclarativepathview/data/pathUpdateOnStartChanged.qml b/tests/auto/declarative/qdeclarativepathview/data/pathUpdateOnStartChanged.qml new file mode 100644 index 0000000..ce0f0c9 --- /dev/null +++ b/tests/auto/declarative/qdeclarativepathview/data/pathUpdateOnStartChanged.qml @@ -0,0 +1,38 @@ +import Qt 4.7 + +Rectangle { + width: 800 + height: 480 + color: "black" + resources: [ + ListModel { + id: appModel + ListElement { color: "green" } + }, + Component { + id: appDelegate + Rectangle { + id: wrapper + objectName: "wrapper" + color: "green" + width: 100 + height: 100 + } + } + ] + PathView { + id: pathView + objectName: "pathView" + model: appModel + anchors.fill: parent + + transformOrigin: "Top" + delegate: appDelegate + path: Path { + objectName: "path" + startX: pathView.width / 2 // startX: 400 <- this works as expected + startY: 300 + PathLine { x: 400; y: 120 } + } + } +} diff --git a/tests/auto/declarative/qdeclarativepathview/tst_qdeclarativepathview.cpp b/tests/auto/declarative/qdeclarativepathview/tst_qdeclarativepathview.cpp index 0e3a74d..c32e9cc 100644 --- a/tests/auto/declarative/qdeclarativepathview/tst_qdeclarativepathview.cpp +++ b/tests/auto/declarative/qdeclarativepathview/tst_qdeclarativepathview.cpp @@ -77,6 +77,7 @@ private slots: void pathChanges(); void componentChanges(); void modelChanges(); + void pathUpdateOnStartChanged(); private: @@ -672,6 +673,28 @@ void tst_QDeclarativePathView::modelChanges() delete canvas; } +void tst_QDeclarativePathView::pathUpdateOnStartChanged() +{ + QDeclarativeView *canvas = createView(); + QVERIFY(canvas); + canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/pathUpdateOnStartChanged.qml")); + + QDeclarativePathView *pathView = canvas->rootObject()->findChild<QDeclarativePathView*>("pathView"); + QVERIFY(pathView); + + QDeclarativePath *path = canvas->rootObject()->findChild<QDeclarativePath*>("path"); + QVERIFY(path); + QCOMPARE(path->startX(), 400.0); + QCOMPARE(path->startY(), 300.0); + + QDeclarativeItem *item = findItem<QDeclarativeItem>(pathView, "wrapper", 0); + QVERIFY(item); + QCOMPARE(item->x(), path->startX() - item->width() / 2.0); + QCOMPARE(item->y(), path->startY() - item->height() / 2.0); + + delete canvas; +} + QDeclarativeView *tst_QDeclarativePathView::createView() { QDeclarativeView *canvas = new QDeclarativeView(0); |