Commit 190dd840 authored by Leander Schulten's avatar Leander Schulten

Merge branch 'master' into windows-release

parents 5d20b484 6a5da4c9
Pipeline #198972 passed with stages
in 7 minutes and 53 seconds
QT += qml quick networkauth network
QT += qml quick networkauth network websockets
CONFIG += c++1z force_debug_info
......@@ -22,6 +22,8 @@ SOURCES += \
audio/aubio/onsetanalysis.cpp \
audio/aubio/tempoanalysis.cpp \
audio/audioeventdata.cpp \
audio/remotevolume.cpp \
audio/systemvolume.cpp \
dmx/channel.cpp \
gui/audioeventdataview.cpp \
gui/channelprogrammeditor.cpp \
......@@ -106,6 +108,8 @@ HEADERS += \
audio/aubio/onsetanalysis.h \
audio/aubio/tempoanalysis.h \
audio/audioeventdata.h \
audio/remotevolume.h \
audio/systemvolume.h \
dmx/deviceprototype.h \
dmx/channel.h \
gui/linegeometry.h \
......
#include "applicationdata.h"
#include <QJsonDocument>
#include "errornotifier.h"
#include "idbase.h"
#include <QJsonArray>
#include "usermanagment.h"
#include "dmx/channel.h"
#include "dmx/deviceprototype.h"
#include "dmx/device.h"
#include "dmx/programmprototype.h"
#include "dmx/deviceprototype.h"
#include "dmx/programm.h"
#include "usermanagment.h"
#include "dmx/programmprototype.h"
#include "gui/controlpanel.h"
#include "gui/mapview.h"
#include <QCryptographicHash>
#include "modules/modulemanager.h"
#include "modules/programblock.h"
#include "spotify/spotify.h"
#include <QCryptographicHash>
#include <QJsonArray>
#include <QJsonDocument>
namespace ApplicationData{
QByteArray password;
namespace ApplicationData {
bool saveData(QFile &file){
if(!file.open(QIODevice::WriteOnly))return false;
bool saveData(const QString &filename) {
QFile file(filename);
if (!file.open(QIODevice::WriteOnly)) {
return false;
}
auto data = saveData();
file.write(data);
file.close();
return true;
}
template<typename Vector>
void saveIDBaseObjects(QJsonObject &o,const Vector & vector, QString entryName ){
template <typename Vector>
void saveIDBaseObjects(QJsonObject &o, const Vector &vector, const QString &entryName) {
QJsonArray array;
for(const auto & p : vector){
for (const auto &p : vector) {
QJsonObject o;
p->writeJsonObject(o);
array.append(o);
}
o.insert(entryName,array);
o.insert(entryName, array);
}
QByteArray saveData(){
qDebug()<<"SAVE DATA";
QByteArray saveData() {
qDebug() << "SAVE DATA";
QJsonObject o;
saveIDBaseObjects(o,ModelManager::get().getDevicePrototypes(),"DevicePrototypes");
saveIDBaseObjects(o,ModelManager::get().getDevices(),"Devices");
saveIDBaseObjects(o,ModelManager::get().getProgramPrototypes(),"ProgrammPrototypes");
saveIDBaseObjects(o,ModelManager::get().getPrograms(),"Programms");
saveIDBaseObjects(o,UserManagment::get()->getUsers(),"Users");
o.insert("password",QString::fromLatin1(password.toBase64()));
saveIDBaseObjects(o, ModelManager::get().getDevicePrototypes(), QStringLiteral("DevicePrototypes"));
saveIDBaseObjects(o, ModelManager::get().getDevices(), QStringLiteral("Devices"));
saveIDBaseObjects(o, ModelManager::get().getProgramPrototypes(), QStringLiteral("ProgrammPrototypes"));
saveIDBaseObjects(o, ModelManager::get().getPrograms(), QStringLiteral("Programms"));
saveIDBaseObjects(o, UserManagment::get()->getUsers(), QStringLiteral("Users"));
if (GUI::ControlPanel::getLastCreated()) {
QJsonObject u;
GUI::ControlPanel::getLastCreated()->writeJsonObject(u);
o.insert("ControlPanel",u);
o.insert(QStringLiteral("ControlPanel"), u);
}
if (GUI::MapView::getLastCreated()) {
QJsonObject u;
GUI::MapView::getLastCreated()->writeJsonObject(u);
o.insert("MapView",u);
o.insert(QStringLiteral("MapView"), u);
}
{
QJsonObject u;
Modules::ModuleManager::singletone()->writeJsonObject(u);
o.insert("ModuleManager",u);
o.insert(QStringLiteral("ModuleManager"), u);
}
{
QJsonObject u;
Modules::ProgramBlockManager::writeToJsonObject(u);
o.insert("ProgramBlockManager",u);
o.insert(QStringLiteral("ProgramBlockManager"), u);
}
{
QJsonObject u;
Spotify::get().writeToJsonObject(u);
o.insert("Spotify",u);
o.insert(QStringLiteral("Spotify"), u);
}
return QJsonDocument(o).toJson();
}
std::function<void()> loadData(QFile &file){
if(!file.open(QIODevice::ReadOnly)){
return [](){qDebug()<<"Error: can not open file";};
std::pair<std::function<void()>, QString> loadData(const QString &filename) {
QFile file(filename);
if (!file.open(QIODevice::ReadOnly)) {
return {{}, "Failed to open settings file " + file.fileName() + ". Error : " + file.errorString()};
}
auto r = loadData(file.readAll());
file.close();
return r;
}
std::function<void()> loadData(QByteArray data){
const auto o = QJsonDocument::fromJson(data).object();
for(const auto e : o["DevicePrototypes"].toArray()){
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)};
}
const auto o = doc.object();
for (const auto e : o[QStringLiteral("DevicePrototypes")].toArray()) {
ModelManager::get().addNewDevicePrototype(e.toObject());
}
for(const auto e : o["Devices"].toArray()){
for (const auto e : o[QStringLiteral("Devices")].toArray()) {
ModelManager::get().addNewDevice(e.toObject());
}
for(const auto e : o["ProgrammPrototypes"].toArray()){
for (const auto e : o[QStringLiteral("ProgrammPrototypes")].toArray()) {
ModelManager::get().addNewProgramPrototype(e.toObject());
}
for(const auto e : o["Programms"].toArray()){
for (const auto e : o[QStringLiteral("Programms")].toArray()) {
ModelManager::get().addNewProgram(e.toObject());
}
password = QByteArray::fromBase64(o["password"].toString().toLatin1());
{//USERS
for(const auto e : o["Users"].toArray()){
{ // USERS
for (const auto e : o[QStringLiteral("Users")].toArray()) {
User::createUser(e.toObject());
}
}
if(password==QCryptographicHash::hash(QString("admin").toLatin1(),QCryptographicHash::Sha3_256)){
qDebug()<<"Erfolgreich";
}else{
qDebug()<<"password : "<<password;
qDebug()<<"hash : "<<QCryptographicHash::hash(QString("admin").toLatin1(),QCryptographicHash::Sha3_256);
}
{
QJsonObject ob = o["ModuleManager"].toObject();
QJsonObject ob = o[QStringLiteral("ModuleManager")].toObject();
Modules::ModuleManager::singletone()->loadModules(ob);
}
Spotify::get().loadFromJsonObject(o["Spotify"].toObject());
password=QCryptographicHash::hash(QString("admin").toLatin1(),QCryptographicHash::Sha3_256);
return [=]() {
if (GUI::MapView::getLastCreated()) GUI::MapView::getLastCreated()->loadFromJsonObject(o["MapView"].toObject());
Modules::ProgramBlockManager::readFromJsonObject(o["ProgramBlockManager"].toObject());
if (GUI::ControlPanel::getLastCreated()) GUI::ControlPanel::getLastCreated()->loadFromJsonObject(o["ControlPanel"].toObject());
};
}
Spotify::get().loadFromJsonObject(o[QStringLiteral("Spotify")].toObject());
return {[=]() {
if (GUI::MapView::getLastCreated()) {
GUI::MapView::getLastCreated()->loadFromJsonObject(o[QStringLiteral("MapView")].toObject());
}
Modules::ProgramBlockManager::readFromJsonObject(o[QStringLiteral("ProgramBlockManager")].toObject());
if (GUI::ControlPanel::getLastCreated()) {
GUI::ControlPanel::getLastCreated()->loadFromJsonObject(o[QStringLiteral("ControlPanel")].toObject());
}
},
{}};
}
} // namespace ApplicationData
......@@ -6,12 +6,32 @@
namespace ApplicationData{
bool saveData(QFile &file);
std::function<void()> loadData(QFile &file);
/**
* @brief saveData saves all application data into the given file
* @param file The file in which the data should be written
* @return true on success, false on failure
*/
bool saveData(const QString &file);
/**
* @brief saveData saves all application data into a QByteArray
* @return The application data as data array
*/
QByteArray saveData();
std::function<void()> loadData(QByteArray data);
/**
* @brief loadData loads the application data from the file and returns a callback
* that should be called, after the QML UI is loaded
* @param file The file with the application data
* @return a callback and a error message. An error occurred, when the error message is not empty
*/
std::pair<std::function<void()>, QString> loadData(const QString &file);
/**
* @brief loadData loads the application data from the byte array and returns a callback
* that should be called, after the QML UI is loaded
* @param data the application data
* @return a callback and a error message. An error occurred, when the error message is not empty
*/
std::pair<std::function<void()>, QString> loadData(const QByteArray &data);
}
} // namespace ApplicationData
#endif // APPLICATIONDATA_H
#include "remotevolume.h"
#include "systemvolume.h"
RemoteVolume::RemoteVolume(Settings &settings) : settings(settings) {
QObject::connect(&settings, &Settings::computerNameChanged, [this]() {
if (isConnected()) {
webSocket.sendTextMessage("Name:" + this->settings.getComputerName());
}
});
QObject::connect(&settings, &Settings::remoteVolumeControlChanged, [this]() {
if (this->settings.remoteVolumeControl()) {
if (!isConnected()) {
connect();
}
} else {
webSocket.close();
}
});
QObject::connect(&webSocket, &QWebSocket::connected, [this]() {
webSocket.sendTextMessage("Name:" + this->settings.getComputerName());
webSocket.sendTextMessage("Value:" + QString::number(SystemVolume::get().getVolume()));
});
QObject::connect(&webSocket, &QWebSocket::textMessageReceived, [](const QString &message) {
bool ok;
double vol = message.toDouble(&ok);
if (ok) {
SystemVolume::get().setVolume(vol);
}
});
QObject::connect(&SystemVolume::get(), &SystemVolume::volumeChanged, [this]() {
if (webSocket.isValid()) {
webSocket.sendTextMessage("Value:" + QString::number(SystemVolume::get().getVolume()));
}
});
if (settings.remoteVolumeControl()) {
connect();
}
}
void RemoteVolume::connect() {
webSocket.open(QUrl(QStringLiteral("wss://orga.symposion.hilton.rwth-aachen.de/volumeClient")));
}
#ifndef REMOTEVOLUME_H
#define REMOTEVOLUME_H
#include <QWebSocket>
#include <settings.h>
class RemoteVolume
{
QWebSocket webSocket;
Settings &settings;
public:
RemoteVolume(Settings &settings);
void connect();
[[nodiscard]] bool isConnected() const { return webSocket.isValid(); }
};
#endif // REMOTEVOLUME_H
#include "systemvolume.h"
SystemVolume::SystemVolume() {
#ifdef Q_OS_WIN
HRESULT hr;
hr = CoInitialize(nullptr);
if (hr < 0) {
return;
}
// Get enumerator for audio endpoint devices.
hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_INPROC_SERVER, __uuidof(IMMDeviceEnumerator), reinterpret_cast<void **>(&pEnumerator));
if (hr < 0) {
return;
}
// Get peak meter for default audio-rendering device.
hr = pEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &pDevice);
if (hr < 0) {
return;
}
hr = pDevice->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_ALL, nullptr, reinterpret_cast<void **>(&endpointVolume));
if (hr < 0) {
return;
}
#endif
startTimer(SystemVolumeUpdateRateInMs);
timerEvent(nullptr);
}
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();
}
}
}
#endif
}
SystemVolume::~SystemVolume() {
if (pEnumerator) {
pEnumerator->Release();
}
if (pDevice) {
pDevice->Release();
}
if (endpointVolume) {
endpointVolume->Release();
}
if (client) {
client->Release();
}
CoUninitialize();
}
void SystemVolume::setVolume(double volume) {
volume = std::clamp(volume, 0., 1.);
if (volume != this->volume) {
this->volume = volume;
emit volumeChanged();
#ifdef Q_OS_WIN
if (endpointVolume) {
endpointVolume->SetMasterVolumeLevelScalar(static_cast<float>(volume), nullptr);
}
#endif
}
}
#ifndef SYSTEMVOLUME_H
#define SYSTEMVOLUME_H
#include <QObject>
#ifdef Q_OS_WIN
// windows code from here: http://www.rohitab.com/discuss/topic/43988-increasedecreasemute-the-master-volume-in-windows/
#include <audioclient.h>
#include <conio.h>
#include <endpointvolume.h>
#include <gdiplus.h>
#include <iostream>
#include <mmdeviceapi.h>
#include <windows.h>
#endif
class SystemVolume : public QObject {
Q_OBJECT
#ifdef Q_OS_WIN
IMMDeviceEnumerator *pEnumerator = nullptr;
IMMDevice *pDevice = nullptr;
IAudioEndpointVolume *endpointVolume = nullptr;
WAVEFORMATEX *wformat = nullptr;
IAudioClient *client = nullptr;
#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() {
static SystemVolume sv;
return sv;
}
~SystemVolume() override;
[[nodiscard]] double getVolume() const { return volume; }
void setVolume(double volume);
signals:
void volumeChanged();
};
#endif // SYSTEMVOLUME_H
......@@ -10,8 +10,9 @@
/**
* @brief Eine ID Klasse, die eindeutige IDs erstellt
*/
class ID{
friend class SyncService;
class ID {
friend class UserManagment;
public:
typedef long long value_type;
private:
......@@ -41,6 +42,4 @@ public:
void writeJsonObject(QJsonObject &o)const;
};
#endif // IDGENERATOR_H
......@@ -8,6 +8,8 @@
#include "updater.h"
#include "usermanagment.h"
#include "audio/audiocapturemanager.h"
#include "audio/remotevolume.h"
#include "audio/systemvolume.h"
#include "dmx/HardwareInterface.h"
#include "dmx/channel.h"
#include "dmx/device.h"
......@@ -217,6 +219,8 @@ int main(int argc, char *argv[]) {
// auto defaultFormat = QSurfaceFormat::defaultFormat();
// defaultFormat.setSamples(8);
// QSurfaceFormat::setDefaultFormat(defaultFormat);
QCoreApplication::setOrganizationName(QStringLiteral("Turmstraße 1 e.V."));
QCoreApplication::setOrganizationDomain(QStringLiteral("hilton.rwth-aachen.de"));
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
CatchingErrorApplication app(argc, argv);
QQmlApplicationEngine engine;
......@@ -262,16 +266,24 @@ int main(int argc, char *argv[]) {
// Load Settings and ApplicationData
Settings::setLocalSettingFile(QFileInfo(QStringLiteral("settings.ini")));
Settings settings;
QFile file(settings.getJsonSettingsFilePath());
if(!file.exists()){
file.setFileName(QStringLiteral("QTJSONFile.json"));
RemoteVolume remoteVolume(settings);
QString file(settings.getJsonSettingsFilePath());
if (!QFile::exists(file)) {
file = QStringLiteral("QTJSONFile.json");
}
if(file.exists()){
file.copy(file.fileName()+"_"+QDateTime::currentDateTime().toString(QStringLiteral("dd.MM.yyyy HH.mm.ss")));
std::function<void()> after;
if (QFile::exists(file)) {
QFile::copy(file, file + "_" + QDateTime::currentDateTime().toString(QStringLiteral("dd.MM.yyyy HH.mm.ss")));
// load application data
QString error;
std::tie(after, error) = ApplicationData::loadData(file);
if (!error.isEmpty()) {
ErrorNotifier::showError(error);
}
} else {
ErrorNotifier::showError(QStringLiteral("No settings file found! The Lichtsteuerung starts wihout content."));
}
auto after = ApplicationData::loadData(file);
// nachdem die Benutzer geladen wurden, auto login durchführen
UserManagment::get()->autoLoginUser();
......@@ -312,8 +324,7 @@ int main(int argc, char *argv[]) {
CatchingErrorApplication::connect(&app, &QGuiApplication::aboutToQuit, [&]() {
Modules::ModuleManager::singletone()->controller().stop();
Audio::AudioCaptureManager::get().stopCapturingAndWait();
QFile savePath(settings.getJsonSettingsFileSavePath());
ApplicationData::saveData(savePath);
ApplicationData::saveData(settings.getJsonSettingsFileSavePath());
Driver::stopAndUnloadDriver();
if (updater.getState() == Updater::ReadyToInstall) {
updater.runUpdateInstaller();
......@@ -328,10 +339,7 @@ int main(int argc, char *argv[]) {
Driver::getCurrentDriver()->setWaitTime(std::chrono::milliseconds(settings.getUpdatePauseInMs()));
}
});
Settings::connect(&settings, &Settings::saveAs, [&](const auto &path) {
QFile saveFile(settings.getJsonSettingsFileSavePath());
ApplicationData::saveData(saveFile);
});
Settings::connect(&settings, &Settings::saveAs, [&](const auto &path) { ApplicationData::saveData(path); });
Modules::ModuleManager::singletone()->loadAllModulesInDir(settings.getModuleDirPath());
Settings::connect(&settings, &Settings::moduleDirPathChanged, [&]() { Modules::ModuleManager::singletone()->loadAllModulesInDir(settings.getModuleDirPath()); });
......@@ -359,13 +367,15 @@ 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("System"), &SystemVolume::get());
engine.load(QUrl(QStringLiteral("qrc:/qml/main.qml")));
engine.load(QUrl(QStringLiteral("qrc:/qml/SlideShowWindow.qml")));
// laden erst nach dem laden des qml ausführen
after();
if (after) {
after();
}
// Treiber laden
//#define USE_DUMMY_DRIVER
......
......@@ -149,9 +149,9 @@ public:
*/
Q_INVOKABLE void save(){
if(settings != nullptr){
QFile savePath(settings->getJsonSettingsFilePath());
if(savePath.exists()){
savePath.copy(savePath.fileName()+"_"+QDateTime::currentDateTime().toString(QStringLiteral("dd.MM.yyyy HH.mm.ss")));
QString savePath(settings->getJsonSettingsFilePath());
if (QFile::exists(savePath)) {
QFile::copy(savePath, savePath + "_" + QDateTime::currentDateTime().toString(QStringLiteral("dd.MM.yyyy HH.mm.ss")));
}
ApplicationData::saveData(savePath);
}else {
......
......@@ -85,5 +85,6 @@
<file>qml/components/CenteredPopup.qml</file>
<file>qml/components/ModalPopupBackground.qml</file>
<file>qml/SlideShowWindow.qml</file>
<file>qml/components/SystemFileDialog.qml</file>
</qresource>
</RCC>
......@@ -122,7 +122,7 @@ ModelView{
Layout.margins: 3
spacing: 0
id: pinComp
property int currentDMXChannel: deviceModelView.currentModelData ? deviceModelView.currentModelData.startDMXChannel : 0
property int currentDMXChannel: deviceModelView.currentModelData ? deviceModelView.currentModelData.startDMXChannel : -1
onCurrentDMXChannelChanged: {
let v = currentDMXChannel;
for(let i = 8; i >= 0; --i){
......@@ -175,7 +175,7 @@ ModelView{
hoverEnabled: true
ToolTip.delay: 1000
ToolTip.visible: containsMouse
ToolTip.text: index === 0 ? "Referenz" : "2^" + (index - 1) + " = " + Math.pow(2, index - 1)
ToolTip.text: index === 0 ? "Referenz" : Math.pow(2, index - 1)
}
}
Text{
......@@ -183,7 +183,7 @@ ModelView{
anchors.topMargin: 3
anchors.horizontalCenter: switchRect.horizontalCenter
color: Material.foreground
text: index === 0 ? switchRect.parent.on ? "On" : "Off" : (index - 1)
text: index === 0 ? switchRect.parent.on ? "On" : "Off" : index
}
}
}
......
......@@ -14,6 +14,7 @@ Item{
Layout.fillWidth: true
Layout.fillHeight: true
Layout.preferredHeight: model.rowCount() * 50
Layout.minimumHeight: 100
clip: true
highlightMoveDuration: 100
highlightResizeDuration: 100
......@@ -101,8 +102,12 @@ Item{
}else if(modelData===UserManagment.currentUser){
UserManagment.logout()
}else{
dialog.user = modelData;
dialog.visible = true;
// check if we can login the user without password
if (!UserManagment.login(modelData)) {
// we need a password to login
dialog.user = modelData;
dialog.visible = true;
}
}
}
}
......
......@@ -27,6 +27,7 @@ Popup {
}
TabButton{
text: "Auto Login"
enabled: user !== UserManagment.defaultUser
}
}
contentItem: SwipeView{
......
......@@ -2,23 +2,24 @@ import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Controls.Material 2.12
import QtQuick.Layouts 1.12
import QtQuick.Dialogs 1.3 as SystemDialog
import QtQuick.Window 2.12
import custom.licht 1.0
import "components"
Pane{
property bool visibleForUser: SwipeView.isCurrentItem
onVisibleForUserChanged: fileDialogLoader.load();
GridLayout{
anchors.left: parent.left
anchors.right: parent.right
rowSpacing: 10
rowSpacing: 4
columns: 2
Label{
text: "Settings file path:"
}
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
......@@ -40,8 +41,8 @@ Pane{
Layout.preferredHeight: implicitHeight - 15
text: "Save as"
onClicked: {
fileDialog.openAt(Settings.jsonSettingsFilePath, false, false);
fileDialog.callback = function(file){
fileDialogLoader.item.openAt(Settings.jsonSettingsFilePath, false, false);
fileDialogLoader.item.callback = function(file){
Settings.setJsonSettingsFilePath(file, false);
};
}
......@@ -52,8 +53,8 @@ Pane{
Layout.preferredHeight: implicitHeight - 15
text: "Load from"
onClicked: {
fileDialog.openAt(Settings.jsonSettingsFilePath, false);
fileDialog.callback = function(file){
fileDialogLoader.item.openAt(Settings.jsonSettingsFilePath, false);
fileDialogLoader.item.callback = function(file){
if(Settings.setJsonSettingsFilePath(file, true)){
popupChangedSettingsFile.visible = true;
}else{
......@@ -74,7 +75,7 @@ Pane{
folder: false
path: Settings.driverFilePath
onPathChanged: {Settings.driverFilePath = path;path = Settings.driverFilePath;}
fileChooser: fileDialog
fileChooser: fileDialogLoader.item
}
Label{
......@@ -100,7 +101,7 @@ Pane{
folder: true
path: Settings.moduleDirPath
onPathChanged: {Settings.moduleDirPath = path;path = Settings.moduleDirPath;}
fileChooser: fileDialog
fileChooser: fileDialogLoader.item
}
Label{
......@@ -113,7 +114,7 @@ Pane{
folder: true
path: Settings.compilerPath
onPathChanged: {Settings.compilerPath = path;path = Settings.compilerPath;}
fileChooser: fileDialog
fileChooser: fileDialogLoader.item
}
Label{
......@@ -126,7 +127,7 @@ Pane{
folder: true
path: Settings.includePath
onPathChanged: {Settings.includePath = path;path = Settings.includePath;}
fileChooser: fileDialog
fileChooser: fileDialogLoader.item
}
Label{
......@@ -177,6 +178,7 @@ Pane{
text: "SlideShow:"
}
RowLayout{
Layout.bottomMargin: -10
Button{
enabled: SlideShow.hasImages
text: SlideShow.windowVisibility !== Window.Hidden ? "Hide" : "Show"
......@@ -221,7 +223,7 @@ Pane{
folder: true
path: SlideShow.path
onPathChanged: SlideShow.path = path;
fileChooser: fileDialog