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);
|
setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||||
setUniformRowHeights(true);
|
setUniformRowHeights(true);
|
||||||
setItemDelegate(new BaseTreeViewDelegate(this));
|
setItemDelegate(new BaseTreeViewDelegate(this));
|
||||||
|
setAlternatingRowColors(false);
|
||||||
|
|
||||||
QHeaderView *h = header();
|
QHeaderView *h = header();
|
||||||
h->setDefaultAlignment(Qt::AlignLeft);
|
h->setDefaultAlignment(Qt::AlignLeft);
|
||||||
@@ -303,6 +304,13 @@ void BaseTreeView::setModel(QAbstractItemModel *m)
|
|||||||
connect(model(), c[i].qsignal, c[i].receiver, c[i].qslot);
|
connect(model(), c[i].qsignal, c[i].receiver, c[i].qslot);
|
||||||
}
|
}
|
||||||
d->restoreState();
|
d->restoreState();
|
||||||
|
|
||||||
|
QVariant delegateBlob = m->data(QModelIndex(), ItemDelegateRole);
|
||||||
|
if (delegateBlob.isValid()) {
|
||||||
|
auto delegate = delegateBlob.value<QAbstractItemDelegate *>();
|
||||||
|
delegate->setParent(this);
|
||||||
|
setItemDelegate(delegate);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -314,6 +322,44 @@ void BaseTreeView::mousePressEvent(QMouseEvent *ev)
|
|||||||
d->toggleColumnWidth(columnAt(ev->x()));
|
d->toggleColumnWidth(columnAt(ev->x()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BaseTreeView::contextMenuEvent(QContextMenuEvent *ev)
|
||||||
|
{
|
||||||
|
QPoint pos = ev->pos();
|
||||||
|
QModelIndex index = indexAt(pos);
|
||||||
|
if (!model()->setData(index, describeEvent(ev, pos, index), ItemViewEventRole))
|
||||||
|
TreeView::contextMenuEvent(ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BaseTreeView::keyPressEvent(QKeyEvent *ev)
|
||||||
|
{
|
||||||
|
if (!model()->setData(QModelIndex(), describeEvent(ev), ItemViewEventRole))
|
||||||
|
TreeView::keyPressEvent(ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BaseTreeView::dragEnterEvent(QDragEnterEvent *ev)
|
||||||
|
{
|
||||||
|
if (!model()->setData(QModelIndex(), describeEvent(ev), ItemViewEventRole))
|
||||||
|
TreeView::dragEnterEvent(ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BaseTreeView::dropEvent(QDropEvent *ev)
|
||||||
|
{
|
||||||
|
if (!model()->setData(QModelIndex(), describeEvent(ev), ItemViewEventRole))
|
||||||
|
TreeView::dropEvent(ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BaseTreeView::dragMoveEvent(QDragMoveEvent *ev)
|
||||||
|
{
|
||||||
|
if (!model()->setData(QModelIndex(), describeEvent(ev), ItemViewEventRole))
|
||||||
|
TreeView::dragMoveEvent(ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BaseTreeView::mouseDoubleClickEvent(QMouseEvent *ev)
|
||||||
|
{
|
||||||
|
if (!model()->setData(QModelIndex(), describeEvent(ev), ItemViewEventRole))
|
||||||
|
TreeView::mouseDoubleClickEvent(ev);
|
||||||
|
}
|
||||||
|
|
||||||
void BaseTreeView::showEvent(QShowEvent *ev)
|
void BaseTreeView::showEvent(QShowEvent *ev)
|
||||||
{
|
{
|
||||||
emit aboutToShow();
|
emit aboutToShow();
|
||||||
@@ -346,6 +392,35 @@ void BaseTreeView::hideProgressIndicator()
|
|||||||
d->m_progressIndicator->hide();
|
d->m_progressIndicator->hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QVariant BaseTreeView::describeEvent(QEvent *ev, const QPoint &pos, const QModelIndex &idx)
|
||||||
|
{
|
||||||
|
ItemViewEvent event;
|
||||||
|
event.m_view = this;
|
||||||
|
event.m_event = ev;
|
||||||
|
event.m_pos = pos;
|
||||||
|
event.m_index = idx;
|
||||||
|
|
||||||
|
QItemSelectionModel *selection = selectionModel();
|
||||||
|
event.m_selectedRows = selection->selectedRows();
|
||||||
|
if (event.m_selectedRows.isEmpty()) {
|
||||||
|
QModelIndex current = selection->currentIndex();
|
||||||
|
if (current.isValid())
|
||||||
|
event.m_selectedRows.append(current);
|
||||||
|
}
|
||||||
|
|
||||||
|
return QVariant::fromValue(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BaseTreeView::rowActivated(const QModelIndex &index)
|
||||||
|
{
|
||||||
|
model()->setData(index, QVariant(), ItemActivatedRole);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BaseTreeView::rowClicked(const QModelIndex &index)
|
||||||
|
{
|
||||||
|
model()->setData(index, QVariant(), ItemClickedRole);
|
||||||
|
}
|
||||||
|
|
||||||
void BaseTreeView::setSettings(QSettings *settings, const QByteArray &key)
|
void BaseTreeView::setSettings(QSettings *settings, const QByteArray &key)
|
||||||
{
|
{
|
||||||
QTC_ASSERT(!d->m_settings, qDebug() << "DUPLICATED setSettings" << key);
|
QTC_ASSERT(!d->m_settings, qDebug() << "DUPLICATED setSettings" << key);
|
||||||
@@ -354,16 +429,9 @@ void BaseTreeView::setSettings(QSettings *settings, const QByteArray &key)
|
|||||||
d->readSettings();
|
d->readSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
QModelIndexList BaseTreeView::activeRows() const
|
QModelIndexList ItemViewEvent::currentOrSelectedRows() const
|
||||||
{
|
{
|
||||||
QItemSelectionModel *selection = selectionModel();
|
return m_selectedRows.isEmpty() ? QModelIndexList() << m_index : m_selectedRows;
|
||||||
QModelIndexList indices = selection->selectedRows();
|
|
||||||
if (indices.isEmpty()) {
|
|
||||||
QModelIndex current = selection->currentIndex();
|
|
||||||
if (current.isValid())
|
|
||||||
indices.append(current);
|
|
||||||
}
|
|
||||||
return indices;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Utils
|
} // namespace Utils
|
||||||
|
|||||||
@@ -42,20 +42,31 @@ class QTCREATOR_UTILS_EXPORT BaseTreeView : public TreeView
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum { ExtraIndicesForColumnWidth = 12734 };
|
enum {
|
||||||
|
ExtraIndicesForColumnWidth = 12734,
|
||||||
|
|
||||||
|
ItemViewEventRole = Qt::UserRole + 12735,
|
||||||
|
|
||||||
|
ItemActivatedRole,
|
||||||
|
ItemClickedRole,
|
||||||
|
|
||||||
|
ItemDelegateRole,
|
||||||
|
};
|
||||||
|
|
||||||
BaseTreeView(QWidget *parent = 0);
|
BaseTreeView(QWidget *parent = 0);
|
||||||
~BaseTreeView();
|
~BaseTreeView();
|
||||||
|
|
||||||
void setSettings(QSettings *settings, const QByteArray &key);
|
void setSettings(QSettings *settings, const QByteArray &key);
|
||||||
QModelIndexList activeRows() const;
|
|
||||||
|
|
||||||
virtual void rowActivated(const QModelIndex &) {}
|
|
||||||
virtual void rowClicked(const QModelIndex &) {}
|
|
||||||
|
|
||||||
void setModel(QAbstractItemModel *model) override;
|
void setModel(QAbstractItemModel *model) override;
|
||||||
void mousePressEvent(QMouseEvent *ev) override;
|
void mousePressEvent(QMouseEvent *ev) override;
|
||||||
|
void contextMenuEvent(QContextMenuEvent *ev) override;
|
||||||
void showEvent(QShowEvent *ev) override;
|
void showEvent(QShowEvent *ev) override;
|
||||||
|
void keyPressEvent(QKeyEvent *ev) override;
|
||||||
|
void dragEnterEvent(QDragEnterEvent *ev) override;
|
||||||
|
void dropEvent(QDropEvent *ev) override;
|
||||||
|
void dragMoveEvent(QDragMoveEvent *ev) override;
|
||||||
|
void mouseDoubleClickEvent(QMouseEvent *ev) override;
|
||||||
|
|
||||||
void showProgressIndicator();
|
void showProgressIndicator();
|
||||||
void hideProgressIndicator();
|
void hideProgressIndicator();
|
||||||
@@ -63,11 +74,70 @@ public:
|
|||||||
signals:
|
signals:
|
||||||
void aboutToShow();
|
void aboutToShow();
|
||||||
|
|
||||||
public slots:
|
|
||||||
void setAlternatingRowColorsHelper(bool on) { setAlternatingRowColors(on); }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
QVariant describeEvent(QEvent *ev,
|
||||||
|
const QPoint &pos = QPoint(),
|
||||||
|
const QModelIndex &idx = QModelIndex());
|
||||||
|
void rowActivated(const QModelIndex &index);
|
||||||
|
void rowClicked(const QModelIndex &index);
|
||||||
|
|
||||||
Internal::BaseTreeViewPrivate *d;
|
Internal::BaseTreeViewPrivate *d;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <typename Event> struct EventCode;
|
||||||
|
template <> struct EventCode<QDragEnterEvent> { enum { code = QEvent::DragEnter }; };
|
||||||
|
template <> struct EventCode<QDragLeaveEvent> { enum { code = QEvent::DragLeave }; };
|
||||||
|
template <> struct EventCode<QDragMoveEvent> { enum { code = QEvent::DragMove }; };
|
||||||
|
template <> struct EventCode<QDropEvent> { enum { code = QEvent::Drop }; };
|
||||||
|
template <> struct EventCode<QContextMenuEvent> { enum { code = QEvent::ContextMenu }; };
|
||||||
|
template <> struct EventCode<QMouseEvent> { enum { code = QEvent::MouseButtonPress }; };
|
||||||
|
template <> struct EventCode<QKeyEvent> { enum { code = QEvent::KeyPress }; };
|
||||||
|
|
||||||
|
template <class T> T *checkEventType(QEvent *ev)
|
||||||
|
{
|
||||||
|
const int cc = EventCode<T>::code;
|
||||||
|
const int tt = ev->type();
|
||||||
|
if (cc == tt)
|
||||||
|
return static_cast<T *>(ev);
|
||||||
|
if (cc == QEvent::MouseButtonPress) {
|
||||||
|
if (tt == QEvent::MouseButtonDblClick || tt == QEvent::MouseButtonRelease || tt == QEvent::MouseMove)
|
||||||
|
return static_cast<T *>(ev);
|
||||||
|
}
|
||||||
|
if (cc == QEvent::KeyPress && tt == QEvent::KeyRelease)
|
||||||
|
return static_cast<T *>(ev);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
class QTCREATOR_UTILS_EXPORT ItemViewEvent
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ItemViewEvent() {}
|
||||||
|
|
||||||
|
template <class T> T *as() const {
|
||||||
|
return checkEventType<T>(m_event);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T> T *as(QEvent::Type t) const {
|
||||||
|
return m_event->type() == t ? as<T>() : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
QEvent::Type type() const { return m_event->type(); }
|
||||||
|
BaseTreeView *view() const { return m_view; }
|
||||||
|
QPoint pos() const { return m_pos; }
|
||||||
|
QPoint globalPos() const { return m_view->mapToGlobal(m_pos); }
|
||||||
|
QModelIndex index() const { return m_index; }
|
||||||
|
QModelIndexList selectedRows() const { return m_selectedRows; }
|
||||||
|
QModelIndexList currentOrSelectedRows() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class BaseTreeView;
|
||||||
|
QEvent *m_event = nullptr;
|
||||||
|
BaseTreeView *m_view = nullptr;
|
||||||
|
QPoint m_pos;
|
||||||
|
QModelIndex m_index;
|
||||||
|
QModelIndexList m_selectedRows;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace Utils
|
} // namespace Utils
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(Utils::ItemViewEvent);
|
||||||
|
|||||||
@@ -35,14 +35,20 @@
|
|||||||
#include <coreplugin/coreconstants.h>
|
#include <coreplugin/coreconstants.h>
|
||||||
#include <coreplugin/coreplugin.h>
|
#include <coreplugin/coreplugin.h>
|
||||||
#include <coreplugin/editormanager/editormanager.h>
|
#include <coreplugin/editormanager/editormanager.h>
|
||||||
|
#include <coreplugin/icore.h>
|
||||||
#include <coreplugin/idocument.h>
|
#include <coreplugin/idocument.h>
|
||||||
|
|
||||||
#include <extensionsystem/invoker.h>
|
#include <extensionsystem/invoker.h>
|
||||||
#include <texteditor/textmark.h>
|
#include <texteditor/textmark.h>
|
||||||
#include <texteditor/texteditor.h>
|
#include <texteditor/texteditor.h>
|
||||||
#include <utils/hostosinfo.h>
|
|
||||||
#include <utils/qtcassert.h>
|
#include <utils/basetreeview.h>
|
||||||
|
#include <utils/checkablemessagebox.h>
|
||||||
#include <utils/fileutils.h>
|
#include <utils/fileutils.h>
|
||||||
|
#include <utils/hostosinfo.h>
|
||||||
|
#include <utils/pathchooser.h>
|
||||||
|
#include <utils/qtcassert.h>
|
||||||
|
#include <utils/savedaction.h>
|
||||||
#include <utils/theme/theme.h>
|
#include <utils/theme/theme.h>
|
||||||
|
|
||||||
#if USE_BREAK_MODEL_TEST
|
#if USE_BREAK_MODEL_TEST
|
||||||
@@ -52,6 +58,13 @@
|
|||||||
#include <QTimerEvent>
|
#include <QTimerEvent>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
#include <QSpinBox>
|
||||||
|
#include <QStyledItemDelegate>
|
||||||
|
#include <QComboBox>
|
||||||
|
#include <QGroupBox>
|
||||||
|
#include <QCheckBox>
|
||||||
|
#include <QFormLayout>
|
||||||
|
#include <QMenu>
|
||||||
|
|
||||||
using namespace Core;
|
using namespace Core;
|
||||||
using namespace Utils;
|
using namespace Utils;
|
||||||
@@ -66,11 +79,11 @@ public:
|
|||||||
{
|
{
|
||||||
if (role == Qt::DisplayRole) {
|
if (role == Qt::DisplayRole) {
|
||||||
switch (column) {
|
switch (column) {
|
||||||
case 0:
|
case BreakpointNumberColumn:
|
||||||
return params.id.toString();
|
return params.id.toString();
|
||||||
case 1:
|
case BreakpointFunctionColumn:
|
||||||
return params.functionName;
|
return params.functionName;
|
||||||
case 4:
|
case BreakpointAddressColumn:
|
||||||
if (params.address)
|
if (params.address)
|
||||||
return QString::fromLatin1("0x%1").arg(params.address, 0, 16);
|
return QString::fromLatin1("0x%1").arg(params.address, 0, 16);
|
||||||
}
|
}
|
||||||
@@ -107,6 +120,7 @@ public:
|
|||||||
int markerLineNumber() const;
|
int markerLineNumber() const;
|
||||||
|
|
||||||
bool needsChange() const;
|
bool needsChange() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class BreakHandler;
|
friend class BreakHandler;
|
||||||
friend class Breakpoint;
|
friend class Breakpoint;
|
||||||
@@ -201,9 +215,9 @@ static QString stateToString(BreakpointState state)
|
|||||||
return BreakHandler::tr("<invalid state>");
|
return BreakHandler::tr("<invalid state>");
|
||||||
}
|
}
|
||||||
|
|
||||||
static QString msgBreakpointAtSpecialFunc(const char *func)
|
static QString msgBreakpointAtSpecialFunc(const QString &func)
|
||||||
{
|
{
|
||||||
return BreakHandler::tr("Breakpoint at \"%1\"").arg(QString::fromLatin1(func));
|
return BreakHandler::tr("Breakpoint at \"%1\"").arg(func);
|
||||||
}
|
}
|
||||||
|
|
||||||
static QString typeToString(BreakpointType type)
|
static QString typeToString(BreakpointType type)
|
||||||
@@ -244,12 +258,644 @@ static QString typeToString(BreakpointType type)
|
|||||||
return BreakHandler::tr("Unknown Breakpoint Type");
|
return BreakHandler::tr("Unknown Breakpoint Type");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class LeftElideDelegate : public QStyledItemDelegate
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LeftElideDelegate() {}
|
||||||
|
|
||||||
|
void paint(QPainter *pain, const QStyleOptionViewItem &option, const QModelIndex &index) const override
|
||||||
|
{
|
||||||
|
QStyleOptionViewItem opt = option;
|
||||||
|
opt.textElideMode = Qt::ElideLeft;
|
||||||
|
QStyledItemDelegate::paint(pain, opt, index);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class SmallTextEdit : public QTextEdit
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit SmallTextEdit(QWidget *parent) : QTextEdit(parent) {}
|
||||||
|
QSize sizeHint() const { return QSize(QTextEdit::sizeHint().width(), 100); }
|
||||||
|
QSize minimumSizeHint() const { return sizeHint(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// BreakpointDialog: Show a dialog for editing breakpoints. Shows controls
|
||||||
|
// for the file-and-line, function and address parameters depending on the
|
||||||
|
// breakpoint type. The controls not applicable to the current type
|
||||||
|
// (say function name for file-and-line) are disabled and cleared out.
|
||||||
|
// However,the values are saved and restored once the respective mode
|
||||||
|
// is again chosen, which is done using m_savedParameters and
|
||||||
|
// setters/getters taking the parts mask enumeration parameter.
|
||||||
|
//
|
||||||
|
///////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
class BreakpointDialog : public QDialog
|
||||||
|
{
|
||||||
|
Q_DECLARE_TR_FUNCTIONS(Debugger::Internal::BreakHandler)
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit BreakpointDialog(Breakpoint b, QWidget *parent = 0);
|
||||||
|
bool showDialog(BreakpointParameters *data, BreakpointParts *parts);
|
||||||
|
|
||||||
|
void setParameters(const BreakpointParameters &data);
|
||||||
|
BreakpointParameters parameters() const;
|
||||||
|
|
||||||
|
void typeChanged(int index);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void setPartsEnabled(unsigned partsMask);
|
||||||
|
void clearOtherParts(unsigned partsMask);
|
||||||
|
void getParts(unsigned partsMask, BreakpointParameters *data) const;
|
||||||
|
void setParts(unsigned partsMask, const BreakpointParameters &data);
|
||||||
|
|
||||||
|
void setType(BreakpointType type);
|
||||||
|
BreakpointType type() const;
|
||||||
|
|
||||||
|
unsigned m_enabledParts;
|
||||||
|
BreakpointParameters m_savedParameters;
|
||||||
|
BreakpointType m_previousType;
|
||||||
|
bool m_firstTypeChange;
|
||||||
|
|
||||||
|
QLabel *m_labelType;
|
||||||
|
QComboBox *m_comboBoxType;
|
||||||
|
QLabel *m_labelFileName;
|
||||||
|
Utils::PathChooser *m_pathChooserFileName;
|
||||||
|
QLabel *m_labelLineNumber;
|
||||||
|
QLineEdit *m_lineEditLineNumber;
|
||||||
|
QLabel *m_labelEnabled;
|
||||||
|
QCheckBox *m_checkBoxEnabled;
|
||||||
|
QLabel *m_labelAddress;
|
||||||
|
QLineEdit *m_lineEditAddress;
|
||||||
|
QLabel *m_labelExpression;
|
||||||
|
QLineEdit *m_lineEditExpression;
|
||||||
|
QLabel *m_labelFunction;
|
||||||
|
QLineEdit *m_lineEditFunction;
|
||||||
|
QLabel *m_labelTracepoint;
|
||||||
|
QCheckBox *m_checkBoxTracepoint;
|
||||||
|
QLabel *m_labelOneShot;
|
||||||
|
QCheckBox *m_checkBoxOneShot;
|
||||||
|
QLabel *m_labelUseFullPath;
|
||||||
|
QLabel *m_labelModule;
|
||||||
|
QLineEdit *m_lineEditModule;
|
||||||
|
QLabel *m_labelCommands;
|
||||||
|
QTextEdit *m_textEditCommands;
|
||||||
|
QComboBox *m_comboBoxPathUsage;
|
||||||
|
QLabel *m_labelMessage;
|
||||||
|
QLineEdit *m_lineEditMessage;
|
||||||
|
QLabel *m_labelCondition;
|
||||||
|
QLineEdit *m_lineEditCondition;
|
||||||
|
QLabel *m_labelIgnoreCount;
|
||||||
|
QSpinBox *m_spinBoxIgnoreCount;
|
||||||
|
QLabel *m_labelThreadSpec;
|
||||||
|
QLineEdit *m_lineEditThreadSpec;
|
||||||
|
QDialogButtonBox *m_buttonBox;
|
||||||
|
};
|
||||||
|
|
||||||
|
BreakpointDialog::BreakpointDialog(Breakpoint b, QWidget *parent)
|
||||||
|
: QDialog(parent), m_enabledParts(~0), m_previousType(UnknownBreakpointType),
|
||||||
|
m_firstTypeChange(true)
|
||||||
|
{
|
||||||
|
setWindowTitle(tr("Edit Breakpoint Properties"));
|
||||||
|
|
||||||
|
auto groupBoxBasic = new QGroupBox(tr("Basic"), this);
|
||||||
|
|
||||||
|
// Match BreakpointType (omitting unknown type).
|
||||||
|
const QStringList types = {
|
||||||
|
tr("File name and line number"),
|
||||||
|
tr("Function name"),
|
||||||
|
tr("Break on memory address"),
|
||||||
|
tr("Break when C++ exception is thrown"),
|
||||||
|
tr("Break when C++ exception is caught"),
|
||||||
|
tr("Break when function \"main\" starts"),
|
||||||
|
tr("Break when a new process is forked"),
|
||||||
|
tr("Break when a new process is executed"),
|
||||||
|
tr("Break when a system call is executed"),
|
||||||
|
tr("Break on data access at fixed address"),
|
||||||
|
tr("Break on data access at address given by expression"),
|
||||||
|
tr("Break on QML signal emit"),
|
||||||
|
tr("Break when JavaScript exception is thrown")
|
||||||
|
};
|
||||||
|
// We don't list UnknownBreakpointType, so 1 less:
|
||||||
|
QTC_CHECK(types.size() + 1 == LastBreakpointType);
|
||||||
|
m_comboBoxType = new QComboBox(groupBoxBasic);
|
||||||
|
m_comboBoxType->setMaxVisibleItems(20);
|
||||||
|
m_comboBoxType->addItems(types);
|
||||||
|
m_labelType = new QLabel(tr("Breakpoint &type:"), groupBoxBasic);
|
||||||
|
m_labelType->setBuddy(m_comboBoxType);
|
||||||
|
|
||||||
|
m_pathChooserFileName = new PathChooser(groupBoxBasic);
|
||||||
|
m_pathChooserFileName->setHistoryCompleter(QLatin1String("Debugger.Breakpoint.File.History"));
|
||||||
|
m_pathChooserFileName->setExpectedKind(PathChooser::File);
|
||||||
|
m_labelFileName = new QLabel(tr("&File name:"), groupBoxBasic);
|
||||||
|
m_labelFileName->setBuddy(m_pathChooserFileName);
|
||||||
|
|
||||||
|
m_lineEditLineNumber = new QLineEdit(groupBoxBasic);
|
||||||
|
m_labelLineNumber = new QLabel(tr("&Line number:"), groupBoxBasic);
|
||||||
|
m_labelLineNumber->setBuddy(m_lineEditLineNumber);
|
||||||
|
|
||||||
|
m_checkBoxEnabled = new QCheckBox(groupBoxBasic);
|
||||||
|
m_labelEnabled = new QLabel(tr("&Enabled:"), groupBoxBasic);
|
||||||
|
m_labelEnabled->setBuddy(m_checkBoxEnabled);
|
||||||
|
|
||||||
|
m_lineEditAddress = new QLineEdit(groupBoxBasic);
|
||||||
|
m_labelAddress = new QLabel(tr("&Address:"), groupBoxBasic);
|
||||||
|
m_labelAddress->setBuddy(m_lineEditAddress);
|
||||||
|
|
||||||
|
m_lineEditExpression = new QLineEdit(groupBoxBasic);
|
||||||
|
m_labelExpression = new QLabel(tr("&Expression:"), groupBoxBasic);
|
||||||
|
m_labelExpression->setBuddy(m_lineEditExpression);
|
||||||
|
|
||||||
|
m_lineEditFunction = new QLineEdit(groupBoxBasic);
|
||||||
|
m_labelFunction = new QLabel(tr("Fun&ction:"), groupBoxBasic);
|
||||||
|
m_labelFunction->setBuddy(m_lineEditFunction);
|
||||||
|
|
||||||
|
auto groupBoxAdvanced = new QGroupBox(tr("Advanced"), this);
|
||||||
|
|
||||||
|
m_checkBoxTracepoint = new QCheckBox(groupBoxAdvanced);
|
||||||
|
m_labelTracepoint = new QLabel(tr("T&racepoint only:"), groupBoxAdvanced);
|
||||||
|
m_labelTracepoint->setBuddy(m_checkBoxTracepoint);
|
||||||
|
|
||||||
|
m_checkBoxOneShot = new QCheckBox(groupBoxAdvanced);
|
||||||
|
m_labelOneShot = new QLabel(tr("&One shot only:"), groupBoxAdvanced);
|
||||||
|
m_labelOneShot->setBuddy(m_checkBoxOneShot);
|
||||||
|
|
||||||
|
const QString pathToolTip =
|
||||||
|
tr("<p>Determines how the path is specified "
|
||||||
|
"when setting breakpoints:</p><ul>"
|
||||||
|
"<li><i>Use Engine Default</i>: Preferred setting of the "
|
||||||
|
"debugger engine.</li>"
|
||||||
|
"<li><i>Use Full Path</i>: Pass full path, avoiding ambiguities "
|
||||||
|
"should files of the same name exist in several modules. "
|
||||||
|
"This is the engine default for CDB and LLDB.</li>"
|
||||||
|
"<li><i>Use File Name</i>: Pass the file name only. This is "
|
||||||
|
"useful when using a source tree whose location does "
|
||||||
|
"not match the one used when building the modules. "
|
||||||
|
"It is the engine default for GDB as using full paths can "
|
||||||
|
"be slow with this engine.</li></ul>");
|
||||||
|
m_comboBoxPathUsage = new QComboBox(groupBoxAdvanced);
|
||||||
|
m_comboBoxPathUsage->addItem(tr("Use Engine Default"));
|
||||||
|
m_comboBoxPathUsage->addItem(tr("Use Full Path"));
|
||||||
|
m_comboBoxPathUsage->addItem(tr("Use File Name"));
|
||||||
|
m_comboBoxPathUsage->setToolTip(pathToolTip);
|
||||||
|
m_labelUseFullPath = new QLabel(tr("Pat&h:"), groupBoxAdvanced);
|
||||||
|
m_labelUseFullPath->setBuddy(m_comboBoxPathUsage);
|
||||||
|
m_labelUseFullPath->setToolTip(pathToolTip);
|
||||||
|
|
||||||
|
const QString moduleToolTip =
|
||||||
|
tr("<p>Specifying the module (base name of the library or executable) "
|
||||||
|
"for function or file type breakpoints can significantly speed up "
|
||||||
|
"debugger start-up times (CDB, LLDB).");
|
||||||
|
m_lineEditModule = new QLineEdit(groupBoxAdvanced);
|
||||||
|
m_lineEditModule->setToolTip(moduleToolTip);
|
||||||
|
m_labelModule = new QLabel(tr("&Module:"), groupBoxAdvanced);
|
||||||
|
m_labelModule->setBuddy(m_lineEditModule);
|
||||||
|
m_labelModule->setToolTip(moduleToolTip);
|
||||||
|
|
||||||
|
const QString commandsToolTip =
|
||||||
|
tr("<p>Debugger commands to be executed when the breakpoint is hit. "
|
||||||
|
"This feature is only available for GDB.");
|
||||||
|
m_textEditCommands = new SmallTextEdit(groupBoxAdvanced);
|
||||||
|
m_textEditCommands->setToolTip(commandsToolTip);
|
||||||
|
m_labelCommands = new QLabel(tr("&Commands:"), groupBoxAdvanced);
|
||||||
|
m_labelCommands->setBuddy(m_textEditCommands);
|
||||||
|
m_labelCommands->setToolTip(commandsToolTip);
|
||||||
|
|
||||||
|
m_lineEditMessage = new QLineEdit(groupBoxAdvanced);
|
||||||
|
m_labelMessage = new QLabel(tr("&Message:"), groupBoxAdvanced);
|
||||||
|
m_labelMessage->setBuddy(m_lineEditMessage);
|
||||||
|
|
||||||
|
m_lineEditCondition = new QLineEdit(groupBoxAdvanced);
|
||||||
|
m_labelCondition = new QLabel(tr("C&ondition:"), groupBoxAdvanced);
|
||||||
|
m_labelCondition->setBuddy(m_lineEditCondition);
|
||||||
|
|
||||||
|
m_spinBoxIgnoreCount = new QSpinBox(groupBoxAdvanced);
|
||||||
|
m_spinBoxIgnoreCount->setMinimum(0);
|
||||||
|
m_spinBoxIgnoreCount->setMaximum(2147483647);
|
||||||
|
m_labelIgnoreCount = new QLabel(tr("&Ignore count:"), groupBoxAdvanced);
|
||||||
|
m_labelIgnoreCount->setBuddy(m_spinBoxIgnoreCount);
|
||||||
|
|
||||||
|
m_lineEditThreadSpec = new QLineEdit(groupBoxAdvanced);
|
||||||
|
m_labelThreadSpec = new QLabel(tr("&Thread specification:"), groupBoxAdvanced);
|
||||||
|
m_labelThreadSpec->setBuddy(m_lineEditThreadSpec);
|
||||||
|
|
||||||
|
m_buttonBox = new QDialogButtonBox(this);
|
||||||
|
m_buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
|
||||||
|
|
||||||
|
if (b) {
|
||||||
|
if (DebuggerEngine *engine = b.engine()) {
|
||||||
|
if (!engine->hasCapability(BreakConditionCapability))
|
||||||
|
m_enabledParts &= ~ConditionPart;
|
||||||
|
if (!engine->hasCapability(BreakModuleCapability))
|
||||||
|
m_enabledParts &= ~ModulePart;
|
||||||
|
if (!engine->hasCapability(TracePointCapability))
|
||||||
|
m_enabledParts &= ~TracePointPart;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto basicLayout = new QFormLayout(groupBoxBasic);
|
||||||
|
basicLayout->addRow(m_labelType, m_comboBoxType);
|
||||||
|
basicLayout->addRow(m_labelFileName, m_pathChooserFileName);
|
||||||
|
basicLayout->addRow(m_labelLineNumber, m_lineEditLineNumber);
|
||||||
|
basicLayout->addRow(m_labelEnabled, m_checkBoxEnabled);
|
||||||
|
basicLayout->addRow(m_labelAddress, m_lineEditAddress);
|
||||||
|
basicLayout->addRow(m_labelExpression, m_lineEditExpression);
|
||||||
|
basicLayout->addRow(m_labelFunction, m_lineEditFunction);
|
||||||
|
basicLayout->addRow(m_labelOneShot, m_checkBoxOneShot);
|
||||||
|
|
||||||
|
auto advancedLeftLayout = new QFormLayout();
|
||||||
|
advancedLeftLayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
|
||||||
|
advancedLeftLayout->addRow(m_labelCondition, m_lineEditCondition);
|
||||||
|
advancedLeftLayout->addRow(m_labelIgnoreCount, m_spinBoxIgnoreCount);
|
||||||
|
advancedLeftLayout->addRow(m_labelThreadSpec, m_lineEditThreadSpec);
|
||||||
|
advancedLeftLayout->addRow(m_labelUseFullPath, m_comboBoxPathUsage);
|
||||||
|
advancedLeftLayout->addRow(m_labelModule, m_lineEditModule);
|
||||||
|
|
||||||
|
auto advancedRightLayout = new QFormLayout();
|
||||||
|
advancedRightLayout->setFieldGrowthPolicy(QFormLayout::ExpandingFieldsGrow);
|
||||||
|
advancedRightLayout->addRow(m_labelCommands, m_textEditCommands);
|
||||||
|
advancedRightLayout->addRow(m_labelTracepoint, m_checkBoxTracepoint);
|
||||||
|
advancedRightLayout->addRow(m_labelMessage, m_lineEditMessage);
|
||||||
|
|
||||||
|
auto horizontalLayout = new QHBoxLayout(groupBoxAdvanced);
|
||||||
|
horizontalLayout->addLayout(advancedLeftLayout);
|
||||||
|
horizontalLayout->addSpacing(15);
|
||||||
|
horizontalLayout->addLayout(advancedRightLayout);
|
||||||
|
|
||||||
|
auto verticalLayout = new QVBoxLayout(this);
|
||||||
|
verticalLayout->addWidget(groupBoxBasic);
|
||||||
|
verticalLayout->addSpacing(10);
|
||||||
|
verticalLayout->addWidget(groupBoxAdvanced);
|
||||||
|
verticalLayout->addSpacing(10);
|
||||||
|
verticalLayout->addWidget(m_buttonBox);
|
||||||
|
verticalLayout->setStretchFactor(groupBoxAdvanced, 10);
|
||||||
|
|
||||||
|
connect(m_comboBoxType, static_cast<void(QComboBox::*)(int)>(&QComboBox::activated),
|
||||||
|
this, &BreakpointDialog::typeChanged);
|
||||||
|
connect(m_buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||||
|
connect(m_buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BreakpointDialog::setType(BreakpointType type)
|
||||||
|
{
|
||||||
|
const int comboIndex = type - 1; // Skip UnknownType.
|
||||||
|
if (comboIndex != m_comboBoxType->currentIndex() || m_firstTypeChange) {
|
||||||
|
m_comboBoxType->setCurrentIndex(comboIndex);
|
||||||
|
typeChanged(comboIndex);
|
||||||
|
m_firstTypeChange = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BreakpointType BreakpointDialog::type() const
|
||||||
|
{
|
||||||
|
const int type = m_comboBoxType->currentIndex() + 1; // Skip unknown type.
|
||||||
|
return static_cast<BreakpointType>(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BreakpointDialog::setParameters(const BreakpointParameters &data)
|
||||||
|
{
|
||||||
|
m_savedParameters = data;
|
||||||
|
setType(data.type);
|
||||||
|
setParts(AllParts, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
BreakpointParameters BreakpointDialog::parameters() const
|
||||||
|
{
|
||||||
|
BreakpointParameters data(type());
|
||||||
|
getParts(AllParts, &data);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BreakpointDialog::setPartsEnabled(unsigned partsMask)
|
||||||
|
{
|
||||||
|
partsMask &= m_enabledParts;
|
||||||
|
m_labelFileName->setEnabled(partsMask & FileAndLinePart);
|
||||||
|
m_pathChooserFileName->setEnabled(partsMask & FileAndLinePart);
|
||||||
|
m_labelLineNumber->setEnabled(partsMask & FileAndLinePart);
|
||||||
|
m_lineEditLineNumber->setEnabled(partsMask & FileAndLinePart);
|
||||||
|
m_labelUseFullPath->setEnabled(partsMask & FileAndLinePart);
|
||||||
|
m_comboBoxPathUsage->setEnabled(partsMask & FileAndLinePart);
|
||||||
|
|
||||||
|
m_labelFunction->setEnabled(partsMask & FunctionPart);
|
||||||
|
m_lineEditFunction->setEnabled(partsMask & FunctionPart);
|
||||||
|
|
||||||
|
m_labelOneShot->setEnabled(partsMask & OneShotPart);
|
||||||
|
m_checkBoxOneShot->setEnabled(partsMask & OneShotPart);
|
||||||
|
|
||||||
|
m_labelAddress->setEnabled(partsMask & AddressPart);
|
||||||
|
m_lineEditAddress->setEnabled(partsMask & AddressPart);
|
||||||
|
m_labelExpression->setEnabled(partsMask & ExpressionPart);
|
||||||
|
m_lineEditExpression->setEnabled(partsMask & ExpressionPart);
|
||||||
|
|
||||||
|
m_labelCondition->setEnabled(partsMask & ConditionPart);
|
||||||
|
m_lineEditCondition->setEnabled(partsMask & ConditionPart);
|
||||||
|
m_labelIgnoreCount->setEnabled(partsMask & IgnoreCountPart);
|
||||||
|
m_spinBoxIgnoreCount->setEnabled(partsMask & IgnoreCountPart);
|
||||||
|
m_labelThreadSpec->setEnabled(partsMask & ThreadSpecPart);
|
||||||
|
m_lineEditThreadSpec->setEnabled(partsMask & ThreadSpecPart);
|
||||||
|
|
||||||
|
m_labelModule->setEnabled(partsMask & ModulePart);
|
||||||
|
m_lineEditModule->setEnabled(partsMask & ModulePart);
|
||||||
|
|
||||||
|
m_labelTracepoint->setEnabled(partsMask & TracePointPart);
|
||||||
|
m_labelTracepoint->hide();
|
||||||
|
m_checkBoxTracepoint->setEnabled(partsMask & TracePointPart);
|
||||||
|
m_checkBoxTracepoint->hide();
|
||||||
|
|
||||||
|
m_labelCommands->setEnabled(partsMask & CommandPart);
|
||||||
|
m_textEditCommands->setEnabled(partsMask & CommandPart);
|
||||||
|
|
||||||
|
m_labelMessage->setEnabled(partsMask & TracePointPart);
|
||||||
|
m_labelMessage->hide();
|
||||||
|
m_lineEditMessage->setEnabled(partsMask & TracePointPart);
|
||||||
|
m_lineEditMessage->hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BreakpointDialog::clearOtherParts(unsigned partsMask)
|
||||||
|
{
|
||||||
|
const unsigned invertedPartsMask = ~partsMask;
|
||||||
|
if (invertedPartsMask & FileAndLinePart) {
|
||||||
|
m_pathChooserFileName->setPath(QString());
|
||||||
|
m_lineEditLineNumber->clear();
|
||||||
|
m_comboBoxPathUsage->setCurrentIndex(BreakpointPathUsageEngineDefault);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (invertedPartsMask & FunctionPart)
|
||||||
|
m_lineEditFunction->clear();
|
||||||
|
|
||||||
|
if (invertedPartsMask & AddressPart)
|
||||||
|
m_lineEditAddress->clear();
|
||||||
|
if (invertedPartsMask & ExpressionPart)
|
||||||
|
m_lineEditExpression->clear();
|
||||||
|
|
||||||
|
if (invertedPartsMask & ConditionPart)
|
||||||
|
m_lineEditCondition->clear();
|
||||||
|
if (invertedPartsMask & IgnoreCountPart)
|
||||||
|
m_spinBoxIgnoreCount->clear();
|
||||||
|
if (invertedPartsMask & ThreadSpecPart)
|
||||||
|
m_lineEditThreadSpec->clear();
|
||||||
|
if (invertedPartsMask & ModulePart)
|
||||||
|
m_lineEditModule->clear();
|
||||||
|
|
||||||
|
if (partsMask & OneShotPart)
|
||||||
|
m_checkBoxOneShot->setChecked(false);
|
||||||
|
if (invertedPartsMask & CommandPart)
|
||||||
|
m_textEditCommands->clear();
|
||||||
|
if (invertedPartsMask & TracePointPart) {
|
||||||
|
m_checkBoxTracepoint->setChecked(false);
|
||||||
|
m_lineEditMessage->clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BreakpointDialog::getParts(unsigned partsMask, BreakpointParameters *data) const
|
||||||
|
{
|
||||||
|
data->enabled = m_checkBoxEnabled->isChecked();
|
||||||
|
|
||||||
|
if (partsMask & FileAndLinePart) {
|
||||||
|
data->lineNumber = m_lineEditLineNumber->text().toInt();
|
||||||
|
data->pathUsage = static_cast<BreakpointPathUsage>(m_comboBoxPathUsage->currentIndex());
|
||||||
|
data->fileName = m_pathChooserFileName->path();
|
||||||
|
}
|
||||||
|
if (partsMask & FunctionPart)
|
||||||
|
data->functionName = m_lineEditFunction->text();
|
||||||
|
|
||||||
|
if (partsMask & AddressPart)
|
||||||
|
data->address = m_lineEditAddress->text().toULongLong(0, 0);
|
||||||
|
if (partsMask & ExpressionPart)
|
||||||
|
data->expression = m_lineEditExpression->text();
|
||||||
|
|
||||||
|
if (partsMask & ConditionPart)
|
||||||
|
data->condition = m_lineEditCondition->text();
|
||||||
|
if (partsMask & IgnoreCountPart)
|
||||||
|
data->ignoreCount = m_spinBoxIgnoreCount->text().toInt();
|
||||||
|
if (partsMask & ThreadSpecPart)
|
||||||
|
data->threadSpec =
|
||||||
|
BreakHandler::threadSpecFromDisplay(m_lineEditThreadSpec->text());
|
||||||
|
if (partsMask & ModulePart)
|
||||||
|
data->module = m_lineEditModule->text();
|
||||||
|
|
||||||
|
if (partsMask & OneShotPart)
|
||||||
|
data->oneShot = m_checkBoxOneShot->isChecked();
|
||||||
|
if (partsMask & CommandPart)
|
||||||
|
data->command = m_textEditCommands->toPlainText().trimmed();
|
||||||
|
if (partsMask & TracePointPart) {
|
||||||
|
data->tracepoint = m_checkBoxTracepoint->isChecked();
|
||||||
|
data->message = m_lineEditMessage->text();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BreakpointDialog::setParts(unsigned mask, const BreakpointParameters &data)
|
||||||
|
{
|
||||||
|
m_checkBoxEnabled->setChecked(data.enabled);
|
||||||
|
m_comboBoxPathUsage->setCurrentIndex(data.pathUsage);
|
||||||
|
m_lineEditMessage->setText(data.message);
|
||||||
|
|
||||||
|
if (mask & FileAndLinePart) {
|
||||||
|
m_pathChooserFileName->setPath(data.fileName);
|
||||||
|
m_lineEditLineNumber->setText(QString::number(data.lineNumber));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mask & FunctionPart)
|
||||||
|
m_lineEditFunction->setText(data.functionName);
|
||||||
|
|
||||||
|
if (mask & AddressPart) {
|
||||||
|
if (data.address) {
|
||||||
|
m_lineEditAddress->setText(QString("0x%1").arg(data.address, 0, 16));
|
||||||
|
} else {
|
||||||
|
m_lineEditAddress->clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mask & ExpressionPart) {
|
||||||
|
if (!data.expression.isEmpty())
|
||||||
|
m_lineEditExpression->setText(data.expression);
|
||||||
|
else
|
||||||
|
m_lineEditExpression->clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mask & ConditionPart)
|
||||||
|
m_lineEditCondition->setText(data.condition);
|
||||||
|
if (mask & IgnoreCountPart)
|
||||||
|
m_spinBoxIgnoreCount->setValue(data.ignoreCount);
|
||||||
|
if (mask & ThreadSpecPart)
|
||||||
|
m_lineEditThreadSpec->
|
||||||
|
setText(BreakHandler::displayFromThreadSpec(data.threadSpec));
|
||||||
|
if (mask & ModulePart)
|
||||||
|
m_lineEditModule->setText(data.module);
|
||||||
|
|
||||||
|
if (mask & OneShotPart)
|
||||||
|
m_checkBoxOneShot->setChecked(data.oneShot);
|
||||||
|
if (mask & TracePointPart)
|
||||||
|
m_checkBoxTracepoint->setChecked(data.tracepoint);
|
||||||
|
if (mask & CommandPart)
|
||||||
|
m_textEditCommands->setPlainText(data.command);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BreakpointDialog::typeChanged(int)
|
||||||
|
{
|
||||||
|
BreakpointType previousType = m_previousType;
|
||||||
|
const BreakpointType newType = type();
|
||||||
|
m_previousType = newType;
|
||||||
|
// Save current state.
|
||||||
|
switch (previousType) {
|
||||||
|
case UnknownBreakpointType:
|
||||||
|
case LastBreakpointType:
|
||||||
|
break;
|
||||||
|
case BreakpointByFileAndLine:
|
||||||
|
getParts(FileAndLinePart|ModulePart|AllConditionParts|TracePointPart|CommandPart, &m_savedParameters);
|
||||||
|
break;
|
||||||
|
case BreakpointByFunction:
|
||||||
|
getParts(FunctionPart|ModulePart|AllConditionParts|TracePointPart|CommandPart, &m_savedParameters);
|
||||||
|
break;
|
||||||
|
case BreakpointAtThrow:
|
||||||
|
case BreakpointAtCatch:
|
||||||
|
case BreakpointAtMain:
|
||||||
|
case BreakpointAtFork:
|
||||||
|
case BreakpointAtExec:
|
||||||
|
//case BreakpointAtVFork:
|
||||||
|
case BreakpointAtSysCall:
|
||||||
|
case BreakpointAtJavaScriptThrow:
|
||||||
|
break;
|
||||||
|
case BreakpointByAddress:
|
||||||
|
case WatchpointAtAddress:
|
||||||
|
getParts(AddressPart|AllConditionParts|TracePointPart|CommandPart, &m_savedParameters);
|
||||||
|
break;
|
||||||
|
case WatchpointAtExpression:
|
||||||
|
getParts(ExpressionPart|AllConditionParts|TracePointPart|CommandPart, &m_savedParameters);
|
||||||
|
break;
|
||||||
|
case BreakpointOnQmlSignalEmit:
|
||||||
|
getParts(FunctionPart, &m_savedParameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable and set up new state from saved values.
|
||||||
|
switch (newType) {
|
||||||
|
case UnknownBreakpointType:
|
||||||
|
case LastBreakpointType:
|
||||||
|
break;
|
||||||
|
case BreakpointByFileAndLine:
|
||||||
|
setParts(FileAndLinePart|AllConditionParts|ModulePart|TracePointPart|CommandPart, m_savedParameters);
|
||||||
|
setPartsEnabled(FileAndLinePart|AllConditionParts|ModulePart|TracePointPart|CommandPart);
|
||||||
|
clearOtherParts(FileAndLinePart|AllConditionParts|ModulePart|TracePointPart|CommandPart);
|
||||||
|
break;
|
||||||
|
case BreakpointByFunction:
|
||||||
|
setParts(FunctionPart|AllConditionParts|ModulePart|TracePointPart|CommandPart, m_savedParameters);
|
||||||
|
setPartsEnabled(FunctionPart|AllConditionParts|ModulePart|TracePointPart|CommandPart);
|
||||||
|
clearOtherParts(FunctionPart|AllConditionParts|ModulePart|TracePointPart|CommandPart);
|
||||||
|
break;
|
||||||
|
case BreakpointAtThrow:
|
||||||
|
case BreakpointAtCatch:
|
||||||
|
case BreakpointAtFork:
|
||||||
|
case BreakpointAtExec:
|
||||||
|
//case BreakpointAtVFork:
|
||||||
|
case BreakpointAtSysCall:
|
||||||
|
clearOtherParts(AllConditionParts|ModulePart|TracePointPart|CommandPart);
|
||||||
|
setPartsEnabled(AllConditionParts|TracePointPart|CommandPart);
|
||||||
|
break;
|
||||||
|
case BreakpointAtJavaScriptThrow:
|
||||||
|
clearOtherParts(AllParts);
|
||||||
|
setPartsEnabled(0);
|
||||||
|
break;
|
||||||
|
case BreakpointAtMain:
|
||||||
|
m_lineEditFunction->setText("main"); // Just for display
|
||||||
|
clearOtherParts(0);
|
||||||
|
setPartsEnabled(0);
|
||||||
|
break;
|
||||||
|
case BreakpointByAddress:
|
||||||
|
case WatchpointAtAddress:
|
||||||
|
setParts(AddressPart|AllConditionParts|TracePointPart|CommandPart, m_savedParameters);
|
||||||
|
setPartsEnabled(AddressPart|AllConditionParts|TracePointPart|CommandPart);
|
||||||
|
clearOtherParts(AddressPart|AllConditionParts|TracePointPart|CommandPart);
|
||||||
|
break;
|
||||||
|
case WatchpointAtExpression:
|
||||||
|
setParts(ExpressionPart|AllConditionParts|TracePointPart|CommandPart, m_savedParameters);
|
||||||
|
setPartsEnabled(ExpressionPart|AllConditionParts|TracePointPart|CommandPart);
|
||||||
|
clearOtherParts(ExpressionPart|AllConditionParts|TracePointPart|CommandPart);
|
||||||
|
break;
|
||||||
|
case BreakpointOnQmlSignalEmit:
|
||||||
|
setParts(FunctionPart, m_savedParameters);
|
||||||
|
setPartsEnabled(FunctionPart);
|
||||||
|
clearOtherParts(FunctionPart);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BreakpointDialog::showDialog(BreakpointParameters *data,
|
||||||
|
BreakpointParts *parts)
|
||||||
|
{
|
||||||
|
setParameters(*data);
|
||||||
|
if (exec() != QDialog::Accepted)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Check if changed.
|
||||||
|
const BreakpointParameters newParameters = parameters();
|
||||||
|
*parts = data->differencesTo(newParameters);
|
||||||
|
if (!*parts)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
*data = newParameters;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dialog allowing changing properties of multiple breakpoints at a time.
|
||||||
|
class MultiBreakPointsDialog : public QDialog
|
||||||
|
{
|
||||||
|
Q_DECLARE_TR_FUNCTIONS(Debugger::Internal::BreakHandler)
|
||||||
|
|
||||||
|
public:
|
||||||
|
MultiBreakPointsDialog(QWidget *parent = 0);
|
||||||
|
|
||||||
|
QString condition() const { return m_lineEditCondition->text(); }
|
||||||
|
int ignoreCount() const { return m_spinBoxIgnoreCount->value(); }
|
||||||
|
int threadSpec() const
|
||||||
|
{ return BreakHandler::threadSpecFromDisplay(m_lineEditThreadSpec->text()); }
|
||||||
|
|
||||||
|
void setCondition(const QString &c) { m_lineEditCondition->setText(c); }
|
||||||
|
void setIgnoreCount(int i) { m_spinBoxIgnoreCount->setValue(i); }
|
||||||
|
void setThreadSpec(int t)
|
||||||
|
{ return m_lineEditThreadSpec->setText(BreakHandler::displayFromThreadSpec(t)); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
QLineEdit *m_lineEditCondition;
|
||||||
|
QSpinBox *m_spinBoxIgnoreCount;
|
||||||
|
QLineEdit *m_lineEditThreadSpec;
|
||||||
|
QDialogButtonBox *m_buttonBox;
|
||||||
|
};
|
||||||
|
|
||||||
|
MultiBreakPointsDialog::MultiBreakPointsDialog(QWidget *parent) :
|
||||||
|
QDialog(parent)
|
||||||
|
{
|
||||||
|
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||||
|
setWindowTitle(tr("Edit Breakpoint Properties"));
|
||||||
|
|
||||||
|
m_lineEditCondition = new QLineEdit(this);
|
||||||
|
m_spinBoxIgnoreCount = new QSpinBox(this);
|
||||||
|
m_spinBoxIgnoreCount->setMinimum(0);
|
||||||
|
m_spinBoxIgnoreCount->setMaximum(2147483647);
|
||||||
|
m_lineEditThreadSpec = new QLineEdit(this);
|
||||||
|
|
||||||
|
m_buttonBox = new QDialogButtonBox(this);
|
||||||
|
m_buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
|
||||||
|
|
||||||
|
auto formLayout = new QFormLayout;
|
||||||
|
if (currentEngine()->hasCapability(BreakConditionCapability))
|
||||||
|
formLayout->addRow(tr("&Condition:"), m_lineEditCondition);
|
||||||
|
formLayout->addRow(tr("&Ignore count:"), m_spinBoxIgnoreCount);
|
||||||
|
formLayout->addRow(tr("&Thread specification:"), m_lineEditThreadSpec);
|
||||||
|
|
||||||
|
auto verticalLayout = new QVBoxLayout(this);
|
||||||
|
verticalLayout->addLayout(formLayout);
|
||||||
|
verticalLayout->addWidget(m_buttonBox);
|
||||||
|
|
||||||
|
connect(m_buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||||
|
connect(m_buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||||
|
}
|
||||||
|
|
||||||
BreakHandler::BreakHandler()
|
BreakHandler::BreakHandler()
|
||||||
: m_syncTimerId(-1)
|
: m_syncTimerId(-1)
|
||||||
{
|
{
|
||||||
qRegisterMetaType<BreakpointModelId>();
|
qRegisterMetaType<BreakpointModelId>();
|
||||||
TextEditor::TextMark::setCategoryColor(Constants::TEXT_MARK_CATEGORY_BREAKPOINT,
|
TextEditor::TextMark::setCategoryColor(Constants::TEXT_MARK_CATEGORY_BREAKPOINT,
|
||||||
Utils::Theme::Debugger_Breakpoint_TextMarkColor);
|
Theme::Debugger_Breakpoint_TextMarkColor);
|
||||||
|
|
||||||
#if USE_BREAK_MODEL_TEST
|
#if USE_BREAK_MODEL_TEST
|
||||||
new ModelTest(this, 0);
|
new ModelTest(this, 0);
|
||||||
@@ -339,9 +985,15 @@ Breakpoint BreakHandler::findBreakpointByFileAndLine(const QString &fileName,
|
|||||||
|
|
||||||
Breakpoint BreakHandler::breakpointById(BreakpointModelId id) const
|
Breakpoint BreakHandler::breakpointById(BreakpointModelId id) const
|
||||||
{
|
{
|
||||||
return Breakpoint(findFirstLevelItem([id](BreakpointItem *b) {
|
return Breakpoint(findFirstLevelItem([id](BreakpointItem *b) { return b->m_id == id; }));
|
||||||
return b->m_id == id;
|
}
|
||||||
}));
|
|
||||||
|
QVariant BreakHandler::data(const QModelIndex &idx, int role) const
|
||||||
|
{
|
||||||
|
if (role == BaseTreeView::ItemDelegateRole)
|
||||||
|
return QVariant::fromValue(new LeftElideDelegate);
|
||||||
|
|
||||||
|
return BreakModel::data(idx, role);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BreakHandler::deletionHelper(BreakpointModelId id)
|
void BreakHandler::deletionHelper(BreakpointModelId id)
|
||||||
@@ -604,7 +1256,7 @@ QVariant BreakpointItem::data(int column, int role) const
|
|||||||
if (role == Qt::DisplayRole) {
|
if (role == Qt::DisplayRole) {
|
||||||
const quint64 address = orig ? m_params.address : m_response.address;
|
const quint64 address = orig ? m_params.address : m_response.address;
|
||||||
if (address)
|
if (address)
|
||||||
return QString::fromLatin1("0x%1").arg(address, 0, 16);
|
return QString("0x%1").arg(address, 0, 16);
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -1243,6 +1895,218 @@ void Breakpoint::changeBreakpointData(const BreakpointParameters ¶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
|
// Storage
|
||||||
|
|||||||
@@ -33,6 +33,8 @@
|
|||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <QPointer>
|
#include <QPointer>
|
||||||
|
|
||||||
|
namespace Utils { class ItemViewEvent; }
|
||||||
|
|
||||||
namespace Debugger {
|
namespace Debugger {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
@@ -157,7 +159,9 @@ inline uint qHash(const Debugger::Internal::Breakpoint &b) { return b.hash(); }
|
|||||||
|
|
||||||
typedef QList<Breakpoint> Breakpoints;
|
typedef QList<Breakpoint> Breakpoints;
|
||||||
|
|
||||||
class BreakHandler : public Utils::LeveledTreeModel<Utils::TreeItem, BreakpointItem, LocationItem>
|
using BreakModel = Utils::LeveledTreeModel<Utils::TypedTreeItem<BreakpointItem>, BreakpointItem, LocationItem>;
|
||||||
|
|
||||||
|
class BreakHandler : public BreakModel
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
@@ -200,8 +204,15 @@ public:
|
|||||||
void setWatchpointAtExpression(const QString &exp);
|
void setWatchpointAtExpression(const QString &exp);
|
||||||
|
|
||||||
Breakpoint breakpointById(BreakpointModelId id) const;
|
Breakpoint breakpointById(BreakpointModelId id) const;
|
||||||
|
void editBreakpoint(Breakpoint bp, QWidget *parent);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
QVariant data(const QModelIndex &idx, int role) const override;
|
||||||
|
bool setData(const QModelIndex &idx, const QVariant &value, int role) override;
|
||||||
|
void timerEvent(QTimerEvent *event) override;
|
||||||
|
|
||||||
|
bool contextMenuEvent(const Utils::ItemViewEvent &ev);
|
||||||
|
|
||||||
friend class BreakpointItem;
|
friend class BreakpointItem;
|
||||||
friend class Breakpoint;
|
friend class Breakpoint;
|
||||||
|
|
||||||
@@ -209,12 +220,17 @@ private:
|
|||||||
void saveBreakpoints();
|
void saveBreakpoints();
|
||||||
|
|
||||||
void appendBreakpointInternal(const BreakpointParameters &data);
|
void appendBreakpointInternal(const BreakpointParameters &data);
|
||||||
|
void deleteBreakpoints(const Breakpoints &bps);
|
||||||
|
void deleteAllBreakpoints();
|
||||||
|
void setBreakpointsEnabled(const Breakpoints &bps, bool enabled);
|
||||||
|
void addBreakpoint();
|
||||||
|
void editBreakpoints(const Breakpoints &bps, QWidget *parent);
|
||||||
|
|
||||||
Q_SLOT void changeLineNumberFromMarkerHelper(Debugger::Internal::BreakpointModelId id);
|
Q_SLOT void changeLineNumberFromMarkerHelper(Debugger::Internal::BreakpointModelId id);
|
||||||
Q_SLOT void deletionHelper(Debugger::Internal::BreakpointModelId id);
|
Q_SLOT void deletionHelper(Debugger::Internal::BreakpointModelId id);
|
||||||
|
|
||||||
void scheduleSynchronization();
|
void scheduleSynchronization();
|
||||||
void timerEvent(QTimerEvent *event);
|
|
||||||
int m_syncTimerId;
|
int m_syncTimerId;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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 += \
|
HEADERS += \
|
||||||
breakhandler.h \
|
breakhandler.h \
|
||||||
breakpoint.h \
|
breakpoint.h \
|
||||||
breakwindow.h \
|
|
||||||
commonoptionspage.h \
|
commonoptionspage.h \
|
||||||
debugger_global.h \
|
debugger_global.h \
|
||||||
debuggeractions.h \
|
debuggeractions.h \
|
||||||
@@ -40,22 +39,18 @@ HEADERS += \
|
|||||||
logwindow.h \
|
logwindow.h \
|
||||||
memoryagent.h \
|
memoryagent.h \
|
||||||
moduleshandler.h \
|
moduleshandler.h \
|
||||||
moduleswindow.h \
|
|
||||||
outputcollector.h \
|
outputcollector.h \
|
||||||
procinterrupt.h \
|
procinterrupt.h \
|
||||||
registerhandler.h \
|
registerhandler.h \
|
||||||
registerwindow.h \
|
|
||||||
snapshothandler.h \
|
snapshothandler.h \
|
||||||
snapshotwindow.h \
|
snapshotwindow.h \
|
||||||
sourceagent.h \
|
sourceagent.h \
|
||||||
sourcefileshandler.h \
|
sourcefileshandler.h \
|
||||||
sourcefileswindow.h \
|
|
||||||
sourceutils.h \
|
sourceutils.h \
|
||||||
stackframe.h \
|
stackframe.h \
|
||||||
stackhandler.h \
|
stackhandler.h \
|
||||||
stackwindow.h \
|
stackwindow.h \
|
||||||
terminal.h \
|
terminal.h \
|
||||||
threadswindow.h \
|
|
||||||
watchhandler.h \
|
watchhandler.h \
|
||||||
watchutils.h \
|
watchutils.h \
|
||||||
watchwindow.h \
|
watchwindow.h \
|
||||||
@@ -74,7 +69,6 @@ HEADERS += \
|
|||||||
SOURCES += \
|
SOURCES += \
|
||||||
breakhandler.cpp \
|
breakhandler.cpp \
|
||||||
breakpoint.cpp \
|
breakpoint.cpp \
|
||||||
breakwindow.cpp \
|
|
||||||
commonoptionspage.cpp \
|
commonoptionspage.cpp \
|
||||||
debuggeractions.cpp \
|
debuggeractions.cpp \
|
||||||
debuggerdialogs.cpp \
|
debuggerdialogs.cpp \
|
||||||
@@ -95,21 +89,17 @@ SOURCES += \
|
|||||||
logwindow.cpp \
|
logwindow.cpp \
|
||||||
memoryagent.cpp \
|
memoryagent.cpp \
|
||||||
moduleshandler.cpp \
|
moduleshandler.cpp \
|
||||||
moduleswindow.cpp \
|
|
||||||
outputcollector.cpp \
|
outputcollector.cpp \
|
||||||
procinterrupt.cpp \
|
procinterrupt.cpp \
|
||||||
registerhandler.cpp \
|
registerhandler.cpp \
|
||||||
registerwindow.cpp \
|
|
||||||
snapshothandler.cpp \
|
snapshothandler.cpp \
|
||||||
snapshotwindow.cpp \
|
snapshotwindow.cpp \
|
||||||
sourceagent.cpp \
|
sourceagent.cpp \
|
||||||
sourcefileshandler.cpp \
|
sourcefileshandler.cpp \
|
||||||
sourcefileswindow.cpp \
|
|
||||||
sourceutils.cpp \
|
sourceutils.cpp \
|
||||||
stackhandler.cpp \
|
stackhandler.cpp \
|
||||||
stackwindow.cpp \
|
stackwindow.cpp \
|
||||||
threadshandler.cpp \
|
threadshandler.cpp \
|
||||||
threadswindow.cpp \
|
|
||||||
terminal.cpp \
|
terminal.cpp \
|
||||||
watchdata.cpp \
|
watchdata.cpp \
|
||||||
watchhandler.cpp \
|
watchhandler.cpp \
|
||||||
|
|||||||
@@ -43,7 +43,6 @@ Project {
|
|||||||
files: [
|
files: [
|
||||||
"breakhandler.cpp", "breakhandler.h",
|
"breakhandler.cpp", "breakhandler.h",
|
||||||
"breakpoint.cpp", "breakpoint.h",
|
"breakpoint.cpp", "breakpoint.h",
|
||||||
"breakwindow.cpp", "breakwindow.h",
|
|
||||||
"commonoptionspage.cpp", "commonoptionspage.h",
|
"commonoptionspage.cpp", "commonoptionspage.h",
|
||||||
"debugger.qrc",
|
"debugger.qrc",
|
||||||
"debugger_global.h",
|
"debugger_global.h",
|
||||||
@@ -76,16 +75,13 @@ Project {
|
|||||||
"memoryagent.cpp", "memoryagent.h",
|
"memoryagent.cpp", "memoryagent.h",
|
||||||
"memoryview.cpp", "memoryview.h",
|
"memoryview.cpp", "memoryview.h",
|
||||||
"moduleshandler.cpp", "moduleshandler.h",
|
"moduleshandler.cpp", "moduleshandler.h",
|
||||||
"moduleswindow.cpp", "moduleswindow.h",
|
|
||||||
"outputcollector.cpp", "outputcollector.h",
|
"outputcollector.cpp", "outputcollector.h",
|
||||||
"procinterrupt.cpp", "procinterrupt.h",
|
"procinterrupt.cpp", "procinterrupt.h",
|
||||||
"registerhandler.cpp", "registerhandler.h",
|
"registerhandler.cpp", "registerhandler.h",
|
||||||
"registerwindow.cpp", "registerwindow.h",
|
|
||||||
"snapshothandler.cpp", "snapshothandler.h",
|
"snapshothandler.cpp", "snapshothandler.h",
|
||||||
"snapshotwindow.cpp", "snapshotwindow.h",
|
"snapshotwindow.cpp", "snapshotwindow.h",
|
||||||
"sourceagent.cpp", "sourceagent.h",
|
"sourceagent.cpp", "sourceagent.h",
|
||||||
"sourcefileshandler.cpp", "sourcefileshandler.h",
|
"sourcefileshandler.cpp", "sourcefileshandler.h",
|
||||||
"sourcefileswindow.cpp", "sourcefileswindow.h",
|
|
||||||
"sourceutils.cpp", "sourceutils.h",
|
"sourceutils.cpp", "sourceutils.h",
|
||||||
"stackframe.cpp", "stackframe.h",
|
"stackframe.cpp", "stackframe.h",
|
||||||
"stackhandler.cpp", "stackhandler.h",
|
"stackhandler.cpp", "stackhandler.h",
|
||||||
@@ -93,7 +89,6 @@ Project {
|
|||||||
"terminal.cpp", "terminal.h",
|
"terminal.cpp", "terminal.h",
|
||||||
"threaddata.h",
|
"threaddata.h",
|
||||||
"threadshandler.cpp", "threadshandler.h",
|
"threadshandler.cpp", "threadshandler.h",
|
||||||
"threadswindow.cpp", "threadswindow.h",
|
|
||||||
"watchdata.cpp", "watchdata.h",
|
"watchdata.cpp", "watchdata.h",
|
||||||
"watchdelegatewidgets.cpp", "watchdelegatewidgets.h",
|
"watchdelegatewidgets.cpp", "watchdelegatewidgets.h",
|
||||||
"watchhandler.cpp", "watchhandler.h",
|
"watchhandler.cpp", "watchhandler.h",
|
||||||
|
|||||||
@@ -32,10 +32,14 @@
|
|||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QSharedPointer>
|
#include <QSharedPointer>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
class QIcon;
|
class QIcon;
|
||||||
class QMessageBox;
|
class QMessageBox;
|
||||||
class QWidget;
|
class QWidget;
|
||||||
|
class QMenu;
|
||||||
|
class QAction;
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
namespace CPlusPlus { class Snapshot; }
|
namespace CPlusPlus { class Snapshot; }
|
||||||
@@ -107,5 +111,12 @@ QMessageBox *showMessageBox(int icon, const QString &title,
|
|||||||
|
|
||||||
bool isReverseDebuggingEnabled();
|
bool isReverseDebuggingEnabled();
|
||||||
|
|
||||||
|
QAction *addAction(QMenu *menu, const QString &display, bool on,
|
||||||
|
const std::function<void()> &onTriggered = {});
|
||||||
|
QAction *addAction(QMenu *menu, const QString &d1, const QString &d2, bool on,
|
||||||
|
const std::function<void()> &onTriggered);
|
||||||
|
QAction *addCheckableAction(QMenu *menu, const QString &display, bool on, bool checked,
|
||||||
|
const std::function<void()> &onTriggered);
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
} // namespace Debugger
|
} // namespace Debugger
|
||||||
|
|||||||
@@ -174,23 +174,15 @@ class DebuggerEnginePrivate : public QObject
|
|||||||
public:
|
public:
|
||||||
DebuggerEnginePrivate(DebuggerEngine *engine, const DebuggerRunParameters &sp)
|
DebuggerEnginePrivate(DebuggerEngine *engine, const DebuggerRunParameters &sp)
|
||||||
: m_engine(engine),
|
: m_engine(engine),
|
||||||
m_masterEngine(0),
|
|
||||||
m_runControl(0),
|
|
||||||
m_runParameters(sp),
|
m_runParameters(sp),
|
||||||
m_state(DebuggerNotReady),
|
|
||||||
m_lastGoodState(DebuggerNotReady),
|
|
||||||
m_targetState(DebuggerNotReady),
|
|
||||||
m_remoteSetupState(RemoteSetupNone),
|
|
||||||
m_inferiorPid(0),
|
|
||||||
m_modulesHandler(engine),
|
m_modulesHandler(engine),
|
||||||
m_registerHandler(engine),
|
m_registerHandler(engine),
|
||||||
m_sourceFilesHandler(),
|
m_sourceFilesHandler(engine),
|
||||||
m_stackHandler(engine),
|
m_stackHandler(engine),
|
||||||
m_threadsHandler(),
|
m_threadsHandler(engine),
|
||||||
m_watchHandler(engine),
|
m_watchHandler(engine),
|
||||||
m_disassemblerAgent(engine),
|
m_disassemblerAgent(engine),
|
||||||
m_memoryAgent(engine),
|
m_memoryAgent(engine)
|
||||||
m_isStateDebugging(false)
|
|
||||||
{
|
{
|
||||||
connect(&m_locationTimer, &QTimer::timeout,
|
connect(&m_locationTimer, &QTimer::timeout,
|
||||||
this, &DebuggerEnginePrivate::resetLocation);
|
this, &DebuggerEnginePrivate::resetLocation);
|
||||||
@@ -298,26 +290,26 @@ public:
|
|||||||
{ return m_masterEngine ? m_masterEngine->runControl() : m_runControl; }
|
{ return m_masterEngine ? m_masterEngine->runControl() : m_runControl; }
|
||||||
void setRemoteSetupState(RemoteSetupState state);
|
void setRemoteSetupState(RemoteSetupState state);
|
||||||
|
|
||||||
DebuggerEngine *m_engine; // Not owned.
|
DebuggerEngine *m_engine = nullptr; // Not owned.
|
||||||
DebuggerEngine *m_masterEngine; // Not owned
|
DebuggerEngine *m_masterEngine = nullptr; // Not owned
|
||||||
DebuggerRunControl *m_runControl; // Not owned.
|
DebuggerRunControl *m_runControl = nullptr; // Not owned.
|
||||||
|
|
||||||
DebuggerRunParameters m_runParameters;
|
DebuggerRunParameters m_runParameters;
|
||||||
|
|
||||||
// The current state.
|
// The current state.
|
||||||
DebuggerState m_state;
|
DebuggerState m_state = DebuggerNotReady;
|
||||||
|
|
||||||
// The state we had before something unexpected happend.
|
// The state we had before something unexpected happend.
|
||||||
DebuggerState m_lastGoodState;
|
DebuggerState m_lastGoodState = DebuggerNotReady;
|
||||||
|
|
||||||
// The state we are aiming for.
|
// The state we are aiming for.
|
||||||
DebuggerState m_targetState;
|
DebuggerState m_targetState = DebuggerNotReady;
|
||||||
|
|
||||||
// State of RemoteSetup signal/slots.
|
// State of RemoteSetup signal/slots.
|
||||||
RemoteSetupState m_remoteSetupState;
|
RemoteSetupState m_remoteSetupState = RemoteSetupNone;
|
||||||
|
|
||||||
Terminal m_terminal;
|
Terminal m_terminal;
|
||||||
qint64 m_inferiorPid;
|
qint64 m_inferiorPid = 0;
|
||||||
|
|
||||||
ModulesHandler m_modulesHandler;
|
ModulesHandler m_modulesHandler;
|
||||||
RegisterHandler m_registerHandler;
|
RegisterHandler m_registerHandler;
|
||||||
@@ -332,7 +324,7 @@ public:
|
|||||||
QScopedPointer<LocationMark> m_locationMark;
|
QScopedPointer<LocationMark> m_locationMark;
|
||||||
QTimer m_locationTimer;
|
QTimer m_locationTimer;
|
||||||
|
|
||||||
bool m_isStateDebugging;
|
bool m_isStateDebugging = false;
|
||||||
|
|
||||||
Utils::FileInProjectFinder m_fileFinder;
|
Utils::FileInProjectFinder m_fileFinder;
|
||||||
QString m_qtNamespace;
|
QString m_qtNamespace;
|
||||||
|
|||||||
@@ -75,23 +75,10 @@ enum ModelRoles
|
|||||||
|
|
||||||
// Locals and Watchers
|
// Locals and Watchers
|
||||||
LocalsINameRole,
|
LocalsINameRole,
|
||||||
LocalsEditTypeRole, // A QVariant::type describing the item
|
|
||||||
LocalsIntegerBaseRole, // Number base 16, 10, 8, 2
|
|
||||||
LocalsNameRole,
|
LocalsNameRole,
|
||||||
LocalsExpressionRole,
|
|
||||||
LocalsRawExpressionRole,
|
|
||||||
LocalsExpandedRole, // The preferred expanded state to the view
|
LocalsExpandedRole, // The preferred expanded state to the view
|
||||||
LocalsRawTypeRole, // Raw type name
|
|
||||||
LocalsTypeRole, // Display type name
|
|
||||||
LocalsTypeFormatListRole,
|
|
||||||
LocalsTypeFormatRole, // Used to communicate alternative formats to the view
|
LocalsTypeFormatRole, // Used to communicate alternative formats to the view
|
||||||
LocalsIndividualFormatRole,
|
LocalsIndividualFormatRole,
|
||||||
LocalsObjectAddressRole, // Memory address of variable as quint64
|
|
||||||
LocalsSizeRole, // Size of variable as quint
|
|
||||||
LocalsRawValueRole, // Unformatted value as string
|
|
||||||
LocalsPointerAddressRole, // Address of (undereferenced) pointer as quint64
|
|
||||||
LocalsIsWatchpointAtObjectAddressRole,
|
|
||||||
LocalsIsWatchpointAtPointerAddressRole,
|
|
||||||
|
|
||||||
// Snapshots
|
// Snapshots
|
||||||
SnapshotCapabilityRole
|
SnapshotCapabilityRole
|
||||||
|
|||||||
@@ -43,17 +43,12 @@
|
|||||||
#include "debuggerkitinformation.h"
|
#include "debuggerkitinformation.h"
|
||||||
#include "memoryagent.h"
|
#include "memoryagent.h"
|
||||||
#include "breakhandler.h"
|
#include "breakhandler.h"
|
||||||
#include "breakwindow.h"
|
|
||||||
#include "disassemblerlines.h"
|
#include "disassemblerlines.h"
|
||||||
#include "logwindow.h"
|
#include "logwindow.h"
|
||||||
#include "moduleswindow.h"
|
|
||||||
#include "moduleshandler.h"
|
#include "moduleshandler.h"
|
||||||
#include "registerwindow.h"
|
|
||||||
#include "snapshotwindow.h"
|
#include "snapshotwindow.h"
|
||||||
#include "stackhandler.h"
|
#include "stackhandler.h"
|
||||||
#include "stackwindow.h"
|
#include "stackwindow.h"
|
||||||
#include "sourcefileswindow.h"
|
|
||||||
#include "threadswindow.h"
|
|
||||||
#include "watchhandler.h"
|
#include "watchhandler.h"
|
||||||
#include "watchwindow.h"
|
#include "watchwindow.h"
|
||||||
#include "watchutils.h"
|
#include "watchutils.h"
|
||||||
@@ -463,6 +458,30 @@ static void setProxyAction(ProxyAction *proxy, Id id)
|
|||||||
proxy->setIcon(visibleStartIcon(id, true));
|
proxy->setIcon(visibleStartIcon(id, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QAction *addAction(QMenu *menu, const QString &display, bool on,
|
||||||
|
const std::function<void()> &onTriggered)
|
||||||
|
{
|
||||||
|
QAction *act = menu->addAction(display);
|
||||||
|
act->setEnabled(on);
|
||||||
|
QObject::connect(act, &QAction::triggered, onTriggered);
|
||||||
|
return act;
|
||||||
|
};
|
||||||
|
|
||||||
|
QAction *addAction(QMenu *menu, const QString &d1, const QString &d2, bool on,
|
||||||
|
const std::function<void()> &onTriggered)
|
||||||
|
{
|
||||||
|
return on ? addAction(menu, d1, true, onTriggered) : addAction(menu, d2, false);
|
||||||
|
};
|
||||||
|
|
||||||
|
QAction *addCheckableAction(QMenu *menu, const QString &display, bool on, bool checked,
|
||||||
|
const std::function<void()> &onTriggered)
|
||||||
|
{
|
||||||
|
QAction *act = addAction(menu, display, on, onTriggered);
|
||||||
|
act->setCheckable(true);
|
||||||
|
act->setChecked(checked);
|
||||||
|
return act;
|
||||||
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// DummyEngine
|
// DummyEngine
|
||||||
@@ -547,15 +566,15 @@ public:
|
|||||||
///////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
static QWidget *addSearch(BaseTreeView *treeView, const QString &title,
|
static QWidget *addSearch(BaseTreeView *treeView, const QString &title,
|
||||||
const char *objectName)
|
const QString &objectName)
|
||||||
{
|
{
|
||||||
QAction *act = action(UseAlternatingRowColors);
|
QAction *act = action(UseAlternatingRowColors);
|
||||||
treeView->setAlternatingRowColors(act->isChecked());
|
treeView->setAlternatingRowColors(act->isChecked());
|
||||||
QObject::connect(act, &QAction::toggled,
|
QObject::connect(act, &QAction::toggled,
|
||||||
treeView, &BaseTreeView::setAlternatingRowColorsHelper);
|
treeView, &BaseTreeView::setAlternatingRowColors);
|
||||||
|
|
||||||
QWidget *widget = ItemViewFind::createSearchableWrapper(treeView);
|
QWidget *widget = ItemViewFind::createSearchableWrapper(treeView);
|
||||||
widget->setObjectName(QLatin1String(objectName));
|
widget->setObjectName(objectName);
|
||||||
widget->setWindowTitle(title);
|
widget->setWindowTitle(title);
|
||||||
return widget;
|
return widget;
|
||||||
}
|
}
|
||||||
@@ -1309,19 +1328,25 @@ bool DebuggerPluginPrivate::initialize(const QStringList &arguments,
|
|||||||
m_logWindow->setObjectName(QLatin1String(DOCKWIDGET_OUTPUT));
|
m_logWindow->setObjectName(QLatin1String(DOCKWIDGET_OUTPUT));
|
||||||
|
|
||||||
m_breakHandler = new BreakHandler;
|
m_breakHandler = new BreakHandler;
|
||||||
m_breakView = new BreakTreeView;
|
m_breakView = new BaseTreeView;;
|
||||||
|
m_breakView->setWindowIcon(Icons::BREAKPOINTS.icon());
|
||||||
|
m_breakView->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||||
|
connect(action(UseAddressInBreakpointsView), &QAction::toggled,
|
||||||
|
this, [this](bool on) { m_breakView->setColumnHidden(BreakpointAddressColumn, !on); });
|
||||||
m_breakView->setSettings(settings, "Debugger.BreakWindow");
|
m_breakView->setSettings(settings, "Debugger.BreakWindow");
|
||||||
m_breakView->setModel(m_breakHandler->model());
|
m_breakView->setModel(m_breakHandler->model());
|
||||||
m_breakWindow = addSearch(m_breakView, tr("Breakpoints"), DOCKWIDGET_BREAK);
|
m_breakWindow = addSearch(m_breakView, tr("Breakpoints"), DOCKWIDGET_BREAK);
|
||||||
|
|
||||||
m_modulesView = new ModulesTreeView;
|
m_modulesView = new BaseTreeView;
|
||||||
|
m_modulesView->setSortingEnabled(true);
|
||||||
m_modulesView->setSettings(settings, "Debugger.ModulesView");
|
m_modulesView->setSettings(settings, "Debugger.ModulesView");
|
||||||
connect(m_modulesView, &BaseTreeView::aboutToShow, this, [this] {
|
connect(m_modulesView, &BaseTreeView::aboutToShow, this, [this] {
|
||||||
m_currentEngine->reloadModules();
|
m_currentEngine->reloadModules();
|
||||||
}, Qt::QueuedConnection);
|
}, Qt::QueuedConnection);
|
||||||
m_modulesWindow = addSearch(m_modulesView, tr("Modules"), DOCKWIDGET_MODULES);
|
m_modulesWindow = addSearch(m_modulesView, tr("Modules"), DOCKWIDGET_MODULES);
|
||||||
|
|
||||||
m_registerView = new RegisterTreeView;
|
m_registerView = new BaseTreeView;
|
||||||
|
m_registerView->setRootIsDecorated(true);
|
||||||
m_registerView->setSettings(settings, "Debugger.RegisterView");
|
m_registerView->setSettings(settings, "Debugger.RegisterView");
|
||||||
connect(m_registerView, &BaseTreeView::aboutToShow, this, [this] {
|
connect(m_registerView, &BaseTreeView::aboutToShow, this, [this] {
|
||||||
m_currentEngine->reloadRegisters();
|
m_currentEngine->reloadRegisters();
|
||||||
@@ -1332,14 +1357,16 @@ bool DebuggerPluginPrivate::initialize(const QStringList &arguments,
|
|||||||
m_stackView->setSettings(settings, "Debugger.StackView");
|
m_stackView->setSettings(settings, "Debugger.StackView");
|
||||||
m_stackWindow = addSearch(m_stackView, tr("Stack"), DOCKWIDGET_STACK);
|
m_stackWindow = addSearch(m_stackView, tr("Stack"), DOCKWIDGET_STACK);
|
||||||
|
|
||||||
m_sourceFilesView = new SourceFilesTreeView;
|
m_sourceFilesView = new BaseTreeView;
|
||||||
|
m_sourceFilesView->setSortingEnabled(true);
|
||||||
m_sourceFilesView->setSettings(settings, "Debugger.SourceFilesView");
|
m_sourceFilesView->setSettings(settings, "Debugger.SourceFilesView");
|
||||||
connect(m_sourceFilesView, &BaseTreeView::aboutToShow, this, [this] {
|
connect(m_sourceFilesView, &BaseTreeView::aboutToShow, this, [this] {
|
||||||
m_currentEngine->reloadSourceFiles();
|
m_currentEngine->reloadSourceFiles();
|
||||||
}, Qt::QueuedConnection);
|
}, Qt::QueuedConnection);
|
||||||
m_sourceFilesWindow = addSearch(m_sourceFilesView, tr("Source Files"), DOCKWIDGET_SOURCE_FILES);
|
m_sourceFilesWindow = addSearch(m_sourceFilesView, tr("Source Files"), DOCKWIDGET_SOURCE_FILES);
|
||||||
|
|
||||||
m_threadsView = new ThreadsTreeView;
|
m_threadsView = new BaseTreeView;
|
||||||
|
m_threadsView->setSortingEnabled(true);
|
||||||
m_threadsView->setSettings(settings, "Debugger.ThreadsView");
|
m_threadsView->setSettings(settings, "Debugger.ThreadsView");
|
||||||
m_threadsWindow = addSearch(m_threadsView, tr("Threads"), DOCKWIDGET_THREADS);
|
m_threadsWindow = addSearch(m_threadsView, tr("Threads"), DOCKWIDGET_THREADS);
|
||||||
|
|
||||||
@@ -2243,7 +2270,7 @@ void DebuggerPluginPrivate::requestContextMenu(TextEditorWidget *widget,
|
|||||||
// Edit existing breakpoint.
|
// Edit existing breakpoint.
|
||||||
act = menu->addAction(tr("Edit Breakpoint %1...").arg(id));
|
act = menu->addAction(tr("Edit Breakpoint %1...").arg(id));
|
||||||
connect(act, &QAction::triggered, [bp] {
|
connect(act, &QAction::triggered, [bp] {
|
||||||
BreakTreeView::editBreakpoint(bp, ICore::dialogParent());
|
breakHandler()->editBreakpoint(bp, ICore::dialogParent());
|
||||||
});
|
});
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -25,30 +25,39 @@
|
|||||||
|
|
||||||
#include "moduleshandler.h"
|
#include "moduleshandler.h"
|
||||||
|
|
||||||
|
#include "debuggerconstants.h"
|
||||||
|
#include "debuggercore.h"
|
||||||
|
#include "debuggerengine.h"
|
||||||
|
|
||||||
|
#include <utils/basetreeview.h>
|
||||||
|
#include <utils/hostosinfo.h>
|
||||||
#include <utils/qtcassert.h>
|
#include <utils/qtcassert.h>
|
||||||
#include <utils/treemodel.h>
|
#include <utils/treemodel.h>
|
||||||
|
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
#include <QMenu>
|
||||||
#include <QSortFilterProxyModel>
|
#include <QSortFilterProxyModel>
|
||||||
|
|
||||||
using namespace Utils;
|
#include <functional>
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////
|
using namespace Utils;
|
||||||
//
|
|
||||||
// ModulesModel
|
|
||||||
//
|
|
||||||
//////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
namespace Debugger {
|
namespace Debugger {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
class ModuleItem : public TreeItem
|
class ModuleItem : public TreeItem
|
||||||
{
|
{
|
||||||
public:
|
Q_DECLARE_TR_FUNCTIONS(Debuggger::Internal::ModulesHandler)
|
||||||
QVariant data(int column, int role) const;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
QVariant data(int column, int role) const override;
|
||||||
|
bool setData(int column, const QVariant &data, int role) override;
|
||||||
|
|
||||||
|
bool contextMenuEvent(const ItemViewEvent &event);
|
||||||
|
|
||||||
|
public:
|
||||||
|
DebuggerEngine *engine;
|
||||||
Module module;
|
Module module;
|
||||||
bool updated;
|
bool updated;
|
||||||
};
|
};
|
||||||
@@ -78,110 +87,158 @@ QVariant ModuleItem::data(int column, int role) const
|
|||||||
case 2:
|
case 2:
|
||||||
if (role == Qt::DisplayRole)
|
if (role == Qt::DisplayRole)
|
||||||
switch (module.symbolsRead) {
|
switch (module.symbolsRead) {
|
||||||
case Module::UnknownReadState: return ModulesHandler::tr("Unknown");
|
case Module::UnknownReadState: return tr("Unknown");
|
||||||
case Module::ReadFailed: return ModulesHandler::tr("No");
|
case Module::ReadFailed: return tr("No");
|
||||||
case Module::ReadOk: return ModulesHandler::tr("Yes");
|
case Module::ReadOk: return tr("Yes");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
if (role == Qt::DisplayRole)
|
if (role == Qt::DisplayRole)
|
||||||
switch (module.elfData.symbolsType) {
|
switch (module.elfData.symbolsType) {
|
||||||
case UnknownSymbols:
|
case UnknownSymbols: return tr("Unknown");
|
||||||
return ModulesHandler::tr("Unknown");
|
case NoSymbols: return tr("None");
|
||||||
case NoSymbols:
|
case PlainSymbols: return tr("Plain");
|
||||||
return ModulesHandler::tr("None");
|
case FastSymbols: return tr("Fast");
|
||||||
case PlainSymbols:
|
case LinkedSymbols: return tr("debuglnk");
|
||||||
return ModulesHandler::tr("Plain");
|
case BuildIdSymbols: return tr("buildid");
|
||||||
case FastSymbols:
|
|
||||||
return ModulesHandler::tr("Fast");
|
|
||||||
case LinkedSymbols:
|
|
||||||
return ModulesHandler::tr("debuglnk");
|
|
||||||
case BuildIdSymbols:
|
|
||||||
return ModulesHandler::tr("buildid");
|
|
||||||
}
|
}
|
||||||
else if (role == Qt::ToolTipRole)
|
else if (role == Qt::ToolTipRole)
|
||||||
switch (module.elfData.symbolsType) {
|
switch (module.elfData.symbolsType) {
|
||||||
case UnknownSymbols:
|
case UnknownSymbols:
|
||||||
return ModulesHandler::tr(
|
return tr("It is unknown whether this module contains debug "
|
||||||
"It is unknown whether this module contains debug "
|
"information.\nUse \"Examine Symbols\" from the "
|
||||||
"information.\nUse \"Examine Symbols\" from the "
|
"context menu to initiate a check.");
|
||||||
"context menu to initiate a check.");
|
|
||||||
case NoSymbols:
|
case NoSymbols:
|
||||||
return ModulesHandler::tr(
|
return tr("This module neither contains nor references debug "
|
||||||
"This module neither contains nor references debug "
|
"information.\nStepping into the module or setting "
|
||||||
"information.\nStepping into the module or setting "
|
"breakpoints by file and line will not work.");
|
||||||
"breakpoints by file and line will not work.");
|
|
||||||
case PlainSymbols:
|
case PlainSymbols:
|
||||||
return ModulesHandler::tr(
|
return tr("This module contains debug information.\nStepping "
|
||||||
"This module contains debug information.\nStepping "
|
"into the module or setting breakpoints by file and "
|
||||||
"into the module or setting breakpoints by file and "
|
"line is expected to work.");
|
||||||
"line is expected to work.");
|
|
||||||
case FastSymbols:
|
case FastSymbols:
|
||||||
return ModulesHandler::tr(
|
return tr("This module contains debug information.\nStepping "
|
||||||
"This module contains debug information.\nStepping "
|
"into the module or setting breakpoints by file and "
|
||||||
"into the module or setting breakpoints by file and "
|
"line is expected to work.");
|
||||||
"line is expected to work.");
|
|
||||||
case LinkedSymbols:
|
case LinkedSymbols:
|
||||||
case BuildIdSymbols:
|
case BuildIdSymbols:
|
||||||
return ModulesHandler::tr(
|
return tr("This module does not contain debug information "
|
||||||
"This module does not contain debug information "
|
"itself, but contains a reference to external "
|
||||||
"itself, but contains a reference to external "
|
"debug information.");
|
||||||
"debug information.");
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
if (role == Qt::DisplayRole)
|
if (role == Qt::DisplayRole)
|
||||||
if (module.startAddress)
|
if (module.startAddress)
|
||||||
return QString(QLatin1String("0x")
|
return QString("0x" + QString::number(module.startAddress, 16));
|
||||||
+ QString::number(module.startAddress, 16));
|
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 5:
|
||||||
if (role == Qt::DisplayRole) {
|
if (role == Qt::DisplayRole) {
|
||||||
if (module.endAddress)
|
if (module.endAddress)
|
||||||
return QString(QLatin1String("0x")
|
return QString("0x" + QString::number(module.endAddress, 16));
|
||||||
+ QString::number(module.endAddress, 16));
|
|
||||||
//: End address of loaded module
|
//: End address of loaded module
|
||||||
return ModulesHandler::tr("<unknown>", "address");
|
return tr("<unknown>", "address");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ModuleItem::setData(int, const QVariant &data, int role)
|
||||||
|
{
|
||||||
|
if (role == BaseTreeView::ItemActivatedRole) {
|
||||||
|
engine->gotoLocation(module.modulePath);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (role == BaseTreeView::ItemViewEventRole) {
|
||||||
|
ItemViewEvent ev = data.value<ItemViewEvent>();
|
||||||
|
if (ev.type() == QEvent::ContextMenu)
|
||||||
|
return contextMenuEvent(ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ModuleItem::contextMenuEvent(const ItemViewEvent &event)
|
||||||
|
{
|
||||||
|
auto menu = new QMenu;
|
||||||
|
|
||||||
|
const bool enabled = engine->debuggerActionsEnabled();
|
||||||
|
const bool canReload = engine->hasCapability(ReloadModuleCapability);
|
||||||
|
const bool canLoadSymbols = engine->hasCapability(ReloadModuleSymbolsCapability);
|
||||||
|
const bool canShowSymbols = engine->hasCapability(ShowModuleSymbolsCapability);
|
||||||
|
const bool moduleNameValid = !module.moduleName.isEmpty();
|
||||||
|
|
||||||
|
addAction(menu, tr("Update Module List"),
|
||||||
|
enabled && canReload,
|
||||||
|
[this] { engine->reloadModules(); });
|
||||||
|
|
||||||
|
addAction(menu, tr("Show Source Files for Module \"%1\"").arg(module.moduleName),
|
||||||
|
enabled && canReload,
|
||||||
|
[this] { engine->loadSymbols(module.modulePath); });
|
||||||
|
|
||||||
|
// FIXME: Dependencies only available on Windows, when "depends" is installed.
|
||||||
|
addAction(menu, tr("Show Dependencies of \"%1\"").arg(module.moduleName),
|
||||||
|
tr("Show Dependencies"),
|
||||||
|
moduleNameValid && !module.modulePath.isEmpty() && HostOsInfo::isWindowsHost(),
|
||||||
|
[this] { QProcess::startDetached("depends", QStringList(module.modulePath)); });
|
||||||
|
|
||||||
|
addAction(menu, tr("Load Symbols for All Modules"),
|
||||||
|
enabled && canLoadSymbols,
|
||||||
|
[this] { engine->loadAllSymbols(); });
|
||||||
|
|
||||||
|
addAction(menu, tr("Examine All Modules"),
|
||||||
|
enabled && canLoadSymbols,
|
||||||
|
[this] { engine->examineModules(); });
|
||||||
|
|
||||||
|
addAction(menu, tr("Load Symbols for Module \"%1\"").arg(module.moduleName),
|
||||||
|
tr("Load Symbols for Module"),
|
||||||
|
canLoadSymbols,
|
||||||
|
[this] { engine->loadSymbols(module.modulePath); });
|
||||||
|
|
||||||
|
addAction(menu, tr("Edit File \"%1\"").arg(module.moduleName),
|
||||||
|
tr("Edit File"),
|
||||||
|
moduleNameValid,
|
||||||
|
[this] { engine->gotoLocation(module.modulePath); });
|
||||||
|
|
||||||
|
addAction(menu, tr("Show Symbols in File \"%1\"").arg(module.moduleName),
|
||||||
|
tr("Show Symbols"),
|
||||||
|
canShowSymbols && moduleNameValid,
|
||||||
|
[this] { engine->requestModuleSymbols(module.modulePath); });
|
||||||
|
|
||||||
|
addAction(menu, tr("Show Sections in File \"%1\"").arg(module.moduleName),
|
||||||
|
tr("Show Sections"),
|
||||||
|
canShowSymbols && moduleNameValid,
|
||||||
|
[this] { engine->requestModuleSections(module.modulePath); });
|
||||||
|
|
||||||
|
menu->popup(event.globalPos());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// ModulesHandler
|
// ModulesHandler
|
||||||
//
|
//
|
||||||
//////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
static ModuleItem *moduleFromPath(TreeItem *root, const QString &modulePath)
|
|
||||||
{
|
|
||||||
// Recent modules are more likely to be unloaded first.
|
|
||||||
for (int i = root->childCount(); --i >= 0; ) {
|
|
||||||
auto item = static_cast<ModuleItem *>(root->childAt(i));
|
|
||||||
if (item->module.modulePath == modulePath)
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ModulesHandler::ModulesHandler(DebuggerEngine *engine)
|
ModulesHandler::ModulesHandler(DebuggerEngine *engine)
|
||||||
{
|
{
|
||||||
m_engine = engine;
|
m_engine = engine;
|
||||||
|
|
||||||
QString pad = QLatin1String(" ");
|
QString pad = " ";
|
||||||
m_model = new TreeModel(this);
|
m_model = new ModulesModel;
|
||||||
m_model->setObjectName(QLatin1String("ModulesModel"));
|
m_model->setObjectName("ModulesModel");
|
||||||
m_model->setHeader(QStringList()
|
m_model->setHeader(QStringList({
|
||||||
<< ModulesHandler::tr("Module Name") + pad
|
tr("Module Name") + pad,
|
||||||
<< ModulesHandler::tr("Module Path") + pad
|
tr("Module Path") + pad,
|
||||||
<< ModulesHandler::tr("Symbols Read") + pad
|
tr("Symbols Read") + pad,
|
||||||
<< ModulesHandler::tr("Symbols Type") + pad
|
tr("Symbols Type") + pad,
|
||||||
<< ModulesHandler::tr("Start Address") + pad
|
tr("Start Address") + pad,
|
||||||
<< ModulesHandler::tr("End Address") + pad);
|
tr("End Address") + pad}));
|
||||||
|
|
||||||
m_proxyModel = new QSortFilterProxyModel(this);
|
m_proxyModel = new QSortFilterProxyModel(this);
|
||||||
m_proxyModel->setObjectName(QLatin1String("ModulesProxyModel"));
|
m_proxyModel->setObjectName("ModulesProxyModel");
|
||||||
m_proxyModel->setSourceModel(m_model);
|
m_proxyModel->setSourceModel(m_model);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,6 +247,14 @@ QAbstractItemModel *ModulesHandler::model() const
|
|||||||
return m_proxyModel;
|
return m_proxyModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ModuleItem *ModulesHandler::moduleFromPath(const QString &modulePath) const
|
||||||
|
{
|
||||||
|
// Recent modules are more likely to be unloaded first.
|
||||||
|
return m_model->findFirstLevelItem([modulePath](ModuleItem *item) {
|
||||||
|
return item->module.modulePath == modulePath;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void ModulesHandler::removeAll()
|
void ModulesHandler::removeAll()
|
||||||
{
|
{
|
||||||
m_model->clear();
|
m_model->clear();
|
||||||
@@ -198,15 +263,13 @@ void ModulesHandler::removeAll()
|
|||||||
Modules ModulesHandler::modules() const
|
Modules ModulesHandler::modules() const
|
||||||
{
|
{
|
||||||
Modules mods;
|
Modules mods;
|
||||||
TreeItem *root = m_model->rootItem();
|
m_model->forFirstLevelItems([&mods](ModuleItem *item) { mods.append(item->module); });
|
||||||
for (int i = root->childCount(); --i >= 0; )
|
|
||||||
mods.append(static_cast<ModuleItem *>(root->childAt(i))->module);
|
|
||||||
return mods;
|
return mods;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModulesHandler::removeModule(const QString &modulePath)
|
void ModulesHandler::removeModule(const QString &modulePath)
|
||||||
{
|
{
|
||||||
if (ModuleItem *item = moduleFromPath(m_model->rootItem(), modulePath))
|
if (ModuleItem *item = moduleFromPath(modulePath))
|
||||||
m_model->destroyItem(item);
|
m_model->destroyItem(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,12 +279,13 @@ void ModulesHandler::updateModule(const Module &module)
|
|||||||
if (path.isEmpty())
|
if (path.isEmpty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ModuleItem *item = moduleFromPath(m_model->rootItem(), path);
|
ModuleItem *item = moduleFromPath(path);
|
||||||
if (item) {
|
if (item) {
|
||||||
item->module = module;
|
item->module = module;
|
||||||
} else {
|
} else {
|
||||||
item = new ModuleItem;
|
item = new ModuleItem;
|
||||||
item->module = module;
|
item->module = module;
|
||||||
|
item->engine = m_engine;
|
||||||
m_model->rootItem()->appendChild(item);
|
m_model->rootItem()->appendChild(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -238,19 +302,17 @@ void ModulesHandler::updateModule(const Module &module)
|
|||||||
|
|
||||||
void ModulesHandler::beginUpdateAll()
|
void ModulesHandler::beginUpdateAll()
|
||||||
{
|
{
|
||||||
TreeItem *root = m_model->rootItem();
|
m_model->forFirstLevelItems([](ModuleItem *item) { item->updated = false; });
|
||||||
for (int i = root->childCount(); --i >= 0; )
|
|
||||||
static_cast<ModuleItem *>(root->childAt(i))->updated = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModulesHandler::endUpdateAll()
|
void ModulesHandler::endUpdateAll()
|
||||||
{
|
{
|
||||||
TreeItem *root = m_model->rootItem();
|
QList<TreeItem *> toDestroy;
|
||||||
for (int i = root->childCount(); --i >= 0; ) {
|
m_model->forFirstLevelItems([&toDestroy](ModuleItem *item) {
|
||||||
auto item = static_cast<ModuleItem *>(root->childAt(i));
|
|
||||||
if (!item->updated)
|
if (!item->updated)
|
||||||
m_model->destroyItem(item);
|
toDestroy.append(item);
|
||||||
}
|
});
|
||||||
|
qDeleteAll(toDestroy);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ namespace Debugger {
|
|||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
class DebuggerEngine;
|
class DebuggerEngine;
|
||||||
|
class ModuleItem;
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
@@ -109,6 +110,8 @@ typedef QVector<Module> Modules;
|
|||||||
//
|
//
|
||||||
//////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
using ModulesModel = Utils::LeveledTreeModel<Utils::TypedTreeItem<ModuleItem>, ModuleItem>;
|
||||||
|
|
||||||
class ModulesHandler : public QObject
|
class ModulesHandler : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@@ -128,8 +131,10 @@ public:
|
|||||||
Modules modules() const;
|
Modules modules() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
ModuleItem *moduleFromPath(const QString &modulePath) const;
|
||||||
|
|
||||||
DebuggerEngine *m_engine;
|
DebuggerEngine *m_engine;
|
||||||
Utils::TreeModel *m_model;
|
ModulesModel *m_model;
|
||||||
QSortFilterProxyModel *m_proxyModel;
|
QSortFilterProxyModel *m_proxyModel;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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 "debuggerengine.h"
|
||||||
#include "watchdelegatewidgets.h"
|
#include "watchdelegatewidgets.h"
|
||||||
|
|
||||||
|
#include "memoryview.h"
|
||||||
|
#include "memoryagent.h"
|
||||||
|
#include "debuggeractions.h"
|
||||||
|
#include "debuggerdialogs.h"
|
||||||
|
#include "debuggercore.h"
|
||||||
|
#include "debuggerengine.h"
|
||||||
|
|
||||||
|
#include <utils/basetreeview.h>
|
||||||
#include <utils/qtcassert.h>
|
#include <utils/qtcassert.h>
|
||||||
|
#include <utils/savedaction.h>
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QItemDelegate>
|
||||||
|
#include <QMenu>
|
||||||
|
#include <QPainter>
|
||||||
|
|
||||||
using namespace Utils;
|
using namespace Utils;
|
||||||
|
|
||||||
namespace Debugger {
|
namespace Debugger {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
|
enum RegisterColumns
|
||||||
|
{
|
||||||
|
RegisterNameColumn,
|
||||||
|
RegisterValueColumn,
|
||||||
|
RegisterColumnCount
|
||||||
|
};
|
||||||
|
|
||||||
|
enum RegisterDataRole
|
||||||
|
{
|
||||||
|
RegisterChangedRole = Qt::UserRole
|
||||||
|
};
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// RegisterDelegate
|
||||||
|
//
|
||||||
|
///////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
class RegisterDelegate : public QItemDelegate
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RegisterDelegate() {}
|
||||||
|
|
||||||
|
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &,
|
||||||
|
const QModelIndex &index) const override
|
||||||
|
{
|
||||||
|
if (index.column() == RegisterValueColumn) {
|
||||||
|
auto lineEdit = new QLineEdit(parent);
|
||||||
|
lineEdit->setAlignment(Qt::AlignLeft);
|
||||||
|
lineEdit->setFrame(false);
|
||||||
|
return lineEdit;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setEditorData(QWidget *editor, const QModelIndex &index) const override
|
||||||
|
{
|
||||||
|
auto lineEdit = qobject_cast<QLineEdit *>(editor);
|
||||||
|
QTC_ASSERT(lineEdit, return);
|
||||||
|
lineEdit->setText(index.data(Qt::EditRole).toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
void setModelData(QWidget *editor, QAbstractItemModel *model,
|
||||||
|
const QModelIndex &index) const override
|
||||||
|
{
|
||||||
|
if (index.column() == RegisterValueColumn) {
|
||||||
|
auto lineEdit = qobject_cast<QLineEdit *>(editor);
|
||||||
|
QTC_ASSERT(lineEdit, return);
|
||||||
|
model->setData(index, lineEdit->text(), Qt::EditRole);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option,
|
||||||
|
const QModelIndex &) const override
|
||||||
|
{
|
||||||
|
editor->setGeometry(option.rect);
|
||||||
|
}
|
||||||
|
|
||||||
|
void paint(QPainter *painter, const QStyleOptionViewItem &option,
|
||||||
|
const QModelIndex &index) const override
|
||||||
|
{
|
||||||
|
if (index.column() == RegisterValueColumn) {
|
||||||
|
const bool paintRed = index.data(RegisterChangedRole).toBool();
|
||||||
|
QPen oldPen = painter->pen();
|
||||||
|
const QColor lightColor(140, 140, 140);
|
||||||
|
if (paintRed)
|
||||||
|
painter->setPen(QColor(200, 0, 0));
|
||||||
|
else
|
||||||
|
painter->setPen(lightColor);
|
||||||
|
// FIXME: performance? this changes only on real font changes.
|
||||||
|
QFontMetrics fm(option.font);
|
||||||
|
int charWidth = qMax(fm.width(QLatin1Char('x')), fm.width(QLatin1Char('0')));
|
||||||
|
QString str = index.data(Qt::DisplayRole).toString();
|
||||||
|
int x = option.rect.x();
|
||||||
|
bool light = !paintRed;
|
||||||
|
for (int i = 0; i < str.size(); ++i) {
|
||||||
|
const QChar c = str.at(i);
|
||||||
|
const int uc = c.unicode();
|
||||||
|
if (light && (uc != 'x' && uc != '0')) {
|
||||||
|
light = false;
|
||||||
|
painter->setPen(oldPen.color());
|
||||||
|
}
|
||||||
|
if (uc == ' ') {
|
||||||
|
light = true;
|
||||||
|
painter->setPen(lightColor);
|
||||||
|
} else {
|
||||||
|
QRect r = option.rect;
|
||||||
|
r.setX(x);
|
||||||
|
r.setWidth(charWidth);
|
||||||
|
painter->drawText(r, Qt::AlignHCenter, c);
|
||||||
|
}
|
||||||
|
x += charWidth;
|
||||||
|
}
|
||||||
|
painter->setPen(oldPen);
|
||||||
|
} else {
|
||||||
|
QItemDelegate::paint(painter, option, index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Register
|
// Register
|
||||||
@@ -288,9 +402,6 @@ void RegisterValue::shiftOneDigit(uint digit, RegisterFormat format)
|
|||||||
//
|
//
|
||||||
//////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
class RegisterItem;
|
|
||||||
class RegisterSubItem;
|
|
||||||
|
|
||||||
class RegisterEditItem : public TypedTreeItem<TreeItem, RegisterSubItem>
|
class RegisterEditItem : public TypedTreeItem<TreeItem, RegisterSubItem>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -308,7 +419,6 @@ public:
|
|||||||
RegisterFormat m_subFormat;
|
RegisterFormat m_subFormat;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class RegisterSubItem : public TypedTreeItem<RegisterEditItem, RegisterItem>
|
class RegisterSubItem : public TypedTreeItem<RegisterEditItem, RegisterItem>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -339,7 +449,7 @@ public:
|
|||||||
class RegisterItem : public TypedTreeItem<RegisterSubItem>
|
class RegisterItem : public TypedTreeItem<RegisterSubItem>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit RegisterItem(const Register ®);
|
RegisterItem(DebuggerEngine *engine, const Register ®);
|
||||||
|
|
||||||
QVariant data(int column, int role) const override;
|
QVariant data(int column, int role) const override;
|
||||||
bool setData(int column, const QVariant &value, int role) override;
|
bool setData(int column, const QVariant &value, int role) override;
|
||||||
@@ -348,13 +458,14 @@ public:
|
|||||||
quint64 addressValue() const;
|
quint64 addressValue() const;
|
||||||
void triggerChange();
|
void triggerChange();
|
||||||
|
|
||||||
|
DebuggerEngine *m_engine;
|
||||||
Register m_reg;
|
Register m_reg;
|
||||||
RegisterFormat m_format;
|
RegisterFormat m_format = HexadecimalFormat;
|
||||||
bool m_changed;
|
bool m_changed = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
RegisterItem::RegisterItem(const Register ®) :
|
RegisterItem::RegisterItem(DebuggerEngine *engine, const Register ®)
|
||||||
m_reg(reg), m_format(HexadecimalFormat), m_changed(true)
|
: m_engine(engine), m_reg(reg), m_changed(true)
|
||||||
{
|
{
|
||||||
if (m_reg.kind == UnknownRegister)
|
if (m_reg.kind == UnknownRegister)
|
||||||
m_reg.guessMissingData();
|
m_reg.guessMissingData();
|
||||||
@@ -395,28 +506,15 @@ quint64 RegisterItem::addressValue() const
|
|||||||
void RegisterItem::triggerChange()
|
void RegisterItem::triggerChange()
|
||||||
{
|
{
|
||||||
QString value = "0x" + m_reg.value.toString(m_reg.kind, m_reg.size, HexadecimalFormat);
|
QString value = "0x" + m_reg.value.toString(m_reg.kind, m_reg.size, HexadecimalFormat);
|
||||||
DebuggerEngine *engine = static_cast<RegisterHandler *>(model())->engine();
|
m_engine->setRegisterValue(m_reg.name, value);
|
||||||
engine->setRegisterValue(m_reg.name, value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant RegisterItem::data(int column, int role) const
|
QVariant RegisterItem::data(int column, int role) const
|
||||||
{
|
{
|
||||||
switch (role) {
|
switch (role) {
|
||||||
case RegisterNameRole:
|
|
||||||
return m_reg.name;
|
|
||||||
|
|
||||||
case RegisterIsBigRole:
|
|
||||||
return m_reg.value.v.u64[1] > 0;
|
|
||||||
|
|
||||||
case RegisterChangedRole:
|
case RegisterChangedRole:
|
||||||
return m_changed;
|
return m_changed;
|
||||||
|
|
||||||
case RegisterAsAddressRole:
|
|
||||||
return addressValue();
|
|
||||||
|
|
||||||
case RegisterFormatRole:
|
|
||||||
return m_format;
|
|
||||||
|
|
||||||
case Qt::DisplayRole:
|
case Qt::DisplayRole:
|
||||||
switch (column) {
|
switch (column) {
|
||||||
case RegisterNameColumn: {
|
case RegisterNameColumn: {
|
||||||
@@ -463,12 +561,6 @@ QVariant RegisterSubItem::data(int column, int role) const
|
|||||||
case RegisterChangedRole:
|
case RegisterChangedRole:
|
||||||
return m_changed;
|
return m_changed;
|
||||||
|
|
||||||
case RegisterFormatRole:
|
|
||||||
return int(parent()->m_format);
|
|
||||||
|
|
||||||
case RegisterAsAddressRole:
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
case Qt::DisplayRole:
|
case Qt::DisplayRole:
|
||||||
switch (column) {
|
switch (column) {
|
||||||
case RegisterNameColumn:
|
case RegisterNameColumn:
|
||||||
@@ -521,7 +613,7 @@ QVariant RegisterSubItem::data(int column, int role) const
|
|||||||
RegisterHandler::RegisterHandler(DebuggerEngine *engine)
|
RegisterHandler::RegisterHandler(DebuggerEngine *engine)
|
||||||
: m_engine(engine)
|
: m_engine(engine)
|
||||||
{
|
{
|
||||||
setObjectName(QLatin1String("RegisterModel"));
|
setObjectName("RegisterModel");
|
||||||
setHeader({tr("Name"), tr("Value")});
|
setHeader({tr("Name"), tr("Value")});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -529,7 +621,7 @@ void RegisterHandler::updateRegister(const Register &r)
|
|||||||
{
|
{
|
||||||
RegisterItem *reg = m_registerByName.value(r.name, 0);
|
RegisterItem *reg = m_registerByName.value(r.name, 0);
|
||||||
if (!reg) {
|
if (!reg) {
|
||||||
reg = new RegisterItem(r);
|
reg = new RegisterItem(m_engine, r);
|
||||||
m_registerByName[r.name] = reg;
|
m_registerByName[r.name] = reg;
|
||||||
rootItem()->appendChild(reg);
|
rootItem()->appendChild(reg);
|
||||||
return;
|
return;
|
||||||
@@ -550,15 +642,6 @@ void RegisterHandler::updateRegister(const Register &r)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RegisterHandler::setNumberFormat(const QString &name, RegisterFormat format)
|
|
||||||
{
|
|
||||||
RegisterItem *reg = m_registerByName.value(name, 0);
|
|
||||||
QTC_ASSERT(reg, return);
|
|
||||||
reg->m_format = format;
|
|
||||||
QModelIndex index = indexForItem(reg);
|
|
||||||
emit dataChanged(index, index);
|
|
||||||
}
|
|
||||||
|
|
||||||
RegisterMap RegisterHandler::registerMap() const
|
RegisterMap RegisterHandler::registerMap() const
|
||||||
{
|
{
|
||||||
RegisterMap result;
|
RegisterMap result;
|
||||||
@@ -571,6 +654,110 @@ RegisterMap RegisterHandler::registerMap() const
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QVariant RegisterHandler::data(const QModelIndex &idx, int role) const
|
||||||
|
{
|
||||||
|
if (role == BaseTreeView::ItemDelegateRole)
|
||||||
|
return QVariant::fromValue(static_cast<QAbstractItemDelegate *>(new RegisterDelegate));
|
||||||
|
|
||||||
|
return RegisterModel::data(idx, role);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RegisterHandler::setData(const QModelIndex &idx, const QVariant &data, int role)
|
||||||
|
{
|
||||||
|
if (role == BaseTreeView::ItemViewEventRole) {
|
||||||
|
ItemViewEvent ev = data.value<ItemViewEvent>();
|
||||||
|
if (ev.type() == QEvent::ContextMenu)
|
||||||
|
return contextMenuEvent(ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
return RegisterModel::setData(idx, data, role);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RegisterHandler::contextMenuEvent(const ItemViewEvent &ev)
|
||||||
|
{
|
||||||
|
const bool actionsEnabled = m_engine->debuggerActionsEnabled();
|
||||||
|
const DebuggerState state = m_engine->state();
|
||||||
|
|
||||||
|
RegisterItem *registerItem = firstLevelItemForIndex(ev.index());
|
||||||
|
RegisterSubItem *registerSubItem = secondLevelItemForIndex(ev.index());
|
||||||
|
|
||||||
|
const quint64 address = registerItem ? registerItem->addressValue() : 0;
|
||||||
|
const QString registerName = registerItem ? registerItem->m_reg.name : QString();
|
||||||
|
|
||||||
|
auto menu = new QMenu;
|
||||||
|
|
||||||
|
addAction(menu, tr("Reload Register Listing"),
|
||||||
|
m_engine->hasCapability(RegisterCapability)
|
||||||
|
&& (state == InferiorStopOk || state == InferiorUnrunnable),
|
||||||
|
[this] { m_engine->reloadRegisters(); });
|
||||||
|
|
||||||
|
menu->addSeparator();
|
||||||
|
|
||||||
|
addAction(menu, tr("Open Memory View at Value of Register %1 0x%2")
|
||||||
|
.arg(registerName).arg(address, 0, 16),
|
||||||
|
tr("Open Memory View at Value of Register"),
|
||||||
|
address,
|
||||||
|
[this, registerName, address] {
|
||||||
|
MemoryViewSetupData data;
|
||||||
|
data.startAddress = address;
|
||||||
|
data.flags = DebuggerEngine::MemoryTrackRegister|DebuggerEngine::MemoryView;
|
||||||
|
data.registerName = registerName;
|
||||||
|
m_engine->openMemoryView(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
addAction(menu, tr("Open Memory Editor at 0x%1").arg(address, 0, 16),
|
||||||
|
tr("Open Memory Editor"),
|
||||||
|
address && actionsEnabled && m_engine->hasCapability(ShowMemoryCapability),
|
||||||
|
[this, registerName, address] {
|
||||||
|
MemoryViewSetupData data;
|
||||||
|
data.startAddress = address;
|
||||||
|
data.registerName = registerName;
|
||||||
|
data.markup = RegisterMemoryView::registerMarkup(address, registerName);
|
||||||
|
data.title = RegisterMemoryView::title(registerName);
|
||||||
|
m_engine->openMemoryView(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
addAction(menu, tr("Open Disassembler at 0x%1").arg(address, 0, 16),
|
||||||
|
tr("Open Disassembler"),
|
||||||
|
address && m_engine->hasCapability(DisassemblerCapability),
|
||||||
|
[this, address] { m_engine->openDisassemblerView(Location(address)); });
|
||||||
|
|
||||||
|
addAction(menu, tr("Open Disassembler..."),
|
||||||
|
m_engine->hasCapability(DisassemblerCapability),
|
||||||
|
[this, address] {
|
||||||
|
AddressDialog dialog;
|
||||||
|
if (address)
|
||||||
|
dialog.setAddress(address);
|
||||||
|
if (dialog.exec() == QDialog::Accepted)
|
||||||
|
m_engine->openDisassemblerView(Location(dialog.address()));
|
||||||
|
});
|
||||||
|
|
||||||
|
menu->addSeparator();
|
||||||
|
|
||||||
|
const RegisterFormat currentFormat = registerItem
|
||||||
|
? registerItem->m_format
|
||||||
|
: registerSubItem
|
||||||
|
? registerSubItem->parent()->m_format
|
||||||
|
: HexadecimalFormat;
|
||||||
|
|
||||||
|
auto addFormatAction = [this, menu, currentFormat, registerItem](const QString &display, RegisterFormat format) {
|
||||||
|
addCheckableAction(menu, display, registerItem, currentFormat == format, [this, registerItem, format] {
|
||||||
|
registerItem->m_format = format;
|
||||||
|
registerItem->update();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
addFormatAction(tr("Hexadecimal"), HexadecimalFormat);
|
||||||
|
addFormatAction(tr("DecimalFormat"), DecimalFormat);
|
||||||
|
addFormatAction(tr("Octal"), OctalFormat);
|
||||||
|
addFormatAction(tr("Binary"), BinaryFormat);
|
||||||
|
|
||||||
|
menu->addSeparator();
|
||||||
|
menu->addAction(action(SettingsDialog));
|
||||||
|
menu->popup(ev.globalPos());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
QVariant RegisterEditItem::data(int column, int role) const
|
QVariant RegisterEditItem::data(int column, int role) const
|
||||||
{
|
{
|
||||||
switch (role) {
|
switch (role) {
|
||||||
|
|||||||
@@ -27,31 +27,15 @@
|
|||||||
|
|
||||||
#include <utils/treemodel.h>
|
#include <utils/treemodel.h>
|
||||||
|
|
||||||
#include <QAbstractTableModel>
|
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
#include <QVector>
|
|
||||||
|
namespace Utils { class ItemViewEvent; }
|
||||||
|
|
||||||
namespace Debugger {
|
namespace Debugger {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
class DebuggerEngine;
|
class DebuggerEngine;
|
||||||
|
|
||||||
enum RegisterColumns
|
|
||||||
{
|
|
||||||
RegisterNameColumn,
|
|
||||||
RegisterValueColumn,
|
|
||||||
RegisterColumnCount
|
|
||||||
};
|
|
||||||
|
|
||||||
enum RegisterDataRole
|
|
||||||
{
|
|
||||||
RegisterNameRole = Qt::UserRole,
|
|
||||||
RegisterIsBigRole,
|
|
||||||
RegisterChangedRole,
|
|
||||||
RegisterFormatRole,
|
|
||||||
RegisterAsAddressRole
|
|
||||||
};
|
|
||||||
|
|
||||||
enum RegisterKind
|
enum RegisterKind
|
||||||
{
|
{
|
||||||
UnknownRegister,
|
UnknownRegister,
|
||||||
@@ -102,7 +86,7 @@ public:
|
|||||||
class Register
|
class Register
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Register() { size = 0; kind = UnknownRegister; }
|
Register() {}
|
||||||
void guessMissingData();
|
void guessMissingData();
|
||||||
|
|
||||||
QString name;
|
QString name;
|
||||||
@@ -110,18 +94,18 @@ public:
|
|||||||
RegisterValue value;
|
RegisterValue value;
|
||||||
RegisterValue previousValue;
|
RegisterValue previousValue;
|
||||||
QString description;
|
QString description;
|
||||||
int size;
|
int size = 0;
|
||||||
RegisterKind kind;
|
RegisterKind kind = UnknownRegister;
|
||||||
};
|
};
|
||||||
|
|
||||||
class RegisterItem;
|
|
||||||
class RegisterSubItem;
|
class RegisterSubItem;
|
||||||
|
class RegisterItem;
|
||||||
using RegisterRootItem = Utils::TypedTreeItem<RegisterItem>;
|
using RegisterRootItem = Utils::TypedTreeItem<RegisterItem>;
|
||||||
|
using RegisterModel = Utils::LeveledTreeModel<RegisterRootItem, RegisterItem, RegisterSubItem>;
|
||||||
|
|
||||||
typedef QMap<quint64, QString> RegisterMap;
|
typedef QMap<quint64, QString> RegisterMap;
|
||||||
|
|
||||||
class RegisterHandler
|
class RegisterHandler : public RegisterModel
|
||||||
: public Utils::LeveledTreeModel<RegisterRootItem, RegisterItem, RegisterSubItem>
|
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
@@ -129,11 +113,8 @@ public:
|
|||||||
explicit RegisterHandler(DebuggerEngine *engine);
|
explicit RegisterHandler(DebuggerEngine *engine);
|
||||||
|
|
||||||
QAbstractItemModel *model() { return this; }
|
QAbstractItemModel *model() { return this; }
|
||||||
DebuggerEngine *engine() const { return m_engine; }
|
|
||||||
|
|
||||||
void updateRegister(const Register ®);
|
void updateRegister(const Register ®);
|
||||||
|
|
||||||
void setNumberFormat(const QString &name, RegisterFormat format);
|
|
||||||
void commitUpdates() { emit layoutChanged(); }
|
void commitUpdates() { emit layoutChanged(); }
|
||||||
RegisterMap registerMap() const;
|
RegisterMap registerMap() const;
|
||||||
|
|
||||||
@@ -141,6 +122,11 @@ signals:
|
|||||||
void registerChanged(const QString &name, quint64 value); // For memory views
|
void registerChanged(const QString &name, quint64 value); // For memory views
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
QVariant data(const QModelIndex &idx, int role) const override;
|
||||||
|
bool setData(const QModelIndex &idx, const QVariant &data, int role) override;
|
||||||
|
|
||||||
|
bool contextMenuEvent(const Utils::ItemViewEvent &ev);
|
||||||
|
|
||||||
QHash<QString, RegisterItem *> m_registerByName;
|
QHash<QString, RegisterItem *> m_registerByName;
|
||||||
DebuggerEngine * const m_engine;
|
DebuggerEngine * const m_engine;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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 "sourcefileshandler.h"
|
||||||
|
|
||||||
|
#include "debuggeractions.h"
|
||||||
|
#include "debuggercore.h"
|
||||||
|
#include "debuggerengine.h"
|
||||||
|
|
||||||
|
#include <utils/basetreeview.h>
|
||||||
|
#include <utils/savedaction.h>
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
|
#include <QMenu>
|
||||||
#include <QSortFilterProxyModel>
|
#include <QSortFilterProxyModel>
|
||||||
|
|
||||||
|
using namespace Utils;
|
||||||
|
|
||||||
namespace Debugger {
|
namespace Debugger {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
SourceFilesHandler::SourceFilesHandler()
|
SourceFilesHandler::SourceFilesHandler(DebuggerEngine *engine)
|
||||||
|
: m_engine(engine)
|
||||||
{
|
{
|
||||||
setObjectName(QLatin1String("SourceFilesModel"));
|
setObjectName("SourceFilesModel");
|
||||||
QSortFilterProxyModel *proxy = new QSortFilterProxyModel(this);
|
QSortFilterProxyModel *proxy = new QSortFilterProxyModel(this);
|
||||||
proxy->setObjectName(QLatin1String("SourceFilesProxyModel"));
|
proxy->setObjectName("SourceFilesProxyModel");
|
||||||
proxy->setSourceModel(this);
|
proxy->setSourceModel(this);
|
||||||
m_proxyModel = proxy;
|
m_proxyModel = proxy;
|
||||||
}
|
}
|
||||||
@@ -97,6 +107,46 @@ QVariant SourceFilesHandler::data(const QModelIndex &index, int role) const
|
|||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SourceFilesHandler::setData(const QModelIndex &idx, const QVariant &data, int role)
|
||||||
|
{
|
||||||
|
if (role == BaseTreeView::ItemActivatedRole) {
|
||||||
|
m_engine->gotoLocation(idx.data().toString());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (role == BaseTreeView::ItemViewEventRole) {
|
||||||
|
ItemViewEvent ev = data.value<ItemViewEvent>();
|
||||||
|
if (ev.type() == QEvent::ContextMenu) {
|
||||||
|
auto menu = new QMenu;
|
||||||
|
QModelIndex index = idx.sibling(idx.row(), 0);
|
||||||
|
QString name = index.data().toString();
|
||||||
|
|
||||||
|
auto addAction = [menu](const QString &display, bool on, const std::function<void()> &onTriggered) {
|
||||||
|
QAction *act = menu->addAction(display);
|
||||||
|
act->setEnabled(on);
|
||||||
|
QObject::connect(act, &QAction::triggered, onTriggered);
|
||||||
|
return act;
|
||||||
|
};
|
||||||
|
|
||||||
|
addAction(tr("Reload Data"), m_engine->debuggerActionsEnabled(),
|
||||||
|
[this] { m_engine->reloadSourceFiles(); });
|
||||||
|
|
||||||
|
if (name.isEmpty())
|
||||||
|
addAction(tr("Open File"), false, {});
|
||||||
|
else
|
||||||
|
addAction(tr("Open File \"%1\"").arg(name), true,
|
||||||
|
[this, name] { m_engine->gotoLocation(name); });
|
||||||
|
|
||||||
|
menu->addSeparator();
|
||||||
|
menu->addAction(action(SettingsDialog));
|
||||||
|
menu->popup(ev.globalPos());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void SourceFilesHandler::setSourceFiles(const QMap<QString, QString> &sourceFiles)
|
void SourceFilesHandler::setSourceFiles(const QMap<QString, QString> &sourceFiles)
|
||||||
{
|
{
|
||||||
beginResetModel();
|
beginResetModel();
|
||||||
|
|||||||
@@ -31,12 +31,14 @@
|
|||||||
namespace Debugger {
|
namespace Debugger {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
|
class DebuggerEngine;
|
||||||
|
|
||||||
class SourceFilesHandler : public QAbstractItemModel
|
class SourceFilesHandler : public QAbstractItemModel
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SourceFilesHandler();
|
explicit SourceFilesHandler(DebuggerEngine *engine);
|
||||||
|
|
||||||
int columnCount(const QModelIndex &parent) const
|
int columnCount(const QModelIndex &parent) const
|
||||||
{ return parent.isValid() ? 0 : 2; }
|
{ return parent.isValid() ? 0 : 2; }
|
||||||
@@ -47,6 +49,7 @@ public:
|
|||||||
{ return createIndex(row, column); }
|
{ return createIndex(row, column); }
|
||||||
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
|
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
|
||||||
QVariant data(const QModelIndex &index, int role) const;
|
QVariant data(const QModelIndex &index, int role) const;
|
||||||
|
bool setData(const QModelIndex &idx, const QVariant &data, int role) override;
|
||||||
Qt::ItemFlags flags(const QModelIndex &index) const;
|
Qt::ItemFlags flags(const QModelIndex &index) const;
|
||||||
|
|
||||||
void clearModel();
|
void clearModel();
|
||||||
@@ -57,6 +60,7 @@ public:
|
|||||||
QAbstractItemModel *model() { return m_proxyModel; }
|
QAbstractItemModel *model() { return m_proxyModel; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
DebuggerEngine *m_engine;
|
||||||
QStringList m_shortNames;
|
QStringList m_shortNames;
|
||||||
QStringList m_fullNames;
|
QStringList m_fullNames;
|
||||||
QAbstractItemModel *m_proxyModel;
|
QAbstractItemModel *m_proxyModel;
|
||||||
|
|||||||
@@ -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 "debuggeractions.h"
|
||||||
#include "debuggercore.h"
|
#include "debuggercore.h"
|
||||||
#include "debuggericons.h"
|
#include "debuggerdialogs.h"
|
||||||
#include "debuggerengine.h"
|
#include "debuggerengine.h"
|
||||||
|
#include "debuggericons.h"
|
||||||
#include "debuggerprotocol.h"
|
#include "debuggerprotocol.h"
|
||||||
|
#include "memoryagent.h"
|
||||||
#include "simplifytype.h"
|
#include "simplifytype.h"
|
||||||
|
|
||||||
|
#include <coreplugin/icore.h>
|
||||||
|
#include <coreplugin/messagebox.h>
|
||||||
|
|
||||||
|
#include <utils/basetreeview.h>
|
||||||
#include <utils/fileutils.h>
|
#include <utils/fileutils.h>
|
||||||
#include <utils/qtcassert.h>
|
#include <utils/qtcassert.h>
|
||||||
#include <utils/savedaction.h>
|
#include <utils/savedaction.h>
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QClipboard>
|
||||||
|
#include <QContextMenuEvent>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QFileDialog>
|
||||||
|
#include <QInputDialog>
|
||||||
|
#include <QMenu>
|
||||||
|
#include <QTextStream>
|
||||||
|
|
||||||
|
using namespace Utils;
|
||||||
|
|
||||||
namespace Debugger {
|
namespace Debugger {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// StackHandler
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\class Debugger::Internal::StackHandler
|
\class Debugger::Internal::StackHandler
|
||||||
\brief The StackHandler class provides a model to represent the stack in a
|
\brief The StackHandler class provides a model to represent the stack in a
|
||||||
@@ -56,13 +67,14 @@ namespace Internal {
|
|||||||
StackHandler::StackHandler(DebuggerEngine *engine)
|
StackHandler::StackHandler(DebuggerEngine *engine)
|
||||||
: m_engine(engine)
|
: m_engine(engine)
|
||||||
{
|
{
|
||||||
setObjectName(QLatin1String("StackModel"));
|
setObjectName("StackModel");
|
||||||
m_resetLocationScheduled = false;
|
|
||||||
m_contentsValid = false;
|
|
||||||
m_currentIndex = -1;
|
|
||||||
m_canExpand = false;
|
|
||||||
connect(action(OperateByInstruction), &QAction::triggered,
|
connect(action(OperateByInstruction), &QAction::triggered,
|
||||||
this, &StackHandler::resetModel);
|
this, &StackHandler::resetModel);
|
||||||
|
connect(action(ExpandStack), &QAction::triggered,
|
||||||
|
this, &StackHandler::reloadFullStack);
|
||||||
|
connect(action(MaximalStackDepth), &QAction::triggered,
|
||||||
|
this, &StackHandler::reloadFullStack);
|
||||||
}
|
}
|
||||||
|
|
||||||
StackHandler::~StackHandler()
|
StackHandler::~StackHandler()
|
||||||
@@ -104,12 +116,12 @@ QVariant StackHandler::data(const QModelIndex &index, int role) const
|
|||||||
case StackFunctionNameColumn:
|
case StackFunctionNameColumn:
|
||||||
return simplifyType(frame.function);
|
return simplifyType(frame.function);
|
||||||
case StackFileNameColumn:
|
case StackFileNameColumn:
|
||||||
return frame.file.isEmpty() ? frame.module : Utils::FileName::fromString(frame.file).fileName();
|
return frame.file.isEmpty() ? frame.module : FileName::fromString(frame.file).fileName();
|
||||||
case StackLineNumberColumn:
|
case StackLineNumberColumn:
|
||||||
return frame.line > 0 ? QVariant(frame.line) : QVariant();
|
return frame.line > 0 ? QVariant(frame.line) : QVariant();
|
||||||
case StackAddressColumn:
|
case StackAddressColumn:
|
||||||
if (frame.address)
|
if (frame.address)
|
||||||
return QString::fromLatin1("0x%1").arg(frame.address, 0, 16);
|
return QString("0x%1").arg(frame.address, 0, 16);
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
return QVariant();
|
return QVariant();
|
||||||
@@ -151,7 +163,24 @@ Qt::ItemFlags StackHandler::flags(const QModelIndex &index) const
|
|||||||
const StackFrame &frame = m_stackFrames.at(index.row());
|
const StackFrame &frame = m_stackFrames.at(index.row());
|
||||||
const bool isValid = frame.isUsable() || boolSetting(OperateByInstruction);
|
const bool isValid = frame.isUsable() || boolSetting(OperateByInstruction);
|
||||||
return isValid && m_contentsValid
|
return isValid && m_contentsValid
|
||||||
? QAbstractTableModel::flags(index) : Qt::ItemFlags();
|
? QAbstractTableModel::flags(index) : Qt::ItemFlags();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool StackHandler::setData(const QModelIndex &idx, const QVariant &data, int role)
|
||||||
|
{
|
||||||
|
Q_UNUSED(idx)
|
||||||
|
|
||||||
|
if (role == BaseTreeView::ItemActivatedRole) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (role == BaseTreeView::ItemViewEventRole) {
|
||||||
|
ItemViewEvent ev = data.value<ItemViewEvent>();
|
||||||
|
if (ev.type() == QEvent::ContextMenu)
|
||||||
|
return contextMenuEvent(ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
StackFrame StackHandler::currentFrame() const
|
StackFrame StackHandler::currentFrame() const
|
||||||
@@ -286,5 +315,171 @@ void StackHandler::resetLocation()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Input a function to be disassembled. Accept CDB syntax
|
||||||
|
// 'Module!function' for module specification
|
||||||
|
|
||||||
|
static StackFrame inputFunctionForDisassembly()
|
||||||
|
{
|
||||||
|
StackFrame frame;
|
||||||
|
QInputDialog dialog;
|
||||||
|
dialog.setInputMode(QInputDialog::TextInput);
|
||||||
|
dialog.setLabelText(StackHandler::tr("Function:"));
|
||||||
|
dialog.setWindowTitle(StackHandler::tr("Disassemble Function"));
|
||||||
|
dialog.setWindowFlags(dialog.windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||||
|
if (dialog.exec() != QDialog::Accepted)
|
||||||
|
return frame;
|
||||||
|
const QString function = dialog.textValue();
|
||||||
|
if (function.isEmpty())
|
||||||
|
return frame;
|
||||||
|
const int bangPos = function.indexOf('!');
|
||||||
|
if (bangPos != -1) {
|
||||||
|
frame.module = function.left(bangPos);
|
||||||
|
frame.function = function.mid(bangPos + 1);
|
||||||
|
} else {
|
||||||
|
frame.function = function;
|
||||||
|
}
|
||||||
|
frame.line = 42; // trick gdb into mixed mode.
|
||||||
|
return frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write stack frames as task file for displaying it in the build issues pane.
|
||||||
|
void StackHandler::saveTaskFile()
|
||||||
|
{
|
||||||
|
QFile file;
|
||||||
|
QFileDialog fileDialog(Core::ICore::dialogParent());
|
||||||
|
fileDialog.setAcceptMode(QFileDialog::AcceptSave);
|
||||||
|
fileDialog.selectFile(QDir::currentPath() + "/stack.tasks");
|
||||||
|
while (!file.isOpen()) {
|
||||||
|
if (fileDialog.exec() != QDialog::Accepted)
|
||||||
|
return;
|
||||||
|
const QString fileName = fileDialog.selectedFiles().front();
|
||||||
|
file.setFileName(fileName);
|
||||||
|
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||||
|
QString msg = tr("Cannot open \"%1\": %2")
|
||||||
|
.arg(QDir::toNativeSeparators(fileName), file.errorString());
|
||||||
|
Core::AsynchronousMessageBox::warning(tr("Cannot Open Task File"), msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QTextStream str(&file);
|
||||||
|
foreach (const StackFrame &frame, frames()) {
|
||||||
|
if (frame.isUsable())
|
||||||
|
str << frame.file << '\t' << frame.line << "\tstack\tFrame #" << frame.level << '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool StackHandler::contextMenuEvent(const ItemViewEvent &ev)
|
||||||
|
{
|
||||||
|
auto menu = new QMenu;
|
||||||
|
|
||||||
|
const int row = ev.index().row();
|
||||||
|
StackFrame frame;
|
||||||
|
if (row >= 0 && row < stackSize())
|
||||||
|
frame = frameAt(row);
|
||||||
|
const quint64 address = frame.address;
|
||||||
|
|
||||||
|
menu->addAction(action(ExpandStack));
|
||||||
|
|
||||||
|
addAction(menu, tr("Copy Contents to Clipboard"), true, [this] { copyContentsToClipboard(); });
|
||||||
|
addAction(menu, tr("Save as Task File..."), true, [this] { saveTaskFile(); });
|
||||||
|
|
||||||
|
if (m_engine->hasCapability(CreateFullBacktraceCapability))
|
||||||
|
menu->addAction(action(CreateFullBacktrace));
|
||||||
|
|
||||||
|
if (m_engine->hasCapability(AdditionalQmlStackCapability))
|
||||||
|
addAction(menu, tr("Load QML Stack"), true, [this] { m_engine->loadAdditionalQmlStack(); });
|
||||||
|
|
||||||
|
if (m_engine->hasCapability(ShowMemoryCapability))
|
||||||
|
addAction(menu, tr("Open Memory Editor at 0x%1").arg(address, 0, 16),
|
||||||
|
tr("Open Memory Editor"),
|
||||||
|
address,
|
||||||
|
[this, row, frame, address] {
|
||||||
|
MemoryViewSetupData data;
|
||||||
|
data.startAddress = address;
|
||||||
|
data.title = tr("Memory at Frame #%1 (%2) 0x%3").
|
||||||
|
arg(row).arg(frame.function).arg(address, 0, 16);
|
||||||
|
data.markup.push_back(MemoryMarkup(address, 1, QColor(Qt::blue).lighter(),
|
||||||
|
tr("Frame #%1 (%2)").arg(row).arg(frame.function)));
|
||||||
|
m_engine->openMemoryView(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (m_engine->hasCapability(DisassemblerCapability)) {
|
||||||
|
addAction(menu, tr("Open Disassembler at 0x%1").arg(address, 0, 16),
|
||||||
|
tr("Open Disassembler"),
|
||||||
|
address,
|
||||||
|
[this, frame] { m_engine->openDisassemblerView(frame); });
|
||||||
|
|
||||||
|
addAction(menu, tr("Open Disassembler at Address..."), true,
|
||||||
|
[this, address] {
|
||||||
|
AddressDialog dialog;
|
||||||
|
if (address)
|
||||||
|
dialog.setAddress(address);
|
||||||
|
if (dialog.exec() == QDialog::Accepted)
|
||||||
|
m_engine->openDisassemblerView(Location(dialog.address()));
|
||||||
|
});
|
||||||
|
|
||||||
|
addAction(menu, tr("Disassemble Function..."), true,
|
||||||
|
[this, address] {
|
||||||
|
const StackFrame frame = inputFunctionForDisassembly();
|
||||||
|
if (!frame.function.isEmpty())
|
||||||
|
m_engine->openDisassemblerView(Location(frame));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_engine->hasCapability(ShowModuleSymbolsCapability)) {
|
||||||
|
addAction(menu, tr("Try to Load Unknown Symbols"), true,
|
||||||
|
[this] { m_engine->loadSymbolsForStack(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_engine->hasCapability(MemoryAddressCapability))
|
||||||
|
menu->addAction(action(UseAddressInStackView));
|
||||||
|
|
||||||
|
menu->addSeparator();
|
||||||
|
menu->addAction(action(UseToolTipsInStackView));
|
||||||
|
menu->addSeparator();
|
||||||
|
menu->addAction(action(SettingsDialog));
|
||||||
|
menu->popup(ev.globalPos());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StackHandler::copyContentsToClipboard()
|
||||||
|
{
|
||||||
|
QString str;
|
||||||
|
int n = rowCount();
|
||||||
|
int m = columnCount();
|
||||||
|
QVector<int> largestColumnWidths(m, 0);
|
||||||
|
|
||||||
|
// First, find the widths of the largest columns,
|
||||||
|
// so that we can print them out nicely aligned.
|
||||||
|
for (int i = 0; i != n; ++i) {
|
||||||
|
for (int j = 0; j < m; ++j) {
|
||||||
|
const QModelIndex idx = index(i, j);
|
||||||
|
const int columnWidth = data(idx, Qt::DisplayRole).toString().size();
|
||||||
|
if (columnWidth > largestColumnWidths.at(j))
|
||||||
|
largestColumnWidths[j] = columnWidth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i != n; ++i) {
|
||||||
|
for (int j = 0; j != m; ++j) {
|
||||||
|
QModelIndex idx = index(i, j);
|
||||||
|
const QString columnEntry = data(idx, Qt::DisplayRole).toString();
|
||||||
|
str += columnEntry;
|
||||||
|
const int difference = largestColumnWidths.at(j) - columnEntry.size();
|
||||||
|
// Add one extra space between columns.
|
||||||
|
str += QString().fill(' ', difference > 0 ? difference + 1 : 1);
|
||||||
|
}
|
||||||
|
str += '\n';
|
||||||
|
}
|
||||||
|
QClipboard *clipboard = QApplication::clipboard();
|
||||||
|
clipboard->setText(str, QClipboard::Selection);
|
||||||
|
clipboard->setText(str, QClipboard::Clipboard);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StackHandler::reloadFullStack()
|
||||||
|
{
|
||||||
|
m_engine->reloadFullStack();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
} // namespace Debugger
|
} // namespace Debugger
|
||||||
|
|||||||
@@ -29,6 +29,8 @@
|
|||||||
|
|
||||||
#include <QAbstractItemModel>
|
#include <QAbstractItemModel>
|
||||||
|
|
||||||
|
namespace Utils { class ItemViewEvent; }
|
||||||
|
|
||||||
namespace Debugger {
|
namespace Debugger {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
@@ -77,20 +79,25 @@ signals:
|
|||||||
void currentIndexChanged();
|
void currentIndexChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// QAbstractTableModel
|
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
int rowCount(const QModelIndex &parent) const;
|
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
int columnCount(const QModelIndex &parent) const;
|
QVariant data(const QModelIndex &index, int role) const override;
|
||||||
QVariant data(const QModelIndex &index, int role) const;
|
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
|
||||||
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
|
Qt::ItemFlags flags(const QModelIndex &index) const override;
|
||||||
Qt::ItemFlags flags(const QModelIndex &index) const;
|
bool setData(const QModelIndex &idx, const QVariant &data, int role) override;
|
||||||
|
|
||||||
|
bool contextMenuEvent(const Utils::ItemViewEvent &event);
|
||||||
void resetModel() { beginResetModel(); endResetModel(); }
|
void resetModel() { beginResetModel(); endResetModel(); }
|
||||||
|
void reloadFullStack();
|
||||||
|
void copyContentsToClipboard();
|
||||||
|
void saveTaskFile();
|
||||||
|
|
||||||
DebuggerEngine *m_engine;
|
DebuggerEngine *m_engine;
|
||||||
StackFrames m_stackFrames;
|
StackFrames m_stackFrames;
|
||||||
int m_currentIndex;
|
int m_currentIndex = -1;
|
||||||
bool m_canExpand;
|
bool m_canExpand = false;
|
||||||
bool m_resetLocationScheduled;
|
bool m_resetLocationScheduled = false;
|
||||||
bool m_contentsValid;
|
bool m_contentsValid = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
|
|||||||
@@ -24,29 +24,14 @@
|
|||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
#include "stackwindow.h"
|
#include "stackwindow.h"
|
||||||
#include "stackhandler.h"
|
|
||||||
|
|
||||||
#include "debuggeractions.h"
|
#include "debuggeractions.h"
|
||||||
#include "debuggercore.h"
|
#include "debuggercore.h"
|
||||||
#include "debuggerengine.h"
|
#include "stackhandler.h"
|
||||||
#include "debuggerdialogs.h"
|
|
||||||
#include "memoryagent.h"
|
|
||||||
|
|
||||||
#include <coreplugin/messagebox.h>
|
|
||||||
|
|
||||||
#include <utils/savedaction.h>
|
#include <utils/savedaction.h>
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QAction>
|
||||||
#include <QTextStream>
|
|
||||||
#include <QFile>
|
|
||||||
#include <QDir>
|
|
||||||
|
|
||||||
#include <QApplication>
|
|
||||||
#include <QClipboard>
|
|
||||||
#include <QContextMenuEvent>
|
|
||||||
#include <QInputDialog>
|
|
||||||
#include <QFileDialog>
|
|
||||||
#include <QMenu>
|
|
||||||
|
|
||||||
namespace Debugger {
|
namespace Debugger {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
@@ -57,10 +42,6 @@ StackTreeView::StackTreeView()
|
|||||||
|
|
||||||
connect(action(UseAddressInStackView), &QAction::toggled,
|
connect(action(UseAddressInStackView), &QAction::toggled,
|
||||||
this, &StackTreeView::showAddressColumn);
|
this, &StackTreeView::showAddressColumn);
|
||||||
connect(action(ExpandStack), &QAction::triggered,
|
|
||||||
this, &StackTreeView::reloadFullStack);
|
|
||||||
connect(action(MaximalStackDepth), &QAction::triggered,
|
|
||||||
this, &StackTreeView::reloadFullStack);
|
|
||||||
showAddressColumn(false);
|
showAddressColumn(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,11 +53,6 @@ void StackTreeView::showAddressColumn(bool on)
|
|||||||
resizeColumnToContents(StackAddressColumn);
|
resizeColumnToContents(StackAddressColumn);
|
||||||
}
|
}
|
||||||
|
|
||||||
void StackTreeView::rowActivated(const QModelIndex &index)
|
|
||||||
{
|
|
||||||
currentEngine()->activateFrame(index.row());
|
|
||||||
}
|
|
||||||
|
|
||||||
void StackTreeView::setModel(QAbstractItemModel *model)
|
void StackTreeView::setModel(QAbstractItemModel *model)
|
||||||
{
|
{
|
||||||
BaseTreeView::setModel(model);
|
BaseTreeView::setModel(model);
|
||||||
@@ -85,198 +61,5 @@ void StackTreeView::setModel(QAbstractItemModel *model)
|
|||||||
showAddressColumn(action(UseAddressInStackView)->isChecked());
|
showAddressColumn(action(UseAddressInStackView)->isChecked());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Input a function to be disassembled. Accept CDB syntax
|
|
||||||
// 'Module!function' for module specification
|
|
||||||
|
|
||||||
static inline StackFrame inputFunctionForDisassembly()
|
|
||||||
{
|
|
||||||
StackFrame frame;
|
|
||||||
QInputDialog dialog;
|
|
||||||
dialog.setInputMode(QInputDialog::TextInput);
|
|
||||||
dialog.setLabelText(StackTreeView::tr("Function:"));
|
|
||||||
dialog.setWindowTitle(StackTreeView::tr("Disassemble Function"));
|
|
||||||
dialog.setWindowFlags(dialog.windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
|
||||||
if (dialog.exec() != QDialog::Accepted)
|
|
||||||
return frame;
|
|
||||||
const QString function = dialog.textValue();
|
|
||||||
if (function.isEmpty())
|
|
||||||
return frame;
|
|
||||||
const int bangPos = function.indexOf(QLatin1Char('!'));
|
|
||||||
if (bangPos != -1) {
|
|
||||||
frame.module = function.left(bangPos);
|
|
||||||
frame.function = function.mid(bangPos + 1);
|
|
||||||
} else {
|
|
||||||
frame.function = function;
|
|
||||||
}
|
|
||||||
frame.line = 42; // trick gdb into mixed mode.
|
|
||||||
return frame;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write stack frames as task file for displaying it in the build issues pane.
|
|
||||||
void saveTaskFile(QWidget *parent, const StackHandler *sh)
|
|
||||||
{
|
|
||||||
QFile file;
|
|
||||||
QFileDialog fileDialog(parent);
|
|
||||||
fileDialog.setAcceptMode(QFileDialog::AcceptSave);
|
|
||||||
fileDialog.selectFile(QDir::currentPath() + QLatin1String("/stack.tasks"));
|
|
||||||
while (!file.isOpen()) {
|
|
||||||
if (fileDialog.exec() != QDialog::Accepted)
|
|
||||||
return;
|
|
||||||
const QString fileName = fileDialog.selectedFiles().front();
|
|
||||||
file.setFileName(fileName);
|
|
||||||
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
|
||||||
Core::AsynchronousMessageBox::warning(StackTreeView::tr("Cannot Open Task File"),
|
|
||||||
StackTreeView::tr("Cannot open \"%1\": %2").arg(QDir::toNativeSeparators(fileName), file.errorString()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QTextStream str(&file);
|
|
||||||
foreach (const StackFrame &frame, sh->frames()) {
|
|
||||||
if (frame.isUsable())
|
|
||||||
str << frame.file << '\t' << frame.line << "\tstack\tFrame #" << frame.level << '\n';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void StackTreeView::contextMenuEvent(QContextMenuEvent *ev)
|
|
||||||
{
|
|
||||||
DebuggerEngine *engine = currentEngine();
|
|
||||||
StackHandler *handler = engine->stackHandler();
|
|
||||||
const QModelIndex index = indexAt(ev->pos());
|
|
||||||
const int row = index.row();
|
|
||||||
StackFrame frame;
|
|
||||||
if (row >= 0 && row < handler->stackSize())
|
|
||||||
frame = handler->frameAt(row);
|
|
||||||
const quint64 address = frame.address;
|
|
||||||
|
|
||||||
QMenu menu;
|
|
||||||
menu.addAction(action(ExpandStack));
|
|
||||||
|
|
||||||
QAction *actCopyContents = menu.addAction(tr("Copy Contents to Clipboard"));
|
|
||||||
actCopyContents->setEnabled(model() != 0);
|
|
||||||
|
|
||||||
QAction *actSaveTaskFile = menu.addAction(tr("Save as Task File..."));
|
|
||||||
actSaveTaskFile->setEnabled(model() != 0);
|
|
||||||
|
|
||||||
if (engine->hasCapability(CreateFullBacktraceCapability))
|
|
||||||
menu.addAction(action(CreateFullBacktrace));
|
|
||||||
|
|
||||||
QAction *additionalQmlStackAction = 0;
|
|
||||||
if (engine->hasCapability(AdditionalQmlStackCapability))
|
|
||||||
additionalQmlStackAction = menu.addAction(tr("Load QML Stack"));
|
|
||||||
|
|
||||||
QAction *actShowMemory = 0;
|
|
||||||
if (engine->hasCapability(ShowMemoryCapability)) {
|
|
||||||
actShowMemory = menu.addAction(QString());
|
|
||||||
if (address == 0) {
|
|
||||||
actShowMemory->setText(tr("Open Memory Editor"));
|
|
||||||
actShowMemory->setEnabled(false);
|
|
||||||
} else {
|
|
||||||
actShowMemory->setText(tr("Open Memory Editor at 0x%1").arg(address, 0, 16));
|
|
||||||
actShowMemory->setEnabled(engine->hasCapability(ShowMemoryCapability));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QAction *actShowDisassemblerAt = 0;
|
|
||||||
QAction *actShowDisassemblerAtAddress = 0;
|
|
||||||
QAction *actShowDisassemblerAtFunction = 0;
|
|
||||||
|
|
||||||
if (engine->hasCapability(DisassemblerCapability)) {
|
|
||||||
actShowDisassemblerAt = menu.addAction(QString());
|
|
||||||
actShowDisassemblerAtAddress = menu.addAction(tr("Open Disassembler at Address..."));
|
|
||||||
actShowDisassemblerAtFunction = menu.addAction(tr("Disassemble Function..."));
|
|
||||||
if (address == 0) {
|
|
||||||
actShowDisassemblerAt->setText(tr("Open Disassembler"));
|
|
||||||
actShowDisassemblerAt->setEnabled(false);
|
|
||||||
} else {
|
|
||||||
actShowDisassemblerAt->setText(tr("Open Disassembler at 0x%1").arg(address, 0, 16));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
QAction *actLoadSymbols = 0;
|
|
||||||
if (engine->hasCapability(ShowModuleSymbolsCapability))
|
|
||||||
actLoadSymbols = menu.addAction(tr("Try to Load Unknown Symbols"));
|
|
||||||
|
|
||||||
if (engine->hasCapability(MemoryAddressCapability))
|
|
||||||
menu.addAction(action(UseAddressInStackView));
|
|
||||||
|
|
||||||
menu.addSeparator();
|
|
||||||
menu.addAction(action(UseToolTipsInStackView));
|
|
||||||
menu.addSeparator();
|
|
||||||
menu.addAction(action(SettingsDialog));
|
|
||||||
|
|
||||||
QAction *act = menu.exec(ev->globalPos());
|
|
||||||
if (!act)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (act == actCopyContents) {
|
|
||||||
copyContentsToClipboard();
|
|
||||||
} else if (act == actShowMemory) {
|
|
||||||
MemoryViewSetupData data;
|
|
||||||
data.startAddress = address;
|
|
||||||
data.title = tr("Memory at Frame #%1 (%2) 0x%3").
|
|
||||||
arg(row).arg(frame.function).arg(address, 0, 16);
|
|
||||||
data.markup.push_back(MemoryMarkup(address, 1, QColor(Qt::blue).lighter(),
|
|
||||||
tr("Frame #%1 (%2)").arg(row).arg(frame.function)));
|
|
||||||
engine->openMemoryView(data);
|
|
||||||
} else if (act == actShowDisassemblerAtAddress) {
|
|
||||||
AddressDialog dialog;
|
|
||||||
if (address)
|
|
||||||
dialog.setAddress(address);
|
|
||||||
if (dialog.exec() == QDialog::Accepted)
|
|
||||||
currentEngine()->openDisassemblerView(Location(dialog.address()));
|
|
||||||
} else if (act == actShowDisassemblerAtFunction) {
|
|
||||||
const StackFrame frame = inputFunctionForDisassembly();
|
|
||||||
if (!frame.function.isEmpty())
|
|
||||||
currentEngine()->openDisassemblerView(Location(frame));
|
|
||||||
} else if (act == actShowDisassemblerAt)
|
|
||||||
engine->openDisassemblerView(frame);
|
|
||||||
else if (act == actLoadSymbols)
|
|
||||||
engine->loadSymbolsForStack();
|
|
||||||
else if (act == actSaveTaskFile)
|
|
||||||
saveTaskFile(this, handler);
|
|
||||||
else if (act == additionalQmlStackAction)
|
|
||||||
engine->loadAdditionalQmlStack();
|
|
||||||
}
|
|
||||||
|
|
||||||
void StackTreeView::copyContentsToClipboard()
|
|
||||||
{
|
|
||||||
QString str;
|
|
||||||
int n = model()->rowCount();
|
|
||||||
int m = model()->columnCount();
|
|
||||||
QVector<int> largestColumnWidths(m, 0);
|
|
||||||
|
|
||||||
// First, find the widths of the largest columns,
|
|
||||||
// so that we can print them out nicely aligned.
|
|
||||||
for (int i = 0; i != n; ++i) {
|
|
||||||
for (int j = 0; j < m; ++j) {
|
|
||||||
const QModelIndex index = model()->index(i, j);
|
|
||||||
const int columnWidth = model()->data(index).toString().size();
|
|
||||||
if (columnWidth > largestColumnWidths.at(j))
|
|
||||||
largestColumnWidths[j] = columnWidth;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i != n; ++i) {
|
|
||||||
for (int j = 0; j != m; ++j) {
|
|
||||||
QModelIndex index = model()->index(i, j);
|
|
||||||
const QString columnEntry = model()->data(index).toString();
|
|
||||||
str += columnEntry;
|
|
||||||
const int difference = largestColumnWidths.at(j) - columnEntry.size();
|
|
||||||
// Add one extra space between columns.
|
|
||||||
str += QString().fill(QLatin1Char(' '), difference > 0 ? difference + 1 : 1);
|
|
||||||
}
|
|
||||||
str += QLatin1Char('\n');
|
|
||||||
}
|
|
||||||
QClipboard *clipboard = QApplication::clipboard();
|
|
||||||
clipboard->setText(str, QClipboard::Selection);
|
|
||||||
clipboard->setText(str, QClipboard::Clipboard);
|
|
||||||
}
|
|
||||||
|
|
||||||
void StackTreeView::reloadFullStack()
|
|
||||||
{
|
|
||||||
currentEngine()->reloadFullStack();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
} // namespace Debugger
|
} // namespace Debugger
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <utils/basetreeview.h>
|
#include <utils/basetreeview.h>
|
||||||
|
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
|
|
||||||
namespace Debugger {
|
namespace Debugger {
|
||||||
@@ -34,13 +35,12 @@ namespace Internal {
|
|||||||
class StackTreeView : public Utils::BaseTreeView
|
class StackTreeView : public Utils::BaseTreeView
|
||||||
{
|
{
|
||||||
Q_DECLARE_TR_FUNCTIONS(Debugger::Internal::StackTreeView)
|
Q_DECLARE_TR_FUNCTIONS(Debugger::Internal::StackTreeView)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
StackTreeView();
|
StackTreeView();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void rowActivated(const QModelIndex &index);
|
void setModel(QAbstractItemModel *model) override;
|
||||||
void setModel(QAbstractItemModel *model);
|
|
||||||
void contextMenuEvent(QContextMenuEvent *ev);
|
|
||||||
|
|
||||||
void showAddressColumn(bool on);
|
void showAddressColumn(bool on);
|
||||||
void reloadFullStack();
|
void reloadFullStack();
|
||||||
|
|||||||
@@ -25,17 +25,22 @@
|
|||||||
|
|
||||||
#include "threadshandler.h"
|
#include "threadshandler.h"
|
||||||
|
|
||||||
|
#include "debuggeractions.h"
|
||||||
#include "debuggercore.h"
|
#include "debuggercore.h"
|
||||||
|
#include "debuggerengine.h"
|
||||||
#include "debuggericons.h"
|
#include "debuggericons.h"
|
||||||
#include "debuggerprotocol.h"
|
#include "debuggerprotocol.h"
|
||||||
#include "watchutils.h"
|
#include "watchutils.h"
|
||||||
|
|
||||||
#include <utils/algorithm.h>
|
#include <utils/algorithm.h>
|
||||||
|
#include <utils/basetreeview.h>
|
||||||
#include <utils/qtcassert.h>
|
#include <utils/qtcassert.h>
|
||||||
|
#include <utils/savedaction.h>
|
||||||
|
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QIcon>
|
#include <QIcon>
|
||||||
|
#include <QMenu>
|
||||||
|
|
||||||
using namespace Utils;
|
using namespace Utils;
|
||||||
|
|
||||||
@@ -225,7 +230,8 @@ public:
|
|||||||
represent the running threads in a QTreeView or ComboBox.
|
represent the running threads in a QTreeView or ComboBox.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
ThreadsHandler::ThreadsHandler()
|
ThreadsHandler::ThreadsHandler(DebuggerEngine *engine)
|
||||||
|
: m_engine(engine)
|
||||||
{
|
{
|
||||||
m_resetLocationScheduled = false;
|
m_resetLocationScheduled = false;
|
||||||
setObjectName(QLatin1String("ThreadsModel"));
|
setObjectName(QLatin1String("ThreadsModel"));
|
||||||
@@ -236,6 +242,24 @@ ThreadsHandler::ThreadsHandler()
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ThreadsHandler::setData(const QModelIndex &idx, const QVariant &data, int role)
|
||||||
|
{
|
||||||
|
if (role == BaseTreeView::ItemActivatedRole) {
|
||||||
|
ThreadId id = ThreadId(idx.data(ThreadData::IdRole).toLongLong());
|
||||||
|
m_engine->selectThread(id);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (role == BaseTreeView::ItemViewEventRole) {
|
||||||
|
auto menu = new QMenu;
|
||||||
|
menu->addAction(action(SettingsDialog));
|
||||||
|
menu->popup(data.value<ItemViewEvent>().globalPos());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
static ThreadItem *itemForThreadId(const ThreadsHandler *handler, ThreadId threadId)
|
static ThreadItem *itemForThreadId(const ThreadsHandler *handler, ThreadId threadId)
|
||||||
{
|
{
|
||||||
const auto matcher = [threadId](ThreadItem *item) { return item->threadData.id == threadId; };
|
const auto matcher = [threadId](ThreadItem *item) { return item->threadData.id == threadId; };
|
||||||
|
|||||||
@@ -38,6 +38,7 @@
|
|||||||
namespace Debugger {
|
namespace Debugger {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
|
class DebuggerEngine;
|
||||||
class GdbMi;
|
class GdbMi;
|
||||||
class ThreadItem;
|
class ThreadItem;
|
||||||
|
|
||||||
@@ -46,7 +47,7 @@ class ThreadsHandler : public Utils::LeveledTreeModel<Utils::TypedTreeItem<Threa
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ThreadsHandler();
|
explicit ThreadsHandler(DebuggerEngine *engine);
|
||||||
|
|
||||||
int currentThreadIndex() const;
|
int currentThreadIndex() const;
|
||||||
ThreadId currentThread() const;
|
ThreadId currentThread() const;
|
||||||
@@ -82,7 +83,9 @@ private:
|
|||||||
void updateThreadBox();
|
void updateThreadBox();
|
||||||
|
|
||||||
void sort(int column, Qt::SortOrder order);
|
void sort(int column, Qt::SortOrder order);
|
||||||
|
bool setData(const QModelIndex &idx, const QVariant &data, int role);
|
||||||
|
|
||||||
|
DebuggerEngine *m_engine;
|
||||||
ThreadId m_currentId;
|
ThreadId m_currentId;
|
||||||
bool m_resetLocationScheduled;
|
bool m_resetLocationScheduled;
|
||||||
QHash<QString, QString> m_pidForGroupId;
|
QHash<QString, QString> m_pidForGroupId;
|
||||||
|
|||||||
@@ -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 watchExpression(const QString &exp, const QString &name = QString());
|
||||||
void updateWatchExpression(WatchItem *item, const QString &newExp);
|
void updateWatchExpression(WatchItem *item, const QString &newExp);
|
||||||
void watchVariable(const QString &exp);
|
void watchVariable(const QString &exp);
|
||||||
void clearWatches();
|
|
||||||
|
|
||||||
const WatchItem *watchItem(const QModelIndex &) const;
|
const WatchItem *watchItem(const QModelIndex &) const;
|
||||||
void fetchMore(const QString &iname) const;
|
void fetchMore(const QString &iname) const;
|
||||||
@@ -96,11 +95,7 @@ public:
|
|||||||
void addDumpers(const GdbMi &dumpers);
|
void addDumpers(const GdbMi &dumpers);
|
||||||
void addTypeFormats(const QString &type, const DisplayFormats &formats);
|
void addTypeFormats(const QString &type, const DisplayFormats &formats);
|
||||||
|
|
||||||
void setUnprintableBase(int base);
|
|
||||||
static int unprintableBase();
|
|
||||||
|
|
||||||
QString watcherName(const QString &exp);
|
QString watcherName(const QString &exp);
|
||||||
QString editorContents(const QModelIndexList &list = QModelIndexList());
|
|
||||||
|
|
||||||
void scheduleResetLocation();
|
void scheduleResetLocation();
|
||||||
void resetLocation();
|
void resetLocation();
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -41,10 +41,8 @@ public:
|
|||||||
WatchType type() const { return m_type; }
|
WatchType type() const { return m_type; }
|
||||||
|
|
||||||
void setModel(QAbstractItemModel *model);
|
void setModel(QAbstractItemModel *model);
|
||||||
void rowActivated(const QModelIndex &index);
|
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
void fillFormatMenu(QMenu *, const QModelIndex &mi);
|
|
||||||
static void reexpand(QTreeView *view, const QModelIndex &idx);
|
static void reexpand(QTreeView *view, const QModelIndex &idx);
|
||||||
|
|
||||||
void watchExpression(const QString &exp);
|
void watchExpression(const QString &exp);
|
||||||
@@ -60,25 +58,10 @@ private:
|
|||||||
void collapseNode(const QModelIndex &idx);
|
void collapseNode(const QModelIndex &idx);
|
||||||
void adjustSlider();
|
void adjustSlider();
|
||||||
|
|
||||||
void showUnprintable(int base);
|
|
||||||
void doItemsLayout();
|
void doItemsLayout();
|
||||||
void keyPressEvent(QKeyEvent *ev);
|
|
||||||
void contextMenuEvent(QContextMenuEvent *ev);
|
|
||||||
void dragEnterEvent(QDragEnterEvent *ev);
|
|
||||||
void dropEvent(QDropEvent *ev);
|
|
||||||
void dragMoveEvent(QDragMoveEvent *ev);
|
|
||||||
void mouseDoubleClickEvent(QMouseEvent *ev);
|
|
||||||
bool event(QEvent *ev);
|
|
||||||
void currentChanged(const QModelIndex ¤t, const QModelIndex &previous);
|
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;
|
WatchType m_type;
|
||||||
bool m_grabbing;
|
|
||||||
int m_sliderPosition;
|
int m_sliderPosition;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user