forked from qt-creator/qt-creator
Merge branch 'master' of git@scm.dev.nokia.troll.no:creator/mainline
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user