2022-12-12 16:45:31 +01:00
|
|
|
// Copyright (C) 2022 The Qt Company Ltd.
|
2023-05-24 10:27:35 +02:00
|
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
2022-11-28 09:48:11 +01:00
|
|
|
|
|
|
|
#include "axivionsettings.h"
|
|
|
|
|
2023-05-24 16:03:13 +02:00
|
|
|
#include "axiviontr.h"
|
|
|
|
|
2023-07-14 16:59:32 +02:00
|
|
|
#include <coreplugin/dialogs/ioptionspage.h>
|
2023-07-06 09:57:16 +02:00
|
|
|
#include <coreplugin/icore.h>
|
|
|
|
|
2024-05-22 12:46:05 +02:00
|
|
|
#include <utils/algorithm.h>
|
2023-07-14 16:59:32 +02:00
|
|
|
#include <utils/id.h>
|
|
|
|
#include <utils/layoutbuilder.h>
|
2024-02-19 22:16:36 +01:00
|
|
|
#include <utils/stringutils.h>
|
2024-09-18 13:16:05 +02:00
|
|
|
#include <utils/utilsicons.h>
|
2022-12-12 16:45:31 +01:00
|
|
|
|
2024-05-22 13:39:12 +02:00
|
|
|
#include <QComboBox>
|
2023-07-14 16:59:32 +02:00
|
|
|
#include <QDialog>
|
|
|
|
#include <QDialogButtonBox>
|
2024-05-22 12:46:05 +02:00
|
|
|
#include <QJsonArray>
|
2022-12-12 16:45:31 +01:00
|
|
|
#include <QJsonDocument>
|
|
|
|
#include <QJsonObject>
|
2024-05-22 15:50:22 +02:00
|
|
|
#include <QMessageBox>
|
2023-07-14 16:59:32 +02:00
|
|
|
#include <QPushButton>
|
|
|
|
#include <QRegularExpression>
|
2024-09-18 13:16:05 +02:00
|
|
|
#include <QTreeWidget>
|
2023-07-14 16:59:32 +02:00
|
|
|
#include <QVBoxLayout>
|
|
|
|
|
2024-02-11 12:53:52 +01:00
|
|
|
using namespace Core;
|
2023-07-14 16:59:32 +02:00
|
|
|
using namespace Utils;
|
2022-11-28 09:48:11 +01:00
|
|
|
|
|
|
|
namespace Axivion::Internal {
|
|
|
|
|
2022-12-12 16:45:31 +01:00
|
|
|
bool AxivionServer::operator==(const AxivionServer &other) const
|
|
|
|
{
|
2024-02-16 13:27:36 +01:00
|
|
|
return id == other.id && dashboard == other.dashboard && username == other.username;
|
2022-12-12 16:45:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool AxivionServer::operator!=(const AxivionServer &other) const
|
|
|
|
{
|
|
|
|
return !(*this == other);
|
|
|
|
}
|
|
|
|
|
|
|
|
QJsonObject AxivionServer::toJson() const
|
|
|
|
{
|
|
|
|
QJsonObject result;
|
|
|
|
result.insert("id", id.toString());
|
|
|
|
result.insert("dashboard", dashboard);
|
2024-02-11 12:44:09 +01:00
|
|
|
result.insert("username", username);
|
2022-12-12 16:45:31 +01:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2024-02-19 22:16:36 +01:00
|
|
|
static QString fixUrl(const QString &url)
|
|
|
|
{
|
|
|
|
const QString trimmed = Utils::trimBack(url, ' ');
|
|
|
|
return trimmed.endsWith('/') ? trimmed : trimmed + '/';
|
|
|
|
}
|
|
|
|
|
2022-12-12 16:45:31 +01:00
|
|
|
AxivionServer AxivionServer::fromJson(const QJsonObject &json)
|
|
|
|
{
|
|
|
|
const AxivionServer invalidServer;
|
|
|
|
const QJsonValue id = json.value("id");
|
|
|
|
if (id == QJsonValue::Undefined)
|
|
|
|
return invalidServer;
|
|
|
|
const QJsonValue dashboard = json.value("dashboard");
|
|
|
|
if (dashboard == QJsonValue::Undefined)
|
|
|
|
return invalidServer;
|
2024-02-11 12:44:09 +01:00
|
|
|
const QJsonValue username = json.value("username");
|
|
|
|
if (username == QJsonValue::Undefined)
|
|
|
|
return invalidServer;
|
2024-02-19 22:16:36 +01:00
|
|
|
return {Id::fromString(id.toString()), fixUrl(dashboard.toString()), username.toString()};
|
2022-12-12 16:45:31 +01:00
|
|
|
}
|
|
|
|
|
2024-09-18 13:16:05 +02:00
|
|
|
bool PathMapping::operator==(const PathMapping &other) const
|
|
|
|
{
|
|
|
|
return projectName == other.projectName && analysisPath == other.analysisPath
|
|
|
|
&& localPath == other.localPath;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool PathMapping::operator!=(const PathMapping &other) const
|
|
|
|
{
|
|
|
|
return !(*this == other);
|
|
|
|
}
|
|
|
|
|
2024-09-18 09:20:57 +02:00
|
|
|
static FilePath axivionJsonFilePath()
|
2022-12-12 16:45:31 +01:00
|
|
|
{
|
2024-02-11 12:53:52 +01:00
|
|
|
return FilePath::fromString(ICore::settings()->fileName()).parentDir()
|
2022-12-12 16:45:31 +01:00
|
|
|
.pathAppended("qtcreator/axivion.json");
|
|
|
|
}
|
|
|
|
|
2024-09-18 09:20:57 +02:00
|
|
|
static void writeAxivionJson(const FilePath &filePath, const QList<AxivionServer> &servers)
|
2022-12-12 16:45:31 +01:00
|
|
|
{
|
|
|
|
QJsonDocument doc;
|
2024-05-22 12:46:05 +02:00
|
|
|
QJsonArray serverArray;
|
|
|
|
for (const AxivionServer &server : servers)
|
|
|
|
serverArray.append(server.toJson());
|
|
|
|
doc.setArray(serverArray);
|
2022-12-12 16:45:31 +01:00
|
|
|
// FIXME error handling?
|
|
|
|
filePath.writeFileContents(doc.toJson());
|
|
|
|
filePath.setPermissions(QFile::ReadUser | QFile::WriteUser);
|
|
|
|
}
|
|
|
|
|
2024-09-18 09:20:57 +02:00
|
|
|
static QList<AxivionServer> readAxivionJson(const FilePath &filePath)
|
2022-12-12 16:45:31 +01:00
|
|
|
{
|
|
|
|
if (!filePath.exists())
|
|
|
|
return {};
|
2024-02-11 12:53:52 +01:00
|
|
|
expected_str<QByteArray> contents = filePath.fileContents();
|
2022-12-12 16:45:31 +01:00
|
|
|
if (!contents)
|
|
|
|
return {};
|
|
|
|
const QJsonDocument doc = QJsonDocument::fromJson(*contents);
|
2024-05-22 12:46:05 +02:00
|
|
|
if (doc.isObject()) // old approach
|
|
|
|
return { AxivionServer::fromJson(doc.object()) };
|
|
|
|
if (!doc.isArray())
|
2022-12-12 16:45:31 +01:00
|
|
|
return {};
|
2024-05-22 12:46:05 +02:00
|
|
|
|
|
|
|
QList<AxivionServer> result;
|
|
|
|
const QJsonArray serverArray = doc.array();
|
|
|
|
for (const auto &serverValue : serverArray) {
|
|
|
|
if (!serverValue.isObject())
|
|
|
|
continue;
|
|
|
|
result.append(AxivionServer::fromJson(serverValue.toObject()));
|
|
|
|
}
|
|
|
|
return result;
|
2022-12-12 16:45:31 +01:00
|
|
|
}
|
|
|
|
|
2024-09-18 13:16:05 +02:00
|
|
|
static QVariant pathMappingToVariant(const PathMapping &pm)
|
|
|
|
{
|
|
|
|
QVariantMap m;
|
|
|
|
m.insert("ProjectName", pm.projectName);
|
|
|
|
m.insert("AnalysisPath", pm.analysisPath.toSettings());
|
|
|
|
m.insert("LocalPath", pm.localPath.toSettings());
|
|
|
|
return m;
|
|
|
|
}
|
|
|
|
|
|
|
|
static QVariant pathMappingsToSetting(const QList<PathMapping> &mappings)
|
|
|
|
{
|
|
|
|
return Utils::transform(mappings,
|
|
|
|
[](const PathMapping &m) { return pathMappingToVariant(m); });
|
|
|
|
}
|
|
|
|
|
|
|
|
static PathMapping pathMappingFromVariant(const QVariant &m)
|
|
|
|
{
|
|
|
|
const QVariantMap map = m.toMap();
|
|
|
|
return map.isEmpty() ? PathMapping{}
|
|
|
|
: PathMapping{map.value("ProjectName").toString(),
|
|
|
|
FilePath::fromSettings(map.value("AnalysisPath")),
|
|
|
|
FilePath::fromSettings(map.value("LocalPath"))};
|
|
|
|
}
|
|
|
|
|
|
|
|
static QList<PathMapping> pathMappingsFromSetting(const QVariant &value)
|
|
|
|
{
|
|
|
|
return Utils::transform(
|
|
|
|
Utils::filtered(value.toList(), &QVariant::isValid), &pathMappingFromVariant);
|
|
|
|
}
|
|
|
|
|
|
|
|
// AxivionSettings
|
|
|
|
|
|
|
|
class PathMappingSettings final : public BaseAspect
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
PathMappingSettings()
|
|
|
|
{
|
|
|
|
setSettingsKey("Axivion/PathMappings");
|
|
|
|
}
|
|
|
|
|
|
|
|
void setVariantValue(const QVariant &value, Announcement howToAnnounce = DoEmit) final
|
|
|
|
{
|
|
|
|
m_pathMapping = pathMappingsFromSetting(value);
|
|
|
|
if (howToAnnounce == DoEmit)
|
|
|
|
emit changed();
|
|
|
|
}
|
|
|
|
|
|
|
|
QVariant variantValue() const final { return pathMappingsToSetting(m_pathMapping); }
|
2024-09-23 13:34:05 +02:00
|
|
|
|
|
|
|
const QList<PathMapping> validPathMappings() const
|
|
|
|
{
|
|
|
|
return Utils::filtered(m_pathMapping, &PathMapping::isValid);
|
|
|
|
}
|
2024-09-18 13:16:05 +02:00
|
|
|
|
|
|
|
private:
|
|
|
|
QList<PathMapping> m_pathMapping;
|
|
|
|
};
|
|
|
|
|
|
|
|
PathMappingSettings &pathMappingSettings()
|
|
|
|
{
|
|
|
|
static PathMappingSettings thePathMapping;
|
|
|
|
return thePathMapping;
|
|
|
|
}
|
2023-07-14 16:59:32 +02:00
|
|
|
|
|
|
|
AxivionSettings &settings()
|
2022-11-28 09:48:11 +01:00
|
|
|
{
|
2023-07-14 16:59:32 +02:00
|
|
|
static AxivionSettings theSettings;
|
|
|
|
return theSettings;
|
2022-11-28 09:48:11 +01:00
|
|
|
}
|
|
|
|
|
2023-07-14 16:59:32 +02:00
|
|
|
AxivionSettings::AxivionSettings()
|
2022-11-28 09:48:11 +01:00
|
|
|
{
|
2023-07-14 16:59:32 +02:00
|
|
|
setSettingsGroup("Axivion");
|
|
|
|
|
2024-02-13 14:20:23 +01:00
|
|
|
highlightMarks.setSettingsKey("HighlightMarks");
|
|
|
|
highlightMarks.setLabelText(Tr::tr("Highlight marks"));
|
2024-02-26 11:53:09 +01:00
|
|
|
highlightMarks.setToolTip(Tr::tr("Marks issues on the scroll bar."));
|
2024-02-13 14:20:23 +01:00
|
|
|
highlightMarks.setDefaultValue(false);
|
2024-05-22 15:50:22 +02:00
|
|
|
m_defaultServerId.setSettingsKey("DefaultDashboardId");
|
2024-09-18 13:16:05 +02:00
|
|
|
pathMappingSettings().readSettings();
|
2023-07-14 16:59:32 +02:00
|
|
|
AspectContainer::readSettings();
|
|
|
|
|
2024-09-18 09:20:57 +02:00
|
|
|
m_allServers = readAxivionJson(axivionJsonFilePath());
|
2024-05-22 12:46:05 +02:00
|
|
|
|
2024-05-22 15:50:22 +02:00
|
|
|
if (m_allServers.size() == 1 && m_defaultServerId().isEmpty()) // handle settings transition
|
|
|
|
m_defaultServerId.setValue(m_allServers.first().id.toString());
|
2023-07-14 16:59:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void AxivionSettings::toSettings() const
|
|
|
|
{
|
2024-09-18 09:20:57 +02:00
|
|
|
writeAxivionJson(axivionJsonFilePath(), m_allServers);
|
2024-02-11 12:53:52 +01:00
|
|
|
AspectContainer::writeSettings();
|
2023-07-14 16:59:32 +02:00
|
|
|
}
|
|
|
|
|
2024-05-22 10:55:09 +02:00
|
|
|
Id AxivionSettings::defaultDashboardId() const
|
|
|
|
{
|
2024-05-22 15:50:22 +02:00
|
|
|
return Id::fromString(m_defaultServerId());
|
2024-05-22 10:55:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
const AxivionServer AxivionSettings::defaultServer() const
|
|
|
|
{
|
|
|
|
return serverForId(defaultDashboardId());
|
|
|
|
}
|
|
|
|
|
|
|
|
const AxivionServer AxivionSettings::serverForId(const Utils::Id &id) const
|
|
|
|
{
|
2024-05-22 12:46:05 +02:00
|
|
|
return Utils::findOrDefault(m_allServers, [&id](const AxivionServer &server) {
|
|
|
|
return id == server.id;
|
|
|
|
});
|
2024-05-22 10:55:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void AxivionSettings::disableCertificateValidation(const Utils::Id &id)
|
|
|
|
{
|
2024-05-22 12:46:05 +02:00
|
|
|
const int index = Utils::indexOf(m_allServers, [&id](const AxivionServer &server) {
|
|
|
|
return id == server.id;
|
|
|
|
});
|
|
|
|
if (index == -1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
m_allServers[index].validateCert = false;
|
2024-05-22 10:55:09 +02:00
|
|
|
}
|
|
|
|
|
2024-09-25 14:57:09 +02:00
|
|
|
bool AxivionSettings::updateDashboardServers(const QList<AxivionServer> &other,
|
|
|
|
const Utils::Id &selected)
|
2024-05-22 10:55:09 +02:00
|
|
|
{
|
2024-05-22 15:50:22 +02:00
|
|
|
const Id oldDefault = defaultDashboardId();
|
2024-09-25 14:57:09 +02:00
|
|
|
if (selected == oldDefault && m_allServers == other)
|
|
|
|
return false;
|
2024-05-22 15:50:22 +02:00
|
|
|
|
2024-09-25 14:57:09 +02:00
|
|
|
m_defaultServerId.setValue(selected.toString());
|
2024-05-22 13:39:12 +02:00
|
|
|
m_allServers = other;
|
|
|
|
emit changed(); // should we be more detailed? (id)
|
2024-09-25 14:57:09 +02:00
|
|
|
return true;
|
2024-05-22 10:55:09 +02:00
|
|
|
}
|
|
|
|
|
2024-09-23 13:34:05 +02:00
|
|
|
const QList<PathMapping> AxivionSettings::validPathMappings() const
|
2024-09-18 13:16:05 +02:00
|
|
|
{
|
2024-09-23 13:34:05 +02:00
|
|
|
return pathMappingSettings().validPathMappings();
|
2024-09-18 13:16:05 +02:00
|
|
|
}
|
|
|
|
|
2023-07-14 16:59:32 +02:00
|
|
|
// AxivionSettingsPage
|
|
|
|
|
|
|
|
// may allow some invalid, but does some minimal check for legality
|
|
|
|
static bool hostValid(const QString &host)
|
|
|
|
{
|
|
|
|
static const QRegularExpression ip(R"(^(\d+).(\d+).(\d+).(\d+)$)");
|
2024-09-30 13:10:06 +02:00
|
|
|
static const QRegularExpression dn(R"(^([a-zA-Z0-9][a-zA-Z0-9-]+\.)*[a-zA-Z0-9][a-zA-Z0-9-]+$)");
|
2023-07-14 16:59:32 +02:00
|
|
|
const QRegularExpressionMatch match = ip.match(host);
|
|
|
|
if (match.hasMatch()) {
|
|
|
|
for (int i = 1; i < 5; ++i) {
|
|
|
|
int val = match.captured(i).toInt();
|
|
|
|
if (val < 0 || val > 255)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
2024-09-30 13:10:06 +02:00
|
|
|
return dn.match(host).hasMatch();
|
2023-07-14 16:59:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static bool isUrlValid(const QString &in)
|
|
|
|
{
|
|
|
|
const QUrl url(in);
|
|
|
|
return hostValid(url.host()) && (url.scheme() == "https" || url.scheme() == "http");
|
|
|
|
}
|
|
|
|
|
|
|
|
class DashboardSettingsWidget : public QWidget
|
|
|
|
{
|
|
|
|
public:
|
2024-05-22 13:39:12 +02:00
|
|
|
explicit DashboardSettingsWidget(QWidget *parent, QPushButton *ok = nullptr);
|
2023-07-14 16:59:32 +02:00
|
|
|
|
|
|
|
AxivionServer dashboardServer() const;
|
|
|
|
void setDashboardServer(const AxivionServer &server);
|
|
|
|
|
|
|
|
bool isValid() const;
|
|
|
|
|
|
|
|
private:
|
|
|
|
Id m_id;
|
|
|
|
StringAspect m_dashboardUrl;
|
2024-02-11 12:44:09 +01:00
|
|
|
StringAspect m_username;
|
2023-07-14 16:59:32 +02:00
|
|
|
BoolAspect m_valid;
|
|
|
|
};
|
|
|
|
|
2024-05-22 13:39:12 +02:00
|
|
|
DashboardSettingsWidget::DashboardSettingsWidget(QWidget *parent, QPushButton *ok)
|
2023-07-14 16:59:32 +02:00
|
|
|
: QWidget(parent)
|
|
|
|
{
|
|
|
|
m_dashboardUrl.setLabelText(Tr::tr("Dashboard URL:"));
|
2024-05-22 13:39:12 +02:00
|
|
|
m_dashboardUrl.setDisplayStyle(StringAspect::LineEditDisplay);
|
2024-02-11 12:44:09 +01:00
|
|
|
m_dashboardUrl.setValidationFunction([](FancyLineEdit *edit, QString *) {
|
2023-07-14 16:59:32 +02:00
|
|
|
return isUrlValid(edit->text());
|
|
|
|
});
|
2024-02-11 12:44:09 +01:00
|
|
|
|
|
|
|
m_username.setLabelText(Tr::tr("Username:"));
|
2024-05-22 13:39:12 +02:00
|
|
|
m_username.setDisplayStyle(StringAspect::LineEditDisplay);
|
2024-02-11 12:44:09 +01:00
|
|
|
m_username.setPlaceHolderText(Tr::tr("User name"));
|
|
|
|
|
2023-07-14 16:59:32 +02:00
|
|
|
using namespace Layouting;
|
|
|
|
|
|
|
|
Form {
|
|
|
|
m_dashboardUrl, br,
|
2024-02-11 12:44:09 +01:00
|
|
|
m_username, br,
|
2024-02-20 15:22:13 +01:00
|
|
|
noMargin
|
2023-07-14 16:59:32 +02:00
|
|
|
}.attachTo(this);
|
|
|
|
|
2024-05-22 13:39:12 +02:00
|
|
|
QTC_ASSERT(ok, return);
|
|
|
|
auto checkValidity = [this, ok] {
|
|
|
|
m_valid.setValue(isValid());
|
|
|
|
ok->setEnabled(m_valid());
|
|
|
|
};
|
2024-07-03 12:16:38 +02:00
|
|
|
m_dashboardUrl.addOnChanged(this, checkValidity);
|
|
|
|
m_username.addOnChanged(this, checkValidity);
|
2022-11-28 09:48:11 +01:00
|
|
|
}
|
|
|
|
|
2023-07-14 16:59:32 +02:00
|
|
|
AxivionServer DashboardSettingsWidget::dashboardServer() const
|
|
|
|
{
|
|
|
|
AxivionServer result;
|
|
|
|
if (m_id.isValid())
|
|
|
|
result.id = m_id;
|
|
|
|
else
|
2024-07-12 11:32:14 +02:00
|
|
|
result.id = Id::generate();
|
2024-02-19 22:16:36 +01:00
|
|
|
result.dashboard = fixUrl(m_dashboardUrl());
|
2024-02-11 12:44:09 +01:00
|
|
|
result.username = m_username();
|
2023-07-14 16:59:32 +02:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DashboardSettingsWidget::setDashboardServer(const AxivionServer &server)
|
|
|
|
{
|
|
|
|
m_id = server.id;
|
|
|
|
m_dashboardUrl.setValue(server.dashboard);
|
2024-02-11 12:44:09 +01:00
|
|
|
m_username.setValue(server.username);
|
2023-07-14 16:59:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool DashboardSettingsWidget::isValid() const
|
|
|
|
{
|
2024-02-16 13:27:36 +01:00
|
|
|
return isUrlValid(m_dashboardUrl());
|
2023-07-14 16:59:32 +02:00
|
|
|
}
|
|
|
|
|
2024-02-11 12:53:52 +01:00
|
|
|
class AxivionSettingsWidget : public IOptionsPageWidget
|
2023-07-14 16:59:32 +02:00
|
|
|
{
|
|
|
|
public:
|
|
|
|
AxivionSettingsWidget();
|
|
|
|
|
|
|
|
void apply() override;
|
|
|
|
|
|
|
|
private:
|
2024-05-22 15:50:22 +02:00
|
|
|
void showServerDialog(bool add);
|
|
|
|
void removeCurrentServerConfig();
|
2024-05-22 13:39:12 +02:00
|
|
|
void updateDashboardServers();
|
2024-05-22 15:50:22 +02:00
|
|
|
void updateEnabledStates();
|
2023-07-14 16:59:32 +02:00
|
|
|
|
2024-05-22 13:39:12 +02:00
|
|
|
QComboBox *m_dashboardServers = nullptr;
|
2023-07-14 16:59:32 +02:00
|
|
|
QPushButton *m_edit = nullptr;
|
2024-05-22 15:50:22 +02:00
|
|
|
QPushButton *m_remove = nullptr;
|
2023-07-14 16:59:32 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
AxivionSettingsWidget::AxivionSettingsWidget()
|
|
|
|
{
|
|
|
|
using namespace Layouting;
|
|
|
|
|
2024-05-22 13:39:12 +02:00
|
|
|
m_dashboardServers = new QComboBox(this);
|
|
|
|
m_dashboardServers->setSizeAdjustPolicy(QComboBox::AdjustToContents);
|
|
|
|
updateDashboardServers();
|
|
|
|
|
2024-05-22 15:50:22 +02:00
|
|
|
auto addButton = new QPushButton(Tr::tr("Add..."), this);
|
2023-07-14 16:59:32 +02:00
|
|
|
m_edit = new QPushButton(Tr::tr("Edit..."), this);
|
2024-05-22 15:50:22 +02:00
|
|
|
m_remove = new QPushButton(Tr::tr("Remove"), this);
|
2024-10-08 08:53:10 +02:00
|
|
|
Column {
|
|
|
|
Row {
|
|
|
|
Form { Tr::tr("Default dashboard server:"), m_dashboardServers, br },
|
2024-06-26 14:15:33 +02:00
|
|
|
st,
|
2024-10-08 08:53:10 +02:00
|
|
|
Column { addButton, m_edit, st, m_remove },
|
2024-02-13 14:20:23 +01:00
|
|
|
},
|
2024-06-26 14:15:33 +02:00
|
|
|
Space(10),
|
|
|
|
br,
|
2024-10-08 08:53:10 +02:00
|
|
|
Row {settings().highlightMarks },
|
|
|
|
st
|
|
|
|
}.attachTo(this);
|
2023-07-14 16:59:32 +02:00
|
|
|
|
2024-05-22 15:50:22 +02:00
|
|
|
connect(addButton, &QPushButton::clicked, this, [this] {
|
|
|
|
// add an empty item unconditionally
|
|
|
|
m_dashboardServers->addItem(Tr::tr("unset"), QVariant::fromValue(AxivionServer()));
|
|
|
|
m_dashboardServers->setCurrentIndex(m_dashboardServers->count() - 1);
|
|
|
|
showServerDialog(true);
|
|
|
|
});
|
|
|
|
connect(m_edit, &QPushButton::clicked, this, [this] { showServerDialog(false); });
|
|
|
|
connect(m_remove, &QPushButton::clicked,
|
|
|
|
this, &AxivionSettingsWidget::removeCurrentServerConfig);
|
|
|
|
updateEnabledStates();
|
2023-07-14 16:59:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void AxivionSettingsWidget::apply()
|
|
|
|
{
|
2024-05-22 13:39:12 +02:00
|
|
|
QList<AxivionServer> servers;
|
|
|
|
for (int i = 0, end = m_dashboardServers->count(); i < end; ++i)
|
|
|
|
servers.append(m_dashboardServers->itemData(i).value<AxivionServer>());
|
2024-09-25 14:57:09 +02:00
|
|
|
if (settings().updateDashboardServers(servers, servers.at(m_dashboardServers->currentIndex()).id))
|
|
|
|
settings().toSettings();
|
2023-07-14 16:59:32 +02:00
|
|
|
}
|
|
|
|
|
2024-05-22 13:39:12 +02:00
|
|
|
void AxivionSettingsWidget::updateDashboardServers()
|
|
|
|
{
|
|
|
|
m_dashboardServers->clear();
|
2024-09-25 14:57:09 +02:00
|
|
|
const QList<AxivionServer> servers = settings().allAvailableServers();
|
|
|
|
for (const AxivionServer &server : servers)
|
2024-05-22 13:39:12 +02:00
|
|
|
m_dashboardServers->addItem(server.displayString(), QVariant::fromValue(server));
|
2024-09-25 14:57:09 +02:00
|
|
|
int index = Utils::indexOf(servers,
|
|
|
|
[id = settings().defaultDashboardId()](const AxivionServer &s) {
|
|
|
|
return id == s.id;
|
|
|
|
});
|
|
|
|
if (index != -1)
|
|
|
|
m_dashboardServers->setCurrentIndex(index);
|
2024-05-22 13:39:12 +02:00
|
|
|
}
|
|
|
|
|
2024-05-22 15:50:22 +02:00
|
|
|
void AxivionSettingsWidget::updateEnabledStates()
|
|
|
|
{
|
|
|
|
const bool enabled = m_dashboardServers->count();
|
|
|
|
m_edit->setEnabled(enabled);
|
|
|
|
m_remove->setEnabled(enabled);
|
|
|
|
}
|
|
|
|
|
|
|
|
void AxivionSettingsWidget::removeCurrentServerConfig()
|
|
|
|
{
|
|
|
|
const QString config = m_dashboardServers->currentData().value<AxivionServer>().displayString();
|
2024-06-26 14:15:33 +02:00
|
|
|
if (QMessageBox::question(
|
|
|
|
ICore::dialogParent(),
|
|
|
|
Tr::tr("Remove Server Configuration"),
|
|
|
|
Tr::tr("Remove the server configuration \"%1\"?").arg(config))
|
|
|
|
!= QMessageBox::Yes) {
|
2024-05-22 15:50:22 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
m_dashboardServers->removeItem(m_dashboardServers->currentIndex());
|
|
|
|
updateEnabledStates();
|
|
|
|
}
|
|
|
|
|
|
|
|
void AxivionSettingsWidget::showServerDialog(bool add)
|
2023-07-14 16:59:32 +02:00
|
|
|
{
|
2024-05-22 13:39:12 +02:00
|
|
|
const AxivionServer old = m_dashboardServers->currentData().value<AxivionServer>();
|
2023-07-14 16:59:32 +02:00
|
|
|
QDialog d;
|
2024-05-22 15:50:22 +02:00
|
|
|
d.setWindowTitle(add ? Tr::tr("Add Dashboard Configuration")
|
|
|
|
: Tr::tr("Edit Dashboard Configuration"));
|
2023-07-14 16:59:32 +02:00
|
|
|
QVBoxLayout *layout = new QVBoxLayout;
|
|
|
|
auto buttons = new QDialogButtonBox(QDialogButtonBox::Cancel | QDialogButtonBox::Ok, this);
|
|
|
|
auto ok = buttons->button(QDialogButtonBox::Ok);
|
2024-05-22 13:39:12 +02:00
|
|
|
auto dashboardWidget = new DashboardSettingsWidget(this, ok);
|
2023-07-14 16:59:32 +02:00
|
|
|
dashboardWidget->setDashboardServer(old);
|
|
|
|
layout->addWidget(dashboardWidget);
|
2024-05-22 13:39:12 +02:00
|
|
|
ok->setEnabled(dashboardWidget->isValid());
|
2023-07-14 16:59:32 +02:00
|
|
|
connect(buttons->button(QDialogButtonBox::Cancel), &QPushButton::clicked, &d, &QDialog::reject);
|
|
|
|
connect(ok, &QPushButton::clicked, &d, &QDialog::accept);
|
|
|
|
layout->addWidget(buttons);
|
|
|
|
d.setLayout(layout);
|
|
|
|
d.resize(500, 200);
|
|
|
|
|
2024-05-22 15:50:22 +02:00
|
|
|
if (d.exec() != QDialog::Accepted) {
|
|
|
|
if (add) { // if we canceled an add, remove the canceled item
|
|
|
|
m_dashboardServers->removeItem(m_dashboardServers->currentIndex());
|
|
|
|
updateEnabledStates();
|
|
|
|
}
|
2023-07-14 16:59:32 +02:00
|
|
|
return;
|
2024-05-22 15:50:22 +02:00
|
|
|
}
|
2023-07-14 16:59:32 +02:00
|
|
|
if (dashboardWidget->isValid()) {
|
|
|
|
const AxivionServer server = dashboardWidget->dashboardServer();
|
2024-05-22 13:39:12 +02:00
|
|
|
if (server != old) {
|
2024-05-22 15:50:22 +02:00
|
|
|
m_dashboardServers->setItemData(m_dashboardServers->currentIndex(),
|
|
|
|
QVariant::fromValue(server));
|
|
|
|
m_dashboardServers->setItemData(m_dashboardServers->currentIndex(),
|
|
|
|
server.displayString(), Qt::DisplayRole);
|
2024-05-22 13:39:12 +02:00
|
|
|
}
|
2023-07-14 16:59:32 +02:00
|
|
|
}
|
2024-05-22 15:50:22 +02:00
|
|
|
updateEnabledStates();
|
2023-07-14 16:59:32 +02:00
|
|
|
}
|
|
|
|
|
2024-09-18 13:16:05 +02:00
|
|
|
// PathMappingSettingsWidget
|
|
|
|
|
|
|
|
class PathMappingDetails : public AspectContainer
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
PathMappingDetails()
|
|
|
|
{
|
|
|
|
m_projectName.setLabelText(Tr::tr("Project name:"));
|
|
|
|
m_projectName.setDisplayStyle(StringAspect::LineEditDisplay);
|
|
|
|
m_analysisPath.setLabelText(Tr::tr("Analysis path:"));
|
|
|
|
m_analysisPath.setDisplayStyle(StringAspect::LineEditDisplay);
|
|
|
|
m_localPath.setLabelText(Tr::tr("Local path:"));
|
|
|
|
m_localPath.setExpectedKind(PathChooser::ExistingDirectory);
|
|
|
|
m_localPath.setAllowPathFromDevice(false);
|
|
|
|
|
|
|
|
using namespace Layouting;
|
|
|
|
setLayouter([this] {
|
|
|
|
return Form {
|
|
|
|
&m_projectName, br,
|
|
|
|
&m_analysisPath, br,
|
|
|
|
&m_localPath,
|
|
|
|
noMargin};
|
|
|
|
});
|
|
|
|
setMacroExpander(nullptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
void updateContent(const PathMapping &mapping)
|
|
|
|
{
|
|
|
|
m_projectName.setValue(mapping.projectName, BaseAspect::BeQuiet);
|
|
|
|
m_analysisPath.setValue(mapping.analysisPath.toUserOutput(), BaseAspect::BeQuiet);
|
|
|
|
m_localPath.setValue(mapping.localPath, BaseAspect::BeQuiet);
|
|
|
|
}
|
|
|
|
|
|
|
|
PathMapping toPathMapping() const
|
|
|
|
{
|
|
|
|
return PathMapping{
|
|
|
|
m_projectName(), FilePath::fromUserInput(m_analysisPath()), m_localPath()
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
StringAspect m_projectName{this};
|
|
|
|
StringAspect m_analysisPath{this};
|
|
|
|
FilePathAspect m_localPath{this};
|
|
|
|
};
|
|
|
|
|
|
|
|
class PathMappingSettingsWidget final : public IOptionsPageWidget
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
PathMappingSettingsWidget();
|
|
|
|
|
|
|
|
void apply() final;
|
|
|
|
|
|
|
|
private:
|
|
|
|
void addMapping();
|
|
|
|
void deleteMapping();
|
|
|
|
void mappingChanged();
|
|
|
|
void currentChanged(const QModelIndex &index, const QModelIndex &previous);
|
2024-09-20 13:33:47 +02:00
|
|
|
void moveCurrent(bool up);
|
2024-09-18 13:16:05 +02:00
|
|
|
|
|
|
|
QTreeWidget m_mappingTree;
|
|
|
|
PathMappingDetails m_details;
|
|
|
|
QWidget *m_detailsWidget = nullptr;
|
|
|
|
QPushButton *m_deleteButton = nullptr;
|
2024-09-20 13:33:47 +02:00
|
|
|
QPushButton *m_moveUp = nullptr;
|
|
|
|
QPushButton *m_moveDown = nullptr;
|
2024-09-18 13:16:05 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
PathMappingSettingsWidget::PathMappingSettingsWidget()
|
|
|
|
{
|
|
|
|
m_detailsWidget = new QWidget(this);
|
|
|
|
m_details.layouter()().attachTo(m_detailsWidget);
|
|
|
|
|
|
|
|
m_mappingTree.setSelectionMode(QAbstractItemView::SingleSelection);
|
|
|
|
m_mappingTree.setSelectionBehavior(QAbstractItemView::SelectRows);
|
|
|
|
m_mappingTree.setHeaderLabels({Tr::tr("Project Name"), Tr::tr("Analysis Path"),
|
|
|
|
Tr::tr("Local Path")});
|
|
|
|
|
|
|
|
auto addButton = new QPushButton(Tr::tr("Add"), this);
|
|
|
|
m_deleteButton = new QPushButton(Tr::tr("Delete"), this);
|
2024-09-20 13:33:47 +02:00
|
|
|
m_moveUp = new QPushButton(Tr::tr("Move Up"), this);
|
|
|
|
m_moveDown = new QPushButton(Tr::tr("Move Down"), this);
|
2024-09-18 13:16:05 +02:00
|
|
|
|
|
|
|
using namespace Layouting;
|
2024-09-20 13:33:47 +02:00
|
|
|
Column buttons { addButton, m_deleteButton, empty, m_moveUp, m_moveDown, st };
|
2024-09-18 13:16:05 +02:00
|
|
|
|
|
|
|
Column {
|
|
|
|
Row { &m_mappingTree, buttons },
|
|
|
|
m_detailsWidget
|
|
|
|
}.attachTo(this);
|
|
|
|
|
2024-09-23 13:34:05 +02:00
|
|
|
const QList<QTreeWidgetItem *> items = Utils::transform(pathMappingSettings().validPathMappings(),
|
2024-09-18 13:16:05 +02:00
|
|
|
[this](const PathMapping &m) {
|
|
|
|
QTreeWidgetItem *item = new QTreeWidgetItem(&m_mappingTree,
|
|
|
|
{m.projectName,
|
|
|
|
m.analysisPath.toUserOutput(),
|
|
|
|
m.localPath.toUserOutput()});
|
|
|
|
if (!m.isValid())
|
|
|
|
item->setIcon(0, Icons::CRITICAL.icon());
|
|
|
|
return item;
|
|
|
|
});
|
|
|
|
m_mappingTree.addTopLevelItems(items);
|
|
|
|
|
|
|
|
m_deleteButton->setEnabled(false);
|
2024-09-20 13:33:47 +02:00
|
|
|
m_moveUp->setEnabled(false);
|
|
|
|
m_moveDown->setEnabled(false);
|
|
|
|
|
2024-09-18 13:16:05 +02:00
|
|
|
m_detailsWidget->setVisible(false);
|
|
|
|
|
|
|
|
connect(addButton, &QPushButton::clicked, this, &PathMappingSettingsWidget::addMapping);
|
|
|
|
connect(m_deleteButton, &QPushButton::clicked, this, &PathMappingSettingsWidget::deleteMapping);
|
2024-09-20 13:33:47 +02:00
|
|
|
connect(m_moveUp, &QPushButton::clicked, this, [this]{ moveCurrent(true); });
|
|
|
|
connect(m_moveDown, &QPushButton::clicked, this, [this]{ moveCurrent(false); });
|
2024-09-18 13:16:05 +02:00
|
|
|
connect(m_mappingTree.selectionModel(), &QItemSelectionModel::currentChanged,
|
|
|
|
this, &PathMappingSettingsWidget::currentChanged);
|
|
|
|
connect(&m_details, &AspectContainer::changed, this,
|
|
|
|
&PathMappingSettingsWidget::mappingChanged);
|
|
|
|
}
|
|
|
|
|
|
|
|
void PathMappingSettingsWidget::apply()
|
|
|
|
{
|
2024-09-23 13:34:05 +02:00
|
|
|
const QList<PathMapping> oldMappings = settings().validPathMappings();
|
2024-09-18 13:16:05 +02:00
|
|
|
QList<PathMapping> newMappings;
|
|
|
|
for (int row = 0, count = m_mappingTree.topLevelItemCount(); row < count; ++row) {
|
|
|
|
const QTreeWidgetItem * const item = m_mappingTree.topLevelItem(row);
|
|
|
|
newMappings.append({item->text(0),
|
|
|
|
FilePath::fromUserInput(item->text(1)),
|
|
|
|
FilePath::fromUserInput(item->text(2))});
|
|
|
|
}
|
|
|
|
if (oldMappings == newMappings)
|
|
|
|
return;
|
|
|
|
|
|
|
|
pathMappingSettings().setVariantValue(pathMappingsToSetting(newMappings));
|
|
|
|
pathMappingSettings().writeSettings();
|
|
|
|
}
|
|
|
|
|
|
|
|
void PathMappingSettingsWidget::addMapping()
|
|
|
|
{
|
|
|
|
QTreeWidgetItem *item = new QTreeWidgetItem(&m_mappingTree, {"", "", ""});
|
|
|
|
m_mappingTree.setCurrentItem(item);
|
|
|
|
item->setIcon(0, Icons::CRITICAL.icon());
|
|
|
|
}
|
|
|
|
|
|
|
|
void PathMappingSettingsWidget::deleteMapping()
|
|
|
|
{
|
|
|
|
QTreeWidgetItem *item = m_mappingTree.currentItem();
|
|
|
|
QTC_ASSERT(item, return);
|
|
|
|
const QModelIndex index = m_mappingTree.indexFromItem(item);
|
|
|
|
if (!index.isValid())
|
|
|
|
return;
|
|
|
|
m_mappingTree.model()->removeRow(index.row());
|
|
|
|
}
|
|
|
|
|
|
|
|
void PathMappingSettingsWidget::mappingChanged()
|
|
|
|
{
|
|
|
|
QTreeWidgetItem *item = m_mappingTree.currentItem();
|
|
|
|
QTC_ASSERT(item, return);
|
|
|
|
PathMapping modified = m_details.toPathMapping();
|
|
|
|
item->setText(0, modified.projectName);
|
|
|
|
item->setText(1, modified.analysisPath.toUserOutput());
|
|
|
|
item->setText(2, modified.localPath.toUserOutput());
|
|
|
|
item->setIcon(0, modified.isValid() ? QIcon{} : Icons::CRITICAL.icon());
|
|
|
|
}
|
|
|
|
|
|
|
|
void PathMappingSettingsWidget::currentChanged(const QModelIndex &index,
|
|
|
|
const QModelIndex &/*previous*/)
|
|
|
|
{
|
2024-09-20 13:33:47 +02:00
|
|
|
const bool indexValid = index.isValid();
|
|
|
|
const int row = index.row();
|
2024-09-18 13:16:05 +02:00
|
|
|
m_deleteButton->setEnabled(indexValid);
|
2024-09-20 13:33:47 +02:00
|
|
|
m_moveUp->setEnabled(indexValid && row > 0);
|
|
|
|
m_moveDown->setEnabled(indexValid && row < m_mappingTree.topLevelItemCount() - 1);
|
2024-09-18 13:16:05 +02:00
|
|
|
m_detailsWidget->setVisible(indexValid);
|
|
|
|
if (indexValid) {
|
|
|
|
const QTreeWidgetItem * const item = m_mappingTree.itemFromIndex(index);
|
|
|
|
m_details.updateContent({item->text(0),
|
|
|
|
FilePath::fromUserInput(item->text(1)),
|
|
|
|
FilePath::fromUserInput(item->text(2))});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-09-20 13:33:47 +02:00
|
|
|
void PathMappingSettingsWidget::moveCurrent(bool up)
|
|
|
|
{
|
|
|
|
const int itemCount = m_mappingTree.topLevelItemCount();
|
|
|
|
const QModelIndexList indexes = m_mappingTree.selectionModel()->selectedRows();
|
|
|
|
QTC_ASSERT(indexes.size() == 1, return);
|
|
|
|
const QModelIndex index = indexes.first();
|
|
|
|
QTC_ASSERT(index.isValid(), return);
|
|
|
|
const int row = index.row();
|
|
|
|
QTC_ASSERT(up ? row > 0 : row < itemCount - 1, return);
|
|
|
|
|
|
|
|
QTreeWidgetItem *item = m_mappingTree.takeTopLevelItem(row);
|
|
|
|
m_mappingTree.insertTopLevelItem(up ? row - 1 : row + 1, item);
|
|
|
|
m_mappingTree.setCurrentItem(item);
|
|
|
|
}
|
|
|
|
|
2024-09-18 13:16:05 +02:00
|
|
|
// settings pages
|
2023-07-14 16:59:32 +02:00
|
|
|
|
2024-02-11 12:53:52 +01:00
|
|
|
class AxivionSettingsPage : public IOptionsPage
|
2023-07-14 16:59:32 +02:00
|
|
|
{
|
|
|
|
public:
|
|
|
|
AxivionSettingsPage()
|
|
|
|
{
|
|
|
|
setId("Axivion.Settings.General");
|
|
|
|
setDisplayName(Tr::tr("General"));
|
|
|
|
setCategory("XY.Axivion");
|
|
|
|
setDisplayCategory(Tr::tr("Axivion"));
|
|
|
|
setCategoryIconPath(":/axivion/images/axivion.png");
|
|
|
|
setWidgetCreator([] { return new AxivionSettingsWidget; });
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2024-09-18 13:16:05 +02:00
|
|
|
class PathMappingSettingsPage : public IOptionsPage
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
PathMappingSettingsPage()
|
|
|
|
{
|
|
|
|
setId("Axivion.Settings.PathMapping");
|
|
|
|
setDisplayName(Tr::tr("Path Mapping"));
|
|
|
|
setCategory("XY.Axivion");
|
|
|
|
setWidgetCreator([] { return new PathMappingSettingsWidget; });
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const AxivionSettingsPage generalSettingsPage;
|
|
|
|
const PathMappingSettingsPage pathMappingSettingsPage;
|
2023-07-14 16:59:32 +02:00
|
|
|
|
2022-12-12 16:45:31 +01:00
|
|
|
} // Axivion::Internal
|