Merge branch 'master' of git@scm.dev.nokia.troll.no:creator/mainline

This commit is contained in:
ck
2009-09-09 17:27:59 +02:00
35 changed files with 2551 additions and 1860 deletions
@@ -500,7 +500,7 @@ void CMakeProject::setUserEnvironmentChanges(const QString &buildConfig, const Q
QStringList list = EnvironmentItem::toStringList(diff);
if (list == value(buildConfig, "userEnvironmentChanges"))
return;
setValue(buildConfig, "userEnvironmentChanges", EnvironmentItem::toStringList(diff));
setValue(buildConfig, "userEnvironmentChanges", list);
emit environmentChanged(buildConfig);
}
+3 -97
View File
@@ -527,8 +527,6 @@ CPPEditorEditable::CPPEditorEditable(CPPEditor *editor)
CPPEditor::CPPEditor(QWidget *parent)
: TextEditor::BaseTextEditor(parent)
, m_mouseNavigationEnabled(true)
, m_showingLink(false)
, m_currentRenameSelection(-1)
, m_inRename(false)
{
@@ -1073,7 +1071,7 @@ void CPPEditor::switchDeclarationDefinition()
}
CPPEditor::Link CPPEditor::findLinkAt(const QTextCursor &cursor,
bool lookupDefinition)
bool resolveTarget)
{
Link link;
@@ -1153,7 +1151,7 @@ CPPEditor::Link CPPEditor::findLinkAt(const QTextCursor &cursor,
if (Symbol *symbol = result.second) {
Symbol *def = 0;
if (lookupDefinition && !lastSymbol->isFunction())
if (resolveTarget && !lastSymbol->isFunction())
def = findDefinition(symbol);
link = linkToSymbol(def ? def : symbol);
@@ -1190,7 +1188,7 @@ CPPEditor::Link CPPEditor::findLinkAt(const QTextCursor &cursor,
void CPPEditor::jumpToDefinition()
{
openCppEditorAt(findLinkAt(textCursor()));
openLink(findLinkAt(textCursor()));
}
Symbol *CPPEditor::findDefinition(Symbol *symbol)
@@ -1342,68 +1340,6 @@ void CPPEditor::contextMenuEvent(QContextMenuEvent *e)
delete menu;
}
void CPPEditor::mouseMoveEvent(QMouseEvent *e)
{
bool linkFound = false;
if (m_mouseNavigationEnabled && e->modifiers() & Qt::ControlModifier) {
// Link emulation behaviour for 'go to definition'
const QTextCursor cursor = cursorForPosition(e->pos());
// Check that the mouse was actually on the text somewhere
bool onText = cursorRect(cursor).right() >= e->x();
if (!onText) {
QTextCursor nextPos = cursor;
nextPos.movePosition(QTextCursor::Right);
onText = cursorRect(nextPos).right() >= e->x();
}
const Link link = findLinkAt(cursor, false);
if (onText && !link.fileName.isEmpty()) {
showLink(link);
linkFound = true;
}
}
if (!linkFound)
clearLink();
TextEditor::BaseTextEditor::mouseMoveEvent(e);
}
void CPPEditor::mouseReleaseEvent(QMouseEvent *e)
{
if (m_mouseNavigationEnabled && e->modifiers() & Qt::ControlModifier
&& !(e->modifiers() & Qt::ShiftModifier)
&& e->button() == Qt::LeftButton) {
const QTextCursor cursor = cursorForPosition(e->pos());
if (openCppEditorAt(findLinkAt(cursor))) {
clearLink();
e->accept();
return;
}
}
TextEditor::BaseTextEditor::mouseReleaseEvent(e);
}
void CPPEditor::leaveEvent(QEvent *e)
{
clearLink();
TextEditor::BaseTextEditor::leaveEvent(e);
}
void CPPEditor::keyReleaseEvent(QKeyEvent *e)
{
// Clear link emulation when Ctrl is released
if (e->key() == Qt::Key_Control)
clearLink();
TextEditor::BaseTextEditor::keyReleaseEvent(e);
}
void CPPEditor::keyPressEvent(QKeyEvent *e)
{
if (m_currentRenameSelection == -1) {
@@ -1491,29 +1427,6 @@ void CPPEditor::keyPressEvent(QKeyEvent *e)
TextEditor::BaseTextEditor::keyPressEvent(e);
}
void CPPEditor::showLink(const Link &link)
{
QTextEdit::ExtraSelection sel;
sel.cursor = textCursor();
sel.cursor.setPosition(link.pos);
sel.cursor.setPosition(link.pos + link.length, QTextCursor::KeepAnchor);
sel.format = m_linkFormat;
sel.format.setFontUnderline(true);
setExtraSelections(OtherSelection, QList<QTextEdit::ExtraSelection>() << sel);
viewport()->setCursor(Qt::PointingHandCursor);
m_showingLink = true;
}
void CPPEditor::clearLink()
{
if (!m_showingLink)
return;
setExtraSelections(OtherSelection, QList<QTextEdit::ExtraSelection>());
viewport()->setCursor(Qt::IBeamCursor);
m_showingLink = false;
}
QList<int> CPPEditorEditable::context() const
{
return m_context;
@@ -1557,17 +1470,10 @@ void CPPEditor::setFontSettings(const TextEditor::FontSettings &fs)
highlighter->setFormats(formats.constBegin(), formats.constEnd());
highlighter->rehighlight();
m_linkFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_LINK));
m_occurrencesFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_OCCURRENCES));
m_occurrenceRenameFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_OCCURRENCES_RENAME));
}
void CPPEditor::setDisplaySettings(const TextEditor::DisplaySettings &ds)
{
TextEditor::BaseTextEditor::setDisplaySettings(ds);
m_mouseNavigationEnabled = ds.m_mouseNavigation;
}
void CPPEditor::unCommentSelection()
{
Core::Utils::unCommentSelection(this);
+3 -33
View File
@@ -193,7 +193,6 @@ public:
public Q_SLOTS:
virtual void setFontSettings(const TextEditor::FontSettings &);
virtual void setDisplaySettings(const TextEditor::DisplaySettings &);
void setSortedMethodOverview(bool sort);
void switchDeclarationDefinition();
void jumpToDefinition();
@@ -208,10 +207,6 @@ public Q_SLOTS:
protected:
bool event(QEvent *e);
void contextMenuEvent(QContextMenuEvent *);
void mouseMoveEvent(QMouseEvent *);
void mouseReleaseEvent(QMouseEvent *);
void leaveEvent(QEvent *);
void keyReleaseEvent(QKeyEvent *);
void keyPressEvent(QKeyEvent *);
TextEditor::BaseTextEditorEditable *createEditableInterface();
@@ -261,36 +256,11 @@ private:
const QString &text = QString());
void abortRename();
struct Link
{
Link(const QString &fileName = QString(),
int line = 0,
int column = 0)
: pos(-1)
, length(-1)
, fileName(fileName)
, line(line)
, column(column)
{}
int pos; // Link position
int length; // Link length
QString fileName; // Target file
int line; // Target line
int column; // Target column
};
void showLink(const Link &);
void clearLink();
Link findLinkAt(const QTextCursor &, bool lookupDefinition = true);
static Link linkToSymbol(CPlusPlus::Symbol *symbol);
Link findLinkAt(const QTextCursor &, bool resolveTarget = true);
bool openLink(const Link &link) { return openCppEditorAt(link); }
bool openCppEditorAt(const Link &);
bool m_mouseNavigationEnabled;
bool m_showingLink;
QTextCharFormat m_linkFormat;
static Link linkToSymbol(CPlusPlus::Symbol *symbol);
CppTools::CppModelManagerInterface *m_modelManager;
+3 -1
View File
@@ -73,6 +73,8 @@ void CppHighlighter::highlightBlock(const QString &text)
userData->setCollapseMode(TextBlockUserData::NoCollapse);
}
TextEditDocumentLayout::clearParentheses(currentBlock());
if (text.length()) // the empty line can still contain whitespace
setFormat(0, text.length(), visualSpaceFormat);
return;
}
@@ -171,7 +173,7 @@ void CppHighlighter::highlightBlock(const QString &text)
}
// mark the trailing white spaces
if (! tokens.isEmpty()) {
{
const SimpleToken tk = tokens.last();
const int lastTokenEnd = tk.position() + tk.length();
if (text.length() > lastTokenEnd)
+2
View File
@@ -87,5 +87,7 @@ include(cdb/cdb.pri)
include(gdb/gdb.pri)
include(script/script.pri)
include(tcf/tcf.pri)
include(symbian/symbian.pri)
include(shared/shared.pri)
OTHER_FILES += Debugger.pluginspec
+2
View File
@@ -103,6 +103,8 @@ namespace Internal {
IDebuggerEngine *createGdbEngine(DebuggerManager *parent, QList<Core::IOptionsPage*> *);
IDebuggerEngine *createSymbianEngine(DebuggerManager *parent, QList<Core::IOptionsPage*> *);
QDebug operator<<(QDebug str, const DebuggerStartParameters &p)
{
QDebug nospace = str.nospace();
+39 -37
View File
@@ -144,7 +144,7 @@ static QByteArray parsePlainConsoleStream(const GdbResultRecord &record)
//
///////////////////////////////////////////////////////////////////////
GdbEngine::GdbEngine(DebuggerManager *parent) :
GdbEngine::GdbEngine(DebuggerManager *parent, GdbProcessBase *gdbProc) :
#ifdef Q_OS_WIN // Do injection loading with MinGW (call loading does not work with 64bit)
m_dumperInjectionLoad(true),
#else
@@ -153,6 +153,7 @@ GdbEngine::GdbEngine(DebuggerManager *parent) :
q(parent),
qq(parent->engineInterface())
{
m_gdbProc = gdbProc;
m_stubProc.setMode(Core::Utils::ConsoleProcess::Debug);
#ifdef Q_OS_UNIX
m_stubProc.setSettings(Core::ICore::instance()->settings());
@@ -164,19 +165,20 @@ GdbEngine::GdbEngine(DebuggerManager *parent) :
GdbEngine::~GdbEngine()
{
// prevent sending error messages afterwards
m_gdbProc.disconnect(this);
m_gdbProc->disconnect(this);
delete m_gdbProc;
}
void GdbEngine::initializeConnections()
{
// Gdb Process interaction
connect(&m_gdbProc, SIGNAL(error(QProcess::ProcessError)),
connect(m_gdbProc, SIGNAL(error(QProcess::ProcessError)),
this, SLOT(gdbProcError(QProcess::ProcessError)));
connect(&m_gdbProc, SIGNAL(readyReadStandardOutput()),
connect(m_gdbProc, SIGNAL(readyReadStandardOutput()),
this, SLOT(readGdbStandardOutput()));
connect(&m_gdbProc, SIGNAL(readyReadStandardError()),
connect(m_gdbProc, SIGNAL(readyReadStandardError()),
this, SLOT(readGdbStandardError()));
connect(&m_gdbProc, SIGNAL(finished(int, QProcess::ExitStatus)),
connect(m_gdbProc, SIGNAL(finished(int, QProcess::ExitStatus)),
q, SLOT(exitDebugger()));
connect(&m_stubProc, SIGNAL(processError(QString)),
@@ -614,7 +616,7 @@ void GdbEngine::stubError(const QString &msg)
void GdbEngine::readGdbStandardError()
{
qWarning() << "Unexpected gdb stderr:" << m_gdbProc.readAllStandardError();
qWarning() << "Unexpected gdb stderr:" << m_gdbProc->readAllStandardError();
}
void GdbEngine::readGdbStandardOutput()
@@ -622,7 +624,7 @@ void GdbEngine::readGdbStandardOutput()
int newstart = 0;
int scan = m_inbuffer.size();
m_inbuffer.append(m_gdbProc.readAllStandardOutput());
m_inbuffer.append(m_gdbProc->readAllStandardOutput());
while (newstart < m_inbuffer.size()) {
int start = newstart;
@@ -651,7 +653,7 @@ void GdbEngine::interruptInferior()
{
qq->notifyInferiorStopRequested();
if (m_gdbProc.state() == QProcess::NotRunning) {
if (m_gdbProc->state() == QProcess::NotRunning) {
debugMessage(_("TRYING TO INTERRUPT INFERIOR WITHOUT RUNNING GDB"));
qq->notifyInferiorExited();
return;
@@ -698,7 +700,7 @@ void GdbEngine::postCommand(const QString &command, GdbCommandFlags flags,
GdbCommandCallback callback, const char *callbackName,
const QVariant &cookie)
{
if (m_gdbProc.state() == QProcess::NotRunning) {
if (m_gdbProc->state() == QProcess::NotRunning) {
debugMessage(_("NO GDB PROCESS RUNNING, CMD IGNORED: ") + command);
return;
}
@@ -742,7 +744,7 @@ void GdbEngine::flushCommand(GdbCommand &cmd)
if (cmd.flags & EmbedToken)
cmd.command = cmd.command.arg(currentToken());
m_gdbProc.write(cmd.command.toLatin1() + "\r\n");
m_gdbProc->write(cmd.command.toLatin1() + "\r\n");
//emit gdbInputAvailable(QString(), " " + currentTime());
//emit gdbInputAvailable(QString(), "[" + currentTime() + "] " + cmd.command);
emit gdbInputAvailable(LogInput, cmd.command);
@@ -829,12 +831,12 @@ void GdbEngine::handleResultRecord(const GdbResultRecord &record)
void GdbEngine::executeDebuggerCommand(const QString &command)
{
if (m_gdbProc.state() == QProcess::NotRunning) {
if (m_gdbProc->state() == QProcess::NotRunning) {
debugMessage(_("NO GDB PROCESS RUNNING, PLAIN CMD IGNORED: ") + command);
return;
}
m_gdbProc.write(command.toLocal8Bit() + "\r\n");
m_gdbProc->write(command.toLocal8Bit() + "\r\n");
}
void GdbEngine::handleTargetCore(const GdbResultRecord &, const QVariant &)
@@ -1434,15 +1436,15 @@ void GdbEngine::detachDebugger()
void GdbEngine::exitDebugger()
{
debugMessage(_("GDBENGINE EXITDEBUGGER: %1").arg(m_gdbProc.state()));
if (m_gdbProc.state() == QProcess::Starting) {
debugMessage(_("GDBENGINE EXITDEBUGGER: %1").arg(m_gdbProc->state()));
if (m_gdbProc->state() == QProcess::Starting) {
debugMessage(_("WAITING FOR GDB STARTUP TO SHUTDOWN: %1")
.arg(m_gdbProc.state()));
m_gdbProc.waitForStarted();
.arg(m_gdbProc->state()));
m_gdbProc->waitForStarted();
}
if (m_gdbProc.state() == QProcess::Running) {
if (m_gdbProc->state() == QProcess::Running) {
debugMessage(_("WAITING FOR RUNNING GDB TO SHUTDOWN: %1")
.arg(m_gdbProc.state()));
.arg(m_gdbProc->state()));
if (q->status() != DebuggerInferiorStopped
&& q->status() != DebuggerProcessStartingUp) {
QTC_ASSERT(q->status() == DebuggerInferiorRunning,
@@ -1455,17 +1457,17 @@ void GdbEngine::exitDebugger()
postCommand(_("kill"));
postCommand(_("-gdb-exit"), CB(handleExit));
// 20s can easily happen when loading webkit debug information
if (!m_gdbProc.waitForFinished(20000)) {
if (!m_gdbProc->waitForFinished(20000)) {
debugMessage(_("FORCING TERMINATION: %1")
.arg(m_gdbProc.state()));
m_gdbProc.terminate();
m_gdbProc.waitForFinished(20000);
.arg(m_gdbProc->state()));
m_gdbProc->terminate();
m_gdbProc->waitForFinished(20000);
}
}
if (m_gdbProc.state() != QProcess::NotRunning) {
if (m_gdbProc->state() != QProcess::NotRunning) {
debugMessage(_("PROBLEM STOPPING DEBUGGER: STATE %1")
.arg(m_gdbProc.state()));
m_gdbProc.kill();
.arg(m_gdbProc->state()));
m_gdbProc->kill();
}
m_outputCollector.shutdown();
@@ -1487,9 +1489,9 @@ bool GdbEngine::startDebugger(const QSharedPointer<DebuggerStartParameters> &sp)
QStringList gdbArgs;
if (m_gdbProc.state() != QProcess::NotRunning) {
debugMessage(_("GDB IS ALREADY RUNNING, STATE: %1").arg(m_gdbProc.state()));
m_gdbProc.kill();
if (m_gdbProc->state() != QProcess::NotRunning) {
debugMessage(_("GDB IS ALREADY RUNNING, STATE: %1").arg(m_gdbProc->state()));
m_gdbProc->kill();
return false;
}
@@ -1529,16 +1531,16 @@ bool GdbEngine::startDebugger(const QSharedPointer<DebuggerStartParameters> &sp)
gdbArgs.prepend(_("--tty=") + m_outputCollector.serverName());
if (!sp->workingDir.isEmpty())
m_gdbProc.setWorkingDirectory(sp->workingDir);
m_gdbProc->setWorkingDirectory(sp->workingDir);
if (!sp->environment.isEmpty())
m_gdbProc.setEnvironment(sp->environment);
m_gdbProc->setEnvironment(sp->environment);
}
#if 0
qDebug() << "Command:" << q->settings()->m_gdbCmd;
qDebug() << "WorkingDirectory:" << m_gdbProc.workingDirectory();
qDebug() << "WorkingDirectory:" << m_gdbProc->workingDirectory();
qDebug() << "ScriptFile:" << q->settings()->m_scriptFile;
qDebug() << "Environment:" << m_gdbProc.environment();
qDebug() << "Environment:" << m_gdbProc->environment();
qDebug() << "Arguments:" << gdbArgs;
qDebug() << "BuildDir:" << sp->buildDir;
qDebug() << "ExeFile:" << sp->executable;
@@ -1546,10 +1548,10 @@ bool GdbEngine::startDebugger(const QSharedPointer<DebuggerStartParameters> &sp)
QString loc = theDebuggerStringSetting(GdbLocation);
q->showStatusMessage(tr("Starting Debugger: ") + loc + _c(' ') + gdbArgs.join(_(" ")));
m_gdbProc.start(loc, gdbArgs);
if (!m_gdbProc.waitForStarted()) {
m_gdbProc->start(loc, gdbArgs);
if (!m_gdbProc->waitForStarted()) {
QMessageBox::critical(q->mainWindow(), tr("Debugger Startup Failure"),
tr("Cannot start debugger: %1").arg(m_gdbProc.errorString()));
tr("Cannot start debugger: %1").arg(m_gdbProc->errorString()));
m_outputCollector.shutdown();
m_stubProc.blockSignals(true);
m_stubProc.stop();
@@ -4198,7 +4200,7 @@ void GdbEngine::handleFetchDisassemblerByAddress0(const GdbResultRecord &record,
IDebuggerEngine *createGdbEngine(DebuggerManager *parent, QList<Core::IOptionsPage*> *opts)
{
opts->push_back(new GdbOptionsPage);
return new GdbEngine(parent);
return new GdbEngine(parent, new GdbProcess);
}
} // namespace Internal
+37 -2
View File
@@ -32,6 +32,7 @@
#include "idebuggerengine.h"
#include "gdbmi.h"
#include "gdbprocessbase.h"
#include "outputcollector.h"
#include "watchutils.h"
@@ -56,6 +57,7 @@ QT_END_NAMESPACE
namespace Debugger {
namespace Internal {
class DebuggerManager;
class IDebuggerManagerAccessForEngines;
class GdbResultRecord;
@@ -72,13 +74,46 @@ enum DebuggingHelperState
DebuggingHelperUnavailable,
};
class GdbProcess : public GdbProcessBase
{
public:
GdbProcess(QObject *parent = 0)
: GdbProcessBase(parent)
{
connect(&m_proc, SIGNAL(error(QProcess::ProcessError)),
this, SIGNAL(error(QProcess::ProcessError)));
connect(&m_proc, SIGNAL(readyReadStandardOutput()),
this, SIGNAL(readyReadStandardOutput()));
connect(&m_proc, SIGNAL(readyReadStandardError()),
this, SIGNAL(readyReadStandardError()));
connect(&m_proc, SIGNAL(finished(int, QProcess::ExitStatus)),
this, SIGNAL(finished(int, QProcess::ExitStatus)));
}
void start(const QString &program, const QStringList &args,
QIODevice::OpenMode mode) { m_proc.start(program, args, mode); }
void kill() { m_proc.kill(); }
void terminate() { m_proc.terminate(); }
bool waitForStarted(int msecs) { return m_proc.waitForStarted(msecs); }
bool waitForFinished(int msecs) { return m_proc.waitForFinished(msecs); }
QProcess::ProcessState state() const { return m_proc.state(); }
QString errorString() const { return m_proc.errorString(); }
QByteArray readAllStandardError() { return m_proc.readAllStandardError(); }
QByteArray readAllStandardOutput() { return m_proc.readAllStandardOutput(); }
qint64 write(const char *data) { return m_proc.write(data); }
void setWorkingDirectory(const QString &dir) { m_proc.setWorkingDirectory(dir); }
void setEnvironment(const QStringList &env) { m_proc.setEnvironment(env); }
private:
QProcess m_proc;
};
class GdbEngine : public IDebuggerEngine
{
Q_OBJECT
public:
GdbEngine(DebuggerManager *parent);
GdbEngine(DebuggerManager *parent, GdbProcessBase *gdbProc);
~GdbEngine();
signals:
@@ -251,7 +286,7 @@ private:
QByteArray m_inbuffer;
QProcess m_gdbProc;
GdbProcessBase *m_gdbProc;
QProcess m_uploadProc;
Core::Utils::ConsoleProcess m_stubProc;
+74
View File
@@ -0,0 +1,74 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this 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.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#ifndef DEBUGGER_PROCESSBASE_H
#define DEBUGGER_PROCESSBASE_H
#include <QtCore/QObject>
#include <QtCore/QProcess>
namespace Debugger {
namespace Internal {
// GdbProcessBase is inherited by GdbProcess and the gdb/trk Adapter.
// In the GdbProcess case it's just a wrapper around a QProcess running
// gdb, in the Adapter case it's the interface to the gdb process in
// the whole rfomm/gdb/gdbserver combo.
class GdbProcessBase : public QObject
{
Q_OBJECT
public:
GdbProcessBase(QObject *parent = 0) : QObject(parent) {}
virtual void start(const QString &program, const QStringList &args,
QIODevice::OpenMode mode = QIODevice::ReadWrite) = 0;
virtual void kill() = 0;
virtual void terminate() = 0;
virtual bool waitForStarted(int msecs = 30000) = 0;
virtual bool waitForFinished(int msecs = 30000) = 0;
virtual QProcess::ProcessState state() const = 0;
virtual QString errorString() const = 0;
virtual QByteArray readAllStandardError() = 0;
virtual QByteArray readAllStandardOutput() = 0;
virtual qint64 write(const char *data) = 0;
virtual void setWorkingDirectory(const QString &dir) = 0;
virtual void setEnvironment(const QStringList &env) = 0;
signals:
void error(QProcess::ProcessError);
void readyReadStandardOutput();
void readyReadStandardError();
void finished(int, QProcess::ExitStatus);
};
} // namespace Internal
} // namespace Debugger
#endif // DEBUGGER_PROCESSBASE_H
+14
View File
@@ -0,0 +1,14 @@
HEADERS += \
$$PWD/trkclient.h \
$$PWD/symbianadapter.h \
#$$PWD/gdboptionspage.h \
SOURCES += \
$$PWD/trkclient.cpp \
$$PWD/symbianadapter.cpp \
$$PWD/symbianengine.cpp \
#$$PWD/gdboptionspage.cpp \
#FORMS += $$PWD/gdboptionspage.ui
#RESOURCES += $$PWD/gdb.qrc
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,247 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this 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.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#ifndef DEBUGGER_SYMBIANADAPTER_H
#define DEBUGGER_SYMBIANADAPTER_H
#include "trkutils.h"
#include "trkclient.h"
#include "../gdb/gdbprocessbase.h"
#include <QtCore/QDebug>
#include <QtCore/QDir>
#include <QtCore/QFile>
#include <QtCore/QHash>
#include <QtCore/QPointer>
#include <QtCore/QProcess>
#include <QtCore/QQueue>
#include <QtCore/QString>
#include <QtCore/QStringList>
#include <QtCore/QTextStream>
#include <QtCore/QTimer>
#include <QtGui/QAction>
#include <QtGui/QApplication>
#include <QtGui/QMainWindow>
#include <QtGui/QKeyEvent>
#include <QtGui/QTextBlock>
#include <QtGui/QTextEdit>
#include <QtGui/QToolBar>
#include <QtNetwork/QTcpServer>
#include <QtNetwork/QTcpSocket>
#include <QtNetwork/QLocalServer>
#include <QtNetwork/QLocalSocket>
namespace Debugger {
namespace Internal {
struct GdbResult
{
QByteArray data;
};
///////////////////////////////////////////////////////////////////////
//
// SymbianAdapter
//
///////////////////////////////////////////////////////////////////////
class SymbianAdapter : public GdbProcessBase
{
Q_OBJECT
public:
typedef trk::TrkResult TrkResult;
typedef trk::TrkFunctor1<const TrkResult &> TrkCallback;
typedef trk::TrkFunctor1<const GdbResult &> GdbCallback;
SymbianAdapter();
~SymbianAdapter();
void setGdbServerName(const QString &name);
QString gdbServerIP() const;
uint gdbServerPort() const;
void setVerbose(int verbose) { m_verbose = verbose; }
void setSerialFrame(bool b) { m_serialFrame = b; }
void setBufferedMemoryRead(bool b) { m_bufferedMemoryRead = b; }
public slots:
void startInferior();
signals:
void output(const QString &senderName, const QString &data);
private slots:
void handleProcError(QProcess::ProcessError error);
void handleProcFinished(int exitCode, QProcess::ExitStatus exitStatus);
void handleProcStarted();
void handleProcStateChanged(QProcess::ProcessState newState);
void run();
void startGdb();
private:
friend class RunnerGui;
void connectProcess(QProcess *proc);
void sendOutput(QObject *sender, const QString &data);
void sendOutput(const QString &data) { sendOutput(0, data); }
QString m_rfcommDevice; // /dev/rfcomm0
QString m_gdbServerName; // 127.0.0.1:(2222+uid)
QProcess m_gdbProc;
QProcess m_rfcommProc;
bool m_running;
public:
//
// Implementation of GdbProcessBase
//
void start(const QString &program, const QStringList &args,
QIODevice::OpenMode mode = QIODevice::ReadWrite);
void kill();
void terminate();
bool waitForStarted(int msecs = 30000);
bool waitForFinished(int msecs = 30000);
QProcess::ProcessState state() const;
QString errorString() const;
QByteArray readAllStandardError();
QByteArray readAllStandardOutput();
qint64 write(const char *data);
void setWorkingDirectory(const QString &dir);
void setEnvironment(const QStringList &env);
//
// TRK
//
void sendTrkMessage(byte code,
TrkCallback callback = TrkCallback(),
const QByteArray &data = QByteArray(),
const QVariant &cookie = QVariant());
Q_SLOT void handleTrkResult(const trk::TrkResult &data);
Q_SLOT void handleTrkError(const QString &msg);
// convenience messages
void sendTrkAck(byte token);
void handleCpuType(const TrkResult &result);
void handleCreateProcess(const TrkResult &result);
void handleClearBreakpoint(const TrkResult &result);
void handleSignalContinue(const TrkResult &result);
void handleStop(const TrkResult &result);
void handleSupportMask(const TrkResult &result);
void handleTrkVersions(const TrkResult &result);
void handleDisconnect(const TrkResult &result);
void handleAndReportCreateProcess(const TrkResult &result);
void handleAndReportReadRegisters(const TrkResult &result);
QByteArray memoryReadLogMessage(uint addr, uint len, const QByteArray &ba) const;
QByteArray trkContinueMessage();
QByteArray trkReadRegisterMessage();
QByteArray trkReadMemoryMessage(uint addr, uint len);
QByteArray trkBreakpointMessage(uint addr, uint len, bool armMode = true);
void handleAndReportSetBreakpoint(const TrkResult &result);
void handleReadMemoryBuffered(const TrkResult &result);
void handleReadMemoryUnbuffered(const TrkResult &result);
void handleStepRange(const TrkResult &result);
void handleReadRegisters(const TrkResult &result);
void reportReadMemoryBuffered(const TrkResult &result);
void reportToGdb(const TrkResult &result);
// set breakpoints behind gdb's back
void handleSetTrkBreakpoint(const TrkResult &result);
void handleSetTrkMainBreakpoint(const TrkResult &result);
void readMemory(uint addr, uint len);
void interruptInferior();
trk::TrkDevice m_trkDevice;
//
// Gdb
//
struct GdbCommand
{
GdbCommand() : flags(0), callback(GdbCallback()), callbackName(0) {}
int flags;
GdbCallback callback;
const char *callbackName;
QString command;
QVariant cookie;
//QTime postTime;
};
void sendGdbMessage(const QString &msg,
GdbCallback callback = GdbCallback(),
const QVariant &cookie = QVariant());
Q_SLOT void handleGdbConnection();
Q_SLOT void readGdbServerCommand();
void readGdbResponse();
void handleGdbServerCommand(const QByteArray &cmd);
void sendGdbServerMessage(const QByteArray &msg,
const QByteArray &logNote = QByteArray());
void sendGdbServerMessageAfterTrkResponse(const QByteArray &msg,
const QByteArray &logNote = QByteArray());
void sendGdbServerAck();
bool sendGdbServerPacket(const QByteArray &packet, bool doFlush);
Q_SLOT void handleGdbReadyReadStandardError();
Q_SLOT void handleGdbReadyReadStandardOutput();
void logMessage(const QString &msg, bool force = false);
Q_SLOT void trkLogMessage(const QString &msg);
void handleInfoAddress(const GdbResult &result);
void handleInfoMainAddress(const GdbResult &result);
QTcpServer m_gdbServer;
QPointer<QTcpSocket> m_gdbConnection;
QByteArray m_gdbReadBuffer;
bool m_gdbAckMode;
QHash<int, GdbCommand> m_gdbCookieForToken;
//
// Rfcomm
//
Q_SLOT void handleRfcommReadyReadStandardError();
Q_SLOT void handleRfcommReadyReadStandardOutput();
// Debuggee state
Q_SLOT void executeCommand(const QString &msg);
trk::Session m_session; // global-ish data (process id, target information)
trk::Snapshot m_snapshot; // local-ish data (memory and registers)
int m_verbose;
bool m_serialFrame;
bool m_bufferedMemoryRead;
};
} // namespace Internal
} // namespace Debugger
#endif // DEBUGGER_SYMBIANADAPTER_H
@@ -0,0 +1,66 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this 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.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#define QT_NO_CAST_FROM_ASCII
#include "gdb/gdbengine.h"
#include "symbianadapter.h"
//#include "debuggerdialogs.h"
#include <utils/qtcassert.h>
#include <texteditor/itexteditor.h>
#include <coreplugin/icore.h>
#include <coreplugin/dialogs/ioptionspage.h>
#include <QtCore/QDebug>
#include <QtCore/QDir>
#include <QtCore/QFileInfo>
#include <QtCore/QMetaObject>
#include <QtCore/QTime>
#include <QtCore/QTimer>
#include <QtCore/QTextStream>
namespace Debugger {
namespace Internal {
IDebuggerEngine *createSymbianEngine(DebuggerManager *parent,
QList<Core::IOptionsPage*> *opts)
{
Q_UNUSED(opts);
//opts->push_back(new GdbOptionsPage);
return new GdbEngine(parent, new SymbianAdapter);
}
} // namespace Internal
} // namespace Debugger
+555
View File
@@ -0,0 +1,555 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this 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.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#include "trkclient.h"
#include "trkutils.h"
#include <QtCore/QString>
#include <QtCore/QDebug>
#include <QtCore/QQueue>
#include <QtCore/QHash>
#include <QtCore/QMap>
#ifdef Q_OS_WIN
# include <windows.h>
#else
# include <QtCore/QFile>
# include <stdio.h>
# include <sys/ioctl.h>
# include <termios.h>
# include <errno.h>
# include <string.h>
#endif
enum { TimerInterval = 100 };
#ifdef Q_OS_WIN
// Format windows error from GetLastError() value: TODO: Use the one provided by the utisl lib.
QString winErrorMessage(unsigned long error)
{
QString rc = QString::fromLatin1("#%1: ").arg(error);
ushort *lpMsgBuf;
const int len = FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, error, 0, (LPTSTR)&lpMsgBuf, 0, NULL);
if (len) {
rc = QString::fromUtf16(lpMsgBuf, len);
LocalFree(lpMsgBuf);
} else {
rc += QString::fromLatin1("<unknown error>");
}
return rc;
}
// Non-blocking replacement for win-api ReadFile function
BOOL WINAPI TryReadFile(HANDLE hFile,
LPVOID lpBuffer,
DWORD nNumberOfBytesToRead,
LPDWORD lpNumberOfBytesRead,
LPOVERLAPPED lpOverlapped)
{
COMSTAT comStat;
if (!ClearCommError(hFile, NULL, &comStat)){
logMessage("ClearCommError() failed");
return FALSE;
}
if (comStat.cbInQue == 0) {
*lpNumberOfBytesRead = 0;
return FALSE;
}
return ReadFile(hFile,
lpBuffer,
qMin(comStat.cbInQue, nNumberOfBytesToRead),
lpNumberOfBytesRead,
lpOverlapped);
}
#endif
namespace trk {
///////////////////////////////////////////////////////////////////////
//
// TrkMessage
//
///////////////////////////////////////////////////////////////////////
/* A message to be send to TRK, triggering a callback on receipt
* of the answer. */
struct TrkMessage
{
typedef TrkFunctor1<const TrkResult &> Callback;
explicit TrkMessage(byte code = 0u, byte token = 0u,
Callback callback = Callback());
byte code;
byte token;
QByteArray data;
QVariant cookie;
Callback callback;
};
TrkMessage::TrkMessage(byte c, byte t, Callback cb) :
code(c),
token(t),
callback(cb)
{
}
///////////////////////////////////////////////////////////////////////
//
// TrkWriteQueue
//
///////////////////////////////////////////////////////////////////////
/* Mixin class that manages a write queue of Trk messages. */
class TrkWriteQueue
{
public:
typedef TrkDevice::Callback Callback;
TrkWriteQueue();
// Enqueue messages.
void queueTrkMessage(byte code, Callback callback,
const QByteArray &data, const QVariant &cookie);
void queueTrkInitialPing();
// Call this from the device read notification with the results.
void slotHandleResult(const TrkResult &result);
// This can be called periodically in a timer to retrieve
// the pending messages to be sent.
bool pendingMessage(TrkMessage *message);
// Notify the queue about the success of the write operation
// after taking the pendingMessage off.
void notifyWriteResult(bool ok);
private:
typedef QMap<byte, TrkMessage> TokenMessageMap;
byte nextTrkWriteToken();
byte trkWriteToken;
QQueue<TrkMessage> trkWriteQueue;
TokenMessageMap writtenTrkMessages;
bool trkWriteBusy;
};
TrkWriteQueue::TrkWriteQueue() :
trkWriteToken(0),
trkWriteBusy(false)
{
}
byte TrkWriteQueue::nextTrkWriteToken()
{
++trkWriteToken;
if (trkWriteToken == 0)
++trkWriteToken;
return trkWriteToken;
}
void TrkWriteQueue::queueTrkMessage(byte code, Callback callback,
const QByteArray &data, const QVariant &cookie)
{
const byte token = code == TRK_WRITE_QUEUE_NOOP_CODE ?
byte(0) : nextTrkWriteToken();
TrkMessage msg(code, token, callback);
msg.data = data;
msg.cookie = cookie;
trkWriteQueue.append(msg);
}
bool TrkWriteQueue::pendingMessage(TrkMessage *message)
{
// Invoked from timer, try to flush out message queue
if (trkWriteBusy || trkWriteQueue.isEmpty())
return false;
// Handle the noop message, just invoke CB
if (trkWriteQueue.front().code == TRK_WRITE_QUEUE_NOOP_CODE) {
TrkMessage noopMessage = trkWriteQueue.dequeue();
if (noopMessage.callback) {
TrkResult result;
result.code = noopMessage.code;
result.token = noopMessage.token;
result.data = noopMessage.data;
result.cookie = noopMessage.cookie;
noopMessage.callback(result);
}
}
// Check again for real messages
if (trkWriteQueue.isEmpty())
return false;
if (message)
*message = trkWriteQueue.front();
return true;
}
void TrkWriteQueue::notifyWriteResult(bool ok)
{
// On success, dequeue message and await result
if (ok) {
TrkMessage firstMsg = trkWriteQueue.dequeue();
writtenTrkMessages.insert(firstMsg.token, firstMsg);
trkWriteBusy = true;
}
}
void TrkWriteQueue::slotHandleResult(const TrkResult &result)
{
trkWriteBusy = false;
//if (result.code != TrkNotifyAck && result.code != TrkNotifyNak)
// return;
// Find which request the message belongs to and invoke callback
// if ACK or on NAK if desired.
const TokenMessageMap::iterator it = writtenTrkMessages.find(result.token);
if (it == writtenTrkMessages.end())
return;
const bool invokeCB = it.value().callback;
if (invokeCB) {
TrkResult result1 = result;
result1.cookie = it.value().cookie;
it.value().callback(result1);
}
writtenTrkMessages.erase(it);
}
void TrkWriteQueue::queueTrkInitialPing()
{
// Ping, reset sequence count
trkWriteQueue.append(TrkMessage(0, 0));
}
///////////////////////////////////////////////////////////////////////
//
// TrkDevicePrivate
//
///////////////////////////////////////////////////////////////////////
struct TrkDevicePrivate
{
TrkDevicePrivate();
TrkWriteQueue queue;
#ifdef Q_OS_WIN
HANDLE hdevice;
#else
QFile file;
#endif
QByteArray trkReadBuffer;
bool trkWriteBusy;
int timerId;
bool serialFrame;
bool verbose;
QString errorString;
};
///////////////////////////////////////////////////////////////////////
//
// TrkDevice
//
///////////////////////////////////////////////////////////////////////
TrkDevicePrivate::TrkDevicePrivate() :
#ifdef Q_OS_WIN
hdevice(INVALID_HANDLE_VALUE),
#endif
trkWriteBusy(false),
timerId(-1),
serialFrame(true),
verbose(false)
{
}
///////////////////////////////////////////////////////////////////////
//
// TrkDevice
//
///////////////////////////////////////////////////////////////////////
TrkDevice::TrkDevice(QObject *parent) :
QObject(parent),
d(new TrkDevicePrivate)
{}
TrkDevice::~TrkDevice()
{
close();
delete d;
}
bool TrkDevice::open(const QString &port, QString *errorMessage)
{
close();
#ifdef Q_OS_WIN
d->hdevice = CreateFile(port.toStdWString().c_str(),
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (INVALID_HANDLE_VALUE == d->hdevice) {
*errorMessage = QString::fromLatin1("Could not open device '%1': %2").arg(port, winErrorMessage(GetLastError()));
return false;
}
d->timerId = startTimer(TimerInterval);
return true;
#else
d->file.setFileName(port);
if (!d->file.open(QIODevice::ReadWrite|QIODevice::Unbuffered)) {
*errorMessage = QString::fromLatin1("Cannot open %1: %2").arg(port, d->file.errorString());
return false;
}
struct termios termInfo;
if (tcgetattr(d->file.handle(), &termInfo) < 0) {
*errorMessage = QString::fromLatin1("Unable to retrieve terminal settings: %1 %2").arg(errno).arg(QString::fromAscii(strerror(errno)));
return false;
}
// Turn off terminal echo as not get messages back, among other things
termInfo.c_cflag |= CREAD|CLOCAL;
termInfo.c_lflag &= (~(ICANON|ECHO|ECHOE|ECHOK|ECHONL|ISIG));
termInfo.c_iflag &= (~(INPCK|IGNPAR|PARMRK|ISTRIP|ICRNL|IXANY));
termInfo.c_oflag &= (~OPOST);
termInfo.c_cc[VMIN] = 0;
termInfo.c_cc[VINTR] = _POSIX_VDISABLE;
termInfo.c_cc[VQUIT] = _POSIX_VDISABLE;
termInfo.c_cc[VSTART] = _POSIX_VDISABLE;
termInfo.c_cc[VSTOP] = _POSIX_VDISABLE;
termInfo.c_cc[VSUSP] = _POSIX_VDISABLE;
if (tcsetattr(d->file.handle(), TCSAFLUSH, &termInfo) < 0) {
*errorMessage = QString::fromLatin1("Unable to apply terminal settings: %1 %2").arg(errno).arg(QString::fromAscii(strerror(errno)));
return false;
}
d->timerId = startTimer(TimerInterval);
return true;
#endif
}
void TrkDevice::close()
{
if (!isOpen())
return;
if (d->timerId != -1) {
killTimer(d->timerId);
d->timerId = -1;
}
#ifdef Q_OS_WIN
CloseHandle(d->hdevice);
d->hdevice = INVALID_HANDLE_VALUE;
#else
d->file.close();
#endif
if (verbose())
logMessage("Close");
}
bool TrkDevice::isOpen() const
{
#ifdef Q_OS_WIN
return d->hdevice != INVALID_HANDLE_VALUE;
#else
return d->file.isOpen();
#endif
}
QString TrkDevice::errorString() const
{
return d->errorString;
}
bool TrkDevice::serialFrame() const
{
return d->serialFrame;
}
void TrkDevice::setSerialFrame(bool f)
{
d->serialFrame = f;
}
bool TrkDevice::verbose() const
{
return true || d->verbose;
}
void TrkDevice::setVerbose(bool b)
{
d->verbose = b;
}
bool TrkDevice::write(const QByteArray &data, QString *errorMessage)
{
#ifdef Q_OS_WIN
DWORD charsWritten;
if (!WriteFile(d->hdevice, data.data(), data.size(), &charsWritten, NULL)) {
*errorMessage = QString::fromLatin1("Error writing data: %1").arg(winErrorMessage(GetLastError()));
return false;
}
FlushFileBuffers(d->hdevice);
return true;
#else
if (d->file.write(data) == -1 || !d->file.flush()) {
*errorMessage = QString::fromLatin1("Cannot write: %1").arg(d->file.errorString());
return false;
}
return true;
#endif
}
#ifndef Q_OS_WIN
static inline int bytesAvailable(int fileNo)
{
int numBytes;
const int rc = ioctl(fileNo, FIONREAD, &numBytes);
if (rc < 0)
numBytes=0;
return numBytes;
}
#endif
void TrkDevice::tryTrkRead()
{
#ifdef Q_OS_WIN
const DWORD BUFFERSIZE = 1024;
char buffer[BUFFERSIZE];
DWORD charsRead;
DWORD totalCharsRead = 0;
while (TryReadFile(d->hdevice, buffer, BUFFERSIZE, &charsRead, NULL)) {
totalCharsRead += charsRead;
d->trkReadBuffer.append(buffer, charsRead);
if (isValidTrkResult(d->trkReadBuffer, d->serialFrame))
break;
}
if (verbose() && totalCharsRead)
logMessage("Read" + d->trkReadBuffer.toHex());
if (!totalCharsRead)
return;
const ushort len = isValidTrkResult(d->trkReadBuffer, d->serialFrame);
if (!len) {
const QString msg = QString::fromLatin1("Partial message: %1").arg(stringFromArray(d->trkReadBuffer));
emitError(msg);
return;
}
#else
const int size = bytesAvailable(d->file.handle());
if (!size)
return;
const QByteArray data = d->file.read(size);
if (verbose())
logMessage("trk: <- " + stringFromArray(data));
d->trkReadBuffer.append(data);
const ushort len = isValidTrkResult(d->trkReadBuffer, d->serialFrame);
if (!len) {
if (d->trkReadBuffer.size() > 10) {
const QString msg = QString::fromLatin1("Unable to extract message from '%1' '%2'").
arg(QLatin1String(d->trkReadBuffer.toHex())).arg(QString::fromAscii(d->trkReadBuffer));
emitError(msg);
}
return;
}
#endif // Q_OS_WIN
TrkResult r;
QByteArray rawData;
while (extractResult(&d->trkReadBuffer, d->serialFrame, &r, &rawData)) {
//if (verbose())
// logMessage("Read TrkResult " + r.data.toHex());
d->queue.slotHandleResult(r);
emit messageReceived(r);
if (!rawData.isEmpty())
emit rawDataReceived(rawData);
}
}
void TrkDevice::timerEvent(QTimerEvent *)
{
tryTrkWrite();
tryTrkRead();
}
void TrkDevice::emitError(const QString &s)
{
d->errorString = s;
qWarning("%s\n", qPrintable(s));
emit error(s);
}
void TrkDevice::sendTrkMessage(byte code, Callback callback,
const QByteArray &data, const QVariant &cookie)
{
d->queue.queueTrkMessage(code, callback, data, cookie);
}
void TrkDevice::sendTrkInitialPing()
{
d->queue.queueTrkInitialPing();
}
bool TrkDevice::sendTrkAck(byte token)
{
// The acknowledgement must not be queued!
TrkMessage msg(0x80, token);
msg.token = token;
msg.data.append('\0');
return trkWriteRawMessage(msg);
// 01 90 00 07 7e 80 01 00 7d 5e 7e
}
void TrkDevice::tryTrkWrite()
{
TrkMessage message;
if (!d->queue.pendingMessage(&message))
return;
const bool success = trkWriteRawMessage(message);
d->queue.notifyWriteResult(success);
}
bool TrkDevice::trkWriteRawMessage(const TrkMessage &msg)
{
const QByteArray ba = frameMessage(msg.code, msg.token, msg.data, serialFrame());
if (verbose())
logMessage("trk: -> " + stringFromArray(ba));
QString errorMessage;
const bool rc = write(ba, &errorMessage);
if (!rc)
emitError(errorMessage);
return rc;
}
} // namespace trk
+124
View File
@@ -0,0 +1,124 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this 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.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#ifndef TRKDEVICE_H
#define TRKDEVICE_H
#include "trkfunctor.h"
#include <QtCore/QObject>
#include <QtCore/QVariant>
#include <QtCore/QByteArray>
#include <QtCore/QSharedPointer>
QT_BEGIN_NAMESPACE
class QIODevice;
QT_END_NAMESPACE
namespace trk {
struct TrkResult;
struct TrkMessage;
struct TrkDevicePrivate;
/* TrkDevice: Implements a Windows COM or Linux device for
* Trk communications. Provides synchronous write and asynchronous
* read operation.
* The serialFrames property specifies whether packets are encapsulated in
* "0x90 <length>" frames, which is currently the case for serial ports.
* Contains write message queue allowing
* for queueing messages with a notification callback. If the message receives
* an ACK, the callback is invoked.
* The special message TRK_WRITE_QUEUE_NOOP_CODE code can be used for synchronisation.
* The respective message will not be sent, the callback is just invoked. */
enum { TRK_WRITE_QUEUE_NOOP_CODE = 0x7f };
class TrkDevice : public QObject
{
Q_OBJECT
Q_PROPERTY(bool serialFrame READ serialFrame WRITE setSerialFrame)
Q_PROPERTY(bool verbose READ verbose WRITE setVerbose)
public:
explicit TrkDevice(QObject *parent = 0);
virtual ~TrkDevice();
bool open(const QString &port, QString *errorMessage);
bool isOpen() const;
void close();
QString errorString() const;
bool serialFrame() const;
void setSerialFrame(bool f);
bool verbose() const;
void setVerbose(bool b);
bool write(const QByteArray &data, QString *errorMessage);
// Construct as 'TrkWriteQueueDevice::Callback(instance, &Klass::method);'
typedef TrkFunctor1<const TrkResult &> Callback;
signals:
void messageReceived(const trk::TrkResult &result);
// Emitted with the contents of messages enclosed in 07e, not for log output
void rawDataReceived(const QByteArray &data);
void error(const QString &msg);
void logMessage(const QString &msg);
protected:
void emitError(const QString &msg);
virtual void timerEvent(QTimerEvent *ev);
public:
void tryTrkRead();
// Enqueue a message with a notification callback.
void sendTrkMessage(unsigned char code,
Callback callBack = Callback(),
const QByteArray &data = QByteArray(),
const QVariant &cookie = QVariant());
// Enqeue an initial ping
void sendTrkInitialPing();
// Send an Ack synchronously, bypassing the queue
bool sendTrkAck(unsigned char token);
private:
void tryTrkWrite();
bool trkWriteRawMessage(const TrkMessage &msg);
TrkDevicePrivate *d;
};
} // namespace trk
#endif // TRKDEVICE_H
+147
View File
@@ -0,0 +1,147 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this 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.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#ifndef DEBUGGER_TRK_FUNCTOR_H
#define DEBUGGER_TRK_FUNCTOR_H
#include <QtGlobal>
// FIXME: rename into something less TRK-specific
namespace trk {
namespace Internal {
/* Helper class for the 1-argument functor:
* Cloneable base class for the implementation which is
* invokeable with the argument. */
template <class Argument>
class TrkFunctor1ImplBase {
Q_DISABLE_COPY(TrkFunctor1ImplBase)
public:
TrkFunctor1ImplBase() {}
virtual TrkFunctor1ImplBase *clone() const = 0;
virtual void invoke(Argument a) = 0;
virtual ~TrkFunctor1ImplBase() {}
};
/* Helper class for the 1-argument functor: Implementation for
* a class instance with a member function pointer. */
template <class Klass, class Argument>
class TrkFunctor1MemberPtrImpl : public TrkFunctor1ImplBase<Argument> {
public:
typedef void (Klass::*MemberFuncPtr)(Argument);
explicit TrkFunctor1MemberPtrImpl(Klass *instance,
MemberFuncPtr memberFunc) :
m_instance(instance),
m_memberFunc(memberFunc) {}
virtual TrkFunctor1ImplBase<Argument> *clone() const
{
return new TrkFunctor1MemberPtrImpl<Klass, Argument>(m_instance, m_memberFunc);
}
virtual void invoke(Argument a)
{ (m_instance->*m_memberFunc)(a); }
private:
Klass *m_instance;
MemberFuncPtr m_memberFunc;
};
} // namespace Internal
/* Default-constructible, copyable 1-argument functor providing an
* operator()(Argument) that invokes a member function of a class:
* \code
class Foo {
public:
void print(const std::string &);
};
...
Foo foo;
TrkFunctor1<const std::string &> f1(&foo, &Foo::print);
f1("test");
\endcode */
template <class Argument>
class TrkFunctor1 {
public:
TrkFunctor1() : m_impl(0) {}
template <class Klass>
TrkFunctor1(Klass *instance,
void (Klass::*memberFunc)(Argument)) :
m_impl(new Internal::TrkFunctor1MemberPtrImpl<Klass,Argument>(instance, memberFunc)) {}
~TrkFunctor1()
{
clean();
}
TrkFunctor1(const TrkFunctor1 &rhs) :
m_impl(0)
{
if (rhs.m_impl)
m_impl = rhs.m_impl->clone();
}
TrkFunctor1 &operator=(const TrkFunctor1 &rhs)
{
if (this != &rhs) {
clean();
if (rhs.m_impl)
m_impl = rhs.m_impl->clone();
}
return *this;
}
inline bool isNull() const { return m_impl == 0; }
operator bool() const { return !isNull(); }
void operator()(Argument a)
{
if (m_impl)
m_impl->invoke(a);
}
private:
void clean()
{
if (m_impl) {
delete m_impl;
m_impl = 0;
}
}
Internal::TrkFunctor1ImplBase<Argument> *m_impl;
};
} // namespace trk
#endif // _TRK_FUNCTOR_H_
+365
View File
@@ -0,0 +1,365 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this 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.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#include "trkutils.h"
#include <QtCore/QDebug>
#define logMessage(s) do { qDebug() << "TRKCLIENT: " << s; } while (0)
namespace trk {
QByteArray hexNumber(uint n, int digits)
{
QByteArray ba = QByteArray::number(n, 16);
if (digits == 0 || ba.size() == digits)
return ba;
return QByteArray(digits - ba.size(), '0') + ba;
}
QByteArray hexxNumber(uint n, int digits)
{
return "0x" + hexNumber(n, digits);
}
TrkResult::TrkResult() :
code(0),
token(0),
isDebugOutput(false)
{
}
void TrkResult::clear()
{
code = token= 0;
isDebugOutput = false;
data.clear();
cookie = QVariant();
}
QString TrkResult::toString() const
{
QString res = stringFromByte(code) + "[" + stringFromByte(token);
res.chop(1);
return res + "] " + stringFromArray(data);
}
QByteArray frameMessage(byte command, byte token, const QByteArray &data, bool serialFrame)
{
byte s = command + token;
for (int i = 0; i != data.size(); ++i)
s += data.at(i);
byte checksum = 255 - (s & 0xff);
//int x = s + ~s;
//logMessage("check: " << s << checksum << x;
QByteArray response;
response.append(char(command));
response.append(char(token));
response.append(data);
response.append(char(checksum));
QByteArray encodedData = encode7d(response);
QByteArray ba;
if (serialFrame) {
ba.append(char(0x01));
ba.append(char(0x90));
const ushort encodedSize = encodedData.size() + 2; // 2 x 0x7e
ba.append(char(encodedSize / 256));
ba.append(char(encodedSize % 256));
}
ba.append(char(0x7e));
ba.append(encodedData);
ba.append(char(0x7e));
return ba;
}
/* returns 0 if array doesn't represent a result,
otherwise returns the length of the result data */
ushort isValidTrkResult(const QByteArray &buffer, bool serialFrame)
{
if (serialFrame) {
// Serial protocol with length info
if (buffer.length() < 4)
return 0;
if (buffer.at(0) != 0x01 || byte(buffer.at(1)) != 0x90)
return 0;
const ushort len = extractShort(buffer.data() + 2);
return (buffer.size() >= len + 4) ? len : ushort(0);
}
// Frameless protocol without length info
const char delimiter = char(0x7e);
const int firstDelimiterPos = buffer.indexOf(delimiter);
// Regular message delimited by 0x7e..0x7e
if (firstDelimiterPos == 0) {
const int endPos = buffer.indexOf(delimiter, firstDelimiterPos + 1);
return endPos != -1 ? endPos + 1 - firstDelimiterPos : 0;
}
// Some ASCII log message up to first delimiter or all
return firstDelimiterPos != -1 ? firstDelimiterPos : buffer.size();
}
bool extractResult(QByteArray *buffer, bool serialFrame, TrkResult *result, QByteArray *rawData)
{
result->clear();
if(rawData)
rawData->clear();
const ushort len = isValidTrkResult(*buffer, serialFrame);
if (!len)
return false;
// handle receiving application output, which is not a regular command
const int delimiterPos = serialFrame ? 4 : 0;
if (buffer->at(delimiterPos) != 0x7e) {
result->isDebugOutput = true;
result->data = buffer->mid(delimiterPos, len);
result->data.replace("\r\n", "\n");
*buffer->remove(0, delimiterPos + len);
return true;
}
// FIXME: what happens if the length contains 0xfe?
// Assume for now that it passes unencoded!
const QByteArray data = decode7d(buffer->mid(delimiterPos + 1, len - 2));
if(rawData)
*rawData = data;
*buffer->remove(0, delimiterPos + len);
byte sum = 0;
for (int i = 0; i < data.size(); ++i) // 3 = 2 * 0xfe + sum
sum += byte(data.at(i));
if (sum != 0xff)
logMessage("*** CHECKSUM ERROR: " << byte(sum));
result->code = data.at(0);
result->token = data.at(1);
result->data = data.mid(2, data.size() - 3);
//logMessage(" REST BUF: " << stringFromArray(*buffer));
//logMessage(" CURR DATA: " << stringFromArray(data));
//QByteArray prefix = "READ BUF: ";
//logMessage((prefix + "HEADER: " + stringFromArray(header).toLatin1()).data());
return true;
}
ushort extractShort(const char *data)
{
return byte(data[0]) * 256 + byte(data[1]);
}
uint extractInt(const char *data)
{
uint res = byte(data[0]);
res *= 256; res += byte(data[1]);
res *= 256; res += byte(data[2]);
res *= 256; res += byte(data[3]);
return res;
}
QString quoteUnprintableLatin1(const QByteArray &ba)
{
QString res;
char buf[10];
for (int i = 0, n = ba.size(); i != n; ++i) {
const byte c = ba.at(i);
if (isprint(c)) {
res += c;
} else {
qsnprintf(buf, sizeof(buf) - 1, "\\%x", int(c));
res += buf;
}
}
return res;
}
QByteArray decode7d(const QByteArray &ba)
{
QByteArray res;
res.reserve(ba.size() + 2);
for (int i = 0; i < ba.size(); ++i) {
byte c = byte(ba.at(i));
if (c == 0x7d) {
++i;
c = 0x20 ^ byte(ba.at(i));
}
res.append(c);
}
//if (res != ba)
// logMessage("DECODED: " << stringFromArray(ba)
// << " -> " << stringFromArray(res));
return res;
}
QByteArray encode7d(const QByteArray &ba)
{
QByteArray res;
res.reserve(ba.size() + 2);
for (int i = 0; i < ba.size(); ++i) {
byte c = byte(ba.at(i));
if (c == 0x7e || c == 0x7d) {
res.append(0x7d);
res.append(0x20 ^ c);
} else {
res.append(c);
}
}
//if (res != ba)
// logMessage("ENCODED: " << stringFromArray(ba)
// << " -> " << stringFromArray(res));
return res;
}
// FIXME: Use the QByteArray based version below?
QString stringFromByte(byte c)
{
return QString("%1 ").arg(c, 2, 16, QChar('0'));
}
QString stringFromArray(const QByteArray &ba, int maxLen)
{
QString str;
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('.');
}
if (size != ba.size()) {
str += "...";
ascii += "...";
}
return str + " " + ascii;
}
void appendByte(QByteArray *ba, byte b)
{
ba->append(b);
}
void appendShort(QByteArray *ba, ushort s, Endianness endian)
{
if (endian == BigEndian) {
ba->append(s / 256);
ba->append(s % 256);
} else {
ba->append(s % 256);
ba->append(s / 256);
}
}
void appendInt(QByteArray *ba, uint i, Endianness endian)
{
int b3 = i % 256; i -= b3; i /= 256;
int b2 = i % 256; i -= b2; i /= 256;
int b1 = i % 256; i -= b1; i /= 256;
int b0 = i % 256; i -= b0; i /= 256;
if (endian == BigEndian) {
ba->append(b0);
ba->append(b1);
ba->append(b2);
ba->append(b3);
} else {
ba->append(b3);
ba->append(b2);
ba->append(b1);
ba->append(b0);
}
}
void appendString(QByteArray *ba, const QByteArray &str, Endianness endian, bool appendNullTerminator)
{
const int n = str.size();
const int fullSize = n + (appendNullTerminator ? 1 : 0);
appendShort(ba, fullSize, endian); // count the terminating \0
for (int i = 0; i != n; ++i)
ba->append(str.at(i));
if (appendNullTerminator)
ba->append('\0');
}
QByteArray errorMessage(byte code)
{
switch (code) {
case 0x00: return "No error";
case 0x01: return "Generic error in CWDS message";
case 0x02: return "Unexpected packet size in send msg";
case 0x03: return "Internal error occurred in CWDS";
case 0x04: return "Escape followed by frame flag";
case 0x05: return "Bad FCS in packet";
case 0x06: return "Packet too long";
case 0x07: return "Sequence ID not expected (gap in sequence)";
case 0x10: return "Command not supported";
case 0x11: return "Command param out of range";
case 0x12: return "An option was not supported";
case 0x13: return "Read/write to invalid memory";
case 0x14: return "Read/write invalid registers";
case 0x15: return "Exception occurred in CWDS";
case 0x16: return "Targeted system or thread is running";
case 0x17: return "Breakpoint resources (HW or SW) exhausted";
case 0x18: return "Requested breakpoint conflicts with existing one";
case 0x20: return "General OS-related error";
case 0x21: return "Request specified invalid process";
case 0x22: return "Request specified invalid thread";
}
return "Unknown error";
}
uint swapEndian(uint in)
{
return (in>>24) | ((in<<8) & 0x00FF0000) | ((in>>8) & 0x0000FF00) | (in<<24);
}
int TrkResult::errorCode() const
{
// NAK means always error, else data sized 1 with a non-null element
const bool isNAK = code == 0xff;
if (data.size() != 1 && !isNAK)
return 0;
if (const int errorCode = data.at(0))
return errorCode;
return isNAK ? 0xff : 0;
}
QString TrkResult::errorString() const
{
// NAK means always error, else data sized 1 with a non-null element
if (code == 0xff)
return "NAK";
if (data.size() < 1)
return "Unknown error packet";
return errorMessage(data.at(0));
}
} // namespace trk
+202
View File
@@ -0,0 +1,202 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this 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.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#ifndef DEBUGGER_TRK_UTILS
#define DEBUGGER_TRK_UTILS
#include <QtCore/QByteArray>
#include <QtCore/QHash>
#include <QtCore/QStringList>
#include <QtCore/QVariant>
typedef unsigned char byte;
namespace trk {
enum Command {
TrkPing = 0x00,
TrkConnect = 0x01,
TrkDisconnect = 0x02,
TrkVersions = 0x04,
TrkSupported = 0x05,
TrkCpuType = 0x06,
TrkHostVersions = 0x09,
TrkContinue = 0x18,
TrkCreateItem = 0x40,
TrkDeleteItem = 0x41,
TrkWriteFile = 0x48,
TrkOpenFile = 0x4a,
TrkCloseFile = 0x4b,
TrkInstallFile = 0x4d,
TrkInstallFile2 = 0x4e,
TrkNotifyAck = 0x80,
TrkNotifyNak = 0xff,
TrkNotifyStopped = 0x90,
TrkNotifyException = 0x91,
TrkNotifyInternalError = 0x92,
TrkNotifyCreated = 0xa0,
TrkNotifyDeleted = 0xa1,
TrkNotifyProcessorStarted = 0xa2,
TrkNotifyProcessorStandBy = 0xa6,
TrkNotifyProcessorReset = 0xa7
};
QByteArray decode7d(const QByteArray &ba);
QByteArray encode7d(const QByteArray &ba);
inline byte extractByte(const char *data) { return *data; }
ushort extractShort(const char *data);
uint extractInt(const char *data);
QString quoteUnprintableLatin1(const QByteArray &ba);
// produces "xx "
QString stringFromByte(byte c);
// produces "xx xx xx "
QString stringFromArray(const QByteArray &ba, int maxLen = - 1);
enum Endianness
{
LittleEndian,
BigEndian,
TargetByteOrder = BigEndian,
};
void appendByte(QByteArray *ba, byte b);
void appendShort(QByteArray *ba, ushort s, Endianness = TargetByteOrder);
void appendInt(QByteArray *ba, uint i, Endianness = TargetByteOrder);
void appendString(QByteArray *ba, const QByteArray &str, Endianness = TargetByteOrder, bool appendNullTerminator = true);
enum CodeMode
{
ArmMode = 0,
ThumbMode,
};
enum TargetConstants
{
RegisterCount = 17,
RegisterSP = 13, // Stack Pointer
RegisterLR = 14, // Return address
RegisterPC = 15, // Program counter
RegisterPSGdb = 25, // gdb's view of the world
RegisterPSTrk = 16, // TRK's view of the world
MemoryChunkSize = 256
};
struct Session
{
Session() {
cpuMajor = 0;
cpuMinor = 0;
bigEndian = 0;
defaultTypeSize = 0;
fpTypeSize = 0;
extended1TypeSize = 0;
extended2TypeSize = 0;
pid = 0;
tid = 0;
codeseg = 0;
dataseg = 0;
currentThread = 0;
}
// Trk feedback
byte cpuMajor;
byte cpuMinor;
byte bigEndian;
byte defaultTypeSize;
byte fpTypeSize;
byte extended1TypeSize;
byte extended2TypeSize;
uint pid;
uint tid;
uint codeseg;
uint dataseg;
QHash<uint, uint> addressToBP;
// Gdb request
uint currentThread;
QStringList modules;
};
struct Snapshot
{
uint registers[RegisterCount];
typedef QHash<uint, QByteArray> Memory;
Memory memory;
};
struct Breakpoint
{
Breakpoint(uint offset_ = 0)
{
number = 0;
offset = offset_;
mode = ArmMode;
}
uint offset;
ushort number;
CodeMode mode;
};
struct TrkResult
{
TrkResult();
void clear();
QString toString() const;
// 0 for no error.
int errorCode() const;
QString errorString() const;
byte code;
byte token;
QByteArray data;
QVariant cookie;
bool isDebugOutput;
};
// 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);
ushort isValidTrkResult(const QByteArray &buffer, bool serialFrame);
bool extractResult(QByteArray *buffer, bool serialFrame, TrkResult *r, QByteArray *rawData = 0);
QByteArray errorMessage(byte code);
QByteArray hexNumber(uint n, int digits = 0);
QByteArray hexxNumber(uint n, int digits = 0); // prepends '0x', too
uint swapEndian(uint in);
} // namespace trk
#endif // DEBUGGER_TRK_UTILS
@@ -58,7 +58,13 @@ void AbstractProcessStep::setCommand(const QString &buildConfiguration, const QS
QString AbstractProcessStep::command(const QString &buildConfiguration) const
{
return value(buildConfiguration, PROCESS_COMMAND).toString();
QString result = value(buildConfiguration, PROCESS_COMMAND).toString();
if (QFileInfo(result).isRelative()) {
QString searchInPath = environment(buildConfiguration).searchInPath(result);
if (!searchInPath.isEmpty())
result = searchInPath;
}
return result;
}
void AbstractProcessStep::setWorkingDirectory(const QString &buildConfiguration, const QString &workingDirectory)
+1 -6
View File
@@ -344,12 +344,7 @@ QString QmlRunConfiguration::type() const
QString QmlRunConfiguration::executable() const
{
if (! QFile::exists(m_qmlViewer)) {
QMessageBox::information(Core::ICore::instance()->mainWindow(),
tr("QML Viewer"),
tr("Could not find the qmlviewer executable, please specify one."));
}
// No need to verify if the QML Viewer exists. The console will tell us anyway when we try to launch it.
return m_qmlViewer;
}
+17 -12
View File
@@ -93,7 +93,7 @@ bool BaseTextDocument::save(const QString &fileName)
cursor.beginEditBlock();
if (m_storageSettings.m_cleanWhitespace)
cleanWhitespace(cursor, m_storageSettings.m_inEntireDocument);
cleanWhitespace(cursor, m_storageSettings.m_cleanIndentation, m_storageSettings.m_inEntireDocument);
if (m_storageSettings.m_addFinalNewLine)
ensureFinalNewLine(cursor);
cursor.endEditBlock();
@@ -301,23 +301,28 @@ void BaseTextDocument::setSyntaxHighlighter(QSyntaxHighlighter *highlighter)
void BaseTextDocument::cleanWhitespace()
void BaseTextDocument::cleanWhitespace(const QTextCursor &cursor)
{
QTextCursor cursor(m_document);
cursor.beginEditBlock();
cleanWhitespace(cursor, true);
if (m_storageSettings.m_addFinalNewLine)
ensureFinalNewLine(cursor);
cursor.endEditBlock();
bool hasSelection = cursor.hasSelection();
QTextCursor copyCursor = cursor;
copyCursor.beginEditBlock();
cleanWhitespace(copyCursor, true, true);
if (!hasSelection)
ensureFinalNewLine(copyCursor);
copyCursor.endEditBlock();
}
void BaseTextDocument::cleanWhitespace(QTextCursor& cursor, bool inEntireDocument)
void BaseTextDocument::cleanWhitespace(QTextCursor& cursor, bool cleanIndentation, bool inEntireDocument)
{
TextEditDocumentLayout *documentLayout = qobject_cast<TextEditDocumentLayout*>(m_document->documentLayout());
QTextBlock block = m_document->firstBlock();
while (block.isValid()) {
QTextBlock block = m_document->findBlock(cursor.selectionStart());
QTextBlock end;
if (cursor.hasSelection())
end = m_document->findBlock(cursor.selectionEnd()-1).next();
while (block.isValid() && block != end) {
if (inEntireDocument || block.revision() > documentLayout->lastSaveRevision) {
@@ -327,7 +332,7 @@ void BaseTextDocument::cleanWhitespace(QTextCursor& cursor, bool inEntireDocumen
cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor, trailing);
cursor.removeSelectedText();
}
if (m_storageSettings.m_cleanIndentation && !m_tabSettings.isIndentationClean(blockText)) {
if (cleanIndentation && !m_tabSettings.isIndentationClean(blockText)) {
cursor.setPosition(block.position());
int firstNonSpace = m_tabSettings.firstNonSpace(blockText);
if (firstNonSpace == blockText.length()) {
+2 -2
View File
@@ -110,7 +110,7 @@ public:
void reload(QTextCodec *codec);
void cleanWhitespace();
void cleanWhitespace(const QTextCursor &cursor);
signals:
void titleChanged(QString title);
@@ -146,7 +146,7 @@ private:
bool m_hasDecodingError;
QByteArray m_decodingErrorSample;
void cleanWhitespace(QTextCursor& cursor, bool onlyInModifiedLines);
void cleanWhitespace(QTextCursor& cursor, bool cleanIndentation, bool inEntireDocument);
void ensureFinalNewLine(QTextCursor& cursor);
};
+135 -16
View File
@@ -833,7 +833,7 @@ void BaseTextEditor::moveLineUpDown(bool up)
void BaseTextEditor::cleanWhitespace()
{
d->m_document->cleanWhitespace();
d->m_document->cleanWhitespace(textCursor());
}
void BaseTextEditor::keyPressEvent(QKeyEvent *e)
@@ -887,13 +887,18 @@ void BaseTextEditor::keyPressEvent(QKeyEvent *e)
QTextCursor cursor = textCursor();
if (d->m_inBlockSelectionMode)
cursor.clearSelection();
if (d->m_document->tabSettings().m_autoIndent) {
const TabSettings &ts = d->m_document->tabSettings();
if (ts.m_autoIndent) {
cursor.beginEditBlock();
cursor.insertBlock();
indent(document(), cursor, QChar::Null);
cursor.endEditBlock();
} else {
cursor.beginEditBlock();
QString previousBlockText = cursor.block().text();
cursor.insertBlock();
cursor.insertText(ts.indentationString(previousBlockText));
cursor.endEditBlock();
}
e->accept();
setTextCursor(cursor);
@@ -1337,6 +1342,16 @@ bool BaseTextEditor::codeFoldingSupported() const
return d->m_codeFoldingSupported;
}
void BaseTextEditor::setMouseNavigationEnabled(bool b)
{
d->m_mouseNavigationEnabled = b;
}
bool BaseTextEditor::mouseNavigationEnabled() const
{
return d->m_mouseNavigationEnabled;
}
void BaseTextEditor::setRevisionsVisible(bool b)
{
d->m_revisionsVisible = b;
@@ -1372,12 +1387,14 @@ BaseTextEditorPrivate::BaseTextEditorPrivate()
m_marksVisible(false),
m_codeFoldingVisible(false),
m_codeFoldingSupported(false),
m_mouseNavigationEnabled(true),
m_revisionsVisible(false),
m_lineNumbersVisible(true),
m_highlightCurrentLine(true),
m_requestMarkEnabled(true),
m_lineSeparatorsAllowed(false),
m_visibleWrapColumn(0),
m_showingLink(false),
m_editable(0),
m_actionHack(0),
m_inBlockSelectionMode(false),
@@ -2721,6 +2738,32 @@ void BaseTextEditorPrivate::clearVisibleCollapsedBlock()
void BaseTextEditor::mouseMoveEvent(QMouseEvent *e)
{
d->m_lastEventWasBlockSelectionEvent = (e->modifiers() & Qt::AltModifier);
bool linkFound = false;
if (d->m_mouseNavigationEnabled && e->modifiers() & Qt::ControlModifier) {
// Link emulation behaviour for 'go to definition'
const QTextCursor cursor = cursorForPosition(e->pos());
// Check that the mouse was actually on the text somewhere
bool onText = cursorRect(cursor).right() >= e->x();
if (!onText) {
QTextCursor nextPos = cursor;
nextPos.movePosition(QTextCursor::Right);
onText = cursorRect(nextPos).right() >= e->x();
}
const Link link = findLinkAt(cursor, false);
if (onText && link.isValid()) {
showLink(link);
linkFound = true;
}
}
if (!linkFound)
clearLink();
if (e->buttons() == Qt::NoButton) {
const QTextBlock collapsedBlock = collapsedBlockAt(e->pos());
const int blockNumber = collapsedBlock.next().blockNumber();
@@ -2767,6 +2810,38 @@ void BaseTextEditor::mousePressEvent(QMouseEvent *e)
QPlainTextEdit::mousePressEvent(e);
}
void BaseTextEditor::mouseReleaseEvent(QMouseEvent *e)
{
if (d->m_mouseNavigationEnabled && e->modifiers() & Qt::ControlModifier
&& !(e->modifiers() & Qt::ShiftModifier)
&& e->button() == Qt::LeftButton) {
const QTextCursor cursor = cursorForPosition(e->pos());
if (openLink(findLinkAt(cursor))) {
clearLink();
return;
}
}
QPlainTextEdit::mouseReleaseEvent(e);
}
void BaseTextEditor::leaveEvent(QEvent *e)
{
// Clear link emulation when the mouse leaves the editor
clearLink();
QPlainTextEdit::leaveEvent(e);
}
void BaseTextEditor::keyReleaseEvent(QKeyEvent *e)
{
// Clear link emulation when Ctrl is released
if (e->key() == Qt::Key_Control)
clearLink();
QPlainTextEdit::keyReleaseEvent(e);
}
void BaseTextEditor::extraAreaLeaveEvent(QEvent *)
{
// fake missing mouse move event from Qt
@@ -2833,7 +2908,7 @@ void BaseTextEditor::extraAreaMouseEvent(QMouseEvent *e)
document()->findBlockByNumber(d->m_highlightBlocksInfo.open.last()).position()
);
QTextBlock c = cursor.block();
if (!TextBlockUserData::canCollapse(c))
if (TextBlockUserData::hasCollapseAfter(c.previous()))
c = c.previous();
toggleBlockVisible(c);
d->moveCursorVisible(false);
@@ -3119,17 +3194,14 @@ void BaseTextEditor::handleBackspaceKey()
continue;
previousIndent = tabSettings.columnAt(previousNonEmptyBlockText,
tabSettings.firstNonSpace(previousNonEmptyBlockText));
if (previousIndent < indent)
if (previousIndent < indent) {
cursor.beginEditBlock();
cursor.setPosition(currentBlock.position(), QTextCursor::KeepAnchor);
cursor.insertText(tabSettings.indentationString(previousNonEmptyBlockText));
cursor.endEditBlock();
break;
}
}
if (previousIndent >= indent)
previousIndent = 0;
cursor.beginEditBlock();
cursor.setPosition(currentBlock.position(), QTextCursor::KeepAnchor);
cursor.insertText(tabSettings.indentationString(0, previousIndent));
cursor.endEditBlock();
}
void BaseTextEditor::wheelEvent(QWheelEvent *e)
@@ -3186,6 +3258,50 @@ void BaseTextEditor::indent(QTextDocument *doc, const QTextCursor &cursor, QChar
}
}
BaseTextEditor::Link BaseTextEditor::findLinkAt(const QTextCursor &, bool)
{
return Link();
}
bool BaseTextEditor::openLink(const Link &link)
{
if (link.fileName.isEmpty())
return false;
if (baseTextDocument()->fileName() == link.fileName) {
Core::EditorManager *editorManager = Core::EditorManager::instance();
editorManager->addCurrentPositionToNavigationHistory();
gotoLine(link.line, link.column);
setFocus();
return true;
}
return openEditorAt(link.fileName, link.line, link.column);
}
void BaseTextEditor::showLink(const Link &link)
{
QTextEdit::ExtraSelection sel;
sel.cursor = textCursor();
sel.cursor.setPosition(link.pos);
sel.cursor.setPosition(link.pos + link.length, QTextCursor::KeepAnchor);
sel.format = d->m_linkFormat;
sel.format.setFontUnderline(true);
setExtraSelections(OtherSelection, QList<QTextEdit::ExtraSelection>() << sel);
viewport()->setCursor(Qt::PointingHandCursor);
d->m_showingLink = true;
}
void BaseTextEditor::clearLink()
{
if (!d->m_showingLink)
return;
setExtraSelections(OtherSelection, QList<QTextEdit::ExtraSelection>());
viewport()->setCursor(Qt::IBeamCursor);
d->m_showingLink = false;
}
void BaseTextEditorPrivate::updateMarksBlock(const QTextBlock &block)
{
if (const TextBlockUserData *userData = TextEditDocumentLayout::testUserData(block))
@@ -4005,8 +4121,8 @@ void BaseTextEditor::unCommentSelection()
void BaseTextEditor::showEvent(QShowEvent* e)
{
if (!d->m_fontSettings.isEmpty()) {
setFontSettings(d->m_fontSettings);
d->m_fontSettings.clear();
setFontSettings(d->m_fontSettings);
d->m_fontSettings.clear();
}
QPlainTextEdit::showEvent(e);
}
@@ -4015,11 +4131,12 @@ void BaseTextEditor::showEvent(QShowEvent* e)
void BaseTextEditor::setFontSettingsIfVisible(const TextEditor::FontSettings &fs)
{
if (!isVisible()) {
d->m_fontSettings = fs;
return;
d->m_fontSettings = fs;
return;
}
setFontSettings(fs);
}
void BaseTextEditor::setFontSettings(const TextEditor::FontSettings &fs)
{
const QTextCharFormat textFormat = fs.toTextCharFormat(QLatin1String(Constants::C_TEXT));
@@ -4030,6 +4147,7 @@ void BaseTextEditor::setFontSettings(const TextEditor::FontSettings &fs)
const QTextCharFormat parenthesesFormat = fs.toTextCharFormat(QLatin1String(Constants::C_PARENTHESES));
d->m_currentLineFormat = fs.toTextCharFormat(QLatin1String(Constants::C_CURRENT_LINE));
d->m_currentLineNumberFormat = fs.toTextCharFormat(QLatin1String(Constants::C_CURRENT_LINE_NUMBER));
d->m_linkFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_LINK));
d->m_ifdefedOutFormat = fs.toTextCharFormat(QLatin1String(Constants::C_DISABLED_CODE));
QFont font(textFormat.font());
@@ -4082,6 +4200,7 @@ void BaseTextEditor::setDisplaySettings(const DisplaySettings &ds)
setCodeFoldingVisible(ds.m_displayFoldingMarkers);
setHighlightCurrentLine(ds.m_highlightCurrentLine);
setRevisionsVisible(ds.m_markTextChanges);
setMouseNavigationEnabled(ds.m_mouseNavigation);
if (d->m_displaySettings.m_visualizeWhitespace != ds.m_visualizeWhitespace) {
if (QSyntaxHighlighter *highlighter = baseTextDocument()->syntaxHighlighter())
+49 -8
View File
@@ -145,13 +145,10 @@ public:
inline static bool hasCollapseAfter(const QTextBlock & block)
{
if (!block.isValid())
return false;
TextBlockUserData *data = static_cast<TextBlockUserData*>(block.userData());
if (data && data->collapseMode() != NoCollapse) {
if (!block.isValid()) {
return false;
} else if (block.next().isValid()) {
data = static_cast<TextBlockUserData*>(block.next().userData());
TextBlockUserData *data = static_cast<TextBlockUserData*>(block.next().userData());
if (data && data->collapseMode() == TextBlockUserData::CollapseThis && !data->m_ifdefedOut)
return true;
}
@@ -277,8 +274,7 @@ private:
};
class TEXTEDITOR_EXPORT BaseTextEditor
: public QPlainTextEdit
class TEXTEDITOR_EXPORT BaseTextEditor : public QPlainTextEdit
{
Q_OBJECT
@@ -348,6 +344,9 @@ public:
void setCodeFoldingSupported(bool b);
bool codeFoldingSupported() const;
void setMouseNavigationEnabled(bool b);
bool mouseNavigationEnabled() const;
void setRevisionsVisible(bool b);
bool revisionsVisible() const;
@@ -499,14 +498,54 @@ protected:
void timerEvent(QTimerEvent *);
void mouseMoveEvent(QMouseEvent *);
void mousePressEvent(QMouseEvent *);
void mouseReleaseEvent(QMouseEvent *);
void leaveEvent(QEvent *);
void keyReleaseEvent(QKeyEvent *);
// Rertuns true if key triggers an indent.
// Returns true if key triggers an indent.
virtual bool isElectricCharacter(const QChar &ch) const;
// Indent a text block based on previous line. Default does nothing
virtual void indentBlock(QTextDocument *doc, QTextBlock block, QChar typedChar);
// Indent at cursor. Calls indentBlock for selection or current line.
virtual void indent(QTextDocument *doc, const QTextCursor &cursor, QChar typedChar);
struct Link
{
Link(const QString &fileName = QString(),
int line = 0,
int column = 0)
: pos(-1)
, length(-1)
, fileName(fileName)
, line(line)
, column(column)
{}
bool isValid() const
{ return !(pos == -1 || length == -1); }
int pos; // Link position
int length; // Link length
QString fileName; // Target file
int line; // Target line
int column; // Target column
};
/*!
Reimplement this function to enable code navigation.
\a resolveTarget is set to true when the target of the link is relevant
(it isn't until the link is used).
*/
virtual Link findLinkAt(const QTextCursor &, bool resolveTarget = true);
/*!
Reimplement this function if you want to customize the way a link is
opened. Returns whether the link was opened succesfully.
*/
virtual bool openLink(const Link &link);
protected slots:
virtual void slotUpdateExtraAreaWidth();
virtual void slotModificationChanged(bool);
@@ -540,6 +579,8 @@ private:
QTextBlock collapsedBlockAt(const QPoint &pos, QRect *box = 0) const;
void showLink(const Link &);
void clearLink();
// parentheses matcher
private slots:
+5 -1
View File
@@ -194,6 +194,7 @@ public:
uint m_marksVisible : 1;
uint m_codeFoldingVisible : 1;
uint m_codeFoldingSupported : 1;
uint m_mouseNavigationEnabled : 1;
uint m_revisionsVisible : 1;
uint m_lineNumbersVisible : 1;
uint m_highlightCurrentLine : 1;
@@ -201,6 +202,9 @@ public:
uint m_lineSeparatorsAllowed : 1;
int m_visibleWrapColumn;
QTextCharFormat m_linkFormat;
bool m_showingLink;
QTextCharFormat m_ifdefedOutFormat;
QRegExp m_searchExpr;
@@ -226,7 +230,7 @@ public:
QString copyBlockSelection();
void removeBlockSelection(const QString &text = QString());
bool m_moveLineUndoHack;
QTextCursor m_findScope;
QTextCursor m_selectBlockAnchor;
+13 -7
View File
@@ -110,6 +110,18 @@ int TabSettings::firstNonSpace(const QString &text) const
return i;
}
QString TabSettings::indentationString(const QString &text) const
{
return text.left(firstNonSpace(text));
}
int TabSettings::indentationColumn(const QString &text) const
{
return columnAt(text, firstNonSpace(text));
}
int TabSettings::trailingWhitespaces(const QString &text) const
{
int i = 0;
@@ -225,7 +237,7 @@ void TabSettings::indentLine(QTextBlock block, int newIndent) const
const int oldBlockLength = text.size();
// Quickly check whether indenting is required.
if (oldBlockLength == 0 && newIndent == 0)
if (indentationColumn(text) == newIndent)
return;
const QString indentString = indentationString(0, newIndent);
@@ -234,12 +246,6 @@ void TabSettings::indentLine(QTextBlock block, int newIndent) const
if (oldBlockLength == indentString.length() && text == indentString)
return;
if (oldBlockLength > indentString.length() &&
text.startsWith(indentString) &&
!text.at(indentString.length()).isSpace()) {
return;
}
QTextCursor cursor(block);
cursor.beginEditBlock();
cursor.movePosition(QTextCursor::StartOfBlock);
+2
View File
@@ -63,6 +63,8 @@ struct TEXTEDITOR_EXPORT TabSettings
int spacesLeftFromPosition(const QString &text, int position) const;
int indentedColumn(int column, bool doIndent = true) const;
QString indentationString(int startColumn, int targetColumn) const;
QString indentationString(const QString &text) const;
int indentationColumn(const QString &text) const;
void indentLine(QTextBlock block, int newIndent) const;
+6 -4
View File
@@ -1139,10 +1139,11 @@ ProItem::ProItemReturn ProFileEvaluator::Private::visitBeginProFile(ProFile * pr
}
if (!qmake_cache.isEmpty()) {
qmake_cache = QDir::cleanPath(qmake_cache);
if (evaluateFileInto(qmake_cache, &m_option->cache_valuemap, 0)) {
QHash<QString, QStringList> cache_valuemap;
if (evaluateFileInto(qmake_cache, &cache_valuemap, 0)) {
m_option->cachefile = qmake_cache;
if (m_option->qmakespec.isEmpty()) {
const QStringList &vals = m_option->cache_valuemap.value(QLatin1String("QMAKESPEC"));
const QStringList &vals = cache_valuemap.value(QLatin1String("QMAKESPEC"));
if (!vals.isEmpty())
m_option->qmakespec = vals.first();
}
@@ -1196,8 +1197,9 @@ ProItem::ProItemReturn ProFileEvaluator::Private::visitBeginProFile(ProFile * pr
if (!evaluateFileInto(spec,
&m_option->base_valuemap, &m_option->base_functions)) {
errorMessage(format("Could not read qmake configuration file %1").arg(spec));
} else {
evaluateFileInto(qmake_cache, &m_option->base_valuemap, 0);
} else if (!m_option->cachefile.isEmpty()) {
evaluateFileInto(m_option->cachefile,
&m_option->base_valuemap, &m_option->base_functions);
}
}
+1 -2
View File
@@ -88,8 +88,7 @@ public:
friend class ProFileEvaluator;
friend class ProFileEvaluator::Private;
static QString field_sep; // Just a cache for quick construction
QHash<QString, QStringList> cache_valuemap; // Cached results of .qmake.cache
QHash<QString, QStringList> base_valuemap; // ~ and qmake.conf and default_pre.prf
QHash<QString, QStringList> base_valuemap; // Cached results of qmake.conf, .qmake.cache & default_pre.prf
FunctionDefs base_functions;
QStringList feature_roots;
};