Commit d5bf91ba authored by Leander Schulten's avatar Leander Schulten

Merge branch 'master' into windows-release

parents e1bd3e6f 6487111d
Pipeline #199378 passed with stages
in 7 minutes and 20 seconds
......@@ -151,6 +151,7 @@ RUN make qtnetworkauth
RUN make qtquickcontrols2
RUN make qtserialport
RUN make qttools
RUN make qtwebsockets
#set path to mxe compiler
RUN export PATH=/usr/src/mxe/usr/bin:$PATH
......@@ -53,6 +53,7 @@ SOURCES += \
modules/dmxconsumer.cpp \
modules/ledconsumer.cpp \
scanner.cpp \
settingsfilewrapper.cpp \
slideshow.cpp \
system_error_handler.cpp \
test/testloopprogramm.cpp \
......@@ -125,6 +126,7 @@ HEADERS += \
modules/ledconsumer.h \
modules/scanner.hpp \
scanner.h \
settingsfilewrapper.h \
slideshow.h \
system_error_handler.h \
updater.h \
......@@ -287,3 +289,8 @@ win32-msvc{
INCLUDEPATH += $$PWD/lib/RtAudio/include
LIBS += -L$$PWD/lib/RtAudio/lib -lrtaudio
win32: LIBS += -lole32 -lwinmm -lksuser -lmfplat -lmfuuid -lwmcodecdspuuid
macx{
# Needed by the SystemVolume class
LIBS += -framework CoreAudio
}
......@@ -90,7 +90,17 @@ std::pair<std::function<void()>, QString> loadData(const QByteArray &data) {
QJsonParseError error{};
auto doc = QJsonDocument::fromJson(data, &error);
if (error.error != QJsonParseError::NoError) {
return {{}, "Failed to parse settings file: " + error.errorString() + " at position " + QString::number(error.offset)};
// compute line and column
int line = 1;
auto lastNewLine = data.cbegin();
for (auto i = data.cbegin(); i != data.cbegin() + error.offset; ++i) {
if (*i == '\n') {
++line;
lastNewLine = i;
}
}
int column = QString::fromUtf8(lastNewLine, data.cbegin() + error.offset - lastNewLine).length();
return {{}, "Failed to parse settings file: " + error.errorString() + " at position " + QString::number(error.offset) + ", Ln " + QString::number(line) + ", Col " + QString::number(column)};
}
const auto o = doc.object();
for (const auto e : o[QStringLiteral("DevicePrototypes")].toArray()) {
......
#include "systemvolume.h"
#include <QtDebug>
#ifdef Q_OS_MAC
OSStatus callback(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, void * /*inClientData*/) {
for (UInt32 i = 0; i < inNumberAddresses; ++i) {
const auto &cur = inAddresses[i];
if (cur.mScope == kAudioDevicePropertyScopeOutput && cur.mSelector == kAudioDevicePropertyVolumeScalar && cur.mElement == 1 /*LEFT_CHANNEL*/) {
AudioObjectPropertyAddress volumePropertyAddress = {
kAudioDevicePropertyVolumeScalar, kAudioDevicePropertyScopeOutput, 1 /*LEFT_CHANNEL*/
};
Float32 volume;
UInt32 volumedataSize = sizeof(volume);
auto result = AudioObjectGetPropertyData(inObjectID, &volumePropertyAddress, 0, nullptr, &volumedataSize, &volume);
if (result == kAudioHardwareNoError) {
SystemVolume::get().setVolume(static_cast<double>(volume));
}
break;
}
}
return noErr;
}
#endif
#ifdef Q_OS_WIN
HRESULT Callback::OnNotify(AUDIO_VOLUME_NOTIFICATION_DATA *pNotify) {
SystemVolume::get().setVolume(static_cast<double>(pNotify->fMasterVolume));
return S_OK;
}
Callback::~Callback() {
if (counter != 0) {
qWarning() << "SystemVolume::Callback: Releasing object with ref count > 0";
}
}
HRESULT Callback::QueryInterface(const IID & /*riid*/, void ** /*ppvObject*/) {
qWarning() << "SystemVolume::Callback: What do they want from me?";
return E_NOINTERFACE;
}
#endif
SystemVolume::SystemVolume() {
#ifdef Q_OS_WIN
......@@ -21,28 +62,52 @@ SystemVolume::SystemVolume() {
if (hr < 0) {
return;
}
hr = endpointVolume->RegisterControlChangeNotify(&callback);
if (hr < 0) {
return;
}
// query initial volume
FLOAT v;
hr = endpointVolume->GetMasterVolumeLevelScalar(&v);
if (hr < 0) {
return;
}
volume = static_cast<double>(v);
emit volumeChanged();
#endif
startTimer(SystemVolumeUpdateRateInMs);
timerEvent(nullptr);
}
#ifdef Q_OS_MAC
AudioObjectPropertyAddress getDefaultOutputDevicePropertyAddress = {kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster};
void SystemVolume::timerEvent(QTimerEvent * /*event*/) {
#ifdef Q_OS_WIN
if (endpointVolume) {
float vol;
auto hr = endpointVolume->GetMasterVolumeLevelScalar(&vol);
if (hr >= 0 /* success */) {
auto v = static_cast<double>(vol);
if (volume != v) {
volume = v;
emit volumeChanged();
}
}
UInt32 outputDeviceSize = sizeof(defaultOutputDeviceID);
OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &getDefaultOutputDevicePropertyAddress, 0, nullptr, &outputDeviceSize, &defaultOutputDeviceID);
if (kAudioHardwareNoError != result) {
qWarning() << "Failed to get the default output device";
}
// register a listener so that we get an event when the volume changed
AudioObjectPropertyAddress volumePropertyAddress = {
kAudioDevicePropertyVolumeScalar, kAudioDevicePropertyScopeOutput, 1 /*LEFT_CHANNEL*/
};
result = AudioObjectAddPropertyListener(defaultOutputDeviceID, &volumePropertyAddress, &callback, nullptr);
if (result != kAudioHardwareNoError) {
qWarning() << "Registration of Audio Listener Failed";
}
// get initial volume
Float32 volume;
UInt32 volumedataSize = sizeof(volume);
result = AudioObjectGetPropertyData(defaultOutputDeviceID, &volumePropertyAddress, 0, nullptr, &volumedataSize, &volume);
if (result == kAudioHardwareNoError) {
this->volume = static_cast<double>(volume);
emit volumeChanged();
}
#endif
}
SystemVolume::~SystemVolume() {
#ifdef Q_OS_WIN
if (pEnumerator) {
pEnumerator->Release();
}
......@@ -50,12 +115,14 @@ SystemVolume::~SystemVolume() {
pDevice->Release();
}
if (endpointVolume) {
endpointVolume->UnregisterControlChangeNotify(&callback);
endpointVolume->Release();
}
if (client) {
client->Release();
}
CoUninitialize();
#endif
}
void SystemVolume::setVolume(double volume) {
......@@ -67,6 +134,26 @@ void SystemVolume::setVolume(double volume) {
if (endpointVolume) {
endpointVolume->SetMasterVolumeLevelScalar(static_cast<float>(volume), nullptr);
}
#endif
#ifdef Q_OS_MAC
if (defaultOutputDeviceID != kAudioDeviceUnknown) {
AudioObjectPropertyAddress volumePropertyAddress = {
kAudioDevicePropertyVolumeScalar, kAudioDevicePropertyScopeOutput, 1 /*LEFT_CHANNEL*/
};
Float32 volume = static_cast<Float32>(this->volume);
auto result = AudioObjectSetPropertyData(defaultOutputDeviceID, &volumePropertyAddress, 0, nullptr, sizeof(volume), &volume);
if (result != kAudioHardwareNoError) {
qWarning() << "Can not set system volume";
} else {
// setting right channel
volumePropertyAddress.mElement = 2 /*RIGHT_CHANNEL*/;
result = AudioObjectSetPropertyData(defaultOutputDeviceID, &volumePropertyAddress, 0, nullptr, sizeof(volume), &volume);
if (result != kAudioHardwareNoError) {
qWarning() << "Can not set system volume of right channel";
}
}
}
#endif
}
}
......@@ -14,6 +14,25 @@
#include <windows.h>
#endif
#ifdef Q_OS_MAC
#include <CoreAudio/CoreAudio.h>
#endif
#ifdef Q_OS_WIN
class Callback : public IAudioEndpointVolumeCallback {
ULONG counter = 0;
HRESULT OnNotify(AUDIO_VOLUME_NOTIFICATION_DATA *pNotify) override;
public:
~Callback();
HRESULT QueryInterface(REFIID /*riid*/, void ** /*ppvObject*/) override;
ULONG AddRef() override { return ++counter; }
ULONG Release() override { return --counter; }
};
#endif
class SystemVolume : public QObject {
Q_OBJECT
#ifdef Q_OS_WIN
......@@ -22,15 +41,15 @@ class SystemVolume : public QObject {
IAudioEndpointVolume *endpointVolume = nullptr;
WAVEFORMATEX *wformat = nullptr;
IAudioClient *client = nullptr;
Callback callback;
#endif
#ifdef Q_OS_MAC
AudioDeviceID defaultOutputDeviceID = kAudioDeviceUnknown;
#endif
double volume = -1;
Q_PROPERTY(double volume READ getVolume WRITE setVolume NOTIFY volumeChanged)
SystemVolume();
static constexpr int SystemVolumeUpdateRateInMs = 10'000;
protected:
void timerEvent(QTimerEvent *event) override;
public:
static SystemVolume &get() {
......
......@@ -3,6 +3,7 @@
#include "errornotifier.h"
#include "modelmanager.h"
#include "settings.h"
#include "settingsfilewrapper.h"
#include "slideshow.h"
#include "sortedmodelview.h"
#include "updater.h"
......@@ -250,8 +251,9 @@ int main(int argc, char *argv[]) {
qmlRegisterType<Settings>("custom.licht", 1, 0, "PopupBackground");
qRegisterMetaType<DMXChannelFilter::Operation>("Operation");
qmlRegisterUncreatableType<UserManagment>("custom.licht",1,0,"Permission",QStringLiteral("Singletone in c++"));
qmlRegisterUncreatableMetaObject(Updater::staticMetaObject,"custom.licht",1,0,"UpdaterState",QStringLiteral("Enum in c++"));
qmlRegisterUncreatableMetaObject(ControlItemData::staticMetaObject,"custom.licht",1,0,"ControlType",QStringLiteral("Enum in c++"));
qmlRegisterUncreatableMetaObject(Updater::staticMetaObject, "custom.licht", 1, 0, "UpdaterState", QStringLiteral("Enum in c++"));
qmlRegisterUncreatableMetaObject(ControlItemData::staticMetaObject, "custom.licht", 1, 0, "ControlType", QStringLiteral("Enum in c++"));
qmlRegisterUncreatableType<SettingsFileWrapper>("custom.licht", 1, 0, "SettingsFileStatus", QStringLiteral("Enum in c++"));
qRegisterMetaType<UserManagment::Permission>("Permission");
qRegisterMetaType<Modules::detail::PropertyInformation::Type>("Type");
qRegisterMetaType<Modules::ValueType>("ValueType");
......@@ -266,7 +268,13 @@ int main(int argc, char *argv[]) {
// Load Settings and ApplicationData
Settings::setLocalSettingFile(QFileInfo(QStringLiteral("settings.ini")));
Settings settings;
SettingsFileWrapper settingsFileWrapper(settings);
RemoteVolume remoteVolume(settings);
if (settings.isStartupVolumeEnabled()) {
SystemVolume::get().setVolume(settings.getStartupVolume());
}
QString file(settings.getJsonSettingsFilePath());
if (!QFile::exists(file)) {
file = QStringLiteral("QTJSONFile.json");
......@@ -278,11 +286,15 @@ int main(int argc, char *argv[]) {
QString error;
std::tie(after, error) = ApplicationData::loadData(file);
if (!error.isEmpty()) {
ErrorNotifier::showError(error);
settingsFileWrapper.setErrorMessage(error);
settingsFileWrapper.setStatus(SettingsFileWrapper::LoadingFailed);
} else {
settingsFileWrapper.setStatus(SettingsFileWrapper::Loaded);
}
} else {
ErrorNotifier::showError(QStringLiteral("No settings file found! The Lichtsteuerung starts wihout content."));
settingsFileWrapper.setStatus(SettingsFileWrapper::NoFile);
}
// nachdem die Benutzer geladen wurden, auto login durchführen
UserManagment::get()->autoLoginUser();
......@@ -324,7 +336,10 @@ int main(int argc, char *argv[]) {
CatchingErrorApplication::connect(&app, &QGuiApplication::aboutToQuit, [&]() {
Modules::ModuleManager::singletone()->controller().stop();
Audio::AudioCaptureManager::get().stopCapturingAndWait();
ApplicationData::saveData(settings.getJsonSettingsFileSavePath());
// if status is not SettingsFileWrapper::Loaded, the loading of the data failed and we should not write back an emtpy file
if (settingsFileWrapper.getStatus() == SettingsFileWrapper::Loaded) {
ApplicationData::saveData(settings.getJsonSettingsFileSavePath());
}
Driver::stopAndUnloadDriver();
if (updater.getState() == Updater::ReadyToInstall) {
updater.runUpdateInstaller();
......@@ -339,7 +354,12 @@ int main(int argc, char *argv[]) {
Driver::getCurrentDriver()->setWaitTime(std::chrono::milliseconds(settings.getUpdatePauseInMs()));
}
});
Settings::connect(&settings, &Settings::saveAs, [&](const auto &path) { ApplicationData::saveData(path); });
Settings::connect(&settings, &Settings::saveAs, [&](const auto &path) {
// if status is not SettingsFileWrapper::Loaded, the loading of the data failed and we should not write back an emtpy file
if (settingsFileWrapper.getStatus() == SettingsFileWrapper::Loaded) {
ApplicationData::saveData(path);
}
});
Modules::ModuleManager::singletone()->loadAllModulesInDir(settings.getModuleDirPath());
Settings::connect(&settings, &Settings::moduleDirPathChanged, [&]() { Modules::ModuleManager::singletone()->loadAllModulesInDir(settings.getModuleDirPath()); });
......@@ -367,6 +387,7 @@ int main(int argc, char *argv[]) {
engine.rootContext()->setContextProperty(QStringLiteral("dmxOutputValues"),&Driver::dmxValueModel);
engine.rootContext()->setContextProperty(QStringLiteral("AudioManager"), &Audio::AudioCaptureManager::get());
engine.rootContext()->setContextProperty(QStringLiteral("SlideShow"), &SlideShow::get());
engine.rootContext()->setContextProperty(QStringLiteral("SettingsFile"), &settingsFileWrapper);
engine.rootContext()->setContextProperty(QStringLiteral("System"), &SystemVolume::get());
engine.load(QUrl(QStringLiteral("qrc:/qml/main.qml")));
engine.load(QUrl(QStringLiteral("qrc:/qml/SlideShowWindow.qml")));
......
......@@ -6,10 +6,21 @@ import QtQuick.Window 2.12
import custom.licht 1.0
import "components"
Pane{
property bool visibleForUser: SwipeView.isCurrentItem
onVisibleForUserChanged: fileDialogLoader.load();
ScrollView{
property bool visibleForUser: SwipeView.isCurrentItem
onVisibleForUserChanged: {
if (visibleForUser) {
fileDialogLoader.load();
// show the scroll bar for a short time so that the user can see that you can scroll here
ScrollBar.vertical.active = true;
ScrollBar.vertical.active = false;
}
}
contentHeight: layout.implicitHeight
contentWidth: Math.max(600, width - 2 * padding)
padding: 10
GridLayout{
id: layout
anchors.left: parent.left
anchors.right: parent.right
rowSpacing: 4
......@@ -19,7 +30,7 @@ Pane{
}
RowLayout{
id: root
//enabled: UserManagment.currentUser.havePermission(Permission.CHANGE_SETTINGS_FILE_PATH)
enabled: UserManagment.currentUser.havePermission(Permission.CHANGE_SETTINGS_FILE_PATH)
Item{
Layout.fillWidth: true
Layout.preferredWidth: inputSettingsPath.implicitWidth
......@@ -282,6 +293,36 @@ Pane{
}
Label {
text: "Startup Volume"
}
RowLayout {
Layout.topMargin: -10
Layout.bottomMargin: -10
Layout.leftMargin: -7
enabled: UserManagment.currentUser.havePermission(Permission.CHANGE_STARTUP_VOLUME)
CheckBox {
text: "enabled"
checked: Settings.isStartupVolumeEnabled
onCheckedChanged: Settings.isStartupVolumeEnabled = checked
}
Label {
visible: Settings.isStartupVolumeEnabled
Layout.leftMargin: 10
Layout.preferredWidth: 100
text: "Volume: " + (Settings.startupVolume * 100).toFixed(0) + "%"
}
Slider {
visible: Settings.isStartupVolumeEnabled
from: 0
to: 1
Layout.preferredWidth: 200
value: Settings.startupVolume
onValueChanged: Settings.startupVolume = value;
}
}
}
Loader {
......@@ -322,3 +363,4 @@ Pane{
}
}
......@@ -7,11 +7,11 @@ TextInput{
color: Material.foreground
selectByMouse: true
font.pixelSize: 15
selectionColor: "lightgreen"
selectionColor: Material.textSelectionColor
property alias underlineColor : underline.color
property alias underline: underline
cursorDelegate: Rectangle{
color: "lightgreen"
color: Material.accentColor
x:parent.cursorRectangle.x
y:parent.cursorRectangle.y
width:parent.cursorRectangle.width+1
......
import QtQuick 2.2
import QtQuick 2.12
import QtQuick.Controls.Material 2.12
Rectangle{
id:underline
......@@ -9,7 +10,7 @@ Rectangle{
width: parent.contentWidth+extendetWidth
height: 2
radius: 1
color: parent.enabled?"lightgreen":"lightgrey"
color: parent.enabled ? Material.accentColor : Material.hintTextColor
//property bool hasFocus
Behavior on width {
NumberAnimation { easing.type: Easing.OutExpo; easing.amplitude: 5.0; easing.period: 2.0; duration: 800 }
......
......@@ -314,4 +314,84 @@ ApplicationWindow {
visible: ErrorNotifier.errorMessage.length !== 0
}
Popup {
Overlay.modal: ModalPopupBackground{}
closePolicy: Popup.NoAutoClose
y: 70
x: (parent.width - width) / 2
modal: true
visible: SettingsFile.status === SettingsFileStatus.LoadingFailed
onVisibleChanged: {
if (visible) {
fileDialogLoader.source = "components/SystemFileDialog.qml"
}
}
contentItem: ColumnLayout {
Label {
font.pointSize: 15
font.bold: true
text: "Failed to load settings file"
}
Label {
id: fontReference
text: SettingsFile.errorMessage
Layout.bottomMargin: 5
Layout.maximumWidth: 500
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
}
TextInput {
Layout.fillWidth: true
Layout.maximumWidth: 500
Layout.leftMargin: 1
Layout.rightMargin: 2
clip: true
color: Material.foreground
selectionColor: Material.textSelectionColor
font: fontReference.font
text: Settings.jsonSettingsFilePath
selectByMouse: true
readOnly: true
}
GridLayout {
columns: 2
rowSpacing: -5
Button {
text: "Open file in default editor"
Layout.fillWidth: true
flat: true
onClicked: SettingsFile.openFileInDefaultEditor();
}
Button {
text: "Open folder where file is"
Layout.fillWidth: true
flat: true
onClicked: SettingsFile.openFileExplorerAtFile();
}
Button {
text: "Reload selected settings file"
Layout.fillWidth: true
onClicked: SettingsFile.reload();
}
Button {
text: "Load an other settings file"
Layout.fillWidth: true
onClicked: fileDialogLoader.item.openAt(Settings.jsonSettingsFilePath, false)
}
}
}
Loader {
asynchronous: true
id: fileDialogLoader
onStatusChanged: {
if (status === Loader.Ready) {
item.callback = path => {
SettingsFile.loadFile(path);
};
}
}
}
}
}
......@@ -57,6 +57,20 @@ void Settings::remoteVolumeControl(bool enable) {
}
}
void Settings::setStartupVolumeEnabled(bool enabled) {
if (enabled != isStartupVolumeEnabled()) {
setValue(QStringLiteral("isStartupVolumeEnabled"), enabled);
emit isStartupVolumeEnabledChanged();
}
}
void Settings::setStartupVolume(double volume) {
if (volume != getStartupVolume()) {
setValue(QStringLiteral("StartupVolume"), volume);
emit startupVolumeChanged();
}
}
void Settings::setValue(const QString &key, const QVariant &value){
if (localSettings) {
if (localSettings->contains(key)) {
......
......@@ -59,6 +59,8 @@ class Settings : public QObject {
Q_PROPERTY(QString includePath READ getIncludePath WRITE setIncludePath NOTIFY includePathChanged)
Q_PROPERTY(QString computerName READ getComputerName WRITE setComputerName NOTIFY computerNameChanged)
Q_PROPERTY(bool remoteVolumeControl READ remoteVolumeControl WRITE remoteVolumeControl NOTIFY remoteVolumeControlChanged)
Q_PROPERTY(bool isStartupVolumeEnabled READ isStartupVolumeEnabled WRITE setStartupVolumeEnabled NOTIFY isStartupVolumeEnabledChanged)
Q_PROPERTY(double startupVolume READ getStartupVolume WRITE setStartupVolume NOTIFY startupVolumeChanged)
Q_PROPERTY(int theme READ getTheme WRITE setTheme NOTIFY themeChanged)
Q_PROPERTY(unsigned int updatePauseInMs READ getUpdatePauseInMs WRITE setUpdatePauseInMs NOTIFY updatePauseInMsChanged)
static inline QFileInfo localSettingsFile;
......@@ -187,6 +189,28 @@ public:
void remoteVolumeControl(bool enable);
[[nodiscard]] bool remoteVolumeControl() const { return value(QStringLiteral("remoteVolumeControl"), false).toBool(); }
/**
* @brief setStartupVolumeEnabled enables or disables the functionality described here: isStartupVolumeEnabled()
* @param enabled true if enabled, false if disabled
*/
void setStartupVolumeEnabled(bool enabled);
/**
* @brief isStartupVolumeEnabled if true, the system volume should be set to getStartupVolume() when the Lichtsteuerung starts
* @return true if should set, false otherwise
*/
[[nodiscard]] bool isStartupVolumeEnabled() const { return value(QStringLiteral("isStartupVolumeEnabled"), false).toBool(); }
/**
* @brief setStartupVolume sets the system volume that should be set when the Lichtsteuerung starts, valid values: 0.0 - 1.0
* @param volume the new startup volume
*/
void setStartupVolume(double volume);
/**
* @brief getStartupVolume the system volume that should be set when the Lichtsteuerung starts, valid values: 0.0 - 1.0
* @return the startup volume, 0.0 - 1.0
*/
[[nodiscard]] double getStartupVolume() const { return value(QStringLiteral("StartupVolume"), -.5).toDouble(); }
void setTheme(int theme) {
if (theme != getTheme()) {
setValue(QStringLiteral("theme"), theme);
......@@ -236,6 +260,8 @@ signals:
void saveAs(QString path);
void computerNameChanged();
void remoteVolumeControlChanged();
void isStartupVolumeEnabledChanged();
void startupVolumeChanged();
public slots:
};
......
#include "settingsfilewrapper.h"
#include "applicationdata.h"
#include "errornotifier.h"
#include "usermanagment.h"
#include <QDesktopServices>
#include <QUrl>
void SettingsFileWrapper::setStatus(SettingsFileWrapper::Status status) {
if (this->status != status) {
this->status = status;
emit statusChanged();
}
}
void SettingsFileWrapper::setErrorMessage(const QString &errorMessage) {
if (this->errorMessage != errorMessage) {
this->errorMessage = errorMessage;
emit errorMessageChanged();
}
}
void SettingsFileWrapper::loadFile(const QString &filepath) {
Q_ASSERT(status != Loaded);
settings.setJsonSettingsFilePath(filepath);
auto [after, error] = ApplicationData::loadData(filepath);
if (error.isEmpty()) {
after();
UserManagment::get()->autoLoginUser();
setStatus(Loaded);
} else {
setErrorMessage(error);
setStatus(LoadingFailed);
}
}
bool SettingsFileWrapper::openFileInDefaultEditor() {
return QDesktopServices::openUrl(QUrl::fromLocalFile(settings.getJsonSettingsFilePath()));
}
void SettingsFileWrapper::openFileExplorerAtFile() {
QDesktopServices::openUrl(QUrl::fromLocalFile(QFileInfo(settings.getJsonSettingsFilePath()).path()));
}
#ifndef SETTINGSFILEWRAPPER_H
#define SETTINGSFILEWRAPPER_H
#include "settings.h"
#include <QObject>
/**
* @brief The SettingsFileWrapper class wrapps the status of the settings file for QML
*/
class SettingsFileWrapper : public QObject {
Q_OBJECT
public:
enum Status { Loaded, NoFile, LoadingFailed, NotLoaded };
Q_ENUM(Status)
private:
Settings &settings;
QString errorMessage;
Q_PROPERTY(QString errorMessage READ getErrorMessage NOTIFY errorMessageChanged)
Q_PROPERTY(Status status READ getStatus NOTIFY statusChanged)
Status status = NotLoaded;
public:
explicit SettingsFileWrapper(Settings &settings) : settings(settings) {}
[[nodiscard]] Status getStatus() const { return status; }
void setStatus(Status status);
[[nodiscard]] QString getErrorMessage() const { return errorMessage; }
void setErrorMessage(const QString &errorMessage);
Q_INVOKABLE void loadFile(const QString &filepath);
Q_INVOKABLE void reload() { loadFile(settings.getJsonSettingsFilePath()); }
Q_INVOKABLE bool openFileInDefaultEditor();
Q_INVOKABLE void openFileExplorerAtFile();
signals:
void statusChanged();
void errorMessageChanged();
};
#endif // SETTINGSFILEWRAPPER_H
......@@ -82,6 +82,7 @@ public:
SAVE_SLIDE_SHOW_SETTINGS = 28,
CHANGE_SYSTEM_VOLUME = 29,
ENABLE_REMOTE_VOLUME_CONTROL = 30,
CHANGE_STARTUP_VOLUME = 31,
};
Q_ENUM(Permission)
/**
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment