2022-08-19 15:59:36 +02:00
|
|
|
// Copyright (C) 2019 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
|
2019-02-19 12:56:28 +01:00
|
|
|
|
|
|
|
|
#include "addrunconfigdialog.h"
|
|
|
|
|
|
|
|
|
|
#include "project.h"
|
2023-01-13 12:38:22 +01:00
|
|
|
#include "projectexplorertr.h"
|
2019-02-19 12:56:28 +01:00
|
|
|
#include "target.h"
|
|
|
|
|
|
|
|
|
|
#include <utils/itemviews.h>
|
2020-12-06 21:37:58 +01:00
|
|
|
#include <utils/fancylineedit.h>
|
2019-02-19 12:56:28 +01:00
|
|
|
#include <utils/qtcassert.h>
|
|
|
|
|
#include <utils/treemodel.h>
|
|
|
|
|
|
|
|
|
|
#include <QDialogButtonBox>
|
|
|
|
|
#include <QHeaderView>
|
|
|
|
|
#include <QItemSelectionModel>
|
|
|
|
|
#include <QPushButton>
|
|
|
|
|
#include <QSortFilterProxyModel>
|
|
|
|
|
#include <QVBoxLayout>
|
|
|
|
|
|
|
|
|
|
using namespace Utils;
|
|
|
|
|
|
|
|
|
|
namespace ProjectExplorer {
|
|
|
|
|
namespace Internal {
|
|
|
|
|
|
|
|
|
|
const Qt::ItemDataRole IsCustomRole = Qt::UserRole;
|
|
|
|
|
|
|
|
|
|
class CandidateTreeItem : public TreeItem
|
|
|
|
|
{
|
|
|
|
|
public:
|
2021-09-17 12:53:21 +02:00
|
|
|
CandidateTreeItem(const RunConfigurationCreationInfo &rci, const Target *target)
|
|
|
|
|
: m_creationInfo(rci), m_projectRoot(target->project()->projectDirectory()),
|
|
|
|
|
m_displayName(target->macroExpander()->expand(rci.displayName))
|
2019-02-19 12:56:28 +01:00
|
|
|
{ }
|
|
|
|
|
|
|
|
|
|
RunConfigurationCreationInfo creationInfo() const { return m_creationInfo; }
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
QVariant data(int column, int role) const override
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(column < 2, return QVariant());
|
|
|
|
|
if (role == IsCustomRole)
|
|
|
|
|
return m_creationInfo.projectFilePath.isEmpty();
|
|
|
|
|
if (column == 0 && role == Qt::DisplayRole)
|
2021-09-17 12:53:21 +02:00
|
|
|
return m_displayName;
|
2019-02-19 12:56:28 +01:00
|
|
|
if (column == 1 && role == Qt::DisplayRole) {
|
2019-05-28 13:49:26 +02:00
|
|
|
FilePath displayPath = m_creationInfo.projectFilePath.relativeChildPath(m_projectRoot);
|
2019-02-19 12:56:28 +01:00
|
|
|
if (displayPath.isEmpty()) {
|
|
|
|
|
displayPath = m_creationInfo.projectFilePath;
|
|
|
|
|
QTC_CHECK(displayPath.isEmpty());
|
|
|
|
|
}
|
2023-01-13 12:38:22 +01:00
|
|
|
return displayPath.isEmpty() ? Tr::tr("[none]") : displayPath.toUserOutput();
|
2019-02-19 12:56:28 +01:00
|
|
|
}
|
2023-08-01 20:52:58 +02:00
|
|
|
return {};
|
2019-02-19 12:56:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const RunConfigurationCreationInfo m_creationInfo;
|
2019-05-28 13:49:26 +02:00
|
|
|
const FilePath m_projectRoot;
|
2021-09-17 12:53:21 +02:00
|
|
|
const QString m_displayName;
|
2019-02-19 12:56:28 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class CandidatesModel : public TreeModel<TreeItem, CandidateTreeItem>
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
CandidatesModel(Target *target, QObject *parent) : TreeModel(parent)
|
|
|
|
|
{
|
2023-01-13 12:38:22 +01:00
|
|
|
setHeader({Tr::tr("Name"), Tr::tr("Source")});
|
2019-02-19 12:56:28 +01:00
|
|
|
for (const RunConfigurationCreationInfo &rci
|
|
|
|
|
: RunConfigurationFactory::creatorsForTarget(target)) {
|
2021-09-17 12:53:21 +02:00
|
|
|
rootItem()->appendChild(new CandidateTreeItem(rci, target));
|
2019-02-19 12:56:28 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class ProxyModel : public QSortFilterProxyModel
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
ProxyModel(QObject *parent) : QSortFilterProxyModel(parent) { }
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const override
|
|
|
|
|
{
|
|
|
|
|
if (source_left.column() == 0) {
|
|
|
|
|
// Let's put the fallback candidates last.
|
|
|
|
|
const bool leftIsCustom = sourceModel()->data(source_left, IsCustomRole).toBool();
|
|
|
|
|
const bool rightIsCustom = sourceModel()->data(source_right, IsCustomRole).toBool();
|
|
|
|
|
if (leftIsCustom != rightIsCustom)
|
|
|
|
|
return rightIsCustom;
|
|
|
|
|
}
|
|
|
|
|
return QSortFilterProxyModel::lessThan(source_left, source_right);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class CandidatesTreeView : public TreeView
|
|
|
|
|
{
|
|
|
|
|
public:
|
2023-08-22 09:22:46 +02:00
|
|
|
CandidatesTreeView(QWidget *parent) : TreeView(parent) {}
|
2019-02-19 12:56:28 +01:00
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
QSize sizeHint() const override
|
|
|
|
|
{
|
|
|
|
|
const int width = columnWidth(0) + columnWidth(1);
|
|
|
|
|
const int height = qMin(model()->rowCount() + 10, 10) * rowHeight(model()->index(0, 0))
|
|
|
|
|
+ header()->sizeHint().height();
|
|
|
|
|
return {width, height};
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
AddRunConfigDialog::AddRunConfigDialog(Target *target, QWidget *parent)
|
|
|
|
|
: QDialog(parent), m_view(new CandidatesTreeView(this))
|
|
|
|
|
{
|
2023-01-13 12:38:22 +01:00
|
|
|
setWindowTitle(Tr::tr("Create Run Configuration"));
|
2019-02-19 12:56:28 +01:00
|
|
|
const auto model = new CandidatesModel(target, this);
|
|
|
|
|
const auto proxyModel = new ProxyModel(this);
|
|
|
|
|
proxyModel->setSourceModel(model);
|
2020-12-06 21:37:58 +01:00
|
|
|
const auto filterEdit = new FancyLineEdit(this);
|
2023-03-07 15:22:04 +01:00
|
|
|
filterEdit->setFocus();
|
2020-12-06 21:37:58 +01:00
|
|
|
filterEdit->setFiltering(true);
|
2023-01-13 12:38:22 +01:00
|
|
|
filterEdit->setPlaceholderText(Tr::tr("Filter candidates by name"));
|
2019-02-19 12:56:28 +01:00
|
|
|
m_view->setSelectionMode(TreeView::SingleSelection);
|
|
|
|
|
m_view->setSelectionBehavior(TreeView::SelectRows);
|
|
|
|
|
m_view->setSortingEnabled(true);
|
|
|
|
|
m_view->setModel(proxyModel);
|
|
|
|
|
m_view->resizeColumnToContents(0);
|
|
|
|
|
m_view->resizeColumnToContents(1);
|
|
|
|
|
m_view->sortByColumn(0, Qt::AscendingOrder);
|
|
|
|
|
const auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
2023-01-13 12:38:22 +01:00
|
|
|
buttonBox->button(QDialogButtonBox::Ok)->setText(Tr::tr("Create"));
|
2019-02-19 12:56:28 +01:00
|
|
|
|
2020-12-06 21:37:58 +01:00
|
|
|
connect(filterEdit, &FancyLineEdit::textChanged, this, [proxyModel](const QString &text) {
|
2020-03-27 11:56:25 +01:00
|
|
|
proxyModel->setFilterRegularExpression(QRegularExpression(text, QRegularExpression::CaseInsensitiveOption));
|
2019-02-19 12:56:28 +01:00
|
|
|
});
|
|
|
|
|
connect(m_view, &TreeView::doubleClicked, this, [this] { accept(); });
|
|
|
|
|
const auto updateOkButton = [buttonBox, this] {
|
|
|
|
|
buttonBox->button(QDialogButtonBox::Ok)
|
|
|
|
|
->setEnabled(m_view->selectionModel()->hasSelection());
|
|
|
|
|
};
|
|
|
|
|
connect(m_view->selectionModel(), &QItemSelectionModel::selectionChanged, this, updateOkButton);
|
|
|
|
|
updateOkButton();
|
|
|
|
|
connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
|
|
|
|
connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
|
|
|
|
|
|
|
|
|
const auto layout = new QVBoxLayout(this);
|
2020-12-06 21:37:58 +01:00
|
|
|
layout->addWidget(filterEdit);
|
2019-02-19 12:56:28 +01:00
|
|
|
layout->addWidget(m_view);
|
|
|
|
|
layout->addWidget(buttonBox);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AddRunConfigDialog::accept()
|
|
|
|
|
{
|
|
|
|
|
const QModelIndexList selected = m_view->selectionModel()->selectedRows();
|
|
|
|
|
QTC_ASSERT(selected.count() == 1, return);
|
|
|
|
|
const auto * const proxyModel = static_cast<ProxyModel *>(m_view->model());
|
|
|
|
|
const auto * const model = static_cast<CandidatesModel *>(proxyModel->sourceModel());
|
|
|
|
|
const TreeItem * const item = model->itemForIndex(proxyModel->mapToSource(selected.first()));
|
|
|
|
|
QTC_ASSERT(item, return);
|
|
|
|
|
m_creationInfo = static_cast<const CandidateTreeItem *>(item)->creationInfo();
|
2020-06-12 07:50:52 +02:00
|
|
|
QTC_ASSERT(m_creationInfo.factory, return);
|
2019-02-19 12:56:28 +01:00
|
|
|
QDialog::accept();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace Internal
|
|
|
|
|
} // namespace ProjectExplorer
|