Debugger: Rework debugger option page

Use Utils::TreeModel internally, simplify code.

Change-Id: Ie5c28519d5c23441fcd6b4fbff470cc70a92ee97
Reviewed-by: Christian Stenger <christian.stenger@theqtcompany.com>
This commit is contained in:
hjk
2014-12-19 10:46:40 +01:00
committed by hjk
parent d9b3c99c1d
commit b9ef7b7b28
11 changed files with 400 additions and 840 deletions

View File

@@ -29,15 +29,14 @@
****************************************************************************/
#include "debuggeroptionspage.h"
#include "debuggeritemmanager.h"
#include "debuggeritemmodel.h"
#include <projectexplorer/projectexplorerconstants.h>
#include <utils/detailswidget.h>
#include <utils/pathchooser.h>
#include <utils/qtcassert.h>
#include <utils/treemodel.h>
#include <utils/winutils.h>
#include <QFileInfo>
@@ -55,17 +54,193 @@ using namespace Utils;
namespace Debugger {
namespace Internal {
static const char debuggingToolsWikiLinkC[] = "http://qt-project.org/wiki/Qt_Creator_Windows_Debugging";
const char debuggingToolsWikiLinkC[] = "http://qt-project.org/wiki/Qt_Creator_Windows_Debugging";
// --------------------------------------------------------------------------
// DebuggerTreeItem
// --------------------------------------------------------------------------
class DebuggerTreeItem : public TreeItem
{
public:
DebuggerTreeItem(const DebuggerItem &item, bool changed) : m_item(item), m_changed(changed) {}
int columnCount() const { return 3; }
QVariant data(int column, int role) const
{
switch (role) {
case Qt::DisplayRole:
switch (column) {
case 0: return m_item.displayName();
case 1: return m_item.command().toUserOutput();
case 2: return m_item.engineTypeName();
}
case Qt::FontRole: {
QFont font;
font.setBold(m_changed);
return font;
}
}
return QVariant();
}
DebuggerItem m_item;
bool m_changed;
};
// --------------------------------------------------------------------------
// DebuggerItemModel
// --------------------------------------------------------------------------
class DebuggerItemModel : public Utils::TreeModel
{
Q_DECLARE_TR_FUNCTIONS(Debugger::DebuggerOptionsPage)
public:
DebuggerItemModel();
QModelIndex lastIndex() const;
void setCurrentIndex(const QModelIndex &index);
DebuggerItem *currentDebugger() const;
void addDebugger(const DebuggerItem &item, bool changed);
void updateDebugger(const DebuggerItem &item);
void removeCurrentDebugger();
void apply();
private:
DebuggerTreeItem *findTreeItemById(const QVariant &id) const;
DebuggerTreeItem *m_currentTreeItem;
QStringList removed;
QList<QVariant> m_removedItems;
};
DebuggerItemModel::DebuggerItemModel()
: m_currentTreeItem(0)
{
auto root = new TreeItem(QStringList() << tr("Name") << tr("Location") << tr("Type"));
root->appendChild(new TreeItem(QStringList() << tr("Auto-detected") << QString() << QString()));
root->appendChild(new TreeItem(QStringList() << tr("Manual") << QString() << QString()));
setRootItem(root);
foreach (const DebuggerItem &item, DebuggerItemManager::debuggers())
addDebugger(item, false);
}
void DebuggerItemModel::addDebugger(const DebuggerItem &item, bool changed)
{
int group = item.isAutoDetected() ? 0 : 1;
appendItem(rootItem()->child(group), new DebuggerTreeItem(item, changed));
}
void DebuggerItemModel::updateDebugger(const DebuggerItem &item)
{
DebuggerTreeItem *treeItem = findTreeItemById(item.id());
QTC_ASSERT(treeItem, return);
TreeItem *parent = treeItem->parent();
QTC_ASSERT(parent, return);
const DebuggerItem *orig = DebuggerItemManager::findById(item.id());
treeItem->m_changed = !orig || *orig != item;
treeItem->m_item = item;
updateItem(treeItem); // Notify views.
}
DebuggerTreeItem *DebuggerItemModel::findTreeItemById(const QVariant &id) const
{
TreeItem *root = rootItem();
for (int k = 0; k < rootItem()->rowCount(); ++k) {
TreeItem *group = root->child(k);
for (int i = 0, n = group->rowCount(); i != n; ++i) {
DebuggerTreeItem *treeItem = static_cast<DebuggerTreeItem *>(group->child(i));
if (treeItem->m_item.m_id == id)
return treeItem;
}
}
return 0;
}
QModelIndex DebuggerItemModel::lastIndex() const
{
TreeItem *manualGroup = rootItem()->lastChild();
TreeItem *lastItem = manualGroup->lastChild();
return lastItem ? indexFromItem(lastItem) : QModelIndex();
}
DebuggerItem *DebuggerItemModel::currentDebugger() const
{
return m_currentTreeItem ? &m_currentTreeItem->m_item : 0;
}
void DebuggerItemModel::removeCurrentDebugger()
{
QTC_ASSERT(m_currentTreeItem, return);
QVariant id = m_currentTreeItem->m_item.id();
DebuggerTreeItem *treeItem = m_currentTreeItem;
m_currentTreeItem = 0;
removeItem(treeItem);
delete treeItem;
m_removedItems.append(id);
}
void DebuggerItemModel::apply()
{
foreach (const QVariant &id, m_removedItems)
DebuggerItemManager::deregisterDebugger(id);
TreeItem *root = rootItem();
for (int k = 0; k < rootItem()->rowCount(); ++k) {
TreeItem *group = root->child(k);
for (int i = 0, n = group->rowCount(); i != n; ++i) {
DebuggerTreeItem *treeItem = static_cast<DebuggerTreeItem *>(group->child(i));
treeItem->m_changed = false;
DebuggerItemManager::updateOrAddDebugger(treeItem->m_item);
}
}
}
void DebuggerItemModel::setCurrentIndex(const QModelIndex &index)
{
TreeItem *treeItem = itemFromIndex(index);
m_currentTreeItem = treeItem && treeItem->level() == 2 ? static_cast<DebuggerTreeItem *>(treeItem) : 0;
}
// -----------------------------------------------------------------------
// DebuggerItemConfigWidget
// -----------------------------------------------------------------------
DebuggerItemConfigWidget::DebuggerItemConfigWidget(DebuggerItemModel *model) :
m_model(model)
class DebuggerItemConfigWidget : public QWidget
{
QTC_CHECK(model);
Q_DECLARE_TR_FUNCTIONS(Debugger::DebuggerOptionsPage)
public:
explicit DebuggerItemConfigWidget(DebuggerItemModel *model);
void load(const DebuggerItem *item);
void store() const;
private:
void binaryPathHasChanged();
DebuggerItem item() const;
void setAbis(const QStringList &abiNames);
DebuggerItemModel *m_model;
QLineEdit *m_displayNameLineEdit;
QLabel *m_cdbLabel;
QLineEdit *m_versionLabel;
PathChooser *m_binaryChooser;
QLineEdit *m_abis;
bool m_autodetected;
DebuggerEngineType m_engineType;
QVariant m_id;
};
DebuggerItemConfigWidget::DebuggerItemConfigWidget(DebuggerItemModel *model)
: m_model(model)
{
m_displayNameLineEdit = new QLineEdit(this);
m_binaryChooser = new PathChooser(this);
@@ -93,15 +268,15 @@ DebuggerItemConfigWidget::DebuggerItemConfigWidget(DebuggerItemModel *model) :
formLayout->addRow(new QLabel(tr("ABIs:")), m_abis);
formLayout->addRow(new QLabel(tr("Version:")), m_versionLabel);
connect(m_binaryChooser, SIGNAL(changed(QString)), this, SLOT(binaryPathHasChanged()));
connect(m_binaryChooser, &PathChooser::changed,
this, &DebuggerItemConfigWidget::binaryPathHasChanged);
connect(m_displayNameLineEdit, &QLineEdit::textChanged,
this, &DebuggerItemConfigWidget::store);
}
DebuggerItem DebuggerItemConfigWidget::item() const
{
DebuggerItem item(m_id);
if (m_id.isNull())
return item;
item.setDisplayName(m_displayNameLineEdit->text());
item.setCommand(m_binaryChooser->fileName());
item.setAutoDetected(m_autodetected);
@@ -117,12 +292,10 @@ DebuggerItem DebuggerItemConfigWidget::item() const
return item;
}
void DebuggerItemConfigWidget::store() const
{
DebuggerItem i = item();
if (i.isValid())
m_model->updateDebugger(i);
if (!m_id.isNull())
m_model->updateDebugger(item());
}
void DebuggerItemConfigWidget::setAbis(const QStringList &abiNames)
@@ -130,8 +303,51 @@ void DebuggerItemConfigWidget::setAbis(const QStringList &abiNames)
m_abis->setText(abiNames.join(QLatin1String(", ")));
}
void DebuggerItemConfigWidget::handleCommandChange()
void DebuggerItemConfigWidget::load(const DebuggerItem *item)
{
m_id = QVariant(); // reset Id to avoid intermediate signal handling
if (!item)
return;
// Set values:
m_autodetected = item->isAutoDetected();
m_displayNameLineEdit->setEnabled(!item->isAutoDetected());
m_displayNameLineEdit->setText(item->displayName());
m_binaryChooser->setReadOnly(item->isAutoDetected());
m_binaryChooser->setFileName(item->command());
QString text;
QString versionCommand;
if (item->engineType() == CdbEngineType) {
const bool is64bit = is64BitWindowsSystem();
const QString versionString = is64bit ? tr("64-bit version") : tr("32-bit version");
//: Label text for path configuration. %2 is "x-bit version".
text = tr("<html><body><p>Specify the path to the "
"<a href=\"%1\">Windows Console Debugger executable</a>"
" (%2) here.</p>""</body></html>").
arg(QLatin1String(debuggingToolsWikiLinkC), versionString);
versionCommand = QLatin1String("-version");
} else {
versionCommand = QLatin1String("--version");
}
m_cdbLabel->setText(text);
m_cdbLabel->setVisible(!text.isEmpty());
m_binaryChooser->setCommandVersionArguments(QStringList(versionCommand));
m_versionLabel->setText(item->version());
setAbis(item->abiNames());
m_engineType = item->engineType();
m_id = item->id();
}
void DebuggerItemConfigWidget::binaryPathHasChanged()
{
// Ignore change if this is no valid DebuggerItem
if (!m_id.isValid())
return;
// Use DebuggerItemManager as a cache:
const DebuggerItem *existing
= DebuggerItemManager::findByCommand(m_binaryChooser->fileName());
@@ -153,65 +369,135 @@ void DebuggerItemConfigWidget::handleCommandChange()
m_engineType = NoEngineType;
}
}
m_model->updateDebugger(item());
store();
}
void DebuggerItemConfigWidget::setItem(const DebuggerItem &item)
// --------------------------------------------------------------------------
// DebuggerConfigWidget
// --------------------------------------------------------------------------
class DebuggerConfigWidget : public QWidget
{
store(); // store away the (changed) settings for future use
public:
DebuggerConfigWidget()
{
m_addButton = new QPushButton(tr("Add"), this);
m_id = QVariant(); // reset Id to avoid intermediate signal handling
m_cloneButton = new QPushButton(tr("Clone"), this);
m_cloneButton->setEnabled(false);
// Set values:
m_autodetected = item.isAutoDetected();
m_delButton = new QPushButton(tr("Remove"), this);
m_delButton->setEnabled(false);
m_displayNameLineEdit->setEnabled(!item.isAutoDetected());
m_displayNameLineEdit->setText(item.displayName());
m_container = new DetailsWidget(this);
m_container->setState(DetailsWidget::NoSummary);
m_container->setVisible(false);
m_binaryChooser->setReadOnly(item.isAutoDetected());
m_binaryChooser->setFileName(item.command());
m_debuggerView = new QTreeView(this);
m_debuggerView->setModel(&m_model);
m_debuggerView->setUniformRowHeights(true);
m_debuggerView->setRootIsDecorated(false);
m_debuggerView->setSelectionMode(QAbstractItemView::SingleSelection);
m_debuggerView->setSelectionBehavior(QAbstractItemView::SelectRows);
m_debuggerView->expandAll();
QString text;
QString versionCommand;
if (item.engineType() == CdbEngineType) {
const bool is64bit = is64BitWindowsSystem();
const QString versionString = is64bit ? tr("64-bit version") : tr("32-bit version");
//: Label text for path configuration. %2 is "x-bit version".
text = tr("<html><body><p>Specify the path to the "
"<a href=\"%1\">Windows Console Debugger executable</a>"
" (%2) here.</p>""</body></html>").
arg(QLatin1String(debuggingToolsWikiLinkC), versionString);
versionCommand = QLatin1String("-version");
} else {
versionCommand = QLatin1String("--version");
auto header = m_debuggerView->header();
header->setStretchLastSection(false);
header->setSectionResizeMode(0, QHeaderView::ResizeToContents);
header->setSectionResizeMode(1, QHeaderView::ResizeToContents);
header->setSectionResizeMode(2, QHeaderView::Stretch);
auto buttonLayout = new QVBoxLayout();
buttonLayout->setSpacing(6);
buttonLayout->setContentsMargins(0, 0, 0, 0);
buttonLayout->addWidget(m_addButton);
buttonLayout->addWidget(m_cloneButton);
buttonLayout->addWidget(m_delButton);
buttonLayout->addItem(new QSpacerItem(10, 40, QSizePolicy::Minimum, QSizePolicy::Expanding));
auto verticalLayout = new QVBoxLayout();
verticalLayout->addWidget(m_debuggerView);
verticalLayout->addWidget(m_container);
auto horizontalLayout = new QHBoxLayout(this);
horizontalLayout->addLayout(verticalLayout);
horizontalLayout->addLayout(buttonLayout);
connect(m_debuggerView->selectionModel(), &QItemSelectionModel::currentChanged,
this, &DebuggerConfigWidget::currentDebuggerChanged, Qt::QueuedConnection);
connect(m_addButton, &QAbstractButton::clicked,
this, &DebuggerConfigWidget::addDebugger, Qt::QueuedConnection);
connect(m_cloneButton, &QAbstractButton::clicked,
this, &DebuggerConfigWidget::cloneDebugger, Qt::QueuedConnection);
connect(m_delButton, &QAbstractButton::clicked,
this, &DebuggerConfigWidget::removeDebugger, Qt::QueuedConnection);
m_itemConfigWidget = new DebuggerItemConfigWidget(&m_model);
m_container->setWidget(m_itemConfigWidget);
}
m_cdbLabel->setText(text);
m_cdbLabel->setVisible(!text.isEmpty());
m_binaryChooser->setCommandVersionArguments(QStringList(versionCommand));
m_versionLabel->setText(item.version());
setAbis(item.abiNames());
m_engineType = item.engineType();
m_id = item.id();
}
void cloneDebugger();
void addDebugger();
void removeDebugger();
void currentDebuggerChanged(const QModelIndex &newCurrent);
void updateState();
void DebuggerItemConfigWidget::apply()
DebuggerItemModel m_model;
QTreeView *m_debuggerView;
QPushButton *m_addButton;
QPushButton *m_cloneButton;
QPushButton *m_delButton;
DetailsWidget *m_container;
DebuggerItemConfigWidget *m_itemConfigWidget;
};
void DebuggerConfigWidget::cloneDebugger()
{
DebuggerItem current = m_model->currentDebugger();
if (!current.isValid())
return; // Nothing was selected here.
store();
setItem(item());
}
void DebuggerItemConfigWidget::binaryPathHasChanged()
{
// Ignore change if this is no valid DebuggerItem
if (!m_id.isValid())
DebuggerItem *item = m_model.currentDebugger();
if (!item)
return;
handleCommandChange();
DebuggerItem newItem;
newItem.createId();
newItem.setAutoDetected(false);
newItem.setCommand(item->command());
newItem.setEngineType(item->engineType());
newItem.setAbis(item->abis());
newItem.setDisplayName(DebuggerItemManager::uniqueDisplayName(tr("Clone of %1").arg(item->displayName())));
newItem.setAutoDetected(false);
m_model.addDebugger(newItem, true);
m_debuggerView->setCurrentIndex(m_model.lastIndex());
}
void DebuggerConfigWidget::addDebugger()
{
DebuggerItem item;
item.createId();
item.setAutoDetected(false);
item.setEngineType(NoEngineType);
item.setDisplayName(DebuggerItemManager::uniqueDisplayName(tr("New Debugger")));
item.setAutoDetected(false);
m_model.addDebugger(item, true);
m_debuggerView->setCurrentIndex(m_model.lastIndex());
}
void DebuggerConfigWidget::removeDebugger()
{
m_model.removeCurrentDebugger();
m_debuggerView->setCurrentIndex(m_model.lastIndex());
}
void DebuggerConfigWidget::currentDebuggerChanged(const QModelIndex &newCurrent)
{
m_model.setCurrentIndex(newCurrent);
DebuggerItem *item = m_model.currentDebugger();
m_itemConfigWidget->load(item);
m_container->setVisible(item);
m_cloneButton->setEnabled(item && item->isValid() && item->canClone());
m_delButton->setEnabled(item && !item->isAutoDetected());
}
// --------------------------------------------------------------------------
@@ -220,13 +506,6 @@ void DebuggerItemConfigWidget::binaryPathHasChanged()
DebuggerOptionsPage::DebuggerOptionsPage()
{
m_model = 0;
m_debuggerView = 0;
m_container = 0;
m_addButton = 0;
m_cloneButton = 0;
m_delButton = 0;
setId(ProjectExplorer::Constants::DEBUGGER_SETTINGS_PAGE_ID);
setDisplayName(tr("Debuggers"));
setCategory(ProjectExplorer::Constants::PROJECTEXPLORER_SETTINGS_CATEGORY);
@@ -237,160 +516,22 @@ DebuggerOptionsPage::DebuggerOptionsPage()
QWidget *DebuggerOptionsPage::widget()
{
if (!m_configWidget) {
m_configWidget = new QWidget;
m_addButton = new QPushButton(tr("Add"), m_configWidget);
m_cloneButton = new QPushButton(tr("Clone"), m_configWidget);
m_delButton = new QPushButton(tr("Remove"), m_configWidget);
m_container = new DetailsWidget(m_configWidget);
m_container->setState(DetailsWidget::NoSummary);
m_container->setVisible(false);
m_debuggerView = new QTreeView(m_configWidget);
m_model = new DebuggerItemModel(m_debuggerView);
m_debuggerView->setModel(m_model);
m_debuggerView->setUniformRowHeights(true);
m_debuggerView->setSelectionMode(QAbstractItemView::SingleSelection);
m_debuggerView->setSelectionBehavior(QAbstractItemView::SelectRows);
m_debuggerView->expandAll();
QHeaderView *header = m_debuggerView->header();
header->setStretchLastSection(false);
header->setSectionResizeMode(0, QHeaderView::ResizeToContents);
header->setSectionResizeMode(1, QHeaderView::ResizeToContents);
header->setSectionResizeMode(2, QHeaderView::Stretch);
QVBoxLayout *buttonLayout = new QVBoxLayout();
buttonLayout->setSpacing(6);
buttonLayout->setContentsMargins(0, 0, 0, 0);
buttonLayout->addWidget(m_addButton);
buttonLayout->addWidget(m_cloneButton);
buttonLayout->addWidget(m_delButton);
buttonLayout->addItem(new QSpacerItem(10, 40, QSizePolicy::Minimum, QSizePolicy::Expanding));
QVBoxLayout *verticalLayout = new QVBoxLayout();
verticalLayout->addWidget(m_debuggerView);
verticalLayout->addWidget(m_container);
QHBoxLayout *horizontalLayout = new QHBoxLayout(m_configWidget);
horizontalLayout->addLayout(verticalLayout);
horizontalLayout->addLayout(buttonLayout);
connect(m_debuggerView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
this, SLOT(debuggerSelectionChanged()));
connect(m_addButton, SIGNAL(clicked()), this, SLOT(addDebugger()), Qt::QueuedConnection);
connect(m_cloneButton, SIGNAL(clicked()), this, SLOT(cloneDebugger()), Qt::QueuedConnection);
connect(m_delButton, SIGNAL(clicked()), this, SLOT(removeDebugger()), Qt::QueuedConnection);
m_itemConfigWidget = new DebuggerItemConfigWidget(m_model);
m_container->setWidget(m_itemConfigWidget);
updateState();
}
if (!m_configWidget)
m_configWidget = new DebuggerConfigWidget;
return m_configWidget;
}
void DebuggerOptionsPage::apply()
{
m_itemConfigWidget->apply();
m_model->apply();
}
void DebuggerOptionsPage::cloneDebugger()
{
DebuggerItem item = m_model->currentDebugger();
if (!item.isValid())
return;
DebuggerItem newItem;
newItem.createId();
newItem.setAutoDetected(false);
newItem.setCommand(item.command());
newItem.setEngineType(item.engineType());
newItem.setAbis(item.abis());
newItem.setDisplayName(DebuggerItemManager::uniqueDisplayName(tr("Clone of %1").arg(item.displayName())));
newItem.setAutoDetected(false);
m_model->addDebugger(newItem);
m_debuggerView->setCurrentIndex(m_model->lastIndex());
}
void DebuggerOptionsPage::addDebugger()
{
DebuggerItem item;
item.createId();
item.setAutoDetected(false);
item.setEngineType(NoEngineType);
item.setDisplayName(DebuggerItemManager::uniqueDisplayName(tr("New Debugger")));
item.setAutoDetected(false);
m_model->addDebugger(item);
m_debuggerView->setCurrentIndex(m_model->lastIndex());
}
void DebuggerOptionsPage::removeDebugger()
{
QVariant id = m_model->currentDebuggerId();
m_model->removeDebugger(id);
m_debuggerView->setCurrentIndex(m_model->lastIndex());
QTC_ASSERT(m_configWidget, return);
m_configWidget->m_itemConfigWidget->store();
m_configWidget->m_model.apply();
}
void DebuggerOptionsPage::finish()
{
delete m_configWidget;
// Children of m_configWidget.
m_model = 0;
m_container = 0;
m_debuggerView = 0;
m_addButton = 0;
m_cloneButton = 0;
m_delButton = 0;
}
void DebuggerOptionsPage::debuggerSelectionChanged()
{
QTC_ASSERT(m_container, return);
QModelIndex mi = m_debuggerView->currentIndex();
mi = mi.sibling(mi.row(), 0);
m_model->setCurrentIndex(mi);
DebuggerItem item = m_model->currentDebugger();
m_itemConfigWidget->setItem(item);
m_container->setVisible(item.isValid());
updateState();
}
void DebuggerOptionsPage::debuggerModelChanged()
{
QTC_ASSERT(m_container, return);
QVariant id = m_model->currentDebuggerId();
const DebuggerItem *item = DebuggerItemManager::findById(id);
if (!item)
return;
m_itemConfigWidget->setItem(*item);
m_container->setVisible(m_model->currentDebuggerId().isValid());
m_debuggerView->setCurrentIndex(m_model->currentIndex());
updateState();
}
void DebuggerOptionsPage::updateState()
{
if (!m_cloneButton)
return;
DebuggerItem item = m_model->currentDebugger();
bool canCopy = item.isValid() && item.canClone();
bool canDelete = m_model->currentIndex().parent().isValid() && !item.isAutoDetected();
m_cloneButton->setEnabled(canCopy);
m_delButton->setEnabled(canDelete);
m_configWidget = 0;
}
} // namespace Internal