Commit 3b2d2eae authored by hjk's avatar hjk
Browse files

Debugger: Re-work breakpoint storage handling



The actual data is now in a TreeModel. As interface to
individual breakpoints there's a new Breakpoint class
essentially providing a checked handle.

On the user code side breakHandler()->foo(bpId) is
replaced by bp.foo().

Change-Id: I82f435bad6301fce85a1d82bf6bf39e9ddba511e
Reviewed-by: default avatarChristian Stenger <christian.stenger@theqtcompany.com>
parent b88cdef0
This diff is collapsed.
......@@ -35,6 +35,9 @@
#include <utils/treemodel.h>
#include <QCoreApplication>
#include <QPointer>
//////////////////////////////////////////////////////////////////
//
// BreakHandler
......@@ -44,9 +47,122 @@
namespace Debugger {
namespace Internal {
class BreakpointMarker;
class BreakpointItem;
class BreakHandler;
class DebuggerEngine;
// Non-owning "deletion-safe" wrapper around a BreakpointItem *
class Breakpoint
{
Q_DECLARE_TR_FUNCTIONS(Debugger::Internal::BreakHandler)
public:
Breakpoint() {}
bool isValid() const;
operator const void *() const { return isValid() ? this : 0; }
bool operator!() const { return !isValid(); }
uint hash() const;
const BreakpointParameters &parameters() const;
BreakpointModelId id() const;
bool isLocatedAt(const QString &fileName, int lineNumber,
bool useMarkerPosition) const;
QIcon icon() const;
BreakpointState state() const;
void setEngine(DebuggerEngine *engine);
// State transitions.
void notifyBreakpointChangeAfterInsertNeeded();
void notifyBreakpointInsertProceeding();
void notifyBreakpointInsertOk();
void notifyBreakpointInsertFailed();
void notifyBreakpointChangeOk();
void notifyBreakpointChangeProceeding();
void notifyBreakpointChangeFailed();
void notifyBreakpointPending();
void notifyBreakpointRemoveProceeding();
void notifyBreakpointRemoveOk();
void notifyBreakpointRemoveFailed();
void notifyBreakpointReleased();
void notifyBreakpointNeedsReinsertion();
void notifyBreakpointAdjusted(const BreakpointParameters &params);
void update();
void gotoLocation() const;
// Getter retrieves property value.
// Setter sets property value and triggers update if changed.
// Only use setters when it is safe to assume that the breakpoint still
// exist. That's not the case if the event loop could run after you
// obtained the BreakpointItem pointer.
BreakpointPathUsage pathUsage() const;
void setPathUsage(const BreakpointPathUsage &u);
QByteArray condition() const;
void setCondition(const QByteArray &condition);
int ignoreCount() const;
void setIgnoreCount(const int &count);
int threadSpec() const;
void setThreadSpec(const int &spec);
QString fileName() const;
void setFileName(const QString &fileName);
QString functionName() const;
void setFunctionName(const QString &functionName);
QString expression() const;
void setExpression(const QString &expression);
QString message() const;
void setMessage(const QString &m);
BreakpointType type() const;
void setType(const BreakpointType &type);
quint64 address() const;
void setAddress(const quint64 &address);
int lineNumber() const;
void changeBreakpointData(const BreakpointParameters &data);
bool isEnabled() const;
void setEnabled(bool on) const;
void updateFileNameFromMarker(const QString &fileName);
void updateLineNumberFromMarker(int lineNumber);
void changeLineNumberFromMarker(int lineNumber);
void setMarkerFileAndLine(const QString &fileName, int lineNumber);
bool isWatchpoint() const;
bool isTracepoint() const;
void setTracepoint(bool on);
DebuggerEngine *engine() const;
const BreakpointResponse &response() const;
void setResponse(const BreakpointResponse &data);
bool needsChange() const;
bool needsChildren() const;
bool isOneShot() const;
void insertSubBreakpoint(const BreakpointResponse &data);
void removeAlienBreakpoint();
void removeBreakpoint() const;
QString msgWatchpointByAddressTriggered(int number, quint64 address) const;
QString msgWatchpointByAddressTriggered(
int number, quint64 address, const QString &threadId) const;
QString msgWatchpointByExpressionTriggered(int number, const QString &expr) const;
QString msgWatchpointByExpressionTriggered(
int number, const QString &expr, const QString &threadId) const;
QString msgBreakpointTriggered(int number, const QString &threadId) const;
private:
void gotoState(BreakpointState target, BreakpointState assumedCurrent);
friend class BreakHandler;
explicit Breakpoint(BreakpointItem *b);
QPointer<BreakpointItem> b;
};
inline uint qHash(const Debugger::Internal::Breakpoint &b) { return b.hash(); }
typedef QList<Breakpoint> Breakpoints;
class BreakHandler : public Utils::TreeModel
{
Q_OBJECT
......@@ -62,21 +178,19 @@ public:
// The only way to add a new breakpoint.
void appendBreakpoint(const BreakpointParameters &data);
void handleAlienBreakpoint(const BreakpointResponse &response, DebuggerEngine *engine);
void insertSubBreakpoint(BreakpointModelId id, const BreakpointResponse &data);
void removeAlienBreakpoint(BreakpointModelId id);
BreakpointModelIds allBreakpointIds() const;
BreakpointModelIds engineBreakpointIds(DebuggerEngine *engine) const;
BreakpointModelIds unclaimedBreakpointIds() const;
Breakpoints allBreakpoints() const;
Breakpoints engineBreakpoints(DebuggerEngine *engine) const;
Breakpoints unclaimedBreakpoints() const;
QStringList engineBreakpointPaths(DebuggerEngine *engine) const;
// Find a breakpoint matching approximately the data in needle.
BreakpointModelId findSimilarBreakpoint(const BreakpointResponse &needle) const;
BreakpointModelId findBreakpointByResponseId(const BreakpointResponseId &resultId) const;
BreakpointModelId findWatchpoint(const BreakpointParameters &data) const;
BreakpointModelId findBreakpointByFunction(const QString &functionName) const;
BreakpointModelId findBreakpointByIndex(const QModelIndex &index) const;
BreakpointModelIds findBreakpointsByIndex(const QList<QModelIndex> &list) const;
Breakpoint findSimilarBreakpoint(const BreakpointResponse &needle) const;
Breakpoint findBreakpointByResponseId(const BreakpointResponseId &resultId) const;
Breakpoint findWatchpoint(const BreakpointParameters &data) const;
Breakpoint findBreakpointByFunction(const QString &functionName) const;
Breakpoint findBreakpointByIndex(const QModelIndex &index) const;
Breakpoints findBreakpointsByIndex(const QList<QModelIndex> &list) const;
void updateMarkers();
static QIcon breakpointIcon();
......@@ -86,77 +200,11 @@ public:
static QIcon watchpointIcon();
static QIcon tracepointIcon();
BreakpointModelId findBreakpointByFileAndLine(const QString &fileName,
Breakpoint findBreakpointByFileAndLine(const QString &fileName,
int lineNumber, bool useMarkerPosition = true);
BreakpointModelId findBreakpointByAddress(quint64 address) const;
Breakpoint findBreakpointByAddress(quint64 address) const;
void breakByFunction(const QString &functionName);
void removeBreakpoint(BreakpointModelId id);
QIcon icon(BreakpointModelId id) const;
void gotoLocation(BreakpointModelId id) const;
// Getter retrieves property value.
// Setter sets property value and triggers update if changed.
BreakpointPathUsage pathUsage(BreakpointModelId id) const;
void setPathUsage(BreakpointModelId, const BreakpointPathUsage &u);
QByteArray condition(BreakpointModelId id) const;
void setCondition(BreakpointModelId, const QByteArray &condition);
int ignoreCount(BreakpointModelId id) const;
void setIgnoreCount(BreakpointModelId, const int &count);
int threadSpec(BreakpointModelId id) const;
void setThreadSpec(BreakpointModelId, const int &spec);
QString fileName(BreakpointModelId id) const;
void setFileName(BreakpointModelId, const QString &fileName);
QString functionName(BreakpointModelId id) const;
void setFunctionName(BreakpointModelId, const QString &functionName);
QString expression(BreakpointModelId id) const;
void setExpression(BreakpointModelId, const QString &expression);
QString message(BreakpointModelId id) const;
void setMessage(BreakpointModelId, const QString &m);
BreakpointType type(BreakpointModelId id) const;
void setType(BreakpointModelId id, const BreakpointType &type);
quint64 address(BreakpointModelId id) const;
void setAddress(BreakpointModelId id, const quint64 &address);
int lineNumber(BreakpointModelId id) const;
void changeBreakpointData(BreakpointModelId id, const BreakpointParameters &data,
BreakpointParts parts);
const BreakpointParameters &breakpointData(BreakpointModelId id) const;
BreakpointState state(BreakpointModelId id) const;
bool isEnabled(BreakpointModelId id) const;
void setEnabled(BreakpointModelId id, bool on);
void updateFileNameFromMarker(BreakpointModelId id, const QString &fileName);
void updateLineNumberFromMarker(BreakpointModelId id, int lineNumber);
void changeLineNumberFromMarker(BreakpointModelId id, int lineNumber);
void setMarkerFileAndLine(BreakpointModelId id,
const QString &fileName, int lineNumber);
bool isOneShot(BreakpointModelId id) const;
bool isWatchpoint(BreakpointModelId id) const;
bool isTracepoint(BreakpointModelId id) const;
void setTracepoint(BreakpointModelId, bool on);
DebuggerEngine *engine(BreakpointModelId id) const;
void setEngine(BreakpointModelId id, DebuggerEngine *engine);
const BreakpointResponse &response(BreakpointModelId id) const;
void setResponse(BreakpointModelId id, const BreakpointResponse &data);
bool needsChange(BreakpointModelId id) const;
bool needsChildren(BreakpointModelId id) const;
// State transitions.
void notifyBreakpointChangeAfterInsertNeeded(BreakpointModelId id);
void notifyBreakpointInsertProceeding(BreakpointModelId id);
void notifyBreakpointInsertOk(BreakpointModelId id);
void notifyBreakpointInsertFailed(BreakpointModelId id);
void notifyBreakpointChangeOk(BreakpointModelId id);
void notifyBreakpointChangeProceeding(BreakpointModelId id);
void notifyBreakpointChangeFailed(BreakpointModelId id);
void notifyBreakpointPending(BreakpointModelId id);
void notifyBreakpointRemoveProceeding(BreakpointModelId id);
void notifyBreakpointRemoveOk(BreakpointModelId id);
void notifyBreakpointRemoveFailed(BreakpointModelId id);
void notifyBreakpointReleased(BreakpointModelId id);
void notifyBreakpointNeedsReinsertion(BreakpointModelId id);
void notifyBreakpointAdjusted(BreakpointModelId id,
const BreakpointParameters &data);
static QString displayFromThreadSpec(int spec);
static int threadSpecFromDisplay(const QString &str);
......@@ -164,54 +212,22 @@ public:
void setWatchpointAtAddress(quint64 address, unsigned size);
void setWatchpointAtExpression(const QString &exp);
Breakpoint breakpointById(BreakpointModelId id) const;
signals:
void requestExpansion(QModelIndex);
private:
bool isEngineRunning(BreakpointModelId id) const;
void setState(BreakpointModelId id, BreakpointState state);
friend class BreakpointItem;
friend class Breakpoint;
void loadBreakpoints();
void saveBreakpoints();
void cleanupBreakpoint(BreakpointModelId id);
void appendBreakpointInternal(const BreakpointParameters &data);
Q_SLOT void changeLineNumberFromMarkerHelper(Debugger::Internal::BreakpointModelId id, int lineNumber);
struct BreakpointItem : public Utils::TreeItem
{
BreakpointItem();
~BreakpointItem();
int columnCount() const { return 8; }
QVariant data(int column, int role) const;
void destroyMarker();
bool needsChange() const;
bool isLocatedAt(const QString &fileName, int lineNumber,
bool useMarkerPosition) const;
void updateMarker();
void updateMarkerIcon();
QString toToolTip() const;
QString markerFileName() const;
int markerLineNumber() const;
QIcon icon() const;
BreakpointModelId id;
BreakpointParameters params;
BreakpointState state; // Current state of breakpoint.
DebuggerEngine *engine; // Engine currently handling the breakpoint.
BreakpointResponse response;
BreakpointMarker *marker;
};
struct LocationItem : public Utils::TreeItem
{
int columnCount() const { return 8; }
QVariant data(int column, int role) const;
BreakpointResponse params;
};
BreakpointItem *breakpointById(BreakpointModelId id) const;
Q_SLOT void changeLineNumberFromMarkerHelper(Debugger::Internal::BreakpointModelId id);
Q_SLOT void deletionHelper(Debugger::Internal::BreakpointModelId id);
void scheduleSynchronization();
void timerEvent(QTimerEvent *event);
......@@ -221,4 +237,6 @@ private:
} // namespace Internal
} // namespace Debugger
Q_DECLARE_METATYPE(Debugger::Internal::Breakpoint)
#endif // DEBUGGER_BREAKHANDLER_H
......@@ -199,7 +199,6 @@ inline void operator|=(BreakpointParts &p, BreakpointParts r)
p = BreakpointParts(int(p) | int(r));
}
class BreakpointParameters
{
public:
......@@ -257,8 +256,6 @@ public:
int correctedLineNumber; //!< Line number as seen by gdb.
};
typedef QList<BreakpointModelId> BreakpointModelIds;
inline uint qHash(const Debugger::Internal::BreakpointModelId &id)
{
return id.toInternalId();
......
......@@ -80,7 +80,7 @@ class BreakpointDialog : public QDialog
{
Q_OBJECT
public:
explicit BreakpointDialog(BreakpointModelId id, QWidget *parent = 0);
explicit BreakpointDialog(Breakpoint b, QWidget *parent = 0);
bool showDialog(BreakpointParameters *data, BreakpointParts *parts);
void setParameters(const BreakpointParameters &data);
......@@ -138,7 +138,7 @@ private:
QDialogButtonBox *m_buttonBox;
};
BreakpointDialog::BreakpointDialog(BreakpointModelId id, QWidget *parent)
BreakpointDialog::BreakpointDialog(Breakpoint b, QWidget *parent)
: QDialog(parent), m_enabledParts(~0), m_previousType(UnknownBreakpointType),
m_firstTypeChange(true)
{
......@@ -268,8 +268,8 @@ BreakpointDialog::BreakpointDialog(BreakpointModelId id, QWidget *parent)
m_buttonBox = new QDialogButtonBox(this);
m_buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
if (id.isValid()) {
if (DebuggerEngine *engine = breakHandler()->engine(id)) {
if (b) {
if (DebuggerEngine *engine = b.engine()) {
if (!engine->hasCapability(BreakConditionCapability))
m_enabledParts &= ~ConditionPart;
if (!engine->hasCapability(BreakModuleCapability))
......@@ -695,7 +695,7 @@ void BreakTreeView::keyPressEvent(QKeyEvent *ev)
QModelIndexList si = sm->selectedRows();
if (si.isEmpty())
si.append(currentIndex());
const BreakpointModelIds ids = breakHandler()->findBreakpointsByIndex(si);
const Breakpoints ids = breakHandler()->findBreakpointsByIndex(si);
int row = qMin(model()->rowCount() - ids.size() - 1, currentIndex().row());
deleteBreakpoints(ids);
setCurrentIndex(model()->index(row, 0));
......@@ -704,10 +704,9 @@ void BreakTreeView::keyPressEvent(QKeyEvent *ev)
QTC_ASSERT(sm, return);
const QModelIndexList selectedIds = sm->selectedRows();
if (!selectedIds.isEmpty()) {
BreakHandler *handler = breakHandler();
const BreakpointModelIds validIds = handler->findBreakpointsByIndex(selectedIds);
const bool isEnabled = validIds.isEmpty() || handler->isEnabled(validIds.at(0));
setBreakpointsEnabled(validIds, !isEnabled);
const Breakpoints items = breakHandler()->findBreakpointsByIndex(selectedIds);
const bool isEnabled = items.isEmpty() || items.at(0).isEnabled();
setBreakpointsEnabled(items, !isEnabled);
foreach (const QModelIndex &id, selectedIds)
update(id);
}
......@@ -720,8 +719,9 @@ void BreakTreeView::mouseDoubleClickEvent(QMouseEvent *ev)
QModelIndex indexUnderMouse = indexAt(ev->pos());
if (indexUnderMouse.isValid()) {
if (indexUnderMouse.column() >= 4) {
BreakpointModelId id = breakHandler()->findBreakpointByIndex(indexUnderMouse);
editBreakpoints(BreakpointModelIds() << id);
Breakpoint b = breakHandler()->findBreakpointByIndex(indexUnderMouse);
QTC_ASSERT(b, return);
editBreakpoints(Breakpoints() << b);
}
} else {
addBreakpoint();
......@@ -740,21 +740,18 @@ void BreakTreeView::contextMenuEvent(QContextMenuEvent *ev)
selectedIndices.append(indexUnderMouse);
BreakHandler *handler = breakHandler();
BreakpointModelIds selectedIds;
foreach (BreakpointModelId id, handler->findBreakpointsByIndex(selectedIndices))
if (id.isMajor())
selectedIds.append(id);
Breakpoints selectedItems = handler->findBreakpointsByIndex(selectedIndices);
const int rowCount = model()->rowCount();
QAction *deleteAction = new QAction(tr("Delete Breakpoint"), &menu);
deleteAction->setEnabled(!selectedIds.empty());
auto deleteAction = new QAction(tr("Delete Breakpoint"), &menu);
deleteAction->setEnabled(!selectedItems.empty());
QAction *deleteAllAction = new QAction(tr("Delete All Breakpoints"), &menu);
auto deleteAllAction = new QAction(tr("Delete All Breakpoints"), &menu);
deleteAllAction->setEnabled(model()->rowCount() > 0);
// Delete by file: Find indices of breakpoints of the same file.
QAction *deleteByFileAction = 0;
BreakpointModelIds breakpointsInFile;
Breakpoints breakpointsInFile;
if (indexUnderMouse.isValid()) {
const QModelIndex index = indexUnderMouse.sibling(indexUnderMouse.row(), 2);
const QString file = index.data().toString();
......@@ -774,36 +771,33 @@ void BreakTreeView::contextMenuEvent(QContextMenuEvent *ev)
deleteByFileAction->setEnabled(false);
}
QAction *editBreakpointAction =
new QAction(tr("Edit Breakpoint..."), &menu);
editBreakpointAction->setEnabled(!selectedIds.isEmpty());
auto editBreakpointAction = new QAction(tr("Edit Breakpoint..."), &menu);
editBreakpointAction->setEnabled(!selectedItems.isEmpty());
int threadId = 0;
// FIXME BP: m_engine->threadsHandler()->currentThreadId();
QString associateTitle = threadId == -1
? tr("Associate Breakpoint With All Threads")
: tr("Associate Breakpoint With Thread %1").arg(threadId);
QAction *associateBreakpointAction = new QAction(associateTitle, &menu);
associateBreakpointAction->setEnabled(!selectedIds.isEmpty());
auto associateBreakpointAction = new QAction(associateTitle, &menu);
associateBreakpointAction->setEnabled(!selectedItems.isEmpty());
QAction *synchronizeAction =
new QAction(tr("Synchronize Breakpoints"), &menu);
auto synchronizeAction = new QAction(tr("Synchronize Breakpoints"), &menu);
synchronizeAction->setEnabled(Internal::hasSnapshots());
bool enabled = selectedIds.isEmpty() || handler->isEnabled(selectedIds.at(0));
bool enabled = selectedItems.isEmpty() || selectedItems.at(0).isEnabled();
const QString str5 = selectedIds.size() > 1
const QString str5 = selectedItems.size() > 1
? enabled
? tr("Disable Selected Breakpoints")
: tr("Enable Selected Breakpoints")
: enabled
? tr("Disable Breakpoint")
: tr("Enable Breakpoint");
QAction *toggleEnabledAction = new QAction(str5, &menu);
toggleEnabledAction->setEnabled(!selectedIds.isEmpty());
auto toggleEnabledAction = new QAction(str5, &menu);
toggleEnabledAction->setEnabled(!selectedItems.isEmpty());
QAction *addBreakpointAction =
new QAction(tr("Add Breakpoint..."), this);
auto addBreakpointAction = new QAction(tr("Add Breakpoint..."), this);
menu.addAction(addBreakpointAction);
menu.addAction(deleteAction);
......@@ -825,28 +819,27 @@ void BreakTreeView::contextMenuEvent(QContextMenuEvent *ev)
QAction *act = menu.exec(ev->globalPos());
if (act == deleteAction)
deleteBreakpoints(selectedIds);
deleteBreakpoints(selectedItems);
else if (act == deleteAllAction)
deleteAllBreakpoints();
else if (act == deleteByFileAction)
deleteBreakpoints(breakpointsInFile);
else if (act == editBreakpointAction)
editBreakpoints(selectedIds);
editBreakpoints(selectedItems);
else if (act == associateBreakpointAction)
associateBreakpoint(selectedIds, threadId);
associateBreakpoint(selectedItems, threadId);
else if (act == synchronizeAction)
; //synchronizeBreakpoints();
else if (act == toggleEnabledAction)
setBreakpointsEnabled(selectedIds, !enabled);
setBreakpointsEnabled(selectedItems, !enabled);
else if (act == addBreakpointAction)
addBreakpoint();
}
void BreakTreeView::setBreakpointsEnabled(const BreakpointModelIds &ids, bool enabled)
void BreakTreeView::setBreakpointsEnabled(const Breakpoints &bps, bool enabled)
{
BreakHandler *handler = breakHandler();
foreach (const BreakpointModelId id, ids)
handler->setEnabled(id, enabled);
foreach (Breakpoint b, bps)
b.setEnabled(enabled);
}
void BreakTreeView::deleteAllBreakpoints()
......@@ -857,55 +850,56 @@ void BreakTreeView::deleteAllBreakpoints()
"from all files in the current session?"),
Core::ICore::settings(),
QLatin1String("RemoveAllBreakpoints")) == QDialogButtonBox::Yes)
deleteBreakpoints(breakHandler()->allBreakpointIds());
deleteBreakpoints(breakHandler()->allBreakpoints());
}
void BreakTreeView::deleteBreakpoints(const BreakpointModelIds &ids)
void BreakTreeView::deleteBreakpoints(const Breakpoints &bps)
{
BreakHandler *handler = breakHandler();
foreach (const BreakpointModelId id, ids)
handler->removeBreakpoint(id);
foreach (Breakpoint bp, bps)
bp.removeBreakpoint();
}
void BreakTreeView::editBreakpoint(BreakpointModelId id, QWidget *parent)
void BreakTreeView::editBreakpoint(Breakpoint bp, QWidget *parent)
{
BreakpointParameters data = breakHandler()->breakpointData(id);
BreakpointParameters data = bp.parameters();
BreakpointParts parts = NoParts;
BreakpointDialog dialog(id, parent);
if (dialog.showDialog(&data, &parts))
breakHandler()->changeBreakpointData(id, data, parts);
BreakpointDialog dialog(bp, parent);
if (!dialog.showDialog(&data, &parts))
return;
bp.changeBreakpointData(data);
}
void BreakTreeView::addBreakpoint()
{
BreakpointParameters data(BreakpointByFileAndLine);
BreakpointParts parts = NoParts;
BreakpointDialog dialog(BreakpointModelId(), this);
BreakpointDialog dialog(Breakpoint(), this);
dialog.setWindowTitle(tr("Add Breakpoint"));
if (dialog.showDialog(&data, &parts))
breakHandler()->appendBreakpoint(data);
}
void BreakTreeView::editBreakpoints(const BreakpointModelIds &ids)
void BreakTreeView::editBreakpoints(const Breakpoints &bps)
{
QTC_ASSERT(!ids.isEmpty(), return);
QTC_ASSERT(!bps.isEmpty(), return);
const BreakpointModelId id = ids.at(0);
const Breakpoint bp = bps.at(0);
if (ids.size() == 1) {
editBreakpoint(id, this);
if (bps.size() == 1) {
editBreakpoint(bp, this);
return;
}
// This allows to change properties of multiple breakpoints at a time.
BreakHandler *handler = breakHandler();
if (!bp)
return;
MultiBreakPointsDialog dialog;
const QString oldCondition = QString::fromLatin1(handler->condition(id));
dialog.setCondition(oldCondition);
const int oldIgnoreCount = handler->ignoreCount(id);
dialog.setIgnoreCount(oldIgnoreCount);
const int oldThreadSpec = handler->threadSpec(id);
dialog.setThreadSpec(oldThreadSpec);
dialog.setCondition(QString::fromLatin1(bp.condition()));
dialog.setIgnoreCount(bp.ignoreCount());
dialog.setThreadSpec(bp.threadSpec());
if (dialog.exec() == QDialog::Rejected)
return;
......@@ -914,27 +908,27 @@ void BreakTreeView::editBreakpoints(const BreakpointModelIds &ids)
const int newIgnoreCount = dialog.ignoreCount();
const int newThreadSpec = dialog.threadSpec();
if (newCondition == oldCondition && newIgnoreCount == oldIgnoreCount
&& newThreadSpec == oldThreadSpec)
return;