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

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

File diff suppressed because it is too large Load Diff

View File

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