2022-08-19 15:59:36 +02:00
|
|
|
// Copyright (C) 2016 The Qt Company Ltd.
|
2022-12-21 10:12:09 +01:00
|
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
2013-10-25 13:47:08 +02:00
|
|
|
|
|
|
|
|
#include "debuggeritemmanager.h"
|
2022-07-05 15:37:08 +02:00
|
|
|
|
2015-02-26 15:29:28 +01:00
|
|
|
#include "debuggeritem.h"
|
2022-07-05 15:37:08 +02:00
|
|
|
#include "debuggertr.h"
|
2013-10-25 13:47:08 +02:00
|
|
|
|
2016-11-28 18:47:18 +01:00
|
|
|
#include <coreplugin/dialogs/ioptionspage.h>
|
2013-10-25 13:47:08 +02:00
|
|
|
#include <coreplugin/icore.h>
|
|
|
|
|
|
2022-02-03 12:08:36 +01:00
|
|
|
#include <projectexplorer/devicesupport/devicemanager.h>
|
2016-11-28 18:47:18 +01:00
|
|
|
#include <projectexplorer/projectexplorerconstants.h>
|
2018-02-28 18:15:57 +01:00
|
|
|
#include <projectexplorer/projectexplorericons.h>
|
2016-11-28 18:47:18 +01:00
|
|
|
|
2017-09-20 12:53:30 +02:00
|
|
|
#include <utils/algorithm.h>
|
2023-06-29 08:23:45 +02:00
|
|
|
#include <utils/async.h>
|
2016-11-28 18:47:18 +01:00
|
|
|
#include <utils/detailswidget.h>
|
2013-10-25 13:47:08 +02:00
|
|
|
#include <utils/environment.h>
|
|
|
|
|
#include <utils/fileutils.h>
|
2016-11-28 18:47:18 +01:00
|
|
|
#include <utils/hostosinfo.h>
|
2023-06-29 08:23:45 +02:00
|
|
|
#include <utils/layoutbuilder.h>
|
2016-11-28 18:47:18 +01:00
|
|
|
#include <utils/pathchooser.h>
|
2013-10-25 13:47:08 +02:00
|
|
|
#include <utils/persistentsettings.h>
|
2023-05-03 17:05:35 +02:00
|
|
|
#include <utils/process.h>
|
2013-10-25 13:47:08 +02:00
|
|
|
#include <utils/qtcassert.h>
|
2016-11-28 18:47:18 +01:00
|
|
|
#include <utils/treemodel.h>
|
|
|
|
|
#include <utils/winutils.h>
|
2013-10-25 13:47:08 +02:00
|
|
|
|
|
|
|
|
#include <QDebug>
|
|
|
|
|
#include <QDir>
|
|
|
|
|
#include <QFileInfo>
|
2016-11-28 18:47:18 +01:00
|
|
|
#include <QFormLayout>
|
2023-06-29 08:23:45 +02:00
|
|
|
#include <QFutureWatcher>
|
2016-11-28 18:47:18 +01:00
|
|
|
#include <QHeaderView>
|
|
|
|
|
#include <QLabel>
|
|
|
|
|
#include <QLineEdit>
|
|
|
|
|
#include <QObject>
|
|
|
|
|
#include <QPointer>
|
|
|
|
|
#include <QPushButton>
|
2022-09-22 15:24:52 +02:00
|
|
|
#include <QTimer>
|
2016-11-28 18:47:18 +01:00
|
|
|
#include <QTreeView>
|
|
|
|
|
#include <QWidget>
|
|
|
|
|
|
|
|
|
|
using namespace Debugger::Internal;
|
2014-12-19 10:46:40 +01:00
|
|
|
using namespace Core;
|
2013-10-25 13:47:08 +02:00
|
|
|
using namespace ProjectExplorer;
|
|
|
|
|
using namespace Utils;
|
|
|
|
|
|
|
|
|
|
namespace Debugger {
|
2016-11-28 18:47:18 +01:00
|
|
|
namespace Internal {
|
2013-10-25 13:47:08 +02:00
|
|
|
|
2014-12-19 10:46:40 +01:00
|
|
|
const char DEBUGGER_COUNT_KEY[] = "DebuggerItem.Count";
|
|
|
|
|
const char DEBUGGER_DATA_KEY[] = "DebuggerItem.";
|
|
|
|
|
const char DEBUGGER_FILE_VERSION_KEY[] = "Version";
|
2021-04-22 16:15:26 +02:00
|
|
|
const char DEBUGGER_FILENAME[] = "debuggers.xml";
|
2016-11-28 18:47:18 +01:00
|
|
|
const char debuggingToolsWikiLinkC[] = "http://wiki.qt.io/Qt_Creator_Windows_Debugging";
|
2014-12-19 10:46:40 +01:00
|
|
|
|
2023-08-09 15:24:02 +02:00
|
|
|
static FilePath userSettingsFileName()
|
2016-11-28 18:47:18 +01:00
|
|
|
{
|
2023-08-09 15:24:02 +02:00
|
|
|
return ICore::userResourcePath(DEBUGGER_FILENAME);
|
|
|
|
|
}
|
2016-11-28 18:47:18 +01:00
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
// DebuggerItemConfigWidget
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
class DebuggerItemConfigWidget : public QWidget
|
|
|
|
|
{
|
|
|
|
|
public:
|
2023-03-28 18:22:58 +02:00
|
|
|
DebuggerItemConfigWidget();
|
|
|
|
|
|
2016-11-28 18:47:18 +01:00
|
|
|
void load(const DebuggerItem *item);
|
|
|
|
|
void store() const;
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
void binaryPathHasChanged();
|
|
|
|
|
DebuggerItem item() const;
|
|
|
|
|
void setAbis(const QStringList &abiNames);
|
|
|
|
|
|
|
|
|
|
QLineEdit *m_displayNameLineEdit;
|
|
|
|
|
QLabel *m_cdbLabel;
|
|
|
|
|
PathChooser *m_binaryChooser;
|
2017-02-22 12:21:55 +01:00
|
|
|
bool m_autodetected = false;
|
2023-03-28 18:22:58 +02:00
|
|
|
bool m_generic = false;
|
2017-02-22 12:21:55 +01:00
|
|
|
DebuggerEngineType m_engineType = NoEngineType;
|
2016-11-28 18:47:18 +01:00
|
|
|
QVariant m_id;
|
2023-03-28 18:22:58 +02:00
|
|
|
|
2023-06-29 08:23:45 +02:00
|
|
|
QLabel *m_abis;
|
|
|
|
|
QLabel *m_version;
|
|
|
|
|
QLabel *m_type;
|
|
|
|
|
|
2023-03-28 18:22:58 +02:00
|
|
|
PathChooser *m_workingDirectoryChooser;
|
2023-06-29 08:23:45 +02:00
|
|
|
QFutureWatcher<DebuggerItem> m_updateWatcher;
|
2016-11-28 18:47:18 +01:00
|
|
|
};
|
2013-10-25 13:47:08 +02:00
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
2016-11-28 18:47:18 +01:00
|
|
|
// DebuggerTreeItem
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
class DebuggerTreeItem : public TreeItem
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
DebuggerTreeItem(const DebuggerItem &item, bool changed)
|
|
|
|
|
: m_item(item), m_orig(item), m_added(changed), m_changed(changed)
|
|
|
|
|
{}
|
|
|
|
|
|
2018-07-23 22:28:49 +02:00
|
|
|
QVariant data(int column, int role) const override
|
2016-11-28 18:47:18 +01:00
|
|
|
{
|
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case Qt::FontRole: {
|
|
|
|
|
QFont font;
|
|
|
|
|
if (m_changed)
|
|
|
|
|
font.setBold(true);
|
|
|
|
|
if (m_removed)
|
|
|
|
|
font.setStrikeOut(true);
|
|
|
|
|
return font;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case Qt::DecorationRole:
|
|
|
|
|
if (column == 0)
|
|
|
|
|
return m_item.decoration();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case Qt::ToolTipRole:
|
|
|
|
|
return m_item.validityMessage();
|
|
|
|
|
}
|
|
|
|
|
return QVariant();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DebuggerItem m_item; // Displayed, possibly unapplied data.
|
|
|
|
|
DebuggerItem m_orig; // Stored original data.
|
|
|
|
|
bool m_added;
|
|
|
|
|
bool m_changed;
|
|
|
|
|
bool m_removed = false;
|
|
|
|
|
};
|
|
|
|
|
|
2013-10-25 13:47:08 +02:00
|
|
|
// --------------------------------------------------------------------------
|
2016-11-28 18:47:18 +01:00
|
|
|
// DebuggerItemModel
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
class DebuggerItemModel : public TreeModel<TreeItem, StaticTreeItem, DebuggerTreeItem>
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
DebuggerItemModel();
|
2023-03-28 07:40:26 +02:00
|
|
|
enum { Generic, AutoDetected, Manual };
|
2016-11-28 18:47:18 +01:00
|
|
|
|
|
|
|
|
QModelIndex lastIndex() const;
|
|
|
|
|
void setCurrentIndex(const QModelIndex &index);
|
2023-08-09 15:24:02 +02:00
|
|
|
DebuggerTreeItem *addDebuggerItem(const DebuggerItem &item, bool changed = false);
|
2016-11-28 18:47:18 +01:00
|
|
|
void updateDebugger(const DebuggerItem &item);
|
|
|
|
|
void apply();
|
|
|
|
|
void cancel();
|
2017-05-12 14:30:08 +02:00
|
|
|
DebuggerTreeItem *currentTreeItem();
|
2016-11-28 18:47:18 +01:00
|
|
|
|
2023-08-09 15:24:02 +02:00
|
|
|
void restoreDebuggers();
|
|
|
|
|
void saveDebuggers();
|
|
|
|
|
|
|
|
|
|
void addDebugger(const DebuggerItem &item);
|
|
|
|
|
QVariant registerDebugger(const DebuggerItem &item);
|
|
|
|
|
void readDebuggers(const FilePath &fileName, bool isSystem);
|
|
|
|
|
void autoDetectCdbDebuggers();
|
|
|
|
|
void autoDetectGdbOrLldbDebuggers(const FilePaths &searchPaths,
|
|
|
|
|
const QString &detectionSource,
|
|
|
|
|
QString *logMessage = nullptr);
|
|
|
|
|
void autoDetectUvscDebuggers();
|
|
|
|
|
QString uniqueDisplayName(const QString &base);
|
|
|
|
|
|
|
|
|
|
PersistentSettingsWriter m_writer{userSettingsFileName(), "QtCreatorDebuggers"};
|
2017-05-12 14:30:08 +02:00
|
|
|
QPersistentModelIndex m_currentIndex;
|
2016-11-28 18:47:18 +01:00
|
|
|
};
|
|
|
|
|
|
2023-08-09 15:24:02 +02:00
|
|
|
static DebuggerItemModel &itemModel()
|
|
|
|
|
{
|
|
|
|
|
static DebuggerItemModel theModel;
|
|
|
|
|
return theModel;
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-24 13:10:16 +02:00
|
|
|
template <typename Predicate>
|
2016-11-28 18:47:18 +01:00
|
|
|
void forAllDebuggers(const Predicate &pred)
|
|
|
|
|
{
|
2023-08-09 15:24:02 +02:00
|
|
|
itemModel().forItemsAtLevel<2>([pred](DebuggerTreeItem *titem) {
|
2016-11-28 18:47:18 +01:00
|
|
|
pred(titem->m_item);
|
|
|
|
|
});
|
|
|
|
|
}
|
2013-10-25 13:47:08 +02:00
|
|
|
|
2017-10-24 13:10:16 +02:00
|
|
|
template <typename Predicate>
|
2016-11-28 18:47:18 +01:00
|
|
|
const DebuggerItem *findDebugger(const Predicate &pred)
|
|
|
|
|
{
|
2023-08-09 15:24:02 +02:00
|
|
|
DebuggerTreeItem *titem = itemModel().findItemAtLevel<2>([pred](DebuggerTreeItem *titem) {
|
2016-11-28 18:47:18 +01:00
|
|
|
return pred(titem->m_item);
|
|
|
|
|
});
|
|
|
|
|
return titem ? &titem->m_item : nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DebuggerItemModel::DebuggerItemModel()
|
|
|
|
|
{
|
2022-07-05 15:37:08 +02:00
|
|
|
setHeader({Tr::tr("Name"), Tr::tr("Path"), Tr::tr("Type")});
|
2023-03-28 07:40:26 +02:00
|
|
|
|
|
|
|
|
auto generic = new StaticTreeItem(Tr::tr("Generic"));
|
|
|
|
|
auto autoDetected = new StaticTreeItem({ProjectExplorer::Constants::msgAutoDetected()},
|
|
|
|
|
{ProjectExplorer::Constants::msgAutoDetectedToolTip()});
|
|
|
|
|
rootItem()->appendChild(generic);
|
|
|
|
|
rootItem()->appendChild(autoDetected);
|
2021-01-29 10:01:03 +01:00
|
|
|
rootItem()->appendChild(new StaticTreeItem(ProjectExplorer::Constants::msgManual()));
|
2023-03-28 07:40:26 +02:00
|
|
|
|
|
|
|
|
DebuggerItem genericGdb(QVariant("gdb"));
|
|
|
|
|
genericGdb.setAutoDetected(true);
|
2023-03-28 18:22:58 +02:00
|
|
|
genericGdb.setGeneric(true);
|
2023-03-28 07:40:26 +02:00
|
|
|
genericGdb.setEngineType(GdbEngineType);
|
|
|
|
|
genericGdb.setAbi(Abi());
|
|
|
|
|
genericGdb.setCommand("gdb");
|
2023-06-13 15:32:09 +02:00
|
|
|
genericGdb.setUnexpandedDisplayName(Tr::tr("GDB from PATH on Build Device"));
|
2023-03-28 07:40:26 +02:00
|
|
|
generic->appendChild(new DebuggerTreeItem(genericGdb, false));
|
|
|
|
|
|
|
|
|
|
DebuggerItem genericLldb(QVariant("lldb"));
|
|
|
|
|
genericLldb.setAutoDetected(true);
|
|
|
|
|
genericLldb.setEngineType(LldbEngineType);
|
2023-03-28 18:22:58 +02:00
|
|
|
genericLldb.setGeneric(true);
|
2023-03-28 07:40:26 +02:00
|
|
|
genericLldb.setAbi(Abi());
|
|
|
|
|
genericLldb.setCommand("lldb");
|
2023-06-13 15:32:09 +02:00
|
|
|
genericLldb.setUnexpandedDisplayName(Tr::tr("LLDB from PATH on Build Device"));
|
2023-03-28 07:40:26 +02:00
|
|
|
generic->appendChild(new DebuggerTreeItem(genericLldb, false));
|
2023-08-09 15:24:02 +02:00
|
|
|
|
|
|
|
|
connect(ICore::instance(), &ICore::saveSettingsRequested,
|
|
|
|
|
this, &DebuggerItemModel::saveDebuggers);
|
2016-11-28 18:47:18 +01:00
|
|
|
}
|
|
|
|
|
|
2023-08-09 15:24:02 +02:00
|
|
|
DebuggerTreeItem *DebuggerItemModel::addDebuggerItem(const DebuggerItem &item, bool changed)
|
2014-12-19 10:46:40 +01:00
|
|
|
{
|
2023-03-28 18:22:58 +02:00
|
|
|
QTC_ASSERT(item.id().isValid(), return {});
|
|
|
|
|
int group = item.isGeneric() ? Generic : (item.isAutoDetected() ? AutoDetected : Manual);
|
|
|
|
|
auto treeItem = new DebuggerTreeItem(item, changed);
|
|
|
|
|
rootItem()->childAt(group)->appendChild(treeItem);
|
|
|
|
|
return treeItem;
|
2014-12-19 10:46:40 +01:00
|
|
|
}
|
2013-10-25 13:47:08 +02:00
|
|
|
|
2016-11-28 18:47:18 +01:00
|
|
|
void DebuggerItemModel::updateDebugger(const DebuggerItem &item)
|
2013-10-25 13:47:08 +02:00
|
|
|
{
|
2016-11-28 18:47:18 +01:00
|
|
|
auto matcher = [item](DebuggerTreeItem *n) { return n->m_item.m_id == item.id(); };
|
|
|
|
|
DebuggerTreeItem *treeItem = findItemAtLevel<2>(matcher);
|
|
|
|
|
QTC_ASSERT(treeItem, return);
|
|
|
|
|
|
|
|
|
|
TreeItem *parent = treeItem->parent();
|
|
|
|
|
QTC_ASSERT(parent, return);
|
|
|
|
|
|
|
|
|
|
treeItem->m_changed = treeItem->m_orig != item;
|
|
|
|
|
treeItem->m_item = item;
|
|
|
|
|
treeItem->update(); // Notify views.
|
2013-10-25 13:47:08 +02:00
|
|
|
}
|
|
|
|
|
|
2016-11-28 18:47:18 +01:00
|
|
|
QModelIndex DebuggerItemModel::lastIndex() const
|
2013-10-25 13:47:08 +02:00
|
|
|
{
|
2016-11-28 18:47:18 +01:00
|
|
|
TreeItem *manualGroup = rootItem()->lastChild();
|
|
|
|
|
TreeItem *lastItem = manualGroup->lastChild();
|
|
|
|
|
return lastItem ? indexForItem(lastItem) : QModelIndex();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DebuggerItemModel::apply()
|
|
|
|
|
{
|
|
|
|
|
QList<DebuggerTreeItem *> toRemove;
|
|
|
|
|
forItemsAtLevel<2>([&toRemove](DebuggerTreeItem *titem) {
|
|
|
|
|
titem->m_added = false;
|
|
|
|
|
if (titem->m_changed) {
|
|
|
|
|
titem->m_changed = false;
|
|
|
|
|
titem->m_orig = titem->m_item;
|
|
|
|
|
}
|
|
|
|
|
if (titem->m_removed)
|
|
|
|
|
toRemove.append(titem);
|
|
|
|
|
});
|
|
|
|
|
for (DebuggerTreeItem *titem : toRemove)
|
|
|
|
|
destroyItem(titem);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DebuggerItemModel::cancel()
|
|
|
|
|
{
|
|
|
|
|
QList<DebuggerTreeItem *> toRemove;
|
|
|
|
|
forItemsAtLevel<2>([&toRemove](DebuggerTreeItem *titem) {
|
|
|
|
|
titem->m_removed = false;
|
|
|
|
|
if (titem->m_changed) {
|
|
|
|
|
titem->m_changed = false;
|
|
|
|
|
titem->m_item = titem->m_orig;
|
|
|
|
|
}
|
|
|
|
|
if (titem->m_added)
|
|
|
|
|
toRemove.append(titem);
|
|
|
|
|
});
|
|
|
|
|
for (DebuggerTreeItem *titem : toRemove)
|
|
|
|
|
destroyItem(titem);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DebuggerItemModel::setCurrentIndex(const QModelIndex &index)
|
|
|
|
|
{
|
2017-05-12 14:30:08 +02:00
|
|
|
m_currentIndex = index;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DebuggerTreeItem *DebuggerItemModel::currentTreeItem()
|
|
|
|
|
{
|
|
|
|
|
TreeItem *treeItem = itemForIndex(m_currentIndex);
|
|
|
|
|
return treeItem && treeItem->level() == 2 ? static_cast<DebuggerTreeItem *>(treeItem) : nullptr;
|
2016-11-28 18:47:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DebuggerItemConfigWidget::DebuggerItemConfigWidget()
|
|
|
|
|
{
|
|
|
|
|
m_displayNameLineEdit = new QLineEdit(this);
|
|
|
|
|
|
|
|
|
|
m_binaryChooser = new PathChooser(this);
|
|
|
|
|
m_binaryChooser->setExpectedKind(PathChooser::ExistingCommand);
|
|
|
|
|
m_binaryChooser->setMinimumWidth(400);
|
|
|
|
|
m_binaryChooser->setHistoryCompleter("DebuggerPaths");
|
2023-06-29 08:23:45 +02:00
|
|
|
m_binaryChooser->setValidationFunction(
|
|
|
|
|
[this](const QString &text) -> FancyLineEdit::AsyncValidationFuture {
|
|
|
|
|
return m_binaryChooser->defaultValidationFunction()(text).then(
|
|
|
|
|
[](const FancyLineEdit::AsyncValidationResult &result)
|
|
|
|
|
-> FancyLineEdit::AsyncValidationResult {
|
|
|
|
|
if (!result)
|
|
|
|
|
return result;
|
|
|
|
|
|
|
|
|
|
DebuggerItem item;
|
|
|
|
|
item.setCommand(FilePath::fromUserInput(result.value()));
|
|
|
|
|
QString errorMessage;
|
|
|
|
|
item.reinitializeFromFile(&errorMessage);
|
|
|
|
|
|
|
|
|
|
if (!errorMessage.isEmpty())
|
|
|
|
|
return make_unexpected(errorMessage);
|
|
|
|
|
|
|
|
|
|
return result.value();
|
|
|
|
|
});
|
|
|
|
|
});
|
2022-05-31 11:16:44 +02:00
|
|
|
m_binaryChooser->setAllowPathFromDevice(true);
|
2016-11-28 18:47:18 +01:00
|
|
|
|
|
|
|
|
m_workingDirectoryChooser = new PathChooser(this);
|
|
|
|
|
m_workingDirectoryChooser->setExpectedKind(PathChooser::Directory);
|
|
|
|
|
m_workingDirectoryChooser->setMinimumWidth(400);
|
|
|
|
|
m_workingDirectoryChooser->setHistoryCompleter("DebuggerPaths");
|
|
|
|
|
|
2023-06-29 08:23:45 +02:00
|
|
|
auto makeInteractiveLabel = []() {
|
|
|
|
|
auto label = new QLabel;
|
|
|
|
|
label->setTextInteractionFlags(Qt::TextEditorInteraction | Qt::TextBrowserInteraction);
|
|
|
|
|
label->setOpenExternalLinks(true);
|
|
|
|
|
return label;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
m_cdbLabel = makeInteractiveLabel();
|
|
|
|
|
m_version = makeInteractiveLabel();
|
|
|
|
|
m_abis = makeInteractiveLabel();
|
|
|
|
|
m_type = makeInteractiveLabel();
|
2016-11-28 18:47:18 +01:00
|
|
|
|
2022-09-02 11:49:36 +02:00
|
|
|
connect(m_binaryChooser, &PathChooser::textChanged,
|
2016-11-28 18:47:18 +01:00
|
|
|
this, &DebuggerItemConfigWidget::binaryPathHasChanged);
|
2022-09-02 11:49:36 +02:00
|
|
|
connect(m_workingDirectoryChooser, &PathChooser::textChanged,
|
2016-11-28 18:47:18 +01:00
|
|
|
this, &DebuggerItemConfigWidget::store);
|
|
|
|
|
connect(m_displayNameLineEdit, &QLineEdit::textChanged,
|
|
|
|
|
this, &DebuggerItemConfigWidget::store);
|
2023-06-29 08:23:45 +02:00
|
|
|
|
|
|
|
|
connect(&m_updateWatcher, &QFutureWatcher<DebuggerItem>::finished, this, [this] {
|
|
|
|
|
if (m_updateWatcher.future().resultCount() > 0) {
|
|
|
|
|
DebuggerItem tmp = m_updateWatcher.result();
|
|
|
|
|
setAbis(tmp.abiNames());
|
|
|
|
|
m_version->setText(tmp.version());
|
|
|
|
|
m_engineType = tmp.engineType();
|
|
|
|
|
m_type->setText(tmp.engineTypeName());
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// clang-format off
|
|
|
|
|
using namespace Layouting;
|
|
|
|
|
Form {
|
|
|
|
|
fieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow),
|
|
|
|
|
Tr::tr("Name:"), m_displayNameLineEdit, br,
|
|
|
|
|
Tr::tr("Path:"), m_binaryChooser, br,
|
|
|
|
|
m_cdbLabel, br,
|
|
|
|
|
Tr::tr("Type:"), m_type, br,
|
|
|
|
|
Tr::tr("ABIs:"), m_abis, br,
|
|
|
|
|
Tr::tr("Version:"), m_version, br,
|
|
|
|
|
Tr::tr("Working directory:"), m_workingDirectoryChooser, br,
|
|
|
|
|
}.attachTo(this);
|
|
|
|
|
// clang-format on
|
2016-11-28 18:47:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DebuggerItem DebuggerItemConfigWidget::item() const
|
|
|
|
|
{
|
2023-03-28 18:22:58 +02:00
|
|
|
static const QRegularExpression noAbi("[^A-Za-z0-9-_]+");
|
|
|
|
|
|
2016-11-28 18:47:18 +01:00
|
|
|
DebuggerItem item(m_id);
|
|
|
|
|
item.setUnexpandedDisplayName(m_displayNameLineEdit->text());
|
2020-04-09 11:05:50 +02:00
|
|
|
item.setCommand(m_binaryChooser->filePath());
|
|
|
|
|
item.setWorkingDirectory(m_workingDirectoryChooser->filePath());
|
2016-11-28 18:47:18 +01:00
|
|
|
item.setAutoDetected(m_autodetected);
|
2019-07-25 17:27:58 +02:00
|
|
|
Abis abiList;
|
2023-03-28 18:22:58 +02:00
|
|
|
const QStringList abis = m_abis->text().split(noAbi);
|
2019-07-25 17:27:58 +02:00
|
|
|
for (const QString &a : abis) {
|
2016-11-28 18:47:18 +01:00
|
|
|
if (a.isNull())
|
|
|
|
|
continue;
|
2018-06-14 15:46:33 +02:00
|
|
|
abiList << Abi::fromString(a);
|
2016-11-28 18:47:18 +01:00
|
|
|
}
|
|
|
|
|
item.setAbis(abiList);
|
2023-03-28 18:22:58 +02:00
|
|
|
item.setVersion(m_version->text());
|
2016-11-28 18:47:18 +01:00
|
|
|
item.setEngineType(m_engineType);
|
2023-03-28 18:22:58 +02:00
|
|
|
item.setGeneric(m_generic);
|
2016-11-28 18:47:18 +01:00
|
|
|
return item;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DebuggerItemConfigWidget::store() const
|
|
|
|
|
{
|
|
|
|
|
if (!m_id.isNull())
|
2023-08-09 15:24:02 +02:00
|
|
|
itemModel().updateDebugger(item());
|
2016-11-28 18:47:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DebuggerItemConfigWidget::setAbis(const QStringList &abiNames)
|
|
|
|
|
{
|
2018-10-07 22:38:47 +03:00
|
|
|
m_abis->setText(abiNames.join(", "));
|
2016-11-28 18:47:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DebuggerItemConfigWidget::load(const DebuggerItem *item)
|
|
|
|
|
{
|
|
|
|
|
m_id = QVariant(); // reset Id to avoid intermediate signal handling
|
|
|
|
|
if (!item)
|
2013-10-25 13:47:08 +02:00
|
|
|
return;
|
|
|
|
|
|
2016-11-28 18:47:18 +01:00
|
|
|
// Set values:
|
2023-03-28 18:22:58 +02:00
|
|
|
m_generic = item->isGeneric();
|
2016-11-28 18:47:18 +01:00
|
|
|
m_autodetected = item->isAutoDetected();
|
|
|
|
|
|
|
|
|
|
m_displayNameLineEdit->setEnabled(!item->isAutoDetected());
|
|
|
|
|
m_displayNameLineEdit->setText(item->unexpandedDisplayName());
|
|
|
|
|
|
2023-06-29 08:23:45 +02:00
|
|
|
m_type->setText(item->engineTypeName());
|
2016-11-28 18:47:18 +01:00
|
|
|
|
|
|
|
|
m_binaryChooser->setReadOnly(item->isAutoDetected());
|
2020-04-09 11:05:50 +02:00
|
|
|
m_binaryChooser->setFilePath(item->command());
|
2023-03-28 18:22:58 +02:00
|
|
|
m_binaryChooser->setExpectedKind(m_generic ? PathChooser::Any : PathChooser::ExistingCommand);
|
|
|
|
|
|
2016-11-28 18:47:18 +01:00
|
|
|
m_workingDirectoryChooser->setReadOnly(item->isAutoDetected());
|
2020-04-09 11:05:50 +02:00
|
|
|
m_workingDirectoryChooser->setFilePath(item->workingDirectory());
|
2016-11-28 18:47:18 +01:00
|
|
|
|
|
|
|
|
QString text;
|
|
|
|
|
QString versionCommand;
|
|
|
|
|
if (item->engineType() == CdbEngineType) {
|
|
|
|
|
const bool is64bit = is64BitWindowsSystem();
|
2022-07-05 15:37:08 +02:00
|
|
|
const QString versionString = is64bit ? Tr::tr("64-bit version") : Tr::tr("32-bit version");
|
2016-11-28 18:47:18 +01:00
|
|
|
//: Label text for path configuration. %2 is "x-bit version".
|
2017-09-13 15:56:47 +02:00
|
|
|
text = "<html><body><p>"
|
2022-07-05 15:37:08 +02:00
|
|
|
+ Tr::tr("Specify the path to the "
|
2017-09-13 15:56:47 +02:00
|
|
|
"<a href=\"%1\">Windows Console Debugger executable</a>"
|
|
|
|
|
" (%2) here.").arg(QLatin1String(debuggingToolsWikiLinkC), versionString)
|
|
|
|
|
+ "</p></body></html>";
|
2018-10-07 22:38:47 +03:00
|
|
|
versionCommand = "-version";
|
2016-11-28 18:47:18 +01:00
|
|
|
} else {
|
2018-10-07 22:38:47 +03:00
|
|
|
versionCommand = "--version";
|
2016-11-28 18:47:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_cdbLabel->setText(text);
|
|
|
|
|
m_cdbLabel->setVisible(!text.isEmpty());
|
|
|
|
|
m_binaryChooser->setCommandVersionArguments(QStringList(versionCommand));
|
2023-03-28 18:22:58 +02:00
|
|
|
m_version->setText(item->version());
|
2016-11-28 18:47:18 +01:00
|
|
|
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())
|
2013-10-25 13:47:08 +02:00
|
|
|
return;
|
|
|
|
|
|
2023-03-28 18:22:58 +02:00
|
|
|
if (!m_generic) {
|
2023-06-29 08:23:45 +02:00
|
|
|
m_updateWatcher.cancel();
|
|
|
|
|
|
2023-03-28 18:22:58 +02:00
|
|
|
DebuggerItem tmp;
|
|
|
|
|
if (m_binaryChooser->filePath().isExecutableFile()) {
|
|
|
|
|
tmp = item();
|
2023-06-29 08:23:45 +02:00
|
|
|
m_updateWatcher.setFuture(Utils::asyncRun([tmp]() mutable {
|
|
|
|
|
tmp.reinitializeFromFile();
|
|
|
|
|
return tmp;
|
|
|
|
|
}));
|
|
|
|
|
} else {
|
|
|
|
|
setAbis(tmp.abiNames());
|
|
|
|
|
m_version->setText(tmp.version());
|
|
|
|
|
m_engineType = tmp.engineType();
|
|
|
|
|
m_type->setText(tmp.engineTypeName());
|
2023-03-28 18:22:58 +02:00
|
|
|
}
|
|
|
|
|
}
|
2016-11-28 18:47:18 +01:00
|
|
|
|
|
|
|
|
store();
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-09 15:24:02 +02:00
|
|
|
void DebuggerItemModel::autoDetectCdbDebuggers()
|
2013-10-25 13:47:08 +02:00
|
|
|
{
|
2019-12-17 14:07:53 +01:00
|
|
|
FilePaths cdbs;
|
2013-10-25 13:47:08 +02:00
|
|
|
|
2022-08-24 14:55:12 +02:00
|
|
|
const QStringList programDirs = {qtcEnvironmentVariable("ProgramFiles"),
|
|
|
|
|
qtcEnvironmentVariable("ProgramFiles(x86)"),
|
|
|
|
|
qtcEnvironmentVariable("ProgramW6432")};
|
2013-10-25 13:47:08 +02:00
|
|
|
|
2017-11-28 10:53:47 +01:00
|
|
|
QFileInfoList kitFolders;
|
|
|
|
|
|
|
|
|
|
for (const QString &dirName : programDirs) {
|
2013-10-25 13:47:08 +02:00
|
|
|
if (dirName.isEmpty())
|
|
|
|
|
continue;
|
2017-11-28 09:22:00 +01:00
|
|
|
const QDir dir(dirName);
|
2013-10-25 13:47:08 +02:00
|
|
|
// Windows SDK's starting from version 8 live in
|
|
|
|
|
// "ProgramDir\Windows Kits\<version>"
|
2017-11-28 09:22:00 +01:00
|
|
|
const QString windowsKitsFolderName = "Windows Kits";
|
2013-10-25 13:47:08 +02:00
|
|
|
if (dir.exists(windowsKitsFolderName)) {
|
|
|
|
|
QDir windowKitsFolder = dir;
|
|
|
|
|
if (windowKitsFolder.cd(windowsKitsFolderName)) {
|
|
|
|
|
// Check in reverse order (latest first)
|
2017-11-28 10:53:47 +01:00
|
|
|
kitFolders.append(windowKitsFolder.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot,
|
|
|
|
|
QDir::Time | QDir::Reversed));
|
2013-10-25 13:47:08 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Pre Windows SDK 8: Check 'Debugging Tools for Windows'
|
2017-11-28 09:22:00 +01:00
|
|
|
for (const QFileInfo &fi : dir.entryInfoList({"Debugging Tools for Windows*"},
|
|
|
|
|
QDir::Dirs | QDir::NoDotAndDotDot)) {
|
2019-05-28 13:49:26 +02:00
|
|
|
const FilePath filePath = FilePath::fromFileInfo(fi).pathAppended("cdb.exe");
|
2013-10-25 13:47:08 +02:00
|
|
|
if (!cdbs.contains(filePath))
|
|
|
|
|
cdbs.append(filePath);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-11-28 10:53:47 +01:00
|
|
|
|
|
|
|
|
constexpr char RootVal[] = "KitsRoot";
|
|
|
|
|
constexpr char RootVal81[] = "KitsRoot81";
|
|
|
|
|
constexpr char RootVal10[] = "KitsRoot10";
|
|
|
|
|
const QSettings installedRoots(
|
|
|
|
|
"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots",
|
|
|
|
|
QSettings::NativeFormat);
|
|
|
|
|
for (auto rootVal : {RootVal, RootVal81, RootVal10}) {
|
|
|
|
|
QFileInfo root(installedRoots.value(QLatin1String(rootVal)).toString());
|
|
|
|
|
if (root.exists() && !kitFolders.contains(root))
|
|
|
|
|
kitFolders.append(root);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const QFileInfo &kitFolderFi : kitFolders) {
|
|
|
|
|
const QString path = kitFolderFi.absoluteFilePath();
|
2022-05-30 10:49:07 +02:00
|
|
|
QStringList abis = {"x86", "x64"};
|
2022-06-20 13:18:28 +02:00
|
|
|
if (HostOsInfo::hostArchitecture() == HostOsInfo::HostArchitectureArm64)
|
2022-05-30 10:49:07 +02:00
|
|
|
abis << "arm64";
|
2022-05-23 18:13:59 +02:00
|
|
|
for (const QString &abi: abis) {
|
|
|
|
|
const QFileInfo cdbBinary(path + "/Debuggers/" + abi + "/cdb.exe");
|
|
|
|
|
if (cdbBinary.isExecutable())
|
|
|
|
|
cdbs.append(FilePath::fromString(cdbBinary.absoluteFilePath()));
|
|
|
|
|
}
|
2017-11-28 10:53:47 +01:00
|
|
|
}
|
|
|
|
|
|
2022-10-07 14:46:06 +02:00
|
|
|
for (const FilePath &cdb : std::as_const(cdbs)) {
|
2016-11-28 18:47:18 +01:00
|
|
|
if (DebuggerItemManager::findByCommand(cdb))
|
2013-10-25 13:47:08 +02:00
|
|
|
continue;
|
|
|
|
|
DebuggerItem item;
|
2013-11-06 16:10:24 +01:00
|
|
|
item.createId();
|
2013-10-25 13:47:08 +02:00
|
|
|
item.setAutoDetected(true);
|
|
|
|
|
item.setAbis(Abi::abisOfBinary(cdb));
|
|
|
|
|
item.setCommand(cdb);
|
|
|
|
|
item.setEngineType(CdbEngineType);
|
2022-07-05 15:37:08 +02:00
|
|
|
item.setUnexpandedDisplayName(uniqueDisplayName(Tr::tr("Auto-detected CDB at %1").arg(cdb.toUserOutput())));
|
2020-07-15 10:59:06 +02:00
|
|
|
item.reinitializeFromFile(); // collect version number
|
2023-08-09 15:24:02 +02:00
|
|
|
addDebuggerItem(item);
|
2013-10-25 13:47:08 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-17 14:07:53 +01:00
|
|
|
static Utils::FilePaths searchGdbPathsFromRegistry()
|
2019-07-09 19:38:02 +03:00
|
|
|
{
|
|
|
|
|
if (!HostOsInfo::isWindowsHost())
|
|
|
|
|
return {};
|
|
|
|
|
|
|
|
|
|
// Registry token for the "GNU Tools for ARM Embedded Processors".
|
|
|
|
|
static const char kRegistryToken[] = "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\" \
|
|
|
|
|
"Windows\\CurrentVersion\\Uninstall\\";
|
|
|
|
|
|
2019-12-17 14:07:53 +01:00
|
|
|
Utils::FilePaths searchPaths;
|
2019-07-09 19:38:02 +03:00
|
|
|
|
|
|
|
|
QSettings registry(kRegistryToken, QSettings::NativeFormat);
|
|
|
|
|
const auto productGroups = registry.childGroups();
|
|
|
|
|
for (const QString &productKey : productGroups) {
|
|
|
|
|
if (!productKey.startsWith("GNU Tools for ARM Embedded Processors"))
|
|
|
|
|
continue;
|
|
|
|
|
registry.beginGroup(productKey);
|
|
|
|
|
QString uninstallFilePath = registry.value("UninstallString").toString();
|
|
|
|
|
if (uninstallFilePath.startsWith(QLatin1Char('"')))
|
|
|
|
|
uninstallFilePath.remove(0, 1);
|
|
|
|
|
if (uninstallFilePath.endsWith(QLatin1Char('"')))
|
|
|
|
|
uninstallFilePath.remove(uninstallFilePath.size() - 1, 1);
|
|
|
|
|
registry.endGroup();
|
|
|
|
|
|
|
|
|
|
const QString toolkitRootPath = QFileInfo(uninstallFilePath).path();
|
|
|
|
|
const QString toolchainPath = toolkitRootPath + QLatin1String("/bin");
|
|
|
|
|
searchPaths.push_back(FilePath::fromString(toolchainPath));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return searchPaths;
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-09 15:24:02 +02:00
|
|
|
void DebuggerItemModel::autoDetectGdbOrLldbDebuggers(const FilePaths &searchPaths,
|
2021-07-13 13:05:36 +02:00
|
|
|
const QString &detectionSource,
|
|
|
|
|
QString *logMessage)
|
2013-10-25 13:47:08 +02:00
|
|
|
{
|
2017-02-22 15:09:35 +01:00
|
|
|
const QStringList filters = {"gdb-i686-pc-mingw32", "gdb-i686-pc-mingw32.exe", "gdb",
|
2019-07-09 19:38:02 +03:00
|
|
|
"gdb.exe", "lldb", "lldb.exe", "lldb-[1-9]*",
|
2022-09-14 17:34:16 +02:00
|
|
|
"arm-none-eabi-gdb-py.exe", "*-*-*-gdb"};
|
2013-10-25 13:47:08 +02:00
|
|
|
|
2021-12-03 10:21:57 +01:00
|
|
|
if (searchPaths.isEmpty())
|
|
|
|
|
return;
|
|
|
|
|
|
2019-12-17 14:07:53 +01:00
|
|
|
FilePaths suspects;
|
2013-10-25 13:47:08 +02:00
|
|
|
|
2021-12-14 12:15:40 +01:00
|
|
|
if (searchPaths.front().osType() == OsTypeMac) {
|
2023-05-03 16:00:22 +02:00
|
|
|
Process proc;
|
2021-05-12 14:25:50 +02:00
|
|
|
proc.setTimeoutS(2);
|
2021-05-17 12:02:42 +02:00
|
|
|
proc.setCommand({"xcrun", {"--find", "lldb"}});
|
|
|
|
|
proc.runBlocking();
|
2021-06-21 10:27:02 +02:00
|
|
|
// FIXME:
|
2022-03-02 04:12:25 +01:00
|
|
|
if (proc.result() == ProcessResult::FinishedWithSuccess) {
|
2021-05-12 14:25:50 +02:00
|
|
|
QString lPath = proc.allOutput().trimmed();
|
2016-04-29 16:52:58 +02:00
|
|
|
if (!lPath.isEmpty()) {
|
|
|
|
|
const QFileInfo fi(lPath);
|
|
|
|
|
if (fi.exists() && fi.isExecutable() && !fi.isDir())
|
2019-05-28 13:49:26 +02:00
|
|
|
suspects.append(FilePath::fromString(fi.absoluteFilePath()));
|
2016-04-29 16:52:58 +02:00
|
|
|
}
|
2013-11-05 11:32:47 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-03 10:21:57 +01:00
|
|
|
FilePaths paths = searchPaths;
|
|
|
|
|
if (!searchPaths.front().needsDevice())
|
2021-06-21 10:27:02 +02:00
|
|
|
paths.append(searchGdbPathsFromRegistry());
|
|
|
|
|
|
|
|
|
|
paths = Utils::filteredUnique(paths);
|
|
|
|
|
|
2023-01-24 11:23:47 +01:00
|
|
|
const auto addSuspect = [&suspects](const FilePath &entry) {
|
|
|
|
|
suspects.append(entry);
|
|
|
|
|
return IterationPolicy::Continue;
|
|
|
|
|
};
|
2021-12-14 12:15:40 +01:00
|
|
|
for (const FilePath &path : paths)
|
2022-01-21 12:22:54 +01:00
|
|
|
path.iterateDirectory(addSuspect, {filters, QDir::Files | QDir::Executable});
|
2013-10-25 13:47:08 +02:00
|
|
|
|
2022-07-05 15:37:08 +02:00
|
|
|
QStringList logMessages{Tr::tr("Searching debuggers...")};
|
2022-10-07 14:46:06 +02:00
|
|
|
for (const FilePath &command : std::as_const(suspects)) {
|
2016-11-28 18:47:18 +01:00
|
|
|
const auto commandMatches = [command](const DebuggerTreeItem *titem) {
|
|
|
|
|
return titem->m_item.command() == command;
|
|
|
|
|
};
|
2023-08-09 15:24:02 +02:00
|
|
|
if (DebuggerTreeItem *existingItem = findItemAtLevel<2>(commandMatches)) {
|
2021-06-17 17:25:38 +02:00
|
|
|
if (command.lastModified() != existingItem->m_item.lastModified())
|
2016-11-28 18:47:18 +01:00
|
|
|
existingItem->m_item.reinitializeFromFile();
|
2014-12-09 22:23:41 +02:00
|
|
|
continue;
|
2016-03-06 23:26:01 +02:00
|
|
|
}
|
2014-12-09 22:23:41 +02:00
|
|
|
DebuggerItem item;
|
|
|
|
|
item.createId();
|
2021-07-09 17:39:50 +02:00
|
|
|
item.setDetectionSource(detectionSource);
|
2022-04-07 14:04:20 +02:00
|
|
|
item.setAutoDetected(true);
|
2014-12-09 22:23:41 +02:00
|
|
|
item.setCommand(command);
|
|
|
|
|
item.reinitializeFromFile();
|
2015-09-04 08:47:27 +02:00
|
|
|
if (item.engineType() == NoEngineType)
|
|
|
|
|
continue;
|
2014-12-09 22:23:41 +02:00
|
|
|
//: %1: Debugger engine type (GDB, LLDB, CDB...), %2: Path
|
2022-07-05 15:37:08 +02:00
|
|
|
const QString name = detectionSource.isEmpty() ? Tr::tr("System %1 at %2") : Tr::tr("Detected %1 at %2");
|
2021-07-09 17:39:50 +02:00
|
|
|
item.setUnexpandedDisplayName(name.arg(item.engineTypeName()).arg(command.toUserOutput()));
|
2023-08-09 15:24:02 +02:00
|
|
|
addDebuggerItem(item);
|
2022-07-05 15:37:08 +02:00
|
|
|
logMessages.append(Tr::tr("Found: \"%1\"").arg(command.toUserOutput()));
|
2013-10-25 13:47:08 +02:00
|
|
|
}
|
2021-07-13 13:05:36 +02:00
|
|
|
if (logMessage)
|
|
|
|
|
*logMessage = logMessages.join('\n');
|
2013-10-25 13:47:08 +02:00
|
|
|
}
|
|
|
|
|
|
2023-08-09 15:24:02 +02:00
|
|
|
void DebuggerItemModel::autoDetectUvscDebuggers()
|
2020-01-24 17:08:27 +03:00
|
|
|
{
|
|
|
|
|
if (!HostOsInfo::isWindowsHost())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// Registry token for the "KEIL uVision" instance.
|
|
|
|
|
static const char kRegistryToken[] = "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\" \
|
|
|
|
|
"Windows\\CurrentVersion\\Uninstall\\Keil \u00B5Vision4";
|
|
|
|
|
|
|
|
|
|
QSettings registry(QLatin1String(kRegistryToken), QSettings::NativeFormat);
|
|
|
|
|
const auto productGroups = registry.childGroups();
|
|
|
|
|
for (const QString &productKey : productGroups) {
|
|
|
|
|
if (!productKey.startsWith("App"))
|
|
|
|
|
continue;
|
|
|
|
|
registry.beginGroup(productKey);
|
|
|
|
|
const QDir rootPath(registry.value("Directory").toString());
|
|
|
|
|
registry.endGroup();
|
|
|
|
|
const FilePath uVision = FilePath::fromString(
|
|
|
|
|
rootPath.absoluteFilePath("UV4/UV4.exe"));
|
|
|
|
|
if (!uVision.exists())
|
|
|
|
|
continue;
|
|
|
|
|
if (DebuggerItemManager::findByCommand(uVision))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
QString errorMsg;
|
|
|
|
|
const QString uVisionVersion = winGetDLLVersion(
|
|
|
|
|
WinDLLFileVersion, uVision.toString(), &errorMsg);
|
|
|
|
|
|
|
|
|
|
DebuggerItem item;
|
|
|
|
|
item.createId();
|
|
|
|
|
item.setAutoDetected(true);
|
|
|
|
|
item.setCommand(uVision);
|
|
|
|
|
item.setVersion(uVisionVersion);
|
|
|
|
|
item.setEngineType(UvscEngineType);
|
|
|
|
|
item.setUnexpandedDisplayName(
|
2022-07-05 15:37:08 +02:00
|
|
|
uniqueDisplayName(Tr::tr("Auto-detected uVision at %1")
|
2020-01-24 17:08:27 +03:00
|
|
|
.arg(uVision.toUserOutput())));
|
2023-08-09 15:24:02 +02:00
|
|
|
addDebuggerItem(item);
|
2020-01-24 17:08:27 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-09 15:24:02 +02:00
|
|
|
QString DebuggerItemModel::uniqueDisplayName(const QString &base)
|
2016-11-28 18:47:18 +01:00
|
|
|
{
|
|
|
|
|
const DebuggerItem *item = findDebugger([base](const DebuggerItem &item) {
|
|
|
|
|
return item.unexpandedDisplayName() == base;
|
|
|
|
|
});
|
|
|
|
|
return item ? uniqueDisplayName(base + " (1)") : base;
|
2013-10-25 13:47:08 +02:00
|
|
|
}
|
|
|
|
|
|
2023-08-09 15:24:02 +02:00
|
|
|
QVariant DebuggerItemModel::registerDebugger(const DebuggerItem &item)
|
2013-10-25 13:47:08 +02:00
|
|
|
{
|
2014-08-24 08:36:07 +03:00
|
|
|
// Try re-using existing item first.
|
2023-08-09 15:24:02 +02:00
|
|
|
DebuggerTreeItem *titem = findItemAtLevel<2>([item](DebuggerTreeItem *titem) {
|
2016-11-28 18:47:18 +01:00
|
|
|
const DebuggerItem &d = titem->m_item;
|
|
|
|
|
return d.command() == item.command()
|
2013-11-06 16:10:24 +01:00
|
|
|
&& d.isAutoDetected() == item.isAutoDetected()
|
|
|
|
|
&& d.engineType() == item.engineType()
|
2015-03-03 16:49:59 +01:00
|
|
|
&& d.unexpandedDisplayName() == item.unexpandedDisplayName()
|
2016-11-28 18:47:18 +01:00
|
|
|
&& d.abis() == item.abis();
|
|
|
|
|
});
|
|
|
|
|
if (titem)
|
|
|
|
|
return titem->m_item.id();
|
2013-10-25 13:47:08 +02:00
|
|
|
|
2014-12-19 10:46:40 +01:00
|
|
|
// If item already has an id, use it. Otherwise, create a new id.
|
2013-11-06 16:10:24 +01:00
|
|
|
DebuggerItem di = item;
|
2014-12-19 10:46:40 +01:00
|
|
|
if (!di.id().isValid())
|
|
|
|
|
di.createId();
|
2013-10-25 13:47:08 +02:00
|
|
|
|
2023-08-09 15:24:02 +02:00
|
|
|
addDebuggerItem(di);
|
2014-12-19 10:46:40 +01:00
|
|
|
return di.id();
|
2013-10-25 13:47:08 +02:00
|
|
|
}
|
|
|
|
|
|
2023-08-09 15:24:02 +02:00
|
|
|
void DebuggerItemModel::readDebuggers(const FilePath &fileName, bool isSystem)
|
2013-10-25 13:47:08 +02:00
|
|
|
{
|
2016-11-28 18:47:18 +01:00
|
|
|
PersistentSettingsReader reader;
|
|
|
|
|
if (!reader.load(fileName))
|
|
|
|
|
return;
|
2023-08-24 16:14:26 +02:00
|
|
|
Store data = reader.restoreValues();
|
2016-11-28 18:47:18 +01:00
|
|
|
|
|
|
|
|
// Check version
|
|
|
|
|
int version = data.value(DEBUGGER_FILE_VERSION_KEY, 0).toInt();
|
|
|
|
|
if (version < 1)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
int count = data.value(DEBUGGER_COUNT_KEY, 0).toInt();
|
|
|
|
|
for (int i = 0; i < count; ++i) {
|
2023-08-30 09:18:19 +02:00
|
|
|
const Key key = numberedKey(DEBUGGER_DATA_KEY, i);
|
2016-11-28 18:47:18 +01:00
|
|
|
if (!data.contains(key))
|
|
|
|
|
continue;
|
2023-08-28 10:55:31 +02:00
|
|
|
const Store dbMap = storeFromVariant(data.value(key));
|
2016-11-28 18:47:18 +01:00
|
|
|
DebuggerItem item(dbMap);
|
|
|
|
|
if (isSystem) {
|
|
|
|
|
item.setAutoDetected(true);
|
|
|
|
|
// SDK debuggers are always considered to be up-to-date, so no need to recheck them.
|
|
|
|
|
} else {
|
|
|
|
|
// User settings.
|
|
|
|
|
if (item.isAutoDetected()) {
|
|
|
|
|
if (!item.isValid() || item.engineType() == NoEngineType) {
|
|
|
|
|
qWarning() << QString("DebuggerItem \"%1\" (%2) read from \"%3\" dropped since it is not valid.")
|
|
|
|
|
.arg(item.command().toUserOutput(), item.id().toString(), fileName.toUserOutput());
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2022-04-07 14:04:20 +02:00
|
|
|
// FIXME: During startup, devices are not yet available, so we cannot check if the file still exists.
|
|
|
|
|
if (!item.command().needsDevice() && !item.command().isExecutableFile()) {
|
2016-11-28 18:47:18 +01:00
|
|
|
qWarning() << QString("DebuggerItem \"%1\" (%2) read from \"%3\" dropped since the command is not executable.")
|
|
|
|
|
.arg(item.command().toUserOutput(), item.id().toString(), fileName.toUserOutput());
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-10-25 13:47:08 +02:00
|
|
|
}
|
2016-11-28 18:47:18 +01:00
|
|
|
registerDebugger(item);
|
2013-10-25 13:47:08 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-09 15:24:02 +02:00
|
|
|
void DebuggerItemModel::restoreDebuggers()
|
2013-10-25 13:47:08 +02:00
|
|
|
{
|
2016-11-28 18:47:18 +01:00
|
|
|
// Read debuggers from SDK
|
2021-04-26 15:46:09 +02:00
|
|
|
readDebuggers(ICore::installerResourcePath(DEBUGGER_FILENAME), true);
|
2016-11-28 18:47:18 +01:00
|
|
|
|
|
|
|
|
// Read all debuggers from user file.
|
|
|
|
|
readDebuggers(userSettingsFileName(), false);
|
|
|
|
|
|
|
|
|
|
// Auto detect current.
|
2022-02-03 12:08:36 +01:00
|
|
|
IDevice::ConstPtr desktop = DeviceManager::defaultDesktopDevice();
|
|
|
|
|
QTC_ASSERT(desktop, return);
|
|
|
|
|
autoDetectGdbOrLldbDebuggers(desktop->systemEnvironment().path(), {});
|
2016-11-28 18:47:18 +01:00
|
|
|
autoDetectCdbDebuggers();
|
2020-01-24 17:08:27 +03:00
|
|
|
autoDetectUvscDebuggers();
|
2013-10-25 13:47:08 +02:00
|
|
|
}
|
|
|
|
|
|
2023-08-09 15:24:02 +02:00
|
|
|
void DebuggerItemModel::saveDebuggers()
|
2013-10-25 13:47:08 +02:00
|
|
|
{
|
2023-08-24 16:14:26 +02:00
|
|
|
Store data;
|
2016-11-28 18:47:18 +01:00
|
|
|
data.insert(DEBUGGER_FILE_VERSION_KEY, 1);
|
|
|
|
|
|
|
|
|
|
int count = 0;
|
|
|
|
|
forAllDebuggers([&count, &data](DebuggerItem &item) {
|
2023-03-29 08:52:37 +02:00
|
|
|
if (item.isGeneric()) // do not store generic debuggers, these get added automatically
|
|
|
|
|
return;
|
2016-11-28 18:47:18 +01:00
|
|
|
if (item.isValid() && item.engineType() != NoEngineType) {
|
2023-08-24 16:14:26 +02:00
|
|
|
Store tmp = item.toMap();
|
2016-11-28 18:47:18 +01:00
|
|
|
if (!tmp.isEmpty()) {
|
2023-08-30 09:18:19 +02:00
|
|
|
data.insert(numberedKey(DEBUGGER_DATA_KEY, count), variantFromStore(tmp));
|
2016-11-28 18:47:18 +01:00
|
|
|
++count;
|
|
|
|
|
}
|
2013-10-25 13:47:08 +02:00
|
|
|
}
|
2016-11-28 18:47:18 +01:00
|
|
|
});
|
|
|
|
|
data.insert(DEBUGGER_COUNT_KEY, count);
|
2020-06-02 09:10:40 +02:00
|
|
|
m_writer.save(data, ICore::dialogParent());
|
2014-12-19 10:46:40 +01:00
|
|
|
|
2016-11-28 18:47:18 +01:00
|
|
|
// Do not save default debuggers as they are set by the SDK.
|
2013-10-25 13:47:08 +02:00
|
|
|
}
|
|
|
|
|
|
2016-11-28 18:47:18 +01:00
|
|
|
} // namespace Internal
|
2017-10-24 13:10:16 +02:00
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
// DebuggerItemManager
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
|
2023-08-09 10:47:36 +02:00
|
|
|
void DebuggerItemManager::restoreDebuggers()
|
2022-09-22 15:24:52 +02:00
|
|
|
{
|
2023-08-09 15:24:02 +02:00
|
|
|
itemModel().restoreDebuggers();
|
2022-09-22 15:24:52 +02:00
|
|
|
}
|
|
|
|
|
|
2018-05-30 15:42:51 +02:00
|
|
|
const QList<DebuggerItem> DebuggerItemManager::debuggers()
|
2017-10-24 13:10:16 +02:00
|
|
|
{
|
|
|
|
|
QList<DebuggerItem> result;
|
|
|
|
|
forAllDebuggers([&result](const DebuggerItem &item) { result.append(item); });
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-28 13:49:26 +02:00
|
|
|
const DebuggerItem *DebuggerItemManager::findByCommand(const FilePath &command)
|
2017-10-24 13:10:16 +02:00
|
|
|
{
|
|
|
|
|
return findDebugger([command](const DebuggerItem &item) {
|
|
|
|
|
return item.command() == command;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const DebuggerItem *DebuggerItemManager::findById(const QVariant &id)
|
|
|
|
|
{
|
|
|
|
|
return findDebugger([id](const DebuggerItem &item) {
|
|
|
|
|
return item.id() == id;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const DebuggerItem *DebuggerItemManager::findByEngineType(DebuggerEngineType engineType)
|
|
|
|
|
{
|
|
|
|
|
return findDebugger([engineType](const DebuggerItem &item) {
|
|
|
|
|
return item.engineType() == engineType;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QVariant DebuggerItemManager::registerDebugger(const DebuggerItem &item)
|
|
|
|
|
{
|
2023-08-09 15:24:02 +02:00
|
|
|
return itemModel().registerDebugger(item);
|
2017-10-24 13:10:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DebuggerItemManager::deregisterDebugger(const QVariant &id)
|
|
|
|
|
{
|
2023-08-09 15:24:02 +02:00
|
|
|
itemModel().forItemsAtLevel<2>([id](DebuggerTreeItem *titem) {
|
2017-10-24 13:10:16 +02:00
|
|
|
if (titem->m_item.id() == id)
|
2023-08-09 15:24:02 +02:00
|
|
|
itemModel().destroyItem(titem);
|
2017-10-24 13:10:16 +02:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-03 10:21:57 +01:00
|
|
|
void DebuggerItemManager::autoDetectDebuggersForDevice(const FilePaths &searchPaths,
|
2021-07-13 13:05:36 +02:00
|
|
|
const QString &detectionSource,
|
|
|
|
|
QString *logMessage)
|
2021-06-21 10:27:02 +02:00
|
|
|
{
|
2023-08-09 15:24:02 +02:00
|
|
|
itemModel().autoDetectGdbOrLldbDebuggers(searchPaths, detectionSource, logMessage);
|
2021-07-13 13:05:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DebuggerItemManager::removeDetectedDebuggers(const QString &detectionSource,
|
|
|
|
|
QString *logMessage)
|
|
|
|
|
{
|
2022-07-05 15:37:08 +02:00
|
|
|
QStringList logMessages{Tr::tr("Removing debugger entries...")};
|
2022-01-27 12:26:29 +01:00
|
|
|
QList<DebuggerTreeItem *> toBeRemoved;
|
|
|
|
|
|
2023-08-09 15:24:02 +02:00
|
|
|
itemModel().forItemsAtLevel<2>([detectionSource, &toBeRemoved](DebuggerTreeItem *titem) {
|
2021-07-13 13:05:36 +02:00
|
|
|
if (titem->m_item.detectionSource() == detectionSource) {
|
2022-01-27 12:26:29 +01:00
|
|
|
toBeRemoved.append(titem);
|
2021-07-13 13:05:36 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// FIXME: These items appeared in early docker development. Ok to remove for Creator 7.0.
|
|
|
|
|
FilePath filePath = titem->m_item.command();
|
2022-01-27 12:26:29 +01:00
|
|
|
if (filePath.scheme() + ':' + filePath.host() == detectionSource)
|
|
|
|
|
toBeRemoved.append(titem);
|
2021-07-13 13:05:36 +02:00
|
|
|
});
|
2022-01-27 12:26:29 +01:00
|
|
|
for (DebuggerTreeItem *current : toBeRemoved) {
|
2022-07-05 15:37:08 +02:00
|
|
|
logMessages.append(Tr::tr("Removed \"%1\"").arg(current->m_item.displayName()));
|
2023-08-09 15:24:02 +02:00
|
|
|
itemModel().destroyItem(current);
|
2022-01-27 12:26:29 +01:00
|
|
|
}
|
|
|
|
|
|
2021-07-13 13:05:36 +02:00
|
|
|
if (logMessage)
|
|
|
|
|
*logMessage = logMessages.join('\n');
|
2021-06-21 10:27:02 +02:00
|
|
|
}
|
|
|
|
|
|
2021-07-15 12:25:23 +02:00
|
|
|
void DebuggerItemManager::listDetectedDebuggers(const QString &detectionSource, QString *logMessage)
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(logMessage, return);
|
2022-07-05 15:37:08 +02:00
|
|
|
QStringList logMessages{Tr::tr("Debuggers:")};
|
2023-08-09 15:24:02 +02:00
|
|
|
itemModel().forItemsAtLevel<2>([detectionSource, &logMessages](DebuggerTreeItem *titem) {
|
2021-07-15 12:25:23 +02:00
|
|
|
if (titem->m_item.detectionSource() == detectionSource)
|
|
|
|
|
logMessages.append(titem->m_item.displayName());
|
|
|
|
|
});
|
|
|
|
|
*logMessage = logMessages.join('\n');
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-09 09:26:57 +02:00
|
|
|
|
|
|
|
|
// DebuggerSettingsPageWidget
|
|
|
|
|
|
|
|
|
|
class DebuggerSettingsPageWidget : public IOptionsPageWidget
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
DebuggerSettingsPageWidget()
|
|
|
|
|
{
|
|
|
|
|
m_addButton = new QPushButton(Tr::tr("Add"), this);
|
|
|
|
|
|
|
|
|
|
m_cloneButton = new QPushButton(Tr::tr("Clone"), this);
|
|
|
|
|
m_cloneButton->setEnabled(false);
|
|
|
|
|
|
|
|
|
|
m_delButton = new QPushButton(this);
|
|
|
|
|
m_delButton->setEnabled(false);
|
|
|
|
|
|
|
|
|
|
m_container = new DetailsWidget(this);
|
|
|
|
|
m_container->setState(DetailsWidget::NoSummary);
|
|
|
|
|
m_container->setVisible(false);
|
|
|
|
|
|
|
|
|
|
m_debuggerView = new QTreeView(this);
|
2023-08-09 15:24:02 +02:00
|
|
|
m_debuggerView->setModel(&itemModel());
|
2023-08-09 09:26:57 +02:00
|
|
|
m_debuggerView->setUniformRowHeights(true);
|
|
|
|
|
m_debuggerView->setSelectionMode(QAbstractItemView::SingleSelection);
|
|
|
|
|
m_debuggerView->setSelectionBehavior(QAbstractItemView::SelectRows);
|
|
|
|
|
m_debuggerView->expandAll();
|
|
|
|
|
|
|
|
|
|
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, &DebuggerSettingsPageWidget::currentDebuggerChanged, Qt::QueuedConnection);
|
|
|
|
|
|
|
|
|
|
connect(m_addButton, &QAbstractButton::clicked,
|
|
|
|
|
this, &DebuggerSettingsPageWidget::addDebugger, Qt::QueuedConnection);
|
|
|
|
|
connect(m_cloneButton, &QAbstractButton::clicked,
|
|
|
|
|
this, &DebuggerSettingsPageWidget::cloneDebugger, Qt::QueuedConnection);
|
|
|
|
|
connect(m_delButton, &QAbstractButton::clicked,
|
|
|
|
|
this, &DebuggerSettingsPageWidget::removeDebugger, Qt::QueuedConnection);
|
|
|
|
|
|
|
|
|
|
m_itemConfigWidget = new DebuggerItemConfigWidget;
|
|
|
|
|
m_container->setWidget(m_itemConfigWidget);
|
|
|
|
|
updateButtons();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void apply() final
|
|
|
|
|
{
|
|
|
|
|
m_itemConfigWidget->store();
|
2023-08-09 15:24:02 +02:00
|
|
|
itemModel().apply();
|
2023-08-09 09:26:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void finish() final
|
|
|
|
|
{
|
2023-08-09 15:24:02 +02:00
|
|
|
itemModel().cancel();
|
2023-08-09 09:26:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void cloneDebugger();
|
|
|
|
|
void addDebugger();
|
|
|
|
|
void removeDebugger();
|
|
|
|
|
void currentDebuggerChanged(const QModelIndex &newCurrent);
|
|
|
|
|
void updateButtons();
|
|
|
|
|
|
|
|
|
|
QTreeView *m_debuggerView;
|
|
|
|
|
QPushButton *m_addButton;
|
|
|
|
|
QPushButton *m_cloneButton;
|
|
|
|
|
QPushButton *m_delButton;
|
|
|
|
|
DetailsWidget *m_container;
|
|
|
|
|
DebuggerItemConfigWidget *m_itemConfigWidget;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
void DebuggerSettingsPageWidget::cloneDebugger()
|
|
|
|
|
{
|
2023-08-09 15:24:02 +02:00
|
|
|
DebuggerTreeItem *treeItem = itemModel().currentTreeItem();
|
2023-08-09 09:26:57 +02:00
|
|
|
if (!treeItem)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
DebuggerItem *item = &treeItem->m_item;
|
|
|
|
|
DebuggerItem newItem;
|
|
|
|
|
newItem.createId();
|
|
|
|
|
newItem.setCommand(item->command());
|
2023-08-09 15:24:02 +02:00
|
|
|
newItem.setUnexpandedDisplayName(itemModel().uniqueDisplayName(Tr::tr("Clone of %1").arg(item->displayName())));
|
2023-08-09 09:26:57 +02:00
|
|
|
newItem.reinitializeFromFile();
|
|
|
|
|
newItem.setAutoDetected(false);
|
|
|
|
|
newItem.setGeneric(item->isGeneric());
|
|
|
|
|
newItem.setEngineType(item->engineType());
|
2023-08-09 15:24:02 +02:00
|
|
|
auto addedItem = itemModel().addDebuggerItem(newItem, true);
|
|
|
|
|
m_debuggerView->setCurrentIndex(itemModel().indexForItem(addedItem));
|
2023-08-09 09:26:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DebuggerSettingsPageWidget::addDebugger()
|
|
|
|
|
{
|
|
|
|
|
DebuggerItem item;
|
|
|
|
|
item.createId();
|
|
|
|
|
item.setEngineType(NoEngineType);
|
2023-08-09 15:24:02 +02:00
|
|
|
item.setUnexpandedDisplayName(itemModel().uniqueDisplayName(Tr::tr("New Debugger")));
|
2023-08-09 09:26:57 +02:00
|
|
|
item.setAutoDetected(false);
|
2023-08-09 15:24:02 +02:00
|
|
|
auto addedItem = itemModel().addDebuggerItem(item, true);
|
|
|
|
|
m_debuggerView->setCurrentIndex(itemModel().indexForItem(addedItem));
|
2023-08-09 09:26:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DebuggerSettingsPageWidget::removeDebugger()
|
|
|
|
|
{
|
2023-08-09 15:24:02 +02:00
|
|
|
DebuggerTreeItem *treeItem = itemModel().currentTreeItem();
|
2023-08-09 09:26:57 +02:00
|
|
|
QTC_ASSERT(treeItem, return);
|
|
|
|
|
treeItem->m_removed = !treeItem->m_removed;
|
|
|
|
|
treeItem->update();
|
|
|
|
|
updateButtons();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DebuggerSettingsPageWidget::currentDebuggerChanged(const QModelIndex &newCurrent)
|
|
|
|
|
{
|
2023-08-09 15:24:02 +02:00
|
|
|
itemModel().setCurrentIndex(newCurrent);
|
2023-08-09 09:26:57 +02:00
|
|
|
updateButtons();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DebuggerSettingsPageWidget::updateButtons()
|
|
|
|
|
{
|
2023-08-09 15:24:02 +02:00
|
|
|
DebuggerTreeItem *titem = itemModel().currentTreeItem();
|
2023-08-09 09:26:57 +02:00
|
|
|
DebuggerItem *item = titem ? &titem->m_item : nullptr;
|
|
|
|
|
|
|
|
|
|
m_itemConfigWidget->load(item);
|
|
|
|
|
m_container->setVisible(item != nullptr);
|
|
|
|
|
m_cloneButton->setEnabled(item && item->isValid() && item->canClone());
|
|
|
|
|
m_delButton->setEnabled(item && !item->isAutoDetected());
|
|
|
|
|
m_delButton->setText(item && titem->m_removed ? Tr::tr("Restore") : Tr::tr("Remove"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// DebuggerSettingsPage
|
|
|
|
|
|
|
|
|
|
class DebuggerSettingsPage : public Core::IOptionsPage
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
DebuggerSettingsPage() {
|
|
|
|
|
setId(ProjectExplorer::Constants::DEBUGGER_SETTINGS_PAGE_ID);
|
|
|
|
|
setDisplayName(Tr::tr("Debuggers"));
|
|
|
|
|
setCategory(ProjectExplorer::Constants::KITS_SETTINGS_CATEGORY);
|
|
|
|
|
setWidgetCreator([] { return new DebuggerSettingsPageWidget; });
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const DebuggerSettingsPage settingsPage;
|
|
|
|
|
|
2016-11-28 18:47:18 +01:00
|
|
|
} // namespace Debugger
|