New upstream version 2.0.1+ds
authorIOhannes m zmölnig (Debian/GNU) <umlaeute@debian.org>
Wed, 30 Aug 2023 07:23:08 +0000 (09:23 +0200)
committerIOhannes m zmölnig (Debian/GNU) <umlaeute@debian.org>
Wed, 30 Aug 2023 07:23:08 +0000 (09:23 +0200)
151 files changed:
CMakeLists.txt
LICENSE.md
build
build-aux/flatpak/org.jacktrip.JackTrip.json
docs/Build/Linux.md
docs/VirtualStudio.md [new file with mode: 0644]
docs/changelog.yml
docs/images/jacktrip_virtual_studio.png [new file with mode: 0644]
docs/index.md
externals/Simple-FFT/LICENSE.md [new file with mode: 0644]
externals/Simple-FFT/README.md [new file with mode: 0644]
externals/Simple-FFT/include/simple_fft/check_fft.hpp [new file with mode: 0644]
externals/Simple-FFT/include/simple_fft/copy_array.hpp [new file with mode: 0644]
externals/Simple-FFT/include/simple_fft/error_handling.hpp [new file with mode: 0644]
externals/Simple-FFT/include/simple_fft/fft.h [new file with mode: 0644]
externals/Simple-FFT/include/simple_fft/fft.hpp [new file with mode: 0644]
externals/Simple-FFT/include/simple_fft/fft_impl.hpp [new file with mode: 0644]
externals/Simple-FFT/include/simple_fft/fft_settings.h [new file with mode: 0644]
jacktrip.pro
linux/README.md
linux/flatpak/org.jacktrip.JackTrip.Devel.yml [deleted file]
linux/flatpak/org.jacktrip.JackTrip.Devel.yml.j2 [deleted file]
linux/flatpak/org.jacktrip.JackTrip.yml [deleted file]
linux/qt/1d21c39.diff [deleted file]
macos/JackTrip.app_template/Contents/Info.plist
macos/assemble_app.sh
macos/entitlements.plist
macos/meson_native_minversion_10.13.ini [new file with mode: 0644]
macos/meson_native_minversion_10.14.ini [new file with mode: 0644]
macos/meson_native_universal.ini [new file with mode: 0644]
macos/meson_native_universal_10.13.ini [new file with mode: 0644]
macos/meson_native_universal_10.14.ini [new file with mode: 0644]
macos/qt/configure.json.patch [deleted file]
meson.build
meson_options.txt
mkdocs.yml
releases/edge/mac-manifests.json
releases/edge/win-manifests.json
releases/stable/linux-manifests.json
releases/stable/mac-manifests.json
releases/stable/win-manifests.json
src/Analyzer.cpp
src/Analyzer.h
src/AudioInterface.cpp
src/AudioInterface.h
src/AudioTester.cpp
src/AudioTester.h
src/Compressor.cpp
src/Effects.h
src/JackAudioInterface.cpp
src/JackTrip.cpp
src/JackTrip.h
src/JackTripWorker.cpp
src/JitterBuffer.cpp
src/JitterBuffer.h
src/Limiter.cpp
src/Limiter.h
src/Meter.cpp
src/Monitor.cpp
src/PacketHeader.cpp
src/Reverb.cpp
src/RingBuffer.cpp
src/RtAudioInterface.cpp
src/RtAudioInterface.h
src/StereoToMono.cpp
src/Tone.cpp
src/UdpDataProtocol.cpp
src/Volume.cpp
src/gui/AppIcon.qml [new file with mode: 0644]
src/gui/AudioInterfaceMode.h
src/gui/AudioSettings.qml
src/gui/Browse.qml
src/gui/ChangeDevices.qml [new file with mode: 0644]
src/gui/Connected.qml
src/gui/DeviceControls.qml [new file with mode: 0644]
src/gui/DeviceControlsGroup.qml [new file with mode: 0644]
src/gui/DeviceRefreshButton.qml [new file with mode: 0644]
src/gui/DeviceWarning.qml [new file with mode: 0644]
src/gui/Failed.qml
src/gui/FeedbackSurvey.qml [new file with mode: 0644]
src/gui/FirstLaunch.qml
src/gui/Footer.qml [new file with mode: 0644]
src/gui/InfoTooltip.qml [new file with mode: 0644]
src/gui/JTVS.png
src/gui/Login.qml
src/gui/Meter.qml
src/gui/MeterBars.qml
src/gui/Permissions.qml [new file with mode: 0644]
src/gui/Recommendations.qml [new file with mode: 0644]
src/gui/SectionHeading.qml
src/gui/Settings.qml
src/gui/Setup.qml
src/gui/Studio.qml
src/gui/VolumeSlider.qml [new file with mode: 0644]
src/gui/Web.qml [new file with mode: 0644]
src/gui/WebEngine.qml [new file with mode: 0644]
src/gui/WebNull.qml [new file with mode: 0644]
src/gui/WebSocketTransport.cpp [new file with mode: 0644]
src/gui/WebSocketTransport.h [new file with mode: 0644]
src/gui/WebView.qml [new file with mode: 0644]
src/gui/about.cpp
src/gui/about.ui
src/gui/ethernet.png [deleted file]
src/gui/ethernet.svg [new file with mode: 0644]
src/gui/expand_less.svg [new file with mode: 0644]
src/gui/expand_more.svg [new file with mode: 0644]
src/gui/externalMic.svg [new file with mode: 0644]
src/gui/language.svg [new file with mode: 0644]
src/gui/networkCheck.svg [new file with mode: 0644]
src/gui/ohno.png [deleted file]
src/gui/qjacktrip.cpp
src/gui/qjacktrip.qrc
src/gui/sentiment_very_dissatisfied.svg [new file with mode: 0644]
src/gui/speed.svg [new file with mode: 0644]
src/gui/star.svg [new file with mode: 0644]
src/gui/virtualstudio.cpp
src/gui/virtualstudio.h
src/gui/vs.qml
src/gui/vsApi.cpp
src/gui/vsApi.h
src/gui/vsAudio.cpp [new file with mode: 0644]
src/gui/vsAudio.h [new file with mode: 0644]
src/gui/vsAudioInterface.cpp [deleted file]
src/gui/vsAudioInterface.h [deleted file]
src/gui/vsAuth.cpp
src/gui/vsAuth.h
src/gui/vsDeeplink.cpp [new file with mode: 0644]
src/gui/vsDeeplink.h [new file with mode: 0644]
src/gui/vsDevice.cpp
src/gui/vsDevice.h
src/gui/vsDeviceCodeFlow.cpp
src/gui/vsInit.cpp [deleted file]
src/gui/vsInit.h [deleted file]
src/gui/vsServerInfo.cpp
src/gui/vsServerInfo.h
src/gui/vsUrlHandler.cpp [deleted file]
src/gui/vsUrlHandler.h [deleted file]
src/gui/vsWebSocket.cpp
src/gui/vsWebSocket.h
src/gui/vsftux.qml [deleted file]
src/gui/warning.svg [new file with mode: 0644]
src/jacktrip_globals.h
src/main.cpp
subprojects/rtaudio.wrap
win/build_installer.bat
win/files.wxs [deleted file]
win/jack.pc [new file with mode: 0644]
win/meson.build
win/qt5.wxs [new file with mode: 0644]
win/qt6-noguids.wxs [new file with mode: 0644]
win/qt6.wxs [new file with mode: 0644]

index e0d803f16fb1de2af86159251d03bddd27646bd6..a4e80a86b45cb0647468cf1f73778e69a6ca269b 100644 (file)
@@ -188,14 +188,13 @@ if (NOT nogui)
       src/gui/vsApi.cpp
       src/gui/vsAuth.cpp
       src/gui/vsDeviceCodeFlow.cpp
-      src/gui/vsInit.cpp
+      src/gui/vsDeeplink.cpp
       src/gui/vsQuickView.cpp
       src/gui/vsServerInfo.cpp
       src/gui/vsPing.cpp
       src/gui/vsPinger.cpp
       src/gui/vsDevice.cpp
-      src/gui/vsAudioInterface.cpp
-      src/gui/vsUrlHandler.cpp
+      src/gui/vsAudio.cpp
       src/gui/vsWebSocket.cpp
       src/gui/vsPermissions.cpp
       src/gui/qjacktrip.qrc
index 517bd0839d45dc5f93a62ece6194a6eb5572029f..39b5c1fc293e9648604a443245aab74b35df74aa 100644 (file)
@@ -1,10 +1,14 @@
 # JackTrip License
 
-Copyright © 2020 Juan-Pablo Caceres, Chris Chafe.  
-SoundWIRE group at CCRMA, Stanford University.  
-Graphical user interface originally released as QJackTrip, 
+Copyright © 2008-2020 Juan-Pablo Caceres, Chris Chafe.
+SoundWIRE group at CCRMA, Stanford University.
+
+Classic mode graphical user interface originally released as QJackTrip,
 Copyright © 2020 Aaron Wyatt
 
+Virtual Studio interface and integration
+Copyright © 2022-2023 JackTrip Labs, Inc.
+
 JackTrip project consists of files under MIT and GPL licenses, indicated in the
 header of individual files. Early versions of JackTrip were licensed under MIT.
 
diff --git a/build b/build
index 0e9c12813c3ea005c9f661337e650437f970f1ec..351873ebb71d42b86534e3cf898a5d08a81dc4ca 100755 (executable)
--- a/build
+++ b/build
@@ -112,10 +112,10 @@ if [[ $platform == 'linux' ]]; then
     echo "Using qmake-qt5"
     QCMD=qmake-qt5
   elif hash qmake 2>/dev/null; then #in case qt was compiled by user
-  echo "Using qmake"
-  QCMD=qmake
-fi
-MCMD=make
+    echo "Using qmake"
+    QCMD=qmake
+  fi
+  MCMD=make
 elif [[ $platform == 'macosx' ]]; then
   QCMD=qmake
   # if qmake is not in path, try homebrew qt5
@@ -145,6 +145,15 @@ fi
 QSPEC=`$QCMD -query | grep QMAKE_SPEC`
 QSPEC=${QSPEC##QMAKE_SPEC:}
 
+# check spec for windows to update make command
+if [[ $QSPEC == "win32-msvc" ]]; then
+  MCMD=nmake
+  JOM=$(which jom.exe)
+  if [[ "x$JOM" != "x" ]]; then
+    MCMD=$JOM
+  fi
+fi
+
 # check for RtAudio
 if [[ $RTAUDIO == 1 ]]; then
   pkg-config --exists rtaudio 2> /dev/null
@@ -183,7 +192,11 @@ $QCMD -spec $QSPEC $CONFIG "${UNKNOWN_OPTIONS[@]}" $PRO_FILE
 if [[ $clean == 1 ]]; then
   $MCMD clean
 fi
-$MCMD -j$jobs release
+if [[ "$MCMD" == "nmake" ]]; then
+  $MCMD release
+else
+  $MCMD -j$jobs release
+fi
 if [[ "$install" == 1 ]]; then
   echo "*** Installing JackTrip ***"
   echo "We need elevated privileges to install JackTrip in the system location"
index 11ceb9294e6f889b3e645551f62d5ade9b6230f2..bfbfef09208ae248062d80ae2ca904e694c96383 100644 (file)
@@ -1,16 +1,20 @@
 {
     "app-id": "org.jacktrip.JackTrip",
     "runtime": "org.kde.Platform",
-    "runtime-version": "5.15-22.08",
+    "runtime-version": "6.4",
     "sdk": "org.kde.Sdk",
+    "base": "io.qt.qtwebengine.BaseApp",
+    "base-version": "6.4",
     "command": "jacktrip",
     "finish-args": [
         "--share=ipc",
-        "--socket=x11",
+        "--socket=wayland",
+        "--socket=fallback-x11",
         "--device=dri",
         "--share=network",
         "--filesystem=xdg-run/pipewire-0",
-        "--env=PIPEWIRE_LATENCY=256/48000",
+        "--env=PIPEWIRE_LATENCY=128/48000",
+        "--env=QML_IMPORT_PATH=/app/qml",
         "--env=QT_QUICK_CONTROLS_STYLE=universal"
     ],
     "cleanup": [
             "name": "jacktrip",
             "buildsystem": "meson",
             "config-opts": [
-                "-Dbuildtype=debugoptimized"
+                "-Dbuildtype=debugoptimized",
+                "-Dpkg_config_path=/app/lib/x86_64-linux-gnu/pkgconfig"
             ],
             "sources": [
                 {
-                    "type": "git",
-                    "disable-submodules": true,
-                    "url": "https://github.com/jacktrip/jacktrip/"
+                    "type": "dir",
+                    "path": "../../"
                 }
             ]
         }
     ]
-}
\ No newline at end of file
+}
index c8d3af86fa32e6bec4a5c9acfaa3e5a8f3ebe8ff..4b56d37e349ae444fafc9384cd9ccff4403bd2d1 100644 (file)
@@ -7,28 +7,45 @@ directory of the project.
 
 ## Dependencies
 - C++ compiler
-- Qt5
+- Meson
+- Qt5, or Qt6 (required for Virtual Studio)
 
 Optional:
 
-- JACK (preferred) or RtAudio (not fully functional)
+- JACK (preferred) or RtAudio (for clients only)
 - help2man for generating the manpage
 
-### Fedora
+### Fedora (Qt5)
 ```sh
 dnf install qt5-qtbase-devel qt5-qtnetworkauth-devel qt5-qtwebsockets-devel qt5-qtquickcontrols2-devel qt5-qtsvg-devel
 dnf groupinstall "C Development Tools and Libraries"
 dnf groupinstall "Development Tools"
-dnf install "pkgconfig(jack)" rtaudio-devel git help2man
+dnf install "pkgconfig(jack)" rtaudio-devel git help2man python3-jinja2
+```
+
+### Fedora (Qt6)
+```sh
+dnf install qt6-qtbase-devel qt5-qtnetworkauth-devel qt5-qtwebsockets-devel qt5-qtquickcontrols2-devel qt5-qtsvg-devel qt6-qtwebengine-devel qt6-qtwebchannel-devel qt6-qt5compat-devel
+dnf groupinstall "C Development Tools and Libraries"
+dnf groupinstall "Development Tools"
+dnf install "pkgconfig(jack)" rtaudio-devel git help2man python3-jinja2
 ```
 
 Clone the git repo with submodules and run `./build install` in the project
 directory or use QtCreator to compile.
 
-### Ubuntu and Debian/Raspbian
+### Ubuntu and Debian/Raspbian (Qt5)
+```sh
+apt install --no-install-recommends build-essential autoconf automake libtool make libjack-jackd2-dev git help2man python3-jinja2
+apt install qtbase5-dev qtbase5-dev-tools qtchooser qt5-qmake qttools5-dev libqt5svg5-dev libqt5websockets5-dev qtdeclarative5-dev qtquickcontrols2-5-dev
+```
+
+### Ubuntu and Debian/Raspbian (Qt6)
 ```sh
-apt install --no-install-recommends build-essential autoconf automake libtool make libjack-jackd2-dev git help2man
-apt install qtbase5-dev qtbase5-dev-tools qtchooser qt5-qmake qttools5-dev libqt5svg5-dev libqt5networkauth5-dev libqt5websockets5-dev qtdeclarative5-dev qtquickcontrols2-5-dev
+apt install --no-install-recommends build-essential autoconf automake libtool make libjack-jackd2-dev git help2man libclang-dev libdbus-1-dev libdbus-1-dev python3-jinja2
+apt install qt6-base-dev qt6-base-dev-tools qmake6 qt6-tools-dev qt6-declarative-dev qt6-webengine-dev qt6-webview-dev qt6-webview-plugins libqt6svg6-dev libqt6websockets6-dev libgl1-mesa-dev
+# for GUI builds
+apt install libfreetype6-dev libxi-dev libxkbcommon-dev libxkbcommon-x11-dev libx11-xcb-dev libdrm-dev libglu1-mesa-dev libwayland-dev libwayland-egl1-mesa libgles2-mesa-dev libwayland-server0 libwayland-egl-backend-dev libxcb1-dev libxext-dev libfontconfig1-dev libxrender-dev libxcb-keysyms1-dev libxcb-image0-dev libxcb-shm0-dev libxcb-icccm4-dev '^libxcb.*-dev' libxcb-render-util0-dev libxcomposite-dev libgtk-3-dev
 apt install librtaudio-dev # if building with RtAudio
 ```
 
@@ -37,9 +54,7 @@ directory or use QtCreator to compile.
 
 ### Building and Installation instructions
 
-For other Linux distributions, install the dependencies listed above. Clone the
-git repo and run `./build install` (or `./build` to skip installation) in the
-project directory, or use QtCreator to compile.
+For other Linux distributions, install the dependencies listed above.
 
 To clone the repo in the Terminal:
 ```sh
@@ -52,65 +67,59 @@ Next, navigate to the cloned repository:
 $ cd jacktrip
 ```
 
-JackTrip provides a build script designed to simplify the build process and
-providing a number of options. To list them, run:
-```sh
-$ ./build -h
-```
-This should print:
-```sh
-usage:
- ./build [noclean nojack rtaudio nogui static install [-config static]]
+JackTrip uses meson. To configure a build directory, use `meson setup` with
+the following parameters:
 
- options:
- noclean - do not run "make clean" first
- nojack - build without jack
- rtaudio - build with rtaudio
- nogui - build without the gui
- static - build with static libraries
- weakjack - build with weak linking of jack libraries
- install - install jacktrip in system location (uses sudo)
-```
+* `rtaudio`: Enabled this to build with support for the RtAudio backend.
+  If an existing installation is found, it will be used. Otherwise, meson
+  uses a subproject to download and install the latest supported release.
+  JackTrip supports both RtAudio v5 and v6.
 
-To just compile JackTrip using the build script, run:
-```sh
-$ ./build
-$ cd builddir
-$ ls
-```
+* `jack`: Enable this to link directly with libraries for the Jack Audio
+  Toolkit. JackTrip will fail if these cannot be found at runtime.
 
-You should see a `jacktrip` in this folder.
+* `weakjack`: Enable this to build with support for the Jack Audio Toolkit,
+  with support for dynamically loading the libraries at runtime. JackTrip
+  will still work if they cannot be found. This requires `rtaudio` and
+  uses a git submodule.
 
-To compile **and install** using the build script, run:
-```sh
-$ ./build install
-# enter your password when prompted
-```
+* `nogui`: Build without support for a graphical user interface (this
+  also disables support for Virtual Studio). Only basic command line
+  features will be included. This requires fewer dependencies, and
+  supports static builds.
 
-If the build script doesn't work, try running qmake directly. You'd need to have
-qmake in your `$PATH`. Then you can build with:
+* `novs`: Build without support for Virtual Studio. Virtual Studio
+  requires Qt6 with the WebEngine and WebChannel libraries installed.
 
-```sh
-$ mkdir builddir
-$ cd builddir
-$ qmake ../jacktrip.pro
-$ make release
-$ sudo make release-install # to install JackTrip system-wide
-```
+* `vsftux`: Skip the "Yes" / "No" first time run screen asking users if
+  they would like to use the Virtual Studio interface. Classic mode can
+  still be selected at startup, and can be used at any time.
+
+* `noupdater`: Build without support for automatic updates. This is
+  implied and has no effect when building for Linux.
+
+* `nofeedback`: Build without support for feedback detection. This
+  feature is optional, and uses the SimpleFFT third party library.
 
-You can pass configuration options to qmake with `-config <option>`, e.g. `qmake
--config nogui ../jacktrip.pro`. Relevant options include:
+* `profile`: Choose build profile / Sets desktop id accordingly
 
-- `-config rtaudio` - build with rtaudio support
-- `-config nogui` - build without the GUI
-- `-config static` - build with static libraries
-- `-config weakjack` - build using weak linking of jack
-- `-config nojack` - build without jack support
+* `qtversion`: Choose to build with either Qt5 or Qt6
 
-To build using QtCreator:
+For example:
+```sh
+$ meson setup -Drtaudio=enabled builddir
+```
+
+To build JackTrip, run:
+```sh
+$ meson compile -C builddir
+```
 
-  * Open jacktrip.pro using QtCreator
-  * Choose a correctly configured Kit
+To install JackTrip, run:
+```sh
+$ meson install -C builddir
+# enter your password when prompted
+```
 
 ### Verification
 
@@ -128,7 +137,7 @@ $ ./jacktrip -v
 If you see something like this, you have successfully installed JackTrip:
 
 ```
-JackTrip VERSION: 1.x.x
+JackTrip VERSION: 2.x.x
 Copyright (c) 2008-2020 Juan-Pablo Caceres, Chris Chafe.
 SoundWIRE group at CCRMA, Stanford University
 ```
diff --git a/docs/VirtualStudio.md b/docs/VirtualStudio.md
new file mode 100644 (file)
index 0000000..2bd2a0f
--- /dev/null
@@ -0,0 +1,26 @@
+# Virtual Studio
+
+Virtual Studio is the easiest way to get started with JackTrip. By leveraging cloud-based edge computing, realtime video and lossless audio transmits at close to the speed of light, enabling musicians to play and sing together in sync. JackTrip Virtual Studio stands out from other real-time music collaboration technologies with unsurpassed scalability supporting hundreds of musicians in a single session.
+
+![Virtual Studio](images/jacktrip_virtual_studio.png)
+
+## Getting Started
+
+Performing together online is as easy as:
+1. Sign up for a free account
+2. Start your Virtual Studio
+3. Invite your friends and play
+
+[Click here to start jamming!](https://app.jacktrip.org/)
+
+## Learn More
+
+You can find more information about this product on our [website](https://www.jacktrip.com/).
+
+## Community
+
+Join other JackTrippers on our [community forum](https://community.jacktrip.org/).
+
+## Help
+
+Having difficulties getting set up? Check out our [troubleshooting guides](https://help.jacktrip.org/hc/en-us) or [reach out to the team](https://help.jacktrip.org/hc/en-us/requests/new).
index 713e75725921afd9c710bb29e1141ad9503acb4c..2419558418f4c0339ba2de7a36ea412812282b30 100644 (file)
@@ -1,3 +1,31 @@
+- Version: "2.0.1"
+  Date: 2023-08-29
+  Description:
+  - (fixed) VS Mode Mix to Mono setting was sending a buzzing noise
+  - (fixed) VS Mode Logout followed by Login was breaking network stats
+  - (fixed) Device names with special characters work again
+- Version: "2.0.0"
+  Date: 2023-08-28
+  Description:
+  - (added) VS Mode integrated video and other UI updates
+  - (added) VS support for refreshing devices while connected to studio
+  - (added) VS feedback survey request after leaving studios
+  - (added) VS Mode improved warnings for high latency audio devices
+  - (added) VS Mode improved getting started recommendations
+  - (updated) official builds now use Qt 6 and RtAudio 6
+  - (updated) official Windows builds now use schannel and MSVC
+  - (updated) Simple-FFT dependency copied into source tree
+  - (updated) Improved user experience when using the RtAudio backend
+  - (fixed) Crashes when audio interfaces don't support buffer size
+  - (fixed) Crashes when audio interfaces are unplugged while active
+  - (fixed) Blacklisting Steinberg Generic ASIO driver due to crashes
+  - (fixed) Bugs with Virtual Studio deep links and connections stats
+  - (fixed) VS Settings will now revert back when Cancel is selected
+  - (fixed) VS Mode device levels no longer reset on first registration
+- Version: "1.10.1"
+  Date: 2023-08-03
+  Description:
+  - (fixed) VS Mode crashes involving ASIO device selection
 - Version: "1.10.0"
   Date: 2023-06-16
   Description:
diff --git a/docs/images/jacktrip_virtual_studio.png b/docs/images/jacktrip_virtual_studio.png
new file mode 100644 (file)
index 0000000..05573f2
Binary files /dev/null and b/docs/images/jacktrip_virtual_studio.png differ
index ececbf5ba0873fa171a7fc47488d96a9a9635f0a..358dde02144a0620245cddd45a78794f0c8b1645 100644 (file)
@@ -5,9 +5,18 @@ It supports any number of channels (as many as the computer/network can handle)
 
 You can use it between any combination of machines e.g., one end using Linux can connect to another using macOS.
 
+## Virtual Studio
+
+With JackTrip Virtual Studio, you may sing with your chorus, or jam with your band, online without leaving home. With built-in support for live broadcasting, you can share your sessions on [JackTrip Radio](https://jacktrip.radio/). Additionally, Virtual Studio allows you to enhance your performances and bring your music to life utilizing Soundscapes digital signal processing. Musicians have creative control with dozens of audio effects with various reverbs, compression, attack and more, in real-time.
+
+[See why thousands of musicians worldwide use JackTrip to rehearse together, perform together, collaborate together, and laugh together.](https://www.jacktrip.com/)
+
 ## Screenshots
 
 
+=== "Virtual Studio"
+
+    ![Virtual Studio](images/jacktrip_virtual_studio.png){width=450}
 
 === "Hubclient // Basic"
 
diff --git a/externals/Simple-FFT/LICENSE.md b/externals/Simple-FFT/LICENSE.md
new file mode 100644 (file)
index 0000000..cabf715
--- /dev/null
@@ -0,0 +1,21 @@
+Copyright (c) 2013-2020 Dmitry Ivanov
+
+The MIT License (MIT)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/externals/Simple-FFT/README.md b/externals/Simple-FFT/README.md
new file mode 100644 (file)
index 0000000..299f320
--- /dev/null
@@ -0,0 +1,98 @@
+Simple-FFT
+==========
+
+**Header-only C++ library implementing fast Fourier transform of 1D, 2D and 3D data.**
+
+### What's this
+
+Simple FFT is a C++ library implementing fast Fourier transform. The implemented FFT is a radix-2 Cooley-Turkey algorithm. This algorithm can't handle transform of data which size is not a power of 2. It is not the most optimal known FFT algorithm.
+
+The library is header-only, you don't need to build anything, just include the files in your project. The user-level interface is simple and reminds the one typically found in mathematical software.
+
+The library is distributed under MIT license
+
+#### Why one more FFT library? Does it have any benefits compared to existing libraries?
+
+Why do humans create things even though they already have something similar? Because they are not satisfied with what they have. So was my impression with existing libraries implementing fast Fourier transforms. My desires were:
+ * Free & open-source library
+ * License allowing to use the library in both open and closed-source software.
+ * Convenient API somewhat similar to the one found in typical mathematical software.
+ * Limited size and dependencies, ideally no dependencies (handy for multiple supported platforms and compilers).
+ * As long as my arrays are not going to be huge, I don't need the fastest FFT in the galaxy, I'd be quite happy with some algorithm getting the job done.
+ * Having said that my arrays are not too large, I still want minimal or no overhead for copying the data to objects of types used in the library.
+
+The last statement deserves some explanation: many popular libraries performing FFT use their own data types, sometimes even their own containers. If you already have the data of your own type/container, you are going to either manually transform (e.g. copy) the data or try to cast pointers. Both ways are not elegant.
+
+I didn't find the solution corresponding to the whole wishlist of mine. So I decided to create my own simple library. I've already mentioned some of disadvantages of such approach:
+ * Not the fastest algorithm known nowadays.
+ * Can't handle data which size is not a power of 2.
+
+The advantages of Simple FFT behind some well-known and widely used libraries are:
+ * Tiny size - just some header files.
+ * No need to build it and link a library to your project.
+ * Can handle 1D, 2D and 3D arrays, extendable for larger dimensions.
+ * Designed to support any _reasonable_ multidimensional array/container type one can imagine.
+
+Again, the last statement needs some details to be revealed: C++ natively supports multidimensional arrays via pointers and also via "std vector of std vectors" approach. However, there are a lot of libraries with their own implementations of multidimensional arrays. I wanted to create a library which in theory can use _any_ type of multidimensional array without data copying or pointer casting. It is not possible to guarantee that my library will work with every multidimensional array type you can imagine but there is only a limited number of restrictions for used types.
+
+#### How the API is convenient? How to actually use the library?
+
+By convenience of API I mean the interface somewhat similar to that found in mathematical software like Mathcad, Matlab, Octave, Scilab etc. The simplest API you can think of is something like `A = FFT(B)`. It is not very easy to efficiently implement in C++ (well, without move semantic of C++11 at least) because with such interface you are going to return the result of B transform by value which means data copying i.e. overhead (unless return by value optimization is employed). So in C++ it is better to return the result by reference. The function can also return boolean which would indicate whether the transform was successful or not. So the simplest interface would look like
+
+```c++
+ b = FFT(A,B); // FFT from A to B
+```
+
+But FFT algorithm requires the knowledge of shape and dimensionality of used arrays. Most multidimensional array implementations can provide this information but they do it in different ways and I wanted something very generic. So I decided to create functions with the same name but different signature depending on the number of dimensions:
+
+```c++
+ b = FFT(A,B,n); // FFT from A to B where A and B are 1D arrays with n elements
+ b = FFT(A,B,n,m); // FFT from A to B where A and B are matrices (2D arrays) with n rows and m columns
+ b = FFT(A,B,n,m,l); // FFT from A to B where A and B are 3D arrays with n rows, m columns and l depth number;
+```
+
+One more thing: if the returned value is false then some error has happened. In order to protect user from debugging into 3rdparty library to figure out what happened I decided to return error description as C-style string (because some people don't use `std::string`). So the interface became looking like this:
+
+```c++
+ const char * error = NULL; // error description
+ b = FFT(A,B,n,error); // FFT from A to B where A and B are 1D arrays with n elements
+ b = FFT(A,B,n,m,error); // FFT from A to B where A and B are matrices (2D arrays) with n rows and m columns
+ b = FFT(A,B,n,m,l,error); // FFT from A to B where A and B are 3D arrays with n rows, m columns and l depth number;
+```
+
+But how about the inverse transform? The flag can be used to tell the forward transform from inverse but I thought that different function names would be easier: FFT for forward transform and IFFT for inverse transform:
+
+```c++
+ const char * error = NULL; // error description
+ b = FFT(A,B,n,error); // forward FFT from A to B where A and B are 1D arrays with n elements
+ b = FFT(A,B,n,m,error); // forward FFT from A to B where A and B are matrices (2D arrays) with n rows and m columns
+ b = FFT(A,B,n,m,l,error); // forward FFT from A to B where A and B are 3D arrays with n rows, m columns and l depth number;
+ b = IFFT(B,A,n,error); // inverse FFT from B to A where A and B are 1D arrays with n elements
+ b = IFFT(B,A,n,m,error); // inverse FFT from B to A where A and B are matrices (2D arrays) with n rows and m columns
+ b = IFFT(B,A,n,m,l,error); // inverse FFT from B to A where A and B are 3D arrays with n rows, m columns and l depth number;
+```
+
+Beyond that there are only two settings:
+* User needs to define two types called `real_type` and `complex_type`. These are needed by design to avoid extra problems with template instantiation
+* User needs to define macro `__USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR` if the multidimensional array type you want to use accesses elements via `operator[]` (for example, if it is native C++ multidimensional array or `boost::multi_array` or something similar). Otherwise the library will attempt to use `operator()` for element access.
+
+That's the whole explanation of API.
+
+#### How does it work with multiple libraries implementing matrices and multidimensional arrays in C++ without being aware of those?
+
+Well, the common generic technique of C++ was used - templates. I also added two `typedef`ed types (`real_type` and `complex_type`) to avoid overcomplication of code. But it's not all about templates - I also tried to implement the most generic element access I could imagine. After a bit of thinking I realized that in terms of interface different libraries implementing multidimensional arrays commonly differ only by their element access operator - some implementations use `operator[]` and others - `operator()`. So I splitted every code section using element access operator into two code blocks - the one with `operator[]` and the one with `operator()`. The switch between them is controlled by macro `__USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR` - if it's defined, `operator[]` is used, if not - `operator()`. User can define this macro in some translation units and not define/undef in other ones - just like I did with unit-tests.
+
+#### Are there any examples or, even better, tests?
+I wrote some tests illustrating how to use SimpleFFT library along with some well-known C++ libraries implementing multidimensional arrays. They can also serve as examples (probably, a bit overcomplicated). The tests are based on the following checks:
+* after each forward of inverse FFT [Parseval's theorem](https://en.wikipedia.org/wiki/Parseval%27s_theorem) must be satisfied for input and output data
+* after each sequence of FFT and IFFT the energy conservation law must be satisfied
+* after each sequence of FFT and IFFT the result should be the very same as initial input; so I decided to measure the largest discrepancy between the result and input and calculate the relative error there. If it is small enough (less than 0.01%), this test is considered passed.
+
+I also implemented a simple benchmark test comparing the execution time of multiple loops of Simple FFT and fftw3. The results for Linux with three compilers can be found in the "benchmark-tests" folder. As expected, fftw3 is much faster.
+
+#### There are multiple files here, which I should use?
+There are only two files of interest for library user:
+* `/include/simple_fft/fft_settings.h`
+* `/include/simple_fft/fft.h`
+
+The first one is supposed to contain `typedef`s for `real_type` and `complex_type` and, if needed, the define of `__USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR` macro. This stuff can also be done somewhere else. The second file is what's actually needed to calculate FFT and IFFT: it contains only API declarations and includes some of other files.
diff --git a/externals/Simple-FFT/include/simple_fft/check_fft.hpp b/externals/Simple-FFT/include/simple_fft/check_fft.hpp
new file mode 100644 (file)
index 0000000..b1b95c9
--- /dev/null
@@ -0,0 +1,571 @@
+/**
+ * Copyright (c) 2013-2020 Dmitry Ivanov
+ *
+ * This file is a part of Simple-FFT project and is distributed under the terms
+ * of MIT license: https://opensource.org/licenses/MIT
+ */
+
+#ifndef __SIMPLE_FFT__CHECK_FFT_HPP__
+#define __SIMPLE_FFT__CHECK_FFT_HPP__
+
+#include "fft_settings.h"
+#include "error_handling.hpp"
+#include "copy_array.hpp"
+#include <cstddef>
+#include <cmath>
+#include <numeric>
+
+using std::size_t;
+
+namespace simple_fft {
+namespace check_fft_private {
+
+enum CheckMode
+{
+    CHECK_FFT_PARSEVAL,
+    CHECK_FFT_ENERGY,
+    CHECK_FFT_EQUALITY
+};
+
+template <class TArray1D, class TComplexArray1D>
+void getMaxAbsoluteAndRelativeErrorNorms(const TArray1D & array1,
+                                         const TComplexArray1D & array2, const size_t size,
+                                         real_type & max_absolute_error_norm,
+                                         real_type & max_relative_error_norm)
+{
+    using std::abs;
+
+    real_type current_error;
+
+    // NOTE: no parallelization here, it is a completely sequential loop!
+    for(size_t i = 0; i < size; ++i) {
+#ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
+        current_error = abs(array1[i] - array2[i]);
+#else
+        current_error = abs(array1(i) - array2(i));
+#endif
+        if (current_error > max_absolute_error_norm) {
+            max_absolute_error_norm = current_error;
+#ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
+            if (abs(array1[i]) > abs(array2[i])) {
+                max_relative_error_norm = (abs(array1[i]) > 1e-20
+                                           ? max_absolute_error_norm / abs(array1[i])
+                                           : 0.0);
+            }
+            else {
+                max_relative_error_norm = (abs(array2[i]) > 1e-20
+                                           ? max_absolute_error_norm / abs(array2[i])
+                                           : 0.0);
+            }
+#else
+            if (abs(array1(i)) > abs(array2(i))) {
+                max_relative_error_norm = (abs(array1(i)) > 1e-20
+                                           ? max_absolute_error_norm / abs(array1(i))
+                                           : 0.0);
+            }
+            else {
+                max_relative_error_norm = (abs(array2(i)) > 1e-20
+                                           ? max_absolute_error_norm / abs(array2(i))
+                                           : 0.0);
+            }
+#endif
+        }
+    }
+}
+
+template <class TArray2D, class TComplexArray2D>
+void getMaxAbsoluteAndRelativeErrorNorms(const TArray2D & array1,
+                                         const TComplexArray2D & array2,
+                                         const size_t size1, const size_t size2,
+                                         real_type & max_absolute_error_norm,
+                                         real_type & max_relative_error_norm)
+{
+    using std::abs;
+
+    real_type current_error;
+
+    // NOTE: no parallelization here, it is a completely sequential loop!
+    for(int i = 0; i < static_cast<int>(size1); ++i) {
+        for(int j = 0; j < static_cast<int>(size2); ++j) {
+#ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
+            current_error = abs(array1[i][j] - array2[i][j]);
+#else
+            current_error = abs(array1(i,j) - array2(i,j));
+#endif
+            if (current_error > max_absolute_error_norm) {
+                max_absolute_error_norm = current_error;
+#ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
+                if (abs(array1[i][j]) > abs(array2[i][j])) {
+                    max_relative_error_norm = (abs(array1[i][j]) > 1e-20
+                                               ? max_absolute_error_norm / abs(array1[i][j])
+                                               : 0.0);
+                }
+                else {
+                    max_relative_error_norm = (abs(array2[i][j]) > 1e-20
+                                               ? max_absolute_error_norm / abs(array2[i][j])
+                                               : 0.0);
+                }
+#else
+                if (abs(array1(i,j)) > abs(array2(i,j))) {
+                    max_relative_error_norm = (abs(array1(i,j)) > 1e-20
+                                               ? max_absolute_error_norm / abs(array1(i,j))
+                                               : 0.0);
+                }
+                else {
+                    max_relative_error_norm = (abs(array2(i,j)) > 1e-20
+                                               ? max_absolute_error_norm / abs(array2(i,j))
+                                               : 0.0);
+                }
+#endif
+            }
+        }
+    }
+}
+
+template <class TArray3D, class TComplexArray3D>
+void getMaxAbsoluteAndRelativeErrorNorms(const TArray3D & array1, const TComplexArray3D & array2,
+                                         const size_t size1, const size_t size2,
+                                         const size_t size3, real_type & max_absolute_error_norm,
+                                         real_type & max_relative_error_norm)
+{
+    using std::abs;
+
+    real_type current_error;
+
+    // NOTE: no parallelization here, it is a completely sequential loop!
+    for(int i = 0; i < static_cast<int>(size1); ++i) {
+        for(int j = 0; j < static_cast<int>(size2); ++j) {
+            for(int k = 0; k < static_cast<int>(size3); ++k) {
+#ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
+                current_error = abs(array1[i][j][k] - array2[i][j][k]);
+#else
+                current_error = abs(array1(i,j,k) - array2(i,j,k));
+#endif
+                if (current_error > max_absolute_error_norm) {
+                    max_absolute_error_norm = current_error;
+#ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
+                    if (abs(array1[i][j][k]) > abs(array2[i][j][k])) {
+                        max_relative_error_norm = (abs(array1[i][j][k]) > 1e-20
+                                                   ? max_absolute_error_norm / abs(array1[i][j][k])
+                                                   : 0.0);
+                    }
+                    else {
+                        max_relative_error_norm = (abs(array2[i][j][k]) > 1e-20
+                                                   ? max_absolute_error_norm / abs(array2[i][j][k])
+                                                   : 0.0);
+                    }
+#else
+                    if (abs(array1(i,j,k)) > abs(array2(i,j,k))) {
+                        max_relative_error_norm = (abs(array1(i,j,k)) > 1e-20
+                                                   ? max_absolute_error_norm / abs(array1(i,j,k))
+                                                   : 0.0);
+                    }
+                    else {
+                        max_relative_error_norm = (abs(array2(i,j,k)) > 1e-20
+                                                   ? max_absolute_error_norm / abs(array2(i,j,k))
+                                                   : 0.0);
+                    }
+#endif
+                }
+            }
+        }
+    }
+}
+
+template <class TArray1D>
+real_type squareAbsAccumulate(const TArray1D & array, const size_t size,
+                              const real_type init)
+{
+    int size_signed = static_cast<int>(size);
+    real_type sum = init;
+
+    using std::abs;
+
+#ifndef __clang__
+#ifdef __USE_OPENMP
+#pragma omp parallel for reduction(+:sum)
+#endif
+#endif
+    for(int i = 0; i < size_signed; ++i) {
+#ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
+        sum += abs(array[i] * array[i]);
+#else
+        sum += abs(array(i) * array(i));
+#endif
+    }
+
+    return sum;
+}
+
+template <class TArray2D>
+real_type squareAbsAccumulate(const TArray2D & array, const size_t size1,
+                              const size_t size2, const real_type init)
+{
+    int size1_signed = static_cast<int>(size1);
+    int size2_signed = static_cast<int>(size2);
+    real_type sum = init;
+
+    using std::abs;
+
+#ifndef __clang__
+#ifdef __USE_OPENMP
+#pragma omp parallel for reduction(+:sum)
+#endif
+#endif
+    for(int i = 0; i < size1_signed; ++i) {
+        for(int j = 0; j < size2_signed; ++j) {
+#ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
+            sum += abs(array[i][j] * array[i][j]);
+#else
+            sum += abs(array(i,j) * array(i,j));
+#endif
+        }
+    }
+
+    return sum;
+}
+
+template <class TArray3D>
+real_type squareAbsAccumulate(const TArray3D & array, const size_t size1,
+                              const size_t size2, const size_t size3,
+                              const real_type init)
+{
+    int size1_signed = static_cast<int>(size1);
+    int size2_signed = static_cast<int>(size2);
+    int size3_signed = static_cast<int>(size3);
+    real_type sum = init;
+
+    using std::abs;
+
+#ifndef __clang__
+#ifdef __USE_OPENMP
+#pragma omp parallel for reduction(+:sum)
+#endif
+#endif
+    for(int i = 0; i < size1_signed; ++i) {
+        for(int j = 0; j < size2_signed; ++j) {
+            for(int k = 0; k < size3_signed; ++k) {
+#ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
+                sum += abs(array[i][j][k] * array[i][j][k]);
+#else
+                sum += abs(array(i,j,k) * array(i,j,k));
+#endif
+            }
+        }
+    }
+
+    return sum;
+}
+
+// Generic template for CCheckFFT struct followed by its explicit specializations
+// for certain numbers of dimensions. TArray can be either of real or complex type.
+// The technique is similar to the one applied for CFFT struct.
+template <class TArray, class TComplexArray, int NumDims>
+struct CCheckFFT
+{};
+
+template <class TArray1D, class TComplexArray1D>
+struct CCheckFFT<TArray1D,TComplexArray1D,1>
+{
+    static bool check_fft(const TArray1D & data_before,
+                          const TComplexArray1D & data_after,
+                          const size_t size, const real_type relative_tolerance,
+                          real_type & discrepancy, const CheckMode check_mode,
+                          const char *& error_description)
+    {
+        using namespace error_handling;
+
+        if(0 == size) {
+            GetErrorDescription(EC_NUM_OF_ELEMS_IS_ZERO, error_description);
+            return false;
+        }
+
+        if ( (CHECK_FFT_PARSEVAL != check_mode) &&
+             (CHECK_FFT_ENERGY   != check_mode) &&
+             (CHECK_FFT_EQUALITY != check_mode) )
+        {
+            GetErrorDescription(EC_WRONG_CHECK_FFT_MODE, error_description);
+            return false;
+        }
+
+        if (CHECK_FFT_EQUALITY != check_mode)
+        {
+            real_type sum_before = squareAbsAccumulate<TArray1D>(data_before, size, 0.0);
+            real_type sum_after  = squareAbsAccumulate<TComplexArray1D>(data_after, size, 0.0);
+
+            if (CHECK_FFT_PARSEVAL == check_mode) {
+                sum_after /= size;
+            }
+
+            using std::abs;
+
+            discrepancy = abs(sum_before - sum_after);
+
+            if (discrepancy / ((sum_before < 1e-20) ? (sum_before + 1e-20) : sum_before) > relative_tolerance) {
+                GetErrorDescription(EC_RELATIVE_ERROR_TOO_LARGE, error_description);
+                return false;
+            }
+            else {
+                return true;
+            }
+        }
+        else {
+            real_type relative_error;
+            getMaxAbsoluteAndRelativeErrorNorms(data_before, data_after, size,
+                                                discrepancy, relative_error);
+            if (relative_error < relative_tolerance) {
+                return true;
+            }
+            else {
+                GetErrorDescription(EC_RELATIVE_ERROR_TOO_LARGE, error_description);
+                return false;
+            }
+        }
+    }
+};
+
+template <class TArray2D, class TComplexArray2D>
+struct CCheckFFT<TArray2D,TComplexArray2D,2>
+{
+    static bool check_fft(const TArray2D & data_before,
+                          const TComplexArray2D & data_after,
+                          const size_t size1, const size_t size2,
+                          const real_type relative_tolerance, real_type & discrepancy,
+                          const CheckMode check_mode, const char *& error_description)
+    {
+        using namespace error_handling;
+
+        if( (0 == size1) || (0 == size2) ) {
+            GetErrorDescription(EC_NUM_OF_ELEMS_IS_ZERO, error_description);
+            return false;
+        }
+
+        if ( (CHECK_FFT_PARSEVAL != check_mode) &&
+             (CHECK_FFT_ENERGY   != check_mode) &&
+             (CHECK_FFT_EQUALITY != check_mode) )
+        {
+            GetErrorDescription(EC_WRONG_CHECK_FFT_MODE, error_description);
+            return false;
+        }
+
+        if (CHECK_FFT_EQUALITY != check_mode)
+        {
+            real_type sum_before = squareAbsAccumulate<TArray2D>(data_before, size1, size2, 0.0);
+            real_type sum_after  = squareAbsAccumulate<TComplexArray2D>(data_after, size1, size2, 0.0);
+
+            if (CHECK_FFT_PARSEVAL == check_mode) {
+                sum_after /= size1 * size2;
+            }
+
+            using std::abs;
+
+            discrepancy = abs(sum_before - sum_after);
+
+            if (discrepancy / ((sum_before < 1e-20) ? (sum_before + 1e-20) : sum_before) > relative_tolerance) {
+                GetErrorDescription(EC_RELATIVE_ERROR_TOO_LARGE, error_description);
+                return false;
+            }
+            else {
+                return true;
+            }
+        }
+        else {
+            real_type relative_error;
+            getMaxAbsoluteAndRelativeErrorNorms(data_before, data_after, size1,
+                                                size2, discrepancy, relative_error);
+            if (relative_error < relative_tolerance) {
+                return true;
+            }
+            else {
+                GetErrorDescription(EC_RELATIVE_ERROR_TOO_LARGE, error_description);
+                return false;
+            }
+        }
+    }
+};
+
+template <class TArray3D, class TComplexArray3D>
+struct CCheckFFT<TArray3D,TComplexArray3D,3>
+{
+    static bool check_fft(const TArray3D & data_before,
+                          const TComplexArray3D & data_after,
+                          const size_t size1, const size_t size2, const size_t size3,
+                          const real_type relative_tolerance, real_type & discrepancy,
+                          const CheckMode check_mode, const char *& error_description)
+    {
+        using namespace error_handling;
+
+        if( (0 == size1) || (0 == size2) || (0 == size3) ) {
+            GetErrorDescription(EC_NUM_OF_ELEMS_IS_ZERO, error_description);
+            return false;
+        }
+
+        if ( (CHECK_FFT_PARSEVAL != check_mode) &&
+             (CHECK_FFT_ENERGY   != check_mode) &&
+             (CHECK_FFT_EQUALITY != check_mode) )
+        {
+            GetErrorDescription(EC_WRONG_CHECK_FFT_MODE, error_description);
+            return false;
+        }
+
+        if (CHECK_FFT_EQUALITY != check_mode)
+        {
+            real_type sum_before = squareAbsAccumulate<TArray3D>(data_before, size1, size2, size3, 0.0);
+            real_type sum_after  = squareAbsAccumulate<TComplexArray3D>(data_after, size1, size2, size3, 0.0);
+
+            if (CHECK_FFT_PARSEVAL == check_mode) {
+                sum_after /= size1 * size2 * size3;
+            }
+
+            using std::abs;
+
+            discrepancy = abs(sum_before - sum_after);
+
+            if (discrepancy / ((sum_before < 1e-20) ? (sum_before + 1e-20) : sum_before) > relative_tolerance) {
+                GetErrorDescription(EC_RELATIVE_ERROR_TOO_LARGE, error_description);
+                return false;
+            }
+            else {
+                return true;
+            }
+        }
+        else {
+            real_type relative_error;
+            getMaxAbsoluteAndRelativeErrorNorms(data_before, data_after, size1,
+                                                size2, size3, discrepancy, relative_error);
+            if (relative_error < relative_tolerance) {
+                return true;
+            }
+            else {
+                GetErrorDescription(EC_RELATIVE_ERROR_TOO_LARGE, error_description);
+                return false;
+            }
+        }
+    }
+};
+
+} // namespace check_fft_private
+
+namespace check_fft {
+
+template <class TArray1D, class TComplexArray1D>
+bool checkParsevalTheorem(const TArray1D & data_before_FFT,
+                          const TComplexArray1D & data_after_FFT,
+                          const size_t size, const real_type relative_tolerance,
+                          real_type & discrepancy, const char *& error_description)
+{
+    return check_fft_private::CCheckFFT<TArray1D,TComplexArray1D,1>::check_fft(data_before_FFT,
+                                             data_after_FFT, size, relative_tolerance,
+                                             discrepancy, check_fft_private::CHECK_FFT_PARSEVAL,
+                                             error_description);
+}
+
+template <class TArray2D, class TComplexArray2D>
+bool checkParsevalTheorem(const TArray2D & data_before_FFT,
+                          const TComplexArray2D & data_after_FFT,
+                          const size_t size1, const size_t size2,
+                          const real_type relative_tolerance,
+                          real_type & discrepancy, const char *& error_description)
+{
+    return check_fft_private::CCheckFFT<TArray2D,TComplexArray2D,2>::check_fft(data_before_FFT,
+                                             data_after_FFT, size1, size2, relative_tolerance,
+                                             discrepancy, check_fft_private::CHECK_FFT_PARSEVAL,
+                                             error_description);
+}
+
+template <class TArray3D, class TComplexArray3D>
+bool checkParsevalTheorem(const TArray3D & data_before_FFT,
+                          const TComplexArray3D & data_after_FFT,
+                          const size_t size1, const size_t size2, const size_t size3,
+                          const real_type relative_tolerance, real_type & discrepancy,
+                          const char *& error_description)
+{
+    return check_fft_private::CCheckFFT<TArray3D,TComplexArray3D,3>::check_fft(data_before_FFT,
+                                                  data_after_FFT, size1, size2, size3,
+                                                  relative_tolerance, discrepancy,
+                                                  check_fft_private::CHECK_FFT_PARSEVAL,
+                                                  error_description);
+}
+
+template <class TArray1D, class TComplexArray1D>
+bool checkEnergyConservation(const TArray1D & data_before_FFT,
+                             const TComplexArray1D & data_after_FFT_and_IFFT,
+                             const size_t size, const real_type relative_tolerance,
+                             real_type & discrepancy, const char *& error_description)
+{
+    return check_fft_private::CCheckFFT<TArray1D,TComplexArray1D,1>::check_fft(data_before_FFT,
+                                    data_after_FFT_and_IFFT, size, relative_tolerance,
+                                    discrepancy, check_fft_private::CHECK_FFT_ENERGY,
+                                    error_description);
+}
+
+template <class TArray2D, class TComplexArray2D>
+bool checkEnergyConservation(const TArray2D & data_before_FFT,
+                             const TComplexArray2D & data_after_FFT_and_IFFT,
+                             const size_t size1, const size_t size2,
+                             const real_type relative_tolerance,
+                             real_type & discrepancy, const char *& error_description)
+{
+    return check_fft_private::CCheckFFT<TArray2D,TComplexArray2D,2>::check_fft(data_before_FFT,
+                                                data_after_FFT_and_IFFT, size1, size2,
+                                                relative_tolerance, discrepancy,
+                                                check_fft_private::CHECK_FFT_ENERGY,
+                                                error_description);
+}
+
+template <class TArray3D, class TComplexArray3D>
+bool checkEnergyConservation(const TArray3D & data_before_FFT,
+                             const TComplexArray3D & data_after_FFT_and_IFFT,
+                             const size_t size1, const size_t size2, const size_t size3,
+                             const real_type relative_tolerance, real_type & discrepancy,
+                             const char *& error_description)
+{
+    return check_fft_private::CCheckFFT<TArray3D,TComplexArray3D,3>::check_fft(data_before_FFT,
+                                                data_after_FFT_and_IFFT, size1, size2,
+                                                size3, relative_tolerance, discrepancy,
+                                                check_fft_private::CHECK_FFT_ENERGY,
+                                                error_description);
+}
+
+template <class TArray1D, class TComplexArray1D>
+bool checkEquality(const TArray1D & data_before_FFT,
+                   const TComplexArray1D & data_after_FFT_and_IFFT,
+                   const size_t size, const real_type relative_tolerance,
+                   real_type & discrepancy, const char *& error_description)
+{
+    return check_fft_private::CCheckFFT<TArray1D,TComplexArray1D,1>::check_fft(data_before_FFT,
+                                             data_after_FFT_and_IFFT, size, relative_tolerance,
+                                             discrepancy, check_fft_private::CHECK_FFT_EQUALITY,
+                                             error_description);
+}
+
+template <class TArray2D, class TComplexArray2D>
+bool checkEquality(const TArray2D & data_before_FFT,
+                   const TComplexArray2D & data_after_FFT_and_IFFT, const size_t size1,
+                   const size_t size2, const real_type relative_tolerance,
+                   real_type & discrepancy, const char *& error_description)
+{
+    return check_fft_private::CCheckFFT<TArray2D,TComplexArray2D,2>::check_fft(data_before_FFT,
+                                                         data_after_FFT_and_IFFT, size1, size2,
+                                                         relative_tolerance, discrepancy,
+                                                         check_fft_private::CHECK_FFT_EQUALITY,
+                                                         error_description);
+}
+
+template <class TArray3D, class TComplexArray3D>
+bool checkEquality(const TArray3D & data_before_FFT,
+                   const TComplexArray3D & data_after_FFT_and_IFFT, const size_t size1,
+                   const size_t size2, const size_t size3, const real_type relative_tolerance,
+                   real_type & discrepancy, const char *& error_description)
+{
+    return check_fft_private::CCheckFFT<TArray3D,TComplexArray3D,3>::check_fft(data_before_FFT,
+                                                         data_after_FFT_and_IFFT, size1, size2,
+                                                         size3, relative_tolerance, discrepancy,
+                                                         check_fft_private::CHECK_FFT_EQUALITY,
+                                                         error_description);
+}
+
+} // namespace check_fft
+} // namespace simple_fft
+
+#endif // __SIMPLE_FFT__CHECK_FFT_HPP__
diff --git a/externals/Simple-FFT/include/simple_fft/copy_array.hpp b/externals/Simple-FFT/include/simple_fft/copy_array.hpp
new file mode 100644 (file)
index 0000000..bb4c076
--- /dev/null
@@ -0,0 +1,173 @@
+/**\r
+ * Copyright (c) 2013-2020 Dmitry Ivanov\r
+ *\r
+ * This file is a part of Simple-FFT project and is distributed under the terms\r
+ * of MIT license: https://opensource.org/licenses/MIT\r
+ */\r
+\r
+#ifndef __SIMPLE_FFT__COPY_ARRAY_HPP\r
+#define __SIMPLE_FFT__COPY_ARRAY_HPP\r
+\r
+#include "fft_settings.h"\r
+#include "error_handling.hpp"\r
+#include <cstddef>\r
+\r
+using std::size_t;\r
+\r
+namespace simple_fft {\r
+namespace copy_array {\r
+\r
+template <class TComplexArray1D>\r
+void copyArray(const TComplexArray1D & data_from, TComplexArray1D & data_to,\r
+               const size_t size)\r
+{\r
+    int size_signed = static_cast<int>(size);\r
+\r
+#ifndef __clang__\r
+#ifdef __USE_OPENMP\r
+#pragma omp parallel for\r
+#endif\r
+#endif\r
+    for(int i = 0; i < size_signed; ++i) {\r
+#ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR\r
+        data_to[i] = data_from[i];\r
+#else\r
+        data_to(i) = data_from(i);\r
+#endif\r
+    }\r
+}\r
+\r
+template <class TComplexArray1D, class TRealArray1D>\r
+void copyArray(const TRealArray1D & data_from, TComplexArray1D & data_to,\r
+               const size_t size)\r
+{\r
+    int size_signed = static_cast<int>(size);\r
+\r
+    // NOTE: user's complex type should have constructor like\r
+    // "complex(real, imag)", where each of real and imag has\r
+    // real type.\r
+\r
+#ifndef __clang__\r
+#ifdef __USE_OPENMP\r
+#pragma omp parallel for\r
+#endif\r
+#endif\r
+    for(int i = 0; i < size_signed; ++i) {\r
+#ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR\r
+        data_to[i] = complex_type(data_from[i], 0.0);\r
+#else\r
+        data_to(i) = complex_type(data_from(i), 0.0);\r
+#endif\r
+    }\r
+}\r
+\r
+template <class TComplexArray2D>\r
+void copyArray(const TComplexArray2D & data_from, TComplexArray2D & data_to,\r
+               const size_t size1, const size_t size2)\r
+{\r
+    int size1_signed = static_cast<int>(size1);\r
+    int size2_signed = static_cast<int>(size2);\r
+\r
+#ifndef __clang__\r
+#ifdef __USE_OPENMP\r
+#pragma omp parallel for\r
+#endif\r
+#endif\r
+    for(int i = 0; i < size1_signed; ++i) {\r
+        for(int j = 0; j < size2_signed; ++j) {\r
+#ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR\r
+            data_to[i][j] = data_from[i][j];\r
+#else\r
+            data_to(i,j) = data_from(i,j);\r
+#endif\r
+        }\r
+    }\r
+}\r
+\r
+template <class TComplexArray2D, class TRealArray2D>\r
+void copyArray(const TRealArray2D & data_from, TComplexArray2D & data_to,\r
+               const size_t size1, const size_t size2)\r
+{\r
+    int size1_signed = static_cast<int>(size1);\r
+    int size2_signed = static_cast<int>(size2);\r
+\r
+    // NOTE: user's complex type should have constructor like\r
+    // "complex(real, imag)", where each of real and imag has\r
+    // real type.\r
+\r
+#ifndef __clang__\r
+#ifdef __USE_OPENMP\r
+#pragma omp parallel for\r
+#endif\r
+#endif\r
+    for(int i = 0; i < size1_signed; ++i) {\r
+        for(int j = 0; j < size2_signed; ++j) {\r
+#ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR\r
+            data_to[i][j] = complex_type(data_from[i][j], 0.0);\r
+#else\r
+            data_to(i,j) = complex_type(data_from(i,j), 0.0);\r
+#endif\r
+        }\r
+    }\r
+}\r
+\r
+template <class TComplexArray3D>\r
+void copyArray(const TComplexArray3D & data_from, TComplexArray3D & data_to,\r
+               const size_t size1, const size_t size2, const size_t size3)\r
+{\r
+    int size1_signed = static_cast<int>(size1);\r
+    int size2_signed = static_cast<int>(size2);\r
+    int size3_signed = static_cast<int>(size3);\r
+\r
+#ifndef __clang__\r
+#ifdef __USE_OPENMP\r
+#pragma omp parallel for\r
+#endif\r
+#endif\r
+    for(int i = 0; i < size1_signed; ++i) {\r
+        for(int j = 0; j < size2_signed; ++j) {\r
+            for(int k = 0; k < size3_signed; ++k) {\r
+#ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR\r
+                data_to[i][j][k] = data_from[i][j][k];\r
+#else\r
+                data_to(i,j,k) = data_from(i,j,k);\r
+#endif\r
+            }\r
+        }\r
+    }\r
+}\r
+\r
+template <class TComplexArray3D, class TRealArray3D>\r
+void copyArray(const TRealArray3D & data_from, TComplexArray3D & data_to,\r
+               const size_t size1, const size_t size2, const size_t size3)\r
+{\r
+    int size1_signed = static_cast<int>(size1);\r
+    int size2_signed = static_cast<int>(size2);\r
+    int size3_signed = static_cast<int>(size3);\r
+\r
+    // NOTE: user's complex type should have constructor like\r
+    // "complex(real, imag)", where each of real and imag has\r
+    // real type.\r
+\r
+#ifndef __clang__\r
+#ifdef __USE_OPENMP\r
+#pragma omp parallel for\r
+#endif\r
+#endif\r
+    for(int i = 0; i < size1_signed; ++i) {\r
+        for(int j = 0; j < size2_signed; ++j) {\r
+            for(int k = 0; k < size3_signed; ++k) {\r
+#ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR\r
+                data_to[i][j][k] = complex_type(data_from[i][j][k], 0.0);\r
+#else\r
+                data_to(i,j,k) = complex_type(data_from(i,j,k), 0.0);\r
+#endif\r
+            }\r
+        }\r
+    }\r
+}\r
+\r
+} // namespace copy_array\r
+} // namespace simple_fft\r
+\r
+#endif // __SIMPLE_FFT__COPY_ARRAY_HPP\r
diff --git a/externals/Simple-FFT/include/simple_fft/error_handling.hpp b/externals/Simple-FFT/include/simple_fft/error_handling.hpp
new file mode 100644 (file)
index 0000000..608ecc9
--- /dev/null
@@ -0,0 +1,64 @@
+/**\r
+ * Copyright (c) 2013-2020 Dmitry Ivanov\r
+ *\r
+ * This file is a part of Simple-FFT project and is distributed under the terms\r
+ * of MIT license: https://opensource.org/licenses/MIT\r
+ */\r
+\r
+#ifndef __SIMPLE_FFT__ERROR_HANDLING_HPP\r
+#define __SIMPLE_FFT__ERROR_HANDLING_HPP\r
+\r
+namespace simple_fft {\r
+namespace error_handling {\r
+\r
+enum EC_SimpleFFT\r
+{\r
+    EC_SUCCESS = 0,\r
+    EC_UNSUPPORTED_DIMENSIONALITY,\r
+    EC_WRONG_FFT_DIRECTION,\r
+    EC_ONE_OF_DIMS_ISNT_POWER_OF_TWO,\r
+    EC_NUM_OF_ELEMS_IS_ZERO,\r
+    EC_WRONG_CHECK_FFT_MODE,\r
+    EC_RELATIVE_ERROR_TOO_LARGE\r
+};\r
+\r
+inline void GetErrorDescription(const EC_SimpleFFT error_code,\r
+                                const char *& error_description)\r
+{\r
+    switch(error_code)\r
+    {\r
+    case EC_SUCCESS:\r
+        error_description = "Calculation was successful!";\r
+        break;\r
+    case EC_UNSUPPORTED_DIMENSIONALITY:\r
+        error_description = "Unsupported dimensionality: currently only 1D, 2D "\r
+                            "and 3D arrays are supported";\r
+        break;\r
+    case EC_WRONG_FFT_DIRECTION:\r
+        error_description = "Wrong direction for FFT was specified";\r
+        break;\r
+    case EC_ONE_OF_DIMS_ISNT_POWER_OF_TWO:\r
+        error_description = "Unsupported dimensionality: one of dimensions is not "\r
+                            "a power of 2";\r
+        break;\r
+    case EC_NUM_OF_ELEMS_IS_ZERO:\r
+        error_description = "Number of elements for FFT or IFFT is zero!";\r
+        break;\r
+    case EC_WRONG_CHECK_FFT_MODE:\r
+        error_description = "Wrong check FFT mode was specified (should be either "\r
+                            "Parseval theorem or energy conservation check";\r
+        break;\r
+    case EC_RELATIVE_ERROR_TOO_LARGE:\r
+        error_description = "Relative error returned by FFT test exceeds specified "\r
+                            "relative tolerance";\r
+        break;\r
+    default:\r
+        error_description = "Unknown error";\r
+        break;\r
+    }\r
+}\r
+\r
+} // namespace error_handling\r
+} // namespace simple_fft\r
+\r
+#endif // __SIMPLE_FFT__ERROR_HANDLING_HPP\r
diff --git a/externals/Simple-FFT/include/simple_fft/fft.h b/externals/Simple-FFT/include/simple_fft/fft.h
new file mode 100644 (file)
index 0000000..3cf77f8
--- /dev/null
@@ -0,0 +1,98 @@
+/**
+ * Copyright (c) 2013-2020 Dmitry Ivanov
+ *
+ * This file is a part of Simple-FFT project and is distributed under the terms
+ * of MIT license: https://opensource.org/licenses/MIT
+ */
+
+#ifndef __SIMPLE_FFT__FFT_H__
+#define __SIMPLE_FFT__FFT_H__
+
+#include <cstddef>
+
+using std::size_t;
+
+/// The public API
+namespace simple_fft {
+
+/// FFT and IFFT functions
+
+// in-place, complex, forward
+template <class TComplexArray1D>
+bool FFT(TComplexArray1D & data, const size_t size, const char *& error_description);
+
+template <class TComplexArray2D>
+bool FFT(TComplexArray2D & data, const size_t size1, const size_t size2,
+         const char *& error_description);
+
+template <class TComplexArray3D>
+bool FFT(TComplexArray3D & data, const size_t size1, const size_t size2, const size_t size3,
+         const char *& error_description);
+
+// in-place, complex, inverse
+template <class TComplexArray1D>
+bool IFFT(TComplexArray1D & data, const size_t size, const char *& error_description);
+
+template <class TComplexArray2D>
+bool IFFT(TComplexArray2D & data, const size_t size1, const size_t size2,
+          const char *& error_description);
+
+template <class TComplexArray3D>
+bool IFFT(TComplexArray3D & data, const size_t size1, const size_t size2, const size_t size3,
+          const char *& error_description);
+
+// not-in-place, complex, forward
+template <class TComplexArray1D>
+bool FFT(const TComplexArray1D & data_in, TComplexArray1D & data_out,
+         const size_t size, const char *& error_description);
+
+template <class TComplexArray2D>
+bool FFT(const TComplexArray2D & data_in, TComplexArray2D & data_out,
+         const size_t size1, const size_t size2, const char *& error_description);
+
+template <class TComplexArray3D>
+bool FFT(const TComplexArray3D & data_in, TComplexArray3D & data_out,
+         const size_t size1, const size_t size2, const size_t size3,
+         const char *& error_description);
+
+// not-in-place, complex, inverse
+template <class TComplexArray1D>
+bool IFFT(const TComplexArray1D & data_in, TComplexArray1D & data_out,
+          const size_t size, const char *& error_description);
+
+template <class TComplexArray2D>
+bool IFFT(const TComplexArray2D & data_in, TComplexArray2D & data_out,
+          const size_t size1, const size_t size2, const char *& error_description);
+
+template <class TComplexArray3D>
+bool IFFT(const TComplexArray3D & data_in, TComplexArray3D & data_out,
+          const size_t size1, const size_t size2, const size_t size3,
+          const char *& error_description);
+
+// not-in-place, real, forward
+template <class TRealArray1D, class TComplexArray1D>
+bool FFT(const TRealArray1D & data_in, TComplexArray1D & data_out,
+         const size_t size, const char *& error_description);
+
+template <class TRealArray2D, class TComplexArray2D>
+bool FFT(const TRealArray2D & data_in, TComplexArray2D & data_out,
+         const size_t size1, const size_t size2, const char *& error_description);
+
+template <class TRealArray3D, class TComplexArray3D>
+bool FFT(const TRealArray3D & data_in, TComplexArray3D & data_out,
+         const size_t size1, const size_t size2, const size_t size3,
+         const char *& error_description);
+
+// NOTE: There is no inverse transform from complex spectrum to real signal
+// because round-off errors during computation of inverse FFT lead to the appearance
+// of signal imaginary components even though they are small by absolute value.
+// These can be ignored but the author of this file thinks adding such an function
+// would be wrong methodogically: looking at complex result, you can estimate
+// the value of spurious imaginary part. Otherwise you may never know that IFFT
+// provides too large imaginary values due to too small grid size, for example.
+
+} // namespace simple_fft
+
+#endif // __SIMPLE_FFT__FFT_H__
+
+#include "fft.hpp"
diff --git a/externals/Simple-FFT/include/simple_fft/fft.hpp b/externals/Simple-FFT/include/simple_fft/fft.hpp
new file mode 100644 (file)
index 0000000..dca498d
--- /dev/null
@@ -0,0 +1,162 @@
+/**\r
+ * Copyright (c) 2013-2020 Dmitry Ivanov\r
+ *\r
+ * This file is a part of Simple-FFT project and is distributed under the terms\r
+ * of MIT license: https://opensource.org/licenses/MIT\r
+ */\r
+\r
+#ifndef __SIMPLE_FFT__FFT_HPP__\r
+#define __SIMPLE_FFT__FFT_HPP__\r
+\r
+#include "copy_array.hpp"\r
+#include "fft_impl.hpp"\r
+\r
+namespace simple_fft {\r
+\r
+// in-place, complex, forward\r
+template <class TComplexArray1D>\r
+bool FFT(TComplexArray1D & data, const size_t size, const char *& error_description)\r
+{\r
+    return impl::CFFT<TComplexArray1D,1>::FFT_inplace(data, size, impl::FFT_FORWARD,\r
+                                                      error_description);\r
+}\r
+\r
+template <class TComplexArray2D>\r
+bool FFT(TComplexArray2D & data, const size_t size1, const size_t size2,\r
+         const char *& error_description)\r
+{\r
+    return impl::CFFT<TComplexArray2D,2>::FFT_inplace(data, size1, size2, impl::FFT_FORWARD,\r
+                                                      error_description);\r
+}\r
+\r
+template <class TComplexArray3D>\r
+bool FFT(TComplexArray3D & data, const size_t size1, const size_t size2, const size_t size3,\r
+         const char *& error_description)\r
+{\r
+    return impl::CFFT<TComplexArray3D,3>::FFT_inplace(data, size1, size2, size3,\r
+                                                      impl::FFT_FORWARD,\r
+                                                      error_description);\r
+}\r
+\r
+// in-place, complex, inverse\r
+template <class TComplexArray1D>\r
+bool IFFT(TComplexArray1D & data, const size_t size, const char *& error_description)\r
+{\r
+    return impl::CFFT<TComplexArray1D,1>::FFT_inplace(data, size, impl::FFT_BACKWARD,\r
+                                                      error_description);\r
+}\r
+\r
+template <class TComplexArray2D>\r
+bool IFFT(TComplexArray2D & data, const size_t size1, const size_t size2,\r
+          const char *& error_description)\r
+{\r
+    return impl::CFFT<TComplexArray2D,2>::FFT_inplace(data, size1, size2, impl::FFT_BACKWARD,\r
+                                                      error_description);\r
+}\r
+\r
+template <class TComplexArray3D>\r
+bool IFFT(TComplexArray3D & data, const size_t size1, const size_t size2, const size_t size3,\r
+          const char *& error_description)\r
+{\r
+    return impl::CFFT<TComplexArray3D,3>::FFT_inplace(data, size1, size2, size3,\r
+                                                      impl::FFT_BACKWARD,\r
+                                                      error_description);\r
+}\r
+\r
+// not-in-place, complex, forward\r
+template <class TComplexArray1D>\r
+bool FFT(const TComplexArray1D & data_in, TComplexArray1D & data_out,\r
+         const size_t size, const char *& error_description)\r
+{\r
+    copy_array::copyArray(data_in, data_out, size);\r
+    return impl::CFFT<TComplexArray1D,1>::FFT_inplace(data_out, size, impl::FFT_FORWARD,\r
+                                                      error_description);\r
+}\r
+\r
+template <class TComplexArray2D>\r
+bool FFT(const TComplexArray2D & data_in, TComplexArray2D & data_out,\r
+         const size_t size1, const size_t size2, const char *& error_description)\r
+{\r
+    copy_array::copyArray(data_in, data_out, size1, size2);\r
+    return impl::CFFT<TComplexArray2D,2>::FFT_inplace(data_out, size1, size2,\r
+                                                      impl::FFT_FORWARD,\r
+                                                      error_description);\r
+}\r
+\r
+template <class TComplexArray3D>\r
+bool FFT(const TComplexArray3D & data_in, TComplexArray3D & data_out,\r
+         const size_t size1, const size_t size2, const size_t size3,\r
+         const char *& error_description)\r
+{\r
+    copy_array::copyArray(data_in, data_out, size1, size2, size3);\r
+    return impl::CFFT<TComplexArray3D,3>::FFT_inplace(data_out, size1, size2, size3,\r
+                                                      impl::FFT_FORWARD,\r
+                                                      error_description);\r
+}\r
+\r
+// not-in-place, complex, inverse\r
+template <class TComplexArray1D>\r
+bool IFFT(const TComplexArray1D & data_in, TComplexArray1D & data_out,\r
+          const size_t size, const char *& error_description)\r
+{\r
+    copy_array::copyArray(data_in, data_out, size);\r
+    return impl::CFFT<TComplexArray1D,1>::FFT_inplace(data_out, size, impl::FFT_BACKWARD,\r
+                                                      error_description);\r
+}\r
+\r
+template <class TComplexArray2D>\r
+bool IFFT(const TComplexArray2D & data_in, TComplexArray2D & data_out,\r
+          const size_t size1, const size_t size2, const char *& error_description)\r
+{\r
+    copy_array::copyArray(data_in, data_out, size1, size2);\r
+    return impl::CFFT<TComplexArray2D,2>::FFT_inplace(data_out, size1, size2,\r
+                                                      impl::FFT_BACKWARD,\r
+                                                      error_description);\r
+}\r
+\r
+template <class TComplexArray3D>\r
+bool IFFT(const TComplexArray3D & data_in, TComplexArray3D & data_out,\r
+          const size_t size1, const size_t size2, const size_t size3,\r
+          const char *& error_description)\r
+{\r
+    copy_array::copyArray(data_in, data_out, size1, size2, size3);\r
+    return impl::CFFT<TComplexArray3D,3>::FFT_inplace(data_out, size1, size2, size3,\r
+                                                      impl::FFT_BACKWARD,\r
+                                                      error_description);\r
+}\r
+\r
+// not-in-place, real, forward\r
+template <class TRealArray1D, class TComplexArray1D>\r
+bool FFT(const TRealArray1D & data_in, TComplexArray1D & data_out,\r
+         const size_t size, const char *& error_description)\r
+{\r
+    copy_array::copyArray(data_in, data_out, size);\r
+    return impl::CFFT<TComplexArray1D,1>::FFT_inplace(data_out, size,\r
+                                                      impl::FFT_FORWARD,\r
+                                                      error_description);\r
+}\r
+\r
+template <class TRealArray2D, class TComplexArray2D>\r
+bool FFT(const TRealArray2D & data_in, TComplexArray2D & data_out,\r
+         const size_t size1, const size_t size2, const char *& error_description)\r
+{\r
+    copy_array::copyArray(data_in, data_out, size1, size2);\r
+    return impl::CFFT<TComplexArray2D,2>::FFT_inplace(data_out, size1, size2,\r
+                                                      impl::FFT_FORWARD,\r
+                                                      error_description);\r
+}\r
+\r
+template <class TRealArray3D, class TComplexArray3D>\r
+bool FFT(const TRealArray3D & data_in, TComplexArray3D & data_out,\r
+         const size_t size1, const size_t size2, const size_t size3,\r
+         const char *& error_description)\r
+{\r
+    copy_array::copyArray(data_in, data_out, size1, size2, size3);\r
+    return impl::CFFT<TComplexArray3D,3>::FFT_inplace(data_out, size1, size2, size3,\r
+                                                      impl::FFT_FORWARD,\r
+                                                      error_description);\r
+}\r
+\r
+} // simple_fft\r
+\r
+#endif // __SIMPLE_FFT__FFT_HPP__\r
diff --git a/externals/Simple-FFT/include/simple_fft/fft_impl.hpp b/externals/Simple-FFT/include/simple_fft/fft_impl.hpp
new file mode 100644 (file)
index 0000000..b385f07
--- /dev/null
@@ -0,0 +1,515 @@
+/**
+ * Copyright (c) 2013-2020 Dmitry Ivanov
+ *
+ * This file is a part of Simple-FFT project and is distributed under the terms
+ * of MIT license: https://opensource.org/licenses/MIT
+ */
+
+#ifndef __SIMPLE_FFT__FFT_IMPL_HPP__
+#define __SIMPLE_FFT__FFT_IMPL_HPP__
+
+#include "fft_settings.h"
+#include "error_handling.hpp"
+#include <cstddef>
+#include <math.h>
+#include <vector>
+
+using std::size_t;
+
+#ifndef M_PI
+#define M_PI 3.1415926535897932
+#endif
+
+namespace simple_fft {
+namespace impl {
+
+enum FFT_direction
+{
+    FFT_FORWARD = 0,
+    FFT_BACKWARD
+};
+
+// checking whether the size of array dimension is power of 2
+// via "complement and compare" method
+inline bool isPowerOfTwo(const size_t num)
+{
+    return num && (!(num & (num - 1)));
+}
+
+inline bool checkNumElements(const size_t num_elements, const char *& error_description)
+{
+    using namespace error_handling;
+
+    if (!isPowerOfTwo(num_elements)) {
+        GetErrorDescription(EC_ONE_OF_DIMS_ISNT_POWER_OF_TWO, error_description);
+        return false;
+    }
+
+    return true;
+}
+
+template <class TComplexArray1D>
+inline void scaleValues(TComplexArray1D & data, const size_t num_elements)
+{
+    real_type mult = 1.0 / num_elements;
+    int num_elements_signed = static_cast<int>(num_elements);
+
+#ifndef __clang__
+#ifdef __USE_OPENMP
+#pragma omp parallel for
+#endif
+#endif
+    for(int i = 0; i < num_elements_signed; ++i) {
+#ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
+        data[i] *= mult;
+#else
+        data(i) *= mult;
+#endif
+    }
+}
+
+// NOTE: explicit template specialization for the case of std::vector<complex_type>
+// because it is used in 2D and 3D FFT for both array classes with square and round
+// brackets of element access operator; I need to guarantee that sub-FFT 1D will
+// use square brackets for element access operator anyway. It is pretty ugly
+// to duplicate the code but I haven't found more elegant solution.
+template <>
+inline void scaleValues<std::vector<complex_type> >(std::vector<complex_type> & data,
+                                                    const size_t num_elements)
+{
+    real_type mult = 1.0 / num_elements;
+    int num_elements_signed = static_cast<int>(num_elements);
+
+#ifndef __clang__
+#ifdef __USE_OPENMP
+#pragma omp parallel for
+#endif
+#endif
+    for(int i = 0; i < num_elements_signed; ++i) {
+        data[i] *= mult;
+    }
+}
+
+template <class TComplexArray1D>
+inline void bufferExchangeHelper(TComplexArray1D & data, const size_t index_from,
+                                 const size_t index_to, complex_type & buf)
+{
+#ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
+    buf = data[index_from];
+    data[index_from] = data[index_to];
+    data[index_to]= buf;
+#else
+    buf = data(index_from);
+    data(index_from) = data(index_to);
+    data(index_to)= buf;
+#endif
+}
+
+// NOTE: explicit template specialization for the case of std::vector<complex_type>
+// because it is used in 2D and 3D FFT for both array classes with square and round
+// brackets of element access operator; I need to guarantee that sub-FFT 1D will
+// use square brackets for element access operator anyway. It is pretty ugly
+// to duplicate the code but I haven't found more elegant solution.
+template <>
+inline void bufferExchangeHelper<std::vector<complex_type> >(std::vector<complex_type> & data,
+                                                             const size_t index_from,
+                                                             const size_t index_to,
+                                                             complex_type & buf)
+{
+    buf = data[index_from];
+    data[index_from] = data[index_to];
+    data[index_to]= buf;
+}
+
+template <class TComplexArray1D>
+void rearrangeData(TComplexArray1D & data, const size_t num_elements)
+{
+    complex_type buf;
+
+    size_t target_index = 0;
+    size_t bit_mask;
+
+    for (size_t i = 0; i < num_elements; ++i)
+    {
+        if (target_index > i)
+        {
+            bufferExchangeHelper(data, target_index, i, buf);
+        }
+
+        // Initialize the bit mask
+        bit_mask = num_elements;
+
+        // While bit is 1
+        while (target_index & (bit_mask >>= 1)) // bit_mask = bit_mask >> 1
+        {
+            // Drop bit:
+            // & is bitwise AND,
+            // ~ is bitwise NOT
+            target_index &= ~bit_mask; // target_index = target_index & (~bit_mask)
+        }
+
+        // | is bitwise OR
+        target_index |= bit_mask; // target_index = target_index | bit_mask
+    }
+}
+
+template <class TComplexArray1D>
+inline void fftTransformHelper(TComplexArray1D & data, const size_t match,
+                               const size_t k, complex_type & product,
+                               const complex_type factor)
+{
+#ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
+    product = data[match] * factor;
+    data[match] = data[k] - product;
+    data[k] += product;
+#else
+    product = data(match) * factor;
+    data(match) = data(k) - product;
+    data(k) += product;
+#endif
+}
+
+// NOTE: explicit template specialization for the case of std::vector<complex_type>
+// because it is used in 2D and 3D FFT for both array classes with square and round
+// brackets of element access operator; I need to guarantee that sub-FFT 1D will
+// use square brackets for element access operator anyway. It is pretty ugly
+// to duplicate the code but I haven't found more elegant solution.
+template <>
+inline void fftTransformHelper<std::vector<complex_type> >(std::vector<complex_type> & data,
+                                                           const size_t match,
+                                                           const size_t k,
+                                                           complex_type & product,
+                                                           const complex_type factor)
+{
+    product = data[match] * factor;
+    data[match] = data[k] - product;
+    data[k] += product;
+}
+
+template <class TComplexArray1D>
+bool makeTransform(TComplexArray1D & data, const size_t num_elements,
+                   const FFT_direction fft_direction, const char *& error_description)
+{
+    using namespace error_handling;
+    using std::sin;
+
+    double local_pi;
+    switch(fft_direction)
+    {
+    case(FFT_FORWARD):
+        local_pi = -M_PI;
+        break;
+    case(FFT_BACKWARD):
+        local_pi = M_PI;
+        break;
+    default:
+        GetErrorDescription(EC_WRONG_FFT_DIRECTION, error_description);
+        return false;
+    }
+
+    // declare variables to cycle the bits of initial signal
+    size_t next, match;
+    real_type sine;
+    real_type delta;
+    complex_type mult, factor, product;
+
+    // NOTE: user's complex type should have constructor like
+    // "complex(real, imag)", where each of real and imag has
+    // real type.
+
+    // cycle for all bit positions of initial signal
+    for (size_t i = 1; i < num_elements; i <<= 1)
+    {
+        next = i << 1;  // getting the next bit
+        delta = local_pi / i;    // angle increasing
+        sine = sin(0.5 * delta);    // supplementary sin
+        // multiplier for trigonometric recurrence
+        mult = complex_type(-2.0 * sine * sine, sin(delta));
+        factor = 1.0;   // start transform factor
+
+        for (size_t j = 0; j < i; ++j) // iterations through groups
+                                       // with different transform factors
+        {
+            for (size_t k = j; k < num_elements; k += next) // iterations through
+                                                            // pairs within group
+            {
+                match = k + i;
+                fftTransformHelper(data, match, k, product, factor);
+            }
+            factor = mult * factor + factor;
+        }
+    }
+
+    return true;
+}
+
+// Generic template for complex FFT followed by its explicit specializations
+template <class TComplexArray, int NumDims>
+struct CFFT
+{};
+
+// 1D FFT:
+template <class TComplexArray1D>
+struct CFFT<TComplexArray1D,1>
+{
+    // NOTE: passing by pointer is needed to avoid using element access operator
+    static bool FFT_inplace(TComplexArray1D & data, const size_t size,
+                            const FFT_direction fft_direction,
+                            const char *& error_description)
+    {
+        if(!checkNumElements(size, error_description)) {
+            return false;
+        }
+
+        rearrangeData(data, size);
+
+        if(!makeTransform(data, size, fft_direction, error_description)) {
+            return false;
+        }
+
+        if (FFT_BACKWARD == fft_direction) {
+            scaleValues(data, size);
+        }
+
+        return true;
+    }
+};
+
+// 2D FFT
+template <class TComplexArray2D>
+struct CFFT<TComplexArray2D,2>
+{
+    static bool FFT_inplace(TComplexArray2D & data, const size_t size1, const size_t size2,
+                            const FFT_direction fft_direction, const char *& error_description)
+    {
+        int n_rows = static_cast<int>(size1);
+        int n_cols = static_cast<int>(size2);
+
+        // fft for columns
+        std::vector<complex_type> subarray(n_rows); // each column has n_rows elements
+
+        for(int j = 0; j < n_cols; ++j)
+        {
+#ifndef __clang__
+#ifdef __USE_OPENMP
+#pragma omp parallel for
+#endif
+#endif
+            for(int i = 0; i < n_rows; ++i) {
+#ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
+                subarray[i] = data[i][j];
+#else
+                subarray[i] = data(i,j);
+#endif
+            }
+
+            if(!CFFT<std::vector<complex_type>,1>::FFT_inplace(subarray, size1,
+                                                               fft_direction,
+                                                               error_description))
+            {
+                return false;
+            }
+
+#ifndef __clang__
+#ifdef __USE_OPENMP
+#pragma omp parallel for
+#endif
+#endif
+            for(int i = 0; i < n_rows; ++i) {
+#ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
+                data[i][j] = subarray[i];
+#else
+                data(i,j) = subarray[i];
+#endif
+            }
+        }
+
+        // fft for rows
+        subarray.resize(n_cols); // each row has n_cols elements
+
+        for(int i = 0; i < n_rows; ++i)
+        {
+#ifndef __clang__
+#ifdef __USE_OPENMP
+#pragma omp parallel for
+#endif
+#endif
+            for(int j = 0; j < n_cols; ++j) {
+#ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
+                subarray[j] = data[i][j];
+#else
+                subarray[j] = data(i,j);
+#endif
+            }
+
+            if(!CFFT<std::vector<complex_type>,1>::FFT_inplace(subarray, size2,
+                                                               fft_direction,
+                                                               error_description))
+            {
+                return false;
+            }
+
+#ifndef __clang__
+#ifdef __USE_OPENMP
+#pragma omp parallel for
+#endif
+#endif
+            for(int j = 0; j < n_cols; ++j) {
+#ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
+                data[i][j] = subarray[j];
+#else
+                data(i,j) = subarray[j];
+#endif
+            }
+        }
+
+        return true;
+    }
+};
+
+// 3D FFT
+template <class TComplexArray3D>
+struct CFFT<TComplexArray3D,3>
+{
+    static bool FFT_inplace(TComplexArray3D & data, const size_t size1, const size_t size2,
+                            const size_t size3, const FFT_direction fft_direction,
+                            const char *& error_description)
+    {
+        int n_rows  = static_cast<int>(size1);
+        int n_cols  = static_cast<int>(size2);
+        int n_depth = static_cast<int>(size3);
+
+        std::vector<complex_type> subarray(n_rows); // for fft for columns: each column has n_rows elements
+
+        for(int k = 0; k < n_depth; ++k) // for all depth layers
+        {
+            // fft for columns
+            for(int j = 0; j < n_cols; ++j)
+            {
+#ifndef __clang__
+#ifdef __USE_OPENMP
+#pragma omp parallel for
+#endif
+#endif
+                for(int i = 0; i < n_rows; ++i) {
+#ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
+                    subarray[i] = data[i][j][k];
+#else
+                    subarray[i] = data(i,j,k);
+#endif
+                }
+
+                if(!CFFT<std::vector<complex_type>,1>::FFT_inplace(subarray, size1,
+                                                                   fft_direction,
+                                                                   error_description))
+                {
+                    return false;
+                }
+
+#ifndef __clang__
+#ifdef __USE_OPENMP
+#pragma omp parallel for
+#endif
+#endif
+                for(int i = 0; i < n_rows; ++i) {
+#ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
+                    data[i][j][k] = subarray[i];
+#else
+                    data(i,j,k) = subarray[i];
+#endif
+                }
+            }
+        }
+
+        subarray.resize(n_cols); // for fft for rows: each row has n_cols elements
+
+        for(int k = 0; k < n_depth; ++k) // for all depth layers
+        {
+            // fft for rows
+            for(int i = 0; i < n_rows; ++i)
+            {
+#ifndef __clang__
+#ifdef __USE_OPENMP
+#pragma omp parallel for
+#endif
+#endif
+                for(int j = 0; j < n_cols; ++j) {
+#ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
+                    subarray[j] = data[i][j][k];
+#else
+                    subarray[j] = data(i,j,k);
+#endif
+                }
+
+                if(!CFFT<std::vector<complex_type>,1>::FFT_inplace(subarray, size2,
+                                                                   fft_direction,
+                                                                   error_description))
+                {
+                    return false;
+                }
+
+#ifndef __clang__
+#ifdef __USE_OPENMP
+#pragma omp parallel for
+#endif
+#endif
+                for(int j = 0; j < n_cols; ++j) {
+#ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
+                    data[i][j][k] = subarray[j];
+#else
+                    data(i,j,k) = subarray[j];
+#endif
+                }
+            }
+        }
+
+        // fft for depth
+        subarray.resize(n_depth); // each depth strip contains n_depth elements
+
+        for(int i = 0; i < n_rows; ++i) // for all rows layers
+        {
+            for(int j = 0; j < n_cols; ++j) // for all cols layers
+            {
+#ifndef __clang__
+#ifdef __USE_OPENMP
+#pragma omp parallel for
+#endif
+#endif
+                for(int k = 0; k < n_depth; ++k) {
+#ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
+                    subarray[k] = data[i][j][k];
+#else
+                    subarray[k] = data(i,j,k);
+#endif
+                }
+
+                if(!CFFT<std::vector<complex_type>,1>::FFT_inplace(subarray, size3,
+                                                                   fft_direction,
+                                                                   error_description))
+                {
+                    return false;
+                }
+
+#ifndef __clang__
+#ifdef __USE_OPENMP
+#pragma omp parallel for
+#endif
+#endif
+                for(int k = 0; k < n_depth; ++k) {
+#ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
+                    data[i][j][k] = subarray[k];
+#else
+                    data(i,j,k) = subarray[k];
+#endif
+                }
+            }
+        }
+
+        return true;
+    }
+};
+
+} // namespace impl
+} // namespace simple_fft
+
+#endif // __SIMPLE_FFT__FFT_IMPL_HPP__
diff --git a/externals/Simple-FFT/include/simple_fft/fft_settings.h b/externals/Simple-FFT/include/simple_fft/fft_settings.h
new file mode 100644 (file)
index 0000000..7d97b7d
--- /dev/null
@@ -0,0 +1,26 @@
+/**\r
+ * Copyright (c) 2013-2020 Dmitry Ivanov\r
+ *\r
+ * This file is a part of Simple-FFT project and is distributed under the terms\r
+ * of MIT license: https://opensource.org/licenses/MIT\r
+ */\r
+\r
+// In this file you can alter some settings of the library:\r
+// 1) Specify the desired real and complex types by typedef'ing real_type and complex_type.\r
+//    By default real_type is double and complex_type is std::complex<real_type>.\r
+// 2) If the array class uses square brackets for element access operator, define\r
+//    the macro __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR\r
+\r
+#ifndef __SIMPLE_FFT__FFT_SETTINGS_H__\r
+#define __SIMPLE_FFT__FFT_SETTINGS_H__\r
+\r
+#include <complex>\r
+\r
+typedef float real_type;\r
+typedef std::complex<real_type> complex_type;\r
+\r
+#ifndef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR\r
+#define __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR\r
+#endif\r
+\r
+#endif // __SIMPLE_FFT__FFT_SETTINGS_H__\r
index 33f095a0490ca18042864da7f684838b8c52354d..c3f9fce3bf8ed6c30883e21d29006c86a2d04495 100644 (file)
@@ -31,12 +31,13 @@ nogui {
   novs {
     DEFINES += NO_VS
   } else {
-    QT += networkauth
     QT += qml
     QT += quick
     QT += quickcontrols2
     QT += svg
     QT += websockets
+    QT += webview
+    QT += webenginequick
     vsftux {
       DEFINES += VS_FTUX
     }
@@ -102,7 +103,7 @@ bundled_rtaudio {
     PKGCONFIG += rtaudio
     win32 {
       # even though we get linker flags from pkg-config, define -lrtaudio again to enforce linking order
-      CONFIG += no_lflags_merge    
+      CONFIG += no_lflags_merge
       LIBS += -lrtaudio -lole32 -lwinmm -lksuser -lmfplat -lmfuuid -lwmcodecdspuuid # -ldsound # -ldsound only needed if rtaudio is built with directsound support
     }
   }
@@ -116,34 +117,34 @@ macx {
     LIBS += -framework Foundation
     CONFIG += objective_c
     !novs {
-      LIBS += -framework AVFoundation
+      LIBS += -framework AVFoundation -framework WebKit
     }
   }
 }
 
 linux-g++ | linux-g++-64 {
-  
+
   FEDORA = $$system(cat /proc/version | grep -o fc)
-  
+
   contains( FEDORA, fc): {
     message(building on fedora)
   }
-  
+
   UBUNTU = $$system(cat /proc/version | grep -o Ubuntu)
-  
+
   contains( UBUNTU, Ubuntu): {
     message(building on  Ubuntu)
-    
+
     # workaround for Qt bug under ubuntu 18.04
     # gcc version 7.3.0 (Ubuntu 7.3.0-16ubuntu3)
     # QMake version 3.1
     # Using Qt version 5.9.5 in /usr/lib/x86_64-linux-gnu
     INCLUDEPATH += /usr/include/x86_64-linux-gnu/c++/7
-    
+
     # sets differences from original fedora version
     DEFINES += __UBUNTU__
   }
-  
+
   QMAKE_CXXFLAGS += -g -O2
 }
 
@@ -261,16 +262,15 @@ HEADERS += src/DataProtocol.h \
                src/gui/vsApi.h \
                src/gui/vsAuth.h \
                src/gui/vsDeviceCodeFlow.h \
-               src/gui/vsInit.h \
+               src/gui/vsDeeplink.h \
                src/gui/vsDevice.h \
-               src/gui/vsAudioInterface.h \
+               src/gui/vsAudio.h \
                src/gui/vsServerInfo.h \
                src/gui/vsQuickView.h \
                src/gui/vsWebSocket.h \
                src/gui/vsPermissions.h \
                src/gui/vsPinger.h \
                src/gui/vsPing.h \
-               src/gui/vsUrlHandler.h \
                src/gui/vsQmlClipboard.h \
                src/JTApplication.h
   }
@@ -331,16 +331,15 @@ SOURCES += src/DataProtocol.cpp \
                src/gui/vsApi.cpp \
                src/gui/vsAuth.cpp \
                src/gui/vsDeviceCodeFlow.cpp \
-               src/gui/vsInit.cpp \
+               src/gui/vsDeeplink.cpp \
                src/gui/vsDevice.cpp \
-               src/gui/vsAudioInterface.cpp \
+               src/gui/vsAudio.cpp \
                src/gui/vsServerInfo.cpp \
                src/gui/vsQuickView.cpp \
                src/gui/vsWebSocket.cpp \
                src/gui/vsPermissions.cpp \
                src/gui/vsPinger.cpp \
-               src/gui/vsPing.cpp \
-               src/gui/vsUrlHandler.cpp
+               src/gui/vsPing.cpp
   }
   !noupdater:!linux-g++:!linux-g++-64 {
     SOURCES += src/dblsqd/feed.cpp \
index 36d332e385c320b8bae6e2be433f8fd5c5d7b4ec..f2e3bfeeb241fc9f4d17b2ad1e3bb0f8fe41a0a2 100644 (file)
@@ -2,6 +2,20 @@
 
 See LICENSE.md for license information.
 
+JackTrip requires that Qt6 is installed on your machine.
+
+For Fedora or RedHat:
+
+```
+dnf install -y qt6-qtbase qt6-qtbase-common qt6-qtbase-gui qt6-qtsvg qt6-qtwebsockets qt6-qtwebengine qt6-qtwebchannel qt6-qt5compat
+```
+
+For Debian or Ubuntu:
+
+```
+apt install -y libqt6core6 libqt6gui6 libqt6network6 libqt6widgets6 libqt6qml6 libqt6qmlcore6 libqt6quick6 libqt6quickcontrols2-6 libqt6svg6  libqt6webchannel6 libqt6webengine6-data libqt6webenginecore6 libqt6webenginecore6-bin libqt6webenginequick6 libqt6websockets6 qt6-qpa-plugins qml6-module-qtquick-controls qml6-module-qtqml-workerscript qml6-module-qtquick-templates qml6-module-qtquick-layouts qml6-module-qt5compat-graphicaleffects qml6-module-qtwebchannel qml6-module-qtwebengine
+```
+
 To install JackTrip as a Linux desktop application:
 
 ```
@@ -13,6 +27,13 @@ desktop-file-install --dir=$HOME/.local/share/applications org.jacktrip.JackTrip
 update-desktop-database $HOME/.local/share/applications
 ```
 
+To install the manual page for JackTrip:
+
+```
+sudo mkdir -p /usr/local/share/man/man1
+sudo cp jacktrip.1.gz /usr/local/share/man/man1
+```
+
 When using jacktrip with the JACK Audio Connection Kit (or Pipewire), ensure that your user account has permission to schedule realtime processes.
 `ulimit -r` should return a value greater than 40.
 
diff --git a/linux/flatpak/org.jacktrip.JackTrip.Devel.yml b/linux/flatpak/org.jacktrip.JackTrip.Devel.yml
deleted file mode 100644 (file)
index ca71033..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-app-id: org.jacktrip.JackTrip.Devel
-runtime: org.kde.Platform
-runtime-version: '5.15-22.08'
-sdk: org.kde.Sdk
-command: jacktrip
-finish-args:
-  # X11 + XShm access
-  - --share=ipc
-  - --socket=x11
-  # Wayland access
-  # - --socket=wayland
-  # OpenGL
-  - --device=dri
-  # Needs network access
-  - --share=network
-  # Pipewire/Jack
-  - --filesystem=xdg-run/pipewire-0
-cleanup:
-  - /lib/python3.8
-  - /share/man
-modules:
-  - name: python3-pyyaml
-    buildsystem: simple
-    cleanup: [ "*" ]
-    build-commands:
-    - pip3 install --verbose --exists-action=i --no-index --find-links="file://${PWD}" --prefix=${FLATPAK_DEST} "pyyaml" --no-build-isolation
-    sources:
-    - type: file
-      url: https://files.pythonhosted.org/packages/36/2b/61d51a2c4f25ef062ae3f74576b01638bebad5e045f747ff12643df63844/PyYAML-6.0.tar.gz
-      sha256: 68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2
-  - name: python3-jinja2
-    buildsystem: simple
-    cleanup: [ "*" ]
-    build-commands:
-    - pip3 install --verbose --exists-action=i --no-index --find-links="file://${PWD}" --prefix=${FLATPAK_DEST} "jinja2" --no-build-isolation
-    sources:
-    - type: file
-      url: https://files.pythonhosted.org/packages/1d/97/2288fe498044284f39ab8950703e88abbac2abbdf65524d576157af70556/MarkupSafe-2.1.1.tar.gz
-      sha256: 7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b
-    - type: file
-      url: https://files.pythonhosted.org/packages/bc/c3/f068337a370801f372f2f8f6bad74a5c140f6fda3d9de154052708dd3c65/Jinja2-3.1.2-py3-none-any.whl
-      sha256: 6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61
-  - name: jacktrip
-    buildsystem: meson
-    config-opts:
-      - -Dprofile=development
-    sources:
-      - type: git
-        disable-submodules: false
-        url: https://github.com/jacktrip/jacktrip.git
-        branch: dev
diff --git a/linux/flatpak/org.jacktrip.JackTrip.Devel.yml.j2 b/linux/flatpak/org.jacktrip.JackTrip.Devel.yml.j2
deleted file mode 100644 (file)
index 044cb22..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-app-id: org.jacktrip.JackTrip.Devel
-runtime: org.kde.Platform
-runtime-version: '5.15-22.08'
-sdk: org.kde.Sdk
-command: jacktrip
-finish-args:
-  # X11 + XShm access
-  - --share=ipc
-  - --socket=x11
-  # Wayland access
-  # - --socket=wayland
-  # OpenGL
-  - --device=dri
-  # Needs network access
-  - --share=network
-  # Pipewire/Jack
-  - --filesystem=xdg-run/pipewire-0
-cleanup:
-  - /lib/python3.8
-  - /share/man
-modules:
-  - name: python3-pyyaml
-    buildsystem: simple
-    cleanup: [ "*" ]
-    build-commands:
-    - pip3 install --verbose --exists-action=i --no-index --find-links="file://${PWD}" --prefix=${FLATPAK_DEST} "pyyaml" --no-build-isolation
-    sources:
-    - type: file
-      url: https://files.pythonhosted.org/packages/36/2b/61d51a2c4f25ef062ae3f74576b01638bebad5e045f747ff12643df63844/PyYAML-6.0.tar.gz
-      sha256: 68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2
-  - name: python3-jinja2
-    buildsystem: simple
-    cleanup: [ "*" ]
-    build-commands:
-    - pip3 install --verbose --exists-action=i --no-index --find-links="file://${PWD}" --prefix=${FLATPAK_DEST} "jinja2" --no-build-isolation
-    sources:
-    - type: file
-      url: https://files.pythonhosted.org/packages/1d/97/2288fe498044284f39ab8950703e88abbac2abbdf65524d576157af70556/MarkupSafe-2.1.1.tar.gz
-      sha256: 7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b
-    - type: file
-      url: https://files.pythonhosted.org/packages/bc/c3/f068337a370801f372f2f8f6bad74a5c140f6fda3d9de154052708dd3c65/Jinja2-3.1.2-py3-none-any.whl
-      sha256: 6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61
-  - name: jacktrip
-    buildsystem: meson
-    config-opts:
-      - -Dprofile=development
-    sources:
-      - type: git
-        disable-submodules: false
-        url: {{ env['REPO'] }}
-        branch: {{ env['REF'] }}
diff --git a/linux/flatpak/org.jacktrip.JackTrip.yml b/linux/flatpak/org.jacktrip.JackTrip.yml
deleted file mode 100644 (file)
index 44b109b..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-app-id: org.jacktrip.JackTrip
-runtime: org.kde.Platform
-runtime-version: '5.15-22.08'
-sdk: org.kde.Sdk
-command: jacktrip
-finish-args:
-  # X11 + XShm access
-  - --share=ipc
-  - --socket=x11
-  # Wayland access
-  # - --socket=wayland
-  # OpenGL
-  - --device=dri
-  # Needs network access
-  - --share=network
-  # Pipewire/Jack
-  - --filesystem=xdg-run/pipewire-0
-cleanup:
-  - /lib/python3.8
-  - /share/man
-modules:
-  - name: python3-pyyaml
-    buildsystem: simple
-    cleanup: [ "*" ]
-    build-commands:
-    - pip3 install --verbose --exists-action=i --no-index --find-links="file://${PWD}" --prefix=${FLATPAK_DEST} "pyyaml" --no-build-isolation
-    sources:
-    - type: file
-      url: https://files.pythonhosted.org/packages/36/2b/61d51a2c4f25ef062ae3f74576b01638bebad5e045f747ff12643df63844/PyYAML-6.0.tar.gz
-      sha256: 68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2
-  - name: python3-jinja2
-    buildsystem: simple
-    cleanup: [ "*" ]
-    build-commands:
-    - pip3 install --verbose --exists-action=i --no-index --find-links="file://${PWD}" --prefix=${FLATPAK_DEST} "jinja2" --no-build-isolation
-    sources:
-    - type: file
-      url: https://files.pythonhosted.org/packages/1d/97/2288fe498044284f39ab8950703e88abbac2abbdf65524d576157af70556/MarkupSafe-2.1.1.tar.gz
-      sha256: 7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b
-    - type: file
-      url: https://files.pythonhosted.org/packages/bc/c3/f068337a370801f372f2f8f6bad74a5c140f6fda3d9de154052708dd3c65/Jinja2-3.1.2-py3-none-any.whl
-      sha256: 6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61
-  - name: jacktrip
-    buildsystem: meson
-    sources:
-      - type: git
-        disable-submodules: false
-        url: https://github.com/jacktrip/jacktrip.git
-        branch: main
diff --git a/linux/qt/1d21c39.diff b/linux/qt/1d21c39.diff
deleted file mode 100644 (file)
index f2eca05..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-diff --git a/src/network/ssl/qsslsocket_openssl_symbols_p.h b/src/network/ssl/qsslsocket_openssl_symbols_p.h
-index 43b5bf6..7160825 100644
---- a/src/network/ssl/qsslsocket_openssl_symbols_p.h
-+++ b/src/network/ssl/qsslsocket_openssl_symbols_p.h
-@@ -237,7 +237,6 @@
- EVP_PKEY_CTX *q_EVP_PKEY_CTX_new(EVP_PKEY *pkey, ENGINE *e);
- void q_EVP_PKEY_CTX_free(EVP_PKEY_CTX *ctx);
- int q_EVP_PKEY_param_check(EVP_PKEY_CTX *ctx);
--int q_EVP_PKEY_base_id(EVP_PKEY *a);
- int q_RSA_bits(RSA *a);
- Q_AUTOTEST_EXPORT int q_OPENSSL_sk_num(OPENSSL_STACK *a);
- Q_AUTOTEST_EXPORT void q_OPENSSL_sk_pop_free(OPENSSL_STACK *a, void (*b)(void *));
-@@ -383,6 +382,17 @@
- int q_EC_GROUP_get_degree(const EC_GROUP* g);
- #endif // OPENSSL_NO_EC
-+// Here we have the ones that make difference between OpenSSL pre/post v3:
-+#if defined(OPENSSL_VERSION_MAJOR) && OPENSSL_VERSION_MAJOR >= 3
-+X509 *q_SSL_get1_peer_certificate(SSL *a);
-+#define q_SSL_get_peer_certificate q_SSL_get1_peer_certificate
-+int q_EVP_PKEY_get_base_id(const EVP_PKEY *pkey);
-+#define q_EVP_PKEY_base_id q_EVP_PKEY_get_base_id
-+#else
-+X509 *q_SSL_get_peer_certificate(SSL *a);
-+int q_EVP_PKEY_base_id(EVP_PKEY *a);
-+#endif // OPENSSL_VERSION_MAJOR >= 3
-+
- DSA *q_DSA_new();
- void q_DSA_free(DSA *a);
- X509 *q_d2i_X509(X509 **a, const unsigned char **b, long c);
-@@ -510,7 +520,6 @@
- int q_SSL_version(const SSL *a);
- int q_SSL_get_error(SSL *a, int b);
- STACK_OF(X509) *q_SSL_get_peer_cert_chain(SSL *a);
--X509 *q_SSL_get_peer_certificate(SSL *a);
- long q_SSL_get_verify_result(const SSL *a);
- SSL *q_SSL_new(SSL_CTX *a);
- SSL_CTX *q_SSL_get_SSL_CTX(SSL *a);
index 99843e7cd945b87b440122b0ec54148c61cd1306..f28fbb133b85732449002fe022644b6cb7920148 100644 (file)
@@ -60,6 +60,8 @@
        <key>NSHumanReadableCopyright</key>
        <string>Copyright © 2020 Juan-Pablo Caceres, Chris Chafe, Aaron Wyatt, et al. All rights reserved.</string>
        <key>NSMicrophoneUsageDescription</key>
-       <string>This app requires microphone access to allow the jack server to capture audio.</string>
+       <string>JackTrip requires microphone access to capture audio.</string>
+       <key>NSCameraUsageDescription</key>
+       <string>JackTrip requires camera access to capture video.</string>
 </dict>
 </plist>
index dc0928cfcb4c541c38185dc6f770696f82a1731d..37c3d4cbabf4184a32fd7725a61310c25ffd4aa3 100755 (executable)
@@ -1,5 +1,7 @@
 #!/bin/sh
 
+set -e
+
 APPNAME="JackTrip"
 BUNDLE_ID="org.jacktrip.jacktrip"
 BUILD_INSTALLER=false
@@ -102,6 +104,13 @@ shift $((OPTIND - 1))
 [ "$#" -gt 0 ] && APPNAME="$1"
 [ "$#" -gt 1 ] && BUNDLE_ID="$2"
 
+DYNAMIC_QT=$(otool -L $BINARY | grep QtCore)
+DYNAMIC_VS=$(otool -L $BINARY | grep QtQml)
+
+if [[ -n "$DYNAMIC_QT" && -n "$QT_PATH" ]]; then
+  export DYLD_FRAMEWORK_PATH=$QT_PATH/lib
+fi
+
 VERSION="$($BINARY -v | awk '/VERSION/{print $NF}')"
 [ -z "$VERSION" ] && { echo "Unable to determine binary version. Quitting."; exit 1; }
 
@@ -121,10 +130,7 @@ cp -Rf ../LICENSES "$APPNAME.app/Contents/Resources/"
 
 [ $PSI = true ] && cp jacktrip_alt.icns "$APPNAME.app/Contents/Resources/jacktrip.icns"
 
-DYNAMIC_QT=$(otool -L $BINARY | grep QtCore)
-DYNAMIC_VS=$(otool -L $BINARY | grep QtQml)
-
-if [ ! -z "$DYNAMIC_QT" ] && [ -z "$DYNAMIC_VS" ]; then
+if [ -n "$DYNAMIC_QT" ] && [ -z "$DYNAMIC_VS" ]; then
     cp "Info_novs.plist" "$APPNAME.app/Contents/Info.plist" 
 fi
 
@@ -132,29 +138,36 @@ sed -i '' "s/%VERSION%/$VERSION/" "$APPNAME.app/Contents/Info.plist"
 sed -i '' "s/%BUNDLENAME%/$APPNAME/" "$APPNAME.app/Contents/Info.plist"
 sed -i '' "s/%BUNDLEID%/$BUNDLE_ID/" "$APPNAME.app/Contents/Info.plist"
 
-if [ ! -z "$DYNAMIC_QT" ]; then
+if [ -n "$DYNAMIC_QT" ]; then
     QT_VERSION="qt$(echo "$DYNAMIC_QT" | sed -E '1!d;s/.*compatibility version ([0-9]+)\.[0-9]+\.[0-9]+.*/\1/g')"
-    echo "Detected a Qt$QT_VERSION binary"
+    echo "Detected a dynamic Qt$QT_VERSION binary"
     DEPLOY_CMD="$(which macdeployqt)"
     if [ -z "$DEPLOY_CMD" ]; then
         # Attempt to find macdeployqt. Try macports location first, then brew.
         if [ -x "/opt/local/libexec/$QT_VERSION/bin/macdeployqt" ]; then
             DEPLOY_CMD="/opt/local/libexec/$QT_VERSION/bin/macdeployqt"
-        elif [ ! -z $(which brew) ] && [ ! -z $(brew --prefix $QT_VERSION) ]; then
+        elif [ -n $(which brew) ] && [ -n $(brew --prefix $QT_VERSION) ]; then
             DEPLOY_CMD="$(brew --prefix $QT_VERSION)/bin/macdeployqt"
         else
             echo "Error: The Qt bin folder needs to be in your PATH for this script to work."
             exit 1
         fi
     fi
-    QMLDIR=""
-    if [ ! -z "$DYNAMIC_VS" ]; then
-        QMLDIR=" -qmldir=../src/gui"
+    DEPLOY_OPTS="-executable=$APPNAME.app/Contents/MacOS/jacktrip -libpath=$QT_PATH/lib"
+    if [ -n "$DYNAMIC_VS" ]; then
+        DEPLOY_OPTS="$DEPLOY_OPTS -qmldir=../src/gui"
     fi
-    if [ ! -z "$CERTIFICATE" ]; then
-        $DEPLOY_CMD "$APPNAME.app"$QMLDIR -codesign="$CERTIFICATE"
-    else
-        $DEPLOY_CMD "$APPNAME.app"$QMLDIR
+    $DEPLOY_CMD "$APPNAME.app" $DEPLOY_OPTS
+
+    if [ -n "$CERTIFICATE" ]; then
+        # manually sign contents since the macdeployqt built-ins do not work (rpath errors)
+        echo "Signing app contents"
+        PATHS="$APPNAME.app/Contents/Frameworks $APPNAME.app/Contents/PlugIns $APPNAME.app/Contents/Resources"
+        find $PATHS -type f | while read fname; do
+            if [[ -f $fname ]]; then
+                codesign -f -s "$CERTIFICATE" --timestamp --entitlements entitlements.plist --options "runtime" "$fname"
+            fi
+        done
     fi
 fi
 
@@ -169,7 +182,10 @@ if [ $PSI = true ]; then
 fi
 
 # Needed for notarization.
-[ ! -z "$CERTIFICATE" ] && codesign -f -s "$CERTIFICATE" --entitlements entitlements.plist --options "runtime" "$APPNAME.app"
+if [ -n "$CERTIFICATE" ]; then
+    echo "Signing $APPNAME.app"
+    codesign -f -s "$CERTIFICATE" --timestamp --entitlements entitlements.plist --options "runtime" "$APPNAME.app"
+fi
 
 # prepare license
 LICENSE_PATH="package/license.txt"
@@ -195,13 +211,14 @@ sed -i '' "s/%VERSION%/$VERSION/" package/JackTrip.pkgproj
 sed -i '' "s/%BUNDLENAME%/$APPNAME/" package/JackTrip.pkgproj
 sed -i '' "s/%BUNDLEID%/$BUNDLE_ID/" package/JackTrip.pkgproj
 
+echo "Building JackTrip.pkg"
 packagesbuild package/JackTrip.pkgproj
 [ $PSI = true ] && mv "package/postinstall.sh.bak" "package/postinstall.sh"
 if pkgutil --check-signature package/build/JackTrip.pkg; then
     echo "Package already signed."
     SIGNED=true
 else
-    if [ ! -z "$PACKAGE_CERT" ]; then
+    if [ -n "$PACKAGE_CERT" ]; then
         echo "Signing package."
         if productsign --sign "$PACKAGE_CERT" package/build/JackTrip.pkg package/build/JackTrip-signed.pkg; then
             mv package/build/JackTrip-signed.pkg package/build/JackTrip.pkg
@@ -223,7 +240,7 @@ if [ $SIGNED = false ]; then
     exit 1
 fi
 
-if [ ! -z "$USERNAME" ] || [ ! -z "$PASSWORD" ] || [ ! -z "$TEAM_ID" ] || [ ! -z "$TEMP_KEYCHAIN" ]; then
+if [ -n "$USERNAME" ] || [ -n "$PASSWORD" ] || [ -n "$TEAM_ID" ] || [ -n "$TEMP_KEYCHAIN" ]; then
     if [ -z "$USERNAME" ] || [ -z "$PASSWORD" ] || [ -z "$TEAM_ID" ]; then
         echo "Error: Missing credentials. Make sure you supply a username, password and team ID."
         exit 1
@@ -231,7 +248,7 @@ if [ ! -z "$USERNAME" ] || [ ! -z "$PASSWORD" ] || [ ! -z "$TEAM_ID" ] || [ ! -z
 fi 
 
 KEYCHAIN=""
-if [ ! -z "$TEMP_KEYCHAIN" ]; then
+if [ -n "$TEMP_KEYCHAIN" ]; then
     echo "Using a temporary keychain"
     [ -e "$TEMP_KEYCHAIN" ] && rm "$TEMP_KEYCHAIN"
     security create-keychain -p "supersecretpassword" "$TEMP_KEYCHAIN"
@@ -244,7 +261,7 @@ elif [ $USE_DEFAULT_KEYCHAIN = true ]; then
     KEYCHAIN=" --keychain \"$DEFAULT_KEYCHAIN\""
 fi
 
-if [ ! -z "$USERNAME" ]; then
+if [ -n "$USERNAME" ]; then
     # We have new credentials. Store them in the keychain so we can use them.
     ARGS="notarytool store-credentials \"$KEY_STORE\" --apple-id \"$USERNAME\" --password \"$PASSWORD\" --team-id \"$TEAM_ID\"$KEYCHAIN"
     echo $ARGS | xargs xcrun
@@ -254,10 +271,10 @@ echo "Sending notarization request"
 ARGS="notarytool submit \"package/build/$APPNAME.pkg\" --keychain-profile \"$KEY_STORE\" --wait$KEYCHAIN"
 echo $ARGS | xargs xcrun
 if [ $? -eq 0 ]; then
-    [ ! -z "$TEMP_KEYCHAIN" ] && security delete-keychain "$TEMP_KEYCHAIN"
+    [ -n "$TEMP_KEYCHAIN" ] && security delete-keychain "$TEMP_KEYCHAIN"
     xcrun stapler staple "package/build/$APPNAME.pkg"
 else
-    [ ! -z "$TEMP_KEYCHAIN" ] && security delete-keychain "$TEMP_KEYCHAIN"
+    [ -n "$TEMP_KEYCHAIN" ] && security delete-keychain "$TEMP_KEYCHAIN"
     echo "Error: Notarization failed"
     exit 1
 fi
index 88233d39f3e75899fe21a76006317f113b0365f5..11f9a7f712c5a147a94755ccf273f004f0fab823 100644 (file)
@@ -4,10 +4,16 @@
 <dict>
        <key>com.apple.security.cs.allow-dyld-environment-variables</key>
        <true/>
+       <key>com.apple.security.cs.allow-unsigned-executable-memory</key>
+       <true/>
        <key>com.apple.security.cs.disable-library-validation</key>
        <true/>
        <key>com.apple.security.device.audio-input</key>
        <true/>
+       <key>com.apple.security.device.microphone</key>
+       <true/>
+       <key>com.apple.security.device.camera</key>
+       <true/>
        <key>com.apple.security.cs.allow-jit</key>
        <true/>
 </dict>
diff --git a/macos/meson_native_minversion_10.13.ini b/macos/meson_native_minversion_10.13.ini
new file mode 100644 (file)
index 0000000..b1ff86a
--- /dev/null
@@ -0,0 +1,10 @@
+[constants]
+minversion_args = ['-mmacosx-version-min=10.13']
+
+[built-in options]
+c_args = minversion_args
+cpp_args = minversion_args
+objcpp_args = minversion_args
+c_link_args = minversion_args
+cpp_link_args = minversion_args
+objcpp_link_args = minversion_args
diff --git a/macos/meson_native_minversion_10.14.ini b/macos/meson_native_minversion_10.14.ini
new file mode 100644 (file)
index 0000000..aafcaff
--- /dev/null
@@ -0,0 +1,10 @@
+[constants]
+minversion_args = ['-mmacosx-version-min=10.14']
+
+[built-in options]
+c_args = minversion_args
+cpp_args = minversion_args
+objcpp_args = minversion_args
+c_link_args = minversion_args
+cpp_link_args = minversion_args
+objcpp_link_args = minversion_args
diff --git a/macos/meson_native_universal.ini b/macos/meson_native_universal.ini
new file mode 100644 (file)
index 0000000..2900e43
--- /dev/null
@@ -0,0 +1,10 @@
+[constants]
+universal_args = ['-arch', 'x86_64', '-arch', 'arm64']
+
+[properties]
+c_args = universal_args
+cpp_args = universal_args
+objcpp_args = universal_args
+c_link_args = universal_args
+cpp_link_args = universal_args
+objcpp_link_args = universal_args
diff --git a/macos/meson_native_universal_10.13.ini b/macos/meson_native_universal_10.13.ini
new file mode 100644 (file)
index 0000000..c41517d
--- /dev/null
@@ -0,0 +1,11 @@
+[constants]
+minversion_args = ['-mmacosx-version-min=10.13']
+universal_args = ['-arch', 'x86_64', '-arch', 'arm64']
+
+[built-in options]
+c_args = universal_args + minversion_args
+cpp_args = universal_args + minversion_args
+objcpp_args = universal_args + minversion_args
+c_link_args = universal_args + minversion_args
+cpp_link_args = universal_args + minversion_args
+objcpp_link_args = universal_args + minversion_args
diff --git a/macos/meson_native_universal_10.14.ini b/macos/meson_native_universal_10.14.ini
new file mode 100644 (file)
index 0000000..88926cd
--- /dev/null
@@ -0,0 +1,11 @@
+[constants]
+minversion_args = ['-mmacosx-version-min=10.14']
+universal_args = ['-arch', 'x86_64', '-arch', 'arm64']
+
+[built-in options]
+c_args = universal_args + minversion_args
+cpp_args = universal_args + minversion_args
+objcpp_args = universal_args + minversion_args
+c_link_args = universal_args + minversion_args
+cpp_link_args = universal_args + minversion_args
+objcpp_link_args = universal_args + minversion_args
diff --git a/macos/qt/configure.json.patch b/macos/qt/configure.json.patch
deleted file mode 100644 (file)
index 30bd49b..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
---- configure.json     2023-04-22 13:05:35
-+++ configure.json.new 2023-04-22 13:05:35
-@@ -1258,7 +1258,6 @@
-         },
-         "neon": {
-             "label": "NEON",
--            "condition": "(arch.arm || arch.arm64) && subarch.neon",
-             "output": [
-                 "privateConfig",
-                 { "type": "define", "name": "QT_COMPILER_SUPPORTS_NEON", "value": 1 }
-@@ -1614,8 +1613,7 @@
-                         },
-                         {
-                             "type": "feature",
--                            "args": "neon",
--                            "condition": "arch.arm || arch.arm64"
-+                            "args": "neon"
-                         },
-                         {
-                             "type": "feature",
index 9a45fb426f402fc621bba23a0ba5cb53ac1b4e9e..d4d2c946440bf9a4f4ae00d1d9a145cdf4f7e027 100644 (file)
@@ -2,18 +2,26 @@ project('jacktrip', ['cpp','c'],
                default_options: ['cpp_std=c++17','warning_level=2','optimization=2'])
 
 if get_option('profile') == 'development'
-  application_id = 'org.jacktrip.JackTrip.Devel'
-  name_suffix = ' (Development Snapshot)'
+       application_id = 'org.jacktrip.JackTrip.Devel'
+       name_suffix = ' (Development Snapshot)'
 else
-  application_id = 'org.jacktrip.JackTrip'
-  name_suffix = ''
+       application_id = 'org.jacktrip.JackTrip'
+       name_suffix = ''
 endif
 
-if get_option('qtversion') == '5'
-  qt = import('qt5')
-else
-  qt = import('qt6')
+qt_version = get_option('qtversion')
+if qt_version == ''
+       qt_dep = dependency('qt6', modules: ['Core'], required: false)
+       if qt_dep.found()
+               qt_version = '6'
+       else
+               qt_version = '5'
+       endif
 endif
+if qt_version == '5' and get_option('nogui') == false and get_option('novs') == false
+       error('JackTrip Virtual Studio requires Qt 6.2 or later.')
+endif
+qt = import('qt' + qt_version)
 
 cmake = import('cmake')
 
@@ -23,10 +31,22 @@ defines = ['-DWAIRTOHUB']
 c_defines = []
 incdirs = []
 
+if get_option('buildtype') == 'release'
+       defines += ['-DNDEBUG']
+       c_defines += ['-DNDEBUG']
+endif
+
+git_tags_cmd = run_command('git', 'describe', '--tags', check: false)
+git_hash_cmd = run_command('git', 'rev-parse', '--short', 'HEAD', check: false)
+if git_tags_cmd.returncode() == 0 and git_hash_cmd.returncode() == 0
+       git_tags = git_tags_cmd.stdout().strip()
+       git_hash = git_hash_cmd.stdout().strip()
+       defines += ['-DJACKTRIP_BUILD_INFO=' + git_tags + '-' + git_hash]
+endif
+
 src = [        'src/DataProtocol.cpp',
        'src/JackTrip.cpp',
        'src/ProcessPlugin.cpp',
-       'src/Analyzer.cpp',
        'src/AudioTester.cpp',
        'src/jacktrip_globals.cpp',
        'src/JackTripWorker.cpp',
@@ -54,7 +74,6 @@ src = [       'src/DataProtocol.cpp',
 moc_h = ['src/DataProtocol.h',
        'src/JackTrip.h',
        'src/ProcessPlugin.h',
-       'src/Analyzer.h',
        'src/Meter.h',
        'src/Monitor.h',
        'src/StereoToMono.h',
@@ -63,8 +82,6 @@ moc_h = ['src/DataProtocol.h',
        'src/JackTripWorker.h',
        'src/PacketHeader.h',
        'src/Regulator.h',
-       'src/WaitFreeRingBuffer.h',
-       'src/WaitFreeFrameBuffer.h',
        'src/Settings.h',
        'src/UdpDataProtocol.h',
        'src/UdpHubListener.h',
@@ -74,13 +91,18 @@ moc_h = ['src/DataProtocol.h',
 ui_h = []
 qres = []
 deps = [dependency('threads')]
+link_args = []
 
 subdir('win')
 subdir('linux')
 
 jack_dep = dependency('jack', required: get_option('jack'))
 if not jack_dep.found()
+       if get_option('weakjack') == true
+               error('unable to find jack and weakjack requested')
+       endif
        defines += '-DNO_JACK'
+       c_defines += '-DNO_JACK'
 else
        src +=  ['src/JackAudioInterface.cpp',
                'src/JMess.cpp',
@@ -91,7 +113,7 @@ else
                src += 'externals/weakjack/weak_libjack.c'
                defines += '-DUSE_WEAK_JACK'
                c_defines += '-DUSE_WEAK_JACK'
-               deps += jack_dep.partial_dependency(includes: true)
+               deps += jack_dep.partial_dependency(compile_args: true, includes: true)
                deps += compiler.find_library('dl', required : false)
        else
                deps += jack_dep
@@ -100,10 +122,10 @@ endif
 
 if get_option('nogui') == true
        defines += '-DNO_GUI'
-       if get_option('qtversion') == '5'
-               qt_dep = dependency('qt5', modules: ['Core', 'Network'], include_type: 'system')
+       if qt_version == '5'
+               deps += dependency('qt5', modules: ['Core', 'Network'], include_type: 'system')
        else
-               qt_dep = dependency('qt6', modules: ['Core', 'Network'], include_type: 'system')
+               deps += dependency('qt6', modules: ['Core', 'Network'], include_type: 'system')
        endif
 else
        src += [
@@ -128,10 +150,10 @@ else
 
        if get_option('novs') == true
                defines += '-DNO_VS'
-               if get_option('qtversion') == '5'
-                       qt_dep = dependency('qt5', modules: ['Core', 'Gui', 'Network', 'Widgets'], include_type: 'system')
+               if qt_version == '5'
+                       deps += dependency('qt5', modules: ['Core', 'Gui', 'Network', 'Widgets'], include_type: 'system')
                else
-                       qt_dep = dependency('qt6', modules: ['Core', 'Gui', 'Network', 'Widgets'], include_type: 'system')
+                       deps += dependency('qt6', modules: ['Core', 'Gui', 'Network', 'Widgets'], include_type: 'system')
                endif
                qres = ['src/gui/qjacktrip_novs.qrc']
        else
@@ -140,52 +162,52 @@ else
                        'src/gui/vsAuth.cpp',
                        'src/gui/vsApi.cpp',
                        'src/gui/vsDeviceCodeFlow.cpp',
-                       'src/gui/vsInit.cpp',
+                       'src/gui/vsDeeplink.cpp',
                        'src/gui/vsDevice.cpp',
-                       'src/gui/vsAudioInterface.cpp',
+                       'src/gui/vsAudio.cpp',
                        'src/gui/vsServerInfo.cpp',
                        'src/gui/vsQuickView.cpp',
                        'src/gui/vsWebSocket.cpp',
-                       'src/gui/vsUrlHandler.cpp',
                        'src/gui/vsPermissions.cpp',
                        'src/gui/vsPinger.cpp',
-                       'src/gui/vsPing.cpp'
+                       'src/gui/vsPing.cpp',
+                       'src/gui/WebSocketTransport.cpp'
                ]
                moc_h += [
                        'src/gui/virtualstudio.h',
                        'src/gui/vsApi.h',
                        'src/gui/vsAuth.h',
                        'src/gui/vsDeviceCodeFlow.h',
-                       'src/gui/vsInit.h',
+                       'src/gui/vsDeeplink.h',
                        'src/gui/vsDevice.h',
-                       'src/gui/vsAudioInterface.h',
+                       'src/gui/vsAudio.h',
                        'src/gui/vsServerInfo.h',
                        'src/gui/vsQuickView.h',
                        'src/gui/vsWebSocket.h',
                        'src/gui/vsPermissions.h',
                        'src/gui/vsPinger.h',
                        'src/gui/vsPing.h',
-                       'src/gui/vsUrlHandler.h',
                        'src/gui/vsQmlClipboard.h',
-                       'src/JTApplication.h'
+                       'src/JTApplication.h',
+                       'src/gui/WebSocketTransport.h'
                ]
 
                if host_machine.system() == 'darwin'
                        moc_h += ['src/gui/vsMacPermissions.h']
                endif
 
-               if get_option('qtversion') == '5'
-                       qt_dep = dependency('qt5', modules: ['Core', 'Gui', 'Network', 'Widgets', 'Quick', 'QuickControls2', 'Qml', 'Svg', 'NetworkAuth', 'WebSockets'], include_type: 'system')
+               if get_option('vsftux') == true
+                       defines += '-DVS_FTUX'
+               endif
+
+               if qt_version == '5'
+                       deps += dependency('qt5', modules: ['Core', 'Gui', 'Network', 'Widgets', 'Quick', 'QuickControls2', 'Qml', 'Svg', 'WebSockets', 'WebChannel'], include_type: 'system')
                else
-                       qt_dep = dependency('qt6', modules: ['Core', 'Gui', 'Network', 'Widgets', 'Quick', 'QuickControls2', 'Qml', 'Svg', 'NetworkAuth', 'WebSockets'], include_type: 'system')
+                       deps += dependency('qt6', modules: ['Core', 'Gui', 'Network', 'Widgets', 'Quick', 'QuickControls2', 'Qml', 'Svg', 'WebSockets', 'WebEngineCore', 'WebEngineQuick', 'WebChannel'], include_type: 'system')
                endif
                qres = ['src/gui/qjacktrip.qrc']
        endif
 
-       if get_option('vsftux') == true
-               defines += '-DVS_FTUX'
-       endif
-
        if get_option('noupdater') == true or host_machine.system() == 'linux'
                defines += '-DNO_UPDATER'
        else
@@ -203,8 +225,18 @@ else
                ]
                ui_h += ['src/dblsqd/update_dialog.ui']
        endif
+
+       if get_option('nofeedback') == true
+               defines += '-DNO_FEEDBACK'
+       else
+               src += [ 'src/Analyzer.cpp' ]
+               moc_h += [ 'src/Analyzer.h' ]
+       endif
+endif
+
+if qt_version == '6' and get_option('default_library') == 'static'
+       deps += dependency('qt6', modules: ['BundledLibpng', 'BundledPcre2', 'BundledHarfbuzz'], include_type: 'system')
 endif
-deps += qt_dep
 
 # TODO: QT_OPENSOURCE should only be defined for open source Qt distribution
 # in QMake this can be checked with QT_EDITION == 'OpenSource'
@@ -237,7 +269,7 @@ endif
 
 if host_machine.system() == 'darwin' and get_option('novs') == false
        src += ['src/gui/vsMacPermissions.mm']
-       apple_av_dep = dependency('appleframeworks', modules : ['avfoundation'])
+       apple_av_dep = dependency('appleframeworks', modules : ['avfoundation', 'webkit'])
        deps += apple_av_dep
 endif
 
@@ -251,19 +283,27 @@ if ui_h.length() > 0
        ui_files = qt.compile_ui(sources: ui_h)
 endif
 
-jacktrip = executable('jacktrip', src, qres_files, ui_files, moc_files, include_directories: incdirs, dependencies: deps, c_args: c_defines, cpp_args: defines, install: true )
+jacktrip = executable('jacktrip', src, qres_files, ui_files, moc_files, include_directories: incdirs, dependencies: deps, link_args: link_args, c_args: c_defines, cpp_args: defines, install: true )
 
 help2man = find_program('help2man', required: false)
-if not (host_machine.system() == 'windows')
+if (host_machine.system() == 'linux')
        if help2man.found()
+               gzip = find_program('gzip', required: false)
                help2man_opts = [
                        '--no-info',
                        '--section=1']
-               custom_target('jacktrip.1',
+               manfile = custom_target('jacktrip.1',
                        output: 'jacktrip.1',
                        command: [help2man, help2man_opts, '--output=@OUTPUT@', jacktrip],
-                       install: true,
+                       install: not gzip.found(),
                        install_dir: get_option('mandir') / 'man1')
+               if gzip.found()
+                       custom_target('jacktrip.1.gz',
+                               output: 'jacktrip.1.gz',
+                               command: [gzip, '-k', manfile],
+                               install: true,
+                               install_dir: get_option('mandir') / 'man1')
+               endif
        endif
 endif
 
@@ -274,5 +314,4 @@ summary({'JACK': jack_dep.found(),
 summary({'Application ID': application_id,
        'GUI': not get_option('nogui'),
        'WAIR': get_option('wair'),
-       'Manpage': help2man.found()}, bool_yn: true, section: 'Configuration')
-
+       'Manpage': help2man.found()}, bool_yn: true, section: 'Configuration')
\ No newline at end of file
index 596fc998a5c303bcecea931e2bda8e78844d3338..f4753a5a5e9b9542b126d47be314dadc3c654bcb 100644 (file)
@@ -6,5 +6,6 @@ option('nogui', type : 'boolean', value : 'false', description: 'Build without g
 option('novs', type : 'boolean', value : 'false', description: 'Build without Virtual Studio support')
 option('vsftux', type : 'boolean', value : 'false', description: 'Build with Virtual Studio first launch experience')
 option('noupdater', type : 'boolean', value : 'false', description: 'Build without auto-update support')
+option('nofeedback', type : 'boolean', value : 'false', description: 'Build without feedback detection')
 option('profile', type: 'combo', choices: ['default', 'development'], value: 'default', description: 'Choose build profile / Sets desktop id accordingly')
-option('qtversion', type : 'combo', choices: ['5', '6'], description: 'Choose to build with either Qt5 or Qt6')
\ No newline at end of file
+option('qtversion', type : 'combo', choices: ['', '5', '6'], description: 'Choose to build with either Qt5 or Qt6')
\ No newline at end of file
index 5e8d7c7b54e9dbea947d2198a561ea516393e321..501de72a55530bc04c2660260ae2ee2e3f3c2dc5 100644 (file)
@@ -5,6 +5,7 @@ nav:
   - Home: index.md
   - User Guide:
     - Installation: Install.md
+    - Virtual Studio: VirtualStudio.md
     - Using custom JACK server name: CustomJackServerName.md
   - Developer Guide:
     - QMake Build:
index dd1dce9855521818fae84ad97bf5d49e4130ec07..1c78c87a53cc23b3474d5ed8ed5b26934a715de1 100644 (file)
@@ -1,6 +1,96 @@
 {
   "app_name": "JackTrip",
   "releases": [
+    {
+      "version": "2.0.0",
+      "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v2.0.0",
+      "download": {
+        "date": "2023-08-28T00:00:00Z",
+        "url": "https://files.jacktrip.org/app-builds/JackTrip-v2.0.0-macOS-x64-signed-installer.pkg",
+        "downloadSize": "177274079",
+        "sha256": "746f8dbefde53a175bc4144ab5b7d492d843c49c5ca6b7e9979fdbc9ce21e1be"
+      }
+    },
+    {
+      "version": "2.0.0-beta2",
+      "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v2.0.0-beta2",
+      "download": {
+        "date": "2023-08-23T00:00:00Z",
+        "url": "https://files.jacktrip.org/app-builds/JackTrip-v2.0.0-beta2-macOS-x64-signed-installer.pkg",
+        "downloadSize": "177087895",
+        "sha256": "fa7663010309ae792dcb271218867602483f092f1946dd5d4257cf6be87b5a84"
+      }
+    },
+    {
+      "version": "2.0.0-beta1",
+      "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v2.0.0-beta1",
+      "download": {
+        "date": "2023-08-07T00:00:00Z",
+        "url": "https://files.jacktrip.org/app-builds/JackTrip-v2.0.0-beta1-macOS-x64-signed-installer.pkg",
+        "downloadSize": "177067983",
+        "sha256": "669852be48688e2b6e78a050f57e6e2776b699c4b50acdd4b2d762dbc4857431"
+      }
+    },
+    {
+      "version": "1.10.1",
+      "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v1.10.1",
+      "download": {
+        "date": "2023-08-04T00:00:00Z",
+        "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.10.1-macOS-x64-signed-installer.pkg",
+        "downloadSize": "22717724",
+        "sha256": "5ee7d73035a2bfc5790b82fac5201d4147ccb3e8cf7573c9fd7078c1683255eb"
+      }
+    },
+    {
+      "version": "1.10.0",
+      "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v1.10.0",
+      "download": {
+        "date": "2023-06-16T00:00:00Z",
+        "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.10.0-macOS-x64-signed-installer.pkg",
+        "downloadSize": "22816785",
+        "sha256": "367cc26cb55946ffadc88f902de89b076b3b91bb36cea00131e9a2cad8a0901f"
+      }
+    },
+    {
+      "version": "1.10.0-beta4",
+      "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v1.10.0-beta4",
+      "download": {
+        "date": "2023-06-13T00:00:00Z",
+        "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.10.0-beta4-macOS-x64-signed-installer.pkg",
+        "downloadSize": "22824520",
+        "sha256": "1bf932eb1dac64639d397caf3cf6010417e029c32abcc6ee61f204fa3f242526"
+      }
+    },
+    {
+      "version": "1.10.0-beta3",
+      "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v1.10.0-beta3",
+      "download": {
+        "date": "2023-06-10T00:00:00Z",
+        "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.10.0-beta3-macOS-x64-signed-installer.pkg",
+        "downloadSize": "22825810",
+        "sha256": "ed8bf80f596843a153eee7930936d5943d15d78f391a6cbb8681783f2f562859"
+      }
+    },
+    {
+      "version": "1.10.0-beta2",
+      "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v1.10.0-beta2",
+      "download": {
+        "date": "2023-06-02T00:00:00Z",
+        "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.10.0-beta2-macOS-x64-signed-installer.pkg",
+        "downloadSize": "22879032",
+        "sha256": "4dcd5f5939d30b3aa9d32dd649acc830752bc8e1c2d9d5ea884225a481dfcaf3"
+      }
+    },
+    {
+      "version": "1.10.0-beta1",
+      "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v1.10.0-beta1",
+      "download": {
+        "date": "2023-05-30T00:00:00Z",
+        "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.10.0-beta1-macOS-x64-signed-installer.pkg",
+        "downloadSize": "22804983",
+        "sha256": "ab46fccbdc2a9a80c782cc4b707ff3358d94677f680333416a74d37d6b54a781"
+      }
+    },
     {
       "version": "1.9.0",
       "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v1.9.0",
index 36f7a4f52727a59454c458faaf6aff76a68e044a..de0d8a442dab811c628fea756d7062b570ccbef9 100644 (file)
@@ -1,6 +1,96 @@
 {
   "app_name": "JackTrip",
   "releases": [
+    {
+      "version": "2.0.0",
+      "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v2.0.0",
+      "download": {
+        "date": "2023-08-28T00:00:00Z",
+        "url": "https://files.jacktrip.org/app-builds/JackTrip-v2.0.0-Windows-x64-signed-installer.msi",
+        "downloadSize": "95846400",
+        "sha256": "c815d992ff7be9a67e5c5b2823cfeda5d652efacf6463d1f9c7fdfb1561296f8"
+      }
+    },
+    {
+      "version": "2.0.0-beta2",
+      "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v2.0.0-beta2",
+      "download": {
+        "date": "2023-08-23T00:00:00Z",
+        "url": "https://files.jacktrip.org/app-builds/JackTrip-v2.0.0-beta2-Windows-x64-signed-installer.msi",
+        "downloadSize": "95608832",
+        "sha256": "0ee198dab26a1ea26d2c2908dc110b27ee031e479f7be59527fbb82070d56bc3"
+      }
+    },
+    {
+      "version": "2.0.0-beta1",
+      "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v2.0.0-beta1",
+      "download": {
+        "date": "2023-08-07T00:00:00Z",
+        "url": "https://files.jacktrip.org/app-builds/JackTrip-v2.0.0-beta1-Windows-x64-signed-installer.msi",
+        "downloadSize": "95596544",
+        "sha256": "5f695fc4cde25cc7d5d0369e27d7e273120afa2251d90095e711825a2fc88093"
+      }
+    },
+    {
+      "version": "1.10.1",
+      "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v1.10.1",
+      "download": {
+        "date": "2023-08-04T00:00:00Z",
+        "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.10.1-Windows-x64-signed-installer.msi",
+        "downloadSize": "46419968",
+        "sha256": "f458526ebbde294771ddc47d5aa0cf13ae5e2ba55f659e15f9bfe57a26ed78bf"
+      }
+    },
+    {
+      "version": "1.10.0",
+      "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v1.10.0",
+      "download": {
+        "date": "2023-06-16T00:00:00Z",
+        "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.10.0-Windows-x64-signed-installer.msi",
+        "downloadSize": "46432256",
+        "sha256": "6d08a83a8a2c153e0fdd43cdb913ebe244552f4f96d873ea4887e3d5be6d235e"
+      }
+    },
+    {
+      "version": "1.10.0-beta4",
+      "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v1.10.0-beta4",
+      "download": {
+        "date": "2023-06-13T00:00:00Z",
+        "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.10.0-beta4-Windows-x64-signed-installer.msi",
+        "downloadSize": "46428160",
+        "sha256": "e9c15938fba55d3391a705394fbf4d0b1d2d83851fbd3ba58d9480739b4452c0"
+      }
+    },
+    {
+      "version": "1.10.0-beta3",
+      "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v1.10.0-beta3",
+      "download": {
+        "date": "2023-06-10T00:00:00Z",
+        "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.10.0-beta3-Windows-x64-signed-installer.msi",
+        "downloadSize": "46436352",
+        "sha256": "2b377eaf1ded51b198274071ad564fbd78d066646f22e6230fca2743233e0d41"
+      }
+    },
+    {
+      "version": "1.10.0-beta2",
+      "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v1.10.0-beta2",
+      "download": {
+        "date": "2023-06-02T00:00:00Z",
+        "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.10.0-beta2-Windows-x64-signed-installer.msi",
+        "downloadSize": "46530560",
+        "sha256": "973c24a27e1594092c0b0c84cd029dce904404bd56d1f7c9bef1e1831425a2d5"
+      }
+    },
+    {
+      "version": "1.10.0-beta1",
+      "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v1.10.0-beta1",
+      "download": {
+        "date": "2023-05-30T00:00:00Z",
+        "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.10.0-beta1-Windows-x64-signed-installer.msi",
+        "downloadSize": "46403584",
+        "sha256": "6a415fbd4fbc7843c88cd2a635a50000aae13800aa00bd5ba6381e91f252e29a"
+      }
+    },
     {
       "version": "1.9.0",
       "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v1.9.0",
index f9e9f1da64180e08a660af82c6a383ded84de8ec..6bbe9b19d7c53e689d9c06cfbc1d842345b91c01 100644 (file)
@@ -1,6 +1,36 @@
 {
   "app_name": "JackTrip",
   "releases": [
+    {
+      "version": "2.0.0",
+      "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v2.0.0",
+      "download": {
+        "date": "2023-08-28T00:00:00Z",
+        "url": "https://files.jacktrip.org/app-builds/JackTrip-v2.0.0-Linux-x64-binary.zip",
+        "downloadSize": "1216331",
+        "sha256": "fa493f9dc9b90df2b899f2caeee488039f4068844df75ae8a129145c37004bf9"
+      }
+    },
+    {
+      "version": "1.10.1",
+      "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v1.10.1",
+      "download": {
+        "date": "2023-08-04T00:00:00Z",
+        "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.10.1-Linux-x64-binary.zip",
+        "downloadSize": "13388786",
+        "sha256": "83911666f2752d273367d91b5ede0783f7993c53c418911d62bde726618200f9"
+      }
+    },
+    {
+      "version": "1.10.0",
+      "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v1.10.0",
+      "download": {
+        "date": "2023-06-16T00:00:00Z",
+        "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.10.0-Linux-x64-binary.zip",
+        "downloadSize": "13669762",
+        "sha256": "c7b97dd56d7976e5ca753c4bcec580ec48fbd40381f22fef0ffa6d81fdf2dd8e"
+      }
+    },
     {
       "version": "1.9.0",
       "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v1.9.0",
index aac7582251502d848136b128309ef7500ab053a7..4ef3dce1ee5d71019e27c2984616292b42d5a3f8 100644 (file)
@@ -1,6 +1,36 @@
 {
   "app_name": "JackTrip",
   "releases": [
+    {
+      "version": "2.0.0",
+      "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v2.0.0",
+      "download": {
+        "date": "2023-08-28T00:00:00Z",
+        "url": "https://files.jacktrip.org/app-builds/JackTrip-v2.0.0-macOS-x64-signed-installer.pkg",
+        "downloadSize": "177274079",
+        "sha256": "746f8dbefde53a175bc4144ab5b7d492d843c49c5ca6b7e9979fdbc9ce21e1be"
+      }
+    },
+    {
+      "version": "1.10.1",
+      "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v1.10.1",
+      "download": {
+        "date": "2023-08-04T00:00:00Z",
+        "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.10.1-macOS-x64-signed-installer.pkg",
+        "downloadSize": "22717724",
+        "sha256": "5ee7d73035a2bfc5790b82fac5201d4147ccb3e8cf7573c9fd7078c1683255eb"
+      }
+    },
+    {
+      "version": "1.10.0",
+      "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v1.10.0",
+      "download": {
+        "date": "2023-06-16T00:00:00Z",
+        "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.10.0-macOS-x64-signed-installer.pkg",
+        "downloadSize": "22816785",
+        "sha256": "367cc26cb55946ffadc88f902de89b076b3b91bb36cea00131e9a2cad8a0901f"
+      }
+    },
     {
       "version": "1.9.0",
       "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v1.9.0",
index 3fdd592cde2c564e23ab28ef8dd6e33ac7b88815..8ab9198fef096ddaadf7581b383575cc635f1765 100644 (file)
@@ -1,6 +1,36 @@
 {
   "app_name": "JackTrip",
   "releases": [
+    {
+      "version": "2.0.0",
+      "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v2.0.0",
+      "download": {
+        "date": "2023-08-28T00:00:00Z",
+        "url": "https://files.jacktrip.org/app-builds/JackTrip-v2.0.0-Windows-x64-signed-installer.msi",
+        "downloadSize": "95846400",
+        "sha256": "c815d992ff7be9a67e5c5b2823cfeda5d652efacf6463d1f9c7fdfb1561296f8"
+      }
+    },
+    {
+      "version": "1.10.1",
+      "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v1.10.1",
+      "download": {
+        "date": "2023-08-04T00:00:00Z",
+        "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.10.1-Windows-x64-signed-installer.msi",
+        "downloadSize": "46419968",
+        "sha256": "f458526ebbde294771ddc47d5aa0cf13ae5e2ba55f659e15f9bfe57a26ed78bf"
+      }
+    },
+    {
+      "version": "1.10.0",
+      "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v1.10.0",
+      "download": {
+        "date": "2023-06-16T00:00:00Z",
+        "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.10.0-Windows-x64-signed-installer.msi",
+        "downloadSize": "46432256",
+        "sha256": "6d08a83a8a2c153e0fdd43cdb913ebe244552f4f96d873ea4887e3d5be6d235e"
+      }
+    },
     {
       "version": "1.9.0",
       "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v1.9.0",
index 9e4f7173105b456ec4979e185038b07a9d0dd5f4..dcd9f99b41f7dac96c90936f66b6c647dc721283 100644 (file)
@@ -102,7 +102,7 @@ void Analyzer::init(int samplingRate, int bufferSize)
 //*******************************************************************************
 void Analyzer::compute(int nframes, float** inputs, float** outputs)
 {
-    if (not inited) {
+    if (!inited) {
         std::cerr << "*** Analyzer " << this << ": init never called! Doing it now.\n";
         init(0, 0);
     }
@@ -136,7 +136,7 @@ void Analyzer::onTick()
         return;
     }
 
-    const uint32_t buffers = mCircularBufferPtr->size();
+    const uint32_t buffers = uint32_t(mCircularBufferPtr->size());
     const uint32_t samples = buffers * mBufferSize;
 
     // require at least mFftSize values to process, otherwise return
index 6ba72b8421bb171f26d940cb838de00cd357746c..71e12d075bf5510b73d570fa7b1929b3dec1b5af 100644 (file)
@@ -87,9 +87,9 @@ class Analyzer : public ProcessPlugin
     bool testSpectralPeakGrowing();
 
     int mInterval                           = 100;
-    float mPeakThresholdMultipler           = 0.5;
-    float mPeakDeviationThresholdMultiplier = 0.4;
-    float mDifferentialThresholdMultiplier  = 0.05;
+    float mPeakThresholdMultipler           = float(0.5);
+    float mPeakDeviationThresholdMultiplier = float(0.4);
+    float mDifferentialThresholdMultiplier  = float(0.05);
 
     float fs;
     int mNumChannels;
index 02f6021d393bf05f2974d8269ae54b638786351c..15f9952c82cacc89248e3eeb29dc584b522db1c3 100644 (file)
@@ -77,7 +77,7 @@ AudioInterface::AudioInterface(QVarLengthArray<int> InputChans,
 #ifndef WAIR
     // cc
     // Initialize and assign memory for ProcessPlugins Buffers
-    int monitorChans = std::min(mInputChans.size(), mOutputChans.size());
+    int monitorChans = int(std::min<size_t>(mInputChans.size(), mOutputChans.size()));
     mInProcessBuffer.resize(mInputChans.size());
     mOutProcessBuffer.resize(mOutputChans.size());
     mMonProcessBuffer.resize(monitorChans);
@@ -97,7 +97,7 @@ AudioInterface::AudioInterface(QVarLengthArray<int> InputChans,
     int oCnt =
         (mOutputChans.size() > mNumNetRevChans) ? mOutputChans.size() : mNumNetRevChans;
     int aCnt = (mNumNetRevChans) ? mInputChans.size() : 0;
-    int mCnt = std::min(iCnt, oCnt);
+    int mCnt = std::min<int>(iCnt, oCnt);
     for (int i = 0; i < iCnt; i++) {
         mInProcessBuffer[i] = NULL;
     }
@@ -154,12 +154,15 @@ AudioInterface::~AudioInterface()
     }
 #endif  // endwhere
     for (auto* i : qAsConst(mProcessPluginsFromNetwork)) {
+        i->disconnect();
         delete i;
     }
     for (auto* i : qAsConst(mProcessPluginsToNetwork)) {
+        i->disconnect();
         delete i;
     }
     for (auto* i : qAsConst(mProcessPluginsToMonitor)) {
+        i->disconnect();
         delete i;
     }
     for (int i = 0; i < mInBufCopy.size(); i++) {
@@ -172,8 +175,8 @@ void AudioInterface::setup(bool /*verbose*/)
 {
     int nChansIn  = mInputChans.size();
     int nChansOut = mOutputChans.size();
-    int nChansMon =
-        std::min(nChansIn, nChansOut);  // Note: Should be 2 when mixing stereo-to-mono
+    int nChansMon = std::min<int>(
+        nChansIn, nChansOut);  // Note: Should be 2 when mixing stereo-to-mono
     inputMixModeT inputMixMode = mInputMixMode;
     if (inputMixMode == MIXTOMONO) {
         nChansIn = 1;
@@ -184,8 +187,8 @@ void AudioInterface::setup(bool /*verbose*/)
     // Allocate buffer memory to read and write
     mSizeInBytesPerChannel = getSizeInBytesPerChannel();
 
-    int size_audio_input  = mSizeInBytesPerChannel * nChansIn;
-    int size_audio_output = mSizeInBytesPerChannel * nChansOut;
+    int size_audio_input  = int(mSizeInBytesPerChannel * nChansIn);
+    int size_audio_output = int(mSizeInBytesPerChannel * nChansOut);
 #ifdef WAIR               // WAIR
     if (mNumNetRevChans)  // else don't change sizes
     {
@@ -272,8 +275,8 @@ void AudioInterface::callback(QVarLengthArray<sample_t*>& in_buffer,
 {
     int nChansIn  = mInputChans.size();
     int nChansOut = mOutputChans.size();
-    int nChansMon =
-        std::min(nChansIn, nChansOut);  // Note: Should be 2 when mixing stereo-to-mono
+    int nChansMon = std::min<int>(
+        nChansIn, nChansOut);  // Note: Should be 2 when mixing stereo-to-mono
     inputMixModeT inputMixMode = mInputMixMode;
     if (inputMixMode == MIXTOMONO) {
         nChansIn = 1;
@@ -656,16 +659,17 @@ void AudioInterface::fromSampleToBitConversion(
     switch (targetBitResolution) {
     case BIT8:
         // 8bit integer between -128 to 127
-        tmp_sample =
-            std::max(-127.0, std::min(127.0, std::round((*input) * 127.0)));  // 2^7 = 128
+        tmp_sample = std::max<double>(
+            -127.0, std::min<double>(127.0, std::round((*input) * 127.0)));  // 2^7 = 128
         tmp_8 = static_cast<int8_t>(tmp_sample);
         std::memcpy(output, &tmp_8, 1);  // 8bits = 1 bytes
         break;
     case BIT16:
         // 16bit integer between -32768 to 32767
         // original scaling: tmp_sample = floor( (*input) * 32768.0 ); // 2^15 = 32768.0
-        tmp_sample = std::max(
-            -32767.0, std::min(32767.0, std::round((*input) * 32767.0)));  // 2^15 = 32768
+        tmp_sample = std::max<double>(
+            -32767.0,
+            std::min<double>(32767.0, std::round((*input) * 32767.0)));  // 2^15 = 32768
         tmp_16 = static_cast<int16_t>(tmp_sample);
         std::memcpy(
             output, &tmp_16,
@@ -692,7 +696,7 @@ void AudioInterface::fromSampleToBitConversion(
     case BIT32:
         tmp_sample = *input;
         // not necessary yet:
-        // tmp_sample = std::max(-1.0, std::min(1.0, tmp_sample));
+        // tmp_sample = std::max<double>(-1.0, std::min<double>(1.0, tmp_sample));
         std::memcpy(output, &tmp_sample, 4);  // 32bit = 4 bytes
         break;
     }
@@ -740,7 +744,7 @@ void AudioInterface::fromBitToSampleConversion(
 //*******************************************************************************
 void AudioInterface::appendProcessPluginToNetwork(ProcessPlugin* plugin)
 {
-    if (not plugin) {
+    if (!plugin) {
         return;
     }
 
@@ -766,7 +770,7 @@ void AudioInterface::appendProcessPluginToNetwork(ProcessPlugin* plugin)
 
 void AudioInterface::appendProcessPluginFromNetwork(ProcessPlugin* plugin)
 {
-    if (not plugin) {
+    if (!plugin) {
         return;
     }
 
@@ -788,13 +792,13 @@ void AudioInterface::appendProcessPluginFromNetwork(ProcessPlugin* plugin)
 
 void AudioInterface::appendProcessPluginToMonitor(ProcessPlugin* plugin)
 {
-    if (not plugin) {
+    if (!plugin) {
         return;
     }
     int nChansIn  = mInputChans.size();
     int nChansOut = mOutputChans.size();
-    int nChansMon =
-        std::min(nChansIn, nChansOut);  // Note: Should be 2 when mixing stereo-to-mono
+    int nChansMon = std::min<int>(
+        nChansIn, nChansOut);  // Note: Should be 2 when mixing stereo-to-mono
     inputMixModeT inputMixMode = mInputMixMode;
     if (inputMixMode == MIXTOMONO) {
         nChansIn = 1;
@@ -829,8 +833,8 @@ void AudioInterface::initPlugins(bool verbose)
 {
     int nChansIn  = mInputChans.size();
     int nChansOut = mOutputChans.size();
-    int nChansMon =
-        std::min(nChansIn, nChansOut);  // Note: Should be 2 when mixing stereo-to-mono
+    int nChansMon = std::min<int>(
+        nChansIn, nChansOut);  // Note: Should be 2 when mixing stereo-to-mono
     inputMixModeT inputMixMode = mInputMixMode;
     if (inputMixMode == MIXTOMONO) {
         nChansIn = 1;
@@ -933,24 +937,28 @@ int AudioInterface::getSampleRateFromType(samplingRateT rate_type)
 void AudioInterface::setDevicesWarningMsg(warningMessageT msg)
 {
     switch (msg) {
-    case DEVICE_WARN_LATENCY:
+    case DEVICE_WARN_BUFFER_LATENCY:
         mWarningMsg =
-            "The selected Input and Output devices are Non-ASIO and may cause high "
-            "latency or audio delay. Installing ASIO drivers and using ASIO Input and "
-            "Output devices will lower audio delays for a 'same room experience'.";
-#ifdef _WIN32
-        mWarningHelpUrl = "https://help.jacktrip.org/hc/en-us/articles/4409919243155";
-#else
-        mWarningHelpUrl = "";
-#endif
+            "The buffer size setting for your audio device will cause high latency "
+            "or audio delay. Use an audio device that supports small buffer sizes "
+            "to reduce audio delays.";
+        mWarningHelpUrl  = "";
+        mHighLatencyFlag = true;
+        break;
+    case DEVICE_WARN_ASIO_LATENCY:
+        mWarningMsg =
+            "You audio device drivers may cause high latency or audio delay. Install "
+            "and use ASIO drivers provided by your device's manufacturer to reduce "
+            "audio delays.";
+        mWarningHelpUrl  = "https://help.jacktrip.org/hc/en-us/articles/4409919243155";
+        mHighLatencyFlag = true;
         break;
     default:
-        mWarningMsg     = "";
-        mWarningHelpUrl = "";
+        mWarningMsg      = "";
+        mWarningHelpUrl  = "";
+        mHighLatencyFlag = false;
         break;
     }
-
-    return;
 }
 
 //*******************************************************************************
@@ -965,7 +973,7 @@ void AudioInterface::setDevicesErrorMsg(errorMessageT msg)
 #ifdef _WIN32
         mErrorHelpUrl = "https://help.jacktrip.org/hc/en-us/articles/4409919243155";
 #else
-        mErrorHelpUrl   = "";
+        mErrorHelpUrl = "";
 #endif
         break;
     case DEVICE_ERR_NO_INPUTS:
@@ -992,29 +1000,4 @@ void AudioInterface::setDevicesErrorMsg(errorMessageT msg)
         mErrorHelpUrl = "";
         break;
     }
-    return;
-}
-
-//*******************************************************************************
-std::string AudioInterface::getDevicesWarningMsg()
-{
-    return mWarningMsg;
-}
-
-//*******************************************************************************
-std::string AudioInterface::getDevicesErrorMsg()
-{
-    return mErrorMsg;
-}
-
-//*******************************************************************************
-std::string AudioInterface::getDevicesWarningHelpUrl()
-{
-    return mWarningHelpUrl;
-}
-
-//*******************************************************************************
-std::string AudioInterface::getDevicesErrorHelpUrl()
-{
-    return mErrorHelpUrl;
 }
index 1018912561a6991c06ffe7aa074cee81797a6e34..80eb8a15087fb3079ce3475845260519ffaa69d9 100644 (file)
@@ -76,7 +76,11 @@ class AudioInterface
         UNDEF   ///< Undefined
     };
 
-    enum warningMessageT { DEVICE_WARN_NONE, DEVICE_WARN_LATENCY };
+    enum warningMessageT {
+        DEVICE_WARN_NONE,
+        DEVICE_WARN_BUFFER_LATENCY,
+        DEVICE_WARN_ASIO_LATENCY
+    };
 
     enum errorMessageT {
         DEVICE_ERR_NONE,
@@ -157,14 +161,14 @@ class AudioInterface
      *               -> JackTrip client -> processPlugin from network -> JACK -> audio
      */
     virtual void appendProcessPluginFromNetwork(ProcessPlugin* plugin);
+    /** \brief appendProcessPluginToMonitor():
+     * Appends plugins used for local monitoring
+     */
+    virtual void appendProcessPluginToMonitor(ProcessPlugin* plugin);
     /** \brief initPlugins():
      * Initialize all ProcessPlugin modules.
      * The audio sampling rate (mSampleRate) must be set at this time.
      */
-    virtual void appendProcessPluginToMonitor(ProcessPlugin* plugin);
-    /** \brief appendProcessPluginFromNetwork():
-     * Appends plugins used for local monitoring
-     */
     void initPlugins(bool verbose = true);
     virtual void connectDefaultPorts() = 0;
     /** \brief Convert a 32bit number (sample_t) into one of the bit resolution
@@ -251,11 +255,12 @@ class AudioInterface
      * \return Sample Rate in Hz
      */
     static int getSampleRateFromType(samplingRateT rate_type);
-    std::string getDevicesWarningMsg();
-    std::string getDevicesErrorMsg();
-    std::string getDevicesWarningHelpUrl();
-    std::string getDevicesErrorHelpUrl();
-
+    const std::string& getDevicesWarningMsg() const { return mWarningMsg; }
+    const std::string& getDevicesErrorMsg() const { return mErrorMsg; }
+    const std::string& getDevicesWarningHelpUrl() const { return mWarningHelpUrl; }
+    const std::string& getDevicesErrorHelpUrl() const { return mErrorHelpUrl; }
+    bool highLatencyBufferSize() const { return getBufferSizeInSamples() > 256; }
+    bool getHighLatencyFlag() const { return mHighLatencyFlag; }
     //------------------------------------------------------------------
 
    private:
@@ -321,6 +326,7 @@ class AudioInterface
     std::string mErrorMsg;
     std::string mWarningHelpUrl;
     std::string mErrorHelpUrl;
+    bool mHighLatencyFlag;
 };
 
 #endif  // __AUDIOINTERFACE_H__
index 1f5a0c0ce4b6e305449295dc94ec0db9e0a5da74..64e141ea73ae566dda67d8bb6eb67ba54713f12b 100644 (file)
@@ -44,7 +44,7 @@
 void AudioTester::lookForReturnPulse(QVarLengthArray<sample_t*>& out_buffer,
                                      unsigned int n_frames)
 {
-    if (not enabled) {
+    if (!enabled) {
         std::cerr << "*** AudioTester.h: lookForReturnPulse: NOT ENABLED\n";
         return;
     }
@@ -138,9 +138,9 @@ void AudioTester::lookForReturnPulse(QVarLengthArray<sample_t*>& out_buffer,
                         // measured time is "buffer time" not sample time
                         int64_t curTimeUS = timeMicroSec();  // time since launch in us
                         double timeSinceLastPrintUS = double(curTimeUS - lastPrintTimeUS);
-                        double stdDev =
-                            sqrt(std::max(0.0, (roundTripMeanSquare
-                                                - (roundTripMean * roundTripMean))));
+                        double stdDev               = sqrt(std::max<double>(
+                            0.0,
+                            (roundTripMeanSquare - (roundTripMean * roundTripMean))));
                         if (timeSinceLastPrintUS >= printIntervalSec * 1.0e6) {
                             if (printIntervalSec == 0.0) {
                                 printf("%0.1f (", elapsedSamplesMS);
@@ -175,7 +175,7 @@ void AudioTester::lookForReturnPulse(QVarLengthArray<sample_t*>& out_buffer,
 void AudioTester::writeImpulse(QVarLengthArray<sample_t*>& mInBufCopy,
                                unsigned int n_frames)
 {
-    if (not enabled) {
+    if (!enabled) {
         std::cerr << "*** AudioTester.h: writeImpulse: NOT ENABLED\n";
         return;
     }
index cfd508d3ce51f69f860938343d5f5a3f69b846d5..f8430a32d19f965a1f2dc33ded8040825c23055b 100644 (file)
@@ -149,10 +149,10 @@ class AudioTester
 
     void extendLatencyHistogram(double latencyMS)
     {
-        int latencyCell = static_cast<int>(
-            floor(std::max(latencyHistogramCellMin,
-                           std::min(latencyHistogramCellMax,
-                                    std::floor(latencyMS / latencyHistogramCellWidth)))));
+        int latencyCell = static_cast<int>(floor(std::max<double>(
+            latencyHistogramCellMin,
+            std::min<double>(latencyHistogramCellMax,
+                             std::floor(latencyMS / latencyHistogramCellWidth)))));
         latencyHistogram[latencyCell] += 1;
     }
 
index 7ed5a36c39bce0d8b0e98696ba42aeb2917afd99..04311fe79488fec738c1767deaf5091b8f30909c 100644 (file)
@@ -111,7 +111,7 @@ void Compressor::init(int samplingRate, int bufferSize)
 //*******************************************************************************
 void Compressor::compute(int nframes, float** inputs, float** outputs)
 {
-    if (not inited) {
+    if (!inited) {
         std::cerr << "*** Compressor " << this << ": init never called! Doing it now.\n";
         init(0, 0);
     }
index 89516eae7077331243e4727e91d5dd12bf41d6f2..0332c4e9dc5f0043e97392f680fe599533e7694a 100644 (file)
@@ -374,7 +374,7 @@ class Effects
         // args can be integerPresetNumberFrom1 or (all optional, any order):
         // c:compressionRatio, a:attackTimeMS, r:releaseTimeMS, g:makeUpGain
         int returnCode = 0;
-        if (not isalpha(args[0])) {
+        if (!isalpha(args[0])) {
             int presetIndexFrom1 = atoi(args);
             setCompressorPresetIndexFrom1(presetIndexFrom1, inOrOut);
         } else {
@@ -384,7 +384,7 @@ class Effects
             if (gVerboseFlag) {
                 std::cout << "parseCompressorArgs = " << args << std::endl;
             }
-            ulong argLen   = strlen(args);
+            ulong argLen   = ulong(strlen(args));
             char lastParam = '\0';
 
             CompressorPreset newPreset(
@@ -491,7 +491,7 @@ class Effects
         if (c == '-' || c == 0) {
             // happens when no -f argument specified
             returnCode = 2;
-        } else if (not isalpha(c)) {  // backward compatibility why not?, e.g., "-f 0.5"
+        } else if (!isalpha(c)) {  // backward compatibility why not?, e.g., "-f 0.5"
             // -f reverbLevelFloat
             mReverbLevel  = atof(optarg);
             outCompressor = true;
@@ -510,7 +510,7 @@ class Effects
             if (gVerboseFlag) {
                 std::cout << cmd << " argument = " << optarg << std::endl;
             }
-            ulong argLen = strlen(optarg);
+            ulong argLen = ulong(strlen(optarg));
 
             for (ulong i = 0; i < argLen; i++) {
                 if (optarg[i] != ')' && parenLevel > 0) {
@@ -681,7 +681,7 @@ class Effects
                 if (haveWarnings) {
                     std::cout << "Enable DISTORTION WARNINGS in Overflow Limiters\n";
                 }
-                if (not haveIncoming and not haveOutgoing) {
+                if (!haveIncoming && !haveOutgoing) {
                     std::cout << "Set up NO Overflow Limiters\n";
                 }
             }  // gVerboseFlag
index 62d9459d1431d23b4a174318c4902d647ba367c0..136f14d0acc1a471c03d60558cd3c1b2db88d8e6 100644 (file)
@@ -88,6 +88,12 @@ void JackAudioInterface::setup(bool verbose)
     setupClient();
     AudioInterface::setup(verbose);
     setProcessCallback();
+    if (highLatencyBufferSize()) {
+        AudioInterface::setDevicesWarningMsg(AudioInterface::DEVICE_WARN_BUFFER_LATENCY);
+    } else {
+        AudioInterface::setDevicesWarningMsg(AudioInterface::DEVICE_WARN_NONE);
+    }
+    AudioInterface::setDevicesErrorMsg(AudioInterface::DEVICE_ERR_NONE);
 }
 
 //*******************************************************************************
@@ -117,6 +123,8 @@ void JackAudioInterface::setupClient()
     /// verbose message, check how to desable them.
     {
         QMutexLocker locker(&sJackMutex);
+        // TODO: this needs a timeout because it will hang indefinitely
+        // if the Jack server is not running
 //#ifndef WAIR // WAIR
 //        mClient = jack_client_open (client_name, options, &status, server_name);
 //#else
@@ -152,7 +160,7 @@ void JackAudioInterface::setupClient()
     }
 
     // Set function to call if Jack shuts down
-    jack_on_info_shutdown(mClient, this->jackShutdown, 0);
+    jack_on_info_shutdown(mClient, this->jackShutdown, this);
 
     // Create input and output channels
     createChannels();
@@ -267,11 +275,18 @@ int JackAudioInterface::stopProcess()
 
 //*******************************************************************************
 void JackAudioInterface::jackShutdown(jack_status_t /*code*/, const char* reason,
-                                      void* /*arg*/)
+                                      void* arg)
 {
-    std::cout << reason << std::endl;
-    JackTrip::sJackStopped = true;
-    std::cout << "The Jack Server was shut down!" << std::endl;
+    std::string errorMsg = "The Jack server was shut down";
+    if (reason != nullptr) {
+        errorMsg += ": ";
+        errorMsg += reason;
+    }
+    if (arg != nullptr) {
+        static_cast<JackAudioInterface*>(arg)->mErrorMsg = errorMsg;
+    }
+    std::cerr << errorMsg << std::endl;
+    JackTrip::sAudioStopped = true;
 }
 
 //*******************************************************************************
index b72a86fd09cf8abadd27f1a5b729aa310bf718de..bf69b3523f9cf6e877f12ad86cd5364563e93d61 100644 (file)
@@ -77,8 +77,8 @@ void sigint_handler(int sig)
 }
 #endif*/
 
-bool JackTrip::sSigInt      = false;
-bool JackTrip::sJackStopped = false;
+bool JackTrip::sSigInt       = false;
+bool JackTrip::sAudioStopped = false;
 
 //*******************************************************************************
 JackTrip::JackTrip(jacktripModeT JacktripMode, dataProtocolT DataProtocolType,
@@ -153,7 +153,7 @@ JackTrip::JackTrip(jacktripModeT JacktripMode, dataProtocolT DataProtocolType,
     , mUseRtUdpPriority(false)
 {
     createHeader(mPacketHeaderType);
-    sJackStopped = false;
+    sAudioStopped = false;
 }
 
 //*******************************************************************************
@@ -177,13 +177,8 @@ void JackTrip::setupAudio(
 )
 {
     // Check if mAudioInterface has already been created or not
-    if (mAudioInterface
-        != NULL) {  // if it has been created, disconnect it from JACK and delete it
-        cout << "WARNING: JackAudio interface was setup already:" << endl;
-        cout << "It will be erased and setup again." << endl;
-        cout << gPrintSeparator << endl;
-        closeAudio();
-    }
+    if (mAudioInterface != nullptr)
+        return;
 
     // Create AudioInterface Client Object
     if (mAudiointerfaceMode == JackTrip::JACK) {
@@ -562,6 +557,12 @@ void JackTrip::startProcess(
         return;
     }
 
+    // AudioInterface::setup() can return a different buffer size
+    // if the audio interface doesn't support the one that was requested
+    if (mAudioInterface->getBufferSizeInSamples() != getBufferSizeInSamples()) {
+        setAudioBufferSizeInSamples(mAudioInterface->getBufferSizeInSamples());
+    }
+
     // cc redundant with instance creator  createHeader(mPacketHeaderType); next line
     // fixme
     createHeader(mPacketHeaderType);
@@ -1096,7 +1097,7 @@ void JackTrip::udpTimerTick()
         return;
     }
 
-    if (mStopped || sSigInt || sJackStopped) {
+    if (mStopped || sSigInt || sAudioStopped) {
         // Stop everything.
         mAwaitingUdp = false;
         mUdpSockTemp.close();
@@ -1123,7 +1124,7 @@ void JackTrip::tcpTimerTick()
         return;
     }
 
-    if (mStopped || sSigInt || sJackStopped) {
+    if (mStopped || sSigInt || sAudioStopped) {
         // Stop everything.
         mAwaitingTcp = false;
         mTcpClient.close();
@@ -1143,12 +1144,11 @@ void JackTrip::tcpTimerTick()
     }
 
     // Use randomized exponential backoff to reconnect the TCP client
-    QRandomGenerator randomizer;
     mRetries++;
     // exponential backoff sleep with 6s maximum + jitter
     int newInterval = 2000 * pow(2, mRetries);
     newInterval     = std::min(newInterval, 6000);
-    newInterval += randomizer.bounded(0, 500);
+    newInterval += QRandomGenerator::global()->bounded(0, 500);
     QString now = QDateTime::currentDateTime().toString(Qt::ISODate);
     qDebug() << "Sleep time " << newInterval << " ms at " << now;
     mRetryTimer.setInterval(newInterval);
@@ -1177,9 +1177,9 @@ void JackTrip::tcpTimerTick()
 //*******************************************************************************
 void JackTrip::stop(const QString& errorMessage)
 {
-    // Take a snapshot of sJackStopped
-    bool serverStopped = sJackStopped;
-    mStopped           = true;
+    // Take a snapshot of sAudioStopped
+    bool audioStopped = sAudioStopped;
+    mStopped          = true;
     // Make sure we're only run once
     if (mHasShutdown) {
         return;
@@ -1199,6 +1199,14 @@ void JackTrip::stop(const QString& errorMessage)
         mDataProtocolReceiver->wait();
     }
 
+    // check for errors from audio interface
+    QString audioErrorMsg;
+    if (mAudioInterface != nullptr && !mAudioInterface->getDevicesErrorMsg().empty()) {
+        audioErrorMsg = QString::fromStdString(mAudioInterface->getDevicesErrorMsg());
+    } else if (audioStopped) {
+        audioErrorMsg = QStringLiteral("Your audio interface has stopped!");
+    }
+
     // Stop the audio processes
     // mAudioInterface->stopProcess();
     closeAudio();
@@ -1207,8 +1215,8 @@ void JackTrip::stop(const QString& errorMessage)
     cout << gPrintSeparator << endl;
 
     // Emit the jack stopped signal
-    if (serverStopped) {
-        emit signalError(QStringLiteral("The Jack Server was shut down!"));
+    if (!audioErrorMsg.isEmpty()) {
+        emit signalError(audioErrorMsg);
     } else if (errorMessage.isEmpty()) {
         emit signalProcessesStopped();
     } else {
@@ -1361,12 +1369,11 @@ int JackTrip::clientPingToServerStart()
 #endif
     {
         QMutexLocker lock(&mTimerMutex);
-        QRandomGenerator randomizer;
         mAwaitingTcp = true;
         mElapsedTime = 0;
         mEndTime     = 30000;  // Timeout after 30 seconds.
-        mRetryTimer.setInterval(randomizer.bounded(
-            static_cast<int>(0), static_cast<int>(2000 * pow(2, mRetries))));
+        mRetryTimer.setInterval(
+            QRandomGenerator::global()->bounded(0, int(2000 * pow(2, mRetries))));
         mRetryTimer.setSingleShot(true);
         mRetryTimer.disconnect();
         connect(&mRetryTimer, &QTimer::timeout, this, &JackTrip::tcpTimerTick);
index 3a0a7079e027719bd00d6560f84a661169389562..49b78bee88438b0f8ce204949288feac5355dbdf 100644 (file)
@@ -158,7 +158,7 @@ class JackTrip : public QObject
         sSigInt = true;
     }
     static bool sSigInt;
-    static bool sJackStopped;
+    static bool sAudioStopped;
 
     /// \brief Starting point for the thread
     /*virtual void run() {
@@ -502,7 +502,7 @@ class JackTrip : public QObject
             return mAudioInterface->getSizeInBytesPerChannel() * mNumNetRevChans;
         else  // not wair
 #endif        // endwhere
-            return mAudioInterface->getSizeInBytesPerChannel() * mNumAudioChansIn;
+            return int(mAudioInterface->getSizeInBytesPerChannel()) * mNumAudioChansIn;
     }
 
     int getTotalAudioOutputPacketSizeInBytes() const
@@ -512,7 +512,36 @@ class JackTrip : public QObject
             return mAudioInterface->getSizeInBytesPerChannel() * mNumNetRevChans;
         else  // not wair
 #endif        // endwhere
-            return mAudioInterface->getSizeInBytesPerChannel() * mNumAudioChansOut;
+            return int(mAudioInterface->getSizeInBytesPerChannel()) * mNumAudioChansOut;
+    }
+    QString getDevicesWarningMsg() const
+    {
+        if (mAudioInterface == nullptr)
+            return QLatin1String("");
+        return QString::fromStdString(mAudioInterface->getDevicesWarningMsg());
+    }
+    QString getDevicesErrorMsg() const
+    {
+        if (mAudioInterface == nullptr)
+            return QLatin1String("");
+        return QString::fromStdString(mAudioInterface->getDevicesErrorMsg());
+    }
+    QString getDevicesWarningHelpUrl() const
+    {
+        if (mAudioInterface == nullptr)
+            return QLatin1String("");
+        return QString::fromStdString(mAudioInterface->getDevicesWarningHelpUrl());
+    }
+    QString getDevicesErrorHelpUrl() const
+    {
+        if (mAudioInterface == nullptr)
+            return QLatin1String("");
+        return QString::fromStdString(mAudioInterface->getDevicesErrorHelpUrl());
+    }
+    bool getHighLatencyFlag() const
+    {
+        return (mAudioInterface == nullptr) ? false
+                                            : mAudioInterface->getHighLatencyFlag();
     }
     //@}
     //------------------------------------------------------------------------------------
index 714b3c8b25fb50fccf02fdf396e55ab618d606f8..b6327946443ceebadf9cca08d579b6da2f2a051f 100644 (file)
@@ -283,7 +283,7 @@ void JackTripWorker::receivedDataUDP()
     if (JackTrip::NORMAL == PeerNumOutgoingChannels) {
         mJackTrip->setNumInputChannels(PeerNumIncomingChannels);
         mJackTrip->setNumOutputChannels(PeerNumIncomingChannels);
-    } else if (std::numeric_limits<uint8_t>::max() == PeerNumOutgoingChannels) {
+    } else if ((std::numeric_limits<uint8_t>::max)() == PeerNumOutgoingChannels) {
         mJackTrip->setNumInputChannels(PeerNumIncomingChannels);
         mJackTrip->setNumOutputChannels(0);
     } else {
index d37479dc224113149572c09f426edb9c80a9e352..99b1121e27667aa12e408ffbaa61a149809e27e5 100644 (file)
@@ -79,7 +79,7 @@ JitterBuffer::JitterBuffer(int buf_samples, int qlen, int sample_rate, int strat
     mOverflowDecTolerance  = 100 * mMaxLatency;
     mWritePosition         = mMaxLatency;
     mStatUnit              = mSlotSize;
-    mLevelDownRate         = std::min(256, mFPP) / (5.0 * sample_rate) * mSlotSize;
+    mLevelDownRate         = std::min<int>(256, mFPP) / (5.0 * sample_rate) * mSlotSize;
     mOverflowDropStep      = mMaxLatency / 2;
     mLevelCur              = mMaxLatency;
     mLevel                 = mLevelCur;
@@ -113,7 +113,7 @@ JitterBuffer::JitterBuffer(int buf_samples, int qlen, int sample_rate, int strat
     }
     mAutoQRate      = mSlotSize * 0.5;
     mAutoQRateMin   = mSlotSize * 0.0005;
-    mAutoQRateDecay = 1.0 - std::min(mFPP * 1.2e-6, 0.0005);
+    mAutoQRateDecay = 1.0 - std::min<double>(mFPP * 1.2e-6, 0.0005);
 }
 
 //*******************************************************************************
@@ -155,10 +155,10 @@ bool JitterBuffer::insertSlotNonBlocking(const int8_t* ptrToSlot, int len, int l
         mBufDecOverflow += delta;
         mLevelCur = mMaxLatency;
     } else if (0 > available
-               && mLevelCur < std::max(mInSlotSize + mMinLevelThreshold,
-                                       mMaxLatency - mUnderrunIncTolerance
-                                           - 2 * mSlotSize * lastCorrFactor())) {
-        delta = -std::min(-available, mSlotSize);
+               && mLevelCur < std::max<double>(mInSlotSize + mMinLevelThreshold,
+                                               mMaxLatency - mUnderrunIncTolerance
+                                                   - 2 * mSlotSize * lastCorrFactor())) {
+        delta = -std::min<int32_t>(-available, mSlotSize);
         mBufIncUnderrun += -delta;
     } else if (mLevelCur
                < mMaxLatency - mCorrIncTolerance - 6 * mSlotSize * lastCorrFactor()) {
@@ -176,7 +176,7 @@ bool JitterBuffer::insertSlotNonBlocking(const int8_t* ptrToSlot, int len, int l
     }
 
     int wpos = mWritePosition % mTotalSize;
-    int n    = std::min(mTotalSize - wpos, len);
+    int n    = std::min<int>(mTotalSize - wpos, len);
     std::memcpy(mRingBuffer + wpos, ptrToSlot, n);
     if (n < len) {
         // cout << "split write: " << len << "-" << n << endl;
@@ -199,7 +199,7 @@ void JitterBuffer::readSlotNonBlocking(int8_t* ptrToReadSlot)
     mReadsNew += len;
     int32_t available = mWritePosition - mReadPosition;
     if (available < mLevelCur) {
-        mLevelCur = std::max((double)available, mLevelCur - mLevelDownRate);
+        mLevelCur = std::max<double>((double)available, mLevelCur - mLevelDownRate);
     } else {
         mLevelCur = available;
     }
@@ -231,7 +231,7 @@ void JitterBuffer::readSlotNonBlocking(int8_t* ptrToReadSlot)
 
     int read_len = qBound(0, available, len);
     int rpos     = mReadPosition % mTotalSize;
-    int n        = std::min(mTotalSize - rpos, read_len);
+    int n        = std::min<int>(mTotalSize - rpos, read_len);
     std::memcpy(ptrToReadSlot, mRingBuffer + rpos, n);
     if (n < read_len) {
         // cout << "split read: " << read_len << "-" << n << endl;
@@ -280,7 +280,7 @@ void JitterBuffer::readBroadcastSlot(int8_t* ptrToReadSlot)
     int read_len      = qBound(0, available, len);
     if (len == mSlotSize) {
         int rpos = mBroadcastPosition % mTotalSize;
-        int n    = std::min(mTotalSize - rpos, read_len);
+        int n    = std::min<int>(mTotalSize - rpos, read_len);
         std::memcpy(ptrToReadSlot, mRingBuffer + rpos, n);
         if (n < read_len) {
             // cout << "split read: " << read_len << "-" << n << endl;
@@ -314,7 +314,8 @@ void JitterBuffer::processPacketLoss(int lostLen)
     mSkewRaw -= lostLen;
 
     int32_t available = mWritePosition - mReadPosition;
-    int delta = std::min(available + mInSlotSize + lostLen - mMaxLatency, lostLen);
+    int delta =
+        std::min<int32_t>(available + mInSlotSize + lostLen - mMaxLatency, lostLen);
     if (0 < delta) {
         lostLen -= delta;
         mBufDecPktLoss += delta;
@@ -327,7 +328,7 @@ void JitterBuffer::processPacketLoss(int lostLen)
                        && mLevelCur > mMaxLatency
                                           - mOverflowDecTolerance
                                                 * (1.1 - lastCorrFactor())))) {
-        delta = std::min(lostLen, mSlotSize);
+        delta = std::min<int>(lostLen, mSlotSize);
         lostLen -= delta;
         mBufDecPktLoss += delta;
         mLevelCur -= delta;
@@ -336,16 +337,16 @@ void JitterBuffer::processPacketLoss(int lostLen)
     }
     if (lostLen >= mTotalSize) {
         std::memset(mRingBuffer, 0, mTotalSize);
-        mUnderruns += std::max(0, lostLen - std::max(0, -available));
+        mUnderruns += std::max<int>(0, lostLen - std::max<int>(0, -available));
     } else if (0 < lostLen) {
         int wpos = mWritePosition % mTotalSize;
-        int n    = std::min(mTotalSize - wpos, lostLen);
+        int n    = std::min<int>(mTotalSize - wpos, lostLen);
         std::memset(mRingBuffer + wpos, 0, n);
         if (n < lostLen) {
             // cout << "split write: " << lostLen << "-" << n << endl;
             std::memset(mRingBuffer, 0, lostLen - n);
         }
-        mUnderruns += std::max(0, lostLen - std::max(0, -available));
+        mUnderruns += std::max<int>(0, lostLen - std::max<int>(0, -available));
     }
     mWritePosition += lostLen;
 }
index 7414420f6f8c8103bb22e5e89f61e356a244054a..a4ca931d1c80e9e82bbaa35cf7bcc32b2d280c5e 100644 (file)
@@ -80,7 +80,10 @@ class JitterBuffer : public RingBuffer
     uint32_t mLastCorrCounter;
     int mLastCorrDirection;
     double mMinLevelThreshold;
-    double lastCorrFactor() const { return 500.0 / std::max(500U, mLastCorrCounter); }
+    double lastCorrFactor() const
+    {
+        return 500.0 / std::max<uint32_t>(500U, mLastCorrCounter);
+    }
 
     int mAutoQueue;
     double mAutoQueueCorr;
index ee05e52429666557fafd204699a38c04358c74ca..8796075df64a3bbb142d7a4ae00a08f88f905e39 100644 (file)
@@ -111,7 +111,7 @@ void Limiter::init(int samplingRate, int bufferSize)
 //*******************************************************************************
 void Limiter::compute(int nframes, float** inputs, float** outputs)
 {
-    if (not inited) {
+    if (!inited) {
         std::cerr << "*** Limiter " << this << ": init never called! Doing it now.\n";
         init(0, 0);
     }
index 560f1fac3636ad4e289ec3f1ba75c09d3ab4d261..165a5c807304e144bbd056a8ec6400ecc90130c7 100644 (file)
@@ -75,7 +75,7 @@ class Limiter : public ProcessPlugin
 
     void setWarningAmplitude(double wa)
     {  // setting to 0 turns off warnings
-        warningAmp = std::max(0.0, std::min(1.0, wa));
+        warningAmp = std::max<double>(0.0, std::min<double>(1.0, wa));
     }
 
    private:
@@ -92,7 +92,7 @@ class Limiter : public ProcessPlugin
                                               // ../faust-src/limiterdsp.dsp
             if (limiterAmp >= warningAmp) {
                 warnCount++;
-                peakMagnitude = std::max(peakMagnitude, limiterAmp);
+                peakMagnitude = std::max<double>(peakMagnitude, limiterAmp);
                 if (warnCount == nextWarning) {
                     double peakMagnitudeDB = 20.0 * std::log10(peakMagnitude);
                     double warningAmpDB    = 20.0 * std::log10(warningAmp);
index e0136014ff7135c5d41f86104b5b0c45b9b4c56b..9130f15b9f7189e150833d03354212be3b41d82b 100644 (file)
@@ -99,7 +99,7 @@ void Meter::init(int samplingRate, int bufferSize)
 //*******************************************************************************
 void Meter::compute(int nframes, float** inputs, float** outputs)
 {
-    if (not inited) {
+    if (!inited) {
         std::cerr << "*** Meter " << this << ": init never called! Doing it now.\n";
         init(0, 0);
     }
@@ -129,10 +129,10 @@ void Meter::compute(int nframes, float** inputs, float** outputs)
         /* Use the existing value of mValues[i] as
            the threshold - this will be reset to the default floor of -80dB
            on each timeout */
-        float maxSample = *std::max_element(mBuffer, mBuffer + nframes);
+        float maxSample = *(std::max_element)(mBuffer, mBuffer + nframes);
 
         /* Update mValues */
-        mValues[i] = std::max(mValues[i], maxSample);
+        mValues[i] = std::max<float>(mValues[i], maxSample);
     }
 
     /* Set processed audio flag */
index 34061f06cae58d42630e12224b69b73bd63e31ed..fb17e166401d8dba0c1ecae88aa226d46881f633 100644 (file)
@@ -88,7 +88,7 @@ void Monitor::init(int samplingRate, int bufferSize)
 //*******************************************************************************
 void Monitor::compute(int nframes, float** inputs, float** outputs)
 {
-    if (not inited) {
+    if (!inited) {
         std::cerr << "*** Monitor " << this << ": init never called! Doing it now.\n";
         init(0, 0);
     }
index 23011e7e12fd19b67e63cbf51d6e290875bed04c..54f6e3fe83797d1b0ebd49215ce0c7cc396ca35d 100644 (file)
@@ -117,7 +117,7 @@ void DefaultHeader::fillHeaderCommonFromAudio()
     if (mJackTrip->getNumInputChannels() == mJackTrip->getNumOutputChannels()) {
         mHeader.NumOutgoingChannelsToNet = 0;
     } else if (0 == mJackTrip->getNumInputChannels()) {
-        mHeader.NumOutgoingChannelsToNet = std::numeric_limits<uint8_t>::max();
+        mHeader.NumOutgoingChannelsToNet = (std::numeric_limits<uint8_t>::max)();
     } else {
         mHeader.NumOutgoingChannelsToNet = mJackTrip->getNumInputChannels();
     }
index 3326f48d42927c7828398851dc5fdb21351604f1..c8f3b45f9bf636ba458b2a39f89c7b7d451de2d2 100644 (file)
@@ -132,7 +132,7 @@ void Reverb::init(int samplingRate, int bufferSize)
 //*******************************************************************************
 void Reverb::compute(int nframes, float** inputs, float** outputs)
 {
-    if (not inited) {
+    if (!inited) {
         std::cerr << "*** Reverb " << this << ": init never called! Doing it now.\n";
         init(0, 0);
     }
index b2fc0e47de6b38c799a2aaa0e18edda67d2d47fe..370355ed9736d800dd12fc8c079e7177d9265f31 100644 (file)
@@ -129,7 +129,7 @@ void RingBuffer::readSlotBlocking(int8_t* ptrToReadSlot)
     while (mFullSlots == 0) {
         // std::cerr << "READ UNDER-RUN BLOCKING before" << endl;
         mBufferIsNotEmpty.wait(&mMutex, 200);
-        if (JackTrip::sJackStopped) {
+        if (JackTrip::sAudioStopped) {
             return;
         }
     }
@@ -190,7 +190,7 @@ void RingBuffer::readSlotNonBlocking(int8_t* ptrToReadSlot)
     QMutexLocker locker(&mMutex);  // lock the mutex
     ++mReadsNew;
     if (mFullSlots < mLevelCur) {
-        mLevelCur = std::max((double)mFullSlots, mLevelCur - mLevelDownRate);
+        mLevelCur = std::max<double>((double)mFullSlots, mLevelCur - mLevelDownRate);
     } else {
         mLevelCur = mFullSlots;
     }
index 814b3b53e6f2f1d33676a1f1562388a1727adc14..0b63d13d31298fb286f84d499cb20156cd29a586 100644 (file)
@@ -50,14 +50,45 @@ using std::endl;
 //*******************************************************************************
 void RtAudioDevice::print() const
 {
-    std::cout << "[" << RtAudio::getApiDisplayName(this->api) << " - " << this->apiIndex
-              << "]"
+    std::cout << "[" << RtAudio::getApiDisplayName(this->api) << " - " << this->ID << "]"
               << ": \"";
-    std::cout << this->name.toStdString() << "\" ";
+    std::cout << this->name << "\" ";
     std::cout << "(" << this->inputChannels << " ins, " << this->outputChannels
               << " outs)" << endl;
 }
 
+//*******************************************************************************
+void RtAudioDevice::printVerbose() const
+{
+    cout << "Audio Device  [" << this->api << " - " << this->ID << "] : " << this->name
+         << endl;
+    cout << "  Output Channels : " << this->outputChannels << endl;
+    cout << "  Input Channels  : " << this->inputChannels << endl;
+    cout << "  Supported Sampling Rates: ";
+    for (unsigned int i = 0; i < this->sampleRates.size(); i++) {
+        cout << this->sampleRates[i] << " ";
+    }
+    cout << endl;
+    if (this->isDefaultOutput) {
+        cout << "  --Default Output Device--" << endl;
+    }
+    if (this->isDefaultInput) {
+        cout << "  --Default Input Device--" << endl;
+    }
+#if RTAUDIO_VERSION_MAJOR < 6
+    if (this->probed) {
+        cout << "  --Probed Successful--" << endl;
+    }
+#endif
+}
+
+//*******************************************************************************
+RtAudioDevice& RtAudioDevice::operator=(const RtAudio::DeviceInfo& info)
+{
+    RtAudio::DeviceInfo::operator=(info);
+    return *this;
+}
+
 //*******************************************************************************
 RtAudioInterface::RtAudioInterface(QVarLengthArray<int> InputChans,
                                    QVarLengthArray<int> OutputChans,
@@ -66,55 +97,45 @@ RtAudioInterface::RtAudioInterface(QVarLengthArray<int> InputChans,
                                    bool processWithNetwork, JackTrip* jacktrip)
     : AudioInterface(InputChans, OutputChans, InputMixMode, AudioBitResolution,
                      processWithNetwork, jacktrip)
-    , mRtAudio(NULL)
 {
 }
 
 //*******************************************************************************
-RtAudioInterface::~RtAudioInterface()
-{
-    if (mRtAudio != NULL) {
-        delete mRtAudio;
-    }
-
-    if (mStereoToMonoMixer != NULL) {
-        delete mStereoToMonoMixer;
-    }
-}
+RtAudioInterface::~RtAudioInterface() {}
 
 //*******************************************************************************
 void RtAudioInterface::setup(bool verbose)
 {
     // Initialize Buffer array to read and write audio and members
-    QVarLengthArray<int> iChans = getInputChannels();
-    QVarLengthArray<int> oChans = getOutputChannels();
-
-    uint32_t chansIn     = iChans.size();
-    uint32_t chansOut    = oChans.size();
-    uint32_t baseChanIn  = 0;
-    uint32_t baseChanOut = 0;
-
-    if (iChans.size() >= 1) {
-        int min = iChans.at(0);
-        for (int i = 0; i < iChans.size(); i++) {
-            if (iChans.at(i) < min) {
-                min = iChans.at(i);
+    QVarLengthArray<int> in_chans  = getInputChannels();
+    QVarLengthArray<int> out_chans = getOutputChannels();
+
+    uint32_t in_chans_num   = in_chans.size();
+    uint32_t out_chans_num  = out_chans.size();
+    uint32_t in_chans_base  = 0;
+    uint32_t out_chans_base = 0;
+
+    if (in_chans.size() >= 1) {
+        int min = in_chans.at(0);
+        for (int i = 0; i < in_chans.size(); i++) {
+            if (in_chans.at(i) < min) {
+                min = in_chans.at(i);
             }
         }
         if (min >= 0) {
-            baseChanIn = min;
+            in_chans_base = min;
         }
     }
 
-    if (oChans.size() >= 1) {
-        int min = oChans.at(0);
-        for (int i = 0; i < oChans.size(); i++) {
-            if (oChans.at(i) < min) {
-                min = iChans.at(i);
+    if (out_chans.size() >= 1) {
+        int min = out_chans.at(0);
+        for (int i = 0; i < out_chans.size(); i++) {
+            if (out_chans.at(i) < min) {
+                min = in_chans.at(i);
             }
         }
         if (min >= 0) {
-            baseChanOut = min;
+            out_chans_base = min;
         }
     }
 
@@ -124,130 +145,88 @@ void RtAudioInterface::setup(bool verbose)
     AudioInterface::setDevicesWarningMsg(AudioInterface::DEVICE_WARN_NONE);
     AudioInterface::setDevicesErrorMsg(AudioInterface::DEVICE_ERR_NONE);
 
-    int index_in  = -1;
-    int index_out = -1;
-    std::string api_in;
-    std::string api_out;
-
     if (mDevices.empty())
         scanDevices(mDevices);
 
-    unsigned int n_devices_input  = getNumInputDevices();
-    unsigned int n_devices_output = getNumOutputDevices();
-    unsigned int n_devices_total  = n_devices_input + n_devices_output;
+    RtAudioDevice in_device;
+    RtAudioDevice out_device;
 
-    RtAudio* rtAudioIn  = NULL;
-    RtAudio* rtAudioOut = NULL;
+    if (getNumInputDevices() == 0) {
+        AudioInterface::setDevicesErrorMsg(AudioInterface::DEVICE_ERR_NO_DEVICES);
+        throw std::runtime_error("no input audio devices found");
+    }
 
-    // unsigned int n_devices = mRtAudio->getDeviceCount();
-    if (n_devices_total < 1) {
+    if (getNumOutputDevices() == 0) {
         AudioInterface::setDevicesErrorMsg(AudioInterface::DEVICE_ERR_NO_DEVICES);
-        cout << "No audio devices found!" << endl;
-        std::exit(0);
+        throw std::runtime_error("no output audio devices found");
+    }
+
+    // Locate the selected input audio device
+    auto in_name = getInputDevice();
+    if (in_name.empty()) {
+        mRtAudio.reset(new RtAudio);
+        long default_device_id = getDefaultDevice(*mRtAudio, true);
+        if (!getDeviceInfoFromId(default_device_id, in_device, true))
+            throw std::runtime_error("default input device not found");
+        cout << "Selected default INPUT device" << endl;
     } else {
-        // Locate the selected input audio device
-        auto inName = getInputDevice();
-        getDeviceInfoFromName(inName, &index_in, &api_in, true);
-        if (!inName.empty() && (index_in < 0)) {
-            throw std::runtime_error("Requested input device \"" + inName
+        if (!getDeviceInfoFromName(in_name, in_device, true)) {
+            throw std::runtime_error("Requested input device \"" + in_name
                                      + "\" not found.");
         }
-        rtAudioIn = new RtAudio(RtAudio::getCompiledApiByName(api_in));
-
-        // The selected input audio device is not available, so select the default device
-        if (index_in < 0) {
-            // reset rtAudioIn using the system default
-            delete rtAudioIn;
-            rtAudioIn = new RtAudio;
-            api_in    = RtAudio::getApiName(rtAudioIn->getCurrentApi());
-
-            // Edge case for Linux Pulse Audio
-            if (rtAudioIn->getCurrentApi() == RtAudio::LINUX_PULSE) {
-                index_in = getDefaultDeviceForLinuxPulseAudio(true);
-            } else {
-                index_in = rtAudioIn->getDefaultInputDevice();
-            }
-
-            cout << "Selected default INPUT device" << endl;
-        } else {
-            cout << "Selected INPUT device " << inName << endl;
-        }
+        mRtAudio.reset(new RtAudio(in_device.api));
+        cout << "Selected INPUT device " << in_name << endl;
+    }
 
-        // Locate the selected output audio device
-        auto outName = getOutputDevice();
-        getDeviceInfoFromName(outName, &index_out, &api_out, false);
-        if (!outName.empty() && (index_out < 0)) {
-            throw std::runtime_error("Requested output device \"" + outName
+    // Locate the selected output audio device
+    auto out_name = getOutputDevice();
+    if (out_name.empty()) {
+        long default_device_id = getDefaultDevice(*mRtAudio, false);
+        if (!getDeviceInfoFromId(default_device_id, out_device, false))
+            throw std::runtime_error("default output device not found");
+        cout << "Selected default OUTPUT device" << endl;
+    } else {
+        if (!getDeviceInfoFromName(out_name, out_device, false)) {
+            throw std::runtime_error("Requested output device \"" + out_name
                                      + "\" not found.");
         }
-        rtAudioOut = new RtAudio(RtAudio::getCompiledApiByName(api_out));
-
-        // The selected output audio device is not available, so select the default device
-        if (index_out < 0) {
-            // reset rtAudioIn using the system default
-            delete rtAudioOut;
-            rtAudioOut = new RtAudio;
-            api_out    = RtAudio::getApiName(rtAudioOut->getCurrentApi());
-
-            // Edge case for Linux Pulse Audio
-            if (rtAudioOut->getCurrentApi() == RtAudio::LINUX_PULSE) {
-                index_out = getDefaultDeviceForLinuxPulseAudio(false);
-            } else {
-                index_out = rtAudioOut->getDefaultOutputDevice();
-            }
-
-            cout << "Selected default OUTPUT device" << endl;
-
-        } else {
-            cout << "Selected OUTPUT device " << outName << endl;
-        }
+        cout << "Selected OUTPUT device " << out_name << endl;
     }
 
-    auto dev_info_input  = rtAudioIn->getDeviceInfo(index_in);
-    auto dev_info_output = rtAudioOut->getDeviceInfo(index_out);
-
-    if (baseChanIn + chansIn > dev_info_input.inputChannels) {
-        baseChanIn = 0;
-        chansIn    = 2;
-        if (dev_info_input.inputChannels < 2) {
-            chansIn = 1;
+    if (in_chans_base + in_chans_num > in_device.inputChannels) {
+        in_chans_base = 0;
+        in_chans_num  = 2;
+        if (in_device.inputChannels < 2) {
+            in_chans_num = 1;
         }
     }
 
-    if (baseChanOut + chansOut > dev_info_output.outputChannels) {
-        baseChanOut = 0;
-        chansOut    = 2;
-        if (dev_info_output.outputChannels < 2) {
-            chansOut = 1;
+    if (out_chans_base + out_chans_num > out_device.outputChannels) {
+        out_chans_base = 0;
+        out_chans_num  = 2;
+        if (out_device.outputChannels < 2) {
+            out_chans_num = 1;
         }
     }
 
     if (verbose) {
         cout << "INPUT DEVICE:" << endl;
-        printDeviceInfo(api_in, index_in);
-
+        in_device.printVerbose();
         cout << gPrintSeparator << endl;
-        cout << "OUTPUT DEVICE:" << endl;
 
-        printDeviceInfo(api_out, index_out);
+        cout << "OUTPUT DEVICE:" << endl;
+        out_device.printVerbose();
         cout << gPrintSeparator << endl;
     }
 
-    if (n_devices_input == 0) {
-        AudioInterface::setDevicesErrorMsg(AudioInterface::DEVICE_ERR_NO_INPUTS);
-    } else if (n_devices_output == 0) {
-        AudioInterface::setDevicesErrorMsg(AudioInterface::DEVICE_ERR_NO_OUTPUTS);
-    }
-
-    delete rtAudioIn;
-    delete rtAudioOut;
-    if (api_in == api_out) {
-        mRtAudio = new RtAudio(RtAudio::getCompiledApiByName(api_in));
+    if (in_device.api == out_device.api) {
 #ifdef _WIN32
-        if (api_in != "asio") {
-            AudioInterface::setDevicesWarningMsg(AudioInterface::DEVICE_WARN_LATENCY);
+        if (in_device.api != RtAudio::WINDOWS_ASIO) {
+            AudioInterface::setDevicesWarningMsg(
+                AudioInterface::DEVICE_WARN_ASIO_LATENCY);
             AudioInterface::setDevicesErrorMsg(AudioInterface::DEVICE_ERR_NONE);
-        } else if (api_in == "asio" && index_in != index_out) {
+        } else if (in_device.api == RtAudio::WINDOWS_ASIO
+                   && in_device.ID != out_device.ID) {
             AudioInterface::setDevicesWarningMsg(AudioInterface::DEVICE_WARN_NONE);
             AudioInterface::setDevicesErrorMsg(AudioInterface::DEVICE_ERR_SAME_ASIO);
         }
@@ -255,16 +234,15 @@ void RtAudioInterface::setup(bool verbose)
     } else {
         AudioInterface::setDevicesWarningMsg(AudioInterface::DEVICE_WARN_NONE);
         AudioInterface::setDevicesErrorMsg(AudioInterface::DEVICE_ERR_INCOMPATIBLE);
-        mRtAudio = NULL;
     }
 
     RtAudio::StreamParameters in_params, out_params;
-    in_params.deviceId      = index_in;
-    out_params.deviceId     = index_out;
-    in_params.nChannels     = chansIn;
-    out_params.nChannels    = chansOut;
-    in_params.firstChannel  = baseChanIn;
-    out_params.firstChannel = baseChanOut;
+    in_params.deviceId      = in_device.ID;
+    out_params.deviceId     = out_device.ID;
+    in_params.nChannels     = in_chans_num;
+    out_params.nChannels    = out_chans_num;
+    in_params.firstChannel  = in_chans_base;
+    out_params.firstChannel = out_chans_base;
 
     RtAudio::StreamOptions options;
     // The second flag affects linux and mac only
@@ -279,44 +257,90 @@ void RtAudioInterface::setup(bool verbose)
     // Update parent class
     QVarLengthArray<int> updatedInputChannels;
     QVarLengthArray<int> updatedOutputChannels;
-    updatedInputChannels.resize(chansIn);
-    updatedOutputChannels.resize(chansOut);
-    for (uint32_t i = 0; i < chansIn; i++) {
-        updatedInputChannels[i] = baseChanIn + i;
+    updatedInputChannels.resize(in_chans_num);
+    updatedOutputChannels.resize(out_chans_num);
+    for (uint32_t i = 0; i < in_chans_num; i++) {
+        updatedInputChannels[i] = in_chans_base + i;
     }
-    for (uint32_t i = 0; i < chansOut; i++) {
-        updatedOutputChannels[i] = baseChanOut + i;
+    for (uint32_t i = 0; i < out_chans_num; i++) {
+        updatedOutputChannels[i] = out_chans_base + i;
     }
     setInputChannels(updatedInputChannels);
     setOutputChannels(updatedOutputChannels);
 
     // Setup buffers
-    mInBuffer.resize(chansIn);
-    mOutBuffer.resize(chansOut);
+    mInBuffer.resize(in_chans_num);
+    mOutBuffer.resize(out_chans_num);
 
     unsigned int sampleRate   = getSampleRate();           // mSamplingRate;
     unsigned int bufferFrames = getBufferSizeInSamples();  // mBufferSize;
-    mStereoToMonoMixer        = new StereoToMono();
-    mStereoToMonoMixer->init(sampleRate, bufferFrames);
 
-    // Setup parent class
-    AudioInterface::setup(verbose);
+    if (in_device.api != out_device.api)
+        return;
+
+    std::string errorText;
+
+    // IMPORTANT NOTE: It's VERY important to remember to pass "this"
+    // to the user data for the process callback, otherwise member won't
+    // be accessible
 
+#if RTAUDIO_VERSION_MAJOR < 6
+    // function pointers used before v6, and lambda conversion to function
+    // pointers does not support capture
+    RtAudioErrorCallback errorFunc = [](RtAudioError::Type type,
+                                        const std::string& errorText) {
+        errorCallback(type, errorText, nullptr);
+    };
     try {
-        // IMPORTANT NOTE: It's VERY important to remember to pass "this"
-        // to the user data in the process callback, otherwise member won't
-        // be accessible
-        if (mRtAudio != NULL) {
-            mRtAudio->openStream(&out_params, &in_params, RTAUDIO_FLOAT32, sampleRate,
-                                 &bufferFrames, &RtAudioInterface::wrapperRtAudioCallback,
-                                 this, &options, &RtAudioInterface::RtAudioErrorCallback);
-        }
+        mRtAudio->openStream(&out_params, &in_params, RTAUDIO_FLOAT32, sampleRate,
+                             &bufferFrames, &RtAudioInterface::wrapperRtAudioCallback,
+                             this, &options, errorFunc);
+    } catch (RtAudioError& e) {
+        errorText = e.getMessage();
+    }
+#else
+    // we need a wrapper since RtAudio doesn't support void* arguments
+    RtAudioErrorCallback errorFunc = [this](RtAudioErrorType type,
+                                            const std::string& errorText) {
+        errorCallback(type, errorText, this);
+    };
+    mRtAudio->setErrorCallback(errorFunc);
+    if (RTAUDIO_NO_ERROR
+        != mRtAudio->openStream(&out_params, &in_params, RTAUDIO_FLOAT32, sampleRate,
+                                &bufferFrames, &RtAudioInterface::wrapperRtAudioCallback,
+                                this, &options)) {
+        errorText = mRtAudio->getErrorText();
+    }
+#endif
 
+    if (!errorText.empty()) {
+        std::cerr << "RtAudioInterface failed to open stream: " << errorText << '\n'
+                  << std::endl;
+        mRtAudio.reset();
+        throw std::runtime_error(errorText);
+    }
+
+    // RtAudio::openStream() can return a different buffer size
+    // if the audio interface doesn't support the one that was requested
+    if (bufferFrames != getBufferSizeInSamples()) {
+        std::cout << "RtAudioInterface updated buffer size to " << bufferFrames
+                  << " samples" << std::endl;
         setBufferSize(bufferFrames);
-    } catch (RtAudioError& e) {
-        std::cout << e.getMessage() << '\n' << std::endl;
-        throw std::runtime_error(e.getMessage());
     }
+
+    if (highLatencyBufferSize() && getDevicesWarningMsg().empty()) {
+        setDevicesWarningMsg(AudioInterface::DEVICE_WARN_BUFFER_LATENCY);
+    }
+
+    // Setup parent class
+    // This MUST be after buffer size is finalized, so that plugins
+    // are initialized with the correct settings
+    AudioInterface::setup(verbose);
+
+    // Setup StereoToMonoMixer
+    // This MUST be after RtAudio::openSteram in case bufferFrames changes
+    mStereoToMonoMixerPtr.reset(new StereoToMono());
+    mStereoToMonoMixerPtr->init(sampleRate, bufferFrames);
 }
 
 //*******************************************************************************
@@ -330,8 +354,8 @@ void RtAudioInterface::printDevices()
 unsigned int RtAudioInterface::getNumInputDevices() const
 {
     unsigned int deviceCount = 0;
-    for (int n = 0; n < mDevices.size(); ++n) {
-        if (mDevices[n].inputChannels > 0) {
+    for (const RtAudioDevice& d : mDevices) {
+        if (d.inputChannels > 0) {
             ++deviceCount;
         }
     }
@@ -342,14 +366,48 @@ unsigned int RtAudioInterface::getNumInputDevices() const
 unsigned int RtAudioInterface::getNumOutputDevices() const
 {
     unsigned int deviceCount = 0;
-    for (int n = 0; n < mDevices.size(); ++n) {
-        if (mDevices[n].outputChannels > 0) {
+    for (const RtAudioDevice& d : mDevices) {
+        if (d.outputChannels > 0) {
             ++deviceCount;
         }
     }
     return deviceCount;
 }
 
+//*******************************************************************************
+void RtAudioInterface::getDeviceIds(RtAudio& rtaudio, std::vector<unsigned int>& ids)
+{
+#if RTAUDIO_VERSION_MAJOR < 6
+    for (unsigned int i = 0; i < rtaudio.getDeviceCount(); i++) {
+        ids.push_back(i);
+    }
+#else
+    ids = rtaudio.getDeviceIds();
+#endif
+}
+
+//*******************************************************************************
+long RtAudioInterface::getDefaultDevice(RtAudio& rtaudio, bool isInput)
+{
+#if RTAUDIO_VERSION_MAJOR < 6
+    if (rtaudio.getCurrentApi() == RtAudio::LINUX_PULSE) {
+        return getDefaultDeviceForLinuxPulseAudio(isInput);
+    }
+#endif
+
+    long defaultId =
+        isInput ? rtaudio.getDefaultInputDevice() : rtaudio.getDefaultOutputDevice();
+
+#if RTAUDIO_VERSION_MAJOR >= 6
+    // In RtAudio v6, 0 is an invalid device id and used to indicate that no devices are
+    // available
+    if (defaultId == 0)
+        defaultId = -1;
+#endif
+
+    return defaultId;
+}
+
 //*******************************************************************************
 // Use this for getting the default device with PulseAudio
 // At the time of writing this, the latest RtAudio release did not properly
@@ -357,51 +415,27 @@ unsigned int RtAudioInterface::getNumOutputDevices() const
 // Once this functinoality is provided upstream and in the distributions'
 // package managers, the following function can be removed and the default device
 // can be obtained by calls to getDefaultInputDevice() / getDefaultOutputDevice()
-unsigned int RtAudioInterface::getDefaultDeviceForLinuxPulseAudio(bool isInput)
+long RtAudioInterface::getDefaultDeviceForLinuxPulseAudio(bool isInput)
 {
-    RtAudio rtaudio;
-    for (unsigned int i = 0; i < rtaudio.getDeviceCount(); i++) {
-        auto info = rtaudio.getDeviceInfo(i);
-        if (info.probed == true) {
-            if (info.isDefaultInput && isInput) {
-                return i;
-            } else if (info.isDefaultOutput && !isInput) {
-                return i;
-            }
+    // Iterate devices to find defaults
+    for (const RtAudioDevice& d : mDevices) {
+#if RTAUDIO_VERSION_MAJOR < 6
+        // probed was removed from DeviceInfo in 6.0
+        if (d.probed == false)
+            continue;
+#endif
+        if (d.isDefaultInput && isInput) {
+            return d.ID;
+        } else if (d.isDefaultOutput && !isInput) {
+            return d.ID;
         }
     }
+
     // return the first device if default was not found
     // this is consistent with RtAudio API
     return 0;
 }
 
-//*******************************************************************************
-void RtAudioInterface::printDeviceInfo(std::string api, unsigned int deviceIndex)
-{
-    RtAudio rtaudio(RtAudio::getCompiledApiByName(api));
-    RtAudio::DeviceInfo info              = rtaudio.getDeviceInfo(deviceIndex);
-    std::vector<unsigned int> sampleRates = info.sampleRates;
-
-    cout << "Audio Device  [" << RtAudio::getApiDisplayName(rtaudio.getCurrentApi())
-         << " - " << deviceIndex << "] : " << info.name << endl;
-    cout << "  Output Channels : " << info.outputChannels << endl;
-    cout << "  Input Channels  : " << info.inputChannels << endl;
-    cout << "  Supported Sampling Rates: ";
-    for (unsigned int i = 0; i < sampleRates.size(); i++) {
-        cout << sampleRates[i] << " ";
-    }
-    cout << endl;
-    if (info.isDefaultOutput) {
-        cout << "  --Default Output Device--" << endl;
-    }
-    if (info.isDefaultInput) {
-        cout << "  --Default Input Device--" << endl;
-    }
-    if (info.probed) {
-        cout << "  --Probed Successful--" << endl;
-    }
-}
-
 //*******************************************************************************
 int RtAudioInterface::RtAudioCallback(void* outputBuffer, void* inputBuffer,
                                       unsigned int nFrames, double /*streamTime*/,
@@ -415,7 +449,7 @@ int RtAudioInterface::RtAudioCallback(void* outputBuffer, void* inputBuffer,
     inputBuffer_sample  = (sample_t*)inputBuffer;
     outputBuffer_sample = (sample_t*)outputBuffer;
 
-    int chansIn = getNumInputChannels();
+    int in_chans_num = getNumInputChannels();
     if (inputBuffer_sample != NULL && outputBuffer_sample != NULL) {
         // Get input and output buffers
         //-------------------------------------------------------------------
@@ -428,9 +462,9 @@ int RtAudioInterface::RtAudioCallback(void* outputBuffer, void* inputBuffer,
             // Output Ports are WRITABLE
             mOutBuffer[i] = outputBuffer_sample + (nFrames * i);
         }
-        if (chansIn == 2 && mInBuffer.size() == chansIn
+        if (in_chans_num == 2 && mInBuffer.size() == in_chans_num
             && mInputMixMode == AudioInterface::MIXTOMONO) {
-            mStereoToMonoMixer->compute(nFrames, mInBuffer.data(), mInBuffer.data());
+            mStereoToMonoMixerPtr->compute(nFrames, mInBuffer.data(), mInBuffer.data());
         }
         AudioInterface::callback(mInBuffer, mOutBuffer, nFrames);
     }
@@ -449,68 +483,135 @@ int RtAudioInterface::wrapperRtAudioCallback(void* outputBuffer, void* inputBuff
 }
 
 //*******************************************************************************
-void RtAudioInterface::RtAudioErrorCallback(RtAudioError::Type type,
-                                            const std::string& errorText)
+void RtAudioInterface::errorCallback(RtAudioErrorType errorType,
+                                     const std::string& errorText, void* arg)
 {
-    if ((type != RtAudioError::WARNING) && (type != RtAudioError::DEBUG_WARNING)) {
-        std::cout << errorText << '\n' << std::endl;
-        throw std::runtime_error(errorText);
-    } else if (type == RtAudioError::WARNING) {
-        std::cout << errorText << '\n' << std::endl;
-    } else if (type == RtAudioError::DEBUG_WARNING) {
-        std::cout << errorText << '\n' << std::endl;
+#if RTAUDIO_VERSION_MAJOR < 6
+    if (errorType == RtAudioError::WARNING || errorType == RtAudioError::DEBUG_WARNING)
+        return;
+#else
+    if (errorType == RTAUDIO_WARNING)
+        return;
+#endif
+    std::string errorMsg = "RtAudio Error";
+    if (!errorText.empty()) {
+        errorMsg += ": ";
+        errorMsg += errorText;
+    }
+    if (arg != nullptr) {
+        static_cast<RtAudioInterface*>(arg)->mErrorMsg = errorMsg;
     }
+    std::cerr << errorMsg << std::endl;
+    JackTrip::sAudioStopped = true;
 }
 
 //*******************************************************************************
 int RtAudioInterface::startProcess()
 {
+    if (mRtAudio.isNull())
+        return 0;
+
+    std::string errorText;
+
+#if RTAUDIO_VERSION_MAJOR < 6
     try {
-        if (mRtAudio != NULL) {
-            mRtAudio->startStream();
-        }
+        mRtAudio->startStream();
     } catch (RtAudioError& e) {
-        std::cout << e.getMessage() << '\n' << std::endl;
+        errorText = e.getMessage();
+    }
+#else
+    if (RTAUDIO_NO_ERROR != mRtAudio->startStream()) {
+        errorText = mRtAudio->getErrorText();
+    }
+#endif
+
+    if (!errorText.empty()) {
+        std::cerr << "RtAudioInterface failed to start stream: " << errorText
+                  << std::endl;
+        mRtAudio.reset();
         return (-1);
     }
+
     return (0);
 }
 
 //*******************************************************************************
 int RtAudioInterface::stopProcess()
 {
+    if (mRtAudio.isNull())
+        return 0;
+
+    std::string errorText;
+
+#if RTAUDIO_VERSION_MAJOR < 6
     try {
-        if (mRtAudio != NULL) {
-            mRtAudio->closeStream();
-            AudioInterface::setDevicesWarningMsg(AudioInterface::DEVICE_WARN_NONE);
-            AudioInterface::setDevicesErrorMsg(AudioInterface::DEVICE_ERR_NONE);
-        }
+        mRtAudio->closeStream();
+        // this causes it to crash for some reason
+        // mRtAudio->abortStream();
     } catch (RtAudioError& e) {
-        std::cout << e.getMessage() << '\n' << std::endl;
+        errorText = e.getMessage();
+    }
+#else
+    if (RTAUDIO_NO_ERROR != mRtAudio->abortStream()) {
+        errorText = mRtAudio->getErrorText();
+    } else {
+        mRtAudio->closeStream();
+    }
+#endif
+
+    mRtAudio.reset();
+
+    if (!errorText.empty()) {
+        std::cerr << errorText << '\n' << std::endl;
         return (-1);
     }
+
+    AudioInterface::setDevicesWarningMsg(AudioInterface::DEVICE_WARN_NONE);
+    AudioInterface::setDevicesErrorMsg(AudioInterface::DEVICE_ERR_NONE);
+
     return 0;
 }
 
 //*******************************************************************************
-void RtAudioInterface::getDeviceInfoFromName(std::string deviceName, int* index,
-                                             std::string* api, bool isInput) const
+bool RtAudioInterface::getDeviceInfoFromName(const std::string& deviceName,
+                                             RtAudioDevice& device, bool isInput) const
 {
-    const QVector<RtAudioDevice>& devices(getDevices());
-    for (int n = 0; n < devices.size(); ++n) {
-        if (deviceName == devices[n].name.toStdString()) {
-            if ((isInput && devices[n].inputChannels > 0)
-                || (!isInput && devices[n].outputChannels > 0)) {
-                *index = devices[n].apiIndex;
-                *api   = RtAudio::getApiName(devices[n].api);
-                return;
+    // convert names to QString to gracefully handle invalid
+    // utf8 character sequences, such as "RØDE Microphone"
+    const QString utf8Name(QString::fromStdString(deviceName));
+    for (const RtAudioDevice& d : mDevices) {
+        if (utf8Name == QString::fromStdString(d.name)) {
+            if ((isInput && d.inputChannels > 0) || (!isInput && d.outputChannels > 0)) {
+                device = d;
+                return true;
             }
         }
     }
+    return false;
+}
 
-    *index = -1;
-    *api   = "";
-    return;
+//*******************************************************************************
+bool RtAudioInterface::getDeviceInfoFromId(const long deviceId, RtAudioDevice& device,
+                                           [[maybe_unused]] bool isInput) const
+{
+#if RTAUDIO_VERSION_MAJOR < 6
+    if (mDevices.size() > deviceId) {
+        device = mDevices[deviceId];
+        return true;
+    }
+#else
+    if (deviceId < 0)
+        return false;
+    for (const RtAudioDevice& d : mDevices) {
+        if (deviceId == d.ID) {
+            if ((isInput && d.inputChannels > 0) || (!isInput && d.outputChannels > 0)) {
+                device = d;
+                return true;
+            }
+        }
+    }
+#endif
+    return false;
 }
 
 //*******************************************************************************
@@ -529,17 +630,20 @@ void RtAudioInterface::scanDevices(QVector<RtAudioDevice>& devices)
         }
 #endif
         RtAudio rtaudio(apis.at(i));
-        unsigned int numDevices = rtaudio.getDeviceCount();
-        for (unsigned int j = 0; j < numDevices; j++) {
-            RtAudio::DeviceInfo info = rtaudio.getDeviceInfo(j);
-            if (!info.probed || (info.inputChannels == 0 && info.outputChannels == 0))
-                continue;
+        std::vector<unsigned int> ids;
+        getDeviceIds(rtaudio, ids);
+        for (unsigned int deviceId : ids) {
             RtAudioDevice device;
-            device.api            = rtaudio.getCurrentApi();
-            device.apiIndex       = j;
-            device.name           = QString::fromStdString(info.name);
-            device.inputChannels  = info.inputChannels;
-            device.outputChannels = info.outputChannels;
+            device     = rtaudio.getDeviceInfo(deviceId);
+            device.api = rtaudio.getCurrentApi();
+#if RTAUDIO_VERSION_MAJOR < 6
+            device.ID = deviceId;
+            // probed was removed from DeviceInfo in 6.0
+            if (device.probed == false)
+                continue;
+#endif
+            if (device.inputChannels == 0 && device.outputChannels == 0)
+                continue;
             devices.push_back(device);
             device.print();
         }
index e32a4361647a47c9bd450fd97a6035320a9d0f3c..c4822f46d120be57419f90ce4d97901d78c0bb28 100644 (file)
 
 #include <RtAudio.h>
 
+#if RTAUDIO_VERSION_MAJOR < 6
+#define RtAudioErrorType RtAudioError::Type
+#endif
+
 #include <QQueue>
+#include <QSharedPointer>
 #include <QString>
 #include <QVector>
 
 class JackTrip;  // Forward declaration
 
 /// \brief Simple Class that represents an audio interface available via RtAudio
-struct RtAudioDevice {
+class RtAudioDevice : public RtAudio::DeviceInfo
+{
+   public:
+#if RTAUDIO_VERSION_MAJOR < 6
+    unsigned int ID;
+#endif
     RtAudio::Api api;
-    QString name;
-    int apiIndex;
-    int inputChannels;
-    int outputChannels;
     void print() const;
+    void printVerbose() const;
+    RtAudioDevice& operator=(const RtAudio::DeviceInfo& info);
 };
 
 /// \brief Base Class that provides an interface with RtAudio
@@ -91,14 +99,20 @@ class RtAudioInterface : public AudioInterface
     // returns number of available output audio devices
     unsigned int getNumOutputDevices() const;
 
+    // populates the ids vector with ids for all known devices
+    // for RtAudio v5 these are just incrementing numbers starting at 0,
+    // while RtAudio v6 uses unique ids for each device that may not correspond with
+    // the index location within the vector
+    static void getDeviceIds(RtAudio& rtaudio, std::vector<unsigned int>& ids);
+
     // populates devices with all available audio interfaces
     static void scanDevices(QVector<RtAudioDevice>& devices);
 
     // sets devices to available audio interfaces
-    void setDevices(QVector<RtAudioDevice>& devices) { mDevices = devices; }
+    void setRtAudioDevices(QVector<RtAudioDevice>& devices) { mDevices = devices; }
 
     // returns all available audio devices
-    inline const QVector<RtAudioDevice>& getDevices() const { return mDevices; }
+    inline void getRtAudioDevices(QVector<RtAudioDevice>& d) const { d = mDevices; }
 
     //--------------SETTERS---------------------------------------------
     /// \brief This has no effect in RtAudio
@@ -107,32 +121,36 @@ class RtAudioInterface : public AudioInterface
 
     //--------------GETTERS---------------------------------------------
     //------------------------------------------------------------------
-
    private:
     int RtAudioCallback(void* outputBuffer, void* inputBuffer, unsigned int nFrames,
                         double streamTime, RtAudioStreamStatus status);
     static int wrapperRtAudioCallback(void* outputBuffer, void* inputBuffer,
                                       unsigned int nFrames, double streamTime,
                                       RtAudioStreamStatus status, void* userData);
-    static void RtAudioErrorCallback(RtAudioError::Type type,
-                                     const std::string& errorText);
-    void printDeviceInfo(std::string api, unsigned int deviceId);
+    static void errorCallback(RtAudioErrorType type, const std::string& errorText,
+                              void* arg = nullptr);
 
     // retrieves info about an audio device by search for its name
-    void getDeviceInfoFromName(std::string deviceName, int* index, std::string* api,
+    // updates device and returns true if found
+    bool getDeviceInfoFromName(const std::string& deviceName, RtAudioDevice& device,
                                bool isInput) const;
 
+    // retrieves info about an audio device by search for its id
+    // updates device and returns true if found
+    bool getDeviceInfoFromId(const long deviceId, RtAudioDevice& device,
+                             bool isInput) const;
+    long getDefaultDevice(RtAudio& rtaudio, bool isInput);
+    long getDefaultDeviceForLinuxPulseAudio(bool isInput);
+
     QVarLengthArray<float*>
         mInBuffer;  ///< Vector of Input buffers/channel read from JACK
     QVarLengthArray<float*>
         mOutBuffer;  ///< Vector of Output buffer/channel to write to JACK
     QVector<RtAudioDevice>
-        mDevices;       ///< Vector of audio interfaces available via RTAudio
-    RtAudio* mRtAudio;  ///< RtAudio class if the input and output device are the same
-
-    unsigned int getDefaultDeviceForLinuxPulseAudio(bool isInput);
-
-    StereoToMono* mStereoToMonoMixer = NULL;
+        mDevices;  ///< Vector of audio interfaces available via RTAudio
+    QSharedPointer<RtAudio>
+        mRtAudio;  ///< RtAudio class if the input and output device are the same
+    QScopedPointer<StereoToMono> mStereoToMonoMixerPtr;
 };
 
 #endif  // __RTAUDIOINTERFACE_H__
index 7327a5ad3aefc0abea448377ba030fbf9488c048..fa99c333ec866a7c737e479970225eb09af068f2 100644 (file)
@@ -69,7 +69,7 @@ void StereoToMono::init(int samplingRate, int bufferSize)
 //*******************************************************************************
 void StereoToMono::compute(int nframes, float** inputs, float** outputs)
 {
-    if (not inited) {
+    if (!inited) {
         std::cerr << "*** Stereo-to-Mono " << this
                   << ": init never called! Doing it now.\n";
         init(0, 0);
index 778467abfe5e1a3a823eb01798735064bb516fac..b003ebeff1f5554b6d64e2c8814bc843453a96bf 100644 (file)
@@ -83,7 +83,7 @@ void Tone::init(int samplingRate, int bufferSize)
 //*******************************************************************************
 void Tone::compute(int nframes, float** inputs, float** outputs)
 {
-    if (not inited) {
+    if (!inited) {
         std::cerr << "*** Tone " << this << ": init never called! Doing it now.\n";
         init(0, 0);
     }
index df281d71bc69d7b94b3666d886fb5e00e5b5c634..fecee8e08dec245c64a7468479f367241b4c5258 100644 (file)
@@ -338,7 +338,7 @@ void UdpDataProtocol::processControlPacket(const char* buf)
 //*******************************************************************************
 int UdpDataProtocol::receivePacket(char* buf, const size_t n)
 {
-    int n_bytes = ::recv(mSocket, buf, n, 0);
+    int n_bytes = ::recv(mSocket, buf, int(n), 0);
     if (n_bytes == mControlPacketSize) {
         processControlPacket(buf);
         return 0;
@@ -366,11 +366,11 @@ functions. DWORD n_bytes; WSABUF buffer; int error; buffer.len = n; buffer.buf =
 #else*/
     int n_bytes;
     if (mIPv6) {
-        n_bytes = ::sendto(mSocket, buf, n, 0, (struct sockaddr*)&mPeerAddr6,
+        n_bytes = ::sendto(mSocket, buf, int(n), 0, (struct sockaddr*)&mPeerAddr6,
                            sizeof(mPeerAddr6));
     } else {
-        n_bytes =
-            ::sendto(mSocket, buf, n, 0, (struct sockaddr*)&mPeerAddr, sizeof(mPeerAddr));
+        n_bytes = ::sendto(mSocket, buf, int(n), 0, (struct sockaddr*)&mPeerAddr,
+                           sizeof(mPeerAddr));
     }
     return n_bytes;
     //#endif
@@ -749,7 +749,7 @@ void UdpDataProtocol::run()
         full_redundant_packet = new int8_t[full_redundant_packet_size];
         std::memset(full_redundant_packet, 0,
                     full_redundant_packet_size); // Initialize to 0
-        while (!mStopped && !JackTrip::sSigInt && !JackTrip::sJackStopped) {
+        while (!mStopped && !JackTrip::sSigInt && !JackTrip::sAudioStopped) {
             sendPacketRedundancy(full_redundant_packet, full_redundant_packet_size,
                                  full_packet_size);
         }
@@ -941,7 +941,7 @@ void UdpDataProtocol::sendPacketRedundancy(int8_t* full_redundant_packet,
     int8_t* src = mAudioPacket;
     if (1 < mChans) {
         // Convert internal interleaved layout to non-interleaved
-        int N       = getAudioPacketSizeInBites() / mChans / mSmplSize;
+        int N       = int(getAudioPacketSizeInBites() / mChans / mSmplSize);
         int8_t* dst = mBuffer.data();
         for (int n = 0; n < N; ++n) {
             for (int c = 0; c < mChans; ++c) {
index 25eb3725c3f6c7448759a095e60d1c7172ccae43..f15f9aa5aa56a5c98b9202e23176fc17bdd9a18c 100644 (file)
@@ -88,7 +88,7 @@ void Volume::init(int samplingRate, int bufferSize)
 //*******************************************************************************
 void Volume::compute(int nframes, float** inputs, float** outputs)
 {
-    if (not inited) {
+    if (!inited) {
         std::cerr << "*** Volume " << this << ": init never called! Doing it now.\n";
         init(0, 0);
     }
diff --git a/src/gui/AppIcon.qml b/src/gui/AppIcon.qml
new file mode 100644 (file)
index 0000000..32eb8aa
--- /dev/null
@@ -0,0 +1,29 @@
+import QtQuick
+import QtQuick.Controls
+
+Item {
+    id: appIcon
+
+    property alias icon: btn.icon
+    property string color: ""
+    property string defaultColor: virtualstudio.darkMode ? "#CCCCCC" : "#333333"
+    signal clicked()
+
+    Button {
+        id: btn
+        anchors.fill: parent
+        anchors.centerIn: parent
+        topInset: 0
+        leftInset: 0
+        rightInset: 0
+        bottomInset: 0
+        padding: 0
+
+        background: Rectangle { color: "transparent" }
+        icon.color: color ? color : defaultColor
+        icon.width: parent.width
+        icon.height: parent.height
+        display: AbstractButton.IconOnly
+        onClicked: appIcon.clicked()
+    }
+}
index 6a4558f8474cbf0b4e2b597220e70519b99b26c6..3416b87d66d3ee472f53367b2f891db5c64a12b3 100644 (file)
@@ -35,6 +35,9 @@
  * \date December 2022
  */
 
+#ifndef AUDIOINTERFACEMODE_H
+#define AUDIOINTERFACEMODE_H
+
 enum class AudioInterfaceMode {
     JACK,     ///< Jack Mode
     RTAUDIO,  ///< RtAudio Mode
@@ -80,4 +83,6 @@ constexpr auto isBackendAvailable()
     } else {
         return false;
     }
-}
\ No newline at end of file
+}
+
+#endif  // AUDIOINTERFACEMODE_H
index 6686a1b06ff802d3525253f7c15474abb819b3c3..ebf77753943ef964fe3d444defa51da255d1a04c 100644 (file)
@@ -1,6 +1,5 @@
-import QtQuick 2.12
-import QtQuick.Controls 2.12
-import QtGraphicalEffects 1.12
+import QtQuick
+import QtQuick.Controls
 
 Rectangle {
     width: parent.width
@@ -20,7 +19,6 @@ Rectangle {
     property int buttonHeight: 25
 
     property string backgroundColour: virtualstudio.darkMode ? "#272525" : "#FAFBFB"
-    property real imageLightnessValue: virtualstudio.darkMode ? 0.8 : 0.2
     property string textColour: virtualstudio.darkMode ? "#FAFBFB" : "#0F0D0D"
     property string buttonColour: virtualstudio.darkMode ? "#494646" : "#EAECEC"
     property string buttonHoverColour: virtualstudio.darkMode ? "#5B5858" : "#D3D4D4"
@@ -28,11 +26,6 @@ Rectangle {
     property string buttonStroke: virtualstudio.darkMode ? "#80827D7D" : "#40979797"
     property string buttonHoverStroke: virtualstudio.darkMode ? "#7B7777" : "#BABCBC"
     property string buttonPressedStroke: virtualstudio.darkMode ? "#827D7D" : "#BABCBC"
-    property string sliderColour: virtualstudio.darkMode ? "#BABCBC" :  "#EAECEC"
-    property string sliderPressedColour: virtualstudio.darkMode ? "#ACAFAF" : "#DEE0E0"
-    property string sliderTrackColour: virtualstudio.darkMode ? "#5B5858" : "light gray"
-    property string sliderActiveTrackColour: virtualstudio.darkMode ? "light gray" : "black"
-    property string warningTextColour: "#DB0A0A"
     property string linkText: virtualstudio.darkMode ? "#8B8D8D" : "#272525"
     property string toolTipBackgroundColour: virtualstudio.darkMode ? "#323232" : "#F3F3F3"
     property string toolTipTextColour: textColour
@@ -40,37 +33,59 @@ Rectangle {
     property string errorFlagColour: "#DB0A0A"
     property string disabledButtonTextColour: virtualstudio.darkMode ? "#827D7D" : "#BABCBC"
 
-    property bool isUsingJack: virtualstudio.audioBackend == "JACK"
-    property bool isUsingRtAudio: virtualstudio.audioBackend == "RtAudio"
-    property bool hasNoBackend: !isUsingJack && !isUsingRtAudio && !virtualstudio.backendAvailable;
-
     function getCurrentInputDeviceIndex () {
-        if (virtualstudio.inputDevice === "") {
-            return virtualstudio.inputComboModel.findIndex(elem => elem.type === "element");
+        if (audio.inputDevice === "") {
+            return audio.inputComboModel.findIndex(elem => elem.type === "element");
         }
 
-        let idx = virtualstudio.inputComboModel.findIndex(elem => elem.type === "element" && elem.text === virtualstudio.inputDevice);
+        let idx = audio.inputComboModel.findIndex(elem => elem.type === "element" && elem.text === audio.inputDevice);
         if (idx < 0) {
-            idx = virtualstudio.inputComboModel.findIndex(elem => elem.type === "element");
+            idx = audio.inputComboModel.findIndex(elem => elem.type === "element");
         }
         return idx;
     }
 
     function getCurrentOutputDeviceIndex() {
-        if (virtualstudio.outputDevice === "") {
-            return virtualstudio.outputComboModel.findIndex(elem => elem.type === "element");
+        if (audio.outputDevice === "") {
+            return audio.outputComboModel.findIndex(elem => elem.type === "element");
+        }
+
+        let idx = audio.outputComboModel.findIndex(elem => elem.type === "element" && elem.text === audio.outputDevice);
+        if (idx < 0) {
+            idx = audio.outputComboModel.findIndex(elem => elem.type === "element");
+        }
+        return idx;
+    }
+
+    function getCurrentInputChannelsIndex() {
+        let idx = audio.inputChannelsComboModel.findIndex(elem => elem.baseChannel === audio.baseInputChannel
+            && elem.numChannels === audio.numInputChannels);
+        if (idx < 0) {
+            idx = 0;
+        }
+        return idx;
+    }
+
+    function getCurrentOutputChannelsIndex() {
+        let idx = audio.outputChannelsComboModel.findIndex(elem => elem.baseChannel === audio.baseOutputChannel
+            && elem.numChannels === audio.numOutputChannels);
+        if (idx < 0) {
+            idx = 0;
         }
+        return idx;
+    }
 
-        let idx = virtualstudio.outputComboModel.findIndex(elem => elem.type === "element" && elem.text === virtualstudio.outputDevice);
+    function getCurrentMixModeIndex() {
+        let idx = audio.inputMixModeComboModel.findIndex(elem => elem.value === audio.inputMixMode);
         if (idx < 0) {
-            idx = virtualstudio.outputComboModel.findIndex(elem => elem.type === "element");
+            idx = 0;
         }
         return idx;
     }
 
     Loader {
         anchors.fill: parent
-        sourceComponent: isUsingRtAudio ? usingRtAudio : (isUsingJack ? usingJACK : noBackend)
+        sourceComponent: audio.audioBackend == "JACK" ? usingJACK : ((!audio.deviceModelsInitialized || audio.scanningDevices) ? scanningDevices : usingRtAudio);
     }
 
     Component {
@@ -100,75 +115,22 @@ Rectangle {
                 color: textColour
             }
 
-            Image {
+            InfoTooltip {
                 id: outputHelpIcon
                 anchors.left: outputLabel.right
                 anchors.bottom: outputLabel.top
                 anchors.bottomMargin: -8 * virtualstudio.uiScale
-                source: "help.svg"
-                sourceSize: Qt.size(12 * virtualstudio.uiScale, 12 * virtualstudio.uiScale)
-                fillMode: Image.PreserveAspectFit
-                smooth: true
-
-                property bool showToolTip: false
-
-                MouseArea {
-                    id: outputMouseArea
-                    anchors.fill: parent
-                    hoverEnabled: true
-                    onEntered: outputHelpIcon.showToolTip = true
-                    onExited: outputHelpIcon.showToolTip = false
-                }
-
-                ToolTip {
-                    visible: outputHelpIcon.showToolTip
-                    contentItem: Rectangle {
-                        color: toolTipBackgroundColour
-                        radius: 3
-                        anchors.fill: parent
-                        anchors.bottomMargin: bottomToolTipMargin * virtualstudio.uiScale
-                        anchors.rightMargin: rightToolTipMargin * virtualstudio.uiScale
-                        layer.enabled: true
-                        border.width: 1
-                        border.color: buttonStroke
-
-                        Text {
-                            anchors.centerIn: parent
-                            font { family: "Poppins"; pixelSize: fontExtraSmall * virtualstudio.fontScale * virtualstudio.uiScale}
-                            text: qsTr("How you'll hear the studio audio")
-                            color: toolTipTextColour
-                        }
-                    }
-                    background: Rectangle {
-                        color: "transparent"
-                    }
-                }
-            }
-
-            Colorize {
-                anchors.fill: outputHelpIcon
-                source: outputHelpIcon
-                hue: 0
-                saturation: 0
-                lightness: imageLightnessValue
+                size: 16 * virtualstudio.uiScale
+                content: qsTr("How you'll hear the studio audio")
             }
 
-            Image {
+            AppIcon {
                 id: headphonesIcon
                 anchors.left: outputLabel.left
                 anchors.verticalCenter: outputDeviceMeters.verticalCenter
-                source: "headphones.svg"
-                sourceSize: Qt.size(28 * virtualstudio.uiScale, 28 * virtualstudio.uiScale)
-                fillMode: Image.PreserveAspectFit
-                smooth: true
-            }
-
-            Colorize {
-                anchors.fill: headphonesIcon
-                source: headphonesIcon
-                hue: 0
-                saturation: 0
-                lightness: imageLightnessValue
+                width: 28 * virtualstudio.uiScale
+                height: 28 * virtualstudio.uiScale
+                icon.source: "headphones.svg"
             }
 
             ComboBox {
@@ -177,7 +139,7 @@ Rectangle {
                 anchors.verticalCenter: outputLabel.verticalCenter
                 anchors.rightMargin: rightMargin * virtualstudio.uiScale
                 width: parent.width - leftSpacer.width - rightMargin * virtualstudio.uiScale
-                model: virtualstudio.outputComboModel
+                model: audio.outputComboModel
                 currentIndex: getCurrentOutputDeviceIndex()
                 delegate: ItemDelegate {
                     required property var modelData
@@ -198,16 +160,16 @@ Rectangle {
                             if (modelData.type == "element") {
                                 outputCombo.currentIndex = index
                                 outputCombo.popup.close()
-                                virtualstudio.outputDevice = modelData.text
+                                audio.outputDevice = modelData.text
                                 if (modelData.category === "Low-Latency (ASIO)") {
                                     let inputComboIdx = inputCombo.model.findIndex(it => it.category === "Low-Latency (ASIO)" && it.text === modelData.text);
                                     if (inputComboIdx !== null && inputComboIdx !== undefined) {
                                         inputCombo.currentIndex = inputComboIdx;
-                                        virtualstudio.inputDevice = modelData.text
+                                        audio.inputDevice = modelData.text
                                     }
                                 }
-                                virtualstudio.restartAudio()
-                                virtualstudio.validateDevicesState()
+                                audio.validateDevices()
+                                audio.restartAudio()
                             }
                         }
                     }
@@ -229,89 +191,23 @@ Rectangle {
                 anchors.top: outputCombo.bottom
                 anchors.topMargin: 16 * virtualstudio.uiScale
                 height: 24 * virtualstudio.uiScale
-                model: virtualstudio.outputMeterLevels
-                clipped: virtualstudio.outputClipped
-                enabled: virtualstudio.audioReady && !Boolean(virtualstudio.devicesError)
+                model: audio.outputMeterLevels
+                clipped: audio.outputClipped
+                enabled: audio.audioReady && !Boolean(audio.devicesError)
             }
 
-            Slider {
+            VolumeSlider {
                 id: outputSlider
-                from: 0.0
-                value: audioInterface ? audioInterface.outputVolume : 0.5
-                onMoved: { audioInterface.outputVolume = value }
-                to: 1.0
-                padding: 0
-                anchors.left: outputQuieterIcon.right
-                anchors.leftMargin: 8 * virtualstudio.uiScale
-                anchors.right: outputLouderIcon.left
-                anchors.rightMargin: 8 * virtualstudio.uiScale
-                anchors.top: outputDeviceMeters.bottom
-                anchors.topMargin: 16 * virtualstudio.uiScale
-
-                background: Rectangle {
-                    x: outputSlider.leftPadding
-                    y: outputSlider.topPadding + outputSlider.availableHeight / 2 - height / 2
-                    implicitWidth: parent.width
-                    implicitHeight: 6
-                    width: outputSlider.availableWidth
-                    height: implicitHeight
-                    radius: 4
-                    color: sliderTrackColour
-
-                    Rectangle {
-                        width: outputSlider.visualPosition * parent.width
-                        height: parent.height
-                        color: sliderActiveTrackColour
-                        radius: 4
-                    }
-                }
-
-                handle: Rectangle {
-                    x: outputSlider.leftPadding + outputSlider.visualPosition * (outputSlider.availableWidth - width)
-                    y: outputSlider.topPadding + outputSlider.availableHeight / 2 - height / 2
-                    implicitWidth: 26 * virtualstudio.uiScale
-                    implicitHeight: 26 * virtualstudio.uiScale
-                    radius: 13 * virtualstudio.uiScale
-                    color: outputSlider.pressed ? sliderPressedColour : sliderColour
-                    border.color: buttonStroke
-                }
-            }
-
-            Image {
-                id: outputQuieterIcon
                 anchors.left: outputCombo.left
-                anchors.verticalCenter: outputSlider.verticalCenter
-                source: "quiet.svg"
-                sourceSize: Qt.size(16 * virtualstudio.uiScale, 16 * virtualstudio.uiScale)
-                fillMode: Image.PreserveAspectFit
-                smooth: true
-            }
-
-            Colorize {
-                anchors.fill: outputQuieterIcon
-                source: outputQuieterIcon
-                hue: 0
-                saturation: 0
-                lightness: imageLightnessValue
-            }
-
-            Image {
-                id: outputLouderIcon
                 anchors.right: parent.right
                 anchors.rightMargin: rightMargin * virtualstudio.uiScale
-                anchors.verticalCenter: outputSlider.verticalCenter
-                source: "loud.svg"
-                sourceSize: Qt.size(16 * virtualstudio.uiScale, 16 * virtualstudio.uiScale)
-                fillMode: Image.PreserveAspectFit
-                smooth: true
-            }
-
-            Colorize {
-                anchors.fill: outputLouderIcon
-                source: outputLouderIcon
-                hue: 0
-                saturation: 0
-                lightness: imageLightnessValue
+                anchors.top: outputDeviceMeters.bottom
+                anchors.topMargin: 16 * virtualstudio.uiScale
+                height: 30 * virtualstudio.uiScale
+                labelText: "Studio"
+                tooltipText: "How loudly you hear other participants"
+                showLabel: false
+                sliderEnabled: true
             }
 
             Text {
@@ -333,15 +229,9 @@ Rectangle {
                 anchors.rightMargin: 8 * virtualstudio.uiScale
                 anchors.top: outputChannelsLabel.bottom
                 anchors.topMargin: 4 * virtualstudio.uiScale
-                model: virtualstudio.outputChannelsComboModel
-                currentIndex: (() => {
-                    let idx = virtualstudio.outputChannelsComboModel.findIndex(elem => elem.baseChannel === virtualstudio.baseOutputChannel
-                        && elem.numChannels === virtualstudio.numOutputChannels);
-                    if (idx < 0) {
-                        idx = 0;
-                    }
-                    return idx;
-                })()
+                model: audio.outputChannelsComboModel
+                enabled: audio.outputChannelsComboModel.length > 1
+                currentIndex: getCurrentOutputChannelsIndex()
                 delegate: ItemDelegate {
                     required property var modelData
                     required property int index
@@ -355,9 +245,10 @@ Rectangle {
                         onClicked: {
                             outputChannelsCombo.currentIndex = index
                             outputChannelsCombo.popup.close()
-                            virtualstudio.baseOutputChannel = modelData.baseChannel
-                            virtualstudio.numOutputChannels = modelData.numChannels
-                            virtualstudio.validateDevicesState()
+                            audio.baseOutputChannel = modelData.baseChannel
+                            audio.numOutputChannels = modelData.numChannels
+                            audio.validateDevices()
+                            audio.restartAudio()
                         }
                     }
                 }
@@ -379,7 +270,7 @@ Rectangle {
                     border.width: 1
                     border.color: testOutputAudioButton.down || testOutputAudioButton.hovered ? buttonPressedStroke : (testOutputAudioButton.hovered ? buttonHoverStroke : buttonStroke)
                 }
-                onClicked: { virtualstudio.playOutputAudio() }
+                onClicked: { audio.playOutputAudio() }
                 anchors.right: parent.right
                 anchors.rightMargin: rightMargin * virtualstudio.uiScale
                 anchors.verticalCenter: outputChannelsCombo.verticalCenter
@@ -410,80 +301,27 @@ Rectangle {
                 color: textColour
             }
 
-            Image {
+            InfoTooltip {
                 id: inputHelpIcon
                 anchors.left: inputLabel.right
                 anchors.bottom: inputLabel.top
                 anchors.bottomMargin: -8 * virtualstudio.uiScale
-                source: "help.svg"
-                sourceSize: Qt.size(12 * virtualstudio.uiScale, 12 * virtualstudio.uiScale)
-                fillMode: Image.PreserveAspectFit
-                smooth: true
-
-                property bool showToolTip: false
-
-                MouseArea {
-                    id: inputMouseArea
-                    anchors.fill: parent
-                    hoverEnabled: true
-                    onEntered: inputHelpIcon.showToolTip = true
-                    onExited: inputHelpIcon.showToolTip = false
-                }
-
-                ToolTip {
-                    visible: inputHelpIcon.showToolTip
-                    contentItem: Rectangle {
-                        color: toolTipBackgroundColour
-                        radius: 3
-                        anchors.fill: parent
-                        anchors.bottomMargin: bottomToolTipMargin * virtualstudio.uiScale
-                        anchors.rightMargin: rightToolTipMargin * virtualstudio.uiScale
-                        layer.enabled: true
-                        border.width: 1
-                        border.color: buttonStroke
-
-                        Text {
-                            anchors.centerIn: parent
-                            font { family: "Poppins"; pixelSize: fontExtraSmall * virtualstudio.fontScale * virtualstudio.uiScale}
-                            text: qsTr("Audio sent to the studio (microphone, instrument, mixer, etc.)")
-                            color: toolTipTextColour
-                        }
-                    }
-                    background: Rectangle {
-                        color: "transparent"
-                    }
-                }
+                size: 16 * virtualstudio.uiScale
+                content: qsTr("Audio sent to the studio (microphone, instrument, mixer, etc.)")
             }
 
-            Colorize {
-                anchors.fill: inputHelpIcon
-                source: inputHelpIcon
-                hue: 0
-                saturation: 0
-                lightness: imageLightnessValue
-            }
-
-            Image {
+            AppIcon {
                 id: microphoneIcon
                 anchors.left: outputLabel.left
                 anchors.verticalCenter: inputDeviceMeters.verticalCenter
-                source: "mic.svg"
-                sourceSize: Qt.size(32 * virtualstudio.uiScale, 32 * virtualstudio.uiScale)
-                fillMode: Image.PreserveAspectFit
-                smooth: true
-            }
-
-            Colorize {
-                anchors.fill: microphoneIcon
-                source: microphoneIcon
-                hue: 0
-                saturation: 0
-                lightness: imageLightnessValue
+                width: 32 * virtualstudio.uiScale
+                height: 32 * virtualstudio.uiScale
+                icon.source: "mic.svg"
             }
 
             ComboBox {
                 id: inputCombo
-                model: virtualstudio.inputComboModel
+                model: audio.inputComboModel
                 currentIndex: getCurrentInputDeviceIndex()
                 anchors.left: outputCombo.left
                 anchors.right: outputCombo.right
@@ -507,16 +345,16 @@ Rectangle {
                             if (modelData.type == "element") {
                                 inputCombo.currentIndex = index
                                 inputCombo.popup.close()
-                                virtualstudio.inputDevice = modelData.text
+                                audio.inputDevice = modelData.text
                                 if (modelData.category === "Low-Latency (ASIO)") {
                                     let outputComboIdx = outputCombo.model.findIndex(it => it.category === "Low-Latency (ASIO)" && it.text === modelData.text);
                                     if (outputComboIdx !== null && outputComboIdx !== undefined) {
                                         outputCombo.currentIndex = outputComboIdx;
-                                        virtualstudio.outputDevice = modelData.text
+                                        audio.outputDevice = modelData.text
                                     }
                                 }
-                                virtualstudio.restartAudio()
-                                virtualstudio.validateDevicesState()
+                                audio.validateDevices()
+                                audio.restartAudio()
                             }
                         }
                     }
@@ -538,89 +376,23 @@ Rectangle {
                 anchors.top: inputCombo.bottom
                 anchors.topMargin: 16 * virtualstudio.uiScale
                 height: 24 * virtualstudio.uiScale
-                model: virtualstudio.inputMeterLevels
-                clipped: virtualstudio.inputClipped
-                enabled: virtualstudio.audioReady && !Boolean(virtualstudio.devicesError)
+                model: audio.inputMeterLevels
+                clipped: audio.inputClipped
+                enabled: audio.audioReady && !Boolean(audio.devicesError)
             }
 
-            Slider {
+            VolumeSlider {
                 id: inputSlider
-                from: 0.0
-                value: audioInterface ? audioInterface.inputVolume : 0.5
-                onMoved: { audioInterface.inputVolume = value }
-                to: 1.0
-                padding: 0
-                anchors.left: inputQuieterIcon.right
-                anchors.leftMargin: 8 * virtualstudio.uiScale
-                anchors.right: inputLouderIcon.left
-                anchors.rightMargin: 8 * virtualstudio.uiScale
-                anchors.top: inputDeviceMeters.bottom
-                anchors.topMargin: 16 * virtualstudio.uiScale
-
-                background: Rectangle {
-                    x: inputSlider.leftPadding
-                    y: inputSlider.topPadding + inputSlider.availableHeight / 2 - height / 2
-                    implicitWidth: parent.width
-                    implicitHeight: 6
-                    width: inputSlider.availableWidth
-                    height: implicitHeight
-                    radius: 4
-                    color: sliderTrackColour
-
-                    Rectangle {
-                        width: inputSlider.visualPosition * parent.width
-                        height: parent.height
-                        color: sliderActiveTrackColour
-                        radius: 4
-                    }
-                }
-
-                handle: Rectangle {
-                    x: inputSlider.leftPadding + inputSlider.visualPosition * (inputSlider.availableWidth - width)
-                    y: inputSlider.topPadding + inputSlider.availableHeight / 2 - height / 2
-                    implicitWidth: 26 * virtualstudio.uiScale
-                    implicitHeight: 26 * virtualstudio.uiScale
-                    radius: 13 * virtualstudio.uiScale
-                    color: inputSlider.pressed ? sliderPressedColour : sliderColour
-                    border.color: buttonStroke
-                }
-            }
-
-            Image {
-                id: inputQuieterIcon
                 anchors.left: inputDeviceMeters.left
-                anchors.verticalCenter: inputSlider.verticalCenter
-                source: "quiet.svg"
-                sourceSize: Qt.size(16 * virtualstudio.uiScale, 16 * virtualstudio.uiScale)
-                fillMode: Image.PreserveAspectFit
-                smooth: true
-            }
-
-            Colorize {
-                anchors.fill: inputQuieterIcon
-                source: inputQuieterIcon
-                hue: 0
-                saturation: 0
-                lightness: imageLightnessValue
-            }
-
-            Image {
-                id: inputLouderIcon
                 anchors.right: parent.right
                 anchors.rightMargin: rightMargin * virtualstudio.uiScale
-                anchors.verticalCenter: inputSlider.verticalCenter
-                source: "loud.svg"
-                sourceSize: Qt.size(16 * virtualstudio.uiScale, 16 * virtualstudio.uiScale)
-                fillMode: Image.PreserveAspectFit
-                smooth: true
-            }
-
-            Colorize {
-                anchors.fill: inputLouderIcon
-                source: inputLouderIcon
-                hue: 0
-                saturation: 0
-                lightness: imageLightnessValue
+                anchors.top: inputDeviceMeters.bottom
+                anchors.topMargin: 16 * virtualstudio.uiScale
+                height: 30 * virtualstudio.uiScale
+                labelText: "Send"
+                tooltipText: "How loudly other participants hear you"
+                showLabel: false
+                sliderEnabled: true
             }
 
             Button {
@@ -651,15 +423,9 @@ Rectangle {
                 anchors.rightMargin: 8 * virtualstudio.uiScale
                 anchors.top: inputChannelsLabel.bottom
                 anchors.topMargin: 4 * virtualstudio.uiScale
-                model: virtualstudio.inputChannelsComboModel
-                currentIndex: (() => {
-                    let idx = virtualstudio.inputChannelsComboModel.findIndex(elem => elem.baseChannel === virtualstudio.baseInputChannel
-                        && elem.numChannels === virtualstudio.numInputChannels);
-                    if (idx < 0) {
-                        idx = 0;
-                    }
-                    return idx;
-                })()
+                model: audio.inputChannelsComboModel
+                enabled: audio.inputChannelsComboModel.length > 1
+                currentIndex: getCurrentInputChannelsIndex()
                 delegate: ItemDelegate {
                     required property var modelData
                     required property int index
@@ -673,9 +439,10 @@ Rectangle {
                         onClicked: {
                             inputChannelsCombo.currentIndex = index
                             inputChannelsCombo.popup.close()
-                            virtualstudio.baseInputChannel = modelData.baseChannel
-                            virtualstudio.numInputChannels = modelData.numChannels
-                            virtualstudio.validateDevicesState()
+                            audio.baseInputChannel = modelData.baseChannel
+                            audio.numInputChannels = modelData.numChannels
+                            audio.validateDevices()
+                            audio.restartAudio()
                         }
                     }
                 }
@@ -709,14 +476,9 @@ Rectangle {
                 anchors.rightMargin: 8 * virtualstudio.uiScale
                 anchors.top: inputMixModeLabel.bottom
                 anchors.topMargin: 4 * virtualstudio.uiScale
-                model: virtualstudio.inputMixModeComboModel
-                currentIndex: (() => {
-                    let idx = virtualstudio.inputMixModeComboModel.findIndex(elem => elem.value === virtualstudio.inputMixMode);
-                    if (idx < 0) {
-                        idx = 0;
-                    }
-                    return idx;
-                })()
+                model: audio.inputMixModeComboModel
+                enabled: audio.inputMixModeComboModel.length > 1
+                currentIndex: getCurrentMixModeIndex()
                 delegate: ItemDelegate {
                     required property var modelData
                     required property int index
@@ -730,8 +492,9 @@ Rectangle {
                         onClicked: {
                             inputMixModeCombo.currentIndex = index
                             inputMixModeCombo.popup.close()
-                            virtualstudio.inputMixMode = virtualstudio.inputMixModeComboModel[index].value
-                            virtualstudio.validateDevicesState()
+                            audio.inputMixMode = audio.inputMixModeComboModel[index].value
+                            audio.validateDevices();
+                            audio.restartAudio()
                         }
                     }
                 }
@@ -754,7 +517,7 @@ Rectangle {
                 anchors.topMargin: 8 * virtualstudio.uiScale
                 textFormat: Text.RichText
                 wrapMode: Text.WordWrap
-                text: "Choose up to 2 channels"
+                text: audio.inputChannelsComboModel.length > 1 ? "Choose up to 2 channels" : "Only 1 channel available"
                 font { family: "Poppins"; pixelSize: fontExtraSmall * virtualstudio.fontScale * virtualstudio.uiScale }
                 color: textColour
             }
@@ -769,11 +532,11 @@ Rectangle {
                 textFormat: Text.RichText
                 wrapMode: Text.WordWrap
                 text: (() => {
-                    if (virtualstudio.inputMixMode === 2) {
+                    if (audio.inputMixMode === 2) {
                         return "Treat the channels as Left and Right signals, coming through each speaker separately.";
-                    } else if (virtualstudio.inputMixMode === 3) {
+                    } else if (audio.inputMixMode === 3) {
                         return "Combine the channels into one central channel coming through both speakers.";
-                    } else if (virtualstudio.inputMixMode === 1) {
+                    } else if (audio.inputMixMode === 1) {
                         return "Send a single channel of audio";
                     } else {
                         return "";
@@ -783,28 +546,31 @@ Rectangle {
                 color: textColour
             }
 
-            Text {
-                id: warningOrErrorMessage
-                anchors.left: inputLabel.left
-                anchors.right: parent.right
-                anchors.rightMargin: 16 * virtualstudio.uiScale
-                anchors.top: inputMixModeHelpMessage.bottom
-                anchors.topMargin: 8 * virtualstudio.uiScale
-                anchors.bottomMargin: 8 * virtualstudio.uiScale
-                textFormat: Text.RichText
-                text: (virtualstudio.devicesError || virtualstudio.devicesWarning)
-                    + ((virtualstudio.devicesErrorHelpUrl || virtualstudio.devicesWarningHelpUrl)
-                        ? `&nbsp;<a style="color: ${linkText};" href=${virtualstudio.devicesErrorHelpUrl || virtualstudio.devicesWarningHelpUrl}>Learn More.</a>`
-                        : ""
-                    )
-                onLinkActivated: link => {
-                    virtualstudio.openLink(link)
+            Connections {
+                target: audio
+                // anything that sets currentIndex to the value of a function needs
+                // to be manually updated whenever there is a change to any vars it uses
+                function onInputDeviceChanged() {
+                    inputCombo.currentIndex = getCurrentInputDeviceIndex();
+                }
+                function onOutputDeviceChanged() {
+                    outputCombo.currentIndex = getCurrentOutputDeviceIndex();
+                }
+                function onNumInputChannelsChanged() {
+                    inputChannelsCombo.currentIndex = getCurrentInputChannelsIndex();
+                }
+                function onBaseInputChannelChanged() {
+                    inputChannelsCombo.currentIndex = getCurrentInputChannelsIndex();
+                }
+                function onNumOutputChannelsChanged() {
+                    outputChannelsCombo.currentIndex = getCurrentOutputChannelsIndex();
+                }
+                function onBaseOutputChannelChanged() {
+                    outputChannelsCombo.currentIndex = getCurrentOutputChannelsIndex();
+                }
+                function onInputMixModeChanged() {
+                    inputMixModeCombo.currentIndex = getCurrentMixModeIndex();
                 }
-                horizontalAlignment: Text.AlignHLeft
-                wrapMode: Text.WordWrap
-                color: warningTextColour
-                font { family: "Poppins"; pixelSize: fontExtraSmall * virtualstudio.fontScale * virtualstudio.uiScale }
-                visible: Boolean(virtualstudio.devicesError) || Boolean(virtualstudio.devicesWarning);
             }
         }
     }
@@ -842,22 +608,13 @@ Rectangle {
                 color: textColour
             }
 
-            Image {
+            AppIcon {
                 id: jackHeadphonesIcon
                 anchors.left: jackOutputLabel.left
                 anchors.verticalCenter: jackOutputVolumeSlider.verticalCenter
-                source: "headphones.svg"
-                sourceSize: Qt.size(28 * virtualstudio.uiScale, 28 * virtualstudio.uiScale)
-                fillMode: Image.PreserveAspectFit
-                smooth: true
-            }
-
-            Colorize {
-                anchors.fill: jackHeadphonesIcon
-                source: jackHeadphonesIcon
-                hue: 0
-                saturation: 0
-                lightness: imageLightnessValue
+                width: 28 * virtualstudio.uiScale
+                height: 28 * virtualstudio.uiScale
+                icon.source: "headphones.svg"
             }
 
             Meter {
@@ -867,9 +624,9 @@ Rectangle {
                 anchors.rightMargin: rightMargin * virtualstudio.uiScale
                 anchors.verticalCenter: jackOutputLabel.verticalCenter
                 height: 24 * virtualstudio.uiScale
-                model: virtualstudio.outputMeterLevels
-                clipped: virtualstudio.outputClipped
-                enabled: virtualstudio.audioReady && !Boolean(virtualstudio.devicesError)
+                model: audio.outputMeterLevels
+                clipped: audio.outputClipped
+                enabled: audio.audioReady && !Boolean(audio.devicesError)
             }
 
             Button {
@@ -880,7 +637,7 @@ Rectangle {
                     border.width: 1
                     border.color: jackTestOutputAudioButton.down ? buttonPressedStroke : (jackTestOutputAudioButton.hovered ? buttonHoverStroke : buttonStroke)
                 }
-                onClicked: { virtualstudio.playOutputAudio() }
+                onClicked: { audio.playOutputAudio() }
                 anchors.right: parent.right
                 anchors.rightMargin: rightMargin * virtualstudio.uiScale
                 anchors.verticalCenter: jackOutputVolumeSlider.verticalCenter
@@ -893,84 +650,18 @@ Rectangle {
                 }
             }
 
-            Slider {
+            VolumeSlider {
                 id: jackOutputVolumeSlider
-                from: 0.0
-                value: audioInterface ? audioInterface.outputVolume : 0.5
-                onMoved: { audioInterface.outputVolume = value }
-                to: 1.0
-                padding: 0
-                anchors.left: jackOutputQuieterButton.right
-                anchors.leftMargin: 8 * virtualstudio.uiScale
-                anchors.right: jackOutputLouderIcon.left
-                anchors.rightMargin: 8 * virtualstudio.uiScale
-                anchors.top: jackOutputMeters.bottom
-                anchors.topMargin: 16 * virtualstudio.uiScale
-
-                background: Rectangle {
-                    x: jackOutputVolumeSlider.leftPadding
-                    y: jackOutputVolumeSlider.topPadding + jackOutputVolumeSlider.availableHeight / 2 - height / 2
-                    implicitWidth: parent.width
-                    implicitHeight: 6
-                    width: jackOutputVolumeSlider.availableWidth
-                    height: implicitHeight
-                    radius: 4
-                    color: sliderTrackColour
-
-                    Rectangle {
-                        width: jackOutputVolumeSlider.visualPosition * parent.width
-                        height: parent.height
-                        color: sliderActiveTrackColour
-                        radius: 4
-                    }
-                }
-
-                handle: Rectangle {
-                    x: jackOutputVolumeSlider.leftPadding + jackOutputVolumeSlider.visualPosition * (jackOutputVolumeSlider.availableWidth - width)
-                    y: jackOutputVolumeSlider.topPadding + jackOutputVolumeSlider.availableHeight / 2 - height / 2
-                    implicitWidth: 26 * virtualstudio.uiScale
-                    implicitHeight: 26 * virtualstudio.uiScale
-                    radius: 13 * virtualstudio.uiScale
-                    color: jackOutputVolumeSlider.pressed ? sliderPressedColour : sliderColour
-                    border.color: buttonStroke
-                }
-            }
-
-            Image {
-                id: jackOutputQuieterButton
                 anchors.left: jackOutputMeters.left
-                anchors.verticalCenter: jackOutputVolumeSlider.verticalCenter
-                source: "quiet.svg"
-                sourceSize: Qt.size(16 * virtualstudio.uiScale, 16 * virtualstudio.uiScale)
-                fillMode: Image.PreserveAspectFit
-                smooth: true
-            }
-
-            Colorize {
-                anchors.fill: jackOutputQuieterButton
-                source: jackOutputQuieterButton
-                hue: 0
-                saturation: 0
-                lightness: imageLightnessValue
-            }
-
-            Image {
-                id: jackOutputLouderIcon
                 anchors.right: jackTestOutputAudioButton.left
                 anchors.rightMargin: rightMargin * virtualstudio.uiScale
-                anchors.verticalCenter: jackOutputVolumeSlider.verticalCenter
-                source: "loud.svg"
-                sourceSize: Qt.size(16 * virtualstudio.uiScale, 16 * virtualstudio.uiScale)
-                fillMode: Image.PreserveAspectFit
-                smooth: true
-            }
-
-            Colorize {
-                anchors.fill: jackOutputLouderIcon
-                source: jackOutputLouderIcon
-                hue: 0
-                saturation: 0
-                lightness: imageLightnessValue
+                anchors.top: jackOutputMeters.bottom
+                anchors.topMargin: 16 * virtualstudio.uiScale
+                height: 30 * virtualstudio.uiScale
+                labelText: "Studio"
+                tooltipText: "How loudly you hear other participants"
+                showLabel: false
+                sliderEnabled: true
             }
 
             Text {
@@ -985,22 +676,13 @@ Rectangle {
                 color: textColour
             }
 
-            Image {
+            AppIcon {
                 id: jackMicrophoneIcon
                 anchors.left: jackInputLabel.left
                 anchors.verticalCenter: jackInputVolumeSlider.verticalCenter
-                source: "mic.svg"
-                sourceSize: Qt.size(32 * virtualstudio.uiScale, 32 * virtualstudio.uiScale)
-                fillMode: Image.PreserveAspectFit
-                smooth: true
-            }
-
-            Colorize {
-                anchors.fill: jackMicrophoneIcon
-                source: jackMicrophoneIcon
-                hue: 0
-                saturation: 0
-                lightness: imageLightnessValue
+                width: 32 * virtualstudio.uiScale
+                height: 32 * virtualstudio.uiScale
+                icon.source: "mic.svg"
             }
 
             Meter {
@@ -1010,89 +692,23 @@ Rectangle {
                 anchors.rightMargin: rightMargin * virtualstudio.uiScale
                 anchors.verticalCenter: jackInputLabel.verticalCenter
                 height: 24 * virtualstudio.uiScale
-                model: virtualstudio.inputMeterLevels
-                clipped: virtualstudio.inputClipped
-                enabled: virtualstudio.audioReady && !Boolean(virtualstudio.devicesError)
+                model: audio.inputMeterLevels
+                clipped: audio.inputClipped
+                enabled: audio.audioReady && !Boolean(audio.devicesError)
             }
 
-            Slider {
+            VolumeSlider {
                 id: jackInputVolumeSlider
-                from: 0.0
-                value: audioInterface ? audioInterface.inputVolume : 0.5
-                onMoved: { audioInterface.inputVolume = value }
-                to: 1.0
-                padding: 0
-                anchors.left: jackInputQuieterButton.right
-                anchors.leftMargin: 8 * virtualstudio.uiScale
-                anchors.right: jackInputLouderIcon.left
-                anchors.rightMargin: 8 * virtualstudio.uiScale
-                anchors.top: jackInputMeters.bottom
-                anchors.topMargin: 16 * virtualstudio.uiScale
-
-                background: Rectangle {
-                    x: jackInputVolumeSlider.leftPadding
-                    y: jackInputVolumeSlider.topPadding + jackInputVolumeSlider.availableHeight / 2 - height / 2
-                    implicitWidth: parent.width
-                    implicitHeight: 6
-                    width: jackInputVolumeSlider.availableWidth
-                    height: implicitHeight
-                    radius: 4
-                    color: sliderTrackColour
-
-                    Rectangle {
-                        width: jackInputVolumeSlider.visualPosition * parent.width
-                        height: parent.height
-                        color: sliderActiveTrackColour
-                        radius: 4
-                    }
-                }
-
-                handle: Rectangle {
-                    x: jackInputVolumeSlider.leftPadding + jackInputVolumeSlider.visualPosition * (jackInputVolumeSlider.availableWidth - width)
-                    y: jackInputVolumeSlider.topPadding + jackInputVolumeSlider.availableHeight / 2 - height / 2
-                    implicitWidth: 26 * virtualstudio.uiScale
-                    implicitHeight: 26 * virtualstudio.uiScale
-                    radius: 13 * virtualstudio.uiScale
-                    color: jackInputVolumeSlider.pressed ? sliderPressedColour : sliderColour
-                    border.color: buttonStroke
-                }
-            }
-
-            Image {
-                id: jackInputQuieterButton
                 anchors.left: jackInputMeters.left
-                anchors.verticalCenter: jackInputVolumeSlider.verticalCenter
-                source: "quiet.svg"
-                sourceSize: Qt.size(16 * virtualstudio.uiScale, 16 * virtualstudio.uiScale)
-                fillMode: Image.PreserveAspectFit
-                smooth: true
-            }
-
-            Colorize {
-                anchors.fill: jackInputQuieterButton
-                source: jackInputQuieterButton
-                hue: 0
-                saturation: 0
-                lightness: imageLightnessValue
-            }
-
-            Image {
-                id: jackInputLouderIcon
                 anchors.right: parent.right
                 anchors.rightMargin: rightMargin * virtualstudio.uiScale
-                anchors.verticalCenter: jackInputVolumeSlider.verticalCenter
-                source: "loud.svg"
-                sourceSize: Qt.size(16 * virtualstudio.uiScale, 16 * virtualstudio.uiScale)
-                fillMode: Image.PreserveAspectFit
-                smooth: true
-            }
-
-            Colorize {
-                anchors.fill: jackInputLouderIcon
-                source: jackInputLouderIcon
-                hue: 0
-                saturation: 0
-                lightness: imageLightnessValue
+                anchors.top: jackInputMeters.bottom
+                anchors.topMargin: 16 * virtualstudio.uiScale
+                height: 30 * virtualstudio.uiScale
+                labelText: "Send"
+                tooltipText: "How loudly other participants hear you"
+                showLabel: false
+                sliderEnabled: true
             }
 
             Button {
@@ -1128,4 +744,27 @@ Rectangle {
             }
         }
     }
+
+    Component {
+        id: scanningDevices
+
+        Item {
+            anchors.top: parent.top
+            anchors.topMargin: 24 * virtualstudio.uiScale
+            anchors.bottom: parent.bottom
+            anchors.left: parent.left
+            anchors.leftMargin: leftMargin * virtualstudio.uiScale
+            anchors.right: parent.right
+
+            Text {
+                id: scanningDevicesLabel
+                x: 0; y: 0
+                width: parent.width - (16 * virtualstudio.uiScale)
+                text: "Scanning audio devices..."
+                font { family: "Poppins"; pixelSize: fontMedium * virtualstudio.fontScale * virtualstudio.uiScale }
+                wrapMode: Text.WordWrap
+                color: textColour
+            }
+        }
+    }
 }
index 85130f9c90ec0901b5790b2b0600b2f086457f9d..18db84437862e68f1bb42059d0a3b18c03889556 100644 (file)
@@ -1,5 +1,5 @@
-import QtQuick 2.12
-import QtQuick.Controls 2.12
+import QtQuick
+import QtQuick.Controls
 
 Item {
     width: parent.width; height: parent.height
@@ -71,24 +71,24 @@ Item {
         spacing: 16 * virtualstudio.uiScale
         header: footer
         footer: footer
-        model: serverModel
+        model: virtualstudio.serverModel
         clip: true
         boundsBehavior: Flickable.StopAtBounds
         delegate: Studio {
             x: 16 * virtualstudio.uiScale
             width: studioListView.width - (2 * x)
-            serverLocation: virtualstudio.regions[location] ? "in " + virtualstudio.regions[location].label : ""
-            flagImage: bannerURL ? bannerURL : flag
-            studioName: name
-            publicStudio: isPublic
-            admin: isAdmin
-            available: canConnect
+            serverLocation: virtualstudio.regions[modelData.location] ? "in " + virtualstudio.regions[modelData.location].label : ""
+            flagImage: modelData.bannerURL ? modelData.bannerURL : modelData.flag
+            studioName: modelData.name
+            publicStudio: modelData.isPublic
+            admin: modelData.isAdmin
+            available: modelData.canConnect
             connected: false
-            studioId: id ? id : ""
-            inviteKeyString: inviteKey ? inviteKey : ""
+            studioId: modelData.id ? modelData.id : ""
+            inviteKeyString: modelData.inviteKey ? modelData.inviteKey : ""
         }
 
-        section {property: "type"; criteria: ViewSection.FullString; delegate: SectionHeading {} }
+        section { property: "modelData.type"; criteria: ViewSection.FullString; delegate: SectionHeading {} }
 
         // Show sectionHeading if there are no Studios in list
         SectionHeading {
@@ -206,7 +206,7 @@ Item {
         MouseArea {
             z: -1
             anchors.fill: parent
-            onWheel: {
+            onWheel: function (wheel) {
                 // trackpad
                 studioListView.contentY -= wheel.pixelDelta.y;
                 // mouse wheel
@@ -219,7 +219,7 @@ Item {
             // Customize scroll properties on different platforms
             if (Qt.platform.os == "linux" || Qt.platform.os == "osx" ||
                 Qt.platform.os == "unix" || Qt.platform.os == "windows") {
-                var scrollBar = Qt.createQmlObject('import QtQuick.Controls 2.12; ScrollBar{}',
+                var scrollBar = Qt.createQmlObject('import QtQuick.Controls; ScrollBar{}',
                                                    studioListView,
                                                    "dynamicSnippet1");
                 scrollBar.policy = ScrollBar.AlwaysOn;
@@ -287,12 +287,7 @@ Item {
                 border.width: 1
                 border.color: settingsButton.down ? buttonPressedStroke : (settingsButton.hovered ? buttonHoverStroke : buttonStroke)
             }
-            Timer {
-                id: restartAudioTimer
-                interval: 805; running: false; repeat: false
-                onTriggered: virtualstudio.audioActivated = true;
-            }
-            onClicked: { virtualstudio.windowState = "settings"; restartAudioTimer.restart(); }
+            onClicked: { virtualstudio.windowState = "settings"; audio.startAudio(); }
             display: AbstractButton.TextBesideIcon
             font {
                 family: "Poppins";
@@ -307,6 +302,9 @@ Item {
         }
     }
 
+    FeedbackSurvey {
+    }
+
     Connections {
         target: virtualstudio
         // Need to do this to avoid layout issues with our section header.
diff --git a/src/gui/ChangeDevices.qml b/src/gui/ChangeDevices.qml
new file mode 100644 (file)
index 0000000..9eea3c9
--- /dev/null
@@ -0,0 +1,510 @@
+import QtQuick
+import QtQuick.Controls
+
+Rectangle {
+    width: parent.width; height: parent.height
+    color: backgroundColour
+    clip: true
+
+    property int fontBig: 28
+    property int fontMedium: 12
+    property int fontSmall: 10
+    property int fontTiny: 8
+
+    property int rightMargin: 16
+    property int bottomToolTipMargin: 8
+    property int rightToolTipMargin: 4
+
+    property string saveButtonText: "#DB0A0A"
+    property string textColour: virtualstudio.darkMode ? "#FAFBFB" : "#0F0D0D"
+    property string meterColor: virtualstudio.darkMode ? "gray" : "#E0E0E0"
+    property real muteButtonLightnessValue: virtualstudio.darkMode ? 1.0 : 0.0
+    property real muteButtonMutedLightnessValue: 0.24
+    property real muteButtonMutedSaturationValue: 0.73
+    property string buttonStroke: virtualstudio.darkMode ? "#80827D7D" : "#34979797"
+    property string shadowColour: virtualstudio.darkMode ? "#40000000" : "#80A1A1A1"
+    property string toolTipBackgroundColour: virtualstudio.darkMode ? "#323232" : "#F3F3F3"
+    property string toolTipTextColour: textColour
+
+    property string browserButtonColour: virtualstudio.darkMode ? "#494646" : "#EAECEC"
+    property string browserButtonHoverColour: virtualstudio.darkMode ? "#5B5858" : "#D3D4D4"
+    property string browserButtonPressedColour: virtualstudio.darkMode ? "#524F4F" : "#DEE0E0"
+    property string browserButtonStroke: virtualstudio.darkMode ? "#80827D7D" : "#40979797"
+    property string browserButtonHoverStroke: virtualstudio.darkMode ? "#7B7777" : "#BABCBC"
+    property string browserButtonPressedStroke: virtualstudio.darkMode ? "#827D7D" : "#BABCBC"
+
+    property string linkText: virtualstudio.darkMode ? "#8B8D8D" : "#272525"
+
+    function getCurrentInputDeviceIndex () {
+        if (audio.inputDevice === "") {
+            return audio.inputComboModel.findIndex(elem => elem.type === "element");
+        }
+
+        let idx = audio.inputComboModel.findIndex(elem => elem.type === "element" && elem.text === audio.inputDevice);
+        if (idx < 0) {
+            idx = audio.inputComboModel.findIndex(elem => elem.type === "element");
+        }
+
+        return idx;
+    }
+
+    function getCurrentOutputDeviceIndex() {
+        if (audio.outputDevice === "") {
+            return audio.outputComboModel.findIndex(elem => elem.type === "element");
+        }
+
+        let idx = audio.outputComboModel.findIndex(elem => elem.type === "element" && elem.text === audio.outputDevice);
+        if (idx < 0) {
+            idx = audio.outputComboModel.findIndex(elem => elem.type === "element");
+        }
+
+        return idx;
+    }
+
+    Rectangle {
+        width: parent.width; height: 360
+        anchors.verticalCenter: parent.verticalCenter
+        color: backgroundColour
+        radius: 6 * virtualstudio.uiScale
+
+        Item {
+            id: usingRtAudio
+            anchors.top: parent.top
+            anchors.topMargin: 24 * virtualstudio.uiScale
+            anchors.bottom: parent.bottom
+            anchors.left: parent.left
+            anchors.leftMargin: 24 * virtualstudio.uiScale
+            anchors.right: parent.right
+
+            Rectangle {
+                id: leftSpacer
+                x: 0; y: 0
+                width: 144 * virtualstudio.uiScale
+                height: 0
+                color: "transparent"
+            }
+
+            DeviceRefreshButton {
+                id: refreshButton
+                y: 0;
+                x: parent.width - (144 + rightMargin) * virtualstudio.uiScale;
+                enabled: !audio.scanningDevices
+                onDeviceRefresh: function () {
+                    virtualstudio.triggerReconnect(true);
+                }
+            }
+
+            Text {
+                text: "Scanning Devices"
+                y: 0;
+                anchors.right: refreshButton.left;
+                anchors.rightMargin: 16 * virtualstudio.uiScale;
+                font { family: "Poppins"; pixelSize: fontTiny * virtualstudio.fontScale * virtualstudio.uiScale }
+                color: textColour
+                visible: audio.scanningDevices
+            }
+
+            Text {
+                id: outputLabel
+                x: 0;
+                anchors.top: refreshButton.bottom
+                anchors.topMargin: 24 * virtualstudio.uiScale
+                text: "Output Device"
+                font { family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale }
+                color: textColour
+            }
+
+            InfoTooltip {
+                id: outputHelpIcon
+                anchors.left: outputLabel.right
+                anchors.bottom: outputLabel.top
+                anchors.bottomMargin: -8 * virtualstudio.uiScale
+                size: 16 * virtualstudio.uiScale
+                content: qsTr("How you'll hear the studio audio")
+            }
+
+            AppIcon {
+                id: headphonesIcon
+                anchors.left: outputLabel.left
+                anchors.top: outputLabel.bottom
+                anchors.topMargin: bottomToolTipMargin * virtualstudio.uiScale
+                width: 28 * virtualstudio.uiScale
+                height: 28 * virtualstudio.uiScale
+                icon.source: "headphones.svg"
+            }
+
+            ComboBox {
+                id: outputCombo
+                anchors.left: leftSpacer.right
+                anchors.verticalCenter: outputLabel.verticalCenter
+                anchors.rightMargin: rightMargin * virtualstudio.uiScale
+                width: parent.width - leftSpacer.width - rightMargin * virtualstudio.uiScale
+                enabled: virtualstudio.connectionState == "Connected"
+                model: audio.outputComboModel
+                currentIndex: getCurrentOutputDeviceIndex()
+                delegate: ItemDelegate {
+                    required property var modelData
+                    required property int index
+
+                    leftPadding: 0
+
+                    width: parent.width
+                    contentItem: Text {
+                        leftPadding: modelData.type === "element" && outputCombo.model.filter(it => it.type === "header").length > 0 ? 24 : 12
+                        text: modelData.text || ""
+                        font.bold: modelData.type === "header"
+                    }
+                    highlighted: outputCombo.highlightedIndex === index
+                    MouseArea {
+                        anchors.fill: parent
+                        onClicked: {
+                            if (modelData.type == "element") {
+                                outputCombo.currentIndex = index
+                                outputCombo.popup.close()
+                                audio.outputDevice = modelData.text
+                                if (modelData.category === "Low-Latency (ASIO)") {
+                                    let inputComboIdx = inputCombo.model.findIndex(it => it.category === "Low-Latency (ASIO)" && it.text === modelData.text);
+                                    if (inputComboIdx !== null && inputComboIdx !== undefined) {
+                                        inputCombo.currentIndex = inputComboIdx;
+                                        audio.inputDevice = modelData.text
+                                    }
+                                }
+                                virtualstudio.triggerReconnect(false);
+                            }
+                        }
+                    }
+                }
+                contentItem: Text {
+                    leftPadding: 12
+                    font: outputCombo.font
+                    horizontalAlignment: Text.AlignHLeft
+                    verticalAlignment: Text.AlignVCenter
+                    elide: Text.ElideRight
+                    text: outputCombo.model[outputCombo.currentIndex] && outputCombo.model[outputCombo.currentIndex].text ? outputCombo.model[outputCombo.currentIndex].text : ""
+                }
+            }
+
+            Text {
+                id: outputChannelsLabel
+                anchors.left: outputCombo.left
+                anchors.right: outputCombo.horizontalCenter
+                anchors.top: outputCombo.bottom
+                anchors.topMargin: 12 * virtualstudio.uiScale
+                textFormat: Text.RichText
+                text: "Output Channel(s)"
+                font { family: "Poppins"; pixelSize: fontTiny * virtualstudio.fontScale * virtualstudio.uiScale }
+                color: textColour
+            }
+
+            ComboBox {
+                id: outputChannelsCombo
+                anchors.left: outputCombo.left
+                anchors.right: outputCombo.horizontalCenter
+                anchors.rightMargin: 8 * virtualstudio.uiScale
+                anchors.top: outputChannelsLabel.bottom
+                anchors.topMargin: 4 * virtualstudio.uiScale
+                enabled: audio.outputChannelsComboModel.length > 1 && virtualstudio.connectionState == "Connected"
+                model: audio.outputChannelsComboModel
+                currentIndex: (() => {
+                    let idx = audio.outputChannelsComboModel.findIndex(elem => elem.baseChannel === audio.baseOutputChannel
+                        && elem.numChannels === audio.numOutputChannels);
+                    if (idx < 0) {
+                        idx = 0;
+                    }
+                    return idx;
+                })()
+                delegate: ItemDelegate {
+                    required property var modelData
+                    required property int index
+                    width: parent.width
+                    contentItem: Text {
+                        text: modelData.label
+                    }
+                    highlighted: outputChannelsCombo.highlightedIndex === index
+                    MouseArea {
+                        anchors.fill: parent
+                        onClicked: {
+                            outputChannelsCombo.currentIndex = index
+                            outputChannelsCombo.popup.close()
+                            audio.baseOutputChannel = modelData.baseChannel
+                            audio.numOutputChannels = modelData.numChannels
+                            virtualstudio.triggerReconnect(false);
+                        }
+                    }
+                }
+                contentItem: Text {
+                    leftPadding: 12
+                    font: inputCombo.font
+                    horizontalAlignment: Text.AlignHLeft
+                    verticalAlignment: Text.AlignVCenter
+                    elide: Text.ElideRight
+                    text: outputChannelsCombo.model[outputChannelsCombo.currentIndex].label || ""
+                }
+            }
+
+            Text {
+                id: inputLabel
+                anchors.left: outputLabel.left
+                anchors.top: outputChannelsCombo.bottom
+                anchors.topMargin: 32 * virtualstudio.uiScale
+                text: "Input Device"
+                font { family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale }
+                color: textColour
+            }
+
+            InfoTooltip {
+                id: inputHelpIcon
+                anchors.left: inputLabel.right
+                anchors.bottom: inputLabel.top
+                anchors.bottomMargin: -8 * virtualstudio.uiScale
+                size: 16 * virtualstudio.uiScale
+                content: qsTr("Audio sent to the studio (microphone, instrument, mixer, etc.)")
+            }
+
+            AppIcon {
+                id: microphoneIcon
+                anchors.left: inputLabel.left
+                anchors.top: inputLabel.bottom
+                anchors.topMargin: bottomToolTipMargin * virtualstudio.uiScale
+                width: 32 * virtualstudio.uiScale
+                height: 32 * virtualstudio.uiScale
+                icon.source: "mic.svg"
+            }
+
+            ComboBox {
+                id: inputCombo
+                model: audio.inputComboModel
+                currentIndex: getCurrentInputDeviceIndex()
+                anchors.left: outputCombo.left
+                anchors.right: outputCombo.right
+                anchors.verticalCenter: inputLabel.verticalCenter
+                enabled: virtualstudio.connectionState == "Connected"
+                delegate: ItemDelegate {
+                    required property var modelData
+                    required property int index
+
+                    leftPadding: 0
+
+                    width: parent.width
+                    contentItem: Text {
+                        leftPadding: modelData.type === "element" && inputCombo.model.filter(it => it.type === "header").length > 0 ? 24 : 12
+                        text: modelData.text || ""
+                        font.bold: modelData.type === "header"
+                    }
+                    highlighted: inputCombo.highlightedIndex === index
+                    MouseArea {
+                        anchors.fill: parent
+                        onClicked: {
+                            if (modelData.type == "element") {
+                                inputCombo.currentIndex = index
+                                inputCombo.popup.close()
+                                audio.inputDevice = modelData.text
+                                if (modelData.category === "Low-Latency (ASIO)") {
+                                    let outputComboIdx = outputCombo.model.findIndex(it => it.category === "Low-Latency (ASIO)" && it.text === modelData.text);
+                                    if (outputComboIdx !== null && outputComboIdx !== undefined) {
+                                        outputCombo.currentIndex = outputComboIdx;
+                                        audio.outputDevice = modelData.text
+                                    }
+                                }
+                                virtualstudio.triggerReconnect(false);
+                            }
+                        }
+                    }
+                }
+                contentItem: Text {
+                    leftPadding: 12
+                    font: inputCombo.font
+                    horizontalAlignment: Text.AlignHLeft
+                    verticalAlignment: Text.AlignVCenter
+                    elide: Text.ElideRight
+                    text: inputCombo.model[inputCombo.currentIndex] && inputCombo.model[inputCombo.currentIndex].text ? inputCombo.model[inputCombo.currentIndex].text : ""
+                }
+            }
+
+            Text {
+                id: inputChannelsLabel
+                anchors.left: inputCombo.left
+                anchors.right: inputCombo.horizontalCenter
+                anchors.top: inputCombo.bottom
+                anchors.topMargin: 12 * virtualstudio.uiScale
+                textFormat: Text.RichText
+                text: "Input Channel(s)"
+                font { family: "Poppins"; pixelSize: fontTiny * virtualstudio.fontScale * virtualstudio.uiScale }
+                color: textColour
+            }
+
+            ComboBox {
+                id: inputChannelsCombo
+                anchors.left: inputCombo.left
+                anchors.right: inputCombo.horizontalCenter
+                anchors.rightMargin: 8 * virtualstudio.uiScale
+                anchors.top: inputChannelsLabel.bottom
+                anchors.topMargin: 4 * virtualstudio.uiScale
+                enabled: audio.inputChannelsComboModel.length > 1 && virtualstudio.connectionState == "Connected"
+                model: audio.inputChannelsComboModel
+                currentIndex: (() => {
+                    let idx = audio.inputChannelsComboModel.findIndex(elem => elem.baseChannel === audio.baseInputChannel
+                        && elem.numChannels === audio.numInputChannels);
+                    if (idx < 0) {
+                        idx = 0;
+                    }
+                    return idx;
+                })()
+                delegate: ItemDelegate {
+                    required property var modelData
+                    required property int index
+                    width: parent.width
+                    contentItem: Text {
+                        text: modelData.label
+                    }
+                    highlighted: inputChannelsCombo.highlightedIndex === index
+                    MouseArea {
+                        anchors.fill: parent
+                        onClicked: {
+                            inputChannelsCombo.currentIndex = index
+                            inputChannelsCombo.popup.close()
+                            audio.baseInputChannel = modelData.baseChannel
+                            audio.numInputChannels = modelData.numChannels
+                            virtualstudio.triggerReconnect(false);
+                        }
+                    }
+                }
+                contentItem: Text {
+                    leftPadding: 12
+                    font: inputCombo.font
+                    horizontalAlignment: Text.AlignHLeft
+                    verticalAlignment: Text.AlignVCenter
+                    elide: Text.ElideRight
+                    text: inputChannelsCombo.model[inputChannelsCombo.currentIndex].label || ""
+                }
+            }
+
+            Text {
+                id: inputMixModeLabel
+                anchors.left: inputCombo.horizontalCenter
+                anchors.right: inputCombo.right
+                anchors.rightMargin: 8 * virtualstudio.uiScale
+                anchors.top: inputCombo.bottom
+                anchors.topMargin: 12 * virtualstudio.uiScale
+                textFormat: Text.RichText
+                text: "Mono / Stereo"
+                font { family: "Poppins"; pixelSize: fontTiny * virtualstudio.fontScale * virtualstudio.uiScale }
+                color: textColour
+            }
+
+            ComboBox {
+                id: inputMixModeCombo
+                anchors.left: inputCombo.horizontalCenter
+                anchors.right: inputCombo.right
+                anchors.rightMargin: 8 * virtualstudio.uiScale
+                anchors.top: inputMixModeLabel.bottom
+                anchors.topMargin: 4 * virtualstudio.uiScale
+                enabled: audio.inputMixModeComboModel.length > 1 && virtualstudio.connectionState == "Connected"
+                model: audio.inputMixModeComboModel
+                currentIndex: (() => {
+                    let idx = audio.inputMixModeComboModel.findIndex(elem => elem.value === audio.inputMixMode);
+                    if (idx < 0) {
+                        idx = 0;
+                    }
+                    return idx;
+                })()
+                delegate: ItemDelegate {
+                    required property var modelData
+                    required property int index
+                    width: parent.width
+                    contentItem: Text {
+                        text: modelData.label
+                    }
+                    highlighted: inputMixModeCombo.highlightedIndex === index
+                    MouseArea {
+                        anchors.fill: parent
+                        onClicked: {
+                            inputMixModeCombo.currentIndex = index
+                            inputMixModeCombo.popup.close()
+                            audio.inputMixMode = audio.inputMixModeComboModel[index].value
+                            virtualstudio.triggerReconnect(false);
+                        }
+                    }
+                }
+                contentItem: Text {
+                    leftPadding: 12
+                    font: inputCombo.font
+                    horizontalAlignment: Text.AlignHLeft
+                    verticalAlignment: Text.AlignVCenter
+                    elide: Text.ElideRight
+                    text: inputMixModeCombo.model[inputMixModeCombo.currentIndex].label || ""
+                }
+            }
+
+            Text {
+                id: inputChannelHelpMessage
+                anchors.left: inputChannelsCombo.left
+                anchors.leftMargin: 2 * virtualstudio.uiScale
+                anchors.right: inputChannelsCombo.right
+                anchors.top: inputChannelsCombo.bottom
+                anchors.topMargin: 8 * virtualstudio.uiScale
+                textFormat: Text.RichText
+                wrapMode: Text.WordWrap
+                text: audio.inputChannelsComboModel.length > 1 ? "Choose up to 2 channels" : "Only 1 channel available"
+                font { family: "Poppins"; pixelSize: fontTiny * virtualstudio.fontScale * virtualstudio.uiScale }
+                color: textColour
+            }
+
+            Text {
+                id: inputMixModeHelpMessage
+                anchors.left: inputMixModeCombo.left
+                anchors.leftMargin: 2 * virtualstudio.uiScale
+                anchors.right: inputMixModeCombo.right
+                anchors.top: inputMixModeCombo.bottom
+                anchors.topMargin: 8 * virtualstudio.uiScale
+                textFormat: Text.RichText
+                wrapMode: Text.WordWrap
+                text: (() => {
+                    if (audio.inputMixMode === 2) {
+                        return "Treat the channels as Left and Right signals, coming through each speaker separately.";
+                    } else if (audio.inputMixMode === 3) {
+                        return "Combine the channels into one central channel coming through both speakers.";
+                    } else if (audio.inputMixMode === 1) {
+                        return "Send a single channel of audio";
+                    } else {
+                        return "";
+                    }
+                })()
+                font { family: "Poppins"; pixelSize: fontTiny * virtualstudio.fontScale * virtualstudio.uiScale }
+                color: textColour
+            }
+
+            DeviceWarning {
+                id: deviceWarning
+                anchors.left: inputCombo.left
+                anchors.top: inputMixModeHelpMessage.bottom
+                anchors.topMargin: 48 * virtualstudio.uiScale
+                visible: Boolean(audio.devicesError) || Boolean(audio.devicesWarning)
+            }
+        }
+    }
+
+    Button {
+        id: backButton
+        background: Rectangle {
+            radius: 6 * virtualstudio.uiScale
+            color: backButton.down ? browserButtonPressedColour : (backButton.hovered ? browserButtonHoverColour : browserButtonColour)
+        }
+        onClicked: {
+            virtualstudio.saveSettings();
+            virtualstudio.windowState = "connected";
+        }
+        anchors.bottom: parent.bottom
+        anchors.bottomMargin: 16 * virtualstudio.uiScale;
+        anchors.left: parent.left
+        anchors.leftMargin: 16 * virtualstudio.uiScale;
+        width: 150 * virtualstudio.uiScale; height: 36 * virtualstudio.uiScale
+
+        Text {
+            text: "Back"
+            font { family: "Poppins"; pixelSize: fontMedium * virtualstudio.fontScale * virtualstudio.uiScale}
+            anchors { horizontalCenter: parent.horizontalCenter; verticalCenter: parent.verticalCenter }
+            color: textColour
+        }
+    }
+}
index b0eb3befe39ae67eff507c69f34356ef703bdca5..2d5199068f7bcef6dada4429fc277a32b76ac5e0 100644 (file)
@@ -1,6 +1,7 @@
-import QtQuick 2.12
-import QtQuick.Controls 2.12
-import QtGraphicalEffects 1.12
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import org.jacktrip.jacktrip 1.0
 
 Item {
     width: parent.width; height: parent.height
@@ -19,13 +20,14 @@ Item {
     property int bottomToolTipMargin: 8
     property int rightToolTipMargin: 4
 
-    property string studioStatus: (virtualstudio.currentStudio >= 0 ? serverModel[virtualstudio.currentStudio].status : "")
+    property string studioStatus: (virtualstudio.currentStudio.id === "" ? "" : virtualstudio.currentStudio.status)
     property bool showReadyScreen: studioStatus === "Ready"
     property bool showStartingScreen: studioStatus === "Starting"
-    property bool showStoppingScreen: (virtualstudio.currentStudio >= 0 ? serverModel[virtualstudio.currentStudio].isAdmin && !serverModel[virtualstudio.currentStudio].enabled && serverModel[virtualstudio.currentStudio].cloudId !== "" : false)
+    property bool showStoppingScreen: (virtualstudio.currentStudio.id === "" ? false : (virtualstudio.currentStudio.isAdmin && !virtualstudio.currentStudio.enabled && virtualstudio.currentStudio.cloudId !== ""))
     property bool showWaitingScreen: !showStoppingScreen && !showStartingScreen && !showReadyScreen
 
     property string buttonColour: virtualstudio.darkMode ? "#494646" : "#EAECEC"
+    property string strokeColor: virtualstudio.darkMode ? "#80827D7D" : "#34979797"
 
     property string browserButtonColour: virtualstudio.darkMode ? "#494646" : "#EAECEC"
     property string browserButtonHoverColour: virtualstudio.darkMode ? "#5B5858" : "#D3D4D4"
@@ -42,15 +44,10 @@ Item {
     property string muteButtonMutedColor: "#FCB6B6"
     property string textColour: virtualstudio.darkMode ? "#FAFBFB" : "#0F0D0D"
     property string meterColor: virtualstudio.darkMode ? "gray" : "#E0E0E0"
-    property real imageLightnessValue: virtualstudio.darkMode ? 0.8 : 0.2
     property real muteButtonLightnessValue: virtualstudio.darkMode ? 1.0 : 0.0
     property real muteButtonMutedLightnessValue: 0.24
     property real muteButtonMutedSaturationValue: 0.73
     property string buttonStroke: virtualstudio.darkMode ? "#80827D7D" : "#34979797"
-    property string sliderColour: virtualstudio.darkMode ? "#BABCBC" :  "#EAECEC"
-    property string sliderPressedColour: virtualstudio.darkMode ? "#ACAFAF" : "#DEE0E0"
-    property string sliderTrackColour: virtualstudio.darkMode ? "#5B5858" : "light gray"
-    property string sliderActiveTrackColour: virtualstudio.darkMode ? "light gray" : "black"
     property string shadowColour: virtualstudio.darkMode ? "#40000000" : "#80A1A1A1"
     property string toolTipBackgroundColour: virtualstudio.darkMode ? "#323232" : "#F3F3F3"
     property string toolTipTextColour: textColour
@@ -61,1582 +58,38 @@ Item {
     property string meterYellow: "#F5BF4F"
     property string meterRed: "#F21B1B"
 
-    property bool isUsingRtAudio: virtualstudio.audioBackend == "RtAudio"
+    property bool isUsingRtAudio: audio.audioBackend == "RtAudio"
 
-    function getCurrentInputDeviceIndex () {
-        if (virtualstudio.inputDevice === "") {
-            return virtualstudio.inputComboModel.findIndex(elem => elem.type === "element");
-        }
-
-        let idx = virtualstudio.inputComboModel.findIndex(elem => elem.type === "element" && elem.text === virtualstudio.inputDevice);
-        if (idx < 0) {
-            idx = virtualstudio.inputComboModel.findIndex(elem => elem.type === "element");
-        }
-
-        return idx;
-    }
-
-    function getCurrentOutputDeviceIndex() {
-        if (virtualstudio.outputDevice === "") {
-            return virtualstudio.outputComboModel.findIndex(elem => elem.type === "element");
-        }
-
-        let idx = virtualstudio.outputComboModel.findIndex(elem => elem.type === "element" && elem.text === virtualstudio.outputDevice);
-        if (idx < 0) {
-            idx = virtualstudio.outputComboModel.findIndex(elem => elem.type === "element");
-        }
-
-        return idx;
-    }
-
-    function getNetworkStatsText () {
-        let minRtt = virtualstudio.networkStats.minRtt;
-        let maxRtt = virtualstudio.networkStats.maxRtt;
-        let avgRtt = virtualstudio.networkStats.avgRtt;
-
-        let texts = ["<b>Outage detected! Your connection is unstable.</b>", "Please plug into Ethernet & turn off WIFI."];
-
-        if (virtualstudio.networkOutage) {
-            return texts;
-        }
-
-        texts = ["Measuring stats ...", ""];
-
-        if (!minRtt || !maxRtt) {
-            return texts;
-        }
-
-        texts[0] = "<b>" + minRtt + " ms - " + maxRtt + " ms</b>, avg " + avgRtt + " ms round-trip time";
-
-        let quality = "poor";
-        if (avgRtt <= 25) {
-
-            if (maxRtt <= 30) {
-                quality = "excellent";
-            } else {
-                quality = "good";
-            }
-
-        } else if (avgRtt <= 30) {
-            quality = "good";
-        } else if (avgRtt <= 35) {
-            quality = "fair";
-        }
-
-        texts[1] = "Your connection quality is <b>" + quality + "</b>."
-        return texts;
-    }
-
-    Connections {
-        target: virtualstudio
-        function onInputDeviceChanged() {
-            inputCombo.currentIndex = getCurrentInputDeviceIndex();
-        }
-        function onOutputDeviceChanged() {
-            outputCombo.currentIndex = getCurrentOutputDeviceIndex();
-        }
-    }
-
-    Image {
-        id: jtlogo
-        x: parent.width - (49 * virtualstudio.uiScale); y: 16 * virtualstudio.uiScale
-        width: 32 * virtualstudio.uiScale; height: 59 * virtualstudio.uiScale
-        source: "logo.svg"
-        sourceSize: Qt.size(jtlogo.width,jtlogo.height)
-        fillMode: Image.PreserveAspectFit
-        smooth: true
-    }
-
-    Text {
-        id: heading
-        text: studioStatus === "Starting" ? "Starting..." : virtualstudio.connectionState
-        x: leftHeaderMargin * virtualstudio.uiScale; y: 34 * virtualstudio.uiScale
-        font { family: "Poppins"; weight: Font.Bold; pixelSize: fontBig * virtualstudio.fontScale * virtualstudio.uiScale }
-        color: textColour
-    }
-
-    Studio {
-        x: leftHeaderMargin * virtualstudio.uiScale; y: 96 * virtualstudio.uiScale
-        width: parent.width - (2 * x)
-        connected: true
-        serverLocation: virtualstudio.currentStudio >= 0 && virtualstudio.regions[serverModel[virtualstudio.currentStudio].location] ? "in " + virtualstudio.regions[serverModel[virtualstudio.currentStudio].location].label : ""
-        flagImage: virtualstudio.currentStudio >= 0 ? ( serverModel[virtualstudio.currentStudio].bannerURL ? serverModel[virtualstudio.currentStudio].bannerURL : serverModel[virtualstudio.currentStudio].flag ) : "flags/DE.svg"
-        studioName: virtualstudio.currentStudio >= 0 ? serverModel[virtualstudio.currentStudio].name : "Test Studio"
-        publicStudio: virtualstudio.currentStudio >= 0 ? serverModel[virtualstudio.currentStudio].isPublic : false
-        admin: virtualstudio.currentStudio >= 0 ? serverModel[virtualstudio.currentStudio].isAdmin : false
-        available: virtualstudio.currentStudio >= 0 ? serverModel[virtualstudio.currentStudio].canConnect : false
-        studioId: virtualstudio.currentStudio >= 0 ? serverModel[virtualstudio.currentStudio].id : ""
-        inviteKeyString: virtualstudio.currentStudio >= 0 ? serverModel[virtualstudio.currentStudio].inviteKey : ""
-    }
-
-    Item {
-        id: deviceSettings
-        visible: showReadyScreen && isUsingRtAudio
-        x: bodyMargin * virtualstudio.uiScale; y: 192 * virtualstudio.uiScale
-        width: parent.width - (2 * x)
-        height: 384 * virtualstudio.uiScale
-        clip: true
-
-        Button {
-            id: deviceSettingsButton
-            background: Rectangle {
-                radius: 6 * virtualstudio.uiScale
-                color: deviceSettingsButton.down ? browserButtonPressedColour : (deviceSettingsButton.hovered ? browserButtonHoverColour : browserButtonColour)
-            }
-            onClicked: popup.open()
-            anchors.right: parent.right
-            width: 144 * virtualstudio.uiScale; height: 24 * virtualstudio.uiScale
-
-            Text {
-                text: "Change Device Settings"
-                font { family: "Poppins"; pixelSize: fontTiny * virtualstudio.fontScale * virtualstudio.uiScale}
-                anchors { horizontalCenter: parent.horizontalCenter; verticalCenter: parent.verticalCenter }
-                color: textColour
-            }
-        }
-
-        Popup {
-            id: popup
-            padding: 1
-            width: parent.width
-            height: parent.height
-            anchors.centerIn: parent
-            modal: true
-            focus: true
-            closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
-            onClosed: {
-                virtualstudio.applySettings()
-            }
-
-            background: Rectangle {
-                anchors.fill: parent
-                color: "transparent"
-                radius: 6 * virtualstudio.uiScale
-                border.width: 1
-                border.color: buttonStroke
-                clip: true
-            }
-
-            contentItem: Rectangle {
-                width: parent.width
-                height: parent.height
-                color: backgroundColour
-                radius: 6 * virtualstudio.uiScale
-
-                Item {
-                    id: usingRtAudio
-                    anchors.top: parent.top
-                    anchors.topMargin: 24 * virtualstudio.uiScale
-                    anchors.bottom: parent.bottom
-                    anchors.left: parent.left
-                    anchors.leftMargin: 24 * virtualstudio.uiScale
-                    anchors.right: parent.right
-
-                    visible: isUsingRtAudio
-
-                    Rectangle {
-                        id: leftSpacer
-                        x: 0; y: 0
-                        width: 144 * virtualstudio.uiScale
-                        height: 0
-                        color: "transparent"
-                    }
-
-                    Text {
-                        id: outputLabel
-                        x: 0; y: 0
-                        text: "Output Device"
-                        font { family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale }
-                        color: textColour
-                    }
-
-                    Image {
-                        id: outputHelpIcon
-                        anchors.left: outputLabel.right
-                        anchors.bottom: outputLabel.top
-                        anchors.bottomMargin: -8 * virtualstudio.uiScale
-                        source: "help.svg"
-                        sourceSize: Qt.size(12 * virtualstudio.uiScale, 12 * virtualstudio.uiScale)
-                        fillMode: Image.PreserveAspectFit
-                        smooth: true
-
-                        property bool showToolTip: false
-
-                        MouseArea {
-                            id: outputMouseArea
-                            anchors.fill: parent
-                            hoverEnabled: true
-                            onEntered: outputHelpIcon.showToolTip = true
-                            onExited: outputHelpIcon.showToolTip = false
-                        }
-
-                        ToolTip {
-                            visible: outputHelpIcon.showToolTip
-                            contentItem: Rectangle {
-                                color: toolTipBackgroundColour
-                                radius: 3
-                                anchors.fill: parent
-                                anchors.bottomMargin: bottomToolTipMargin * virtualstudio.uiScale
-                                anchors.rightMargin: rightToolTipMargin * virtualstudio.uiScale
-                                layer.enabled: true
-                                border.width: 1
-                                border.color: buttonStroke
-
-                                Text {
-                                    anchors.centerIn: parent
-                                    font { family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale}
-                                    text: qsTr("How you'll hear the studio audio")
-                                    color: toolTipTextColour
-                                }
-                            }
-                            background: Rectangle {
-                                color: "transparent"
-                            }
-                        }
-                    }
-
-                    Colorize {
-                        anchors.fill: outputHelpIcon
-                        source: outputHelpIcon
-                        hue: 0
-                        saturation: 0
-                        lightness: imageLightnessValue
-                    }
-
-                    Image {
-                        id: headphonesIcon
-                        anchors.left: outputLabel.left
-                        anchors.top: outputLabel.bottom
-                        anchors.topMargin: bottomToolTipMargin * virtualstudio.uiScale
-                        source: "headphones.svg"
-                        sourceSize: Qt.size(28 * virtualstudio.uiScale, 28 * virtualstudio.uiScale)
-                        fillMode: Image.PreserveAspectFit
-                        smooth: true
-                    }
-
-                    Colorize {
-                        anchors.fill: headphonesIcon
-                        source: headphonesIcon
-                        hue: 0
-                        saturation: 0
-                        lightness: imageLightnessValue
-                    }
-
-                    ComboBox {
-                        id: outputCombo
-                        anchors.left: leftSpacer.right
-                        anchors.verticalCenter: outputLabel.verticalCenter
-                        anchors.rightMargin: rightMargin * virtualstudio.uiScale
-                        width: parent.width - leftSpacer.width - rightMargin * virtualstudio.uiScale
-                        enabled: virtualstudio.connectionState == "Connected"
-                        model: virtualstudio.outputComboModel
-                        currentIndex: getCurrentOutputDeviceIndex()
-                        delegate: ItemDelegate {
-                            required property var modelData
-                            required property int index
-
-                            leftPadding: 0
-
-                            width: parent.width
-                            contentItem: Text {
-                                leftPadding: modelData.type === "element" && outputCombo.model.filter(it => it.type === "header").length > 0 ? 24 : 12
-                                text: modelData.text || ""
-                                font.bold: modelData.type === "header"
-                            }
-                            highlighted: outputCombo.highlightedIndex === index
-                            MouseArea {
-                                anchors.fill: parent
-                                onClicked: {
-                                    if (modelData.type == "element") {
-                                        outputCombo.currentIndex = index
-                                        outputCombo.popup.close()
-                                        virtualstudio.outputDevice = modelData.text
-                                        if (modelData.category === "Low-Latency (ASIO)") {
-                                            let inputComboIdx = inputCombo.model.findIndex(it => it.category === "Low-Latency (ASIO)" && it.text === modelData.text);
-                                            if (inputComboIdx !== null && inputComboIdx !== undefined) {
-                                                inputCombo.currentIndex = inputComboIdx;
-                                                virtualstudio.inputDevice = modelData.text
-                                            }
-                                        }
-                                        virtualstudio.validateDevicesState()
-                                    }
-                                }
-                            }
-                        }
-                        contentItem: Text {
-                            leftPadding: 12
-                            font: outputCombo.font
-                            horizontalAlignment: Text.AlignHLeft
-                            verticalAlignment: Text.AlignVCenter
-                            elide: Text.ElideRight
-                            text: outputCombo.model[outputCombo.currentIndex] && outputCombo.model[outputCombo.currentIndex].text ? outputCombo.model[outputCombo.currentIndex].text : ""
-                        }
-                    }
-
-                    Text {
-                        id: outputChannelsLabel
-                        anchors.left: outputCombo.left
-                        anchors.right: outputCombo.horizontalCenter
-                        anchors.top: outputCombo.bottom
-                        anchors.topMargin: 12 * virtualstudio.uiScale
-                        textFormat: Text.RichText
-                        text: "Output Channel(s)"
-                        font { family: "Poppins"; pixelSize: fontTiny * virtualstudio.fontScale * virtualstudio.uiScale }
-                        color: textColour
-                    }
-
-                    ComboBox {
-                        id: outputChannelsCombo
-                        anchors.left: outputCombo.left
-                        anchors.right: outputCombo.horizontalCenter
-                        anchors.rightMargin: 8 * virtualstudio.uiScale
-                        anchors.top: outputChannelsLabel.bottom
-                        anchors.topMargin: 4 * virtualstudio.uiScale
-                        enabled: virtualstudio.connectionState == "Connected"
-                        model: virtualstudio.outputChannelsComboModel
-                        currentIndex: (() => {
-                            let idx = virtualstudio.outputChannelsComboModel.findIndex(elem => elem.baseChannel === virtualstudio.baseOutputChannel
-                                && elem.numChannels === virtualstudio.numOutputChannels);
-                            if (idx < 0) {
-                                idx = 0;
-                            }
-                            return idx;
-                        })()
-                        delegate: ItemDelegate {
-                            required property var modelData
-                            required property int index
-                            width: parent.width
-                            contentItem: Text {
-                                text: modelData.label
-                            }
-                            highlighted: outputChannelsCombo.highlightedIndex === index
-                            MouseArea {
-                                anchors.fill: parent
-                                onClicked: {
-                                    outputChannelsCombo.currentIndex = index
-                                    outputChannelsCombo.popup.close()
-                                    virtualstudio.baseOutputChannel = modelData.baseChannel
-                                    virtualstudio.numOutputChannels = modelData.numChannels
-                                    virtualstudio.validateDevicesState()
-                                }
-                            }
-                        }
-                        contentItem: Text {
-                            leftPadding: 12
-                            font: inputCombo.font
-                            horizontalAlignment: Text.AlignHLeft
-                            verticalAlignment: Text.AlignVCenter
-                            elide: Text.ElideRight
-                            text: outputChannelsCombo.model[outputChannelsCombo.currentIndex].label || ""
-                        }
-                    }
-
-                    Text {
-                        id: inputLabel
-                        anchors.left: outputLabel.left
-                        anchors.top: outputChannelsCombo.bottom
-                        anchors.topMargin: 32 * virtualstudio.uiScale
-                        text: "Input Device"
-                        font { family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale }
-                        color: textColour
-                    }
-
-                    Image {
-                        id: inputHelpIcon
-                        anchors.left: inputLabel.right
-                        anchors.bottom: inputLabel.top
-                        anchors.bottomMargin: -8 * virtualstudio.uiScale
-                        source: "help.svg"
-                        sourceSize: Qt.size(12 * virtualstudio.uiScale, 12 * virtualstudio.uiScale)
-                        fillMode: Image.PreserveAspectFit
-                        smooth: true
-
-                        property bool showToolTip: false
-
-                        MouseArea {
-                            id: inputMouseArea
-                            anchors.fill: parent
-                            hoverEnabled: true
-                            onEntered: inputHelpIcon.showToolTip = true
-                            onExited: inputHelpIcon.showToolTip = false
-                        }
-
-                        ToolTip {
-                            visible: inputHelpIcon.showToolTip
-                            contentItem: Rectangle {
-                                color: toolTipBackgroundColour
-                                radius: 3
-                                anchors.fill: parent
-                                anchors.bottomMargin: bottomToolTipMargin * virtualstudio.uiScale
-                                anchors.rightMargin: rightToolTipMargin * virtualstudio.uiScale
-                                layer.enabled: true
-                                border.width: 1
-                                border.color: buttonStroke
-
-                                Text {
-                                    anchors.centerIn: parent
-                                    font { family: "Poppins"; pixelSize: fontTiny * virtualstudio.fontScale * virtualstudio.uiScale}
-                                    text: qsTr("Audio sent to the studio (microphone, instrument, mixer, etc.)")
-                                    color: toolTipTextColour
-                                }
-                            }
-                            background: Rectangle {
-                                color: "transparent"
-                            }
-                        }
-                    }
-
-                    Colorize {
-                        anchors.fill: inputHelpIcon
-                        source: inputHelpIcon
-                        hue: 0
-                        saturation: 0
-                        lightness: imageLightnessValue
-                    }
-
-                    Image {
-                        id: microphoneIcon
-                        anchors.left: inputLabel.left
-                        anchors.top: inputLabel.bottom
-                        anchors.topMargin: bottomToolTipMargin * virtualstudio.uiScale
-                        source: "mic.svg"
-                        sourceSize: Qt.size(32 * virtualstudio.uiScale, 32 * virtualstudio.uiScale)
-                        fillMode: Image.PreserveAspectFit
-                        smooth: true
-                    }
-
-                    Colorize {
-                        anchors.fill: microphoneIcon
-                        source: microphoneIcon
-                        hue: 0
-                        saturation: 0
-                        lightness: imageLightnessValue
-                    }
-
-                    ComboBox {
-                        id: inputCombo
-                        model: virtualstudio.inputComboModel
-                        currentIndex: getCurrentInputDeviceIndex()
-                        anchors.left: outputCombo.left
-                        anchors.right: outputCombo.right
-                        anchors.verticalCenter: inputLabel.verticalCenter
-                        enabled: virtualstudio.connectionState == "Connected"
-                        delegate: ItemDelegate {
-                            required property var modelData
-                            required property int index
-
-                            leftPadding: 0
-
-                            width: parent.width
-                            contentItem: Text {
-                                leftPadding: modelData.type === "element" && inputCombo.model.filter(it => it.type === "header").length > 0 ? 24 : 12
-                                text: modelData.text || ""
-                                font.bold: modelData.type === "header"
-                            }
-                            highlighted: inputCombo.highlightedIndex === index
-                            MouseArea {
-                                anchors.fill: parent
-                                onClicked: {
-                                    if (modelData.type == "element") {
-                                        inputCombo.currentIndex = index
-                                        inputCombo.popup.close()
-                                        virtualstudio.inputDevice = modelData.text
-                                        if (modelData.category === "Low-Latency (ASIO)") {
-                                            let outputComboIdx = outputCombo.model.findIndex(it => it.category === "Low-Latency (ASIO)" && it.text === modelData.text);
-                                            if (outputComboIdx !== null && outputComboIdx !== undefined) {
-                                                outputCombo.currentIndex = outputComboIdx;
-                                                virtualstudio.outputDevice = modelData.text
-                                            }
-                                        }
-                                        virtualstudio.validateDevicesState()
-                                    }
-                                }
-                            }
-                        }
-                        contentItem: Text {
-                            leftPadding: 12
-                            font: inputCombo.font
-                            horizontalAlignment: Text.AlignHLeft
-                            verticalAlignment: Text.AlignVCenter
-                            elide: Text.ElideRight
-                            text: inputCombo.model[inputCombo.currentIndex] && inputCombo.model[inputCombo.currentIndex].text ? inputCombo.model[inputCombo.currentIndex].text : ""
-                        }
-                    }
-
-                    Text {
-                        id: inputChannelsLabel
-                        anchors.left: inputCombo.left
-                        anchors.right: inputCombo.horizontalCenter
-                        anchors.top: inputCombo.bottom
-                        anchors.topMargin: 12 * virtualstudio.uiScale
-                        textFormat: Text.RichText
-                        text: "Input Channel(s)"
-                        font { family: "Poppins"; pixelSize: fontTiny * virtualstudio.fontScale * virtualstudio.uiScale }
-                        color: textColour
-                    }
-
-                    ComboBox {
-                        id: inputChannelsCombo
-                        anchors.left: inputCombo.left
-                        anchors.right: inputCombo.horizontalCenter
-                        anchors.rightMargin: 8 * virtualstudio.uiScale
-                        anchors.top: inputChannelsLabel.bottom
-                        anchors.topMargin: 4 * virtualstudio.uiScale
-                        enabled: virtualstudio.connectionState == "Connected"
-                        model: virtualstudio.inputChannelsComboModel
-                        currentIndex: (() => {
-                            let idx = virtualstudio.inputChannelsComboModel.findIndex(elem => elem.baseChannel === virtualstudio.baseInputChannel
-                                && elem.numChannels === virtualstudio.numInputChannels);
-                            if (idx < 0) {
-                                idx = 0;
-                            }
-                            return idx;
-                        })()
-                        delegate: ItemDelegate {
-                            required property var modelData
-                            required property int index
-                            width: parent.width
-                            contentItem: Text {
-                                text: modelData.label
-                            }
-                            highlighted: inputChannelsCombo.highlightedIndex === index
-                            MouseArea {
-                                anchors.fill: parent
-                                onClicked: {
-                                    inputChannelsCombo.currentIndex = index
-                                    inputChannelsCombo.popup.close()
-                                    virtualstudio.baseInputChannel = modelData.baseChannel
-                                    virtualstudio.numInputChannels = modelData.numChannels
-                                    virtualstudio.validateDevicesState()
-                                }
-                            }
-                        }
-                        contentItem: Text {
-                            leftPadding: 12
-                            font: inputCombo.font
-                            horizontalAlignment: Text.AlignHLeft
-                            verticalAlignment: Text.AlignVCenter
-                            elide: Text.ElideRight
-                            text: inputChannelsCombo.model[inputChannelsCombo.currentIndex].label || ""
-                        }
-                    }
-
-                    Text {
-                        id: inputMixModeLabel
-                        anchors.left: inputCombo.horizontalCenter
-                        anchors.right: inputCombo.right
-                        anchors.rightMargin: 8 * virtualstudio.uiScale
-                        anchors.top: inputCombo.bottom
-                        anchors.topMargin: 12 * virtualstudio.uiScale
-                        textFormat: Text.RichText
-                        text: "Mono / Stereo"
-                        font { family: "Poppins"; pixelSize: fontTiny * virtualstudio.fontScale * virtualstudio.uiScale }
-                        color: textColour
-                    }
-
-                    ComboBox {
-                        id: inputMixModeCombo
-                        anchors.left: inputCombo.horizontalCenter
-                        anchors.right: inputCombo.right
-                        anchors.rightMargin: 8 * virtualstudio.uiScale
-                        anchors.top: inputMixModeLabel.bottom
-                        anchors.topMargin: 4 * virtualstudio.uiScale
-                        enabled: virtualstudio.connectionState == "Connected"
-                        model: virtualstudio.inputMixModeComboModel
-                        currentIndex: (() => {
-                            let idx = virtualstudio.inputMixModeComboModel.findIndex(elem => elem.value === virtualstudio.inputMixMode);
-                            if (idx < 0) {
-                                idx = 0;
-                            }
-                            return idx;
-                        })()
-                        delegate: ItemDelegate {
-                            required property var modelData
-                            required property int index
-                            width: parent.width
-                            contentItem: Text {
-                                text: modelData.label
-                            }
-                            highlighted: inputMixModeCombo.highlightedIndex === index
-                            MouseArea {
-                                anchors.fill: parent
-                                onClicked: {
-                                    inputMixModeCombo.currentIndex = index
-                                    inputMixModeCombo.popup.close()
-                                    virtualstudio.inputMixMode = virtualstudio.inputMixModeComboModel[index].value
-                                    virtualstudio.validateDevicesState()
-                                }
-                            }
-                        }
-                        contentItem: Text {
-                            leftPadding: 12
-                            font: inputCombo.font
-                            horizontalAlignment: Text.AlignHLeft
-                            verticalAlignment: Text.AlignVCenter
-                            elide: Text.ElideRight
-                            text: inputMixModeCombo.model[inputMixModeCombo.currentIndex].label || ""
-                        }
-                    }
-
-                    Text {
-                        id: inputChannelHelpMessage
-                        anchors.left: inputChannelsCombo.left
-                        anchors.leftMargin: 2 * virtualstudio.uiScale
-                        anchors.right: inputChannelsCombo.right
-                        anchors.top: inputChannelsCombo.bottom
-                        anchors.topMargin: 8 * virtualstudio.uiScale
-                        textFormat: Text.RichText
-                        wrapMode: Text.WordWrap
-                        text: "Choose up to 2 channels"
-                        font { family: "Poppins"; pixelSize: fontTiny * virtualstudio.fontScale * virtualstudio.uiScale }
-                        color: textColour
-                    }
-
-                    Text {
-                        id: inputMixModeHelpMessage
-                        anchors.left: inputMixModeCombo.left
-                        anchors.leftMargin: 2 * virtualstudio.uiScale
-                        anchors.right: inputMixModeCombo.right
-                        anchors.top: inputMixModeCombo.bottom
-                        anchors.topMargin: 8 * virtualstudio.uiScale
-                        textFormat: Text.RichText
-                        wrapMode: Text.WordWrap
-                        text: (() => {
-                            if (virtualstudio.inputMixMode === 2) {
-                                return "Treat the channels as Left and Right signals, coming through each speaker separately.";
-                            } else if (virtualstudio.inputMixMode === 3) {
-                                return "Combine the channels into one central channel coming through both speakers.";
-                            } else if (virtualstudio.inputMixMode === 1) {
-                                return "Send a single channel of audio";
-                            } else {
-                                return "";
-                            }
-                        })()
-                        font { family: "Poppins"; pixelSize: fontTiny * virtualstudio.fontScale * virtualstudio.uiScale }
-                        color: textColour
-                    }
-
-                    Button {
-                        id: closePopupButton
-                        anchors.right: parent.right
-                        anchors.rightMargin: rightMargin * virtualstudio.uiScale
-                        anchors.bottomMargin: rightMargin * virtualstudio.uiScale
-                        anchors.bottom: parent.bottom
-                        width: 150 * virtualstudio.uiScale; height: 30 * virtualstudio.uiScale
-                        onClicked: popup.close()
-
-                        background: Rectangle {
-                            radius: 6 * virtualstudio.uiScale
-                            color: closePopupButton.down ? browserButtonPressedColour : (closePopupButton.hovered ? browserButtonHoverColour : browserButtonColour)
-                            border.width: 1
-                            border.color: closePopupButton.down ? browserButtonPressedStroke : (closePopupButton.hovered ? browserButtonHoverStroke : browserButtonStroke)
-                        }
-
-                        Text {
-                            text: "Close"
-                            font.family: "Poppins"
-                            font.pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale
-                            font.weight: Font.Bold
-                            color: !Boolean(virtualstudio.devicesError) && virtualstudio.backendAvailable ? saveButtonText : disabledButtonText
-                            anchors.horizontalCenter: parent.horizontalCenter
-                            anchors.verticalCenter: parent.verticalCenter
-                        }
-                    }
-
-                    Button {
-                        id: refreshButton
-                        text: "Refresh Devices"
-                        anchors.right: closePopupButton.left
-                        anchors.rightMargin: 8 * virtualstudio.uiScale
-                        anchors.bottomMargin: rightMargin * virtualstudio.uiScale
-                        anchors.bottom: parent.bottom
-                        width: 150 * virtualstudio.uiScale; height: 30 * virtualstudio.uiScale
-                        enabled: virtualstudio.connectionState == "Connected"
-
-                        palette.buttonText: textColour
-                        background: Rectangle {
-                            radius: 6 * virtualstudio.uiScale
-                            color: refreshButton.down ? browserButtonPressedColour : (refreshButton.hovered ? browserButtonHoverColour : browserButtonColour)
-                            border.width: 1
-                            border.color: refreshButton.down ? browserButtonPressedStroke : (refreshButton.hovered ? browserButtonHoverStroke : browserButtonStroke)
-                        }
-
-                        icon {
-                            source: "refresh.svg";
-                            color: textColour;
-                        }
-                        display: AbstractButton.TextBesideIcon
-                        onClicked: {
-                            virtualstudio.validateDevicesState();
-                        }
-
-                        font {
-                            family: "Poppins"
-                            pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale
-                        }
-                    }
-                }
-            }
-        }
-
-        Popup {
-            id: feedbackDetectedModal
-            padding: 1
-            width: parent.width
-            height: 232 * virtualstudio.uiScale
-            anchors.centerIn: parent
-            modal: true
-            focus: true
-            closePolicy: Popup.NoAutoClose
-
-            background: Rectangle {
-                anchors.fill: parent
-                color: "transparent"
-                radius: 6 * virtualstudio.uiScale
-                border.width: 1
-                border.color: buttonStroke
-                clip: true
-            }
-
-            contentItem: Rectangle {
-                width: parent.width
-                height: 232 * virtualstudio.uiScale
-                color: backgroundColour
-                radius: 6 * virtualstudio.uiScale
-
-                Item {
-                    id: feedbackDetectedContent
-                    anchors.top: parent.top
-                    anchors.topMargin: 24 * virtualstudio.uiScale
-                    anchors.bottom: parent.bottom
-                    anchors.left: parent.left
-                    anchors.leftMargin: 24 * virtualstudio.uiScale
-                    anchors.right: parent.right
-
-                    Text {
-                        id: feedbackDetectedHeader
-                        anchors.top: parent.top
-                        anchors.topMargin: 16 * virtualstudio.uiScale
-                        width: parent.width
-                        text: "Audio feedback detected!"
-                        font {family: "Poppins"; pixelSize: fontMedium * virtualstudio.fontScale * virtualstudio.uiScale; bold: true }
-                        color: textColour
-                        elide: Text.ElideRight
-                        wrapMode: Text.WordWrap
-                    }
-
-                    Text {
-                        id: feedbackDetectedText
-                        anchors.top: feedbackDetectedHeader.bottom
-                        anchors.topMargin: 16 * virtualstudio.uiScale
-                        width: parent.width
-                        text: "JackTrip detected a feedback loop. Your monitor and input volume have automatically been disabled."
-                        font {family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale }
-                        color: textColour
-                        elide: Text.ElideRight
-                        wrapMode: Text.WordWrap
-                    }
-
-                    Text {
-                        id: feedbackDetectedText2
-                        anchors.top: feedbackDetectedText.bottom
-                        anchors.topMargin: 16 * virtualstudio.uiScale
-                        width: parent.width
-                        text: "You can disable this behavior under <b>Settings</b> > <b>Advanced</b>"
-                        textFormat: Text.RichText
-                        font {family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale }
-                        color: textColour
-                        elide: Text.ElideRight
-                        wrapMode: Text.WordWrap
-                    }
-
-                    Button {
-                        id: closeFeedbackDetectedModalButton
-                        anchors.horizontalCenter: parent.horizontalCenter
-                        anchors.bottomMargin: rightMargin * virtualstudio.uiScale
-                        anchors.bottom: parent.bottom
-                        width: 150 * virtualstudio.uiScale; height: 30 * virtualstudio.uiScale
-                        onClicked: feedbackDetectedModal.close()
-
-                        background: Rectangle {
-                            radius: 6 * virtualstudio.uiScale
-                            color: closeFeedbackDetectedModalButton.down ? browserButtonPressedColour : (closeFeedbackDetectedModalButton.hovered ? browserButtonHoverColour : browserButtonColour)
-                            border.width: 1
-                            border.color: closeFeedbackDetectedModalButton.down ? browserButtonPressedStroke : (closeFeedbackDetectedModalButton.hovered ? browserButtonHoverStroke : browserButtonStroke)
-                        }
-
-                        Text {
-                            text: "Ok"
-                            font.family: "Poppins"
-                            font.pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale
-                            font.weight: Font.Bold
-                            color: !Boolean(virtualstudio.devicesError) && virtualstudio.backendAvailable ? saveButtonText : disabledButtonText
-                            anchors.horizontalCenter: parent.horizontalCenter
-                            anchors.verticalCenter: parent.verticalCenter
-                        }
-                    }
-
-                }
-            }
-        }
-    }
-
-    Item {
-        id: inputDevice
-        visible: showReadyScreen
-        x: bodyMargin * virtualstudio.uiScale; y: 240 * virtualstudio.uiScale
-        width: Math.min(parent.width / 2, 320 * virtualstudio.uiScale) - x
-        height: 100 * virtualstudio.uiScale
-        clip: true
-
-        Image {
-            id: mic
-            source: "mic.svg"
-            x: 0; y: 0
-            width: 28 * virtualstudio.uiScale; height: 28 * virtualstudio.uiScale
-            sourceSize: Qt.size(mic.width,mic.height)
-            fillMode: Image.PreserveAspectFit
-            smooth: true
-        }
-
-        Colorize {
-            anchors.fill: mic
-            source: mic
-            hue: 0
-            saturation: 0
-            lightness: imageLightnessValue
-        }
-
-        Text {
-            id: inputDeviceHeader
-            x: 64 * virtualstudio.uiScale
-            width: parent.width - 64 * virtualstudio.uiScale
-            text: "<b>Input Device</b>"
-            font {family: "Poppins"; pixelSize: fontMedium * virtualstudio.fontScale * virtualstudio.uiScale }
-            anchors.verticalCenter: mic.verticalCenter
-            color: textColour
-            elide: Text.ElideRight
-        }
-
-        Text {
-            id: inputDeviceName
-            width: parent.width - 100 * virtualstudio.uiScale
-            anchors.top: inputDeviceHeader.bottom
-            anchors.left: inputDeviceHeader.left
-            text: virtualstudio.audioBackend == "JACK" ?
-                virtualstudio.audioBackend : virtualstudio.inputDevice
-            font {family: "Poppins"; pixelSize: fontTiny * virtualstudio.fontScale * virtualstudio.uiScale }
-            color: textColour
-            elide: Text.ElideRight
-        }
-    }
-
-    Item {
-        id: outputDevice
-        visible: showReadyScreen
-        x: bodyMargin * virtualstudio.uiScale; y: 320 * virtualstudio.uiScale
-        width: Math.min(parent.width / 2, 320 * virtualstudio.uiScale) - x
-        height: 124 * virtualstudio.uiScale
-        clip: true
-        anchors.top: inputDevice.bottom
-
-        Image {
-            id: headphones
-            source: "headphones.svg"
-            x: 0; y: 0
-            width: 28 * virtualstudio.uiScale; height: 28 * virtualstudio.uiScale
-            sourceSize: Qt.size(headphones.width,headphones.height)
-            fillMode: Image.PreserveAspectFit
-            smooth: true
-        }
-
-        Colorize {
-            anchors.fill: headphones
-            source: headphones
-            hue: 0
-            saturation: 0
-            lightness: imageLightnessValue
-        }
-
-        Text {
-            id: outputDeviceHeader
-            x: 64 * virtualstudio.uiScale
-            width: parent.width - 64 * virtualstudio.uiScale
-            text: "<b>Output Device</b>"
-            font {family: "Poppins"; pixelSize: fontMedium * virtualstudio.fontScale * virtualstudio.uiScale }
-            anchors.verticalCenter: headphones.verticalCenter
-            color: textColour
-            elide: Text.ElideRight
-        }
-
-        Text {
-            id: outputDeviceName
-            width: parent.width - 100 * virtualstudio.uiScale
-            anchors.top: outputDeviceHeader.bottom
-            anchors.left: outputDeviceHeader.left
-            text: virtualstudio.audioBackend == "JACK" ?
-                virtualstudio.audioBackend : virtualstudio.outputDevice
-            font {family: "Poppins"; pixelSize: fontTiny * virtualstudio.fontScale * virtualstudio.uiScale }
-            color: textColour
-            elide: Text.ElideRight
-        }
-    }
-
-    Item {
-        id: inputControls
-        visible: showReadyScreen
-        x: inputDevice.x + inputDevice.width; y: 240 * virtualstudio.uiScale
-        width: parent.width - inputDevice.width - 2 * bodyMargin * virtualstudio.uiScale
-
-        Meter {
-            id: inputDeviceMeters
-            x: 0; y: 0
-            width: parent.width
-            height: 100 * virtualstudio.uiScale
-            model: virtualstudio.inputMeterLevels
-            clipped: virtualstudio.inputClipped
-        }
-
-        Slider {
-            id: inputSlider
-            from: 0.0
-            value: virtualstudio ? virtualstudio.inputVolume : 0.5
-            onMoved: { virtualstudio.inputVolume = value }
-            to: 1.0
-            enabled: !virtualstudio.inputMuted
-            padding: 0
-            y: inputDeviceMeters.y + 36 * virtualstudio.uiScale
-            anchors.left: inputMute.right
-            anchors.right: inputStudioText.left
-            anchors.leftMargin: 8 * virtualstudio.uiScale
-            anchors.rightMargin: 16 * virtualstudio.uiScale
-            opacity: virtualstudio.inputMuted ? 0.3 : 1
-
-            background: Rectangle {
-                x: inputSlider.leftPadding
-                y: inputSlider.topPadding + inputSlider.availableHeight / 2 - height / 2
-                implicitWidth: parent.width
-                implicitHeight: 6
-                width: inputSlider.availableWidth
-                height: implicitHeight
-                radius: 4
-                color: sliderTrackColour
-
-                Rectangle {
-                    width: inputSlider.visualPosition * parent.width
-                    height: parent.height
-                    color: sliderActiveTrackColour
-                    radius: 4
-                }
-            }
-
-            handle: Rectangle {
-                x: inputSlider.leftPadding + inputSlider.visualPosition * (inputSlider.availableWidth - width)
-                y: inputSlider.topPadding + inputSlider.availableHeight / 2 - height / 2
-                implicitWidth: 26 * virtualstudio.uiScale
-                implicitHeight: 26 * virtualstudio.uiScale
-                radius: 13 * virtualstudio.uiScale
-                color: inputSlider.pressed ? sliderPressedColour : sliderColour
-                border.color: buttonStroke
-                opacity: virtualstudio.inputMuted ? 0.3 : 1
-            }
-        }
-
-        Button {
-            id: inputMute
-            width: 24 * virtualstudio.uiScale
-            height: 24
-            anchors.left: inputDeviceMeters.left
-            anchors.verticalCenter: inputDeviceMeters.verticalCenter
-            background: Rectangle {
-                color: virtualstudio.inputMuted ? muteButtonMutedColor : buttonColour
-                width: 24 * virtualstudio.uiScale
-                radius: 4 * virtualstudio.uiScale
-            }
-            onClicked: { virtualstudio.inputMuted = !virtualstudio.inputMuted }
-            Image {
-                id: micMute
-                width: 18 * virtualstudio.uiScale; height: 18 * virtualstudio.uiScale
-                anchors { verticalCenter: parent.verticalCenter; horizontalCenter: parent.horizontalCenter }
-                source: virtualstudio.inputMuted ? "micoff.svg" : "mic.svg"
-                sourceSize: Qt.size(micMute.width,micMute.height)
-                fillMode: Image.PreserveAspectFit
-                smooth: true
-            }
-            Colorize {
-                anchors.fill: micMute
-                source: micMute
-                hue: 0
-                saturation: virtualstudio.inputMuted ? muteButtonMutedSaturationValue : 0
-                lightness: virtualstudio.inputMuted ? (inputMute.hovered ? muteButtonMutedLightnessValue + .1 : muteButtonMutedLightnessValue) : (inputMute.hovered ? muteButtonLightnessValue - .1 : muteButtonLightnessValue)
-            }
-            ToolTip {
-                parent: inputMute
-                visible: inputMute.hovered
-                bottomPadding: bottomToolTipMargin * virtualstudio.uiScale
-                rightPadding: rightToolTipMargin * virtualstudio.uiScale
-                delay: 100
-                contentItem: Rectangle {
-                    color: toolTipBackgroundColour
-                    radius: 3
-                    anchors.fill: parent
-                    anchors.bottomMargin: bottomToolTipMargin * virtualstudio.uiScale
-                    anchors.rightMargin: rightToolTipMargin * virtualstudio.uiScale
-                    layer.enabled: true
-                    border.width: 1
-                    border.color: buttonStroke
-
-                    Text {
-                        anchors.centerIn: parent
-                        font { family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale}
-                        text: virtualstudio.inputMuted ?  qsTr("Click to unmute yourself") : qsTr("Click to mute yourself")
-                        color: toolTipTextColour
-                    }
-                }
-                background: Rectangle {
-                    color: "transparent"
-                }
-            }
-        }
-
-        Text {
-            id: inputStudioText
-            width: 40 * virtualstudio.uiScale
-            height: 24
-            horizontalAlignment: Text.AlignRight
-            anchors.right: inputDeviceMeters.right
-            anchors.verticalCenter: inputSlider.verticalCenter
-            topPadding: 4 * virtualstudio.uiScale
-            rightPadding: 4 * virtualstudio.uiScale
-            text: "Send"
-            font {family: "Poppins"; pixelSize: fontTiny * virtualstudio.fontScale * virtualstudio.uiScale; bold: true }
-            color: textColour
-        }
-
-        Image {
-            id: inputStudioHelpIcon
-            anchors.left: inputStudioText.right
-            anchors.verticalCenter: inputStudioText.verticalCenter
-            anchors.bottomMargin: -8 * virtualstudio.uiScale
-            source: "help.svg"
-            sourceSize: Qt.size(12 * virtualstudio.uiScale, 12 * virtualstudio.uiScale)
-            fillMode: Image.PreserveAspectFit
-            smooth: true
-
-            property bool showToolTip: false
-
-            MouseArea {
-                id: inputStudioMouseArea
-                anchors.fill: parent
-                hoverEnabled: true
-                onEntered: inputStudioHelpIcon.showToolTip = true
-                onExited: inputStudioHelpIcon.showToolTip = false
-            }
-
-            ToolTip {
-                visible: inputStudioHelpIcon.showToolTip
-                contentItem: Rectangle {
-                    color: toolTipBackgroundColour
-                    radius: 3
-                    anchors.fill: parent
-                    anchors.bottomMargin: bottomToolTipMargin * virtualstudio.uiScale
-                    anchors.rightMargin: rightToolTipMargin * virtualstudio.uiScale
-                    layer.enabled: true
-                    border.width: 1
-                    border.color: buttonStroke
-
-                    Text {
-                        anchors.centerIn: parent
-                        font { family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale}
-                        text: qsTr("How loudly other participants hear you")
-                        color: toolTipTextColour
-                    }
-                }
-                background: Rectangle {
-                    color: "transparent"
-                }
-            }
-        }
-
-        Colorize {
-            anchors.fill: inputStudioHelpIcon
-            source: inputStudioHelpIcon
-            hue: 0
-            saturation: 0
-            lightness: imageLightnessValue
-        }
-    }
-
-    Item {
-        id: outputControls
-        visible: showReadyScreen
-        x: outputDevice.x + outputDevice.width; y: 320 * virtualstudio.uiScale
-        width: parent.width - inputDevice.width - 2 * bodyMargin * virtualstudio.uiScale
-        anchors.top: inputDevice.bottom
-
-        Meter {
-            id: outputDeviceMeters
-            x: 0; y: 0
-            width: parent.width
-            height: 100 * virtualstudio.uiScale
-            model: virtualstudio.outputMeterLevels
-            clipped: virtualstudio.outputClipped
-        }
-
-        Slider {
-            id: outputSlider
-            from: 0.0
-            value: virtualstudio ? virtualstudio.outputVolume : 0.5
-            onMoved: { virtualstudio.outputVolume = value }
-            to: 1.0
-            padding: 0
-            y: outputDeviceMeters.y + 36 * virtualstudio.uiScale
-            anchors.left: outputDeviceMeters.left
-            anchors.right: outputStudioText.left
-            anchors.rightMargin: 16 * virtualstudio.uiScale
-
-            background: Rectangle {
-                x: outputSlider.leftPadding
-                y: outputSlider.topPadding + outputSlider.availableHeight / 2 - height / 2
-                implicitWidth: parent.width
-                implicitHeight: 6
-                width: outputSlider.availableWidth
-                height: implicitHeight
-                radius: 4
-                color: sliderTrackColour
-
-                Rectangle {
-                    width: outputSlider.visualPosition * parent.width
-                    height: parent.height
-                    color: sliderActiveTrackColour
-                    radius: 4
-                }
-            }
-
-            handle: Rectangle {
-                x: outputSlider.leftPadding + outputSlider.visualPosition * (outputSlider.availableWidth - width)
-                y: outputSlider.topPadding + outputSlider.availableHeight / 2 - height / 2
-                implicitWidth: 26 * virtualstudio.uiScale
-                implicitHeight: 26 * virtualstudio.uiScale
-                radius: 13 * virtualstudio.uiScale
-                color: outputSlider.pressed ? sliderPressedColour : sliderColour
-                border.color: buttonStroke
-            }
-        }
-
-        Slider {
-            id: monitorSlider
-            from: 0.0
-            value: virtualstudio ? virtualstudio.monitorVolume : 0.5
-            onMoved: { virtualstudio.monitorVolume = value }
-            to: 1.0
-            padding: 0
-            y: outputSlider.y + 36 * virtualstudio.uiScale
-            anchors.left: outputDeviceMeters.left
-            anchors.right: outputMonText.left
-            anchors.rightMargin: 16 * virtualstudio.uiScale
-
-            background: Rectangle {
-                x: monitorSlider.leftPadding
-                y: monitorSlider.topPadding + monitorSlider.availableHeight / 2 - height / 2
-                implicitWidth: parent.width
-                implicitHeight: 6
-                width: monitorSlider.availableWidth
-                height: implicitHeight
-                radius: 4
-                color: sliderTrackColour
-
-                Rectangle {
-                    width: monitorSlider.visualPosition * parent.width
-                    height: parent.height
-                    color: sliderActiveTrackColour
-                    radius: 4
-                }
-            }
-
-            handle: Rectangle {
-                x: monitorSlider.leftPadding + monitorSlider.visualPosition * (monitorSlider.availableWidth - width)
-                y: monitorSlider.topPadding + monitorSlider.availableHeight / 2 - height / 2
-                implicitWidth: 26 * virtualstudio.uiScale
-                implicitHeight: 26 * virtualstudio.uiScale
-                radius: 13 * virtualstudio.uiScale
-                color: monitorSlider.pressed ? sliderPressedColour : sliderColour
-                border.color: buttonStroke
-            }
-        }
-
-        Text {
-            id: outputStudioText
-            width: 40 * virtualstudio.uiScale
-            height: 24
-            horizontalAlignment: Text.AlignRight
-            anchors.right: outputDeviceMeters.right
-            anchors.verticalCenter: outputSlider.verticalCenter
-            topPadding: 4 * virtualstudio.uiScale
-            rightPadding: 4 * virtualstudio.uiScale
-            text: "Studio"
-            font {family: "Poppins"; pixelSize: fontTiny * virtualstudio.fontScale * virtualstudio.uiScale; bold: true }
-            color: textColour
-        }
-
-        Image {
-            id: outputStudioHelpIcon
-            anchors.left: outputStudioText.right
-            anchors.verticalCenter: outputStudioText.verticalCenter
-            anchors.bottomMargin: -8 * virtualstudio.uiScale
-            source: "help.svg"
-            sourceSize: Qt.size(12 * virtualstudio.uiScale, 12 * virtualstudio.uiScale)
-            fillMode: Image.PreserveAspectFit
-            smooth: true
-
-            property bool showToolTip: false
+    Loader {
+        id: studioWebLoader
+        anchors.top: parent.top
+        anchors.right: parent.right
+        anchors.left: parent.left
+        anchors.bottom: deviceControlsGroup.top
 
-            MouseArea {
-                id: outputStudioMouseArea
-                anchors.fill: parent
-                hoverEnabled: true
-                onEntered: outputStudioHelpIcon.showToolTip = true
-                onExited: outputStudioHelpIcon.showToolTip = false
-            }
+        property string accessToken: auth.isAuthenticated && Boolean(auth.accessToken) ? auth.accessToken : ""
+        property string studioId: virtualstudio.currentStudio.id
 
-            ToolTip {
-                visible: outputStudioHelpIcon.showToolTip
-                contentItem: Rectangle {
-                    color: toolTipBackgroundColour
-                    radius: 3
-                    anchors.fill: parent
-                    anchors.bottomMargin: bottomToolTipMargin * virtualstudio.uiScale
-                    anchors.rightMargin: rightToolTipMargin * virtualstudio.uiScale
-                    layer.enabled: true
-                    border.width: 1
-                    border.color: buttonStroke
-
-                    Text {
-                        anchors.centerIn: parent
-                        font { family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale}
-                        text: qsTr("How loudly you hear other participants")
-                        color: toolTipTextColour
-                    }
-                }
-                background: Rectangle {
-                    color: "transparent"
-                }
-            }
-        }
-
-        Colorize {
-            anchors.fill: outputStudioHelpIcon
-            source: outputStudioHelpIcon
-            hue: 0
-            saturation: 0
-            lightness: imageLightnessValue
-        }
-
-        Text {
-            id: outputMonText
-            width: 40 * virtualstudio.uiScale
-            height: 24
-            horizontalAlignment: Text.AlignRight
-            anchors.right: outputDeviceMeters.right
-            anchors.verticalCenter: monitorSlider.verticalCenter
-            topPadding: 4 * virtualstudio.uiScale
-            rightPadding: 4 * virtualstudio.uiScale
-            text: "Monitor"
-            font {family: "Poppins"; pixelSize: fontTiny * virtualstudio.fontScale * virtualstudio.uiScale; bold: true }
-            color: textColour
-        }
-
-        Image {
-            id: outputMonHelpIcon
-            anchors.left: outputMonText.right
-            anchors.verticalCenter: outputMonText.verticalCenter
-            anchors.bottomMargin: -8 * virtualstudio.uiScale
-            source: "help.svg"
-            sourceSize: Qt.size(12 * virtualstudio.uiScale, 12 * virtualstudio.uiScale)
-            fillMode: Image.PreserveAspectFit
-            smooth: true
-
-            property bool showToolTip: false
-
-            MouseArea {
-                id: outputMonMouseArea
-                anchors.fill: parent
-                hoverEnabled: true
-                onEntered: outputMonHelpIcon.showToolTip = true
-                onExited: outputMonHelpIcon.showToolTip = false
-            }
-
-            ToolTip {
-                visible: outputMonHelpIcon.showToolTip
-                contentItem: Rectangle {
-                    color: toolTipBackgroundColour
-                    radius: 3
-                    anchors.fill: parent
-                    anchors.bottomMargin: bottomToolTipMargin * virtualstudio.uiScale
-                    anchors.rightMargin: rightToolTipMargin * virtualstudio.uiScale
-                    layer.enabled: true
-                    border.width: 1
-                    border.color: buttonStroke
-
-                    Text {
-                        anchors.centerIn: parent
-                        font { family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale}
-                        text: qsTr("How loudly you hear yourself")
-                        color: toolTipTextColour
-                    }
-                }
-                background: Rectangle {
-                    color: "transparent"
-                }
-            }
-        }
-
-        Colorize {
-            anchors.fill: outputMonHelpIcon
-            source: outputMonHelpIcon
-            hue: 0
-            saturation: 0
-            lightness: imageLightnessValue
-        }
+        source: accessToken && studioId ? "Web.qml" : "WebNull.qml"
     }
 
-    Item {
-        id: networkStatsHeader
-        visible: showReadyScreen
-        x: bodyMargin * virtualstudio.uiScale; y: 450 * virtualstudio.uiScale
-        width: Math.min(parent.width / 2, 320 * virtualstudio.uiScale) - x
-        height: 128 * virtualstudio.uiScale
-        anchors.top: outputDevice.bottom
-
-        Image {
-            id: network
-            source: "network.svg"
-            x: 0; y: 0
-            width: 28 * virtualstudio.uiScale; height: 28 * virtualstudio.uiScale
-            sourceSize: Qt.size(network.width,network.height)
-            fillMode: Image.PreserveAspectFit
-            smooth: true
-        }
-
-        Colorize {
-            anchors.fill: network
-            source: network
-            hue: 0
-            saturation: 0
-            lightness: imageLightnessValue
-        }
-
-        Text {
-            id: networkStatsHeaderText
-            text: "<b>Network</b>"
-            font {family: "Poppins"; pixelSize: fontMedium * virtualstudio.fontScale * virtualstudio.uiScale }
-            x: 64 * virtualstudio.uiScale
-            anchors.verticalCenter: network.verticalCenter
-            color: textColour
-        }
+    DeviceControlsGroup {
+        id: deviceControlsGroup
+        showMinified: false
+        anchors.bottom: footer.top
     }
 
-    Item {
-        id: networkStatsText
-        visible: showReadyScreen
-        x: networkStatsHeader.x + networkStatsHeader.width; y: 450 * virtualstudio.uiScale
-        width: parent.width - networkStatsHeader.width - 2 * bodyMargin * virtualstudio.uiScale
-        height: 72 * virtualstudio.uiScale
-        anchors.top: outputDevice.bottom
-
-        Text {
-            id: netstat0
-            x: 0; y: 0
-            text: getNetworkStatsText()[0]
-            font {family: "Poppins"; pixelSize: fontTiny * virtualstudio.fontScale * virtualstudio.uiScale }
-            color: textColour
-        }
-
-        Text {
-            id: netstat1
-            x: 0
-            text: getNetworkStatsText()[1]
-            font {family: "Poppins"; pixelSize: fontTiny * virtualstudio.fontScale * virtualstudio.uiScale }
-            topPadding: 8 * virtualstudio.uiScale
-            anchors.top: netstat0.bottom
-            color: textColour
-        }
-    }
-
-    Item {
-        id: devicesWarning
-        visible: showReadyScreen && Boolean(virtualstudio.devicesWarning)
-        x: bodyMargin * virtualstudio.uiScale
-        width: parent.width - (2 * x)
-        anchors.top: networkStatsText.bottom
-        anchors.topMargin: 12 * virtualstudio.uiScale
-
-        Text {
-            x: 0; y: 0
-            width: devicesWarning.width
-            textFormat: Text.RichText
-            text: (virtualstudio.devicesWarning)
-                + ((virtualstudio.devicesWarningHelpUrl)
-                    ? `&nbsp;<a style="color: ${linkText};" href=${virtualstudio.devicesWarningHelpUrl}>Learn More.</a>`
-                    : ""
-                )
-            onLinkActivated: link => {
-                virtualstudio.openLink(link)
-            }
-            horizontalAlignment: Text.AlignHLeft
-            wrapMode: Text.WordWrap
-            font {family: "Poppins"; pixelSize: fontTiny * virtualstudio.fontScale * virtualstudio.uiScale }
-            color: warningTextColour
-        }
-    }
-
-    Item {
-        id: waitingScreen
-        visible: showWaitingScreen
-        x: bodyMargin * virtualstudio.uiScale; y: 230 * virtualstudio.uiScale
-        width: parent.width - (2 * x)
-
-        property bool isAdmin: (virtualstudio.currentStudio >= 0 ? serverModel[virtualstudio.currentStudio].isAdmin : false)
-
-        Text {
-            id: waitingText0
-            x: 0
-            width: parent.width
-            color: textColour
-            font {family: "Poppins"; pixelSize: fontMedium * virtualstudio.fontScale * virtualstudio.uiScale }
-            text: parent.isAdmin
-                    ? "Waiting for this studio to start. Please start the studio using one of the options below."
-                    : "This studio is currently inactive. Please contact an owner or admin for this studio to start it."
-            wrapMode: Text.WordWrap
-        }
-
-        Item {
-            id: startButtonsBox
-            anchors.top: waitingText0.bottom
-            anchors.topMargin: 16 * virtualstudio.uiScale
-            anchors.bottomMargin: 16 * virtualstudio.uiScale
-            visible: parent.isAdmin
-
-            height: 64 * virtualstudio.uiScale
-
-            Button {
-                id: startStudioNowButton
-                anchors.verticalCenter: startButtonsBox.verticalCenter
-                x: 0
-                onClicked: {
-                    virtualstudio.manageStudio(-1, true)
-                }
-
-                width: 210 * virtualstudio.uiScale; height: 45 * virtualstudio.uiScale
-                background: Rectangle {
-                    radius: 6 * virtualstudio.uiScale
-                    color: startStudioNowButton.down ? browserButtonPressedColour : (startStudioNowButton.hovered ? browserButtonHoverColour : browserButtonColour)
-                    border.width: 1
-                    border.color: startStudioNowButton.down ? browserButtonPressedStroke : (startStudioNowButton.hovered ? browserButtonHoverStroke : browserButtonStroke)
-                }
-
-                Text {
-                    text: "Start Studio"
-                    font.family: "Poppins"
-                    font.pixelSize: fontMedium * virtualstudio.fontScale * virtualstudio.uiScale
-                    anchors.horizontalCenter: parent.horizontalCenter
-                    anchors.verticalCenter: parent.verticalCenter
-                    color: textColour
-                }
-            }
-
-            Text {
-                id: startStudioInBrowserText
-                anchors.verticalCenter: startStudioNowButton.verticalCenter
-                anchors.left: startStudioNowButton.right
-                anchors.leftMargin: 24 * virtualstudio.uiScale
-                width: 240 * virtualstudio.uiScale
-                textFormat: Text.RichText
-                text:`<a style="color: ${textColour};" href="https://${virtualstudio.apiHost}/studios/${virtualstudio.currentStudio >= 0 ? serverModel[virtualstudio.currentStudio].id : ""}/live?start=true">Change Settings and Start</a>`
-
-                onLinkActivated: link => {
-                    virtualstudio.openLink(link)
-                }
-                horizontalAlignment: Text.AlignHLeft
-                wrapMode: Text.WordWrap
-                font { family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale }
-            }
-        }
-
-        Text {
-            id: waitingText1
-            x: 0
-            width: parent.width
-            color: textColour
-            anchors.top: parent.isAdmin ? startButtonsBox.bottom : waitingText0.bottom
-            anchors.topMargin: 16 * virtualstudio.uiScale
-            anchors.bottomMargin: 16 * virtualstudio.uiScale
-            visible: parent.isAdmin
-            font {family: "Poppins"; pixelSize: fontMedium * virtualstudio.fontScale * virtualstudio.uiScale }
-            text: "You will be automatically connected to the studio when it is ready."
-            wrapMode: Text.WordWrap
-        }
-
-        Text {
-            id: connectedErrorMessage1
-            x: 0
-            width: parent.width
-            color: warningTextColour
-            anchors.top: waitingText1.bottom
-            anchors.topMargin: 16 * virtualstudio.uiScale
-            anchors.bottomMargin: 16 * virtualstudio.uiScale
-            visible: parent.isAdmin && Boolean(virtualstudio.connectedErrorMsg)
-            textFormat: Text.RichText
-            text: virtualstudio.connectedErrorMsg == "one-studio-limit-reached"
-                ? `Your current plan allows you to use 1 studio at a time. <a style="color: ${linkText}; cursor: pointer" href="https://help.jacktrip.org/hc/en-us/requests/new">Contact us</a> to use multiple studios at a time.`
-                : ""
-            onLinkActivated: link => {
-                virtualstudio.openLink(link)
-            }
-            font {family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale }
-            wrapMode: Text.WordWrap
-        }
-    }
-
-    Item {
-        id: studioStartingScreen
-        visible: showStartingScreen
-        x: bodyMargin * virtualstudio.uiScale; y: 230 * virtualstudio.uiScale
-        width: parent.width - (2 * x)
-
-        Text {
-            id: studioStartingText0
-            x: 0
-            width: parent.width
-            color: textColour
-            font {family: "Poppins"; pixelSize: fontMedium * virtualstudio.fontScale * virtualstudio.uiScale }
-            text: "This studio is currently starting up. You will be connected automatically when it is ready."
-            wrapMode: Text.WordWrap
-        }
-    }
-
-    Item {
-        id: studioStoppingScreen
-        visible: showStoppingScreen
-        x: bodyMargin * virtualstudio.uiScale; y: 230 * virtualstudio.uiScale
-        width: parent.width - (2 * x)
-
-        Text {
-            id: studioStoppingText0
-            x: 0
-            width: parent.width
-            color: textColour
-            font {family: "Poppins"; pixelSize: fontMedium * virtualstudio.fontScale * virtualstudio.uiScale }
-            text: "This studio is shutting down, please wait to start it again."
-            wrapMode: Text.WordWrap
-        }
+    Footer {
+        id: footer
+        anchors.bottom: parent.bottom
     }
 
     Connections {
         target: virtualstudio
 
-        function onFeedbackDetected() {
-            feedbackDetectedModal.visible = true;
+        // self-managed servers do not support minified controls so keep it full size
+        function onCollapseDeviceControlsChanged(collapseDeviceControls) {
+            deviceControlsGroup.showMinified = virtualstudio.currentStudio.isManaged && collapseDeviceControls;
         }
     }
 }
diff --git a/src/gui/DeviceControls.qml b/src/gui/DeviceControls.qml
new file mode 100644 (file)
index 0000000..8dd779b
--- /dev/null
@@ -0,0 +1,202 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import Qt5Compat.GraphicalEffects
+
+Item {
+    width: parent.width
+    height: parent.height
+    clip: true
+
+    required property bool isInput
+    property string muteButtonMutedColor: "#FCB6B6"
+    property string buttonColour: virtualstudio.darkMode ? "#494646" : "#D5D9D9"
+
+    Component {
+        id: controlIndicator
+
+        Button {
+            id: iconButton
+            width: 24 * virtualstudio.uiScale
+            height: 24 * virtualstudio.uiScale
+
+            anchors.left: parent.left
+            anchors.leftMargin: 8 * virtualstudio.uiScale
+            anchors.verticalCenter: parent.verticalCenter
+
+            background: Rectangle {
+                color: isInput ? (audio.inputMuted ? muteButtonMutedColor : buttonColour) : "transparent"
+                width: 24 * virtualstudio.uiScale
+                radius: 4 * virtualstudio.uiScale
+            }
+
+            onClicked: isInput ? audio.inputMuted = !audio.inputMuted : console.log()
+
+            AppIcon {
+                id: iconImage
+                anchors.centerIn: parent
+                width: 24 * virtualstudio.uiScale
+                height: 24 * virtualstudio.uiScale
+                icon.source: isInput ? (audio.inputMuted ? "micoff.svg" : "mic.svg") : "headphones.svg"
+                color: isInput ? (audio.inputMuted ? "red" : ( virtualstudio.darkMode ? "#CCCCCC" : "#333333" )) : (virtualstudio.darkMode ? "#CCCCCC" : "#333333")
+                onClicked: isInput ? audio.inputMuted = !audio.inputMuted : console.log()
+            }
+
+            ToolTip {
+                visible: isInput && iconButton.hovered
+                x: iconButton.x + iconButton.width
+                y: iconButton.y + iconButton.height
+
+                contentItem: Text {
+                    text: audio.inputMuted ? qsTr("Click to unmute yourself") : qsTr("Click to mute yourself")
+                    font { family: "Poppins"; pixelSize: fontTiny * virtualstudio.fontScale * virtualstudio.uiScale }
+                    color: textColour
+                }
+
+                background: Rectangle {
+                    color: toolTipBackgroundColour
+                    radius: 4
+                    layer.enabled: true
+                    layer.effect: Glow {
+                        color: "#66000000"
+                        transparentBorder: true
+                    }
+                }
+            }
+        }
+    }
+
+    Component {
+        id: inputControls
+        
+        ColumnLayout {
+            anchors.fill: parent
+            spacing: 2
+
+            VolumeSlider {
+                Layout.fillWidth: true
+                Layout.fillHeight: true
+                labelText: "Send"
+                tooltipText: "How loudly other participants hear you"
+                sliderEnabled: !audio.inputMuted
+            }
+
+            DeviceWarning {
+                id: deviceWarning
+                visible: Boolean(audio.devicesError) || Boolean(audio.devicesWarning)
+            }
+        }
+    }
+
+    Component {
+        id: outputControls
+
+        ColumnLayout {
+            anchors.fill: parent
+            spacing: 2
+
+            VolumeSlider {
+                Layout.fillWidth: true
+                Layout.fillHeight: true
+                labelText: "Studio"
+                tooltipText: "How loudly you hear other participants"
+                sliderEnabled: true
+            }
+
+            VolumeSlider {
+                Layout.fillWidth: true
+                Layout.fillHeight: true
+                labelText: "Monitor"
+                tooltipText: "How loudly you hear yourself"
+                sliderEnabled: true
+            }
+        }
+    }
+
+    ColumnLayout {
+        anchors.fill: parent
+        spacing: 2
+
+        Item {
+            Layout.preferredHeight: minifiedHeight
+            Layout.fillWidth: true
+
+            RowLayout {
+                anchors.fill: parent
+                spacing: 8
+
+                Item {
+                    Layout.fillHeight: true
+                    Layout.preferredWidth: 100
+
+                    Loader {
+                        id: typeIconIndicator
+                        anchors.left: parent.left
+                        anchors.verticalCenter: parent.verticalCenter
+                        sourceComponent: controlIndicator
+                    }
+
+                    Text {
+                        id: label
+                        anchors.left: parent.left
+                        anchors.leftMargin: 36 * virtualstudio.uiScale
+                        anchors.verticalCenter: parent.verticalCenter
+
+                        text: isInput ? "Input" : "Output"
+                        font { family: "Poppins"; weight: Font.Bold; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale }
+                        color: textColour
+                    }
+
+                    InfoTooltip {
+                        content: isInput ? qsTr("Audio sent to the studio (microphone, instrument, mixer, etc.)") : qsTr("How you'll hear the studio audio")
+                        size: 16
+                        anchors.left: label.right
+                        anchors.leftMargin: 2 * virtualstudio.uiScale
+                        anchors.verticalCenter: label.verticalCenter
+                    }
+                }
+
+                Item {
+                    Layout.fillHeight: true
+                    Layout.fillWidth: true
+                    Layout.preferredWidth: 200
+
+                    Meter {
+                        anchors.fill: parent
+                        anchors.topMargin: 5 * virtualstudio.uiScale
+                        anchors.rightMargin: 8 * virtualstudio.uiScale
+                        model: isInput ? audio.inputMeterLevels : audio.outputMeterLevels
+                        clipped: isInput ? audio.inputClipped : audio.outputClipped
+                        enabled: true
+                    }
+                }
+            }
+        }
+
+        Item {
+            Layout.preferredHeight: 42
+            Layout.minimumHeight: 42
+            Layout.maximumHeight: 42
+            Layout.fillWidth: true
+
+            RowLayout {
+                anchors.fill: parent
+                spacing: 2
+
+                Item {
+                    Layout.fillHeight: true
+                    Layout.fillWidth: true
+                    Layout.alignment: Qt.AlignVCenter
+                    Layout.leftMargin: 8 * virtualstudio.uiScale
+                    Layout.rightMargin: 8 * virtualstudio.uiScale
+
+                    Loader {
+                        anchors.fill: parent
+                        anchors.verticalCenter: parent.verticalCenter
+                        sourceComponent: isInput ? inputControls : outputControls
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/src/gui/DeviceControlsGroup.qml b/src/gui/DeviceControlsGroup.qml
new file mode 100644 (file)
index 0000000..aaa8c0b
--- /dev/null
@@ -0,0 +1,332 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+
+Rectangle {
+    required property bool showMinified
+
+    property string disabledButtonText: "#D3D4D4"
+    property string saveButtonText: "#DB0A0A"
+    property int minifiedHeight: 36
+    property int fullHeight: 80
+
+    id: deviceControlsGroup
+    width: parent.width
+    height: showMinified ? minifiedHeight : fullHeight
+    color: backgroundColour
+
+    property bool showDeviceControls: studioStatus === "Ready"
+
+    MouseArea {
+        anchors.fill: parent
+        propagateComposedEvents: false
+    }
+
+    RowLayout {
+        id: layout
+        anchors.fill: parent
+        spacing: 2
+        visible: !feedbackDetectedModal.visible
+
+        Item {
+            Layout.fillHeight: true
+            Layout.fillWidth: true
+            visible: !showDeviceControls
+
+            Button {
+                id: backButton
+                anchors.centerIn: parent
+                width: 180 * virtualstudio.uiScale
+                height: 40 * virtualstudio.uiScale
+                background: Rectangle {
+                    radius: 8 * virtualstudio.uiScale
+                    color: backButton.down ? browserButtonPressedColour : (backButton.hovered ? browserButtonHoverColour : browserButtonColour)
+                }
+                onClicked: virtualstudio.disconnect()
+
+                Text {
+                    text: "Back to Studios"
+                    font { family: "Poppins"; pixelSize: fontMedium * virtualstudio.fontScale * virtualstudio.uiScale}
+                    anchors { horizontalCenter: parent.horizontalCenter; verticalCenter: parent.verticalCenter }
+                    color: textColour
+                }
+            }
+        }
+
+        Item {
+            Layout.fillHeight: true
+            Layout.fillWidth: true
+            visible: showDeviceControls
+
+            DeviceControls {
+                isInput: true
+            }
+        }
+
+        Item {
+            Layout.fillHeight: true
+            Layout.fillWidth: true
+            visible: showDeviceControls
+
+            DeviceControls {
+                isInput: false
+            }
+        }
+
+        Item {
+            Layout.fillHeight: true
+            Layout.preferredWidth: 48
+            visible: showDeviceControls
+
+            ColumnLayout {
+                anchors.fill: parent
+                spacing: 2
+
+                Item {
+                    Layout.preferredHeight: 20
+                    Layout.preferredWidth: 40
+                    Layout.alignment: Qt.AlignHCenter
+                }
+
+                Item {
+                    Layout.preferredHeight: 48
+                    Layout.preferredWidth: 40
+                    Layout.alignment: Qt.AlignHCenter
+                    visible: !showMinified
+
+                    Button {
+                        id: changeDevicesButton
+                        width: 36
+                        height: 36
+                        anchors.top: parent.top
+                        anchors.horizontalCenter: parent.horizontalCenter
+                        background: Rectangle {
+                            radius: 8 * virtualstudio.uiScale
+                            color: changeDevicesButton.down ? browserButtonPressedColour : (changeDevicesButton.hovered ? browserButtonHoverColour : browserButtonColour)
+                        }
+                        onClicked: {
+                            virtualstudio.windowState = "change_devices"
+                            if (!audio.deviceModelsInitialized) {
+                                audio.refreshDevices();
+                            }
+                        }
+
+                        AppIcon {
+                            id: changeDevicesIcon
+                            anchors { verticalCenter: parent.verticalCenter; horizontalCenter: parent.horizontalCenter }
+                            width: 20 * virtualstudio.uiScale
+                            height: 20 * virtualstudio.uiScale
+                            icon.source: "cog.svg"
+                            onClicked: {
+                                virtualstudio.windowState = "change_devices"
+                                if (!audio.deviceModelsInitialized) {
+                                    audio.refreshDevices();
+                                }
+                            }
+                        }
+                    }
+
+                    Text {
+                        anchors.top: changeDevicesButton.bottom
+                        text: "Devices"
+                        font { family: "Poppins"; pixelSize: fontTiny * virtualstudio.fontScale * virtualstudio.uiScale}
+                        anchors { horizontalCenter: parent.horizontalCenter; verticalCenter: parent.verticalCenter }
+                        color: textColour
+                    }
+                }
+            }
+        }
+    }
+
+    Rectangle {
+        id: backgroundBorder
+        width: parent.width
+        height: 1
+        anchors.top: layout.top
+        color: strokeColor
+    }
+
+    Popup {
+        id: feedbackDetectedModal
+        padding: 1
+        width: parent.width
+        height: parent.height
+        anchors.centerIn: parent
+        dim: false
+        modal: false
+        focus: true
+        closePolicy: Popup.NoAutoClose
+
+        background: Rectangle {
+            anchors.fill: parent
+            color: "transparent"
+            border.width: 1
+            border.color: buttonStroke
+            clip: true
+        }
+
+        contentItem: Rectangle {
+            width: parent.width
+            height: 232 * virtualstudio.uiScale
+            color: backgroundColour
+
+            Item {
+                id: feedbackDetectedContent
+                anchors.top: parent.top
+                anchors.bottom: parent.bottom
+                anchors.left: parent.left
+                anchors.leftMargin: 16 * virtualstudio.uiScale
+                anchors.right: parent.right
+
+                AppIcon {
+                    id: feedbackWarningIcon
+                    anchors.left: parent.left
+                    anchors.top: parent.top
+                    anchors.topMargin: 10 * virtualstudio.uiScale
+                    width: 32 * virtualstudio.uiScale
+                    height: 32 * virtualstudio.uiScale
+                    icon.source: "warning.svg"
+                    color: "#F21B1B"
+                    visible: !showMinified
+                }
+
+                AppIcon {
+                    id: feedbackWarningIconMinified
+                    anchors.left: parent.left
+                    anchors.verticalCenter: parent.verticalCenter
+                    height: 24 * virtualstudio.uiScale
+                    width: 24 * virtualstudio.uiScale
+                    icon.source: "warning.svg"
+                    color: "#F21B1B"
+                    visible: showMinified
+                }
+
+                Text {
+                    id: feedbackDetectedHeader
+                    anchors.top: parent.top
+                    anchors.topMargin: 10 * virtualstudio.uiScale
+                    anchors.left: feedbackWarningIcon.right
+                    anchors.leftMargin: 16 * virtualstudio.uiScale
+                    width: parent.width
+                    text: "Audio feedback detected!"
+                    font {family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale; bold: true }
+                    color: textColour
+                    elide: Text.ElideRight
+                    wrapMode: Text.WordWrap
+                    visible: !showMinified
+                }
+
+                Text {
+                    id: feedbackDetectedText
+                    anchors.top: feedbackDetectedHeader.bottom
+                    anchors.topMargin: 4 * virtualstudio.uiScale
+                    anchors.left: feedbackWarningIcon.right
+                    anchors.leftMargin: 16 * virtualstudio.uiScale
+                    width: parent.width
+                    text: "JackTrip detected a feedback loop. Your monitor and input volume have automatically been disabled."
+                    font {family: "Poppins"; pixelSize: fontTiny * virtualstudio.fontScale * virtualstudio.uiScale }
+                    color: textColour
+                    elide: Text.ElideRight
+                    wrapMode: Text.WordWrap
+                    visible: !showMinified
+                }
+
+                Text {
+                    id: feedbackDetectedTextMinified
+                    anchors.verticalCenter: parent.verticalCenter
+                    anchors.left: feedbackWarningIcon.right
+                    anchors.leftMargin: 16 * virtualstudio.uiScale
+                    width: parent.width
+                    text: "JackTrip detected a feedback loop. Your monitor and input volume have automatically been disabled."
+                    font {family: "Poppins"; pixelSize: fontTiny * virtualstudio.fontScale * virtualstudio.uiScale }
+                    color: textColour
+                    elide: Text.ElideRight
+                    wrapMode: Text.WordWrap
+                    visible: showMinified
+                }
+
+                Text {
+                    id: feedbackDetectedText2
+                    anchors.top: feedbackDetectedText.bottom
+                    anchors.topMargin: 2 * virtualstudio.uiScale
+                    anchors.left: feedbackWarningIcon.right
+                    anchors.leftMargin: 16 * virtualstudio.uiScale
+                    width: parent.width
+                    text: "You can disable this behavior under <b>Settings</b> > <b>Advanced</b>"
+                    textFormat: Text.RichText
+                    font {family: "Poppins"; pixelSize: fontTiny * virtualstudio.fontScale * virtualstudio.uiScale }
+                    color: textColour
+                    elide: Text.ElideRight
+                    wrapMode: Text.WordWrap
+                    visible: !showMinified
+                }
+
+                Button {
+                    id: closeFeedbackDetectedModalButton
+                    anchors.right: parent.right
+                    anchors.rightMargin: rightMargin * virtualstudio.uiScale
+                    anchors.verticalCenter: parent.verticalCenter
+                    width: 128 * virtualstudio.uiScale;
+                    height: 30 * virtualstudio.uiScale
+                    onClicked: feedbackDetectedModal.close()
+
+                    background: Rectangle {
+                        radius: 6 * virtualstudio.uiScale
+                        color: closeFeedbackDetectedModalButton.down ? browserButtonPressedColour : (closeFeedbackDetectedModalButton.hovered ? browserButtonHoverColour : browserButtonColour)
+                        border.width: 1
+                        border.color: closeFeedbackDetectedModalButton.down ? browserButtonPressedStroke : (closeFeedbackDetectedModalButton.hovered ? browserButtonHoverStroke : browserButtonStroke)
+                    }
+
+                    Text {
+                        text: "Ok"
+                        font.family: "Poppins"
+                        font.pixelSize: showMinified ? fontTiny * virtualstudio.fontScale * virtualstudio.uiScale : fontSmall * virtualstudio.fontScale * virtualstudio.uiScale
+                        font.weight: Font.Bold
+                        color: !Boolean(audio.devicesError) && audio.backendAvailable ? saveButtonText : disabledButtonText
+                        anchors.horizontalCenter: parent.horizontalCenter
+                        anchors.verticalCenter: parent.verticalCenter
+                    }
+                    visible: !showMinified
+                }
+
+                Button {
+                    id: closeFeedbackDetectedModalButtonMinified
+                    anchors.right: parent.right
+                    anchors.rightMargin: rightMargin * virtualstudio.uiScale
+                    anchors.verticalCenter: parent.verticalCenter
+                    width: 80 * virtualstudio.uiScale
+                    height: 24 * virtualstudio.uiScale
+                    onClicked: feedbackDetectedModal.close()
+
+                    background: Rectangle {
+                        radius: 6 * virtualstudio.uiScale
+                        color: closeFeedbackDetectedModalButton.down ? browserButtonPressedColour : (closeFeedbackDetectedModalButton.hovered ? browserButtonHoverColour : browserButtonColour)
+                        border.width: 1
+                        border.color: closeFeedbackDetectedModalButton.down ? browserButtonPressedStroke : (closeFeedbackDetectedModalButton.hovered ? browserButtonHoverStroke : browserButtonStroke)
+                    }
+
+                    Text {
+                        text: "Ok"
+                        font.family: "Poppins"
+                        font.pixelSize: fontTiny * virtualstudio.fontScale * virtualstudio.uiScale
+                        font.weight: Font.Bold
+                        color: !Boolean(audio.devicesError) && audio.backendAvailable ? saveButtonText : disabledButtonText
+                        anchors.horizontalCenter: parent.horizontalCenter
+                        anchors.verticalCenter: parent.verticalCenter
+                    }
+                    visible: showMinified
+                }
+            }
+        }
+    }
+
+    Connections {
+        target: virtualstudio
+
+        function onFeedbackDetected() {
+            if (virtualstudio.windowState === "connected") {
+                feedbackDetectedModal.visible = true;
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/gui/DeviceRefreshButton.qml b/src/gui/DeviceRefreshButton.qml
new file mode 100644 (file)
index 0000000..1c3ed65
--- /dev/null
@@ -0,0 +1,38 @@
+import QtQuick
+import QtQuick.Controls
+
+Button {
+    id: refreshButton
+    text: "Refresh Devices"
+
+    property int fontExtraSmall: 8
+    property string textColour: virtualstudio.darkMode ? "#FAFBFB" : "#0F0D0D"
+    property string buttonColour: virtualstudio.darkMode ? "#494646" : "#EAECEC"
+    property string buttonHoverColour: virtualstudio.darkMode ? "#5B5858" : "#D3D4D4"
+    property string buttonPressedColour: virtualstudio.darkMode ? "#524F4F" : "#DEE0E0"
+    property string buttonStroke: virtualstudio.darkMode ? "#80827D7D" : "#34979797"
+    property string buttonHoverStroke: virtualstudio.darkMode ? "#7B7777" : "#BABCBC"
+    property string buttonPressedStroke: virtualstudio.darkMode ? "#827D7D" : "#BABCBC"
+    property var onDeviceRefresh: function () { audio.refreshDevices(); };
+
+    width: 144 * virtualstudio.uiScale;
+    height: 30 * virtualstudio.uiScale
+    palette.buttonText: textColour
+    display: AbstractButton.TextBesideIcon
+
+    background: Rectangle {
+        radius: 6 * virtualstudio.uiScale
+        color: refreshButton.down ? buttonPressedColour : (refreshButton.hovered ? buttonHoverColour : buttonColour)
+        border.width: 1
+        border.color: refreshButton.down ? buttonPressedStroke : (refreshButton.hovered ? buttonHoverStroke : buttonStroke)
+    }
+    icon {
+        source: "refresh.svg";
+        color: textColour;
+    }
+    onClicked: { onDeviceRefresh(); }
+    font {
+        family: "Poppins"
+        pixelSize: fontExtraSmall * virtualstudio.fontScale * virtualstudio.uiScale
+    }
+}
diff --git a/src/gui/DeviceWarning.qml b/src/gui/DeviceWarning.qml
new file mode 100644 (file)
index 0000000..1a62d52
--- /dev/null
@@ -0,0 +1,52 @@
+import QtQuick
+import QtQuick.Controls
+
+Item {
+    height: 28 * virtualstudio.uiScale
+    property string devicesWarningColour: "#F21B1B"
+
+    AppIcon {
+        id: devicesWarningIcon
+        anchors.left: parent.left
+        anchors.verticalCenter: parent.verticalCenter
+        width: parent.height
+        height: parent.height
+        icon.source: "warning.svg"
+        color: devicesWarningColour
+        visible: Boolean(audio.devicesError) || Boolean(audio.devicesWarning)
+    }
+    
+    Text {
+        id: warningOrErrorText
+        text: Boolean(audio.devicesError) ? "Audio Configuration Error" : "Audio Configuration Warning"
+        anchors.left: devicesWarningIcon.right
+        anchors.leftMargin: 4 * virtualstudio.uiScale
+        anchors.verticalCenter: devicesWarningIcon.verticalCenter
+        visible: Boolean(audio.devicesError) || Boolean(audio.devicesWarning)
+        font { family: "Poppins"; pixelSize: 9 * virtualstudio.fontScale * virtualstudio.uiScale }
+        color: devicesWarningColour
+    }
+
+    InfoTooltip {
+        id: devicesWarningTooltip
+        anchors.left: warningOrErrorText.right
+        anchors.leftMargin: 2 * virtualstudio.uiScale
+        anchors.bottom: warningOrErrorText.bottom
+        anchors.bottomMargin: 6 * virtualstudio.uiScale
+        content: qsTr(audio.devicesError || audio.devicesWarning)
+        iconColor: devicesWarningColour
+        size: 16 * virtualstudio.uiScale
+        visible: Boolean(audio.devicesError) || Boolean(audio.devicesWarning)
+    }
+
+    MouseArea {
+        id: devicesWarningToolTipArea
+        anchors.top: devicesWarningIcon.top
+        anchors.bottom: devicesWarningIcon.bottom
+        anchors.left: devicesWarningIcon.left
+        anchors.right: warningOrErrorText.right
+        hoverEnabled: true
+        onEntered: devicesWarningTooltip.showToolTip = true
+        onExited: devicesWarningTooltip.showToolTip = false
+    }
+}
index 5778f5c41036c4c52994b0a9eea1a1bf28091942..956406de82cec285c2ffee58e90e14d057484239 100644 (file)
@@ -1,6 +1,5 @@
-import QtQuick 2.12
-import QtQuick.Controls 2.12
-import QtGraphicalEffects 1.12
+import QtQuick
+import QtQuick.Controls
 
 Item {
     width: parent.width; height: parent.height
@@ -20,23 +19,13 @@ Item {
     property string buttonTextHover: virtualstudio.darkMode ? "#242222" : "#D00A0A"
     property string buttonTextPressed: virtualstudio.darkMode ? "#323030" : "#D00A0A"
 
-    property real imageLightnessValue: virtualstudio.darkMode ? 1.0 : 0.0
-
-    Image {
+    AppIcon {
         id: ohnoImage
-        source: "ohno.png"
-        width: 180
-        height: 180
         y: 60
         anchors.horizontalCenter: parent.horizontalCenter
-    }
-
-    Colorize {
-        anchors.fill: ohnoImage
-        source: ohnoImage
-        hue: 0
-        saturation: 0
-        lightness: imageLightnessValue
+        width: 180
+        height: 180
+        icon.source: "sentiment_very_dissatisfied.svg"
     }
 
     Text {
diff --git a/src/gui/FeedbackSurvey.qml b/src/gui/FeedbackSurvey.qml
new file mode 100644 (file)
index 0000000..eaeae14
--- /dev/null
@@ -0,0 +1,442 @@
+import QtQuick
+import QtQuick.Controls
+
+Item {
+    id: userFeedbackSurvey
+
+    anchors.centerIn: parent
+    width: 480 * virtualstudio.uiScale
+    height: 232 * virtualstudio.uiScale
+
+    property int leftHeaderMargin: 16
+    property int fontBig: 28
+    property int fontMedium: 12
+    property int fontSmall: 10
+    property int fontTiny: 8
+    property int bodyMargin: 60
+    property int rightMargin: 16
+    property int bottomToolTipMargin: 8
+    property int rightToolTipMargin: 4
+
+    property string buttonColour: "#F2F3F3"
+    property string buttonHoverColour: "#E7E8E8"
+    property string buttonPressedColour: "#E7E8E8"
+    property string buttonStroke: "#EAEBEB"
+    property string buttonHoverStroke: "#B0B5B5"
+    property string buttonPressedStroke: "#B0B5B5"
+
+    property string textColour: virtualstudio.darkMode ? "#FAFBFB" : "#0F0D0D"
+    property string textAreaTextColour: virtualstudio.darkMode ? "#A6A6A6" : "#757575"
+    property string textAreaColour: virtualstudio.darkMode ? "#494646" : "#EAECEC"
+
+    property string serverId: ""
+
+    property int rating: 0
+    property int hover: star1MouseArea.containsMouse ? 1 : star2MouseArea.containsMouse ? 2 : star3MouseArea.containsMouse ? 3 : star4MouseArea.containsMouse ? 4 : star5MouseArea.containsMouse ? 5 : 0
+    property int currentView: (hover > 0 ? hover : rating)
+    property bool submitted: false;
+
+    property string message: ""
+
+    Popup {
+        id: userFeedbackModal
+        padding: 1
+        width: parent.width
+        height: 300 * virtualstudio.uiScale
+        anchors.centerIn: parent
+        modal: true
+        focus: true
+        closePolicy: Popup.NoAutoClose
+
+        background: Rectangle {
+            anchors.fill: parent
+            color: "transparent"
+            radius: 6 * virtualstudio.uiScale
+            border.width: 1
+            border.color: buttonStroke
+            clip: true
+        }
+
+        contentItem: Rectangle {
+            width: parent.width
+            height: parent.height
+            color: backgroundColour
+            radius: 6 * virtualstudio.uiScale
+
+            Item {
+                id: userFeedbackSurveyContent
+                anchors.top: parent.top
+                anchors.topMargin: 24 * virtualstudio.uiScale
+                anchors.bottom: parent.bottom
+                anchors.left: parent.left
+                anchors.right: parent.right
+                visible: !submitted
+
+                Text {
+                    id: userFeedbackSurveyHeader
+                    anchors.top: parent.top
+                    anchors.topMargin: 16 * virtualstudio.uiScale
+                    anchors.horizontalCenter: parent.horizontalCenter
+                    width: parent.width
+                    text: "How did your session go?"
+                    font {family: "Poppins"; pixelSize: fontMedium * virtualstudio.fontScale * virtualstudio.uiScale; bold: true }
+                    horizontalAlignment: Text.AlignHCenter
+                    color: textColour
+                    elide: Text.ElideRight
+                    wrapMode: Text.WordWrap
+                }
+
+                Text {
+                    id: ratingItemInstructions
+                    anchors.top: userFeedbackSurveyHeader.bottom
+                    anchors.topMargin: 12 * virtualstudio.uiScale
+                    anchors.horizontalCenter: parent.horizontalCenter
+                    width: parent.width
+                    text: "Rate your session on a scale of 1 to 5"
+                    font {family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale }
+                    horizontalAlignment: Text.AlignHCenter
+                    color: textColour
+                    elide: Text.ElideRight
+                    wrapMode: Text.WordWrap
+                }
+
+                Item {
+                    id: ratingItem
+                    anchors.top: ratingItemInstructions.bottom
+                    anchors.topMargin: 4 * virtualstudio.uiScale
+                    anchors.horizontalCenter: parent.horizontalCenter
+                    height: 40 * virtualstudio.uiScale
+                    width: 200 * virtualstudio.uiScale
+
+                    Item {
+                        id: star1Container
+                        anchors.verticalCenter: parent.verticalCenter
+                        anchors.right: star2Container.left
+                        width: parent.width / 5
+                        height: parent.height
+
+                        AppIcon {
+                            id: star1Icon
+                            anchors.centerIn: parent
+                            width: currentView >= 1 ? parent.width : 20 * virtualstudio.uiScale
+                            height: currentView >= 1 ? parent.height : 20 * virtualstudio.uiScale
+                            icon.source: "star.svg"
+                            color: currentView >= 1 ? "#faaf00" : "#606060"
+                        }
+
+                        MouseArea {
+                            id: star1MouseArea
+                            anchors.fill: parent
+                            hoverEnabled: true
+                            onClicked: () => {
+                                if (rating === 1) {
+                                    rating = 0;
+                                } else {
+                                    rating = 1;
+                                }
+                            }
+                        }
+                    }
+
+                    Item {
+                        id: star2Container
+                        anchors.verticalCenter: parent.verticalCenter
+                        anchors.right: star3Container.left
+                        width: parent.width / 5
+                        height: parent.height
+
+                        AppIcon {
+                            id: star2Icon
+                            anchors.centerIn: parent
+                            width: currentView >= 2 ? parent.width : 20 * virtualstudio.uiScale
+                            height: currentView >= 2 ? parent.height : 20 * virtualstudio.uiScale
+                            icon.source: "star.svg"
+                            color: currentView >= 2 ? "#faaf00" : "#606060"
+                        }
+
+                        MouseArea {
+                            id: star2MouseArea
+                            anchors.fill: parent
+                            hoverEnabled: true
+                            onClicked: () => {
+                                if (rating === 2) {
+                                    rating = 0;
+                                } else {
+                                    rating = 2;
+                                }
+                            }
+                        }
+                    }
+
+                    Item {
+                        id: star3Container
+                        anchors.verticalCenter: parent.verticalCenter
+                        anchors.horizontalCenter: parent.horizontalCenter
+                        width: parent.width / 5
+                        height: parent.height
+
+                        AppIcon {
+                            id: star3Icon
+                            anchors.centerIn: parent
+                            width: currentView >= 3 ? parent.width : 20 * virtualstudio.uiScale
+                            height: currentView >= 3 ? parent.height : 20 * virtualstudio.uiScale
+                            icon.source: "star.svg"
+                            color: currentView >= 3 ? "#faaf00" : "#606060"
+                        }
+
+                        MouseArea {
+                            id: star3MouseArea
+                            anchors.fill: parent
+                            hoverEnabled: true
+                            onClicked: () => {
+                                if (rating === 3) {
+                                    rating = 0;
+                                } else {
+                                    rating = 3;
+                                }
+                            }
+                        }
+                    }
+
+                    Item {
+                        id: star4Container
+                        anchors.verticalCenter: parent.verticalCenter
+                        anchors.left: star3Container.right
+                        width: parent.width / 5
+                        height: parent.height
+
+                        AppIcon {
+                            id: star4Icon
+                            anchors.centerIn: parent
+                            width: currentView >= 4 ? parent.width : 20 * virtualstudio.uiScale
+                            height: currentView >= 4 ? parent.height : 20 * virtualstudio.uiScale
+                            icon.source: "star.svg"
+                            color: currentView >= 4 ? "#faaf00" : "#606060"
+                        }
+
+                        MouseArea {
+                            id: star4MouseArea
+                            anchors.fill: parent
+                            hoverEnabled: true
+                            onClicked: () => {
+                                if (rating === 4) {
+                                    rating = 0;
+                                } else {
+                                    rating = 4;
+                                }
+                            }
+                        }
+                    }
+
+                    Item {
+                        id: star5Container
+                        anchors.verticalCenter: parent.verticalCenter
+                        anchors.left: star4Container.right
+                        width: parent.width / 5
+                        height: parent.height
+
+                        AppIcon {
+                            id: star5Icon
+                            anchors.centerIn: parent
+                            width: currentView >= 5 ? parent.width : 20 * virtualstudio.uiScale
+                            height: currentView >= 5 ? parent.height : 20 * virtualstudio.uiScale
+                            icon.source: "star.svg"
+                            color: currentView >= 5 ? "#faaf00" : "#606060"
+                        }
+
+                        MouseArea {
+                            id: star5MouseArea
+                            anchors.fill: parent
+                            hoverEnabled: true
+                            onClicked: () => {
+                                if (rating === 5) {
+                                    rating = 0;
+                                } else {
+                                    rating = 5;
+                                }
+                            }
+                        }
+                    }
+                }
+
+                ScrollView {
+                    id: messageBoxScrollArea
+                    anchors.left: parent.left
+                    anchors.leftMargin: 32 * virtualstudio.uiScale
+                    anchors.right: parent.right
+                    anchors.rightMargin: 32 * virtualstudio.uiScale
+                    anchors.top: ratingItem.bottom
+                    anchors.topMargin: 12 * virtualstudio.uiScale
+                    height: 64 * virtualstudio.uiScale
+
+                    TextArea {
+                        id: messageBox
+                        placeholderText: qsTr("(Optional) Let us know how we can improve your experience.")
+                        placeholderTextColor: textAreaTextColour
+                        color: textColour
+                        background: Rectangle {
+                        color: textAreaColour
+                        radius: 6 * virtualstudio.uiScale
+                        border.width: 1
+                        border.color: buttonStroke
+                        }
+                    }
+                }
+
+                Item {
+                    id: buttonsArea
+                    height: 32 * virtualstudio.uiScale
+                    width: 324 * virtualstudio.uiScale
+                    anchors.horizontalCenter: messageBoxScrollArea.horizontalCenter
+                    anchors.top: messageBoxScrollArea.bottom
+                    anchors.topMargin: 24 * virtualstudio.uiScale
+
+                    Button {
+                        id: noUserFeedbackButton
+                        anchors.left: buttonsArea.left
+                        anchors.verticalCenter: parent.buttonsArea
+                        width: 150 * virtualstudio.uiScale; height: 30 * virtualstudio.uiScale
+                        onClicked: () => {
+                            userFeedbackModal.close();
+                            rating = 0;
+                            serverId = "";
+                            messageBox.clear();
+                        }
+
+                        background: Rectangle {
+                            radius: 6 * virtualstudio.uiScale
+                            color: noUserFeedbackButton.down ? buttonPressedColour : (noUserFeedbackButton.hovered ? buttonHoverColour : buttonColour)
+                            border.width: 1
+                            border.color: noUserFeedbackButton.down ? buttonPressedStroke : (noUserFeedbackButton.hovered ? buttonHoverStroke : buttonStroke)
+                        }
+
+                        Text {
+                            text: "No thanks"
+                            font.family: "Poppins"
+                            font.pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale
+                            anchors.horizontalCenter: parent.horizontalCenter
+                            anchors.verticalCenter: parent.verticalCenter
+                        }
+                    }
+
+                    Button {
+                        id: submitUserFeedbackButton
+                        anchors.right: buttonsArea.right
+                        anchors.verticalCenter: parent.buttonsArea
+                        width: 150 * virtualstudio.uiScale; height: 30 * virtualstudio.uiScale
+                        onClicked: () => {
+                            virtualstudio.collectFeedbackSurvey(serverId, rating, messageBox.text);
+                            submitted = true;
+                            rating = 0;
+                            serverId = "";
+                            messageBox.clear();
+                            userFeedbackModal.height = 150 * virtualstudio.uiScale
+                            submittedFeedbackTimer.start();
+                        }
+
+                        background: Rectangle {
+                            radius: 6 * virtualstudio.uiScale
+                            color: submitUserFeedbackButton.down ? buttonPressedColour : (submitUserFeedbackButton.hovered ? buttonHoverColour : buttonColour)
+                            border.width: 1
+                            border.color: submitUserFeedbackButton.down ? buttonPressedStroke : (submitUserFeedbackButton.hovered ? buttonHoverStroke : buttonStroke)
+                        }
+
+                        Text {
+                            text: "Submit"
+                            font.family: "Poppins"
+                            font.pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale
+                            font.weight: Font.Bold
+                            color: "#DB0A0A"
+                            anchors.horizontalCenter: parent.horizontalCenter
+                            anchors.verticalCenter: parent.verticalCenter
+                        }
+                    }
+
+                    Timer {
+                        id: submittedFeedbackTimer
+                        interval: 5000; running: false; repeat: false
+                        onTriggered: () => {
+                            submitted = false;
+                            userFeedbackModal.height = 300 * virtualstudio.uiScale
+                            userFeedbackModal.close();
+                        }
+                    }
+                }
+            }
+
+            Item {
+                id: submittedFeedbackContent
+                anchors.top: parent.top
+                anchors.bottom: parent.bottom
+                anchors.left: parent.left
+                anchors.right: parent.right
+                visible: submitted
+
+                Text {
+                    id: submittedFeedbackHeader
+                    anchors.top: submittedFeedbackContent.top
+                    anchors.topMargin: 24 * virtualstudio.uiScale
+                    anchors.horizontalCenter: parent.horizontalCenter
+                    width: parent.width
+                    text: "Thank you!"
+                    font {family: "Poppins"; pixelSize: fontMedium * virtualstudio.fontScale * virtualstudio.uiScale; bold: true }
+                    horizontalAlignment: Text.AlignHCenter
+                    color: textColour
+                    elide: Text.ElideRight
+                    wrapMode: Text.WordWrap
+                }
+
+                Text {
+                    id: submittedFeedbackText
+                    anchors.top: submittedFeedbackHeader.bottom
+                    anchors.topMargin: 16 * virtualstudio.uiScale
+                    anchors.horizontalCenter: parent.horizontalCenter
+                    width: parent.width
+                    text: "Your feedback has been recorded."
+                    font {family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale }
+                    horizontalAlignment: Text.AlignHCenter
+                    color: textColour
+                    elide: Text.ElideRight
+                    wrapMode: Text.WordWrap
+                }
+
+                Button {
+                    id: closeButtonFeedback
+                    anchors.horizontalCenter: parent.horizontalCenter
+                    anchors.top: submittedFeedbackText.bottom
+                    anchors.topMargin: 16 * virtualstudio.uiScale
+                    width: 150 * virtualstudio.uiScale; height: 30 * virtualstudio.uiScale
+                    onClicked: () => {
+                        submitted = false;
+                        userFeedbackModal.height = 300 * virtualstudio.uiScale
+                        userFeedbackModal.close();
+                    }
+
+                    background: Rectangle {
+                        radius: 6 * virtualstudio.uiScale
+                        color: closeButtonFeedback.down ? buttonPressedColour : (closeButtonFeedback.hovered ? buttonHoverColour : buttonColour)
+                        border.width: 1
+                        border.color: closeButtonFeedback.down ? buttonPressedStroke : (closeButtonFeedback.hovered ? buttonHoverStroke : buttonStroke)
+                    }
+
+                    Text {
+                        text: "Close"
+                        font.family: "Poppins"
+                        font.pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale
+                        anchors.horizontalCenter: parent.horizontalCenter
+                        anchors.verticalCenter: parent.verticalCenter
+                    }
+                }
+            }
+        }
+    }
+
+    Connections {
+      target: virtualstudio
+
+      function onOpenFeedbackSurveyModal(serverId) {
+        userFeedbackSurvey.serverId = serverId;
+        userFeedbackModal.open();
+      }
+    }
+}
index ecb3ddd3837b11bb5c46dbe63138a79400664384..70d8f1c4fc7f456c15b55c38fffebc8b2cd09dc8 100644 (file)
@@ -1,5 +1,5 @@
-import QtQuick 2.12
-import QtQuick.Controls 2.12
+import QtQuick
+import QtQuick.Controls
 
 Item {
     width: parent.width; height: parent.height
@@ -66,7 +66,7 @@ Item {
         }
     }
     Text {
-        text: "• Connect to Virtual Studios<br>• Broadcast on JackTrip Radio<br>• Apply FX with Soundscapes"
+        text: "• Seamless Audio &amp; Video<br>• Recording &amp; Livestreaming<br>• No Servers Required"
         textFormat: Text.StyledText
         font.family: "Poppins"
         font.pixelSize: 10 * virtualstudio.fontScale * virtualstudio.uiScale
@@ -81,7 +81,7 @@ Item {
     Image {
         source: "JTVS.png"
         x: parent.width / 2 - (265 * virtualstudio.uiScale); y: 420 * virtualstudio.uiScale
-        width: 234 * virtualstudio.uiScale; height: 201.48 * virtualstudio.uiScale;
+        width: 234 * virtualstudio.uiScale; height: 195 * virtualstudio.uiScale;
     }
 
     Button {
@@ -93,7 +93,7 @@ Item {
             border.color: standardButton.down ? buttonPressedStroke : (standardButton.hovered ? buttonHoverStroke : buttonStroke)
             layer.enabled: standardButton.hovered && !standardButton.down
         }
-        onClicked: { virtualstudio.windowState = "login"; virtualstudio.toStandard(); }
+        onClicked: { virtualstudio.toStandard(); }
         x: parent.width / 2 + (32 * virtualstudio.uiScale); y: 290 * virtualstudio.uiScale
         width: 234 * virtualstudio.uiScale; height: 49 * virtualstudio.uiScale
         Text {
diff --git a/src/gui/Footer.qml b/src/gui/Footer.qml
new file mode 100644 (file)
index 0000000..6b76643
--- /dev/null
@@ -0,0 +1,184 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+
+Rectangle {
+    id: footer
+    width: parent.width
+    height: 24
+    anchors.bottom: parent.bottom
+    color: backgroundColour
+    clip: true
+
+    property string connectionStateColor: getConnectionStateColor()
+    property variant networkStatsText: getNetworkStatsText()
+
+    function getConnectionStateColor() {
+        if (virtualstudio.connectionState == "Connected") {
+            return meterGreen
+        }
+        if (virtualstudio.connectionState.includes("Disconnected") || virtualstudio.connectionState.includes("Error")) {
+            return meterRed
+        }
+        if (studioStatus === "Starting" || virtualstudio.connectionState == "Connecting..." || virtualstudio.connectionState == "Reconnecting...") {
+            return meterYellow
+        }
+        return "grey"
+    }
+
+    function getNetworkStatsText() {
+        let minRtt = virtualstudio.networkStats.minRtt;
+        let maxRtt = virtualstudio.networkStats.maxRtt;
+        let avgRtt = virtualstudio.networkStats.avgRtt;
+
+        let texts = ["Unstable", "Please plug into Ethernet & turn off WIFI.", meterRed];
+        if (virtualstudio.networkOutage) {
+            return texts;
+        }
+
+        texts = ["Measuring...", "", "grey"];
+        if (!minRtt || !maxRtt) {
+            return texts;
+        }
+
+        texts[1] = "<b>" + minRtt + " ms - " + maxRtt + " ms</b>, avg " + avgRtt + " ms";
+        let quality = "Poor";
+        let color = meterRed;
+        if (avgRtt <= 25) {
+            if (maxRtt <= 30) {
+                quality = "Excellent";
+                color = meterGreen;
+            } else {
+                quality = "Good";
+                color = meterGreen;
+            }
+        } else if (avgRtt <= 30) {
+            quality = "Good";
+            color = meterGreen;
+        } else if (avgRtt <= 35) {
+            quality = "Fair";
+            color = meterYellow;
+        }
+
+        texts[0] = quality
+        texts[2] = color;
+        return texts;
+    }
+
+    MouseArea {
+        anchors.fill: parent
+        propagateComposedEvents: false
+    }
+
+    RowLayout {
+        id: layout
+        anchors.fill: parent
+        spacing: 4
+
+        Rectangle {
+            color: backgroundColour
+            Layout.minimumWidth: 256
+            Layout.preferredWidth: 512
+            Layout.maximumWidth: 640
+            Layout.fillHeight: true
+            Layout.fillWidth: true
+            visible: studioStatus === "Ready"
+
+            AppIcon {
+                id: connectionQualityIcon
+                anchors.left: parent.left
+                anchors.leftMargin: 8 * virtualstudio.uiScale
+                anchors.verticalCenter: parent.verticalCenter
+                width: 20 * virtualstudio.uiScale
+                height: 20 * virtualstudio.uiScale
+                icon.source: "speed.svg"
+            }
+
+            Text {
+                id: connectionQualityText
+                anchors.left: connectionQualityIcon.right
+                anchors.leftMargin: 4 * virtualstudio.uiScale
+                anchors.verticalCenter: parent.verticalCenter
+                text: "Connection:"
+                font { family: "Poppins"; pixelSize: fontTiny * virtualstudio.fontScale * virtualstudio.uiScale }
+                color: textColour
+            }
+
+            Text {
+                id: connectionQualityName
+                anchors.left: connectionQualityText.right
+                anchors.leftMargin: 2 * virtualstudio.uiScale
+                anchors.verticalCenter: parent.verticalCenter
+                text: networkStatsText[0]
+                font { family: "Poppins"; weight: Font.Bold; pixelSize: fontTiny * virtualstudio.fontScale * virtualstudio.uiScale }
+                color: networkStatsText[2]
+            }
+
+            Text {
+                id: connectionQualityTime
+                anchors.left: connectionQualityName.right
+                anchors.leftMargin: 8 * virtualstudio.uiScale
+                anchors.verticalCenter: parent.verticalCenter
+                text: networkStatsText[1]
+                font { family: "Poppins"; pixelSize: fontTiny * virtualstudio.fontScale * virtualstudio.uiScale }
+                color: textColour
+            }
+        }
+
+        Item {
+            Layout.fillWidth: true
+            Layout.fillHeight: true
+        }
+
+        Rectangle {
+            color: backgroundColour
+            Layout.minimumWidth: 96
+            Layout.preferredWidth: 128
+            Layout.maximumWidth: 160
+            Layout.fillHeight: true
+
+            Rectangle {
+                id: connectionStatusDot
+                anchors.right: connectionStatusText.left
+                anchors.rightMargin: 4 * virtualstudio.uiScale
+                anchors.verticalCenter: parent.verticalCenter
+                width: 12
+                height: connectionStatusDot.width
+                radius: connectionStatusDot.height / 2
+                color: connectionStateColor
+            }
+
+            Text {
+                id: connectionStatusText
+                anchors.right: parent.right
+                anchors.rightMargin: 8 * virtualstudio.uiScale
+                anchors.verticalCenter: parent.verticalCenter
+                text: studioStatus === "Starting" ? "Starting..." : virtualstudio.connectionState
+                font { family: "Poppins"; pixelSize: fontTiny * virtualstudio.fontScale * virtualstudio.uiScale }
+                color: textColour
+            }
+        }
+    }
+
+    Rectangle {
+        id: backgroundBorder
+        width: parent.width
+        height: 1
+        y: parent.height - footer.height
+        color: buttonStroke
+    }
+
+    Connections {
+        target: virtualstudio
+
+        function onConnectionStateChanged() {
+            connectionStatusDot.color = getConnectionStateColor()
+        }
+        function onNetworkStatsChanged() {
+            networkStatsText = getNetworkStatsText();
+        }
+        function onUpdatedNetworkOutage() {
+            networkStatsText = getNetworkStatsText();
+        }
+    }
+}
diff --git a/src/gui/InfoTooltip.qml b/src/gui/InfoTooltip.qml
new file mode 100644 (file)
index 0000000..5d5c95e
--- /dev/null
@@ -0,0 +1,63 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import Qt5Compat.GraphicalEffects
+
+Item {
+    required property string content
+    required property int size
+
+    width: size * virtualstudio.uiScale
+    height: size * virtualstudio.uiScale
+
+    property string iconSource: "help.svg"
+    property string iconColor: ""
+    property string backgroundColour: virtualstudio.darkMode ? "#323232" : "#F3F3F3"
+    property bool showToolTip: false
+
+    Item {
+        anchors.fill: parent
+
+        AppIcon {
+            id: tooltipIcon
+            anchors.centerIn: parent
+            width: parent.width
+            height: parent.height
+            icon.source: iconSource
+            color: iconColor
+        }
+
+        MouseArea {
+            id: mouseArea
+            anchors.fill: tooltipIcon
+            hoverEnabled: true
+            onEntered: showToolTip = true
+            onExited: showToolTip = false
+        }
+
+        ToolTip {
+            visible: showToolTip
+            x: tooltipIcon.x + tooltipIcon.width
+            y: tooltipIcon.y + tooltipIcon.height
+
+            contentItem: Text {
+                text: content
+                font { family: "Poppins"; pixelSize: fontTiny * virtualstudio.fontScale * virtualstudio.uiScale }
+                color: textColour
+                elide: Text.ElideRight
+                wrapMode: Text.WordWrap
+            }
+
+            background: Rectangle {
+                color: backgroundColour
+                radius: 4
+                layer.enabled: true
+                layer.effect: Glow {
+                    radius: 8
+                    color: "#66000000"
+                    transparentBorder: true
+                }
+            }
+        }
+    }
+}
index bbd5c1d19de892faf62f10804398b2f25afdcfa6..05573f2b64bc26fdb49f7824bb9179edeabab088 100644 (file)
Binary files a/src/gui/JTVS.png and b/src/gui/JTVS.png differ
index 2cae435021500bd61a5450943116f74e2a2edf20..a11e1d52ea3b515f304479a9989df540722fcab7 100644 (file)
@@ -1,6 +1,5 @@
-import QtQuick 2.12
-import QtQuick.Controls 2.12
-import QtGraphicalEffects 1.12
+import QtQuick
+import QtQuick.Controls
 import VS 1.0
 
 Item {
@@ -121,23 +120,14 @@ Item {
             }
         }
 
-        Image {
+        AppIcon {
             id: successIcon
-            source: "check.svg"
             y: 224 * virtualstudio.uiScale
             anchors.horizontalCenter: parent.horizontalCenter
-            visible: loginScreen.state === "success"
-            sourceSize: Qt.size(96 * virtualstudio.uiScale, 96 * virtualstudio.uiScale)
-            fillMode: Image.PreserveAspectFit
-            smooth: true
-        }
-
-        Colorize {
-            anchors.fill: successIcon
-            source: successIcon
-            hue: .44
-            saturation: .55
-            lightness: .49
+            width: 96 * virtualstudio.uiScale
+            height: 96 * virtualstudio.uiScale
+            icon.source: "check.svg"
+            color: "green"
             visible: loginScreen.state === "success"
         }
 
@@ -329,20 +319,6 @@ Item {
         y: 108 * virtualstudio.uiScale
         visible: showLoading
 
-        Text {
-            id: loadingAudioInterfaces
-            text: "Configuring Audio...";
-            font.family: "Poppins"
-            font.pixelSize: 16 * virtualstudio.fontScale * virtualstudio.uiScale
-            anchors.horizontalCenter: parent.horizontalCenter
-            y: 214 * virtualstudio.uiScale
-            width: 360 * virtualstudio.uiScale;
-            color: textColour
-            wrapMode: Text.WordWrap
-            horizontalAlignment: Text.AlignHCenter
-            visible: loginScreen.state === "success"
-        }
-
         Text {
             id: loadingViaRefreshToken
             text: "Logging In...";
@@ -354,7 +330,6 @@ Item {
             color: textColour
             wrapMode: Text.WordWrap
             horizontalAlignment: Text.AlignHCenter
-            visible: !loadingAudioInterfaces.visible
         }
     }
 
index 79d31e1fdacfeff46c8afa8aae9bc0db6d589f84..16d0d4c9693047ffc7e7b1a5a8f71743b0ecd903 100644 (file)
@@ -1,29 +1,28 @@
-import QtQuick 2.12
-import QtQuick.Controls 2.12
-import QtGraphicalEffects 1.12
+import QtQuick
+import QtQuick.Controls
 
 Item {
     required property var model
-    property int bins: 15
+    property int bins: Math.max(15, width/20)
     property int innerMargin: 2 * virtualstudio.uiScale
-    property int clipWidth: 10 * virtualstudio.uiScale
+    property int boxRadius: 3 * virtualstudio.uiScale
+    property int boxThickness: 12 * virtualstudio.uiScale
     required property bool clipped
     property bool enabled: true
-    property string meterColor: enabled ? (virtualstudio.darkMode ? "#5B5858" : "#D3D4D4") : "#EAECEC"
+    property string meterColor: enabled ? (virtualstudio.darkMode ? "#5B5858" : "#D3D4D4") : (virtualstudio.darkMode ? "#7b7979" : "#EAECEC")
     property string meterRed: "#F21B1B"
 
     Item {
         id: meters
-        x: 0; y: 0
-        width: parent.width - clipWidth
+        width: parent.width - boxThickness - innerMargin
         height: parent.height
 
         MeterBars {
             id: leftchannel
-            x: 0;
-            y: 0;
-            width: parent.width - clipWidth
-            height: 14 * virtualstudio.uiScale
+            x: 0
+            y: 0
+            width: parent.width
+            height: boxThickness
             level: parent.parent.model[0]
             enabled: parent.parent.enabled
         }
@@ -31,9 +30,10 @@ Item {
         MeterBars {
             id: rightchannel
             x: 0;
-            y: leftchannel.height
-            width: parent.width - clipWidth
-            height: 14 * virtualstudio.uiScale
+            anchors.top: leftchannel.bottom
+            anchors.topMargin: innerMargin
+            width: parent.width
+            height: boxThickness
             level: parent.parent.model[1]
             enabled: parent.parent.enabled
         }
@@ -41,10 +41,13 @@ Item {
 
     Rectangle {
         id: clipIndicator
-        x: meters.x + meters.width; y: 0
-        width: Math.min(clipWidth, ((parent.width - clipWidth) / bins) - innerMargin)
-        height: 24 * virtualstudio.uiScale
-        radius: 4 * virtualstudio.uiScale
+        y: 0
+        anchors.left: meters.right
+        anchors.leftMargin: innerMargin
+
+        width: boxThickness
+        height: leftchannel.height + rightchannel.height + innerMargin
+        radius: boxRadius
         color: clipped ? meterRed : meterColor
     }
 }
\ No newline at end of file
index 6bad9755174f44bfe6a2093a46d903e3a7bac365..cbdbf0f37e986cc26f239c051c191a84193864bf 100644 (file)
@@ -1,16 +1,13 @@
-import QtQuick 2.12
-import QtQuick.Controls 2.12
-import QtGraphicalEffects 1.12
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
 
 Item {
     required property var level
     required property var enabled
-    property int bins: 15
-    property int innerMargin: 2 * virtualstudio.uiScale
-    property int boxHeight: 10 * virtualstudio.uiScale
+    property int boxHeight: height
     property int boxWidth: (width / bins) - innerMargin
-    property int boxRadius: 4 * virtualstudio.uiScale
-    property string meterColor: enabled ? (virtualstudio.darkMode ? "#5B5858" : "#D3D4D4") : "#EAECEC"
+    property string meterColor: enabled ? (virtualstudio.darkMode ? "#5B5858" : "#D3D4D4") : (virtualstudio.darkMode ? "#7b7979" : "#EAECEC")
     property string meterGreen: "#61C554"
     property string meterYellow: "#F5BF4F"
     property string meterRed: "#F21B1B"
@@ -22,161 +19,31 @@ Item {
         }
         // Case where the meter should be filled
         let fillColor = meterGreen;
-        if (idx > 8 && idx <= 11) {
+        if (idx > 0.5*bins && idx <= 0.8*bins) {
             fillColor = meterYellow;
-        } else if (idx > 11) {
+        } else if (idx > 0.8*bins) {
             fillColor = meterRed;
         }
         return fillColor;
     }
 
-    Rectangle {
-        id: box0
-        x: 0;
-        y: 0;
-        width: boxWidth
-        height: boxHeight
-        color: getBoxColor(0)
-        radius: boxRadius
-    }
-
-    Rectangle {
-        id: box1
-        x: boxWidth + innerMargin;
-        y: 0;
-        width: boxWidth
-        height: boxHeight
-        color: getBoxColor(1)
-        radius: boxRadius
-    }
-
-    Rectangle {
-        id: box2
-        x: (boxWidth) * 2 + innerMargin * 2;
-        y: 0;
-        width: boxWidth
-        height: boxHeight
-        color: getBoxColor(2)
-        radius: boxRadius
-    }
-
-    Rectangle {
-        id: box3
-        x: (boxWidth) * 3 + innerMargin * 3;
-        y: 0;
-        width: boxWidth
-        height: boxHeight
-        color: getBoxColor(3)
-        radius: boxRadius
-    }
-
-    Rectangle {
-        id: box4
-        x: (boxWidth) * 4 + innerMargin * 4;
-        y: 0;
-        width: boxWidth
-        height: boxHeight
-        color: getBoxColor(4)
-        radius: boxRadius
-    }
-
-    Rectangle {
-        id: box5
-        x: (boxWidth) * 5 + innerMargin * 5;
-        y: 0;
-        width: boxWidth
-        height: boxHeight
-        color: getBoxColor(5)
-        radius: boxRadius
-    }
-
-    Rectangle {
-        id: box6
-        x: (boxWidth) * 6 + innerMargin * 6;
-        y: 0;
-        width: boxWidth
-        height: boxHeight
-        color: getBoxColor(6)
-        radius: boxRadius
-    }
-
-    Rectangle {
-        id: box7
-        x: (boxWidth) * 7 + innerMargin * 7;
-        y: 0;
-        width: boxWidth
-        height: boxHeight
-        color: getBoxColor(7)
-        radius: boxRadius
-    }
-
-    Rectangle {
-        id: box8
-        x: (boxWidth) * 8 + innerMargin * 8;
-        y: 0;
-        width: boxWidth
-        height: boxHeight
-        color: getBoxColor(8)
-        radius: boxRadius
-    }
-
-    Rectangle {
-        id: box9
-        x: (boxWidth) * 9 + innerMargin * 9;
-        y: 0;
-        width: boxWidth
-        height: boxHeight
-        color: getBoxColor(9)
-        radius: boxRadius
-    }
-
-    Rectangle {
-        id: box10
-        x: (boxWidth) * 10 + innerMargin * 10;
-        y: 0;
-        width: boxWidth
-        height: boxHeight
-        color: getBoxColor(10)
-        radius: boxRadius
-    }
-
-    Rectangle {
-        id: box11
-        x: (boxWidth) * 11 + innerMargin * 11;
-        y: 0;
-        width: boxWidth
-        height: boxHeight
-        color: getBoxColor(11)
-        radius: boxRadius
-    }
-
-    Rectangle {
-        id: box12
-        x: (boxWidth) * 12 + innerMargin * 12;
-        y: 0;
-        width: boxWidth
-        height: boxHeight
-        color: getBoxColor(12)
-        radius: boxRadius
-    }
-
-    Rectangle {
-        id: box13
-        x: (boxWidth) * 13 + innerMargin * 13;
-        y: 0;
-        width: boxWidth
-        height: boxHeight
-        color: getBoxColor(13)
-        radius: boxRadius
-    }
-
-    Rectangle {
-        id: box14
-        x: (boxWidth) * 14 + innerMargin * 14;
-        y: 0;
-        width: boxWidth
-        height: boxHeight
-        color: getBoxColor(14)
-        radius: boxRadius
+    RowLayout {
+        anchors.fill: parent
+        spacing: innerMargin
+
+        Repeater {
+            model: bins
+            Rectangle {
+                Layout.fillHeight: true
+                Layout.fillWidth: true
+                x: (boxWidth) * index + innerMargin * index;
+                y: 0
+                z: 1
+                width: boxWidth
+                height: boxHeight
+                color: getBoxColor(index)
+                radius: boxRadius
+            }
+        }
     }
-}
\ No newline at end of file
+}
diff --git a/src/gui/Permissions.qml b/src/gui/Permissions.qml
new file mode 100644 (file)
index 0000000..6c53314
--- /dev/null
@@ -0,0 +1,205 @@
+import QtQuick
+import QtQuick.Controls
+
+Item {
+    width: parent.width; height: parent.height
+    clip: true
+
+    property int fontBig: 20
+    property int fontMedium: 13
+    property int fontSmall: 11
+    property int fontExtraSmall: 8
+
+    property string saveButtonBackgroundColour: "#F2F3F3"
+    property string saveButtonStroke: "#EAEBEB"
+    property string saveButtonText: "#DB0A0A"
+
+    Item {
+        id: requestMicPermissionsItem
+        width: parent.width; height: parent.height
+        visible: permissions.micPermission == "unknown"
+
+        AppIcon {
+            id: microphonePrompt
+            y: 60
+            anchors.horizontalCenter: parent.horizontalCenter
+            width: 260
+            height: 250
+            icon.source: "Prompt.svg"
+        }
+
+        Image {
+            id: micLogo
+            source: "logo.svg"
+            anchors.horizontalCenter: parent.horizontalCenter
+            anchors.top: microphonePrompt.top
+            anchors.topMargin: 18 * virtualstudio.uiScale
+            width: 32 * virtualstudio.uiScale; height: 59 * virtualstudio.uiScale
+            sourceSize: Qt.size(micLogo.width,micLogo.height)
+            fillMode: Image.PreserveAspectFit
+            smooth: true
+        }
+
+        Button {
+            id: showPromptButton
+            width: 112 * virtualstudio.uiScale
+            height: 30 * virtualstudio.uiScale
+            background: Rectangle {
+                radius: 6 * virtualstudio.uiScale
+                color: showPromptButton.down ? saveButtonPressedColour : saveButtonBackgroundColour
+                border.width: 2
+                border.color: showPromptButton.down || showPromptButton.hovered ? saveButtonPressedStroke : saveButtonStroke
+                layer.enabled: showPromptButton.hovered && !showPromptButton.down
+            }
+            onClicked: {
+                permissions.getMicPermission();
+            }
+            anchors.right: microphonePrompt.right
+            anchors.rightMargin: 13.5 * virtualstudio.uiScale
+            anchors.bottomMargin: 17 * virtualstudio.uiScale
+            anchors.bottom: microphonePrompt.bottom
+            Text {
+                text: "OK"
+                font.pixelSize: 11 * virtualstudio.fontScale * virtualstudio.uiScale
+                font.weight: Font.Bold
+                color: saveButtonText
+                anchors.horizontalCenter: parent.horizontalCenter
+                anchors.verticalCenter: parent.verticalCenter
+            }
+        }
+
+        Text {
+            id: micPermissionsHeader
+            text: "JackTrip needs your sounds!"
+            font { family: "Poppins"; weight: Font.Bold; pixelSize: fontMedium * virtualstudio.fontScale * virtualstudio.uiScale }
+            color: textColour
+            anchors.horizontalCenter: parent.horizontalCenter
+            anchors.top: microphonePrompt.bottom
+            anchors.topMargin: 48 * virtualstudio.uiScale
+        }
+
+        Text {
+            id: micPermissionsSubheader1
+            text: "JackTrip requires permission to use your microphone."
+            font { family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale }
+            color: textColour
+            width: 400
+            wrapMode: Text.Wrap
+            horizontalAlignment: Text.AlignHCenter
+            anchors.horizontalCenter: parent.horizontalCenter
+            anchors.top: micPermissionsHeader.bottom
+            anchors.topMargin: 32 * virtualstudio.uiScale
+        }
+
+        Text {
+            id: micPermissionsSubheader2
+            text: "Click ‘OK’ to give JackTrip access to your microphone, instrument, or other audio device."
+            font { family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale }
+            color: textColour
+            width: 400
+            wrapMode: Text.Wrap
+            horizontalAlignment: Text.AlignHCenter
+            anchors.horizontalCenter: parent.horizontalCenter
+            anchors.top: micPermissionsSubheader1.bottom
+            anchors.topMargin: 24 * virtualstudio.uiScale
+        }
+    }
+
+    Item {
+        id: noMicItem
+        width: parent.width; height: parent.height
+        visible: permissions.micPermission == "denied"
+
+        AppIcon {
+            id: noMic
+            y: 60
+            anchors.horizontalCenter: parent.horizontalCenter
+            width: 109.27
+            height: 170
+            icon.source: "micoff.svg"
+        }
+
+        Button {
+            id: openSettingsButton
+            background: Rectangle {
+                radius: 6 * virtualstudio.uiScale
+                color: openSettingsButton.down ? saveButtonPressedColour : saveButtonBackgroundColour
+                border.width: 1
+                border.color: openSettingsButton.down || openSettingsButton.hovered ? saveButtonPressedStroke : saveButtonStroke
+                layer.enabled: openSettingsButton.hovered && !openSettingsButton.down
+            }
+            onClicked: {
+                permissions.openSystemPrivacy();
+            }
+            anchors.right: parent.right
+            anchors.rightMargin: 16 * virtualstudio.uiScale
+            anchors.bottomMargin: 16 * virtualstudio.uiScale
+            anchors.bottom: parent.bottom
+            width: 200 * virtualstudio.uiScale; height: 30 * virtualstudio.uiScale
+            Text {
+                text: "Open Privacy Settings"
+                font.family: "Poppins"
+                font.pixelSize: 11 * virtualstudio.fontScale * virtualstudio.uiScale
+                font.weight: Font.Bold
+                color: saveButtonText
+                anchors.horizontalCenter: parent.horizontalCenter
+                anchors.verticalCenter: parent.verticalCenter
+            }
+        }
+
+        Text {
+            id: noMicHeader
+            text: "JackTrip can't hear you!"
+            font { family: "Poppins"; weight: Font.Bold; pixelSize: fontMedium * virtualstudio.fontScale * virtualstudio.uiScale }
+            color: textColour
+            anchors.horizontalCenter: parent.horizontalCenter
+            anchors.top: noMic.bottom
+            anchors.topMargin: 48 * virtualstudio.uiScale
+        }
+
+        Text {
+            id: noMicSubheader1
+            text: "JackTrip requires permission to use your microphone."
+            font { family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale }
+            color: textColour
+            width: 400
+            wrapMode: Text.Wrap
+            horizontalAlignment: Text.AlignHCenter
+            anchors.horizontalCenter: parent.horizontalCenter
+            anchors.top: noMicHeader.bottom
+            anchors.topMargin: 32 * virtualstudio.uiScale
+        }
+
+        Text {
+            id: noMicSubheader2
+            text: "Click 'Open Privacy Settings' to give JackTrip permission to access your microphone, instrument, or other audio device."
+            font { family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale }
+            color: textColour
+            width: 400
+            wrapMode: Text.Wrap
+            horizontalAlignment: Text.AlignHCenter
+            anchors.horizontalCenter: parent.horizontalCenter
+            anchors.top: noMicSubheader1.bottom
+            anchors.topMargin: 24 * virtualstudio.uiScale
+        }
+    }
+
+    Connections {
+        target: permissions
+
+        function onMicPermissionUpdated() {
+            if (permissions.micPermission === "granted") {
+                if (virtualstudio.studioToJoin.toString() === "") {
+                    virtualstudio.windowState = "browse";
+                } else if (virtualstudio.showDeviceSetup) {
+                    virtualstudio.windowState = "setup";
+                    audio.startAudio();
+                } else {
+                    virtualstudio.windowState = "connected";
+                    virtualstudio.joinStudio();
+                }
+            }
+        }
+    }
+
+}
diff --git a/src/gui/Recommendations.qml b/src/gui/Recommendations.qml
new file mode 100644 (file)
index 0000000..b0bf039
--- /dev/null
@@ -0,0 +1,550 @@
+import QtQuick
+import QtQuick.Controls
+
+Item {
+    width: parent.width; height: parent.height
+    clip: true
+
+    property int fontBig: 20
+    property int fontMedium: 13
+    property int fontSmall: 11
+    property int fontExtraSmall: 8
+
+    property int buttonWidth: 103
+    property int buttonHeight: 25
+
+    property int leftMargin: 48
+    property int rightMargin: 16
+
+    property string backgroundColour: virtualstudio.darkMode ? "#272525" : "#FAFBFB"
+    property string textColour: virtualstudio.darkMode ? "#FAFBFB" : "#0F0D0D"
+    property string buttonColour: virtualstudio.darkMode ? "#494646" : "#EAECEC"
+    property string buttonHoverColour: virtualstudio.darkMode ? "#5B5858" : "#D3D4D4"
+    property string buttonPressedColour: virtualstudio.darkMode ? "#524F4F" : "#DEE0E0"
+    property string buttonStroke: virtualstudio.darkMode ? "#80827D7D" : "#34979797"
+    property string buttonHoverStroke: virtualstudio.darkMode ? "#7B7777" : "#BABCBC"
+    property string buttonPressedStroke: virtualstudio.darkMode ? "#827D7D" : "#BABCBC"
+    property string saveButtonBackgroundColour: "#F2F3F3"
+    property string saveButtonPressedColour: "#E7E8E8"
+    property string saveButtonStroke: "#EAEBEB"
+    property string saveButtonPressedStroke: "#B0B5B5"
+    property string recommendationText: "#DB0A0A"
+    property string saveButtonText: "#DB0A0A"
+    property string checkboxStroke: "#0062cc"
+    property string checkboxPressedStroke: "#007AFF"
+    property string disabledButtonText: "#D3D4D4"
+    property string linkText: virtualstudio.darkMode ? "#8B8D8D" : "#272525"
+
+    property bool currShowRecommendations: virtualstudio.showWarnings
+    property string recommendationScreen: virtualstudio.showWarnings ? "ethernet" : ( permissions.micPermission == "unknown" ? "microphone" : "acknowledged")
+    property bool onWindows: Qt.platform.os === "windows"
+
+    Rectangle {
+        id: recommendationsHeader
+        x: -1
+        y: 0
+
+        width: parent.width + 2
+        height: 64
+
+        color: backgroundColour
+        border.color: "#33979797"
+
+        Image {
+            source: virtualstudio.darkMode ? "jacktrip white.png" : "jacktrip.png"
+            anchors.left: parent.left
+            anchors.leftMargin: 32 * virtualstudio.uiScale
+            anchors.verticalCenter: parent.verticalCenter
+            width: 119 * virtualstudio.uiScale; height: 28 * virtualstudio.uiScale
+        }
+
+        Text {
+            id: gettingStartedText1
+            visible: recommendationScreen === "ethernet"
+            text: "Getting Started with JackTrip (1/5)"
+            font { family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale }
+            color: textColour
+            anchors.right: parent.right
+            anchors.rightMargin: 32 * virtualstudio.uiScale
+            anchors.verticalCenter: parent.verticalCenter
+        }
+        
+        Text {
+            id: gettingStartedText2
+            visible: recommendationScreen === "fiber"
+            text: "Getting Started with JackTrip (2/5)"
+            font { family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale }
+            color: textColour
+            anchors.right: parent.right
+            anchors.rightMargin: 32 * virtualstudio.uiScale
+            anchors.verticalCenter: parent.verticalCenter
+        }
+
+        Text {
+            id: gettingStartedText3
+            visible: recommendationScreen === "audiointerface"
+            text: "Getting Started with JackTrip (3/5)"
+            font { family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale }
+            color: textColour
+            anchors.right: parent.right
+            anchors.rightMargin: 32 * virtualstudio.uiScale
+            anchors.verticalCenter: parent.verticalCenter
+        }
+
+        Text {
+            id: gettingStartedText4
+            visible: recommendationScreen === "headphones"
+            text: "Getting Started with JackTrip (4/5)"
+            font { family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale }
+            color: textColour
+            anchors.right: parent.right
+            anchors.rightMargin: 32 * virtualstudio.uiScale
+            anchors.verticalCenter: parent.verticalCenter
+        }
+
+        Text {
+            id: gettingStartedText5
+            visible: recommendationScreen === "acknowledged"
+            text: "Getting Started with JackTrip (5/5)"
+            font { family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale }
+            color: textColour
+            anchors.right: parent.right
+            anchors.rightMargin: 32 * virtualstudio.uiScale
+            anchors.verticalCenter: parent.verticalCenter
+        }
+    }
+
+    Item {
+        id: ethernetRecommendationItem
+        width: parent.width; height: parent.height
+        visible: recommendationScreen == "ethernet"
+
+        AppIcon {
+            id: ethernetRecommendationLogo
+            y: 120
+            anchors.horizontalCenter: parent.horizontalCenter
+            width: 179
+            height: 128
+            icon.source: "ethernet.svg"
+        }
+
+        Text {
+            id: ethernetRecommendationHeader1
+            text: "Wired Ethernet Recommended"
+            font { family: "Poppins"; weight: Font.Bold; pixelSize: fontMedium * virtualstudio.fontScale * virtualstudio.uiScale }
+            color: textColour
+            anchors.horizontalCenter: parent.horizontalCenter
+            anchors.top: ethernetRecommendationLogo.bottom
+            anchors.topMargin: 32 * virtualstudio.uiScale
+        }
+
+        Text {
+            id: ethernetRecommendationSubheader1
+            text: "JackTrip works best when you connect your computer directly to your Internet router via a wired ethernet cable."
+                + "<br/><br/>"
+                + "WiFi works OK for some people, but generates significantly more latency and audio glitches."
+            font { family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale }
+            color: textColour
+            width: 560
+            wrapMode: Text.Wrap
+            horizontalAlignment: Text.AlignHCenter
+            anchors.horizontalCenter: parent.horizontalCenter
+            anchors.top: ethernetRecommendationHeader1.bottom
+            anchors.topMargin: 32 * virtualstudio.uiScale
+        }
+
+        Button {
+            id: okButtonEthernet
+            background: Rectangle {
+                radius: 6 * virtualstudio.uiScale
+                color: okButtonEthernet.down ? saveButtonPressedColour : saveButtonBackgroundColour
+                border.width: 1
+                border.color: okButtonEthernet.down || okButtonEthernet.hovered ? saveButtonPressedStroke : saveButtonStroke
+                layer.enabled: okButtonEthernet.hovered && !okButtonEthernet.down
+            }
+            onClicked: { recommendationScreen = "fiber" }
+            anchors.right: parent.right
+            anchors.rightMargin: 16 * virtualstudio.uiScale
+            anchors.bottomMargin: 16 * virtualstudio.uiScale
+            anchors.bottom: parent.bottom
+            width: 150 * virtualstudio.uiScale; height: 30 * virtualstudio.uiScale
+            Text {
+                text: "Continue"
+                font.family: "Poppins"
+                font.pixelSize: 11 * virtualstudio.fontScale * virtualstudio.uiScale
+                font.weight: Font.Bold
+                color: saveButtonText
+                anchors.horizontalCenter: parent.horizontalCenter
+                anchors.verticalCenter: parent.verticalCenter
+            }
+        }
+    }
+
+    Item {
+        id: fiberRecommendationItem
+        width: parent.width; height: parent.height
+        visible: recommendationScreen == "fiber"
+
+        AppIcon {
+            id: fiberRecommendationLogo
+            y: 120
+            anchors.horizontalCenter: parent.horizontalCenter
+            width: 179
+            height: 128
+            icon.source: "networkCheck.svg"
+        }
+
+        Text {
+            id: fiberRecommendationHeader
+            text: "Fiber Internet Recommended"
+            font { family: "Poppins"; weight: Font.Bold; pixelSize: fontMedium * virtualstudio.fontScale * virtualstudio.uiScale }
+            color: textColour
+            anchors.horizontalCenter: parent.horizontalCenter
+            anchors.top: fiberRecommendationLogo.bottom
+            anchors.topMargin: 32 * virtualstudio.uiScale
+        }
+
+        Text {
+            id: fiberRecommendationSubheader
+            text: "A Fiber Internet connection from your Internet Service Provider (ISP) will give you the best experience while using JackTrip."
+                + "<br/><br/>"
+                + "It's OK to use JackTrip with Cable and DSL, but these types of Internet connections introduce significantly more latency."
+            font { family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale }
+            color: textColour
+            width: 560
+            wrapMode: Text.Wrap
+            horizontalAlignment: Text.AlignHCenter
+            anchors.horizontalCenter: parent.horizontalCenter
+            anchors.top: fiberRecommendationHeader.bottom
+            anchors.topMargin: 32 * virtualstudio.uiScale
+        }
+
+
+        Button {
+            id: okButtonFiber
+            background: Rectangle {
+                radius: 6 * virtualstudio.uiScale
+                color: okButtonFiber.down ? saveButtonPressedColour : saveButtonBackgroundColour
+                border.width: 1
+                border.color: okButtonFiber.down || okButtonFiber.hovered ? saveButtonPressedStroke : saveButtonStroke
+                layer.enabled: okButtonFiber.hovered && !okButtonFiber.down
+            }
+            onClicked: { recommendationScreen = "audiointerface" }
+            anchors.right: parent.right
+            anchors.rightMargin: 16 * virtualstudio.uiScale
+            anchors.bottomMargin: 16 * virtualstudio.uiScale
+            anchors.bottom: parent.bottom
+            width: 150 * virtualstudio.uiScale; height: 30 * virtualstudio.uiScale
+            Text {
+                text: "Continue"
+                font.family: "Poppins"
+                font.pixelSize: 11 * virtualstudio.fontScale * virtualstudio.uiScale
+                font.weight: Font.Bold
+                color: saveButtonText
+                anchors.horizontalCenter: parent.horizontalCenter
+                anchors.verticalCenter: parent.verticalCenter
+            }
+        }
+    }
+
+    Item {
+        id: headphoneRecommendationItem
+        width: parent.width; height: parent.height
+        visible: recommendationScreen == "headphones"
+
+        AppIcon {
+            id: headphoneWarningLogo
+            y: 120
+            anchors.horizontalCenter: parent.horizontalCenter
+            width: 118
+            height: 128
+            icon.source: "headphones.svg"
+        }
+
+        Text {
+            id: headphoneRecommendationHeader1
+            text: "Wired Headphones Required"
+            font { family: "Poppins"; weight: Font.Bold; pixelSize: fontMedium * virtualstudio.fontScale * virtualstudio.uiScale }
+            color: textColour
+            anchors.horizontalCenter: parent.horizontalCenter
+            anchors.top: headphoneWarningLogo.bottom
+            anchors.topMargin: 32 * virtualstudio.uiScale
+        }
+
+        Text {
+            id: headphoneRecommendationSubheader1
+            text: "JackTrip requires the use of wired headphones."
+                + "<br/><br/>"
+                + "Using speakers will generate echos and loud feedback loops."
+                + "<br/><br/>"
+                + "Wireless and bluetooth headphones introduce higher latency."
+            font { family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale }
+            color: textColour
+            width: 560
+            wrapMode: Text.Wrap
+            horizontalAlignment: Text.AlignHCenter
+            anchors.horizontalCenter: parent.horizontalCenter
+            anchors.top: headphoneRecommendationHeader1.bottom
+            anchors.topMargin: 32 * virtualstudio.uiScale
+        }
+
+        Button {
+            id: okButtonHeadphones
+            background: Rectangle {
+                radius: 6 * virtualstudio.uiScale
+                color: okButtonHeadphones.down ? saveButtonPressedColour : saveButtonBackgroundColour
+                border.width: 1
+                border.color: okButtonHeadphones.down || okButtonHeadphones.hovered ? saveButtonPressedStroke : saveButtonStroke
+                layer.enabled: okButtonHeadphones.hovered && !okButtonHeadphones.down
+            }
+            onClicked: {
+                recommendationScreen = "acknowledged";
+            }
+            anchors.right: parent.right
+            anchors.rightMargin: 16 * virtualstudio.uiScale
+            anchors.bottomMargin: 16 * virtualstudio.uiScale
+            anchors.bottom: parent.bottom
+            width: 150 * virtualstudio.uiScale; height: 30 * virtualstudio.uiScale
+            Text {
+                text: "Continue"
+                font.family: "Poppins"
+                font.pixelSize: 11 * virtualstudio.fontScale * virtualstudio.uiScale
+                font.weight: Font.Bold
+                color: saveButtonText
+                anchors.horizontalCenter: parent.horizontalCenter
+                anchors.verticalCenter: parent.verticalCenter
+            }
+        }
+    }
+
+    Item {
+        id: audioInterfaceRecommendationItem
+        width: parent.width; height: parent.height
+        visible: recommendationScreen == "audiointerface"
+
+        AppIcon {
+            id: audioInterfaceRecommendationLogo
+            y: 120
+            anchors.horizontalCenter: parent.horizontalCenter
+            width: 118
+            height: 128
+            icon.source: "externalMic.svg"
+        }
+
+        Text {
+            id: audioInterfaceRecommendationHeaderNonWindows
+            visible: !onWindows
+            text: "External Audio Device Recommended"
+            font { family: "Poppins"; weight: Font.Bold; pixelSize: fontMedium * virtualstudio.fontScale * virtualstudio.uiScale }
+            color: textColour
+            anchors.horizontalCenter: parent.horizontalCenter
+            anchors.top: audioInterfaceRecommendationLogo.bottom
+            anchors.topMargin: 32 * virtualstudio.uiScale
+        }
+
+        Text {
+            id: audioInterfaceRecommendationSubheaderNonWindows
+            visible: !onWindows
+            text: "Your audio device controls the quality of sound, and can also have a big impact on latency."
+                + "<br/><br/>"
+                + "It's OK to use the audio device that is built into your computer, but external USB and "
+                + "Thunderbolt audio interfaces will usually produce better quality and lower latency."
+            font { family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale }
+            color: textColour
+            width: 560
+            wrapMode: Text.Wrap
+            horizontalAlignment: Text.AlignHCenter
+            anchors.horizontalCenter: parent.horizontalCenter
+            anchors.top: audioInterfaceRecommendationHeaderNonWindows.bottom
+            anchors.topMargin: 32 * virtualstudio.uiScale
+        }
+
+        Text {
+            id: audioInterfaceRecommendationHeaderWindows
+            visible: onWindows
+            text: "External Audio Device Recommended"
+            font { family: "Poppins"; weight: Font.Bold; pixelSize: fontMedium * virtualstudio.fontScale * virtualstudio.uiScale }
+            color: textColour
+            anchors.horizontalCenter: parent.horizontalCenter
+            anchors.top: audioInterfaceRecommendationLogo.bottom
+            anchors.topMargin: 32 * virtualstudio.uiScale
+        }
+
+        Text {
+            id: audioInterfaceRecommendationSubheaderWindows
+            visible: onWindows
+            text: "Your audio device controls the quality of sound, and can also have a big impact on latency."
+                + "<br/><br/>"
+                + "Additionally, low latency on Windows requires the use of ASIO drivers. "
+                + "Beware that using ASIO drivers which are not made specifically for your device can cause crashes."
+                + "<br/><br/>"
+                + "It's OK to use the audio device that is built into your computer, but external USB and "
+                + "Thunderbolt audio interfaces that provide ASIO drivers will produce better quality and much lower latency."
+            font { family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale }
+            color: textColour
+            width: 560
+            wrapMode: Text.Wrap
+            horizontalAlignment: Text.AlignHCenter
+            anchors.horizontalCenter: parent.horizontalCenter
+            anchors.top: audioInterfaceRecommendationHeaderWindows.bottom
+            anchors.topMargin: 32 * virtualstudio.uiScale
+        }
+
+        Button {
+            id: okButtonAudioInterface
+            background: Rectangle {
+                radius: 6 * virtualstudio.uiScale
+                color: okButtonAudioInterface.down ? saveButtonPressedColour : saveButtonBackgroundColour
+                border.width: 1
+                border.color: okButtonAudioInterface.down || okButtonAudioInterface.hovered ? saveButtonPressedStroke : saveButtonStroke
+                layer.enabled: okButtonAudioInterface.hovered && !okButtonAudioInterface.down
+            }
+            onClicked: {
+                recommendationScreen = "headphones";
+            }
+            anchors.right: parent.right
+            anchors.rightMargin: 16 * virtualstudio.uiScale
+            anchors.bottomMargin: 16 * virtualstudio.uiScale
+            anchors.bottom: parent.bottom
+            width: 150 * virtualstudio.uiScale; height: 30 * virtualstudio.uiScale
+            Text {
+                text: "Continue"
+                font.family: "Poppins"
+                font.pixelSize: 11 * virtualstudio.fontScale * virtualstudio.uiScale
+                font.weight: Font.Bold
+                color: saveButtonText
+                anchors.horizontalCenter: parent.horizontalCenter
+                anchors.verticalCenter: parent.verticalCenter
+            }
+        }
+    }
+
+    Item {
+        id: acknowledgedRecommendationItem
+        width: parent.width; height: parent.height
+        visible: recommendationScreen == "acknowledged"
+
+        Text {
+            id: acknowledgedHeader
+            text: "Remind Me Again Next Time?"
+            font { family: "Poppins"; weight: Font.Bold; pixelSize: fontMedium * virtualstudio.fontScale * virtualstudio.uiScale }
+            color: textColour
+            anchors.horizontalCenter: parent.horizontalCenter
+            anchors.top: parent.top
+            anchors.topMargin: 176 * virtualstudio.uiScale
+        }
+
+        Text {
+            id: acknowledgedSubheader
+            text: "Would you like to review the getting started recommendations again the next time you start JackTrip?"
+            font { family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale }
+            color: textColour
+            width: 560
+            wrapMode: Text.Wrap
+            horizontalAlignment: Text.AlignHCenter
+            anchors.horizontalCenter: parent.horizontalCenter
+            anchors.top: acknowledgedHeader.bottom
+            anchors.topMargin: 32 * virtualstudio.uiScale
+        }
+
+        Item {
+            id: acknowledgedButtonsContainer
+            width: 320
+
+            anchors.top: acknowledgedSubheader.bottom
+            anchors.topMargin: 64 * virtualstudio.uiScale
+            anchors.horizontalCenter: parent.horizontalCenter
+
+            Button {
+                id: acknowledgedYesButton
+                anchors.left: parent.left
+                anchors.verticalCenter: parent.verticalCenter
+                background: Rectangle {
+                    radius: 6 * virtualstudio.uiScale
+                    color: acknowledgedYesButton.down ? saveButtonPressedColour : saveButtonBackgroundColour
+                    border.width: 1
+                    border.color: acknowledgedYesButton.down || acknowledgedYesButton.hovered ? saveButtonPressedStroke : saveButtonStroke
+                    layer.enabled: acknowledgedYesButton.hovered && !acknowledgedYesButton.down
+                }
+                onClicked: {
+                    virtualstudio.showWarnings = true;
+                    virtualstudio.saveSettings();
+                    if (permissions.micPermission !== "granted") {
+                        virtualstudio.windowState = "permissions";
+                    } else if (virtualstudio.studioToJoin.toString() === "") {
+                        virtualstudio.windowState = "browse";
+                    } else if (virtualstudio.showDeviceSetup) {
+                        virtualstudio.windowState = "setup";
+                        audio.startAudio();
+                    } else {
+                        virtualstudio.windowState = "connected";
+                        virtualstudio.joinStudio();
+                    }
+                }
+                width: 150 * virtualstudio.uiScale; height: 30 * virtualstudio.uiScale
+                Text {
+                    text: "Yes"
+                    font.family: "Poppins"
+                    font.pixelSize: 11 * virtualstudio.fontScale * virtualstudio.uiScale
+                    font.weight: Font.Bold
+                    color: saveButtonText
+                    anchors.horizontalCenter: parent.horizontalCenter
+                    anchors.verticalCenter: parent.verticalCenter
+                }
+            }
+
+
+            Button {
+                id: acknowledgedNoButton
+                anchors.right: parent.right
+                anchors.verticalCenter: parent.verticalCenter
+                background: Rectangle {
+                    radius: 6 * virtualstudio.uiScale
+                    color: acknowledgedNoButton.down ? saveButtonPressedColour : saveButtonBackgroundColour
+                    border.width: 1
+                    border.color: acknowledgedNoButton.down || acknowledgedNoButton.hovered ? saveButtonPressedStroke : saveButtonStroke
+                    layer.enabled: acknowledgedNoButton.hovered && !acknowledgedNoButton.down
+                }
+                onClicked: {
+                    virtualstudio.showWarnings = false;
+                    virtualstudio.saveSettings();
+                    if (permissions.micPermission !== "granted") {
+                        virtualstudio.windowState = "permissions";
+                    } else if (virtualstudio.studioToJoin.toString() === "") {
+                        virtualstudio.windowState = "browse";
+                    } else if (virtualstudio.showDeviceSetup) {
+                        virtualstudio.windowState = "setup";
+                        audio.startAudio();
+                    } else {
+                        virtualstudio.windowState = "connected";
+                        virtualstudio.joinStudio();
+                    }
+                }
+                width: 150 * virtualstudio.uiScale; height: 30 * virtualstudio.uiScale
+                Text {
+                    text: "No"
+                    font.family: "Poppins"
+                    font.pixelSize: 11 * virtualstudio.fontScale * virtualstudio.uiScale
+                    font.weight: Font.Bold
+                    color: saveButtonText
+                    anchors.horizontalCenter: parent.horizontalCenter
+                    anchors.verticalCenter: parent.verticalCenter
+                }
+            }
+        }
+
+
+        Text {
+            id: acknowledgedSettingsInfo
+            text: "You can change this setting at any time under <b>Settings > Advanced</b>"
+            font { family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale }
+            color: textColour
+            width: 560
+            wrapMode: Text.Wrap
+            horizontalAlignment: Text.AlignHCenter
+            anchors.horizontalCenter: parent.horizontalCenter
+            anchors.top: acknowledgedButtonsContainer.bottom
+            anchors.topMargin: 64 * virtualstudio.uiScale
+        }
+    }
+}
index e4ab9eff033588bb3640f44e60c1dca7b8f82dc3..84568df2da9bac18172a1322a9b3115554494164 100644 (file)
@@ -1,5 +1,5 @@
-import QtQuick 2.12
-import QtQuick.Controls 2.12
+import QtQuick
+import QtQuick.Controls
 
 Rectangle {
   property string filterStroke: virtualstudio.darkMode ? "#827D7D" : "#BABCBC"
index f7c12e6374df25b730a9637bc956bd155c0476b1..312f56837f9c07cd49f240745a509dff78ff281e 100644 (file)
@@ -1,6 +1,5 @@
-import QtQuick 2.12
-import QtQuick.Controls 2.12
-import QtGraphicalEffects 1.12
+import QtQuick
+import QtQuick.Controls
 
 Item {
     width: parent.width; height: parent.height
@@ -34,6 +33,8 @@ Item {
     property string sliderTrackColour: virtualstudio.darkMode ? "#5B5858" : "light gray"
     property string sliderActiveTrackColour: virtualstudio.darkMode ? "light gray" : "black"
     property string warningTextColour: "#DB0A0A"
+    property string checkboxStroke: "#0062cc"
+    property string checkboxPressedStroke: "#007AFF"
     property string linkText: virtualstudio.darkMode ? "#8B8D8D" : "#272525"
 
     property string errorFlagColour: "#DB0A0A"
@@ -41,32 +42,20 @@ Item {
 
     property string settingsGroupView: "Audio"
 
-    property int inputCurrIndex: getCurrentInputDeviceIndex()
-    property int outputCurrIndex: getCurrentOutputDeviceIndex()
-
-    function getCurrentInputDeviceIndex () {
-        if (virtualstudio.inputDevice === "") {
-            return virtualstudio.inputComboModel.findIndex(elem => elem.type === "element");
-        }
-
-        let idx = virtualstudio.inputComboModel.findIndex(elem => elem.type === "element" && elem.text === virtualstudio.inputDevice);
+    function getCurrentBufferSizeIndex () {
+        let bufferSize = audio.bufferSize;
+        let idx = audio.bufferSizeComboModel.findIndex(elem => parseInt(elem) === bufferSize);
         if (idx < 0) {
-            idx = virtualstudio.inputComboModel.findIndex(elem => elem.type === "element");
+            idx = 0;
         }
-
         return idx;
     }
 
-    function getCurrentOutputDeviceIndex() {
-        if (virtualstudio.outputDevice === "") {
-            return virtualstudio.outputComboModel.findIndex(elem => elem.type === "element");
-        }
-
-        let idx = virtualstudio.outputComboModel.findIndex(elem => elem.type === "element" && elem.text === virtualstudio.outputDevice);
+    function getCurrentAudioBackendIndex () {
+        let idx = audio.audioBackendComboModel.findIndex(elem => elem === audio.audioBackend);
         if (idx < 0) {
-            idx = virtualstudio.outputComboModel.findIndex(elem => elem.type === "element");
+            idx = 0;
         }
-
         return idx;
     }
 
@@ -78,9 +67,18 @@ Item {
         y: header.height
         visible: settingsGroupView == "Audio"
 
-        AudioSettings{
+        AudioSettings {
             id: audioSettings
         }
+
+        DeviceWarning {
+            id: deviceWarning
+            anchors.left: parent.left
+            anchors.leftMargin: 168 * virtualstudio.uiScale
+            anchors.bottom: parent.bottom
+            anchors.bottomMargin: 48 * virtualstudio.uiScale
+            visible: Boolean(audio.devicesError) || Boolean(audio.devicesWarning)
+        }
     }
 
     ToolBar {
@@ -98,9 +96,6 @@ Item {
             id: headerContent
             width: header.width
             height: header.height
-            x: 16 * virtualstudio.uiScale; y: 32 * virtualstudio.uiScale
-
-            property bool isUsingRtAudio: virtualstudio.audioBackend == "RtAudio"
 
             Label {
                 id: pageTitle
@@ -115,41 +110,25 @@ Item {
                 color: textColour
             }
 
-            Button {
+            DeviceRefreshButton {
                 id: refreshButton
-                text: "Refresh Devices"
                 anchors.verticalCenter: pageTitle.verticalCenter;
                 anchors.right: headerContent.right;
                 anchors.rightMargin: 16 * virtualstudio.uiScale;
-
-                palette.buttonText: textColour
-                background: Rectangle {
-                    radius: 6 * virtualstudio.uiScale
-                    color: refreshButton.down ? buttonPressedColour : (refreshButton.hovered ? buttonHoverColour : buttonColour)
-                    border.width: 1
-                    border.color: refreshButton.down ? buttonPressedStroke : (refreshButton.hovered ? buttonHoverStroke : buttonStroke)
-                }
-                icon {
-                    source: "refresh.svg";
-                    color: textColour;
-                }
-                display: AbstractButton.TextBesideIcon
-                onClicked: {
-                    virtualstudio.refreshDevices();
-                    inputCurrIndex = getCurrentInputDeviceIndex();
-                    outputCurrIndex = getCurrentOutputDeviceIndex();
-                }
-                width: 144 * virtualstudio.uiScale; height: 30 * virtualstudio.uiScale
-                font {
-                    family: "Poppins"
-                    pixelSize: fontExtraSmall * virtualstudio.fontScale * virtualstudio.uiScale
-                }
-                visible: parent.isUsingRtAudio && settingsGroupView == "Audio"
+                visible: audio.audioBackend == "RtAudio" && settingsGroupView == "Audio"
+                enabled: audio.audioReady && !audio.scanningDevices
             }
 
+            Text {
+                text: "Restarting Audio"
+                anchors.verticalCenter: pageTitle.verticalCenter;
+                anchors.right: refreshButton.left;
+                anchors.rightMargin: 16 * virtualstudio.uiScale;
+                font { family: "Poppins"; pixelSize: fontExtraSmall * virtualstudio.fontScale * virtualstudio.uiScale }
+                color: textColour
+                visible: !audio.audioReady
+            }
         }
-
-
     }
 
     Drawer {
@@ -168,7 +147,9 @@ Item {
 
         ButtonGroup {
             buttons: viewControls.children
-            onClicked: { settingsGroupView = button.text }
+            onClicked: function(button) {
+                settingsGroupView = button.text
+            }
         }
 
         Column {
@@ -187,7 +168,7 @@ Item {
                     Label {
                         id: audioButtonText
                         text: audioBtn.text
-                        width: Boolean(virtualstudio.devicesError) ? parent.width - 16 * virtualstudio.uiScale : parent.width
+                        width: Boolean(audio.devicesError) ? parent.width - 16 * virtualstudio.uiScale : parent.width
                         font { family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale }
                         horizontalAlignment: Text.AlignHCenter
                         verticalAlignment: Text.AlignVCenter
@@ -203,7 +184,7 @@ Item {
                         height: 8 * virtualstudio.uiScale
                         color: errorFlagColour
                         radius: 4 * virtualstudio.uiScale
-                        visible: Boolean(virtualstudio.devicesError)
+                        visible: Boolean(audio.devicesError)
                     }
                 }
                 background: Rectangle {
@@ -317,7 +298,7 @@ Item {
             id: scaleSlider
             x: 234 * virtualstudio.uiScale; y: 100 * virtualstudio.uiScale
             width: backendCombo.width
-            from: 1; to: 2; value: virtualstudio.uiScale
+            from: 1; to: 1.25; value: virtualstudio.uiScale
             onMoved: { virtualstudio.uiScale = value }
 
             background: Rectangle {
@@ -404,10 +385,10 @@ Item {
             }
             onClicked: {
                 // essentially the same here as clicking the cancel button
+                audio.stopAudio();
                 virtualstudio.windowState = "browse";
-                inputCurrIndex = virtualstudio.previousInput;
-                outputCurrIndex = virtualstudio.previousOutput;
-                virtualstudio.revertSettings();
+                virtualstudio.loadSettings();
+                audio.validateDevices();
 
                 // switch mode
                 virtualstudio.toStandard();
@@ -434,7 +415,7 @@ Item {
             id: updateChannelCombo
             x: 234 * virtualstudio.uiScale; y: modeButton.y + (48 * virtualstudio.uiScale)
             width: parent.width - x - (16 * virtualstudio.uiScale); height: 36 * virtualstudio.uiScale
-            model: updateChannelComboModel
+            model: virtualstudio.updateChannelComboModel
             currentIndex: virtualstudio.updateChannel == "stable" ? 0 : 1
             onActivated: { virtualstudio.updateChannel = currentIndex == 0 ? "stable": "edge" }
             font.family: "Poppins"
@@ -452,12 +433,15 @@ Item {
 
         ComboBox {
             id: backendCombo
-            model: backendComboModel
-            currentIndex: virtualstudio.audioBackend == "JACK" ? 0 : 1
-            onActivated: { virtualstudio.audioBackend = currentText }
+            model: audio.audioBackendComboModel
+            enabled: audio.audioBackendComboModel.length > 1
+            currentIndex: getCurrentAudioBackendIndex()
+            onActivated: {
+                audio.audioBackend = currentText
+                audio.restartAudio();
+            }
             x: 234 * virtualstudio.uiScale; y: updateChannelCombo.y + (48 * virtualstudio.uiScale)
             width: updateChannelCombo.width; height: updateChannelCombo.height
-            visible: virtualstudio.selectableBackend
         }
 
         Text {
@@ -466,7 +450,6 @@ Item {
             x: leftMargin * virtualstudio.uiScale
             text: "Audio Backend"
             font { family: "Poppins"; pixelSize: fontMedium * virtualstudio.fontScale * virtualstudio.uiScale }
-            visible: virtualstudio.selectableBackend
             color: textColour
         }
 
@@ -474,11 +457,14 @@ Item {
             id: bufferCombo
             x: 234 * virtualstudio.uiScale; y: backendCombo.y + (48 * virtualstudio.uiScale)
             width: backendCombo.width; height: updateChannelCombo.height
-            model: bufferComboModel
-            currentIndex: virtualstudio.bufferSize
-            onActivated: { virtualstudio.bufferSize = currentIndex }
+            model: audio.bufferSizeComboModel
+            currentIndex: getCurrentBufferSizeIndex()
+            onActivated: {
+                audio.bufferSize = parseInt(currentText, 10);
+                audio.restartAudio();
+            }
             font.family: "Poppins"
-            visible: virtualstudio.audioBackend != "JACK"
+            enabled: audio.audioBackend != "JACK"
         }
 
         Text {
@@ -486,7 +472,7 @@ Item {
             x: 48 * virtualstudio.uiScale
             text: "Buffer Size"
             font { family: "Poppins"; pixelSize: fontMedium * virtualstudio.fontScale * virtualstudio.uiScale }
-            visible: virtualstudio.audioBackend != "JACK"
+            visible: audio.audioBackend != "JACK"
             color: textColour
         }
 
@@ -494,9 +480,9 @@ Item {
             id: bufferStrategyCombo
             x: updateChannelCombo.x; y: bufferCombo.y + (48 * virtualstudio.uiScale)
             width: updateChannelCombo.width; height: updateChannelCombo.height
-            model: bufferStrategyComboModel
-            currentIndex: virtualstudio.bufferStrategy
-            onActivated: { virtualstudio.bufferStrategy = currentIndex }
+            model: audio.bufferStrategyComboModel
+            currentIndex: audio.bufferStrategy
+            onActivated: { audio.bufferStrategy = currentIndex }
             font.family: "Poppins"
         }
 
@@ -512,13 +498,13 @@ Item {
             id: feedbackDetectionCombo
             x: updateChannelCombo.x; y: bufferStrategyCombo.y + (48 * virtualstudio.uiScale)
             width: updateChannelCombo.width; height: updateChannelCombo.height
-            model: feedbackDetectionComboModel
-            currentIndex: virtualstudio.feedbackDetectionEnabled ? 0 : 1
+            model: audio.feedbackDetectionComboModel
+            currentIndex: audio.feedbackDetectionEnabled ? 0 : 1
             onActivated: {
                 if (currentIndex === 1) {
-                    virtualstudio.feedbackDetectionEnabled = false;
+                    audio.feedbackDetectionEnabled = false;
                 } else {
-                    virtualstudio.feedbackDetectionEnabled = true;
+                    audio.feedbackDetectionEnabled = true;
                 }
             }
             font.family: "Poppins"
@@ -532,6 +518,91 @@ Item {
             color: textColour
         }
 
+        CheckBox {
+            id: showStartupSetup
+            checked: virtualstudio.showDeviceSetup
+            text: qsTr("Show device setup screen before connecting to a studio")
+            x: updateChannelCombo.x; y: feedbackDetectionCombo.y + (48 * virtualstudio.uiScale)
+            onClicked: { virtualstudio.showDeviceSetup = showStartupSetup.checkState == Qt.Checked; }
+            indicator: Rectangle {
+                implicitWidth: 16 * virtualstudio.uiScale
+                implicitHeight: 16 * virtualstudio.uiScale
+                x: showStartupSetup.leftPadding
+                y: parent.height / 2 - height / 2
+                radius: 3 * virtualstudio.uiScale
+                border.color: showStartupSetup.down || showStartupSetup.hovered ? checkboxPressedStroke : checkboxStroke
+
+                Rectangle {
+                    width: 10 * virtualstudio.uiScale
+                    height: 10 * virtualstudio.uiScale
+                    x: 3 * virtualstudio.uiScale
+                    y: 3 * virtualstudio.uiScale
+                    radius: 2 * virtualstudio.uiScale
+                    color: showStartupSetup.down || showStartupSetup.hovered ? checkboxPressedStroke : checkboxStroke
+                    visible: showStartupSetup.checked
+                }
+            }
+            contentItem: Text {
+                text: showStartupSetup.text
+                font.family: "Poppins"
+                font.pixelSize: 10 * virtualstudio.fontScale * virtualstudio.uiScale
+                anchors.horizontalCenter: parent.horizontalCenter
+                anchors.verticalCenter: parent.verticalCenter
+                leftPadding: showStartupSetup.indicator.width + showStartupSetup.spacing
+                color: textColour
+            }
+        }
+
+        Text {
+            anchors.verticalCenter: showStartupSetup.verticalCenter
+            x: 48 * virtualstudio.uiScale
+            text: "Device Setup"
+            font { family: "Poppins"; pixelSize: fontMedium * virtualstudio.fontScale * virtualstudio.uiScale }
+            color: textColour
+        }
+
+        CheckBox {
+            id: showStartupWarnings
+            checked: virtualstudio.showWarnings
+            text: qsTr("Show recommendations on startup again next time")
+            x: updateChannelCombo.x; y: showStartupSetup.y + (48 * virtualstudio.uiScale)
+            onClicked: { virtualstudio.showWarnings = showStartupWarnings.checkState == Qt.Checked; }
+            indicator: Rectangle {
+                implicitWidth: 16 * virtualstudio.uiScale
+                implicitHeight: 16 * virtualstudio.uiScale
+                x: showStartupWarnings.leftPadding
+                y: parent.height / 2 - height / 2
+                radius: 3 * virtualstudio.uiScale
+                border.color: showStartupWarnings.down || showStartupWarnings.hovered ? checkboxPressedStroke : checkboxStroke
+
+                Rectangle {
+                    width: 10 * virtualstudio.uiScale
+                    height: 10 * virtualstudio.uiScale
+                    x: 3 * virtualstudio.uiScale
+                    y: 3 * virtualstudio.uiScale
+                    radius: 2 * virtualstudio.uiScale
+                    color: showStartupWarnings.down || showStartupWarnings.hovered ? checkboxPressedStroke : checkboxStroke
+                    visible: showStartupWarnings.checked
+                }
+            }
+            contentItem: Text {
+                text: showStartupWarnings.text
+                font.family: "Poppins"
+                font.pixelSize: 10 * virtualstudio.fontScale * virtualstudio.uiScale
+                anchors.horizontalCenter: parent.horizontalCenter
+                anchors.verticalCenter: parent.verticalCenter
+                leftPadding: showStartupWarnings.indicator.width + showStartupWarnings.spacing
+                color: textColour
+            }
+        }
+
+        Text {
+            anchors.verticalCenter: showStartupWarnings.verticalCenter
+            x: 48 * virtualstudio.uiScale
+            text: "Recommendations"
+            font { family: "Poppins"; pixelSize: fontMedium * virtualstudio.fontScale * virtualstudio.uiScale }
+            color: textColour
+        }
     }
 
     Rectangle {
@@ -618,7 +689,15 @@ Item {
                 border.width: 1
                 border.color: testModeButton.down ? buttonPressedStroke : (testModeButton.hovered ? buttonHoverStroke : buttonStroke)
             }
-            onClicked: { virtualstudio.testMode = !virtualstudio.testMode; }
+            onClicked: {
+                virtualstudio.testMode = !virtualstudio.testMode;
+
+                // behave like "Cancel" and switch back to browse mode
+                audio.stopAudio();
+                virtualstudio.windowState = "browse";
+                virtualstudio.loadSettings();
+                audio.validateDevices();
+            }
             anchors.horizontalCenter: parent.horizontalCenter
             y: logoutButton.y + (48 * virtualstudio.uiScale)
             width: 260 * virtualstudio.uiScale; height: 30 * virtualstudio.uiScale
@@ -647,10 +726,10 @@ Item {
                 border.color: cancelButton.down ? buttonPressedStroke : (cancelButton.hovered ? buttonHoverStroke : buttonStroke)
             }
             onClicked: {
+                audio.stopAudio();
                 virtualstudio.windowState = "browse";
-                inputCurrIndex = virtualstudio.previousInput;
-                outputCurrIndex = virtualstudio.previousOutput;
-                virtualstudio.revertSettings()
+                virtualstudio.loadSettings();
+                audio.validateDevices();
             }
             anchors.verticalCenter: parent.verticalCenter
             x: parent.width - (230 * virtualstudio.uiScale)
@@ -665,14 +744,18 @@ Item {
 
         Button {
             id: saveButton
-            enabled: !Boolean(virtualstudio.devicesError)
+            enabled: !Boolean(audio.devicesError)
             background: Rectangle {
                 radius: 6 * virtualstudio.uiScale
                 color: saveButton.down ? buttonPressedColour : (saveButton.hovered ? buttonHoverColour : buttonColour)
                 border.width: 1
                 border.color: saveButton.down ? buttonPressedStroke : (saveButton.hovered ? buttonHoverStroke : buttonStroke)
             }
-            onClicked: { virtualstudio.windowState = "browse"; virtualstudio.applySettings() }
+            onClicked: {
+                audio.stopAudio();
+                virtualstudio.windowState = "browse";
+                virtualstudio.saveSettings();
+            }
             anchors.verticalCenter: parent.verticalCenter
             x: parent.width - (119 * virtualstudio.uiScale)
             width: buttonWidth * virtualstudio.uiScale; height: buttonHeight * virtualstudio.uiScale
@@ -680,8 +763,20 @@ Item {
                 text: "Save"
                 font { family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale }
                 anchors { horizontalCenter: parent.horizontalCenter; verticalCenter: parent.verticalCenter }
-                color: Boolean(virtualstudio.devicesError) ? disabledButtonTextColour : textColour
+                color: Boolean(audio.devicesError) ? disabledButtonTextColour : textColour
             }
         }
     }
+
+    Connections {
+        target: audio
+        // anything that sets currentIndex to the value of a function needs
+        // to be manually updated whenever there is a change to any vars it uses
+        function onBufferSizeChanged() {
+            bufferCombo.currentIndex = getCurrentBufferSizeIndex();
+        }
+        function onAudioBackendChanged() {
+            backendCombo.currentIndex = getCurrentAudioBackendIndex();
+        }
+    }
 }
index 240062220970c5801ae4350bb174b2f7e2b5beff..dc045307901941b80517731d23c4629b0bc630be 100644 (file)
@@ -1,6 +1,5 @@
-import QtQuick 2.12
-import QtQuick.Controls 2.12
-import QtGraphicalEffects 1.12
+import QtQuick
+import QtQuick.Controls
 
 Item {
     width: parent.width; height: parent.height
@@ -11,550 +10,95 @@ Item {
     property int fontSmall: 11
     property int fontExtraSmall: 8
 
-    property int buttonWidth: 103
-    property int buttonHeight: 25
-
     property int leftMargin: 48
     property int rightMargin: 16
 
-    property string backgroundColour: virtualstudio.darkMode ? "#272525" : "#FAFBFB"
-    property real imageLightnessValue: virtualstudio.darkMode ? 1.0 : 0.0
     property string textColour: virtualstudio.darkMode ? "#FAFBFB" : "#0F0D0D"
     property string buttonColour: virtualstudio.darkMode ? "#494646" : "#EAECEC"
-    property string buttonHoverColour: virtualstudio.darkMode ? "#5B5858" : "#D3D4D4"
+    property string buttonHoverColour: virtualstudio.darkMode ? "#5B5858" : "#D3D4D4"  
     property string buttonPressedColour: virtualstudio.darkMode ? "#524F4F" : "#DEE0E0"
     property string buttonStroke: virtualstudio.darkMode ? "#80827D7D" : "#34979797"
-    property string buttonHoverStroke: virtualstudio.darkMode ? "#7B7777" : "#BABCBC"
+    property string buttonHoverStroke: virtualstudio.darkMode ? "#7B7777" : "#BABCBC"  
     property string buttonPressedStroke: virtualstudio.darkMode ? "#827D7D" : "#BABCBC"
-    property string sliderColour: virtualstudio.darkMode ? "#BABCBC" :  "#EAECEC"
-    property string sliderPressedColour: virtualstudio.darkMode ? "#ACAFAF" : "#DEE0E0"
-    property string sliderTrackColour: virtualstudio.darkMode ? "#5B5858" : "light gray"
-    property string sliderActiveTrackColour: virtualstudio.darkMode ? "light gray" : "black"
     property string saveButtonBackgroundColour: "#F2F3F3"
-    property string saveButtonPressedColour: "#E7E8E8"
+    property string saveButtonPressedColour: "#E7E8E8" 
     property string saveButtonStroke: "#EAEBEB"
     property string saveButtonPressedStroke: "#B0B5B5"
-    property string warningText: "#DB0A0A"
     property string saveButtonText: "#DB0A0A"
     property string checkboxStroke: "#0062cc"
     property string checkboxPressedStroke: "#007AFF"
-    property string disabledButtonText: "#D3D4D4"
-    property string linkText: virtualstudio.darkMode ? "#8B8D8D" : "#272525"
-
-    property bool currShowWarnings: virtualstudio.showWarnings
-    property string warningScreen: virtualstudio.showWarnings ? "ethernet" : ( permissions.micPermission == "unknown" ? "microphone" : "acknowledged")
+    property string disabledButtonText: virtualstudio.darkMode ? "#827D7D" : "#BABCBC"
 
     Item {
-        id: ethernetWarningItem
+        id: setupItem
         width: parent.width; height: parent.height
-        visible: warningScreen == "ethernet"
-
-        Image {
-            id: ethernetWarningLogo
-            source: "ethernet.png"
-            width: 179
-            height: 128
-            y: 60
-            anchors.horizontalCenter: parent.horizontalCenter
-        }
 
-        Colorize {
-            anchors.fill: ethernetWarningLogo
-            source: ethernetWarningLogo
-            hue: 0
-            saturation: 0
-            lightness: imageLightnessValue
-        }
+        property bool isUsingRtAudio: audio.audioBackend == "RtAudio"
 
         Text {
-            id: ethernetWarningHeader
-            text: "Connect via Wired Ethernet"
-            font { family: "Poppins"; weight: Font.Bold; pixelSize: fontMedium * virtualstudio.fontScale * virtualstudio.uiScale }
-            color: textColour
-            anchors.horizontalCenter: parent.horizontalCenter
-            anchors.top: ethernetWarningLogo.bottom
-            anchors.topMargin: 32 * virtualstudio.uiScale
-        }
-
-        Text {
-            id: ethernetWarningSubheader1
-            text: "JackTrip works best when you connect directly to your router via wired ethernet."
-            font { family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale }
-            color: textColour
-            width: 400
-            wrapMode: Text.Wrap
-            horizontalAlignment: Text.AlignHCenter
-            anchors.horizontalCenter: parent.horizontalCenter
-            anchors.top: ethernetWarningHeader.bottom
-            anchors.topMargin: 32 * virtualstudio.uiScale
-        }
-
-        Text {
-            id: ethernetWarningSubheader2
-            text: "WiFi works OK for some people, but you will experience higher latency and audio glitches."
-            font { family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale }
+            id: pageTitle
+            x: 16 * virtualstudio.uiScale; y: 32 * virtualstudio.uiScale
+            text: "Choose your audio devices"
+            font { family: "Poppins"; weight: Font.Bold; pixelSize: fontBig * virtualstudio.fontScale * virtualstudio.uiScale }
             color: textColour
-            width: 400
-            wrapMode: Text.Wrap
-            horizontalAlignment: Text.AlignHCenter
-            anchors.horizontalCenter: parent.horizontalCenter
-            anchors.top: ethernetWarningSubheader1.bottom
-            anchors.topMargin: 24 * virtualstudio.uiScale
         }
 
-        Button {
-            id: okButtonEthernet
-            background: Rectangle {
-                radius: 6 * virtualstudio.uiScale
-                color: okButtonEthernet.down ? saveButtonPressedColour : saveButtonBackgroundColour
-                border.width: 1
-                border.color: okButtonEthernet.down || okButtonEthernet.hovered ? saveButtonPressedStroke : saveButtonStroke
-                layer.enabled: okButtonEthernet.hovered && !okButtonEthernet.down
-            }
-            onClicked: { warningScreen = "headphones" }
+        DeviceRefreshButton {
+            id: refreshButton
             anchors.right: parent.right
-            anchors.rightMargin: 16 * virtualstudio.uiScale
-            anchors.bottomMargin: 16 * virtualstudio.uiScale
-            anchors.bottom: parent.bottom
-            width: 150 * virtualstudio.uiScale; height: 30 * virtualstudio.uiScale
-            Text {
-                text: "OK"
-                font.family: "Poppins"
-                font.pixelSize: 11 * virtualstudio.fontScale * virtualstudio.uiScale
-                font.weight: Font.Bold
-                color: saveButtonText
-                anchors.horizontalCenter: parent.horizontalCenter
-                anchors.verticalCenter: parent.verticalCenter
-            }
-        }
-
-        CheckBox {
-            id: showEthernetWarningCheckbox
-            checked: currShowWarnings
-            text: qsTr("Show warnings again next time")
-            anchors.right: okButtonEthernet.left
-            anchors.rightMargin: 16 * virtualstudio.uiScale
-            anchors.verticalCenter: okButtonEthernet.verticalCenter
-            onClicked: { currShowWarnings = showEthernetWarningCheckbox.checkState == Qt.Checked }
-            indicator: Rectangle {
-                implicitWidth: 16 * virtualstudio.uiScale
-                implicitHeight: 16 * virtualstudio.uiScale
-                x: showEthernetWarningCheckbox.leftPadding
-                y: parent.height / 2 - height / 2
-                radius: 3 * virtualstudio.uiScale
-                border.color: showEthernetWarningCheckbox.down || showEthernetWarningCheckbox.hovered  ? checkboxPressedStroke : checkboxStroke
-
-                Rectangle {
-                    width: 10 * virtualstudio.uiScale
-                    height: 10 * virtualstudio.uiScale
-                    x: 3 * virtualstudio.uiScale
-                    y: 3 * virtualstudio.uiScale
-                    radius: 2 * virtualstudio.uiScale
-                    color: showEthernetWarningCheckbox.down ||  showEthernetWarningCheckbox.hovered ? checkboxPressedStroke : checkboxStroke
-                    visible: showEthernetWarningCheckbox.checked
-                }
-            }
-            contentItem: Text {
-                text: showEthernetWarningCheckbox.text
-                font.family: "Poppins"
-                font.pixelSize: 10 * virtualstudio.fontScale * virtualstudio.uiScale
-                anchors.horizontalCenter: parent.horizontalCenter
-                anchors.verticalCenter: parent.verticalCenter
-                leftPadding: showEthernetWarningCheckbox.indicator.width + showEthernetWarningCheckbox.spacing
-                color: textColour
-            }
-        }
-    }
-
-    Item {
-        id: headphoneWarningItem
-        width: parent.width; height: parent.height
-        visible: warningScreen == "headphones"
-
-        Image {
-            id: headphoneWarningLogo
-            source: "headphones.svg"
-            sourceSize: Qt.size( img.sourceSize.width*5, img.sourceSize.height*5 )
-            Image {
-                id: img
-                source: parent.source
-                width: 0
-                height: 0
-            }
-            width: 118
-            height: 128
-            y: 60
-            anchors.horizontalCenter: parent.horizontalCenter
-        }
-
-        Colorize {
-            anchors.fill: headphoneWarningLogo
-            source: headphoneWarningLogo
-            hue: 0
-            saturation: 0
-            lightness: imageLightnessValue
-        }
-
-        Text {
-            id: headphoneWarningHeader
-            text: "Use Wired Headphones"
-            font { family: "Poppins"; weight: Font.Bold; pixelSize: fontMedium * virtualstudio.fontScale * virtualstudio.uiScale }
-            color: textColour
-            anchors.horizontalCenter: parent.horizontalCenter
-            anchors.top: headphoneWarningLogo.bottom
-            anchors.topMargin: 32 * virtualstudio.uiScale
-        }
-
-        Text {
-            id: headphoneWarningSubheader1
-            text: "JackTrip requires the use of wired headphones."
-            font { family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale }
-            color: textColour
-            width: 400
-            wrapMode: Text.Wrap
-            horizontalAlignment: Text.AlignHCenter
-            anchors.horizontalCenter: parent.horizontalCenter
-            anchors.top: headphoneWarningHeader.bottom
-            anchors.topMargin: 32 * virtualstudio.uiScale
+            anchors.rightMargin: rightMargin * virtualstudio.uiScale
+            anchors.verticalCenter: pageTitle.verticalCenter
+            visible: parent.isUsingRtAudio
+            enabled: audio.audioReady && !audio.scanningDevices
         }
 
         Text {
-            id: headphoneWarningSubheader2
-            text: "Using speakers can cause loud feedback loops."
-            font { family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale }
+            text: "Restarting Audio"
+            anchors.verticalCenter: pageTitle.verticalCenter;
+            anchors.right: refreshButton.left;
+            anchors.rightMargin: 16 * virtualstudio.uiScale;
+            font { family: "Poppins"; pixelSize: fontExtraSmall * virtualstudio.fontScale * virtualstudio.uiScale }
             color: textColour
-            width: 400
-            wrapMode: Text.Wrap
-            horizontalAlignment: Text.AlignHCenter
-            anchors.horizontalCenter: parent.horizontalCenter
-            anchors.top: headphoneWarningSubheader1.bottom
-            anchors.topMargin: 24 * virtualstudio.uiScale
+            visible: !audio.audioReady
         }
 
-        Text {
-            id: headphoneWarningSubheader3
-            text: "Wireless headphones add way too much latency."
-            font { family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale }
-            color: textColour
-            width: 400
-            wrapMode: Text.Wrap
-            horizontalAlignment: Text.AlignHCenter
-            anchors.horizontalCenter: parent.horizontalCenter
-            anchors.top: headphoneWarningSubheader2.bottom
+        AudioSettings {
+            id: audioSettings
+            width: parent.width
+            anchors.top: pageTitle.bottom
             anchors.topMargin: 24 * virtualstudio.uiScale
         }
 
         Button {
-            id: okButtonHeadphones
+            id: backButton
             background: Rectangle {
                 radius: 6 * virtualstudio.uiScale
-                color: okButtonHeadphones.down ? saveButtonPressedColour : saveButtonBackgroundColour
+                color: backButton.down ? buttonPressedColour : buttonColour
                 border.width: 1
-                border.color: okButtonHeadphones.down || okButtonHeadphones.hovered ? saveButtonPressedStroke : saveButtonStroke
-                layer.enabled: okButtonHeadphones.hovered && !okButtonHeadphones.down
+                border.color: backButton.down || backButton.hovered ? buttonPressedStroke : buttonStroke
             }
-            onClicked: {
-                if (permissions.micPermission == "unknown") {
-                    virtualstudio.showWarnings = currShowWarnings; warningScreen = "microphone"
-                } else {
-                    virtualstudio.showWarnings = currShowWarnings; warningScreen = "acknowledged"
-                }
-            }
-            anchors.right: parent.right
-            anchors.rightMargin: 16 * virtualstudio.uiScale
-            anchors.bottomMargin: 16 * virtualstudio.uiScale
+            onClicked: { virtualstudio.windowState = "browse"; virtualstudio.studioToJoin = ""; audio.stopAudio(); }
+            anchors.left: parent.left
+            anchors.leftMargin: 16 * virtualstudio.uiScale
+            anchors.bottomMargin: rightMargin * virtualstudio.uiScale
             anchors.bottom: parent.bottom
             width: 150 * virtualstudio.uiScale; height: 30 * virtualstudio.uiScale
             Text {
-                text: "OK"
-                font.family: "Poppins"
-                font.pixelSize: 11 * virtualstudio.fontScale * virtualstudio.uiScale
-                font.weight: Font.Bold
-                color: saveButtonText
-                anchors.horizontalCenter: parent.horizontalCenter
-                anchors.verticalCenter: parent.verticalCenter
-            }
-        }
-
-        CheckBox {
-            id: showHeadphonesWarningCheckbox
-            checked: currShowWarnings
-            text: qsTr("Show warnings again next time")
-            anchors.right: okButtonHeadphones.left
-            anchors.rightMargin: 16 * virtualstudio.uiScale
-            anchors.verticalCenter: okButtonHeadphones.verticalCenter
-            onClicked: { currShowWarnings = showHeadphonesWarningCheckbox.checkState == Qt.Checked }
-            indicator: Rectangle {
-                implicitWidth: 16 * virtualstudio.uiScale
-                implicitHeight: 16 * virtualstudio.uiScale
-                x: showHeadphonesWarningCheckbox.leftPadding
-                y: parent.height / 2 - height / 2
-                radius: 3 * virtualstudio.uiScale
-                border.color: showHeadphonesWarningCheckbox.down || showHeadphonesWarningCheckbox.hovered ? checkboxPressedStroke : checkboxStroke
-
-                Rectangle {
-                    width: 10 * virtualstudio.uiScale
-                    height: 10 * virtualstudio.uiScale
-                    x: 3 * virtualstudio.uiScale
-                    y: 3 * virtualstudio.uiScale
-                    radius: 2 * virtualstudio.uiScale
-                    color: showHeadphonesWarningCheckbox.down || showHeadphonesWarningCheckbox.hovered ? checkboxPressedStroke : checkboxStroke
-                    visible: showHeadphonesWarningCheckbox.checked
-                }
-            }
-            contentItem: Text {
-                text: showHeadphonesWarningCheckbox.text
+                text: "Back"
                 font.family: "Poppins"
-                font.pixelSize: 10 * virtualstudio.fontScale * virtualstudio.uiScale
-                anchors.horizontalCenter: parent.horizontalCenter
-                anchors.verticalCenter: parent.verticalCenter
-                leftPadding: showHeadphonesWarningCheckbox.indicator.width + showHeadphonesWarningCheckbox.spacing
+                font.pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale
                 color: textColour
-            }
-        }
-    }
-
-    Item {
-        id: requestMicPermissionsItem
-        width: parent.width; height: parent.height
-        visible: warningScreen == "microphone" && permissions.micPermission == "unknown"
-
-        Image {
-            id: microphonePrompt
-            source: "Prompt.svg"
-            width: 260
-            height: 250
-            y: 60
-            anchors.horizontalCenter: parent.horizontalCenter
-            sourceSize: Qt.size(microphonePrompt.width,microphonePrompt.height)
-            fillMode: Image.PreserveAspectFit
-            smooth: true
-        }
-
-        Image {
-            id: micLogo
-            source: "logo.svg"
-            anchors.horizontalCenter: parent.horizontalCenter
-            anchors.top: microphonePrompt.top
-            anchors.topMargin: 18 * virtualstudio.uiScale
-            width: 32 * virtualstudio.uiScale; height: 59 * virtualstudio.uiScale
-            sourceSize: Qt.size(micLogo.width,micLogo.height)
-            fillMode: Image.PreserveAspectFit
-            smooth: true
-        }
-
-        Colorize {
-            anchors.fill: microphonePrompt
-            source: microphonePrompt
-            hue: 0
-            saturation: 0
-            lightness: imageLightnessValue
-        }
-
-        Button {
-            id: showPromptButton
-            width: 112 * virtualstudio.uiScale
-            height: 30 * virtualstudio.uiScale
-            background: Rectangle {
-                radius: 6 * virtualstudio.uiScale
-                color: showPromptButton.down ? saveButtonPressedColour : saveButtonBackgroundColour
-                border.width: 2
-                border.color: showPromptButton.down || showPromptButton.hovered ? saveButtonPressedStroke : saveButtonStroke
-                layer.enabled: showPromptButton.hovered && !showPromptButton.down
-            }
-            onClicked: {
-                permissions.getMicPermission();
-            }
-            anchors.right: microphonePrompt.right
-            anchors.rightMargin: 13.5 * virtualstudio.uiScale
-            anchors.bottomMargin: 17 * virtualstudio.uiScale
-            anchors.bottom: microphonePrompt.bottom
-            Text {
-                text: "OK"
-                font.pixelSize: 11 * virtualstudio.fontScale * virtualstudio.uiScale
-                font.weight: Font.Bold
-                color: saveButtonText
                 anchors.horizontalCenter: parent.horizontalCenter
                 anchors.verticalCenter: parent.verticalCenter
             }
         }
 
-        Text {
-            id: micPermissionsHeader
-            text: "JackTrip needs your sounds!"
-            font { family: "Poppins"; weight: Font.Bold; pixelSize: fontMedium * virtualstudio.fontScale * virtualstudio.uiScale }
-            color: textColour
-            anchors.horizontalCenter: parent.horizontalCenter
-            anchors.top: microphonePrompt.bottom
-            anchors.topMargin: 48 * virtualstudio.uiScale
-        }
-
-        Text {
-            id: micPermissionsSubheader1
-            text: "JackTrip requires permission to use your microphone."
-            font { family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale }
-            color: textColour
-            width: 400
-            wrapMode: Text.Wrap
-            horizontalAlignment: Text.AlignHCenter
-            anchors.horizontalCenter: parent.horizontalCenter
-            anchors.top: micPermissionsHeader.bottom
-            anchors.topMargin: 32 * virtualstudio.uiScale
-        }
-
-        Text {
-            id: micPermissionsSubheader2
-            text: "Click ‘OK’ to give JackTrip access to your microphone, instrument, or other audio device."
-            font { family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale }
-            color: textColour
-            width: 400
-            wrapMode: Text.Wrap
-            horizontalAlignment: Text.AlignHCenter
-            anchors.horizontalCenter: parent.horizontalCenter
-            anchors.top: micPermissionsSubheader1.bottom
-            anchors.topMargin: 24 * virtualstudio.uiScale
-        }
-    }
-
-    Item {
-        id: noMicItem
-        width: parent.width; height: parent.height
-        visible: (warningScreen == "acknowledged" || warningScreen == "microphone") && permissions.micPermission == "denied"
-
-        Image {
-            id: noMic
-            source: "micoff.svg"
-            width: 109.27
-            height: 170
-            y: 60
-            anchors.horizontalCenter: parent.horizontalCenter
-            sourceSize: Qt.size(noMic.width,noMic.height)
-            fillMode: Image.PreserveAspectFit
-            smooth: true
-        }
-
-        Colorize {
-            anchors.fill: noMic
-            source: noMic
-            hue: 0
-            saturation: 0
-            lightness: imageLightnessValue
-        }
-
-        Button {
-            id: openSettingsButton
-            background: Rectangle {
-                radius: 6 * virtualstudio.uiScale
-                color: openSettingsButton.down ? saveButtonPressedColour : saveButtonBackgroundColour
-                border.width: 1
-                border.color: openSettingsButton.down || openSettingsButton.hovered ? saveButtonPressedStroke : saveButtonStroke
-                layer.enabled: openSettingsButton.hovered && !openSettingsButton.down
-            }
-            onClicked: {
-                permissions.openSystemPrivacy();
-            }
-            anchors.right: parent.right
-            anchors.rightMargin: 16 * virtualstudio.uiScale
-            anchors.bottomMargin: 16 * virtualstudio.uiScale
-            anchors.bottom: parent.bottom
-            width: 200 * virtualstudio.uiScale; height: 30 * virtualstudio.uiScale
-            Text {
-                text: "Open Privacy Settings"
-                font.family: "Poppins"
-                font.pixelSize: 11 * virtualstudio.fontScale * virtualstudio.uiScale
-                font.weight: Font.Bold
-                color: saveButtonText
-                anchors.horizontalCenter: parent.horizontalCenter
-                anchors.verticalCenter: parent.verticalCenter
-            }
-        }
-
-        Text {
-            id: noMicHeader
-            text: "JackTrip can't hear you!"
-            font { family: "Poppins"; weight: Font.Bold; pixelSize: fontMedium * virtualstudio.fontScale * virtualstudio.uiScale }
-            color: textColour
-            anchors.horizontalCenter: parent.horizontalCenter
-            anchors.top: noMic.bottom
-            anchors.topMargin: 48 * virtualstudio.uiScale
-        }
-
-        Text {
-            id: noMicSubheader1
-            text: "JackTrip requires permission to use your microphone."
-            font { family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale }
-            color: textColour
-            width: 400
-            wrapMode: Text.Wrap
-            horizontalAlignment: Text.AlignHCenter
-            anchors.horizontalCenter: parent.horizontalCenter
-            anchors.top: noMicHeader.bottom
-            anchors.topMargin: 32 * virtualstudio.uiScale
-        }
-
-        Text {
-            id: noMicSubheader2
-            text: "Click 'Open Privacy Settings' to give JackTrip permission to access your microphone, instrument, or other audio device."
-            font { family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale }
-            color: textColour
-            width: 400
-            wrapMode: Text.Wrap
-            horizontalAlignment: Text.AlignHCenter
-            anchors.horizontalCenter: parent.horizontalCenter
-            anchors.top: noMicSubheader1.bottom
-            anchors.topMargin: 24 * virtualstudio.uiScale
-        }
-    }
-
-    Item {
-        id: setupItem
-        width: parent.width; height: parent.height
-        visible: (warningScreen == "acknowledged" || warningScreen == "microphone") && permissions.micPermission == "granted"
-
-        property bool isUsingRtAudio: virtualstudio.audioBackend == "RtAudio"
-
-        Text {
-            id: pageTitle
-            x: 16 * virtualstudio.uiScale; y: 32 * virtualstudio.uiScale
-            text: "Choose your audio devices"
-            font { family: "Poppins"; weight: Font.Bold; pixelSize: fontBig * virtualstudio.fontScale * virtualstudio.uiScale }
-            color: textColour
-        }
-
-        Button {
-            id: refreshButton
-            text: "Refresh Devices"
-            palette.buttonText: textColour
-            background: Rectangle {
-                radius: 6 * virtualstudio.uiScale
-                color: refreshButton.down ? buttonPressedColour : (refreshButton.hovered ? buttonHoverColour : buttonColour)
-                border.width: 1
-                border.color: refreshButton.down ? buttonPressedStroke : (refreshButton.hovered ? buttonHoverStroke : buttonStroke)
-            }
-            icon {
-                source: "refresh.svg";
-                color: textColour;
-            }
-            display: AbstractButton.TextBesideIcon
-            onClicked: {
-                virtualstudio.refreshDevices();
-            }
-            anchors.right: parent.right
-            anchors.rightMargin: rightMargin * virtualstudio.uiScale
-            anchors.verticalCenter: pageTitle.verticalCenter
-            width: 144 * virtualstudio.uiScale; height: 30 * virtualstudio.uiScale
-            font {
-                family: "Poppins"
-                pixelSize: fontExtraSmall * virtualstudio.fontScale * virtualstudio.uiScale
-            }
-            visible: parent.isUsingRtAudio
-        }
-
-        AudioSettings {
-            id: audioSettings
-            width: parent.width
-            anchors.top: pageTitle.bottom
-            anchors.topMargin: 24 * virtualstudio.uiScale
+        DeviceWarning {
+            id: deviceWarning
+            anchors.left: backButton.right
+            anchors.leftMargin: 16 * virtualstudio.uiScale
+            anchors.verticalCenter: backButton.verticalCenter
+            visible: Boolean(audio.devicesError) || Boolean(audio.devicesWarning)
         }
 
         Button {
@@ -565,19 +109,24 @@ Item {
                 border.width: 1
                 border.color: saveButton.down || saveButton.hovered ? saveButtonPressedStroke : saveButtonStroke
             }
-            enabled: !Boolean(virtualstudio.devicesError) && virtualstudio.backendAvailable
-            onClicked: { virtualstudio.windowState = "browse"; virtualstudio.applySettings() }
+            enabled: !Boolean(audio.devicesError) && audio.backendAvailable && audio.audioReady
+            onClicked: {
+                audio.stopAudio(true);
+                virtualstudio.windowState = "connected";
+                virtualstudio.saveSettings();
+                virtualstudio.joinStudio();
+            }
             anchors.right: parent.right
             anchors.rightMargin: rightMargin * virtualstudio.uiScale
             anchors.bottomMargin: rightMargin * virtualstudio.uiScale
             anchors.bottom: parent.bottom
             width: 150 * virtualstudio.uiScale; height: 30 * virtualstudio.uiScale
             Text {
-                text: virtualstudio.studioToJoin.toString() ? "Connect to Studio" : "Save Settings"
+                text: "Connect to Studio"
                 font.family: "Poppins"
                 font.pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale
                 font.weight: Font.Bold
-                color: !Boolean(virtualstudio.devicesError) && virtualstudio.backendAvailable ? saveButtonText : disabledButtonText
+                color: !Boolean(audio.devicesError) && audio.backendAvailable && audio.audioReady ? saveButtonText : disabledButtonText
                 anchors.horizontalCenter: parent.horizontalCenter
                 anchors.verticalCenter: parent.verticalCenter
             }
@@ -586,7 +135,7 @@ Item {
         CheckBox {
             id: showAgainCheckbox
             checked: virtualstudio.showDeviceSetup
-            visible: virtualstudio.backendAvailable
+            visible: audio.backendAvailable
             text: qsTr("Ask again next time")
             anchors.right: saveButton.left
             anchors.rightMargin: 16 * virtualstudio.uiScale
index 0a2845e1a54cb2adff873c7665cae6a6a8bb4dbd..91f8397e1cde743b6c48d8638dce7cddad9f27c5 100644 (file)
@@ -1,6 +1,6 @@
-import QtQuick 2.12
-import QtQuick.Controls 2.12
-import QtGraphicalEffects 1.12
+import QtQuick
+import QtQuick.Controls
+import Qt5Compat.GraphicalEffects
 import VS 1.0
 
 Rectangle {
@@ -123,6 +123,16 @@ Rectangle {
         layer.effect: OpacityMask {
             maskSource: mask
         }
+
+        AppIcon {
+            id: defaultFlag
+            anchors.fill: parent
+            width: 32 * virtualstudio.uiScale
+            height: 32 * virtualstudio.uiScale
+            icon.source: "language.svg"
+            color: "white"
+            visible: flag.status != Image.Ready
+        }
     }
 
     Rectangle {
@@ -187,8 +197,14 @@ Rectangle {
         }
         visible: !connected
         onClicked: {
-            virtualstudio.windowState = "connected";
-            virtualstudio.connectToStudio(index);
+            virtualstudio.studioToJoin = `jacktrip://join/${studioId}`
+            if (virtualstudio.showDeviceSetup) {
+                virtualstudio.windowState = "setup";
+                audio.startAudio();
+            } else {
+                virtualstudio.windowState = "connected";
+                virtualstudio.joinStudio();
+            }
         }
         Image {
             id: join
@@ -322,9 +338,9 @@ Rectangle {
         }
         onClicked: {
             if (connected) {
-                virtualstudio.launchVideo(-1)
+                virtualstudio.launchVideo(studioId)
             } else {
-                virtualstudio.manageStudio(index);
+                virtualstudio.manageStudio(studioId);
             }
         }
         visible: admin || connected
diff --git a/src/gui/VolumeSlider.qml b/src/gui/VolumeSlider.qml
new file mode 100644 (file)
index 0000000..b076199
--- /dev/null
@@ -0,0 +1,122 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+
+Item {
+    width: parent.width
+    height: parent.height
+
+    required property string labelText
+    required property string tooltipText
+    required property bool sliderEnabled
+    property bool showLabel: true
+    property int fontSize: 8
+
+    property string iconColor: virtualstudio.darkMode ? "#ACAFAF" : "gray"
+    property string sliderPressedColour: virtualstudio.darkMode ? "#BABCBC" :  "#EAECEC"
+
+    Item {
+        anchors.fill: parent
+        anchors.verticalCenter: parent.verticalCenter
+
+        Text {
+            id: label
+            anchors.left: parent.left
+            anchors.verticalCenter: parent.verticalCenter
+            width: 40 * virtualstudio.uiScale
+            horizontalAlignment: Text.AlignRight
+            text: labelText
+            font {family: "Poppins"; weight: Font.Medium; pixelSize: fontSize * virtualstudio.fontScale * virtualstudio.uiScale }
+            color: textColour
+            visible: showLabel
+        }
+
+        InfoTooltip {
+            id: tooltip
+            content: tooltipText
+            size: 16
+            anchors.left: label.right
+            anchors.leftMargin: 2 * virtualstudio.uiScale
+            anchors.verticalCenter: label.verticalCenter
+            visible: showLabel
+        }
+
+        AppIcon {
+            id: quieterIcon
+            anchors.left: showLabel ? tooltip.right : parent.left
+            anchors.leftMargin: showLabel ? 8 * virtualstudio.uiScale : 0
+            anchors.verticalCenter: label.verticalCenter
+            width: 16 * virtualstudio.uiScale
+            height: 16 * virtualstudio.uiScale
+            icon.source: "quiet.svg"
+            color: iconColor
+        }
+
+        AppIcon {
+            id: louderIcon
+            anchors.right: parent.right
+            anchors.verticalCenter: label.verticalCenter
+            width: 18 * virtualstudio.uiScale
+            height: 18 * virtualstudio.uiScale
+            icon.source: "loud.svg"
+            color: iconColor
+        }
+
+        Slider {
+            id: slider
+            value: labelText == "Monitor" ? audio.monitorVolume : (labelText == "Studio" ? audio.outputVolume : audio.inputVolume )
+            onMoved: {
+                if (labelText == "Monitor") {
+                    audio.monitorVolume = value;
+                } else if (labelText == "Studio") {
+                    audio.outputVolume = value;
+                } else {
+                    audio.inputVolume = value;
+                }
+            }
+            enabled: sliderEnabled
+            from: 0.0
+            to: 1.0
+            stepSize: 0.01
+            padding: 0
+            anchors.left: quieterIcon.right
+            anchors.leftMargin: 4 * virtualstudio.uiScale
+            anchors.right: louderIcon.left
+            anchors.rightMargin: 4 * virtualstudio.uiScale
+            anchors.verticalCenter: label.verticalCenter
+
+            background: Rectangle {
+                x: slider.leftPadding
+                y: slider.topPadding + slider.availableHeight / 2 - height / 2
+                implicitWidth: parent.width
+                implicitHeight: 8 * virtualstudio.uiScale
+                width: slider.availableWidth
+                height: implicitHeight
+                radius: 4
+                color: "gray"
+
+                Rectangle {
+                    width: slider.visualPosition * parent.width
+                    height: parent.height
+                    radius: 4
+                    gradient: Gradient {
+                        orientation: Gradient.Horizontal
+                        GradientStop { position: 0.0; color: sliderEnabled ? "#67C6F3" : "light gray" }
+                        GradientStop { position: 1.0; color: sliderEnabled ? "#00897b" : "light gray" }
+                    }
+                }
+            }
+
+            handle: Rectangle {
+                x: slider.leftPadding + slider.visualPosition * (slider.availableWidth - width)
+                y: slider.topPadding + slider.availableHeight / 2 - height / 2
+                implicitWidth: 20 * virtualstudio.uiScale
+                implicitHeight: 20 * virtualstudio.uiScale
+                radius: implicitWidth / 2
+                color: slider.pressed ? sliderPressedColour : "white"
+                border.width: 3
+                border.color: sliderEnabled ? "#00897b" : "light gray"
+            }
+        }
+    }
+}
diff --git a/src/gui/Web.qml b/src/gui/Web.qml
new file mode 100644 (file)
index 0000000..13927f8
--- /dev/null
@@ -0,0 +1,10 @@
+import QtQuick
+import QtQuick.Controls
+
+Loader {
+    anchors.fill: parent
+    source: "WebEngine.qml"
+
+    // TODO: Add support for QtWebView
+    // source: useWebEngine ? "WebEngine.qml" : "WebView.qml"
+}
diff --git a/src/gui/WebEngine.qml b/src/gui/WebEngine.qml
new file mode 100644 (file)
index 0000000..a45b459
--- /dev/null
@@ -0,0 +1,121 @@
+import QtQuick
+import QtQuick.Controls
+import QtWebEngine
+
+Item {
+    width: parent.width; height: parent.height
+    clip: true
+
+    function contentScriptFactory (port) {
+        return `
+            // add script tag for qwebchannel
+            document.head.addEventListener("initqwebchannel", () => {
+
+                var script = document.createElement("script");
+                script.onload = function () {
+                    var url = "ws://localhost:${port}";
+                    var socket = new WebSocket(url);
+                    
+                    socket.onclose = function() {
+                        console.error("[QT] web channel closed");
+                    };
+                    socket.onerror = function(event) {
+                        console.error("[QT] web channel error: " + event.type);
+                    };
+                    socket.onopen = function() {
+                        new QWebChannel(socket, function(channel) {
+                            console.log("[QT] Socket opened");
+
+                            // make core object accessible globally
+                            window.virtualstudio = channel.objects.virtualstudio;
+                            window.auth = channel.objects.auth;
+                            window.clipboard = channel.objects.clipboard;
+
+                            const event = new CustomEvent("qwebchannelinitialized");
+                            document.head.dispatchEvent(event);
+                            console.log("[QT] Dispatched qwebchannelinitialized event");
+                            console.log("[QT] Connected to WebChannel, ready to send/receive messages!");
+                        });
+                    }
+                }
+                script.setAttribute("src", "qrc:///qtwebchannel/qwebchannel.js");
+                script.setAttribute("type", "text/javascript");
+                document.head.appendChild(script);
+                console.log("[QT] Added qwebchannel initialization script to DOM.");
+            });
+            console.log("[QT] Added initqwebchannel event listener");
+        `
+    }
+
+    Rectangle {
+        id: web
+        anchors.fill: parent
+        color: backgroundColour
+
+        property string accessToken: auth.isAuthenticated && Boolean(auth.accessToken) ? auth.accessToken : ""
+        property string studioId: virtualstudio.currentStudio.id
+
+        WebEngineView {
+            id: webEngineView
+            anchors.fill: parent
+            settings.javascriptCanAccessClipboard: true
+            settings.javascriptCanPaste: true
+            settings.screenCaptureEnabled: true
+            profile.httpUserAgent: `JackTrip/${virtualstudio.versionString}`
+            url: `https://${virtualstudio.apiHost}/studios/${studioId}/live?accessToken=${accessToken}`
+
+            // useful for debugging
+            // onJavaScriptConsoleMessage: function(level, message, lineNumber, sourceID) {
+            //     console.log(level, message, lineNumber, sourceID);
+            // }
+
+            // useful for debugging
+            // onLoadingChanged: function(loadRequest) {
+            //     console.log("onLoadingChanged", loadRequest.errorCode, loadRequest.errorDomain, loadRequest.errorString, loadRequest.status, loadRequest.url);
+            // }
+
+            onContextMenuRequested: function(request) {
+                // this disables the default context menu: https://doc.qt.io/qt-6.2/qml-qtwebengine-contextmenurequest.html#accepted-prop
+                request.accepted = true;
+            }
+
+            onNewWindowRequested: function(request) {
+                Qt.openUrlExternally(request.requestedUrl);
+            }
+
+            onFeaturePermissionRequested: function(securityOrigin, feature) {
+                webEngineView.grantFeaturePermission(securityOrigin, feature, true);
+            }
+
+            onRenderProcessTerminated: function(terminationStatus, exitCode) {
+                var status = "";
+                switch (terminationStatus) {
+                case WebEngineView.NormalTerminationStatus:
+                    status = "(normal exit)";
+                    break;
+                case WebEngineView.AbnormalTerminationStatus:
+                    status = "(abnormal exit)";
+                    break;
+                case WebEngineView.CrashedTerminationStatus:
+                    status = "(crashed)";
+                    break;
+                case WebEngineView.KilledTerminationStatus:
+                    status = "(killed)";
+                    break;
+                }
+                console.log("Render process exited with code " + exitCode + " " + status);
+            }
+
+            onNavigationRequested: function(request) {
+                webEngineView.userScripts.collection = [
+                    {
+                        name: "script",
+                        sourceCode: contentScriptFactory(virtualstudio.webChannelPort),
+                        injectionPoint: WebEngineScript.DocumentReady,
+                        worldId: WebEngineScript.MainWorld
+                    }
+                ]
+            }
+        }
+    }
+}
diff --git a/src/gui/WebNull.qml b/src/gui/WebNull.qml
new file mode 100644 (file)
index 0000000..6f1c6de
--- /dev/null
@@ -0,0 +1,12 @@
+import QtQuick
+import QtQuick.Controls
+
+Item {
+    width: parent.width; height: parent.height
+    clip: true
+
+    Item {
+        id: webNull
+        anchors.fill: parent
+    }
+}
diff --git a/src/gui/WebSocketTransport.cpp b/src/gui/WebSocketTransport.cpp
new file mode 100644 (file)
index 0000000..d9c9688
--- /dev/null
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com,
+*author Milian Wolff <milian.wolff@kdab.com>
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtWebChannel module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "WebSocketTransport.h"
+
+#include <QDebug>
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QtWebSockets/QWebSocket>
+
+/*!
+    \brief QWebChannelAbstractSocket implementation that uses a QWebSocket internally.
+    The transport delegates all messages received over the QWebSocket over its
+    textMessageReceived signal. Analogously, all calls to sendTextMessage will
+    be send over the QWebSocket to the remote client.
+*/
+
+QT_BEGIN_NAMESPACE
+
+/*!
+    Construct the transport object and wrap the given socket.
+    The socket is also set as the parent of the transport object.
+*/
+WebSocketTransport::WebSocketTransport(QWebSocket* socket)
+    : QWebChannelAbstractTransport(socket), m_socket(socket)
+{
+    connect(socket, &QWebSocket::textMessageReceived, this,
+            &WebSocketTransport::textMessageReceived);
+}
+
+/*!
+    Destroys the WebSocketTransport.
+*/
+WebSocketTransport::~WebSocketTransport() {}
+
+/*!
+    Serialize the JSON message and send it as a text message via the WebSocket to the
+   client.
+*/
+void WebSocketTransport::sendMessage(const QJsonObject& message)
+{
+    QJsonDocument doc(message);
+    m_socket->sendTextMessage(QString::fromUtf8(doc.toJson(QJsonDocument::Compact)));
+}
+
+/*!
+    Deserialize the stringified JSON messageData and emit messageReceived.
+*/
+void WebSocketTransport::textMessageReceived(const QString& messageData)
+{
+    QJsonParseError error;
+    QJsonDocument message = QJsonDocument::fromJson(messageData.toUtf8(), &error);
+    if (error.error) {
+        qWarning() << "Failed to parse text message as JSON object:" << messageData
+                   << "Error is:" << error.errorString();
+        return;
+    } else if (!message.isObject()) {
+        qWarning() << "Received JSON message that is not an object: " << messageData;
+        return;
+    }
+    emit messageReceived(message.object(), this);
+}
+
+QT_END_NAMESPACE
\ No newline at end of file
diff --git a/src/gui/WebSocketTransport.h b/src/gui/WebSocketTransport.h
new file mode 100644 (file)
index 0000000..4f93b23
--- /dev/null
@@ -0,0 +1,61 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com,
+*author Milian Wolff <milian.wolff@kdab.com>
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtWebChannel module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef WEBSOCKETTRANSPORT_H
+#define WEBSOCKETTRANSPORT_H
+
+#include <QtWebChannel/QWebChannelAbstractTransport>
+
+QT_BEGIN_NAMESPACE
+
+class QWebSocket;
+class WebSocketTransport : public QWebChannelAbstractTransport
+{
+    Q_OBJECT
+   public:
+    explicit WebSocketTransport(QWebSocket* socket);
+    virtual ~WebSocketTransport();
+
+    void sendMessage(const QJsonObject& message) Q_DECL_OVERRIDE;
+
+   private Q_SLOTS:
+    void textMessageReceived(const QString& message);
+
+   private:
+    QWebSocket* m_socket;
+};
+
+QT_END_NAMESPACE
+
+#endif  // WEBSOCKETTRANSPORT_H
\ No newline at end of file
diff --git a/src/gui/WebView.qml b/src/gui/WebView.qml
new file mode 100644 (file)
index 0000000..871a1d9
--- /dev/null
@@ -0,0 +1,23 @@
+import QtQuick
+import QtQuick.Controls
+import QtWebView
+
+Item {
+    width: parent.width; height: parent.height
+    clip: true
+
+    Item {
+        id: web
+        anchors.fill: parent
+
+        property string accessToken: auth.isAuthenticated && Boolean(auth.accessToken) ? auth.accessToken : ""
+        property string studioId: virtualstudio.currentStudio.id
+
+        WebView {
+            id: webEngineView
+            anchors.fill: parent
+            httpUserAgent: `JackTrip/${virtualstudio.versionString}`
+            url: `https://${virtualstudio.apiHost}/studios/${studioId}/live?accessToken=${accessToken}`
+        }
+    }
+}
index 7b82019ce9cd4c7ba33c056fe4a41006a62646e3..02b982dcaa62445d3f62d2b74f66594722bcb4a5 100644 (file)
@@ -35,8 +35,12 @@ const QString About::s_buildType = QLatin1String("");
 #endif
 #ifdef BUILD_ID
 const QString About::s_buildID = QStringLiteral(BUILD_ID);
+#elif defined(JACKTRIP_BUILD_INFO)
+#define STR(s)       #s
+#define TO_STRING(s) STR(s)
+const QString About::s_buildID   = QLatin1String(TO_STRING(JACKTRIP_BUILD_INFO));
 #else
-const QString About::s_buildID   = QLatin1String("");
+const QString About::s_buildID = QLatin1String("");
 #endif
 
 About::About(QWidget* parent) : QDialog(parent), m_ui(new Ui::About)
index 9d140e8b982d3906cec857b875e120657f166c97..e644f2e0edbf7533039d8408fa00a4d61bf27f52 100644 (file)
@@ -93,7 +93,7 @@ border: 4px solid black; </string>
         </size>
        </property>
        <property name="text">
-        <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;A system for high quality audio network performance over the internet.&lt;/p&gt;&lt;p&gt;Version %VERSION%%BUILD%&lt;br/&gt;Using Qt %QTVERSION%&lt;/p&gt;&lt;p&gt;Copyright © 2008-2020 Juan-Pablo Caceres, Chris Chafe, et al. SoundWIRE group at CCRMA, Stanford University.&lt;/p&gt;&lt;p&gt;Graphical user interface component originally released as QJackTrip, Copyright © 2020 Aaron Wyatt.&lt;/p&gt;&lt;p&gt;%LICENSE%JackTrip source code is released under MIT and GPL licenses. See LICENSE.md file for more information.&lt;/p&gt;&lt;p&gt;This is free software and is provided &amp;quot;as is&amp;quot;, without warranty of any kind.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+        <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;A system for high quality audio network performance over the internet.&lt;/p&gt;&lt;p&gt;&lt;b&gt;Version %VERSION%&lt;/b&gt;%BUILD%&lt;br/&gt;Using Qt %QTVERSION%&lt;/p&gt;&lt;p&gt;Copyright © 2008-2020 Juan-Pablo Caceres, Chris Chafe, et al. SoundWIRE group at CCRMA, Stanford University.&lt;/p&gt;&lt;p&gt;Classic mode graphical user interface component originally released as QJackTrip, Copyright © 2020 Aaron Wyatt.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;Virtual Studio interface and integration Copyright © 2022-2023 JackTrip Labs, Inc.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;%LICENSE%JackTrip source code is released under MIT and GPL licenses. See LICENSE.md file for more information.&lt;/p&gt;&lt;p&gt;This is free software and is provided &amp;quot;as is&amp;quot;, without warranty of any kind.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
        </property>
        <property name="alignment">
         <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
diff --git a/src/gui/ethernet.png b/src/gui/ethernet.png
deleted file mode 100644 (file)
index c911dc4..0000000
Binary files a/src/gui/ethernet.png and /dev/null differ
diff --git a/src/gui/ethernet.svg b/src/gui/ethernet.svg
new file mode 100644 (file)
index 0000000..d35645b
--- /dev/null
@@ -0,0 +1,3 @@
+<svg width="179" height="128" viewBox="0 0 179 128" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+  <image id="b" x="0" y="0" width="179" height="128" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALMAAACACAYAAABA+ljwAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAABLPSURBVHgB7V0HsCxFFT0GjIiICeNrMGMAMcf/AMtQRkRFUeSpKIgKmFBA2fkqiGJE+CgGnmJAxZzje5SlRZkFFDHNA8UESFCy8O3j7NTv7Z2e7p7pnrA7p+rU/2+mZ6Z79uzd2923bwMDBvhjNOaAAb3GwZIbxzwcAwb0FK/HJiF3TtDXQXs4QHJXyftLXh/zh39L/kjydZK/R/dBIb/VcO4IyUMxp/g4pr/h88y90W3wC1dW/wslb4U5BH+W+iCwJnmt5H3RTYxgF/KDMac4B/0QWNM8Bt2DzSJfLPkg7ZpFzAkE+iGsNvhHdAsHoby+F0nuoF1z2PjcXIxyCPRDWG0wRXcwQnldz5Pc0XLNrmgYszyK8ATJq1Afu0gegvnBayWTkvO0yDtJnqEcG2nXnCj5Rcw4BJqxcuchHB4JNFLnFO1jhPI6/kPy3pZrPiF5XcwBBJoRxgrCQQCN1DlFu3gNyuvHUQvdRx5pZT4teT2tDMU9kz60QDPC+BzC4UaSlwPR65yiPbwadiHfU7tmpJU5GZNu62bI3I38/MwJWqAZMR+FsEiB6HVuS8yHobxe58Jukekfb6aVOVYrk2DGINCMmEPPqP0AiF7nFM3jVbBbZF3IiVbma5I3VM7zl+yTaEHIfR/N+K7k9wqOfx9h8XZkH5oOxpdsjX7ijZJvKjn/F8mdMRk3kmAy9PPbkrtJXqkce7/kc5S/12NSzAuST5N8L3oOgbDW7GC0i1X00zK/0lKXSzBtkddrZWgwbqyV+QrKLfJtJH8zPtd7H1ogrJj3QLv4GvonZpuQc46Ua7ZHFj+SnztF8ibK+c2RdQDV69+sPZe/YGcq56+QvBd6DIGwYt4F7WIZ/RLzG+BXp5Fy7eOQCfqHkjdVjjOM+GMot8i3kzxLK7OEnkMgrJgX0S6W0R8xH4hq9Rop93iq5M2UvynkE7XyuvtwB8nfaWV218oI9BACYcV8f7SLo9APMR+KenUbFdyTs3y6kBOtjEDWgVTLvFAr8xhkoyZFz/BC10YzvoFsytQVMQXgghX4BaUvonkrxBGXt6Aeni/5bmQdwxxflnyi8rc+anEnZKNNd9Huc6Ly9yMkv45sjJrXXobwcwTRIGD+9m/A7EPAPAET44u5P+r/YvwRxV/Ap2NTpzDRzt1pfF1+D5bTLfIzJK/GZPsFegQB80ubF3AEpgkxH4Iwrs+dlXvyl/xZyt/sFCbac1n+T9p9XqKVoUW/CD0WMiEwiPmZiC/mVyCMRVaFzACiVZh9aGIB00LeRyvD8NFrUC5k3v9YdBwCg5hjizmUkIVyTwr5JK2MLugFTLtQ+2llOPN3MexCzs93WtACg5hjivn1iGORTZNDqqD3RJY+IT+3v1a3xyLrQJYJWZ9l5Cr+NtNhlEJgEHMsMb8c4S3yFrDPciZK+bxTeKBWt70wOYNYJOSRdt8T0HEIDGKOIWbb4lMX8vm6Rf6047Uj5bptMd3eS+EnZAb0qyGltM67oWMQGMQcWswvQxiLbOrsuXJUULenFLRRaGV0IX9eO69Ol78LHYLAIOYiMXMG7GXwRyghC+WeFPJXK95rpNxnAf6uxbJ2nkI+QSuzMzoCAfOLEJgPcPWzbq12hD9sa/ZcyGfrFvmkmvccKfd7ATJBuwiZLo0aiUchf1grUxZ/3TgEyl/EPphtfAGT7f2m5O3hj/1QX8hFnb2qFlnneuW+u8Au5I9ictSiyCLrIaVbomUIlL+ETvlEEZBiU1uPKCm3Rck521KnqhbZtbNXxUKrOFIrRx95c+V8kZAT7R75LKMpK2kjEBjEzHa+tKQMg2+4iPTAgnMvRRiLXLezV1XQI+08A5F0i6zHRushpaz7acr5d6MlCAxiPqHkvB6qqeY83hFhhCyUe4Z0LVwEzWyh+cQJF72qyWKKLLJuebfC9HQ52UqkncB8i3kFWS9fB/0//twWvZMXKeVs+S3KmGLaIn+qxv2qCpq/PHRpVFeqSMhHYhJFAUymZzQCgfkW86MLjgnJP6D8vai57qoIWnctKKTQPrKPoFVQyB/RyurpfXXXoojLiAj2aLfRjgmEEzPHG/ceczvl+I7K8Z2U49soxx+jHN9SOb6n9oy9Faq9592U49ugHmwfUk411sFH0CnCD79V5QFa24ss8nqtjM0ik8chUgwHpyGXxw+JKeZl5Tq1s5Qox09Qjj9VOb5iqNPF2jPUuqnpqX6qHH8+quNg+InBV9DsSDbV2bPxdEwP0a2T/I9S5p3aeRchfygvHDpbI60Xk7LshQE2CPjnjmDilNzl4Af/Gkt55qlQF6Byenkdmsea5JPH/xL5rxxTFlArFOV7kH1Bc7Czt4ryXz4K+cWIAA7+/wKT3xofy8xl8D4D4cvot2U+A9WtnI+FVttK43VKjedWYSp5a6UOd5T8FSZ9aH0GtFXXgiL8ZcEDfcTsi2X0V8z6lHZsQasLTx8e4Nmu5BdWKM/mr4T67kaYRqudPf6UnWV46CDmaQhMhkQ2IegUky7luwI9f6PlmUJ5JvNnFBm8kVKmlkWu6zPfAFm8wd0xwBWfwWRATR3Qh84FTR/aFHknkKWuzcGAnfMRD79GNpK0Nv6bv9wM8t++oGyCTNAU8irKfWQGHuWzoFOoK+bjkf1sDXADf0EehLBQO4VM1/BaQ7mtlP9zZXSCOFiTfBImhfxjFAs5R4Ksv2UTcmlnr46Y2RPfCwNcITA9hhoK/CxyC/0OTAuaEySqK7YA+0hIFawhm7JeG//NHHOrkndzuHarknMfQCbkUne0qpgPwXztwBQCnKjYAvGguhyqoJlm9tnI4ooJCnkV4ePHc9ci3xyJFpkZqrZHPdAi7wuHflWV9FyceZuLTQsDgmv0HoL4yBN4H41M0H9GFkiUI5aQ1zDtWqwijJCdx5F9LbNAtvzbBexgXIQBAs0mRaeg81RYdC8uHf8/pkVWXYs8aCqUkK0WOYePZabwv4PMD3IB17pdiAFMwt30qgj9M4ptkVXXgvmbt0M9eAuZ8BEz98C4q2NZJiNZxQD6rQ9As+BMquoGxhQyLXIuZGZDpbGrK2R29ozDb2VwdTPo7yWOZb8k+Tb4Q2C2IND8sp4QQub2DFzSdUFJmaLOHjfr2QH14NzZK4KLmDlkchLcsIYs11kVrGC2BM2ZquuhOXDYr66Q2cd5GLIVLg9EttBUxxomO3scoWG8R93E716dvargGivXKcynW+4lLNezs/IPR15quM50/ELtWeq9XI6frxy/YnxsDcVi2Qdxp4p1Hqo9fwH+G3Hy/awraAtdiXyKmfdUg4Zc0ne5kEKOEjSk4l4eFTre4X4C9RrdNe5uaOPFDdYhlJBtLsISptcP/qRCfXUuo6HkiGc5Vogvz2Vzxy2B2o3vCt9naKNAM9sTFwm5yrP/hmkh0/d+JczIp6jr1p8bYDYi5CW4V2rJ/bb4LFD7JbTNFOXJWxYQX9AHBXimbTthuhd6rDEt8qk16p3zQ2gI3P/Y9cWk8MPWaM5yxeIDYYeI2M5Ee9YCqgn5ntp9EkPZj4zbw3jkUEJuxCIT+8K9Ykvwh0DWU97YQybwa2ca+PmxOnuLlmvOwaatguswmmthuukaivM76OASdteJlCJwV6K7oF/4keRV2rERMoEcXVBeINywY6hxZI4Rn1pwLkHc/BPMVrSETNSNYAlxrfKsQUj+F9n7OLCkTIqwFrnKfYs6exyFUlesJDXr2bhFLoPryzkXAwh9K13TKIBAdUGX5VNzvW9RZ2+Dcj6moBvr7Kl4CtwrOA+bUNrApUhF7+YwQ3mBaoLmuHXZChXbfYs6exsKyqkx6kmFepqE3LhFJnxSNtWdh+87BCYTmOjct+S6FP6ioCCrCJrXPVQre2zJc0Ja6FZcC0LAvZKnY8ALYH9PpqVJAtUtdFmWfd5XnYa/HNNC3uDwnBCCXkaLWIJ7Rd+JAQSD4G3v6iDDtQJxXQ6bjxxT0I3EWpRhFe6VHVZkb4KLoF9nuFYgnoUuGrXwfY4q6EMdr2mls6dia7g3kGOsrX7rOgjmULa9t5HhWoE4FlqFj0XW6dMpbN0iE3vAvXErmF/QTxaGc7RitndnWgsoJP8F988gJ6+xCfrYCvcts9CJoQxXiHTCyC3DvWHvwXxCIBNPCrOgmX/Yx9LlYLD7tXD/DHQLbXI5jql4T19Bfxgdgs+ce+e2fW0IX8emd5DCLGgXl0OdzXsOslGHOkIrcjlCCrlM0AxC6ozbuQC/Bi1i/lC0y1OKcgt9DcrfI12O50lejTBC46/GTuPnV+nsufKFSjt3Rcf6T9wSwacxt8R8QUiejeJ3kcIs6CXEE5SJtNAnN/CczmazSuDXkHkDP7iy95Ging/dV+6PDsInrvg8zB+YfnYZ1QXtMsoxCDoQGJ/rWvkU8wsmILS9G1NKVpdOYV/ZKZeDKwgGMbvBFohVJugmfNk2yFwbrW/KniMPLB/EvAmmiEC6HDYLzc7ittp1S+iPOH35cnQIPhU/G7MPgaydZXsS2iw0r8+XgzE38lXohzB9uR4dg0/lL8XsQ+0QH2co49IppPvmEoRUhVWmvkPzTDSbfswJvo3YDLML7hylt/e4kvI2lyMGEzSbaKaI56CjeQF9G3IPzCa4UtwkkLI1eE1uqJ4ozxVoR9B85iPQUfg2xpYcsa+wxTKUuRxfRbNCziHQrKCZlb8zIxdF8G3QqzGb4F6GNmGUdQpjC/oNhucKxBc0778zegDfhn0MswuBLIVCWftNFvrJiCuoMkEza30a6ZlcIhdzl6ygYGIQn8b9BbMNAbswdEEz+q1qPLIvRyX1Pj3gc1IU52vuNH4P/4beEbMBUxpeuhw2C527HFz93GQ+ZjK2y8EFGJ32jU2g2+Db2E7N+lSEQPZF3q3kvE3QDNiPYZG59ZgtHjqGoPk+Hoke4yj4N3oF/Uf+JWZwvEnQLp3C0EzGz+bMYZOC5i/NzdFz7An/F34JevozNIaewIWC3t1QVsBuoUMLOQeXVMUWNP3sRcwImJK2youPsZF4ExAwRwqWuRwuwqhD08SMi4UeGa4VKO8U0je+CWYI3D7tn/B/+T9BP3EizG1iQNBzDdfR5fAd+fFhKnk3w7NDW2j+vRNmFFUH/BfRP5gyd+Y0+dDc5+4ixBMzyWFPUVztYILmBvE3wwyDSbKrvPwV9BP80G2CVqftmZfiAsQVck7656b4l7qdwnWYAzDIpurLX0Q/YbPQdDko6PuhOSHnTDGdTzmHi4U2ZU6aG5yCai9+Ff2FzUKTMSZEEthXxf8d2aaiRWAGpKqdwrkAG1/1w9kD/UWddlcVco7EUpa5lu9tqDffeZmgy+KwZx4cN7Z9203kyoc+J4cZoXkh50gs1zC9g0AxigS9hhkeqfDBt1H9gzoZ/YaLy1GHv4V5oimBXdD3NVyrdgqPQb8ns4JiEfU+sL3RbzAJYExB/xLmSYq3Wa5dQ9YZLQLDT3sRb9w0fJLC6GRnaXv0Exx+44aPGyNzFdUtNEdVtsUAZ/hsn1bEFB1d7FiC+yDbA2RjQ6SFNgW8J5ZrXRKMD1BA/66uoLdGP/AAxFm6b7vnzyRvYahTYrm2c/kquownov6HyQTmAt3GXoiToCVBtpTJtvLj5zCPAiUF5TndPYxUVMB3Uf9DTdFdQbvunlRFyDlcBH2a5FaGOiZKufdhGKmoDIEwHaJU8iHoDuircufQ2ELO4Spok1AZN7MOA2oj5NjrCO3j0Ygbl3y84bkU9BmWa38teRsMiIpTEe7D5gcq0Dxo9d6OeCJW+cGSOpxmuZb9jL50nHsJgfDDVkdI3h7xcSNk2/3+HQhafxs/YKjPrSV/UXIdF8cegAFRwWD10B/4GrIhpjsjPGgFKYqYK0NsNC2DoqCLfOgU87mTVys4EnE+dCY7/7jk41Efi8iixZqYADka9jjnowz11AXNdztTa/H6gNgZLzkd/gVkrsEisoTdRfvMXR+Z+/MoZL19Bjk1OYN3+LgeDP4521L2SBSDgv7SuA0DWgCHtUJ2CF14JTKXJFVYd0fTOnyr9k4oaJuFPhwDOgkK+ldoT0xt0jSN7GKhy3I8D2gRnIJlqoE+CDAUE5TDxUIPU9EdBUcM6gTz94UMfH8J3MBt04oE/Tt0ONv8gE2gP9gXYfqSKzweCj+oLge/CBzR6E1+4wHAG9EvkbpwVfK2qAYK+qcYYip6C2b8SdEvwRaRs3AjDJh7MJnMBvRLvCoH33bAFB6HfllpjlsnKJ6gGTDg/+DOVD4bzTdNuhSfQf/WLA5oCQJZHEPT+37Y+C3Jh2HAgAq4HbJIthTtCZgZ/zkTtwMGDAgEJixZRvx8xySj8lYk98Owhm5AZDwWWVoprlQOJWAu8+cGPEsYBDw36FrvncJ7MLKkLAz/3G58LOfmyOp8GTKrzhm2v0r+SfJMZNF1TM17LgbMHf4H4Vg2S7fqhVUAAAAASUVORK5CYII="/>
+</svg>
diff --git a/src/gui/expand_less.svg b/src/gui/expand_less.svg
new file mode 100644 (file)
index 0000000..08039b6
--- /dev/null
@@ -0,0 +1,3 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M12 8.29504L6 14.295L7.41 15.705L12 11.125L16.59 15.705L18 14.295L12 8.29504Z" fill="black"/>
+</svg>
diff --git a/src/gui/expand_more.svg b/src/gui/expand_more.svg
new file mode 100644 (file)
index 0000000..71b70fd
--- /dev/null
@@ -0,0 +1,3 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M16.59 8.29504L12 12.875L7.41 8.29504L6 9.70504L12 15.705L18 9.70504L16.59 8.29504Z" fill="black"/>
+</svg>
diff --git a/src/gui/externalMic.svg b/src/gui/externalMic.svg
new file mode 100644 (file)
index 0000000..ad8e7dd
--- /dev/null
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 -960 960 960" width="48"><path d="M184-692q-14-15-19-34t-5-39q0-47.917 33.25-81.458Q226.5-880 274-880t80.75 33.542Q388-812.917 388-765q0 20-5 39t-19 34H184ZM396-80q-63.938 0-109.469-45Q241-170 241-235h-20q-6 0-10.125-3.889T206-249l-36-369q-2-13.5 7.25-23.25T200-651h148q13.5 0 22.75 9.75T378-618l-36 369q-.75 6.222-4.875 10.111Q333-235 327-235h-26q0 39 27.867 67 27.868 28 67 28Q435-140 462.5-167.906 490-195.812 490-235v-490q0-65 45-110t110-45q65 0 110 45t45 110v615q0 12.75-8.675 21.375Q782.649-80 769.825-80 757-80 748.5-88.625T740-110v-615q0-39.188-27.867-67.094-27.867-27.906-67-27.906Q606-820 578-792.094 550-764.188 550-725v490q0 65-45.237 110Q459.525-80 396-80ZM261-295h26l28-296h-82l28 296Zm26-296h-54 82-28Z"/></svg>
\ No newline at end of file
diff --git a/src/gui/language.svg b/src/gui/language.svg
new file mode 100644 (file)
index 0000000..8273aed
--- /dev/null
@@ -0,0 +1,3 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M11.99 2C6.47 2 2 6.48 2 12C2 17.52 6.47 22 11.99 22C17.52 22 22 17.52 22 12C22 6.48 17.52 2 11.99 2ZM18.92 8H15.97C15.65 6.75 15.19 5.55 14.59 4.44C16.43 5.07 17.96 6.35 18.92 8ZM12 4.04C12.83 5.24 13.48 6.57 13.91 8H10.09C10.52 6.57 11.17 5.24 12 4.04ZM4.26 14C4.1 13.36 4 12.69 4 12C4 11.31 4.1 10.64 4.26 10H7.64C7.56 10.66 7.5 11.32 7.5 12C7.5 12.68 7.56 13.34 7.64 14H4.26ZM5.08 16H8.03C8.35 17.25 8.81 18.45 9.41 19.56C7.57 18.93 6.04 17.66 5.08 16ZM8.03 8H5.08C6.04 6.34 7.57 5.07 9.41 4.44C8.81 5.55 8.35 6.75 8.03 8ZM12 19.96C11.17 18.76 10.52 17.43 10.09 16H13.91C13.48 17.43 12.83 18.76 12 19.96ZM14.34 14H9.66C9.57 13.34 9.5 12.68 9.5 12C9.5 11.32 9.57 10.65 9.66 10H14.34C14.43 10.65 14.5 11.32 14.5 12C14.5 12.68 14.43 13.34 14.34 14ZM14.59 19.56C15.19 18.45 15.65 17.25 15.97 16H18.92C17.96 17.65 16.43 18.93 14.59 19.56ZM16.36 14C16.44 13.34 16.5 12.68 16.5 12C16.5 11.32 16.44 10.66 16.36 10H19.74C19.9 10.64 20 11.31 20 12C20 12.69 19.9 13.36 19.74 14H16.36Z" fill="black"/>
+</svg>
diff --git a/src/gui/networkCheck.svg b/src/gui/networkCheck.svg
new file mode 100644 (file)
index 0000000..3b402a6
--- /dev/null
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 -960 960 960" width="48"><path d="M444-164q-27-11-40-41t2-59q8-15 40-80.5t70-145q38-79.5 74.5-155T643-754q3-7 10.5-10t15.5-1q8 2 13 9t3 15q-9 36-29.5 119T613-451.5q-22 87.5-41 159T547-206q-12 30-43 41.5t-60 .5Zm484-393q-13 13-31.5 13T864-556q-34-29-71-54t-71-41l23-90q54 24 98.5 55t84.5 66q14 12 14 30.5T928-557Zm-896 0q-13-13-13.5-32T32-620q92-84 207-132t241-48q24 0 54.5 2.5T594-790l-42 85q-16-2-34-3.5t-38-1.5q-108 0-204.5 41.5T96-556q-14 12-32.5 12T32-557Zm727 169q-13 13-30.5 13T696-387q-11-8-19-14t-14-11l22-92q16 9 34.5 22.5T759-450q14 12 14 30t-14 32Zm-558 0q-14-14-13-32.5t13-29.5q61-53 129-81.5T483-560l-45 93q-49 6-92.5 27T264-387q-15 12-32.5 12T201-388Z"/></svg>
\ No newline at end of file
diff --git a/src/gui/ohno.png b/src/gui/ohno.png
deleted file mode 100644 (file)
index 7d118f3..0000000
Binary files a/src/gui/ohno.png and /dev/null differ
index 47104c4a388d89146bbbac1449f21914e1ea03e0..86b6ee40121384551a3e7171d96210796d888616 100644 (file)
@@ -44,6 +44,7 @@
 #endif
 
 #ifdef RT_AUDIO
+#include "../RtAudioInterface.h"
 #include "RtAudio.h"
 #endif
 
@@ -1123,6 +1124,7 @@ void QJackTrip::virtualStudioMode()
 {
     this->hide();
     m_vs->show();
+    m_vs->toVirtualStudio();
 }
 #endif
 
@@ -1984,26 +1986,21 @@ void QJackTrip::populateDeviceMenu(QComboBox* menu, bool isInput)
     menu->clear();
     menu->addItem(QStringLiteral("(default)"));
 
-    std::vector<RtAudio::Api> apis;
-    RtAudio::getCompiledApi(apis);
-
-    for (uint32_t i = 0; i < apis.size(); i++) {
-        RtAudio rtaudio(apis.at(i));
-        unsigned int devices = rtaudio.getDeviceCount();
-        for (unsigned int j = 0; j < devices; j++) {
-            RtAudio::DeviceInfo info = rtaudio.getDeviceInfo(j);
-            if (info.probed == true) {
-                // Don't include duplicate entries
-                if (menu->findText(QString::fromStdString(info.name)) != -1) {
-                    continue;
-                }
+    QVector<RtAudioDevice> devices;
+    RtAudioInterface::scanDevices(devices);
+    for (auto info : devices) {
+        // convert names to QString to gracefully handle invalid
+        // utf8 character sequences, such as "RØDE Microphone"
+        const QString utf8Name(QString::fromStdString(info.name));
 
-                if (isInput && info.inputChannels > 0) {
-                    menu->addItem(QString::fromStdString(info.name));
-                } else if (!isInput && info.outputChannels > 0) {
-                    menu->addItem(QString::fromStdString(info.name));
-                }
-            }
+        // Don't include duplicate entries
+        if (menu->findText(utf8Name) != -1) {
+            continue;
+        }
+        if (isInput && info.inputChannels > 0) {
+            menu->addItem(utf8Name);
+        } else if (!isInput && info.outputChannels > 0) {
+            menu->addItem(utf8Name);
         }
     }
 
index 8ba4d58b9f877ee41f5aab0e28bcfa4f9b1bb8d2..8e2b9f5bbd6d3399d7cd7f6905e7abca5993292f 100644 (file)
@@ -6,9 +6,11 @@
   </qresource>
   <qresource prefix="vs">
     <file>vs.qml</file>
-    <file>vsftux.qml</file>
     <file>FirstLaunch.qml</file>
     <file>Login.qml</file>
+    <file>Recommendations.qml</file>
+    <file>Permissions.qml</file>
+    <file>ChangeDevices.qml</file>
     <file>Studio.qml</file>
     <file>Browse.qml</file>
     <file>AudioSettings.qml</file>
     <file>Failed.qml</file>
     <file>Setup.qml</file>
     <file>SectionHeading.qml</file>
+    <file>Footer.qml</file>
+    <file>VolumeSlider.qml</file>
+    <file>DeviceControls.qml</file>
+    <file>DeviceControlsGroup.qml</file>
+    <file>DeviceRefreshButton.qml</file>
+    <file>DeviceWarning.qml</file>
+    <file>InfoTooltip.qml</file>
+    <file>Web.qml</file>
+    <file>WebView.qml</file>
+    <file>WebEngine.qml</file>
+    <file>WebNull.qml</file>
+    <file>FeedbackSurvey.qml</file>
+    <file>AppIcon.qml</file>
     <file>logo.svg</file>
     <file>wedge.svg</file>
     <file>wedge_inactive.svg</file>
     <file>join.svg</file>
     <file>leave.svg</file>
     <file>manage.svg</file>
+    <file>speed.svg</file>
     <file>share.svg</file>
     <file>start.svg</file>
+    <file>star.svg</file>
     <file>cog.svg</file>
     <file>mic.svg</file>
+    <file>language.svg</file>
     <file>micoff.svg</file>
     <file>help.svg</file>
     <file>quiet.svg</file>
     <file>loud.svg</file>
     <file>refresh.svg</file>
-    <file>ethernet.png</file>
+    <file>ethernet.svg</file>
+    <file>networkCheck.svg</file>
+    <file>externalMic.svg</file>
     <file>check.svg</file>
-    <file>ohno.png</file>
+    <file>warning.svg</file>
+    <file>expand_less.svg</file>
+    <file>expand_more.svg</file>
+    <file>sentiment_very_dissatisfied.svg</file>
     <file>headphones.svg</file>
     <file>Prompt.svg</file>
     <file>network.svg</file>
diff --git a/src/gui/sentiment_very_dissatisfied.svg b/src/gui/sentiment_very_dissatisfied.svg
new file mode 100644 (file)
index 0000000..5af1e4b
--- /dev/null
@@ -0,0 +1,3 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M12 13.5C9.67 13.5 7.69 14.96 6.89 17H17.11C16.31 14.96 14.33 13.5 12 13.5ZM7.82 12L8.88 10.94L9.94 12L11 10.94L9.94 9.88L11 8.82L9.94 7.76L8.88 8.82L7.82 7.76L6.76 8.82L7.82 9.88L6.76 10.94L7.82 12ZM11.99 2C6.47 2 2 6.47 2 12C2 17.53 6.47 22 11.99 22C17.51 22 22 17.53 22 12C22 6.47 17.52 2 11.99 2ZM12 20C7.58 20 4 16.42 4 12C4 7.58 7.58 4 12 4C16.42 4 20 7.58 20 12C20 16.42 16.42 20 12 20ZM16.18 7.76L15.12 8.82L14.06 7.76L13 8.82L14.06 9.88L13 10.94L14.06 12L15.12 10.94L16.18 12L17.24 10.94L16.18 9.88L17.24 8.82L16.18 7.76Z" fill="black"/>
+</svg>
diff --git a/src/gui/speed.svg b/src/gui/speed.svg
new file mode 100644 (file)
index 0000000..7ab86e4
--- /dev/null
@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M20.3642 8.55102L19.1342 10.401C19.7274 11.5841 20.0178 12.8958 19.9794 14.2187C19.941 15.5416 19.575 16.8343 18.9142 17.981H5.05424C4.19541 16.4911 3.83954 14.7641 4.03938 13.0561C4.23923 11.348 4.98415 9.74984 6.16372 8.49844C7.34329 7.24705 8.89471 6.40906 10.588 6.10871C12.2813 5.80837 14.0262 6.06165 15.5642 6.83102L17.4142 5.60102C15.5307 4.39323 13.2966 3.85202 11.0691 4.06395C8.8417 4.27588 6.74969 5.22871 5.12773 6.77003C3.50578 8.31134 2.4476 10.3521 2.12246 12.5658C1.79732 14.7796 2.22399 17.0384 3.33424 18.981C3.50875 19.2833 3.75933 19.5346 4.06107 19.7101C4.36282 19.8855 4.70521 19.9789 5.05424 19.981H18.9042C19.2567 19.9824 19.6032 19.8907 19.9087 19.7151C20.2143 19.5395 20.468 19.2862 20.6442 18.981C21.5656 17.3849 22.028 15.5653 21.9804 13.723C21.9327 11.8807 21.3769 10.0873 20.3742 8.54102L20.3642 8.55102Z" fill="black"/>
+<path d="M10.5742 15.391C10.76 15.577 10.9806 15.7245 11.2234 15.8251C11.4662 15.9258 11.7264 15.9776 11.9892 15.9776C12.2521 15.9776 12.5123 15.9258 12.7551 15.8251C12.9979 15.7245 13.2185 15.577 13.4042 15.391L19.0642 6.90102L10.5742 12.561C10.3883 12.7468 10.2408 12.9673 10.1401 13.2101C10.0395 13.4529 9.98767 13.7132 9.98767 13.976C9.98767 14.2388 10.0395 14.4991 10.1401 14.7419C10.2408 14.9847 10.3883 15.2053 10.5742 15.391Z" fill="black"/>
+</svg>
diff --git a/src/gui/star.svg b/src/gui/star.svg
new file mode 100644 (file)
index 0000000..971dd91
--- /dev/null
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="48px" viewBox="0 0 24 24" width="48px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M12 17.27l5.17 3.12c.38.23.85-.11.75-.54l-1.37-5.88 4.56-3.95c.33-.29.16-.84-.29-.88l-6.01-.51-2.35-5.54c-.17-.41-.75-.41-.92 0L9.19 8.63l-6.01.51c-.44.04-.62.59-.28.88l4.56 3.95-1.37 5.88c-.1.43.37.77.75.54L12 17.27z"/></svg>
\ No newline at end of file
index f146ecfaf709525b22f94c74e665eafc9ba2e662..0da9a3bfa8e85ed4d7b4c5f6917fc598cc859901 100644 (file)
 #include <QMessageBox>
 #include <QQmlContext>
 #include <QQmlEngine>
+#include <QSettings>
 #include <QSslSocket>
 #include <algorithm>
 #include <iostream>
 
+// TODO: remove me; including this to work-around this bug
+// https://bugreports.qt.io/browse/QTBUG-55199
+#include <QSvgGenerator>
+
+#include "../JackTrip.h"
 #include "../Settings.h"
 #include "../jacktrip_globals.h"
+#include "WebSocketTransport.h"
 #include "about.h"
 #include "qjacktrip.h"
+#include "vsApi.h"
+#include "vsAudio.h"
+#include "vsAuth.h"
+#include "vsDevice.h"
+#include "vsWebSocket.h"
 
-#ifdef USE_WEAK_JACK
-#include "weak_libjack.h"
-#endif
-#ifdef RT_AUDIO
-#include "RtAudio.h"
+#ifdef __APPLE__
+#include "vsMacPermissions.h"
+#else
+#include "vsPermissions.h"
 #endif
 
 #ifdef _WIN32
 
 VirtualStudio::VirtualStudio(bool firstRun, QObject* parent)
     : QObject(parent)
+    , m_audioConfigPtr(
+          new VsAudio(this))  // this needs to be constructed before loadSettings()
     , m_showFirstRun(firstRun)
-    , m_inputMeterLevels(2, 0)
-    , m_outputMeterLevels(2, 0)
-    , m_inputComboModel(QJsonArray::fromStringList(QStringList(QLatin1String(""))))
-    , m_outputComboModel(QJsonArray::fromStringList(QStringList(QLatin1String(""))))
-    , m_inputChannelsComboModel(
-          QJsonArray::fromStringList(QStringList(QLatin1String(""))))
-    , m_outputChannelsComboModel(
-          QJsonArray::fromStringList(QStringList(QLatin1String(""))))
-    , m_inputMixModeComboModel(QJsonArray::fromStringList(QStringList(QLatin1String(""))))
 {
-    QSettings settings;
-    m_updateChannel =
-        settings.value(QStringLiteral("UpdateChannel"), "stable").toString().toLower();
-    settings.beginGroup(QStringLiteral("VirtualStudio"));
-    m_refreshToken    = settings.value(QStringLiteral("RefreshToken"), "").toString();
-    m_userId          = settings.value(QStringLiteral("UserId"), "").toString();
-    m_uiScale         = settings.value(QStringLiteral("UiScale"), 1).toFloat();
-    m_darkMode        = settings.value(QStringLiteral("DarkMode"), false).toBool();
-    m_testMode        = settings.value(QStringLiteral("TestMode"), false).toBool();
-    m_showInactive    = settings.value(QStringLiteral("ShowInactive"), true).toBool();
-    m_showSelfHosted  = settings.value(QStringLiteral("ShowSelfHosted"), false).toBool();
-    m_showDeviceSetup = settings.value(QStringLiteral("ShowDeviceSetup"), true).toBool();
-    m_showWarnings    = settings.value(QStringLiteral("ShowWarnings"), true).toBool();
-    settings.endGroup();
-    m_previousUiScale = m_uiScale;
+    // load or initialize persisted settings
+    loadSettings();
+
+    // TODO: remove me; this is a hack for this bug
+    // https://bugreports.qt.io/browse/QTBUG-55199
+    QSvgGenerator svgImageHack;
 
     // use a singleton QNetworkAccessManager
     m_networkAccessManager.reset(new QNetworkAccessManager);
@@ -102,10 +97,9 @@ VirtualStudio::VirtualStudio(bool firstRun, QObject* parent)
     }
 
     // instantiate auth
-    m_auth.reset(new VsAuth(&m_view, m_networkAccessManager.data(), m_api.data()));
+    m_auth.reset(new VsAuth(m_networkAccessManager.data(), m_api.data()));
     connect(m_auth.data(), &VsAuth::authSucceeded, this,
             &VirtualStudio::slotAuthSucceeded);
-    connect(m_auth.data(), &VsAuth::authFailed, this, &VirtualStudio::slotAuthFailed);
     connect(m_auth.data(), &VsAuth::refreshTokenFailed, this, [=]() {
         m_auth->authenticate(QStringLiteral(""));  // retry without using refresh token
     });
@@ -116,33 +110,22 @@ VirtualStudio::VirtualStudio(bool firstRun, QObject* parent)
         m_auth->authenticate(QStringLiteral(""));  // retry without using refresh token
     });
 
+    m_webChannelServer.reset(new QWebSocketServer(
+        QStringLiteral("Qt6 Virtual Studio Server"), QWebSocketServer::NonSecureMode));
+    connect(m_webChannelServer.data(), &QWebSocketServer::newConnection, this, [=]() {
+        m_webChannel->connectTo(
+            new WebSocketTransport(m_webChannelServer->nextPendingConnection()));
+    });
+
+    m_webChannel.reset(new QWebChannel());
+    m_webChannel->registerObject(QStringLiteral("virtualstudio"), this);
+
     // Load our font for our qml interface
     QFontDatabase::addApplicationFont(QStringLiteral(":/vs/Poppins-Regular.ttf"));
     QFontDatabase::addApplicationFont(QStringLiteral(":/vs/Poppins-Bold.ttf"));
 
-    connect(&m_view, &VsQuickView::windowClose, this, &VirtualStudio::exit);
-
     // Set our font scaling to convert points to pixels
-    m_fontScale = 4.0 / 3.0;
-
-    // Initialize timers needed for clip indicators
-    m_inputClipTimer.setTimerType(Qt::CoarseTimer);
-    m_inputClipTimer.setSingleShot(true);
-    m_inputClipTimer.setInterval(3000);
-    m_outputClipTimer.setTimerType(Qt::CoarseTimer);
-    m_outputClipTimer.setSingleShot(true);
-    m_outputClipTimer.setInterval(3000);
-    m_inputClipTimer.callOnTimeout([&]() {
-        m_inputClipped = false;
-        emit updatedInputClipped(m_inputClipped);
-    });
-    m_outputClipTimer.callOnTimeout([&]() {
-        m_outputClipped = false;
-        emit updatedOutputClipped(m_outputClipped);
-    });
-
-    m_inputMeterLevels[0] = m_inputMeterLevels[1] = 0;
-    m_outputMeterLevels[0] = m_outputMeterLevels[1] = 0;
+    m_fontScale = float(4.0 / 3.0);
 
     // Initialize timer needed for network outage indicator
     m_networkOutageTimer.setTimerType(Qt::CoarseTimer);
@@ -153,191 +136,58 @@ VirtualStudio::VirtualStudio(bool firstRun, QObject* parent)
         emit updatedNetworkOutage(m_networkOutage);
     });
 
-    settings.beginGroup(QStringLiteral("Audio"));
-    m_inMultiplier  = settings.value(QStringLiteral("InMultiplier"), 1).toFloat();
-    m_outMultiplier = settings.value(QStringLiteral("OutMultiplier"), 1).toFloat();
-    m_inMuted       = settings.value(QStringLiteral("InMuted"), false).toBool();
-    m_outMuted      = settings.value(QStringLiteral("OutMuted"), false).toBool();
-    m_feedbackDetectionEnabled =
-        settings.value(QStringLiteral("FeedbackDetectionEnabled"), true).toBool();
-
-#ifdef RT_AUDIO
-    m_useRtAudio   = settings.value(QStringLiteral("Backend"), 1).toInt() == 1;
-    m_inputDevice  = settings.value(QStringLiteral("InputDevice"), "").toString();
-    m_outputDevice = settings.value(QStringLiteral("OutputDevice"), "").toString();
-
-    if (m_inputDevice == QStringLiteral("(default)")) {
-        m_inputDevice = "";
-    }
-    if (m_outputDevice == QStringLiteral("(default)")) {
-        m_outputDevice = "";
-    }
-
-    // use default base channel 0, if the setting does not exist
-    m_baseInputChannel  = settings.value(QStringLiteral("BaseInputChannel"), 0).toInt();
-    m_baseOutputChannel = settings.value(QStringLiteral("BaseOutputChannel"), 0).toInt();
-
-    // Handle migration scenarios. Assume this is a new user
-    // if we have m_inputDevice == "" and m_outputDevice == ""
-    if (m_inputDevice == "" && m_outputDevice == "") {
-        // for fresh installs, use mono by default
-        m_numInputChannels =
-            settings.value(QStringLiteral("NumInputChannels"), 1).toInt();
-        m_inputMixMode = settings
-                             .value(QStringLiteral("InputMixMode"),
-                                    static_cast<int>(AudioInterface::MONO))
-                             .toInt();
-
-        // use 2 channels for output
-        m_numOutputChannels =
-            settings.value(QStringLiteral("NumOutputChannels"), 2).toInt();
-    } else {
-        // existing installs - keep using stereo
-        m_numInputChannels =
-            settings.value(QStringLiteral("NumInputChannels"), 2).toInt();
-        m_inputMixMode = settings
-                             .value(QStringLiteral("InputMixMode"),
-                                    static_cast<int>(AudioInterface::STEREO))
-                             .toInt();
-
-        // use 2 channels for output
-        m_numOutputChannels =
-            settings.value(QStringLiteral("NumOutputChannels"), 2).toInt();
+    if ((m_uiMode == QJackTrip::UNSET && vsFtux())
+        || (m_uiMode == QJackTrip::VIRTUAL_STUDIO)) {
+        m_windowState = QStringLiteral("login");
     }
 
-    m_bufferSize     = settings.value(QStringLiteral("BufferSize"), 128).toInt();
-    m_previousBuffer = m_bufferSize;
-    m_previousInput  = m_inputDevice;
-    m_previousOutput = m_outputDevice;
-
-    if constexpr (!isBackendAvailable<AudioInterfaceMode::ALL>()) {
-        m_selectableBackend = false;
-    }
-#else
-    m_selectableBackend = false;
-    QJsonObject element;
-
-    element.insert(QString::fromStdString("label"), QString::fromStdString("Mono"));
-    element.insert(QString::fromStdString("value"),
-                   static_cast<int>(AudioInterface::MONO));
-    m_inputMixModeComboModel = QJsonArray();
-    m_inputMixModeComboModel.push_back(element);
-
-    element = QJsonObject();
-    element.insert(QString::fromStdString("label"), QString::fromStdString("1"));
-    element.insert(QString::fromStdString("baseChannel"), QVariant(0).toInt());
-    element.insert(QString::fromStdString("numChannels"), QVariant(1).toInt());
-    m_inputChannelsComboModel = QJsonArray();
-    m_inputChannelsComboModel.push_back(element);
-
-    element = QJsonObject();
-    element.insert(QString::fromStdString("label"), QString::fromStdString("1 & 2"));
-    element.insert(QString::fromStdString("baseChannel"), QVariant(0).toInt());
-    element.insert(QString::fromStdString("numChannels"), QVariant(2).toInt());
-    m_outputChannelsComboModel = QJsonArray();
-    m_outputChannelsComboModel.push_back(element);
-
-#endif
-    m_bufferStrategy = settings.value(QStringLiteral("BufferStrategy"), 2).toInt();
-    settings.endGroup();
+    // register QML types
+    qmlRegisterType<VsServerInfo>("org.jacktrip.jacktrip", 1, 0, "VsServerInfo");
 
-#ifdef USE_WEAK_JACK
-    // Check if Jack is available
-    if (have_libjack() != 0) {
-#ifdef RT_AUDIO
-        m_useRtAudio        = true;
-        m_selectableBackend = false;
-#else
-        // TODO: Handle this more gracefully, even if it's an unlikely scenario
-        qFatal("JACK not found and not built with RtAudio support.");
-#endif  // RT_AUDIO
-    }
-#endif  // USE_WEAK_JACK
-#ifdef RT_AUDIO
-    m_previousUseRtAudio = m_useRtAudio;
-#endif
-
-    m_view.engine()->rootContext()->setContextProperty(
-        QStringLiteral("bufferComboModel"), QVariant::fromValue(m_bufferOptions));
-    m_view.engine()->rootContext()->setContextProperty(
-        QStringLiteral("bufferStrategyComboModel"),
-        QVariant::fromValue(m_bufferStrategyOptions));
-    m_view.engine()->rootContext()->setContextProperty(
-        QStringLiteral("updateChannelComboModel"),
-        QVariant::fromValue(m_updateChannelOptions));
-    m_view.engine()->rootContext()->setContextProperty(
-        QStringLiteral("feedbackDetectionComboModel"),
-        QVariant::fromValue(m_feedbackDetectionOptions));
+    // setup QML view
     m_view.engine()->rootContext()->setContextProperty(QStringLiteral("virtualstudio"),
                                                        this);
-    m_view.engine()->rootContext()->setContextProperty(QStringLiteral("serverModel"),
-                                                       QVariant::fromValue(m_servers));
-    m_view.engine()->rootContext()->setContextProperty(QStringLiteral("audioInterface"),
-                                                       m_vsAudioInterface.data());
-    // Add permissions for Mac
-#ifdef __APPLE__
-    m_permissions.reset(new VsMacPermissions());
-    m_view.engine()->rootContext()->setContextProperty(
-        QStringLiteral("permissions"), QVariant::fromValue(m_permissions.data()));
-    if (m_permissions->micPermissionChecked()
-        && m_permissions->micPermission() == "unknown") {
-        m_permissions->getMicPermission();
-    }
-    connect(m_permissions.data(), &VsMacPermissions::micPermissionUpdated, this,
-            &VirtualStudio::startAudio);
-#else
-    m_permissions.reset(new VsPermissions());
+    m_view.engine()->rootContext()->setContextProperty(QStringLiteral("auth"),
+                                                       m_auth.get());
+    m_view.engine()->rootContext()->setContextProperty(QStringLiteral("audio"),
+                                                       m_audioConfigPtr.get());
     m_view.engine()->rootContext()->setContextProperty(
-        QStringLiteral("permissions"), QVariant::fromValue(m_permissions.data()));
-#endif
-
-    resetMeters();
-
-    m_view.engine()->rootContext()->setContextProperty(
-        QStringLiteral("backendComboModel"),
-        QVariant::fromValue(QStringList()
-                            << QStringLiteral("JACK") << QStringLiteral("RtAudio")));
-#ifdef VS_FTUX
-    m_view.setSource(QUrl(QStringLiteral("qrc:/vs/vsftux.qml")));
-#else
+        QStringLiteral("permissions"),
+        QVariant::fromValue(&m_audioConfigPtr->getPermissions()));
     m_view.setSource(QUrl(QStringLiteral("qrc:/vs/vs.qml")));
-#endif  // VS_FTUX
-    m_view.setMinimumSize(QSize(594, 519));
+    m_view.setMinimumSize(QSize(800, 640));
     // m_view.setMaximumSize(QSize(696, 577));
     m_view.setResizeMode(QQuickView::SizeRootObjectToView);
-    m_view.resize(696 * m_uiScale, 676 * m_uiScale);
+    m_view.resize(800 * m_uiScale, 640 * m_uiScale);
 
     // Connect our timers
-    connect(&m_retryPeriodTimer, &QTimer::timeout, this, &VirtualStudio::endRetryPeriod);
     connect(&m_refreshTimer, &QTimer::timeout, this, [&]() {
-        m_refreshMutex.lock();
-        if (m_allowRefresh) {
-            m_refreshMutex.unlock();
-            emit periodicRefresh();
-        } else {
-            m_refreshMutex.unlock();
-        }
-    });
-
-    connect(&m_heartbeatTimer, &QTimer::timeout, this, [&]() {
-        sendHeartbeat();
+        emit periodicRefresh();
     });
+    connect(&m_heartbeatTimer, &QTimer::timeout, this, &VirtualStudio::sendHeartbeat,
+            Qt::QueuedConnection);
 
     // QueuedConnection since refreshFinished is sometimes signaled from a network reply
     // thread
     connect(this, &VirtualStudio::refreshFinished, this, &VirtualStudio::joinStudio,
             Qt::QueuedConnection);
 
-    connect(this, &VirtualStudio::audioActivatedChanged, this,
-            &VirtualStudio::toggleAudio, Qt::QueuedConnection);
-    connect(
-        this, &VirtualStudio::studioToJoinChanged, this,
-        [&]() {
-            if (!m_studioToJoin.isEmpty()) {
-                joinStudio();
-            }
-        },
-        Qt::QueuedConnection);
+    // handle audio config errors
+    connect(&m_audioConfigPtr->getWorker(), &VsAudioWorker::signalError, this,
+            &VirtualStudio::processError, Qt::QueuedConnection);
+
+    // when connected to server, trigger UI modal when feedback is detected
+    connect(m_audioConfigPtr.get(), &VsAudio::feedbackDetected, this,
+            &VirtualStudio::detectedFeedbackLoop, Qt::QueuedConnection);
+
+    // call exit() when the UI window is closed
+    connect(&m_view, &VsQuickView::windowClose, this, &VirtualStudio::exit,
+            Qt::QueuedConnection);
+
+    if ((m_uiMode == QJackTrip::UNSET && vsFtux())
+        || (m_uiMode == QJackTrip::VIRTUAL_STUDIO)) {
+        login();
+    }
 }
 
 void VirtualStudio::setStandardWindow(QSharedPointer<QJackTrip> window)
@@ -364,9 +214,6 @@ void VirtualStudio::show()
         }
         m_checkSsl = false;
     }
-    if (m_windowState == "login") {
-        login();
-    }
     m_view.show();
 }
 
@@ -376,9 +223,9 @@ void VirtualStudio::raiseToTop()
     m_view.requestActivate();  // Raise to top
 }
 
-bool VirtualStudio::vsModeActive()
+int VirtualStudio::webChannelPort()
 {
-    return m_vsModeActive;
+    return m_webChannelPort;
 }
 
 bool VirtualStudio::showFirstRun()
@@ -388,6 +235,8 @@ bool VirtualStudio::showFirstRun()
 
 void VirtualStudio::setShowFirstRun(bool show)
 {
+    if (m_showFirstRun == show)
+        return;
     m_showFirstRun = show;
     emit showFirstRunChanged();
 }
@@ -407,216 +256,6 @@ QString VirtualStudio::logoSection()
     return m_logoSection;
 }
 
-bool VirtualStudio::selectableBackend()
-{
-    return m_selectableBackend;
-}
-
-QString VirtualStudio::audioBackend()
-{
-    return m_useRtAudio ? QStringLiteral("RtAudio") : QStringLiteral("JACK");
-}
-
-void VirtualStudio::setAudioBackend(const QString& backend)
-{
-    if (!m_selectableBackend) {
-        return;
-    }
-    m_useRtAudio = (backend == QStringLiteral("RtAudio"));
-    emit audioBackendChanged(m_useRtAudio);
-}
-
-QString VirtualStudio::inputDevice()
-{
-#ifdef RT_AUDIO
-    return m_inputDevice;
-#endif
-    return QStringLiteral("");
-}
-
-void VirtualStudio::setInputDevice([[maybe_unused]] const QString& device)
-{
-    if (!m_useRtAudio) {
-        return;
-    }
-#ifdef RT_AUDIO
-    m_inputDevice = device;
-    emit inputDeviceChanged(m_inputDevice, false);
-#endif
-}
-
-#ifdef RT_AUDIO
-int VirtualStudio::baseInputChannel()
-{
-    if (m_useRtAudio) {
-        return m_baseInputChannel;
-    }
-    return 0;
-}
-
-void VirtualStudio::setBaseInputChannel([[maybe_unused]] int baseChannel)
-{
-    if (!m_useRtAudio) {
-        return;
-    }
-    m_baseInputChannel = baseChannel;
-    emit baseInputChannelChanged(m_baseInputChannel, true);
-}
-
-int VirtualStudio::numInputChannels()
-{
-    if (m_useRtAudio) {
-        return m_numInputChannels;
-    }
-    return 0;
-}
-
-void VirtualStudio::setNumInputChannels([[maybe_unused]] int numChannels)
-{
-    if (!m_useRtAudio) {
-        return;
-    }
-    m_numInputChannels = numChannels;
-    emit numInputChannelsChanged(m_numInputChannels, true);
-}
-
-void VirtualStudio::setInputMixMode(int mode)
-{
-    if (!m_useRtAudio) {
-        return;
-    }
-    m_inputMixMode = mode;
-    emit inputMixModeChanged(m_inputMixMode, true);
-}
-
-int VirtualStudio::inputMixMode()
-{
-    if (!m_useRtAudio) {
-        return -1;
-    }
-    return m_inputMixMode;
-}
-#endif
-
-QString VirtualStudio::outputDevice()
-{
-#ifdef RT_AUDIO
-    return m_outputDevice;
-#endif
-    return QStringLiteral("");
-}
-
-void VirtualStudio::setOutputDevice([[maybe_unused]] const QString& device)
-{
-    if (!m_useRtAudio) {
-        return;
-    }
-#ifdef RT_AUDIO
-    m_outputDevice = device;
-    emit outputDeviceChanged(m_outputDevice, false);
-#endif
-}
-
-#ifdef RT_AUDIO
-int VirtualStudio::baseOutputChannel()
-{
-    if (m_useRtAudio) {
-        return m_baseOutputChannel;
-    }
-    return 0;
-}
-
-void VirtualStudio::setBaseOutputChannel([[maybe_unused]] int baseChannel)
-{
-    if (!m_useRtAudio) {
-        return;
-    }
-    m_baseOutputChannel = baseChannel;
-    emit baseOutputChannelChanged(m_baseOutputChannel, true);
-}
-
-int VirtualStudio::numOutputChannels()
-{
-    if (m_useRtAudio) {
-        return m_numOutputChannels;
-    }
-    return 0;
-}
-
-void VirtualStudio::setNumOutputChannels([[maybe_unused]] int numChannels)
-{
-    if (!m_useRtAudio) {
-        return;
-    }
-    m_numOutputChannels = numChannels;
-    emit numOutputChannelsChanged(m_numOutputChannels, true);
-}
-#endif
-
-int VirtualStudio::previousInput()
-{
-#ifdef RT_AUDIO
-    if (m_useRtAudio) {
-        int index = m_inputDeviceList.indexOf(m_previousInput);
-        return index >= 0 ? index : 0;
-    }
-#endif
-    return 0;
-}
-
-void VirtualStudio::setPreviousInput([[maybe_unused]] int device)
-{
-    if (!m_useRtAudio) {
-        return;
-    }
-#ifdef RT_AUDIO
-    m_previousInput = m_inputDeviceList.at(device);
-    emit previousInputChanged();
-#endif
-}
-
-int VirtualStudio::previousOutput()
-{
-#ifdef RT_AUDIO
-    if (m_useRtAudio) {
-        int index = m_outputDeviceList.indexOf(m_previousOutput);
-        return index >= 0 ? index : 0;
-    }
-#endif
-    return 0;
-}
-
-void VirtualStudio::setPreviousOutput([[maybe_unused]] int device)
-{
-    if (!m_useRtAudio) {
-        return;
-    }
-#ifdef RT_AUDIO
-    m_previousOutput = m_outputDeviceList.at(device);
-    emit previousOutputChanged();
-#endif
-}
-
-QString VirtualStudio::devicesWarning()
-{
-    return m_devicesWarningMsg;
-}
-
-QString VirtualStudio::devicesError()
-{
-    return m_devicesErrorMsg;
-}
-
-QString VirtualStudio::devicesWarningHelpUrl()
-{
-    return m_devicesWarningHelpUrl;
-}
-
-QString VirtualStudio::devicesErrorHelpUrl()
-{
-    return m_devicesErrorHelpUrl;
-}
-
 QString VirtualStudio::connectedErrorMsg()
 {
     return m_connectedErrorMsg;
@@ -624,207 +263,17 @@ QString VirtualStudio::connectedErrorMsg()
 
 void VirtualStudio::setConnectedErrorMsg(const QString& msg)
 {
+    if (m_connectedErrorMsg == msg)
+        return;
     m_connectedErrorMsg = msg;
     emit connectedErrorMsgChanged();
 }
 
-float VirtualStudio::inputVolume()
-{
-    return m_inMultiplier;
-}
-
-float VirtualStudio::outputVolume()
-{
-    return m_outMultiplier;
-}
-
-float VirtualStudio::monitorVolume()
-{
-    return m_monMultiplier;
-}
-
-bool VirtualStudio::inputMuted()
-{
-    return m_inMuted;
-}
-
-bool VirtualStudio::outputMuted()
-{
-    return m_outMuted;
-}
-
-bool VirtualStudio::monitorMuted()
-{
-    return m_monMuted;
-}
-
-bool VirtualStudio::audioActivated()
-{
-    return m_audioActivated;
-}
-
-bool VirtualStudio::audioReady()
-{
-    return m_audioReady;
-}
-
-bool VirtualStudio::inputClipped()
-{
-    return m_inputClipped;
-}
-
-bool VirtualStudio::outputClipped()
-{
-    return m_outputClipped;
-}
-
 bool VirtualStudio::networkOutage()
 {
     return m_networkOutage;
 }
 
-bool VirtualStudio::backendAvailable()
-{
-    if constexpr ((isBackendAvailable<AudioInterfaceMode::JACK>()
-                   || isBackendAvailable<AudioInterfaceMode::RTAUDIO>())) {
-        return true;
-    } else {
-        return false;
-    }
-}
-
-void VirtualStudio::setInputVolume(float multiplier)
-{
-    m_inMultiplier = multiplier;
-    QSettings settings;
-    settings.beginGroup(QStringLiteral("Audio"));
-    settings.setValue(QStringLiteral("InMultiplier"), m_inMultiplier);
-    settings.endGroup();
-    emit updatedInputVolume(multiplier);
-}
-
-void VirtualStudio::setOutputVolume(float multiplier)
-{
-    m_outMultiplier = multiplier;
-    QSettings settings;
-    settings.beginGroup(QStringLiteral("Audio"));
-    settings.setValue(QStringLiteral("OutMultiplier"), m_outMultiplier);
-    settings.endGroup();
-    emit updatedOutputVolume(multiplier);
-}
-
-void VirtualStudio::setMonitorVolume(float multiplier)
-{
-    m_monMultiplier = multiplier;
-    QSettings settings;
-    settings.beginGroup(QStringLiteral("Audio"));
-    settings.setValue(QStringLiteral("MonMultiplier"), m_monMultiplier);
-    settings.endGroup();
-    emit updatedMonitorVolume(multiplier);
-}
-
-void VirtualStudio::setInputMuted(bool muted)
-{
-    m_inMuted = muted;
-    QSettings settings;
-    settings.beginGroup(QStringLiteral("Audio"));
-    settings.setValue(QStringLiteral("InMuted"), m_inMuted ? 1 : 0);
-    settings.endGroup();
-    emit updatedInputMuted(muted);
-}
-
-void VirtualStudio::setOutputMuted(bool muted)
-{
-    m_outMuted = muted;
-    QSettings settings;
-    settings.beginGroup(QStringLiteral("Audio"));
-    settings.setValue(QStringLiteral("OutMuted"), m_outMuted ? 1 : 0);
-    settings.endGroup();
-    emit updatedOutputMuted(muted);
-}
-
-void VirtualStudio::setMonitorMuted(bool muted)
-{
-    m_monMuted = muted;
-    QSettings settings;
-    settings.beginGroup(QStringLiteral("Audio"));
-    settings.setValue(QStringLiteral("MonMuted"), m_monMuted ? 1 : 0);
-    settings.endGroup();
-    emit updatedMonitorMuted(muted);
-}
-
-int VirtualStudio::bufferSize()
-{
-#ifdef RT_AUDIO
-    if (m_useRtAudio) {
-        int index = m_bufferOptions.indexOf(QString::number(m_bufferSize));
-        // It shouldn't be possible that our buffer size doesn't exists
-        // but default to 128 if something goes wrong.
-        return index >= 0 ? index : m_bufferOptions.indexOf(QStringLiteral("128"));
-    }
-#endif
-    return 3;
-}
-
-void VirtualStudio::setBufferSize([[maybe_unused]] int index)
-{
-    if (!m_useRtAudio) {
-        return;
-    }
-#ifdef RT_AUDIO
-    m_bufferSize = m_bufferOptions.at(index).toInt();
-#endif
-}
-
-int VirtualStudio::bufferStrategy()
-{
-    return m_bufferStrategy;
-}
-
-void VirtualStudio::setBufferStrategy(int index)
-{
-    m_bufferStrategy =
-        index >= 0 ? index
-                   : m_bufferStrategyOptions.indexOf(QStringLiteral("Minimal Latency"));
-    QSettings settings;
-    settings.beginGroup(QStringLiteral("Audio"));
-    settings.setValue(QStringLiteral("BufferStrategy"), m_bufferStrategy);
-    settings.endGroup();
-}
-
-bool VirtualStudio::feedbackDetectionEnabled()
-{
-    return m_feedbackDetectionEnabled;
-}
-
-void VirtualStudio::setFeedbackDetectionEnabled(bool enabled)
-{
-    m_feedbackDetectionEnabled = enabled;
-    QSettings settings;
-    settings.beginGroup(QStringLiteral("Audio"));
-    settings.setValue(QStringLiteral("FeedbackDetectionEnabled"),
-                      m_feedbackDetectionEnabled);
-    settings.endGroup();
-    emit feedbackDetectionEnabledChanged();
-}
-
-void VirtualStudio::setAudioActivated(bool activated)
-{
-    m_audioActivated = activated;
-    emit audioActivatedChanged();
-}
-
-void VirtualStudio::setAudioReady(bool ready)
-{
-    m_audioReady = ready;
-    emit audioReadyChanged();
-}
-
-int VirtualStudio::currentStudio()
-{
-    return m_currentStudio;
-}
-
 QJsonObject VirtualStudio::regions()
 {
     return m_regions;
@@ -845,41 +294,6 @@ QJsonObject VirtualStudio::networkStats()
     return m_networkStats;
 }
 
-const QVector<float>& VirtualStudio::inputMeterLevels() const
-{
-    return m_inputMeterLevels;
-}
-
-const QVector<float>& VirtualStudio::outputMeterLevels() const
-{
-    return m_outputMeterLevels;
-}
-
-const QJsonArray& VirtualStudio::inputComboModel() const
-{
-    return m_inputComboModel;
-}
-
-const QJsonArray& VirtualStudio::outputComboModel() const
-{
-    return m_outputComboModel;
-}
-
-const QJsonArray& VirtualStudio::inputChannelsComboModel() const
-{
-    return m_inputChannelsComboModel;
-}
-
-const QJsonArray& VirtualStudio::outputChannelsComboModel() const
-{
-    return m_outputChannelsComboModel;
-}
-
-const QJsonArray& VirtualStudio::inputMixModeComboModel() const
-{
-    return m_inputMixModeComboModel;
-}
-
 QString VirtualStudio::updateChannel()
 {
     return m_updateChannel;
@@ -887,9 +301,9 @@ QString VirtualStudio::updateChannel()
 
 void VirtualStudio::setUpdateChannel(const QString& channel)
 {
+    if (m_updateChannel == channel)
+        return;
     m_updateChannel = channel;
-    QSettings settings;
-    settings.setValue(QStringLiteral("UpdateChannel"), m_updateChannel);
     emit updateChannelChanged();
 }
 
@@ -900,7 +314,11 @@ bool VirtualStudio::showInactive()
 
 void VirtualStudio::setShowInactive(bool inactive)
 {
+    if (m_showInactive == inactive)
+        return;
     m_showInactive = inactive;
+    emit showInactiveChanged();
+
     QSettings settings;
     settings.beginGroup(QStringLiteral("VirtualStudio"));
     settings.setValue(QStringLiteral("ShowInactive"), m_showInactive);
@@ -914,7 +332,11 @@ bool VirtualStudio::showSelfHosted()
 
 void VirtualStudio::setShowSelfHosted(bool selfHosted)
 {
+    if (m_showSelfHosted == selfHosted)
+        return;
     m_showSelfHosted = selfHosted;
+    emit showSelfHostedChanged();
+
     QSettings settings;
     settings.beginGroup(QStringLiteral("VirtualStudio"));
     settings.setValue(QStringLiteral("ShowSelfHosted"), m_showSelfHosted);
@@ -928,6 +350,8 @@ bool VirtualStudio::showCreateStudio()
 
 void VirtualStudio::setShowCreateStudio(bool createStudio)
 {
+    if (m_showCreateStudio == createStudio)
+        return;
     m_showCreateStudio = createStudio;
     emit showCreateStudioChanged();
 }
@@ -939,7 +363,10 @@ bool VirtualStudio::showDeviceSetup()
 
 void VirtualStudio::setShowDeviceSetup(bool show)
 {
+    if (m_showDeviceSetup == show)
+        return;
     m_showDeviceSetup = show;
+    emit showDeviceSetupChanged();
 }
 
 QString VirtualStudio::windowState()
@@ -949,6 +376,8 @@ QString VirtualStudio::windowState()
 
 void VirtualStudio::setWindowState(QString state)
 {
+    if (m_windowState == state)
+        return;
     m_windowState = state;
     emit windowStateUpdated();
 }
@@ -960,6 +389,8 @@ QString VirtualStudio::apiHost()
 
 void VirtualStudio::setApiHost(QString host)
 {
+    if (m_apiHost == host)
+        return;
     m_apiHost = host;
     emit apiHostChanged();
 }
@@ -969,6 +400,22 @@ bool VirtualStudio::vsFtux()
     return m_vsFtux;
 }
 
+bool VirtualStudio::isExiting()
+{
+    return m_isExiting;
+}
+
+void VirtualStudio::collectFeedbackSurvey(QString serverId, int rating, QString message)
+{
+    QJsonObject feedback;
+    feedback.insert(QStringLiteral("rating"), rating);
+    feedback.insert(QStringLiteral("message"), message);
+
+    QJsonDocument data = QJsonDocument(feedback);
+    m_api->submitServerFeedback(serverId, data.toJson());
+    return;
+}
+
 bool VirtualStudio::showWarnings()
 {
     return m_showWarnings;
@@ -976,16 +423,10 @@ bool VirtualStudio::showWarnings()
 
 void VirtualStudio::setShowWarnings(bool show)
 {
+    if (m_showWarnings == show)
+        return;
     m_showWarnings = show;
-    QSettings settings;
-    settings.beginGroup(QStringLiteral("VirtualStudio"));
-    settings.setValue(QStringLiteral("ShowWarnings"), m_showWarnings);
-    settings.endGroup();
     emit showWarningsChanged();
-    // attempt to join studio if requested
-    if (!m_studioToJoin.isEmpty()) {
-        joinStudio();
-    }
 }
 
 float VirtualStudio::fontScale()
@@ -1000,6 +441,8 @@ float VirtualStudio::uiScale()
 
 void VirtualStudio::setUiScale(float scale)
 {
+    if (scale == m_uiScale)
+        return;
     m_uiScale = scale;
     emit uiScaleChanged();
 }
@@ -1011,14 +454,25 @@ bool VirtualStudio::darkMode()
 
 void VirtualStudio::setDarkMode(bool dark)
 {
+    if (dark == m_darkMode)
+        return;
     m_darkMode = dark;
-    QSettings settings;
-    settings.beginGroup(QStringLiteral("VirtualStudio"));
-    settings.setValue(QStringLiteral("DarkMode"), m_darkMode);
-    settings.endGroup();
     emit darkModeChanged();
 }
 
+bool VirtualStudio::collapseDeviceControls()
+{
+    return m_collapseDeviceControls;
+}
+
+void VirtualStudio::setCollapseDeviceControls(bool collapseDeviceControls)
+{
+    if (m_collapseDeviceControls == collapseDeviceControls)
+        return;
+    m_collapseDeviceControls = collapseDeviceControls;
+    emit collapseDeviceControlsChanged(collapseDeviceControls);
+}
+
 bool VirtualStudio::testMode()
 {
     return m_testMode;
@@ -1026,6 +480,9 @@ bool VirtualStudio::testMode()
 
 void VirtualStudio::setTestMode(bool test)
 {
+    if (m_testMode == test)
+        return;
+
     QString userEmail = m_userMetadata[QStringLiteral("email")].toString();
     if (m_userMetadata.isEmpty() || userEmail == ""
         || !userEmail.endsWith("@jacktrip.org")) {
@@ -1033,6 +490,14 @@ void VirtualStudio::setTestMode(bool test)
         return;
     }
 
+    // deregister app
+    if (!m_devicePtr.isNull()) {
+        m_devicePtr->removeApp();
+        m_devicePtr->disconnect();
+        m_devicePtr.reset();
+    }
+    m_webChannelServer->close();
+
     m_testMode = test;
 
     // clear existing auth state
@@ -1045,20 +510,9 @@ void VirtualStudio::setTestMode(bool test)
     settings.setValue(QStringLiteral("TestMode"), m_testMode);
     settings.remove(QStringLiteral("RefreshToken"));
     settings.remove(QStringLiteral("UserId"));
-    settings.remove(QStringLiteral("ShowInactive"));
-    settings.remove(QStringLiteral("ShowSelfHosted"));
-    settings.remove(QStringLiteral("ShowDeviceSetup"));
-    settings.remove(QStringLiteral("ShowWarnings"));
     settings.endGroup();
 
-    // deregister app
-    if (m_device != nullptr) {
-        m_device->removeApp();
-    }
-
-    // stop timers, clear data, etc.
-    m_refreshTimer.stop();
-    m_heartbeatTimer.stop();
+    // clear user data
     m_userMetadata = QJsonObject();
     m_userId.clear();
 
@@ -1075,6 +529,8 @@ QUrl VirtualStudio::studioToJoin()
 
 void VirtualStudio::setStudioToJoin(const QUrl& url)
 {
+    if (m_studioToJoin == url)
+        return;
     m_studioToJoin = url;
     emit studioToJoinChanged();
 }
@@ -1104,27 +560,26 @@ QString VirtualStudio::failedMessage()
 
 void VirtualStudio::joinStudio()
 {
+    QMutexLocker locker(&m_refreshMutex);
     bool authenticated = m_auth->isAuthenticated();
     if (!authenticated || m_studioToJoin.isEmpty() || m_servers.isEmpty()) {
         // No servers yet. Making sure we have them.
         // getServerList emits refreshFinished which
         // will come back to this function.
         if (authenticated && !m_studioToJoin.isEmpty() && m_servers.isEmpty()) {
-            getServerList(true, true);
+            locker.unlock();
+            getServerList(true);
         }
         return;
     }
-
-    // FTUX shows warnings and device setup views
-    // if any of these enabled, do not immediately join
-    if (!readyToJoin()) {
-        return;
+    if (m_windowState != "connected") {
+        return;  // on audio setup screen before joining the studio
     }
 
     QString scheme = m_studioToJoin.scheme();
     QString path   = m_studioToJoin.path();
     QString url    = m_studioToJoin.toString();
-    m_studioToJoin.clear();
+    setStudioToJoin(QUrl(""));
 
     m_failedMessage = "";
     if (scheme != "jacktrip" || path.length() <= 1) {
@@ -1135,13 +590,20 @@ void VirtualStudio::joinStudio()
     }
     QString targetId = path.remove(0, 1);
 
-    int i = 0;
-    for (i = 0; i < m_servers.count(); i++) {
-        if (static_cast<VsServerInfo*>(m_servers.at(i))->id() == targetId) {
-            connectToStudio(i);
-            return;
+    VsServerInfoPointer sPtr;
+    for (const VsServerInfoPointer& s : m_servers) {
+        if (s->id() == targetId) {
+            sPtr = s;
+            break;
         }
     }
+    locker.unlock();
+
+    if (!sPtr.isNull()) {
+        connectToStudio(*sPtr);
+        return;
+    }
+
     m_failedMessage = "Unable to find studio " + targetId;
     emit failedMessageChanged();
     emit failed();
@@ -1149,13 +611,17 @@ void VirtualStudio::joinStudio()
 
 void VirtualStudio::toStandard()
 {
-    if (!m_standardWindow.isNull()) {
-        m_view.hide();
-        m_standardWindow->show();
-        m_vsModeActive = false;
-    }
+    if (m_standardWindow.isNull())
+        qDebug() << "Unable to switch modes: standard window is missing!";
+
+    m_view.hide();
+    m_standardWindow->show();
+
     QSettings settings;
-    settings.setValue(QStringLiteral("UiMode"), QJackTrip::STANDARD);
+    m_uiMode = QJackTrip::STANDARD;
+    settings.setValue(QStringLiteral("UiMode"), m_uiMode);
+
+    m_webChannelServer->close();
     m_refreshTimer.stop();
     m_heartbeatTimer.stop();
 
@@ -1167,6 +633,13 @@ void VirtualStudio::toStandard()
 
 void VirtualStudio::toVirtualStudio()
 {
+    QSettings settings;
+    m_uiMode = QJackTrip::VIRTUAL_STUDIO;
+    settings.setValue(QStringLiteral("UiMode"), m_uiMode);
+
+    if (m_windowState == "start") {
+        setWindowState(QStringLiteral("login"));
+    }
     if (m_windowState == "login") {
         login();
     }
@@ -1183,10 +656,15 @@ void VirtualStudio::login()
 
 void VirtualStudio::logout()
 {
-    if (m_device != nullptr) {
-        m_device->removeApp();
+    // deregister app
+    if (!m_devicePtr.isNull()) {
+        m_devicePtr->removeApp();
+        m_devicePtr->disconnect();
+        m_devicePtr.reset();
     }
 
+    m_webChannelServer->close();
+
     QUrl logoutURL = QUrl("https://auth.jacktrip.org/v2/logout");
     QUrlQuery query;
     query.addQueryItem(QStringLiteral("client_id"), AUTH_CLIENT_ID);
@@ -1207,15 +685,9 @@ void VirtualStudio::logout()
     settings.beginGroup(QStringLiteral("VirtualStudio"));
     settings.remove(QStringLiteral("RefreshToken"));
     settings.remove(QStringLiteral("UserId"));
-    settings.remove(QStringLiteral("ShowInactive"));
-    settings.remove(QStringLiteral("ShowSelfHosted"));
-    settings.remove(QStringLiteral("ShowDeviceSetup"));
-    settings.remove(QStringLiteral("ShowWarnings"));
     settings.endGroup();
 
-    m_refreshTimer.stop();
-    m_heartbeatTimer.stop();
-
+    // clear user data
     m_refreshToken.clear();
     m_userMetadata = QJsonObject();
     m_userId.clear();
@@ -1223,408 +695,90 @@ void VirtualStudio::logout()
 
     // reset window state
     setWindowState(QStringLiteral("login"));
+    login();  // called to retrieve new code flow token
 }
 
 void VirtualStudio::refreshStudios(int index, bool signalRefresh)
 {
     getSubscriptions();
-    getServerList(false, signalRefresh, index);
-}
-
-void VirtualStudio::refreshDevices()
-{
-#ifdef RT_AUDIO
-    if (m_vsAudioInterface.isNull())
-        return;
-    m_vsAudioInterface->closeAudio();
-    setAudioReady(false);
-    restartAudio();
-#endif
-}
-
-void VirtualStudio::refreshRtAudioDevices()
-{
-    if (!m_useRtAudio || m_vsAudioInterface.isNull())
-        return;
-#ifdef RT_AUDIO
-    m_vsAudioInterface->refreshRtAudioDevices();
-    m_vsAudioInterface->getDeviceList(&m_inputDeviceList, &m_inputDeviceCategories,
-                                      &m_inputDeviceChannels, true);
-    m_vsAudioInterface->getDeviceList(&m_outputDeviceList, &m_outputDeviceCategories,
-                                      &m_outputDeviceChannels, false);
-    m_inputComboModel  = formatDeviceList(m_inputDeviceList, m_inputDeviceCategories,
-                                          m_inputDeviceChannels);
-    m_outputComboModel = formatDeviceList(m_outputDeviceList, m_outputDeviceCategories,
-                                          m_outputDeviceChannels);
-    emit inputComboModelChanged();
-    emit outputComboModelChanged();
-#endif
-}
-
-void VirtualStudio::validateDevicesState()
-{
-    validateInputDevicesState();
-    validateOutputDevicesState();
-    if (m_useRtAudio && m_connectionState == "Connected") {
-        triggerReconnect();
-    }
-}
-
-void VirtualStudio::validateInputDevicesState()
-{
-    if (!m_useRtAudio) {
-        return;
-    }
-#ifdef RT_AUDIO
-    if (m_inputDeviceList.size() == 0 || m_outputDeviceList.size() == 0) {
-        return;
-    }
-
-    // Given input device list, check that the currently set device
-    // actually exists
-    if (m_inputDevice == QStringLiteral("")
-        || m_inputDeviceList.indexOf(m_inputDevice) == -1) {
-        m_inputDevice = m_inputDeviceList[0];
-    }
-    emit inputDeviceChanged(m_inputDevice, false);
-
-    // Given the currently selected input device, reset the available input channel
-    // options
-    int indexOfInput = m_inputDeviceList.indexOf(m_inputDevice);
-    if (indexOfInput == -1) {
-        std::cerr << "Invalid state. Input device index should never be -1" << std::endl;
-        return;
-    }
-
-    int numDevicesChannelsAvailable = m_inputDeviceChannels.at(indexOfInput);
-    if (numDevicesChannelsAvailable < 1) {
-        std::cerr << "Invalid state. Number of channels should never be less than 1"
-                  << std::endl;
-        return;
-    } else if (numDevicesChannelsAvailable == 1) {
-        // Set the input mix mode to just have "Mono" as the option
-        QJsonObject inputMixModeComboElement = QJsonObject();
-        inputMixModeComboElement.insert(QString::fromStdString("label"),
-                                        QString::fromStdString("Mono"));
-        inputMixModeComboElement.insert(QString::fromStdString("value"),
-                                        static_cast<int>(AudioInterface::MONO));
-        m_inputMixModeComboModel = QJsonArray();
-        m_inputMixModeComboModel.push_back(inputMixModeComboElement);
-        emit inputMixModeComboModelChanged();
-
-        // Set the input channels combo to only have channel 1 as an option
-        QJsonObject inputChannelsComboElement;
-        inputChannelsComboElement.insert(QString::fromStdString("label"),
-                                         QString::fromStdString("1"));
-        inputChannelsComboElement.insert(QString::fromStdString("baseChannel"),
-                                         QVariant(0).toInt());
-        inputChannelsComboElement.insert(QString::fromStdString("numChannels"),
-                                         QVariant(1).toInt());
-        m_inputChannelsComboModel = QJsonArray();
-        m_inputChannelsComboModel.push_back(inputChannelsComboElement);
-        emit inputChannelsComboModelChanged();
-
-        // Set the only allowed options for these variables automatically
-        m_baseInputChannel = 0;
-        m_numInputChannels = 1;
-        m_inputMixMode     = static_cast<int>(AudioInterface::MONO);
-
-        emit baseInputChannelChanged(m_baseInputChannel, false);
-        emit numInputChannelsChanged(m_numInputChannels, false);
-        emit inputMixModeChanged(m_inputMixMode, false);
-    } else {
-        // set the input channels selector to have the options based on the currently
-        // selected device
-        m_inputChannelsComboModel = QJsonArray();
-        for (int i = 0; i < numDevicesChannelsAvailable; i++) {
-            QJsonObject element = QJsonObject();
-            element.insert(QString::fromStdString("label"), QVariant(i + 1).toString());
-            element.insert(QString::fromStdString("baseChannel"), QVariant(i).toInt());
-            element.insert(QString::fromStdString("numChannels"), QVariant(1).toInt());
-            m_inputChannelsComboModel.push_back(element);
-        }
-        for (int i = 0; i < numDevicesChannelsAvailable; i++) {
-            if (i % 2 == 0) {
-                QJsonObject element = QJsonObject();
-                element.insert(
-                    QString::fromStdString("label"),
-                    QVariant(i + 1).toString() + " & " + QVariant(i + 2).toString());
-                element.insert(QString::fromStdString("baseChannel"),
-                               QVariant(i).toInt());
-                element.insert(QString::fromStdString("numChannels"),
-                               QVariant(2).toInt());
-                m_inputChannelsComboModel.push_back(element);
-            }
-        }
-        emit inputChannelsComboModelChanged();
-
-        // if the current m_baseInputChannel or m_numInputChannels is invalid based on
-        // this device's option, use the first two channels by default
-        if (m_baseInputChannel + m_numInputChannels > numDevicesChannelsAvailable) {
-            // we're in the case where numDevicesChannelsAvailable >= 2, so always have
-            // the ability to use the first 2 channels
-            m_baseInputChannel = 0;
-            m_numInputChannels = 2;
-            emit baseInputChannelChanged(m_baseInputChannel, false);
-            emit numInputChannelsChanged(m_numInputChannels, false);
-        }
-        if (m_numInputChannels != 1) {
-            // Set the input mix mode to have two options: "Stereo" and "Mix to Mono" if
-            // we're using 2 channels
-            QJsonObject inputMixModeComboElement1 = QJsonObject();
-            inputMixModeComboElement1.insert(QString::fromStdString("label"),
-                                             QString::fromStdString("Stereo"));
-            inputMixModeComboElement1.insert(QString::fromStdString("value"),
-                                             static_cast<int>(AudioInterface::STEREO));
-            QJsonObject inputMixModeComboElement2 = QJsonObject();
-            inputMixModeComboElement2.insert(QString::fromStdString("label"),
-                                             QString::fromStdString("Mix to Mono"));
-            inputMixModeComboElement2.insert(QString::fromStdString("value"),
-                                             static_cast<int>(AudioInterface::MIXTOMONO));
-            m_inputMixModeComboModel = QJsonArray();
-            m_inputMixModeComboModel.push_back(inputMixModeComboElement1);
-            m_inputMixModeComboModel.push_back(inputMixModeComboElement2);
-            emit inputMixModeComboModelChanged();
-
-            // if m_inputMixMode is an invalid value, set it to "stereo" by default
-            // given that we are using 2 channels
-            if (m_inputMixMode != static_cast<int>(AudioInterface::STEREO)
-                && m_inputMixMode != static_cast<int>(AudioInterface::MIXTOMONO)) {
-                m_inputMixMode = static_cast<int>(AudioInterface::STEREO);
-                emit inputMixModeChanged(m_inputMixMode, false);
-            }
-        } else {
-            // Set the input mix mode to just have "Mono" as the option if we're using 1
-            // channel
-            QJsonObject inputMixModeComboElement = QJsonObject();
-            inputMixModeComboElement.insert(QString::fromStdString("label"),
-                                            QString::fromStdString("Mono"));
-            inputMixModeComboElement.insert(QString::fromStdString("value"),
-                                            static_cast<int>(AudioInterface::MONO));
-            m_inputMixModeComboModel = QJsonArray();
-            m_inputMixModeComboModel.push_back(inputMixModeComboElement);
-            emit inputMixModeComboModelChanged();
-
-            // if m_inputMixMode is an invalid value, set it to AudioInterface::MONO
-            if (m_inputMixMode != static_cast<int>(AudioInterface::MONO)) {
-                m_inputMixMode = static_cast<int>(AudioInterface::MONO);
-                emit inputMixModeChanged(m_inputMixMode, false);
-            }
-        }
-    }
-#endif  // RT_AUDIO
-}
-
-void VirtualStudio::validateOutputDevicesState()
-{
-    if (!m_useRtAudio) {
-        return;
-    }
-#ifdef RT_AUDIO
-    if (m_outputDeviceList.size() == 0 || m_outputDeviceList.size() == 0) {
-        return;
-    }
-
-    // Given output device list, check that the currently set device
-    // actually exists
-    if (m_outputDevice == QStringLiteral("")
-        || m_outputDeviceList.indexOf(m_outputDevice) == -1) {
-        m_outputDevice = m_outputDeviceList[0];
-    }
-    emit outputDeviceChanged(m_outputDevice, false);
-
-    // Given the currently selected output device, reset the available output channel
-    // options
-    int indexOfOutput = m_outputDeviceList.indexOf(m_outputDevice);
-    if (indexOfOutput == -1) {
-        std::cerr << "Invalid state. Output device index should never be -1" << std::endl;
-        return;
-    }
-
-    int numDevicesChannelsAvailable = m_outputDeviceChannels.at(indexOfOutput);
-    if (numDevicesChannelsAvailable < 1) {
-        std::cerr << "Invalid state. Number of channels should never be less than 1"
-                  << std::endl;
-        return;
-    } else if (numDevicesChannelsAvailable == 1) {
-        // Set the output channels combo to only have channel 1 as an option
-        QJsonObject outputChannelsComboElement = QJsonObject();
-        outputChannelsComboElement.insert(QString::fromStdString("label"),
-                                          QString::fromStdString("1"));
-        outputChannelsComboElement.insert(QString::fromStdString("baseChannel"),
-                                          QVariant(0).toInt());
-        outputChannelsComboElement.insert(QString::fromStdString("numChannels"),
-                                          QVariant(1).toInt());
-        m_outputChannelsComboModel = QJsonArray();
-        m_outputChannelsComboModel.push_back(outputChannelsComboElement);
-        emit outputChannelsComboModelChanged();
-
-        // Set the only allowed options for these variables automatically
-        m_baseOutputChannel = 0;
-        m_numOutputChannels = 1;
-
-        emit baseOutputChannelChanged(m_baseOutputChannel, false);
-        emit numOutputChannelsChanged(m_numOutputChannels, false);
-    } else {
-        // set the output channels selector to have the options based on the currently
-        // selected device
-        m_outputChannelsComboModel = QJsonArray();
-        for (int i = 0; i < numDevicesChannelsAvailable; i++) {
-            if (i % 2 == 0) {
-                QJsonObject element = QJsonObject();
-                element.insert(
-                    QString::fromStdString("label"),
-                    QVariant(i + 1).toString() + " & " + QVariant(i + 2).toString());
-                element.insert(QString::fromStdString("baseChannel"),
-                               QVariant(i).toInt());
-                element.insert(QString::fromStdString("numChannels"),
-                               QVariant(2).toInt());
-                m_outputChannelsComboModel.push_back(element);
-            }
-        }
-        emit outputChannelsComboModelChanged();
-
-        // if the current m_baseOutputChannel or m_numOutputChannels is invalid based on
-        // this device's option, use the first two channels by default
-        if (m_baseOutputChannel + m_numOutputChannels > numDevicesChannelsAvailable) {
-            // we're in the case where numDevicesChannelsAvailable >= 2, so always have
-            // the ability to use the first 2 channels
-            m_baseOutputChannel = 0;
-            m_numOutputChannels = 2;
-            emit baseOutputChannelChanged(m_baseOutputChannel, false);
-            emit numOutputChannelsChanged(m_numOutputChannels, false);
-        }
-    }
-#endif  // RT_AUDIO
-}
-
-void VirtualStudio::playOutputAudio()
-{
-    emit triggerPlayOutputAudio();
-}
-
-void VirtualStudio::revertSettings()
-{
-    m_uiScale = m_previousUiScale;
-    emit uiScaleChanged();
-
-    setAudioActivated(false);
-#ifdef RT_AUDIO
-    // Restore our previous settings
-    m_inputDevice  = m_previousInput;
-    m_outputDevice = m_previousOutput;
-    m_bufferSize   = m_previousBuffer;
-    m_useRtAudio   = m_previousUseRtAudio;
-
-    emit inputDeviceChanged(m_inputDevice, false);
-    emit outputDeviceChanged(m_outputDevice, false);
-    emit baseInputChannelChanged(m_baseInputChannel, false);
-    emit numInputChannelsChanged(m_numInputChannels, false);
-    emit inputMixModeChanged(m_inputMixMode, false);
-    emit bufferSizeChanged();
-    if (m_useRtAudio != m_previousUseRtAudio) {
-        emit audioBackendChanged(m_useRtAudio, false);
-    }
-#endif
+    getServerList(signalRefresh, index);
 }
 
-void VirtualStudio::applySettings()
+void VirtualStudio::loadSettings()
 {
-    m_previousUiScale = m_uiScale;
-    emit newScale();
+    QSettings settings;
+    m_uiMode = static_cast<QJackTrip::uiModeT>(
+        settings.value(QStringLiteral("UiMode"), QJackTrip::UNSET).toInt());
+    setUpdateChannel(
+        settings.value(QStringLiteral("UpdateChannel"), "stable").toString().toLower());
+
+    settings.beginGroup(QStringLiteral("VirtualStudio"));
+    m_refreshToken   = settings.value(QStringLiteral("RefreshToken"), "").toString();
+    m_userId         = settings.value(QStringLiteral("UserId"), "").toString();
+    m_testMode       = settings.value(QStringLiteral("TestMode"), false).toBool();
+    m_showInactive   = settings.value(QStringLiteral("ShowInactive"), true).toBool();
+    m_showSelfHosted = settings.value(QStringLiteral("ShowSelfHosted"), false).toBool();
+
+    // use setters to emit signals for these if they change; otherwise, the
+    // user interface will not revert back after cancelling settings changes
+    setUiScale(settings.value(QStringLiteral("UiScale"), 1).toFloat());
+    setDarkMode(settings.value(QStringLiteral("DarkMode"), false).toBool());
+    setShowDeviceSetup(settings.value(QStringLiteral("ShowDeviceSetup"), true).toBool());
+    setShowWarnings(settings.value(QStringLiteral("ShowWarnings"), true).toBool());
+    settings.endGroup();
 
-    setAudioActivated(false);
+    m_audioConfigPtr->loadSettings();
+}
 
+void VirtualStudio::saveSettings()
+{
     QSettings settings;
     settings.beginGroup(QStringLiteral("VirtualStudio"));
     settings.setValue(QStringLiteral("UiScale"), m_uiScale);
+    settings.setValue(QStringLiteral("DarkMode"), m_darkMode);
     settings.setValue(QStringLiteral("ShowDeviceSetup"), m_showDeviceSetup);
+    settings.setValue(QStringLiteral("ShowWarnings"), m_showWarnings);
     settings.endGroup();
-#ifdef RT_AUDIO
-    settings.beginGroup(QStringLiteral("Audio"));
-    settings.setValue(QStringLiteral("Backend"), m_useRtAudio ? 1 : 0);
-    settings.setValue(QStringLiteral("BufferSize"), m_bufferSize);
-    settings.setValue(QStringLiteral("InputDevice"), m_inputDevice);
-    settings.setValue(QStringLiteral("OutputDevice"), m_outputDevice);
-    settings.setValue(QStringLiteral("BaseInputChannel"), m_baseInputChannel);
-    settings.setValue(QStringLiteral("NumInputChannels"), m_numInputChannels);
-    settings.setValue(QStringLiteral("InputMixMode"), m_inputMixMode);
-    settings.setValue(QStringLiteral("BaseOutputChannel"), m_baseOutputChannel);
-    settings.setValue(QStringLiteral("NumOutputChannels"), m_numOutputChannels);
-    settings.setValue(QStringLiteral("FeedbackDetectionEnabled"),
-                      m_feedbackDetectionEnabled);
-    settings.endGroup();
-
-    m_previousUseRtAudio = m_useRtAudio;
-    m_previousBuffer     = m_bufferSize;
-    m_previousInput      = m_inputDevice;
-    m_previousOutput     = m_outputDevice;
-
-    emit previousInputChanged();
-    emit previousOutputChanged();
-    emit inputDeviceChanged(m_inputDevice, false);
-    emit outputDeviceChanged(m_outputDevice, false);
-    emit baseInputChannelChanged(m_baseInputChannel, false);
-    emit numInputChannelsChanged(m_numInputChannels, false);
-    emit inputMixModeChanged(m_inputMixMode, false);
-#endif
 
-    // attempt to join studio if requested
-    // this function is called after the device setup view
-    // which can display upon opening the app from join link
-    if (!m_studioToJoin.isEmpty()) {
-        // We're done waiting to be on the browse page
-        joinStudio();
-    }
+    m_audioConfigPtr->saveSettings();
 }
 
-void VirtualStudio::connectToStudio(int studioIndex)
+void VirtualStudio::connectToStudio(VsServerInfo& studio)
 {
-    {
-        QMutexLocker locker(&m_refreshMutex);
-        m_allowRefresh = false;
-    }
     m_refreshTimer.stop();
 
     m_networkStats = QJsonObject();
     emit networkStatsChanged();
 
-    m_currentStudio          = studioIndex;
-    VsServerInfo* studioInfo = static_cast<VsServerInfo*>(m_servers.at(m_currentStudio));
+    m_currentStudio = studio;
     emit currentStudioChanged();
     m_onConnectedScreen = true;
 
-    m_studioSocket = new VsWebSocket(
+    m_studioSocketPtr.reset(new VsWebSocket(
         QUrl(QStringLiteral("wss://%1/api/servers/%2?auth_code=%3")
-                 .arg(m_api->getApiHost(), studioInfo->id(), m_auth->accessToken())),
-        m_auth->accessToken(), QString(), QString());
-    connect(m_studioSocket, &VsWebSocket::textMessageReceived, this,
-            [&](QString message) {
-                handleWebsocketMessage(message);
-            });
-    m_studioSocket->openSocket();
+                 .arg(m_api->getApiHost(), m_currentStudio.id(), m_auth->accessToken())),
+        m_auth->accessToken(), QString(), QString()));
+    connect(m_studioSocketPtr.get(), &VsWebSocket::textMessageReceived, this,
+            &VirtualStudio::handleWebsocketMessage);
+    connect(m_studioSocketPtr.get(), &VsWebSocket::disconnected, this,
+            &VirtualStudio::restartStudioSocket);
+    m_studioSocketPtr->openSocket();
 
     // Check if we have an address for our server
-    if (studioInfo->status() != "Ready" && studioInfo->isAdmin() == true) {
+    if (m_currentStudio.status() != "Ready" && m_currentStudio.isAdmin() == true) {
         m_connectionState = QStringLiteral("Waiting...");
         emit connectionStateChanged();
     } else {
         completeConnection();
     }
 
-    if (m_device != nullptr) {
-        m_device->setReconnect(false);
-    }
+    m_reconnectState = ReconnectState::NOT_RECONNECTING;
 }
 
 void VirtualStudio::completeConnection()
 {
-    if (m_currentStudio < 0) {
-        return;
-    }
-
-    VsServerInfo* studioInfo = static_cast<VsServerInfo*>(m_servers.at(m_currentStudio));
-    if (studioInfo->status() == QStringLiteral("Disabled")) {
+    if (m_currentStudio.id() == ""
+        || m_currentStudio.status() == QStringLiteral("Disabled")) {
         return;
     }
 
@@ -1632,8 +786,10 @@ void VirtualStudio::completeConnection()
     m_connectionState = QStringLiteral("Preparing audio...");
     emit connectionStateChanged();
     try {
+        bool useRtAudio       = m_audioConfigPtr->getUseRtAudio();
         std::string input     = "";
         std::string output    = "";
+        int buffer_strategy   = m_audioConfigPtr->getBufferStrategy();
         int buffer_size       = 0;
         int inputMixMode      = -1;
         int baseInputChannel  = 0;
@@ -1641,28 +797,35 @@ void VirtualStudio::completeConnection()
         int baseOutputChannel = 0;
         int numOutputChannels = 2;
 #ifdef RT_AUDIO
-        if (m_useRtAudio) {
-            input             = m_inputDevice.toStdString();
-            output            = m_outputDevice.toStdString();
-            buffer_size       = m_bufferSize;
-            inputMixMode      = m_inputMixMode;
-            baseInputChannel  = m_baseInputChannel;
-            numInputChannels  = m_numInputChannels;
-            baseOutputChannel = m_baseOutputChannel;
-            numOutputChannels = m_numOutputChannels;
+        if (useRtAudio) {
+            // pre-populate device cache and validate first, if using rtaudio
+            if (!m_audioConfigPtr->getDeviceModelsInitialized())
+                m_audioConfigPtr->refreshDevices(true);
+            // initialize jacktrip using audio settings
+            input             = m_audioConfigPtr->getInputDevice().toStdString();
+            output            = m_audioConfigPtr->getOutputDevice().toStdString();
+            buffer_size       = m_audioConfigPtr->getBufferSize();
+            inputMixMode      = m_audioConfigPtr->getInputMixMode();
+            baseInputChannel  = m_audioConfigPtr->getBaseInputChannel();
+            numInputChannels  = m_audioConfigPtr->getNumInputChannels();
+            baseOutputChannel = m_audioConfigPtr->getBaseOutputChannel();
+            numOutputChannels = m_audioConfigPtr->getNumOutputChannels();
         }
 #endif
-        JackTrip* jackTrip = m_device->initJackTrip(
-            m_useRtAudio, input, output, baseInputChannel, numInputChannels,
+        JackTrip* jackTrip = m_devicePtr->initJackTrip(
+            useRtAudio, input, output, baseInputChannel, numInputChannels,
             baseOutputChannel, numOutputChannels, inputMixMode, buffer_size,
-            m_bufferStrategy, studioInfo);
+            buffer_strategy, &m_currentStudio);
         if (jackTrip == 0) {
             processError("Could not bind port");
             return;
         }
 
+        // this passes ownership to JackTrip
+        jackTrip->setAudioInterface(m_audioConfigPtr->newAudioInterface(jackTrip));
+
         QObject::connect(jackTrip, &JackTrip::signalProcessesStopped, this,
-                         &VirtualStudio::processFinished, Qt::QueuedConnection);
+                         &VirtualStudio::connectionFinished, Qt::QueuedConnection);
         QObject::connect(jackTrip, &JackTrip::signalError, this,
                          &VirtualStudio::processError, Qt::QueuedConnection);
         QObject::connect(jackTrip, &JackTrip::signalReceivedConnectionFromPeer, this,
@@ -1671,84 +834,19 @@ void VirtualStudio::completeConnection()
         QObject::connect(jackTrip, &JackTrip::signalUdpWaitingTooLong, this,
                          &VirtualStudio::udpWaitingTooLong, Qt::QueuedConnection);
 
-        setAudioActivated(false);
-
-        // Setup output volume
-        m_outputVolumePlugin = new Volume(jackTrip->getNumOutputChannels());
-        jackTrip->appendProcessPluginFromNetwork(m_outputVolumePlugin);
-        connect(this, &VirtualStudio::updatedOutputVolume, m_outputVolumePlugin,
-                &Volume::volumeUpdated);
-        connect(this, &VirtualStudio::updatedOutputMuted, m_outputVolumePlugin,
-                &Volume::muteUpdated);
-
-        // Setup input volume
-        m_inputVolumePlugin = new Volume(jackTrip->getNumInputChannels());
-        jackTrip->appendProcessPluginToNetwork(m_inputVolumePlugin);
-        connect(this, &VirtualStudio::updatedInputVolume, m_inputVolumePlugin,
-                &Volume::volumeUpdated);
-        connect(this, &VirtualStudio::updatedInputMuted, m_inputVolumePlugin,
-                &Volume::muteUpdated);
-
-        // Setup input meter
-        m_inputMeter = new Meter(jackTrip->getNumInputChannels());
-        jackTrip->appendProcessPluginToNetwork(m_inputMeter);
-        connect(m_inputMeter, &Meter::onComputedVolumeMeasurements, this,
-                &VirtualStudio::updatedInputVuMeasurements);
-
-        // Setup monitor
-        // Note: Constructor determines how many internal monitor buffers to allocate
-        m_monitor = new Monitor(
-            std::max(jackTrip->getNumInputChannels(), jackTrip->getNumOutputChannels()));
-        jackTrip->appendProcessPluginToMonitor(m_monitor);
-        connect(this, &VirtualStudio::updatedMonitorVolume, m_monitor,
-                &Monitor::volumeUpdated);
-
-        // Setup output analyzer
-        if (m_feedbackDetectionEnabled) {
-            m_outputAnalyzerPlugin = new Analyzer(jackTrip->getNumOutputChannels());
-            m_outputAnalyzerPlugin->setIsMonitoringAnalyzer(true);
-            jackTrip->appendProcessPluginToMonitor(m_outputAnalyzerPlugin);
-            connect(m_outputAnalyzerPlugin, &Analyzer::signalFeedbackDetected, this,
-                    &VirtualStudio::detectedFeedbackLoop);
-        }
-
-        // Setup output meter
-        // Note: Add this to monitor process to include self-volume
-        m_outputMeter = new Meter(jackTrip->getNumOutputChannels());
-        m_outputMeter->setIsMonitoringMeter(true);
-        jackTrip->appendProcessPluginToMonitor(m_outputMeter);
-        connect(m_outputMeter, &Meter::onComputedVolumeMeasurements, this,
-                &VirtualStudio::updatedOutputVuMeasurements);
-
-        // Grab previous levels
-        QSettings settings;
-        settings.beginGroup(QStringLiteral("Audio"));
-        m_inMultiplier  = settings.value(QStringLiteral("InMultiplier"), 1).toFloat();
-        m_outMultiplier = settings.value(QStringLiteral("OutMultiplier"), 1).toFloat();
-        m_monMultiplier = settings.value(QStringLiteral("MonMultiplier"), 0).toFloat();
-        m_inMuted       = settings.value(QStringLiteral("InMuted"), false).toBool();
-        m_outMuted      = settings.value(QStringLiteral("OutMuted"), false).toBool();
-        m_monMuted      = settings.value(QStringLiteral("MonMuted"), false).toBool();
-
-        emit updatedInputVolume(m_inMultiplier);
-        emit updatedOutputVolume(m_outMultiplier);
-        emit updatedMonitorVolume(m_monMultiplier);
-        emit updatedInputMuted(m_inMuted);
-        emit updatedOutputMuted(m_outMuted);
-
         m_connectionState = QStringLiteral("Connecting...");
         emit connectionStateChanged();
-#ifdef RT_AUDIO
-        if (m_useRtAudio) {
-            // This is a hack. RtAudio::openStream blocks the UI thread.
-            // But I am not comfortable changing how all of JackTrip consumes
-            // RtAudio to fix a VS mode bug.
-            delay(805);
-        }
-#endif
-        m_device->startJackTrip();
-        resetMeters();
-        m_device->startPinger(studioInfo);
+        m_devicePtr->startJackTrip(m_currentStudio);
+
+        // update device error messages and warnings based on latest results
+        // this is necessary because we may have never loaded audio settings,
+        // or the state may have changed via the connected change devices screen
+        m_audioConfigPtr->setDevicesWarningMsg(jackTrip->getDevicesWarningMsg());
+        m_audioConfigPtr->setDevicesErrorMsg(jackTrip->getDevicesErrorMsg());
+        m_audioConfigPtr->setDevicesWarningHelpUrl(jackTrip->getDevicesWarningHelpUrl());
+        m_audioConfigPtr->setDevicesErrorHelpUrl(jackTrip->getDevicesErrorHelpUrl());
+        m_audioConfigPtr->setHighLatencyFlag(jackTrip->getHighLatencyFlag());
+
     } catch (const std::exception& e) {
         // Let the user know what our exception was.
         m_connectionState = QStringLiteral("JackTrip Error");
@@ -1763,17 +861,27 @@ void VirtualStudio::completeConnection()
 #endif
 }
 
-void VirtualStudio::triggerReconnect()
+void VirtualStudio::triggerReconnect(bool refresh)
 {
-    if (m_jackTripRunning) {
-        m_connectionState = QStringLiteral("Reconnecting...");
-        emit connectionStateChanged();
-        m_retryPeriodTimer.stop();
-        m_retryPeriod = false;
-        if (m_device != nullptr) {
-            m_device->setReconnect(true);
-        }
+    if (!m_jackTripRunning || m_devicePtr.isNull()) {
+        if (refresh)
+            m_audioConfigPtr->refreshDevices(true);
+        else
+            m_audioConfigPtr->validateDevices();
+        return;
     }
+
+    // this needs to be synchronous to avoid both trying
+    // to use the audio interfaces at the same time
+    // note that connectionFinished() checks m_reconnectState
+    // and uses that to update audio, then reconnect
+    m_reconnectState  = refresh ? ReconnectState::RECONNECTING_REFRESH
+                                : ReconnectState::RECONNECTING_VALIDATE;
+    m_connectionState = QStringLiteral("Reconnecting...");
+    emit connectionStateChanged();
+
+    // keep device enabled while stopping jacktrip
+    m_devicePtr->stopJackTrip(true);
 }
 
 void VirtualStudio::disconnect()
@@ -1781,12 +889,11 @@ void VirtualStudio::disconnect()
     m_connectionState = QStringLiteral("Disconnecting...");
     emit connectionStateChanged();
     setConnectedErrorMsg("");
-    m_retryPeriodTimer.stop();
-    m_retryPeriod = false;
 
     if (m_jackTripRunning) {
-        m_device->stopPinger();
-        m_device->stopJackTrip();
+        m_devicePtr->stopJackTrip(false);
+        // persist any volume level or device changes
+        m_audioConfigPtr->saveSettings();
     } else {
         // How did we get here? This shouldn't be possible, but include for safety.
         if (m_isExiting) {
@@ -1799,34 +906,37 @@ void VirtualStudio::disconnect()
 
     // Restart our studio refresh timer.
     if (!m_isExiting) {
-        QMutexLocker locker(&m_refreshMutex);
-        m_allowRefresh = true;
         m_refreshTimer.start();
     }
 
     m_connectionState = QStringLiteral("Disconnected");
     emit connectionStateChanged();
 
+    if (!m_currentStudio.id().isEmpty()) {
+        emit openFeedbackSurveyModal(m_currentStudio.id());
+    }
+
     // cleanup
-    m_monitor            = nullptr;
-    m_inputMeter         = nullptr;
-    m_outputMeter        = nullptr;
-    m_inputVolumePlugin  = nullptr;
-    m_outputVolumePlugin = nullptr;
+    m_currentStudio.setId("");
+    emit currentStudioChanged();
+
+    if (!m_studioSocketPtr.isNull()) {
+        m_studioSocketPtr->closeSocket();
+        m_studioSocketPtr->disconnect();
+        m_studioSocketPtr.reset();
+    }
 }
 
-void VirtualStudio::manageStudio(int studioIndex, bool start)
+void VirtualStudio::manageStudio(const QString& studioId, bool start)
 {
-    if (studioIndex == -1) {
-        // We're here from a connected screen. Use our current studio.
-        studioIndex = m_currentStudio;
+    if (studioId.isEmpty()) {
+        processError("Manage requires a unique studio identifier");
+        return;
     }
-
     QUrl url;
     if (!start) {
-        url = QUrl(QStringLiteral("https://%1/studios/%2")
-                       .arg(m_api->getApiHost(),
-                            static_cast<VsServerInfo*>(m_servers.at(studioIndex))->id()));
+        url = QUrl(
+            QStringLiteral("https://%1/studios/%2").arg(m_api->getApiHost(), studioId));
     } else {
         QString expiration =
             QDateTime::currentDateTimeUtc().addSecs(60 * 30).toString(Qt::ISODate);
@@ -1834,9 +944,7 @@ void VirtualStudio::manageStudio(int studioIndex, bool start)
                             {QLatin1String("expiresAt"), expiration}};
         QJsonDocument request = QJsonDocument(json);
 
-        QNetworkReply* reply = m_api->updateServer(
-            (static_cast<VsServerInfo*>(m_servers.at(studioIndex)))->id(),
-            request.toJson());
+        QNetworkReply* reply = m_api->updateServer(studioId, request.toJson());
         connect(reply, &QNetworkReply::finished, this, [&, reply]() {
             if (reply->error() != QNetworkReply::NoError) {
                 m_connectionState      = QStringLiteral("Unable to Start Studio");
@@ -1865,16 +973,14 @@ void VirtualStudio::manageStudio(int studioIndex, bool start)
     QDesktopServices::openUrl(url);
 }
 
-void VirtualStudio::launchVideo(int studioIndex)
+void VirtualStudio::launchVideo(const QString& studioId)
 {
-    if (studioIndex == -1) {
-        // We're here from a connected screen. Use our current studio.
-        studioIndex = m_currentStudio;
+    if (studioId.isEmpty()) {
+        processError("Manage requires a unique studio identifier");
+        return;
     }
-    QUrl url =
-        QUrl(QStringLiteral("https://%1/studios/%2/live")
-                 .arg(m_api->getApiHost(),
-                      static_cast<VsServerInfo*>(m_servers.at(studioIndex))->id()));
+    QUrl url = QUrl(
+        QStringLiteral("https://%1/studios/%2/live").arg(m_api->getApiHost(), studioId));
     QDesktopServices::openUrl(url);
 }
 
@@ -1902,24 +1008,88 @@ void VirtualStudio::openLink(const QString& link)
     QDesktopServices::openUrl(url);
 }
 
+void VirtualStudio::handleDeeplinkRequest(const QUrl& link)
+{
+    // check link is valid
+    if (link.scheme() != QLatin1String("jacktrip")
+        || link.host() != QLatin1String("join")) {
+        qDebug() << "Ignoring invalid deeplink to " << link;
+        return;
+    }
+
+    // check if already connected (ignore)
+    if (m_windowState == "connected" || m_windowState == "change_devices") {
+        qDebug() << "Already connected; ignoring deeplink to " << link;
+        return;
+    }
+
+    qDebug() << "Handling deeplink to " << link;
+    setStudioToJoin(link);
+    raiseToTop();
+
+    // Switch to virtual studio mode, if necessary
+    // Note that this doesn't change the startup preference
+    if (m_uiMode != QJackTrip::VIRTUAL_STUDIO) {
+        m_standardWindow->hide();
+        m_view.show();
+        if (m_windowState == "start") {
+            setWindowState(QStringLiteral("login"));
+        }
+        if (m_windowState == "login") {
+            login();
+        }
+    }
+
+    // special case if on settings screen
+    if (m_windowState == "settings") {
+        if (showDeviceSetup()) {
+            // audio is already active, so we can just flip screens
+            setWindowState("setup");
+        } else {
+            // we need to stop audio before connecting
+            setWindowState("connected");
+            m_audioConfigPtr->stopAudio(true);
+            joinStudio();
+        }
+        return;
+    }
+
+    // special case if on browsing screen
+    if (m_windowState == "browse") {
+        setWindowState("connected");
+        joinStudio();
+        return;
+    }
+
+    if (m_windowState == "failed") {
+        setWindowState("connected");
+        joinStudio();
+        return;
+    }
+
+    // otherwise, assume we are on setup screens and can let the normal flow handle it
+}
+
 void VirtualStudio::exit()
 {
+    // if multiple close events are received, emit the signalExit to force-quit the app
+    if (m_isExiting) {
+        emit signalExit();
+    }
+
+    // triggering isExitingChanged will force any WebEngine things to close properly
+    m_isExiting = true;
+    emit isExitingChanged();
+
     m_startTimer.stop();
-    m_retryPeriodTimer.stop();
     m_refreshTimer.stop();
     m_heartbeatTimer.stop();
-    m_inputClipTimer.stop();
-    m_outputClipTimer.stop();
     m_networkOutageTimer.stop();
     if (m_onConnectedScreen) {
-        m_isExiting = true;
-
-        if (m_device != nullptr) {
-            m_device->stopPinger();
-            m_device->stopJackTrip();
+        // manually disconnect on self-managed studios
+        if (!m_currentStudio.id().isEmpty() && !m_currentStudio.isManaged()) {
+            disconnect();
         }
-
-        disconnect();
     } else {
         emit signalExit();
     }
@@ -1934,80 +1104,63 @@ void VirtualStudio::slotAuthSucceeded()
     }
     m_api->setApiHost(m_apiHost);
 
+    // Get refresh token and userId
     m_refreshToken = m_auth->refreshToken();
+    m_userId       = m_auth->userId();
     emit hasRefreshTokenChanged();
+
     QSettings settings;
-    settings.setValue(QStringLiteral("UiMode"), QJackTrip::VIRTUAL_STUDIO);
     settings.beginGroup(QStringLiteral("VirtualStudio"));
     settings.setValue(QStringLiteral("RefreshToken"), m_refreshToken);
+    settings.setValue(QStringLiteral("UserId"), m_userId);
     settings.endGroup();
-    m_vsModeActive = true;
-
-    m_device = new VsDevice(m_auth.data(), m_api.data());
-    m_device->registerApp();
-
-    // always activate audio at startup for now.
-    // otherwise, IF someone has the device setup disabled/unchecked,
-    // AND IF they don't manually navigate to audio settings before connecting,
-    // the "Change Device Settings" dialog will have all empty dropdown lists
-    // TODO: rework so it can be deferred properly
-    // if (m_showDeviceSetup) {
-    if constexpr (isBackendAvailable<AudioInterfaceMode::JACK>()
-                  || isBackendAvailable<AudioInterfaceMode::RTAUDIO>()) {
-        setAudioActivated(true);
-    }
 
-    getUserId();
-    getSubscriptions();
-    getServerList(true, false);
+    // initialize new VsDevice and wire up signals/slots before registering app
+    m_devicePtr.reset(new VsDevice(m_auth, m_api, m_audioConfigPtr));
+    connect(m_devicePtr.get(), &VsDevice::updateNetworkStats, this,
+            &VirtualStudio::updatedStats);
+    connect(m_audioConfigPtr.get(), &VsAudio::updatedInputVolume, m_devicePtr.get(),
+            &VsDevice::syncDeviceSettings);
+    connect(m_audioConfigPtr.get(), &VsAudio::updatedInputMuted, m_devicePtr.get(),
+            &VsDevice::syncDeviceSettings);
+    connect(m_audioConfigPtr.get(), &VsAudio::updatedOutputVolume, m_devicePtr.get(),
+            &VsDevice::syncDeviceSettings);
+    connect(m_audioConfigPtr.get(), &VsAudio::updatedMonitorVolume, m_devicePtr.get(),
+            &VsDevice::syncDeviceSettings);
+
+    m_devicePtr->registerApp();
+
+    if (!m_webChannelServer->listen(QHostAddress::LocalHost)) {
+        // shouldn't happen
+        std::cout << "ERROR: Failed to start server!" << std::endl;
+    }
+    m_webChannelPort = m_webChannelServer->serverPort();
+    emit webChannelPortChanged(m_webChannelPort);
+    std::cout << "QWebChannel listening on port: " << m_webChannelPort << std::endl;
+
     getRegions();
     getUserMetadata();
+    getSubscriptions();
+    getServerList(false);
+}
 
-    // attempt to join studio if requested
-    if (!m_studioToJoin.isEmpty()) {
-        joinStudio();
-    }
-    connect(m_device, &VsDevice::updateNetworkStats, this, &VirtualStudio::updatedStats);
-    connect(m_device, &VsDevice::updatedCaptureVolumeFromServer, this,
-            &VirtualStudio::setInputVolume);
-    connect(m_device, &VsDevice::updatedCaptureMuteFromServer, this,
-            &VirtualStudio::setInputMuted);
-    connect(m_device, &VsDevice::updatedPlaybackVolumeFromServer, this,
-            &VirtualStudio::setOutputVolume);
-    connect(m_device, &VsDevice::updatedPlaybackMuteFromServer, this,
-            &VirtualStudio::setOutputMuted);
-    connect(m_device, &VsDevice::updatedMonitorVolume, this,
-            &VirtualStudio::setMonitorVolume);
-    connect(this, &VirtualStudio::updatedInputVolume, m_device,
-            &VsDevice::updateCaptureVolume);
-    connect(this, &VirtualStudio::updatedInputMuted, m_device,
-            &VsDevice::updateCaptureMute);
-    connect(this, &VirtualStudio::updatedOutputVolume, m_device,
-            &VsDevice::updatePlaybackVolume);
-    connect(this, &VirtualStudio::updatedOutputMuted, m_device,
-            &VsDevice::updatePlaybackMute);
-    connect(this, &VirtualStudio::updatedMonitorVolume, m_device,
-            &VsDevice::updateMonitorVolume);
-}
-
-void VirtualStudio::slotAuthFailed()
-{
-    emit authFailed();
-}
-
-void VirtualStudio::processFinished()
-{
-    if (m_device != nullptr && m_device->reconnect()) {
-        if (m_device != nullptr && m_device->hasTerminated()) {
-            if (m_useRtAudio) {
-                refreshRtAudioDevices();
-                validateInputDevicesState();
-                validateOutputDevicesState();
+void VirtualStudio::connectionFinished()
+{
+    if (!m_devicePtr.isNull()
+        && (m_reconnectState == ReconnectState::RECONNECTING_VALIDATE
+            || m_reconnectState == ReconnectState::RECONNECTING_REFRESH)) {
+        if (m_devicePtr->hasTerminated()) {
+            if (m_reconnectState == ReconnectState::RECONNECTING_REFRESH) {
+                m_audioConfigPtr->refreshDevices(true);
+            } else {
+                m_audioConfigPtr->validateDevices(true);
             }
             connectToStudio(m_currentStudio);
         }
         return;
     }
+    m_reconnectState = ReconnectState::NOT_RECONNECTING;
+
     // use disconnect function to handle reset of all internal flags and timers
     disconnect();
 
@@ -2041,38 +1194,57 @@ void VirtualStudio::processFinished()
 
 void VirtualStudio::processError(const QString& errorMessage)
 {
-    bool shouldSwitchToRtAudio = false;
-    if (!m_retryPeriod) {
-        QMessageBox msgBox;
-        if (errorMessage == QLatin1String("Peer Stopped")) {
-            // Report the other end quitting as a regular occurance rather than an error.
-            msgBox.setText("The Studio has been stopped.");
-            msgBox.setWindowTitle(QStringLiteral("Disconnected"));
-        } else if (errorMessage
-                   == QLatin1String("Maybe the JACK server is not running?")) {
-            // Report the other end quitting as a regular occurance rather than an error.
-            msgBox.setText("The JACK server is not running. Switching back to RtAudio.");
-            msgBox.setWindowTitle(QStringLiteral("No JACK server"));
-            shouldSwitchToRtAudio = true;
+    static const QString RtAudioErrorMsg = QStringLiteral("RtAudio Error");
+    static const QString JackAudioErrorMsg =
+        QStringLiteral("The Jack server was shut down");
+
+    const bool shouldSwitchToRtAudio =
+        (errorMessage == QLatin1String("Maybe the JACK server is not running?"));
+
+    QMessageBox msgBox;
+    if (shouldSwitchToRtAudio) {
+        // Report the other end quitting as a regular occurance rather than an error.
+        msgBox.setText("The JACK server is not running. Switching back to RtAudio.");
+        msgBox.setWindowTitle(QStringLiteral("No JACK server"));
+    } else if (errorMessage == QLatin1String("Peer Stopped")) {
+        // Report the other end quitting as a regular occurance rather than an error.
+        msgBox.setText("The Studio has been stopped.");
+        msgBox.setWindowTitle(QStringLiteral("Disconnected"));
+    } else if (errorMessage.startsWith(RtAudioErrorMsg)) {
+        if (errorMessage.length() > RtAudioErrorMsg.length() + 2) {
+            const QString details(errorMessage.sliced(RtAudioErrorMsg.length() + 2));
+            if (details.startsWith(
+                    QStringLiteral("RtApiCore: the stream device was disconnected"))) {
+                msgBox.setText(QStringLiteral("Your audio interface was disconnected."));
+            } else {
+                msgBox.setText(details);
+            }
         } else {
-            msgBox.setText(QStringLiteral("Error: ").append(errorMessage));
-            msgBox.setWindowTitle(QStringLiteral("Doh!"));
+            msgBox.setText(errorMessage);
         }
-        msgBox.exec();
-    }
-    if (shouldSwitchToRtAudio) {
-        setAudioBackend("RtAudio");
+        msgBox.setWindowTitle(QStringLiteral("Audio Interface Error"));
+    } else if (errorMessage.startsWith(JackAudioErrorMsg)) {
+        if (errorMessage.length() > JackAudioErrorMsg.length() + 2) {
+            msgBox.setText(errorMessage.sliced(JackAudioErrorMsg.length() + 2));
+        } else {
+            msgBox.setText(QStringLiteral("The JACK Audio Server was stopped."));
+        }
+        msgBox.setWindowTitle(QStringLiteral("Jack Audio Error"));
     } else {
-        processFinished();
+        msgBox.setText(QStringLiteral("Error: ").append(errorMessage));
+        msgBox.setWindowTitle(QStringLiteral("Doh!"));
     }
+    msgBox.exec();
+
+    if (shouldSwitchToRtAudio)
+        m_audioConfigPtr->setAudioBackend("RtAudio");
+    if (m_jackTripRunning)
+        connectionFinished();
 }
 
 void VirtualStudio::receivedConnectionFromPeer()
 {
     // Connect via API
-    VsServerInfo* studioInfo = static_cast<VsServerInfo*>(m_servers.at(m_currentStudio));
-    m_device->setServerId(studioInfo->id());
-
     m_connectionState = QStringLiteral("Connected");
     emit connectionStateChanged();
     std::cout << "Received connection" << std::endl;
@@ -2081,38 +1253,43 @@ void VirtualStudio::receivedConnectionFromPeer()
 
 void VirtualStudio::handleWebsocketMessage(const QString& msg)
 {
-    QJsonObject serverState  = QJsonDocument::fromJson(msg.toUtf8()).object();
-    QString serverStatus     = serverState[QStringLiteral("status")].toString();
-    bool serverEnabled       = serverState[QStringLiteral("enabled")].toBool();
-    QString serverCloudId    = serverState[QStringLiteral("cloudId")].toString();
-    VsServerInfo* studioInfo = static_cast<VsServerInfo*>(m_servers.at(m_currentStudio));
-    studioInfo->setStatus(serverStatus);
-    studioInfo->setEnabled(serverEnabled);
-    studioInfo->setCloudId(serverCloudId);
+    QJsonObject serverState = QJsonDocument::fromJson(msg.toUtf8()).object();
+    QString serverStatus    = serverState[QStringLiteral("status")].toString();
+    bool serverEnabled      = serverState[QStringLiteral("enabled")].toBool();
+    QString serverCloudId   = serverState[QStringLiteral("cloudId")].toString();
+
+    // server notifications are also transmitted along this websocket, so ignore data if
+    // it contains "message"
+    QString message = serverState[QStringLiteral("message")].toString();
+    if (!message.isEmpty()) {
+        return;
+    }
+    if (m_currentStudio.id() == "") {
+        return;
+    }
+    m_currentStudio.setStatus(serverStatus);
+    m_currentStudio.setEnabled(serverEnabled);
+    m_currentStudio.setCloudId(serverCloudId);
     if (!m_jackTripRunning) {
         if (serverStatus == QLatin1String("Ready") && m_onConnectedScreen) {
-            studioInfo->setHost(serverState[QStringLiteral("serverHost")].toString());
-            studioInfo->setPort(serverState[QStringLiteral("serverPort")].toInt());
-            studioInfo->setSessionId(serverState[QStringLiteral("sessionId")].toString());
-
-            // Call completeConnection after a short timeout
-            m_startTimer.setInterval(1000);
-            m_startTimer.setSingleShot(true);
-            connect(&m_startTimer, &QTimer::timeout, this, [&]() {
-                completeConnection();
-            });
-
-            m_startTimer.start();
+            m_currentStudio.setHost(serverState[QStringLiteral("serverHost")].toString());
+            m_currentStudio.setPort(serverState[QStringLiteral("serverPort")].toInt());
+            m_currentStudio.setSessionId(
+                serverState[QStringLiteral("sessionId")].toString());
+            completeConnection();
         }
     }
 
     emit currentStudioChanged();
 }
 
-void VirtualStudio::endRetryPeriod()
+void VirtualStudio::restartStudioSocket()
 {
-    m_retryPeriod = false;
-    m_retryPeriodTimer.stop();
+    if (m_onConnectedScreen) {
+        if (!m_studioSocketPtr.isNull()) {
+            m_studioSocketPtr->openSocket();
+        }
+    }
 }
 
 void VirtualStudio::launchBrowser(const QUrl& url)
@@ -2139,112 +1316,6 @@ void VirtualStudio::updatedStats(const QJsonObject& stats)
     return;
 }
 
-void VirtualStudio::updatedDevicesErrorMsg(const QString& msg)
-{
-    m_devicesErrorMsg = msg;
-    emit devicesErrorChanged();
-    return;
-}
-
-void VirtualStudio::updatedDevicesWarningMsg(const QString& msg)
-{
-    m_devicesWarningMsg = msg;
-    emit devicesWarningChanged();
-    return;
-}
-
-void VirtualStudio::updatedDevicesErrorHelpUrl(const QString& url)
-{
-    m_devicesErrorHelpUrl = url;
-    emit devicesErrorHelpUrlChanged();
-    return;
-}
-
-void VirtualStudio::updatedDevicesWarningHelpUrl(const QString& url)
-{
-    m_devicesWarningHelpUrl = url;
-    emit devicesWarningHelpUrlChanged();
-    return;
-}
-
-void VirtualStudio::updatedInputVuMeasurements(const float* valuesInDecibels,
-                                               int numChannels)
-{
-    bool detectedClip = false;
-
-    // Always output 2 meter readings to the UI
-    for (int i = 0; i < 2; i++) {
-        // Determine decibel reading
-        float dB = m_meterMin;
-        if (i < numChannels) {
-            dB = std::max(m_meterMin, valuesInDecibels[i]);
-        }
-
-        // Produce a normalized value from 0 to 1
-        m_inputMeterLevels[i] = (dB - m_meterMin) / (m_meterMax - m_meterMin);
-
-        // Signal a clip if we haven't done so already
-        if (dB >= -0.05 && !detectedClip) {
-            m_inputClipTimer.start();
-            m_inputClipped = true;
-            emit updatedInputClipped(m_inputClipped);
-            detectedClip = true;
-        }
-    }
-
-#ifdef RT_AUDIO
-    // For certain specific cases, copy the first channel's value into the second
-    // channel's value
-    if ((m_inputMixMode == static_cast<int>(AudioInterface::MONO)
-         && m_numInputChannels == 1)
-        || (m_inputMixMode == static_cast<int>(AudioInterface::MIXTOMONO)
-            && m_numInputChannels == 2)) {
-        m_inputMeterLevels[1] = m_inputMeterLevels[0];
-    }
-#endif
-
-    emit updatedInputMeterLevels(m_inputMeterLevels);
-}
-
-void VirtualStudio::updatedOutputVuMeasurements(const float* valuesInDecibels,
-                                                int numChannels)
-{
-    bool detectedClip = false;
-
-    // Always output 2 meter readings to the UI
-    for (int i = 0; i < 2; i++) {
-        // Determine decibel reading
-        float dB = m_meterMin;
-        if (i < numChannels) {
-            dB = std::max(m_meterMin, valuesInDecibels[i]);
-        }
-
-        // Produce a normalized value from 0 to 1
-        m_outputMeterLevels[i] = (dB - m_meterMin) / (m_meterMax - m_meterMin);
-
-        // Signal a clip if we haven't done so already
-        if (dB >= -0.05 && !detectedClip) {
-            m_outputClipTimer.start();
-            m_outputClipped = true;
-            emit updatedOutputClipped(m_outputClipped);
-            detectedClip = true;
-        }
-    }
-#ifdef RT_AUDIO
-    if (m_numOutputChannels == 1) {
-        m_outputMeterLevels[1] = m_outputMeterLevels[0];
-    }
-#endif
-    emit updatedOutputMeterLevels(m_outputMeterLevels);
-}
-
-void VirtualStudio::detectedFeedbackLoop()
-{
-    setInputMuted(true);
-    setMonitorVolume(0);
-    emit feedbackDetected();
-}
-
 void VirtualStudio::udpWaitingTooLong()
 {
     m_networkOutageTimer.start();
@@ -2254,48 +1325,43 @@ void VirtualStudio::udpWaitingTooLong()
 
 void VirtualStudio::sendHeartbeat()
 {
-    if (m_device != nullptr && m_connectionState != "Connecting..."
+    if (!m_devicePtr.isNull() && m_connectionState != "Connecting..."
         && m_connectionState != "Preparing audio...") {
-        m_device->sendHeartbeat();
+        m_devicePtr->sendHeartbeat();
     }
 }
 
-void VirtualStudio::getServerList(bool firstLoad, bool signalRefresh, int index)
+void VirtualStudio::getServerList(bool signalRefresh, int index)
 {
-    {
-        QMutexLocker locker(&m_refreshMutex);
-        if (!m_allowRefresh || m_refreshInProgress) {
-            if (signalRefresh) {
-                emit refreshFinished(index);
-            }
-            return;
-        } else {
-            m_refreshInProgress = true;
-        }
-    }
+    QMutexLocker refreshLock(&m_refreshMutex);
+    if (m_refreshInProgress)
+        return;
+    m_refreshInProgress = true;
 
     // Get the serverId of the server at the top of our screen if we know it
     QString topServerId;
     if (index >= 0 && index < m_servers.count()) {
-        topServerId = static_cast<VsServerInfo*>(m_servers.at(index))->id();
+        topServerId = m_servers.at(index)->id();
     }
+    refreshLock.unlock();
 
     QNetworkReply* reply = m_api->getServers();
     connect(
-        reply, &QNetworkReply::finished, this,
-        [&, reply, topServerId, firstLoad, signalRefresh]() {
+        reply, &QNetworkReply::finished, this, [&, reply, topServerId, signalRefresh]() {
             if (reply->error() != QNetworkReply::NoError) {
                 if (signalRefresh) {
                     emit refreshFinished(index);
                 }
                 std::cout << "Error: " << reply->errorString().toStdString() << std::endl;
-                emit authFailed();
                 reply->deleteLater();
+                QMutexLocker getServersLock(&m_refreshMutex);
+                m_refreshInProgress = false;
                 return;
             }
 
             QByteArray response      = reply->readAll();
             QJsonDocument serverList = QJsonDocument::fromJson(response);
+            reply->deleteLater();
             if (!serverList.isArray()) {
                 if (signalRefresh) {
                     emit refreshFinished(index);
@@ -2303,21 +1369,22 @@ void VirtualStudio::getServerList(bool firstLoad, bool signalRefresh, int index)
                 std::cout << "Error: Not an array" << std::endl;
                 QMutexLocker locker(&m_refreshMutex);
                 m_refreshInProgress = false;
-                emit authFailed();
-                reply->deleteLater();
+                QMutexLocker getServersLock(&m_refreshMutex);
+                m_refreshInProgress = false;
                 return;
             }
+
             QJsonArray servers = serverList.array();
             // Divide our servers by category initially so that they're easier to sort
-            QList<QObject*> yourServers;
-            QList<QObject*> subServers;
-            QList<QObject*> pubServers;
+            QVector<VsServerInfoPointer> yourServers;
+            QVector<VsServerInfoPointer> subServers;
+            QVector<VsServerInfoPointer> pubServers;
             int skippedStudios = 0;
 
             for (int i = 0; i < servers.count(); i++) {
                 if (servers.at(i)[QStringLiteral("type")].toString().contains(
                         QStringLiteral("JackTrip"))) {
-                    VsServerInfo* serverInfo = new VsServerInfo(this);
+                    QSharedPointer<VsServerInfo> serverInfo(new VsServerInfo(this));
                     serverInfo->setIsAdmin(
                         servers.at(i)[QStringLiteral("admin")].toBool());
                     QString status = servers.at(i)[QStringLiteral("status")].toString();
@@ -2378,26 +1445,20 @@ void VirtualStudio::getServerList(bool firstLoad, bool signalRefresh, int index)
                             serverInfo->setSection(VsServerInfo::SUBSCRIBED_STUDIOS);
                         } else {
                             pubServers.append(serverInfo);
+                            serverInfo->setSection(VsServerInfo::PUBLIC_STUDIOS);
                         }
                     }
                 }
             }
 
-            std::sort(yourServers.begin(), yourServers.end(),
-                      [](QObject* first, QObject* second) {
-                          return *static_cast<VsServerInfo*>(first)
-                                 < *static_cast<VsServerInfo*>(second);
-                      });
-            std::sort(subServers.begin(), subServers.end(),
-                      [](QObject* first, QObject* second) {
-                          return *static_cast<VsServerInfo*>(first)
-                                 < *static_cast<VsServerInfo*>(second);
-                      });
-            std::sort(pubServers.begin(), pubServers.end(),
-                      [](QObject* first, QObject* second) {
-                          return *static_cast<VsServerInfo*>(first)
-                                 < *static_cast<VsServerInfo*>(second);
-                      });
+            // sort studios in each section by name
+            auto serverSorter = [](VsServerInfoPointer first,
+                                   VsServerInfoPointer second) {
+                return *first < *second;
+            };
+            std::sort(yourServers.begin(), yourServers.end(), serverSorter);
+            std::sort(subServers.begin(), subServers.end(), serverSorter);
+            std::sort(pubServers.begin(), pubServers.end(), serverSorter);
 
             // If we don't have any owned servers, move the JackTrip logo to an
             // appropriate section header.
@@ -2422,71 +1483,45 @@ void VirtualStudio::getServerList(bool firstLoad, bool signalRefresh, int index)
                 emit logoSectionChanged();
             }
 
-            QMutexLocker locker(&m_refreshMutex);
-            // Check that we haven't tried connecting to a server between the
-            // request going out and the response.
-            if (!m_allowRefresh) {
-                m_refreshInProgress = false;
-                if (signalRefresh) {
-                    emit refreshFinished(index);
-                }
-                return;
-            }
+            QMutexLocker getServersLock(&m_refreshMutex);
             m_servers.clear();
             m_servers.append(yourServers);
             m_servers.append(subServers);
             m_servers.append(pubServers);
-            m_view.engine()->rootContext()->setContextProperty(
-                QStringLiteral("serverModel"), QVariant::fromValue(m_servers));
+            m_serverModel.clear();
+            for (const VsServerInfoPointer& s : m_servers) {
+                m_serverModel.append(s.get());
+            }
+            emit serverModelChanged();
             int index = -1;
             if (!topServerId.isEmpty()) {
                 for (int i = 0; i < m_servers.count(); i++) {
-                    if (static_cast<VsServerInfo*>(m_servers.at(i))->id()
-                        == topServerId) {
+                    if (m_servers.at(i)->id() == topServerId) {
                         index = i;
                         break;
                     }
                 }
             }
-            if (firstLoad) {
-                emit authSucceeded();
-                m_refreshTimer.setInterval(10000);
+            if (m_firstRefresh) {
+                m_refreshTimer.setInterval(5000);
                 m_refreshTimer.start();
                 m_heartbeatTimer.setInterval(5000);
                 m_heartbeatTimer.start();
+                m_firstRefresh = false;
             }
-
+            m_refreshInProgress = false;
             if (signalRefresh) {
                 emit refreshFinished(index);
             }
-
-            m_refreshInProgress = false;
-
-            reply->deleteLater();
         });
 }
 
-void VirtualStudio::getUserId()
-{
-    m_userId = m_auth->userId();
-    if (m_userId.isEmpty()) {
-        emit authFailed();
-        return;
-    }
-
-    QSettings settings;
-    settings.beginGroup(QStringLiteral("VirtualStudio"));
-    settings.setValue(QStringLiteral("UserId"), m_userId);
-    settings.endGroup();
-}
-
 void VirtualStudio::getSubscriptions()
 {
     QNetworkReply* reply = m_api->getSubscriptions(m_userId);
     connect(reply, &QNetworkReply::finished, this, [&, reply]() {
         if (reply->error() != QNetworkReply::NoError) {
             std::cout << "Error: " << reply->errorString().toStdString() << std::endl;
-            emit authFailed();
             reply->deleteLater();
             return;
         }
@@ -2495,14 +1530,14 @@ void VirtualStudio::getSubscriptions()
         QJsonDocument subscriptionList = QJsonDocument::fromJson(response);
         if (!subscriptionList.isArray()) {
             std::cout << "Error: Not an array" << std::endl;
-            emit authFailed();
             reply->deleteLater();
             return;
         }
+        m_subscribedServers.clear();
         QJsonArray subscriptions = subscriptionList.array();
         for (int i = 0; i < subscriptions.count(); i++) {
-            m_subscribedServers.append(
-                subscriptions.at(i)[QStringLiteral("serverId")].toString());
+            m_subscribedServers.insert(
+                subscriptions.at(i)[QStringLiteral("serverId")].toString(), true);
         }
         reply->deleteLater();
     });
@@ -2514,7 +1549,6 @@ void VirtualStudio::getRegions()
     connect(reply, &QNetworkReply::finished, this, [&, reply]() {
         if (reply->error() != QNetworkReply::NoError) {
             std::cout << "Error: " << reply->errorString().toStdString() << std::endl;
-            emit authFailed();
             reply->deleteLater();
             return;
         }
@@ -2531,7 +1565,6 @@ void VirtualStudio::getUserMetadata()
     connect(reply, &QNetworkReply::finished, this, [&, reply]() {
         if (reply->error() != QNetworkReply::NoError) {
             std::cout << "Error: " << reply->errorString().toStdString() << std::endl;
-            emit authFailed();
             reply->deleteLater();
             return;
         }
@@ -2542,158 +1575,16 @@ void VirtualStudio::getUserMetadata()
     });
 }
 
-void VirtualStudio::startAudio()
-{
-    std::cout << "Starting Audio" << std::endl;
-#ifdef __APPLE__
-    if (m_permissions->micPermission() != "granted") {
-        return;
-    }
-#endif
-    if (m_vsAudioInterface.isNull()) {
-        m_vsAudioInterface.reset(new VsAudioInterface());
-        m_view.engine()->rootContext()->setContextProperty(
-            QStringLiteral("audioInterface"), m_vsAudioInterface.data());
-    }
-#ifdef RT_AUDIO
-    refreshRtAudioDevices();
-    validateDevicesState();
-    m_vsAudioInterface->setInputDevice(m_inputDevice, false);
-    m_vsAudioInterface->setOutputDevice(m_outputDevice, false);
-    m_vsAudioInterface->setAudioInterfaceMode(m_useRtAudio, false);
-    m_vsAudioInterface->setBaseInputChannel(m_baseInputChannel, false);
-    m_vsAudioInterface->setNumInputChannels(m_numInputChannels, false);
-    m_vsAudioInterface->setInputMixMode(m_inputMixMode, false);
-    m_vsAudioInterface->setBaseOutputChannel(m_baseOutputChannel, false);
-    m_vsAudioInterface->setNumOutputChannels(m_numOutputChannels, false);
-#endif
-    connect(m_vsAudioInterface.data(), &VsAudioInterface::devicesErrorMsgChanged, this,
-            &VirtualStudio::updatedDevicesErrorMsg);
-    connect(m_vsAudioInterface.data(), &VsAudioInterface::devicesWarningMsgChanged, this,
-            &VirtualStudio::updatedDevicesWarningMsg);
-    connect(m_vsAudioInterface.data(), &VsAudioInterface::devicesErrorHelpUrlChanged,
-            this, &VirtualStudio::updatedDevicesErrorHelpUrl);
-    connect(m_vsAudioInterface.data(), &VsAudioInterface::devicesWarningHelpUrlChanged,
-            this, &VirtualStudio::updatedDevicesWarningHelpUrl);
-    m_vsAudioInterface->setupAudio();
-
-    connect(this, &VirtualStudio::inputDeviceChanged, m_vsAudioInterface.data(),
-            &VsAudioInterface::setInputDevice);
-    connect(this, &VirtualStudio::outputDeviceChanged, m_vsAudioInterface.data(),
-            &VsAudioInterface::setOutputDevice);
-
-#ifdef RT_AUDIO
-    connect(this, &VirtualStudio::baseInputChannelChanged, m_vsAudioInterface.data(),
-            &VsAudioInterface::setBaseInputChannel);
-    connect(this, &VirtualStudio::numInputChannelsChanged, m_vsAudioInterface.data(),
-            &VsAudioInterface::setNumInputChannels);
-    connect(this, &VirtualStudio::inputMixModeChanged, m_vsAudioInterface.data(),
-            &VsAudioInterface::setInputMixMode);
-    connect(this, &VirtualStudio::baseOutputChannelChanged, m_vsAudioInterface.data(),
-            &VsAudioInterface::setBaseOutputChannel);
-    connect(this, &VirtualStudio::numOutputChannelsChanged, m_vsAudioInterface.data(),
-            &VsAudioInterface::setNumOutputChannels);
-#endif
-    connect(this, &VirtualStudio::audioBackendChanged, m_vsAudioInterface.data(),
-            &VsAudioInterface::setAudioInterfaceMode);
-    connect(this, &VirtualStudio::triggerPlayOutputAudio, m_vsAudioInterface.data(),
-            &VsAudioInterface::triggerPlayback);
-    connect(m_vsAudioInterface.data(), &VsAudioInterface::newInputMeterMeasurements, this,
-            &VirtualStudio::updatedInputVuMeasurements);
-    connect(m_vsAudioInterface.data(), &VsAudioInterface::newOutputMeterMeasurements,
-            this, &VirtualStudio::updatedOutputVuMeasurements);
-    connect(m_vsAudioInterface.data(), &VsAudioInterface::errorToProcess, this,
-            &VirtualStudio::processError);
-
-    m_vsAudioInterface->setupPlugins();
-
-    m_audioReady = true;
-    emit audioReadyChanged();
-    resetMeters();
-
-    m_vsAudioInterface->startProcess();
-}
-
-void VirtualStudio::restartAudio()
-{
-    std::cout << "Restarting Audio" << std::endl;
-#ifdef __APPLE__
-    if (m_permissions->micPermission() != "granted") {
-        return;
-    }
-#endif
-    // Start VsAudioInterface again
-    if (!m_vsAudioInterface.isNull()) {
-#ifdef RT_AUDIO
-        refreshRtAudioDevices();
-        validateDevicesState();
-        m_vsAudioInterface->setInputDevice(m_inputDevice, false);
-        m_vsAudioInterface->setOutputDevice(m_outputDevice, false);
-#endif
-        m_vsAudioInterface->setupAudio();
-        m_vsAudioInterface->setupPlugins();
-
-        m_audioReady = true;
-        emit audioReadyChanged();
-        resetMeters();
-
-        m_vsAudioInterface->startProcess();
-    } else {
-        startAudio();
-    }
-}
-
-void VirtualStudio::resetMeters()
-{
-    m_inputMeterLevels[0] = m_inputMeterLevels[1] = 0;
-    m_outputMeterLevels[0] = m_outputMeterLevels[1] = 0;
-    m_inputClipped = m_outputClipped = false;
-    emit updatedInputMeterLevels(m_inputMeterLevels);
-    emit updatedOutputMeterLevels(m_outputMeterLevels);
-    emit updatedInputClipped(m_inputClipped);
-    emit updatedOutputClipped(m_outputClipped);
-}
-
-void VirtualStudio::stopAudio()
-{
-    // Stop VsAudioInterface
-    if (!m_vsAudioInterface.isNull()) {
-        m_vsAudioInterface->closeAudio();
-        setAudioReady(false);
-    }
-}
-
-void VirtualStudio::toggleAudio()
-{
-#ifdef __APPLE__
-    if (m_permissions->micPermission() != "granted") {
-        return;
-    }
-#endif
-
-    if constexpr (!(isBackendAvailable<AudioInterfaceMode::JACK>()
-                    || isBackendAvailable<AudioInterfaceMode::RTAUDIO>())) {
-        return;
-    }
-
-    if (!m_audioActivated) {
-        stopAudio();
-    } else {
-        restartAudio();
-    }
-}
-
 void VirtualStudio::stopStudio()
 {
-    if (m_currentStudio < 0) {
+    if (m_currentStudio.id() == "") {
         return;
     }
 
-    VsServerInfo* studioInfo = static_cast<VsServerInfo*>(m_servers.at(m_currentStudio));
-    QJsonObject json         = {{QLatin1String("enabled"), false}};
-    QJsonDocument request    = QJsonDocument(json);
-    studioInfo->setHost(QLatin1String(""));
-    QNetworkReply* reply = m_api->updateServer(studioInfo->id(), request.toJson());
+    QJsonObject json      = {{QLatin1String("enabled"), false}};
+    QJsonDocument request = QJsonDocument(json);
+    m_currentStudio.setHost(QLatin1String(""));
+    QNetworkReply* reply = m_api->updateServer(m_currentStudio.id(), request.toJson());
     connect(reply, &QNetworkReply::finished, this, [=]() {
         if (m_isExiting && !m_jackTripRunning) {
             emit signalExit();
@@ -2706,69 +1597,17 @@ bool VirtualStudio::readyToJoin()
 {
     // FTUX shows warnings and device setup views
     // if any of these enabled, do not immediately join
-    return m_windowState == "browse"
+    return m_windowState == "connected"
            && (m_connectionState == QStringLiteral("Waiting...")
                || m_connectionState == QStringLiteral("Disconnected"));
 }
 
-#ifdef RT_AUDIO
-QJsonArray VirtualStudio::formatDeviceList(const QStringList& devices,
-                                           const QStringList& categories,
-                                           const QList<int>& channels)
-{
-    QStringList uniqueCategories = QStringList(categories);
-    uniqueCategories.removeDuplicates();
-
-    bool containsCategories = true;
-    if (uniqueCategories.size() == 0) {
-        containsCategories = false;
-    } else if (uniqueCategories.size() == 1 && uniqueCategories.at(0) == "") {
-        containsCategories = false;
-    }
-
-    QJsonArray items;
-    for (int i = 0; i < uniqueCategories.size(); i++) {
-        QString category = uniqueCategories.at(i);
-
-        if (containsCategories) {
-            QJsonObject header = QJsonObject();
-            header.insert(QString::fromStdString("text"), category);
-            header.insert(QString::fromStdString("type"),
-                          QString::fromStdString("header"));
-            header.insert(QString::fromStdString("category"), category);
-            items.push_back(header);
-        }
-
-        for (int j = 0; j < devices.size(); j++) {
-            if (categories.at(j).toStdString() == category.toStdString()) {
-                QJsonObject element = QJsonObject();
-                element.insert(QString::fromStdString("text"), devices.at(j));
-                element.insert(QString::fromStdString("type"),
-                               QString::fromStdString("element"));
-                element.insert(QString::fromStdString("channels"), channels.at(j));
-                element.insert(QString::fromStdString("category"), category);
-                items.push_back(element);
-            }
-        }
-    }
-
-    return items;
+void VirtualStudio::detectedFeedbackLoop()
+{
+    emit feedbackDetected();
 }
-#endif
 
 VirtualStudio::~VirtualStudio()
 {
-    for (int i = 0; i < m_servers.count(); i++) {
-        delete m_servers.at(i);
-    }
-
-    delete m_monitor;
-    delete m_inputMeter;
-    delete m_outputMeter;
-    delete m_outputVolumePlugin;
-    delete m_inputVolumePlugin;
-    delete m_inputTestMeter;
-    delete m_studioSocket;
-
     QDesktopServices::unsetUrlHandler("jacktrip");
 }
index b7f5684addfa2ac676ff997fe7fe3034070b42e3..e5b1ab216ae64feb365005cdc691fa0bf7e6b138 100644 (file)
 #ifndef VIRTUALSTUDIO_H
 #define VIRTUALSTUDIO_H
 
-#include <QEventLoop>
-#include <QList>
+#include <QJsonObject>
+#include <QMap>
 #include <QMutex>
 #include <QNetworkAccessManager>
 #include <QScopedPointer>
 #include <QSharedPointer>
+#include <QString>
+#include <QStringList>
 #include <QTimer>
+#include <QUrl>
 #include <QVector>
-#include <QtNetworkAuth>
+#include <QWebChannel>
+#include <QWebSocketServer>
 
-#include "../Analyzer.h"
-#include "../JackTrip.h"
-#include "../Meter.h"
-#include "../Monitor.h"
-#include "../Volume.h"
-#include "vsApi.h"
-#include "vsAudioInterface.h"
-#include "vsAuth.h"
+#include "qjacktrip.h"
 #include "vsConstants.h"
-#include "vsDevice.h"
 #include "vsQuickView.h"
 #include "vsServerInfo.h"
-#include "vsUrlHandler.h"
-#include "vsWebSocket.h"
 
 #ifdef __APPLE__
 #include "NoNap.h"
-#include "vsMacPermissions.h"
-#else
-#include "vsPermissions.h"
 #endif
 
-class QJackTrip;
+class JackTrip;
+class VsAudio;
+class VsApi;
+class VsAuth;
+class VsDevice;
+class VsWebSocket;
+
+typedef QSharedPointer<VsServerInfo> VsServerInfoPointer;
 
 class VirtualStudio : public QObject
 {
     Q_OBJECT
+    Q_PROPERTY(int webChannelPort READ webChannelPort NOTIFY webChannelPortChanged)
     Q_PROPERTY(bool showFirstRun READ showFirstRun WRITE setShowFirstRun NOTIFY
                    showFirstRunChanged)
     Q_PROPERTY(bool hasRefreshToken READ hasRefreshToken NOTIFY hasRefreshTokenChanged)
     Q_PROPERTY(QString versionString READ versionString CONSTANT)
     Q_PROPERTY(QString logoSection READ logoSection NOTIFY logoSectionChanged)
-    Q_PROPERTY(bool selectableBackend READ selectableBackend CONSTANT)
-    Q_PROPERTY(QString audioBackend READ audioBackend WRITE setAudioBackend NOTIFY
-                   audioBackendChanged)
-    Q_PROPERTY(QString inputDevice READ inputDevice WRITE setInputDevice NOTIFY
-                   inputDeviceChanged)
-    Q_PROPERTY(QString outputDevice READ outputDevice WRITE setOutputDevice NOTIFY
-                   outputDeviceChanged)
-    Q_PROPERTY(int previousInput READ previousInput WRITE setPreviousInput NOTIFY
-                   previousInputChanged)
-    Q_PROPERTY(int previousOutput READ previousOutput WRITE setPreviousOutput NOTIFY
-                   previousOutputChanged)
-#ifdef RT_AUDIO
-    Q_PROPERTY(int baseInputChannel READ baseInputChannel WRITE setBaseInputChannel NOTIFY
-                   baseInputChannelChanged)
-    Q_PROPERTY(int numInputChannels READ numInputChannels WRITE setNumInputChannels NOTIFY
-                   numInputChannelsChanged)
-    Q_PROPERTY(int inputMixMode READ inputMixMode WRITE setInputMixMode NOTIFY
-                   inputMixModeChanged)
-    Q_PROPERTY(int baseOutputChannel READ baseOutputChannel WRITE setBaseOutputChannel
-                   NOTIFY baseOutputChannelChanged)
-    Q_PROPERTY(int numOutputChannels READ numOutputChannels WRITE setNumOutputChannels
-                   NOTIFY numOutputChannelsChanged)
-#endif
-    Q_PROPERTY(QString devicesWarning READ devicesWarning NOTIFY devicesWarningChanged)
-    Q_PROPERTY(QString devicesError READ devicesError NOTIFY devicesErrorChanged)
-    Q_PROPERTY(QString devicesWarningHelpUrl READ devicesWarningHelpUrl NOTIFY
-                   devicesWarningHelpUrlChanged)
-    Q_PROPERTY(QString devicesErrorHelpUrl READ devicesErrorHelpUrl NOTIFY
-                   devicesErrorHelpUrlChanged)
     Q_PROPERTY(
         QString connectedErrorMsg READ connectedErrorMsg NOTIFY connectedErrorMsgChanged)
 
     Q_PROPERTY(
-        int bufferSize READ bufferSize WRITE setBufferSize NOTIFY bufferSizeChanged)
-    Q_PROPERTY(int bufferStrategy READ bufferStrategy WRITE setBufferStrategy NOTIFY
-                   bufferStrategyChanged)
-    Q_PROPERTY(bool feedbackDetectionEnabled READ feedbackDetectionEnabled WRITE
-                   setFeedbackDetectionEnabled NOTIFY feedbackDetectionEnabledChanged)
-    Q_PROPERTY(int currentStudio READ currentStudio NOTIFY currentStudioChanged)
+        QVector<VsServerInfo*> serverModel READ getServerModel NOTIFY serverModelChanged)
+    Q_PROPERTY(VsServerInfo* currentStudio READ currentStudio NOTIFY currentStudioChanged)
     Q_PROPERTY(QUrl studioToJoin READ studioToJoin WRITE setStudioToJoin NOTIFY
                    studioToJoinChanged)
     Q_PROPERTY(QJsonObject regions READ regions NOTIFY regionsChanged)
@@ -138,47 +104,23 @@ class VirtualStudio : public QObject
     Q_PROPERTY(float fontScale READ fontScale CONSTANT)
     Q_PROPERTY(float uiScale READ uiScale WRITE setUiScale NOTIFY uiScaleChanged)
     Q_PROPERTY(bool darkMode READ darkMode WRITE setDarkMode NOTIFY darkModeChanged)
+    Q_PROPERTY(bool collapseDeviceControls READ collapseDeviceControls WRITE
+                   setCollapseDeviceControls NOTIFY collapseDeviceControlsChanged)
     Q_PROPERTY(bool testMode READ testMode WRITE setTestMode NOTIFY testModeChanged)
     Q_PROPERTY(bool showDeviceSetup READ showDeviceSetup WRITE setShowDeviceSetup NOTIFY
                    showDeviceSetupChanged)
     Q_PROPERTY(bool showWarnings READ showWarnings WRITE setShowWarnings NOTIFY
                    showWarningsChanged)
+    Q_PROPERTY(bool isExiting READ isExiting NOTIFY isExitingChanged)
     Q_PROPERTY(bool noUpdater READ noUpdater CONSTANT)
     Q_PROPERTY(bool psiBuild READ psiBuild CONSTANT)
     Q_PROPERTY(QString failedMessage READ failedMessage NOTIFY failedMessageChanged)
-    Q_PROPERTY(
-        float inputVolume READ inputVolume WRITE setInputVolume NOTIFY updatedInputVolume)
-    Q_PROPERTY(float outputVolume READ outputVolume WRITE setOutputVolume NOTIFY
-                   updatedOutputVolume)
-    Q_PROPERTY(float monitorVolume READ monitorVolume WRITE setMonitorVolume NOTIFY
-                   updatedMonitorVolume)
-    Q_PROPERTY(
-        bool inputMuted READ inputMuted WRITE setInputMuted NOTIFY updatedInputMuted)
-    Q_PROPERTY(QVector<float> outputMeterLevels READ outputMeterLevels NOTIFY
-                   updatedOutputMeterLevels)
-    Q_PROPERTY(QVector<float> inputMeterLevels READ inputMeterLevels NOTIFY
-                   updatedInputMeterLevels)
-    Q_PROPERTY(
-        QJsonArray inputComboModel READ inputComboModel NOTIFY inputComboModelChanged)
-    Q_PROPERTY(
-        QJsonArray outputComboModel READ outputComboModel NOTIFY outputComboModelChanged)
-    Q_PROPERTY(QJsonArray inputChannelsComboModel READ inputChannelsComboModel NOTIFY
-                   inputChannelsComboModelChanged)
-    Q_PROPERTY(QJsonArray outputChannelsComboModel READ outputChannelsComboModel NOTIFY
-                   outputChannelsComboModelChanged)
-    Q_PROPERTY(QJsonArray inputMixModeComboModel READ inputMixModeComboModel NOTIFY
-                   inputMixModeComboModelChanged)
-    Q_PROPERTY(bool inputClipped READ inputClipped NOTIFY updatedInputClipped)
-    Q_PROPERTY(bool outputClipped READ outputClipped NOTIFY updatedOutputClipped)
-    Q_PROPERTY(bool audioActivated READ audioActivated WRITE setAudioActivated NOTIFY
-                   audioActivatedChanged)
-    Q_PROPERTY(
-        bool audioReady READ audioReady WRITE setAudioReady NOTIFY audioReadyChanged)
-    Q_PROPERTY(bool backendAvailable READ backendAvailable CONSTANT)
     Q_PROPERTY(QString windowState READ windowState WRITE setWindowState NOTIFY
                    windowStateUpdated)
     Q_PROPERTY(QString apiHost READ apiHost WRITE setApiHost NOTIFY apiHostChanged)
     Q_PROPERTY(bool vsFtux READ vsFtux CONSTANT)
+    Q_PROPERTY(
+        QStringList updateChannelComboModel READ getUpdateChannelComboModel CONSTANT)
 
    public:
     explicit VirtualStudio(bool firstRun = false, QObject* parent = nullptr);
@@ -187,62 +129,21 @@ class VirtualStudio : public QObject
     void setStandardWindow(QSharedPointer<QJackTrip> window);
     void show();
     void raiseToTop();
-    bool vsModeActive();
 
+    int webChannelPort();
     bool showFirstRun();
     void setShowFirstRun(bool show);
     bool hasRefreshToken();
     QString versionString();
     QString logoSection();
-    bool selectableBackend();
-    QString audioBackend();
-    void setAudioBackend(const QString& backend);
-    QString inputDevice();
-    void setInputDevice(const QString& device);
-#ifdef RT_AUDIO
-    int baseInputChannel();
-    void setBaseInputChannel(int baseChannel);
-    int numInputChannels();
-    void setNumInputChannels(int numChannels);
-    void setInputMixMode(int mode);
-    int inputMixMode();
-#endif
-    QString outputDevice();
-    void setOutputDevice(const QString& device);
-#ifdef RT_AUDIO
-    int baseOutputChannel();
-    void setBaseOutputChannel(int baseChannel);
-    int numOutputChannels();
-    void setNumOutputChannels(int numChannels);
-#endif
-    int previousInput();
-    void setPreviousInput(int device);
-    int previousOutput();
-    void setPreviousOutput(int device);
-    QString devicesWarning();
-    QString devicesError();
-    QString devicesWarningHelpUrl();
-    QString devicesErrorHelpUrl();
     QString connectedErrorMsg();
     void setConnectedErrorMsg(const QString& msg);
-    int bufferSize();
-    void setBufferSize(int index);
-    int bufferStrategy();
-    void setBufferStrategy(int index);
-    bool feedbackDetectionEnabled();
-    void setFeedbackDetectionEnabled(bool enabled);
-    int currentStudio();
+    const QVector<VsServerInfo*>& getServerModel() const { return m_serverModel; }
+    VsServerInfo* currentStudio() { return &m_currentStudio; }
     QJsonObject regions();
     QJsonObject userMetadata();
     QString connectionState();
     QJsonObject networkStats();
-    const QVector<float>& inputMeterLevels() const;
-    const QVector<float>& outputMeterLevels() const;
-    const QJsonArray& inputComboModel() const;
-    const QJsonArray& outputComboModel() const;
-    const QJsonArray& inputChannelsComboModel() const;
-    const QJsonArray& outputChannelsComboModel() const;
-    const QJsonArray& inputMixModeComboModel() const;
     QString updateChannel();
     void setUpdateChannel(const QString& channel);
     bool showInactive();
@@ -256,6 +157,8 @@ class VirtualStudio : public QObject
     void setUiScale(float scale);
     bool darkMode();
     void setDarkMode(bool dark);
+    bool collapseDeviceControls();
+    void setCollapseDeviceControls(bool collapseDeviceControls);
     bool testMode();
     void setTestMode(bool test);
     QUrl studioToJoin();
@@ -267,23 +170,17 @@ class VirtualStudio : public QObject
     bool noUpdater();
     bool psiBuild();
     QString failedMessage();
-    float inputVolume();
-    float outputVolume();
-    float monitorVolume();
-    bool inputMuted();
-    bool outputMuted();
-    bool monitorMuted();
-    Q_INVOKABLE void restartAudio();
-    bool audioActivated();
-    bool audioReady();
-    bool inputClipped();
-    bool outputClipped();
     bool networkOutage();
     bool backendAvailable();
     QString windowState();
     QString apiHost();
     void setApiHost(QString host);
     bool vsFtux();
+    bool isExiting();
+    const QStringList& getUpdateChannelComboModel() const
+    {
+        return m_updateChannelOptions;
+    }
 
    public slots:
     void toStandard();
@@ -291,68 +188,33 @@ class VirtualStudio : public QObject
     void login();
     void logout();
     void refreshStudios(int index, bool signalRefresh = false);
-    void refreshDevices();
-    void refreshRtAudioDevices();
-    void validateDevicesState();
-    void validateInputDevicesState();
-    void validateOutputDevicesState();
-    void playOutputAudio();
-    void revertSettings();
-    void applySettings();
-    void connectToStudio(int studioIndex);
-    void completeConnection();
-    void triggerReconnect();
-    void disconnect();
-    void manageStudio(int studioIndex, bool start = false);
-    void launchVideo(int studioIndex);
+    void loadSettings();
+    void saveSettings();
+    void triggerReconnect(bool refresh);
+    void manageStudio(const QString& studioId, bool start = false);
+    void launchVideo(const QString& studioId);
     void createStudio();
     void editProfile();
     void showAbout();
     void openLink(const QString& url);
-    void updatedInputVuMeasurements(const float* valuesInDecibels, int numChannels);
-    void updatedOutputVuMeasurements(const float* valuesInDecibels, int numChannels);
+    void handleDeeplinkRequest(const QUrl& url);
     void udpWaitingTooLong();
-    void setInputVolume(float multiplier);
-    void setOutputVolume(float multiplier);
-    void setMonitorVolume(float multiplier);
-    void setInputMuted(bool muted);
-    void setOutputMuted(bool muted);
-    void setMonitorMuted(bool muted);
-    void setAudioActivated(bool activated);
-    void setAudioReady(bool ready);
-    void detectedFeedbackLoop();
     void setWindowState(QString state);
-    void exit();
+    void joinStudio();
+    void disconnect();
+    void collectFeedbackSurvey(QString serverId, int rating, QString message);
 
    signals:
-    void authSucceeded();
-    void authFailed();
     void failed();
     void connected();
     void disconnected();
     void refreshFinished(int index);
+    void webChannelPortChanged(int webChannelPort);
     void showFirstRunChanged();
     void hasRefreshTokenChanged();
     void logoSectionChanged();
-    void audioBackendChanged(bool useRtAudio, bool shouldRestart = true);
-    void inputDeviceChanged(QString device, bool shouldRestart = true);
-    void baseInputChannelChanged(int baseChannel, bool shouldRestart = true);
-    void numInputChannelsChanged(int numChannels, bool shouldRestart = true);
-    void inputMixModeChanged(int mode, bool shouldRestart = true);
-    void outputDeviceChanged(QString device, bool shouldRestart = true);
-    void baseOutputChannelChanged(int baseChannel, bool shouldRestart = true);
-    void numOutputChannelsChanged(int numChannels, bool shouldRestart = true);
-    void previousInputChanged();
-    void previousOutputChanged();
-    void devicesWarningChanged();
-    void devicesErrorChanged();
-    void devicesWarningHelpUrlChanged();
-    void devicesErrorHelpUrlChanged();
     void connectedErrorMsgChanged();
-    void triggerPlayOutputAudio();
-    void bufferSizeChanged();
-    void bufferStrategyChanged();
-    void feedbackDetectionEnabledChanged();
+    void serverModelChanged();
     void currentStudioChanged();
     void regionsChanged();
     void userMetadataChanged();
@@ -365,220 +227,118 @@ class VirtualStudio : public QObject
     void showDeviceSetupChanged();
     void showWarningsChanged();
     void uiScaleChanged();
+    void collapseDeviceControlsChanged(bool collapseDeviceControls);
     void newScale();
     void darkModeChanged();
     void testModeChanged();
-    void feedbackDetected();
     void signalExit();
     void periodicRefresh();
     void failedMessageChanged();
     void studioToJoinChanged();
-    void updatedInputVolume(float multiplier);
-    void updatedOutputVolume(float multiplier);
-    void updatedMonitorVolume(float multiplier);
-    void updatedInputMuted(bool muted);
-    void updatedOutputMuted(bool muted);
-    void updatedMonitorMuted(bool muted);
-    void updatedInputMeterLevels(const QVector<float>& levels);
-    void updatedOutputMeterLevels(const QVector<float>& levels);
-    void inputComboModelChanged();
-    void outputComboModelChanged();
-    void inputChannelsComboModelChanged();
-    void outputChannelsComboModelChanged();
-    void inputMixModeComboModelChanged();
-    void updatedInputClipped(bool clip);
-    void updatedOutputClipped(bool clip);
     void updatedNetworkOutage(bool outage);
-    void audioActivatedChanged();
-    void audioReadyChanged();
     void windowStateUpdated();
+    void isExitingChanged();
     void apiHostChanged();
+    void feedbackDetected();
+    void openFeedbackSurveyModal(QString serverId);
 
    private slots:
     void slotAuthSucceeded();
-    void slotAuthFailed();
-    void processFinished();
-    void processError(const QString& errorMessage);
     void receivedConnectionFromPeer();
     void handleWebsocketMessage(const QString& msg);
-    void endRetryPeriod();
+    void restartStudioSocket();
     void launchBrowser(const QUrl& url);
-    void joinStudio();
     void updatedStats(const QJsonObject& stats);
-    void startAudio();
-    void updatedDevicesErrorMsg(const QString& msg);
-    void updatedDevicesWarningMsg(const QString& msg);
-    void updatedDevicesErrorHelpUrl(const QString& url);
-    void updatedDevicesWarningHelpUrl(const QString& url);
+    void processError(const QString& errorMessage);
+    void detectedFeedbackLoop();
+    void sendHeartbeat();
+    void connectionFinished();
+    void exit();
 
    private:
-    void sendHeartbeat();
-    void getServerList(bool firstLoad = false, bool signalRefresh = false,
-                       int index = -1);
-    void getUserId();
+    void getServerList(bool signalRefresh = false, int index = -1);
     void getSubscriptions();
     void getRegions();
     void getUserMetadata();
     void stopStudio();
-    void toggleAudio();
-    void resetMeters();
-    void stopAudio();
     bool readyToJoin();
-#ifdef RT_AUDIO
-    QJsonArray formatDeviceList(const QStringList& devices, const QStringList& categories,
-                                const QList<int>& channels);
-#endif
+    void connectToStudio(VsServerInfo& studio);
+    void completeConnection();
+
+   private:
+    enum ReconnectState {
+        NOT_RECONNECTING = 0,
+        RECONNECTING_VALIDATE,
+        RECONNECTING_REFRESH
+    };
 
-    bool m_showFirstRun = false;
-    bool m_checkSsl     = true;
-    bool m_vsModeActive = false;
-    QString m_updateChannel;
-    QString m_refreshToken;
-    QString m_userId;
     VsQuickView m_view;
+    VsServerInfo m_currentStudio;
     QSharedPointer<QJackTrip> m_standardWindow;
-    QScopedPointer<VsAuth> m_auth;
-    QScopedPointer<VsApi> m_api;
     QScopedPointer<QNetworkAccessManager> m_networkAccessManager;
-
-    QList<QObject*> m_servers;
-    QStringList m_subscribedServers;
+    QSharedPointer<VsAuth> m_auth;
+    QSharedPointer<VsApi> m_api;
+    QScopedPointer<VsDevice> m_devicePtr;
+    QScopedPointer<VsWebSocket> m_studioSocketPtr;
+    QSharedPointer<VsAudio> m_audioConfigPtr;
+    QVector<VsServerInfoPointer> m_servers;
+    QVector<VsServerInfo*> m_serverModel;  //< qml doesn't like smart pointers
+    QScopedPointer<QWebSocketServer> m_webChannelServer;
+    QScopedPointer<QWebChannel> m_webChannel;
+    QMap<QString, bool> m_subscribedServers;
     QJsonObject m_regions;
     QJsonObject m_userMetadata;
-    QString m_logoSection     = QStringLiteral("Your Studios");
-    bool m_selectableBackend  = true;
-    bool m_useRtAudio         = false;
-    int m_currentStudio       = -1;
-    QString m_connectionState = QStringLiteral("Waiting...");
-    QScopedPointer<JackTrip> m_jackTrip;
-    VsWebSocket* m_studioSocket = NULL;
+    QJsonObject m_networkStats;
     QTimer m_startTimer;
-    QTimer m_retryPeriodTimer;
-    bool m_retryPeriod;
-    bool m_jackTripRunning = false;
-
     QTimer m_refreshTimer;
-    QMutex m_refreshMutex;
-    bool m_allowRefresh      = true;
-    bool m_refreshInProgress = false;
-
-    QJsonObject m_networkStats;
-
     QTimer m_heartbeatTimer;
-    VsWebSocket* m_heartbeatWebSocket = NULL;
-    VsDevice* m_device                = NULL;
-
-    bool m_onConnectedScreen = false;
-    bool m_isExiting         = false;
-    bool m_showInactive      = true;
-    bool m_showSelfHosted    = false;
-    bool m_showCreateStudio  = false;
-    bool m_showDeviceSetup   = true;
-    bool m_showWarnings      = true;
-    float m_fontScale        = 1;
-    float m_uiScale;
-    float m_previousUiScale;
-    int m_bufferStrategy            = 0;
-    bool m_feedbackDetectionEnabled = true;
-    QString m_apiHost               = PROD_API_HOST;
-    bool m_darkMode                 = false;
-    bool m_testMode                 = false;
-    QString m_failedMessage         = "";
-    QUrl m_studioToJoin;
-    bool m_authenticated  = false;
-    bool m_audioActivated = false;
-    bool m_audioReady     = false;
-    bool m_inputClipped   = false;
-    bool m_outputClipped  = false;
-    bool m_networkOutage  = false;
-
-    Analyzer* m_inputAnalyzerPlugin;
-    Analyzer* m_outputAnalyzerPlugin;
-    QVector<float> m_inputMeterLevels;
-    QVector<float> m_outputMeterLevels;
-    QJsonArray m_inputComboModel;
-    QJsonArray m_outputComboModel;
-    QJsonArray m_inputChannelsComboModel;
-    QJsonArray m_outputChannelsComboModel;
-    QJsonArray m_inputMixModeComboModel;
-    Meter* m_inputMeter;
-    Meter* m_outputMeter;
-    Meter* m_inputTestMeter;
-    Volume* m_inputVolumePlugin;
-    Volume* m_outputVolumePlugin;
-    Monitor* m_monitor;
-    QTimer m_inputClipTimer;
-    QTimer m_outputClipTimer;
     QTimer m_networkOutageTimer;
+    QMutex m_refreshMutex;
+    QUrl m_studioToJoin;
+    QString m_updateChannel;
+    QString m_refreshToken;
+    QString m_userId;
+    QString m_apiHost               = PROD_API_HOST;
+    ReconnectState m_reconnectState = ReconnectState::NOT_RECONNECTING;
+    QJackTrip::uiModeT m_uiMode     = QJackTrip::UNSET;
+
+    bool m_firstRefresh           = true;
+    bool m_jackTripRunning        = false;
+    bool m_showFirstRun           = false;
+    bool m_checkSsl               = true;
+    bool m_refreshInProgress      = false;
+    bool m_onConnectedScreen      = false;
+    bool m_isExiting              = false;
+    bool m_showInactive           = true;
+    bool m_showSelfHosted         = false;
+    bool m_showCreateStudio       = false;
+    bool m_showDeviceSetup        = true;
+    bool m_showWarnings           = true;
+    bool m_darkMode               = false;
+    bool m_collapseDeviceControls = false;
+    bool m_testMode               = false;
+    bool m_authenticated          = false;
+    bool m_networkOutage          = false;
+    float m_fontScale             = 1;
+    float m_uiScale               = 1;
+    uint32_t m_webChannelPort     = 1;
+
+    QString m_failedMessage            = QStringLiteral("");
+    QString m_windowState              = QStringLiteral("start");
+    QString m_connectedErrorMsg        = QStringLiteral("");
+    QString m_logoSection              = QStringLiteral("Your Studios");
+    QString m_connectionState          = QStringLiteral("Waiting...");
+    QStringList m_updateChannelOptions = {"Stable", "Edge"};
 
-    QString m_devicesWarningMsg     = QStringLiteral("");
-    QString m_devicesErrorMsg       = QStringLiteral("");
-    QString m_devicesWarningHelpUrl = QStringLiteral("");
-    QString m_devicesErrorHelpUrl   = QStringLiteral("");
-    QString m_windowState           = QStringLiteral("login");
-    QString m_connectedErrorMsg     = QStringLiteral("");
-
-    float m_meterMax = 0.0;
-    float m_meterMin = -64.0;
-
-    float m_inMultiplier  = 1.0;
-    float m_outMultiplier = 1.0;
-    float m_monMultiplier = 1.0;
-    bool m_inMuted        = false;
-    bool m_outMuted       = false;
-    bool m_monMuted       = false;
-
-    QSharedPointer<VsAudioInterface> m_vsAudioInterface;
+#ifdef __APPLE__
+    NoNap m_noNap;
+#endif
 
 #ifdef VS_FTUX
     bool m_vsFtux = true;
 #else
     bool m_vsFtux = false;
 #endif
-
-#ifdef RT_AUDIO
-    QStringList m_inputDeviceList;
-    QStringList m_outputDeviceList;
-    QStringList m_inputDeviceCategories;
-    QStringList m_outputDeviceCategories;
-    QList<int> m_inputDeviceChannels;
-    QList<int> m_outputDeviceChannels;
-    QString m_inputDevice;
-    QString m_outputDevice;
-    quint16 m_bufferSize;
-    QString m_previousInput;
-    QString m_previousOutput;
-    quint16 m_previousBuffer;
-
-    int m_baseInputChannel;
-    int m_numInputChannels;
-    int m_inputMixMode;
-
-    int m_baseOutputChannel;
-    int m_numOutputChannels;
-
-    bool m_previousUseRtAudio = false;
-    inline void delay(int millisecondsWait)
-    {
-        QEventLoop loop;
-        QTimer t;
-        t.connect(&t, &QTimer::timeout, &loop, &QEventLoop::quit);
-        t.start(millisecondsWait);
-        loop.exec();
-    }
-#endif
-    QStringList m_bufferOptions         = {"16", "32", "64", "128", "256", "512", "1024"};
-    QStringList m_bufferStrategyOptions = {"Minimal Latency", "Stable Latency",
-                                           "Loss Concealment (3)",
-                                           "Loss Concealment (4)"};
-    QStringList m_updateChannelOptions  = {"Stable", "Edge"};
-    QStringList m_feedbackDetectionOptions = {"Enabled", "Disabled"};
-
-#ifdef __APPLE__
-    NoNap m_noNap;
-#endif
-
-    QSharedPointer<VsPermissions> m_permissions;
 };
 
 #endif  // VIRTUALSTUDIO_H
index ade4586514921c51c9e824c1f0ec3e0ce8b15fc1..ee4913e666e05fdd940ca07cc517a413aac2adee 100644 (file)
@@ -1,14 +1,12 @@
-import QtQuick 2.12
-import QtQuick.Controls 2.12
+import QtQuick
+import QtQuick.Controls
 
 Rectangle {
     property string backgroundColour: virtualstudio.darkMode ? "#272525" : "#FAFBFB"
     property string textColour: virtualstudio.darkMode ? "#FAFBFB" : "#0F0D0D"
 
-    width: 696
-    height: 577
     color: backgroundColour
-    state: virtualstudio.showFirstRun ? "start" : virtualstudio.windowState
+    state: virtualstudio.windowState
     anchors.fill: parent
 
     id: window
@@ -17,10 +15,13 @@ Rectangle {
             name: "start"
             PropertyChanges { target: startScreen; x: 0 }
             PropertyChanges { target: loginScreen; x: window.width; }
+            PropertyChanges { target: recommendationsScreen; x: window.width }
+            PropertyChanges { target: permissionsScreen; x: window.width }
             PropertyChanges { target: setupScreen; x: window.width }
             PropertyChanges { target: browseScreen; x: window.width }
             PropertyChanges { target: settingsScreen; x: window.width }
             PropertyChanges { target: connectedScreen; x: window.width }
+            PropertyChanges { target: changeDevicesScreen; x: 2*window.width }
             PropertyChanges { target: failedScreen; x: window.width }
         },
 
@@ -28,10 +29,41 @@ Rectangle {
             name: "login"
             PropertyChanges { target: startScreen; x: -startScreen.width }
             PropertyChanges { target: loginScreen; x: 0; }
+            PropertyChanges { target: recommendationsScreen; x: window.width }
+            PropertyChanges { target: permissionsScreen; x: window.width }
             PropertyChanges { target: setupScreen; x: window.width }
             PropertyChanges { target: browseScreen; x: window.width }
             PropertyChanges { target: settingsScreen; x: window.width }
             PropertyChanges { target: connectedScreen; x: window.width }
+            PropertyChanges { target: changeDevicesScreen; x: 2*window.width }
+            PropertyChanges { target: failedScreen; x: window.width }
+        },
+
+        State {
+            name: "recommendations"
+            PropertyChanges { target: loginScreen; x: -loginScreen.width }
+            PropertyChanges { target: startScreen; x: -startScreen.width }
+            PropertyChanges { target: recommendationsScreen; x: 0 }
+            PropertyChanges { target: permissionsScreen; x: window.width }
+            PropertyChanges { target: setupScreen; x: window.width }
+            PropertyChanges { target: browseScreen; x: window.width }
+            PropertyChanges { target: settingsScreen; x: window.width }
+            PropertyChanges { target: connectedScreen; x: window.width }
+            PropertyChanges { target: changeDevicesScreen; x: 2*window.width }
+            PropertyChanges { target: failedScreen; x: window.width }
+        },
+
+        State {
+            name: "permissions"
+            PropertyChanges { target: loginScreen; x: -loginScreen.width }
+            PropertyChanges { target: startScreen; x: -startScreen.width }
+            PropertyChanges { target: recommendationsScreen; x: -recommendationsScreen.width }
+            PropertyChanges { target: permissionsScreen; x: 0 }
+            PropertyChanges { target: setupScreen; x: window.width }
+            PropertyChanges { target: browseScreen; x: window.width }
+            PropertyChanges { target: settingsScreen; x: window.width }
+            PropertyChanges { target: connectedScreen; x: window.width }
+            PropertyChanges { target: changeDevicesScreen; x: 2*window.width }
             PropertyChanges { target: failedScreen; x: window.width }
         },
 
@@ -39,10 +71,13 @@ Rectangle {
             name: "setup"
             PropertyChanges { target: loginScreen; x: -loginScreen.width }
             PropertyChanges { target: startScreen; x: -startScreen.width }
+            PropertyChanges { target: recommendationsScreen; x: -recommendationsScreen.width }
+            PropertyChanges { target: permissionsScreen; x: -permissionsScreen.width }
             PropertyChanges { target: setupScreen; x: 0 }
-            PropertyChanges { target: browseScreen; x: window.width }
+            PropertyChanges { target: browseScreen; x: -browseScreen.width }
             PropertyChanges { target: settingsScreen; x: window.width }
             PropertyChanges { target: connectedScreen; x: window.width }
+            PropertyChanges { target: changeDevicesScreen; x: 2*window.width }
             PropertyChanges { target: failedScreen; x: window.width }
         },
 
@@ -50,10 +85,13 @@ Rectangle {
             name: "browse"
             PropertyChanges { target: loginScreen; x: -loginScreen.width }
             PropertyChanges { target: startScreen; x: -startScreen.width }
-            PropertyChanges { target: setupScreen; x: -setupScreen.width }
+            PropertyChanges { target: recommendationsScreen; x: -recommendationsScreen.width }
+            PropertyChanges { target: permissionsScreen; x: -permissionsScreen.width }
+            PropertyChanges { target: setupScreen; x: window.width }
             PropertyChanges { target: browseScreen; x: 0 }
             PropertyChanges { target: settingsScreen; x: window.width }
             PropertyChanges { target: connectedScreen; x: window.width }
+            PropertyChanges { target: changeDevicesScreen; x: 2*window.width }
             PropertyChanges { target: failedScreen; x: window.width }
         },
 
@@ -61,10 +99,13 @@ Rectangle {
             name: "settings"
             PropertyChanges { target: loginScreen; x: -loginScreen.width }
             PropertyChanges { target: startScreen; x: -startScreen.width }
-            PropertyChanges { target: setupScreen; x: -setupScreen.width }
+            PropertyChanges { target: recommendationsScreen; x: -recommendationsScreen.width }
+            PropertyChanges { target: permissionsScreen; x: -permissionsScreen.width }
+            PropertyChanges { target: setupScreen; x: window.width }
             PropertyChanges { target: browseScreen; x: -browseScreen.width }
             PropertyChanges { target: settingsScreen; x: 0 }
             PropertyChanges { target: connectedScreen; x: window.width }
+            PropertyChanges { target: changeDevicesScreen; x: 2*window.width }
             PropertyChanges { target: failedScreen; x: window.width }
         },
 
@@ -72,10 +113,27 @@ Rectangle {
             name: "connected"
             PropertyChanges { target: loginScreen; x: -loginScreen.width }
             PropertyChanges { target: startScreen; x: -startScreen.width }
-            PropertyChanges { target: setupScreen; x: -setupScreen.width }
+            PropertyChanges { target: recommendationsScreen; x: -recommendationsScreen.width }
+            PropertyChanges { target: permissionsScreen; x: -permissionsScreen.width }
+            PropertyChanges { target: setupScreen; x: 0 }
+            PropertyChanges { target: browseScreen; x: -browseScreen.width }
+            PropertyChanges { target: settingsScreen; x: window.width }
+            PropertyChanges { target: connectedScreen; x: 0 }
+            PropertyChanges { target: changeDevicesScreen; x: window.width }
+            PropertyChanges { target: failedScreen; x: window.width }
+        },
+
+        State {
+            name: "change_devices"
+            PropertyChanges { target: loginScreen; x: -loginScreen.width }
+            PropertyChanges { target: startScreen; x: -startScreen.width }
+            PropertyChanges { target: recommendationsScreen; x: -recommendationsScreen.width }
+            PropertyChanges { target: permissionsScreen; x: -permissionsScreen.width }
+            PropertyChanges { target: setupScreen; x: 0 }
             PropertyChanges { target: browseScreen; x: -browseScreen.width }
             PropertyChanges { target: settingsScreen; x: window.width }
             PropertyChanges { target: connectedScreen; x: 0 }
+            PropertyChanges { target: changeDevicesScreen; x: 0 }
             PropertyChanges { target: failedScreen; x: window.width }
         },
 
@@ -83,32 +141,43 @@ Rectangle {
             name: "failed"
             PropertyChanges { target: loginScreen; x: -loginScreen.width }
             PropertyChanges { target: startScreen; x: -startScreen.width }
+            PropertyChanges { target: recommendationsScreen; x: -recommendationsScreen.width }
+            PropertyChanges { target: permissionsScreen; x: -permissionsScreen.width }
             PropertyChanges { target: setupScreen; x: -setupScreen.width }
             PropertyChanges { target: browseScreen; x: -browseScreen.width }
             PropertyChanges { target: settingsScreen; x: window.width }
             PropertyChanges { target: connectedScreen; x: window.width }
+            PropertyChanges { target: changeDevicesScreen; x: 2*window.width }
             PropertyChanges { target: failedScreen; x: 0 }
         }
     ]
 
     transitions: Transition {
-        NumberAnimation { properties: "x"; duration: 800; easing.type: Easing.InOutQuad }
+        NumberAnimation { properties: "x"; duration: 500; easing.type: Easing.InOutQuad }
     }
 
     FirstLaunch {
         id: startScreen
     }
 
-    Setup {
-        id: setupScreen
+    Login {
+        id: loginScreen
+    }
+
+    Recommendations {
+        id: recommendationsScreen
+    }
+
+    Permissions {
+        id: permissionsScreen
     }
 
     Browse {
         id: browseScreen
     }
 
-    Login {
-        id: loginScreen
+    Setup {
+        id: setupScreen
     }
 
     Settings {
@@ -119,24 +188,88 @@ Rectangle {
         id: connectedScreen
     }
 
+    ChangeDevices {
+        id: changeDevicesScreen
+    }
+
     Failed {
         id: failedScreen
     }
 
+    onWidthChanged: {
+        if (virtualstudio.windowState === "start") {
+            startScreen.x = 0
+        } else if (virtualstudio.windowState === "login") {
+            loginScreen.x = 0
+        } else if (virtualstudio.windowState === "recommendations") {
+            recommendationsScreen.x = 0;
+        } else if (virtualstudio.windowState === "permissions") {
+            permissionsScreen.x = 0;
+        } else if (virtualstudio.windowState === "setup") {
+            setupScreen.x = 0
+        } else if (virtualstudio.windowState === "browse") {
+            browseScreen.x = 0
+        } else if (virtualstudio.windowState === "settings") {
+            settingsScreen.x = 0
+        } else if (virtualstudio.windowState === "connected") {
+            connectedScreen.x = 0
+        } else if (virtualstudio.windowState === "change_devices") {
+            changeDevicesScreen.x = 0
+        } else if (virtualstudio.windowState === "failed") {
+            failedScreen.x = 0
+        }
+    }
+
+    onHeightChanged: {
+        if (virtualstudio.windowState === "start") {
+            startScreen.x = 0
+        } else if (virtualstudio.windowState === "login") {
+            loginScreen.x = 0
+        } else if (virtualstudio.windowState === "recommendations") {
+            recommendationsScreen.x = 0;
+        } else if (virtualstudio.windowState === "permissions") {
+            permissionsScreen.x = 0;
+        } else if (virtualstudio.windowState === "setup") {
+            setupScreen.x = 0
+        } else if (virtualstudio.windowState === "browse") {
+            browseScreen.x = 0
+        } else if (virtualstudio.windowState === "settings") {
+            settingsScreen.x = 0
+        } else if (virtualstudio.windowState === "connected") {
+            connectedScreen.x = 0
+        } else if (virtualstudio.windowState === "change_devices") {
+            changeDevicesScreen.x = 0
+        } else if (virtualstudio.windowState === "failed") {
+            failedScreen.x = 0
+        }
+    }
+
     Connections {
-        target: virtualstudio
+        target: auth
         function onAuthSucceeded() {
             if (virtualstudio.windowState !== "login") {
                 // can happen on settings screen when switching between prod and test
                 return;
             }
-            if (virtualstudio.showDeviceSetup) {
+            if (virtualstudio.showWarnings) {
+                virtualstudio.windowState = "recommendations";
+            } else if (virtualstudio.studioToJoin.toString() === "") {
+                virtualstudio.windowState = "browse";
+            } else if (virtualstudio.showDeviceSetup) {
                 virtualstudio.windowState = "setup";
+                audio.startAudio();
             } else {
-                virtualstudio.windowState = "browse";
+                virtualstudio.windowState = "connected";
+                virtualstudio.joinStudio();
             }
         }
+    }
+    Connections {
+        target: virtualstudio
         function onConnected() {
+            if (virtualstudio.windowState == "change_devices") {
+                return;
+            }
             virtualstudio.windowState = "connected";
         }
         function onFailed() {
@@ -145,10 +278,5 @@ Rectangle {
         function onDisconnected() {
             virtualstudio.windowState = "browse";
         }
-        function onWindowStateUpdated() {
-            if (virtualstudio.windowState === "login") {
-                virtualstudio.login();
-            }
-        }
     }
 }
index 9c4bd3c2155d63032644e6dd15933eb190ee6ebb..a177e8f15145c388b59913b985bf45179754092b 100644 (file)
@@ -85,6 +85,14 @@ QNetworkReply* VsApi::postDeviceHeartbeat(const QString& deviceId, const QByteAr
         data);
 }
 
+QNetworkReply* VsApi::submitServerFeedback(const QString& serverId,
+                                           const QByteArray& data)
+{
+    return post(
+        QUrl(QString("https://%1/api/servers/%2/feedback").arg(m_apiHost, serverId)),
+        data);
+}
+
 QNetworkReply* VsApi::updateServer(const QString& serverId, const QByteArray& data)
 {
     return put(QUrl(QString("https://%1/api/servers/%2").arg(m_apiHost, serverId)), data);
index 04122460cc78f4f1a9707ee2af6c095585863a94..2d64f0d14096c18ae10b07fbb22ac8d1bf823ac6 100644 (file)
@@ -68,6 +68,7 @@ class VsApi : public QObject
 
     QNetworkReply* postDevice(const QByteArray& data);
     QNetworkReply* postDeviceHeartbeat(const QString& deviceId, const QByteArray& data);
+    QNetworkReply* submitServerFeedback(const QString& serverId, const QByteArray& data);
 
     QNetworkReply* updateServer(const QString& serverId, const QByteArray& data);
     QNetworkReply* updateDevice(const QString& deviceId, const QByteArray& data);
diff --git a/src/gui/vsAudio.cpp b/src/gui/vsAudio.cpp
new file mode 100644 (file)
index 0000000..f108747
--- /dev/null
@@ -0,0 +1,1401 @@
+//*****************************************************************
+/*
+  JackTrip: A System for High-Quality Audio Network Performance
+  over the Internet
+
+  Copyright (c) 2008-2022 Juan-Pablo Caceres, Chris Chafe.
+  SoundWIRE group at CCRMA, Stanford University.
+
+  Permission is hereby granted, free of charge, to any person
+  obtaining a copy of this software and associated documentation
+  files (the "Software"), to deal in the Software without
+  restriction, including without limitation the rights to use,
+  copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the
+  Software is furnished to do so, subject to the following
+  conditions:
+
+  The above copyright notice and this permission notice shall be
+  included in all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+  OTHER DEALINGS IN THE SOFTWARE.
+*/
+//*****************************************************************
+
+/**
+ * \file vsAudio.cpp
+ * \author Matt Horton
+ * \date September 2022
+ */
+
+#include "vsAudio.h"
+
+#include <QDebug>
+#include <QEventLoop>
+#include <QJsonObject>
+#include <QSettings>
+#include <QThread>
+
+#ifdef USE_WEAK_JACK
+#include "weak_libjack.h"
+#endif
+
+#ifndef NO_JACK
+#include "../JackAudioInterface.h"
+#endif
+
+#ifdef __APPLE__
+#include "vsMacPermissions.h"
+#else
+#include "vsPermissions.h"
+#endif
+
+#ifndef NO_FEEDBACK
+#include "../Analyzer.h"
+#endif
+
+#include "../JackTrip.h"
+#include "../Meter.h"
+#include "../Monitor.h"
+#include "../Tone.h"
+#include "../Volume.h"
+#include "AudioInterfaceMode.h"
+
+// generic function to wait for a signal to be emitted
+template<typename SignalSenderPtr, typename SignalFuncPtr>
+static inline void WaitForSignal(SignalSenderPtr sender, SignalFuncPtr signal)
+{
+    QTimer timer;
+    timer.setTimerType(Qt::CoarseTimer);
+    timer.setSingleShot(true);
+
+    QEventLoop loop;
+    QObject::connect(sender, signal, &loop, &QEventLoop::quit);
+    QObject::connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
+    timer.start(10000);  // wait for max 10 seconds
+    loop.exec();
+}
+
+// Constructor
+VsAudio::VsAudio(QObject* parent)
+    : QObject(parent)
+    , m_inputMeterLevels(2, 0)
+    , m_outputMeterLevels(2, 0)
+    , m_inputComboModel(QJsonArray::fromStringList(QStringList(QLatin1String(""))))
+    , m_outputComboModel(QJsonArray::fromStringList(QStringList(QLatin1String(""))))
+    , m_inputChannelsComboModel(
+          QJsonArray::fromStringList(QStringList(QLatin1String(""))))
+    , m_outputChannelsComboModel(
+          QJsonArray::fromStringList(QStringList(QLatin1String(""))))
+    , m_inputMixModeComboModel(QJsonArray::fromStringList(QStringList(QLatin1String(""))))
+    , m_audioWorkerPtr(new VsAudioWorker(this))
+{
+    loadSettings();
+
+    if (isBackendAvailable<AudioInterfaceMode::RTAUDIO>()) {
+        if (!jackIsAvailable()) {
+            m_audioBackendComboModel = {"RtAudio"};
+        }
+    } else {
+        if (!jackIsAvailable()) {
+            // shouldn't happen
+            m_audioBackendComboModel = {};
+        } else {
+            m_audioBackendComboModel = {"JACK"};
+        }
+    }
+
+    QJsonObject element;
+    element.insert(QString::fromStdString("label"), QString::fromStdString("Mono"));
+    element.insert(QString::fromStdString("value"),
+                   static_cast<int>(AudioInterface::MONO));
+    m_inputMixModeComboModel = QJsonArray();
+    m_inputMixModeComboModel.push_back(element);
+
+    element = QJsonObject();
+    element.insert(QString::fromStdString("label"), QString::fromStdString("1"));
+    element.insert(QString::fromStdString("baseChannel"), QVariant(0).toInt());
+    element.insert(QString::fromStdString("numChannels"), QVariant(1).toInt());
+    m_inputChannelsComboModel = QJsonArray();
+    m_inputChannelsComboModel.push_back(element);
+
+    element = QJsonObject();
+    element.insert(QString::fromStdString("label"), QString::fromStdString("1 & 2"));
+    element.insert(QString::fromStdString("baseChannel"), QVariant(0).toInt());
+    element.insert(QString::fromStdString("numChannels"), QVariant(2).toInt());
+    m_outputChannelsComboModel = QJsonArray();
+    m_outputChannelsComboModel.push_back(element);
+
+    // Initialize timers needed for clip indicators
+    m_inputClipTimer.setTimerType(Qt::CoarseTimer);
+    m_inputClipTimer.setSingleShot(true);
+    m_inputClipTimer.setInterval(3000);
+    m_outputClipTimer.setTimerType(Qt::CoarseTimer);
+    m_outputClipTimer.setSingleShot(true);
+    m_outputClipTimer.setInterval(3000);
+    m_inputClipTimer.callOnTimeout([&]() {
+        if (m_inputClipped) {
+            m_inputClipped = false;
+            emit updatedInputClipped(m_inputClipped);
+        }
+    });
+    m_outputClipTimer.callOnTimeout([&]() {
+        if (m_outputClipped) {
+            m_outputClipped = false;
+            emit updatedOutputClipped(m_outputClipped);
+        }
+    });
+
+    // move audio worker to its own thread
+    m_workerThread.reset(new QThread);
+    m_workerThread->setObjectName("VsAudioWorker");
+    m_workerThread->start();
+    m_audioWorkerPtr->moveToThread(m_workerThread.get());
+
+    // connect worker signals to slots
+    connect(this, &VsAudio::signalStartAudio, m_audioWorkerPtr.get(),
+            &VsAudioWorker::openAudioInterface, Qt::QueuedConnection);
+    connect(this, &VsAudio::signalStopAudio, m_audioWorkerPtr.get(),
+            &VsAudioWorker::closeAudioInterface, Qt::QueuedConnection);
+#ifdef RT_AUDIO
+    connect(this, &VsAudio::signalRefreshDevices, m_audioWorkerPtr.get(),
+            &VsAudioWorker::refreshDevices, Qt::QueuedConnection);
+    connect(this, &VsAudio::signalValidateDevices, m_audioWorkerPtr.get(),
+            &VsAudioWorker::validateDevices, Qt::QueuedConnection);
+    connect(m_audioWorkerPtr.get(), &VsAudioWorker::signalUpdatedDeviceModels, this,
+            &VsAudio::setDeviceModels, Qt::QueuedConnection);
+#endif
+
+    // Add permissions for Mac
+#ifdef __APPLE__
+    m_permissionsPtr.reset(new VsMacPermissions());
+    if (m_permissionsPtr->micPermissionChecked()
+        && m_permissionsPtr->micPermission() == "unknown") {
+        m_permissionsPtr->getMicPermission();
+    }
+#else
+    m_permissionsPtr.reset(new VsPermissions());
+#endif
+}
+
+bool VsAudio::backendAvailable() const
+{
+    if constexpr ((isBackendAvailable<AudioInterfaceMode::JACK>()
+                   || isBackendAvailable<AudioInterfaceMode::RTAUDIO>())) {
+        return true;
+    } else {
+        return false;
+    }
+}
+
+bool VsAudio::jackIsAvailable() const
+{
+    if constexpr (isBackendAvailable<AudioInterfaceMode::JACK>()) {
+#ifdef USE_WEAK_JACK
+        // Check if Jack is available
+        return (have_libjack() == 0);
+#else
+        return true;
+#endif
+    } else {
+        return false;
+    }
+}
+
+void VsAudio::setAudioReady(bool ready)
+{
+    if (ready == m_audioReady)
+        return;
+    m_audioReady = ready;
+    emit signalAudioReadyChanged();
+    if (m_audioReady)
+        emit signalAudioIsReady();
+    else
+        emit signalAudioIsNotReady();
+}
+
+void VsAudio::setScanningDevices(bool b)
+{
+    if (b == m_scanningDevices)
+        return;
+    m_scanningDevices = b;
+    emit signalScanningDevicesChanged();
+}
+
+void VsAudio::setAudioBackend(const QString& backend)
+{
+    bool useRtAudio = (backend == QStringLiteral("RtAudio"));
+    if (useRtAudio) {
+        if (getUseRtAudio())
+            return;
+        m_backend = AudioBackendType::RTAUDIO;
+        refreshDevices();
+    } else {
+        if (!getUseRtAudio())
+            return;
+        m_backend = AudioBackendType::JACK;
+    }
+    emit audioBackendChanged(useRtAudio);
+}
+
+void VsAudio::setFeedbackDetectionEnabled(bool enabled)
+{
+    if (m_feedbackDetectionEnabled == enabled)
+        return;
+    m_feedbackDetectionEnabled = enabled;
+    emit feedbackDetectionEnabledChanged();
+}
+
+void VsAudio::setBufferSize([[maybe_unused]] int bufSize)
+{
+    if (m_audioBufferSize == bufSize)
+        return;
+    m_audioBufferSize = bufSize;
+    emit bufferSizeChanged();
+}
+
+void VsAudio::setBufferStrategy(int bufStrategy)
+{
+    if (m_bufferStrategy == bufStrategy)
+        return;
+    m_bufferStrategy = bufStrategy;
+    emit bufferStrategyChanged();
+}
+
+void VsAudio::setNumInputChannels(int numChannels)
+{
+    if (numChannels == m_numInputChannels)
+        return;
+    m_numInputChannels = numChannels;
+    emit numInputChannelsChanged(numChannels);
+}
+
+void VsAudio::setNumOutputChannels(int numChannels)
+{
+    if (numChannels == m_numOutputChannels)
+        return;
+    m_numOutputChannels = numChannels;
+    emit numOutputChannelsChanged(numChannels);
+}
+
+void VsAudio::setBaseInputChannel(int baseChannel)
+{
+    if (baseChannel == m_baseInputChannel)
+        return;
+    m_baseInputChannel = baseChannel;
+    emit baseInputChannelChanged(baseChannel);
+    return;
+}
+
+void VsAudio::setBaseOutputChannel(int baseChannel)
+{
+    if (baseChannel == m_baseOutputChannel)
+        return;
+    m_baseOutputChannel = baseChannel;
+    emit baseOutputChannelChanged(baseChannel);
+    return;
+}
+
+void VsAudio::setInputMixMode(const int mode)
+{
+    if (mode == m_inputMixMode)
+        return;
+    m_inputMixMode = mode;
+    emit inputMixModeChanged(mode);
+    return;
+}
+
+void VsAudio::setInputMuted(bool muted)
+{
+    if (m_inMuted == muted)
+        return;
+    m_inMuted = muted;
+    emit updatedInputMuted(muted);
+}
+
+void VsAudio::setInputVolume(float multiplier)
+{
+    if (multiplier == m_inMultiplier)
+        return;
+    m_inMultiplier = multiplier;
+    emit updatedInputVolume(multiplier);
+}
+
+void VsAudio::setOutputVolume(float multiplier)
+{
+    if (multiplier == m_outMultiplier)
+        return;
+    m_outMultiplier = multiplier;
+    emit updatedOutputVolume(multiplier);
+}
+
+void VsAudio::setMonitorVolume(float multiplier)
+{
+    if (multiplier == m_monMultiplier)
+        return;
+    m_monMultiplier = multiplier;
+    emit updatedMonitorVolume(multiplier);
+}
+
+void VsAudio::setInputDevice([[maybe_unused]] const QString& device)
+{
+    if (!getUseRtAudio())
+        return;
+#ifdef RT_AUDIO
+    if (device == m_inputDevice)
+        return;
+    m_inputDevice = device;
+    emit inputDeviceChanged(m_inputDevice);
+#endif
+}
+
+void VsAudio::setOutputDevice([[maybe_unused]] const QString& device)
+{
+    if (!getUseRtAudio())
+        return;
+#ifdef RT_AUDIO
+    if (device == m_outputDevice)
+        return;
+    m_outputDevice = device;
+    emit outputDeviceChanged(m_outputDevice);
+#endif
+}
+
+void VsAudio::setDevicesErrorMsg(const QString& msg)
+{
+    if (m_devicesErrorMsg == msg)
+        return;
+    m_devicesErrorMsg = msg;
+    emit devicesErrorChanged();
+}
+
+void VsAudio::setDevicesWarningMsg(const QString& msg)
+{
+    if (m_devicesWarningMsg == msg)
+        return;
+    m_devicesWarningMsg = msg;
+    emit devicesWarningChanged();
+}
+
+void VsAudio::setDevicesErrorHelpUrl(const QString& url)
+{
+    if (m_devicesErrorHelpUrl == url)
+        return;
+    m_devicesErrorHelpUrl = url;
+    emit devicesErrorHelpUrlChanged();
+}
+
+void VsAudio::setDevicesWarningHelpUrl(const QString& url)
+{
+    if (m_devicesWarningHelpUrl == url)
+        return;
+    m_devicesWarningHelpUrl = url;
+    emit devicesWarningHelpUrlChanged();
+}
+
+void VsAudio::setHighLatencyFlag(bool highLatencyFlag)
+{
+    if (m_highLatencyFlag == highLatencyFlag)
+        return;
+    m_highLatencyFlag = highLatencyFlag;
+    emit highLatencyFlagChanged(highLatencyFlag);
+}
+
+void VsAudio::startAudio(bool block)
+{
+    // note this is also used for restartAudio()
+    emit signalStartAudio();
+    if (!block)
+        return;
+    WaitForSignal(this, &VsAudio::signalAudioIsReady);
+}
+
+void VsAudio::stopAudio(bool block)
+{
+    if (!getAudioReady())
+        return;
+    emit signalStopAudio();
+    if (!block)
+        return;
+    WaitForSignal(this, &VsAudio::signalAudioIsNotReady);
+}
+
+void VsAudio::refreshDevices(bool block)
+{
+    if (!getUseRtAudio())
+        return;
+    emit signalRefreshDevices();
+    if (!block)
+        return;
+    WaitForSignal(m_audioWorkerPtr.get(), &VsAudioWorker::signalDevicesValidated);
+}
+
+void VsAudio::validateDevices(bool block)
+{
+    if (!getUseRtAudio())
+        return;
+    emit signalValidateDevices();
+    if (!block)
+        return;
+    WaitForSignal(m_audioWorkerPtr.get(), &VsAudioWorker::signalDevicesValidated);
+}
+
+void VsAudio::loadSettings()
+{
+    QSettings settings;
+    settings.beginGroup(QStringLiteral("Audio"));
+    setInputVolume(settings.value(QStringLiteral("InMultiplier"), 1).toFloat());
+    setOutputVolume(settings.value(QStringLiteral("OutMultiplier"), 1).toFloat());
+    setMonitorVolume(settings.value(QStringLiteral("MonMultiplier"), 0).toFloat());
+    // note: we should always reset input muted to false; otherwise, bad things
+    setInputMuted(false);
+    // setInputMuted(settings.value(QStringLiteral("InMuted"), false).toBool());
+
+    // load audio backend
+    AudioBackendType audioBackend;
+    if constexpr (isBackendAvailable<AudioInterfaceMode::ALL>()) {
+        audioBackend =
+            (settings.value(QStringLiteral("Backend"), AudioBackendType::RTAUDIO).toInt()
+             == 1)
+                ? AudioBackendType::RTAUDIO
+                : AudioBackendType::JACK;
+    } else if constexpr (isBackendAvailable<AudioInterfaceMode::RTAUDIO>()) {
+        audioBackend = AudioBackendType::RTAUDIO;
+    } else {
+        audioBackend = AudioBackendType::JACK;
+    }
+#ifdef USE_WEAK_JACK
+    // Check if Jack is available
+    if (have_libjack() != 0) {
+#ifdef RT_AUDIO
+        audioBackend = AudioBackendType::RTAUDIO;
+#else
+        // TODO: Handle this more gracefully, even if it's an unlikely scenario
+        qFatal("JACK not found and not built with RtAudio support.");
+#endif  // RT_AUDIO
+    }
+#endif  // USE_WEAK_JACK
+    if (audioBackend != m_backend) {
+        setAudioBackend(audioBackend == AudioBackendType::RTAUDIO
+                            ? QStringLiteral("RtAudio")
+                            : QStringLiteral("JACK"));
+    }
+
+    // load input and output devices
+    QString inputDevice  = settings.value(QStringLiteral("InputDevice"), "").toString();
+    QString outputDevice = settings.value(QStringLiteral("OutputDevice"), "").toString();
+    if (inputDevice == QStringLiteral("(default)")) {
+        inputDevice = "";
+    }
+    if (outputDevice == QStringLiteral("(default)")) {
+        outputDevice = "";
+    }
+    setInputDevice(inputDevice);
+    setOutputDevice(outputDevice);
+
+    // use default base channel 0, if the setting does not exist
+    setBaseInputChannel(settings.value(QStringLiteral("BaseInputChannel"), 0).toInt());
+    setBaseOutputChannel(settings.value(QStringLiteral("BaseOutputChannel"), 0).toInt());
+
+    // Handle migration scenarios. Assume this is a new user
+    // if we have m_inputDevice == "" and m_outputDevice == ""
+    if (m_inputDevice == "" && m_outputDevice == "") {
+        // for fresh installs, use mono by default
+        setNumInputChannels(
+            settings.value(QStringLiteral("NumInputChannels"), 1).toInt());
+        setInputMixMode(settings
+                            .value(QStringLiteral("InputMixMode"),
+                                   static_cast<int>(AudioInterface::MONO))
+                            .toInt());
+
+        // use 2 channels for output
+        setNumOutputChannels(
+            settings.value(QStringLiteral("NumOutputChannels"), 2).toInt());
+    } else {
+        // existing installs - keep using stereo
+        setNumInputChannels(
+            settings.value(QStringLiteral("NumInputChannels"), 2).toInt());
+        setInputMixMode(settings
+                            .value(QStringLiteral("InputMixMode"),
+                                   static_cast<int>(AudioInterface::STEREO))
+                            .toInt());
+
+        // use 2 channels for output
+        setNumOutputChannels(
+            settings.value(QStringLiteral("NumOutputChannels"), 2).toInt());
+    }
+
+    setBufferSize(settings.value(QStringLiteral("BufferSize"), 128).toInt());
+    setBufferStrategy(settings.value(QStringLiteral("BufferStrategy"), 2).toInt());
+    setFeedbackDetectionEnabled(
+        settings.value(QStringLiteral("FeedbackDetectionEnabled"), true).toBool());
+    settings.endGroup();
+}
+
+void VsAudio::saveSettings()
+{
+    QSettings settings;
+    settings.beginGroup(QStringLiteral("Audio"));
+    settings.setValue(QStringLiteral("InMultiplier"), m_inMultiplier);
+    settings.setValue(QStringLiteral("OutMultiplier"), m_outMultiplier);
+    settings.setValue(QStringLiteral("MonMultiplier"), m_monMultiplier);
+    // settings.setValue(QStringLiteral("InMuted"), m_inMuted);
+    settings.setValue(QStringLiteral("Backend"), getUseRtAudio() ? 1 : 0);
+    settings.setValue(QStringLiteral("InputDevice"), m_inputDevice);
+    settings.setValue(QStringLiteral("OutputDevice"), m_outputDevice);
+    settings.setValue(QStringLiteral("BaseInputChannel"), m_baseInputChannel);
+    settings.setValue(QStringLiteral("NumInputChannels"), m_numInputChannels);
+    settings.setValue(QStringLiteral("InputMixMode"), m_inputMixMode);
+    settings.setValue(QStringLiteral("BaseOutputChannel"), m_baseOutputChannel);
+    settings.setValue(QStringLiteral("NumOutputChannels"), m_numOutputChannels);
+    settings.setValue(QStringLiteral("BufferSize"), m_audioBufferSize);
+    settings.setValue(QStringLiteral("BufferStrategy"), m_bufferStrategy);
+    settings.setValue(QStringLiteral("FeedbackDetectionEnabled"),
+                      m_feedbackDetectionEnabled);
+    settings.endGroup();
+}
+
+void VsAudio::detectedFeedbackLoop()
+{
+    setInputMuted(true);
+    setMonitorVolume(0);
+    emit feedbackDetected();
+}
+
+void VsAudio::updatedInputVuMeasurements(const float* valuesInDecibels, int numChannels)
+{
+    bool detectedClip = false;
+
+    // Always output 2 meter readings to the UI
+    for (int i = 0; i < 2; i++) {
+        // Determine decibel reading
+        float dB = m_meterMin;
+        if (i < numChannels) {
+            dB = std::max(m_meterMin, valuesInDecibels[i]);
+        }
+
+        // Produce a normalized value from 0 to 1
+        m_inputMeterLevels[i] = (dB - m_meterMin) / (m_meterMax - m_meterMin);
+
+        // Signal a clip if we haven't done so already
+        if (dB >= -0.05 && !detectedClip) {
+            m_inputClipTimer.start();
+            m_inputClipped = true;
+            emit updatedInputClipped(m_inputClipped);
+            detectedClip = true;
+        }
+    }
+
+#ifdef RT_AUDIO
+    // For certain specific cases, copy the first channel's value into the second
+    // channel's value
+    if (getUseRtAudio()
+        && ((m_inputMixMode == static_cast<int>(AudioInterface::MONO)
+             && m_numInputChannels == 1)
+            || (m_inputMixMode == static_cast<int>(AudioInterface::MIXTOMONO)
+                && m_numInputChannels == 2))) {
+        m_inputMeterLevels[1] = m_inputMeterLevels[0];
+    }
+#endif
+
+    emit updatedInputMeterLevels(m_inputMeterLevels);
+}
+
+void VsAudio::updatedOutputVuMeasurements(const float* valuesInDecibels, int numChannels)
+{
+    bool detectedClip = false;
+
+    // Always output 2 meter readings to the UI
+    for (int i = 0; i < 2; i++) {
+        // Determine decibel reading
+        float dB = m_meterMin;
+        if (i < numChannels) {
+            dB = std::max(m_meterMin, valuesInDecibels[i]);
+        }
+
+        // Produce a normalized value from 0 to 1
+        m_outputMeterLevels[i] = (dB - m_meterMin) / (m_meterMax - m_meterMin);
+
+        // Signal a clip if we haven't done so already
+        if (dB >= -0.05 && !detectedClip) {
+            m_outputClipTimer.start();
+            m_outputClipped = true;
+            emit updatedOutputClipped(m_outputClipped);
+            detectedClip = true;
+        }
+    }
+#ifdef RT_AUDIO
+    if (m_numOutputChannels == 1) {
+        m_outputMeterLevels[1] = m_outputMeterLevels[0];
+    }
+#endif
+    emit updatedOutputMeterLevels(m_outputMeterLevels);
+}
+
+void VsAudio::appendProcessPlugins(AudioInterface& audioInterface, bool forJackTrip,
+                                   int numInputChannels, int numOutputChannels)
+{
+    // Make sure clip timers are stopped
+    m_inputClipTimer.stop();
+    m_outputClipTimer.stop();
+
+    // Reset meters
+    m_inputMeterLevels[0] = m_inputMeterLevels[1] = 0;
+    m_outputMeterLevels[0] = m_outputMeterLevels[1] = 0;
+    m_inputClipped = m_outputClipped = false;
+    emit updatedInputMeterLevels(m_inputMeterLevels);
+    emit updatedOutputMeterLevels(m_outputMeterLevels);
+    emit updatedInputClipped(m_inputClipped);
+    emit updatedOutputClipped(m_outputClipped);
+    setInputMuted(false);
+
+    // Create plugins
+    m_inputMeterPluginPtr   = new Meter(numInputChannels);
+    m_outputMeterPluginPtr  = new Meter(numOutputChannels);
+    m_inputVolumePluginPtr  = new Volume(numInputChannels);
+    m_outputVolumePluginPtr = new Volume(numOutputChannels);
+
+    // initialize input and output volumes
+    m_outputVolumePluginPtr->volumeUpdated(m_outMultiplier);
+    m_inputVolumePluginPtr->volumeUpdated(m_inMultiplier);
+    m_inputVolumePluginPtr->muteUpdated(m_inMuted);
+
+    // Connect plugins for communication with UI
+    connect(m_inputMeterPluginPtr, &Meter::onComputedVolumeMeasurements, this,
+            &VsAudio::updatedInputVuMeasurements);
+    connect(m_outputMeterPluginPtr, &Meter::onComputedVolumeMeasurements, this,
+            &VsAudio::updatedOutputVuMeasurements);
+    connect(this, &VsAudio::updatedInputVolume, m_inputVolumePluginPtr,
+            &Volume::volumeUpdated);
+    connect(this, &VsAudio::updatedOutputVolume, m_outputVolumePluginPtr,
+            &Volume::volumeUpdated);
+    connect(this, &VsAudio::updatedInputMuted, m_inputVolumePluginPtr,
+            &Volume::muteUpdated);
+
+    // Note that plugin ownership is passed to the JackTrip class
+    // In particular, the AudioInterface that it uses to connect
+    audioInterface.appendProcessPluginToNetwork(m_inputVolumePluginPtr);
+    audioInterface.appendProcessPluginToNetwork(m_inputMeterPluginPtr);
+
+    if (forJackTrip) {
+        // plugins for stream going to audio interface
+        audioInterface.appendProcessPluginFromNetwork(m_outputVolumePluginPtr);
+
+        // Setup monitor
+        // Note: Constructor determines how many internal monitor buffers to allocate
+        m_monitorPluginPtr = new Monitor(std::max(numInputChannels, numOutputChannels));
+        m_monitorPluginPtr->volumeUpdated(m_monMultiplier);
+        audioInterface.appendProcessPluginToMonitor(m_monitorPluginPtr);
+        connect(this, &VsAudio::updatedMonitorVolume, m_monitorPluginPtr,
+                &Monitor::volumeUpdated);
+
+#ifndef NO_FEEDBACK
+        // Setup output analyzer
+        if (m_feedbackDetectionEnabled) {
+            m_outputAnalyzerPluginPtr = new Analyzer(numOutputChannels);
+            m_outputAnalyzerPluginPtr->setIsMonitoringAnalyzer(true);
+            audioInterface.appendProcessPluginToMonitor(m_outputAnalyzerPluginPtr);
+            connect(m_outputAnalyzerPluginPtr, &Analyzer::signalFeedbackDetected, this,
+                    &VsAudio::detectedFeedbackLoop);
+        }
+#endif
+
+        // Setup output meter
+        // Note: Add this to monitor process to include self-volume
+        m_outputMeterPluginPtr->setIsMonitoringMeter(true);
+        audioInterface.appendProcessPluginToMonitor(m_outputMeterPluginPtr);
+
+    } else {
+        // tone plugin is used to test audio output
+        Tone* outputTonePluginPtr = new Tone(getNumOutputChannels());
+        connect(this, &VsAudio::signalPlayOutputAudio, outputTonePluginPtr,
+                &Tone::triggerPlayback);
+        audioInterface.appendProcessPluginFromNetwork(outputTonePluginPtr);
+
+        // plugins for stream going to audio interface
+        audioInterface.appendProcessPluginFromNetwork(m_outputVolumePluginPtr);
+        audioInterface.appendProcessPluginFromNetwork(m_outputMeterPluginPtr);
+    }
+}
+
+void VsAudio::setDeviceModels(QJsonArray inputComboModel, QJsonArray outputComboModel)
+{
+    m_inputComboModel  = inputComboModel;
+    m_outputComboModel = outputComboModel;
+    emit inputComboModelChanged();
+    emit outputComboModelChanged();
+    if (!m_deviceModelsInitialized) {
+        m_deviceModelsInitialized = true;
+        emit deviceModelsInitializedChanged(true);
+    }
+}
+
+void VsAudio::setInputChannelsComboModel(QJsonArray& model)
+{
+    m_inputChannelsComboModel = model;
+    emit inputChannelsComboModelChanged();
+}
+
+void VsAudio::setOutputChannelsComboModel(QJsonArray& model)
+{
+    m_outputChannelsComboModel = model;
+    emit outputChannelsComboModelChanged();
+}
+
+void VsAudio::setInputMixModeComboModel(QJsonArray& model)
+{
+    m_inputMixModeComboModel = model;
+    emit inputMixModeComboModelChanged();
+}
+
+void VsAudio::updateDeviceMessages(AudioInterface& audioInterface)
+{
+    QString devicesWarningMsg =
+        QString::fromStdString(audioInterface.getDevicesWarningMsg());
+    QString devicesErrorMsg = QString::fromStdString(audioInterface.getDevicesErrorMsg());
+    QString devicesWarningHelpUrl =
+        QString::fromStdString(audioInterface.getDevicesWarningHelpUrl());
+    QString devicesErrorHelpUrl =
+        QString::fromStdString(audioInterface.getDevicesErrorHelpUrl());
+
+    if (devicesWarningMsg != "") {
+        qDebug() << "Devices Warning: " << devicesWarningMsg;
+        if (devicesWarningHelpUrl != "") {
+            qDebug() << "Learn More: " << devicesWarningHelpUrl;
+        }
+    }
+
+    if (devicesErrorMsg != "") {
+        qDebug() << "Devices Error: " << devicesErrorMsg;
+        if (devicesErrorHelpUrl != "") {
+            qDebug() << "Learn More: " << devicesErrorHelpUrl;
+        }
+    }
+
+    setDevicesWarningMsg(devicesWarningMsg);
+    setDevicesErrorMsg(devicesErrorMsg);
+    setDevicesWarningHelpUrl(devicesWarningHelpUrl);
+    setDevicesErrorHelpUrl(devicesErrorHelpUrl);
+    setHighLatencyFlag(audioInterface.getHighLatencyFlag());
+}
+
+AudioInterface* VsAudio::newAudioInterface(JackTrip* jackTripPtr)
+{
+    AudioInterface* ifPtr = nullptr;
+
+    // Create AudioInterface Client Object
+    if (isBackendAvailable<AudioInterfaceMode::ALL>() && jackIsAvailable()) {
+        // all backends area available
+        if (m_backend == VsAudio::AudioBackendType::JACK) {
+            qDebug() << "Using JACK backend";
+            ifPtr = newJackAudioInterface(jackTripPtr);
+        } else {
+            qDebug() << "Using RtAudio backend";
+            ifPtr = newRtAudioInterface(jackTripPtr);
+        }
+    } else if (isBackendAvailable<AudioInterfaceMode::RTAUDIO>()) {
+        // only RtAudio backend is available
+        if (m_backend != VsAudio::AudioBackendType::RTAUDIO) {
+            // A different backend was requested
+            qDebug()
+                << "Switching audio backend to RtAudio since nothing else is available";
+            setAudioBackend(QStringLiteral("RtAudio"));
+        } else {
+            qDebug() << "Using RtAudio backend";
+        }
+        ifPtr = newRtAudioInterface(jackTripPtr);
+    } else if (m_backend == VsAudio::AudioBackendType::JACK && jackIsAvailable()) {
+        // only Jack backend is available
+        if (m_backend != VsAudio::AudioBackendType::JACK) {
+            // A different backend was requested
+            qDebug() << "Switching audio backend to Jack since nothing else is available";
+            setAudioBackend(QStringLiteral("JACK"));
+        } else {
+            qDebug() << "Using JACK backend";
+        }
+        ifPtr = newJackAudioInterface(jackTripPtr);
+    } else {
+        // no backends are available!
+        throw std::runtime_error(
+            "JackTrip was compiled without RtAudio and can't find JACK. "
+            "In order to use JackTrip, you'll need to install JACK or "
+            "rebuild with RtAudio support.");
+        std::exit(1);
+    }
+
+    if (ifPtr == nullptr)
+        return ifPtr;
+
+    // AudioInterface::setup() can return a different buffer size
+    // if the audio interface doesn't support the one that was requested
+    if (ifPtr->getBufferSizeInSamples() != uint32_t(getBufferSize())) {
+        setBufferSize(ifPtr->getBufferSizeInSamples());
+    }
+
+    std::cout << "The Sampling Rate is: " << m_sampleRate << std::endl;
+    std::cout << gPrintSeparator << std::endl;
+    int AudioBufferSizeInBytes = ifPtr->getBufferSizeInSamples() * sizeof(sample_t);
+    std::cout << "The Audio Buffer Size is: " << ifPtr->getBufferSizeInSamples()
+              << " samples" << std::endl;
+    std::cout << "                      or: " << AudioBufferSizeInBytes << " bytes"
+              << std::endl;
+    std::cout << gPrintSeparator << std::endl;
+    std::cout << "The Number of Channels is: " << ifPtr->getNumInputChannels()
+              << std::endl;
+    std::cout << gPrintSeparator << std::endl;
+
+    // setup audio plugins
+    appendProcessPlugins(*ifPtr, jackTripPtr != nullptr, getNumInputChannels(),
+                         getNumOutputChannels());
+
+    return ifPtr;
+}
+
+AudioInterface* VsAudio::newJackAudioInterface([[maybe_unused]] JackTrip* jackTripPtr)
+{
+    static const int numJackChannels = 2;
+    AudioInterface* ifPtr            = nullptr;
+#ifndef NO_JACK
+    if constexpr (isBackendAvailable<AudioInterfaceMode::ALL>()
+                  || isBackendAvailable<AudioInterfaceMode::JACK>()) {
+        QVarLengthArray<int> inputChans;
+        QVarLengthArray<int> outputChans;
+        inputChans.resize(numJackChannels);
+        outputChans.resize(numJackChannels);
+
+        for (int i = 0; i < numJackChannels; i++) {
+            inputChans[i] = 1 + i;
+        }
+        for (int i = 0; i < numJackChannels; i++) {
+            outputChans[i] = 1 + i;
+        }
+
+        ifPtr = new JackAudioInterface(inputChans, outputChans, m_audioBitResolution,
+                                       jackTripPtr != nullptr, jackTripPtr);
+        ifPtr->setClientName(QStringLiteral("JackTrip"));
+        ifPtr->setup(true);
+
+        m_sampleRate = ifPtr->getSampleRate();
+    }
+#endif
+    return ifPtr;
+}
+
+AudioInterface* VsAudio::newRtAudioInterface([[maybe_unused]] JackTrip* jackTripPtr)
+{
+    AudioInterface* ifPtr = nullptr;
+#ifdef RT_AUDIO
+    QVarLengthArray<int> inputChans;
+    QVarLengthArray<int> outputChans;
+    inputChans.resize(getNumInputChannels());
+    outputChans.resize(getNumOutputChannels());
+
+    for (int i = 0; i < getNumInputChannels(); i++) {
+        inputChans[i] = getBaseInputChannel() + i;
+    }
+    for (int i = 0; i < getNumOutputChannels(); i++) {
+        outputChans[i] = getBaseOutputChannel() + i;
+    }
+
+    ifPtr = new RtAudioInterface(
+        inputChans, outputChans,
+        static_cast<AudioInterface::inputMixModeT>(getInputMixMode()),
+        m_audioBitResolution, jackTripPtr != nullptr, jackTripPtr);
+    ifPtr->setSampleRate(m_sampleRate);
+    ifPtr->setInputDevice(getInputDevice().toStdString());
+    ifPtr->setOutputDevice(getOutputDevice().toStdString());
+    ifPtr->setBufferSizeInSamples(getBufferSize());
+
+    QVector<RtAudioDevice> devices = m_audioWorkerPtr->getDevices();
+    if (!devices.empty())
+        static_cast<RtAudioInterface*>(ifPtr)->setRtAudioDevices(devices);
+
+    // Note: setup might change the number of channels and/or buffer size
+    ifPtr->setup(true);
+
+    // TODO: Add check for if base input channel needs to change
+    if (jackTripPtr != nullptr && getNumInputChannels() == 2
+        && getInputMixMode() == AudioInterface::MIXTOMONO)
+        jackTripPtr->setNumInputChannels(1);
+
+#endif  // RT_AUDIO
+    return ifPtr;
+}
+
+// VsAudioWorker methods
+
+VsAudioWorker::VsAudioWorker(VsAudio* ptr) : m_parentPtr(ptr) {}
+
+void VsAudioWorker::openAudioInterface()
+{
+#ifdef __APPLE__
+    if (m_parentPtr->m_permissionsPtr->micPermission() != "granted") {
+        return;
+    }
+#endif
+
+    if constexpr (!(isBackendAvailable<AudioInterfaceMode::JACK>()
+                    || isBackendAvailable<AudioInterfaceMode::RTAUDIO>())) {
+        return;
+    }
+
+    if (!m_audioInterfacePtr.isNull()) {
+        std::cout << "Restarting Audio" << std::endl;
+        closeAudioInterface();
+    } else {
+        std::cout << "Starting Audio" << std::endl;
+    }
+
+    unsigned int maxTries = 2;
+#ifdef RT_AUDIO
+    // Update devices, if not already initialized
+    if (getUseRtAudio()) {
+        if (!m_parentPtr->getDeviceModelsInitialized()) {
+            updateDeviceModels();
+            maxTries = 1;
+        }
+    }
+#endif
+    for (unsigned int tryNum = 0; tryNum < maxTries; ++tryNum) {
+#ifdef RT_AUDIO
+        if (tryNum > 0) {
+            if (getUseRtAudio()) {
+                updateDeviceModels();
+            } else {
+                m_parentPtr->setAudioBackend("RtAudio");
+            }
+        }
+#endif
+        try {
+            // create and setup a new audio interface
+            m_audioInterfacePtr.reset(m_parentPtr->newAudioInterface());
+            // success if it doesn't throw
+            break;
+        } catch (const std::exception& e) {
+            emit signalError(QString::fromUtf8(e.what()));
+        }
+    }
+
+    if (m_audioInterfacePtr.isNull()) {
+        emit signalError(QStringLiteral("Failed to initialize audio interface"));
+        return;
+    }
+
+    // initialize plugins and start the audio callback process
+    m_audioInterfacePtr->initPlugins(false);
+    m_audioInterfacePtr->startProcess();
+
+    if (m_parentPtr->m_backend == VsAudio::AudioBackendType::JACK) {
+        // this crashes on windows
+#ifndef _WIN32
+        m_audioInterfacePtr->connectDefaultPorts();
+#endif
+    }
+
+    m_parentPtr->updateDeviceMessages(*m_audioInterfacePtr);
+    m_parentPtr->setAudioReady(true);
+}
+
+void VsAudioWorker::closeAudioInterface()
+{
+    if (m_audioInterfacePtr.isNull())
+        return;
+    std::cout << "Stopping Audio" << std::endl;
+    try {
+        m_audioInterfacePtr->stopProcess();
+    } catch (const std::exception& e) {
+        emit signalError(QString::fromUtf8(e.what()));
+    }
+    m_audioInterfacePtr.clear();
+    m_parentPtr->setAudioReady(false);
+}
+
+#ifdef RT_AUDIO
+
+void VsAudioWorker::refreshDevices()
+{
+    if (!getUseRtAudio())
+        return;
+    bool restartAudio = !m_audioInterfacePtr.isNull();
+    if (restartAudio)
+        closeAudioInterface();
+    updateDeviceModels();
+    if (restartAudio)
+        openAudioInterface();
+}
+
+void VsAudioWorker::updateDeviceModels()
+{
+    if (!getUseRtAudio())
+        return;
+
+    // note: audio must not be active when scanning devices
+    m_parentPtr->setScanningDevices(true);
+    closeAudioInterface();
+    RtAudioInterface::scanDevices(m_devices);
+
+    QStringList inputDeviceCategories;
+    QStringList outputDeviceCategories;
+
+    getDeviceList(m_devices, m_inputDeviceList, inputDeviceCategories,
+                  m_inputDeviceChannels, true);
+    getDeviceList(m_devices, m_outputDeviceList, outputDeviceCategories,
+                  m_outputDeviceChannels, false);
+
+    QJsonArray inputComboModel =
+        formatDeviceList(m_inputDeviceList, inputDeviceCategories, m_inputDeviceChannels);
+    QJsonArray outputComboModel = formatDeviceList(
+        m_outputDeviceList, outputDeviceCategories, m_outputDeviceChannels);
+
+    validateDevices();
+
+    // let VsAudio know that things have been updated
+    m_parentPtr->setScanningDevices(false);
+    emit signalUpdatedDeviceModels(inputComboModel, outputComboModel);
+}
+
+void VsAudioWorker::getDeviceList(const QVector<RtAudioDevice>& devices,
+                                  QStringList& list, QStringList& categories,
+                                  QList<int>& channels, bool isInput)
+{
+    categories.clear();
+    channels.clear();
+    list.clear();
+
+    // do not include blacklisted audio interfaces
+    // these are known to be unstable and cause JackTrip to crash
+    QVector<QString> blacklisted_devices = {
+#ifdef _WIN32
+        // Realtek ASIO: seems to crash any computer that tries to use it
+        QString::fromUtf8("Realtek ASIO"),
+#endif
+        // JackRouter: crashes if not running; use Jack backend instead
+        QString::fromUtf8("JackRouter"),
+    };
+
+    for (int n = 0; n < devices.size(); ++n) {
+#ifdef _WIN32
+        if (devices[n].api == RtAudio::UNIX_JACK) {
+            continue;
+        }
+#endif
+        const QString deviceName(QString::fromStdString(devices[n].name));
+
+        // Don't include duplicate entries
+        if (list.contains(deviceName)) {
+            continue;
+        }
+
+        // Skip if no channels available
+        if ((isInput && devices[n].inputChannels == 0)
+            || (!isInput && devices[n].outputChannels == 0)) {
+            continue;
+        }
+
+        // Skip blacklisted devices
+        if (blacklisted_devices.contains(deviceName)) {
+            std::cout << "RTAudio: blacklisted " << (isInput ? "input" : "output")
+                      << " device: " << devices[n].name << std::endl;
+            continue;
+        }
+
+        // Good to go!
+        if (isInput) {
+            list.append(deviceName);
+            channels.append(devices[n].inputChannels);
+        } else {
+            list.append(deviceName);
+            channels.append(devices[n].outputChannels);
+        }
+
+#ifdef _WIN32
+        switch (devices[n].api) {
+        case RtAudio::WINDOWS_ASIO:
+            categories.append("Low-Latency (ASIO)");
+            break;
+        case RtAudio::WINDOWS_WASAPI:
+            categories.append("High-Latency (Non-ASIO)");
+            break;
+        case RtAudio::WINDOWS_DS:
+            categories.append("High-Latency (Non-ASIO)");
+            break;
+        default:
+            categories.append("");
+            break;
+        }
+#else
+        categories.append("");
+#endif
+    }
+}
+
+QJsonArray VsAudioWorker::formatDeviceList(const QStringList& devices,
+                                           const QStringList& categories,
+                                           const QList<int>& channels)
+{
+    QStringList uniqueCategories = QStringList(categories);
+    uniqueCategories.removeDuplicates();
+
+    bool containsCategories = true;
+    if (uniqueCategories.size() == 0) {
+        containsCategories = false;
+    } else if (uniqueCategories.size() == 1 && uniqueCategories.at(0) == "") {
+        containsCategories = false;
+    }
+
+    QJsonArray items;
+    for (int i = 0; i < uniqueCategories.size(); i++) {
+        QString category = uniqueCategories.at(i);
+
+        if (containsCategories) {
+            QJsonObject header = QJsonObject();
+            header.insert(QString::fromStdString("text"), category);
+            header.insert(QString::fromStdString("type"),
+                          QString::fromStdString("header"));
+            header.insert(QString::fromStdString("category"), category);
+            items.push_back(header);
+        }
+
+        for (int j = 0; j < devices.size(); j++) {
+            if (categories.at(j).toStdString() == category.toStdString()) {
+                QJsonObject element = QJsonObject();
+                element.insert(QString::fromStdString("text"), devices.at(j));
+                element.insert(QString::fromStdString("type"),
+                               QString::fromStdString("element"));
+                element.insert(QString::fromStdString("channels"), channels.at(j));
+                element.insert(QString::fromStdString("category"), category);
+                items.push_back(element);
+            }
+        }
+    }
+
+    return items;
+}
+
+void VsAudioWorker::validateDevices()
+{
+    if (!getUseRtAudio())
+        return;
+    validateInputDevicesState();
+    validateOutputDevicesState();
+    emit signalDevicesValidated();
+}
+
+void VsAudioWorker::validateInputDevicesState()
+{
+    if (!getUseRtAudio()) {
+        return;
+    }
+    if (m_inputDeviceList.size() == 0 || m_outputDeviceList.size() == 0) {
+        return;
+    }
+
+    // Given input device list, check that the currently set device
+    // actually exists
+    if (getInputDevice() == QStringLiteral("")
+        || m_inputDeviceList.indexOf(getInputDevice()) == -1) {
+        m_parentPtr->setInputDevice(m_inputDeviceList[0]);
+    }
+
+    // Given the currently selected input device, reset the available input channel
+    // options
+    int indexOfInput = m_inputDeviceList.indexOf(getInputDevice());
+    if (indexOfInput == -1) {
+        std::cerr << "Invalid state. Input device index should never be -1" << std::endl;
+        return;
+    }
+
+    int numDevicesChannelsAvailable = m_inputDeviceChannels.at(indexOfInput);
+    if (numDevicesChannelsAvailable < 1) {
+        std::cerr << "Invalid state. Number of channels should never be less than 1"
+                  << std::endl;
+        return;
+    } else if (numDevicesChannelsAvailable == 1) {
+        // Set the input mix mode to just have "Mono" as the option
+        QJsonObject inputMixModeComboElement = QJsonObject();
+        inputMixModeComboElement.insert(QString::fromStdString("label"),
+                                        QString::fromStdString("Mono"));
+        inputMixModeComboElement.insert(QString::fromStdString("value"),
+                                        static_cast<int>(AudioInterface::MONO));
+        QJsonArray inputMixModeComboModel;
+        inputMixModeComboModel.push_back(inputMixModeComboElement);
+        m_parentPtr->setInputMixModeComboModel(inputMixModeComboModel);
+
+        // Set the input channels combo to only have channel 1 as an option
+        QJsonObject inputChannelsComboElement;
+        inputChannelsComboElement.insert(QString::fromStdString("label"),
+                                         QString::fromStdString("1"));
+        inputChannelsComboElement.insert(QString::fromStdString("baseChannel"),
+                                         QVariant(0).toInt());
+        inputChannelsComboElement.insert(QString::fromStdString("numChannels"),
+                                         QVariant(1).toInt());
+        QJsonArray inputChannelsComboModel;
+        inputChannelsComboModel.push_back(inputChannelsComboElement);
+        m_parentPtr->setInputChannelsComboModel(inputChannelsComboModel);
+
+        // Set the only allowed options for these variables automatically
+        m_parentPtr->setBaseInputChannel(0);
+        m_parentPtr->setNumInputChannels(1);
+        m_parentPtr->setInputMixMode(static_cast<int>(AudioInterface::MONO));
+    } else {
+        // set the input channels selector to have the options based on the currently
+        // selected device
+        QJsonArray inputChannelsComboModel;
+        for (int i = 0; i < numDevicesChannelsAvailable; i++) {
+            QJsonObject element = QJsonObject();
+            element.insert(QString::fromStdString("label"), QVariant(i + 1).toString());
+            element.insert(QString::fromStdString("baseChannel"), QVariant(i).toInt());
+            element.insert(QString::fromStdString("numChannels"), QVariant(1).toInt());
+            inputChannelsComboModel.push_back(element);
+        }
+        for (int i = 0; i < numDevicesChannelsAvailable; i++) {
+            if (i % 2 == 0) {
+                QJsonObject element = QJsonObject();
+                element.insert(
+                    QString::fromStdString("label"),
+                    QVariant(i + 1).toString() + " & " + QVariant(i + 2).toString());
+                element.insert(QString::fromStdString("baseChannel"),
+                               QVariant(i).toInt());
+                element.insert(QString::fromStdString("numChannels"),
+                               QVariant(2).toInt());
+                inputChannelsComboModel.push_back(element);
+            }
+        }
+        m_parentPtr->setInputChannelsComboModel(inputChannelsComboModel);
+
+        // if the current m_baseInputChannel or m_numInputChannels is invalid based on
+        // this device's option, use the first two channels by default
+        if (getBaseInputChannel() + getNumInputChannels() > numDevicesChannelsAvailable) {
+            // we're in the case where numDevicesChannelsAvailable >= 2, so always have
+            // the ability to use the first 2 channels
+            m_parentPtr->setBaseInputChannel(0);
+            m_parentPtr->setNumInputChannels(2);
+        }
+        if (getNumInputChannels() != 1) {
+            // Set the input mix mode to have two options: "Stereo" and "Mix to Mono" if
+            // we're using 2 channels
+            QJsonObject inputMixModeComboElement1 = QJsonObject();
+            inputMixModeComboElement1.insert(QString::fromStdString("label"),
+                                             QString::fromStdString("Stereo"));
+            inputMixModeComboElement1.insert(QString::fromStdString("value"),
+                                             static_cast<int>(AudioInterface::STEREO));
+            QJsonObject inputMixModeComboElement2 = QJsonObject();
+            inputMixModeComboElement2.insert(QString::fromStdString("label"),
+                                             QString::fromStdString("Mix to Mono"));
+            inputMixModeComboElement2.insert(QString::fromStdString("value"),
+                                             static_cast<int>(AudioInterface::MIXTOMONO));
+            QJsonArray inputMixModeComboModel;
+            inputMixModeComboModel.push_back(inputMixModeComboElement1);
+            inputMixModeComboModel.push_back(inputMixModeComboElement2);
+            m_parentPtr->setInputMixModeComboModel(inputMixModeComboModel);
+
+            // if m_inputMixMode is an invalid value, set it to "stereo" by default
+            // given that we are using 2 channels
+            if (getInputMixMode() != static_cast<int>(AudioInterface::STEREO)
+                && getInputMixMode() != static_cast<int>(AudioInterface::MIXTOMONO)) {
+                m_parentPtr->setInputMixMode(static_cast<int>(AudioInterface::STEREO));
+            }
+        } else {
+            // Set the input mix mode to just have "Mono" as the option if we're using 1
+            // channel
+            QJsonObject inputMixModeComboElement = QJsonObject();
+            inputMixModeComboElement.insert(QString::fromStdString("label"),
+                                            QString::fromStdString("Mono"));
+            inputMixModeComboElement.insert(QString::fromStdString("value"),
+                                            static_cast<int>(AudioInterface::MONO));
+            QJsonArray inputMixModeComboModel;
+            inputMixModeComboModel.push_back(inputMixModeComboElement);
+            m_parentPtr->setInputMixModeComboModel(inputMixModeComboModel);
+
+            // if m_inputMixMode is an invalid value, set it to AudioInterface::MONO
+            if (getInputMixMode() != static_cast<int>(AudioInterface::MONO)) {
+                m_parentPtr->setInputMixMode(static_cast<int>(AudioInterface::MONO));
+            }
+        }
+    }
+}
+
+void VsAudioWorker::validateOutputDevicesState()
+{
+    if (!getUseRtAudio()) {
+        return;
+    }
+    if (m_outputDeviceList.size() == 0 || m_outputDeviceList.size() == 0) {
+        return;
+    }
+
+    // Given output device list, check that the currently set device
+    // actually exists
+    if (getOutputDevice() == QStringLiteral("")
+        || m_outputDeviceList.indexOf(getOutputDevice()) == -1) {
+        m_parentPtr->setOutputDevice(m_outputDeviceList[0]);
+    }
+
+    // Given the currently selected output device, reset the available output channel
+    // options
+    int indexOfOutput = m_outputDeviceList.indexOf(getOutputDevice());
+    if (indexOfOutput == -1) {
+        std::cerr << "Invalid state. Output device index should never be -1" << std::endl;
+        return;
+    }
+
+    int numDevicesChannelsAvailable = m_outputDeviceChannels.at(indexOfOutput);
+    if (numDevicesChannelsAvailable < 1) {
+        std::cerr << "Invalid state. Number of channels should never be less than 1"
+                  << std::endl;
+        return;
+    } else if (numDevicesChannelsAvailable == 1) {
+        // Set the output channels combo to only have channel 1 as an option
+        QJsonObject outputChannelsComboElement = QJsonObject();
+        outputChannelsComboElement.insert(QString::fromStdString("label"),
+                                          QString::fromStdString("1"));
+        outputChannelsComboElement.insert(QString::fromStdString("baseChannel"),
+                                          QVariant(0).toInt());
+        outputChannelsComboElement.insert(QString::fromStdString("numChannels"),
+                                          QVariant(1).toInt());
+        QJsonArray outputChannelsComboModel;
+        outputChannelsComboModel.push_back(outputChannelsComboElement);
+        m_parentPtr->setOutputChannelsComboModel(outputChannelsComboModel);
+
+        // Set the only allowed options for these variables automatically
+        m_parentPtr->setBaseOutputChannel(0);
+        m_parentPtr->setNumOutputChannels(1);
+    } else {
+        // set the output channels selector to have the options based on the currently
+        // selected device
+        QJsonArray outputChannelsComboModel;
+        for (int i = 0; i < numDevicesChannelsAvailable; i++) {
+            if (i % 2 == 0) {
+                QJsonObject element = QJsonObject();
+                element.insert(
+                    QString::fromStdString("label"),
+                    QVariant(i + 1).toString() + " & " + QVariant(i + 2).toString());
+                element.insert(QString::fromStdString("baseChannel"),
+                               QVariant(i).toInt());
+                element.insert(QString::fromStdString("numChannels"),
+                               QVariant(2).toInt());
+                outputChannelsComboModel.push_back(element);
+            }
+        }
+        m_parentPtr->setOutputChannelsComboModel(outputChannelsComboModel);
+
+        // if the current m_baseOutputChannel or m_numOutputChannels is invalid based on
+        // this device's option, use the first two channels by default
+        if (getBaseOutputChannel() + getNumOutputChannels()
+            > numDevicesChannelsAvailable) {
+            // we're in the case where numDevicesChannelsAvailable >= 2, so always have
+            // the ability to use the first 2 channels
+            m_parentPtr->setBaseOutputChannel(0);
+            m_parentPtr->setNumOutputChannels(2);
+        }
+    }
+}
+
+#endif  // RT_AUDIO
diff --git a/src/gui/vsAudio.h b/src/gui/vsAudio.h
new file mode 100644 (file)
index 0000000..5f37b67
--- /dev/null
@@ -0,0 +1,447 @@
+//*****************************************************************
+/*
+  JackTrip: A System for High-Quality Audio Network Performance
+  over the Internet
+
+  Copyright (c) 2008-2022 Juan-Pablo Caceres, Chris Chafe.
+  SoundWIRE group at CCRMA, Stanford University.
+
+  Permission is hereby granted, free of charge, to any person
+  obtaining a copy of this software and associated documentation
+  files (the "Software"), to deal in the Software without
+  restriction, including without limitation the rights to use,
+  copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the
+  Software is furnished to do so, subject to the following
+  conditions:
+
+  The above copyright notice and this permission notice shall be
+  included in all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+  OTHER DEALINGS IN THE SOFTWARE.
+*/
+//*****************************************************************
+
+/**
+ * \file vsAudio.h
+ * \author Matt Horton
+ * \date September 2022
+ */
+
+#ifndef VSDAUDIO_H
+#define VSDAUDIO_H
+
+#include <QJsonArray>
+#include <QList>
+#include <QObject>
+#include <QSharedPointer>
+#include <QString>
+#include <QStringList>
+#include <QTimer>
+
+#include "../AudioInterface.h"
+#include "../jacktrip_globals.h"
+
+#ifdef RT_AUDIO
+#include "../RtAudioInterface.h"
+#endif
+
+class Analyzer;
+class JackTrip;
+class Meter;
+class Monitor;
+class QThread;
+class Tone;
+class Volume;
+class VsPermissions;
+class VsAudioWorker;
+
+class VsAudio : public QObject
+{
+    Q_OBJECT
+
+    // state shared with QML
+    Q_PROPERTY(bool audioReady READ getAudioReady NOTIFY signalAudioReadyChanged)
+    Q_PROPERTY(bool scanningDevices READ getScanningDevices WRITE setScanningDevices
+                   NOTIFY signalScanningDevicesChanged)
+    Q_PROPERTY(bool feedbackDetectionEnabled READ getFeedbackDetectionEnabled WRITE
+                   setFeedbackDetectionEnabled NOTIFY feedbackDetectionEnabledChanged)
+    Q_PROPERTY(bool deviceModelsInitialized READ getDeviceModelsInitialized NOTIFY
+                   deviceModelsInitializedChanged)
+    Q_PROPERTY(bool backendAvailable READ backendAvailable CONSTANT)
+    Q_PROPERTY(QString audioBackend READ getAudioBackend WRITE setAudioBackend NOTIFY
+                   audioBackendChanged)
+    Q_PROPERTY(
+        int bufferSize READ getBufferSize WRITE setBufferSize NOTIFY bufferSizeChanged)
+    Q_PROPERTY(int bufferStrategy READ getBufferStrategy WRITE setBufferStrategy NOTIFY
+                   bufferStrategyChanged)
+    Q_PROPERTY(int numInputChannels READ getNumInputChannels WRITE setNumInputChannels
+                   NOTIFY numInputChannelsChanged)
+    Q_PROPERTY(int numOutputChannels READ getNumOutputChannels WRITE setNumOutputChannels
+                   NOTIFY numOutputChannelsChanged)
+    Q_PROPERTY(int baseInputChannel READ getBaseInputChannel WRITE setBaseInputChannel
+                   NOTIFY baseInputChannelChanged)
+    Q_PROPERTY(int baseOutputChannel READ getBaseOutputChannel WRITE setBaseOutputChannel
+                   NOTIFY baseOutputChannelChanged)
+    Q_PROPERTY(int inputMixMode READ getInputMixMode WRITE setInputMixMode NOTIFY
+                   inputMixModeChanged)
+    Q_PROPERTY(
+        bool inputMuted READ getInputMuted WRITE setInputMuted NOTIFY updatedInputMuted)
+    Q_PROPERTY(bool inputClipped READ getInputClipped NOTIFY updatedInputClipped)
+    Q_PROPERTY(bool outputClipped READ getOutputClipped NOTIFY updatedOutputClipped)
+    Q_PROPERTY(float inputVolume READ getInputVolume WRITE setInputVolume NOTIFY
+                   updatedInputVolume)
+    Q_PROPERTY(float outputVolume READ getOutputVolume WRITE setOutputVolume NOTIFY
+                   updatedOutputVolume)
+    Q_PROPERTY(float monitorVolume READ getMonitorVolume WRITE setMonitorVolume NOTIFY
+                   updatedMonitorVolume)
+    Q_PROPERTY(QString inputDevice READ getInputDevice WRITE setInputDevice NOTIFY
+                   inputDeviceChanged)
+    Q_PROPERTY(QString outputDevice READ getOutputDevice WRITE setOutputDevice NOTIFY
+                   outputDeviceChanged)
+    Q_PROPERTY(QVector<float> inputMeterLevels READ getInputMeterLevels NOTIFY
+                   updatedInputMeterLevels)
+    Q_PROPERTY(QVector<float> outputMeterLevels READ getOutputMeterLevels NOTIFY
+                   updatedOutputMeterLevels)
+    Q_PROPERTY(
+        QJsonArray inputComboModel READ getInputComboModel NOTIFY inputComboModelChanged)
+    Q_PROPERTY(QJsonArray outputComboModel READ getOutputComboModel NOTIFY
+                   outputComboModelChanged)
+    Q_PROPERTY(QJsonArray inputChannelsComboModel READ getInputChannelsComboModel NOTIFY
+                   inputChannelsComboModelChanged)
+    Q_PROPERTY(QJsonArray outputChannelsComboModel READ getOutputChannelsComboModel NOTIFY
+                   outputChannelsComboModelChanged)
+    Q_PROPERTY(QJsonArray inputMixModeComboModel READ getInputMixModeComboModel NOTIFY
+                   inputMixModeComboModelChanged)
+    Q_PROPERTY(QStringList feedbackDetectionComboModel READ getFeedbackDetectionComboModel
+                   CONSTANT)
+    Q_PROPERTY(QStringList bufferSizeComboModel READ getBufferSizeComboModel CONSTANT)
+    Q_PROPERTY(
+        QStringList bufferStrategyComboModel READ getBufferStrategyComboModel CONSTANT)
+    Q_PROPERTY(QStringList audioBackendComboModel READ getAudioBackendComboModel CONSTANT)
+    Q_PROPERTY(
+        QString devicesWarning READ getDevicesWarningMsg NOTIFY devicesWarningChanged)
+    Q_PROPERTY(QString devicesError READ getDevicesErrorMsg NOTIFY devicesErrorChanged)
+    Q_PROPERTY(QString devicesWarningHelpUrl READ getDevicesWarningHelpUrl NOTIFY
+                   devicesWarningHelpUrlChanged)
+    Q_PROPERTY(QString devicesErrorHelpUrl READ getDevicesErrorHelpUrl NOTIFY
+                   devicesErrorHelpUrlChanged)
+    Q_PROPERTY(bool highLatencyFlag READ getHighLatencyFlag NOTIFY highLatencyFlagChanged)
+
+   public:
+    enum AudioBackendType {
+        JACK = 0,  ///< Jack Mode
+        RTAUDIO    ///< RtAudio Mode
+    };
+
+    // Constructor
+    explicit VsAudio(QObject* parent = nullptr);
+    virtual ~VsAudio() {}
+
+    // allow VirtualStudio to get Permissions to bind to QML view
+    VsPermissions& getPermissions() { return *m_permissionsPtr; }
+    VsAudioWorker& getWorker() { return *m_audioWorkerPtr; }
+
+    //  allow VirtualStudio to create new audio interfaces
+    AudioInterface* newAudioInterface(JackTrip* jackTripPtr = nullptr);
+
+    // allow VirtualStudio to load and save settings
+    void loadSettings();
+    void saveSettings();
+
+    // getters for state shared with QML
+    bool backendAvailable() const;
+    bool jackIsAvailable() const;
+    bool getAudioReady() const { return m_audioReady; }
+    bool getScanningDevices() const { return m_scanningDevices; }
+    bool getFeedbackDetectionEnabled() const { return m_feedbackDetectionEnabled; }
+    bool getDeviceModelsInitialized() const { return m_deviceModelsInitialized; }
+    bool getUseRtAudio() const { return m_backend == AudioBackendType::RTAUDIO; }
+    QString getAudioBackend() const
+    {
+        return getUseRtAudio() ? QStringLiteral("RtAudio") : QStringLiteral("JACK");
+    }
+    int getBufferSize() const { return m_audioBufferSize; }
+    int getBufferStrategy() const { return m_bufferStrategy; }
+    int getNumInputChannels() const { return getUseRtAudio() ? m_numInputChannels : 2; }
+    int getNumOutputChannels() const { return getUseRtAudio() ? m_numOutputChannels : 2; }
+    int getBaseInputChannel() const { return getUseRtAudio() ? m_baseInputChannel : 0; }
+    int getBaseOutputChannel() const { return getUseRtAudio() ? m_baseOutputChannel : 0; }
+    int getInputMixMode() const { return getUseRtAudio() ? m_inputMixMode : 0; }
+    bool getInputMuted() const { return m_inMuted; }
+    bool getInputClipped() const { return m_inputClipped; }
+    bool getOutputClipped() const { return m_outputClipped; }
+    float getInputVolume() const { return m_inMultiplier; }
+    float getOutputVolume() const { return m_outMultiplier; }
+    float getMonitorVolume() const { return m_monMultiplier; }
+    const QString& getInputDevice() const { return m_inputDevice; }
+    const QString& getOutputDevice() const { return m_outputDevice; }
+    const QVector<float>& getInputMeterLevels() const { return m_inputMeterLevels; }
+    const QVector<float>& getOutputMeterLevels() const { return m_outputMeterLevels; }
+    const QJsonArray& getInputComboModel() const { return m_inputComboModel; }
+    const QJsonArray& getOutputComboModel() const { return m_outputComboModel; }
+    const QJsonArray& getInputChannelsComboModel() const
+    {
+        return m_inputChannelsComboModel;
+    }
+    const QJsonArray& getOutputChannelsComboModel() const
+    {
+        return m_outputChannelsComboModel;
+    }
+    const QJsonArray& getInputMixModeComboModel() const
+    {
+        return m_inputMixModeComboModel;
+    }
+    const QStringList& getFeedbackDetectionComboModel() const
+    {
+        return m_feedbackDetectionComboModel;
+    }
+    const QStringList& getBufferSizeComboModel() const { return m_bufferSizeComboModel; }
+    const QStringList& getBufferStrategyComboModel() const
+    {
+        return m_bufferStrategyComboModel;
+    }
+    const QStringList& getAudioBackendComboModel() const
+    {
+        return m_audioBackendComboModel;
+    }
+    const QString& getDevicesWarningMsg() const { return m_devicesWarningMsg; }
+    const QString& getDevicesErrorMsg() const { return m_devicesErrorMsg; }
+    const QString& getDevicesWarningHelpUrl() const { return m_devicesWarningHelpUrl; }
+    const QString& getDevicesErrorHelpUrl() const { return m_devicesErrorHelpUrl; }
+    bool getHighLatencyFlag() const { return m_highLatencyFlag; }
+   public slots:
+
+    // setters for state shared with QML
+    void setFeedbackDetectionEnabled(bool enabled);
+    void setAudioBackend(const QString& backend);
+    void setBufferSize(int bufSize);
+    void setBufferStrategy(int bufStrategy);
+    void setNumInputChannels(int numChannels);
+    void setNumOutputChannels(int numChannels);
+    void setBaseInputChannel(int baseChannel);
+    void setBaseOutputChannel(int baseChannel);
+    void setInputMixMode(int mode);
+    void setInputMuted(bool muted);
+    void setInputVolume(float multiplier);
+    void setOutputVolume(float multiplier);
+    void setMonitorVolume(float multiplier);
+    void setInputDevice(const QString& device);
+    void setOutputDevice(const QString& device);
+    void setDevicesErrorMsg(const QString& msg);
+    void setDevicesWarningMsg(const QString& msg);
+    void setDevicesErrorHelpUrl(const QString& url);
+    void setDevicesWarningHelpUrl(const QString& url);
+    void setHighLatencyFlag(bool highLatency);
+
+    // public methods accessible by QML
+    void startAudio(bool block = false);
+    void stopAudio(bool block = false);
+    void refreshDevices(bool block = false);
+    void validateDevices(bool block = false);
+    void restartAudio(bool block = false) { return startAudio(block); }
+    void playOutputAudio() { emit signalPlayOutputAudio(); }
+
+   signals:
+
+    // signals for QML state changes
+    void signalAudioReadyChanged();
+    void signalAudioIsReady();
+    void signalAudioIsNotReady();
+    void signalScanningDevicesChanged();
+    void deviceModelsInitializedChanged(bool initialized);
+    void audioBackendChanged(bool useRtAudio);
+    void bufferSizeChanged();
+    void bufferStrategyChanged();
+    void numInputChannelsChanged(int numChannels);
+    void numOutputChannelsChanged(int numChannels);
+    void baseInputChannelChanged(int baseChannel);
+    void baseOutputChannelChanged(int baseChannel);
+    void inputMixModeChanged(int mode);
+    void updatedInputMuted(bool muted);
+    void updatedInputClipped(bool clip);
+    void updatedOutputClipped(bool clip);
+    void updatedInputVolume(float multiplier);
+    void updatedOutputVolume(float multiplier);
+    void updatedMonitorVolume(float multiplier);
+    void inputDeviceChanged(QString device);
+    void outputDeviceChanged(QString device);
+    void updatedInputMeterLevels(const QVector<float>& levels);
+    void updatedOutputMeterLevels(const QVector<float>& levels);
+    void feedbackDetectionEnabledChanged();
+    void feedbackDetected();
+    void inputComboModelChanged();
+    void outputComboModelChanged();
+    void inputChannelsComboModelChanged();
+    void outputChannelsComboModelChanged();
+    void inputMixModeComboModelChanged();
+    void devicesWarningChanged();
+    void devicesErrorChanged();
+    void devicesWarningHelpUrlChanged();
+    void devicesErrorHelpUrlChanged();
+    void highLatencyFlagChanged(bool highLatencyFlag);
+
+    // other signals to perform actions
+    void signalPlayOutputAudio();
+    void signalStartAudio();
+    void signalStopAudio();
+    void signalRefreshDevices();
+    void signalValidateDevices();
+    void signalDevicesValidated();
+
+   private slots:
+    void setDeviceModels(QJsonArray inputComboModel, QJsonArray outputComboModel);
+    void setInputChannelsComboModel(QJsonArray& model);
+    void setOutputChannelsComboModel(QJsonArray& model);
+    void setInputMixModeComboModel(QJsonArray& model);
+
+   private:
+    // private methods
+    void setAudioReady(bool ready);
+    void setScanningDevices(bool b);
+    void detectedFeedbackLoop();
+    void updatedInputVuMeasurements(const float* valuesInDecibels, int numChannels);
+    void updatedOutputVuMeasurements(const float* valuesInDecibels, int numChannels);
+    void appendProcessPlugins(AudioInterface& audioInterface, bool forJackTrip,
+                              int numInputChannels, int numOutputChannels);
+    void updateDeviceMessages(AudioInterface& audioInterface);
+    AudioInterface* newJackAudioInterface(JackTrip* jackTripPtr = nullptr);
+    AudioInterface* newRtAudioInterface(JackTrip* jackTripPtr = nullptr);
+
+    // range for volume meters
+    static constexpr float m_meterMax = 0.0;
+    static constexpr float m_meterMin = -64.0;
+
+    // audio bit resolution
+    static constexpr AudioInterface::audioBitResolutionT m_audioBitResolution =
+        AudioInterface::BIT16;
+
+    // state shared with QML
+    AudioBackendType m_backend      = AudioBackendType::JACK;
+    bool m_audioReady               = false;
+    bool m_scanningDevices          = false;
+    bool m_feedbackDetectionEnabled = true;
+    bool m_deviceModelsInitialized  = false;
+    int m_audioBufferSize =
+        gDefaultBufferSizeInSamples;  ///< Audio buffer size to process on each callback
+    int m_bufferStrategy    = 0;
+    int m_numInputChannels  = gDefaultNumInChannels;
+    int m_numOutputChannels = gDefaultNumOutChannels;
+    int m_baseInputChannel  = 0;
+    int m_baseOutputChannel = 0;
+    int m_inputMixMode      = 0;
+    bool m_inMuted          = false;
+    bool m_inputClipped     = false;
+    bool m_outputClipped    = false;
+    float m_inMultiplier    = 1.0;
+    float m_outMultiplier   = 1.0;
+    float m_monMultiplier   = 0;
+    uint32_t m_sampleRate   = gDefaultSampleRate;
+
+    QString m_inputDevice;
+    QString m_outputDevice;
+    QVector<float> m_inputMeterLevels;
+    QVector<float> m_outputMeterLevels;
+    QJsonArray m_inputComboModel;
+    QJsonArray m_outputComboModel;
+    QJsonArray m_inputChannelsComboModel;
+    QJsonArray m_outputChannelsComboModel;
+    QJsonArray m_inputMixModeComboModel;
+    QString m_devicesWarningMsg     = QStringLiteral("");
+    QString m_devicesErrorMsg       = QStringLiteral("");
+    QString m_devicesWarningHelpUrl = QStringLiteral("");
+    QString m_devicesErrorHelpUrl   = QStringLiteral("");
+    bool m_highLatencyFlag          = false;
+
+    // other state not shared with QML
+    QSharedPointer<VsPermissions> m_permissionsPtr;
+    QScopedPointer<VsAudioWorker> m_audioWorkerPtr;
+    QScopedPointer<QThread> m_workerThread;
+    QTimer m_inputClipTimer;
+    QTimer m_outputClipTimer;
+    Meter* m_inputMeterPluginPtr;
+    Meter* m_outputMeterPluginPtr;
+    Volume* m_inputVolumePluginPtr;
+    Volume* m_outputVolumePluginPtr;
+    Monitor* m_monitorPluginPtr;
+
+#ifndef NO_FEEDBACK
+    Analyzer* m_outputAnalyzerPluginPtr;
+#endif
+
+    QStringList m_audioBackendComboModel      = {"JACK", "RtAudio"};
+    QStringList m_feedbackDetectionComboModel = {"Enabled", "Disabled"};
+    QStringList m_bufferSizeComboModel = {"16", "32", "64", "128", "256", "512", "1024"};
+    QStringList m_bufferStrategyComboModel = {"Minimal Latency", "Stable Latency",
+                                              "Loss Concealment (3)",
+                                              "Loss Concealment (4)"};
+
+    friend class VsAudioWorker;
+};
+
+/// VsAudioWorker uses a separate thread to help VsAudio
+class VsAudioWorker : public QObject
+{
+    Q_OBJECT
+
+   public:
+    VsAudioWorker(VsAudio* ptr);
+    virtual ~VsAudioWorker() {}
+
+   signals:
+    void signalDevicesValidated();
+    void signalUpdatedDeviceModels(QJsonArray inputComboModel,
+                                   QJsonArray outputComboModel);
+    void signalError(const QString& errorMessage);
+
+   public slots:
+    void openAudioInterface();
+    void closeAudioInterface();
+#ifdef RT_AUDIO
+    void refreshDevices();
+    void validateDevices();
+
+   private:
+    void updateDeviceModels();
+    void validateInputDevicesState();
+    void validateOutputDevicesState();
+    static void getDeviceList(const QVector<RtAudioDevice>& devices, QStringList& list,
+                              QStringList& categories, QList<int>& channels,
+                              bool isInput);
+    static QJsonArray formatDeviceList(const QStringList& devices,
+                                       const QStringList& categories,
+                                       const QList<int>& channels);
+    QVector<RtAudioDevice> m_devices;
+
+   public:
+    QVector<RtAudioDevice> getDevices() const { return m_devices; }
+#endif
+
+   private:
+    // parent getter wrappers
+    bool getUseRtAudio() const { return m_parentPtr->getUseRtAudio(); }
+    int getNumInputChannels() const { return m_parentPtr->getNumInputChannels(); }
+    int getNumOutputChannels() const { return m_parentPtr->getNumOutputChannels(); }
+    int getBaseInputChannel() const { return m_parentPtr->getBaseInputChannel(); }
+    int getBaseOutputChannel() const { return m_parentPtr->getBaseOutputChannel(); }
+    int getBufferSize() const { return m_parentPtr->getBufferSize(); }
+    int getInputMixMode() const { return m_parentPtr->getInputMixMode(); }
+    const QString& getInputDevice() const { return m_parentPtr->getInputDevice(); }
+    const QString& getOutputDevice() const { return m_parentPtr->getOutputDevice(); }
+
+    VsAudio* m_parentPtr;
+    QSharedPointer<AudioInterface> m_audioInterfacePtr;
+    QList<int> m_inputDeviceChannels;
+    QList<int> m_outputDeviceChannels;
+    QStringList m_inputDeviceList;
+    QStringList m_outputDeviceList;
+};
+
+#endif  // VSDAUDIO_H
diff --git a/src/gui/vsAudioInterface.cpp b/src/gui/vsAudioInterface.cpp
deleted file mode 100644 (file)
index 0d69619..0000000
+++ /dev/null
@@ -1,732 +0,0 @@
-//*****************************************************************
-/*
-  JackTrip: A System for High-Quality Audio Network Performance
-  over the Internet
-
-  Copyright (c) 2008-2022 Juan-Pablo Caceres, Chris Chafe.
-  SoundWIRE group at CCRMA, Stanford University.
-
-  Permission is hereby granted, free of charge, to any person
-  obtaining a copy of this software and associated documentation
-  files (the "Software"), to deal in the Software without
-  restriction, including without limitation the rights to use,
-  copy, modify, merge, publish, distribute, sublicense, and/or sell
-  copies of the Software, and to permit persons to whom the
-  Software is furnished to do so, subject to the following
-  conditions:
-
-  The above copyright notice and this permission notice shall be
-  included in all copies or substantial portions of the Software.
-
-  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
-  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
-  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-  OTHER DEALINGS IN THE SOFTWARE.
-*/
-//*****************************************************************
-
-/**
- * \file vsAudioInterface.cpp
- * \author Matt Horton
- * \date September 2022
- */
-
-#include "vsAudioInterface.h"
-
-#include <QDebug>
-#include <QMessageBox>
-#include <QSettings>
-
-#include "../Meter.h"
-#include "../Tone.h"
-
-// Constructor
-VsAudioInterface::VsAudioInterface(int NumChansIn, int NumChansOut,
-                                   AudioInterface::audioBitResolutionT AudioBitResolution,
-                                   QObject* parent)
-    : QObject(parent)
-    , m_numAudioChansIn(NumChansIn)
-    , m_numAudioChansOut(NumChansOut)
-    , m_audioBitResolution(AudioBitResolution)
-    , m_sampleRate(gDefaultSampleRate)
-    , m_deviceID(gDefaultDeviceID)
-    , m_inputDeviceName("")
-    , m_outputDeviceName("")
-    , m_audioBufferSize(gDefaultBufferSizeInSamples)
-#ifdef RT_AUDIO
-    , m_audioInterfaceMode(VsAudioInterface::RTAUDIO)
-#else
-    , m_audioInterfaceMode(VsAudioInterface::JACK)
-#endif
-{
-    QSettings settings;
-    settings.beginGroup(QStringLiteral("Audio"));
-    m_inMultiplier  = settings.value(QStringLiteral("InMultiplier"), 1).toFloat();
-    m_outMultiplier = settings.value(QStringLiteral("OutMultiplier"), 1).toFloat();
-    m_inMuted       = settings.value(QStringLiteral("InMuted"), false).toBool();
-    m_outMuted      = settings.value(QStringLiteral("OutMuted"), false).toBool();
-    if constexpr (isBackendAvailable<AudioInterfaceMode::ALL>()) {
-        m_audioInterfaceMode = (settings.value(QStringLiteral("Backend"), 0).toInt() == 1)
-                                   ? VsAudioInterface::RTAUDIO
-                                   : VsAudioInterface::JACK;
-    } else if constexpr (isBackendAvailable<AudioInterfaceMode::RTAUDIO>()) {
-        m_audioInterfaceMode = VsAudioInterface::RTAUDIO;
-    } else {
-        m_audioInterfaceMode = VsAudioInterface::JACK;
-    }
-
-    m_inputDeviceName =
-        settings.value(QStringLiteral("InputDevice"), "").toString().toStdString();
-    m_outputDeviceName =
-        settings.value(QStringLiteral("OutputDevice"), "").toString().toStdString();
-    settings.endGroup();
-
-    connect(this, &VsAudioInterface::settingsUpdated, this,
-            &VsAudioInterface::replaceProcess);
-    connect(this, &VsAudioInterface::modeUpdated, this,
-            &VsAudioInterface::replaceProcess);
-}
-
-VsAudioInterface::~VsAudioInterface()
-{
-    closeAudio();
-}
-
-void VsAudioInterface::setupAudio()
-{
-    try {
-        // Check if m_audioInterface has already been created or not
-        if (!m_audioInterface.isNull()) {  // if it has been created, disconnect it from
-                                           // JACK and delete it
-            std::cout << "WARNING: JackAudio interface was setup already:" << std::endl;
-            std::cout << "It will be erased and setup again." << std::endl;
-            std::cout << gPrintSeparator << std::endl;
-            closeAudio();
-        }
-
-        // Create AudioInterface Client Object
-        if (m_audioInterfaceMode == VsAudioInterface::JACK) {
-            if constexpr (isBackendAvailable<AudioInterfaceMode::ALL>()
-                          || isBackendAvailable<AudioInterfaceMode::JACK>()) {
-                setupJackAudio();
-            } else {
-                if constexpr (isBackendAvailable<AudioInterfaceMode::RTAUDIO>()) {
-                    setupRtAudio();
-                } else {
-                    throw std::runtime_error(
-                        "JackTrip was compiled without RtAudio and can't find JACK. In "
-                        "order to use JackTrip, you'll need to install JACK or rebuild "
-                        "with RtAudio support.");
-                    std::exit(1);
-                }
-            }
-        } else if (m_audioInterfaceMode == VsAudioInterface::RTAUDIO) {
-            if constexpr (isBackendAvailable<AudioInterfaceMode::RTAUDIO>()) {
-                setupRtAudio();
-            } else {
-                throw std::runtime_error(
-                    "JackTrip was compiled without RtAudio and can't find JACK. In order "
-                    "to use JackTrip, you'll need to install JACK or rebuild with "
-                    "RtAudio support.");
-                std::exit(1);
-            }
-        }
-
-        std::cout << "The Sampling Rate is: " << m_sampleRate << std::endl;
-        std::cout << gPrintSeparator << std::endl;
-        int AudioBufferSizeInBytes = m_audioBufferSize * sizeof(sample_t);
-        std::cout << "The Audio Buffer Size is: " << m_audioBufferSize << " samples"
-                  << std::endl;
-        std::cout << "                      or: " << AudioBufferSizeInBytes << " bytes"
-                  << std::endl;
-        std::cout << gPrintSeparator << std::endl;
-        std::cout << "The Number of Channels is: "
-                  << m_audioInterface->getNumInputChannels() << std::endl;
-        std::cout << gPrintSeparator << std::endl;
-        QThread::usleep(100);
-    } catch (const std::exception& e) {
-        emit errorToProcess(QString::fromUtf8(e.what()));
-    }
-}
-
-void VsAudioInterface::setupJackAudio()
-{
-#ifndef NO_JACK
-    if constexpr (isBackendAvailable<AudioInterfaceMode::ALL>()
-                  || isBackendAvailable<AudioInterfaceMode::JACK>()) {
-        if (gVerboseFlag)
-            std::cout << "  JackTrip:setupAudio before new JackAudioInterface"
-                      << std::endl;
-
-        QVarLengthArray<int> inputChans;
-        QVarLengthArray<int> outputChans;
-        inputChans.resize(m_numAudioChansIn);
-        outputChans.resize(m_numAudioChansOut);
-
-        for (int i = 0; i < m_numAudioChansIn; i++) {
-            inputChans[i] = 1 + i;
-        }
-        for (int i = 0; i < m_numAudioChansOut; i++) {
-            outputChans[i] = 1 + i;
-        }
-
-        m_audioInterface.reset(
-            new JackAudioInterface(inputChans, outputChans, m_audioBitResolution));
-
-        m_audioInterface->setClientName(QStringLiteral("JackTrip"));
-
-        if (gVerboseFlag)
-            std::cout << "  JackTrip:setupAudio before m_audioInterface->setup"
-                      << std::endl;
-        m_audioInterface->setup(true);
-
-        std::string devicesWarningMsg = m_audioInterface->getDevicesWarningMsg();
-        std::string devicesErrorMsg   = m_audioInterface->getDevicesErrorMsg();
-
-        if (devicesWarningMsg != "") {
-            qDebug() << "Devices Warning: " << QString::fromStdString(devicesWarningMsg);
-        }
-
-        if (devicesErrorMsg != "") {
-            qDebug() << "Devices Error: " << QString::fromStdString(devicesErrorMsg);
-        }
-
-        updateDevicesWarningMsg(QString::fromStdString(devicesWarningMsg));
-        updateDevicesErrorMsg(QString::fromStdString(devicesErrorMsg));
-
-        if (gVerboseFlag)
-            std::cout << "  JackTrip:setupAudio before m_audioInterface->getSampleRate"
-                      << std::endl;
-        m_sampleRate = m_audioInterface->getSampleRate();
-        if (gVerboseFlag)
-            std::cout << "  JackTrip:setupAudio before m_audioInterface->getDeviceID"
-                      << std::endl;
-        m_deviceID = m_audioInterface->getDeviceID();
-        if (gVerboseFlag)
-            std::cout << "  JackTrip:setupAudio before "
-                         "m_audioInterface->getBufferSizeInSamples"
-                      << std::endl;
-        m_audioBufferSize = m_audioInterface->getBufferSizeInSamples();
-    } else {
-        return;
-    }
-#else
-    return;
-#endif
-}
-
-void VsAudioInterface::setupRtAudio()
-{
-#ifdef RT_AUDIO
-    if constexpr (isBackendAvailable<AudioInterfaceMode::ALL>()
-                  || isBackendAvailable<AudioInterfaceMode::RTAUDIO>()) {
-        QVarLengthArray<int> inputChans;
-        QVarLengthArray<int> outputChans;
-        inputChans.resize(m_numAudioChansIn);
-        outputChans.resize(m_numAudioChansOut);
-
-        for (int i = 0; i < m_numAudioChansIn; i++) {
-            inputChans[i] = m_baseInputChannel + i;
-        }
-        for (int i = 0; i < m_numAudioChansOut; i++) {
-            outputChans[i] = m_baseOutputChannel + i;
-        }
-
-        m_audioInterface.reset(new RtAudioInterface(
-            inputChans, outputChans,
-            static_cast<AudioInterface::inputMixModeT>(m_inputMixMode),
-            m_audioBitResolution));
-        m_audioInterface->setSampleRate(m_sampleRate);
-        m_audioInterface->setDeviceID(m_deviceID);
-        m_audioInterface->setInputDevice(m_inputDeviceName);
-        m_audioInterface->setOutputDevice(m_outputDeviceName);
-        m_audioInterface->setBufferSizeInSamples(m_audioBufferSize);
-        static_cast<RtAudioInterface*>(m_audioInterface.get())->setDevices(m_devices);
-
-        // Note: setup might change the number of channels and/or buffer size
-        m_audioInterface->setup(true);
-
-        std::string devicesWarningMsg     = m_audioInterface->getDevicesWarningMsg();
-        std::string devicesErrorMsg       = m_audioInterface->getDevicesErrorMsg();
-        std::string devicesWarningHelpUrl = m_audioInterface->getDevicesWarningHelpUrl();
-        std::string devicesErrorHelpUrl   = m_audioInterface->getDevicesErrorHelpUrl();
-
-        if (devicesWarningMsg != "") {
-            qDebug() << "Devices Warning: " << QString::fromStdString(devicesWarningMsg);
-            if (devicesWarningHelpUrl != "") {
-                qDebug() << "Learn More: "
-                         << QString::fromStdString(devicesWarningHelpUrl);
-            }
-        }
-
-        if (devicesErrorMsg != "") {
-            qDebug() << "Devices Error: " << QString::fromStdString(devicesErrorMsg);
-            if (devicesErrorHelpUrl != "") {
-                qDebug() << "Learn More: " << QString::fromStdString(devicesErrorHelpUrl);
-            }
-        }
-
-        updateDevicesWarningMsg(QString::fromStdString(devicesWarningMsg));
-        updateDevicesErrorMsg(QString::fromStdString(devicesErrorMsg));
-        updateDevicesWarningHelpUrl(QString::fromStdString(devicesWarningHelpUrl));
-        updateDevicesErrorHelpUrl(QString::fromStdString(devicesErrorHelpUrl));
-    } else {
-        return;
-    }
-#else
-    return;
-#endif
-}
-
-void VsAudioInterface::closeAudio()
-{
-    if (!m_audioInterface.isNull()) {
-        try {
-            if (m_audioActive) {
-                m_audioInterface->stopProcess();
-                m_audioActive = false;
-            }
-        } catch (const std::exception& e) {
-            emit errorToProcess(QString::fromUtf8(e.what()));
-        }
-        m_audioInterface.clear();
-        m_deviceID = gDefaultDeviceID;
-    }
-}
-
-void VsAudioInterface::replaceProcess()
-{
-    if (m_hasBeenActive) {
-        closeAudio();
-        setupAudio();
-        setupPlugins();
-        startProcess();
-    }
-}
-
-void VsAudioInterface::processInputMeterMeasurements(float* values, int numChannels)
-{
-    emit newInputMeterMeasurements(values, numChannels);
-}
-
-void VsAudioInterface::processOutputMeterMeasurements(float* values, int numChannels)
-{
-    emit newOutputMeterMeasurements(values, numChannels);
-}
-
-void VsAudioInterface::addInputPlugin(ProcessPlugin* plugin)
-{
-    m_audioInterface->appendProcessPluginToNetwork(plugin);
-}
-
-void VsAudioInterface::addOutputPlugin(ProcessPlugin* plugin)
-{
-    m_audioInterface->appendProcessPluginFromNetwork(plugin);
-}
-
-void VsAudioInterface::addMonitorPlugin(ProcessPlugin* plugin)
-{
-    m_audioInterface->appendProcessPluginToMonitor(plugin);
-}
-
-void VsAudioInterface::setInputDevice(QString deviceName, bool shouldRestart)
-{
-    m_inputDeviceName = deviceName.toStdString();
-    if (!m_audioInterface.isNull()) {
-        if (m_audioActive && shouldRestart) {
-            emit settingsUpdated();
-        }
-    }
-}
-
-void VsAudioInterface::setOutputDevice(QString deviceName, bool shouldRestart)
-{
-    m_outputDeviceName = deviceName.toStdString();
-    if (!m_audioInterface.isNull()) {
-        if (m_audioActive && shouldRestart) {
-            emit settingsUpdated();
-        }
-    }
-}
-
-#ifdef RT_AUDIO
-void VsAudioInterface::setBaseInputChannel(int baseChannel, bool shouldRestart)
-{
-    if (m_audioInterfaceMode != VsAudioInterface::RTAUDIO) {
-        return;
-    }
-    m_baseInputChannel = baseChannel;
-    if (!m_audioInterface.isNull()) {
-        if (m_audioActive && shouldRestart) {
-            emit settingsUpdated();
-        }
-    }
-    return;
-}
-
-void VsAudioInterface::setNumInputChannels(int numChannels, bool shouldRestart)
-{
-    if (m_audioInterfaceMode != VsAudioInterface::RTAUDIO) {
-        return;
-    }
-    m_numAudioChansIn = numChannels;
-    if (!m_audioInterface.isNull()) {
-        if (m_audioActive && shouldRestart) {
-            emit settingsUpdated();
-        }
-    }
-}
-
-void VsAudioInterface::setInputMixMode(const int mode, bool shouldRestart)
-{
-    if (m_audioInterfaceMode != VsAudioInterface::RTAUDIO) {
-        return;
-    }
-    m_inputMixMode = mode;
-    if (!m_audioInterface.isNull()) {
-        if (m_audioActive && shouldRestart) {
-            emit settingsUpdated();
-        }
-    }
-    return;
-}
-
-void VsAudioInterface::setBaseOutputChannel(int baseChannel, bool shouldRestart)
-{
-    if (m_audioInterfaceMode != VsAudioInterface::RTAUDIO) {
-        return;
-    }
-    m_baseOutputChannel = baseChannel;
-    if (!m_audioInterface.isNull()) {
-        if (m_audioActive && shouldRestart) {
-            emit settingsUpdated();
-        }
-    }
-    return;
-}
-
-void VsAudioInterface::setNumOutputChannels(int numChannels, bool shouldRestart)
-{
-    if (m_audioInterfaceMode != VsAudioInterface::RTAUDIO) {
-        return;
-    }
-    m_numAudioChansOut = numChannels;
-    if (!m_audioInterface.isNull()) {
-        if (m_audioActive && shouldRestart) {
-            emit settingsUpdated();
-        }
-    }
-}
-
-void VsAudioInterface::refreshRtAudioDevices()
-{
-    RtAudioInterface::scanDevices(m_devices);
-}
-
-void VsAudioInterface::getDeviceList(QStringList* list, QStringList* categories,
-                                     QList<int>* channels, bool isInput)
-{
-    RtAudio baseRtAudio;
-    RtAudio::Api baseRtAudioApi = baseRtAudio.getCurrentApi();
-    if (categories != NULL) {
-        categories->clear();
-    }
-    if (channels != NULL) {
-        channels->clear();
-    }
-    list->clear();
-
-    // do not include blacklisted audio interfaces
-    // these are known to be unstable and cause JackTrip to crash
-    QVector<QString> blacklisted_devices = {
-#ifdef _WIN32
-        // Realtek ASIO: seems to crash any computer that tries to use it
-        QString::fromUtf8("Realtek ASIO"),
-#endif
-        // JackRouter: crashes if not running; use Jack backend instead
-        QString::fromUtf8("JackRouter"),
-    };
-
-    // Explicitly add default device
-    QString defaultDeviceName = "";
-    uint32_t defaultDeviceIdx;
-    RtAudio::DeviceInfo defaultDeviceInfo;
-    if (isInput) {
-        defaultDeviceIdx = baseRtAudio.getDefaultInputDevice();
-    } else {
-        defaultDeviceIdx = baseRtAudio.getDefaultOutputDevice();
-    }
-
-    if (defaultDeviceIdx != 0) {
-        defaultDeviceInfo = baseRtAudio.getDeviceInfo(defaultDeviceIdx);
-        defaultDeviceName = QString::fromStdString(defaultDeviceInfo.name);
-    }
-
-    if (blacklisted_devices.contains(defaultDeviceName)) {
-        std::cout << "RTAudio: blacklisted default " << (isInput ? "input" : "output")
-                  << " device: " << defaultDeviceName.toStdString() << std::endl;
-    } else if (defaultDeviceName != "") {
-        list->append(defaultDeviceName);
-        if (categories != NULL) {
-#ifdef _WIN32
-            switch (baseRtAudioApi) {
-            case RtAudio::WINDOWS_ASIO:
-                categories->append(QStringLiteral("Low-Latency (ASIO)"));
-                break;
-            case RtAudio::WINDOWS_WASAPI:
-                categories->append(QStringLiteral("High-Latency (Non-ASIO)"));
-                break;
-            case RtAudio::WINDOWS_DS:
-                categories->append(QStringLiteral("High-Latency (Non-ASIO)"));
-                break;
-            default:
-                categories->append(QStringLiteral(""));
-                break;
-            }
-#else
-            categories->append(QStringLiteral(""));
-#endif
-        }
-        if (channels != NULL) {
-            if (isInput) {
-                channels->append(defaultDeviceInfo.inputChannels);
-            } else {
-                channels->append(defaultDeviceInfo.outputChannels);
-            }
-        }
-    }
-
-    for (int n = 0; n < m_devices.size(); ++n) {
-#ifdef _WIN32
-        if (m_devices[n].api == RtAudio::UNIX_JACK) {
-            continue;
-        }
-#endif
-        // Don't include duplicate entries
-        if (list->contains(m_devices[n].name)) {
-            continue;
-        }
-
-        // Skip the default device, since we already added it
-        if (m_devices[n].name == defaultDeviceName
-            && m_devices[n].api == baseRtAudioApi) {
-            continue;
-        }
-
-        // Skip if no channels available
-        if ((isInput && m_devices[n].inputChannels == 0)
-            || (!isInput && m_devices[n].outputChannels == 0)) {
-            continue;
-        }
-
-        // Skip blacklisted devices
-        if (blacklisted_devices.contains(m_devices[n].name)) {
-            std::cout << "RTAudio: blacklisted " << (isInput ? "input" : "output")
-                      << " device: " << m_devices[n].name.toStdString() << std::endl;
-            continue;
-        }
-
-        // Good to go!
-        if (isInput) {
-            list->append(m_devices[n].name);
-            if (channels != NULL) {
-                channels->append(m_devices[n].inputChannels);
-            }
-        } else {
-            list->append(m_devices[n].name);
-            if (channels != NULL) {
-                channels->append(m_devices[n].outputChannels);
-            }
-        }
-
-        if (categories == NULL) {
-            continue;
-        }
-
-#ifdef _WIN32
-        switch (m_devices[n].api) {
-        case RtAudio::WINDOWS_ASIO:
-            categories->append("Low-Latency (ASIO)");
-            break;
-        case RtAudio::WINDOWS_WASAPI:
-            categories->append("High-Latency (Non-ASIO)");
-            break;
-        case RtAudio::WINDOWS_DS:
-            categories->append("High-Latency (Non-ASIO)");
-            break;
-        default:
-            categories->append("");
-            break;
-        }
-#else
-        categories->append("");
-#endif
-    }
-}
-#endif
-
-void VsAudioInterface::setAudioInterfaceMode(bool useRtAudio, bool shouldRestart)
-{
-    if (useRtAudio) {
-#ifdef RT_AUDIO
-        m_audioInterfaceMode = VsAudioInterface::RTAUDIO;
-#endif
-    } else {
-#ifndef NO_JACK
-        m_audioInterfaceMode = VsAudioInterface::JACK;
-#endif
-    }
-    if ((!m_audioInterface.isNull() || m_hasBeenActive) && shouldRestart) {
-        emit modeUpdated();
-    }
-}
-
-int VsAudioInterface::getNumInputChannels()
-{
-    return m_audioInterface->getNumInputChannels();
-}
-
-int VsAudioInterface::getNumOutputChannels()
-{
-    return m_audioInterface->getNumOutputChannels();
-}
-
-void VsAudioInterface::setupPlugins()
-{
-    // Create plugins
-    m_inputMeter         = new Meter(getNumInputChannels());
-    m_outputMeter        = new Meter(getNumOutputChannels());
-    m_inputVolumePlugin  = new Volume(getNumInputChannels());
-    m_outputVolumePlugin = new Volume(getNumOutputChannels());
-    m_outputTonePlugin   = new Tone(getNumOutputChannels());
-
-    // Add plugins to chains
-    addOutputPlugin(m_outputTonePlugin);
-    addInputPlugin(m_inputVolumePlugin);
-    addOutputPlugin(m_outputVolumePlugin);
-    addInputPlugin(m_inputMeter);
-    addOutputPlugin(m_outputMeter);
-
-    // Connect plugins for communication with UI
-    connect(m_inputMeter, &Meter::onComputedVolumeMeasurements, this,
-            &VsAudioInterface::processInputMeterMeasurements);
-    connect(m_outputMeter, &Meter::onComputedVolumeMeasurements, this,
-            &VsAudioInterface::processOutputMeterMeasurements);
-    connect(this, &VsAudioInterface::updatedInputVolume, m_inputVolumePlugin,
-            &Volume::volumeUpdated);
-    connect(this, &VsAudioInterface::updatedOutputVolume, m_outputVolumePlugin,
-            &Volume::volumeUpdated);
-    connect(this, &VsAudioInterface::updatedInputMuted, m_inputVolumePlugin,
-            &Volume::muteUpdated);
-    connect(this, &VsAudioInterface::updatedOutputMuted, m_outputVolumePlugin,
-            &Volume::muteUpdated);
-    connect(this, &VsAudioInterface::triggerPlayback, m_outputTonePlugin,
-            &Tone::triggerPlayback);
-}
-
-void VsAudioInterface::startProcess()
-{
-    if (!m_audioInterface.isNull() && !m_audioActive) {
-        try {
-            m_audioInterface->initPlugins(false);
-            m_audioInterface->startProcess();
-#ifndef NO_JACK
-            if (m_audioInterfaceMode == VsAudioInterface::JACK) {
-                m_audioInterface->connectDefaultPorts();
-            }
-#endif
-        } catch (const std::exception& e) {
-            emit errorToProcess(QString::fromUtf8(e.what()));
-        }
-        m_audioActive   = true;
-        m_hasBeenActive = true;
-    }
-}
-
-float VsAudioInterface::inputVolume()
-{
-    return m_inMultiplier;
-}
-
-float VsAudioInterface::outputVolume()
-{
-    return m_outMultiplier;
-}
-
-bool VsAudioInterface::inputMuted()
-{
-    return m_inMuted;
-}
-
-bool VsAudioInterface::outputMuted()
-{
-    return m_outMuted;
-}
-
-void VsAudioInterface::setInputVolume(float multiplier)
-{
-    m_inMultiplier = multiplier;
-    QSettings settings;
-    settings.beginGroup(QStringLiteral("Audio"));
-    settings.setValue(QStringLiteral("InMultiplier"), m_inMultiplier);
-    settings.endGroup();
-    emit updatedInputVolume(multiplier);
-}
-
-void VsAudioInterface::setOutputVolume(float multiplier)
-{
-    m_outMultiplier = multiplier;
-    QSettings settings;
-    settings.beginGroup(QStringLiteral("Audio"));
-    settings.setValue(QStringLiteral("OutMultiplier"), m_outMultiplier);
-    settings.endGroup();
-    emit updatedOutputVolume(multiplier);
-}
-
-void VsAudioInterface::setInputMuted(bool muted)
-{
-    m_inMuted = muted;
-    QSettings settings;
-    settings.beginGroup(QStringLiteral("Audio"));
-    settings.setValue(QStringLiteral("InMuted"), m_inMuted ? 1 : 0);
-    settings.endGroup();
-    emit updatedInputMuted(muted);
-}
-
-void VsAudioInterface::setOutputMuted(bool muted)
-{
-    m_outMuted = muted;
-    QSettings settings;
-    settings.beginGroup(QStringLiteral("Audio"));
-    settings.setValue(QStringLiteral("OutMuted"), m_outMuted ? 1 : 0);
-    settings.endGroup();
-    emit updatedOutputMuted(muted);
-}
-
-void VsAudioInterface::updateDevicesErrorMsg(const QString& msg)
-{
-    emit devicesErrorMsgChanged(msg);
-    return;
-}
-
-void VsAudioInterface::updateDevicesWarningMsg(const QString& msg)
-{
-    emit devicesWarningMsgChanged(msg);
-    return;
-}
-
-void VsAudioInterface::updateDevicesWarningHelpUrl(const QString& url)
-{
-    emit devicesWarningHelpUrlChanged(url);
-    return;
-}
-
-void VsAudioInterface::updateDevicesErrorHelpUrl(const QString& url)
-{
-    emit devicesErrorHelpUrlChanged(url);
-    return;
-}
diff --git a/src/gui/vsAudioInterface.h b/src/gui/vsAudioInterface.h
deleted file mode 100644 (file)
index 9bd672c..0000000
+++ /dev/null
@@ -1,180 +0,0 @@
-//*****************************************************************
-/*
-  JackTrip: A System for High-Quality Audio Network Performance
-  over the Internet
-
-  Copyright (c) 2008-2022 Juan-Pablo Caceres, Chris Chafe.
-  SoundWIRE group at CCRMA, Stanford University.
-
-  Permission is hereby granted, free of charge, to any person
-  obtaining a copy of this software and associated documentation
-  files (the "Software"), to deal in the Software without
-  restriction, including without limitation the rights to use,
-  copy, modify, merge, publish, distribute, sublicense, and/or sell
-  copies of the Software, and to permit persons to whom the
-  Software is furnished to do so, subject to the following
-  conditions:
-
-  The above copyright notice and this permission notice shall be
-  included in all copies or substantial portions of the Software.
-
-  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
-  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
-  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-  OTHER DEALINGS IN THE SOFTWARE.
-*/
-//*****************************************************************
-
-/**
- * \file vsAudioInterface.h
- * \author Matt Horton
- * \date September 2022
- */
-
-#ifndef VSDAUDIOINTERFACE_H
-#define VSDAUDIOINTERFACE_H
-
-#include <QDebug>
-#include <QList>
-#include <QObject>
-#include <QSharedPointer>
-#include <QString>
-#include <QStringList>
-
-#ifndef NO_JACK
-#include "../JackAudioInterface.h"
-#endif
-#ifdef RT_AUDIO
-#include "../RtAudioInterface.h"
-#endif
-
-#include "../Meter.h"
-#include "../Tone.h"
-#include "../Volume.h"
-#include "../jacktrip_globals.h"
-#include "AudioInterfaceMode.h"
-
-class VsAudioInterface : public QObject
-{
-    Q_OBJECT
-    Q_PROPERTY(
-        float inputVolume READ inputVolume WRITE setInputVolume NOTIFY updatedInputVolume)
-    Q_PROPERTY(float outputVolume READ outputVolume WRITE setOutputVolume NOTIFY
-                   updatedOutputVolume)
-
-   public:
-    // Constructor
-    explicit VsAudioInterface(
-        int NumChansIn = gDefaultNumInChannels, int NumChansOut = gDefaultNumOutChannels,
-        AudioInterface::audioBitResolutionT AudioBitResolution = AudioInterface::BIT16,
-        QObject* parent                                        = nullptr);
-    ~VsAudioInterface();
-
-    // Public functions
-    void setupAudio();
-    void closeAudio();
-    void startProcess();
-    void addInputPlugin(ProcessPlugin* plugin);
-    void addOutputPlugin(ProcessPlugin* plugin);
-    void addMonitorPlugin(ProcessPlugin* plugin);
-    int getNumInputChannels();
-    int getNumOutputChannels();
-    void setupPlugins();
-    float inputVolume();
-    float outputVolume();
-    bool inputMuted();
-    bool outputMuted();
-
-    enum audiointerfaceModeT {
-        JACK,    ///< Jack Mode
-        RTAUDIO  ///< RtAudio Mode
-    };
-
-   public slots:
-    void setInputDevice(QString deviceName, bool shouldRestart = true);
-    void setOutputDevice(QString deviceName, bool shouldRestart = true);
-#ifdef RT_AUDIO
-    void setBaseInputChannel(int baseChannel, bool shouldRestart = true);
-    void setNumInputChannels(int numChannels, bool shouldRestart = true);
-    void setInputMixMode(const int mode, bool shouldRestart = true);
-    void setBaseOutputChannel(int baseChannel, bool shouldRestart = true);
-    void setNumOutputChannels(int numChannels, bool shouldRestart = true);
-    void refreshRtAudioDevices();
-    void getDeviceList(QStringList* list, QStringList* categories, QList<int>* channels,
-                       bool isInput);
-#endif
-    void setAudioInterfaceMode(bool useRtAudio, bool shouldRestart = true);
-    void setInputVolume(float multiplier);
-    void setOutputVolume(float multiplier);
-    void setInputMuted(bool muted);
-    void setOutputMuted(bool muted);
-
-   signals:
-    void updatedInputVolume(float multiplier);
-    void updatedOutputVolume(float multiplier);
-    void updatedInputMuted(bool muted);
-    void updatedOutputMuted(bool muted);
-    void triggerPlayback();
-    void settingsUpdated();
-    void modeUpdated();
-    void newInputMeterMeasurements(float* values, int numChannels);
-    void newOutputMeterMeasurements(float* values, int numChannels);
-    void errorToProcess(const QString& errorMessage);
-    void devicesErrorMsgChanged(const QString& msg);
-    void devicesWarningMsgChanged(const QString& msg);
-    void devicesErrorHelpUrlChanged(const QString& url);
-    void devicesWarningHelpUrlChanged(const QString& url);
-
-   private slots:
-    // void refreshAudioStream();
-    void replaceProcess();
-    void processInputMeterMeasurements(float* values, int numChannels);
-    void processOutputMeterMeasurements(float* values, int numChannels);
-
-   private:
-    void setupJackAudio();
-    void setupRtAudio();
-
-    float m_inMultiplier  = 1.0;
-    float m_outMultiplier = 1.0;
-    bool m_inMuted        = false;
-    bool m_outMuted       = false;
-    bool m_audioActive    = false;
-    bool m_hasBeenActive  = false;
-
-    int m_baseInputChannel  = 0;
-    int m_baseOutputChannel = 0;
-    int m_inputMixMode      = 0;
-
-    // Needed in constructor
-    int m_numAudioChansIn;   ///< Number of Audio Input Channels
-    int m_numAudioChansOut;  ///< Number of Audio Output Channels
-    AudioInterface::audioBitResolutionT m_audioBitResolution;  ///< Audio Bit Resolutions
-
-    QSharedPointer<AudioInterface> m_audioInterface;
-    uint32_t m_sampleRate;                              ///< Sample Rate
-    uint32_t m_deviceID;                                ///< RTAudio DeviceID
-    std::string m_inputDeviceName, m_outputDeviceName;  ///< RTAudio device names
-    uint32_t m_audioBufferSize;  ///< Audio buffer size to process on each callback
-    VsAudioInterface::audiointerfaceModeT m_audioInterfaceMode;
-    Meter* m_outputMeter;
-    Meter* m_inputMeter;
-    Volume* m_inputVolumePlugin;
-    Volume* m_outputVolumePlugin;
-    Tone* m_outputTonePlugin;
-
-#ifdef RT_AUDIO
-    QVector<RtAudioDevice> m_devices;
-#endif
-
-    void updateDevicesErrorMsg(const QString& msg);
-    void updateDevicesWarningMsg(const QString& msg);
-    void updateDevicesErrorHelpUrl(const QString& url);
-    void updateDevicesWarningHelpUrl(const QString& url);
-};
-
-#endif  // VSDAUDIOINTERFACE_H
index ae9221bde42314adb4c3da691e375746ad6ed5d0..970855c1088b63587a142f5c7addb783b5205442 100644 (file)
 
 #include "./vsConstants.h"
 
-VsAuth::VsAuth(VsQuickView* view, QNetworkAccessManager* networkAccessManager, VsApi* api)
+VsAuth::VsAuth(QNetworkAccessManager* networkAccessManager, VsApi* api)
     : m_clientId(AUTH_CLIENT_ID), m_authorizationServerHost(AUTH_SERVER_HOST)
 {
-    m_view                 = view;
     m_networkAccessManager = networkAccessManager;
     m_api                  = api;
     m_deviceCodeFlow.reset(new VsDeviceCodeFlow(networkAccessManager));
@@ -56,8 +55,6 @@ VsAuth::VsAuth(VsQuickView* view, QNetworkAccessManager* networkAccessManager, V
     connect(m_deviceCodeFlow.data(), &VsDeviceCodeFlow::deviceCodeFlowTimedOut, this,
             &VsAuth::codeExpired);
 
-    m_view->engine()->rootContext()->setContextProperty(QStringLiteral("auth"), this);
-
     m_verificationUrl = QStringLiteral("https://auth.jacktrip.org/activate");
 }
 
@@ -104,12 +101,22 @@ void VsAuth::fetchUserInfo(QString accessToken)
         QJsonDocument userInfo = QJsonDocument::fromJson(response);
         QString userId         = userInfo.object()[QStringLiteral("sub")].toString();
 
+        reply->deleteLater();
+
+        if (userId.isEmpty()) {
+            std::cout << "VsAuth::fetchUserInfo Error: empty userId" << std::endl;
+            handleAuthFailed();  // handle failure
+            emit fetchUserInfoFailed();
+            return;
+        }
+
         handleAuthSucceeded(userId, accessToken);
     });
 }
 
 void VsAuth::refreshAccessToken(QString refreshToken)
 {
+    qDebug() << "Refreshing access token";
     m_authenticationStage = QStringLiteral("refreshing");
     emit updatedAuthenticationStage(m_authenticationStage);
 
@@ -154,8 +161,12 @@ void VsAuth::refreshAccessToken(QString refreshToken)
         QJsonObject object  = data.object();
         QString accessToken = object.value(QLatin1String("access_token")).toString();
         m_api->setAccessToken(accessToken);  // set access token
-        fetchUserInfo(accessToken);          // get user ID from Auth0
         reply->deleteLater();
+        if (m_userId.isEmpty()) {
+            fetchUserInfo(accessToken);  // get user ID from Auth0
+        } else {
+            handleRefreshSucceeded(accessToken);
+        }
     });
 }
 
@@ -179,6 +190,19 @@ void VsAuth::codeExpired()
     emit deviceCodeExpired();
 }
 
+void VsAuth::handleRefreshSucceeded(QString accessToken)
+{
+    qDebug() << "Successfully refreshed access token";
+
+    m_accessToken            = accessToken;
+    m_authenticationStage    = QStringLiteral("success");
+    m_attemptingRefreshToken = false;
+
+    emit updatedAuthenticationStage(m_authenticationStage);
+    emit updatedVerificationCode(m_verificationCode);
+    emit updatedAttemptingRefreshToken(m_attemptingRefreshToken);
+}
+
 void VsAuth::handleAuthSucceeded(QString userId, QString accessToken)
 {
     // Success case: we got our access token (either through the refresh token or device
@@ -238,6 +262,7 @@ void VsAuth::handleAuthFailed()
 
 void VsAuth::cancelAuthenticationFlow()
 {
+    qDebug() << "Canceling authentication flow";
     m_deviceCodeFlow->cancelCodeFlow();
 
     m_userId              = QStringLiteral("");
@@ -257,6 +282,7 @@ void VsAuth::logout()
     if (!m_isAuthenticated) {
         std::cout << "Warning: attempting to logout while not authenticated" << std::endl;
     }
+    qDebug() << "Logging out";
 
     // reset auth state
     m_userId              = QStringLiteral("");
index 71c0d26cf36fadb4b1928b6b723ee230b048adb5..61ccb855d626a87345f7e51f66940b1402b4de21 100644 (file)
@@ -46,7 +46,6 @@
 
 #include "vsApi.h"
 #include "vsDeviceCodeFlow.h"
-#include "vsQuickView.h"
 
 class VsAuth : public QObject
 {
@@ -63,9 +62,10 @@ class VsAuth : public QObject
     Q_PROPERTY(bool attemptingRefreshToken READ attemptingRefreshToken NOTIFY
                    updatedAttemptingRefreshToken);
     Q_PROPERTY(QString userId READ userId NOTIFY updatedUserId);
+    Q_PROPERTY(QString accessToken READ accessToken CONSTANT);
 
    public:
-    VsAuth(VsQuickView* view, QNetworkAccessManager* networkAccessManager, VsApi* api);
+    VsAuth(QNetworkAccessManager* networkAccessManager, VsApi* api);
 
     void authenticate(QString currentRefreshToken);
     void refreshAccessToken(QString refreshToken);
@@ -101,6 +101,7 @@ class VsAuth : public QObject
     void deviceCodeExpired();
 
    private slots:
+    void handleRefreshSucceeded(QString accessToken);
     void handleAuthSucceeded(QString userId, QString accessToken);
     void handleAuthFailed();
     void initializedCodeFlow(QString code, QString verificationUrl);
@@ -124,7 +125,6 @@ class VsAuth : public QObject
     QString m_accessToken;
     QString m_refreshToken;
 
-    VsQuickView* m_view;
     QNetworkAccessManager* m_networkAccessManager;
     VsApi* m_api;
     QScopedPointer<VsDeviceCodeFlow> m_deviceCodeFlow;
diff --git a/src/gui/vsDeeplink.cpp b/src/gui/vsDeeplink.cpp
new file mode 100644 (file)
index 0000000..ed6f41d
--- /dev/null
@@ -0,0 +1,228 @@
+//*****************************************************************
+/*
+  JackTrip: A System for High-Quality Audio Network Performance
+  over the Internet
+
+  Copyright (c) 2008-2023 Juan-Pablo Caceres, Chris Chafe.
+  SoundWIRE group at CCRMA, Stanford University.
+
+  Permission is hereby granted, free of charge, to any person
+  obtaining a copy of this software and associated documentation
+  files (the "Software"), to deal in the Software without
+  restriction, including without limitation the rights to use,
+  copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the
+  Software is furnished to do so, subject to the following
+  conditions:
+
+  The above copyright notice and this permission notice shall be
+  included in all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+  OTHER DEALINGS IN THE SOFTWARE.
+*/
+//*****************************************************************
+
+/**
+ * \file vsDeeplink.cpp
+ * \author Aaron Wyatt, based on code by Matt Horton
+ * \date February 2023
+ */
+
+#include "vsDeeplink.h"
+
+#include <QCommandLineParser>
+#include <QDebug>
+#include <QDesktopServices>
+#include <QDir>
+#include <QEventLoop>
+#include <QMutexLocker>
+#include <QSettings>
+#include <QTimer>
+
+VsDeeplink::VsDeeplink(QCoreApplication* app) : m_deeplink(parseDeeplink(app))
+{
+    setUrlScheme();
+    checkForInstance();
+    QDesktopServices::setUrlHandler(QStringLiteral("jacktrip"), this, "handleUrl");
+}
+
+VsDeeplink::~VsDeeplink()
+{
+    QDesktopServices::unsetUrlHandler(QStringLiteral("jacktrip"));
+}
+
+bool VsDeeplink::waitForReady()
+{
+    while (!m_isReady) {
+        QTimer timer;
+        timer.setTimerType(Qt::CoarseTimer);
+        timer.setSingleShot(true);
+
+        QEventLoop loop;
+        QObject::connect(this, &VsDeeplink::signalIsReady, &loop, &QEventLoop::quit);
+        QObject::connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
+        timer.start(100);  // wait for 100ms
+        loop.exec();
+    }
+    return m_readyToExit;
+}
+
+void VsDeeplink::readyForSignals()
+{
+    m_readyForSignals = true;
+    if (!m_deeplink.isEmpty()) {
+        emit signalDeeplink(m_deeplink);
+        m_deeplink.clear();
+    }
+}
+
+void VsDeeplink::handleUrl(const QUrl& url)
+{
+    if (m_readyForSignals) {
+        emit signalDeeplink(url);
+    } else {
+        m_deeplink = url;
+    }
+}
+
+void VsDeeplink::checkForInstance()
+{
+    // Create socket
+    m_instanceCheckSocket.reset(new QLocalSocket(this));
+    QObject::connect(m_instanceCheckSocket.data(), &QLocalSocket::connected, this,
+                     &VsDeeplink::connectionReceived, Qt::QueuedConnection);
+    // Create instanceServer to prevent new instances from being created
+    void (QLocalSocket::*errorFunc)(QLocalSocket::LocalSocketError);
+#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0))
+    errorFunc = &QLocalSocket::error;
+#else
+    errorFunc = &QLocalSocket::errorOccurred;
+#endif
+    QObject::connect(m_instanceCheckSocket.data(), errorFunc, this,
+                     &VsDeeplink::connectionFailed);
+    // Check for existing instance
+    m_instanceCheckSocket->connectToServer("jacktripExists");
+}
+
+void VsDeeplink::connectionReceived()
+{
+    // another jacktrip instance is running
+    if (!m_deeplink.isEmpty()) {
+        // pass deeplink to existing instance before quitting
+        QString deeplinkStr   = m_deeplink.toString();
+        QByteArray baDeeplink = deeplinkStr.toLocal8Bit();
+        qint64 writeBytes     = m_instanceCheckSocket->write(baDeeplink);
+        if (writeBytes < 0) {
+            qDebug() << "sending deeplink failed";
+        } else {
+            qDebug() << "Sent deeplink request to remote instance";
+        }
+
+        // make sure it isn't processed again
+        m_deeplink.clear();
+
+        // End process if another instance exists
+        m_readyToExit = true;
+    }
+
+    m_instanceCheckSocket->flush();
+    m_instanceCheckSocket->disconnectFromServer();  // remove next
+
+    // let main thread know we are finished
+    m_isReady = true;
+    emit signalIsReady();
+}
+
+void VsDeeplink::connectionFailed(QLocalSocket::LocalSocketError socketError)
+{
+    switch (socketError) {
+    case QLocalSocket::ServerNotFoundError:
+    case QLocalSocket::SocketTimeoutError:
+    case QLocalSocket::ConnectionRefusedError:
+        // no other jacktrip instance is running, so we will take over handling deep links
+        qDebug() << "Listening for deep link requests";
+        m_instanceServer.reset(new QLocalServer(this));
+        m_instanceServer->setSocketOptions(QLocalServer::WorldAccessOption);
+        m_instanceServer->listen("jacktripExists");
+        QObject::connect(m_instanceServer.data(), &QLocalServer::newConnection, this,
+                         &VsDeeplink::handleDeeplinkRequest, Qt::QueuedConnection);
+        break;
+    case QLocalSocket::PeerClosedError:
+        break;
+    default:
+        qDebug() << m_instanceCheckSocket->errorString();
+    }
+
+    // let main thread know we are finished
+    m_isReady = true;
+    emit signalIsReady();
+}
+
+void VsDeeplink::handleDeeplinkRequest()
+{
+    while (m_instanceServer->hasPendingConnections()) {
+        // Receive URL from 2nd instance
+        QLocalSocket* connectedSocket = m_instanceServer->nextPendingConnection();
+
+        if (!connectedSocket->waitForConnected()) {
+            qDebug() << "Never received connection";
+            return;
+        }
+
+        if (!connectedSocket->waitForReadyRead()) {
+            qDebug() << "Never ready to read";
+            if (!(connectedSocket->bytesAvailable() > 0)) {
+                qDebug() << "Not ready and no bytes available";
+                return;
+            }
+        }
+
+        if (connectedSocket->bytesAvailable() < (int)sizeof(quint16)) {
+            qDebug() << "no bytes available";
+            break;
+        }
+
+        QByteArray in(connectedSocket->readAll());
+        QString urlString(in);
+        handleUrl(urlString);
+    }
+}
+
+QUrl VsDeeplink::parseDeeplink(QCoreApplication* app)
+{
+    // Parse command line for deep link
+    QCommandLineParser parser;
+    QCommandLineOption deeplinkOption(QStringList() << QStringLiteral("deeplink"));
+    deeplinkOption.setValueName(QStringLiteral("deeplink"));
+    parser.addOption(deeplinkOption);
+    parser.parse(app->arguments());
+    if (parser.isSet(deeplinkOption)) {
+        return QUrl(parser.value(deeplinkOption));
+    } else {
+        return QUrl("");
+    }
+}
+
+void VsDeeplink::setUrlScheme()
+{
+#ifdef _WIN32
+    // Set url scheme in registry
+    QString path = QDir::toNativeSeparators(qApp->applicationFilePath());
+
+    QSettings set("HKEY_CURRENT_USER\\Software\\Classes", QSettings::NativeFormat);
+    set.beginGroup("jacktrip");
+    set.setValue("Default", "URL:JackTrip Protocol");
+    set.setValue("DefaultIcon/Default", path);
+    set.setValue("URL Protocol", "");
+    set.setValue("shell/open/command/Default",
+                 QString("\"%1\"").arg(path) + " --gui --deeplink \"%1\"");
+    set.endGroup();
+#endif
+}
diff --git a/src/gui/vsDeeplink.h b/src/gui/vsDeeplink.h
new file mode 100644 (file)
index 0000000..789ca01
--- /dev/null
@@ -0,0 +1,116 @@
+//*****************************************************************
+/*
+  JackTrip: A System for High-Quality Audio Network Performance
+  over the Internet
+
+  Copyright (c) 2008-2023 Juan-Pablo Caceres, Chris Chafe.
+  SoundWIRE group at CCRMA, Stanford University.
+
+  Permission is hereby granted, free of charge, to any person
+  obtaining a copy of this software and associated documentation
+  files (the "Software"), to deal in the Software without
+  restriction, including without limitation the rights to use,
+  copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the
+  Software is furnished to do so, subject to the following
+  conditions:
+
+  The above copyright notice and this permission notice shall be
+  included in all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+  OTHER DEALINGS IN THE SOFTWARE.
+*/
+//*****************************************************************
+
+/**
+ * \file vsDeeplink.h
+ * \author Mike Dickey, based on code by Aaron Wyatt and Matt Horton
+ * \date August 2023
+ */
+
+#ifndef __VSDEEPLINK_H__
+#define __VSDEEPLINK_H__
+
+#include <QCoreApplication>
+#include <QLocalServer>
+#include <QLocalSocket>
+#include <QScopedPointer>
+#include <QUrl>
+
+class VsDeeplink : public QObject
+{
+    Q_OBJECT
+
+   public:
+    // construct with an instance of the application, to parse command line args
+    VsDeeplink(QCoreApplication* app);
+
+    // virtual destructor since it inherits from QObject
+    // this is used to unregister url handler
+    virtual ~VsDeeplink();
+
+    // blocks main thread until local socket server is ready
+    // returns true if a deeplink was handled and we should exit now
+    bool waitForReady();
+
+    // used to let us know VirtualStudio is ready to process deeplink signals
+    void readyForSignals();
+
+    // returns deeplink extracted from command line, if any
+    const QUrl& getDeeplink() const { return m_deeplink; }
+
+   signals:
+
+    // signalIsReady is emitted when the local socket server is ready
+    void signalIsReady();
+
+    // signalDeeplink is emitted when we want the local instance to process a deeplink
+    void signalDeeplink(const QUrl& url);
+
+   private slots:
+
+    // handleUrl is called to trigger processing of a deeplink
+    void handleUrl(const QUrl& url);
+
+    // checks to see if another instance of jacktrip is available to process requests.
+    // if there is, this will send any command line deeplinks to it and exit.
+    // if there isn't, this will start listening for requests.
+    void checkForInstance();
+
+    // called if a connection was established with another instance of VS
+    void connectionReceived();
+
+    // called if unable to connect to another instance of VS
+    void connectionFailed(QLocalSocket::LocalSocketError socketError);
+
+    // called by local socket server to process deeplink requests
+    void handleDeeplinkRequest();
+
+   private:
+    // return string from parsing a deep link request
+    static QUrl parseDeeplink(QCoreApplication* app);
+
+    // sets url scheme for windows machines; does nothing on other platforms
+    static void setUrlScheme();
+
+    // used to check if there is a virtual studio instance already running
+    QScopedPointer<QLocalSocket> m_instanceCheckSocket;
+
+    // used to listen for deeplink requests via local socket connections
+    QScopedPointer<QLocalServer> m_instanceServer;
+
+    // used to synchronize with main thread at startup
+    bool m_isReady         = false;
+    bool m_readyForSignals = false;
+    bool m_readyToExit     = false;
+    QUrl m_deeplink;
+};
+
+#endif  // __VSDEEPLINK_H__
index 4c2365440dd934b3980d4d7222b35b69fb431e98..d3717be8a4f588e14c369fabf4d68e94f68c3a40 100644 (file)
 
 #include "vsDevice.h"
 
-#include <QDebug>
+#include <QEventLoop>
 
 // Constructor
-VsDevice::VsDevice(VsAuth* auth, VsApi* api, QObject* parent)
-    : QObject(parent), m_auth(auth), m_api(api), m_sendVolumeTimer(this)
+VsDevice::VsDevice(QSharedPointer<VsAuth>& auth, QSharedPointer<VsApi>& api,
+                   QSharedPointer<VsAudio>& audio, QObject* parent)
+    : QObject(parent)
+    , m_auth(auth)
+    , m_api(api)
+    , m_audioConfigPtr(audio)
+    , m_sendVolumeTimer(this)
 {
     QSettings settings;
     settings.beginGroup(QStringLiteral("VirtualStudio"));
@@ -50,86 +55,22 @@ VsDevice::VsDevice(VsAuth* auth, VsApi* api, QObject* parent)
     m_appUUID   = settings.value(QStringLiteral("AppUUID"), "").toString();
     m_appID     = settings.value(QStringLiteral("AppID"), "").toString();
     settings.endGroup();
-    settings.beginGroup(QStringLiteral("Audio"));
-    m_captureVolume =
-        (float)settings.value(QStringLiteral("InMultiplier"), 1.0).toDouble();
-    m_captureMute = settings.value(QStringLiteral("InMuted"), false).toBool();
-    m_playbackVolume =
-        (float)settings.value(QStringLiteral("OutMultiplier"), 1.0).toDouble();
-    m_playbackMute = settings.value(QStringLiteral("OutMuted"), false).toBool();
-    m_monitorVolume =
-        (float)settings.value(QStringLiteral("MonMultiplier"), 0).toDouble();
-    settings.endGroup();
+
+    if (!m_appID.isEmpty()) {
+        std::cout << "Device ID: " << m_appID.toStdString() << std::endl;
+    }
 
     m_sendVolumeTimer.setSingleShot(true);
     connect(&m_sendVolumeTimer, &QTimer::timeout, this, &VsDevice::sendLevels);
 
     // Set server levels to stored versions
-    QJsonObject json = {
-        {QLatin1String("captureVolume"), (int)(m_captureVolume * 100.0)},
-        {QLatin1String("captureMute"), m_captureMute},
-        {QLatin1String("playbackVolume"), (int)(m_playbackVolume * 100.0)},
-        {QLatin1String("playbackMute"), m_playbackMute},
-        {QLatin1String("monitorVolume"), (int)(m_monitorVolume * 100.0)}};
-    QJsonDocument request = QJsonDocument(json);
-
-    QNetworkReply* reply = m_api->updateDevice(m_appID, request.toJson());
-    connect(reply, &QNetworkReply::finished, this, [=]() {
-        // Got error
-        if (reply->error() != QNetworkReply::NoError) {
-            QVariant statusCode =
-                reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
-            if (!statusCode.isValid()) {
-                std::cout << "Error: " << reply->errorString().toStdString() << std::endl;
-                reply->deleteLater();
-                return;
-            }
-        } else {
-            QByteArray response       = reply->readAll();
-            QJsonDocument deviceState = QJsonDocument::fromJson(response);
-
-            // capture (input) volume
-            m_captureVolume =
-                (float)(deviceState.object()[QStringLiteral("captureVolume")].toDouble()
-                        / 100.0);
-            m_captureMute = deviceState.object()[QStringLiteral("captureMute")].toBool();
-            emit updatedCaptureVolumeFromServer(m_captureVolume);
-            emit updatedCaptureMuteFromServer(m_captureMute);
-
-            // playback (output) volume
-            m_playbackVolume =
-                (float)(deviceState.object()[QStringLiteral("playbackVolume")].toDouble()
-                        / 100.0);
-            m_playbackMute =
-                deviceState.object()[QStringLiteral("playbackMute")].toBool();
-            emit updatedPlaybackVolumeFromServer(m_playbackVolume);
-            emit updatedPlaybackMuteFromServer(m_playbackMute);
-
-            // monitor volume
-            m_monitorVolume =
-                (float)(deviceState.object()[QStringLiteral("monitorVolume")].toDouble()
-                        / 100.0);
-            emit updatedMonitorVolume(m_monitorVolume);
-        }
-
-        QSettings settings;
-        settings.beginGroup(QStringLiteral("Audio"));
-        settings.setValue(QStringLiteral("InMultiplier"), m_captureVolume);
-        settings.setValue(QStringLiteral("InMuted"), m_captureMute);
-        settings.setValue(QStringLiteral("OutMultiplier"), m_playbackVolume);
-        settings.setValue(QStringLiteral("OutMuted"), m_playbackMute);
-        settings.setValue(QStringLiteral("MonMultiplier"), m_monitorVolume);
-        settings.endGroup();
-
-        reply->deleteLater();
-    });
+    sendLevels();
 }
 
 VsDevice::~VsDevice()
 {
     m_sendVolumeTimer.stop();
-    stopJackTrip();
-    stopPinger();
+    stopJackTrip(false);
 }
 
 // registerApp idempotently registers an emulated device belonging to the current user
@@ -141,7 +82,6 @@ void VsDevice::registerApp()
 
     // check if device exists
     QNetworkReply* reply = m_api->getDevice(m_appID);
-    ;
     connect(reply, &QNetworkReply::finished, this, [=]() {
         // Got error
         if (reply->error() != QNetworkReply::NoError) {
@@ -188,57 +128,37 @@ void VsDevice::registerApp()
 // removeApp deletes the emulated device
 void VsDevice::removeApp()
 {
-    if (m_appID == "") {
+    if (m_appID.isEmpty()) {
         return;
     }
 
     QNetworkReply* reply = m_api->deleteDevice(m_appID);
-    connect(reply, &QNetworkReply::finished, this, [=]() {
-        if (reply->error() != QNetworkReply::NoError) {
-            std::cout << "Error: " << reply->errorString().toStdString() << std::endl;
-            reply->deleteLater();
-            return;
-        } else {
-            m_appID.clear();
-            m_appUUID.clear();
-            m_apiPrefix.clear();
-            m_apiSecret.clear();
+    QEventLoop loop;
+    connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
+    loop.exec();
 
-            QSettings settings;
-            settings.beginGroup(QStringLiteral("VirtualStudio"));
-            settings.remove(QStringLiteral("AppID"));
-            settings.remove(QStringLiteral("AppUUID"));
-            settings.remove(QStringLiteral("ApiPrefix"));
-            settings.remove(QStringLiteral("ApiSecret"));
-            settings.endGroup();
-        }
+    if (reply->error() != QNetworkReply::NoError) {
+        std::cout << "Error: " << reply->errorString().toStdString() << std::endl;
+    } else {
+        m_appID.clear();
+        m_appUUID.clear();
+        m_apiPrefix.clear();
+        m_apiSecret.clear();
 
-        reply->deleteLater();
-    });
+        QSettings settings;
+        settings.beginGroup(QStringLiteral("VirtualStudio"));
+        settings.remove(QStringLiteral("AppID"));
+        settings.remove(QStringLiteral("AppUUID"));
+        settings.remove(QStringLiteral("ApiPrefix"));
+        settings.remove(QStringLiteral("ApiSecret"));
+        settings.endGroup();
+    }
+    reply->deleteLater();
 }
 
 // sendHeartbeat is reponsible for sending liveness heartbeats to the API
 void VsDevice::sendHeartbeat()
 {
-    if (m_webSocket == nullptr) {
-        m_webSocket =
-            new VsWebSocket(QUrl(QStringLiteral("wss://%1/api/devices/%2/heartbeat")
-                                     .arg(m_api->getApiHost(), m_appID)),
-                            m_auth->accessToken(), m_apiPrefix, m_apiSecret);
-        connect(m_webSocket, &VsWebSocket::textMessageReceived, this,
-                &VsDevice::onTextMessageReceived);
-    }
-
-    if (enabled()) {
-        // When the device is connected to a server, use the underlying wss connection
-        if (!m_webSocket->isValid()) {
-            m_webSocket->openSocket();
-        }
-    } else {
-        // When the device is not connected to a server, use the standard API
-        m_webSocket->closeSocket();
-    }
-
     QString now = QDateTime::currentDateTimeUtc().toString(Qt::ISODate);
 
     QJsonObject json = {
@@ -251,7 +171,7 @@ void VsDevice::sendHeartbeat()
     };
 
     // Add stats to heartbeat body
-    if (m_pinger != nullptr) {
+    if (!m_pinger.isNull()) {
         VsPinger::PingStat stats = m_pinger->getPingStats();
 
         // API server expects RTTs to be in int64 nanoseconds, so we must convert
@@ -264,6 +184,8 @@ void VsDevice::sendHeartbeat()
         json.insert(QLatin1String("max_rtt"), (qint64)(stats.maxRtt * ns_per_ms));
         json.insert(QLatin1String("avg_rtt"), (qint64)(stats.avgRtt * ns_per_ms));
         json.insert(QLatin1String("stddev_rtt"), (qint64)(stats.stdDevRtt * ns_per_ms));
+        json.insert(QLatin1String("high_latency"),
+                    m_audioConfigPtr->getHighLatencyFlag());
 
         // For the internal application UI, ms will suffice. No conversion needed
         QJsonObject pingStats = {};
@@ -274,48 +196,24 @@ void VsDevice::sendHeartbeat()
         pingStats.insert(QLatin1String("avgRtt"), ((int)(10 * stats.avgRtt)) / 10.0);
         pingStats.insert(QLatin1String("stdDevRtt"),
                          ((int)(10 * stats.stdDevRtt)) / 10.0);
+        pingStats.insert(QLatin1String("highLatency"),
+                         m_audioConfigPtr->getHighLatencyFlag());
         emit updateNetworkStats(pingStats);
     }
 
     QJsonDocument request = QJsonDocument(json);
 
-    if (m_webSocket->isValid()) {
+    if (!m_deviceSocketPtr.isNull() && m_deviceSocketPtr->isValid()) {
         // Send heartbeat via websocket
-        m_webSocket->sendMessage(request.toJson());
+        m_deviceSocketPtr->sendMessage(request.toJson());
     } else {
-        // Send heartbeat via POST API
-        QNetworkReply* reply = m_api->postDeviceHeartbeat(m_appID, request.toJson());
-        connect(reply, &QNetworkReply::finished, this, [=]() {
-            if (reply->error() != QNetworkReply::NoError) {
-                std::cout << "Error: " << reply->errorString().toStdString() << std::endl;
-                reply->deleteLater();
-                return;
+        if (enabled()) {
+            if (m_deviceSocketPtr.isNull()) {
+                qDebug() << "Heartbeat not sent";
             } else {
-                QJsonDocument response = QJsonDocument::fromJson(reply->readAll());
-                reconcileAgentConfig(response);
+                qDebug() << "Heartbeat not sent; trying to reopen socket";
+                m_deviceSocketPtr->openSocket();
             }
-
-            reply->deleteLater();
-        });
-    }
-}
-
-bool VsDevice::reconnect()
-{
-    return m_reconnect;
-}
-
-void VsDevice::setReconnect(bool reconnect)
-{
-    m_reconnect = reconnect;
-    if (reconnect) {
-        stopPinger();
-        if (m_webSocket != nullptr && m_webSocket->isValid()) {
-            m_webSocket->closeSocket();
-        }
-        if (!m_jackTrip.isNull()) {
-            m_jackTrip->stop();
-            m_jackTrip.reset();
         }
     }
 }
@@ -325,41 +223,46 @@ bool VsDevice::hasTerminated()
     return m_jackTrip.isNull();
 }
 
-// setServerId updates the emulated device with the provided serverId
-void VsDevice::setServerId(QString serverId)
+// updateState updates the emulated device with the provided state
+void VsDevice::updateState(const QString& serverId)
 {
+    m_deviceAgentConfig.insert("serverId", serverId);
+    m_deviceAgentConfig.insert("enabled", !serverId.isEmpty());
     QJsonObject json = {
         {QLatin1String("serverId"), serverId},
+        {QLatin1String("enabled"), !serverId.isEmpty()},
     };
     QJsonDocument request = QJsonDocument(json);
     QNetworkReply* reply  = m_api->updateDevice(m_appID, request.toJson());
     connect(reply, &QNetworkReply::finished, this, [=]() {
         if (reply->error() != QNetworkReply::NoError) {
             std::cout << "Error: " << reply->errorString().toStdString() << std::endl;
-            reply->deleteLater();
-            return;
         }
-        m_deviceAgentConfig.insert("serverId", serverId);
         reply->deleteLater();
     });
 }
 
 void VsDevice::sendLevels()
 {
+    if (m_appID.isEmpty()) {
+        return;
+    }
     // Add latest volume and mute values to heartbeat body
-    QJsonObject json = {
-        {QLatin1String("captureVolume"), (int)(m_captureVolume * 100.0)},
-        {QLatin1String("captureMute"), m_captureMute},
-        {QLatin1String("playbackVolume"), (int)(m_playbackVolume * 100.0)},
-        {QLatin1String("playbackMute"), m_playbackMute},
-        {QLatin1String("monitorVolume"), (int)(m_monitorVolume * 100.0)}};
+    QJsonObject json = {{QLatin1String("version"), QLatin1String(gVersion)},
+                        {QLatin1String("captureVolume"),
+                         (int)(m_audioConfigPtr->getInputVolume() * 100.0)},
+                        {QLatin1String("captureMute"), m_audioConfigPtr->getInputMuted()},
+                        {QLatin1String("playbackVolume"),
+                         (int)(m_audioConfigPtr->getOutputVolume() * 100.0)},
+                        {QLatin1String("playbackMute"), false},
+                        {QLatin1String("monitorVolume"),
+                         (int)(m_audioConfigPtr->getMonitorVolume() * 100.0)}};
+
     QJsonDocument request = QJsonDocument(json);
     QNetworkReply* reply  = m_api->updateDevice(m_appID, request.toJson());
     connect(reply, &QNetworkReply::finished, this, [=]() {
         if (reply->error() != QNetworkReply::NoError) {
             std::cout << "Error: " << reply->errorString().toStdString() << std::endl;
-            reply->deleteLater();
-            return;
         }
         reply->deleteLater();
     });
@@ -412,16 +315,30 @@ JackTrip* VsDevice::initJackTrip(
     m_jackTrip->setPeerHandshakePort(studioInfo->port());
 
     QObject::connect(m_jackTrip.data(), &JackTrip::signalProcessesStopped, this,
-                     &VsDevice::terminateJackTrip, Qt::QueuedConnection);
+                     &VsDevice::handleJackTripError, Qt::QueuedConnection);
     QObject::connect(m_jackTrip.data(), &JackTrip::signalError, this,
-                     &VsDevice::terminateJackTrip, Qt::QueuedConnection);
+                     &VsDevice::handleJackTripError, Qt::QueuedConnection);
 
     return m_jackTrip.data();
 }
 
 // startJackTrip starts the current jacktrip process if applicable
-void VsDevice::startJackTrip()
+void VsDevice::startJackTrip(const VsServerInfo& studioInfo)
 {
+    m_stopping = false;
+    updateState(studioInfo.id());
+
+    // setup websocket listener
+    m_deviceSocketPtr.reset(
+        new VsWebSocket(QUrl(QStringLiteral("wss://%1/api/devices/%2/heartbeat")
+                                 .arg(m_api->getApiHost(), m_appID)),
+                        m_auth->accessToken(), m_apiPrefix, m_apiSecret));
+    connect(m_deviceSocketPtr.get(), &VsWebSocket::textMessageReceived, this,
+            &VsDevice::onTextMessageReceived);
+    connect(m_deviceSocketPtr.get(), &VsWebSocket::disconnected, this,
+            &VsDevice::restartDeviceSocket);
+    m_deviceSocketPtr->openSocket();
+
     if (!m_jackTrip.isNull()) {
 #ifdef WAIRTOHUB                      // WAIR
         m_jackTrip->startProcess(0);  // for WAIR compatibility, ID in jack client name
@@ -429,16 +346,38 @@ void VsDevice::startJackTrip()
         m_jackTrip->startProcess();
 #endif  // endwhere
     }
+
+    // intialize the pinger used to generate network latency statistics for
+    // Virtual Studio
+    QString host = studioInfo.sessionId();
+    host.append(QString::fromStdString(".jacktrip.cloud"));
+    m_pinger.reset(new VsPinger(QString::fromStdString("wss"), host,
+                                QString::fromStdString("/ping")));
 }
 
 // stopJackTrip stops the current jacktrip process if applicable
-void VsDevice::stopJackTrip()
+void VsDevice::stopJackTrip(bool isReconnecting)
 {
+    // check if another process has already initiated
+    QMutexLocker stopLock(&m_stopMutex);
+    if (m_stopping)
+        return;
+    m_stopping = true;
+
+    // only clear state if we are not reconnecting
+    if (!isReconnecting)
+        updateState("");
+
+    // stop the Virtual Studio pinger
+    if (!m_pinger.isNull()) {
+        m_pinger->stop();
+        m_pinger->unsetToken();
+    }
+
     if (!m_jackTrip.isNull()) {
-        if (m_webSocket != nullptr && m_webSocket->isValid()) {
-            m_webSocket->closeSocket();
+        if (!m_deviceSocketPtr.isNull()) {
+            m_deviceSocketPtr->closeSocket();
         }
-        setServerId("");
         m_jackTrip->stop();
         m_jackTrip.reset();
     }
@@ -457,147 +396,63 @@ void VsDevice::reconcileAgentConfig(QJsonDocument newState)
         // if currently enabled but new config is not enabled, disconnect immediately
         if (enabled() && it.key() == "enabled" && !it.value().toBool()
             && !m_jackTrip.isNull()) {
-            stopJackTrip();
+            stopJackTrip(false);
         }
         m_deviceAgentConfig.insert(it.key(), it.value());
     }
 }
 
-// initPinger intializes the pinger used to generate network latency statistics for
-// Virtual Studio
-VsPinger* VsDevice::startPinger(VsServerInfo* studioInfo)
+// syncDeviceSettings updates volume/mute controls against the API
+void VsDevice::syncDeviceSettings()
 {
-    QString id   = studioInfo->id();
-    QString host = studioInfo->sessionId();
-    host.append(QString::fromStdString(".jacktrip.cloud"));
-
-    m_pinger = new VsPinger(QString::fromStdString("wss"), host,
-                            QString::fromStdString("/ping"));
-
-    return m_pinger;
-}
-
-// stopPinger stops the Virtual Studio pinger
-void VsDevice::stopPinger()
-{
-    if (m_pinger != nullptr) {
-        m_pinger->stop();
-        m_pinger->unsetToken();
-    }
-}
-
-// updateCaptureVolume sets VsDevice's capture (input) volume to the provided float
-void VsDevice::updateCaptureVolume(float multiplier)
-{
-    if (multiplier == m_captureVolume) {
-        return;
-    }
-    m_captureVolume = multiplier;
-    m_sendVolumeTimer.start(100);
-}
-
-// updateCaptureMute sets VsDevice's capture (input) mute to the provided boolean
-void VsDevice::updateCaptureMute(bool muted)
-{
-    if (muted == m_captureMute) {
-        return;
-    }
-    m_captureMute = muted;
     m_sendVolumeTimer.start(100);
 }
 
-// updatePlaybackVolume sets VsDevice's playback (output) volume to the provided float
-void VsDevice::updatePlaybackVolume(float multiplier)
+// handleJackTripError is a slot intended to be triggered on jacktrip process signals
+void VsDevice::handleJackTripError()
 {
-    if (multiplier == m_playbackVolume) {
-        return;
-    }
-    m_playbackVolume = multiplier;
-    m_sendVolumeTimer.start(100);
-}
-
-// updatePlaybackMute sets VsDevice's playback (output) mute to the provided boolean
-void VsDevice::updatePlaybackMute(bool muted)
-{
-    if (muted == m_playbackMute) {
-        return;
-    }
-    m_playbackMute = muted;
-    m_sendVolumeTimer.start(100);
-}
-
-// updateMonitorVolume sets VsDevice's monitor to the provided float
-void VsDevice::updateMonitorVolume(float multiplier)
-{
-    if (multiplier == m_monitorVolume) {
-        return;
-    }
-    m_monitorVolume = multiplier;
-    m_sendVolumeTimer.start(100);
-}
-
-// terminateJackTrip is a slot intended to be triggered on jacktrip process signals
-void VsDevice::terminateJackTrip()
-{
-    if (!enabled()) {
-        setServerId("");
-    }
-    if (!m_jackTrip.isNull()) {
-        m_jackTrip.reset();
-    }
+    stopJackTrip(false);
 }
 
 // onTextMessageReceived is a slot intended to be triggered by new incoming WSS messages
 void VsDevice::onTextMessageReceived(const QString& message)
 {
     QJsonDocument newState = QJsonDocument::fromJson(message.toUtf8());
+    QJsonObject newObj     = newState.object();
 
     // We have a heartbeat from which we can read the studio auth token
     // Use it to set up and start the pinger connection
     QString token = newState["authToken"].toString();
-    if (m_pinger != nullptr && !m_pinger->active()) {
+    if (!m_pinger.isNull() && !m_pinger->active()) {
         m_pinger->setToken(token);
         m_pinger->start();
     }
 
     // capture (input) volume
-    bool newMute    = newState["captureMute"].toBool();
-    float newVolume = (float)(newState["captureVolume"].toDouble() / 100.0);
-
-    if (newVolume != m_captureVolume) {
-        m_captureVolume = newVolume;
-        emit updatedCaptureVolumeFromServer(m_captureVolume);
-    }
-
-    if (newMute != m_captureMute) {
-        m_captureMute = newMute;
-        emit updatedCaptureMuteFromServer(m_captureMute);
-    }
+    m_audioConfigPtr->setInputVolume(
+        (float)(newObj[QStringLiteral("captureVolume")].toDouble() / 100.0));
+    m_audioConfigPtr->setInputMuted(newObj[QStringLiteral("captureMute")].toBool());
 
     // playback (output) volume
-    newMute   = newState["playbackMute"].toBool();
-    newVolume = (float)(newState["playbackVolume"].toDouble() / 100.0);
-
-    if (newVolume != m_playbackVolume) {
-        m_playbackVolume = newVolume;
-        emit updatedPlaybackVolumeFromServer(m_playbackVolume);
-    }
-
-    if (newMute != m_playbackMute) {
-        m_playbackMute = newMute;
-        emit updatedPlaybackMuteFromServer(m_playbackMute);
-    }
+    m_audioConfigPtr->setOutputVolume(
+        (float)(newObj[QStringLiteral("playbackVolume")].toDouble() / 100.0));
 
     // monitor volume
-    newVolume = (float)(newState["monitorVolume"].toDouble() / 100.0);
-    if (newVolume != m_monitorVolume) {
-        m_monitorVolume = newVolume;
-        emit updatedMonitorVolume(m_monitorVolume);
-    }
+    m_audioConfigPtr->setMonitorVolume(
+        (float)(newObj[QStringLiteral("monitorVolume")].toDouble() / 100.0));
 
     reconcileAgentConfig(newState);
 }
 
+void VsDevice::restartDeviceSocket()
+{
+    if (m_deviceAgentConfig[QStringLiteral("serverId")].toString() != "") {
+        if (!m_deviceSocketPtr.isNull()) {
+            m_deviceSocketPtr->openSocket();
+        }
+    }
+}
+
 // registerJTAsDevice creates the emulated device belonging to the current user
 void VsDevice::registerJTAsDevice()
 {
@@ -667,13 +522,30 @@ void VsDevice::registerJTAsDevice()
             return;
         } else {
             QJsonDocument response = QJsonDocument::fromJson(reply->readAll());
+            QJsonObject newObject  = response.object();
+
+            m_appID = newObject[QStringLiteral("id")].toString();
+
+            // capture (input) volume
+            m_audioConfigPtr->setInputVolume(
+                (float)(newObject[QStringLiteral("captureVolume")].toDouble() / 100.0));
+            m_audioConfigPtr->setInputMuted(
+                newObject[QStringLiteral("captureMute")].toBool());
+
+            // playback (output) volume
+            m_audioConfigPtr->setOutputVolume(
+                (float)(newObject[QStringLiteral("playbackVolume")].toDouble() / 100.0));
+
+            // monitor volume
+            m_audioConfigPtr->setMonitorVolume(
+                (float)(newObject[QStringLiteral("monitorVolume")].toDouble() / 100.0));
 
-            m_appID = response.object()[QStringLiteral("id")].toString();
             QSettings settings;
             settings.beginGroup(QStringLiteral("VirtualStudio"));
             settings.setValue(QStringLiteral("AppID"), m_appID);
             settings.endGroup();
 
+            std::cout << "Device ID: " << m_appID.toStdString() << std::endl;
             sendHeartbeat();
         }
 
index 298233c63f3909368bc9a02c91475cc57e09583c..3c8298248835fec17e43937bda7df9fa5bcd8c57 100644 (file)
 #ifndef VSDEVICE_H
 #define VSDEVICE_H
 
+#include <QMutex>
 #include <QObject>
 #include <QString>
 #include <QTimer>
 #include <QUuid>
-#include <QtNetworkAuth>
 #include <QtWebSockets>
 
 #include "../JackTrip.h"
 #include "../jacktrip_globals.h"
 #include "vsApi.h"
+#include "vsAudio.h"
 #include "vsAuth.h"
 #include "vsConstants.h"
 #include "vsPinger.h"
@@ -60,74 +61,60 @@ class VsDevice : public QObject
 
    public:
     // Constructor
-    explicit VsDevice(VsAuth* auth, VsApi* api, QObject* parent = nullptr);
+    explicit VsDevice(QSharedPointer<VsAuth>& auth, QSharedPointer<VsApi>& api,
+                      QSharedPointer<VsAudio>& audio, QObject* parent = nullptr);
     virtual ~VsDevice();
 
     // Public functions
     void registerApp();
     void removeApp();
     void sendHeartbeat();
-    bool reconnect();
-    void setReconnect(bool reconnect);
     bool hasTerminated();
-    void setServerId(QString studioID);
     JackTrip* initJackTrip(bool useRtAudio, std::string input, std::string output,
                            int baseInputChannel, int numChannelsIn, int baseOutputChannel,
                            int numChannelsOut, int inputMixMode, int bufferSize,
                            int bufferStrategy, VsServerInfo* studioInfo);
-    void startJackTrip();
-    void stopJackTrip();
+    void startJackTrip(const VsServerInfo& studioInfo);
+    void stopJackTrip(bool isReconnecting = false);
     void reconcileAgentConfig(QJsonDocument newState);
 
-    VsPinger* startPinger(VsServerInfo* studioInfo);
-    void stopPinger();
-
    signals:
     void updateNetworkStats(QJsonObject stats);
-    void updatedCaptureVolumeFromServer(float multiplier);
-    void updatedCaptureMuteFromServer(bool muted);
-    void updatedPlaybackVolumeFromServer(float multiplier);
-    void updatedPlaybackMuteFromServer(bool muted);
-    void updatedMonitorVolume(float multiplier);
 
    public slots:
-    void updateCaptureVolume(float multiplier);
-    void updateCaptureMute(bool muted);
-    void updatePlaybackVolume(float multiplier);
-    void updatePlaybackMute(bool muted);
-    void updateMonitorVolume(float multiplier);
+    void syncDeviceSettings();
 
    private slots:
-    void terminateJackTrip();
+    void handleJackTripError();
     void onTextMessageReceived(const QString& message);
+    void restartDeviceSocket();
     void sendLevels();
 
    private:
+    void updateState(const QString& serverId);
     void registerJTAsDevice();
     bool enabled();
     int selectBindPort();
     QString randomString(int stringLength);
 
-    VsAuth* m_auth     = nullptr;
-    VsApi* m_api       = nullptr;
-    VsPinger* m_pinger = NULL;
+    QSharedPointer<VsAuth> m_auth;
+    QSharedPointer<VsApi> m_api;
+    QSharedPointer<VsAudio> m_audioConfigPtr;
+    QScopedPointer<VsPinger> m_pinger;
 
     QString m_appID;
     QString m_appUUID;
     QString m_token;
     QString m_apiPrefix;
     QString m_apiSecret;
+    QMutex m_stopMutex;
     QJsonObject m_deviceAgentConfig;
-    VsWebSocket* m_webSocket = NULL;
+    QScopedPointer<VsWebSocket> m_deviceSocketPtr;
     QScopedPointer<JackTrip> m_jackTrip;
     QRandomGenerator m_randomizer;
-    float m_captureVolume  = 1.0;
-    bool m_captureMute     = false;
-    float m_playbackVolume = 1.0;
-    bool m_playbackMute    = false;
-    float m_monitorVolume  = 0;
     QTimer m_sendVolumeTimer;
-    bool m_reconnect = false;
+    bool m_highLatencyFlag = false;
+    bool m_stopping        = false;
 };
 
 #endif  // VSDEVICE_H
index d586604888439923cb1e8636fbea0c705e01e08b..dbc17e5f9d8c4cf4831e4c9c2e6f0ace19b25c3b 100644 (file)
@@ -195,7 +195,7 @@ bool VsDeviceCodeFlow::processDeviceCodeNetworkReply(QNetworkReply* reply)
     m_verificationUriComplete =
         object.value(QLatin1String("verification_uri_complete")).toString();
     m_pollingInterval =
-        object.value(QLatin1String("interval")).toInt(5);  // default to 5s
+        object.value(QLatin1String("interval")).toInt(2);  // default to 2s
     m_deviceCodeValidityDuration =
         object.value(QLatin1String("expires_in")).toInt(900);  // default to 900s
 
diff --git a/src/gui/vsInit.cpp b/src/gui/vsInit.cpp
deleted file mode 100644 (file)
index b74f92b..0000000
+++ /dev/null
@@ -1,170 +0,0 @@
-//*****************************************************************
-/*
-  JackTrip: A System for High-Quality Audio Network Performance
-  over the Internet
-
-  Copyright (c) 2008-2023 Juan-Pablo Caceres, Chris Chafe.
-  SoundWIRE group at CCRMA, Stanford University.
-
-  Permission is hereby granted, free of charge, to any person
-  obtaining a copy of this software and associated documentation
-  files (the "Software"), to deal in the Software without
-  restriction, including without limitation the rights to use,
-  copy, modify, merge, publish, distribute, sublicense, and/or sell
-  copies of the Software, and to permit persons to whom the
-  Software is furnished to do so, subject to the following
-  conditions:
-
-  The above copyright notice and this permission notice shall be
-  included in all copies or substantial portions of the Software.
-
-  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
-  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
-  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-  OTHER DEALINGS IN THE SOFTWARE.
-*/
-//*****************************************************************
-
-/**
- * \file vsInit.cpp
- * \author Aaron Wyatt, based on code by Matt Horton
- * \date February 2023
- */
-
-#include "vsInit.h"
-
-#include <QCommandLineParser>
-#include <QDir>
-#include <QSettings>
-
-QString VsInit::parseDeeplink(QCoreApplication* app)
-{
-    // Parse command line for deep link
-    QCommandLineParser parser;
-    QCommandLineOption deeplinkOption(QStringList() << QStringLiteral("deeplink"));
-    deeplinkOption.setValueName(QStringLiteral("deeplink"));
-    parser.addOption(deeplinkOption);
-    parser.parse(app->arguments());
-    if (parser.isSet(deeplinkOption)) {
-        return parser.value(deeplinkOption);
-    } else {
-        return QLatin1String("");
-    }
-}
-
-void VsInit::checkForInstance(QString& deeplink)
-{
-    m_deeplink = deeplink;
-    // Create socket
-    m_instanceCheckSocket.reset(new QLocalSocket(this));
-    QObject::connect(m_instanceCheckSocket.data(), &QLocalSocket::connected, this,
-                     &VsInit::connectionReceived, Qt::QueuedConnection);
-    // Create instanceServer to prevent new instances from being created
-    void (QLocalSocket::*errorFunc)(QLocalSocket::LocalSocketError);
-#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0))
-    errorFunc = &QLocalSocket::error;
-#else
-    errorFunc = &QLocalSocket::errorOccurred;
-#endif
-    QObject::connect(m_instanceCheckSocket.data(), errorFunc, this,
-                     &VsInit::connectionFailed);
-    // Check for existing instance
-    m_instanceCheckSocket->connectToServer("jacktripExists");
-}
-
-#ifdef _WIN32
-void VsInit::setUrlScheme()
-{
-    // Set url scheme in registry
-    QString path = QDir::toNativeSeparators(qApp->applicationFilePath());
-
-    QSettings set("HKEY_CURRENT_USER\\Software\\Classes", QSettings::NativeFormat);
-    set.beginGroup("jacktrip");
-    set.setValue("Default", "URL:JackTrip Protocol");
-    set.setValue("DefaultIcon/Default", path);
-    set.setValue("URL Protocol", "");
-    set.setValue("shell/open/command/Default",
-                 QString("\"%1\"").arg(path) + " --gui --deeplink \"%1\"");
-    set.endGroup();
-}
-#endif
-
-void VsInit::connectionReceived()
-{
-    // pass deeplink to existing instance before quitting
-    if (!m_deeplink.isEmpty()) {
-        QByteArray baDeeplink = m_deeplink.toLocal8Bit();
-        qint64 writeBytes     = m_instanceCheckSocket->write(baDeeplink);
-        m_instanceCheckSocket->flush();
-        m_instanceCheckSocket->disconnectFromServer();  // remove next
-
-        if (writeBytes < 0) {
-            qDebug() << "sending deeplink failed";
-        }
-    }
-    // End process if instance exists
-    emit QCoreApplication::quit();
-}
-
-void VsInit::connectionFailed(QLocalSocket::LocalSocketError socketError)
-{
-    switch (socketError) {
-    case QLocalSocket::ServerNotFoundError:
-    case QLocalSocket::SocketTimeoutError:
-    case QLocalSocket::ConnectionRefusedError:
-        m_instanceServer.reset(new QLocalServer(this));
-        m_instanceServer->setSocketOptions(QLocalServer::WorldAccessOption);
-        m_instanceServer->listen("jacktripExists");
-        QObject::connect(m_instanceServer.data(), &QLocalServer::newConnection, this,
-                         &VsInit::responseReceived, Qt::QueuedConnection);
-        break;
-    case QLocalSocket::PeerClosedError:
-        break;
-    default:
-        qDebug() << m_instanceCheckSocket->errorString();
-    }
-}
-
-void VsInit::responseReceived()
-{
-    // This is the first instance. Bring it to the top.
-    if (!m_vs.isNull() && m_vs->vsModeActive()) {
-        m_vs->raiseToTop();
-    }
-    while (m_instanceServer->hasPendingConnections()) {
-        // Receive URL from 2nd instance
-        QLocalSocket* connectedSocket = m_instanceServer->nextPendingConnection();
-
-        if (!connectedSocket->waitForConnected()) {
-            qDebug() << "Never received connection";
-            return;
-        }
-
-        if (!connectedSocket->waitForReadyRead()) {
-            qDebug() << "Never ready to read";
-            if (!(connectedSocket->bytesAvailable() > 0)) {
-                qDebug() << "Not ready and no bytes available";
-                return;
-            }
-        }
-
-        if (connectedSocket->bytesAvailable() < (int)sizeof(quint16)) {
-            qDebug() << "no bytes available";
-            break;
-        }
-
-        QByteArray in(connectedSocket->readAll());
-        QString urlString(in);
-        QUrl url(urlString);
-
-        // Join studio using received URL
-        if (!m_vs.isNull() && m_vs->vsModeActive() && url.scheme() == "jacktrip"
-            && url.host() == "join") {
-            m_vs->setStudioToJoin(url);
-        }
-    }
-}
diff --git a/src/gui/vsInit.h b/src/gui/vsInit.h
deleted file mode 100644 (file)
index cfe7b2f..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-//*****************************************************************
-/*
-  JackTrip: A System for High-Quality Audio Network Performance
-  over the Internet
-
-  Copyright (c) 2008-2023 Juan-Pablo Caceres, Chris Chafe.
-  SoundWIRE group at CCRMA, Stanford University.
-
-  Permission is hereby granted, free of charge, to any person
-  obtaining a copy of this software and associated documentation
-  files (the "Software"), to deal in the Software without
-  restriction, including without limitation the rights to use,
-  copy, modify, merge, publish, distribute, sublicense, and/or sell
-  copies of the Software, and to permit persons to whom the
-  Software is furnished to do so, subject to the following
-  conditions:
-
-  The above copyright notice and this permission notice shall be
-  included in all copies or substantial portions of the Software.
-
-  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
-  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
-  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-  OTHER DEALINGS IN THE SOFTWARE.
-*/
-//*****************************************************************
-
-/**
- * \file vsInit.h
- * \author Aaron Wyatt, based on code by Matt Horton
- * \date February 2023
- */
-
-#ifndef __VSINIT_H__
-#define __VSINIT_H__
-
-#include <QCoreApplication>
-#include <QLocalServer>
-#include <QLocalSocket>
-#include <QScopedPointer>
-
-#include "virtualstudio.h"
-
-class VsInit : public QObject
-{
-    Q_OBJECT
-
-   public:
-    VsInit() = default;
-
-    static QString parseDeeplink(QCoreApplication* app);
-    void checkForInstance(QString& deeplink);
-    void setVs(QSharedPointer<VirtualStudio> vs) { m_vs = vs; }
-#ifdef _WIN32
-    static void setUrlScheme();
-#endif
-
-   private slots:
-    void connectionReceived();
-    void connectionFailed(QLocalSocket::LocalSocketError socketError);
-    void responseReceived();
-
-   private:
-    QScopedPointer<QLocalServer> m_instanceServer;
-    QScopedPointer<QLocalSocket> m_instanceCheckSocket;
-    QSharedPointer<VirtualStudio> m_vs;
-    QString m_deeplink;
-};
-
-#endif  // __VSINIT_H__
index 4f6cdcd5b717ea54f41da56267be273bde3a1a63..20dfdb3c4026d54bcc481455edc5f0271b7b6d6b 100644 (file)
 
 VsServerInfo::VsServerInfo(QObject* parent) : QObject(parent) {}
 
+VsServerInfo& VsServerInfo::operator=(const VsServerInfo& info)
+{
+    m_section     = info.m_section;
+    m_name        = info.m_name;
+    m_host        = info.m_host;
+    m_port        = info.m_port;
+    m_enabled     = info.m_enabled;
+    m_owner       = info.m_owner;
+    m_admin       = info.m_admin;
+    m_isManaged   = info.m_isManaged;
+    m_isPublic    = info.m_isPublic;
+    m_region      = info.m_region;
+    m_period      = info.m_period;
+    m_sampleRate  = info.m_sampleRate;
+    m_queueBuffer = info.m_queueBuffer;
+    m_bannerURL   = info.m_bannerURL;
+    m_id          = info.m_id;
+    m_sessionId   = info.m_sessionId;
+    m_status      = info.m_status;
+    m_cloudId     = info.m_cloudId;
+    m_inviteKey   = info.m_inviteKey;
+    return *this;
+}
+
 VsServerInfo::serverSectionT VsServerInfo::section()
 {
     return m_section;
index e1c945d68a6dbf16b63d0c79746e8ce16a90fa0b..fc524cd96af12329ae8973be3afc5fac12a20ae5 100644 (file)
@@ -69,6 +69,7 @@ class VsServerInfo : public QObject
     enum serverSectionT { YOUR_STUDIOS, SUBSCRIBED_STUDIOS, PUBLIC_STUDIOS };
 
     explicit VsServerInfo(QObject* parent = nullptr);
+    VsServerInfo& operator=(const VsServerInfo& info);
     ~VsServerInfo() override;
 
     serverSectionT section();
diff --git a/src/gui/vsUrlHandler.cpp b/src/gui/vsUrlHandler.cpp
deleted file mode 100644 (file)
index 8de8a0f..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-//*****************************************************************
-/*
-  JackTrip: A System for High-Quality Audio Network Performance
-  over the Internet
-
-  Copyright (c) 2008-2022 Juan-Pablo Caceres, Chris Chafe.
-  SoundWIRE group at CCRMA, Stanford University.
-
-  Permission is hereby granted, free of charge, to any person
-  obtaining a copy of this software and associated documentation
-  files (the "Software"), to deal in the Software without
-  restriction, including without limitation the rights to use,
-  copy, modify, merge, publish, distribute, sublicense, and/or sell
-  copies of the Software, and to permit persons to whom the
-  Software is furnished to do so, subject to the following
-  conditions:
-
-  The above copyright notice and this permission notice shall be
-  included in all copies or substantial portions of the Software.
-
-  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
-  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
-  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-  OTHER DEALINGS IN THE SOFTWARE.
-*/
-//*****************************************************************
-
-/**
- * \file vsUrlHandler.cpp
- * \author Matt Horton
- * \date June 2022
- */
-
-#include "vsUrlHandler.h"
-
-#include <QDebug>
-#include <iostream>
-
-void VsUrlHandler::handleUrl(const QUrl& url)
-{
-    emit joinUrlClicked(url);
-}
\ No newline at end of file
diff --git a/src/gui/vsUrlHandler.h b/src/gui/vsUrlHandler.h
deleted file mode 100644 (file)
index 1e85ff8..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-//*****************************************************************
-/*
-  JackTrip: A System for High-Quality Audio Network Performance
-  over the Internet
-
-  Copyright (c) 2008-2022 Juan-Pablo Caceres, Chris Chafe.
-  SoundWIRE group at CCRMA, Stanford University.
-
-  Permission is hereby granted, free of charge, to any person
-  obtaining a copy of this software and associated documentation
-  files (the "Software"), to deal in the Software without
-  restriction, including without limitation the rights to use,
-  copy, modify, merge, publish, distribute, sublicense, and/or sell
-  copies of the Software, and to permit persons to whom the
-  Software is furnished to do so, subject to the following
-  conditions:
-
-  The above copyright notice and this permission notice shall be
-  included in all copies or substantial portions of the Software.
-
-  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
-  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
-  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-  OTHER DEALINGS IN THE SOFTWARE.
-*/
-//*****************************************************************
-
-/**
- * \file vsUrlHandler.h
- * \author Matt Horton
- * \date June 2022
- */
-
-#ifndef VSURLHANDLER_H
-#define VSURLHANDLER_H
-
-#include <QDesktopServices>
-#include <QObject>
-#include <QSslError>
-#include <QString>
-#include <QUrl>
-
-class VsUrlHandler : public QObject
-{
-    Q_OBJECT
-
-   signals:
-    void joinUrlClicked(const QUrl& url);
-
-   public slots:
-    void handleUrl(const QUrl& url);
-};
-
-#endif  // VSURLHANDLER_H
index fa7b5f72e9a4c1944cb40f5d7aeac982ccadb478..d217cc865862f60e0e3e072779f11a9f6752989b 100644 (file)
@@ -49,19 +49,33 @@ VsWebSocket::VsWebSocket(const QUrl& url, QString token, QString apiPrefix,
     , m_apiPrefix(apiPrefix)
     , m_apiSecret(apiSecret)
 {
-    connect(&m_webSocket, &QWebSocket::connected, this, &VsWebSocket::onConnected);
-    connect(&m_webSocket, &QWebSocket::disconnected, this, &VsWebSocket::onClosed);
-    connect(&m_webSocket, QOverload<const QList<QSslError>&>::of(&QWebSocket::sslErrors),
-            this, &VsWebSocket::onSslErrors);
-    connect(&m_webSocket, QOverload<QAbstractSocket::SocketError>::of(&QWebSocket::error),
-            this, &VsWebSocket::onError);
-    connect(&m_webSocket, &QWebSocket::textMessageReceived, this,
+    m_webSocket.reset(new QWebSocket());
+    connect(m_webSocket.get(), &QWebSocket::disconnected, this,
+            &VsWebSocket::disconnected);
+    connect(m_webSocket.get(),
+            QOverload<const QList<QSslError>&>::of(&QWebSocket::sslErrors), this,
+            &VsWebSocket::onSslErrors);
+    connect(m_webSocket.get(),
+            QOverload<QAbstractSocket::SocketError>::of(&QWebSocket::error), this,
+            &VsWebSocket::onError);
+    connect(m_webSocket.get(), &QWebSocket::textMessageReceived, this,
             &VsWebSocket::textMessageReceived);
 }
 
+VsWebSocket::~VsWebSocket()
+{
+    if (isValid()) {
+        closeSocket();
+    }
+    if (!m_webSocket.isNull()) {
+        m_webSocket->disconnect();
+        m_webSocket.reset();
+    }
+}
+
 void VsWebSocket::openSocket()
 {
-    if (m_connected) {
+    if (isValid()) {
         return;
     }
 
@@ -75,49 +89,52 @@ void VsWebSocket::openSocket()
     req.setRawHeader(QByteArray("APIPrefix"), m_apiPrefix.toUtf8());
     req.setRawHeader(QByteArray("APISecret"), m_apiSecret.toUtf8());
 
-    m_webSocket.open(req);
+    if (!m_webSocket.isNull()) {
+        m_webSocket->open(req);
+        qDebug() << "Opened websocket:" << QUrl(m_url).toString(QUrl::RemoveQuery);
+    }
 }
 
 void VsWebSocket::closeSocket()
 {
-    if (m_connected) {
-        m_webSocket.close();
+    if (!m_webSocket.isNull()
+        && m_webSocket->state() != QAbstractSocket::UnconnectedState) {
+        qDebug() << "Closing websocket:" << QUrl(m_url).toString(QUrl::RemoveQuery);
+        m_webSocket->abort();
     }
 }
 
-// Fires when connected to websocket
-void VsWebSocket::onConnected()
-{
-    m_connected = true;
-    m_error     = false;
-}
-
-// Fires when disconnected from websocket
-void VsWebSocket::onClosed()
-{
-    m_connected = false;
-}
-
-void VsWebSocket::onError(QAbstractSocket::SocketError /*error*/)
+void VsWebSocket::onError(QAbstractSocket::SocketError error)
 {
-    // qDebug() << error;
-    m_error = true;
+    // RemoteHostClosedError may be expected due to finite connection durations
+    // ConnectionRefusedError may be expected if the server-side endpoint is closed
+    if (error != QAbstractSocket::RemoteHostClosedError) {
+        qDebug() << "Websocket error: " << error;
+    }
+    if (!m_webSocket.isNull()) {
+        m_webSocket->abort();
+    }
 }
 
 void VsWebSocket::onSslErrors(const QList<QSslError>& errors)
 {
     for (int i = 0; i < errors.size(); ++i) {
-        // qDebug() << errors.at(i);
+        qDebug() << "SSL error: " << errors.at(i);
+    }
+    if (!m_webSocket.isNull()) {
+        m_webSocket->abort();
     }
-    m_error = true;
 }
 
 void VsWebSocket::sendMessage(const QByteArray& message)
 {
-    m_webSocket.sendBinaryMessage(message);
+    if (isValid()) {
+        m_webSocket->sendBinaryMessage(message);
+    }
 }
 
 bool VsWebSocket::isValid()
 {
-    return !m_error && m_connected;
+    return !m_webSocket.isNull()
+           && m_webSocket->state() == QAbstractSocket::ConnectedState;
 }
index 4581355065e2ee1d85e904e4b0db4249af99e32d..61b19c614a3c4121b9e0582605290c4fdae1f0ee 100644 (file)
@@ -40,6 +40,7 @@
 
 #include <QList>
 #include <QObject>
+#include <QScopedPointer>
 #include <QSslError>
 #include <QString>
 #include <QUrl>
@@ -53,6 +54,7 @@ class VsWebSocket : public QObject
     // Constructor
     explicit VsWebSocket(const QUrl& url, QString token, QString apiPrefix,
                          QString apiSecret, QObject* parent = nullptr);
+    virtual ~VsWebSocket();
 
     // Public functions
     void openSocket();
@@ -62,18 +64,15 @@ class VsWebSocket : public QObject
 
    signals:
     void textMessageReceived(const QString& message);
+    void disconnected();
 
    private slots:
-    void onConnected();
-    void onClosed();
     void onError(QAbstractSocket::SocketError error);
     void onSslErrors(const QList<QSslError>& errors);
 
    private:
-    QWebSocket m_webSocket;
+    QScopedPointer<QWebSocket> m_webSocket;
     QUrl m_url;
-    bool m_connected = false;
-    bool m_error     = false;
     QString m_token;
     QString m_apiPrefix;
     QString m_apiSecret;
diff --git a/src/gui/vsftux.qml b/src/gui/vsftux.qml
deleted file mode 100644 (file)
index 2ee5c36..0000000
+++ /dev/null
@@ -1,133 +0,0 @@
-import QtQuick 2.12
-import QtQuick.Controls 2.12
-
-Rectangle {
-    property string backgroundColour: virtualstudio.darkMode ? "#272525" : "#FAFBFB"
-    property string textColour: virtualstudio.darkMode ? "#FAFBFB" : "#0F0D0D"
-
-    width: 696
-    height: 577
-    color: backgroundColour
-    state: virtualstudio.windowState
-    anchors.fill: parent
-
-    id: window
-    states: [
-        State {
-            name: "login"
-            PropertyChanges { target: loginScreen; x: 0 }
-            PropertyChanges { target: setupScreen; x: window.width }
-            PropertyChanges { target: browseScreen; x: window.width }
-            PropertyChanges { target: settingsScreen; x: window.width }
-            PropertyChanges { target: connectedScreen; x: window.width }
-            PropertyChanges { target: failedScreen; x: window.width }
-        },
-
-        State {
-            name: "setup"
-            PropertyChanges { target: loginScreen; x: -loginScreen.width }
-            PropertyChanges { target: setupScreen; x: 0 }
-            PropertyChanges { target: browseScreen; x: window.width }
-            PropertyChanges { target: settingsScreen; x: window.width }
-            PropertyChanges { target: connectedScreen; x: window.width }
-            PropertyChanges { target: failedScreen; x: window.width }
-        },
-
-        State {
-            name: "browse"
-            PropertyChanges { target: loginScreen; x: -loginScreen.width }
-            PropertyChanges { target: setupScreen; x: -setupScreen.width }
-            PropertyChanges { target: browseScreen; x: 0 }
-            PropertyChanges { target: settingsScreen; x: window.width }
-            PropertyChanges { target: connectedScreen; x: window.width }
-            PropertyChanges { target: failedScreen; x: window.width }
-        },
-
-        State {
-            name: "settings"
-            PropertyChanges { target: loginScreen; x: -loginScreen.width }
-            PropertyChanges { target: setupScreen; x: -setupScreen.width }
-            PropertyChanges { target: browseScreen; x: -browseScreen.width }
-            PropertyChanges { target: settingsScreen; x: 0 }
-            PropertyChanges { target: connectedScreen; x: window.width }
-            PropertyChanges { target: failedScreen; x: window.width }
-        },
-
-        State {
-            name: "connected"
-            PropertyChanges { target: loginScreen; x: -loginScreen.width }
-            PropertyChanges { target: setupScreen; x: -setupScreen.width }
-            PropertyChanges { target: browseScreen; x: -browseScreen.width }
-            PropertyChanges { target: settingsScreen; x: window.width }
-            PropertyChanges { target: connectedScreen; x: 0 }
-            PropertyChanges { target: failedScreen; x: window.width }
-        },
-
-        State {
-            name: "failed"
-            PropertyChanges { target: loginScreen; x: -loginScreen.width }
-            PropertyChanges { target: setupScreen; x: -setupScreen.width }
-            PropertyChanges { target: browseScreen; x: -browseScreen.width }
-            PropertyChanges { target: settingsScreen; x: window.width }
-            PropertyChanges { target: connectedScreen; x: window.width }
-            PropertyChanges { target: failedScreen; x: 0 }
-        }
-    ]
-
-    transitions: Transition {
-        NumberAnimation { properties: "x"; duration: 800; easing.type: Easing.InOutQuad }
-    }
-
-    Setup {
-        id: setupScreen
-    }
-
-    Browse {
-        id: browseScreen
-    }
-
-    Login {
-        id: loginScreen
-    }
-
-    Settings {
-        id: settingsScreen
-    }
-
-    Connected {
-        id: connectedScreen
-    }
-
-    Failed {
-        id: failedScreen
-    }
-
-    Connections {
-        target: virtualstudio
-        function onAuthSucceeded() {
-            if (virtualstudio.windowState !== "login") {
-                // can happen on settings screen when switching between prod and test
-                return;
-            }
-            if (virtualstudio.showDeviceSetup) {
-                virtualstudio.windowState = "setup";
-            } else {
-                virtualstudio.windowState = "browse";
-            }
-        }
-        function onConnected() {
-            virtualstudio.windowState = "connected";
-        }
-        function onFailed() {
-            virtualstudio.windowState = "failed";
-        }
-        function onDisconnected() {
-            virtualstudio.windowState = "browse";
-        }
-        function onWindowStateUpdated() {
-            if (virtualstudio.windowState === "login") {
-                virtualstudio.login();
-            }
-        }
-    }
-}
diff --git a/src/gui/warning.svg b/src/gui/warning.svg
new file mode 100644 (file)
index 0000000..e532c5f
--- /dev/null
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 -960 960 960" width="48"><path d="M92-120q-9 0-15.652-4.125Q69.696-128.25 66-135q-4.167-6.6-4.583-14.3Q61-157 66-165l388-670q5-8 11.5-11.5T480-850q8 0 14.5 3.5T506-835l388 670q5 8 4.583 15.7-.416 7.7-4.583 14.3-3.696 6.75-10.348 10.875Q877-120 868-120H92Zm52-60h672L480-760 144-180Zm340.175-57q12.825 0 21.325-8.675 8.5-8.676 8.5-21.5 0-12.825-8.675-21.325-8.676-8.5-21.5-8.5-12.825 0-21.325 8.675-8.5 8.676-8.5 21.5 0 12.825 8.675 21.325 8.676 8.5 21.5 8.5Zm0-111q12.825 0 21.325-8.625T514-378v-164q0-12.75-8.675-21.375-8.676-8.625-21.5-8.625-12.825 0-21.325 8.625T454-542v164q0 12.75 8.675 21.375 8.676 8.625 21.5 8.625ZM480-470Z"/></svg>
\ No newline at end of file
index 206c2e1b0901b11766c60465b6dc101d7b57d0c5..8cee2524aeb9a7c5ccaeedffb8297b08124f49b4 100644 (file)
@@ -40,7 +40,7 @@
 
 #include "AudioInterface.h"
 
-constexpr const char* const gVersion = "1.10.0";  ///< JackTrip version
+constexpr const char* const gVersion = "2.0.1";  ///< JackTrip version
 
 //*******************************************************************************
 /// \name Default Values
index 8cf45afd13e40451dc91c656842f575c6788312a..30ae6bf073a074ecbac9306c481d1c3f12a4a9e3 100644 (file)
 
 #ifndef NO_GUI
 #include <QApplication>
+#if !defined(NO_VS) && QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+#include <QQuickStyle>
+#endif
 
 #ifndef NO_UPDATER
 #include "dblsqd/feed.h"
 #include "dblsqd/update_dialog.h"
 #endif
 
-#ifndef NO_VS
+#if !defined(NO_VS) && QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
 #include <QDebug>
+#include <QDir>
 #include <QFile>
 #include <QQmlEngine>
 #include <QQuickView>
 #include <QSettings>
+#include <QStandardPaths>
 #include <QTextStream>
+// TODO: Add support for QtWebView
+//#include <QtWebView>
+#include <QtWebEngineQuick/qtwebenginequickglobal.h>
 
 #include "JTApplication.h"
 #include "gui/virtualstudio.h"
-#include "gui/vsInit.h"
+#include "gui/vsDeeplink.h"
 #include "gui/vsQmlClipboard.h"
-#include "gui/vsUrlHandler.h"
-#endif
+#endif  // NO_VS && QT_VERSION
 
 #include "gui/qjacktrip.h"
 #else
 #endif
 
 #ifndef NO_GUI
-#ifndef NO_VS
+#if !defined(NO_VS) && QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
 static QTextStream* ts;
 static QFile outFile;
-#endif  // NO_VS
+#endif  // NO_VS && QT_VERSION
 #endif  // NO_GUI
 
 QCoreApplication* createApplication(int& argc, char* argv[])
@@ -135,7 +142,7 @@ QCoreApplication* createApplication(int& argc, char* argv[])
             std::exit(1);
         }
 #endif
-#if defined(Q_OS_MACOS) && !defined(NO_VS)
+#if defined(Q_OS_MACOS) && !defined(NO_VS) && QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
         // Turn on high DPI support.
 #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
         JTApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
@@ -175,14 +182,10 @@ void qtMessageHandler([[maybe_unused]] QtMsgType type,
 {
     std::cerr << msg.toStdString() << std::endl;
 #ifndef NO_GUI
-#ifndef NO_VS
+#if !defined(NO_VS) && QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
     // Writes to file in order to debug bundles and executables
-#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
     *ts << msg << Qt::endl;
-#else
-    *ts << msg << endl;
-#endif  // QT_VERSION > 5.14.0
-#endif  // NO_VS
+#endif  // NO_VS && QT_VERSION
 #endif  // NO_GUI
 }
 
@@ -265,6 +268,16 @@ bool isRunFromCmd()
             if (size >= 6 && strncmp(pname + size - 6, "wt.exe", 6) == 0) {
                 return true;
             }
+            // a few extras for msys/cygwin/etc
+            if (size >= 8 && strncmp(pname + size - 8, "bash.exe", 8) == 0) {
+                return true;
+            }
+            if (size >= 6 && strncmp(pname + size - 6, "sh.exe", 6) == 0) {
+                return true;
+            }
+            if (size >= 7 && strncmp(pname + size - 7, "zsh.exe", 7) == 0) {
+                return true;
+            }
         } else {
             CloseHandle(h);
         }
@@ -276,21 +289,30 @@ bool isRunFromCmd()
 
 int main(int argc, char* argv[])
 {
+#ifndef NO_GUI
+#if !defined(NO_VS) && QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+    QtWebEngineQuick::initialize();
+    // TODO: Add support for QtWebView
+    // qputenv("QT_WEBVIEW_PLUGIN", "native");
+    // QtWebView::initialize();
+#endif  // NO_VS && QT_VERSION
+#endif  // NO_GUI
+
     QScopedPointer<QCoreApplication> app(createApplication(argc, argv));
     QScopedPointer<JackTrip> jackTrip;
     QScopedPointer<UdpHubListener> udpHub;
 #ifndef NO_GUI
     QSharedPointer<QJackTrip> window;
+#if !defined(NO_VS) && QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+    QQuickStyle::setStyle("Basic");
+#endif  // QT_VERSION
 
-#ifndef NO_VS
-    QString deeplink;
-    QSharedPointer<VirtualStudio> vs;
-#ifdef _WIN32
-    QScopedPointer<VsInit> vsInit;
-#endif
-#endif
+#if !defined(NO_VS) && QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+    QSharedPointer<VirtualStudio> vsPtr;
+    QScopedPointer<VsDeeplink> vsDeeplinkPtr;
+#endif  // NO_VS && QT_VERSION
 
-#if defined(Q_OS_MACOS) && !defined(NO_VS)
+#if defined(Q_OS_MACOS) && !defined(NO_VS) && QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
     if (qobject_cast<JTApplication*>(app.data())) {
 #else
     if (qobject_cast<QApplication*>(app.data())) {
@@ -320,55 +342,42 @@ int main(int argc, char* argv[])
         cliSettings.reset(new Settings(true));
         cliSettings->parseInput(argc, argv);
 
-#ifndef NO_VS
+#if !defined(NO_VS) && QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
         // Register clipboard Qml type
         qmlRegisterType<VsQmlClipboard>("VS", 1, 0, "Clipboard");
 
-        // Parse command line for deep link
-        deeplink = VsInit::parseDeeplink(app.data());
+        // prepare handler for deeplinks jacktrip://join/<StudioID>
+        vsDeeplinkPtr.reset(new VsDeeplink(app.data()));
+        if (!vsDeeplinkPtr->getDeeplink().isEmpty()) {
+            bool readyForExit = vsDeeplinkPtr->waitForReady();
+            if (readyForExit)
+                return 0;
+        }
 
         // Check if we need to show our first run window.
         QSettings settings;
         int uiMode = settings.value(QStringLiteral("UiMode"), QJackTrip::UNSET).toInt();
-#ifdef _WIN32
-        // Set url scheme in registry
-        VsInit::setUrlScheme();
-        vsInit.reset(new VsInit());
-        vsInit->checkForInstance(deeplink);
-#endif  // _WIN32
-        window.reset(new QJackTrip(cliSettings, !deeplink.isEmpty()));
+        window.reset(new QJackTrip(cliSettings, !vsDeeplinkPtr->getDeeplink().isEmpty()));
 #else
         window.reset(new QJackTrip(cliSettings));
 #endif  // NO_VS
         QObject::connect(window.data(), &QJackTrip::signalExit, app.data(),
                          &QCoreApplication::quit, Qt::QueuedConnection);
-#ifndef NO_VS
-        vs.reset(new VirtualStudio(uiMode == QJackTrip::UNSET));
-        QObject::connect(vs.data(), &VirtualStudio::signalExit, app.data(),
+
+#if !defined(NO_VS) && QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+        vsPtr.reset(new VirtualStudio(uiMode == QJackTrip::UNSET));
+        QObject::connect(vsPtr.data(), &VirtualStudio::signalExit, app.data(),
                          &QCoreApplication::quit, Qt::QueuedConnection);
-#ifdef _WIN32
-        vsInit->setVs(vs);
-#endif
-        vs->setStandardWindow(window);
-        window->setVs(vs);
-
-        VsUrlHandler* m_urlHandler = new VsUrlHandler();
-        QDesktopServices::setUrlHandler(QStringLiteral("jacktrip"), m_urlHandler,
-                                        "handleUrl");
-        QObject::connect(m_urlHandler, &VsUrlHandler::joinUrlClicked, vs.data(),
-                         [&](const QUrl& url) {
-                             if (url.scheme() == QLatin1String("jacktrip")
-                                 && url.host() == QLatin1String("join")) {
-                                 vs->setStudioToJoin(url);
-                             }
-                         });
-        // Open with any command line-passed url
-        QDesktopServices::openUrl(QUrl(deeplink));
+        vsPtr->setStandardWindow(window);
+        window->setVs(vsPtr);
+        QObject::connect(vsDeeplinkPtr.get(), &VsDeeplink::signalDeeplink, vsPtr.get(),
+                         &VirtualStudio::handleDeeplinkRequest, Qt::QueuedConnection);
+        vsDeeplinkPtr->readyForSignals();
 
         if (uiMode == QJackTrip::UNSET) {
-            vs->show();
+            vsPtr->show();
         } else if (uiMode == QJackTrip::VIRTUAL_STUDIO) {
-            vs->show();
+            vsPtr->show();
         } else {
             window->show();
         }
@@ -394,7 +403,8 @@ int main(int argc, char* argv[])
 
 #if !defined(NO_UPDATER) && !defined(__unix__)
 #ifndef PSI
-#if defined(NO_VS)
+#if defined(NO_VS) || QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+
         // This wasn't set up earlier in NO_VS builds. Create it here.
         QSettings settings;
 #endif
index 5aeba8f89ebb530950f20539503552a530e7e3c8..a57365a9abd8a2a0a2bad4f1e1b2a60290e65e43 100644 (file)
@@ -1,9 +1,8 @@
 [wrap-file]
-directory = rtaudio-5.2.0
-source_url = https://github.com/thestk/rtaudio/archive/refs/tags/5.2.0.tar.gz
-source_filename = 5.2.0.tar.gz
-source_hash = a8d9c738addffd485c3f0bab14cbba72600267e3113f274398c67829bbb49332
+directory = rtaudio-6.0.1
+source_url = https://github.com/thestk/rtaudio/archive/refs/tags/6.0.1.tar.gz
+source_filename = 6.0.1.tar.gz
+source_hash = 7206c8b6cee43b474f43d64988fefaadfdcfc4264ed38d8de5f5d0e6ddb0a123
 
 [provide]
 dependency_names = rtaudio
-
index 06d5f2e941ce2b1f2d45db368451be4b4115a661..eade10dca69699703fd7a30d9d10789a0c4d7d19 100755 (executable)
@@ -1,25 +1,10 @@
 @echo off\r
 setlocal EnableDelayedExpansion\r
 \r
-set QTVERSION="5"\r
-\r
-if not defined QTBINPATH (\r
-       for /f "delims=" %%a in ('dir /b C:\Qt\%QTVERSION%.*') do set QTFULLVERSION=%%a\r
-       for /f "delims=" %%a in ('dir /b C:\Qt\!QTFULLVERSION!\mingw*') do set QTBINPATH=%%a\r
-       set QTBINPATH=C:\Qt\!QTFULLVERSION!\!QTBINPATH!\bin\r
-)\r
-echo Using Qt Version %QTFULLVERSION%\r
-if not defined QTLIBPATH (\r
-       for /f "delims=" %%a in ('dir /b C:\Qt\Tools\mingw*') do set QTLIBPATH=%%a\r
-       set QTLIBPATH=C:\Qt\Tools\!QTLIBPATH!\bin\r
-)\r
-echo Using mingw libraries from %QTLIBPATH%\r
 if not defined WIXPATH (\r
        for /f "delims=" %%a in ('dir /b "C:\Program Files (x86)\Wix Toolset*"') do set WIXPATH=%%a\r
        set WIXPATH=C:\Program Files ^(x86^)\!WIXPATH!\bin\r
 )\r
-if not defined SSLPATH (set SSLPATH=C:\Qt\Tools\OpenSSL\Win_x64\bin)\r
-if not defined RTAUDIOLIB (set RTAUDIOLIB="C:\Program Files (x86)\RtAudio\bin\librtaudio.dll")\r
 \r
 set PATH=%PATH%;%WIXPATH%\r
 del deploy /s /q\r
@@ -48,38 +33,32 @@ if "%~1"=="/q" (
 if exist ..\builddir\release\jacktrip.exe (set JACKTRIP=..\builddir\release\jacktrip.exe) else (set JACKTRIP=..\builddir\jacktrip.exe)\r
 copy %JACKTRIP% deploy\\r
 cd deploy\r
+\r
 set "WIXDEFINES="\r
-for /f "tokens=*" %%a in ('%QTLIBPATH%\objdump -p jacktrip.exe ^| findstr Qt%QTVERSION%Core.dll') do set DYNAMIC_QT=%%a\r
+for /f "tokens=*" %%a in ('objdump -p jacktrip.exe ^| findstr Qt5Core.dll') do (\r
+       set DYNAMIC_QT=%%a\r
+       set QTVERSION=5\r
+)\r
+if not defined DYNAMIC_QT (\r
+       for /f "tokens=*" %%a in ('objdump -p jacktrip.exe ^| findstr Qt6Core.dll') do (\r
+               set DYNAMIC_QT=%%a\r
+               set QTVERSION=6\r
+       )\r
+)\r
 if defined DYNAMIC_QT (\r
-       echo Including Qt Files\r
-       for /f "tokens=*" %%a in ('%QTLIBPATH%\objdump -p jacktrip.exe ^| findstr Qt%QTVERSION%Qml.dll') do set VS=%%a\r
+       echo Including Qt%QTVERSION% Files\r
+       for /f "tokens=*" %%a in ('objdump -p jacktrip.exe ^| findstr Qt%QTVERSION%Qml.dll') do set VS=%%a\r
        if defined VS (\r
-               if %QTVERSION%=="6" (\r
-                       echo The installer is not designed to handle dynamic Virtual Studio builds using Qt6 yet.\r
-                       exit /b 1\r
-               )\r
-               %QTBINPATH%\windeployqt --qmldir ..\..\src\gui jacktrip.exe\r
+               windeployqt -release --qmldir ..\..\src\gui jacktrip.exe\r
                set WIXDEFINES=%WIXDEFINES% -dvs\r
        ) else (\r
-               %QTBINPATH%\windeployqt jacktrip.exe\r
+               windeployqt -release jacktrip.exe\r
        )\r
-       copy "%QTLIBPATH%\libgcc_s_seh-1.dll" .\\r
-       copy "%QTLIBPATH%\libstdc++-6.dll" .\\r
-       copy "%QTLIBPATH%\libwinpthread-1.dll" .\\r
-       copy "%SSLPATH%\libcrypto-1_1-x64.dll" .\\r
-       copy "%SSLPATH%\libssl-1_1-x64.dll" .\\r
-       set WIXDEFINES=!WIXDEFINES! -ddynamic\r
-)\r
-for /f "tokens=*" %%a in ('%QTLIBPATH%\objdump -p jacktrip.exe ^| findstr librtaudio.dll') do set RTAUDIO=%%a\r
-if defined RTAUDIO (\r
-       echo Including librtaudio\r
-       copy %RTAUDIOLIB% .\\r
-       set WIXDEFINES=%WIXDEFINES% -drtaudio\r
+       set WIXDEFINES=!WIXDEFINES! -ddynamic -dqt%QTVERSION%\r
 )\r
-set WIXDEFINES=%WIXDEFINES% -dqt%QTVERSION%\r
 \r
 copy ..\jacktrip.wxs .\\r
-copy ..\files.wxs .\\r
+copy ..\qt%QTVERSION%.wxs .\\r
 .\jacktrip --test-gui\r
 if %ERRORLEVEL% NEQ 0 (\r
        echo You need to build jacktrip with gui support to build the installer.\r
@@ -89,6 +68,6 @@ rem Get our version number
 for /f "tokens=*" %%a in ('.\jacktrip -v ^| findstr VERSION') do for %%b in (%%~a) do set VERSION=%%b\r
 for /f "tokens=1 delims=-" %%a in ("%VERSION%") do set VERSION=%%a\r
 echo Version=%VERSION%\r
-candle.exe -arch x64 -ext WixUIExtension -ext WixUtilExtension -dVersion=%VERSION%%WIXDEFINES% jacktrip.wxs files.wxs\r
-light.exe -ext WixUIExtension -ext WixUtilExtension -o JackTrip.msi jacktrip.wixobj files.wixobj\r
+candle.exe -arch x64 -ext WixUIExtension -ext WixUtilExtension -dVersion=%VERSION%%WIXDEFINES% ..\jacktrip.wxs ..\qt%QTVERSION%.wxs\r
+light.exe -ext WixUIExtension -ext WixUtilExtension -o JackTrip.msi jacktrip.wixobj qt%QTVERSION%.wixobj\r
 endlocal\r
diff --git a/win/files.wxs b/win/files.wxs
deleted file mode 100644 (file)
index cda2140..0000000
+++ /dev/null
@@ -1,1867 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>\r
-<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">\r
-    <Fragment>\r
-        <DirectoryRef Id="INSTALLDIR">\r
-            <Component Id="cmpE6579DC78E167F46AB23E0BE3EAA58CE" Guid="27BFCC7D-F4A8-4F71-8A2B-00A8BC74B106">\r
-                <File Id="fil5E6E1243EAE085FE802B6D64690357B0" KeyPath="yes" Source="SourceDir\jacktrip.exe" />\r
-                <Shortcut Id="startmenuJackTrip" Directory="ProgramMenuDir" Name="JackTrip"\r
-                    WorkingDirectory="INSTALLDIR" Icon="jacktrip.exe" IconIndex="0" Advertise="yes" />\r
-            </Component>\r
-            <Component Id="cmp52FE9CFF214054652C281C37201305EC" Guid="{158D5247-525B-417F-B12B-48F13CD7E414}">\r
-                <File Id="fil8AF433A253A49035CB32EA6F957BF7D4" KeyPath="yes" Source="SourceDir\LICENSE.md" />\r
-            </Component>\r
-            <Directory Id="LICENSESDIR" Name="LICENSES">\r
-                <Component Id="cmpA07F2DC814BECD1E5F3FC36DC47718E5" Guid="{36E317A7-BA26-4E2B-99CD-0728CD8779DD}">\r
-                    <File Id="fil39657C9CD79E70F8B3033F0CF1D035A1" KeyPath="yes" Source="SourceDir\LICENSES\LGPL-3.0-only.txt" />\r
-                </Component>\r
-                <Component Id="cmpBF7FA1E27DF091A3CCFA1CD7B7CD94BA" Guid="{2D555037-9C09-4042-8F0C-C6EBAB80681A}">\r
-                    <File Id="fil496C76DCE2246C589E09AC68BCFDFB32" KeyPath="yes" Source="SourceDir\LICENSES\MIT.txt" />\r
-                </Component>\r
-                <Component Id="cmpF915A8D749EFFA98CD8DD582A7C82614" Guid="{A3C72CE1-0570-497B-A84F-F21C53771ABC}">\r
-                    <File Id="filCCF13ED800A4C7B438EC52A50C3A6755" KeyPath="yes" Source="SourceDir\LICENSES\GPL-3.0.txt" />\r
-                </Component>\r
-              </Directory>\r
-<?ifdef rtaudio?>\r
-            <Component Id="cmpD683260746BC875AE3BDED667AA8F954" Guid="20A867E8-36B6-49E3-A1DA-76CFCE967908">\r
-                <File Id="filA7BCB2CDD7D00C9DFB32AEB94F294425" KeyPath="yes" Source="SourceDir\librtaudio.dll" />\r
-            </Component>\r
-<?endif?>\r
-<?ifdef dynamic?>\r
-            <Component Id="cmpF83098612A5B11C5398DBA3894ED590A" Guid="6A0A67A9-3318-4CFB-BC07-5AF3FF7B7E87">\r
-                <File Id="fil526FA994C1850220CFA7BD7FCD995C32" KeyPath="yes" Source="SourceDir\D3Dcompiler_47.dll" />\r
-            </Component>\r
-<?ifdef qt5?>\r
-            <Component Id="cmp5DDCF0CE56D406D1B8B1F93E988AED6B" Guid="4346C308-2912-4CAF-B01B-1FB85781DAD0">\r
-                <File Id="fil0F4CD3729AF07C1C460E292E0F081D0A" KeyPath="yes" Source="SourceDir\libEGL.dll" />\r
-            </Component>\r
-            <Component Id="cmpB0CFEB9FF932521D6A75B6BE6F8D644F" Guid="73A8C873-FBDB-4BC5-BB74-2F82619DC3EC">\r
-                <File Id="fil736048FE57EC840EA5DCE88625C38E8D" KeyPath="yes" Source="SourceDir\libGLESv2.dll" />\r
-            </Component>\r
-<?endif?>\r
-            <Component Id="cmpB60B52D038613C3F20F84A0E1C83D4D4" Guid="62E3CBE5-9787-4A5C-B6CD-B867781773EE">\r
-                <File Id="fil09D765981E2E493119D2DBD2DA179B9D" KeyPath="yes" Source="SourceDir\libcrypto-1_1-x64.dll" />\r
-            </Component>\r
-            <Component Id="cmpDEFDDDBEBEA7255D07EE4400F2CDA2FF" Guid="904EB309-B381-443D-A064-4C2669E8E66E">\r
-                <File Id="fil2ECEB8B8BE6121A2C5659BA0F64D92FC" KeyPath="yes" Source="SourceDir\libgcc_s_seh-1.dll" />\r
-            </Component>\r
-            <Component Id="cmp809509D62BD9EA848EF243F03D83758F" Guid="FE4AFCE9-7DE1-4A82-A96E-42C8B93E04C7">\r
-                <File Id="filCE00A8B273087E758109FEC2AC699195" KeyPath="yes" Source="SourceDir\libssl-1_1-x64.dll" />\r
-            </Component>\r
-            <Component Id="cmp57DDA83E372188A5B760684B961DE685" Guid="96741680-04B3-4A4A-8D55-08C48D6B2201">\r
-                <File Id="filF57E66845C852C7C1EFFE6996027BE3C" KeyPath="yes" Source="SourceDir\libstdc++-6.dll" />\r
-            </Component>\r
-            <Component Id="cmpF76D0CD1138260DB167395B86D0011CF" Guid="88B4D808-2CD2-40E0-9BC4-51093AFD3D61">\r
-                <File Id="fil8C56E6114C3ACED0DF58456A08DC0437" KeyPath="yes" Source="SourceDir\libwinpthread-1.dll" />\r
-            </Component>\r
-            <Component Id="cmpEA16414E09FD1D5068139520966B49EB" Guid="9B14A007-CA41-4C45-BE9E-0B1067DD5311">\r
-                <File Id="filFC8C908E3AD35E8090AFF39C9B0FB744" KeyPath="yes" Source="SourceDir\opengl32sw.dll" />\r
-            </Component>\r
-<?ifdef qt5?>\r
-            <Component Id="cmp01229088A6F1DEA1BE391E6E7CC3D3EE" Guid="9A610B9C-6322-4045-9C4B-910FFBA38502">\r
-                <File Id="filAD80F4724AAE3D8B0682F5982CC2548D" KeyPath="yes" Source="SourceDir\Qt5Core.dll" />\r
-            </Component>\r
-            <Component Id="cmp1658AEC83DA2CDD0D93D5BAAABBEAD46" Guid="9D7923EE-3849-4FCE-A0C4-A2254AD769D3">\r
-                <File Id="fil7E727B350B33C4EEEFE406E27624E057" KeyPath="yes" Source="SourceDir\Qt5Gui.dll" />\r
-            </Component>\r
-            <Component Id="cmp2DE804C107C75B0C71470A93647D09B5" Guid="F8E604E4-A6B2-444D-9F1F-2D86230D5585">\r
-                <File Id="filD86C7802D20EDF450E8B03BC177BA71F" KeyPath="yes" Source="SourceDir\Qt5Network.dll" />\r
-            </Component>\r
-            <Component Id="cmp82912105030EA0D2D7550CF8AC5677F6" Guid="17A8DAEB-44BD-451C-BEEE-C3C2EF883516">\r
-                <File Id="fil6DA129DEAE11F969B17E1886F9AF9AA0" KeyPath="yes" Source="SourceDir\Qt5Svg.dll" />\r
-            </Component>\r
-            <Component Id="cmp975651B71289ACA70B3EB154C08A27F0" Guid="FD612706-4DF7-439B-B82B-57EFA2657F84">\r
-                <File Id="filE89B28CF105C7A7691C046E6516D540E" KeyPath="yes" Source="SourceDir\Qt5Widgets.dll" />\r
-            </Component>\r
-            <Directory Id="dirFF9CCFA394406B344C7FF767066C0809" Name="bearer">\r
-                <Component Id="cmp3CE5AF7CB2F8B2689D2096769883BC56" Guid="FD4AB5B8-4F89-4C08-819B-58A7E8F6360A">\r
-                    <File Id="fil94C33B546082B4DF43813FFB7569FBF6" KeyPath="yes" Source="SourceDir\bearer\qgenericbearer.dll" />\r
-                </Component>\r
-            </Directory>\r
-<?endif?>\r
-<?ifdef qt6?>\r
-            <Component Id="cmp9147A534CD8EDAB4BA7FDEAEEFE9C511" Guid="{8D05F121-517E-4A1F-86FE-26A735F23F1C}">\r
-                <File Id="fil88DECE330C4A1F60E0198BE9EAD7F35B" KeyPath="yes" Source="SourceDir\Qt6Core.dll" />\r
-            </Component>\r
-            <Component Id="cmp52AB384603E096D6B6A1E1FC66B089D4" Guid="{FA746517-7C88-4473-9162-22E9A8CF6507}">\r
-                <File Id="fil93995D4EFFB081D1CEF92B0D38526E04" KeyPath="yes" Source="SourceDir\Qt6Gui.dll" />\r
-            </Component>\r
-            <Component Id="cmp0E8605D5C55843FCDFE809B5EE8DFDFA" Guid="{499C045E-88C6-43A6-B567-6C3438CA5FFD}">\r
-                <File Id="fil63F02A7FFBCDAAB6EAB6E65E642AC7B0" KeyPath="yes" Source="SourceDir\Qt6Network.dll" />\r
-            </Component>\r
-            <Component Id="cmp5D904885C8104A4049616BBAA95F34A3" Guid="{B47BE8F9-D9B5-4345-A232-F2EE8B6D4931}">\r
-                <File Id="fil30A776C0F63064A6AF65369FF23FDB8C" KeyPath="yes" Source="SourceDir\Qt6Svg.dll" />\r
-            </Component>\r
-            <Component Id="cmpCAE694BB34A9EC66AEA9D10D697F1EAA" Guid="{C1A11A59-3BF3-4562-92C6-304A80B93DAE}">\r
-                <File Id="fil43665A392379256FF1D68CF38B97EA38" KeyPath="yes" Source="SourceDir\Qt6Widgets.dll" />\r
-            </Component>\r
-<?endif qt6?>\r
-            <Directory Id="dirB4D909CA5877E8EBEB32B83B2D15F595" Name="iconengines">\r
-                <Component Id="cmp1AAFF160C8E04AD945D74CA884667ABC" Guid="F9CAC368-2789-40F4-ADB8-0EDF377306DF">\r
-                    <File Id="filB4B2363760DE489E24EBDBD0BC837FDE" KeyPath="yes" Source="SourceDir\iconengines\qsvgicon.dll" />\r
-                </Component>\r
-            </Directory>\r
-            <Directory Id="dir60B76952C227590F998DC42F548339BE" Name="imageformats">\r
-                <Component Id="cmp02252B1C1976959CFEE7FA11B6E36C4C" Guid="67FB3C37-C4EB-404B-84E0-2B85288C9A46">\r
-                    <File Id="fil646F5D906D2B2A321C0C481D1D22C9C7" KeyPath="yes" Source="SourceDir\imageformats\qgif.dll" />\r
-                </Component>\r
-                <Component Id="cmp62CBFF830BF239A4E376FAE29937AFAB" Guid="FCFFB090-6600-4F92-9F71-A9EFDA3F5F05">\r
-                    <File Id="fil2132CC3E0278FB6088A48B8A3C120D44" KeyPath="yes" Source="SourceDir\imageformats\qicns.dll" />\r
-                </Component>\r
-                <Component Id="cmpD9DF9496C7453F1AA6C20CD4A1C94ED7" Guid="6EAE06EC-8FC6-4075-8B7D-2E5E2ECDBC1D">\r
-                    <File Id="fil0A4C8F6CF0C62302928ED23A10F88E84" KeyPath="yes" Source="SourceDir\imageformats\qico.dll" />\r
-                </Component>\r
-                <Component Id="cmp0E8940C551429DC79C890130F6D10B6D" Guid="4E9F8DE6-5789-421B-8299-04C8F95F6804">\r
-                    <File Id="fil080B2419A3875B7348050418715B59D5" KeyPath="yes" Source="SourceDir\imageformats\qjpeg.dll" />\r
-                </Component>\r
-                <Component Id="cmpC6FCE143A8E5EAFEA6119E94128459C6" Guid="E22AD241-DA8F-4016-A34B-3DA0ABB77AE9">\r
-                    <File Id="filE6DE80FB6D1ADCBBC8D5665DCF3A4674" KeyPath="yes" Source="SourceDir\imageformats\qsvg.dll" />\r
-                </Component>\r
-                <Component Id="cmpFCC4763BEDEA49F63E8D8E3A18E86329" Guid="D3ABC0A1-CE5F-4ABB-8B15-E630FCF1C9B1">\r
-                    <File Id="filAABDE938CE7225264D2AD6E5B85F0F35" KeyPath="yes" Source="SourceDir\imageformats\qtga.dll" />\r
-                </Component>\r
-                <Component Id="cmpF278C1175D7FB53021C0126B8F155FC2" Guid="24C00F19-08E6-40E6-913E-AD54EFB81AB7">\r
-                    <File Id="fil102CCD3441784435E39937C763D95F2F" KeyPath="yes" Source="SourceDir\imageformats\qtiff.dll" />\r
-                </Component>\r
-                <Component Id="cmp608F60C2BD04F2C37AB51C3211ABF615" Guid="8A88549A-3B1F-4A94-B721-7ED99998CA95">\r
-                    <File Id="fil8698D33404DE182991C30D32BA39A5EC" KeyPath="yes" Source="SourceDir\imageformats\qwbmp.dll" />\r
-                </Component>\r
-                <Component Id="cmpE6323C810986405043F219CECD6B6C10" Guid="7194531B-2B4C-4DB0-8BED-B89929695F17">\r
-                    <File Id="filB9C6E81C6DBC26365A8C44CD0A36C49F" KeyPath="yes" Source="SourceDir\imageformats\qwebp.dll" />\r
-                </Component>\r
-            </Directory>\r
-            <Directory Id="dir623D6610C7C3C018A72040463AD22773" Name="platforms">\r
-                <Component Id="cmp9132FD413972F732967B8BC075BC2781" Guid="0DAA1BEB-EA44-4EEC-BCC8-22CE871FC519">\r
-                    <File Id="fil835F5A10B680242272F647CE0A82B16C" KeyPath="yes" Source="SourceDir\platforms\qwindows.dll" />\r
-                </Component>\r
-            </Directory>\r
-            <Directory Id="dirF1334AF64D097D9920D6F285D2DE292C" Name="styles">\r
-                <Component Id="cmp52840D209B2AC1DC45A29A6BA5A75FF8" Guid="3FA00436-A967-45AB-8871-A6DA384044CE">\r
-                    <File Id="fil84E6446D9ECFA487C5A2EC4752E07F04" KeyPath="yes" Source="SourceDir\styles\qwindowsvistastyle.dll" />\r
-                </Component>\r
-            </Directory>\r
-<?ifdef qt6?>\r
-            <Directory Id="dir65521F18679A6D14A7BDE5A8612E8C69" Name="tls">\r
-                <Component Id="cmp83139013FA2C9A99CF2772FD54090FAA" Guid="{60DFDBBA-4832-44FD-B14A-C67EBD4D8119}">\r
-                    <File Id="fil737932C7578FEA0392CDEFB7540D7E44" KeyPath="yes" Source="SourceDir\tls\qcertonlybackend.dll" />\r
-                </Component>\r
-                <Component Id="cmp6E4460E3D6226693FEA5DBBF500895C7" Guid="{CBF26A3F-BA32-4017-BAEF-014AA3C51653}">\r
-                    <File Id="fil02E5C4A93CDAD6084E6372A2EE1F7ABA" KeyPath="yes" Source="SourceDir\tls\qopensslbackend.dll" />\r
-                </Component>\r
-                <Component Id="cmp53306F84AD30BD49CF0C1A68007F9531" Guid="{47907844-2DC3-4E99-B1C9-B9C716E59407}">\r
-                    <File Id="filD182E98DFB41D91EC2F2A531F6CAF918" KeyPath="yes" Source="SourceDir\tls\qschannelbackend.dll" />\r
-                </Component>\r
-            </Directory>\r
-<?endif qt6?>\r
-            <Directory Id="dirF1C5FA08924D46D2A43D5134DF70C0A8" Name="translations">\r
-                <Component Id="cmpBCDC6995284E75B9A24E4646E6117744" Guid="51A3610B-4AE8-4020-926E-80B77B650E52">\r
-                    <File Id="fil694FD3DFB9C088844BF9929014423509" KeyPath="yes" Source="SourceDir\translations\qt_ar.qm" />\r
-                </Component>\r
-                <Component Id="cmp3B3D2ADEE08668758CB9FDB1D243AA11" Guid="E84288C8-80B5-43C0-ABC1-13720B58F806">\r
-                    <File Id="fil0877F2432B761D0289C5DB92EC1E528E" KeyPath="yes" Source="SourceDir\translations\qt_bg.qm" />\r
-                </Component>\r
-                <Component Id="cmpDAE87E8CC29C30A200A5D17D81DF1DE7" Guid="BC72F77D-4D7F-4AFD-BC4F-0F9E337182A9">\r
-                    <File Id="fil36E8A56D7DAEC62D31CEA91730D42174" KeyPath="yes" Source="SourceDir\translations\qt_ca.qm" />\r
-                </Component>\r
-                <Component Id="cmp719D62CCB63A2C08F22335557CBAB533" Guid="084DAFF0-3499-4D2E-9685-D0F7573A0723">\r
-                    <File Id="fil5ECAF3F9F1940369AA594EE12825D0FB" KeyPath="yes" Source="SourceDir\translations\qt_cs.qm" />\r
-                </Component>\r
-                <Component Id="cmp8BDD412901C153FC8A2BF7335835AC78" Guid="ED469E4F-52A1-4BDC-83EA-304D779025ED">\r
-                    <File Id="fil035B9FC36AE44B63202E6201D2A9608E" KeyPath="yes" Source="SourceDir\translations\qt_da.qm" />\r
-                </Component>\r
-                <Component Id="cmp56040A1BBEBE8A547F7D6672326FF4EE" Guid="F0F32818-EB50-415C-8302-B3BCFA3D1A5F">\r
-                    <File Id="fil0AA794E087CE581F4CA99D75C295092D" KeyPath="yes" Source="SourceDir\translations\qt_de.qm" />\r
-                </Component>\r
-                <Component Id="cmp42D4B2D8A4C91F9ED463DA42F3895E27" Guid="1444EE4B-AD59-4C33-9B17-3B21B132DAA9">\r
-                    <File Id="filAD08CEBCD34B84E90701D6248FDB5FA9" KeyPath="yes" Source="SourceDir\translations\qt_en.qm" />\r
-                </Component>\r
-                <Component Id="cmpB92DA15530102518B1B98DBF1DAC135D" Guid="10E7AA9E-7DC7-45E5-A52C-9B33DD7CD157">\r
-                    <File Id="filB05A80E55C2FCBFE9999B66E309F0312" KeyPath="yes" Source="SourceDir\translations\qt_es.qm" />\r
-                </Component>\r
-                <Component Id="cmp7210C3ACD533D6A934BD5977AB16AF71" Guid="8D242F4F-56C4-4BBD-82EF-7EC103AB4445">\r
-                    <File Id="fil4B803C16DFCB1C5EA4D308AFD35049E3" KeyPath="yes" Source="SourceDir\translations\qt_fi.qm" />\r
-                </Component>\r
-                <Component Id="cmp0643210AD0A541FA765CE55469F22FBB" Guid="3DAD67C2-7EBC-4FE2-9375-ED04F44B95C2">\r
-                    <File Id="fil65E050731B55F3764817B70B50D1F483" KeyPath="yes" Source="SourceDir\translations\qt_fr.qm" />\r
-                </Component>\r
-                <Component Id="cmpBB98B3096730FFFE87E708A6291ED47C" Guid="4FC0CA17-91C9-4207-B18F-0D3676A4EAA8">\r
-                    <File Id="fil38E65580D7E2598A2AB22B21A55AE808" KeyPath="yes" Source="SourceDir\translations\qt_gd.qm" />\r
-                </Component>\r
-                <Component Id="cmp3666ECDFA614EA095AF7310AECCD44B7" Guid="A4ADC90F-ED1F-49EC-9595-226C90767318">\r
-                    <File Id="filD812B4BB739798D5C3BF25BEFBFBA938" KeyPath="yes" Source="SourceDir\translations\qt_he.qm" />\r
-                </Component>\r
-                <Component Id="cmpC4FA2671255E7459BD46070D88B76FF1" Guid="427500F9-DE40-499D-AA91-34944AD8AD45">\r
-                    <File Id="fil750345F0F25CC28174362BF42A332BFA" KeyPath="yes" Source="SourceDir\translations\qt_hu.qm" />\r
-                </Component>\r
-                <Component Id="cmp8D644C0D5E80E34EBCB6FC3643C879AF" Guid="FA35CBD4-A861-494E-AABC-76262872F717">\r
-                    <File Id="filF6634E490610EB73E2D852D47B43255E" KeyPath="yes" Source="SourceDir\translations\qt_it.qm" />\r
-                </Component>\r
-                <Component Id="cmpA8DDE2BD9E1AB00C9631DE447CBAA11D" Guid="15D64C1E-B407-4531-9684-A785DAF53421">\r
-                    <File Id="fil493967A00B1FFB8D251F7A844D53A64A" KeyPath="yes" Source="SourceDir\translations\qt_ja.qm" />\r
-                </Component>\r
-                <Component Id="cmp23B3657EE14B0ED7D00B271AFF81187C" Guid="6FC090EB-D1CC-423E-9527-DBDF4E7A4B8F">\r
-                    <File Id="fil5A7EE1B788293556A515EAFFC95747DB" KeyPath="yes" Source="SourceDir\translations\qt_ko.qm" />\r
-                </Component>\r
-                <Component Id="cmp2D105F39980CF93A40405E25755346BB" Guid="79E81F16-F627-4CD1-BB41-5C52A141BE5F">\r
-                    <File Id="fil17B2A1B774381BBA3BF85AF1FFB636AA" KeyPath="yes" Source="SourceDir\translations\qt_lv.qm" />\r
-                </Component>\r
-                <Component Id="cmpC3CB042567F53DD612D309E9CA2E10A6" Guid="CED83F6F-60ED-4321-94B6-7917D1DB232C">\r
-                    <File Id="filC674EE989D2B9719C388F537FC8EFFCF" KeyPath="yes" Source="SourceDir\translations\qt_pl.qm" />\r
-                </Component>\r
-                <Component Id="cmp43D57F897A909679308EFC03849D7E2E" Guid="8E42022B-B599-4ABD-8D30-F5E378FE60FE">\r
-                    <File Id="filADA551AF628F8CBA7E3D06EF76F2713A" KeyPath="yes" Source="SourceDir\translations\qt_ru.qm" />\r
-                </Component>\r
-                <Component Id="cmpF5EA7E37332E95C614BD2D7EAA933E5D" Guid="6516FDA0-5035-420D-AADF-41D1E3735EDA">\r
-                    <File Id="fil49E3CA421049811064612E40502A6147" KeyPath="yes" Source="SourceDir\translations\qt_sk.qm" />\r
-                </Component>\r
-                <Component Id="cmp8DAF13208A863A841AEE7A46E79FB87C" Guid="{A628AE90-BA9E-4CB9-86E0-02E6F2A0358B}">\r
-                    <File Id="filD139779D4ABC7CD76AE12C511550A8C3" KeyPath="yes" Source="SourceDir\translations\qt_tr.qm" />\r
-                </Component>\r
-                <Component Id="cmpA214B663A372012F5DCBA6A5CDAE421D" Guid="790AF12A-1970-4B96-BFAD-925734F604EB">\r
-                    <File Id="fil1380C741E2DBC1F60AA4DC32D66B1E6C" KeyPath="yes" Source="SourceDir\translations\qt_uk.qm" />\r
-                </Component>\r
-                <Component Id="cmpFDB70117D8E46EB188997470A3793B5D" Guid="EA2F54B9-EE49-4426-A2EF-EDDF3CAC83FD">\r
-                    <File Id="fil9085627E4E33630ABCDB62A5CC05B794" KeyPath="yes" Source="SourceDir\translations\qt_zh_TW.qm" />\r
-                </Component>\r
-<?ifdef qt6?>\r
-                <Component Id="cmpF0099CF54A0669D87CC17418C3911422" Guid="{3405A05E-CB26-47D2-9C70-C52167AB8FDC}">\r
-                    <File Id="fil71B9851F396CBCC25B18299EDCBF5FF5" KeyPath="yes" Source="SourceDir\translations\qt_fa.qm" />\r
-                </Component>\r
-                <Component Id="cmpE89BF9966FD9EC39B16BC14B5586E0FA" Guid="{818D0415-1B0D-4C20-A21B-634467D7ABED}">\r
-                    <File Id="filB4B324E9728AE54A2B57A18E91814BC1" KeyPath="yes" Source="SourceDir\translations\qt_hr.qm" />\r
-                </Component>\r
-                <Component Id="cmp7F6AC80CE7E39EEDC9A962EAFADD113B" Guid="{E7491B72-07BC-4811-9AA2-9C90EA586475}">\r
-                    <File Id="filAB0862690AA36917007DE3C425A282CD" KeyPath="yes" Source="SourceDir\translations\qt_nl.qm" />\r
-                </Component>\r
-                <Component Id="cmp9EB4F508A604A4AD35BC558C456111DD" Guid="{035418D9-E833-45B1-B33E-15B8C6EA4E03}">\r
-                    <File Id="filA89B168478D587343A282C54242055A0" KeyPath="yes" Source="SourceDir\translations\qt_nn.qm" />\r
-                </Component>\r
-                <Component Id="cmp09D69EFDCF1B331A37D028C962FE6147" Guid="{BBD6ABD9-DDF9-4AC2-B8C1-FB4CB547B14F}">\r
-                    <File Id="fil3DBA8083389C96E9A47E27507D39EA05" KeyPath="yes" Source="SourceDir\translations\qt_pt_BR.qm" />\r
-                </Component>\r
-                <Component Id="cmpA23046CBC7F8B7E62AF4843CBB72DEC9" Guid="{FE5DC38D-3ABF-406D-8FB7-258119E76380}">\r
-                    <File Id="filCAF43146A9F7B1727B4975BB51D0A045" KeyPath="yes" Source="SourceDir\translations\qt_zh_CN.qm" />\r
-                </Component>\r
-<?endif?>\r
-            </Directory>\r
-<?ifdef vs?>\r
-            <Component Id="cmp26187F331810660B2B23AB9501BE69AF" Guid="{B9D40CBB-9764-4DAA-9121-B74B8EADE326}">\r
-                <File Id="fil548462476B423A98BF378EB6B7FED697" KeyPath="yes" Source="SourceDir\Qt5NetworkAuth.dll" />\r
-            </Component>\r
-            <Component Id="cmp10C81658E3C8F63B19EF9D1C26B58363" Guid="{67FF3D4C-A3F5-4C19-8988-CB8111149469}">\r
-                <File Id="fil0F208DFAC8D7EB86E7793690C6ECF15E" KeyPath="yes" Source="SourceDir\Qt5Qml.dll" />\r
-            </Component>\r
-            <Component Id="cmp5B4749C387D33EAF993DDC89F9DBC102" Guid="{E1A0612E-1BAA-4304-A53F-04F9E5FC2C95}">\r
-                <File Id="fil4D31B1A954CDE740FB32D9FFC142CA37" KeyPath="yes" Source="SourceDir\Qt5QmlModels.dll" />\r
-            </Component>\r
-            <Component Id="cmp7F83581B3F7835094D371708381AD391" Guid="{F123314E-1319-4B12-90E9-BB27786C2234}">\r
-                <File Id="filEFD45339E9495CECA6AD117B583ED589" KeyPath="yes" Source="SourceDir\Qt5QmlWorkerScript.dll" />\r
-            </Component>\r
-            <Component Id="cmp33B78CE22F0CBCA4F41C5037A792210C" Guid="{5653E0BB-A956-4565-B2AD-11712CC59571}">\r
-                <File Id="fil87DAAA273949BD9AD08743E49398302F" KeyPath="yes" Source="SourceDir\Qt5Quick.dll" />\r
-            </Component>\r
-            <Component Id="cmpB5571DB4C3153CA848ED1238DBCD4281" Guid="{AAD89C99-7211-4F02-B07D-A89CD18DF9D5}">\r
-                <File Id="fil07F4E15C2DC44A3DA783DB682CA4FF5C" KeyPath="yes" Source="SourceDir\Qt5QuickControls2.dll" />\r
-            </Component>\r
-            <Component Id="cmp4C1BE3EACCCE195C8B88434D5DF77DE5" Guid="{C8585769-1336-4CC6-B8CD-5D9CEE77F079}">\r
-                <File Id="filBE9D9480511F5B210D1FE72753A138A6" KeyPath="yes" Source="SourceDir\Qt5QuickTemplates2.dll" />\r
-            </Component>\r
-            <Component Id="cmpE4BE73E4CCE2EE3CCA22CAE0C82CD298" Guid="{33CEA31D-C927-4EA2-B6ED-BE88D77154C3}">\r
-                <File Id="fil0A02B63810F4F84B87ED6449067762AA" KeyPath="yes" Source="SourceDir\Qt5RemoteObjects.dll" />\r
-            </Component>\r
-            <Directory Id="dirAB1B0C3E5160B20B93DD2AC0AE9E5509" Name="qmltooling">\r
-                <Component Id="cmpFEC2C3E75BBEACDB1A6F4AFB808CD58E" Guid="{64ED0E30-E431-4238-BD39-465D306D9520}">\r
-                    <File Id="fil09C44C978E58830A831DCC0DBAF3AEA2" KeyPath="yes" Source="SourceDir\qmltooling\qmldbg_debugger.dll" />\r
-                </Component>\r
-                <Component Id="cmp8B3D94901F4D369CD276B339B8B00F70" Guid="{554EAB57-00E2-4FCB-87FF-6F07EF14EDC4}">\r
-                    <File Id="fil3E82AB436354230BADBA9DD68B38C2AD" KeyPath="yes" Source="SourceDir\qmltooling\qmldbg_inspector.dll" />\r
-                </Component>\r
-                <Component Id="cmp0B6C2EAABA7EAA14C510F4C9D68A3F40" Guid="{A211CD9B-6E6A-4600-9FA1-8DF999A942AE}">\r
-                    <File Id="fil8CD8981EA2309B09460020F4D9AC275C" KeyPath="yes" Source="SourceDir\qmltooling\qmldbg_local.dll" />\r
-                </Component>\r
-                <Component Id="cmpE75E4FEE65DCA51D044DF4B2AEDCD828" Guid="{625FA227-EB72-4C9E-AD73-8FAC837EE2D2}">\r
-                    <File Id="fil2A36EBC77D39FBFF748DE570C1F7DC96" KeyPath="yes" Source="SourceDir\qmltooling\qmldbg_messages.dll" />\r
-                </Component>\r
-                <Component Id="cmpA8BBE7010AD624EFBA7166925167402F" Guid="{D044FD19-6C91-4736-80E4-D2DA97CD60B4}">\r
-                    <File Id="filA4CF6CC4639F20D9F436C3DE2C32102E" KeyPath="yes" Source="SourceDir\qmltooling\qmldbg_native.dll" />\r
-                </Component>\r
-                <Component Id="cmp00B1DB73ED33EC1DD6658036D3901CB7" Guid="{0FCBA1AB-A703-4399-A518-94B72B55BBCF}">\r
-                    <File Id="fil04B1E67A26C29E877D34C6CDB21D302D" KeyPath="yes" Source="SourceDir\qmltooling\qmldbg_nativedebugger.dll" />\r
-                </Component>\r
-                <Component Id="cmp9F481CFBAECB8853AF07C1D5060B77D7" Guid="{C2D900C8-5DE0-4EFA-A124-A3865FFE4149}">\r
-                    <File Id="filCD389466FEC0CB778B4940AD0354D4A0" KeyPath="yes" Source="SourceDir\qmltooling\qmldbg_preview.dll" />\r
-                </Component>\r
-                <Component Id="cmp93816105EAC51740B98AF9C1041D28D1" Guid="{D8F9A3EE-B929-4418-A3EE-F3228B05A5F2}">\r
-                    <File Id="filE23CD7951728F9C858C61FDBAFD405D2" KeyPath="yes" Source="SourceDir\qmltooling\qmldbg_profiler.dll" />\r
-                </Component>\r
-                <Component Id="cmp5ED50822278A4A846E891B41C07F7B6A" Guid="{FBDFDAD6-886A-479A-8ED1-D04B0028E9EA}">\r
-                    <File Id="fil935ED8A28D14AB20137795A9C347694D" KeyPath="yes" Source="SourceDir\qmltooling\qmldbg_quickprofiler.dll" />\r
-                </Component>\r
-                <Component Id="cmpACA0A17DA54991A194B823CE40D06263" Guid="{8AA14A55-6F27-48E4-9A18-90D618AD8194}">\r
-                    <File Id="fil6E61B7F24F3C3FDB83A90CEE46B2C831" KeyPath="yes" Source="SourceDir\qmltooling\qmldbg_server.dll" />\r
-                </Component>\r
-                <Component Id="cmp3D2C980EC975DBBCBD13A9178DD1A03B" Guid="{8AA853F3-4BAD-4E76-8F48-CB9FC961AE1F}">\r
-                    <File Id="fil0C06BD9370B146FE43DEFD060222FC12" KeyPath="yes" Source="SourceDir\qmltooling\qmldbg_tcp.dll" />\r
-                </Component>\r
-            </Directory>\r
-            <Directory Id="dir6932501040B63B39C33E640A395AEA58" Name="QtGraphicalEffects">\r
-                <Component Id="cmp356189FC3558D542AA8AD79067A42632" Guid="{25EE1F94-BDC5-4BF4-9CF5-4471CB2CDC99}">\r
-                    <File Id="filC50FC92D4690551FD9149804C9524CB6" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\Blend.qml" />\r
-                </Component>\r
-                <Component Id="cmpABC392DBCC9A5A469B77C129899873EE" Guid="{DB29E30C-5362-4A23-BB9D-C1E6B4569979}">\r
-                    <File Id="fil585EA7DCEFEA763B0D34D92C25BF236D" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\BrightnessContrast.qml" />\r
-                </Component>\r
-                <Component Id="cmp740F889D5799A2FF0F93A24058617EB6" Guid="{304F8096-0A76-4DA8-904E-DBC6ECB871EA}">\r
-                    <File Id="fil44DA8C781EB6783323659B96815E6B06" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\Colorize.qml" />\r
-                </Component>\r
-                <Component Id="cmp2799826256C0E84847E94E11C7878018" Guid="{9D087450-2C02-4859-8100-57FD4BE69464}">\r
-                    <File Id="fil79E5BBBAD1145AE67A9F57C761872AB7" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\ColorOverlay.qml" />\r
-                </Component>\r
-                <Component Id="cmp659424F663F96AE55FBA532B4CBB2EC8" Guid="{B9C2D28B-C4EF-45DB-B9BA-E1B9405D76F4}">\r
-                    <File Id="filACFDAF73B5B767483881AB3B6CD4F973" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\ConicalGradient.qml" />\r
-                </Component>\r
-                <Component Id="cmpDF6A99C1FA6B3943D690F21136BA7F9F" Guid="{03C439A4-BE19-49B8-9F1F-972FF89C415D}">\r
-                    <File Id="fil12DD1BF106F38168A5EE7CAFE7622E54" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\Desaturate.qml" />\r
-                </Component>\r
-                <Component Id="cmp81B459304782BB6AC65E381BA13726B0" Guid="{11D487DE-683F-4647-872C-C2E1DAA33042}">\r
-                    <File Id="fil71154C20C340083D34D22ED1F8EFA765" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\DirectionalBlur.qml" />\r
-                </Component>\r
-                <Component Id="cmp3CC0196A91D9C805D66BFFF4EA38EBE7" Guid="{16A372C2-116D-42AC-B431-3B220C37B9EE}">\r
-                    <File Id="fil0038BC20F6506BDA5417B08B88C92BC1" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\Displace.qml" />\r
-                </Component>\r
-                <Component Id="cmp67E65C02C04C3567636F47EFAA888D12" Guid="{2358043B-BBC7-450B-B5D3-8C26E5249358}">\r
-                    <File Id="fil7602377B1CFE07CFCA64BC753B0AE930" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\DropShadow.qml" />\r
-                </Component>\r
-                <Component Id="cmpADC8E894229BBB48BBC40D4F033FD791" Guid="{2EF6B35A-FD9D-42BA-A12C-3763684AC39C}">\r
-                    <File Id="filC46A98B519397B6CE253A5C9F8D5E0E4" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\FastBlur.qml" />\r
-                </Component>\r
-                <Component Id="cmp0D2D3E713559661141BC5DD9F687F29D" Guid="{7556A68C-05FC-44CB-B9FD-56535DE7653D}">\r
-                    <File Id="filA97688F2BA74A175F3E4EB3F9C1BB027" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\GammaAdjust.qml" />\r
-                </Component>\r
-                <Component Id="cmp3C102DD93EC83164DA87E7820364DDAC" Guid="{6C1D3B9D-A04C-4749-84B6-B276EDC28D20}">\r
-                    <File Id="fil5B2FC5A0BED021E222F6738C10D3081C" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\GaussianBlur.qml" />\r
-                </Component>\r
-                <Component Id="cmp4B666BB592E998D07B55C82CF76F5B1C" Guid="{FAC4A87D-2D90-46C6-98C6-D3A5CB94732C}">\r
-                    <File Id="filC5E145603A0E00BF0F22F2B8A67C9A22" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\Glow.qml" />\r
-                </Component>\r
-                <Component Id="cmp4C99F5024E504FEAB151291478878934" Guid="{E0EB60EE-815C-45C3-B5BD-F8D68E099B3B}">\r
-                    <File Id="fil5F059EE301BFBC9CC82E3CCBEDF542CE" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\HueSaturation.qml" />\r
-                </Component>\r
-                <Component Id="cmp84BC34E1E20AAE86B427C9CD3129C4FA" Guid="{0763338E-FBE7-40BC-9591-8F3B5DB576E3}">\r
-                    <File Id="filA56659AFD9BB9DFBFDDA906B787EFCDD" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\InnerShadow.qml" />\r
-                </Component>\r
-                <Component Id="cmpCBA14389AA89D290E481E0F2625854CE" Guid="{406FB202-E76B-4FFD-A1E4-8FEB2F67401F}">\r
-                    <File Id="fil0C15C4F0A2C4CBC263B38BB384C9DA59" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\LevelAdjust.qml" />\r
-                </Component>\r
-                <Component Id="cmp6FFF646F52B60CE854DA0A1B8C47EC55" Guid="{ED6F3223-0243-4539-96C3-8EE89715F16F}">\r
-                    <File Id="fil53C443677D967DDB997226A1954ED5C7" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\LinearGradient.qml" />\r
-                </Component>\r
-                <Component Id="cmp5E04D231070BE2CE70B2672E56C6B9DE" Guid="{04ACB86E-F6EE-4019-BB49-B556AAF08359}">\r
-                    <File Id="filA148939302F08F0A8B0CC79A709DFA15" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\MaskedBlur.qml" />\r
-                </Component>\r
-                <Component Id="cmp8C5CA067EC2AB41E7CAFE253F2EC86A8" Guid="{76E67D4A-0F5F-4AAF-8A78-847BF0EACDB4}">\r
-                    <File Id="fil2921C33FE8BBE6511E3EB7CA8C06115F" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\OpacityMask.qml" />\r
-                </Component>\r
-                <Component Id="cmp8441DAF2F2BC211F1F6FD10DCE7B51FA" Guid="{70CBBCF5-9030-4FE8-98F0-6665ABF0D3D7}">\r
-                    <File Id="filC8B8AEF14AC36FE600581D00FE128545" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\plugins.qmltypes" />\r
-                </Component>\r
-                <Component Id="cmpC3A1EC5089BA6B0798970A12E614BBDB" Guid="{3EC761B0-3826-451A-8D11-DD0B77E965DB}">\r
-                    <File Id="filDDA0AA71F77A367B9A901655165B1B51" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\qmldir" />\r
-                </Component>\r
-                <Component Id="cmp854B1173EACBB218B6AF849A7E8A56D7" Guid="{F879D731-522C-4E76-85F8-5DF0BD32421E}">\r
-                    <File Id="fil587B7775B793D6255A1BB1DE826E6A95" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\qtgraphicaleffectsplugin.dll" />\r
-                </Component>\r
-                <Component Id="cmp7D04EA58DD5BF794EB2D3A75609DEFF9" Guid="{2A98610D-386B-433E-ABC0-C878C1A632DA}">\r
-                    <File Id="fil9044BD6346C5BE02A746FAC271ED7427" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\RadialBlur.qml" />\r
-                </Component>\r
-                <Component Id="cmp688C34D162C3029BDE9A236F29106F11" Guid="{D34CDD08-E05A-4D45-9800-514F9C53AA0D}">\r
-                    <File Id="fil1B3F7EAA750FA63EF84F1FBA40ADEACB" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\RadialGradient.qml" />\r
-                </Component>\r
-                <Component Id="cmp7F9ABA6F59701379E7E54AB2EF4B01EA" Guid="{618C4845-14BB-41FB-8504-F256C0651929}">\r
-                    <File Id="fil89AD529AFFF6D62301719C7BAA8F28FD" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\RectangularGlow.qml" />\r
-                </Component>\r
-                <Component Id="cmp46E93DFCA823AE21F1FD874F13C0A6CA" Guid="{3248C193-B4CA-4AC1-8941-8096A52FE13F}">\r
-                    <File Id="filD9317E9BA692B66CC2C535F5B3A2F3BB" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\RecursiveBlur.qml" />\r
-                </Component>\r
-                <Component Id="cmp2F1F9DE69090BFD2946A7621F2DA410E" Guid="{2040A763-52C4-4BC3-8C8E-0B7F80C709A5}">\r
-                    <File Id="filBE37116DE14EE120D97D6725D27F4907" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\ThresholdMask.qml" />\r
-                </Component>\r
-                <Component Id="cmpE650E3AF80C0073C2B52BC369D45B79C" Guid="{59F7E175-5A48-4A83-9D2E-13A259A30660}">\r
-                    <File Id="fil4590ECDA88EBA09BB76C6180A1F4FA4B" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\ZoomBlur.qml" />\r
-                </Component>\r
-                <Directory Id="dir796AB3D358CFA364C82060B28476B244" Name="private">\r
-                    <Component Id="cmpB62C6E9BF562754AAC86BB52A361231D" Guid="{DD730ADB-7BC5-40E9-B34E-60DBB65391B6}">\r
-                        <File Id="filEC6B5D0DA92369DBBD0CF163C14FDFBD" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\private\DropShadowBase.qml" />\r
-                    </Component>\r
-                    <Component Id="cmpE98977BCFBF844FDBCB5325BFC639084" Guid="{12EF9B8B-D9BC-41CB-97AF-5A8E73864194}">\r
-                        <File Id="filDA1AA9A2CBC27B220108E0997BC9F94C" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\private\DropShadowBase.qmlc" />\r
-                    </Component>\r
-                    <Component Id="cmpA11A92D9D7D10A9221DD05699B7B1B8C" Guid="{9CE273AB-3553-4CE8-9E0D-40202A50CEDC}">\r
-                        <File Id="fil746D3769B6A878D31F7B39B82F020A30" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\private\FastGlow.qml" />\r
-                    </Component>\r
-                    <Component Id="cmpE9BBCB95E45E4F80549E5C111FA7A855" Guid="{7E341751-8BD3-4B67-8759-A3A4625B6520}">\r
-                        <File Id="fil68500F4B2DC90A64BA585E3D5E9141C2" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\private\FastGlow.qmlc" />\r
-                    </Component>\r
-                    <Component Id="cmpA9B83EF4B33682EFEA153198F1C44255" Guid="{26AF03C7-77EC-40A4-ACB0-7866EE13492B}">\r
-                        <File Id="fil7DF3A69E24DEEFB702D593D5E4B76C0F" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\private\FastInnerShadow.qml" />\r
-                    </Component>\r
-                    <Component Id="cmp8960218D3E3E2D16BFEF14C48404B5D1" Guid="{71C7363F-2CB6-48D3-9FCF-41DE21F660DB}">\r
-                        <File Id="fil0BE696756A233B2576651FD0ABDF1F8A" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\private\FastInnerShadow.qmlc" />\r
-                    </Component>\r
-                    <Component Id="cmp4951B8E17EA0B3A6B265E2C0E893A50A" Guid="{48B858D5-8008-4CB0-B9AA-B3DB33C43E3D}">\r
-                        <File Id="filA527CD28BB0968D628976B78FEB0CC38" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\private\FastMaskedBlur.qml" />\r
-                    </Component>\r
-                    <Component Id="cmp48FE69666F780EE04FC507C3AF73AB4C" Guid="{CCBEE1BB-277C-4B21-9D4D-8C512734CE6E}">\r
-                        <File Id="fil2EF59FFDCD107D97D22F5ADD8449CFEF" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\private\FastMaskedBlur.qmlc" />\r
-                    </Component>\r
-                    <Component Id="cmp2BD742974C0791D6BD1AE4F7E3646D99" Guid="{E43BE5AA-F4D0-4CA7-A32F-3D807202C8BC}">\r
-                        <File Id="fil56212E247BB6F393FBE83AB4A7EAE2FB" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\private\GaussianDirectionalBlur.qml" />\r
-                    </Component>\r
-                    <Component Id="cmp36A30368477DCD705088EDB144FAA0D0" Guid="{73AF5C27-1693-4475-924C-6451ECBF0266}">\r
-                        <File Id="fil0621241FE97703659CD3D7D5C4ADE2D5" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\private\GaussianDirectionalBlur.qmlc" />\r
-                    </Component>\r
-                    <Component Id="cmpF8459C652CF5C20ED87F853C1ABE1752" Guid="{D85D4DD2-A83E-485D-A94D-66B892DBBC21}">\r
-                        <File Id="fil39F0CB1500963D3395433DA5DFC9B13D" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\private\GaussianGlow.qml" />\r
-                    </Component>\r
-                    <Component Id="cmpA1791452BCB3728CD63E88FCBF971D62" Guid="{6835392D-29F4-494F-87A9-B37664364D81}">\r
-                        <File Id="fil3D91872D9408E151E87002A30A11AEAA" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\private\GaussianGlow.qmlc" />\r
-                    </Component>\r
-                    <Component Id="cmpCA0AF56CA87A52053FE61B6ECD156E35" Guid="{6E13F7D4-4D44-43F7-BCAA-A77EB901AF5F}">\r
-                        <File Id="filA28BA0B69A49D99C713AC84E1BE3CD39" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\private\GaussianInnerShadow.qml" />\r
-                    </Component>\r
-                    <Component Id="cmp0F8FC23D5E3669050C7A79355797347B" Guid="{B5251E3C-0B3B-4155-BB91-A23FE75F7FB9}">\r
-                        <File Id="fil172F0C2476A5F0AF55072EC73E6E4430" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\private\GaussianInnerShadow.qmlc" />\r
-                    </Component>\r
-                    <Component Id="cmpBC9523950CF52702CB1A519123F9E519" Guid="{6F73A2D5-41FF-4C3A-80CB-05DE74669583}">\r
-                        <File Id="filE0A177DDE81AF18FB3CFE57A0727C02E" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\private\GaussianMaskedBlur.qml" />\r
-                    </Component>\r
-                    <Component Id="cmpE2FE4AD9E8750F9DCD6AD8C03FFD973F" Guid="{0BB1158F-0EAB-44FF-88D4-F67F9B40DDF8}">\r
-                        <File Id="filBC86557C9AE14F08D5AD621C6B02F3C8" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\private\GaussianMaskedBlur.qmlc" />\r
-                    </Component>\r
-                    <Component Id="cmp302F033AE251BD62E648805EFB1BB536" Guid="{CDCAB63D-67AE-4A62-8FB1-9C0151617C46}">\r
-                        <File Id="fil51BFE00C51A89DC3E74DC04564BCB18A" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\private\qmldir" />\r
-                    </Component>\r
-                    <Component Id="cmpF3F704D8F2236A0A62B980FA41D3CB5E" Guid="{2CB437C9-31D4-478A-8BFA-9D8EBCD368DB}">\r
-                        <File Id="fil09960BC83EE77CD38744C380783E9547" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\private\qtgraphicaleffectsprivate.dll" />\r
-                    </Component>\r
-                </Directory>\r
-            </Directory>\r
-            <Directory Id="dir898F47F1C6F351486C9AAE4005F1B49F" Name="QtQml">\r
-                <Component Id="cmp00AC1A2BE52EA04246F38C5F0B22A9D0" Guid="{DC866072-ECA9-4CA2-9613-C25D77760BE7}">\r
-                    <File Id="filE33FDBE48E6FC8479D62000A1D4DB79C" KeyPath="yes" Source="SourceDir\QtQml\plugins.qmltypes" />\r
-                </Component>\r
-                <Component Id="cmp0A8C093EE03780F9F7823442F10A6C82" Guid="{109B01C3-36B5-4130-9A37-ACA89A7BB868}">\r
-                    <File Id="filFF531EAEA986913D79F95FFBCB9A3DD8" KeyPath="yes" Source="SourceDir\QtQml\qmldir" />\r
-                </Component>\r
-                <Component Id="cmp44B6838E73CD47A3F0061ECCE2EB48D0" Guid="{29B239A3-D455-40DA-8A21-F9AEAAEAA42B}">\r
-                    <File Id="filA5A3A998AB34A694D3B664958BD2376A" KeyPath="yes" Source="SourceDir\QtQml\qmlplugin.dll" />\r
-                </Component>\r
-                <Directory Id="dir48A5FBBF91E1C202003BAC3F8CB3BF51" Name="Models.2">\r
-                    <Component Id="cmp57A7D8DDBA7FB780E38A3CF0C3DE50F6" Guid="{FC1A6BF2-A27C-4AB4-BC53-31092E0481A9}">\r
-                        <File Id="fil6BB08B36A3E11C569382D84D1E88C9B4" KeyPath="yes" Source="SourceDir\QtQml\Models.2\modelsplugin.dll" />\r
-                    </Component>\r
-                    <Component Id="cmp74FB488898EE30854670C634A6CA57CB" Guid="{928C1715-2F0C-4BA0-9CEE-8DA1D740D679}">\r
-                        <File Id="filBEE366112102E123818634DA4D2DD766" KeyPath="yes" Source="SourceDir\QtQml\Models.2\plugins.qmltypes" />\r
-                    </Component>\r
-                    <Component Id="cmpFF41000DFB8364F307DEC40444A8ED49" Guid="{0E33686C-EA52-43E3-83D4-AF793E79EA25}">\r
-                        <File Id="fil4EF76584838408469F9A719687A6816D" KeyPath="yes" Source="SourceDir\QtQml\Models.2\qmldir" />\r
-                    </Component>\r
-                </Directory>\r
-                <Directory Id="dirB873E81E5DE46F6AFD9FA4F4CB07FA16" Name="RemoteObjects">\r
-                    <Component Id="cmpF79607E800F2F8BD75203694AEAEF46A" Guid="{12A3CEE1-10CD-44B8-B7B9-3DB0C53161F4}">\r
-                        <File Id="fil0CCC725AAD4A4C3AF785A0E6C05B5C01" KeyPath="yes" Source="SourceDir\QtQml\RemoteObjects\plugins.qmltypes" />\r
-                    </Component>\r
-                    <Component Id="cmp1E5895F7DD6F9A8239FBB2DD8A187651" Guid="{15329E23-708B-4A03-A52F-7A1D82FCE4A3}">\r
-                        <File Id="fil553CE70C26DE61B484FBA8843B1FEE43" KeyPath="yes" Source="SourceDir\QtQml\RemoteObjects\qmldir" />\r
-                    </Component>\r
-                    <Component Id="cmpBAB6384A9280EFB631A74D56E4849879" Guid="{17B1657A-A346-49C0-9FC5-261B190DCD78}">\r
-                        <File Id="fil9282D53CFA747D92DF590161D94F7C8B" KeyPath="yes" Source="SourceDir\QtQml\RemoteObjects\qtqmlremoteobjects.dll" />\r
-                    </Component>\r
-                </Directory>\r
-                <Directory Id="dir7F50D8639A90BC7F159C74A902C07A8A" Name="StateMachine">\r
-                    <Component Id="cmpE8348F57B7F7ED3E102061755EA59678" Guid="{2BFB9277-FB81-4D69-A8D4-94BF7B3492A6}">\r
-                        <File Id="filEA848B17E993426219C6780A6AD0EF90" KeyPath="yes" Source="SourceDir\QtQml\StateMachine\plugins.qmltypes" />\r
-                    </Component>\r
-                    <Component Id="cmp789571C85C4C76BD48E9A471271B5128" Guid="{5ED22F6C-7FCE-4947-9538-4C89ECB8F4CF}">\r
-                        <File Id="fil4203D832E588C73EC48309F8D3AC2BFE" KeyPath="yes" Source="SourceDir\QtQml\StateMachine\qmldir" />\r
-                    </Component>\r
-                    <Component Id="cmp07E1D3988D9259288197F71CC0B0559A" Guid="{5D21407C-F3C3-428F-B397-9C4E0D666EF5}">\r
-                        <File Id="filFC54BEA29869B52A91A0A804A6843B4A" KeyPath="yes" Source="SourceDir\QtQml\StateMachine\qtqmlstatemachine.dll" />\r
-                    </Component>\r
-                </Directory>\r
-                <Directory Id="dir38701E48D40F53928079A0277B1BE418" Name="WorkerScript.2">\r
-                    <Component Id="cmp91EB04F6C5AFAF292A0BFCE3570DCBFE" Guid="{4D931278-DD8E-4ACA-B8E0-8436B303B917}">\r
-                        <File Id="fil3AD6975608C156FBDC0A714EFD2E7779" KeyPath="yes" Source="SourceDir\QtQml\WorkerScript.2\plugins.qmltypes" />\r
-                    </Component>\r
-                    <Component Id="cmpDA0DD041411FA5AC356FAD21EBB0BFCC" Guid="{04B2D75A-13D6-47E2-BCE4-0E230B94AE5F}">\r
-                        <File Id="fil7757938E9EFF9E82F19FF17128644874" KeyPath="yes" Source="SourceDir\QtQml\WorkerScript.2\qmldir" />\r
-                    </Component>\r
-                    <Component Id="cmpC0C294F18C416B2F09D563A7D2F171C0" Guid="{0B5FC87C-D82F-46BE-AB18-E10BAC7566E4}">\r
-                        <File Id="fil585107DFD90EACA350C802AFD64551B4" KeyPath="yes" Source="SourceDir\QtQml\WorkerScript.2\workerscriptplugin.dll" />\r
-                    </Component>\r
-                </Directory>\r
-            </Directory>\r
-            <Directory Id="dirA71A8D5C38BE16878A4EB5F3B4E1599D" Name="QtQuick">\r
-                <Directory Id="dir1CF0A9FB12A9B3F0BCC43F64CAB69841" Name="Controls.2">\r
-                    <Component Id="cmpBBC07C7AA9E1D56538F0EE5BC883A106" Guid="{18653B7C-05E4-485E-9C62-31D4F2664E1F}">\r
-                        <File Id="filC6250BEA59F89937D08252CE2344D671" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\AbstractButton.qml" />\r
-                    </Component>\r
-                    <Component Id="cmpA2AD6F575E441894DDF6301F935EF3E7" Guid="{6B3F6F40-74AE-43F1-8EDC-97CC40267B5F}">\r
-                        <File Id="fil7E4C91C7F6D6DE165F5E68148D5B85FA" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Action.qml" />\r
-                    </Component>\r
-                    <Component Id="cmp346061900FCE515D6B32CFCD7971325C" Guid="{AE4849A3-BD9E-4D75-84A4-F78DE4AE50E2}">\r
-                        <File Id="filB221D15EF9AD3699086FE43E3C355121" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\ActionGroup.qml" />\r
-                    </Component>\r
-                    <Component Id="cmpE5AC86A748B88CA71F59F050376D4BC8" Guid="{3D56EB30-C37B-4040-9341-53A74B36291D}">\r
-                        <File Id="fil8B70502E8A7420E0F20E1C107A446561" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\ApplicationWindow.qml" />\r
-                    </Component>\r
-                    <Component Id="cmp26A45023E109A672FAA78FE5C4F827F4" Guid="{F0BE556F-358F-4AA8-B311-8102DE486887}">\r
-                        <File Id="fil2FB69D0E22B7CC46B57FAB19D2931318" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\BusyIndicator.qml" />\r
-                    </Component>\r
-                    <Component Id="cmp92014B7FBE8B02881A6A53384BDCD1C4" Guid="{65C1C88B-5B3E-46E2-965C-DCD4426EFCA5}">\r
-                        <File Id="fil29272699B19CB5F1A2B83C23066B00AC" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Button.qml" />\r
-                    </Component>\r
-                    <Component Id="cmp372134AB6EF24BAC7497CC47466F0727" Guid="{B8E80B2D-19A3-434C-A380-DA262254F876}">\r
-                        <File Id="fil9BAAB7742819691F86B3837FCA161A6E" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\ButtonGroup.qml" />\r
-                    </Component>\r
-                    <Component Id="cmp17FB129D73D5BCA38BEC406E5A7A9853" Guid="{B4EEB089-7AA1-4EAE-BAF3-13837AA5AA71}">\r
-                        <File Id="fil40C33AA9302346640FF8F3431BDFCC16" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\CheckBox.qml" />\r
-                    </Component>\r
-                    <Component Id="cmp59BA1197723375EE2B1A8FC0971407A8" Guid="{C256DDB3-D9F8-47D2-8702-9290C36AB4E4}">\r
-                        <File Id="fil1D4756FDB82E5F7C242AC3DC37F9B2F9" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\CheckDelegate.qml" />\r
-                    </Component>\r
-                    <Component Id="cmp9076C49961F4F0F4F5B4F65EF0CE7925" Guid="{6F747374-523D-4121-B79B-E0062B08B433}">\r
-                        <File Id="fil6A4AAD9D20B45D7D2DA5707F1390AF0C" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\ComboBox.qml" />\r
-                    </Component>\r
-                    <Component Id="cmpCBF4FE8555DE1D61528AF79EA3C5870F" Guid="{26599BF7-BCE2-4202-8B5A-F009B999646C}">\r
-                        <File Id="filC5D80E96EA90020D98A1BA9C7A32D089" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Container.qml" />\r
-                    </Component>\r
-                    <Component Id="cmp63B837CFDE5F5C8ADC92D6A80E367621" Guid="{F67247A2-2EA5-450D-87C4-AED6C9A8E069}">\r
-                        <File Id="fil82056DDF3A22A3C956EB50AD96DE681B" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Control.qml" />\r
-                    </Component>\r
-                    <Component Id="cmp73FC7C1B9421F67443A7CA5E03C6F59C" Guid="{2225B539-878A-40DC-986D-742CFC262E18}">\r
-                        <File Id="fil00CD17A8DC26B3D4E21AA1AA951F3910" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\DelayButton.qml" />\r
-                    </Component>\r
-                    <Component Id="cmp24D33989EF8148F7E8E801168FA75D02" Guid="{88F2FD96-6F60-460D-BBEC-F0B0BE0CE94B}">\r
-                        <File Id="fil0F8C8B52CEAB25AD4FD610A146431D1D" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Dial.qml" />\r
-                    </Component>\r
-                    <Component Id="cmp781F94BE6B67AAB9F2DD4A6865F67043" Guid="{12F45E61-A438-49AD-890E-BF5AF913BAED}">\r
-                        <File Id="fil9429984A3AD047F645C68743E4853D3E" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Dialog.qml" />\r
-                    </Component>\r
-                    <Component Id="cmpBF8FE1999E64BE53CD0FFE028F2BA8E5" Guid="{C825307D-6856-48E9-952F-D8DD4371E086}">\r
-                        <File Id="filB66714CD3CC8044DE03BFB0C2998771E" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\DialogButtonBox.qml" />\r
-                    </Component>\r
-                    <Component Id="cmp508D7E9DE20A057C80595708342CCD11" Guid="{79285B58-ED37-4B36-81A1-2F01EE9A8D30}">\r
-                        <File Id="fil4951F0E0C838BCE74B6A4FB412B33C39" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Drawer.qml" />\r
-                    </Component>\r
-                    <Component Id="cmp814DB5A7174FAD3D492689B3CFFD4FC6" Guid="{C5568152-77D0-43EF-BB9D-0066F205825E}">\r
-                        <File Id="fil26FF980D9A1E103DAEF78DA89A8B39AE" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Frame.qml" />\r
-                    </Component>\r
-                    <Component Id="cmp9FCA0D048C5FEDEF35CFDCF4220D148D" Guid="{552D678A-1984-4C88-89CF-42371097B8C7}">\r
-                        <File Id="fil7EC20FB2AF608FD4D48A9F19F1B92A7D" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\GroupBox.qml" />\r
-                    </Component>\r
-                    <Component Id="cmp8302E43F7B461A53125D24EBAB2C8BBA" Guid="{9908593A-2DF6-4B1B-844E-22E9D4AF8FC4}">\r
-                        <File Id="filA490553F561E23B426ED5F47B779E827" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\HorizontalHeaderView.qml" />\r
-                    </Component>\r
-                    <Component Id="cmpDD4764A31C5966C382C9FADCF2747DAE" Guid="{A8C30115-60B3-4519-BCC1-668717555DB0}">\r
-                        <File Id="fil20BF35D7A8C7028231C16A4F8A8B8E7A" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\ItemDelegate.qml" />\r
-                    </Component>\r
-                    <Component Id="cmp4DC6229A880E85A8EE64B69A175EB7F8" Guid="{59036E4B-ACD7-4733-B14A-EBAA93E714E8}">\r
-                        <File Id="filADF106B97E56C6627AF27930AA08B829" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Label.qml" />\r
-                    </Component>\r
-                    <Component Id="cmpC3E4C72480B21745D1D55462CD128148" Guid="{B1B90609-C99E-4B1A-8B10-13BE83B179BB}">\r
-                        <File Id="filCDF998E5B0CD0C7D86DEDA98E71F235F" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Menu.qml" />\r
-                    </Component>\r
-                    <Component Id="cmp9C2C5E7D9EAC8AD7E9FB8BA9B0147BAB" Guid="{BB4200E2-ADED-44D8-A1A1-31A0E8707754}">\r
-                        <File Id="filA5709A532A6415760B623CBD689B3F88" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\MenuBar.qml" />\r
-                    </Component>\r
-                    <Component Id="cmp66ADBF15C183C1EF05BA59EBF57DC04C" Guid="{592C458A-E1E3-4735-A22F-92B5FEECBA7F}">\r
-                        <File Id="fil9818915B5CDD8FDC7B9B723C438DDA64" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\MenuBarItem.qml" />\r
-                    </Component>\r
-                    <Component Id="cmpB85DB993D3D58E717F4EC2A2A5AC4130" Guid="{604A7793-4EC9-4F5F-9BAF-DE22555BFF3A}">\r
-                        <File Id="filFAE639B09FB48AA6FC953557339B53EC" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\MenuItem.qml" />\r
-                    </Component>\r
-                    <Component Id="cmp93014FB27329F9718833E30A8DCE2F23" Guid="{EF1D6D21-BDC6-4DDF-9BC0-875EC3FAE40B}">\r
-                        <File Id="fil09A4B643E4AEAFC3F22479FBC0E09388" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\MenuSeparator.qml" />\r
-                    </Component>\r
-                    <Component Id="cmp9D0CC60826A95D19D52A3A08FB44155C" Guid="{DFE9585E-6220-430F-9F1D-1B2130D88FF1}">\r
-                        <File Id="filDFFEC8756E4A921EAD31C7CD841BAC7B" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Page.qml" />\r
-                    </Component>\r
-                    <Component Id="cmp06BD88C78B72DD375F0FD8DC37812A7A" Guid="{9DE25AD5-FA94-4DFB-B129-0FFBF312E353}">\r
-                        <File Id="filDC61FDA5B2042E021FC28DFA1C464ED4" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\PageIndicator.qml" />\r
-                    </Component>\r
-                    <Component Id="cmp6577B61F7B803092F263CE9BD76D7B19" Guid="{D6D9670E-CD58-40D4-B772-C8AC087E74B3}">\r
-                        <File Id="filD74C331BEAD78D4B70AE295DE481F56E" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Pane.qml" />\r
-                    </Component>\r
-                    <Component Id="cmpD880E6EDC6A23CDFF94AB5E0A0018A10" Guid="{A3989AE9-2FD6-470A-B8CF-78D908DBA2E6}">\r
-                        <File Id="filA2017FDECC21B1BD1F3A42BBA6781F78" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\plugins.qmltypes" />\r
-                    </Component>\r
-                    <Component Id="cmp00D081BB0762FEC39933D1C2960FC831" Guid="{ED79D960-6E3D-4B98-91D1-376FFD6D0C55}">\r
-                        <File Id="fil3BF8126A47717D54F2FC8B0E9EE88747" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Popup.qml" />\r
-                    </Component>\r
-                    <Component Id="cmpD69A2401CBD6D26E44BE1271C8905A24" Guid="{0231D127-E285-4F07-8AB2-AB26F8850A75}">\r
-                        <File Id="fil7827CED2892E4BD2A7216EE8DC487134" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\ProgressBar.qml" />\r
-                    </Component>\r
-                    <Component Id="cmp0B7DD63BE81BC1EA8DB09A99C12CB156" Guid="{A7BE0958-70D3-4C14-82E3-FD911E5B2EAF}">\r
-                        <File Id="filBF429532FD2FB957D749DBFBEC0C1781" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\qmldir" />\r
-                    </Component>\r
-                    <Component Id="cmp1EC09C5C4D6CE477EAA2BE5A3798E1CC" Guid="{BA7E711D-1F6E-43CA-9957-C06AF625F0F8}">\r
-                        <File Id="fil8A3E57CDCB38EA2957526857F4BF7833" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\qtquickcontrols2plugin.dll" />\r
-                    </Component>\r
-                    <Component Id="cmp577EBA2061AD5D464AC5B629EF1A63DE" Guid="{248F67F3-A6F9-4619-B355-BB41E97401D1}">\r
-                        <File Id="fil2CF3A6D6E008AC1C946D645F7484E0AA" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\RadioButton.qml" />\r
-                    </Component>\r
-                    <Component Id="cmpFD9033FF82B141429428B759027C0ECB" Guid="{E0382F5D-D17D-4955-81ED-046C720C45AE}">\r
-                        <File Id="filBBA1B0E513D4C6E93929431510FFC51E" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\RadioDelegate.qml" />\r
-                    </Component>\r
-                    <Component Id="cmpD9A7A9076E4844ACEB4385E651C6FC27" Guid="{8B8F1FD8-F041-431E-BC04-94955681EC8B}">\r
-                        <File Id="fil92EBFA0D398D93C208D55E0F2DE5C412" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\RangeSlider.qml" />\r
-                    </Component>\r
-                    <Component Id="cmp952D38FA5543D46E8B0B6CAC867E6D19" Guid="{6E73EE8C-FFF9-4D5E-80E4-49DCE2750DD8}">\r
-                        <File Id="fil3BC533F32E89455AE3C47B34F2D990BC" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\RoundButton.qml" />\r
-                    </Component>\r
-                    <Component Id="cmp8360F4A2F8333FCC0A95F36461DAC0AE" Guid="{6FEA34D3-39E4-4BC0-85DB-ECDF94B77477}">\r
-                        <File Id="filACE9F613C573BFC25A9A0E48B6C143C3" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\ScrollBar.qml" />\r
-                    </Component>\r
-                    <Component Id="cmpA8C48D6004583C73A28027E805ECF241" Guid="{FB1C3A2B-9330-4EA7-A34B-25612A2C2E6A}">\r
-                        <File Id="fil7BD43F79B053411805FC22E0BD5D488F" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\ScrollIndicator.qml" />\r
-                    </Component>\r
-                    <Component Id="cmpF0F52E0AA644EE5B8A4212507A2A9632" Guid="{40D7143A-FFDF-44AC-ACD4-00A6A5464BE7}">\r
-                        <File Id="fil89005ED22EF40F5C060D464B8096616E" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\ScrollView.qml" />\r
-                    </Component>\r
-                    <Component Id="cmp1F2A598B5F572A95F619F914ADFDFD01" Guid="{3536777D-FF8B-4EEA-B93C-5EE1301E5039}">\r
-                        <File Id="fil1C7BF5B291EA31328091F13B845FED6A" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Slider.qml" />\r
-                    </Component>\r
-                    <Component Id="cmp894CC6997FDC49D32993AF049D19661B" Guid="{30F4723C-B159-4C29-9EA0-C8470CDC3204}">\r
-                        <File Id="fil298110AF9B2ED52166A7A0ACE2C9EEE9" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\SpinBox.qml" />\r
-                    </Component>\r
-                    <Component Id="cmp45A86D8F1E2A0DB9085F1C386BE8C441" Guid="{361C717A-E7E1-48E2-B5FD-E934ADF6C5F5}">\r
-                        <File Id="fil7E7C8E62F5F6317023CD5DDFBE502AA0" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\SplitView.qml" />\r
-                    </Component>\r
-                    <Component Id="cmpD7F91C9BF1B37E7ECEFC803745680533" Guid="{2972CE14-8B1C-44AA-B8DE-80551FE96662}">\r
-                        <File Id="fil6479652BE44E448F123964A1BFFDC025" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\StackView.qml" />\r
-                    </Component>\r
-                    <Component Id="cmp6C7380F6F9D3432EF0FF68155BE5B7EA" Guid="{D8A7D60B-63A0-4AD1-950D-055177620665}">\r
-                        <File Id="fil2616AA919B65C7E1223B8EEAE8A0A520" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\SwipeDelegate.qml" />\r
-                    </Component>\r
-                    <Component Id="cmp163F0198648660C0C4DED70616CD3A50" Guid="{1FBF4796-E2E8-4ADB-A545-4BF2807D20B1}">\r
-                        <File Id="filCF410250FBBD03B7BDF88E86CDE7F5FA" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\SwipeView.qml" />\r
-                    </Component>\r
-                    <Component Id="cmpF5CA7A4D3DB71A67C3E4BC87D9A553DE" Guid="{81DD052B-A012-47F6-B62F-EA0E79AC7846}">\r
-                        <File Id="fil263B8E09CAABE937FBEBF1178E2C8264" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Switch.qml" />\r
-                    </Component>\r
-                    <Component Id="cmp2599D94F81ED612C05479E9A8F58A189" Guid="{7DB969D4-59FE-4E04-8D11-E3BDEFB46738}">\r
-                        <File Id="filA4030299DEE7EEF863C5BDC4ADA0363B" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\SwitchDelegate.qml" />\r
-                    </Component>\r
-                    <Component Id="cmp9557C3464EC921CAC1A43D77B20BA92D" Guid="{DB31EB03-0A74-4002-B530-18B87D842D6D}">\r
-                        <File Id="fil13E41D7725EECC41EB76F1C55FB37034" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\TabBar.qml" />\r
-                    </Component>\r
-                    <Component Id="cmpFDD06D370422F04997F37CDCF9077AAF" Guid="{8EAE7AAA-839E-4BD7-A7D2-135517DD05B0}">\r
-                        <File Id="filC1B3E77F09168E169AC46F312631A9A5" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\TabButton.qml" />\r
-                    </Component>\r
-                    <Component Id="cmpC5C6ADF92BCA401BFD4AB6B8DF07ABE6" Guid="{E2A3B225-AEDA-4573-B8CD-F3F84AE9A8A0}">\r
-                        <File Id="fil8804492F60CD4E2F68C71219E02292EF" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\TextArea.qml" />\r
-                    </Component>\r
-                    <Component Id="cmpC7F75CEAF1EB837B6998ED5DC6550404" Guid="{01405B24-2CEB-418E-9B59-5F96A26C9D50}">\r
-                        <File Id="fil6F19FED3B4412AE010061DF7D4BD3DA0" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\TextField.qml" />\r
-                    </Component>\r
-                    <Component Id="cmp4E0B2D6970453AF152BA060236BD8B71" Guid="{5ED9BA2B-AD36-4E01-9655-9EE6A765360C}">\r
-                        <File Id="fil340E1F0612644D4DC4EADFE9E87F24EF" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\ToolBar.qml" />\r
-                    </Component>\r
-                    <Component Id="cmp3A32B8F13C49569A0A4AD1D11045BE3A" Guid="{A7D20B7C-B367-4014-9FDE-1DFBDCEB6C9B}">\r
-                        <File Id="filFBEB8E9016BF58F623173CDBF130428D" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\ToolButton.qml" />\r
-                    </Component>\r
-                    <Component Id="cmp6D11E1D44ADD3049CE735F324F7F4521" Guid="{A972070C-BE88-401A-8BCC-8C6F4C4D9071}">\r
-                        <File Id="fil18978F440E9B6E6F62CE45EEC31C0F8F" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\ToolSeparator.qml" />\r
-                    </Component>\r
-                    <Component Id="cmp6763D9C183B24CD6659B616DD72994DA" Guid="{BD007679-0CF6-49ED-B858-D57788EFEA1F}">\r
-                        <File Id="filB20E33BEBD71D625CEA0199980F77F55" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\ToolTip.qml" />\r
-                    </Component>\r
-                    <Component Id="cmp0FEA3716333AE56EBB72D727432DFB0A" Guid="{1BEC8363-459E-4097-BCF8-724A52CCFD53}">\r
-                        <File Id="filC8F8B2D82FDC1ED4350CA76D702241C7" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Tumbler.qml" />\r
-                    </Component>\r
-                    <Component Id="cmpF77FF7EB150083CBBB82946EC3F849C5" Guid="{77DC42B7-660D-4EB2-9553-1A8F3A054CB6}">\r
-                        <File Id="filD758C10071B91117472250A6D6E5C07A" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\VerticalHeaderView.qml" />\r
-                    </Component>\r
-                    <Directory Id="dir035355762BDDA6A88B77A84B585AD0EF" Name="Fusion">\r
-                        <Component Id="cmp8693A45A9517FDC707611CD9592AD887" Guid="{FE1A6B72-4416-4058-B8DE-FD0ADA5E00AB}">\r
-                            <File Id="fil7A76573ABBE1FDF6842F852511E36ED4" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\ApplicationWindow.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp22009CC98E7BCD73739238DBAD70F5FF" Guid="{EE86CB3D-757B-4591-8A4A-71DFB47EEC98}">\r
-                            <File Id="fil4D6655B7651D7B655CDC465A96569C20" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\BusyIndicator.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp006248D9C946635CD2C0D21D711BAF97" Guid="{71322A32-8BC1-4BC3-A2D3-43877F86E8E4}">\r
-                            <File Id="fil4677B542408030BF8A7E55B148B934EA" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\Button.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp8F4980EC013CDADC9677179C1B5FB4DB" Guid="{9C60553B-BC7A-4E02-A56A-58833FC75143}">\r
-                            <File Id="filF44FA16699EA690B580F758E29738AAA" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\ButtonPanel.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp7CB5E11B511D00866734F2A59242071E" Guid="{AE591101-9987-4988-BB53-276AC7E46073}">\r
-                            <File Id="fil3CF03534ACD9304F06C0597102C48388" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\CheckBox.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp15339C0E193CBE17FB6CB89DA971471D" Guid="{E5A1CF93-FF79-4BFF-9429-876C326CD587}">\r
-                            <File Id="filB933AB67B806FBFAEE5B4C6EEA24FD2A" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\CheckDelegate.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp7A634DCF2DA1EDFF9FBAF699758570C3" Guid="{E8EFCE73-1AB6-466D-AD08-C8873EF95506}">\r
-                            <File Id="fil3FDFED2EAAAED2687815DA9661B0C595" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\CheckIndicator.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpAA64F717D23C1375E79BE28E5A3DE005" Guid="{EF2F8A2E-4BBF-401E-A1B7-041B7329F812}">\r
-                            <File Id="fil95F73162924608F461FCFA52B66A0A6E" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\ComboBox.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp14F754092A68F3FE755963981761F031" Guid="{E0E26331-F0B7-4DFF-89E5-8894EF16DAC2}">\r
-                            <File Id="fil96C211B2F831A30A5AD62EDC6CB0AE86" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\DelayButton.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp8DBD1F81C17E5D01D819E619D30E83CA" Guid="{242B321E-98A7-4685-84E9-8C8BD6CAFC92}">\r
-                            <File Id="fil1CC8736C80CF69BDCE8C8B989F215658" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\Dial.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp9A97912EFCCFD956EF8F155A81CB494F" Guid="{D2C607E0-8E4B-4F55-B60B-024D8BC34882}">\r
-                            <File Id="fil9912627C86943446F991D141E64E84A3" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\Dialog.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp37BA96169B5087F3D2C6E769FF5E9270" Guid="{B5153F7B-2A7E-4FB2-B7EA-C68D043B4F76}">\r
-                            <File Id="fil9D217918F4DE6A7DE53939E61C810E92" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\DialogButtonBox.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpE74780CCB3558FB4C37DDE65264DC1F4" Guid="{4F45062D-232A-4157-ABD7-9351742C3994}">\r
-                            <File Id="fil404168979C602A6C3E2A8E77521843CF" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\Drawer.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp86D5D0E9BB2DE2BDCC6CF459CB7C4E01" Guid="{6FA92936-097C-41B0-A5C2-E7DC100A7908}">\r
-                            <File Id="filCFFF61300EF689F96CFDD9B3A8F7B83D" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\Frame.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp3951161EB256315C232D7C2B211A5D42" Guid="{FD64BFEF-8970-459F-843E-9D4E07A8DEBD}">\r
-                            <File Id="filAC8A24BE65028BFAF11984F7DB0B168C" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\GroupBox.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp6CAF0B6CD8ABE85E62A160036392A2CC" Guid="{88F69974-2FD0-4D7C-8E3D-36CD596B9459}">\r
-                            <File Id="filB0D55ECBE8B840F45E54296721149C79" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\HorizontalHeaderView.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp55E217C43921E96FF82040712469E0F3" Guid="{D471DAE1-7C90-4202-91FB-0BE5C9E77506}">\r
-                            <File Id="fil2D7C328BD2280D7E0E3ABE5468F4CEDD" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\ItemDelegate.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp57277D9EB23963010750499ED2F541B8" Guid="{1096E01B-7229-42C2-A357-E3C45DA54168}">\r
-                            <File Id="fil3FAB3A32AA7A668FFB5482A34769E564" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\Label.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp651F7330940689374F3F4500C1F20484" Guid="{0D729967-F2B6-44F7-85D7-461595625C6E}">\r
-                            <File Id="fil2E6E1F91E7A60FB5D16E4C853B247E7A" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\Menu.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp02C5C41CDED7F94E885A4A2A28EC3600" Guid="{F4E65E26-2552-46BA-AF61-C547429D8F01}">\r
-                            <File Id="fil46C51416AFB762FFEE81E338F1212F54" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\MenuBar.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp95C32716B4F28A90B070E08CAC81BAC4" Guid="{7B371B0C-5AD7-4E93-B5E1-173CC26FD81C}">\r
-                            <File Id="filDA216EA9FC2E71295498C5082FABF4AF" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\MenuBarItem.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp2CEB59611FF3F33F7AEF7D2EAAE0CB05" Guid="{2F5B5AEE-CA5D-4579-AE12-C90238707035}">\r
-                            <File Id="filCA0597907941481E694D27A9151A67F3" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\MenuItem.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp8D1031BFABA660F80B7793BB3761EB75" Guid="{E7361D28-855B-4961-8AA5-71A3E6BD7F08}">\r
-                            <File Id="filD9334919AAFD0FAA024307105F6E2274" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\MenuSeparator.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpB52CD2CEE9160F5F67372170543F85B4" Guid="{3D125A08-8FBF-4F5F-8632-A08FD9F18975}">\r
-                            <File Id="fil549E037AEFA7D53E8ABEFA528585B365" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\Page.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp909217E4E53799CCE36ED3F7B817DF15" Guid="{7CDFBC69-1743-4B4D-9D6D-CC727453AB80}">\r
-                            <File Id="fil622B076063785FD360184B7CCE9C1963" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\PageIndicator.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp88C4BDBD3D48F447FF34FD6DF1FC3C41" Guid="{8249DEF8-38C4-4F19-974E-6E455FB45D9F}">\r
-                            <File Id="fil258E3E262CBA06FD489EBCE314036F9D" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\Pane.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp6D134750A96C36CCE37CDAEAC146A810" Guid="{AC624DFB-C752-43D2-B643-A18625D081A8}">\r
-                            <File Id="fil1872A6AD9CAD77362D7D04C024F76B25" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\plugins.qmltypes" />\r
-                        </Component>\r
-                        <Component Id="cmp17431F800533AAD492A8DA91C537AEE5" Guid="{CC784F75-F543-4123-9E10-3097581F6EE6}">\r
-                            <File Id="fil5955CD9FB6B733D9B586B52CCD59F161" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\Popup.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp5651D28BAA4D7517AB64D608440B8947" Guid="{0E1DA31F-123B-411E-A310-910106CF7103}">\r
-                            <File Id="fil1F6E4572AA5C15F6F2F86B70AD845F39" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\ProgressBar.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp1FC8B77EF51F392FB0F6CD269098431D" Guid="{921782D1-C60C-4012-B36C-D44FBFB1A9A1}">\r
-                            <File Id="filDEC4823160DF80F031FFEDD16C5FA73B" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\qmldir" />\r
-                        </Component>\r
-                        <Component Id="cmp403E7038C7324A61D8727DC1D939E547" Guid="{F851CBDB-DEF2-4DA0-AB32-F038CA4D94F8}">\r
-                            <File Id="filD279E9A3D628C42CFDEC99CA9E5A2F3D" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\qtquickcontrols2fusionstyleplugin.dll" />\r
-                        </Component>\r
-                        <Component Id="cmp98D46B3DC15B713ACF5F53B34EE13068" Guid="{8C570892-F30F-4CB2-AC3A-47382EC6B368}">\r
-                            <File Id="fil50FF1AE0E3D989A4CA249AAF9F8A7B26" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\RadioButton.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp82998B9C0403FF9F31583386A05AA9BB" Guid="{74DB260E-F5DF-4426-BDFE-14F1B954C661}">\r
-                            <File Id="fil1A72CA8DAF4CE929C79B9574D2F30382" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\RadioDelegate.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp0D0B1F4459593A8B3D4A1BCBA139D98F" Guid="{FA5F9B73-E11E-4A44-8F7F-1C51E7F48E5D}">\r
-                            <File Id="fil46018A40ED31116940522AF13C6F1FBA" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\RadioIndicator.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpF083A0EDD7D82B7EEFA116A49700462C" Guid="{809F9508-ED4F-401C-89F7-D01F181560F7}">\r
-                            <File Id="fil3E28D7DC0567E0CD67D51AB0878E1837" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\RangeSlider.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp094723DF4DF9079CABB07B397F53FC14" Guid="{75821D85-CF6B-464F-90B3-72300BC5245F}">\r
-                            <File Id="fil0C857F8E3C8E3F5D9E839C9D1A2D1BE6" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\RoundButton.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpE46F1304BE1F25953FA1502338B46B6E" Guid="{E57FB784-7FCA-4108-8A90-43C9DAC92E74}">\r
-                            <File Id="fil0A615F671CBAFA25B83B6DDC1A57BE65" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\ScrollBar.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpF34990BAAB80CEB1B1FBA3F75A8BB18E" Guid="{BA75016B-BCEE-4639-AC14-960BBBA684CC}">\r
-                            <File Id="fil4926BC97FC5BF303193B003042C8161D" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\ScrollIndicator.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpF8164C6D715D603507F60EA7662104BB" Guid="{3DF8922C-C3E9-49D8-A369-F8D21BFB1D94}">\r
-                            <File Id="filBAF585899E98CD52881FF18EA3FB54F6" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\Slider.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpBC49C3C8C0D1C882B8E4BFA5DDB84097" Guid="{7A79B435-9227-413C-9444-137D88C7B937}">\r
-                            <File Id="filF5CA1B27FCBF261A45FC19EA4D7BA023" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\SliderGroove.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp026A749908FF7AE58055B0E1AB393168" Guid="{DE24E255-176E-4087-AFAC-E6F56B5D5EAC}">\r
-                            <File Id="fil929EAB5C19264E6900242F51F02822E8" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\SliderHandle.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp4644F2A5A8ADFEA7ED73D09A41CB9AFF" Guid="{2A5E1174-6741-4BCF-BBA3-CE6FE52F79AC}">\r
-                            <File Id="filBA9EEEC5C7078CFE9E1B0DF8A177B5ED" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\SpinBox.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpF0E7DBBAED60C3A0D5E4980C6384C524" Guid="{5DC166C0-8A39-44D0-A9B8-FF474AC6EBDA}">\r
-                            <File Id="fil15BD010BEC93BDA46E1F2686D8FF6EBC" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\SplitView.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpF26383485D9640683A7276A7CFD06280" Guid="{BF3FBD86-AD76-426B-A685-CDF8FE76CC5E}">\r
-                            <File Id="fil55D360EE1ABEF5F5BD900B65B188D41F" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\SwipeDelegate.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp5D85AEBC11476CF382025D6654F36103" Guid="{029CDF92-C9FE-44CF-B9B6-E4A1673AB30A}">\r
-                            <File Id="fil3EDB2B2C23697DAF9F92FFBA99A84650" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\Switch.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp44002125F963B5A1D193E0D787FF6AF6" Guid="{04725E3F-4601-427F-9A74-36F09BC8A604}">\r
-                            <File Id="fil50318A9E962C21697CA18156BA2D0905" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\SwitchDelegate.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpF8E1020B8AEA4B8F647DF3962BB9CF2C" Guid="{FA2434F7-F3ED-4B26-A434-58E112C46E31}">\r
-                            <File Id="fil921E1EA2463721903BCAD40702BF03D1" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\SwitchIndicator.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp6F5B3D19FBA609A3ECE0700E2DB1C805" Guid="{9F1F0447-B481-4AB2-96BF-259633CF6889}">\r
-                            <File Id="fil8B653517ED9597AB20ECDD5E3A75C756" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\TabBar.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpF0883486B5B318E646462C05EB5DBF23" Guid="{E121C66B-7244-4EAB-8225-A363D938EB07}">\r
-                            <File Id="fil17982E06A90DCC3F94109971D657D754" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\TabButton.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp8F7277DF6FE75EF54431CCAB073199B5" Guid="{D75EED9E-1240-47D8-9DC5-5F5832E061F9}">\r
-                            <File Id="filE3100069BCDAE1F6526502196EFBED22" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\TextArea.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpBC3A73EE4B192F1BF88AF2E61E4212EA" Guid="{B137AF9C-F8CC-446F-B263-2DD5A818E699}">\r
-                            <File Id="fil3563463C4219FC7B6DFE6A95F3E9B75A" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\TextField.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpD48A473B45218C0BDE5C64912EFCBAFC" Guid="{2106F73A-F03F-4BEA-A2B6-D8B6C8632E15}">\r
-                            <File Id="filBA1284C10E3F10A1ECC1A687F9997CB4" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\ToolBar.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp86201E518FC2273E24BCD76E296C18E1" Guid="{1027D379-4165-4AC8-B902-6ADAF098ABEB}">\r
-                            <File Id="fil8B324D3244F760FD93C00D0E58BAB57B" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\ToolButton.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpFF8739D6C4BEC98E8F1240FFBD0A64B0" Guid="{AAD9D3FB-1258-4397-8DB0-703866C2D3CF}">\r
-                            <File Id="fil47FA961107A0E38C1F9CC57AAFD5CC5C" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\ToolSeparator.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp38720475F2109CBC7EF4F43C365CC5CA" Guid="{BCF56568-1D7B-40A5-ADCE-DD4376A42888}">\r
-                            <File Id="filB001EB30F35E5F650A63D03CC18ECE35" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\ToolTip.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp996D8C56417E10EF44329F7339E474F2" Guid="{1C26B967-00BF-4D70-8844-0A80BB8FD464}">\r
-                            <File Id="fil93134119EC59B8D442C2BE7A07DD5D2B" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\Tumbler.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpED22A8F3ACEE04AB6CE44D533F4F300A" Guid="{72F16F90-779D-465A-892E-3B55A338F3AE}">\r
-                            <File Id="filE8C20555ACA351E1325A91CF709D8D00" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\VerticalHeaderView.qml" />\r
-                        </Component>\r
-                    </Directory>\r
-                    <Directory Id="dirFDC25EE37CF6C040EF6B98E257279EC3" Name="Imagine">\r
-                        <Component Id="cmp838B8853704ACB2A7F45BA00B950F3BB" Guid="{141E5A2D-23F0-4522-864B-D3456E2280FD}">\r
-                            <File Id="filC7285D9D5529D85E5E0E217C2D16EA81" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\ApplicationWindow.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpDF9CC0BB490A823FFADEFFBDAB92C2C7" Guid="{5360026C-8AA3-4FE8-AF6A-CABD86B2B70F}">\r
-                            <File Id="fil9835BFE6BBC02870FA9F628A2E0DB6E4" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\BusyIndicator.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp73DF7086A467801478C7321A8B435AA2" Guid="{37150093-DBE6-4270-81A5-8D100EB60F79}">\r
-                            <File Id="fil5DA765828D51355DC019424A172FB9C1" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\Button.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp10D7AC86A56DE2D04356715F35250435" Guid="{4371D289-F3B2-4B73-9E56-9903FA7DCFE5}">\r
-                            <File Id="fil9CB23D5FD7586E7DA94F805DF1529360" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\CheckBox.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpB8BBD8CCBCDE8771AD9E3D6D456FDD6B" Guid="{744F1464-6E9C-4793-981C-73CB5510C194}">\r
-                            <File Id="filC24A1ADF38694A93849B2804D4415C22" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\CheckDelegate.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp5DA5D6BC1A869E7409CC08ACDEBAA08F" Guid="{6769AE38-D978-44BD-A8C5-113A1244E476}">\r
-                            <File Id="fil3F2D3CBED068F4115DE0002AAC205A4F" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\ComboBox.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpEFE26691A6CBD74691FA18F89319520B" Guid="{50DC9AD6-A6CB-47B6-85DC-1C319A7F6F27}">\r
-                            <File Id="fil1164CEFD82A18A964BA86D08201F9CE8" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\DelayButton.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpF14CA7FF23A8FEBB250D9C1A4DBAB5F2" Guid="{6102C842-3167-40B0-A897-7A562F821FA3}">\r
-                            <File Id="fil535F2A996B2C4FC84039F35960BC8B8C" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\Dial.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp76B901918023AFA1ECF5AFE1C5AADFD6" Guid="{B925E4BD-BDF8-419D-A21A-C4F593D261B6}">\r
-                            <File Id="fil2A26AB8828D225E79420414A7C55B95B" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\Dialog.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp3B04385170209415EA0392D6C6D9394D" Guid="{FFC78D64-1508-495B-A0E2-0A2290A40E05}">\r
-                            <File Id="filBB42C691204A19DFDDA38926D9A52E56" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\DialogButtonBox.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpAC7FCF48DC2DD2FF598D8D8EDFBDDE10" Guid="{68D0C46D-1AB5-4CA5-85E0-2175ADEC7360}">\r
-                            <File Id="fil152D23A298E533A4987600002CF20A9E" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\Drawer.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp31295E08BF0ADC9F7414B879A59EAB38" Guid="{EA9C98FB-2D3F-4970-95A7-2CFE7B8C3FCE}">\r
-                            <File Id="fil652FE91BEC083A5CA0F16DAC5115ADD1" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\Frame.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp26ED7398D2B7FD7CE79B20C6F81AA21B" Guid="{C1F1452B-9C22-49DA-AEEC-466D9615DB99}">\r
-                            <File Id="fil4E530D55EE5754394244F29DDD5A4339" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\GroupBox.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp0AF138C48EF9389FB53BCFFF127C63B7" Guid="{99C63B6F-E80E-489E-B320-33CB9324A31C}">\r
-                            <File Id="fil7623E549EBD32B153F482A9ED3D10247" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\HorizontalHeaderView.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpB84DF029169B6913288463C0CD791FD7" Guid="{0982DCB5-B9A0-4CEB-9E86-DB3A55620327}">\r
-                            <File Id="fil47584D2EBEBF080D3E283A08BD78771C" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\ItemDelegate.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpB2768FB92A037D37B0EDB0A50917DB6E" Guid="{C49D785A-76CF-4A89-8FF5-67F8DDA0A0D8}">\r
-                            <File Id="fil85E8BEE2FF4E7D88271DEE95F4B021F6" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\Label.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp60DE9C3DB4C8AE187B4D19BDEDC6C16A" Guid="{A7D686DC-8D8F-4482-96F2-6D0C4F5BCFF0}">\r
-                            <File Id="fil21654FC5F7960A777690B781D204AE5F" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\Menu.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp8439239F2F45A8EAE3D84F080943DDD1" Guid="{048C9830-5BC6-4C43-8E25-3C6B349700F9}">\r
-                            <File Id="filB0FB084CE7E619D3AD1FCFC01826933F" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\MenuItem.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp16223EE59A9E78D62203D037E29D698D" Guid="{7B56130B-F94B-4A3A-B065-E6E4B66A9C20}">\r
-                            <File Id="filF1296202126A8FC1A5FF68833D23C1DD" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\MenuSeparator.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp018AFFA2C882E3A63FE78C48058C9701" Guid="{76589C5E-289E-46A5-A080-04A8D1FEC084}">\r
-                            <File Id="fil16A10C6B3AC90B78F38FF79B36A1D633" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\Page.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpD9344D05ADD481EACDED9FFB5EE9D160" Guid="{CF6DB635-DAE3-4E6C-8A89-3F5145779366}">\r
-                            <File Id="fil1D2EF351CF8F0C59F0CF7CD0CF748B0E" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\PageIndicator.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpA9175F4256E542F0F7D7729B65AC8716" Guid="{1B82A4FA-CC2D-45D0-8871-6FDF98408E5C}">\r
-                            <File Id="fil6ED79940CE9EDFACD1AA339B928CD411" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\Pane.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp022C885B9AF26895C6BBDBE1AF964DA7" Guid="{D7776A87-CE99-4B5B-BDD0-A6ACC91624F5}">\r
-                            <File Id="filD12EF181236C4FD2D8823E2A06702BBC" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\plugins.qmltypes" />\r
-                        </Component>\r
-                        <Component Id="cmpB42CC102A79539E49D9D71AF819BDCFA" Guid="{FB36BE43-4D52-4433-A6B5-F2915F1CBA12}">\r
-                            <File Id="fil9D3116A765C1FFBA2E5F4AF29524555B" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\Popup.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpDE27E4C6B7C8141F7B15529523211BDE" Guid="{5EBA816D-DF14-4E34-A151-FA79A354623A}">\r
-                            <File Id="filC4C7A697145B9CE44EC9CBEE5AD5EB56" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\ProgressBar.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp8A1FE21942F63A12FE8C262291D2F118" Guid="{10CFD156-62EE-4E2A-AC14-CDE672E214D2}">\r
-                            <File Id="fil8D5351D7FEFC8E0D7B074BCD4BE7E93A" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\qmldir" />\r
-                        </Component>\r
-                        <Component Id="cmp89930EDDF65A84FB647750A3889D3998" Guid="{242DF3DF-4813-4AD4-800B-4A6A7F1E00BA}">\r
-                            <File Id="filE4098FA13958E23AF469CE23DE03ADB7" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\qtquickcontrols2imaginestyleplugin.dll" />\r
-                        </Component>\r
-                        <Component Id="cmp7A7C588B21E8E3817983B40E6C15A8DC" Guid="{9E76F7E0-607B-468F-8E59-B2614AD30602}">\r
-                            <File Id="fil09BC6622E343777DE250D2F47E297ED1" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\RadioButton.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpFAF5864C58442FAA1D5C0A01C83FACE9" Guid="{202BAC2C-95C9-467D-BEB4-1EA7E27E31B6}">\r
-                            <File Id="filB5EFC9A07D2D7FD06A43503D3A79870E" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\RadioDelegate.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp48641EDC547835A4C015C138C9EE12D6" Guid="{EC581895-46A3-4CFE-AC75-5FEB7C9C2ACF}">\r
-                            <File Id="fil977DBB2F4A4B7DFED8BB0612A7037970" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\RangeSlider.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp74D44A9BA04153CCD0A6FA413CA57C4B" Guid="{7166BA46-DB87-4DE6-9C1A-1EB28816178D}">\r
-                            <File Id="fil11ADFCA60637AB6A2C391D9EA6B3C543" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\RoundButton.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp84CD7E65AAF5D3AD9F421445901CF37D" Guid="{CEAB5076-3A76-4680-AC7F-379B2329E9D8}">\r
-                            <File Id="filD9610FFD3B120D5B16F73DC0CBCFFD2E" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\ScrollBar.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpAB0EEB7FDAB89DB33CC1872F2D020B9A" Guid="{21EE7B59-9A55-4660-8849-2BAC9C1651D3}">\r
-                            <File Id="filAE6B882E064E88D269983EB2BF4F7EFF" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\ScrollIndicator.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpEA5C0DF0E7CC7FF0C0A116E8ED0DFD39" Guid="{4B3DB6DB-BDC4-400A-8559-032E988E2B47}">\r
-                            <File Id="fil0EB5F1D6A344D46C999C749965A81DF7" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\Slider.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp48F6BA7469D6C5E8A90EFDC2F1F12A03" Guid="{2F7713CC-29A6-4EC2-AA4B-E2E2970448EE}">\r
-                            <File Id="filE4BBEB9676EAAFD512614F5B243CD4F5" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\SpinBox.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpEACB1D29768FC4AD7754B7A8BB5CEAA4" Guid="{D905B9C9-ADBE-4760-9853-EFD1EC7FBA99}">\r
-                            <File Id="fil2F0169274B3846093E9CE0E0CDA3914D" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\SplitView.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp40DFBE5C4004C66BD421833A0916A402" Guid="{03C419A3-AF46-4CA9-A7C3-8ECFCD57104A}">\r
-                            <File Id="fil8E7130A25B7C8446BC71EC968B0715CF" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\StackView.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpD46A2953ED55E92D9E0462BE4D66B828" Guid="{FFD1F1D1-32CD-4E77-ACA3-580CD5596381}">\r
-                            <File Id="filCECBBB01A4F76F4742FDB628AB2398D7" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\SwipeDelegate.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp9BBFEBDFBD798ED64CF1BFB852E57719" Guid="{45F72B4D-8EB6-4BE2-B31A-C9BF307DD2C8}">\r
-                            <File Id="fil3A23A94C3ED39F75C988138BFBEEA2AA" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\SwipeView.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpD60B739F43AAC84324A66CEFFCDDBDFF" Guid="{E0F387DC-7D7F-432C-8EA9-9ED7F865D6CE}">\r
-                            <File Id="fil4751E171C8A0EFA145946D3D11DED958" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\Switch.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp4AF8FAB5F76A012B1E00956784FB86C0" Guid="{DC4C5366-E401-48CB-9A6E-BE7F33FA39E8}">\r
-                            <File Id="fil0AB6CCC6384F6C6B1A85535F2E435452" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\SwitchDelegate.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpD3F2F08057724AFA25E4EBF3514406A6" Guid="{CB2E0B76-1F9A-4B9B-904E-FD373A79E56C}">\r
-                            <File Id="fil06EEC5BCC6DCFD868503648FEB22C2E2" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\TabBar.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp5D978D2112EB628D93B78BBB779859CA" Guid="{2F43967B-733D-4C8C-A715-175288FDA3FD}">\r
-                            <File Id="fil23FF41EA1352977BF0D165A14A816D7F" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\TabButton.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp6800D8FB1FBB983412E92B80451A1109" Guid="{8109FFCD-D535-4EAD-BA10-8A16D8409AA0}">\r
-                            <File Id="fil9E0F7A4390203BCAA10D42088F80E582" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\TextArea.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp71287354FF8DCC602BB0D3D414E05962" Guid="{71FB3A16-2141-4948-89C3-73AB0B166A7E}">\r
-                            <File Id="fil220878B77DD5BA3F56A6D5C7A5EAD1EF" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\TextField.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp0DF5FCC7255520F9D24CAC298FA859D0" Guid="{ADFEC3CF-FD46-4295-96F6-30FDEBBFAB2A}">\r
-                            <File Id="fil87EA3DC0E8CE7A5D1B6D1980848D1E5C" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\ToolBar.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp3301D802CB29561A15AE015C15DD8543" Guid="{0CEB23DC-F5E8-403F-BFC6-02DAEC745718}">\r
-                            <File Id="fil5D5982165FA435D4FA833ABC64663AF4" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\ToolButton.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp00A4060669FB650FE92DDD9A09A857C0" Guid="{F095E727-9F5F-486D-B30C-406D1CE193FD}">\r
-                            <File Id="filD17CC03AD31A7AC8639971FE0D6C45A4" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\ToolSeparator.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp2D3FB42E796C744949D0480EE5C04892" Guid="{211FA71C-4714-49D9-AFFA-CA730B964213}">\r
-                            <File Id="filAA9B4A8954E641AD01238DA365D35C84" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\ToolTip.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp11B5D7ADB851C95DCFE5C71959222497" Guid="{6B13434F-4A6A-4874-95A6-C796BE931A62}">\r
-                            <File Id="filA16915B6759149D53E63B3A6662327D5" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\Tumbler.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp27BC211BA8D2261D578FC390503266AF" Guid="{30C94813-4571-479E-825B-10EDE44E2077}">\r
-                            <File Id="fil64AED8EAC8A4A27B2290235AC66A8987" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\VerticalHeaderView.qml" />\r
-                        </Component>\r
-                    </Directory>\r
-                    <Directory Id="dir98C439A324E49AE59FF9F7CAFC527818" Name="Material">\r
-                        <Component Id="cmp60AB973400004A31DBB4967567BB3780" Guid="{7D351719-339C-46E2-81CB-905AACB7BEC7}">\r
-                            <File Id="fil64C921AC09AAAAFB16510D0E258259FF" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\ApplicationWindow.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp026D50205EC5D03323420AC1EC10FD89" Guid="{4B779DF7-4593-4A4D-AC2D-38DC4BF88B0B}">\r
-                            <File Id="filDB01E686286D4C2DEE2CFA3FD38FCB40" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\BoxShadow.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpE0BD5867C82F9D8554FECDDC4B6901C7" Guid="{852AD272-C64B-4C36-96B7-8664046E31AA}">\r
-                            <File Id="filE4A4C7B299CDCB670C96418B8EDDB5D8" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\BusyIndicator.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp9F4E4D4D0534820ED7B1592321BACF51" Guid="{A6935D04-49FA-49DF-82EC-BE62493947D5}">\r
-                            <File Id="fil01470810824935FDCE6313DD60621BF3" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\Button.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpCB85EA75A98576ACBC265DECF4F7697F" Guid="{12C6E0D2-3BFA-4BC6-8111-800F3AB9059D}">\r
-                            <File Id="fil1D098E5C4AA1CEC913EAFA6AEC902AB9" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\CheckBox.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpD8B043EAF161B1EED3C0D0E0E8B0717E" Guid="{C800976E-3768-42C1-9BE6-CC3B47528DC8}">\r
-                            <File Id="fil4E240E0B681A24079C96E7EE1B59BB81" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\CheckDelegate.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp006E05B629020152BA06266DCF586F94" Guid="{BCED27FF-F076-445C-A572-6A64493517E1}">\r
-                            <File Id="filB14F8F42CDE72A0055838F894912C34C" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\CheckIndicator.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp6390600916A9D80DEECC6B64C1A5734B" Guid="{A3D8818D-3D8F-4412-A219-82CAD7EA3A78}">\r
-                            <File Id="filAA1D779F78FFD687244475818E1E676D" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\ComboBox.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpA029FDE3C079115EA43947112C138B8C" Guid="{79E6AF29-37B7-4067-89C7-C733024B28CF}">\r
-                            <File Id="filA865175498A738900D1E48E261E9A7A2" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\CursorDelegate.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp050C776C9A28886790788280CCEC3D91" Guid="{E6A61937-0614-478F-A2AE-986106471D6F}">\r
-                            <File Id="fil3A782A789161DA1643DAB87A06031CC9" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\DelayButton.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpBFBC5C595627F87399C1078F9A522E9D" Guid="{B0F3C31E-D1FD-4C88-9603-9DC8E7359565}">\r
-                            <File Id="filAA2365440AA7A931BCC671A9A5ED2CC3" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\Dial.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp2B0856221A67BDF81C2238000B184A17" Guid="{F1026998-7BAD-4314-85CA-4B879412E77F}">\r
-                            <File Id="fil633492C397BA579319833DD3AAD2951E" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\Dialog.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp835605203F95C84205B718EDDDE2091E" Guid="{CC7B5354-0ED3-4872-9481-CD24218471F3}">\r
-                            <File Id="filFEB37EE6FA694858E6397E30CA14D53E" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\DialogButtonBox.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpD37BF99FBDB8EFD807188027A80B8DA3" Guid="{1C7EC37F-ED5A-4AF4-824B-78897568843C}">\r
-                            <File Id="fil9275FC5F45942812B0181C0AD249DA8D" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\Drawer.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp31DA9547A00549AAE408F3DADB011332" Guid="{F1E700ED-73BD-4B51-8D93-13745C69E2DC}">\r
-                            <File Id="filEE7349C912899BD32A59C5B164F24EEB" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\ElevationEffect.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpC869C8092B8F2AAC631C286328891A94" Guid="{FDCDB596-6781-4C12-AD6E-BA12D276162E}">\r
-                            <File Id="filD25404A21A1FB54078D4A8BDD39139B9" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\Frame.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp0305CAFC1FCE93ED480CC829AAD832AF" Guid="{A75B3F5F-8C84-4A5A-9D6C-B2D06223F0D5}">\r
-                            <File Id="filAFE4512E9BFCE1DBEA4D23E504D69326" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\GroupBox.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp5D66D11EA79AD27A1AE641BF9C7CB9F2" Guid="{3CB05274-E6ED-4762-B185-AAD551464977}">\r
-                            <File Id="fil2F55E2A2B3B694DC0B8F749076B915FC" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\HorizontalHeaderView.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpA817DF6EA38B1DF29BCE614C5450544B" Guid="{90C497D8-B166-45CF-A710-1DD2C5BFFA41}">\r
-                            <File Id="fil6DE65D9A2CA853FB7F9873AC257E7BF1" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\ItemDelegate.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpFB26590AF40355E4D807DA7009B7836D" Guid="{F9B22F54-D0AD-471D-B1EB-96E584134B22}">\r
-                            <File Id="fil677DE17071787C3A48E9C6FBAA519F5B" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\Label.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpFA168D7F4EFC52948B17DF862E07E25A" Guid="{C5F7BEEF-1B71-4D45-B859-4CA51121EA22}">\r
-                            <File Id="filEA122269BF1F9F0E5DE2853767AC894F" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\Menu.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp80B129BEE9AED15B717DBBC4E1C91B8D" Guid="{E64751DA-EE5A-4C99-9AD0-24FCDAA4D5CB}">\r
-                            <File Id="fil16F020BBAFC0D9DF9F1A85E68CE222C1" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\MenuBar.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp222BD8179F2D4ECA9CD649598F1665FE" Guid="{B3D9F018-4A95-4520-911A-84862B132195}">\r
-                            <File Id="fil0BBF38951FC82CBE8636981293A2B84A" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\MenuBarItem.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpE9263BDE73548B76D4CF00FA421C6394" Guid="{00E8C1F0-E2DF-43CD-B437-E781866A6E5E}">\r
-                            <File Id="fil12E8E206331572B0EC4AD0A4EE328DC8" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\MenuItem.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp665380529EE1C0ED1DE23E8B26850B19" Guid="{85D9EC2F-DABD-4B96-8B43-107FEC2268E8}">\r
-                            <File Id="fil3BCAD81F81A42FD7A740540891FC5A4B" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\MenuSeparator.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp8DD36362549E19ECEA88E3D1B1ACBB85" Guid="{44592660-851B-47BE-AEFA-34A0EC7F3289}">\r
-                            <File Id="fil615CC9ED7E0F011723181A7F99A61740" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\Page.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpD2EA9700B16C8836D5206D1185C549A0" Guid="{35B14087-6829-4BA2-97DE-4EB8F160A07E}">\r
-                            <File Id="filAD99EB039065C97EF184865D6503E4C1" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\PageIndicator.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp73D2F27C7DDDD4CF7ADAEBF8E6E42EB1" Guid="{2EBCA9CE-E074-4C0E-BDA0-D9CFE4AA041B}">\r
-                            <File Id="filC2DFBF2CB10C8FCB3D1AF32F2A8D57DD" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\Pane.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp7E31792D0EB10DD8AFD37842DFFCA924" Guid="{8A30E34E-392A-406A-8DCA-131248202236}">\r
-                            <File Id="fil1C6676EE896DF9A856E5FE3F9EFAC32C" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\plugins.qmltypes" />\r
-                        </Component>\r
-                        <Component Id="cmpA158D23F2833A86832CC8AB04B51E8D0" Guid="{67B192AC-1AB5-4AD6-BFF4-71902B569AD2}">\r
-                            <File Id="fil70BCFDA53E4BE604641DD96490B3F5EE" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\Popup.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpA62BD30E70FBA79DED1CC038B0404EBC" Guid="{F121CC1F-A122-4191-88EC-836831DD39E9}">\r
-                            <File Id="fil900BC61782FD456E2C7E15DCBA8CAC47" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\ProgressBar.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp78F57F7853BF2F83CA4C27E09AE1E09D" Guid="{5200AA4B-3F84-4020-B572-61028451737C}">\r
-                            <File Id="fil95B30068F5A41AD2EC7F7D95D514FB65" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\qmldir" />\r
-                        </Component>\r
-                        <Component Id="cmpE3E3B7C94E5AFDAF94C8CC78998771DF" Guid="{27D226AC-CC01-4B3C-9CD9-FF83E2BE77C6}">\r
-                            <File Id="fil4C5BC6926C010F47CFB93190824A9A38" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\qtquickcontrols2materialstyleplugin.dll" />\r
-                        </Component>\r
-                        <Component Id="cmpBC000241C708266C86D302A435E66F1C" Guid="{5A4DA7CA-1BF1-4394-A52B-61834B951EC8}">\r
-                            <File Id="filA32CA6F1B47518CE771D5FD69B4BAE80" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\RadioButton.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp3B8600643B709AD9AD113079EDB50C94" Guid="{B6B8BD52-4464-46A3-8A15-2D9DB2F451FA}">\r
-                            <File Id="filFDFEFD51A9E2DF0C75D2E48D4697846D" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\RadioDelegate.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpB755C164EDFD73E1498268DB3DA880E5" Guid="{EEC2C85F-94A4-463C-99D2-0C94DDAB1731}">\r
-                            <File Id="fil438F051667D9B5E1DFEC7C42714E7AC5" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\RadioIndicator.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp1CBDAA564E02886155300F7A8AF049CF" Guid="{52FE64E7-76EA-4E72-8ABC-F72A6912CEDD}">\r
-                            <File Id="filEFF497CDD0E8BF7A1A284FE00DBAC232" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\RangeSlider.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpFAE3866F17FF7039CA9618739C64A1A5" Guid="{1D8BBF80-37C6-4356-981C-E84624F4D130}">\r
-                            <File Id="fil950F62018D52CBBA67D0D037CE077CAF" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\RectangularGlow.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp1BEDE14E6357E29206EDC740AE50650E" Guid="{3832C565-CF30-48A8-9F57-1F1203A9D094}">\r
-                            <File Id="fil7B1D1957A48A219B6EA1E51D1B9D5094" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\RoundButton.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp9848706507698E3D2DE99B0C079EAA41" Guid="{F4159B1A-5628-4239-B834-139C4300EE69}">\r
-                            <File Id="fil7B7E8B589CFA5A08CD0BDCFEBBB37F6E" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\ScrollBar.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpAF75487C0AF2238D5F5AB27EE11B8C29" Guid="{CC851481-1F5C-40DF-B542-CF2331CFBDE2}">\r
-                            <File Id="fil6C7298CEC9325745BABD8553BD2C314E" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\ScrollIndicator.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpF1D853F627859C5B31EEDA150FC58C6E" Guid="{1CCA22C1-4843-4405-84E6-1359524F55C0}">\r
-                            <File Id="filE2D55223B3943A1418684BC06ADC9873" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\Slider.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp24C324DDA2757C24A5D64657F10D1861" Guid="{036A1520-EE7A-49D0-AB3B-6FDA0821BF57}">\r
-                            <File Id="fil456E349BA3AA06452991F85C418012A0" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\SliderHandle.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp24A094BA67CBF70909850C9D82D581FB" Guid="{D516F3DC-29FF-4823-9F3C-2F4DDC120705}">\r
-                            <File Id="filD62846BDB9F588D73844FBE505CDA20B" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\SpinBox.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp855A7D3DC1E9E7D07C156736FB529CB6" Guid="{322DCB01-BEB8-423B-B3CF-9260650724A0}">\r
-                            <File Id="filB7F5B7769F99CFB3EEF067B579C69441" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\SplitView.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpF44D1A83846E86F2D4EFFEDC9DA14661" Guid="{A0B9F909-8889-4C14-BD04-3250095E9F92}">\r
-                            <File Id="fil2E77DB68C32D27DC45D541C2515A7DCB" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\StackView.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp15902266961B205BB7C49E3F21B0A166" Guid="{D1B88C45-21E8-4C9F-96FF-EDE6CBCB383F}">\r
-                            <File Id="fil6E8DF910D77FBD6DC889D4B17EDB8C73" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\SwipeDelegate.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp50966487B32C316D078827563E31DB30" Guid="{F008F394-1A7C-4F15-ADE3-4BAFF638F1AB}">\r
-                            <File Id="fil04105F1624C2D26E2FF0F79EEDB6FB6F" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\SwipeView.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpA1B9598A2B3B9C203B5780C91F7075D7" Guid="{A0B5114E-0A0C-44EF-BE1D-C9CB730A4EAA}">\r
-                            <File Id="fil8BD5245958D87E18B746A319B5EA03EB" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\Switch.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpEF587C49FDE4B67961EDEA007128D3DE" Guid="{FBEE1A46-7848-4E35-9344-6D6B062DD6A5}">\r
-                            <File Id="filE64764B9E04E5C2C205F98B593CE79AD" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\SwitchDelegate.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp793DBF048FBD4343297F1E17756BBF6B" Guid="{8185D55C-9168-4D19-BEAC-34505A4D7106}">\r
-                            <File Id="fil7BF94FDDFE952066A6BF1FC7268E4C41" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\SwitchIndicator.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpD98F61FC599E1BD66057249B28016A0E" Guid="{254D4BDB-E170-408F-B943-DFF5044C0519}">\r
-                            <File Id="fil35E18FE45A8EF14EF0AF02F9E3BB944D" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\TabBar.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp4C6EA3AFEB0324ED3875A46DCCB4EA48" Guid="{48E4E179-8433-4CED-A5C0-9A31EEE63415}">\r
-                            <File Id="filAA08F0B10CF6925991C628327DC46BB4" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\TabButton.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpA7F194C9D57330A5F72C74CD96507E48" Guid="{576229B2-14D1-4DAC-8482-31499B873B5E}">\r
-                            <File Id="fil5669F09C2045004CC25FD46B2565629E" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\TextArea.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp337BDFFE231941F8A3E54BF4AC644B58" Guid="{F791A188-A943-454F-AEE1-FD797A3D801F}">\r
-                            <File Id="filE58155B28FABF154CD98B2C02EC12126" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\TextField.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp0255A607D5334E94D517A2FEDDB2105D" Guid="{10D77816-5456-4FF9-B162-3DF69F8E56EA}">\r
-                            <File Id="fil9261EC814FA4325E3177494A396D8066" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\ToolBar.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpBDEB6272B93E7E2FD1982012B5EF3D02" Guid="{7028EB68-4086-4009-BAD5-6D19E4D5EC9E}">\r
-                            <File Id="fil36BCCA29A63BD763D066F4FD320B6D05" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\ToolButton.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp6ACA30972AFD6D78448054BA4368AEE7" Guid="{8D2AD71A-31A2-4ED5-B3BD-43E761CB7142}">\r
-                            <File Id="fil21C810954616BBB037440CDD95167F6B" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\ToolSeparator.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp0014F4AC9A48147F8EAB3F17B2C144E0" Guid="{DE7EB59D-A01E-453B-B333-887D374C00EB}">\r
-                            <File Id="filE2CD377318429F05238B017CA75F21B1" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\ToolTip.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpB1ABCFD82FD1858E7657C34A8104404C" Guid="{8F3123BF-9523-47FF-ABE9-2B9B3F5D0181}">\r
-                            <File Id="fil98EBD20C2BD6799247161723C27818F9" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\Tumbler.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp6A432A70B05CC4AC38DA38F776B97812" Guid="{4E8C826F-1AAA-4063-9CAE-38614D103CD6}">\r
-                            <File Id="filB345996D2977939507887C6FB257738B" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\VerticalHeaderView.qml" />\r
-                        </Component>\r
-                    </Directory>\r
-                    <Directory Id="dir8677A4B366B4AB433BB4754EEBBBE2EA" Name="Universal">\r
-                        <Component Id="cmp134038F5FD44A08B4DA0CFD8921DFB42" Guid="{1B4AC1E8-E146-4872-A0B9-90AAF7A63406}">\r
-                            <File Id="filEE54942B1FE1F7E0FE0DA9372D63E2E9" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\ApplicationWindow.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp34FB0E37566676B69EEEB98F670D23C4" Guid="{09BBA204-8172-4676-B116-B7249D1FB914}">\r
-                            <File Id="filF048A4DBF3EE1F42AB0045720B3A1275" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\BusyIndicator.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp166A7C72CA2D82533EBF24FB87FE4BC5" Guid="{5D75BCE9-3E98-4292-BC43-C72DA6C7B3FD}">\r
-                            <File Id="fil9FC3621AAA61D06486256ACFCEDEFA58" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\Button.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp11D7406D35C2641BC20018F1FA8ADC45" Guid="{50AC31B7-B2E4-492F-97B4-7125DFA06DCE}">\r
-                            <File Id="fil4CC941157F98FD9DCC1CD03DB1383D81" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\CheckBox.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp6CA80FCA9CDD4C55F2582BF139AA4B1B" Guid="{C9EF0708-5E19-48B7-BD71-120ACD0FA185}">\r
-                            <File Id="filAA2312432C29EB3BE6C44D5CB9E552C0" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\CheckDelegate.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpDFBFE88B708CE2CA5904473FE0CDB861" Guid="{0135FF85-D1FF-4305-9E95-CDFA4427F376}">\r
-                            <File Id="fil0D3BF90DE4E4AC4108503B2DB286E7F6" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\CheckIndicator.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp8D47A9ABEDE81A100C924FF154224483" Guid="{AD996097-10A0-48E2-AE3F-2A31A8B99BC7}">\r
-                            <File Id="fil3445161ADB5A36CCA4D8DEE8401F79D9" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\ComboBox.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp0071C7F9D6D4B5DC3218D487DABFC218" Guid="{85A7872B-F79B-43E1-97CC-B30383C2846A}">\r
-                            <File Id="fil1B3A6163FC6D639F2BD22118F8DA406F" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\DelayButton.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp54B17CC907A926962454D9FDEA99E369" Guid="{4492FD9C-0227-4592-931B-1CE255A56335}">\r
-                            <File Id="fil1E9230BCC1B46DE57D1B254EB036939B" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\Dial.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpDF321BF03AC6D81897FFD4B64045DE88" Guid="{76EBA34B-E864-4B0F-9382-2786ABA98828}">\r
-                            <File Id="fil64F4012F0ABC224742C0420695795D29" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\Dialog.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpEBBBCD3B40ADAF40F5F7BBDAD1628BAB" Guid="{3CEB3BEF-B036-491B-8156-71DFCF99AEEA}">\r
-                            <File Id="fil1AD8E7E159B82A10473C11ACD1622A84" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\DialogButtonBox.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp4781FFA691E6B55670D19CCA10E68D4A" Guid="{D15EEBBD-53D6-4922-AA40-39A8594785FA}">\r
-                            <File Id="fil4E217CB94A8B78C9D074F1BAEBB7A41C" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\Drawer.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp9D75099F72BC1EA408C69BAE1036713A" Guid="{5F48BA1E-8F54-465F-8738-CD28295DD983}">\r
-                            <File Id="fil9D815A989044908A0CB2481100231444" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\Frame.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpD507403E3878D92F029FB454CD349781" Guid="{A5180010-062C-494E-82C5-2A7804D7118A}">\r
-                            <File Id="filFA88E95F2ACC7A5522DCF4C107B83B7A" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\GroupBox.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpDB50F59C1041F3C78FB11F782D92A59C" Guid="{DC08DC21-9312-4C4D-BF3A-C43E1A675FB1}">\r
-                            <File Id="fil911A2600E1379E8C5A85199969DB74CF" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\HorizontalHeaderView.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp9ACED442699FD1B84B592FD9A678FF1F" Guid="{9D19D01E-C44E-4FC6-AAD5-6709B0298D2B}">\r
-                            <File Id="filB5F9B6495241535B1200DA731AF5A5F9" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\ItemDelegate.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpC959D8B9C731BB4CB168C53232754164" Guid="{AE31E225-63D5-46EC-BFF9-01D536DE1235}">\r
-                            <File Id="filE924AE836BA5C65F25B0FF78B6A2425C" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\Label.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpF036082D9E70D219B49993BF2078FC9B" Guid="{740E5BA2-9846-417D-81D1-C4F6AB3049A7}">\r
-                            <File Id="fil0FE051A3F7254F4C87AB2787BB8B9195" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\Menu.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp610C62A18FB0CBD4A23E6420089991A6" Guid="{D53BB533-7392-4096-8630-CA6D234AC952}">\r
-                            <File Id="fil0078083DF303E6033ED65F84CCCDA8E0" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\MenuBar.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpF0A89D556198AEC41AA931DE764420C9" Guid="{2478D487-C1B4-498E-A15E-8AFC8EBDD54F}">\r
-                            <File Id="fil141BCF473057F39197B9662FCF606CC8" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\MenuBarItem.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp1FB4C766D7DFBDC29927513C52FF444C" Guid="{07AC968A-CAD2-4435-B348-325CF309AF6D}">\r
-                            <File Id="fil1E9D45ADE39F5FB611AB5AAB33294D3D" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\MenuItem.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpAB3EF735C77E4B6501B74C3070F17E8D" Guid="{9C5FC5C2-834C-48C0-980E-2D5179A53773}">\r
-                            <File Id="filD6ADE8C35F973E505931BD4F09EF0A78" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\MenuSeparator.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpEF370FB1E0BE2F7C44ACA77A0B9A380F" Guid="{FEE2D791-0609-42CC-9183-81B0FCFB3AAF}">\r
-                            <File Id="filBEEE5C63314DE5A1C43E6865925031F3" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\Page.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpD53F72AC172BCE3F947CB966768B983E" Guid="{55F0B5A9-6568-4369-BEC8-9CE9FAB4C886}">\r
-                            <File Id="filD257FB636047A1ABDD8322D56BF83DED" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\PageIndicator.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpB58DE0208168537FC63A28BA40094D3E" Guid="{DDA681AA-E7A3-4935-B6F3-A26C591C0F3D}">\r
-                            <File Id="filF940749E1F1250D0E5E454F677E4811A" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\Pane.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp1E24968394165967A4C933056CC97E8B" Guid="{BCC5C83A-1795-4E9A-9F3B-9EF43B56B4E2}">\r
-                            <File Id="filBBCB3725136BF248504E6130E398CC37" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\plugins.qmltypes" />\r
-                        </Component>\r
-                        <Component Id="cmpA780D5122E140F762FC4C3A0D02E7179" Guid="{8898C5E7-1BF7-40C3-A46D-F097D6A2110A}">\r
-                            <File Id="fil9FC53D09D020635EA1B1AC7DAB106FEB" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\Popup.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpE61937584021054E6A0A34AFBB281937" Guid="{6937E766-8BD5-415D-B76A-F464FEF6E71B}">\r
-                            <File Id="filA8BB46F9B5116CF916C7B675148EFC4E" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\ProgressBar.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpB2D199D19AF00559ED6198A864F4874E" Guid="{DED65452-7CC9-485D-807E-43904CBA7FEA}">\r
-                            <File Id="filFB7A404E13EF7802DE1DDCBEBDD42A2E" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\qmldir" />\r
-                        </Component>\r
-                        <Component Id="cmpA29A335EDA4A585067146163B51D3643" Guid="{86C75BC9-682C-49B2-A48D-B18AA904AAF9}">\r
-                            <File Id="fil23BEBDFB38598BCC408151AC6B584604" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\qtquickcontrols2universalstyleplugin.dll" />\r
-                        </Component>\r
-                        <Component Id="cmp4F8C12B67EDC519F78AA4840C85494D1" Guid="{4F5352F8-AA69-45F8-B461-8FB3CCEDA086}">\r
-                            <File Id="fil2FF1D14D6D70E386EAEAFC412E469DBA" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\RadioButton.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp30F05444D83E8DAFE75CAAB16F50E3AD" Guid="{A4922AB5-4A0B-4CDE-A60E-6B88921844AB}">\r
-                            <File Id="fil5904A6F1649189A762EE1680E07001FA" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\RadioDelegate.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp7573B19F4A56CF2B1D359A87D25A39B4" Guid="{2B71C5A1-2DEA-42CF-B0B5-8856B0B47752}">\r
-                            <File Id="fil0FDD8C1A5AF94ACD8822A21F4762C124" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\RadioIndicator.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp0B698F624CC72E780FB1FCCBE6340AAD" Guid="{F91E47DD-8607-4DBE-938B-D67E14187DD0}">\r
-                            <File Id="fil8EB7C1B7193D2DDA9E532A1B16502A73" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\RangeSlider.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp898F4E8E360375D6D8FE6F606F899441" Guid="{EF555FB1-D8BD-416A-B581-1DCCA58FAABD}">\r
-                            <File Id="fil8455667A31A0B1322CE00154CCF24A08" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\RoundButton.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp246EDCD16E1C5BC88F69EFDF059C62B6" Guid="{363B06E4-66EB-437D-9454-16D14379D4A1}">\r
-                            <File Id="fil8161E8E97F7F2526D40A4E9426ED9CAC" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\ScrollBar.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpB92657C823E717DB4819343891A10BB1" Guid="{3860F820-ABC8-4AA4-A7F9-8B2D10A85511}">\r
-                            <File Id="filF04EF2D65266F90D588563833EE46211" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\ScrollIndicator.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpF4DE6E8F14E6A8E27857D8BC89787BAF" Guid="{DE05151F-0839-4D83-9AB5-7D089240403F}">\r
-                            <File Id="fil4987E569AB3CD95456311C90D8DB245D" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\Slider.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpF49A6AC910C2EB6FF0917041E1F96E8A" Guid="{74BB77A1-0A70-4175-84CD-C4266DDF3112}">\r
-                            <File Id="filE8E232330635AC59E8BE3A0694C54370" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\SpinBox.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp1DF54EA158668A0E21013A820D2E7F51" Guid="{56BF8F73-ECAC-4474-BAB8-CDC47BC0A267}">\r
-                            <File Id="filADDD2AFDAE9A339C8A92C247781CEF40" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\SplitView.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpDFA6653A3AA375D59BE8318C927D5DC4" Guid="{B734122F-CD08-4401-B7BA-5E1D5858EE5A}">\r
-                            <File Id="filAC7F2D3C36D8E02F2FAF7A5578199576" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\StackView.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp20B23CB93B2BC9BF69A97D0994698310" Guid="{E5A49C0D-CAA9-4E51-A610-C46AF31FC864}">\r
-                            <File Id="fil5DFC169D4B5F10EDE586C1C5760B6573" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\SwipeDelegate.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp735A0D9DB2D96E80F3352A232FCAB063" Guid="{E77595FB-FEE6-48C0-A749-862C8CEEC8A0}">\r
-                            <File Id="fil0867DDC68FB2A26C4732AA7C6CF0EF5F" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\Switch.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpDB346892873A00B4024D345FE4C725BA" Guid="{43E075FC-DDDD-43B2-9308-F8174880A344}">\r
-                            <File Id="fil83DE4DE33E72D0F08A1C59200E82B424" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\SwitchDelegate.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp32E0784BA82190D0BD64E488EF265F55" Guid="{EEB2DEA6-7C4F-4CCB-9474-1E5F83FBEEA2}">\r
-                            <File Id="fil26EA260B1D46E42906FBF1BB1CE3B145" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\SwitchIndicator.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp4215773DF3A614E82EF59C456A6EFA97" Guid="{EBFB2786-8DCA-4B6B-870D-4FC90BD82E23}">\r
-                            <File Id="filA8F80D2C80356344CDCD864FCA88D48E" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\TabBar.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp72E1D5A67C90BF0538F8015E486898B0" Guid="{BA27BD86-15CA-47C1-8D6A-7998EDE35547}">\r
-                            <File Id="filDF1DA27F8944A4FC08060F351077F7E8" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\TabButton.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp02A0C8EDA0860CBAE68AE51FAE93EBB1" Guid="{3840413B-B9C9-4DD5-8AD5-D03695BC18C2}">\r
-                            <File Id="fil039EFA8518F7A118F885C10615BAE8BA" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\TextArea.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpC54A96A34D6E8EEB099E5754699E907D" Guid="{14E4461E-D756-4E2D-A569-2AB17F5BD204}">\r
-                            <File Id="filF7DE49C3E03DBE7D7DAEF87FB2CF7EEC" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\TextField.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp2564368EA9BDCD6CBD86BD0B47845CE8" Guid="{D705AE33-5B56-4582-B520-1967673B5A01}">\r
-                            <File Id="fil7BB9016C1239CAC2A691B7E80E155716" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\ToolBar.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp4C037A4C1F7E6103342D757CE84FB3B0" Guid="{37196B4C-A886-40EC-B577-360811B5BBA2}">\r
-                            <File Id="filE5A4593700B13AAF5E707064E4DCBD71" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\ToolButton.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp2782B54139300C706E3DF5D779A32A4E" Guid="{2096E3B3-6475-456F-8104-7044EE32C396}">\r
-                            <File Id="fil043B63F16633A9DEDFD28882B52AD567" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\ToolSeparator.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp5ACAEC60F8EC208DC179E4790FE9090B" Guid="{29854CE8-EAC3-4341-BBF3-6A65CB8B1E5D}">\r
-                            <File Id="fil893C89D4CD3775E4FDF5EBC7DC2F2B64" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\ToolTip.qml" />\r
-                        </Component>\r
-                        <Component Id="cmpE672C34FD6F4C63A61786B77A67D0BCA" Guid="{317CDB5D-8CB9-4E8A-B272-7EF4044501C0}">\r
-                            <File Id="fil7F438968C07C1FD861B594449C04FB30" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\Tumbler.qml" />\r
-                        </Component>\r
-                        <Component Id="cmp05B02931195D1A41BF37EA6B3C26F10B" Guid="{D6329835-9FFD-46EB-8BC2-7E253275209A}">\r
-                            <File Id="fil147142765918ADD0B8E6F1C083AC829F" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\VerticalHeaderView.qml" />\r
-                        </Component>\r
-                    </Directory>\r
-                </Directory>\r
-                <Directory Id="dir05FA62F47E8C43CCE7A3869D5CB62C6D" Name="Templates.2">\r
-                    <Component Id="cmp2214585F3508B759DB1308CC180757FB" Guid="{B480F13B-F2EF-46A5-839E-E43E5F688DD2}">\r
-                        <File Id="filAB37F3DFF1567C85745F8546C34DB8E4" KeyPath="yes" Source="SourceDir\QtQuick\Templates.2\plugins.qmltypes" />\r
-                    </Component>\r
-                    <Component Id="cmpA1474840D69304F1AD34009D58C2B5DA" Guid="{38B68372-B6BE-4B9D-9A2D-757FEDFA3F74}">\r
-                        <File Id="fil58857EAF612484AD6F1B7594F5496CA3" KeyPath="yes" Source="SourceDir\QtQuick\Templates.2\qmldir" />\r
-                    </Component>\r
-                    <Component Id="cmp40B743148ECA12B22598A157C6147B6E" Guid="{9450D1E0-9549-4179-9BE3-303787AE3E0D}">\r
-                        <File Id="fil3A4423AFDA5CD5B5725F48FA3C3D437A" KeyPath="yes" Source="SourceDir\QtQuick\Templates.2\qtquicktemplates2plugin.dll" />\r
-                    </Component>\r
-                </Directory>\r
-                <Directory Id="dirA3E21ED94D77D9B21B5BA9468AEE4AB0" Name="Window.2">\r
-                    <Component Id="cmp104D8EDE2CF475C5EA4FE8169E74E299" Guid="{C251DAA6-2564-4B1B-AF03-E008227A46D3}">\r
-                        <File Id="fil0DA074889234E8261DE830FC8ADD1A9B" KeyPath="yes" Source="SourceDir\QtQuick\Window.2\plugins.qmltypes" />\r
-                    </Component>\r
-                    <Component Id="cmpDC8A03C1B37F2CFE043AED01502A741C" Guid="{D5570982-F17E-42B8-A5C7-BDE01B78A561}">\r
-                        <File Id="fil06DE355E21B919381A7CB928407CB593" KeyPath="yes" Source="SourceDir\QtQuick\Window.2\qmldir" />\r
-                    </Component>\r
-                    <Component Id="cmp87D7B0E5B5E51059D964EDCED844F4AD" Guid="{45586400-217C-4389-AE8E-89EA2A8FF29C}">\r
-                        <File Id="filAFCC613AFB897A177641333EC6418959" KeyPath="yes" Source="SourceDir\QtQuick\Window.2\windowplugin.dll" />\r
-                    </Component>\r
-                </Directory>\r
-            </Directory>\r
-            <Directory Id="dir00FAA9E14E20D0621E9EF928F4E50AD3" Name="QtQuick.2">\r
-                <Component Id="cmp80E63D0ACF59FD23F5B051FE5BD003DF" Guid="{B7FA0594-A58C-4959-B5A0-F550B3224D62}">\r
-                    <File Id="fil71AA2CD6AFACEC9523574269EB6E2F80" KeyPath="yes" Source="SourceDir\QtQuick.2\plugins.qmltypes" />\r
-                </Component>\r
-                <Component Id="cmp65C97B5A564AB844DB2F02FFA5B163A4" Guid="{9EA96BC5-BDB5-449F-A6F7-BB9AD9A146F0}">\r
-                    <File Id="fil5B9ECA215F124EDEB5921629049ECBD9" KeyPath="yes" Source="SourceDir\QtQuick.2\qmldir" />\r
-                </Component>\r
-                <Component Id="cmp4A028D7062688CA7458F90092B47AAAC" Guid="{AB143E12-669C-4FC2-A85D-4A67112D8041}">\r
-                    <File Id="fil4ED5B7A970D8897099777F501E3BDC88" KeyPath="yes" Source="SourceDir\QtQuick.2\qtquick2plugin.dll" />\r
-                </Component>\r
-            </Directory>\r
-<?endif?>\r
-<?endif?>\r
-        </DirectoryRef>\r
-    </Fragment>\r
-    <Fragment>\r
-        <ComponentGroup Id="jacktrip">\r
-            <ComponentRef Id="cmpE6579DC78E167F46AB23E0BE3EAA58CE" />\r
-            <ComponentRef Id="cmp52FE9CFF214054652C281C37201305EC" />\r
-            <ComponentRef Id="cmpA07F2DC814BECD1E5F3FC36DC47718E5" />\r
-            <ComponentRef Id="cmpBF7FA1E27DF091A3CCFA1CD7B7CD94BA" />\r
-            <ComponentRef Id="cmpF915A8D749EFFA98CD8DD582A7C82614" />\r
-<?ifdef rtaudio?>\r
-            <ComponentRef Id="cmpD683260746BC875AE3BDED667AA8F954" />\r
-<?endif?>\r
-<?ifdef dynamic?>\r
-            <ComponentRef Id="cmpF83098612A5B11C5398DBA3894ED590A" />\r
-<?ifdef qt5?>\r
-            <ComponentRef Id="cmp5DDCF0CE56D406D1B8B1F93E988AED6B" />\r
-            <ComponentRef Id="cmpB0CFEB9FF932521D6A75B6BE6F8D644F" />\r
-<?endif?>\r
-            <ComponentRef Id="cmpB60B52D038613C3F20F84A0E1C83D4D4" />\r
-            <ComponentRef Id="cmpDEFDDDBEBEA7255D07EE4400F2CDA2FF" />\r
-            <ComponentRef Id="cmp809509D62BD9EA848EF243F03D83758F" />\r
-            <ComponentRef Id="cmp57DDA83E372188A5B760684B961DE685" />\r
-            <ComponentRef Id="cmpF76D0CD1138260DB167395B86D0011CF" />\r
-            <ComponentRef Id="cmpEA16414E09FD1D5068139520966B49EB" />\r
-<?ifdef qt5?>\r
-            <ComponentRef Id="cmp01229088A6F1DEA1BE391E6E7CC3D3EE" />\r
-            <ComponentRef Id="cmp1658AEC83DA2CDD0D93D5BAAABBEAD46" />\r
-            <ComponentRef Id="cmp2DE804C107C75B0C71470A93647D09B5" />\r
-            <ComponentRef Id="cmp82912105030EA0D2D7550CF8AC5677F6" />\r
-            <ComponentRef Id="cmp975651B71289ACA70B3EB154C08A27F0" />\r
-            <ComponentRef Id="cmp3CE5AF7CB2F8B2689D2096769883BC56" />\r
-<?endif?>\r
-<?ifdef qt6?>\r
-            <ComponentRef Id="cmp9147A534CD8EDAB4BA7FDEAEEFE9C511" />\r
-            <ComponentRef Id="cmp52AB384603E096D6B6A1E1FC66B089D4" />\r
-            <ComponentRef Id="cmp0E8605D5C55843FCDFE809B5EE8DFDFA" />\r
-            <ComponentRef Id="cmp5D904885C8104A4049616BBAA95F34A3" />\r
-            <ComponentRef Id="cmpCAE694BB34A9EC66AEA9D10D697F1EAA" />\r
-<?endif?>\r
-            <ComponentRef Id="cmp1AAFF160C8E04AD945D74CA884667ABC" />\r
-            <ComponentRef Id="cmp02252B1C1976959CFEE7FA11B6E36C4C" />\r
-            <ComponentRef Id="cmp62CBFF830BF239A4E376FAE29937AFAB" />\r
-            <ComponentRef Id="cmpD9DF9496C7453F1AA6C20CD4A1C94ED7" />\r
-            <ComponentRef Id="cmp0E8940C551429DC79C890130F6D10B6D" />\r
-            <ComponentRef Id="cmpC6FCE143A8E5EAFEA6119E94128459C6" />\r
-            <ComponentRef Id="cmpFCC4763BEDEA49F63E8D8E3A18E86329" />\r
-            <ComponentRef Id="cmpF278C1175D7FB53021C0126B8F155FC2" />\r
-            <ComponentRef Id="cmp608F60C2BD04F2C37AB51C3211ABF615" />\r
-            <ComponentRef Id="cmpE6323C810986405043F219CECD6B6C10" />\r
-            <ComponentRef Id="cmp9132FD413972F732967B8BC075BC2781" />\r
-            <ComponentRef Id="cmp52840D209B2AC1DC45A29A6BA5A75FF8" />\r
-<?ifdef qt6?>\r
-            <ComponentRef Id="cmp83139013FA2C9A99CF2772FD54090FAA" />\r
-            <ComponentRef Id="cmp6E4460E3D6226693FEA5DBBF500895C7" />\r
-            <ComponentRef Id="cmp53306F84AD30BD49CF0C1A68007F9531" />\r
-<?endif?>\r
-            <ComponentRef Id="cmpBCDC6995284E75B9A24E4646E6117744" />\r
-            <ComponentRef Id="cmp3B3D2ADEE08668758CB9FDB1D243AA11" />\r
-            <ComponentRef Id="cmpDAE87E8CC29C30A200A5D17D81DF1DE7" />\r
-            <ComponentRef Id="cmp719D62CCB63A2C08F22335557CBAB533" />\r
-            <ComponentRef Id="cmp8BDD412901C153FC8A2BF7335835AC78" />\r
-            <ComponentRef Id="cmp56040A1BBEBE8A547F7D6672326FF4EE" />\r
-            <ComponentRef Id="cmp42D4B2D8A4C91F9ED463DA42F3895E27" />\r
-            <ComponentRef Id="cmpB92DA15530102518B1B98DBF1DAC135D" />\r
-            <ComponentRef Id="cmp7210C3ACD533D6A934BD5977AB16AF71" />\r
-            <ComponentRef Id="cmp0643210AD0A541FA765CE55469F22FBB" />\r
-            <ComponentRef Id="cmpBB98B3096730FFFE87E708A6291ED47C" />\r
-            <ComponentRef Id="cmp3666ECDFA614EA095AF7310AECCD44B7" />\r
-            <ComponentRef Id="cmpC4FA2671255E7459BD46070D88B76FF1" />\r
-            <ComponentRef Id="cmp8D644C0D5E80E34EBCB6FC3643C879AF" />\r
-            <ComponentRef Id="cmpA8DDE2BD9E1AB00C9631DE447CBAA11D" />\r
-            <ComponentRef Id="cmp23B3657EE14B0ED7D00B271AFF81187C" />\r
-            <ComponentRef Id="cmp2D105F39980CF93A40405E25755346BB" />\r
-            <ComponentRef Id="cmpC3CB042567F53DD612D309E9CA2E10A6" />\r
-            <ComponentRef Id="cmp43D57F897A909679308EFC03849D7E2E" />\r
-            <ComponentRef Id="cmpF5EA7E37332E95C614BD2D7EAA933E5D" />\r
-            <ComponentRef Id="cmp8DAF13208A863A841AEE7A46E79FB87C" />\r
-            <ComponentRef Id="cmpA214B663A372012F5DCBA6A5CDAE421D" />\r
-            <ComponentRef Id="cmpFDB70117D8E46EB188997470A3793B5D" />\r
-<?ifdef qt6?>\r
-            <ComponentRef Id="cmpF0099CF54A0669D87CC17418C3911422" />\r
-            <ComponentRef Id="cmpE89BF9966FD9EC39B16BC14B5586E0FA" />\r
-            <ComponentRef Id="cmp7F6AC80CE7E39EEDC9A962EAFADD113B" />\r
-            <ComponentRef Id="cmp9EB4F508A604A4AD35BC558C456111DD" />\r
-            <ComponentRef Id="cmp09D69EFDCF1B331A37D028C962FE6147" />\r
-            <ComponentRef Id="cmpA23046CBC7F8B7E62AF4843CBB72DEC9" />\r
-<?endif?>\r
-<?ifdef vs?>\r
-            <ComponentRef Id="cmp26187F331810660B2B23AB9501BE69AF" />\r
-            <ComponentRef Id="cmp10C81658E3C8F63B19EF9D1C26B58363" />\r
-            <ComponentRef Id="cmp5B4749C387D33EAF993DDC89F9DBC102" />\r
-            <ComponentRef Id="cmp7F83581B3F7835094D371708381AD391" />\r
-            <ComponentRef Id="cmp33B78CE22F0CBCA4F41C5037A792210C" />\r
-            <ComponentRef Id="cmpB5571DB4C3153CA848ED1238DBCD4281" />\r
-            <ComponentRef Id="cmp4C1BE3EACCCE195C8B88434D5DF77DE5" />\r
-            <ComponentRef Id="cmpE4BE73E4CCE2EE3CCA22CAE0C82CD298" />\r
-            <ComponentRef Id="cmpFEC2C3E75BBEACDB1A6F4AFB808CD58E" />\r
-            <ComponentRef Id="cmp8B3D94901F4D369CD276B339B8B00F70" />\r
-            <ComponentRef Id="cmp0B6C2EAABA7EAA14C510F4C9D68A3F40" />\r
-            <ComponentRef Id="cmpE75E4FEE65DCA51D044DF4B2AEDCD828" />\r
-            <ComponentRef Id="cmpA8BBE7010AD624EFBA7166925167402F" />\r
-            <ComponentRef Id="cmp00B1DB73ED33EC1DD6658036D3901CB7" />\r
-            <ComponentRef Id="cmp9F481CFBAECB8853AF07C1D5060B77D7" />\r
-            <ComponentRef Id="cmp93816105EAC51740B98AF9C1041D28D1" />\r
-            <ComponentRef Id="cmp5ED50822278A4A846E891B41C07F7B6A" />\r
-            <ComponentRef Id="cmpACA0A17DA54991A194B823CE40D06263" />\r
-            <ComponentRef Id="cmp3D2C980EC975DBBCBD13A9178DD1A03B" />\r
-            <ComponentRef Id="cmp356189FC3558D542AA8AD79067A42632" />\r
-            <ComponentRef Id="cmpABC392DBCC9A5A469B77C129899873EE" />\r
-            <ComponentRef Id="cmp740F889D5799A2FF0F93A24058617EB6" />\r
-            <ComponentRef Id="cmp2799826256C0E84847E94E11C7878018" />\r
-            <ComponentRef Id="cmp659424F663F96AE55FBA532B4CBB2EC8" />\r
-            <ComponentRef Id="cmpDF6A99C1FA6B3943D690F21136BA7F9F" />\r
-            <ComponentRef Id="cmp81B459304782BB6AC65E381BA13726B0" />\r
-            <ComponentRef Id="cmp3CC0196A91D9C805D66BFFF4EA38EBE7" />\r
-            <ComponentRef Id="cmp67E65C02C04C3567636F47EFAA888D12" />\r
-            <ComponentRef Id="cmpADC8E894229BBB48BBC40D4F033FD791" />\r
-            <ComponentRef Id="cmp0D2D3E713559661141BC5DD9F687F29D" />\r
-            <ComponentRef Id="cmp3C102DD93EC83164DA87E7820364DDAC" />\r
-            <ComponentRef Id="cmp4B666BB592E998D07B55C82CF76F5B1C" />\r
-            <ComponentRef Id="cmp4C99F5024E504FEAB151291478878934" />\r
-            <ComponentRef Id="cmp84BC34E1E20AAE86B427C9CD3129C4FA" />\r
-            <ComponentRef Id="cmpCBA14389AA89D290E481E0F2625854CE" />\r
-            <ComponentRef Id="cmp6FFF646F52B60CE854DA0A1B8C47EC55" />\r
-            <ComponentRef Id="cmp5E04D231070BE2CE70B2672E56C6B9DE" />\r
-            <ComponentRef Id="cmp8C5CA067EC2AB41E7CAFE253F2EC86A8" />\r
-            <ComponentRef Id="cmp8441DAF2F2BC211F1F6FD10DCE7B51FA" />\r
-            <ComponentRef Id="cmpC3A1EC5089BA6B0798970A12E614BBDB" />\r
-            <ComponentRef Id="cmp854B1173EACBB218B6AF849A7E8A56D7" />\r
-            <ComponentRef Id="cmp7D04EA58DD5BF794EB2D3A75609DEFF9" />\r
-            <ComponentRef Id="cmp688C34D162C3029BDE9A236F29106F11" />\r
-            <ComponentRef Id="cmp7F9ABA6F59701379E7E54AB2EF4B01EA" />\r
-            <ComponentRef Id="cmp46E93DFCA823AE21F1FD874F13C0A6CA" />\r
-            <ComponentRef Id="cmp2F1F9DE69090BFD2946A7621F2DA410E" />\r
-            <ComponentRef Id="cmpE650E3AF80C0073C2B52BC369D45B79C" />\r
-            <ComponentRef Id="cmpB62C6E9BF562754AAC86BB52A361231D" />\r
-            <ComponentRef Id="cmpE98977BCFBF844FDBCB5325BFC639084" />\r
-            <ComponentRef Id="cmpA11A92D9D7D10A9221DD05699B7B1B8C" />\r
-            <ComponentRef Id="cmpE9BBCB95E45E4F80549E5C111FA7A855" />\r
-            <ComponentRef Id="cmpA9B83EF4B33682EFEA153198F1C44255" />\r
-            <ComponentRef Id="cmp8960218D3E3E2D16BFEF14C48404B5D1" />\r
-            <ComponentRef Id="cmp4951B8E17EA0B3A6B265E2C0E893A50A" />\r
-            <ComponentRef Id="cmp48FE69666F780EE04FC507C3AF73AB4C" />\r
-            <ComponentRef Id="cmp2BD742974C0791D6BD1AE4F7E3646D99" />\r
-            <ComponentRef Id="cmp36A30368477DCD705088EDB144FAA0D0" />\r
-            <ComponentRef Id="cmpF8459C652CF5C20ED87F853C1ABE1752" />\r
-            <ComponentRef Id="cmpA1791452BCB3728CD63E88FCBF971D62" />\r
-            <ComponentRef Id="cmpCA0AF56CA87A52053FE61B6ECD156E35" />\r
-            <ComponentRef Id="cmp0F8FC23D5E3669050C7A79355797347B" />\r
-            <ComponentRef Id="cmpBC9523950CF52702CB1A519123F9E519" />\r
-            <ComponentRef Id="cmpE2FE4AD9E8750F9DCD6AD8C03FFD973F" />\r
-            <ComponentRef Id="cmp302F033AE251BD62E648805EFB1BB536" />\r
-            <ComponentRef Id="cmpF3F704D8F2236A0A62B980FA41D3CB5E" />\r
-            <ComponentRef Id="cmp00AC1A2BE52EA04246F38C5F0B22A9D0" />\r
-            <ComponentRef Id="cmp0A8C093EE03780F9F7823442F10A6C82" />\r
-            <ComponentRef Id="cmp44B6838E73CD47A3F0061ECCE2EB48D0" />\r
-            <ComponentRef Id="cmp57A7D8DDBA7FB780E38A3CF0C3DE50F6" />\r
-            <ComponentRef Id="cmp74FB488898EE30854670C634A6CA57CB" />\r
-            <ComponentRef Id="cmpFF41000DFB8364F307DEC40444A8ED49" />\r
-            <ComponentRef Id="cmpF79607E800F2F8BD75203694AEAEF46A" />\r
-            <ComponentRef Id="cmp1E5895F7DD6F9A8239FBB2DD8A187651" />\r
-            <ComponentRef Id="cmpBAB6384A9280EFB631A74D56E4849879" />\r
-            <ComponentRef Id="cmpE8348F57B7F7ED3E102061755EA59678" />\r
-            <ComponentRef Id="cmp789571C85C4C76BD48E9A471271B5128" />\r
-            <ComponentRef Id="cmp07E1D3988D9259288197F71CC0B0559A" />\r
-            <ComponentRef Id="cmp91EB04F6C5AFAF292A0BFCE3570DCBFE" />\r
-            <ComponentRef Id="cmpDA0DD041411FA5AC356FAD21EBB0BFCC" />\r
-            <ComponentRef Id="cmpC0C294F18C416B2F09D563A7D2F171C0" />\r
-            <ComponentRef Id="cmpBBC07C7AA9E1D56538F0EE5BC883A106" />\r
-            <ComponentRef Id="cmpA2AD6F575E441894DDF6301F935EF3E7" />\r
-            <ComponentRef Id="cmp346061900FCE515D6B32CFCD7971325C" />\r
-            <ComponentRef Id="cmpE5AC86A748B88CA71F59F050376D4BC8" />\r
-            <ComponentRef Id="cmp26A45023E109A672FAA78FE5C4F827F4" />\r
-            <ComponentRef Id="cmp92014B7FBE8B02881A6A53384BDCD1C4" />\r
-            <ComponentRef Id="cmp372134AB6EF24BAC7497CC47466F0727" />\r
-            <ComponentRef Id="cmp17FB129D73D5BCA38BEC406E5A7A9853" />\r
-            <ComponentRef Id="cmp59BA1197723375EE2B1A8FC0971407A8" />\r
-            <ComponentRef Id="cmp9076C49961F4F0F4F5B4F65EF0CE7925" />\r
-            <ComponentRef Id="cmpCBF4FE8555DE1D61528AF79EA3C5870F" />\r
-            <ComponentRef Id="cmp63B837CFDE5F5C8ADC92D6A80E367621" />\r
-            <ComponentRef Id="cmp73FC7C1B9421F67443A7CA5E03C6F59C" />\r
-            <ComponentRef Id="cmp24D33989EF8148F7E8E801168FA75D02" />\r
-            <ComponentRef Id="cmp781F94BE6B67AAB9F2DD4A6865F67043" />\r
-            <ComponentRef Id="cmpBF8FE1999E64BE53CD0FFE028F2BA8E5" />\r
-            <ComponentRef Id="cmp508D7E9DE20A057C80595708342CCD11" />\r
-            <ComponentRef Id="cmp814DB5A7174FAD3D492689B3CFFD4FC6" />\r
-            <ComponentRef Id="cmp9FCA0D048C5FEDEF35CFDCF4220D148D" />\r
-            <ComponentRef Id="cmp8302E43F7B461A53125D24EBAB2C8BBA" />\r
-            <ComponentRef Id="cmpDD4764A31C5966C382C9FADCF2747DAE" />\r
-            <ComponentRef Id="cmp4DC6229A880E85A8EE64B69A175EB7F8" />\r
-            <ComponentRef Id="cmpC3E4C72480B21745D1D55462CD128148" />\r
-            <ComponentRef Id="cmp9C2C5E7D9EAC8AD7E9FB8BA9B0147BAB" />\r
-            <ComponentRef Id="cmp66ADBF15C183C1EF05BA59EBF57DC04C" />\r
-            <ComponentRef Id="cmpB85DB993D3D58E717F4EC2A2A5AC4130" />\r
-            <ComponentRef Id="cmp93014FB27329F9718833E30A8DCE2F23" />\r
-            <ComponentRef Id="cmp9D0CC60826A95D19D52A3A08FB44155C" />\r
-            <ComponentRef Id="cmp06BD88C78B72DD375F0FD8DC37812A7A" />\r
-            <ComponentRef Id="cmp6577B61F7B803092F263CE9BD76D7B19" />\r
-            <ComponentRef Id="cmpD880E6EDC6A23CDFF94AB5E0A0018A10" />\r
-            <ComponentRef Id="cmp00D081BB0762FEC39933D1C2960FC831" />\r
-            <ComponentRef Id="cmpD69A2401CBD6D26E44BE1271C8905A24" />\r
-            <ComponentRef Id="cmp0B7DD63BE81BC1EA8DB09A99C12CB156" />\r
-            <ComponentRef Id="cmp1EC09C5C4D6CE477EAA2BE5A3798E1CC" />\r
-            <ComponentRef Id="cmp577EBA2061AD5D464AC5B629EF1A63DE" />\r
-            <ComponentRef Id="cmpFD9033FF82B141429428B759027C0ECB" />\r
-            <ComponentRef Id="cmpD9A7A9076E4844ACEB4385E651C6FC27" />\r
-            <ComponentRef Id="cmp952D38FA5543D46E8B0B6CAC867E6D19" />\r
-            <ComponentRef Id="cmp8360F4A2F8333FCC0A95F36461DAC0AE" />\r
-            <ComponentRef Id="cmpA8C48D6004583C73A28027E805ECF241" />\r
-            <ComponentRef Id="cmpF0F52E0AA644EE5B8A4212507A2A9632" />\r
-            <ComponentRef Id="cmp1F2A598B5F572A95F619F914ADFDFD01" />\r
-            <ComponentRef Id="cmp894CC6997FDC49D32993AF049D19661B" />\r
-            <ComponentRef Id="cmp45A86D8F1E2A0DB9085F1C386BE8C441" />\r
-            <ComponentRef Id="cmpD7F91C9BF1B37E7ECEFC803745680533" />\r
-            <ComponentRef Id="cmp6C7380F6F9D3432EF0FF68155BE5B7EA" />\r
-            <ComponentRef Id="cmp163F0198648660C0C4DED70616CD3A50" />\r
-            <ComponentRef Id="cmpF5CA7A4D3DB71A67C3E4BC87D9A553DE" />\r
-            <ComponentRef Id="cmp2599D94F81ED612C05479E9A8F58A189" />\r
-            <ComponentRef Id="cmp9557C3464EC921CAC1A43D77B20BA92D" />\r
-            <ComponentRef Id="cmpFDD06D370422F04997F37CDCF9077AAF" />\r
-            <ComponentRef Id="cmpC5C6ADF92BCA401BFD4AB6B8DF07ABE6" />\r
-            <ComponentRef Id="cmpC7F75CEAF1EB837B6998ED5DC6550404" />\r
-            <ComponentRef Id="cmp4E0B2D6970453AF152BA060236BD8B71" />\r
-            <ComponentRef Id="cmp3A32B8F13C49569A0A4AD1D11045BE3A" />\r
-            <ComponentRef Id="cmp6D11E1D44ADD3049CE735F324F7F4521" />\r
-            <ComponentRef Id="cmp6763D9C183B24CD6659B616DD72994DA" />\r
-            <ComponentRef Id="cmp0FEA3716333AE56EBB72D727432DFB0A" />\r
-            <ComponentRef Id="cmpF77FF7EB150083CBBB82946EC3F849C5" />\r
-            <ComponentRef Id="cmp8693A45A9517FDC707611CD9592AD887" />\r
-            <ComponentRef Id="cmp22009CC98E7BCD73739238DBAD70F5FF" />\r
-            <ComponentRef Id="cmp006248D9C946635CD2C0D21D711BAF97" />\r
-            <ComponentRef Id="cmp8F4980EC013CDADC9677179C1B5FB4DB" />\r
-            <ComponentRef Id="cmp7CB5E11B511D00866734F2A59242071E" />\r
-            <ComponentRef Id="cmp15339C0E193CBE17FB6CB89DA971471D" />\r
-            <ComponentRef Id="cmp7A634DCF2DA1EDFF9FBAF699758570C3" />\r
-            <ComponentRef Id="cmpAA64F717D23C1375E79BE28E5A3DE005" />\r
-            <ComponentRef Id="cmp14F754092A68F3FE755963981761F031" />\r
-            <ComponentRef Id="cmp8DBD1F81C17E5D01D819E619D30E83CA" />\r
-            <ComponentRef Id="cmp9A97912EFCCFD956EF8F155A81CB494F" />\r
-            <ComponentRef Id="cmp37BA96169B5087F3D2C6E769FF5E9270" />\r
-            <ComponentRef Id="cmpE74780CCB3558FB4C37DDE65264DC1F4" />\r
-            <ComponentRef Id="cmp86D5D0E9BB2DE2BDCC6CF459CB7C4E01" />\r
-            <ComponentRef Id="cmp3951161EB256315C232D7C2B211A5D42" />\r
-            <ComponentRef Id="cmp6CAF0B6CD8ABE85E62A160036392A2CC" />\r
-            <ComponentRef Id="cmp55E217C43921E96FF82040712469E0F3" />\r
-            <ComponentRef Id="cmp57277D9EB23963010750499ED2F541B8" />\r
-            <ComponentRef Id="cmp651F7330940689374F3F4500C1F20484" />\r
-            <ComponentRef Id="cmp02C5C41CDED7F94E885A4A2A28EC3600" />\r
-            <ComponentRef Id="cmp95C32716B4F28A90B070E08CAC81BAC4" />\r
-            <ComponentRef Id="cmp2CEB59611FF3F33F7AEF7D2EAAE0CB05" />\r
-            <ComponentRef Id="cmp8D1031BFABA660F80B7793BB3761EB75" />\r
-            <ComponentRef Id="cmpB52CD2CEE9160F5F67372170543F85B4" />\r
-            <ComponentRef Id="cmp909217E4E53799CCE36ED3F7B817DF15" />\r
-            <ComponentRef Id="cmp88C4BDBD3D48F447FF34FD6DF1FC3C41" />\r
-            <ComponentRef Id="cmp6D134750A96C36CCE37CDAEAC146A810" />\r
-            <ComponentRef Id="cmp17431F800533AAD492A8DA91C537AEE5" />\r
-            <ComponentRef Id="cmp5651D28BAA4D7517AB64D608440B8947" />\r
-            <ComponentRef Id="cmp1FC8B77EF51F392FB0F6CD269098431D" />\r
-            <ComponentRef Id="cmp403E7038C7324A61D8727DC1D939E547" />\r
-            <ComponentRef Id="cmp98D46B3DC15B713ACF5F53B34EE13068" />\r
-            <ComponentRef Id="cmp82998B9C0403FF9F31583386A05AA9BB" />\r
-            <ComponentRef Id="cmp0D0B1F4459593A8B3D4A1BCBA139D98F" />\r
-            <ComponentRef Id="cmpF083A0EDD7D82B7EEFA116A49700462C" />\r
-            <ComponentRef Id="cmp094723DF4DF9079CABB07B397F53FC14" />\r
-            <ComponentRef Id="cmpE46F1304BE1F25953FA1502338B46B6E" />\r
-            <ComponentRef Id="cmpF34990BAAB80CEB1B1FBA3F75A8BB18E" />\r
-            <ComponentRef Id="cmpF8164C6D715D603507F60EA7662104BB" />\r
-            <ComponentRef Id="cmpBC49C3C8C0D1C882B8E4BFA5DDB84097" />\r
-            <ComponentRef Id="cmp026A749908FF7AE58055B0E1AB393168" />\r
-            <ComponentRef Id="cmp4644F2A5A8ADFEA7ED73D09A41CB9AFF" />\r
-            <ComponentRef Id="cmpF0E7DBBAED60C3A0D5E4980C6384C524" />\r
-            <ComponentRef Id="cmpF26383485D9640683A7276A7CFD06280" />\r
-            <ComponentRef Id="cmp5D85AEBC11476CF382025D6654F36103" />\r
-            <ComponentRef Id="cmp44002125F963B5A1D193E0D787FF6AF6" />\r
-            <ComponentRef Id="cmpF8E1020B8AEA4B8F647DF3962BB9CF2C" />\r
-            <ComponentRef Id="cmp6F5B3D19FBA609A3ECE0700E2DB1C805" />\r
-            <ComponentRef Id="cmpF0883486B5B318E646462C05EB5DBF23" />\r
-            <ComponentRef Id="cmp8F7277DF6FE75EF54431CCAB073199B5" />\r
-            <ComponentRef Id="cmpBC3A73EE4B192F1BF88AF2E61E4212EA" />\r
-            <ComponentRef Id="cmpD48A473B45218C0BDE5C64912EFCBAFC" />\r
-            <ComponentRef Id="cmp86201E518FC2273E24BCD76E296C18E1" />\r
-            <ComponentRef Id="cmpFF8739D6C4BEC98E8F1240FFBD0A64B0" />\r
-            <ComponentRef Id="cmp38720475F2109CBC7EF4F43C365CC5CA" />\r
-            <ComponentRef Id="cmp996D8C56417E10EF44329F7339E474F2" />\r
-            <ComponentRef Id="cmpED22A8F3ACEE04AB6CE44D533F4F300A" />\r
-            <ComponentRef Id="cmp838B8853704ACB2A7F45BA00B950F3BB" />\r
-            <ComponentRef Id="cmpDF9CC0BB490A823FFADEFFBDAB92C2C7" />\r
-            <ComponentRef Id="cmp73DF7086A467801478C7321A8B435AA2" />\r
-            <ComponentRef Id="cmp10D7AC86A56DE2D04356715F35250435" />\r
-            <ComponentRef Id="cmpB8BBD8CCBCDE8771AD9E3D6D456FDD6B" />\r
-            <ComponentRef Id="cmp5DA5D6BC1A869E7409CC08ACDEBAA08F" />\r
-            <ComponentRef Id="cmpEFE26691A6CBD74691FA18F89319520B" />\r
-            <ComponentRef Id="cmpF14CA7FF23A8FEBB250D9C1A4DBAB5F2" />\r
-            <ComponentRef Id="cmp76B901918023AFA1ECF5AFE1C5AADFD6" />\r
-            <ComponentRef Id="cmp3B04385170209415EA0392D6C6D9394D" />\r
-            <ComponentRef Id="cmpAC7FCF48DC2DD2FF598D8D8EDFBDDE10" />\r
-            <ComponentRef Id="cmp31295E08BF0ADC9F7414B879A59EAB38" />\r
-            <ComponentRef Id="cmp26ED7398D2B7FD7CE79B20C6F81AA21B" />\r
-            <ComponentRef Id="cmp0AF138C48EF9389FB53BCFFF127C63B7" />\r
-            <ComponentRef Id="cmpB84DF029169B6913288463C0CD791FD7" />\r
-            <ComponentRef Id="cmpB2768FB92A037D37B0EDB0A50917DB6E" />\r
-            <ComponentRef Id="cmp60DE9C3DB4C8AE187B4D19BDEDC6C16A" />\r
-            <ComponentRef Id="cmp8439239F2F45A8EAE3D84F080943DDD1" />\r
-            <ComponentRef Id="cmp16223EE59A9E78D62203D037E29D698D" />\r
-            <ComponentRef Id="cmp018AFFA2C882E3A63FE78C48058C9701" />\r
-            <ComponentRef Id="cmpD9344D05ADD481EACDED9FFB5EE9D160" />\r
-            <ComponentRef Id="cmpA9175F4256E542F0F7D7729B65AC8716" />\r
-            <ComponentRef Id="cmp022C885B9AF26895C6BBDBE1AF964DA7" />\r
-            <ComponentRef Id="cmpB42CC102A79539E49D9D71AF819BDCFA" />\r
-            <ComponentRef Id="cmpDE27E4C6B7C8141F7B15529523211BDE" />\r
-            <ComponentRef Id="cmp8A1FE21942F63A12FE8C262291D2F118" />\r
-            <ComponentRef Id="cmp89930EDDF65A84FB647750A3889D3998" />\r
-            <ComponentRef Id="cmp7A7C588B21E8E3817983B40E6C15A8DC" />\r
-            <ComponentRef Id="cmpFAF5864C58442FAA1D5C0A01C83FACE9" />\r
-            <ComponentRef Id="cmp48641EDC547835A4C015C138C9EE12D6" />\r
-            <ComponentRef Id="cmp74D44A9BA04153CCD0A6FA413CA57C4B" />\r
-            <ComponentRef Id="cmp84CD7E65AAF5D3AD9F421445901CF37D" />\r
-            <ComponentRef Id="cmpAB0EEB7FDAB89DB33CC1872F2D020B9A" />\r
-            <ComponentRef Id="cmpEA5C0DF0E7CC7FF0C0A116E8ED0DFD39" />\r
-            <ComponentRef Id="cmp48F6BA7469D6C5E8A90EFDC2F1F12A03" />\r
-            <ComponentRef Id="cmpEACB1D29768FC4AD7754B7A8BB5CEAA4" />\r
-            <ComponentRef Id="cmp40DFBE5C4004C66BD421833A0916A402" />\r
-            <ComponentRef Id="cmpD46A2953ED55E92D9E0462BE4D66B828" />\r
-            <ComponentRef Id="cmp9BBFEBDFBD798ED64CF1BFB852E57719" />\r
-            <ComponentRef Id="cmpD60B739F43AAC84324A66CEFFCDDBDFF" />\r
-            <ComponentRef Id="cmp4AF8FAB5F76A012B1E00956784FB86C0" />\r
-            <ComponentRef Id="cmpD3F2F08057724AFA25E4EBF3514406A6" />\r
-            <ComponentRef Id="cmp5D978D2112EB628D93B78BBB779859CA" />\r
-            <ComponentRef Id="cmp6800D8FB1FBB983412E92B80451A1109" />\r
-            <ComponentRef Id="cmp71287354FF8DCC602BB0D3D414E05962" />\r
-            <ComponentRef Id="cmp0DF5FCC7255520F9D24CAC298FA859D0" />\r
-            <ComponentRef Id="cmp3301D802CB29561A15AE015C15DD8543" />\r
-            <ComponentRef Id="cmp00A4060669FB650FE92DDD9A09A857C0" />\r
-            <ComponentRef Id="cmp2D3FB42E796C744949D0480EE5C04892" />\r
-            <ComponentRef Id="cmp11B5D7ADB851C95DCFE5C71959222497" />\r
-            <ComponentRef Id="cmp27BC211BA8D2261D578FC390503266AF" />\r
-            <ComponentRef Id="cmp60AB973400004A31DBB4967567BB3780" />\r
-            <ComponentRef Id="cmp026D50205EC5D03323420AC1EC10FD89" />\r
-            <ComponentRef Id="cmpE0BD5867C82F9D8554FECDDC4B6901C7" />\r
-            <ComponentRef Id="cmp9F4E4D4D0534820ED7B1592321BACF51" />\r
-            <ComponentRef Id="cmpCB85EA75A98576ACBC265DECF4F7697F" />\r
-            <ComponentRef Id="cmpD8B043EAF161B1EED3C0D0E0E8B0717E" />\r
-            <ComponentRef Id="cmp006E05B629020152BA06266DCF586F94" />\r
-            <ComponentRef Id="cmp6390600916A9D80DEECC6B64C1A5734B" />\r
-            <ComponentRef Id="cmpA029FDE3C079115EA43947112C138B8C" />\r
-            <ComponentRef Id="cmp050C776C9A28886790788280CCEC3D91" />\r
-            <ComponentRef Id="cmpBFBC5C595627F87399C1078F9A522E9D" />\r
-            <ComponentRef Id="cmp2B0856221A67BDF81C2238000B184A17" />\r
-            <ComponentRef Id="cmp835605203F95C84205B718EDDDE2091E" />\r
-            <ComponentRef Id="cmpD37BF99FBDB8EFD807188027A80B8DA3" />\r
-            <ComponentRef Id="cmp31DA9547A00549AAE408F3DADB011332" />\r
-            <ComponentRef Id="cmpC869C8092B8F2AAC631C286328891A94" />\r
-            <ComponentRef Id="cmp0305CAFC1FCE93ED480CC829AAD832AF" />\r
-            <ComponentRef Id="cmp5D66D11EA79AD27A1AE641BF9C7CB9F2" />\r
-            <ComponentRef Id="cmpA817DF6EA38B1DF29BCE614C5450544B" />\r
-            <ComponentRef Id="cmpFB26590AF40355E4D807DA7009B7836D" />\r
-            <ComponentRef Id="cmpFA168D7F4EFC52948B17DF862E07E25A" />\r
-            <ComponentRef Id="cmp80B129BEE9AED15B717DBBC4E1C91B8D" />\r
-            <ComponentRef Id="cmp222BD8179F2D4ECA9CD649598F1665FE" />\r
-            <ComponentRef Id="cmpE9263BDE73548B76D4CF00FA421C6394" />\r
-            <ComponentRef Id="cmp665380529EE1C0ED1DE23E8B26850B19" />\r
-            <ComponentRef Id="cmp8DD36362549E19ECEA88E3D1B1ACBB85" />\r
-            <ComponentRef Id="cmpD2EA9700B16C8836D5206D1185C549A0" />\r
-            <ComponentRef Id="cmp73D2F27C7DDDD4CF7ADAEBF8E6E42EB1" />\r
-            <ComponentRef Id="cmp7E31792D0EB10DD8AFD37842DFFCA924" />\r
-            <ComponentRef Id="cmpA158D23F2833A86832CC8AB04B51E8D0" />\r
-            <ComponentRef Id="cmpA62BD30E70FBA79DED1CC038B0404EBC" />\r
-            <ComponentRef Id="cmp78F57F7853BF2F83CA4C27E09AE1E09D" />\r
-            <ComponentRef Id="cmpE3E3B7C94E5AFDAF94C8CC78998771DF" />\r
-            <ComponentRef Id="cmpBC000241C708266C86D302A435E66F1C" />\r
-            <ComponentRef Id="cmp3B8600643B709AD9AD113079EDB50C94" />\r
-            <ComponentRef Id="cmpB755C164EDFD73E1498268DB3DA880E5" />\r
-            <ComponentRef Id="cmp1CBDAA564E02886155300F7A8AF049CF" />\r
-            <ComponentRef Id="cmpFAE3866F17FF7039CA9618739C64A1A5" />\r
-            <ComponentRef Id="cmp1BEDE14E6357E29206EDC740AE50650E" />\r
-            <ComponentRef Id="cmp9848706507698E3D2DE99B0C079EAA41" />\r
-            <ComponentRef Id="cmpAF75487C0AF2238D5F5AB27EE11B8C29" />\r
-            <ComponentRef Id="cmpF1D853F627859C5B31EEDA150FC58C6E" />\r
-            <ComponentRef Id="cmp24C324DDA2757C24A5D64657F10D1861" />\r
-            <ComponentRef Id="cmp24A094BA67CBF70909850C9D82D581FB" />\r
-            <ComponentRef Id="cmp855A7D3DC1E9E7D07C156736FB529CB6" />\r
-            <ComponentRef Id="cmpF44D1A83846E86F2D4EFFEDC9DA14661" />\r
-            <ComponentRef Id="cmp15902266961B205BB7C49E3F21B0A166" />\r
-            <ComponentRef Id="cmp50966487B32C316D078827563E31DB30" />\r
-            <ComponentRef Id="cmpA1B9598A2B3B9C203B5780C91F7075D7" />\r
-            <ComponentRef Id="cmpEF587C49FDE4B67961EDEA007128D3DE" />\r
-            <ComponentRef Id="cmp793DBF048FBD4343297F1E17756BBF6B" />\r
-            <ComponentRef Id="cmpD98F61FC599E1BD66057249B28016A0E" />\r
-            <ComponentRef Id="cmp4C6EA3AFEB0324ED3875A46DCCB4EA48" />\r
-            <ComponentRef Id="cmpA7F194C9D57330A5F72C74CD96507E48" />\r
-            <ComponentRef Id="cmp337BDFFE231941F8A3E54BF4AC644B58" />\r
-            <ComponentRef Id="cmp0255A607D5334E94D517A2FEDDB2105D" />\r
-            <ComponentRef Id="cmpBDEB6272B93E7E2FD1982012B5EF3D02" />\r
-            <ComponentRef Id="cmp6ACA30972AFD6D78448054BA4368AEE7" />\r
-            <ComponentRef Id="cmp0014F4AC9A48147F8EAB3F17B2C144E0" />\r
-            <ComponentRef Id="cmpB1ABCFD82FD1858E7657C34A8104404C" />\r
-            <ComponentRef Id="cmp6A432A70B05CC4AC38DA38F776B97812" />\r
-            <ComponentRef Id="cmp134038F5FD44A08B4DA0CFD8921DFB42" />\r
-            <ComponentRef Id="cmp34FB0E37566676B69EEEB98F670D23C4" />\r
-            <ComponentRef Id="cmp166A7C72CA2D82533EBF24FB87FE4BC5" />\r
-            <ComponentRef Id="cmp11D7406D35C2641BC20018F1FA8ADC45" />\r
-            <ComponentRef Id="cmp6CA80FCA9CDD4C55F2582BF139AA4B1B" />\r
-            <ComponentRef Id="cmpDFBFE88B708CE2CA5904473FE0CDB861" />\r
-            <ComponentRef Id="cmp8D47A9ABEDE81A100C924FF154224483" />\r
-            <ComponentRef Id="cmp0071C7F9D6D4B5DC3218D487DABFC218" />\r
-            <ComponentRef Id="cmp54B17CC907A926962454D9FDEA99E369" />\r
-            <ComponentRef Id="cmpDF321BF03AC6D81897FFD4B64045DE88" />\r
-            <ComponentRef Id="cmpEBBBCD3B40ADAF40F5F7BBDAD1628BAB" />\r
-            <ComponentRef Id="cmp4781FFA691E6B55670D19CCA10E68D4A" />\r
-            <ComponentRef Id="cmp9D75099F72BC1EA408C69BAE1036713A" />\r
-            <ComponentRef Id="cmpD507403E3878D92F029FB454CD349781" />\r
-            <ComponentRef Id="cmpDB50F59C1041F3C78FB11F782D92A59C" />\r
-            <ComponentRef Id="cmp9ACED442699FD1B84B592FD9A678FF1F" />\r
-            <ComponentRef Id="cmpC959D8B9C731BB4CB168C53232754164" />\r
-            <ComponentRef Id="cmpF036082D9E70D219B49993BF2078FC9B" />\r
-            <ComponentRef Id="cmp610C62A18FB0CBD4A23E6420089991A6" />\r
-            <ComponentRef Id="cmpF0A89D556198AEC41AA931DE764420C9" />\r
-            <ComponentRef Id="cmp1FB4C766D7DFBDC29927513C52FF444C" />\r
-            <ComponentRef Id="cmpAB3EF735C77E4B6501B74C3070F17E8D" />\r
-            <ComponentRef Id="cmpEF370FB1E0BE2F7C44ACA77A0B9A380F" />\r
-            <ComponentRef Id="cmpD53F72AC172BCE3F947CB966768B983E" />\r
-            <ComponentRef Id="cmpB58DE0208168537FC63A28BA40094D3E" />\r
-            <ComponentRef Id="cmp1E24968394165967A4C933056CC97E8B" />\r
-            <ComponentRef Id="cmpA780D5122E140F762FC4C3A0D02E7179" />\r
-            <ComponentRef Id="cmpE61937584021054E6A0A34AFBB281937" />\r
-            <ComponentRef Id="cmpB2D199D19AF00559ED6198A864F4874E" />\r
-            <ComponentRef Id="cmpA29A335EDA4A585067146163B51D3643" />\r
-            <ComponentRef Id="cmp4F8C12B67EDC519F78AA4840C85494D1" />\r
-            <ComponentRef Id="cmp30F05444D83E8DAFE75CAAB16F50E3AD" />\r
-            <ComponentRef Id="cmp7573B19F4A56CF2B1D359A87D25A39B4" />\r
-            <ComponentRef Id="cmp0B698F624CC72E780FB1FCCBE6340AAD" />\r
-            <ComponentRef Id="cmp898F4E8E360375D6D8FE6F606F899441" />\r
-            <ComponentRef Id="cmp246EDCD16E1C5BC88F69EFDF059C62B6" />\r
-            <ComponentRef Id="cmpB92657C823E717DB4819343891A10BB1" />\r
-            <ComponentRef Id="cmpF4DE6E8F14E6A8E27857D8BC89787BAF" />\r
-            <ComponentRef Id="cmpF49A6AC910C2EB6FF0917041E1F96E8A" />\r
-            <ComponentRef Id="cmp1DF54EA158668A0E21013A820D2E7F51" />\r
-            <ComponentRef Id="cmpDFA6653A3AA375D59BE8318C927D5DC4" />\r
-            <ComponentRef Id="cmp20B23CB93B2BC9BF69A97D0994698310" />\r
-            <ComponentRef Id="cmp735A0D9DB2D96E80F3352A232FCAB063" />\r
-            <ComponentRef Id="cmpDB346892873A00B4024D345FE4C725BA" />\r
-            <ComponentRef Id="cmp32E0784BA82190D0BD64E488EF265F55" />\r
-            <ComponentRef Id="cmp4215773DF3A614E82EF59C456A6EFA97" />\r
-            <ComponentRef Id="cmp72E1D5A67C90BF0538F8015E486898B0" />\r
-            <ComponentRef Id="cmp02A0C8EDA0860CBAE68AE51FAE93EBB1" />\r
-            <ComponentRef Id="cmpC54A96A34D6E8EEB099E5754699E907D" />\r
-            <ComponentRef Id="cmp2564368EA9BDCD6CBD86BD0B47845CE8" />\r
-            <ComponentRef Id="cmp4C037A4C1F7E6103342D757CE84FB3B0" />\r
-            <ComponentRef Id="cmp2782B54139300C706E3DF5D779A32A4E" />\r
-            <ComponentRef Id="cmp5ACAEC60F8EC208DC179E4790FE9090B" />\r
-            <ComponentRef Id="cmpE672C34FD6F4C63A61786B77A67D0BCA" />\r
-            <ComponentRef Id="cmp05B02931195D1A41BF37EA6B3C26F10B" />\r
-            <ComponentRef Id="cmp2214585F3508B759DB1308CC180757FB" />\r
-            <ComponentRef Id="cmpA1474840D69304F1AD34009D58C2B5DA" />\r
-            <ComponentRef Id="cmp40B743148ECA12B22598A157C6147B6E" />\r
-            <ComponentRef Id="cmp104D8EDE2CF475C5EA4FE8169E74E299" />\r
-            <ComponentRef Id="cmpDC8A03C1B37F2CFE043AED01502A741C" />\r
-            <ComponentRef Id="cmp87D7B0E5B5E51059D964EDCED844F4AD" />\r
-            <ComponentRef Id="cmp80E63D0ACF59FD23F5B051FE5BD003DF" />\r
-            <ComponentRef Id="cmp65C97B5A564AB844DB2F02FFA5B163A4" />\r
-            <ComponentRef Id="cmp4A028D7062688CA7458F90092B47AAAC" />\r
-<?endif?>\r
-<?endif?>\r
-        </ComponentGroup>\r
-    </Fragment>\r
-</Wix>\r
diff --git a/win/jack.pc b/win/jack.pc
new file mode 100644 (file)
index 0000000..3d2529d
--- /dev/null
@@ -0,0 +1,11 @@
+prefix=/c/Program\ Files/JACK2
+exec_prefix=/c/Program\ Files/JACK2
+libdir=/c/Program\ Files/JACK2/lib
+includedir=/c/Program\ Files/JACK2/include
+server_libs=-L/c/Program\ Files/JACK2/lib -ljackserver64
+
+Name: jack
+Description: the Jack Audio Connection Kit: a low-latency synchronous callback-based media server
+Version: 1.9.19
+Libs: -L/c/Program\ Files/JACK2/lib -ljack64
+Cflags: -I/c/Program\ Files/JACK2/include
index 4bdf80732f8c6269bfc1bd37b5e36216c4cc9da3..d557456c48e113a8f67a6e9242ff18b719eea7a7 100644 (file)
@@ -20,6 +20,16 @@ if host_machine.system() == 'windows'
                endif
                wingetopt = cmake.subproject('wingetopt', options: opt_var)
                deps += wingetopt.dependency('wingetopt')
+               link_args += 'userenv.lib'
+               link_args += 'Synchronization.lib'
+               link_args += 'Netapi32.lib'
+               link_args += 'Version.lib'
+               link_args += 'Dwrite.lib'
+               link_args += 'Iphlpapi.lib'
+               link_args += 'Secur32.lib'
+               link_args += 'Winhttp.lib'
+               link_args += 'Dnsapi.lib'
+               link_args += 'Iphlpapi.lib'
        endif
 
 endif
diff --git a/win/qt5.wxs b/win/qt5.wxs
new file mode 100644 (file)
index 0000000..0334793
--- /dev/null
@@ -0,0 +1,1845 @@
+<?xml version="1.0" encoding="utf-8"?>\r
+<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">\r
+    <Fragment>\r
+        <DirectoryRef Id="INSTALLDIR">\r
+            <Component Id="cmpE6579DC78E167F46AB23E0BE3EAA58CE" Guid="27BFCC7D-F4A8-4F71-8A2B-00A8BC74B106">\r
+                <File Id="fil5E6E1243EAE085FE802B6D64690357B0" KeyPath="yes" Source="SourceDir\jacktrip.exe" />\r
+                <Shortcut Id="startmenuJackTrip" Directory="ProgramMenuDir" Name="JackTrip"\r
+                    WorkingDirectory="INSTALLDIR" Icon="jacktrip.exe" IconIndex="0" Advertise="yes" />\r
+            </Component>\r
+            <Component Id="cmp52FE9CFF214054652C281C37201305EC" Guid="{158D5247-525B-417F-B12B-48F13CD7E414}">\r
+                <File Id="fil8AF433A253A49035CB32EA6F957BF7D4" KeyPath="yes" Source="SourceDir\LICENSE.md" />\r
+            </Component>\r
+            <Directory Id="LICENSESDIR" Name="LICENSES">\r
+                <Component Id="cmpA07F2DC814BECD1E5F3FC36DC47718E5" Guid="{36E317A7-BA26-4E2B-99CD-0728CD8779DD}">\r
+                    <File Id="fil39657C9CD79E70F8B3033F0CF1D035A1" KeyPath="yes" Source="SourceDir\LICENSES\LGPL-3.0-only.txt" />\r
+                </Component>\r
+                <Component Id="cmpBF7FA1E27DF091A3CCFA1CD7B7CD94BA" Guid="{2D555037-9C09-4042-8F0C-C6EBAB80681A}">\r
+                    <File Id="fil496C76DCE2246C589E09AC68BCFDFB32" KeyPath="yes" Source="SourceDir\LICENSES\MIT.txt" />\r
+                </Component>\r
+                <Component Id="cmpF915A8D749EFFA98CD8DD582A7C82614" Guid="{A3C72CE1-0570-497B-A84F-F21C53771ABC}">\r
+                    <File Id="filCCF13ED800A4C7B438EC52A50C3A6755" KeyPath="yes" Source="SourceDir\LICENSES\GPL-3.0.txt" />\r
+                </Component>\r
+              </Directory>\r
+<?ifdef rtaudio?>\r
+            <Component Id="cmpD683260746BC875AE3BDED667AA8F954" Guid="20A867E8-36B6-49E3-A1DA-76CFCE967908">\r
+                <File Id="filA7BCB2CDD7D00C9DFB32AEB94F294425" KeyPath="yes" Source="SourceDir\librtaudio.dll" />\r
+            </Component>\r
+<?endif?>\r
+<?ifdef dynamic?>\r
+            <Component Id="cmpF83098612A5B11C5398DBA3894ED590A" Guid="6A0A67A9-3318-4CFB-BC07-5AF3FF7B7E87">\r
+                <File Id="fil526FA994C1850220CFA7BD7FCD995C32" KeyPath="yes" Source="SourceDir\D3Dcompiler_47.dll" />\r
+            </Component>\r
+<?ifdef qt5?>\r
+            <Component Id="cmp5DDCF0CE56D406D1B8B1F93E988AED6B" Guid="4346C308-2912-4CAF-B01B-1FB85781DAD0">\r
+                <File Id="fil0F4CD3729AF07C1C460E292E0F081D0A" KeyPath="yes" Source="SourceDir\libEGL.dll" />\r
+            </Component>\r
+            <Component Id="cmpB0CFEB9FF932521D6A75B6BE6F8D644F" Guid="73A8C873-FBDB-4BC5-BB74-2F82619DC3EC">\r
+                <File Id="fil736048FE57EC840EA5DCE88625C38E8D" KeyPath="yes" Source="SourceDir\libGLESv2.dll" />\r
+            </Component>\r
+<?endif?>\r
+<?ifdef mingw?>\r
+            <Component Id="cmpB60B52D038613C3F20F84A0E1C83D4D4" Guid="62E3CBE5-9787-4A5C-B6CD-B867781773EE">\r
+                <File Id="fil09D765981E2E493119D2DBD2DA179B9D" KeyPath="yes" Source="SourceDir\libcrypto-1_1-x64.dll" />\r
+            </Component>\r
+            <Component Id="cmpDEFDDDBEBEA7255D07EE4400F2CDA2FF" Guid="904EB309-B381-443D-A064-4C2669E8E66E">\r
+                <File Id="fil2ECEB8B8BE6121A2C5659BA0F64D92FC" KeyPath="yes" Source="SourceDir\libgcc_s_seh-1.dll" />\r
+            </Component>\r
+            <Component Id="cmp809509D62BD9EA848EF243F03D83758F" Guid="FE4AFCE9-7DE1-4A82-A96E-42C8B93E04C7">\r
+                <File Id="filCE00A8B273087E758109FEC2AC699195" KeyPath="yes" Source="SourceDir\libssl-1_1-x64.dll" />\r
+            </Component>\r
+            <Component Id="cmp57DDA83E372188A5B760684B961DE685" Guid="96741680-04B3-4A4A-8D55-08C48D6B2201">\r
+                <File Id="filF57E66845C852C7C1EFFE6996027BE3C" KeyPath="yes" Source="SourceDir\libstdc++-6.dll" />\r
+            </Component>\r
+            <Component Id="cmpF76D0CD1138260DB167395B86D0011CF" Guid="88B4D808-2CD2-40E0-9BC4-51093AFD3D61">\r
+                <File Id="fil8C56E6114C3ACED0DF58456A08DC0437" KeyPath="yes" Source="SourceDir\libwinpthread-1.dll" />\r
+            </Component>\r
+            <Component Id="cmpEA16414E09FD1D5068139520966B49EB" Guid="9B14A007-CA41-4C45-BE9E-0B1067DD5311">\r
+                <File Id="filFC8C908E3AD35E8090AFF39C9B0FB744" KeyPath="yes" Source="SourceDir\opengl32sw.dll" />\r
+            </Component>\r
+<?endif mingw?>\r
+<?ifdef qt5?>\r
+            <Component Id="cmp01229088A6F1DEA1BE391E6E7CC3D3EE" Guid="9A610B9C-6322-4045-9C4B-910FFBA38502">\r
+                <File Id="filAD80F4724AAE3D8B0682F5982CC2548D" KeyPath="yes" Source="SourceDir\Qt5Core.dll" />\r
+            </Component>\r
+            <Component Id="cmp1658AEC83DA2CDD0D93D5BAAABBEAD46" Guid="9D7923EE-3849-4FCE-A0C4-A2254AD769D3">\r
+                <File Id="fil7E727B350B33C4EEEFE406E27624E057" KeyPath="yes" Source="SourceDir\Qt5Gui.dll" />\r
+            </Component>\r
+            <Component Id="cmp2DE804C107C75B0C71470A93647D09B5" Guid="F8E604E4-A6B2-444D-9F1F-2D86230D5585">\r
+                <File Id="filD86C7802D20EDF450E8B03BC177BA71F" KeyPath="yes" Source="SourceDir\Qt5Network.dll" />\r
+            </Component>\r
+            <Component Id="cmp82912105030EA0D2D7550CF8AC5677F6" Guid="17A8DAEB-44BD-451C-BEEE-C3C2EF883516">\r
+                <File Id="fil6DA129DEAE11F969B17E1886F9AF9AA0" KeyPath="yes" Source="SourceDir\Qt5Svg.dll" />\r
+            </Component>\r
+            <Component Id="cmp975651B71289ACA70B3EB154C08A27F0" Guid="FD612706-4DF7-439B-B82B-57EFA2657F84">\r
+                <File Id="filE89B28CF105C7A7691C046E6516D540E" KeyPath="yes" Source="SourceDir\Qt5Widgets.dll" />\r
+            </Component>\r
+            <Directory Id="dirFF9CCFA394406B344C7FF767066C0809" Name="bearer">\r
+                <Component Id="cmp3CE5AF7CB2F8B2689D2096769883BC56" Guid="FD4AB5B8-4F89-4C08-819B-58A7E8F6360A">\r
+                    <File Id="fil94C33B546082B4DF43813FFB7569FBF6" KeyPath="yes" Source="SourceDir\bearer\qgenericbearer.dll" />\r
+                </Component>\r
+            </Directory>\r
+<?endif?>\r
+<?ifdef qt6?>\r
+            <Component Id="cmp9147A534CD8EDAB4BA7FDEAEEFE9C511" Guid="{8D05F121-517E-4A1F-86FE-26A735F23F1C}">\r
+                <File Id="fil88DECE330C4A1F60E0198BE9EAD7F35B" KeyPath="yes" Source="SourceDir\Qt6Core.dll" />\r
+            </Component>\r
+            <Component Id="cmp52AB384603E096D6B6A1E1FC66B089D4" Guid="{FA746517-7C88-4473-9162-22E9A8CF6507}">\r
+                <File Id="fil93995D4EFFB081D1CEF92B0D38526E04" KeyPath="yes" Source="SourceDir\Qt6Gui.dll" />\r
+            </Component>\r
+            <Component Id="cmp0E8605D5C55843FCDFE809B5EE8DFDFA" Guid="{499C045E-88C6-43A6-B567-6C3438CA5FFD}">\r
+                <File Id="fil63F02A7FFBCDAAB6EAB6E65E642AC7B0" KeyPath="yes" Source="SourceDir\Qt6Network.dll" />\r
+            </Component>\r
+            <Component Id="cmp5D904885C8104A4049616BBAA95F34A3" Guid="{B47BE8F9-D9B5-4345-A232-F2EE8B6D4931}">\r
+                <File Id="fil30A776C0F63064A6AF65369FF23FDB8C" KeyPath="yes" Source="SourceDir\Qt6Svg.dll" />\r
+            </Component>\r
+            <Component Id="cmpCAE694BB34A9EC66AEA9D10D697F1EAA" Guid="{C1A11A59-3BF3-4562-92C6-304A80B93DAE}">\r
+                <File Id="fil43665A392379256FF1D68CF38B97EA38" KeyPath="yes" Source="SourceDir\Qt6Widgets.dll" />\r
+            </Component>\r
+<?endif qt6?>\r
+            <Directory Id="dirB4D909CA5877E8EBEB32B83B2D15F595" Name="iconengines">\r
+                <Component Id="cmp1AAFF160C8E04AD945D74CA884667ABC" Guid="F9CAC368-2789-40F4-ADB8-0EDF377306DF">\r
+                    <File Id="filB4B2363760DE489E24EBDBD0BC837FDE" KeyPath="yes" Source="SourceDir\iconengines\qsvgicon.dll" />\r
+                </Component>\r
+            </Directory>\r
+            <Directory Id="dir60B76952C227590F998DC42F548339BE" Name="imageformats">\r
+                <Component Id="cmp02252B1C1976959CFEE7FA11B6E36C4C" Guid="67FB3C37-C4EB-404B-84E0-2B85288C9A46">\r
+                    <File Id="fil646F5D906D2B2A321C0C481D1D22C9C7" KeyPath="yes" Source="SourceDir\imageformats\qgif.dll" />\r
+                </Component>\r
+                <Component Id="cmpD9DF9496C7453F1AA6C20CD4A1C94ED7" Guid="6EAE06EC-8FC6-4075-8B7D-2E5E2ECDBC1D">\r
+                    <File Id="fil0A4C8F6CF0C62302928ED23A10F88E84" KeyPath="yes" Source="SourceDir\imageformats\qico.dll" />\r
+                </Component>\r
+                <Component Id="cmp0E8940C551429DC79C890130F6D10B6D" Guid="4E9F8DE6-5789-421B-8299-04C8F95F6804">\r
+                    <File Id="fil080B2419A3875B7348050418715B59D5" KeyPath="yes" Source="SourceDir\imageformats\qjpeg.dll" />\r
+                </Component>\r
+                <Component Id="cmpC6FCE143A8E5EAFEA6119E94128459C6" Guid="E22AD241-DA8F-4016-A34B-3DA0ABB77AE9">\r
+                    <File Id="filE6DE80FB6D1ADCBBC8D5665DCF3A4674" KeyPath="yes" Source="SourceDir\imageformats\qsvg.dll" />\r
+                </Component>\r
+<?ifdef extraimageformats?>\r
+                <Component Id="cmp62CBFF830BF239A4E376FAE29937AFAB" Guid="FCFFB090-6600-4F92-9F71-A9EFDA3F5F05">\r
+                    <File Id="fil2132CC3E0278FB6088A48B8A3C120D44" KeyPath="yes" Source="SourceDir\imageformats\qicns.dll" />\r
+                </Component>\r
+                <Component Id="cmpFCC4763BEDEA49F63E8D8E3A18E86329" Guid="D3ABC0A1-CE5F-4ABB-8B15-E630FCF1C9B1">\r
+                    <File Id="filAABDE938CE7225264D2AD6E5B85F0F35" KeyPath="yes" Source="SourceDir\imageformats\qtga.dll" />\r
+                </Component>\r
+                <Component Id="cmpF278C1175D7FB53021C0126B8F155FC2" Guid="24C00F19-08E6-40E6-913E-AD54EFB81AB7">\r
+                    <File Id="fil102CCD3441784435E39937C763D95F2F" KeyPath="yes" Source="SourceDir\imageformats\qtiff.dll" />\r
+                </Component>\r
+                <Component Id="cmp608F60C2BD04F2C37AB51C3211ABF615" Guid="8A88549A-3B1F-4A94-B721-7ED99998CA95">\r
+                    <File Id="fil8698D33404DE182991C30D32BA39A5EC" KeyPath="yes" Source="SourceDir\imageformats\qwbmp.dll" />\r
+                </Component>\r
+                <Component Id="cmpE6323C810986405043F219CECD6B6C10" Guid="7194531B-2B4C-4DB0-8BED-B89929695F17">\r
+                    <File Id="filB9C6E81C6DBC26365A8C44CD0A36C49F" KeyPath="yes" Source="SourceDir\imageformats\qwebp.dll" />\r
+                </Component>\r
+<?endif extraimageformats?>\r
+            </Directory>\r
+            <Directory Id="dir623D6610C7C3C018A72040463AD22773" Name="platforms">\r
+                <Component Id="cmp9132FD413972F732967B8BC075BC2781" Guid="0DAA1BEB-EA44-4EEC-BCC8-22CE871FC519">\r
+                    <File Id="fil835F5A10B680242272F647CE0A82B16C" KeyPath="yes" Source="SourceDir\platforms\qwindows.dll" />\r
+                </Component>\r
+            </Directory>\r
+            <Directory Id="dirF1334AF64D097D9920D6F285D2DE292C" Name="styles">\r
+                <Component Id="cmp52840D209B2AC1DC45A29A6BA5A75FF8" Guid="3FA00436-A967-45AB-8871-A6DA384044CE">\r
+                    <File Id="fil84E6446D9ECFA487C5A2EC4752E07F04" KeyPath="yes" Source="SourceDir\styles\qwindowsvistastyle.dll" />\r
+                </Component>\r
+            </Directory>\r
+<?ifdef qt6?>\r
+            <Directory Id="dir65521F18679A6D14A7BDE5A8612E8C69" Name="tls">\r
+                <Component Id="cmp83139013FA2C9A99CF2772FD54090FAA" Guid="{60DFDBBA-4832-44FD-B14A-C67EBD4D8119}">\r
+                    <File Id="fil737932C7578FEA0392CDEFB7540D7E44" KeyPath="yes" Source="SourceDir\tls\qcertonlybackend.dll" />\r
+                </Component>\r
+                <Component Id="cmp6E4460E3D6226693FEA5DBBF500895C7" Guid="{CBF26A3F-BA32-4017-BAEF-014AA3C51653}">\r
+                    <File Id="fil02E5C4A93CDAD6084E6372A2EE1F7ABA" KeyPath="yes" Source="SourceDir\tls\qopensslbackend.dll" />\r
+                </Component>\r
+                <Component Id="cmp53306F84AD30BD49CF0C1A68007F9531" Guid="{47907844-2DC3-4E99-B1C9-B9C716E59407}">\r
+                    <File Id="filD182E98DFB41D91EC2F2A531F6CAF918" KeyPath="yes" Source="SourceDir\tls\qschannelbackend.dll" />\r
+                </Component>\r
+            </Directory>\r
+<?endif qt6?>\r
+<?ifdef hastranslations?>\r
+            <Directory Id="dirF1C5FA08924D46D2A43D5134DF70C0A8" Name="translations">\r
+                <Component Id="cmpBCDC6995284E75B9A24E4646E6117744" Guid="51A3610B-4AE8-4020-926E-80B77B650E52">\r
+                    <File Id="fil694FD3DFB9C088844BF9929014423509" KeyPath="yes" Source="SourceDir\translations\qt_ar.qm" />\r
+                </Component>\r
+                <Component Id="cmp3B3D2ADEE08668758CB9FDB1D243AA11" Guid="E84288C8-80B5-43C0-ABC1-13720B58F806">\r
+                    <File Id="fil0877F2432B761D0289C5DB92EC1E528E" KeyPath="yes" Source="SourceDir\translations\qt_bg.qm" />\r
+                </Component>\r
+                <Component Id="cmpDAE87E8CC29C30A200A5D17D81DF1DE7" Guid="BC72F77D-4D7F-4AFD-BC4F-0F9E337182A9">\r
+                    <File Id="fil36E8A56D7DAEC62D31CEA91730D42174" KeyPath="yes" Source="SourceDir\translations\qt_ca.qm" />\r
+                </Component>\r
+                <Component Id="cmp719D62CCB63A2C08F22335557CBAB533" Guid="084DAFF0-3499-4D2E-9685-D0F7573A0723">\r
+                    <File Id="fil5ECAF3F9F1940369AA594EE12825D0FB" KeyPath="yes" Source="SourceDir\translations\qt_cs.qm" />\r
+                </Component>\r
+                <Component Id="cmp8BDD412901C153FC8A2BF7335835AC78" Guid="ED469E4F-52A1-4BDC-83EA-304D779025ED">\r
+                    <File Id="fil035B9FC36AE44B63202E6201D2A9608E" KeyPath="yes" Source="SourceDir\translations\qt_da.qm" />\r
+                </Component>\r
+                <Component Id="cmp56040A1BBEBE8A547F7D6672326FF4EE" Guid="F0F32818-EB50-415C-8302-B3BCFA3D1A5F">\r
+                    <File Id="fil0AA794E087CE581F4CA99D75C295092D" KeyPath="yes" Source="SourceDir\translations\qt_de.qm" />\r
+                </Component>\r
+                <Component Id="cmp42D4B2D8A4C91F9ED463DA42F3895E27" Guid="1444EE4B-AD59-4C33-9B17-3B21B132DAA9">\r
+                    <File Id="filAD08CEBCD34B84E90701D6248FDB5FA9" KeyPath="yes" Source="SourceDir\translations\qt_en.qm" />\r
+                </Component>\r
+                <Component Id="cmpB92DA15530102518B1B98DBF1DAC135D" Guid="10E7AA9E-7DC7-45E5-A52C-9B33DD7CD157">\r
+                    <File Id="filB05A80E55C2FCBFE9999B66E309F0312" KeyPath="yes" Source="SourceDir\translations\qt_es.qm" />\r
+                </Component>\r
+                <Component Id="cmp7210C3ACD533D6A934BD5977AB16AF71" Guid="8D242F4F-56C4-4BBD-82EF-7EC103AB4445">\r
+                    <File Id="fil4B803C16DFCB1C5EA4D308AFD35049E3" KeyPath="yes" Source="SourceDir\translations\qt_fi.qm" />\r
+                </Component>\r
+                <Component Id="cmp0643210AD0A541FA765CE55469F22FBB" Guid="3DAD67C2-7EBC-4FE2-9375-ED04F44B95C2">\r
+                    <File Id="fil65E050731B55F3764817B70B50D1F483" KeyPath="yes" Source="SourceDir\translations\qt_fr.qm" />\r
+                </Component>\r
+                <Component Id="cmpBB98B3096730FFFE87E708A6291ED47C" Guid="4FC0CA17-91C9-4207-B18F-0D3676A4EAA8">\r
+                    <File Id="fil38E65580D7E2598A2AB22B21A55AE808" KeyPath="yes" Source="SourceDir\translations\qt_gd.qm" />\r
+                </Component>\r
+                <Component Id="cmp3666ECDFA614EA095AF7310AECCD44B7" Guid="A4ADC90F-ED1F-49EC-9595-226C90767318">\r
+                    <File Id="filD812B4BB739798D5C3BF25BEFBFBA938" KeyPath="yes" Source="SourceDir\translations\qt_he.qm" />\r
+                </Component>\r
+                <Component Id="cmpC4FA2671255E7459BD46070D88B76FF1" Guid="427500F9-DE40-499D-AA91-34944AD8AD45">\r
+                    <File Id="fil750345F0F25CC28174362BF42A332BFA" KeyPath="yes" Source="SourceDir\translations\qt_hu.qm" />\r
+                </Component>\r
+                <Component Id="cmp8D644C0D5E80E34EBCB6FC3643C879AF" Guid="FA35CBD4-A861-494E-AABC-76262872F717">\r
+                    <File Id="filF6634E490610EB73E2D852D47B43255E" KeyPath="yes" Source="SourceDir\translations\qt_it.qm" />\r
+                </Component>\r
+                <Component Id="cmpA8DDE2BD9E1AB00C9631DE447CBAA11D" Guid="15D64C1E-B407-4531-9684-A785DAF53421">\r
+                    <File Id="fil493967A00B1FFB8D251F7A844D53A64A" KeyPath="yes" Source="SourceDir\translations\qt_ja.qm" />\r
+                </Component>\r
+                <Component Id="cmp23B3657EE14B0ED7D00B271AFF81187C" Guid="6FC090EB-D1CC-423E-9527-DBDF4E7A4B8F">\r
+                    <File Id="fil5A7EE1B788293556A515EAFFC95747DB" KeyPath="yes" Source="SourceDir\translations\qt_ko.qm" />\r
+                </Component>\r
+                <Component Id="cmp2D105F39980CF93A40405E25755346BB" Guid="79E81F16-F627-4CD1-BB41-5C52A141BE5F">\r
+                    <File Id="fil17B2A1B774381BBA3BF85AF1FFB636AA" KeyPath="yes" Source="SourceDir\translations\qt_lv.qm" />\r
+                </Component>\r
+                <Component Id="cmpC3CB042567F53DD612D309E9CA2E10A6" Guid="CED83F6F-60ED-4321-94B6-7917D1DB232C">\r
+                    <File Id="filC674EE989D2B9719C388F537FC8EFFCF" KeyPath="yes" Source="SourceDir\translations\qt_pl.qm" />\r
+                </Component>\r
+                <Component Id="cmp43D57F897A909679308EFC03849D7E2E" Guid="8E42022B-B599-4ABD-8D30-F5E378FE60FE">\r
+                    <File Id="filADA551AF628F8CBA7E3D06EF76F2713A" KeyPath="yes" Source="SourceDir\translations\qt_ru.qm" />\r
+                </Component>\r
+                <Component Id="cmpF5EA7E37332E95C614BD2D7EAA933E5D" Guid="6516FDA0-5035-420D-AADF-41D1E3735EDA">\r
+                    <File Id="fil49E3CA421049811064612E40502A6147" KeyPath="yes" Source="SourceDir\translations\qt_sk.qm" />\r
+                </Component>\r
+                <Component Id="cmp8DAF13208A863A841AEE7A46E79FB87C" Guid="{A628AE90-BA9E-4CB9-86E0-02E6F2A0358B}">\r
+                    <File Id="filD139779D4ABC7CD76AE12C511550A8C3" KeyPath="yes" Source="SourceDir\translations\qt_tr.qm" />\r
+                </Component>\r
+                <Component Id="cmpA214B663A372012F5DCBA6A5CDAE421D" Guid="790AF12A-1970-4B96-BFAD-925734F604EB">\r
+                    <File Id="fil1380C741E2DBC1F60AA4DC32D66B1E6C" KeyPath="yes" Source="SourceDir\translations\qt_uk.qm" />\r
+                </Component>\r
+                <Component Id="cmpFDB70117D8E46EB188997470A3793B5D" Guid="EA2F54B9-EE49-4426-A2EF-EDDF3CAC83FD">\r
+                    <File Id="fil9085627E4E33630ABCDB62A5CC05B794" KeyPath="yes" Source="SourceDir\translations\qt_zh_TW.qm" />\r
+                </Component>\r
+<?ifdef qt6?>\r
+                <Component Id="cmpF0099CF54A0669D87CC17418C3911422" Guid="{3405A05E-CB26-47D2-9C70-C52167AB8FDC}">\r
+                    <File Id="fil71B9851F396CBCC25B18299EDCBF5FF5" KeyPath="yes" Source="SourceDir\translations\qt_fa.qm" />\r
+                </Component>\r
+                <Component Id="cmpE89BF9966FD9EC39B16BC14B5586E0FA" Guid="{818D0415-1B0D-4C20-A21B-634467D7ABED}">\r
+                    <File Id="filB4B324E9728AE54A2B57A18E91814BC1" KeyPath="yes" Source="SourceDir\translations\qt_hr.qm" />\r
+                </Component>\r
+                <Component Id="cmp7F6AC80CE7E39EEDC9A962EAFADD113B" Guid="{E7491B72-07BC-4811-9AA2-9C90EA586475}">\r
+                    <File Id="filAB0862690AA36917007DE3C425A282CD" KeyPath="yes" Source="SourceDir\translations\qt_nl.qm" />\r
+                </Component>\r
+                <Component Id="cmp9EB4F508A604A4AD35BC558C456111DD" Guid="{035418D9-E833-45B1-B33E-15B8C6EA4E03}">\r
+                    <File Id="filA89B168478D587343A282C54242055A0" KeyPath="yes" Source="SourceDir\translations\qt_nn.qm" />\r
+                </Component>\r
+                <Component Id="cmp09D69EFDCF1B331A37D028C962FE6147" Guid="{BBD6ABD9-DDF9-4AC2-B8C1-FB4CB547B14F}">\r
+                    <File Id="fil3DBA8083389C96E9A47E27507D39EA05" KeyPath="yes" Source="SourceDir\translations\qt_pt_BR.qm" />\r
+                </Component>\r
+                <Component Id="cmpA23046CBC7F8B7E62AF4843CBB72DEC9" Guid="{FE5DC38D-3ABF-406D-8FB7-258119E76380}">\r
+                    <File Id="filCAF43146A9F7B1727B4975BB51D0A045" KeyPath="yes" Source="SourceDir\translations\qt_zh_CN.qm" />\r
+                </Component>\r
+<?endif?>\r
+            </Directory>\r
+<?endif hastranslations?>\r
+<?ifdef vs?>\r
+            <Component Id="cmp10C81658E3C8F63B19EF9D1C26B58363" Guid="{67FF3D4C-A3F5-4C19-8988-CB8111149469}">\r
+                <File Id="fil0F208DFAC8D7EB86E7793690C6ECF15E" KeyPath="yes" Source="SourceDir\Qt5Qml.dll" />\r
+            </Component>\r
+            <Component Id="cmp5B4749C387D33EAF993DDC89F9DBC102" Guid="{E1A0612E-1BAA-4304-A53F-04F9E5FC2C95}">\r
+                <File Id="fil4D31B1A954CDE740FB32D9FFC142CA37" KeyPath="yes" Source="SourceDir\Qt5QmlModels.dll" />\r
+            </Component>\r
+            <Component Id="cmp7F83581B3F7835094D371708381AD391" Guid="{F123314E-1319-4B12-90E9-BB27786C2234}">\r
+                <File Id="filEFD45339E9495CECA6AD117B583ED589" KeyPath="yes" Source="SourceDir\Qt5QmlWorkerScript.dll" />\r
+            </Component>\r
+            <Component Id="cmp33B78CE22F0CBCA4F41C5037A792210C" Guid="{5653E0BB-A956-4565-B2AD-11712CC59571}">\r
+                <File Id="fil87DAAA273949BD9AD08743E49398302F" KeyPath="yes" Source="SourceDir\Qt5Quick.dll" />\r
+            </Component>\r
+            <Component Id="cmpB5571DB4C3153CA848ED1238DBCD4281" Guid="{AAD89C99-7211-4F02-B07D-A89CD18DF9D5}">\r
+                <File Id="fil07F4E15C2DC44A3DA783DB682CA4FF5C" KeyPath="yes" Source="SourceDir\Qt5QuickControls2.dll" />\r
+            </Component>\r
+            <Component Id="cmp4C1BE3EACCCE195C8B88434D5DF77DE5" Guid="{C8585769-1336-4CC6-B8CD-5D9CEE77F079}">\r
+                <File Id="filBE9D9480511F5B210D1FE72753A138A6" KeyPath="yes" Source="SourceDir\Qt5QuickTemplates2.dll" />\r
+            </Component>\r
+            <Component Id="cmp937697C855506045D9FF8DF5588CAFEE" Guid="{DD5543D0-2071-11EE-9B4B-0800200C9A66}">\r
+                <File Id="filE314D75F1A97D0D3A74F77E884E64AD5" KeyPath="yes" Source="SourceDir\Qt5WebSockets.dll" />\r
+            </Component>\r
+            <Directory Id="dirAB1B0C3E5160B20B93DD2AC0AE9E5509" Name="qmltooling">\r
+                <Component Id="cmpFEC2C3E75BBEACDB1A6F4AFB808CD58E" Guid="{64ED0E30-E431-4238-BD39-465D306D9520}">\r
+                    <File Id="fil09C44C978E58830A831DCC0DBAF3AEA2" KeyPath="yes" Source="SourceDir\qmltooling\qmldbg_debugger.dll" />\r
+                </Component>\r
+                <Component Id="cmp8B3D94901F4D369CD276B339B8B00F70" Guid="{554EAB57-00E2-4FCB-87FF-6F07EF14EDC4}">\r
+                    <File Id="fil3E82AB436354230BADBA9DD68B38C2AD" KeyPath="yes" Source="SourceDir\qmltooling\qmldbg_inspector.dll" />\r
+                </Component>\r
+                <Component Id="cmp0B6C2EAABA7EAA14C510F4C9D68A3F40" Guid="{A211CD9B-6E6A-4600-9FA1-8DF999A942AE}">\r
+                    <File Id="fil8CD8981EA2309B09460020F4D9AC275C" KeyPath="yes" Source="SourceDir\qmltooling\qmldbg_local.dll" />\r
+                </Component>\r
+                <Component Id="cmpE75E4FEE65DCA51D044DF4B2AEDCD828" Guid="{625FA227-EB72-4C9E-AD73-8FAC837EE2D2}">\r
+                    <File Id="fil2A36EBC77D39FBFF748DE570C1F7DC96" KeyPath="yes" Source="SourceDir\qmltooling\qmldbg_messages.dll" />\r
+                </Component>\r
+                <Component Id="cmpA8BBE7010AD624EFBA7166925167402F" Guid="{D044FD19-6C91-4736-80E4-D2DA97CD60B4}">\r
+                    <File Id="filA4CF6CC4639F20D9F436C3DE2C32102E" KeyPath="yes" Source="SourceDir\qmltooling\qmldbg_native.dll" />\r
+                </Component>\r
+                <Component Id="cmp00B1DB73ED33EC1DD6658036D3901CB7" Guid="{0FCBA1AB-A703-4399-A518-94B72B55BBCF}">\r
+                    <File Id="fil04B1E67A26C29E877D34C6CDB21D302D" KeyPath="yes" Source="SourceDir\qmltooling\qmldbg_nativedebugger.dll" />\r
+                </Component>\r
+                <Component Id="cmp9F481CFBAECB8853AF07C1D5060B77D7" Guid="{C2D900C8-5DE0-4EFA-A124-A3865FFE4149}">\r
+                    <File Id="filCD389466FEC0CB778B4940AD0354D4A0" KeyPath="yes" Source="SourceDir\qmltooling\qmldbg_preview.dll" />\r
+                </Component>\r
+                <Component Id="cmp93816105EAC51740B98AF9C1041D28D1" Guid="{D8F9A3EE-B929-4418-A3EE-F3228B05A5F2}">\r
+                    <File Id="filE23CD7951728F9C858C61FDBAFD405D2" KeyPath="yes" Source="SourceDir\qmltooling\qmldbg_profiler.dll" />\r
+                </Component>\r
+                <Component Id="cmp5ED50822278A4A846E891B41C07F7B6A" Guid="{FBDFDAD6-886A-479A-8ED1-D04B0028E9EA}">\r
+                    <File Id="fil935ED8A28D14AB20137795A9C347694D" KeyPath="yes" Source="SourceDir\qmltooling\qmldbg_quickprofiler.dll" />\r
+                </Component>\r
+                <Component Id="cmpACA0A17DA54991A194B823CE40D06263" Guid="{8AA14A55-6F27-48E4-9A18-90D618AD8194}">\r
+                    <File Id="fil6E61B7F24F3C3FDB83A90CEE46B2C831" KeyPath="yes" Source="SourceDir\qmltooling\qmldbg_server.dll" />\r
+                </Component>\r
+                <Component Id="cmp3D2C980EC975DBBCBD13A9178DD1A03B" Guid="{8AA853F3-4BAD-4E76-8F48-CB9FC961AE1F}">\r
+                    <File Id="fil0C06BD9370B146FE43DEFD060222FC12" KeyPath="yes" Source="SourceDir\qmltooling\qmldbg_tcp.dll" />\r
+                </Component>\r
+            </Directory>\r
+            <Directory Id="dir6932501040B63B39C33E640A395AEA58" Name="QtGraphicalEffects">\r
+                <Component Id="cmp356189FC3558D542AA8AD79067A42632" Guid="{25EE1F94-BDC5-4BF4-9CF5-4471CB2CDC99}">\r
+                    <File Id="filC50FC92D4690551FD9149804C9524CB6" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\Blend.qml" />\r
+                </Component>\r
+                <Component Id="cmpABC392DBCC9A5A469B77C129899873EE" Guid="{DB29E30C-5362-4A23-BB9D-C1E6B4569979}">\r
+                    <File Id="fil585EA7DCEFEA763B0D34D92C25BF236D" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\BrightnessContrast.qml" />\r
+                </Component>\r
+                <Component Id="cmp740F889D5799A2FF0F93A24058617EB6" Guid="{304F8096-0A76-4DA8-904E-DBC6ECB871EA}">\r
+                    <File Id="fil44DA8C781EB6783323659B96815E6B06" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\Colorize.qml" />\r
+                </Component>\r
+                <Component Id="cmp2799826256C0E84847E94E11C7878018" Guid="{9D087450-2C02-4859-8100-57FD4BE69464}">\r
+                    <File Id="fil79E5BBBAD1145AE67A9F57C761872AB7" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\ColorOverlay.qml" />\r
+                </Component>\r
+                <Component Id="cmp659424F663F96AE55FBA532B4CBB2EC8" Guid="{B9C2D28B-C4EF-45DB-B9BA-E1B9405D76F4}">\r
+                    <File Id="filACFDAF73B5B767483881AB3B6CD4F973" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\ConicalGradient.qml" />\r
+                </Component>\r
+                <Component Id="cmpDF6A99C1FA6B3943D690F21136BA7F9F" Guid="{03C439A4-BE19-49B8-9F1F-972FF89C415D}">\r
+                    <File Id="fil12DD1BF106F38168A5EE7CAFE7622E54" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\Desaturate.qml" />\r
+                </Component>\r
+                <Component Id="cmp81B459304782BB6AC65E381BA13726B0" Guid="{11D487DE-683F-4647-872C-C2E1DAA33042}">\r
+                    <File Id="fil71154C20C340083D34D22ED1F8EFA765" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\DirectionalBlur.qml" />\r
+                </Component>\r
+                <Component Id="cmp3CC0196A91D9C805D66BFFF4EA38EBE7" Guid="{16A372C2-116D-42AC-B431-3B220C37B9EE}">\r
+                    <File Id="fil0038BC20F6506BDA5417B08B88C92BC1" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\Displace.qml" />\r
+                </Component>\r
+                <Component Id="cmp67E65C02C04C3567636F47EFAA888D12" Guid="{2358043B-BBC7-450B-B5D3-8C26E5249358}">\r
+                    <File Id="fil7602377B1CFE07CFCA64BC753B0AE930" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\DropShadow.qml" />\r
+                </Component>\r
+                <Component Id="cmpADC8E894229BBB48BBC40D4F033FD791" Guid="{2EF6B35A-FD9D-42BA-A12C-3763684AC39C}">\r
+                    <File Id="filC46A98B519397B6CE253A5C9F8D5E0E4" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\FastBlur.qml" />\r
+                </Component>\r
+                <Component Id="cmp0D2D3E713559661141BC5DD9F687F29D" Guid="{7556A68C-05FC-44CB-B9FD-56535DE7653D}">\r
+                    <File Id="filA97688F2BA74A175F3E4EB3F9C1BB027" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\GammaAdjust.qml" />\r
+                </Component>\r
+                <Component Id="cmp3C102DD93EC83164DA87E7820364DDAC" Guid="{6C1D3B9D-A04C-4749-84B6-B276EDC28D20}">\r
+                    <File Id="fil5B2FC5A0BED021E222F6738C10D3081C" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\GaussianBlur.qml" />\r
+                </Component>\r
+                <Component Id="cmp4B666BB592E998D07B55C82CF76F5B1C" Guid="{FAC4A87D-2D90-46C6-98C6-D3A5CB94732C}">\r
+                    <File Id="filC5E145603A0E00BF0F22F2B8A67C9A22" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\Glow.qml" />\r
+                </Component>\r
+                <Component Id="cmp4C99F5024E504FEAB151291478878934" Guid="{E0EB60EE-815C-45C3-B5BD-F8D68E099B3B}">\r
+                    <File Id="fil5F059EE301BFBC9CC82E3CCBEDF542CE" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\HueSaturation.qml" />\r
+                </Component>\r
+                <Component Id="cmp84BC34E1E20AAE86B427C9CD3129C4FA" Guid="{0763338E-FBE7-40BC-9591-8F3B5DB576E3}">\r
+                    <File Id="filA56659AFD9BB9DFBFDDA906B787EFCDD" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\InnerShadow.qml" />\r
+                </Component>\r
+                <Component Id="cmpCBA14389AA89D290E481E0F2625854CE" Guid="{406FB202-E76B-4FFD-A1E4-8FEB2F67401F}">\r
+                    <File Id="fil0C15C4F0A2C4CBC263B38BB384C9DA59" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\LevelAdjust.qml" />\r
+                </Component>\r
+                <Component Id="cmp6FFF646F52B60CE854DA0A1B8C47EC55" Guid="{ED6F3223-0243-4539-96C3-8EE89715F16F}">\r
+                    <File Id="fil53C443677D967DDB997226A1954ED5C7" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\LinearGradient.qml" />\r
+                </Component>\r
+                <Component Id="cmp5E04D231070BE2CE70B2672E56C6B9DE" Guid="{04ACB86E-F6EE-4019-BB49-B556AAF08359}">\r
+                    <File Id="filA148939302F08F0A8B0CC79A709DFA15" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\MaskedBlur.qml" />\r
+                </Component>\r
+                <Component Id="cmp8C5CA067EC2AB41E7CAFE253F2EC86A8" Guid="{76E67D4A-0F5F-4AAF-8A78-847BF0EACDB4}">\r
+                    <File Id="fil2921C33FE8BBE6511E3EB7CA8C06115F" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\OpacityMask.qml" />\r
+                </Component>\r
+                <Component Id="cmp8441DAF2F2BC211F1F6FD10DCE7B51FA" Guid="{70CBBCF5-9030-4FE8-98F0-6665ABF0D3D7}">\r
+                    <File Id="filC8B8AEF14AC36FE600581D00FE128545" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\plugins.qmltypes" />\r
+                </Component>\r
+                <Component Id="cmpC3A1EC5089BA6B0798970A12E614BBDB" Guid="{3EC761B0-3826-451A-8D11-DD0B77E965DB}">\r
+                    <File Id="filDDA0AA71F77A367B9A901655165B1B51" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\qmldir" />\r
+                </Component>\r
+                <Component Id="cmp854B1173EACBB218B6AF849A7E8A56D7" Guid="{F879D731-522C-4E76-85F8-5DF0BD32421E}">\r
+                    <File Id="fil587B7775B793D6255A1BB1DE826E6A95" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\qtgraphicaleffectsplugin.dll" />\r
+                </Component>\r
+                <Component Id="cmp7D04EA58DD5BF794EB2D3A75609DEFF9" Guid="{2A98610D-386B-433E-ABC0-C878C1A632DA}">\r
+                    <File Id="fil9044BD6346C5BE02A746FAC271ED7427" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\RadialBlur.qml" />\r
+                </Component>\r
+                <Component Id="cmp688C34D162C3029BDE9A236F29106F11" Guid="{D34CDD08-E05A-4D45-9800-514F9C53AA0D}">\r
+                    <File Id="fil1B3F7EAA750FA63EF84F1FBA40ADEACB" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\RadialGradient.qml" />\r
+                </Component>\r
+                <Component Id="cmp7F9ABA6F59701379E7E54AB2EF4B01EA" Guid="{618C4845-14BB-41FB-8504-F256C0651929}">\r
+                    <File Id="fil89AD529AFFF6D62301719C7BAA8F28FD" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\RectangularGlow.qml" />\r
+                </Component>\r
+                <Component Id="cmp46E93DFCA823AE21F1FD874F13C0A6CA" Guid="{3248C193-B4CA-4AC1-8941-8096A52FE13F}">\r
+                    <File Id="filD9317E9BA692B66CC2C535F5B3A2F3BB" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\RecursiveBlur.qml" />\r
+                </Component>\r
+                <Component Id="cmp2F1F9DE69090BFD2946A7621F2DA410E" Guid="{2040A763-52C4-4BC3-8C8E-0B7F80C709A5}">\r
+                    <File Id="filBE37116DE14EE120D97D6725D27F4907" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\ThresholdMask.qml" />\r
+                </Component>\r
+                <Component Id="cmpE650E3AF80C0073C2B52BC369D45B79C" Guid="{59F7E175-5A48-4A83-9D2E-13A259A30660}">\r
+                    <File Id="fil4590ECDA88EBA09BB76C6180A1F4FA4B" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\ZoomBlur.qml" />\r
+                </Component>\r
+                <Directory Id="dir796AB3D358CFA364C82060B28476B244" Name="private">\r
+                    <Component Id="cmpB62C6E9BF562754AAC86BB52A361231D" Guid="{DD730ADB-7BC5-40E9-B34E-60DBB65391B6}">\r
+                        <File Id="filEC6B5D0DA92369DBBD0CF163C14FDFBD" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\private\DropShadowBase.qml" />\r
+                    </Component>\r
+                    <Component Id="cmpA11A92D9D7D10A9221DD05699B7B1B8C" Guid="{9CE273AB-3553-4CE8-9E0D-40202A50CEDC}">\r
+                        <File Id="fil746D3769B6A878D31F7B39B82F020A30" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\private\FastGlow.qml" />\r
+                    </Component>\r
+                    <Component Id="cmpA9B83EF4B33682EFEA153198F1C44255" Guid="{26AF03C7-77EC-40A4-ACB0-7866EE13492B}">\r
+                        <File Id="fil7DF3A69E24DEEFB702D593D5E4B76C0F" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\private\FastInnerShadow.qml" />\r
+                    </Component>\r
+                    <Component Id="cmp4951B8E17EA0B3A6B265E2C0E893A50A" Guid="{48B858D5-8008-4CB0-B9AA-B3DB33C43E3D}">\r
+                        <File Id="filA527CD28BB0968D628976B78FEB0CC38" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\private\FastMaskedBlur.qml" />\r
+                    </Component>\r
+                    <Component Id="cmp2BD742974C0791D6BD1AE4F7E3646D99" Guid="{E43BE5AA-F4D0-4CA7-A32F-3D807202C8BC}">\r
+                        <File Id="fil56212E247BB6F393FBE83AB4A7EAE2FB" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\private\GaussianDirectionalBlur.qml" />\r
+                    </Component>\r
+                    <Component Id="cmpF8459C652CF5C20ED87F853C1ABE1752" Guid="{D85D4DD2-A83E-485D-A94D-66B892DBBC21}">\r
+                        <File Id="fil39F0CB1500963D3395433DA5DFC9B13D" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\private\GaussianGlow.qml" />\r
+                    </Component>\r
+                    <Component Id="cmpCA0AF56CA87A52053FE61B6ECD156E35" Guid="{6E13F7D4-4D44-43F7-BCAA-A77EB901AF5F}">\r
+                        <File Id="filA28BA0B69A49D99C713AC84E1BE3CD39" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\private\GaussianInnerShadow.qml" />\r
+                    </Component>\r
+                    <Component Id="cmpBC9523950CF52702CB1A519123F9E519" Guid="{6F73A2D5-41FF-4C3A-80CB-05DE74669583}">\r
+                        <File Id="filE0A177DDE81AF18FB3CFE57A0727C02E" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\private\GaussianMaskedBlur.qml" />\r
+                    </Component>\r
+                    <Component Id="cmp302F033AE251BD62E648805EFB1BB536" Guid="{CDCAB63D-67AE-4A62-8FB1-9C0151617C46}">\r
+                        <File Id="fil51BFE00C51A89DC3E74DC04564BCB18A" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\private\qmldir" />\r
+                    </Component>\r
+                    <Component Id="cmpF3F704D8F2236A0A62B980FA41D3CB5E" Guid="{2CB437C9-31D4-478A-8BFA-9D8EBCD368DB}">\r
+                        <File Id="fil09960BC83EE77CD38744C380783E9547" KeyPath="yes" Source="SourceDir\QtGraphicalEffects\private\qtgraphicaleffectsprivate.dll" />\r
+                    </Component>\r
+                </Directory>\r
+            </Directory>\r
+            <Directory Id="dir898F47F1C6F351486C9AAE4005F1B49F" Name="QtQml">\r
+                <Component Id="cmp00AC1A2BE52EA04246F38C5F0B22A9D0" Guid="{DC866072-ECA9-4CA2-9613-C25D77760BE7}">\r
+                    <File Id="filE33FDBE48E6FC8479D62000A1D4DB79C" KeyPath="yes" Source="SourceDir\QtQml\plugins.qmltypes" />\r
+                </Component>\r
+                <Component Id="cmp0A8C093EE03780F9F7823442F10A6C82" Guid="{109B01C3-36B5-4130-9A37-ACA89A7BB868}">\r
+                    <File Id="filFF531EAEA986913D79F95FFBCB9A3DD8" KeyPath="yes" Source="SourceDir\QtQml\qmldir" />\r
+                </Component>\r
+                <Component Id="cmp44B6838E73CD47A3F0061ECCE2EB48D0" Guid="{29B239A3-D455-40DA-8A21-F9AEAAEAA42B}">\r
+                    <File Id="filA5A3A998AB34A694D3B664958BD2376A" KeyPath="yes" Source="SourceDir\QtQml\qmlplugin.dll" />\r
+                </Component>\r
+                <Directory Id="dir48A5FBBF91E1C202003BAC3F8CB3BF51" Name="Models.2">\r
+                    <Component Id="cmp57A7D8DDBA7FB780E38A3CF0C3DE50F6" Guid="{FC1A6BF2-A27C-4AB4-BC53-31092E0481A9}">\r
+                        <File Id="fil6BB08B36A3E11C569382D84D1E88C9B4" KeyPath="yes" Source="SourceDir\QtQml\Models.2\modelsplugin.dll" />\r
+                    </Component>\r
+                    <Component Id="cmp74FB488898EE30854670C634A6CA57CB" Guid="{928C1715-2F0C-4BA0-9CEE-8DA1D740D679}">\r
+                        <File Id="filBEE366112102E123818634DA4D2DD766" KeyPath="yes" Source="SourceDir\QtQml\Models.2\plugins.qmltypes" />\r
+                    </Component>\r
+                    <Component Id="cmpFF41000DFB8364F307DEC40444A8ED49" Guid="{0E33686C-EA52-43E3-83D4-AF793E79EA25}">\r
+                        <File Id="fil4EF76584838408469F9A719687A6816D" KeyPath="yes" Source="SourceDir\QtQml\Models.2\qmldir" />\r
+                    </Component>\r
+                </Directory>\r
+                <Directory Id="dir7F50D8639A90BC7F159C74A902C07A8A" Name="StateMachine">\r
+                    <Component Id="cmpE8348F57B7F7ED3E102061755EA59678" Guid="{2BFB9277-FB81-4D69-A8D4-94BF7B3492A6}">\r
+                        <File Id="filEA848B17E993426219C6780A6AD0EF90" KeyPath="yes" Source="SourceDir\QtQml\StateMachine\plugins.qmltypes" />\r
+                    </Component>\r
+                    <Component Id="cmp789571C85C4C76BD48E9A471271B5128" Guid="{5ED22F6C-7FCE-4947-9538-4C89ECB8F4CF}">\r
+                        <File Id="fil4203D832E588C73EC48309F8D3AC2BFE" KeyPath="yes" Source="SourceDir\QtQml\StateMachine\qmldir" />\r
+                    </Component>\r
+                    <Component Id="cmp07E1D3988D9259288197F71CC0B0559A" Guid="{5D21407C-F3C3-428F-B397-9C4E0D666EF5}">\r
+                        <File Id="filFC54BEA29869B52A91A0A804A6843B4A" KeyPath="yes" Source="SourceDir\QtQml\StateMachine\qtqmlstatemachine.dll" />\r
+                    </Component>\r
+                </Directory>\r
+                <Directory Id="dir38701E48D40F53928079A0277B1BE418" Name="WorkerScript.2">\r
+                    <Component Id="cmp91EB04F6C5AFAF292A0BFCE3570DCBFE" Guid="{4D931278-DD8E-4ACA-B8E0-8436B303B917}">\r
+                        <File Id="fil3AD6975608C156FBDC0A714EFD2E7779" KeyPath="yes" Source="SourceDir\QtQml\WorkerScript.2\plugins.qmltypes" />\r
+                    </Component>\r
+                    <Component Id="cmpDA0DD041411FA5AC356FAD21EBB0BFCC" Guid="{04B2D75A-13D6-47E2-BCE4-0E230B94AE5F}">\r
+                        <File Id="fil7757938E9EFF9E82F19FF17128644874" KeyPath="yes" Source="SourceDir\QtQml\WorkerScript.2\qmldir" />\r
+                    </Component>\r
+                    <Component Id="cmpC0C294F18C416B2F09D563A7D2F171C0" Guid="{0B5FC87C-D82F-46BE-AB18-E10BAC7566E4}">\r
+                        <File Id="fil585107DFD90EACA350C802AFD64551B4" KeyPath="yes" Source="SourceDir\QtQml\WorkerScript.2\workerscriptplugin.dll" />\r
+                    </Component>\r
+                </Directory>\r
+            </Directory>\r
+            <Directory Id="dirA71A8D5C38BE16878A4EB5F3B4E1599D" Name="QtQuick">\r
+                <Directory Id="dir1CF0A9FB12A9B3F0BCC43F64CAB69841" Name="Controls.2">\r
+                    <Component Id="cmpBBC07C7AA9E1D56538F0EE5BC883A106" Guid="{18653B7C-05E4-485E-9C62-31D4F2664E1F}">\r
+                        <File Id="filC6250BEA59F89937D08252CE2344D671" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\AbstractButton.qml" />\r
+                    </Component>\r
+                    <Component Id="cmpA2AD6F575E441894DDF6301F935EF3E7" Guid="{6B3F6F40-74AE-43F1-8EDC-97CC40267B5F}">\r
+                        <File Id="fil7E4C91C7F6D6DE165F5E68148D5B85FA" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Action.qml" />\r
+                    </Component>\r
+                    <Component Id="cmp346061900FCE515D6B32CFCD7971325C" Guid="{AE4849A3-BD9E-4D75-84A4-F78DE4AE50E2}">\r
+                        <File Id="filB221D15EF9AD3699086FE43E3C355121" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\ActionGroup.qml" />\r
+                    </Component>\r
+                    <Component Id="cmpE5AC86A748B88CA71F59F050376D4BC8" Guid="{3D56EB30-C37B-4040-9341-53A74B36291D}">\r
+                        <File Id="fil8B70502E8A7420E0F20E1C107A446561" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\ApplicationWindow.qml" />\r
+                    </Component>\r
+                    <Component Id="cmp26A45023E109A672FAA78FE5C4F827F4" Guid="{F0BE556F-358F-4AA8-B311-8102DE486887}">\r
+                        <File Id="fil2FB69D0E22B7CC46B57FAB19D2931318" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\BusyIndicator.qml" />\r
+                    </Component>\r
+                    <Component Id="cmp92014B7FBE8B02881A6A53384BDCD1C4" Guid="{65C1C88B-5B3E-46E2-965C-DCD4426EFCA5}">\r
+                        <File Id="fil29272699B19CB5F1A2B83C23066B00AC" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Button.qml" />\r
+                    </Component>\r
+                    <Component Id="cmp372134AB6EF24BAC7497CC47466F0727" Guid="{B8E80B2D-19A3-434C-A380-DA262254F876}">\r
+                        <File Id="fil9BAAB7742819691F86B3837FCA161A6E" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\ButtonGroup.qml" />\r
+                    </Component>\r
+                    <Component Id="cmp17FB129D73D5BCA38BEC406E5A7A9853" Guid="{B4EEB089-7AA1-4EAE-BAF3-13837AA5AA71}">\r
+                        <File Id="fil40C33AA9302346640FF8F3431BDFCC16" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\CheckBox.qml" />\r
+                    </Component>\r
+                    <Component Id="cmp59BA1197723375EE2B1A8FC0971407A8" Guid="{C256DDB3-D9F8-47D2-8702-9290C36AB4E4}">\r
+                        <File Id="fil1D4756FDB82E5F7C242AC3DC37F9B2F9" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\CheckDelegate.qml" />\r
+                    </Component>\r
+                    <Component Id="cmp9076C49961F4F0F4F5B4F65EF0CE7925" Guid="{6F747374-523D-4121-B79B-E0062B08B433}">\r
+                        <File Id="fil6A4AAD9D20B45D7D2DA5707F1390AF0C" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\ComboBox.qml" />\r
+                    </Component>\r
+                    <Component Id="cmpCBF4FE8555DE1D61528AF79EA3C5870F" Guid="{26599BF7-BCE2-4202-8B5A-F009B999646C}">\r
+                        <File Id="filC5D80E96EA90020D98A1BA9C7A32D089" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Container.qml" />\r
+                    </Component>\r
+                    <Component Id="cmp63B837CFDE5F5C8ADC92D6A80E367621" Guid="{F67247A2-2EA5-450D-87C4-AED6C9A8E069}">\r
+                        <File Id="fil82056DDF3A22A3C956EB50AD96DE681B" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Control.qml" />\r
+                    </Component>\r
+                    <Component Id="cmp73FC7C1B9421F67443A7CA5E03C6F59C" Guid="{2225B539-878A-40DC-986D-742CFC262E18}">\r
+                        <File Id="fil00CD17A8DC26B3D4E21AA1AA951F3910" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\DelayButton.qml" />\r
+                    </Component>\r
+                    <Component Id="cmp24D33989EF8148F7E8E801168FA75D02" Guid="{88F2FD96-6F60-460D-BBEC-F0B0BE0CE94B}">\r
+                        <File Id="fil0F8C8B52CEAB25AD4FD610A146431D1D" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Dial.qml" />\r
+                    </Component>\r
+                    <Component Id="cmp781F94BE6B67AAB9F2DD4A6865F67043" Guid="{12F45E61-A438-49AD-890E-BF5AF913BAED}">\r
+                        <File Id="fil9429984A3AD047F645C68743E4853D3E" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Dialog.qml" />\r
+                    </Component>\r
+                    <Component Id="cmpBF8FE1999E64BE53CD0FFE028F2BA8E5" Guid="{C825307D-6856-48E9-952F-D8DD4371E086}">\r
+                        <File Id="filB66714CD3CC8044DE03BFB0C2998771E" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\DialogButtonBox.qml" />\r
+                    </Component>\r
+                    <Component Id="cmp508D7E9DE20A057C80595708342CCD11" Guid="{79285B58-ED37-4B36-81A1-2F01EE9A8D30}">\r
+                        <File Id="fil4951F0E0C838BCE74B6A4FB412B33C39" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Drawer.qml" />\r
+                    </Component>\r
+                    <Component Id="cmp814DB5A7174FAD3D492689B3CFFD4FC6" Guid="{C5568152-77D0-43EF-BB9D-0066F205825E}">\r
+                        <File Id="fil26FF980D9A1E103DAEF78DA89A8B39AE" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Frame.qml" />\r
+                    </Component>\r
+                    <Component Id="cmp9FCA0D048C5FEDEF35CFDCF4220D148D" Guid="{552D678A-1984-4C88-89CF-42371097B8C7}">\r
+                        <File Id="fil7EC20FB2AF608FD4D48A9F19F1B92A7D" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\GroupBox.qml" />\r
+                    </Component>\r
+                    <Component Id="cmp8302E43F7B461A53125D24EBAB2C8BBA" Guid="{9908593A-2DF6-4B1B-844E-22E9D4AF8FC4}">\r
+                        <File Id="filA490553F561E23B426ED5F47B779E827" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\HorizontalHeaderView.qml" />\r
+                    </Component>\r
+                    <Component Id="cmpDD4764A31C5966C382C9FADCF2747DAE" Guid="{A8C30115-60B3-4519-BCC1-668717555DB0}">\r
+                        <File Id="fil20BF35D7A8C7028231C16A4F8A8B8E7A" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\ItemDelegate.qml" />\r
+                    </Component>\r
+                    <Component Id="cmp4DC6229A880E85A8EE64B69A175EB7F8" Guid="{59036E4B-ACD7-4733-B14A-EBAA93E714E8}">\r
+                        <File Id="filADF106B97E56C6627AF27930AA08B829" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Label.qml" />\r
+                    </Component>\r
+                    <Component Id="cmpC3E4C72480B21745D1D55462CD128148" Guid="{B1B90609-C99E-4B1A-8B10-13BE83B179BB}">\r
+                        <File Id="filCDF998E5B0CD0C7D86DEDA98E71F235F" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Menu.qml" />\r
+                    </Component>\r
+                    <Component Id="cmp9C2C5E7D9EAC8AD7E9FB8BA9B0147BAB" Guid="{BB4200E2-ADED-44D8-A1A1-31A0E8707754}">\r
+                        <File Id="filA5709A532A6415760B623CBD689B3F88" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\MenuBar.qml" />\r
+                    </Component>\r
+                    <Component Id="cmp66ADBF15C183C1EF05BA59EBF57DC04C" Guid="{592C458A-E1E3-4735-A22F-92B5FEECBA7F}">\r
+                        <File Id="fil9818915B5CDD8FDC7B9B723C438DDA64" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\MenuBarItem.qml" />\r
+                    </Component>\r
+                    <Component Id="cmpB85DB993D3D58E717F4EC2A2A5AC4130" Guid="{604A7793-4EC9-4F5F-9BAF-DE22555BFF3A}">\r
+                        <File Id="filFAE639B09FB48AA6FC953557339B53EC" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\MenuItem.qml" />\r
+                    </Component>\r
+                    <Component Id="cmp93014FB27329F9718833E30A8DCE2F23" Guid="{EF1D6D21-BDC6-4DDF-9BC0-875EC3FAE40B}">\r
+                        <File Id="fil09A4B643E4AEAFC3F22479FBC0E09388" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\MenuSeparator.qml" />\r
+                    </Component>\r
+                    <Component Id="cmp9D0CC60826A95D19D52A3A08FB44155C" Guid="{DFE9585E-6220-430F-9F1D-1B2130D88FF1}">\r
+                        <File Id="filDFFEC8756E4A921EAD31C7CD841BAC7B" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Page.qml" />\r
+                    </Component>\r
+                    <Component Id="cmp06BD88C78B72DD375F0FD8DC37812A7A" Guid="{9DE25AD5-FA94-4DFB-B129-0FFBF312E353}">\r
+                        <File Id="filDC61FDA5B2042E021FC28DFA1C464ED4" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\PageIndicator.qml" />\r
+                    </Component>\r
+                    <Component Id="cmp6577B61F7B803092F263CE9BD76D7B19" Guid="{D6D9670E-CD58-40D4-B772-C8AC087E74B3}">\r
+                        <File Id="filD74C331BEAD78D4B70AE295DE481F56E" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Pane.qml" />\r
+                    </Component>\r
+                    <Component Id="cmpD880E6EDC6A23CDFF94AB5E0A0018A10" Guid="{A3989AE9-2FD6-470A-B8CF-78D908DBA2E6}">\r
+                        <File Id="filA2017FDECC21B1BD1F3A42BBA6781F78" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\plugins.qmltypes" />\r
+                    </Component>\r
+                    <Component Id="cmp00D081BB0762FEC39933D1C2960FC831" Guid="{ED79D960-6E3D-4B98-91D1-376FFD6D0C55}">\r
+                        <File Id="fil3BF8126A47717D54F2FC8B0E9EE88747" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Popup.qml" />\r
+                    </Component>\r
+                    <Component Id="cmpD69A2401CBD6D26E44BE1271C8905A24" Guid="{0231D127-E285-4F07-8AB2-AB26F8850A75}">\r
+                        <File Id="fil7827CED2892E4BD2A7216EE8DC487134" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\ProgressBar.qml" />\r
+                    </Component>\r
+                    <Component Id="cmp0B7DD63BE81BC1EA8DB09A99C12CB156" Guid="{A7BE0958-70D3-4C14-82E3-FD911E5B2EAF}">\r
+                        <File Id="filBF429532FD2FB957D749DBFBEC0C1781" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\qmldir" />\r
+                    </Component>\r
+                    <Component Id="cmp1EC09C5C4D6CE477EAA2BE5A3798E1CC" Guid="{BA7E711D-1F6E-43CA-9957-C06AF625F0F8}">\r
+                        <File Id="fil8A3E57CDCB38EA2957526857F4BF7833" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\qtquickcontrols2plugin.dll" />\r
+                    </Component>\r
+                    <Component Id="cmp577EBA2061AD5D464AC5B629EF1A63DE" Guid="{248F67F3-A6F9-4619-B355-BB41E97401D1}">\r
+                        <File Id="fil2CF3A6D6E008AC1C946D645F7484E0AA" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\RadioButton.qml" />\r
+                    </Component>\r
+                    <Component Id="cmpFD9033FF82B141429428B759027C0ECB" Guid="{E0382F5D-D17D-4955-81ED-046C720C45AE}">\r
+                        <File Id="filBBA1B0E513D4C6E93929431510FFC51E" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\RadioDelegate.qml" />\r
+                    </Component>\r
+                    <Component Id="cmpD9A7A9076E4844ACEB4385E651C6FC27" Guid="{8B8F1FD8-F041-431E-BC04-94955681EC8B}">\r
+                        <File Id="fil92EBFA0D398D93C208D55E0F2DE5C412" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\RangeSlider.qml" />\r
+                    </Component>\r
+                    <Component Id="cmp952D38FA5543D46E8B0B6CAC867E6D19" Guid="{6E73EE8C-FFF9-4D5E-80E4-49DCE2750DD8}">\r
+                        <File Id="fil3BC533F32E89455AE3C47B34F2D990BC" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\RoundButton.qml" />\r
+                    </Component>\r
+                    <Component Id="cmp8360F4A2F8333FCC0A95F36461DAC0AE" Guid="{6FEA34D3-39E4-4BC0-85DB-ECDF94B77477}">\r
+                        <File Id="filACE9F613C573BFC25A9A0E48B6C143C3" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\ScrollBar.qml" />\r
+                    </Component>\r
+                    <Component Id="cmpA8C48D6004583C73A28027E805ECF241" Guid="{FB1C3A2B-9330-4EA7-A34B-25612A2C2E6A}">\r
+                        <File Id="fil7BD43F79B053411805FC22E0BD5D488F" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\ScrollIndicator.qml" />\r
+                    </Component>\r
+                    <Component Id="cmpF0F52E0AA644EE5B8A4212507A2A9632" Guid="{40D7143A-FFDF-44AC-ACD4-00A6A5464BE7}">\r
+                        <File Id="fil89005ED22EF40F5C060D464B8096616E" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\ScrollView.qml" />\r
+                    </Component>\r
+                    <Component Id="cmp1F2A598B5F572A95F619F914ADFDFD01" Guid="{3536777D-FF8B-4EEA-B93C-5EE1301E5039}">\r
+                        <File Id="fil1C7BF5B291EA31328091F13B845FED6A" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Slider.qml" />\r
+                    </Component>\r
+                    <Component Id="cmp894CC6997FDC49D32993AF049D19661B" Guid="{30F4723C-B159-4C29-9EA0-C8470CDC3204}">\r
+                        <File Id="fil298110AF9B2ED52166A7A0ACE2C9EEE9" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\SpinBox.qml" />\r
+                    </Component>\r
+                    <Component Id="cmp45A86D8F1E2A0DB9085F1C386BE8C441" Guid="{361C717A-E7E1-48E2-B5FD-E934ADF6C5F5}">\r
+                        <File Id="fil7E7C8E62F5F6317023CD5DDFBE502AA0" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\SplitView.qml" />\r
+                    </Component>\r
+                    <Component Id="cmpD7F91C9BF1B37E7ECEFC803745680533" Guid="{2972CE14-8B1C-44AA-B8DE-80551FE96662}">\r
+                        <File Id="fil6479652BE44E448F123964A1BFFDC025" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\StackView.qml" />\r
+                    </Component>\r
+                    <Component Id="cmp6C7380F6F9D3432EF0FF68155BE5B7EA" Guid="{D8A7D60B-63A0-4AD1-950D-055177620665}">\r
+                        <File Id="fil2616AA919B65C7E1223B8EEAE8A0A520" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\SwipeDelegate.qml" />\r
+                    </Component>\r
+                    <Component Id="cmp163F0198648660C0C4DED70616CD3A50" Guid="{1FBF4796-E2E8-4ADB-A545-4BF2807D20B1}">\r
+                        <File Id="filCF410250FBBD03B7BDF88E86CDE7F5FA" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\SwipeView.qml" />\r
+                    </Component>\r
+                    <Component Id="cmpF5CA7A4D3DB71A67C3E4BC87D9A553DE" Guid="{81DD052B-A012-47F6-B62F-EA0E79AC7846}">\r
+                        <File Id="fil263B8E09CAABE937FBEBF1178E2C8264" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Switch.qml" />\r
+                    </Component>\r
+                    <Component Id="cmp2599D94F81ED612C05479E9A8F58A189" Guid="{7DB969D4-59FE-4E04-8D11-E3BDEFB46738}">\r
+                        <File Id="filA4030299DEE7EEF863C5BDC4ADA0363B" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\SwitchDelegate.qml" />\r
+                    </Component>\r
+                    <Component Id="cmp9557C3464EC921CAC1A43D77B20BA92D" Guid="{DB31EB03-0A74-4002-B530-18B87D842D6D}">\r
+                        <File Id="fil13E41D7725EECC41EB76F1C55FB37034" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\TabBar.qml" />\r
+                    </Component>\r
+                    <Component Id="cmpFDD06D370422F04997F37CDCF9077AAF" Guid="{8EAE7AAA-839E-4BD7-A7D2-135517DD05B0}">\r
+                        <File Id="filC1B3E77F09168E169AC46F312631A9A5" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\TabButton.qml" />\r
+                    </Component>\r
+                    <Component Id="cmpC5C6ADF92BCA401BFD4AB6B8DF07ABE6" Guid="{E2A3B225-AEDA-4573-B8CD-F3F84AE9A8A0}">\r
+                        <File Id="fil8804492F60CD4E2F68C71219E02292EF" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\TextArea.qml" />\r
+                    </Component>\r
+                    <Component Id="cmpC7F75CEAF1EB837B6998ED5DC6550404" Guid="{01405B24-2CEB-418E-9B59-5F96A26C9D50}">\r
+                        <File Id="fil6F19FED3B4412AE010061DF7D4BD3DA0" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\TextField.qml" />\r
+                    </Component>\r
+                    <Component Id="cmp4E0B2D6970453AF152BA060236BD8B71" Guid="{5ED9BA2B-AD36-4E01-9655-9EE6A765360C}">\r
+                        <File Id="fil340E1F0612644D4DC4EADFE9E87F24EF" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\ToolBar.qml" />\r
+                    </Component>\r
+                    <Component Id="cmp3A32B8F13C49569A0A4AD1D11045BE3A" Guid="{A7D20B7C-B367-4014-9FDE-1DFBDCEB6C9B}">\r
+                        <File Id="filFBEB8E9016BF58F623173CDBF130428D" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\ToolButton.qml" />\r
+                    </Component>\r
+                    <Component Id="cmp6D11E1D44ADD3049CE735F324F7F4521" Guid="{A972070C-BE88-401A-8BCC-8C6F4C4D9071}">\r
+                        <File Id="fil18978F440E9B6E6F62CE45EEC31C0F8F" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\ToolSeparator.qml" />\r
+                    </Component>\r
+                    <Component Id="cmp6763D9C183B24CD6659B616DD72994DA" Guid="{BD007679-0CF6-49ED-B858-D57788EFEA1F}">\r
+                        <File Id="filB20E33BEBD71D625CEA0199980F77F55" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\ToolTip.qml" />\r
+                    </Component>\r
+                    <Component Id="cmp0FEA3716333AE56EBB72D727432DFB0A" Guid="{1BEC8363-459E-4097-BCF8-724A52CCFD53}">\r
+                        <File Id="filC8F8B2D82FDC1ED4350CA76D702241C7" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Tumbler.qml" />\r
+                    </Component>\r
+                    <Component Id="cmpF77FF7EB150083CBBB82946EC3F849C5" Guid="{77DC42B7-660D-4EB2-9553-1A8F3A054CB6}">\r
+                        <File Id="filD758C10071B91117472250A6D6E5C07A" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\VerticalHeaderView.qml" />\r
+                    </Component>\r
+                    <Directory Id="dir035355762BDDA6A88B77A84B585AD0EF" Name="Fusion">\r
+                        <Component Id="cmp8693A45A9517FDC707611CD9592AD887" Guid="{FE1A6B72-4416-4058-B8DE-FD0ADA5E00AB}">\r
+                            <File Id="fil7A76573ABBE1FDF6842F852511E36ED4" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\ApplicationWindow.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp22009CC98E7BCD73739238DBAD70F5FF" Guid="{EE86CB3D-757B-4591-8A4A-71DFB47EEC98}">\r
+                            <File Id="fil4D6655B7651D7B655CDC465A96569C20" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\BusyIndicator.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp006248D9C946635CD2C0D21D711BAF97" Guid="{71322A32-8BC1-4BC3-A2D3-43877F86E8E4}">\r
+                            <File Id="fil4677B542408030BF8A7E55B148B934EA" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\Button.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp8F4980EC013CDADC9677179C1B5FB4DB" Guid="{9C60553B-BC7A-4E02-A56A-58833FC75143}">\r
+                            <File Id="filF44FA16699EA690B580F758E29738AAA" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\ButtonPanel.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp7CB5E11B511D00866734F2A59242071E" Guid="{AE591101-9987-4988-BB53-276AC7E46073}">\r
+                            <File Id="fil3CF03534ACD9304F06C0597102C48388" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\CheckBox.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp15339C0E193CBE17FB6CB89DA971471D" Guid="{E5A1CF93-FF79-4BFF-9429-876C326CD587}">\r
+                            <File Id="filB933AB67B806FBFAEE5B4C6EEA24FD2A" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\CheckDelegate.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp7A634DCF2DA1EDFF9FBAF699758570C3" Guid="{E8EFCE73-1AB6-466D-AD08-C8873EF95506}">\r
+                            <File Id="fil3FDFED2EAAAED2687815DA9661B0C595" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\CheckIndicator.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpAA64F717D23C1375E79BE28E5A3DE005" Guid="{EF2F8A2E-4BBF-401E-A1B7-041B7329F812}">\r
+                            <File Id="fil95F73162924608F461FCFA52B66A0A6E" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\ComboBox.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp14F754092A68F3FE755963981761F031" Guid="{E0E26331-F0B7-4DFF-89E5-8894EF16DAC2}">\r
+                            <File Id="fil96C211B2F831A30A5AD62EDC6CB0AE86" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\DelayButton.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp8DBD1F81C17E5D01D819E619D30E83CA" Guid="{242B321E-98A7-4685-84E9-8C8BD6CAFC92}">\r
+                            <File Id="fil1CC8736C80CF69BDCE8C8B989F215658" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\Dial.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp9A97912EFCCFD956EF8F155A81CB494F" Guid="{D2C607E0-8E4B-4F55-B60B-024D8BC34882}">\r
+                            <File Id="fil9912627C86943446F991D141E64E84A3" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\Dialog.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp37BA96169B5087F3D2C6E769FF5E9270" Guid="{B5153F7B-2A7E-4FB2-B7EA-C68D043B4F76}">\r
+                            <File Id="fil9D217918F4DE6A7DE53939E61C810E92" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\DialogButtonBox.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpE74780CCB3558FB4C37DDE65264DC1F4" Guid="{4F45062D-232A-4157-ABD7-9351742C3994}">\r
+                            <File Id="fil404168979C602A6C3E2A8E77521843CF" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\Drawer.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp86D5D0E9BB2DE2BDCC6CF459CB7C4E01" Guid="{6FA92936-097C-41B0-A5C2-E7DC100A7908}">\r
+                            <File Id="filCFFF61300EF689F96CFDD9B3A8F7B83D" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\Frame.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp3951161EB256315C232D7C2B211A5D42" Guid="{FD64BFEF-8970-459F-843E-9D4E07A8DEBD}">\r
+                            <File Id="filAC8A24BE65028BFAF11984F7DB0B168C" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\GroupBox.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp6CAF0B6CD8ABE85E62A160036392A2CC" Guid="{88F69974-2FD0-4D7C-8E3D-36CD596B9459}">\r
+                            <File Id="filB0D55ECBE8B840F45E54296721149C79" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\HorizontalHeaderView.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp55E217C43921E96FF82040712469E0F3" Guid="{D471DAE1-7C90-4202-91FB-0BE5C9E77506}">\r
+                            <File Id="fil2D7C328BD2280D7E0E3ABE5468F4CEDD" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\ItemDelegate.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp57277D9EB23963010750499ED2F541B8" Guid="{1096E01B-7229-42C2-A357-E3C45DA54168}">\r
+                            <File Id="fil3FAB3A32AA7A668FFB5482A34769E564" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\Label.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp651F7330940689374F3F4500C1F20484" Guid="{0D729967-F2B6-44F7-85D7-461595625C6E}">\r
+                            <File Id="fil2E6E1F91E7A60FB5D16E4C853B247E7A" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\Menu.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp02C5C41CDED7F94E885A4A2A28EC3600" Guid="{F4E65E26-2552-46BA-AF61-C547429D8F01}">\r
+                            <File Id="fil46C51416AFB762FFEE81E338F1212F54" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\MenuBar.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp95C32716B4F28A90B070E08CAC81BAC4" Guid="{7B371B0C-5AD7-4E93-B5E1-173CC26FD81C}">\r
+                            <File Id="filDA216EA9FC2E71295498C5082FABF4AF" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\MenuBarItem.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp2CEB59611FF3F33F7AEF7D2EAAE0CB05" Guid="{2F5B5AEE-CA5D-4579-AE12-C90238707035}">\r
+                            <File Id="filCA0597907941481E694D27A9151A67F3" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\MenuItem.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp8D1031BFABA660F80B7793BB3761EB75" Guid="{E7361D28-855B-4961-8AA5-71A3E6BD7F08}">\r
+                            <File Id="filD9334919AAFD0FAA024307105F6E2274" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\MenuSeparator.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpB52CD2CEE9160F5F67372170543F85B4" Guid="{3D125A08-8FBF-4F5F-8632-A08FD9F18975}">\r
+                            <File Id="fil549E037AEFA7D53E8ABEFA528585B365" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\Page.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp909217E4E53799CCE36ED3F7B817DF15" Guid="{7CDFBC69-1743-4B4D-9D6D-CC727453AB80}">\r
+                            <File Id="fil622B076063785FD360184B7CCE9C1963" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\PageIndicator.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp88C4BDBD3D48F447FF34FD6DF1FC3C41" Guid="{8249DEF8-38C4-4F19-974E-6E455FB45D9F}">\r
+                            <File Id="fil258E3E262CBA06FD489EBCE314036F9D" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\Pane.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp6D134750A96C36CCE37CDAEAC146A810" Guid="{AC624DFB-C752-43D2-B643-A18625D081A8}">\r
+                            <File Id="fil1872A6AD9CAD77362D7D04C024F76B25" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\plugins.qmltypes" />\r
+                        </Component>\r
+                        <Component Id="cmp17431F800533AAD492A8DA91C537AEE5" Guid="{CC784F75-F543-4123-9E10-3097581F6EE6}">\r
+                            <File Id="fil5955CD9FB6B733D9B586B52CCD59F161" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\Popup.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp5651D28BAA4D7517AB64D608440B8947" Guid="{0E1DA31F-123B-411E-A310-910106CF7103}">\r
+                            <File Id="fil1F6E4572AA5C15F6F2F86B70AD845F39" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\ProgressBar.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp1FC8B77EF51F392FB0F6CD269098431D" Guid="{921782D1-C60C-4012-B36C-D44FBFB1A9A1}">\r
+                            <File Id="filDEC4823160DF80F031FFEDD16C5FA73B" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\qmldir" />\r
+                        </Component>\r
+                        <Component Id="cmp403E7038C7324A61D8727DC1D939E547" Guid="{F851CBDB-DEF2-4DA0-AB32-F038CA4D94F8}">\r
+                            <File Id="filD279E9A3D628C42CFDEC99CA9E5A2F3D" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\qtquickcontrols2fusionstyleplugin.dll" />\r
+                        </Component>\r
+                        <Component Id="cmp98D46B3DC15B713ACF5F53B34EE13068" Guid="{8C570892-F30F-4CB2-AC3A-47382EC6B368}">\r
+                            <File Id="fil50FF1AE0E3D989A4CA249AAF9F8A7B26" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\RadioButton.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp82998B9C0403FF9F31583386A05AA9BB" Guid="{74DB260E-F5DF-4426-BDFE-14F1B954C661}">\r
+                            <File Id="fil1A72CA8DAF4CE929C79B9574D2F30382" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\RadioDelegate.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp0D0B1F4459593A8B3D4A1BCBA139D98F" Guid="{FA5F9B73-E11E-4A44-8F7F-1C51E7F48E5D}">\r
+                            <File Id="fil46018A40ED31116940522AF13C6F1FBA" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\RadioIndicator.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpF083A0EDD7D82B7EEFA116A49700462C" Guid="{809F9508-ED4F-401C-89F7-D01F181560F7}">\r
+                            <File Id="fil3E28D7DC0567E0CD67D51AB0878E1837" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\RangeSlider.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp094723DF4DF9079CABB07B397F53FC14" Guid="{75821D85-CF6B-464F-90B3-72300BC5245F}">\r
+                            <File Id="fil0C857F8E3C8E3F5D9E839C9D1A2D1BE6" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\RoundButton.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpE46F1304BE1F25953FA1502338B46B6E" Guid="{E57FB784-7FCA-4108-8A90-43C9DAC92E74}">\r
+                            <File Id="fil0A615F671CBAFA25B83B6DDC1A57BE65" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\ScrollBar.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpF34990BAAB80CEB1B1FBA3F75A8BB18E" Guid="{BA75016B-BCEE-4639-AC14-960BBBA684CC}">\r
+                            <File Id="fil4926BC97FC5BF303193B003042C8161D" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\ScrollIndicator.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpF8164C6D715D603507F60EA7662104BB" Guid="{3DF8922C-C3E9-49D8-A369-F8D21BFB1D94}">\r
+                            <File Id="filBAF585899E98CD52881FF18EA3FB54F6" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\Slider.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpBC49C3C8C0D1C882B8E4BFA5DDB84097" Guid="{7A79B435-9227-413C-9444-137D88C7B937}">\r
+                            <File Id="filF5CA1B27FCBF261A45FC19EA4D7BA023" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\SliderGroove.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp026A749908FF7AE58055B0E1AB393168" Guid="{DE24E255-176E-4087-AFAC-E6F56B5D5EAC}">\r
+                            <File Id="fil929EAB5C19264E6900242F51F02822E8" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\SliderHandle.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp4644F2A5A8ADFEA7ED73D09A41CB9AFF" Guid="{2A5E1174-6741-4BCF-BBA3-CE6FE52F79AC}">\r
+                            <File Id="filBA9EEEC5C7078CFE9E1B0DF8A177B5ED" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\SpinBox.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpF0E7DBBAED60C3A0D5E4980C6384C524" Guid="{5DC166C0-8A39-44D0-A9B8-FF474AC6EBDA}">\r
+                            <File Id="fil15BD010BEC93BDA46E1F2686D8FF6EBC" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\SplitView.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpF26383485D9640683A7276A7CFD06280" Guid="{BF3FBD86-AD76-426B-A685-CDF8FE76CC5E}">\r
+                            <File Id="fil55D360EE1ABEF5F5BD900B65B188D41F" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\SwipeDelegate.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp5D85AEBC11476CF382025D6654F36103" Guid="{029CDF92-C9FE-44CF-B9B6-E4A1673AB30A}">\r
+                            <File Id="fil3EDB2B2C23697DAF9F92FFBA99A84650" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\Switch.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp44002125F963B5A1D193E0D787FF6AF6" Guid="{04725E3F-4601-427F-9A74-36F09BC8A604}">\r
+                            <File Id="fil50318A9E962C21697CA18156BA2D0905" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\SwitchDelegate.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpF8E1020B8AEA4B8F647DF3962BB9CF2C" Guid="{FA2434F7-F3ED-4B26-A434-58E112C46E31}">\r
+                            <File Id="fil921E1EA2463721903BCAD40702BF03D1" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\SwitchIndicator.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp6F5B3D19FBA609A3ECE0700E2DB1C805" Guid="{9F1F0447-B481-4AB2-96BF-259633CF6889}">\r
+                            <File Id="fil8B653517ED9597AB20ECDD5E3A75C756" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\TabBar.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpF0883486B5B318E646462C05EB5DBF23" Guid="{E121C66B-7244-4EAB-8225-A363D938EB07}">\r
+                            <File Id="fil17982E06A90DCC3F94109971D657D754" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\TabButton.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp8F7277DF6FE75EF54431CCAB073199B5" Guid="{D75EED9E-1240-47D8-9DC5-5F5832E061F9}">\r
+                            <File Id="filE3100069BCDAE1F6526502196EFBED22" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\TextArea.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpBC3A73EE4B192F1BF88AF2E61E4212EA" Guid="{B137AF9C-F8CC-446F-B263-2DD5A818E699}">\r
+                            <File Id="fil3563463C4219FC7B6DFE6A95F3E9B75A" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\TextField.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpD48A473B45218C0BDE5C64912EFCBAFC" Guid="{2106F73A-F03F-4BEA-A2B6-D8B6C8632E15}">\r
+                            <File Id="filBA1284C10E3F10A1ECC1A687F9997CB4" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\ToolBar.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp86201E518FC2273E24BCD76E296C18E1" Guid="{1027D379-4165-4AC8-B902-6ADAF098ABEB}">\r
+                            <File Id="fil8B324D3244F760FD93C00D0E58BAB57B" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\ToolButton.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpFF8739D6C4BEC98E8F1240FFBD0A64B0" Guid="{AAD9D3FB-1258-4397-8DB0-703866C2D3CF}">\r
+                            <File Id="fil47FA961107A0E38C1F9CC57AAFD5CC5C" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\ToolSeparator.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp38720475F2109CBC7EF4F43C365CC5CA" Guid="{BCF56568-1D7B-40A5-ADCE-DD4376A42888}">\r
+                            <File Id="filB001EB30F35E5F650A63D03CC18ECE35" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\ToolTip.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp996D8C56417E10EF44329F7339E474F2" Guid="{1C26B967-00BF-4D70-8844-0A80BB8FD464}">\r
+                            <File Id="fil93134119EC59B8D442C2BE7A07DD5D2B" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\Tumbler.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpED22A8F3ACEE04AB6CE44D533F4F300A" Guid="{72F16F90-779D-465A-892E-3B55A338F3AE}">\r
+                            <File Id="filE8C20555ACA351E1325A91CF709D8D00" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Fusion\VerticalHeaderView.qml" />\r
+                        </Component>\r
+                    </Directory>\r
+                    <Directory Id="dirFDC25EE37CF6C040EF6B98E257279EC3" Name="Imagine">\r
+                        <Component Id="cmp838B8853704ACB2A7F45BA00B950F3BB" Guid="{141E5A2D-23F0-4522-864B-D3456E2280FD}">\r
+                            <File Id="filC7285D9D5529D85E5E0E217C2D16EA81" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\ApplicationWindow.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpDF9CC0BB490A823FFADEFFBDAB92C2C7" Guid="{5360026C-8AA3-4FE8-AF6A-CABD86B2B70F}">\r
+                            <File Id="fil9835BFE6BBC02870FA9F628A2E0DB6E4" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\BusyIndicator.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp73DF7086A467801478C7321A8B435AA2" Guid="{37150093-DBE6-4270-81A5-8D100EB60F79}">\r
+                            <File Id="fil5DA765828D51355DC019424A172FB9C1" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\Button.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp10D7AC86A56DE2D04356715F35250435" Guid="{4371D289-F3B2-4B73-9E56-9903FA7DCFE5}">\r
+                            <File Id="fil9CB23D5FD7586E7DA94F805DF1529360" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\CheckBox.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpB8BBD8CCBCDE8771AD9E3D6D456FDD6B" Guid="{744F1464-6E9C-4793-981C-73CB5510C194}">\r
+                            <File Id="filC24A1ADF38694A93849B2804D4415C22" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\CheckDelegate.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp5DA5D6BC1A869E7409CC08ACDEBAA08F" Guid="{6769AE38-D978-44BD-A8C5-113A1244E476}">\r
+                            <File Id="fil3F2D3CBED068F4115DE0002AAC205A4F" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\ComboBox.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpEFE26691A6CBD74691FA18F89319520B" Guid="{50DC9AD6-A6CB-47B6-85DC-1C319A7F6F27}">\r
+                            <File Id="fil1164CEFD82A18A964BA86D08201F9CE8" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\DelayButton.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpF14CA7FF23A8FEBB250D9C1A4DBAB5F2" Guid="{6102C842-3167-40B0-A897-7A562F821FA3}">\r
+                            <File Id="fil535F2A996B2C4FC84039F35960BC8B8C" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\Dial.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp76B901918023AFA1ECF5AFE1C5AADFD6" Guid="{B925E4BD-BDF8-419D-A21A-C4F593D261B6}">\r
+                            <File Id="fil2A26AB8828D225E79420414A7C55B95B" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\Dialog.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp3B04385170209415EA0392D6C6D9394D" Guid="{FFC78D64-1508-495B-A0E2-0A2290A40E05}">\r
+                            <File Id="filBB42C691204A19DFDDA38926D9A52E56" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\DialogButtonBox.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpAC7FCF48DC2DD2FF598D8D8EDFBDDE10" Guid="{68D0C46D-1AB5-4CA5-85E0-2175ADEC7360}">\r
+                            <File Id="fil152D23A298E533A4987600002CF20A9E" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\Drawer.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp31295E08BF0ADC9F7414B879A59EAB38" Guid="{EA9C98FB-2D3F-4970-95A7-2CFE7B8C3FCE}">\r
+                            <File Id="fil652FE91BEC083A5CA0F16DAC5115ADD1" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\Frame.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp26ED7398D2B7FD7CE79B20C6F81AA21B" Guid="{C1F1452B-9C22-49DA-AEEC-466D9615DB99}">\r
+                            <File Id="fil4E530D55EE5754394244F29DDD5A4339" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\GroupBox.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp0AF138C48EF9389FB53BCFFF127C63B7" Guid="{99C63B6F-E80E-489E-B320-33CB9324A31C}">\r
+                            <File Id="fil7623E549EBD32B153F482A9ED3D10247" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\HorizontalHeaderView.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpB84DF029169B6913288463C0CD791FD7" Guid="{0982DCB5-B9A0-4CEB-9E86-DB3A55620327}">\r
+                            <File Id="fil47584D2EBEBF080D3E283A08BD78771C" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\ItemDelegate.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpB2768FB92A037D37B0EDB0A50917DB6E" Guid="{C49D785A-76CF-4A89-8FF5-67F8DDA0A0D8}">\r
+                            <File Id="fil85E8BEE2FF4E7D88271DEE95F4B021F6" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\Label.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp60DE9C3DB4C8AE187B4D19BDEDC6C16A" Guid="{A7D686DC-8D8F-4482-96F2-6D0C4F5BCFF0}">\r
+                            <File Id="fil21654FC5F7960A777690B781D204AE5F" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\Menu.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp8439239F2F45A8EAE3D84F080943DDD1" Guid="{048C9830-5BC6-4C43-8E25-3C6B349700F9}">\r
+                            <File Id="filB0FB084CE7E619D3AD1FCFC01826933F" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\MenuItem.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp16223EE59A9E78D62203D037E29D698D" Guid="{7B56130B-F94B-4A3A-B065-E6E4B66A9C20}">\r
+                            <File Id="filF1296202126A8FC1A5FF68833D23C1DD" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\MenuSeparator.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp018AFFA2C882E3A63FE78C48058C9701" Guid="{76589C5E-289E-46A5-A080-04A8D1FEC084}">\r
+                            <File Id="fil16A10C6B3AC90B78F38FF79B36A1D633" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\Page.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpD9344D05ADD481EACDED9FFB5EE9D160" Guid="{CF6DB635-DAE3-4E6C-8A89-3F5145779366}">\r
+                            <File Id="fil1D2EF351CF8F0C59F0CF7CD0CF748B0E" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\PageIndicator.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpA9175F4256E542F0F7D7729B65AC8716" Guid="{1B82A4FA-CC2D-45D0-8871-6FDF98408E5C}">\r
+                            <File Id="fil6ED79940CE9EDFACD1AA339B928CD411" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\Pane.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp022C885B9AF26895C6BBDBE1AF964DA7" Guid="{D7776A87-CE99-4B5B-BDD0-A6ACC91624F5}">\r
+                            <File Id="filD12EF181236C4FD2D8823E2A06702BBC" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\plugins.qmltypes" />\r
+                        </Component>\r
+                        <Component Id="cmpB42CC102A79539E49D9D71AF819BDCFA" Guid="{FB36BE43-4D52-4433-A6B5-F2915F1CBA12}">\r
+                            <File Id="fil9D3116A765C1FFBA2E5F4AF29524555B" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\Popup.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpDE27E4C6B7C8141F7B15529523211BDE" Guid="{5EBA816D-DF14-4E34-A151-FA79A354623A}">\r
+                            <File Id="filC4C7A697145B9CE44EC9CBEE5AD5EB56" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\ProgressBar.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp8A1FE21942F63A12FE8C262291D2F118" Guid="{10CFD156-62EE-4E2A-AC14-CDE672E214D2}">\r
+                            <File Id="fil8D5351D7FEFC8E0D7B074BCD4BE7E93A" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\qmldir" />\r
+                        </Component>\r
+                        <Component Id="cmp89930EDDF65A84FB647750A3889D3998" Guid="{242DF3DF-4813-4AD4-800B-4A6A7F1E00BA}">\r
+                            <File Id="filE4098FA13958E23AF469CE23DE03ADB7" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\qtquickcontrols2imaginestyleplugin.dll" />\r
+                        </Component>\r
+                        <Component Id="cmp7A7C588B21E8E3817983B40E6C15A8DC" Guid="{9E76F7E0-607B-468F-8E59-B2614AD30602}">\r
+                            <File Id="fil09BC6622E343777DE250D2F47E297ED1" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\RadioButton.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpFAF5864C58442FAA1D5C0A01C83FACE9" Guid="{202BAC2C-95C9-467D-BEB4-1EA7E27E31B6}">\r
+                            <File Id="filB5EFC9A07D2D7FD06A43503D3A79870E" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\RadioDelegate.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp48641EDC547835A4C015C138C9EE12D6" Guid="{EC581895-46A3-4CFE-AC75-5FEB7C9C2ACF}">\r
+                            <File Id="fil977DBB2F4A4B7DFED8BB0612A7037970" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\RangeSlider.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp74D44A9BA04153CCD0A6FA413CA57C4B" Guid="{7166BA46-DB87-4DE6-9C1A-1EB28816178D}">\r
+                            <File Id="fil11ADFCA60637AB6A2C391D9EA6B3C543" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\RoundButton.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp84CD7E65AAF5D3AD9F421445901CF37D" Guid="{CEAB5076-3A76-4680-AC7F-379B2329E9D8}">\r
+                            <File Id="filD9610FFD3B120D5B16F73DC0CBCFFD2E" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\ScrollBar.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpAB0EEB7FDAB89DB33CC1872F2D020B9A" Guid="{21EE7B59-9A55-4660-8849-2BAC9C1651D3}">\r
+                            <File Id="filAE6B882E064E88D269983EB2BF4F7EFF" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\ScrollIndicator.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpEA5C0DF0E7CC7FF0C0A116E8ED0DFD39" Guid="{4B3DB6DB-BDC4-400A-8559-032E988E2B47}">\r
+                            <File Id="fil0EB5F1D6A344D46C999C749965A81DF7" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\Slider.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp48F6BA7469D6C5E8A90EFDC2F1F12A03" Guid="{2F7713CC-29A6-4EC2-AA4B-E2E2970448EE}">\r
+                            <File Id="filE4BBEB9676EAAFD512614F5B243CD4F5" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\SpinBox.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpEACB1D29768FC4AD7754B7A8BB5CEAA4" Guid="{D905B9C9-ADBE-4760-9853-EFD1EC7FBA99}">\r
+                            <File Id="fil2F0169274B3846093E9CE0E0CDA3914D" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\SplitView.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp40DFBE5C4004C66BD421833A0916A402" Guid="{03C419A3-AF46-4CA9-A7C3-8ECFCD57104A}">\r
+                            <File Id="fil8E7130A25B7C8446BC71EC968B0715CF" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\StackView.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpD46A2953ED55E92D9E0462BE4D66B828" Guid="{FFD1F1D1-32CD-4E77-ACA3-580CD5596381}">\r
+                            <File Id="filCECBBB01A4F76F4742FDB628AB2398D7" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\SwipeDelegate.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp9BBFEBDFBD798ED64CF1BFB852E57719" Guid="{45F72B4D-8EB6-4BE2-B31A-C9BF307DD2C8}">\r
+                            <File Id="fil3A23A94C3ED39F75C988138BFBEEA2AA" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\SwipeView.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpD60B739F43AAC84324A66CEFFCDDBDFF" Guid="{E0F387DC-7D7F-432C-8EA9-9ED7F865D6CE}">\r
+                            <File Id="fil4751E171C8A0EFA145946D3D11DED958" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\Switch.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp4AF8FAB5F76A012B1E00956784FB86C0" Guid="{DC4C5366-E401-48CB-9A6E-BE7F33FA39E8}">\r
+                            <File Id="fil0AB6CCC6384F6C6B1A85535F2E435452" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\SwitchDelegate.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpD3F2F08057724AFA25E4EBF3514406A6" Guid="{CB2E0B76-1F9A-4B9B-904E-FD373A79E56C}">\r
+                            <File Id="fil06EEC5BCC6DCFD868503648FEB22C2E2" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\TabBar.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp5D978D2112EB628D93B78BBB779859CA" Guid="{2F43967B-733D-4C8C-A715-175288FDA3FD}">\r
+                            <File Id="fil23FF41EA1352977BF0D165A14A816D7F" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\TabButton.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp6800D8FB1FBB983412E92B80451A1109" Guid="{8109FFCD-D535-4EAD-BA10-8A16D8409AA0}">\r
+                            <File Id="fil9E0F7A4390203BCAA10D42088F80E582" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\TextArea.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp71287354FF8DCC602BB0D3D414E05962" Guid="{71FB3A16-2141-4948-89C3-73AB0B166A7E}">\r
+                            <File Id="fil220878B77DD5BA3F56A6D5C7A5EAD1EF" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\TextField.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp0DF5FCC7255520F9D24CAC298FA859D0" Guid="{ADFEC3CF-FD46-4295-96F6-30FDEBBFAB2A}">\r
+                            <File Id="fil87EA3DC0E8CE7A5D1B6D1980848D1E5C" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\ToolBar.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp3301D802CB29561A15AE015C15DD8543" Guid="{0CEB23DC-F5E8-403F-BFC6-02DAEC745718}">\r
+                            <File Id="fil5D5982165FA435D4FA833ABC64663AF4" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\ToolButton.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp00A4060669FB650FE92DDD9A09A857C0" Guid="{F095E727-9F5F-486D-B30C-406D1CE193FD}">\r
+                            <File Id="filD17CC03AD31A7AC8639971FE0D6C45A4" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\ToolSeparator.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp2D3FB42E796C744949D0480EE5C04892" Guid="{211FA71C-4714-49D9-AFFA-CA730B964213}">\r
+                            <File Id="filAA9B4A8954E641AD01238DA365D35C84" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\ToolTip.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp11B5D7ADB851C95DCFE5C71959222497" Guid="{6B13434F-4A6A-4874-95A6-C796BE931A62}">\r
+                            <File Id="filA16915B6759149D53E63B3A6662327D5" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\Tumbler.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp27BC211BA8D2261D578FC390503266AF" Guid="{30C94813-4571-479E-825B-10EDE44E2077}">\r
+                            <File Id="fil64AED8EAC8A4A27B2290235AC66A8987" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Imagine\VerticalHeaderView.qml" />\r
+                        </Component>\r
+                    </Directory>\r
+                    <Directory Id="dir98C439A324E49AE59FF9F7CAFC527818" Name="Material">\r
+                        <Component Id="cmp60AB973400004A31DBB4967567BB3780" Guid="{7D351719-339C-46E2-81CB-905AACB7BEC7}">\r
+                            <File Id="fil64C921AC09AAAAFB16510D0E258259FF" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\ApplicationWindow.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp026D50205EC5D03323420AC1EC10FD89" Guid="{4B779DF7-4593-4A4D-AC2D-38DC4BF88B0B}">\r
+                            <File Id="filDB01E686286D4C2DEE2CFA3FD38FCB40" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\BoxShadow.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpE0BD5867C82F9D8554FECDDC4B6901C7" Guid="{852AD272-C64B-4C36-96B7-8664046E31AA}">\r
+                            <File Id="filE4A4C7B299CDCB670C96418B8EDDB5D8" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\BusyIndicator.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp9F4E4D4D0534820ED7B1592321BACF51" Guid="{A6935D04-49FA-49DF-82EC-BE62493947D5}">\r
+                            <File Id="fil01470810824935FDCE6313DD60621BF3" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\Button.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpCB85EA75A98576ACBC265DECF4F7697F" Guid="{12C6E0D2-3BFA-4BC6-8111-800F3AB9059D}">\r
+                            <File Id="fil1D098E5C4AA1CEC913EAFA6AEC902AB9" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\CheckBox.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpD8B043EAF161B1EED3C0D0E0E8B0717E" Guid="{C800976E-3768-42C1-9BE6-CC3B47528DC8}">\r
+                            <File Id="fil4E240E0B681A24079C96E7EE1B59BB81" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\CheckDelegate.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp006E05B629020152BA06266DCF586F94" Guid="{BCED27FF-F076-445C-A572-6A64493517E1}">\r
+                            <File Id="filB14F8F42CDE72A0055838F894912C34C" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\CheckIndicator.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp6390600916A9D80DEECC6B64C1A5734B" Guid="{A3D8818D-3D8F-4412-A219-82CAD7EA3A78}">\r
+                            <File Id="filAA1D779F78FFD687244475818E1E676D" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\ComboBox.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpA029FDE3C079115EA43947112C138B8C" Guid="{79E6AF29-37B7-4067-89C7-C733024B28CF}">\r
+                            <File Id="filA865175498A738900D1E48E261E9A7A2" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\CursorDelegate.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp050C776C9A28886790788280CCEC3D91" Guid="{E6A61937-0614-478F-A2AE-986106471D6F}">\r
+                            <File Id="fil3A782A789161DA1643DAB87A06031CC9" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\DelayButton.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpBFBC5C595627F87399C1078F9A522E9D" Guid="{B0F3C31E-D1FD-4C88-9603-9DC8E7359565}">\r
+                            <File Id="filAA2365440AA7A931BCC671A9A5ED2CC3" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\Dial.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp2B0856221A67BDF81C2238000B184A17" Guid="{F1026998-7BAD-4314-85CA-4B879412E77F}">\r
+                            <File Id="fil633492C397BA579319833DD3AAD2951E" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\Dialog.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp835605203F95C84205B718EDDDE2091E" Guid="{CC7B5354-0ED3-4872-9481-CD24218471F3}">\r
+                            <File Id="filFEB37EE6FA694858E6397E30CA14D53E" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\DialogButtonBox.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpD37BF99FBDB8EFD807188027A80B8DA3" Guid="{1C7EC37F-ED5A-4AF4-824B-78897568843C}">\r
+                            <File Id="fil9275FC5F45942812B0181C0AD249DA8D" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\Drawer.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp31DA9547A00549AAE408F3DADB011332" Guid="{F1E700ED-73BD-4B51-8D93-13745C69E2DC}">\r
+                            <File Id="filEE7349C912899BD32A59C5B164F24EEB" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\ElevationEffect.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpC869C8092B8F2AAC631C286328891A94" Guid="{FDCDB596-6781-4C12-AD6E-BA12D276162E}">\r
+                            <File Id="filD25404A21A1FB54078D4A8BDD39139B9" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\Frame.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp0305CAFC1FCE93ED480CC829AAD832AF" Guid="{A75B3F5F-8C84-4A5A-9D6C-B2D06223F0D5}">\r
+                            <File Id="filAFE4512E9BFCE1DBEA4D23E504D69326" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\GroupBox.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp5D66D11EA79AD27A1AE641BF9C7CB9F2" Guid="{3CB05274-E6ED-4762-B185-AAD551464977}">\r
+                            <File Id="fil2F55E2A2B3B694DC0B8F749076B915FC" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\HorizontalHeaderView.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpA817DF6EA38B1DF29BCE614C5450544B" Guid="{90C497D8-B166-45CF-A710-1DD2C5BFFA41}">\r
+                            <File Id="fil6DE65D9A2CA853FB7F9873AC257E7BF1" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\ItemDelegate.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpFB26590AF40355E4D807DA7009B7836D" Guid="{F9B22F54-D0AD-471D-B1EB-96E584134B22}">\r
+                            <File Id="fil677DE17071787C3A48E9C6FBAA519F5B" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\Label.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpFA168D7F4EFC52948B17DF862E07E25A" Guid="{C5F7BEEF-1B71-4D45-B859-4CA51121EA22}">\r
+                            <File Id="filEA122269BF1F9F0E5DE2853767AC894F" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\Menu.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp80B129BEE9AED15B717DBBC4E1C91B8D" Guid="{E64751DA-EE5A-4C99-9AD0-24FCDAA4D5CB}">\r
+                            <File Id="fil16F020BBAFC0D9DF9F1A85E68CE222C1" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\MenuBar.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp222BD8179F2D4ECA9CD649598F1665FE" Guid="{B3D9F018-4A95-4520-911A-84862B132195}">\r
+                            <File Id="fil0BBF38951FC82CBE8636981293A2B84A" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\MenuBarItem.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpE9263BDE73548B76D4CF00FA421C6394" Guid="{00E8C1F0-E2DF-43CD-B437-E781866A6E5E}">\r
+                            <File Id="fil12E8E206331572B0EC4AD0A4EE328DC8" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\MenuItem.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp665380529EE1C0ED1DE23E8B26850B19" Guid="{85D9EC2F-DABD-4B96-8B43-107FEC2268E8}">\r
+                            <File Id="fil3BCAD81F81A42FD7A740540891FC5A4B" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\MenuSeparator.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp8DD36362549E19ECEA88E3D1B1ACBB85" Guid="{44592660-851B-47BE-AEFA-34A0EC7F3289}">\r
+                            <File Id="fil615CC9ED7E0F011723181A7F99A61740" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\Page.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpD2EA9700B16C8836D5206D1185C549A0" Guid="{35B14087-6829-4BA2-97DE-4EB8F160A07E}">\r
+                            <File Id="filAD99EB039065C97EF184865D6503E4C1" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\PageIndicator.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp73D2F27C7DDDD4CF7ADAEBF8E6E42EB1" Guid="{2EBCA9CE-E074-4C0E-BDA0-D9CFE4AA041B}">\r
+                            <File Id="filC2DFBF2CB10C8FCB3D1AF32F2A8D57DD" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\Pane.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp7E31792D0EB10DD8AFD37842DFFCA924" Guid="{8A30E34E-392A-406A-8DCA-131248202236}">\r
+                            <File Id="fil1C6676EE896DF9A856E5FE3F9EFAC32C" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\plugins.qmltypes" />\r
+                        </Component>\r
+                        <Component Id="cmpA158D23F2833A86832CC8AB04B51E8D0" Guid="{67B192AC-1AB5-4AD6-BFF4-71902B569AD2}">\r
+                            <File Id="fil70BCFDA53E4BE604641DD96490B3F5EE" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\Popup.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpA62BD30E70FBA79DED1CC038B0404EBC" Guid="{F121CC1F-A122-4191-88EC-836831DD39E9}">\r
+                            <File Id="fil900BC61782FD456E2C7E15DCBA8CAC47" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\ProgressBar.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp78F57F7853BF2F83CA4C27E09AE1E09D" Guid="{5200AA4B-3F84-4020-B572-61028451737C}">\r
+                            <File Id="fil95B30068F5A41AD2EC7F7D95D514FB65" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\qmldir" />\r
+                        </Component>\r
+                        <Component Id="cmpBC000241C708266C86D302A435E66F1C" Guid="{5A4DA7CA-1BF1-4394-A52B-61834B951EC8}">\r
+                            <File Id="filA32CA6F1B47518CE771D5FD69B4BAE80" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\RadioButton.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp3B8600643B709AD9AD113079EDB50C94" Guid="{B6B8BD52-4464-46A3-8A15-2D9DB2F451FA}">\r
+                            <File Id="filFDFEFD51A9E2DF0C75D2E48D4697846D" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\RadioDelegate.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpB755C164EDFD73E1498268DB3DA880E5" Guid="{EEC2C85F-94A4-463C-99D2-0C94DDAB1731}">\r
+                            <File Id="fil438F051667D9B5E1DFEC7C42714E7AC5" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\RadioIndicator.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp1CBDAA564E02886155300F7A8AF049CF" Guid="{52FE64E7-76EA-4E72-8ABC-F72A6912CEDD}">\r
+                            <File Id="filEFF497CDD0E8BF7A1A284FE00DBAC232" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\RangeSlider.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpFAE3866F17FF7039CA9618739C64A1A5" Guid="{1D8BBF80-37C6-4356-981C-E84624F4D130}">\r
+                            <File Id="fil950F62018D52CBBA67D0D037CE077CAF" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\RectangularGlow.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp1BEDE14E6357E29206EDC740AE50650E" Guid="{3832C565-CF30-48A8-9F57-1F1203A9D094}">\r
+                            <File Id="fil7B1D1957A48A219B6EA1E51D1B9D5094" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\RoundButton.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp9848706507698E3D2DE99B0C079EAA41" Guid="{F4159B1A-5628-4239-B834-139C4300EE69}">\r
+                            <File Id="fil7B7E8B589CFA5A08CD0BDCFEBBB37F6E" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\ScrollBar.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpAF75487C0AF2238D5F5AB27EE11B8C29" Guid="{CC851481-1F5C-40DF-B542-CF2331CFBDE2}">\r
+                            <File Id="fil6C7298CEC9325745BABD8553BD2C314E" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\ScrollIndicator.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpF1D853F627859C5B31EEDA150FC58C6E" Guid="{1CCA22C1-4843-4405-84E6-1359524F55C0}">\r
+                            <File Id="filE2D55223B3943A1418684BC06ADC9873" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\Slider.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp24C324DDA2757C24A5D64657F10D1861" Guid="{036A1520-EE7A-49D0-AB3B-6FDA0821BF57}">\r
+                            <File Id="fil456E349BA3AA06452991F85C418012A0" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\SliderHandle.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp24A094BA67CBF70909850C9D82D581FB" Guid="{D516F3DC-29FF-4823-9F3C-2F4DDC120705}">\r
+                            <File Id="filD62846BDB9F588D73844FBE505CDA20B" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\SpinBox.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp855A7D3DC1E9E7D07C156736FB529CB6" Guid="{322DCB01-BEB8-423B-B3CF-9260650724A0}">\r
+                            <File Id="filB7F5B7769F99CFB3EEF067B579C69441" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\SplitView.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpF44D1A83846E86F2D4EFFEDC9DA14661" Guid="{A0B9F909-8889-4C14-BD04-3250095E9F92}">\r
+                            <File Id="fil2E77DB68C32D27DC45D541C2515A7DCB" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\StackView.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp15902266961B205BB7C49E3F21B0A166" Guid="{D1B88C45-21E8-4C9F-96FF-EDE6CBCB383F}">\r
+                            <File Id="fil6E8DF910D77FBD6DC889D4B17EDB8C73" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\SwipeDelegate.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp50966487B32C316D078827563E31DB30" Guid="{F008F394-1A7C-4F15-ADE3-4BAFF638F1AB}">\r
+                            <File Id="fil04105F1624C2D26E2FF0F79EEDB6FB6F" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\SwipeView.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpA1B9598A2B3B9C203B5780C91F7075D7" Guid="{A0B5114E-0A0C-44EF-BE1D-C9CB730A4EAA}">\r
+                            <File Id="fil8BD5245958D87E18B746A319B5EA03EB" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\Switch.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpEF587C49FDE4B67961EDEA007128D3DE" Guid="{FBEE1A46-7848-4E35-9344-6D6B062DD6A5}">\r
+                            <File Id="filE64764B9E04E5C2C205F98B593CE79AD" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\SwitchDelegate.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp793DBF048FBD4343297F1E17756BBF6B" Guid="{8185D55C-9168-4D19-BEAC-34505A4D7106}">\r
+                            <File Id="fil7BF94FDDFE952066A6BF1FC7268E4C41" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\SwitchIndicator.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpD98F61FC599E1BD66057249B28016A0E" Guid="{254D4BDB-E170-408F-B943-DFF5044C0519}">\r
+                            <File Id="fil35E18FE45A8EF14EF0AF02F9E3BB944D" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\TabBar.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp4C6EA3AFEB0324ED3875A46DCCB4EA48" Guid="{48E4E179-8433-4CED-A5C0-9A31EEE63415}">\r
+                            <File Id="filAA08F0B10CF6925991C628327DC46BB4" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\TabButton.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpA7F194C9D57330A5F72C74CD96507E48" Guid="{576229B2-14D1-4DAC-8482-31499B873B5E}">\r
+                            <File Id="fil5669F09C2045004CC25FD46B2565629E" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\TextArea.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp337BDFFE231941F8A3E54BF4AC644B58" Guid="{F791A188-A943-454F-AEE1-FD797A3D801F}">\r
+                            <File Id="filE58155B28FABF154CD98B2C02EC12126" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\TextField.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp0255A607D5334E94D517A2FEDDB2105D" Guid="{10D77816-5456-4FF9-B162-3DF69F8E56EA}">\r
+                            <File Id="fil9261EC814FA4325E3177494A396D8066" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\ToolBar.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpBDEB6272B93E7E2FD1982012B5EF3D02" Guid="{7028EB68-4086-4009-BAD5-6D19E4D5EC9E}">\r
+                            <File Id="fil36BCCA29A63BD763D066F4FD320B6D05" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\ToolButton.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp6ACA30972AFD6D78448054BA4368AEE7" Guid="{8D2AD71A-31A2-4ED5-B3BD-43E761CB7142}">\r
+                            <File Id="fil21C810954616BBB037440CDD95167F6B" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\ToolSeparator.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp0014F4AC9A48147F8EAB3F17B2C144E0" Guid="{DE7EB59D-A01E-453B-B333-887D374C00EB}">\r
+                            <File Id="filE2CD377318429F05238B017CA75F21B1" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\ToolTip.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpB1ABCFD82FD1858E7657C34A8104404C" Guid="{8F3123BF-9523-47FF-ABE9-2B9B3F5D0181}">\r
+                            <File Id="fil98EBD20C2BD6799247161723C27818F9" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\Tumbler.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp6A432A70B05CC4AC38DA38F776B97812" Guid="{4E8C826F-1AAA-4063-9CAE-38614D103CD6}">\r
+                            <File Id="filB345996D2977939507887C6FB257738B" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Material\VerticalHeaderView.qml" />\r
+                        </Component>\r
+                    </Directory>\r
+                    <Directory Id="dir8677A4B366B4AB433BB4754EEBBBE2EA" Name="Universal">\r
+                        <Component Id="cmp134038F5FD44A08B4DA0CFD8921DFB42" Guid="{1B4AC1E8-E146-4872-A0B9-90AAF7A63406}">\r
+                            <File Id="filEE54942B1FE1F7E0FE0DA9372D63E2E9" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\ApplicationWindow.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp34FB0E37566676B69EEEB98F670D23C4" Guid="{09BBA204-8172-4676-B116-B7249D1FB914}">\r
+                            <File Id="filF048A4DBF3EE1F42AB0045720B3A1275" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\BusyIndicator.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp166A7C72CA2D82533EBF24FB87FE4BC5" Guid="{5D75BCE9-3E98-4292-BC43-C72DA6C7B3FD}">\r
+                            <File Id="fil9FC3621AAA61D06486256ACFCEDEFA58" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\Button.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp11D7406D35C2641BC20018F1FA8ADC45" Guid="{50AC31B7-B2E4-492F-97B4-7125DFA06DCE}">\r
+                            <File Id="fil4CC941157F98FD9DCC1CD03DB1383D81" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\CheckBox.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp6CA80FCA9CDD4C55F2582BF139AA4B1B" Guid="{C9EF0708-5E19-48B7-BD71-120ACD0FA185}">\r
+                            <File Id="filAA2312432C29EB3BE6C44D5CB9E552C0" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\CheckDelegate.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpDFBFE88B708CE2CA5904473FE0CDB861" Guid="{0135FF85-D1FF-4305-9E95-CDFA4427F376}">\r
+                            <File Id="fil0D3BF90DE4E4AC4108503B2DB286E7F6" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\CheckIndicator.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp8D47A9ABEDE81A100C924FF154224483" Guid="{AD996097-10A0-48E2-AE3F-2A31A8B99BC7}">\r
+                            <File Id="fil3445161ADB5A36CCA4D8DEE8401F79D9" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\ComboBox.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp0071C7F9D6D4B5DC3218D487DABFC218" Guid="{85A7872B-F79B-43E1-97CC-B30383C2846A}">\r
+                            <File Id="fil1B3A6163FC6D639F2BD22118F8DA406F" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\DelayButton.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp54B17CC907A926962454D9FDEA99E369" Guid="{4492FD9C-0227-4592-931B-1CE255A56335}">\r
+                            <File Id="fil1E9230BCC1B46DE57D1B254EB036939B" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\Dial.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpDF321BF03AC6D81897FFD4B64045DE88" Guid="{76EBA34B-E864-4B0F-9382-2786ABA98828}">\r
+                            <File Id="fil64F4012F0ABC224742C0420695795D29" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\Dialog.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpEBBBCD3B40ADAF40F5F7BBDAD1628BAB" Guid="{3CEB3BEF-B036-491B-8156-71DFCF99AEEA}">\r
+                            <File Id="fil1AD8E7E159B82A10473C11ACD1622A84" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\DialogButtonBox.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp4781FFA691E6B55670D19CCA10E68D4A" Guid="{D15EEBBD-53D6-4922-AA40-39A8594785FA}">\r
+                            <File Id="fil4E217CB94A8B78C9D074F1BAEBB7A41C" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\Drawer.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp9D75099F72BC1EA408C69BAE1036713A" Guid="{5F48BA1E-8F54-465F-8738-CD28295DD983}">\r
+                            <File Id="fil9D815A989044908A0CB2481100231444" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\Frame.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpD507403E3878D92F029FB454CD349781" Guid="{A5180010-062C-494E-82C5-2A7804D7118A}">\r
+                            <File Id="filFA88E95F2ACC7A5522DCF4C107B83B7A" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\GroupBox.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpDB50F59C1041F3C78FB11F782D92A59C" Guid="{DC08DC21-9312-4C4D-BF3A-C43E1A675FB1}">\r
+                            <File Id="fil911A2600E1379E8C5A85199969DB74CF" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\HorizontalHeaderView.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp9ACED442699FD1B84B592FD9A678FF1F" Guid="{9D19D01E-C44E-4FC6-AAD5-6709B0298D2B}">\r
+                            <File Id="filB5F9B6495241535B1200DA731AF5A5F9" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\ItemDelegate.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpC959D8B9C731BB4CB168C53232754164" Guid="{AE31E225-63D5-46EC-BFF9-01D536DE1235}">\r
+                            <File Id="filE924AE836BA5C65F25B0FF78B6A2425C" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\Label.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpF036082D9E70D219B49993BF2078FC9B" Guid="{740E5BA2-9846-417D-81D1-C4F6AB3049A7}">\r
+                            <File Id="fil0FE051A3F7254F4C87AB2787BB8B9195" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\Menu.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp610C62A18FB0CBD4A23E6420089991A6" Guid="{D53BB533-7392-4096-8630-CA6D234AC952}">\r
+                            <File Id="fil0078083DF303E6033ED65F84CCCDA8E0" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\MenuBar.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpF0A89D556198AEC41AA931DE764420C9" Guid="{2478D487-C1B4-498E-A15E-8AFC8EBDD54F}">\r
+                            <File Id="fil141BCF473057F39197B9662FCF606CC8" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\MenuBarItem.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp1FB4C766D7DFBDC29927513C52FF444C" Guid="{07AC968A-CAD2-4435-B348-325CF309AF6D}">\r
+                            <File Id="fil1E9D45ADE39F5FB611AB5AAB33294D3D" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\MenuItem.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpAB3EF735C77E4B6501B74C3070F17E8D" Guid="{9C5FC5C2-834C-48C0-980E-2D5179A53773}">\r
+                            <File Id="filD6ADE8C35F973E505931BD4F09EF0A78" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\MenuSeparator.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpEF370FB1E0BE2F7C44ACA77A0B9A380F" Guid="{FEE2D791-0609-42CC-9183-81B0FCFB3AAF}">\r
+                            <File Id="filBEEE5C63314DE5A1C43E6865925031F3" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\Page.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpD53F72AC172BCE3F947CB966768B983E" Guid="{55F0B5A9-6568-4369-BEC8-9CE9FAB4C886}">\r
+                            <File Id="filD257FB636047A1ABDD8322D56BF83DED" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\PageIndicator.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpB58DE0208168537FC63A28BA40094D3E" Guid="{DDA681AA-E7A3-4935-B6F3-A26C591C0F3D}">\r
+                            <File Id="filF940749E1F1250D0E5E454F677E4811A" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\Pane.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp1E24968394165967A4C933056CC97E8B" Guid="{BCC5C83A-1795-4E9A-9F3B-9EF43B56B4E2}">\r
+                            <File Id="filBBCB3725136BF248504E6130E398CC37" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\plugins.qmltypes" />\r
+                        </Component>\r
+                        <Component Id="cmpA780D5122E140F762FC4C3A0D02E7179" Guid="{8898C5E7-1BF7-40C3-A46D-F097D6A2110A}">\r
+                            <File Id="fil9FC53D09D020635EA1B1AC7DAB106FEB" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\Popup.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpE61937584021054E6A0A34AFBB281937" Guid="{6937E766-8BD5-415D-B76A-F464FEF6E71B}">\r
+                            <File Id="filA8BB46F9B5116CF916C7B675148EFC4E" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\ProgressBar.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpB2D199D19AF00559ED6198A864F4874E" Guid="{DED65452-7CC9-485D-807E-43904CBA7FEA}">\r
+                            <File Id="filFB7A404E13EF7802DE1DDCBEBDD42A2E" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\qmldir" />\r
+                        </Component>\r
+                        <Component Id="cmpA29A335EDA4A585067146163B51D3643" Guid="{86C75BC9-682C-49B2-A48D-B18AA904AAF9}">\r
+                            <File Id="fil23BEBDFB38598BCC408151AC6B584604" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\qtquickcontrols2universalstyleplugin.dll" />\r
+                        </Component>\r
+                        <Component Id="cmp4F8C12B67EDC519F78AA4840C85494D1" Guid="{4F5352F8-AA69-45F8-B461-8FB3CCEDA086}">\r
+                            <File Id="fil2FF1D14D6D70E386EAEAFC412E469DBA" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\RadioButton.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp30F05444D83E8DAFE75CAAB16F50E3AD" Guid="{A4922AB5-4A0B-4CDE-A60E-6B88921844AB}">\r
+                            <File Id="fil5904A6F1649189A762EE1680E07001FA" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\RadioDelegate.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp7573B19F4A56CF2B1D359A87D25A39B4" Guid="{2B71C5A1-2DEA-42CF-B0B5-8856B0B47752}">\r
+                            <File Id="fil0FDD8C1A5AF94ACD8822A21F4762C124" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\RadioIndicator.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp0B698F624CC72E780FB1FCCBE6340AAD" Guid="{F91E47DD-8607-4DBE-938B-D67E14187DD0}">\r
+                            <File Id="fil8EB7C1B7193D2DDA9E532A1B16502A73" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\RangeSlider.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp898F4E8E360375D6D8FE6F606F899441" Guid="{EF555FB1-D8BD-416A-B581-1DCCA58FAABD}">\r
+                            <File Id="fil8455667A31A0B1322CE00154CCF24A08" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\RoundButton.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp246EDCD16E1C5BC88F69EFDF059C62B6" Guid="{363B06E4-66EB-437D-9454-16D14379D4A1}">\r
+                            <File Id="fil8161E8E97F7F2526D40A4E9426ED9CAC" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\ScrollBar.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpB92657C823E717DB4819343891A10BB1" Guid="{3860F820-ABC8-4AA4-A7F9-8B2D10A85511}">\r
+                            <File Id="filF04EF2D65266F90D588563833EE46211" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\ScrollIndicator.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpF4DE6E8F14E6A8E27857D8BC89787BAF" Guid="{DE05151F-0839-4D83-9AB5-7D089240403F}">\r
+                            <File Id="fil4987E569AB3CD95456311C90D8DB245D" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\Slider.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpF49A6AC910C2EB6FF0917041E1F96E8A" Guid="{74BB77A1-0A70-4175-84CD-C4266DDF3112}">\r
+                            <File Id="filE8E232330635AC59E8BE3A0694C54370" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\SpinBox.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp1DF54EA158668A0E21013A820D2E7F51" Guid="{56BF8F73-ECAC-4474-BAB8-CDC47BC0A267}">\r
+                            <File Id="filADDD2AFDAE9A339C8A92C247781CEF40" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\SplitView.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpDFA6653A3AA375D59BE8318C927D5DC4" Guid="{B734122F-CD08-4401-B7BA-5E1D5858EE5A}">\r
+                            <File Id="filAC7F2D3C36D8E02F2FAF7A5578199576" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\StackView.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp20B23CB93B2BC9BF69A97D0994698310" Guid="{E5A49C0D-CAA9-4E51-A610-C46AF31FC864}">\r
+                            <File Id="fil5DFC169D4B5F10EDE586C1C5760B6573" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\SwipeDelegate.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp735A0D9DB2D96E80F3352A232FCAB063" Guid="{E77595FB-FEE6-48C0-A749-862C8CEEC8A0}">\r
+                            <File Id="fil0867DDC68FB2A26C4732AA7C6CF0EF5F" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\Switch.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpDB346892873A00B4024D345FE4C725BA" Guid="{43E075FC-DDDD-43B2-9308-F8174880A344}">\r
+                            <File Id="fil83DE4DE33E72D0F08A1C59200E82B424" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\SwitchDelegate.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp32E0784BA82190D0BD64E488EF265F55" Guid="{EEB2DEA6-7C4F-4CCB-9474-1E5F83FBEEA2}">\r
+                            <File Id="fil26EA260B1D46E42906FBF1BB1CE3B145" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\SwitchIndicator.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp4215773DF3A614E82EF59C456A6EFA97" Guid="{EBFB2786-8DCA-4B6B-870D-4FC90BD82E23}">\r
+                            <File Id="filA8F80D2C80356344CDCD864FCA88D48E" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\TabBar.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp72E1D5A67C90BF0538F8015E486898B0" Guid="{BA27BD86-15CA-47C1-8D6A-7998EDE35547}">\r
+                            <File Id="filDF1DA27F8944A4FC08060F351077F7E8" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\TabButton.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp02A0C8EDA0860CBAE68AE51FAE93EBB1" Guid="{3840413B-B9C9-4DD5-8AD5-D03695BC18C2}">\r
+                            <File Id="fil039EFA8518F7A118F885C10615BAE8BA" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\TextArea.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpC54A96A34D6E8EEB099E5754699E907D" Guid="{14E4461E-D756-4E2D-A569-2AB17F5BD204}">\r
+                            <File Id="filF7DE49C3E03DBE7D7DAEF87FB2CF7EEC" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\TextField.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp2564368EA9BDCD6CBD86BD0B47845CE8" Guid="{D705AE33-5B56-4582-B520-1967673B5A01}">\r
+                            <File Id="fil7BB9016C1239CAC2A691B7E80E155716" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\ToolBar.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp4C037A4C1F7E6103342D757CE84FB3B0" Guid="{37196B4C-A886-40EC-B577-360811B5BBA2}">\r
+                            <File Id="filE5A4593700B13AAF5E707064E4DCBD71" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\ToolButton.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp2782B54139300C706E3DF5D779A32A4E" Guid="{2096E3B3-6475-456F-8104-7044EE32C396}">\r
+                            <File Id="fil043B63F16633A9DEDFD28882B52AD567" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\ToolSeparator.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp5ACAEC60F8EC208DC179E4790FE9090B" Guid="{29854CE8-EAC3-4341-BBF3-6A65CB8B1E5D}">\r
+                            <File Id="fil893C89D4CD3775E4FDF5EBC7DC2F2B64" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\ToolTip.qml" />\r
+                        </Component>\r
+                        <Component Id="cmpE672C34FD6F4C63A61786B77A67D0BCA" Guid="{317CDB5D-8CB9-4E8A-B272-7EF4044501C0}">\r
+                            <File Id="fil7F438968C07C1FD861B594449C04FB30" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\Tumbler.qml" />\r
+                        </Component>\r
+                        <Component Id="cmp05B02931195D1A41BF37EA6B3C26F10B" Guid="{D6329835-9FFD-46EB-8BC2-7E253275209A}">\r
+                            <File Id="fil147142765918ADD0B8E6F1C083AC829F" KeyPath="yes" Source="SourceDir\QtQuick\Controls.2\Universal\VerticalHeaderView.qml" />\r
+                        </Component>\r
+                    </Directory>\r
+                </Directory>\r
+                <Directory Id="dir91DAB6FA29B9FC0AA3F33D5E9F775BC2" Name="Layouts">\r
+                    <Component Id="cmp5FBDC59196ACDF8BDC99FB67B966EB33" Guid="{D8C9AF4A-7179-4745-978B-509993AE272A}">\r
+                        <File Id="fil6633E4E120DA0B6C86F07E8B5E0D813C" KeyPath="yes" Source="SourceDir\QtQuick\Layouts\plugins.qmltypes" />\r
+                    </Component>\r
+                    <Component Id="cmp7599F21DFBFDE604667821D2BEC7A104" Guid="{28609AD7-3A84-4661-A657-44AD9F5C9112}">\r
+                        <File Id="filD17988DD251240A5450B0DF8AAE7D8A9" KeyPath="yes" Source="SourceDir\QtQuick\Layouts\qmldir" />\r
+                    </Component>\r
+                    <Component Id="cmp4C010137216B0EB61B4FB2EFCBE29E10" Guid="{FA31D92A-9BB1-4958-8103-8BA6CE201050}">\r
+                        <File Id="filDA7F5228817F450CCF860D99CCB135A2" KeyPath="yes" Source="SourceDir\QtQuick\Layouts\qquicklayoutsplugin.dll" />\r
+                    </Component>\r
+                </Directory>\r
+                <Directory Id="dir05FA62F47E8C43CCE7A3869D5CB62C6D" Name="Templates.2">\r
+                    <Component Id="cmp2214585F3508B759DB1308CC180757FB" Guid="{B480F13B-F2EF-46A5-839E-E43E5F688DD2}">\r
+                        <File Id="filAB37F3DFF1567C85745F8546C34DB8E4" KeyPath="yes" Source="SourceDir\QtQuick\Templates.2\plugins.qmltypes" />\r
+                    </Component>\r
+                    <Component Id="cmpA1474840D69304F1AD34009D58C2B5DA" Guid="{38B68372-B6BE-4B9D-9A2D-757FEDFA3F74}">\r
+                        <File Id="fil58857EAF612484AD6F1B7594F5496CA3" KeyPath="yes" Source="SourceDir\QtQuick\Templates.2\qmldir" />\r
+                    </Component>\r
+                    <Component Id="cmp40B743148ECA12B22598A157C6147B6E" Guid="{9450D1E0-9549-4179-9BE3-303787AE3E0D}">\r
+                        <File Id="fil3A4423AFDA5CD5B5725F48FA3C3D437A" KeyPath="yes" Source="SourceDir\QtQuick\Templates.2\qtquicktemplates2plugin.dll" />\r
+                    </Component>\r
+                </Directory>\r
+                <Directory Id="dirA3E21ED94D77D9B21B5BA9468AEE4AB0" Name="Window.2">\r
+                    <Component Id="cmp104D8EDE2CF475C5EA4FE8169E74E299" Guid="{C251DAA6-2564-4B1B-AF03-E008227A46D3}">\r
+                        <File Id="fil0DA074889234E8261DE830FC8ADD1A9B" KeyPath="yes" Source="SourceDir\QtQuick\Window.2\plugins.qmltypes" />\r
+                    </Component>\r
+                    <Component Id="cmpDC8A03C1B37F2CFE043AED01502A741C" Guid="{D5570982-F17E-42B8-A5C7-BDE01B78A561}">\r
+                        <File Id="fil06DE355E21B919381A7CB928407CB593" KeyPath="yes" Source="SourceDir\QtQuick\Window.2\qmldir" />\r
+                    </Component>\r
+                    <Component Id="cmp87D7B0E5B5E51059D964EDCED844F4AD" Guid="{45586400-217C-4389-AE8E-89EA2A8FF29C}">\r
+                        <File Id="filAFCC613AFB897A177641333EC6418959" KeyPath="yes" Source="SourceDir\QtQuick\Window.2\windowplugin.dll" />\r
+                    </Component>\r
+                </Directory>\r
+            </Directory>\r
+            <Directory Id="dir00FAA9E14E20D0621E9EF928F4E50AD3" Name="QtQuick.2">\r
+                <Component Id="cmp80E63D0ACF59FD23F5B051FE5BD003DF" Guid="{B7FA0594-A58C-4959-B5A0-F550B3224D62}">\r
+                    <File Id="fil71AA2CD6AFACEC9523574269EB6E2F80" KeyPath="yes" Source="SourceDir\QtQuick.2\plugins.qmltypes" />\r
+                </Component>\r
+                <Component Id="cmp65C97B5A564AB844DB2F02FFA5B163A4" Guid="{9EA96BC5-BDB5-449F-A6F7-BB9AD9A146F0}">\r
+                    <File Id="fil5B9ECA215F124EDEB5921629049ECBD9" KeyPath="yes" Source="SourceDir\QtQuick.2\qmldir" />\r
+                </Component>\r
+                <Component Id="cmp4A028D7062688CA7458F90092B47AAAC" Guid="{AB143E12-669C-4FC2-A85D-4A67112D8041}">\r
+                    <File Id="fil4ED5B7A970D8897099777F501E3BDC88" KeyPath="yes" Source="SourceDir\QtQuick.2\qtquick2plugin.dll" />\r
+                </Component>\r
+            </Directory>\r
+            <Directory Id="dir28D98C712CB0DD42D15220AE3CD09B89" Name="scenegraph">\r
+                <Component Id="cmpB8094893EB4777EEB8D6692DE6B1F4C8" Guid="{58569060-2073-11EE-9B4B-0800200C9A66}">\r
+                    <File Id="fil794512AFD5ACCF820F3B44008D29A080" KeyPath="yes" Source="SourceDir\scenegraph\qsgd3d12backend.dll" />\r
+                </Component>\r
+            </Directory>\r
+<?endif?>\r
+<?endif?>\r
+        </DirectoryRef>\r
+    </Fragment>\r
+    <Fragment>\r
+        <ComponentGroup Id="jacktrip">\r
+            <ComponentRef Id="cmpE6579DC78E167F46AB23E0BE3EAA58CE" />\r
+            <ComponentRef Id="cmp52FE9CFF214054652C281C37201305EC" />\r
+            <ComponentRef Id="cmpA07F2DC814BECD1E5F3FC36DC47718E5" />\r
+            <ComponentRef Id="cmpBF7FA1E27DF091A3CCFA1CD7B7CD94BA" />\r
+            <ComponentRef Id="cmpF915A8D749EFFA98CD8DD582A7C82614" />\r
+<?ifdef rtaudio?>\r
+            <ComponentRef Id="cmpD683260746BC875AE3BDED667AA8F954" />\r
+<?endif?>\r
+<?ifdef dynamic?>\r
+            <ComponentRef Id="cmpF83098612A5B11C5398DBA3894ED590A" />\r
+<?ifdef qt5?>\r
+            <ComponentRef Id="cmp5DDCF0CE56D406D1B8B1F93E988AED6B" />\r
+            <ComponentRef Id="cmpB0CFEB9FF932521D6A75B6BE6F8D644F" />\r
+<?endif?>\r
+<?ifdef mingw?>\r
+            <ComponentRef Id="cmpB60B52D038613C3F20F84A0E1C83D4D4" />\r
+            <ComponentRef Id="cmpDEFDDDBEBEA7255D07EE4400F2CDA2FF" />\r
+            <ComponentRef Id="cmp809509D62BD9EA848EF243F03D83758F" />\r
+            <ComponentRef Id="cmp57DDA83E372188A5B760684B961DE685" />\r
+            <ComponentRef Id="cmpF76D0CD1138260DB167395B86D0011CF" />\r
+            <ComponentRef Id="cmpEA16414E09FD1D5068139520966B49EB" />\r
+<?endif mingw?>\r
+<?ifdef qt5?>\r
+            <ComponentRef Id="cmp01229088A6F1DEA1BE391E6E7CC3D3EE" />\r
+            <ComponentRef Id="cmp1658AEC83DA2CDD0D93D5BAAABBEAD46" />\r
+            <ComponentRef Id="cmp2DE804C107C75B0C71470A93647D09B5" />\r
+            <ComponentRef Id="cmp82912105030EA0D2D7550CF8AC5677F6" />\r
+            <ComponentRef Id="cmp975651B71289ACA70B3EB154C08A27F0" />\r
+            <ComponentRef Id="cmp3CE5AF7CB2F8B2689D2096769883BC56" />\r
+<?endif?>\r
+<?ifdef qt6?>\r
+            <ComponentRef Id="cmp9147A534CD8EDAB4BA7FDEAEEFE9C511" />\r
+            <ComponentRef Id="cmp52AB384603E096D6B6A1E1FC66B089D4" />\r
+            <ComponentRef Id="cmp0E8605D5C55843FCDFE809B5EE8DFDFA" />\r
+            <ComponentRef Id="cmp5D904885C8104A4049616BBAA95F34A3" />\r
+            <ComponentRef Id="cmpCAE694BB34A9EC66AEA9D10D697F1EAA" />\r
+<?endif?>\r
+            <ComponentRef Id="cmp1AAFF160C8E04AD945D74CA884667ABC" />\r
+            <ComponentRef Id="cmp02252B1C1976959CFEE7FA11B6E36C4C" />\r
+            <ComponentRef Id="cmpD9DF9496C7453F1AA6C20CD4A1C94ED7" />\r
+            <ComponentRef Id="cmp0E8940C551429DC79C890130F6D10B6D" />\r
+            <ComponentRef Id="cmpC6FCE143A8E5EAFEA6119E94128459C6" />\r
+<?ifdef extraimageformats?>\r
+            <ComponentRef Id="cmp62CBFF830BF239A4E376FAE29937AFAB" />\r
+            <ComponentRef Id="cmpFCC4763BEDEA49F63E8D8E3A18E86329" />\r
+            <ComponentRef Id="cmpF278C1175D7FB53021C0126B8F155FC2" />\r
+            <ComponentRef Id="cmp608F60C2BD04F2C37AB51C3211ABF615" />\r
+            <ComponentRef Id="cmpE6323C810986405043F219CECD6B6C10" />\r
+<?endif extraimageformats?>\r
+            <ComponentRef Id="cmp9132FD413972F732967B8BC075BC2781" />\r
+            <ComponentRef Id="cmp52840D209B2AC1DC45A29A6BA5A75FF8" />\r
+<?ifdef qt6?>\r
+            <ComponentRef Id="cmp83139013FA2C9A99CF2772FD54090FAA" />\r
+            <ComponentRef Id="cmp6E4460E3D6226693FEA5DBBF500895C7" />\r
+            <ComponentRef Id="cmp53306F84AD30BD49CF0C1A68007F9531" />\r
+<?endif?>\r
+<?ifdef hastranslations?>\r
+            <ComponentRef Id="cmpBCDC6995284E75B9A24E4646E6117744" />\r
+            <ComponentRef Id="cmp3B3D2ADEE08668758CB9FDB1D243AA11" />\r
+            <ComponentRef Id="cmpDAE87E8CC29C30A200A5D17D81DF1DE7" />\r
+            <ComponentRef Id="cmp719D62CCB63A2C08F22335557CBAB533" />\r
+            <ComponentRef Id="cmp8BDD412901C153FC8A2BF7335835AC78" />\r
+            <ComponentRef Id="cmp56040A1BBEBE8A547F7D6672326FF4EE" />\r
+            <ComponentRef Id="cmp42D4B2D8A4C91F9ED463DA42F3895E27" />\r
+            <ComponentRef Id="cmpB92DA15530102518B1B98DBF1DAC135D" />\r
+            <ComponentRef Id="cmp7210C3ACD533D6A934BD5977AB16AF71" />\r
+            <ComponentRef Id="cmp0643210AD0A541FA765CE55469F22FBB" />\r
+            <ComponentRef Id="cmpBB98B3096730FFFE87E708A6291ED47C" />\r
+            <ComponentRef Id="cmp3666ECDFA614EA095AF7310AECCD44B7" />\r
+            <ComponentRef Id="cmpC4FA2671255E7459BD46070D88B76FF1" />\r
+            <ComponentRef Id="cmp8D644C0D5E80E34EBCB6FC3643C879AF" />\r
+            <ComponentRef Id="cmpA8DDE2BD9E1AB00C9631DE447CBAA11D" />\r
+            <ComponentRef Id="cmp23B3657EE14B0ED7D00B271AFF81187C" />\r
+            <ComponentRef Id="cmp2D105F39980CF93A40405E25755346BB" />\r
+            <ComponentRef Id="cmpC3CB042567F53DD612D309E9CA2E10A6" />\r
+            <ComponentRef Id="cmp43D57F897A909679308EFC03849D7E2E" />\r
+            <ComponentRef Id="cmpF5EA7E37332E95C614BD2D7EAA933E5D" />\r
+            <ComponentRef Id="cmp8DAF13208A863A841AEE7A46E79FB87C" />\r
+            <ComponentRef Id="cmpA214B663A372012F5DCBA6A5CDAE421D" />\r
+            <ComponentRef Id="cmpFDB70117D8E46EB188997470A3793B5D" />\r
+<?ifdef qt6?>\r
+            <ComponentRef Id="cmpF0099CF54A0669D87CC17418C3911422" />\r
+            <ComponentRef Id="cmpE89BF9966FD9EC39B16BC14B5586E0FA" />\r
+            <ComponentRef Id="cmp7F6AC80CE7E39EEDC9A962EAFADD113B" />\r
+            <ComponentRef Id="cmp9EB4F508A604A4AD35BC558C456111DD" />\r
+            <ComponentRef Id="cmp09D69EFDCF1B331A37D028C962FE6147" />\r
+            <ComponentRef Id="cmpA23046CBC7F8B7E62AF4843CBB72DEC9" />\r
+<?endif?>\r
+<?endif hastranslations?>\r
+<?ifdef vs?>\r
+            <ComponentRef Id="cmp10C81658E3C8F63B19EF9D1C26B58363" />\r
+            <ComponentRef Id="cmp5B4749C387D33EAF993DDC89F9DBC102" />\r
+            <ComponentRef Id="cmp7F83581B3F7835094D371708381AD391" />\r
+            <ComponentRef Id="cmp33B78CE22F0CBCA4F41C5037A792210C" />\r
+            <ComponentRef Id="cmpB5571DB4C3153CA848ED1238DBCD4281" />\r
+            <ComponentRef Id="cmp4C1BE3EACCCE195C8B88434D5DF77DE5" />\r
+            <ComponentRef Id="cmp937697C855506045D9FF8DF5588CAFEE" />\r
+            <ComponentRef Id="cmpFEC2C3E75BBEACDB1A6F4AFB808CD58E" />\r
+            <ComponentRef Id="cmp8B3D94901F4D369CD276B339B8B00F70" />\r
+            <ComponentRef Id="cmp0B6C2EAABA7EAA14C510F4C9D68A3F40" />\r
+            <ComponentRef Id="cmpE75E4FEE65DCA51D044DF4B2AEDCD828" />\r
+            <ComponentRef Id="cmpA8BBE7010AD624EFBA7166925167402F" />\r
+            <ComponentRef Id="cmp00B1DB73ED33EC1DD6658036D3901CB7" />\r
+            <ComponentRef Id="cmp9F481CFBAECB8853AF07C1D5060B77D7" />\r
+            <ComponentRef Id="cmp93816105EAC51740B98AF9C1041D28D1" />\r
+            <ComponentRef Id="cmp5ED50822278A4A846E891B41C07F7B6A" />\r
+            <ComponentRef Id="cmpACA0A17DA54991A194B823CE40D06263" />\r
+            <ComponentRef Id="cmp3D2C980EC975DBBCBD13A9178DD1A03B" />\r
+            <ComponentRef Id="cmp356189FC3558D542AA8AD79067A42632" />\r
+            <ComponentRef Id="cmpABC392DBCC9A5A469B77C129899873EE" />\r
+            <ComponentRef Id="cmp740F889D5799A2FF0F93A24058617EB6" />\r
+            <ComponentRef Id="cmp2799826256C0E84847E94E11C7878018" />\r
+            <ComponentRef Id="cmp659424F663F96AE55FBA532B4CBB2EC8" />\r
+            <ComponentRef Id="cmpDF6A99C1FA6B3943D690F21136BA7F9F" />\r
+            <ComponentRef Id="cmp81B459304782BB6AC65E381BA13726B0" />\r
+            <ComponentRef Id="cmp3CC0196A91D9C805D66BFFF4EA38EBE7" />\r
+            <ComponentRef Id="cmp67E65C02C04C3567636F47EFAA888D12" />\r
+            <ComponentRef Id="cmpADC8E894229BBB48BBC40D4F033FD791" />\r
+            <ComponentRef Id="cmp0D2D3E713559661141BC5DD9F687F29D" />\r
+            <ComponentRef Id="cmp3C102DD93EC83164DA87E7820364DDAC" />\r
+            <ComponentRef Id="cmp4B666BB592E998D07B55C82CF76F5B1C" />\r
+            <ComponentRef Id="cmp4C99F5024E504FEAB151291478878934" />\r
+            <ComponentRef Id="cmp84BC34E1E20AAE86B427C9CD3129C4FA" />\r
+            <ComponentRef Id="cmpCBA14389AA89D290E481E0F2625854CE" />\r
+            <ComponentRef Id="cmp6FFF646F52B60CE854DA0A1B8C47EC55" />\r
+            <ComponentRef Id="cmp5E04D231070BE2CE70B2672E56C6B9DE" />\r
+            <ComponentRef Id="cmp8C5CA067EC2AB41E7CAFE253F2EC86A8" />\r
+            <ComponentRef Id="cmp8441DAF2F2BC211F1F6FD10DCE7B51FA" />\r
+            <ComponentRef Id="cmpC3A1EC5089BA6B0798970A12E614BBDB" />\r
+            <ComponentRef Id="cmp854B1173EACBB218B6AF849A7E8A56D7" />\r
+            <ComponentRef Id="cmp7D04EA58DD5BF794EB2D3A75609DEFF9" />\r
+            <ComponentRef Id="cmp688C34D162C3029BDE9A236F29106F11" />\r
+            <ComponentRef Id="cmp7F9ABA6F59701379E7E54AB2EF4B01EA" />\r
+            <ComponentRef Id="cmp46E93DFCA823AE21F1FD874F13C0A6CA" />\r
+            <ComponentRef Id="cmp2F1F9DE69090BFD2946A7621F2DA410E" />\r
+            <ComponentRef Id="cmpE650E3AF80C0073C2B52BC369D45B79C" />\r
+            <ComponentRef Id="cmpB62C6E9BF562754AAC86BB52A361231D" />\r
+            <ComponentRef Id="cmpA11A92D9D7D10A9221DD05699B7B1B8C" />\r
+            <ComponentRef Id="cmpA9B83EF4B33682EFEA153198F1C44255" />\r
+            <ComponentRef Id="cmp4951B8E17EA0B3A6B265E2C0E893A50A" />\r
+            <ComponentRef Id="cmp2BD742974C0791D6BD1AE4F7E3646D99" />\r
+            <ComponentRef Id="cmpF8459C652CF5C20ED87F853C1ABE1752" />\r
+            <ComponentRef Id="cmpCA0AF56CA87A52053FE61B6ECD156E35" />\r
+            <ComponentRef Id="cmpBC9523950CF52702CB1A519123F9E519" />\r
+            <ComponentRef Id="cmp302F033AE251BD62E648805EFB1BB536" />\r
+            <ComponentRef Id="cmpF3F704D8F2236A0A62B980FA41D3CB5E" />\r
+            <ComponentRef Id="cmp00AC1A2BE52EA04246F38C5F0B22A9D0" />\r
+            <ComponentRef Id="cmp0A8C093EE03780F9F7823442F10A6C82" />\r
+            <ComponentRef Id="cmp44B6838E73CD47A3F0061ECCE2EB48D0" />\r
+            <ComponentRef Id="cmp57A7D8DDBA7FB780E38A3CF0C3DE50F6" />\r
+            <ComponentRef Id="cmp74FB488898EE30854670C634A6CA57CB" />\r
+            <ComponentRef Id="cmpFF41000DFB8364F307DEC40444A8ED49" />\r
+            <ComponentRef Id="cmpE8348F57B7F7ED3E102061755EA59678" />\r
+            <ComponentRef Id="cmp789571C85C4C76BD48E9A471271B5128" />\r
+            <ComponentRef Id="cmp07E1D3988D9259288197F71CC0B0559A" />\r
+            <ComponentRef Id="cmp91EB04F6C5AFAF292A0BFCE3570DCBFE" />\r
+            <ComponentRef Id="cmpDA0DD041411FA5AC356FAD21EBB0BFCC" />\r
+            <ComponentRef Id="cmpC0C294F18C416B2F09D563A7D2F171C0" />\r
+            <ComponentRef Id="cmpBBC07C7AA9E1D56538F0EE5BC883A106" />\r
+            <ComponentRef Id="cmpA2AD6F575E441894DDF6301F935EF3E7" />\r
+            <ComponentRef Id="cmp346061900FCE515D6B32CFCD7971325C" />\r
+            <ComponentRef Id="cmpE5AC86A748B88CA71F59F050376D4BC8" />\r
+            <ComponentRef Id="cmp26A45023E109A672FAA78FE5C4F827F4" />\r
+            <ComponentRef Id="cmp92014B7FBE8B02881A6A53384BDCD1C4" />\r
+            <ComponentRef Id="cmp372134AB6EF24BAC7497CC47466F0727" />\r
+            <ComponentRef Id="cmp17FB129D73D5BCA38BEC406E5A7A9853" />\r
+            <ComponentRef Id="cmp59BA1197723375EE2B1A8FC0971407A8" />\r
+            <ComponentRef Id="cmp9076C49961F4F0F4F5B4F65EF0CE7925" />\r
+            <ComponentRef Id="cmpCBF4FE8555DE1D61528AF79EA3C5870F" />\r
+            <ComponentRef Id="cmp63B837CFDE5F5C8ADC92D6A80E367621" />\r
+            <ComponentRef Id="cmp73FC7C1B9421F67443A7CA5E03C6F59C" />\r
+            <ComponentRef Id="cmp24D33989EF8148F7E8E801168FA75D02" />\r
+            <ComponentRef Id="cmp781F94BE6B67AAB9F2DD4A6865F67043" />\r
+            <ComponentRef Id="cmpBF8FE1999E64BE53CD0FFE028F2BA8E5" />\r
+            <ComponentRef Id="cmp508D7E9DE20A057C80595708342CCD11" />\r
+            <ComponentRef Id="cmp814DB5A7174FAD3D492689B3CFFD4FC6" />\r
+            <ComponentRef Id="cmp9FCA0D048C5FEDEF35CFDCF4220D148D" />\r
+            <ComponentRef Id="cmp8302E43F7B461A53125D24EBAB2C8BBA" />\r
+            <ComponentRef Id="cmpDD4764A31C5966C382C9FADCF2747DAE" />\r
+            <ComponentRef Id="cmp4DC6229A880E85A8EE64B69A175EB7F8" />\r
+            <ComponentRef Id="cmpC3E4C72480B21745D1D55462CD128148" />\r
+            <ComponentRef Id="cmp9C2C5E7D9EAC8AD7E9FB8BA9B0147BAB" />\r
+            <ComponentRef Id="cmp66ADBF15C183C1EF05BA59EBF57DC04C" />\r
+            <ComponentRef Id="cmpB85DB993D3D58E717F4EC2A2A5AC4130" />\r
+            <ComponentRef Id="cmp93014FB27329F9718833E30A8DCE2F23" />\r
+            <ComponentRef Id="cmp9D0CC60826A95D19D52A3A08FB44155C" />\r
+            <ComponentRef Id="cmp06BD88C78B72DD375F0FD8DC37812A7A" />\r
+            <ComponentRef Id="cmp6577B61F7B803092F263CE9BD76D7B19" />\r
+            <ComponentRef Id="cmpD880E6EDC6A23CDFF94AB5E0A0018A10" />\r
+            <ComponentRef Id="cmp00D081BB0762FEC39933D1C2960FC831" />\r
+            <ComponentRef Id="cmpD69A2401CBD6D26E44BE1271C8905A24" />\r
+            <ComponentRef Id="cmp0B7DD63BE81BC1EA8DB09A99C12CB156" />\r
+            <ComponentRef Id="cmp1EC09C5C4D6CE477EAA2BE5A3798E1CC" />\r
+            <ComponentRef Id="cmp577EBA2061AD5D464AC5B629EF1A63DE" />\r
+            <ComponentRef Id="cmpFD9033FF82B141429428B759027C0ECB" />\r
+            <ComponentRef Id="cmpD9A7A9076E4844ACEB4385E651C6FC27" />\r
+            <ComponentRef Id="cmp952D38FA5543D46E8B0B6CAC867E6D19" />\r
+            <ComponentRef Id="cmp8360F4A2F8333FCC0A95F36461DAC0AE" />\r
+            <ComponentRef Id="cmpA8C48D6004583C73A28027E805ECF241" />\r
+            <ComponentRef Id="cmpF0F52E0AA644EE5B8A4212507A2A9632" />\r
+            <ComponentRef Id="cmp1F2A598B5F572A95F619F914ADFDFD01" />\r
+            <ComponentRef Id="cmp894CC6997FDC49D32993AF049D19661B" />\r
+            <ComponentRef Id="cmp45A86D8F1E2A0DB9085F1C386BE8C441" />\r
+            <ComponentRef Id="cmpD7F91C9BF1B37E7ECEFC803745680533" />\r
+            <ComponentRef Id="cmp6C7380F6F9D3432EF0FF68155BE5B7EA" />\r
+            <ComponentRef Id="cmp163F0198648660C0C4DED70616CD3A50" />\r
+            <ComponentRef Id="cmpF5CA7A4D3DB71A67C3E4BC87D9A553DE" />\r
+            <ComponentRef Id="cmp2599D94F81ED612C05479E9A8F58A189" />\r
+            <ComponentRef Id="cmp9557C3464EC921CAC1A43D77B20BA92D" />\r
+            <ComponentRef Id="cmpFDD06D370422F04997F37CDCF9077AAF" />\r
+            <ComponentRef Id="cmpC5C6ADF92BCA401BFD4AB6B8DF07ABE6" />\r
+            <ComponentRef Id="cmpC7F75CEAF1EB837B6998ED5DC6550404" />\r
+            <ComponentRef Id="cmp4E0B2D6970453AF152BA060236BD8B71" />\r
+            <ComponentRef Id="cmp3A32B8F13C49569A0A4AD1D11045BE3A" />\r
+            <ComponentRef Id="cmp6D11E1D44ADD3049CE735F324F7F4521" />\r
+            <ComponentRef Id="cmp6763D9C183B24CD6659B616DD72994DA" />\r
+            <ComponentRef Id="cmp0FEA3716333AE56EBB72D727432DFB0A" />\r
+            <ComponentRef Id="cmpF77FF7EB150083CBBB82946EC3F849C5" />\r
+            <ComponentRef Id="cmp8693A45A9517FDC707611CD9592AD887" />\r
+            <ComponentRef Id="cmp22009CC98E7BCD73739238DBAD70F5FF" />\r
+            <ComponentRef Id="cmp006248D9C946635CD2C0D21D711BAF97" />\r
+            <ComponentRef Id="cmp8F4980EC013CDADC9677179C1B5FB4DB" />\r
+            <ComponentRef Id="cmp7CB5E11B511D00866734F2A59242071E" />\r
+            <ComponentRef Id="cmp15339C0E193CBE17FB6CB89DA971471D" />\r
+            <ComponentRef Id="cmp7A634DCF2DA1EDFF9FBAF699758570C3" />\r
+            <ComponentRef Id="cmpAA64F717D23C1375E79BE28E5A3DE005" />\r
+            <ComponentRef Id="cmp14F754092A68F3FE755963981761F031" />\r
+            <ComponentRef Id="cmp8DBD1F81C17E5D01D819E619D30E83CA" />\r
+            <ComponentRef Id="cmp9A97912EFCCFD956EF8F155A81CB494F" />\r
+            <ComponentRef Id="cmp37BA96169B5087F3D2C6E769FF5E9270" />\r
+            <ComponentRef Id="cmpE74780CCB3558FB4C37DDE65264DC1F4" />\r
+            <ComponentRef Id="cmp86D5D0E9BB2DE2BDCC6CF459CB7C4E01" />\r
+            <ComponentRef Id="cmp3951161EB256315C232D7C2B211A5D42" />\r
+            <ComponentRef Id="cmp6CAF0B6CD8ABE85E62A160036392A2CC" />\r
+            <ComponentRef Id="cmp55E217C43921E96FF82040712469E0F3" />\r
+            <ComponentRef Id="cmp57277D9EB23963010750499ED2F541B8" />\r
+            <ComponentRef Id="cmp651F7330940689374F3F4500C1F20484" />\r
+            <ComponentRef Id="cmp02C5C41CDED7F94E885A4A2A28EC3600" />\r
+            <ComponentRef Id="cmp95C32716B4F28A90B070E08CAC81BAC4" />\r
+            <ComponentRef Id="cmp2CEB59611FF3F33F7AEF7D2EAAE0CB05" />\r
+            <ComponentRef Id="cmp8D1031BFABA660F80B7793BB3761EB75" />\r
+            <ComponentRef Id="cmpB52CD2CEE9160F5F67372170543F85B4" />\r
+            <ComponentRef Id="cmp909217E4E53799CCE36ED3F7B817DF15" />\r
+            <ComponentRef Id="cmp88C4BDBD3D48F447FF34FD6DF1FC3C41" />\r
+            <ComponentRef Id="cmp6D134750A96C36CCE37CDAEAC146A810" />\r
+            <ComponentRef Id="cmp17431F800533AAD492A8DA91C537AEE5" />\r
+            <ComponentRef Id="cmp5651D28BAA4D7517AB64D608440B8947" />\r
+            <ComponentRef Id="cmp1FC8B77EF51F392FB0F6CD269098431D" />\r
+            <ComponentRef Id="cmp403E7038C7324A61D8727DC1D939E547" />\r
+            <ComponentRef Id="cmp98D46B3DC15B713ACF5F53B34EE13068" />\r
+            <ComponentRef Id="cmp82998B9C0403FF9F31583386A05AA9BB" />\r
+            <ComponentRef Id="cmp0D0B1F4459593A8B3D4A1BCBA139D98F" />\r
+            <ComponentRef Id="cmpF083A0EDD7D82B7EEFA116A49700462C" />\r
+            <ComponentRef Id="cmp094723DF4DF9079CABB07B397F53FC14" />\r
+            <ComponentRef Id="cmpE46F1304BE1F25953FA1502338B46B6E" />\r
+            <ComponentRef Id="cmpF34990BAAB80CEB1B1FBA3F75A8BB18E" />\r
+            <ComponentRef Id="cmpF8164C6D715D603507F60EA7662104BB" />\r
+            <ComponentRef Id="cmpBC49C3C8C0D1C882B8E4BFA5DDB84097" />\r
+            <ComponentRef Id="cmp026A749908FF7AE58055B0E1AB393168" />\r
+            <ComponentRef Id="cmp4644F2A5A8ADFEA7ED73D09A41CB9AFF" />\r
+            <ComponentRef Id="cmpF0E7DBBAED60C3A0D5E4980C6384C524" />\r
+            <ComponentRef Id="cmpF26383485D9640683A7276A7CFD06280" />\r
+            <ComponentRef Id="cmp5D85AEBC11476CF382025D6654F36103" />\r
+            <ComponentRef Id="cmp44002125F963B5A1D193E0D787FF6AF6" />\r
+            <ComponentRef Id="cmpF8E1020B8AEA4B8F647DF3962BB9CF2C" />\r
+            <ComponentRef Id="cmp6F5B3D19FBA609A3ECE0700E2DB1C805" />\r
+            <ComponentRef Id="cmpF0883486B5B318E646462C05EB5DBF23" />\r
+            <ComponentRef Id="cmp8F7277DF6FE75EF54431CCAB073199B5" />\r
+            <ComponentRef Id="cmpBC3A73EE4B192F1BF88AF2E61E4212EA" />\r
+            <ComponentRef Id="cmpD48A473B45218C0BDE5C64912EFCBAFC" />\r
+            <ComponentRef Id="cmp86201E518FC2273E24BCD76E296C18E1" />\r
+            <ComponentRef Id="cmpFF8739D6C4BEC98E8F1240FFBD0A64B0" />\r
+            <ComponentRef Id="cmp38720475F2109CBC7EF4F43C365CC5CA" />\r
+            <ComponentRef Id="cmp996D8C56417E10EF44329F7339E474F2" />\r
+            <ComponentRef Id="cmpED22A8F3ACEE04AB6CE44D533F4F300A" />\r
+            <ComponentRef Id="cmp838B8853704ACB2A7F45BA00B950F3BB" />\r
+            <ComponentRef Id="cmpDF9CC0BB490A823FFADEFFBDAB92C2C7" />\r
+            <ComponentRef Id="cmp73DF7086A467801478C7321A8B435AA2" />\r
+            <ComponentRef Id="cmp10D7AC86A56DE2D04356715F35250435" />\r
+            <ComponentRef Id="cmpB8BBD8CCBCDE8771AD9E3D6D456FDD6B" />\r
+            <ComponentRef Id="cmp5DA5D6BC1A869E7409CC08ACDEBAA08F" />\r
+            <ComponentRef Id="cmpEFE26691A6CBD74691FA18F89319520B" />\r
+            <ComponentRef Id="cmpF14CA7FF23A8FEBB250D9C1A4DBAB5F2" />\r
+            <ComponentRef Id="cmp76B901918023AFA1ECF5AFE1C5AADFD6" />\r
+            <ComponentRef Id="cmp3B04385170209415EA0392D6C6D9394D" />\r
+            <ComponentRef Id="cmpAC7FCF48DC2DD2FF598D8D8EDFBDDE10" />\r
+            <ComponentRef Id="cmp31295E08BF0ADC9F7414B879A59EAB38" />\r
+            <ComponentRef Id="cmp26ED7398D2B7FD7CE79B20C6F81AA21B" />\r
+            <ComponentRef Id="cmp0AF138C48EF9389FB53BCFFF127C63B7" />\r
+            <ComponentRef Id="cmpB84DF029169B6913288463C0CD791FD7" />\r
+            <ComponentRef Id="cmpB2768FB92A037D37B0EDB0A50917DB6E" />\r
+            <ComponentRef Id="cmp60DE9C3DB4C8AE187B4D19BDEDC6C16A" />\r
+            <ComponentRef Id="cmp8439239F2F45A8EAE3D84F080943DDD1" />\r
+            <ComponentRef Id="cmp16223EE59A9E78D62203D037E29D698D" />\r
+            <ComponentRef Id="cmp018AFFA2C882E3A63FE78C48058C9701" />\r
+            <ComponentRef Id="cmpD9344D05ADD481EACDED9FFB5EE9D160" />\r
+            <ComponentRef Id="cmpA9175F4256E542F0F7D7729B65AC8716" />\r
+            <ComponentRef Id="cmp022C885B9AF26895C6BBDBE1AF964DA7" />\r
+            <ComponentRef Id="cmpB42CC102A79539E49D9D71AF819BDCFA" />\r
+            <ComponentRef Id="cmpDE27E4C6B7C8141F7B15529523211BDE" />\r
+            <ComponentRef Id="cmp8A1FE21942F63A12FE8C262291D2F118" />\r
+            <ComponentRef Id="cmp89930EDDF65A84FB647750A3889D3998" />\r
+            <ComponentRef Id="cmp7A7C588B21E8E3817983B40E6C15A8DC" />\r
+            <ComponentRef Id="cmpFAF5864C58442FAA1D5C0A01C83FACE9" />\r
+            <ComponentRef Id="cmp48641EDC547835A4C015C138C9EE12D6" />\r
+            <ComponentRef Id="cmp74D44A9BA04153CCD0A6FA413CA57C4B" />\r
+            <ComponentRef Id="cmp84CD7E65AAF5D3AD9F421445901CF37D" />\r
+            <ComponentRef Id="cmpAB0EEB7FDAB89DB33CC1872F2D020B9A" />\r
+            <ComponentRef Id="cmpEA5C0DF0E7CC7FF0C0A116E8ED0DFD39" />\r
+            <ComponentRef Id="cmp48F6BA7469D6C5E8A90EFDC2F1F12A03" />\r
+            <ComponentRef Id="cmpEACB1D29768FC4AD7754B7A8BB5CEAA4" />\r
+            <ComponentRef Id="cmp40DFBE5C4004C66BD421833A0916A402" />\r
+            <ComponentRef Id="cmpD46A2953ED55E92D9E0462BE4D66B828" />\r
+            <ComponentRef Id="cmp9BBFEBDFBD798ED64CF1BFB852E57719" />\r
+            <ComponentRef Id="cmpD60B739F43AAC84324A66CEFFCDDBDFF" />\r
+            <ComponentRef Id="cmp4AF8FAB5F76A012B1E00956784FB86C0" />\r
+            <ComponentRef Id="cmpD3F2F08057724AFA25E4EBF3514406A6" />\r
+            <ComponentRef Id="cmp5D978D2112EB628D93B78BBB779859CA" />\r
+            <ComponentRef Id="cmp6800D8FB1FBB983412E92B80451A1109" />\r
+            <ComponentRef Id="cmp71287354FF8DCC602BB0D3D414E05962" />\r
+            <ComponentRef Id="cmp0DF5FCC7255520F9D24CAC298FA859D0" />\r
+            <ComponentRef Id="cmp3301D802CB29561A15AE015C15DD8543" />\r
+            <ComponentRef Id="cmp00A4060669FB650FE92DDD9A09A857C0" />\r
+            <ComponentRef Id="cmp2D3FB42E796C744949D0480EE5C04892" />\r
+            <ComponentRef Id="cmp11B5D7ADB851C95DCFE5C71959222497" />\r
+            <ComponentRef Id="cmp27BC211BA8D2261D578FC390503266AF" />\r
+            <ComponentRef Id="cmp60AB973400004A31DBB4967567BB3780" />\r
+            <ComponentRef Id="cmp026D50205EC5D03323420AC1EC10FD89" />\r
+            <ComponentRef Id="cmpE0BD5867C82F9D8554FECDDC4B6901C7" />\r
+            <ComponentRef Id="cmp9F4E4D4D0534820ED7B1592321BACF51" />\r
+            <ComponentRef Id="cmpCB85EA75A98576ACBC265DECF4F7697F" />\r
+            <ComponentRef Id="cmpD8B043EAF161B1EED3C0D0E0E8B0717E" />\r
+            <ComponentRef Id="cmp006E05B629020152BA06266DCF586F94" />\r
+            <ComponentRef Id="cmp6390600916A9D80DEECC6B64C1A5734B" />\r
+            <ComponentRef Id="cmpA029FDE3C079115EA43947112C138B8C" />\r
+            <ComponentRef Id="cmp050C776C9A28886790788280CCEC3D91" />\r
+            <ComponentRef Id="cmpBFBC5C595627F87399C1078F9A522E9D" />\r
+            <ComponentRef Id="cmp2B0856221A67BDF81C2238000B184A17" />\r
+            <ComponentRef Id="cmp835605203F95C84205B718EDDDE2091E" />\r
+            <ComponentRef Id="cmpD37BF99FBDB8EFD807188027A80B8DA3" />\r
+            <ComponentRef Id="cmp31DA9547A00549AAE408F3DADB011332" />\r
+            <ComponentRef Id="cmpC869C8092B8F2AAC631C286328891A94" />\r
+            <ComponentRef Id="cmp0305CAFC1FCE93ED480CC829AAD832AF" />\r
+            <ComponentRef Id="cmp5D66D11EA79AD27A1AE641BF9C7CB9F2" />\r
+            <ComponentRef Id="cmpA817DF6EA38B1DF29BCE614C5450544B" />\r
+            <ComponentRef Id="cmpFB26590AF40355E4D807DA7009B7836D" />\r
+            <ComponentRef Id="cmpFA168D7F4EFC52948B17DF862E07E25A" />\r
+            <ComponentRef Id="cmp80B129BEE9AED15B717DBBC4E1C91B8D" />\r
+            <ComponentRef Id="cmp222BD8179F2D4ECA9CD649598F1665FE" />\r
+            <ComponentRef Id="cmpE9263BDE73548B76D4CF00FA421C6394" />\r
+            <ComponentRef Id="cmp665380529EE1C0ED1DE23E8B26850B19" />\r
+            <ComponentRef Id="cmp8DD36362549E19ECEA88E3D1B1ACBB85" />\r
+            <ComponentRef Id="cmpD2EA9700B16C8836D5206D1185C549A0" />\r
+            <ComponentRef Id="cmp73D2F27C7DDDD4CF7ADAEBF8E6E42EB1" />\r
+            <ComponentRef Id="cmp7E31792D0EB10DD8AFD37842DFFCA924" />\r
+            <ComponentRef Id="cmpA158D23F2833A86832CC8AB04B51E8D0" />\r
+            <ComponentRef Id="cmpA62BD30E70FBA79DED1CC038B0404EBC" />\r
+            <ComponentRef Id="cmp78F57F7853BF2F83CA4C27E09AE1E09D" />\r
+            <ComponentRef Id="cmpBC000241C708266C86D302A435E66F1C" />\r
+            <ComponentRef Id="cmp3B8600643B709AD9AD113079EDB50C94" />\r
+            <ComponentRef Id="cmpB755C164EDFD73E1498268DB3DA880E5" />\r
+            <ComponentRef Id="cmp1CBDAA564E02886155300F7A8AF049CF" />\r
+            <ComponentRef Id="cmpFAE3866F17FF7039CA9618739C64A1A5" />\r
+            <ComponentRef Id="cmp1BEDE14E6357E29206EDC740AE50650E" />\r
+            <ComponentRef Id="cmp9848706507698E3D2DE99B0C079EAA41" />\r
+            <ComponentRef Id="cmpAF75487C0AF2238D5F5AB27EE11B8C29" />\r
+            <ComponentRef Id="cmpF1D853F627859C5B31EEDA150FC58C6E" />\r
+            <ComponentRef Id="cmp24C324DDA2757C24A5D64657F10D1861" />\r
+            <ComponentRef Id="cmp24A094BA67CBF70909850C9D82D581FB" />\r
+            <ComponentRef Id="cmp855A7D3DC1E9E7D07C156736FB529CB6" />\r
+            <ComponentRef Id="cmpF44D1A83846E86F2D4EFFEDC9DA14661" />\r
+            <ComponentRef Id="cmp15902266961B205BB7C49E3F21B0A166" />\r
+            <ComponentRef Id="cmp50966487B32C316D078827563E31DB30" />\r
+            <ComponentRef Id="cmpA1B9598A2B3B9C203B5780C91F7075D7" />\r
+            <ComponentRef Id="cmpEF587C49FDE4B67961EDEA007128D3DE" />\r
+            <ComponentRef Id="cmp793DBF048FBD4343297F1E17756BBF6B" />\r
+            <ComponentRef Id="cmpD98F61FC599E1BD66057249B28016A0E" />\r
+            <ComponentRef Id="cmp4C6EA3AFEB0324ED3875A46DCCB4EA48" />\r
+            <ComponentRef Id="cmpA7F194C9D57330A5F72C74CD96507E48" />\r
+            <ComponentRef Id="cmp337BDFFE231941F8A3E54BF4AC644B58" />\r
+            <ComponentRef Id="cmp0255A607D5334E94D517A2FEDDB2105D" />\r
+            <ComponentRef Id="cmpBDEB6272B93E7E2FD1982012B5EF3D02" />\r
+            <ComponentRef Id="cmp6ACA30972AFD6D78448054BA4368AEE7" />\r
+            <ComponentRef Id="cmp0014F4AC9A48147F8EAB3F17B2C144E0" />\r
+            <ComponentRef Id="cmpB1ABCFD82FD1858E7657C34A8104404C" />\r
+            <ComponentRef Id="cmp6A432A70B05CC4AC38DA38F776B97812" />\r
+            <ComponentRef Id="cmp134038F5FD44A08B4DA0CFD8921DFB42" />\r
+            <ComponentRef Id="cmp34FB0E37566676B69EEEB98F670D23C4" />\r
+            <ComponentRef Id="cmp166A7C72CA2D82533EBF24FB87FE4BC5" />\r
+            <ComponentRef Id="cmp11D7406D35C2641BC20018F1FA8ADC45" />\r
+            <ComponentRef Id="cmp6CA80FCA9CDD4C55F2582BF139AA4B1B" />\r
+            <ComponentRef Id="cmpDFBFE88B708CE2CA5904473FE0CDB861" />\r
+            <ComponentRef Id="cmp8D47A9ABEDE81A100C924FF154224483" />\r
+            <ComponentRef Id="cmp0071C7F9D6D4B5DC3218D487DABFC218" />\r
+            <ComponentRef Id="cmp54B17CC907A926962454D9FDEA99E369" />\r
+            <ComponentRef Id="cmpDF321BF03AC6D81897FFD4B64045DE88" />\r
+            <ComponentRef Id="cmpEBBBCD3B40ADAF40F5F7BBDAD1628BAB" />\r
+            <ComponentRef Id="cmp4781FFA691E6B55670D19CCA10E68D4A" />\r
+            <ComponentRef Id="cmp9D75099F72BC1EA408C69BAE1036713A" />\r
+            <ComponentRef Id="cmpD507403E3878D92F029FB454CD349781" />\r
+            <ComponentRef Id="cmpDB50F59C1041F3C78FB11F782D92A59C" />\r
+            <ComponentRef Id="cmp9ACED442699FD1B84B592FD9A678FF1F" />\r
+            <ComponentRef Id="cmpC959D8B9C731BB4CB168C53232754164" />\r
+            <ComponentRef Id="cmpF036082D9E70D219B49993BF2078FC9B" />\r
+            <ComponentRef Id="cmp610C62A18FB0CBD4A23E6420089991A6" />\r
+            <ComponentRef Id="cmpF0A89D556198AEC41AA931DE764420C9" />\r
+            <ComponentRef Id="cmp1FB4C766D7DFBDC29927513C52FF444C" />\r
+            <ComponentRef Id="cmpAB3EF735C77E4B6501B74C3070F17E8D" />\r
+            <ComponentRef Id="cmpEF370FB1E0BE2F7C44ACA77A0B9A380F" />\r
+            <ComponentRef Id="cmpD53F72AC172BCE3F947CB966768B983E" />\r
+            <ComponentRef Id="cmpB58DE0208168537FC63A28BA40094D3E" />\r
+            <ComponentRef Id="cmp1E24968394165967A4C933056CC97E8B" />\r
+            <ComponentRef Id="cmpA780D5122E140F762FC4C3A0D02E7179" />\r
+            <ComponentRef Id="cmpE61937584021054E6A0A34AFBB281937" />\r
+            <ComponentRef Id="cmpB2D199D19AF00559ED6198A864F4874E" />\r
+            <ComponentRef Id="cmpA29A335EDA4A585067146163B51D3643" />\r
+            <ComponentRef Id="cmp4F8C12B67EDC519F78AA4840C85494D1" />\r
+            <ComponentRef Id="cmp30F05444D83E8DAFE75CAAB16F50E3AD" />\r
+            <ComponentRef Id="cmp7573B19F4A56CF2B1D359A87D25A39B4" />\r
+            <ComponentRef Id="cmp0B698F624CC72E780FB1FCCBE6340AAD" />\r
+            <ComponentRef Id="cmp898F4E8E360375D6D8FE6F606F899441" />\r
+            <ComponentRef Id="cmp246EDCD16E1C5BC88F69EFDF059C62B6" />\r
+            <ComponentRef Id="cmpB92657C823E717DB4819343891A10BB1" />\r
+            <ComponentRef Id="cmpF4DE6E8F14E6A8E27857D8BC89787BAF" />\r
+            <ComponentRef Id="cmpF49A6AC910C2EB6FF0917041E1F96E8A" />\r
+            <ComponentRef Id="cmp1DF54EA158668A0E21013A820D2E7F51" />\r
+            <ComponentRef Id="cmpDFA6653A3AA375D59BE8318C927D5DC4" />\r
+            <ComponentRef Id="cmp20B23CB93B2BC9BF69A97D0994698310" />\r
+            <ComponentRef Id="cmp735A0D9DB2D96E80F3352A232FCAB063" />\r
+            <ComponentRef Id="cmpDB346892873A00B4024D345FE4C725BA" />\r
+            <ComponentRef Id="cmp32E0784BA82190D0BD64E488EF265F55" />\r
+            <ComponentRef Id="cmp4215773DF3A614E82EF59C456A6EFA97" />\r
+            <ComponentRef Id="cmp72E1D5A67C90BF0538F8015E486898B0" />\r
+            <ComponentRef Id="cmp02A0C8EDA0860CBAE68AE51FAE93EBB1" />\r
+            <ComponentRef Id="cmpC54A96A34D6E8EEB099E5754699E907D" />\r
+            <ComponentRef Id="cmp2564368EA9BDCD6CBD86BD0B47845CE8" />\r
+            <ComponentRef Id="cmp4C037A4C1F7E6103342D757CE84FB3B0" />\r
+            <ComponentRef Id="cmp2782B54139300C706E3DF5D779A32A4E" />\r
+            <ComponentRef Id="cmp5ACAEC60F8EC208DC179E4790FE9090B" />\r
+            <ComponentRef Id="cmpE672C34FD6F4C63A61786B77A67D0BCA" />\r
+            <ComponentRef Id="cmp05B02931195D1A41BF37EA6B3C26F10B" />\r
+            <ComponentRef Id="cmp5FBDC59196ACDF8BDC99FB67B966EB33" />\r
+            <ComponentRef Id="cmp7599F21DFBFDE604667821D2BEC7A104" />\r
+            <ComponentRef Id="cmp4C010137216B0EB61B4FB2EFCBE29E10" />\r
+            <ComponentRef Id="cmp2214585F3508B759DB1308CC180757FB" />\r
+            <ComponentRef Id="cmpA1474840D69304F1AD34009D58C2B5DA" />\r
+            <ComponentRef Id="cmp40B743148ECA12B22598A157C6147B6E" />\r
+            <ComponentRef Id="cmp104D8EDE2CF475C5EA4FE8169E74E299" />\r
+            <ComponentRef Id="cmpDC8A03C1B37F2CFE043AED01502A741C" />\r
+            <ComponentRef Id="cmp87D7B0E5B5E51059D964EDCED844F4AD" />\r
+            <ComponentRef Id="cmp80E63D0ACF59FD23F5B051FE5BD003DF" />\r
+            <ComponentRef Id="cmp65C97B5A564AB844DB2F02FFA5B163A4" />\r
+            <ComponentRef Id="cmp4A028D7062688CA7458F90092B47AAAC" />\r
+            <ComponentRef Id="cmpB8094893EB4777EEB8D6692DE6B1F4C8" />\r
+<?endif?>\r
+<?endif?>\r
+        </ComponentGroup>\r
+    </Fragment>\r
+</Wix>\r
diff --git a/win/qt6-noguids.wxs b/win/qt6-noguids.wxs
new file mode 100644 (file)
index 0000000..b77394a
--- /dev/null
@@ -0,0 +1,1985 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
+    <Fragment>
+        <DirectoryRef Id="TARGETDIR">
+            <Directory Id="dirB10C059F651528B59E1FD268EB08C6D6" Name="iconengines" />
+            <Directory Id="dir20189C824D4DDABC3D9FF9449C089CE0" Name="imageformats" />
+            <Directory Id="dir690B8E6D609B0DB17DB71E8B1FB2FEC5" Name="LICENSES" />
+            <Directory Id="dir4C08960BD61EEE75AE4A87FC65A5DE2A" Name="networkinformation" />
+            <Directory Id="dir826692792B0995C8DF68919F5C684331" Name="platforms" />
+            <Directory Id="dirC15EA267FAD4FC447E84FB3A32CBDB68" Name="position" />
+            <Directory Id="dir9331125B8BF1F459D0AC9F91A6A2F779" Name="qmltooling" />
+            <Directory Id="dirDF4CFBD3627B084EE34DEDC7F0745F0E" Name="Qt5Compat" />
+            <Directory Id="dir70AFC0A9D84A710EC0128D3FED451CDC" Name="QtQml" />
+            <Directory Id="dir9D005E7F3893047E4557D6C5273ADD9E" Name="QtQuick" />
+            <Directory Id="dir8F7FCDAEC833D8963B08029F5D194C05" Name="QtWebEngine" />
+            <Directory Id="dir6D65237267C4C59098A0E61E06E59952" Name="QtWebView" />
+            <Directory Id="dir08941CEA6046320FCCF158F759AF80F2" Name="resources" />
+            <Directory Id="dirD0D2BD28387A827DDF373DED43543ABA" Name="sqldrivers" />
+            <Directory Id="dir3BC41331752E78D0C2719C277915294F" Name="styles" />
+            <Directory Id="dir509C75F94B9AF6C35CA00410005C14EE" Name="tls" />
+            <Directory Id="dir1F7AD7AA9B228AED2E74CDD7C3088B5C" Name="translations" />
+            <Directory Id="dir90EFFBA81E5867E3E251FBDCB82DBC0C" Name="webview" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <ComponentGroup Id="jacktrip">
+            <Component Id="cmp5D831CD15043495A344038AE6FBC4FAF" Directory="TARGETDIR" Guid="PUT-GUID-HERE">
+                <File Id="fil7FD1FE597BD52B987AB17EB8CF74B0D5" KeyPath="yes" Source="SourceDir\D3Dcompiler_47.dll" />
+            </Component>
+            <Component Id="cmpAD46F02BC5359E4227248E749EB0E74F" Directory="TARGETDIR" Guid="PUT-GUID-HERE">
+                <File Id="fil572E83C11D35ADB0E4CB1A0A0AF474C6" KeyPath="yes" Source="SourceDir\dialog.bmp" />
+            </Component>
+            <Component Id="cmp61BABE677B13AE7A25F57865DD2CB150" Directory="TARGETDIR" Guid="PUT-GUID-HERE">
+                <File Id="filD0B8E5384B7EB5609D1B2A67A98FF334" KeyPath="yes" Source="SourceDir\jacktrip.exe" />
+            </Component>
+            <Component Id="cmpCD23A5CC7196B582D23DF7AC7DFE061F" Directory="TARGETDIR" Guid="PUT-GUID-HERE">
+                <File Id="fil00C2A327ECCB1D33F7BBF313ADEEFD43" KeyPath="yes" Source="SourceDir\jacktrip.wixobj" />
+            </Component>
+            <Component Id="cmp63BFF65F319CB52D38DBB3FE6B8DE6F6" Directory="TARGETDIR" Guid="PUT-GUID-HERE">
+                <File Id="fil886DC1D217D62357ED536BCFF93E7C0F" KeyPath="yes" Source="SourceDir\jacktrip.wxs" />
+            </Component>
+            <Component Id="cmp64BB59E0E19E219D4D4D5C775D71731F" Directory="TARGETDIR" Guid="PUT-GUID-HERE">
+                <File Id="fil7D228BFB31A8A99EE307AF8BFC2E1841" KeyPath="yes" Source="SourceDir\LICENSE.md" />
+            </Component>
+            <Component Id="cmpE2BC49F17425202019B2C48873956253" Directory="TARGETDIR" Guid="PUT-GUID-HERE">
+                <File Id="fil011E2822115DD1D5BD9A9096D1914F51" KeyPath="yes" Source="SourceDir\license.rtf" />
+            </Component>
+            <Component Id="cmpD16A82FC04DA86995B222E5199ABF99B" Directory="TARGETDIR" Guid="PUT-GUID-HERE">
+                <File Id="fil03546A9C6805C45BEB0A60D29FFC5D22" KeyPath="yes" Source="SourceDir\qt6.wixobj" />
+            </Component>
+            <Component Id="cmp2A0D7AC2E166178EE2B0C41EB50258BA" Directory="TARGETDIR" Guid="PUT-GUID-HERE">
+                <File Id="fil2E03208E0826B1237D2BAB19634F2DC3" KeyPath="yes" Source="SourceDir\qt6.wxs" />
+            </Component>
+            <Component Id="cmp7111706DBFB0E180EC7A8BDC93A90139" Directory="TARGETDIR" Guid="PUT-GUID-HERE">
+                <File Id="fil1A08189509DCFE1E185AEC6B499929BA" KeyPath="yes" Source="SourceDir\Qt6Core.dll" />
+            </Component>
+            <Component Id="cmpD22BE3A3E3A804D9EC17161FD6ED218F" Directory="TARGETDIR" Guid="PUT-GUID-HERE">
+                <File Id="fil0427F270C0F2EA2C38D6BB72F32BC982" KeyPath="yes" Source="SourceDir\Qt6Gui.dll" />
+            </Component>
+            <Component Id="cmp9FB603605D751CA9FE101013C48671B7" Directory="TARGETDIR" Guid="PUT-GUID-HERE">
+                <File Id="filFE4FFAF140893FFCBD7F595F88958077" KeyPath="yes" Source="SourceDir\Qt6Network.dll" />
+            </Component>
+            <Component Id="cmp62FBAA57D2AA6EAC6A5C151902D999B3" Directory="TARGETDIR" Guid="PUT-GUID-HERE">
+                <File Id="fil720F59CECD8953F508A84670C46B640F" KeyPath="yes" Source="SourceDir\Qt6OpenGL.dll" />
+            </Component>
+            <Component Id="cmp6F8A9D8010540EBE393F23E015D5A792" Directory="TARGETDIR" Guid="PUT-GUID-HERE">
+                <File Id="fil1192A9C8E95EF194DCF3EF7BE626B48E" KeyPath="yes" Source="SourceDir\Qt6Positioning.dll" />
+            </Component>
+            <Component Id="cmpB6D50DE19D6ADD781A08727F7F6266E8" Directory="TARGETDIR" Guid="PUT-GUID-HERE">
+                <File Id="filCF6DBEF1D6A13C80715404D32B934AD5" KeyPath="yes" Source="SourceDir\Qt6Qml.dll" />
+            </Component>
+            <Component Id="cmp4E9BFD75DF26062E9937847116425997" Directory="TARGETDIR" Guid="PUT-GUID-HERE">
+                <File Id="filAE1BF7A9D9BF060761C9FAAB52C1582A" KeyPath="yes" Source="SourceDir\Qt6QmlLocalStorage.dll" />
+            </Component>
+            <Component Id="cmp65C2886373B0C96AD79BAD6B11F4BCEC" Directory="TARGETDIR" Guid="PUT-GUID-HERE">
+                <File Id="fil1A464A34B69E38E90B0748094F580925" KeyPath="yes" Source="SourceDir\Qt6QmlModels.dll" />
+            </Component>
+            <Component Id="cmp609AE9C23E17BA20AC06AEA3F3551DAE" Directory="TARGETDIR" Guid="PUT-GUID-HERE">
+                <File Id="fil70941CA86B91BA9374327AE51424DF95" KeyPath="yes" Source="SourceDir\Qt6QmlWorkerScript.dll" />
+            </Component>
+            <Component Id="cmpF560A3BA9C9935A03A5C37CBAFF8321F" Directory="TARGETDIR" Guid="PUT-GUID-HERE">
+                <File Id="fil123F08A484F5DF5CC72BFCCDC197DBF9" KeyPath="yes" Source="SourceDir\Qt6QmlXmlListModel.dll" />
+            </Component>
+            <Component Id="cmpED65C4A3E125070BC17313D1A32F9C76" Directory="TARGETDIR" Guid="PUT-GUID-HERE">
+                <File Id="filFDA17D38CD6D73072EBAF9E430B8DCD4" KeyPath="yes" Source="SourceDir\Qt6Quick.dll" />
+            </Component>
+            <Component Id="cmpA463F9E3D9E20729888D635B58292550" Directory="TARGETDIR" Guid="PUT-GUID-HERE">
+                <File Id="filDF57D75497458D7E64C07BB9AD1456C9" KeyPath="yes" Source="SourceDir\Qt6QuickControls2.dll" />
+            </Component>
+            <Component Id="cmp0571EF0C5FB8E314588A9065CF74AE35" Directory="TARGETDIR" Guid="PUT-GUID-HERE">
+                <File Id="filDC1C47A2ADA5BD76E86CE6CE8EC4F5B4" KeyPath="yes" Source="SourceDir\Qt6QuickControls2Impl.dll" />
+            </Component>
+            <Component Id="cmp2BB41ECBA565E0A0B4EDF020A49025F9" Directory="TARGETDIR" Guid="PUT-GUID-HERE">
+                <File Id="fil57860EF8422A74A3CE88A88BAD0CABB7" KeyPath="yes" Source="SourceDir\Qt6QuickDialogs2.dll" />
+            </Component>
+            <Component Id="cmp73D256572698B6DB61D05CCD551BEF9A" Directory="TARGETDIR" Guid="PUT-GUID-HERE">
+                <File Id="filC9FA5D0D485CF88CB948BE06870C3B8A" KeyPath="yes" Source="SourceDir\Qt6QuickDialogs2QuickImpl.dll" />
+            </Component>
+            <Component Id="cmpC26015BC765576311A63C5B026DB71FF" Directory="TARGETDIR" Guid="PUT-GUID-HERE">
+                <File Id="fil2911EE010D604A656A176E72D572CCAC" KeyPath="yes" Source="SourceDir\Qt6QuickDialogs2Utils.dll" />
+            </Component>
+            <Component Id="cmpD246CE6E593DB826C58A42EFAB8AC176" Directory="TARGETDIR" Guid="PUT-GUID-HERE">
+                <File Id="fil89BECDEB84C26F37AC111B6D300D2835" KeyPath="yes" Source="SourceDir\Qt6QuickLayouts.dll" />
+            </Component>
+            <Component Id="cmp05C682DFA26731F5F96864E240BB9367" Directory="TARGETDIR" Guid="PUT-GUID-HERE">
+                <File Id="filF00A542F2EB321619F0825C65334A7F5" KeyPath="yes" Source="SourceDir\Qt6QuickParticles.dll" />
+            </Component>
+            <Component Id="cmpB2448C4481D00046098304AC1C230B93" Directory="TARGETDIR" Guid="PUT-GUID-HERE">
+                <File Id="fil359D410486F61CCCDFEFE2410735AC75" KeyPath="yes" Source="SourceDir\Qt6QuickShapes.dll" />
+            </Component>
+            <Component Id="cmpD3B7B317B2EAD7974F7386A4F4137733" Directory="TARGETDIR" Guid="PUT-GUID-HERE">
+                <File Id="filA1B7D830C14FF020BDC9FAF8F83402A1" KeyPath="yes" Source="SourceDir\Qt6QuickTemplates2.dll" />
+            </Component>
+            <Component Id="cmpDAA5AE12CA74181FE29953002CC830A3" Directory="TARGETDIR" Guid="PUT-GUID-HERE">
+                <File Id="filEF75955C67A60E62CD86B26117C750AE" KeyPath="yes" Source="SourceDir\Qt6Sql.dll" />
+            </Component>
+            <Component Id="cmp2BFDE38E15CC8879E8B911A7A296477A" Directory="TARGETDIR" Guid="PUT-GUID-HERE">
+                <File Id="fil08B70D0B09545B2C830255009EF93341" KeyPath="yes" Source="SourceDir\Qt6Svg.dll" />
+            </Component>
+            <Component Id="cmp501A53153FAB3908951DADAE039AB865" Directory="TARGETDIR" Guid="PUT-GUID-HERE">
+                <File Id="filDAAFC08AB32ED0F61D6C56EA5B665627" KeyPath="yes" Source="SourceDir\Qt6WebChannel.dll" />
+            </Component>
+            <Component Id="cmp5046A1A989E72DACE21F8D481D0D71C5" Directory="TARGETDIR" Guid="PUT-GUID-HERE">
+                <File Id="fil4DF01BF67F8DD9C408204A2F5BD56FF9" KeyPath="yes" Source="SourceDir\Qt6WebEngineCore.dll" />
+            </Component>
+            <Component Id="cmp8015426E62DE3ED33343DB50BE0A9A0D" Directory="TARGETDIR" Guid="PUT-GUID-HERE">
+                <File Id="fil58988C3D49189FE2287F68174F6944A2" KeyPath="yes" Source="SourceDir\Qt6WebEngineQuick.dll" />
+            </Component>
+            <Component Id="cmp268C15DC40B82F983C534C9C3DAABD1B" Directory="TARGETDIR" Guid="PUT-GUID-HERE">
+                <File Id="filA069DC2EC3525A8E547CF9EFC89A25E6" KeyPath="yes" Source="SourceDir\Qt6WebEngineQuickDelegatesQml.dll" />
+            </Component>
+            <Component Id="cmp7B3BE582AD25B07FE55DD249E6D5C333" Directory="TARGETDIR" Guid="PUT-GUID-HERE">
+                <File Id="filF8FC62A4978258AB67C456CC256AAA8B" KeyPath="yes" Source="SourceDir\Qt6WebSockets.dll" />
+            </Component>
+            <Component Id="cmpA72EB9BC77F4E13CA8F69B2B9413199C" Directory="TARGETDIR" Guid="PUT-GUID-HERE">
+                <File Id="fil84A612B35A3B234B8729B7998FD0742D" KeyPath="yes" Source="SourceDir\Qt6WebView.dll" />
+            </Component>
+            <Component Id="cmp9CC20E8457A681268437C2AC7D562FE2" Directory="TARGETDIR" Guid="PUT-GUID-HERE">
+                <File Id="filA8C33CBAC2EC4965ED511DF205E7961F" KeyPath="yes" Source="SourceDir\Qt6WebViewQuick.dll" />
+            </Component>
+            <Component Id="cmpBCCA6FD4EC03D708E3811B7D4723801F" Directory="TARGETDIR" Guid="PUT-GUID-HERE">
+                <File Id="filAC348090A125BCB41DB84097A13158A0" KeyPath="yes" Source="SourceDir\Qt6Widgets.dll" />
+            </Component>
+            <Component Id="cmp648D7B3CDDA6427D7237CE400D68C36A" Directory="TARGETDIR" Guid="PUT-GUID-HERE">
+                <File Id="fil71DF3511D06EBF7BFB8342792898BEE4" KeyPath="yes" Source="SourceDir\QtWebEngineProcess.exe" />
+            </Component>
+            <Component Id="cmpE3D572FB13B4249EA0FB18BD0F44DE18" Directory="dirB10C059F651528B59E1FD268EB08C6D6" Guid="PUT-GUID-HERE">
+                <File Id="fil7320C9B04E87091E801ED591E25C7F1E" KeyPath="yes" Source="SourceDir\iconengines\qsvgicon.dll" />
+            </Component>
+            <Component Id="cmp5D66BEFD65BDC9AC7D48118D254EBF30" Directory="dir20189C824D4DDABC3D9FF9449C089CE0" Guid="PUT-GUID-HERE">
+                <File Id="fil167B858704F9A353B556ECC7639B590E" KeyPath="yes" Source="SourceDir\imageformats\qgif.dll" />
+            </Component>
+            <Component Id="cmp2A9FEB410F41B453B0CA26766D4AD20C" Directory="dir20189C824D4DDABC3D9FF9449C089CE0" Guid="PUT-GUID-HERE">
+                <File Id="filB7BCDEF234107021D6B84547174B24BC" KeyPath="yes" Source="SourceDir\imageformats\qico.dll" />
+            </Component>
+            <Component Id="cmp71D8FCDDEED93F03008649285B372ADA" Directory="dir20189C824D4DDABC3D9FF9449C089CE0" Guid="PUT-GUID-HERE">
+                <File Id="fil22403FED9C4013C9328A99701DEA8873" KeyPath="yes" Source="SourceDir\imageformats\qjpeg.dll" />
+            </Component>
+            <Component Id="cmpC07A85EAE90864EB28FD65896AE228D2" Directory="dir20189C824D4DDABC3D9FF9449C089CE0" Guid="PUT-GUID-HERE">
+                <File Id="fil310D7F5AA577821D98D425950E96E344" KeyPath="yes" Source="SourceDir\imageformats\qsvg.dll" />
+            </Component>
+            <Component Id="cmpB46D0962D2340404F851F10CAAECA549" Directory="dir690B8E6D609B0DB17DB71E8B1FB2FEC5" Guid="PUT-GUID-HERE">
+                <File Id="filFCC7617B3CC611B027C8B3FB6DE6EA20" KeyPath="yes" Source="SourceDir\LICENSES\GPL-3.0.txt" />
+            </Component>
+            <Component Id="cmp65AA9556A3F4786F01B316E5DEA9E6F6" Directory="dir690B8E6D609B0DB17DB71E8B1FB2FEC5" Guid="PUT-GUID-HERE">
+                <File Id="filC36144D44D8E4AE0EFCD137DD2752E9D" KeyPath="yes" Source="SourceDir\LICENSES\LGPL-3.0-only.txt" />
+            </Component>
+            <Component Id="cmp727893E2EF55BD50F18FDD0ECF2C824F" Directory="dir690B8E6D609B0DB17DB71E8B1FB2FEC5" Guid="PUT-GUID-HERE">
+                <File Id="fil52FC0319C49E6B01FF651770DA57735F" KeyPath="yes" Source="SourceDir\LICENSES\MIT.txt" />
+            </Component>
+            <Component Id="cmp907AF1F6FA03E2DE91C9FEA025010D55" Directory="dir4C08960BD61EEE75AE4A87FC65A5DE2A" Guid="PUT-GUID-HERE">
+                <File Id="filD108AA20F08DED7C4D3797E896B5D090" KeyPath="yes" Source="SourceDir\networkinformation\qnetworklistmanager.dll" />
+            </Component>
+            <Component Id="cmp830C378ADEFF439CEF6C0F2550FD4D2F" Directory="dir826692792B0995C8DF68919F5C684331" Guid="PUT-GUID-HERE">
+                <File Id="filF7B01E0AFAD9057CB024B85137A38049" KeyPath="yes" Source="SourceDir\platforms\qwindows.dll" />
+            </Component>
+            <Component Id="cmp7C2CB506AB0D7E9648A5F2A7C2C7770A" Directory="dirC15EA267FAD4FC447E84FB3A32CBDB68" Guid="PUT-GUID-HERE">
+                <File Id="filC58409E068EE2AD6FE8C6C57174D6DDD" KeyPath="yes" Source="SourceDir\position\qtposition_positionpoll.dll" />
+            </Component>
+            <Component Id="cmp8539D5D6C3427F1F4C82034569A0F132" Directory="dirC15EA267FAD4FC447E84FB3A32CBDB68" Guid="PUT-GUID-HERE">
+                <File Id="filEF2EB40CA1790B7BDC4320E6F7638744" KeyPath="yes" Source="SourceDir\position\qtposition_winrt.dll" />
+            </Component>
+            <Component Id="cmp3C72ADF7447C02CBC442F4D498F97D6E" Directory="dir9331125B8BF1F459D0AC9F91A6A2F779" Guid="PUT-GUID-HERE">
+                <File Id="fil1468B4258ED9D02B8E682200C411FD95" KeyPath="yes" Source="SourceDir\qmltooling\qmldbg_debugger.dll" />
+            </Component>
+            <Component Id="cmpD130E0EDF6AF950EA6D6A53A1751AA96" Directory="dir9331125B8BF1F459D0AC9F91A6A2F779" Guid="PUT-GUID-HERE">
+                <File Id="filF63AD8464B4C5A27DCC098B1D1D42511" KeyPath="yes" Source="SourceDir\qmltooling\qmldbg_inspector.dll" />
+            </Component>
+            <Component Id="cmp5BDACEF05055E085579E7B097E890197" Directory="dir9331125B8BF1F459D0AC9F91A6A2F779" Guid="PUT-GUID-HERE">
+                <File Id="filB504D3FAAE942E0495C3640CFD5B377B" KeyPath="yes" Source="SourceDir\qmltooling\qmldbg_local.dll" />
+            </Component>
+            <Component Id="cmpBC2D56955BEA8DFE7E62B048E7F30EE8" Directory="dir9331125B8BF1F459D0AC9F91A6A2F779" Guid="PUT-GUID-HERE">
+                <File Id="fil26713E98F405141391C6BEA20282D73E" KeyPath="yes" Source="SourceDir\qmltooling\qmldbg_messages.dll" />
+            </Component>
+            <Component Id="cmpC9297AB1335961FB731B05361407FD09" Directory="dir9331125B8BF1F459D0AC9F91A6A2F779" Guid="PUT-GUID-HERE">
+                <File Id="fil7BAF56034C328F11684D5D91E5582BA6" KeyPath="yes" Source="SourceDir\qmltooling\qmldbg_native.dll" />
+            </Component>
+            <Component Id="cmp19E5AFEAFD4EE414B80BF9256489A85F" Directory="dir9331125B8BF1F459D0AC9F91A6A2F779" Guid="PUT-GUID-HERE">
+                <File Id="fil715013DA57F3A007F8DF4E53099D3C7A" KeyPath="yes" Source="SourceDir\qmltooling\qmldbg_nativedebugger.dll" />
+            </Component>
+            <Component Id="cmp57FF2A44B7244B35C17BB63FE8A1D97F" Directory="dir9331125B8BF1F459D0AC9F91A6A2F779" Guid="PUT-GUID-HERE">
+                <File Id="fil75DECF1849BEFF9DAC535DA290EA2440" KeyPath="yes" Source="SourceDir\qmltooling\qmldbg_preview.dll" />
+            </Component>
+            <Component Id="cmp7F8E313E83EEF3F0A5320389A0D409F0" Directory="dir9331125B8BF1F459D0AC9F91A6A2F779" Guid="PUT-GUID-HERE">
+                <File Id="filD2DF020A7341138A3174AA9926C94254" KeyPath="yes" Source="SourceDir\qmltooling\qmldbg_profiler.dll" />
+            </Component>
+            <Component Id="cmpA1FA5ACBABE6ABAF59E8A15E771A1020" Directory="dir9331125B8BF1F459D0AC9F91A6A2F779" Guid="PUT-GUID-HERE">
+                <File Id="fil2F21F43E56AEBBDE0C3002B3565087E0" KeyPath="yes" Source="SourceDir\qmltooling\qmldbg_quickprofiler.dll" />
+            </Component>
+            <Component Id="cmp3EA7E1216A1E529D7944144D42506A2C" Directory="dir9331125B8BF1F459D0AC9F91A6A2F779" Guid="PUT-GUID-HERE">
+                <File Id="filFABEED80D5D5652BC0BA2F689F6592E0" KeyPath="yes" Source="SourceDir\qmltooling\qmldbg_server.dll" />
+            </Component>
+            <Component Id="cmpCBA063886C021F79C883C543705AD5ED" Directory="dir9331125B8BF1F459D0AC9F91A6A2F779" Guid="PUT-GUID-HERE">
+                <File Id="fil0839ED3FA6DF360E9FE61C24F7241731" KeyPath="yes" Source="SourceDir\qmltooling\qmldbg_tcp.dll" />
+            </Component>
+            <Component Id="cmp945254526E795AC11DD727FA63082A1E" Directory="dir4EBCE940C66AD416A8F8D45D795DED89" Guid="PUT-GUID-HERE">
+                <File Id="filD59D8AFA0CDE0F773FE8451EA0C4A4AF" KeyPath="yes" Source="SourceDir\Qt5Compat\GraphicalEffects\BrightnessContrast.qml" />
+            </Component>
+            <Component Id="cmpFA306336B80423FCC8C0962DF02FC26D" Directory="dir4EBCE940C66AD416A8F8D45D795DED89" Guid="PUT-GUID-HERE">
+                <File Id="fil91FA03ACC514A8F70E33CD950EBC31A8" KeyPath="yes" Source="SourceDir\Qt5Compat\GraphicalEffects\Colorize.qml" />
+            </Component>
+            <Component Id="cmp482FD31DA01FAC7A356FBCE35BDFDB4F" Directory="dir4EBCE940C66AD416A8F8D45D795DED89" Guid="PUT-GUID-HERE">
+                <File Id="fil54CEE194AAB2B1FA1C981523D3A41937" KeyPath="yes" Source="SourceDir\Qt5Compat\GraphicalEffects\ColorOverlay.qml" />
+            </Component>
+            <Component Id="cmpF6043AA78938689C64CEE897318DA32F" Directory="dir4EBCE940C66AD416A8F8D45D795DED89" Guid="PUT-GUID-HERE">
+                <File Id="filC8833D6659696BA7801476772756C502" KeyPath="yes" Source="SourceDir\Qt5Compat\GraphicalEffects\ConicalGradient.qml" />
+            </Component>
+            <Component Id="cmp24C8C5A5AC88293EE054C9A86C9149CB" Directory="dir4EBCE940C66AD416A8F8D45D795DED89" Guid="PUT-GUID-HERE">
+                <File Id="fil555C55E2C7BBD35C2B95C89388217E64" KeyPath="yes" Source="SourceDir\Qt5Compat\GraphicalEffects\Desaturate.qml" />
+            </Component>
+            <Component Id="cmpE79F8C6AF8BF9B976459932AF82C6E4B" Directory="dir4EBCE940C66AD416A8F8D45D795DED89" Guid="PUT-GUID-HERE">
+                <File Id="filD15780CD824A15AADD69C7C4A8BEBF0B" KeyPath="yes" Source="SourceDir\Qt5Compat\GraphicalEffects\Displace.qml" />
+            </Component>
+            <Component Id="cmp05FC0A312BDE8E70E741C4C90C622639" Directory="dir4EBCE940C66AD416A8F8D45D795DED89" Guid="PUT-GUID-HERE">
+                <File Id="fil8B4BD8109771AFD1469381DBAA7CA77F" KeyPath="yes" Source="SourceDir\Qt5Compat\GraphicalEffects\DropShadow.qml" />
+            </Component>
+            <Component Id="cmpC85EEEC797E1E24770CC8316B9F4A7BB" Directory="dir4EBCE940C66AD416A8F8D45D795DED89" Guid="PUT-GUID-HERE">
+                <File Id="fil9D6AE9AF67863D8BD7CD68B27480E91E" KeyPath="yes" Source="SourceDir\Qt5Compat\GraphicalEffects\FastBlur.qml" />
+            </Component>
+            <Component Id="cmpB65602DB13223293DF84A1F03AB696C2" Directory="dir4EBCE940C66AD416A8F8D45D795DED89" Guid="PUT-GUID-HERE">
+                <File Id="filB06BFD9D6F49C51DC6EB0EBF39AC7207" KeyPath="yes" Source="SourceDir\Qt5Compat\GraphicalEffects\GammaAdjust.qml" />
+            </Component>
+            <Component Id="cmp529A90FD8AD0F278DAC442F8AECA9A91" Directory="dir4EBCE940C66AD416A8F8D45D795DED89" Guid="PUT-GUID-HERE">
+                <File Id="fil776C641CD1029493F23FAC8C55C2A9ED" KeyPath="yes" Source="SourceDir\Qt5Compat\GraphicalEffects\Glow.qml" />
+            </Component>
+            <Component Id="cmpDEAA0EFE8F05663D56AFC9A4D642B920" Directory="dir4EBCE940C66AD416A8F8D45D795DED89" Guid="PUT-GUID-HERE">
+                <File Id="fil60D6E98973B7B2F15997CBA972190713" KeyPath="yes" Source="SourceDir\Qt5Compat\GraphicalEffects\HueSaturation.qml" />
+            </Component>
+            <Component Id="cmp42C7CA719F60AFEBC2F0F8264E0D5DCC" Directory="dir4EBCE940C66AD416A8F8D45D795DED89" Guid="PUT-GUID-HERE">
+                <File Id="fil6747E895BD0148F7E016D09BBE90C467" KeyPath="yes" Source="SourceDir\Qt5Compat\GraphicalEffects\LevelAdjust.qml" />
+            </Component>
+            <Component Id="cmp17F26F9C315A657D76B33BE901D83C5E" Directory="dir4EBCE940C66AD416A8F8D45D795DED89" Guid="PUT-GUID-HERE">
+                <File Id="fil00C6E7DACEFFB748A88395184149B93C" KeyPath="yes" Source="SourceDir\Qt5Compat\GraphicalEffects\LinearGradient.qml" />
+            </Component>
+            <Component Id="cmp9B01EF285CAFA2B4BA31C7FDCA0F922B" Directory="dir4EBCE940C66AD416A8F8D45D795DED89" Guid="PUT-GUID-HERE">
+                <File Id="fil44A00B41A29C23382B450B339EA3DD28" KeyPath="yes" Source="SourceDir\Qt5Compat\GraphicalEffects\OpacityMask.qml" />
+            </Component>
+            <Component Id="cmp1163E46DF6F9C565A6CC1A9527814DD7" Directory="dir4EBCE940C66AD416A8F8D45D795DED89" Guid="PUT-GUID-HERE">
+                <File Id="fil48F932878AB695E8E49099B989416D53" KeyPath="yes" Source="SourceDir\Qt5Compat\GraphicalEffects\plugins.qmltypes" />
+            </Component>
+            <Component Id="cmpFADBFC6EAF30F0EF2C8B5E0E45A1E213" Directory="dir4EBCE940C66AD416A8F8D45D795DED89" Guid="PUT-GUID-HERE">
+                <File Id="fil91587697FF52370F7056F904CE90D781" KeyPath="yes" Source="SourceDir\Qt5Compat\GraphicalEffects\qmldir" />
+            </Component>
+            <Component Id="cmp229B96EC474CAA25D38BED9C228783F3" Directory="dir4EBCE940C66AD416A8F8D45D795DED89" Guid="PUT-GUID-HERE">
+                <File Id="filB26147ACAD14F15A286B32B7168E0A7E" KeyPath="yes" Source="SourceDir\Qt5Compat\GraphicalEffects\qtgraphicaleffectsplugin.dll" />
+            </Component>
+            <Component Id="cmpC93BCD1EAE827CC0DDD4D564F7E221DB" Directory="dir4EBCE940C66AD416A8F8D45D795DED89" Guid="PUT-GUID-HERE">
+                <File Id="fil6171FF7F9337ED0B77B1C9D481E039D7" KeyPath="yes" Source="SourceDir\Qt5Compat\GraphicalEffects\RadialGradient.qml" />
+            </Component>
+            <Component Id="cmp457D3F95070575E819AB610C1ABC0B4F" Directory="dir4EBCE940C66AD416A8F8D45D795DED89" Guid="PUT-GUID-HERE">
+                <File Id="fil1406D8C64F563242EE22DC5AC4C3DF4F" KeyPath="yes" Source="SourceDir\Qt5Compat\GraphicalEffects\RectangularGlow.qml" />
+            </Component>
+            <Component Id="cmpB5F5961D68AC44BC3F4D7F8E09C72DB5" Directory="dir4EBCE940C66AD416A8F8D45D795DED89" Guid="PUT-GUID-HERE">
+                <File Id="filBE5042CC2EDD6FB009F28089BCF6A2CE" KeyPath="yes" Source="SourceDir\Qt5Compat\GraphicalEffects\ThresholdMask.qml" />
+            </Component>
+            <Component Id="cmp2A4B284F625466C5F8FC12832EE7690A" Directory="dir24D778EAF9E4AD78E4448B53856DC5AB" Guid="PUT-GUID-HERE">
+                <File Id="filE6CD47174B801A0004C0DA94D744C83E" KeyPath="yes" Source="SourceDir\Qt5Compat\GraphicalEffects\private\FastGlow.qml" />
+            </Component>
+            <Component Id="cmpEF7DCF95664BF5E27244737858CCA436" Directory="dir24D778EAF9E4AD78E4448B53856DC5AB" Guid="PUT-GUID-HERE">
+                <File Id="fil97EC56ED8D0A2C1484FF186420628094" KeyPath="yes" Source="SourceDir\Qt5Compat\GraphicalEffects\private\qmldir" />
+            </Component>
+            <Component Id="cmpA77342C1842BFCA8360F66ECB1AA754A" Directory="dir24D778EAF9E4AD78E4448B53856DC5AB" Guid="PUT-GUID-HERE">
+                <File Id="fil335AC65D8F3D6494F03FE525ADC6C8C4" KeyPath="yes" Source="SourceDir\Qt5Compat\GraphicalEffects\private\qtgraphicaleffectsprivateplugin.dll" />
+            </Component>
+            <Component Id="cmpC295964BD8DF71C916C2FD3D4D11C6CA" Directory="dir70AFC0A9D84A710EC0128D3FED451CDC" Guid="PUT-GUID-HERE">
+                <File Id="filF951B868BF244A85F29053232BCCA952" KeyPath="yes" Source="SourceDir\QtQml\plugins.qmltypes" />
+            </Component>
+            <Component Id="cmp5561966E8256C014F16E7FE4EA658097" Directory="dir70AFC0A9D84A710EC0128D3FED451CDC" Guid="PUT-GUID-HERE">
+                <File Id="filFBC347544E9C5EA7F56ABE34B2A84784" KeyPath="yes" Source="SourceDir\QtQml\qmldir" />
+            </Component>
+            <Component Id="cmp6CB39FB81228DE9B93125C008D231598" Directory="dir70AFC0A9D84A710EC0128D3FED451CDC" Guid="PUT-GUID-HERE">
+                <File Id="filF0802A3CC352307D805625AA768049DB" KeyPath="yes" Source="SourceDir\QtQml\qmlplugin.dll" />
+            </Component>
+            <Component Id="cmp389F3B9DCF84CB30BEBE7F2C0099AC0B" Directory="dir49DBFE81A3C2546F0A0D7B91984B25A9" Guid="PUT-GUID-HERE">
+                <File Id="fil8C1068D9ED2B20D022CC9C9383812A1B" KeyPath="yes" Source="SourceDir\QtQml\Models\modelsplugin.dll" />
+            </Component>
+            <Component Id="cmp94CDC11A039D0103B4D7199420F7FC50" Directory="dir49DBFE81A3C2546F0A0D7B91984B25A9" Guid="PUT-GUID-HERE">
+                <File Id="filF8AEBF4DCF62371C28A61789BC9B60C9" KeyPath="yes" Source="SourceDir\QtQml\Models\plugins.qmltypes" />
+            </Component>
+            <Component Id="cmp598CA1BC124E50AC062FAD3723570CA8" Directory="dir49DBFE81A3C2546F0A0D7B91984B25A9" Guid="PUT-GUID-HERE">
+                <File Id="filC10013F340A191C09D677E614248B05B" KeyPath="yes" Source="SourceDir\QtQml\Models\qmldir" />
+            </Component>
+            <Component Id="cmp70D1A478617931123B81F6D2D2994212" Directory="dir040BCEF4BA1A47FD347F1E983F275641" Guid="PUT-GUID-HERE">
+                <File Id="fil4B45AC3C5F767355995043201ED724EF" KeyPath="yes" Source="SourceDir\QtQml\WorkerScript\plugins.qmltypes" />
+            </Component>
+            <Component Id="cmpA199263755A832BCAA8FD26E7875C730" Directory="dir040BCEF4BA1A47FD347F1E983F275641" Guid="PUT-GUID-HERE">
+                <File Id="fil8643DA009D020149E2D8F76BD4D0A3BD" KeyPath="yes" Source="SourceDir\QtQml\WorkerScript\qmldir" />
+            </Component>
+            <Component Id="cmpC5535C974DD300AF1B3368991C3F9998" Directory="dir040BCEF4BA1A47FD347F1E983F275641" Guid="PUT-GUID-HERE">
+                <File Id="fil5FA064C3A03EBF4827F2FF689E6FF52A" KeyPath="yes" Source="SourceDir\QtQml\WorkerScript\workerscriptplugin.dll" />
+            </Component>
+            <Component Id="cmpCD8F9C674B06B07EDDDB260AF148377F" Directory="dirDF3BB8A52CE36475D7FA56568CBEA7A4" Guid="PUT-GUID-HERE">
+                <File Id="fil3B585A05F1CF7AE5DAA29FC56086FE84" KeyPath="yes" Source="SourceDir\QtQml\XmlListModel\plugins.qmltypes" />
+            </Component>
+            <Component Id="cmp5F2CBC62909D55884F306D0EAA36BE6A" Directory="dirDF3BB8A52CE36475D7FA56568CBEA7A4" Guid="PUT-GUID-HERE">
+                <File Id="filC0277244B0C30937CD5F818F183F92AB" KeyPath="yes" Source="SourceDir\QtQml\XmlListModel\qmldir" />
+            </Component>
+            <Component Id="cmpBAA1AF87AC60BB0B0EDF8484233CA948" Directory="dirDF3BB8A52CE36475D7FA56568CBEA7A4" Guid="PUT-GUID-HERE">
+                <File Id="fil1875712CD39FA24B2A06C0F244E29789" KeyPath="yes" Source="SourceDir\QtQml\XmlListModel\qmlxmllistmodelplugin.dll" />
+            </Component>
+            <Component Id="cmp58020242039257006BB9832E84C98670" Directory="dir9D005E7F3893047E4557D6C5273ADD9E" Guid="PUT-GUID-HERE">
+                <File Id="fil3714BF5922F4FA9E95484B97BCFD40A6" KeyPath="yes" Source="SourceDir\QtQuick\plugins.qmltypes" />
+            </Component>
+            <Component Id="cmpE1A9F2278C9A7DAA74FE5906A844F954" Directory="dir9D005E7F3893047E4557D6C5273ADD9E" Guid="PUT-GUID-HERE">
+                <File Id="fil356BD72CA2CACF4DB94370FFCA1E4E2F" KeyPath="yes" Source="SourceDir\QtQuick\qmldir" />
+            </Component>
+            <Component Id="cmpAB665C492211D9E739C618628990AFD5" Directory="dir9D005E7F3893047E4557D6C5273ADD9E" Guid="PUT-GUID-HERE">
+                <File Id="filCE359623B7F5482511466CB0C2BC02B2" KeyPath="yes" Source="SourceDir\QtQuick\qtquick2plugin.dll" />
+            </Component>
+            <Component Id="cmp498B55DCBFF36960DCA912CEE847D696" Directory="dirDCD6F45EC68D7ABC2968E38BA6C2E3B4" Guid="PUT-GUID-HERE">
+                <File Id="fil23E5D4EB38F16653E2987545CEF878EC" KeyPath="yes" Source="SourceDir\QtQuick\Controls\plugins.qmltypes" />
+            </Component>
+            <Component Id="cmp562DB404A131AD7C3086FB3CD819E814" Directory="dirDCD6F45EC68D7ABC2968E38BA6C2E3B4" Guid="PUT-GUID-HERE">
+                <File Id="fil65480B31AECCDDD6B12C33E8A26E3AEF" KeyPath="yes" Source="SourceDir\QtQuick\Controls\qmldir" />
+            </Component>
+            <Component Id="cmp1D9FD82E27172D3A6FA9FEA184DF2E3E" Directory="dirDCD6F45EC68D7ABC2968E38BA6C2E3B4" Guid="PUT-GUID-HERE">
+                <File Id="filFDC51E1BC2B0F884DA7F5CE0B5F0DDB4" KeyPath="yes" Source="SourceDir\QtQuick\Controls\qtquickcontrols2plugin.dll" />
+            </Component>
+            <Component Id="cmp39E3BF441C84CD2E294D8E7F57365C93" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="PUT-GUID-HERE">
+                <File Id="filE0405A96CB6039A810D7076771307605" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\AbstractButton.qml" />
+            </Component>
+            <Component Id="cmp13F47DE57D0E880784A1ACAFD6FFE678" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="PUT-GUID-HERE">
+                <File Id="filE5C7E166D6D67CDF9A4BB87C0709E983" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\Action.qml" />
+            </Component>
+            <Component Id="cmpAF1A8723D2F99700E1A767A114FD58E4" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="PUT-GUID-HERE">
+                <File Id="filD615F4D9A187866255668509CDDBA0C0" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\ActionGroup.qml" />
+            </Component>
+            <Component Id="cmp7D1CE48AFA1D68D8112E46EC712B1A2A" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="PUT-GUID-HERE">
+                <File Id="filC133DCD99E549792CC136B39782E9970" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\ApplicationWindow.qml" />
+            </Component>
+            <Component Id="cmp3226F3C6C4139EFF5FF083C50454526E" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="PUT-GUID-HERE">
+                <File Id="fil3014C4EBA2ADFECBD067E505ED839770" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\BusyIndicator.qml" />
+            </Component>
+            <Component Id="cmpAFF2E0688F9EEFE57DC3D595B7480AC4" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="PUT-GUID-HERE">
+                <File Id="filDABA4C7F42E824B3BD5B6A75C2683B98" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\Button.qml" />
+            </Component>
+            <Component Id="cmpFEBC544096B496ED497C50CEC586F991" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="PUT-GUID-HERE">
+                <File Id="filD0BCB194277908B7652753F9708A9CAC" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\ButtonGroup.qml" />
+            </Component>
+            <Component Id="cmp460799C7403500F008F382231E93D92C" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="PUT-GUID-HERE">
+                <File Id="fil8334FFE9C02650DEC329168E3D23CD86" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\CheckBox.qml" />
+            </Component>
+            <Component Id="cmpC97CE0B6EC4977EE6BFA9695E31E1E8F" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="PUT-GUID-HERE">
+                <File Id="fil0613DD6744734519C77B1AD39A738DA9" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\CheckDelegate.qml" />
+            </Component>
+            <Component Id="cmp1EEE42D5F042D85B2ED628CC367864CC" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="PUT-GUID-HERE">
+                <File Id="fil6772157AD78D3BFDD3D4CF9F2B1FC8A9" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\ComboBox.qml" />
+            </Component>
+            <Component Id="cmpB7161814C193537B0678C47FCCEE0906" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="PUT-GUID-HERE">
+                <File Id="fil1518A26590DFC2C5E545B7BD6789151B" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\Container.qml" />
+            </Component>
+            <Component Id="cmp444DFB6BF11F2A5524079C8B42790CED" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="PUT-GUID-HERE">
+                <File Id="filAF046240483A1147D2F772820458E4B5" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\Control.qml" />
+            </Component>
+            <Component Id="cmp93071B0BE396A0BE670EE327358C3BE5" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="PUT-GUID-HERE">
+                <File Id="fil8370F717D3A289CB18A871E133552D67" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\DelayButton.qml" />
+            </Component>
+            <Component Id="cmpEFE249C5563CA612E610026CC13993FF" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="PUT-GUID-HERE">
+                <File Id="filC3A23304C9A22DDA6209D1A4B465CA3E" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\Dial.qml" />
+            </Component>
+            <Component Id="cmp9AED433DBDF1B120D1C954030112107F" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="PUT-GUID-HERE">
+                <File Id="fil324D39A993BE8D40514A4FC971246D10" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\Dialog.qml" />
+            </Component>
+            <Component Id="cmp53251CA3F634BC98009E538529819399" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="PUT-GUID-HERE">
+                <File Id="fil77B1BA93A52DE992ACF37A8414873897" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\DialogButtonBox.qml" />
+            </Component>
+            <Component Id="cmpBC47B2660BE28F19D26D82EEA61AC315" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="PUT-GUID-HERE">
+                <File Id="filE6E031847C6472BE22ADE7E12049CCC7" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\Drawer.qml" />
+            </Component>
+            <Component Id="cmpDAF2DA7B94D53738426273877D5C8EAA" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="PUT-GUID-HERE">
+                <File Id="fil6B983CAD6D4B7CF30314AD6141E5F008" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\Frame.qml" />
+            </Component>
+            <Component Id="cmpB8B6E0189F3A99B18BFEE9C2FBB6E7CB" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="PUT-GUID-HERE">
+                <File Id="fil82A44412EC8B48B71850C6F39776DDBA" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\GroupBox.qml" />
+            </Component>
+            <Component Id="cmp28C29EF45496D5E56CC019D1C804BDC6" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="PUT-GUID-HERE">
+                <File Id="filC3D18EE9C070AF77C19B6483A78F96E7" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\HorizontalHeaderView.qml" />
+            </Component>
+            <Component Id="cmp096A72C75835E73B556FBE045151AD84" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="PUT-GUID-HERE">
+                <File Id="fil6927D552E8E746D2564A9093BC6F4010" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\ItemDelegate.qml" />
+            </Component>
+            <Component Id="cmpCB3B27B649765931CAFCDB9558C89CD7" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="PUT-GUID-HERE">
+                <File Id="fil45E2AA74D45B3939CAAA4AE2FC5217F0" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\Label.qml" />
+            </Component>
+            <Component Id="cmpA1DFDF9DBA363A02E1296B9C7F7E028A" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="PUT-GUID-HERE">
+                <File Id="fil2A5852AC62177CA1F4D7C90418DF3B99" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\Menu.qml" />
+            </Component>
+            <Component Id="cmp1D7E1ACADF1DB55930E04973075327E2" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="PUT-GUID-HERE">
+                <File Id="fil6872870244C4DA24309E3E140F1BBF10" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\MenuBar.qml" />
+            </Component>
+            <Component Id="cmp00F32E1E79E785E7900A6BE78AB73DD0" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="PUT-GUID-HERE">
+                <File Id="filDFEEB007AC6245AA515CFEC1D3CC32E6" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\MenuBarItem.qml" />
+            </Component>
+            <Component Id="cmp96A0A6316688992E2EEB96041D4B1124" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="PUT-GUID-HERE">
+                <File Id="filADA6001ECBDDA7BD3196AE733EF95BC1" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\MenuItem.qml" />
+            </Component>
+            <Component Id="cmpD97C9727C2F49C32AE6E794FCA617F3B" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="PUT-GUID-HERE">
+                <File Id="filDE539B30D628C3A7AA24F1193F83AAC7" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\MenuSeparator.qml" />
+            </Component>
+            <Component Id="cmp3F2A3045CD47F5B92A65055853047DAD" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="PUT-GUID-HERE">
+                <File Id="fil2F33928FB0D2CE37679B6D6AE9A653CD" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\Page.qml" />
+            </Component>
+            <Component Id="cmp32620ADB561085068DBDF1E93E885BBE" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="PUT-GUID-HERE">
+                <File Id="filF9BBCBD4094D68CBEC82EE19772E960C" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\PageIndicator.qml" />
+            </Component>
+            <Component Id="cmp176781C50AD2C199CFF82CD69AA557A6" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="PUT-GUID-HERE">
+                <File Id="fil0242C2206149CFF1D785B06488D2BDDF" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\Pane.qml" />
+            </Component>
+            <Component Id="cmpB910C2A89C678D4DFF1C10127CEF1871" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="PUT-GUID-HERE">
+                <File Id="fil795F34AE8CAE09C1100923382BBCF57D" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\plugins.qmltypes" />
+            </Component>
+            <Component Id="cmp7C08CB1B9A52F1E86769F7046A400A85" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="PUT-GUID-HERE">
+                <File Id="filCA23B23206C1E01F6F148AC09F04EE65" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\Popup.qml" />
+            </Component>
+            <Component Id="cmpB7604AC467FDB08CA5F897B461F249F6" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="PUT-GUID-HERE">
+                <File Id="fil0B7A072F8416E2DA85B235F68AFDEE1A" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\ProgressBar.qml" />
+            </Component>
+            <Component Id="cmpF64844BE891499A5EBA53BD112979B45" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="PUT-GUID-HERE">
+                <File Id="filF4BF6B3715B227D16B8010B5BF41CF54" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\qmldir" />
+            </Component>
+            <Component Id="cmp28A47D855FFEC3C59FA27B23A56F3ADA" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="PUT-GUID-HERE">
+                <File Id="fil516C1F96FA6E0D0445918BE9615FDEF2" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\qtquickcontrols2basicstyleplugin.dll" />
+            </Component>
+            <Component Id="cmp7E3F340C4EB0A16568754701511B9C1D" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="PUT-GUID-HERE">
+                <File Id="fil9401262A1FBD0350983CAA7A69128EEA" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\RadioButton.qml" />
+            </Component>
+            <Component Id="cmpAE9517901AB3AD27D048A7EDE377222D" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="PUT-GUID-HERE">
+                <File Id="fil58508D4C722A6050E3A6C6AFB09F14AD" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\RadioDelegate.qml" />
+            </Component>
+            <Component Id="cmpE3157FE6CF15D4C1DD224852360A0915" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="PUT-GUID-HERE">
+                <File Id="fil0D637BC81D28F9AC37720B648A0FFE22" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\RangeSlider.qml" />
+            </Component>
+            <Component Id="cmpC24EDDD8484051DDEF1687FFF0C4FE14" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="PUT-GUID-HERE">
+                <File Id="filF9825B69059C4512381CBB1ED38CE677" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\RoundButton.qml" />
+            </Component>
+            <Component Id="cmp44A277143BAED4BF0D4C12A45B62966A" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="PUT-GUID-HERE">
+                <File Id="fil223FE71601C3052DB16C28922C97C3CD" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\ScrollBar.qml" />
+            </Component>
+            <Component Id="cmp1A37E19BA0D3D569C2F4A720EC581A72" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="PUT-GUID-HERE">
+                <File Id="fil3E500552913D6E5EFB882701A0640471" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\ScrollIndicator.qml" />
+            </Component>
+            <Component Id="cmp6B503919F5459E5A8A6ACEA8D2EB8873" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="PUT-GUID-HERE">
+                <File Id="filA27E268B9A524AA1B4FD47712C8BF7B2" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\ScrollView.qml" />
+            </Component>
+            <Component Id="cmpAEAB0E732A86AB87EACD7B7ABCDE202F" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="PUT-GUID-HERE">
+                <File Id="fil49FCC757A5412B4514C646558EC3AD3A" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\SelectionRectangle.qml" />
+            </Component>
+            <Component Id="cmp3A17AF10F75FC68978D74CDFC7397BD9" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="PUT-GUID-HERE">
+                <File Id="fil1D41E82D87B97B78E7045FDE970EE5B8" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\Slider.qml" />
+            </Component>
+            <Component Id="cmp8EBF1FC1A1508143AED171ECD969A21A" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="PUT-GUID-HERE">
+                <File Id="fil0286392B1E4AE12D681EC673B0FFF0DD" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\SpinBox.qml" />
+            </Component>
+            <Component Id="cmpADF688B243C6067D34B7121262F45B7D" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="PUT-GUID-HERE">
+                <File Id="fil24D6F394262FCDAEA68149CF43C63220" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\SplitView.qml" />
+            </Component>
+            <Component Id="cmp14B3F918468BAC922D69BC9C0A0BF8F4" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="PUT-GUID-HERE">
+                <File Id="fil37A3D716490D601495E75F36C1616D98" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\StackView.qml" />
+            </Component>
+            <Component Id="cmp6C88EEC02A082F4AB67C01AA087713B9" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="PUT-GUID-HERE">
+                <File Id="fil5902D6B5881EC5198DC87C4B37E6F773" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\SwipeDelegate.qml" />
+            </Component>
+            <Component Id="cmpA52455B6F63A98C4A8C1C7B7FEF6697D" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="PUT-GUID-HERE">
+                <File Id="filDC4854131B09909A15B41D48D11A10FD" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\SwipeView.qml" />
+            </Component>
+            <Component Id="cmp4B0D452072AC6C23F73AD5197C1E306C" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="PUT-GUID-HERE">
+                <File Id="fil3FA59E20FEAAD096E2ACFECC65DA05E6" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\Switch.qml" />
+            </Component>
+            <Component Id="cmp94A4D7BA55C18ABFE7169AFE56222542" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="PUT-GUID-HERE">
+                <File Id="fil01B1917034EF7069154B5D74AAA17DEF" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\SwitchDelegate.qml" />
+            </Component>
+            <Component Id="cmp24D052E6B20BE5C50D38CECB8698C9CD" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="PUT-GUID-HERE">
+                <File Id="filB530256DFF8FD3F7BFCBA01FF22B4973" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\TabBar.qml" />
+            </Component>
+            <Component Id="cmp4869F0C47A93CBBC8D904461A59D4B51" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="PUT-GUID-HERE">
+                <File Id="filEDF1726BE8620A4A227FECF9CDAA17CE" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\TabButton.qml" />
+            </Component>
+            <Component Id="cmpE7926A6DAB570EF55A188C06D59B0152" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="PUT-GUID-HERE">
+                <File Id="fil2403A6F074DB07174C58C6B3560CE6A3" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\TextArea.qml" />
+            </Component>
+            <Component Id="cmp65D8852BE8B18D40EDC1D4ED531BA5F3" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="PUT-GUID-HERE">
+                <File Id="fil65CACB8AF03F1560DF05FA1990AE23F2" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\TextField.qml" />
+            </Component>
+            <Component Id="cmpEE0678D57E918B0B86AF94882FFC00AE" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="PUT-GUID-HERE">
+                <File Id="filB7BB3860784F05376030AB01B762E81F" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\ToolBar.qml" />
+            </Component>
+            <Component Id="cmp5629792356E2195B748193DDF721DF76" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="PUT-GUID-HERE">
+                <File Id="fil300EE75FDB68B619C4E282A19E61F95F" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\ToolButton.qml" />
+            </Component>
+            <Component Id="cmp1CD4D9E7E53660B7F864EECB4F6F1686" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="PUT-GUID-HERE">
+                <File Id="filF8B7C7B797BEA766D781CBCAE9CB2067" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\ToolSeparator.qml" />
+            </Component>
+            <Component Id="cmpF5C63A567BF77BC306496620B3B40E0B" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="PUT-GUID-HERE">
+                <File Id="fil47E23689D76FAF8447A14EBCF09B819C" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\ToolTip.qml" />
+            </Component>
+            <Component Id="cmpCCADBB1D8335A08277A040F9120BF10C" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="PUT-GUID-HERE">
+                <File Id="fil2B65A45E3538C6946ADBF329ED1E8B8C" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\Tumbler.qml" />
+            </Component>
+            <Component Id="cmp6D6AFFD2FE505287A8B4899D905225F8" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="PUT-GUID-HERE">
+                <File Id="filEB3713EBC2AA05DF3AE5C80240CA5F29" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\VerticalHeaderView.qml" />
+            </Component>
+            <Component Id="cmpD550D210E3DB52D8334D7EE916A7971B" Directory="dirDBD23FE4F11ED29A51E2F3787280CC12" Guid="PUT-GUID-HERE">
+                <File Id="fil0DA76C9C1FB7B19A9C1250D1606F4375" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\impl\plugins.qmltypes" />
+            </Component>
+            <Component Id="cmp8853A6E2FEDF5C48CE3538ECEC698070" Directory="dirDBD23FE4F11ED29A51E2F3787280CC12" Guid="PUT-GUID-HERE">
+                <File Id="filCEE3AF66760C2A693F631DBC19C11FA9" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\impl\qmldir" />
+            </Component>
+            <Component Id="cmp2EDF0CFA2C7BEF541CE841B6E4ED8ABE" Directory="dirDBD23FE4F11ED29A51E2F3787280CC12" Guid="PUT-GUID-HERE">
+                <File Id="filF8813763A2BC55C52576E4872E40E32B" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\impl\qtquickcontrols2basicstyleimplplugin.dll" />
+            </Component>
+            <Component Id="cmp45B50EB84A857B8F62EF4529B3456513" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="PUT-GUID-HERE">
+                <File Id="filD12AED5ADB2C9E2415789A94E4A515D2" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\ApplicationWindow.qml" />
+            </Component>
+            <Component Id="cmpF6BB6E5DECD94FBCEE2F3B3B7A31DEF4" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="PUT-GUID-HERE">
+                <File Id="fil3799FE4CE54EEE23DEA152F3356D8BF2" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\BusyIndicator.qml" />
+            </Component>
+            <Component Id="cmp6C0DC0D93562BD0BB051C3BA68D38BAB" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="PUT-GUID-HERE">
+                <File Id="fil7BBC045C60D7D4018D580735F4511B2D" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\Button.qml" />
+            </Component>
+            <Component Id="cmp966432BC1820394B57C176A5165A995A" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="PUT-GUID-HERE">
+                <File Id="fil7AD480BD39EB266912C5EC7D2DF5358D" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\CheckBox.qml" />
+            </Component>
+            <Component Id="cmp8BE9AC25130F427CDEC3A445EBBE3B17" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="PUT-GUID-HERE">
+                <File Id="filF6F09CE4F2FE30F87734FC2AAAD0174D" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\CheckDelegate.qml" />
+            </Component>
+            <Component Id="cmp7C2F1E3031A9D19AFB668BE58F73EC98" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="PUT-GUID-HERE">
+                <File Id="fil453585CF257BD3008D8C9E17D48292E3" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\ComboBox.qml" />
+            </Component>
+            <Component Id="cmp451EFDC232DFFD28BD6B7B016C0C054A" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="PUT-GUID-HERE">
+                <File Id="filE19B7125D874977D503ECC18D17CF67E" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\DelayButton.qml" />
+            </Component>
+            <Component Id="cmpF70E05446EBB48ADAC31F492DC97DAFB" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="PUT-GUID-HERE">
+                <File Id="fil376C858BD505AE1A19E77664F43475C4" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\Dial.qml" />
+            </Component>
+            <Component Id="cmp836E33FAF9164C5A638CA34211DB1732" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="PUT-GUID-HERE">
+                <File Id="filA0A0234994D4A5107C2663D39BFE7ACC" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\Dialog.qml" />
+            </Component>
+            <Component Id="cmp5CC46A434C01817BCBD3896BCFD43FD5" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="PUT-GUID-HERE">
+                <File Id="filDE6AC31443AD9360EA2E04C68E3CDFB5" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\DialogButtonBox.qml" />
+            </Component>
+            <Component Id="cmp2F7F77BBA374BBBAAA8FFC0B160FC6CF" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="PUT-GUID-HERE">
+                <File Id="fil0F35F15A0401B7776532AA3340B6AAE6" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\Drawer.qml" />
+            </Component>
+            <Component Id="cmp0617FA4E631963809B63F4A246C8C44E" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="PUT-GUID-HERE">
+                <File Id="filF1968E83BA39945E7BB9C41DAE91308A" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\Frame.qml" />
+            </Component>
+            <Component Id="cmpDE4EE6DF5AFB0CC72D73C5AECB1488A1" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="PUT-GUID-HERE">
+                <File Id="fil030ABBC28FB009966081FFD1F5C79C2C" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\GroupBox.qml" />
+            </Component>
+            <Component Id="cmpCA5CDA48E3ECB805C8142591DDAB8570" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="PUT-GUID-HERE">
+                <File Id="filB6A558B0F0C553040F86701ADB75F341" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\HorizontalHeaderView.qml" />
+            </Component>
+            <Component Id="cmpED9F11A19AB496483C376EC280BCB16D" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="PUT-GUID-HERE">
+                <File Id="filFDF2A44F70022D7058AB02C41E696820" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\ItemDelegate.qml" />
+            </Component>
+            <Component Id="cmp519201BC4E40B8AED1A7313C4725FA7F" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="PUT-GUID-HERE">
+                <File Id="fil4FA1DA0DC3F645A4141E937B3ECBDAD0" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\Label.qml" />
+            </Component>
+            <Component Id="cmpE8C184C891DA740B62B6535CC76C1F40" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="PUT-GUID-HERE">
+                <File Id="filDC06C91EC735B746012F3CCD99CBA9C4" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\Menu.qml" />
+            </Component>
+            <Component Id="cmp64D872D8577E18D373DCECA1F7619FA2" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="PUT-GUID-HERE">
+                <File Id="filF2CFF720B27A02AB20775878B64CB545" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\MenuBar.qml" />
+            </Component>
+            <Component Id="cmpBDFD410CAB76FB8E3B925DF858CAEF42" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="PUT-GUID-HERE">
+                <File Id="fil2B0B04F76D6C8FF35BF648B0333FCD7D" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\MenuBarItem.qml" />
+            </Component>
+            <Component Id="cmpCDD1BC254A0AD9C08C60D51FE632703E" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="PUT-GUID-HERE">
+                <File Id="fil69B62EDDD47BD323833EBE5605E8EF0E" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\MenuItem.qml" />
+            </Component>
+            <Component Id="cmp0B895B34812F9CCD138C1D6E639087B0" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="PUT-GUID-HERE">
+                <File Id="fil0DAE410DDC1D6076414745664B1A9C75" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\MenuSeparator.qml" />
+            </Component>
+            <Component Id="cmpCAA8935F0B8F24E84D9C7625F65A1CE1" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="PUT-GUID-HERE">
+                <File Id="filB1853C6038F2C85701670C2E70D0CA96" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\Page.qml" />
+            </Component>
+            <Component Id="cmp789B9EA1BFC24059135A1AC3F2ABE291" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="PUT-GUID-HERE">
+                <File Id="fil9A83D7A8CF9E7D1C3545F7E3AE1033D8" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\PageIndicator.qml" />
+            </Component>
+            <Component Id="cmpD5D68F5560DA456F36CADB3BDFA1B780" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="PUT-GUID-HERE">
+                <File Id="fil9B6DB44693F30FF014DE62FD7FB40222" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\Pane.qml" />
+            </Component>
+            <Component Id="cmp34A56673371F4AF3995953F37A132F06" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="PUT-GUID-HERE">
+                <File Id="fil864DA3DD8B02F28538D5A0B251D6D6F5" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\plugins.qmltypes" />
+            </Component>
+            <Component Id="cmp50281D8A1CF30DB8B193E606005162EC" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="PUT-GUID-HERE">
+                <File Id="filF4E3AC2154FF8B451D8D35F6ED647873" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\Popup.qml" />
+            </Component>
+            <Component Id="cmp88C6959F45BE2744A66C1D548580CE10" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="PUT-GUID-HERE">
+                <File Id="fil6563E751A8B56CC0FD2E8B94E7E3427B" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\ProgressBar.qml" />
+            </Component>
+            <Component Id="cmp027CBFF047CAEAB709547731194901F5" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="PUT-GUID-HERE">
+                <File Id="fil784EDC2FF7EB8275EBF00057CD18E0E8" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\qmldir" />
+            </Component>
+            <Component Id="cmpFB4520B28A546E5B869862913E252B65" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="PUT-GUID-HERE">
+                <File Id="filAD426ED04EAF5BE201C01C9AF5AA8E21" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\qtquickcontrols2fusionstyleplugin.dll" />
+            </Component>
+            <Component Id="cmpC40D621B55A5A716AADB80CC3C1DF229" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="PUT-GUID-HERE">
+                <File Id="fil5A94630CD9E744D5D464EE97F9548299" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\RadioButton.qml" />
+            </Component>
+            <Component Id="cmpA94CE1EE69C9965967436C719EA9213B" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="PUT-GUID-HERE">
+                <File Id="fil08424268E1754B8970DD70E6FC3CBC3C" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\RadioDelegate.qml" />
+            </Component>
+            <Component Id="cmp6DCC1FE0E816F31EAD56506065BEA7F3" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="PUT-GUID-HERE">
+                <File Id="fil6F747DEB1F167D7556931A15405C1EB5" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\RangeSlider.qml" />
+            </Component>
+            <Component Id="cmp77B3E98FCB0336A3FB9AE25C345ADFE3" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="PUT-GUID-HERE">
+                <File Id="fil008F33B3FA66479839C8B451ED8EDE78" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\RoundButton.qml" />
+            </Component>
+            <Component Id="cmp513B048AA96D48CEFEDB8AFF9B55BE8C" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="PUT-GUID-HERE">
+                <File Id="filFFDA65058EF9380014996AA8238C0755" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\ScrollBar.qml" />
+            </Component>
+            <Component Id="cmp9F019135B1D40203DECC8FC0D1006F7C" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="PUT-GUID-HERE">
+                <File Id="fil7CA907CC4753D913CB514E3A6C2D39C0" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\ScrollIndicator.qml" />
+            </Component>
+            <Component Id="cmp72B0CF8FF52FCE6BF04B4BF080992DB9" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="PUT-GUID-HERE">
+                <File Id="filFA0830B53CA178784DF6346244A6B370" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\ScrollView.qml" />
+            </Component>
+            <Component Id="cmp3585F3AD5A6C9106FB8A68B358F4D049" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="PUT-GUID-HERE">
+                <File Id="filBD72FEBD06396F4A86949A38C9B7C1DF" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\SelectionRectangle.qml" />
+            </Component>
+            <Component Id="cmpA5543D5C64A49E8697E2A87F769D4AE9" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="PUT-GUID-HERE">
+                <File Id="fil71F6D64EE99309A0281571FEC0194EE8" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\Slider.qml" />
+            </Component>
+            <Component Id="cmp6B6B735B21C0358A6E42CD273BFF60CD" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="PUT-GUID-HERE">
+                <File Id="fil736D187093D1AF942C5C590A16FDD3FF" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\SpinBox.qml" />
+            </Component>
+            <Component Id="cmp011A9101A35BAEDFAAB317FB44B5E6B9" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="PUT-GUID-HERE">
+                <File Id="fil07F5DAE9EBCA70EC370A2339E4156BAD" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\SplitView.qml" />
+            </Component>
+            <Component Id="cmpDA358FA4C827D79CFEAD11AAA8A9B061" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="PUT-GUID-HERE">
+                <File Id="filDFFC5E566607F8B4CA48C5BB921A78C0" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\SwipeDelegate.qml" />
+            </Component>
+            <Component Id="cmpFF96327A6553E712825496431B0A7AB0" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="PUT-GUID-HERE">
+                <File Id="fil209EF9E49BCBE6329844DE77D5E266AB" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\Switch.qml" />
+            </Component>
+            <Component Id="cmp4D1313B7A2F7741E4B402C2731A57661" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="PUT-GUID-HERE">
+                <File Id="filFBC15FA939122A9ABF71555B56A6D9F0" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\SwitchDelegate.qml" />
+            </Component>
+            <Component Id="cmp6CA898D45BF72C630F2632FD2EC9DFC9" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="PUT-GUID-HERE">
+                <File Id="fil926125BB768EAFA858C38DDC25B15227" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\TabBar.qml" />
+            </Component>
+            <Component Id="cmpB9CFDD5F1084C1CEE30776008F83259D" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="PUT-GUID-HERE">
+                <File Id="filB2B1E761DCED7BD1711107F2151C1B88" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\TabButton.qml" />
+            </Component>
+            <Component Id="cmp9A1943D41E4A3351F06735BFED56CE01" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="PUT-GUID-HERE">
+                <File Id="fil2CA95E018FA9B738B0F1B2C42F66F070" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\TextArea.qml" />
+            </Component>
+            <Component Id="cmpAB62CDBE21637A49145F438CAEEE71FF" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="PUT-GUID-HERE">
+                <File Id="fil17898560EE4F87AD357EAA697F579747" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\TextField.qml" />
+            </Component>
+            <Component Id="cmp3A37860F05D23962BD23C4596F828989" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="PUT-GUID-HERE">
+                <File Id="fil69C8C1B9B33621669AA4D2E6A0F80CC5" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\ToolBar.qml" />
+            </Component>
+            <Component Id="cmp118A0CC4F4677FB6C1014C64AAAD38FD" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="PUT-GUID-HERE">
+                <File Id="fil94969C7CF55378593CE7952AB696F7EC" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\ToolButton.qml" />
+            </Component>
+            <Component Id="cmp21D95CD727ED9F6FB17642549B08202A" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="PUT-GUID-HERE">
+                <File Id="fil8FC9CE6BE23A0BF7AF7FE4CEFE5C05AD" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\ToolSeparator.qml" />
+            </Component>
+            <Component Id="cmpDE8FA2F547F246F03228627DAA35B3E3" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="PUT-GUID-HERE">
+                <File Id="filF1920823B38E8638793F6BFD066E17E5" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\ToolTip.qml" />
+            </Component>
+            <Component Id="cmp85462F0F4DC060B6E43D59D95C2EAF3E" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="PUT-GUID-HERE">
+                <File Id="filFE26B68F72738DDDBFC6F5C5E53EC072" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\Tumbler.qml" />
+            </Component>
+            <Component Id="cmp316981E3BB804C288B6B9B7ADD2838C8" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="PUT-GUID-HERE">
+                <File Id="fil50364DA66061827762CA321EF70210E4" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\VerticalHeaderView.qml" />
+            </Component>
+            <Component Id="cmpF804F89B59165412AFB541FB764F3DC7" Directory="dir48925D264F208B9FCBF827D59CF75478" Guid="PUT-GUID-HERE">
+                <File Id="filC789BD92FF5510416E35EAA91F7DC547" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\impl\ButtonPanel.qml" />
+            </Component>
+            <Component Id="cmpF321754AA0E0CE543797EDA62FFF8639" Directory="dir48925D264F208B9FCBF827D59CF75478" Guid="PUT-GUID-HERE">
+                <File Id="filA4F1AA4B0800C4003D8176BBED77831B" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\impl\CheckIndicator.qml" />
+            </Component>
+            <Component Id="cmp42C78DF19CC84929317A806122FE1E3D" Directory="dir48925D264F208B9FCBF827D59CF75478" Guid="PUT-GUID-HERE">
+                <File Id="filD74D59820D4E6AAA1DFC2F4A96D22A3C" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\impl\plugins.qmltypes" />
+            </Component>
+            <Component Id="cmp1D28DF87F3986CF924B956D11C487A09" Directory="dir48925D264F208B9FCBF827D59CF75478" Guid="PUT-GUID-HERE">
+                <File Id="fil1FFDB110C2E8797FBEC3DBF2B92DA6A7" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\impl\qmldir" />
+            </Component>
+            <Component Id="cmp4B00A6DD6F32AF3A9063A83F168D951D" Directory="dir48925D264F208B9FCBF827D59CF75478" Guid="PUT-GUID-HERE">
+                <File Id="filBCEA08117F1F4EDEEBB15E9994113AA3" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\impl\qtquickcontrols2fusionstyleimplplugin.dll" />
+            </Component>
+            <Component Id="cmpEF0E1D1EFA3D0E69B9C4DB528D05CEE7" Directory="dir48925D264F208B9FCBF827D59CF75478" Guid="PUT-GUID-HERE">
+                <File Id="fil505D3898B47D220F4B05CE551525B83C" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\impl\RadioIndicator.qml" />
+            </Component>
+            <Component Id="cmp031FC0E00FCB88CFC82FE5DF334FFA2C" Directory="dir48925D264F208B9FCBF827D59CF75478" Guid="PUT-GUID-HERE">
+                <File Id="filF2ACFC9DD60EAF71D280600602CCFABE" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\impl\SliderGroove.qml" />
+            </Component>
+            <Component Id="cmpA20A5B36D761A799EB3C0428B1343D7C" Directory="dir48925D264F208B9FCBF827D59CF75478" Guid="PUT-GUID-HERE">
+                <File Id="filAD78AEF1B5063C1F77A1EFF416D2B6FA" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\impl\SliderHandle.qml" />
+            </Component>
+            <Component Id="cmpC70464974B77DC61CFF9DC89E1E9A73D" Directory="dir48925D264F208B9FCBF827D59CF75478" Guid="PUT-GUID-HERE">
+                <File Id="filCE7F9028197C38EC9B0684E40BA5D779" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\impl\SwitchIndicator.qml" />
+            </Component>
+            <Component Id="cmp0AE92B0F56F7065ED92C3ADF99303E22" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="PUT-GUID-HERE">
+                <File Id="filB767EF37381F7141C7A3DCAC39840B77" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\ApplicationWindow.qml" />
+            </Component>
+            <Component Id="cmp2B59A523302C9529B784D617FB787C70" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="PUT-GUID-HERE">
+                <File Id="filF7B6296DCAE1C456B644D87D9F1055BE" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\BusyIndicator.qml" />
+            </Component>
+            <Component Id="cmpA29DA44FC1E3C3CD62397666F7B22D1A" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="PUT-GUID-HERE">
+                <File Id="fil6C488007970D4BAFB2DB08C754F80FFB" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\Button.qml" />
+            </Component>
+            <Component Id="cmp2E3630D2E1619E501BE32DDC04E8354D" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="PUT-GUID-HERE">
+                <File Id="fil7B9685746F56E01B6AB2567E13880535" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\CheckBox.qml" />
+            </Component>
+            <Component Id="cmp4D80101EADD6D3DDD203B9EB9C85F431" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="PUT-GUID-HERE">
+                <File Id="fil4240AAD799B705DA78F6E223EB4A43EA" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\CheckDelegate.qml" />
+            </Component>
+            <Component Id="cmp37445D06A5B3E1E7935577CC10B83898" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="PUT-GUID-HERE">
+                <File Id="filC9412E7839E28AEAFF838B937FA619D9" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\ComboBox.qml" />
+            </Component>
+            <Component Id="cmpA9942B4E6B49EACD30FBCEB2155B76F9" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="PUT-GUID-HERE">
+                <File Id="fil835E27839A441A92412B36A1436AE7E2" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\DelayButton.qml" />
+            </Component>
+            <Component Id="cmp8CA79672BD5731C6F1820F9A9A0686E0" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="PUT-GUID-HERE">
+                <File Id="fil32B7FECA37D20FB437DAE4EAE8D83D9F" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\Dial.qml" />
+            </Component>
+            <Component Id="cmp1E19938B3005A79AC76779BB3361BA8E" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="PUT-GUID-HERE">
+                <File Id="fil238234A2323BBE1B65CF566AEA3F5D59" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\Dialog.qml" />
+            </Component>
+            <Component Id="cmp791127701CE2A50DF1852818B7491A9B" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="PUT-GUID-HERE">
+                <File Id="fil4293568265BDAF9DEBDF93686F1DD5FD" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\DialogButtonBox.qml" />
+            </Component>
+            <Component Id="cmp7D52281AAE07BA676640D549C3E43E52" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="PUT-GUID-HERE">
+                <File Id="filA7D587F803CE8A93102C2F7A92D32700" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\Drawer.qml" />
+            </Component>
+            <Component Id="cmpE7122D05325A3775A171F8C1697C7FBB" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="PUT-GUID-HERE">
+                <File Id="fil78658507F1694BA1ABC3BA8E20CE6C30" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\Frame.qml" />
+            </Component>
+            <Component Id="cmpC388276A63FE9399AB33D949E56BE42B" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="PUT-GUID-HERE">
+                <File Id="fil7B32D2B48797ADBAD654D1B89897E1F0" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\GroupBox.qml" />
+            </Component>
+            <Component Id="cmp1882DAC6A3EBE8964A93AF06ACFC7EB4" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="PUT-GUID-HERE">
+                <File Id="fil364F48C9D098535A05FBEC76AEE76B2A" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\HorizontalHeaderView.qml" />
+            </Component>
+            <Component Id="cmp9C6D36C97809FD889F7352F368EF795B" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="PUT-GUID-HERE">
+                <File Id="fil3C3DC74E90776063E5DD535D5133F1FB" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\ItemDelegate.qml" />
+            </Component>
+            <Component Id="cmp0FB99193EC45424300BBC1CC9FE23F3B" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="PUT-GUID-HERE">
+                <File Id="filF78C5321460DFDEB14ACD3D54D932D22" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\Label.qml" />
+            </Component>
+            <Component Id="cmpB1425086E74FCFE7599D5B98F96B5784" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="PUT-GUID-HERE">
+                <File Id="fil3649AA3B8C7DAE7AC4D0E353BDEE7229" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\Menu.qml" />
+            </Component>
+            <Component Id="cmp90336EEF38CCA73580CA5B9E08C80EAF" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="PUT-GUID-HERE">
+                <File Id="filAD597F01E7E839A0979C491EBB930C78" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\MenuItem.qml" />
+            </Component>
+            <Component Id="cmp67B34C88BCFD4BC9E9DC64A14C63B406" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="PUT-GUID-HERE">
+                <File Id="fil304DBC131B1D58544E62E9B56B0A728E" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\MenuSeparator.qml" />
+            </Component>
+            <Component Id="cmp1B35E65FB4F3E5B95D841682C48E6657" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="PUT-GUID-HERE">
+                <File Id="filDE75722F397D008570FDDC797FB941B8" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\Page.qml" />
+            </Component>
+            <Component Id="cmp336EE7E45BAAE772C3C37D4742EB2971" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="PUT-GUID-HERE">
+                <File Id="fil9FD9AB969D40B479E112503F24402B78" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\PageIndicator.qml" />
+            </Component>
+            <Component Id="cmpD9A4ABD4DAC06729B089B94DC2659DAC" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="PUT-GUID-HERE">
+                <File Id="fil91B1FE6304E8FF2B14ABEBA731149ECC" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\Pane.qml" />
+            </Component>
+            <Component Id="cmpCF3FCB3F233565D555FB8F6D9555BEE7" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="PUT-GUID-HERE">
+                <File Id="filD3989B72FD12A9018E51557B644C957D" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\plugins.qmltypes" />
+            </Component>
+            <Component Id="cmp5E1C8E699FC4F995BDC27D657EDF394A" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="PUT-GUID-HERE">
+                <File Id="filDBDD17973F7D18C5EE4A718E329F753E" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\Popup.qml" />
+            </Component>
+            <Component Id="cmpFA5F7E69197933807328D50D858DCECE" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="PUT-GUID-HERE">
+                <File Id="filBBD31AE80B657F75029FFDAC9E518728" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\ProgressBar.qml" />
+            </Component>
+            <Component Id="cmp3990B5E9CF8258348A632B5B699F36ED" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="PUT-GUID-HERE">
+                <File Id="filFA6D4FA7FE353656D802FBF9D675E767" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\qmldir" />
+            </Component>
+            <Component Id="cmp986F544E9308DF1B743DFA1EAC30EF43" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="PUT-GUID-HERE">
+                <File Id="fil864CC154589BC52C31E3A37D8E894C1C" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\qtquickcontrols2imaginestyleplugin.dll" />
+            </Component>
+            <Component Id="cmp18F8A218D9D2036ACE6D3B8670DFB611" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="PUT-GUID-HERE">
+                <File Id="filE22A686DBC4A065B54A768CF28B40B24" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\RadioButton.qml" />
+            </Component>
+            <Component Id="cmpF24F72D6E16C2DF2FB9361DB63B08FED" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="PUT-GUID-HERE">
+                <File Id="fil80AC24E33C1863479132EC0565A358DC" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\RadioDelegate.qml" />
+            </Component>
+            <Component Id="cmp88E9B95C71435CA87A11074283BDF487" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="PUT-GUID-HERE">
+                <File Id="fil2E8044C05FEED1C25D49003C83AB596F" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\RangeSlider.qml" />
+            </Component>
+            <Component Id="cmp9736CC8B57B700A14B0980A91EE2D1BA" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="PUT-GUID-HERE">
+                <File Id="filB695029EC2EDB433FB2380A43F2C14F9" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\RoundButton.qml" />
+            </Component>
+            <Component Id="cmpEB828AE5BEBAF16603A099FE6089767E" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="PUT-GUID-HERE">
+                <File Id="filE9494FFC76BECC63991B6D8C4F671092" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\ScrollBar.qml" />
+            </Component>
+            <Component Id="cmp44B48BD69A63FE32825423623FD36B15" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="PUT-GUID-HERE">
+                <File Id="fil14D8341EE42E94CB074A0F12E995A278" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\ScrollIndicator.qml" />
+            </Component>
+            <Component Id="cmpE765F59E9BF850583FBA116A6AF4F621" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="PUT-GUID-HERE">
+                <File Id="fil3184E525C64E9B2B4AA2A1E368D4CA81" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\ScrollView.qml" />
+            </Component>
+            <Component Id="cmp7DD15A60151DE8CA6F8816C39266A1C8" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="PUT-GUID-HERE">
+                <File Id="fil7DC60796B28AB3E3BABB417771AB8ED3" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\SelectionRectangle.qml" />
+            </Component>
+            <Component Id="cmp9F4B9B4F143B31AD8537AFF4C2D16838" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="PUT-GUID-HERE">
+                <File Id="fil69D52DA927BD3817C3C611FDFEE5CCD5" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\Slider.qml" />
+            </Component>
+            <Component Id="cmp4341710343B0A84873572097216DEADA" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="PUT-GUID-HERE">
+                <File Id="filB69332E29D92BF81C951E37807BDF81C" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\SpinBox.qml" />
+            </Component>
+            <Component Id="cmp5FD195FDA6B02A8E02EE196705B70A9F" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="PUT-GUID-HERE">
+                <File Id="filB750E3C7B8FF0D9B591E2CE95B4324E6" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\SplitView.qml" />
+            </Component>
+            <Component Id="cmpA01799766E703E7B6D11942B04364561" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="PUT-GUID-HERE">
+                <File Id="fil39DD4401AFDBABF27165F3F9A43E06E2" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\StackView.qml" />
+            </Component>
+            <Component Id="cmp06A47B65D20F3BF5058EBBC021329BAA" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="PUT-GUID-HERE">
+                <File Id="fil2BA002626459ED0D4CED59E5C9870199" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\SwipeDelegate.qml" />
+            </Component>
+            <Component Id="cmp4D232C26F610D7B34074B8BD1D75AA70" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="PUT-GUID-HERE">
+                <File Id="fil3ECA9A90BEBA5018E6E9720F74FD809F" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\SwipeView.qml" />
+            </Component>
+            <Component Id="cmpAB50455C6C07F9A4A26623A87DFB0900" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="PUT-GUID-HERE">
+                <File Id="fil99D76CA8B195C7F5D9FEA7E8999F60EB" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\Switch.qml" />
+            </Component>
+            <Component Id="cmp980F9D626381D2AF3FD27BE6FA155359" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="PUT-GUID-HERE">
+                <File Id="fil953F291148872594E9AF482D2038D494" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\SwitchDelegate.qml" />
+            </Component>
+            <Component Id="cmp2B4D9D30B7AB9C7AD428DE97D579AA64" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="PUT-GUID-HERE">
+                <File Id="filC62AFE4AEE7C035A98789387806D9383" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\TabBar.qml" />
+            </Component>
+            <Component Id="cmp33B75162158ECB88D7A23F30FD763F95" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="PUT-GUID-HERE">
+                <File Id="fil7083F0C7C56C404D3DD517C64BC36412" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\TabButton.qml" />
+            </Component>
+            <Component Id="cmpE1AC5477D77CA612836AD567921AE60C" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="PUT-GUID-HERE">
+                <File Id="filF04A3782818976B48D5E7BC0A06713B0" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\TextArea.qml" />
+            </Component>
+            <Component Id="cmp808A5FAA30807132EFCAA566A341DC91" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="PUT-GUID-HERE">
+                <File Id="fil7C07F0B29F32604B91E663BAAB4124EA" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\TextField.qml" />
+            </Component>
+            <Component Id="cmp6A3CCDB84C8EB5571B473CA7C10A20B5" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="PUT-GUID-HERE">
+                <File Id="filFF5FBF3050797FF55DF18AECFFF107A8" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\ToolBar.qml" />
+            </Component>
+            <Component Id="cmpF61931DC5F3FF58EDA86C22C3F58C328" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="PUT-GUID-HERE">
+                <File Id="fil52A5496FC0D3BA163BA098734EA289A7" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\ToolButton.qml" />
+            </Component>
+            <Component Id="cmp9C8B9DF33CB082D2CF05065BEEA8DCB8" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="PUT-GUID-HERE">
+                <File Id="filEBBFBE394CB64593B4F8CE36E75F19E1" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\ToolSeparator.qml" />
+            </Component>
+            <Component Id="cmpDE436E9A542E46614FFFD60B10959990" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="PUT-GUID-HERE">
+                <File Id="fil99F32395F78F660CF65B83DB4C24F2A8" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\ToolTip.qml" />
+            </Component>
+            <Component Id="cmp883D99D09DD15DBCB8F5272F8A8429CD" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="PUT-GUID-HERE">
+                <File Id="fil1B9300F5C5F509BA3632F23D4632D6AB" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\Tumbler.qml" />
+            </Component>
+            <Component Id="cmpFB59FF560864ECF64EECAABE64112B4E" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="PUT-GUID-HERE">
+                <File Id="fil2A3BDE0D397759039C2846BE56F55838" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\VerticalHeaderView.qml" />
+            </Component>
+            <Component Id="cmp2F4A067EF22E659A29351F47EC649485" Directory="dir7970C160330F95B9F7E3CCDAB2CF5702" Guid="PUT-GUID-HERE">
+                <File Id="fil7EFD45119BDFBBBC4F3EEB8A3168B58F" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\impl\OpacityMask.qml" />
+            </Component>
+            <Component Id="cmpD3F5C4FA5F6A0F45634B7CB9D759B11F" Directory="dir7970C160330F95B9F7E3CCDAB2CF5702" Guid="PUT-GUID-HERE">
+                <File Id="fil467DFB7AB69F634BA67A0541A502CF6C" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\impl\plugins.qmltypes" />
+            </Component>
+            <Component Id="cmp6F7F439DA976D2187EA2E2870C2A2EDF" Directory="dir7970C160330F95B9F7E3CCDAB2CF5702" Guid="PUT-GUID-HERE">
+                <File Id="filD8199E2FBE24BC9B8A0E289A9148E944" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\impl\qmldir" />
+            </Component>
+            <Component Id="cmpFAA3B9C7407D0FD54E5641949342F04B" Directory="dir7970C160330F95B9F7E3CCDAB2CF5702" Guid="PUT-GUID-HERE">
+                <File Id="fil94320F53E375FBB7BA4382411890CFF8" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\impl\qtquickcontrols2imaginestyleimplplugin.dll" />
+            </Component>
+            <Component Id="cmp7E1D2A30E36DEC4B04D58E1205BC6EE3" Directory="dir68B807445C3B5B5D97077A041B375DC5" Guid="PUT-GUID-HERE">
+                <File Id="fil0A1E55ED27EFBE34A162E88114B4D27A" KeyPath="yes" Source="SourceDir\QtQuick\Controls\impl\plugins.qmltypes" />
+            </Component>
+            <Component Id="cmp4736493ABAB34042B01981695B30997A" Directory="dir68B807445C3B5B5D97077A041B375DC5" Guid="PUT-GUID-HERE">
+                <File Id="filE4E9BB2F6A2058E5E3819A19F127A9AE" KeyPath="yes" Source="SourceDir\QtQuick\Controls\impl\qmldir" />
+            </Component>
+            <Component Id="cmp3F77E0CAF99C597DC95D9FFDAE729F19" Directory="dir68B807445C3B5B5D97077A041B375DC5" Guid="PUT-GUID-HERE">
+                <File Id="filC54932CFABAD7D1B8167610A3675D2F2" KeyPath="yes" Source="SourceDir\QtQuick\Controls\impl\qtquickcontrols2implplugin.dll" />
+            </Component>
+            <Component Id="cmp2484ACCA4386C4EA0755C5DC029E0B17" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="PUT-GUID-HERE">
+                <File Id="fil98CE2BFE43345127253926D10D8FE106" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\ApplicationWindow.qml" />
+            </Component>
+            <Component Id="cmp1CABE6D723F88A8B7C5CFC6E637BA0DD" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="PUT-GUID-HERE">
+                <File Id="filCD22BC7ADA9B892C477A1BFB652D27B2" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\BusyIndicator.qml" />
+            </Component>
+            <Component Id="cmp150B8F53E3E4D187A3635EC7DF0B3496" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="PUT-GUID-HERE">
+                <File Id="filAA267072FF70009974591236F3976908" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\Button.qml" />
+            </Component>
+            <Component Id="cmpBA71C2D28506DF91E8604A48D4D7A681" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="PUT-GUID-HERE">
+                <File Id="fil36DB7EB1294D2BD6DC2556971A6ACBF5" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\CheckBox.qml" />
+            </Component>
+            <Component Id="cmpAD5A1008138AEC7C44E2B8F881D727D8" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="PUT-GUID-HERE">
+                <File Id="fil8D439F19C8177116A9EC9B0D46D06ABE" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\CheckDelegate.qml" />
+            </Component>
+            <Component Id="cmpFE8134A082F65474A8A7C96F550F7B6F" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="PUT-GUID-HERE">
+                <File Id="filD94D37CFFC95F23BE5BF3E7156351502" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\ComboBox.qml" />
+            </Component>
+            <Component Id="cmp92EC30E2F97599E6475833FC935F5ADE" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="PUT-GUID-HERE">
+                <File Id="filB5A130F555AF929C8A44F676CA51411B" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\DelayButton.qml" />
+            </Component>
+            <Component Id="cmpF7269A0A3A4FA0533A026EBB0D054514" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="PUT-GUID-HERE">
+                <File Id="fil3FDAFFCB7CEFF8929DF88A615B64CA9D" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\Dial.qml" />
+            </Component>
+            <Component Id="cmp49EF77F2FDAC5E8D7F5AD5057BB8BC5B" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="PUT-GUID-HERE">
+                <File Id="filD8F74A3034CADD0EBE81EED5817B7CBD" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\Dialog.qml" />
+            </Component>
+            <Component Id="cmpAAEF1F717A8228116C2BFC9C972091AE" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="PUT-GUID-HERE">
+                <File Id="fil1CEB800ABC17A533C224D38D599DA8E2" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\DialogButtonBox.qml" />
+            </Component>
+            <Component Id="cmp919D0F58C7BF8E066F8AD19FBFFF401F" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="PUT-GUID-HERE">
+                <File Id="fil55D8CF7962BD62EF276366D4F51A5108" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\Drawer.qml" />
+            </Component>
+            <Component Id="cmpDAC86E05F7C5A0C685057D15EE8EBFBB" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="PUT-GUID-HERE">
+                <File Id="fil4104A2470A08C2E99FB8497856FB5CCC" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\Frame.qml" />
+            </Component>
+            <Component Id="cmpFA397A980F26746B37DA49E25CB9BE6B" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="PUT-GUID-HERE">
+                <File Id="filD5C43CB4B55D52B7842A05F4854763E4" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\GroupBox.qml" />
+            </Component>
+            <Component Id="cmp876A1F2FA211E31C812F77CCE6B7BF9F" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="PUT-GUID-HERE">
+                <File Id="fil1F63F7828CEF6DE31A501BBD48E88E1D" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\HorizontalHeaderView.qml" />
+            </Component>
+            <Component Id="cmp9D106D22A350BC32239D948D1B1DB7B3" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="PUT-GUID-HERE">
+                <File Id="filBBBB46A8A3BA617AA5B8D35907153E71" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\ItemDelegate.qml" />
+            </Component>
+            <Component Id="cmp00936E9FC59249D652B2965A887DAB92" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="PUT-GUID-HERE">
+                <File Id="fil277FE7AE1BCD078B80B9E701965A27EC" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\Label.qml" />
+            </Component>
+            <Component Id="cmp85D562BA4AC03A938CE51BA067EE9411" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="PUT-GUID-HERE">
+                <File Id="fil3663264AD718BFF48BF30125B2E57940" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\Menu.qml" />
+            </Component>
+            <Component Id="cmp88C26B837D1623B7D9328BBC647BA71F" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="PUT-GUID-HERE">
+                <File Id="filA77CFDEC0F9A76D2591207810823A525" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\MenuBar.qml" />
+            </Component>
+            <Component Id="cmp84DE03E70A555E06B6C3168D04E5ACD6" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="PUT-GUID-HERE">
+                <File Id="fil1289F89AC5D07B94DB5B19B6798FB0A3" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\MenuBarItem.qml" />
+            </Component>
+            <Component Id="cmp31B9C2EFE4AECA846D066FDA7625A4BD" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="PUT-GUID-HERE">
+                <File Id="filA29EEE317FE4F6B75D1C6368C2D1A028" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\MenuItem.qml" />
+            </Component>
+            <Component Id="cmp564788F5E82213B81DB7AA5A87DB3E25" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="PUT-GUID-HERE">
+                <File Id="filDC3ED25FF0BB2356EE3005F5A3E502E8" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\MenuSeparator.qml" />
+            </Component>
+            <Component Id="cmp47C69EB69EC227A0B7A322E2B676378C" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="PUT-GUID-HERE">
+                <File Id="filAE954BA0FE603BAF163499F85AB043AF" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\Page.qml" />
+            </Component>
+            <Component Id="cmp5F61DDC08BCDB7719CA7E527708C93C1" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="PUT-GUID-HERE">
+                <File Id="filFC23E466F737593E81324EC0331719C1" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\PageIndicator.qml" />
+            </Component>
+            <Component Id="cmp2DB3EABA95D743CE983D528D8CC93E06" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="PUT-GUID-HERE">
+                <File Id="fil14DB824F7C509C01068C72C3B37E7A5D" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\Pane.qml" />
+            </Component>
+            <Component Id="cmpC27D371B7AF16E9440323FC8B97961E7" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="PUT-GUID-HERE">
+                <File Id="fil0D3BE9F2C475F1A3363C95FCA224C987" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\plugins.qmltypes" />
+            </Component>
+            <Component Id="cmp2ADAAB6963EEC51A27B8C83E06615107" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="PUT-GUID-HERE">
+                <File Id="fil17B4B5694D32AE50511F087E8186666D" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\Popup.qml" />
+            </Component>
+            <Component Id="cmpBC16C2D5C71B4025BBE65FC1ADCC2E2E" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="PUT-GUID-HERE">
+                <File Id="fil67AADC50B8EBF8B4559BE98F2A643C7E" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\ProgressBar.qml" />
+            </Component>
+            <Component Id="cmp2A7EFC2FC51AAC11A7AD530967908CAF" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="PUT-GUID-HERE">
+                <File Id="filDC5DDEA55DD6396AA1BD11833AC67126" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\qmldir" />
+            </Component>
+            <Component Id="cmpC2D73E1422E4A677607B108FDF480025" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="PUT-GUID-HERE">
+                <File Id="filBFDB50B68C3C7AFA6D33280DE84AB611" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\qtquickcontrols2materialstyleplugin.dll" />
+            </Component>
+            <Component Id="cmp944C1F5E73CDEFE57A9F1847773FF643" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="PUT-GUID-HERE">
+                <File Id="filAF33564E5C73709F58B84B9680B95573" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\RadioButton.qml" />
+            </Component>
+            <Component Id="cmp6292EDD00596B1A384B82CC027A79DF1" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="PUT-GUID-HERE">
+                <File Id="fil2826159FE6C201461BF4D8E595D96978" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\RadioDelegate.qml" />
+            </Component>
+            <Component Id="cmpC4B1039C7160CC45EE73054EFAAAC0B8" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="PUT-GUID-HERE">
+                <File Id="fil9E47F0E6223EDAEDC34AEFBABB730867" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\RangeSlider.qml" />
+            </Component>
+            <Component Id="cmp022DFBDE861C6B29A054F5963EE2B5EA" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="PUT-GUID-HERE">
+                <File Id="fil5E54E72E7781831F2755AC3FA09083DC" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\RoundButton.qml" />
+            </Component>
+            <Component Id="cmpD0CECCC44B61261D9DFDC13668F84FC1" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="PUT-GUID-HERE">
+                <File Id="fil6817407CD64183FF2E1B2116A58A4040" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\ScrollBar.qml" />
+            </Component>
+            <Component Id="cmp68F38D4DBC594717681DA90B161659CA" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="PUT-GUID-HERE">
+                <File Id="filC3E86B969A4F3B35AE391131A155A110" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\ScrollIndicator.qml" />
+            </Component>
+            <Component Id="cmp5C104AE9099052E2403BF80E5010CA05" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="PUT-GUID-HERE">
+                <File Id="fil93FBDF6840EDC337A5B23CC9CD3374BD" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\ScrollView.qml" />
+            </Component>
+            <Component Id="cmpD3B596CCF391818683C45670554D70E6" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="PUT-GUID-HERE">
+                <File Id="fil44DECFF6A9EAECE06988197FE80B12B0" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\SelectionRectangle.qml" />
+            </Component>
+            <Component Id="cmp5E88D1FB666FBADDAEDDF7F6D5965FEB" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="PUT-GUID-HERE">
+                <File Id="fil6D5485BB82D9B226FC460ABE76A795A0" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\Slider.qml" />
+            </Component>
+            <Component Id="cmp61D9C682647B4E15638AB211625A20EF" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="PUT-GUID-HERE">
+                <File Id="fil5F041632EB639600BE98BBC0978477EC" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\SpinBox.qml" />
+            </Component>
+            <Component Id="cmpC11C5AEEC78A36A400623D2B2634FE0E" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="PUT-GUID-HERE">
+                <File Id="fil96BCF71BDD33EEF508D856CCC6CF69C1" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\SplitView.qml" />
+            </Component>
+            <Component Id="cmp5A87B6D2F5D12F503E12312658D8D561" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="PUT-GUID-HERE">
+                <File Id="filF1080461F27F55D2AB51B496B3A4F207" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\StackView.qml" />
+            </Component>
+            <Component Id="cmp10387E3129C01B96666D6A3A3AF1C23A" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="PUT-GUID-HERE">
+                <File Id="fil0225C1BDD92DC16E5502A5BCA49F96F6" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\SwipeDelegate.qml" />
+            </Component>
+            <Component Id="cmp317381168221ED463BF2ED6046F0DEDC" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="PUT-GUID-HERE">
+                <File Id="filF3D28904BF3EF3D9D8C821F8C3B4E3D4" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\SwipeView.qml" />
+            </Component>
+            <Component Id="cmpA101EFF8776D6124F03EE80470EA6C79" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="PUT-GUID-HERE">
+                <File Id="filD30ED475815DD2180B7D0EBB8A8730C9" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\Switch.qml" />
+            </Component>
+            <Component Id="cmp84E49F27B152536770E1D198D6882AE8" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="PUT-GUID-HERE">
+                <File Id="filB5769414552C2D940DD6BF741B76A10C" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\SwitchDelegate.qml" />
+            </Component>
+            <Component Id="cmp0C7120286638333E7DB2CC823AA01BF7" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="PUT-GUID-HERE">
+                <File Id="filB31C5970C979FF32921717A317A703FB" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\TabBar.qml" />
+            </Component>
+            <Component Id="cmp1669E3B861A273DBAEC1B07897FC94A2" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="PUT-GUID-HERE">
+                <File Id="filE73A0FEE0A4B54A5B6F71CD9CA9C5DE3" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\TabButton.qml" />
+            </Component>
+            <Component Id="cmp6F48BD948C8607BD25BBD32809192134" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="PUT-GUID-HERE">
+                <File Id="filE232E218B2134E4A11649C7A39A214FE" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\TextArea.qml" />
+            </Component>
+            <Component Id="cmp02A8A63E7AA36D9142FA648F74300E72" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="PUT-GUID-HERE">
+                <File Id="fil83B097C7750B41F18D22229E22BC275C" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\TextField.qml" />
+            </Component>
+            <Component Id="cmp94F104BB2082647E01DA35DFE6C2BC2C" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="PUT-GUID-HERE">
+                <File Id="filEFE4324C9894458EAF3DD996B2574670" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\ToolBar.qml" />
+            </Component>
+            <Component Id="cmp1912AF5DC3DEBD163AA91F8BA3B01720" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="PUT-GUID-HERE">
+                <File Id="fil98F955F3D239A653EFD4067ADF1B1F35" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\ToolButton.qml" />
+            </Component>
+            <Component Id="cmp168E0C3533ED0B546C5FE8AAC03EF7AD" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="PUT-GUID-HERE">
+                <File Id="fil2F89C2CD6A812EB5D9E9031047AF4CF4" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\ToolSeparator.qml" />
+            </Component>
+            <Component Id="cmp55EE8538AAABFE4047346A048968B6CD" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="PUT-GUID-HERE">
+                <File Id="fil3E346F115EA572A085D64B711F64DB03" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\ToolTip.qml" />
+            </Component>
+            <Component Id="cmpBEFC0E759EB09ADD1528DEA72860D434" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="PUT-GUID-HERE">
+                <File Id="filAE9CAC6E57F455490318BD9F44F4C83F" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\Tumbler.qml" />
+            </Component>
+            <Component Id="cmp3F64B12F194CD470A60FAB7FAEBE121A" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="PUT-GUID-HERE">
+                <File Id="fil1387B19E1BF6C097EFE9C94098AE2AD7" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\VerticalHeaderView.qml" />
+            </Component>
+            <Component Id="cmp4613B82FE5805E334C2EAE293D1FDA73" Directory="dirD4CF5DF9BDC9CA55E064A8C389B40501" Guid="PUT-GUID-HERE">
+                <File Id="filA68DB84A811B266C685CEE70242C70CD" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\impl\BoxShadow.qml" />
+            </Component>
+            <Component Id="cmpB13F649E8CA70FE0AD2F692D328DBE5A" Directory="dirD4CF5DF9BDC9CA55E064A8C389B40501" Guid="PUT-GUID-HERE">
+                <File Id="filD19E4FD2FC8939034E33A4082B244427" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\impl\CheckIndicator.qml" />
+            </Component>
+            <Component Id="cmp4F579F22F5991056C50BA0820141FC7C" Directory="dirD4CF5DF9BDC9CA55E064A8C389B40501" Guid="PUT-GUID-HERE">
+                <File Id="fil71B68E5ADBC74C83E430B37D3F64FCD5" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\impl\CursorDelegate.qml" />
+            </Component>
+            <Component Id="cmpE8448728EB5B6A914C13B66662D9758F" Directory="dirD4CF5DF9BDC9CA55E064A8C389B40501" Guid="PUT-GUID-HERE">
+                <File Id="fil3F896C09724090C33709CAB5291EEB4D" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\impl\ElevationEffect.qml" />
+            </Component>
+            <Component Id="cmp380FEFB7977E8FD5EF81EEA08381907D" Directory="dirD4CF5DF9BDC9CA55E064A8C389B40501" Guid="PUT-GUID-HERE">
+                <File Id="filF94FE54BFCC8A8E474435BB57BDB1754" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\impl\plugins.qmltypes" />
+            </Component>
+            <Component Id="cmpD65057448FF4D30321F0DCA26C382450" Directory="dirD4CF5DF9BDC9CA55E064A8C389B40501" Guid="PUT-GUID-HERE">
+                <File Id="fil13A64843D2611849C3B307D18803ABAE" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\impl\qmldir" />
+            </Component>
+            <Component Id="cmp0A4CAF85E34D1B2AF078FCB322719BCF" Directory="dirD4CF5DF9BDC9CA55E064A8C389B40501" Guid="PUT-GUID-HERE">
+                <File Id="filCFEBC999C66756E9120E4B973F72CE21" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\impl\qtquickcontrols2materialstyleimplplugin.dll" />
+            </Component>
+            <Component Id="cmp2821A8A5A3876FCE4EAB1EF52EB062FA" Directory="dirD4CF5DF9BDC9CA55E064A8C389B40501" Guid="PUT-GUID-HERE">
+                <File Id="fil2365F1274AD13460FE3C5D3C8FF653FF" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\impl\RadioIndicator.qml" />
+            </Component>
+            <Component Id="cmp4279783DBDC9942B50B483804FD3AEC3" Directory="dirD4CF5DF9BDC9CA55E064A8C389B40501" Guid="PUT-GUID-HERE">
+                <File Id="filAD98AF073F9190BF775B5794C1638A58" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\impl\RectangularGlow.qml" />
+            </Component>
+            <Component Id="cmpF1F8978AF520020EF78A370452C80628" Directory="dirD4CF5DF9BDC9CA55E064A8C389B40501" Guid="PUT-GUID-HERE">
+                <File Id="fil37EE179F06BB7014856960611AA2145B" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\impl\SliderHandle.qml" />
+            </Component>
+            <Component Id="cmp200DE3D51BCF5719541F1CEC3149726C" Directory="dirD4CF5DF9BDC9CA55E064A8C389B40501" Guid="PUT-GUID-HERE">
+                <File Id="fil86DEB21E8EB571AC7F36DCC977A4298C" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\impl\SwitchIndicator.qml" />
+            </Component>
+            <Component Id="cmp2FAD1AA520E7686F634427FB374BB635" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="PUT-GUID-HERE">
+                <File Id="fil0AD512B7C2B483BB7C87049E8B45559B" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\ApplicationWindow.qml" />
+            </Component>
+            <Component Id="cmp2ADEFC5A9071EE0843D5BFE12567B84A" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="PUT-GUID-HERE">
+                <File Id="fil4B00A0A1204162CA3C14830EB9EF1DEE" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\BusyIndicator.qml" />
+            </Component>
+            <Component Id="cmp4FAA1AC9495E561E3D9DEEDFD7F56B35" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="PUT-GUID-HERE">
+                <File Id="fil9EBDDDC54FC2C8FE2E1B7763CFD8B030" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\Button.qml" />
+            </Component>
+            <Component Id="cmp98299BBCE2B330E6111405911B6BE08B" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="PUT-GUID-HERE">
+                <File Id="filFF7CBD4AFC5CD29D415B8CA842BD3953" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\CheckBox.qml" />
+            </Component>
+            <Component Id="cmp74653D62C0025B52EACC5CE4705BA4AC" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="PUT-GUID-HERE">
+                <File Id="fil2F5AF98342429458AD63810BB297BE8F" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\CheckDelegate.qml" />
+            </Component>
+            <Component Id="cmp82CBBB6E4192B7021A218112BCB5EABA" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="PUT-GUID-HERE">
+                <File Id="filB6681E9463BADDB6ADD9558C3C817457" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\ComboBox.qml" />
+            </Component>
+            <Component Id="cmpB7B372C2E3988CDD1F9809917CE52585" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="PUT-GUID-HERE">
+                <File Id="fil749DFC3226F07A88D6102926D0536AD5" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\DelayButton.qml" />
+            </Component>
+            <Component Id="cmp24F6257663361F90EF52CA9A4F658D44" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="PUT-GUID-HERE">
+                <File Id="fil40243C01A353CA3D602F8100942F2B4A" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\Dial.qml" />
+            </Component>
+            <Component Id="cmp3614FDEBFF9DE873AF9B0CB586840E34" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="PUT-GUID-HERE">
+                <File Id="filAF75807656F449DF60B3006684E77B9D" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\Dialog.qml" />
+            </Component>
+            <Component Id="cmp52DEE6CB79C4277264AC4090D5E2531C" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="PUT-GUID-HERE">
+                <File Id="fil4A391652834C19895C58027631C47055" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\DialogButtonBox.qml" />
+            </Component>
+            <Component Id="cmpA83413D9000B4AF5C07C05A3CF1108A9" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="PUT-GUID-HERE">
+                <File Id="fil7CE5827EEA12748D5B79208226FD2FC3" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\Drawer.qml" />
+            </Component>
+            <Component Id="cmpAFF05B29E306BDDF612E08480F111B94" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="PUT-GUID-HERE">
+                <File Id="filE319DC1ADC5570BB3EFA6B8C96B9ADB2" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\Frame.qml" />
+            </Component>
+            <Component Id="cmp07EFE87D2155105B9BADFAF62AD12DE7" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="PUT-GUID-HERE">
+                <File Id="fil6238E69A3FF45BAED0A93FF54D64C608" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\GroupBox.qml" />
+            </Component>
+            <Component Id="cmp63D09EE503A89DFB92B4E05D8D74882D" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="PUT-GUID-HERE">
+                <File Id="fil8315853BEBD2B22F2D2FA1421BFDB11D" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\HorizontalHeaderView.qml" />
+            </Component>
+            <Component Id="cmp69655929CAB1E209527EA04B131BAF08" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="PUT-GUID-HERE">
+                <File Id="fil13A04CE8C24A5E3A3F4951AFD6BC646E" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\ItemDelegate.qml" />
+            </Component>
+            <Component Id="cmpD0906DBB6FC23D842FE548E71D4FD4F2" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="PUT-GUID-HERE">
+                <File Id="filE87D7D52B86C4561BB4BBD0B0BE17872" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\Label.qml" />
+            </Component>
+            <Component Id="cmp314F7C86D57FDF1E0A16D4D98E2D9567" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="PUT-GUID-HERE">
+                <File Id="filC51B15821AAFCB45308245AD9F65241E" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\Menu.qml" />
+            </Component>
+            <Component Id="cmp8A717A9077FC79285557357EB3E6306E" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="PUT-GUID-HERE">
+                <File Id="fil6AAEB2864E9962A27AFF6EB36AA78047" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\MenuBar.qml" />
+            </Component>
+            <Component Id="cmpF60FE6F301CE8A6103133FB10FCADF93" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="PUT-GUID-HERE">
+                <File Id="filBE4024BA28E10561875CA50061DEF7A4" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\MenuBarItem.qml" />
+            </Component>
+            <Component Id="cmp45AF55E12BC0056BF2CBA087DE3A15C1" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="PUT-GUID-HERE">
+                <File Id="filD3D9490012EDC98F5745045B46E1B05B" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\MenuItem.qml" />
+            </Component>
+            <Component Id="cmp87EA1031250A82B1A41C269330D64DF2" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="PUT-GUID-HERE">
+                <File Id="filC4AD17C140F4833AFB47B45E9A244A59" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\MenuSeparator.qml" />
+            </Component>
+            <Component Id="cmp0647FD315BFF5036E9CA6E851E77571E" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="PUT-GUID-HERE">
+                <File Id="filED2FA4F6482AD59889D955531A2D8796" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\Page.qml" />
+            </Component>
+            <Component Id="cmpF7497FA50C72610AA9142D074D791C59" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="PUT-GUID-HERE">
+                <File Id="fil2BB9C91E2C1EB4411E3DD5127EF2B7D4" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\PageIndicator.qml" />
+            </Component>
+            <Component Id="cmpB53227D234784EE80FC0B55813AD12E3" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="PUT-GUID-HERE">
+                <File Id="filF9F1163D16E5DD3C931976CF0D21D49B" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\Pane.qml" />
+            </Component>
+            <Component Id="cmpA871A6B27440759B744A6657386532DA" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="PUT-GUID-HERE">
+                <File Id="fil944E0533221778E4A86FC800C6EB0868" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\plugins.qmltypes" />
+            </Component>
+            <Component Id="cmp366792226A445DC856A9C8F90A7E9357" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="PUT-GUID-HERE">
+                <File Id="filAEB9611E73CA47FE708B5BAE2383AA7F" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\Popup.qml" />
+            </Component>
+            <Component Id="cmpAF803553492E1968BD6573328578177B" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="PUT-GUID-HERE">
+                <File Id="fil0460E95677EBC7E9D99F075593F8AB4F" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\ProgressBar.qml" />
+            </Component>
+            <Component Id="cmp51B77E664C5381F7D9825079760DAAB1" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="PUT-GUID-HERE">
+                <File Id="filB9C70D68EF80B50079A5D2A205897696" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\qmldir" />
+            </Component>
+            <Component Id="cmp9087E21C348FF3ADF9E819DFC8F4CEB6" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="PUT-GUID-HERE">
+                <File Id="filC09D64083B771A951AC13BC28F6BB594" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\qtquickcontrols2universalstyleplugin.dll" />
+            </Component>
+            <Component Id="cmp931D54FC538FDE70C9FF8C58C7FE8384" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="PUT-GUID-HERE">
+                <File Id="filC6F54E9404B2D08089A2FA24F1B5D74B" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\RadioButton.qml" />
+            </Component>
+            <Component Id="cmpD42BB5B3F23454FBAFA6FB26A89980BA" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="PUT-GUID-HERE">
+                <File Id="fil00F4C559972EA6F8F3E4D84E3898DA0E" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\RadioDelegate.qml" />
+            </Component>
+            <Component Id="cmp4C747396BCBDB0519CE6284BEED0FE7E" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="PUT-GUID-HERE">
+                <File Id="fil35A0D2497D80784A5708FFFED620C565" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\RangeSlider.qml" />
+            </Component>
+            <Component Id="cmp2B01E8CC8E48574980219C4F51C088C7" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="PUT-GUID-HERE">
+                <File Id="fil58B9612E0F9621875C71B5CD402542D2" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\RoundButton.qml" />
+            </Component>
+            <Component Id="cmp8212CD0C96EAB4C613A761E754011D70" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="PUT-GUID-HERE">
+                <File Id="fil2001B522BF0953FBEDAF186CB387907C" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\ScrollBar.qml" />
+            </Component>
+            <Component Id="cmpD7CA60E6EDE60D5C9AEB3414A198CDC1" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="PUT-GUID-HERE">
+                <File Id="fil1309AAA5E9FBAAC455ACEDB0DD6BE528" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\ScrollIndicator.qml" />
+            </Component>
+            <Component Id="cmpE0B957E426C7F6077568110AA5364520" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="PUT-GUID-HERE">
+                <File Id="fil59C85F1D86A5D26C018D9485DBC2F8C8" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\ScrollView.qml" />
+            </Component>
+            <Component Id="cmp63EB5E41D4F0BEB3B9F57F6FDE2BF818" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="PUT-GUID-HERE">
+                <File Id="fil5D3938CE0A0C1A757669B8F0C784AD0D" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\SelectionRectangle.qml" />
+            </Component>
+            <Component Id="cmpF37676A5F0931B20CFF0D58B6C7F2F99" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="PUT-GUID-HERE">
+                <File Id="fil053C2F4943464D8367F8F708F6CD2522" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\Slider.qml" />
+            </Component>
+            <Component Id="cmp823062B1DF2BD46ABC0C991611360E70" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="PUT-GUID-HERE">
+                <File Id="fil442E543AA50E98A0D8CC13797B60A0CB" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\SpinBox.qml" />
+            </Component>
+            <Component Id="cmp60704BAFE2CEE50EBEE1DD76F2C94BD6" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="PUT-GUID-HERE">
+                <File Id="filD46EBE0768CFBE0A2C922ECB1A5F8710" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\SplitView.qml" />
+            </Component>
+            <Component Id="cmp08B8BF38C3777507DCD07252C30A2EED" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="PUT-GUID-HERE">
+                <File Id="filC63E50933072DDA2A2F81AEA313AF5B7" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\StackView.qml" />
+            </Component>
+            <Component Id="cmp5DF60F5D23994DBDC45EE597DAE82C98" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="PUT-GUID-HERE">
+                <File Id="filE8EA5E4B4467A7D21DDD94FB0E8087D8" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\SwipeDelegate.qml" />
+            </Component>
+            <Component Id="cmp1126502F28518CB73077CAE5B99E0084" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="PUT-GUID-HERE">
+                <File Id="fil61546DED3B721A83958E1250B4101229" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\Switch.qml" />
+            </Component>
+            <Component Id="cmp1E2D3D098C0C3EDEF52EFDB9E11A68D5" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="PUT-GUID-HERE">
+                <File Id="fil6079EB46A6FA930D4CC18C0A09D93286" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\SwitchDelegate.qml" />
+            </Component>
+            <Component Id="cmp0786D85C308613496238A9CB6FB5E626" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="PUT-GUID-HERE">
+                <File Id="fil0524F140118812BB5021429D3B8F844D" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\TabBar.qml" />
+            </Component>
+            <Component Id="cmp04B304EE75DB9C2FE23AB462D633CA65" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="PUT-GUID-HERE">
+                <File Id="filDC7507CF41EE0CEC36C58379F9931C05" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\TabButton.qml" />
+            </Component>
+            <Component Id="cmpCAFA00528B35D03089962912927E2DA1" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="PUT-GUID-HERE">
+                <File Id="filCC3CE611A1257C01753A25B0B37F6B54" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\TextArea.qml" />
+            </Component>
+            <Component Id="cmp644523A4EA82DAB1ACAA7E966D92D48C" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="PUT-GUID-HERE">
+                <File Id="fil1B2AEB5E69D93467FE25800346697D31" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\TextField.qml" />
+            </Component>
+            <Component Id="cmp26639D51A08FA4BAF5F80E49C171E45F" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="PUT-GUID-HERE">
+                <File Id="filE4BBB3467656627DF714EC07193C5449" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\ToolBar.qml" />
+            </Component>
+            <Component Id="cmp3F6EDAA6430EC43721B35CA18D3BA78F" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="PUT-GUID-HERE">
+                <File Id="fil4BFBEF1AE43AD4AA92C7416190678A9D" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\ToolButton.qml" />
+            </Component>
+            <Component Id="cmp77F56C58CA7D3A2AE058A692FA0DB0FA" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="PUT-GUID-HERE">
+                <File Id="filD95FC4F33B80E4425E3F655CBE9DD423" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\ToolSeparator.qml" />
+            </Component>
+            <Component Id="cmp4293429955B7E41962EDA87AAAC1A864" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="PUT-GUID-HERE">
+                <File Id="fil367938606BD72BFD59CC703368AB5ADC" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\ToolTip.qml" />
+            </Component>
+            <Component Id="cmpECF2EB8A7C8F610B440E0DB95DFB1C34" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="PUT-GUID-HERE">
+                <File Id="fil88D922FE8AA17DB7E9DFAE0C5AA9C02E" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\Tumbler.qml" />
+            </Component>
+            <Component Id="cmpE2432E964D455B2F8020CEE8E70045BF" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="PUT-GUID-HERE">
+                <File Id="filF5E98B65CEE286B98532681C3B212115" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\VerticalHeaderView.qml" />
+            </Component>
+            <Component Id="cmp3C7BCDA16EBE7C7234300679974E5AF8" Directory="dir40011B386F4B1F0F2AE25C2E3ADB18E8" Guid="PUT-GUID-HERE">
+                <File Id="fil5E3D7806E4ACE06AE8206169AE47D35E" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\impl\CheckIndicator.qml" />
+            </Component>
+            <Component Id="cmp839F94E8CF3D4B7CF0DD1F309B8EF06C" Directory="dir40011B386F4B1F0F2AE25C2E3ADB18E8" Guid="PUT-GUID-HERE">
+                <File Id="filDA4416BDF11B09021A1326B00223FB37" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\impl\plugins.qmltypes" />
+            </Component>
+            <Component Id="cmpD1CF68B645D21C100068C4EF660BDC0F" Directory="dir40011B386F4B1F0F2AE25C2E3ADB18E8" Guid="PUT-GUID-HERE">
+                <File Id="filB125192383A639A7758297A7C4B197E6" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\impl\qmldir" />
+            </Component>
+            <Component Id="cmp2AEEE092AE10D40D28E4AC098E540740" Directory="dir40011B386F4B1F0F2AE25C2E3ADB18E8" Guid="PUT-GUID-HERE">
+                <File Id="fil4E63F58A1B36B63D317111EBE3B48AA6" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\impl\qtquickcontrols2universalstyleimplplugin.dll" />
+            </Component>
+            <Component Id="cmpFA802CBD27EC3716FCB763360C00ED06" Directory="dir40011B386F4B1F0F2AE25C2E3ADB18E8" Guid="PUT-GUID-HERE">
+                <File Id="filD0FB5A202A8DF4C9B739C4930248B14D" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\impl\RadioIndicator.qml" />
+            </Component>
+            <Component Id="cmp169AF5E7D2D401073A9E07AB61EC6A17" Directory="dir40011B386F4B1F0F2AE25C2E3ADB18E8" Guid="PUT-GUID-HERE">
+                <File Id="fil58A590E9CE55A76BCC66D2BED9287B7C" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\impl\SwitchIndicator.qml" />
+            </Component>
+            <Component Id="cmpB3BDFF757AD79A1F0F732B4E9266F68F" Directory="dir5BF786D89AC746B54F9B967AA07926B8" Guid="PUT-GUID-HERE">
+                <File Id="fil9FEC5118E51E3BF277C47160386C5982" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Windows\Button.qml" />
+            </Component>
+            <Component Id="cmpF4A88E9F2EA021D2F2A53064773F9BB4" Directory="dir5BF786D89AC746B54F9B967AA07926B8" Guid="PUT-GUID-HERE">
+                <File Id="filBD3EBDDB47E26B6E7EC6932DF5C67EFF" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Windows\CheckBox.qml" />
+            </Component>
+            <Component Id="cmpDC75B8A533B90F0242BA2101EAD7FD28" Directory="dir5BF786D89AC746B54F9B967AA07926B8" Guid="PUT-GUID-HERE">
+                <File Id="fil3CCF4F05FDBC45FD8DEACA92B13CDB86" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Windows\ComboBox.qml" />
+            </Component>
+            <Component Id="cmpA976CCF99401B90F612B7CC48326B83D" Directory="dir5BF786D89AC746B54F9B967AA07926B8" Guid="PUT-GUID-HERE">
+                <File Id="filF863D2D56FBB28CB9CEB338B060AD016" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Windows\Frame.qml" />
+            </Component>
+            <Component Id="cmp49EB228C54CE552ED0E58608D46917CF" Directory="dir5BF786D89AC746B54F9B967AA07926B8" Guid="PUT-GUID-HERE">
+                <File Id="fil439BD2BAEBB9F9F7BE95217EF772C924" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Windows\GroupBox.qml" />
+            </Component>
+            <Component Id="cmp97952AFE76A2C2EF1F031E7D877F76D4" Directory="dir5BF786D89AC746B54F9B967AA07926B8" Guid="PUT-GUID-HERE">
+                <File Id="filF2B713C14D9A0A8790D19998F4CB2FC9" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Windows\plugins.qmltypes" />
+            </Component>
+            <Component Id="cmpB76B4394B54997D1E96DAA67F0DE6665" Directory="dir5BF786D89AC746B54F9B967AA07926B8" Guid="PUT-GUID-HERE">
+                <File Id="fil0DEE413D0B0D99E29AC79BBE51AF79C3" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Windows\ProgressBar.qml" />
+            </Component>
+            <Component Id="cmpC803B18B5B045E6CF331B778BADCD578" Directory="dir5BF786D89AC746B54F9B967AA07926B8" Guid="PUT-GUID-HERE">
+                <File Id="filA74A9299B5D6C22A7737DC48C3A92ACA" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Windows\qmldir" />
+            </Component>
+            <Component Id="cmp3E7086A1482D5FF87E271201932317A5" Directory="dir5BF786D89AC746B54F9B967AA07926B8" Guid="PUT-GUID-HERE">
+                <File Id="fil019F2B846E62B5B91A295E8419FF2355" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Windows\qtquickcontrols2windowsstyleplugin.dll" />
+            </Component>
+            <Component Id="cmpD286EF4971EE3CBC3CD0A133F2D1A670" Directory="dir5BF786D89AC746B54F9B967AA07926B8" Guid="PUT-GUID-HERE">
+                <File Id="filC5CA9BA0552F55E13DC8426A9DF08496" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Windows\RadioButton.qml" />
+            </Component>
+            <Component Id="cmpF327A2339C7A11964AABF6B9F652FF03" Directory="dir5BF786D89AC746B54F9B967AA07926B8" Guid="PUT-GUID-HERE">
+                <File Id="filCB25715DDB15881A1F71502EBD323089" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Windows\ScrollBar.qml" />
+            </Component>
+            <Component Id="cmp2C1245C0A84A37B8C23B42FB44963758" Directory="dir5BF786D89AC746B54F9B967AA07926B8" Guid="PUT-GUID-HERE">
+                <File Id="fil7E2F08DBD14FEA3964524C99658C1B5A" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Windows\ScrollView.qml" />
+            </Component>
+            <Component Id="cmp9133069D287F16E19B35C5C7B6044E3A" Directory="dir5BF786D89AC746B54F9B967AA07926B8" Guid="PUT-GUID-HERE">
+                <File Id="filD5421510C4C3CE49250C746303BFB31C" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Windows\SelectionRectangle.qml" />
+            </Component>
+            <Component Id="cmpEE7D4B815DD804200D22F38ABCD27A9E" Directory="dir5BF786D89AC746B54F9B967AA07926B8" Guid="PUT-GUID-HERE">
+                <File Id="fil890479C0BE4FBEB626D203A333EC6F30" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Windows\Slider.qml" />
+            </Component>
+            <Component Id="cmpB70AB13785BADC070517E87F1A8AD8E8" Directory="dir5BF786D89AC746B54F9B967AA07926B8" Guid="PUT-GUID-HERE">
+                <File Id="filCDB92D2CE105B4A7A04435787DA9836B" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Windows\SpinBox.qml" />
+            </Component>
+            <Component Id="cmp46BE888FD0CF137BEE5FDD9E7CE0AFB5" Directory="dir5BF786D89AC746B54F9B967AA07926B8" Guid="PUT-GUID-HERE">
+                <File Id="fil3E89DE0E7D5951AA3682EA072150DC10" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Windows\TextArea.qml" />
+            </Component>
+            <Component Id="cmp3A6FC96D5C6489B596B47615A6CB9C09" Directory="dir5BF786D89AC746B54F9B967AA07926B8" Guid="PUT-GUID-HERE">
+                <File Id="fil88212E2E7362494501102B5760BEA7FA" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Windows\TextField.qml" />
+            </Component>
+            <Component Id="cmp6BB722D488CF9C0E5CF4251DC77DE110" Directory="dirC14EEB8602D1812B8846FA3737C83C55" Guid="PUT-GUID-HERE">
+                <File Id="filE51FD3248165EF5A8434CD4F9DD668BA" KeyPath="yes" Source="SourceDir\QtQuick\Dialogs\plugins.qmltypes" />
+            </Component>
+            <Component Id="cmp741F3432BD35B45A41FDB40E304BEB0A" Directory="dirC14EEB8602D1812B8846FA3737C83C55" Guid="PUT-GUID-HERE">
+                <File Id="fil4529CD80F93002A4D807D810DD8169E0" KeyPath="yes" Source="SourceDir\QtQuick\Dialogs\qmldir" />
+            </Component>
+            <Component Id="cmp6C43EF3E8388BB2C7D46C5784E380B52" Directory="dirC14EEB8602D1812B8846FA3737C83C55" Guid="PUT-GUID-HERE">
+                <File Id="fil79C6181D3E8854EA573E7D82C562C3C1" KeyPath="yes" Source="SourceDir\QtQuick\Dialogs\qtquickdialogsplugin.dll" />
+            </Component>
+            <Component Id="cmp08BEB980E2729DAFADE97B8634BDE406" Directory="dir9B4F25D95E0FA7C4E83388EAF889E777" Guid="PUT-GUID-HERE">
+                <File Id="filB2C2D7BF0A3A15DDABDB696B15032171" KeyPath="yes" Source="SourceDir\QtQuick\Dialogs\quickimpl\plugins.qmltypes" />
+            </Component>
+            <Component Id="cmp0DA58AD9E8779979F94EDA7686430734" Directory="dir9B4F25D95E0FA7C4E83388EAF889E777" Guid="PUT-GUID-HERE">
+                <File Id="filA6C426D24C8B4B69ACE0064DBBB1AB5A" KeyPath="yes" Source="SourceDir\QtQuick\Dialogs\quickimpl\qmldir" />
+            </Component>
+            <Component Id="cmp53A16983A6B65DBA056D22C754BEFFBB" Directory="dir9B4F25D95E0FA7C4E83388EAF889E777" Guid="PUT-GUID-HERE">
+                <File Id="fil1A12DB1F35EA5264494B5034BE0405EC" KeyPath="yes" Source="SourceDir\QtQuick\Dialogs\quickimpl\qtquickdialogs2quickimplplugin.dll" />
+            </Component>
+            <Component Id="cmpBD0006BEDCE6A52F75A9E4796534B5FC" Directory="dir5A065D859888E6B36CD8ABD4BF35B615" Guid="PUT-GUID-HERE">
+                <File Id="filD87CA1491CB279F613BE404EE77EF708" KeyPath="yes" Source="SourceDir\QtQuick\Dialogs\quickimpl\qml\FileDialog.qml" />
+            </Component>
+            <Component Id="cmp9AE86FECBB97E284FFC5E155EF6210B8" Directory="dir5A065D859888E6B36CD8ABD4BF35B615" Guid="PUT-GUID-HERE">
+                <File Id="fil120FB34D88453E18D0B53BB5354F5CFB" KeyPath="yes" Source="SourceDir\QtQuick\Dialogs\quickimpl\qml\FileDialogDelegate.qml" />
+            </Component>
+            <Component Id="cmp2E8FF0E78E8FAB1186D43A9AC6FEB60F" Directory="dir5A065D859888E6B36CD8ABD4BF35B615" Guid="PUT-GUID-HERE">
+                <File Id="fil280CF0D59F5E8F4DD0F9ED1F7AFE91F6" KeyPath="yes" Source="SourceDir\QtQuick\Dialogs\quickimpl\qml\FileDialogDelegateLabel.qml" />
+            </Component>
+            <Component Id="cmp7F0E1A3957D9D6487496539F39E648B9" Directory="dir5A065D859888E6B36CD8ABD4BF35B615" Guid="PUT-GUID-HERE">
+                <File Id="fil7FFAD14DE995883CF5F474CB8BC600D5" KeyPath="yes" Source="SourceDir\QtQuick\Dialogs\quickimpl\qml\FolderBreadcrumbBar.qml" />
+            </Component>
+            <Component Id="cmpC570F1BF81B811749E7D778F3E9A4E0C" Directory="dir5A065D859888E6B36CD8ABD4BF35B615" Guid="PUT-GUID-HERE">
+                <File Id="fil1B6BF8D81E67AFA93D781B1B2A06881F" KeyPath="yes" Source="SourceDir\QtQuick\Dialogs\quickimpl\qml\FontDialog.qml" />
+            </Component>
+            <Component Id="cmp3C1322FAAC25FB323D8A9F079BF7B300" Directory="dir5A065D859888E6B36CD8ABD4BF35B615" Guid="PUT-GUID-HERE">
+                <File Id="fil5EFF7CAD771E72A6DCEEA0A713AAE1F2" KeyPath="yes" Source="SourceDir\QtQuick\Dialogs\quickimpl\qml\FontDialogContent.qml" />
+            </Component>
+            <Component Id="cmp4B491056D2B8FF4E74CE7253D57BC33F" Directory="dir7931425F6E509D7B972EE970C5C8EE72" Guid="PUT-GUID-HERE">
+                <File Id="fil399F52BE93EDAD8FF04BA019C008C59D" KeyPath="yes" Source="SourceDir\QtQuick\Dialogs\quickimpl\qml\+Fusion\FileDialog.qml" />
+            </Component>
+            <Component Id="cmp7C203A2B1798FF9860772C53A0B3CA4A" Directory="dir7931425F6E509D7B972EE970C5C8EE72" Guid="PUT-GUID-HERE">
+                <File Id="fil29A8E7EFCBEAA78565EA3B5133BBDD12" KeyPath="yes" Source="SourceDir\QtQuick\Dialogs\quickimpl\qml\+Fusion\FileDialogDelegate.qml" />
+            </Component>
+            <Component Id="cmpED32AA987458F362954681E246C0D0EC" Directory="dir7931425F6E509D7B972EE970C5C8EE72" Guid="PUT-GUID-HERE">
+                <File Id="filED3B4AD4B72B48D693576B7C52862B22" KeyPath="yes" Source="SourceDir\QtQuick\Dialogs\quickimpl\qml\+Fusion\FolderBreadcrumbBar.qml" />
+            </Component>
+            <Component Id="cmp2F197AFD32EA1AEEFB2F5A6D9C074523" Directory="dir7931425F6E509D7B972EE970C5C8EE72" Guid="PUT-GUID-HERE">
+                <File Id="filBDC89A40670D4D68D90F9C93EA32B4E2" KeyPath="yes" Source="SourceDir\QtQuick\Dialogs\quickimpl\qml\+Fusion\FontDialog.qml" />
+            </Component>
+            <Component Id="cmp063C49C671D9A253EB1C81EECAE960CC" Directory="dir54FC92B404B993A4BC39A98D15582E79" Guid="PUT-GUID-HERE">
+                <File Id="filA1FCE90F83293E73EB0C021DCA479BCE" KeyPath="yes" Source="SourceDir\QtQuick\Dialogs\quickimpl\qml\+Imagine\FileDialog.qml" />
+            </Component>
+            <Component Id="cmpDEBAA555D9500981A1E5BC6A3F766FAE" Directory="dir54FC92B404B993A4BC39A98D15582E79" Guid="PUT-GUID-HERE">
+                <File Id="filC18830D02EF0F5324823EDD4CCEE88B6" KeyPath="yes" Source="SourceDir\QtQuick\Dialogs\quickimpl\qml\+Imagine\FileDialogDelegate.qml" />
+            </Component>
+            <Component Id="cmp0AE1282EF97A00BC559C00D09A9AFB47" Directory="dir54FC92B404B993A4BC39A98D15582E79" Guid="PUT-GUID-HERE">
+                <File Id="fil836D153CED1BEBAD0B0D83D5BEA08616" KeyPath="yes" Source="SourceDir\QtQuick\Dialogs\quickimpl\qml\+Imagine\FolderBreadcrumbBar.qml" />
+            </Component>
+            <Component Id="cmp5CF0B28B79CBF8AA0A96A12D8314909C" Directory="dir54FC92B404B993A4BC39A98D15582E79" Guid="PUT-GUID-HERE">
+                <File Id="fil0A699C21404BA188D4C9521D4DC9C5D3" KeyPath="yes" Source="SourceDir\QtQuick\Dialogs\quickimpl\qml\+Imagine\FontDialog.qml" />
+            </Component>
+            <Component Id="cmp822E598C562C1400AA10AD5699DCE0CF" Directory="dir8024AEF9D92D06355510B5D11BC9CB61" Guid="PUT-GUID-HERE">
+                <File Id="filD6DB2DA899D51B7BA0777511935DEFB6" KeyPath="yes" Source="SourceDir\QtQuick\Dialogs\quickimpl\qml\+Material\FileDialog.qml" />
+            </Component>
+            <Component Id="cmpBE7DBAD3FFBDD0A6FEE3E4D7BB2E4500" Directory="dir8024AEF9D92D06355510B5D11BC9CB61" Guid="PUT-GUID-HERE">
+                <File Id="fil395D91707A007382A0E41BE3563DCA33" KeyPath="yes" Source="SourceDir\QtQuick\Dialogs\quickimpl\qml\+Material\FileDialogDelegate.qml" />
+            </Component>
+            <Component Id="cmp6A44D3919ACBB35BA724407199F5B232" Directory="dir8024AEF9D92D06355510B5D11BC9CB61" Guid="PUT-GUID-HERE">
+                <File Id="filB3750138093FB6985EB2A7393E268CB8" KeyPath="yes" Source="SourceDir\QtQuick\Dialogs\quickimpl\qml\+Material\FolderBreadcrumbBar.qml" />
+            </Component>
+            <Component Id="cmpEEBEBDE0A6706B05437353FFC06577A8" Directory="dir8024AEF9D92D06355510B5D11BC9CB61" Guid="PUT-GUID-HERE">
+                <File Id="filF4558ACD5D94AFE5C935A6B6B5DCB82A" KeyPath="yes" Source="SourceDir\QtQuick\Dialogs\quickimpl\qml\+Material\FontDialog.qml" />
+            </Component>
+            <Component Id="cmp7EDB6F95D746AA1CFADC8F03E3F90489" Directory="dir2FA1B14588FEA7C153D23281391CE63E" Guid="PUT-GUID-HERE">
+                <File Id="fil825AFFD5957EDB64A959DD0561FB635B" KeyPath="yes" Source="SourceDir\QtQuick\Dialogs\quickimpl\qml\+Universal\FileDialog.qml" />
+            </Component>
+            <Component Id="cmpF76AE841E954D7B21051ED16A4442EDF" Directory="dir2FA1B14588FEA7C153D23281391CE63E" Guid="PUT-GUID-HERE">
+                <File Id="filFAB12041E1B4B051DCDDE4EF80551291" KeyPath="yes" Source="SourceDir\QtQuick\Dialogs\quickimpl\qml\+Universal\FileDialogDelegate.qml" />
+            </Component>
+            <Component Id="cmp3A138D8A41289BF4F2F43748D9258045" Directory="dir2FA1B14588FEA7C153D23281391CE63E" Guid="PUT-GUID-HERE">
+                <File Id="fil945A580A5D4D5F5315CC92EC7511A62C" KeyPath="yes" Source="SourceDir\QtQuick\Dialogs\quickimpl\qml\+Universal\FolderBreadcrumbBar.qml" />
+            </Component>
+            <Component Id="cmpABE134A2EBFEE34A2C96F49CE87230B9" Directory="dir2FA1B14588FEA7C153D23281391CE63E" Guid="PUT-GUID-HERE">
+                <File Id="fil365E4AB154888FE45E7A5ADE8E4A329A" KeyPath="yes" Source="SourceDir\QtQuick\Dialogs\quickimpl\qml\+Universal\FontDialog.qml" />
+            </Component>
+            <Component Id="cmp42D10FA963CC0F5787806B3D22751506" Directory="dir25E537F6AD7990426F898BAE24EACE9D" Guid="PUT-GUID-HERE">
+                <File Id="fil91D796915632A8DABC35AB5C05CAC4CA" KeyPath="yes" Source="SourceDir\QtQuick\Layouts\plugins.qmltypes" />
+            </Component>
+            <Component Id="cmp49DE331F7ECBFD4B6496E456E9202BBD" Directory="dir25E537F6AD7990426F898BAE24EACE9D" Guid="PUT-GUID-HERE">
+                <File Id="fil949FA2140AB477002A7281B6460D11B3" KeyPath="yes" Source="SourceDir\QtQuick\Layouts\qmldir" />
+            </Component>
+            <Component Id="cmpAE9DE0B96DBCA94B2F3EBF16BB3A94DA" Directory="dir25E537F6AD7990426F898BAE24EACE9D" Guid="PUT-GUID-HERE">
+                <File Id="fil8505A4EEC74818F1E593A5170BAE3B9F" KeyPath="yes" Source="SourceDir\QtQuick\Layouts\qquicklayoutsplugin.dll" />
+            </Component>
+            <Component Id="cmp894EA37B6C07C1AFF2FF7385D96F4136" Directory="dirD2281A4769E4849962EB43101484B62C" Guid="PUT-GUID-HERE">
+                <File Id="filFEDFF984EDB2DA8EBB2F157D10C7AC86" KeyPath="yes" Source="SourceDir\QtQuick\LocalStorage\plugins.qmltypes" />
+            </Component>
+            <Component Id="cmp71902BDE985B8A3898433CF2FE8497AA" Directory="dirD2281A4769E4849962EB43101484B62C" Guid="PUT-GUID-HERE">
+                <File Id="filE2E12F2D3ABE11E9341038B6A4CFF6CF" KeyPath="yes" Source="SourceDir\QtQuick\LocalStorage\qmldir" />
+            </Component>
+            <Component Id="cmp412B93C6A674A57E344E4C06CC523E63" Directory="dirD2281A4769E4849962EB43101484B62C" Guid="PUT-GUID-HERE">
+                <File Id="filB65FCEE4A204A52B152F506E3475A7A9" KeyPath="yes" Source="SourceDir\QtQuick\LocalStorage\qmllocalstorageplugin.dll" />
+            </Component>
+            <Component Id="cmpCC37388AE8E9C4CC65133A3BFF1F3471" Directory="dir005903123E8E65ADACEE188EEB8D5FED" Guid="PUT-GUID-HERE">
+                <File Id="fil78C9F149A0F4F42EF89E5FA3C65CDD90" KeyPath="yes" Source="SourceDir\QtQuick\NativeStyle\plugins.qmltypes" />
+            </Component>
+            <Component Id="cmp47B299C26C816A390C90115A477DF243" Directory="dir005903123E8E65ADACEE188EEB8D5FED" Guid="PUT-GUID-HERE">
+                <File Id="fil048C4F21A9707861CC1FC3CB9D44FE06" KeyPath="yes" Source="SourceDir\QtQuick\NativeStyle\qmldir" />
+            </Component>
+            <Component Id="cmp90A955C9BC5C4A52DBD5D702B208CD05" Directory="dir005903123E8E65ADACEE188EEB8D5FED" Guid="PUT-GUID-HERE">
+                <File Id="fil73C32516FEA683CA4184237DBA008AA0" KeyPath="yes" Source="SourceDir\QtQuick\NativeStyle\qtquickcontrols2nativestyleplugin.dll" />
+            </Component>
+            <Component Id="cmp4C91050C1BC606A0599930B8EDFFBB2B" Directory="dir58487975ACF4D38FB978C7C039C7F158" Guid="PUT-GUID-HERE">
+                <File Id="fil1EC82E3920444D40F1C32A7127E6DC63" KeyPath="yes" Source="SourceDir\QtQuick\NativeStyle\controls\DefaultButton.qml" />
+            </Component>
+            <Component Id="cmp9BBE8F36F9F51D6FCDE56164634CFADB" Directory="dir58487975ACF4D38FB978C7C039C7F158" Guid="PUT-GUID-HERE">
+                <File Id="fil4EAF4E9D2CEC4CCF5C53B8FAE5238AA7" KeyPath="yes" Source="SourceDir\QtQuick\NativeStyle\controls\DefaultCheckBox.qml" />
+            </Component>
+            <Component Id="cmpECD7B0876D12394C8220DB77945DD434" Directory="dir58487975ACF4D38FB978C7C039C7F158" Guid="PUT-GUID-HERE">
+                <File Id="filC52706F4FDA6F45D673B949DA292AB0E" KeyPath="yes" Source="SourceDir\QtQuick\NativeStyle\controls\DefaultComboBox.qml" />
+            </Component>
+            <Component Id="cmpF1BD28E0623C9DE773CB2E937B6424EC" Directory="dir58487975ACF4D38FB978C7C039C7F158" Guid="PUT-GUID-HERE">
+                <File Id="filA7504C829C24C1DE33B93EC0E5C0E649" KeyPath="yes" Source="SourceDir\QtQuick\NativeStyle\controls\DefaultDial.qml" />
+            </Component>
+            <Component Id="cmp051842CF8FE3D1137616C232EE2D3575" Directory="dir58487975ACF4D38FB978C7C039C7F158" Guid="PUT-GUID-HERE">
+                <File Id="fil9D21375511DC7630EB7CDAC73ECAB057" KeyPath="yes" Source="SourceDir\QtQuick\NativeStyle\controls\DefaultFrame.qml" />
+            </Component>
+            <Component Id="cmp342648DA2DA6885B1468429E36BE06EB" Directory="dir58487975ACF4D38FB978C7C039C7F158" Guid="PUT-GUID-HERE">
+                <File Id="filCA066B1D13BF7FA84151E79B06E7DE69" KeyPath="yes" Source="SourceDir\QtQuick\NativeStyle\controls\DefaultGroupBox.qml" />
+            </Component>
+            <Component Id="cmpFA1E15CE76C93BD272389D9C32F55B34" Directory="dir58487975ACF4D38FB978C7C039C7F158" Guid="PUT-GUID-HERE">
+                <File Id="fil4207683472EC834F413508BA9B05E615" KeyPath="yes" Source="SourceDir\QtQuick\NativeStyle\controls\DefaultProgressBar.qml" />
+            </Component>
+            <Component Id="cmp7FD57642930EF39FA4599BE4802C75EA" Directory="dir58487975ACF4D38FB978C7C039C7F158" Guid="PUT-GUID-HERE">
+                <File Id="fil7DCE042084620C1F655F4A4E093B14DC" KeyPath="yes" Source="SourceDir\QtQuick\NativeStyle\controls\DefaultRadioButton.qml" />
+            </Component>
+            <Component Id="cmp071BE82A132951958F2232532EE2EE6B" Directory="dir58487975ACF4D38FB978C7C039C7F158" Guid="PUT-GUID-HERE">
+                <File Id="fil4200AF3B417230FE3B0F30E172A97E72" KeyPath="yes" Source="SourceDir\QtQuick\NativeStyle\controls\DefaultScrollBar.qml" />
+            </Component>
+            <Component Id="cmpF6226C87327DF4D998B01BEF7376EC39" Directory="dir58487975ACF4D38FB978C7C039C7F158" Guid="PUT-GUID-HERE">
+                <File Id="filE0EF8562357E0D5F66E87F419DF8FC3D" KeyPath="yes" Source="SourceDir\QtQuick\NativeStyle\controls\DefaultSlider.qml" />
+            </Component>
+            <Component Id="cmpB8FB38364C5563EFB005FB7DB448AA01" Directory="dir58487975ACF4D38FB978C7C039C7F158" Guid="PUT-GUID-HERE">
+                <File Id="fil477038CAC0614C163F76DF3E3ADEE13D" KeyPath="yes" Source="SourceDir\QtQuick\NativeStyle\controls\DefaultSpinBox.qml" />
+            </Component>
+            <Component Id="cmp5686F4E3083759CC017D006862B2D579" Directory="dir58487975ACF4D38FB978C7C039C7F158" Guid="PUT-GUID-HERE">
+                <File Id="fil957B452CC7F1B00930C5BE41249AFAFF" KeyPath="yes" Source="SourceDir\QtQuick\NativeStyle\controls\DefaultTextArea.qml" />
+            </Component>
+            <Component Id="cmp66E4ED6563300BD3B8633FBD3365F612" Directory="dir58487975ACF4D38FB978C7C039C7F158" Guid="PUT-GUID-HERE">
+                <File Id="fil5C150E53C4C75647FD50D2D56DB81945" KeyPath="yes" Source="SourceDir\QtQuick\NativeStyle\controls\DefaultTextField.qml" />
+            </Component>
+            <Component Id="cmp4A4AE02C0F83BB8FB5A861622C5AEDE5" Directory="dir90B3BDF6AA234878B0614AA16A2106A5" Guid="PUT-GUID-HERE">
+                <File Id="fil252BAC11D1191443F95EA90148C978B5" KeyPath="yes" Source="SourceDir\QtQuick\Particles\particlesplugin.dll" />
+            </Component>
+            <Component Id="cmp729D15F2AC478EC490D6CF877E3D4E7C" Directory="dir90B3BDF6AA234878B0614AA16A2106A5" Guid="PUT-GUID-HERE">
+                <File Id="filCAC30C15B89D46B843BECB4B8D57B13C" KeyPath="yes" Source="SourceDir\QtQuick\Particles\plugins.qmltypes" />
+            </Component>
+            <Component Id="cmpED9A78002DADC76CE30B1FD385E0B1E2" Directory="dir90B3BDF6AA234878B0614AA16A2106A5" Guid="PUT-GUID-HERE">
+                <File Id="filF34B1FB8399C0B42D8B3928AA49C3E7B" KeyPath="yes" Source="SourceDir\QtQuick\Particles\qmldir" />
+            </Component>
+            <Component Id="cmpD439D7A99D5E651813EB49EC0882F3BE" Directory="dirABDE04EACFBE2E08EF0F50035924A8DF" Guid="PUT-GUID-HERE">
+                <File Id="fil74A2E398BE6C7596DA7DE596B3F8F8FB" KeyPath="yes" Source="SourceDir\QtQuick\Shapes\plugins.qmltypes" />
+            </Component>
+            <Component Id="cmp1680E5779B149890D5A5507DCF06EE98" Directory="dirABDE04EACFBE2E08EF0F50035924A8DF" Guid="PUT-GUID-HERE">
+                <File Id="filC3C759716122B36496470D74063EF98C" KeyPath="yes" Source="SourceDir\QtQuick\Shapes\qmldir" />
+            </Component>
+            <Component Id="cmpBF182C25C9484DE2C8F81FF3BBAC0A92" Directory="dirABDE04EACFBE2E08EF0F50035924A8DF" Guid="PUT-GUID-HERE">
+                <File Id="fil53232A2116C7477545FCB24744234C70" KeyPath="yes" Source="SourceDir\QtQuick\Shapes\qmlshapesplugin.dll" />
+            </Component>
+            <Component Id="cmp8B5292AE63D8923F5CA6F2E0DB5913A1" Directory="dir8A47127E3442CB3A3D754FBB35FF92DC" Guid="PUT-GUID-HERE">
+                <File Id="filD1127CFA0BCE28FFF917169873EFD96C" KeyPath="yes" Source="SourceDir\QtQuick\Templates\plugins.qmltypes" />
+            </Component>
+            <Component Id="cmp91D5CD5761372BB05377C141862904F3" Directory="dir8A47127E3442CB3A3D754FBB35FF92DC" Guid="PUT-GUID-HERE">
+                <File Id="fil796B4670D5FA6AD7323B1D4691145D9B" KeyPath="yes" Source="SourceDir\QtQuick\Templates\qmldir" />
+            </Component>
+            <Component Id="cmpD368DD14F73B73FF975CD1D8CEEBFE1D" Directory="dir8A47127E3442CB3A3D754FBB35FF92DC" Guid="PUT-GUID-HERE">
+                <File Id="filA823C7A49089B90099CA49EDD36637A6" KeyPath="yes" Source="SourceDir\QtQuick\Templates\qtquicktemplates2plugin.dll" />
+            </Component>
+            <Component Id="cmpEACF597186E09F814895FA9D47B5E29C" Directory="dirC8FA895F8CD333726C0D487F219EA7A7" Guid="PUT-GUID-HERE">
+                <File Id="filB358E5A578D457E54A0EF042DAF60A33" KeyPath="yes" Source="SourceDir\QtQuick\tooling\Component.qml" />
+            </Component>
+            <Component Id="cmp5587BA20BAC0657E89B04B2E6D4241AB" Directory="dirC8FA895F8CD333726C0D487F219EA7A7" Guid="PUT-GUID-HERE">
+                <File Id="filB5D063FE0461A7737CE51D3A7FB375FE" KeyPath="yes" Source="SourceDir\QtQuick\tooling\Enum.qml" />
+            </Component>
+            <Component Id="cmpA4DB76EA3BD0A4BF5582D317448C18E5" Directory="dirC8FA895F8CD333726C0D487F219EA7A7" Guid="PUT-GUID-HERE">
+                <File Id="filC40BA93F96661082D93A8D8010DA87DF" KeyPath="yes" Source="SourceDir\QtQuick\tooling\Member.qml" />
+            </Component>
+            <Component Id="cmpCE67865698F21301B6ADAE2D32A5FAB7" Directory="dirC8FA895F8CD333726C0D487F219EA7A7" Guid="PUT-GUID-HERE">
+                <File Id="fil49F2C076ED538E221D4B5C38C347B867" KeyPath="yes" Source="SourceDir\QtQuick\tooling\Method.qml" />
+            </Component>
+            <Component Id="cmpB01579FBDB01BD41F64E7857C566956E" Directory="dirC8FA895F8CD333726C0D487F219EA7A7" Guid="PUT-GUID-HERE">
+                <File Id="filD4E1E28991AE1E0E70938484B0C885B8" KeyPath="yes" Source="SourceDir\QtQuick\tooling\Module.qml" />
+            </Component>
+            <Component Id="cmp7BB4FFAB0CA5270054C7793CAD410F6D" Directory="dirC8FA895F8CD333726C0D487F219EA7A7" Guid="PUT-GUID-HERE">
+                <File Id="fil4745AB0E32DF4F3C9587061F21B2AACE" KeyPath="yes" Source="SourceDir\QtQuick\tooling\Parameter.qml" />
+            </Component>
+            <Component Id="cmp731DFBDC900C1C9BB223B739755FD2CD" Directory="dirC8FA895F8CD333726C0D487F219EA7A7" Guid="PUT-GUID-HERE">
+                <File Id="fil2781FD54A8AB90F7F7CFC325B51DF81D" KeyPath="yes" Source="SourceDir\QtQuick\tooling\Property.qml" />
+            </Component>
+            <Component Id="cmpC26AD011E7FA325C674E2BA6A88DCF09" Directory="dirC8FA895F8CD333726C0D487F219EA7A7" Guid="PUT-GUID-HERE">
+                <File Id="fil019A2201C31B561A73311066C9DE8311" KeyPath="yes" Source="SourceDir\QtQuick\tooling\qmldir" />
+            </Component>
+            <Component Id="cmpC990907454F78A9E516FCFFE34EA0108" Directory="dirC8FA895F8CD333726C0D487F219EA7A7" Guid="PUT-GUID-HERE">
+                <File Id="fil3F71995BCDC0375D3BC5E7BB9031A4CF" KeyPath="yes" Source="SourceDir\QtQuick\tooling\quicktooling.qmltypes" />
+            </Component>
+            <Component Id="cmpAF18F8EFDA7D59547C85BFA6DBC13929" Directory="dirC8FA895F8CD333726C0D487F219EA7A7" Guid="PUT-GUID-HERE">
+                <File Id="filA5B3B18BA1555F81AEE40AF778B9EBC8" KeyPath="yes" Source="SourceDir\QtQuick\tooling\quicktoolingplugin.dll" />
+            </Component>
+            <Component Id="cmp0C6B8BA9D537F628E3097545E441EBB8" Directory="dirC8FA895F8CD333726C0D487F219EA7A7" Guid="PUT-GUID-HERE">
+                <File Id="filF47329CE4A386718B5A5EA11FB947408" KeyPath="yes" Source="SourceDir\QtQuick\tooling\Signal.qml" />
+            </Component>
+            <Component Id="cmp3C0189FA8242F9E1C2DE1F83B3CC19CB" Directory="dirD15C819041ADE7ED5CEE37C13C9C814C" Guid="PUT-GUID-HERE">
+                <File Id="fil7E80137ABBE4A40A694BD4670C290B8C" KeyPath="yes" Source="SourceDir\QtQuick\Window\qmldir" />
+            </Component>
+            <Component Id="cmp9D86EA60634EF3657D30B2609FE912F2" Directory="dirD15C819041ADE7ED5CEE37C13C9C814C" Guid="PUT-GUID-HERE">
+                <File Id="filBF2E7974F72A41ED254CD9C13493A149" KeyPath="yes" Source="SourceDir\QtQuick\Window\quickwindow.qmltypes" />
+            </Component>
+            <Component Id="cmp15ECA5760E267CCD81E10DB031BBAB43" Directory="dirD15C819041ADE7ED5CEE37C13C9C814C" Guid="PUT-GUID-HERE">
+                <File Id="fil2922F007C5B8D16F6C67EDB66EFC3F94" KeyPath="yes" Source="SourceDir\QtQuick\Window\quickwindowplugin.dll" />
+            </Component>
+            <Component Id="cmpE7E3F707C5E511F7D507BBD32EA1979E" Directory="dir8F7FCDAEC833D8963B08029F5D194C05" Guid="PUT-GUID-HERE">
+                <File Id="fil01A1242D7B0CEFE3F72FB22B7AE0C1BE" KeyPath="yes" Source="SourceDir\QtWebEngine\plugins.qmltypes" />
+            </Component>
+            <Component Id="cmpB717FBF9C2EE490798C0BC76BF337B6D" Directory="dir8F7FCDAEC833D8963B08029F5D194C05" Guid="PUT-GUID-HERE">
+                <File Id="fil23CA126EAA1FBEA81206309761002603" KeyPath="yes" Source="SourceDir\QtWebEngine\qmldir" />
+            </Component>
+            <Component Id="cmpFDC70989694DECD0EDE269DB9F4D0BAF" Directory="dir8F7FCDAEC833D8963B08029F5D194C05" Guid="PUT-GUID-HERE">
+                <File Id="fil31787DE56D19F02F0B205E6A9F89BD3A" KeyPath="yes" Source="SourceDir\QtWebEngine\qtwebenginequickplugin.dll" />
+            </Component>
+            <Component Id="cmpDF0F9BDC77CA471526B71486F35DFB9B" Directory="dir2E19425B618D245C054C135AF0EF7016" Guid="PUT-GUID-HERE">
+                <File Id="filFFE188A040A97569DC74BC81768AA5DE" KeyPath="yes" Source="SourceDir\QtWebEngine\ControlsDelegates\AlertDialog.qml" />
+            </Component>
+            <Component Id="cmpE1A25230A7E46503132FEC7A3C00AF0F" Directory="dir2E19425B618D245C054C135AF0EF7016" Guid="PUT-GUID-HERE">
+                <File Id="filFAC0218DCF618390C8F5BEE2640C4D08" KeyPath="yes" Source="SourceDir\QtWebEngine\ControlsDelegates\AuthenticationDialog.qml" />
+            </Component>
+            <Component Id="cmp2B1D11C446B55914DBE518FD72561E4E" Directory="dir2E19425B618D245C054C135AF0EF7016" Guid="PUT-GUID-HERE">
+                <File Id="filCCF8841D536DBDDFB69D210BE3D9D76E" KeyPath="yes" Source="SourceDir\QtWebEngine\ControlsDelegates\ColorDialog.qml" />
+            </Component>
+            <Component Id="cmpB2B720B0D1CF3C80F732E5E49EA82619" Directory="dir2E19425B618D245C054C135AF0EF7016" Guid="PUT-GUID-HERE">
+                <File Id="fil821C7A10D3271DB3BC5621CF9A60A7F2" KeyPath="yes" Source="SourceDir\QtWebEngine\ControlsDelegates\ConfirmDialog.qml" />
+            </Component>
+            <Component Id="cmp292A5DD1BF16818B059E728A64D4CA7F" Directory="dir2E19425B618D245C054C135AF0EF7016" Guid="PUT-GUID-HERE">
+                <File Id="filA0FCE9FC58F7C743CB4572AA310875AA" KeyPath="yes" Source="SourceDir\QtWebEngine\ControlsDelegates\FilePicker.qml" />
+            </Component>
+            <Component Id="cmpAA26FBCAAF96F7737078C402B7E31120" Directory="dir2E19425B618D245C054C135AF0EF7016" Guid="PUT-GUID-HERE">
+                <File Id="fil3756EB7973C98B536EBEA0A52780CE9D" KeyPath="yes" Source="SourceDir\QtWebEngine\ControlsDelegates\Menu.qml" />
+            </Component>
+            <Component Id="cmpDF8DBD17AAC0AAB693F23E2D1247356F" Directory="dir2E19425B618D245C054C135AF0EF7016" Guid="PUT-GUID-HERE">
+                <File Id="fil6E4A5DA543B4DDC83B13F5EA6E6977B2" KeyPath="yes" Source="SourceDir\QtWebEngine\ControlsDelegates\MenuItem.qml" />
+            </Component>
+            <Component Id="cmp24205A44E4FC113132ECE476CEF11D67" Directory="dir2E19425B618D245C054C135AF0EF7016" Guid="PUT-GUID-HERE">
+                <File Id="fil9E3F4A0B30C44991467E72B615BFFFD7" KeyPath="yes" Source="SourceDir\QtWebEngine\ControlsDelegates\MenuSeparator.qml" />
+            </Component>
+            <Component Id="cmp887D7B71BB53FF5089AEEAF6D2FC3346" Directory="dir2E19425B618D245C054C135AF0EF7016" Guid="PUT-GUID-HERE">
+                <File Id="fil6CC76EC60F31BAADF0AA814E7197CDAE" KeyPath="yes" Source="SourceDir\QtWebEngine\ControlsDelegates\PromptDialog.qml" />
+            </Component>
+            <Component Id="cmp3AB7D3E872EF4F725A60C59E13A86F31" Directory="dir2E19425B618D245C054C135AF0EF7016" Guid="PUT-GUID-HERE">
+                <File Id="fil85B05D80843E623312D04CEAF51CB410" KeyPath="yes" Source="SourceDir\QtWebEngine\ControlsDelegates\qmldir" />
+            </Component>
+            <Component Id="cmpA4059FF09BF38AC0BDD4F13757D659EA" Directory="dir2E19425B618D245C054C135AF0EF7016" Guid="PUT-GUID-HERE">
+                <File Id="fil507C36CCA4826044277CFC5FA352C7F8" KeyPath="yes" Source="SourceDir\QtWebEngine\ControlsDelegates\qtwebenginequickdelegatesplugin.dll" />
+            </Component>
+            <Component Id="cmpFF98E07BFF47BDFFF9BA32BA466A1381" Directory="dir2E19425B618D245C054C135AF0EF7016" Guid="PUT-GUID-HERE">
+                <File Id="fil952116F4ACB7206535133E7772824526" KeyPath="yes" Source="SourceDir\QtWebEngine\ControlsDelegates\ToolTip.qml" />
+            </Component>
+            <Component Id="cmpBB8D0A4FA47401781C7B8DA495C2DFAE" Directory="dir2E19425B618D245C054C135AF0EF7016" Guid="PUT-GUID-HERE">
+                <File Id="filDC668B3B40F974B0A5781DC00CB65175" KeyPath="yes" Source="SourceDir\QtWebEngine\ControlsDelegates\TouchHandle.qml" />
+            </Component>
+            <Component Id="cmp3140BF819007B9D17798BC88ACD07DD9" Directory="dir2E19425B618D245C054C135AF0EF7016" Guid="PUT-GUID-HERE">
+                <File Id="fil9891C37A1C4B5AE90246674EFCFD727F" KeyPath="yes" Source="SourceDir\QtWebEngine\ControlsDelegates\TouchSelectionMenu.qml" />
+            </Component>
+            <Component Id="cmp62DBE17548E4B21AAE3863D932966787" Directory="dir2E19425B618D245C054C135AF0EF7016" Guid="PUT-GUID-HERE">
+                <File Id="fil19BF70606CFCB78877764CAAF59A65EC" KeyPath="yes" Source="SourceDir\QtWebEngine\ControlsDelegates\WebEngineQuickDelegatesQml.qmltypes" />
+            </Component>
+            <Component Id="cmpFC8A4A78B4CE6E58788FAA6683F3EE04" Directory="dir6D65237267C4C59098A0E61E06E59952" Guid="PUT-GUID-HERE">
+                <File Id="fil1D56A5525238C8F2C6EBF4CFA2996B74" KeyPath="yes" Source="SourceDir\QtWebView\plugins.qmltypes" />
+            </Component>
+            <Component Id="cmp3DF6BDEE2FC0EED062F14241DE7A96BE" Directory="dir6D65237267C4C59098A0E61E06E59952" Guid="PUT-GUID-HERE">
+                <File Id="fil7039F0E9C61B5A059A7DD4774086AADC" KeyPath="yes" Source="SourceDir\QtWebView\qmldir" />
+            </Component>
+            <Component Id="cmp35DC69EF5B19FDA08112276BEC5FCD1C" Directory="dir6D65237267C4C59098A0E61E06E59952" Guid="PUT-GUID-HERE">
+                <File Id="filC4336BF378848043A038FDA65D177107" KeyPath="yes" Source="SourceDir\QtWebView\qtwebviewquickplugin.dll" />
+            </Component>
+            <Component Id="cmp7939DBD081402A3D5A1E67BDD3FDC674" Directory="dir08941CEA6046320FCCF158F759AF80F2" Guid="PUT-GUID-HERE">
+                <File Id="fil51E0E82B321287CAC39351EA74609C90" KeyPath="yes" Source="SourceDir\resources\icudtl.dat" />
+            </Component>
+            <Component Id="cmpFCDF3702D9093A75BC161A3A1BB6BB86" Directory="dir08941CEA6046320FCCF158F759AF80F2" Guid="PUT-GUID-HERE">
+                <File Id="fil5E3E79E2C8D58B41AC6EF5891C08A810" KeyPath="yes" Source="SourceDir\resources\qtwebengine_devtools_resources.pak" />
+            </Component>
+            <Component Id="cmp09CCD93B93EDDE01A6B28A19B724194A" Directory="dir08941CEA6046320FCCF158F759AF80F2" Guid="PUT-GUID-HERE">
+                <File Id="fil08082A21F54192390BB8CCF88F7E6B9D" KeyPath="yes" Source="SourceDir\resources\qtwebengine_resources.pak" />
+            </Component>
+            <Component Id="cmp263082AF02EA4B9F434F2501E23AC972" Directory="dir08941CEA6046320FCCF158F759AF80F2" Guid="PUT-GUID-HERE">
+                <File Id="fil24B7F212EFD5AB10B522561407BDDFC9" KeyPath="yes" Source="SourceDir\resources\qtwebengine_resources_100p.pak" />
+            </Component>
+            <Component Id="cmpC2267073DB55586843E13B823F6E0E8B" Directory="dir08941CEA6046320FCCF158F759AF80F2" Guid="PUT-GUID-HERE">
+                <File Id="filFDCD09910FB501B1A747E0A04B8D3E31" KeyPath="yes" Source="SourceDir\resources\qtwebengine_resources_200p.pak" />
+            </Component>
+            <Component Id="cmp48ADD1B8E5492EE4CAF352021174B247" Directory="dirD0D2BD28387A827DDF373DED43543ABA" Guid="PUT-GUID-HERE">
+                <File Id="filDE0A213E8CE9DC57CA82C7CBF94A73CA" KeyPath="yes" Source="SourceDir\sqldrivers\qsqlite.dll" />
+            </Component>
+            <Component Id="cmpF7561543EA617CDB8DB9446A484C743E" Directory="dirD0D2BD28387A827DDF373DED43543ABA" Guid="PUT-GUID-HERE">
+                <File Id="filA1594F9BAF7751AC04C6C8465D7FF4EA" KeyPath="yes" Source="SourceDir\sqldrivers\qsqlmysql.dll" />
+            </Component>
+            <Component Id="cmpFA34FD86FCB201001C1C95C2210B09BB" Directory="dirD0D2BD28387A827DDF373DED43543ABA" Guid="PUT-GUID-HERE">
+                <File Id="filABD7D7F4392312E7C2C8CDC617BC2016" KeyPath="yes" Source="SourceDir\sqldrivers\qsqlodbc.dll" />
+            </Component>
+            <Component Id="cmp5DAC8E92F0D1CE1028B76D02F12152DE" Directory="dir3BC41331752E78D0C2719C277915294F" Guid="PUT-GUID-HERE">
+                <File Id="fil4F3C3348649DA965E980E99B9E82B7FF" KeyPath="yes" Source="SourceDir\styles\qwindowsvistastyle.dll" />
+            </Component>
+            <Component Id="cmpD57693E12A6CF9AFDC034864F2950D6F" Directory="dir509C75F94B9AF6C35CA00410005C14EE" Guid="PUT-GUID-HERE">
+                <File Id="filA3197B081536A45213DBC7CD12A615C4" KeyPath="yes" Source="SourceDir\tls\qcertonlybackend.dll" />
+            </Component>
+            <Component Id="cmpC8A93EB5F0E2881753F8DCDA1B02129B" Directory="dir509C75F94B9AF6C35CA00410005C14EE" Guid="PUT-GUID-HERE">
+                <File Id="fil63F4FB8EB696B5BA01AD976846F45663" KeyPath="yes" Source="SourceDir\tls\qschannelbackend.dll" />
+            </Component>
+            <Component Id="cmpE7627EECD23898BC1AEEEBA80CF4C18E" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="PUT-GUID-HERE">
+                <File Id="fil841A28DBCBF2256981B583E30DC241AF" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\am.pak" />
+            </Component>
+            <Component Id="cmp4AA1EF8E6B3E8E5742AC3878237B15BD" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="PUT-GUID-HERE">
+                <File Id="fil7B1E977DD95738FCA43A5AF4E3DB820F" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\ar.pak" />
+            </Component>
+            <Component Id="cmp489F46694306763E664E68F1A1D9398A" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="PUT-GUID-HERE">
+                <File Id="filF1068C95C51734B905AD68641F8932DE" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\bg.pak" />
+            </Component>
+            <Component Id="cmpCB3806B057A11129336C03B60DB93368" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="PUT-GUID-HERE">
+                <File Id="fil5E98C112CCDD7A81098634A800367973" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\bn.pak" />
+            </Component>
+            <Component Id="cmp117BEDE3E308B6346D00371AE16FC727" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="PUT-GUID-HERE">
+                <File Id="fil1F344AC58CF6783633B80F8532689D84" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\ca.pak" />
+            </Component>
+            <Component Id="cmp1B4BD11EB6D78DE042388FC7B3B10979" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="PUT-GUID-HERE">
+                <File Id="fil2DFDD403496C8DA8549416381A275F4B" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\cs.pak" />
+            </Component>
+            <Component Id="cmp8B5804D6EA4CE8594E25FA2EAA53B2CE" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="PUT-GUID-HERE">
+                <File Id="fil8998ADC69728900F32D27523BE7C0A7A" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\da.pak" />
+            </Component>
+            <Component Id="cmp14635E19526FBB5CC031D48A21B8DFE4" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="PUT-GUID-HERE">
+                <File Id="filE4885D67548AFA65C78C08300C700419" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\de.pak" />
+            </Component>
+            <Component Id="cmp0DD34B1E211835A0825C4F37AEC7D421" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="PUT-GUID-HERE">
+                <File Id="fil544A6F5F8C37D55BB452375A1C857B0E" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\el.pak" />
+            </Component>
+            <Component Id="cmpBB56FFD9B4B03F1B7EFFE608596C1DF0" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="PUT-GUID-HERE">
+                <File Id="filC138B25B3D7A4129D2F01586DDAB4929" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\en-GB.pak" />
+            </Component>
+            <Component Id="cmp7D1B7857542317367F521676B9C49306" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="PUT-GUID-HERE">
+                <File Id="fil1726E7077A64870D0E207D3578CBBDF4" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\en-US.pak" />
+            </Component>
+            <Component Id="cmpAC456BCAF3DCFA01198913534FCD2F3E" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="PUT-GUID-HERE">
+                <File Id="fil2DFEE5CE81BFF7E138A6954DE8118BBF" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\es-419.pak" />
+            </Component>
+            <Component Id="cmp561D4111326710AC9AA46907E05AE2F8" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="PUT-GUID-HERE">
+                <File Id="fil5149EA34D842EEA58E32E2FF3EA9FC74" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\es.pak" />
+            </Component>
+            <Component Id="cmp767F92E764F493FC663E591BBF691924" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="PUT-GUID-HERE">
+                <File Id="fil175C98AD989D0A19A6051CECB41755E7" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\et.pak" />
+            </Component>
+            <Component Id="cmp83CE7D31B03F0E640D486C8951C9F88A" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="PUT-GUID-HERE">
+                <File Id="fil80A0D15334F4AE3C93E9AB996047F063" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\fa.pak" />
+            </Component>
+            <Component Id="cmpB556D1FABA724DC54358690B4A4F5528" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="PUT-GUID-HERE">
+                <File Id="fil9E20F6D165CA887227872340CF842DB7" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\fi.pak" />
+            </Component>
+            <Component Id="cmp2229E25AED1AE14594087A37BF635290" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="PUT-GUID-HERE">
+                <File Id="filF183998CF77694A80E81B414ADAAD6F6" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\fil.pak" />
+            </Component>
+            <Component Id="cmp7E4EE520FFDF02CB789F8284CCF745D8" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="PUT-GUID-HERE">
+                <File Id="fil2CEB374D9A3E9F8697A7245C40BAE974" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\fr.pak" />
+            </Component>
+            <Component Id="cmpB06A2E85562494B9B6F676A03E87D654" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="PUT-GUID-HERE">
+                <File Id="filBC5A940DFAF4F4289C03179C3331CA32" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\gu.pak" />
+            </Component>
+            <Component Id="cmpCCDE1181B25851E3340339CF61382B80" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="PUT-GUID-HERE">
+                <File Id="fil96390711BE9B947D7B7FA5E0211E42E9" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\he.pak" />
+            </Component>
+            <Component Id="cmp25690B79DEAC48ED058A09275BBAAC7B" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="PUT-GUID-HERE">
+                <File Id="fil85370AA4C63C2DECEC33E30E3DB303F9" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\hi.pak" />
+            </Component>
+            <Component Id="cmp6EE71F73860FDA82CE0BA04F55AB4E65" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="PUT-GUID-HERE">
+                <File Id="fil8F215425A3BC657C3D4C352ECD1F7101" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\hr.pak" />
+            </Component>
+            <Component Id="cmpD21C6E91A9244444957B19A630773D29" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="PUT-GUID-HERE">
+                <File Id="filE9F1E4FD2A7A617A4E4166414AB45691" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\hu.pak" />
+            </Component>
+            <Component Id="cmp38389365912F0AE1DFACDDC8479C7BF2" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="PUT-GUID-HERE">
+                <File Id="fil7D1F559C2C1110D18CA5764F1971BBA7" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\id.pak" />
+            </Component>
+            <Component Id="cmp9BCC30608F875FB2B6809ACBE266A0EB" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="PUT-GUID-HERE">
+                <File Id="fil4CA2DD502CC46D09E241817165D07A69" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\it.pak" />
+            </Component>
+            <Component Id="cmp66B3A389FF5706DD47F6031C96870AC3" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="PUT-GUID-HERE">
+                <File Id="fil9A44734AE8E81C970FCD4CD1A15562EC" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\ja.pak" />
+            </Component>
+            <Component Id="cmpC35D767A0329A304ED9409EE1758B26E" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="PUT-GUID-HERE">
+                <File Id="filBEF8A5F318E161CAB9EB973C81EE8BAD" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\kn.pak" />
+            </Component>
+            <Component Id="cmp4FAE7008D38C2834FF608ECB10F422B3" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="PUT-GUID-HERE">
+                <File Id="fil0E05F04BA9603B8A46D202EE87E01968" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\ko.pak" />
+            </Component>
+            <Component Id="cmp41E232D5090F2D0E0B1781C90734A625" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="PUT-GUID-HERE">
+                <File Id="filB1E277BB14B69222B7C7DCC91A1E41D0" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\lt.pak" />
+            </Component>
+            <Component Id="cmp68559A181BEB78EFD5137F57CAD5FB00" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="PUT-GUID-HERE">
+                <File Id="fil75045C6FE752E32D2CA7E724F92A4C03" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\lv.pak" />
+            </Component>
+            <Component Id="cmp0F1F24602E525646795B185F7B39955A" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="PUT-GUID-HERE">
+                <File Id="fil4EAF6A9B314FEFD4868F09695F2883EF" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\ml.pak" />
+            </Component>
+            <Component Id="cmpAD12B9F7ABF28F48C3C94ADA02853AD5" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="PUT-GUID-HERE">
+                <File Id="filD4AE47CF3E0E2219C965A01B69346DC6" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\mr.pak" />
+            </Component>
+            <Component Id="cmpA6990FFF2D1086D28FBE7A600D52F67C" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="PUT-GUID-HERE">
+                <File Id="fil8DA799DEB9484A9C1D8C766F558D928B" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\ms.pak" />
+            </Component>
+            <Component Id="cmp74CC7AE3E2D73D6EB448973554564268" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="PUT-GUID-HERE">
+                <File Id="filD917C45F7D063800C10DFF694350E88F" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\nb.pak" />
+            </Component>
+            <Component Id="cmpCF99C5168606D7B5069B19761014FF91" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="PUT-GUID-HERE">
+                <File Id="fil0BBBA2ED5EA505A0761814636A3E14CC" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\nl.pak" />
+            </Component>
+            <Component Id="cmpCF5AA8564BF2B7AECAAE7FFE84D8E4D9" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="PUT-GUID-HERE">
+                <File Id="filBBE0AA8ADCAD675D5BE7906A7557CA33" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\pl.pak" />
+            </Component>
+            <Component Id="cmp1E4EF1965C96EAF1EDF0ED36C03EAE83" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="PUT-GUID-HERE">
+                <File Id="fil6A9727728F57E501FC885A71011F615B" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\pt-BR.pak" />
+            </Component>
+            <Component Id="cmp291C58BF5375B658AD36A7D92A0C8479" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="PUT-GUID-HERE">
+                <File Id="fil5444B8CC9431DD88F907C57EA9A90572" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\pt-PT.pak" />
+            </Component>
+            <Component Id="cmpAE702D6C4F5F596DB6B15FB36C6D9DAB" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="PUT-GUID-HERE">
+                <File Id="filB15BEEE3DA276ADEA0E8A24C77D5A9C2" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\ro.pak" />
+            </Component>
+            <Component Id="cmpDF7385B2F039DBE31A2545C254ABF0F0" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="PUT-GUID-HERE">
+                <File Id="filBCB5F742C4B473A8C6C5CF4917E6144A" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\ru.pak" />
+            </Component>
+            <Component Id="cmpDF8F1EB0E2A70BD5F396A75FECB92533" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="PUT-GUID-HERE">
+                <File Id="fil9769BAFE384BB1853A2C508DFA05229F" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\sk.pak" />
+            </Component>
+            <Component Id="cmpDB5BD4BF5A46761C6544B87CF7085EEB" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="PUT-GUID-HERE">
+                <File Id="filA84154373398754DDDB4E950A134C5F5" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\sl.pak" />
+            </Component>
+            <Component Id="cmp1C71511A57869D1F5DD784E9E4191BFD" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="PUT-GUID-HERE">
+                <File Id="fil5F171B8A37BA8123184CABD8A9F23CFA" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\sr.pak" />
+            </Component>
+            <Component Id="cmpB922A5B2744CD81821403BFD931F8112" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="PUT-GUID-HERE">
+                <File Id="fil20AA02ACBBE6A5F32DE18FF2F0A7AEFF" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\sv.pak" />
+            </Component>
+            <Component Id="cmp6DAB86BB3D2953D10957B0FAFF22E155" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="PUT-GUID-HERE">
+                <File Id="fil6199BA4B33801E936F1847E792F5CAAB" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\sw.pak" />
+            </Component>
+            <Component Id="cmpFC87AE461BD4B51F4A19E039A3C25CA1" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="PUT-GUID-HERE">
+                <File Id="fil38C023D4016EF85C52C0B835FC94F0C7" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\ta.pak" />
+            </Component>
+            <Component Id="cmp58B01AD92CF1FEEF6EA0325F60D05AA6" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="PUT-GUID-HERE">
+                <File Id="fil09449582AB62FB08D7CD3574754B8C0C" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\te.pak" />
+            </Component>
+            <Component Id="cmpA3A2E454F58F8435858E88AC73495360" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="PUT-GUID-HERE">
+                <File Id="fil27FE0443556E7587E13EE46CFBF62A4F" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\th.pak" />
+            </Component>
+            <Component Id="cmp15CD85788F2B7BE37D3D33C7B69D5FF6" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="PUT-GUID-HERE">
+                <File Id="fil55F7FECA91CEE1F865B75C6581438FFC" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\tr.pak" />
+            </Component>
+            <Component Id="cmp2D4EB727F69B23FADD8074CB77E51F36" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="PUT-GUID-HERE">
+                <File Id="filC97CBBD646D7E81182BFE9ECB61844CA" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\uk.pak" />
+            </Component>
+            <Component Id="cmp43AF26BE2B604BD873763D73460FB4B9" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="PUT-GUID-HERE">
+                <File Id="fil467C07F74FBFC01F0D823F0E76F07FB7" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\vi.pak" />
+            </Component>
+            <Component Id="cmp8CCECC99D6C73FD198EA365D4835864F" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="PUT-GUID-HERE">
+                <File Id="filD0D4650DF850FAD942030BBC923D8D7A" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\zh-CN.pak" />
+            </Component>
+            <Component Id="cmp9F8E9C0770E5832169033250113D1A21" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="PUT-GUID-HERE">
+                <File Id="fil2FAD3F60E739CE8D347E5ADE50EA653C" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\zh-TW.pak" />
+            </Component>
+            <Component Id="cmp8F41C0E31AFDC29F8BDF013E907B2A5B" Directory="dir90EFFBA81E5867E3E251FBDCB82DBC0C" Guid="PUT-GUID-HERE">
+                <File Id="filCF87AAEC5D091D6D8852B61917DB3BBF" KeyPath="yes" Source="SourceDir\webview\qtwebview_webengine.dll" />
+            </Component>
+        </ComponentGroup>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dir9D005E7F3893047E4557D6C5273ADD9E">
+            <Directory Id="dir005903123E8E65ADACEE188EEB8D5FED" Name="NativeStyle" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dir70AFC0A9D84A710EC0128D3FED451CDC">
+            <Directory Id="dir040BCEF4BA1A47FD347F1E983F275641" Name="WorkerScript" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dirDCD6F45EC68D7ABC2968E38BA6C2E3B4">
+            <Directory Id="dir124EAE5849AC3D2D22C86A18E3063919" Name="Universal" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dir4EBCE940C66AD416A8F8D45D795DED89">
+            <Directory Id="dir24D778EAF9E4AD78E4448B53856DC5AB" Name="private" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dir9D005E7F3893047E4557D6C5273ADD9E">
+            <Directory Id="dir25E537F6AD7990426F898BAE24EACE9D" Name="Layouts" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dir8F7FCDAEC833D8963B08029F5D194C05">
+            <Directory Id="dir2E19425B618D245C054C135AF0EF7016" Name="ControlsDelegates" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dir5A065D859888E6B36CD8ABD4BF35B615">
+            <Directory Id="dir2FA1B14588FEA7C153D23281391CE63E" Name="+Universal" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dirDCD6F45EC68D7ABC2968E38BA6C2E3B4">
+            <Directory Id="dir3F609D3494FEF5EC2888E9B865EA8336" Name="Imagine" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dir124EAE5849AC3D2D22C86A18E3063919">
+            <Directory Id="dir40011B386F4B1F0F2AE25C2E3ADB18E8" Name="impl" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dirB7DD0BB42FA107F6BC6439938BFE98A2">
+            <Directory Id="dir48925D264F208B9FCBF827D59CF75478" Name="impl" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dir70AFC0A9D84A710EC0128D3FED451CDC">
+            <Directory Id="dir49DBFE81A3C2546F0A0D7B91984B25A9" Name="Models" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dirDF4CFBD3627B084EE34DEDC7F0745F0E">
+            <Directory Id="dir4EBCE940C66AD416A8F8D45D795DED89" Name="GraphicalEffects" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dir5A065D859888E6B36CD8ABD4BF35B615">
+            <Directory Id="dir54FC92B404B993A4BC39A98D15582E79" Name="+Imagine" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dir005903123E8E65ADACEE188EEB8D5FED">
+            <Directory Id="dir58487975ACF4D38FB978C7C039C7F158" Name="controls" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dir9B4F25D95E0FA7C4E83388EAF889E777">
+            <Directory Id="dir5A065D859888E6B36CD8ABD4BF35B615" Name="qml" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dirDCD6F45EC68D7ABC2968E38BA6C2E3B4">
+            <Directory Id="dir5BF786D89AC746B54F9B967AA07926B8" Name="Windows" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dirDCD6F45EC68D7ABC2968E38BA6C2E3B4">
+            <Directory Id="dir68B807445C3B5B5D97077A041B375DC5" Name="impl" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dirDCD6F45EC68D7ABC2968E38BA6C2E3B4">
+            <Directory Id="dir7849AFCA9B74D36A839FBB59A8264BFC" Name="Basic" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dir5A065D859888E6B36CD8ABD4BF35B615">
+            <Directory Id="dir7931425F6E509D7B972EE970C5C8EE72" Name="+Fusion" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dir3F609D3494FEF5EC2888E9B865EA8336">
+            <Directory Id="dir7970C160330F95B9F7E3CCDAB2CF5702" Name="impl" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dir5A065D859888E6B36CD8ABD4BF35B615">
+            <Directory Id="dir8024AEF9D92D06355510B5D11BC9CB61" Name="+Material" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dir9D005E7F3893047E4557D6C5273ADD9E">
+            <Directory Id="dir8A47127E3442CB3A3D754FBB35FF92DC" Name="Templates" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dir9D005E7F3893047E4557D6C5273ADD9E">
+            <Directory Id="dir90B3BDF6AA234878B0614AA16A2106A5" Name="Particles" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dirC14EEB8602D1812B8846FA3737C83C55">
+            <Directory Id="dir9B4F25D95E0FA7C4E83388EAF889E777" Name="quickimpl" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dir1F7AD7AA9B228AED2E74CDD7C3088B5C">
+            <Directory Id="dirA016FBC978C7F28C3C5CDBC06002A5D6" Name="qtwebengine_locales" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dir9D005E7F3893047E4557D6C5273ADD9E">
+            <Directory Id="dirABDE04EACFBE2E08EF0F50035924A8DF" Name="Shapes" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dirDCD6F45EC68D7ABC2968E38BA6C2E3B4">
+            <Directory Id="dirB7DD0BB42FA107F6BC6439938BFE98A2" Name="Fusion" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dir9D005E7F3893047E4557D6C5273ADD9E">
+            <Directory Id="dirC14EEB8602D1812B8846FA3737C83C55" Name="Dialogs" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dir9D005E7F3893047E4557D6C5273ADD9E">
+            <Directory Id="dirC8FA895F8CD333726C0D487F219EA7A7" Name="tooling" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dir9D005E7F3893047E4557D6C5273ADD9E">
+            <Directory Id="dirD15C819041ADE7ED5CEE37C13C9C814C" Name="Window" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dir9D005E7F3893047E4557D6C5273ADD9E">
+            <Directory Id="dirD2281A4769E4849962EB43101484B62C" Name="LocalStorage" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dirF45E3860752A8B1AC79318D838CCE535">
+            <Directory Id="dirD4CF5DF9BDC9CA55E064A8C389B40501" Name="impl" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dir7849AFCA9B74D36A839FBB59A8264BFC">
+            <Directory Id="dirDBD23FE4F11ED29A51E2F3787280CC12" Name="impl" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dir9D005E7F3893047E4557D6C5273ADD9E">
+            <Directory Id="dirDCD6F45EC68D7ABC2968E38BA6C2E3B4" Name="Controls" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dir70AFC0A9D84A710EC0128D3FED451CDC">
+            <Directory Id="dirDF3BB8A52CE36475D7FA56568CBEA7A4" Name="XmlListModel" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dirDCD6F45EC68D7ABC2968E38BA6C2E3B4">
+            <Directory Id="dirF45E3860752A8B1AC79318D838CCE535" Name="Material" />
+        </DirectoryRef>
+    </Fragment>
+</Wix>
\ No newline at end of file
diff --git a/win/qt6.wxs b/win/qt6.wxs
new file mode 100644 (file)
index 0000000..ed9ced1
--- /dev/null
@@ -0,0 +1,1966 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
+    <Fragment>
+        <DirectoryRef Id="INSTALLDIR">
+            <Directory Id="dirB10C059F651528B59E1FD268EB08C6D6" Name="iconengines" />
+            <Directory Id="dir20189C824D4DDABC3D9FF9449C089CE0" Name="imageformats" />
+            <Directory Id="dir690B8E6D609B0DB17DB71E8B1FB2FEC5" Name="LICENSES" />
+            <Directory Id="dir4C08960BD61EEE75AE4A87FC65A5DE2A" Name="networkinformation" />
+            <Directory Id="dir826692792B0995C8DF68919F5C684331" Name="platforms" />
+            <Directory Id="dirC15EA267FAD4FC447E84FB3A32CBDB68" Name="position" />
+            <Directory Id="dir9331125B8BF1F459D0AC9F91A6A2F779" Name="qmltooling" />
+            <Directory Id="dirDF4CFBD3627B084EE34DEDC7F0745F0E" Name="Qt5Compat" />
+            <Directory Id="dir70AFC0A9D84A710EC0128D3FED451CDC" Name="QtQml" />
+            <Directory Id="dir9D005E7F3893047E4557D6C5273ADD9E" Name="QtQuick" />
+            <Directory Id="dir8F7FCDAEC833D8963B08029F5D194C05" Name="QtWebEngine" />
+            <Directory Id="dir6D65237267C4C59098A0E61E06E59952" Name="QtWebView" />
+            <Directory Id="dir08941CEA6046320FCCF158F759AF80F2" Name="resources" />
+            <Directory Id="dirD0D2BD28387A827DDF373DED43543ABA" Name="sqldrivers" />
+            <Directory Id="dir3BC41331752E78D0C2719C277915294F" Name="styles" />
+            <Directory Id="dir509C75F94B9AF6C35CA00410005C14EE" Name="tls" />
+            <Directory Id="dir1F7AD7AA9B228AED2E74CDD7C3088B5C" Name="translations" />
+            <Directory Id="dir90EFFBA81E5867E3E251FBDCB82DBC0C" Name="webview" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <ComponentGroup Id="jacktrip">
+            <Component Id="cmpBD33227FC5AD6BA1A293384B57B5C3E1" Directory="INSTALLDIR" Guid="{887636EF-438E-4FE7-867F-50D3594FF8A9}">
+                <File Id="filF7FA605CFD2E8074C565F825A465F67E" KeyPath="yes" Source="SourceDir\D3Dcompiler_47.dll" />
+            </Component>
+            <Component Id="cmpAD46F02BC5359E4227248E749EB0E74F" Directory="INSTALLDIR" Guid="{B912A765-5293-4F00-822B-0B355CF6C665}">
+                <File Id="fil572E83C11D35ADB0E4CB1A0A0AF474C6" KeyPath="yes" Source="SourceDir\dialog.bmp" />
+            </Component>
+            <Component Id="cmpE6579DC78E167F46AB23E0BE3EAA58CE" Directory="INSTALLDIR" Guid="{27BFCC7D-F4A8-4F71-8A2B-00A8BC74B106}">
+                <File Id="fil5E6E1243EAE085FE802B6D64690357B0" KeyPath="yes" Source="SourceDir\jacktrip.exe" />
+                <Shortcut Id="startmenuJackTrip" Directory="ProgramMenuDir" Name="JackTrip"
+                    WorkingDirectory="INSTALLDIR" Icon="jacktrip.exe" IconIndex="0" Advertise="yes" />
+            </Component>
+            <Component Id="cmp64BB59E0E19E219D4D4D5C775D71731F" Directory="INSTALLDIR" Guid="{AC1AB986-E75A-4F3D-B849-F0CB485F1573}">
+                <File Id="fil7D228BFB31A8A99EE307AF8BFC2E1841" KeyPath="yes" Source="SourceDir\LICENSE.md" />
+            </Component>
+            <Component Id="cmpE2BC49F17425202019B2C48873956253" Directory="INSTALLDIR" Guid="{4F42E138-BC56-4253-8AE7-47D1C92EA803}">
+                <File Id="fil011E2822115DD1D5BD9A9096D1914F51" KeyPath="yes" Source="SourceDir\license.rtf" />
+            </Component>
+            <Component Id="cmp7111706DBFB0E180EC7A8BDC93A90139" Directory="INSTALLDIR" Guid="{B19ABB59-5607-4637-93D8-C03CA180767F}">
+                <File Id="fil1A08189509DCFE1E185AEC6B499929BA" KeyPath="yes" Source="SourceDir\Qt6Core.dll" />
+            </Component>
+            <Component Id="cmpD22BE3A3E3A804D9EC17161FD6ED218F" Directory="INSTALLDIR" Guid="{4119B512-1A33-4A9B-B0AA-69D22F8C9658}">
+                <File Id="fil0427F270C0F2EA2C38D6BB72F32BC982" KeyPath="yes" Source="SourceDir\Qt6Gui.dll" />
+            </Component>
+            <Component Id="cmp9FB603605D751CA9FE101013C48671B7" Directory="INSTALLDIR" Guid="{D15528CB-9F26-434A-AFBC-55A1BDD4372A}">
+                <File Id="filFE4FFAF140893FFCBD7F595F88958077" KeyPath="yes" Source="SourceDir\Qt6Network.dll" />
+            </Component>
+            <Component Id="cmp62FBAA57D2AA6EAC6A5C151902D999B3" Directory="INSTALLDIR" Guid="{638E44A5-87D1-4315-8AF6-F59A52EFA987}">
+                <File Id="fil720F59CECD8953F508A84670C46B640F" KeyPath="yes" Source="SourceDir\Qt6OpenGL.dll" />
+            </Component>
+            <Component Id="cmp6F8A9D8010540EBE393F23E015D5A792" Directory="INSTALLDIR" Guid="{EBE3A0EF-3C2E-4202-B9F7-AABADBF2E45F}">
+                <File Id="fil1192A9C8E95EF194DCF3EF7BE626B48E" KeyPath="yes" Source="SourceDir\Qt6Positioning.dll" />
+            </Component>
+            <Component Id="cmpB6D50DE19D6ADD781A08727F7F6266E8" Directory="INSTALLDIR" Guid="{9CE1CD64-108E-45B7-855E-E9A783CB19E3}">
+                <File Id="filCF6DBEF1D6A13C80715404D32B934AD5" KeyPath="yes" Source="SourceDir\Qt6Qml.dll" />
+            </Component>
+            <Component Id="cmp4E9BFD75DF26062E9937847116425997" Directory="INSTALLDIR" Guid="{3BA3D48C-4E32-4447-B837-0A9DF7AE9067}">
+                <File Id="filAE1BF7A9D9BF060761C9FAAB52C1582A" KeyPath="yes" Source="SourceDir\Qt6QmlLocalStorage.dll" />
+            </Component>
+            <Component Id="cmp65C2886373B0C96AD79BAD6B11F4BCEC" Directory="INSTALLDIR" Guid="{E0C4FC0C-4A28-460A-B092-FCB45FEAA648}">
+                <File Id="fil1A464A34B69E38E90B0748094F580925" KeyPath="yes" Source="SourceDir\Qt6QmlModels.dll" />
+            </Component>
+            <Component Id="cmp609AE9C23E17BA20AC06AEA3F3551DAE" Directory="INSTALLDIR" Guid="{B92EE4BB-8805-4317-BD8D-BF4C53D6306A}">
+                <File Id="fil70941CA86B91BA9374327AE51424DF95" KeyPath="yes" Source="SourceDir\Qt6QmlWorkerScript.dll" />
+            </Component>
+            <Component Id="cmpF560A3BA9C9935A03A5C37CBAFF8321F" Directory="INSTALLDIR" Guid="{CE2EC749-6224-409F-8E35-3ED8816465C9}">
+                <File Id="fil123F08A484F5DF5CC72BFCCDC197DBF9" KeyPath="yes" Source="SourceDir\Qt6QmlXmlListModel.dll" />
+            </Component>
+            <Component Id="cmpED65C4A3E125070BC17313D1A32F9C76" Directory="INSTALLDIR" Guid="{C8F815E9-AB86-4742-8D35-7354A635233F}">
+                <File Id="filFDA17D38CD6D73072EBAF9E430B8DCD4" KeyPath="yes" Source="SourceDir\Qt6Quick.dll" />
+            </Component>
+            <Component Id="cmpA463F9E3D9E20729888D635B58292550" Directory="INSTALLDIR" Guid="{2AD5E6DF-195C-432C-A40D-19196C3E57D2}">
+                <File Id="filDF57D75497458D7E64C07BB9AD1456C9" KeyPath="yes" Source="SourceDir\Qt6QuickControls2.dll" />
+            </Component>
+            <Component Id="cmp0571EF0C5FB8E314588A9065CF74AE35" Directory="INSTALLDIR" Guid="{6C00C0D4-3FF3-4CBC-B5CC-7D5116782A33}">
+                <File Id="filDC1C47A2ADA5BD76E86CE6CE8EC4F5B4" KeyPath="yes" Source="SourceDir\Qt6QuickControls2Impl.dll" />
+            </Component>
+            <Component Id="cmp2BB41ECBA565E0A0B4EDF020A49025F9" Directory="INSTALLDIR" Guid="{E2397D5D-10EE-49D5-B2BC-D25A5286321E}">
+                <File Id="fil57860EF8422A74A3CE88A88BAD0CABB7" KeyPath="yes" Source="SourceDir\Qt6QuickDialogs2.dll" />
+            </Component>
+            <Component Id="cmp73D256572698B6DB61D05CCD551BEF9A" Directory="INSTALLDIR" Guid="{2D33DF32-5940-4433-8273-C071C66C179E}">
+                <File Id="filC9FA5D0D485CF88CB948BE06870C3B8A" KeyPath="yes" Source="SourceDir\Qt6QuickDialogs2QuickImpl.dll" />
+            </Component>
+            <Component Id="cmpC26015BC765576311A63C5B026DB71FF" Directory="INSTALLDIR" Guid="{E0DFA170-69C3-41FB-8E1E-6F17DC027777}">
+                <File Id="fil2911EE010D604A656A176E72D572CCAC" KeyPath="yes" Source="SourceDir\Qt6QuickDialogs2Utils.dll" />
+            </Component>
+            <Component Id="cmpD246CE6E593DB826C58A42EFAB8AC176" Directory="INSTALLDIR" Guid="{55D69872-A85E-43F6-912A-BF1E493C8928}">
+                <File Id="fil89BECDEB84C26F37AC111B6D300D2835" KeyPath="yes" Source="SourceDir\Qt6QuickLayouts.dll" />
+            </Component>
+            <Component Id="cmp05C682DFA26731F5F96864E240BB9367" Directory="INSTALLDIR" Guid="{9D8C8DAF-F52F-4FC3-A765-94117661FF1F}">
+                <File Id="filF00A542F2EB321619F0825C65334A7F5" KeyPath="yes" Source="SourceDir\Qt6QuickParticles.dll" />
+            </Component>
+            <Component Id="cmpB2448C4481D00046098304AC1C230B93" Directory="INSTALLDIR" Guid="{78810566-5226-4058-AFB5-478189D94251}">
+                <File Id="fil359D410486F61CCCDFEFE2410735AC75" KeyPath="yes" Source="SourceDir\Qt6QuickShapes.dll" />
+            </Component>
+            <Component Id="cmpD3B7B317B2EAD7974F7386A4F4137733" Directory="INSTALLDIR" Guid="{29B8D38E-CA38-4E5F-A2FF-3D39E7DCC3C0}">
+                <File Id="filA1B7D830C14FF020BDC9FAF8F83402A1" KeyPath="yes" Source="SourceDir\Qt6QuickTemplates2.dll" />
+            </Component>
+            <Component Id="cmpDAA5AE12CA74181FE29953002CC830A3" Directory="INSTALLDIR" Guid="{2C9383C2-EBF6-4FFC-B3F5-A014EACC05C7}">
+                <File Id="filEF75955C67A60E62CD86B26117C750AE" KeyPath="yes" Source="SourceDir\Qt6Sql.dll" />
+            </Component>
+            <Component Id="cmp2BFDE38E15CC8879E8B911A7A296477A" Directory="INSTALLDIR" Guid="{5B1DDDD7-72B7-4804-B939-BD805721BF9E}">
+                <File Id="fil08B70D0B09545B2C830255009EF93341" KeyPath="yes" Source="SourceDir\Qt6Svg.dll" />
+            </Component>
+            <Component Id="cmp501A53153FAB3908951DADAE039AB865" Directory="INSTALLDIR" Guid="{F754EC1C-C49A-4BB5-B1F6-44F39D697964}">
+                <File Id="filDAAFC08AB32ED0F61D6C56EA5B665627" KeyPath="yes" Source="SourceDir\Qt6WebChannel.dll" />
+            </Component>
+            <Component Id="cmp5046A1A989E72DACE21F8D481D0D71C5" Directory="INSTALLDIR" Guid="{63DC7AE7-1E3D-403D-9172-777C8F2185B9}">
+                <File Id="fil4DF01BF67F8DD9C408204A2F5BD56FF9" KeyPath="yes" Source="SourceDir\Qt6WebEngineCore.dll" />
+            </Component>
+            <Component Id="cmp8015426E62DE3ED33343DB50BE0A9A0D" Directory="INSTALLDIR" Guid="{F4778D74-570E-4173-912A-CEECCC885B65}">
+                <File Id="fil58988C3D49189FE2287F68174F6944A2" KeyPath="yes" Source="SourceDir\Qt6WebEngineQuick.dll" />
+            </Component>
+            <Component Id="cmp268C15DC40B82F983C534C9C3DAABD1B" Directory="INSTALLDIR" Guid="{1B8AC40A-AC0C-41C5-B5A8-D7C52FF8C206}">
+                <File Id="filA069DC2EC3525A8E547CF9EFC89A25E6" KeyPath="yes" Source="SourceDir\Qt6WebEngineQuickDelegatesQml.dll" />
+            </Component>
+            <Component Id="cmp7B3BE582AD25B07FE55DD249E6D5C333" Directory="INSTALLDIR" Guid="{2D8AA939-E8CB-4EFE-8C38-2D8D6BF0E65C}">
+                <File Id="filF8FC62A4978258AB67C456CC256AAA8B" KeyPath="yes" Source="SourceDir\Qt6WebSockets.dll" />
+            </Component>
+            <Component Id="cmpA72EB9BC77F4E13CA8F69B2B9413199C" Directory="INSTALLDIR" Guid="{2229E93A-8BC6-41F5-BE8E-1C23B56695B9}">
+                <File Id="fil84A612B35A3B234B8729B7998FD0742D" KeyPath="yes" Source="SourceDir\Qt6WebView.dll" />
+            </Component>
+            <Component Id="cmp9CC20E8457A681268437C2AC7D562FE2" Directory="INSTALLDIR" Guid="{5FA68071-6C16-43EC-9531-47C130EDB186}">
+                <File Id="filA8C33CBAC2EC4965ED511DF205E7961F" KeyPath="yes" Source="SourceDir\Qt6WebViewQuick.dll" />
+            </Component>
+            <Component Id="cmpBCCA6FD4EC03D708E3811B7D4723801F" Directory="INSTALLDIR" Guid="{7DF3B16D-52F5-46BB-A9AB-8756ED2C7014}">
+                <File Id="filAC348090A125BCB41DB84097A13158A0" KeyPath="yes" Source="SourceDir\Qt6Widgets.dll" />
+            </Component>
+            <Component Id="cmp648D7B3CDDA6427D7237CE400D68C36A" Directory="INSTALLDIR" Guid="{D6E813F8-FD27-48E7-BB04-6A065B38DC96}">
+                <File Id="fil71DF3511D06EBF7BFB8342792898BEE4" KeyPath="yes" Source="SourceDir\QtWebEngineProcess.exe" />
+            </Component>
+            <Component Id="cmpE3D572FB13B4249EA0FB18BD0F44DE18" Directory="dirB10C059F651528B59E1FD268EB08C6D6" Guid="{B7820FB2-5FBA-4761-B35B-113EB413AB63}">
+                <File Id="fil7320C9B04E87091E801ED591E25C7F1E" KeyPath="yes" Source="SourceDir\iconengines\qsvgicon.dll" />
+            </Component>
+            <Component Id="cmp5D66BEFD65BDC9AC7D48118D254EBF30" Directory="dir20189C824D4DDABC3D9FF9449C089CE0" Guid="{82C49CF0-2BCB-41B3-9F9B-8434E59F866C}">
+                <File Id="fil167B858704F9A353B556ECC7639B590E" KeyPath="yes" Source="SourceDir\imageformats\qgif.dll" />
+            </Component>
+            <Component Id="cmp2A9FEB410F41B453B0CA26766D4AD20C" Directory="dir20189C824D4DDABC3D9FF9449C089CE0" Guid="{92936E1E-6A17-4B46-8427-423FDFF2FA6E}">
+                <File Id="filB7BCDEF234107021D6B84547174B24BC" KeyPath="yes" Source="SourceDir\imageformats\qico.dll" />
+            </Component>
+            <Component Id="cmp71D8FCDDEED93F03008649285B372ADA" Directory="dir20189C824D4DDABC3D9FF9449C089CE0" Guid="{9F77CC5D-33B3-4BA5-9BBF-F212CB051C56}">
+                <File Id="fil22403FED9C4013C9328A99701DEA8873" KeyPath="yes" Source="SourceDir\imageformats\qjpeg.dll" />
+            </Component>
+            <Component Id="cmpC07A85EAE90864EB28FD65896AE228D2" Directory="dir20189C824D4DDABC3D9FF9449C089CE0" Guid="{F49D9D5C-5294-42E7-8B0E-EDB8DB957741}">
+                <File Id="fil310D7F5AA577821D98D425950E96E344" KeyPath="yes" Source="SourceDir\imageformats\qsvg.dll" />
+            </Component>
+            <Component Id="cmpB46D0962D2340404F851F10CAAECA549" Directory="dir690B8E6D609B0DB17DB71E8B1FB2FEC5" Guid="{3477D6DD-3CEA-4AC0-A526-0546C5A9971F}">
+                <File Id="filFCC7617B3CC611B027C8B3FB6DE6EA20" KeyPath="yes" Source="SourceDir\LICENSES\GPL-3.0.txt" />
+            </Component>
+            <Component Id="cmp65AA9556A3F4786F01B316E5DEA9E6F6" Directory="dir690B8E6D609B0DB17DB71E8B1FB2FEC5" Guid="{F11EAAA8-623C-4DA6-9C2A-D84964A47BE0}">
+                <File Id="filC36144D44D8E4AE0EFCD137DD2752E9D" KeyPath="yes" Source="SourceDir\LICENSES\LGPL-3.0-only.txt" />
+            </Component>
+            <Component Id="cmp727893E2EF55BD50F18FDD0ECF2C824F" Directory="dir690B8E6D609B0DB17DB71E8B1FB2FEC5" Guid="{A4D1E23C-ECA1-46D5-B695-300B878F379B}">
+                <File Id="fil52FC0319C49E6B01FF651770DA57735F" KeyPath="yes" Source="SourceDir\LICENSES\MIT.txt" />
+            </Component>
+            <Component Id="cmp907AF1F6FA03E2DE91C9FEA025010D55" Directory="dir4C08960BD61EEE75AE4A87FC65A5DE2A" Guid="{71C450B7-F43F-4E17-8694-23705AC67046}">
+                <File Id="filD108AA20F08DED7C4D3797E896B5D090" KeyPath="yes" Source="SourceDir\networkinformation\qnetworklistmanager.dll" />
+            </Component>
+            <Component Id="cmp830C378ADEFF439CEF6C0F2550FD4D2F" Directory="dir826692792B0995C8DF68919F5C684331" Guid="{20794704-18E0-49BE-BBD5-15EB1ED6249E}">
+                <File Id="filF7B01E0AFAD9057CB024B85137A38049" KeyPath="yes" Source="SourceDir\platforms\qwindows.dll" />
+            </Component>
+            <Component Id="cmp7C2CB506AB0D7E9648A5F2A7C2C7770A" Directory="dirC15EA267FAD4FC447E84FB3A32CBDB68" Guid="{7A098C8A-30B2-4DAB-AD6E-CD4BE5DADA72}">
+                <File Id="filC58409E068EE2AD6FE8C6C57174D6DDD" KeyPath="yes" Source="SourceDir\position\qtposition_positionpoll.dll" />
+            </Component>
+            <Component Id="cmp8539D5D6C3427F1F4C82034569A0F132" Directory="dirC15EA267FAD4FC447E84FB3A32CBDB68" Guid="{43FEA413-3848-49B8-848E-15455D1C4803}">
+                <File Id="filEF2EB40CA1790B7BDC4320E6F7638744" KeyPath="yes" Source="SourceDir\position\qtposition_winrt.dll" />
+            </Component>
+            <Component Id="cmp3C72ADF7447C02CBC442F4D498F97D6E" Directory="dir9331125B8BF1F459D0AC9F91A6A2F779" Guid="{33B44E03-270E-41E8-88E5-3FCA09403760}">
+                <File Id="fil1468B4258ED9D02B8E682200C411FD95" KeyPath="yes" Source="SourceDir\qmltooling\qmldbg_debugger.dll" />
+            </Component>
+            <Component Id="cmpD130E0EDF6AF950EA6D6A53A1751AA96" Directory="dir9331125B8BF1F459D0AC9F91A6A2F779" Guid="{059E0231-292B-41FE-8958-F7CCCD71482A}">
+                <File Id="filF63AD8464B4C5A27DCC098B1D1D42511" KeyPath="yes" Source="SourceDir\qmltooling\qmldbg_inspector.dll" />
+            </Component>
+            <Component Id="cmp5BDACEF05055E085579E7B097E890197" Directory="dir9331125B8BF1F459D0AC9F91A6A2F779" Guid="{2543FBAD-8F1E-4EAE-871E-760FA6E1EA64}">
+                <File Id="filB504D3FAAE942E0495C3640CFD5B377B" KeyPath="yes" Source="SourceDir\qmltooling\qmldbg_local.dll" />
+            </Component>
+            <Component Id="cmpBC2D56955BEA8DFE7E62B048E7F30EE8" Directory="dir9331125B8BF1F459D0AC9F91A6A2F779" Guid="{897C818C-DDAF-4A86-A7C8-4E999167C954}">
+                <File Id="fil26713E98F405141391C6BEA20282D73E" KeyPath="yes" Source="SourceDir\qmltooling\qmldbg_messages.dll" />
+            </Component>
+            <Component Id="cmpC9297AB1335961FB731B05361407FD09" Directory="dir9331125B8BF1F459D0AC9F91A6A2F779" Guid="{7D3D2565-3902-43C5-B03E-67E14EE8AE24}">
+                <File Id="fil7BAF56034C328F11684D5D91E5582BA6" KeyPath="yes" Source="SourceDir\qmltooling\qmldbg_native.dll" />
+            </Component>
+            <Component Id="cmp19E5AFEAFD4EE414B80BF9256489A85F" Directory="dir9331125B8BF1F459D0AC9F91A6A2F779" Guid="{C070071F-2298-4606-A25B-6A5D7829755C}">
+                <File Id="fil715013DA57F3A007F8DF4E53099D3C7A" KeyPath="yes" Source="SourceDir\qmltooling\qmldbg_nativedebugger.dll" />
+            </Component>
+            <Component Id="cmp57FF2A44B7244B35C17BB63FE8A1D97F" Directory="dir9331125B8BF1F459D0AC9F91A6A2F779" Guid="{305D972F-6917-4C3B-8B03-31953D49B965}">
+                <File Id="fil75DECF1849BEFF9DAC535DA290EA2440" KeyPath="yes" Source="SourceDir\qmltooling\qmldbg_preview.dll" />
+            </Component>
+            <Component Id="cmp7F8E313E83EEF3F0A5320389A0D409F0" Directory="dir9331125B8BF1F459D0AC9F91A6A2F779" Guid="{8C98FD2F-DFD0-4050-BB49-63CDF19E08C3}">
+                <File Id="filD2DF020A7341138A3174AA9926C94254" KeyPath="yes" Source="SourceDir\qmltooling\qmldbg_profiler.dll" />
+            </Component>
+            <Component Id="cmpA1FA5ACBABE6ABAF59E8A15E771A1020" Directory="dir9331125B8BF1F459D0AC9F91A6A2F779" Guid="{5CF27846-29A9-459F-B2D6-970AA89267F7}">
+                <File Id="fil2F21F43E56AEBBDE0C3002B3565087E0" KeyPath="yes" Source="SourceDir\qmltooling\qmldbg_quickprofiler.dll" />
+            </Component>
+            <Component Id="cmp3EA7E1216A1E529D7944144D42506A2C" Directory="dir9331125B8BF1F459D0AC9F91A6A2F779" Guid="{A41413ED-7A41-484E-B0DD-919DF20A5EF4}">
+                <File Id="filFABEED80D5D5652BC0BA2F689F6592E0" KeyPath="yes" Source="SourceDir\qmltooling\qmldbg_server.dll" />
+            </Component>
+            <Component Id="cmpCBA063886C021F79C883C543705AD5ED" Directory="dir9331125B8BF1F459D0AC9F91A6A2F779" Guid="{72954704-28D3-4571-8DA4-967AA7987E4B}">
+                <File Id="fil0839ED3FA6DF360E9FE61C24F7241731" KeyPath="yes" Source="SourceDir\qmltooling\qmldbg_tcp.dll" />
+            </Component>
+            <Component Id="cmp945254526E795AC11DD727FA63082A1E" Directory="dir4EBCE940C66AD416A8F8D45D795DED89" Guid="{1144212D-F2A0-42D3-8CAE-A9B59700A5DB}">
+                <File Id="filD59D8AFA0CDE0F773FE8451EA0C4A4AF" KeyPath="yes" Source="SourceDir\Qt5Compat\GraphicalEffects\BrightnessContrast.qml" />
+            </Component>
+            <Component Id="cmpFA306336B80423FCC8C0962DF02FC26D" Directory="dir4EBCE940C66AD416A8F8D45D795DED89" Guid="{BDF62729-9322-4266-A864-CE3A318F7E50}">
+                <File Id="fil91FA03ACC514A8F70E33CD950EBC31A8" KeyPath="yes" Source="SourceDir\Qt5Compat\GraphicalEffects\Colorize.qml" />
+            </Component>
+            <Component Id="cmp482FD31DA01FAC7A356FBCE35BDFDB4F" Directory="dir4EBCE940C66AD416A8F8D45D795DED89" Guid="{2EF7ACCE-9E47-4963-AE0C-57BFAC218BB9}">
+                <File Id="fil54CEE194AAB2B1FA1C981523D3A41937" KeyPath="yes" Source="SourceDir\Qt5Compat\GraphicalEffects\ColorOverlay.qml" />
+            </Component>
+            <Component Id="cmpF6043AA78938689C64CEE897318DA32F" Directory="dir4EBCE940C66AD416A8F8D45D795DED89" Guid="{D348E4E5-C51D-49D5-A5C2-B4583FE10D28}">
+                <File Id="filC8833D6659696BA7801476772756C502" KeyPath="yes" Source="SourceDir\Qt5Compat\GraphicalEffects\ConicalGradient.qml" />
+            </Component>
+            <Component Id="cmp24C8C5A5AC88293EE054C9A86C9149CB" Directory="dir4EBCE940C66AD416A8F8D45D795DED89" Guid="{F1A2D020-56B8-4B8A-9347-CFBF9D9028E1}">
+                <File Id="fil555C55E2C7BBD35C2B95C89388217E64" KeyPath="yes" Source="SourceDir\Qt5Compat\GraphicalEffects\Desaturate.qml" />
+            </Component>
+            <Component Id="cmpE79F8C6AF8BF9B976459932AF82C6E4B" Directory="dir4EBCE940C66AD416A8F8D45D795DED89" Guid="{5A362CB3-8CB6-42FD-B161-BC081588D148}">
+                <File Id="filD15780CD824A15AADD69C7C4A8BEBF0B" KeyPath="yes" Source="SourceDir\Qt5Compat\GraphicalEffects\Displace.qml" />
+            </Component>
+            <Component Id="cmp05FC0A312BDE8E70E741C4C90C622639" Directory="dir4EBCE940C66AD416A8F8D45D795DED89" Guid="{230EFF92-9DDA-47FE-9AA5-AAC3F122C503}">
+                <File Id="fil8B4BD8109771AFD1469381DBAA7CA77F" KeyPath="yes" Source="SourceDir\Qt5Compat\GraphicalEffects\DropShadow.qml" />
+            </Component>
+            <Component Id="cmpC85EEEC797E1E24770CC8316B9F4A7BB" Directory="dir4EBCE940C66AD416A8F8D45D795DED89" Guid="{B12A748E-CD45-4E71-BD6B-E74ADA7B939E}">
+                <File Id="fil9D6AE9AF67863D8BD7CD68B27480E91E" KeyPath="yes" Source="SourceDir\Qt5Compat\GraphicalEffects\FastBlur.qml" />
+            </Component>
+            <Component Id="cmpB65602DB13223293DF84A1F03AB696C2" Directory="dir4EBCE940C66AD416A8F8D45D795DED89" Guid="{2F15B3FD-7A4C-40F3-ABDC-03671AA709FF}">
+                <File Id="filB06BFD9D6F49C51DC6EB0EBF39AC7207" KeyPath="yes" Source="SourceDir\Qt5Compat\GraphicalEffects\GammaAdjust.qml" />
+            </Component>
+            <Component Id="cmp529A90FD8AD0F278DAC442F8AECA9A91" Directory="dir4EBCE940C66AD416A8F8D45D795DED89" Guid="{C917F1D8-44B6-44DD-BDB7-E86752289646}">
+                <File Id="fil776C641CD1029493F23FAC8C55C2A9ED" KeyPath="yes" Source="SourceDir\Qt5Compat\GraphicalEffects\Glow.qml" />
+            </Component>
+            <Component Id="cmpDEAA0EFE8F05663D56AFC9A4D642B920" Directory="dir4EBCE940C66AD416A8F8D45D795DED89" Guid="{D7E8FC7C-6203-475A-8D63-2AB49D1DF3DA}">
+                <File Id="fil60D6E98973B7B2F15997CBA972190713" KeyPath="yes" Source="SourceDir\Qt5Compat\GraphicalEffects\HueSaturation.qml" />
+            </Component>
+            <Component Id="cmp42C7CA719F60AFEBC2F0F8264E0D5DCC" Directory="dir4EBCE940C66AD416A8F8D45D795DED89" Guid="{6F9533E3-6DF4-4ABA-B816-3DF64014F359}">
+                <File Id="fil6747E895BD0148F7E016D09BBE90C467" KeyPath="yes" Source="SourceDir\Qt5Compat\GraphicalEffects\LevelAdjust.qml" />
+            </Component>
+            <Component Id="cmp17F26F9C315A657D76B33BE901D83C5E" Directory="dir4EBCE940C66AD416A8F8D45D795DED89" Guid="{6BFAE503-E8EE-40DA-B0ED-B84E7EAF35C8}">
+                <File Id="fil00C6E7DACEFFB748A88395184149B93C" KeyPath="yes" Source="SourceDir\Qt5Compat\GraphicalEffects\LinearGradient.qml" />
+            </Component>
+            <Component Id="cmp9B01EF285CAFA2B4BA31C7FDCA0F922B" Directory="dir4EBCE940C66AD416A8F8D45D795DED89" Guid="{7BA5BDFA-2258-4F43-8895-D8AB15662CC4}">
+                <File Id="fil44A00B41A29C23382B450B339EA3DD28" KeyPath="yes" Source="SourceDir\Qt5Compat\GraphicalEffects\OpacityMask.qml" />
+            </Component>
+            <Component Id="cmp1163E46DF6F9C565A6CC1A9527814DD7" Directory="dir4EBCE940C66AD416A8F8D45D795DED89" Guid="{EB981BC1-1694-45C8-B943-F41E172CB38B}">
+                <File Id="fil48F932878AB695E8E49099B989416D53" KeyPath="yes" Source="SourceDir\Qt5Compat\GraphicalEffects\plugins.qmltypes" />
+            </Component>
+            <Component Id="cmpFADBFC6EAF30F0EF2C8B5E0E45A1E213" Directory="dir4EBCE940C66AD416A8F8D45D795DED89" Guid="{AC38DF2B-7E99-4EA4-B6B3-7926B4AEAF9B}">
+                <File Id="fil91587697FF52370F7056F904CE90D781" KeyPath="yes" Source="SourceDir\Qt5Compat\GraphicalEffects\qmldir" />
+            </Component>
+            <Component Id="cmp229B96EC474CAA25D38BED9C228783F3" Directory="dir4EBCE940C66AD416A8F8D45D795DED89" Guid="{4D75B80D-1494-428A-A146-5E97B67D9866}">
+                <File Id="filB26147ACAD14F15A286B32B7168E0A7E" KeyPath="yes" Source="SourceDir\Qt5Compat\GraphicalEffects\qtgraphicaleffectsplugin.dll" />
+            </Component>
+            <Component Id="cmpC93BCD1EAE827CC0DDD4D564F7E221DB" Directory="dir4EBCE940C66AD416A8F8D45D795DED89" Guid="{2A89D5E5-A2E8-483D-A1EA-85A56690CE2D}">
+                <File Id="fil6171FF7F9337ED0B77B1C9D481E039D7" KeyPath="yes" Source="SourceDir\Qt5Compat\GraphicalEffects\RadialGradient.qml" />
+            </Component>
+            <Component Id="cmp457D3F95070575E819AB610C1ABC0B4F" Directory="dir4EBCE940C66AD416A8F8D45D795DED89" Guid="{28FC6065-1EA3-478B-9787-FE69A246098F}">
+                <File Id="fil1406D8C64F563242EE22DC5AC4C3DF4F" KeyPath="yes" Source="SourceDir\Qt5Compat\GraphicalEffects\RectangularGlow.qml" />
+            </Component>
+            <Component Id="cmpB5F5961D68AC44BC3F4D7F8E09C72DB5" Directory="dir4EBCE940C66AD416A8F8D45D795DED89" Guid="{F7D10354-0904-497F-98B7-773F2656D8CE}">
+                <File Id="filBE5042CC2EDD6FB009F28089BCF6A2CE" KeyPath="yes" Source="SourceDir\Qt5Compat\GraphicalEffects\ThresholdMask.qml" />
+            </Component>
+            <Component Id="cmp2A4B284F625466C5F8FC12832EE7690A" Directory="dir24D778EAF9E4AD78E4448B53856DC5AB" Guid="{177C274A-4507-40E4-BDBD-C5B8F7BF1DEF}">
+                <File Id="filE6CD47174B801A0004C0DA94D744C83E" KeyPath="yes" Source="SourceDir\Qt5Compat\GraphicalEffects\private\FastGlow.qml" />
+            </Component>
+            <Component Id="cmpEF7DCF95664BF5E27244737858CCA436" Directory="dir24D778EAF9E4AD78E4448B53856DC5AB" Guid="{924B9D4B-1F16-4340-AA21-3F8B380A7D74}">
+                <File Id="fil97EC56ED8D0A2C1484FF186420628094" KeyPath="yes" Source="SourceDir\Qt5Compat\GraphicalEffects\private\qmldir" />
+            </Component>
+            <Component Id="cmpA77342C1842BFCA8360F66ECB1AA754A" Directory="dir24D778EAF9E4AD78E4448B53856DC5AB" Guid="{AA95A5EF-3EED-400B-9E9F-C4C1B8F0B24E}">
+                <File Id="fil335AC65D8F3D6494F03FE525ADC6C8C4" KeyPath="yes" Source="SourceDir\Qt5Compat\GraphicalEffects\private\qtgraphicaleffectsprivateplugin.dll" />
+            </Component>
+            <Component Id="cmpC295964BD8DF71C916C2FD3D4D11C6CA" Directory="dir70AFC0A9D84A710EC0128D3FED451CDC" Guid="{382A8E24-DC27-4CDC-95DE-BFDAF310431B}">
+                <File Id="filF951B868BF244A85F29053232BCCA952" KeyPath="yes" Source="SourceDir\QtQml\plugins.qmltypes" />
+            </Component>
+            <Component Id="cmp5561966E8256C014F16E7FE4EA658097" Directory="dir70AFC0A9D84A710EC0128D3FED451CDC" Guid="{E16D7B59-8E56-4AE6-8DAC-4C734DFB0D3D}">
+                <File Id="filFBC347544E9C5EA7F56ABE34B2A84784" KeyPath="yes" Source="SourceDir\QtQml\qmldir" />
+            </Component>
+            <Component Id="cmp6CB39FB81228DE9B93125C008D231598" Directory="dir70AFC0A9D84A710EC0128D3FED451CDC" Guid="{2D36EAC4-1CFA-4A5A-8AAC-757E79AE2477}">
+                <File Id="filF0802A3CC352307D805625AA768049DB" KeyPath="yes" Source="SourceDir\QtQml\qmlplugin.dll" />
+            </Component>
+            <Component Id="cmp389F3B9DCF84CB30BEBE7F2C0099AC0B" Directory="dir49DBFE81A3C2546F0A0D7B91984B25A9" Guid="{FCF419CF-E046-412A-9A1C-3455FF73384B}">
+                <File Id="fil8C1068D9ED2B20D022CC9C9383812A1B" KeyPath="yes" Source="SourceDir\QtQml\Models\modelsplugin.dll" />
+            </Component>
+            <Component Id="cmp94CDC11A039D0103B4D7199420F7FC50" Directory="dir49DBFE81A3C2546F0A0D7B91984B25A9" Guid="{D18B4ED3-8A33-4FEF-BF59-4717FCDFB753}">
+                <File Id="filF8AEBF4DCF62371C28A61789BC9B60C9" KeyPath="yes" Source="SourceDir\QtQml\Models\plugins.qmltypes" />
+            </Component>
+            <Component Id="cmp598CA1BC124E50AC062FAD3723570CA8" Directory="dir49DBFE81A3C2546F0A0D7B91984B25A9" Guid="{CD6875BD-36D6-44BE-A04A-96EFA0510BCB}">
+                <File Id="filC10013F340A191C09D677E614248B05B" KeyPath="yes" Source="SourceDir\QtQml\Models\qmldir" />
+            </Component>
+            <Component Id="cmp70D1A478617931123B81F6D2D2994212" Directory="dir040BCEF4BA1A47FD347F1E983F275641" Guid="{4C86F328-1113-4CA1-9662-0784DE3CB086}">
+                <File Id="fil4B45AC3C5F767355995043201ED724EF" KeyPath="yes" Source="SourceDir\QtQml\WorkerScript\plugins.qmltypes" />
+            </Component>
+            <Component Id="cmpA199263755A832BCAA8FD26E7875C730" Directory="dir040BCEF4BA1A47FD347F1E983F275641" Guid="{E74A3C35-81AD-4ABF-9DD4-60CBE180BC36}">
+                <File Id="fil8643DA009D020149E2D8F76BD4D0A3BD" KeyPath="yes" Source="SourceDir\QtQml\WorkerScript\qmldir" />
+            </Component>
+            <Component Id="cmpC5535C974DD300AF1B3368991C3F9998" Directory="dir040BCEF4BA1A47FD347F1E983F275641" Guid="{75886F50-68C0-45C1-A6EC-F4C001D60010}">
+                <File Id="fil5FA064C3A03EBF4827F2FF689E6FF52A" KeyPath="yes" Source="SourceDir\QtQml\WorkerScript\workerscriptplugin.dll" />
+            </Component>
+            <Component Id="cmpCD8F9C674B06B07EDDDB260AF148377F" Directory="dirDF3BB8A52CE36475D7FA56568CBEA7A4" Guid="{31311101-D328-4D94-8EA6-FD2EB14FBA12}">
+                <File Id="fil3B585A05F1CF7AE5DAA29FC56086FE84" KeyPath="yes" Source="SourceDir\QtQml\XmlListModel\plugins.qmltypes" />
+            </Component>
+            <Component Id="cmp5F2CBC62909D55884F306D0EAA36BE6A" Directory="dirDF3BB8A52CE36475D7FA56568CBEA7A4" Guid="{C20B56DF-DD49-4341-A373-3702D3EA5A6A}">
+                <File Id="filC0277244B0C30937CD5F818F183F92AB" KeyPath="yes" Source="SourceDir\QtQml\XmlListModel\qmldir" />
+            </Component>
+            <Component Id="cmpBAA1AF87AC60BB0B0EDF8484233CA948" Directory="dirDF3BB8A52CE36475D7FA56568CBEA7A4" Guid="{71A3ED9F-5D65-4F7F-B860-BBC952FF234A}">
+                <File Id="fil1875712CD39FA24B2A06C0F244E29789" KeyPath="yes" Source="SourceDir\QtQml\XmlListModel\qmlxmllistmodelplugin.dll" />
+            </Component>
+            <Component Id="cmp58020242039257006BB9832E84C98670" Directory="dir9D005E7F3893047E4557D6C5273ADD9E" Guid="{4FC76C5A-E345-4DF0-BE88-F61FBD06F46C}">
+                <File Id="fil3714BF5922F4FA9E95484B97BCFD40A6" KeyPath="yes" Source="SourceDir\QtQuick\plugins.qmltypes" />
+            </Component>
+            <Component Id="cmpE1A9F2278C9A7DAA74FE5906A844F954" Directory="dir9D005E7F3893047E4557D6C5273ADD9E" Guid="{72EF2BD4-7051-4910-BA0C-7C3FD041B983}">
+                <File Id="fil356BD72CA2CACF4DB94370FFCA1E4E2F" KeyPath="yes" Source="SourceDir\QtQuick\qmldir" />
+            </Component>
+            <Component Id="cmpAB665C492211D9E739C618628990AFD5" Directory="dir9D005E7F3893047E4557D6C5273ADD9E" Guid="{AEE6DD8C-BEC6-43CF-A241-3E5E4AE40E04}">
+                <File Id="filCE359623B7F5482511466CB0C2BC02B2" KeyPath="yes" Source="SourceDir\QtQuick\qtquick2plugin.dll" />
+            </Component>
+            <Component Id="cmp498B55DCBFF36960DCA912CEE847D696" Directory="dirDCD6F45EC68D7ABC2968E38BA6C2E3B4" Guid="{7DC0C26F-88DA-4E9D-A29D-21A07E866DBA}">
+                <File Id="fil23E5D4EB38F16653E2987545CEF878EC" KeyPath="yes" Source="SourceDir\QtQuick\Controls\plugins.qmltypes" />
+            </Component>
+            <Component Id="cmp562DB404A131AD7C3086FB3CD819E814" Directory="dirDCD6F45EC68D7ABC2968E38BA6C2E3B4" Guid="{9B90498A-7D36-4CB5-8505-084EE4F227A3}">
+                <File Id="fil65480B31AECCDDD6B12C33E8A26E3AEF" KeyPath="yes" Source="SourceDir\QtQuick\Controls\qmldir" />
+            </Component>
+            <Component Id="cmp1D9FD82E27172D3A6FA9FEA184DF2E3E" Directory="dirDCD6F45EC68D7ABC2968E38BA6C2E3B4" Guid="{08ABF963-E6C4-4546-AA9D-EA0DCA9BFD9B}">
+                <File Id="filFDC51E1BC2B0F884DA7F5CE0B5F0DDB4" KeyPath="yes" Source="SourceDir\QtQuick\Controls\qtquickcontrols2plugin.dll" />
+            </Component>
+            <Component Id="cmp39E3BF441C84CD2E294D8E7F57365C93" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="{2065B1C6-727B-47F2-A79F-E1E57C1E37E3}">
+                <File Id="filE0405A96CB6039A810D7076771307605" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\AbstractButton.qml" />
+            </Component>
+            <Component Id="cmp13F47DE57D0E880784A1ACAFD6FFE678" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="{448F8CFD-991E-493E-A197-59FFFDEF4934}">
+                <File Id="filE5C7E166D6D67CDF9A4BB87C0709E983" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\Action.qml" />
+            </Component>
+            <Component Id="cmpAF1A8723D2F99700E1A767A114FD58E4" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="{463CBE74-3F8B-4A48-86ED-9601A17F7BBB}">
+                <File Id="filD615F4D9A187866255668509CDDBA0C0" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\ActionGroup.qml" />
+            </Component>
+            <Component Id="cmp7D1CE48AFA1D68D8112E46EC712B1A2A" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="{0F843DDE-7D1A-4D15-8468-F21AF8B19BFB}">
+                <File Id="filC133DCD99E549792CC136B39782E9970" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\ApplicationWindow.qml" />
+            </Component>
+            <Component Id="cmp3226F3C6C4139EFF5FF083C50454526E" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="{B945DE5F-76DD-4653-8C4B-036C97CE06D2}">
+                <File Id="fil3014C4EBA2ADFECBD067E505ED839770" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\BusyIndicator.qml" />
+            </Component>
+            <Component Id="cmpAFF2E0688F9EEFE57DC3D595B7480AC4" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="{C4BE06E6-B11E-4A5F-BB17-224D368EB682}">
+                <File Id="filDABA4C7F42E824B3BD5B6A75C2683B98" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\Button.qml" />
+            </Component>
+            <Component Id="cmpFEBC544096B496ED497C50CEC586F991" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="{B3A315F8-0F74-4168-9066-378970A8D077}">
+                <File Id="filD0BCB194277908B7652753F9708A9CAC" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\ButtonGroup.qml" />
+            </Component>
+            <Component Id="cmp460799C7403500F008F382231E93D92C" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="{718E0351-3143-4A55-A266-B6CF63D54953}">
+                <File Id="fil8334FFE9C02650DEC329168E3D23CD86" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\CheckBox.qml" />
+            </Component>
+            <Component Id="cmpC97CE0B6EC4977EE6BFA9695E31E1E8F" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="{6AAC73F3-6711-40CB-9146-E7D5D274D1B3}">
+                <File Id="fil0613DD6744734519C77B1AD39A738DA9" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\CheckDelegate.qml" />
+            </Component>
+            <Component Id="cmp1EEE42D5F042D85B2ED628CC367864CC" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="{81FF8593-8628-44B6-AEAD-EF69E4345EAE}">
+                <File Id="fil6772157AD78D3BFDD3D4CF9F2B1FC8A9" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\ComboBox.qml" />
+            </Component>
+            <Component Id="cmpB7161814C193537B0678C47FCCEE0906" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="{4DB10A13-F1A1-4214-91D5-6ADE763C82AF}">
+                <File Id="fil1518A26590DFC2C5E545B7BD6789151B" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\Container.qml" />
+            </Component>
+            <Component Id="cmp444DFB6BF11F2A5524079C8B42790CED" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="{C3B3FB34-AA50-4012-B85C-C02C723E43BB}">
+                <File Id="filAF046240483A1147D2F772820458E4B5" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\Control.qml" />
+            </Component>
+            <Component Id="cmp93071B0BE396A0BE670EE327358C3BE5" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="{3A605529-93A0-43A9-8A21-CF315A44996F}">
+                <File Id="fil8370F717D3A289CB18A871E133552D67" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\DelayButton.qml" />
+            </Component>
+            <Component Id="cmpEFE249C5563CA612E610026CC13993FF" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="{8E886F0C-F586-4E90-9696-76F9B6CC9107}">
+                <File Id="filC3A23304C9A22DDA6209D1A4B465CA3E" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\Dial.qml" />
+            </Component>
+            <Component Id="cmp9AED433DBDF1B120D1C954030112107F" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="{25E4E00A-CB30-446A-9896-983D6F61FA32}">
+                <File Id="fil324D39A993BE8D40514A4FC971246D10" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\Dialog.qml" />
+            </Component>
+            <Component Id="cmp53251CA3F634BC98009E538529819399" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="{AE3BA354-39F5-4538-B33C-35F966BD7260}">
+                <File Id="fil77B1BA93A52DE992ACF37A8414873897" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\DialogButtonBox.qml" />
+            </Component>
+            <Component Id="cmpBC47B2660BE28F19D26D82EEA61AC315" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="{E945CC6B-0006-4644-8988-75573CF04756}">
+                <File Id="filE6E031847C6472BE22ADE7E12049CCC7" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\Drawer.qml" />
+            </Component>
+            <Component Id="cmpDAF2DA7B94D53738426273877D5C8EAA" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="{35A1D867-68A1-4132-8209-9999901E7F68}">
+                <File Id="fil6B983CAD6D4B7CF30314AD6141E5F008" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\Frame.qml" />
+            </Component>
+            <Component Id="cmpB8B6E0189F3A99B18BFEE9C2FBB6E7CB" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="{2951BE43-EC65-405E-921D-88BCAFD7E849}">
+                <File Id="fil82A44412EC8B48B71850C6F39776DDBA" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\GroupBox.qml" />
+            </Component>
+            <Component Id="cmp28C29EF45496D5E56CC019D1C804BDC6" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="{A4C2C2AB-9F2F-4F2E-8C34-87983158599C}">
+                <File Id="filC3D18EE9C070AF77C19B6483A78F96E7" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\HorizontalHeaderView.qml" />
+            </Component>
+            <Component Id="cmp096A72C75835E73B556FBE045151AD84" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="{C6E8B6DE-83DD-4383-900F-1B5C1B5C6FE3}">
+                <File Id="fil6927D552E8E746D2564A9093BC6F4010" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\ItemDelegate.qml" />
+            </Component>
+            <Component Id="cmpCB3B27B649765931CAFCDB9558C89CD7" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="{3054E6E7-E39D-4E99-BED4-2352E6D58E8E}">
+                <File Id="fil45E2AA74D45B3939CAAA4AE2FC5217F0" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\Label.qml" />
+            </Component>
+            <Component Id="cmpA1DFDF9DBA363A02E1296B9C7F7E028A" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="{CE2D8FCB-CCC8-4C1E-84BC-87566ADD6E27}">
+                <File Id="fil2A5852AC62177CA1F4D7C90418DF3B99" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\Menu.qml" />
+            </Component>
+            <Component Id="cmp1D7E1ACADF1DB55930E04973075327E2" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="{22B7FA70-9A1C-4267-9C04-1B41DD351170}">
+                <File Id="fil6872870244C4DA24309E3E140F1BBF10" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\MenuBar.qml" />
+            </Component>
+            <Component Id="cmp00F32E1E79E785E7900A6BE78AB73DD0" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="{98514FBF-58AB-42F1-BB3B-9D0375CEEC33}">
+                <File Id="filDFEEB007AC6245AA515CFEC1D3CC32E6" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\MenuBarItem.qml" />
+            </Component>
+            <Component Id="cmp96A0A6316688992E2EEB96041D4B1124" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="{091BB528-1367-469E-8D5F-18E64E54EC3C}">
+                <File Id="filADA6001ECBDDA7BD3196AE733EF95BC1" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\MenuItem.qml" />
+            </Component>
+            <Component Id="cmpD97C9727C2F49C32AE6E794FCA617F3B" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="{57120041-5F1C-43B2-94A7-B26D57978687}">
+                <File Id="filDE539B30D628C3A7AA24F1193F83AAC7" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\MenuSeparator.qml" />
+            </Component>
+            <Component Id="cmp3F2A3045CD47F5B92A65055853047DAD" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="{163935B2-26DC-43E0-8EEB-7532169C03E7}">
+                <File Id="fil2F33928FB0D2CE37679B6D6AE9A653CD" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\Page.qml" />
+            </Component>
+            <Component Id="cmp32620ADB561085068DBDF1E93E885BBE" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="{F0844385-3B08-47F0-AF3C-5F218753EEFE}">
+                <File Id="filF9BBCBD4094D68CBEC82EE19772E960C" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\PageIndicator.qml" />
+            </Component>
+            <Component Id="cmp176781C50AD2C199CFF82CD69AA557A6" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="{FFE3AFAD-6302-48D9-AD66-CF897C225E3D}">
+                <File Id="fil0242C2206149CFF1D785B06488D2BDDF" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\Pane.qml" />
+            </Component>
+            <Component Id="cmpB910C2A89C678D4DFF1C10127CEF1871" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="{717801AD-E7B8-4022-A668-3EC139E36142}">
+                <File Id="fil795F34AE8CAE09C1100923382BBCF57D" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\plugins.qmltypes" />
+            </Component>
+            <Component Id="cmp7C08CB1B9A52F1E86769F7046A400A85" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="{D3EE998C-8B17-470E-8F29-233D0071C5A4}">
+                <File Id="filCA23B23206C1E01F6F148AC09F04EE65" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\Popup.qml" />
+            </Component>
+            <Component Id="cmpB7604AC467FDB08CA5F897B461F249F6" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="{87C35DB1-E679-41C7-8FA4-7F84E0938E84}">
+                <File Id="fil0B7A072F8416E2DA85B235F68AFDEE1A" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\ProgressBar.qml" />
+            </Component>
+            <Component Id="cmpF64844BE891499A5EBA53BD112979B45" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="{DFDC9F95-77A9-4D30-B3C3-D091D449ADD7}">
+                <File Id="filF4BF6B3715B227D16B8010B5BF41CF54" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\qmldir" />
+            </Component>
+            <Component Id="cmp28A47D855FFEC3C59FA27B23A56F3ADA" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="{11A9B033-FD19-4CC7-9CC9-DDD86097265A}">
+                <File Id="fil516C1F96FA6E0D0445918BE9615FDEF2" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\qtquickcontrols2basicstyleplugin.dll" />
+            </Component>
+            <Component Id="cmp7E3F340C4EB0A16568754701511B9C1D" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="{FEF00A13-D29B-418D-9E92-AED80D260CF5}">
+                <File Id="fil9401262A1FBD0350983CAA7A69128EEA" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\RadioButton.qml" />
+            </Component>
+            <Component Id="cmpAE9517901AB3AD27D048A7EDE377222D" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="{6A38C19C-80E0-4254-935F-681C4E4CB004}">
+                <File Id="fil58508D4C722A6050E3A6C6AFB09F14AD" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\RadioDelegate.qml" />
+            </Component>
+            <Component Id="cmpE3157FE6CF15D4C1DD224852360A0915" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="{1D016386-C73B-42EF-8458-900CF9B1D15C}">
+                <File Id="fil0D637BC81D28F9AC37720B648A0FFE22" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\RangeSlider.qml" />
+            </Component>
+            <Component Id="cmpC24EDDD8484051DDEF1687FFF0C4FE14" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="{BA81EAA4-7C53-4701-87E4-DF6F0E593E82}">
+                <File Id="filF9825B69059C4512381CBB1ED38CE677" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\RoundButton.qml" />
+            </Component>
+            <Component Id="cmp44A277143BAED4BF0D4C12A45B62966A" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="{10F85273-5CB4-4C75-BBF6-89BFE99B9194}">
+                <File Id="fil223FE71601C3052DB16C28922C97C3CD" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\ScrollBar.qml" />
+            </Component>
+            <Component Id="cmp1A37E19BA0D3D569C2F4A720EC581A72" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="{2A58EFCE-B49E-498F-9FCF-68C12A449DCF}">
+                <File Id="fil3E500552913D6E5EFB882701A0640471" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\ScrollIndicator.qml" />
+            </Component>
+            <Component Id="cmp6B503919F5459E5A8A6ACEA8D2EB8873" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="{CCAD8611-5649-4AD0-8CA7-9904D04C3939}">
+                <File Id="filA27E268B9A524AA1B4FD47712C8BF7B2" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\ScrollView.qml" />
+            </Component>
+            <Component Id="cmpAEAB0E732A86AB87EACD7B7ABCDE202F" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="{A4954B94-260A-4AD8-8CAD-082B1E5BA2DB}">
+                <File Id="fil49FCC757A5412B4514C646558EC3AD3A" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\SelectionRectangle.qml" />
+            </Component>
+            <Component Id="cmp3A17AF10F75FC68978D74CDFC7397BD9" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="{F267DFD1-CE02-486B-B0B7-06FC017BD969}">
+                <File Id="fil1D41E82D87B97B78E7045FDE970EE5B8" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\Slider.qml" />
+            </Component>
+            <Component Id="cmp8EBF1FC1A1508143AED171ECD969A21A" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="{D70DED43-7597-41C3-A40C-F23A9D8DDEFB}">
+                <File Id="fil0286392B1E4AE12D681EC673B0FFF0DD" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\SpinBox.qml" />
+            </Component>
+            <Component Id="cmpADF688B243C6067D34B7121262F45B7D" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="{D047352C-2A94-414D-84D3-DD97BACF6B90}">
+                <File Id="fil24D6F394262FCDAEA68149CF43C63220" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\SplitView.qml" />
+            </Component>
+            <Component Id="cmp14B3F918468BAC922D69BC9C0A0BF8F4" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="{601059AD-8A5E-4052-943C-90CE3590648B}">
+                <File Id="fil37A3D716490D601495E75F36C1616D98" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\StackView.qml" />
+            </Component>
+            <Component Id="cmp6C88EEC02A082F4AB67C01AA087713B9" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="{46811764-93BC-4D57-B6D6-F5557A3CFDDD}">
+                <File Id="fil5902D6B5881EC5198DC87C4B37E6F773" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\SwipeDelegate.qml" />
+            </Component>
+            <Component Id="cmpA52455B6F63A98C4A8C1C7B7FEF6697D" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="{63438FF2-E1CC-4F5E-A954-764EFE6E9F77}">
+                <File Id="filDC4854131B09909A15B41D48D11A10FD" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\SwipeView.qml" />
+            </Component>
+            <Component Id="cmp4B0D452072AC6C23F73AD5197C1E306C" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="{DB2B681D-7320-4E37-A348-E20402B2AA67}">
+                <File Id="fil3FA59E20FEAAD096E2ACFECC65DA05E6" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\Switch.qml" />
+            </Component>
+            <Component Id="cmp94A4D7BA55C18ABFE7169AFE56222542" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="{FA9D77E3-A800-4A65-B541-35A28F34B5B0}">
+                <File Id="fil01B1917034EF7069154B5D74AAA17DEF" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\SwitchDelegate.qml" />
+            </Component>
+            <Component Id="cmp24D052E6B20BE5C50D38CECB8698C9CD" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="{3B8A10FC-1BA5-4484-862B-165F3B5CEE7C}">
+                <File Id="filB530256DFF8FD3F7BFCBA01FF22B4973" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\TabBar.qml" />
+            </Component>
+            <Component Id="cmp4869F0C47A93CBBC8D904461A59D4B51" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="{D7D6CDE3-09AA-42F3-B860-0D3A0E999354}">
+                <File Id="filEDF1726BE8620A4A227FECF9CDAA17CE" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\TabButton.qml" />
+            </Component>
+            <Component Id="cmpE7926A6DAB570EF55A188C06D59B0152" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="{B66568CF-7C2D-49F7-A53B-C041F4AE60BD}">
+                <File Id="fil2403A6F074DB07174C58C6B3560CE6A3" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\TextArea.qml" />
+            </Component>
+            <Component Id="cmp65D8852BE8B18D40EDC1D4ED531BA5F3" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="{50841396-1B35-4BBE-8913-791B5F6AE312}">
+                <File Id="fil65CACB8AF03F1560DF05FA1990AE23F2" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\TextField.qml" />
+            </Component>
+            <Component Id="cmpEE0678D57E918B0B86AF94882FFC00AE" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="{E1AE00ED-4AF7-4B0F-B147-30BEBB6FEC93}">
+                <File Id="filB7BB3860784F05376030AB01B762E81F" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\ToolBar.qml" />
+            </Component>
+            <Component Id="cmp5629792356E2195B748193DDF721DF76" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="{F8D0370C-6F1A-4734-8FA2-C37A0E7417E2}">
+                <File Id="fil300EE75FDB68B619C4E282A19E61F95F" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\ToolButton.qml" />
+            </Component>
+            <Component Id="cmp1CD4D9E7E53660B7F864EECB4F6F1686" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="{337A7EE3-B6E7-41DD-B63B-4FC2F241222F}">
+                <File Id="filF8B7C7B797BEA766D781CBCAE9CB2067" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\ToolSeparator.qml" />
+            </Component>
+            <Component Id="cmpF5C63A567BF77BC306496620B3B40E0B" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="{FB8B903E-361F-47D9-B490-A30654E6C79F}">
+                <File Id="fil47E23689D76FAF8447A14EBCF09B819C" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\ToolTip.qml" />
+            </Component>
+            <Component Id="cmpCCADBB1D8335A08277A040F9120BF10C" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="{AB474319-3115-450C-97A0-71F407A867C1}">
+                <File Id="fil2B65A45E3538C6946ADBF329ED1E8B8C" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\Tumbler.qml" />
+            </Component>
+            <Component Id="cmp6D6AFFD2FE505287A8B4899D905225F8" Directory="dir7849AFCA9B74D36A839FBB59A8264BFC" Guid="{7DA8E9AE-AB0F-448F-8D57-72303EA43D92}">
+                <File Id="filEB3713EBC2AA05DF3AE5C80240CA5F29" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\VerticalHeaderView.qml" />
+            </Component>
+            <Component Id="cmpD550D210E3DB52D8334D7EE916A7971B" Directory="dirDBD23FE4F11ED29A51E2F3787280CC12" Guid="{A4ED8666-4D51-4CA8-B7AA-B713A2E72B75}">
+                <File Id="fil0DA76C9C1FB7B19A9C1250D1606F4375" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\impl\plugins.qmltypes" />
+            </Component>
+            <Component Id="cmp8853A6E2FEDF5C48CE3538ECEC698070" Directory="dirDBD23FE4F11ED29A51E2F3787280CC12" Guid="{3246662E-2CE9-4CA7-8273-8480CDB32A96}">
+                <File Id="filCEE3AF66760C2A693F631DBC19C11FA9" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\impl\qmldir" />
+            </Component>
+            <Component Id="cmp2EDF0CFA2C7BEF541CE841B6E4ED8ABE" Directory="dirDBD23FE4F11ED29A51E2F3787280CC12" Guid="{3264B72F-174F-4796-945F-7C130A3AC06E}">
+                <File Id="filF8813763A2BC55C52576E4872E40E32B" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Basic\impl\qtquickcontrols2basicstyleimplplugin.dll" />
+            </Component>
+            <Component Id="cmp45B50EB84A857B8F62EF4529B3456513" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="{CD04F9CF-300C-4568-AD5A-C4E92E708FDE}">
+                <File Id="filD12AED5ADB2C9E2415789A94E4A515D2" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\ApplicationWindow.qml" />
+            </Component>
+            <Component Id="cmpF6BB6E5DECD94FBCEE2F3B3B7A31DEF4" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="{00DF4BF5-A64E-4BFD-9E28-3E4EB9050437}">
+                <File Id="fil3799FE4CE54EEE23DEA152F3356D8BF2" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\BusyIndicator.qml" />
+            </Component>
+            <Component Id="cmp6C0DC0D93562BD0BB051C3BA68D38BAB" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="{53D6DF90-908E-4199-B131-3F5395D73333}">
+                <File Id="fil7BBC045C60D7D4018D580735F4511B2D" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\Button.qml" />
+            </Component>
+            <Component Id="cmp966432BC1820394B57C176A5165A995A" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="{F1D4D877-37DE-4181-8BF8-F76F9A1D9DD7}">
+                <File Id="fil7AD480BD39EB266912C5EC7D2DF5358D" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\CheckBox.qml" />
+            </Component>
+            <Component Id="cmp8BE9AC25130F427CDEC3A445EBBE3B17" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="{BECF3DB2-D232-454D-AEAA-EE8FEF98E72C}">
+                <File Id="filF6F09CE4F2FE30F87734FC2AAAD0174D" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\CheckDelegate.qml" />
+            </Component>
+            <Component Id="cmp7C2F1E3031A9D19AFB668BE58F73EC98" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="{083DC8A1-457B-4087-B0A3-FE73E6468020}">
+                <File Id="fil453585CF257BD3008D8C9E17D48292E3" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\ComboBox.qml" />
+            </Component>
+            <Component Id="cmp451EFDC232DFFD28BD6B7B016C0C054A" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="{ED374B97-1933-4FCD-887C-1FB8E09961E3}">
+                <File Id="filE19B7125D874977D503ECC18D17CF67E" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\DelayButton.qml" />
+            </Component>
+            <Component Id="cmpF70E05446EBB48ADAC31F492DC97DAFB" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="{B4021119-5A01-4574-B282-B052D4D949A6}">
+                <File Id="fil376C858BD505AE1A19E77664F43475C4" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\Dial.qml" />
+            </Component>
+            <Component Id="cmp836E33FAF9164C5A638CA34211DB1732" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="{1052ADFD-A254-439E-A8A5-F6A4F21700D0}">
+                <File Id="filA0A0234994D4A5107C2663D39BFE7ACC" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\Dialog.qml" />
+            </Component>
+            <Component Id="cmp5CC46A434C01817BCBD3896BCFD43FD5" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="{90C10DF3-C3E2-468F-AF24-9F7A3CF06CD4}">
+                <File Id="filDE6AC31443AD9360EA2E04C68E3CDFB5" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\DialogButtonBox.qml" />
+            </Component>
+            <Component Id="cmp2F7F77BBA374BBBAAA8FFC0B160FC6CF" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="{D1EB3968-0141-4FDB-A8D0-841DF4C2C5F1}">
+                <File Id="fil0F35F15A0401B7776532AA3340B6AAE6" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\Drawer.qml" />
+            </Component>
+            <Component Id="cmp0617FA4E631963809B63F4A246C8C44E" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="{511FE9C8-644C-4CB3-9D80-5C85E1D2F1B3}">
+                <File Id="filF1968E83BA39945E7BB9C41DAE91308A" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\Frame.qml" />
+            </Component>
+            <Component Id="cmpDE4EE6DF5AFB0CC72D73C5AECB1488A1" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="{07385A44-D2FF-443F-B24A-3FEC9C1FA8BF}">
+                <File Id="fil030ABBC28FB009966081FFD1F5C79C2C" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\GroupBox.qml" />
+            </Component>
+            <Component Id="cmpCA5CDA48E3ECB805C8142591DDAB8570" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="{5F43F91C-1405-4C5D-9454-F7D96ABD07DC}">
+                <File Id="filB6A558B0F0C553040F86701ADB75F341" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\HorizontalHeaderView.qml" />
+            </Component>
+            <Component Id="cmpED9F11A19AB496483C376EC280BCB16D" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="{745A0CF6-31F4-4378-A89C-3B78B2A1E83D}">
+                <File Id="filFDF2A44F70022D7058AB02C41E696820" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\ItemDelegate.qml" />
+            </Component>
+            <Component Id="cmp519201BC4E40B8AED1A7313C4725FA7F" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="{0A371C06-3FAA-4DC7-987E-01BAEBFDD2C4}">
+                <File Id="fil4FA1DA0DC3F645A4141E937B3ECBDAD0" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\Label.qml" />
+            </Component>
+            <Component Id="cmpE8C184C891DA740B62B6535CC76C1F40" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="{4B671DFF-B289-49C3-90C6-7193616115FC}">
+                <File Id="filDC06C91EC735B746012F3CCD99CBA9C4" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\Menu.qml" />
+            </Component>
+            <Component Id="cmp64D872D8577E18D373DCECA1F7619FA2" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="{F0658718-CE59-4977-B723-F850CDCA9150}">
+                <File Id="filF2CFF720B27A02AB20775878B64CB545" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\MenuBar.qml" />
+            </Component>
+            <Component Id="cmpBDFD410CAB76FB8E3B925DF858CAEF42" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="{B3461F8A-6CBD-4496-AEFE-8A0EA5297C6E}">
+                <File Id="fil2B0B04F76D6C8FF35BF648B0333FCD7D" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\MenuBarItem.qml" />
+            </Component>
+            <Component Id="cmpCDD1BC254A0AD9C08C60D51FE632703E" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="{B45436A3-9622-4E5E-AAC0-CB2913E27F74}">
+                <File Id="fil69B62EDDD47BD323833EBE5605E8EF0E" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\MenuItem.qml" />
+            </Component>
+            <Component Id="cmp0B895B34812F9CCD138C1D6E639087B0" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="{2EDDF6F9-8ABA-4FCA-A940-9BDDE00BC47D}">
+                <File Id="fil0DAE410DDC1D6076414745664B1A9C75" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\MenuSeparator.qml" />
+            </Component>
+            <Component Id="cmpCAA8935F0B8F24E84D9C7625F65A1CE1" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="{631B330A-6753-436B-9C3C-574B8EA441E0}">
+                <File Id="filB1853C6038F2C85701670C2E70D0CA96" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\Page.qml" />
+            </Component>
+            <Component Id="cmp789B9EA1BFC24059135A1AC3F2ABE291" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="{FC5FCF2B-B0C1-43C5-9E3C-279CB895611A}">
+                <File Id="fil9A83D7A8CF9E7D1C3545F7E3AE1033D8" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\PageIndicator.qml" />
+            </Component>
+            <Component Id="cmpD5D68F5560DA456F36CADB3BDFA1B780" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="{83947676-82E5-4C2C-A675-A0A2A35F8EF4}">
+                <File Id="fil9B6DB44693F30FF014DE62FD7FB40222" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\Pane.qml" />
+            </Component>
+            <Component Id="cmp34A56673371F4AF3995953F37A132F06" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="{ED7C3FB9-9B1C-48FB-BD99-EF287695179F}">
+                <File Id="fil864DA3DD8B02F28538D5A0B251D6D6F5" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\plugins.qmltypes" />
+            </Component>
+            <Component Id="cmp50281D8A1CF30DB8B193E606005162EC" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="{68FC957A-DF41-4831-93D2-31C409B596FD}">
+                <File Id="filF4E3AC2154FF8B451D8D35F6ED647873" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\Popup.qml" />
+            </Component>
+            <Component Id="cmp88C6959F45BE2744A66C1D548580CE10" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="{A139B4FA-001F-4262-9C9E-4A00312D16DB}">
+                <File Id="fil6563E751A8B56CC0FD2E8B94E7E3427B" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\ProgressBar.qml" />
+            </Component>
+            <Component Id="cmp027CBFF047CAEAB709547731194901F5" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="{09859454-65E3-4D49-AED0-51F8E0884F31}">
+                <File Id="fil784EDC2FF7EB8275EBF00057CD18E0E8" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\qmldir" />
+            </Component>
+            <Component Id="cmpFB4520B28A546E5B869862913E252B65" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="{CCB65031-691E-4B07-A85D-7EDF8C61AEE0}">
+                <File Id="filAD426ED04EAF5BE201C01C9AF5AA8E21" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\qtquickcontrols2fusionstyleplugin.dll" />
+            </Component>
+            <Component Id="cmpC40D621B55A5A716AADB80CC3C1DF229" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="{E7130D95-4382-4A7F-B501-B119FA60CF76}">
+                <File Id="fil5A94630CD9E744D5D464EE97F9548299" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\RadioButton.qml" />
+            </Component>
+            <Component Id="cmpA94CE1EE69C9965967436C719EA9213B" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="{A257D844-CB7A-47C9-B5A6-84F707C9EEC5}">
+                <File Id="fil08424268E1754B8970DD70E6FC3CBC3C" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\RadioDelegate.qml" />
+            </Component>
+            <Component Id="cmp6DCC1FE0E816F31EAD56506065BEA7F3" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="{A1191E1A-9324-411E-9241-2FD41B567F6C}">
+                <File Id="fil6F747DEB1F167D7556931A15405C1EB5" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\RangeSlider.qml" />
+            </Component>
+            <Component Id="cmp77B3E98FCB0336A3FB9AE25C345ADFE3" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="{EC6496DA-85E4-4398-873B-B32756D45A12}">
+                <File Id="fil008F33B3FA66479839C8B451ED8EDE78" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\RoundButton.qml" />
+            </Component>
+            <Component Id="cmp513B048AA96D48CEFEDB8AFF9B55BE8C" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="{B3F0CE95-A969-4CAD-808E-6D18AA92D2A9}">
+                <File Id="filFFDA65058EF9380014996AA8238C0755" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\ScrollBar.qml" />
+            </Component>
+            <Component Id="cmp9F019135B1D40203DECC8FC0D1006F7C" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="{38549EA0-2761-4D82-BBB2-B6201914D72C}">
+                <File Id="fil7CA907CC4753D913CB514E3A6C2D39C0" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\ScrollIndicator.qml" />
+            </Component>
+            <Component Id="cmp72B0CF8FF52FCE6BF04B4BF080992DB9" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="{0C24564F-1F8C-44A6-995B-DC12FFCAABA6}">
+                <File Id="filFA0830B53CA178784DF6346244A6B370" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\ScrollView.qml" />
+            </Component>
+            <Component Id="cmp3585F3AD5A6C9106FB8A68B358F4D049" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="{5B8298FB-D734-4B5B-AB22-EDCF81A105D4}">
+                <File Id="filBD72FEBD06396F4A86949A38C9B7C1DF" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\SelectionRectangle.qml" />
+            </Component>
+            <Component Id="cmpA5543D5C64A49E8697E2A87F769D4AE9" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="{FEA9E307-E901-4C06-B8C8-808AE96E0808}">
+                <File Id="fil71F6D64EE99309A0281571FEC0194EE8" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\Slider.qml" />
+            </Component>
+            <Component Id="cmp6B6B735B21C0358A6E42CD273BFF60CD" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="{8B3647ED-20E5-43EB-B7E9-2E4E4E838BE7}">
+                <File Id="fil736D187093D1AF942C5C590A16FDD3FF" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\SpinBox.qml" />
+            </Component>
+            <Component Id="cmp011A9101A35BAEDFAAB317FB44B5E6B9" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="{E01F0182-6907-4F83-91E8-71E9712645CB}">
+                <File Id="fil07F5DAE9EBCA70EC370A2339E4156BAD" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\SplitView.qml" />
+            </Component>
+            <Component Id="cmpDA358FA4C827D79CFEAD11AAA8A9B061" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="{95588DB3-879D-421E-87D9-07D6BE3FBF35}">
+                <File Id="filDFFC5E566607F8B4CA48C5BB921A78C0" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\SwipeDelegate.qml" />
+            </Component>
+            <Component Id="cmpFF96327A6553E712825496431B0A7AB0" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="{8E459142-017F-40C0-8A51-DD300AE5DBE1}">
+                <File Id="fil209EF9E49BCBE6329844DE77D5E266AB" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\Switch.qml" />
+            </Component>
+            <Component Id="cmp4D1313B7A2F7741E4B402C2731A57661" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="{6DDB93E8-E6BB-4E68-A9D6-3B11A8EF20D7}">
+                <File Id="filFBC15FA939122A9ABF71555B56A6D9F0" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\SwitchDelegate.qml" />
+            </Component>
+            <Component Id="cmp6CA898D45BF72C630F2632FD2EC9DFC9" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="{3C21EA1F-BC09-416C-A3A4-23643B28F834}">
+                <File Id="fil926125BB768EAFA858C38DDC25B15227" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\TabBar.qml" />
+            </Component>
+            <Component Id="cmpB9CFDD5F1084C1CEE30776008F83259D" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="{EFE3954E-985E-42D1-B2DB-37C30496B8D4}">
+                <File Id="filB2B1E761DCED7BD1711107F2151C1B88" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\TabButton.qml" />
+            </Component>
+            <Component Id="cmp9A1943D41E4A3351F06735BFED56CE01" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="{606C65A6-E362-48E0-A0D6-1F4BB09305B4}">
+                <File Id="fil2CA95E018FA9B738B0F1B2C42F66F070" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\TextArea.qml" />
+            </Component>
+            <Component Id="cmpAB62CDBE21637A49145F438CAEEE71FF" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="{5698C3D7-F011-47A9-9F6C-389234DC964D}">
+                <File Id="fil17898560EE4F87AD357EAA697F579747" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\TextField.qml" />
+            </Component>
+            <Component Id="cmp3A37860F05D23962BD23C4596F828989" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="{27C65510-D09E-4D7A-A9AC-0B2800A6721A}">
+                <File Id="fil69C8C1B9B33621669AA4D2E6A0F80CC5" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\ToolBar.qml" />
+            </Component>
+            <Component Id="cmp118A0CC4F4677FB6C1014C64AAAD38FD" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="{5D82C108-BD03-4B99-B062-315F8422BF99}">
+                <File Id="fil94969C7CF55378593CE7952AB696F7EC" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\ToolButton.qml" />
+            </Component>
+            <Component Id="cmp21D95CD727ED9F6FB17642549B08202A" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="{53EF4D8F-5DE9-491B-9968-9E2BE9C8E845}">
+                <File Id="fil8FC9CE6BE23A0BF7AF7FE4CEFE5C05AD" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\ToolSeparator.qml" />
+            </Component>
+            <Component Id="cmpDE8FA2F547F246F03228627DAA35B3E3" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="{DC84C513-69DB-4D4F-B5BC-2448255C2F06}">
+                <File Id="filF1920823B38E8638793F6BFD066E17E5" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\ToolTip.qml" />
+            </Component>
+            <Component Id="cmp85462F0F4DC060B6E43D59D95C2EAF3E" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="{DA67059C-E81A-456E-B6AE-892965E8F981}">
+                <File Id="filFE26B68F72738DDDBFC6F5C5E53EC072" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\Tumbler.qml" />
+            </Component>
+            <Component Id="cmp316981E3BB804C288B6B9B7ADD2838C8" Directory="dirB7DD0BB42FA107F6BC6439938BFE98A2" Guid="{07A0251C-F703-47B6-B0A2-CFC66ABCB950}">
+                <File Id="fil50364DA66061827762CA321EF70210E4" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\VerticalHeaderView.qml" />
+            </Component>
+            <Component Id="cmpF804F89B59165412AFB541FB764F3DC7" Directory="dir48925D264F208B9FCBF827D59CF75478" Guid="{163C6DBF-89B8-4A22-A397-F20997CDDDE8}">
+                <File Id="filC789BD92FF5510416E35EAA91F7DC547" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\impl\ButtonPanel.qml" />
+            </Component>
+            <Component Id="cmpF321754AA0E0CE543797EDA62FFF8639" Directory="dir48925D264F208B9FCBF827D59CF75478" Guid="{B5D401AB-642E-4995-94B8-FA4EC94100DF}">
+                <File Id="filA4F1AA4B0800C4003D8176BBED77831B" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\impl\CheckIndicator.qml" />
+            </Component>
+            <Component Id="cmp42C78DF19CC84929317A806122FE1E3D" Directory="dir48925D264F208B9FCBF827D59CF75478" Guid="{0A27DDCA-3A08-4614-9696-37FC56B57769}">
+                <File Id="filD74D59820D4E6AAA1DFC2F4A96D22A3C" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\impl\plugins.qmltypes" />
+            </Component>
+            <Component Id="cmp1D28DF87F3986CF924B956D11C487A09" Directory="dir48925D264F208B9FCBF827D59CF75478" Guid="{4B0ED5F2-4517-4FD7-884F-EE741D7D1F61}">
+                <File Id="fil1FFDB110C2E8797FBEC3DBF2B92DA6A7" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\impl\qmldir" />
+            </Component>
+            <Component Id="cmp4B00A6DD6F32AF3A9063A83F168D951D" Directory="dir48925D264F208B9FCBF827D59CF75478" Guid="{C4B1337F-F61D-48F3-991E-24CC23DB3BE1}">
+                <File Id="filBCEA08117F1F4EDEEBB15E9994113AA3" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\impl\qtquickcontrols2fusionstyleimplplugin.dll" />
+            </Component>
+            <Component Id="cmpEF0E1D1EFA3D0E69B9C4DB528D05CEE7" Directory="dir48925D264F208B9FCBF827D59CF75478" Guid="{08FA67F1-1156-4309-96DC-C4D36C1BB303}">
+                <File Id="fil505D3898B47D220F4B05CE551525B83C" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\impl\RadioIndicator.qml" />
+            </Component>
+            <Component Id="cmp031FC0E00FCB88CFC82FE5DF334FFA2C" Directory="dir48925D264F208B9FCBF827D59CF75478" Guid="{8B375BD4-2E86-46FF-A35C-2659E5E112C6}">
+                <File Id="filF2ACFC9DD60EAF71D280600602CCFABE" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\impl\SliderGroove.qml" />
+            </Component>
+            <Component Id="cmpA20A5B36D761A799EB3C0428B1343D7C" Directory="dir48925D264F208B9FCBF827D59CF75478" Guid="{03EB9086-DC6A-433B-9F20-9286F990E41D}">
+                <File Id="filAD78AEF1B5063C1F77A1EFF416D2B6FA" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\impl\SliderHandle.qml" />
+            </Component>
+            <Component Id="cmpC70464974B77DC61CFF9DC89E1E9A73D" Directory="dir48925D264F208B9FCBF827D59CF75478" Guid="{D1543809-42DE-45FB-915C-59E3F86056B0}">
+                <File Id="filCE7F9028197C38EC9B0684E40BA5D779" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Fusion\impl\SwitchIndicator.qml" />
+            </Component>
+            <Component Id="cmp0AE92B0F56F7065ED92C3ADF99303E22" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="{07B6FE3F-19AD-446F-B6E3-F6E980ADF055}">
+                <File Id="filB767EF37381F7141C7A3DCAC39840B77" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\ApplicationWindow.qml" />
+            </Component>
+            <Component Id="cmp2B59A523302C9529B784D617FB787C70" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="{89793048-A20C-4A6E-939C-0BC21433E507}">
+                <File Id="filF7B6296DCAE1C456B644D87D9F1055BE" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\BusyIndicator.qml" />
+            </Component>
+            <Component Id="cmpA29DA44FC1E3C3CD62397666F7B22D1A" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="{EC35E983-F8B5-4390-B127-E2AC2A55BF57}">
+                <File Id="fil6C488007970D4BAFB2DB08C754F80FFB" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\Button.qml" />
+            </Component>
+            <Component Id="cmp2E3630D2E1619E501BE32DDC04E8354D" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="{766CDCE8-26CE-4012-85BD-E3F05FC20A05}">
+                <File Id="fil7B9685746F56E01B6AB2567E13880535" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\CheckBox.qml" />
+            </Component>
+            <Component Id="cmp4D80101EADD6D3DDD203B9EB9C85F431" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="{ADCC06A3-25D4-4FD0-9009-63EABD506C67}">
+                <File Id="fil4240AAD799B705DA78F6E223EB4A43EA" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\CheckDelegate.qml" />
+            </Component>
+            <Component Id="cmp37445D06A5B3E1E7935577CC10B83898" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="{89CE0653-88E2-4F90-97C8-08186513E174}">
+                <File Id="filC9412E7839E28AEAFF838B937FA619D9" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\ComboBox.qml" />
+            </Component>
+            <Component Id="cmpA9942B4E6B49EACD30FBCEB2155B76F9" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="{25A1E39A-3034-49E7-AC6F-6B154474D08D}">
+                <File Id="fil835E27839A441A92412B36A1436AE7E2" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\DelayButton.qml" />
+            </Component>
+            <Component Id="cmp8CA79672BD5731C6F1820F9A9A0686E0" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="{04D2BD7A-3349-47D2-9CDC-8FBD0C1A6222}">
+                <File Id="fil32B7FECA37D20FB437DAE4EAE8D83D9F" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\Dial.qml" />
+            </Component>
+            <Component Id="cmp1E19938B3005A79AC76779BB3361BA8E" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="{2957B9CE-7E0A-4E78-9AA6-092F4294E855}">
+                <File Id="fil238234A2323BBE1B65CF566AEA3F5D59" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\Dialog.qml" />
+            </Component>
+            <Component Id="cmp791127701CE2A50DF1852818B7491A9B" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="{CEE44C67-586A-4631-AC87-3C578E7FBF97}">
+                <File Id="fil4293568265BDAF9DEBDF93686F1DD5FD" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\DialogButtonBox.qml" />
+            </Component>
+            <Component Id="cmp7D52281AAE07BA676640D549C3E43E52" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="{D8E1A8B5-63DD-491A-8AE9-4B6E412BFE49}">
+                <File Id="filA7D587F803CE8A93102C2F7A92D32700" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\Drawer.qml" />
+            </Component>
+            <Component Id="cmpE7122D05325A3775A171F8C1697C7FBB" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="{73E00C6A-1A85-4CB7-B52C-ED54812AD95F}">
+                <File Id="fil78658507F1694BA1ABC3BA8E20CE6C30" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\Frame.qml" />
+            </Component>
+            <Component Id="cmpC388276A63FE9399AB33D949E56BE42B" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="{E18C9F03-5805-4059-BF6E-6D6CDCBBEA0C}">
+                <File Id="fil7B32D2B48797ADBAD654D1B89897E1F0" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\GroupBox.qml" />
+            </Component>
+            <Component Id="cmp1882DAC6A3EBE8964A93AF06ACFC7EB4" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="{09E79AEC-94D7-4C9F-91F9-ABE79F40957E}">
+                <File Id="fil364F48C9D098535A05FBEC76AEE76B2A" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\HorizontalHeaderView.qml" />
+            </Component>
+            <Component Id="cmp9C6D36C97809FD889F7352F368EF795B" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="{5040365C-5C6F-46F2-82DC-3C044033C495}">
+                <File Id="fil3C3DC74E90776063E5DD535D5133F1FB" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\ItemDelegate.qml" />
+            </Component>
+            <Component Id="cmp0FB99193EC45424300BBC1CC9FE23F3B" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="{A824CFA6-C01B-45B3-BAF0-7443F6649C9A}">
+                <File Id="filF78C5321460DFDEB14ACD3D54D932D22" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\Label.qml" />
+            </Component>
+            <Component Id="cmpB1425086E74FCFE7599D5B98F96B5784" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="{CF53BC93-7561-472C-9136-B59EEAE667CA}">
+                <File Id="fil3649AA3B8C7DAE7AC4D0E353BDEE7229" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\Menu.qml" />
+            </Component>
+            <Component Id="cmp90336EEF38CCA73580CA5B9E08C80EAF" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="{0DB8A527-ECF2-4C69-A569-CA2F754FB573}">
+                <File Id="filAD597F01E7E839A0979C491EBB930C78" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\MenuItem.qml" />
+            </Component>
+            <Component Id="cmp67B34C88BCFD4BC9E9DC64A14C63B406" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="{034B4705-B048-499C-82E4-2544DD8812F2}">
+                <File Id="fil304DBC131B1D58544E62E9B56B0A728E" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\MenuSeparator.qml" />
+            </Component>
+            <Component Id="cmp1B35E65FB4F3E5B95D841682C48E6657" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="{555D8E6C-B030-45CB-AD25-01BE1F49FDB7}">
+                <File Id="filDE75722F397D008570FDDC797FB941B8" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\Page.qml" />
+            </Component>
+            <Component Id="cmp336EE7E45BAAE772C3C37D4742EB2971" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="{6D418C05-5ED4-4BE3-9B25-12EE16FC6329}">
+                <File Id="fil9FD9AB969D40B479E112503F24402B78" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\PageIndicator.qml" />
+            </Component>
+            <Component Id="cmpD9A4ABD4DAC06729B089B94DC2659DAC" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="{20339E2B-E967-4D48-8322-135BDD00EB72}">
+                <File Id="fil91B1FE6304E8FF2B14ABEBA731149ECC" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\Pane.qml" />
+            </Component>
+            <Component Id="cmpCF3FCB3F233565D555FB8F6D9555BEE7" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="{42F98759-6706-40B3-9B87-C5CF2AC363E7}">
+                <File Id="filD3989B72FD12A9018E51557B644C957D" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\plugins.qmltypes" />
+            </Component>
+            <Component Id="cmp5E1C8E699FC4F995BDC27D657EDF394A" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="{79209391-2421-437E-A229-9DA9CC066ACE}">
+                <File Id="filDBDD17973F7D18C5EE4A718E329F753E" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\Popup.qml" />
+            </Component>
+            <Component Id="cmpFA5F7E69197933807328D50D858DCECE" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="{66D84901-C834-4C41-9715-1CE0D1B9FE97}">
+                <File Id="filBBD31AE80B657F75029FFDAC9E518728" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\ProgressBar.qml" />
+            </Component>
+            <Component Id="cmp3990B5E9CF8258348A632B5B699F36ED" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="{67A563BD-EFAB-4380-9A68-DAEDFD43844A}">
+                <File Id="filFA6D4FA7FE353656D802FBF9D675E767" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\qmldir" />
+            </Component>
+            <Component Id="cmp986F544E9308DF1B743DFA1EAC30EF43" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="{1A3A952D-02F2-4FB0-BEC0-FBDB5B1428A5}">
+                <File Id="fil864CC154589BC52C31E3A37D8E894C1C" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\qtquickcontrols2imaginestyleplugin.dll" />
+            </Component>
+            <Component Id="cmp18F8A218D9D2036ACE6D3B8670DFB611" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="{B8562AF3-A136-48E9-8174-AF5F5A7877B5}">
+                <File Id="filE22A686DBC4A065B54A768CF28B40B24" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\RadioButton.qml" />
+            </Component>
+            <Component Id="cmpF24F72D6E16C2DF2FB9361DB63B08FED" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="{B446B314-E038-4350-A00D-DEF66E12F56B}">
+                <File Id="fil80AC24E33C1863479132EC0565A358DC" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\RadioDelegate.qml" />
+            </Component>
+            <Component Id="cmp88E9B95C71435CA87A11074283BDF487" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="{3550E313-A3BA-4369-97D3-AC62302BD360}">
+                <File Id="fil2E8044C05FEED1C25D49003C83AB596F" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\RangeSlider.qml" />
+            </Component>
+            <Component Id="cmp9736CC8B57B700A14B0980A91EE2D1BA" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="{5FF2B94E-7FA3-4D41-9EDA-8281C7911E8B}">
+                <File Id="filB695029EC2EDB433FB2380A43F2C14F9" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\RoundButton.qml" />
+            </Component>
+            <Component Id="cmpEB828AE5BEBAF16603A099FE6089767E" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="{12FB9C90-998A-43BC-B9A1-112118A57D2B}">
+                <File Id="filE9494FFC76BECC63991B6D8C4F671092" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\ScrollBar.qml" />
+            </Component>
+            <Component Id="cmp44B48BD69A63FE32825423623FD36B15" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="{627A3844-55C1-4E51-A2D8-A721CB736D6B}">
+                <File Id="fil14D8341EE42E94CB074A0F12E995A278" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\ScrollIndicator.qml" />
+            </Component>
+            <Component Id="cmpE765F59E9BF850583FBA116A6AF4F621" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="{C2566002-1641-4275-AD6F-48078C5B8045}">
+                <File Id="fil3184E525C64E9B2B4AA2A1E368D4CA81" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\ScrollView.qml" />
+            </Component>
+            <Component Id="cmp7DD15A60151DE8CA6F8816C39266A1C8" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="{98F605ED-33E4-4F31-87C0-D358B66F08E0}">
+                <File Id="fil7DC60796B28AB3E3BABB417771AB8ED3" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\SelectionRectangle.qml" />
+            </Component>
+            <Component Id="cmp9F4B9B4F143B31AD8537AFF4C2D16838" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="{3B1DD356-DC8A-441A-B531-E097259D8F6B}">
+                <File Id="fil69D52DA927BD3817C3C611FDFEE5CCD5" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\Slider.qml" />
+            </Component>
+            <Component Id="cmp4341710343B0A84873572097216DEADA" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="{352340D9-AD5D-43E4-B467-E8FC97378927}">
+                <File Id="filB69332E29D92BF81C951E37807BDF81C" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\SpinBox.qml" />
+            </Component>
+            <Component Id="cmp5FD195FDA6B02A8E02EE196705B70A9F" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="{338FA44F-43F0-4083-B204-9D308EBC22E6}">
+                <File Id="filB750E3C7B8FF0D9B591E2CE95B4324E6" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\SplitView.qml" />
+            </Component>
+            <Component Id="cmpA01799766E703E7B6D11942B04364561" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="{F2C15300-8DF1-41E8-9739-FE433066090C}">
+                <File Id="fil39DD4401AFDBABF27165F3F9A43E06E2" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\StackView.qml" />
+            </Component>
+            <Component Id="cmp06A47B65D20F3BF5058EBBC021329BAA" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="{5C67EC30-7CEC-4B89-8582-DA34AC5B9B5E}">
+                <File Id="fil2BA002626459ED0D4CED59E5C9870199" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\SwipeDelegate.qml" />
+            </Component>
+            <Component Id="cmp4D232C26F610D7B34074B8BD1D75AA70" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="{5ED69F27-0F72-4E55-B732-D11C148651B4}">
+                <File Id="fil3ECA9A90BEBA5018E6E9720F74FD809F" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\SwipeView.qml" />
+            </Component>
+            <Component Id="cmpAB50455C6C07F9A4A26623A87DFB0900" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="{9C15A6F2-7EE5-48A7-94D8-B7DB81F97ACB}">
+                <File Id="fil99D76CA8B195C7F5D9FEA7E8999F60EB" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\Switch.qml" />
+            </Component>
+            <Component Id="cmp980F9D626381D2AF3FD27BE6FA155359" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="{F4B34407-772D-4A18-86AB-BAAC59ACA0E5}">
+                <File Id="fil953F291148872594E9AF482D2038D494" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\SwitchDelegate.qml" />
+            </Component>
+            <Component Id="cmp2B4D9D30B7AB9C7AD428DE97D579AA64" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="{4103A006-5FE0-458D-893B-F8BDD4A52729}">
+                <File Id="filC62AFE4AEE7C035A98789387806D9383" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\TabBar.qml" />
+            </Component>
+            <Component Id="cmp33B75162158ECB88D7A23F30FD763F95" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="{09119FBC-0A0E-40E6-B71E-3F77C7BCD3A4}">
+                <File Id="fil7083F0C7C56C404D3DD517C64BC36412" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\TabButton.qml" />
+            </Component>
+            <Component Id="cmpE1AC5477D77CA612836AD567921AE60C" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="{E7250105-9B9D-4367-B9A6-941F34700D36}">
+                <File Id="filF04A3782818976B48D5E7BC0A06713B0" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\TextArea.qml" />
+            </Component>
+            <Component Id="cmp808A5FAA30807132EFCAA566A341DC91" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="{180BE63F-B8F6-409E-B29A-98A578CEFD6B}">
+                <File Id="fil7C07F0B29F32604B91E663BAAB4124EA" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\TextField.qml" />
+            </Component>
+            <Component Id="cmp6A3CCDB84C8EB5571B473CA7C10A20B5" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="{E35CC85B-F030-44FD-B094-5667A63E2D31}">
+                <File Id="filFF5FBF3050797FF55DF18AECFFF107A8" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\ToolBar.qml" />
+            </Component>
+            <Component Id="cmpF61931DC5F3FF58EDA86C22C3F58C328" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="{CB2F424B-BAAD-4A2D-8D14-503D437994EA}">
+                <File Id="fil52A5496FC0D3BA163BA098734EA289A7" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\ToolButton.qml" />
+            </Component>
+            <Component Id="cmp9C8B9DF33CB082D2CF05065BEEA8DCB8" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="{999489B1-3885-450D-94C8-C738611E2429}">
+                <File Id="filEBBFBE394CB64593B4F8CE36E75F19E1" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\ToolSeparator.qml" />
+            </Component>
+            <Component Id="cmpDE436E9A542E46614FFFD60B10959990" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="{D823B44D-78EC-49ED-B41E-33E55E820003}">
+                <File Id="fil99F32395F78F660CF65B83DB4C24F2A8" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\ToolTip.qml" />
+            </Component>
+            <Component Id="cmp883D99D09DD15DBCB8F5272F8A8429CD" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="{2B5DC513-1B7B-4BFE-ACDC-67917EC14C7F}">
+                <File Id="fil1B9300F5C5F509BA3632F23D4632D6AB" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\Tumbler.qml" />
+            </Component>
+            <Component Id="cmpFB59FF560864ECF64EECAABE64112B4E" Directory="dir3F609D3494FEF5EC2888E9B865EA8336" Guid="{5C370036-CA88-487E-99D2-F88018186558}">
+                <File Id="fil2A3BDE0D397759039C2846BE56F55838" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\VerticalHeaderView.qml" />
+            </Component>
+            <Component Id="cmp2F4A067EF22E659A29351F47EC649485" Directory="dir7970C160330F95B9F7E3CCDAB2CF5702" Guid="{E517CB59-B856-4829-971E-C4C676C8D123}">
+                <File Id="fil7EFD45119BDFBBBC4F3EEB8A3168B58F" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\impl\OpacityMask.qml" />
+            </Component>
+            <Component Id="cmpD3F5C4FA5F6A0F45634B7CB9D759B11F" Directory="dir7970C160330F95B9F7E3CCDAB2CF5702" Guid="{CADD3C14-F288-4745-B4B6-875500C18FB3}">
+                <File Id="fil467DFB7AB69F634BA67A0541A502CF6C" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\impl\plugins.qmltypes" />
+            </Component>
+            <Component Id="cmp6F7F439DA976D2187EA2E2870C2A2EDF" Directory="dir7970C160330F95B9F7E3CCDAB2CF5702" Guid="{A2122248-2DD0-4CAF-AE11-DE7157496A66}">
+                <File Id="filD8199E2FBE24BC9B8A0E289A9148E944" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\impl\qmldir" />
+            </Component>
+            <Component Id="cmpFAA3B9C7407D0FD54E5641949342F04B" Directory="dir7970C160330F95B9F7E3CCDAB2CF5702" Guid="{53C67972-3A08-467F-9C37-EE532F861B30}">
+                <File Id="fil94320F53E375FBB7BA4382411890CFF8" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Imagine\impl\qtquickcontrols2imaginestyleimplplugin.dll" />
+            </Component>
+            <Component Id="cmp7E1D2A30E36DEC4B04D58E1205BC6EE3" Directory="dir68B807445C3B5B5D97077A041B375DC5" Guid="{1C3910EE-B329-4121-93AB-9BF1AB10C35C}">
+                <File Id="fil0A1E55ED27EFBE34A162E88114B4D27A" KeyPath="yes" Source="SourceDir\QtQuick\Controls\impl\plugins.qmltypes" />
+            </Component>
+            <Component Id="cmp4736493ABAB34042B01981695B30997A" Directory="dir68B807445C3B5B5D97077A041B375DC5" Guid="{E8960954-4507-4427-92D8-AF6C5D836EA8}">
+                <File Id="filE4E9BB2F6A2058E5E3819A19F127A9AE" KeyPath="yes" Source="SourceDir\QtQuick\Controls\impl\qmldir" />
+            </Component>
+            <Component Id="cmp3F77E0CAF99C597DC95D9FFDAE729F19" Directory="dir68B807445C3B5B5D97077A041B375DC5" Guid="{886ACCA2-C85A-42FF-992A-25869FE1773C}">
+                <File Id="filC54932CFABAD7D1B8167610A3675D2F2" KeyPath="yes" Source="SourceDir\QtQuick\Controls\impl\qtquickcontrols2implplugin.dll" />
+            </Component>
+            <Component Id="cmp2484ACCA4386C4EA0755C5DC029E0B17" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="{43FA47BC-79BE-4C71-B4F0-30B2B1C60369}">
+                <File Id="fil98CE2BFE43345127253926D10D8FE106" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\ApplicationWindow.qml" />
+            </Component>
+            <Component Id="cmp1CABE6D723F88A8B7C5CFC6E637BA0DD" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="{A5F22543-0EC0-4B08-A7C5-43E65759977F}">
+                <File Id="filCD22BC7ADA9B892C477A1BFB652D27B2" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\BusyIndicator.qml" />
+            </Component>
+            <Component Id="cmp150B8F53E3E4D187A3635EC7DF0B3496" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="{857563F1-B5E3-4740-BEF8-4C5921A97FA0}">
+                <File Id="filAA267072FF70009974591236F3976908" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\Button.qml" />
+            </Component>
+            <Component Id="cmpBA71C2D28506DF91E8604A48D4D7A681" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="{0DEDB2AA-1080-4941-BFC3-3E277AD6916D}">
+                <File Id="fil36DB7EB1294D2BD6DC2556971A6ACBF5" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\CheckBox.qml" />
+            </Component>
+            <Component Id="cmpAD5A1008138AEC7C44E2B8F881D727D8" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="{BB779B87-99C3-4DC2-9E86-D49064AD4130}">
+                <File Id="fil8D439F19C8177116A9EC9B0D46D06ABE" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\CheckDelegate.qml" />
+            </Component>
+            <Component Id="cmpFE8134A082F65474A8A7C96F550F7B6F" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="{D9AE3F92-A033-4737-BA18-705DDD4D0C38}">
+                <File Id="filD94D37CFFC95F23BE5BF3E7156351502" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\ComboBox.qml" />
+            </Component>
+            <Component Id="cmp92EC30E2F97599E6475833FC935F5ADE" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="{06508922-4E56-472A-A749-44F1EDD8E166}">
+                <File Id="filB5A130F555AF929C8A44F676CA51411B" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\DelayButton.qml" />
+            </Component>
+            <Component Id="cmpF7269A0A3A4FA0533A026EBB0D054514" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="{24707249-4A94-4C93-B87B-6E9B8D60FB7C}">
+                <File Id="fil3FDAFFCB7CEFF8929DF88A615B64CA9D" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\Dial.qml" />
+            </Component>
+            <Component Id="cmp49EF77F2FDAC5E8D7F5AD5057BB8BC5B" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="{4D9C71AA-D022-4834-B051-5CE3B48EBB86}">
+                <File Id="filD8F74A3034CADD0EBE81EED5817B7CBD" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\Dialog.qml" />
+            </Component>
+            <Component Id="cmpAAEF1F717A8228116C2BFC9C972091AE" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="{3AC32B0F-57B5-4FB8-8EA6-9EC25E88168A}">
+                <File Id="fil1CEB800ABC17A533C224D38D599DA8E2" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\DialogButtonBox.qml" />
+            </Component>
+            <Component Id="cmp919D0F58C7BF8E066F8AD19FBFFF401F" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="{FEDE7553-E677-4556-995E-2C152E71A8F6}">
+                <File Id="fil55D8CF7962BD62EF276366D4F51A5108" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\Drawer.qml" />
+            </Component>
+            <Component Id="cmpDAC86E05F7C5A0C685057D15EE8EBFBB" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="{C8A3C751-F37F-4673-AD9E-EF0342D89014}">
+                <File Id="fil4104A2470A08C2E99FB8497856FB5CCC" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\Frame.qml" />
+            </Component>
+            <Component Id="cmpFA397A980F26746B37DA49E25CB9BE6B" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="{812C2717-569D-4695-8C51-DAE5E87486FF}">
+                <File Id="filD5C43CB4B55D52B7842A05F4854763E4" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\GroupBox.qml" />
+            </Component>
+            <Component Id="cmp876A1F2FA211E31C812F77CCE6B7BF9F" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="{696EDA55-CC8D-4A74-910D-F0564640D421}">
+                <File Id="fil1F63F7828CEF6DE31A501BBD48E88E1D" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\HorizontalHeaderView.qml" />
+            </Component>
+            <Component Id="cmp9D106D22A350BC32239D948D1B1DB7B3" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="{70B1AF17-2E17-485A-8FDD-10CF0BD618F3}">
+                <File Id="filBBBB46A8A3BA617AA5B8D35907153E71" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\ItemDelegate.qml" />
+            </Component>
+            <Component Id="cmp00936E9FC59249D652B2965A887DAB92" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="{90E65CB6-75E9-4144-B34A-92CA3E0E92A9}">
+                <File Id="fil277FE7AE1BCD078B80B9E701965A27EC" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\Label.qml" />
+            </Component>
+            <Component Id="cmp85D562BA4AC03A938CE51BA067EE9411" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="{8035BCC7-2EE7-42BD-94A9-603C0436F60D}">
+                <File Id="fil3663264AD718BFF48BF30125B2E57940" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\Menu.qml" />
+            </Component>
+            <Component Id="cmp88C26B837D1623B7D9328BBC647BA71F" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="{19E2B7C4-803A-4EC2-94CC-5C546694EB6A}">
+                <File Id="filA77CFDEC0F9A76D2591207810823A525" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\MenuBar.qml" />
+            </Component>
+            <Component Id="cmp84DE03E70A555E06B6C3168D04E5ACD6" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="{789978A9-0FD1-4922-AE7A-0B1AD74BAD59}">
+                <File Id="fil1289F89AC5D07B94DB5B19B6798FB0A3" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\MenuBarItem.qml" />
+            </Component>
+            <Component Id="cmp31B9C2EFE4AECA846D066FDA7625A4BD" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="{8A4AB853-AE5D-4270-9315-D659B0D8370B}">
+                <File Id="filA29EEE317FE4F6B75D1C6368C2D1A028" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\MenuItem.qml" />
+            </Component>
+            <Component Id="cmp564788F5E82213B81DB7AA5A87DB3E25" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="{364495D5-8721-4EE9-8796-DE0A9EFDCF95}">
+                <File Id="filDC3ED25FF0BB2356EE3005F5A3E502E8" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\MenuSeparator.qml" />
+            </Component>
+            <Component Id="cmp47C69EB69EC227A0B7A322E2B676378C" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="{EC763578-DB72-41FD-9E81-1211A95F9FB2}">
+                <File Id="filAE954BA0FE603BAF163499F85AB043AF" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\Page.qml" />
+            </Component>
+            <Component Id="cmp5F61DDC08BCDB7719CA7E527708C93C1" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="{9C9BB529-246D-4FDC-824F-76FBD12841EF}">
+                <File Id="filFC23E466F737593E81324EC0331719C1" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\PageIndicator.qml" />
+            </Component>
+            <Component Id="cmp2DB3EABA95D743CE983D528D8CC93E06" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="{ADD52DB2-D508-4004-AEDD-109B98A2AFDE}">
+                <File Id="fil14DB824F7C509C01068C72C3B37E7A5D" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\Pane.qml" />
+            </Component>
+            <Component Id="cmpC27D371B7AF16E9440323FC8B97961E7" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="{49C99942-878E-4C33-A027-DFD06A34F8B3}">
+                <File Id="fil0D3BE9F2C475F1A3363C95FCA224C987" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\plugins.qmltypes" />
+            </Component>
+            <Component Id="cmp2ADAAB6963EEC51A27B8C83E06615107" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="{086EF0F9-A02F-42D0-8318-E9E6084C54A2}">
+                <File Id="fil17B4B5694D32AE50511F087E8186666D" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\Popup.qml" />
+            </Component>
+            <Component Id="cmpBC16C2D5C71B4025BBE65FC1ADCC2E2E" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="{121BBB71-1FCC-4BFD-A45D-F688A01F156B}">
+                <File Id="fil67AADC50B8EBF8B4559BE98F2A643C7E" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\ProgressBar.qml" />
+            </Component>
+            <Component Id="cmp2A7EFC2FC51AAC11A7AD530967908CAF" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="{AA17815B-AAC6-4319-B9F1-DBA7382F2F53}">
+                <File Id="filDC5DDEA55DD6396AA1BD11833AC67126" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\qmldir" />
+            </Component>
+            <Component Id="cmpC2D73E1422E4A677607B108FDF480025" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="{CFC54AAB-0648-49D0-9DB7-C0D576712868}">
+                <File Id="filBFDB50B68C3C7AFA6D33280DE84AB611" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\qtquickcontrols2materialstyleplugin.dll" />
+            </Component>
+            <Component Id="cmp944C1F5E73CDEFE57A9F1847773FF643" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="{A03555AF-DAE1-4F57-97BF-0AF69B900144}">
+                <File Id="filAF33564E5C73709F58B84B9680B95573" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\RadioButton.qml" />
+            </Component>
+            <Component Id="cmp6292EDD00596B1A384B82CC027A79DF1" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="{D3926CBF-FDAA-45E9-99BC-0C430014AC7A}">
+                <File Id="fil2826159FE6C201461BF4D8E595D96978" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\RadioDelegate.qml" />
+            </Component>
+            <Component Id="cmpC4B1039C7160CC45EE73054EFAAAC0B8" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="{A905C955-37E4-4AEC-BAB6-C83D64E233CD}">
+                <File Id="fil9E47F0E6223EDAEDC34AEFBABB730867" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\RangeSlider.qml" />
+            </Component>
+            <Component Id="cmp022DFBDE861C6B29A054F5963EE2B5EA" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="{B63ABD34-1130-499C-B419-E9043DC23A82}">
+                <File Id="fil5E54E72E7781831F2755AC3FA09083DC" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\RoundButton.qml" />
+            </Component>
+            <Component Id="cmpD0CECCC44B61261D9DFDC13668F84FC1" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="{56EBD770-75BA-46A5-907E-664CA8CEE8D5}">
+                <File Id="fil6817407CD64183FF2E1B2116A58A4040" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\ScrollBar.qml" />
+            </Component>
+            <Component Id="cmp68F38D4DBC594717681DA90B161659CA" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="{3B8DCEA0-969F-4BC7-B55D-C5FBE8785EF9}">
+                <File Id="filC3E86B969A4F3B35AE391131A155A110" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\ScrollIndicator.qml" />
+            </Component>
+            <Component Id="cmp5C104AE9099052E2403BF80E5010CA05" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="{8A645BD2-C22B-4277-BB08-01336D3A3AFF}">
+                <File Id="fil93FBDF6840EDC337A5B23CC9CD3374BD" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\ScrollView.qml" />
+            </Component>
+            <Component Id="cmpD3B596CCF391818683C45670554D70E6" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="{3D2B1F50-BD0A-4C36-82FF-35C540C37873}">
+                <File Id="fil44DECFF6A9EAECE06988197FE80B12B0" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\SelectionRectangle.qml" />
+            </Component>
+            <Component Id="cmp5E88D1FB666FBADDAEDDF7F6D5965FEB" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="{AC73625F-C47F-4F39-84DC-FC43A67C7441}">
+                <File Id="fil6D5485BB82D9B226FC460ABE76A795A0" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\Slider.qml" />
+            </Component>
+            <Component Id="cmp61D9C682647B4E15638AB211625A20EF" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="{948E4029-C85F-4582-B25B-CA9E8A0438E1}">
+                <File Id="fil5F041632EB639600BE98BBC0978477EC" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\SpinBox.qml" />
+            </Component>
+            <Component Id="cmpC11C5AEEC78A36A400623D2B2634FE0E" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="{2A2A19AD-AE01-41BF-A998-A152B5E3C7B2}">
+                <File Id="fil96BCF71BDD33EEF508D856CCC6CF69C1" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\SplitView.qml" />
+            </Component>
+            <Component Id="cmp5A87B6D2F5D12F503E12312658D8D561" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="{A48DDE6A-9534-4657-B723-B9303F6ECFB3}">
+                <File Id="filF1080461F27F55D2AB51B496B3A4F207" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\StackView.qml" />
+            </Component>
+            <Component Id="cmp10387E3129C01B96666D6A3A3AF1C23A" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="{DBC15503-146A-4A31-A14A-A79AB66A92E1}">
+                <File Id="fil0225C1BDD92DC16E5502A5BCA49F96F6" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\SwipeDelegate.qml" />
+            </Component>
+            <Component Id="cmp317381168221ED463BF2ED6046F0DEDC" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="{2AFC67FA-C28D-4F8C-81CD-E63EDF33D56B}">
+                <File Id="filF3D28904BF3EF3D9D8C821F8C3B4E3D4" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\SwipeView.qml" />
+            </Component>
+            <Component Id="cmpA101EFF8776D6124F03EE80470EA6C79" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="{B025CACC-62AB-4EBF-9106-4F7BA09F1E86}">
+                <File Id="filD30ED475815DD2180B7D0EBB8A8730C9" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\Switch.qml" />
+            </Component>
+            <Component Id="cmp84E49F27B152536770E1D198D6882AE8" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="{9B24C75E-325F-475D-9BE0-7E3135C380E1}">
+                <File Id="filB5769414552C2D940DD6BF741B76A10C" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\SwitchDelegate.qml" />
+            </Component>
+            <Component Id="cmp0C7120286638333E7DB2CC823AA01BF7" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="{F9FC8087-FC05-40C2-B45C-148FADE6C316}">
+                <File Id="filB31C5970C979FF32921717A317A703FB" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\TabBar.qml" />
+            </Component>
+            <Component Id="cmp1669E3B861A273DBAEC1B07897FC94A2" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="{00BA5EE4-6D7C-4319-B793-D37AF0683500}">
+                <File Id="filE73A0FEE0A4B54A5B6F71CD9CA9C5DE3" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\TabButton.qml" />
+            </Component>
+            <Component Id="cmp6F48BD948C8607BD25BBD32809192134" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="{0C537AA1-A473-41D8-9C17-05C37D5A9CA1}">
+                <File Id="filE232E218B2134E4A11649C7A39A214FE" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\TextArea.qml" />
+            </Component>
+            <Component Id="cmp02A8A63E7AA36D9142FA648F74300E72" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="{5D1B5A70-F74F-488A-8784-CEFD0151D657}">
+                <File Id="fil83B097C7750B41F18D22229E22BC275C" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\TextField.qml" />
+            </Component>
+            <Component Id="cmp94F104BB2082647E01DA35DFE6C2BC2C" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="{A246808D-84FC-4E38-A454-569C40470E0B}">
+                <File Id="filEFE4324C9894458EAF3DD996B2574670" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\ToolBar.qml" />
+            </Component>
+            <Component Id="cmp1912AF5DC3DEBD163AA91F8BA3B01720" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="{AE99CA03-122C-466C-A74B-ADC4D85DB42F}">
+                <File Id="fil98F955F3D239A653EFD4067ADF1B1F35" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\ToolButton.qml" />
+            </Component>
+            <Component Id="cmp168E0C3533ED0B546C5FE8AAC03EF7AD" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="{E89F9802-3B78-4D82-A950-DDAC4298D124}">
+                <File Id="fil2F89C2CD6A812EB5D9E9031047AF4CF4" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\ToolSeparator.qml" />
+            </Component>
+            <Component Id="cmp55EE8538AAABFE4047346A048968B6CD" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="{F03BF936-F54E-4FDA-AB4B-78036E0D52D1}">
+                <File Id="fil3E346F115EA572A085D64B711F64DB03" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\ToolTip.qml" />
+            </Component>
+            <Component Id="cmpBEFC0E759EB09ADD1528DEA72860D434" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="{B20518A3-C5C9-4B79-93CD-55205D998E3F}">
+                <File Id="filAE9CAC6E57F455490318BD9F44F4C83F" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\Tumbler.qml" />
+            </Component>
+            <Component Id="cmp3F64B12F194CD470A60FAB7FAEBE121A" Directory="dirF45E3860752A8B1AC79318D838CCE535" Guid="{1848BFA2-AC22-48AC-A0D5-82BE575A342D}">
+                <File Id="fil1387B19E1BF6C097EFE9C94098AE2AD7" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\VerticalHeaderView.qml" />
+            </Component>
+            <Component Id="cmp4613B82FE5805E334C2EAE293D1FDA73" Directory="dirD4CF5DF9BDC9CA55E064A8C389B40501" Guid="{9D7B48AD-76CC-4D89-85F2-811CCFD756F8}">
+                <File Id="filA68DB84A811B266C685CEE70242C70CD" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\impl\BoxShadow.qml" />
+            </Component>
+            <Component Id="cmpB13F649E8CA70FE0AD2F692D328DBE5A" Directory="dirD4CF5DF9BDC9CA55E064A8C389B40501" Guid="{E4E1FB1F-244B-45C1-9CCE-AE42DF2B5AAF}">
+                <File Id="filD19E4FD2FC8939034E33A4082B244427" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\impl\CheckIndicator.qml" />
+            </Component>
+            <Component Id="cmp4F579F22F5991056C50BA0820141FC7C" Directory="dirD4CF5DF9BDC9CA55E064A8C389B40501" Guid="{5F32B5AC-0FC6-499B-91F8-99B6B4071BD6}">
+                <File Id="fil71B68E5ADBC74C83E430B37D3F64FCD5" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\impl\CursorDelegate.qml" />
+            </Component>
+            <Component Id="cmpE8448728EB5B6A914C13B66662D9758F" Directory="dirD4CF5DF9BDC9CA55E064A8C389B40501" Guid="{787EFBEC-CFBD-41E2-81DA-B36D9E26165D}">
+                <File Id="fil3F896C09724090C33709CAB5291EEB4D" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\impl\ElevationEffect.qml" />
+            </Component>
+            <Component Id="cmp380FEFB7977E8FD5EF81EEA08381907D" Directory="dirD4CF5DF9BDC9CA55E064A8C389B40501" Guid="{5B3905E5-C124-401B-8E38-263C2A112242}">
+                <File Id="filF94FE54BFCC8A8E474435BB57BDB1754" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\impl\plugins.qmltypes" />
+            </Component>
+            <Component Id="cmpD65057448FF4D30321F0DCA26C382450" Directory="dirD4CF5DF9BDC9CA55E064A8C389B40501" Guid="{35865162-D504-405D-8014-51A1FFACE1F0}">
+                <File Id="fil13A64843D2611849C3B307D18803ABAE" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\impl\qmldir" />
+            </Component>
+            <Component Id="cmp0A4CAF85E34D1B2AF078FCB322719BCF" Directory="dirD4CF5DF9BDC9CA55E064A8C389B40501" Guid="{67CCB798-EBD0-4BA4-B2AF-082ECB441447}">
+                <File Id="filCFEBC999C66756E9120E4B973F72CE21" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\impl\qtquickcontrols2materialstyleimplplugin.dll" />
+            </Component>
+            <Component Id="cmp2821A8A5A3876FCE4EAB1EF52EB062FA" Directory="dirD4CF5DF9BDC9CA55E064A8C389B40501" Guid="{D34561BA-9D12-4E45-B892-60BAD38CBD03}">
+                <File Id="fil2365F1274AD13460FE3C5D3C8FF653FF" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\impl\RadioIndicator.qml" />
+            </Component>
+            <Component Id="cmp4279783DBDC9942B50B483804FD3AEC3" Directory="dirD4CF5DF9BDC9CA55E064A8C389B40501" Guid="{7074B73C-76A3-4BA8-A858-31432EB3217A}">
+                <File Id="filAD98AF073F9190BF775B5794C1638A58" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\impl\RectangularGlow.qml" />
+            </Component>
+            <Component Id="cmpF1F8978AF520020EF78A370452C80628" Directory="dirD4CF5DF9BDC9CA55E064A8C389B40501" Guid="{22D4E932-DA65-4AF0-896F-077CA3C76784}">
+                <File Id="fil37EE179F06BB7014856960611AA2145B" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\impl\SliderHandle.qml" />
+            </Component>
+            <Component Id="cmp200DE3D51BCF5719541F1CEC3149726C" Directory="dirD4CF5DF9BDC9CA55E064A8C389B40501" Guid="{1DD8D1E4-CA06-4FE9-B624-DE716869AFD4}">
+                <File Id="fil86DEB21E8EB571AC7F36DCC977A4298C" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Material\impl\SwitchIndicator.qml" />
+            </Component>
+            <Component Id="cmp2FAD1AA520E7686F634427FB374BB635" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="{46E5928E-4743-44FE-B476-1907B5400AAD}">
+                <File Id="fil0AD512B7C2B483BB7C87049E8B45559B" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\ApplicationWindow.qml" />
+            </Component>
+            <Component Id="cmp2ADEFC5A9071EE0843D5BFE12567B84A" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="{897AF0C3-9BFD-492F-B722-94317F21E45A}">
+                <File Id="fil4B00A0A1204162CA3C14830EB9EF1DEE" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\BusyIndicator.qml" />
+            </Component>
+            <Component Id="cmp4FAA1AC9495E561E3D9DEEDFD7F56B35" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="{1C38F098-3FB4-4CFD-9F75-010DF1113016}">
+                <File Id="fil9EBDDDC54FC2C8FE2E1B7763CFD8B030" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\Button.qml" />
+            </Component>
+            <Component Id="cmp98299BBCE2B330E6111405911B6BE08B" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="{F485B7FD-A073-4960-AC78-87FEEBC79D49}">
+                <File Id="filFF7CBD4AFC5CD29D415B8CA842BD3953" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\CheckBox.qml" />
+            </Component>
+            <Component Id="cmp74653D62C0025B52EACC5CE4705BA4AC" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="{6C342AA9-109B-48D0-8FE7-87B76EDA99BB}">
+                <File Id="fil2F5AF98342429458AD63810BB297BE8F" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\CheckDelegate.qml" />
+            </Component>
+            <Component Id="cmp82CBBB6E4192B7021A218112BCB5EABA" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="{629C4666-AC8A-4934-9A70-60E040A19879}">
+                <File Id="filB6681E9463BADDB6ADD9558C3C817457" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\ComboBox.qml" />
+            </Component>
+            <Component Id="cmpB7B372C2E3988CDD1F9809917CE52585" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="{10D5BEC1-ABA5-4A30-9076-C1815EFBA827}">
+                <File Id="fil749DFC3226F07A88D6102926D0536AD5" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\DelayButton.qml" />
+            </Component>
+            <Component Id="cmp24F6257663361F90EF52CA9A4F658D44" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="{1D252B25-F043-4B4E-8E77-1BB629BBDF93}">
+                <File Id="fil40243C01A353CA3D602F8100942F2B4A" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\Dial.qml" />
+            </Component>
+            <Component Id="cmp3614FDEBFF9DE873AF9B0CB586840E34" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="{8410C199-6911-4AFD-B305-DAF51DF92FB0}">
+                <File Id="filAF75807656F449DF60B3006684E77B9D" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\Dialog.qml" />
+            </Component>
+            <Component Id="cmp52DEE6CB79C4277264AC4090D5E2531C" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="{C2237F22-8603-46EB-A9CC-05A41F43D7DB}">
+                <File Id="fil4A391652834C19895C58027631C47055" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\DialogButtonBox.qml" />
+            </Component>
+            <Component Id="cmpA83413D9000B4AF5C07C05A3CF1108A9" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="{A47F1F47-88D6-4269-9A7F-C02514E48DC8}">
+                <File Id="fil7CE5827EEA12748D5B79208226FD2FC3" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\Drawer.qml" />
+            </Component>
+            <Component Id="cmpAFF05B29E306BDDF612E08480F111B94" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="{7E1BB6E8-59A7-4FA4-A219-9067662D8502}">
+                <File Id="filE319DC1ADC5570BB3EFA6B8C96B9ADB2" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\Frame.qml" />
+            </Component>
+            <Component Id="cmp07EFE87D2155105B9BADFAF62AD12DE7" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="{A0E280A5-CD9C-46FE-AAA8-CCD6189AAC7D}">
+                <File Id="fil6238E69A3FF45BAED0A93FF54D64C608" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\GroupBox.qml" />
+            </Component>
+            <Component Id="cmp63D09EE503A89DFB92B4E05D8D74882D" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="{7D43FF84-F7E5-4976-B5B1-E158D558949F}">
+                <File Id="fil8315853BEBD2B22F2D2FA1421BFDB11D" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\HorizontalHeaderView.qml" />
+            </Component>
+            <Component Id="cmp69655929CAB1E209527EA04B131BAF08" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="{0EF0F839-7593-4A48-AB4C-5AC1100D72DC}">
+                <File Id="fil13A04CE8C24A5E3A3F4951AFD6BC646E" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\ItemDelegate.qml" />
+            </Component>
+            <Component Id="cmpD0906DBB6FC23D842FE548E71D4FD4F2" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="{4230190B-C18A-4140-95AD-0057AC3B6090}">
+                <File Id="filE87D7D52B86C4561BB4BBD0B0BE17872" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\Label.qml" />
+            </Component>
+            <Component Id="cmp314F7C86D57FDF1E0A16D4D98E2D9567" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="{06E7100D-5788-4347-A015-0E2EE506BC3D}">
+                <File Id="filC51B15821AAFCB45308245AD9F65241E" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\Menu.qml" />
+            </Component>
+            <Component Id="cmp8A717A9077FC79285557357EB3E6306E" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="{3335C3A8-F5BB-4375-BB14-E7D8E9E6705D}">
+                <File Id="fil6AAEB2864E9962A27AFF6EB36AA78047" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\MenuBar.qml" />
+            </Component>
+            <Component Id="cmpF60FE6F301CE8A6103133FB10FCADF93" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="{8E2F0D8A-2DBB-4C5F-8BAA-38F7F9A6FCE2}">
+                <File Id="filBE4024BA28E10561875CA50061DEF7A4" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\MenuBarItem.qml" />
+            </Component>
+            <Component Id="cmp45AF55E12BC0056BF2CBA087DE3A15C1" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="{F951505B-B4BC-49C4-A644-62D40BEA1C41}">
+                <File Id="filD3D9490012EDC98F5745045B46E1B05B" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\MenuItem.qml" />
+            </Component>
+            <Component Id="cmp87EA1031250A82B1A41C269330D64DF2" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="{AA0CB117-D517-4E75-A49F-34C7895D7846}">
+                <File Id="filC4AD17C140F4833AFB47B45E9A244A59" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\MenuSeparator.qml" />
+            </Component>
+            <Component Id="cmp0647FD315BFF5036E9CA6E851E77571E" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="{4B2DB474-2A7A-4BA1-AB7A-9C7B06B7EFE7}">
+                <File Id="filED2FA4F6482AD59889D955531A2D8796" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\Page.qml" />
+            </Component>
+            <Component Id="cmpF7497FA50C72610AA9142D074D791C59" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="{8A651561-6FDD-49CD-8E43-7FA2E2C2318F}">
+                <File Id="fil2BB9C91E2C1EB4411E3DD5127EF2B7D4" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\PageIndicator.qml" />
+            </Component>
+            <Component Id="cmpB53227D234784EE80FC0B55813AD12E3" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="{61EC16BC-7E89-4C54-A667-C65974BA3CFE}">
+                <File Id="filF9F1163D16E5DD3C931976CF0D21D49B" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\Pane.qml" />
+            </Component>
+            <Component Id="cmpA871A6B27440759B744A6657386532DA" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="{AE9597EF-429D-47AB-86F4-E96314AB3608}">
+                <File Id="fil944E0533221778E4A86FC800C6EB0868" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\plugins.qmltypes" />
+            </Component>
+            <Component Id="cmp366792226A445DC856A9C8F90A7E9357" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="{9C91136D-F009-4810-B9AB-EDD455D0876E}">
+                <File Id="filAEB9611E73CA47FE708B5BAE2383AA7F" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\Popup.qml" />
+            </Component>
+            <Component Id="cmpAF803553492E1968BD6573328578177B" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="{D7A195AF-4F58-49F2-A5A0-8D788AA8EC79}">
+                <File Id="fil0460E95677EBC7E9D99F075593F8AB4F" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\ProgressBar.qml" />
+            </Component>
+            <Component Id="cmp51B77E664C5381F7D9825079760DAAB1" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="{B4B441C7-7CED-4004-B193-EF794678C19B}">
+                <File Id="filB9C70D68EF80B50079A5D2A205897696" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\qmldir" />
+            </Component>
+            <Component Id="cmp9087E21C348FF3ADF9E819DFC8F4CEB6" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="{50902854-6550-4CFF-9C60-764854FF251D}">
+                <File Id="filC09D64083B771A951AC13BC28F6BB594" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\qtquickcontrols2universalstyleplugin.dll" />
+            </Component>
+            <Component Id="cmp931D54FC538FDE70C9FF8C58C7FE8384" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="{B7853443-FA63-4071-B3F7-34D6A9F512A2}">
+                <File Id="filC6F54E9404B2D08089A2FA24F1B5D74B" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\RadioButton.qml" />
+            </Component>
+            <Component Id="cmpD42BB5B3F23454FBAFA6FB26A89980BA" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="{3BE12163-002B-4493-AB01-778F666F9D16}">
+                <File Id="fil00F4C559972EA6F8F3E4D84E3898DA0E" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\RadioDelegate.qml" />
+            </Component>
+            <Component Id="cmp4C747396BCBDB0519CE6284BEED0FE7E" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="{335FEFAF-D15E-4EDB-B9CC-CCFD8B7F0D22}">
+                <File Id="fil35A0D2497D80784A5708FFFED620C565" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\RangeSlider.qml" />
+            </Component>
+            <Component Id="cmp2B01E8CC8E48574980219C4F51C088C7" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="{E9AA2267-FD7A-4BC2-A637-87FF784FB962}">
+                <File Id="fil58B9612E0F9621875C71B5CD402542D2" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\RoundButton.qml" />
+            </Component>
+            <Component Id="cmp8212CD0C96EAB4C613A761E754011D70" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="{B92B9F69-78E8-49A7-83CD-6B480785379A}">
+                <File Id="fil2001B522BF0953FBEDAF186CB387907C" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\ScrollBar.qml" />
+            </Component>
+            <Component Id="cmpD7CA60E6EDE60D5C9AEB3414A198CDC1" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="{E87419F6-73B2-4873-AF05-33BA390EC07F}">
+                <File Id="fil1309AAA5E9FBAAC455ACEDB0DD6BE528" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\ScrollIndicator.qml" />
+            </Component>
+            <Component Id="cmpE0B957E426C7F6077568110AA5364520" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="{D23DDC90-2991-4620-800C-42B4F2333359}">
+                <File Id="fil59C85F1D86A5D26C018D9485DBC2F8C8" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\ScrollView.qml" />
+            </Component>
+            <Component Id="cmp63EB5E41D4F0BEB3B9F57F6FDE2BF818" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="{392364E2-D670-4DAD-82C2-163EB8ACD9DF}">
+                <File Id="fil5D3938CE0A0C1A757669B8F0C784AD0D" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\SelectionRectangle.qml" />
+            </Component>
+            <Component Id="cmpF37676A5F0931B20CFF0D58B6C7F2F99" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="{4D690D2A-AB27-461B-9E0D-ED0194E82C3E}">
+                <File Id="fil053C2F4943464D8367F8F708F6CD2522" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\Slider.qml" />
+            </Component>
+            <Component Id="cmp823062B1DF2BD46ABC0C991611360E70" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="{FD5E3DBB-4BE9-4C1C-B1A7-E2E1173C6E2E}">
+                <File Id="fil442E543AA50E98A0D8CC13797B60A0CB" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\SpinBox.qml" />
+            </Component>
+            <Component Id="cmp60704BAFE2CEE50EBEE1DD76F2C94BD6" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="{5EB50370-E542-4664-9DAB-CA2E9E2063ED}">
+                <File Id="filD46EBE0768CFBE0A2C922ECB1A5F8710" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\SplitView.qml" />
+            </Component>
+            <Component Id="cmp08B8BF38C3777507DCD07252C30A2EED" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="{96C9925B-1130-4C6E-A4D1-38F038C96CDB}">
+                <File Id="filC63E50933072DDA2A2F81AEA313AF5B7" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\StackView.qml" />
+            </Component>
+            <Component Id="cmp5DF60F5D23994DBDC45EE597DAE82C98" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="{9C567408-5F28-4A18-B116-2FAAEC4F8B50}">
+                <File Id="filE8EA5E4B4467A7D21DDD94FB0E8087D8" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\SwipeDelegate.qml" />
+            </Component>
+            <Component Id="cmp1126502F28518CB73077CAE5B99E0084" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="{A1B40D74-DF56-46B5-9650-CE61E8AD855C}">
+                <File Id="fil61546DED3B721A83958E1250B4101229" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\Switch.qml" />
+            </Component>
+            <Component Id="cmp1E2D3D098C0C3EDEF52EFDB9E11A68D5" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="{70CCEAF2-5AC9-402D-8126-F768ECC1BF21}">
+                <File Id="fil6079EB46A6FA930D4CC18C0A09D93286" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\SwitchDelegate.qml" />
+            </Component>
+            <Component Id="cmp0786D85C308613496238A9CB6FB5E626" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="{2568AFEA-E8C2-4886-B4C9-09765B5D88CB}">
+                <File Id="fil0524F140118812BB5021429D3B8F844D" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\TabBar.qml" />
+            </Component>
+            <Component Id="cmp04B304EE75DB9C2FE23AB462D633CA65" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="{5CCB6126-2E05-4B56-A06A-83F72AF5B8BB}">
+                <File Id="filDC7507CF41EE0CEC36C58379F9931C05" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\TabButton.qml" />
+            </Component>
+            <Component Id="cmpCAFA00528B35D03089962912927E2DA1" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="{1BCA5E21-2553-41EC-9530-D972A8E6748D}">
+                <File Id="filCC3CE611A1257C01753A25B0B37F6B54" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\TextArea.qml" />
+            </Component>
+            <Component Id="cmp644523A4EA82DAB1ACAA7E966D92D48C" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="{71C170B1-3F00-4A8F-A4C1-F97598483F3E}">
+                <File Id="fil1B2AEB5E69D93467FE25800346697D31" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\TextField.qml" />
+            </Component>
+            <Component Id="cmp26639D51A08FA4BAF5F80E49C171E45F" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="{32218424-2787-433C-B8A8-B063DCFDC34C}">
+                <File Id="filE4BBB3467656627DF714EC07193C5449" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\ToolBar.qml" />
+            </Component>
+            <Component Id="cmp3F6EDAA6430EC43721B35CA18D3BA78F" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="{9F3B269E-C8FF-41F8-A172-80859B8DCEB8}">
+                <File Id="fil4BFBEF1AE43AD4AA92C7416190678A9D" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\ToolButton.qml" />
+            </Component>
+            <Component Id="cmp77F56C58CA7D3A2AE058A692FA0DB0FA" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="{4749B517-1F25-4DA7-A489-B3C2D122E392}">
+                <File Id="filD95FC4F33B80E4425E3F655CBE9DD423" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\ToolSeparator.qml" />
+            </Component>
+            <Component Id="cmp4293429955B7E41962EDA87AAAC1A864" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="{FFA24F08-4F9F-4946-A39C-38ACA252E3A9}">
+                <File Id="fil367938606BD72BFD59CC703368AB5ADC" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\ToolTip.qml" />
+            </Component>
+            <Component Id="cmpECF2EB8A7C8F610B440E0DB95DFB1C34" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="{41FFB0F9-A29A-44E9-BBBC-475B998F41A7}">
+                <File Id="fil88D922FE8AA17DB7E9DFAE0C5AA9C02E" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\Tumbler.qml" />
+            </Component>
+            <Component Id="cmpE2432E964D455B2F8020CEE8E70045BF" Directory="dir124EAE5849AC3D2D22C86A18E3063919" Guid="{A3E6E31A-2B98-4DAD-9993-ECCBDA48FF2D}">
+                <File Id="filF5E98B65CEE286B98532681C3B212115" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\VerticalHeaderView.qml" />
+            </Component>
+            <Component Id="cmp3C7BCDA16EBE7C7234300679974E5AF8" Directory="dir40011B386F4B1F0F2AE25C2E3ADB18E8" Guid="{C97C3EAA-039E-4277-B809-779227F214DE}">
+                <File Id="fil5E3D7806E4ACE06AE8206169AE47D35E" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\impl\CheckIndicator.qml" />
+            </Component>
+            <Component Id="cmp839F94E8CF3D4B7CF0DD1F309B8EF06C" Directory="dir40011B386F4B1F0F2AE25C2E3ADB18E8" Guid="{F4D06829-BBB6-4F6A-A650-3C944A02DF1E}">
+                <File Id="filDA4416BDF11B09021A1326B00223FB37" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\impl\plugins.qmltypes" />
+            </Component>
+            <Component Id="cmpD1CF68B645D21C100068C4EF660BDC0F" Directory="dir40011B386F4B1F0F2AE25C2E3ADB18E8" Guid="{032C34E8-E9D2-48E4-855D-A4B444376751}">
+                <File Id="filB125192383A639A7758297A7C4B197E6" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\impl\qmldir" />
+            </Component>
+            <Component Id="cmp2AEEE092AE10D40D28E4AC098E540740" Directory="dir40011B386F4B1F0F2AE25C2E3ADB18E8" Guid="{882A1D73-D297-4E16-A0D3-232DFE05DDB1}">
+                <File Id="fil4E63F58A1B36B63D317111EBE3B48AA6" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\impl\qtquickcontrols2universalstyleimplplugin.dll" />
+            </Component>
+            <Component Id="cmpFA802CBD27EC3716FCB763360C00ED06" Directory="dir40011B386F4B1F0F2AE25C2E3ADB18E8" Guid="{BB548922-135B-43CD-A99B-64D562A427D3}">
+                <File Id="filD0FB5A202A8DF4C9B739C4930248B14D" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\impl\RadioIndicator.qml" />
+            </Component>
+            <Component Id="cmp169AF5E7D2D401073A9E07AB61EC6A17" Directory="dir40011B386F4B1F0F2AE25C2E3ADB18E8" Guid="{EB8C448D-D00C-486A-8C3E-51DEB8676742}">
+                <File Id="fil58A590E9CE55A76BCC66D2BED9287B7C" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Universal\impl\SwitchIndicator.qml" />
+            </Component>
+            <Component Id="cmpB3BDFF757AD79A1F0F732B4E9266F68F" Directory="dir5BF786D89AC746B54F9B967AA07926B8" Guid="{E283F744-EE2B-40D0-8F90-7C7E9643577E}">
+                <File Id="fil9FEC5118E51E3BF277C47160386C5982" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Windows\Button.qml" />
+            </Component>
+            <Component Id="cmpF4A88E9F2EA021D2F2A53064773F9BB4" Directory="dir5BF786D89AC746B54F9B967AA07926B8" Guid="{B09BCB26-D736-4DE2-9B11-3734F48F8250}">
+                <File Id="filBD3EBDDB47E26B6E7EC6932DF5C67EFF" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Windows\CheckBox.qml" />
+            </Component>
+            <Component Id="cmpDC75B8A533B90F0242BA2101EAD7FD28" Directory="dir5BF786D89AC746B54F9B967AA07926B8" Guid="{6F2D78DA-C5D7-48EF-A0DC-BAE4EB65CB14}">
+                <File Id="fil3CCF4F05FDBC45FD8DEACA92B13CDB86" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Windows\ComboBox.qml" />
+            </Component>
+            <Component Id="cmpA976CCF99401B90F612B7CC48326B83D" Directory="dir5BF786D89AC746B54F9B967AA07926B8" Guid="{2E120F60-5B83-4778-8776-9492E873588D}">
+                <File Id="filF863D2D56FBB28CB9CEB338B060AD016" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Windows\Frame.qml" />
+            </Component>
+            <Component Id="cmp49EB228C54CE552ED0E58608D46917CF" Directory="dir5BF786D89AC746B54F9B967AA07926B8" Guid="{4D7EABE2-2FF9-463F-A7CF-A81553042C06}">
+                <File Id="fil439BD2BAEBB9F9F7BE95217EF772C924" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Windows\GroupBox.qml" />
+            </Component>
+            <Component Id="cmp97952AFE76A2C2EF1F031E7D877F76D4" Directory="dir5BF786D89AC746B54F9B967AA07926B8" Guid="{563BF2EA-CDCC-4D99-ADA7-9C9949EA7422}">
+                <File Id="filF2B713C14D9A0A8790D19998F4CB2FC9" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Windows\plugins.qmltypes" />
+            </Component>
+            <Component Id="cmpB76B4394B54997D1E96DAA67F0DE6665" Directory="dir5BF786D89AC746B54F9B967AA07926B8" Guid="{2D3E043F-5BDF-48E8-9739-F1592D88E9F8}">
+                <File Id="fil0DEE413D0B0D99E29AC79BBE51AF79C3" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Windows\ProgressBar.qml" />
+            </Component>
+            <Component Id="cmpC803B18B5B045E6CF331B778BADCD578" Directory="dir5BF786D89AC746B54F9B967AA07926B8" Guid="{ACB4D9C8-7F95-4EC7-ADD4-D255E220F2CC}">
+                <File Id="filA74A9299B5D6C22A7737DC48C3A92ACA" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Windows\qmldir" />
+            </Component>
+            <Component Id="cmp3E7086A1482D5FF87E271201932317A5" Directory="dir5BF786D89AC746B54F9B967AA07926B8" Guid="{E3E6FB8E-5F92-47EA-A0A2-33498B6B8645}">
+                <File Id="fil019F2B846E62B5B91A295E8419FF2355" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Windows\qtquickcontrols2windowsstyleplugin.dll" />
+            </Component>
+            <Component Id="cmpD286EF4971EE3CBC3CD0A133F2D1A670" Directory="dir5BF786D89AC746B54F9B967AA07926B8" Guid="{C52007FF-A9A8-41FD-AF53-71014B5FB295}">
+                <File Id="filC5CA9BA0552F55E13DC8426A9DF08496" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Windows\RadioButton.qml" />
+            </Component>
+            <Component Id="cmpF327A2339C7A11964AABF6B9F652FF03" Directory="dir5BF786D89AC746B54F9B967AA07926B8" Guid="{E7E205A2-55F5-44F6-B19C-88D19414EB70}">
+                <File Id="filCB25715DDB15881A1F71502EBD323089" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Windows\ScrollBar.qml" />
+            </Component>
+            <Component Id="cmp2C1245C0A84A37B8C23B42FB44963758" Directory="dir5BF786D89AC746B54F9B967AA07926B8" Guid="{21AA493A-CDD4-4B34-8504-894FC9311436}">
+                <File Id="fil7E2F08DBD14FEA3964524C99658C1B5A" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Windows\ScrollView.qml" />
+            </Component>
+            <Component Id="cmp9133069D287F16E19B35C5C7B6044E3A" Directory="dir5BF786D89AC746B54F9B967AA07926B8" Guid="{26621BE8-338C-4160-A589-738627F193EE}">
+                <File Id="filD5421510C4C3CE49250C746303BFB31C" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Windows\SelectionRectangle.qml" />
+            </Component>
+            <Component Id="cmpEE7D4B815DD804200D22F38ABCD27A9E" Directory="dir5BF786D89AC746B54F9B967AA07926B8" Guid="{F24C9091-ABCF-4580-87D5-7E6F30314285}">
+                <File Id="fil890479C0BE4FBEB626D203A333EC6F30" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Windows\Slider.qml" />
+            </Component>
+            <Component Id="cmpB70AB13785BADC070517E87F1A8AD8E8" Directory="dir5BF786D89AC746B54F9B967AA07926B8" Guid="{34459F00-108A-48CD-9CA6-99493ECFDB33}">
+                <File Id="filCDB92D2CE105B4A7A04435787DA9836B" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Windows\SpinBox.qml" />
+            </Component>
+            <Component Id="cmp46BE888FD0CF137BEE5FDD9E7CE0AFB5" Directory="dir5BF786D89AC746B54F9B967AA07926B8" Guid="{BE9A5E08-F960-4E0E-9D74-717101739745}">
+                <File Id="fil3E89DE0E7D5951AA3682EA072150DC10" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Windows\TextArea.qml" />
+            </Component>
+            <Component Id="cmp3A6FC96D5C6489B596B47615A6CB9C09" Directory="dir5BF786D89AC746B54F9B967AA07926B8" Guid="{2BD3CAEF-7A77-4E00-AA8C-C2DB8377BF4F}">
+                <File Id="fil88212E2E7362494501102B5760BEA7FA" KeyPath="yes" Source="SourceDir\QtQuick\Controls\Windows\TextField.qml" />
+            </Component>
+            <Component Id="cmp6BB722D488CF9C0E5CF4251DC77DE110" Directory="dirC14EEB8602D1812B8846FA3737C83C55" Guid="{9C12FD5D-4F49-494F-A9A9-561D4F3ED457}">
+                <File Id="filE51FD3248165EF5A8434CD4F9DD668BA" KeyPath="yes" Source="SourceDir\QtQuick\Dialogs\plugins.qmltypes" />
+            </Component>
+            <Component Id="cmp741F3432BD35B45A41FDB40E304BEB0A" Directory="dirC14EEB8602D1812B8846FA3737C83C55" Guid="{7604082E-4ADC-45B5-A438-9AA6D8A8E5FD}">
+                <File Id="fil4529CD80F93002A4D807D810DD8169E0" KeyPath="yes" Source="SourceDir\QtQuick\Dialogs\qmldir" />
+            </Component>
+            <Component Id="cmp6C43EF3E8388BB2C7D46C5784E380B52" Directory="dirC14EEB8602D1812B8846FA3737C83C55" Guid="{EAC18143-52B7-4D69-8A96-3934156E2BDE}">
+                <File Id="fil79C6181D3E8854EA573E7D82C562C3C1" KeyPath="yes" Source="SourceDir\QtQuick\Dialogs\qtquickdialogsplugin.dll" />
+            </Component>
+            <Component Id="cmp08BEB980E2729DAFADE97B8634BDE406" Directory="dir9B4F25D95E0FA7C4E83388EAF889E777" Guid="{9F1F96EA-0023-4BDF-8773-1BED8B99DDF2}">
+                <File Id="filB2C2D7BF0A3A15DDABDB696B15032171" KeyPath="yes" Source="SourceDir\QtQuick\Dialogs\quickimpl\plugins.qmltypes" />
+            </Component>
+            <Component Id="cmp0DA58AD9E8779979F94EDA7686430734" Directory="dir9B4F25D95E0FA7C4E83388EAF889E777" Guid="{D63FE718-5D71-4D7B-93DC-C0514B084B42}">
+                <File Id="filA6C426D24C8B4B69ACE0064DBBB1AB5A" KeyPath="yes" Source="SourceDir\QtQuick\Dialogs\quickimpl\qmldir" />
+            </Component>
+            <Component Id="cmp53A16983A6B65DBA056D22C754BEFFBB" Directory="dir9B4F25D95E0FA7C4E83388EAF889E777" Guid="{674893B5-9E0B-4E40-9C2B-B829F1CF927D}">
+                <File Id="fil1A12DB1F35EA5264494B5034BE0405EC" KeyPath="yes" Source="SourceDir\QtQuick\Dialogs\quickimpl\qtquickdialogs2quickimplplugin.dll" />
+            </Component>
+            <Component Id="cmpBD0006BEDCE6A52F75A9E4796534B5FC" Directory="dir5A065D859888E6B36CD8ABD4BF35B615" Guid="{3F763B3C-A4D0-43BD-811F-970776054532}">
+                <File Id="filD87CA1491CB279F613BE404EE77EF708" KeyPath="yes" Source="SourceDir\QtQuick\Dialogs\quickimpl\qml\FileDialog.qml" />
+            </Component>
+            <Component Id="cmp9AE86FECBB97E284FFC5E155EF6210B8" Directory="dir5A065D859888E6B36CD8ABD4BF35B615" Guid="{905DEFA9-8A91-4DB2-91CA-87D73BC2B3A0}">
+                <File Id="fil120FB34D88453E18D0B53BB5354F5CFB" KeyPath="yes" Source="SourceDir\QtQuick\Dialogs\quickimpl\qml\FileDialogDelegate.qml" />
+            </Component>
+            <Component Id="cmp2E8FF0E78E8FAB1186D43A9AC6FEB60F" Directory="dir5A065D859888E6B36CD8ABD4BF35B615" Guid="{144529DC-BCEB-46E2-ACA1-1A6E14B7B7D5}">
+                <File Id="fil280CF0D59F5E8F4DD0F9ED1F7AFE91F6" KeyPath="yes" Source="SourceDir\QtQuick\Dialogs\quickimpl\qml\FileDialogDelegateLabel.qml" />
+            </Component>
+            <Component Id="cmp7F0E1A3957D9D6487496539F39E648B9" Directory="dir5A065D859888E6B36CD8ABD4BF35B615" Guid="{DDE67B63-74A6-4E0F-97F1-009A98A8972C}">
+                <File Id="fil7FFAD14DE995883CF5F474CB8BC600D5" KeyPath="yes" Source="SourceDir\QtQuick\Dialogs\quickimpl\qml\FolderBreadcrumbBar.qml" />
+            </Component>
+            <Component Id="cmpC570F1BF81B811749E7D778F3E9A4E0C" Directory="dir5A065D859888E6B36CD8ABD4BF35B615" Guid="{25CB2A85-0263-403B-90E8-806BB097E58D}">
+                <File Id="fil1B6BF8D81E67AFA93D781B1B2A06881F" KeyPath="yes" Source="SourceDir\QtQuick\Dialogs\quickimpl\qml\FontDialog.qml" />
+            </Component>
+            <Component Id="cmp3C1322FAAC25FB323D8A9F079BF7B300" Directory="dir5A065D859888E6B36CD8ABD4BF35B615" Guid="{1A4B5F42-941F-48C4-A491-62C5BE39F2C1}">
+                <File Id="fil5EFF7CAD771E72A6DCEEA0A713AAE1F2" KeyPath="yes" Source="SourceDir\QtQuick\Dialogs\quickimpl\qml\FontDialogContent.qml" />
+            </Component>
+            <Component Id="cmp4B491056D2B8FF4E74CE7253D57BC33F" Directory="dir7931425F6E509D7B972EE970C5C8EE72" Guid="{705FBB22-E6BB-41E6-AB5B-081F8A4EF12D}">
+                <File Id="fil399F52BE93EDAD8FF04BA019C008C59D" KeyPath="yes" Source="SourceDir\QtQuick\Dialogs\quickimpl\qml\+Fusion\FileDialog.qml" />
+            </Component>
+            <Component Id="cmp7C203A2B1798FF9860772C53A0B3CA4A" Directory="dir7931425F6E509D7B972EE970C5C8EE72" Guid="{EA312CA2-2BA8-4DBA-9794-B29460EE05A9}">
+                <File Id="fil29A8E7EFCBEAA78565EA3B5133BBDD12" KeyPath="yes" Source="SourceDir\QtQuick\Dialogs\quickimpl\qml\+Fusion\FileDialogDelegate.qml" />
+            </Component>
+            <Component Id="cmpED32AA987458F362954681E246C0D0EC" Directory="dir7931425F6E509D7B972EE970C5C8EE72" Guid="{C2C1BB59-6750-42E8-80D3-8C55DC67702E}">
+                <File Id="filED3B4AD4B72B48D693576B7C52862B22" KeyPath="yes" Source="SourceDir\QtQuick\Dialogs\quickimpl\qml\+Fusion\FolderBreadcrumbBar.qml" />
+            </Component>
+            <Component Id="cmp2F197AFD32EA1AEEFB2F5A6D9C074523" Directory="dir7931425F6E509D7B972EE970C5C8EE72" Guid="{A790BE76-929C-4736-A758-7B71D6DCC296}">
+                <File Id="filBDC89A40670D4D68D90F9C93EA32B4E2" KeyPath="yes" Source="SourceDir\QtQuick\Dialogs\quickimpl\qml\+Fusion\FontDialog.qml" />
+            </Component>
+            <Component Id="cmp063C49C671D9A253EB1C81EECAE960CC" Directory="dir54FC92B404B993A4BC39A98D15582E79" Guid="{DDD2BC70-4D26-4967-A5D1-FDF5EF5BDB17}">
+                <File Id="filA1FCE90F83293E73EB0C021DCA479BCE" KeyPath="yes" Source="SourceDir\QtQuick\Dialogs\quickimpl\qml\+Imagine\FileDialog.qml" />
+            </Component>
+            <Component Id="cmpDEBAA555D9500981A1E5BC6A3F766FAE" Directory="dir54FC92B404B993A4BC39A98D15582E79" Guid="{8E50F467-0C61-41EE-9B20-F8FAE515B86F}">
+                <File Id="filC18830D02EF0F5324823EDD4CCEE88B6" KeyPath="yes" Source="SourceDir\QtQuick\Dialogs\quickimpl\qml\+Imagine\FileDialogDelegate.qml" />
+            </Component>
+            <Component Id="cmp0AE1282EF97A00BC559C00D09A9AFB47" Directory="dir54FC92B404B993A4BC39A98D15582E79" Guid="{8E7563F5-162A-4E43-B9E0-424A201024D6}">
+                <File Id="fil836D153CED1BEBAD0B0D83D5BEA08616" KeyPath="yes" Source="SourceDir\QtQuick\Dialogs\quickimpl\qml\+Imagine\FolderBreadcrumbBar.qml" />
+            </Component>
+            <Component Id="cmp5CF0B28B79CBF8AA0A96A12D8314909C" Directory="dir54FC92B404B993A4BC39A98D15582E79" Guid="{12C7DC4E-DD7B-446A-AC35-1EB64D935A41}">
+                <File Id="fil0A699C21404BA188D4C9521D4DC9C5D3" KeyPath="yes" Source="SourceDir\QtQuick\Dialogs\quickimpl\qml\+Imagine\FontDialog.qml" />
+            </Component>
+            <Component Id="cmp822E598C562C1400AA10AD5699DCE0CF" Directory="dir8024AEF9D92D06355510B5D11BC9CB61" Guid="{43E5B74C-72ED-4451-87EF-B23D738DC5EE}">
+                <File Id="filD6DB2DA899D51B7BA0777511935DEFB6" KeyPath="yes" Source="SourceDir\QtQuick\Dialogs\quickimpl\qml\+Material\FileDialog.qml" />
+            </Component>
+            <Component Id="cmpBE7DBAD3FFBDD0A6FEE3E4D7BB2E4500" Directory="dir8024AEF9D92D06355510B5D11BC9CB61" Guid="{0C12F85C-AB38-492D-859D-C8B304B6E740}">
+                <File Id="fil395D91707A007382A0E41BE3563DCA33" KeyPath="yes" Source="SourceDir\QtQuick\Dialogs\quickimpl\qml\+Material\FileDialogDelegate.qml" />
+            </Component>
+            <Component Id="cmp6A44D3919ACBB35BA724407199F5B232" Directory="dir8024AEF9D92D06355510B5D11BC9CB61" Guid="{F23015EC-2D71-4490-BEFB-5822C7CD01B9}">
+                <File Id="filB3750138093FB6985EB2A7393E268CB8" KeyPath="yes" Source="SourceDir\QtQuick\Dialogs\quickimpl\qml\+Material\FolderBreadcrumbBar.qml" />
+            </Component>
+            <Component Id="cmpEEBEBDE0A6706B05437353FFC06577A8" Directory="dir8024AEF9D92D06355510B5D11BC9CB61" Guid="{6A78DA96-59B1-494A-BBC7-96ED3AAFA484}">
+                <File Id="filF4558ACD5D94AFE5C935A6B6B5DCB82A" KeyPath="yes" Source="SourceDir\QtQuick\Dialogs\quickimpl\qml\+Material\FontDialog.qml" />
+            </Component>
+            <Component Id="cmp7EDB6F95D746AA1CFADC8F03E3F90489" Directory="dir2FA1B14588FEA7C153D23281391CE63E" Guid="{AAA3027D-782C-4CC3-88A2-BFE733038CF9}">
+                <File Id="fil825AFFD5957EDB64A959DD0561FB635B" KeyPath="yes" Source="SourceDir\QtQuick\Dialogs\quickimpl\qml\+Universal\FileDialog.qml" />
+            </Component>
+            <Component Id="cmpF76AE841E954D7B21051ED16A4442EDF" Directory="dir2FA1B14588FEA7C153D23281391CE63E" Guid="{9FAD140C-F87E-4EE9-BC9D-1EB1826F683E}">
+                <File Id="filFAB12041E1B4B051DCDDE4EF80551291" KeyPath="yes" Source="SourceDir\QtQuick\Dialogs\quickimpl\qml\+Universal\FileDialogDelegate.qml" />
+            </Component>
+            <Component Id="cmp3A138D8A41289BF4F2F43748D9258045" Directory="dir2FA1B14588FEA7C153D23281391CE63E" Guid="{200577EE-FCE7-4D5F-9A10-9D4EFF5D2EF2}">
+                <File Id="fil945A580A5D4D5F5315CC92EC7511A62C" KeyPath="yes" Source="SourceDir\QtQuick\Dialogs\quickimpl\qml\+Universal\FolderBreadcrumbBar.qml" />
+            </Component>
+            <Component Id="cmpABE134A2EBFEE34A2C96F49CE87230B9" Directory="dir2FA1B14588FEA7C153D23281391CE63E" Guid="{613EBB5D-77E6-4A82-A9E8-A6BD7F015D38}">
+                <File Id="fil365E4AB154888FE45E7A5ADE8E4A329A" KeyPath="yes" Source="SourceDir\QtQuick\Dialogs\quickimpl\qml\+Universal\FontDialog.qml" />
+            </Component>
+            <Component Id="cmp42D10FA963CC0F5787806B3D22751506" Directory="dir25E537F6AD7990426F898BAE24EACE9D" Guid="{300C9EF3-8E59-49A2-9683-067ED4A4F564}">
+                <File Id="fil91D796915632A8DABC35AB5C05CAC4CA" KeyPath="yes" Source="SourceDir\QtQuick\Layouts\plugins.qmltypes" />
+            </Component>
+            <Component Id="cmp49DE331F7ECBFD4B6496E456E9202BBD" Directory="dir25E537F6AD7990426F898BAE24EACE9D" Guid="{7D3D6C2C-A08F-4FC5-8831-38B10224AEF9}">
+                <File Id="fil949FA2140AB477002A7281B6460D11B3" KeyPath="yes" Source="SourceDir\QtQuick\Layouts\qmldir" />
+            </Component>
+            <Component Id="cmpAE9DE0B96DBCA94B2F3EBF16BB3A94DA" Directory="dir25E537F6AD7990426F898BAE24EACE9D" Guid="{C8FB8466-3202-4E1F-B1E0-7E64DCA58755}">
+                <File Id="fil8505A4EEC74818F1E593A5170BAE3B9F" KeyPath="yes" Source="SourceDir\QtQuick\Layouts\qquicklayoutsplugin.dll" />
+            </Component>
+            <Component Id="cmp894EA37B6C07C1AFF2FF7385D96F4136" Directory="dirD2281A4769E4849962EB43101484B62C" Guid="{B002F224-15E9-4478-BD0E-C0FA014EAFD5}">
+                <File Id="filFEDFF984EDB2DA8EBB2F157D10C7AC86" KeyPath="yes" Source="SourceDir\QtQuick\LocalStorage\plugins.qmltypes" />
+            </Component>
+            <Component Id="cmp71902BDE985B8A3898433CF2FE8497AA" Directory="dirD2281A4769E4849962EB43101484B62C" Guid="{D588DBF5-7084-43CA-9DA4-A38D81613331}">
+                <File Id="filE2E12F2D3ABE11E9341038B6A4CFF6CF" KeyPath="yes" Source="SourceDir\QtQuick\LocalStorage\qmldir" />
+            </Component>
+            <Component Id="cmp412B93C6A674A57E344E4C06CC523E63" Directory="dirD2281A4769E4849962EB43101484B62C" Guid="{B79D7BDC-A2A8-4BD5-A7E1-6018972A5E0A}">
+                <File Id="filB65FCEE4A204A52B152F506E3475A7A9" KeyPath="yes" Source="SourceDir\QtQuick\LocalStorage\qmllocalstorageplugin.dll" />
+            </Component>
+            <Component Id="cmpCC37388AE8E9C4CC65133A3BFF1F3471" Directory="dir005903123E8E65ADACEE188EEB8D5FED" Guid="{23DE5995-22C6-4884-B505-B39C6667E615}">
+                <File Id="fil78C9F149A0F4F42EF89E5FA3C65CDD90" KeyPath="yes" Source="SourceDir\QtQuick\NativeStyle\plugins.qmltypes" />
+            </Component>
+            <Component Id="cmp47B299C26C816A390C90115A477DF243" Directory="dir005903123E8E65ADACEE188EEB8D5FED" Guid="{0F248716-31B0-43C1-BE60-6B6386E135B3}">
+                <File Id="fil048C4F21A9707861CC1FC3CB9D44FE06" KeyPath="yes" Source="SourceDir\QtQuick\NativeStyle\qmldir" />
+            </Component>
+            <Component Id="cmp90A955C9BC5C4A52DBD5D702B208CD05" Directory="dir005903123E8E65ADACEE188EEB8D5FED" Guid="{466DE6D2-9CA1-4A1B-8457-2DE49C1C8927}">
+                <File Id="fil73C32516FEA683CA4184237DBA008AA0" KeyPath="yes" Source="SourceDir\QtQuick\NativeStyle\qtquickcontrols2nativestyleplugin.dll" />
+            </Component>
+            <Component Id="cmp4C91050C1BC606A0599930B8EDFFBB2B" Directory="dir58487975ACF4D38FB978C7C039C7F158" Guid="{D19ABA94-4293-4C98-9A3A-B21A4C0A544F}">
+                <File Id="fil1EC82E3920444D40F1C32A7127E6DC63" KeyPath="yes" Source="SourceDir\QtQuick\NativeStyle\controls\DefaultButton.qml" />
+            </Component>
+            <Component Id="cmp9BBE8F36F9F51D6FCDE56164634CFADB" Directory="dir58487975ACF4D38FB978C7C039C7F158" Guid="{25E54164-D58E-4A5E-BC8F-0181005DFD60}">
+                <File Id="fil4EAF4E9D2CEC4CCF5C53B8FAE5238AA7" KeyPath="yes" Source="SourceDir\QtQuick\NativeStyle\controls\DefaultCheckBox.qml" />
+            </Component>
+            <Component Id="cmpECD7B0876D12394C8220DB77945DD434" Directory="dir58487975ACF4D38FB978C7C039C7F158" Guid="{5692EFFB-7A06-46D2-89FB-A210E35B6669}">
+                <File Id="filC52706F4FDA6F45D673B949DA292AB0E" KeyPath="yes" Source="SourceDir\QtQuick\NativeStyle\controls\DefaultComboBox.qml" />
+            </Component>
+            <Component Id="cmpF1BD28E0623C9DE773CB2E937B6424EC" Directory="dir58487975ACF4D38FB978C7C039C7F158" Guid="{5AE1F3CC-61DF-41F6-AA83-649DB096A5CC}">
+                <File Id="filA7504C829C24C1DE33B93EC0E5C0E649" KeyPath="yes" Source="SourceDir\QtQuick\NativeStyle\controls\DefaultDial.qml" />
+            </Component>
+            <Component Id="cmp051842CF8FE3D1137616C232EE2D3575" Directory="dir58487975ACF4D38FB978C7C039C7F158" Guid="{3208442A-0F3B-4B30-814F-E48D845B9065}">
+                <File Id="fil9D21375511DC7630EB7CDAC73ECAB057" KeyPath="yes" Source="SourceDir\QtQuick\NativeStyle\controls\DefaultFrame.qml" />
+            </Component>
+            <Component Id="cmp342648DA2DA6885B1468429E36BE06EB" Directory="dir58487975ACF4D38FB978C7C039C7F158" Guid="{FC61EEB6-6A18-46E9-BE70-605303E03760}">
+                <File Id="filCA066B1D13BF7FA84151E79B06E7DE69" KeyPath="yes" Source="SourceDir\QtQuick\NativeStyle\controls\DefaultGroupBox.qml" />
+            </Component>
+            <Component Id="cmpFA1E15CE76C93BD272389D9C32F55B34" Directory="dir58487975ACF4D38FB978C7C039C7F158" Guid="{48058FB0-94D9-4111-8B0C-7D35642F9EA2}">
+                <File Id="fil4207683472EC834F413508BA9B05E615" KeyPath="yes" Source="SourceDir\QtQuick\NativeStyle\controls\DefaultProgressBar.qml" />
+            </Component>
+            <Component Id="cmp7FD57642930EF39FA4599BE4802C75EA" Directory="dir58487975ACF4D38FB978C7C039C7F158" Guid="{05BA8E31-26F4-4D77-AC34-D5ECBD90971F}">
+                <File Id="fil7DCE042084620C1F655F4A4E093B14DC" KeyPath="yes" Source="SourceDir\QtQuick\NativeStyle\controls\DefaultRadioButton.qml" />
+            </Component>
+            <Component Id="cmp071BE82A132951958F2232532EE2EE6B" Directory="dir58487975ACF4D38FB978C7C039C7F158" Guid="{ED7FAA8D-22DF-42D2-B2A4-E1C05515507C}">
+                <File Id="fil4200AF3B417230FE3B0F30E172A97E72" KeyPath="yes" Source="SourceDir\QtQuick\NativeStyle\controls\DefaultScrollBar.qml" />
+            </Component>
+            <Component Id="cmpF6226C87327DF4D998B01BEF7376EC39" Directory="dir58487975ACF4D38FB978C7C039C7F158" Guid="{68541CC6-4EDD-49B7-9B0C-F2447589B4D7}">
+                <File Id="filE0EF8562357E0D5F66E87F419DF8FC3D" KeyPath="yes" Source="SourceDir\QtQuick\NativeStyle\controls\DefaultSlider.qml" />
+            </Component>
+            <Component Id="cmpB8FB38364C5563EFB005FB7DB448AA01" Directory="dir58487975ACF4D38FB978C7C039C7F158" Guid="{51B96ACB-F081-4461-BD93-2EB39D6AEDEF}">
+                <File Id="fil477038CAC0614C163F76DF3E3ADEE13D" KeyPath="yes" Source="SourceDir\QtQuick\NativeStyle\controls\DefaultSpinBox.qml" />
+            </Component>
+            <Component Id="cmp5686F4E3083759CC017D006862B2D579" Directory="dir58487975ACF4D38FB978C7C039C7F158" Guid="{C702B466-B52E-491C-B843-E3EAFC05BDF1}">
+                <File Id="fil957B452CC7F1B00930C5BE41249AFAFF" KeyPath="yes" Source="SourceDir\QtQuick\NativeStyle\controls\DefaultTextArea.qml" />
+            </Component>
+            <Component Id="cmp66E4ED6563300BD3B8633FBD3365F612" Directory="dir58487975ACF4D38FB978C7C039C7F158" Guid="{A68ABEB5-B749-4601-8311-0E7393A024BB}">
+                <File Id="fil5C150E53C4C75647FD50D2D56DB81945" KeyPath="yes" Source="SourceDir\QtQuick\NativeStyle\controls\DefaultTextField.qml" />
+            </Component>
+            <Component Id="cmp4A4AE02C0F83BB8FB5A861622C5AEDE5" Directory="dir90B3BDF6AA234878B0614AA16A2106A5" Guid="{27F6A524-5098-4D6A-8C0B-874AAF753131}">
+                <File Id="fil252BAC11D1191443F95EA90148C978B5" KeyPath="yes" Source="SourceDir\QtQuick\Particles\particlesplugin.dll" />
+            </Component>
+            <Component Id="cmp729D15F2AC478EC490D6CF877E3D4E7C" Directory="dir90B3BDF6AA234878B0614AA16A2106A5" Guid="{7CB800A7-9331-416C-945A-E14D4E49EE5F}">
+                <File Id="filCAC30C15B89D46B843BECB4B8D57B13C" KeyPath="yes" Source="SourceDir\QtQuick\Particles\plugins.qmltypes" />
+            </Component>
+            <Component Id="cmpED9A78002DADC76CE30B1FD385E0B1E2" Directory="dir90B3BDF6AA234878B0614AA16A2106A5" Guid="{3F4DCDA3-BD83-4FBD-ADC2-EC1D851C7AE6}">
+                <File Id="filF34B1FB8399C0B42D8B3928AA49C3E7B" KeyPath="yes" Source="SourceDir\QtQuick\Particles\qmldir" />
+            </Component>
+            <Component Id="cmpD439D7A99D5E651813EB49EC0882F3BE" Directory="dirABDE04EACFBE2E08EF0F50035924A8DF" Guid="{ABA9BC2A-B200-438B-A34A-A0F97713F016}">
+                <File Id="fil74A2E398BE6C7596DA7DE596B3F8F8FB" KeyPath="yes" Source="SourceDir\QtQuick\Shapes\plugins.qmltypes" />
+            </Component>
+            <Component Id="cmp1680E5779B149890D5A5507DCF06EE98" Directory="dirABDE04EACFBE2E08EF0F50035924A8DF" Guid="{936EA795-A290-4C29-8969-7FAFD4AF30A0}">
+                <File Id="filC3C759716122B36496470D74063EF98C" KeyPath="yes" Source="SourceDir\QtQuick\Shapes\qmldir" />
+            </Component>
+            <Component Id="cmpBF182C25C9484DE2C8F81FF3BBAC0A92" Directory="dirABDE04EACFBE2E08EF0F50035924A8DF" Guid="{9254B99F-B983-4D00-91A2-E8901E1191C8}">
+                <File Id="fil53232A2116C7477545FCB24744234C70" KeyPath="yes" Source="SourceDir\QtQuick\Shapes\qmlshapesplugin.dll" />
+            </Component>
+            <Component Id="cmp8B5292AE63D8923F5CA6F2E0DB5913A1" Directory="dir8A47127E3442CB3A3D754FBB35FF92DC" Guid="{8E2A2771-FF5F-4C83-8B65-2BEEE09DF690}">
+                <File Id="filD1127CFA0BCE28FFF917169873EFD96C" KeyPath="yes" Source="SourceDir\QtQuick\Templates\plugins.qmltypes" />
+            </Component>
+            <Component Id="cmp91D5CD5761372BB05377C141862904F3" Directory="dir8A47127E3442CB3A3D754FBB35FF92DC" Guid="{5A474330-A627-48CB-A837-DCE41AFBC135}">
+                <File Id="fil796B4670D5FA6AD7323B1D4691145D9B" KeyPath="yes" Source="SourceDir\QtQuick\Templates\qmldir" />
+            </Component>
+            <Component Id="cmpD368DD14F73B73FF975CD1D8CEEBFE1D" Directory="dir8A47127E3442CB3A3D754FBB35FF92DC" Guid="{58243353-5A4B-4982-96B8-460CAC8D6AA6}">
+                <File Id="filA823C7A49089B90099CA49EDD36637A6" KeyPath="yes" Source="SourceDir\QtQuick\Templates\qtquicktemplates2plugin.dll" />
+            </Component>
+            <Component Id="cmpEACF597186E09F814895FA9D47B5E29C" Directory="dirC8FA895F8CD333726C0D487F219EA7A7" Guid="{FA9C3344-D355-4C38-B30E-FD8DD70156DE}">
+                <File Id="filB358E5A578D457E54A0EF042DAF60A33" KeyPath="yes" Source="SourceDir\QtQuick\tooling\Component.qml" />
+            </Component>
+            <Component Id="cmp5587BA20BAC0657E89B04B2E6D4241AB" Directory="dirC8FA895F8CD333726C0D487F219EA7A7" Guid="{FEFE21B0-3DDC-4EE4-8217-ADAA51E9C367}">
+                <File Id="filB5D063FE0461A7737CE51D3A7FB375FE" KeyPath="yes" Source="SourceDir\QtQuick\tooling\Enum.qml" />
+            </Component>
+            <Component Id="cmpA4DB76EA3BD0A4BF5582D317448C18E5" Directory="dirC8FA895F8CD333726C0D487F219EA7A7" Guid="{C50CEE7F-4C33-4591-B93D-C73B8AC9A470}">
+                <File Id="filC40BA93F96661082D93A8D8010DA87DF" KeyPath="yes" Source="SourceDir\QtQuick\tooling\Member.qml" />
+            </Component>
+            <Component Id="cmpCE67865698F21301B6ADAE2D32A5FAB7" Directory="dirC8FA895F8CD333726C0D487F219EA7A7" Guid="{70659947-7FB6-4A64-BFE8-319452145073}">
+                <File Id="fil49F2C076ED538E221D4B5C38C347B867" KeyPath="yes" Source="SourceDir\QtQuick\tooling\Method.qml" />
+            </Component>
+            <Component Id="cmpB01579FBDB01BD41F64E7857C566956E" Directory="dirC8FA895F8CD333726C0D487F219EA7A7" Guid="{44CC9D10-57E8-4DE0-B814-C657E8901FAC}">
+                <File Id="filD4E1E28991AE1E0E70938484B0C885B8" KeyPath="yes" Source="SourceDir\QtQuick\tooling\Module.qml" />
+            </Component>
+            <Component Id="cmp7BB4FFAB0CA5270054C7793CAD410F6D" Directory="dirC8FA895F8CD333726C0D487F219EA7A7" Guid="{E70E74F6-3676-4857-AC6A-43CCA35FC2C5}">
+                <File Id="fil4745AB0E32DF4F3C9587061F21B2AACE" KeyPath="yes" Source="SourceDir\QtQuick\tooling\Parameter.qml" />
+            </Component>
+            <Component Id="cmp731DFBDC900C1C9BB223B739755FD2CD" Directory="dirC8FA895F8CD333726C0D487F219EA7A7" Guid="{4430AC9B-ED57-4056-BB9B-83F34CF4C6D4}">
+                <File Id="fil2781FD54A8AB90F7F7CFC325B51DF81D" KeyPath="yes" Source="SourceDir\QtQuick\tooling\Property.qml" />
+            </Component>
+            <Component Id="cmpC26AD011E7FA325C674E2BA6A88DCF09" Directory="dirC8FA895F8CD333726C0D487F219EA7A7" Guid="{1294610F-75DF-4621-9C0F-FC3B30E8C840}">
+                <File Id="fil019A2201C31B561A73311066C9DE8311" KeyPath="yes" Source="SourceDir\QtQuick\tooling\qmldir" />
+            </Component>
+            <Component Id="cmpC990907454F78A9E516FCFFE34EA0108" Directory="dirC8FA895F8CD333726C0D487F219EA7A7" Guid="{726B00C8-22E5-4B25-8F84-A70BE7611110}">
+                <File Id="fil3F71995BCDC0375D3BC5E7BB9031A4CF" KeyPath="yes" Source="SourceDir\QtQuick\tooling\quicktooling.qmltypes" />
+            </Component>
+            <Component Id="cmpAF18F8EFDA7D59547C85BFA6DBC13929" Directory="dirC8FA895F8CD333726C0D487F219EA7A7" Guid="{87B6D9B0-FB5D-43DF-8FED-D5E50B7811E3}">
+                <File Id="filA5B3B18BA1555F81AEE40AF778B9EBC8" KeyPath="yes" Source="SourceDir\QtQuick\tooling\quicktoolingplugin.dll" />
+            </Component>
+            <Component Id="cmp0C6B8BA9D537F628E3097545E441EBB8" Directory="dirC8FA895F8CD333726C0D487F219EA7A7" Guid="{5FB52E95-9DA1-4D21-AE38-0B3A17B80656}">
+                <File Id="filF47329CE4A386718B5A5EA11FB947408" KeyPath="yes" Source="SourceDir\QtQuick\tooling\Signal.qml" />
+            </Component>
+            <Component Id="cmp3C0189FA8242F9E1C2DE1F83B3CC19CB" Directory="dirD15C819041ADE7ED5CEE37C13C9C814C" Guid="{53687607-D045-43D9-9FD8-ABAE39712316}">
+                <File Id="fil7E80137ABBE4A40A694BD4670C290B8C" KeyPath="yes" Source="SourceDir\QtQuick\Window\qmldir" />
+            </Component>
+            <Component Id="cmp9D86EA60634EF3657D30B2609FE912F2" Directory="dirD15C819041ADE7ED5CEE37C13C9C814C" Guid="{53FEFAEA-C791-475C-B1CE-D51C0DC9F276}">
+                <File Id="filBF2E7974F72A41ED254CD9C13493A149" KeyPath="yes" Source="SourceDir\QtQuick\Window\quickwindow.qmltypes" />
+            </Component>
+            <Component Id="cmp15ECA5760E267CCD81E10DB031BBAB43" Directory="dirD15C819041ADE7ED5CEE37C13C9C814C" Guid="{D08D5DF9-53E3-4672-BD2A-C496BFA432BC}">
+                <File Id="fil2922F007C5B8D16F6C67EDB66EFC3F94" KeyPath="yes" Source="SourceDir\QtQuick\Window\quickwindowplugin.dll" />
+            </Component>
+            <Component Id="cmpE7E3F707C5E511F7D507BBD32EA1979E" Directory="dir8F7FCDAEC833D8963B08029F5D194C05" Guid="{BE921772-0799-4ED1-B10C-1D3218F1B0C1}">
+                <File Id="fil01A1242D7B0CEFE3F72FB22B7AE0C1BE" KeyPath="yes" Source="SourceDir\QtWebEngine\plugins.qmltypes" />
+            </Component>
+            <Component Id="cmpB717FBF9C2EE490798C0BC76BF337B6D" Directory="dir8F7FCDAEC833D8963B08029F5D194C05" Guid="{40A1A00A-DBAB-41B0-84AA-FCB8B5C33904}">
+                <File Id="fil23CA126EAA1FBEA81206309761002603" KeyPath="yes" Source="SourceDir\QtWebEngine\qmldir" />
+            </Component>
+            <Component Id="cmpFDC70989694DECD0EDE269DB9F4D0BAF" Directory="dir8F7FCDAEC833D8963B08029F5D194C05" Guid="{72D6F0E3-23FD-4391-BF98-7E95AF767150}">
+                <File Id="fil31787DE56D19F02F0B205E6A9F89BD3A" KeyPath="yes" Source="SourceDir\QtWebEngine\qtwebenginequickplugin.dll" />
+            </Component>
+            <Component Id="cmpDF0F9BDC77CA471526B71486F35DFB9B" Directory="dir2E19425B618D245C054C135AF0EF7016" Guid="{9E134271-3E7B-49CE-B42D-DC9AD00682E8}">
+                <File Id="filFFE188A040A97569DC74BC81768AA5DE" KeyPath="yes" Source="SourceDir\QtWebEngine\ControlsDelegates\AlertDialog.qml" />
+            </Component>
+            <Component Id="cmpE1A25230A7E46503132FEC7A3C00AF0F" Directory="dir2E19425B618D245C054C135AF0EF7016" Guid="{E95309BA-53A6-4E9C-A6DE-C4696A4FDA13}">
+                <File Id="filFAC0218DCF618390C8F5BEE2640C4D08" KeyPath="yes" Source="SourceDir\QtWebEngine\ControlsDelegates\AuthenticationDialog.qml" />
+            </Component>
+            <Component Id="cmp2B1D11C446B55914DBE518FD72561E4E" Directory="dir2E19425B618D245C054C135AF0EF7016" Guid="{88F0C33D-66B3-4943-A5C0-CCB9369EF66B}">
+                <File Id="filCCF8841D536DBDDFB69D210BE3D9D76E" KeyPath="yes" Source="SourceDir\QtWebEngine\ControlsDelegates\ColorDialog.qml" />
+            </Component>
+            <Component Id="cmpB2B720B0D1CF3C80F732E5E49EA82619" Directory="dir2E19425B618D245C054C135AF0EF7016" Guid="{EA445F63-AC0E-46D3-A17E-9FAAE9505CC2}">
+                <File Id="fil821C7A10D3271DB3BC5621CF9A60A7F2" KeyPath="yes" Source="SourceDir\QtWebEngine\ControlsDelegates\ConfirmDialog.qml" />
+            </Component>
+            <Component Id="cmp292A5DD1BF16818B059E728A64D4CA7F" Directory="dir2E19425B618D245C054C135AF0EF7016" Guid="{7B966599-F358-479E-AB71-695349447480}">
+                <File Id="filA0FCE9FC58F7C743CB4572AA310875AA" KeyPath="yes" Source="SourceDir\QtWebEngine\ControlsDelegates\FilePicker.qml" />
+            </Component>
+            <Component Id="cmpAA26FBCAAF96F7737078C402B7E31120" Directory="dir2E19425B618D245C054C135AF0EF7016" Guid="{05992FF8-082E-427B-BC17-A66CA3BFEF9C}">
+                <File Id="fil3756EB7973C98B536EBEA0A52780CE9D" KeyPath="yes" Source="SourceDir\QtWebEngine\ControlsDelegates\Menu.qml" />
+            </Component>
+            <Component Id="cmpDF8DBD17AAC0AAB693F23E2D1247356F" Directory="dir2E19425B618D245C054C135AF0EF7016" Guid="{E4A8709C-DD55-4EA1-BB44-3CC89CB434DF}">
+                <File Id="fil6E4A5DA543B4DDC83B13F5EA6E6977B2" KeyPath="yes" Source="SourceDir\QtWebEngine\ControlsDelegates\MenuItem.qml" />
+            </Component>
+            <Component Id="cmp24205A44E4FC113132ECE476CEF11D67" Directory="dir2E19425B618D245C054C135AF0EF7016" Guid="{A1FB2585-5BA2-4031-97CB-E2C1CEFB1D14}">
+                <File Id="fil9E3F4A0B30C44991467E72B615BFFFD7" KeyPath="yes" Source="SourceDir\QtWebEngine\ControlsDelegates\MenuSeparator.qml" />
+            </Component>
+            <Component Id="cmp887D7B71BB53FF5089AEEAF6D2FC3346" Directory="dir2E19425B618D245C054C135AF0EF7016" Guid="{B2A66694-9703-4C98-BAAC-C46BEA02A6FE}">
+                <File Id="fil6CC76EC60F31BAADF0AA814E7197CDAE" KeyPath="yes" Source="SourceDir\QtWebEngine\ControlsDelegates\PromptDialog.qml" />
+            </Component>
+            <Component Id="cmp3AB7D3E872EF4F725A60C59E13A86F31" Directory="dir2E19425B618D245C054C135AF0EF7016" Guid="{7E1403F7-CE38-49A6-862E-5C8812E2988A}">
+                <File Id="fil85B05D80843E623312D04CEAF51CB410" KeyPath="yes" Source="SourceDir\QtWebEngine\ControlsDelegates\qmldir" />
+            </Component>
+            <Component Id="cmpA4059FF09BF38AC0BDD4F13757D659EA" Directory="dir2E19425B618D245C054C135AF0EF7016" Guid="{580792B5-5C7F-4E30-9D73-AB706333747A}">
+                <File Id="fil507C36CCA4826044277CFC5FA352C7F8" KeyPath="yes" Source="SourceDir\QtWebEngine\ControlsDelegates\qtwebenginequickdelegatesplugin.dll" />
+            </Component>
+            <Component Id="cmpFF98E07BFF47BDFFF9BA32BA466A1381" Directory="dir2E19425B618D245C054C135AF0EF7016" Guid="{8469D8FC-40AF-4222-A077-A0A42EE45CE6}">
+                <File Id="fil952116F4ACB7206535133E7772824526" KeyPath="yes" Source="SourceDir\QtWebEngine\ControlsDelegates\ToolTip.qml" />
+            </Component>
+            <Component Id="cmpBB8D0A4FA47401781C7B8DA495C2DFAE" Directory="dir2E19425B618D245C054C135AF0EF7016" Guid="{0B01446F-C534-4FFA-B1EB-53C877EDE2EF}">
+                <File Id="filDC668B3B40F974B0A5781DC00CB65175" KeyPath="yes" Source="SourceDir\QtWebEngine\ControlsDelegates\TouchHandle.qml" />
+            </Component>
+            <Component Id="cmp3140BF819007B9D17798BC88ACD07DD9" Directory="dir2E19425B618D245C054C135AF0EF7016" Guid="{03264FA9-90D1-4D81-86FA-0C86CCE0F6E7}">
+                <File Id="fil9891C37A1C4B5AE90246674EFCFD727F" KeyPath="yes" Source="SourceDir\QtWebEngine\ControlsDelegates\TouchSelectionMenu.qml" />
+            </Component>
+            <Component Id="cmp62DBE17548E4B21AAE3863D932966787" Directory="dir2E19425B618D245C054C135AF0EF7016" Guid="{45566CA9-21EC-4BA7-9AEE-A4D91C1742D1}">
+                <File Id="fil19BF70606CFCB78877764CAAF59A65EC" KeyPath="yes" Source="SourceDir\QtWebEngine\ControlsDelegates\WebEngineQuickDelegatesQml.qmltypes" />
+            </Component>
+            <Component Id="cmpFC8A4A78B4CE6E58788FAA6683F3EE04" Directory="dir6D65237267C4C59098A0E61E06E59952" Guid="{23F856C3-3BBE-47F5-A1B7-7D5A6E433130}">
+                <File Id="fil1D56A5525238C8F2C6EBF4CFA2996B74" KeyPath="yes" Source="SourceDir\QtWebView\plugins.qmltypes" />
+            </Component>
+            <Component Id="cmp3DF6BDEE2FC0EED062F14241DE7A96BE" Directory="dir6D65237267C4C59098A0E61E06E59952" Guid="{C8AD80C4-B813-41B2-8953-E6F244CA483A}">
+                <File Id="fil7039F0E9C61B5A059A7DD4774086AADC" KeyPath="yes" Source="SourceDir\QtWebView\qmldir" />
+            </Component>
+            <Component Id="cmp35DC69EF5B19FDA08112276BEC5FCD1C" Directory="dir6D65237267C4C59098A0E61E06E59952" Guid="{FE3BC74B-688D-4B4C-A537-FA3A49A8EC98}">
+                <File Id="filC4336BF378848043A038FDA65D177107" KeyPath="yes" Source="SourceDir\QtWebView\qtwebviewquickplugin.dll" />
+            </Component>
+            <Component Id="cmp7939DBD081402A3D5A1E67BDD3FDC674" Directory="dir08941CEA6046320FCCF158F759AF80F2" Guid="{70690AC8-9EAC-4207-AEC3-2CC877115035}">
+                <File Id="fil51E0E82B321287CAC39351EA74609C90" KeyPath="yes" Source="SourceDir\resources\icudtl.dat" />
+            </Component>
+            <Component Id="cmpFCDF3702D9093A75BC161A3A1BB6BB86" Directory="dir08941CEA6046320FCCF158F759AF80F2" Guid="{7A483067-48E9-4830-9A70-46DF777DFF49}">
+                <File Id="fil5E3E79E2C8D58B41AC6EF5891C08A810" KeyPath="yes" Source="SourceDir\resources\qtwebengine_devtools_resources.pak" />
+            </Component>
+            <Component Id="cmp09CCD93B93EDDE01A6B28A19B724194A" Directory="dir08941CEA6046320FCCF158F759AF80F2" Guid="{28157F51-E8DF-4080-9275-9853C0E3D2B2}">
+                <File Id="fil08082A21F54192390BB8CCF88F7E6B9D" KeyPath="yes" Source="SourceDir\resources\qtwebengine_resources.pak" />
+            </Component>
+            <Component Id="cmp263082AF02EA4B9F434F2501E23AC972" Directory="dir08941CEA6046320FCCF158F759AF80F2" Guid="{EB5C7253-D603-4B31-95CF-7C7157A3E3F4}">
+                <File Id="fil24B7F212EFD5AB10B522561407BDDFC9" KeyPath="yes" Source="SourceDir\resources\qtwebengine_resources_100p.pak" />
+            </Component>
+            <Component Id="cmpC2267073DB55586843E13B823F6E0E8B" Directory="dir08941CEA6046320FCCF158F759AF80F2" Guid="{B7CB4FC1-BF14-47CF-AD7C-04B581B028F8}">
+                <File Id="filFDCD09910FB501B1A747E0A04B8D3E31" KeyPath="yes" Source="SourceDir\resources\qtwebengine_resources_200p.pak" />
+            </Component>
+            <Component Id="cmp5DAC8E92F0D1CE1028B76D02F12152DE" Directory="dir3BC41331752E78D0C2719C277915294F" Guid="{3773817D-66C1-48E3-A370-333237394858}">
+                <File Id="fil4F3C3348649DA965E980E99B9E82B7FF" KeyPath="yes" Source="SourceDir\styles\qwindowsvistastyle.dll" />
+            </Component>
+            <Component Id="cmpD57693E12A6CF9AFDC034864F2950D6F" Directory="dir509C75F94B9AF6C35CA00410005C14EE" Guid="{F49F8C94-0B60-4F26-B9B0-345928B1C3BA}">
+                <File Id="filA3197B081536A45213DBC7CD12A615C4" KeyPath="yes" Source="SourceDir\tls\qcertonlybackend.dll" />
+            </Component>
+            <Component Id="cmpC8A93EB5F0E2881753F8DCDA1B02129B" Directory="dir509C75F94B9AF6C35CA00410005C14EE" Guid="{89FB7332-0A1E-4DB8-937E-E293E2F90075}">
+                <File Id="fil63F4FB8EB696B5BA01AD976846F45663" KeyPath="yes" Source="SourceDir\tls\qschannelbackend.dll" />
+            </Component>
+            <Component Id="cmpE7627EECD23898BC1AEEEBA80CF4C18E" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="{316228AE-54BF-4084-A220-5718FD5F2575}">
+                <File Id="fil841A28DBCBF2256981B583E30DC241AF" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\am.pak" />
+            </Component>
+            <Component Id="cmp4AA1EF8E6B3E8E5742AC3878237B15BD" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="{2B7AE48B-46A6-4D7A-B9A0-DA8CDB51E47E}">
+                <File Id="fil7B1E977DD95738FCA43A5AF4E3DB820F" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\ar.pak" />
+            </Component>
+            <Component Id="cmp489F46694306763E664E68F1A1D9398A" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="{37E51441-D238-4C10-A4E7-99E6F40ABAA7}">
+                <File Id="filF1068C95C51734B905AD68641F8932DE" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\bg.pak" />
+            </Component>
+            <Component Id="cmpCB3806B057A11129336C03B60DB93368" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="{CA89D25B-C4C3-4038-A33C-4786A0EABBC8}">
+                <File Id="fil5E98C112CCDD7A81098634A800367973" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\bn.pak" />
+            </Component>
+            <Component Id="cmp117BEDE3E308B6346D00371AE16FC727" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="{108DBE8F-87E4-416F-A7ED-CBBBCE4BF903}">
+                <File Id="fil1F344AC58CF6783633B80F8532689D84" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\ca.pak" />
+            </Component>
+            <Component Id="cmp1B4BD11EB6D78DE042388FC7B3B10979" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="{56E502AD-1F48-4BC3-A8B8-2CCC94942864}">
+                <File Id="fil2DFDD403496C8DA8549416381A275F4B" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\cs.pak" />
+            </Component>
+            <Component Id="cmp8B5804D6EA4CE8594E25FA2EAA53B2CE" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="{10031699-B994-41DF-AC30-9D1E3EF39BEA}">
+                <File Id="fil8998ADC69728900F32D27523BE7C0A7A" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\da.pak" />
+            </Component>
+            <Component Id="cmp14635E19526FBB5CC031D48A21B8DFE4" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="{2147E310-8D3C-4E8A-A96A-A69FD8871F35}">
+                <File Id="filE4885D67548AFA65C78C08300C700419" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\de.pak" />
+            </Component>
+            <Component Id="cmp0DD34B1E211835A0825C4F37AEC7D421" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="{4E40A463-2A73-488B-A0D6-9972A050CD9D}">
+                <File Id="fil544A6F5F8C37D55BB452375A1C857B0E" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\el.pak" />
+            </Component>
+            <Component Id="cmpBB56FFD9B4B03F1B7EFFE608596C1DF0" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="{868AF772-3DDE-43A9-A878-59F4A8AC74F8}">
+                <File Id="filC138B25B3D7A4129D2F01586DDAB4929" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\en-GB.pak" />
+            </Component>
+            <Component Id="cmp7D1B7857542317367F521676B9C49306" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="{EA66E25D-5305-46DC-8C66-2618D1C7B382}">
+                <File Id="fil1726E7077A64870D0E207D3578CBBDF4" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\en-US.pak" />
+            </Component>
+            <Component Id="cmpAC456BCAF3DCFA01198913534FCD2F3E" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="{811D68EC-064D-4C0D-8707-346D564CCA9F}">
+                <File Id="fil2DFEE5CE81BFF7E138A6954DE8118BBF" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\es-419.pak" />
+            </Component>
+            <Component Id="cmp561D4111326710AC9AA46907E05AE2F8" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="{BC07D34B-0DEE-422B-B8A6-6D22A039B010}">
+                <File Id="fil5149EA34D842EEA58E32E2FF3EA9FC74" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\es.pak" />
+            </Component>
+            <Component Id="cmp767F92E764F493FC663E591BBF691924" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="{08879442-0461-4BD9-A3CA-05685EDC3464}">
+                <File Id="fil175C98AD989D0A19A6051CECB41755E7" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\et.pak" />
+            </Component>
+            <Component Id="cmp83CE7D31B03F0E640D486C8951C9F88A" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="{C6C7DDC4-95C2-409F-B96D-1A967BBDA27E}">
+                <File Id="fil80A0D15334F4AE3C93E9AB996047F063" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\fa.pak" />
+            </Component>
+            <Component Id="cmpB556D1FABA724DC54358690B4A4F5528" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="{94F25F58-8232-4978-B451-E21C0305AC3B}">
+                <File Id="fil9E20F6D165CA887227872340CF842DB7" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\fi.pak" />
+            </Component>
+            <Component Id="cmp2229E25AED1AE14594087A37BF635290" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="{44E44245-0A1C-4257-8EA8-978A85CF8A6A}">
+                <File Id="filF183998CF77694A80E81B414ADAAD6F6" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\fil.pak" />
+            </Component>
+            <Component Id="cmp7E4EE520FFDF02CB789F8284CCF745D8" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="{C01581FA-A19C-438B-AAB4-23BF0254167C}">
+                <File Id="fil2CEB374D9A3E9F8697A7245C40BAE974" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\fr.pak" />
+            </Component>
+            <Component Id="cmpB06A2E85562494B9B6F676A03E87D654" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="{5D04712D-14A9-4707-BE57-AEA0D2EE9F8B}">
+                <File Id="filBC5A940DFAF4F4289C03179C3331CA32" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\gu.pak" />
+            </Component>
+            <Component Id="cmpCCDE1181B25851E3340339CF61382B80" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="{050EA410-4B39-4C02-81AD-3FB847BF94AA}">
+                <File Id="fil96390711BE9B947D7B7FA5E0211E42E9" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\he.pak" />
+            </Component>
+            <Component Id="cmp25690B79DEAC48ED058A09275BBAAC7B" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="{96CC859F-82BF-41BD-AB1F-D9F9F9A8097D}">
+                <File Id="fil85370AA4C63C2DECEC33E30E3DB303F9" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\hi.pak" />
+            </Component>
+            <Component Id="cmp6EE71F73860FDA82CE0BA04F55AB4E65" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="{6FA8B74C-91C4-497A-8047-964F6B93A4EE}">
+                <File Id="fil8F215425A3BC657C3D4C352ECD1F7101" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\hr.pak" />
+            </Component>
+            <Component Id="cmpD21C6E91A9244444957B19A630773D29" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="{2F7071AA-56BA-4FA2-ABE0-ECB0D5F4259F}">
+                <File Id="filE9F1E4FD2A7A617A4E4166414AB45691" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\hu.pak" />
+            </Component>
+            <Component Id="cmp38389365912F0AE1DFACDDC8479C7BF2" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="{7C72D56E-D050-4BFF-B17D-09A32D8AF46E}">
+                <File Id="fil7D1F559C2C1110D18CA5764F1971BBA7" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\id.pak" />
+            </Component>
+            <Component Id="cmp9BCC30608F875FB2B6809ACBE266A0EB" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="{F7AB65A5-FFAD-4A78-9DC6-A24F2B54BA79}">
+                <File Id="fil4CA2DD502CC46D09E241817165D07A69" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\it.pak" />
+            </Component>
+            <Component Id="cmp66B3A389FF5706DD47F6031C96870AC3" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="{F4E2360B-4EB2-4E96-9F59-EFBAFA5ECCCF}">
+                <File Id="fil9A44734AE8E81C970FCD4CD1A15562EC" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\ja.pak" />
+            </Component>
+            <Component Id="cmpC35D767A0329A304ED9409EE1758B26E" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="{CCF07DE0-7975-4D9C-9728-FEE0C72C27AC}">
+                <File Id="filBEF8A5F318E161CAB9EB973C81EE8BAD" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\kn.pak" />
+            </Component>
+            <Component Id="cmp4FAE7008D38C2834FF608ECB10F422B3" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="{8FE6E9B9-1BCB-467D-9345-0651E7D798BD}">
+                <File Id="fil0E05F04BA9603B8A46D202EE87E01968" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\ko.pak" />
+            </Component>
+            <Component Id="cmp41E232D5090F2D0E0B1781C90734A625" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="{334C395C-21E0-44D0-9570-4643A55F0660}">
+                <File Id="filB1E277BB14B69222B7C7DCC91A1E41D0" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\lt.pak" />
+            </Component>
+            <Component Id="cmp68559A181BEB78EFD5137F57CAD5FB00" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="{3224E314-C635-4F09-8A28-EEB759E669D4}">
+                <File Id="fil75045C6FE752E32D2CA7E724F92A4C03" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\lv.pak" />
+            </Component>
+            <Component Id="cmp0F1F24602E525646795B185F7B39955A" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="{D16B0186-716A-43FA-AF63-0FF5F1C019EC}">
+                <File Id="fil4EAF6A9B314FEFD4868F09695F2883EF" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\ml.pak" />
+            </Component>
+            <Component Id="cmpAD12B9F7ABF28F48C3C94ADA02853AD5" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="{31391CBB-65BE-4943-841D-2FDBE1471BB8}">
+                <File Id="filD4AE47CF3E0E2219C965A01B69346DC6" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\mr.pak" />
+            </Component>
+            <Component Id="cmpA6990FFF2D1086D28FBE7A600D52F67C" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="{25E8974E-27FB-4A5F-9D6C-281519DAAD65}">
+                <File Id="fil8DA799DEB9484A9C1D8C766F558D928B" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\ms.pak" />
+            </Component>
+            <Component Id="cmp74CC7AE3E2D73D6EB448973554564268" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="{9E32E662-0FE7-4024-9293-A4CB5CC70EC9}">
+                <File Id="filD917C45F7D063800C10DFF694350E88F" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\nb.pak" />
+            </Component>
+            <Component Id="cmpCF99C5168606D7B5069B19761014FF91" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="{7EE07CC1-BDAB-4CA7-B082-7ACBBCD867DA}">
+                <File Id="fil0BBBA2ED5EA505A0761814636A3E14CC" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\nl.pak" />
+            </Component>
+            <Component Id="cmpCF5AA8564BF2B7AECAAE7FFE84D8E4D9" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="{50B28306-7296-452D-8855-84FBE0DEF3C6}">
+                <File Id="filBBE0AA8ADCAD675D5BE7906A7557CA33" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\pl.pak" />
+            </Component>
+            <Component Id="cmp1E4EF1965C96EAF1EDF0ED36C03EAE83" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="{631D7CAE-7F80-49A8-871D-C97B28659180}">
+                <File Id="fil6A9727728F57E501FC885A71011F615B" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\pt-BR.pak" />
+            </Component>
+            <Component Id="cmp291C58BF5375B658AD36A7D92A0C8479" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="{A16B236D-8BD1-48FD-889C-87E3E1A84FB5}">
+                <File Id="fil5444B8CC9431DD88F907C57EA9A90572" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\pt-PT.pak" />
+            </Component>
+            <Component Id="cmpAE702D6C4F5F596DB6B15FB36C6D9DAB" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="{4195DB42-C6EE-4F23-B1F5-5F310A93E660}">
+                <File Id="filB15BEEE3DA276ADEA0E8A24C77D5A9C2" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\ro.pak" />
+            </Component>
+            <Component Id="cmpDF7385B2F039DBE31A2545C254ABF0F0" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="{23B8D5DF-092F-4F9F-A409-B10F7C3F43E3}">
+                <File Id="filBCB5F742C4B473A8C6C5CF4917E6144A" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\ru.pak" />
+            </Component>
+            <Component Id="cmpDF8F1EB0E2A70BD5F396A75FECB92533" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="{6C528DEF-26A6-41B1-890D-7C93C5C00CEF}">
+                <File Id="fil9769BAFE384BB1853A2C508DFA05229F" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\sk.pak" />
+            </Component>
+            <Component Id="cmpDB5BD4BF5A46761C6544B87CF7085EEB" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="{3C78325E-A8C8-481D-8170-3FB1DB79399D}">
+                <File Id="filA84154373398754DDDB4E950A134C5F5" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\sl.pak" />
+            </Component>
+            <Component Id="cmp1C71511A57869D1F5DD784E9E4191BFD" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="{C8E2297C-65D1-4781-B7B6-01EE34763396}">
+                <File Id="fil5F171B8A37BA8123184CABD8A9F23CFA" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\sr.pak" />
+            </Component>
+            <Component Id="cmpB922A5B2744CD81821403BFD931F8112" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="{1602F9E2-E6D2-4232-83BC-9624941DDA8D}">
+                <File Id="fil20AA02ACBBE6A5F32DE18FF2F0A7AEFF" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\sv.pak" />
+            </Component>
+            <Component Id="cmp6DAB86BB3D2953D10957B0FAFF22E155" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="{C2B6BA19-F347-4DC5-B7D2-D91A62E89EA9}">
+                <File Id="fil6199BA4B33801E936F1847E792F5CAAB" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\sw.pak" />
+            </Component>
+            <Component Id="cmpFC87AE461BD4B51F4A19E039A3C25CA1" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="{C7484B41-0BC9-42B1-9FA6-7847B56C883B}">
+                <File Id="fil38C023D4016EF85C52C0B835FC94F0C7" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\ta.pak" />
+            </Component>
+            <Component Id="cmp58B01AD92CF1FEEF6EA0325F60D05AA6" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="{DDB5E1A6-82AB-48F8-A46B-F3B05A6B2B8C}">
+                <File Id="fil09449582AB62FB08D7CD3574754B8C0C" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\te.pak" />
+            </Component>
+            <Component Id="cmpA3A2E454F58F8435858E88AC73495360" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="{0277E03E-1470-4733-AF67-DB4166968375}">
+                <File Id="fil27FE0443556E7587E13EE46CFBF62A4F" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\th.pak" />
+            </Component>
+            <Component Id="cmp15CD85788F2B7BE37D3D33C7B69D5FF6" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="{04AE80EA-26AC-4B7B-8A8E-7CB0CCB58B04}">
+                <File Id="fil55F7FECA91CEE1F865B75C6581438FFC" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\tr.pak" />
+            </Component>
+            <Component Id="cmp2D4EB727F69B23FADD8074CB77E51F36" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="{5A9D30E7-6F12-4978-82A9-BF0CB15BA670}">
+                <File Id="filC97CBBD646D7E81182BFE9ECB61844CA" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\uk.pak" />
+            </Component>
+            <Component Id="cmp43AF26BE2B604BD873763D73460FB4B9" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="{88D4C378-A57F-4F12-9CBD-ED7F62D7060A}">
+                <File Id="fil467C07F74FBFC01F0D823F0E76F07FB7" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\vi.pak" />
+            </Component>
+            <Component Id="cmp8CCECC99D6C73FD198EA365D4835864F" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="{8B20312C-891D-48C7-9898-CDF4C2A84671}">
+                <File Id="filD0D4650DF850FAD942030BBC923D8D7A" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\zh-CN.pak" />
+            </Component>
+            <Component Id="cmp9F8E9C0770E5832169033250113D1A21" Directory="dirA016FBC978C7F28C3C5CDBC06002A5D6" Guid="{6FADF2B8-45CE-4E11-B85C-C800C3F90616}">
+                <File Id="fil2FAD3F60E739CE8D347E5ADE50EA653C" KeyPath="yes" Source="SourceDir\translations\qtwebengine_locales\zh-TW.pak" />
+            </Component>
+            <Component Id="cmp8F41C0E31AFDC29F8BDF013E907B2A5B" Directory="dir90EFFBA81E5867E3E251FBDCB82DBC0C" Guid="{6EBE96D6-0BC9-4EA2-9990-CF184EC5958E}">
+                <File Id="filCF87AAEC5D091D6D8852B61917DB3BBF" KeyPath="yes" Source="SourceDir\webview\qtwebview_webengine.dll" />
+            </Component>
+        </ComponentGroup>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dir9D005E7F3893047E4557D6C5273ADD9E">
+            <Directory Id="dir005903123E8E65ADACEE188EEB8D5FED" Name="NativeStyle" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dir70AFC0A9D84A710EC0128D3FED451CDC">
+            <Directory Id="dir040BCEF4BA1A47FD347F1E983F275641" Name="WorkerScript" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dirDCD6F45EC68D7ABC2968E38BA6C2E3B4">
+            <Directory Id="dir124EAE5849AC3D2D22C86A18E3063919" Name="Universal" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dir4EBCE940C66AD416A8F8D45D795DED89">
+            <Directory Id="dir24D778EAF9E4AD78E4448B53856DC5AB" Name="private" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dir9D005E7F3893047E4557D6C5273ADD9E">
+            <Directory Id="dir25E537F6AD7990426F898BAE24EACE9D" Name="Layouts" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dir8F7FCDAEC833D8963B08029F5D194C05">
+            <Directory Id="dir2E19425B618D245C054C135AF0EF7016" Name="ControlsDelegates" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dir5A065D859888E6B36CD8ABD4BF35B615">
+            <Directory Id="dir2FA1B14588FEA7C153D23281391CE63E" Name="+Universal" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dirDCD6F45EC68D7ABC2968E38BA6C2E3B4">
+            <Directory Id="dir3F609D3494FEF5EC2888E9B865EA8336" Name="Imagine" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dir124EAE5849AC3D2D22C86A18E3063919">
+            <Directory Id="dir40011B386F4B1F0F2AE25C2E3ADB18E8" Name="impl" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dirB7DD0BB42FA107F6BC6439938BFE98A2">
+            <Directory Id="dir48925D264F208B9FCBF827D59CF75478" Name="impl" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dir70AFC0A9D84A710EC0128D3FED451CDC">
+            <Directory Id="dir49DBFE81A3C2546F0A0D7B91984B25A9" Name="Models" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dirDF4CFBD3627B084EE34DEDC7F0745F0E">
+            <Directory Id="dir4EBCE940C66AD416A8F8D45D795DED89" Name="GraphicalEffects" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dir5A065D859888E6B36CD8ABD4BF35B615">
+            <Directory Id="dir54FC92B404B993A4BC39A98D15582E79" Name="+Imagine" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dir005903123E8E65ADACEE188EEB8D5FED">
+            <Directory Id="dir58487975ACF4D38FB978C7C039C7F158" Name="controls" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dir9B4F25D95E0FA7C4E83388EAF889E777">
+            <Directory Id="dir5A065D859888E6B36CD8ABD4BF35B615" Name="qml" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dirDCD6F45EC68D7ABC2968E38BA6C2E3B4">
+            <Directory Id="dir5BF786D89AC746B54F9B967AA07926B8" Name="Windows" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dirDCD6F45EC68D7ABC2968E38BA6C2E3B4">
+            <Directory Id="dir68B807445C3B5B5D97077A041B375DC5" Name="impl" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dirDCD6F45EC68D7ABC2968E38BA6C2E3B4">
+            <Directory Id="dir7849AFCA9B74D36A839FBB59A8264BFC" Name="Basic" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dir5A065D859888E6B36CD8ABD4BF35B615">
+            <Directory Id="dir7931425F6E509D7B972EE970C5C8EE72" Name="+Fusion" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dir3F609D3494FEF5EC2888E9B865EA8336">
+            <Directory Id="dir7970C160330F95B9F7E3CCDAB2CF5702" Name="impl" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dir5A065D859888E6B36CD8ABD4BF35B615">
+            <Directory Id="dir8024AEF9D92D06355510B5D11BC9CB61" Name="+Material" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dir9D005E7F3893047E4557D6C5273ADD9E">
+            <Directory Id="dir8A47127E3442CB3A3D754FBB35FF92DC" Name="Templates" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dir9D005E7F3893047E4557D6C5273ADD9E">
+            <Directory Id="dir90B3BDF6AA234878B0614AA16A2106A5" Name="Particles" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dirC14EEB8602D1812B8846FA3737C83C55">
+            <Directory Id="dir9B4F25D95E0FA7C4E83388EAF889E777" Name="quickimpl" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dir1F7AD7AA9B228AED2E74CDD7C3088B5C">
+            <Directory Id="dirA016FBC978C7F28C3C5CDBC06002A5D6" Name="qtwebengine_locales" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dir9D005E7F3893047E4557D6C5273ADD9E">
+            <Directory Id="dirABDE04EACFBE2E08EF0F50035924A8DF" Name="Shapes" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dirDCD6F45EC68D7ABC2968E38BA6C2E3B4">
+            <Directory Id="dirB7DD0BB42FA107F6BC6439938BFE98A2" Name="Fusion" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dir9D005E7F3893047E4557D6C5273ADD9E">
+            <Directory Id="dirC14EEB8602D1812B8846FA3737C83C55" Name="Dialogs" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dir9D005E7F3893047E4557D6C5273ADD9E">
+            <Directory Id="dirC8FA895F8CD333726C0D487F219EA7A7" Name="tooling" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dir9D005E7F3893047E4557D6C5273ADD9E">
+            <Directory Id="dirD15C819041ADE7ED5CEE37C13C9C814C" Name="Window" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dir9D005E7F3893047E4557D6C5273ADD9E">
+            <Directory Id="dirD2281A4769E4849962EB43101484B62C" Name="LocalStorage" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dirF45E3860752A8B1AC79318D838CCE535">
+            <Directory Id="dirD4CF5DF9BDC9CA55E064A8C389B40501" Name="impl" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dir7849AFCA9B74D36A839FBB59A8264BFC">
+            <Directory Id="dirDBD23FE4F11ED29A51E2F3787280CC12" Name="impl" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dir9D005E7F3893047E4557D6C5273ADD9E">
+            <Directory Id="dirDCD6F45EC68D7ABC2968E38BA6C2E3B4" Name="Controls" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dir70AFC0A9D84A710EC0128D3FED451CDC">
+            <Directory Id="dirDF3BB8A52CE36475D7FA56568CBEA7A4" Name="XmlListModel" />
+        </DirectoryRef>
+    </Fragment>
+    <Fragment>
+        <DirectoryRef Id="dirDCD6F45EC68D7ABC2968E38BA6C2E3B4">
+            <Directory Id="dirF45E3860752A8B1AC79318D838CCE535" Name="Material" />
+        </DirectoryRef>
+    </Fragment>
+</Wix>
\ No newline at end of file