Library migration #36
10 changed files with 87 additions and 61 deletions
|
@ -18,7 +18,7 @@ DEFINES += QT_DEPRECATED_WARNINGS
|
||||||
# You can also select to disable deprecated APIs only up to a certain version of Qt.
|
# You can also select to disable deprecated APIs only up to a certain version of Qt.
|
||||||
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
|
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
|
||||||
|
|
||||||
TARGET = speedclimbing_stw
|
TARGET = ScStwApp
|
||||||
|
|
||||||
# include submodules
|
# include submodules
|
||||||
CONFIG += ScStwLibraries_QML ScStwLibraries_Styling ScStwLibraries_ClientLibs
|
CONFIG += ScStwLibraries_QML ScStwLibraries_Styling ScStwLibraries_ClientLibs
|
||||||
|
|
|
@ -4,13 +4,13 @@
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
#include <QStandardPaths>
|
#include <QStandardPaths>
|
||||||
#include "scstwsettings.h"
|
#include "scstwremotesettings.h"
|
||||||
|
|
||||||
class ScStwAppSettings : public ScStwSettings
|
class ScStwAppSettings : public ScStwRemoteSettings
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit ScStwAppSettings(QObject *parent = nullptr);
|
explicit ScStwAppSettings(ScStwClient * scStwClient, QObject *parent = nullptr);
|
||||||
~ScStwAppSettings();
|
~ScStwAppSettings();
|
||||||
|
|
||||||
enum AppInternalSetting {
|
enum AppInternalSetting {
|
||||||
|
@ -20,14 +20,19 @@ public:
|
||||||
};
|
};
|
||||||
Q_ENUM(AppInternalSetting)
|
Q_ENUM(AppInternalSetting)
|
||||||
|
|
||||||
using ScStwSettings::readSetting;
|
enum KeyLevelEnum {
|
||||||
QVariant readSetting(AppInternalSetting key);
|
KeyLevel = 1
|
||||||
using ScStwSettings::writeSetting;
|
};
|
||||||
bool writeSetting(AppInternalSetting key, QVariant value);
|
Q_ENUM(KeyLevelEnum)
|
||||||
using ScStwSettings::setDefaultSetting;
|
|
||||||
void setDefaultSetting(AppInternalSetting key, QVariant defaultVariant);
|
|
||||||
|
|
||||||
QSettings *settingsManager;
|
using ScStwSettings::readSetting;
|
||||||
|
Q_INVOKABLE QVariant readSetting(AppInternalSetting key);
|
||||||
|
Q_INVOKABLE QVariant readSetting(int key, int level);
|
||||||
|
using ScStwSettings::writeSetting;
|
||||||
|
Q_INVOKABLE bool writeSetting(AppInternalSetting key, QVariant value);
|
||||||
|
Q_INVOKABLE bool writeSetting(int key, QVariant value, int level);
|
||||||
|
using ScStwSettings::setDefaultSetting;
|
||||||
|
Q_INVOKABLE void setDefaultSetting(AppInternalSetting key, QVariant defaultVariant);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
|
||||||
|
|
|
@ -134,11 +134,11 @@ Column {
|
||||||
text: qsTr("IP")
|
text: qsTr("IP")
|
||||||
|
|
||||||
inputHint: "IP"
|
inputHint: "IP"
|
||||||
inputText: speedBackend.readSetting("baseStationIpAdress")
|
inputText: speedBackend.settings.readSetting(ScStwAppSettings.BaseStationIpSetting)
|
||||||
inputTextFieldWidth: width * 0.7
|
inputTextFieldWidth: width * 0.7
|
||||||
|
|
||||||
onInputTextChanged: {
|
onInputTextChanged: {
|
||||||
speedBackend.writeSetting("baseStationIpAdress", inputText)
|
speedBackend.settings.writeSetting(ScStwAppSettings.BaseStationIpSetting, inputText)
|
||||||
}
|
}
|
||||||
|
|
||||||
width: parent.width
|
width: parent.width
|
||||||
|
@ -229,12 +229,12 @@ Column {
|
||||||
|
|
||||||
onSliderFinished: {
|
onSliderFinished: {
|
||||||
enabled = false
|
enabled = false
|
||||||
speedBackend.writeSetting("SoundVolume", sliderValue)
|
speedBackend.settings.writeSetting(ScStwSettings.SoundVolumeSetting, sliderValue, ScStwSettings.KeyLevel)
|
||||||
enabled = true
|
enabled = true
|
||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
var val = speedBackend.readSetting("SoundVolume")
|
var val = speedBackend.settings.readSetting(ScStwSettings.SoundVolumeSetting, ScStwSettings.KeyLevel)
|
||||||
if(val !== "false"){
|
if(val !== "false"){
|
||||||
sliderValue = parseFloat(val)
|
sliderValue = parseFloat(val)
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,12 +23,12 @@ Column {
|
||||||
|
|
||||||
function updateSetting(key, val, del){
|
function updateSetting(key, val, del){
|
||||||
del.busy = true
|
del.busy = true
|
||||||
speedBackend.writeSetting(scStw.baseStationSettingToString(key) , val)
|
speedBackend.settings.writeSetting(key, val, ScStwSettings.KeyLevel)
|
||||||
del.busy = false
|
del.busy = false
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadSetting(key, del){
|
function loadSetting(key, del){
|
||||||
return speedBackend.readSetting(key)
|
return speedBackend.settings.readSetting(key, ScStwSettings.KeyLevel)
|
||||||
}
|
}
|
||||||
|
|
||||||
SmoothSwitchDelegate {
|
SmoothSwitchDelegate {
|
||||||
|
@ -43,10 +43,10 @@ Column {
|
||||||
|
|
||||||
text: qsTr("say 'ready'")
|
text: qsTr("say 'ready'")
|
||||||
|
|
||||||
checked: parent.loadSetting(ScStw.ReadySoundEnableSetting, ready_del) === "true"
|
checked: parent.loadSetting(ScStwSettings.ReadySoundEnableSetting, ready_del)
|
||||||
|
|
||||||
onCheckedChanged: {
|
onCheckedChanged: {
|
||||||
parent.updateSetting(ScStw.ReadySoundEnableSetting, checked, ready_del)
|
parent.updateSetting(ScStwSettings.ReadySoundEnableSetting, checked, ready_del)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,10 +64,10 @@ Column {
|
||||||
inputHint: qsTr("time")
|
inputHint: qsTr("time")
|
||||||
inputMethodHints: Qt.ImhFormattedNumbersOnly
|
inputMethodHints: Qt.ImhFormattedNumbersOnly
|
||||||
|
|
||||||
inputText: control.loadSetting(ScStw.ReadySoundDelaySetting, ready_delay_del)
|
inputText: control.loadSetting(ScStwSettings.ReadySoundDelaySetting, ready_delay_del)
|
||||||
|
|
||||||
onInputFinished: {
|
onInputFinished: {
|
||||||
control.updateSetting(ScStw.ReadySoundDelaySetting, inputText, ready_delay_del)
|
control.updateSetting(ScStwSettings.ReadySoundDelaySetting, inputText, ready_delay_del)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,10 +83,10 @@ Column {
|
||||||
|
|
||||||
text: qsTr("say 'at your marks'")
|
text: qsTr("say 'at your marks'")
|
||||||
|
|
||||||
checked: control.loadSetting(ScStw.AtYourMarksSoundEnableSetting , ready_del) === "true"
|
checked: control.loadSetting(ScStwSettings.AtYourMarksSoundEnableSetting , ready_del)
|
||||||
|
|
||||||
onCheckedChanged: {
|
onCheckedChanged: {
|
||||||
parent.updateSetting(ScStw.AtYourMarksSoundEnableSetting, at_marks_del.checked, at_marks_del)
|
parent.updateSetting(ScStwSettings.AtYourMarksSoundEnableSetting, at_marks_del.checked, at_marks_del)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,10 +104,10 @@ Column {
|
||||||
|
|
||||||
enabled: !busy && at_marks_del.checked
|
enabled: !busy && at_marks_del.checked
|
||||||
|
|
||||||
inputText: control.loadSetting(ScStw.AtYourMarksSoundDelaySetting, at_marks_delay_del)
|
inputText: control.loadSetting(ScStwSettings.AtYourMarksSoundDelaySetting, at_marks_delay_del)
|
||||||
|
|
||||||
onInputFinished: {
|
onInputFinished: {
|
||||||
control.updateSetting(ScStw.AtYourMarksSoundDelaySetting, inputText, at_marks_delay_del)
|
control.updateSetting(ScStwSettings.AtYourMarksSoundDelaySetting, inputText, at_marks_delay_del)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,11 +55,11 @@ Column {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: parentObj.delegateHeight
|
height: parentObj.delegateHeight
|
||||||
|
|
||||||
checked: speedBackend.readSetting("theme") === "Dark"
|
checked: speedBackend.settings.readSetting(ScStwAppSettings.AppThemeSetting) === "Dark"
|
||||||
|
|
||||||
onCheckedChanged: {
|
onCheckedChanged: {
|
||||||
var newTheme = checked ? "Dark":"Light"
|
var newTheme = checked ? "Dark":"Light"
|
||||||
speedBackend.writeSetting("theme", newTheme)
|
speedBackend.settings.writeSetting(ScStwAppSettings.AppThemeSetting, newTheme)
|
||||||
appTheme.setTheme(newTheme)
|
appTheme.setTheme(newTheme)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,7 @@ Window {
|
||||||
visible: true
|
visible: true
|
||||||
width: 540
|
width: 540
|
||||||
height: 960
|
height: 960
|
||||||
title: "Speedclimbing stw"
|
title: "ScStwApp"
|
||||||
property date currentTime: new Date()
|
property date currentTime: new Date()
|
||||||
property int millis: 0
|
property int millis: 0
|
||||||
|
|
||||||
|
@ -103,7 +103,7 @@ Window {
|
||||||
id: appTheme
|
id: appTheme
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
appTheme.setTheme(speedBackend.readSetting("theme"))
|
appTheme.setTheme(speedBackend.settings.readSetting(ScStwAppSettings.AppThemeSetting))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 3d18b72eaf7fc8993ff60f1e087fa0c30516f4b5
|
Subproject commit 6c1ce8e65407452ce82f412fdd26d1367bf3ea38
|
|
@ -81,10 +81,11 @@ int main(int argc, char *argv[])
|
||||||
});
|
});
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ScStwAppSettings * pAppSettings = new ScStwAppSettings();
|
|
||||||
|
|
||||||
// setup speed backend
|
// setup speed backend
|
||||||
qmlRegisterType<ScStwAppBackend>("de.itsblue.ScStwApp", 2, 0, "SpeedBackend");
|
qmlRegisterType<ScStwAppBackend>("de.itsblue.ScStwApp", 2, 0, "SpeedBackend");
|
||||||
|
qmlRegisterUncreatableType<ScStwAppSettings>("de.itsblue.ScStwApp", 2, 0, "ScStwAppSettings", "The ScStwAppSettings type is not creatable!");
|
||||||
|
|
||||||
|
qRegisterMetaType<ScStwAppSettings::AppInternalSetting>("ScStwAppSettings::BaseStationSetting");
|
||||||
|
|
||||||
QQmlApplicationEngine engine;
|
QQmlApplicationEngine engine;
|
||||||
ScStwLibraries::init();
|
ScStwLibraries::init();
|
||||||
|
@ -92,11 +93,6 @@ int main(int argc, char *argv[])
|
||||||
|
|
||||||
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
|
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
|
||||||
|
|
||||||
QQmlContext *context = engine.rootContext();
|
|
||||||
|
|
||||||
// setup app settings
|
|
||||||
context->setContextProperty("_cppAppSettings", pAppSettings);
|
|
||||||
|
|
||||||
if (engine.rootObjects().isEmpty())
|
if (engine.rootObjects().isEmpty())
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
|
|
@ -2,23 +2,25 @@
|
||||||
|
|
||||||
ScStwAppBackend::ScStwAppBackend(QObject *parent) : QObject(parent)
|
ScStwAppBackend::ScStwAppBackend(QObject *parent) : QObject(parent)
|
||||||
{
|
{
|
||||||
this->appSettings = new ScStwAppSettings(this);
|
this->scStwClient = new ScStwClient(this);
|
||||||
|
this->appSettings = new ScStwAppSettings(this->scStwClient, this);
|
||||||
|
this->localRace = new ScStwRace(this);
|
||||||
|
this->remoteRace = new ScStwRemoteMonitorRace(this->scStwClient, this);
|
||||||
|
this->mode = LOCAL;
|
||||||
|
|
||||||
this->appSettings->setDefaultSetting(ScStwSettings::ReadySoundEnableSetting, false);
|
connect(this->appSettings, &ScStwAppSettings::settingChanged, this, &ScStwAppBackend::handleSettingChange);
|
||||||
this->appSettings->setDefaultSetting(ScStwSettings::ReadySoundDelaySetting, 0);
|
|
||||||
this->appSettings->setDefaultSetting(ScStwSettings::AtYourMarksSoundEnableSetting, false);
|
this->appSettings->setDefaultSetting(ScStwSettings::BaseStationSetting::ReadySoundEnableSetting, false);
|
||||||
this->appSettings->setDefaultSetting(ScStwSettings::AtYourMarksSoundDelaySetting, 0);
|
this->appSettings->setDefaultSetting(ScStwSettings::BaseStationSetting::ReadySoundDelaySetting, 0);
|
||||||
|
this->appSettings->setDefaultSetting(ScStwSettings::BaseStationSetting::AtYourMarksSoundEnableSetting, false);
|
||||||
|
this->appSettings->setDefaultSetting(ScStwSettings::BaseStationSetting::AtYourMarksSoundDelaySetting, 0);
|
||||||
|
|
||||||
this->appSettings->setDefaultSetting(ScStwAppSettings::AppThemeSetting, "Light");
|
this->appSettings->setDefaultSetting(ScStwAppSettings::AppThemeSetting, "Light");
|
||||||
|
|
||||||
this->appSettings->setDefaultSetting(ScStwAppSettings::BaseStationIpSetting, "192.168.4.1");
|
this->appSettings->setDefaultSetting(ScStwAppSettings::BaseStationIpSetting, "192.168.4.1");
|
||||||
|
|
||||||
this->scStwClient = new ScStwClient();
|
|
||||||
this->localRace = new ScStwRace(this);
|
|
||||||
this->remoteRace = new ScStwRemoteMonitorRace(this->scStwClient, this);
|
|
||||||
this->mode = LOCAL;
|
|
||||||
|
|
||||||
this->scStwClient->setIP(this->appSettings->readSetting(ScStwAppSettings::BaseStationIpSetting).toString());
|
this->scStwClient->setIP(this->appSettings->readSetting(ScStwAppSettings::BaseStationIpSetting).toString());
|
||||||
|
|
||||||
connect(this->scStwClient, &ScStwClient::stateChanged, this, &ScStwAppBackend::baseStationStateChanged);
|
connect(this->scStwClient, &ScStwClient::stateChanged, this, &ScStwAppBackend::baseStationStateChanged);
|
||||||
connect(this->scStwClient, &ScStwClient::stateChanged, this, &ScStwAppBackend::refreshMode);
|
connect(this->scStwClient, &ScStwClient::stateChanged, this, &ScStwAppBackend::refreshMode);
|
||||||
connect(this, &ScStwAppBackend::baseStationStateChanged, this, &ScStwAppBackend::baseStationPropertiesChanged);
|
connect(this, &ScStwAppBackend::baseStationStateChanged, this, &ScStwAppBackend::baseStationPropertiesChanged);
|
||||||
|
@ -189,14 +191,14 @@ int ScStwAppBackend::getMode() {
|
||||||
void ScStwAppBackend::reloadRaceSettings() {
|
void ScStwAppBackend::reloadRaceSettings() {
|
||||||
this->getRace()->writeStartActionSetting(
|
this->getRace()->writeStartActionSetting(
|
||||||
ScStwRace::AtYourMarks,
|
ScStwRace::AtYourMarks,
|
||||||
this->appSettings->readSetting(ScStwSettings::AtYourMarksSoundEnableSetting).toBool(),
|
this->appSettings->readSetting(ScStwSettings::BaseStationSetting::AtYourMarksSoundEnableSetting).toBool(),
|
||||||
this->appSettings->readSetting(ScStwSettings::AtYourMarksSoundDelaySetting).toDouble()
|
this->appSettings->readSetting(ScStwSettings::BaseStationSetting::AtYourMarksSoundDelaySetting).toDouble()
|
||||||
);
|
);
|
||||||
|
|
||||||
this->getRace()->writeStartActionSetting(
|
this->getRace()->writeStartActionSetting(
|
||||||
ScStwRace::Ready,
|
ScStwRace::Ready,
|
||||||
this->appSettings->readSetting(ScStwSettings::ReadySoundEnableSetting).toBool(),
|
this->appSettings->readSetting(ScStwSettings::BaseStationSetting::ReadySoundEnableSetting).toBool(),
|
||||||
this->appSettings->readSetting(ScStwSettings::ReadySoundDelaySetting).toDouble()
|
this->appSettings->readSetting(ScStwSettings::BaseStationSetting::ReadySoundDelaySetting).toDouble()
|
||||||
);
|
);
|
||||||
|
|
||||||
this->getRace()->setSoundVolume(1);
|
this->getRace()->setSoundVolume(1);
|
||||||
|
@ -208,12 +210,17 @@ void ScStwAppBackend::reloadBaseStationIpAdress() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScStwAppBackend::handleSettingChange(int keyInt, int keyLevel) {
|
void ScStwAppBackend::handleSettingChange(int keyInt, int keyLevel) {
|
||||||
|
if(keyInt == -1) {
|
||||||
|
emit this->settingsChanged();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
switch (keyLevel) {
|
switch (keyLevel) {
|
||||||
case 0: {
|
case 0: {
|
||||||
// BaseStationSetting!!
|
// BaseStationSetting!!
|
||||||
ScStwSettings::BaseStationSetting key = static_cast<ScStwSettings::BaseStationSetting>(keyInt);
|
ScStwSettings::BaseStationSetting key = static_cast<ScStwSettings::BaseStationSetting>(keyInt);
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case ScStwSettings::InvalidSetting:
|
case ScStwSettings::BaseStationSetting::InvalidSetting:
|
||||||
return;
|
return;
|
||||||
default:
|
default:
|
||||||
this->reloadRaceSettings();
|
this->reloadRaceSettings();
|
||||||
|
@ -236,4 +243,6 @@ void ScStwAppBackend::handleSettingChange(int keyInt, int keyLevel) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
emit this->settingsChanged();
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,30 +19,46 @@
|
||||||
|
|
||||||
ScStwAppSettings * pGlobalAppSettings = nullptr;
|
ScStwAppSettings * pGlobalAppSettings = nullptr;
|
||||||
|
|
||||||
ScStwAppSettings::ScStwAppSettings(QObject* parent)
|
ScStwAppSettings::ScStwAppSettings(ScStwClient * client, QObject* parent)
|
||||||
:ScStwSettings(parent)
|
:ScStwRemoteSettings(client, parent)
|
||||||
{
|
{
|
||||||
QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
|
|
||||||
|
|
||||||
this->settingsManager = new QSettings(path+"/settings.ini", QSettings::IniFormat, this);
|
|
||||||
|
|
||||||
pGlobalAppSettings = this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant ScStwAppSettings::readSetting(AppInternalSetting key) {
|
QVariant ScStwAppSettings::readSetting(AppInternalSetting key) {
|
||||||
return ScStwSettings::readSetting(QMetaEnum::fromType<AppInternalSetting>().valueToKey(int(key)));
|
return ScStwSettings::readSetting(QMetaEnum::fromType<AppInternalSetting>().valueToKey(int(key)), int(key), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant ScStwAppSettings::readSetting(int key, int keyLevel) {
|
||||||
|
switch (keyLevel) {
|
||||||
|
case ScStwAppSettings::KeyLevel:
|
||||||
|
return this->readSetting(ScStwAppSettings::AppInternalSetting(key));
|
||||||
|
case ScStwSettings::KeyLevel:
|
||||||
|
return ScStwSettings::readSetting(ScStwSettings::BaseStationSetting(key));
|
||||||
|
default:
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ScStwAppSettings::writeSetting(AppInternalSetting key, QVariant value) {
|
bool ScStwAppSettings::writeSetting(AppInternalSetting key, QVariant value) {
|
||||||
return ScStwSettings::writeSetting(QMetaEnum::fromType<AppInternalSetting>().valueToKey(int(key)), value, int(key), 1);
|
return ScStwSettings::writeSetting(QMetaEnum::fromType<AppInternalSetting>().valueToKey(int(key)), value, int(key), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ScStwAppSettings::writeSetting(int key, QVariant value, int keyLevel) {
|
||||||
|
switch (keyLevel) {
|
||||||
|
case ScStwAppSettings::KeyLevel:
|
||||||
|
return this->writeSetting(ScStwAppSettings::AppInternalSetting(key), value);
|
||||||
|
case ScStwSettings::KeyLevel:
|
||||||
|
return ScStwSettings::writeSetting(ScStwSettings::BaseStationSetting(key), value);
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ScStwAppSettings::setDefaultSetting(AppInternalSetting key, QVariant defaultVariant) {
|
void ScStwAppSettings::setDefaultSetting(AppInternalSetting key, QVariant defaultVariant) {
|
||||||
ScStwSettings::setDefaultSetting(QMetaEnum::fromType<AppInternalSetting>().valueToKey(int(key)), defaultVariant, int(key), 1);
|
ScStwSettings::setDefaultSetting(QMetaEnum::fromType<AppInternalSetting>().valueToKey(int(key)), defaultVariant, int(key), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
ScStwAppSettings::~ScStwAppSettings()
|
ScStwAppSettings::~ScStwAppSettings()
|
||||||
{
|
{
|
||||||
delete settingsManager;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Reference in a new issue