Debugger: Remove some uses of semi-global currentEngine()

Make use of recent TreeModel improvements in various
tool views, push more operations into the engine-
owned data models, specifically context menu creation.

Change-Id: I479c97102b9fb81611c6461c6df1cec59295179a
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
hjk
2016-07-18 12:36:31 +02:00
committed by hjk
parent 84f1466b01
commit 2d79bdc29c
36 changed files with 2636 additions and 3364 deletions

View File

@@ -248,6 +248,7 @@ BaseTreeView::BaseTreeView(QWidget *parent)
setSelectionMode(QAbstractItemView::ExtendedSelection); setSelectionMode(QAbstractItemView::ExtendedSelection);
setUniformRowHeights(true); setUniformRowHeights(true);
setItemDelegate(new BaseTreeViewDelegate(this)); setItemDelegate(new BaseTreeViewDelegate(this));
setAlternatingRowColors(false);
QHeaderView *h = header(); QHeaderView *h = header();
h->setDefaultAlignment(Qt::AlignLeft); h->setDefaultAlignment(Qt::AlignLeft);
@@ -303,6 +304,13 @@ void BaseTreeView::setModel(QAbstractItemModel *m)
connect(model(), c[i].qsignal, c[i].receiver, c[i].qslot); connect(model(), c[i].qsignal, c[i].receiver, c[i].qslot);
} }
d->restoreState(); d->restoreState();
QVariant delegateBlob = m->data(QModelIndex(), ItemDelegateRole);
if (delegateBlob.isValid()) {
auto delegate = delegateBlob.value<QAbstractItemDelegate *>();
delegate->setParent(this);
setItemDelegate(delegate);
}
} }
} }
@@ -314,6 +322,44 @@ void BaseTreeView::mousePressEvent(QMouseEvent *ev)
d->toggleColumnWidth(columnAt(ev->x())); d->toggleColumnWidth(columnAt(ev->x()));
} }
void BaseTreeView::contextMenuEvent(QContextMenuEvent *ev)
{
QPoint pos = ev->pos();
QModelIndex index = indexAt(pos);
if (!model()->setData(index, describeEvent(ev, pos, index), ItemViewEventRole))
TreeView::contextMenuEvent(ev);
}
void BaseTreeView::keyPressEvent(QKeyEvent *ev)
{
if (!model()->setData(QModelIndex(), describeEvent(ev), ItemViewEventRole))
TreeView::keyPressEvent(ev);
}
void BaseTreeView::dragEnterEvent(QDragEnterEvent *ev)
{
if (!model()->setData(QModelIndex(), describeEvent(ev), ItemViewEventRole))
TreeView::dragEnterEvent(ev);
}
void BaseTreeView::dropEvent(QDropEvent *ev)
{
if (!model()->setData(QModelIndex(), describeEvent(ev), ItemViewEventRole))
TreeView::dropEvent(ev);
}
void BaseTreeView::dragMoveEvent(QDragMoveEvent *ev)
{
if (!model()->setData(QModelIndex(), describeEvent(ev), ItemViewEventRole))
TreeView::dragMoveEvent(ev);
}
void BaseTreeView::mouseDoubleClickEvent(QMouseEvent *ev)
{
if (!model()->setData(QModelIndex(), describeEvent(ev), ItemViewEventRole))
TreeView::mouseDoubleClickEvent(ev);
}
void BaseTreeView::showEvent(QShowEvent *ev) void BaseTreeView::showEvent(QShowEvent *ev)
{ {
emit aboutToShow(); emit aboutToShow();
@@ -346,6 +392,35 @@ void BaseTreeView::hideProgressIndicator()
d->m_progressIndicator->hide(); d->m_progressIndicator->hide();
} }
QVariant BaseTreeView::describeEvent(QEvent *ev, const QPoint &pos, const QModelIndex &idx)
{
ItemViewEvent event;
event.m_view = this;
event.m_event = ev;
event.m_pos = pos;
event.m_index = idx;
QItemSelectionModel *selection = selectionModel();
event.m_selectedRows = selection->selectedRows();
if (event.m_selectedRows.isEmpty()) {
QModelIndex current = selection->currentIndex();
if (current.isValid())
event.m_selectedRows.append(current);
}
return QVariant::fromValue(event);
}
void BaseTreeView::rowActivated(const QModelIndex &index)
{
model()->setData(index, QVariant(), ItemActivatedRole);
}
void BaseTreeView::rowClicked(const QModelIndex &index)
{
model()->setData(index, QVariant(), ItemClickedRole);
}
void BaseTreeView::setSettings(QSettings *settings, const QByteArray &key) void BaseTreeView::setSettings(QSettings *settings, const QByteArray &key)
{ {
QTC_ASSERT(!d->m_settings, qDebug() << "DUPLICATED setSettings" << key); QTC_ASSERT(!d->m_settings, qDebug() << "DUPLICATED setSettings" << key);
@@ -354,16 +429,9 @@ void BaseTreeView::setSettings(QSettings *settings, const QByteArray &key)
d->readSettings(); d->readSettings();
} }
QModelIndexList BaseTreeView::activeRows() const QModelIndexList ItemViewEvent::currentOrSelectedRows() const
{ {
QItemSelectionModel *selection = selectionModel(); return m_selectedRows.isEmpty() ? QModelIndexList() << m_index : m_selectedRows;
QModelIndexList indices = selection->selectedRows();
if (indices.isEmpty()) {
QModelIndex current = selection->currentIndex();
if (current.isValid())
indices.append(current);
}
return indices;
} }
} // namespace Utils } // namespace Utils

View File

@@ -42,20 +42,31 @@ class QTCREATOR_UTILS_EXPORT BaseTreeView : public TreeView
Q_OBJECT Q_OBJECT
public: public:
enum { ExtraIndicesForColumnWidth = 12734 }; enum {
ExtraIndicesForColumnWidth = 12734,
ItemViewEventRole = Qt::UserRole + 12735,
ItemActivatedRole,
ItemClickedRole,
ItemDelegateRole,
};
BaseTreeView(QWidget *parent = 0); BaseTreeView(QWidget *parent = 0);
~BaseTreeView(); ~BaseTreeView();
void setSettings(QSettings *settings, const QByteArray &key); void setSettings(QSettings *settings, const QByteArray &key);
QModelIndexList activeRows() const;
virtual void rowActivated(const QModelIndex &) {}
virtual void rowClicked(const QModelIndex &) {}
void setModel(QAbstractItemModel *model) override; void setModel(QAbstractItemModel *model) override;
void mousePressEvent(QMouseEvent *ev) override; void mousePressEvent(QMouseEvent *ev) override;
void contextMenuEvent(QContextMenuEvent *ev) override;
void showEvent(QShowEvent *ev) override; void showEvent(QShowEvent *ev) override;
void keyPressEvent(QKeyEvent *ev) override;
void dragEnterEvent(QDragEnterEvent *ev) override;
void dropEvent(QDropEvent *ev) override;
void dragMoveEvent(QDragMoveEvent *ev) override;
void mouseDoubleClickEvent(QMouseEvent *ev) override;
void showProgressIndicator(); void showProgressIndicator();
void hideProgressIndicator(); void hideProgressIndicator();
@@ -63,11 +74,70 @@ public:
signals: signals:
void aboutToShow(); void aboutToShow();
public slots:
void setAlternatingRowColorsHelper(bool on) { setAlternatingRowColors(on); }
private: private:
QVariant describeEvent(QEvent *ev,
const QPoint &pos = QPoint(),
const QModelIndex &idx = QModelIndex());
void rowActivated(const QModelIndex &index);
void rowClicked(const QModelIndex &index);
Internal::BaseTreeViewPrivate *d; Internal::BaseTreeViewPrivate *d;
}; };
template <typename Event> struct EventCode;
template <> struct EventCode<QDragEnterEvent> { enum { code = QEvent::DragEnter }; };
template <> struct EventCode<QDragLeaveEvent> { enum { code = QEvent::DragLeave }; };
template <> struct EventCode<QDragMoveEvent> { enum { code = QEvent::DragMove }; };
template <> struct EventCode<QDropEvent> { enum { code = QEvent::Drop }; };
template <> struct EventCode<QContextMenuEvent> { enum { code = QEvent::ContextMenu }; };
template <> struct EventCode<QMouseEvent> { enum { code = QEvent::MouseButtonPress }; };
template <> struct EventCode<QKeyEvent> { enum { code = QEvent::KeyPress }; };
template <class T> T *checkEventType(QEvent *ev)
{
const int cc = EventCode<T>::code;
const int tt = ev->type();
if (cc == tt)
return static_cast<T *>(ev);
if (cc == QEvent::MouseButtonPress) {
if (tt == QEvent::MouseButtonDblClick || tt == QEvent::MouseButtonRelease || tt == QEvent::MouseMove)
return static_cast<T *>(ev);
}
if (cc == QEvent::KeyPress && tt == QEvent::KeyRelease)
return static_cast<T *>(ev);
return nullptr;
}
class QTCREATOR_UTILS_EXPORT ItemViewEvent
{
public:
ItemViewEvent() {}
template <class T> T *as() const {
return checkEventType<T>(m_event);
}
template <class T> T *as(QEvent::Type t) const {
return m_event->type() == t ? as<T>() : nullptr;
}
QEvent::Type type() const { return m_event->type(); }
BaseTreeView *view() const { return m_view; }
QPoint pos() const { return m_pos; }
QPoint globalPos() const { return m_view->mapToGlobal(m_pos); }
QModelIndex index() const { return m_index; }
QModelIndexList selectedRows() const { return m_selectedRows; }
QModelIndexList currentOrSelectedRows() const;
private:
friend class BaseTreeView;
QEvent *m_event = nullptr;
BaseTreeView *m_view = nullptr;
QPoint m_pos;
QModelIndex m_index;
QModelIndexList m_selectedRows;
};
} // namespace Utils } // namespace Utils
Q_DECLARE_METATYPE(Utils::ItemViewEvent);

View File

