Commit aa8548d3 authored by Friedemann Kleint's avatar Friedemann Kleint
Browse files

Added Cdb Debugger path options (including symbol server).

Made pathlisteditor use a toolbutton menu as it looks too
much of a "button desert" otherwise.
parent d971065b
......@@ -32,16 +32,19 @@
#include <QtGui/QVBoxLayout>
#include <QtGui/QHBoxLayout>
#include <QtGui/QPlainTextEdit>
#include <QtGui/QPushButton>
#include <QtGui/QToolButton>
#include <QtGui/QSpacerItem>
#include <QtGui/QFileDialog>
#include <QtGui/QTextCursor>
#include <QtGui/QTextBlock>
#include <QtGui/QMenu>
#include <QtGui/QAction>
#include <QtCore/QSignalMapper>
#include <QtCore/QMimeData>
#include <QtCore/QSharedPointer>
#include <QtCore/QDir>
#include <QtCore/QDebug>
namespace Core {
namespace Utils {
......@@ -86,6 +89,8 @@ struct PathListEditorPrivate {
QHBoxLayout *layout;
QVBoxLayout *buttonLayout;
QToolButton *toolButton;
QMenu *buttonMenu;
QPlainTextEdit *edit;
QSignalMapper *envVarMapper;
QString fileDialogTitle;
......@@ -94,12 +99,16 @@ struct PathListEditorPrivate {
PathListEditorPrivate::PathListEditorPrivate() :
layout(new QHBoxLayout),
buttonLayout(new QVBoxLayout),
toolButton(new QToolButton),
buttonMenu(new QMenu),
edit(new PathListPlainTextEdit),
envVarMapper(0)
{
layout->setMargin(0);
layout->addWidget(edit);
layout->addLayout(buttonLayout);
buttonLayout->addWidget(toolButton);
buttonLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Ignored, QSizePolicy::MinimumExpanding));
layout->addLayout(buttonLayout);
}
PathListEditor::PathListEditor(QWidget *parent) :
......@@ -107,9 +116,14 @@ PathListEditor::PathListEditor(QWidget *parent) :
m_d(new PathListEditorPrivate)
{
setLayout(m_d->layout);
addButton(tr("Add..."), this, SLOT(slotAdd()));
addButton(tr("Delete"), this, SLOT(deletePathAtCursor()));
addButton(tr("Clear"), this, SLOT(clear()));
m_d->toolButton->setPopupMode(QToolButton::MenuButtonPopup);
m_d->toolButton->setText(tr("Insert..."));
m_d->toolButton->setMenu(m_d->buttonMenu);
connect(m_d->toolButton, SIGNAL(clicked()), this, SLOT(slotInsert()));
addAction(tr("Add..."), this, SLOT(slotAdd()));
addAction(tr("Delete line"), this, SLOT(deletePathAtCursor()));
addAction(tr("Clear"), this, SLOT(clear()));
}
PathListEditor::~PathListEditor()
......@@ -117,26 +131,43 @@ PathListEditor::~PathListEditor()
delete m_d;
}
QAbstractButton *PathListEditor::addButton(const QString &text, QObject * receiver, const char *slotFunc)
static inline QAction *createAction(QObject *parent, const QString &text, QObject * receiver, const char *slotFunc)
{
// before Spacer item
return insertButton(m_d->buttonLayout->count() - 1, text, receiver, slotFunc);
QAction *rc = new QAction(text, parent);
QObject::connect(rc, SIGNAL(triggered()), receiver, slotFunc);
return rc;
}
QAbstractButton *PathListEditor::insertButton(int index, const QString &text, QObject * receiver, const char *slotFunc)
QAction *PathListEditor::addAction(const QString &text, QObject * receiver, const char *slotFunc)
{
#ifdef Q_OS_MAC
typedef QPushButton Button;
#else
typedef QToolButton Button;
#endif
Button *rc = new Button;
rc->setText(text);
connect(rc, SIGNAL(clicked()), receiver, slotFunc);
m_d->buttonLayout->insertWidget(index, rc);
QAction *rc = createAction(this, text, receiver, slotFunc);
m_d->buttonMenu->addAction(rc);
return rc;
}
QAction *PathListEditor::insertAction(int index /* -1 */, const QString &text, QObject * receiver, const char *slotFunc)
{
// Find the 'before' action
QAction *beforeAction = 0;
if (index >= 0) {
const QList<QAction*> actions = m_d->buttonMenu->actions();
if (index < actions.size())
beforeAction = actions.at(index);
}
QAction *rc = createAction(this, text, receiver, slotFunc);
if (beforeAction) {
m_d->buttonMenu->insertAction(beforeAction, rc);
} else {
m_d->buttonMenu->addAction(rc);
}
return rc;
}
int PathListEditor::lastAddActionIndex()
{
return 0; // Insert/Add
}
QString PathListEditor::pathListString() const
{
return pathList().join(separator());
......@@ -193,7 +224,14 @@ void PathListEditor::slotAdd()
{
const QString dir = QFileDialog::getExistingDirectory(this, m_d->fileDialogTitle);
if (!dir.isEmpty())
insertPathAtCursor(dir);
appendPath(QDir::toNativeSeparators(dir));
}
void PathListEditor::slotInsert()
{
const QString dir = QFileDialog::getExistingDirectory(this, m_d->fileDialogTitle);
if (!dir.isEmpty())
insertPathAtCursor(QDir::toNativeSeparators(dir));
}
QChar PathListEditor::separator()
......@@ -207,15 +245,16 @@ QChar PathListEditor::separator()
}
// Add a button "Import from 'Path'"
void PathListEditor::addEnvVariableImportButton(const QString &var)
void PathListEditor::addEnvVariableImportAction(const QString &var)
{
if (!m_d->envVarMapper) {
m_d->envVarMapper = new QSignalMapper(this);
connect(m_d->envVarMapper, SIGNAL(mapped(QString)), this, SLOT(setPathListFromEnvVariable(QString)));
}
QAbstractButton *b = addButton(tr("From \"%1\"").arg(var), m_d->envVarMapper, SLOT(map()));
m_d->envVarMapper->setMapping(b, var);
QAction *a = insertAction(lastAddActionIndex() + 1,
tr("From \"%1\"").arg(var), m_d->envVarMapper, SLOT(map()));
m_d->envVarMapper->setMapping(a, var);
}
QString PathListEditor::text() const
......@@ -247,14 +286,25 @@ void PathListEditor::insertPathAtCursor(const QString &path)
}
}
void PathListEditor::appendPath(const QString &path)
{
QString paths = text().trimmed();
if (!paths.isEmpty())
paths += QLatin1Char('\n');
paths += path;
setText(paths);
}
void PathListEditor::deletePathAtCursor()
{
// Delete current line
QTextCursor cursor = m_d->edit->textCursor();
if (cursor.block().isValid()) {
cursor.select(QTextCursor::BlockUnderCursor);
cursor.removeSelectedText();
cursor.movePosition(QTextCursor::StartOfLine, QTextCursor::MoveAnchor);
// Select down or until end of [last] line
if (!cursor.movePosition(QTextCursor::Down, QTextCursor::KeepAnchor))
cursor.movePosition(QTextCursor::EndOfLine, QTextCursor::KeepAnchor);
cursor.removeSelectedText();
m_d->edit->setTextCursor(cursor);
}
}
......
......@@ -36,7 +36,7 @@
#include <QtCore/QStringList>
QT_BEGIN_NAMESPACE
class QAbstractButton;
class QAction;
QT_END_NAMESPACE
namespace Core {
......@@ -72,8 +72,8 @@ public:
static QChar separator();
// Add a convenience button "Import from 'Path'" (environment variable)
void addEnvVariableImportButton(const QString &var);
// Add a convenience action "Import from 'Path'" (environment variable)
void addEnvVariableImportAction(const QString &var);
public slots:
void clear();
......@@ -83,17 +83,22 @@ public slots:
void setFileDialogTitle(const QString &l);
protected:
QAbstractButton *insertButton(int index /* -1 */, const QString &text, QObject * receiver, const char *slotFunc);
QAbstractButton *addButton(const QString &text, QObject * receiver, const char *slotFunc);
// Index after which to insert further "Add" actions
static int lastAddActionIndex();
QAction *insertAction(int index /* -1 */, const QString &text, QObject * receiver, const char *slotFunc);
QAction *addAction(const QString &text, QObject * receiver, const char *slotFunc);
QString text() const;
void setText(const QString &);
protected slots:
void insertPathAtCursor(const QString &);
void deletePathAtCursor();
void appendPath(const QString &);
private slots:
void slotAdd();
void slotInsert();
private:
PathListEditorPrivate *m_d;
......
......@@ -40,7 +40,8 @@ HEADERS += \
$$PWD/cdbassembler.h \
$$PWD/cdboptions.h \
$$PWD/cdboptionspage.h \
$$PWD/cdbdumperhelper.h
$$PWD/cdbdumperhelper.h \
$$PWD/cdbsymbolpathlisteditor.h
SOURCES += \
$$PWD/cdbdebugengine.cpp \
......@@ -54,7 +55,8 @@ SOURCES += \
$$PWD/cdbassembler.cpp \
$$PWD/cdboptions.cpp \
$$PWD/cdboptionspage.cpp \
$$PWD/cdbdumperhelper.cpp
$$PWD/cdbdumperhelper.cpp \
$$PWD/cdbsymbolpathlisteditor.cpp
FORMS += $$PWD/cdboptionspagewidget.ui
......
......@@ -347,8 +347,10 @@ IDebuggerEngine *CdbDebugEngine::create(DebuggerManager *parent,
QString *errorMessage)
{
CdbDebugEngine *rc = new CdbDebugEngine(parent, options);
if (rc->m_d->init(errorMessage))
if (rc->m_d->init(errorMessage)) {
rc->syncDebuggerPaths();
return rc;
}
delete rc;
return 0;
}
......@@ -452,6 +454,7 @@ void CdbDebugEnginePrivate::clearDisplay()
bool CdbDebugEngine::startDebugger()
{
m_d->clearDisplay();
const DebuggerStartMode mode = m_d->m_debuggerManager->startMode();
// Figure out dumper. @TODO: same in gdb...
const QString dumperLibName = QDir::toNativeSeparators(m_d->m_debuggerManagerAccess->qtDumperLibraryName());
......@@ -1489,6 +1492,59 @@ void CdbDebugEngine::reloadSourceFiles()
{
}
QStringList CdbDebugEnginePrivate::sourcePaths() const
{
WCHAR wszBuf[MAX_PATH];
if (SUCCEEDED(m_cif.debugSymbols->GetSourcePathWide(wszBuf, MAX_PATH, 0)))
return QString::fromUtf16(wszBuf).split(QLatin1Char(';'));
return QStringList();
}
void CdbDebugEngine::syncDebuggerPaths()
{
if (debugCDB)
qDebug() << Q_FUNC_INFO << m_d->m_options->symbolPaths << m_d->m_options->sourcePaths;
QString errorMessage;
if (!m_d->setSourcePaths(m_d->m_options->sourcePaths, &errorMessage)
|| !m_d->setSymbolPaths(m_d->m_options->symbolPaths, &errorMessage)) {
errorMessage = QString::fromLatin1("Unable to set the debugger paths: %1").arg(errorMessage);
qWarning("%s\n", qPrintable(errorMessage));
}
}
static inline QString pathString(const QStringList &s)
{ return s.join(QString(QLatin1Char(';'))); }
bool CdbDebugEnginePrivate::setSourcePaths(const QStringList &s, QString *errorMessage)
{
const HRESULT hr = m_cif.debugSymbols->SetSourcePathWide(pathString(s).utf16());
if (FAILED(hr)) {
if (errorMessage)
*errorMessage = msgComFailed("SetSourcePathWide", hr);
return false;
}
return true;
}
QStringList CdbDebugEnginePrivate::symbolPaths() const
{
WCHAR wszBuf[MAX_PATH];
if (SUCCEEDED(m_cif.debugSymbols->GetSymbolPathWide(wszBuf, MAX_PATH, 0)))
return QString::fromUtf16(wszBuf).split(QLatin1Char(';'));
return QStringList();
}
bool CdbDebugEnginePrivate::setSymbolPaths(const QStringList &s, QString *errorMessage)
{
const HRESULT hr = m_cif.debugSymbols->SetSymbolPathWide(pathString(s).utf16());
if (FAILED(hr)) {
if (errorMessage)
*errorMessage = msgComFailed("SetSymbolPathWide", hr);
return false;
}
return true;
}
} // namespace Internal
} // namespace Debugger
......@@ -1512,5 +1568,6 @@ Debugger::Internal::IDebuggerEngine *createWinEngine(Debugger::Internal::Debugge
optionsPage->setFailureMessage(errorMessage);
qWarning("%s", qPrintable(errorMessage));
}
QObject::connect(optionsPage, SIGNAL(debuggerPathsChanged()), engine, SLOT(syncDebuggerPaths()));
return engine;
}
......@@ -96,13 +96,16 @@ public:
virtual void reloadSourceFiles();
virtual void reloadFullStack() {}
public slots:
void syncDebuggerPaths();
protected:
void timerEvent(QTimerEvent*);
private slots:
void slotConsoleStubStarted();
void slotConsoleStubError(const QString &msg);
void slotConsoleStubTerminated();
void slotConsoleStubTerminated();
private:
bool startAttachDebugger(qint64 pid, QString *errorMessage);
......
......@@ -135,6 +135,12 @@ struct CdbDebugEnginePrivate
static bool executeDebuggerCommand(CIDebugControl *ctrl, const QString &command, QString *errorMessage);
static bool evaluateExpression(CIDebugControl *ctrl, const QString &expression, DEBUG_VALUE *v, QString *errorMessage);
QStringList sourcePaths() const;
bool setSourcePaths(const QStringList &s, QString *errorMessage);
QStringList symbolPaths() const;
bool setSymbolPaths(const QStringList &s, QString *errorMessage);
const QSharedPointer<CdbOptions> m_options;
HANDLE m_hDebuggeeProcess;
HANDLE m_hDebuggeeThread;
......
......@@ -34,8 +34,10 @@
#include <QtCore/QFileInfo>
static const char *settingsGroupC = "CDB";
static const char *enabledKeyC = "enabled";
static const char *pathKeyC = "path";
static const char *enabledKeyC = "Enabled";
static const char *pathKeyC = "Path";
static const char *symbolPathsKeyC = "SymbolPaths";
static const char *sourcePathsKeyC = "SourcePaths";
namespace Debugger {
namespace Internal {
......@@ -65,6 +67,8 @@ void CdbOptions::fromSettings(const QSettings *s)
}
enabled = s->value(enabledKey, false).toBool();
path = s->value(keyRoot + QLatin1String(pathKeyC), QString()).toString();
symbolPaths = s->value(keyRoot + QLatin1String(symbolPathsKeyC), QStringList()).toStringList();
sourcePaths = s->value(keyRoot + QLatin1String(sourcePathsKeyC), QStringList()).toStringList();
}
void CdbOptions::toSettings(QSettings *s) const
......@@ -72,6 +76,8 @@ void CdbOptions::toSettings(QSettings *s) const
s->beginGroup(QLatin1String(settingsGroupC));
s->setValue(QLatin1String(enabledKeyC), enabled);
s->setValue(QLatin1String(pathKeyC), path);
s->setValue(QLatin1String(symbolPathsKeyC), symbolPaths);
s->setValue(QLatin1String(sourcePathsKeyC), sourcePaths);
s->endGroup();
}
......@@ -105,9 +111,14 @@ bool CdbOptions::autoDetectPath(QString *outPath)
return false;
}
bool CdbOptions::equals(const CdbOptions &rhs) const
unsigned CdbOptions::compare(const CdbOptions &rhs) const
{
return enabled == rhs.enabled && path == rhs.path;
unsigned rc = 0;
if (enabled != rhs.enabled || path != rhs.path)
rc |= InitializationOptionsChanged;
if (symbolPaths != rhs.symbolPaths || sourcePaths != rhs.sourcePaths)
rc |= DebuggerPathsChanged;
return rc;
}
} // namespace Internal
......
......@@ -30,7 +30,7 @@
#ifndef CDBSETTINGS_H
#define CDBSETTINGS_H
#include <QtCore/QString>
#include <QtCore/QStringList>
QT_BEGIN_NAMESPACE
class QSettings;
......@@ -48,19 +48,23 @@ public:
void fromSettings(const QSettings *s);
void toSettings(QSettings *s) const;
bool equals(const CdbOptions &s) const;
// A set of flags for comparison function.
enum ChangeFlags { InitializationOptionsChanged = 0x1, DebuggerPathsChanged = 0x2 };
unsigned compare(const CdbOptions &s) const;
// Locate the debugging tools
static bool autoDetectPath(QString *path);
bool enabled;
QString path;
QStringList symbolPaths;
QStringList sourcePaths;
};
inline bool operator==(const CdbOptions &s1, const CdbOptions &s2)
{ return s1.equals(s2); }
{ return s1.compare(s2) == 0u; }
inline bool operator!=(const CdbOptions &s1, const CdbOptions &s2)
{ return !s1.equals(s2); }
{ return s1.compare(s2) != 0u; }
} // namespace Internal
} // namespace Debugger
......
......@@ -53,6 +53,8 @@ void CdbOptionsPageWidget::setOptions(CdbOptions &o)
{
m_ui.pathChooser->setPath(o.path);
m_ui.cdbOptionsGroupBox->setChecked(o.enabled);
m_ui.symbolPathListEditor->setPathList(o.symbolPaths);
m_ui.sourcePathListEditor->setPathList(o.sourcePaths);
}
CdbOptions CdbOptionsPageWidget::options() const
......@@ -60,6 +62,8 @@ CdbOptions CdbOptionsPageWidget::options() const
CdbOptions rc;
rc.path = m_ui.pathChooser->path();
rc.enabled = m_ui.cdbOptionsGroupBox->isChecked();
rc.symbolPaths = m_ui.symbolPathListEditor->pathList();
rc.sourcePaths = m_ui.sourcePathListEditor->pathList();
return rc;
}
......@@ -121,9 +125,11 @@ void CdbOptionsPage::apply()
if (!m_widget)
return;
const CdbOptions newOptions = m_widget->options();
if (newOptions != *m_options) {
if (const unsigned changedMask = m_options->compare(newOptions)) {
*m_options = newOptions;
m_options->toSettings(Core::ICore::instance()->settings());
if (changedMask & CdbOptions::DebuggerPathsChanged)
emit debuggerPathsChanged();
}
}
......
......@@ -83,6 +83,9 @@ public:
// Load failure messages can be displayed here
void setFailureMessage(const QString &msg) { m_failureMessage = msg; }
signals:
void debuggerPathsChanged();
private:
const QSharedPointer<CdbOptions> m_options;
QPointer<CdbOptionsPageWidget> m_widget;
......
......@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>369</width>
<height>281</height>
<width>522</width>
<height>512</height>
</rect>
</property>
<property name="windowTitle">
......@@ -48,7 +48,7 @@
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<width>0</width>
<height>20</height>
</size>
</property>
......@@ -56,6 +56,38 @@
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="pathGroupBox">
<property name="title">
<string>Debugger Paths</string>
</property>
<layout class="QFormLayout" name="formLayout_2">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<item row="0" column="0">
<widget class="QLabel" name="symbolPathLabel">
<property name="text">
<string>Symbol paths:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="Debugger::Internal::CdbSymbolPathListEditor" name="symbolPathListEditor" native="true"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="sourcePathLabel">
<property name="text">
<string>Source paths:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="Core::Utils::PathListEditor" name="sourcePathListEditor" native="true"/>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
......@@ -64,7 +96,7 @@
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>223</height>
<height>203</height>
</size>
</property>
</spacer>
......@@ -91,6 +123,18 @@
<header location="global">utils/pathchooser.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>Core::Utils::PathListEditor</class>
<extends>QWidget</extends>
<header location="global">utils/pathlisteditor.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>Debugger::Internal::CdbSymbolPathListEditor</class>
<extends>QWidget</extends>
<header>cdbsymbolpathlisteditor.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
......
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Qt Software Information (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 qt-sales@nokia.com.
**