diff options
Diffstat (limited to 'tools')
54 files changed, 8093 insertions, 5 deletions
diff --git a/tools/linguist/lupdate/lupdate.h b/tools/linguist/lupdate/lupdate.h index 45eee98..ae04218 100644 --- a/tools/linguist/lupdate/lupdate.h +++ b/tools/linguist/lupdate/lupdate.h @@ -79,6 +79,7 @@ void loadCPP(Translator &translator, const QStringList &filenames, ConversionDat bool loadJava(Translator &translator, const QString &filename, ConversionData &cd); bool loadQScript(Translator &translator, const QString &filename, ConversionData &cd); bool loadUI(Translator &translator, const QString &filename, ConversionData &cd); +bool loadQml(Translator &translator, const QString &filename, ConversionData &cd); QT_END_NAMESPACE diff --git a/tools/linguist/lupdate/lupdate.pro b/tools/linguist/lupdate/lupdate.pro index ccc2d47..283d69f 100644 --- a/tools/linguist/lupdate/lupdate.pro +++ b/tools/linguist/lupdate/lupdate.pro @@ -15,6 +15,9 @@ build_all:!build_pass { include(../shared/formats.pri) include(../shared/proparser.pri) +include($$QT_SOURCE_TREE/src/declarative/qml/parser/parser.pri) +INCLUDEPATH += $$QT_SOURCE_TREE/src/declarative/qml + SOURCES += \ main.cpp \ merge.cpp \ @@ -23,6 +26,7 @@ SOURCES += \ cpp.cpp \ java.cpp \ qscript.cpp \ + qml.cpp \ ui.cpp HEADERS += \ diff --git a/tools/linguist/lupdate/main.cpp b/tools/linguist/lupdate/main.cpp index bdaec4f..af40616 100644 --- a/tools/linguist/lupdate/main.cpp +++ b/tools/linguist/lupdate/main.cpp @@ -427,7 +427,8 @@ int main(int argc, char **argv) if (!fn.endsWith(QLatin1String(".java")) && !fn.endsWith(QLatin1String(".ui")) && !fn.endsWith(QLatin1String(".js")) - && !fn.endsWith(QLatin1String(".qs"))) { + && !fn.endsWith(QLatin1String(".qs")) + && !fn.endsWith(QLatin1String(".qml"))) { int offset = 0; int depth = 0; do { @@ -518,6 +519,8 @@ int main(int argc, char **argv) else if (it->endsWith(QLatin1String(".js"), Qt::CaseInsensitive) || it->endsWith(QLatin1String(".qs"), Qt::CaseInsensitive)) loadQScript(fetchedTor, *it, cd); + else if (it->endsWith(QLatin1String(".qml"), Qt::CaseInsensitive)) + loadQml(fetchedTor, *it, cd); else sourceFilesCpp << *it; } diff --git a/tools/linguist/lupdate/qml.cpp b/tools/linguist/lupdate/qml.cpp new file mode 100644 index 0000000..860d87c --- /dev/null +++ b/tools/linguist/lupdate/qml.cpp @@ -0,0 +1,240 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Linguist 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 "lupdate.h" + +#include <translator.h> + +#include <QtCore/QDebug> +#include <QtCore/QFile> +#include <QtCore/QString> + +#include "parser/qmljsengine_p.h" +#include "parser/qmljsparser_p.h" +#include "parser/qmljslexer_p.h" +#include "parser/qmljsnodepool_p.h" +#include "parser/qmljsastvisitor_p.h" +#include "parser/qmljsast_p.h" + +#include <QCoreApplication> +#include <QFile> +#include <QFileInfo> +#include <QtDebug> +#include <QStringList> + +#include <iostream> +#include <cstdlib> + +QT_BEGIN_NAMESPACE + +using namespace QmlJS; + +class FindTrCalls: protected AST::Visitor +{ +public: + void operator()(Translator *translator, const QString &fileName, AST::Node *node) + { + m_translator = translator; + m_fileName = fileName; + m_component = QFileInfo(fileName).baseName(); //matches qsTr usage in QScriptEngine + accept(node); + } + +protected: + using AST::Visitor::visit; + using AST::Visitor::endVisit; + + void accept(AST::Node *node) + { AST::Node::acceptChild(node, this); } + + virtual void endVisit(AST::CallExpression *node) + { + if (AST::IdentifierExpression *idExpr = AST::cast<AST::IdentifierExpression *>(node->base)) { + if (idExpr->name->asString() == QLatin1String("qsTr") || + idExpr->name->asString() == QLatin1String("QT_TR_NOOP")) { + if (node->arguments && AST::cast<AST::StringLiteral *>(node->arguments->expression)) { + AST::StringLiteral *literal = AST::cast<AST::StringLiteral *>(node->arguments->expression); + const QString source = literal->value->asString(); + + QString comment; + bool plural = false; + AST::ArgumentList *commentNode = node->arguments->next; + if (commentNode) { + literal = AST::cast<AST::StringLiteral *>(commentNode->expression); + comment = literal->value->asString(); + + AST::ArgumentList *nNode = commentNode->next; + if (nNode) { + AST::NumericLiteral *numLiteral = AST::cast<AST::NumericLiteral *>(nNode->expression); + if (numLiteral) { + plural = true; + } + } + } + + TranslatorMessage msg(m_component, source, + comment, QString(), m_fileName, + node->firstSourceLocation().startLine, QStringList(), + TranslatorMessage::Unfinished, plural); + m_translator->extend(msg); + } + } else if (idExpr->name->asString() == QLatin1String("qsTranslate") || + idExpr->name->asString() == QLatin1String("QT_TRANSLATE_NOOP")) { + if (node->arguments && AST::cast<AST::StringLiteral *>(node->arguments->expression)) { + AST::StringLiteral *literal = AST::cast<AST::StringLiteral *>(node->arguments->expression); + const QString context = literal->value->asString(); + + QString source; + QString comment; + bool plural = false; + AST::ArgumentList *sourceNode = node->arguments->next; + if (sourceNode) { + literal = AST::cast<AST::StringLiteral *>(sourceNode->expression); + source = literal->value->asString(); + AST::ArgumentList *commentNode = sourceNode->next; + if (commentNode) { + literal = AST::cast<AST::StringLiteral *>(commentNode->expression); + comment = literal->value->asString(); + + AST::ArgumentList *nNode = commentNode->next; + if (nNode) { + AST::NumericLiteral *numLiteral = AST::cast<AST::NumericLiteral *>(nNode->expression); + if (numLiteral) { + plural = true; + } + } + } + } + + TranslatorMessage msg(context, source, + comment, QString(), m_fileName, + node->firstSourceLocation().startLine, QStringList(), + TranslatorMessage::Unfinished, plural); + m_translator->extend(msg); + } + + } + } + } + +private: + Translator *m_translator; + QString m_fileName; + QString m_component; +}; + +QString createErrorString(const QString &filename, const QString &code, Parser &parser) +{ + // print out error + QStringList lines = code.split(QLatin1Char('\n')); + lines.append(QLatin1String("\n")); // sentinel. + QString errorString; + + foreach (const DiagnosticMessage &m, parser.diagnosticMessages()) { + + if (m.isWarning()) + continue; + + QString error = filename + QLatin1Char(':') + QString::number(m.loc.startLine) + + QLatin1Char(':') + QString::number(m.loc.startColumn) + QLatin1String(": error: ") + + m.message + QLatin1Char('\n'); + + int line = 0; + if (m.loc.startLine > 0) + line = m.loc.startLine - 1; + + const QString textLine = lines.at(line); + + error += textLine + QLatin1Char('\n'); + + int column = m.loc.startColumn - 1; + if (column < 0) + column = 0; + + column = qMin(column, textLine.length()); + + for (int i = 0; i < column; ++i) { + const QChar ch = textLine.at(i); + if (ch.isSpace()) + error += ch.unicode(); + else + error += QLatin1Char(' '); + } + error += QLatin1String("^\n"); + errorString += error; + } + return errorString; +} + +bool loadQml(Translator &translator, const QString &filename, ConversionData &cd) +{ + cd.m_sourceFileName = filename; + QFile file(filename); + if (!file.open(QIODevice::ReadOnly)) { + cd.appendError(QString::fromLatin1("Cannot open %1: %2") + .arg(filename, file.errorString())); + return false; + } + + const QString code = QTextStream(&file).readAll(); + + Engine driver; + Parser parser(&driver); + + NodePool nodePool(filename, &driver); + driver.setNodePool(&nodePool); + + Lexer lexer(&driver); + lexer.setCode(code, /*line = */ 1); + driver.setLexer(&lexer); + + if (parser.parse()) { + FindTrCalls trCalls; + trCalls(&translator, filename, parser.ast()); + } else { + QString error = createErrorString(filename, code, parser); + cd.appendError(error); + return false; + } + return true; +} + +QT_END_NAMESPACE diff --git a/tools/qdoc3/cppcodemarker.cpp b/tools/qdoc3/cppcodemarker.cpp index a8f6a02..f3c8fa5 100644 --- a/tools/qdoc3/cppcodemarker.cpp +++ b/tools/qdoc3/cppcodemarker.cpp @@ -1127,7 +1127,7 @@ QList<Section> CppCodeMarker::qmlSections(const QmlClassNode* qmlClassNode, "signal", "signals"); FastSection qmlattachedsignals(qmlClassNode, - "QML Attached Signals", + "Attached Signals", "signal", "signals"); FastSection qmlmethods(qmlClassNode, @@ -1135,7 +1135,7 @@ QList<Section> CppCodeMarker::qmlSections(const QmlClassNode* qmlClassNode, "method", "methods"); FastSection qmlattachedmethods(qmlClassNode, - "QML Attached Methods", + "Attached Methods", "method", "methods"); diff --git a/tools/qdoc3/test/qml.qdocconf b/tools/qdoc3/test/qml.qdocconf new file mode 100644 index 0000000..3b5d8dc --- /dev/null +++ b/tools/qdoc3/test/qml.qdocconf @@ -0,0 +1,80 @@ +include(compat.qdocconf) +include(macros.qdocconf) +include(qt-cpp-ignore.qdocconf) +include(qt-html-templates.qdocconf) +include(qt-defines.qdocconf) + +project = Qml +description = Qml Reference Documentation +url = http://doc.qtsoftware.com/4.6 +qmlonly = true + +edition.Console.modules = QtCore QtDBus QtNetwork QtScript QtSql QtXml \ + QtXmlPatterns QtTest +edition.Desktop.modules = QtCore QtDBus QtGui QtNetwork QtOpenGL QtScript QtSql QtSvg \ + QtWebKit QtXml QtXmlPatterns Qt3Support QtHelp \ + QtDesigner QtAssistant QAxContainer Phonon \ + QAxServer QtUiTools QtTest QtDBus +edition.DesktopLight.modules = QtCore QtDBus QtGui Qt3SupportLight QtTest +edition.DesktopLight.groups = -graphicsview-api + +qhp.projects = Qml + +qhp.Qml.file = qml.qhp +qhp.Qml.namespace = com.trolltech.qml.460 +qhp.Qml.virtualFolder = qdoc +qhp.Qml.indexTitle = Qml Reference + +# Files not referenced in any qdoc file +# See also extraimages.HTML +qhp.Qml.extraFiles = classic.css \ + images/qt-logo.png + +qhp.Qml.filterAttributes = qt 4.6.0 qtrefdoc +qhp.Qml.customFilters.Qt.name = Qt 4.6.0 +qhp.Qml.customFilters.Qt.filterAttributes = qt 4.6.0 +qhp.Qml.subprojects = classes +qhp.Qml.subprojects.classes.title = Elements +qhp.Qml.subprojects.classes.indexTitle = Qml Elements +qhp.Qml.subprojects.classes.selectors = fake:qmlclass +qhp.Qml.subprojects.classes.sortPages = true + +language = Cpp + +headerdirs = $QT_SOURCE_TREE/src/declarative +sourcedirs = $QT_SOURCE_TREE/src/declarative \ + $QT_SOURCE_TREE/doc/src/declarative + +sources += $QT_SOURCE_TREE/doc/src/tutorials/declarative.qdoc + +sources.fileextensions = "*.cpp *.qdoc" +examples.fileextensions = "*.cpp *.h *.js *.qml" + +exampledirs = $QT_SOURCE_TREE/doc/src \ + $QT_SOURCE_TREE/examples \ + $QT_SOURCE_TREE/examples/tutorials \ + $QT_SOURCE_TREE \ + $QT_SOURCE_TREE/qmake/examples \ + $QT_SOURCE_TREE/src/3rdparty/webkit/WebKit/qt/docs +imagedirs = $QT_SOURCE_TREE/doc/src/images \ + $QT_SOURCE_TREE/examples \ + $QT_SOURCE_TREE/doc/src/declarative/pics +outputdir = $QT_BUILD_TREE/doc-build/html-qml +tagfile = $QT_BUILD_TREE/doc-build/html-qml/qt.tags +base = file:$QT_BUILD_TREE/doc/html-qml + +HTML.stylesheets = classic.css + +HTML.postheader = "<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\">\n" \ + "<tr>\n" \ + "<td align=\"left\" valign=\"top\">" \ + "<img src=\"images/qt-logo.png\" align=\"left\" border=\"0\"/>" \ + "</td>\n" \ + "<td width=\"1\"> </td>" \ + "<td class=\"postheader\" valign=\"center\" align=\"left\">" \ + "<a href=\"qmlreference.html\">" \ + "<font color=\"#004faf\">Home</font></a> ·" \ + " <a href=\"qmlelements.html\">" \ + "<font color=\"#004faf\">Elements</font></a>" \ + "</td>\n" \ + "</tr></table>" diff --git a/tools/qdoc3/test/qt-build-docs.qdocconf b/tools/qdoc3/test/qt-build-docs.qdocconf index d1733e5..0848beb 100644 --- a/tools/qdoc3/test/qt-build-docs.qdocconf +++ b/tools/qdoc3/test/qt-build-docs.qdocconf @@ -105,7 +105,8 @@ exampledirs = $QT_SOURCE_TREE/doc/src \ $QT_SOURCE_TREE/qmake/examples \ $QT_SOURCE_TREE/src/3rdparty/webkit/WebKit/qt/docs imagedirs = $QT_SOURCE_TREE/doc/src/images \ - $QT_SOURCE_TREE/examples + $QT_SOURCE_TREE/examples \ + $QT_SOURCE_TREE/doc/src/declarative/pics outputdir = $QT_BUILD_TREE/doc/html tagfile = $QT_BUILD_TREE/doc/html/qt.tags base = file:$QT_BUILD_TREE/doc/html diff --git a/tools/qdoc3/test/qt.qdocconf b/tools/qdoc3/test/qt.qdocconf index d70ef58..d019bdf 100644 --- a/tools/qdoc3/test/qt.qdocconf +++ b/tools/qdoc3/test/qt.qdocconf @@ -108,7 +108,8 @@ exampledirs = $QTDIR/doc/src \ $QTDIR/qmake/examples \ $QTDIR/src/3rdparty/webkit/WebKit/qt/docs imagedirs = $QTDIR/doc/src/images \ - $QTDIR/examples + $QTDIR/examples \ + $QTDIR/doc/src/declarative/pics outputdir = $QTDIR/doc/html tagfile = $QTDIR/doc/html/qt.tags base = file:$QTDIR/doc/html diff --git a/tools/qmldebugger/creatorplugin/QmlInspector.pluginspec b/tools/qmldebugger/creatorplugin/QmlInspector.pluginspec new file mode 100644 index 0000000..cdba135 --- /dev/null +++ b/tools/qmldebugger/creatorplugin/QmlInspector.pluginspec @@ -0,0 +1,28 @@ +<plugin name="QmlInspector" version="1.3.80" compatVersion="1.3.80"> + <vendor>Nokia Corporation</vendor> + <copyright>(C) 2008-2009 Nokia Corporation</copyright> + <license> +Commercial Usage + +Licensees holding valid Qt Commercial licenses may use this plugin in +accordance with the Qt Commercial License Agreement provided with the +Software or, alternatively, in accordance with the terms contained in +a written agreement between you and Nokia. + +GNU Lesser General Public License Usage + +Alternatively, this plugin may be used under the terms of the GNU Lesser +General Public License version 2.1 as published by the Free Software +Foundation. 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.</license> + <description>Qml support</description> + <url>http://qt.nokia.com</url> + <dependencyList> + <dependency name="QmlProjectManager" version="1.3.80"/> + <dependency name="ProjectExplorer" version="1.3.80"/> + <dependency name="CppTools" version="1.3.80"/> + <dependency name="CppEditor" version="1.3.80"/> + <dependency name="Help" version="1.3.80"/> + </dependencyList> +</plugin>
\ No newline at end of file diff --git a/tools/qmldebugger/creatorplugin/README b/tools/qmldebugger/creatorplugin/README new file mode 100644 index 0000000..e7e205e --- /dev/null +++ b/tools/qmldebugger/creatorplugin/README @@ -0,0 +1,5 @@ +To enable this project, qmldebugger.pro requires that these two environment +variables be defined: + +CREATOR_SRC_DIR -> source directory for Qt Creator +CREATOR_BUILD_DIR -> build directory for Qt Creator diff --git a/tools/qmldebugger/creatorplugin/creatorplugin.pro b/tools/qmldebugger/creatorplugin/creatorplugin.pro new file mode 100644 index 0000000..ff7f3da --- /dev/null +++ b/tools/qmldebugger/creatorplugin/creatorplugin.pro @@ -0,0 +1,30 @@ +TEMPLATE = lib +TARGET = QmlInspector + +INCLUDEPATH += . +DEPENDPATH += . + +include(../standalone/qmldebugger.pri) + +HEADERS += qmlinspectorplugin.h \ + qmlinspector.h \ + qmlinspectormode.h \ + inspectoroutputpane.h \ + runcontrol.h + +SOURCES += qmlinspectorplugin.cpp \ + qmlinspectormode.cpp \ + inspectoroutputpane.cpp \ + runcontrol.cpp + +OTHER_FILES += QmlInspector.pluginspec +RESOURCES += qmlinspector.qrc + +IDE_BUILD_TREE=$$(CREATOR_BUILD_DIR) + +include($$(CREATOR_SRC_DIR)/src/qtcreatorplugin.pri) +include($$(CREATOR_SRC_DIR)/src/plugins/projectexplorer/projectexplorer.pri) +include($$(CREATOR_SRC_DIR)/src/plugins/coreplugin/coreplugin.pri) +include($$(CREATOR_SRC_DIR)/src/plugins/texteditor/texteditor.pri) +LIBS += -L$$(CREATOR_BUILD_DIR)/lib/qtcreator + diff --git a/tools/qmldebugger/creatorplugin/images/logo.png b/tools/qmldebugger/creatorplugin/images/logo.png Binary files differnew file mode 100644 index 0000000..5ac14a5 --- /dev/null +++ b/tools/qmldebugger/creatorplugin/images/logo.png diff --git a/tools/qmldebugger/creatorplugin/inspectoroutputpane.cpp b/tools/qmldebugger/creatorplugin/inspectoroutputpane.cpp new file mode 100644 index 0000000..b830007 --- /dev/null +++ b/tools/qmldebugger/creatorplugin/inspectoroutputpane.cpp @@ -0,0 +1,150 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt QML Debugger 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 <QtGui/qtextedit.h> + +#include "inspectoroutputpane.h" + +InspectorOutputPane::InspectorOutputPane(QObject *parent) + : Core::IOutputPane(parent), + m_textEdit(new QTextEdit) +{ +} + +InspectorOutputPane::~InspectorOutputPane() +{ + delete m_textEdit; +} + +QWidget *InspectorOutputPane::outputWidget(QWidget *parent) +{ + Q_UNUSED(parent); + return m_textEdit; +} + +QList<QWidget*> InspectorOutputPane::toolBarWidgets() const +{ + return QList<QWidget *>(); +} + +QString InspectorOutputPane::name() const +{ + return tr("Inspector Output"); +} + +int InspectorOutputPane::priorityInStatusBar() const +{ + return 1; +} + +void InspectorOutputPane::clearContents() +{ + m_textEdit->clear(); +} + +void InspectorOutputPane::visibilityChanged(bool visible) +{ + Q_UNUSED(visible); +} + +void InspectorOutputPane::setFocus() +{ + m_textEdit->setFocus(); +} + +bool InspectorOutputPane::hasFocus() +{ + return m_textEdit->hasFocus(); +} + +bool InspectorOutputPane::canFocus() +{ + return true; +} + +bool InspectorOutputPane::canNavigate() +{ + return false; +} + +bool InspectorOutputPane::canNext() +{ + return false; +} + +bool InspectorOutputPane::canPrevious() +{ + return false; +} + +void InspectorOutputPane::goToNext() +{ +} + +void InspectorOutputPane::goToPrev() +{ +} + +void InspectorOutputPane::addOutput(RunControl *, const QString &text) +{ + m_textEdit->insertPlainText(text); + m_textEdit->moveCursor(QTextCursor::End); +} + +void InspectorOutputPane::addOutputInline(RunControl *, const QString &text) +{ + m_textEdit->insertPlainText(text); + m_textEdit->moveCursor(QTextCursor::End); +} + +void InspectorOutputPane::addErrorOutput(RunControl *, const QString &text) +{ + m_textEdit->append(text); + m_textEdit->moveCursor(QTextCursor::End); +} + +void InspectorOutputPane::addInspectorStatus(const QString &text) +{ + m_textEdit->setTextColor(Qt::darkGreen); + m_textEdit->append(text); + m_textEdit->moveCursor(QTextCursor::End); + m_textEdit->setTextColor(Qt::black); +} + diff --git a/tools/qmldebugger/creatorplugin/inspectoroutputpane.h b/tools/qmldebugger/creatorplugin/inspectoroutputpane.h new file mode 100644 index 0000000..04f506d --- /dev/null +++ b/tools/qmldebugger/creatorplugin/inspectoroutputpane.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt QML Debugger 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 INSPECTOROUTPUTPANE_H +#define INSPECTOROUTPUTPANE_H + +#include <QtCore/QObject> + +#include <coreplugin/ioutputpane.h> + + +QT_BEGIN_NAMESPACE + +class QTextEdit; + +class RunControl; + +class InspectorOutputPane : public Core::IOutputPane +{ + Q_OBJECT +public: + InspectorOutputPane(QObject *parent = 0); + virtual ~InspectorOutputPane(); + + virtual QWidget *outputWidget(QWidget *parent); + virtual QList<QWidget*> toolBarWidgets() const; + virtual QString name() const; + + virtual int priorityInStatusBar() const; + + virtual void clearContents(); + virtual void visibilityChanged(bool visible); + + virtual void setFocus(); + virtual bool hasFocus(); + virtual bool canFocus(); + + virtual bool canNavigate(); + virtual bool canNext(); + virtual bool canPrevious(); + virtual void goToNext(); + virtual void goToPrev(); + +public slots: + void addOutput(RunControl *, const QString &text); + void addOutputInline(RunControl *, const QString &text); + + void addErrorOutput(RunControl *, const QString &text); + void addInspectorStatus(const QString &text); + +private: + QTextEdit *m_textEdit; +}; + +QT_END_NAMESPACE + +#endif + diff --git a/tools/qmldebugger/creatorplugin/qmlinspector.h b/tools/qmldebugger/creatorplugin/qmlinspector.h new file mode 100644 index 0000000..354e88e --- /dev/null +++ b/tools/qmldebugger/creatorplugin/qmlinspector.h @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt QML Debugger 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 QMLINSPECTOR_H +#define QMLINSPECTOR_H + +#include <QString> + +namespace QmlInspector { + namespace Constants { + const char * const RUN = "QmlInspector.Run"; + const char * const STOP = "QmlInspector.Stop"; + + const char * const C_INSPECTOR = "QmlInspector"; + }; + + class StartParameters + { + public: + StartParameters() : port(0) {} + ~StartParameters() {} + + QString address; + quint16 port; + }; +}; + +#endif diff --git a/tools/qmldebugger/creatorplugin/qmlinspector.qrc b/tools/qmldebugger/creatorplugin/qmlinspector.qrc new file mode 100644 index 0000000..45e8dda --- /dev/null +++ b/tools/qmldebugger/creatorplugin/qmlinspector.qrc @@ -0,0 +1,6 @@ +<RCC> + <qresource prefix="/qmlinspector" > + <file>images/logo.png</file> + </qresource> +</RCC> + diff --git a/tools/qmldebugger/creatorplugin/qmlinspectormode.cpp b/tools/qmldebugger/creatorplugin/qmlinspectormode.cpp new file mode 100644 index 0000000..aa7d292 --- /dev/null +++ b/tools/qmldebugger/creatorplugin/qmlinspectormode.cpp @@ -0,0 +1,566 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt QML Debugger 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/QStringList> +#include <QtCore/QtPlugin> +#include <QtCore/QDebug> + +#include <QtGui/qtoolbutton.h> +#include <QtGui/qtoolbar.h> +#include <QtGui/qboxlayout.h> +#include <QtGui/qlabel.h> +#include <QtGui/qdockwidget.h> +#include <QtGui/qaction.h> +#include <QtGui/qlineedit.h> +#include <QtGui/qlabel.h> +#include <QtGui/qspinbox.h> + +#include <coreplugin/basemode.h> +#include <coreplugin/findplaceholder.h> +#include <coreplugin/minisplitter.h> +#include <coreplugin/outputpane.h> +#include <coreplugin/rightpane.h> +#include <coreplugin/navigationwidget.h> +#include <coreplugin/icore.h> +#include <coreplugin/coreconstants.h> +#include <coreplugin/uniqueidmanager.h> + +#include <coreplugin/editormanager/editormanager.h> +#include <coreplugin/actionmanager/actionmanager.h> + +#include <texteditor/itexteditor.h> + +#include <projectexplorer/runconfiguration.h> +#include <projectexplorer/projectexplorer.h> +#include <projectexplorer/projectexplorerconstants.h> +#include <projectexplorer/project.h> + +#include <utils/styledbar.h> +#include <utils/fancymainwindow.h> + +#include <private/qmldebug_p.h> +#include <private/qmldebugclient_p.h> + +#include "../standalone/objectpropertiesview.h" +#include "../standalone/objecttree.h" +#include "../standalone/watchtable.h" +#include "../standalone/canvasframerate.h" +#include "../standalone/expressionquerywidget.h" + +#include "qmlinspector.h" +#include "qmlinspectormode.h" + +QT_BEGIN_NAMESPACE + + +class EngineSpinBox : public QSpinBox +{ + Q_OBJECT +public: + struct EngineInfo + { + QString name; + int id; + }; + + EngineSpinBox(QWidget *parent = 0); + + void addEngine(int engine, const QString &name); + void clearEngines(); + +protected: + virtual QString textFromValue(int value) const; + virtual int valueFromText(const QString &text) const; + +private: + QList<EngineInfo> m_engines; +}; + +EngineSpinBox::EngineSpinBox(QWidget *parent) + : QSpinBox(parent) +{ + setEnabled(false); + setReadOnly(true); + setRange(0, 0); +} + +void EngineSpinBox::addEngine(int engine, const QString &name) +{ + EngineInfo info; + info.id = engine; + if (name.isEmpty()) + info.name = tr("Engine %1", "engine number").arg(engine); + else + info.name = name; + m_engines << info; + + setRange(0, m_engines.count()-1); +} + +void EngineSpinBox::clearEngines() +{ + m_engines.clear(); +} + +QString EngineSpinBox::textFromValue(int value) const +{ + for (int i=0; i<m_engines.count(); i++) { + if (m_engines[i].id == value) + return m_engines[i].name; + } + return QLatin1String("<None>"); +} + +int EngineSpinBox::valueFromText(const QString &text) const +{ + for (int i=0; i<m_engines.count(); i++) { + if (m_engines[i].name == text) + return m_engines[i].id; + } + return -1; +} + + +QmlInspectorMode::QmlInspectorMode(QObject *parent) + : Core::BaseMode(parent), + m_conn(0), + m_client(0), + m_engineQuery(0), + m_contextQuery(0) +{ + m_watchTableModel = new WatchTableModel(0, this); + + initActions(); + setWidget(createModeWindow()); + + setName(tr("QML Inspect")); + setIcon(QIcon(":/qmlinspector/images/logo.png")); + setUniqueModeName("QML_INSPECT_MODE"); +} + +quint16 QmlInspectorMode::viewerPort() const +{ + return m_portSpinBox->value(); +} + +void QmlInspectorMode::connectToViewer() +{ + if (m_conn && m_conn->state() != QAbstractSocket::UnconnectedState) + return; + + delete m_client; m_client = 0; + + if (m_conn) { + m_conn->disconnectFromHost(); + delete m_conn; + } + + m_conn = new QmlDebugConnection(this); + connect(m_conn, SIGNAL(stateChanged(QAbstractSocket::SocketState)), + SLOT(connectionStateChanged())); + connect(m_conn, SIGNAL(error(QAbstractSocket::SocketError)), + SLOT(connectionError())); + m_conn->connectToHost(m_addressEdit->text(), m_portSpinBox->value()); +} + +void QmlInspectorMode::disconnectFromViewer() +{ + m_conn->disconnectFromHost(); +} + +void QmlInspectorMode::connectionStateChanged() +{ + switch (m_conn->state()) { + default: + case QAbstractSocket::UnconnectedState: + { + emit statusMessage(tr("[Inspector] disconnected.\n\n")); + m_addressEdit->setEnabled(true); + m_portSpinBox->setEnabled(true); + + delete m_engineQuery; + m_engineQuery = 0; + delete m_contextQuery; + m_contextQuery = 0; + break; + } + case QAbstractSocket::HostLookupState: + emit statusMessage(tr("[Inspector] resolving host...")); + break; + case QAbstractSocket::ConnectingState: + emit statusMessage(tr("[Inspector] connecting to debug server...")); + break; + case QAbstractSocket::ConnectedState: + { + emit statusMessage(tr("[Inspector] connected.\n")); + m_addressEdit->setEnabled(false); + m_portSpinBox->setEnabled(false); + + if (!m_client) { + m_client = new QmlEngineDebug(m_conn, this); + m_objectTreeWidget->setEngineDebug(m_client); + m_propertiesWidget->setEngineDebug(m_client); + m_watchTableModel->setEngineDebug(m_client); + m_expressionWidget->setEngineDebug(m_client); + } + + m_objectTreeWidget->clear(); + m_propertiesWidget->clear(); + m_expressionWidget->clear(); + m_watchTableModel->removeAllWatches(); + m_frameRateWidget->reset(m_conn); + + reloadEngines(); + break; + } + case QAbstractSocket::ClosingState: + emit statusMessage(tr("[Inspector] closing...")); + break; + } +} + +void QmlInspectorMode::connectionError() +{ + emit statusMessage(tr("[Inspector] error: (%1) %2", "%1=error code, %2=error message") + .arg(m_conn->error()).arg(m_conn->errorString())); +} + +void QmlInspectorMode::initActions() +{ + m_actions.startAction = new QAction(tr("Start Inspector"), this); + m_actions.startAction->setIcon(QIcon(ProjectExplorer::Constants::ICON_RUN)); + + m_actions.stopAction = new QAction(tr("Stop Inspector"), this); + m_actions.stopAction->setIcon(QIcon(ProjectExplorer::Constants::ICON_STOP)); + + Core::ICore *core = Core::ICore::instance(); + Core::ActionManager *am = core->actionManager(); + Core::UniqueIDManager *uidm = core->uniqueIDManager(); + + QList<int> context; + context << uidm->uniqueIdentifier(QmlInspector::Constants::C_INSPECTOR); + + am->registerAction(m_actions.startAction, QmlInspector::Constants::RUN, context); + connect(m_actions.startAction, SIGNAL(triggered()), SIGNAL(startViewer())); + + am->registerAction(m_actions.stopAction, QmlInspector::Constants::STOP, context); + connect(m_actions.stopAction, SIGNAL(triggered()), SIGNAL(stopViewer())); +} + + +QToolButton *QmlInspectorMode::createToolButton(QAction *action) +{ + QToolButton *button = new QToolButton; + button->setDefaultAction(action); + return button; +} + +QWidget *QmlInspectorMode::createMainView() +{ + initWidgets(); + + Utils::FancyMainWindow *mainWindow = new Utils::FancyMainWindow; + mainWindow->setTabPosition(Qt::AllDockWidgetAreas, QTabWidget::North); + mainWindow->setDocumentMode(true); + + QBoxLayout *editorHolderLayout = new QVBoxLayout; + editorHolderLayout->setMargin(0); + editorHolderLayout->setSpacing(0); + + QWidget *editorAndFindWidget = new QWidget; + editorAndFindWidget->setLayout(editorHolderLayout); + editorHolderLayout->addWidget(new Core::EditorManagerPlaceHolder(this)); + editorHolderLayout->addWidget(new Core::FindToolBarPlaceHolder(editorAndFindWidget)); + + Utils::StyledBar *treeOptionBar = new Utils::StyledBar; + QHBoxLayout *treeOptionBarLayout = new QHBoxLayout(treeOptionBar); + treeOptionBarLayout->setContentsMargins(5, 0, 5, 0); + treeOptionBarLayout->setSpacing(5); + treeOptionBarLayout->addWidget(new QLabel(tr("QML engine:"))); + treeOptionBarLayout->addWidget(m_engineSpinBox); + + QWidget *treeWindow = new QWidget; + QVBoxLayout *treeWindowLayout = new QVBoxLayout(treeWindow); + treeWindowLayout->setMargin(0); + treeWindowLayout->setSpacing(0); + treeWindowLayout->addWidget(treeOptionBar); + treeWindowLayout->addWidget(m_objectTreeWidget); + + Core::MiniSplitter *documentAndTree = new Core::MiniSplitter; + documentAndTree->addWidget(editorAndFindWidget); + documentAndTree->addWidget(new Core::RightPanePlaceHolder(this)); + documentAndTree->addWidget(treeWindow); + documentAndTree->setStretchFactor(0, 2); + documentAndTree->setStretchFactor(1, 0); + documentAndTree->setStretchFactor(2, 0); + + Utils::StyledBar *configBar = new Utils::StyledBar; + configBar->setProperty("topBorder", true); + + QHBoxLayout *configBarLayout = new QHBoxLayout(configBar); + configBarLayout->setMargin(0); + configBarLayout->setSpacing(5); + + Core::ICore *core = Core::ICore::instance(); + Core::ActionManager *am = core->actionManager(); + configBarLayout->addWidget(createToolButton(am->command(QmlInspector::Constants::RUN)->action())); + configBarLayout->addWidget(createToolButton(am->command(QmlInspector::Constants::STOP)->action())); + configBarLayout->addWidget(m_addressEdit); + configBarLayout->addWidget(m_portSpinBox); + configBarLayout->addStretch(); + + QWidget *widgetAboveTabs = new QWidget; + QVBoxLayout *widgetAboveTabsLayout = new QVBoxLayout(widgetAboveTabs); + widgetAboveTabsLayout->setMargin(0); + widgetAboveTabsLayout->setSpacing(0); + widgetAboveTabsLayout->addWidget(documentAndTree); + widgetAboveTabsLayout->addWidget(configBar); + + Core::MiniSplitter *mainSplitter = new Core::MiniSplitter(Qt::Vertical); + mainSplitter->addWidget(widgetAboveTabs); + mainSplitter->addWidget(createBottomWindow()); + mainSplitter->setStretchFactor(0, 3); + mainSplitter->setStretchFactor(1, 1); + + QWidget *centralWidget = new QWidget; + QVBoxLayout *centralLayout = new QVBoxLayout(centralWidget); + centralLayout->setMargin(0); + centralLayout->setSpacing(0); + centralLayout->addWidget(mainSplitter); + + mainWindow->setCentralWidget(centralWidget); + + return mainWindow; +} + +QWidget *QmlInspectorMode::createBottomWindow() +{ + Utils::FancyMainWindow *win = new Utils::FancyMainWindow; + win->setTabPosition(Qt::AllDockWidgetAreas, QTabWidget::North); + win->setDocumentMode(true); + win->setTrackingEnabled(true); + + Core::MiniSplitter *leftSplitter = new Core::MiniSplitter(Qt::Vertical); + leftSplitter->addWidget(m_propertiesWidget); + leftSplitter->addWidget(m_expressionWidget); + leftSplitter->setStretchFactor(0, 2); + leftSplitter->setStretchFactor(1, 1); + + Core::MiniSplitter *propSplitter = new Core::MiniSplitter(Qt::Horizontal); + propSplitter->addWidget(leftSplitter); + propSplitter->addWidget(m_watchTableView); + propSplitter->setStretchFactor(0, 2); + propSplitter->setStretchFactor(1, 1); + propSplitter->setWindowTitle(tr("Properties and Watchers")); + + QDockWidget *propertiesDock = win->addDockForWidget(propSplitter); + win->addDockWidget(Qt::TopDockWidgetArea, propertiesDock); + + QDockWidget *frameRateDock = win->addDockForWidget(m_frameRateWidget); + win->addDockWidget(Qt::TopDockWidgetArea, frameRateDock); + + // stack the dock widgets as tabs + win->tabifyDockWidget(frameRateDock, propertiesDock); + + return win; +} + +QWidget *QmlInspectorMode::createModeWindow() +{ + // right-side window with editor, output etc. + Core::MiniSplitter *mainWindowSplitter = new Core::MiniSplitter; + mainWindowSplitter->addWidget(createMainView()); + mainWindowSplitter->addWidget(new Core::OutputPanePlaceHolder(this)); + mainWindowSplitter->setStretchFactor(0, 10); + mainWindowSplitter->setStretchFactor(1, 0); + mainWindowSplitter->setOrientation(Qt::Vertical); + + // navigation + right-side window + Core::MiniSplitter *splitter = new Core::MiniSplitter; + splitter->addWidget(new Core::NavigationWidgetPlaceHolder(this)); + splitter->addWidget(mainWindowSplitter); + splitter->setStretchFactor(0, 0); + splitter->setStretchFactor(1, 1); + return splitter; +} + +void QmlInspectorMode::initWidgets() +{ + m_objectTreeWidget = new ObjectTree; + m_propertiesWidget = new ObjectPropertiesView; + m_watchTableView = new WatchTableView(m_watchTableModel); + m_frameRateWidget = new CanvasFrameRate; + m_expressionWidget = new ExpressionQueryWidget(ExpressionQueryWidget::ShellMode); + + // FancyMainWindow uses widgets' window titles for tab labels + m_objectTreeWidget->setWindowTitle(tr("Object Tree")); + m_frameRateWidget->setWindowTitle(tr("Frame rate")); + + m_watchTableView->setModel(m_watchTableModel); + WatchTableHeaderView *header = new WatchTableHeaderView(m_watchTableModel); + m_watchTableView->setHorizontalHeader(header); + + connect(m_objectTreeWidget, SIGNAL(activated(QmlDebugObjectReference)), + this, SLOT(treeObjectActivated(QmlDebugObjectReference))); + + connect(m_objectTreeWidget, SIGNAL(currentObjectChanged(QmlDebugObjectReference)), + m_propertiesWidget, SLOT(reload(QmlDebugObjectReference))); + + connect(m_objectTreeWidget, SIGNAL(expressionWatchRequested(QmlDebugObjectReference,QString)), + m_watchTableModel, SLOT(expressionWatchRequested(QmlDebugObjectReference,QString))); + + connect(m_propertiesWidget, SIGNAL(activated(QmlDebugObjectReference,QmlDebugPropertyReference)), + m_watchTableModel, SLOT(togglePropertyWatch(QmlDebugObjectReference,QmlDebugPropertyReference))); + + connect(m_watchTableModel, SIGNAL(watchCreated(QmlDebugWatch*)), + m_propertiesWidget, SLOT(watchCreated(QmlDebugWatch*))); + + connect(m_watchTableModel, SIGNAL(rowsInserted(QModelIndex,int,int)), + m_watchTableView, SLOT(scrollToBottom())); + + connect(m_watchTableView, SIGNAL(objectActivated(int)), + m_objectTreeWidget, SLOT(setCurrentObject(int))); + + connect(m_objectTreeWidget, SIGNAL(currentObjectChanged(QmlDebugObjectReference)), + m_expressionWidget, SLOT(setCurrentObject(QmlDebugObjectReference))); + + m_addressEdit = new QLineEdit; + m_addressEdit->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred); + m_addressEdit->setText("127.0.0.1"); + + m_portSpinBox = new QSpinBox; + m_portSpinBox->setMinimum(1024); + m_portSpinBox->setMaximum(20000); + m_portSpinBox->setValue(3768); + + m_engineSpinBox = new EngineSpinBox; + m_engineSpinBox->setEnabled(false); + connect(m_engineSpinBox, SIGNAL(valueChanged(int)), + SLOT(queryEngineContext(int))); +} + +void QmlInspectorMode::reloadEngines() +{ + if (m_engineQuery) { + emit statusMessage("[Inspector] Waiting for response to previous engine query"); + return; + } + + m_engineSpinBox->setEnabled(false); + + m_engineQuery = m_client->queryAvailableEngines(this); + if (!m_engineQuery->isWaiting()) + enginesChanged(); + else + QObject::connect(m_engineQuery, SIGNAL(stateChanged(QmlDebugQuery::State)), + this, SLOT(enginesChanged())); +} + +void QmlInspectorMode::enginesChanged() +{ + m_engineSpinBox->clearEngines(); + + QList<QmlDebugEngineReference> engines = m_engineQuery->engines(); + delete m_engineQuery; m_engineQuery = 0; + + if (engines.isEmpty()) + qWarning("qmldebugger: no engines found!"); + + m_engineSpinBox->setEnabled(true); + + for (int i=0; i<engines.count(); i++) + m_engineSpinBox->addEngine(engines.at(i).debugId(), engines.at(i).name()); + + if (engines.count() > 0) { + m_engineSpinBox->setValue(engines.at(0).debugId()); + queryEngineContext(engines.at(0).debugId()); + } +} + +void QmlInspectorMode::queryEngineContext(int id) +{ + if (id < 0) + return; + + if (m_contextQuery) { + delete m_contextQuery; + m_contextQuery = 0; + } + + m_contextQuery = m_client->queryRootContexts(QmlDebugEngineReference(id), this); + if (!m_contextQuery->isWaiting()) + contextChanged(); + else + QObject::connect(m_contextQuery, SIGNAL(stateChanged(QmlDebugQuery::State)), + this, SLOT(contextChanged())); +} + +void QmlInspectorMode::contextChanged() +{ + //dump(m_contextQuery->rootContext(), 0); + + foreach (const QmlDebugObjectReference &object, m_contextQuery->rootContext().objects()) + m_objectTreeWidget->reload(object.debugId()); + + delete m_contextQuery; m_contextQuery = 0; +} + +void QmlInspectorMode::treeObjectActivated(const QmlDebugObjectReference &obj) +{ + QmlDebugFileReference source = obj.source(); + QString fileName = source.url().toLocalFile(); + + if (source.lineNumber() < 0 || !QFile::exists(fileName)) + return; + + Core::EditorManager *editorManager = Core::EditorManager::instance(); + TextEditor::ITextEditor *editor = qobject_cast<TextEditor::ITextEditor*>(editorManager->openEditor(fileName)); + if (editor) { + editorManager->ensureEditorManagerVisible(); + editorManager->addCurrentPositionToNavigationHistory(); + editor->gotoLine(source.lineNumber()); + editor->widget()->setFocus(); + } +} + +QT_END_NAMESPACE + +#include "qmlinspectormode.moc" + diff --git a/tools/qmldebugger/creatorplugin/qmlinspectormode.h b/tools/qmldebugger/creatorplugin/qmlinspectormode.h new file mode 100644 index 0000000..b4158f1 --- /dev/null +++ b/tools/qmldebugger/creatorplugin/qmlinspectormode.h @@ -0,0 +1,133 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt QML Debugger 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 QMLINSPECTORMODE_H +#define QMLINSPECTORMODE_H + +#include <QAction> + +#include <coreplugin/basemode.h> +#include <QtCore/QObject> + +QT_BEGIN_NAMESPACE + +class QToolButton; +class QLineEdit; +class QSpinBox; +class QLabel; + +class QmlEngineDebug; +class QmlDebugConnection; +class QmlDebugEnginesQuery; +class QmlDebugRootContextQuery; +class QmlDebugObjectReference; +class ObjectTree; +class WatchTableModel; +class WatchTableView; +class ObjectPropertiesView; +class CanvasFrameRate; +class ExpressionQueryWidget; +class EngineSpinBox; + + +class QmlInspectorMode : public Core::BaseMode +{ + Q_OBJECT + +public: + QmlInspectorMode(QObject *parent = 0); + + + quint16 viewerPort() const; + +signals: + void startViewer(); + void stopViewer(); + void statusMessage(const QString &text); + +public slots: + void connectToViewer(); // using host, port from widgets + void disconnectFromViewer(); + +private slots: + void connectionStateChanged(); + void connectionError(); + void reloadEngines(); + void enginesChanged(); + void queryEngineContext(int); + void contextChanged(); + void treeObjectActivated(const QmlDebugObjectReference &obj); + +private: + struct Actions { + QAction *startAction; + QAction *stopAction; + }; + + void initActions(); + QWidget *createModeWindow(); + QWidget *createMainView(); + void initWidgets(); + QWidget *createBottomWindow(); + QToolButton *createToolButton(QAction *action); + + Actions m_actions; + + QmlDebugConnection *m_conn; + QmlEngineDebug *m_client; + + QmlDebugEnginesQuery *m_engineQuery; + QmlDebugRootContextQuery *m_contextQuery; + + ObjectTree *m_objectTreeWidget; + ObjectPropertiesView *m_propertiesWidget; + WatchTableModel *m_watchTableModel; + WatchTableView *m_watchTableView; + CanvasFrameRate *m_frameRateWidget; + ExpressionQueryWidget *m_expressionWidget; + + QLineEdit *m_addressEdit; + QSpinBox *m_portSpinBox; + EngineSpinBox *m_engineSpinBox; +}; + +QT_END_NAMESPACE + +#endif diff --git a/tools/qmldebugger/creatorplugin/qmlinspectorplugin.cpp b/tools/qmldebugger/creatorplugin/qmlinspectorplugin.cpp new file mode 100644 index 0000000..cc42470 --- /dev/null +++ b/tools/qmldebugger/creatorplugin/qmlinspectorplugin.cpp @@ -0,0 +1,175 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt QML Debugger 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/QStringList> +#include <QtCore/QtPlugin> +#include <QtCore/QDebug> + +#include <coreplugin/icore.h> + +#include <projectexplorer/runconfiguration.h> +#include <projectexplorer/projectexplorer.h> +#include <projectexplorer/projectexplorerconstants.h> +#include <projectexplorer/project.h> + +#include <coreplugin/coreconstants.h> +#include <coreplugin/uniqueidmanager.h> + +#include <extensionsystem/pluginmanager.h> + +#include <private/qmldebug_p.h> +#include <private/qmldebugclient_p.h> + +#include "runcontrol.h" +#include "qmlinspector.h" +#include "qmlinspectormode.h" +#include "inspectoroutputpane.h" +#include "qmlinspectorplugin.h" + +QT_BEGIN_NAMESPACE + + +QmlInspectorPlugin::QmlInspectorPlugin() + : m_inspectMode(0), + m_runControl(0) +{ +} + +QmlInspectorPlugin::~QmlInspectorPlugin() +{ +} + +void QmlInspectorPlugin::shutdown() +{ + removeObject(m_inspectMode); + delete m_inspectMode; + m_inspectMode = 0; + + removeObject(m_outputPane); + delete m_outputPane; + m_outputPane = 0; +} + +bool QmlInspectorPlugin::initialize(const QStringList &arguments, QString *errorString) +{ + Q_UNUSED(arguments); + Q_UNUSED(errorString); + + Core::ICore *core = Core::ICore::instance(); + Core::UniqueIDManager *uidm = core->uniqueIDManager(); + + QList<int> context; + context.append(uidm->uniqueIdentifier(QmlInspector::Constants::C_INSPECTOR)); + context.append(uidm->uniqueIdentifier(Core::Constants::C_EDITORMANAGER)); + context.append(uidm->uniqueIdentifier(Core::Constants::C_NAVIGATION_PANE)); + + m_inspectMode = new QmlInspectorMode(this); + connect(m_inspectMode, SIGNAL(startViewer()), SLOT(startViewer())); + connect(m_inspectMode, SIGNAL(stopViewer()), SLOT(stopViewer())); + m_inspectMode->setContext(context); + addObject(m_inspectMode); + + m_outputPane = new InspectorOutputPane; + addObject(m_outputPane); + + connect(m_inspectMode, SIGNAL(statusMessage(QString)), + m_outputPane, SLOT(addInspectorStatus(QString))); + + m_runControlFactory = new QmlInspectorRunControlFactory(this); + addAutoReleasedObject(m_runControlFactory); + + return true; +} + +void QmlInspectorPlugin::extensionsInitialized() +{ +} + +void QmlInspectorPlugin::startViewer() +{ + stopViewer(); + + ProjectExplorer::Project *project = 0; + ProjectExplorer::ProjectExplorerPlugin *plugin = ProjectExplorer::ProjectExplorerPlugin::instance(); + if (plugin) + project = plugin->currentProject(); + if (!project) { + qDebug() << "No project loaded"; // TODO should this just run the debugger without a viewer? + return; + } + + QSharedPointer<ProjectExplorer::RunConfiguration> rc = project->activeRunConfiguration(); + + QmlInspector::StartParameters sp; + sp.port = m_inspectMode->viewerPort(); + + m_runControl = m_runControlFactory->create(rc, ProjectExplorer::Constants::RUNMODE, sp); + + if (m_runControl) { + connect(m_runControl, SIGNAL(started()), m_inspectMode, SLOT(connectToViewer())); + connect(m_runControl, SIGNAL(finished()), m_inspectMode, SLOT(disconnectFromViewer())); + + connect(m_runControl, SIGNAL(addToOutputWindow(RunControl*,QString)), + m_outputPane, SLOT(addOutput(RunControl*,QString))); + connect(m_runControl, SIGNAL(addToOutputWindowInline(RunControl*,QString)), + m_outputPane, SLOT(addOutputInline(RunControl*,QString))); + connect(m_runControl, SIGNAL(error(RunControl*,QString)), + m_outputPane, SLOT(addErrorOutput(RunControl*,QString))); + + m_runControl->start(); + m_outputPane->popup(false); + } + +} + +void QmlInspectorPlugin::stopViewer() +{ + if (m_runControl) { + m_runControl->stop(); + m_runControl->deleteLater(); + m_runControl = 0; + } +} + + +Q_EXPORT_PLUGIN(QmlInspectorPlugin) + +QT_END_NAMESPACE + diff --git a/tools/qmldebugger/creatorplugin/qmlinspectorplugin.h b/tools/qmldebugger/creatorplugin/qmlinspectorplugin.h new file mode 100644 index 0000000..57d9fb3 --- /dev/null +++ b/tools/qmldebugger/creatorplugin/qmlinspectorplugin.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt QML Debugger 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 QMLINSPECTORPLUGIN_H +#define QMLINSPECTORPLUGIN_H + +#include <extensionsystem/iplugin.h> + +#include <QtCore/QObject> +#include <QtCore/QPointer> + +QT_BEGIN_NAMESPACE + +class QStringList; + + +class QmlInspectorRunControlFactory; +class QmlInspectorMode; +class InspectorOutputPane; + +namespace ProjectExplorer +{ + class RunControl; +} + +class QmlInspectorPlugin : public ExtensionSystem::IPlugin +{ + Q_OBJECT + +public: + QmlInspectorPlugin(); + ~QmlInspectorPlugin(); + + virtual bool initialize(const QStringList &arguments, QString *errorString); + virtual void extensionsInitialized(); + virtual void shutdown(); + +private slots: + void startViewer(); + void stopViewer(); + +private: + QmlInspectorMode *m_inspectMode; + InspectorOutputPane *m_outputPane; + + QmlInspectorRunControlFactory *m_runControlFactory; + QPointer<ProjectExplorer::RunControl> m_runControl; +}; + + +QT_END_NAMESPACE + +#endif // QMLINSPECTORPLUGIN_H diff --git a/tools/qmldebugger/creatorplugin/runcontrol.cpp b/tools/qmldebugger/creatorplugin/runcontrol.cpp new file mode 100644 index 0000000..279f287 --- /dev/null +++ b/tools/qmldebugger/creatorplugin/runcontrol.cpp @@ -0,0 +1,175 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt QML Debugger 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/qtimer.h> + +#include <projectexplorer/applicationlauncher.h> +#include <projectexplorer/applicationrunconfiguration.h> +#include <projectexplorer/projectexplorerconstants.h> + +#include "runcontrol.h" + +using namespace ProjectExplorer; + + +QmlInspectorRunControlFactory::QmlInspectorRunControlFactory(QObject *parent) + : ProjectExplorer::IRunControlFactory(parent) +{ +} + +bool QmlInspectorRunControlFactory::canRun(const QSharedPointer<RunConfiguration> &runConfiguration, const QString &mode) const +{ + Q_UNUSED(runConfiguration); + if (mode != ProjectExplorer::Constants::RUNMODE) + return false; + return true; +} + +ProjectExplorer::RunControl *QmlInspectorRunControlFactory::create(const QSharedPointer<RunConfiguration> &runConfiguration, const QString &mode) +{ + Q_UNUSED(mode); + return new QmlInspectorRunControl(runConfiguration); +} + +ProjectExplorer::RunControl *QmlInspectorRunControlFactory::create(const QSharedPointer<ProjectExplorer::RunConfiguration> &runConfiguration, +const QString &mode, const QmlInspector::StartParameters &sp) +{ + Q_UNUSED(mode); + return new QmlInspectorRunControl(runConfiguration, sp); +} + +QString QmlInspectorRunControlFactory::displayName() const +{ + return tr("Qml Inspector"); +} + +QWidget *QmlInspectorRunControlFactory::configurationWidget(const QSharedPointer<RunConfiguration> &runConfiguration) +{ + Q_UNUSED(runConfiguration); + return 0; +} + + + +QmlInspectorRunControl::QmlInspectorRunControl(const QSharedPointer<ProjectExplorer::RunConfiguration> &runConfiguration, +const QmlInspector::StartParameters &sp) + : ProjectExplorer::RunControl(runConfiguration), + m_running(false), + m_viewerLauncher(0), + m_startParams(sp) +{ +} + +QmlInspectorRunControl::~QmlInspectorRunControl() +{ +} + +void QmlInspectorRunControl::start() +{ + if (m_running || m_viewerLauncher) + return; + + m_viewerLauncher = new ProjectExplorer::ApplicationLauncher(this); + connect(m_viewerLauncher, SIGNAL(applicationError(QString)), SLOT(applicationError(QString))); + connect(m_viewerLauncher, SIGNAL(processExited(int)), SLOT(viewerExited())); + connect(m_viewerLauncher, SIGNAL(appendOutput(QString)), SLOT(appendOutput(QString))); + connect(m_viewerLauncher, SIGNAL(bringToForegroundRequested(qint64)), + this, SLOT(appStarted())); + + QSharedPointer<LocalApplicationRunConfiguration> rc = + runConfiguration().objectCast<LocalApplicationRunConfiguration>(); + if (rc.isNull()) { // TODO + return; + } + + ProjectExplorer::Environment env = rc->environment(); + env.set("QML_DEBUG_SERVER_PORT", QString::number(m_startParams.port)); + + QStringList arguments = rc->commandLineArguments(); + arguments << QLatin1String("-stayontop"); + + m_viewerLauncher->setEnvironment(env.toStringList()); + m_viewerLauncher->setWorkingDirectory(rc->workingDirectory()); + + m_running = true; + + m_viewerLauncher->start(static_cast<ApplicationLauncher::Mode>(rc->runMode()), + rc->executable(), arguments); +} + +void QmlInspectorRunControl::stop() +{ + if (m_viewerLauncher->isRunning()) + m_viewerLauncher->stop(); +} + +bool QmlInspectorRunControl::isRunning() const +{ + return m_running; +} + +void QmlInspectorRunControl::appStarted() +{ + QTimer::singleShot(500, this, SLOT(delayedStart())); +} + +void QmlInspectorRunControl::appendOutput(const QString &s) +{ + emit addToOutputWindow(this, s); +} + +void QmlInspectorRunControl::delayedStart() +{ + emit started(); +} + +void QmlInspectorRunControl::viewerExited() +{ + m_running = false; + emit finished(); + + deleteLater(); +} + +void QmlInspectorRunControl::applicationError(const QString &s) +{ + emit error(this, s); +} diff --git a/tools/qmldebugger/creatorplugin/runcontrol.h b/tools/qmldebugger/creatorplugin/runcontrol.h new file mode 100644 index 0000000..312fe56 --- /dev/null +++ b/tools/qmldebugger/creatorplugin/runcontrol.h @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt QML Debugger 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 QMLINSPECTORRUNCONTROL_H +#define QMLINSPECTORRUNCONTROL_H + +#include <QtCore/qobject.h> + +#include <projectexplorer/runconfiguration.h> + +#include "qmlinspector.h" + +namespace ProjectExplorer { + class ApplicationLauncher; +} + +class QmlInspectorRunControlFactory : public ProjectExplorer::IRunControlFactory +{ + Q_OBJECT + +public: + explicit QmlInspectorRunControlFactory(QObject *parent); + + virtual bool canRun( + const QSharedPointer<ProjectExplorer::RunConfiguration> &runConfiguration, + const QString &mode) const; + + virtual ProjectExplorer::RunControl *create( + const QSharedPointer<ProjectExplorer::RunConfiguration> &runConfiguration, + const QString &mode); + + ProjectExplorer::RunControl *create( + const QSharedPointer<ProjectExplorer::RunConfiguration> &runConfiguration, + const QString &mode, + const QmlInspector::StartParameters &sp); + + virtual QString displayName() const; + + virtual QWidget *configurationWidget(const QSharedPointer<ProjectExplorer::RunConfiguration> &runConfiguration); +}; + +class QmlInspectorRunControl : public ProjectExplorer::RunControl +{ + Q_OBJECT + +public: + explicit QmlInspectorRunControl(const QSharedPointer<ProjectExplorer::RunConfiguration> &runConfiguration, + const QmlInspector::StartParameters &sp = QmlInspector::StartParameters()); + ~QmlInspectorRunControl(); + + virtual void start(); + virtual void stop(); + virtual bool isRunning() const; + +private slots: + void appendOutput(const QString &s); + void appStarted(); + void delayedStart(); + void viewerExited(); + void applicationError(const QString &error); + +private: + bool m_running; + ProjectExplorer::ApplicationLauncher *m_viewerLauncher; + QmlInspector::StartParameters m_startParams; +}; + +#endif diff --git a/tools/qmldebugger/qmldebugger.pro b/tools/qmldebugger/qmldebugger.pro new file mode 100644 index 0000000..2dd353a --- /dev/null +++ b/tools/qmldebugger/qmldebugger.pro @@ -0,0 +1,10 @@ +TEMPLATE = subdirs +CONFIG += ordered + +SUBDIRS = standalone + +CREATOR_SRC = $$(CREATOR_SRC_DIR) +CREATOR_BUILD = $$(CREATOR_BUILD_DIR) +!isEmpty(CREATOR_SRC):!isEmpty(CREATOR_BUILD) { + SUBDIRS += creatorplugin +} diff --git a/tools/qmldebugger/standalone/canvasframerate.cpp b/tools/qmldebugger/standalone/canvasframerate.cpp new file mode 100644 index 0000000..d956029 --- /dev/null +++ b/tools/qmldebugger/standalone/canvasframerate.cpp @@ -0,0 +1,581 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt QML Debugger 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/qstringlist.h> +#include <QtCore/qdatastream.h> +#include <QtCore/qmargins.h> + +#include <QtGui/qapplication.h> +#include <QtGui/qpainter.h> +#include <QtGui/qtooltip.h> +#include <QtGui/qslider.h> +#include <QtGui/qscrollbar.h> +#include <QtGui/qspinbox.h> +#include <QtGui/qgroupbox.h> +#include <QtGui/qboxlayout.h> +#include <QtGui/qlabel.h> +#include <QtGui/qlineedit.h> +#include <QtGui/qpushbutton.h> +#include <QtGui/qtabwidget.h> + +#include <QResizeEvent> +#include <QShowEvent> + +#include <private/qmldebugclient_p.h> +#include "canvasframerate.h" + +QT_BEGIN_NAMESPACE + +class QLineGraph : public QWidget +{ +Q_OBJECT +public: + QLineGraph(QAbstractSlider *slider, QWidget * = 0); + + void setPosition(int); + +public slots: + void addSample(int, int, int, bool); + void setResolutionForHeight(int); + void clear(); + +protected: + virtual void paintEvent(QPaintEvent *); + virtual void mouseMoveEvent(QMouseEvent *); + virtual void leaveEvent(QEvent *); + virtual void wheelEvent(QWheelEvent *event); + +private slots: + void sliderChanged(int); + +private: + void updateSlider(); + void drawSample(QPainter *, int, const QRect &, QList<QRect> *); + void drawTime(QPainter *, const QRect &); + QRect findContainingRect(const QList<QRect> &rects, const QPoint &pos) const; + struct Sample { + int sample[3]; + bool isBreak; + }; + QList<Sample> _samples; + + QAbstractSlider *slider; + int position; + int samplesPerWidth; + int resolutionForHeight; + bool ignoreScroll; + QMargins graphMargins; + + QList<QRect> rectsPaintTime; // time to do a paintEvent() + QList<QRect> rectsTimeBetween; // time between frames + QRect highlightedBar; +}; + +QLineGraph::QLineGraph(QAbstractSlider *slider, QWidget *parent) +: QWidget(parent), slider(slider), position(-1), samplesPerWidth(99), resolutionForHeight(50), + ignoreScroll(false), graphMargins(65, 10, 71, 35) +{ + setMouseTracking(true); + + slider->setMaximum(0); + slider->setMinimum(0); + slider->setSingleStep(1); + + connect(slider, SIGNAL(valueChanged(int)), this, SLOT(sliderChanged(int))); +} + +void QLineGraph::sliderChanged(int v) +{ + if(ignoreScroll) + return; + + if (v == slider->maximum()) + position = -1; + else + position = v; + + update(); + + // update highlightedRect + QPoint pos = mapFromGlobal(QCursor::pos()); + if (geometry().contains(pos)) { + QMouseEvent *me = new QMouseEvent(QEvent::MouseMove, pos, + Qt::NoButton, Qt::NoButton, Qt::NoModifier); + QApplication::postEvent(this, me); + } +} + +void QLineGraph::clear() +{ + _samples.clear(); + rectsPaintTime.clear(); + rectsTimeBetween.clear(); + highlightedBar = QRect(); + position = -1; + + updateSlider(); + update(); +} + +void QLineGraph::updateSlider() +{ + ignoreScroll = true; + slider->setMaximum(qMax(0, _samples.count() - samplesPerWidth - 1)); + + if(position == -1) { + slider->setValue(slider->maximum()); + } else { + slider->setValue(position); + } + ignoreScroll = false; +} + +void QLineGraph::addSample(int a, int b, int d, bool isBreak) +{ + Sample s; + s.isBreak = isBreak; + s.sample[0] = a; + s.sample[1] = b; + s.sample[2] = d; + _samples << s; + updateSlider(); + update(); +} + +void QLineGraph::setPosition(int p) +{ + sliderChanged(p); +} + +void QLineGraph::drawTime(QPainter *p, const QRect &rect) +{ + if(_samples.isEmpty()) + return; + + int first = position; + if(first == -1) + first = qMax(0, _samples.count() - samplesPerWidth - 1); + int last = qMin(_samples.count() - 1, first + samplesPerWidth); + + qreal scaleX = qreal(rect.width()) / qreal(samplesPerWidth); + + int t = 0; + + for(int ii = first; ii <= last; ++ii) { + int sampleTime = _samples.at(ii).sample[2] / 1000; + if(sampleTime != t) { + + int xEnd = rect.left() + scaleX * (ii - first); + p->drawLine(xEnd, rect.bottom(), xEnd, rect.bottom() + 7); + + QRect text(xEnd - 30, rect.bottom() + 10, 60, 30); + + p->drawText(text, Qt::AlignHCenter | Qt::AlignTop, QString::number(_samples.at(ii).sample[2])); + + t = sampleTime; + } + } + +} + +void QLineGraph::drawSample(QPainter *p, int s, const QRect &rect, QList<QRect> *record) +{ + if(_samples.isEmpty()) + return; + + int first = position; + if(first == -1) + first = qMax(0, _samples.count() - samplesPerWidth - 1); + int last = qMin(_samples.count() - 1, first + samplesPerWidth); + + qreal scaleY = qreal(rect.height()) / resolutionForHeight; + qreal scaleX = qreal(rect.width()) / qreal(samplesPerWidth); + + int xEnd; + int lastXEnd = rect.left(); + + p->save(); + p->setPen(Qt::NoPen); + for(int ii = first + 1; ii <= last; ++ii) { + + xEnd = rect.left() + scaleX * (ii - first); + int yEnd = rect.bottom() - _samples.at(ii).sample[s] * scaleY; + + if (!(s == 0 && _samples.at(ii).isBreak)) { + QRect bar(lastXEnd, yEnd, scaleX, _samples.at(ii).sample[s] * scaleY); + record->append(bar); + p->drawRect(bar); + } + + lastXEnd = xEnd; + } + p->restore(); +} + +void QLineGraph::paintEvent(QPaintEvent *) +{ + QPainter p(this); + p.setRenderHint(QPainter::Antialiasing); + + QRect r(graphMargins.left(), graphMargins.top(), + width() - graphMargins.right(), height() - graphMargins.bottom()); + + p.save(); + p.rotate(-90); + p.translate(-r.height()/2 - r.width()/2 - graphMargins.right(), -r.height()/2); + p.drawText(r, Qt::AlignCenter, tr("Frame rate")); + p.restore(); + + p.setBrush(QColor("lightsteelblue")); + rectsTimeBetween.clear(); + drawSample(&p, 0, r, &rectsTimeBetween); + + p.setBrush(QColor("pink")); + rectsPaintTime.clear(); + drawSample(&p, 1, r, &rectsPaintTime); + + if (!highlightedBar.isNull()) { + p.setBrush(Qt::darkGreen); + p.drawRect(highlightedBar); + } + + p.setBrush(Qt::NoBrush); + p.drawRect(r); + + slider->setGeometry(x() + r.x(), slider->y(), r.width(), slider->height()); + + for(int ii = 0; ii <= resolutionForHeight; ++ii) { + int y = 1 + r.bottom() - ii * r.height() / resolutionForHeight; + + if((ii % 10) == 0) { + p.drawLine(r.left() - 20, y, r.left(), y); + QRect text(r.left() - 20 - 53, y - 10, 50, 20); + p.drawText(text, Qt::AlignRight | Qt::AlignVCenter, QString::number(ii)); + } else { + p.drawLine(r.left() - 7, y, r.left(), y); + } + } + + drawTime(&p, r); +} + +void QLineGraph::mouseMoveEvent(QMouseEvent *event) +{ + QPoint pos = event->pos(); + + QRect rect = findContainingRect(rectsPaintTime, pos); + if (rect.isNull()) + rect = findContainingRect(rectsTimeBetween, pos); + + if (!highlightedBar.isNull()) + update(highlightedBar.adjusted(-1, -1, 1, 1)); + highlightedBar = rect; + + if (!rect.isNull()) { + QRect graph(graphMargins.left(), graphMargins.top(), + width() - graphMargins.right(), height() - graphMargins.bottom()); + qreal scaleY = qreal(graph.height()) / resolutionForHeight; + QToolTip::showText(event->globalPos(), QString::number(qRound(rect.height() / scaleY)), this, rect); + update(rect.adjusted(-1, -1, 1, 1)); + } +} + +void QLineGraph::leaveEvent(QEvent *) +{ + if (!highlightedBar.isNull()) { + QRect bar = highlightedBar.adjusted(-1, -1, 1, 1); + highlightedBar = QRect(); + update(bar); + } +} + +void QLineGraph::wheelEvent(QWheelEvent *event) +{ + QWheelEvent we(QPoint(0,0), event->delta(), event->buttons(), event->modifiers(), event->orientation()); + QApplication::sendEvent(slider, &we); +} + +void QLineGraph::setResolutionForHeight(int resolution) +{ + resolutionForHeight = resolution; + update(); +} + +QRect QLineGraph::findContainingRect(const QList<QRect> &rects, const QPoint &pos) const +{ + for (int i=0; i<rects.count(); i++) { + if (rects[i].contains(pos)) + return rects[i]; + } + return QRect(); +} + + +class GraphWindow : public QWidget +{ + Q_OBJECT +public: + GraphWindow(QWidget *parent = 0); + + virtual QSize sizeHint() const; + +public slots: + void addSample(int, int, int, bool); + void setResolutionForHeight(int); + void clear(); + +private: + QLineGraph *m_graph; +}; + +GraphWindow::GraphWindow(QWidget *parent) + : QWidget(parent) +{ + QSlider *scroll = new QSlider(Qt::Horizontal); + scroll->setFocusPolicy(Qt::WheelFocus); + m_graph = new QLineGraph(scroll); + + setFocusPolicy(Qt::WheelFocus); + setFocusProxy(scroll); + + QVBoxLayout *layout = new QVBoxLayout(this); + layout->setContentsMargins(0, 0, 5, 0); + layout->setSpacing(0); + layout->addWidget(m_graph, 2); + layout->addWidget(new QLabel(tr("Total time elapsed (ms)")), 0, Qt::AlignHCenter); + layout->addWidget(scroll); +} + +void GraphWindow::addSample(int a, int b, int d, bool isBreak) +{ + m_graph->addSample(a, b, d, isBreak); +} + +void GraphWindow::setResolutionForHeight(int res) +{ + m_graph->setResolutionForHeight(res); +} + +void GraphWindow::clear() +{ + m_graph->clear(); +} + +QSize GraphWindow::sizeHint() const +{ + return QSize(400, 220); +} + + +class CanvasFrameRatePlugin : public QmlDebugClient +{ + Q_OBJECT +public: + CanvasFrameRatePlugin(QmlDebugConnection *client); + +signals: + void sample(int, int, int, bool); + +protected: + virtual void messageReceived(const QByteArray &); + +private: + int lb; + int ld; +}; + +CanvasFrameRatePlugin::CanvasFrameRatePlugin(QmlDebugConnection *client) +: QmlDebugClient(QLatin1String("CanvasFrameRate"), client), lb(-1) +{ +} + +void CanvasFrameRatePlugin::messageReceived(const QByteArray &data) +{ + QByteArray rwData = data; + QDataStream stream(&rwData, QIODevice::ReadOnly); + + int b; int c; int d; bool isBreak; + stream >> b >> c >> d >> isBreak; + + if (lb != -1) + emit sample(c, lb, ld, isBreak); + + lb = b; + ld = d; +} + +CanvasFrameRate::CanvasFrameRate(QWidget *parent) +: QWidget(parent), + m_plugin(0) +{ + m_tabs = new QTabWidget(this); + + QHBoxLayout *bottom = new QHBoxLayout; + bottom->setMargin(0); + bottom->setSpacing(10); + + m_res = new QSpinBox; + m_res->setRange(30, 200); + m_res->setValue(m_res->minimum()); + m_res->setSingleStep(10); + m_res->setSuffix(QLatin1String("ms")); + bottom->addWidget(new QLabel(tr("Resolution:"))); + bottom->addWidget(m_res); + + bottom->addStretch(); + + m_clearButton = new QPushButton(tr("Clear")); + connect(m_clearButton, SIGNAL(clicked()), SLOT(clearGraph())); + bottom->addWidget(m_clearButton); + + QPushButton *pb = new QPushButton(tr("New Graph"), this); + connect(pb, SIGNAL(clicked()), this, SLOT(newTab())); + bottom->addWidget(pb); + + m_group = new QGroupBox(tr("Enabled")); + m_group->setCheckable(true); + m_group->setChecked(false); + connect(m_group, SIGNAL(toggled(bool)), SLOT(enabledToggled(bool))); + + QVBoxLayout *groupLayout = new QVBoxLayout(m_group); + groupLayout->setContentsMargins(5, 0, 5, 0); + groupLayout->setSpacing(2); + groupLayout->addWidget(m_tabs); + groupLayout->addLayout(bottom); + + QVBoxLayout *layout = new QVBoxLayout; + layout->setContentsMargins(0, 10, 0, 0); + layout->setSpacing(0); + layout->addWidget(m_group); + setLayout(layout); +} + +void CanvasFrameRate::reset(QmlDebugConnection *conn) +{ + delete m_plugin; + m_plugin = 0; + + QWidget *w; + for (int i=0; i<m_tabs->count(); i++) { + w = m_tabs->widget(i); + m_tabs->removeTab(i); + delete w; + } + + if (conn) { + connect(conn, SIGNAL(stateChanged(QAbstractSocket::SocketState)), + SLOT(connectionStateChanged(QAbstractSocket::SocketState))); + if (conn->state() == QAbstractSocket::ConnectedState) + handleConnected(conn); + } +} + +void CanvasFrameRate::connectionStateChanged(QAbstractSocket::SocketState state) +{ + if (state == QAbstractSocket::UnconnectedState) { + delete m_plugin; + m_plugin = 0; + } else if (state == QAbstractSocket::ConnectedState) { + handleConnected(qobject_cast<QmlDebugConnection*>(sender())); + } +} + +void CanvasFrameRate::handleConnected(QmlDebugConnection *conn) +{ + delete m_plugin; + m_plugin = new CanvasFrameRatePlugin(conn); + enabledToggled(m_group->isChecked()); + newTab(); +} + +void CanvasFrameRate::setSizeHint(const QSize &size) +{ + m_sizeHint = size; +} + +QSize CanvasFrameRate::sizeHint() const +{ + return m_sizeHint; +} + +void CanvasFrameRate::clearGraph() +{ + if (m_tabs->count()) { + GraphWindow *w = qobject_cast<GraphWindow*>(m_tabs->currentWidget()); + if (w) + w->clear(); + } +} + +void CanvasFrameRate::newTab() +{ + if (!m_plugin) + return; + + if (m_tabs->count()) { + QWidget *w = m_tabs->widget(m_tabs->count() - 1); + QObject::disconnect(m_plugin, SIGNAL(sample(int,int,int,bool)), + w, SLOT(addSample(int,int,int,bool))); + } + + int count = m_tabs->count(); + + GraphWindow *graph = new GraphWindow; + graph->setResolutionForHeight(m_res->value()); + connect(m_plugin, SIGNAL(sample(int,int,int,bool)), + graph, SLOT(addSample(int,int,int,bool))); + connect(m_res, SIGNAL(valueChanged(int)), + graph, SLOT(setResolutionForHeight(int))); + + QString name = QLatin1String("Graph ") + QString::number(count + 1); + m_tabs->addTab(graph, name); + m_tabs->setCurrentIndex(count); +} + +void CanvasFrameRate::enabledToggled(bool checked) +{ + if (m_plugin) + static_cast<QmlDebugClient *>(m_plugin)->setEnabled(checked); +} + +QT_END_NAMESPACE + +#include "canvasframerate.moc" diff --git a/tools/qmldebugger/standalone/canvasframerate.h b/tools/qmldebugger/standalone/canvasframerate.h new file mode 100644 index 0000000..f8eec59 --- /dev/null +++ b/tools/qmldebugger/standalone/canvasframerate.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt QML Debugger 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 CANVASFRAMERATE_H +#define CANVASFRAMERATE_H + +#include <QtCore/qpointer.h> +#include <QtGui/qwidget.h> + +#include <private/qmldebugclient_p.h> + +QT_BEGIN_NAMESPACE + +class QTabWidget; +class QSlider; +class QGroupBox; +class QLabel; +class QSpinBox; +class QPushButton; + +class CanvasFrameRatePlugin; + +class CanvasFrameRate : public QWidget +{ + Q_OBJECT +public: + CanvasFrameRate(QWidget *parent = 0); + + void reset(QmlDebugConnection *conn); + + void setSizeHint(const QSize &); + virtual QSize sizeHint() const; + +private slots: + void clearGraph(); + void newTab(); + void enabledToggled(bool); + void connectionStateChanged(QAbstractSocket::SocketState state); + +private: + void handleConnected(QmlDebugConnection *conn); + + QGroupBox *m_group; + QTabWidget *m_tabs; + QSpinBox *m_res; + QPushButton *m_clearButton; + CanvasFrameRatePlugin *m_plugin; + QSize m_sizeHint; +}; + +QT_END_NAMESPACE + +#endif // CANVASFRAMERATE_H + diff --git a/tools/qmldebugger/standalone/engine.cpp b/tools/qmldebugger/standalone/engine.cpp new file mode 100644 index 0000000..6cfd82b --- /dev/null +++ b/tools/qmldebugger/standalone/engine.cpp @@ -0,0 +1,219 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt QML Debugger 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 <QVBoxLayout> +#include <QHBoxLayout> +#include <QSplitter> +#include <QTabWidget> +#include <QFile> + +#include <private/qmlenginedebug_p.h> +#include <private/qmldebugclient_p.h> +#include <QtDeclarative/qmlcomponent.h> +#include <QtDeclarative/qmlgraphicsitem.h> +#include <private/qmldebugservice_p.h> + +#include "engine.h" +#include "objectpropertiesview.h" +#include "expressionquerywidget.h" +#include "objecttree.h" +#include "watchtable.h" + +QT_BEGIN_NAMESPACE + + +class DebuggerEngineItem : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString name READ name CONSTANT); + Q_PROPERTY(int engineId READ engineId CONSTANT); + +public: + DebuggerEngineItem(const QString &name, int id) + : m_name(name), m_engineId(id) {} + + QString name() const { return m_name; } + int engineId() const { return m_engineId; } + +private: + QString m_name; + int m_engineId; +}; + +EnginePane::EnginePane(QmlDebugConnection *conn, QWidget *parent) +: QWidget(parent), m_client(new QmlEngineDebug(conn, this)), m_engines(0), m_context(0), m_watchTableModel(0), m_exprQueryWidget(0) +{ + QVBoxLayout *layout = new QVBoxLayout(this); + layout->setContentsMargins(0, 0, 0, 0); + + QFile enginesFile(":/engines.qml"); + enginesFile.open(QFile::ReadOnly); + Q_ASSERT(enginesFile.isOpen()); + + m_engineView = new QmlView(this); + m_engineView->rootContext()->setContextProperty("engines", qVariantFromValue(&m_engineItems)); + m_engineView->setContentResizable(true); + m_engineView->setQml(enginesFile.readAll()); + m_engineView->execute(); + m_engineView->setFixedHeight(100); + QObject::connect(m_engineView->root(), SIGNAL(engineClicked(int)), + this, SLOT(engineSelected(int))); + QObject::connect(m_engineView->root(), SIGNAL(refreshEngines()), + this, SLOT(refreshEngines())); + + m_engineView->setVisible(false); + layout->addWidget(m_engineView); + + QSplitter *splitter = new QSplitter; + + m_objTree = new ObjectTree(m_client, this); + m_propertiesView = new ObjectPropertiesView(m_client); + m_watchTableModel = new WatchTableModel(m_client, this); + + m_watchTableView = new WatchTableView(m_watchTableModel); + m_watchTableView->setModel(m_watchTableModel); + WatchTableHeaderView *header = new WatchTableHeaderView(m_watchTableModel); + m_watchTableView->setHorizontalHeader(header); + + connect(m_objTree, SIGNAL(currentObjectChanged(QmlDebugObjectReference)), + m_propertiesView, SLOT(reload(QmlDebugObjectReference))); + connect(m_objTree, SIGNAL(expressionWatchRequested(QmlDebugObjectReference,QString)), + m_watchTableModel, SLOT(expressionWatchRequested(QmlDebugObjectReference,QString))); + + connect(m_propertiesView, SIGNAL(activated(QmlDebugObjectReference,QmlDebugPropertyReference)), + m_watchTableModel, SLOT(togglePropertyWatch(QmlDebugObjectReference,QmlDebugPropertyReference))); + + connect(m_watchTableModel, SIGNAL(watchCreated(QmlDebugWatch*)), + m_propertiesView, SLOT(watchCreated(QmlDebugWatch*))); + + connect(m_watchTableView, SIGNAL(objectActivated(int)), + m_objTree, SLOT(setCurrentObject(int))); + + m_exprQueryWidget = new ExpressionQueryWidget(ExpressionQueryWidget::SeparateEntryMode, m_client); + connect(m_objTree, SIGNAL(currentObjectChanged(QmlDebugObjectReference)), + m_exprQueryWidget, SLOT(setCurrentObject(QmlDebugObjectReference))); + + QSplitter *propertiesTab = new QSplitter(Qt::Vertical); + propertiesTab->addWidget(m_propertiesView); + propertiesTab->addWidget(m_exprQueryWidget); + propertiesTab->setStretchFactor(0, 2); + propertiesTab->setStretchFactor(1, 1); + + m_tabs = new QTabWidget(this); + m_tabs->addTab(propertiesTab, tr("Properties")); + m_tabs->addTab(m_watchTableView, tr("Watched")); + + splitter->addWidget(m_objTree); + splitter->addWidget(m_tabs); + splitter->setStretchFactor(1, 2); + layout->addWidget(splitter); +} + +void EnginePane::engineSelected(int id) +{ + qWarning() << "Engine selected" << id; + queryContext(id); +} + +void EnginePane::queryContext(int id) +{ + if (m_context) { + delete m_context; + m_context = 0; + } + + m_context = m_client->queryRootContexts(QmlDebugEngineReference(id), this); + if (!m_context->isWaiting()) + contextChanged(); + else + QObject::connect(m_context, SIGNAL(stateChanged(QmlDebugQuery::State)), + this, SLOT(contextChanged())); +} + +void EnginePane::contextChanged() +{ + //dump(m_context->rootContext(), 0); + + foreach (const QmlDebugObjectReference &object, m_context->rootContext().objects()) + m_objTree->reload(object.debugId()); + + delete m_context; m_context = 0; +} + +void EnginePane::refreshEngines() +{ + if (m_engines) + return; + + m_engines = m_client->queryAvailableEngines(this); + if (!m_engines->isWaiting()) + enginesChanged(); + else + QObject::connect(m_engines, SIGNAL(stateChanged(QmlDebugQuery::State)), + this, SLOT(enginesChanged())); +} + +void EnginePane::enginesChanged() +{ + qDeleteAll(m_engineItems); + m_engineItems.clear(); + + QList<QmlDebugEngineReference> engines = m_engines->engines(); + delete m_engines; m_engines = 0; + + if (engines.isEmpty()) + qWarning("qmldebugger: no engines found!"); + + for (int ii = 0; ii < engines.count(); ++ii) + m_engineItems << new DebuggerEngineItem(engines.at(ii).name(), + engines.at(ii).debugId()); + + m_engineView->rootContext()->setContextProperty("engines", qVariantFromValue(&m_engineItems)); + + m_engineView->setVisible(m_engineItems.count() > 1); + if (m_engineItems.count() == 1) + engineSelected(qobject_cast<DebuggerEngineItem*>(m_engineItems.at(0))->engineId()); +} + + +#include "engine.moc" + +QT_END_NAMESPACE + diff --git a/tools/qmldebugger/standalone/engine.h b/tools/qmldebugger/standalone/engine.h new file mode 100644 index 0000000..f4c4275 --- /dev/null +++ b/tools/qmldebugger/standalone/engine.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt QML Debugger 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 ENGINE_H +#define ENGINE_H + +#include <QWidget> +#include <QtCore/qpointer.h> +#include <QtDeclarative/qmlengine.h> +#include <QtDeclarative/qmlcontext.h> +#include <QtDeclarative/qmlview.h> +#include <private/qmldebug_p.h> + +QT_BEGIN_NAMESPACE + +class ObjectPropertiesView; +class QmlDebugConnection; +class QmlDebugPropertyReference; +class QmlDebugWatch; +class ObjectTree; +class WatchTableModel; +class WatchTableView; +class ExpressionQueryWidget; + +class QTabWidget; + +class EnginePane : public QWidget +{ +Q_OBJECT +public: + EnginePane(QmlDebugConnection *, QWidget *parent = 0); + +public slots: + void refreshEngines(); + +private slots: + void enginesChanged(); + + void queryContext(int); + void contextChanged(); + + void engineSelected(int); + +private: + QmlEngineDebug *m_client; + QmlDebugEnginesQuery *m_engines; + QmlDebugRootContextQuery *m_context; + + ObjectTree *m_objTree; + QTabWidget *m_tabs; + WatchTableView *m_watchTableView; + WatchTableModel *m_watchTableModel; + ExpressionQueryWidget *m_exprQueryWidget; + + QmlView *m_engineView; + QList<QObject *> m_engineItems; + + ObjectPropertiesView *m_propertiesView; +}; + +QT_END_NAMESPACE + +#endif // ENGINE_H + diff --git a/tools/qmldebugger/standalone/engine.png b/tools/qmldebugger/standalone/engine.png Binary files differnew file mode 100644 index 0000000..a0a8a04 --- /dev/null +++ b/tools/qmldebugger/standalone/engine.png diff --git a/tools/qmldebugger/standalone/engines.qml b/tools/qmldebugger/standalone/engines.qml new file mode 100644 index 0000000..1e9335b --- /dev/null +++ b/tools/qmldebugger/standalone/engines.qml @@ -0,0 +1,46 @@ +import Qt 4.6 + +Item { + height: 100 + id: Root + signal engineClicked(int id) + signal refreshEngines() + + Row { + anchors.fill: parent + Repeater { + model: engines + Item { + width: 100; height: 100; + Image { + id: EngineIcon; + source: "qrc:/engine.png" + anchors.horizontalCenter: parent.horizontalCenter + } + Text { + anchors.top: EngineIcon.bottom; + text: modelData.name + "(" + modelData.engineId + ")" + anchors.horizontalCenter: parent.horizontalCenter + } + MouseRegion { + anchors.fill: parent + onClicked: Root.engineClicked(modelData.engineId); + } + } + } + } + + + Image { + y: 15 + source: "qrc:/refresh.png"; + width: 75; + height: 63; + smooth: true + anchors.right: parent.right + MouseRegion { + anchors.fill: parent + onClicked: Root.refreshEngines() + } + } +} diff --git a/tools/qmldebugger/standalone/expressionquerywidget.cpp b/tools/qmldebugger/standalone/expressionquerywidget.cpp new file mode 100644 index 0000000..cd59871 --- /dev/null +++ b/tools/qmldebugger/standalone/expressionquerywidget.cpp @@ -0,0 +1,276 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt QML Debugger 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 <QtGui/qlabel.h> +#include <QtGui/qtextedit.h> +#include <QtGui/qlineedit.h> +#include <QtGui/qpushbutton.h> +#include <QtGui/qevent.h> +#include <QtGui/qgroupbox.h> +#include <QtGui/qtextobject.h> +#include <QtGui/qlayout.h> + +#include "expressionquerywidget.h" + +ExpressionQueryWidget::ExpressionQueryWidget(Mode mode, QmlEngineDebug *client, QWidget *parent) + : QWidget(parent), + m_mode(mode), + m_client(client), + m_query(0), + m_textEdit(new QTextEdit), + m_lineEdit(0) +{ + m_prompt = QLatin1String(">> "); + + QVBoxLayout *layout = new QVBoxLayout(this); + layout->setMargin(0); + layout->setSpacing(0); + layout->addWidget(m_textEdit); + + updateTitle(); + + if (m_mode == SeparateEntryMode) { + m_lineEdit = new QLineEdit; + connect(m_lineEdit, SIGNAL(returnPressed()), SLOT(executeExpression())); + QHBoxLayout *hbox = new QHBoxLayout; + hbox->setMargin(5); + hbox->setSpacing(5); + hbox->addWidget(new QLabel(tr("Expression:"))); + hbox->addWidget(m_lineEdit); + layout->addLayout(hbox); + + m_textEdit->setReadOnly(true); + m_lineEdit->installEventFilter(this); + } else { + m_textEdit->installEventFilter(this); + appendPrompt(); + } +} + +void ExpressionQueryWidget::setEngineDebug(QmlEngineDebug *client) +{ + m_client = client; +} + +void ExpressionQueryWidget::clear() +{ + m_textEdit->clear(); + if (m_lineEdit) + m_lineEdit->clear(); + if (m_mode == ShellMode) + appendPrompt(); +} + +void ExpressionQueryWidget::updateTitle() +{ + if (m_currObject.debugId() < 0) { + m_title = tr("Expression queries"); + } else { + QString desc = QLatin1String("<") + + m_currObject.className() + QLatin1String(": ") + + (m_currObject.name().isEmpty() ? QLatin1String("<unnamed>") : m_currObject.name()) + + QLatin1String(">"); + m_title = tr("Expression queries (using context for %1)" , "Selected object").arg(desc); + } +} + +void ExpressionQueryWidget::appendPrompt() +{ + m_textEdit->moveCursor(QTextCursor::End); + + if (m_mode == SeparateEntryMode) { + m_textEdit->insertPlainText("\n"); + } else { + m_textEdit->setTextColor(Qt::gray); + m_textEdit->append(m_prompt); + } +} + +void ExpressionQueryWidget::setCurrentObject(const QmlDebugObjectReference &obj) +{ + m_currObject = obj; + updateTitle(); +} + +void ExpressionQueryWidget::checkCurrentContext() +{ + m_textEdit->moveCursor(QTextCursor::End); + + if (m_currObject.debugId() != -1 && m_currObject.debugId() != m_objectAtLastFocus.debugId()) + showCurrentContext(); + m_objectAtLastFocus = m_currObject; +} + +void ExpressionQueryWidget::showCurrentContext() +{ + if (m_mode == ShellMode) { + // clear the initial prompt + if (m_textEdit->document()->lineCount() == 1) + m_textEdit->clear(); + } + + m_textEdit->moveCursor(QTextCursor::End); + m_textEdit->setTextColor(Qt::darkGreen); + m_textEdit->append(m_currObject.className() + + QLatin1String(": ") + + (m_currObject.name().isEmpty() ? QLatin1String("<unnamed object>") : m_currObject.name())); + appendPrompt(); +} + +void ExpressionQueryWidget::executeExpression() +{ + if (!m_client) + return; + + if (m_mode == SeparateEntryMode) + m_expr = m_lineEdit->text().trimmed(); + else + m_expr = m_expr.trimmed(); + + if (!m_expr.isEmpty() && m_currObject.debugId() != -1) { + if (m_query) + delete m_query; + m_query = m_client->queryExpressionResult(m_currObject.debugId(), m_expr, this); + if (!m_query->isWaiting()) + showResult(); + else + QObject::connect(m_query, SIGNAL(stateChanged(QmlDebugQuery::State)), + this, SLOT(showResult())); + + m_lastExpr = m_expr; + if (m_lineEdit) + m_lineEdit->clear(); + } +} + +void ExpressionQueryWidget::showResult() +{ + if (m_query) { + m_textEdit->moveCursor(QTextCursor::End); + QVariant value = m_query->result(); + QString result; + + if (value.type() == QVariant::List || value.type() == QVariant::StringList) { + result = tr("<%1 items>", "%1 = number of items").arg(value.toList().count()); + } else if (value.isNull()) { + result = QLatin1String("<no value>"); + } else { + result = value.toString(); + } + + if (m_mode == SeparateEntryMode) { + m_textEdit->setTextColor(Qt::black); + m_textEdit->setFontWeight(QFont::Bold); + m_textEdit->insertPlainText(m_expr + " : "); + m_textEdit->setFontWeight(QFont::Normal); + m_textEdit->insertPlainText(result); + } else { + m_textEdit->setTextColor(Qt::darkGreen); + m_textEdit->insertPlainText(" => "); + m_textEdit->setTextColor(Qt::black); + m_textEdit->insertPlainText(result); + } + appendPrompt(); + m_expr.clear(); + } +} + +bool ExpressionQueryWidget::eventFilter(QObject *obj, QEvent *event) +{ + if (obj == m_textEdit) { + switch (event->type()) { + case QEvent::KeyPress: + { + QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event); + int key = keyEvent->key(); + if (key == Qt::Key_Return || key == Qt::Key_Enter) { + executeExpression(); + return true; + } else if (key == Qt::Key_Backspace) { + // ensure m_expr doesn't contain backspace characters + QTextCursor cursor = m_textEdit->textCursor(); + bool atLastLine = !(cursor.block().next().isValid()); + if (!atLastLine) + return true; + if (cursor.columnNumber() <= m_prompt.count()) + return true; + cursor.deletePreviousChar(); + m_expr = cursor.block().text().mid(m_prompt.count()); + return true; + } else { + m_textEdit->moveCursor(QTextCursor::End); + m_textEdit->setTextColor(Qt::black); + m_expr += keyEvent->text(); + } + break; + } + case QEvent::FocusIn: + checkCurrentContext(); + m_textEdit->moveCursor(QTextCursor::End); + break; + default: + break; + } + } else if (obj == m_lineEdit) { + switch (event->type()) { + case QEvent::KeyPress: + { + QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event); + int key = keyEvent->key(); + if (key == Qt::Key_Up && m_lineEdit->text() != m_lastExpr) { + m_expr = m_lineEdit->text(); + if (!m_lastExpr.isEmpty()) + m_lineEdit->setText(m_lastExpr); + } else if (key == Qt::Key_Down) { + m_lineEdit->setText(m_expr); + } + break; + } + case QEvent::FocusIn: + checkCurrentContext(); + break; + default: + break; + } + } + return QWidget::eventFilter(obj, event); +} diff --git a/tools/qmldebugger/standalone/expressionquerywidget.h b/tools/qmldebugger/standalone/expressionquerywidget.h new file mode 100644 index 0000000..8c224f8 --- /dev/null +++ b/tools/qmldebugger/standalone/expressionquerywidget.h @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt QML Debugger 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 EXPRESSIONQUERYWIDGET_H +#define EXPRESSIONQUERYWIDGET_H + +#include <QWidget> + +#include <private/qmldebug_p.h> + +QT_BEGIN_NAMESPACE + +class QGroupBox; +class QTextEdit; +class QLineEdit; +class QPushButton; + +class ExpressionQueryWidget : public QWidget +{ + Q_OBJECT +public: + enum Mode { + SeparateEntryMode, + ShellMode + }; + + ExpressionQueryWidget(Mode mode = SeparateEntryMode, QmlEngineDebug *client = 0, QWidget *parent = 0); + + void setEngineDebug(QmlEngineDebug *client); + void clear(); + +protected: + bool eventFilter(QObject *obj, QEvent *event); + +public slots: + void setCurrentObject(const QmlDebugObjectReference &obj); + +private slots: + void executeExpression(); + void showResult(); + +private: + void appendPrompt(); + void checkCurrentContext(); + void showCurrentContext(); + void updateTitle(); + + Mode m_mode; + + QmlEngineDebug *m_client; + QmlDebugExpressionQuery *m_query; + QTextEdit *m_textEdit; + QLineEdit *m_lineEdit; + QPushButton *m_button; + QString m_prompt; + QString m_expr; + QString m_lastExpr; + + QString m_title; + + QmlDebugObjectReference m_currObject; + QmlDebugObjectReference m_objectAtLastFocus; +}; + +QT_END_NAMESPACE + +#endif + diff --git a/tools/qmldebugger/standalone/main.cpp b/tools/qmldebugger/standalone/main.cpp new file mode 100644 index 0000000..715837e --- /dev/null +++ b/tools/qmldebugger/standalone/main.cpp @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt QML Debugger 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 <QtGui/qapplication.h> + +#include "qmldebugger.h" + +int main(int argc, char ** argv) +{ + QApplication app(argc, argv); + app.setApplicationName("QtQmlDebugger"); + app.setOrganizationName("Nokia"); + app.setOrganizationDomain("nokia.com"); + + QStringList args = app.arguments(); + + QmlDebugger win; + if (args.contains("--engine")) + win.showEngineTab(); + + for (int i=0; i<args.count(); i++) { + if (!args[i].contains(':')) + continue; + QStringList hostAndPort = args[i].split(':'); + bool ok = false; + quint16 port = hostAndPort.value(1).toInt(&ok); + if (ok) { + qWarning() << "qmldebugger connecting to" + << hostAndPort[0] << port << "..."; + win.setHost(hostAndPort[0]); + win.setPort(port); + win.connectToHost(); + break; + } + } + + win.show(); + + return app.exec(); +} diff --git a/tools/qmldebugger/standalone/objectpropertiesview.cpp b/tools/qmldebugger/standalone/objectpropertiesview.cpp new file mode 100644 index 0000000..3a8d8c8 --- /dev/null +++ b/tools/qmldebugger/standalone/objectpropertiesview.cpp @@ -0,0 +1,274 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt QML Debugger 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 <QtGui/qtreewidget.h> +#include <QtGui/qlayout.h> +#include <QtGui/qheaderview.h> + +#include <private/qmldebugservice_p.h> +#include <private/qmldebug_p.h> +#include <private/qmldebugclient_p.h> + +#include "objectpropertiesview.h" + +QT_BEGIN_NAMESPACE + +class PropertiesViewItem : public QObject, public QTreeWidgetItem +{ + Q_OBJECT +public: + enum Type { + BindingType, + OtherType + }; + + PropertiesViewItem(QTreeWidget *widget, Type type = OtherType); + PropertiesViewItem(QTreeWidgetItem *parent, Type type = OtherType); + + QmlDebugPropertyReference property; + Type type; +}; + +PropertiesViewItem::PropertiesViewItem(QTreeWidget *widget, Type type) + : QTreeWidgetItem(widget), type(type) +{ +} + +PropertiesViewItem::PropertiesViewItem(QTreeWidgetItem *parent, Type type) + : QTreeWidgetItem(parent), type(type) +{ +} + +ObjectPropertiesView::ObjectPropertiesView(QmlEngineDebug *client, QWidget *parent) + : QWidget(parent), + m_client(client), + m_query(0), + m_watch(0) +{ + QVBoxLayout *layout = new QVBoxLayout; + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + setLayout(layout); + + m_tree = new QTreeWidget(this); + m_tree->setAlternatingRowColors(true); + m_tree->setExpandsOnDoubleClick(false); + m_tree->setHeaderLabels(QStringList() + << tr("Name") << tr("Value") << tr("Type")); + QObject::connect(m_tree, SIGNAL(itemActivated(QTreeWidgetItem *, int)), + this, SLOT(itemActivated(QTreeWidgetItem *))); + + m_tree->setColumnCount(3); + m_tree->header()->setDefaultSectionSize(150); + + layout->addWidget(m_tree); +} + +void ObjectPropertiesView::setEngineDebug(QmlEngineDebug *client) +{ + m_client = client; +} + +void ObjectPropertiesView::clear() +{ + setObject(QmlDebugObjectReference()); +} + +void ObjectPropertiesView::reload(const QmlDebugObjectReference &obj) +{ + if (!m_client) + return; + if (m_query) + delete m_query; + + m_query = m_client->queryObjectRecursive(obj, this); + if (!m_query->isWaiting()) + queryFinished(); + else + QObject::connect(m_query, SIGNAL(stateChanged(QmlDebugQuery::State)), + this, SLOT(queryFinished())); +} + +void ObjectPropertiesView::queryFinished() +{ + if (!m_client || !m_query) + return; + + QmlDebugObjectReference obj = m_query->object(); + + QmlDebugWatch *watch = m_client->addWatch(obj, this); + if (watch->state() == QmlDebugWatch::Dead) { + delete watch; + watch = 0; + } else { + if (m_watch) { + m_client->removeWatch(m_watch); + delete m_watch; + } + m_watch = watch; + QObject::connect(watch, SIGNAL(valueChanged(QByteArray,QVariant)), + this, SLOT(valueChanged(QByteArray,QVariant))); + } + + delete m_query; + m_query = 0; + + setObject(obj); +} + +void ObjectPropertiesView::setPropertyValue(PropertiesViewItem *item, const QVariant &value, bool makeGray) +{ + if (value.type() == QVariant::List || value.type() == QVariant::StringList) { + PropertiesViewItem *bindingItem = static_cast<PropertiesViewItem*>(item->takeChild(item->childCount() - 1)); + if (bindingItem && bindingItem->type != PropertiesViewItem::BindingType) { + delete bindingItem; + bindingItem = 0; + } + + qDeleteAll(item->takeChildren()); + + QVariantList variants = value.toList(); + item->setText(1, tr("<%1 items>", "%1 = number of items").arg(variants.count())); + item->setText(2, QString::fromUtf8(value.typeName())); + + PropertiesViewItem *child; + for (int i=0; i<variants.count(); i++) { + child = new PropertiesViewItem(item); + setPropertyValue(child, variants[i], makeGray); + } + + if (bindingItem) + item->addChild(bindingItem); + + item->setExpanded(false); + } else { + item->setText(1, (value.isNull() ? QLatin1String("<no value>") : value.toString())); + item->setExpanded(true); + } + + if (makeGray) { + for (int i=0; i<m_tree->columnCount(); i++) + item->setForeground(i, Qt::gray); + } +} + +void ObjectPropertiesView::setObject(const QmlDebugObjectReference &object) +{ + m_object = object; + m_tree->clear(); + + QList<QmlDebugPropertyReference> properties = object.properties(); + for (int i=0; i<properties.count(); i++) { + const QmlDebugPropertyReference &p = properties[i]; + + PropertiesViewItem *item = new PropertiesViewItem(m_tree); + item->property = p; + + item->setText(0, p.name()); + item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); + + setPropertyValue(item, p.value(), !p.hasNotifySignal()); + item->setText(2, p.valueTypeName()); + + // binding is set after property value to ensure it is added to the end of the + // list, if the value is a list + if (!p.binding().isEmpty()) { + PropertiesViewItem *binding = new PropertiesViewItem(item, PropertiesViewItem::BindingType); + binding->setText(1, p.binding()); + binding->setForeground(1, Qt::darkGreen); + } + } +} + +void ObjectPropertiesView::watchCreated(QmlDebugWatch *watch) +{ + if (watch->objectDebugId() == m_object.debugId() + && qobject_cast<QmlDebugPropertyWatch*>(watch)) { + connect(watch, SIGNAL(stateChanged(QmlDebugWatch::State)), SLOT(watchStateChanged())); + setWatched(qobject_cast<QmlDebugPropertyWatch*>(watch)->name(), true); + } +} + +void ObjectPropertiesView::watchStateChanged() +{ + QmlDebugWatch *watch = qobject_cast<QmlDebugWatch*>(sender()); + + if (watch->objectDebugId() == m_object.debugId() + && qobject_cast<QmlDebugPropertyWatch*>(watch) + && watch->state() == QmlDebugWatch::Inactive) { + setWatched(qobject_cast<QmlDebugPropertyWatch*>(watch)->name(), false); + } +} + +void ObjectPropertiesView::setWatched(const QString &property, bool watched) +{ + for (int i=0; i<m_tree->topLevelItemCount(); i++) { + PropertiesViewItem *item = static_cast<PropertiesViewItem *>(m_tree->topLevelItem(i)); + if (item->property.name() == property && item->property.hasNotifySignal()) { + QFont font = m_tree->font(); + font.setBold(watched); + item->setFont(0, font); + } + } +} + +void ObjectPropertiesView::valueChanged(const QByteArray &name, const QVariant &value) +{ + for (int i=0; i<m_tree->topLevelItemCount(); i++) { + PropertiesViewItem *item = static_cast<PropertiesViewItem *>(m_tree->topLevelItem(i)); + if (item->property.name() == name) { + setPropertyValue(item, value, !item->property.hasNotifySignal()); + return; + } + } +} + +void ObjectPropertiesView::itemActivated(QTreeWidgetItem *i) +{ + PropertiesViewItem *item = static_cast<PropertiesViewItem *>(i); + if (!item->property.name().isEmpty()) + emit activated(m_object, item->property); +} + +QT_END_NAMESPACE + +#include "objectpropertiesview.moc" diff --git a/tools/qmldebugger/standalone/objectpropertiesview.h b/tools/qmldebugger/standalone/objectpropertiesview.h new file mode 100644 index 0000000..43413dc --- /dev/null +++ b/tools/qmldebugger/standalone/objectpropertiesview.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt QML Debugger 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 PROPERTIESTABLEMODEL_H +#define PROPERTIESTABLEMODEL_H + +#include <private/qmldebug_p.h> + +#include <QtGui/qwidget.h> + +QT_BEGIN_NAMESPACE + +class QTreeWidget; +class QTreeWidgetItem; +class QmlDebugConnection; +class PropertiesViewItem; + +class ObjectPropertiesView : public QWidget +{ + Q_OBJECT +public: + ObjectPropertiesView(QmlEngineDebug *client = 0, QWidget *parent = 0); + + void setEngineDebug(QmlEngineDebug *client); + void clear(); + +signals: + void activated(const QmlDebugObjectReference &, const QmlDebugPropertyReference &); + +public slots: + void reload(const QmlDebugObjectReference &); + void watchCreated(QmlDebugWatch *); + +private slots: + void queryFinished(); + void watchStateChanged(); + void valueChanged(const QByteArray &name, const QVariant &value); + void itemActivated(QTreeWidgetItem *i); + +private: + void setObject(const QmlDebugObjectReference &object); + void setWatched(const QString &property, bool watched); + void setPropertyValue(PropertiesViewItem *item, const QVariant &value, bool makeGray); + + QmlEngineDebug *m_client; + QmlDebugObjectQuery *m_query; + QmlDebugWatch *m_watch; + + QTreeWidget *m_tree; + QmlDebugObjectReference m_object; +}; + + +QT_END_NAMESPACE + +#endif diff --git a/tools/qmldebugger/standalone/objecttree.cpp b/tools/qmldebugger/standalone/objecttree.cpp new file mode 100644 index 0000000..cf467f2 --- /dev/null +++ b/tools/qmldebugger/standalone/objecttree.cpp @@ -0,0 +1,231 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt QML Debugger 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 <QtGui/qevent.h> +#include <QtGui/qmenu.h> +#include <QtGui/qaction.h> + +#include <QInputDialog> + +#include <private/qmldebugservice_p.h> +#include <private/qmldebug_p.h> +#include <private/qmldebugclient_p.h> + +#include "objecttree.h" + +Q_DECLARE_METATYPE(QmlDebugObjectReference) + +ObjectTree::ObjectTree(QmlEngineDebug *client, QWidget *parent) + : QTreeWidget(parent), + m_client(client), + m_query(0) +{ + setHeaderHidden(true); + setMinimumWidth(250); + setExpandsOnDoubleClick(false); + + connect(this, SIGNAL(currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)), + SLOT(currentItemChanged(QTreeWidgetItem *))); + connect(this, SIGNAL(itemActivated(QTreeWidgetItem *, int)), + SLOT(activated(QTreeWidgetItem *))); +} + +void ObjectTree::setEngineDebug(QmlEngineDebug *client) +{ + m_client = client; +} + +void ObjectTree::reload(int objectDebugId) +{ + if (!m_client) + return; + + if (m_query) { + delete m_query; + m_query = 0; + } + + m_query = m_client->queryObjectRecursive(QmlDebugObjectReference(objectDebugId), this); + if (!m_query->isWaiting()) + objectFetched(); + else + QObject::connect(m_query, SIGNAL(stateChanged(QmlDebugQuery::State)), + this, SLOT(objectFetched())); +} + +void ObjectTree::setCurrentObject(int debugId) +{ + QTreeWidgetItem *item = findItemByObjectId(debugId); + if (item) { + setCurrentItem(item); + scrollToItem(item); + item->setExpanded(true); + } +} + +void ObjectTree::objectFetched() +{ + dump(m_query->object(), 0); + buildTree(m_query->object(), 0); + setCurrentItem(topLevelItem(0)); + + delete m_query; + m_query = 0; +} + +void ObjectTree::currentItemChanged(QTreeWidgetItem *item) +{ + if (!item) + return; + + QmlDebugObjectReference obj = item->data(0, Qt::UserRole).value<QmlDebugObjectReference>(); + if (obj.debugId() >= 0) + emit currentObjectChanged(obj); +} + +void ObjectTree::activated(QTreeWidgetItem *item) +{ + if (!item) + return; + + QmlDebugObjectReference obj = item->data(0, Qt::UserRole).value<QmlDebugObjectReference>(); + if (obj.debugId() >= 0) + emit activated(obj); +} + +void ObjectTree::buildTree(const QmlDebugObjectReference &obj, QTreeWidgetItem *parent) +{ + if (!parent) + clear(); + + QTreeWidgetItem *item = parent ? new QTreeWidgetItem(parent) : new QTreeWidgetItem(this); + item->setText(0, obj.className()); + item->setData(0, Qt::UserRole, qVariantFromValue(obj)); + + if (parent && obj.contextDebugId() >= 0 + && obj.contextDebugId() != parent->data(0, Qt::UserRole + ).value<QmlDebugObjectReference>().contextDebugId()) { + QmlDebugFileReference source = obj.source(); + if (!source.url().isEmpty()) { + QString toolTipString = QLatin1String("URL: ") + source.url().toString(); + item->setToolTip(0, toolTipString); + } + item->setForeground(0, QColor("orange")); + } else { + item->setExpanded(true); + } + + if (obj.contextDebugId() < 0) + item->setForeground(0, Qt::lightGray); + + for (int ii = 0; ii < obj.children().count(); ++ii) + buildTree(obj.children().at(ii), item); +} + +void ObjectTree::dump(const QmlDebugContextReference &ctxt, int ind) +{ + QByteArray indent(ind * 4, ' '); + qWarning().nospace() << indent.constData() << ctxt.debugId() << " " + << qPrintable(ctxt.name()); + + for (int ii = 0; ii < ctxt.contexts().count(); ++ii) + dump(ctxt.contexts().at(ii), ind + 1); + + for (int ii = 0; ii < ctxt.objects().count(); ++ii) + dump(ctxt.objects().at(ii), ind); +} + +void ObjectTree::dump(const QmlDebugObjectReference &obj, int ind) +{ + QByteArray indent(ind * 4, ' '); + qWarning().nospace() << indent.constData() << qPrintable(obj.className()) + << " " << qPrintable(obj.name()) << " " + << obj.debugId(); + + for (int ii = 0; ii < obj.children().count(); ++ii) + dump(obj.children().at(ii), ind + 1); +} + +QTreeWidgetItem *ObjectTree::findItemByObjectId(int debugId) const +{ + for (int i=0; i<topLevelItemCount(); i++) { + QTreeWidgetItem *item = findItem(topLevelItem(i), debugId); + if (item) + return item; + } + + return 0; +} + +QTreeWidgetItem *ObjectTree::findItem(QTreeWidgetItem *item, int debugId) const +{ + if (item->data(0, Qt::UserRole).value<QmlDebugObjectReference>().debugId() == debugId) + return item; + + QTreeWidgetItem *child; + for (int i=0; i<item->childCount(); i++) { + child = findItem(item->child(i), debugId); + if (child) + return child; + } + + return 0; +} + +void ObjectTree::mousePressEvent(QMouseEvent *me) +{ + QTreeWidget::mousePressEvent(me); + if (!currentItem()) + return; + if(me->button() == Qt::RightButton && me->type() == QEvent::MouseButtonPress) { + QAction action(tr("Add watch..."), 0); + QList<QAction *> actions; + actions << &action; + QmlDebugObjectReference obj = + currentItem()->data(0, Qt::UserRole).value<QmlDebugObjectReference>(); + if (QMenu::exec(actions, me->globalPos())) { + bool ok = false; + QString watch = QInputDialog::getText(this, tr("Watch expression"), + tr("Expression:"), QLineEdit::Normal, QString(), &ok); + if (ok && !watch.isEmpty()) + emit expressionWatchRequested(obj, watch); + } + } +} diff --git a/tools/qmldebugger/standalone/objecttree.h b/tools/qmldebugger/standalone/objecttree.h new file mode 100644 index 0000000..c8d625c --- /dev/null +++ b/tools/qmldebugger/standalone/objecttree.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt QML Debugger 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 OBJECTTREE_H +#define OBJECTTREE_H + +#include <QtGui/qtreewidget.h> + +QT_BEGIN_NAMESPACE + +class QTreeWidgetItem; + +class QmlEngineDebug; +class QmlDebugObjectReference; +class QmlDebugObjectQuery; +class QmlDebugContextReference; +class QmlDebugConnection; + + +class ObjectTree : public QTreeWidget +{ + Q_OBJECT +public: + ObjectTree(QmlEngineDebug *client = 0, QWidget *parent = 0); + + void setEngineDebug(QmlEngineDebug *client); + +signals: + void currentObjectChanged(const QmlDebugObjectReference &); + void activated(const QmlDebugObjectReference &); + void expressionWatchRequested(const QmlDebugObjectReference &, const QString &); + +public slots: + void reload(int objectDebugId); // set the root object + void setCurrentObject(int debugId); // select an object in the tree + +protected: + virtual void mousePressEvent(QMouseEvent *); + +private slots: + void objectFetched(); + void currentItemChanged(QTreeWidgetItem *); + void activated(QTreeWidgetItem *); + +private: + QTreeWidgetItem *findItemByObjectId(int debugId) const; + QTreeWidgetItem *findItem(QTreeWidgetItem *item, int debugId) const; + void dump(const QmlDebugContextReference &, int); + void dump(const QmlDebugObjectReference &, int); + void buildTree(const QmlDebugObjectReference &, QTreeWidgetItem *parent); + + QmlEngineDebug *m_client; + QmlDebugObjectQuery *m_query; +}; + +QT_END_NAMESPACE + + +#endif diff --git a/tools/qmldebugger/standalone/qmldebugger.cpp b/tools/qmldebugger/standalone/qmldebugger.cpp new file mode 100644 index 0000000..4d86377 --- /dev/null +++ b/tools/qmldebugger/standalone/qmldebugger.cpp @@ -0,0 +1,181 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt QML Debugger 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/qtimer.h> +#include <QtCore/qdebug.h> +#include <QtCore/qsettings.h> + +#include <QtGui/qlayout.h> +#include <QtGui/qpushbutton.h> +#include <QtGui/qlineedit.h> +#include <QtGui/qtabwidget.h> +#include <QtGui/qspinbox.h> +#include <QtGui/qlabel.h> + +#include "canvasframerate.h" +#include "engine.h" +#include "qmldebugger.h" + +QmlDebugger::QmlDebugger(QWidget *parent) +: QWidget(parent) +{ + QVBoxLayout *layout = new QVBoxLayout; + setLayout(layout); + + QHBoxLayout *connectLayout = new QHBoxLayout; + layout->addLayout(connectLayout); + connectLayout->addStretch(2); + + m_connectionState = new QLabel(this); + connectLayout->addWidget(m_connectionState); + m_host = new QLineEdit(this); + connectLayout->addWidget(m_host); + m_port = new QSpinBox(this); + m_port->setMinimum(1024); + m_port->setMaximum(20000); + connectLayout->addWidget(m_port); + m_connectButton = new QPushButton(tr("Connect"), this); + QObject::connect(m_connectButton, SIGNAL(clicked()), + this, SLOT(connectToHost())); + connectLayout->addWidget(m_connectButton); + m_disconnectButton = new QPushButton(tr("Disconnect"), this); + QObject::connect(m_disconnectButton, SIGNAL(clicked()), + this, SLOT(disconnectFromHost())); + m_disconnectButton->setEnabled(false); + connectLayout->addWidget(m_disconnectButton); + + m_tabs = new QTabWidget(this); + layout->addWidget(m_tabs); + + CanvasFrameRate *cfr = new CanvasFrameRate(this); + cfr->reset(&client); + cfr->setSizeHint(QSize(800, 600)); + m_tabs->addTab(cfr, tr("Frame Rate")); + + m_enginePane = new EnginePane(&client, this); + m_tabs->addTab(m_enginePane, tr("QML Engine")); + + QObject::connect(&client, SIGNAL(stateChanged(QAbstractSocket::SocketState)), + this, SLOT(connectionStateChanged())); + connectionStateChanged(); + + QObject::connect(&client, SIGNAL(error(QAbstractSocket::SocketError)), + this, SLOT(connectionError(QAbstractSocket::SocketError))); + + QSettings settings; + m_host->setText(settings.value("Host", "127.0.0.1").toString()); + m_port->setValue(settings.value("Port", 3768).toInt()); + + connectToHost(); +} + +void QmlDebugger::setHost(const QString &host) +{ + m_host->setText(host); +} + +void QmlDebugger::setPort(quint16 port) +{ + m_port->setValue(port); +} + +void QmlDebugger::showEngineTab() +{ + m_tabs->setCurrentWidget(m_enginePane); +} + +void QmlDebugger::closeEvent(QCloseEvent *event) +{ + QSettings settings; + settings.setValue("Host", m_host->text()); + settings.setValue("Port", m_port->value()); + + QWidget::closeEvent(event); +} + +void QmlDebugger::connectionStateChanged() +{ + switch (client.state()) { + default: + case QAbstractSocket::UnconnectedState: + m_connectionState->setText(tr("Disconnected")); + m_connectButton->setEnabled(true); + m_disconnectButton->setEnabled(false); + break; + case QAbstractSocket::HostLookupState: + m_connectionState->setText(tr("Resolving")); + m_connectButton->setEnabled(false); + m_disconnectButton->setEnabled(true); + break; + case QAbstractSocket::ConnectingState: + m_connectionState->setText(tr("Connecting")); + m_connectButton->setEnabled(false); + m_disconnectButton->setEnabled(true); + break; + case QAbstractSocket::ConnectedState: + m_connectionState->setText(tr("Connected")); + m_connectButton->setEnabled(false); + m_disconnectButton->setEnabled(true); + + QTimer::singleShot(0, m_enginePane, SLOT(refreshEngines())); + break; + case QAbstractSocket::ClosingState: + m_connectionState->setText(tr("Closing")); + m_connectButton->setEnabled(false); + m_disconnectButton->setEnabled(false); + break; + } +} + +void QmlDebugger::connectionError(QAbstractSocket::SocketError socketError) +{ + qWarning() << "qmldebugger cannot connect:" << socketError + << client.errorString(); +} + +void QmlDebugger::connectToHost() +{ + client.connectToHost(m_host->text(), m_port->value()); +} + +void QmlDebugger::disconnectFromHost() +{ + client.disconnectFromHost(); +} diff --git a/tools/qmldebugger/standalone/qmldebugger.h b/tools/qmldebugger/standalone/qmldebugger.h new file mode 100644 index 0000000..da95ef9 --- /dev/null +++ b/tools/qmldebugger/standalone/qmldebugger.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt QML Debugger 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 QMLDEBUGGER_H +#define QMLDEBUGGER_H + +#include <private/qmldebugclient_p.h> +#include <QtNetwork/qtcpsocket.h> +#include <QtGui/qwidget.h> + +class QLabel; +class QLineEdit; +class QSpinBox; +class QPushButton; +class QTabWidget; + +class EnginePane; + +class QmlDebugger : public QWidget +{ + Q_OBJECT +public: + QmlDebugger(QWidget * = 0); + + void setHost(const QString &host); + void setPort(quint16 port); + void showEngineTab(); + +public slots: + void connectToHost(); + void disconnectFromHost(); + +protected: + void closeEvent(QCloseEvent *); + +private slots: + void connectionStateChanged(); + void connectionError(QAbstractSocket::SocketError socketError); + +private: + QmlDebugConnection client; + + QLabel *m_connectionState; + QLineEdit *m_host; + QSpinBox *m_port; + QPushButton *m_connectButton; + QPushButton *m_disconnectButton; + + EnginePane *m_enginePane; + QTabWidget *m_tabs; +}; + +#endif diff --git a/tools/qmldebugger/standalone/qmldebugger.pri b/tools/qmldebugger/standalone/qmldebugger.pri new file mode 100644 index 0000000..aad5eb1 --- /dev/null +++ b/tools/qmldebugger/standalone/qmldebugger.pri @@ -0,0 +1,16 @@ +QT += network declarative +contains(QT_CONFIG, opengles2)|contains(QT_CONFIG, opengles1): QT += opengl + +# Input +HEADERS += $$PWD/canvasframerate.h \ + $$PWD/watchtable.h \ + $$PWD/objecttree.h \ + $$PWD/objectpropertiesview.h \ + $$PWD/expressionquerywidget.h + +SOURCES += $$PWD/canvasframerate.cpp \ + $$PWD/watchtable.cpp \ + $$PWD/objecttree.cpp \ + $$PWD/objectpropertiesview.cpp \ + $$PWD/expressionquerywidget.cpp + diff --git a/tools/qmldebugger/standalone/qmldebugger.qrc b/tools/qmldebugger/standalone/qmldebugger.qrc new file mode 100644 index 0000000..cb53ad5 --- /dev/null +++ b/tools/qmldebugger/standalone/qmldebugger.qrc @@ -0,0 +1,7 @@ +<RCC> + <qresource prefix="/"> + <file>engines.qml</file> + <file>engine.png</file> + <file>refresh.png</file> + </qresource> +</RCC> diff --git a/tools/qmldebugger/standalone/refresh.png b/tools/qmldebugger/standalone/refresh.png Binary files differnew file mode 100644 index 0000000..8befc80 --- /dev/null +++ b/tools/qmldebugger/standalone/refresh.png diff --git a/tools/qmldebugger/standalone/standalone.pro b/tools/qmldebugger/standalone/standalone.pro new file mode 100644 index 0000000..72d051f --- /dev/null +++ b/tools/qmldebugger/standalone/standalone.pro @@ -0,0 +1,19 @@ +DESTDIR = ../../../bin +TARGET = qmldebugger + +include(qmldebugger.pri) + +HEADERS += $$PWD/qmldebugger.h \ + $$PWD/engine.h + +SOURCES += $$PWD/qmldebugger.cpp \ + $$PWD/engine.cpp \ + $$PWD/main.cpp + +RESOURCES += $$PWD/qmldebugger.qrc +OTHER_FILES += $$PWD/engines.qml + +target.path=$$[QT_INSTALL_BINS] +INSTALLS += target + +CONFIG += console diff --git a/tools/qmldebugger/standalone/watchtable.cpp b/tools/qmldebugger/standalone/watchtable.cpp new file mode 100644 index 0000000..0e73de5 --- /dev/null +++ b/tools/qmldebugger/standalone/watchtable.cpp @@ -0,0 +1,366 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt QML Debugger 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 "watchtable.h" + +#include <QtCore/qdebug.h> +#include <QtGui/qevent.h> +#include <QtGui/qaction.h> +#include <QtGui/qmenu.h> + +#include <private/qmldebug_p.h> +#include <QtDeclarative/qmlmetatype.h> + +QT_BEGIN_NAMESPACE + + +WatchTableModel::WatchTableModel(QmlEngineDebug *client, QObject *parent) + : QAbstractTableModel(parent), + m_client(client) +{ +} + +WatchTableModel::~WatchTableModel() +{ + for (int i=0; i<m_columns.count(); i++) + delete m_columns[i].watch; +} + +void WatchTableModel::setEngineDebug(QmlEngineDebug *client) +{ + m_client = client; +} + +void WatchTableModel::addWatch(QmlDebugWatch *watch, const QString &title) +{ + QString property; + if (qobject_cast<QmlDebugPropertyWatch *>(watch)) + property = qobject_cast<QmlDebugPropertyWatch *>(watch)->name(); + + connect(watch, SIGNAL(valueChanged(QByteArray,QVariant)), + SLOT(watchedValueChanged(QByteArray,QVariant))); + + connect(watch, SIGNAL(stateChanged(QmlDebugWatch::State)), SLOT(watchStateChanged())); + + int col = columnCount(QModelIndex()); + beginInsertColumns(QModelIndex(), col, col); + + WatchedEntity e; + e.title = title; + e.hasFirstValue = false; + e.property = property; + e.watch = watch; + m_columns.append(e); + + endInsertColumns(); +} + +void WatchTableModel::removeWatch(QmlDebugWatch *watch) +{ + int column = columnForWatch(watch); + if (column == -1) + return; + + WatchedEntity entity = m_columns.takeAt(column); + + for (QList<Value>::Iterator iter = m_values.begin(); iter != m_values.end();) { + if (iter->column == column) { + iter = m_values.erase(iter); + } else { + if(iter->column > column) + --iter->column; + ++iter; + } + } + + reset(); +} + +void WatchTableModel::updateWatch(QmlDebugWatch *watch, const QVariant &value) +{ + int column = columnForWatch(watch); + if (column == -1) + return; + + addValue(column, value); + + if (!m_columns[column].hasFirstValue) { + m_columns[column].hasFirstValue = true; + m_values[m_values.count() - 1].first = true; + } +} + +QmlDebugWatch *WatchTableModel::findWatch(int column) const +{ + if (column < m_columns.count()) + return m_columns.at(column).watch; + return 0; +} + +QmlDebugWatch *WatchTableModel::findWatch(int objectDebugId, const QString &property) const +{ + for (int i=0; i<m_columns.count(); i++) { + if (m_columns[i].watch->objectDebugId() == objectDebugId + && m_columns[i].property == property) { + return m_columns[i].watch; + } + } + return 0; +} + +int WatchTableModel::rowCount(const QModelIndex &) const +{ + return m_values.count(); +} + +int WatchTableModel::columnCount(const QModelIndex &) const +{ + return m_columns.count(); +} + +QVariant WatchTableModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Horizontal) { + if (section < m_columns.count() && role == Qt::DisplayRole) + return m_columns.at(section).title; + } else { + if (role == Qt::DisplayRole) + return section + 1; + } + return QVariant(); +} + +QVariant WatchTableModel::data(const QModelIndex &idx, int role) const +{ + if (m_values.at(idx.row()).column == idx.column()) { + if (role == Qt::DisplayRole) { + const QVariant &value = m_values.at(idx.row()).variant; + QString str = value.toString(); + + if (str.isEmpty() && QmlMetaType::isObject(value.userType())) { + QObject *o = QmlMetaType::toQObject(value); + if(o) { + QString objectName = o->objectName(); + if(objectName.isEmpty()) + objectName = QLatin1String("<unnamed>"); + str = QLatin1String(o->metaObject()->className()) + + QLatin1String(": ") + objectName; + } + } + + if(str.isEmpty()) { + QDebug d(&str); + d << value; + } + return QVariant(str); + } else if(role == Qt::BackgroundRole) { + if(m_values.at(idx.row()).first) + return QColor(Qt::green); + else + return QVariant(); + } else { + return QVariant(); + } + } else { + return QVariant(); + } +} + +void WatchTableModel::watchStateChanged() +{ + QmlDebugWatch *watch = qobject_cast<QmlDebugWatch*>(sender()); + + if (watch && watch->state() == QmlDebugWatch::Inactive) { + removeWatch(watch); + watch->deleteLater(); + } +} + +int WatchTableModel::columnForWatch(QmlDebugWatch *watch) const +{ + for (int i=0; i<m_columns.count(); i++) { + if (m_columns.at(i).watch == watch) + return i; + } + return -1; +} + +void WatchTableModel::addValue(int column, const QVariant &value) +{ + int row = columnCount(QModelIndex()); + beginInsertRows(QModelIndex(), row, row); + + Value v; + v.column = column; + v.variant = value; + v.first = false; + m_values.append(v); + + endInsertRows(); +} + +void WatchTableModel::togglePropertyWatch(const QmlDebugObjectReference &object, const QmlDebugPropertyReference &property) +{ + if (!m_client || !property.hasNotifySignal()) + return; + + QmlDebugWatch *watch = findWatch(object.debugId(), property.name()); + if (watch) { + // watch will be deleted in watchStateChanged() + m_client->removeWatch(watch); + return; + } + + watch = m_client->addWatch(property, this); + if (watch->state() == QmlDebugWatch::Dead) { + delete watch; + watch = 0; + } else { + QString desc = property.name() + + QLatin1String(" on\n") + + object.className() + + QLatin1String(":\n") + + (object.name().isEmpty() ? QLatin1String("<unnamed object>") : object.name()); + addWatch(watch, desc); + emit watchCreated(watch); + } +} + +void WatchTableModel::watchedValueChanged(const QByteArray &propertyName, const QVariant &value) +{ + Q_UNUSED(propertyName); + QmlDebugWatch *watch = qobject_cast<QmlDebugWatch*>(sender()); + if (watch) + updateWatch(watch, value); +} + +void WatchTableModel::expressionWatchRequested(const QmlDebugObjectReference &obj, const QString &expr) +{ + if (!m_client) + return; + + QmlDebugWatch *watch = m_client->addWatch(obj, expr, this); + + if (watch->state() == QmlDebugWatch::Dead) { + delete watch; + watch = 0; + } else { + addWatch(watch, expr); + emit watchCreated(watch); + } +} + +void WatchTableModel::removeWatchAt(int column) +{ + if (!m_client) + return; + + QmlDebugWatch *watch = findWatch(column); + if (watch) { + m_client->removeWatch(watch); + delete watch; + watch = 0; + } +} + +void WatchTableModel::removeAllWatches() +{ + for (int i=0; i<m_columns.count(); i++) { + if (m_client) + m_client->removeWatch(m_columns[i].watch); + else + delete m_columns[i].watch; + } + m_columns.clear(); + m_values.clear(); + reset(); +} + +//---------------------------------------------- + +WatchTableHeaderView::WatchTableHeaderView(WatchTableModel *model, QWidget *parent) + : QHeaderView(Qt::Horizontal, parent), + m_model(model) +{ + setClickable(true); +} + +void WatchTableHeaderView::mousePressEvent(QMouseEvent *me) +{ + QHeaderView::mousePressEvent(me); + + if (me->button() == Qt::RightButton && me->type() == QEvent::MouseButtonPress) { + int col = logicalIndexAt(me->pos()); + if (col >= 0) { + QAction action(tr("Stop watching"), 0); + QList<QAction *> actions; + actions << &action; + if (QMenu::exec(actions, me->globalPos())) + m_model->removeWatchAt(col); + } + } +} + + +//---------------------------------------------- + +WatchTableView::WatchTableView(WatchTableModel *model, QWidget *parent) + : QTableView(parent), + m_model(model) +{ + setAlternatingRowColors(true); + connect(model, SIGNAL(watchCreated(QmlDebugWatch*)), SLOT(watchCreated(QmlDebugWatch*))); + connect(this, SIGNAL(activated(QModelIndex)), SLOT(indexActivated(QModelIndex))); +} + +void WatchTableView::indexActivated(const QModelIndex &index) +{ + QmlDebugWatch *watch = m_model->findWatch(index.column()); + if (watch) + emit objectActivated(watch->objectDebugId()); +} + +void WatchTableView::watchCreated(QmlDebugWatch *watch) +{ + int column = m_model->columnForWatch(watch); + resizeColumnToContents(column); +} + +QT_END_NAMESPACE diff --git a/tools/qmldebugger/standalone/watchtable.h b/tools/qmldebugger/standalone/watchtable.h new file mode 100644 index 0000000..fd12d3d --- /dev/null +++ b/tools/qmldebugger/standalone/watchtable.h @@ -0,0 +1,154 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt QML Debugger 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 WATCHTABLEMODEL_H +#define WATCHTABLEMODEL_H + +#include <QtCore/qpointer.h> +#include <QtCore/qlist.h> + +#include <QWidget> +#include <QHeaderView> +#include <QAbstractTableModel> +#include <QTableView> + +QT_BEGIN_NAMESPACE + +class QmlDebugWatch; +class QmlEngineDebug; +class QmlDebugConnection; +class QmlDebugPropertyReference; +class QmlDebugObjectReference; + +class WatchTableModel : public QAbstractTableModel +{ + Q_OBJECT +public: + WatchTableModel(QmlEngineDebug *client = 0, QObject *parent = 0); + ~WatchTableModel(); + + void setEngineDebug(QmlEngineDebug *client); + + QmlDebugWatch *findWatch(int column) const; + int columnForWatch(QmlDebugWatch *watch) const; + + void removeWatchAt(int column); + void removeAllWatches(); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant headerData(int section, Qt::Orientation orientation, int role) const; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + +signals: + void watchCreated(QmlDebugWatch *watch); + +public slots: + void togglePropertyWatch(const QmlDebugObjectReference &obj, const QmlDebugPropertyReference &prop); + void expressionWatchRequested(const QmlDebugObjectReference &, const QString &); + +private slots: + void watchStateChanged(); + void watchedValueChanged(const QByteArray &propertyName, const QVariant &value); + +private: + void addWatch(QmlDebugWatch *watch, const QString &title); + void removeWatch(QmlDebugWatch *watch); + void updateWatch(QmlDebugWatch *watch, const QVariant &value); + + QmlDebugWatch *findWatch(int objectDebugId, const QString &property) const; + + void addValue(int column, const QVariant &value); + + struct WatchedEntity + { + QString title; + bool hasFirstValue; + QString property; + QPointer<QmlDebugWatch> watch; + }; + + struct Value { + int column; + QVariant variant; + bool first; + }; + + QmlEngineDebug *m_client; + QList<WatchedEntity> m_columns; + QList<Value> m_values; +}; + + +class WatchTableHeaderView : public QHeaderView +{ + Q_OBJECT +public: + WatchTableHeaderView(WatchTableModel *model, QWidget *parent = 0); + +protected: + void mousePressEvent(QMouseEvent *me); + +private: + WatchTableModel *m_model; +}; + + +class WatchTableView : public QTableView +{ + Q_OBJECT +public: + WatchTableView(WatchTableModel *model, QWidget *parent = 0); + +signals: + void objectActivated(int objectDebugId); + +private slots: + void indexActivated(const QModelIndex &index); + void watchCreated(QmlDebugWatch *watch); + +private: + WatchTableModel *m_model; +}; + + +QT_END_NAMESPACE + +#endif // WATCHTABLEMODEL_H diff --git a/tools/qmlviewer/main.cpp b/tools/qmlviewer/main.cpp new file mode 100644 index 0000000..ac0d732 --- /dev/null +++ b/tools/qmlviewer/main.cpp @@ -0,0 +1,300 @@ +/**************************************************************************** +** +** Copyright (C) 2009 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 "qml.h" +#include "qmlviewer.h" +#include <QWidget> +#include <QDir> +#include <QApplication> +#include <QTranslator> +#include <QDebug> + +void usage() +{ + qWarning("Usage: qmlviewer [options] <filename>"); + qWarning(" "); + qWarning(" options:"); + qWarning(" -v, -version ............................. display version"); + qWarning(" -frameless ............................... run with no window frame"); + qWarning(" -maximized................................ run maximized"); + qWarning(" -fullscreen............................... run fullscreen"); + qWarning(" -stayontop................................ keep viewer window on top"); + qWarning(" -skin <qvfbskindir> ...................... run with a skin window frame"); + qWarning(" \"list\" for a list of built-ins"); + qWarning(" -resizeview .............................. resize the view, not the skin"); + qWarning(" -recordfile <output> ..................... set video recording file"); + qWarning(" - ImageMagick 'convert' for GIF)"); + qWarning(" - png file for raw frames"); + qWarning(" - 'ffmpeg' for other formats"); + qWarning(" -recorddither ordered|threshold|floyd .... set GIF dither recording mode"); + qWarning(" -recordrate <fps> ........................ set recording frame rate"); + qWarning(" -record arg .............................. add a recording process argument"); + qWarning(" -autorecord [from-]<tomilliseconds> ...... set recording to start and stop"); + qWarning(" -devicekeys .............................. use numeric keys (see F1)"); + qWarning(" -netcache <size> ......................... set disk cache to size bytes"); + qWarning(" -translation <translationfile> ........... set the language to run in"); + qWarning(" -L <directory> ........................... prepend to the library search path"); + qWarning(" -opengl .................................. use a QGLWidget for the viewport"); + qWarning(" -script <path> ........................... set the script to use"); + qWarning(" -scriptopts <options>|help ............... set the script options to use"); + + qWarning(" "); + qWarning(" Press F1 for interactive help"); + exit(1); +} + +void scriptOptsUsage() +{ + qWarning("Usage: qmlviewer -scriptopts <option>[,<option>...] ..."); + qWarning(" options:"); + qWarning(" record ................................... record a new script"); + qWarning(" play ..................................... playback an existing script"); + qWarning(" testimages ............................... record images or compare images on playback"); + qWarning(" testerror ................................ test 'error' property of root item on playback"); + qWarning(" exitoncomplete ........................... cleanly exit the viewer on script completion"); + qWarning(" exitonfailure ............................ immediately exit the viewer on script failure"); + qWarning(" saveonexit ............................... save recording on viewer exit"); + qWarning(" "); + qWarning(" One of record, play or both must be specified."); + exit(1); +} + +int main(int argc, char ** argv) +{ + //### default to using raster graphics backend for now + bool gsSpecified = false; + for (int i = 0; i < argc; ++i) { + QString arg = argv[i]; + if (arg == "-graphicssystem") { + gsSpecified = true; + break; + } + } + if (!gsSpecified) + QApplication::setGraphicsSystem("raster"); + + QApplication app(argc, argv); + app.setApplicationName("viewer"); + app.setOrganizationName("Nokia"); + app.setOrganizationDomain("nokia.com"); + + bool frameless = false; + bool resizeview = false; + QString fileName; + double fps = 0; + int autorecord_from = 0; + int autorecord_to = 0; + QString dither = "none"; + QString recordfile; + QStringList recordargs; + QStringList libraries; + QString skin; + QString script; + QString scriptopts; + bool runScript = false; + bool devkeys = false; + int cache = 0; + QString translationFile; + bool useGL = false; + bool fullScreen = false; + bool stayOnTop = false; + bool maximized = false; + + for (int i = 1; i < argc; ++i) { + bool lastArg = (i == argc - 1); + QString arg = argv[i]; + if (arg == "-frameless") { + frameless = true; + } else if (arg == "-maximized") { + maximized = true; + } else if (arg == "-fullscreen") { + fullScreen = true; + } else if (arg == "-stayontop") { + stayOnTop = true; + } else if (arg == "-skin") { + if (lastArg) usage(); + skin = QString(argv[++i]); + } else if (arg == "-resizeview") { + resizeview = true; + } else if (arg == "-netcache") { + if (lastArg) usage(); + cache = QString(argv[++i]).toInt(); + } else if (arg == "-recordrate") { + if (lastArg) usage(); + fps = QString(argv[++i]).toDouble(); + } else if (arg == "-recordfile") { + if (lastArg) usage(); + recordfile = QString(argv[++i]); + } else if (arg == "-record") { + if (lastArg) usage(); + recordargs << QString(argv[++i]); + } else if (arg == "-recorddither") { + if (lastArg) usage(); + dither = QString(argv[++i]); + } else if (arg == "-autorecord") { + if (lastArg) usage(); + QString range = QString(argv[++i]); + int dash = range.indexOf('-'); + if (dash > 0) + autorecord_from = range.left(dash).toInt(); + autorecord_to = range.mid(dash+1).toInt(); + } else if (arg == "-devicekeys") { + devkeys = true; + } else if (arg == QLatin1String("-v") || arg == QLatin1String("-version")) { + fprintf(stderr, "Qt Declarative UI Viewer version %s\n", QT_VERSION_STR); + return 0; + } else if (arg == "-translation") { + if (lastArg) usage(); + translationFile = argv[++i]; + } else if (arg == "-opengl") { + useGL = true; + } else if (arg == "-L") { + if (lastArg) usage(); + libraries << QString(argv[++i]); + } else if (arg == "-script") { + if (lastArg) usage(); + script = QString(argv[++i]); + } else if (arg == "-scriptopts") { + if (lastArg) usage(); + scriptopts = QString(argv[++i]); + } else if (arg == "-savescript") { + if (lastArg) usage(); + script = QString(argv[++i]); + runScript = false; + } else if (arg == "-playscript") { + if (lastArg) usage(); + script = QString(argv[++i]); + runScript = true; + } else if (arg[0] != '-') { + fileName = arg; + } else if (1 || arg == "-help") { + usage(); + } + } + + QTranslator qmlTranslator; + if (!translationFile.isEmpty()) { + qmlTranslator.load(translationFile); + app.installTranslator(&qmlTranslator); + } + + Qt::WFlags wflags = (frameless ? Qt::FramelessWindowHint : Qt::Widget); + if (stayOnTop) + wflags |= Qt::WindowStaysOnTopHint; + + QmlViewer viewer(0, wflags); + if (!scriptopts.isEmpty()) { + QStringList options = + scriptopts.split(QLatin1Char(','), QString::SkipEmptyParts); + + QmlViewer::ScriptOptions scriptOptions = 0; + for (int i = 0; i < options.count(); ++i) { + const QString &option = options.at(i); + if (option == QLatin1String("help")) { + scriptOptsUsage(); + } else if (option == QLatin1String("play")) { + scriptOptions |= QmlViewer::Play; + } else if (option == QLatin1String("record")) { + scriptOptions |= QmlViewer::Record; + } else if (option == QLatin1String("testimages")) { + scriptOptions |= QmlViewer::TestImages; + } else if (option == QLatin1String("testerror")) { + scriptOptions |= QmlViewer::TestErrorProperty; + } else if (option == QLatin1String("exitoncomplete")) { + scriptOptions |= QmlViewer::ExitOnComplete; + } else if (option == QLatin1String("exitonfailure")) { + scriptOptions |= QmlViewer::ExitOnFailure; + } else if (option == QLatin1String("saveonexit")) { + scriptOptions |= QmlViewer::SaveOnExit; + } else { + scriptOptsUsage(); + } + } + + if (script.isEmpty()) + usage(); + + if (!(scriptOptions & QmlViewer::Record) && !(scriptOptions & QmlViewer::Play)) + scriptOptsUsage(); + viewer.setScriptOptions(scriptOptions); + viewer.setScript(script); + } else if (!script.isEmpty()) { + usage(); + } + + viewer.setUseGL(useGL); + foreach (QString lib, libraries) + viewer.addLibraryPath(lib); + viewer.setNetworkCacheSize(cache); + viewer.setRecordFile(recordfile); + if (resizeview) + viewer.setScaleView(); + if (fps>0) + viewer.setRecordRate(fps); + if (autorecord_to) + viewer.setAutoRecord(autorecord_from,autorecord_to); + if (!skin.isEmpty()) { + if (skin == "list") { + foreach (QString s, viewer.builtinSkins()) + qWarning(s.toUtf8()); + exit(0); + } else { + viewer.setSkin(skin); + } + } + if (devkeys) + viewer.setDeviceKeys(true); + viewer.setRecordDither(dither); + if (recordargs.count()) + viewer.setRecordArgs(recordargs); + if (fullScreen && maximized) + qWarning() << "Both -fullscreen and -maximized specified. Using -fullscreen."; + if (!fileName.isEmpty()) { + viewer.openQml(fileName); + fullScreen ? viewer.showFullScreen() : maximized ? viewer.showMaximized() : viewer.show(); + } else { + fullScreen ? viewer.showFullScreen() : maximized ? viewer.showMaximized() : viewer.show(); + viewer.open(); + } + viewer.raise(); + + return app.exec(); +} diff --git a/tools/qmlviewer/proxysettings.cpp b/tools/qmlviewer/proxysettings.cpp new file mode 100644 index 0000000..52fa74f --- /dev/null +++ b/tools/qmlviewer/proxysettings.cpp @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2009 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 <QIntValidator> +#include <QSettings> + +#include "proxysettings.h" + +ProxySettings::ProxySettings (QWidget * parent) + : QDialog (parent), Ui::ProxySettings() +{ + setupUi (this); + + proxyServerEdit->setInputMask ("000.000.000.000;_"); + QIntValidator *validator = new QIntValidator (0, 9999, this); + proxyPortEdit->setValidator (validator); + + QSettings settings; + proxyCheckBox->setChecked (settings.value ("http_proxy/use", 0).toBool ()); + proxyServerEdit->insert (settings.value ("http_proxy/hostname", "").toString ()); + proxyPortEdit->insert (settings.value ("http_proxy/port", "80").toString ()); + usernameEdit->insert (settings.value ("http_proxy/username", "").toString ()); + passwordEdit->insert (settings.value ("http_proxy/password", "").toString ()); +} + +ProxySettings::~ProxySettings() +{ +} + +void ProxySettings::accept () +{ + QSettings settings; + + settings.setValue ("http_proxy/use", proxyCheckBox->isChecked ()); + settings.setValue ("http_proxy/hostname", proxyServerEdit->text ()); + settings.setValue ("http_proxy/port", proxyPortEdit->text ()); + settings.setValue ("http_proxy/username", usernameEdit->text ()); + settings.setValue ("http_proxy/password", passwordEdit->text ()); + + QDialog::accept (); +} + +QNetworkProxy ProxySettings::httpProxy () +{ + QSettings settings; + QNetworkProxy proxy; + + bool proxyInUse = settings.value ("http_proxy/use", 0).toBool (); + if (proxyInUse) { + proxy.setType (QNetworkProxy::HttpProxy); + proxy.setHostName (settings.value ("http_proxy/hostname", "").toString ());// "192.168.220.5" + proxy.setPort (settings.value ("http_proxy/port", 80).toInt ()); // 8080 + proxy.setUser (settings.value ("http_proxy/username", "").toString ()); + proxy.setPassword (settings.value ("http_proxy/password", "").toString ()); + //QNetworkProxy::setApplicationProxy (proxy); + } + else { + proxy.setType (QNetworkProxy::NoProxy); + } + return proxy; +} + +bool ProxySettings::httpProxyInUse() +{ + QSettings settings; + return settings.value ("http_proxy/use", 0).toBool (); +} diff --git a/tools/qmlviewer/proxysettings.h b/tools/qmlviewer/proxysettings.h new file mode 100644 index 0000000..b2f3e25 --- /dev/null +++ b/tools/qmlviewer/proxysettings.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2009 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 PROXYSETTINGS_H +#define PROXYSETTINGS_H + +#include <QDialog> +#include <QNetworkProxy> +#include "ui_proxysettings.h" + +/** +*/ +class ProxySettings : public QDialog, public Ui::ProxySettings +{ + +Q_OBJECT + +public: + ProxySettings(QWidget * parent = 0); + + ~ProxySettings(); + + static QNetworkProxy httpProxy (); + static bool httpProxyInUse (); + +public slots: + virtual void accept (); +}; + +#endif // PROXYSETTINGS_H diff --git a/tools/qmlviewer/proxysettings.ui b/tools/qmlviewer/proxysettings.ui new file mode 100644 index 0000000..84e39fe --- /dev/null +++ b/tools/qmlviewer/proxysettings.ui @@ -0,0 +1,115 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ProxySettings</class> + <widget class="QDialog" name="ProxySettings"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>318</width> + <height>199</height> + </rect> + </property> + <property name="windowTitle"> + <string>Dialog</string> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0" colspan="2"> + <widget class="QCheckBox" name="proxyCheckBox"> + <property name="text"> + <string>Use http proxy</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="serverAddressLabel"> + <property name="text"> + <string>Server Address:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLineEdit" name="proxyServerEdit"/> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Port:</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QLineEdit" name="proxyPortEdit"/> + </item> + <item row="3" column="0"> + <widget class="QLabel" name="usernameLabel"> + <property name="text"> + <string>Username:</string> + </property> + </widget> + </item> + <item row="3" column="1"> + <widget class="QLineEdit" name="usernameEdit"/> + </item> + <item row="4" column="0"> + <widget class="QLabel" name="passwordLabel"> + <property name="text"> + <string>Password:</string> + </property> + </widget> + </item> + <item row="4" column="1"> + <widget class="QLineEdit" name="passwordEdit"> + <property name="echoMode"> + <enum>QLineEdit::Password</enum> + </property> + </widget> + </item> + <item row="5" column="0" colspan="2"> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>ProxySettings</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>ProxySettings</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/tools/qmlviewer/qfxtester.cpp b/tools/qmlviewer/qfxtester.cpp new file mode 100644 index 0000000..5484771 --- /dev/null +++ b/tools/qmlviewer/qfxtester.cpp @@ -0,0 +1,378 @@ +/**************************************************************************** +** +** Copyright (C) 2009 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 <qfxtester.h> +#include <QDebug> +#include <QApplication> +#include <qmlview.h> +#include <QFile> +#include <QmlComponent> +#include <QDir> +#include <QCryptographicHash> +#include <private/qabstractanimation_p.h> +#include <private/qmlgraphicsitem_p.h> + +QT_BEGIN_NAMESPACE + +QML_DEFINE_TYPE(Qt.VisualTest, 4,6, VisualTest, QmlGraphicsVisualTest); +QML_DEFINE_TYPE(Qt.VisualTest, 4,6, Frame, QmlGraphicsVisualTestFrame); +QML_DEFINE_TYPE(Qt.VisualTest, 4,6, Mouse, QmlGraphicsVisualTestMouse); +QML_DEFINE_TYPE(Qt.VisualTest, 4,6, Key, QmlGraphicsVisualTestKey); + +QmlGraphicsTester::QmlGraphicsTester(const QString &script, QmlViewer::ScriptOptions opts, + QmlView *parent) +: QAbstractAnimation(parent), m_script(script), m_view(parent), filterEvents(true), options(opts), + testscript(0), hasCompleted(false), hasFailed(false) +{ + parent->viewport()->installEventFilter(this); + parent->installEventFilter(this); + QUnifiedTimer::instance()->setConsistentTiming(true); + if (options & QmlViewer::Play) + this->run(); + start(); +} + +QmlGraphicsTester::~QmlGraphicsTester() +{ + if (!hasFailed && + options & QmlViewer::Record && + options & QmlViewer::SaveOnExit) + save(); +} + +int QmlGraphicsTester::duration() const +{ + return -1; +} + +void QmlGraphicsTester::addMouseEvent(Destination dest, QMouseEvent *me) +{ + MouseEvent e(me); + e.destination = dest; + m_mouseEvents << e; +} + +void QmlGraphicsTester::addKeyEvent(Destination dest, QKeyEvent *ke) +{ + KeyEvent e(ke); + e.destination = dest; + m_keyEvents << e; +} + +bool QmlGraphicsTester::eventFilter(QObject *o, QEvent *e) +{ + if (!filterEvents) + return false; + + Destination destination; + if (o == m_view) { + destination = View; + } else if (o == m_view->viewport()) { + destination = ViewPort; + } else { + return false; + } + + switch (e->type()) { + case QEvent::KeyPress: + case QEvent::KeyRelease: + addKeyEvent(destination, (QKeyEvent *)e); + return true; + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::MouseMove: + case QEvent::MouseButtonDblClick: + addMouseEvent(destination, (QMouseEvent *)e); + return true; + default: + break; + } + return false; +} + +void QmlGraphicsTester::executefailure() +{ + hasFailed = true; + + if (options & QmlViewer::ExitOnFailure) + exit(-1); +} + +void QmlGraphicsTester::imagefailure() +{ + hasFailed = true; + + if (options & QmlViewer::ExitOnFailure) + exit(-1); +} + +void QmlGraphicsTester::complete() +{ + if ((options & QmlViewer::TestErrorProperty) && !hasFailed) { + QString e = m_view->root()->property("error").toString(); + if (!e.isEmpty()) { + qWarning() << "Test failed:" << e; + hasFailed = true; + } + } + if (options & QmlViewer::ExitOnComplete) + QApplication::exit(hasFailed?-1:0); + + if (hasCompleted) + return; + hasCompleted = true; + + if (options & QmlViewer::Play) + qWarning("Script playback complete"); +} + +void QmlGraphicsTester::run() +{ + QmlComponent c(m_view->engine(), m_script + QLatin1String(".qml")); + + testscript = qobject_cast<QmlGraphicsVisualTest *>(c.create()); + if (testscript) testscript->setParent(this); + else executefailure(); + testscriptidx = 0; +} + +void QmlGraphicsTester::save() +{ + QString filename = m_script + QLatin1String(".qml"); + QFileInfo filenameInfo(filename); + QDir saveDir = filenameInfo.absoluteDir(); + saveDir.mkpath("."); + + QFile file(filename); + file.open(QIODevice::WriteOnly); + QTextStream ts(&file); + + ts << "import Qt.VisualTest 4.6\n\n"; + ts << "VisualTest {\n"; + + int imgCount = 0; + QList<KeyEvent> keyevents = m_savedKeyEvents; + QList<MouseEvent> mouseevents = m_savedMouseEvents; + for (int ii = 0; ii < m_savedFrameEvents.count(); ++ii) { + const FrameEvent &fe = m_savedFrameEvents.at(ii); + ts << " Frame {\n"; + ts << " msec: " << fe.msec << "\n"; + if (!fe.hash.isEmpty()) { + ts << " hash: \"" << fe.hash.toHex() << "\"\n"; + } else if (!fe.image.isNull()) { + QString filename = filenameInfo.baseName() + "." + QString::number(imgCount) + ".png"; + fe.image.save(m_script + "." + QString::number(imgCount) + ".png"); + imgCount++; + ts << " image: \"" << filename << "\"\n"; + } + ts << " }\n"; + + while (!mouseevents.isEmpty() && + mouseevents.first().msec == fe.msec) { + MouseEvent me = mouseevents.takeFirst(); + + ts << " Mouse {\n"; + ts << " type: " << me.type << "\n"; + ts << " button: " << me.button << "\n"; + ts << " buttons: " << me.buttons << "\n"; + ts << " x: " << me.pos.x() << "; y: " << me.pos.y() << "\n"; + ts << " modifiers: " << me.modifiers << "\n"; + if (me.destination == ViewPort) + ts << " sendToViewport: true\n"; + ts << " }\n"; + } + + while (!keyevents.isEmpty() && + keyevents.first().msec == fe.msec) { + KeyEvent ke = keyevents.takeFirst(); + + ts << " Key {\n"; + ts << " type: " << ke.type << "\n"; + ts << " key: " << ke.key << "\n"; + ts << " modifiers: " << ke.modifiers << "\n"; + ts << " text: \"" << ke.text.toUtf8().toHex() << "\"\n"; + ts << " autorep: " << (ke.autorep?"true":"false") << "\n"; + ts << " count: " << ke.count << "\n"; + if (ke.destination == ViewPort) + ts << " sendToViewport: true\n"; + ts << " }\n"; + } + } + + ts << "}\n"; + file.close(); +} + +void QmlGraphicsTester::updateCurrentTime(int msec) +{ + QmlGraphicsItemPrivate::setConsistentTime(msec); + + QImage img(m_view->width(), m_view->height(), QImage::Format_RGB32); + + if (options & QmlViewer::TestImages) { + img.fill(qRgb(255,255,255)); + QPainter p(&img); + m_view->render(&p); + } + + FrameEvent fe; + fe.msec = msec; + if (msec == 0 || !(options & QmlViewer::TestImages)) { + // Skip first frame, skip if not doing images + } else if (0 == (m_savedFrameEvents.count() % 60)) { + fe.image = img; + } else { + QCryptographicHash hash(QCryptographicHash::Md5); + hash.addData((const char *)img.bits(), img.bytesPerLine() * img.height()); + fe.hash = hash.result(); + } + m_savedFrameEvents.append(fe); + + // Deliver mouse events + filterEvents = false; + + if (!testscript) { + for (int ii = 0; ii < m_mouseEvents.count(); ++ii) { + MouseEvent &me = m_mouseEvents[ii]; + me.msec = msec; + QMouseEvent event(me.type, me.pos, me.button, me.buttons, me.modifiers); + + if (me.destination == View) { + QCoreApplication::sendEvent(m_view, &event); + } else { + QCoreApplication::sendEvent(m_view->viewport(), &event); + } + } + + for (int ii = 0; ii < m_keyEvents.count(); ++ii) { + KeyEvent &ke = m_keyEvents[ii]; + ke.msec = msec; + QKeyEvent event(ke.type, ke.key, ke.modifiers, ke.text, ke.autorep, ke.count); + + if (ke.destination == View) { + QCoreApplication::sendEvent(m_view, &event); + } else { + QCoreApplication::sendEvent(m_view->viewport(), &event); + } + } + m_savedMouseEvents.append(m_mouseEvents); + m_savedKeyEvents.append(m_keyEvents); + } + + m_mouseEvents.clear(); + m_keyEvents.clear(); + + // Advance test script + static int imgCount = 0; + while (testscript && testscript->count() > testscriptidx) { + + QObject *event = testscript->event(testscriptidx); + + if (QmlGraphicsVisualTestFrame *frame = qobject_cast<QmlGraphicsVisualTestFrame *>(event)) { + if (frame->msec() < msec) { + if (options & QmlViewer::TestImages && !(options & QmlViewer::Record)) { + qWarning() << "QmlGraphicsTester: Extra frame. Seen:" + << msec << "Expected:" << frame->msec(); + imagefailure(); + } + } else if (frame->msec() == msec) { + if (!frame->hash().isEmpty() && frame->hash().toUtf8() != fe.hash.toHex()) { + if (options & QmlViewer::TestImages && !(options & QmlViewer::Record)) { + qWarning() << "QmlGraphicsTester: Mismatched frame hash. Seen:" + << fe.hash.toHex() << "Expected:" + << frame->hash().toUtf8(); + imagefailure(); + } + } + } else if (frame->msec() > msec) { + break; + } + + if (options & QmlViewer::TestImages && !(options & QmlViewer::Record) && !frame->image().isEmpty()) { + QImage goodImage(frame->image().toLocalFile()); + if (goodImage != img) { + QString reject(frame->image().toLocalFile() + ".reject.png"); + qWarning() << "QmlGraphicsTester: Image mismatch. Reject saved to:" + << reject; + img.save(reject); + imagefailure(); + } + } + } else if (QmlGraphicsVisualTestMouse *mouse = qobject_cast<QmlGraphicsVisualTestMouse *>(event)) { + QPoint pos(mouse->x(), mouse->y()); + QPoint globalPos = m_view->mapToGlobal(QPoint(0, 0)) + pos; + QMouseEvent event((QEvent::Type)mouse->type(), pos, globalPos, (Qt::MouseButton)mouse->button(), (Qt::MouseButtons)mouse->buttons(), (Qt::KeyboardModifiers)mouse->modifiers()); + + MouseEvent me(&event); + me.msec = msec; + if (!mouse->sendToViewport()) { + QCoreApplication::sendEvent(m_view, &event); + me.destination = View; + } else { + QCoreApplication::sendEvent(m_view->viewport(), &event); + me.destination = ViewPort; + } + m_savedMouseEvents.append(me); + } else if (QmlGraphicsVisualTestKey *key = qobject_cast<QmlGraphicsVisualTestKey *>(event)) { + + QKeyEvent event((QEvent::Type)key->type(), key->key(), (Qt::KeyboardModifiers)key->modifiers(), QString::fromUtf8(QByteArray::fromHex(key->text().toUtf8())), key->autorep(), key->count()); + + KeyEvent ke(&event); + ke.msec = msec; + if (!key->sendToViewport()) { + QCoreApplication::sendEvent(m_view, &event); + ke.destination = View; + } else { + QCoreApplication::sendEvent(m_view->viewport(), &event); + ke.destination = ViewPort; + } + m_savedKeyEvents.append(ke); + } + testscriptidx++; + } + + filterEvents = true; + + if (testscript && testscript->count() <= testscriptidx) + complete(); +} + +QT_END_NAMESPACE diff --git a/tools/qmlviewer/qfxtester.h b/tools/qmlviewer/qfxtester.h new file mode 100644 index 0000000..303ac3a --- /dev/null +++ b/tools/qmlviewer/qfxtester.h @@ -0,0 +1,262 @@ +/**************************************************************************** +** +** Copyright (C) 2009 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 QFXTESTER_H +#define QFXTESTER_H + +#include <QEvent> +#include <QMouseEvent> +#include <QKeyEvent> +#include <qmlviewer.h> + +QT_BEGIN_NAMESPACE + +class QmlGraphicsVisualTest : public QObject +{ + Q_OBJECT + Q_PROPERTY(QList<QObject *>* events READ events CONSTANT) + Q_CLASSINFO("DefaultProperty", "events") +public: + QmlGraphicsVisualTest() {} + + QList<QObject *> *events() { return &m_events; } + + int count() const { return m_events.count(); } + QObject *event(int idx) { return m_events.at(idx); } + +private: + QList<QObject *> m_events; +}; +QML_DECLARE_TYPE(QmlGraphicsVisualTest) + +class QmlGraphicsVisualTestFrame : public QObject +{ + Q_OBJECT + Q_PROPERTY(int msec READ msec WRITE setMsec) + Q_PROPERTY(QString hash READ hash WRITE setHash) + Q_PROPERTY(QUrl image READ image WRITE setImage) +public: + QmlGraphicsVisualTestFrame() : m_msec(-1) {} + + int msec() const { return m_msec; } + void setMsec(int m) { m_msec = m; } + + QString hash() const { return m_hash; } + void setHash(const QString &hash) { m_hash = hash; } + + QUrl image() const { return m_image; } + void setImage(const QUrl &image) { m_image = image; } + +private: + int m_msec; + QString m_hash; + QUrl m_image; +}; +QML_DECLARE_TYPE(QmlGraphicsVisualTestFrame) + +class QmlGraphicsVisualTestMouse : public QObject +{ + Q_OBJECT + Q_PROPERTY(int type READ type WRITE setType) + Q_PROPERTY(int button READ button WRITE setButton) + Q_PROPERTY(int buttons READ buttons WRITE setButtons) + Q_PROPERTY(int x READ x WRITE setX) + Q_PROPERTY(int y READ y WRITE setY) + Q_PROPERTY(int modifiers READ modifiers WRITE setModifiers) + Q_PROPERTY(bool sendToViewport READ sendToViewport WRITE setSendToViewport) +public: + QmlGraphicsVisualTestMouse() : m_type(0), m_button(0), m_buttons(0), m_x(0), m_y(0), m_modifiers(0), m_viewport(false) {} + + int type() const { return m_type; } + void setType(int t) { m_type = t; } + + int button() const { return m_button; } + void setButton(int b) { m_button = b; } + + int buttons() const { return m_buttons; } + void setButtons(int b) { m_buttons = b; } + + int x() const { return m_x; } + void setX(int x) { m_x = x; } + + int y() const { return m_y; } + void setY(int y) { m_y = y; } + + int modifiers() const { return m_modifiers; } + void setModifiers(int modifiers) { m_modifiers = modifiers; } + + bool sendToViewport() const { return m_viewport; } + void setSendToViewport(bool v) { m_viewport = v; } +private: + int m_type; + int m_button; + int m_buttons; + int m_x; + int m_y; + int m_modifiers; + bool m_viewport; +}; +QML_DECLARE_TYPE(QmlGraphicsVisualTestMouse) + +class QmlGraphicsVisualTestKey : public QObject +{ + Q_OBJECT + Q_PROPERTY(int type READ type WRITE setType) + Q_PROPERTY(int key READ key WRITE setKey) + Q_PROPERTY(int modifiers READ modifiers WRITE setModifiers) + Q_PROPERTY(QString text READ text WRITE setText) + Q_PROPERTY(bool autorep READ autorep WRITE setAutorep) + Q_PROPERTY(int count READ count WRITE setCount) + Q_PROPERTY(bool sendToViewport READ sendToViewport WRITE setSendToViewport) +public: + QmlGraphicsVisualTestKey() : m_type(0), m_key(0), m_modifiers(0), m_autorep(false), m_count(0), m_viewport(false) {} + + int type() const { return m_type; } + void setType(int t) { m_type = t; } + + int key() const { return m_key; } + void setKey(int k) { m_key = k; } + + int modifiers() const { return m_modifiers; } + void setModifiers(int m) { m_modifiers = m; } + + QString text() const { return m_text; } + void setText(const QString &t) { m_text = t; } + + bool autorep() const { return m_autorep; } + void setAutorep(bool a) { m_autorep = a; } + + int count() const { return m_count; } + void setCount(int c) { m_count = c; } + + bool sendToViewport() const { return m_viewport; } + void setSendToViewport(bool v) { m_viewport = v; } +private: + int m_type; + int m_key; + int m_modifiers; + QString m_text; + bool m_autorep; + int m_count; + bool m_viewport; +}; +QML_DECLARE_TYPE(QmlGraphicsVisualTestKey) + +class QmlGraphicsTester : public QAbstractAnimation +{ +public: + QmlGraphicsTester(const QString &script, QmlViewer::ScriptOptions options, QmlView *parent); + ~QmlGraphicsTester(); + + virtual int duration() const; + + void run(); + void save(); + + void executefailure(); +protected: + virtual void updateCurrentTime(int msecs); + virtual bool eventFilter(QObject *, QEvent *); + +private: + QString m_script; + + void imagefailure(); + void complete(); + + enum Destination { View, ViewPort }; + void addKeyEvent(Destination, QKeyEvent *); + void addMouseEvent(Destination, QMouseEvent *); + QmlView *m_view; + + struct MouseEvent { + MouseEvent(QMouseEvent *e) + : type(e->type()), button(e->button()), buttons(e->buttons()), + pos(e->pos()), modifiers(e->modifiers()), destination(View) {} + + QEvent::Type type; + Qt::MouseButton button; + Qt::MouseButtons buttons; + QPoint pos; + Qt::KeyboardModifiers modifiers; + Destination destination; + + int msec; + }; + struct KeyEvent { + KeyEvent(QKeyEvent *e) + : type(e->type()), key(e->key()), modifiers(e->modifiers()), text(e->text()), + autorep(e->isAutoRepeat()), count(e->count()), destination(View) {} + QEvent::Type type; + int key; + Qt::KeyboardModifiers modifiers; + QString text; + bool autorep; + ushort count; + Destination destination; + + int msec; + }; + struct FrameEvent { + QImage image; + QByteArray hash; + int msec; + }; + QList<MouseEvent> m_mouseEvents; + QList<KeyEvent> m_keyEvents; + + QList<MouseEvent> m_savedMouseEvents; + QList<KeyEvent> m_savedKeyEvents; + QList<FrameEvent> m_savedFrameEvents; + bool filterEvents; + + QmlViewer::ScriptOptions options; + int testscriptidx; + QmlGraphicsVisualTest *testscript; + + bool hasCompleted; + bool hasFailed; +}; + + +QT_END_NAMESPACE + +#endif // QFXTESTER_H diff --git a/tools/qmlviewer/qmlviewer.cpp b/tools/qmlviewer/qmlviewer.cpp new file mode 100644 index 0000000..ea0936b --- /dev/null +++ b/tools/qmlviewer/qmlviewer.cpp @@ -0,0 +1,1160 @@ +/**************************************************************************** +** +** Copyright (C) 2009 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 <qmlview.h> +#include "ui_recopts.h" + +#include "qmlviewer.h" +#include <QtDeclarative/qmlcontext.h> +#include <QtDeclarative/qmlengine.h> +#include "qml.h" +#include <private/qperformancelog_p_p.h> +#include <private/qabstractanimation_p.h> +#include <QAbstractAnimation> +#include "deviceskin.h" + +#include <QSettings> +#include <QNetworkCookieJar> +#include <QNetworkDiskCache> +#include <QNetworkAccessManager> +#include <QSignalMapper> +#include <QmlComponent> +#include <QWidget> +#include <QApplication> +#include <QDir> +#include <QTextBrowser> +#include <QFile> +#include <QFileInfo> +#include <QVBoxLayout> +#include <QProgressDialog> +#include <QProcess> +#include <QMenuBar> +#include <QMenu> +#include <QAction> +#include <QFileDialog> +#include <QTimer> +#include <QNetworkProxyFactory> +#include <QKeyEvent> +#include "proxysettings.h" + +#ifdef GL_SUPPORTED +#include <QGLWidget> +#endif + +#include <qfxtester.h> + +QT_BEGIN_NAMESPACE + + +class SizedMenuBar : public QMenuBar +{ + Q_OBJECT +public: + SizedMenuBar(QWidget *parent, QWidget *referenceWidget) + : QMenuBar(parent), refWidget(referenceWidget) + { + } + + virtual QSize sizeHint() const + { + return QSize(refWidget->sizeHint().width(), QMenuBar::sizeHint().height()); + } + +private: + QWidget *refWidget; +}; + + +class PreviewDeviceSkin : public DeviceSkin +{ + Q_OBJECT +public: + explicit PreviewDeviceSkin(const DeviceSkinParameters ¶meters, QWidget *parent); + + void setPreview(QWidget *formWidget); + void setPreviewAndScale(QWidget *formWidget); + + void setScreenSize(const QSize& size) + { + QMatrix fit; + fit = fit.scale(qreal(size.width())/m_screenSize.width(), + qreal(size.height())/m_screenSize.height()); + setTransform(fit); + QApplication::syncX(); + } + + QSize standardScreenSize() const { return m_screenSize; } + + QMenu* menu; + +private slots: + void slotSkinKeyPressEvent(int code, const QString& text, bool autorep); + void slotSkinKeyReleaseEvent(int code, const QString& text, bool autorep); + void slotPopupMenu(); + +private: + const QSize m_screenSize; +}; + + +PreviewDeviceSkin::PreviewDeviceSkin(const DeviceSkinParameters ¶meters, QWidget *parent) : + DeviceSkin(parameters, parent), + m_screenSize(parameters.screenSize()) +{ + menu = new QMenu(this); + connect(this, SIGNAL(skinKeyPressEvent(int,QString,bool)), + this, SLOT(slotSkinKeyPressEvent(int,QString,bool))); + connect(this, SIGNAL(skinKeyReleaseEvent(int,QString,bool)), + this, SLOT(slotSkinKeyReleaseEvent(int,QString,bool))); + connect(this, SIGNAL(popupMenu()), this, SLOT(slotPopupMenu())); +} + +void PreviewDeviceSkin::setPreview(QWidget *formWidget) +{ + formWidget->setFixedSize(m_screenSize); + formWidget->setParent(this, Qt::SubWindow); + formWidget->setAutoFillBackground(true); + setView(formWidget); +} + +void PreviewDeviceSkin::setPreviewAndScale(QWidget *formWidget) +{ + setScreenSize(formWidget->sizeHint()); + formWidget->setParent(this, Qt::SubWindow); + formWidget->setAutoFillBackground(true); + setView(formWidget); +} + +void PreviewDeviceSkin::slotSkinKeyPressEvent(int code, const QString& text, bool autorep) +{ + if (QWidget *focusWidget = QApplication::focusWidget()) { + QKeyEvent e(QEvent::KeyPress,code,0,text,autorep); + QApplication::sendEvent(focusWidget, &e); + } + +} + +void PreviewDeviceSkin::slotSkinKeyReleaseEvent(int code, const QString& text, bool autorep) +{ + if (QWidget *focusWidget = QApplication::focusWidget()) { + QKeyEvent e(QEvent::KeyRelease,code,0,text,autorep); + QApplication::sendEvent(focusWidget, &e); + } +} + +void PreviewDeviceSkin::slotPopupMenu() +{ + menu->exec(QCursor::pos()); +} + +static struct { const char *name, *args; } ffmpegprofiles[] = { + {"Maximum Quality", "-sameq"}, + {"High Quality", "-qmax 2"}, + {"Medium Quality", "-qmax 6"}, + {"Low Quality", "-qmax 16"}, + {"Custom ffmpeg arguments", ""}, + {0,0} +}; + +class RecordingDialog : public QDialog, public Ui::RecordingOptions { + Q_OBJECT + +public: + RecordingDialog(QWidget *parent) : QDialog(parent) + { + setupUi(this); + hz->setValidator(new QDoubleValidator(hz)); + for (int i=0; ffmpegprofiles[i].name; ++i) { + profile->addItem(ffmpegprofiles[i].name); + } + } + + void setArguments(QString a) + { + int i; + for (i=0; ffmpegprofiles[i].args[0]; ++i) { + if (ffmpegprofiles[i].args == a) { + profile->setCurrentIndex(i); + args->setText(QLatin1String(ffmpegprofiles[i].args)); + return; + } + } + customargs = a; + args->setText(a); + profile->setCurrentIndex(i); + } + + QString arguments() const + { + int i = profile->currentIndex(); + return ffmpegprofiles[i].args[0] ? QLatin1String(ffmpegprofiles[i].args) : customargs; + } + +private slots: + void pickProfile(int i) + { + if (ffmpegprofiles[i].args[0]) { + args->setText(QLatin1String(ffmpegprofiles[i].args)); + } else { + args->setText(customargs); + } + } + + void storeCustomArgs(QString s) + { + setArguments(s); + } + +private: + QString customargs; +}; + +class ConfiguredNetworkAccessManager : public QNetworkAccessManager { +public: + ConfiguredNetworkAccessManager(QObject *parent) : QNetworkAccessManager(parent) { } + + QNetworkReply *createRequest (Operation op, const QNetworkRequest &req, QIODevice * outgoingData) + { + QNetworkRequest request = req; + request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache); + request.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); + return QNetworkAccessManager::createRequest(op,request,outgoingData); + } +}; + +class PersistentCookieJar : public QNetworkCookieJar { +public: + PersistentCookieJar(QObject *parent) : QNetworkCookieJar(parent) { load(); } + ~PersistentCookieJar() { save(); } + +private: + void save() + { + QList<QNetworkCookie> list = allCookies(); + QByteArray data; + foreach (QNetworkCookie cookie, list) { + if (!cookie.isSessionCookie()) { + data.append(cookie.toRawForm()); + data.append("\n"); + } + } + QSettings settings("Nokia", "QtQmlViewer"); + settings.setValue("Cookies",data); + } + + void load() + { + QSettings settings("Nokia", "QtQmlViewer"); + QByteArray data = settings.value("Cookies").toByteArray(); + setAllCookies(QNetworkCookie::parseCookies(data)); + } +}; + +QString QmlViewer::getVideoFileName() +{ + QString title = convertAvailable || ffmpegAvailable ? tr("Save Video File") : tr("Save PNG Frames"); + QStringList types; + if (ffmpegAvailable) types += tr("Common Video files")+QLatin1String(" (*.avi *.mpeg *.mov)"); + if (convertAvailable) types += tr("GIF Animation")+QLatin1String(" (*.gif)"); + types += tr("Individual PNG frames")+QLatin1String(" (*.png)"); + if (ffmpegAvailable) types += tr("All ffmpeg formats (*.*)"); + return QFileDialog::getSaveFileName(this, title, "", types.join(";; ")); +} + + +QmlViewer::QmlViewer(QWidget *parent, Qt::WindowFlags flags) + : QWidget(parent, flags), frame_stream(0), scaleSkin(true), mb(0), m_scriptOptions(0), + tester(0) +{ + devicemode = false; + skin = 0; + canvas = 0; + record_autotime = 0; + record_rate = 50; + record_args += QLatin1String("-sameq"); + + recdlg = new RecordingDialog(this); + connect(recdlg->pickfile, SIGNAL(clicked()), this, SLOT(pickRecordingFile())); + senseFfmpeg(); + senseImageMagick(); + if (!ffmpegAvailable) + recdlg->ffmpegOptions->hide(); + if (!ffmpegAvailable && !convertAvailable) + recdlg->rateOptions->hide(); + QString warn; + if (!ffmpegAvailable) { + if (!convertAvailable) + warn = tr("ffmpeg and ImageMagick not available - no video output"); + else + warn = tr("ffmpeg not available - GIF and PNG outputs only"); + recdlg->warning->setText(warn); + } else { + recdlg->warning->hide(); + } + + canvas = new QmlView(this); + canvas->setAttribute(Qt::WA_OpaquePaintEvent); + canvas->setAttribute(Qt::WA_NoSystemBackground); + canvas->setContentResizable(!skin || !scaleSkin); + canvas->engine()->setNetworkAccessManager(new ConfiguredNetworkAccessManager(canvas->engine())); + canvas->setFocus(); + + QObject::connect(canvas, SIGNAL(sceneResized(QSize)), this, SLOT(sceneResized(QSize))); + QObject::connect(canvas, SIGNAL(initialSize(QSize)), this, SLOT(adjustSizeSlot())); + QObject::connect(canvas, SIGNAL(errors(QList<QmlError>)), this, SLOT(executeErrors())); + + if (!(flags & Qt::FramelessWindowHint)) + createMenu(menuBar(),0); + + QVBoxLayout *layout = new QVBoxLayout; + layout->setMargin(0); + layout->setSpacing(0); + setLayout(layout); + if (mb) + layout->addWidget(mb); + layout->addWidget(canvas); + + setupProxy(); + canvas->engine()->networkAccessManager()->setCookieJar(new PersistentCookieJar(this)); + + connect(&autoStartTimer, SIGNAL(triggered()), this, SLOT(autoStartRecording())); + connect(&autoStopTimer, SIGNAL(triggered()), this, SLOT(autoStopRecording())); + connect(&recordTimer, SIGNAL(triggered()), this, SLOT(recordFrame())); + autoStartTimer.setRunning(false); + autoStopTimer.setRunning(false); + recordTimer.setRunning(false); + recordTimer.setRepeating(true); +} + +void QmlViewer::adjustSizeSlot() +{ + resize(sizeHint()); +} + +QMenuBar *QmlViewer::menuBar() const +{ + if (!mb) + mb = new SizedMenuBar((QWidget*)this, canvas); + + return mb; +} + +void QmlViewer::createMenu(QMenuBar *menu, QMenu *flatmenu) +{ + QObject *parent = flatmenu ? (QObject*)flatmenu : (QObject*)menu; + + QMenu *fileMenu = flatmenu ? flatmenu : menu->addMenu(tr("&File")); + + QAction *openAction = new QAction(tr("&Open..."), parent); + openAction->setShortcut(QKeySequence("Ctrl+O")); + connect(openAction, SIGNAL(triggered()), this, SLOT(open())); + fileMenu->addAction(openAction); + + QAction *reloadAction = new QAction(tr("&Reload"), parent); + reloadAction->setShortcut(QKeySequence("Ctrl+R")); + connect(reloadAction, SIGNAL(triggered()), this, SLOT(reload())); + fileMenu->addAction(reloadAction); + + if (flatmenu) flatmenu->addSeparator(); + + QMenu *recordMenu = flatmenu ? flatmenu : menu->addMenu(tr("&Recording")); + + QAction *snapshotAction = new QAction(tr("&Take Snapshot\tF3"), parent); + connect(snapshotAction, SIGNAL(triggered()), this, SLOT(takeSnapShot())); + recordMenu->addAction(snapshotAction); + + recordAction = new QAction(tr("Start Recording &Video\tF9"), parent); + connect(recordAction, SIGNAL(triggered()), this, SLOT(toggleRecordingWithSelection())); + recordMenu->addAction(recordAction); + + QAction *recordOptions = new QAction(tr("Video &Options..."), parent); + connect(recordOptions, SIGNAL(triggered()), this, SLOT(chooseRecordingOptions())); + + if (flatmenu) + flatmenu->addAction(recordOptions); + + if (flatmenu) flatmenu->addSeparator(); + + QMenu *debugMenu = flatmenu ? flatmenu->addMenu(tr("&Debugging")) : menu->addMenu(tr("&Debugging")); + + QAction *slowAction = new QAction(tr("&Slow Down Animations"), parent); + slowAction->setShortcut(QKeySequence("Ctrl+.")); + slowAction->setCheckable(true); + connect(slowAction, SIGNAL(triggered(bool)), this, SLOT(setSlowMode(bool))); + debugMenu->addAction(slowAction); + + if (flatmenu) flatmenu->addSeparator(); + + QMenu *skinMenu = flatmenu ? flatmenu->addMenu(tr("&Skin")) : menu->addMenu(tr("&Skin")); + + QActionGroup *skinActions; + QAction *skinAction; + + skinActions = new QActionGroup(parent); + skinAction = new QAction(tr("Scale skin"), parent); + skinAction->setCheckable(true); + skinAction->setChecked(scaleSkin); + skinActions->addAction(skinAction); + skinMenu->addAction(skinAction); + connect(skinAction, SIGNAL(triggered()), this, SLOT(setScaleSkin())); + skinAction = new QAction(tr("Resize view"), parent); + skinAction->setCheckable(true); + skinAction->setChecked(!scaleSkin); + skinActions->addAction(skinAction); + skinMenu->addAction(skinAction); + connect(skinAction, SIGNAL(triggered()), this, SLOT(setScaleView())); + skinMenu->addSeparator(); + + skinActions = new QActionGroup(parent); + QSignalMapper *mapper = new QSignalMapper(parent); + skinAction = new QAction(tr("None"), parent); + skinAction->setCheckable(true); + if (currentSkin.isEmpty()) + skinAction->setChecked(true); + skinActions->addAction(skinAction); + skinMenu->addAction(skinAction); + mapper->setMapping(skinAction, ""); + connect(skinAction, SIGNAL(triggered()), mapper, SLOT(map())); + skinMenu->addSeparator(); + + foreach (QString name, builtinSkins()) { + skinAction = new QAction(name, parent); + skinActions->addAction(skinAction); + skinMenu->addAction(skinAction); + skinAction->setCheckable(true); + if (":skin/"+name+".skin" == currentSkin) + skinAction->setChecked(true); + mapper->setMapping(skinAction, name); + connect(skinAction, SIGNAL(triggered()), mapper, SLOT(map())); + } + connect(mapper, SIGNAL(mapped(QString)), this, SLOT(setSkin(QString))); + + if (flatmenu) flatmenu->addSeparator(); + + QMenu *settingsMenu = flatmenu ? flatmenu : menu->addMenu(tr("S&ettings")); + QAction *proxyAction = new QAction(tr("Http &proxy..."), parent); + connect(proxyAction, SIGNAL(triggered()), this, SLOT(showProxySettings())); + settingsMenu->addAction(proxyAction); + if (!flatmenu) + settingsMenu->addAction(recordOptions); + + if (flatmenu) flatmenu->addSeparator(); + + QMenu *helpMenu = flatmenu ? flatmenu : menu->addMenu(tr("&Help")); + QAction *aboutAction = new QAction(tr("&About Qt..."), parent); + connect(aboutAction, SIGNAL(triggered()), qApp, SLOT(aboutQt())); + helpMenu->addAction(aboutAction); + + QAction *quitAction = new QAction(tr("&Quit"), parent); + quitAction->setShortcut(QKeySequence("Ctrl+Q")); + connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit())); + fileMenu->addSeparator(); + fileMenu->addAction(quitAction); + if (menu) { + menu->setFixedHeight(menu->sizeHint().height()); + menu->setMinimumWidth(10); + } +} + +void QmlViewer::showProxySettings() +{ + ProxySettings settingsDlg (this); + + connect (&settingsDlg, SIGNAL (accepted()), this, SLOT (proxySettingsChanged ())); + + settingsDlg.exec(); +} + +void QmlViewer::proxySettingsChanged() +{ + setupProxy (); + reload (); +} + +void QmlViewer::setScaleSkin() +{ + if (scaleSkin) + return; + scaleSkin = true; + canvas->setContentResizable(!skin || !scaleSkin); + if (skin) { + canvas->setFixedSize(canvas->sizeHint()); + skin->setScreenSize(canvas->sizeHint()); + } +} + +void QmlViewer::setScaleView() +{ + if (!scaleSkin) + return; + scaleSkin = false; + if (skin) { + canvas->setContentResizable(!skin || !scaleSkin); + canvas->setMinimumSize(QSize(0,0)); + canvas->setMaximumSize(QSize(16777215,16777215)); + canvas->resize(skin->standardScreenSize()); + skin->setScreenSize(skin->standardScreenSize()); + } +} + + +void QmlViewer::takeSnapShot() +{ + static int snapshotcount = 1; + QString snapFileName = QString(QLatin1String("snapshot%1.png")).arg(snapshotcount); + QPixmap::grabWidget(canvas).save(snapFileName); + qDebug() << "Wrote" << snapFileName; + ++snapshotcount; +} + +void QmlViewer::pickRecordingFile() +{ + QString fileName = getVideoFileName(); + if (!fileName.isEmpty()) + recdlg->file->setText(fileName); +} + +void QmlViewer::chooseRecordingOptions() +{ + // File + recdlg->file->setText(record_file); + + // Size + recdlg->sizeOriginal->setText(tr("Original (%1x%2)").arg(canvas->width()).arg(canvas->height())); + if (recdlg->sizeWidth->value()<=1) { + recdlg->sizeWidth->setValue(canvas->width()); + recdlg->sizeHeight->setValue(canvas->height()); + } + + // Rate + if (record_rate == 24) + recdlg->hz24->setChecked(true); + else if (record_rate == 25) + recdlg->hz25->setChecked(true); + else if (record_rate == 50) + recdlg->hz50->setChecked(true); + else if (record_rate == 60) + recdlg->hz60->setChecked(true); + else { + recdlg->hzCustom->setChecked(true); + recdlg->hz->setText(QString::number(record_rate)); + } + + // Profile + recdlg->setArguments(record_args.join(" ")); + if (recdlg->exec()) { + // File + record_file = recdlg->file->text(); + // Size + if (recdlg->sizeOriginal->isChecked()) + record_outsize = QSize(); + else if (recdlg->size720p->isChecked()) + record_outsize = QSize(1280,720); + else if (recdlg->sizeVGA->isChecked()) + record_outsize = QSize(640,480); + else if (recdlg->sizeQVGA->isChecked()) + record_outsize = QSize(320,240); + else + record_outsize = QSize(recdlg->sizeWidth->value(),recdlg->sizeHeight->value()); + // Rate + if (recdlg->hz24->isChecked()) + record_rate = 24; + else if (recdlg->hz25->isChecked()) + record_rate = 25; + else if (recdlg->hz50->isChecked()) + record_rate = 50; + else if (recdlg->hz60->isChecked()) + record_rate = 60; + else { + record_rate = recdlg->hz->text().toDouble(); + } + // Profile + record_args = recdlg->arguments().split(" ",QString::SkipEmptyParts); + } +} + +void QmlViewer::toggleRecordingWithSelection() +{ + if (!recordTimer.isRunning()) { + if (record_file.isEmpty()) { + QString fileName = getVideoFileName(); + if (fileName.isEmpty()) + return; + if (!fileName.contains(QRegExp(".[^\\/]*$"))) + fileName += ".avi"; + setRecordFile(fileName); + } + } + toggleRecording(); +} + +void QmlViewer::toggleRecording() +{ + if (record_file.isEmpty()) { + toggleRecordingWithSelection(); + return; + } + bool recording = !recordTimer.isRunning(); + recordAction->setText(recording ? tr("&Stop Recording Video\tF9") : tr("&Start Recording Video\tF9")); + setRecording(recording); +} + +void QmlViewer::setSlowMode(bool enable) +{ + QUnifiedTimer::instance()->setSlowModeEnabled(enable); +} + +void QmlViewer::addLibraryPath(const QString& lib) +{ + canvas->engine()->addImportPath(lib); +} + +void QmlViewer::reload() +{ + openQml(currentFileOrUrl); +} + +void QmlViewer::open() +{ + QString cur = canvas->url().toLocalFile(); + QString fileName = QFileDialog::getOpenFileName(this, tr("Open QML file"), cur, tr("QML Files (*.qml)")); + if (!fileName.isEmpty()) { + QFileInfo fi(fileName); + openQml(fi.absoluteFilePath()); + } +} + +void QmlViewer::executeErrors() +{ + if (tester) tester->executefailure(); +} + +void QmlViewer::openQml(const QString& file_or_url) +{ + currentFileOrUrl = file_or_url; + + QUrl url; + QFileInfo fi(file_or_url); + if (fi.exists()) + url = QUrl::fromLocalFile(fi.absoluteFilePath()); + else + url = QUrl(file_or_url); + setWindowTitle(tr("%1 - Qt Declarative UI Viewer").arg(file_or_url)); + + if (!m_script.isEmpty()) + tester = new QmlGraphicsTester(m_script, m_scriptOptions, canvas); + + canvas->reset(); + + QString fileName = url.toLocalFile(); + if (!fileName.isEmpty()) { + QFileInfo fi(fileName); + if (fi.exists()) { + if (fi.suffix().toLower() != QLatin1String("qml")) { + qWarning() << "qmlviewer cannot open non-QML file" << fileName; + return; + } + + QmlContext *ctxt = canvas->rootContext(); + QDir dir(fi.path()+"/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(); + QmlComponent comp(canvas->engine()); + comp.setData(data, QUrl()); + QObject *dummyData = comp.create(); + + if(comp.isError()) { + QList<QmlError> errors = comp.errors(); + foreach (const QmlError &error, errors) { + qWarning() << error; + } + if (tester) tester->executefailure(); + } + + if (dummyData) { + qWarning() << "Loaded dummy data:" << dir.filePath(qml); + qml.truncate(qml.length()-4); + ctxt->setContextProperty(qml, dummyData); + dummyData->setParent(this); + } + } + } else { + qWarning() << "qmlviewer cannot find file:" << fileName; + return; + } + } + + canvas->setUrl(url); + + QTime t; + t.start(); + canvas->execute(); + qWarning() << "Wall startup time:" << t.elapsed(); + + if (!skin) { + canvas->updateGeometry(); + if (mb) + mb->updateGeometry(); + resize(sizeHint()); + } else { + if (scaleSkin) + canvas->resize(canvas->sizeHint()); + else { + canvas->setFixedSize(skin->standardScreenSize()); + canvas->resize(skin->standardScreenSize()); + } + } + +#ifdef QTOPIA + show(); +#endif +} + +QStringList QmlViewer::builtinSkins() const +{ + QDir dir(":/skins/","*.skin"); + const QFileInfoList l = dir.entryInfoList(); + QStringList r; + for (QFileInfoList::const_iterator it = l.begin(); it != l.end(); ++it) { + r += (*it).baseName(); + } + return r; +} + +void QmlViewer::setSkin(const QString& skinDirOrName) +{ + QString skinDirectory = skinDirOrName; + + if (!QDir(skinDirOrName).exists() && QDir(":/skins/"+skinDirOrName+".skin").exists()) + skinDirectory = ":/skins/"+skinDirOrName+".skin"; + + if (currentSkin == skinDirectory) + return; + + currentSkin = skinDirectory; + + // XXX QWidget::setMask does not handle changes well, and we may + // XXX have been signalled from an item in a menu we're replacing, + // XXX hence some rather convoluted resetting here... + + QString err; + if (skin) { + skin->hide(); + skin->deleteLater(); + } + + canvas->setContentResizable(!skin || !scaleSkin); + + DeviceSkinParameters parameters; + if (!skinDirectory.isEmpty() && parameters.read(skinDirectory,DeviceSkinParameters::ReadAll,&err)) { + layout()->setEnabled(false); + //setMenuBar(0); + if (mb) + mb->hide(); + if (!err.isEmpty()) + qWarning() << err; + skin = new PreviewDeviceSkin(parameters,this); + canvas->resize(canvas->sizeHint()); + if (scaleSkin) + skin->setPreviewAndScale(canvas); + else + skin->setPreview(canvas); + createMenu(0,skin->menu); + skin->show(); + } else { + skin = 0; + clearMask(); + menuBar()->clear(); + canvas->setParent(this, Qt::SubWindow); + createMenu(menuBar(),0); + mb->show(); + setMinimumSize(QSize(0,0)); + setMaximumSize(QSize(16777215,16777215)); + canvas->setMinimumSize(QSize(0,0)); + canvas->setMaximumSize(QSize(16777215,16777215)); + QRect g = geometry(); + g.setSize(sizeHint()); + setParent(0,windowFlags()); // recreate + canvas->move(0,menuBar()->sizeHint().height()); + setGeometry(g); + layout()->setEnabled(true); + show(); + } + canvas->show(); +} + +void QmlViewer::setAutoRecord(int from, int to) +{ + if (from==0) from=1; // ensure resized + record_autotime = to-from; + autoStartTimer.setInterval(from); + autoStartTimer.setRunning(true); +} + +void QmlViewer::setRecordArgs(const QStringList& a) +{ + record_args = a; +} + +void QmlViewer::setRecordFile(const QString& f) +{ + record_file = f; +} + +void QmlViewer::setRecordRate(int fps) +{ + record_rate = fps; +} + +void QmlViewer::sceneResized(QSize size) +{ + if (size.width() > 0 && size.height() > 0) { + if (skin && scaleSkin) + skin->setScreenSize(size); + } +} + +void QmlViewer::keyPressEvent(QKeyEvent *event) +{ + if (event->key() == Qt::Key_0 && devicemode) + exit(0); + else if (event->key() == Qt::Key_F1 || (event->key() == Qt::Key_1 && devicemode)) { + qDebug() << "F1 - help\n" + << "F2 - save test script\n" + << "F3 - take PNG snapshot\n" + << "F4 - show items and state\n" + << "F5 - reload QML\n" + << "F6 - show object tree\n" + << "F7 - show timing\n" + << "F8 - show performance (if available)\n" + << "F9 - toggle video recording\n" + << "device keys: 0=quit, 1..8=F1..F8" + ; + } else if (event->key() == Qt::Key_F2 || (event->key() == Qt::Key_2 && devicemode)) { + if (tester && m_scriptOptions & Record) + tester->save(); + } else if (event->key() == Qt::Key_F3 || (event->key() == Qt::Key_3 && devicemode)) { + takeSnapShot(); + } else if (event->key() == Qt::Key_F5 || (event->key() == Qt::Key_5 && devicemode)) { + reload(); + } else if (event->key() == Qt::Key_F8 || (event->key() == Qt::Key_8 && devicemode)) { + QPerformanceLog::displayData(); + QPerformanceLog::clear(); + } else if (event->key() == Qt::Key_F9 || (event->key() == Qt::Key_9 && devicemode)) { + toggleRecording(); + } + + QWidget::keyPressEvent(event); +} + +void QmlViewer::senseImageMagick() +{ + QProcess proc; + proc.start("convert", QStringList() << "-h"); + proc.waitForFinished(2000); + QString help = proc.readAllStandardOutput(); + convertAvailable = help.contains("ImageMagick"); +} + +void QmlViewer::senseFfmpeg() +{ + QProcess proc; + proc.start("ffmpeg", QStringList() << "-h"); + proc.waitForFinished(2000); + QString ffmpegHelp = proc.readAllStandardOutput(); + ffmpegAvailable = ffmpegHelp.contains("-s "); + ffmpegHelp = tr("Video recording uses ffmpeg:")+"\n\n"+ffmpegHelp; + + QDialog *d = new QDialog(recdlg); + QVBoxLayout *l = new QVBoxLayout(d); + QTextBrowser *b = new QTextBrowser(d); + QFont f = b->font(); + f.setFamily("courier"); + b->setFont(f); + b->setText(ffmpegHelp); + l->addWidget(b); + d->setLayout(l); + ffmpegHelpWindow = d; + connect(recdlg->ffmpegHelp,SIGNAL(clicked()), ffmpegHelpWindow, SLOT(show())); +} + +void QmlViewer::setRecording(bool on) +{ + if (on == recordTimer.isRunning()) + return; + + int period = int(1000/record_rate+0.5); + QUnifiedTimer::instance()->setTimingInterval(on ? period:16); + QUnifiedTimer::instance()->setConsistentTiming(on); + if (on) { + canvas->setViewportUpdateMode(QGraphicsView::FullViewportUpdate); + recordTimer.setInterval(period); + recordTimer.setRunning(true); + frame_fmt = record_file.right(4).toLower(); + frame = QImage(canvas->width(),canvas->height(),QImage::Format_RGB32); + if (frame_fmt != ".png" && (!convertAvailable || frame_fmt != ".gif")) { + // Stream video to ffmpeg + + QProcess *proc = new QProcess(this); + connect(proc, SIGNAL(finished(int)), this, SLOT(ffmpegFinished(int))); + frame_stream = proc; + + QStringList args; + args << "-y"; + args << "-r" << QString::number(record_rate); + args << "-f" << "rawvideo"; + args << "-pix_fmt" << (frame_fmt == ".gif" ? "rgb24" : "rgb32"); + args << "-s" << QString("%1x%2").arg(canvas->width()).arg(canvas->height()); + args << "-i" << "-"; + if (record_outsize.isValid()) { + args << "-s" << QString("%1x%2").arg(record_outsize.width()).arg(record_outsize.height()); + args << "-aspect" << QString::number(double(canvas->width())/canvas->height()); + } + args += record_args; + args << record_file; + proc->start("ffmpeg",args); + + } else { + // Store frames, save to GIF/PNG + frame_stream = 0; + } + } else { + canvas->setViewportUpdateMode(QGraphicsView::MinimalViewportUpdate); + recordTimer.setRunning(false); + if (frame_stream) { + qDebug() << "Saving video..."; + frame_stream->close(); + qDebug() << "Wrote" << record_file; + } else { + QProgressDialog progress(tr("Saving frames..."), tr("Cancel"), 0, frames.count()+10, this); + progress.setWindowModality(Qt::WindowModal); + + int frame=0; + QStringList inputs; + qDebug() << "Saving frames..."; + + QString framename; + bool png_output = false; + if (record_file.right(4).toLower()==".png") { + if (record_file.contains('%')) + framename = record_file; + else + framename = record_file.left(record_file.length()-4)+"%04d"+record_file.right(4); + png_output = true; + } else { + framename = "tmp-frame%04d.png"; + png_output = false; + } + foreach (QImage* img, frames) { + progress.setValue(progress.value()+1); + if (progress.wasCanceled()) + break; + QString name; + name.sprintf(framename.toLocal8Bit(),frame++); + if (record_outsize.isValid()) + *img = img->scaled(record_outsize,Qt::IgnoreAspectRatio,Qt::SmoothTransformation); + if (record_dither=="ordered") + img->convertToFormat(QImage::Format_Indexed8,Qt::PreferDither|Qt::OrderedDither).save(name); + else if (record_dither=="threshold") + img->convertToFormat(QImage::Format_Indexed8,Qt::PreferDither|Qt::ThresholdDither).save(name); + else if (record_dither=="floyd") + img->convertToFormat(QImage::Format_Indexed8,Qt::PreferDither).save(name); + else + img->save(name); + inputs << name; + delete img; + } + + if (!progress.wasCanceled()) { + if (png_output) { + framename.replace(QRegExp("%\\d*."),"*"); + qDebug() << "Wrote frames" << framename; + inputs.clear(); // don't remove them + } else { + // ImageMagick and gifsicle for GIF encoding + progress.setLabelText(tr("Converting frames to GIF file...")); + QStringList args; + args << "-delay" << QString::number(period/10); + args << inputs; + args << record_file; + qDebug() << "Converting..." << record_file << "(this may take a while)"; + if (0!=QProcess::execute("convert", args)) { + qWarning() << "Cannot run ImageMagick 'convert' - recorded frames not converted"; + inputs.clear(); // don't remove them + qDebug() << "Wrote frames tmp-frame*.png"; + } else { + if (record_file.right(4).toLower() == ".gif") { + qDebug() << "Compressing..." << record_file; + if (0!=QProcess::execute("gifsicle", QStringList() << "-O2" << "-o" << record_file << record_file)) + qWarning() << "Cannot run 'gifsicle' - not compressed"; + } + qDebug() << "Wrote" << record_file; + } + } + } + + progress.setValue(progress.maximum()-1); + foreach (QString name, inputs) + QFile::remove(name); + + frames.clear(); + } + } + qDebug() << "Recording: " << (recordTimer.isRunning()?"ON":"OFF"); +} + +void QmlViewer::ffmpegFinished(int code) +{ + qDebug() << "ffmpeg returned" << code << frame_stream->readAllStandardError(); +} + +void QmlViewer::autoStartRecording() +{ + setRecording(true); + autoStopTimer.setInterval(record_autotime); + autoStopTimer.setRunning(true); +} + +void QmlViewer::autoStopRecording() +{ + setRecording(false); +} + +void QmlViewer::recordFrame() +{ + canvas->QWidget::render(&frame); + if (frame_stream) { + if (frame_fmt == ".gif") { + // ffmpeg can't do 32bpp with gif + QImage rgb24 = frame.convertToFormat(QImage::Format_RGB888); + frame_stream->write((char*)rgb24.bits(),rgb24.numBytes()); + } else { + frame_stream->write((char*)frame.bits(),frame.numBytes()); + } + } else { + frames.append(new QImage(frame)); + } +} + +void QmlViewer::setDeviceKeys(bool on) +{ + devicemode = on; +} + +void QmlViewer::setupProxy() +{ + class SystemProxyFactory : public QNetworkProxyFactory + { + public: + virtual QList<QNetworkProxy> queryProxy(const QNetworkProxyQuery &query) + { + QString protocolTag = query.protocolTag(); + if (httpProxyInUse && (protocolTag == "http" || protocolTag == "https")) { + QList<QNetworkProxy> ret; + ret << httpProxy; + return ret; + } + return QNetworkProxyFactory::systemProxyForQuery(query); + } + void setHttpProxy (QNetworkProxy proxy) + { + httpProxy = proxy; + httpProxyInUse = true; + } + void unsetHttpProxy () + { + httpProxyInUse = false; + } + private: + bool httpProxyInUse; + QNetworkProxy httpProxy; + }; + + QNetworkAccessManager * nam = canvas->engine()->networkAccessManager(); + SystemProxyFactory *proxyFactory = new SystemProxyFactory; + if (ProxySettings::httpProxyInUse()) + proxyFactory->setHttpProxy(ProxySettings::httpProxy()); + else + proxyFactory->unsetHttpProxy(); + nam->setProxyFactory(proxyFactory); +} + +void QmlViewer::setNetworkCacheSize(int size) +{ + QNetworkAccessManager * nam = canvas->engine()->networkAccessManager(); + QNetworkDiskCache *cache = qobject_cast<QNetworkDiskCache*>(nam->cache()); + if (!cache) { + if (size==0) + return; + cache = new QNetworkDiskCache; + cache->setCacheDirectory(QDir::tempPath()+QLatin1String("/qml-duiviewer-network-cache")); + nam->setCache(cache); + } + if (size == cache->maximumCacheSize()) + return; + if (size>0) { + // Setup a caching network manager + cache->setMaximumCacheSize(size); + } else { + nam->setCache(0); + } +} + +void QmlViewer::setUseGL(bool useGL) +{ +#ifdef GL_SUPPORTED + if (useGL) { + QGLFormat format = QGLFormat::defaultFormat(); + format.setSampleBuffers(false); + + QGLWidget *glWidget = new QGLWidget(format); + glWidget->setAutoFillBackground(false); + canvas->setViewport(glWidget); + } +#endif +} +QT_END_NAMESPACE + +#include "qmlviewer.moc" diff --git a/tools/qmlviewer/qmlviewer.h b/tools/qmlviewer/qmlviewer.h new file mode 100644 index 0000000..78512a9 --- /dev/null +++ b/tools/qmlviewer/qmlviewer.h @@ -0,0 +1,165 @@ +/**************************************************************************** +** +** Copyright (C) 2009 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 QMLVIEWER_H +#define QMLVIEWER_H + +#include <QMenuBar> +#include <private/qmltimer_p.h> +#include <QTime> +#include <QList> + +QT_BEGIN_NAMESPACE + +class QmlView; +class PreviewDeviceSkin; +class QmlGraphicsTestEngine; +class QProcess; +class RecordingDialog; +class QmlGraphicsTester; + +class QmlViewer : public QWidget +{ +Q_OBJECT +public: + QmlViewer(QWidget *parent=0, Qt::WindowFlags flags=0); + + enum ScriptOption { + Play = 0x00000001, + Record = 0x00000002, + TestImages = 0x00000004, + TestErrorProperty = 0x00000008, + SaveOnExit = 0x00000010, + ExitOnComplete = 0x00000020, + ExitOnFailure = 0x00000040 + }; + Q_DECLARE_FLAGS(ScriptOptions, ScriptOption) + void setScript(const QString &s) { m_script = s; } + void setScriptOptions(ScriptOptions opt) { m_scriptOptions = opt; } + void setRecordDither(const QString& s) { record_dither = s; } + void setRecordRate(int fps); + void setRecordFile(const QString&); + void setRecordArgs(const QStringList&); + void setRecording(bool on); + bool isRecording() const { return recordTimer.isRunning(); } + void setAutoRecord(int from, int to); + void setDeviceKeys(bool); + void setNetworkCacheSize(int size); + void addLibraryPath(const QString& lib); + void setUseGL(bool use); + + QStringList builtinSkins() const; + + QMenuBar *menuBar() const; + +public slots: + void sceneResized(QSize size); + void openQml(const QString&); + void open(); + void reload(); + void takeSnapShot(); + void toggleRecording(); + void toggleRecordingWithSelection(); + void ffmpegFinished(int code); + void setSkin(const QString& skinDirectory); + void showProxySettings (); + void proxySettingsChanged (); + void setScaleView(); + void executeErrors(); + void setSlowMode(bool); + +protected: + virtual void keyPressEvent(QKeyEvent *); + + void createMenu(QMenuBar *menu, QMenu *flatmenu); + +private slots: + void autoStartRecording(); + void autoStopRecording(); + void recordFrame(); + void chooseRecordingOptions(); + void pickRecordingFile(); + void setScaleSkin(); + void adjustSizeSlot(); + +private: + void setupProxy(); + QString getVideoFileName(); + + PreviewDeviceSkin *skin; + QSize skinscreensize; + QmlView *canvas; + QString currentFileOrUrl; + QmlTimer recordTimer; + QString frame_fmt; + QImage frame; + QList<QImage*> frames; + QProcess* frame_stream; + QmlTimer autoStartTimer; + QmlTimer autoStopTimer; + QString record_dither; + QString record_file; + QSize record_outsize; + QStringList record_args; + int record_rate; + int record_autotime; + bool devicemode; + QAction *recordAction; + QString currentSkin; + bool scaleSkin; + mutable QMenuBar *mb; + RecordingDialog *recdlg; + + void senseImageMagick(); + void senseFfmpeg(); + QWidget *ffmpegHelpWindow; + bool ffmpegAvailable; + bool convertAvailable; + + QString m_script; + ScriptOptions m_scriptOptions; + QmlGraphicsTester *tester; +}; +Q_DECLARE_OPERATORS_FOR_FLAGS(QmlViewer::ScriptOptions) + +QT_END_NAMESPACE + +#endif diff --git a/tools/qmlviewer/qmlviewer.pro b/tools/qmlviewer/qmlviewer.pro new file mode 100644 index 0000000..899a3ab --- /dev/null +++ b/tools/qmlviewer/qmlviewer.pro @@ -0,0 +1,35 @@ +TEMPLATE = app +CONFIG += qt \ + uic +DESTDIR = ../../bin +QT += declarative \ + script \ + network \ + sql + +contains(QT_CONFIG, opengl) { + QT += opengl + DEFINES += GL_SUPPORTED +} + +# Input +HEADERS += qmlviewer.h \ + proxysettings.h \ + qfxtester.h +SOURCES += main.cpp \ + qmlviewer.cpp \ + proxysettings.cpp \ + qfxtester.cpp +FORMS = recopts.ui \ + proxysettings.ui +include($$QT_SOURCE_TREE/tools/shared/deviceskin/deviceskin.pri) +target.path = $$[QT_INSTALL_BINS] +INSTALLS += target + +wince* { +QT += scripttools \ + xml \ + xmlpatterns \ + webkit \ + phonon +} diff --git a/tools/qmlviewer/recopts.ui b/tools/qmlviewer/recopts.ui new file mode 100644 index 0000000..ce2da30 --- /dev/null +++ b/tools/qmlviewer/recopts.ui @@ -0,0 +1,513 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>RecordingOptions</class> + <widget class="QDialog" name="RecordingOptions"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>316</width> + <height>436</height> + </rect> + </property> + <property name="windowTitle"> + <string>Video options</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <item> + <widget class="QLabel" name="label"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>File:</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="file"/> + </item> + <item> + <widget class="QToolButton" name="pickfile"> + <property name="text"> + <string>...</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QGroupBox" name="groupBox"> + <property name="title"> + <string>Size</string> + </property> + <layout class="QGridLayout" name="gridLayout_2"> + <item row="0" column="0"> + <widget class="QRadioButton" name="sizeOriginal"> + <property name="text"> + <string/> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QRadioButton" name="sizeVGA"> + <property name="text"> + <string>VGA</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QRadioButton" name="size720p"> + <property name="text"> + <string>720p</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QRadioButton" name="sizeQVGA"> + <property name="text"> + <string>QVGA</string> + </property> + </widget> + </item> + <item row="2" column="0" colspan="3"> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <widget class="QRadioButton" name="sizeCustom"> + <property name="text"> + <string>Width:</string> + </property> + </widget> + </item> + <item> + <widget class="QSpinBox" name="sizeWidth"> + <property name="minimum"> + <number>1</number> + </property> + <property name="maximum"> + <number>9999</number> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="label_2"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Height:</string> + </property> + </widget> + </item> + <item> + <widget class="QSpinBox" name="sizeHeight"> + <property name="minimum"> + <number>1</number> + </property> + <property name="maximum"> + <number>9999</number> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_3"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::MinimumExpanding</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + <item row="0" column="2"> + <spacer name="horizontalSpacer_4"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::MinimumExpanding</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="rateOptions"> + <property name="title"> + <string>Rate</string> + </property> + <layout class="QGridLayout" name="gridLayout_4"> + <item row="0" column="0"> + <widget class="QRadioButton" name="hz60"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>60Hz</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QRadioButton" name="hz50"> + <property name="text"> + <string>50Hz</string> + </property> + </widget> + </item> + <item row="0" column="2"> + <widget class="QRadioButton" name="hz25"> + <property name="text"> + <string>25Hz</string> + </property> + </widget> + </item> + <item row="2" column="0" colspan="4"> + <layout class="QHBoxLayout" name="horizontalLayout"> + <property name="spacing"> + <number>0</number> + </property> + <item> + <widget class="QRadioButton" name="hzCustom"> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="hz"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>60</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>100</width> + <height>16777215</height> + </size> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>Hz</string> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_2"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::MinimumExpanding</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + <item row="1" column="2"> + <widget class="QRadioButton" name="hz24"> + <property name="text"> + <string>24Hz</string> + </property> + </widget> + </item> + <item row="0" column="3"> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::MinimumExpanding</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="0" column="1"> + <spacer name="horizontalSpacer_5"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Fixed</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="ffmpegOptions"> + <property name="title"> + <string>Profile</string> + </property> + <layout class="QGridLayout" name="gridLayout_3"> + <item row="0" column="0" colspan="3"> + <widget class="QComboBox" name="profile"/> + </item> + <item row="1" column="0" colspan="2"> + <widget class="QLineEdit" name="args"/> + </item> + <item row="1" column="2"> + <widget class="QToolButton" name="ffmpegHelp"> + <property name="text"> + <string>Help</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QLabel" name="warning"> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <tabstops> + <tabstop>file</tabstop> + <tabstop>pickfile</tabstop> + <tabstop>sizeOriginal</tabstop> + <tabstop>sizeVGA</tabstop> + <tabstop>size720p</tabstop> + <tabstop>sizeQVGA</tabstop> + <tabstop>sizeCustom</tabstop> + <tabstop>sizeWidth</tabstop> + <tabstop>sizeHeight</tabstop> + <tabstop>hz60</tabstop> + <tabstop>hz25</tabstop> + <tabstop>hz50</tabstop> + <tabstop>hz24</tabstop> + <tabstop>hzCustom</tabstop> + <tabstop>hz</tabstop> + <tabstop>profile</tabstop> + <tabstop>args</tabstop> + <tabstop>ffmpegHelp</tabstop> + <tabstop>buttonBox</tabstop> + </tabstops> + <resources/> + <connections> + <connection> + <sender>hzCustom</sender> + <signal>clicked()</signal> + <receiver>hz</receiver> + <slot>setFocus()</slot> + <hints> + <hint type="sourcelabel"> + <x>43</x> + <y>257</y> + </hint> + <hint type="destinationlabel"> + <x>129</x> + <y>262</y> + </hint> + </hints> + </connection> + <connection> + <sender>hz</sender> + <signal>textChanged(QString)</signal> + <receiver>hzCustom</receiver> + <slot>toggle()</slot> + <hints> + <hint type="sourcelabel"> + <x>143</x> + <y>262</y> + </hint> + <hint type="destinationlabel"> + <x>43</x> + <y>257</y> + </hint> + </hints> + </connection> + <connection> + <sender>hz</sender> + <signal>selectionChanged()</signal> + <receiver>hzCustom</receiver> + <slot>toggle()</slot> + <hints> + <hint type="sourcelabel"> + <x>143</x> + <y>262</y> + </hint> + <hint type="destinationlabel"> + <x>43</x> + <y>257</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>RecordingOptions</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>258</x> + <y>424</y> + </hint> + <hint type="destinationlabel"> + <x>60</x> + <y>219</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>RecordingOptions</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>258</x> + <y>424</y> + </hint> + <hint type="destinationlabel"> + <x>92</x> + <y>219</y> + </hint> + </hints> + </connection> + <connection> + <sender>profile</sender> + <signal>activated(int)</signal> + <receiver>RecordingOptions</receiver> + <slot>pickProfile(int)</slot> + <hints> + <hint type="sourcelabel"> + <x>92</x> + <y>329</y> + </hint> + <hint type="destinationlabel"> + <x>48</x> + <y>194</y> + </hint> + </hints> + </connection> + <connection> + <sender>args</sender> + <signal>textEdited(QString)</signal> + <receiver>RecordingOptions</receiver> + <slot>storeCustomArgs(QString)</slot> + <hints> + <hint type="sourcelabel"> + <x>128</x> + <y>357</y> + </hint> + <hint type="destinationlabel"> + <x>102</x> + <y>189</y> + </hint> + </hints> + </connection> + <connection> + <sender>sizeWidth</sender> + <signal>valueChanged(int)</signal> + <receiver>sizeCustom</receiver> + <slot>toggle()</slot> + <hints> + <hint type="sourcelabel"> + <x>108</x> + <y>133</y> + </hint> + <hint type="destinationlabel"> + <x>48</x> + <y>133</y> + </hint> + </hints> + </connection> + <connection> + <sender>sizeHeight</sender> + <signal>valueChanged(int)</signal> + <receiver>sizeCustom</receiver> + <slot>toggle()</slot> + <hints> + <hint type="sourcelabel"> + <x>212</x> + <y>133</y> + </hint> + <hint type="destinationlabel"> + <x>64</x> + <y>129</y> + </hint> + </hints> + </connection> + </connections> + <slots> + <signal>filePicked(QString)</signal> + <signal>argumentsPicked(QString)</signal> + <slot>pickFile()</slot> + <slot>pickProfile(int)</slot> + <slot>storeCustomArgs(QString)</slot> + </slots> +</ui> |