summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/assistant/tools/qcollectiongenerator/main.cpp2
-rw-r--r--tools/configure/configure.pro3
-rw-r--r--tools/configure/configureapp.cpp77
-rw-r--r--tools/designer/src/components/buddyeditor/buddyeditor.cpp1
-rw-r--r--tools/designer/src/components/formeditor/qmdiarea_container.cpp1
-rw-r--r--tools/designer/src/lib/shared/qdesigner_menu.cpp8
-rw-r--r--tools/designer/src/lib/shared/qdesigner_toolbar.cpp1
-rw-r--r--tools/linguist/lupdate/qdeclarative.cpp84
-rw-r--r--tools/qdoc3/doc/files/qt.qdocconf10
-rw-r--r--tools/qdoc3/doc/qdoc-manual.qdoc21
-rw-r--r--tools/qdoc3/helpprojectwriter.cpp124
-rw-r--r--tools/qdoc3/helpprojectwriter.h1
-rw-r--r--tools/qdoc3/htmlgenerator.cpp523
-rw-r--r--tools/qdoc3/htmlgenerator.h27
-rw-r--r--tools/qdoc3/test/qdeclarative.qdocconf10
-rw-r--r--tools/qdoc3/test/qt-html-templates.qdocconf6
-rw-r--r--tools/qdoc3/test/style/style.css2
-rw-r--r--tools/qdoc3/tree.cpp18
-rw-r--r--tools/qml/deviceorientation_maemo5.cpp (renamed from tools/qml/deviceorientation_maemo.cpp)2
-rw-r--r--tools/qml/deviceorientation_symbian.cpp162
-rw-r--r--tools/qml/loggerwidget.cpp25
-rw-r--r--tools/qml/loggerwidget.h12
-rw-r--r--tools/qml/main.cpp23
-rw-r--r--tools/qml/proxysettings.cpp5
-rw-r--r--tools/qml/proxysettings.h4
-rw-r--r--tools/qml/proxysettings_maemo5.ui177
-rw-r--r--tools/qml/qdeclarativetester.h3
-rw-r--r--tools/qml/qml.pri14
-rw-r--r--tools/qml/qml.pro6
-rw-r--r--tools/qml/qmlruntime.cpp653
-rw-r--r--tools/qml/qmlruntime.h33
-rw-r--r--tools/qml/recopts_maemo5.ui254
-rw-r--r--tools/qml/texteditautoresizer_maemo5.h113
-rw-r--r--tools/runonphone/main.cpp18
-rw-r--r--tools/runonphone/serenum.h2
-rw-r--r--tools/runonphone/serenum_unix.cpp107
-rw-r--r--tools/runonphone/serenum_win.cpp2
-rw-r--r--tools/runonphone/symbianutils/json.cpp490
-rw-r--r--tools/runonphone/symbianutils/json.h149
-rw-r--r--tools/runonphone/symbianutils/launcher.cpp180
-rw-r--r--tools/runonphone/symbianutils/launcher.h12
-rw-r--r--tools/runonphone/symbianutils/symbianutils.pri12
-rw-r--r--tools/runonphone/symbianutils/tcftrkdevice.cpp929
-rw-r--r--tools/runonphone/symbianutils/tcftrkdevice.h295
-rw-r--r--tools/runonphone/symbianutils/tcftrkmessage.cpp562
-rw-r--r--tools/runonphone/symbianutils/tcftrkmessage.h296
-rw-r--r--tools/runonphone/symbianutils/trkdevice.cpp5
-rw-r--r--tools/runonphone/symbianutils/trkutils.cpp143
-rw-r--r--tools/runonphone/symbianutils/trkutils.h31
-rw-r--r--tools/runonphone/symbianutils/trkutils_p.h2
-rw-r--r--tools/runonphone/trksignalhandler.cpp180
-rw-r--r--tools/runonphone/trksignalhandler.h8
52 files changed, 4903 insertions, 925 deletions
diff --git a/tools/assistant/tools/qcollectiongenerator/main.cpp b/tools/assistant/tools/qcollectiongenerator/main.cpp
index 7fcb4e1..b3f6bd9 100644
--- a/tools/assistant/tools/qcollectiongenerator/main.cpp
+++ b/tools/assistant/tools/qcollectiongenerator/main.cpp
@@ -459,6 +459,8 @@ int main(int argc, char *argv[])
return -1;
}
}
+ if (!config.filesToRegister().isEmpty())
+ CollectionConfiguration::updateLastRegisterTime(helpEngine);
if (!config.title().isEmpty())
CollectionConfiguration::setWindowTitle(helpEngine, config.title());
diff --git a/tools/configure/configure.pro b/tools/configure/configure.pro
index 64a6d9a..73f3317 100644
--- a/tools/configure/configure.pro
+++ b/tools/configure/configure.pro
@@ -23,7 +23,8 @@ win32-msvc* {
PRECOMPILED_HEADER = configure_pch.h
-INCPATH += $$QT_SOURCE_TREE/src/corelib/arch/generic \
+INCLUDEPATH += \
+ $$QT_SOURCE_TREE/src/corelib/arch/generic \
$$QT_SOURCE_TREE/src/corelib/global \
$$QT_BUILD_TREE/include \
$$QT_BUILD_TREE/include/QtCore \
diff --git a/tools/configure/configureapp.cpp b/tools/configure/configureapp.cpp
index 55ad87e..4a30ef8 100644
--- a/tools/configure/configureapp.cpp
+++ b/tools/configure/configureapp.cpp
@@ -581,17 +581,13 @@ void Configure::parseCmdLine()
// Image formats --------------------------------------------
else if (configCmdLine.at(i) == "-no-gif")
dictionary[ "GIF" ] = "no";
- else if( configCmdLine.at(i) == "-qt-gif" )
- dictionary[ "GIF" ] = "auto";
else if (configCmdLine.at(i) == "-no-libtiff") {
dictionary[ "TIFF"] = "no";
dictionary[ "LIBTIFF" ] = "no";
} else if (configCmdLine.at(i) == "-qt-libtiff") {
- dictionary[ "TIFF" ] = "plugin";
dictionary[ "LIBTIFF" ] = "qt";
} else if (configCmdLine.at(i) == "-system-libtiff") {
- dictionary[ "TIFF" ] = "plugin";
dictionary[ "LIBTIFF" ] = "system";
}
@@ -599,10 +595,8 @@ void Configure::parseCmdLine()
dictionary[ "JPEG" ] = "no";
dictionary[ "LIBJPEG" ] = "no";
} else if (configCmdLine.at(i) == "-qt-libjpeg") {
- dictionary[ "JPEG" ] = "plugin";
dictionary[ "LIBJPEG" ] = "qt";
} else if (configCmdLine.at(i) == "-system-libjpeg") {
- dictionary[ "JPEG" ] = "plugin";
dictionary[ "LIBJPEG" ] = "system";
}
@@ -610,10 +604,8 @@ void Configure::parseCmdLine()
dictionary[ "PNG" ] = "no";
dictionary[ "LIBPNG" ] = "no";
} else if (configCmdLine.at(i) == "-qt-libpng") {
- dictionary[ "PNG" ] = "qt";
dictionary[ "LIBPNG" ] = "qt";
} else if (configCmdLine.at(i) == "-system-libpng") {
- dictionary[ "PNG" ] = "qt";
dictionary[ "LIBPNG" ] = "system";
}
@@ -621,10 +613,8 @@ void Configure::parseCmdLine()
dictionary[ "MNG" ] = "no";
dictionary[ "LIBMNG" ] = "no";
} else if (configCmdLine.at(i) == "-qt-libmng") {
- dictionary[ "MNG" ] = "qt";
dictionary[ "LIBMNG" ] = "qt";
} else if (configCmdLine.at(i) == "-system-libmng") {
- dictionary[ "MNG" ] = "qt";
dictionary[ "LIBMNG" ] = "system";
}
@@ -1750,22 +1740,22 @@ bool Configure::displayHelp()
desc("ZLIB", "qt", "-qt-zlib", "Use the zlib bundled with Qt.");
desc("ZLIB", "system", "-system-zlib", "Use zlib from the operating system.\nSee http://www.gzip.org/zlib\n");
- desc("GIF", "no", "-no-gif", "Do not compile the plugin for GIF reading support.");
- desc("GIF", "auto", "-qt-gif", "Compile the plugin for GIF reading support.\nSee also src/plugins/imageformats/gif/qgifhandler.h\n");
+ desc("GIF", "no", "-no-gif", "Do not compile GIF reading support.");
+ desc("GIF", "auto", "-qt-gif", "Compile GIF reading support.\nSee also src/gui/image/qgifhandler.h\n");
- desc("LIBPNG", "no", "-no-libpng", "Do not compile in PNG support.");
+ desc("LIBPNG", "no", "-no-libpng", "Do not compile PNG support.");
desc("LIBPNG", "qt", "-qt-libpng", "Use the libpng bundled with Qt.");
desc("LIBPNG", "system","-system-libpng", "Use libpng from the operating system.\nSee http://www.libpng.org/pub/png\n");
- desc("LIBMNG", "no", "-no-libmng", "Do not compile in MNG support.");
+ desc("LIBMNG", "no", "-no-libmng", "Do not compile MNG support.");
desc("LIBMNG", "qt", "-qt-libmng", "Use the libmng bundled with Qt.");
desc("LIBMNG", "system","-system-libmng", "Use libmng from the operating system.\nSee See http://www.libmng.com\n");
- desc("LIBTIFF", "no", "-no-libtiff", "Do not compile the plugin for TIFF support.");
+ desc("LIBTIFF", "no", "-no-libtiff", "Do not compile TIFF support.");
desc("LIBTIFF", "qt", "-qt-libtiff", "Use the libtiff bundled with Qt.");
desc("LIBTIFF", "system","-system-libtiff", "Use libtiff from the operating system.\nSee http://www.libtiff.org\n");
- desc("LIBJPEG", "no", "-no-libjpeg", "Do not compile the plugin for JPEG support.");
+ desc("LIBJPEG", "no", "-no-libjpeg", "Do not compile JPEG support.");
desc("LIBJPEG", "qt", "-qt-libjpeg", "Use the libjpeg bundled with Qt.");
desc("LIBJPEG", "system","-system-libjpeg", "Use libjpeg from the operating system.\nSee http://www.ijg.org\n");
@@ -1977,21 +1967,28 @@ QString Configure::defaultTo(const QString &option)
|| option == "LIBTIFF")
return "system";
- // We want PNG built-in
+ // PNG is always built-in, never a plugin
if (option == "PNG")
- return "qt";
+ return "yes";
- // The JPEG image library can only be a plugin
- if (option == "JPEG"
- || option == "MNG" || option == "TIFF")
- return "plugin";
-
- // GIF off by default
- if (option == "GIF") {
- if (dictionary["SHARED"] == "yes")
+ // These database drivers and image formats can be built-in or plugins.
+ // Prefer plugins when Qt is shared.
+ if (dictionary[ "SHARED" ] == "yes") {
+ if (option == "SQL_MYSQL"
+ || option == "SQL_MYSQL"
+ || option == "SQL_ODBC"
+ || option == "SQL_OCI"
+ || option == "SQL_PSQL"
+ || option == "SQL_TDS"
+ || option == "SQL_DB2"
+ || option == "SQL_SQLITE"
+ || option == "SQL_SQLITE2"
+ || option == "SQL_IBASE"
+ || option == "JPEG"
+ || option == "MNG"
+ || option == "TIFF"
+ || option == "GIF")
return "plugin";
- else
- return "yes";
}
// By default we do not want to compile OCI driver when compiling with
@@ -2001,18 +1998,6 @@ QString Configure::defaultTo(const QString &option)
&& option == "SQL_OCI")
return "no";
- if (option == "SQL_MYSQL"
- || option == "SQL_MYSQL"
- || option == "SQL_ODBC"
- || option == "SQL_OCI"
- || option == "SQL_PSQL"
- || option == "SQL_TDS"
- || option == "SQL_DB2"
- || option == "SQL_SQLITE"
- || option == "SQL_SQLITE2"
- || option == "SQL_IBASE")
- return "plugin";
-
if (option == "SYNCQT"
&& (!QFile::exists(sourcePath + "/bin/syncqt") ||
!QFile::exists(sourcePath + "/bin/syncqt.bat")))
@@ -2489,14 +2474,14 @@ void Configure::generateOutputVars()
if (dictionary[ "PNG" ] == "no")
qtConfig += "no-png";
- else if( dictionary[ "PNG" ] == "qt" )
+ else if (dictionary[ "PNG" ] == "yes")
qtConfig += "png";
if (dictionary[ "LIBPNG" ] == "system")
qtConfig += "system-png";
if (dictionary[ "MNG" ] == "no")
qtConfig += "no-mng";
- else if( dictionary[ "MNG" ] == "qt" )
+ else if (dictionary[ "MNG" ] == "yes")
qtConfig += "mng";
if (dictionary[ "LIBMNG" ] == "system")
qtConfig += "system-mng";
@@ -3068,10 +3053,10 @@ void Configure::generateConfigfiles()
if (dictionary["STYLE_GTK"] != "yes") qconfigList += "QT_NO_STYLE_GTK";
if (dictionary["GIF"] == "yes") qconfigList += "QT_BUILTIN_GIF_READER=1";
- if (dictionary["PNG"] == "no") qconfigList += "QT_NO_IMAGEFORMAT_PNG";
- if (dictionary["MNG"] == "no") qconfigList += "QT_NO_IMAGEFORMAT_MNG";
- if (dictionary["JPEG"] == "no") qconfigList += "QT_NO_IMAGEFORMAT_JPEG";
- if (dictionary["TIFF"] == "no") qconfigList += "QT_NO_IMAGEFORMAT_TIFF";
+ if (dictionary["PNG"] != "yes") qconfigList += "QT_NO_IMAGEFORMAT_PNG";
+ if (dictionary["MNG"] != "yes") qconfigList += "QT_NO_IMAGEFORMAT_MNG";
+ if (dictionary["JPEG"] != "yes") qconfigList += "QT_NO_IMAGEFORMAT_JPEG";
+ if (dictionary["TIFF"] != "yes") qconfigList += "QT_NO_IMAGEFORMAT_TIFF";
if (dictionary["ZLIB"] == "no") {
qconfigList += "QT_NO_ZLIB";
qconfigList += "QT_NO_COMPRESS";
diff --git a/tools/designer/src/components/buddyeditor/buddyeditor.cpp b/tools/designer/src/components/buddyeditor/buddyeditor.cpp
index 9da257e..e367f9a 100644
--- a/tools/designer/src/components/buddyeditor/buddyeditor.cpp
+++ b/tools/designer/src/components/buddyeditor/buddyeditor.cpp
@@ -405,6 +405,7 @@ QWidget *BuddyEditor::findBuddy(QLabel *l, const QWidgetList &existingBuddies) c
const int y = geom.center().y();
QWidget *neighbour = 0;
switch (l->layoutDirection()) {
+ case Qt::LayoutDirectionAuto:
case Qt::LeftToRight: { // Walk right to find next managed neighbour
const int xEnd = parent->size().width();
for (int x = geom.right() + 1; x < xEnd; x += DeltaX)
diff --git a/tools/designer/src/components/formeditor/qmdiarea_container.cpp b/tools/designer/src/components/formeditor/qmdiarea_container.cpp
index 5971094..5e266f4 100644
--- a/tools/designer/src/components/formeditor/qmdiarea_container.cpp
+++ b/tools/designer/src/components/formeditor/qmdiarea_container.cpp
@@ -105,6 +105,7 @@ void QMdiAreaContainer::positionNewMdiChild(const QWidget *area, QWidget *mdiChi
const QPoint pos = mdiChild->pos();
const QSize areaSize = area->size();
switch (QApplication::layoutDirection()) {
+ case Qt::LayoutDirectionAuto:
case Qt::LeftToRight: {
const QSize fullSize = QSize(areaSize.width() - pos.x(), areaSize.height() - pos.y());
if (fullSize.width() > MinSize && fullSize.height() > MinSize)
diff --git a/tools/designer/src/lib/shared/qdesigner_menu.cpp b/tools/designer/src/lib/shared/qdesigner_menu.cpp
index ba512e4..31a226a 100644
--- a/tools/designer/src/lib/shared/qdesigner_menu.cpp
+++ b/tools/designer/src/lib/shared/qdesigner_menu.cpp
@@ -77,6 +77,7 @@ using namespace qdesigner_internal;
static inline void extendClickableArea(QRect *subMenuRect, Qt::LayoutDirection dir)
{
switch (dir) {
+ case Qt::LayoutDirectionAuto: // Should never happen
case Qt::LeftToRight:
subMenuRect->setLeft(subMenuRect->left() - 20);
break;
@@ -931,8 +932,8 @@ void QDesignerMenu::moveUp(bool ctrl)
if (ctrl)
(void) swap(m_currentIndex, m_currentIndex - 1);
-
- m_currentIndex = qMax(0, --m_currentIndex);
+ --m_currentIndex;
+ m_currentIndex = qMax(0, m_currentIndex);
// Always re-select, swapping destroys order
update();
selectCurrentAction();
@@ -947,7 +948,8 @@ void QDesignerMenu::moveDown(bool ctrl)
if (ctrl)
(void) swap(m_currentIndex + 1, m_currentIndex);
- m_currentIndex = qMin(actions().count() - 1, ++m_currentIndex);
+ ++m_currentIndex;
+ m_currentIndex = qMin(actions().count() - 1, m_currentIndex);
update();
if (!ctrl)
selectCurrentAction();
diff --git a/tools/designer/src/lib/shared/qdesigner_toolbar.cpp b/tools/designer/src/lib/shared/qdesigner_toolbar.cpp
index 02a2f6d..e3bc64c 100644
--- a/tools/designer/src/lib/shared/qdesigner_toolbar.cpp
+++ b/tools/designer/src/lib/shared/qdesigner_toolbar.cpp
@@ -467,6 +467,7 @@ QRect ToolBarEventFilter::freeArea(const QToolBar *tb)
switch (tb->orientation()) {
case Qt::Horizontal:
switch (tb->layoutDirection()) {
+ case Qt::LayoutDirectionAuto: // Should never happen
case Qt::LeftToRight:
rc.setX(exclusionRectangle.right() + 1);
break;
diff --git a/tools/linguist/lupdate/qdeclarative.cpp b/tools/linguist/lupdate/qdeclarative.cpp
index a734e99..2377416 100644
--- a/tools/linguist/lupdate/qdeclarative.cpp
+++ b/tools/linguist/lupdate/qdeclarative.cpp
@@ -87,27 +87,31 @@ protected:
virtual void endVisit(AST::CallExpression *node)
{
+ m_bSource.clear();
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();
+ if (!node->arguments)
+ return;
+ AST::BinaryExpression *binary = AST::cast<AST::BinaryExpression *>(node->arguments->expression);
+ if (binary) {
+ if (!createString(binary))
+ m_bSource.clear();
+ }
+ AST::StringLiteral *literal = AST::cast<AST::StringLiteral *>(node->arguments->expression);
+ if (literal || !m_bSource.isEmpty()) {
+ const QString source = literal ? literal->value->asString() : m_bSource;
QString comment;
bool plural = false;
AST::ArgumentList *commentNode = node->arguments->next;
- if (commentNode) {
+ if (commentNode && AST::cast<AST::StringLiteral *>(commentNode->expression)) {
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;
- }
- }
+ if (nNode)
+ plural = true;
}
TranslatorMessage msg(m_component, source,
@@ -126,22 +130,25 @@ protected:
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;
- }
- }
- }
+ if (!sourceNode)
+ return;
+ literal = AST::cast<AST::StringLiteral *>(sourceNode->expression);
+ AST::BinaryExpression *binary = AST::cast<AST::BinaryExpression *>(sourceNode->expression);
+ if (binary) {
+ if (!createString(binary))
+ m_bSource.clear();
+ }
+ if (!literal && m_bSource.isEmpty())
+ return;
+ source = literal ? literal->value->asString() : m_bSource;
+ AST::ArgumentList *commentNode = sourceNode->next;
+ if (commentNode && AST::cast<AST::StringLiteral *>(commentNode->expression)) {
+ literal = AST::cast<AST::StringLiteral *>(commentNode->expression);
+ comment = literal->value->asString();
+
+ AST::ArgumentList *nNode = commentNode->next;
+ if (nNode)
+ plural = true;
}
TranslatorMessage msg(context, source,
@@ -156,9 +163,34 @@ protected:
}
private:
+ bool createString(AST::BinaryExpression *b) {
+ if (!b || b->op != 0)
+ return false;
+ AST::BinaryExpression *l = AST::cast<AST::BinaryExpression *>(b->left);
+ AST::BinaryExpression *r = AST::cast<AST::BinaryExpression *>(b->right);
+ AST::StringLiteral *ls = AST::cast<AST::StringLiteral *>(b->left);
+ AST::StringLiteral *rs = AST::cast<AST::StringLiteral *>(b->right);
+ if ((!l && !ls) || (!r && !rs))
+ return false;
+ if (l) {
+ if (!createString(l))
+ return false;
+ } else
+ m_bSource.prepend(ls->value->asString());
+
+ if (r) {
+ if (!createString(r))
+ return false;
+ } else
+ m_bSource.append(rs->value->asString());
+
+ return true;
+ }
+
Translator *m_translator;
QString m_fileName;
QString m_component;
+ QString m_bSource;
};
QString createErrorString(const QString &filename, const QString &code, Parser &parser)
diff --git a/tools/qdoc3/doc/files/qt.qdocconf b/tools/qdoc3/doc/files/qt.qdocconf
index 942d023..09c112a 100644
--- a/tools/qdoc3/doc/files/qt.qdocconf
+++ b/tools/qdoc3/doc/files/qt.qdocconf
@@ -8,7 +8,7 @@ project = Qt
versionsym =
version = %VERSION%
description = Qt Reference Documentation
-url = http://qt.nokia.com/doc/4.6
+url = http://qt.nokia.com/doc/4.7
edition.Console.modules = QtCore QtDBus QtNetwork QtScript QtSql QtXml \
QtXmlPatterns QtTest
@@ -22,7 +22,7 @@ edition.DesktopLight.groups = -graphicsview-api
qhp.projects = Qt
qhp.Qt.file = qt.qhp
-qhp.Qt.namespace = com.trolltech.qt.460
+qhp.Qt.namespace = com.trolltech.qt.470
qhp.Qt.virtualFolder = qdoc
qhp.Qt.indexTitle = Qt Reference Documentation
qhp.Qt.indexRoot =
@@ -36,9 +36,9 @@ qhp.Qt.extraFiles = classic.css \
images/dynamiclayouts-example.png \
images/stylesheet-coffee-plastique.png
-qhp.Qt.filterAttributes = qt 4.6.0 qtrefdoc
-qhp.Qt.customFilters.Qt.name = Qt 4.6.0
-qhp.Qt.customFilters.Qt.filterAttributes = qt 4.6.0
+qhp.Qt.filterAttributes = qt 4.7.0 qtrefdoc
+qhp.Qt.customFilters.Qt.name = Qt 4.7.0
+qhp.Qt.customFilters.Qt.filterAttributes = qt 4.7.0
qhp.Qt.subprojects = classes overviews examples
qhp.Qt.subprojects.classes.title = Classes
qhp.Qt.subprojects.classes.indexTitle = Qt's Classes
diff --git a/tools/qdoc3/doc/qdoc-manual.qdoc b/tools/qdoc3/doc/qdoc-manual.qdoc
index 57c17ba..c3ab731 100644
--- a/tools/qdoc3/doc/qdoc-manual.qdoc
+++ b/tools/qdoc3/doc/qdoc-manual.qdoc
@@ -8075,6 +8075,27 @@
qhp.Qt.subprojects.examples.indexTitle = Qt Examples
qhp.Qt.subprojects.examples.selectors = fake:example
\endcode
+
+ To create a table of contents for a manual, create a subproject with
+ a \c{type} property and set it to \c{manual}. The page in the documentation
+ referred to by the \c{indexTitle} property must contain a list of links
+ that acts as a table of contents for the whole manual. QDoc will take the
+ information in this list and create a table of contents for the subproject.
+
+ For example, the configuration file for Qt Creator defines only one
+ subproject for its documentation, including all the documentation in a
+ single manual:
+
+ \code
+ qhp.QtCreator.subprojects = manual
+ qhp.QtCreator.subprojects.manual.title = Qt Creator Manual
+ qhp.QtCreator.subprojects.manual.indexTitle = Qt Creator Manual
+ qhp.QtCreator.subprojects.manual.type = manual
+ \endcode
+
+ In this example, the page entitled "Qt Creator Manual" contains a nested
+ list of links to pages in the documentation which is duplicated in
+ Qt Assistant's Contents tab.
*/
/*!
diff --git a/tools/qdoc3/helpprojectwriter.cpp b/tools/qdoc3/helpprojectwriter.cpp
index edba097..98246c4 100644
--- a/tools/qdoc3/helpprojectwriter.cpp
+++ b/tools/qdoc3/helpprojectwriter.cpp
@@ -41,7 +41,7 @@
#include <QHash>
#include <QMap>
-#include <qdebug.h>
+//#include <qdebug.h>
#include "atom.h"
#include "helpprojectwriter.h"
@@ -91,6 +91,7 @@ HelpProjectWriter::HelpProjectWriter(const Config &config, const QString &defaul
subproject.title = config.getString(subprefix + "title");
subproject.indexTitle = config.getString(subprefix + "indexTitle");
subproject.sortPages = config.getBool(subprefix + "sortPages");
+ subproject.type = config.getString(subprefix + "type");
readSelectors(subproject, config.getStringList(subprefix + "selectors"));
project.subprojects[name] = subproject;
}
@@ -625,44 +626,99 @@ void HelpProjectWriter::generateProject(HelpProject &project)
foreach (const QString &name, project.subprojects.keys()) {
SubProject subproject = project.subprojects[name];
- if (!name.isEmpty()) {
- writer.writeStartElement("section");
- QString indexPath = tree->fullDocumentLocation(tree->findFakeNodeByTitle(subproject.indexTitle));
- writer.writeAttribute("ref", HtmlGenerator::cleanRef(indexPath));
- writer.writeAttribute("title", subproject.title);
- project.files.insert(indexPath);
- }
- if (subproject.sortPages) {
- QStringList titles = subproject.nodes.keys();
- titles.sort();
- foreach (const QString &title, titles)
- writeNode(project, writer, subproject.nodes[title]);
+ if (subproject.type == QLatin1String("manual")) {
+
+ const FakeNode *indexPage = tree->findFakeNodeByTitle(subproject.indexTitle);
+ if (indexPage) {
+ Text indexBody = indexPage->doc().body();
+ const Atom *atom = indexBody.firstAtom();
+ QStack<int> sectionStack;
+ bool inItem = false;
+
+ while (atom) {
+ switch (atom->type()) {
+ case Atom::ListLeft:
+ sectionStack.push(0);
+ break;
+ case Atom::ListRight:
+ if (sectionStack.pop() > 0)
+ writer.writeEndElement(); // section
+ break;
+ case Atom::ListItemLeft:
+ inItem = true;
+ break;
+ case Atom::ListItemRight:
+ inItem = false;
+ break;
+ case Atom::Link:
+ if (inItem) {
+ if (sectionStack.top() > 0)
+ writer.writeEndElement(); // section
+
+ const FakeNode *page = tree->findFakeNodeByTitle(atom->string());
+ writer.writeStartElement("section");
+ QString indexPath = tree->fullDocumentLocation(page);
+ writer.writeAttribute("ref", HtmlGenerator::cleanRef(indexPath));
+ writer.writeAttribute("title", atom->string());
+ project.files.insert(indexPath);
+
+ sectionStack.top() += 1;
+ }
+ break;
+ default:
+ ;
+ }
+
+ if (atom == indexBody.lastAtom())
+ break;
+ atom = atom->next();
+ }
+ } else
+ rootNode->doc().location().warning(
+ tr("Failed to find index: %1").arg(subproject.indexTitle)
+ );
+
} else {
- // Find a contents node and navigate from there, using the NextLink values.
- foreach (const Node *node, subproject.nodes) {
- QString nextTitle = node->links().value(Node::NextLink).first;
- if (!nextTitle.isEmpty() &&
- node->links().value(Node::ContentsLink).first.isEmpty()) {
-
- FakeNode *nextPage = const_cast<FakeNode *>(tree->findFakeNodeByTitle(nextTitle));
-
- // Write the contents node.
- writeNode(project, writer, node);
-
- while (nextPage) {
- writeNode(project, writer, nextPage);
- nextTitle = nextPage->links().value(Node::NextLink).first;
- if(nextTitle.isEmpty())
- break;
- nextPage = const_cast<FakeNode *>(tree->findFakeNodeByTitle(nextTitle));
+
+ if (!name.isEmpty()) {
+ writer.writeStartElement("section");
+ QString indexPath = tree->fullDocumentLocation(tree->findFakeNodeByTitle(subproject.indexTitle));
+ writer.writeAttribute("ref", HtmlGenerator::cleanRef(indexPath));
+ writer.writeAttribute("title", subproject.title);
+ project.files.insert(indexPath);
+ }
+ if (subproject.sortPages) {
+ QStringList titles = subproject.nodes.keys();
+ titles.sort();
+ foreach (const QString &title, titles)
+ writeNode(project, writer, subproject.nodes[title]);
+ } else {
+ // Find a contents node and navigate from there, using the NextLink values.
+ foreach (const Node *node, subproject.nodes) {
+ QString nextTitle = node->links().value(Node::NextLink).first;
+ if (!nextTitle.isEmpty() &&
+ node->links().value(Node::ContentsLink).first.isEmpty()) {
+
+ FakeNode *nextPage = const_cast<FakeNode *>(tree->findFakeNodeByTitle(nextTitle));
+
+ // Write the contents node.
+ writeNode(project, writer, node);
+
+ while (nextPage) {
+ writeNode(project, writer, nextPage);
+ nextTitle = nextPage->links().value(Node::NextLink).first;
+ if(nextTitle.isEmpty())
+ break;
+ nextPage = const_cast<FakeNode *>(tree->findFakeNodeByTitle(nextTitle));
+ }
+ break;
}
- break;
}
}
- }
- if (!name.isEmpty())
- writer.writeEndElement(); // section
+ if (!name.isEmpty())
+ writer.writeEndElement(); // section
+ }
}
writer.writeEndElement(); // section
diff --git a/tools/qdoc3/helpprojectwriter.h b/tools/qdoc3/helpprojectwriter.h
index 511a9dd..7a67dff 100644
--- a/tools/qdoc3/helpprojectwriter.h
+++ b/tools/qdoc3/helpprojectwriter.h
@@ -60,6 +60,7 @@ struct SubProject
QString indexTitle;
QHash<Node::Type, QSet<FakeNode::SubType> > selectors;
bool sortPages;
+ QString type;
QHash<QString, const Node *> nodes;
};
diff --git a/tools/qdoc3/htmlgenerator.cpp b/tools/qdoc3/htmlgenerator.cpp
index b93db4f..89b1e98 100644
--- a/tools/qdoc3/htmlgenerator.cpp
+++ b/tools/qdoc3/htmlgenerator.cpp
@@ -61,6 +61,7 @@ QT_BEGIN_NAMESPACE
#define COMMAND_VERSION Doc::alias("version")
int HtmlGenerator::id = 0;
+bool HtmlGenerator::debugging_on = false;
QString HtmlGenerator::sinceTitles[] =
{
@@ -443,7 +444,7 @@ int HtmlGenerator::generateAtom(const Atom *atom,
{
int skipAhead = 0;
static bool in_para = false;
-
+
switch (atom->type()) {
case Atom::AbstractLeft:
break;
@@ -1066,11 +1067,11 @@ int HtmlGenerator::generateAtom(const Atom *atom,
}
sectionNumber.last() = QString::number(sectionNumber.last().toInt() + 1);
}
- out() << "<a name=\"sec-" << sectionNumber.join("-") << "\"></a>\n";
+ out() << "<a name=\"sec-" << sectionNumber.join("-") << "\"></a><div class=\"navTop\"><a href=\"#toc\"><img src=\"./images/bullet_up.png\"></a></div>\n";
}
#else
out() << "<a name=\"" << Doc::canonicalTitle(Text::sectionHeading(atom).toString())
- << "\"></a>\n";
+ << "\"></a><div class=\"navTop\"><a href=\"#toc\"><img src=\"./images/bullet_up.png\"></a></div>\n";
#endif
break;
case Atom::SectionRight:
@@ -1339,26 +1340,26 @@ void HtmlGenerator::generateClassLikeNode(const InnerNode *inner,
}
else {
if (!s->members.isEmpty()) {
- out() << "<hr />\n";
+ // out() << "<hr />\n";
out() << "<a name=\""
<< registerRef((*s).name.toLower())
- << "\"></a>\n";
+ << "\"></a><div class=\"navTop\"><a href=\"#toc\"><img src=\"./images/bullet_up.png\"></a></div>\n";
out() << "<h2>" << protectEnc((*s).name) << "</h2>\n";
generateSection(s->members, inner, marker, CodeMarker::Summary);
}
if (!s->reimpMembers.isEmpty()) {
QString name = QString("Reimplemented ") + (*s).name;
- out() << "<hr />\n";
+ // out() << "<hr />\n";
out() << "<a name=\""
<< registerRef(name.toLower())
- << "\"></a>\n";
+ << "\"></a><div class=\"navTop\"><a href=\"#toc\"><img src=\"./images/bullet_up.png\"></a></div>\n";
out() << "<h2>" << protectEnc(name) << "</h2>\n";
generateSection(s->reimpMembers, inner, marker, CodeMarker::Summary);
}
if (!s->inherited.isEmpty()) {
out() << "<ul>\n";
- generateSectionInheritedList(*s, inner, marker, true);
+ generateSectionInheritedList(*s, inner, marker);
out() << "</ul>\n";
}
}
@@ -1378,11 +1379,11 @@ void HtmlGenerator::generateClassLikeNode(const InnerNode *inner,
out() << "</ul>\n";
}
- out() << "<a name=\"" << registerRef("details") << "\"></a>\n";
+ out() << "<a name=\"" << registerRef("details") << "\"></a><div class=\"navTop\"><a href=\"#toc\"><img src=\"./images/bullet_up.png\"></a></div>\n";
if (!inner->doc().isEmpty()) {
- out() << "<hr />\n"
- << "<div class=\"descr\"/>\n" // QTBUG-9504
+ //out() << "<hr />\n"
+ out() << "<div class=\"descr\"/>\n" // QTBUG-9504
<< "<h2>" << "Detailed Description" << "</h2>\n";
generateBody(inner, marker);
out() << "</div>\n"; // QTBUG-9504
@@ -1392,7 +1393,7 @@ void HtmlGenerator::generateClassLikeNode(const InnerNode *inner,
sections = marker->sections(inner, CodeMarker::Detailed, CodeMarker::Okay);
s = sections.begin();
while (s != sections.end()) {
- out() << "<hr />\n";
+ //out() << "<hr />\n";
if (!(*s).divClass.isEmpty())
out() << "<div class=\"" << (*s).divClass << "\"/>\n"; // QTBUG-9504
out() << "<h2>" << protectEnc((*s).name) << "</h2>\n";
@@ -1518,12 +1519,12 @@ void HtmlGenerator::generateFakeNode(const FakeNode *fake, CodeMarker *marker)
generateStatus(fake, marker);
if (moduleNamespaceMap.contains(fake->name())) {
- out() << "<a name=\"" << registerRef("namespaces") << "\"></a>\n";
+ out() << "<a name=\"" << registerRef("namespaces") << "\"></a><div class=\"navTop\"><a href=\"#toc\"><img src=\"./images/bullet_up.png\"></a></div>\n";
out() << "<h2>Namespaces</h2>\n";
generateAnnotatedList(fake, marker, moduleNamespaceMap[fake->name()]);
}
if (moduleClassMap.contains(fake->name())) {
- out() << "<a name=\"" << registerRef("classes") << "\"></a>\n";
+ out() << "<a name=\"" << registerRef("classes") << "\"></a><div class=\"navTop\"><a href=\"#toc\"><img src=\"./images/bullet_up.png\"></a></div>\n";
out() << "<h2>Classes</h2>\n";
generateAnnotatedList(fake, marker, moduleClassMap[fake->name()]);
}
@@ -1586,19 +1587,19 @@ void HtmlGenerator::generateFakeNode(const FakeNode *fake, CodeMarker *marker)
sections = marker->qmlSections(qml_cn,CodeMarker::Summary);
s = sections.begin();
while (s != sections.end()) {
- out() << "<a name=\"" << registerRef((*s).name) << "\"></a>\n";
+ out() << "<a name=\"" << registerRef((*s).name) << "\"></a><div class=\"navTop\"><a href=\"#toc\"><img src=\"./images/bullet_up.png\"></a></div>\n";
out() << "<h2>" << protectEnc((*s).name) << "</h2>\n";
generateQmlSummary(*s,fake,marker);
++s;
}
- out() << "<a name=\"" << registerRef("details") << "\"></a>\n";
+ out() << "<a name=\"" << registerRef("details") << "\"></a><div class=\"navTop\"><a href=\"#toc\"><img src=\"./images/bullet_up.png\"></a></div>\n";
out() << "<h2>" << "Detailed Description" << "</h2>\n";
generateBody(fake, marker);
if (cn)
generateQmlText(cn->doc().body(), cn, marker, fake->name());
generateAlsoList(fake, marker);
- out() << "<hr />\n";
+ //out() << "<hr />\n";
sections = marker->qmlSections(qml_cn,CodeMarker::Detailed);
s = sections.begin();
@@ -1622,7 +1623,7 @@ void HtmlGenerator::generateFakeNode(const FakeNode *fake, CodeMarker *marker)
sections = marker->sections(fake, CodeMarker::Summary, CodeMarker::Okay);
s = sections.begin();
while (s != sections.end()) {
- out() << "<a name=\"" << registerRef((*s).name) << "\"></a>\n";
+ out() << "<a name=\"" << registerRef((*s).name) << "\"></a><div class=\"navTop\"><a href=\"#toc\"><img src=\"./images/bullet_up.png\"></a></div>\n";
out() << "<h2>" << protectEnc((*s).name) << "</h2>\n";
generateSectionList(*s, fake, marker, CodeMarker::Summary);
++s;
@@ -1630,7 +1631,7 @@ void HtmlGenerator::generateFakeNode(const FakeNode *fake, CodeMarker *marker)
Text brief = fake->doc().briefText();
if (fake->subType() == Node::Module && !brief.isEmpty()) {
- out() << "<a name=\"" << registerRef("details") << "\"></a>\n";
+ out() << "<a name=\"" << registerRef("details") << "\"></a><div class=\"navTop\"><a href=\"#toc\"><img src=\"./images/bullet_up.png\"></a></div>\n";
out() << "<div class=\"descr\"/>\n"; // QTBUG-9504
out() << "<h2>" << "Detailed Description" << "</h2>\n";
}
@@ -1655,7 +1656,7 @@ void HtmlGenerator::generateFakeNode(const FakeNode *fake, CodeMarker *marker)
sections = marker->sections(fake, CodeMarker::Detailed, CodeMarker::Okay);
s = sections.begin();
while (s != sections.end()) {
- out() << "<hr />\n";
+ //out() << "<hr />\n";
out() << "<h2>" << protectEnc((*s).name) << "</h2>\n";
NodeList::ConstIterator m = (*s).members.begin();
@@ -1801,9 +1802,10 @@ void HtmlGenerator::generateHeader(const QString& title,
if (offlineDocs)
{
- out() << " <link rel=\"stylesheet\" type=\"text/css\" href=\"style/OfflineStyle.css\" />";
+ out() << " <link rel=\"stylesheet\" type=\"text/css\" href=\"style/style.css\" />";
out() << "</head>\n";
out() << "<body class=\"offline narrow\" >\n"; // narrow mainly for Creator
+ out() << " <script src=\"scripts/functions.js\" type=\"text/javascript\"></script>\n";
}
else
{
@@ -2039,7 +2041,7 @@ void HtmlGenerator::generateTableOfContents(const Node *node,
inLink = true;
out() << "<div class=\"toc\">\n";
- out() << "<h3>Contents</h3>\n";
+ out() << "<h3><a name=\"toc\">Contents</a></h3>\n";
sectionNumber.append("1");
out() << "<ul>\n";
@@ -2252,7 +2254,7 @@ QString HtmlGenerator::generateLowStatusMemberFile(const InnerNode *inner,
sections = marker->sections(inner, CodeMarker::Detailed, status);
for (i = 0; i < sections.size(); ++i) {
- out() << "<hr />\n";
+ //out() << "<hr />\n";
out() << "<h2>" << protectEnc(sections.at(i).name) << "</h2>\n";
NodeList::ConstIterator m = sections.at(i).members.begin();
@@ -2621,7 +2623,7 @@ void HtmlGenerator::generateLegaleseList(const Node *relative,
QMap<Text, const Node *>::ConstIterator it = legaleseTexts.begin();
while (it != legaleseTexts.end()) {
Text text = it.key();
- out() << "<hr />\n";
+ //out() << "<hr />\n";
generateText(text, relative, marker);
out() << "<ul>\n";
do {
@@ -2634,44 +2636,6 @@ void HtmlGenerator::generateLegaleseList(const Node *relative,
}
}
-/*void HtmlGenerator::generateSynopsis(const Node *node,
- const Node *relative,
- CodeMarker *marker,
- CodeMarker::SynopsisStyle style)
-{
- QString marked = marker->markedUpSynopsis(node, relative, style);
- QRegExp templateTag("(<[^@>]*>)");
- if (marked.indexOf(templateTag) != -1) {
- QString contents = protectEnc(marked.mid(templateTag.pos(1),
- templateTag.cap(1).length()));
- marked.replace(templateTag.pos(1), templateTag.cap(1).length(),
- contents);
- }
- marked.replace(QRegExp("<@param>([a-z]+)_([1-9n])</@param>"),
- "<i>\\1<sub>\\2</sub></i>");
- marked.replace("<@param>", "<i>");
- marked.replace("</@param>", "</i>");
-
- if (style == CodeMarker::Summary)
- marked.replace("@name>", "b>");
-
- if (style == CodeMarker::SeparateList) {
- QRegExp extraRegExp("<@extra>.*</@extra>");
- extraRegExp.setMinimal(true);
- marked.replace(extraRegExp, "");
- }
- else {
- marked.replace("<@extra>", "&nbsp;&nbsp;<tt>");
- marked.replace("</@extra>", "</tt>");
- }
-
- if (style != CodeMarker::Detailed) {
- marked.replace("<@type>", "");
- marked.replace("</@type>", "");
- }
- out() << highlightedCode(marked, marker, relative);
-}*/
-
#ifdef QDOC_QML
void HtmlGenerator::generateQmlItem(const Node *node,
const Node *relative,
@@ -2701,7 +2665,19 @@ void HtmlGenerator::generateQmlItem(const Node *node,
marked.replace("<@type>", "");
marked.replace("</@type>", "");
}
+ if (node->type() == Node::QmlProperty) {
+ const QmlPropertyNode* qpn = static_cast<const QmlPropertyNode*>(node);
+ if (!summary && qpn->name() == "color" && qpn->dataType() == "color") {
+ if (relative && relative->name() == "GradientStop") {
+ qDebug() << "color : color";
+ debugging_on = true;
+ qDebug() << " " << relative->name() << relative->type() << relative->subType();
+ qDebug() << marked;
+ }
+ }
+ }
out() << highlightedCode(marked, marker, relative);
+ debugging_on = false;
}
#endif
@@ -2819,24 +2795,23 @@ void HtmlGenerator::generateOverviewList(const Node *relative, CodeMarker * /* m
}
}
-#ifdef QDOC_NAME_ALIGNMENT
void HtmlGenerator::generateSection(const NodeList& nl,
const Node *relative,
CodeMarker *marker,
CodeMarker::SynopsisStyle style)
{
- bool name_alignment = true;
+ bool alignNames = true;
if (!nl.isEmpty()) {
bool twoColumn = false;
if (style == CodeMarker::SeparateList) {
- name_alignment = false;
+ alignNames = false;
twoColumn = (nl.count() >= 16);
}
else if (nl.first()->type() == Node::Property) {
twoColumn = (nl.count() >= 5);
- name_alignment = false;
+ alignNames = false;
}
- if (name_alignment) {
+ if (alignNames) {
out() << "<table class=\"alignedsummary\">\n";
}
else {
@@ -2854,7 +2829,7 @@ void HtmlGenerator::generateSection(const NodeList& nl,
continue;
}
- if (name_alignment) {
+ if (alignNames) {
out() << "<tr><td class=\"memItemLeft rightAlign topAlign\"> ";
}
else {
@@ -2863,15 +2838,15 @@ void HtmlGenerator::generateSection(const NodeList& nl,
out() << "<li class=\"fn\">";
}
- generateSynopsis(*m, relative, marker, style, name_alignment);
- if (name_alignment)
+ generateSynopsis(*m, relative, marker, style, alignNames);
+ if (alignNames)
out() << "</td></tr>\n";
else
out() << "</li>\n";
i++;
++m;
}
- if (name_alignment)
+ if (alignNames)
out() << "</table>\n";
else {
out() << "</ul>\n";
@@ -2886,18 +2861,18 @@ void HtmlGenerator::generateSectionList(const Section& section,
CodeMarker *marker,
CodeMarker::SynopsisStyle style)
{
- bool name_alignment = true;
+ bool alignNames = true;
if (!section.members.isEmpty()) {
bool twoColumn = false;
if (style == CodeMarker::SeparateList) {
- name_alignment = false;
+ alignNames = false;
twoColumn = (section.members.count() >= 16);
}
else if (section.members.first()->type() == Node::Property) {
twoColumn = (section.members.count() >= 5);
- name_alignment = false;
+ alignNames = false;
}
- if (name_alignment) {
+ if (alignNames) {
out() << "<table class=\"alignedsummary\">\n";
}
else {
@@ -2915,7 +2890,7 @@ void HtmlGenerator::generateSectionList(const Section& section,
continue;
}
- if (name_alignment) {
+ if (alignNames) {
out() << "<tr><td class=\"memItemLeft topAlign rightAlign\"> ";
}
else {
@@ -2924,15 +2899,15 @@ void HtmlGenerator::generateSectionList(const Section& section,
out() << "<li class=\"fn\">";
}
- generateSynopsis(*m, relative, marker, style, name_alignment);
- if (name_alignment)
+ generateSynopsis(*m, relative, marker, style, alignNames);
+ if (alignNames)
out() << "</td></tr>\n";
else
out() << "</li>\n";
i++;
++m;
}
- if (name_alignment)
+ if (alignNames)
out() << "</table>\n";
else {
out() << "</ul>\n";
@@ -2943,22 +2918,18 @@ void HtmlGenerator::generateSectionList(const Section& section,
if (style == CodeMarker::Summary && !section.inherited.isEmpty()) {
out() << "<ul>\n";
- generateSectionInheritedList(section, relative, marker, name_alignment);
+ generateSectionInheritedList(section, relative, marker);
out() << "</ul>\n";
}
}
void HtmlGenerator::generateSectionInheritedList(const Section& section,
const Node *relative,
- CodeMarker *marker,
- bool nameAlignment)
+ CodeMarker *marker)
{
QList<QPair<ClassNode *, int> >::ConstIterator p = section.inherited.begin();
while (p != section.inherited.end()) {
- if (nameAlignment)
- out() << "<li class=\"fn\">";
- else
- out() << "<li class=\"fn\">";
+ out() << "<li class=\"fn\">";
out() << (*p).second << " ";
if ((*p).second == 1) {
out() << section.singularMember;
@@ -2978,7 +2949,7 @@ void HtmlGenerator::generateSynopsis(const Node *node,
const Node *relative,
CodeMarker *marker,
CodeMarker::SynopsisStyle style,
- bool nameAlignment)
+ bool alignNames)
{
QString marked = marker->markedUpSynopsis(node, relative, style);
QRegExp templateTag("(<[^@>]*>)");
@@ -3011,14 +2982,13 @@ void HtmlGenerator::generateSynopsis(const Node *node,
marked.replace("<@type>", "");
marked.replace("</@type>", "");
}
- out() << highlightedCode(marked, marker, relative, style, nameAlignment);
+ out() << highlightedCode(marked, marker, relative, alignNames);
}
QString HtmlGenerator::highlightedCode(const QString& markedCode,
- CodeMarker *marker,
- const Node *relative,
- CodeMarker::SynopsisStyle ,
- bool nameAlignment)
+ CodeMarker* marker,
+ const Node* relative,
+ bool alignNames)
{
QString src = markedCode;
QString html;
@@ -3028,20 +2998,24 @@ QString HtmlGenerator::highlightedCode(const QString& markedCode,
const QChar charLangle = '<';
const QChar charAt = '@';
- // replace all <@link> tags: "(<@link node=\"([^\"]+)\">).*(</@link>)"
+ static const QString typeTag("type");
+ static const QString headerTag("headerfile");
+ static const QString funcTag("func");
static const QString linkTag("link");
+
+ // replace all <@link> tags: "(<@link node=\"([^\"]+)\">).*(</@link>)"
bool done = false;
- for (int i = 0, n = src.size(); i < n;) {
+ for (int i = 0, srcSize = src.size(); i < srcSize;) {
if (src.at(i) == charLangle && src.at(i + 1).unicode() == '@') {
- if (nameAlignment && !done) {// && (i != 0)) Why was this here?
+ if (alignNames && !done) {// && (i != 0)) Why was this here?
html += "</td><td class=\"memItemRight bottomAlign\">";
done = true;
}
i += 2;
- if (parseArg(src, linkTag, &i, n, &arg, &par1)) {
+ if (parseArg(src, linkTag, &i, srcSize, &arg, &par1)) {
html += "<b>";
- QString link = linkForNode(
- CodeMarker::nodeForString(par1.toString()), relative);
+ const Node* n = CodeMarker::nodeForString(par1.toString());
+ QString link = linkForNode(n, relative);
addLink(link, arg, &html);
html += "</b>";
}
@@ -3061,16 +3035,14 @@ QString HtmlGenerator::highlightedCode(const QString& markedCode,
// replace all <@func> tags: "(<@func target=\"([^\"]*)\">)(.*)(</@func>)"
src = html;
html = QString();
- static const QString funcTag("func");
- for (int i = 0, n = src.size(); i < n;) {
+ for (int i = 0, srcSize = src.size(); i < srcSize;) {
if (src.at(i) == charLangle && src.at(i + 1) == charAt) {
i += 2;
- if (parseArg(src, funcTag, &i, n, &arg, &par1)) {
- QString link = linkForNode(
- marker->resolveTarget(par1.toString(),
- myTree,
- relative),
- relative);
+ if (parseArg(src, funcTag, &i, srcSize, &arg, &par1)) {
+ const Node* n = marker->resolveTarget(par1.toString(),
+ myTree,
+ relative);
+ QString link = linkForNode(n, relative);
addLink(link, arg, &html);
par1 = QStringRef();
}
@@ -3088,296 +3060,36 @@ QString HtmlGenerator::highlightedCode(const QString& markedCode,
// replace all "(<@(type|headerfile|func)(?: +[^>]*)?>)(.*)(</@\\2>)" tags
src = html;
html = QString();
- static const QString typeTags[] = { "type", "headerfile", "func" };
- for (int i = 0, n = src.size(); i < n;) {
- if (src.at(i) == charLangle && src.at(i + 1) == charAt) {
+
+ for (int i=0, srcSize=src.size(); i<srcSize;) {
+ if (src.at(i) == charLangle && src.at(i+1) == charAt) {
i += 2;
bool handled = false;
- for (int k = 0; k != 3; ++k) {
- if (parseArg(src, typeTags[k], &i, n, &arg, &par1)) {
- par1 = QStringRef();
- QString link = linkForNode(
- marker->resolveTarget(arg.toString(), myTree, relative),
- relative);
- addLink(link, arg, &html);
- handled = true;
- break;
+ if (parseArg(src, typeTag, &i, srcSize, &arg, &par1)) {
+ par1 = QStringRef();
+ const Node* n = marker->resolveTarget(arg.toString(), myTree, relative);
+ if (HtmlGenerator::debugging_on) {
+ if (n) {
+ qDebug() << " " << n->name() << n->type() << n->subType();
+ qDebug() << " " << relative->name() << relative->type() << relative->subType();
+ }
}
+ addLink(linkForNode(n,relative), arg, &html);
+ handled = true;
}
- if (!handled) {
- html += charLangle;
- html += charAt;
+ else if (parseArg(src, headerTag, &i, srcSize, &arg, &par1)) {
+ par1 = QStringRef();
+ const Node* n = marker->resolveTarget(arg.toString(), myTree, relative);
+ addLink(linkForNode(n,relative), arg, &html);
+ handled = true;
}
- }
- else {
- html += src.at(i++);
- }
- }
-
- // replace all
- // "<@comment>" -> "<span class=\"comment\">";
- // "<@preprocessor>" -> "<span class=\"preprocessor\">";
- // "<@string>" -> "<span class=\"string\">";
- // "<@char>" -> "<span class=\"char\">";
- // "</@(?:comment|preprocessor|string|char)>" -> "</span>"
- src = html;
- html = QString();
- static const QString spanTags[] = {
- "<@comment>", "<span class=\"comment\">",
- "<@preprocessor>", "<span class=\"preprocessor\">",
- "<@string>", "<span class=\"string\">",
- "<@char>", "<span class=\"char\">",
- "</@comment>", "</span>",
- "</@preprocessor>","</span>",
- "</@string>", "</span>",
- "</@char>", "</span>"
- // "<@char>", "<font color=blue>",
- // "</@char>", "</font>",
- // "<@func>", "<font color=green>",
- // "</@func>", "</font>",
- // "<@id>", "<i>",
- // "</@id>", "</i>",
- // "<@keyword>", "<b>",
- // "</@keyword>", "</b>",
- // "<@number>", "<font color=yellow>",
- // "</@number>", "</font>",
- // "<@op>", "<b>",
- // "</@op>", "</b>",
- // "<@param>", "<i>",
- // "</@param>", "</i>",
- // "<@string>", "<font color=green>",
- // "</@string>", "</font>",
- };
- for (int i = 0, n = src.size(); i < n;) {
- if (src.at(i) == charLangle) {
- bool handled = false;
- for (int k = 0; k != 8; ++k) {
- const QString & tag = spanTags[2 * k];
- if (tag == QStringRef(&src, i, tag.length())) {
- html += spanTags[2 * k + 1];
- i += tag.length();
- handled = true;
- break;
- }
- }
- if (!handled) {
- ++i;
- if (src.at(i) == charAt ||
- (src.at(i) == QLatin1Char('/') && src.at(i + 1) == charAt)) {
- // drop 'our' unknown tags (the ones still containing '@')
- while (i < n && src.at(i) != QLatin1Char('>'))
- ++i;
- ++i;
- }
- else {
- // retain all others
- html += charLangle;
- }
+ else if (parseArg(src, funcTag, &i, srcSize, &arg, &par1)) {
+ par1 = QStringRef();
+ const Node* n = marker->resolveTarget(arg.toString(), myTree, relative);
+ addLink(linkForNode(n,relative), arg, &html);
+ handled = true;
}
- }
- else {
- html += src.at(i);
- ++i;
- }
- }
-
- return html;
-}
-
-#else
-void HtmlGenerator::generateSectionList(const Section& section,
- const Node *relative,
- CodeMarker *marker,
- CodeMarker::SynopsisStyle style)
-{
- if (!section.members.isEmpty()) {
- bool twoColumn = false;
- if (style == CodeMarker::SeparateList) {
- twoColumn = (section.members.count() >= 16);
- }
- else if (section.members.first()->type() == Node::Property) {
- twoColumn = (section.members.count() >= 5);
- }
- if (twoColumn)
- out() << "<table class=\"generic\">\n";
- if (++numTableRows % 2 == 1)
- out() << "<tr class=\"odd topAlign\">";
- else
- out() << "<tr class=\"even topAlign\">";
-
-// << "<tr><td class=\"topAlign\">";
- out() << "<ul>\n";
- int i = 0;
- NodeList::ConstIterator m = section.members.begin();
- while (m != section.members.end()) {
- if ((*m)->access() == Node::Private) {
- ++m;
- continue;
- }
-
- if (twoColumn && i == (int) (section.members.count() + 1) / 2)
- out() << "</ul></td><td class=\"topAlign\"><ul>\n";
-
- out() << "<li class=\"fn\">";
- if (style == CodeMarker::Accessors)
- out() << "<b>";
- generateSynopsis(*m, relative, marker, style);
- if (style == CodeMarker::Accessors)
- out() << "</b>";
- out() << "</li>\n";
- i++;
- ++m;
- }
- out() << "</ul>\n";
- if (twoColumn)
- out() << "</td></tr>\n</table>\n";
- }
-
- if (style == CodeMarker::Summary && !section.inherited.isEmpty()) {
- out() << "<ul>\n";
- generateSectionInheritedList(section, relative, marker);
- out() << "</ul>\n";
- }
-}
-
-void HtmlGenerator::generateSectionInheritedList(const Section& section,
- const Node *relative,
- CodeMarker *marker)
-{
- QList<QPair<ClassNode *, int> >::ConstIterator p = section.inherited.begin();
- while (p != section.inherited.end()) {
- out() << "<li class=\"fn\">";
- out() << (*p).second << " ";
- if ((*p).second == 1) {
- out() << section.singularMember;
- } else {
- out() << section.pluralMember;
- }
- out() << " inherited from <a href=\"" << fileName((*p).first)
- << "#" << HtmlGenerator::cleanRef(section.name.toLower()) << "\">"
- << protectEnc(marker->plainFullName((*p).first, relative))
- << "</a></li>\n";
- ++p;
- }
-}
-
-void HtmlGenerator::generateSynopsis(const Node *node,
- const Node *relative,
- CodeMarker *marker,
- CodeMarker::SynopsisStyle style)
-{
- QString marked = marker->markedUpSynopsis(node, relative, style);
- QRegExp templateTag("(<[^@>]*>)");
- if (marked.indexOf(templateTag) != -1) {
- QString contents = protectEnc(marked.mid(templateTag.pos(1),
- templateTag.cap(1).length()));
- marked.replace(templateTag.pos(1), templateTag.cap(1).length(),
- contents);
- }
- marked.replace(QRegExp("<@param>([a-z]+)_([1-9n])</@param>"), "<i>\\1<sub>\\2</sub></i>");
- marked.replace("<@param>", "<i>");
- marked.replace("</@param>", "</i>");
-
- if (style == CodeMarker::Summary)
- marked.replace("@name>", "b>");
-
- if (style == CodeMarker::SeparateList) {
- QRegExp extraRegExp("<@extra>.*</@extra>");
- extraRegExp.setMinimal(true);
- marked.replace(extraRegExp, "");
- } else {
- marked.replace("<@extra>", "<tt>");
- marked.replace("</@extra>", "</tt>");
- }
-
- if (style != CodeMarker::Detailed) {
- marked.replace("<@type>", "");
- marked.replace("</@type>", "");
- }
- out() << highlightedCode(marked, marker, relative);
-}
-
-QString HtmlGenerator::highlightedCode(const QString& markedCode,
- CodeMarker *marker,
- const Node *relative)
-{
- QString src = markedCode;
- QString html;
- QStringRef arg;
- QStringRef par1;
-
- const QChar charLangle = '<';
- const QChar charAt = '@';
-
- // replace all <@link> tags: "(<@link node=\"([^\"]+)\">).*(</@link>)"
- static const QString linkTag("link");
- for (int i = 0, n = src.size(); i < n;) {
- if (src.at(i) == charLangle && src.at(i + 1) == charAt) {
- i += 2;
- if (parseArg(src, linkTag, &i, n, &arg, &par1)) {
- const Node* node = CodeMarker::nodeForString(par1.toString());
- QString link = linkForNode(node, relative);
- addLink(link, arg, &html);
- }
- else {
- html += charLangle;
- html += charAt;
- }
- }
- else {
- html += src.at(i++);
- }
- }
-
- if (slow) {
- // is this block ever used at all?
- // replace all <@func> tags: "(<@func target=\"([^\"]*)\">)(.*)(</@func>)"
- src = html;
- html = QString();
- static const QString funcTag("func");
- for (int i = 0, n = src.size(); i < n;) {
- if (src.at(i) == charLangle && src.at(i + 1) == charAt) {
- i += 2;
- if (parseArg(src, funcTag, &i, n, &arg, &par1)) {
- QString link = linkForNode(
- marker->resolveTarget(par1.toString(),
- myTree,
- relative),
- relative);
- addLink(link, arg, &html);
- par1 = QStringRef();
- }
- else {
- html += charLangle;
- html += charAt;
- }
- }
- else {
- html += src.at(i++);
- }
- }
- }
-
- // replace all "(<@(type|headerfile|func)(?: +[^>]*)?>)(.*)(</@\\2>)" tags
- src = html;
- html = QString();
- static const QString typeTags[] = { "type", "headerfile", "func" };
- for (int i = 0, n = src.size(); i < n;) {
- if (src.at(i) == charLangle && src.at(i + 1) == charAt) {
- i += 2;
- bool handled = false;
- for (int k = 0; k != 3; ++k) {
- if (parseArg(src, typeTags[k], &i, n, &arg, &par1)) {
- par1 = QStringRef();
- QString link = linkForNode(
- marker->resolveTarget(arg.toString(), myTree, relative),
- relative);
- addLink(link, arg, &html);
- handled = true;
- break;
- }
- }
if (!handled) {
html += charLangle;
html += charAt;
@@ -3405,22 +3117,6 @@ QString HtmlGenerator::highlightedCode(const QString& markedCode,
"</@preprocessor>","</span>",
"</@string>", "</span>",
"</@char>", "</span>"
- // "<@char>", "<font color=blue>",
- // "</@char>", "</font>",
- // "<@func>", "<font color=green>",
- // "</@func>", "</font>",
- // "<@id>", "<i>",
- // "</@id>", "</i>",
- // "<@keyword>", "<b>",
- // "</@keyword>", "</b>",
- // "<@number>", "<font color=yellow>",
- // "</@number>", "</font>",
- // "<@op>", "<b>",
- // "</@op>", "</b>",
- // "<@param>", "<i>",
- // "</@param>", "</i>",
- // "<@string>", "<font color=green>",
- // "</@string>", "</font>",
};
for (int i = 0, n = src.size(); i < n;) {
if (src.at(i) == charLangle) {
@@ -3457,7 +3153,6 @@ QString HtmlGenerator::highlightedCode(const QString& markedCode,
return html;
}
-#endif
void HtmlGenerator::generateLink(const Atom* atom,
const Node* /* relative */,
@@ -3828,7 +3523,7 @@ void HtmlGenerator::generateDetailedMember(const Node *node,
out() << "<h3 class=\"fn\">";
out() << "<a name=\"" + refForNode(node) + "\"></a>";
generateSynopsis(node, relative, marker, CodeMarker::Detailed);
- out() << "</h3>\n";
+ out() << "</h3><div class=\"navTop\"><a href=\"#toc\"><img src=\"./images/bullet_up.png\"></a></div>\n";
}
generateStatus(node, marker);
@@ -4413,6 +4108,7 @@ void HtmlGenerator::generateQmlSummary(const Section& section,
bool twoColumn = false;
if (section.members.first()->type() == Node::QmlProperty) {
twoColumn = (count >= 5);
+ twoColumn = false;
}
if (twoColumn)
out() << "<table class=\"qmlsummary\">\n";
@@ -4462,14 +4158,13 @@ void HtmlGenerator::generateDetailedQmlMember(const Node *node,
while (p != qpgn->childNodes().end()) {
if ((*p)->type() == Node::QmlProperty) {
qpn = static_cast<const QmlPropertyNode*>(*p);
-
- if (++numTableRows % 2 == 1)
- out() << "<tr class=\"odd\">";
- else
- out() << "<tr class=\"even\">";
+ if (++numTableRows % 2 == 1)
+ out() << "<tr class=\"odd\">";
+ else
+ out() << "<tr class=\"even\">";
- out() << "<td><p>";
- //out() << "<tr><td>"; // old
+ out() << "<td><p>";
+
out() << "<a name=\"" + refForNode(qpn) + "\"></a>";
if (!qpn->isWritable())
out() << "<span class=\"qmlreadonly\">read-only</span>";
diff --git a/tools/qdoc3/htmlgenerator.h b/tools/qdoc3/htmlgenerator.h
index e060257..80341de 100644
--- a/tools/qdoc3/htmlgenerator.h
+++ b/tools/qdoc3/htmlgenerator.h
@@ -46,8 +46,6 @@
#ifndef HTMLGENERATOR_H
#define HTMLGENERATOR_H
-#define QDOC_NAME_ALIGNMENT
-
#include <qmap.h>
#include <qregexp.h>
#include <QXmlStreamWriter>
@@ -198,7 +196,7 @@ class HtmlGenerator : public PageGenerator
void generateQmlInstantiates(const QmlClassNode* qcn, CodeMarker* marker);
void generateInstantiatedBy(const ClassNode* cn, CodeMarker* marker);
#endif
-#ifdef QDOC_NAME_ALIGNMENT
+
void generateSection(const NodeList& nl,
const Node *relative,
CodeMarker *marker,
@@ -207,28 +205,15 @@ class HtmlGenerator : public PageGenerator
const Node *relative,
CodeMarker *marker,
CodeMarker::SynopsisStyle style,
- bool nameAlignment = false);
- void generateSectionInheritedList(const Section& section,
- const Node *relative,
- CodeMarker *marker,
- bool nameAlignment = false);
- QString highlightedCode(const QString& markedCode,
- CodeMarker *marker,
- const Node *relative,
- CodeMarker::SynopsisStyle style = CodeMarker::Accessors,
- bool nameAlignment = false);
-#else
- void generateSynopsis(const Node *node,
- const Node *relative,
- CodeMarker *marker,
- CodeMarker::SynopsisStyle style);
+ bool alignNames = false);
void generateSectionInheritedList(const Section& section,
const Node *relative,
CodeMarker *marker);
QString highlightedCode(const QString& markedCode,
CodeMarker *marker,
- const Node *relative);
-#endif
+ const Node *relative,
+ bool alignNames = false);
+
void generateFullName(const Node *apparentNode,
const Node *relative,
CodeMarker *marker,
@@ -341,6 +326,8 @@ class HtmlGenerator : public PageGenerator
NewClassMaps newClassMaps;
NewClassMaps newQmlClassMaps;
static int id;
+ public:
+ static bool debugging_on;
};
#define HTMLGENERATOR_ADDRESS "address"
diff --git a/tools/qdoc3/test/qdeclarative.qdocconf b/tools/qdoc3/test/qdeclarative.qdocconf
index 74fd802..facec5c 100644
--- a/tools/qdoc3/test/qdeclarative.qdocconf
+++ b/tools/qdoc3/test/qdeclarative.qdocconf
@@ -6,7 +6,7 @@ include(qt-defines.qdocconf)
project = Qml
description = Qml Reference Documentation
-url = http://qt.nokia.com/doc/4.6/
+url = http://qt.nokia.com/doc/4.7/
qmlonly = true
edition.Console.modules = QtCore QtDBus QtNetwork QtScript QtSql QtXml \
@@ -21,7 +21,7 @@ edition.DesktopLight.groups = -graphicsview-api
qhp.projects = Qml
qhp.Qml.file = qml.qhp
-qhp.Qml.namespace = com.trolltech.qml.460
+qhp.Qml.namespace = com.trolltech.qml.470
qhp.Qml.virtualFolder = qdoc
qhp.Qml.indexTitle = Qml Reference
@@ -55,9 +55,9 @@ qhp.Qml.extraFiles = images/bg_l.png \
style/style_ie8.css \
style/style.css
-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.filterAttributes = qt 4.7.0 qtrefdoc
+qhp.Qml.customFilters.Qt.name = Qt 4.7.0
+qhp.Qml.customFilters.Qt.filterAttributes = qt 4.7.0
qhp.Qml.subprojects = classes
qhp.Qml.subprojects.classes.title = Elements
qhp.Qml.subprojects.classes.indexTitle = Qml Elements
diff --git a/tools/qdoc3/test/qt-html-templates.qdocconf b/tools/qdoc3/test/qt-html-templates.qdocconf
index 629c501..4c8d7e7 100644
--- a/tools/qdoc3/test/qt-html-templates.qdocconf
+++ b/tools/qdoc3/test/qt-html-templates.qdocconf
@@ -11,7 +11,7 @@ HTML.postheader = " <div class=\"header\" id=\"qtdocheader\">\n" \
" <a href=\"index.html\" class=\"qtref\"><span>Qt Reference Documentation</span></a>\n" \
" <div id=\"narrowsearch\"><form onsubmit=\"return false;\" action=\"\" id=\"qtdocsearch\">\n" \
" <fieldset>\n" \
- " <input type=\"text\" value=\"\" id=\"pageType2\" name=\"searchstring\">\n" \
+ " <input type=\"text\" value=\"\" id=\"pageType2\" name=\"searchstring\"/>\n" \
" </fieldset>\n" \
" </form></div>\n" \
" <div id=\"nav-topright\">\n" \
@@ -49,6 +49,7 @@ HTML.postheader = " <div class=\"header\" id=\"qtdocheader\">\n" \
" <li><a href=\"declarativeui.html\">Device UI's &amp; Qt Quick</a></li> \n" \
" <li><a href=\"qt-gui-concepts.html\">Desktop UI components</a></li> \n" \
" <li><a href=\"platform-specific.html\">Platform-specific info</a></li> \n" \
+ " <li><a href=\"best-practices.html\">How-To's and Best Practices</a></li> \n" \
" </ul> \n" \
" </li> \n" \
" <li><a href=\"#\">Examples</a> \n" \
@@ -57,7 +58,6 @@ HTML.postheader = " <div class=\"header\" id=\"qtdocheader\">\n" \
" <li><a href=\"tutorials.html\">Tutorials</a></li> \n" \
" <li><a href=\"demos.html\">Demos</a></li> \n" \
" <li><a href=\"qdeclarativeexamples.html\">QML Examples</a></li> \n" \
- " <li><a href=\"qdeclarativeexamples.html#Demos\">QML Demos</a></li> \n" \
" </ul> \n" \
" </li> \n" \
" </ul> \n" \
@@ -160,7 +160,7 @@ HTML.footer = " <!-- /div -->\n" \
" <div id=\"feedbackBox\">\n" \
" <div id=\"feedcloseX\" class=\"feedclose t_button\">X</div>\n" \
" <form id=\"feedform\" action=\"http://doc.qt.nokia.com/docFeedbck/feedback.php\" method=\"get\">\n" \
- " <p>Thank you for giving your feedback. <div class=\"note\">Make sure it is related the page. For more general bugs and \n" \
+ " <p id=\"noteHead\">Thank you for giving your feedback. <div class=\"note\">Make sure it is related the page. For more general bugs and \n" \
" requests, please use the <a href=\"http://bugreports.qt.nokia.com/secure/Dashboard.jspa\">Qt Bug Tracker</a></div></p>\n" \
" <p><textarea id=\"feedbox\" name=\"feedText\" rows=\"5\" cols=\"40\"></textarea></p>\n" \
" <p><input id=\"feedsubmit\" class=\"feedclose\" type=\"submit\" name=\"feedback\" /></p>\n" \
diff --git a/tools/qdoc3/test/style/style.css b/tools/qdoc3/test/style/style.css
index 9c290f5..dff0772 100644
--- a/tools/qdoc3/test/style/style.css
+++ b/tools/qdoc3/test/style/style.css
@@ -776,12 +776,14 @@
}
.qmlreadonly
{
+ padding-left: 3px;
float: right;
color: #254117;
}
.qmldefault
{
+ padding-left: 3px;
float: right;
color: red;
}
diff --git a/tools/qdoc3/tree.cpp b/tools/qdoc3/tree.cpp
index d31de4d..70b998f 100644
--- a/tools/qdoc3/tree.cpp
+++ b/tools/qdoc3/tree.cpp
@@ -134,15 +134,16 @@ Node *Tree::findNode(const QStringList &path, Node *relative, int findFlags)
/*!
*/
-const Node *Tree::findNode(const QStringList &path,
- const Node *relative,
+const Node* Tree::findNode(const QStringList &path,
+ const Node* start,
int findFlags) const
{
- if (!relative)
- relative = root();
+ const Node* current = start;
+ if (!current)
+ current = root();
do {
- const Node *node = relative;
+ const Node *node = current;
int i;
for (i = 0; i < path.size(); ++i) {
@@ -171,9 +172,10 @@ const Node *Tree::findNode(const QStringList &path,
if (node && i == path.size()
&& (!(findFlags & NonFunction) || node->type() != Node::Function
|| ((FunctionNode *)node)->metaness() == FunctionNode::MacroWithoutParams))
- return node;
- relative = relative->parent();
- } while (relative);
+ if ((node != start) && (node->subType() != Node::QmlPropertyGroup))
+ return node;
+ current = current->parent();
+ } while (current);
return 0;
}
diff --git a/tools/qml/deviceorientation_maemo.cpp b/tools/qml/deviceorientation_maemo5.cpp
index 443edc8..e942579 100644
--- a/tools/qml/deviceorientation_maemo.cpp
+++ b/tools/qml/deviceorientation_maemo5.cpp
@@ -124,4 +124,4 @@ DeviceOrientation* DeviceOrientation::instance()
return o;
}
-#include "deviceorientation_maemo.moc"
+#include "deviceorientation_maemo5.moc"
diff --git a/tools/qml/deviceorientation_symbian.cpp b/tools/qml/deviceorientation_symbian.cpp
new file mode 100644
index 0000000..c305f94
--- /dev/null
+++ b/tools/qml/deviceorientation_symbian.cpp
@@ -0,0 +1,162 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "deviceorientation.h"
+
+#include <e32base.h>
+#include <sensrvchannelfinder.h>
+#include <sensrvdatalistener.h>
+#include <sensrvchannel.h>
+#include <sensrvorientationsensor.h>
+
+class SymbianOrientation : public DeviceOrientation, public MSensrvDataListener
+{
+ Q_OBJECT
+public:
+ SymbianOrientation()
+ : DeviceOrientation(), m_current(UnknownOrientation), m_sensorChannel(0)
+ {
+ TRAP_IGNORE(initL());
+ if (!m_sensorChannel)
+ qWarning("No valid sensors found.");
+ }
+
+ ~SymbianOrientation()
+ {
+ if (m_sensorChannel) {
+ m_sensorChannel->StopDataListening();
+ m_sensorChannel->CloseChannel();
+ delete m_sensorChannel;
+ }
+ }
+
+ void initL()
+ {
+ CSensrvChannelFinder *channelFinder = CSensrvChannelFinder::NewLC();
+ RSensrvChannelInfoList channelInfoList;
+ CleanupClosePushL(channelInfoList);
+
+ TSensrvChannelInfo searchConditions;
+ searchConditions.iChannelType = KSensrvChannelTypeIdOrientationData;
+ channelFinder->FindChannelsL(channelInfoList, searchConditions);
+
+ for (int i = 0; i < channelInfoList.Count(); ++i) {
+ TRAPD(error, m_sensorChannel = CSensrvChannel::NewL(channelInfoList[i]));
+ if (!error)
+ TRAP(error, m_sensorChannel->OpenChannelL());
+ if (!error) {
+ TRAP(error, m_sensorChannel->StartDataListeningL(this, 1, 1, 0));
+ break;
+ }
+ if (error) {
+ delete m_sensorChannel;
+ m_sensorChannel = 0;
+ }
+ }
+
+ channelInfoList.Close();
+ CleanupStack::Pop(&channelInfoList);
+ CleanupStack::PopAndDestroy(channelFinder);
+ }
+
+ Orientation orientation() const
+ {
+ return m_current;
+ }
+
+ void setOrientation(Orientation) { }
+
+private:
+ DeviceOrientation::Orientation m_current;
+ CSensrvChannel *m_sensorChannel;
+
+ void DataReceived(CSensrvChannel &channel, TInt count, TInt dataLost)
+ {
+ if (channel.GetChannelInfo().iChannelType == KSensrvChannelTypeIdOrientationData) {
+ TSensrvOrientationData data;
+ for (int i = 0; i < count; ++i) {
+ TPckgBuf<TSensrvOrientationData> dataBuf;
+ channel.GetData(dataBuf);
+ data = dataBuf();
+ Orientation o = UnknownOrientation;
+ switch (data.iDeviceOrientation) {
+ case TSensrvOrientationData::EOrientationDisplayUp:
+ o = Portrait;
+ break;
+ case TSensrvOrientationData::EOrientationDisplayRightUp:
+ o = Landscape;
+ break;
+ case TSensrvOrientationData::EOrientationDisplayLeftUp:
+ case TSensrvOrientationData::EOrientationDisplayDown:
+ case TSensrvOrientationData::EOrientationUndefined:
+ case TSensrvOrientationData::EOrientationDisplayUpwards:
+ case TSensrvOrientationData::EOrientationDisplayDownwards:
+ default:
+ break;
+ }
+
+ if (m_current != o && o != UnknownOrientation) {
+ m_current = o;
+ emit orientationChanged();
+ }
+ }
+ }
+ }
+
+ void DataError(CSensrvChannel& /* channel */, TSensrvErrorSeverity /* error */)
+ {
+ }
+
+ void GetDataListenerInterfaceL(TUid /* interfaceUid */, TAny*& /* interface */)
+ {
+ }
+};
+
+
+DeviceOrientation* DeviceOrientation::instance()
+{
+ static SymbianOrientation *o = 0;
+ if (!o)
+ o = new SymbianOrientation;
+ return o;
+}
+
+#include "deviceorientation_symbian.moc"
diff --git a/tools/qml/loggerwidget.cpp b/tools/qml/loggerwidget.cpp
index 3ae2b5e..8aa029f 100644
--- a/tools/qml/loggerwidget.cpp
+++ b/tools/qml/loggerwidget.cpp
@@ -39,29 +39,48 @@
**
****************************************************************************/
-#include "loggerwidget.h"
#include <qglobal.h>
#include <QDebug>
#include <QSettings>
#include <QActionGroup>
#include <QMenu>
+#include <QPlainTextEdit>
+#ifdef Q_WS_MAEMO_5
+# include <QScrollArea>
+# include <QVBoxLayout>
+# include "texteditautoresizer_maemo5.h"
+#endif
+
+#include "loggerwidget.h"
QT_BEGIN_NAMESPACE
LoggerWidget::LoggerWidget(QWidget *parent) :
- QPlainTextEdit(parent),
+ QMainWindow(parent),
m_visibilityOrigin(SettingsOrigin)
{
setAttribute(Qt::WA_QuitOnClose, false);
setWindowTitle(tr("Warnings"));
+ m_plainTextEdit = new QPlainTextEdit();
+
+#ifdef Q_WS_MAEMO_5
+ new TextEditAutoResizer(m_plainTextEdit);
+ setAttribute(Qt::WA_Maemo5StackedWindow);
+ QScrollArea *area = new QScrollArea();
+ area->setWidget(m_plainTextEdit);
+ area->setWidgetResizable(true);
+ setCentralWidget(area);
+#else
+ setCentralWidget(m_plainTextEdit);
+#endif
readSettings();
setupPreferencesMenu();
}
void LoggerWidget::append(const QString &msg)
{
- appendPlainText(msg);
+ m_plainTextEdit->appendPlainText(msg);
if (!isVisible() && (defaultVisibility() == AutoShowWarnings))
setVisible(true);
diff --git a/tools/qml/loggerwidget.h b/tools/qml/loggerwidget.h
index fd20c41..13c319f 100644
--- a/tools/qml/loggerwidget.h
+++ b/tools/qml/loggerwidget.h
@@ -42,12 +42,17 @@
#ifndef LOGGERWIDGET_H
#define LOGGERWIDGET_H
-#include <QPlainTextEdit>
+#include <QMainWindow>
+#include <QMetaType>
QT_BEGIN_NAMESPACE
-class LoggerWidget : public QPlainTextEdit {
-Q_OBJECT
+class QPlainTextEdit;
+class QMenu;
+class QAction;
+
+class LoggerWidget : public QMainWindow {
+ Q_OBJECT
public:
LoggerWidget(QWidget *parent=0);
@@ -80,6 +85,7 @@ private:
QMenu *m_preferencesMenu;
QAction *m_showWidgetAction;
+ QPlainTextEdit *m_plainTextEdit;
enum ConfigOrigin { CommandLineOrigin, SettingsOrigin };
ConfigOrigin m_visibilityOrigin;
diff --git a/tools/qml/main.cpp b/tools/qml/main.cpp
index a75023b..4b1162e 100644
--- a/tools/qml/main.cpp
+++ b/tools/qml/main.cpp
@@ -49,12 +49,15 @@
#include <QTranslator>
#include <QDebug>
#include <QMessageBox>
+#include <QAtomicInt>
#include "qdeclarativetester.h"
QT_USE_NAMESPACE
QtMsgHandler systemMsgOutput = 0;
+
+
#if defined (Q_OS_SYMBIAN)
#include <unistd.h>
#include <sys/types.h>
@@ -89,19 +92,25 @@ void showWarnings()
}
}
+static QAtomicInt recursiveLock(0);
+
void myMessageOutput(QtMsgType type, const char *msg)
{
- if (!logger.isNull()) {
- QString strMsg = QString::fromAscii(msg);
- QMetaObject::invokeMethod(logger.data(), "append", Q_ARG(QString, strMsg));
+ QString strMsg = QString::fromLatin1(msg);
+
+ if (!logger.isNull() && !QCoreApplication::closingDown()) {
+ if (recursiveLock.testAndSetOrdered(0, 1)) {
+ QMetaObject::invokeMethod(logger.data(), "append", Q_ARG(QString, strMsg));
+ recursiveLock = 0;
+ }
} else {
- warnings += msg;
+ warnings += strMsg;
warnings += QLatin1Char('\n');
}
if (systemMsgOutput) { // Windows
systemMsgOutput(type, msg);
} else { // Unix
- fprintf(stderr, "%s\n",msg);
+ fprintf(stderr, "%s\n", msg);
fflush(stderr);
}
}
@@ -119,7 +128,7 @@ void usage()
qWarning(" -fullscreen............................... run fullscreen");
qWarning(" -stayontop................................ keep viewer window on top");
qWarning(" -sizeviewtorootobject .................... the view resizes to the changes in the content");
- qWarning(" -sizerootobjecttoview .................... the content resizes to the changes in the view");
+ qWarning(" -sizerootobjecttoview .................... the content resizes to the changes in the view (default)");
qWarning(" -qmlbrowser .............................. use a QML-based file browser");
qWarning(" -warnings [show|hide]..................... show warnings in a separate log window");
qWarning(" -recordfile <output> ..................... set video recording file");
@@ -200,6 +209,8 @@ int main(int argc, char ** argv)
app.setOrganizationName("Nokia");
app.setOrganizationDomain("nokia.com");
+
+
QDeclarativeViewer::registerTypes();
QDeclarativeTester::registerTypes();
diff --git a/tools/qml/proxysettings.cpp b/tools/qml/proxysettings.cpp
index 3255e42..ffaa4c0 100644
--- a/tools/qml/proxysettings.cpp
+++ b/tools/qml/proxysettings.cpp
@@ -48,11 +48,14 @@
QT_BEGIN_NAMESPACE
ProxySettings::ProxySettings (QWidget * parent)
- : QDialog (parent), Ui::ProxySettings()
+ : QDialog (parent), Ui::ProxySettings()
{
setupUi (this);
+#if !defined Q_WS_MAEMO_5
+ // the onscreen keyboard can't cope with masks
proxyServerEdit->setInputMask ("000.000.000.000;_");
+#endif
QIntValidator *validator = new QIntValidator (0, 9999, this);
proxyPortEdit->setValidator (validator);
diff --git a/tools/qml/proxysettings.h b/tools/qml/proxysettings.h
index 325929a..5d4d137 100644
--- a/tools/qml/proxysettings.h
+++ b/tools/qml/proxysettings.h
@@ -44,7 +44,11 @@
#include <QDialog>
#include <QNetworkProxy>
+#ifdef Q_WS_MAEMO_5
+#include "ui_proxysettings_maemo5.h"
+#else
#include "ui_proxysettings.h"
+#endif
QT_BEGIN_NAMESPACE
/**
diff --git a/tools/qml/proxysettings_maemo5.ui b/tools/qml/proxysettings_maemo5.ui
new file mode 100644
index 0000000..83f0c2a
--- /dev/null
+++ b/tools/qml/proxysettings_maemo5.ui
@@ -0,0 +1,177 @@
+<?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>449</width>
+ <height>164</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>HTTP Proxy</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_2">
+ <property name="leftMargin">
+ <number>16</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>16</number>
+ </property>
+ <property name="bottomMargin">
+ <number>8</number>
+ </property>
+ <property name="horizontalSpacing">
+ <number>16</number>
+ </property>
+ <property name="verticalSpacing">
+ <number>0</number>
+ </property>
+ <item row="0" column="0">
+ <widget class="QCheckBox" name="proxyCheckBox">
+ <property name="text">
+ <string>Use HTTP Proxy</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1" rowspan="2">
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="1" column="0" rowspan="2">
+ <widget class="QWidget" name="widget" native="true">
+ <layout class="QGridLayout" name="gridLayout">
+ <property name="horizontalSpacing">
+ <number>16</number>
+ </property>
+ <property name="verticalSpacing">
+ <number>0</number>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <item row="0" column="0">
+ <widget class="QLabel" name="serverAddressLabel">
+ <property name="text">
+ <string>Server</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLineEdit" name="proxyServerEdit">
+ <property name="placeholderText">
+ <string>Name or IP</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Port</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLineEdit" name="proxyPortEdit">
+ <property name="text">
+ <string>8080</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="usernameLabel">
+ <property name="text">
+ <string>Username</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QLineEdit" name="usernameEdit"/>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="passwordLabel">
+ <property name="text">
+ <string>Password</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QLineEdit" name="passwordEdit">
+ <property name="echoMode">
+ <enum>QLineEdit::Password</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <tabstops>
+ <tabstop>proxyCheckBox</tabstop>
+ <tabstop>proxyServerEdit</tabstop>
+ <tabstop>proxyPortEdit</tabstop>
+ <tabstop>usernameEdit</tabstop>
+ <tabstop>passwordEdit</tabstop>
+ <tabstop>buttonBox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>ProxySettings</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>318</x>
+ <y>100</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>157</x>
+ <y>116</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>ProxySettings</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>318</x>
+ <y>100</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>286</x>
+ <y>116</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/tools/qml/qdeclarativetester.h b/tools/qml/qdeclarativetester.h
index d49c9b8..021869d 100644
--- a/tools/qml/qdeclarativetester.h
+++ b/tools/qml/qdeclarativetester.h
@@ -48,6 +48,9 @@
#include <QImage>
#include <QUrl>
#include <qmlruntime.h>
+#include <qdeclarativelist.h>
+#include <qdeclarative.h>
+#include <QAbstractAnimation>
QT_BEGIN_NAMESPACE
diff --git a/tools/qml/qml.pri b/tools/qml/qml.pri
index 58d8cc1..fcd0c33 100644
--- a/tools/qml/qml.pri
+++ b/tools/qml/qml.pri
@@ -19,10 +19,16 @@ SOURCES += $$PWD/qmlruntime.cpp \
RESOURCES = $$PWD/qmlruntime.qrc
maemo5 {
QT += dbus
- SOURCES += $$PWD/deviceorientation_maemo.cpp
+ HEADERS += $$PWD/texteditautoresizer_maemo5.h
+ SOURCES += $$PWD/deviceorientation_maemo5.cpp
+ FORMS = $$PWD/recopts_maemo5.ui \
+ $$PWD/proxysettings_maemo5.ui
+} symbian:!contains(S60_VERSION, 3.1):!contains(S60_VERSION, 3.2) {
+ SOURCES += $$PWD/deviceorientation_symbian.cpp
+ FORMS = $$PWD/recopts.ui \
+ $$PWD/proxysettings.ui
} else {
SOURCES += $$PWD/deviceorientation.cpp
+ FORMS = $$PWD/recopts.ui \
+ $$PWD/proxysettings.ui
}
-
-FORMS = $$PWD/recopts.ui \
- $$PWD/proxysettings.ui
diff --git a/tools/qml/qml.pro b/tools/qml/qml.pro
index 9cdec77..bb69e8a 100644
--- a/tools/qml/qml.pro
+++ b/tools/qml/qml.pro
@@ -29,11 +29,17 @@ wince* {
QT += webkit
}
}
+maemo5 {
+ QT += maemo5
+}
symbian {
TARGET.UID3 = 0x20021317
include($$QT_SOURCE_TREE/examples/symbianpkgrules.pri)
TARGET.EPOCHEAPSIZE = 0x20000 0x2000000
TARGET.CAPABILITY = NetworkServices ReadUserData
+ !contains(S60_VERSION, 3.1):!contains(S60_VERSION, 3.2) {
+ LIBS += -lsensrvclient -lsensrvutil
+ }
}
mac {
QMAKE_INFO_PLIST=Info_mac.plist
diff --git a/tools/qml/qmlruntime.cpp b/tools/qml/qmlruntime.cpp
index 676881d..951b187 100644
--- a/tools/qml/qmlruntime.cpp
+++ b/tools/qml/qmlruntime.cpp
@@ -44,15 +44,23 @@
#ifdef hz
#undef hz
#endif
-#include "ui_recopts.h"
+#ifdef Q_WS_MAEMO_5
+# include <QMaemo5ValueButton>
+# include <QMaemo5ListPickSelector>
+# include <QWidgetAction>
+# include <QStringListModel>
+# include "ui_recopts_maemo5.h"
+#else
+# include "ui_recopts.h"
+#endif
#include "qmlruntime.h"
#include <qdeclarativecontext.h>
#include <qdeclarativeengine.h>
#include <qdeclarativenetworkaccessmanagerfactory.h>
#include "qdeclarative.h"
-#include <private/qabstractanimation_p.h>
#include <QAbstractAnimation>
+#include <private/qabstractanimation_p.h>
#include <QSettings>
#include <QXmlStreamReader>
@@ -136,24 +144,59 @@ private:
};
-class SizedMenuBar : public QMenuBar
-{
+
+#if defined(Q_WS_MAEMO_5)
+
+class Maemo5PickerAction : public QWidgetAction {
Q_OBJECT
public:
- SizedMenuBar(QWidget *parent, QWidget *referenceWidget)
- : QMenuBar(parent), refWidget(referenceWidget)
+ Maemo5PickerAction(const QString &text, QActionGroup *actions, QObject *parent)
+ : QWidgetAction(parent), m_text(text), m_actions(actions)
+ { }
+
+ QWidget *createWidget(QWidget *parent)
{
+ QMaemo5ValueButton *button = new QMaemo5ValueButton(m_text, parent);
+ button->setValueLayout(QMaemo5ValueButton::ValueUnderTextCentered);
+ QMaemo5ListPickSelector *pick = new QMaemo5ListPickSelector(button);
+ button->setPickSelector(pick);
+ if (m_actions) {
+ QStringList sl;
+ int curIdx = -1, idx = 0;
+ foreach (QAction *a, m_actions->actions()) {
+ sl << a->text();
+ if (a->isChecked())
+ curIdx = idx;
+ idx++;
+ }
+ pick->setModel(new QStringListModel(sl));
+ pick->setCurrentIndex(curIdx);
+ } else {
+ button->setEnabled(false);
+ }
+ connect(pick, SIGNAL(selected(QString)), this, SLOT(emitTriggered()));
+ return button;
}
- virtual QSize sizeHint() const
+private slots:
+ void emitTriggered()
{
- return QSize(refWidget->sizeHint().width(), QMenuBar::sizeHint().height());
+ QMaemo5ListPickSelector *pick = qobject_cast<QMaemo5ListPickSelector *>(sender());
+ if (!pick)
+ return;
+ int idx = pick->currentIndex();
+
+ if (m_actions && idx >= 0 && idx < m_actions->actions().count())
+ m_actions->actions().at(idx)->trigger();
}
private:
- QWidget *refWidget;
+ QString m_text;
+ QPointer<QActionGroup> m_actions;
};
+#endif // Q_WS_MAEMO_5
+
static struct { const char *name, *args; } ffmpegprofiles[] = {
{"Maximum Quality", "-sameq"},
{"High Quality", "-qmax 2"},
@@ -170,7 +213,9 @@ public:
RecordingDialog(QWidget *parent) : QDialog(parent)
{
setupUi(this);
+#ifndef Q_WS_MAEMO_5
hz->setValidator(new QDoubleValidator(hz));
+#endif
for (int i=0; ffmpegprofiles[i].name; ++i) {
profile->addItem(ffmpegprofiles[i].name);
}
@@ -197,6 +242,132 @@ public:
return ffmpegprofiles[i].args[0] ? QLatin1String(ffmpegprofiles[i].args) : customargs;
}
+ void setOriginalSize(const QSize &s)
+ {
+ QString str = tr("Original (%1x%2)").arg(s.width()).arg(s.height());
+
+#ifdef Q_WS_MAEMO_5
+ sizeCombo->setItemText(0, str);
+#else
+ sizeOriginal->setText(str);
+ if (sizeWidth->value()<=1) {
+ sizeWidth->setValue(s.width());
+ sizeHeight->setValue(s.height());
+ }
+#endif
+ }
+
+ void showffmpegOptions(bool b)
+ {
+#ifdef Q_WS_MAEMO_5
+ profileLabel->setVisible(b);
+ profile->setVisible(b);
+ ffmpegHelp->setVisible(b);
+ args->setVisible(b);
+#else
+ ffmpegOptions->setVisible(b);
+#endif
+ }
+
+ void showRateOptions(bool b)
+ {
+#ifdef Q_WS_MAEMO_5
+ rateLabel->setVisible(b);
+ rateCombo->setVisible(b);
+#else
+ rateOptions->setVisible(b);
+#endif
+ }
+
+ void setVideoRate(int rate)
+ {
+#ifdef Q_WS_MAEMO_5
+ int idx;
+ if (rate >= 60)
+ idx = 0;
+ else if (rate >= 50)
+ idx = 2;
+ else if (rate >= 25)
+ idx = 3;
+ else if (rate >= 24)
+ idx = 4;
+ else if (rate >= 20)
+ idx = 5;
+ else if (rate >= 15)
+ idx = 6;
+ else
+ idx = 7;
+ rateCombo->setCurrentIndex(idx);
+#else
+ if (rate == 24)
+ hz24->setChecked(true);
+ else if (rate == 25)
+ hz25->setChecked(true);
+ else if (rate == 50)
+ hz50->setChecked(true);
+ else if (rate == 60)
+ hz60->setChecked(true);
+ else {
+ hzCustom->setChecked(true);
+ hz->setText(QString::number(rate));
+ }
+#endif
+ }
+
+ int videoRate() const
+ {
+#ifdef Q_WS_MAEMO_5
+ switch (rateCombo->currentIndex()) {
+ case 0: return 60;
+ case 1: return 50;
+ case 2: return 25;
+ case 3: return 24;
+ case 4: return 20;
+ case 5: return 15;
+ case 7: return 10;
+ default: return 60;
+ }
+#else
+ if (hz24->isChecked())
+ return 24;
+ else if (hz25->isChecked())
+ return 25;
+ else if (hz50->isChecked())
+ return 50;
+ else if (hz60->isChecked())
+ return 60;
+ else {
+ return hz->text().toInt();
+ }
+#endif
+ }
+
+ QSize videoSize() const
+ {
+#ifdef Q_WS_MAEMO_5
+ switch (sizeCombo->currentIndex()) {
+ case 0: return QSize();
+ case 1: return QSize(640,480);
+ case 2: return QSize(320,240);
+ case 3: return QSize(1280,720);
+ default: return QSize();
+ }
+#else
+ if (sizeOriginal->isChecked())
+ return QSize();
+ else if (size720p->isChecked())
+ return QSize(1280,720);
+ else if (sizeVGA->isChecked())
+ return QSize(640,480);
+ else if (sizeQVGA->isChecked())
+ return QSize(320,240);
+ else
+ return QSize(sizeWidth->value(), sizeHeight->value());
+#endif
+ }
+
+
+
private slots:
void pickProfile(int i)
{
@@ -260,65 +431,82 @@ private:
mutable QMutex mutex;
};
-class NetworkAccessManagerFactory : public QDeclarativeNetworkAccessManagerFactory
+class SystemProxyFactory : public QNetworkProxyFactory
{
public:
- NetworkAccessManagerFactory() : cacheSize(0) {}
- ~NetworkAccessManagerFactory() {}
-
- QNetworkAccessManager *create(QObject *parent);
+ SystemProxyFactory() : proxyDirty(true), httpProxyInUse(false) {
+ }
- void setupProxy(QNetworkAccessManager *nam)
+ virtual QList<QNetworkProxy> queryProxy(const QNetworkProxyQuery &query)
{
- 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;
- }
+ if (proxyDirty)
+ setupProxy();
+ QString protocolTag = query.protocolTag();
+ if (httpProxyInUse && (protocolTag == "http" || protocolTag == "https")) {
+ QList<QNetworkProxy> ret;
+ ret << httpProxy;
+ return ret;
+ }
#ifdef Q_OS_WIN
- // systemProxyForQuery can take insanely long on Windows (QTBUG-10106)
- return QNetworkProxyFactory::proxyForQuery(query);
+ // systemProxyForQuery can take insanely long on Windows (QTBUG-10106)
+ return QNetworkProxyFactory::proxyForQuery(query);
#else
- return QNetworkProxyFactory::systemProxyForQuery(query);
+ return QNetworkProxyFactory::systemProxyForQuery(query);
#endif
- }
- void setHttpProxy (QNetworkProxy proxy)
- {
- httpProxy = proxy;
- httpProxyInUse = true;
- }
- void unsetHttpProxy ()
- {
- httpProxyInUse = false;
- }
- private:
- bool httpProxyInUse;
- QNetworkProxy httpProxy;
- };
-
- SystemProxyFactory *proxyFactory = new SystemProxyFactory;
- if (ProxySettings::httpProxyInUse())
- proxyFactory->setHttpProxy(ProxySettings::httpProxy());
- else
- proxyFactory->unsetHttpProxy();
- nam->setProxyFactory(proxyFactory);
}
+ void setupProxy() {
+ // Don't bother locking because we know that the proxy only
+ // changes in response to the settings dialog and that
+ // the view will be reloaded.
+ proxyDirty = false;
+ httpProxyInUse = ProxySettings::httpProxyInUse();
+ if (httpProxyInUse)
+ httpProxy = ProxySettings::httpProxy();
+ }
+
+ void proxyChanged() {
+ proxyDirty = true;
+ }
+
+private:
+ volatile bool proxyDirty;
+ bool httpProxyInUse;
+ QNetworkProxy httpProxy;
+};
+
+class NetworkAccessManagerFactory : public QObject, public QDeclarativeNetworkAccessManagerFactory
+{
+ Q_OBJECT
+public:
+ NetworkAccessManagerFactory() : cacheSize(0) {}
+ ~NetworkAccessManagerFactory() {}
+
+ QNetworkAccessManager *create(QObject *parent);
+
void setCacheSize(int size) {
if (size != cacheSize) {
cacheSize = size;
}
}
+ void proxyChanged() {
+ foreach (QNetworkAccessManager *nam, namList) {
+ static_cast<SystemProxyFactory*>(nam->proxyFactory())->proxyChanged();
+ }
+ }
+
static PersistentCookieJar *cookieJar;
+
+private slots:
+ void managerDestroyed(QObject *obj) {
+ namList.removeOne(static_cast<QNetworkAccessManager*>(obj));
+ }
+
+private:
QMutex mutex;
int cacheSize;
+ QList<QNetworkAccessManager*> namList;
};
PersistentCookieJar *NetworkAccessManagerFactory::cookieJar = 0;
@@ -339,7 +527,7 @@ QNetworkAccessManager *NetworkAccessManagerFactory::create(QObject *parent)
}
manager->setCookieJar(cookieJar);
cookieJar->setParent(0);
- setupProxy(manager);
+ manager->setProxyFactory(new SystemProxyFactory);
if (cacheSize > 0) {
QNetworkDiskCache *cache = new QNetworkDiskCache;
cache->setCacheDirectory(QDir::tempPath()+QLatin1String("/qml-viewer-network-cache"));
@@ -348,6 +536,8 @@ QNetworkAccessManager *NetworkAccessManagerFactory::create(QObject *parent)
} else {
manager->setCache(0);
}
+ connect(manager, SIGNAL(destroyed(QObject*)), this, SLOT(managerDestroyed(QObject*)));
+ namList.append(manager);
qDebug() << "created new network access manager for" << parent;
return manager;
}
@@ -363,15 +553,10 @@ QString QDeclarativeViewer::getVideoFileName()
return QFileDialog::getSaveFileName(this, title, "", types.join(";; "));
}
-
QDeclarativeViewer::QDeclarativeViewer(QWidget *parent, Qt::WindowFlags flags)
-#if defined(Q_OS_SYMBIAN)
: QMainWindow(parent, flags)
-#else
- : QWidget(parent, flags)
-#endif
- , loggerWindow(new LoggerWidget())
- , frame_stream(0), mb(0)
+ , loggerWindow(new LoggerWidget(this))
+ , frame_stream(0)
, orientation(0)
, showWarningsWindow(0)
, m_scriptOptions(0)
@@ -381,6 +566,10 @@ QDeclarativeViewer::QDeclarativeViewer(QWidget *parent, Qt::WindowFlags flags)
{
QDeclarativeViewer::registerTypes();
setWindowTitle(tr("Qt QML Viewer"));
+#ifdef Q_WS_MAEMO_5
+ setAttribute(Qt::WA_Maemo5StackedWindow);
+// setPalette(QApplication::palette("QLabel"));
+#endif
devicemode = false;
canvas = 0;
@@ -393,9 +582,9 @@ QDeclarativeViewer::QDeclarativeViewer(QWidget *parent, Qt::WindowFlags flags)
senseFfmpeg();
senseImageMagick();
if (!ffmpegAvailable)
- recdlg->ffmpegOptions->hide();
+ recdlg->showffmpegOptions(false);
if (!ffmpegAvailable && !convertAvailable)
- recdlg->rateOptions->hide();
+ recdlg->showRateOptions(false);
QString warn;
if (!ffmpegAvailable) {
if (!convertAvailable)
@@ -422,33 +611,27 @@ QDeclarativeViewer::QDeclarativeViewer(QWidget *parent, Qt::WindowFlags flags)
QObject::connect(warningsWidget(), SIGNAL(closed()), this, SLOT(warningsWidgetClosed()));
if (!(flags & Qt::FramelessWindowHint)) {
- createMenu(menuBar(),0);
+ createMenu();
changeOrientation(orientation->actions().value(0));
+ } else {
+ setMenuBar(0);
}
-#if !defined(Q_OS_SYMBIAN)
- QVBoxLayout *layout = new QVBoxLayout;
- layout->setMargin(0);
- layout->setSpacing(0);
- setLayout(layout);
- if (mb)
- layout->addWidget(mb);
- layout->addWidget(canvas);
-#else
setCentralWidget(canvas);
-#endif
+
namFactory = new NetworkAccessManagerFactory;
canvas->engine()->setNetworkAccessManagerFactory(namFactory);
- connect(&autoStartTimer, SIGNAL(triggered()), this, SLOT(autoStartRecording()));
- connect(&autoStopTimer, SIGNAL(triggered()), this, SLOT(autoStopRecording()));
- connect(&recordTimer, SIGNAL(triggered()), this, SLOT(recordFrame()));
+ connect(&autoStartTimer, SIGNAL(timeout()), this, SLOT(autoStartRecording()));
+ connect(&autoStopTimer, SIGNAL(timeout()), this, SLOT(autoStopRecording()));
+ connect(&recordTimer, SIGNAL(timeout()), this, SLOT(recordFrame()));
connect(DeviceOrientation::instance(), SIGNAL(orientationChanged()),
this, SLOT(orientationChanged()), Qt::QueuedConnection);
- autoStartTimer.setRunning(false);
- autoStopTimer.setRunning(false);
- recordTimer.setRunning(false);
- recordTimer.setRepeating(true);
+ autoStartTimer.setSingleShot(true);
+ autoStopTimer.setSingleShot(true);
+ recordTimer.setSingleShot(false);
+
+ QObject::connect(qApp, SIGNAL(aboutToQuit()), this, SLOT(appAboutToQuit()));
}
QDeclarativeViewer::~QDeclarativeViewer()
@@ -468,26 +651,6 @@ void QDeclarativeViewer::enableExperimentalGestures()
canvas->viewport()->setAttribute(Qt::WA_AcceptTouchEvents);
}
-int QDeclarativeViewer::menuBarHeight() const
-{
- if (!(windowFlags() & Qt::FramelessWindowHint))
- return menuBar()->height();
- else
- return 0; // don't create menu
-}
-
-QMenuBar *QDeclarativeViewer::menuBar() const
-{
-#if !defined(Q_OS_SYMBIAN)
- if (!mb)
- mb = new SizedMenuBar((QWidget*)this, canvas);
-#else
- mb = QMainWindow::menuBar();
-#endif
-
- return mb;
-}
-
QDeclarativeView *QDeclarativeViewer::view() const
{
return canvas;
@@ -498,120 +661,128 @@ LoggerWidget *QDeclarativeViewer::warningsWidget() const
return loggerWindow;
}
-void QDeclarativeViewer::createMenu(QMenuBar *menu, QMenu *flatmenu)
+void QDeclarativeViewer::createMenu()
{
- QObject *parent = flatmenu ? (QObject*)flatmenu : (QObject*)menu;
-
- QMenu *fileMenu = flatmenu ? flatmenu : menu->addMenu(tr("&File"));
-
- QAction *openAction = new QAction(tr("&Open..."), parent);
+ QAction *openAction = new QAction(tr("&Open..."), this);
openAction->setShortcut(QKeySequence("Ctrl+O"));
connect(openAction, SIGNAL(triggered()), this, SLOT(openFile()));
- fileMenu->addAction(openAction);
- QAction *reloadAction = new QAction(tr("&Reload"), parent);
+ QAction *reloadAction = new QAction(tr("&Reload"), this);
reloadAction->setShortcut(QKeySequence("Ctrl+R"));
connect(reloadAction, SIGNAL(triggered()), this, SLOT(reload()));
- fileMenu->addAction(reloadAction);
-
-#if !defined(Q_OS_SYMBIAN)
- if (flatmenu) flatmenu->addSeparator();
-
- QMenu *recordMenu = flatmenu ? flatmenu : menu->addMenu(tr("&Recording"));
- QAction *snapshotAction = new QAction(tr("&Take Snapshot\tF3"), parent);
+ QAction *snapshotAction = new QAction(tr("&Take Snapshot"), this);
+ snapshotAction->setShortcut(QKeySequence("F3"));
connect(snapshotAction, SIGNAL(triggered()), this, SLOT(takeSnapShot()));
- recordMenu->addAction(snapshotAction);
- recordAction = new QAction(tr("Start Recording &Video\tF9"), parent);
+ recordAction = new QAction(tr("Start Recording &Video"), this);
+ recordAction->setShortcut(QKeySequence("F9"));
connect(recordAction, SIGNAL(triggered()), this, SLOT(toggleRecordingWithSelection()));
- recordMenu->addAction(recordAction);
- QAction *recordOptions = new QAction(tr("Video &Options..."), parent);
+ QAction *recordOptions = new QAction(tr("Video &Options..."), this);
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);
+ QAction *slowAction = new QAction(tr("&Slow Down Animations"), this);
slowAction->setShortcut(QKeySequence("Ctrl+."));
slowAction->setCheckable(true);
connect(slowAction, SIGNAL(triggered(bool)), this, SLOT(setSlowMode(bool)));
- debugMenu->addAction(slowAction);
- showWarningsWindow = new QAction(tr("Show Warnings"), parent);
+ showWarningsWindow = new QAction(tr("Show Warnings"), this);
showWarningsWindow->setCheckable((true));
showWarningsWindow->setChecked(loggerWindow->isVisible());
connect(showWarningsWindow, SIGNAL(triggered(bool)), this, SLOT(showWarnings(bool)));
-#if !defined(Q_OS_SYMBIAN)
- debugMenu->addAction(showWarningsWindow);
-#endif
-
- if (flatmenu) flatmenu->addSeparator();
-
-#endif // Q_OS_SYMBIAN
-
- QMenu *settingsMenu = flatmenu ? flatmenu : menu->addMenu(tr("S&ettings"));
- QAction *proxyAction = new QAction(tr("Http &proxy..."), parent);
+ QAction *proxyAction = new QAction(tr("HTTP &Proxy..."), this);
connect(proxyAction, SIGNAL(triggered()), this, SLOT(showProxySettings()));
- settingsMenu->addAction(proxyAction);
-#if !defined(Q_OS_SYMBIAN)
- if (!flatmenu)
- settingsMenu->addAction(recordOptions);
- settingsMenu->addMenu(loggerWindow->preferencesMenu());
-#else
- QAction *fullscreenAction = new QAction(tr("Full Screen"), parent);
+ QAction *fullscreenAction = new QAction(tr("Full Screen"), this);
fullscreenAction->setCheckable(true);
connect(fullscreenAction, SIGNAL(triggered()), this, SLOT(toggleFullScreen()));
- settingsMenu->addAction(fullscreenAction);
-#endif
-
- if (flatmenu) flatmenu->addSeparator();
-
- QMenu *propertiesMenu = settingsMenu->addMenu(tr("Properties"));
-
- orientation = new QActionGroup(parent);
- QAction *rotateOrientation = new QAction(tr("Rotate orientation"), parent);
+ QAction *rotateOrientation = new QAction(tr("Rotate orientation"), this);
rotateOrientation->setShortcut(QKeySequence("Ctrl+T"));
- settingsMenu->addAction(rotateOrientation);
connect(rotateOrientation, SIGNAL(triggered()), this, SLOT(rotateOrientation()));
+ orientation = new QActionGroup(this);
orientation->setExclusive(true);
connect(orientation, SIGNAL(triggered(QAction*)), this, SLOT(changeOrientation(QAction*)));
- orientation->addAction(tr("orientation: Portrait"));
- orientation->addAction(tr("orientation: Landscape"));
- orientation->addAction(tr("orientation: Portrait (Inverted)"));
- orientation->addAction(tr("orientation: Landscape (Inverted)"));
- QList<QAction *> actions = orientation->actions();
- for (int i=0; i<actions.count(); i++) {
- propertiesMenu->addAction(actions[i]);
- actions[i]->setCheckable(true);
- }
-
- if (flatmenu) flatmenu->addSeparator();
+ QAction *portraitAction = new QAction(tr("Portrait"), this);
+ portraitAction->setCheckable(true);
+ QAction *landscapeAction = new QAction(tr("Landscape"), this);
+ landscapeAction->setCheckable(true);
+ QAction *portraitInvAction = new QAction(tr("Portrait (inverted)"), this);
+ portraitInvAction->setCheckable(true);
+ QAction *landscapeInvAction = new QAction(tr("Landscape (inverted)"), this);
+ landscapeInvAction->setCheckable(true);
- QMenu *helpMenu = flatmenu ? flatmenu : menu->addMenu(tr("&Help"));
- QAction *aboutAction = new QAction(tr("&About Qt..."), parent);
+ QAction *aboutAction = new QAction(tr("&About Qt..."), this);
connect(aboutAction, SIGNAL(triggered()), qApp, SLOT(aboutQt()));
- helpMenu->addAction(aboutAction);
- QAction *quitAction = new QAction(tr("&Quit"), parent);
+ QAction *quitAction = new QAction(tr("&Quit"), this);
quitAction->setShortcut(QKeySequence("Ctrl+Q"));
connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit()));
+
+ QMenuBar *menu = menuBar();
+ if (!menu)
+ return;
+
+#if defined(Q_WS_MAEMO_5)
+ menu->addAction(openAction);
+ menu->addAction(reloadAction);
+
+ menu->addAction(snapshotAction);
+ menu->addAction(recordAction);
+
+ menu->addAction(recordOptions);
+ menu->addAction(proxyAction);
+
+ menu->addAction(slowAction);
+ menu->addAction(showWarningsWindow);
+
+ orientation->addAction(landscapeAction);
+ orientation->addAction(portraitAction);
+ menu->addAction(new Maemo5PickerAction(tr("Set orientation"), orientation, this));
+ menu->addAction(fullscreenAction);
+ return;
+#endif // Q_WS_MAEMO_5
+
+ QMenu *fileMenu = menu->addMenu(tr("&File"));
+ fileMenu->addAction(openAction);
+ fileMenu->addAction(reloadAction);
fileMenu->addSeparator();
fileMenu->addAction(quitAction);
- if (menu) {
- menu->setFixedHeight(menu->sizeHint().height());
- menu->setMinimumWidth(10);
- }
+
+#if !defined(Q_OS_SYMBIAN)
+ QMenu *recordMenu = menu->addMenu(tr("&Recording"));
+ recordMenu->addAction(snapshotAction);
+ recordMenu->addAction(recordAction);
+
+ QMenu *debugMenu = menu->addMenu(tr("&Debugging"));
+ debugMenu->addAction(slowAction);
+ debugMenu->addAction(showWarningsWindow);
+#endif // ! Q_OS_SYMBIAN
+
+ QMenu *settingsMenu = menu->addMenu(tr("S&ettings"));
+ settingsMenu->addAction(proxyAction);
+#if !defined(Q_OS_SYMBIAN)
+ settingsMenu->addAction(recordOptions);
+ settingsMenu->addMenu(loggerWindow->preferencesMenu());
+#else // ! Q_OS_SYMBIAN
+ settingsMenu->addAction(fullscreenAction);
+#endif // Q_OS_SYMBIAN
+ settingsMenu->addAction(rotateOrientation);
+
+ QMenu *propertiesMenu = settingsMenu->addMenu(tr("Properties"));
+
+ orientation->addAction(portraitAction);
+ orientation->addAction(landscapeAction);
+ orientation->addAction(portraitInvAction);
+ orientation->addAction(landscapeInvAction);
+ propertiesMenu->addActions(orientation->actions());
+
+ QMenu *helpMenu = menu->addMenu(tr("&Help"));
+ helpMenu->addAction(aboutAction);
}
void QDeclarativeViewer::showProxySettings()
@@ -625,6 +796,7 @@ void QDeclarativeViewer::showProxySettings()
void QDeclarativeViewer::proxySettingsChanged()
{
+ namFactory->proxyChanged();
reload ();
}
@@ -685,25 +857,11 @@ void QDeclarativeViewer::chooseRecordingOptions()
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());
- }
+ recdlg->setOriginalSize(canvas->size());
// 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));
- }
+ recdlg->setVideoRate(record_rate);
+
// Profile
recdlg->setArguments(record_args.join(" "));
@@ -711,28 +869,9 @@ void QDeclarativeViewer::chooseRecordingOptions()
// 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());
+ record_outsize = recdlg->videoSize();
// 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();
- }
+ record_rate = recdlg->videoRate();
// Profile
record_args = recdlg->arguments().split(" ",QString::SkipEmptyParts);
}
@@ -740,7 +879,7 @@ void QDeclarativeViewer::chooseRecordingOptions()
void QDeclarativeViewer::toggleRecordingWithSelection()
{
- if (!recordTimer.isRunning()) {
+ if (!recordTimer.isActive()) {
if (record_file.isEmpty()) {
QString fileName = getVideoFileName();
if (fileName.isEmpty())
@@ -759,7 +898,7 @@ void QDeclarativeViewer::toggleRecording()
toggleRecordingWithSelection();
return;
}
- bool recording = !recordTimer.isRunning();
+ bool recording = !recordTimer.isActive();
recordAction->setText(recording ? tr("&Stop Recording Video\tF9") : tr("&Start Recording Video\tF9"));
setRecording(recording);
}
@@ -805,12 +944,7 @@ void QDeclarativeViewer::statusChanged()
if (canvas->status() == QDeclarativeView::Ready) {
initialSize = canvas->initialSize();
- if (canvas->resizeMode() == QDeclarativeView::SizeRootObjectToView) {
- if (!isFullScreen() && !isMaximized()) {
- resize(QSize(initialSize.width(), initialSize.height()+menuBarHeight()));
- updateSizeHints();
- }
- }
+ updateSizeHints(true);
}
}
@@ -917,7 +1051,7 @@ void QDeclarativeViewer::setAutoRecord(int from, int to)
if (from==0) from=1; // ensure resized
record_autotime = to-from;
autoStartTimer.setInterval(from);
- autoStartTimer.setRunning(true);
+ autoStartTimer.start();
}
void QDeclarativeViewer::setRecordArgs(const QStringList& a)
@@ -935,13 +1069,9 @@ void QDeclarativeViewer::setRecordRate(int fps)
record_rate = fps;
}
-void QDeclarativeViewer::sceneResized(QSize size)
+void QDeclarativeViewer::sceneResized(QSize)
{
- if (size.width() > 0 && size.height() > 0) {
- if (canvas->resizeMode() == QDeclarativeView::SizeViewToRootObject) {
- updateSizeHints();
- }
- }
+ updateSizeHints();
}
void QDeclarativeViewer::keyPressEvent(QKeyEvent *event)
@@ -1019,7 +1149,7 @@ void QDeclarativeViewer::senseFfmpeg()
void QDeclarativeViewer::setRecording(bool on)
{
- if (on == recordTimer.isRunning())
+ if (on == recordTimer.isActive())
return;
int period = int(1000/record_rate+0.5);
@@ -1028,7 +1158,7 @@ void QDeclarativeViewer::setRecording(bool on)
if (on) {
canvas->setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
recordTimer.setInterval(period);
- recordTimer.setRunning(true);
+ recordTimer.start();
frame_fmt = record_file.right(4).toLower();
frame = QImage(canvas->width(),canvas->height(),QImage::Format_RGB32);
if (frame_fmt != ".png" && (!convertAvailable || frame_fmt != ".gif")) {
@@ -1059,7 +1189,7 @@ void QDeclarativeViewer::setRecording(bool on)
}
} else {
canvas->setViewportUpdateMode(QGraphicsView::MinimalViewportUpdate);
- recordTimer.setRunning(false);
+ recordTimer.stop();
if (frame_stream) {
qDebug() << "Saving video...";
frame_stream->close();
@@ -1139,7 +1269,7 @@ void QDeclarativeViewer::setRecording(bool on)
frames.clear();
}
}
- qDebug() << "Recording: " << (recordTimer.isRunning()?"ON":"OFF");
+ qDebug() << "Recording: " << (recordTimer.isActive()?"ON":"OFF");
}
void QDeclarativeViewer::ffmpegFinished(int code)
@@ -1147,11 +1277,21 @@ void QDeclarativeViewer::ffmpegFinished(int code)
qDebug() << "ffmpeg returned" << code << frame_stream->readAllStandardError();
}
+void QDeclarativeViewer::appAboutToQuit()
+{
+ // avoid QGLContext errors about invalid contexts on exit
+ canvas->setViewport(0);
+
+ // avoid crashes if messages are received after app has closed
+ delete loggerWindow;
+ loggerWindow = 0;
+}
+
void QDeclarativeViewer::autoStartRecording()
{
setRecording(true);
autoStopTimer.setInterval(record_autotime);
- autoStopTimer.setRunning(true);
+ autoStopTimer.start();
}
void QDeclarativeViewer::autoStopRecording()
@@ -1181,28 +1321,20 @@ void QDeclarativeViewer::changeOrientation(QAction *action)
return;
action->setChecked(true);
- QString o = action->text().split(QLatin1Char(':')).value(1).trimmed();
+ QString o = action->text();
if (o == QLatin1String("Portrait"))
DeviceOrientation::instance()->setOrientation(DeviceOrientation::Portrait);
else if (o == QLatin1String("Landscape"))
DeviceOrientation::instance()->setOrientation(DeviceOrientation::Landscape);
- else if (o == QLatin1String("Portrait (Inverted)"))
+ else if (o == QLatin1String("Portrait (inverted)"))
DeviceOrientation::instance()->setOrientation(DeviceOrientation::PortraitInverted);
- else if (o == QLatin1String("Landscape (Inverted)"))
+ else if (o == QLatin1String("Landscape (inverted)"))
DeviceOrientation::instance()->setOrientation(DeviceOrientation::LandscapeInverted);
}
void QDeclarativeViewer::orientationChanged()
{
- if (canvas->resizeMode() == QDeclarativeView::SizeRootObjectToView) {
- if (canvas->rootObject()) {
- QSizeF rootObjectSize = canvas->rootObject()->boundingRect().size();
- QSize newSize(rootObjectSize.width(), rootObjectSize.height()+menuBarHeight());
- if (size() != newSize) {
- resize(newSize);
- }
- }
- }
+ updateSizeHints();
}
void QDeclarativeViewer::setDeviceKeys(bool on)
@@ -1232,6 +1364,8 @@ void QDeclarativeViewer::setUseGL(bool useGL)
canvas->setViewport(glWidget);
}
+#else
+ Q_UNUSED(useGL)
#endif
}
@@ -1249,21 +1383,32 @@ void QDeclarativeViewer::setSizeToView(bool sizeToView)
}
}
-void QDeclarativeViewer::updateSizeHints()
+void QDeclarativeViewer::updateSizeHints(bool initial)
{
- if (canvas->resizeMode() == QDeclarativeView::SizeViewToRootObject) {
- QSize newWindowSize = canvas->sizeHint();
- newWindowSize.setHeight(newWindowSize.height()+menuBarHeight());
+ static bool isRecursive = false;
+
+ if (isRecursive)
+ return;
+ isRecursive = true;
+
+ if (initial || (canvas->resizeMode() == QDeclarativeView::SizeViewToRootObject)) {
+ QSize newWindowSize = initial ? initialSize : canvas->sizeHint();
+ //qWarning() << "USH:" << (initial ? "INIT:" : "V2R:") << "setting fixed size " << newWindowSize;
if (!isFullScreen() && !isMaximized()) {
- resize(newWindowSize);
- setFixedSize(newWindowSize);
+ canvas->setFixedSize(newWindowSize);
+ resize(1, 1);
+ layout()->setSizeConstraint(QLayout::SetFixedSize);
+ layout()->activate();
}
- } else { // QDeclarativeView::SizeRootObjectToView
- canvas->setMinimumSize(QSize(0,0));
- canvas->setMaximumSize(QSize(16777215,16777215));
- setMinimumSize(QSize(0,0));
- setMaximumSize(QSize(16777215,16777215));
}
+ //qWarning() << "USH: R2V: setting free size ";
+ layout()->setSizeConstraint(QLayout::SetNoConstraint);
+ layout()->activate();
+ setMaximumSize(QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX));
+ canvas->setMinimumSize(QSize(0,0));
+ canvas->setMaximumSize(QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX));
+
+ isRecursive = false;
}
void QDeclarativeViewer::registerTypes()
diff --git a/tools/qml/qmlruntime.h b/tools/qml/qmlruntime.h
index 27bd217..68d3452 100644
--- a/tools/qml/qmlruntime.h
+++ b/tools/qml/qmlruntime.h
@@ -43,8 +43,7 @@
#define QDECLARATIVEVIEWER_H
#include <QMainWindow>
-#include <QMenuBar>
-#include <private/qdeclarativetimer_p.h>
+#include <QTimer>
#include <QTime>
#include <QList>
@@ -62,17 +61,16 @@ class QNetworkReply;
class QNetworkCookieJar;
class NetworkAccessManagerFactory;
class QTranslator;
+class QActionGroup;
+class QMenuBar;
class QDeclarativeViewer
-#if defined(Q_OS_SYMBIAN)
: public QMainWindow
-#else
- : public QWidget
-#endif
{
-Q_OBJECT
+ Q_OBJECT
+
public:
- QDeclarativeViewer(QWidget *parent=0, Qt::WindowFlags flags=0);
+ QDeclarativeViewer(QWidget *parent = 0, Qt::WindowFlags flags = 0);
~QDeclarativeViewer();
static void registerTypes();
@@ -95,7 +93,7 @@ public:
void setRecordFile(const QString&);
void setRecordArgs(const QStringList&);
void setRecording(bool on);
- bool isRecording() const { return recordTimer.isRunning(); }
+ bool isRecording() const { return recordTimer.isActive(); }
void setAutoRecord(int from, int to);
void setDeviceKeys(bool);
void setNetworkCacheSize(int size);
@@ -103,11 +101,8 @@ public:
void addPluginPath(const QString& plugin);
void setUseGL(bool use);
void setUseNativeFileBrowser(bool);
- void updateSizeHints();
void setSizeToView(bool sizeToView);
- QMenuBar *menuBar() const;
-
QDeclarativeView *view() const;
LoggerWidget *warningsWidget() const;
@@ -132,9 +127,11 @@ public slots:
protected:
virtual void keyPressEvent(QKeyEvent *);
virtual bool event(QEvent *);
- void createMenu(QMenuBar *menu, QMenu *flatmenu);
+ void createMenu();
private slots:
+ void appAboutToQuit();
+
void autoStartRecording();
void autoStopRecording();
void recordFrame();
@@ -149,20 +146,21 @@ private slots:
void warningsWidgetClosed();
private:
+ void updateSizeHints(bool initial = false);
+
QString getVideoFileName();
- int menuBarHeight() const;
LoggerWidget *loggerWindow;
QDeclarativeView *canvas;
QSize initialSize;
QString currentFileOrUrl;
- QDeclarativeTimer recordTimer;
+ QTimer recordTimer;
QString frame_fmt;
QImage frame;
QList<QImage*> frames;
QProcess* frame_stream;
- QDeclarativeTimer autoStartTimer;
- QDeclarativeTimer autoStopTimer;
+ QTimer autoStartTimer;
+ QTimer autoStopTimer;
QString record_dither;
QString record_file;
QSize record_outsize;
@@ -173,7 +171,6 @@ private:
QAction *recordAction;
QString currentSkin;
bool scaleSkin;
- mutable QMenuBar *mb;
RecordingDialog *recdlg;
void senseImageMagick();
diff --git a/tools/qml/recopts_maemo5.ui b/tools/qml/recopts_maemo5.ui
new file mode 100644
index 0000000..3bb5eca
--- /dev/null
+++ b/tools/qml/recopts_maemo5.ui
@@ -0,0 +1,254 @@
+<?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>469</width>
+ <height>142</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Video options</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout" columnstretch="0,2,0">
+ <property name="sizeConstraint">
+ <enum>QLayout::SetMinAndMaxSize</enum>
+ </property>
+ <property name="leftMargin">
+ <number>16</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>16</number>
+ </property>
+ <property name="bottomMargin">
+ <number>8</number>
+ </property>
+ <property name="horizontalSpacing">
+ <number>16</number>
+ </property>
+ <property name="verticalSpacing">
+ <number>0</number>
+ </property>
+ <item row="0" column="1">
+ <widget class="QLineEdit" name="file"/>
+ </item>
+ <item row="0" column="2" rowspan="3">
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>72</width>
+ <height>56</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>Size</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QComboBox" name="sizeCombo">
+ <item>
+ <property name="text">
+ <string/>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>VGA</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>QVGA</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>720p</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="2" column="0" rowspan="2">
+ <widget class="QLabel" name="rateLabel">
+ <property name="text">
+ <string>Rate</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1" rowspan="2">
+ <widget class="QComboBox" name="rateCombo">
+ <item>
+ <property name="text">
+ <string>60 Hz</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>50 Hz</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>25 Hz</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>24 Hz</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>20 Hz</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>15 Hz</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>10 Hz</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="5" column="1">
+ <widget class="QLineEdit" name="args"/>
+ </item>
+ <item row="4" column="1">
+ <widget class="QComboBox" name="profile"/>
+ </item>
+ <item row="4" column="0">
+ <widget class="QLabel" name="profileLabel">
+ <property name="text">
+ <string>Profile</string>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="0" colspan="2">
+ <widget class="QLabel" name="warning">
+ <property name="text">
+ <string notr="true">warning</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="2" rowspan="3">
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QPushButton" name="pickfile">
+ <property name="text">
+ <string>File</string>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="0">
+ <widget class="QPushButton" name="ffmpegHelp">
+ <property name="text">
+ <string>Options</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <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>
+ </connections>
+ <slots>
+ <signal>filePicked(QString)</signal>
+ <signal>argumentsPicked(QString)</signal>
+ <slot>pickFile()</slot>
+ <slot>pickProfile(int)</slot>
+ <slot>storeCustomArgs(QString)</slot>
+ </slots>
+</ui>
diff --git a/tools/qml/texteditautoresizer_maemo5.h b/tools/qml/texteditautoresizer_maemo5.h
new file mode 100644
index 0000000..bb5567a
--- /dev/null
+++ b/tools/qml/texteditautoresizer_maemo5.h
@@ -0,0 +1,113 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the examples 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/qplaintextedit.h>
+#include <QtGui/qtextedit.h>
+#include <QtGui/qabstractkineticscroller.h>
+#include <QtGui/qscrollarea.h>
+#include <QtDebug>
+
+#ifndef TEXTEDITAUTORESIZER_H
+#define TEXTEDITAUTORESIZER_H
+
+class TextEditAutoResizer : public QObject
+{
+ Q_OBJECT
+public:
+ TextEditAutoResizer(QWidget *parent)
+ : QObject(parent), plainTextEdit(qobject_cast<QPlainTextEdit *>(parent)),
+ textEdit(qobject_cast<QTextEdit *>(parent)), edit(qobject_cast<QFrame *>(parent))
+ {
+ // parent must either inherit QPlainTextEdit or QTextEdit!
+ Q_ASSERT(plainTextEdit || textEdit);
+
+ connect(parent, SIGNAL(textChanged()), this, SLOT(textEditChanged()));
+ connect(parent, SIGNAL(cursorPositionChanged()), this, SLOT(textEditChanged()));
+
+ textEditChanged();
+ }
+
+private Q_SLOTS:
+ inline void textEditChanged();
+
+private:
+ QPlainTextEdit *plainTextEdit;
+ QTextEdit *textEdit;
+ QFrame *edit;
+};
+
+void TextEditAutoResizer::textEditChanged()
+{
+ QTextDocument *doc = textEdit ? textEdit->document() : plainTextEdit->document();
+ QRect cursor = textEdit ? textEdit->cursorRect() : plainTextEdit->cursorRect();
+
+ QSize s = doc->size().toSize();
+ if (plainTextEdit)
+ s.setHeight((s.height() + 2) * edit->fontMetrics().lineSpacing());
+
+ const QRect fr = edit->frameRect();
+ const QRect cr = edit->contentsRect();
+
+ edit->setMinimumHeight(qMax(70, s.height() + (fr.height() - cr.height() - 1)));
+
+ // make sure the cursor is visible in case we have a QAbstractScrollArea parent
+ QPoint pos = edit->pos();
+ QWidget *pw = edit->parentWidget();
+ while (pw) {
+ if (qobject_cast<QScrollArea *>(pw))
+ break;
+ pw = pw->parentWidget();
+ }
+
+ if (pw) {
+ QScrollArea *area = static_cast<QScrollArea *>(pw);
+ QPoint scrollto = area->widget()->mapFrom(edit, cursor.center());
+ QPoint margin(10 + cursor.width(), 2 * cursor.height());
+
+ if (QAbstractKineticScroller *scroller = area->property("kineticScroller").value<QAbstractKineticScroller *>()) {
+ scroller->ensureVisible(scrollto, margin.x(), margin.y());
+ } else {
+ area->ensureVisible(scrollto.x(), scrollto.y(), margin.x(), margin.y());
+ }
+ }
+}
+
+#endif
diff --git a/tools/runonphone/main.cpp b/tools/runonphone/main.cpp
index 885d029..7767e4b 100644
--- a/tools/runonphone/main.cpp
+++ b/tools/runonphone/main.cpp
@@ -62,6 +62,8 @@ void printUsage(QTextStream& outstream, QString exeName)
<< "-v, --verbose show debugging output" << endl
<< "-q, --quiet hide progress messages" << endl
<< "-d, --download <remote file> <local file> copy file from phone to PC after running test" << endl
+ << "--nocrashlog Don't capture call stack if test crashes" << endl
+ << "--crashlogpath <dir> Path to save crash logs (default=working dir)" << endl
<< endl
<< "USB COM ports can usually be autodetected, use -p or -f to force a specific port." << endl
<< "If using System TRK, it is possible to copy the program directly to sys/bin on the phone." << endl
@@ -85,6 +87,8 @@ int main(int argc, char *argv[])
QString downloadLocalFile;
int loglevel=1;
int timeout=0;
+ bool crashlog = true;
+ QString crashlogpath;
QListIterator<QString> it(args);
it.next(); //skip name of program
while (it.hasNext()) {
@@ -126,6 +130,12 @@ int main(int argc, char *argv[])
loglevel=2;
else if (arg == "--quiet" || arg == "-q")
loglevel=0;
+ else if (arg == "--nocrashlog")
+ crashlog = false;
+ else if (arg == "--crashlogpath") {
+ CHECK_PARAMETER_EXISTS
+ crashlogpath = it.next();
+ }
else
errstream << "unknown command line option " << arg << endl;
} else {
@@ -145,7 +155,7 @@ int main(int argc, char *argv[])
if (serialPortName.isEmpty()) {
if (loglevel > 0)
outstream << "Detecting serial ports" << endl;
- foreach (const SerialPortId &id, enumerateSerialPorts()) {
+ foreach (const SerialPortId &id, enumerateSerialPorts(loglevel)) {
if (loglevel > 0)
outstream << "Port Name: " << id.portName << ", "
<< "Friendly Name:" << id.friendlyName << endl;
@@ -199,6 +209,8 @@ int main(int argc, char *argv[])
TrkSignalHandler handler;
handler.setLogLevel(loglevel);
+ handler.setCrashLogging(crashlog);
+ handler.setCrashLogPath(crashlogpath);
QObject::connect(launcher.data(), SIGNAL(copyingStarted()), &handler, SLOT(copyingStarted()));
QObject::connect(launcher.data(), SIGNAL(canNotConnect(const QString &)), &handler, SLOT(canNotConnect(const QString &)));
@@ -215,8 +227,12 @@ int main(int argc, char *argv[])
QObject::connect(launcher.data(), SIGNAL(copyProgress(int)), &handler, SLOT(copyProgress(int)));
QObject::connect(launcher.data(), SIGNAL(stateChanged(int)), &handler, SLOT(stateChanged(int)));
QObject::connect(launcher.data(), SIGNAL(processStopped(uint,uint,uint,QString)), &handler, SLOT(stopped(uint,uint,uint,QString)));
+ QObject::connect(launcher.data(), SIGNAL(libraryLoaded(trk::Library)), &handler, SLOT(libraryLoaded(trk::Library)));
+ QObject::connect(launcher.data(), SIGNAL(libraryUnloaded(trk::Library)), &handler, SLOT(libraryUnloaded(trk::Library)));
+ QObject::connect(launcher.data(), SIGNAL(registersAndCallStackReadComplete(const QList<uint> &,const QByteArray &)), &handler, SLOT(registersAndCallStackReadComplete(const QList<uint> &,const QByteArray &)));
QObject::connect(&handler, SIGNAL(resume(uint,uint)), launcher.data(), SLOT(resumeProcess(uint,uint)));
QObject::connect(&handler, SIGNAL(terminate()), launcher.data(), SLOT(terminate()));
+ QObject::connect(&handler, SIGNAL(getRegistersAndCallStack(uint,uint)), launcher.data(), SLOT(getRegistersAndCallStack(uint,uint)));
QObject::connect(launcher.data(), SIGNAL(finished()), &handler, SLOT(finished()));
QTimer timer;
diff --git a/tools/runonphone/serenum.h b/tools/runonphone/serenum.h
index b794b97..a65c8cb 100644
--- a/tools/runonphone/serenum.h
+++ b/tools/runonphone/serenum.h
@@ -51,6 +51,6 @@ struct SerialPortId
QString friendlyName;
};
-QList<SerialPortId> enumerateSerialPorts();
+QList<SerialPortId> enumerateSerialPorts(int loglevel);
#endif // WIN32SERENUM_H
diff --git a/tools/runonphone/serenum_unix.cpp b/tools/runonphone/serenum_unix.cpp
index b6f0293..db6375e 100644
--- a/tools/runonphone/serenum_unix.cpp
+++ b/tools/runonphone/serenum_unix.cpp
@@ -48,9 +48,32 @@
#include <usb.h>
-QList<SerialPortId> enumerateSerialPorts()
+class InterfaceInfo
{
- QList<QString> eligableInterfaces;
+public:
+ InterfaceInfo(const QString &mf, const QString &pr, int mfid, int prid);
+ QString manufacturer;
+ QString product;
+ int manufacturerid;
+ int productid;
+};
+
+InterfaceInfo::InterfaceInfo(const QString &mf, const QString &pr, int mfid, int prid) :
+ manufacturer(mf),
+ product(pr),
+ manufacturerid(mfid),
+ productid(prid)
+{
+ if(mf.isEmpty())
+ manufacturer = QString("[%1]").arg(mfid, 4, 16, QChar('0'));
+ if(pr.isEmpty())
+ product = QString("[%1]").arg(prid, 4, 16, QChar('0'));
+}
+
+QList<SerialPortId> enumerateSerialPorts(int loglevel)
+{
+ QList<QString> eligibleInterfaces;
+ QList<InterfaceInfo> eligibleInterfacesInfo;
QList<SerialPortId> list;
usb_init();
@@ -85,6 +108,41 @@ QList<SerialPortId> enumerateSerialPorts()
}
}
}
+
+ if (usableInterfaces.isEmpty())
+ continue;
+
+ QString manufacturerString;
+ QString productString;
+
+ usb_dev_handle *devh = usb_open(device);
+ if (devh) {
+ QByteArray buf;
+ buf.resize(256);
+ int err = usb_get_string_simple(devh, device->descriptor.iManufacturer, buf.data(), buf.size());
+ if (err < 0) {
+ if (loglevel > 1)
+ qDebug() << " can't read manufacturer name, error:" << err;
+ } else {
+ manufacturerString = QString::fromAscii(buf);
+ if (loglevel > 1)
+ qDebug() << " manufacturer:" << manufacturerString;
+ }
+
+ buf.resize(256);
+ err = usb_get_string_simple(devh, device->descriptor.iProduct, buf.data(), buf.size());
+ if (err < 0) {
+ if (loglevel > 1)
+ qDebug() << " can't read product name, error:" << err;
+ } else {
+ productString = QString::fromAscii(buf);
+ if (loglevel > 1)
+ qDebug() << " product:" << productString;
+ }
+ usb_close(devh);
+ } else if (loglevel > 0) {
+ qDebug() << " can't open usb device";
+ }
// second loop to find the actual data interface.
foreach (int i, usableInterfaces) {
@@ -94,11 +152,22 @@ QList<SerialPortId> enumerateSerialPorts()
if (descriptor.bInterfaceNumber != i)
continue;
if (descriptor.bInterfaceClass == 10) { // "CDC Data"
- // qDebug() << " found the data port"
- // << "bus:" << bus->dirname
- // << "device" << device->filename
- // << "interface" << descriptor.bInterfaceNumber;
- eligableInterfaces << QString("if%1").arg(QString::number(i), 2, QChar('0')); // fix!
+ if (loglevel > 1) {
+ qDebug() << " found the data port"
+ << "bus:" << bus->dirname
+ << "device" << device->filename
+ << "interface" << descriptor.bInterfaceNumber;
+ }
+ // ### manufacturer and product strings are only readable as root :(
+ if (!manufacturerString.isEmpty() && !productString.isEmpty()) {
+ eligibleInterfaces << QString("usb-%1_%2-if%3")
+ .arg(manufacturerString.replace(QChar(' '), QChar('_')))
+ .arg(productString.replace(QChar(' '), QChar('_')))
+ .arg(i, 2, 16, QChar('0'));
+ } else {
+ eligibleInterfaces << QString("if%1").arg(i, 2, 16, QChar('0')); // fix!
+ }
+ eligibleInterfacesInfo << InterfaceInfo(manufacturerString, productString, device->descriptor.idVendor, device->descriptor.idProduct);
}
}
}
@@ -106,13 +175,18 @@ QList<SerialPortId> enumerateSerialPorts()
}
}
}
+
+ if (loglevel > 1)
+ qDebug() << " searching for interfaces:" << eligibleInterfaces;
QDir dir("/dev/serial/by-id/");
foreach (const QFileInfo &info, dir.entryInfoList()) {
if (!info.isDir()) {
- bool usable = eligableInterfaces.isEmpty();
- foreach (const QString &iface, eligableInterfaces) {
+ bool usable = eligibleInterfaces.isEmpty();
+ foreach (const QString &iface, eligibleInterfaces) {
if (info.fileName().contains(iface)) {
+ if (loglevel > 1)
+ qDebug() << " found device file:" << info.fileName() << endl;
usable = true;
break;
}
@@ -126,6 +200,21 @@ QList<SerialPortId> enumerateSerialPorts()
list << id;
}
}
+
+ if (list.isEmpty() && !eligibleInterfacesInfo.isEmpty() && loglevel > 0) {
+ qDebug() << "Possible USB devices found, but without serial drivers:";
+ foreach(const InterfaceInfo &iface, eligibleInterfacesInfo) {
+ qDebug() << " Manufacturer:"
+ << iface.manufacturer
+ << "Product:"
+ << iface.product
+ << endl
+ << " Load generic driver using:"
+ << QString("sudo modprobe usbserial vendor=0x%1 product=0x%2")
+ .arg(iface.manufacturerid, 4, 16, QChar('0'))
+ .arg(iface.productid, 4, 16, QChar('0'));
+ }
+ }
return list;
}
diff --git a/tools/runonphone/serenum_win.cpp b/tools/runonphone/serenum_win.cpp
index 572161c..070cac2 100644
--- a/tools/runonphone/serenum_win.cpp
+++ b/tools/runonphone/serenum_win.cpp
@@ -53,7 +53,7 @@
//{4d36e978-e325-11ce-bfc1-08002be10318}
//DEFINE_GUID(GUID_DEVCLASS_PORTS, 0x4D36E978, 0xE325, 0x11CE, 0xBF, 0xC1, 0x08, 0x00, 0x2B, 0xE1, 0x03, 0x18 );
-QList<SerialPortId> enumerateSerialPorts()
+QList<SerialPortId> enumerateSerialPorts(int)
{
DWORD index=0;
SP_DEVINFO_DATA info;
diff --git a/tools/runonphone/symbianutils/json.cpp b/tools/runonphone/symbianutils/json.cpp
new file mode 100644
index 0000000..2d58824
--- /dev/null
+++ b/tools/runonphone/symbianutils/json.cpp
@@ -0,0 +1,490 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "json.h"
+
+#ifdef TODO_USE_CREATOR
+#include <utils/qtcassert.h>
+#endif // TODO_USE_CREATOR
+
+#include <QtCore/QByteArray>
+#include <QtCore/QTextStream>
+#include <QtCore/QDebug>
+#include <QtCore/QStringList>
+
+#include <ctype.h>
+
+//#define DEBUG_JASON
+#ifdef DEBUG_JASON
+#define JDEBUG(s) qDebug() << s
+#else
+#define JDEBUG(s)
+#endif
+
+namespace tcftrk {
+
+static void skipSpaces(const char *&from, const char *to)
+{
+ while (from != to && isspace(*from))
+ ++from;
+}
+
+QTextStream &operator<<(QTextStream &os, const JsonValue &mi)
+{
+ return os << mi.toString();
+}
+
+void JsonValue::parsePair(const char *&from, const char *to)
+{
+ skipSpaces(from, to);
+ JDEBUG("parsePair: " << QByteArray(from, to - from));
+ m_name = parseCString(from, to);
+ skipSpaces(from, to);
+ while (from < to && *from != ':') {
+ JDEBUG("not a colon" << *from);
+ ++from;
+ }
+ ++from;
+ parseValue(from, to);
+ skipSpaces(from, to);
+}
+
+QByteArray JsonValue::parseNumber(const char *&from, const char *to)
+{
+ QByteArray result;
+ if (from < to && *from == '-') // Leading '-'.
+ result.append(*from++);
+ while (from < to && *from >= '0' && *from <= '9')
+ result.append(*from++);
+ return result;
+}
+
+QByteArray JsonValue::parseCString(const char *&from, const char *to)
+{
+ QByteArray result;
+ JDEBUG("parseCString: " << QByteArray(from, to - from));
+ if (*from != '"') {
+ qDebug() << "JSON Parse Error, double quote expected";
+ ++from; // So we don't hang
+ return QByteArray();
+ }
+ const char *ptr = from;
+ ++ptr;
+ while (ptr < to) {
+ if (*ptr == '"') {
+ ++ptr;
+ result = QByteArray(from + 1, ptr - from - 2);
+ break;
+ }
+ if (*ptr == '\\') {
+ ++ptr;
+ if (ptr == to) {
+ qDebug() << "JSON Parse Error, unterminated backslash escape";
+ from = ptr; // So we don't hang
+ return QByteArray();
+ }
+ }
+ ++ptr;
+ }
+ from = ptr;
+
+ int idx = result.indexOf('\\');
+ if (idx >= 0) {
+ char *dst = result.data() + idx;
+ const char *src = dst + 1, *end = result.data() + result.length();
+ do {
+ char c = *src++;
+ switch (c) {
+ case 'a': *dst++ = '\a'; break;
+ case 'b': *dst++ = '\b'; break;
+ case 'f': *dst++ = '\f'; break;
+ case 'n': *dst++ = '\n'; break;
+ case 'r': *dst++ = '\r'; break;
+ case 't': *dst++ = '\t'; break;
+ case 'v': *dst++ = '\v'; break;
+ case '"': *dst++ = '"'; break;
+ case '\\': *dst++ = '\\'; break;
+ default:
+ {
+ int chars = 0;
+ uchar prod = 0;
+ forever {
+ if (c < '0' || c > '7') {
+ --src;
+ break;
+ }
+ prod = prod * 8 + c - '0';
+ if (++chars == 3 || src == end)
+ break;
+ c = *src++;
+ }
+ if (!chars) {
+ qDebug() << "JSON Parse Error, unrecognized backslash escape";
+ return QByteArray();
+ }
+ *dst++ = prod;
+ }
+ }
+ while (src != end) {
+ char c = *src++;
+ if (c == '\\')
+ break;
+ *dst++ = c;
+ }
+ } while (src != end);
+ *dst = 0;
+ result.truncate(dst - result.data());
+ }
+
+ JDEBUG("parseCString, got " << result);
+ return result;
+}
+
+
+
+void JsonValue::parseValue(const char *&from, const char *to)
+{
+ JDEBUG("parseValue: " << QByteArray(from, to - from));
+ switch (*from) {
+ case '{':
+ parseObject(from, to);
+ break;
+ case 't':
+ if (to - from >= 4 && qstrncmp(from, "true", 4) == 0) {
+ m_data = QByteArray(from, 4);
+ from += m_data.size();
+ m_type = Boolean;
+ }
+ break;
+ case 'f':
+ if (to - from >= 5 && qstrncmp(from, "false", 5) == 0) {
+ m_data = QByteArray(from, 5);
+ from += m_data.size();
+ m_type = Boolean;
+ }
+ break;
+ case 'n':
+ if (to - from >= 4 && qstrncmp(from, "null", 4) == 0) {
+ m_data = QByteArray(from, 4);
+ from += m_data.size();
+ m_type = NullObject;
+ }
+ break;
+ case '[':
+ parseArray(from, to);
+ break;
+ case '"':
+ m_type = String;
+ m_data = parseCString(from, to);
+ break;
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ case '-':
+ m_type = Number;
+ m_data = parseNumber(from, to);
+ default:
+ break;
+ }
+}
+
+void JsonValue::parseObject(const char *&from, const char *to)
+{
+ JDEBUG("parseObject: " << QByteArray(from, to - from));
+#ifdef TODO_USE_CREATOR
+ QTC_ASSERT(*from == '{', /**/);
+#endif
+ ++from;
+ m_type = Object;
+ while (from < to) {
+ if (*from == '}') {
+ ++from;
+ break;
+ }
+ JsonValue child;
+ child.parsePair(from, to);
+ if (!child.isValid())
+ return;
+ m_children += child;
+ if (*from == ',')
+ ++from;
+ }
+}
+
+void JsonValue::parseArray(const char *&from, const char *to)
+{
+ JDEBUG("parseArray: " << QByteArray(from, to - from));
+#ifdef TODO_USE_CREATOR
+ QTC_ASSERT(*from == '[', /**/);
+#endif
+ ++from;
+ m_type = Array;
+ while (from < to) {
+ if (*from == ']') {
+ ++from;
+ break;
+ }
+ JsonValue child;
+ child.parseValue(from, to);
+ if (child.isValid())
+ m_children += child;
+ if (*from == ',')
+ ++from;
+ }
+}
+
+void JsonValue::setStreamOutput(const QByteArray &name, const QByteArray &content)
+{
+ if (content.isEmpty())
+ return;
+ JsonValue child;
+ child.m_type = String;
+ child.m_name = name;
+ child.m_data = content;
+ m_children += child;
+ if (m_type == Invalid)
+ m_type = Object;
+}
+
+static QByteArray ind(int indent)
+{
+ return QByteArray(2 * indent, ' ');
+}
+
+void JsonValue::dumpChildren(QByteArray * str, bool multiline, int indent) const
+{
+ for (int i = 0; i < m_children.size(); ++i) {
+ if (i != 0) {
+ *str += ',';
+ if (multiline)
+ *str += '\n';
+ }
+ if (multiline)
+ *str += ind(indent);
+ *str += m_children.at(i).toString(multiline, indent);
+ }
+}
+
+class MyString : public QString {
+public:
+ ushort at(int i) const { return constData()[i].unicode(); }
+};
+
+template<class ST, typename CT>
+inline ST escapeCStringTpl(const ST &ba)
+{
+ ST ret;
+ ret.reserve(ba.length() * 2);
+ for (int i = 0; i < ba.length(); ++i) {
+ CT c = ba.at(i);
+ switch (c) {
+ case '\\': ret += "\\\\"; break;
+ case '\a': ret += "\\a"; break;
+ case '\b': ret += "\\b"; break;
+ case '\f': ret += "\\f"; break;
+ case '\n': ret += "\\n"; break;
+ case '\r': ret += "\\r"; break;
+ case '\t': ret += "\\t"; break;
+ case '\v': ret += "\\v"; break;
+ case '"': ret += "\\\""; break;
+ default:
+ if (c < 32 || c == 127) {
+ ret += '\\';
+ ret += '0' + (c >> 6);
+ ret += '0' + ((c >> 3) & 7);
+ ret += '0' + (c & 7);
+ } else {
+ ret += c;
+ }
+ }
+ }
+ return ret;
+}
+
+QString JsonValue::escapeCString(const QString &ba)
+{
+ return escapeCStringTpl<MyString, ushort>(static_cast<const MyString &>(ba));
+}
+
+QByteArray JsonValue::escapeCString(const QByteArray &ba)
+{
+ return escapeCStringTpl<QByteArray, uchar>(ba);
+}
+
+QByteArray JsonValue::toString(bool multiline, int indent) const
+{
+ QByteArray result;
+ switch (m_type) {
+ case Invalid:
+ if (multiline)
+ result += ind(indent) + "Invalid\n";
+ else
+ result += "Invalid";
+ break;
+ case String:
+ if (!m_name.isEmpty())
+ result += m_name + "=";
+ result += '"' + escapeCString(m_data) + '"';
+ break;
+ case Number:
+ if (!m_name.isEmpty())
+ result += '"' + m_name + "\":";
+ result += m_data;
+ break;
+ case Boolean:
+ case NullObject:
+ if (!m_name.isEmpty())
+ result += '"' + m_name + "\":";
+ result += m_data;
+ break;
+ case Object:
+ if (!m_name.isEmpty())
+ result += m_name + '=';
+ if (multiline) {
+ result += "{\n";
+ dumpChildren(&result, multiline, indent + 1);
+ result += '\n' + ind(indent) + "}";
+ } else {
+ result += "{";
+ dumpChildren(&result, multiline, indent + 1);
+ result += "}";
+ }
+ break;
+ case Array:
+ if (!m_name.isEmpty())
+ result += m_name + "=";
+ if (multiline) {
+ result += "[\n";
+ dumpChildren(&result, multiline, indent + 1);
+ result += '\n' + ind(indent) + "]";
+ } else {
+ result += "[";
+ dumpChildren(&result, multiline, indent + 1);
+ result += "]";
+ }
+ break;
+ }
+ return result;
+}
+
+void JsonValue::fromString(const QByteArray &ba)
+{
+ const char *from = ba.constBegin();
+ const char *to = ba.constEnd();
+ parseValue(from, to);
+}
+
+JsonValue JsonValue::findChild(const char *name) const
+{
+ for (int i = 0; i < m_children.size(); ++i)
+ if (m_children.at(i).m_name == name)
+ return m_children.at(i);
+ return JsonValue();
+}
+
+void JsonInputStream::appendCString(const char *s)
+{
+ m_target.append('"');
+ for (const char *p = s; *p; p++) {
+ if (*p == '"' || *p == '\\')
+ m_target.append('\\');
+ m_target.append(*p);
+ }
+ m_target.append('"');
+}
+
+void JsonInputStream::appendString(const QString &in)
+{
+ if (in.isEmpty()) {
+ m_target.append("\"\"");
+ return;
+ }
+
+ const QChar doubleQuote('"');
+ const QChar backSlash('\\');
+ QString rc;
+ const int inSize = in.size();
+ rc.reserve(in.size() + 5);
+ rc.append(doubleQuote);
+ for (int i = 0; i < inSize; i++) {
+ const QChar c = in.at(i);
+ if (c == doubleQuote || c == backSlash)
+ rc.append(backSlash);
+ rc.append(c);
+ }
+ rc.append(doubleQuote);
+ m_target.append(rc.toUtf8());
+ return;
+}
+
+JsonInputStream &JsonInputStream::operator<<(const QStringList &in)
+{
+ m_target.append('[');
+ const int count = in.size();
+ for (int i = 0 ; i < count; i++) {
+ if (i)
+ m_target.append(',');
+ appendString(in.at(i));
+ }
+ m_target.append(']');
+ return *this;
+}
+
+JsonInputStream &JsonInputStream::operator<<(const QVector<QByteArray> &ba)
+{
+ m_target.append('[');
+ const int count = ba.size();
+ for (int i = 0 ; i < count; i++) {
+ if (i)
+ m_target.append(',');
+ appendCString(ba.at(i).constData());
+ }
+ m_target.append(']');
+ return *this;
+}
+
+JsonInputStream &JsonInputStream::operator<<(bool b)
+{
+ m_target.append(b ? "true" : "false");
+ return *this;
+}
+
+} // namespace tcftrk
+
diff --git a/tools/runonphone/symbianutils/json.h b/tools/runonphone/symbianutils/json.h
new file mode 100644
index 0000000..ffcb7ab
--- /dev/null
+++ b/tools/runonphone/symbianutils/json.h
@@ -0,0 +1,149 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef SYMBIANUTILS_JSON_H
+#define SYMBIANUTILS_JSON_H
+
+#include "symbianutils_global.h"
+
+#include <QtCore/QByteArray>
+#include <QtCore/QStringList>
+#include <QtCore/QVector>
+
+namespace tcftrk {
+
+class SYMBIANUTILS_EXPORT JsonValue
+{
+public:
+ JsonValue() : m_type(Invalid) {}
+ explicit JsonValue(const QByteArray &str) { fromString(str); }
+
+ QByteArray m_name;
+ QByteArray m_data;
+ QList<JsonValue> m_children;
+
+ enum Type {
+ Invalid,
+ String,
+ Number,
+ Boolean,
+ Object,
+ NullObject,
+ Array,
+ };
+
+ Type m_type;
+
+ inline Type type() const { return m_type; }
+ inline QByteArray name() const { return m_name; }
+ inline bool hasName(const char *name) const { return m_name == name; }
+
+ inline bool isValid() const { return m_type != Invalid; }
+ inline bool isNumber() const { return m_type == Number; }
+ inline bool isString() const { return m_type == String; }
+ inline bool isObject() const { return m_type == Object; }
+ inline bool isArray() const { return m_type == Array; }
+
+
+ inline QByteArray data() const { return m_data; }
+ inline const QList<JsonValue> &children() const { return m_children; }
+ inline int childCount() const { return m_children.size(); }
+
+ const JsonValue &childAt(int index) const { return m_children[index]; }
+ JsonValue &childAt(int index) { return m_children[index]; }
+ JsonValue findChild(const char *name) const;
+
+ QByteArray toString(bool multiline = false, int indent = 0) const;
+ void fromString(const QByteArray &str);
+ void setStreamOutput(const QByteArray &name, const QByteArray &content);
+
+private:
+ static QByteArray parseCString(const char *&from, const char *to);
+ static QByteArray parseNumber(const char *&from, const char *to);
+ static QByteArray escapeCString(const QByteArray &ba);
+ static QString escapeCString(const QString &ba);
+ void parsePair(const char *&from, const char *to);
+ void parseValue(const char *&from, const char *to);
+ void parseObject(const char *&from, const char *to);
+ void parseArray(const char *&from, const char *to);
+
+ void dumpChildren(QByteArray *str, bool multiline, int indent) const;
+};
+
+/* Thin wrapper around QByteArray for formatting JSON input. Use as in:
+ * JsonInputStream(byteArray) << '{' << "bla" << ':' << "blup" << '}';
+ * Note that strings get double quotes and JSON-escaping, characters should be
+ * used for the array/hash delimiters.
+ * */
+class SYMBIANUTILS_EXPORT JsonInputStream {
+public:
+ explicit JsonInputStream(QByteArray &a) : m_target(a) {}
+
+ JsonInputStream &operator<<(char c) { m_target.append(c); return *this; }
+ JsonInputStream &operator<<(const char *c) { appendCString(c); return *this; }
+ JsonInputStream &operator<<(const QByteArray &a) { appendCString(a.constData()); return *this; }
+ JsonInputStream &operator<<(const QString &c) { appendString(c); return *this; }
+
+ // Format as array
+ JsonInputStream &operator<<(const QStringList &c);
+
+ // Format as array
+ JsonInputStream &operator<<(const QVector<QByteArray> &ba);
+
+ JsonInputStream &operator<<(bool b);
+
+ JsonInputStream &operator<<(int i)
+ { m_target.append(QByteArray::number(i)); return *this; }
+ JsonInputStream &operator<<(unsigned i)
+ { m_target.append(QByteArray::number(i)); return *this; }
+ JsonInputStream &operator<<(quint64 i)
+ { m_target.append(QByteArray::number(i)); return *this; }
+
+private:
+ void appendString(const QString &);
+ void appendCString(const char *c);
+
+ QByteArray &m_target;
+};
+
+} // namespace tcftrk
+
+#endif // SYMBIANUTILS_JSON_H
diff --git a/tools/runonphone/symbianutils/launcher.cpp b/tools/runonphone/symbianutils/launcher.cpp
index ecb067e..ee87480 100644
--- a/tools/runonphone/symbianutils/launcher.cpp
+++ b/tools/runonphone/symbianutils/launcher.cpp
@@ -58,6 +58,33 @@
namespace trk {
+struct CrashReportState {
+ CrashReportState();
+ void clear();
+
+ typedef uint Thread;
+ typedef QList<Thread> Threads;
+ Threads threads;
+
+ QList<uint> registers;
+ QByteArray stack;
+ uint sp;
+ uint fetchingStackPID;
+ uint fetchingStackTID;
+};
+
+CrashReportState::CrashReportState()
+{
+ clear();
+}
+
+void CrashReportState::clear()
+{
+ threads.clear();
+ stack.clear();
+ sp = fetchingStackPID = fetchingStackTID = 0;
+}
+
struct LauncherPrivate {
struct CopyState {
QString sourceFileName;
@@ -86,6 +113,7 @@ struct LauncherPrivate {
int m_verbose;
Launcher::Actions m_startupActions;
bool m_closeDevice;
+ CrashReportState m_crashReportState;
};
LauncherPrivate::LauncherPrivate(const TrkDevicePtr &d) :
@@ -202,6 +230,7 @@ void Launcher::setCloseDevice(bool c)
bool Launcher::startServer(QString *errorMessage)
{
errorMessage->clear();
+ d->m_crashReportState.clear();
if (d->m_verbose) {
QString msg;
QTextStream str(&msg);
@@ -375,7 +404,7 @@ void Launcher::handleResult(const TrkResult &result)
logMessage("TEXT TRACE: " + msg);
}
} else {
- logMessage("APPLICATION OUTPUT: " + result.data);
+ logMessage("APPLICATION OUTPUT: " + stringFromArray(result.data));
msg = result.data;
}
msg.replace("\r\n", "\n");
@@ -416,49 +445,54 @@ void Launcher::handleResult(const TrkResult &result)
// target->host OS notification
case TrkNotifyCreated: { // Notify Created
- /*
- const char *data = result.data.data();
- byte error = result.data.at(0);
- byte type = result.data.at(1); // type: 1 byte; for dll item, this value is 2.
- uint pid = extractInt(data + 2); // ProcessID: 4 bytes;
- uint tid = extractInt(data + 6); //threadID: 4 bytes
- uint codeseg = extractInt(data + 10); //code address: 4 bytes; code base address for the library
- uint dataseg = extractInt(data + 14); //data address: 4 bytes; data base address for the library
- uint len = extractShort(data + 18); //length: 2 bytes; length of the library name string to follow
- QByteArray name = result.data.mid(20, len); // name: library name
-
- logMessage(prefix + "NOTE: LIBRARY LOAD: " + str);
- logMessage(prefix + "TOKEN: " + result.token);
- logMessage(prefix + "ERROR: " + int(error));
- logMessage(prefix + "TYPE: " + int(type));
- logMessage(prefix + "PID: " + pid);
- logMessage(prefix + "TID: " + tid);
- logMessage(prefix + "CODE: " + codeseg);
- logMessage(prefix + "DATA: " + dataseg);
- logMessage(prefix + "LEN: " + len);
- logMessage(prefix + "NAME: " + name);
- */
if (result.data.size() < 10)
break;
+ const char *data = result.data.constData();
+ const byte error = result.data.at(0);
+ Q_UNUSED(error)
+ const byte type = result.data.at(1); // type: 1 byte; for dll item, this value is 2.
+ const uint tid = extractInt(data + 6); //threadID: 4 bytes
+ Q_UNUSED(tid)
+ if (type == kDSOSDLLItem && result.data.size() >=20) {
+ const Library lib = Library(result);
+ d->m_session.libraries.push_back(lib);
+ emit libraryLoaded(lib);
+ }
QByteArray ba;
ba.append(result.data.mid(2, 8));
d->m_device->sendTrkMessage(TrkContinue, TrkCallback(), ba, "CONTINUE");
- //d->m_device->sendTrkAck(result.token)
break;
}
case TrkNotifyDeleted: { // NotifyDeleted
const ushort itemType = (unsigned char)result.data.at(1);
- const ushort len = result.data.size() > 12 ? extractShort(result.data.data() + 10) : ushort(0);
+ const uint pid = result.data.size() >= 6 ? extractShort(result.data.constData() + 2) : 0;
+ const uint tid = result.data.size() >= 10 ? extractShort(result.data.constData() + 6) : 0;
+ Q_UNUSED(tid)
+ const ushort len = result.data.size() > 12 ? extractShort(result.data.constData() + 10) : ushort(0);
const QString name = len ? QString::fromAscii(result.data.mid(12, len)) : QString();
logMessage(QString::fromLatin1("%1 %2 UNLOAD: %3").
arg(QString::fromAscii(prefix)).arg(itemType ? QLatin1String("LIB") : QLatin1String("PROCESS")).
arg(name));
d->m_device->sendTrkAck(result.token);
- if (itemType == 0 // process
+ if (itemType == kDSOSProcessItem // process
&& result.data.size() >= 10
&& d->m_session.pid == extractInt(result.data.data() + 6)) {
- copyFileFromRemote();
+ if (d->m_startupActions & ActionDownload)
+ copyFileFromRemote();
+ else
+ disconnectTrk();
+ }
+ else if (itemType == kDSOSDLLItem && len) {
+ // Remove libraries of process.
+ for (QList<Library>::iterator it = d->m_session.libraries.begin(); it != d->m_session.libraries.end(); ) {
+ if ((*it).pid == pid && (*it).name == name) {
+ emit libraryUnloaded(*it);
+ it = d->m_session.libraries.erase(it);
+ } else {
+ ++it;
+ }
+ }
}
break;
}
@@ -705,6 +739,15 @@ void Launcher::handleCreateProcess(const TrkResult &result)
logMessage(msg);
}
emit applicationRunning(d->m_session.pid);
+ //create a "library" entry for the executable which launched the process
+ Library lib;
+ lib.pid = d->m_session.pid;
+ lib.codeseg = d->m_session.codeseg;
+ lib.dataseg = d->m_session.dataseg;
+ lib.name = d->m_fileName.toUtf8();
+ d->m_session.libraries << lib;
+ emit libraryLoaded(lib);
+
QByteArray ba;
appendInt(&ba, d->m_session.pid);
appendInt(&ba, d->m_session.tid);
@@ -856,6 +899,30 @@ QByteArray Launcher::startProcessMessage(const QString &executable,
return ba;
}
+QByteArray Launcher::readMemoryMessage(uint pid, uint tid, uint from, uint len)
+{
+ QByteArray ba;
+ ba.reserve(11);
+ ba.append(char(0x8)); // Options, FIXME: why?
+ appendShort(&ba, len);
+ appendInt(&ba, from);
+ appendInt(&ba, pid);
+ appendInt(&ba, tid);
+ return ba;
+}
+
+QByteArray Launcher::readRegistersMessage(uint pid, uint tid)
+{
+ QByteArray ba;
+ ba.reserve(15);
+ ba.append(char(0)); // Register set, only 0 supported
+ appendShort(&ba, 0); //R0
+ appendShort(&ba, 16); // last register CPSR
+ appendInt(&ba, pid);
+ appendInt(&ba, tid);
+ return ba;
+}
+
void Launcher::startInferiorIfNeeded()
{
emit startingApplication();
@@ -907,4 +974,63 @@ void Launcher::releaseToDeviceManager(Launcher *launcher)
sdm->releaseDevice(launcher->trkServerName());
}
+void Launcher::getRegistersAndCallStack(uint pid, uint tid)
+{
+ d->m_device->sendTrkMessage(TrkReadRegisters,
+ TrkCallback(this, &Launcher::handleReadRegisters),
+ Launcher::readRegistersMessage(pid, tid));
+ d->m_crashReportState.fetchingStackPID = pid;
+ d->m_crashReportState.fetchingStackTID = tid;
+}
+
+void Launcher::handleReadRegisters(const TrkResult &result)
+{
+ if(result.errorCode() || result.data.size() < (17*4)) {
+ terminate();
+ return;
+ }
+ const char* data = result.data.constData() + 1;
+ d->m_crashReportState.registers.clear();
+ d->m_crashReportState.stack.clear();
+ for (int i=0;i<17;i++) {
+ uint r = extractInt(data);
+ data += 4;
+ d->m_crashReportState.registers.append(r);
+ }
+ d->m_crashReportState.sp = d->m_crashReportState.registers.at(13);
+
+ const ushort len = 1024 - (d->m_crashReportState.sp % 1024); //read to 1k boundary first
+ const QByteArray ba = Launcher::readMemoryMessage(d->m_crashReportState.fetchingStackPID,
+ d->m_crashReportState.fetchingStackTID,
+ d->m_crashReportState.sp,
+ len);
+ d->m_device->sendTrkMessage(TrkReadMemory, TrkCallback(this, &Launcher::handleReadStack), ba);
+ d->m_crashReportState.sp += len;
+}
+
+void Launcher::handleReadStack(const TrkResult &result)
+{
+ if (result.errorCode()) {
+ //error implies memory fault when reaching end of stack
+ emit registersAndCallStackReadComplete(d->m_crashReportState.registers, d->m_crashReportState.stack);
+ return;
+ }
+
+ const uint len = extractShort(result.data.constData() + 1);
+ d->m_crashReportState.stack.append(result.data.mid(3, len));
+
+ if (d->m_crashReportState.sp - d->m_crashReportState.registers.at(13) > 0x10000) {
+ //read enough stack, stop here
+ emit registersAndCallStackReadComplete(d->m_crashReportState.registers, d->m_crashReportState.stack);
+ return;
+ }
+ //read 1k more
+ const QByteArray ba = Launcher::readMemoryMessage(d->m_crashReportState.fetchingStackPID,
+ d->m_crashReportState.fetchingStackTID,
+ d->m_crashReportState.sp,
+ 1024);
+ d->m_device->sendTrkMessage(TrkReadMemory, TrkCallback(this, &Launcher::handleReadStack), ba);
+ d->m_crashReportState.sp += 1024;
+}
+
} // namespace trk
diff --git a/tools/runonphone/symbianutils/launcher.h b/tools/runonphone/symbianutils/launcher.h
index 6db69d0..230a122 100644
--- a/tools/runonphone/symbianutils/launcher.h
+++ b/tools/runonphone/symbianutils/launcher.h
@@ -43,6 +43,7 @@
#define LAUNCHER_H
#include "trkdevice.h"
+#include "trkutils.h"
#include <QtCore/QObject>
#include <QtCore/QVariant>
@@ -122,6 +123,9 @@ public:
// Create Trk message to start a process.
static QByteArray startProcessMessage(const QString &executable,
const QStringList &arguments);
+ // Create Trk message to read memory
+ static QByteArray readMemoryMessage(uint pid, uint tid, uint from, uint len);
+ static QByteArray readRegistersMessage(uint pid, uint tid);
// Parse a TrkNotifyStopped message
static bool parseNotifyStopped(const QByteArray &a,
uint *pid, uint *tid, uint *address,
@@ -149,12 +153,18 @@ signals:
void copyProgress(int percent);
void stateChanged(int);
void processStopped(uint pc, uint pid, uint tid, const QString& reason);
+ void processResumed(uint pid, uint tid);
+ void libraryLoaded(const trk::Library &lib);
+ void libraryUnloaded(const trk::Library &lib);
+ void registersAndCallStackReadComplete(const QList<uint>& registers, const QByteArray& stack);
// Emitted by the destructor, for releasing devices of SymbianDeviceManager by name
void destroyed(const QString &serverName);
public slots:
void terminate();
void resumeProcess(uint pid, uint tid);
+ //can be used to obtain traceback after a breakpoint / exception
+ void getRegistersAndCallStack(uint pid, uint tid);
private slots:
void handleResult(const trk::TrkResult &data);
@@ -182,6 +192,8 @@ private:
void handleStop(const TrkResult &result);
void handleSupportMask(const TrkResult &result);
void handleTrkVersion(const TrkResult &result);
+ void handleReadRegisters(const TrkResult &result);
+ void handleReadStack(const TrkResult &result);
void copyFileToRemote();
void copyFileFromRemote();
diff --git a/tools/runonphone/symbianutils/symbianutils.pri b/tools/runonphone/symbianutils/symbianutils.pri
index 6309517..f07e494 100644
--- a/tools/runonphone/symbianutils/symbianutils.pri
+++ b/tools/runonphone/symbianutils/symbianutils.pri
@@ -1,5 +1,7 @@
INCLUDEPATH *= $$PWD
+QT += network
+
# Input
HEADERS += $$PWD/symbianutils_global.h \
$$PWD/callback.h \
@@ -9,14 +11,20 @@ HEADERS += $$PWD/symbianutils_global.h \
$$PWD/launcher.h \
$$PWD/bluetoothlistener.h \
$$PWD/communicationstarter.h \
- $$PWD/symbiandevicemanager.h
+ $$PWD/symbiandevicemanager.h \
+ $$PWD/tcftrkdevice.h \
+ $$PWD/tcftrkmessage.h \
+ $$PWD/json.h
SOURCES += $$PWD/trkutils.cpp \
$$PWD/trkdevice.cpp \
$$PWD/launcher.cpp \
$$PWD/bluetoothlistener.cpp \
$$PWD/communicationstarter.cpp \
- $$PWD/symbiandevicemanager.cpp
+ $$PWD/symbiandevicemanager.cpp \
+ $$PWD/tcftrkdevice.cpp \
+ $$PWD/tcftrkmessage.cpp \
+ $$PWD/json.cpp
# Tests/trklauncher is a console application
contains(QT, gui) {
diff --git a/tools/runonphone/symbianutils/tcftrkdevice.cpp b/tools/runonphone/symbianutils/tcftrkdevice.cpp
new file mode 100644
index 0000000..50e3937
--- /dev/null
+++ b/tools/runonphone/symbianutils/tcftrkdevice.cpp
@@ -0,0 +1,929 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "tcftrkdevice.h"
+#include "json.h"
+
+#include <QtNetwork/QAbstractSocket>
+#include <QtCore/QDebug>
+#include <QtCore/QVector>
+#include <QtCore/QQueue>
+#include <QtCore/QTextStream>
+#include <QtCore/QDateTime>
+#include <QtCore/QFileInfo>
+
+enum { debug = 0 };
+
+static const char messageTerminatorC[] = "\003\001";
+
+namespace tcftrk {
+// ------------- TcfTrkCommandError
+
+TcfTrkCommandError::TcfTrkCommandError() : timeMS(0), code(0), alternativeCode(0)
+{
+}
+
+void TcfTrkCommandError::clear()
+{
+ timeMS = 0;
+ code = alternativeCode = 0;
+ format.clear();
+ alternativeOrganization.clear();
+}
+
+void TcfTrkCommandError::write(QTextStream &str) const
+{
+ if (timeMS) {
+ const QDateTime time(QDate(1970, 1, 1));
+ str << time.addMSecs(timeMS).toString(Qt::ISODate) << ": Error code: " << code
+ << " '" << format << '\'';
+ if (!alternativeOrganization.isEmpty())
+ str << " ('" << alternativeOrganization << "', code: " << alternativeCode << ')';
+ } else{
+ str << "<No error>";
+ }
+}
+
+QString TcfTrkCommandError::toString() const
+{
+ QString rc;
+ QTextStream str(&rc);
+ write(str);
+ return rc;
+}
+
+/* {"Time":1277459762255,"Code":1,"AltCode":-6,"AltOrg":"POSIX","Format":"Unknown error: -6"} */
+bool TcfTrkCommandError::parse(const QVector<JsonValue> &values)
+{
+ // Parse an arbitrary hash (that could as well be a command response)
+ // and check for error elements.
+ unsigned errorKeyCount = 0;
+ clear();
+ do {
+ if (values.isEmpty() || values.front().type() != JsonValue::Object)
+ break;
+ foreach (const JsonValue &c, values.front().children()) {
+ if (c.name() == "Time") {
+ timeMS = c.data().toULongLong();
+ errorKeyCount++;
+ } else if (c.name() == "Code") {
+ code = c.data().toInt();
+ errorKeyCount++;
+ } else if (c.name() == "Format") {
+ format = c.data();
+ errorKeyCount++;
+ } else if (c.name() == "AltCode") {
+ alternativeCode = c.data().toInt();
+ errorKeyCount++;
+ } else if (c.name() == "AltOrg") {
+ alternativeOrganization = c.data();
+ errorKeyCount++;
+ }
+ }
+ } while (false);
+ const bool errorFound = errorKeyCount >= 2u; // Should be at least 'Time', 'Code'.
+ if (!errorFound)
+ clear();
+ if (debug) {
+ qDebug() << "TcfTrkCommandError::parse: Found error: " << errorFound;
+ if (!values.isEmpty())
+ qDebug() << values.front().toString();
+ }
+ return errorFound;
+}
+
+// ------------ TcfTrkCommandResult
+
+TcfTrkCommandResult::TcfTrkCommandResult(Type t) :
+ type(t), service(LocatorService)
+{
+}
+
+TcfTrkCommandResult::TcfTrkCommandResult(char typeChar, Services s,
+ const QByteArray &r,
+ const QVector<JsonValue> &v,
+ const QVariant &ck) :
+ type(FailReply), service(s), request(r), values(v), cookie(ck)
+{
+ switch (typeChar) {
+ case 'N':
+ type = FailReply;
+ break;
+ case 'P':
+ type = ProgressReply;
+ break;
+ case 'R':
+ type = commandError.parse(values) ? CommandErrorReply : SuccessReply;
+ break;
+ default:
+ qWarning("Unknown TCF reply type '%c'", typeChar);
+ }
+}
+
+QString TcfTrkCommandResult::errorString() const
+{
+ QString rc;
+ QTextStream str(&rc);
+
+ switch (type) {
+ case SuccessReply:
+ case ProgressReply:
+ str << "<No error>";
+ return rc;
+ case FailReply:
+ str << "NAK";
+ case CommandErrorReply:
+ commandError.write(str);
+ break;
+ }
+ // Append the failed command for reference
+ str << " (Command was: '";
+ QByteArray printableRequest = request;
+ printableRequest.replace('\0', '|');
+ str << printableRequest << "')";
+ return rc;
+}
+
+QString TcfTrkCommandResult::toString() const
+{
+ QString rc;
+ QTextStream str(&rc);
+ str << "Command answer ";
+ switch (type) {
+ case SuccessReply:
+ str << "[success]";
+ break;
+ case CommandErrorReply:
+ str << "[command error]";
+ break;
+ case FailReply:
+ str << "[fail (NAK)]";
+ break;
+ case ProgressReply:
+ str << "[progress]";
+ break;
+ }
+ str << ", " << values.size() << " values(s) to request: '";
+ QByteArray printableRequest = request;
+ printableRequest.replace('\0', '|');
+ str << printableRequest << "' ";
+ if (cookie.isValid())
+ str << " cookie: " << cookie.toString();
+ str << '\n';
+ for (int i = 0, count = values.size(); i < count; i++)
+ str << '#' << i << ' ' << values.at(i).toString() << '\n';
+ if (type == CommandErrorReply)
+ str << "Error: " << errorString();
+ return rc;
+}
+
+struct TcfTrkSendQueueEntry
+{
+ typedef TcfTrkDevice::MessageType MessageType;
+
+ explicit TcfTrkSendQueueEntry(MessageType mt,
+ int tok,
+ Services s,
+ const QByteArray &d,
+ const TcfTrkCallback &cb= TcfTrkCallback(),
+ const QVariant &ck = QVariant()) :
+ messageType(mt), service(s), data(d), token(tok), cookie(ck), callback(cb) {}
+
+ MessageType messageType;
+ Services service;
+ QByteArray data;
+ int token;
+ QVariant cookie;
+ TcfTrkCallback callback;
+};
+
+struct TcfTrkDevicePrivate {
+ typedef TcfTrkDevice::IODevicePtr IODevicePtr;
+ typedef QHash<int, TcfTrkSendQueueEntry> TokenWrittenMessageMap;
+
+ TcfTrkDevicePrivate();
+
+ const QByteArray m_messageTerminator;
+
+ IODevicePtr m_device;
+ unsigned m_verbose;
+ QByteArray m_readBuffer;
+ int m_token;
+ QQueue<TcfTrkSendQueueEntry> m_sendQueue;
+ TokenWrittenMessageMap m_writtenMessages;
+ QVector<QByteArray> m_registerNames;
+};
+
+TcfTrkDevicePrivate::TcfTrkDevicePrivate() :
+ m_messageTerminator(messageTerminatorC),
+ m_verbose(0), m_token(0)
+{
+}
+
+TcfTrkDevice::TcfTrkDevice(QObject *parent) :
+ QObject(parent), d(new TcfTrkDevicePrivate)
+{
+}
+
+TcfTrkDevice::~TcfTrkDevice()
+{
+ delete d;
+}
+
+QVector<QByteArray> TcfTrkDevice::registerNames() const
+{
+ return d->m_registerNames;
+}
+
+void TcfTrkDevice::setRegisterNames(const QVector<QByteArray>& n)
+{
+ d->m_registerNames = n;
+ if (d->m_verbose) {
+ QString msg;
+ QTextStream str(&msg);
+ const int count = n.size();
+ str << "Registers (" << count << "): ";
+ for (int i = 0; i < count; i++)
+ str << '#' << i << '=' << n.at(i) << ' ';
+ emitLogMessage(msg);
+ }
+}
+
+TcfTrkDevice::IODevicePtr TcfTrkDevice::device() const
+{
+ return d->m_device;
+}
+
+TcfTrkDevice::IODevicePtr TcfTrkDevice::takeDevice()
+{
+ const IODevicePtr old = d->m_device;
+ if (!old.isNull()) {
+ old.data()->disconnect(this);
+ d->m_device = IODevicePtr();
+ }
+ d->m_readBuffer.clear();
+ d->m_token = 0;
+ d->m_sendQueue.clear();
+ return old;
+}
+
+void TcfTrkDevice::setDevice(const IODevicePtr &dp)
+{
+ if (dp.data() == d->m_device.data())
+ return;
+ if (dp.isNull()) {
+ emitLogMessage(QLatin1String("Internal error: Attempt to set NULL device."));
+ return;
+ }
+ takeDevice();
+ d->m_device = dp;
+ connect(dp.data(), SIGNAL(readyRead()), this, SLOT(slotDeviceReadyRead()));
+ if (QAbstractSocket *s = qobject_cast<QAbstractSocket *>(dp.data())) {
+ connect(s, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(slotDeviceError()));
+ connect(s, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(slotDeviceSocketStateChanged()));
+ }
+}
+
+void TcfTrkDevice::slotDeviceError()
+{
+ const QString message = d->m_device->errorString();
+ emitLogMessage(message);
+ emit error(message);
+}
+
+void TcfTrkDevice::slotDeviceSocketStateChanged()
+{
+ if (const QAbstractSocket *s = qobject_cast<const QAbstractSocket *>(d->m_device.data())) {
+ const QAbstractSocket::SocketState st = s->state();
+ switch (st) {
+ case QAbstractSocket::UnconnectedState:
+ emitLogMessage(QLatin1String("Unconnected"));
+ break;
+ case QAbstractSocket::HostLookupState:
+ emitLogMessage(QLatin1String("HostLookupState"));
+ break;
+ case QAbstractSocket::ConnectingState:
+ emitLogMessage(QLatin1String("Connecting"));
+ break;
+ case QAbstractSocket::ConnectedState:
+ emitLogMessage(QLatin1String("Connected"));
+ break;
+ case QAbstractSocket::ClosingState:
+ emitLogMessage(QLatin1String("Closing"));
+ break;
+ default:
+ emitLogMessage(QString::fromLatin1("State %1").arg(st));
+ break;
+ }
+ }
+}
+
+static inline QString debugMessage(QByteArray message, const char *prefix = 0)
+{
+ message.replace('\0', '|');
+ const QString messageS = QString::fromLatin1(message);
+ return prefix ?
+ (QLatin1String(prefix) + messageS) : messageS;
+}
+
+void TcfTrkDevice::slotDeviceReadyRead()
+{
+ d->m_readBuffer += d->m_device->readAll();
+ // Take complete message off front of readbuffer.
+ do {
+ const int messageEndPos = d->m_readBuffer.indexOf(d->m_messageTerminator);
+ if (messageEndPos == -1)
+ break;
+ const QByteArray message = d->m_readBuffer.left(messageEndPos);
+ if (debug)
+ qDebug("Read:\n%s", qPrintable(formatData(message)));
+ if (const int errorCode = parseMessage(message)) {
+ emitLogMessage(QString::fromLatin1("Parse error %1 for: %2").arg(errorCode).arg(debugMessage(message)));
+ }
+ d->m_readBuffer.remove(0, messageEndPos + d->m_messageTerminator.size());
+ } while (!d->m_readBuffer.isEmpty());
+ checkSendQueue(); // Send off further message
+}
+
+// Split \0-terminated message into tokens, skipping the initial type character
+static inline QVector<QByteArray> splitMessage(const QByteArray &message)
+{
+ QVector<QByteArray> tokens;
+ tokens.reserve(7);
+ const int messageSize = message.size();
+ for (int pos = 2; pos < messageSize; ) {
+ const int nextPos = message.indexOf('\0', pos);
+ if (nextPos == -1)
+ break;
+ tokens.push_back(message.mid(pos, nextPos - pos));
+ pos = nextPos + 1;
+ }
+ return tokens;
+}
+
+int TcfTrkDevice::parseMessage(const QByteArray &message)
+{
+ if (d->m_verbose)
+ emitLogMessage(debugMessage(message, "TCF ->"));
+ // Special JSON parse error message or protocol format error.
+ // The port is usually closed after receiving it.
+ // "\3\2{"Time":1276096098255,"Code":3,"Format": "Protocol format error"}"
+ if (message.startsWith("\003\002")) {
+ QByteArray text = message.mid(2);
+ const QString errorMessage = QString::fromLatin1("Parse error received: %1").arg(QString::fromAscii(text));
+ emit error(errorMessage);
+ return 0;
+ }
+ if (message.size() < 4 || message.at(1) != '\0')
+ return 1;
+ // Split into tokens
+ const char type = message.at(0);
+ const QVector<QByteArray> tokens = splitMessage(message);
+ switch (type) {
+ case 'E':
+ return parseTcfEvent(tokens);
+ case 'R': // Command replies
+ case 'N':
+ case 'P':
+ return parseTcfCommandReply(type, tokens);
+ default:
+ emitLogMessage(QString::fromLatin1("Unhandled message type: %1").arg(debugMessage(message)));
+ return 756;
+ }
+ return 0;
+}
+
+int TcfTrkDevice::parseTcfCommandReply(char type, const QVector<QByteArray> &tokens)
+{
+ typedef TcfTrkDevicePrivate::TokenWrittenMessageMap::iterator TokenWrittenMessageMapIterator;
+ // Find the corresponding entry in the written messages hash.
+ const int tokenCount = tokens.size();
+ if (tokenCount < 1)
+ return 234;
+ bool tokenOk;
+ const int token = tokens.at(0).toInt(&tokenOk);
+ if (!tokenOk)
+ return 235;
+ const TokenWrittenMessageMapIterator it = d->m_writtenMessages.find(token);
+ if (it == d->m_writtenMessages.end()) {
+ qWarning("TcfTrkDevice: Internal error: token %d not found for '%s'",
+ token, qPrintable(joinByteArrays(tokens)));
+ return 236;
+ }
+ // No callback: remove entry from map, happy
+ if (!it.value().callback) {
+ d->m_writtenMessages.erase(it);
+ return 0;
+ }
+ // Parse values into JSON
+ QVector<JsonValue> values;
+ values.reserve(tokenCount);
+ for (int i = 1; i < tokenCount; i++) {
+ if (!tokens.at(i).isEmpty()) { // Strange: Empty tokens occur.
+ const JsonValue value(tokens.at(i));
+ if (value.isValid()) {
+ values.push_back(value);
+ } else {
+ qWarning("JSON parse error for reply to command token %d: #%d '%s'",
+ token, i, tokens.at(i).constData());
+ d->m_writtenMessages.erase(it);
+ return -1;
+ }
+ }
+ }
+
+ // Construct result and invoke callback, remove entry from map.
+ TcfTrkCallback callback = it.value().callback;
+ TcfTrkCommandResult result(type, it.value().service, it.value().data,
+ values, it.value().cookie);
+ d->m_writtenMessages.erase(it);
+ callback(result);
+ return 0;
+}
+
+static const char locatorAnswerC[] = "E\0Locator\0Hello\0[\"Locator\"]";
+
+int TcfTrkDevice::parseTcfEvent(const QVector<QByteArray> &tokens)
+{
+ // Event: Ignore the periodical heartbeat event, answer 'Hello',
+ // emit signal for the rest
+ if (tokens.size() < 3)
+ return 433;
+ const Services service = serviceFromName(tokens.at(0).constData());
+ if (service == LocatorService && tokens.at(1) == "peerHeartBeat")
+ return 0;
+ QVector<JsonValue> values;
+ for (int i = 2; i < tokens.size(); i++) {
+ const JsonValue value(tokens.at(i));
+ if (!value.isValid())
+ return 434;
+ values.push_back(value);
+ }
+ // Parse known events, emit signals
+ QScopedPointer<TcfTrkEvent> knownEvent(TcfTrkEvent::parseEvent(service, tokens.at(1), values));
+ if (!knownEvent.isNull()) {
+ // Answer hello event.
+ if (knownEvent->type() == TcfTrkEvent::LocatorHello)
+ writeMessage(QByteArray(locatorAnswerC, sizeof(locatorAnswerC)));
+ emit tcfEvent(*knownEvent);
+ }
+ emit genericTcfEvent(service, tokens.at(1), values);
+
+ if (debug || d->m_verbose) {
+ QString msg;
+ QTextStream str(&msg);
+ if (knownEvent.isNull()) {
+ str << "Event: " << tokens.at(0) << ' ' << tokens.at(1) << '\n';
+ foreach(const JsonValue &val, values)
+ str << " " << val.toString() << '\n';
+ } else {
+ str << knownEvent->toString();
+ }
+ emitLogMessage(msg);
+ }
+
+ return 0;
+}
+
+unsigned TcfTrkDevice::verbose() const
+{
+ return d->m_verbose;
+}
+
+void TcfTrkDevice::setVerbose(unsigned v)
+{
+ d->m_verbose = v;
+}
+
+void TcfTrkDevice::emitLogMessage(const QString &m)
+{
+ if (debug)
+ qWarning("%s", qPrintable(m));
+ emit logMessage(m);
+}
+
+bool TcfTrkDevice::checkOpen()
+{
+ if (d->m_device.isNull()) {
+ emitLogMessage(QLatin1String("Internal error: No device set on TcfTrkDevice."));
+ return false;
+ }
+ if (!d->m_device->isOpen()) {
+ emitLogMessage(QLatin1String("Internal error: Device not open in TcfTrkDevice."));
+ return false;
+ }
+ return true;
+}
+
+void TcfTrkDevice::sendTcfTrkMessage(MessageType mt, Services service, const char *command,
+ const char *commandParameters, int commandParametersLength,
+ const TcfTrkCallback &callBack,
+ const QVariant &cookie)
+
+{
+ if (!checkOpen())
+ return;
+ // Format the message
+ const int token = d->m_token++;
+ QByteArray data;
+ data.reserve(30 + commandParametersLength);
+ data.append('C');
+ data.append('\0');
+ data.append(QByteArray::number(token));
+ data.append('\0');
+ data.append(serviceName(service));
+ data.append('\0');
+ data.append(command);
+ data.append('\0');
+ if (commandParametersLength)
+ data.append(commandParameters, commandParametersLength);
+ const TcfTrkSendQueueEntry entry(mt, token, service, data, callBack, cookie);
+ d->m_sendQueue.enqueue(entry);
+ checkSendQueue();
+}
+
+void TcfTrkDevice::sendTcfTrkMessage(MessageType mt, Services service, const char *command,
+ const QByteArray &commandParameters,
+ const TcfTrkCallback &callBack,
+ const QVariant &cookie)
+{
+ sendTcfTrkMessage(mt, service, command, commandParameters.constData(), commandParameters.size(),
+ callBack, cookie);
+}
+
+// Enclose in message frame and write.
+void TcfTrkDevice::writeMessage(QByteArray data)
+{
+ if (!checkOpen())
+ return;
+
+ if (d->m_verbose)
+ emitLogMessage(debugMessage(data, "TCF <-"));
+
+ // Ensure \0-termination which easily gets lost in QByteArray CT.
+ if (!data.endsWith('\0'))
+ data.append('\0');
+ data += d->m_messageTerminator;
+
+ if (debug > 1)
+ qDebug("Writing:\n%s", qPrintable(formatData(data)));
+
+ d->m_device->write(data);
+ if (QAbstractSocket *as = qobject_cast<QAbstractSocket *>(d->m_device.data()))
+ as->flush();
+}
+
+void TcfTrkDevice::checkSendQueue()
+{
+ // Fire off messages or invoke noops until a message with reply is found
+ // and an entry to writtenMessages is made.
+ while (d->m_writtenMessages.empty()) {
+ if (d->m_sendQueue.isEmpty())
+ break;
+ TcfTrkSendQueueEntry entry = d->m_sendQueue.dequeue();
+ switch (entry.messageType) {
+ case MessageWithReply:
+ d->m_writtenMessages.insert(entry.token, entry);
+ writeMessage(entry.data);
+ break;
+ case MessageWithoutReply:
+ writeMessage(entry.data);
+ break;
+ case NoopMessage: // Invoke the noop-callback for synchronization
+ if (entry.callback) {
+ TcfTrkCommandResult noopResult(TcfTrkCommandResult::SuccessReply);
+ noopResult.cookie = entry.cookie;
+ entry.callback(noopResult);
+ }
+ break;
+ }
+ }
+}
+
+// Fix slashes
+static inline QString fixFileName(QString in)
+{
+ in.replace(QLatin1Char('/'), QLatin1Char('\\'));
+ return in;
+}
+
+// Start a process (consisting of a non-reply setSettings and start).
+void TcfTrkDevice::sendProcessStartCommand(const TcfTrkCallback &callBack,
+ const QString &binaryIn,
+ unsigned uid,
+ QStringList arguments,
+ QString workingDirectory,
+ bool debugControl,
+ const QStringList &additionalLibraries,
+ const QVariant &cookie)
+{
+ // Obtain the bin directory, expand by c:/sys/bin if missing
+ const QChar backSlash('\\');
+ int slashPos = binaryIn.lastIndexOf(QLatin1Char('/'));
+ if (slashPos == -1)
+ slashPos = binaryIn.lastIndexOf(backSlash);
+ const QString sysBin = QLatin1String("c:/sys/bin");
+ const QString binaryFileName = slashPos == -1 ? binaryIn : binaryIn.mid(slashPos + 1);
+ const QString binaryDirectory = slashPos == -1 ? sysBin : binaryIn.left(slashPos);
+ const QString binary = fixFileName(binaryDirectory + QLatin1Char('/') + binaryFileName);
+
+ // Fixup: Does argv[0] convention exist on Symbian?
+ arguments.push_front(binary);
+ if (workingDirectory.isEmpty())
+ workingDirectory = sysBin;
+
+ // Format settings with empty dummy parameter
+ QByteArray setData;
+ JsonInputStream setStr(setData);
+ setStr << "" << '\0'
+ << '[' << "exeToLaunch" << ',' << "addExecutables" << ',' << "addLibraries" << ']'
+ << '\0' << '['
+ << binary << ','
+ << '{' << binaryFileName << ':' << QString::number(uid, 16) << '}' << ','
+ << additionalLibraries
+ << ']';
+ sendTcfTrkMessage(MessageWithoutReply, SettingsService, "set", setData);
+
+ QByteArray startData;
+ JsonInputStream startStr(startData);
+ startStr << fixFileName(workingDirectory)
+ << '\0' << binary << '\0' << arguments << '\0'
+ << QStringList() << '\0' // Env is an array ["PATH=value"] (non-standard)
+ << debugControl;
+ sendTcfTrkMessage(MessageWithReply, ProcessesService, "start", startData, callBack, cookie);
+}
+
+void TcfTrkDevice::sendProcessTerminateCommand(const TcfTrkCallback &callBack,
+ const QByteArray &id,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ str << id;
+ sendTcfTrkMessage(MessageWithReply, ProcessesService, "terminate", data, callBack, cookie);
+}
+
+void TcfTrkDevice::sendRunControlTerminateCommand(const TcfTrkCallback &callBack,
+ const QByteArray &id,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ str << id;
+ sendTcfTrkMessage(MessageWithReply, RunControlService, "terminate", data, callBack, cookie);
+}
+
+// Non-standard: Remove executable from settings
+void TcfTrkDevice::sendSettingsRemoveExecutableCommand(const QString &binaryIn,
+ unsigned uid,
+ const QStringList &additionalLibraries,
+ const QVariant &cookie)
+{
+ QByteArray setData;
+ JsonInputStream setStr(setData);
+ setStr << "" << '\0'
+ << '[' << "removedExecutables" << ',' << "removedLibraries" << ']'
+ << '\0' << '['
+ << '{' << QFileInfo(binaryIn).fileName() << ':' << QString::number(uid, 16) << '}' << ','
+ << additionalLibraries
+ << ']';
+ sendTcfTrkMessage(MessageWithoutReply, SettingsService, "set", setData, TcfTrkCallback(), cookie);
+}
+
+void TcfTrkDevice::sendRunControlResumeCommand(const TcfTrkCallback &callBack,
+ const QByteArray &id,
+ RunControlResumeMode mode,
+ unsigned count,
+ quint64 rangeStart,
+ quint64 rangeEnd,
+ const QVariant &cookie)
+{
+ QByteArray resumeData;
+ JsonInputStream str(resumeData);
+ str << id << '\0' << int(mode) << '\0' << count;
+ switch (mode) {
+ case RM_STEP_OVER_RANGE:
+ case RM_STEP_INTO_RANGE:
+ case RM_REVERSE_STEP_OVER_RANGE:
+ case RM_REVERSE_STEP_INTO_RANGE:
+ str << '\0' << '{' << "RANGE_START" << ':' << rangeStart
+ << ',' << "RANGE_END" << ':' << rangeEnd << '}';
+ break;
+ default:
+ break;
+ }
+ sendTcfTrkMessage(MessageWithReply, RunControlService, "resume", resumeData, callBack, cookie);
+}
+
+void TcfTrkDevice::sendRunControlSuspendCommand(const TcfTrkCallback &callBack,
+ const QByteArray &id,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ str << id;
+ sendTcfTrkMessage(MessageWithReply, RunControlService, "suspend", data, callBack, cookie);
+}
+
+void TcfTrkDevice::sendRunControlResumeCommand(const TcfTrkCallback &callBack,
+ const QByteArray &id,
+ const QVariant &cookie)
+{
+ sendRunControlResumeCommand(callBack, id, RM_RESUME, 1, 0, 0, cookie);
+}
+
+void TcfTrkDevice::sendBreakpointsAddCommand(const TcfTrkCallback &callBack,
+ const Breakpoint &bp,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ str << bp;
+ sendTcfTrkMessage(MessageWithReply, BreakpointsService, "add", data, callBack, cookie);
+}
+
+void TcfTrkDevice::sendBreakpointsRemoveCommand(const TcfTrkCallback &callBack,
+ const QByteArray &id,
+ const QVariant &cookie)
+{
+ sendBreakpointsRemoveCommand(callBack, QVector<QByteArray>(1, id), cookie);
+}
+
+void TcfTrkDevice::sendBreakpointsRemoveCommand(const TcfTrkCallback &callBack,
+ const QVector<QByteArray> &ids,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ str << ids;
+ sendTcfTrkMessage(MessageWithReply, BreakpointsService, "remove", data, callBack, cookie);
+}
+
+void TcfTrkDevice::sendBreakpointsEnableCommand(const TcfTrkCallback &callBack,
+ const QByteArray &id,
+ bool enable,
+ const QVariant &cookie)
+{
+ sendBreakpointsEnableCommand(callBack, QVector<QByteArray>(1, id), enable, cookie);
+}
+
+void TcfTrkDevice::sendBreakpointsEnableCommand(const TcfTrkCallback &callBack,
+ const QVector<QByteArray> &ids,
+ bool enable,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ str << ids;
+ sendTcfTrkMessage(MessageWithReply, BreakpointsService,
+ enable ? "enable" : "disable",
+ data, callBack, cookie);
+}
+
+void TcfTrkDevice::sendMemorySetCommand(const TcfTrkCallback &callBack,
+ const QByteArray &contextId,
+ quint64 start, const QByteArray& data,
+ const QVariant &cookie)
+{
+ QByteArray getData;
+ JsonInputStream str(getData);
+ // start/word size/mode. Mode should ideally be 1 (continue on error?)
+ str << contextId << '\0' << start << '\0' << 1 << '\0' << data.size() << '\0' << 1
+ << '\0' << data.toBase64();
+ sendTcfTrkMessage(MessageWithReply, MemoryService, "set", getData, callBack, cookie);
+}
+
+void TcfTrkDevice::sendMemoryGetCommand(const TcfTrkCallback &callBack,
+ const QByteArray &contextId,
+ quint64 start, quint64 size,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ // start/word size/mode. Mode should ideally be 1 (continue on error?)
+ str << contextId << '\0' << start << '\0' << 1 << '\0' << size << '\0' << 1;
+ sendTcfTrkMessage(MessageWithReply, MemoryService, "get", data, callBack, cookie);
+}
+
+QByteArray TcfTrkDevice::parseMemoryGet(const TcfTrkCommandResult &r)
+{
+ if (r.type != TcfTrkCommandResult::SuccessReply || r.values.size() < 1)
+ return QByteArray();
+ const JsonValue &memoryV = r.values.front();
+
+ if (memoryV.type() != JsonValue::String || memoryV.data().size() < 2
+ || !memoryV.data().endsWith('='))
+ return QByteArray();
+ // Catch errors reported as hash:
+ // R.4."TlVMTA==".{"Time":1276786871255,"Code":1,"AltCode":-38,"AltOrg":"POSIX","Format":"BadDescriptor"}
+ // Not sure what to make of it.
+ if (r.values.size() >= 2 && r.values.at(1).type() == JsonValue::Object)
+ qWarning("Error retrieving memory: %s", r.values.at(1).toString(false).constData());
+ // decode
+ const QByteArray memory = QByteArray::fromBase64(memoryV.data());
+ if (memory.isEmpty())
+ qWarning("Base64 decoding of %s failed.", memoryV.data().constData());
+ if (debug)
+ qDebug("TcfTrkDevice::parseMemoryGet: received %d bytes", memory.size());
+ return memory;
+}
+
+void TcfTrkDevice::sendRegistersGetMCommand(const TcfTrkCallback &callBack,
+ const QByteArray &contextId,
+ const QVector<QByteArray> &ids,
+ const QVariant &cookie)
+{
+ // TODO: use "Registers" (which uses base64-encoded values)
+ QByteArray data;
+ JsonInputStream str(data);
+ str << contextId << '\0' << ids;
+ sendTcfTrkMessage(MessageWithReply, SimpleRegistersService, "get", data, callBack, cookie);
+}
+
+void TcfTrkDevice::sendRegistersGetMRangeCommand(const TcfTrkCallback &callBack,
+ const QByteArray &contextId,
+ unsigned start, unsigned count)
+{
+ const unsigned end = start + count;
+ if (end > (unsigned)d->m_registerNames.size()) {
+ qWarning("TcfTrkDevice: No register name set for index %u (size: %d).", end, d->m_registerNames.size());
+ return;
+ }
+
+ QVector<QByteArray> ids;
+ ids.reserve(count);
+ for (unsigned i = start; i < end; i++)
+ ids.push_back(d->m_registerNames.at(i));
+ sendRegistersGetMCommand(callBack, contextId, ids, QVariant(start));
+}
+
+// Set register
+void TcfTrkDevice::sendRegistersSetCommand(const TcfTrkCallback &callBack,
+ const QByteArray &contextId,
+ const QByteArray &id,
+ unsigned value,
+ const QVariant &cookie)
+{
+ // TODO: use "Registers" (which uses base64-encoded values)
+ QByteArray data;
+ JsonInputStream str(data);
+ str << contextId << '\0' << QVector<QByteArray>(1, id)
+ << '\0' << QVector<QByteArray>(1, QByteArray::number(value, 16));
+ sendTcfTrkMessage(MessageWithReply, SimpleRegistersService, "set", data, callBack, cookie);
+}
+
+// Set register
+void TcfTrkDevice::sendRegistersSetCommand(const TcfTrkCallback &callBack,
+ const QByteArray &contextId,
+ unsigned registerNumber,
+ unsigned value,
+ const QVariant &cookie)
+{
+ if (registerNumber >= (unsigned)d->m_registerNames.size()) {
+ qWarning("TcfTrkDevice: No register name set for index %u (size: %d).", registerNumber, d->m_registerNames.size());
+ return;
+ }
+ sendRegistersSetCommand(callBack, contextId,
+ d->m_registerNames[registerNumber],
+ value, cookie);
+}
+
+} // namespace tcftrk
diff --git a/tools/runonphone/symbianutils/tcftrkdevice.h b/tools/runonphone/symbianutils/tcftrkdevice.h
new file mode 100644
index 0000000..67955e5
--- /dev/null
+++ b/tools/runonphone/symbianutils/tcftrkdevice.h
@@ -0,0 +1,295 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef TCFTRKENGINE_H
+#define TCFTRKENGINE_H
+
+#include "symbianutils_global.h"
+#include "tcftrkmessage.h"
+#include "callback.h"
+#include "json.h"
+
+#include <QtCore/QObject>
+#include <QtCore/QSharedPointer>
+#include <QtCore/QVector>
+#include <QtCore/QVariant>
+#include <QtCore/QStringList>
+
+QT_BEGIN_NAMESPACE
+class QIODevice;
+class QTextStream;
+QT_END_NAMESPACE
+
+namespace tcftrk {
+
+struct TcfTrkDevicePrivate;
+struct Breakpoint;
+
+/* Command error handling in TCF:
+ * 1) 'Severe' errors (JSON format, parameter format): Trk emits a
+ * nonstandard message (\3\2 error paramaters) and closes the connection.
+ * 2) Protocol errors: 'N' without error message is returned.
+ * 3) Errors in command execution: 'R' with a TCF error hash is returned
+ * (see TcfTrkCommandError). */
+
+/* Error code return in 'R' reply to command
+ * (see top of 'Services' documentation). */
+struct SYMBIANUTILS_EXPORT TcfTrkCommandError {
+ TcfTrkCommandError();
+ void clear();
+ operator bool() const { return timeMS != 0; }
+ QString toString() const;
+ void write(QTextStream &str) const;
+ bool parse(const QVector<JsonValue> &values);
+
+ quint64 timeMS; // Since 1.1.1970
+ int code;
+ QByteArray format; // message
+ // 'Alternative' meaning, like altOrg="POSIX"/altCode=<some errno>
+ QByteArray alternativeOrganization;
+ int alternativeCode;
+};
+
+/* Answer to a Tcf command passed to the callback. */
+struct SYMBIANUTILS_EXPORT TcfTrkCommandResult {
+ enum Type {
+ SuccessReply, // 'R' and no error -> all happy.
+ CommandErrorReply, // 'R' with TcfTrkCommandError received
+ ProgressReply, // 'P', progress indicator
+ FailReply // 'N' Protocol NAK, severe error
+ };
+
+ explicit TcfTrkCommandResult(Type t = SuccessReply);
+ explicit TcfTrkCommandResult(char typeChar, Services service,
+ const QByteArray &request,
+ const QVector<JsonValue> &values,
+ const QVariant &cookie);
+
+ QString toString() const;
+ QString errorString() const;
+ operator bool() const { return type == SuccessReply || type == ProgressReply; }
+
+ Type type;
+ Services service;
+ QByteArray request;
+ TcfTrkCommandError commandError;
+ QVector<JsonValue> values;
+ QVariant cookie;
+};
+
+typedef trk::Callback<const TcfTrkCommandResult &> TcfTrkCallback;
+
+/* TcfTrkDevice: TCF communication helper using an asynchronous QIODevice
+ * implementing the TCF protocol according to:
+http://dev.eclipse.org/svnroot/dsdp/org.eclipse.tm.tcf/trunk/docs/TCF%20Specification.html
+http://dev.eclipse.org/svnroot/dsdp/org.eclipse.tm.tcf/trunk/docs/TCF%20Services.html
+ * Commands can be sent along with callbacks that are passed a
+ * TcfTrkCommandResult and an opaque QVariant cookie. In addition, events are emitted.
+*/
+
+class SYMBIANUTILS_EXPORT TcfTrkDevice : public QObject
+{
+ Q_PROPERTY(unsigned verbose READ verbose WRITE setVerbose)
+ Q_OBJECT
+public:
+ enum MessageType { MessageWithReply,
+ MessageWithoutReply, /* Non-standard: "Settings:set" command does not reply */
+ NoopMessage };
+
+ typedef QSharedPointer<QIODevice> IODevicePtr;
+
+ explicit TcfTrkDevice(QObject *parent = 0);
+ virtual ~TcfTrkDevice();
+
+ unsigned verbose() const;
+
+ // Mapping of register names for indices
+ QVector<QByteArray> registerNames() const;
+ void setRegisterNames(const QVector<QByteArray>& n);
+
+ IODevicePtr device() const;
+ IODevicePtr takeDevice();
+ void setDevice(const IODevicePtr &dp);
+
+ void sendTcfTrkMessage(MessageType mt, Services service,
+ const char *command,
+ const char *commandParameters, int commandParametersLength,
+ const TcfTrkCallback &callBack = TcfTrkCallback(),
+ const QVariant &cookie = QVariant());
+
+ void sendTcfTrkMessage(MessageType mt, Services service, const char *command,
+ const QByteArray &commandParameters,
+ const TcfTrkCallback &callBack = TcfTrkCallback(),
+ const QVariant &cookie = QVariant());
+
+ // Convenience messages: Start a process
+ void sendProcessStartCommand(const TcfTrkCallback &callBack,
+ const QString &binary,
+ unsigned uid,
+ QStringList arguments = QStringList(),
+ QString workingDirectory = QString(),
+ bool debugControl = true,
+ const QStringList &additionalLibraries = QStringList(),
+ const QVariant &cookie = QVariant());
+
+ // Preferred over Processes:Terminate by TCF TRK.
+ void sendRunControlTerminateCommand(const TcfTrkCallback &callBack,
+ const QByteArray &id,
+ const QVariant &cookie = QVariant());
+
+ void sendProcessTerminateCommand(const TcfTrkCallback &callBack,
+ const QByteArray &id,
+ const QVariant &cookie = QVariant());
+
+ // Non-standard: Remove executable from settings.
+ // Probably needs to be called after stopping. This command has no response.
+ void sendSettingsRemoveExecutableCommand(const QString &binaryIn,
+ unsigned uid,
+ const QStringList &additionalLibraries = QStringList(),
+ const QVariant &cookie = QVariant());
+
+ void sendRunControlSuspendCommand(const TcfTrkCallback &callBack,
+ const QByteArray &id,
+ const QVariant &cookie = QVariant());
+
+ // Resume / Step (see RunControlResumeMode).
+ void sendRunControlResumeCommand(const TcfTrkCallback &callBack,
+ const QByteArray &id,
+ RunControlResumeMode mode,
+ unsigned count /* = 1, currently ignored. */,
+ quint64 rangeStart, quint64 rangeEnd,
+ const QVariant &cookie = QVariant());
+
+ // Convenience to resume a suspended process
+ void sendRunControlResumeCommand(const TcfTrkCallback &callBack,
+ const QByteArray &id,
+ const QVariant &cookie = QVariant());
+
+ void sendBreakpointsAddCommand(const TcfTrkCallback &callBack,
+ const Breakpoint &b,
+ const QVariant &cookie = QVariant());
+
+ void sendBreakpointsRemoveCommand(const TcfTrkCallback &callBack,
+ const QByteArray &id,
+ const QVariant &cookie = QVariant());
+
+ void sendBreakpointsRemoveCommand(const TcfTrkCallback &callBack,
+ const QVector<QByteArray> &id,
+ const QVariant &cookie = QVariant());
+
+ void sendBreakpointsEnableCommand(const TcfTrkCallback &callBack,
+ const QByteArray &id,
+ bool enable,
+ const QVariant &cookie = QVariant());
+
+ void sendBreakpointsEnableCommand(const TcfTrkCallback &callBack,
+ const QVector<QByteArray> &id,
+ bool enable,
+ const QVariant &cookie = QVariant());
+
+
+ void sendMemoryGetCommand(const TcfTrkCallback &callBack,
+ const QByteArray &contextId,
+ quint64 start, quint64 size,
+ const QVariant &cookie = QVariant());
+
+ void sendMemorySetCommand(const TcfTrkCallback &callBack,
+ const QByteArray &contextId,
+ quint64 start, const QByteArray& data,
+ const QVariant &cookie = QVariant());
+
+ // Reply is an array of hexvalues
+ void sendRegistersGetMCommand(const TcfTrkCallback &callBack,
+ const QByteArray &contextId,
+ const QVector<QByteArray> &ids,
+ const QVariant &cookie = QVariant());
+
+ // Convenience to get a range of register "R0" .. "R<n>".
+ // Cookie will be an int containing "start".
+ void sendRegistersGetMRangeCommand(const TcfTrkCallback &callBack,
+ const QByteArray &contextId,
+ unsigned start, unsigned count);
+
+ // Set register
+ void sendRegistersSetCommand(const TcfTrkCallback &callBack,
+ const QByteArray &contextId,
+ const QByteArray &ids,
+ unsigned value,
+ const QVariant &cookie = QVariant());
+ // Set register
+ void sendRegistersSetCommand(const TcfTrkCallback &callBack,
+ const QByteArray &contextId,
+ unsigned registerNumber,
+ unsigned value,
+ const QVariant &cookie = QVariant());
+
+ static QByteArray parseMemoryGet(const TcfTrkCommandResult &r);
+
+signals:
+ void genericTcfEvent(int service, const QByteArray &name, const QVector<tcftrk::JsonValue> &value);
+ void tcfEvent(const tcftrk::TcfTrkEvent &knownEvent);
+
+ void logMessage(const QString &);
+ void error(const QString &);
+
+public slots:
+ void setVerbose(unsigned v);
+
+private slots:
+ void slotDeviceError();
+ void slotDeviceSocketStateChanged();
+ void slotDeviceReadyRead();
+
+private:
+ bool checkOpen();
+ void checkSendQueue();
+ void writeMessage(QByteArray data);
+ void emitLogMessage(const QString &);
+ int parseMessage(const QByteArray &);
+ int parseTcfCommandReply(char type, const QVector<QByteArray> &tokens);
+ int parseTcfEvent(const QVector<QByteArray> &tokens);
+
+ TcfTrkDevicePrivate *d;
+};
+
+} // namespace tcftrk
+
+#endif // TCFTRKENGINE_H
diff --git a/tools/runonphone/symbianutils/tcftrkmessage.cpp b/tools/runonphone/symbianutils/tcftrkmessage.cpp
new file mode 100644
index 0000000..06035ab
--- /dev/null
+++ b/tools/runonphone/symbianutils/tcftrkmessage.cpp
@@ -0,0 +1,562 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "tcftrkmessage.h"
+#include "json.h"
+
+#include <QtCore/QString>
+#include <QtCore/QTextStream>
+
+// Names matching the enum
+static const char *serviceNamesC[] =
+{ "Locator", "RunControl", "Processes", "Memory", "Settings", "Breakpoints",
+ "Registers", "SimpleRegisters",
+ "UnknownService"};
+
+namespace tcftrk {
+
+SYMBIANUTILS_EXPORT QString joinByteArrays(const QVector<QByteArray> &a, char sep)
+{
+ QString rc;
+ const int count = a.size();
+ for (int i = 0; i < count; i++) {
+ if (i)
+ rc += QLatin1Char(sep);
+ rc += QString::fromUtf8(a.at(i));
+ }
+ return rc;
+}
+
+static inline bool jsonToBool(const JsonValue& js)
+{
+ return js.type() == JsonValue::Boolean && js.data() == "true";
+}
+
+SYMBIANUTILS_EXPORT const char *serviceName(Services s)
+{
+ return serviceNamesC[s];
+}
+
+SYMBIANUTILS_EXPORT Services serviceFromName(const char *n)
+{
+ const int count = sizeof(serviceNamesC)/sizeof(char *);
+ for (int i = 0; i < count; i++)
+ if (!qstrcmp(serviceNamesC[i], n))
+ return static_cast<Services>(i);
+ return UnknownService;
+}
+
+SYMBIANUTILS_EXPORT QString formatData(const QByteArray &a)
+{
+ const int columns = 16;
+ QString rc;
+ QTextStream str(&rc);
+ str.setIntegerBase(16);
+ str.setPadChar(QLatin1Char('0'));
+ const unsigned char *start = reinterpret_cast<const unsigned char *>(a.constData());
+ const unsigned char *end = start + a.size();
+ for (const unsigned char *p = start; p < end ; ) {
+ str << "0x";
+ str.setFieldWidth(4);
+ str << (p - start);
+ str.setFieldWidth(0);
+ str << ' ';
+ QString asc;
+ int c = 0;
+ for ( ; c < columns && p < end; c++, p++) {
+ const unsigned u = *p;
+ str.setFieldWidth(2);
+ str << u;
+ str.setFieldWidth(0);
+ str << ' ';
+ switch (u) {
+ case '\n':
+ asc += QLatin1String("\\n");
+ break;
+ case '\r':
+ asc += QLatin1String("\\r");
+ break;
+ case '\t':
+ asc += QLatin1String("\\t");
+ break;
+ default:
+ if (u >= 32 && u < 128) {
+ asc += QLatin1Char(' ');
+ asc += QLatin1Char(u);
+ } else {
+ asc += QLatin1String(" .");
+ }
+ break;
+ }
+ }
+ if (const int remainder = columns - c)
+ str << QString(3 * remainder, QLatin1Char(' '));
+ str << ' ' << asc << '\n';
+ }
+ return rc;
+}
+
+// ----------- RunControlContext
+RunControlContext::RunControlContext() :
+ flags(0), resumeFlags(0)
+{
+}
+
+void RunControlContext::clear()
+{
+ flags =0;
+ resumeFlags = 0;
+ id.clear();
+ osid.clear();
+ parentId.clear();
+}
+
+RunControlContext::Type RunControlContext::typeFromTcfId(const QByteArray &id)
+{
+ // "p12" or "p12.t34"?
+ return id.contains(".t") ? Thread : Process;
+}
+
+unsigned RunControlContext::processId() const
+{
+ return processIdFromTcdfId(id);
+}
+
+unsigned RunControlContext::threadId() const
+{
+ return threadIdFromTcdfId(id);
+}
+
+unsigned RunControlContext::processIdFromTcdfId(const QByteArray &id)
+{
+ // Cut out process id from "p12" or "p12.t34"?
+ if (!id.startsWith('p'))
+ return 0;
+ const int dotPos = id.indexOf('.');
+ const int pLen = dotPos == -1 ? id.size() : dotPos;
+ return id.mid(1, pLen - 1).toUInt();
+}
+
+unsigned RunControlContext::threadIdFromTcdfId(const QByteArray &id)
+{
+ const int tPos = id.indexOf(".t");
+ return tPos != -1 ? id.mid(tPos + 2).toUInt() : uint(0);
+}
+
+QByteArray RunControlContext::tcfId(unsigned processId, unsigned threadId /* = 0 */)
+{
+ QByteArray rc("p");
+ rc += QByteArray::number(processId);
+ if (threadId) {
+ rc += ".t";
+ rc += QByteArray::number(threadId);
+ }
+ return rc;
+}
+
+RunControlContext::Type RunControlContext::type() const
+{
+ return RunControlContext::typeFromTcfId(id);
+}
+
+bool RunControlContext::parse(const JsonValue &val)
+{
+ clear();
+ if (val.type() != JsonValue::Object)
+ return false;
+ foreach(const JsonValue &c, val.children()) {
+ if (c.name() == "ID") {
+ id = c.data();
+ } else if (c.name() == "OSID") {
+ osid = c.data();
+ } else if (c.name() == "ParentID") {
+ parentId = c.data();
+ } else if (c.name() == "IsContainer") {
+ if (jsonToBool(c))
+ flags |= Container;
+ } else if (c.name() == "CanTerminate") {
+ if (jsonToBool(c))
+ flags |= CanTerminate;
+ } else if (c.name() == "CanResume") {
+ resumeFlags = c.data().toUInt();
+ } else if (c.name() == "HasState") {
+ if (jsonToBool(c))
+ flags |= HasState;
+ } else if (c.name() == "CanSuspend") {
+ if (jsonToBool(c))
+ flags |= CanSuspend;
+ }
+ }
+ return true;
+}
+
+QString RunControlContext::toString() const
+{
+ QString rc;
+ QTextStream str(&rc);
+ format(str);
+ return rc;
+}
+
+void RunControlContext::format(QTextStream &str) const
+{
+ str << " id='" << id << "' osid='" << osid
+ << "' parentId='" << parentId <<"' ";
+ if (flags & Container)
+ str << "[container] ";
+ if (flags & HasState)
+ str << "[has state] ";
+ if (flags & CanSuspend)
+ str << "[can suspend] ";
+ if (flags & CanSuspend)
+ str << "[can terminate] ";
+ str.setIntegerBase(16);
+ str << " resume_flags: 0x" << resumeFlags;
+ str.setIntegerBase(10);
+}
+
+// ------ ModuleLoadEventInfo
+ModuleLoadEventInfo::ModuleLoadEventInfo() :
+ loaded(false), codeAddress(0), dataAddress(0), requireResume(false)
+{
+}
+
+void ModuleLoadEventInfo::clear()
+{
+ loaded = requireResume = false;
+ codeAddress = dataAddress =0;
+}
+
+bool ModuleLoadEventInfo::parse(const JsonValue &val)
+{
+ clear();
+ if (val.type() != JsonValue::Object)
+ return false;
+ foreach(const JsonValue &c, val.children()) {
+ if (c.name() == "Name") {
+ name = c.data();
+ } else if (c.name() == "File") {
+ file = c.data();
+ } else if (c.name() == "CodeAddress") {
+ codeAddress = c.data().toULongLong();
+ } else if (c.name() == "DataAddress") {
+ dataAddress = c.data().toULongLong();
+ } else if (c.name() == "Loaded") {
+ loaded = jsonToBool(c);
+ } else if (c.name() == "RequireResume") {
+ requireResume =jsonToBool(c);
+ }
+ }
+ return true;
+}
+void ModuleLoadEventInfo::format(QTextStream &str) const
+{
+ str << "name='" << name << "' file='" << file << "' " <<
+ (loaded ? "[loaded] " : "[not loaded] ");
+ if (requireResume)
+ str << "[requires resume] ";
+ str.setIntegerBase(16);
+ str << " code: 0x" << codeAddress << " data: 0x" << dataAddress;
+ str.setIntegerBase(10);
+}
+
+// ---------------------- Breakpoint
+
+// Types matching enum
+static const char *breakPointTypesC[] = {"Software", "Hardware", "Auto"};
+
+Breakpoint::Breakpoint(quint64 loc) :
+ type(Auto), enabled(true), ignoreCount(0), location(loc), size(1), thumb(true)
+{
+ if (loc)
+ id = idFromLocation(location);
+}
+
+void Breakpoint::setContextId(unsigned processId, unsigned threadId)
+{
+ contextIds = QVector<QByteArray>(1, RunControlContext::tcfId(processId, threadId));
+}
+
+QByteArray Breakpoint::idFromLocation(quint64 loc)
+{
+ return QByteArray("BP_0x") + QByteArray::number(loc, 16);
+}
+
+QString Breakpoint::toString() const
+{
+ QString rc;
+ QTextStream str(&rc);
+ str.setIntegerBase(16);
+ str << "Breakpoint '" << id << "' " << breakPointTypesC[type] << " for contexts '"
+ << joinByteArrays(contextIds, ',') << "' at 0x" << location;
+ str.setIntegerBase(10);
+ str << " size " << size;
+ if (enabled)
+ str << " [enabled]";
+ if (thumb)
+ str << " [thumb]";
+ if (ignoreCount)
+ str << " IgnoreCount " << ignoreCount;
+ return rc;
+}
+
+JsonInputStream &operator<<(JsonInputStream &str, const Breakpoint &b)
+{
+ if (b.contextIds.isEmpty())
+ qWarning("tcftrk::Breakpoint: No context ids specified");
+
+ str << '{' << "ID" << ':' << QString::fromUtf8(b.id) << ','
+ << "BreakpointType" << ':' << breakPointTypesC[b.type] << ','
+ << "Enabled" << ':' << b.enabled << ','
+ << "IgnoreCount" << ':' << b.ignoreCount << ','
+ << "ContextIds" << ':' << b.contextIds << ','
+ << "Location" << ':' << QString::number(b.location) << ','
+ << "Size" << ':' << b.size << ','
+ << "THUMB_BREAKPOINT" << ':' << b.thumb
+ << '}';
+ return str;
+}
+
+// --- Events
+TcfTrkEvent::TcfTrkEvent(Type type) : m_type(type)
+{
+}
+
+TcfTrkEvent::~TcfTrkEvent()
+{
+}
+
+TcfTrkEvent::Type TcfTrkEvent::type() const
+{
+ return m_type;
+}
+
+QString TcfTrkEvent::toString() const
+{
+ return QString();
+}
+
+static const char sharedLibrarySuspendReasonC[] = "Shared Library";
+
+TcfTrkEvent *TcfTrkEvent::parseEvent(Services s, const QByteArray &nameBA, const QVector<JsonValue> &values)
+{
+ switch (s) {
+ case LocatorService:
+ if (nameBA == "Hello" && values.size() == 1 && values.front().type() == JsonValue::Array) {
+ QStringList services;
+ foreach (const JsonValue &jv, values.front().children())
+ services.push_back(QString::fromUtf8(jv.data()));
+ return new TcfTrkLocatorHelloEvent(services);
+ }
+ break;
+ case RunControlService:
+ if (values.empty())
+ return 0;
+ // "id/PC/Reason/Data"
+ if (nameBA == "contextSuspended" && values.size() == 4) {
+ const QByteArray idBA = values.at(0).data();
+ const quint64 pc = values.at(1).data().toULongLong();
+ const QByteArray reasonBA = values.at(2).data();
+ // Module load: Special
+ if (reasonBA == sharedLibrarySuspendReasonC) {
+ ModuleLoadEventInfo info;
+ if (!info.parse(values.at(3)))
+ return 0;
+ return new TcfTrkRunControlModuleLoadContextSuspendedEvent(idBA, reasonBA, pc, info);
+ }
+ return new TcfTrkRunControlContextSuspendedEvent(idBA, reasonBA, pc);
+ } // "contextSuspended"
+ if (nameBA == "contextAdded")
+ return TcfTrkRunControlContextAddedEvent::parseEvent(values);
+ if (nameBA == "contextRemoved" && values.front().type() == JsonValue::Array) {
+ QVector<QByteArray> ids;
+ foreach(const JsonValue &c, values.front().children())
+ ids.push_back(c.data());
+ return new TcfTrkRunControlContextRemovedEvent(ids);
+ }
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+// -------------- TcfTrkServiceHelloEvent
+TcfTrkLocatorHelloEvent::TcfTrkLocatorHelloEvent(const QStringList &s) :
+ TcfTrkEvent(LocatorHello),
+ m_services(s)
+{
+}
+
+QString TcfTrkLocatorHelloEvent::toString() const
+{
+ return QLatin1String("ServiceHello: ") + m_services.join(QLatin1String(", "));
+}
+
+// -------------- TcfTrkIdEvent
+TcfTrkIdEvent::TcfTrkIdEvent(Type t, const QByteArray &id) :
+ TcfTrkEvent(t), m_id(id)
+{
+}
+
+// ---------- TcfTrkIdsEvent
+TcfTrkIdsEvent::TcfTrkIdsEvent(Type t, const QVector<QByteArray> &ids) :
+ TcfTrkEvent(t), m_ids(ids)
+{
+}
+
+QString TcfTrkIdsEvent::joinedIdString(const char sep) const
+{
+ return joinByteArrays(m_ids, sep);
+}
+
+// ---------------- TcfTrkRunControlContextAddedEvent
+TcfTrkRunControlContextAddedEvent::TcfTrkRunControlContextAddedEvent(const RunControlContexts &c) :
+ TcfTrkEvent(RunControlContextAdded), m_contexts(c)
+{
+}
+
+TcfTrkRunControlContextAddedEvent
+ *TcfTrkRunControlContextAddedEvent::parseEvent(const QVector<JsonValue> &values)
+{
+ // Parse array of contexts
+ if (values.size() < 1 || values.front().type() != JsonValue::Array)
+ return 0;
+
+ RunControlContexts contexts;
+ foreach (const JsonValue &v, values.front().children()) {
+ RunControlContext context;
+ if (context.parse(v))
+ contexts.push_back(context);
+ }
+ return new TcfTrkRunControlContextAddedEvent(contexts);
+}
+
+QString TcfTrkRunControlContextAddedEvent::toString() const
+{
+ QString rc;
+ QTextStream str(&rc);
+ str << "RunControl: " << m_contexts.size() << " context(s) "
+ << (type() == RunControlContextAdded ? "added" : "removed")
+ << '\n';
+ foreach (const RunControlContext &c, m_contexts) {
+ c.format(str);
+ str << '\n';
+ }
+ return rc;
+}
+
+// --------------- TcfTrkRunControlContextRemovedEvent
+TcfTrkRunControlContextRemovedEvent::TcfTrkRunControlContextRemovedEvent(const QVector<QByteArray> &ids) :
+ TcfTrkIdsEvent(RunControlContextRemoved, ids)
+{
+}
+
+QString TcfTrkRunControlContextRemovedEvent::toString() const
+{
+ return QLatin1String("RunControl: Removed contexts '") + joinedIdString() + ("'.");
+}
+
+// --------------- TcfTrkRunControlContextSuspendedEvent
+TcfTrkRunControlContextSuspendedEvent::TcfTrkRunControlContextSuspendedEvent(const QByteArray &id,
+ const QByteArray &reason,
+ quint64 pc) :
+ TcfTrkIdEvent(RunControlSuspended, id), m_pc(pc), m_reason(reason)
+{
+}
+
+TcfTrkRunControlContextSuspendedEvent::TcfTrkRunControlContextSuspendedEvent(Type t,
+ const QByteArray &id,
+ const QByteArray &reason,
+ quint64 pc) :
+ TcfTrkIdEvent(t, id), m_pc(pc), m_reason(reason)
+{
+}
+
+void TcfTrkRunControlContextSuspendedEvent::format(QTextStream &str) const
+{
+ str.setIntegerBase(16);
+ str << "RunControl: '" << idString() << "' suspended at 0x"
+ << m_pc << ": '" << m_reason << "'.";
+ str.setIntegerBase(10);
+}
+
+QString TcfTrkRunControlContextSuspendedEvent::toString() const
+{
+ QString rc;
+ QTextStream str(&rc);
+ format(str);
+ return rc;
+}
+
+TcfTrkRunControlContextSuspendedEvent::Reason TcfTrkRunControlContextSuspendedEvent::reason() const
+{
+ if (m_reason == sharedLibrarySuspendReasonC)
+ return ModuleLoad;
+ if (m_reason == "Breakpoint")
+ return BreakPoint;
+ // 'Data abort exception'/'Thread has panicked' ... unfortunately somewhat unspecific.
+ if (m_reason.contains("exception") || m_reason.contains("panick"))
+ return Crash;
+ return Other;
+}
+
+TcfTrkRunControlModuleLoadContextSuspendedEvent::TcfTrkRunControlModuleLoadContextSuspendedEvent(const QByteArray &id,
+ const QByteArray &reason,
+ quint64 pc,
+ const ModuleLoadEventInfo &mi) :
+ TcfTrkRunControlContextSuspendedEvent(RunControlModuleLoadSuspended, id, reason, pc),
+ m_mi(mi)
+{
+}
+
+QString TcfTrkRunControlModuleLoadContextSuspendedEvent::toString() const
+{
+ QString rc;
+ QTextStream str(&rc);
+ TcfTrkRunControlContextSuspendedEvent::format(str);
+ str << ' ';
+ m_mi.format(str);
+ return rc;
+}
+
+
+} // namespace tcftrk
diff --git a/tools/runonphone/symbianutils/tcftrkmessage.h b/tools/runonphone/symbianutils/tcftrkmessage.h
new file mode 100644
index 0000000..510b485
--- /dev/null
+++ b/tools/runonphone/symbianutils/tcftrkmessage.h
@@ -0,0 +1,296 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef TRCFTRKMESSAGE_H
+#define TRCFTRKMESSAGE_H
+
+#include "symbianutils_global.h"
+
+#include <QtCore/QStringList>
+#include <QtCore/QVector>
+
+QT_BEGIN_NAMESPACE
+class QTextStream;
+QT_END_NAMESPACE
+
+namespace tcftrk {
+
+class JsonValue;
+class JsonInputStream;
+
+enum Services {
+ LocatorService,
+ RunControlService,
+ ProcessesService,
+ MemoryService,
+ SettingsService, // non-standard, trk specific
+ BreakpointsService,
+ RegistersService,
+ SimpleRegistersService, // non-standard, trk specific
+ UnknownService
+}; // Note: Check string array 'serviceNamesC' of same size when modifying this.
+
+// Modes of RunControl/'Resume' (see EDF documentation).
+// As of 24.6.2010, RM_RESUME, RM_STEP_OVER, RM_STEP_INTO,
+// RM_STEP_OVER_RANGE, RM_STEP_INTO_RANGE are supported with
+// RANG_START/RANGE_END parameters.
+enum RunControlResumeMode {
+ RM_RESUME = 0,
+ RM_STEP_OVER = 1, RM_STEP_INTO = 2,
+ RM_STEP_OVER_LINE = 3, RM_STEP_INTO_LINE = 4,
+ RM_STEP_OUT = 5, RM_REVERSE_RESUME = 6,
+ RM_REVERSE_STEP_OVER = 7, RM_REVERSE_STEP_INTO = 8,
+ RM_REVERSE_STEP_OVER_LINE = 9, RM_REVERSE_STEP_INTO_LINE = 10,
+ RM_REVERSE_STEP_OUT = 11, RM_STEP_OVER_RANGE = 12,
+ RM_STEP_INTO_RANGE = 13, RM_REVERSE_STEP_OVER_RANGE = 14,
+ RM_REVERSE_STEP_INTO_RANGE = 15
+};
+
+SYMBIANUTILS_EXPORT const char *serviceName(Services s);
+SYMBIANUTILS_EXPORT Services serviceFromName(const char *);
+
+// Debug helpers
+SYMBIANUTILS_EXPORT QString formatData(const QByteArray &a);
+SYMBIANUTILS_EXPORT QString joinByteArrays(const QVector<QByteArray> &a, char sep = ',');
+
+// Context used in 'RunControl contextAdded' events and in reply
+// to 'Processes start'. Could be thread or process.
+struct SYMBIANUTILS_EXPORT RunControlContext {
+ enum Flags {
+ Container = 0x1, HasState = 0x2, CanSuspend = 0x4,
+ CanTerminate = 0x8
+ };
+ enum Type { Process, Thread };
+
+ RunControlContext();
+ Type type() const;
+ unsigned processId() const;
+ unsigned threadId() const;
+
+ void clear();
+ bool parse(const JsonValue &v);
+ void format(QTextStream &str) const;
+ QString toString() const;
+
+ // Helper for converting the TCF ids ("p12" or "p12.t34")
+ static Type typeFromTcfId(const QByteArray &id);
+ static unsigned processIdFromTcdfId(const QByteArray &id);
+ static unsigned threadIdFromTcdfId(const QByteArray &id);
+ static QByteArray tcfId(unsigned processId, unsigned threadId = 0);
+
+ unsigned flags;
+ unsigned resumeFlags;
+ QByteArray id; // "p434.t699"
+ QByteArray osid; // Non-standard: Process or thread id
+ QByteArray parentId; // Parent process id of a thread.
+};
+
+// Module load information occuring with 'RunControl contextSuspended' events
+struct SYMBIANUTILS_EXPORT ModuleLoadEventInfo {
+ ModuleLoadEventInfo();
+ void clear();
+ bool parse(const JsonValue &v);
+ void format(QTextStream &str) const;
+
+ QByteArray name;
+ QByteArray file;
+ bool loaded;
+ quint64 codeAddress;
+ quint64 dataAddress;
+ bool requireResume;
+};
+
+// Breakpoint as supported by TcfTrk source June 2010
+// TODO: Add watchpoints,etc once they are implemented
+struct SYMBIANUTILS_EXPORT Breakpoint {
+ enum Type { Software, Hardware, Auto };
+
+ explicit Breakpoint(quint64 loc = 0);
+ void setContextId(unsigned processId, unsigned threadId = 0);
+ QString toString() const;
+
+ static QByteArray idFromLocation(quint64 loc); // Automagically determine from location
+
+ Type type;
+ bool enabled;
+ int ignoreCount;
+ QVector<QByteArray> contextIds; // Process or thread ids.
+ QByteArray id; // Id of the breakpoint;
+ quint64 location;
+ unsigned size;
+ bool thumb;
+};
+
+SYMBIANUTILS_EXPORT JsonInputStream &operator<<(JsonInputStream &str, const Breakpoint &b);
+
+// Event hierarchy
+class SYMBIANUTILS_EXPORT TcfTrkEvent {
+ Q_DISABLE_COPY(TcfTrkEvent)
+public:
+ enum Type { None,
+ LocatorHello,
+ RunControlContextAdded,
+ RunControlContextRemoved,
+ RunControlSuspended,
+ RunControlBreakpointSuspended,
+ RunControlModuleLoadSuspended,
+ RunControlResumed
+ };
+
+ virtual ~TcfTrkEvent();
+
+ Type type() const;
+ virtual QString toString() const;
+
+ static TcfTrkEvent *parseEvent(Services s, const QByteArray &name, const QVector<JsonValue> &val);
+
+protected:
+ explicit TcfTrkEvent(Type type = None);
+
+private:
+ const Type m_type;
+};
+
+// ServiceHello
+class SYMBIANUTILS_EXPORT TcfTrkLocatorHelloEvent : public TcfTrkEvent {
+public:
+ explicit TcfTrkLocatorHelloEvent(const QStringList &);
+
+ const QStringList &services() { return m_services; }
+ virtual QString toString() const;
+
+private:
+ QStringList m_services;
+};
+
+// Base for events that just have one id as parameter
+// (simple suspend)
+class SYMBIANUTILS_EXPORT TcfTrkIdEvent : public TcfTrkEvent {
+protected:
+ explicit TcfTrkIdEvent(Type t, const QByteArray &id);
+public:
+ QByteArray id() const { return m_id; }
+ QString idString() const { return QString::fromUtf8(m_id); }
+
+private:
+ const QByteArray m_id;
+};
+
+// Base for events that just have some ids as parameter
+// (context removed)
+class SYMBIANUTILS_EXPORT TcfTrkIdsEvent : public TcfTrkEvent {
+protected:
+ explicit TcfTrkIdsEvent(Type t, const QVector<QByteArray> &ids);
+
+public:
+ QVector<QByteArray> ids() const { return m_ids; }
+ QString joinedIdString(const char sep = ',') const;
+
+private:
+ const QVector<QByteArray> m_ids;
+};
+
+// RunControlContextAdded
+class SYMBIANUTILS_EXPORT TcfTrkRunControlContextAddedEvent : public TcfTrkEvent {
+public:
+ typedef QVector<RunControlContext> RunControlContexts;
+
+ explicit TcfTrkRunControlContextAddedEvent(const RunControlContexts &c);
+
+ const RunControlContexts &contexts() const { return m_contexts; }
+ virtual QString toString() const;
+
+ static TcfTrkRunControlContextAddedEvent *parseEvent(const QVector<JsonValue> &val);
+
+private:
+ const RunControlContexts m_contexts;
+};
+
+// RunControlContextRemoved
+class SYMBIANUTILS_EXPORT TcfTrkRunControlContextRemovedEvent : public TcfTrkIdsEvent {
+public:
+ explicit TcfTrkRunControlContextRemovedEvent(const QVector<QByteArray> &id);
+ virtual QString toString() const;
+};
+
+// Simple RunControlContextSuspended (process/thread)
+class SYMBIANUTILS_EXPORT TcfTrkRunControlContextSuspendedEvent : public TcfTrkIdEvent {
+public:
+ enum Reason { BreakPoint, ModuleLoad, Crash, Other } ;
+
+ explicit TcfTrkRunControlContextSuspendedEvent(const QByteArray &id,
+ const QByteArray &reason,
+ quint64 pc = 0);
+ virtual QString toString() const;
+
+ quint64 pc() const { return m_pc; }
+ QByteArray reasonID() const { return m_reason; }
+ Reason reason() const;
+
+protected:
+ explicit TcfTrkRunControlContextSuspendedEvent(Type t,
+ const QByteArray &id,
+ const QByteArray &reason,
+ quint64 pc = 0);
+ void format(QTextStream &str) const;
+
+private:
+ const quint64 m_pc;
+ const QByteArray m_reason;
+};
+
+// RunControlContextSuspended due to module load
+class SYMBIANUTILS_EXPORT TcfTrkRunControlModuleLoadContextSuspendedEvent : public TcfTrkRunControlContextSuspendedEvent {
+public:
+ explicit TcfTrkRunControlModuleLoadContextSuspendedEvent(const QByteArray &id,
+ const QByteArray &reason,
+ quint64 pc,
+ const ModuleLoadEventInfo &mi);
+
+ virtual QString toString() const;
+ const ModuleLoadEventInfo &info() const { return m_mi; }
+
+private:
+ const ModuleLoadEventInfo m_mi;
+};
+
+} // namespace tcftrk
+#endif // TRCFTRKMESSAGE_H
diff --git a/tools/runonphone/symbianutils/trkdevice.cpp b/tools/runonphone/symbianutils/trkdevice.cpp
index bd24300..9039184 100644
--- a/tools/runonphone/symbianutils/trkdevice.cpp
+++ b/tools/runonphone/symbianutils/trkdevice.cpp
@@ -636,10 +636,11 @@ private:
void readMessages();
QByteArray m_trkReadBuffer;
+ bool linkEstablishmentMode;
};
ReaderThreadBase::ReaderThreadBase(const QSharedPointer<DeviceContext> &context) :
- m_context(context)
+ m_context(context), linkEstablishmentMode(true)
{
static const int trkResultMetaId = qRegisterMetaType<trk::TrkResult>();
Q_UNUSED(trkResultMetaId)
@@ -662,7 +663,7 @@ void ReaderThreadBase::readMessages()
{
TrkResult r;
QByteArray rawData;
- while (extractResult(&m_trkReadBuffer, m_context->serialFrame, &r, &rawData)) {
+ while (extractResult(&m_trkReadBuffer, m_context->serialFrame, &r, linkEstablishmentMode, &rawData)) {
emit messageReceived(r, rawData);
}
}
diff --git a/tools/runonphone/symbianutils/trkutils.cpp b/tools/runonphone/symbianutils/trkutils.cpp
index 60e391e..d89da20 100644
--- a/tools/runonphone/symbianutils/trkutils.cpp
+++ b/tools/runonphone/symbianutils/trkutils.cpp
@@ -52,6 +52,25 @@
namespace trk {
+Library::Library() : codeseg(0), dataseg(0), pid(0)
+{
+}
+
+Library::Library(const TrkResult &result) : codeseg(0), dataseg(0), pid(0)
+{
+ if (result.data.size() < 20) {
+ qWarning("Invalid trk creation notification received.");
+ return;
+ }
+
+ const char *data = result.data.constData();
+ pid = extractInt(data + 2);
+ codeseg = extractInt(data + 10);
+ dataseg = extractInt(data + 14);
+ const uint len = extractShort(data + 18);
+ name = result.data.mid(20, len);
+}
+
TrkAppVersion::TrkAppVersion()
{
reset();
@@ -77,11 +96,11 @@ void Session::reset()
extended1TypeSize = 0;
extended2TypeSize = 0;
pid = 0;
+ mainTid = 0;
tid = 0;
codeseg = 0;
dataseg = 0;
- currentThread = 0;
libraries.clear();
trkAppVersion.reset();
}
@@ -143,6 +162,90 @@ QString Session::deviceDescription(unsigned verbose) const
return msg.arg(formatTrkVersion(trkAppVersion));
}
+QByteArray Session::gdbLibraryList() const
+{
+ const int count = libraries.size();
+ QByteArray response = "l<library-list>";
+ for (int i = 0; i != count; ++i) {
+ const trk::Library &lib = libraries.at(i);
+ response += "<library name=\"";
+ response += lib.name;
+ response += "\">";
+ response += "<section address=\"0x";
+ response += trk::hexNumber(lib.codeseg);
+ response += "\"/>";
+ response += "<section address=\"0x";
+ response += trk::hexNumber(lib.dataseg);
+ response += "\"/>";
+ response += "<section address=\"0x";
+ response += trk::hexNumber(lib.dataseg);
+ response += "\"/>";
+ response += "</library>";
+ }
+ response += "</library-list>";
+ return response;
+}
+
+QByteArray Session::gdbQsDllInfo(int start, int count) const
+{
+ // Happens with gdb 6.4.50.20060226-cvs / CodeSourcery.
+ // Never made it into FSF gdb that got qXfer:libraries:read instead.
+ // http://sourceware.org/ml/gdb/2007-05/msg00038.html
+ // Name=hexname,TextSeg=textaddr[,DataSeg=dataaddr]
+ const int libraryCount = libraries.size();
+ const int end = count < 0 ? libraryCount : qMin(libraryCount, start + count);
+ QByteArray response(1, end == libraryCount ? 'l' : 'm');
+ for (int i = start; i < end; ++i) {
+ if (i != start)
+ response += ';';
+ const Library &lib = libraries.at(i);
+ response += "Name=";
+ response += lib.name.toHex();
+ response += ",TextSeg=";
+ response += hexNumber(lib.codeseg);
+ response += ",DataSeg=";
+ response += hexNumber(lib.dataseg);
+ }
+ return response;
+}
+
+QString Session::toString() const
+{
+ QString rc;
+ QTextStream str(&rc);
+ str << "Session: " << deviceDescription(false) << '\n'
+ << "pid: " << pid << "main thread: " << mainTid
+ << " current thread: " << tid << ' ';
+ str.setIntegerBase(16);
+ str << " code: 0x" << codeseg << " data: 0x" << dataseg << '\n';
+ if (const int libCount = libraries.size()) {
+ str << "Libraries:\n";
+ for (int i = 0; i < libCount; i++)
+ str << " #" << i << ' ' << libraries.at(i).name
+ << " code: 0x" << libraries.at(i).codeseg
+ << " data: 0x" << libraries.at(i).dataseg << '\n';
+ }
+ if (const int moduleCount = modules.size()) {
+ str << "Modules:\n";
+ for (int i = 0; i < moduleCount; i++)
+ str << " #" << i << ' ' << modules.at(i) << '\n';
+ }
+ str.setIntegerBase(10);
+ if (!addressToBP.isEmpty()) {
+ typedef QHash<uint, uint>::const_iterator BP_ConstIterator;
+ str << "Breakpoints:\n";
+ const BP_ConstIterator cend = addressToBP.constEnd();
+ for (BP_ConstIterator it = addressToBP.constBegin(); it != cend; ++it) {
+ str.setIntegerBase(16);
+ str << " 0x" << it.key();
+ str.setIntegerBase(10);
+ str << ' ' << it.value() << '\n';
+ }
+ }
+
+ return rc;
+}
+
// --------------
QByteArray decode7d(const QByteArray &ba)
@@ -188,18 +291,15 @@ SYMBIANUTILS_EXPORT QString stringFromArray(const QByteArray &ba, int maxLen)
QString ascii;
const int size = maxLen == -1 ? ba.size() : qMin(ba.size(), maxLen);
for (int i = 0; i < size; ++i) {
- //if (i == 5 || i == ba.size() - 2)
- // str += " ";
- int c = byte(ba.at(i));
- str += QString("%1 ").arg(c, 2, 16, QChar('0'));
- if (i >= 8 && i < ba.size() - 2)
- ascii += QChar(c).isPrint() ? QChar(c) : QChar('.');
+ const int c = byte(ba.at(i));
+ str += QString::fromAscii("%1 ").arg(c, 2, 16, QChar('0'));
+ ascii += QChar(c).isPrint() ? QChar(c) : QChar('.');
}
if (size != ba.size()) {
- str += "...";
- ascii += "...";
+ str += QLatin1String("...");
+ ascii += QLatin1String("...");
}
- return str + " " + ascii;
+ return str + QLatin1String(" ") + ascii;
}
SYMBIANUTILS_EXPORT QByteArray hexNumber(uint n, int digits)
@@ -299,20 +399,30 @@ ushort isValidTrkResult(const QByteArray &buffer, bool serialFrame, ushort& mux)
return firstDelimiterPos != -1 ? firstDelimiterPos : buffer.size();
}
-bool extractResult(QByteArray *buffer, bool serialFrame, TrkResult *result, QByteArray *rawData)
+bool extractResult(QByteArray *buffer, bool serialFrame, TrkResult *result, bool &linkEstablishmentMode, QByteArray *rawData)
{
result->clear();
if(rawData)
rawData->clear();
- const ushort len = isValidTrkResult(*buffer, serialFrame, result->multiplex);
- if (!len)
- return false;
+ ushort len = isValidTrkResult(*buffer, serialFrame, result->multiplex);
// handle receiving application output, which is not a regular command
const int delimiterPos = serialFrame ? 4 : 0;
+ if (linkEstablishmentMode) {
+ //when "hot connecting" a device, we can receive partial frames.
+ //this code resyncs by discarding data until a TRK frame is found
+ while (buffer->length() > delimiterPos
+ && result->multiplex != MuxTextTrace
+ && !(result->multiplex == MuxTrk && buffer->at(delimiterPos) == 0x7e)) {
+ buffer->remove(0,1);
+ len = isValidTrkResult(*buffer, serialFrame, result->multiplex);
+ }
+ }
+ if (!len)
+ return false;
if (buffer->at(delimiterPos) != 0x7e) {
result->isDebugOutput = true;
result->data = buffer->mid(delimiterPos, len);
- *buffer->remove(0, delimiterPos + len);
+ buffer->remove(0, delimiterPos + len);
return true;
}
// FIXME: what happens if the length contains 0xfe?
@@ -320,7 +430,7 @@ bool extractResult(QByteArray *buffer, bool serialFrame, TrkResult *result, QByt
const QByteArray data = decode7d(buffer->mid(delimiterPos + 1, len - 2));
if(rawData)
*rawData = data;
- *buffer->remove(0, delimiterPos + len);
+ buffer->remove(0, delimiterPos + len);
byte sum = 0;
for (int i = 0; i < data.size(); ++i) // 3 = 2 * 0xfe + sum
@@ -335,6 +445,7 @@ bool extractResult(QByteArray *buffer, bool serialFrame, TrkResult *result, QByt
//logMessage(" CURR DATA: " << stringFromArray(data));
//QByteArray prefix = "READ BUF: ";
//logMessage((prefix + "HEADER: " + stringFromArray(header).toLatin1()).data());
+ linkEstablishmentMode = false; //have received a good TRK packet, therefore in sync
return true;
}
diff --git a/tools/runonphone/symbianutils/trkutils.h b/tools/runonphone/symbianutils/trkutils.h
index e571028..d365f0d 100644
--- a/tools/runonphone/symbianutils/trkutils.h
+++ b/tools/runonphone/symbianutils/trkutils.h
@@ -56,6 +56,7 @@ QT_END_NAMESPACE
namespace trk {
typedef unsigned char byte;
+struct TrkResult;
enum Command {
//meta commands
@@ -135,6 +136,20 @@ enum Command {
TrkDSPositionFile = 0xd4
};
+enum DSOSItemTypes {
+ kDSOSProcessItem = 0x0000,
+ kDSOSThreadItem = 0x0001,
+ kDSOSDLLItem = 0x0002,
+ kDSOSAppItem = 0x0003,
+ kDSOSMemBlockItem = 0x0004,
+ kDSOSProcAttachItem = 0x0005,
+ kDSOSThreadAttachItem = 0x0006,
+ kDSOSProcAttach2Item = 0x0007,
+ kDSOSProcRunItem = 0x0008,
+ /* 0x0009 - 0x00ff reserved for general expansion */
+ /* 0x0100 - 0xffff available for target-specific use */
+};
+
enum SerialMultiplexor {
MuxRaw = 0,
MuxTextTrace = 0x0102,
@@ -164,11 +179,14 @@ SYMBIANUTILS_EXPORT void appendString(QByteArray *ba, const QByteArray &str, End
struct SYMBIANUTILS_EXPORT Library
{
- Library() {}
+ Library();
+ explicit Library(const TrkResult &r);
QByteArray name;
uint codeseg;
uint dataseg;
+ //library addresses are valid for a given process (depending on memory model, they might be loaded at the same address in all processes or not)
+ uint pid;
};
struct SYMBIANUTILS_EXPORT TrkAppVersion
@@ -187,6 +205,11 @@ struct SYMBIANUTILS_EXPORT Session
Session();
void reset();
QString deviceDescription(unsigned verbose) const;
+ QString toString() const;
+ // Answer to qXfer::libraries
+ QByteArray gdbLibraryList() const;
+ // Answer to qsDllInfo, can be sent chunk-wise.
+ QByteArray gdbQsDllInfo(int start = 0, int count = -1) const;
// Trk feedback
byte cpuMajor;
@@ -198,6 +221,7 @@ struct SYMBIANUTILS_EXPORT Session
byte extended2TypeSize;
TrkAppVersion trkAppVersion;
uint pid;
+ uint mainTid;
uint tid;
uint codeseg;
uint dataseg;
@@ -206,12 +230,7 @@ struct SYMBIANUTILS_EXPORT Session
typedef QList<Library> Libraries;
Libraries libraries;
- typedef uint Thread;
- typedef QList<Thread> Threads;
- Threads threads;
-
// Gdb request
- uint currentThread;
QStringList modules;
};
diff --git a/tools/runonphone/symbianutils/trkutils_p.h b/tools/runonphone/symbianutils/trkutils_p.h
index 12b0109..05df83a 100644
--- a/tools/runonphone/symbianutils/trkutils_p.h
+++ b/tools/runonphone/symbianutils/trkutils_p.h
@@ -55,7 +55,7 @@ void appendDateTime(QByteArray *ba, QDateTime dateTime, Endianness = TargetByteO
// returns a QByteArray containing optionally
// the serial frame [0x01 0x90 <len>] and 0x7e encoded7d(ba) 0x7e
QByteArray frameMessage(byte command, byte token, const QByteArray &data, bool serialFrame);
-bool extractResult(QByteArray *buffer, bool serialFrame, TrkResult *r, QByteArray *rawData = 0);
+bool extractResult(QByteArray *buffer, bool serialFrame, TrkResult *r, bool& linkEstablishmentMode, QByteArray *rawData = 0);
} // namespace trk
diff --git a/tools/runonphone/trksignalhandler.cpp b/tools/runonphone/trksignalhandler.cpp
index 2abf91f..898692a 100644
--- a/tools/runonphone/trksignalhandler.cpp
+++ b/tools/runonphone/trksignalhandler.cpp
@@ -42,7 +42,19 @@
#include <QDebug>
#include <QCoreApplication>
#include <QObject>
+#include <QFile>
+#include <QDir>
#include "trksignalhandler.h"
+#include "trkutils.h"
+
+class CrashState
+{
+public:
+ uint pid;
+ uint tid;
+ QString crashReason;
+ uint crashPC;
+};
class TrkSignalHandlerPrivate
{
@@ -55,6 +67,14 @@ private:
QTextStream err;
int loglevel;
int lastpercent;
+ QList<trk::Library> libraries;
+ QFile crashlogtextfile;
+ QFile crashstackfile;
+ QList<CrashState> queuedCrashes;
+ QList<int> dyingThreads;
+ QString crashlogPath;
+ bool crashlog;
+ bool terminateNeeded;
};
void TrkSignalHandler::copyingStarted()
@@ -108,6 +128,7 @@ void TrkSignalHandler::startingApplication()
void TrkSignalHandler::applicationRunning(uint pid)
{
+ Q_UNUSED(pid)
if (d->loglevel > 0)
d->out << "Running..." << endl;
}
@@ -155,13 +176,163 @@ void TrkSignalHandler::setLogLevel(int level)
d->loglevel = level;
}
+void TrkSignalHandler::setCrashLogging(bool enabled)
+{
+ d->crashlog = enabled;
+}
+
+void TrkSignalHandler::setCrashLogPath(QString path)
+{
+ d->crashlogPath = path;
+}
+
+bool lessThanCodeBase(const trk::Library& cs1, const trk::Library& cs2)
+{
+ return cs1.codeseg < cs2.codeseg;
+}
+
void TrkSignalHandler::stopped(uint pc, uint pid, uint tid, const QString& reason)
{
d->err << "STOPPED: pc=" << hex << pc << " pid=" << pid
<< " tid=" << tid << dec << " - " << reason << endl;
- // if it was a breakpoint, then we could continue with "emit resume(pid, tid);"
- // since we have set no breakpoints, it will be a just in time debug of a panic / exception
- emit terminate();
+
+ if (d->crashlog) {
+ CrashState cs;
+ cs.pid = pid;
+ cs.tid = tid;
+ cs.crashPC = pc;
+ cs.crashReason = reason;
+
+ if (d->dyingThreads.contains(tid)) {
+ if(d->queuedCrashes.isEmpty())
+ emit terminate();
+ else
+ d->terminateNeeded = true;
+ } else {
+ d->queuedCrashes.append(cs);
+ d->dyingThreads.append(tid);
+
+ if (d->queuedCrashes.count() == 1) {
+ d->err << "Fetching registers and stack..." << endl;
+ emit getRegistersAndCallStack(pid, tid);
+ }
+ }
+ }
+ else
+ emit terminate();
+}
+
+void TrkSignalHandler::registersAndCallStackReadComplete(const QList<uint>& registers, const QByteArray& stack)
+{
+ CrashState cs = d->queuedCrashes.first();
+ QDir dir(d->crashlogPath);
+ d->crashlogtextfile.setFileName(dir.filePath(QString("d_exc_%1.txt").arg(cs.tid)));
+ d->crashstackfile.setFileName(dir.filePath(QString("d_exc_%1.stk").arg(cs.tid)));
+ d->crashlogtextfile.open(QIODevice::WriteOnly);
+ QTextStream crashlog(&d->crashlogtextfile);
+
+ crashlog << "-----------------------------------------------------------------------------" << endl;
+ crashlog << "EKA2 USER CRASH LOG" << endl;
+ crashlog << "Thread Name: " << QString("ProcessID-%1::ThreadID-%2").arg(cs.pid).arg(cs.tid) << endl;
+ crashlog << "Thread ID: " << cs.tid << endl;
+ //this is wrong, but TRK doesn't make stack limit available so we lie
+ crashlog << QString("User Stack %1-%2").arg(registers.at(13), 8, 16, QChar('0')).arg(registers.at(13) + stack.size(), 8, 16, QChar('0')) << endl;
+ //this is also wrong, but TRK doesn't give all information for exceptions
+ crashlog << QString("Panic: PC=%1 ").arg(cs.crashPC, 8, 16, QChar('0')) << cs.crashReason << endl;
+ crashlog << endl;
+ crashlog << "USER REGISTERS:" << endl;
+ crashlog << QString("CPSR=%1").arg(registers.at(16), 8, 16, QChar('0')) << endl;
+ for (int i=0;i<16;i+=4) {
+ crashlog << QString("r%1=%2 %3 %4 %5")
+ .arg(i, 2, 10, QChar('0'))
+ .arg(registers.at(i), 8, 16, QChar('0'))
+ .arg(registers.at(i+1), 8, 16, QChar('0'))
+ .arg(registers.at(i+2), 8, 16, QChar('0'))
+ .arg(registers.at(i+3), 8, 16, QChar('0')) << endl;
+ }
+ crashlog << endl;
+
+ //emit info for post mortem debug
+ qSort(d->libraries.begin(), d->libraries.end(), lessThanCodeBase);
+ d->err << "Code Segments:" << endl;
+ crashlog << "CODE SEGMENTS:" << endl;
+ for(int i=0; i<d->libraries.count(); i++) {
+ const trk::Library& seg = d->libraries.at(i);
+ if(seg.pid != cs.pid)
+ continue;
+ if (d->loglevel > 1) {
+ d->err << QString("Code: %1 Data: %2 Name: ")
+ .arg(seg.codeseg, 8, 16, QChar('0'))
+ .arg(seg.dataseg, 8, 16, QChar('0'))
+ << seg.name << endl;
+ }
+
+ //produce fake code segment end addresses since we don't get the real ones from TRK
+ uint end;
+ if (i+1 < d->libraries.count())
+ end = d->libraries.at(i+1).codeseg - 1;
+ else
+ end = 0xFFFFFFFF;
+
+ crashlog << QString("%1-%2 ")
+ .arg(seg.codeseg, 8, 16, QChar('0'))
+ .arg(end, 8, 16, QChar('0'))
+ << seg.name << endl;
+ }
+
+ d->crashlogtextfile.close();
+
+ if (d->loglevel > 1) {
+ d->err << "Registers:" << endl;
+ for (int i=0;i<16;i++) {
+ d->err << QString("R%1: %2 ").arg(i, 2, 10, QChar('0')).arg(registers.at(i), 8, 16, QChar('0'));
+ if (i % 4 == 3)
+ d->err << endl;
+ }
+ d->err << QString("CPSR: %1").arg(registers.at(16), 8, 16, QChar('0')) << endl;
+
+ d->err << "Stack:" << endl;
+ uint sp = registers.at(13);
+ for(int i=0; i<stack.size(); i+=16, sp+=16) {
+ d->err << QString("%1: ").arg(sp, 8, 16, QChar('0'));
+ d->err << trk::stringFromArray(stack.mid(i,16));
+ d->err << endl;
+ }
+ }
+ d->crashstackfile.open(QIODevice::WriteOnly);
+ d->crashstackfile.write(stack);
+ d->crashstackfile.close();
+
+ if (d->loglevel > 0)
+ d->err << "Crash logs saved to " << d->crashlogtextfile.fileName() << " & " << d->crashstackfile.fileName() << endl;
+
+ // resume the thread to allow Symbian OS to handle the panic normally.
+ // terminate when a non main thread is suspended reboots the phone (TRK bug)
+ emit resume(cs.pid, cs.tid);
+
+ //fetch next crashed thread
+ d->queuedCrashes.removeFirst();
+ if (d->queuedCrashes.count()) {
+ cs = d->queuedCrashes.first();
+ d->err << "Fetching registers and stack..." << endl;
+ emit getRegistersAndCallStack(cs.pid, cs.tid);
+ }
+ else if (d->terminateNeeded)
+ emit terminate();
+
+}
+
+void TrkSignalHandler::libraryLoaded(const trk::Library &lib)
+{
+ d->libraries << lib;
+}
+
+void TrkSignalHandler::libraryUnloaded(const trk::Library &lib)
+{
+ for (QList<trk::Library>::iterator i = d->libraries.begin(); i != d->libraries.end(); i++) {
+ if((*i).name == lib.name && (*i).pid == lib.pid)
+ i = d->libraries.erase(i);
+ }
}
void TrkSignalHandler::timeout()
@@ -174,7 +345,8 @@ TrkSignalHandlerPrivate::TrkSignalHandlerPrivate()
: out(stdout),
err(stderr),
loglevel(0),
- lastpercent(0)
+ lastpercent(0),
+ terminateNeeded(false)
{
}
diff --git a/tools/runonphone/trksignalhandler.h b/tools/runonphone/trksignalhandler.h
index d31e46f..bfe2c3e 100644
--- a/tools/runonphone/trksignalhandler.h
+++ b/tools/runonphone/trksignalhandler.h
@@ -43,6 +43,7 @@
#define TRKSIGNALHANDLER_H
#include <QObject>
#include <QString>
+#include "symbianutils/trkutils.h"
class TrkSignalHandlerPrivate;
class TrkSignalHandler : public QObject
@@ -66,13 +67,20 @@ public slots:
void stateChanged(int);
void stopped(uint pc, uint pid, uint tid, const QString& reason);
void timeout();
+ void libraryLoaded(const trk::Library &lib);
+ void libraryUnloaded(const trk::Library &lib);
+ void registersAndCallStackReadComplete(const QList<uint>& registers, const QByteArray& stack);
signals:
void resume(uint pid, uint tid);
+ void stop(uint pid, uint tid);
void terminate();
+ void getRegistersAndCallStack(uint pid, uint tid);
public:
TrkSignalHandler();
~TrkSignalHandler();
void setLogLevel(int);
+ void setCrashLogging(bool);
+ void setCrashLogPath(QString);
private:
TrkSignalHandlerPrivate *d;
};