Commit d37a2d3a authored by hjk's avatar hjk

debugger: introduce enabling/disabling of breakpoints

parent 72eb8dc9
......@@ -49,15 +49,17 @@ using namespace Debugger::Internal;
namespace Debugger {
namespace Internal {
// The red blob on the left side in the cpp editor.
class BreakpointMarker : public TextEditor::BaseTextMark
{
Q_OBJECT
public:
BreakpointMarker(BreakpointData *data, const QString &fileName, int lineNumber)
: BaseTextMark(fileName, lineNumber), m_data(data), m_pending(true)
: BaseTextMark(fileName, lineNumber)
{
m_data = data;
m_pending = true;
m_enabled = true;
//qDebug() << "CREATE MARKER " << fileName << lineNumber;
}
......@@ -68,17 +70,24 @@ public:
}
QIcon icon() const
{
return icon(m_pending, m_enabled);
}
static const QIcon &icon(bool pending, bool enabled)
{
static const QIcon icon(":/gdbdebugger/images/breakpoint.svg");
static const QIcon icon1(":/gdbdebugger/images/breakpoint_disabled.svg");
static const QIcon icon2(":/gdbdebugger/images/breakpoint_pending.svg");
return m_pending ? icon2 : icon;
return enabled ? (pending ? icon2 : icon) : icon1;
}
void setPending(bool pending)
void setPending(bool pending, bool enabled)
{
if (pending == m_pending)
if (pending == m_pending && enabled == m_enabled)
return;
m_pending = pending;
m_enabled = enabled;
updateMarker();
}
......@@ -122,6 +131,7 @@ public:
private:
BreakpointData *m_data;
bool m_pending;
bool m_enabled;
};
} // namespace Internal
......@@ -139,6 +149,7 @@ BreakpointData::BreakpointData(BreakHandler *handler)
{
//qDebug() << "CREATE BREAKPOINTDATA" << this;
m_handler = handler;
enabled = true;
pending = true;
marker = 0;
markerLineNumber = 0;
......@@ -168,7 +179,7 @@ void BreakpointData::updateMarker()
marker = new BreakpointMarker(this, markerFileName, markerLineNumber);
if (marker)
marker->setPending(pending);
marker->setPending(pending, enabled);
}
QString BreakpointData::toToolTip() const
......@@ -281,6 +292,14 @@ int BreakHandler::findBreakpoint(const BreakpointData &needle)
return -1;
}
int BreakHandler::findBreakpoint(const QString &fileName, int lineNumber)
{
for (int index = 0; index != size(); ++index)
if (at(index)->isLocatedAt(fileName, lineNumber))
return index;
return -1;
}
int BreakHandler::findBreakpoint(int bpNumber)
{
for (int index = 0; index != size(); ++index)
......@@ -305,6 +324,8 @@ void BreakHandler::saveBreakpoints()
map["condition"] = data->condition;
if (!data->ignoreCount.isEmpty())
map["ignorecount"] = data->ignoreCount;
if (!data->enabled)
map["disabled"] = "1";
list.append(map);
}
setSessionValueRequested("Breakpoints", list);
......@@ -325,6 +346,7 @@ void BreakHandler::loadBreakpoints()
data->condition = map["condition"].toString();
data->ignoreCount = map["ignorecount"].toString();
data->funcName = map["funcname"].toString();
data->enabled = !map["disabled"].toInt();
data->markerFileName = data->fileName;
data->markerLineNumber = data->lineNumber.toInt();
append(data);
......@@ -372,8 +394,6 @@ QVariant BreakHandler::headerData(int section,
QVariant BreakHandler::data(const QModelIndex &mi, int role) const
{
static const QIcon icon(":/gdbdebugger/images/breakpoint.svg");
static const QIcon icon2(":/gdbdebugger/images/breakpoint_pending.svg");
static const QString empty = QString(QLatin1Char('-'));
QTC_ASSERT(mi.isValid(), return QVariant());
......@@ -388,8 +408,12 @@ QVariant BreakHandler::data(const QModelIndex &mi, int role) const
QString str = data->bpNumber;
return str.isEmpty() ? empty : str;
}
//if (role == Qt::CheckStateRole)
// return data->enabled ? Qt::Checked : Qt::Unchecked;
if (role == Qt::UserRole)
return data->enabled;
if (role == Qt::DecorationRole)
return data->pending ? icon2 : icon;
return BreakpointMarker::icon(data->pending, data->enabled);
break;
case 1:
if (role == Qt::DisplayRole) {
......@@ -432,6 +456,16 @@ QVariant BreakHandler::data(const QModelIndex &mi, int role) const
return QVariant();
}
Qt::ItemFlags BreakHandler::flags(const QModelIndex &mi) const
{
switch (mi.column()) {
//case 0:
// return Qt::ItemIsUserCheckable | Qt::ItemIsEnabled;
default:
return QAbstractItemModel::flags(mi);
}
}
bool BreakHandler::setData(const QModelIndex &mi, const QVariant &value, int role)
{
if (role != Qt::EditRole)
......@@ -439,6 +473,13 @@ bool BreakHandler::setData(const QModelIndex &mi, const QVariant &value, int rol
BreakpointData *data = at(mi.row());
switch (mi.column()) {
case 0: {
if (data->enabled != value.toBool()) {
toggleBreakpointEnabled(data);
dataChanged(mi, mi);
}
return true;
}
case 4: {
QString val = value.toString();
if (val != data->condition) {
......@@ -468,6 +509,20 @@ QList<BreakpointData *> BreakHandler::takeRemovedBreakpoints()
return result;
}
QList<BreakpointData *> BreakHandler::takeEnabledBreakpoints()
{
QList<BreakpointData *> result = m_enabled;
m_enabled.clear();
return result;
}
QList<BreakpointData *> BreakHandler::takeDisabledBreakpoints()
{
QList<BreakpointData *> result = m_disabled;
m_disabled.clear();
return result;
}
void BreakHandler::removeBreakpointHelper(int index)
{
BreakpointData *data = m_bp.at(index);
......@@ -476,7 +531,6 @@ void BreakHandler::removeBreakpointHelper(int index)
m_removed.append(data);
}
void BreakHandler::removeBreakpoint(int index)
{
if (index < 0 || index >= size())
......@@ -486,13 +540,24 @@ void BreakHandler::removeBreakpoint(int index)
saveBreakpoints();
}
void BreakHandler::toggleBreakpointEnabled(BreakpointData *data)
{
QTC_ASSERT(data, return);
data->enabled = !data->enabled;
if (data->enabled) {
m_enabled.append(data);
m_disabled.removeAll(data);
} else {
m_enabled.removeAll(data);
m_disabled.append(data);
}
saveBreakpoints();
updateMarkers();
}
int BreakHandler::indexOf(const QString &fileName, int lineNumber)
void BreakHandler::toggleBreakpointEnabled(const QString &fileName, int lineNumber)
{
for (int index = 0; index != size(); ++index)
if (at(index)->isLocatedAt(fileName, lineNumber))
return index;
return -1;
toggleBreakpointEnabled(at(findBreakpoint(fileName, lineNumber)));
}
void BreakHandler::setBreakpoint(const QString &fileName, int lineNumber)
......
......@@ -69,6 +69,7 @@ private:
BreakHandler *m_handler; // not owned.
public:
bool enabled; // should we talk to the debugger engine?
bool pending; // does the debugger engine know about us already?
// this "user requested information". will get stored in the session
......@@ -125,15 +126,19 @@ public:
void removeAt(int index); // also deletes the marker
void clear(); // also deletes all the marker
int indexOf(BreakpointData *data) { return m_bp.indexOf(data); }
int indexOf(const QString &fileName, int lineNumber);
int findBreakpoint(const QString &fileName, int lineNumber);
int findBreakpoint(const BreakpointData &data); // returns index
int findBreakpoint(int bpNumber); // returns index
void updateMarkers();
QList<BreakpointData *> takeRemovedBreakpoints();
QList<BreakpointData *> takeRemovedBreakpoints(); // owned
QList<BreakpointData *> takeEnabledBreakpoints(); // not owned
QList<BreakpointData *> takeDisabledBreakpoints(); // not owned
public slots:
void setBreakpoint(const QString &fileName, int lineNumber);
void toggleBreakpointEnabled(BreakpointData *data);
void toggleBreakpointEnabled(const QString &fileName, int lineNumber);
void breakByFunction(const QString &functionName);
void activateBreakpoint(int index);
void removeBreakpoint(int index);
......@@ -156,6 +161,7 @@ private:
QModelIndex index(int row, int column, const QModelIndex &) const
{ return createIndex(row, column); }
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
Qt::ItemFlags flags(const QModelIndex &index) const;
void markerUpdated(BreakpointMarker *, int lineNumber);
void loadBreakpoints();
......@@ -164,7 +170,9 @@ private:
void removeBreakpointHelper(int index);
QList<BreakpointData *> m_bp;
QList<BreakpointData *> m_removed;
QList<BreakpointData *> m_removed; // lately removed breakpoints
QList<BreakpointData *> m_enabled; // lately enabled breakpoints
QList<BreakpointData *> m_disabled; // lately disabled breakpoints
};
} // namespace Internal
......
......@@ -85,6 +85,7 @@ void BreakWindow::contextMenuEvent(QContextMenuEvent *ev)
{
QMenu menu;
const QModelIndex index = indexAt(ev->pos());
const QModelIndex index0 = index.sibling(index.row(), 0);
const bool indexIsValid = index.isValid();
QAction *act0 = new QAction(tr("Delete breakpoint"), &menu);
act0->setEnabled(indexIsValid);
......@@ -95,9 +96,13 @@ void BreakWindow::contextMenuEvent(QContextMenuEvent *ev)
QAction *act3 = new QAction(tr("Edit condition..."), &menu);
act3->setEnabled(indexIsValid);
QAction *act4 = new QAction(tr("Synchronize breakpoints"), &menu);
bool enabled = model()->data(index0, Qt::UserRole).toBool();
QString str = enabled ? tr("Disable breakpoint") : tr("Enable breakpoint");
QAction *act5 = new QAction(str, &menu);
menu.addAction(act0);
menu.addAction(act3);
menu.addAction(act5);
menu.addSeparator();
menu.addAction(act1);
menu.addAction(act2);
......@@ -117,6 +122,10 @@ void BreakWindow::contextMenuEvent(QContextMenuEvent *ev)
editCondition(index);
else if (act == act4)
emit breakpointSynchronizationRequested();
else if (act == act5) {
model()->setData(index0, !enabled);
emit breakpointSynchronizationRequested();
}
}
void BreakWindow::deleteBreakpoint(const QModelIndex &idx)
......
......@@ -2,6 +2,7 @@
<qresource prefix="/gdbdebugger" >
<file>images/breakpoint.svg</file>
<file>images/breakpoint_pending.svg</file>
<file>images/breakpoint_disabled.svg</file>
<file>images/debugger_breakpoints.png</file>
<file>images/debugger_continue_small.png</file>
<file>images/debugger_interrupt_small.png</file>
......
......@@ -163,19 +163,19 @@ DebuggerSettings *DebuggerSettings::instance()
item->setDefaultValue(true);
item = new SavedAction(instance);
instance->insertItem(UseCustomDebuggingHelperLocation, item);
item->setSettingsKey(debugModeGroup, QLatin1String("CustomDebuggingHelperLocation"));
instance->insertItem(UseCustomDebuggingHelperLocation, item);
item->setCheckable(true);
item = new SavedAction(instance);
instance->insertItem(CustomDebuggingHelperLocation, item);
item->setSettingsKey(debugModeGroup, QLatin1String("CustomDebuggingHelperLocation"));
instance->insertItem(CustomDebuggingHelperLocation, item);
item = new SavedAction(instance);
instance->insertItem(DebugDebuggingHelpers, item);
item->setSettingsKey(debugModeGroup, QLatin1String("DebugDebuggingHelpers"));
item->setText(tr("Debug debugging helper"));
item->setCheckable(true);
instance->insertItem(DebugDebuggingHelpers, item);
item = new SavedAction(instance);
......@@ -189,6 +189,7 @@ DebuggerSettings *DebuggerSettings::instance()
item->setText(tr("Syncronize breakpoints"));
instance->insertItem(SynchronizeBreakpoints, item);
//
// Registers
//
......
......@@ -144,14 +144,14 @@ static IDebuggerEngine *scriptEngine = 0;
// The creation functions take a list of options pages they can add to.
// This allows for having a "enabled" toggle on the page indepently
// of the engine.
extern IDebuggerEngine *createGdbEngine(DebuggerManager *parent, QList<Core::IOptionsPage*> *);
extern IDebuggerEngine *createWinEngine(DebuggerManager *, bool /* cmdLineDisabled */, QList<Core::IOptionsPage*> *)
IDebuggerEngine *createGdbEngine(DebuggerManager *parent, QList<Core::IOptionsPage*> *);
IDebuggerEngine *createWinEngine(DebuggerManager *, bool /* cmdLineDisabled */, QList<Core::IOptionsPage*> *)
#ifdef CDB_ENABLED
;
#else
{ return 0; }
#endif
extern IDebuggerEngine *createScriptEngine(DebuggerManager *parent, QList<Core::IOptionsPage*> *);
IDebuggerEngine *createScriptEngine(DebuggerManager *parent, QList<Core::IOptionsPage*> *);
DebuggerManager::DebuggerManager()
{
......@@ -687,6 +687,14 @@ void DebuggerManager::shutdown()
//qDebug() << "DEBUGGER_MANAGER SHUTDOWN END";
}
BreakpointData *DebuggerManager::findBreakpoint(const QString &fileName, int lineNumber)
{
if (!m_breakHandler)
return 0;
int index = m_breakHandler->findBreakpoint(fileName, lineNumber);
return index == -1 ? 0 : m_breakHandler->at(index);
}
void DebuggerManager::toggleBreakpoint()
{
QString fileName;
......@@ -712,7 +720,7 @@ void DebuggerManager::toggleBreakpoint(const QString &fileName, int lineNumber)
return;
}
int index = m_breakHandler->indexOf(fileName, lineNumber);
int index = m_breakHandler->findBreakpoint(fileName, lineNumber);
if (index == -1)
m_breakHandler->setBreakpoint(fileName, lineNumber);
else
......@@ -720,6 +728,25 @@ void DebuggerManager::toggleBreakpoint(const QString &fileName, int lineNumber)
m_engine->attemptBreakpointSynchronization();
}
void DebuggerManager::toggleBreakpointEnabled(const QString &fileName, int lineNumber)
{
if (Debugger::Constants::Internal::debug)
qDebug() << Q_FUNC_INFO << fileName << lineNumber;
QTC_ASSERT(m_engine, return);
QTC_ASSERT(m_breakHandler, return);
if (status() != DebuggerInferiorRunning
&& status() != DebuggerInferiorStopped
&& status() != DebuggerProcessNotReady) {
showStatusMessage(tr("Changing breakpoint state requires either a "
"fully running or fully stopped application."));
return;
}
m_breakHandler->toggleBreakpointEnabled(fileName, lineNumber);
m_engine->attemptBreakpointSynchronization();
}
void DebuggerManager::attemptBreakpointSynchronization()
{
m_engine->attemptBreakpointSynchronization();
......
......@@ -368,6 +368,8 @@ private:
void shutdown();
void toggleBreakpoint(const QString &fileName, int lineNumber);
void toggleBreakpointEnabled(const QString &fileName, int lineNumber);
BreakpointData *findBreakpoint(const QString &fileName, int lineNumber);
void setToolTipExpression(const QPoint &pos, const QString &exp0);
DebuggerStartMode m_startMode;
......
......@@ -29,6 +29,7 @@
#include "debuggerplugin.h"
#include "breakhandler.h"
#include "debuggeractions.h"
#include "debuggerconstants.h"
#include "debuggermanager.h"
......@@ -148,6 +149,11 @@ static ProjectExplorer::SessionManager *sessionManager()
return ProjectExplorer::ProjectExplorerPlugin::instance()->session();
}
static QSettings *settings()
{
return ICore::instance()->settings();
}
///////////////////////////////////////////////////////////////////////
//
// DebugMode
......@@ -246,13 +252,17 @@ public:
CommonOptionsPage() {}
// IOptionsPage
QString id() const { return QLatin1String(Debugger::Constants::DEBUGGER_COMMON_SETTINGS_PAGE); }
QString trName() const { return QCoreApplication::translate("Debugger", Debugger::Constants::DEBUGGER_COMMON_SETTINGS_PAGE); }
QString category() const { return QLatin1String(Debugger::Constants::DEBUGGER_SETTINGS_CATEGORY); }
QString trCategory() const { return QCoreApplication::translate("Debugger", Debugger::Constants::DEBUGGER_SETTINGS_CATEGORY); }
QString id() const
{ return QLatin1String(Debugger::Constants::DEBUGGER_COMMON_SETTINGS_PAGE); }
QString trName() const
{ return QCoreApplication::translate("Debugger", Debugger::Constants::DEBUGGER_COMMON_SETTINGS_PAGE); }
QString category() const
{ return QLatin1String(Debugger::Constants::DEBUGGER_SETTINGS_CATEGORY); }
QString trCategory() const
{ return QCoreApplication::translate("Debugger", Debugger::Constants::DEBUGGER_SETTINGS_CATEGORY); }
QWidget *createPage(QWidget *parent);
void apply() { m_group.apply(ICore::instance()->settings()); }
void apply() { m_group.apply(settings()); }
void finish() { m_group.finish(); }
private:
......@@ -305,7 +315,7 @@ public:
QString trCategory() const { return QCoreApplication::translate("Debugger", Debugger::Constants::DEBUGGER_SETTINGS_CATEGORY); }
QWidget *createPage(QWidget *parent);
void apply() { m_group.apply(ICore::instance()->settings()); }
void apply() { m_group.apply(settings()); }
void finish() { m_group.finish(); }
private:
......@@ -377,25 +387,17 @@ void DebuggingHelperOptionPage::updateState()
//
///////////////////////////////////////////////////////////////////////
DebuggerPlugin::DebuggerPlugin() :
m_manager(0),
DebuggerPlugin::DebuggerPlugin()
: m_manager(0),
m_debugMode(0),
m_locationMark(0),
m_gdbRunningContext(0),
m_breakpointMarginAction(0),
m_toggleLockedAction(0),
m_breakpointMarginActionLineNumber(0)
{
}
m_toggleLockedAction(0)
{}
DebuggerPlugin::~DebuggerPlugin()
{}
static QSettings *settings()
{
return ICore::instance()->settings();
}
void DebuggerPlugin::shutdown()
{
if (m_debugMode)
......@@ -455,13 +457,6 @@ bool DebuggerPlugin::initialize(const QStringList &arguments, QString *errorMess
m_gdbRunningContext = uidm->uniqueIdentifier(Constants::GDBRUNNING);
// FIXME: make this a global action
m_breakpointMarginAction = new QAction(this);
m_breakpointMarginAction->setText(tr("Toggle Breakpoint"));
//m_breakpointMarginAction->setIcon(QIcon(":/gdbdebugger/images/breakpoint.svg"));
connect(m_breakpointMarginAction, SIGNAL(triggered()),
this, SLOT(breakpointMarginActionTriggered()));
//Core::ActionContainer *mcppcontext =
// am->actionContainer(CppEditor::Constants::M_CONTEXT);
......@@ -837,17 +832,53 @@ void DebuggerPlugin::editorAboutToClose(Core::IEditor *editor)
void DebuggerPlugin::requestContextMenu(TextEditor::ITextEditor *editor,
int lineNumber, QMenu *menu)
{
m_breakpointMarginActionLineNumber = lineNumber;
m_breakpointMarginActionFileName = editor->file()->fileName();
menu->addAction(m_breakpointMarginAction);
QString fileName = editor->file()->fileName();
QString position = fileName + QString(":%1").arg(lineNumber);
BreakpointData *data = m_manager->findBreakpoint(fileName, lineNumber);
if (data) {
// existing breakpoint
QAction *act = new QAction(tr("Remove Breakpoint"), menu);
act->setData(position);
connect(act, SIGNAL(triggered()),
this, SLOT(breakpointSetRemoveMarginActionTriggered()));
menu->addAction(act);
QAction *act2;
if (data->enabled)
act2 = new QAction(tr("Disable Breakpoint"), menu);
else
act2 = new QAction(tr("Enable Breakpoint"), menu);
act2->setData(position);
connect(act2, SIGNAL(triggered()),
this, SLOT(breakpointEnableDisableMarginActionTriggered()));
menu->addAction(act2);
} else {
// non-existing
QAction *act = new QAction(tr("Set Breakpoint"), menu);
act->setData(position);
connect(act, SIGNAL(triggered()),
this, SLOT(breakpointSetRemoveMarginActionTriggered()));
menu->addAction(act);
}
}
void DebuggerPlugin::breakpointSetRemoveMarginActionTriggered()
{
if (QAction *act = qobject_cast<QAction *>(sender())) {
QString str = act->data().toString();
int pos = str.lastIndexOf(':');
m_manager->toggleBreakpoint(str.left(pos), str.mid(pos + 1).toInt());
}
}
void DebuggerPlugin::breakpointMarginActionTriggered()
void DebuggerPlugin::breakpointEnableDisableMarginActionTriggered()
{
m_manager->toggleBreakpoint(
m_breakpointMarginActionFileName,
m_breakpointMarginActionLineNumber
);
if (QAction *act = qobject_cast<QAction *>(sender())) {
QString str = act->data().toString();
int pos = str.lastIndexOf(':');
m_manager->toggleBreakpointEnabled(str.left(pos), str.mid(pos + 1).toInt());
}
}
void DebuggerPlugin::requestMark(TextEditor::ITextEditor *editor, int lineNumber)
......@@ -988,8 +1019,9 @@ void DebuggerPlugin::focusCurrentEditor(IMode *mode)
void DebuggerPlugin::showSettingsDialog()
{
Core::ICore::instance()->showOptionsDialog(QLatin1String(Debugger::Constants::DEBUGGER_SETTINGS_CATEGORY),
QLatin1String(Debugger::Constants::DEBUGGER_COMMON_SETTINGS_PAGE));
Core::ICore::instance()->showOptionsDialog(
QLatin1String(Debugger::Constants::DEBUGGER_SETTINGS_CATEGORY),
QLatin1String(Debugger::Constants::DEBUGGER_COMMON_SETTINGS_PAGE));
}
#include "debuggerplugin.moc"
......
......@@ -54,6 +54,7 @@ class ITextEditor;
namespace Debugger {
namespace Internal {
class BreakpointData;
class DebuggerManager;
class DebugMode;
class LocationMark;
......@@ -90,7 +91,8 @@ private slots:
void resetLocation();
void gotoLocation(const QString &fileName, int line, bool setMarker);
void breakpointMarginActionTriggered();
void breakpointSetRemoveMarginActionTriggered();
void breakpointEnableDisableMarginActionTriggered();
void focusCurrentEditor(Core::IMode *mode);
void showSettingsDialog();
......@@ -110,10 +112,7 @@ private:
LocationMark *m_locationMark;
int m_gdbRunningContext;
QAction *m_breakpointMarginAction;
QAction *m_toggleLockedAction;
int m_breakpointMarginActionLineNumber;
QString m_breakpointMarginActionFileName;
};
} // namespace Internal
......
......@@ -128,6 +128,8 @@ enum GdbCommandType
BreakEnablePending,
BreakSetAnnotate,
BreakDelete,
BreakEnable,
BreakDisable,
BreakList,
BreakIgnore,
BreakInfo,
......@@ -802,6 +804,8 @@ void GdbEngine::handleResult(const GdbResultRecord & record, int type,
break;
case BreakEnablePending:
case BreakDelete:
case BreakEnable:
case BreakDisable:
// nothing
break;
case BreakIgnore:
......@@ -2281,6 +2285,20 @@ void GdbEngine::attemptBreakpointSynchronization()
BreakHandler *handler = qq->breakHandler();
foreach (BreakpointData *data, handler->takeDisabledBreakpoints()) {
QString bpNumber = data->bpNumber;
if (!bpNumber.trimmed().isEmpty())
sendCommand("-break-disable " + bpNumber, BreakDisable, QVariant(),
NeedsStop);
}