@@ -35,14 +35,20 @@
#include <coreplugin/coreconstants.h> #include <coreplugin/coreconstants.h>
#include <coreplugin/coreplugin.h> #include <coreplugin/coreplugin.h>
#include <coreplugin/editormanager/editormanager.h> #include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/icore.h>
#include <coreplugin/idocument.h> #include <coreplugin/idocument.h>
#include <extensionsystem/invoker.h> #include <extensionsystem/invoker.h>
#include <texteditor/textmark.h> #include <texteditor/textmark.h>
#include <texteditor/texteditor.h> #include <texteditor/texteditor.h>
#include <utils/hostosinfo.h>
#include <utils/qtcassert.h> #include <utils/basetreeview.h>
#include <utils/checkablemessagebox.h>
#include <utils/fileutils.h> #include <utils/fileutils.h>
#include <utils/hostosinfo.h>
#include <utils/pathchooser.h>
#include <utils/qtcassert.h>
#include <utils/savedaction.h>
#include <utils/theme/theme.h> #include <utils/theme/theme.h>
#if USE_BREAK_MODEL_TEST #if USE_BREAK_MODEL_TEST
@@ -52,6 +58,13 @@
#include <QTimerEvent> #include <QTimerEvent>
#include <QDir> #include <QDir>
#include <QDebug> #include <QDebug>
#include <QSpinBox>
#include <QStyledItemDelegate>
#include <QComboBox>
#include <QGroupBox>
#include <QCheckBox>
#include <QFormLayout>
#include <QMenu>
using namespace Core; using namespace Core;
using namespace Utils; using namespace Utils;
@@ -66,11 +79,11 @@ public:
{ {
if (role == Qt::DisplayRole) { if (role == Qt::DisplayRole) {
switch (column) { switch (column) {
case 0: case BreakpointNumberColumn:
return params.id.toString(); return params.id.toString();
case 1: case BreakpointFunctionColumn:
return params.functionName; return params.functionName;
case 4: case BreakpointAddressColumn:
if (params.address) if (params.address)
return QString::fromLatin1("0x%1").arg(params.address, 0, 16); return QString::fromLatin1("0x%1").arg(params.address, 0, 16);
} }
@@ -107,6 +120,7 @@ public:
int markerLineNumber() const; int markerLineNumber() const;
bool needsChange() const; bool needsChange() const;
private: private:
friend class BreakHandler; friend class BreakHandler;
friend class Breakpoint; friend class Breakpoint;
@@ -201,9 +215,9 @@ static QString stateToString(BreakpointState state)
return BreakHandler::tr("<invalid state>"); return BreakHandler::tr("<invalid state>");
} }
static QString msgBreakpointAtSpecialFunc(const char *func) static QString msgBreakpointAtSpecialFunc(const QString &func)
{ {
return BreakHandler::tr("Breakpoint at \"%1\"").arg(QString::fromLatin1(func)); return BreakHandler::tr("Breakpoint at \"%1\"").arg(func);
} }
static QString typeToString(BreakpointType type) static QString typeToString(BreakpointType type)
@@ -244,12 +258,644 @@ static QString typeToString(BreakpointType type)
return BreakHandler::tr("Unknown Breakpoint Type"); return BreakHandler::tr("Unknown Breakpoint Type");
} }
class LeftElideDelegate : public QStyledItemDelegate
{
public:
LeftElideDelegate() {}
void paint(QPainter *pain, const QStyleOptionViewItem &option, const QModelIndex &index) const override
{
QStyleOptionViewItem opt = option;
opt.textElideMode = Qt::ElideLeft;
QStyledItemDelegate::paint(pain, opt, index);
}
};
class SmallTextEdit : public QTextEdit
{
public:
explicit SmallTextEdit(QWidget *parent) : QTextEdit(parent) {}
QSize sizeHint() const { return QSize(QTextEdit::sizeHint().width(), 100); }
QSize minimumSizeHint() const { return sizeHint(); }
};
///////////////////////////////////////////////////////////////////////
//
// BreakpointDialog: Show a dialog for editing breakpoints. Shows controls
// for the file-and-line, function and address parameters depending on the
// breakpoint type. The controls not applicable to the current type
// (say function name for file-and-line) are disabled and cleared out.
// However,the values are saved and restored once the respective mode
// is again chosen, which is done using m_savedParameters and
// setters/getters taking the parts mask enumeration parameter.
//
///////////////////////////////////////////////////////////////////////
class BreakpointDialog : public QDialog
{
Q_DECLARE_TR_FUNCTIONS(Debugger::Internal::BreakHandler)
public:
explicit BreakpointDialog(Breakpoint b, QWidget *parent = 0);
bool showDialog(BreakpointParameters *data, BreakpointParts *parts);
void setParameters(const BreakpointParameters &data);
BreakpointParameters parameters() const;
void typeChanged(int index);
private:
void setPartsEnabled(unsigned partsMask);
void clearOtherParts(unsigned partsMask);
void getParts(unsigned partsMask, BreakpointParameters *data) const;
void setParts(unsigned partsMask, const BreakpointParameters &data);
void setType(BreakpointType type);
BreakpointType type() const;
unsigned m_enabledParts;
BreakpointParameters m_savedParameters;
BreakpointType m_previousType;
bool m_firstTypeChange;
QLabel *m_labelType;
QComboBox *m_comboBoxType;
QLabel *m_labelFileName;
Utils::PathChooser *m_pathChooserFileName;
QLabel *m_labelLineNumber;
QLineEdit *m_lineEditLineNumber;
QLabel *m_labelEnabled;
QCheckBox *m_checkBoxEnabled;
QLabel *m_labelAddress;
QLineEdit *m_lineEditAddress;
QLabel *m_labelExpression;
QLineEdit *m_lineEditExpression;
QLabel *m_labelFunction;
QLineEdit *m_lineEditFunction;
QLabel *m_labelTracepoint;
QCheckBox *m_checkBoxTracepoint;
QLabel *m_labelOneShot;
QCheckBox *m_checkBoxOneShot;
QLabel *m_labelUseFullPath;
QLabel *m_labelModule;
QLineEdit *m_lineEditModule;
QLabel *m_labelCommands;
QTextEdit *m_textEditCommands;
QComboBox *m_comboBoxPathUsage;
QLabel *m_labelMessage;
QLineEdit *m_lineEditMessage;
QLabel *m_labelCondition;
QLineEdit *m_lineEditCondition;
QLabel *m_labelIgnoreCount;
QSpinBox *m_spinBoxIgnoreCount;
QLabel *m_labelThreadSpec;
QLineEdit *m_lineEditThreadSpec;
QDialogButtonBox *m_buttonBox;
};
BreakpointDialog::BreakpointDialog(Breakpoint b, QWidget *parent)
: QDialog(parent), m_enabledParts(~0), m_previousType(UnknownBreakpointType),
m_firstTypeChange(true)
{
setWindowTitle(tr("Edit Breakpoint Properties"));
auto groupBoxBasic = new QGroupBox(tr("Basic"), this);
// Match BreakpointType (omitting unknown type).
const QStringList types = {
tr("File name and line number"),
tr("Function name"),
tr("Break on memory address"),
tr("Break when C++ exception is thrown"),
tr("Break when C++ exception is caught"),
tr("Break when function \"main\" starts"),
tr("Break when a new process is forked"),
tr("Break when a new process is executed"),
tr("Break when a system call is executed"),
tr("Break on data access at fixed address"),
tr("Break on data access at address given by expression"),
tr("Break on QML signal emit"),
tr("Break when JavaScript exception is thrown")
};
// We don't list UnknownBreakpointType, so 1 less:
QTC_CHECK(types.size() + 1 == LastBreakpointType);
m_comboBoxType = new QComboBox(groupBoxBasic);
m_comboBoxType->setMaxVisibleItems(20);
m_comboBoxType->addItems(types);
m_labelType = new QLabel(tr("Breakpoint &type:"), groupBoxBasic);
m_labelType->setBuddy(m_comboBoxType);
m_pathChooserFileName = new PathChooser(groupBoxBasic);
m_pathChooserFileName->setHistoryCompleter(QLatin1String("Debugger.Breakpoint.File.History"));
m_pathChooserFileName->setExpectedKind(PathChooser::File);
m_labelFileName = new QLabel(tr("&File name:"), groupBoxBasic);
m_labelFileName->setBuddy(m_pathChooserFileName);
m_lineEditLineNumber = new QLineEdit(groupBoxBasic);
m_labelLineNumber = new QLabel(tr("&Line number:"), groupBoxBasic);
m_labelLineNumber->setBuddy(m_lineEditLineNumber);
m_checkBoxEnabled = new QCheckBox(groupBoxBasic);
m_labelEnabled = new QLabel(tr("&Enabled:"), groupBoxBasic);
m_labelEnabled->setBuddy(m_checkBoxEnabled);
m_lineEditAddress = new QLineEdit(groupBoxBasic);
m_labelAddress = new QLabel(tr("&Address:"), groupBoxBasic);
m_labelAddress->setBuddy(m_lineEditAddress);
m_lineEditExpression = new QLineEdit(groupBoxBasic);
m_labelExpression = new QLabel(tr("&Expression:"), groupBoxBasic);
m_labelExpression->setBuddy(m_lineEditExpression);
m_lineEditFunction = new QLineEdit(groupBoxBasic);
m_labelFunction = new QLabel(tr("Fun&ction:"), groupBoxBasic);
m_labelFunction->setBuddy(m_lineEditFunction);
auto groupBoxAdvanced = new QGroupBox(tr("Advanced"), this);
m_checkBoxTracepoint = new QCheckBox(groupBoxAdvanced);
m_labelTracepoint = new QLabel(tr("T&racepoint only:"), groupBoxAdvanced);
m_labelTracepoint->setBuddy(m_checkBoxTracepoint);
m_checkBoxOneShot = new QCheckBox(groupBoxAdvanced);
m_labelOneShot = new QLabel(tr("&One shot only:"), groupBoxAdvanced);
m_labelOneShot->setBuddy(m_checkBoxOneShot);
const QString pathToolTip =
tr("<p>Determines how the path is specified "
"when setting breakpoints:</p><ul>"
"<li><i>Use Engine Default</i>: Preferred setting of the "
"debugger engine.</li>"
"<li><i>Use Full Path</i>: Pass full path, avoiding ambiguities "
"should files of the same name exist in several modules. "
"This is the engine default for CDB and LLDB.</li>"
"<li><i>Use File Name</i>: Pass the file name only. This is "
"useful when using a source tree whose location does "
"not match the one used when building the modules. "
"It is the engine default for GDB as using full paths can "
"be slow with this engine.</li></ul>");
m_comboBoxPathUsage = new QComboBox(groupBoxAdvanced);
m_comboBoxPathUsage->addItem(tr("Use Engine Default"));
m_comboBoxPathUsage->addItem(tr("Use Full Path"));
m_comboBoxPathUsage->addItem(tr("Use File Name"));
m_comboBoxPathUsage->setToolTip(pathToolTip);
m_labelUseFullPath = new QLabel(tr("Pat&h:"), groupBoxAdvanced);
m_labelUseFullPath->setBuddy(m_comboBoxPathUsage);
m_labelUseFullPath->setToolTip(pathToolTip);
const QString moduleToolTip =
tr("<p>Specifying the module (base name of the library or executable) "
"for function or file type breakpoints can significantly speed up "
"debugger start-up times (CDB, LLDB).");
m_lineEditModule = new QLineEdit(groupBoxAdvanced);
m_lineEditModule->setToolTip(moduleToolTip);
m_labelModule = new QLabel(tr("&Module:"), groupBoxAdvanced);
m_labelModule->setBuddy(m_lineEditModule);
m_labelModule->setToolTip(moduleToolTip);
const QString commandsToolTip =
tr("<p>Debugger commands to be executed when the breakpoint is hit. "
"This feature is only available for GDB.");
m_textEditCommands = new SmallTextEdit(groupBoxAdvanced);
m_textEditCommands->setToolTip(commandsToolTip);
m_labelCommands = new QLabel(tr("&Commands:"), groupBoxAdvanced);
m_labelCommands->setBuddy(m_textEditCommands);
m_labelCommands->setToolTip(commandsToolTip);
m_lineEditMessage = new QLineEdit(groupBoxAdvanced);
m_labelMessage = new QLabel(tr("&Message:"), groupBoxAdvanced);
m_labelMessage->setBuddy(m_lineEditMessage);
m_lineEditCondition = new QLineEdit(groupBoxAdvanced);
m_labelCondition = new QLabel(tr("C&ondition:"), groupBoxAdvanced);
m_labelCondition->setBuddy(m_lineEditCondition);
m_spinBoxIgnoreCount = new QSpinBox(groupBoxAdvanced);
m_spinBoxIgnoreCount->setMinimum(0);
m_spinBoxIgnoreCount->setMaximum(2147483647);
m_labelIgnoreCount = new QLabel(tr("&Ignore count:"), groupBoxAdvanced);
m_labelIgnoreCount->setBuddy(m_spinBoxIgnoreCount);
m_lineEditThreadSpec = new QLineEdit(groupBoxAdvanced);
m_labelThreadSpec = new QLabel(tr("&Thread specification:"), groupBoxAdvanced);
m_labelThreadSpec->setBuddy(m_lineEditThreadSpec);
m_buttonBox = new QDialogButtonBox(this);
m_buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
if (b) {
if (DebuggerEngine *engine = b.engine()) {
if (!engine->hasCapability(BreakConditionCapability))
m_enabledParts &= ~ConditionPart;
if (!engine->hasCapability(BreakModuleCapability))
m_enabledParts &= ~ModulePart;
if (!engine->hasCapability(TracePointCapability))
m_enabledParts &= ~TracePointPart;
}
}
auto basicLayout = new QFormLayout(groupBoxBasic);
basicLayout->addRow(m_labelType, m_comboBoxType);
basicLayout->addRow(m_labelFileName, m_pathChooserFileName);
basicLayout->addRow(m_labelLineNumber, m_lineEditLineNumber);
basicLayout->addRow(m_labelEnabled, m_checkBoxEnabled);
basicLayout->addRow(m_labelAddress, m_lineEditAddress);
basicLayout->addRow(m_labelExpression, m_lineEditExpression);
basicLayout->addRow(m_labelFunction, m_lineEditFunction);
basicLayout->addRow(m_labelOneShot, m_checkBoxOneShot);
auto advancedLeftLayout = new QFormLayout();
advancedLeftLayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
advancedLeftLayout->addRow(m_labelCondition, m_lineEditCondition);
advancedLeftLayout->addRow(m_labelIgnoreCount, m_spinBoxIgnoreCount);
advancedLeftLayout->addRow(m_labelThreadSpec, m_lineEditThreadSpec);
advancedLeftLayout->addRow(m_labelUseFullPath, m_comboBoxPathUsage);
advancedLeftLayout->addRow(m_labelModule, m_lineEditModule);
auto advancedRightLayout = new QFormLayout();
advancedRightLayout->setFieldGrowthPolicy(QFormLayout::ExpandingFieldsGrow);
advancedRightLayout->addRow(m_labelCommands, m_textEditCommands);
advancedRightLayout->addRow(m_labelTracepoint, m_checkBoxTracepoint);
advancedRightLayout->addRow(m_labelMessage, m_lineEditMessage);
auto horizontalLayout = new QHBoxLayout(groupBoxAdvanced);
horizontalLayout->addLayout(advancedLeftLayout);
horizontalLayout->addSpacing(15);
horizontalLayout->addLayout(advancedRightLayout);
auto verticalLayout = new QVBoxLayout(this);
verticalLayout->addWidget(groupBoxBasic);
verticalLayout->addSpacing(10);
verticalLayout->addWidget(groupBoxAdvanced);
verticalLayout->addSpacing(10);
verticalLayout->addWidget(m_buttonBox);
verticalLayout->setStretchFactor(groupBoxAdvanced, 10);
connect(m_comboBoxType, static_cast<void(QComboBox::*)(int)>(&QComboBox::activated),
this, &BreakpointDialog::typeChanged);
connect(m_buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(m_buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
}
void BreakpointDialog::setType(BreakpointType type)
{
const int comboIndex = type - 1; // Skip UnknownType.
if (comboIndex != m_comboBoxType->currentIndex() || m_firstTypeChange) {
m_comboBoxType->setCurrentIndex(comboIndex);
typeChanged(comboIndex);
m_firstTypeChange = false;
}
}
BreakpointType BreakpointDialog::type() const
{
const int type = m_comboBoxType->currentIndex() + 1; // Skip unknown type.
return static_cast<BreakpointType>(type);
}
void BreakpointDialog::setParameters(const BreakpointParameters &data)
{
m_savedParameters = data;
setType(data.type);
setParts(AllParts, data);
}
BreakpointParameters BreakpointDialog::parameters() const
{
BreakpointParameters data(type());
getParts(AllParts, &data);
return data;
}
void BreakpointDialog::setPartsEnabled(unsigned partsMask)
{
partsMask &= m_enabledParts;
m_labelFileName->setEnabled(partsMask & FileAndLinePart);
m_pathChooserFileName->setEnabled(partsMask & FileAndLinePart);
m_labelLineNumber->setEnabled(partsMask & FileAndLinePart);
m_lineEditLineNumber->setEnabled(partsMask & FileAndLinePart);
m_labelUseFullPath->setEnabled(partsMask & FileAndLinePart);
m_comboBoxPathUsage->setEnabled(partsMask & FileAndLinePart);
m_labelFunction->setEnabled(partsMask & FunctionPart);
m_lineEditFunction->setEnabled(partsMask & FunctionPart);
m_labelOneShot->setEnabled(partsMask & OneShotPart);
m_checkBoxOneShot->setEnabled(partsMask & OneShotPart);
m_labelAddress->setEnabled(partsMask & AddressPart);
m_lineEditAddress->setEnabled(partsMask & AddressPart);
m_labelExpression->setEnabled(partsMask & ExpressionPart);
m_lineEditExpression->setEnabled(partsMask & ExpressionPart);
m_labelCondition->setEnabled(partsMask & ConditionPart);
m_lineEditCondition->setEnabled(partsMask & ConditionPart);
m_labelIgnoreCount->setEnabled(partsMask & IgnoreCountPart);
m_spinBoxIgnoreCount->setEnabled(partsMask & IgnoreCountPart);
m_labelThreadSpec->setEnabled(partsMask & ThreadSpecPart);
m_lineEditThreadSpec->setEnabled(partsMask & ThreadSpecPart);
m_labelModule->setEnabled(partsMask & ModulePart);
m_lineEditModule->setEnabled(partsMask & ModulePart);
m_labelTracepoint->setEnabled(partsMask & TracePointPart);
m_labelTracepoint->hide();
m_checkBoxTracepoint->setEnabled(partsMask & TracePointPart);
m_checkBoxTracepoint->hide();
m_labelCommands->setEnabled(partsMask & CommandPart);
m_textEditCommands->setEnabled(partsMask & CommandPart);
m_labelMessage->setEnabled(partsMask & TracePointPart);
m_labelMessage->hide();
m_lineEditMessage->setEnabled(partsMask & TracePointPart);
m_lineEditMessage->hide();
}
void BreakpointDialog::clearOtherParts(unsigned partsMask)
{
const unsigned invertedPartsMask = ~partsMask;
if (invertedPartsMask & FileAndLinePart) {
m_pathChooserFileName->setPath(QString());
m_lineEditLineNumber->clear();
m_comboBoxPathUsage->setCurrentIndex(BreakpointPathUsageEngineDefault);
}
if (invertedPartsMask & FunctionPart)
m_lineEditFunction->clear();
if (invertedPartsMask & AddressPart)
m_lineEditAddress->clear();
if (invertedPartsMask & ExpressionPart)
m_lineEditExpression->clear();
if (invertedPartsMask & ConditionPart)
m_lineEditCondition->clear();
if (invertedPartsMask & IgnoreCountPart)
m_spinBoxIgnoreCount->clear();
if (invertedPartsMask & ThreadSpecPart)
m_lineEditThreadSpec->clear();
if (invertedPartsMask & ModulePart)
m_lineEditModule->clear();
if (partsMask & OneShotPart)
m_checkBoxOneShot->setChecked(false);
if (invertedPartsMask & CommandPart)
m_textEditCommands->clear();
if (invertedPartsMask & TracePointPart) {
m_checkBoxTracepoint->setChecked(false);
m_lineEditMessage->clear();
}
}
void BreakpointDialog::getParts(unsigned partsMask, BreakpointParameters *data) const
{
data->enabled = m_checkBoxEnabled->isChecked();
if (partsMask & FileAndLinePart) {
data->lineNumber = m_lineEditLineNumber->text().toInt();
data->pathUsage = static_cast<BreakpointPathUsage>(m_comboBoxPathUsage->currentIndex());
data->fileName = m_pathChooserFileName->path();
}
if (partsMask & FunctionPart)
data->functionName = m_lineEditFunction->text();
if (partsMask & AddressPart)
data->address = m_lineEditAddress->text().toULongLong(0, 0);
if (partsMask & ExpressionPart)
data->expression = m_lineEditExpression->text();
if (partsMask & ConditionPart)
data->condition = m_lineEditCondition->text();
if (partsMask & IgnoreCountPart)
data->ignoreCount = m_spinBoxIgnoreCount->text().toInt();
if (partsMask & ThreadSpecPart)
data->threadSpec =
BreakHandler::threadSpecFromDisplay(m_lineEditThreadSpec->text());
if (partsMask & ModulePart)
data->module = m_lineEditModule->text();
if (partsMask & OneShotPart)
data->oneShot = m_checkBoxOneShot->isChecked();
if (partsMask & CommandPart)
data->command = m_textEditCommands->toPlainText().trimmed();
if (partsMask & TracePointPart) {
data->tracepoint = m_checkBoxTracepoint->isChecked();
data->message = m_lineEditMessage->text();
}
}
void BreakpointDialog::setParts(unsigned mask, const BreakpointParameters &data)
{
m_checkBoxEnabled->setChecked(data.enabled);
m_comboBoxPathUsage->setCurrentIndex(data.pathUsage);
m_lineEditMessage->setText(data.message);
if (mask & FileAndLinePart) {
m_pathChooserFileName->setPath(data.fileName);
m_lineEditLineNumber->setText(QString::number(data.lineNumber));
}
if (mask & FunctionPart)
m_lineEditFunction->setText(data.functionName);
if (mask & AddressPart) {
if (data.address) {
m_lineEditAddress->setText(QString("0x%1").arg(data.address, 0, 16));
} else {
m_lineEditAddress->clear();
}
}
if (mask & ExpressionPart) {
if (!data.expression.isEmpty())
m_lineEditExpression->setText(data.expression);
else
m_lineEditExpression->clear();
}
if (mask & ConditionPart)
m_lineEditCondition->setText(data.condition);
if (mask & IgnoreCountPart)
m_spinBoxIgnoreCount->setValue(data.ignoreCount);
if (mask & ThreadSpecPart)
m_lineEditThreadSpec->
setText(BreakHandler::displayFromThreadSpec(data.threadSpec));
if (mask & ModulePart)
m_lineEditModule->setText(data.module);
if (mask & OneShotPart)
m_checkBoxOneShot->setChecked(data.oneShot);
if (mask & TracePointPart)
m_checkBoxTracepoint->setChecked(data.tracepoint);
if (mask & CommandPart)
m_textEditCommands->setPlainText(data.command);
}
void BreakpointDialog::typeChanged(int)
{
BreakpointType previousType = m_previousType;
const BreakpointType newType = type();
m_previousType = newType;
// Save current state.
switch (previousType) {
case UnknownBreakpointType:
case LastBreakpointType:
break;
case BreakpointByFileAndLine:
getParts(FileAndLinePart|ModulePart|AllConditionParts|TracePointPart|CommandPart, &m_savedParameters);
break;
case BreakpointByFunction:
getParts(FunctionPart|ModulePart|AllConditionParts|TracePointPart|CommandPart, &m_savedParameters);
break;
case BreakpointAtThrow:
case BreakpointAtCatch:
case BreakpointAtMain:
case BreakpointAtFork:
case BreakpointAtExec:
//case BreakpointAtVFork:
case BreakpointAtSysCall:
case BreakpointAtJavaScriptThrow:
break;
case BreakpointByAddress:
case WatchpointAtAddress:
getParts(AddressPart|AllConditionParts|TracePointPart|CommandPart, &m_savedParameters);
break;
case WatchpointAtExpression:
getParts(ExpressionPart|AllConditionParts|TracePointPart|CommandPart, &m_savedParameters);
break;
case BreakpointOnQmlSignalEmit:
getParts(FunctionPart, &m_savedParameters);
}
// Enable and set up new state from saved values.
switch (newType) {
case UnknownBreakpointType:
case LastBreakpointType:
break;
case BreakpointByFileAndLine:
setParts(FileAndLinePart|AllConditionParts|ModulePart|TracePointPart|CommandPart, m_savedParameters);
setPartsEnabled(FileAndLinePart|AllConditionParts|ModulePart|TracePointPart|CommandPart);
clearOtherParts(FileAndLinePart|AllConditionParts|ModulePart|TracePointPart|CommandPart);
break;
case BreakpointByFunction:
setParts(FunctionPart|AllConditionParts|ModulePart|TracePointPart|CommandPart, m_savedParameters);
setPartsEnabled(FunctionPart|AllConditionParts|ModulePart|TracePointPart|CommandPart);
clearOtherParts(FunctionPart|AllConditionParts|ModulePart|TracePointPart|CommandPart);
break;
case BreakpointAtThrow:
case BreakpointAtCatch:
case BreakpointAtFork:
case BreakpointAtExec:
//case BreakpointAtVFork:
case BreakpointAtSysCall:
clearOtherParts(AllConditionParts|ModulePart|TracePointPart|CommandPart);
setPartsEnabled(AllConditionParts|TracePointPart|CommandPart);
break;
case BreakpointAtJavaScriptThrow:
clearOtherParts(AllParts);
setPartsEnabled(0);
break;
case BreakpointAtMain:
m_lineEditFunction->setText("main"); // Just for display
clearOtherParts(0);
setPartsEnabled(0);
break;
case BreakpointByAddress:
case WatchpointAtAddress:
setParts(AddressPart|AllConditionParts|TracePointPart|CommandPart, m_savedParameters);
setPartsEnabled(AddressPart|AllConditionParts|TracePointPart|CommandPart);
clearOtherParts(AddressPart|AllConditionParts|TracePointPart|CommandPart);
break;
case WatchpointAtExpression:
setParts(ExpressionPart|AllConditionParts|TracePointPart|CommandPart, m_savedParameters);
setPartsEnabled(ExpressionPart|AllConditionParts|TracePointPart|CommandPart);
clearOtherParts(ExpressionPart|AllConditionParts|TracePointPart|CommandPart);
break;
case BreakpointOnQmlSignalEmit:
setParts(FunctionPart, m_savedParameters);
setPartsEnabled(FunctionPart);
clearOtherParts(FunctionPart);
}
}
bool BreakpointDialog::showDialog(BreakpointParameters *data,
BreakpointParts *parts)
{
setParameters(*data);
if (exec() != QDialog::Accepted)
return false;
// Check if changed.
const BreakpointParameters newParameters = parameters();
*parts = data->differencesTo(newParameters);
if (!*parts)
return false;
*data = newParameters;
return true;
}
// Dialog allowing changing properties of multiple breakpoints at a time.
class MultiBreakPointsDialog : public QDialog
{
Q_DECLARE_TR_FUNCTIONS(Debugger::Internal::BreakHandler)
public:
MultiBreakPointsDialog(QWidget *parent = 0);
QString condition() const { return m_lineEditCondition->text(); }
int ignoreCount() const { return m_spinBoxIgnoreCount->value(); }
int threadSpec() const
{ return BreakHandler::threadSpecFromDisplay(m_lineEditThreadSpec->text()); }
void setCondition(const QString &c) { m_lineEditCondition->setText(c); }
void setIgnoreCount(int i) { m_spinBoxIgnoreCount->setValue(i); }
void setThreadSpec(int t)
{ return m_lineEditThreadSpec->setText(BreakHandler::displayFromThreadSpec(t)); }
private:
QLineEdit *m_lineEditCondition;
QSpinBox *m_spinBoxIgnoreCount;
QLineEdit *m_lineEditThreadSpec;
QDialogButtonBox *m_buttonBox;
};
MultiBreakPointsDialog::MultiBreakPointsDialog(QWidget *parent) :
QDialog(parent)
{
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
setWindowTitle(tr("Edit Breakpoint Properties"));
m_lineEditCondition = new QLineEdit(this);
m_spinBoxIgnoreCount = new QSpinBox(this);
m_spinBoxIgnoreCount->setMinimum(0);
m_spinBoxIgnoreCount->setMaximum(2147483647);
m_lineEditThreadSpec = new QLineEdit(this);
m_buttonBox = new QDialogButtonBox(this);
m_buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
auto formLayout = new QFormLayout;
if (currentEngine()->hasCapability(BreakConditionCapability))
formLayout->addRow(tr("&Condition:"), m_lineEditCondition);
formLayout->addRow(tr("&Ignore count:"), m_spinBoxIgnoreCount);
formLayout->addRow(tr("&Thread specification:"), m_lineEditThreadSpec);
auto verticalLayout = new QVBoxLayout(this);
verticalLayout->addLayout(formLayout);
verticalLayout->addWidget(m_buttonBox);
connect(m_buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(m_buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
}
BreakHandler::BreakHandler() BreakHandler::BreakHandler()
: m_syncTimerId(-1) : m_syncTimerId(-1)
{ {
qRegisterMetaType<BreakpointModelId>(); qRegisterMetaType<BreakpointModelId>();
TextEditor::TextMark::setCategoryColor(Constants::TEXT_MARK_CATEGORY_BREAKPOINT, TextEditor::TextMark::setCategoryColor(Constants::TEXT_MARK_CATEGORY_BREAKPOINT,
Utils::Theme::Debugger_Breakpoint_TextMarkColor); Theme::Debugger_Breakpoint_TextMarkColor);
#if USE_BREAK_MODEL_TEST #if USE_BREAK_MODEL_TEST
new ModelTest(this, 0); new ModelTest(this, 0);
@@ -339,9 +985,15 @@ Breakpoint BreakHandler::findBreakpointByFileAndLine(const QString &fileName,
Breakpoint BreakHandler::breakpointById(BreakpointModelId id) const Breakpoint BreakHandler::breakpointById(BreakpointModelId id) const
{ {
return Breakpoint(findFirstLevelItem([id](BreakpointItem *b) { return Breakpoint(findFirstLevelItem([id](BreakpointItem *b) { return b->m_id == id; }));
return b->m_id == id; }
}));
QVariant BreakHandler::data(const QModelIndex &idx, int role) const
{
if (role == BaseTreeView::ItemDelegateRole)
return QVariant::fromValue(new LeftElideDelegate);
return BreakModel::data(idx, role);
} }
void BreakHandler::deletionHelper(BreakpointModelId id) void BreakHandler::deletionHelper(BreakpointModelId id)
@@ -604,7 +1256,7 @@ QVariant BreakpointItem::data(int column, int role) const
if (role == Qt::DisplayRole) { if (role == Qt::DisplayRole) {
const quint64 address = orig ? m_params.address : m_response.address; const quint64 address = orig ? m_params.address : m_response.address;
if (address) if (address)
return QString::fromLatin1("0x%1").arg(address, 0, 16); return QString("0x%1").arg(address, 0, 16);
return QVariant(); return QVariant();
} }
break; break;
@@ -1243,6 +1895,218 @@ void Breakpoint::changeBreakpointData(const BreakpointParameters &params)
} }
} }
bool BreakHandler::setData(const QModelIndex &idx, const QVariant &value, int role)
{
if (role == BaseTreeView::ItemActivatedRole) {
if (Breakpoint bp = findBreakpointByIndex(idx))
bp.gotoLocation();
return true;
}
if (role == BaseTreeView::ItemViewEventRole) {
ItemViewEvent ev = value.value<ItemViewEvent>();
if (ev.as<QContextMenuEvent>())
return contextMenuEvent(ev);
if (auto kev = ev.as<QKeyEvent>(QEvent::KeyPress)) {
if (kev->key() == Qt::Key_Delete) {
QModelIndexList si = ev.currentOrSelectedRows();
const Breakpoints ids = findBreakpointsByIndex(si);
// int row = qMin(rowCount() - ids.size() - 1, idx.row());
deleteBreakpoints(ids);
// setCurrentIndex(index(row, 0)); FIXME
return true;
}
if (kev->key() == Qt::Key_Space) {
const QModelIndexList selectedIds = ev.selectedRows();
if (!selectedIds.isEmpty()) {
const Breakpoints items = findBreakpointsByIndex(selectedIds);
const bool isEnabled = items.isEmpty() || items.at(0).isEnabled();
setBreakpointsEnabled(items, !isEnabled);
// FIXME
// foreach (const QModelIndex &id, selectedIds)
// update(id);
return true;
}
}
}
if (ev.as<QMouseEvent>(QEvent::MouseButtonDblClick)) {
if (Breakpoint b = findBreakpointByIndex(idx)) {
if (idx.column() >= BreakpointAddressColumn)
editBreakpoints({ b }, ev.view());
} else {
addBreakpoint();
}
return true;
}
}
return false;
}
bool BreakHandler::contextMenuEvent(const ItemViewEvent &ev)
{
const QModelIndexList selectedIndices = ev.selectedRows();
const Breakpoints selectedItems = findBreakpointsByIndex(selectedIndices);
const bool enabled = selectedItems.isEmpty() || selectedItems.at(0).isEnabled();
auto menu = new QMenu;
addAction(menu, tr("Add Breakpoint..."), true, [this] { addBreakpoint(); });
addAction(menu, tr("Delete Selected Breakpoints"),
!selectedItems.isEmpty(),
[this, selectedItems] { deleteBreakpoints(selectedItems); });
addAction(menu, tr("Edit Selected Breakpoints..."),
!selectedItems.isEmpty(),
[this, selectedItems, ev] { editBreakpoints(selectedItems, ev.view()); });
// FIXME BP: m_engine->threadsHandler()->currentThreadId();
int threadId = 0;
addAction(menu,
threadId == -1 ? tr("Associate Breakpoint With All Threads")
: tr("Associate Breakpoint With Thread %1").arg(threadId),
!selectedItems.isEmpty(),
[this, selectedItems, threadId] {
for (Breakpoint bp : selectedItems)
bp.setThreadSpec(threadId);
});
addAction(menu,
selectedItems.size() > 1
? enabled ? tr("Disable Selected Breakpoints") : tr("Enable Selected Breakpoints")
: enabled ? tr("Disable Breakpoint") : tr("Enable Breakpoint"),
!selectedItems.isEmpty(),
[this, selectedItems, enabled] { setBreakpointsEnabled(selectedItems, !enabled); });
menu->addSeparator();
addAction(menu, tr("Delete All Breakpoints"),
rowCount() > 0,
[this] { deleteAllBreakpoints(); });
// Delete by file: Find indices of breakpoints of the same file.
BreakpointItem *item = firstLevelItemForIndex(ev.index());
Breakpoints breakpointsInFile;
QString file;
if (item) {
const QModelIndex index = ev.index().sibling(ev.index().row(), BreakpointFileColumn);
if (!file.isEmpty()) {
for (int i = 0; i != rowCount(); ++i)
if (index.data().toString() == file)
breakpointsInFile.append(findBreakpointByIndex(index));
}
}
addAction(menu, tr("Delete Breakpoints of \"%1\"").arg(file),
tr("Delete Breakpoints of File"),
breakpointsInFile.size() > 1,
[this, breakpointsInFile] { deleteBreakpoints(breakpointsInFile); });
menu->addSeparator();
addAction(menu, tr("Synchronize Breakpoints"),
Internal::hasSnapshots(),
[this] { Internal::synchronizeBreakpoints(); });
menu->addSeparator();
menu->addAction(action(UseToolTipsInBreakpointsView));
if (currentEngine()->hasCapability(MemoryAddressCapability))
menu->addAction(action(UseAddressInBreakpointsView));
menu->addSeparator();
menu->addAction(action(SettingsDialog));
menu->popup(ev.globalPos());
return true;
}
void BreakHandler::setBreakpointsEnabled(const Breakpoints &bps, bool enabled)
{
foreach (Breakpoint b, bps)
b.setEnabled(enabled);
}
void BreakHandler::deleteAllBreakpoints()
{
QDialogButtonBox::StandardButton pressed =
CheckableMessageBox::doNotAskAgainQuestion(ICore::dialogParent(),
tr("Remove All Breakpoints"),
tr("Are you sure you want to remove all breakpoints "
"from all files in the current session?"),
ICore::settings(),
"RemoveAllBreakpoints");
if (pressed == QDialogButtonBox::Yes)
deleteBreakpoints(breakHandler()->allBreakpoints());
}
void BreakHandler::deleteBreakpoints(const Breakpoints &bps)
{
foreach (Breakpoint bp, bps)
bp.removeBreakpoint();
}
void BreakHandler::editBreakpoint(Breakpoint bp, QWidget *parent)
{
BreakpointParameters data = bp.parameters();
BreakpointParts parts = NoParts;
BreakpointDialog dialog(bp, parent);
if (!dialog.showDialog(&data, &parts))
return;
bp.changeBreakpointData(data);
}
void BreakHandler::addBreakpoint()
{
BreakpointParameters data(BreakpointByFileAndLine);
BreakpointParts parts = NoParts;
BreakpointDialog dialog(Breakpoint(), ICore::dialogParent());
dialog.setWindowTitle(tr("Add Breakpoint"));
if (dialog.showDialog(&data, &parts))
appendBreakpoint(data);
}
void BreakHandler::editBreakpoints(const Breakpoints &bps, QWidget *parent)
{
QTC_ASSERT(!bps.isEmpty(), return);
const Breakpoint bp = bps.at(0);
if (bps.size() == 1) {
editBreakpoint(bp, parent);
return;
}
// This allows to change properties of multiple breakpoints at a time.
if (!bp)
return;
MultiBreakPointsDialog dialog;
dialog.setCondition(bp.condition());
dialog.setIgnoreCount(bp.ignoreCount());
dialog.setThreadSpec(bp.threadSpec());
if (dialog.exec() == QDialog::Rejected)
return;
const QString newCondition = dialog.condition();
const int newIgnoreCount = dialog.ignoreCount();
const int newThreadSpec = dialog.threadSpec();
foreach (Breakpoint bp, bps) {
if (bp) {
bp.setCondition(newCondition);
bp.setIgnoreCount(newIgnoreCount);
bp.setThreadSpec(newThreadSpec);
}
}
}
////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////
// //
// Storage // Storage

View File

@@ -33,6 +33,8 @@
#include <QCoreApplication> #include <QCoreApplication>
#include <QPointer> #include <QPointer>
namespace Utils { class ItemViewEvent; }
namespace Debugger { namespace Debugger {
namespace Internal { namespace Internal {
@@ -157,7 +159,9 @@ inline uint qHash(const Debugger::Internal::Breakpoint &b) { return b.hash(); }
typedef QList<Breakpoint> Breakpoints; typedef QList<Breakpoint> Breakpoints;
class BreakHandler : public Utils::LeveledTreeModel<Utils::TreeItem, BreakpointItem, LocationItem> using BreakModel = Utils::LeveledTreeModel<Utils::TypedTreeItem<BreakpointItem>, BreakpointItem, LocationItem>;
class BreakHandler : public BreakModel
{ {
Q_OBJECT Q_OBJECT
@@ -200,8 +204,15 @@ public:
void setWatchpointAtExpression(const QString &exp); void setWatchpointAtExpression(const QString &exp);
Breakpoint breakpointById(BreakpointModelId id) const; Breakpoint breakpointById(BreakpointModelId id) const;
void editBreakpoint(Breakpoint bp, QWidget *parent);
private: private:
QVariant data(const QModelIndex &idx, int role) const override;
bool setData(const QModelIndex &idx, const QVariant &value, int role) override;
void timerEvent(QTimerEvent *event) override;
bool contextMenuEvent(const Utils::ItemViewEvent &ev);
friend class BreakpointItem; friend class BreakpointItem;
friend class Breakpoint; friend class Breakpoint;
@@ -209,12 +220,17 @@ private:
void saveBreakpoints(); void saveBreakpoints();
void appendBreakpointInternal(const BreakpointParameters &data); void appendBreakpointInternal(const BreakpointParameters &data);
void deleteBreakpoints(const Breakpoints &bps);
void deleteAllBreakpoints();
void setBreakpointsEnabled(const Breakpoints &bps, bool enabled);
void addBreakpoint();
void editBreakpoints(const Breakpoints &bps, QWidget *parent);
Q_SLOT void changeLineNumberFromMarkerHelper(Debugger::Internal::BreakpointModelId id); Q_SLOT void changeLineNumberFromMarkerHelper(Debugger::Internal::BreakpointModelId id);
Q_SLOT void deletionHelper(Debugger::Internal::BreakpointModelId id); Q_SLOT void deletionHelper(Debugger::Internal::BreakpointModelId id);
void scheduleSynchronization(); void scheduleSynchronization();
void timerEvent(QTimerEvent *event);
int m_syncTimerId; int m_syncTimerId;
}; };

View File

@@ -1,955 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "breakwindow.h"
#include "breakhandler.h"
#include "debuggerengine.h"
#include "debuggeractions.h"
#include "debuggercore.h"
#include "debuggericons.h"
#include <coreplugin/mainwindow.h>
#include <utils/checkablemessagebox.h>
#include <utils/pathchooser.h>
#include <utils/qtcassert.h>
#include <utils/savedaction.h>
#include <QCheckBox>
#include <QComboBox>
#include <QDialog>
#include <QDialogButtonBox>
#include <QFormLayout>
#include <QGroupBox>
#include <QKeyEvent>
#include <QLabel>
#include <QLineEdit>
#include <QMenu>
#include <QSpinBox>
#include <QStyledItemDelegate>
#include <QTextEdit>
namespace Debugger {
namespace Internal {
class LeftElideDelegate : public QStyledItemDelegate
{
public:
LeftElideDelegate(QObject *parent) : QStyledItemDelegate(parent) {}
void paint(QPainter *pain, const QStyleOptionViewItem &option, const QModelIndex &index) const override
{
QStyleOptionViewItem opt = option;
opt.textElideMode = Qt::ElideLeft;
QStyledItemDelegate::paint(pain, opt, index);
}
};
class SmallTextEdit : public QTextEdit
{
public:
explicit SmallTextEdit(QWidget *parent) : QTextEdit(parent) {}
QSize sizeHint() const { return QSize(QTextEdit::sizeHint().width(), 100); }
QSize minimumSizeHint() const { return sizeHint(); }
};
///////////////////////////////////////////////////////////////////////
//
// BreakpointDialog: Show a dialog for editing breakpoints. Shows controls
// for the file-and-line, function and address parameters depending on the
// breakpoint type. The controls not applicable to the current type
// (say function name for file-and-line) are disabled and cleared out.
// However,the values are saved and restored once the respective mode
// is again chosen, which is done using m_savedParameters and
// setters/getters taking the parts mask enumeration parameter.
//
///////////////////////////////////////////////////////////////////////
class BreakpointDialog : public QDialog
{
Q_OBJECT
public:
explicit BreakpointDialog(Breakpoint b, QWidget *parent = 0);
bool showDialog(BreakpointParameters *data, BreakpointParts *parts);
void setParameters(const BreakpointParameters &data);
BreakpointParameters parameters() const;
void typeChanged(int index);
private:
void setPartsEnabled(unsigned partsMask);
void clearOtherParts(unsigned partsMask);
void getParts(unsigned partsMask, BreakpointParameters *data) const;
void setParts(unsigned partsMask, const BreakpointParameters &data);
void setType(BreakpointType type);
BreakpointType type() const;
unsigned m_enabledParts;
BreakpointParameters m_savedParameters;
BreakpointType m_previousType;
bool m_firstTypeChange;
QLabel *m_labelType;
QComboBox *m_comboBoxType;
QLabel *m_labelFileName;
Utils::PathChooser *m_pathChooserFileName;
QLabel *m_labelLineNumber;
QLineEdit *m_lineEditLineNumber;
QLabel *m_labelEnabled;
QCheckBox *m_checkBoxEnabled;
QLabel *m_labelAddress;
QLineEdit *m_lineEditAddress;
QLabel *m_labelExpression;
QLineEdit *m_lineEditExpression;
QLabel *m_labelFunction;
QLineEdit *m_lineEditFunction;
QLabel *m_labelTracepoint;
QCheckBox *m_checkBoxTracepoint;
QLabel *m_labelOneShot;
QCheckBox *m_checkBoxOneShot;
QLabel *m_labelUseFullPath;
QLabel *m_labelModule;
QLineEdit *m_lineEditModule;
QLabel *m_labelCommands;
QTextEdit *m_textEditCommands;
QComboBox *m_comboBoxPathUsage;
QLabel *m_labelMessage;
QLineEdit *m_lineEditMessage;
QLabel *m_labelCondition;
QLineEdit *m_lineEditCondition;
QLabel *m_labelIgnoreCount;
QSpinBox *m_spinBoxIgnoreCount;
QLabel *m_labelThreadSpec;
QLineEdit *m_lineEditThreadSpec;
QDialogButtonBox *m_buttonBox;
};
BreakpointDialog::BreakpointDialog(Breakpoint b, QWidget *parent)
: QDialog(parent), m_enabledParts(~0), m_previousType(UnknownBreakpointType),
m_firstTypeChange(true)
{
setWindowTitle(tr("Edit Breakpoint Properties"));
auto groupBoxBasic = new QGroupBox(tr("Basic"), this);
// Match BreakpointType (omitting unknown type).
QStringList types;
types << tr("File name and line number")
<< tr("Function name")
<< tr("Break on memory address")
<< tr("Break when C++ exception is thrown")
<< tr("Break when C++ exception is caught")
<< tr("Break when function \"main\" starts")
<< tr("Break when a new process is forked")
<< tr("Break when a new process is executed")
<< tr("Break when a system call is executed")
<< tr("Break on data access at fixed address")
<< tr("Break on data access at address given by expression")
<< tr("Break on QML signal emit")
<< tr("Break when JavaScript exception is thrown");
// We don't list UnknownBreakpointType, so 1 less:
QTC_CHECK(types.size() + 1 == LastBreakpointType);
m_comboBoxType = new QComboBox(groupBoxBasic);
m_comboBoxType->setMaxVisibleItems(20);
m_comboBoxType->addItems(types);
m_labelType = new QLabel(tr("Breakpoint &type:"), groupBoxBasic);
m_labelType->setBuddy(m_comboBoxType);
m_pathChooserFileName = new Utils::PathChooser(groupBoxBasic);
m_pathChooserFileName->setHistoryCompleter(QLatin1String("Debugger.Breakpoint.File.History"));
m_pathChooserFileName->setExpectedKind(Utils::PathChooser::File);
m_labelFileName = new QLabel(tr("&File name:"), groupBoxBasic);
m_labelFileName->setBuddy(m_pathChooserFileName);
m_lineEditLineNumber = new QLineEdit(groupBoxBasic);
m_labelLineNumber = new QLabel(tr("&Line number:"), groupBoxBasic);
m_labelLineNumber->setBuddy(m_lineEditLineNumber);
m_checkBoxEnabled = new QCheckBox(groupBoxBasic);
m_labelEnabled = new QLabel(tr("&Enabled:"), groupBoxBasic);
m_labelEnabled->setBuddy(m_checkBoxEnabled);
m_lineEditAddress = new QLineEdit(groupBoxBasic);
m_labelAddress = new QLabel(tr("&Address:"), groupBoxBasic);
m_labelAddress->setBuddy(m_lineEditAddress);
m_lineEditExpression = new QLineEdit(groupBoxBasic);
m_labelExpression = new QLabel(tr("&Expression:"), groupBoxBasic);
m_labelExpression->setBuddy(m_lineEditExpression);
m_lineEditFunction = new QLineEdit(groupBoxBasic);
m_labelFunction = new QLabel(tr("Fun&ction:"), groupBoxBasic);
m_labelFunction->setBuddy(m_lineEditFunction);
auto groupBoxAdvanced = new QGroupBox(tr("Advanced"), this);
m_checkBoxTracepoint = new QCheckBox(groupBoxAdvanced);
m_labelTracepoint = new QLabel(tr("T&racepoint only:"), groupBoxAdvanced);
m_labelTracepoint->setBuddy(m_checkBoxTracepoint);
m_checkBoxOneShot = new QCheckBox(groupBoxAdvanced);
m_labelOneShot = new QLabel(tr("&One shot only:"), groupBoxAdvanced);
m_labelOneShot->setBuddy(m_checkBoxOneShot);
const QString pathToolTip =
tr("<p>Determines how the path is specified "
"when setting breakpoints:</p><ul>"
"<li><i>Use Engine Default</i>: Preferred setting of the "
"debugger engine.</li>"
"<li><i>Use Full Path</i>: Pass full path, avoiding ambiguities "
"should files of the same name exist in several modules. "
"This is the engine default for CDB and LLDB.</li>"
"<li><i>Use File Name</i>: Pass the file name only. This is "
"useful when using a source tree whose location does "
"not match the one used when building the modules. "
"It is the engine default for GDB as using full paths can "
"be slow with this engine.</li></ul>");
m_comboBoxPathUsage = new QComboBox(groupBoxAdvanced);
m_comboBoxPathUsage->addItem(tr("Use Engine Default"));
m_comboBoxPathUsage->addItem(tr("Use Full Path"));
m_comboBoxPathUsage->addItem(tr("Use File Name"));
m_comboBoxPathUsage->setToolTip(pathToolTip);
m_labelUseFullPath = new QLabel(tr("Pat&h:"), groupBoxAdvanced);
m_labelUseFullPath->setBuddy(m_comboBoxPathUsage);
m_labelUseFullPath->setToolTip(pathToolTip);
const QString moduleToolTip =
tr("<p>Specifying the module (base name of the library or executable) "
"for function or file type breakpoints can significantly speed up "
"debugger start-up times (CDB, LLDB).");
m_lineEditModule = new QLineEdit(groupBoxAdvanced);
m_lineEditModule->setToolTip(moduleToolTip);
m_labelModule = new QLabel(tr("&Module:"), groupBoxAdvanced);
m_labelModule->setBuddy(m_lineEditModule);
m_labelModule->setToolTip(moduleToolTip);
const QString commandsToolTip =
tr("<p>Debugger commands to be executed when the breakpoint is hit. "
"This feature is only available for GDB.");
m_textEditCommands = new SmallTextEdit(groupBoxAdvanced);
m_textEditCommands->setToolTip(commandsToolTip);
m_labelCommands = new QLabel(tr("&Commands:"), groupBoxAdvanced);
m_labelCommands->setBuddy(m_textEditCommands);
m_labelCommands->setToolTip(commandsToolTip);
m_lineEditMessage = new QLineEdit(groupBoxAdvanced);
m_labelMessage = new QLabel(tr("&Message:"), groupBoxAdvanced);
m_labelMessage->setBuddy(m_lineEditMessage);
m_lineEditCondition = new QLineEdit(groupBoxAdvanced);
m_labelCondition = new QLabel(tr("C&ondition:"), groupBoxAdvanced);
m_labelCondition->setBuddy(m_lineEditCondition);
m_spinBoxIgnoreCount = new QSpinBox(groupBoxAdvanced);
m_spinBoxIgnoreCount->setMinimum(0);
m_spinBoxIgnoreCount->setMaximum(2147483647);
m_labelIgnoreCount = new QLabel(tr("&Ignore count:"), groupBoxAdvanced);
m_labelIgnoreCount->setBuddy(m_spinBoxIgnoreCount);
m_lineEditThreadSpec = new QLineEdit(groupBoxAdvanced);
m_labelThreadSpec = new QLabel(tr("&Thread specification:"), groupBoxAdvanced);
m_labelThreadSpec->setBuddy(m_lineEditThreadSpec);
m_buttonBox = new QDialogButtonBox(this);
m_buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
if (b) {
if (DebuggerEngine *engine = b.engine()) {
if (!engine->hasCapability(BreakConditionCapability))
m_enabledParts &= ~ConditionPart;
if (!engine->hasCapability(BreakModuleCapability))
m_enabledParts &= ~ModulePart;
if (!engine->hasCapability(TracePointCapability))
m_enabledParts &= ~TracePointPart;
}
}
auto basicLayout = new QFormLayout(groupBoxBasic);
basicLayout->addRow(m_labelType, m_comboBoxType);
basicLayout->addRow(m_labelFileName, m_pathChooserFileName);
basicLayout->addRow(m_labelLineNumber, m_lineEditLineNumber);
basicLayout->addRow(m_labelEnabled, m_checkBoxEnabled);
basicLayout->addRow(m_labelAddress, m_lineEditAddress);
basicLayout->addRow(m_labelExpression, m_lineEditExpression);
basicLayout->addRow(m_labelFunction, m_lineEditFunction);
basicLayout->addRow(m_labelOneShot, m_checkBoxOneShot);
auto advancedLeftLayout = new QFormLayout();
advancedLeftLayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
advancedLeftLayout->addRow(m_labelCondition, m_lineEditCondition);
advancedLeftLayout->addRow(m_labelIgnoreCount, m_spinBoxIgnoreCount);
advancedLeftLayout->addRow(m_labelThreadSpec, m_lineEditThreadSpec);
advancedLeftLayout->addRow(m_labelUseFullPath, m_comboBoxPathUsage);
advancedLeftLayout->addRow(m_labelModule, m_lineEditModule);
auto advancedRightLayout = new QFormLayout();
advancedRightLayout->setFieldGrowthPolicy(QFormLayout::ExpandingFieldsGrow);
advancedRightLayout->addRow(m_labelCommands, m_textEditCommands);
advancedRightLayout->addRow(m_labelTracepoint, m_checkBoxTracepoint);
advancedRightLayout->addRow(m_labelMessage, m_lineEditMessage);
auto horizontalLayout = new QHBoxLayout(groupBoxAdvanced);
horizontalLayout->addLayout(advancedLeftLayout);
horizontalLayout->addSpacing(15);
horizontalLayout->addLayout(advancedRightLayout);
auto verticalLayout = new QVBoxLayout(this);
verticalLayout->addWidget(groupBoxBasic);
verticalLayout->addSpacing(10);
verticalLayout->addWidget(groupBoxAdvanced);
verticalLayout->addSpacing(10);
verticalLayout->addWidget(m_buttonBox);
verticalLayout->setStretchFactor(groupBoxAdvanced, 10);
connect(m_comboBoxType, static_cast<void(QComboBox::*)(int)>(&QComboBox::activated),
this, &BreakpointDialog::typeChanged);
connect(m_buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(m_buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
}
void BreakpointDialog::setType(BreakpointType type)
{
const int comboIndex = type - 1; // Skip UnknownType.
if (comboIndex != m_comboBoxType->currentIndex() || m_firstTypeChange) {
m_comboBoxType->setCurrentIndex(comboIndex);
typeChanged(comboIndex);
m_firstTypeChange = false;
}
}
BreakpointType BreakpointDialog::type() const
{
const int type = m_comboBoxType->currentIndex() + 1; // Skip unknown type.
return static_cast<BreakpointType>(type);
}
void BreakpointDialog::setParameters(const BreakpointParameters &data)
{
m_savedParameters = data;
setType(data.type);
setParts(AllParts, data);
}
BreakpointParameters BreakpointDialog::parameters() const
{
BreakpointParameters data(type());
getParts(AllParts, &data);
return data;
}
void BreakpointDialog::setPartsEnabled(unsigned partsMask)
{
partsMask &= m_enabledParts;
m_labelFileName->setEnabled(partsMask & FileAndLinePart);
m_pathChooserFileName->setEnabled(partsMask & FileAndLinePart);
m_labelLineNumber->setEnabled(partsMask & FileAndLinePart);
m_lineEditLineNumber->setEnabled(partsMask & FileAndLinePart);
m_labelUseFullPath->setEnabled(partsMask & FileAndLinePart);
m_comboBoxPathUsage->setEnabled(partsMask & FileAndLinePart);
m_labelFunction->setEnabled(partsMask & FunctionPart);
m_lineEditFunction->setEnabled(partsMask & FunctionPart);
m_labelOneShot->setEnabled(partsMask & OneShotPart);
m_checkBoxOneShot->setEnabled(partsMask & OneShotPart);
m_labelAddress->setEnabled(partsMask & AddressPart);
m_lineEditAddress->setEnabled(partsMask & AddressPart);
m_labelExpression->setEnabled(partsMask & ExpressionPart);
m_lineEditExpression->setEnabled(partsMask & ExpressionPart);
m_labelCondition->setEnabled(partsMask & ConditionPart);
m_lineEditCondition->setEnabled(partsMask & ConditionPart);
m_labelIgnoreCount->setEnabled(partsMask & IgnoreCountPart);
m_spinBoxIgnoreCount->setEnabled(partsMask & IgnoreCountPart);
m_labelThreadSpec->setEnabled(partsMask & ThreadSpecPart);
m_lineEditThreadSpec->setEnabled(partsMask & ThreadSpecPart);
m_labelModule->setEnabled(partsMask & ModulePart);
m_lineEditModule->setEnabled(partsMask & ModulePart);
m_labelTracepoint->setEnabled(partsMask & TracePointPart);
m_labelTracepoint->hide();
m_checkBoxTracepoint->setEnabled(partsMask & TracePointPart);
m_checkBoxTracepoint->hide();
m_labelCommands->setEnabled(partsMask & CommandPart);
m_textEditCommands->setEnabled(partsMask & CommandPart);
m_labelMessage->setEnabled(partsMask & TracePointPart);
m_labelMessage->hide();
m_lineEditMessage->setEnabled(partsMask & TracePointPart);
m_lineEditMessage->hide();
}
void BreakpointDialog::clearOtherParts(unsigned partsMask)
{
const unsigned invertedPartsMask = ~partsMask;
if (invertedPartsMask & FileAndLinePart) {
m_pathChooserFileName->setPath(QString());
m_lineEditLineNumber->clear();
m_comboBoxPathUsage->setCurrentIndex(BreakpointPathUsageEngineDefault);
}
if (invertedPartsMask & FunctionPart)
m_lineEditFunction->clear();
if (invertedPartsMask & AddressPart)
m_lineEditAddress->clear();
if (invertedPartsMask & ExpressionPart)
m_lineEditExpression->clear();
if (invertedPartsMask & ConditionPart)
m_lineEditCondition->clear();
if (invertedPartsMask & IgnoreCountPart)
m_spinBoxIgnoreCount->clear();
if (invertedPartsMask & ThreadSpecPart)
m_lineEditThreadSpec->clear();
if (invertedPartsMask & ModulePart)
m_lineEditModule->clear();
if (partsMask & OneShotPart)
m_checkBoxOneShot->setChecked(false);
if (invertedPartsMask & CommandPart)
m_textEditCommands->clear();
if (invertedPartsMask & TracePointPart) {
m_checkBoxTracepoint->setChecked(false);
m_lineEditMessage->clear();
}
}
void BreakpointDialog::getParts(unsigned partsMask, BreakpointParameters *data) const
{
data->enabled = m_checkBoxEnabled->isChecked();
if (partsMask & FileAndLinePart) {
data->lineNumber = m_lineEditLineNumber->text().toInt();
data->pathUsage = static_cast<BreakpointPathUsage>(m_comboBoxPathUsage->currentIndex());
data->fileName = m_pathChooserFileName->path();
}
if (partsMask & FunctionPart)
data->functionName = m_lineEditFunction->text();
if (partsMask & AddressPart)
data->address = m_lineEditAddress->text().toULongLong(0, 0);
if (partsMask & ExpressionPart)
data->expression = m_lineEditExpression->text();
if (partsMask & ConditionPart)
data->condition = m_lineEditCondition->text();
if (partsMask & IgnoreCountPart)
data->ignoreCount = m_spinBoxIgnoreCount->text().toInt();
if (partsMask & ThreadSpecPart)
data->threadSpec =
BreakHandler::threadSpecFromDisplay(m_lineEditThreadSpec->text());
if (partsMask & ModulePart)
data->module = m_lineEditModule->text();
if (partsMask & OneShotPart)
data->oneShot = m_checkBoxOneShot->isChecked();
if (partsMask & CommandPart)
data->command = m_textEditCommands->toPlainText().trimmed();
if (partsMask & TracePointPart) {
data->tracepoint = m_checkBoxTracepoint->isChecked();
data->message = m_lineEditMessage->text();
}
}
void BreakpointDialog::setParts(unsigned mask, const BreakpointParameters &data)
{
m_checkBoxEnabled->setChecked(data.enabled);
m_comboBoxPathUsage->setCurrentIndex(data.pathUsage);
m_lineEditMessage->setText(data.message);
if (mask & FileAndLinePart) {
m_pathChooserFileName->setPath(data.fileName);
m_lineEditLineNumber->setText(QString::number(data.lineNumber));
}
if (mask & FunctionPart)
m_lineEditFunction->setText(data.functionName);
if (mask & AddressPart) {
if (data.address) {
m_lineEditAddress->setText(
QString::fromLatin1("0x%1").arg(data.address, 0, 16));
} else {
m_lineEditAddress->clear();
}
}
if (mask & ExpressionPart) {
if (!data.expression.isEmpty())
m_lineEditExpression->setText(data.expression);
else
m_lineEditExpression->clear();
}
if (mask & ConditionPart)
m_lineEditCondition->setText(data.condition);
if (mask & IgnoreCountPart)
m_spinBoxIgnoreCount->setValue(data.ignoreCount);
if (mask & ThreadSpecPart)
m_lineEditThreadSpec->
setText(BreakHandler::displayFromThreadSpec(data.threadSpec));
if (mask & ModulePart)
m_lineEditModule->setText(data.module);
if (mask & OneShotPart)
m_checkBoxOneShot->setChecked(data.oneShot);
if (mask & TracePointPart)
m_checkBoxTracepoint->setChecked(data.tracepoint);
if (mask & CommandPart)
m_textEditCommands->setPlainText(data.command);
}
void BreakpointDialog::typeChanged(int)
{
BreakpointType previousType = m_previousType;
const BreakpointType newType = type();
m_previousType = newType;
// Save current state.
switch (previousType) {
case UnknownBreakpointType:
case LastBreakpointType:
break;
case BreakpointByFileAndLine:
getParts(FileAndLinePart|ModulePart|AllConditionParts|TracePointPart|CommandPart, &m_savedParameters);
break;
case BreakpointByFunction:
getParts(FunctionPart|ModulePart|AllConditionParts|TracePointPart|CommandPart, &m_savedParameters);
break;
case BreakpointAtThrow:
case BreakpointAtCatch:
case BreakpointAtMain:
case BreakpointAtFork:
case BreakpointAtExec:
//case BreakpointAtVFork:
case BreakpointAtSysCall:
case BreakpointAtJavaScriptThrow:
break;
case BreakpointByAddress:
case WatchpointAtAddress:
getParts(AddressPart|AllConditionParts|TracePointPart|CommandPart, &m_savedParameters);
break;
case WatchpointAtExpression:
getParts(ExpressionPart|AllConditionParts|TracePointPart|CommandPart, &m_savedParameters);
break;
case BreakpointOnQmlSignalEmit:
getParts(FunctionPart, &m_savedParameters);
}
// Enable and set up new state from saved values.
switch (newType) {
case UnknownBreakpointType:
case LastBreakpointType:
break;
case BreakpointByFileAndLine:
setParts(FileAndLinePart|AllConditionParts|ModulePart|TracePointPart|CommandPart, m_savedParameters);
setPartsEnabled(FileAndLinePart|AllConditionParts|ModulePart|TracePointPart|CommandPart);
clearOtherParts(FileAndLinePart|AllConditionParts|ModulePart|TracePointPart|CommandPart);
break;
case BreakpointByFunction:
setParts(FunctionPart|AllConditionParts|ModulePart|TracePointPart|CommandPart, m_savedParameters);
setPartsEnabled(FunctionPart|AllConditionParts|ModulePart|TracePointPart|CommandPart);
clearOtherParts(FunctionPart|AllConditionParts|ModulePart|TracePointPart|CommandPart);
break;
case BreakpointAtThrow:
case BreakpointAtCatch:
case BreakpointAtFork:
case BreakpointAtExec:
//case BreakpointAtVFork:
case BreakpointAtSysCall:
clearOtherParts(AllConditionParts|ModulePart|TracePointPart|CommandPart);
setPartsEnabled(AllConditionParts|TracePointPart|CommandPart);
break;
case BreakpointAtJavaScriptThrow:
clearOtherParts(AllParts);
setPartsEnabled(0);
break;
case BreakpointAtMain:
m_lineEditFunction->setText(QLatin1String("main")); // Just for display
clearOtherParts(0);
setPartsEnabled(0);
break;
case BreakpointByAddress:
case WatchpointAtAddress:
setParts(AddressPart|AllConditionParts|TracePointPart|CommandPart, m_savedParameters);
setPartsEnabled(AddressPart|AllConditionParts|TracePointPart|CommandPart);
clearOtherParts(AddressPart|AllConditionParts|TracePointPart|CommandPart);
break;
case WatchpointAtExpression:
setParts(ExpressionPart|AllConditionParts|TracePointPart|CommandPart, m_savedParameters);
setPartsEnabled(ExpressionPart|AllConditionParts|TracePointPart|CommandPart);
clearOtherParts(ExpressionPart|AllConditionParts|TracePointPart|CommandPart);
break;
case BreakpointOnQmlSignalEmit:
setParts(FunctionPart, m_savedParameters);
setPartsEnabled(FunctionPart);
clearOtherParts(FunctionPart);
}
}
bool BreakpointDialog::showDialog(BreakpointParameters *data,
BreakpointParts *parts)
{
setParameters(*data);
if (exec() != QDialog::Accepted)
return false;
// Check if changed.
const BreakpointParameters newParameters = parameters();
*parts = data->differencesTo(newParameters);
if (!*parts)
return false;
*data = newParameters;
return true;
}
// Dialog allowing changing properties of multiple breakpoints at a time.
class MultiBreakPointsDialog : public QDialog
{
Q_OBJECT
public:
MultiBreakPointsDialog(QWidget *parent = 0);
QString condition() const { return m_lineEditCondition->text(); }
int ignoreCount() const { return m_spinBoxIgnoreCount->value(); }
int threadSpec() const
{ return BreakHandler::threadSpecFromDisplay(m_lineEditThreadSpec->text()); }
void setCondition(const QString &c) { m_lineEditCondition->setText(c); }
void setIgnoreCount(int i) { m_spinBoxIgnoreCount->setValue(i); }
void setThreadSpec(int t)
{ return m_lineEditThreadSpec->setText(BreakHandler::displayFromThreadSpec(t)); }
private:
QLineEdit *m_lineEditCondition;
QSpinBox *m_spinBoxIgnoreCount;
QLineEdit *m_lineEditThreadSpec;
QDialogButtonBox *m_buttonBox;
};
MultiBreakPointsDialog::MultiBreakPointsDialog(QWidget *parent) :
QDialog(parent)
{
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
setWindowTitle(tr("Edit Breakpoint Properties"));
m_lineEditCondition = new QLineEdit(this);
m_spinBoxIgnoreCount = new QSpinBox(this);
m_spinBoxIgnoreCount->setMinimum(0);
m_spinBoxIgnoreCount->setMaximum(2147483647);
m_lineEditThreadSpec = new QLineEdit(this);
m_buttonBox = new QDialogButtonBox(this);
m_buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
auto formLayout = new QFormLayout;
if (currentEngine()->hasCapability(BreakConditionCapability))
formLayout->addRow(tr("&Condition:"), m_lineEditCondition);
formLayout->addRow(tr("&Ignore count:"), m_spinBoxIgnoreCount);
formLayout->addRow(tr("&Thread specification:"), m_lineEditThreadSpec);
auto verticalLayout = new QVBoxLayout(this);
verticalLayout->addLayout(formLayout);
verticalLayout->addWidget(m_buttonBox);
connect(m_buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(m_buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
}
///////////////////////////////////////////////////////////////////////
//
// BreakWindow
//
///////////////////////////////////////////////////////////////////////
BreakTreeView::BreakTreeView()
{
setWindowIcon(Icons::BREAKPOINTS.icon());
setSelectionMode(QAbstractItemView::ExtendedSelection);
setItemDelegateForColumn(BreakpointFileColumn, new LeftElideDelegate(this));
connect(action(UseAddressInBreakpointsView), &QAction::toggled,
this, &BreakTreeView::showAddressColumn);
}
void BreakTreeView::showAddressColumn(bool on)
{
setColumnHidden(BreakpointAddressColumn, !on);
}
void BreakTreeView::keyPressEvent(QKeyEvent *ev)
{
if (ev->key() == Qt::Key_Delete) {
QItemSelectionModel *sm = selectionModel();
QTC_ASSERT(sm, return);
QModelIndexList si = sm->selectedRows();
if (si.isEmpty())
si.append(currentIndex());
const Breakpoints ids = breakHandler()->findBreakpointsByIndex(si);
int row = qMin(model()->rowCount() - ids.size() - 1, currentIndex().row());
deleteBreakpoints(ids);
setCurrentIndex(model()->index(row, 0));
} else if (ev->key() == Qt::Key_Space) {
QItemSelectionModel *sm = selectionModel();
QTC_ASSERT(sm, return);
const QModelIndexList selectedIds = sm->selectedRows();
if (!selectedIds.isEmpty()) {
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);
}
}
BaseTreeView::keyPressEvent(ev);
}
void BreakTreeView::mouseDoubleClickEvent(QMouseEvent *ev)
{
QModelIndex indexUnderMouse = indexAt(ev->pos());
if (indexUnderMouse.isValid()) {
if (indexUnderMouse.column() >= BreakpointAddressColumn) {
Breakpoint b = breakHandler()->findBreakpointByIndex(indexUnderMouse);
QTC_ASSERT(b, return);
editBreakpoints(Breakpoints() << b);
}
} else {
addBreakpoint();
}
BaseTreeView::mouseDoubleClickEvent(ev);
}
void BreakTreeView::contextMenuEvent(QContextMenuEvent *ev)
{
QMenu menu;
QItemSelectionModel *sm = selectionModel();
QTC_ASSERT(sm, return);
QModelIndexList selectedIndices = sm->selectedRows();
QModelIndex indexUnderMouse = indexAt(ev->pos());
if (selectedIndices.isEmpty() && indexUnderMouse.isValid())
selectedIndices.append(indexUnderMouse);
BreakHandler *handler = breakHandler();
Breakpoints selectedItems = handler->findBreakpointsByIndex(selectedIndices);
const int rowCount = model()->rowCount();
auto deleteAction = new QAction(tr("Delete Selected Breakpoints"), &menu);
deleteAction->setEnabled(!selectedItems.empty());
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;
Breakpoints breakpointsInFile;
if (indexUnderMouse.isValid()) {
const QModelIndex index = indexUnderMouse.sibling(indexUnderMouse.row(),
BreakpointFileColumn);
const QString file = index.data().toString();
if (!file.isEmpty()) {
for (int i = 0; i != rowCount; ++i)
if (index.data().toString() == file)
breakpointsInFile.append(handler->findBreakpointByIndex(index));
if (breakpointsInFile.size() > 1) {
deleteByFileAction =
new QAction(tr("Delete Breakpoints of \"%1\"").arg(file), &menu);
deleteByFileAction->setEnabled(true);
}
}
}
if (!deleteByFileAction) {
deleteByFileAction = new QAction(tr("Delete Breakpoints of File"), &menu);
deleteByFileAction->setEnabled(false);
}
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);
auto associateBreakpointAction = new QAction(associateTitle, &menu);
associateBreakpointAction->setEnabled(!selectedItems.isEmpty());
auto synchronizeAction = new QAction(tr("Synchronize Breakpoints"), &menu);
synchronizeAction->setEnabled(Internal::hasSnapshots());
bool enabled = selectedItems.isEmpty() || selectedItems.at(0).isEnabled();
const QString str5 = selectedItems.size() > 1
? enabled
? tr("Disable Selected Breakpoints")
: tr("Enable Selected Breakpoints")
: enabled
? tr("Disable Breakpoint")
: tr("Enable Breakpoint");
auto toggleEnabledAction = new QAction(str5, &menu);
toggleEnabledAction->setEnabled(!selectedItems.isEmpty());
auto addBreakpointAction = new QAction(tr("Add Breakpoint..."), this);
menu.addAction(addBreakpointAction);
menu.addAction(deleteAction);
menu.addAction(editBreakpointAction);
menu.addAction(associateBreakpointAction);
menu.addAction(toggleEnabledAction);
menu.addSeparator();
menu.addAction(deleteAllAction);
//menu.addAction(deleteByFileAction);
menu.addSeparator();
menu.addAction(synchronizeAction);
menu.addSeparator();
menu.addAction(action(UseToolTipsInBreakpointsView));
if (currentEngine()->hasCapability(MemoryAddressCapability))
menu.addAction(action(UseAddressInBreakpointsView));
menu.addSeparator();
menu.addAction(action(SettingsDialog));
QAction *act = menu.exec(ev->globalPos());
if (act == deleteAction)
deleteBreakpoints(selectedItems);
else if (act == deleteAllAction)
deleteAllBreakpoints();
else if (act == deleteByFileAction)
deleteBreakpoints(breakpointsInFile);
else if (act == editBreakpointAction)
editBreakpoints(selectedItems);
else if (act == associateBreakpointAction)
associateBreakpoint(selectedItems, threadId);
else if (act == synchronizeAction)
; //synchronizeBreakpoints();
else if (act == toggleEnabledAction)
setBreakpointsEnabled(selectedItems, !enabled);
else if (act == addBreakpointAction)
addBreakpoint();
}
void BreakTreeView::setBreakpointsEnabled(const Breakpoints &bps, bool enabled)
{
foreach (Breakpoint b, bps)
b.setEnabled(enabled);
}
void BreakTreeView::deleteAllBreakpoints()
{
if (Utils::CheckableMessageBox::doNotAskAgainQuestion(Core::ICore::dialogParent(),
tr("Remove All Breakpoints"),
tr("Are you sure you want to remove all breakpoints "
"from all files in the current session?"),
Core::ICore::settings(),
QLatin1String("RemoveAllBreakpoints")) == QDialogButtonBox::Yes)
deleteBreakpoints(breakHandler()->allBreakpoints());
}
void BreakTreeView::deleteBreakpoints(const Breakpoints &bps)
{
foreach (Breakpoint bp, bps)
bp.removeBreakpoint();
}
void BreakTreeView::editBreakpoint(Breakpoint bp, QWidget *parent)
{
BreakpointParameters data = bp.parameters();
BreakpointParts parts = NoParts;
BreakpointDialog dialog(bp, parent);
if (!dialog.showDialog(&data, &parts))
return;
bp.changeBreakpointData(data);
}
void BreakTreeView::addBreakpoint()
{
BreakpointParameters data(BreakpointByFileAndLine);
BreakpointParts parts = NoParts;
BreakpointDialog dialog(Breakpoint(), this);
dialog.setWindowTitle(tr("Add Breakpoint"));
if (dialog.showDialog(&data, &parts))
breakHandler()->appendBreakpoint(data);
}
void BreakTreeView::editBreakpoints(const Breakpoints &bps)
{
QTC_ASSERT(!bps.isEmpty(), return);
const Breakpoint bp = bps.at(0);
if (bps.size() == 1) {
editBreakpoint(bp, this);
return;
}
// This allows to change properties of multiple breakpoints at a time.
if (!bp)
return;
MultiBreakPointsDialog dialog;
dialog.setCondition(bp.condition());
dialog.setIgnoreCount(bp.ignoreCount());
dialog.setThreadSpec(bp.threadSpec());
if (dialog.exec() == QDialog::Rejected)
return;
const QString newCondition = dialog.condition();
const int newIgnoreCount = dialog.ignoreCount();
const int newThreadSpec = dialog.threadSpec();
foreach (Breakpoint bp, bps) {
if (bp) {
bp.setCondition(newCondition);
bp.setIgnoreCount(newIgnoreCount);
bp.setThreadSpec(newThreadSpec);
}
}
}
void BreakTreeView::associateBreakpoint(const Breakpoints &bps, int threadId)
{
foreach (Breakpoint bp, bps) {
if (bp)
bp.setThreadSpec(threadId);
}
}
void BreakTreeView::rowActivated(const QModelIndex &index)
{
if (Breakpoint bp = breakHandler()->findBreakpointByIndex(index))
bp.gotoLocation();
}
} // namespace Internal
} // namespace Debugger
#include "breakwindow.moc"

View File

@@ -1,59 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include "breakhandler.h"
#include <utils/basetreeview.h>
namespace Debugger {
namespace Internal {
class BreakTreeView : public Utils::BaseTreeView
{
Q_OBJECT
public:
BreakTreeView();
static void editBreakpoint(Breakpoint bp, QWidget *parent);
private:
void rowActivated(const QModelIndex &index);
void contextMenuEvent(QContextMenuEvent *ev);
void keyPressEvent(QKeyEvent *ev);
void mouseDoubleClickEvent(QMouseEvent *ev);
void showAddressColumn(bool on);
void deleteBreakpoints(const Breakpoints &bps);
void deleteAllBreakpoints();
void addBreakpoint();
void editBreakpoints(const Breakpoints &bps);
void associateBreakpoint(const Breakpoints &bps, int thread);
void setBreakpointsEnabled(const Breakpoints &bps, bool enabled);
};
} // namespace Internal
} // namespace Debugger

View File

@@ -14,7 +14,6 @@ CONFIG += exceptions
HEADERS += \ HEADERS += \
breakhandler.h \ breakhandler.h \
breakpoint.h \ breakpoint.h \
breakwindow.h \
commonoptionspage.h \ commonoptionspage.h \
debugger_global.h \ debugger_global.h \
debuggeractions.h \ debuggeractions.h \
@@ -40,22 +39,18 @@ HEADERS += \
logwindow.h \ logwindow.h \
memoryagent.h \ memoryagent.h \
moduleshandler.h \ moduleshandler.h \
moduleswindow.h \
outputcollector.h \ outputcollector.h \
procinterrupt.h \ procinterrupt.h \
registerhandler.h \ registerhandler.h \
registerwindow.h \
snapshothandler.h \ snapshothandler.h \
snapshotwindow.h \ snapshotwindow.h \
sourceagent.h \ sourceagent.h \
sourcefileshandler.h \ sourcefileshandler.h \
sourcefileswindow.h \
sourceutils.h \ sourceutils.h \
stackframe.h \ stackframe.h \
stackhandler.h \ stackhandler.h \
stackwindow.h \ stackwindow.h \
terminal.h \ terminal.h \
threadswindow.h \
watchhandler.h \ watchhandler.h \
watchutils.h \ watchutils.h \
watchwindow.h \ watchwindow.h \
@@ -74,7 +69,6 @@ HEADERS += \
SOURCES += \ SOURCES += \
breakhandler.cpp \ breakhandler.cpp \
breakpoint.cpp \ breakpoint.cpp \
breakwindow.cpp \
commonoptionspage.cpp \ commonoptionspage.cpp \
debuggeractions.cpp \ debuggeractions.cpp \
debuggerdialogs.cpp \ debuggerdialogs.cpp \
@@ -95,21 +89,17 @@ SOURCES += \
logwindow.cpp \ logwindow.cpp \
memoryagent.cpp \ memoryagent.cpp \
moduleshandler.cpp \ moduleshandler.cpp \
moduleswindow.cpp \
outputcollector.cpp \ outputcollector.cpp \
procinterrupt.cpp \ procinterrupt.cpp \
registerhandler.cpp \ registerhandler.cpp \
registerwindow.cpp \
snapshothandler.cpp \ snapshothandler.cpp \
snapshotwindow.cpp \ snapshotwindow.cpp \
sourceagent.cpp \ sourceagent.cpp \
sourcefileshandler.cpp \ sourcefileshandler.cpp \
sourcefileswindow.cpp \
sourceutils.cpp \ sourceutils.cpp \
stackhandler.cpp \ stackhandler.cpp \
stackwindow.cpp \ stackwindow.cpp \
threadshandler.cpp \ threadshandler.cpp \
threadswindow.cpp \
terminal.cpp \ terminal.cpp \
watchdata.cpp \ watchdata.cpp \
watchhandler.cpp \ watchhandler.cpp \

View File

@@ -43,7 +43,6 @@ Project {
files: [ files: [
"breakhandler.cpp", "breakhandler.h", "breakhandler.cpp", "breakhandler.h",
"breakpoint.cpp", "breakpoint.h", "breakpoint.cpp", "breakpoint.h",
"breakwindow.cpp", "breakwindow.h",
"commonoptionspage.cpp", "commonoptionspage.h", "commonoptionspage.cpp", "commonoptionspage.h",
"debugger.qrc", "debugger.qrc",
"debugger_global.h", "debugger_global.h",
@@ -76,16 +75,13 @@ Project {
"memoryagent.cpp", "memoryagent.h", "memoryagent.cpp", "memoryagent.h",
"memoryview.cpp", "memoryview.h", "memoryview.cpp", "memoryview.h",
"moduleshandler.cpp", "moduleshandler.h", "moduleshandler.cpp", "moduleshandler.h",
"moduleswindow.cpp", "moduleswindow.h",
"outputcollector.cpp", "outputcollector.h", "outputcollector.cpp", "outputcollector.h",
"procinterrupt.cpp", "procinterrupt.h", "procinterrupt.cpp", "procinterrupt.h",
"registerhandler.cpp", "registerhandler.h", "registerhandler.cpp", "registerhandler.h",
"registerwindow.cpp", "registerwindow.h",
"snapshothandler.cpp", "snapshothandler.h", "snapshothandler.cpp", "snapshothandler.h",
"snapshotwindow.cpp", "snapshotwindow.h", "snapshotwindow.cpp", "snapshotwindow.h",
"sourceagent.cpp", "sourceagent.h", "sourceagent.cpp", "sourceagent.h",
"sourcefileshandler.cpp", "sourcefileshandler.h", "sourcefileshandler.cpp", "sourcefileshandler.h",
"sourcefileswindow.cpp", "sourcefileswindow.h",
"sourceutils.cpp", "sourceutils.h", "sourceutils.cpp", "sourceutils.h",
"stackframe.cpp", "stackframe.h", "stackframe.cpp", "stackframe.h",
"stackhandler.cpp", "stackhandler.h", "stackhandler.cpp", "stackhandler.h",
@@ -93,7 +89,6 @@ Project {
"terminal.cpp", "terminal.h", "terminal.cpp", "terminal.h",
"threaddata.h", "threaddata.h",
"threadshandler.cpp", "threadshandler.h", "threadshandler.cpp", "threadshandler.h",
"threadswindow.cpp", "threadswindow.h",
"watchdata.cpp", "watchdata.h", "watchdata.cpp", "watchdata.h",
"watchdelegatewidgets.cpp", "watchdelegatewidgets.h", "watchdelegatewidgets.cpp", "watchdelegatewidgets.h",
"watchhandler.cpp", "watchhandler.h", "watchhandler.cpp", "watchhandler.h",

View File

@@ -32,10 +32,14 @@
#include <QObject> #include <QObject>
#include <QSharedPointer> #include <QSharedPointer>
#include <functional>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QIcon; class QIcon;
class QMessageBox; class QMessageBox;
class QWidget; class QWidget;
class QMenu;
class QAction;
QT_END_NAMESPACE QT_END_NAMESPACE
namespace CPlusPlus { class Snapshot; } namespace CPlusPlus { class Snapshot; }
@@ -107,5 +111,12 @@ QMessageBox *showMessageBox(int icon, const QString &title,
bool isReverseDebuggingEnabled(); bool isReverseDebuggingEnabled();
QAction *addAction(QMenu *menu, const QString &display, bool on,
const std::function<void()> &onTriggered = {});
QAction *addAction(QMenu *menu, const QString &d1, const QString &d2, bool on,
const std::function<void()> &onTriggered);
QAction *addCheckableAction(QMenu *menu, const QString &display, bool on, bool checked,
const std::function<void()> &onTriggered);
} // namespace Internal } // namespace Internal
} // namespace Debugger } // namespace Debugger

View File

@@ -174,23 +174,15 @@ class DebuggerEnginePrivate : public QObject
public: public:
DebuggerEnginePrivate(DebuggerEngine *engine, const DebuggerRunParameters &sp) DebuggerEnginePrivate(DebuggerEngine *engine, const DebuggerRunParameters &sp)
: m_engine(engine), : m_engine(engine),
m_masterEngine(0),
m_runControl(0),
m_runParameters(sp), m_runParameters(sp),
m_state(DebuggerNotReady),
m_lastGoodState(DebuggerNotReady),
m_targetState(DebuggerNotReady),
m_remoteSetupState(RemoteSetupNone),
m_inferiorPid(0),
m_modulesHandler(engine), m_modulesHandler(engine),
m_registerHandler(engine), m_registerHandler(engine),
m_sourceFilesHandler(), m_sourceFilesHandler(engine),
m_stackHandler(engine), m_stackHandler(engine),
m_threadsHandler(), m_threadsHandler(engine),
m_watchHandler(engine), m_watchHandler(engine),
m_disassemblerAgent(engine), m_disassemblerAgent(engine),
m_memoryAgent(engine), m_memoryAgent(engine)
m_isStateDebugging(false)
{ {
connect(&m_locationTimer, &QTimer::timeout, connect(&m_locationTimer, &QTimer::timeout,
this, &DebuggerEnginePrivate::resetLocation); this, &DebuggerEnginePrivate::resetLocation);
@@ -298,26 +290,26 @@ public:
{ return m_masterEngine ? m_masterEngine->runControl() : m_runControl; } { return m_masterEngine ? m_masterEngine->runControl() : m_runControl; }
void setRemoteSetupState(RemoteSetupState state); void setRemoteSetupState(RemoteSetupState state);
DebuggerEngine *m_engine; // Not owned. DebuggerEngine *m_engine = nullptr; // Not owned.
DebuggerEngine *m_masterEngine; // Not owned DebuggerEngine *m_masterEngine = nullptr; // Not owned
DebuggerRunControl *m_runControl; // Not owned. DebuggerRunControl *m_runControl = nullptr; // Not owned.
DebuggerRunParameters m_runParameters; DebuggerRunParameters m_runParameters;
// The current state. // The current state.
DebuggerState m_state; DebuggerState m_state = DebuggerNotReady;
// The state we had before something unexpected happend. // The state we had before something unexpected happend.
DebuggerState m_lastGoodState; DebuggerState m_lastGoodState = DebuggerNotReady;
// The state we are aiming for. // The state we are aiming for.
DebuggerState m_targetState; DebuggerState m_targetState = DebuggerNotReady;
// State of RemoteSetup signal/slots. // State of RemoteSetup signal/slots.
RemoteSetupState m_remoteSetupState; RemoteSetupState m_remoteSetupState = RemoteSetupNone;
Terminal m_terminal; Terminal m_terminal;
qint64 m_inferiorPid; qint64 m_inferiorPid = 0;
ModulesHandler m_modulesHandler; ModulesHandler m_modulesHandler;
RegisterHandler m_registerHandler; RegisterHandler m_registerHandler;
@@ -332,7 +324,7 @@ public:
QScopedPointer<LocationMark> m_locationMark; QScopedPointer<LocationMark> m_locationMark;
QTimer m_locationTimer; QTimer m_locationTimer;
bool m_isStateDebugging; bool m_isStateDebugging = false;
Utils::FileInProjectFinder m_fileFinder; Utils::FileInProjectFinder m_fileFinder;
QString m_qtNamespace; QString m_qtNamespace;

View File

@@ -75,23 +75,10 @@ enum ModelRoles
// Locals and Watchers // Locals and Watchers
LocalsINameRole, LocalsINameRole,
LocalsEditTypeRole, // A QVariant::type describing the item
LocalsIntegerBaseRole, // Number base 16, 10, 8, 2
LocalsNameRole, LocalsNameRole,
LocalsExpressionRole,
LocalsRawExpressionRole,
LocalsExpandedRole, // The preferred expanded state to the view LocalsExpandedRole, // The preferred expanded state to the view
LocalsRawTypeRole, // Raw type name
LocalsTypeRole, // Display type name
LocalsTypeFormatListRole,
LocalsTypeFormatRole, // Used to communicate alternative formats to the view LocalsTypeFormatRole, // Used to communicate alternative formats to the view
LocalsIndividualFormatRole, LocalsIndividualFormatRole,
LocalsObjectAddressRole, // Memory address of variable as quint64
LocalsSizeRole, // Size of variable as quint
LocalsRawValueRole, // Unformatted value as string
LocalsPointerAddressRole, // Address of (undereferenced) pointer as quint64
LocalsIsWatchpointAtObjectAddressRole,
LocalsIsWatchpointAtPointerAddressRole,
// Snapshots // Snapshots
SnapshotCapabilityRole SnapshotCapabilityRole

View File

@@ -43,17 +43,12 @@
#include "debuggerkitinformation.h" #include "debuggerkitinformation.h"
#include "memoryagent.h" #include "memoryagent.h"
#include "breakhandler.h" #include "breakhandler.h"
#include "breakwindow.h"
#include "disassemblerlines.h" #include "disassemblerlines.h"
#include "logwindow.h" #include "logwindow.h"
#include "moduleswindow.h"
#include "moduleshandler.h" #include "moduleshandler.h"
#include "registerwindow.h"
#include "snapshotwindow.h" #include "snapshotwindow.h"
#include "stackhandler.h" #include "stackhandler.h"
#include "stackwindow.h" #include "stackwindow.h"
#include "sourcefileswindow.h"
#include "threadswindow.h"
#include "watchhandler.h" #include "watchhandler.h"
#include "watchwindow.h" #include "watchwindow.h"
#include "watchutils.h" #include "watchutils.h"
@@ -463,6 +458,30 @@ static void setProxyAction(ProxyAction *proxy, Id id)
proxy->setIcon(visibleStartIcon(id, true)); proxy->setIcon(visibleStartIcon(id, true));
} }
QAction *addAction(QMenu *menu, const QString &display, bool on,
const std::function<void()> &onTriggered)
{
QAction *act = menu->addAction(display);
act->setEnabled(on);
QObject::connect(act, &QAction::triggered, onTriggered);
return act;
};
QAction *addAction(QMenu *menu, const QString &d1, const QString &d2, bool on,
const std::function<void()> &onTriggered)
{
return on ? addAction(menu, d1, true, onTriggered) : addAction(menu, d2, false);
};
QAction *addCheckableAction(QMenu *menu, const QString &display, bool on, bool checked,
const std::function<void()> &onTriggered)
{
QAction *act = addAction(menu, display, on, onTriggered);
act->setCheckable(true);
act->setChecked(checked);
return act;
}
/////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////
// //
// DummyEngine // DummyEngine
@@ -547,15 +566,15 @@ public:
/////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////
static QWidget *addSearch(BaseTreeView *treeView, const QString &title, static QWidget *addSearch(BaseTreeView *treeView, const QString &title,
const char *objectName) const QString &objectName)
{ {
QAction *act = action(UseAlternatingRowColors); QAction *act = action(UseAlternatingRowColors);
treeView->setAlternatingRowColors(act->isChecked()); treeView->setAlternatingRowColors(act->isChecked());
QObject::connect(act, &QAction::toggled, QObject::connect(act, &QAction::toggled,
treeView, &BaseTreeView::setAlternatingRowColorsHelper); treeView, &BaseTreeView::setAlternatingRowColors);
QWidget *widget = ItemViewFind::createSearchableWrapper(treeView); QWidget *widget = ItemViewFind::createSearchableWrapper(treeView);
widget->setObjectName(QLatin1String(objectName)); widget->setObjectName(objectName);
widget->setWindowTitle(title); widget->setWindowTitle(title);
return widget; return widget;
} }
@@ -1309,19 +1328,25 @@ bool DebuggerPluginPrivate::initialize(const QStringList &arguments,
m_logWindow->setObjectName(QLatin1String(DOCKWIDGET_OUTPUT)); m_logWindow->setObjectName(QLatin1String(DOCKWIDGET_OUTPUT));
m_breakHandler = new BreakHandler; m_breakHandler = new BreakHandler;
m_breakView = new BreakTreeView; m_breakView = new BaseTreeView;;
m_breakView->setWindowIcon(Icons::BREAKPOINTS.icon());
m_breakView->setSelectionMode(QAbstractItemView::ExtendedSelection);
connect(action(UseAddressInBreakpointsView), &QAction::toggled,
this, [this](bool on) { m_breakView->setColumnHidden(BreakpointAddressColumn, !on); });
m_breakView->setSettings(settings, "Debugger.BreakWindow"); m_breakView->setSettings(settings, "Debugger.BreakWindow");
m_breakView->setModel(m_breakHandler->model()); m_breakView->setModel(m_breakHandler->model());
m_breakWindow = addSearch(m_breakView, tr("Breakpoints"), DOCKWIDGET_BREAK); m_breakWindow = addSearch(m_breakView, tr("Breakpoints"), DOCKWIDGET_BREAK);
m_modulesView = new ModulesTreeView; m_modulesView = new BaseTreeView;
m_modulesView->setSortingEnabled(true);
m_modulesView->setSettings(settings, "Debugger.ModulesView"); m_modulesView->setSettings(settings, "Debugger.ModulesView");
connect(m_modulesView, &BaseTreeView::aboutToShow, this, [this] { connect(m_modulesView, &BaseTreeView::aboutToShow, this, [this] {
m_currentEngine->reloadModules(); m_currentEngine->reloadModules();
}, Qt::QueuedConnection); }, Qt::QueuedConnection);
m_modulesWindow = addSearch(m_modulesView, tr("Modules"), DOCKWIDGET_MODULES); m_modulesWindow = addSearch(m_modulesView, tr("Modules"), DOCKWIDGET_MODULES);
m_registerView = new RegisterTreeView; m_registerView = new BaseTreeView;
m_registerView->setRootIsDecorated(true);
m_registerView->setSettings(settings, "Debugger.RegisterView"); m_registerView->setSettings(settings, "Debugger.RegisterView");
connect(m_registerView, &BaseTreeView::aboutToShow, this, [this] { connect(m_registerView, &BaseTreeView::aboutToShow, this, [this] {
m_currentEngine->reloadRegisters(); m_currentEngine->reloadRegisters();
@@ -1332,14 +1357,16 @@ bool DebuggerPluginPrivate::initialize(const QStringList &arguments,
m_stackView->setSettings(settings, "Debugger.StackView"); m_stackView->setSettings(settings, "Debugger.StackView");
m_stackWindow = addSearch(m_stackView, tr("Stack"), DOCKWIDGET_STACK); m_stackWindow = addSearch(m_stackView, tr("Stack"), DOCKWIDGET_STACK);
m_sourceFilesView = new SourceFilesTreeView; m_sourceFilesView = new BaseTreeView;
m_sourceFilesView->setSortingEnabled(true);
m_sourceFilesView->setSettings(settings, "Debugger.SourceFilesView"); m_sourceFilesView->setSettings(settings, "Debugger.SourceFilesView");
connect(m_sourceFilesView, &BaseTreeView::aboutToShow, this, [this] { connect(m_sourceFilesView, &BaseTreeView::aboutToShow, this, [this] {
m_currentEngine->reloadSourceFiles(); m_currentEngine->reloadSourceFiles();
}, Qt::QueuedConnection); }, Qt::QueuedConnection);
m_sourceFilesWindow = addSearch(m_sourceFilesView, tr("Source Files"), DOCKWIDGET_SOURCE_FILES); m_sourceFilesWindow = addSearch(m_sourceFilesView, tr("Source Files"), DOCKWIDGET_SOURCE_FILES);
m_threadsView = new ThreadsTreeView; m_threadsView = new BaseTreeView;
m_threadsView->setSortingEnabled(true);
m_threadsView->setSettings(settings, "Debugger.ThreadsView"); m_threadsView->setSettings(settings, "Debugger.ThreadsView");
m_threadsWindow = addSearch(m_threadsView, tr("Threads"), DOCKWIDGET_THREADS); m_threadsWindow = addSearch(m_threadsView, tr("Threads"), DOCKWIDGET_THREADS);
@@ -2243,7 +2270,7 @@ void DebuggerPluginPrivate::requestContextMenu(TextEditorWidget *widget,
// Edit existing breakpoint. // Edit existing breakpoint.
act = menu->addAction(tr("Edit Breakpoint %1...").arg(id)); act = menu->addAction(tr("Edit Breakpoint %1...").arg(id));
connect(act, &QAction::triggered, [bp] { connect(act, &QAction::triggered, [bp] {
BreakTreeView::editBreakpoint(bp, ICore::dialogParent()); breakHandler()->editBreakpoint(bp, ICore::dialogParent());
}); });
} else { } else {

View File

@@ -25,30 +25,39 @@
#include "moduleshandler.h" #include "moduleshandler.h"
#include "debuggerconstants.h"
#include "debuggercore.h"
#include "debuggerengine.h"
#include <utils/basetreeview.h>
#include <utils/hostosinfo.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <utils/treemodel.h> #include <utils/treemodel.h>
#include <QCoreApplication> #include <QCoreApplication>
#include <QDebug> #include <QDebug>
#include <QMenu>
#include <QSortFilterProxyModel> #include <QSortFilterProxyModel>
using namespace Utils; #include <functional>
////////////////////////////////////////////////////////////////// using namespace Utils;
//
// ModulesModel
//
//////////////////////////////////////////////////////////////////
namespace Debugger { namespace Debugger {
namespace Internal { namespace Internal {
class ModuleItem : public TreeItem class ModuleItem : public TreeItem
{ {
public: Q_DECLARE_TR_FUNCTIONS(Debuggger::Internal::ModulesHandler)
QVariant data(int column, int role) const;
public: public:
QVariant data(int column, int role) const override;
bool setData(int column, const QVariant &data, int role) override;
bool contextMenuEvent(const ItemViewEvent &event);
public:
DebuggerEngine *engine;
Module module; Module module;
bool updated; bool updated;
}; };
@@ -78,110 +87,158 @@ QVariant ModuleItem::data(int column, int role) const
case 2: case 2:
if (role == Qt::DisplayRole) if (role == Qt::DisplayRole)
switch (module.symbolsRead) { switch (module.symbolsRead) {
case Module::UnknownReadState: return ModulesHandler::tr("Unknown"); case Module::UnknownReadState: return tr("Unknown");
case Module::ReadFailed: return ModulesHandler::tr("No"); case Module::ReadFailed: return tr("No");
case Module::ReadOk: return ModulesHandler::tr("Yes"); case Module::ReadOk: return tr("Yes");
} }
break; break;
case 3: case 3:
if (role == Qt::DisplayRole) if (role == Qt::DisplayRole)
switch (module.elfData.symbolsType) { switch (module.elfData.symbolsType) {
case UnknownSymbols: case UnknownSymbols: return tr("Unknown");
return ModulesHandler::tr("Unknown"); case NoSymbols: return tr("None");
case NoSymbols: case PlainSymbols: return tr("Plain");
return ModulesHandler::tr("None"); case FastSymbols: return tr("Fast");
case PlainSymbols: case LinkedSymbols: return tr("debuglnk");
return ModulesHandler::tr("Plain"); case BuildIdSymbols: return tr("buildid");
case FastSymbols:
return ModulesHandler::tr("Fast");
case LinkedSymbols:
return ModulesHandler::tr("debuglnk");
case BuildIdSymbols:
return ModulesHandler::tr("buildid");
} }
else if (role == Qt::ToolTipRole) else if (role == Qt::ToolTipRole)
switch (module.elfData.symbolsType) { switch (module.elfData.symbolsType) {
case UnknownSymbols: case UnknownSymbols:
return ModulesHandler::tr( return tr("It is unknown whether this module contains debug "
"It is unknown whether this module contains debug " "information.\nUse \"Examine Symbols\" from the "
"information.\nUse \"Examine Symbols\" from the " "context menu to initiate a check.");
"context menu to initiate a check.");
case NoSymbols: case NoSymbols:
return ModulesHandler::tr( return tr("This module neither contains nor references debug "
"This module neither contains nor references debug " "information.\nStepping into the module or setting "
"information.\nStepping into the module or setting " "breakpoints by file and line will not work.");
"breakpoints by file and line will not work.");
case PlainSymbols: case PlainSymbols:
return ModulesHandler::tr( return tr("This module contains debug information.\nStepping "
"This module contains debug information.\nStepping " "into the module or setting breakpoints by file and "
"into the module or setting breakpoints by file and " "line is expected to work.");
"line is expected to work.");
case FastSymbols: case FastSymbols:
return ModulesHandler::tr( return tr("This module contains debug information.\nStepping "
"This module contains debug information.\nStepping " "into the module or setting breakpoints by file and "
"into the module or setting breakpoints by file and " "line is expected to work.");
"line is expected to work.");
case LinkedSymbols: case LinkedSymbols:
case BuildIdSymbols: case BuildIdSymbols:
return ModulesHandler::tr( return tr("This module does not contain debug information "
"This module does not contain debug information " "itself, but contains a reference to external "
"itself, but contains a reference to external " "debug information.");
"debug information.");
} }
break; break;
case 4: case 4:
if (role == Qt::DisplayRole) if (role == Qt::DisplayRole)
if (module.startAddress) if (module.startAddress)
return QString(QLatin1String("0x") return QString("0x" + QString::number(module.startAddress, 16));
+ QString::number(module.startAddress, 16));
break; break;
case 5: case 5:
if (role == Qt::DisplayRole) { if (role == Qt::DisplayRole) {
if (module.endAddress) if (module.endAddress)
return QString(QLatin1String("0x") return QString("0x" + QString::number(module.endAddress, 16));
+ QString::number(module.endAddress, 16));
//: End address of loaded module //: End address of loaded module
return ModulesHandler::tr("<unknown>", "address"); return tr("<unknown>", "address");
} }
break; break;
} }
return QVariant(); return QVariant();
} }
bool ModuleItem::setData(int, const QVariant &data, int role)
{
if (role == BaseTreeView::ItemActivatedRole) {
engine->gotoLocation(module.modulePath);
return true;
}
if (role == BaseTreeView::ItemViewEventRole) {
ItemViewEvent ev = data.value<ItemViewEvent>();
if (ev.type() == QEvent::ContextMenu)
return contextMenuEvent(ev);
}
return false;
}
bool ModuleItem::contextMenuEvent(const ItemViewEvent &event)
{
auto menu = new QMenu;
const bool enabled = engine->debuggerActionsEnabled();
const bool canReload = engine->hasCapability(ReloadModuleCapability);
const bool canLoadSymbols = engine->hasCapability(ReloadModuleSymbolsCapability);
const bool canShowSymbols = engine->hasCapability(ShowModuleSymbolsCapability);
const bool moduleNameValid = !module.moduleName.isEmpty();
addAction(menu, tr("Update Module List"),
enabled && canReload,
[this] { engine->reloadModules(); });
addAction(menu, tr("Show Source Files for Module \"%1\"").arg(module.moduleName),
enabled && canReload,
[this] { engine->loadSymbols(module.modulePath); });
// FIXME: Dependencies only available on Windows, when "depends" is installed.
addAction(menu, tr("Show Dependencies of \"%1\"").arg(module.moduleName),
tr("Show Dependencies"),
moduleNameValid && !module.modulePath.isEmpty() && HostOsInfo::isWindowsHost(),
[this] { QProcess::startDetached("depends", QStringList(module.modulePath)); });
addAction(menu, tr("Load Symbols for All Modules"),
enabled && canLoadSymbols,
[this] { engine->loadAllSymbols(); });
addAction(menu, tr("Examine All Modules"),
enabled && canLoadSymbols,
[this] { engine->examineModules(); });
addAction(menu, tr("Load Symbols for Module \"%1\"").arg(module.moduleName),
tr("Load Symbols for Module"),
canLoadSymbols,
[this] { engine->loadSymbols(module.modulePath); });
addAction(menu, tr("Edit File \"%1\"").arg(module.moduleName),
tr("Edit File"),
moduleNameValid,
[this] { engine->gotoLocation(module.modulePath); });
addAction(menu, tr("Show Symbols in File \"%1\"").arg(module.moduleName),
tr("Show Symbols"),
canShowSymbols && moduleNameValid,
[this] { engine->requestModuleSymbols(module.modulePath); });
addAction(menu, tr("Show Sections in File \"%1\"").arg(module.moduleName),
tr("Show Sections"),
canShowSymbols && moduleNameValid,
[this] { engine->requestModuleSections(module.modulePath); });
menu->popup(event.globalPos());
return true;
}
////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////
// //
// ModulesHandler // ModulesHandler
// //
////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////
static ModuleItem *moduleFromPath(TreeItem *root, const QString &modulePath)
{
// Recent modules are more likely to be unloaded first.
for (int i = root->childCount(); --i >= 0; ) {
auto item = static_cast<ModuleItem *>(root->childAt(i));
if (item->module.modulePath == modulePath)
return item;
}
return 0;
}
ModulesHandler::ModulesHandler(DebuggerEngine *engine) ModulesHandler::ModulesHandler(DebuggerEngine *engine)
{ {
m_engine = engine; m_engine = engine;
QString pad = QLatin1String(" "); QString pad = " ";
m_model = new TreeModel(this); m_model = new ModulesModel;
m_model->setObjectName(QLatin1String("ModulesModel")); m_model->setObjectName("ModulesModel");
m_model->setHeader(QStringList() m_model->setHeader(QStringList({
<< ModulesHandler::tr("Module Name") + pad tr("Module Name") + pad,
<< ModulesHandler::tr("Module Path") + pad tr("Module Path") + pad,
<< ModulesHandler::tr("Symbols Read") + pad tr("Symbols Read") + pad,
<< ModulesHandler::tr("Symbols Type") + pad tr("Symbols Type") + pad,
<< ModulesHandler::tr("Start Address") + pad tr("Start Address") + pad,
<< ModulesHandler::tr("End Address") + pad); tr("End Address") + pad}));
m_proxyModel = new QSortFilterProxyModel(this); m_proxyModel = new QSortFilterProxyModel(this);
m_proxyModel->setObjectName(QLatin1String("ModulesProxyModel")); m_proxyModel->setObjectName("ModulesProxyModel");
m_proxyModel->setSourceModel(m_model); m_proxyModel->setSourceModel(m_model);
} }
@@ -190,6 +247,14 @@ QAbstractItemModel *ModulesHandler::model() const
return m_proxyModel; return m_proxyModel;
} }
ModuleItem *ModulesHandler::moduleFromPath(const QString &modulePath) const
{
// Recent modules are more likely to be unloaded first.
return m_model->findFirstLevelItem([modulePath](ModuleItem *item) {
return item->module.modulePath == modulePath;
});
}
void ModulesHandler::removeAll() void ModulesHandler::removeAll()
{ {
m_model->clear(); m_model->clear();
@@ -198,15 +263,13 @@ void ModulesHandler::removeAll()
Modules ModulesHandler::modules() const Modules ModulesHandler::modules() const
{ {
Modules mods; Modules mods;
TreeItem *root = m_model->rootItem(); m_model->forFirstLevelItems([&mods](ModuleItem *item) { mods.append(item->module); });
for (int i = root->childCount(); --i >= 0; )
mods.append(static_cast<ModuleItem *>(root->childAt(i))->module);
return mods; return mods;
} }
void ModulesHandler::removeModule(const QString &modulePath) void ModulesHandler::removeModule(const QString &modulePath)
{ {
if (ModuleItem *item = moduleFromPath(m_model->rootItem(), modulePath)) if (ModuleItem *item = moduleFromPath(modulePath))
m_model->destroyItem(item); m_model->destroyItem(item);
} }
@@ -216,12 +279,13 @@ void ModulesHandler::updateModule(const Module &module)
if (path.isEmpty()) if (path.isEmpty())
return; return;
ModuleItem *item = moduleFromPath(m_model->rootItem(), path); ModuleItem *item = moduleFromPath(path);
if (item) { if (item) {
item->module = module; item->module = module;
} else { } else {
item = new ModuleItem; item = new ModuleItem;
item->module = module; item->module = module;
item->engine = m_engine;
m_model->rootItem()->appendChild(item); m_model->rootItem()->appendChild(item);
} }
@@ -238,19 +302,17 @@ void ModulesHandler::updateModule(const Module &module)
void ModulesHandler::beginUpdateAll() void ModulesHandler::beginUpdateAll()
{ {
TreeItem *root = m_model->rootItem(); m_model->forFirstLevelItems([](ModuleItem *item) { item->updated = false; });
for (int i = root->childCount(); --i >= 0; )
static_cast<ModuleItem *>(root->childAt(i))->updated = false;
} }
void ModulesHandler::endUpdateAll() void ModulesHandler::endUpdateAll()
{ {
TreeItem *root = m_model->rootItem(); QList<TreeItem *> toDestroy;
for (int i = root->childCount(); --i >= 0; ) { m_model->forFirstLevelItems([&toDestroy](ModuleItem *item) {
auto item = static_cast<ModuleItem *>(root->childAt(i));
if (!item->updated) if (!item->updated)
m_model->destroyItem(item); toDestroy.append(item);
} });
qDeleteAll(toDestroy);
} }
} // namespace Internal } // namespace Internal

View File

@@ -37,6 +37,7 @@ namespace Debugger {
namespace Internal { namespace Internal {
class DebuggerEngine; class DebuggerEngine;
class ModuleItem;
////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////
// //
@@ -109,6 +110,8 @@ typedef QVector<Module> Modules;
// //
////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////
using ModulesModel = Utils::LeveledTreeModel<Utils::TypedTreeItem<ModuleItem>, ModuleItem>;
class ModulesHandler : public QObject class ModulesHandler : public QObject
{ {
Q_OBJECT Q_OBJECT
@@ -128,8 +131,10 @@ public:
Modules modules() const; Modules modules() const;
private: private:
ModuleItem *moduleFromPath(const QString &modulePath) const;
DebuggerEngine *m_engine; DebuggerEngine *m_engine;
Utils::TreeModel *m_model; ModulesModel *m_model;
QSortFilterProxyModel *m_proxyModel; QSortFilterProxyModel *m_proxyModel;
}; };

View File

@@ -1,173 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "moduleswindow.h"
#include "debuggeractions.h"
#include "debuggercore.h"
#include "debuggerengine.h"
#include <utils/hostosinfo.h>
#include <utils/qtcassert.h>
#include <utils/savedaction.h>
#include <QDebug>
#include <QMenu>
#include <QProcess>
#include <QResizeEvent>
///////////////////////////////////////////////////////////////////////////
//
// ModulesWindow
//
///////////////////////////////////////////////////////////////////////////
namespace Debugger {
namespace Internal {
ModulesTreeView::ModulesTreeView()
{
setSortingEnabled(true);
connect(this, &QAbstractItemView::activated,
this, &ModulesTreeView::moduleActivated);
}
void ModulesTreeView::moduleActivated(const QModelIndex &index)
{
DebuggerEngine *engine = currentEngine();
QTC_ASSERT(engine, return);
if (index.isValid())
engine->gotoLocation(index.sibling(index.row(), 1).data().toString());
}
void ModulesTreeView::contextMenuEvent(QContextMenuEvent *ev)
{
QString name;
QString fileName;
QModelIndex index = indexAt(ev->pos());
if (index.isValid())
index = index.sibling(index.row(), 0);
if (index.isValid()) {
name = index.data().toString();
fileName = index.sibling(index.row(), 1).data().toString();
}
DebuggerEngine *engine = currentEngine();
QTC_ASSERT(engine, return);
const bool enabled = engine->debuggerActionsEnabled();
const bool canReload = engine->hasCapability(ReloadModuleCapability);
const bool canLoadSymbols = engine->hasCapability(ReloadModuleSymbolsCapability);
QMenu menu;
QAction *actUpdateModuleList
= new QAction(tr("Update Module List"), &menu);
actUpdateModuleList->setEnabled(enabled && canReload);
QAction *actShowModuleSources
= new QAction(tr("Show Source Files for Module \"%1\"").arg(name), &menu);
actShowModuleSources->setEnabled(enabled && canReload);
QAction *actLoadSymbolsForAllModules
= new QAction(tr("Load Symbols for All Modules"), &menu);
actLoadSymbolsForAllModules->setEnabled(enabled && canLoadSymbols);
QAction *actExamineAllModules
= new QAction(tr("Examine All Modules"), &menu);
actExamineAllModules->setEnabled(enabled && canLoadSymbols);
QAction *actLoadSymbolsForModule = 0;
QAction *actEditFile = 0;
QAction *actShowModuleSymbols = 0;
QAction *actShowModuleSections = 0;
QAction *actShowDependencies = 0; // Show dependencies by running 'depends.exe'
if (name.isEmpty()) {
actLoadSymbolsForModule = new QAction(tr("Load Symbols for Module"), &menu);
actLoadSymbolsForModule->setEnabled(false);
actEditFile = new QAction(tr("Edit File"), &menu);
actEditFile->setEnabled(false);
actShowModuleSymbols = new QAction(tr("Show Symbols"), &menu);
actShowModuleSymbols->setEnabled(false);
actShowModuleSections = new QAction(tr("Show Sections"), &menu);
actShowModuleSections->setEnabled(false);
actShowDependencies = new QAction(tr("Show Dependencies"), &menu);
actShowDependencies->setEnabled(false);
} else {
actLoadSymbolsForModule
= new QAction(tr("Load Symbols for Module \"%1\"").arg(name), &menu);
actLoadSymbolsForModule->setEnabled(canLoadSymbols);
actEditFile
= new QAction(tr("Edit File \"%1\"").arg(name), &menu);
actShowModuleSymbols
= new QAction(tr("Show Symbols in File \"%1\"").arg(name), &menu);
actShowModuleSymbols->setEnabled(engine->hasCapability(ShowModuleSymbolsCapability));
actShowModuleSections
= new QAction(tr("Show Sections in File \"%1\"").arg(name), &menu);
actShowModuleSections->setEnabled(engine->hasCapability(ShowModuleSymbolsCapability));
actShowDependencies = new QAction(tr("Show Dependencies of \"%1\"").arg(name), &menu);
actShowDependencies->setEnabled(!fileName.isEmpty());
if (!Utils::HostOsInfo::isWindowsHost())
// FIXME: Dependencies only available on Windows, when "depends" is installed.
actShowDependencies->setEnabled(false);
}
menu.addAction(actUpdateModuleList);
//menu.addAction(actShowModuleSources); // FIXME
menu.addAction(actShowDependencies);
menu.addAction(actLoadSymbolsForAllModules);
menu.addAction(actExamineAllModules);
menu.addAction(actLoadSymbolsForModule);
menu.addAction(actEditFile);
menu.addAction(actShowModuleSymbols);
menu.addAction(actShowModuleSections);
menu.addSeparator();
menu.addAction(action(SettingsDialog));
QAction *act = menu.exec(ev->globalPos());
if (act == actUpdateModuleList)
engine->reloadModules();
else if (act == actShowModuleSources)
engine->loadSymbols(fileName);
else if (act == actLoadSymbolsForAllModules)
engine->loadAllSymbols();
else if (act == actExamineAllModules)
engine->examineModules();
else if (act == actLoadSymbolsForModule)
engine->loadSymbols(fileName);
else if (act == actEditFile)
engine->gotoLocation(fileName);
else if (act == actShowModuleSymbols)
engine->requestModuleSymbols(fileName);
else if (act == actShowModuleSections)
engine->requestModuleSections(fileName);
else if (actShowDependencies && act == actShowDependencies)
QProcess::startDetached(QLatin1String("depends"), QStringList(fileName));
}
} // namespace Internal
} // namespace Debugger

View File

@@ -1,46 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include <utils/basetreeview.h>
namespace Debugger {
namespace Internal {
class ModulesTreeView : public Utils::BaseTreeView
{
Q_OBJECT
public:
ModulesTreeView();
private:
void moduleActivated(const QModelIndex &index);
void contextMenuEvent(QContextMenuEvent *ev);
};
} // namespace Internal
} // namespace Debugger

View File

@@ -28,13 +28,127 @@
#include "debuggerengine.h" #include "debuggerengine.h"
#include "watchdelegatewidgets.h" #include "watchdelegatewidgets.h"
#include "memoryview.h"
#include "memoryagent.h"
#include "debuggeractions.h"
#include "debuggerdialogs.h"
#include "debuggercore.h"
#include "debuggerengine.h"
#include <utils/basetreeview.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <utils/savedaction.h>
#include <QDebug>
#include <QItemDelegate>
#include <QMenu>
#include <QPainter>
using namespace Utils; using namespace Utils;
namespace Debugger { namespace Debugger {
namespace Internal { namespace Internal {
enum RegisterColumns
{
RegisterNameColumn,
RegisterValueColumn,
RegisterColumnCount
};
enum RegisterDataRole
{
RegisterChangedRole = Qt::UserRole
};
///////////////////////////////////////////////////////////////////////
//
// RegisterDelegate
//
///////////////////////////////////////////////////////////////////////
class RegisterDelegate : public QItemDelegate
{
public:
RegisterDelegate() {}
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &,
const QModelIndex &index) const override
{
if (index.column() == RegisterValueColumn) {
auto lineEdit = new QLineEdit(parent);
lineEdit->setAlignment(Qt::AlignLeft);
lineEdit->setFrame(false);
return lineEdit;
}
return 0;
}
void setEditorData(QWidget *editor, const QModelIndex &index) const override
{
auto lineEdit = qobject_cast<QLineEdit *>(editor);
QTC_ASSERT(lineEdit, return);
lineEdit->setText(index.data(Qt::EditRole).toString());
}
void setModelData(QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index) const override
{
if (index.column() == RegisterValueColumn) {
auto lineEdit = qobject_cast<QLineEdit *>(editor);
QTC_ASSERT(lineEdit, return);
model->setData(index, lineEdit->text(), Qt::EditRole);
}
}
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option,
const QModelIndex &) const override
{
editor->setGeometry(option.rect);
}
void paint(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const override
{
if (index.column() == RegisterValueColumn) {
const bool paintRed = index.data(RegisterChangedRole).toBool();
QPen oldPen = painter->pen();
const QColor lightColor(140, 140, 140);
if (paintRed)
painter->setPen(QColor(200, 0, 0));
else
painter->setPen(lightColor);
// FIXME: performance? this changes only on real font changes.
QFontMetrics fm(option.font);
int charWidth = qMax(fm.width(QLatin1Char('x')), fm.width(QLatin1Char('0')));
QString str = index.data(Qt::DisplayRole).toString();
int x = option.rect.x();
bool light = !paintRed;
for (int i = 0; i < str.size(); ++i) {
const QChar c = str.at(i);
const int uc = c.unicode();
if (light && (uc != 'x' && uc != '0')) {
light = false;
painter->setPen(oldPen.color());
}
if (uc == ' ') {
light = true;
painter->setPen(lightColor);
} else {
QRect r = option.rect;
r.setX(x);
r.setWidth(charWidth);
painter->drawText(r, Qt::AlignHCenter, c);
}
x += charWidth;
}
painter->setPen(oldPen);
} else {
QItemDelegate::paint(painter, option, index);
}
}
};
////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////
// //
// Register // Register
@@ -288,9 +402,6 @@ void RegisterValue::shiftOneDigit(uint digit, RegisterFormat format)
// //
////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////
class RegisterItem;
class RegisterSubItem;
class RegisterEditItem : public TypedTreeItem<TreeItem, RegisterSubItem> class RegisterEditItem : public TypedTreeItem<TreeItem, RegisterSubItem>
{ {
public: public:
@@ -308,7 +419,6 @@ public:
RegisterFormat m_subFormat; RegisterFormat m_subFormat;
}; };
class RegisterSubItem : public TypedTreeItem<RegisterEditItem, RegisterItem> class RegisterSubItem : public TypedTreeItem<RegisterEditItem, RegisterItem>
{ {
public: public:
@@ -339,7 +449,7 @@ public:
class RegisterItem : public TypedTreeItem<RegisterSubItem> class RegisterItem : public TypedTreeItem<RegisterSubItem>
{ {
public: public:
explicit RegisterItem(const Register &reg); RegisterItem(DebuggerEngine *engine, const Register &reg);
QVariant data(int column, int role) const override; QVariant data(int column, int role) const override;
bool setData(int column, const QVariant &value, int role) override; bool setData(int column, const QVariant &value, int role) override;
@@ -348,13 +458,14 @@ public:
quint64 addressValue() const; quint64 addressValue() const;
void triggerChange(); void triggerChange();
DebuggerEngine *m_engine;
Register m_reg; Register m_reg;
RegisterFormat m_format; RegisterFormat m_format = HexadecimalFormat;
bool m_changed; bool m_changed = true;
}; };
RegisterItem::RegisterItem(const Register &reg) : RegisterItem::RegisterItem(DebuggerEngine *engine, const Register &reg)
m_reg(reg), m_format(HexadecimalFormat), m_changed(true) : m_engine(engine), m_reg(reg), m_changed(true)
{ {
if (m_reg.kind == UnknownRegister) if (m_reg.kind == UnknownRegister)
m_reg.guessMissingData(); m_reg.guessMissingData();
@@ -395,28 +506,15 @@ quint64 RegisterItem::addressValue() const
void RegisterItem::triggerChange() void RegisterItem::triggerChange()
{ {
QString value = "0x" + m_reg.value.toString(m_reg.kind, m_reg.size, HexadecimalFormat); QString value = "0x" + m_reg.value.toString(m_reg.kind, m_reg.size, HexadecimalFormat);
DebuggerEngine *engine = static_cast<RegisterHandler *>(model())->engine(); m_engine->setRegisterValue(m_reg.name, value);
engine->setRegisterValue(m_reg.name, value);
} }
QVariant RegisterItem::data(int column, int role) const QVariant RegisterItem::data(int column, int role) const
{ {
switch (role) { switch (role) {
case RegisterNameRole:
return m_reg.name;
case RegisterIsBigRole:
return m_reg.value.v.u64[1] > 0;
case RegisterChangedRole: case RegisterChangedRole:
return m_changed; return m_changed;
case RegisterAsAddressRole:
return addressValue();
case RegisterFormatRole:
return m_format;
case Qt::DisplayRole: case Qt::DisplayRole:
switch (column) { switch (column) {
case RegisterNameColumn: { case RegisterNameColumn: {
@@ -463,12 +561,6 @@ QVariant RegisterSubItem::data(int column, int role) const
case RegisterChangedRole: case RegisterChangedRole:
return m_changed; return m_changed;
case RegisterFormatRole:
return int(parent()->m_format);
case RegisterAsAddressRole:
return 0;
case Qt::DisplayRole: case Qt::DisplayRole:
switch (column) { switch (column) {
case RegisterNameColumn: case RegisterNameColumn:
@@ -521,7 +613,7 @@ QVariant RegisterSubItem::data(int column, int role) const
RegisterHandler::RegisterHandler(DebuggerEngine *engine) RegisterHandler::RegisterHandler(DebuggerEngine *engine)
: m_engine(engine) : m_engine(engine)
{ {
setObjectName(QLatin1String("RegisterModel")); setObjectName("RegisterModel");
setHeader({tr("Name"), tr("Value")}); setHeader({tr("Name"), tr("Value")});
} }
@@ -529,7 +621,7 @@ void RegisterHandler::updateRegister(const Register &r)
{ {
RegisterItem *reg = m_registerByName.value(r.name, 0); RegisterItem *reg = m_registerByName.value(r.name, 0);
if (!reg) { if (!reg) {
reg = new RegisterItem(r); reg = new RegisterItem(m_engine, r);
m_registerByName[r.name] = reg; m_registerByName[r.name] = reg;
rootItem()->appendChild(reg); rootItem()->appendChild(reg);
return; return;
@@ -550,15 +642,6 @@ void RegisterHandler::updateRegister(const Register &r)
} }
} }
void RegisterHandler::setNumberFormat(const QString &name, RegisterFormat format)
{
RegisterItem *reg = m_registerByName.value(name, 0);
QTC_ASSERT(reg, return);
reg->m_format = format;
QModelIndex index = indexForItem(reg);
emit dataChanged(index, index);
}
RegisterMap RegisterHandler::registerMap() const RegisterMap RegisterHandler::registerMap() const
{ {
RegisterMap result; RegisterMap result;
@@ -571,6 +654,110 @@ RegisterMap RegisterHandler::registerMap() const
return result; return result;
} }
QVariant RegisterHandler::data(const QModelIndex &idx, int role) const
{
if (role == BaseTreeView::ItemDelegateRole)
return QVariant::fromValue(static_cast<QAbstractItemDelegate *>(new RegisterDelegate));
return RegisterModel::data(idx, role);
}
bool RegisterHandler::setData(const QModelIndex &idx, const QVariant &data, int role)
{
if (role == BaseTreeView::ItemViewEventRole) {
ItemViewEvent ev = data.value<ItemViewEvent>();
if (ev.type() == QEvent::ContextMenu)
return contextMenuEvent(ev);
}
return RegisterModel::setData(idx, data, role);
}
bool RegisterHandler::contextMenuEvent(const ItemViewEvent &ev)
{
const bool actionsEnabled = m_engine->debuggerActionsEnabled();
const DebuggerState state = m_engine->state();
RegisterItem *registerItem = firstLevelItemForIndex(ev.index());
RegisterSubItem *registerSubItem = secondLevelItemForIndex(ev.index());
const quint64 address = registerItem ? registerItem->addressValue() : 0;
const QString registerName = registerItem ? registerItem->m_reg.name : QString();
auto menu = new QMenu;
addAction(menu, tr("Reload Register Listing"),
m_engine->hasCapability(RegisterCapability)
&& (state == InferiorStopOk || state == InferiorUnrunnable),
[this] { m_engine->reloadRegisters(); });
menu->addSeparator();
addAction(menu, tr("Open Memory View at Value of Register %1 0x%2")
.arg(registerName).arg(address, 0, 16),
tr("Open Memory View at Value of Register"),
address,
[this, registerName, address] {
MemoryViewSetupData data;
data.startAddress = address;
data.flags = DebuggerEngine::MemoryTrackRegister|DebuggerEngine::MemoryView;
data.registerName = registerName;
m_engine->openMemoryView(data);
});
addAction(menu, tr("Open Memory Editor at 0x%1").arg(address, 0, 16),
tr("Open Memory Editor"),
address && actionsEnabled && m_engine->hasCapability(ShowMemoryCapability),
[this, registerName, address] {
MemoryViewSetupData data;
data.startAddress = address;
data.registerName = registerName;
data.markup = RegisterMemoryView::registerMarkup(address, registerName);
data.title = RegisterMemoryView::title(registerName);
m_engine->openMemoryView(data);
});
addAction(menu, tr("Open Disassembler at 0x%1").arg(address, 0, 16),
tr("Open Disassembler"),
address && m_engine->hasCapability(DisassemblerCapability),
[this, address] { m_engine->openDisassemblerView(Location(address)); });
addAction(menu, tr("Open Disassembler..."),
m_engine->hasCapability(DisassemblerCapability),
[this, address] {
AddressDialog dialog;
if (address)
dialog.setAddress(address);
if (dialog.exec() == QDialog::Accepted)
m_engine->openDisassemblerView(Location(dialog.address()));
});
menu->addSeparator();
const RegisterFormat currentFormat = registerItem
? registerItem->m_format
: registerSubItem
? registerSubItem->parent()->m_format
: HexadecimalFormat;
auto addFormatAction = [this, menu, currentFormat, registerItem](const QString &display, RegisterFormat format) {
addCheckableAction(menu, display, registerItem, currentFormat == format, [this, registerItem, format] {
registerItem->m_format = format;
registerItem->update();
});
};
addFormatAction(tr("Hexadecimal"), HexadecimalFormat);
addFormatAction(tr("DecimalFormat"), DecimalFormat);
addFormatAction(tr("Octal"), OctalFormat);
addFormatAction(tr("Binary"), BinaryFormat);
menu->addSeparator();
menu->addAction(action(SettingsDialog));
menu->popup(ev.globalPos());
return true;
}
QVariant RegisterEditItem::data(int column, int role) const QVariant RegisterEditItem::data(int column, int role) const
{ {
switch (role) { switch (role) {

View File

@@ -27,31 +27,15 @@
#include <utils/treemodel.h> #include <utils/treemodel.h>
#include <QAbstractTableModel>
#include <QHash> #include <QHash>
#include <QVector>
namespace Utils { class ItemViewEvent; }
namespace Debugger { namespace Debugger {
namespace Internal { namespace Internal {
class DebuggerEngine; class DebuggerEngine;
enum RegisterColumns
{
RegisterNameColumn,
RegisterValueColumn,
RegisterColumnCount
};
enum RegisterDataRole
{
RegisterNameRole = Qt::UserRole,
RegisterIsBigRole,
RegisterChangedRole,
RegisterFormatRole,
RegisterAsAddressRole
};
enum RegisterKind enum RegisterKind
{ {
UnknownRegister, UnknownRegister,
@@ -102,7 +86,7 @@ public:
class Register class Register
{ {
public: public:
Register() { size = 0; kind = UnknownRegister; } Register() {}
void guessMissingData(); void guessMissingData();
QString name; QString name;
@@ -110,18 +94,18 @@ public:
RegisterValue value; RegisterValue value;
RegisterValue previousValue; RegisterValue previousValue;
QString description; QString description;
int size; int size = 0;
RegisterKind kind; RegisterKind kind = UnknownRegister;
}; };
class RegisterItem;
class RegisterSubItem; class RegisterSubItem;
class RegisterItem;
using RegisterRootItem = Utils::TypedTreeItem<RegisterItem>; using RegisterRootItem = Utils::TypedTreeItem<RegisterItem>;
using RegisterModel = Utils::LeveledTreeModel<RegisterRootItem, RegisterItem, RegisterSubItem>;
typedef QMap<quint64, QString> RegisterMap; typedef QMap<quint64, QString> RegisterMap;
class RegisterHandler class RegisterHandler : public RegisterModel
: public Utils::LeveledTreeModel<RegisterRootItem, RegisterItem, RegisterSubItem>
{ {
Q_OBJECT Q_OBJECT
@@ -129,11 +113,8 @@ public:
explicit RegisterHandler(DebuggerEngine *engine); explicit RegisterHandler(DebuggerEngine *engine);
QAbstractItemModel *model() { return this; } QAbstractItemModel *model() { return this; }
DebuggerEngine *engine() const { return m_engine; }
void updateRegister(const Register &reg); void updateRegister(const Register &reg);
void setNumberFormat(const QString &name, RegisterFormat format);
void commitUpdates() { emit layoutChanged(); } void commitUpdates() { emit layoutChanged(); }
RegisterMap registerMap() const; RegisterMap registerMap() const;
@@ -141,6 +122,11 @@ signals:
void registerChanged(const QString &name, quint64 value); // For memory views void registerChanged(const QString &name, quint64 value); // For memory views
private: private:
QVariant data(const QModelIndex &idx, int role) const override;
bool setData(const QModelIndex &idx, const QVariant &data, int role) override;
bool contextMenuEvent(const Utils::ItemViewEvent &ev);
QHash<QString, RegisterItem *> m_registerByName; QHash<QString, RegisterItem *> m_registerByName;
DebuggerEngine * const m_engine; DebuggerEngine * const m_engine;
}; };

View File

@@ -1,258 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "registerwindow.h"
#include "memoryview.h"
#include "memoryagent.h"
#include "debuggeractions.h"
#include "debuggerdialogs.h"
#include "debuggercore.h"
#include "debuggerengine.h"
#include "registerhandler.h"
#include "watchdelegatewidgets.h"
#include <utils/savedaction.h>
#include <utils/qtcassert.h>
#include <QDebug>
#include <QItemDelegate>
#include <QMenu>
#include <QPainter>
namespace Debugger {
namespace Internal {
///////////////////////////////////////////////////////////////////////
//
// RegisterDelegate
//
///////////////////////////////////////////////////////////////////////
class RegisterDelegate : public QItemDelegate
{
public:
RegisterDelegate(QObject *parent)
: QItemDelegate(parent)
{}
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &,
const QModelIndex &index) const override
{
if (index.column() == RegisterValueColumn) {
auto lineEdit = new QLineEdit(parent);
lineEdit->setAlignment(Qt::AlignLeft);
lineEdit->setFrame(false);
return lineEdit;
}
return 0;
}
void setEditorData(QWidget *editor, const QModelIndex &index) const override
{
auto lineEdit = qobject_cast<QLineEdit *>(editor);
QTC_ASSERT(lineEdit, return);
lineEdit->setText(index.data(Qt::EditRole).toString());
}
void setModelData(QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index) const override
{
if (index.column() == RegisterValueColumn) {
auto lineEdit = qobject_cast<QLineEdit *>(editor);
QTC_ASSERT(lineEdit, return);
model->setData(index, lineEdit->text(), Qt::EditRole);
}
}
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option,
const QModelIndex &) const override
{
editor->setGeometry(option.rect);
}
void paint(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const override
{
if (index.column() == RegisterValueColumn) {
const bool paintRed = index.data(RegisterChangedRole).toBool();
QPen oldPen = painter->pen();
const QColor lightColor(140, 140, 140);
if (paintRed)
painter->setPen(QColor(200, 0, 0));
else
painter->setPen(lightColor);
// FIXME: performance? this changes only on real font changes.
QFontMetrics fm(option.font);
int charWidth = qMax(fm.width(QLatin1Char('x')), fm.width(QLatin1Char('0')));
QString str = index.data(Qt::DisplayRole).toString();
int x = option.rect.x();
bool light = !paintRed;
for (int i = 0; i < str.size(); ++i) {
const QChar c = str.at(i);
const int uc = c.unicode();
if (light && (uc != 'x' && uc != '0')) {
light = false;
painter->setPen(oldPen.color());
}
if (uc == ' ') {
light = true;
painter->setPen(lightColor);
} else {
QRect r = option.rect;
r.setX(x);
r.setWidth(charWidth);
painter->drawText(r, Qt::AlignHCenter, c);
}
x += charWidth;
}
painter->setPen(oldPen);
} else {
QItemDelegate::paint(painter, option, index);
}
}
};
///////////////////////////////////////////////////////////////////////
//
// RegisterWindow
//
///////////////////////////////////////////////////////////////////////
RegisterTreeView::RegisterTreeView()
{
setItemDelegate(new RegisterDelegate(this));
setRootIsDecorated(true);
}
void RegisterTreeView::contextMenuEvent(QContextMenuEvent *ev)
{
QMenu menu;
DebuggerEngine *engine = currentEngine();
QTC_ASSERT(engine, return);
RegisterHandler *handler = engine->registerHandler();
const bool actionsEnabled = engine->debuggerActionsEnabled();
const DebuggerState state = engine->state();
QAction *actReload = menu.addAction(tr("Reload Register Listing"));
actReload->setEnabled(engine->hasCapability(RegisterCapability)
&& (state == InferiorStopOk || state == InferiorUnrunnable));
menu.addSeparator();
const QModelIndex idx = indexAt(ev->pos());
const quint64 address = idx.data(RegisterAsAddressRole).toULongLong();
QAction *actViewMemory = menu.addAction(QString());
QAction *actEditMemory = menu.addAction(QString());
QAction *actShowDisassemblerAt = menu.addAction(QString());
QAction *actShowDisassembler = menu.addAction(tr("Open Disassembler..."));
actShowDisassembler->setEnabled(engine->hasCapability(DisassemblerCapability));
const QString registerName = idx.data(RegisterNameRole).toString();
if (address) {
const bool canShow = actionsEnabled && engine->hasCapability(ShowMemoryCapability);
actEditMemory->setText(tr("Open Memory Editor at 0x%1").arg(address, 0, 16));
actEditMemory->setEnabled(canShow);
actViewMemory->setText(tr("Open Memory View at Value of Register %1 0x%2")
.arg(registerName).arg(address, 0, 16));
actShowDisassemblerAt->setText(tr("Open Disassembler at 0x%1")
.arg(address, 0, 16));
actShowDisassemblerAt->setEnabled(engine->hasCapability(DisassemblerCapability));
} else {
actEditMemory->setText(tr("Open Memory Editor"));
actViewMemory->setText(tr("Open Memory View at Value of Register"));
actEditMemory->setEnabled(false);
actViewMemory->setEnabled(false);
actShowDisassemblerAt->setText(tr("Open Disassembler"));
actShowDisassemblerAt->setEnabled(false);
}
menu.addSeparator();
const RegisterFormat format = RegisterFormat(idx.data(RegisterFormatRole).toInt());
QAction *act16 = menu.addAction(tr("Hexadecimal"));
act16->setCheckable(true);
act16->setChecked(format == HexadecimalFormat);
QAction *act10 = menu.addAction(tr("Decimal"));
act10->setCheckable(true);
act10->setChecked(format == DecimalFormat);
QAction *act8 = menu.addAction(tr("Octal"));
act8->setCheckable(true);
act8->setChecked(format == OctalFormat);
QAction *act2 = menu.addAction(tr("Binary"));
act2->setCheckable(true);
act2->setChecked(format == BinaryFormat);
menu.addSeparator();
menu.addAction(action(SettingsDialog));
const QPoint position = ev->globalPos();
QAction *act = menu.exec(position);
if (act == actReload) {
engine->reloadRegisters();
} else if (act == actEditMemory) {
MemoryViewSetupData data;
data.startAddress = address;
data.registerName = registerName;
data.markup = RegisterMemoryView::registerMarkup(address, registerName);
data.title = RegisterMemoryView::title(registerName);
engine->openMemoryView(data);
} else if (act == actViewMemory) {
MemoryViewSetupData data;
data.startAddress = address;
data.flags = DebuggerEngine::MemoryTrackRegister|DebuggerEngine::MemoryView,
data.registerName = registerName;
data.pos = position;
data.parent = this;
engine->openMemoryView(data);
} else if (act == actShowDisassembler) {
AddressDialog dialog;
if (address)
dialog.setAddress(address);
if (dialog.exec() == QDialog::Accepted)
currentEngine()->openDisassemblerView(Location(dialog.address()));
} else if (act == actShowDisassemblerAt) {
engine->openDisassemblerView(Location(address));
} else if (act == act16)
handler->setNumberFormat(registerName, HexadecimalFormat);
else if (act == act10)
handler->setNumberFormat(registerName, DecimalFormat);
else if (act == act8)
handler->setNumberFormat(registerName, OctalFormat);
else if (act == act2)
handler->setNumberFormat(registerName, BinaryFormat);
}
void RegisterTreeView::reloadRegisters()
{
// FIXME: Only trigger when becoming visible?
currentEngine()->reloadRegisters();
}
} // namespace Internal
} // namespace Debugger

View File

@@ -1,47 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include <utils/basetreeview.h>
namespace Debugger {
namespace Internal {
class RegisterTreeView : public Utils::BaseTreeView
{
Q_OBJECT
public:
RegisterTreeView();
void reloadRegisters();
private:
void contextMenuEvent(QContextMenuEvent *ev);
};
} // namespace Internal
} // namespace Debugger

View File

@@ -25,19 +25,29 @@
#include "sourcefileshandler.h" #include "sourcefileshandler.h"
#include "debuggeractions.h"
#include "debuggercore.h"
#include "debuggerengine.h"
#include <utils/basetreeview.h>
#include <utils/savedaction.h>
#include <QDebug> #include <QDebug>
#include <QFileInfo> #include <QFileInfo>
#include <QMenu>
#include <QSortFilterProxyModel> #include <QSortFilterProxyModel>
using namespace Utils;
namespace Debugger { namespace Debugger {
namespace Internal { namespace Internal {
SourceFilesHandler::SourceFilesHandler() SourceFilesHandler::SourceFilesHandler(DebuggerEngine *engine)
: m_engine(engine)
{ {
setObjectName(QLatin1String("SourceFilesModel")); setObjectName("SourceFilesModel");
QSortFilterProxyModel *proxy = new QSortFilterProxyModel(this); QSortFilterProxyModel *proxy = new QSortFilterProxyModel(this);
proxy->setObjectName(QLatin1String("SourceFilesProxyModel")); proxy->setObjectName("SourceFilesProxyModel");
proxy->setSourceModel(this); proxy->setSourceModel(this);
m_proxyModel = proxy; m_proxyModel = proxy;
} }
@@ -97,6 +107,46 @@ QVariant SourceFilesHandler::data(const QModelIndex &index, int role) const
return QVariant(); return QVariant();
} }
bool SourceFilesHandler::setData(const QModelIndex &idx, const QVariant &data, int role)
{
if (role == BaseTreeView::ItemActivatedRole) {
m_engine->gotoLocation(idx.data().toString());
return true;
}
if (role == BaseTreeView::ItemViewEventRole) {
ItemViewEvent ev = data.value<ItemViewEvent>();
if (ev.type() == QEvent::ContextMenu) {
auto menu = new QMenu;
QModelIndex index = idx.sibling(idx.row(), 0);
QString name = index.data().toString();
auto addAction = [menu](const QString &display, bool on, const std::function<void()> &onTriggered) {
QAction *act = menu->addAction(display);
act->setEnabled(on);
QObject::connect(act, &QAction::triggered, onTriggered);
return act;
};
addAction(tr("Reload Data"), m_engine->debuggerActionsEnabled(),
[this] { m_engine->reloadSourceFiles(); });
if (name.isEmpty())
addAction(tr("Open File"), false, {});
else
addAction(tr("Open File \"%1\"").arg(name), true,
[this, name] { m_engine->gotoLocation(name); });
menu->addSeparator();
menu->addAction(action(SettingsDialog));
menu->popup(ev.globalPos());
return true;
}
}
return false;
}
void SourceFilesHandler::setSourceFiles(const QMap<QString, QString> &sourceFiles) void SourceFilesHandler::setSourceFiles(const QMap<QString, QString> &sourceFiles)
{ {
beginResetModel(); beginResetModel();

View File

@@ -31,12 +31,14 @@
namespace Debugger { namespace Debugger {
namespace Internal { namespace Internal {
class DebuggerEngine;
class SourceFilesHandler : public QAbstractItemModel class SourceFilesHandler : public QAbstractItemModel
{ {
Q_OBJECT Q_OBJECT
public: public:
SourceFilesHandler(); explicit SourceFilesHandler(DebuggerEngine *engine);
int columnCount(const QModelIndex &parent) const int columnCount(const QModelIndex &parent) const
{ return parent.isValid() ? 0 : 2; } { return parent.isValid() ? 0 : 2; }
@@ -47,6 +49,7 @@ public:
{ return createIndex(row, column); } { return createIndex(row, column); }
QVariant headerData(int section, Qt::Orientation orientation, int role) const; QVariant headerData(int section, Qt::Orientation orientation, int role) const;
QVariant data(const QModelIndex &index, int role) const; QVariant data(const QModelIndex &index, int role) const;
bool setData(const QModelIndex &idx, const QVariant &data, int role) override;
Qt::ItemFlags flags(const QModelIndex &index) const; Qt::ItemFlags flags(const QModelIndex &index) const;
void clearModel(); void clearModel();
@@ -57,6 +60,7 @@ public:
QAbstractItemModel *model() { return m_proxyModel; } QAbstractItemModel *model() { return m_proxyModel; }
private: private:
DebuggerEngine *m_engine;
QStringList m_shortNames; QStringList m_shortNames;
QStringList m_fullNames; QStringList m_fullNames;
QAbstractItemModel *m_proxyModel; QAbstractItemModel *m_proxyModel;

View File

@@ -1,100 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "sourcefileswindow.h"
#include "debuggeractions.h"
#include "debuggercore.h"
#include "debuggerengine.h"
#include <utils/qtcassert.h>
#include <utils/savedaction.h>
#include <QDebug>
#include <QContextMenuEvent>
#include <QMenu>
//////////////////////////////////////////////////////////////////
//
// SourceFilesWindow
//
//////////////////////////////////////////////////////////////////
namespace Debugger {
namespace Internal {
SourceFilesTreeView::SourceFilesTreeView()
{
setSortingEnabled(true);
}
void SourceFilesTreeView::rowActivated(const QModelIndex &index)
{
DebuggerEngine *engine = currentEngine();
QTC_ASSERT(engine, return);
engine->gotoLocation(index.data().toString());
}
void SourceFilesTreeView::contextMenuEvent(QContextMenuEvent *ev)
{
DebuggerEngine *engine = currentEngine();
QTC_ASSERT(engine, return);
QModelIndex index = indexAt(ev->pos());
index = index.sibling(index.row(), 0);
QString name = index.data().toString();
bool engineActionsEnabled = engine->debuggerActionsEnabled();
QMenu menu;
QAction *act1 = new QAction(tr("Reload Data"), &menu);
act1->setEnabled(engineActionsEnabled);
//act1->setCheckable(true);
QAction *act2 = 0;
if (name.isEmpty()) {
act2 = new QAction(tr("Open File"), &menu);
act2->setEnabled(false);
} else {
act2 = new QAction(tr("Open File \"%1\"'").arg(name), &menu);
act2->setEnabled(true);
}
menu.addAction(act1);
menu.addAction(act2);
menu.addSeparator();
menu.addAction(action(SettingsDialog));
QAction *act = menu.exec(ev->globalPos());
if (act == act1)
engine->reloadSourceFiles();
else if (act == act2)
engine->gotoLocation(name);
}
} // namespace Internal
} // namespace Debugger

View File

@@ -1,46 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include <utils/basetreeview.h>
namespace Debugger {
namespace Internal {
class SourceFilesTreeView : public Utils::BaseTreeView
{
Q_OBJECT
public:
SourceFilesTreeView();
private:
void rowActivated(const QModelIndex &index);
void contextMenuEvent(QContextMenuEvent *ev);
};
} // namespace Internal
} // namespace Debugger

View File

@@ -27,26 +27,37 @@
#include "debuggeractions.h" #include "debuggeractions.h"
#include "debuggercore.h" #include "debuggercore.h"
#include "debuggericons.h" #include "debuggerdialogs.h"
#include "debuggerengine.h" #include "debuggerengine.h"
#include "debuggericons.h"
#include "debuggerprotocol.h" #include "debuggerprotocol.h"
#include "memoryagent.h"
#include "simplifytype.h" #include "simplifytype.h"
#include <coreplugin/icore.h>
#include <coreplugin/messagebox.h>
#include <utils/basetreeview.h>
#include <utils/fileutils.h> #include <utils/fileutils.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <utils/savedaction.h> #include <utils/savedaction.h>
#include <QApplication>
#include <QClipboard>
#include <QContextMenuEvent>
#include <QDebug> #include <QDebug>
#include <QDir>
#include <QFile>
#include <QFileDialog>
#include <QInputDialog>
#include <QMenu>
#include <QTextStream>
using namespace Utils;
namespace Debugger { namespace Debugger {
namespace Internal { namespace Internal {
////////////////////////////////////////////////////////////////////////
//
// StackHandler
//
////////////////////////////////////////////////////////////////////////
/*! /*!
\class Debugger::Internal::StackHandler \class Debugger::Internal::StackHandler
\brief The StackHandler class provides a model to represent the stack in a \brief The StackHandler class provides a model to represent the stack in a
@@ -56,13 +67,14 @@ namespace Internal {
StackHandler::StackHandler(DebuggerEngine *engine) StackHandler::StackHandler(DebuggerEngine *engine)
: m_engine(engine) : m_engine(engine)
{ {
setObjectName(QLatin1String("StackModel")); setObjectName("StackModel");
m_resetLocationScheduled = false;
m_contentsValid = false;
m_currentIndex = -1;
m_canExpand = false;
connect(action(OperateByInstruction), &QAction::triggered, connect(action(OperateByInstruction), &QAction::triggered,
this, &StackHandler::resetModel); this, &StackHandler::resetModel);
connect(action(ExpandStack), &QAction::triggered,
this, &StackHandler::reloadFullStack);
connect(action(MaximalStackDepth), &QAction::triggered,
this, &StackHandler::reloadFullStack);
} }
StackHandler::~StackHandler() StackHandler::~StackHandler()
@@ -104,12 +116,12 @@ QVariant StackHandler::data(const QModelIndex &index, int role) const
case StackFunctionNameColumn: case StackFunctionNameColumn:
return simplifyType(frame.function); return simplifyType(frame.function);
case StackFileNameColumn: case StackFileNameColumn:
return frame.file.isEmpty() ? frame.module : Utils::FileName::fromString(frame.file).fileName(); return frame.file.isEmpty() ? frame.module : FileName::fromString(frame.file).fileName();
case StackLineNumberColumn: case StackLineNumberColumn:
return frame.line > 0 ? QVariant(frame.line) : QVariant(); return frame.line > 0 ? QVariant(frame.line) : QVariant();
case StackAddressColumn: case StackAddressColumn:
if (frame.address) if (frame.address)
return QString::fromLatin1("0x%1").arg(frame.address, 0, 16); return QString("0x%1").arg(frame.address, 0, 16);
return QString(); return QString();
} }
return QVariant(); return QVariant();
@@ -151,7 +163,24 @@ Qt::ItemFlags StackHandler::flags(const QModelIndex &index) const
const StackFrame &frame = m_stackFrames.at(index.row()); const StackFrame &frame = m_stackFrames.at(index.row());
const bool isValid = frame.isUsable() || boolSetting(OperateByInstruction); const bool isValid = frame.isUsable() || boolSetting(OperateByInstruction);
return isValid && m_contentsValid return isValid && m_contentsValid
? QAbstractTableModel::flags(index) : Qt::ItemFlags(); ? QAbstractTableModel::flags(index) : Qt::ItemFlags();
}
bool StackHandler::setData(const QModelIndex &idx, const QVariant &data, int role)
{
Q_UNUSED(idx)
if (role == BaseTreeView::ItemActivatedRole) {
return true;
}
if (role == BaseTreeView::ItemViewEventRole) {
ItemViewEvent ev = data.value<ItemViewEvent>();
if (ev.type() == QEvent::ContextMenu)
return contextMenuEvent(ev);
}
return false;
} }
StackFrame StackHandler::currentFrame() const StackFrame StackHandler::currentFrame() const
@@ -286,5 +315,171 @@ void StackHandler::resetLocation()
} }
} }
// Input a function to be disassembled. Accept CDB syntax
// 'Module!function' for module specification
static StackFrame inputFunctionForDisassembly()
{
StackFrame frame;
QInputDialog dialog;
dialog.setInputMode(QInputDialog::TextInput);
dialog.setLabelText(StackHandler::tr("Function:"));
dialog.setWindowTitle(StackHandler::tr("Disassemble Function"));
dialog.setWindowFlags(dialog.windowFlags() & ~Qt::WindowContextHelpButtonHint);
if (dialog.exec() != QDialog::Accepted)
return frame;
const QString function = dialog.textValue();
if (function.isEmpty())
return frame;
const int bangPos = function.indexOf('!');
if (bangPos != -1) {
frame.module = function.left(bangPos);
frame.function = function.mid(bangPos + 1);
} else {
frame.function = function;
}
frame.line = 42; // trick gdb into mixed mode.
return frame;
}
// Write stack frames as task file for displaying it in the build issues pane.
void StackHandler::saveTaskFile()
{
QFile file;
QFileDialog fileDialog(Core::ICore::dialogParent());
fileDialog.setAcceptMode(QFileDialog::AcceptSave);
fileDialog.selectFile(QDir::currentPath() + "/stack.tasks");
while (!file.isOpen()) {
if (fileDialog.exec() != QDialog::Accepted)
return;
const QString fileName = fileDialog.selectedFiles().front();
file.setFileName(fileName);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
QString msg = tr("Cannot open \"%1\": %2")
.arg(QDir::toNativeSeparators(fileName), file.errorString());
Core::AsynchronousMessageBox::warning(tr("Cannot Open Task File"), msg);
}
}
QTextStream str(&file);
foreach (const StackFrame &frame, frames()) {
if (frame.isUsable())
str << frame.file << '\t' << frame.line << "\tstack\tFrame #" << frame.level << '\n';
}
}
bool StackHandler::contextMenuEvent(const ItemViewEvent &ev)
{
auto menu = new QMenu;
const int row = ev.index().row();
StackFrame frame;
if (row >= 0 && row < stackSize())
frame = frameAt(row);
const quint64 address = frame.address;
menu->addAction(action(ExpandStack));
addAction(menu, tr("Copy Contents to Clipboard"), true, [this] { copyContentsToClipboard(); });
addAction(menu, tr("Save as Task File..."), true, [this] { saveTaskFile(); });
if (m_engine->hasCapability(CreateFullBacktraceCapability))
menu->addAction(action(CreateFullBacktrace));
if (m_engine->hasCapability(AdditionalQmlStackCapability))
addAction(menu, tr("Load QML Stack"), true, [this] { m_engine->loadAdditionalQmlStack(); });
if (m_engine->hasCapability(ShowMemoryCapability))
addAction(menu, tr("Open Memory Editor at 0x%1").arg(address, 0, 16),
tr("Open Memory Editor"),
address,
[this, row, frame, address] {
MemoryViewSetupData data;
data.startAddress = address;
data.title = tr("Memory at Frame #%1 (%2) 0x%3").
arg(row).arg(frame.function).arg(address, 0, 16);
data.markup.push_back(MemoryMarkup(address, 1, QColor(Qt::blue).lighter(),
tr("Frame #%1 (%2)").arg(row).arg(frame.function)));
m_engine->openMemoryView(data);
});
if (m_engine->hasCapability(DisassemblerCapability)) {
addAction(menu, tr("Open Disassembler at 0x%1").arg(address, 0, 16),
tr("Open Disassembler"),
address,
[this, frame] { m_engine->openDisassemblerView(frame); });
addAction(menu, tr("Open Disassembler at Address..."), true,
[this, address] {
AddressDialog dialog;
if (address)
dialog.setAddress(address);
if (dialog.exec() == QDialog::Accepted)
m_engine->openDisassemblerView(Location(dialog.address()));
});
addAction(menu, tr("Disassemble Function..."), true,
[this, address] {
const StackFrame frame = inputFunctionForDisassembly();
if (!frame.function.isEmpty())
m_engine->openDisassemblerView(Location(frame));
});
}
if (m_engine->hasCapability(ShowModuleSymbolsCapability)) {
addAction(menu, tr("Try to Load Unknown Symbols"), true,
[this] { m_engine->loadSymbolsForStack(); });
}
if (m_engine->hasCapability(MemoryAddressCapability))
menu->addAction(action(UseAddressInStackView));
menu->addSeparator();
menu->addAction(action(UseToolTipsInStackView));
menu->addSeparator();
menu->addAction(action(SettingsDialog));
menu->popup(ev.globalPos());
return true;
}
void StackHandler::copyContentsToClipboard()
{
QString str;
int n = rowCount();
int m = columnCount();
QVector<int> largestColumnWidths(m, 0);
// First, find the widths of the largest columns,
// so that we can print them out nicely aligned.
for (int i = 0; i != n; ++i) {
for (int j = 0; j < m; ++j) {
const QModelIndex idx = index(i, j);
const int columnWidth = data(idx, Qt::DisplayRole).toString().size();
if (columnWidth > largestColumnWidths.at(j))
largestColumnWidths[j] = columnWidth;
}
}
for (int i = 0; i != n; ++i) {
for (int j = 0; j != m; ++j) {
QModelIndex idx = index(i, j);
const QString columnEntry = data(idx, Qt::DisplayRole).toString();
str += columnEntry;
const int difference = largestColumnWidths.at(j) - columnEntry.size();
// Add one extra space between columns.
str += QString().fill(' ', difference > 0 ? difference + 1 : 1);
}
str += '\n';
}
QClipboard *clipboard = QApplication::clipboard();
clipboard->setText(str, QClipboard::Selection);
clipboard->setText(str, QClipboard::Clipboard);
}
void StackHandler::reloadFullStack()
{
m_engine->reloadFullStack();
}
} // namespace Internal } // namespace Internal
} // namespace Debugger } // namespace Debugger

View File

@@ -29,6 +29,8 @@
#include <QAbstractItemModel> #include <QAbstractItemModel>
namespace Utils { class ItemViewEvent; }
namespace Debugger { namespace Debugger {
namespace Internal { namespace Internal {
@@ -77,20 +79,25 @@ signals:
void currentIndexChanged(); void currentIndexChanged();
private: private:
// QAbstractTableModel int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int rowCount(const QModelIndex &parent) const; int columnCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent) const; QVariant data(const QModelIndex &index, int role) const override;
QVariant data(const QModelIndex &index, int role) const; QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role) const; Qt::ItemFlags flags(const QModelIndex &index) const override;
Qt::ItemFlags flags(const QModelIndex &index) const; bool setData(const QModelIndex &idx, const QVariant &data, int role) override;
bool contextMenuEvent(const Utils::ItemViewEvent &event);
void resetModel() { beginResetModel(); endResetModel(); } void resetModel() { beginResetModel(); endResetModel(); }
void reloadFullStack();
void copyContentsToClipboard();
void saveTaskFile();
DebuggerEngine *m_engine; DebuggerEngine *m_engine;
StackFrames m_stackFrames; StackFrames m_stackFrames;
int m_currentIndex; int m_currentIndex = -1;
bool m_canExpand; bool m_canExpand = false;
bool m_resetLocationScheduled; bool m_resetLocationScheduled = false;
bool m_contentsValid; bool m_contentsValid = false;
}; };
} // namespace Internal } // namespace Internal

View File

@@ -24,29 +24,14 @@
****************************************************************************/ ****************************************************************************/
#include "stackwindow.h" #include "stackwindow.h"
#include "stackhandler.h"
#include "debuggeractions.h" #include "debuggeractions.h"
#include "debuggercore.h" #include "debuggercore.h"
#include "debuggerengine.h" #include "stackhandler.h"
#include "debuggerdialogs.h"
#include "memoryagent.h"
#include <coreplugin/messagebox.h>
#include <utils/savedaction.h> #include <utils/savedaction.h>
#include <QDebug> #include <QAction>
#include <QTextStream>
#include <QFile>
#include <QDir>
#include <QApplication>
#include <QClipboard>
#include <QContextMenuEvent>
#include <QInputDialog>
#include <QFileDialog>
#include <QMenu>
namespace Debugger { namespace Debugger {
namespace Internal { namespace Internal {
@@ -57,10 +42,6 @@ StackTreeView::StackTreeView()
connect(action(UseAddressInStackView), &QAction::toggled, connect(action(UseAddressInStackView), &QAction::toggled,
this, &StackTreeView::showAddressColumn); this, &StackTreeView::showAddressColumn);
connect(action(ExpandStack), &QAction::triggered,
this, &StackTreeView::reloadFullStack);
connect(action(MaximalStackDepth), &QAction::triggered,
this, &StackTreeView::reloadFullStack);
showAddressColumn(false); showAddressColumn(false);
} }
@@ -72,11 +53,6 @@ void StackTreeView::showAddressColumn(bool on)
resizeColumnToContents(StackAddressColumn); resizeColumnToContents(StackAddressColumn);
} }
void StackTreeView::rowActivated(const QModelIndex &index)
{
currentEngine()->activateFrame(index.row());
}
void StackTreeView::setModel(QAbstractItemModel *model) void StackTreeView::setModel(QAbstractItemModel *model)
{ {
BaseTreeView::setModel(model); BaseTreeView::setModel(model);
@@ -85,198 +61,5 @@ void StackTreeView::setModel(QAbstractItemModel *model)
showAddressColumn(action(UseAddressInStackView)->isChecked()); showAddressColumn(action(UseAddressInStackView)->isChecked());
} }
// Input a function to be disassembled. Accept CDB syntax
// 'Module!function' for module specification
static inline StackFrame inputFunctionForDisassembly()
{
StackFrame frame;
QInputDialog dialog;
dialog.setInputMode(QInputDialog::TextInput);
dialog.setLabelText(StackTreeView::tr("Function:"));
dialog.setWindowTitle(StackTreeView::tr("Disassemble Function"));
dialog.setWindowFlags(dialog.windowFlags() & ~Qt::WindowContextHelpButtonHint);
if (dialog.exec() != QDialog::Accepted)
return frame;
const QString function = dialog.textValue();
if (function.isEmpty())
return frame;
const int bangPos = function.indexOf(QLatin1Char('!'));
if (bangPos != -1) {
frame.module = function.left(bangPos);
frame.function = function.mid(bangPos + 1);
} else {
frame.function = function;
}
frame.line = 42; // trick gdb into mixed mode.
return frame;
}
// Write stack frames as task file for displaying it in the build issues pane.
void saveTaskFile(QWidget *parent, const StackHandler *sh)
{
QFile file;
QFileDialog fileDialog(parent);
fileDialog.setAcceptMode(QFileDialog::AcceptSave);
fileDialog.selectFile(QDir::currentPath() + QLatin1String("/stack.tasks"));
while (!file.isOpen()) {
if (fileDialog.exec() != QDialog::Accepted)
return;
const QString fileName = fileDialog.selectedFiles().front();
file.setFileName(fileName);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
Core::AsynchronousMessageBox::warning(StackTreeView::tr("Cannot Open Task File"),
StackTreeView::tr("Cannot open \"%1\": %2").arg(QDir::toNativeSeparators(fileName), file.errorString()));
}
}
QTextStream str(&file);
foreach (const StackFrame &frame, sh->frames()) {
if (frame.isUsable())
str << frame.file << '\t' << frame.line << "\tstack\tFrame #" << frame.level << '\n';
}
}
void StackTreeView::contextMenuEvent(QContextMenuEvent *ev)
{
DebuggerEngine *engine = currentEngine();
StackHandler *handler = engine->stackHandler();
const QModelIndex index = indexAt(ev->pos());
const int row = index.row();
StackFrame frame;
if (row >= 0 && row < handler->stackSize())
frame = handler->frameAt(row);
const quint64 address = frame.address;
QMenu menu;
menu.addAction(action(ExpandStack));
QAction *actCopyContents = menu.addAction(tr("Copy Contents to Clipboard"));
actCopyContents->setEnabled(model() != 0);
QAction *actSaveTaskFile = menu.addAction(tr("Save as Task File..."));
actSaveTaskFile->setEnabled(model() != 0);
if (engine->hasCapability(CreateFullBacktraceCapability))
menu.addAction(action(CreateFullBacktrace));
QAction *additionalQmlStackAction = 0;
if (engine->hasCapability(AdditionalQmlStackCapability))
additionalQmlStackAction = menu.addAction(tr("Load QML Stack"));
QAction *actShowMemory = 0;
if (engine->hasCapability(ShowMemoryCapability)) {
actShowMemory = menu.addAction(QString());
if (address == 0) {
actShowMemory->setText(tr("Open Memory Editor"));
actShowMemory->setEnabled(false);
} else {
actShowMemory->setText(tr("Open Memory Editor at 0x%1").arg(address, 0, 16));
actShowMemory->setEnabled(engine->hasCapability(ShowMemoryCapability));
}
}
QAction *actShowDisassemblerAt = 0;
QAction *actShowDisassemblerAtAddress = 0;
QAction *actShowDisassemblerAtFunction = 0;
if (engine->hasCapability(DisassemblerCapability)) {
actShowDisassemblerAt = menu.addAction(QString());
actShowDisassemblerAtAddress = menu.addAction(tr("Open Disassembler at Address..."));
actShowDisassemblerAtFunction = menu.addAction(tr("Disassemble Function..."));
if (address == 0) {
actShowDisassemblerAt->setText(tr("Open Disassembler"));
actShowDisassemblerAt->setEnabled(false);
} else {
actShowDisassemblerAt->setText(tr("Open Disassembler at 0x%1").arg(address, 0, 16));
}
}
QAction *actLoadSymbols = 0;
if (engine->hasCapability(ShowModuleSymbolsCapability))
actLoadSymbols = menu.addAction(tr("Try to Load Unknown Symbols"));
if (engine->hasCapability(MemoryAddressCapability))
menu.addAction(action(UseAddressInStackView));
menu.addSeparator();
menu.addAction(action(UseToolTipsInStackView));
menu.addSeparator();
menu.addAction(action(SettingsDialog));
QAction *act = menu.exec(ev->globalPos());
if (!act)
return;
if (act == actCopyContents) {
copyContentsToClipboard();
} else if (act == actShowMemory) {
MemoryViewSetupData data;
data.startAddress = address;
data.title = tr("Memory at Frame #%1 (%2) 0x%3").
arg(row).arg(frame.function).arg(address, 0, 16);
data.markup.push_back(MemoryMarkup(address, 1, QColor(Qt::blue).lighter(),
tr("Frame #%1 (%2)").arg(row).arg(frame.function)));
engine->openMemoryView(data);
} else if (act == actShowDisassemblerAtAddress) {
AddressDialog dialog;
if (address)
dialog.setAddress(address);
if (dialog.exec() == QDialog::Accepted)
currentEngine()->openDisassemblerView(Location(dialog.address()));
} else if (act == actShowDisassemblerAtFunction) {
const StackFrame frame = inputFunctionForDisassembly();
if (!frame.function.isEmpty())
currentEngine()->openDisassemblerView(Location(frame));
} else if (act == actShowDisassemblerAt)
engine->openDisassemblerView(frame);
else if (act == actLoadSymbols)
engine->loadSymbolsForStack();
else if (act == actSaveTaskFile)
saveTaskFile(this, handler);
else if (act == additionalQmlStackAction)
engine->loadAdditionalQmlStack();
}
void StackTreeView::copyContentsToClipboard()
{
QString str;
int n = model()->rowCount();
int m = model()->columnCount();
QVector<int> largestColumnWidths(m, 0);
// First, find the widths of the largest columns,
// so that we can print them out nicely aligned.
for (int i = 0; i != n; ++i) {
for (int j = 0; j < m; ++j) {
const QModelIndex index = model()->index(i, j);
const int columnWidth = model()->data(index).toString().size();
if (columnWidth > largestColumnWidths.at(j))
largestColumnWidths[j] = columnWidth;
}
}
for (int i = 0; i != n; ++i) {
for (int j = 0; j != m; ++j) {
QModelIndex index = model()->index(i, j);
const QString columnEntry = model()->data(index).toString();
str += columnEntry;
const int difference = largestColumnWidths.at(j) - columnEntry.size();
// Add one extra space between columns.
str += QString().fill(QLatin1Char(' '), difference > 0 ? difference + 1 : 1);
}
str += QLatin1Char('\n');
}
QClipboard *clipboard = QApplication::clipboard();
clipboard->setText(str, QClipboard::Selection);
clipboard->setText(str, QClipboard::Clipboard);
}
void StackTreeView::reloadFullStack()
{
currentEngine()->reloadFullStack();
}
} // namespace Internal } // namespace Internal
} // namespace Debugger } // namespace Debugger

View File

@@ -26,6 +26,7 @@
#pragma once #pragma once
#include <utils/basetreeview.h> #include <utils/basetreeview.h>
#include <QCoreApplication> #include <QCoreApplication>
namespace Debugger { namespace Debugger {
@@ -34,13 +35,12 @@ namespace Internal {
class StackTreeView : public Utils::BaseTreeView class StackTreeView : public Utils::BaseTreeView
{ {
Q_DECLARE_TR_FUNCTIONS(Debugger::Internal::StackTreeView) Q_DECLARE_TR_FUNCTIONS(Debugger::Internal::StackTreeView)
public: public:
StackTreeView(); StackTreeView();
private: private:
void rowActivated(const QModelIndex &index); void setModel(QAbstractItemModel *model) override;
void setModel(QAbstractItemModel *model);
void contextMenuEvent(QContextMenuEvent *ev);
void showAddressColumn(bool on); void showAddressColumn(bool on);
void reloadFullStack(); void reloadFullStack();

View File

@@ -25,17 +25,22 @@
#include "threadshandler.h" #include "threadshandler.h"
#include "debuggeractions.h"
#include "debuggercore.h" #include "debuggercore.h"
#include "debuggerengine.h"
#include "debuggericons.h" #include "debuggericons.h"
#include "debuggerprotocol.h" #include "debuggerprotocol.h"
#include "watchutils.h" #include "watchutils.h"
#include <utils/algorithm.h> #include <utils/algorithm.h>
#include <utils/basetreeview.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <utils/savedaction.h>
#include <QCoreApplication> #include <QCoreApplication>
#include <QDebug> #include <QDebug>
#include <QIcon> #include <QIcon>
#include <QMenu>
using namespace Utils; using namespace Utils;
@@ -225,7 +230,8 @@ public:
represent the running threads in a QTreeView or ComboBox. represent the running threads in a QTreeView or ComboBox.
*/ */
ThreadsHandler::ThreadsHandler() ThreadsHandler::ThreadsHandler(DebuggerEngine *engine)
: m_engine(engine)
{ {
m_resetLocationScheduled = false; m_resetLocationScheduled = false;
setObjectName(QLatin1String("ThreadsModel")); setObjectName(QLatin1String("ThreadsModel"));
@@ -236,6 +242,24 @@ ThreadsHandler::ThreadsHandler()
}); });
} }
bool ThreadsHandler::setData(const QModelIndex &idx, const QVariant &data, int role)
{
if (role == BaseTreeView::ItemActivatedRole) {
ThreadId id = ThreadId(idx.data(ThreadData::IdRole).toLongLong());
m_engine->selectThread(id);
return true;
}
if (role == BaseTreeView::ItemViewEventRole) {
auto menu = new QMenu;
menu->addAction(action(SettingsDialog));
menu->popup(data.value<ItemViewEvent>().globalPos());
return true;
}
return false;
}
static ThreadItem *itemForThreadId(const ThreadsHandler *handler, ThreadId threadId) static ThreadItem *itemForThreadId(const ThreadsHandler *handler, ThreadId threadId)
{ {
const auto matcher = [threadId](ThreadItem *item) { return item->threadData.id == threadId; }; const auto matcher = [threadId](ThreadItem *item) { return item->threadData.id == threadId; };

View File

@@ -38,6 +38,7 @@
namespace Debugger { namespace Debugger {
namespace Internal { namespace Internal {
class DebuggerEngine;
class GdbMi; class GdbMi;
class ThreadItem; class ThreadItem;
@@ -46,7 +47,7 @@ class ThreadsHandler : public Utils::LeveledTreeModel<Utils::TypedTreeItem<Threa
Q_OBJECT Q_OBJECT
public: public:
ThreadsHandler(); explicit ThreadsHandler(DebuggerEngine *engine);
int currentThreadIndex() const; int currentThreadIndex() const;
ThreadId currentThread() const; ThreadId currentThread() const;
@@ -82,7 +83,9 @@ private:
void updateThreadBox(); void updateThreadBox();
void sort(int column, Qt::SortOrder order); void sort(int column, Qt::SortOrder order);
bool setData(const QModelIndex &idx, const QVariant &data, int role);
DebuggerEngine *m_engine;
ThreadId m_currentId; ThreadId m_currentId;
bool m_resetLocationScheduled; bool m_resetLocationScheduled;
QHash<QString, QString> m_pidForGroupId; QHash<QString, QString> m_pidForGroupId;

View File

@@ -1,61 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "threadswindow.h"
#include "threaddata.h"
#include "debuggeractions.h"
#include "debuggercore.h"
#include "debuggerengine.h"
#include <utils/savedaction.h>
#include <QDebug>
#include <QContextMenuEvent>
#include <QMenu>
namespace Debugger {
namespace Internal {
ThreadsTreeView::ThreadsTreeView()
{
setSortingEnabled(true);
}
void ThreadsTreeView::rowActivated(const QModelIndex &index)
{
ThreadId id = ThreadId(index.data(ThreadData::IdRole).toLongLong());
currentEngine()->selectThread(id);
}
void ThreadsTreeView::contextMenuEvent(QContextMenuEvent *ev)
{
QMenu menu;
menu.addAction(action(SettingsDialog));
menu.exec(ev->globalPos());
}
} // namespace Internal
} // namespace Debugger

View File

@@ -1,46 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include <utils/basetreeview.h>
namespace Debugger {
namespace Internal {
class ThreadsTreeView : public Utils::BaseTreeView
{
Q_OBJECT
public:
ThreadsTreeView();
private:
void rowActivated(const QModelIndex &index);
void contextMenuEvent(QContextMenuEvent *ev);
};
} // namespace Internal
} // namespace Debugger

File diff suppressed because it is too large Load Diff

View File

@@ -68,7 +68,6 @@ public:
void watchExpression(const QString &exp, const QString &name = QString()); void watchExpression(const QString &exp, const QString &name = QString());
void updateWatchExpression(WatchItem *item, const QString &newExp); void updateWatchExpression(WatchItem *item, const QString &newExp);
void watchVariable(const QString &exp); void watchVariable(const QString &exp);
void clearWatches();
const WatchItem *watchItem(const QModelIndex &) const; const WatchItem *watchItem(const QModelIndex &) const;
void fetchMore(const QString &iname) const; void fetchMore(const QString &iname) const;
@@ -96,11 +95,7 @@ public:
void addDumpers(const GdbMi &dumpers); void addDumpers(const GdbMi &dumpers);
void addTypeFormats(const QString &type, const DisplayFormats &formats); void addTypeFormats(const QString &type, const DisplayFormats &formats);
void setUnprintableBase(int base);
static int unprintableBase();
QString watcherName(const QString &exp); QString watcherName(const QString &exp);
QString editorContents(const QModelIndexList &list = QModelIndexList());
void scheduleResetLocation(); void scheduleResetLocation();
void resetLocation(); void resetLocation();

File diff suppressed because it is too large Load Diff

View File

@@ -41,10 +41,8 @@ public:
WatchType type() const { return m_type; } WatchType type() const { return m_type; }
void setModel(QAbstractItemModel *model); void setModel(QAbstractItemModel *model);
void rowActivated(const QModelIndex &index);
void reset(); void reset();
void fillFormatMenu(QMenu *, const QModelIndex &mi);
static void reexpand(QTreeView *view, const QModelIndex &idx); static void reexpand(QTreeView *view, const QModelIndex &idx);
void watchExpression(const QString &exp); void watchExpression(const QString &exp);
@@ -60,25 +58,10 @@ private:
void collapseNode(const QModelIndex &idx); void collapseNode(const QModelIndex &idx);
void adjustSlider(); void adjustSlider();
void showUnprintable(int base);
void doItemsLayout(); void doItemsLayout();
void keyPressEvent(QKeyEvent *ev);
void contextMenuEvent(QContextMenuEvent *ev);
void dragEnterEvent(QDragEnterEvent *ev);
void dropEvent(QDropEvent *ev);
void dragMoveEvent(QDragMoveEvent *ev);
void mouseDoubleClickEvent(QMouseEvent *ev);
bool event(QEvent *ev);
void currentChanged(const QModelIndex &current, const QModelIndex &previous); void currentChanged(const QModelIndex &current, const QModelIndex &previous);
void inputNewExpression();
void editItem(const QModelIndex &idx);
void setModelData(int role, const QVariant &value = QVariant(),
const QModelIndex &index = QModelIndex());
WatchType m_type; WatchType m_type;
bool m_grabbing;
int m_sliderPosition; int m_sliderPosition;
}; };