forked from qt-creator/qt-creator
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:
@@ -248,6 +248,7 @@ BaseTreeView::BaseTreeView(QWidget *parent)
|
||||
setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||
setUniformRowHeights(true);
|
||||
setItemDelegate(new BaseTreeViewDelegate(this));
|
||||
setAlternatingRowColors(false);
|
||||
|
||||
QHeaderView *h = header();
|
||||
h->setDefaultAlignment(Qt::AlignLeft);
|
||||
@@ -303,6 +304,13 @@ void BaseTreeView::setModel(QAbstractItemModel *m)
|
||||
connect(model(), c[i].qsignal, c[i].receiver, c[i].qslot);
|
||||
}
|
||||
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()));
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
emit aboutToShow();
|
||||
@@ -346,6 +392,35 @@ void BaseTreeView::hideProgressIndicator()
|
||||
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)
|
||||
{
|
||||
QTC_ASSERT(!d->m_settings, qDebug() << "DUPLICATED setSettings" << key);
|
||||
@@ -354,16 +429,9 @@ void BaseTreeView::setSettings(QSettings *settings, const QByteArray &key)
|
||||
d->readSettings();
|
||||
}
|
||||
|
||||
QModelIndexList BaseTreeView::activeRows() const
|
||||
QModelIndexList ItemViewEvent::currentOrSelectedRows() const
|
||||
{
|
||||
QItemSelectionModel *selection = selectionModel();
|
||||
QModelIndexList indices = selection->selectedRows();
|
||||
if (indices.isEmpty()) {
|
||||
QModelIndex current = selection->currentIndex();
|
||||
if (current.isValid())
|
||||
indices.append(current);
|
||||
}
|
||||
return indices;
|
||||
return m_selectedRows.isEmpty() ? QModelIndexList() << m_index : m_selectedRows;
|
||||
}
|
||||
|
||||
} // namespace Utils
|
||||
|
||||
@@ -42,20 +42,31 @@ class QTCREATOR_UTILS_EXPORT BaseTreeView : public TreeView
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum { ExtraIndicesForColumnWidth = 12734 };
|
||||
enum {
|
||||
ExtraIndicesForColumnWidth = 12734,
|
||||
|
||||
ItemViewEventRole = Qt::UserRole + 12735,
|
||||
|
||||
ItemActivatedRole,
|
||||
ItemClickedRole,
|
||||
|
||||
ItemDelegateRole,
|
||||
};
|
||||
|
||||
BaseTreeView(QWidget *parent = 0);
|
||||
~BaseTreeView();
|
||||
|
||||
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 mousePressEvent(QMouseEvent *ev) override;
|
||||
void contextMenuEvent(QContextMenuEvent *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 hideProgressIndicator();
|
||||
@@ -63,11 +74,70 @@ public:
|
||||
signals:
|
||||
void aboutToShow();
|
||||
|
||||
public slots:
|
||||
void setAlternatingRowColorsHelper(bool on) { setAlternatingRowColors(on); }
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
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
|
||||
|
||||
Q_DECLARE_METATYPE(Utils::ItemViewEvent);
|
||||
|
||||
@@ -35,14 +35,20 @@
|
||||
#include <coreplugin/coreconstants.h>
|
||||
#include <coreplugin/coreplugin.h>
|
||||
#include <coreplugin/editormanager/editormanager.h>
|
||||
#include <coreplugin/icore.h>
|
||||
#include <coreplugin/idocument.h>
|
||||
|
||||
#include <extensionsystem/invoker.h>
|
||||
#include <texteditor/textmark.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/hostosinfo.h>
|
||||
#include <utils/pathchooser.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/savedaction.h>
|
||||
#include <utils/theme/theme.h>
|
||||
|
||||
#if USE_BREAK_MODEL_TEST
|
||||
@@ -52,6 +58,13 @@
|
||||
#include <QTimerEvent>
|
||||
#include <QDir>
|
||||
#include <QDebug>
|
||||
#include <QSpinBox>
|
||||
#include <QStyledItemDelegate>
|
||||
#include <QComboBox>
|
||||
#include <QGroupBox>
|
||||
#include <QCheckBox>
|
||||
#include <QFormLayout>
|
||||
#include <QMenu>
|
||||
|
||||
using namespace Core;
|
||||
using namespace Utils;
|
||||
@@ -66,11 +79,11 @@ public:
|
||||
{
|
||||
if (role == Qt::DisplayRole) {
|
||||
switch (column) {
|
||||
case 0:
|
||||
case BreakpointNumberColumn:
|
||||
return params.id.toString();
|
||||
case 1:
|
||||
case BreakpointFunctionColumn:
|
||||
return params.functionName;
|
||||
case 4:
|
||||
case BreakpointAddressColumn:
|
||||
if (params.address)
|
||||
return QString::fromLatin1("0x%1").arg(params.address, 0, 16);
|
||||
}
|
||||
@@ -107,6 +120,7 @@ public:
|
||||
int markerLineNumber() const;
|
||||
|
||||
bool needsChange() const;
|
||||
|
||||
private:
|
||||
friend class BreakHandler;
|
||||
friend class Breakpoint;
|
||||
@@ -201,9 +215,9 @@ static QString stateToString(BreakpointState 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)
|
||||
@@ -244,12 +258,644 @@ static QString typeToString(BreakpointType 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()
|
||||
: m_syncTimerId(-1)
|
||||
{
|
||||
qRegisterMetaType<BreakpointModelId>();
|
||||
TextEditor::TextMark::setCategoryColor(Constants::TEXT_MARK_CATEGORY_BREAKPOINT,
|
||||
Utils::Theme::Debugger_Breakpoint_TextMarkColor);
|
||||
Theme::Debugger_Breakpoint_TextMarkColor);
|
||||
|
||||
#if USE_BREAK_MODEL_TEST
|
||||
new ModelTest(this, 0);
|
||||
@@ -339,9 +985,15 @@ Breakpoint BreakHandler::findBreakpointByFileAndLine(const QString &fileName,
|
||||
|
||||
Breakpoint BreakHandler::breakpointById(BreakpointModelId id) const
|
||||
{
|
||||
return Breakpoint(findFirstLevelItem([id](BreakpointItem *b) {
|
||||
return b->m_id == id;
|
||||
}));
|
||||
return Breakpoint(findFirstLevelItem([id](BreakpointItem *b) { 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)
|
||||
@@ -604,7 +1256,7 @@ QVariant BreakpointItem::data(int column, int role) const
|
||||
if (role == Qt::DisplayRole) {
|
||||
const quint64 address = orig ? m_params.address : m_response.address;
|
||||
if (address)
|
||||
return QString::fromLatin1("0x%1").arg(address, 0, 16);
|
||||
return QString("0x%1").arg(address, 0, 16);
|
||||
return QVariant();
|
||||
}
|
||||
break;
|
||||
@@ -1243,6 +1895,218 @@ void Breakpoint::changeBreakpointData(const BreakpointParameters ¶ms)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@@ -33,6 +33,8 @@
|
||||
#include <QCoreApplication>
|
||||
#include <QPointer>
|
||||
|
||||
namespace Utils { class ItemViewEvent; }
|
||||
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
@@ -157,7 +159,9 @@ inline uint qHash(const Debugger::Internal::Breakpoint &b) { return b.hash(); }
|
||||
|
||||
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
|
||||
|
||||
@@ -200,8 +204,15 @@ public:
|
||||
void setWatchpointAtExpression(const QString &exp);
|
||||
|
||||
Breakpoint breakpointById(BreakpointModelId id) const;
|
||||
void editBreakpoint(Breakpoint bp, QWidget *parent);
|
||||
|
||||
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 Breakpoint;
|
||||
|
||||
@@ -209,12 +220,17 @@ private:
|
||||
void saveBreakpoints();
|
||||
|
||||
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 deletionHelper(Debugger::Internal::BreakpointModelId id);
|
||||
|
||||
void scheduleSynchronization();
|
||||
void timerEvent(QTimerEvent *event);
|
||||
|
||||
int m_syncTimerId;
|
||||
};
|
||||
|
||||
|
||||
@@ -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"
|
||||
@@ -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
|
||||
@@ -14,7 +14,6 @@ CONFIG += exceptions
|
||||
HEADERS += \
|
||||
breakhandler.h \
|
||||
breakpoint.h \
|
||||
breakwindow.h \
|
||||
commonoptionspage.h \
|
||||
debugger_global.h \
|
||||
debuggeractions.h \
|
||||
@@ -40,22 +39,18 @@ HEADERS += \
|
||||
logwindow.h \
|
||||
memoryagent.h \
|
||||
moduleshandler.h \
|
||||
moduleswindow.h \
|
||||
outputcollector.h \
|
||||
procinterrupt.h \
|
||||
registerhandler.h \
|
||||
registerwindow.h \
|
||||
snapshothandler.h \
|
||||
snapshotwindow.h \
|
||||
sourceagent.h \
|
||||
sourcefileshandler.h \
|
||||
sourcefileswindow.h \
|
||||
sourceutils.h \
|
||||
stackframe.h \
|
||||
stackhandler.h \
|
||||
stackwindow.h \
|
||||
terminal.h \
|
||||
threadswindow.h \
|
||||
watchhandler.h \
|
||||
watchutils.h \
|
||||
watchwindow.h \
|
||||
@@ -74,7 +69,6 @@ HEADERS += \
|
||||
SOURCES += \
|
||||
breakhandler.cpp \
|
||||
breakpoint.cpp \
|
||||
breakwindow.cpp \
|
||||
commonoptionspage.cpp \
|
||||
debuggeractions.cpp \
|
||||
debuggerdialogs.cpp \
|
||||
@@ -95,21 +89,17 @@ SOURCES += \
|
||||
logwindow.cpp \
|
||||
memoryagent.cpp \
|
||||
moduleshandler.cpp \
|
||||
moduleswindow.cpp \
|
||||
outputcollector.cpp \
|
||||
procinterrupt.cpp \
|
||||
registerhandler.cpp \
|
||||
registerwindow.cpp \
|
||||
snapshothandler.cpp \
|
||||
snapshotwindow.cpp \
|
||||
sourceagent.cpp \
|
||||
sourcefileshandler.cpp \
|
||||
sourcefileswindow.cpp \
|
||||
sourceutils.cpp \
|
||||
stackhandler.cpp \
|
||||
stackwindow.cpp \
|
||||
threadshandler.cpp \
|
||||
threadswindow.cpp \
|
||||
terminal.cpp \
|
||||
watchdata.cpp \
|
||||
watchhandler.cpp \
|
||||
|
||||
@@ -43,7 +43,6 @@ Project {
|
||||
files: [
|
||||
"breakhandler.cpp", "breakhandler.h",
|
||||
"breakpoint.cpp", "breakpoint.h",
|
||||
"breakwindow.cpp", "breakwindow.h",
|
||||
"commonoptionspage.cpp", "commonoptionspage.h",
|
||||
"debugger.qrc",
|
||||
"debugger_global.h",
|
||||
@@ -76,16 +75,13 @@ Project {
|
||||
"memoryagent.cpp", "memoryagent.h",
|
||||
"memoryview.cpp", "memoryview.h",
|
||||
"moduleshandler.cpp", "moduleshandler.h",
|
||||
"moduleswindow.cpp", "moduleswindow.h",
|
||||
"outputcollector.cpp", "outputcollector.h",
|
||||
"procinterrupt.cpp", "procinterrupt.h",
|
||||
"registerhandler.cpp", "registerhandler.h",
|
||||
"registerwindow.cpp", "registerwindow.h",
|
||||
"snapshothandler.cpp", "snapshothandler.h",
|
||||
"snapshotwindow.cpp", "snapshotwindow.h",
|
||||
"sourceagent.cpp", "sourceagent.h",
|
||||
"sourcefileshandler.cpp", "sourcefileshandler.h",
|
||||
"sourcefileswindow.cpp", "sourcefileswindow.h",
|
||||
"sourceutils.cpp", "sourceutils.h",
|
||||
"stackframe.cpp", "stackframe.h",
|
||||
"stackhandler.cpp", "stackhandler.h",
|
||||
@@ -93,7 +89,6 @@ Project {
|
||||
"terminal.cpp", "terminal.h",
|
||||
"threaddata.h",
|
||||
"threadshandler.cpp", "threadshandler.h",
|
||||
"threadswindow.cpp", "threadswindow.h",
|
||||
"watchdata.cpp", "watchdata.h",
|
||||
"watchdelegatewidgets.cpp", "watchdelegatewidgets.h",
|
||||
"watchhandler.cpp", "watchhandler.h",
|
||||
|
||||
@@ -32,10 +32,14 @@
|
||||
#include <QObject>
|
||||
#include <QSharedPointer>
|
||||
|
||||
#include <functional>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QIcon;
|
||||
class QMessageBox;
|
||||
class QWidget;
|
||||
class QMenu;
|
||||
class QAction;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace CPlusPlus { class Snapshot; }
|
||||
@@ -107,5 +111,12 @@ QMessageBox *showMessageBox(int icon, const QString &title,
|
||||
|
||||
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 Debugger
|
||||
|
||||
@@ -174,23 +174,15 @@ class DebuggerEnginePrivate : public QObject
|
||||
public:
|
||||
DebuggerEnginePrivate(DebuggerEngine *engine, const DebuggerRunParameters &sp)
|
||||
: m_engine(engine),
|
||||
m_masterEngine(0),
|
||||
m_runControl(0),
|
||||
m_runParameters(sp),
|
||||
m_state(DebuggerNotReady),
|
||||
m_lastGoodState(DebuggerNotReady),
|
||||
m_targetState(DebuggerNotReady),
|
||||
m_remoteSetupState(RemoteSetupNone),
|
||||
m_inferiorPid(0),
|
||||
m_modulesHandler(engine),
|
||||
m_registerHandler(engine),
|
||||
m_sourceFilesHandler(),
|
||||
m_sourceFilesHandler(engine),
|
||||
m_stackHandler(engine),
|
||||
m_threadsHandler(),
|
||||
m_threadsHandler(engine),
|
||||
m_watchHandler(engine),
|
||||
m_disassemblerAgent(engine),
|
||||
m_memoryAgent(engine),
|
||||
m_isStateDebugging(false)
|
||||
m_memoryAgent(engine)
|
||||
{
|
||||
connect(&m_locationTimer, &QTimer::timeout,
|
||||
this, &DebuggerEnginePrivate::resetLocation);
|
||||
@@ -298,26 +290,26 @@ public:
|
||||
{ return m_masterEngine ? m_masterEngine->runControl() : m_runControl; }
|
||||
void setRemoteSetupState(RemoteSetupState state);
|
||||
|
||||
DebuggerEngine *m_engine; // Not owned.
|
||||
DebuggerEngine *m_masterEngine; // Not owned
|
||||
DebuggerRunControl *m_runControl; // Not owned.
|
||||
DebuggerEngine *m_engine = nullptr; // Not owned.
|
||||
DebuggerEngine *m_masterEngine = nullptr; // Not owned
|
||||
DebuggerRunControl *m_runControl = nullptr; // Not owned.
|
||||
|
||||
DebuggerRunParameters m_runParameters;
|
||||
|
||||
// The current state.
|
||||
DebuggerState m_state;
|
||||
DebuggerState m_state = DebuggerNotReady;
|
||||
|
||||
// The state we had before something unexpected happend.
|
||||
DebuggerState m_lastGoodState;
|
||||
DebuggerState m_lastGoodState = DebuggerNotReady;
|
||||
|
||||
// The state we are aiming for.
|
||||
DebuggerState m_targetState;
|
||||
DebuggerState m_targetState = DebuggerNotReady;
|
||||
|
||||
// State of RemoteSetup signal/slots.
|
||||
RemoteSetupState m_remoteSetupState;
|
||||
RemoteSetupState m_remoteSetupState = RemoteSetupNone;
|
||||
|
||||
Terminal m_terminal;
|
||||
qint64 m_inferiorPid;
|
||||
qint64 m_inferiorPid = 0;
|
||||
|
||||
ModulesHandler m_modulesHandler;
|
||||
RegisterHandler m_registerHandler;
|
||||
@@ -332,7 +324,7 @@ public:
|
||||
QScopedPointer<LocationMark> m_locationMark;
|
||||
QTimer m_locationTimer;
|
||||
|
||||
bool m_isStateDebugging;
|
||||
bool m_isStateDebugging = false;
|
||||
|
||||
Utils::FileInProjectFinder m_fileFinder;
|
||||
QString m_qtNamespace;
|
||||
|
||||
@@ -75,23 +75,10 @@ enum ModelRoles
|
||||
|
||||
// Locals and Watchers
|
||||
LocalsINameRole,
|
||||
LocalsEditTypeRole, // A QVariant::type describing the item
|
||||
LocalsIntegerBaseRole, // Number base 16, 10, 8, 2
|
||||
LocalsNameRole,
|
||||
LocalsExpressionRole,
|
||||
LocalsRawExpressionRole,
|
||||
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
|
||||
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
|
||||
SnapshotCapabilityRole
|
||||
|
||||
@@ -43,17 +43,12 @@
|
||||
#include "debuggerkitinformation.h"
|
||||
#include "memoryagent.h"
|
||||
#include "breakhandler.h"
|
||||
#include "breakwindow.h"
|
||||
#include "disassemblerlines.h"
|
||||
#include "logwindow.h"
|
||||
#include "moduleswindow.h"
|
||||
#include "moduleshandler.h"
|
||||
#include "registerwindow.h"
|
||||
#include "snapshotwindow.h"
|
||||
#include "stackhandler.h"
|
||||
#include "stackwindow.h"
|
||||
#include "sourcefileswindow.h"
|
||||
#include "threadswindow.h"
|
||||
#include "watchhandler.h"
|
||||
#include "watchwindow.h"
|
||||
#include "watchutils.h"
|
||||
@@ -463,6 +458,30 @@ static void setProxyAction(ProxyAction *proxy, Id id)
|
||||
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
|
||||
@@ -547,15 +566,15 @@ public:
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
|
||||
static QWidget *addSearch(BaseTreeView *treeView, const QString &title,
|
||||
const char *objectName)
|
||||
const QString &objectName)
|
||||
{
|
||||
QAction *act = action(UseAlternatingRowColors);
|
||||
treeView->setAlternatingRowColors(act->isChecked());
|
||||
QObject::connect(act, &QAction::toggled,
|
||||
treeView, &BaseTreeView::setAlternatingRowColorsHelper);
|
||||
treeView, &BaseTreeView::setAlternatingRowColors);
|
||||
|
||||
QWidget *widget = ItemViewFind::createSearchableWrapper(treeView);
|
||||
widget->setObjectName(QLatin1String(objectName));
|
||||
widget->setObjectName(objectName);
|
||||
widget->setWindowTitle(title);
|
||||
return widget;
|
||||
}
|
||||
@@ -1309,19 +1328,25 @@ bool DebuggerPluginPrivate::initialize(const QStringList &arguments,
|
||||
m_logWindow->setObjectName(QLatin1String(DOCKWIDGET_OUTPUT));
|
||||
|
||||
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->setModel(m_breakHandler->model());
|
||||
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");
|
||||
connect(m_modulesView, &BaseTreeView::aboutToShow, this, [this] {
|
||||
m_currentEngine->reloadModules();
|
||||
}, Qt::QueuedConnection);
|
||||
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");
|
||||
connect(m_registerView, &BaseTreeView::aboutToShow, this, [this] {
|
||||
m_currentEngine->reloadRegisters();
|
||||
@@ -1332,14 +1357,16 @@ bool DebuggerPluginPrivate::initialize(const QStringList &arguments,
|
||||
m_stackView->setSettings(settings, "Debugger.StackView");
|
||||
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");
|
||||
connect(m_sourceFilesView, &BaseTreeView::aboutToShow, this, [this] {
|
||||
m_currentEngine->reloadSourceFiles();
|
||||
}, Qt::QueuedConnection);
|
||||
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_threadsWindow = addSearch(m_threadsView, tr("Threads"), DOCKWIDGET_THREADS);
|
||||
|
||||
@@ -2243,7 +2270,7 @@ void DebuggerPluginPrivate::requestContextMenu(TextEditorWidget *widget,
|
||||
// Edit existing breakpoint.
|
||||
act = menu->addAction(tr("Edit Breakpoint %1...").arg(id));
|
||||
connect(act, &QAction::triggered, [bp] {
|
||||
BreakTreeView::editBreakpoint(bp, ICore::dialogParent());
|
||||
breakHandler()->editBreakpoint(bp, ICore::dialogParent());
|
||||
});
|
||||
|
||||
} else {
|
||||
|
||||
@@ -25,30 +25,39 @@
|
||||
|
||||
#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/treemodel.h>
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDebug>
|
||||
#include <QMenu>
|
||||
#include <QSortFilterProxyModel>
|
||||
|
||||
using namespace Utils;
|
||||
#include <functional>
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// ModulesModel
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////
|
||||
using namespace Utils;
|
||||
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
class ModuleItem : public TreeItem
|
||||
{
|
||||
public:
|
||||
QVariant data(int column, int role) const;
|
||||
Q_DECLARE_TR_FUNCTIONS(Debuggger::Internal::ModulesHandler)
|
||||
|
||||
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;
|
||||
bool updated;
|
||||
};
|
||||
@@ -78,110 +87,158 @@ QVariant ModuleItem::data(int column, int role) const
|
||||
case 2:
|
||||
if (role == Qt::DisplayRole)
|
||||
switch (module.symbolsRead) {
|
||||
case Module::UnknownReadState: return ModulesHandler::tr("Unknown");
|
||||
case Module::ReadFailed: return ModulesHandler::tr("No");
|
||||
case Module::ReadOk: return ModulesHandler::tr("Yes");
|
||||
case Module::UnknownReadState: return tr("Unknown");
|
||||
case Module::ReadFailed: return tr("No");
|
||||
case Module::ReadOk: return tr("Yes");
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
if (role == Qt::DisplayRole)
|
||||
switch (module.elfData.symbolsType) {
|
||||
case UnknownSymbols:
|
||||
return ModulesHandler::tr("Unknown");
|
||||
case NoSymbols:
|
||||
return ModulesHandler::tr("None");
|
||||
case PlainSymbols:
|
||||
return ModulesHandler::tr("Plain");
|
||||
case FastSymbols:
|
||||
return ModulesHandler::tr("Fast");
|
||||
case LinkedSymbols:
|
||||
return ModulesHandler::tr("debuglnk");
|
||||
case BuildIdSymbols:
|
||||
return ModulesHandler::tr("buildid");
|
||||
case UnknownSymbols: return tr("Unknown");
|
||||
case NoSymbols: return tr("None");
|
||||
case PlainSymbols: return tr("Plain");
|
||||
case FastSymbols: return tr("Fast");
|
||||
case LinkedSymbols: return tr("debuglnk");
|
||||
case BuildIdSymbols: return tr("buildid");
|
||||
}
|
||||
else if (role == Qt::ToolTipRole)
|
||||
switch (module.elfData.symbolsType) {
|
||||
case UnknownSymbols:
|
||||
return ModulesHandler::tr(
|
||||
"It is unknown whether this module contains debug "
|
||||
"information.\nUse \"Examine Symbols\" from the "
|
||||
"context menu to initiate a check.");
|
||||
return tr("It is unknown whether this module contains debug "
|
||||
"information.\nUse \"Examine Symbols\" from the "
|
||||
"context menu to initiate a check.");
|
||||
case NoSymbols:
|
||||
return ModulesHandler::tr(
|
||||
"This module neither contains nor references debug "
|
||||
"information.\nStepping into the module or setting "
|
||||
"breakpoints by file and line will not work.");
|
||||
return tr("This module neither contains nor references debug "
|
||||
"information.\nStepping into the module or setting "
|
||||
"breakpoints by file and line will not work.");
|
||||
case PlainSymbols:
|
||||
return ModulesHandler::tr(
|
||||
"This module contains debug information.\nStepping "
|
||||
"into the module or setting breakpoints by file and "
|
||||
"line is expected to work.");
|
||||
return tr("This module contains debug information.\nStepping "
|
||||
"into the module or setting breakpoints by file and "
|
||||
"line is expected to work.");
|
||||
case FastSymbols:
|
||||
return ModulesHandler::tr(
|
||||
"This module contains debug information.\nStepping "
|
||||
"into the module or setting breakpoints by file and "
|
||||
"line is expected to work.");
|
||||
return tr("This module contains debug information.\nStepping "
|
||||
"into the module or setting breakpoints by file and "
|
||||
"line is expected to work.");
|
||||
case LinkedSymbols:
|
||||
case BuildIdSymbols:
|
||||
return ModulesHandler::tr(
|
||||
"This module does not contain debug information "
|
||||
"itself, but contains a reference to external "
|
||||
"debug information.");
|
||||
return tr("This module does not contain debug information "
|
||||
"itself, but contains a reference to external "
|
||||
"debug information.");
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
if (role == Qt::DisplayRole)
|
||||
if (module.startAddress)
|
||||
return QString(QLatin1String("0x")
|
||||
+ QString::number(module.startAddress, 16));
|
||||
return QString("0x" + QString::number(module.startAddress, 16));
|
||||
break;
|
||||
case 5:
|
||||
if (role == Qt::DisplayRole) {
|
||||
if (module.endAddress)
|
||||
return QString(QLatin1String("0x")
|
||||
+ QString::number(module.endAddress, 16));
|
||||
return QString("0x" + QString::number(module.endAddress, 16));
|
||||
//: End address of loaded module
|
||||
return ModulesHandler::tr("<unknown>", "address");
|
||||
return tr("<unknown>", "address");
|
||||
}
|
||||
break;
|
||||
}
|
||||
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
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////
|
||||
|
||||
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)
|
||||
{
|
||||
m_engine = engine;
|
||||
|
||||
QString pad = QLatin1String(" ");
|
||||
m_model = new TreeModel(this);
|
||||
m_model->setObjectName(QLatin1String("ModulesModel"));
|
||||
m_model->setHeader(QStringList()
|
||||
<< ModulesHandler::tr("Module Name") + pad
|
||||
<< ModulesHandler::tr("Module Path") + pad
|
||||
<< ModulesHandler::tr("Symbols Read") + pad
|
||||
<< ModulesHandler::tr("Symbols Type") + pad
|
||||
<< ModulesHandler::tr("Start Address") + pad
|
||||
<< ModulesHandler::tr("End Address") + pad);
|
||||
QString pad = " ";
|
||||
m_model = new ModulesModel;
|
||||
m_model->setObjectName("ModulesModel");
|
||||
m_model->setHeader(QStringList({
|
||||
tr("Module Name") + pad,
|
||||
tr("Module Path") + pad,
|
||||
tr("Symbols Read") + pad,
|
||||
tr("Symbols Type") + pad,
|
||||
tr("Start Address") + pad,
|
||||
tr("End Address") + pad}));
|
||||
|
||||
m_proxyModel = new QSortFilterProxyModel(this);
|
||||
m_proxyModel->setObjectName(QLatin1String("ModulesProxyModel"));
|
||||
m_proxyModel->setObjectName("ModulesProxyModel");
|
||||
m_proxyModel->setSourceModel(m_model);
|
||||
}
|
||||
|
||||
@@ -190,6 +247,14 @@ QAbstractItemModel *ModulesHandler::model() const
|
||||
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()
|
||||
{
|
||||
m_model->clear();
|
||||
@@ -198,15 +263,13 @@ void ModulesHandler::removeAll()
|
||||
Modules ModulesHandler::modules() const
|
||||
{
|
||||
Modules mods;
|
||||
TreeItem *root = m_model->rootItem();
|
||||
for (int i = root->childCount(); --i >= 0; )
|
||||
mods.append(static_cast<ModuleItem *>(root->childAt(i))->module);
|
||||
m_model->forFirstLevelItems([&mods](ModuleItem *item) { mods.append(item->module); });
|
||||
return mods;
|
||||
}
|
||||
|
||||
void ModulesHandler::removeModule(const QString &modulePath)
|
||||
{
|
||||
if (ModuleItem *item = moduleFromPath(m_model->rootItem(), modulePath))
|
||||
if (ModuleItem *item = moduleFromPath(modulePath))
|
||||
m_model->destroyItem(item);
|
||||
}
|
||||
|
||||
@@ -216,12 +279,13 @@ void ModulesHandler::updateModule(const Module &module)
|
||||
if (path.isEmpty())
|
||||
return;
|
||||
|
||||
ModuleItem *item = moduleFromPath(m_model->rootItem(), path);
|
||||
ModuleItem *item = moduleFromPath(path);
|
||||
if (item) {
|
||||
item->module = module;
|
||||
} else {
|
||||
item = new ModuleItem;
|
||||
item->module = module;
|
||||
item->engine = m_engine;
|
||||
m_model->rootItem()->appendChild(item);
|
||||
}
|
||||
|
||||
@@ -238,19 +302,17 @@ void ModulesHandler::updateModule(const Module &module)
|
||||
|
||||
void ModulesHandler::beginUpdateAll()
|
||||
{
|
||||
TreeItem *root = m_model->rootItem();
|
||||
for (int i = root->childCount(); --i >= 0; )
|
||||
static_cast<ModuleItem *>(root->childAt(i))->updated = false;
|
||||
m_model->forFirstLevelItems([](ModuleItem *item) { item->updated = false; });
|
||||
}
|
||||
|
||||
void ModulesHandler::endUpdateAll()
|
||||
{
|
||||
TreeItem *root = m_model->rootItem();
|
||||
for (int i = root->childCount(); --i >= 0; ) {
|
||||
auto item = static_cast<ModuleItem *>(root->childAt(i));
|
||||
QList<TreeItem *> toDestroy;
|
||||
m_model->forFirstLevelItems([&toDestroy](ModuleItem *item) {
|
||||
if (!item->updated)
|
||||
m_model->destroyItem(item);
|
||||
}
|
||||
toDestroy.append(item);
|
||||
});
|
||||
qDeleteAll(toDestroy);
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
|
||||
@@ -37,6 +37,7 @@ namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
class DebuggerEngine;
|
||||
class ModuleItem;
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
//
|
||||
@@ -109,6 +110,8 @@ typedef QVector<Module> Modules;
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////
|
||||
|
||||
using ModulesModel = Utils::LeveledTreeModel<Utils::TypedTreeItem<ModuleItem>, ModuleItem>;
|
||||
|
||||
class ModulesHandler : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -128,8 +131,10 @@ public:
|
||||
Modules modules() const;
|
||||
|
||||
private:
|
||||
ModuleItem *moduleFromPath(const QString &modulePath) const;
|
||||
|
||||
DebuggerEngine *m_engine;
|
||||
Utils::TreeModel *m_model;
|
||||
ModulesModel *m_model;
|
||||
QSortFilterProxyModel *m_proxyModel;
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -28,13 +28,127 @@
|
||||
#include "debuggerengine.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/savedaction.h>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QItemDelegate>
|
||||
#include <QMenu>
|
||||
#include <QPainter>
|
||||
|
||||
using namespace Utils;
|
||||
|
||||
namespace Debugger {
|
||||
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
|
||||
@@ -288,9 +402,6 @@ void RegisterValue::shiftOneDigit(uint digit, RegisterFormat format)
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////
|
||||
|
||||
class RegisterItem;
|
||||
class RegisterSubItem;
|
||||
|
||||
class RegisterEditItem : public TypedTreeItem<TreeItem, RegisterSubItem>
|
||||
{
|
||||
public:
|
||||
@@ -308,7 +419,6 @@ public:
|
||||
RegisterFormat m_subFormat;
|
||||
};
|
||||
|
||||
|
||||
class RegisterSubItem : public TypedTreeItem<RegisterEditItem, RegisterItem>
|
||||
{
|
||||
public:
|
||||
@@ -339,7 +449,7 @@ public:
|
||||
class RegisterItem : public TypedTreeItem<RegisterSubItem>
|
||||
{
|
||||
public:
|
||||
explicit RegisterItem(const Register ®);
|
||||
RegisterItem(DebuggerEngine *engine, const Register ®);
|
||||
|
||||
QVariant data(int column, int role) const override;
|
||||
bool setData(int column, const QVariant &value, int role) override;
|
||||
@@ -348,13 +458,14 @@ public:
|
||||
quint64 addressValue() const;
|
||||
void triggerChange();
|
||||
|
||||
DebuggerEngine *m_engine;
|
||||
Register m_reg;
|
||||
RegisterFormat m_format;
|
||||
bool m_changed;
|
||||
RegisterFormat m_format = HexadecimalFormat;
|
||||
bool m_changed = true;
|
||||
};
|
||||
|
||||
RegisterItem::RegisterItem(const Register ®) :
|
||||
m_reg(reg), m_format(HexadecimalFormat), m_changed(true)
|
||||
RegisterItem::RegisterItem(DebuggerEngine *engine, const Register ®)
|
||||
: m_engine(engine), m_reg(reg), m_changed(true)
|
||||
{
|
||||
if (m_reg.kind == UnknownRegister)
|
||||
m_reg.guessMissingData();
|
||||
@@ -395,28 +506,15 @@ quint64 RegisterItem::addressValue() const
|
||||
void RegisterItem::triggerChange()
|
||||
{
|
||||
QString value = "0x" + m_reg.value.toString(m_reg.kind, m_reg.size, HexadecimalFormat);
|
||||
DebuggerEngine *engine = static_cast<RegisterHandler *>(model())->engine();
|
||||
engine->setRegisterValue(m_reg.name, value);
|
||||
m_engine->setRegisterValue(m_reg.name, value);
|
||||
}
|
||||
|
||||
QVariant RegisterItem::data(int column, int role) const
|
||||
{
|
||||
switch (role) {
|
||||
case RegisterNameRole:
|
||||
return m_reg.name;
|
||||
|
||||
case RegisterIsBigRole:
|
||||
return m_reg.value.v.u64[1] > 0;
|
||||
|
||||
case RegisterChangedRole:
|
||||
return m_changed;
|
||||
|
||||
case RegisterAsAddressRole:
|
||||
return addressValue();
|
||||
|
||||
case RegisterFormatRole:
|
||||
return m_format;
|
||||
|
||||
case Qt::DisplayRole:
|
||||
switch (column) {
|
||||
case RegisterNameColumn: {
|
||||
@@ -463,12 +561,6 @@ QVariant RegisterSubItem::data(int column, int role) const
|
||||
case RegisterChangedRole:
|
||||
return m_changed;
|
||||
|
||||
case RegisterFormatRole:
|
||||
return int(parent()->m_format);
|
||||
|
||||
case RegisterAsAddressRole:
|
||||
return 0;
|
||||
|
||||
case Qt::DisplayRole:
|
||||
switch (column) {
|
||||
case RegisterNameColumn:
|
||||
@@ -521,7 +613,7 @@ QVariant RegisterSubItem::data(int column, int role) const
|
||||
RegisterHandler::RegisterHandler(DebuggerEngine *engine)
|
||||
: m_engine(engine)
|
||||
{
|
||||
setObjectName(QLatin1String("RegisterModel"));
|
||||
setObjectName("RegisterModel");
|
||||
setHeader({tr("Name"), tr("Value")});
|
||||
}
|
||||
|
||||
@@ -529,7 +621,7 @@ void RegisterHandler::updateRegister(const Register &r)
|
||||
{
|
||||
RegisterItem *reg = m_registerByName.value(r.name, 0);
|
||||
if (!reg) {
|
||||
reg = new RegisterItem(r);
|
||||
reg = new RegisterItem(m_engine, r);
|
||||
m_registerByName[r.name] = reg;
|
||||
rootItem()->appendChild(reg);
|
||||
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 result;
|
||||
@@ -571,6 +654,110 @@ RegisterMap RegisterHandler::registerMap() const
|
||||
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
|
||||
{
|
||||
switch (role) {
|
||||
|
||||
@@ -27,31 +27,15 @@
|
||||
|
||||
#include <utils/treemodel.h>
|
||||
|
||||
#include <QAbstractTableModel>
|
||||
#include <QHash>
|
||||
#include <QVector>
|
||||
|
||||
namespace Utils { class ItemViewEvent; }
|
||||
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
class DebuggerEngine;
|
||||
|
||||
enum RegisterColumns
|
||||
{
|
||||
RegisterNameColumn,
|
||||
RegisterValueColumn,
|
||||
RegisterColumnCount
|
||||
};
|
||||
|
||||
enum RegisterDataRole
|
||||
{
|
||||
RegisterNameRole = Qt::UserRole,
|
||||
RegisterIsBigRole,
|
||||
RegisterChangedRole,
|
||||
RegisterFormatRole,
|
||||
RegisterAsAddressRole
|
||||
};
|
||||
|
||||
enum RegisterKind
|
||||
{
|
||||
UnknownRegister,
|
||||
@@ -102,7 +86,7 @@ public:
|
||||
class Register
|
||||
{
|
||||
public:
|
||||
Register() { size = 0; kind = UnknownRegister; }
|
||||
Register() {}
|
||||
void guessMissingData();
|
||||
|
||||
QString name;
|
||||
@@ -110,18 +94,18 @@ public:
|
||||
RegisterValue value;
|
||||
RegisterValue previousValue;
|
||||
QString description;
|
||||
int size;
|
||||
RegisterKind kind;
|
||||
int size = 0;
|
||||
RegisterKind kind = UnknownRegister;
|
||||
};
|
||||
|
||||
class RegisterItem;
|
||||
class RegisterSubItem;
|
||||
class RegisterItem;
|
||||
using RegisterRootItem = Utils::TypedTreeItem<RegisterItem>;
|
||||
using RegisterModel = Utils::LeveledTreeModel<RegisterRootItem, RegisterItem, RegisterSubItem>;
|
||||
|
||||
typedef QMap<quint64, QString> RegisterMap;
|
||||
|
||||
class RegisterHandler
|
||||
: public Utils::LeveledTreeModel<RegisterRootItem, RegisterItem, RegisterSubItem>
|
||||
class RegisterHandler : public RegisterModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
@@ -129,11 +113,8 @@ public:
|
||||
explicit RegisterHandler(DebuggerEngine *engine);
|
||||
|
||||
QAbstractItemModel *model() { return this; }
|
||||
DebuggerEngine *engine() const { return m_engine; }
|
||||
|
||||
void updateRegister(const Register ®);
|
||||
|
||||
void setNumberFormat(const QString &name, RegisterFormat format);
|
||||
void commitUpdates() { emit layoutChanged(); }
|
||||
RegisterMap registerMap() const;
|
||||
|
||||
@@ -141,6 +122,11 @@ signals:
|
||||
void registerChanged(const QString &name, quint64 value); // For memory views
|
||||
|
||||
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;
|
||||
DebuggerEngine * const m_engine;
|
||||
};
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -25,19 +25,29 @@
|
||||
|
||||
#include "sourcefileshandler.h"
|
||||
|
||||
#include "debuggeractions.h"
|
||||
#include "debuggercore.h"
|
||||
#include "debuggerengine.h"
|
||||
|
||||
#include <utils/basetreeview.h>
|
||||
#include <utils/savedaction.h>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QFileInfo>
|
||||
|
||||
#include <QMenu>
|
||||
#include <QSortFilterProxyModel>
|
||||
|
||||
using namespace Utils;
|
||||
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
SourceFilesHandler::SourceFilesHandler()
|
||||
SourceFilesHandler::SourceFilesHandler(DebuggerEngine *engine)
|
||||
: m_engine(engine)
|
||||
{
|
||||
setObjectName(QLatin1String("SourceFilesModel"));
|
||||
setObjectName("SourceFilesModel");
|
||||
QSortFilterProxyModel *proxy = new QSortFilterProxyModel(this);
|
||||
proxy->setObjectName(QLatin1String("SourceFilesProxyModel"));
|
||||
proxy->setObjectName("SourceFilesProxyModel");
|
||||
proxy->setSourceModel(this);
|
||||
m_proxyModel = proxy;
|
||||
}
|
||||
@@ -97,6 +107,46 @@ QVariant SourceFilesHandler::data(const QModelIndex &index, int role) const
|
||||
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)
|
||||
{
|
||||
beginResetModel();
|
||||
|
||||
@@ -31,12 +31,14 @@
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
class DebuggerEngine;
|
||||
|
||||
class SourceFilesHandler : public QAbstractItemModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
SourceFilesHandler();
|
||||
explicit SourceFilesHandler(DebuggerEngine *engine);
|
||||
|
||||
int columnCount(const QModelIndex &parent) const
|
||||
{ return parent.isValid() ? 0 : 2; }
|
||||
@@ -47,6 +49,7 @@ public:
|
||||
{ return createIndex(row, column); }
|
||||
QVariant headerData(int section, Qt::Orientation orientation, 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;
|
||||
|
||||
void clearModel();
|
||||
@@ -57,6 +60,7 @@ public:
|
||||
QAbstractItemModel *model() { return m_proxyModel; }
|
||||
|
||||
private:
|
||||
DebuggerEngine *m_engine;
|
||||
QStringList m_shortNames;
|
||||
QStringList m_fullNames;
|
||||
QAbstractItemModel *m_proxyModel;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -27,26 +27,37 @@
|
||||
|
||||
#include "debuggeractions.h"
|
||||
#include "debuggercore.h"
|
||||
#include "debuggericons.h"
|
||||
#include "debuggerdialogs.h"
|
||||
#include "debuggerengine.h"
|
||||
#include "debuggericons.h"
|
||||
#include "debuggerprotocol.h"
|
||||
#include "memoryagent.h"
|
||||
#include "simplifytype.h"
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
#include <coreplugin/messagebox.h>
|
||||
|
||||
#include <utils/basetreeview.h>
|
||||
#include <utils/fileutils.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/savedaction.h>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QClipboard>
|
||||
#include <QContextMenuEvent>
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QFileDialog>
|
||||
#include <QInputDialog>
|
||||
#include <QMenu>
|
||||
#include <QTextStream>
|
||||
|
||||
using namespace Utils;
|
||||
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// StackHandler
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*!
|
||||
\class Debugger::Internal::StackHandler
|
||||
\brief The StackHandler class provides a model to represent the stack in a
|
||||
@@ -56,13 +67,14 @@ namespace Internal {
|
||||
StackHandler::StackHandler(DebuggerEngine *engine)
|
||||
: m_engine(engine)
|
||||
{
|
||||
setObjectName(QLatin1String("StackModel"));
|
||||
m_resetLocationScheduled = false;
|
||||
m_contentsValid = false;
|
||||
m_currentIndex = -1;
|
||||
m_canExpand = false;
|
||||
setObjectName("StackModel");
|
||||
|
||||
connect(action(OperateByInstruction), &QAction::triggered,
|
||||
this, &StackHandler::resetModel);
|
||||
connect(action(ExpandStack), &QAction::triggered,
|
||||
this, &StackHandler::reloadFullStack);
|
||||
connect(action(MaximalStackDepth), &QAction::triggered,
|
||||
this, &StackHandler::reloadFullStack);
|
||||
}
|
||||
|
||||
StackHandler::~StackHandler()
|
||||
@@ -104,12 +116,12 @@ QVariant StackHandler::data(const QModelIndex &index, int role) const
|
||||
case StackFunctionNameColumn:
|
||||
return simplifyType(frame.function);
|
||||
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:
|
||||
return frame.line > 0 ? QVariant(frame.line) : QVariant();
|
||||
case StackAddressColumn:
|
||||
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 QVariant();
|
||||
@@ -151,7 +163,24 @@ Qt::ItemFlags StackHandler::flags(const QModelIndex &index) const
|
||||
const StackFrame &frame = m_stackFrames.at(index.row());
|
||||
const bool isValid = frame.isUsable() || boolSetting(OperateByInstruction);
|
||||
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
|
||||
@@ -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 Debugger
|
||||
|
||||
@@ -29,6 +29,8 @@
|
||||
|
||||
#include <QAbstractItemModel>
|
||||
|
||||
namespace Utils { class ItemViewEvent; }
|
||||
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
@@ -77,20 +79,25 @@ signals:
|
||||
void currentIndexChanged();
|
||||
|
||||
private:
|
||||
// QAbstractTableModel
|
||||
int rowCount(const QModelIndex &parent) const;
|
||||
int columnCount(const QModelIndex &parent) const;
|
||||
QVariant data(const QModelIndex &index, int role) const;
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
|
||||
Qt::ItemFlags flags(const QModelIndex &index) const;
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
QVariant data(const QModelIndex &index, int role) const override;
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
|
||||
Qt::ItemFlags flags(const QModelIndex &index) const override;
|
||||
bool setData(const QModelIndex &idx, const QVariant &data, int role) override;
|
||||
|
||||
bool contextMenuEvent(const Utils::ItemViewEvent &event);
|
||||
void resetModel() { beginResetModel(); endResetModel(); }
|
||||
void reloadFullStack();
|
||||
void copyContentsToClipboard();
|
||||
void saveTaskFile();
|
||||
|
||||
DebuggerEngine *m_engine;
|
||||
StackFrames m_stackFrames;
|
||||
int m_currentIndex;
|
||||
bool m_canExpand;
|
||||
bool m_resetLocationScheduled;
|
||||
bool m_contentsValid;
|
||||
int m_currentIndex = -1;
|
||||
bool m_canExpand = false;
|
||||
bool m_resetLocationScheduled = false;
|
||||
bool m_contentsValid = false;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
|
||||
@@ -24,29 +24,14 @@
|
||||
****************************************************************************/
|
||||
|
||||
#include "stackwindow.h"
|
||||
#include "stackhandler.h"
|
||||
|
||||
#include "debuggeractions.h"
|
||||
#include "debuggercore.h"
|
||||
#include "debuggerengine.h"
|
||||
#include "debuggerdialogs.h"
|
||||
#include "memoryagent.h"
|
||||
|
||||
#include <coreplugin/messagebox.h>
|
||||
#include "stackhandler.h"
|
||||
|
||||
#include <utils/savedaction.h>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QTextStream>
|
||||
#include <QFile>
|
||||
#include <QDir>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QClipboard>
|
||||
#include <QContextMenuEvent>
|
||||
#include <QInputDialog>
|
||||
#include <QFileDialog>
|
||||
#include <QMenu>
|
||||
#include <QAction>
|
||||
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
@@ -57,10 +42,6 @@ StackTreeView::StackTreeView()
|
||||
|
||||
connect(action(UseAddressInStackView), &QAction::toggled,
|
||||
this, &StackTreeView::showAddressColumn);
|
||||
connect(action(ExpandStack), &QAction::triggered,
|
||||
this, &StackTreeView::reloadFullStack);
|
||||
connect(action(MaximalStackDepth), &QAction::triggered,
|
||||
this, &StackTreeView::reloadFullStack);
|
||||
showAddressColumn(false);
|
||||
}
|
||||
|
||||
@@ -72,11 +53,6 @@ void StackTreeView::showAddressColumn(bool on)
|
||||
resizeColumnToContents(StackAddressColumn);
|
||||
}
|
||||
|
||||
void StackTreeView::rowActivated(const QModelIndex &index)
|
||||
{
|
||||
currentEngine()->activateFrame(index.row());
|
||||
}
|
||||
|
||||
void StackTreeView::setModel(QAbstractItemModel *model)
|
||||
{
|
||||
BaseTreeView::setModel(model);
|
||||
@@ -85,198 +61,5 @@ void StackTreeView::setModel(QAbstractItemModel *model)
|
||||
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 Debugger
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <utils/basetreeview.h>
|
||||
|
||||
#include <QCoreApplication>
|
||||
|
||||
namespace Debugger {
|
||||
@@ -34,13 +35,12 @@ namespace Internal {
|
||||
class StackTreeView : public Utils::BaseTreeView
|
||||
{
|
||||
Q_DECLARE_TR_FUNCTIONS(Debugger::Internal::StackTreeView)
|
||||
|
||||
public:
|
||||
StackTreeView();
|
||||
|
||||
private:
|
||||
void rowActivated(const QModelIndex &index);
|
||||
void setModel(QAbstractItemModel *model);
|
||||
void contextMenuEvent(QContextMenuEvent *ev);
|
||||
void setModel(QAbstractItemModel *model) override;
|
||||
|
||||
void showAddressColumn(bool on);
|
||||
void reloadFullStack();
|
||||
|
||||
@@ -25,17 +25,22 @@
|
||||
|
||||
#include "threadshandler.h"
|
||||
|
||||
#include "debuggeractions.h"
|
||||
#include "debuggercore.h"
|
||||
#include "debuggerengine.h"
|
||||
#include "debuggericons.h"
|
||||
#include "debuggerprotocol.h"
|
||||
#include "watchutils.h"
|
||||
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/basetreeview.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/savedaction.h>
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDebug>
|
||||
#include <QIcon>
|
||||
#include <QMenu>
|
||||
|
||||
using namespace Utils;
|
||||
|
||||
@@ -225,7 +230,8 @@ public:
|
||||
represent the running threads in a QTreeView or ComboBox.
|
||||
*/
|
||||
|
||||
ThreadsHandler::ThreadsHandler()
|
||||
ThreadsHandler::ThreadsHandler(DebuggerEngine *engine)
|
||||
: m_engine(engine)
|
||||
{
|
||||
m_resetLocationScheduled = false;
|
||||
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)
|
||||
{
|
||||
const auto matcher = [threadId](ThreadItem *item) { return item->threadData.id == threadId; };
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
class DebuggerEngine;
|
||||
class GdbMi;
|
||||
class ThreadItem;
|
||||
|
||||
@@ -46,7 +47,7 @@ class ThreadsHandler : public Utils::LeveledTreeModel<Utils::TypedTreeItem<Threa
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ThreadsHandler();
|
||||
explicit ThreadsHandler(DebuggerEngine *engine);
|
||||
|
||||
int currentThreadIndex() const;
|
||||
ThreadId currentThread() const;
|
||||
@@ -82,7 +83,9 @@ private:
|
||||
void updateThreadBox();
|
||||
|
||||
void sort(int column, Qt::SortOrder order);
|
||||
bool setData(const QModelIndex &idx, const QVariant &data, int role);
|
||||
|
||||
DebuggerEngine *m_engine;
|
||||
ThreadId m_currentId;
|
||||
bool m_resetLocationScheduled;
|
||||
QHash<QString, QString> m_pidForGroupId;
|
||||
|
||||
@@ -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
|
||||
@@ -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
@@ -68,7 +68,6 @@ public:
|
||||
void watchExpression(const QString &exp, const QString &name = QString());
|
||||
void updateWatchExpression(WatchItem *item, const QString &newExp);
|
||||
void watchVariable(const QString &exp);
|
||||
void clearWatches();
|
||||
|
||||
const WatchItem *watchItem(const QModelIndex &) const;
|
||||
void fetchMore(const QString &iname) const;
|
||||
@@ -96,11 +95,7 @@ public:
|
||||
void addDumpers(const GdbMi &dumpers);
|
||||
void addTypeFormats(const QString &type, const DisplayFormats &formats);
|
||||
|
||||
void setUnprintableBase(int base);
|
||||
static int unprintableBase();
|
||||
|
||||
QString watcherName(const QString &exp);
|
||||
QString editorContents(const QModelIndexList &list = QModelIndexList());
|
||||
|
||||
void scheduleResetLocation();
|
||||
void resetLocation();
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -41,10 +41,8 @@ public:
|
||||
WatchType type() const { return m_type; }
|
||||
|
||||
void setModel(QAbstractItemModel *model);
|
||||
void rowActivated(const QModelIndex &index);
|
||||
void reset();
|
||||
|
||||
void fillFormatMenu(QMenu *, const QModelIndex &mi);
|
||||
static void reexpand(QTreeView *view, const QModelIndex &idx);
|
||||
|
||||
void watchExpression(const QString &exp);
|
||||
@@ -60,25 +58,10 @@ private:
|
||||
void collapseNode(const QModelIndex &idx);
|
||||
void adjustSlider();
|
||||
|
||||
void showUnprintable(int base);
|
||||
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 ¤t, 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;
|
||||
bool m_grabbing;
|
||||
int m_sliderPosition;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user