summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/src/declarative/network.qdoc81
-rw-r--r--doc/src/declarative/qmlformat.qdoc4
-rw-r--r--src/declarative/extra/qmlfontloader.cpp14
-rw-r--r--src/declarative/fx/qfxborderimage.cpp14
-rw-r--r--src/declarative/fx/qfxpixmapcache.cpp18
-rw-r--r--src/declarative/qml/qmlcompositetypemanager.cpp18
-rw-r--r--src/declarative/qml/qmlengine.cpp13
-rw-r--r--src/declarative/qml/qmlerror.cpp1
-rw-r--r--src/declarative/qml/qmlscriptparser.cpp1
-rw-r--r--tests/auto/declarative/animations/data/badproperty1.qml2
-rw-r--r--tests/auto/declarative/animations/tst_animations.cpp2
-rw-r--r--tests/auto/declarative/declarative.pro3
-rw-r--r--tests/auto/declarative/qmllanguage/data/I18nType30.qml (renamed from tests/auto/declarative/qmllanguage/data/I18nÁâãäå.qml)0
-rw-r--r--tests/auto/declarative/qmllanguage/data/i18nType.qml2
-rw-r--r--tests/auto/declarative/qmllanguage/tst_qmllanguage.cpp30
-rw-r--r--tools/qmldebugger/engine.cpp401
-rw-r--r--tools/qmldebugger/engine.h43
-rw-r--r--tools/qmldebugger/main.cpp175
-rw-r--r--tools/qmldebugger/objectpropertiesview.cpp166
-rw-r--r--tools/qmldebugger/objectpropertiesview.h47
-rw-r--r--tools/qmldebugger/objecttree.cpp168
-rw-r--r--tools/qmldebugger/objecttree.h55
-rw-r--r--tools/qmldebugger/propertyview.cpp117
-rw-r--r--tools/qmldebugger/propertyview.h40
-rw-r--r--tools/qmldebugger/qmldebugger.cpp125
-rw-r--r--tools/qmldebugger/qmldebugger.h47
-rw-r--r--tools/qmldebugger/qmldebugger.pri22
-rw-r--r--tools/qmldebugger/qmldebugger.pro16
-rw-r--r--tools/qmldebugger/watchtable.cpp (renamed from tools/qmldebugger/watchtablemodel.cpp)141
-rw-r--r--tools/qmldebugger/watchtable.h (renamed from tools/qmldebugger/watchtablemodel.h)67
30 files changed, 1053 insertions, 780 deletions
diff --git a/doc/src/declarative/network.qdoc b/doc/src/declarative/network.qdoc
index 3d75706..da4495f 100644
--- a/doc/src/declarative/network.qdoc
+++ b/doc/src/declarative/network.qdoc
@@ -43,11 +43,84 @@
\page qmlnetwork.html
\title Network Transparency
+QML supports network transparency by using URLs (rather than file names) for all
+references from a QML document to other content. Since a \i relative URL is the same
+as a relative file, development of QML on regular file systems remains simple.
+
+\section1 Accessing Network Reesources from QML
+
+Whenever an object has a property of type URL (QUrl), assigning a string to that
+property will actually assign an absolute URL - by resolving the string against
+the URL of the document where the string is used.
+
+For example, consider this content in \c{http://example.com/mystuff/test.qml}:
+
+\code
+Image {
+ source: "images/logo.png"
+}
+\endcode
+
+The \l Image source property will be assigned \c{http://example.com/mystuff/images/logo.png},
+but while the QML is being developed, in say \c C:\User\Fred\Documents\MyStuff\test.qml, it will be assigned
+\c C:\User\Fred\Documents\MyStuff\images\logo.png.
+
+Network transparency is supported throughout QML:
+
+\list
+\o Types - if the \c test.qml file above used "Hello { }", that would refer to \c http://example.com/mystuff/Hello.qml
+\o Scripts - the \c source property of \l Script is a URL
+\o Images - the \c source property of \l Image and similar types is a URL
+\o Fonts - the \c source property of FontLoader is a URL
+\o WebViews - the \c url property of WebView may be assigned a relative URL string
+\endlist
+
+Because of the declarative nature of QML and the asynchronous nature of network resources,
+objects which reference network resource generally change state as the network resource loads.
+For example, an Image with a network source will initially have
+a \c width and \c height of 0, a \c status of \c Loading, and a \c progress of 0.0.
+While the content loads, the \c progress will increase until
+the content is fully loaded from the network,
+at which point the \c width and \c height become the content size, the \c status becomes \c Ready, and the \c progress reaches 1.0.
+Applications can bind to these changing states to provide visual progress indicators where appropriate, or simply
+bind to the \c width and \c height as if the content was a local file, adapting as those bound values change.
+
+Note that when objects reference local files they immediately have the \c Ready status, but applications wishing
+to remain network transparent should not rely on this. Future versions of QML may also use asynchronous local file I/O
+to improve performance.
+
+\section1 Limitations
+
+The \c import statement only works network transparently if it has an "as" clause.
+
\list
-\o Documents and script blocks can be fetched transparently over the network (blocking)
-\o Images, fonts can be fetched transparently over the network (non-blocking)
-\o Configuring the network access manager
-\o Relative URL resolution from ECMAScript/QML
+\o \c{import "dir"} only works on local file systems
+\o \c{import libraryUri} only works on local file systems
+\o \c{import "dir" as D} works network transparently
+\o \c{import libraryUrl as U} works network transparently
\endlist
+\section1 Configuring the Network Access Manager
+
+All network access from QML is managed by a QNetworkAccessManager set on the QmlEngine which executes the QML.
+By default, this is an unmodified Qt QNetworkAccessManager. You may set a different manager using
+QmlEngine::setNetworkAccessManager() as appropriate for the policies of your application.
+For eample, the \l qmlviewer tool sets a new QNetworkAccessManager which
+trusts HTTP Expiry headers to avoid network cache checks, allows HTTP Pipelining, adds a persistent HTTP CookieJar,
+a simple disk cache, and supports proxy settings.
+
+\section1 QRC Resources
+
+One of the URL schemes built into Qt is the "qrc" scheme. This allows content to be compiled into
+the executable using \l{The Qt Resource System}. Using this, an executable can reference QML content
+that is compiled into the executable:
+
+\code
+ QmlView *canvas = new QmlView;
+ canvas->setUrl(QUrl("qrc:/dial.qml"));
+\endcode
+
+The content itself can then use relative URLs, and so be transparently unaware that the content is
+compiled into the executable.
+
*/
diff --git a/doc/src/declarative/qmlformat.qdoc b/doc/src/declarative/qmlformat.qdoc
index be1afa4..72bbe40 100644
--- a/doc/src/declarative/qmlformat.qdoc
+++ b/doc/src/declarative/qmlformat.qdoc
@@ -192,6 +192,10 @@ different property bindings.
\section1 Syntax
+\section2 Encoding
+
+QML files are always encoded in UTF-8 format.
+
\section2 Commenting
The commenting rules in QML are the same as for ECMAScript. Both \e {MultiLineComment} blocks and \e {SingleLineComment}'s are supported.
diff --git a/src/declarative/extra/qmlfontloader.cpp b/src/declarative/extra/qmlfontloader.cpp
index 66c8567..4a447de 100644
--- a/src/declarative/extra/qmlfontloader.cpp
+++ b/src/declarative/extra/qmlfontloader.cpp
@@ -92,6 +92,15 @@ QmlFontLoader::~QmlFontLoader()
{
}
+static QString toLocalFileOrQrc(const QUrl& url)
+{
+ QString r = url.toLocalFile();
+ if (r.isEmpty() && url.scheme() == QLatin1String("qrc"))
+ r = QLatin1Char(':') + url.path();
+ return r;
+}
+
+
/*!
\qmlproperty url FontLoader::source
The url of the font to load.
@@ -112,8 +121,9 @@ void QmlFontLoader::setSource(const QUrl &url)
d->status = Loading;
emit statusChanged();
#ifndef QT_NO_LOCALFILE_OPTIMIZED_QML
- if (d->url.scheme() == QLatin1String("file")) {
- QFile file(d->url.toLocalFile());
+ QString lf = toLocalFileOrQrc(d->url);
+ if (!lf.isEmpty()) {
+ QFile file(lf);
file.open(QIODevice::ReadOnly);
QByteArray ba = file.readAll();
d->addFontToDatabase(ba);
diff --git a/src/declarative/fx/qfxborderimage.cpp b/src/declarative/fx/qfxborderimage.cpp
index 8f98a11..f1574e5 100644
--- a/src/declarative/fx/qfxborderimage.cpp
+++ b/src/declarative/fx/qfxborderimage.cpp
@@ -138,6 +138,15 @@ QFxBorderImage::~QFxBorderImage()
The URL may be absolute, or relative to the URL of the component.
*/
+static QString toLocalFileOrQrc(const QUrl& url)
+{
+ QString r = url.toLocalFile();
+ if (r.isEmpty() && url.scheme() == QLatin1String("qrc"))
+ r = QLatin1Char(':') + url.path();
+ return r;
+}
+
+
void QFxBorderImage::setSource(const QUrl &url)
{
Q_D(QFxBorderImage);
@@ -180,8 +189,9 @@ void QFxBorderImage::setSource(const QUrl &url)
d->status = Loading;
if (d->url.path().endsWith(QLatin1String(".sci"))) {
#ifndef QT_NO_LOCALFILE_OPTIMIZED_QML
- if (d->url.scheme() == QLatin1String("file")) {
- QFile file(d->url.toLocalFile());
+ QString lf = toLocalFileOrQrc(d->url);
+ if (!lf.isEmpty()) {
+ QFile file(lf);
file.open(QIODevice::ReadOnly);
setGridScaledImage(QFxGridScaledImage(&file));
} else
diff --git a/src/declarative/fx/qfxpixmapcache.cpp b/src/declarative/fx/qfxpixmapcache.cpp
index 80b5011..13e1b16 100644
--- a/src/declarative/fx/qfxpixmapcache.cpp
+++ b/src/declarative/fx/qfxpixmapcache.cpp
@@ -124,6 +124,14 @@ static bool readImage(QIODevice *dev, QPixmap *pixmap)
This class is NOT reentrant.
*/
+static QString toLocalFileOrQrc(const QUrl& url)
+{
+ QString r = url.toLocalFile();
+ if (r.isEmpty() && url.scheme() == QLatin1String("qrc"))
+ r = QLatin1Char(':') + url.path();
+ return r;
+}
+
/*!
Finds the cached pixmap corresponding to \a url.
A previous call to get() must have requested the URL,
@@ -142,8 +150,9 @@ bool QFxPixmapCache::find(const QUrl& url, QPixmap *pixmap)
bool ok = true;
if (!QPixmapCache::find(key,pixmap)) {
#ifndef QT_NO_LOCALFILE_OPTIMIZED_QML
- if (url.scheme()==QLatin1String("file")) {
- QFile f(url.toLocalFile());
+ QString lf = toLocalFileOrQrc(url);
+ if (!lf.isEmpty()) {
+ QFile f(lf);
if (f.open(QIODevice::ReadOnly)) {
if (!readImage(&f, pixmap)) {
qWarning() << "Format error loading" << url;
@@ -207,10 +216,11 @@ bool QFxPixmapCache::find(const QUrl& url, QPixmap *pixmap)
QNetworkReply *QFxPixmapCache::get(QmlEngine *engine, const QUrl& url, QPixmap *pixmap)
{
#ifndef QT_NO_LOCALFILE_OPTIMIZED_QML
- if (url.scheme()==QLatin1String("file")) {
+ QString lf = toLocalFileOrQrc(url);
+ if (!lf.isEmpty()) {
QString key = url.toString();
if (!QPixmapCache::find(key,pixmap)) {
- QFile f(url.toLocalFile());
+ QFile f(lf);
if (f.open(QIODevice::ReadOnly)) {
if (!readImage(&f, pixmap)) {
qWarning() << "Format error loading" << url;
diff --git a/src/declarative/qml/qmlcompositetypemanager.cpp b/src/declarative/qml/qmlcompositetypemanager.cpp
index 13bd02c..3c76344 100644
--- a/src/declarative/qml/qmlcompositetypemanager.cpp
+++ b/src/declarative/qml/qmlcompositetypemanager.cpp
@@ -262,13 +262,22 @@ void QmlCompositeTypeManager::resourceReplyFinished()
reply->deleteLater();
}
+static QString toLocalFileOrQrc(const QUrl& url)
+{
+ QString r = url.toLocalFile();
+ if (r.isEmpty() && url.scheme() == QLatin1String("qrc"))
+ r = QLatin1Char(':') + url.path();
+ return r;
+}
+
void QmlCompositeTypeManager::loadResource(QmlCompositeTypeResource *resource)
{
QUrl url(resource->url);
- if (url.scheme() == QLatin1String("file")) {
+ QString lf = toLocalFileOrQrc(url);
+ if (!lf.isEmpty()) {
- QFile file(url.toLocalFile());
+ QFile file(lf);
if (file.open(QFile::ReadOnly)) {
resource->data = file.readAll();
resource->status = QmlCompositeTypeResource::Complete;
@@ -290,9 +299,10 @@ void QmlCompositeTypeManager::loadSource(QmlCompositeTypeData *unit)
{
QUrl url(unit->imports.baseUrl());
- if (url.scheme() == QLatin1String("file")) {
+ QString lf = toLocalFileOrQrc(url);
+ if (!lf.isEmpty()) {
- QFile file(url.toLocalFile());
+ QFile file(lf);
if (file.open(QFile::ReadOnly)) {
QByteArray data = file.readAll();
setData(unit, data, url);
diff --git a/src/declarative/qml/qmlengine.cpp b/src/declarative/qml/qmlengine.cpp
index 4dbc336..f0ecf1d 100644
--- a/src/declarative/qml/qmlengine.cpp
+++ b/src/declarative/qml/qmlengine.cpp
@@ -959,6 +959,15 @@ QVariant QmlScriptClass::toVariant(QmlEngine *engine, const QScriptValue &val)
return QVariant();
}
+// XXX this beyonds in QUrl::toLocalFile()
+static QString toLocalFileOrQrc(const QUrl& url)
+{
+ QString r = url.toLocalFile();
+ if (r.isEmpty() && url.scheme() == QLatin1String("qrc"))
+ r = QLatin1Char(':') + url.path();
+ return r;
+}
+
/////////////////////////////////////////////////////////////
struct QmlEnginePrivate::ImportedNamespace {
QStringList urls;
@@ -989,7 +998,7 @@ struct QmlEnginePrivate::ImportedNamespace {
QUrl url = QUrl(urls.at(i) + QLatin1String("/") + QString::fromUtf8(type) + QLatin1String(".qml"));
if (vmaj || vmin) {
// Check version file - XXX cache these in QmlEngine!
- QFile qmldir(QUrl(urls.at(i)+QLatin1String("/qmldir")).toLocalFile());
+ QFile qmldir(toLocalFileOrQrc(QUrl(urls.at(i)+QLatin1String("/qmldir"))));
if (qmldir.open(QIODevice::ReadOnly)) {
do {
QByteArray lineba = qmldir.readLine();
@@ -1020,7 +1029,7 @@ struct QmlEnginePrivate::ImportedNamespace {
}
} else {
// XXX search non-files too! (eg. zip files, see QT-524)
- QFileInfo f(url.toLocalFile());
+ QFileInfo f(toLocalFileOrQrc(url));
if (f.exists()) {
if (url_return)
*url_return = url;
diff --git a/src/declarative/qml/qmlerror.cpp b/src/declarative/qml/qmlerror.cpp
index 5ee9144..514fe44 100644
--- a/src/declarative/qml/qmlerror.cpp
+++ b/src/declarative/qml/qmlerror.cpp
@@ -201,6 +201,7 @@ QDebug operator<<(QDebug debug, const QmlError &error)
if (f.open(QIODevice::ReadOnly)) {
QByteArray data = f.readAll();
QTextStream stream(data, QIODevice::ReadOnly);
+ stream.setCodec("UTF-8");
const QString code = stream.readAll();
const QStringList lines = code.split(QLatin1Char('\n'));
diff --git a/src/declarative/qml/qmlscriptparser.cpp b/src/declarative/qml/qmlscriptparser.cpp
index 9cc12b3..6e5f315 100644
--- a/src/declarative/qml/qmlscriptparser.cpp
+++ b/src/declarative/qml/qmlscriptparser.cpp
@@ -870,6 +870,7 @@ bool QmlScriptParser::parse(const QByteArray &qmldata, const QUrl &url)
const QString fileName = url.toString();
QTextStream stream(qmldata, QIODevice::ReadOnly);
+ stream.setCodec("UTF-8");
const QString code = stream.readAll();
data = new QmlScriptParserJsASTData(fileName);
diff --git a/tests/auto/declarative/animations/data/badproperty1.qml b/tests/auto/declarative/animations/data/badproperty1.qml
index a01753e..78da34a 100644
--- a/tests/auto/declarative/animations/data/badproperty1.qml
+++ b/tests/auto/declarative/animations/data/badproperty1.qml
@@ -16,7 +16,7 @@ Rectangle {
}
states: State {
name: "state1"
- PropertyChanges { target: MyRect; pen.color: "blue" }
+ PropertyChanges { target: MyRect; border.color: "blue" }
}
transitions: Transition {
ColorAnimation { target: MyRect; to: "red"; properties: "pen.colr"; duration: 1000 }
diff --git a/tests/auto/declarative/animations/tst_animations.cpp b/tests/auto/declarative/animations/tst_animations.cpp
index 0e46224..336f0d3 100644
--- a/tests/auto/declarative/animations/tst_animations.cpp
+++ b/tests/auto/declarative/animations/tst_animations.cpp
@@ -86,7 +86,7 @@ void tst_animations::dotProperty()
QTest::qWait(animation.duration() + 50);
QCOMPARE(rect.border()->width(), 10);
- rect.border()->setWidth(1);
+ rect.border()->setWidth(0);
animation.start();
animation.pause();
animation.setCurrentTime(125);
diff --git a/tests/auto/declarative/declarative.pro b/tests/auto/declarative/declarative.pro
index eef9da7..b51e285 100644
--- a/tests/auto/declarative/declarative.pro
+++ b/tests/auto/declarative/declarative.pro
@@ -25,9 +25,6 @@ SUBDIRS += anchors \
states \
visual
-SUBDIRS -= examples # Human-interactive
-
# Tests which should run in Pulse
PULSE_TESTS = $$SUBDIRS
-PULSE_TESTS -= visual # All except 'visual' tests, allegedly too flaky
diff --git a/tests/auto/declarative/qmllanguage/data/I18nÁâãäå.qml b/tests/auto/declarative/qmllanguage/data/I18nType30.qml
index 42dbc69..42dbc69 100644
--- a/tests/auto/declarative/qmllanguage/data/I18nÁâãäå.qml
+++ b/tests/auto/declarative/qmllanguage/data/I18nType30.qml
diff --git a/tests/auto/declarative/qmllanguage/data/i18nType.qml b/tests/auto/declarative/qmllanguage/data/i18nType.qml
index 11ef895..d7954ef 100644
--- a/tests/auto/declarative/qmllanguage/data/i18nType.qml
+++ b/tests/auto/declarative/qmllanguage/data/i18nType.qml
@@ -1 +1 @@
-I18nÁâãäå { }
+I18nTypeÁâãäå { }
diff --git a/tests/auto/declarative/qmllanguage/tst_qmllanguage.cpp b/tests/auto/declarative/qmllanguage/tst_qmllanguage.cpp
index b99d040..b68d9bf 100644
--- a/tests/auto/declarative/qmllanguage/tst_qmllanguage.cpp
+++ b/tests/auto/declarative/qmllanguage/tst_qmllanguage.cpp
@@ -24,6 +24,8 @@ public:
}
private slots:
+ void initTestCase();
+ void cleanupTestCase();
void errors_data();
void errors();
@@ -116,6 +118,24 @@ inline QUrl TEST_FILE(const char *filename)
return TEST_FILE(QLatin1String(filename));
}
+void tst_qmllanguage::initTestCase()
+{
+ // Create locale-specific file
+ // For POSIX, this will just be data/I18nType.qml, since POSIX is 7-bit
+ // For iso8859-1 locale, this will just be data/I18nType?????.qml where ????? is 5 8-bit characters
+ // For utf-8 locale, this will be data/I18nType??????????.qml where ?????????? is 5 8-bit characters, UTF-8 encoded
+ QFile in(TEST_FILE(QLatin1String("I18nType30.qml")).toLocalFile());
+ QVERIFY(in.open(QIODevice::ReadOnly));
+ QFile out(TEST_FILE(QString::fromUtf8("I18nType\303\241\303\242\303\243\303\244\303\245.qml")).toLocalFile());
+ QVERIFY(out.open(QIODevice::WriteOnly));
+ out.write(in.readAll());
+}
+
+void tst_qmllanguage::cleanupTestCase()
+{
+ QVERIFY(QFile::remove(TEST_FILE(QString::fromUtf8("I18nType\303\241\303\242\303\243\303\244\303\245.qml")).toLocalFile()));
+}
+
void tst_qmllanguage::errors_data()
{
QTest::addColumn<QString>("file");
@@ -686,11 +706,11 @@ void tst_qmllanguage::i18n_data()
{
QTest::addColumn<QString>("file");
QTest::addColumn<QString>("stringProperty");
- QTest::newRow("i18nStrings") << "i18nStrings.qml" << QString::fromUtf8("Test áâãäå (5 accented 'a' letters)");
- QTest::newRow("i18nDeclaredPropertyNames") << "i18nDeclaredPropertyNames.qml" << QString::fromUtf8("Test áâãäå: 10");
- QTest::newRow("i18nDeclaredPropertyUse") << "i18nDeclaredPropertyUse.qml" << QString::fromUtf8("Test áâãäå: 15");
- QTest::newRow("i18nScript") << "i18nScript.qml" << QString::fromUtf8("Test áâãäå: 20");
- QTest::newRow("i18nType") << "i18nType.qml" << QString::fromUtf8("Test áâãäå: 30");
+ QTest::newRow("i18nStrings") << "i18nStrings.qml" << QString::fromUtf8("Test \303\241\303\242\303\243\303\244\303\245 (5 accented 'a' letters)");
+ QTest::newRow("i18nDeclaredPropertyNames") << "i18nDeclaredPropertyNames.qml" << QString::fromUtf8("Test \303\241\303\242\303\243\303\244\303\245: 10");
+ QTest::newRow("i18nDeclaredPropertyUse") << "i18nDeclaredPropertyUse.qml" << QString::fromUtf8("Test \303\241\303\242\303\243\303\244\303\245: 15");
+ QTest::newRow("i18nScript") << "i18nScript.qml" << QString::fromUtf8("Test \303\241\303\242\303\243\303\244\303\245: 20");
+ QTest::newRow("i18nType") << "i18nType.qml" << QString::fromUtf8("Test \303\241\303\242\303\243\303\244\303\245: 30");
}
void tst_qmllanguage::i18n()
diff --git a/tools/qmldebugger/engine.cpp b/tools/qmldebugger/engine.cpp
index 321f5e0..fac10f3 100644
--- a/tools/qmldebugger/engine.cpp
+++ b/tools/qmldebugger/engine.cpp
@@ -1,144 +1,21 @@
-#include "engine.h"
-#include "propertyview.h"
-#include "watchtablemodel.h"
-#include <QtDeclarative/qmldebugclient.h>
-#include <QPushButton>
#include <QVBoxLayout>
#include <QHBoxLayout>
-#include <QLineEdit>
-#include <QTreeWidget>
-#include <QTableWidget>
#include <QSplitter>
#include <QTabWidget>
-#include <QMouseEvent>
-#include <QAction>
-#include <QMenu>
-#include <QInputDialog>
#include <QFile>
-#include <QHeaderView>
-#include <QPointer>
+
#include <private/qmlenginedebug_p.h>
+#include <QtDeclarative/qmldebugclient.h>
#include <QtDeclarative/qmlcomponent.h>
#include <QtDeclarative/qfxitem.h>
#include <QtDeclarative/qmldebugservice.h>
-QT_BEGIN_NAMESPACE
-
-
-class QmlObjectTree : public QTreeWidget
-{
- Q_OBJECT
-public:
- enum AdditionalRoles {
- ContextIdRole = Qt::UserRole + 1
- };
-
- QmlObjectTree(QWidget *parent = 0);
-
- QTreeWidgetItem *findItemByObjectId(int debugId) const;
-
-signals:
- void addExpressionWatch(int debugId, const QString &);
-
-protected:
- virtual void mousePressEvent(QMouseEvent *);
-
-private:
- QTreeWidgetItem *findItem(QTreeWidgetItem *item, int debugId) const;
-};
-
-QmlObjectTree::QmlObjectTree(QWidget *parent)
-: QTreeWidget(parent)
-{
-}
-
-QTreeWidgetItem *QmlObjectTree::findItemByObjectId(int debugId) const
-{
- for (int i=0; i<topLevelItemCount(); i++) {
- QTreeWidgetItem *item = findItem(topLevelItem(i), debugId);
- if (item)
- return item;
- }
-
- return 0;
-}
-
-QTreeWidgetItem *QmlObjectTree::findItem(QTreeWidgetItem *item, int debugId) const
-{
- if (item->data(0, Qt::UserRole).toInt() == 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 QmlObjectTree::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;
- int debugId = currentItem()->data(0, Qt::UserRole).toInt();
- if (debugId >= 0 && 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 addExpressionWatch(debugId, watch);
- }
- }
-}
-
-
-class WatchTableHeaderView : public QHeaderView
-{
- Q_OBJECT
-public:
- WatchTableHeaderView(QTableView *parent);
-
-signals:
- void stopWatching(int column);
-
-protected:
- void mousePressEvent(QMouseEvent *me);
-
-private:
- QTableView *m_table;
-};
-
-WatchTableHeaderView::WatchTableHeaderView(QTableView *parent)
- : QHeaderView(Qt::Horizontal, parent), m_table(parent)
-{
- QObject::connect(this, SIGNAL(sectionClicked(int)),
- m_table, SLOT(selectColumn(int)));
- setClickable(true);
-}
+#include "engine.h"
+#include "objectpropertiesview.h"
+#include "objecttree.h"
+#include "watchtable.h"
-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) {
- m_table->selectColumn(col);
- QAction action(tr("Stop watching"), 0);
- QList<QAction *> actions;
- actions << &action;
- if (QMenu::exec(actions, me->globalPos()))
- emit stopWatching(col);
- }
- }
-}
+QT_BEGIN_NAMESPACE
class DebuggerEngineItem : public QObject
@@ -159,14 +36,12 @@ private:
int m_engineId;
};
-EnginePane::EnginePane(QmlDebugConnection *client, QWidget *parent)
-: QWidget(parent), m_client(client), m_engines(0), m_context(0), m_object(0), m_watchedObject(0), m_watchTableModel(0)
+EnginePane::EnginePane(QmlDebugConnection *conn, QWidget *parent)
+: QWidget(parent), m_client(new QmlEngineDebug(conn, this)), m_engines(0), m_context(0), m_watchTableModel(0)
{
- QVBoxLayout *layout = new QVBoxLayout;
+ QVBoxLayout *layout = new QVBoxLayout(this);
layout->setContentsMargins(0, 0, 0, 0);
- setLayout(layout);
-
QFile enginesFile(":/engines.qml");
enginesFile.open(QFile::ReadOnly);
Q_ASSERT(enginesFile.isOpen());
@@ -187,30 +62,34 @@ EnginePane::EnginePane(QmlDebugConnection *client, QWidget *parent)
QSplitter *splitter = new QSplitter;
- m_objTree = new QmlObjectTree(this);
- m_objTree->setHeaderHidden(true);
- connect(m_objTree, SIGNAL(itemClicked(QTreeWidgetItem *, int)), this, SLOT(itemClicked(QTreeWidgetItem *)));
- connect(m_objTree, SIGNAL(addExpressionWatch(int,QString)), this, SLOT(addExpressionWatch(int,QString)));
- splitter->addWidget(m_objTree);
+ 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(objectSelected(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)));
- m_propView = new PropertyView(this);
- connect(m_propView, SIGNAL(propertyActivated(QmlDebugPropertyReference)),
- this, SLOT(propertyActivated(QmlDebugPropertyReference)));
+ connect(m_watchTableModel, SIGNAL(watchCreated(QmlDebugWatch*)),
+ m_propertiesView, SLOT(watchCreated(QmlDebugWatch*)));
- m_watchTableModel = new WatchTableModel(this);
- m_watchTable = new QTableView(this);
- m_watchTable->setModel(m_watchTableModel);
- QObject::connect(m_watchTable, SIGNAL(activated(QModelIndex)),
- this, SLOT(watchedItemActivated(QModelIndex)));
- WatchTableHeaderView *header = new WatchTableHeaderView(m_watchTable);
- m_watchTable->setHorizontalHeader(header);
- QObject::connect(header, SIGNAL(stopWatching(int)),
- this, SLOT(stopWatching(int)));
+ connect(m_watchTableView, SIGNAL(objectActivated(int)),
+ m_objTree, SLOT(selectObject(int)));
m_tabs = new QTabWidget(this);
- m_tabs->addTab(m_propView, tr("Properties"));
- m_tabs->addTab(m_watchTable, tr("Watched"));
+ m_tabs->addTab(m_propertiesView, tr("Properties"));
+ m_tabs->addTab(m_watchTableView, tr("Watched"));
+ splitter->addWidget(m_objTree);
splitter->addWidget(m_tabs);
splitter->setStretchFactor(1, 2);
layout->addWidget(splitter);
@@ -222,128 +101,6 @@ void EnginePane::engineSelected(int id)
queryContext(id);
}
-void EnginePane::itemClicked(QTreeWidgetItem *item)
-{
- m_propView->clear();
-
- if (m_object) {
- delete m_object;
- m_object = 0;
- }
-
- m_object = m_client.queryObjectRecursive(QmlDebugObjectReference(item->data(0, Qt::UserRole).toInt()), this);
- if (!m_object->isWaiting())
- showProperties();
- else
- QObject::connect(m_object, SIGNAL(stateChanged(State)),
- this, SLOT(showProperties()));
-}
-
-void EnginePane::showProperties()
-{
- QmlDebugObjectReference obj = m_object->object();
- m_propView->setObject(obj);
-
- if (m_watchedObject) {
- m_client.removeWatch(m_watchedObject);
- delete m_watchedObject;
- m_watchedObject = 0;
- }
-
- QmlDebugWatch *watch = m_client.addWatch(obj, this);
- if (watch->state() != QmlDebugWatch::Dead) {
- m_watchedObject = watch;
- QObject::connect(watch, SIGNAL(valueChanged(QByteArray,QVariant)),
- this, SLOT(valueChanged(QByteArray,QVariant)));
- }
-
- delete m_object; m_object = 0;
-}
-
-void EnginePane::addExpressionWatch(int debugId, const QString &expr)
-{
- QmlDebugWatch *watch = m_client.addWatch(QmlDebugObjectReference(debugId), expr, this);
-
- if (watch->state() != QmlDebugWatch::Dead) {
- QObject::connect(watch, SIGNAL(valueChanged(QByteArray,QVariant)),
- this, SLOT(valueChanged(QByteArray,QVariant)));
- m_watchTableModel->addWatch(watch, expr);
- m_watchTable->resizeColumnsToContents();
- }
-}
-
-void EnginePane::valueChanged(const QByteArray &propertyName, const QVariant &value)
-{
- QmlDebugWatch *watch = qobject_cast<QmlDebugWatch*>(sender());
-
- m_watchTableModel->updateWatch(watch, value);
-
- if (!propertyName.isEmpty()) {
- if (watch->objectDebugId() == m_propView->object().debugId())
- m_propView->updateProperty(propertyName, value);
- }
-}
-
-void EnginePane::propertyActivated(const QmlDebugPropertyReference &property)
-{
- PropertyView *view = qobject_cast<PropertyView*>(sender());
- if (!view)
- return;
-
- QmlDebugObjectReference object = view->object();
- QmlDebugWatch *watch = m_watchTableModel->findWatch(object.debugId(), property.name());
- if (watch) {
- m_client.removeWatch(watch);
- delete watch;
- watch = 0;
- } else {
- QmlDebugWatch *watch = m_client.addWatch(property, this);
- if (watch->state() != QmlDebugWatch::Dead) {
- QObject::connect(watch, SIGNAL(stateChanged(State)),
- this, SLOT(propertyWatchStateChanged()));
- QObject::connect(watch, SIGNAL(valueChanged(QByteArray,QVariant)),
- this, SLOT(valueChanged(QByteArray,QVariant)));
- QString desc = property.name()
- + QLatin1String(" on\n")
- + object.className()
- + QLatin1String(": ")
- + (object.name().isEmpty() ? QLatin1String("<unnamed>") : object.name());
- m_watchTableModel->addWatch(watch, desc);
- m_watchTable->resizeColumnsToContents();
- }
- }
-}
-
-void EnginePane::propertyWatchStateChanged()
-{
- QmlDebugPropertyWatch *watch = qobject_cast<QmlDebugPropertyWatch*>(sender());
- if (watch && watch->objectDebugId() == m_propView->object().debugId())
- m_propView->setPropertyIsWatched(watch->name(), watch->state() == QmlDebugWatch::Active);
-}
-
-void EnginePane::stopWatching(int column)
-{
- QmlDebugWatch *watch = m_watchTableModel->findWatch(column);
- if (watch) {
- m_client.removeWatch(watch);
- delete watch;
- watch = 0;
- }
-}
-
-void EnginePane::watchedItemActivated(const QModelIndex &index)
-{
- QmlDebugWatch *watch = m_watchTableModel->findWatch(index.column());
- if (!watch)
- return;
- QTreeWidgetItem *item = m_objTree->findItemByObjectId(watch->objectDebugId());
- if (item) {
- m_objTree->setCurrentItem(item);
- m_objTree->scrollToItem(item);
- item->setExpanded(true);
- }
-}
-
void EnginePane::queryContext(int id)
{
if (m_context) {
@@ -351,88 +108,30 @@ void EnginePane::queryContext(int id)
m_context = 0;
}
- m_context = m_client.queryRootContexts(QmlDebugEngineReference(id), this);
+ m_context = m_client->queryRootContexts(QmlDebugEngineReference(id), this);
if (!m_context->isWaiting())
contextChanged();
else
- QObject::connect(m_context, SIGNAL(stateChanged(State)),
+ QObject::connect(m_context, SIGNAL(stateChanged(State)),
this, SLOT(contextChanged()));
}
void EnginePane::contextChanged()
{
- dump(m_context->rootContext(), 0);
+ //dump(m_context->rootContext(), 0);
+
foreach (const QmlDebugObjectReference &object, m_context->rootContext().objects())
- fetchObject(object.debugId());
+ m_objTree->reload(object.debugId());
delete m_context; m_context = 0;
}
-void EnginePane::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 EnginePane::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);
-}
-
-
-void EnginePane::buildTree(const QmlDebugObjectReference &obj, QTreeWidgetItem *parent)
-{
- if (!parent)
- m_objTree->clear();
-
- QTreeWidgetItem *item = parent ? new QTreeWidgetItem(parent) : new QTreeWidgetItem(m_objTree);
- item->setText(0, obj.className());
- item->setData(0, Qt::UserRole, obj.debugId());
- item->setData(0, QmlObjectTree::ContextIdRole, obj.contextDebugId());
-
- if (parent && obj.contextDebugId() >= 0
- && obj.contextDebugId() != parent->data(0, QmlObjectTree::ContextIdRole).toInt()) {
- 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 EnginePane::refreshEngines()
{
if (m_engines)
return;
- QList<QmlDebugWatch *> watches = m_watchTableModel->watches();
- for (int i=0; i<watches.count(); i++)
- m_client.removeWatch(watches[i]);
- qDeleteAll(watches);
-
- m_engines = m_client.queryAvailableEngines(this);
+ m_engines = m_client->queryAvailableEngines(this);
if (!m_engines->isWaiting())
enginesChanged();
else
@@ -462,28 +161,6 @@ void EnginePane::enginesChanged()
engineSelected(qobject_cast<DebuggerEngineItem*>(m_engineItems.at(0))->engineId());
}
-void EnginePane::fetchObject(int id)
-{
- if (m_object) {
- delete m_object;
- m_object = 0;
- }
-
- m_object = m_client.queryObjectRecursive(QmlDebugObjectReference(id), this);
- if (!m_object->isWaiting())
- objectFetched();
- else
- QObject::connect(m_object, SIGNAL(stateChanged(State)),
- this, SLOT(objectFetched()));
-}
-
-void EnginePane::objectFetched()
-{
- dump(m_object->object(), 0);
- buildTree(m_object->object(), 0);
- delete m_object; m_object = 0;
-}
-
#include "engine.moc"
diff --git a/tools/qmldebugger/engine.h b/tools/qmldebugger/engine.h
index c7707ed..8e8c0f2 100644
--- a/tools/qmldebugger/engine.h
+++ b/tools/qmldebugger/engine.h
@@ -10,21 +10,15 @@
QT_BEGIN_NAMESPACE
+class ObjectPropertiesView;
class QmlDebugConnection;
class QmlDebugPropertyReference;
class QmlDebugWatch;
-class QmlObjectTree;
-class EngineClientPlugin;
-class PropertyView;
+class ObjectTree;
class WatchTableModel;
-class QLineEdit;
-class QModelIndex;
-class QTreeWidget;
-class QTreeWidgetItem;
+class WatchTableView;
+
class QTabWidget;
-class QTableWidget;
-class QTableView;
-class QTableWidgetItem;
class EnginePane : public QWidget
{
@@ -41,42 +35,23 @@ private slots:
void queryContext(int);
void contextChanged();
- void fetchObject(int);
- void objectFetched();
-
void engineSelected(int);
- void itemClicked(QTreeWidgetItem *);
- void showProperties();
- void addExpressionWatch(int debugId, const QString &expr);
-
- void valueChanged(const QByteArray &property, const QVariant &value);
-
- void propertyActivated(const QmlDebugPropertyReference &property);
- void propertyWatchStateChanged();
- void watchedItemActivated(const QModelIndex &index);
- void stopWatching(int column);
-
private:
- void dump(const QmlDebugContextReference &, int);
- void dump(const QmlDebugObjectReference &, int);
- void buildTree(const QmlDebugObjectReference &, QTreeWidgetItem *parent);
-
- QmlEngineDebug m_client;
+ QmlEngineDebug *m_client;
QmlDebugEnginesQuery *m_engines;
QmlDebugRootContextQuery *m_context;
- QmlDebugObjectQuery *m_object;
- QmlObjectTree *m_objTree;
+ ObjectTree *m_objTree;
QTabWidget *m_tabs;
- PropertyView *m_propView;
- QTableView *m_watchTable;
+ WatchTableView *m_watchTableView;
QmlView *m_engineView;
QList<QObject *> m_engineItems;
- QmlDebugWatch *m_watchedObject;
WatchTableModel *m_watchTableModel;
+
+ ObjectPropertiesView *m_propertiesView;
};
QT_END_NAMESPACE
diff --git a/tools/qmldebugger/main.cpp b/tools/qmldebugger/main.cpp
index ccd3761..c9983cd 100644
--- a/tools/qmldebugger/main.cpp
+++ b/tools/qmldebugger/main.cpp
@@ -1,154 +1,6 @@
-#include <QtNetwork/qtcpsocket.h>
#include <QtGui/qapplication.h>
-#include <QtGui/qwidget.h>
-#include <QtGui/qpainter.h>
-#include <QtGui/qscrollbar.h>
-#include <QtDeclarative/qmldebugclient.h>
-#include <QtCore/qdebug.h>
-#include <QtCore/qstringlist.h>
-#include <QtCore/qdatastream.h>
-#include <QtCore/qtimer.h>
-#include "canvasframerate.h"
-#include "engine.h"
-#include <QVBoxLayout>
-#include <QPushButton>
-#include <QLineEdit>
-#include <QTabWidget>
-#include <QSpinBox>
-#include <QLabel>
-class Shell : public QWidget
-{
-Q_OBJECT
-public:
- Shell(QWidget * = 0);
-
- void setHost(const QString &host);
- void setPort(quint16 port);
- void showEngineTab();
-
-public slots:
- void connectToHost();
- void disconnectFromHost();
-
-private slots:
- void connectionStateChanged();
-
-private:
- QmlDebugConnection client;
-
- QLabel *m_connectionState;
- QLineEdit *m_host;
- QSpinBox *m_port;
- QPushButton *m_connectButton;
- QPushButton *m_disconnectButton;
-
- EnginePane *m_enginePane;
- QTabWidget *m_tabs;
-};
-
-Shell::Shell(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);
- m_host->setText("127.0.0.1");
- connectLayout->addWidget(m_host);
- m_port = new QSpinBox(this);
- m_port->setMinimum(1024);
- m_port->setMaximum(20000);
- m_port->setValue(3768);
- 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(&client, this);
- 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();
-}
-
-void Shell::setHost(const QString &host)
-{
- m_host->setText(host);
-}
-
-void Shell::setPort(quint16 port)
-{
- m_port->setValue(port);
-}
-
-void Shell::showEngineTab()
-{
- m_tabs->setCurrentWidget(m_enginePane);
-}
-
-void Shell::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 Shell::connectToHost()
-{
- client.connectToHost(m_host->text(), m_port->value());
-}
-
-void Shell::disconnectFromHost()
-{
- client.disconnectFromHost();
-}
+#include "qmldebugger.h"
int main(int argc, char ** argv)
{
@@ -156,24 +8,27 @@ int main(int argc, char ** argv)
QStringList args = app.arguments();
- Shell shell;
+ QmlDebugger win;
if (args.contains("--engine"))
- shell.showEngineTab();
+ win.showEngineTab();
- if (args.count() > 1 && args.at(1).contains(':')) {
- QStringList hostAndPort = args.at(1).split(':');
+ for (int i=0; i<args.count(); i++) {
+ if (!args[i].contains(':'))
+ continue;
+ QStringList hostAndPort = args[i].split(':');
bool ok = false;
- quint16 port = hostAndPort[1].toInt(&ok);
+ quint16 port = hostAndPort.value(1).toInt(&ok);
if (ok) {
- shell.setHost(hostAndPort[0]);
- shell.setPort(port);
- shell.connectToHost();
+ qWarning() << "qmldebugger connecting to"
+ << hostAndPort[0] << port << "...";
+ win.setHost(hostAndPort[0]);
+ win.setPort(port);
+ win.connectToHost();
+ break;
}
}
- shell.show();
+ win.show();
return app.exec();
}
-
-#include "main.moc"
diff --git a/tools/qmldebugger/objectpropertiesview.cpp b/tools/qmldebugger/objectpropertiesview.cpp
new file mode 100644
index 0000000..2237fbb
--- /dev/null
+++ b/tools/qmldebugger/objectpropertiesview.cpp
@@ -0,0 +1,166 @@
+#include <QtGui/qtreewidget.h>
+#include <QtGui/qlayout.h>
+
+#include <QtDeclarative/qmldebugservice.h>
+#include <QtDeclarative/qmldebug.h>
+#include <QtDeclarative/qmldebugclient.h>
+
+#include "objectpropertiesview.h"
+
+QT_BEGIN_NAMESPACE
+
+class PropertiesViewItem : public QObject, public QTreeWidgetItem
+{
+ Q_OBJECT
+public:
+ PropertiesViewItem(QTreeWidget *widget);
+ PropertiesViewItem(QTreeWidgetItem *parent);
+
+ QmlDebugPropertyReference property;
+};
+
+PropertiesViewItem::PropertiesViewItem(QTreeWidget *widget)
+: QTreeWidgetItem(widget)
+{
+}
+
+PropertiesViewItem::PropertiesViewItem(QTreeWidgetItem *parent)
+: QTreeWidgetItem(parent)
+{
+}
+
+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->setExpandsOnDoubleClick(false);
+ m_tree->setHeaderLabels(QStringList() << tr("Property") << tr("Value"));
+ QObject::connect(m_tree, SIGNAL(itemActivated(QTreeWidgetItem *, int)),
+ this, SLOT(itemActivated(QTreeWidgetItem *)));
+
+ m_tree->setColumnCount(2);
+
+ layout->addWidget(m_tree);
+}
+
+void ObjectPropertiesView::reload(const QmlDebugObjectReference &obj)
+{
+ m_query = m_client->queryObjectRecursive(obj, this);
+ if (!m_query->isWaiting())
+ queryFinished();
+ else
+ QObject::connect(m_query, SIGNAL(stateChanged(State)),
+ this, SLOT(queryFinished()));
+}
+
+void ObjectPropertiesView::queryFinished()
+{
+ if (m_watch) {
+ m_client->removeWatch(m_watch);
+ delete m_watch;
+ m_watch = 0;
+ }
+
+ QmlDebugObjectReference obj = m_query->object();
+
+ QmlDebugWatch *watch = m_client->addWatch(obj, this);
+ if (watch->state() != QmlDebugWatch::Dead) {
+ 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::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);
+ if (!p.hasNotifySignal())
+ item->setForeground(0, Qt::lightGray);
+
+ if (!p.binding().isEmpty()) {
+ PropertiesViewItem *binding = new PropertiesViewItem(item);
+ binding->setText(1, p.binding());
+ binding->setForeground(1, Qt::darkGreen);
+ }
+
+ item->setExpanded(true);
+ }
+
+ m_tree->resizeColumnToContents(0);
+}
+
+void ObjectPropertiesView::watchCreated(QmlDebugWatch *watch)
+{
+ if (watch->objectDebugId() == m_object.debugId()
+ && qobject_cast<QmlDebugPropertyWatch*>(watch)) {
+ connect(watch, SIGNAL(stateChanged(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)
+ item->setText(1, value.toString());
+ }
+}
+
+void ObjectPropertiesView::itemActivated(QTreeWidgetItem *i)
+{
+ PropertiesViewItem *item = static_cast<PropertiesViewItem *>(i);
+ if (!item->property.name().isEmpty() && item->property.hasNotifySignal())
+ emit activated(m_object, item->property);
+}
+
+QT_END_NAMESPACE
+
+#include "objectpropertiesview.moc"
diff --git a/tools/qmldebugger/objectpropertiesview.h b/tools/qmldebugger/objectpropertiesview.h
new file mode 100644
index 0000000..0f72ff4
--- /dev/null
+++ b/tools/qmldebugger/objectpropertiesview.h
@@ -0,0 +1,47 @@
+#ifndef PROPERTIESTABLEMODEL_H
+#define PROPERTIESTABLEMODEL_H
+
+#include <QtDeclarative/qmldebug.h>
+
+#include <QtGui/qwidget.h>
+
+QT_BEGIN_NAMESPACE
+
+class QTreeWidget;
+class QTreeWidgetItem;
+
+class ObjectPropertiesView : public QWidget
+{
+ Q_OBJECT
+public:
+ ObjectPropertiesView(QmlEngineDebug *client, QWidget *parent = 0);
+
+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);
+
+ QmlEngineDebug *m_client;
+ QmlDebugObjectQuery *m_query;
+ QmlDebugWatch *m_watch;
+
+ QTreeWidget *m_tree;
+ QmlDebugObjectReference m_object;
+};
+
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/tools/qmldebugger/objecttree.cpp b/tools/qmldebugger/objecttree.cpp
new file mode 100644
index 0000000..f398987
--- /dev/null
+++ b/tools/qmldebugger/objecttree.cpp
@@ -0,0 +1,168 @@
+#include <QtGui/qevent.h>
+#include <QtGui/qmenu.h>
+#include <QtGui/qaction.h>
+
+#include <QInputDialog>
+
+#include <QtDeclarative/qmldebugservice.h>
+#include <QtDeclarative/qmldebug.h>
+#include <QtDeclarative/qmldebugclient.h>
+
+#include "objecttree.h"
+
+Q_DECLARE_METATYPE(QmlDebugObjectReference)
+
+ObjectTree::ObjectTree(QmlEngineDebug *client, QWidget *parent)
+ : QTreeWidget(parent),
+ m_client(client),
+ m_query(0)
+{
+ setHeaderHidden(true);
+
+ connect(this, SIGNAL(itemClicked(QTreeWidgetItem *, int)),
+ this, SLOT(handleItemClicked(QTreeWidgetItem *)));
+}
+
+void ObjectTree::reload(int objectDebugId)
+{
+ 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(State)),
+ this, SLOT(objectFetched()));
+}
+
+void ObjectTree::selectObject(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);
+
+ delete m_query;
+ m_query = 0;
+}
+
+void ObjectTree::handleItemClicked(QTreeWidgetItem *item)
+{
+ QmlDebugObjectReference obj = item->data(0, Qt::UserRole).value<QmlDebugObjectReference>();
+ if (obj.debugId() < 0) {
+ qWarning("QML Object Tree: bad object id");
+ return;
+ }
+ emit objectSelected(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));
+ item->setData(0, ObjectTree::ContextIdRole, obj.contextDebugId());
+
+ if (parent && obj.contextDebugId() >= 0
+ && obj.contextDebugId() != parent->data(0, ObjectTree::ContextIdRole).toInt()) {
+ 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/objecttree.h b/tools/qmldebugger/objecttree.h
new file mode 100644
index 0000000..a924ec5
--- /dev/null
+++ b/tools/qmldebugger/objecttree.h
@@ -0,0 +1,55 @@
+#ifndef OBJECTTREE_H
+#define OBJECTTREE_H
+
+#include <QtGui/qtreewidget.h>
+
+QT_BEGIN_NAMESPACE
+
+class QTreeWidgetItem;
+
+class QmlEngineDebug;
+class QmlDebugObjectReference;
+class QmlDebugObjectQuery;
+class QmlDebugContextReference;
+
+
+class ObjectTree : public QTreeWidget
+{
+ Q_OBJECT
+public:
+ enum AdditionalRoles {
+ ContextIdRole = Qt::UserRole + 1
+ };
+
+ ObjectTree(QmlEngineDebug *client, QWidget *parent = 0);
+
+signals:
+ void objectSelected(const QmlDebugObjectReference &);
+ void expressionWatchRequested(const QmlDebugObjectReference &, const QString &);
+
+public slots:
+ void reload(int objectDebugId);
+ void selectObject(int debugId);
+
+protected:
+ virtual void mousePressEvent(QMouseEvent *);
+
+private slots:
+ void objectFetched();
+ void handleItemClicked(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/propertyview.cpp b/tools/qmldebugger/propertyview.cpp
deleted file mode 100644
index 44e406b..0000000
--- a/tools/qmldebugger/propertyview.cpp
+++ /dev/null
@@ -1,117 +0,0 @@
-#include "propertyview.h"
-#include <QtCore/qdebug.h>
-#include <QtGui/qboxlayout.h>
-#include <QtGui/qtreewidget.h>
-
-QT_BEGIN_NAMESPACE
-
-PropertyView::PropertyView(QWidget *parent)
-: QWidget(parent), m_tree(0)
-{
- QVBoxLayout *layout = new QVBoxLayout;
- layout->setContentsMargins(0, 0, 0, 0);
- layout->setSpacing(0);
- setLayout(layout);
-
- m_tree = new QTreeWidget(this);
- m_tree->setExpandsOnDoubleClick(false);
- m_tree->setHeaderLabels(QStringList() << tr("Property") << tr("Value"));
- QObject::connect(m_tree, SIGNAL(itemActivated(QTreeWidgetItem *, int)),
- this, SLOT(itemActivated(QTreeWidgetItem *)));
-
- m_tree->setColumnCount(2);
-
- layout->addWidget(m_tree);
-}
-
-class PropertyViewItem : public QObject, public QTreeWidgetItem
-{
- Q_OBJECT
-public:
- PropertyViewItem(QTreeWidget *widget);
- PropertyViewItem(QTreeWidgetItem *parent);
-
- QmlDebugPropertyReference property;
-};
-
-PropertyViewItem::PropertyViewItem(QTreeWidget *widget)
-: QTreeWidgetItem(widget)
-{
-}
-
-PropertyViewItem::PropertyViewItem(QTreeWidgetItem *parent)
-: QTreeWidgetItem(parent)
-{
-}
-
-
-void PropertyView::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];
-
- PropertyViewItem *item = new PropertyViewItem(m_tree);
- item->property = p;
-
- item->setText(0, p.name());
- item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
- if (!p.hasNotifySignal())
- item->setForeground(0, Qt::lightGray);
-
- if (!p.binding().isEmpty()) {
- PropertyViewItem *binding = new PropertyViewItem(item);
- binding->setText(1, p.binding());
- binding->setForeground(1, Qt::darkGreen);
- }
-
- item->setExpanded(true);
- }
-
- m_tree->resizeColumnToContents(0);
-}
-
-const QmlDebugObjectReference &PropertyView::object() const
-{
- return m_object;
-}
-
-void PropertyView::clear()
-{
- setObject(QmlDebugObjectReference());
-}
-
-void PropertyView::updateProperty(const QString &name, const QVariant &value)
-{
- for (int i=0; i<m_tree->topLevelItemCount(); i++) {
- PropertyViewItem *item = static_cast<PropertyViewItem *>(m_tree->topLevelItem(i));
- if (item->property.name() == name)
- item->setText(1, value.toString());
- }
-}
-
-void PropertyView::setPropertyIsWatched(const QString &name, bool watched)
-{
- for (int i=0; i<m_tree->topLevelItemCount(); i++) {
- PropertyViewItem *item = static_cast<PropertyViewItem *>(m_tree->topLevelItem(i));
- if (item->property.name() == name && item->property.hasNotifySignal()) {
- QFont font = m_tree->font();
- font.setBold(watched);
- item->setFont(0, font);
- }
- }
-}
-
-void PropertyView::itemActivated(QTreeWidgetItem *i)
-{
- PropertyViewItem *item = static_cast<PropertyViewItem *>(i);
- if (!item->property.name().isEmpty() && item->property.hasNotifySignal())
- emit propertyActivated(item->property);
-}
-
-QT_END_NAMESPACE
-
-#include "propertyview.moc"
diff --git a/tools/qmldebugger/propertyview.h b/tools/qmldebugger/propertyview.h
deleted file mode 100644
index 6b69bdf..0000000
--- a/tools/qmldebugger/propertyview.h
+++ /dev/null
@@ -1,40 +0,0 @@
-#ifndef PROPERTYVIEW_H
-#define PROPERTYVIEW_H
-
-#include <QtGui/qwidget.h>
-#include <QtCore/qpointer.h>
-#include <QtDeclarative/qmldebug.h>
-
-QT_BEGIN_NAMESPACE
-
-class QTreeWidget;
-class QTreeWidgetItem;
-
-class PropertyView : public QWidget
-{
- Q_OBJECT
-public:
- PropertyView(QWidget *parent = 0);
-
- void setObject(const QmlDebugObjectReference &object);
- const QmlDebugObjectReference &object() const;
-
- void updateProperty(const QString &name, const QVariant &value);
- void setPropertyIsWatched(const QString &name, bool watched);
-
- void clear();
-
-signals:
- void propertyActivated(const QmlDebugPropertyReference &property);
-
-private slots:
- void itemActivated(QTreeWidgetItem *);
-
-private:
- QmlDebugObjectReference m_object;
- QTreeWidget *m_tree;
-};
-
-QT_END_NAMESPACE
-
-#endif // PROPERTYVIEW_H
diff --git a/tools/qmldebugger/qmldebugger.cpp b/tools/qmldebugger/qmldebugger.cpp
new file mode 100644
index 0000000..0f0fc03
--- /dev/null
+++ b/tools/qmldebugger/qmldebugger.cpp
@@ -0,0 +1,125 @@
+#include <QtCore/qtimer.h>
+#include <QtCore/qdebug.h>
+#include <QVBoxLayout>
+#include <QPushButton>
+#include <QLineEdit>
+#include <QTabWidget>
+#include <QSpinBox>
+#include <QLabel>
+
+#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);
+ m_host->setText("127.0.0.1");
+ connectLayout->addWidget(m_host);
+ m_port = new QSpinBox(this);
+ m_port->setMinimum(1024);
+ m_port->setMaximum(20000);
+ m_port->setValue(3768);
+ 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(&client, this);
+ 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)));
+}
+
+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::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/qmldebugger.h b/tools/qmldebugger/qmldebugger.h
new file mode 100644
index 0000000..9203e33
--- /dev/null
+++ b/tools/qmldebugger/qmldebugger.h
@@ -0,0 +1,47 @@
+#ifndef QMLDEBUGGER_H
+#define QMLDEBUGGER_H
+
+#include <QtDeclarative/qmldebugclient.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();
+
+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/qmldebugger.pri b/tools/qmldebugger/qmldebugger.pri
new file mode 100644
index 0000000..ce36381
--- /dev/null
+++ b/tools/qmldebugger/qmldebugger.pri
@@ -0,0 +1,22 @@
+QT += network declarative
+contains(QT_CONFIG, opengles2)|contains(QT_CONFIG, opengles1): QT += opengl
+
+# Input
+HEADERS += $$PWD/qmldebugger.h \
+ $$PWD/canvasframerate.h \
+ $$PWD/watchtable.h \
+ $$PWD/engine.h \
+ $$PWD/objecttree.h \
+ $$PWD/objectpropertiesview.h
+
+SOURCES += $$PWD/qmldebugger.cpp \
+ $$PWD/main.cpp \
+ $$PWD/canvasframerate.cpp \
+ $$PWD/watchtable.cpp \
+ $$PWD/engine.cpp \
+ $$PWD/objecttree.cpp \
+ $$PWD/objectpropertiesview.cpp
+
+RESOURCES += $$PWD/qmldebugger.qrc
+
+OTHER_FILES += $$PWD/engines.qml
diff --git a/tools/qmldebugger/qmldebugger.pro b/tools/qmldebugger/qmldebugger.pro
index b177875..4cdfd18 100644
--- a/tools/qmldebugger/qmldebugger.pro
+++ b/tools/qmldebugger/qmldebugger.pro
@@ -1,20 +1,6 @@
DESTDIR = ../../bin
-QT += network declarative
-contains(QT_CONFIG, opengles2)|contains(QT_CONFIG, opengles1): QT += opengl
-# Input
-HEADERS += canvasframerate.h \
- watchtablemodel.h \
- propertyview.h \
- engine.h
-SOURCES += main.cpp \
- canvasframerate.cpp \
- watchtablemodel.cpp \
- propertyview.cpp \
- engine.cpp
-RESOURCES += qmldebugger.qrc
-
-OTHER_FILES += engines.qml
+include(qmldebugger.pri)
target.path=$$[QT_INSTALL_BINS]
INSTALLS += target
diff --git a/tools/qmldebugger/watchtablemodel.cpp b/tools/qmldebugger/watchtable.cpp
index 9d3ca1e..e4163dc 100644
--- a/tools/qmldebugger/watchtablemodel.cpp
+++ b/tools/qmldebugger/watchtable.cpp
@@ -1,25 +1,38 @@
-#include "watchtablemodel.h"
+#include "watchtable.h"
#include <QtCore/qdebug.h>
+#include <QtGui/qevent.h>
+#include <QtGui/qaction.h>
+#include <QtGui/qmenu.h>
+
#include <QtDeclarative/qmldebug.h>
#include <QtDeclarative/qmlmetatype.h>
QT_BEGIN_NAMESPACE
-WatchTableModel::WatchTableModel(QObject *parent)
- : QAbstractTableModel(parent)
+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::addWatch(QmlDebugWatch *watch, const QString &title)
{
QString property;
if (qobject_cast<QmlDebugPropertyWatch *>(watch))
property = qobject_cast<QmlDebugPropertyWatch *>(watch)->name();
- // Watch will be automatically removed when its state is Inactive
- QObject::connect(watch, SIGNAL(stateChanged(State)), SLOT(watchStateChanged()));
+ connect(watch, SIGNAL(valueChanged(QByteArray,QVariant)),
+ SLOT(watchedValueChanged(QByteArray,QVariant)));
+
+ connect(watch, SIGNAL(stateChanged(State)), SLOT(watchStateChanged()));
int col = columnCount(QModelIndex());
beginInsertColumns(QModelIndex(), col, col);
@@ -87,14 +100,6 @@ QmlDebugWatch *WatchTableModel::findWatch(int objectDebugId, const QString &prop
return 0;
}
-QList<QmlDebugWatch *> WatchTableModel::watches() const
-{
- QList<QmlDebugWatch *> watches;
- for (int i=0; i<m_columns.count(); i++)
- watches << m_columns[i].watch;
- return watches;
-}
-
int WatchTableModel::rowCount(const QModelIndex &) const
{
return m_values.count();
@@ -156,8 +161,11 @@ QVariant WatchTableModel::data(const QModelIndex &idx, int role) const
void WatchTableModel::watchStateChanged()
{
QmlDebugWatch *watch = qobject_cast<QmlDebugWatch*>(sender());
- if (watch && watch->state() == QmlDebugWatch::Inactive)
+
+ if (watch && watch->state() == QmlDebugWatch::Inactive) {
removeWatch(watch);
+ watch->deleteLater();
+ }
}
int WatchTableModel::columnForWatch(QmlDebugWatch *watch) const
@@ -183,4 +191,109 @@ void WatchTableModel::addValue(int column, const QVariant &value)
endInsertRows();
}
+void WatchTableModel::togglePropertyWatch(const QmlDebugObjectReference &object, const QmlDebugPropertyReference &property)
+{
+ 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(": ")
+ + (object.name().isEmpty() ? QLatin1String("<unnamed>") : 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)
+{
+ 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::stopWatching(int column)
+{
+ QmlDebugWatch *watch = findWatch(column);
+ if (watch) {
+ m_client->removeWatch(watch);
+ delete watch;
+ watch = 0;
+ }
+}
+
+
+//----------------------------------------------
+
+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->stopWatching(col);
+ }
+ }
+}
+
+
+//----------------------------------------------
+
+WatchTableView::WatchTableView(WatchTableModel *model, QWidget *parent)
+ : QTableView(parent),
+ m_model(model)
+{
+ 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/watchtablemodel.h b/tools/qmldebugger/watchtable.h
index 306b9d8..abada2b 100644
--- a/tools/qmldebugger/watchtablemodel.h
+++ b/tools/qmldebugger/watchtable.h
@@ -1,41 +1,57 @@
#ifndef WATCHTABLEMODEL_H
#define WATCHTABLEMODEL_H
-#include <QWidget>
#include <QtCore/qpointer.h>
#include <QtCore/qlist.h>
+
+#include <QWidget>
+#include <QHeaderView>
#include <QAbstractTableModel>
+#include <QTableView>
QT_BEGIN_NAMESPACE
class QmlDebugWatch;
+class QmlEngineDebug;
+class QmlDebugPropertyReference;
+class QmlDebugObjectReference;
class WatchTableModel : public QAbstractTableModel
{
Q_OBJECT
public:
- WatchTableModel(QObject *parent = 0);
-
- void addWatch(QmlDebugWatch *watch, const QString &title);
- void updateWatch(QmlDebugWatch *watch, const QVariant &value);
+ WatchTableModel(QmlEngineDebug *client, QObject *parent = 0);
+ ~WatchTableModel();
QmlDebugWatch *findWatch(int column) const;
- QmlDebugWatch *findWatch(int objectDebugId, const QString &property) const;
+ int columnForWatch(QmlDebugWatch *watch) const;
- QList<QmlDebugWatch *> watches() const;
+ void stopWatching(int column);
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:
- int columnForWatch(QmlDebugWatch *watch) const;
- void addValue(int column, const QVariant &value);
+ 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
{
@@ -51,11 +67,44 @@ private:
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