diff options
author | Qt by Nokia <qt-info@nokia.com> | 2011-04-27 14:13:26 +0200 |
---|---|---|
committer | axis <qt-info@nokia.com> | 2011-04-27 14:40:44 +0200 |
commit | a129444bb0156c936900dbd2f12bd9f427ff366c (patch) | |
tree | e2e8e77f185d5a5ac32ea0a96cd9945c742e8663 /tools | |
parent | 885735d011472bcfbb96e688d9e64553d7fe9d4b (diff) |
Initial import from qtquick2.
Branched from the monolithic repo, Qt qtquick2 branch, at commit
a4a585d2ee907746682846ae6e8a48e19deef469
Diffstat (limited to 'tools')
-rw-r--r-- | tools/distfieldgen/distfieldgen.pro | 12 | ||||
-rw-r--r-- | tools/distfieldgen/main.cpp | 262 | ||||
-rw-r--r-- | tools/qmlplugindump/Info.plist | 16 | ||||
-rw-r--r-- | tools/qmlplugindump/main.cpp | 597 | ||||
-rw-r--r-- | tools/qmlplugindump/qmlplugindump.pro | 20 | ||||
-rw-r--r-- | tools/qmlplugindump/qmlstreamwriter.cpp | 183 | ||||
-rw-r--r-- | tools/qmlplugindump/qmlstreamwriter.h | 79 | ||||
-rw-r--r-- | tools/qmlscene/main.cpp | 574 | ||||
-rw-r--r-- | tools/qmlscene/qmlscene.pro | 20 | ||||
-rw-r--r-- | tools/qmlviewer/main.cpp | 7 | ||||
-rw-r--r-- | tools/qmlviewer/qmlruntime.cpp | 1 | ||||
-rw-r--r-- | tools/tools.pro | 2 |
12 files changed, 1769 insertions, 4 deletions
diff --git a/tools/distfieldgen/distfieldgen.pro b/tools/distfieldgen/distfieldgen.pro new file mode 100644 index 0000000000..4c2d63603b --- /dev/null +++ b/tools/distfieldgen/distfieldgen.pro @@ -0,0 +1,12 @@ +TARGET = distfieldgen +TEMPLATE = app + +QT += declarative opengl + +CONFIG += console +CONFIG -= app_bundle +DESTDIR = ../../bin + +INCLUDEPATH += $$PWD/../../src/3rdparty/harfbuzz/src + +SOURCES += main.cpp diff --git a/tools/distfieldgen/main.cpp b/tools/distfieldgen/main.cpp new file mode 100644 index 0000000000..3c944b3d4c --- /dev/null +++ b/tools/distfieldgen/main.cpp @@ -0,0 +1,262 @@ +/**************************************************************************** +** +** 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 Qt scene graph research project. +** +** $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 <QtCore> +#include <QtGui> + +#include <private/qsgdistancefieldglyphcache_p.h> + +static void usage() +{ + qWarning("Usage: distfieldgen [options] <font_filename>"); + qWarning(" "); + qWarning("Distfieldgen generates distance-field renderings of the provided font file,"); + qWarning("one for each font family/style it contains."); + qWarning("Unless the QT_QML_DISTFIELDDIR environment variable is set, the renderings are"); + qWarning("saved in the fonts/distancefields directory where the Qt libraries are located."); + qWarning("You can also override the output directory with the -d option."); + qWarning(" "); + qWarning(" options:"); + qWarning(" -d <directory>................................ output directory"); + qWarning(" --no-multithread.............................. don't use multiple threads to render distance-fields"); + qWarning(" --force-all-styles............................ force rendering of styles Normal, Bold, Italic and Bold Italic"); + qWarning(" -styles \"style1,style2,..\".................... force rendering of specified styles"); + + qWarning(" "); + exit(1); +} + +void printProgress(int p) +{ + printf("\r ["); + for (int i = 0; i < 50; ++i) + printf(i < p / 2 ? "=" : " "); + printf("]"); + printf(" %d%%", p); + fflush(stdout); +} + +class DistFieldGenTask : public QRunnable +{ +public: + DistFieldGenTask(QSGDistanceFieldGlyphCache *atlas, int c, int nbGlyph, QMap<int, QImage> *outList) + : QRunnable() + , m_atlas(atlas) + , m_char(c) + , m_nbGlyph(nbGlyph) + , m_outList(outList) + { } + + void run() + { + QImage df = m_atlas->renderDistanceFieldGlyph(m_char); + QMutexLocker lock(&m_mutex); + m_outList->insert(m_char, df); + printProgress(float(m_outList->count()) / m_nbGlyph * 100); + } + + static QMutex m_mutex; + QSGDistanceFieldGlyphCache *m_atlas; + int m_char; + int m_nbGlyph; + QMap<int, QImage> *m_outList; +}; + +QMutex DistFieldGenTask::m_mutex; + +static void generateDistanceFieldForFont(const QFont &font, const QString &destinationDir, bool multithread) +{ + QSGDistanceFieldGlyphCache *atlas = QSGDistanceFieldGlyphCache::get(QGLContext::currentContext(), font); + QFontDatabase db; + QString fontString = font.family() + QLatin1String(" ") + db.styleString(font); + qWarning("> Generating distance-field for font '%s' (%d glyphs)", fontString.toLatin1().constData(), atlas->glyphCount()); + + QMap<int, QImage> distfields; + for (int i = 0; i < atlas->glyphCount(); ++i) { + if (multithread) { + DistFieldGenTask *task = new DistFieldGenTask(atlas, i, atlas->glyphCount(), &distfields); + QThreadPool::globalInstance()->start(task); + } else { + QImage df = atlas->renderDistanceFieldGlyph(i); + distfields.insert(i, df); + printProgress(float(distfields.count()) / atlas->glyphCount() * 100); + } + } + + if (multithread) + QThreadPool::globalInstance()->waitForDone(); + + // Combine dist fields in one image + int size = qCeil(qSqrt(qreal(atlas->glyphCount()))) * 64; + QImage output(size, size, QImage::Format_ARGB32_Premultiplied); + output.fill(Qt::transparent); + QPainter p(&output); + int x, y = 0; + for (QMap<int, QImage>::const_iterator i = distfields.constBegin(); i != distfields.constEnd(); ++i) { + p.drawImage(x, y, i.value()); + x += 64; + if (x >= size) { + x = 0; + y += 64; + } + } + p.end(); + printProgress(100); + printf("\n"); + + // Save output + QFileInfo dfi(destinationDir); + if (!dfi.isDir()) { + qWarning("Error: '%s' is not a directory.", destinationDir.toLatin1().constData()); + qWarning(" "); + exit(1); + } + + QString filename = font.family(); + filename.remove(QLatin1String(" ")); + QString italic = font.italic() ? QLatin1String("i") : QLatin1String(""); + QString bold = font.weight() > QFont::Normal ? QLatin1String("b") : QLatin1String(""); + filename = filename + bold + italic; + QString out = QString(QLatin1String("%1/%2.png")).arg(destinationDir).arg(filename); + output.save(out); + qWarning(" Distance-field saved to '%s'\n", out.toLatin1().constData()); +} + +class MyWidget : public QGLWidget +{ + Q_OBJECT +public: + MyWidget() + : QGLWidget() + { } + + ~MyWidget() { } + + void showEvent(QShowEvent *e) + { + QStringList args = QApplication::arguments(); + + bool noMultithread = args.contains(QLatin1String("--no-multithread")); + bool forceAllStyles = args.contains(QLatin1String("--force-all-styles")); + + QString fontFile; + QString destDir; + for (int i = 0; i < args.count(); ++i) { + QString a = args.at(i); + if (!a.startsWith('-') && QFileInfo(a).exists()) + fontFile = a; + if (a == QLatin1String("-d")) + destDir = args.at(++i); + } + if (destDir.isEmpty()) { + destDir = QFileInfo(fontFile).canonicalPath(); + } + + QStringList customStyles; + if (args.contains(QLatin1String("-styles"))) { + int index = args.indexOf(QLatin1String("-styles")); + QString styles = args.at(index + 1); + customStyles = styles.split(QLatin1String(",")); + } + + // Load the font + int fontID = QFontDatabase::addApplicationFont(fontFile); + if (fontID == -1) { + qWarning("Error: Invalid font file."); + qWarning(" "); + exit(1); + } + + QStringList allStyles = QStringList() << QLatin1String("Normal") + << QLatin1String("Bold") + << QLatin1String("Italic") + << QLatin1String("Bold Italic"); + + // Generate distance-fields for all families and all styles provided by the font file + QFontDatabase fontDatabase; + QStringList families = QFontDatabase::applicationFontFamilies(fontID); + int famCount = families.count(); + for (int i = 0; i < famCount; ++i) { + QStringList styles; + if (forceAllStyles) + styles = allStyles; + else if (customStyles.count() > 0) + styles = customStyles; + else + styles = fontDatabase.styles(families.at(i)); + + int styleCount = styles.count(); + for (int j = 0; j < styleCount; ++j) { + QFont font; + if (forceAllStyles || customStyles.count() > 0) { + int weight = styles.at(j).contains(QLatin1String("Bold")) ? QFont::Bold : QFont::Normal; + font = QFont(families.at(i), 10, weight, styles.at(j).contains(QLatin1String("Italic"))); + } else { + font = fontDatabase.font(families.at(i), styles.at(j), 10); // point size is ignored + } + generateDistanceFieldForFont(font, destDir, !noMultithread); + } + } + + exit(0); + } +}; + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + QStringList args = QApplication::arguments(); + + if (argc < 2 + || args.contains(QLatin1String("--help")) + || args.contains(QLatin1String("-help")) + || args.contains(QLatin1String("--h")) + || args.contains(QLatin1String("-h"))) + usage(); + + + MyWidget w; + w.show(); + + return app.exec(); +} + +#include "main.moc" diff --git a/tools/qmlplugindump/Info.plist b/tools/qmlplugindump/Info.plist new file mode 100644 index 0000000000..f35846d048 --- /dev/null +++ b/tools/qmlplugindump/Info.plist @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundlePackageType</key> + <string>APPL</string> + <key>CFBundleSignature</key> + <string>@TYPEINFO@</string> + <key>CFBundleExecutable</key> + <string>@EXECUTABLE@</string> + <key>CFBundleIdentifier</key> + <string>com.nokia.qt.qmlplugindump</string> + <key>LSUIElement</key> + <string>1</string> +</dict> +</plist> diff --git a/tools/qmlplugindump/main.cpp b/tools/qmlplugindump/main.cpp new file mode 100644 index 0000000000..848b0917cb --- /dev/null +++ b/tools/qmlplugindump/main.cpp @@ -0,0 +1,597 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications 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 <QtDeclarative/QtDeclarative> +#include <QtDeclarative/private/qdeclarativemetatype_p.h> +#include <QtDeclarative/private/qdeclarativeopenmetaobject_p.h> +#include <QtDeclarative/QDeclarativeView> + +#include <QtGui/QApplication> + +#include <QtCore/QSet> +#include <QtCore/QMetaObject> +#include <QtCore/QMetaProperty> +#include <QtCore/QDebug> +#include <QtCore/private/qobject_p.h> +#include <QtCore/private/qmetaobject_p.h> + +#include <iostream> + +#include "qmlstreamwriter.h" + +#ifdef QT_SIMULATOR +#include <QtGui/private/qsimulatorconnection_p.h> +#endif + +#ifdef Q_OS_UNIX +#include <signal.h> +#endif + +void collectReachableMetaObjects(const QMetaObject *meta, QSet<const QMetaObject *> *metas) +{ + if (! meta || metas->contains(meta)) + return; + + // dynamic meta objects break things badly, so just ignore them + const QMetaObjectPrivate *mop = reinterpret_cast<const QMetaObjectPrivate *>(meta->d.data); + if (!(mop->flags & DynamicMetaObject)) + metas->insert(meta); + + collectReachableMetaObjects(meta->superClass(), metas); +} + +QString currentProperty; + +void collectReachableMetaObjects(QObject *object, QSet<const QMetaObject *> *metas) +{ + if (! object) + return; + + const QMetaObject *meta = object->metaObject(); + qDebug() << "Processing object" << meta->className(); + collectReachableMetaObjects(meta, metas); + + for (int index = 0; index < meta->propertyCount(); ++index) { + QMetaProperty prop = meta->property(index); + if (QDeclarativeMetaType::isQObject(prop.userType())) { + qDebug() << " Processing property" << prop.name(); + currentProperty = QString("%1::%2").arg(meta->className(), prop.name()); + + // if the property was not initialized during construction, + // accessing a member of oo is going to cause a segmentation fault + QObject *oo = QDeclarativeMetaType::toQObject(prop.read(object)); + if (oo && !metas->contains(oo->metaObject())) + collectReachableMetaObjects(oo, metas); + currentProperty.clear(); + } + } +} + +void collectReachableMetaObjects(const QDeclarativeType *ty, QSet<const QMetaObject *> *metas) +{ + collectReachableMetaObjects(ty->metaObject(), metas); + if (ty->attachedPropertiesType()) + collectReachableMetaObjects(ty->attachedPropertiesType(), metas); +} + +/* We want to add the MetaObject for 'Qt' to the list, this is a + simple way to access it. +*/ +class FriendlyQObject: public QObject +{ +public: + static const QMetaObject *qtMeta() { return &staticQtMetaObject; } +}; + +/* When we dump a QMetaObject, we want to list all the types it is exported as. + To do this, we need to find the QDeclarativeTypes associated with this + QMetaObject. +*/ +static QHash<QByteArray, QSet<const QDeclarativeType *> > qmlTypesByCppName; + +static QHash<QByteArray, QByteArray> cppToId; + +/* Takes a C++ type name, such as Qt::LayoutDirection or QString and + maps it to how it should appear in the description file. + + These names need to be unique globally, so we don't change the C++ symbol's + name much. It is mostly used to for explicit translations such as + QString->string and translations for extended QML objects. +*/ +QByteArray convertToId(const QByteArray &cppName) +{ + return cppToId.value(cppName, cppName); +} + +QSet<const QMetaObject *> collectReachableMetaObjects(const QString &importCode, QDeclarativeEngine *engine) +{ + QSet<const QMetaObject *> metas; + metas.insert(FriendlyQObject::qtMeta()); + + QHash<QByteArray, QSet<QByteArray> > extensions; + foreach (const QDeclarativeType *ty, QDeclarativeMetaType::qmlTypes()) { + qmlTypesByCppName[ty->metaObject()->className()].insert(ty); + if (ty->isExtendedType()) { + extensions[ty->typeName()].insert(ty->metaObject()->className()); + } + collectReachableMetaObjects(ty, &metas); + } + + // Adjust ids of extended objects. + // The chain ends up being: + // __extended__.originalname - the base object + // __extension_0_.originalname - first extension + // .. + // __extension_n-2_.originalname - second to last extension + // originalname - last extension + // ### does this actually work for multiple extensions? it seems like the prototypes might be wrong + foreach (const QByteArray &extendedCpp, extensions.keys()) { + cppToId.remove(extendedCpp); + const QByteArray extendedId = convertToId(extendedCpp); + cppToId.insert(extendedCpp, "__extended__." + extendedId); + QSet<QByteArray> extensionCppNames = extensions.value(extendedCpp); + int c = 0; + foreach (const QByteArray &extensionCppName, extensionCppNames) { + if (c != extensionCppNames.size() - 1) { + QByteArray adjustedName = QString("__extension__%1.%2").arg(QString::number(c), QString(extendedId)).toAscii(); + cppToId.insert(extensionCppName, adjustedName); + } else { + cppToId.insert(extensionCppName, extendedId); + } + ++c; + } + } + + // find even more QMetaObjects by instantiating QML types and running + // over the instances + foreach (const QDeclarativeType *ty, QDeclarativeMetaType::qmlTypes()) { + if (ty->isExtendedType()) + continue; + + QByteArray tyName = ty->qmlTypeName(); + tyName = tyName.mid(tyName.lastIndexOf('/') + 1); + + QByteArray code = importCode.toUtf8(); + code += tyName; + code += " {}\n"; + + QDeclarativeComponent c(engine); + c.setData(code, QUrl("typeinstance")); + + QObject *object = c.create(); + if (object) + collectReachableMetaObjects(object, &metas); + else + qDebug() << "Could not create" << tyName << ":" << c.errorString(); + } + + return metas; +} + + +class Dumper +{ + QmlStreamWriter *qml; + QString relocatableModuleUri; + +public: + Dumper(QmlStreamWriter *qml) : qml(qml) {} + + void setRelocatableModuleUri(const QString &uri) + { + relocatableModuleUri = uri; + } + + void dump(const QMetaObject *meta) + { + qml->writeStartObject("Component"); + + QByteArray id = convertToId(meta->className()); + qml->writeScriptBinding(QLatin1String("name"), enquote(id)); + + for (int index = meta->classInfoCount() - 1 ; index >= 0 ; --index) { + QMetaClassInfo classInfo = meta->classInfo(index); + if (QLatin1String(classInfo.name()) == QLatin1String("DefaultProperty")) { + qml->writeScriptBinding(QLatin1String("defaultProperty"), enquote(QLatin1String(classInfo.value()))); + break; + } + } + + if (meta->superClass()) + qml->writeScriptBinding(QLatin1String("prototype"), enquote(convertToId(meta->superClass()->className()))); + + QSet<const QDeclarativeType *> qmlTypes = qmlTypesByCppName.value(meta->className()); + if (!qmlTypes.isEmpty()) { + QStringList exports; + + foreach (const QDeclarativeType *qmlTy, qmlTypes) { + QString qmlTyName = qmlTy->qmlTypeName(); + // some qmltype names are missing the actual names, ignore that import + if (qmlTyName.endsWith('/')) + continue; + if (qmlTyName.startsWith(relocatableModuleUri + QLatin1Char('/'))) { + qmlTyName.remove(0, relocatableModuleUri.size() + 1); + } + exports += enquote(QString("%1 %2.%3").arg( + qmlTyName, + QString::number(qmlTy->majorVersion()), + QString::number(qmlTy->minorVersion()))); + } + + // ensure exports are sorted and don't change order when the plugin is dumped again + exports.removeDuplicates(); + qSort(exports); + + qml->writeArrayBinding(QLatin1String("exports"), exports); + + if (const QMetaObject *attachedType = (*qmlTypes.begin())->attachedPropertiesType()) { + qml->writeScriptBinding(QLatin1String("attachedType"), enquote( + convertToId(attachedType->className()))); + } + } + + for (int index = meta->enumeratorOffset(); index < meta->enumeratorCount(); ++index) + dump(meta->enumerator(index)); + + for (int index = meta->propertyOffset(); index < meta->propertyCount(); ++index) + dump(meta->property(index)); + + for (int index = meta->methodOffset(); index < meta->methodCount(); ++index) + dump(meta->method(index)); + + qml->writeEndObject(); + } + + void writeEasingCurve() + { + qml->writeStartObject("Component"); + qml->writeScriptBinding(QLatin1String("name"), enquote(QLatin1String("QEasingCurve"))); + qml->writeScriptBinding(QLatin1String("prototype"), enquote(QLatin1String("QDeclarativeEasingValueType"))); + qml->writeEndObject(); + } + +private: + static QString enquote(const QString &string) + { + return QString("\"%1\"").arg(string); + } + + /* Removes pointer and list annotations from a type name, returning + what was removed in isList and isPointer + */ + static void removePointerAndList(QByteArray *typeName, bool *isList, bool *isPointer) + { + static QByteArray declListPrefix = "QDeclarativeListProperty<"; + + if (typeName->endsWith('*')) { + *isPointer = true; + typeName->truncate(typeName->length() - 1); + removePointerAndList(typeName, isList, isPointer); + } else if (typeName->startsWith(declListPrefix)) { + *isList = true; + typeName->truncate(typeName->length() - 1); // get rid of the suffix '>' + *typeName = typeName->mid(declListPrefix.size()); + removePointerAndList(typeName, isList, isPointer); + } + + *typeName = convertToId(*typeName); + } + + void writeTypeProperties(QByteArray typeName, bool isWritable) + { + bool isList = false, isPointer = false; + removePointerAndList(&typeName, &isList, &isPointer); + + qml->writeScriptBinding(QLatin1String("type"), enquote(typeName)); + if (isList) + qml->writeScriptBinding(QLatin1String("isList"), QLatin1String("true")); + if (!isWritable) + qml->writeScriptBinding(QLatin1String("isReadonly"), QLatin1String("true")); + if (isPointer) + qml->writeScriptBinding(QLatin1String("isPointer"), QLatin1String("true")); + } + + void dump(const QMetaProperty &prop) + { + qml->writeStartObject("Property"); + + qml->writeScriptBinding(QLatin1String("name"), enquote(QString::fromUtf8(prop.name()))); + writeTypeProperties(prop.typeName(), prop.isWritable()); + + qml->writeEndObject(); + } + + void dump(const QMetaMethod &meth) + { + if (meth.methodType() == QMetaMethod::Signal) { + if (meth.access() != QMetaMethod::Protected) + return; // nothing to do. + } else if (meth.access() != QMetaMethod::Public) { + return; // nothing to do. + } + + QByteArray name = meth.signature(); + int lparenIndex = name.indexOf('('); + if (lparenIndex == -1) { + return; // invalid signature + } + name = name.left(lparenIndex); + + if (meth.methodType() == QMetaMethod::Signal) + qml->writeStartObject(QLatin1String("Signal")); + else + qml->writeStartObject(QLatin1String("Method")); + + qml->writeScriptBinding(QLatin1String("name"), enquote(name)); + + const QString typeName = convertToId(meth.typeName()); + if (! typeName.isEmpty()) + qml->writeScriptBinding(QLatin1String("type"), enquote(typeName)); + + for (int i = 0; i < meth.parameterTypes().size(); ++i) { + QByteArray argName = meth.parameterNames().at(i); + + qml->writeStartObject(QLatin1String("Parameter")); + if (! argName.isEmpty()) + qml->writeScriptBinding(QLatin1String("name"), enquote(argName)); + writeTypeProperties(meth.parameterTypes().at(i), true); + qml->writeEndObject(); + } + + qml->writeEndObject(); + } + + void dump(const QMetaEnum &e) + { + qml->writeStartObject(QLatin1String("Enum")); + qml->writeScriptBinding(QLatin1String("name"), enquote(QString::fromUtf8(e.name()))); + + QList<QPair<QString, QString> > namesValues; + for (int index = 0; index < e.keyCount(); ++index) { + namesValues.append(qMakePair(enquote(QString::fromUtf8(e.key(index))), QString::number(e.value(index)))); + } + + qml->writeScriptObjectLiteralBinding(QLatin1String("values"), namesValues); + qml->writeEndObject(); + } +}; + + +enum ExitCode { + EXIT_INVALIDARGUMENTS = 1, + EXIT_SEGV = 2, + EXIT_IMPORTERROR = 3 +}; + +#ifdef Q_OS_UNIX +void sigSegvHandler(int) { + fprintf(stderr, "Error: SEGV\n"); + if (!currentProperty.isEmpty()) + fprintf(stderr, "While processing the property '%s', which probably has uninitialized data.\n", currentProperty.toLatin1().constData()); + exit(EXIT_SEGV); +} +#endif + +void printUsage(const QString &appName) +{ + qWarning() << qPrintable(QString( + "Usage: %1 [-notrelocatable] module.uri version [module/import/path]\n" + " %1 -path path/to/qmldir/directory [version]\n" + " %1 -builtins\n" + "Example: %1 Qt.labs.particles 4.7 /home/user/dev/qt-install/imports").arg( + appName)); +} + +int main(int argc, char *argv[]) +{ +#ifdef Q_OS_UNIX + // qmldump may crash, but we don't want any crash handlers to pop up + // therefore we intercept the segfault and just exit() ourselves + struct sigaction action; + + sigemptyset(&action.sa_mask); + action.sa_handler = &sigSegvHandler; + action.sa_flags = 0; + + sigaction(SIGSEGV, &action, 0); +#endif + +#ifdef QT_SIMULATOR + // Running this application would bring up the Qt Simulator (since it links QtGui), avoid that! + QtSimulatorPrivate::SimulatorConnection::createStubInstance(); +#endif + QApplication app(argc, argv); + const QStringList args = app.arguments(); + const QString appName = QFileInfo(app.applicationFilePath()).baseName(); + if (!(args.size() >= 3 + || (args.size() == 2 + && (args.at(1) == QLatin1String("--builtins") + || args.at(1) == QLatin1String("-builtins"))))) { + printUsage(appName); + return EXIT_INVALIDARGUMENTS; + } + + QString pluginImportUri; + QString pluginImportVersion; + QString pluginImportPath; + bool relocatable = true; + bool pathImport = false; + if (args.size() >= 3) { + QStringList positionalArgs; + foreach (const QString &arg, args) { + if (!arg.startsWith(QLatin1Char('-'))) { + positionalArgs.append(arg); + continue; + } + + if (arg == QLatin1String("--notrelocatable") + || arg == QLatin1String("-notrelocatable")) { + relocatable = false; + } else if (arg == QLatin1String("--path") + || arg == QLatin1String("-path")) { + pathImport = true; + } else { + qWarning() << "Invalid argument: " << arg; + return EXIT_INVALIDARGUMENTS; + } + } + + if (!pathImport) { + if (positionalArgs.size() != 3 && positionalArgs.size() != 4) { + qWarning() << "Incorrect number of positional arguments"; + return EXIT_INVALIDARGUMENTS; + } + pluginImportUri = positionalArgs[1]; + pluginImportVersion = positionalArgs[2]; + if (positionalArgs.size() >= 4) + pluginImportPath = positionalArgs[3]; + } else { + if (positionalArgs.size() != 2 && positionalArgs.size() != 3) { + qWarning() << "Incorrect number of positional arguments"; + return EXIT_INVALIDARGUMENTS; + } + pluginImportPath = positionalArgs[1]; + if (positionalArgs.size() == 3) + pluginImportVersion = positionalArgs[2]; + } + } + + QDeclarativeView view; + QDeclarativeEngine *engine = view.engine(); + if (!pluginImportPath.isEmpty()) + engine->addImportPath(pluginImportPath); + + // find all QMetaObjects reachable from the builtin module + QByteArray importCode("import QtQuick 1.0\n"); + QSet<const QMetaObject *> defaultReachable = collectReachableMetaObjects(importCode, engine); + + // this will hold the meta objects we want to dump information of + QSet<const QMetaObject *> metas; + + if (pluginImportUri.isEmpty() && !pathImport) { + metas = defaultReachable; + } else { + // find all QMetaObjects reachable when the specified module is imported + if (!pathImport) { + importCode += QString("import %0 %1\n").arg(pluginImportUri, pluginImportVersion).toAscii(); + } else { + // pluginImportVersion can be empty + importCode += QString("import \"%1\" %2\n").arg(pluginImportPath, pluginImportVersion).toAscii(); + } + + // create a component with these imports to make sure the imports are valid + // and to populate the declarative meta type system + { + QByteArray code = importCode; + code += "QtObject {}"; + QDeclarativeComponent c(engine); + + c.setData(code, QUrl("typelist")); + c.create(); + if (!c.errors().isEmpty()) { + foreach (const QDeclarativeError &error, c.errors()) + qWarning() << error.toString(); + return EXIT_IMPORTERROR; + } + } + + QSet<const QMetaObject *> candidates = collectReachableMetaObjects(importCode, engine); + candidates.subtract(defaultReachable); + + // Also eliminate meta objects with the same classname. + // This is required because extended objects seem not to share + // a single meta object instance. + QSet<QByteArray> defaultReachableNames; + foreach (const QMetaObject *mo, defaultReachable) + defaultReachableNames.insert(QByteArray(mo->className())); + foreach (const QMetaObject *mo, candidates) { + if (!defaultReachableNames.contains(mo->className())) + metas.insert(mo); + } + } + + // setup static rewrites of type names + cppToId.insert("QString", "string"); + cppToId.insert("QDeclarativeEasingValueType::Type", "Type"); + + // start dumping data + QByteArray bytes; + QmlStreamWriter qml(&bytes); + + qml.writeStartDocument(); + qml.writeLibraryImport(QLatin1String("QtQuick.tooling"), 1, 0); + qml.write("\n" + "// This file describes the plugin-supplied types contained in the library.\n" + "// It is used for QML tooling purposes only.\n" + "\n"); + qml.writeStartObject("Module"); + + // put the metaobjects into a map so they are always dumped in the same order + QMap<QString, const QMetaObject *> nameToMeta; + foreach (const QMetaObject *meta, metas) + nameToMeta.insert(convertToId(meta->className()), meta); + + Dumper dumper(&qml); + if (relocatable) + dumper.setRelocatableModuleUri(pluginImportUri); + foreach (const QMetaObject *meta, nameToMeta) { + dumper.dump(meta); + } + + // define QEasingCurve as an extension of QDeclarativeEasingValueType, this way + // properties using the QEasingCurve type get useful type information. + if (pluginImportUri.isEmpty()) + dumper.writeEasingCurve(); + + qml.writeEndObject(); + qml.writeEndDocument(); + + std::cout << bytes.constData(); + + // workaround to avoid crashes on exit + QTimer timer; + timer.setSingleShot(true); + timer.setInterval(0); + QObject::connect(&timer, SIGNAL(timeout()), &app, SLOT(quit())); + timer.start(); + + return app.exec(); +} diff --git a/tools/qmlplugindump/qmlplugindump.pro b/tools/qmlplugindump/qmlplugindump.pro new file mode 100644 index 0000000000..53827e2f40 --- /dev/null +++ b/tools/qmlplugindump/qmlplugindump.pro @@ -0,0 +1,20 @@ +TEMPLATE = app +CONFIG += qt uic console +DESTDIR = ../../bin + +QT += declarative + +TARGET = qmlplugindump + +SOURCES += \ + main.cpp \ + qmlstreamwriter.cpp + +HEADERS += \ + qmlstreamwriter.h + +OTHER_FILES += Info.plist +macx: QMAKE_INFO_PLIST = Info.plist + +target.path = $$[QT_INSTALL_BINS] +INSTALLS += target diff --git a/tools/qmlplugindump/qmlstreamwriter.cpp b/tools/qmlplugindump/qmlstreamwriter.cpp new file mode 100644 index 0000000000..d083f7b64c --- /dev/null +++ b/tools/qmlplugindump/qmlstreamwriter.cpp @@ -0,0 +1,183 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications 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 "qmlstreamwriter.h" + +#include <QtCore/QBuffer> +#include <QtCore/QStringList> + +QmlStreamWriter::QmlStreamWriter(QByteArray *array) + : m_indentDepth(0) + , m_pendingLineLength(0) + , m_maybeOneline(false) + , m_stream(new QBuffer(array)) +{ + m_stream->open(QIODevice::WriteOnly); +} + +void QmlStreamWriter::writeStartDocument() +{ +} + +void QmlStreamWriter::writeEndDocument() +{ +} + +void QmlStreamWriter::writeLibraryImport(const QString &uri, int majorVersion, int minorVersion, const QString &as) +{ + m_stream->write(QString("import %1 %2.%3").arg(uri, QString::number(majorVersion), QString::number(minorVersion)).toUtf8()); + if (!as.isEmpty()) + m_stream->write(QString(" as %1").arg(as).toUtf8()); + m_stream->write("\n"); +} + +void QmlStreamWriter::writeStartObject(const QString &component) +{ + flushPotentialLinesWithNewlines(); + writeIndent(); + m_stream->write(QString("%1 {").arg(component).toUtf8()); + ++m_indentDepth; + m_maybeOneline = true; +} + +void QmlStreamWriter::writeEndObject() +{ + if (m_maybeOneline && !m_pendingLines.isEmpty()) { + --m_indentDepth; + for (int i = 0; i < m_pendingLines.size(); ++i) { + m_stream->write(" "); + m_stream->write(m_pendingLines.at(i).trimmed()); + if (i != m_pendingLines.size() - 1) + m_stream->write(";"); + } + m_stream->write(" }\n"); + m_pendingLines.clear(); + m_pendingLineLength = 0; + m_maybeOneline = false; + } else { + if (m_maybeOneline) + flushPotentialLinesWithNewlines(); + --m_indentDepth; + writeIndent(); + m_stream->write("}\n"); + } +} + +void QmlStreamWriter::writeScriptBinding(const QString &name, const QString &rhs) +{ + writePotentialLine(QString("%1: %2").arg(name, rhs).toUtf8()); +} + +void QmlStreamWriter::writeArrayBinding(const QString &name, const QStringList &elements) +{ + flushPotentialLinesWithNewlines(); + writeIndent(); + m_stream->write(QString("%1: [\n").arg(name).toUtf8()); + ++m_indentDepth; + for (int i = 0; i < elements.size(); ++i) { + writeIndent(); + m_stream->write(elements.at(i).toUtf8()); + if (i != elements.size() - 1) { + m_stream->write(",\n"); + } else { + m_stream->write("\n"); + } + } + --m_indentDepth; + writeIndent(); + m_stream->write("]\n"); +} + +void QmlStreamWriter::write(const QString &data) +{ + flushPotentialLinesWithNewlines(); + m_stream->write(data.toUtf8()); +} + +void QmlStreamWriter::writeScriptObjectLiteralBinding(const QString &name, const QList<QPair<QString, QString> > &keyValue) +{ + flushPotentialLinesWithNewlines(); + writeIndent(); + m_stream->write(QString("%1: {\n").arg(name).toUtf8()); + ++m_indentDepth; + for (int i = 0; i < keyValue.size(); ++i) { + const QString key = keyValue.at(i).first; + const QString value = keyValue.at(i).second; + writeIndent(); + m_stream->write(QString("%1: %2").arg(key, value).toUtf8()); + if (i != keyValue.size() - 1) { + m_stream->write(",\n"); + } else { + m_stream->write("\n"); + } + } + --m_indentDepth; + writeIndent(); + m_stream->write("}\n"); +} + +void QmlStreamWriter::writeIndent() +{ + m_stream->write(QByteArray(m_indentDepth * 4, ' ')); +} + +void QmlStreamWriter::writePotentialLine(const QByteArray &line) +{ + m_pendingLines.append(line); + m_pendingLineLength += line.size(); + if (m_pendingLineLength >= 80) { + flushPotentialLinesWithNewlines(); + } +} + +void QmlStreamWriter::flushPotentialLinesWithNewlines() +{ + if (m_maybeOneline) + m_stream->write("\n"); + foreach (const QByteArray &line, m_pendingLines) { + writeIndent(); + m_stream->write(line); + m_stream->write("\n"); + } + m_pendingLines.clear(); + m_pendingLineLength = 0; + m_maybeOneline = false; +} diff --git a/tools/qmlplugindump/qmlstreamwriter.h b/tools/qmlplugindump/qmlstreamwriter.h new file mode 100644 index 0000000000..cd73aad8f2 --- /dev/null +++ b/tools/qmlplugindump/qmlstreamwriter.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications 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 QMLSTREAMWRITER_H +#define QMLSTREAMWRITER_H + +#include <QtCore/QIODevice> +#include <QtCore/QList> +#include <QtCore/QString> +#include <QtCore/QScopedPointer> +#include <QtCore/QPair> + +class QmlStreamWriter +{ +public: + QmlStreamWriter(QByteArray *array); + + void writeStartDocument(); + void writeEndDocument(); + void writeLibraryImport(const QString &uri, int majorVersion, int minorVersion, const QString &as = QString()); + //void writeFilesystemImport(const QString &file, const QString &as = QString()); + void writeStartObject(const QString &component); + void writeEndObject(); + void writeScriptBinding(const QString &name, const QString &rhs); + void writeScriptObjectLiteralBinding(const QString &name, const QList<QPair<QString, QString> > &keyValue); + void writeArrayBinding(const QString &name, const QStringList &elements); + void write(const QString &data); + +private: + void writeIndent(); + void writePotentialLine(const QByteArray &line); + void flushPotentialLinesWithNewlines(); + + int m_indentDepth; + QList<QByteArray> m_pendingLines; + int m_pendingLineLength; + bool m_maybeOneline; + QScopedPointer<QIODevice> m_stream; +}; + +#endif // QMLSTREAMWRITER_H diff --git a/tools/qmlscene/main.cpp b/tools/qmlscene/main.cpp new file mode 100644 index 0000000000..765a9dc2fb --- /dev/null +++ b/tools/qmlscene/main.cpp @@ -0,0 +1,574 @@ +/**************************************************************************** +** +** 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 tools applications 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 <QtCore/qdebug.h> +#include <QtCore/qabstractanimation.h> +#include <QtGui/qapplication.h> +#include <QtDeclarative/qdeclarative.h> +#include <QtDeclarative/qdeclarativeengine.h> +#include <QtDeclarative/qdeclarativecomponent.h> +#include <QtDeclarative/qdeclarativeview.h> +#include <QtCore/qdir.h> +#include <QtGui/QFormLayout> +#include <QtGui/QComboBox> +#include <QtGui/QCheckBox> +#include <QtGui/QDialog> +#include <QtGui/QDialogButtonBox> +#include <QtGui/QFileDialog> +#include <QtGui/QGraphicsView> + +#include <QtDeclarative/qdeclarativeitem.h> +#include <QtDeclarative/qdeclarativecontext.h> +#include <private/qdeclarativedebughelper_p.h> + +// ### This should be private API +#include <qsgitem.h> +#include <qsgview.h> + +#define QT_NO_SCENEGRAPHITEM + +#ifndef QT_NO_SCENEGRAPHITEM +#include "scenegraphitem.h" +#endif + +#include <QtCore/qmath.h> + +#ifdef QML_RUNTIME_TESTING +class RenderStatistics +{ +public: + static void updateStats(); + static void printTotalStats(); +private: + static QVector<qreal> timePerFrame; + static QVector<int> timesPerFrames; +}; + +QVector<qreal> RenderStatistics::timePerFrame; +QVector<int> RenderStatistics::timesPerFrames; + +void RenderStatistics::updateStats() +{ + static QTime time; + static int frames; + static int lastTime; + + if (frames == 0) { + time.start(); + } else { + int elapsed = time.elapsed(); + timesPerFrames.append(elapsed - lastTime); + lastTime = elapsed; + + if (elapsed > 5000) { + qreal avgtime = elapsed / (qreal) frames; + qreal var = 0; + for (int i = 0; i < timesPerFrames.size(); ++i) { + qreal diff = timesPerFrames.at(i) - avgtime; + var += diff * diff; + } + var /= timesPerFrames.size(); + + qDebug("Average time per frame: %f ms (%i fps), std.dev: %f ms", avgtime, qRound(1000. / avgtime), qSqrt(var)); + + timePerFrame.append(avgtime); + timesPerFrames.clear(); + time.start(); + lastTime = 0; + frames = 0; + } + } + ++frames; +} + +void RenderStatistics::printTotalStats() +{ + int count = timePerFrame.count(); + if (count == 0) + return; + + qreal minTime = 0; + qreal maxTime = 0; + qreal avg = 0; + for (int i = 0; i < count; ++i) { + minTime = minTime == 0 ? timePerFrame.at(i) : qMin(minTime, timePerFrame.at(i)); + maxTime = qMax(maxTime, timePerFrame.at(i)); + avg += timePerFrame.at(i); + } + avg /= count; + + qDebug(" "); + qDebug("----- Statistics -----"); + qDebug("Average time per frame: %f ms (%i fps)", avg, qRound(1000. / avg)); + qDebug("Best time per frame: %f ms (%i fps)", minTime, int(1000 / minTime)); + qDebug("Worst time per frame: %f ms (%i fps)", maxTime, int(1000 / maxTime)); + qDebug("----------------------"); + qDebug(" "); +} +#endif + + +static QGLFormat getFormat() +{ + QGLFormat f = QGLFormat::defaultFormat(); + f.setSampleBuffers(!qApp->arguments().contains("--no-multisample")); + f.setSwapInterval(qApp->arguments().contains("--nonblocking-swap") ? 0 : 1); + f.setStereo(qApp->arguments().contains("--stereo")); + return f; +} + +class MyQSGView : public QSGView +{ +public: + MyQSGView() : QSGView(getFormat()) + { + setResizeMode(QSGView::SizeRootObjectToView); + } + +protected: + void paintEvent(QPaintEvent *e) { + QSGView::paintEvent(e); + +#ifdef QML_RUNTIME_TESTING +// RenderStatistics::updateStats(); +#endif + + static bool continuousUpdate = qApp->arguments().contains("--continuous-update"); + if (continuousUpdate) + update(); + } +}; + +class MyDeclarativeView: public QDeclarativeView +{ +public: + MyDeclarativeView(QWidget *parent = 0) : QDeclarativeView(parent) + { + setResizeMode(QDeclarativeView::SizeRootObjectToView); + } + +protected: + void paintEvent(QPaintEvent *event) + { + QDeclarativeView::paintEvent(event); + +#ifdef QML_RUNTIME_TESTING + RenderStatistics::updateStats(); +#endif + + static bool continuousUpdate = qApp->arguments().contains("--continuous-update"); + if (continuousUpdate) + scene()->update(); + } +}; + +#ifndef QT_NO_SCENEGRAPHITEM +class MyGraphicsView: public QGraphicsView +{ +public: + MyGraphicsView(bool clip, QWidget *parent = 0) : QGraphicsView(parent) + { + setViewport(new QGLWidget(getFormat())); + setScene(&scene); + scene.addItem(&item); + item.setFlag(QGraphicsItem::ItemClipsToShape, clip); + QGraphicsTextItem *text; + text = scene.addText(QLatin1String("Scene graph on graphics view."), QFont(QLatin1String("Times"), 10)); + text->setX(5); + text->setY(5); + text->setDefaultTextColor(Qt::black); + text = scene.addText(QLatin1String("Scene graph on graphics view."), QFont(QLatin1String("Times"), 10)); + text->setX(4); + text->setY(4); + text->setDefaultTextColor(Qt::yellow); + } + + SceneGraphItem *sceneGraphItem() { return &item; } + +protected: + void paintEvent(QPaintEvent *event) + { + QGraphicsView::paintEvent(event); + +#ifdef QML_RUNTIME_TESTING + RenderStatistics::updateStats(); +#endif + + static bool continuousUpdate = qApp->arguments().contains("--continuous-update"); + if (continuousUpdate) + QGraphicsView::scene()->update(); + } + + QGraphicsScene scene; + SceneGraphItem item; +}; +#endif + +struct Options +{ + Options() + : originalQml(false) + , originalQmlRaster(false) + , maximized(false) + , fullscreen(false) + , scenegraphOnGraphicsview(false) + , clip(false) + , versionDetection(true) + { + } + + QUrl file; + bool originalQml; + bool originalQmlRaster; + bool maximized; + bool fullscreen; + bool scenegraphOnGraphicsview; + bool clip; + bool versionDetection; +}; + +#if defined(QMLSCENE_BUNDLE) +Q_DECLARE_METATYPE(QFileInfo); +QFileInfoList findQmlFiles(const QString &dirName) +{ + QDir dir(dirName); + + QFileInfoList ret; + if (dir.exists()) { + QFileInfoList fileInfos = dir.entryInfoList(QStringList() << "*.qml", + QDir::Files | QDir::AllDirs | QDir::NoDotAndDotDot); + + foreach (QFileInfo fileInfo, fileInfos) { + if (fileInfo.isDir()) + ret += findQmlFiles(fileInfo.filePath()); + else if (fileInfo.fileName().length() > 0 && fileInfo.fileName().at(0).isLower()) + ret.append(fileInfo); + } + } + + return ret; +} + +static int displayOptionsDialog(Options *options) +{ + QDialog dialog; + + QFormLayout *layout = new QFormLayout(&dialog); + + QComboBox *qmlFileComboBox = new QComboBox(&dialog); + QFileInfoList fileInfos = findQmlFiles(":/bundle") + findQmlFiles("./qmlscene-resources"); + + foreach (QFileInfo fileInfo, fileInfos) + qmlFileComboBox->addItem(fileInfo.dir().dirName() + "/" + fileInfo.fileName(), QVariant::fromValue(fileInfo)); + + QCheckBox *originalCheckBox = new QCheckBox(&dialog); + originalCheckBox->setText("Use original QML viewer"); + originalCheckBox->setChecked(options->originalQml); + + QCheckBox *fullscreenCheckBox = new QCheckBox(&dialog); + fullscreenCheckBox->setText("Start fullscreen"); + fullscreenCheckBox->setChecked(options->fullscreen); + + QCheckBox *maximizedCheckBox = new QCheckBox(&dialog); + maximizedCheckBox->setText("Start maximized"); + maximizedCheckBox->setChecked(options->maximized); + + QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, + Qt::Horizontal, + &dialog); + QObject::connect(buttonBox, SIGNAL(accepted()), &dialog, SLOT(accept())); + QObject::connect(buttonBox, SIGNAL(rejected()), &dialog, SLOT(reject())); + + layout->addRow("Qml file:", qmlFileComboBox); + layout->addWidget(originalCheckBox); + layout->addWidget(maximizedCheckBox); + layout->addWidget(fullscreenCheckBox); + layout->addWidget(buttonBox); + + int result = dialog.exec(); + if (result == QDialog::Accepted) { + QVariant variant = qmlFileComboBox->itemData(qmlFileComboBox->currentIndex()); + QFileInfo fileInfo = variant.value<QFileInfo>(); + + if (fileInfo.canonicalFilePath().startsWith(":")) + options->file = QUrl("qrc" + fileInfo.canonicalFilePath()); + else + options->file = QUrl::fromLocalFile(fileInfo.canonicalFilePath()); + options->originalQml = originalCheckBox->isChecked(); + options->maximized = maximizedCheckBox->isChecked(); + options->fullscreen = fullscreenCheckBox->isChecked(); + } + return result; +} +#endif + +static void checkAndAdaptVersion(const QUrl &url) +{ + if (!qgetenv("QMLSCENE_IMPORT_NAME").isEmpty()) { + return; + } + + QString fileName = url.toLocalFile(); + if (fileName.isEmpty()) + return; + + QFile f(fileName); + if (!f.open(QFile::ReadOnly | QFile::Text)) { + qWarning("qmlscene: failed to check version of file '%s', could not open...", + qPrintable(fileName)); + return; + } + + QRegExp quick1("import +QtQuick +1\\."); + QRegExp qt47("import +Qt +4\\.7"); + + QString envToWrite; + QString compat; + + QTextStream stream(&f); + bool codeFound= false; + while (!codeFound && envToWrite.isEmpty()) { + QString line = stream.readLine(); + if (line.contains("{")) + codeFound = true; + if (quick1.indexIn(line) >= 0) { + envToWrite = QLatin1String("quick1"); + compat = QLatin1String("QtQuick 1.0"); + } else if (qt47.indexIn(line) >= 0) { + envToWrite = QLatin1String("qt"); + compat = QLatin1String("Qt 4.7"); + } + } + + if (!envToWrite.isEmpty()) { + qWarning("qmlscene: Autodetecting compatibility import \"%s\"...", qPrintable(compat)); + if (qgetenv("QMLSCENE_IMPORT_NAME").isEmpty()) + qputenv("QMLSCENE_IMPORT_NAME", envToWrite.toLatin1().constData()); + } +} + +static void displayFileDialog(Options *options) +{ + QString fileName = QFileDialog::getOpenFileName(0, "Open QML file", QString(), "QML Files (*.qml)"); + if (!fileName.isEmpty()) { + QFileInfo fi(fileName); + options->file = QUrl::fromLocalFile(fi.canonicalFilePath()); + } +} + +static void loadDummyDataFiles(QDeclarativeEngine &engine, const QString& directory) +{ + QDir dir(directory+"/dummydata", "*.qml"); + QStringList list = dir.entryList(); + for (int i = 0; i < list.size(); ++i) { + QString qml = list.at(i); + QFile f(dir.filePath(qml)); + f.open(QIODevice::ReadOnly); + QByteArray data = f.readAll(); + QDeclarativeComponent comp(&engine); + comp.setData(data, QUrl()); + QObject *dummyData = comp.create(); + + if(comp.isError()) { + QList<QDeclarativeError> errors = comp.errors(); + foreach (const QDeclarativeError &error, errors) { + qWarning() << error; + } + } + + if (dummyData) { + qWarning() << "Loaded dummy data:" << dir.filePath(qml); + qml.truncate(qml.length()-4); + engine.rootContext()->setContextProperty(qml, dummyData); + dummyData->setParent(&engine); + } + } +} + +static void usage() +{ + qWarning("Usage: qmlscene [options] <filename>"); + qWarning(" "); + qWarning(" options:"); + qWarning(" --maximized ............................... run maximized"); + qWarning(" --fullscreen .............................. run fullscreen"); + qWarning(" --original-qml ............................ run using QGraphicsView instead of scenegraph (OpenGL engine)"); + qWarning(" --original-qml-raster ..................... run using QGraphicsView instead of scenegraph (Raster engine)"); + qWarning(" --no-multisample .......................... Disable multisampling (anti-aliasing)"); + qWarning(" --continuous-update ....................... Continuously render the scene"); + qWarning(" --nonblocking-swap ........................ Do not wait for v-sync to swap buffers"); + qWarning(" --stereo .................................. Enable stereo on the GL context"); +#ifndef QT_NO_SCENEGRAPHITEM + qWarning(" --sg-on-gv [--clip] ....................... Scenegraph on graphicsview (and clip to item)"); +#endif + qWarning(" --no-version-detection .................... Do not try to detect the version of the .qml file"); + + qWarning(" "); + exit(1); +} + +int main(int argc, char ** argv) +{ +#ifdef Q_WS_X11 + QApplication::setAttribute(Qt::AA_X11InitThreads); +#endif + + Options options; + + QDeclarativeDebugHelper::enableDebugging(); + QStringList imports; + for (int i = 1; i < argc; ++i) { + if (*argv[i] != '-' && QFileInfo(argv[i]).exists()) + options.file = QUrl::fromLocalFile(argv[i]); + else if (QString::fromLatin1(argv[i]).toLower() == QLatin1String("--original-qml")) + options.originalQml = true; + else if (QString::fromLatin1(argv[i]).toLower() == QLatin1String("--original-qml-raster")) + options.originalQmlRaster = true; + else if (QString::fromLatin1(argv[i]).toLower() == QLatin1String("--maximized")) + options.maximized = true; + else if (QString::fromLatin1(argv[i]).toLower() == QLatin1String("--fullscreen")) + options.fullscreen = true; + else if (QString::fromLatin1(argv[i]).toLower() == QLatin1String("--sg-on-gv")) + options.scenegraphOnGraphicsview = true; + else if (QString::fromLatin1(argv[i]).toLower() == QLatin1String("--clip")) + options.clip = true; + else if (QString::fromLatin1(argv[i]).toLower() == QLatin1String("--no-version-detection")) + options.versionDetection = false; + else if (QString::fromLatin1(argv[i]).toLower() == QLatin1String("-i") && i + 1 < argc) + imports.append(QString::fromLatin1(argv[++i])); + else if (QString::fromLatin1(argv[i]).toLower() == QLatin1String("--help") + || QString::fromLatin1(argv[i]).toLower() == QLatin1String("-help") + || QString::fromLatin1(argv[i]).toLower() == QLatin1String("--h") + || QString::fromLatin1(argv[i]).toLower() == QLatin1String("-h")) + usage(); + } + + QApplication::setGraphicsSystem("raster"); + + QApplication app(argc, argv); + app.setApplicationName("QtQmlViewer"); + app.setOrganizationName("Nokia"); + app.setOrganizationDomain("nokia.com"); + + if (options.file.isEmpty()) +#if defined(QMLSCENE_BUNDLE) + displayOptionsDialog(&options); +#else + displayFileDialog(&options); +#endif + + QWidget *view = 0; + QDeclarativeEngine *engine = 0; + + int exitCode = 0; + + if (!options.file.isEmpty()) { +#ifndef QT_NO_SCENEGRAPHITEM + if (options.scenegraphOnGraphicsview) { + MyGraphicsView *gvView = new MyGraphicsView(options.clip); + SceneGraphItem *item = gvView->sceneGraphItem(); + engine = item->engine(); + for (int i = 0; i < imports.size(); ++i) + engine->addImportPath(imports.at(i)); + view = gvView; + if (options.file.isLocalFile()) { + QFileInfo fi(options.file.toLocalFile()); + loadDummyDataFiles(*engine, fi.path()); + } + item->setSource(options.file); + } else +#endif + if (!options.originalQml && !options.originalQmlRaster) { + if (options.versionDetection) + checkAndAdaptVersion(options.file); + QSGView *qxView = new MyQSGView(); + engine = qxView->engine(); + for (int i = 0; i < imports.size(); ++i) + engine->addImportPath(imports.at(i)); + view = qxView; + if (options.file.isLocalFile()) { + QFileInfo fi(options.file.toLocalFile()); + loadDummyDataFiles(*engine, fi.path()); + } + qxView->setSource(options.file); + + } else { + MyDeclarativeView *gvView = new MyDeclarativeView(); + engine = gvView->engine(); + for (int i = 0; i < imports.size(); ++i) + engine->addImportPath(imports.at(i)); + view = gvView; + if (options.file.isLocalFile()) { + QFileInfo fi(options.file.toLocalFile()); + loadDummyDataFiles(*engine, fi.path()); + } + gvView->setSource(options.file); + if (!options.originalQmlRaster) { + QGLWidget *viewport = new QGLWidget(getFormat()); + gvView->setViewport(viewport); + } + } + + QObject::connect(engine, SIGNAL(quit()), QCoreApplication::instance(), SLOT(quit())); + + if (options.fullscreen) + view->showFullScreen(); + else if (options.maximized) + view->showMaximized(); + else + view->show(); + +#ifdef Q_WS_MAC + view->raise(); +#endif + + exitCode = app.exec(); + + delete view; + +#ifdef QML_RUNTIME_TESTING + RenderStatistics::printTotalStats(); +#endif + } + + return exitCode; +} + diff --git a/tools/qmlscene/qmlscene.pro b/tools/qmlscene/qmlscene.pro new file mode 100644 index 0000000000..3849336fc8 --- /dev/null +++ b/tools/qmlscene/qmlscene.pro @@ -0,0 +1,20 @@ +TEMPLATE = app +TARGET = qmlscene +DESTDIR= ../../bin + +QT += declarative + +target.path = $$[QT_INSTALL_BINS] +INSTALLS += target + +macx: CONFIG -= app_bundle + +SOURCES += main.cpp + +CONFIG += console + +symbian { + TARGET.EPOCHEAPSIZE = 0x20000 0x5000000 +} + +DEFINES += QML_RUNTIME_TESTING diff --git a/tools/qmlviewer/main.cpp b/tools/qmlviewer/main.cpp index b2c7f4f730..b1a10ff6c4 100644 --- a/tools/qmlviewer/main.cpp +++ b/tools/qmlviewer/main.cpp @@ -156,7 +156,9 @@ void usage() qWarning(" -P <directory> ........................... prepend to the plugin search path"); #if defined(Q_WS_MAC) qWarning(" -no-opengl ............................... don't use a QGLWidget for the viewport"); + qWarning(" -opengl .................................. use a QGLWidget for the viewport (default)"); #else + qWarning(" -no-opengl ............................... don't use a QGLWidget for the viewport (default)"); qWarning(" -opengl .................................. use a QGLWidget for the viewport"); #endif qWarning(" -script <path> ........................... set the script to use"); @@ -375,13 +377,10 @@ static void parseCommandLineOptions(const QStringList &arguments) } else if (arg == "-translation") { if (lastArg) usage(); opts.translationFile = arguments.at(++i); -#if defined(Q_WS_MAC) } else if (arg == "-no-opengl") { opts.useGL = false; -#else } else if (arg == "-opengl") { opts.useGL = true; -#endif } else if (arg == "-qmlbrowser") { opts.useNativeFileBrowser = false; } else if (arg == "-warnings") { @@ -522,6 +521,8 @@ QDeclarativeViewer *openFile(const QString &fileName) int main(int argc, char ** argv) { + QDeclarativeDebugHelper::enableDebugging(); + systemMsgOutput = qInstallMsgHandler(myMessageOutput); #if defined (Q_WS_X11) || defined (Q_WS_MAC) diff --git a/tools/qmlviewer/qmlruntime.cpp b/tools/qmlviewer/qmlruntime.cpp index 36915d12bf..4bae7f3ec0 100644 --- a/tools/qmlviewer/qmlruntime.cpp +++ b/tools/qmlviewer/qmlruntime.cpp @@ -1477,6 +1477,7 @@ void QDeclarativeViewer::setUseGL(bool useGL) QGLFormat format = QGLFormat::defaultFormat(); #ifdef Q_WS_MAC format.setSampleBuffers(true); + format.setSwapInterval(1); #else format.setSampleBuffers(false); #endif diff --git a/tools/tools.pro b/tools/tools.pro index 2035460ce2..6d5b43a9e4 100644 --- a/tools/tools.pro +++ b/tools/tools.pro @@ -1,2 +1,2 @@ TEMPLATE = subdirs -SUBDIRS += qmlviewer +SUBDIRS += qmlviewer qmlscene qmlplugindump distfieldgen |