diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 6192f27dfd..cc71eb7d26 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -45,7 +45,7 @@ jobs:
steps:
- name: Checkout
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
with:
submodules: false
@@ -176,10 +176,11 @@ jobs:
- name: Coveralls
if: ${{ startsWith( matrix.task, 'coverage') }}
- uses: coverallsapp/github-action@1.1.3
+ uses: coverallsapp/github-action@v2
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
- path-to-lcov: coverage/coverage.info
+ file: coverage/coverage.info
+ format: lcov
- name: Install
if: ${{ ! startsWith( matrix.task, 'coverage') }}
@@ -202,7 +203,7 @@ jobs:
- name: Store original install artifacts before stripping and AppImage
# Activate for debugging
if: ${{ false && ! startsWith( matrix.task, 'coverage') }}
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: ${{ matrix.task }}-files
path: ${{ env.INSTALL_ROOT }}
@@ -246,7 +247,7 @@ jobs:
- name: Store AppImage artifacts
if: ${{ ! startsWith( matrix.task, 'coverage') }}
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: ${{ matrix.task }}-AppImage
path: qlcplus-${{env.TASK}}-${{env.APPVERSION}}-${{env.BUILD_DATE}}-${{env.GIT_REV}}.AppImage
@@ -266,12 +267,12 @@ jobs:
QMAKESPEC: win32-g++
QT_MODULES:
qtscript
- CC: /mingw32/bin/i686-w64-mingw32-gcc.exe
- CXX: /mingw32/bin/i686-w64-mingw32-g++.exe
+ CC: /mingw64/bin/x86_64-w64-mingw32-gcc.exe
+ CXX: /mingw64/bin/x86_64-w64-mingw32-g++.exe
steps:
- name: Checkout
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
with:
submodules: false
@@ -321,52 +322,52 @@ jobs:
- name: Update and install MSYS2
uses: msys2/setup-msys2@v2
with:
- msystem: mingw32
+ msystem: mingw64
release: true
update: false
path-type: inherit
install: >-
wget
unzip
- mingw-w64-i686-gcc
- mingw-w64-i686-gcc-libs
- mingw-w64-i686-cmake
- mingw-w64-i686-libmad
- mingw-w64-i686-libsndfile
- mingw-w64-i686-flac
- mingw-w64-i686-fftw
- mingw-w64-i686-libusb
- mingw-w64-i686-python-lxml
- mingw-w64-i686-qt5-base
- mingw-w64-i686-qt5-multimedia
- mingw-w64-i686-qt5-serialport
- mingw-w64-i686-qt5-script
- mingw-w64-i686-qt5-tools
- mingw-w64-i686-qt5-imageformats
- mingw-w64-i686-qt5-svg
- mingw-w64-i686-qt5-declarative
- mingw-w64-i686-qt5-quickcontrols
- mingw-w64-i686-qt5-quickcontrols2
- mingw-w64-i686-qt5-3d
- mingw-w64-i686-qt5-quick3d
- mingw-w64-i686-nsis
+ mingw-w64-x86_64-gcc
+ mingw-w64-x86_64-gcc-libs
+ mingw-w64-x86_64-cmake
+ mingw-w64-x86_64-libmad
+ mingw-w64-x86_64-libsndfile
+ mingw-w64-x86_64-flac
+ mingw-w64-x86_64-fftw
+ mingw-w64-x86_64-libusb
+ mingw-w64-x86_64-python-lxml
+ mingw-w64-x86_64-qt5-base
+ mingw-w64-x86_64-qt5-multimedia
+ mingw-w64-x86_64-qt5-serialport
+ mingw-w64-x86_64-qt5-script
+ mingw-w64-x86_64-qt5-tools
+ mingw-w64-x86_64-qt5-imageformats
+ mingw-w64-x86_64-qt5-svg
+ mingw-w64-x86_64-qt5-declarative
+ mingw-w64-x86_64-qt5-quickcontrols
+ mingw-w64-x86_64-qt5-quickcontrols2
+ mingw-w64-x86_64-qt5-3d
+ mingw-w64-x86_64-qt5-quick3d
+ mingw-w64-x86_64-nsis
- name: D2XX SDK
shell: msys2 {0}
run: |
- set MSYSTEM=MINGW32
- mkdir -p /c/Qt/D2XXSDK
- wget https://ftdichip.com/wp-content/uploads/2023/09/CDM-v2.12.36.4-WHQL-Certified.zip -O /c/Qt/D2XXSDK/cdm.zip
- cd /c/Qt/D2XXSDK
+ set MSYSTEM=MINGW64
+ mkdir -p /c/projects/D2XXSDK
+ wget https://ftdichip.com/wp-content/uploads/2023/09/CDM-v2.12.36.4-WHQL-Certified.zip -O /c/projects/D2XXSDK/cdm.zip
+ cd /c/projects/D2XXSDK
unzip cdm.zip
- cd i386
- gendef.exe - ftd2xx.dll > ftd2xx.def
- dlltool -k --input-def ftd2xx.def --dllname ftd2xx.dll --output-lib libftd2xx.a
+ cd amd64
+ gendef.exe - ftd2xx64.dll > ftd2xx.def
+ dlltool -k --input-def ftd2xx.def --dllname ftd2xx64.dll --output-lib libftd2xx.a
- name: Print program versions
shell: msys2 {0}
run: |
- set MSYSTEM=MINGW32
+ set MSYSTEM=MINGW64
echo "pwd:"
pwd
echo "CXX:"
@@ -381,19 +382,21 @@ jobs:
#if: false
shell: msys2 {0}
run: |
- set MSYSTEM=MINGW32
+ set MSYSTEM=MINGW64
+ # force a release build
+ sed -i -e 's/Debug/Release/g' CMakeLists.txt
# disable Velleman plugin
sed -i -e 's/ add_subdirectory(velleman)/# add_subdirectory(velleman)/g' plugins/CMakeLists.txt
# fix MSYS2 system path
sed -i -e 's/$ENV{SystemDrive}\/msys64/D:\/a\/_temp\/msys64/g' platforms/windows/CMakeLists.txt
# fix project path in NSIS script
- sed -i -e 's/c\:\\Qt/d:\\a\\qlcplus/g' platforms/windows/${{env.NSIS_SCRIPT}}
+ sed -i -e 's/c\:\\projects/d:\\a\\qlcplus/g' platforms/windows/${{env.NSIS_SCRIPT}}
- name: Configure v4 build for Windows
shell: msys2 {0}
if: ${{ matrix.task == 'compile-qt5' }}
run: |
- set MSYSTEM=MINGW32
+ set MSYSTEM=MINGW64
mkdir build
cd build
cmake -G "Unix Makefiles" ..
@@ -402,7 +405,7 @@ jobs:
shell: msys2 {0}
if: ${{ matrix.task == 'compile-qt5qml' }}
run: |
- set MSYSTEM=MINGW32
+ set MSYSTEM=MINGW64
mkdir build
cd build
cmake -G "Unix Makefiles" -Dqmlui=ON ..
@@ -410,14 +413,14 @@ jobs:
- name: Build for Windows
shell: msys2 {0}
run: |
- set MSYSTEM=MINGW32
+ set MSYSTEM=MINGW64
cd build
make -j${NPROC}
- name: Install on Windows
shell: msys2 {0}
run: |
- set MSYSTEM=MINGW32
+ set MSYSTEM=MINGW64
#echo 'Silently installing QLC+...'
cd build
make install/fast
@@ -427,14 +430,14 @@ jobs:
- name: Build installation package
shell: msys2 {0}
run: |
- set MSYSTEM=MINGW32
+ set MSYSTEM=MINGW64
cd /c/qlcplus
echo 'Creating package...'
makensis -X'SetCompressor /FINAL lzma' ${{env.NSIS_SCRIPT}}
mv /c/qlcplus/${{env.OUTFILE}} /d/a/qlcplus/qlcplus
- name: Store executable artifact
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: QLC+-${{env.TASK}}-${{env.APPVERSION}}-${{env.BUILD_DATE}}-${{env.GIT_REV}}.exe
path: ${{env.OUTFILE}}
diff --git a/CMakeLists.txt b/CMakeLists.txt
index a6c93a4e84..db94657018 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,9 +1,9 @@
cmake_minimum_required(VERSION 3.16)
-project(qlcplus VERSION 4.12.8 LANGUAGES C CXX)
+project(qlcplus VERSION 4.13.0 LANGUAGES C CXX)
# Set Release build type by default
if(NOT CMAKE_BUILD_TYPE)
- set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE)
+ set(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE)
endif()
# Prevent CMake make install strips off non-standard build paths
@@ -20,6 +20,18 @@ if(UNIX)
endif()
endif()
+if (ANDROID OR IOS)
+ set(qmlui ON)
+endif()
+
+if (ANDROID)
+ if(QT_VERSION_MAJOR GREATER 5)
+ set(QT_ANDROID_PACKAGE_SOURCE_DIR ${PROJECT_SOURCE_DIR}/platforms/android CACHE INTERNAL "")
+ else()
+ set(ANDROID_PACKAGE_SOURCE_DIR ${PROJECT_SOURCE_DIR}/platforms/android CACHE INTERNAL "")
+ endif()
+endif()
+
set(CMAKE_INCLUDE_CURRENT_DIR ON)
# Set up AUTOMOC and some sensible defaults for runtime execution
@@ -32,6 +44,9 @@ find_package(QT NAMES Qt5 Qt6 REQUIRED COMPONENTS Core)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Gui Multimedia MultimediaWidgets Network PrintSupport Qml Quick Svg Test Widgets LinguistTools)
if(qmlui)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS 3DCore 3DInput 3DQuick 3DQuickExtras 3DRender)
+ if(ANDROID)
+ find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Concurrent OpenGL)
+ endif()
endif()
message("Found Qt version ${QT_VERSION_MAJOR}: ${QT_DIR}")
diff --git a/README.md b/README.md
index 5361556bc8..a6685dcf0f 100644
--- a/README.md
+++ b/README.md
@@ -3,17 +3,26 @@
# Q Light Controller Plus
-![GitHub release)](https://img.shields.io/github/v/release/mcallegari/qlcplus)
-![QLC+ Github Actions CI Build](https://github.com/mcallegari/qlcplus/actions/workflows/build.yml/badge.svg)
+[![GitHub release](https://img.shields.io/github/v/release/mcallegari/qlcplus)
+![GitHub Release Date - Published_At](https://img.shields.io/github/release-date/mcallegari/qlcplus)](https://github.com/mcallegari/qlcplus/releases/latest)
+
+https://www.qlcplus.org/download
## Introduction
-QLC+ is a powerful and user-friendly software designed for lighting control. Whether you're an experienced lighting professional or just getting started, QLC+ empowers you to take control of your lighting fixtures with ease. The primary goal of this project is to bring QLC+ to the level of available commercial software.
+QLC+ is powerful and user-friendly software designed to control lighting. Whether you're an experienced lighting professional or just getting started, QLC+ empowers you to take control of your lighting fixtures with ease. The primary goal of this project is to bring QLC+ to the level of available commercial software.
QLC+ runs on Linux, Windows (7+), macOS (10.7+) and the Raspberry Pi.
Copyright © Heikki Junnila, Massimo Callegari
-
-
-### Key Resources:
+### Supported Protocols
+[![MIDI](https://img.shields.io/badge/MIDI-%23323330.svg?style=for-the-badge&logo=midi&logoColor=%23F7DF1E)](https://docs.qlcplus.org/v4/plugins/midi)
+[![OSC](https://img.shields.io/badge/OSC-%23323330.svg?style=for-the-badge&logo=aiohttp&logoColor=%23F7DF1E)](https://docs.qlcplus.org/v4/plugins/osc)
+[![HID](https://img.shields.io/badge/HID-%23323330.svg?style=for-the-badge&logo=applearcade&logoColor=%23F7DF1E)](https://docs.qlcplus.org/v4/plugins/hid)
+[![DMX](https://img.shields.io/badge/DMX-%23323330.svg?style=for-the-badge&logo=amazonec2&logoColor=%23F7DF1E)](https://docs.qlcplus.org/v4/plugins/dmx-usb)
+[![ArtNet](https://img.shields.io/badge/ArtNet-%23323330.svg?style=for-the-badge&logo=aiohttp&logoColor=%23F7DF1E)](https://docs.qlcplus.org/v4/plugins/art-net)
+[![E1.31/S.ACN](https://img.shields.io/badge/E1.31%20S.ACN-%23323330.svg?style=for-the-badge&logo=aiohttp&logoColor=%23F7DF1E)](https://docs.qlcplus.org/v4/plugins/e1-31-sacn)
+[![OS2L](https://img.shields.io/badge/OS2L-%23323330.svg?style=for-the-badge&logo=aiohttp&logoColor=%23F7DF1E)](https://docs.qlcplus.org/v4/plugins/os2l)
+
+### Key Resources
@@ -41,71 +50,60 @@ Copyright © Heikki Junnila, Massimo Callegari
-### QLC+ Social Media:
+### QLC+ Social Media
[![Instagram](https://img.shields.io/badge/Instagram-%23E4405F.svg?style=for-the-badge&logo=Instagram&logoColor=white)](https://www.instagram.com/qlcplus/) [![YouTube](https://img.shields.io/badge/YouTube-%23FF0000.svg?style=for-the-badge&logo=YouTube&logoColor=white)](https://www.youtube.com/watch?v=I9bccwcYQpM&list=PLHT-wIriuitDiW4A9oKSDr__Z_jcmMVdi) [![Facebook](https://img.shields.io/badge/Facebook-%231877F2.svg?style=for-the-badge&logo=Facebook&logoColor=white)](https://www.facebook.com/qlcplus)
-
-
## Contributing
We welcome contributions from the community to help make QLC+ even better. Before diving into coding, we encourage you to start a discussion in our [Software Development](https://www.qlcplus.org/forum/viewforum.php?f=12) forum if you're considering adding a new feature or making significant changes. This provides an opportunity for feedback, collaboration, and ensuring alignment with the project's goals.
Further guidelines are available in the [CONTRIBUTING.md](CONTRIBUTING.md) document.
+### Help wanted
+Click the badge below to see the currently confirmed issues with QLC+. Perhaps you can find a solution?
+
+[![GitHub issues by-label](https://img.shields.io/github/issues/mcallegari/qlcplus/issue%20confirmed?logo=github&color=red)](https://github.com/mcallegari/qlcplus/issues?q=is%3Aopen+is%3Aissue+label%3A%22issue+confirmed%22)
-### 🚧 DEVELOPERS AT WORK 🚧
+### 🚧 Developers at work 🚧
+If you're regularly updating QLC+ sources with git pull, you may encounter compiler warnings, errors, or unresolved symbols. This is because the source package is still in development. We strive to keep the GIT master branch free of critical errors; However, dependencies between objects can sometimes cause issues, requiring a full package recompilation rather than just updating recent changes.
-If you're compiling QLC+ from sources and you regularly do "git pull"
-to get the latest sources, you probably end up seeing some
-compiler warnings and errors from time to time. Since the whole source package
-is under development, you might even encounter unresolved symbols etc. If such a thing occurs, you should do a "make
-distclean" on qlcplus (top-most source directory) and then "qmake" and "make"
-again. We attempt to keep the GIT master free of fatal errors and it should
-compile all the time. However, some inter-object dependencies do get mixed up
-sometimes and you need to compile the whole package instead of just the latest
-changes. Sometimes even that doesn't work, because QLC+ installs its common
-libraries to system directories, where (at least unixes) fetch them instead
-of the source directory. In those cases, you might try going to the libs
-directory, compile it with "make" and install with "make install" and then
-attempt to re-compile the whole package with "make".
+[![QLC+ Github Actions CI Build](https://github.com/mcallegari/qlcplus/actions/workflows/build.yml/badge.svg)](https://github.com/mcallegari/qlcplus/actions) [![Coverage Status](https://coveralls.io/repos/github/mcallegari/qlcplus/badge.svg?branch=master)](https://coveralls.io/github/mcallegari/qlcplus?branch=master)
+[![GitHub commits since latest release (by SemVer including pre-releases)](https://img.shields.io/github/commits-since/mcallegari/qlcplus/latest/master)](https://github.com/mcallegari/qlcplus/commits/master/) ![GitHub commit activity (branch)](https://img.shields.io/github/commit-activity/w/mcallegari/qlcplus)
-## Compiling And Installation
+## Compiling and installation
Please refer to the online wiki pages: https://github.com/mcallegari/qlcplus/wiki
## Requirements
### Linux
-
-* Qt >= 5.0 development libraries & tools
-* libudev-dev, libmad0-dev, libsndfile1-dev, libfftw3-dev
-* DMX USB plugin: libftdi-dev, pkg-config
-* MIDI plugin: libasound, libasound-dev, pkg-config
-* OLA plugin: libola, ola-dev, pkg-config (see libs/olaout/README)
-* uDMX plugin: libusb, libusb-dev, pkg-config
-* Peperoni plugin: libusb, libusb-dev, pkg-config
-* Velleman plugin: **Not available**
+* Qt >= 5.0 development libraries & tools
+* libudev-dev, libmad0-dev, libsndfile1-dev, libfftw3-dev
+* DMX USB plugin: libftdi-dev, pkg-config
+* MIDI plugin: libasound, libasound-dev, pkg-config
+* OLA plugin: libola, ola-dev, pkg-config (see libs/olaout/README)
+* uDMX plugin: libusb, libusb-dev, pkg-config
+* Peperoni plugin: libusb, libusb-dev, pkg-config
+* Velleman plugin: **Not available**
### Windows
-* MSYS2 environment (https://msys2.github.io/)
-* DMX USB plugin: D2XX driver & development package (http://www.ftdichip.com/Drivers/D2XX.htm)
-* ENTTEC Wing plugin: D2XX driver & development package (http://www.ftdichip.com/Drivers/D2XX.htm)
-* OLA plugin: **Not available**
-* Velleman plugin: K8062 SDK from www.velleman.eu
-
+* MSYS2 environment (https://msys2.github.io/)
+* DMX USB plugin: D2XX driver & development package (http://www.ftdichip.com/Drivers/D2XX.htm)
+* ENTTEC Wing plugin: D2XX driver & development package (http://www.ftdichip.com/Drivers/D2XX.htm)
+* OLA plugin: **Not available**
+* Velleman plugin: K8062 SDK from www.velleman.eu
### Mac OS X
-* XCode (http://developer.apple.com/technologies/tools/xcode.html)
-* Qt >= 5.0.x (http://download.qt.io/official_releases/qt/)
-* macports (https://www.macports.org/)
-* DMX USB plugin: macports, libftdi-dev, pkg-config
-* OLA plugin: libola, ola-dev, pkg-config (see libs/olaout/README)
-* uDMX plugin: macports, libusb-compat, pkg-config
-* Peperoni plugin: macports, libusb-compat, pkg-config
-* Velleman plugin: **Not available**
-
+* XCode (http://developer.apple.com/technologies/tools/xcode.html)
+* Qt >= 5.0.x (http://download.qt.io/official_releases/qt/)
+* macports (https://www.macports.org/)
+* DMX USB plugin: macports, libftdi-dev, pkg-config
+* OLA plugin: libola, ola-dev, pkg-config (see libs/olaout/README)
+* uDMX plugin: macports, libusb-compat, pkg-config
+* Peperoni plugin: macports, libusb-compat, pkg-config
+* Velleman plugin: **Not available**
## Support & Bug Reports
@@ -116,58 +114,64 @@ For developers wiki and code patches, go to:
https://github.com/mcallegari/qlcplus
## Contributors
+
QLC+ owes its success to the dedication and expertise of numerous individuals who have generously contributed their time and skills. The following list recognizes those whose remarkable contributions have played a pivotal role in shaping QLC+ into what it is today.
-### QLC+ 5:
-
-* Eric Arnebäck (3D preview features)
-* Santiago Benejam Torres (Catalan translation)
-* Luis García Tornel (Spanish translation)
-* Nils Van Zuijlen, Jérôme Lebleu (French translation)
-* Felix Edelmann, Florian Edelmann (fixture definitions, German translation)
-* Jannis Achstetter (German translation)
-* Dai Suetake (Japanese translation)
-* Hannes Bossuyt (Dutch translation)
-* Aleksandr Gusarov (Russian translation)
-* Vadim Syniuhin (Ukrainian translation)
-* Mateusz Kędzierski (Polish translation)
-
-### QLC+ 4:
-
-* Jano Svitok (bugfix, new features and improvements)
-* David Garyga (bugfix, new features and improvements)
-* Lukas Jähn (bugfix, new features)
-* Robert Box (fixtures review)
-* Thomas Achtner (ENTTEC wing improvements)
-* Joep Admiraal (MIDI SysEx init messages, Dutch translation)
-* Florian Euchner (FX5 USB DMX support)
-* Stefan Riemens (new features)
-* Bartosz Grabias (new features)
-* Simon Newton, Peter Newman (OLA plugin)
-* Janosch Frank (webaccess improvements)
-* Karri Kaksonen (DMX USB Eurolite USB DMX512 Pro support)
-* Stefan Krupop (HID DMXControl Projects e.V. Nodle U1 support)
-* Nathan Durnan (RGB scripts, new features)
-* Giorgio Rebecchi (new features)
-* Florian Edelmann (code cleanup, German translation)
-* Heiko Fanieng, Jannis Achstetter (German translation)
-* NiKoyes, Jérôme Lebleu, Olivier Humbert, Nils Van Zuijlen (French translation)
-* Raymond Van Laake (Dutch translation)
-* Luis García Tornel (Spanish translation)
-* Jan Lachman (Czech translation)
-* Nuno Almeida, Carlos Eduardo Porto de Oliveira (Portuguese translation)
-* Santiago Benejam Torres (Catalan translation)
-* Koichiro Saito, Dai Suetake (Japanese translation)
-
-### QLC:
-
-* Stefan Krumm (Bugfixes, new features)
-* Christian Suehs (Bugfixes, new features)
-* Christopher Staite (Bugfixes)
-* Klaus Weidenbach (Bugfixes, German translation)
-* Lutz Hillebrand (uDMX plugin)
-* Matthew Jaggard (Velleman plugin)
-* Ptit Vachon (French translation)
+![GitHub contributors](https://img.shields.io/github/contributors/mcallegari/qlcplus)
+### QLC+ 5
+
+* Eric Arnebäck (3D preview features)
+* Santiago Benejam Torres (Catalan translation)
+* Luis García Tornel (Spanish translation)
+* Nils Van Zuijlen, Jérôme Lebleu (French translation)
+* Felix Edelmann, Florian Edelmann (fixture definitions, German translation)
+* Jannis Achstetter (German translation)
+* Dai Suetake (Japanese translation)
+* Hannes Bossuyt (Dutch translation)
+* Aleksandr Gusarov (Russian translation)
+* Vadim Syniuhin (Ukrainian translation)
+* Mateusz Kędzierski (Polish translation)
+
+### QLC+ 4
+
+* Jano Svitok (bugfix, new features and improvements)
+* David Garyga (bugfix, new features and improvements)
+* Lukas Jähn (bugfix, new features)
+* Robert Box (fixtures review)
+* Thomas Achtner (ENTTEC wing improvements)
+* Joep Admiraal (MIDI SysEx init messages, Dutch translation)
+* Florian Euchner (FX5 USB DMX support)
+* Stefan Riemens (new features)
+* Bartosz Grabias (new features)
+* Simon Newton, Peter Newman (OLA plugin)
+* Janosch Frank (webaccess improvements)
+* Karri Kaksonen (DMX USB Eurolite USB DMX512 Pro support)
+* Stefan Krupop (HID DMXControl Projects e.V. Nodle U1 support)
+* Nathan Durnan (RGB scripts, new features)
+* Giorgio Rebecchi (new features)
+* Florian Edelmann (code cleanup, German translation)
+* Heiko Fanieng, Jannis Achstetter (German translation)
+* NiKoyes, Jérôme Lebleu, Olivier Humbert, Nils Van Zuijlen (French translation)
+* Raymond Van Laake (Dutch translation)
+* Luis García Tornel (Spanish translation)
+* Jan Lachman (Czech translation)
+* Nuno Almeida, Carlos Eduardo Porto de Oliveira (Portuguese translation)
+* Santiago Benejam Torres (Catalan translation)
+* Koichiro Saito, Dai Suetake (Japanese translation)
+
+### Q Light Controller
+
+* Stefan Krumm (Bugfixes, new features)
+* Christian Suehs (Bugfixes, new features)
+* Christopher Staite (Bugfixes)
+* Klaus Weidenbach (Bugfixes, German translation)
+* Lutz Hillebrand (uDMX plugin)
+* Matthew Jaggard (Velleman plugin)
+* Ptit Vachon (French translation)
+
+
+
+
## Apache 2.0
![GitHub License](https://img.shields.io/github/license/mcallegari/qlcplus)
diff --git a/create-dmg.sh b/create-dmg.sh
index 4e14a3edd8..9aba03f771 100755
--- a/create-dmg.sh
+++ b/create-dmg.sh
@@ -42,13 +42,13 @@ fi
OUTDIR=$PWD
cd platforms/macos/dmg
./create-dmg --volname "Q Light Controller Plus $VERSION" \
- --volicon $OUTDIR/resources/icons/qlcplus.icns \
- --background background.png \
- --window-size 400 300 \
- --window-pos 200 100 \
- --icon-size 64 \
- --icon "QLC+" 0 150 \
- --app-drop-link 200 150 \
- $OUTDIR/QLC+_$VERSION.dmg \
- ~/QLC+.app
+ --volicon $OUTDIR/resources/icons/qlcplus.icns \
+ --background background.png \
+ --window-size 400 300 \
+ --window-pos 200 100 \
+ --icon-size 64 \
+ --icon "QLC+" 0 150 \
+ --app-drop-link 200 150 \
+ $OUTDIR/QLC+_$VERSION.dmg \
+ ~/QLC+.app
cd -
diff --git a/debian/changelog b/debian/changelog
index d6ff84904f..72bf1c2d08 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,32 +1,68 @@
-qlcplus (4.12.8) stable; urgency=low
+qlcplus (4.13.1) stable; urgency=low
+
+ * engine: fix blackout not working
+ * engine: include relative EFX in blackout
+ * engine: fix RGB Matrix clone control mode
+ * Show Manager: improve resume after pause
+ * Virtual Console: fix OSC feedback regression
+ * Virtual Console/Slider: add an optional button to flash in playback mode
+ * Virtual Console/XY Pad: copy presets when cloning (thanks to Hans-Jürgen Tappe)
+ * Plugins/DMX USB: restore Vince DMX512 output (thanks to Jérôme Lebleu)
+ * Web Access: added getWidgetSubIdList API and Animation widget sub-control example (API test page updated)
+ * New fixtures: beamZ BAC500 and BAC506 (thanks to Olivier Michel)
+ * New fixtures: American DJ Par Z4, beamZ SB400, OXO ColorBeam 7 FCW IR, Pro-Lights Pixie Spot (thanks to Dmitry Kolesnikov)
+ * New fixtures: BoomToneDJ LED PAR 7X10W 5in1, BoomToneDJ Maxi Spot 60, Mac Mah FLAT PAR 7x12W 6in1, Eurolite LED PIX-16 QCL Bar (thanks to Cédric Monféfoul)
+ * New fixture: Showtec ACT PC 60 RGBW (thanks to Michel Sliepenbeek)
+ * New fixtures: Robe LEDBeam 350, Robe LEDBeam 350 RGBA, Briteq COB Blinder 2x100W, Ayrton MiniPanel FX (thanks to Giacomo Gorini)
+ * New fixture: Elation ELED B48 (thanks to Xoneoo)
+ * New fixture: beamZ PS10W (thanks to Jesper Korsen)
+ * New fixtures: Ayra ComPar 10 and ERO 406 (thanks to René Knuvers)
+ * New fixtures: Mac Mah Moving-FX Bar, Varytec LED Pad Bar Compact ST RGB, Laserworld EL-400RGB MK2 (thanks to Clément Delabroye)
+ * New fixtures: Electroconcept Club Scan 30, Club Scan 120, LED Blinder, Profile 120 Spot LED, Micro Spot 60 LED (thanks to Clément Delabroye)
+
+ -- Massimo Callegari Sun, 30 Jun 2024 12:13:14 +0200
+
+qlcplus (4.13.0) stable; urgency=low
* engine: fix Chaser random startup (thanks to Dennis Suermann)
* engine: do not fade out looped audio
+ * engine: further rework to properly handle 16bit fading
* engine: fix stopping audio with fade in and fade out while fading in
* engine: new EFX algorithm: SquareTrue (thanks to Justin Hornsby)
* engine: handle 'string' and 'float' types in RGB Scripts
+ * UI: save the geometry of all the dialogs (thanks to Nils Tijtgat)
+ * UI: add color lookup table to input profiles and a dedicated dialog for custom feedback
* Virtual Console/Slider: fix switching from playback to submaster mode
* Virtual Console/Slider: fix submaster @0 not affecting function intensity
* Virtual Console/XY Pad: fix Scene preset controlling wrong channels
* Virtual Console/Clock: fix running a schedule the day after
* Virtual Console/Button: Scene flashing can force LTP and override (thanks to Dennis Suermann)
+ * Virtual Console/Button: add monitoring feedback value to custom feedback (thanks to ditcheshurt)
* Virtual Console/Cue List: fix off by one offset error in steps mode (thanks to kpr0th)
+ * Virtual Console/Audio Triggers: fix attached VC Slider not updating values
+ * Virtual Console/Audio Triggers: fix loading a project with DMX bars with no channels set
+ * Virtual Console/Audio Triggers: fix enable button feedback to external controllers
* Plugins/ArtNet: add default standard transmission mode as per protocol specifications
* Plugins/ArtNet,E1.31,OSC: add a parameter to wait for interfaces to be ready
* Plugins/DMX USB: add support for DMXKing MAX products
* Plugins/DMX USB: FTDI USB device no longer disappear after closing QLC+ on Linux
* Fixture Editor: fix aliases not updated when renaming a mode
* Web Access: add support for Cue List side fader and buttons layout (thanks to Itay Lifshitz)
+ * Web Access: add support for Cue List note editing (thanks to Itay Lifshitz)
* Web Access: add support for Slider knob appearance (thanks to Itay Lifshitz)
- * Web Access: add VC Animation widget support (thanks to Itay Lifshitz)
+ * Web Access: add support for VC Frame disable button (thanks to Itay Lifshitz)
+ * Web Access: add Virtual Console Animation widget support (thanks to Itay Lifshitz)
+ * Web Access: add Virtual Console Grand Master (thanks to Itay Lifshitz)
* Web Access: add event to notify Function start/stop
* Input profiles: added PMJ 9 Faders Controller, Circus and MidiKey
+ * Input profiles: added Worlde Easypad.12 (thanks to Christoph Müllner)
+ * Input profiles: added Worlde Orca PAD16
* New fixture: Ibiza Mini Moving Star Wash (thanks to Chris Shucksmith)
* New fixtures: FOS Technologies IQ Par, IQ 28x12 Wash, Iridium 75W Spot (thanks to Maurizio Aru)
* New fixture: Varytec Hero Spot 60 (thanks to Hans-Jürgen Tappe)
* New fixture: beamZ BAC503 (thanks to archlinette)
* New fixtures: Cameo Flat Pro 7, 12 and 18 (thanks to Janosch Frank)
- * New fixture: Eurolite LED TMH-X4 (thanks to Tolmino Muccitelli)
+ * New fixtures: Eurolite LED TMH-X4, lightmaXX Vector ARC Flood II (thanks to Tolmino Muccitelli)
* New fixtures: Cameo Q-Spot 40 RGBW, Varytec LED PAR 14x8W, Varytec LED Typhoon PAR Outdoor (12x10) (thanks to Jochen Becker)
* New fixtures: Audibax Iowa 70, Pro-Lights CromoWash100 (thanks to Cristian)
* New fixtures: Showtec Spectral M1000 Q4, Showtec Kanjo Wash RGB (thanks to Michel Sliepenbeek)
@@ -46,13 +82,33 @@ qlcplus (4.12.8) stable; urgency=low
* New fixture: Shehds GalaxyJet Waterproof IP65 380W 19R Beam Moving Head (thanks to István Király, Feiyu Shehds)
* New fixture: Martin Ego X6 (thanks to Michael Tosatto)
* New fixture: Blizzard Lighting LB Hex Unplugged (thanks to David Sparks)
- * New fixture: Eurolite LED Strobe SMD PRO 132 DMX RGB (thanks to Fede79)
+ * New fixtures: Eurolite LED Strobe SMD PRO 132 DMX RGB, Briteq BT Theatre HD2, Eurolite KLS-180-6, BoomToneDJ KUB 500 RGB (thanks to Fede79)
* New fixture: Eurolite LED PLL-480 CW/WW (thanks to Benjamin Drung)
* New fixture: Robe Spiider (thanks to Nicolò)
* New fixture: Betopper LB230 (thanks to Viktor)
+ * New fixtures: EK R3 Wash, Chauvet Intimidator Spot 475ZX, Chauvet Intimidator Wash Zoom 450 IRC (thanks to Harrison Bostock)
* New fixtures: Varytec Typhoon True Kid 720Z RGBW IP65, Showtec Performer 2000 RGBAL (thanks to Clément Delabroye)
-
- -- Massimo Callegari Sun, 17 Dec 2023 12:13:14 +0200
+ * New fixtures: Showtec LED Par 64 Short V2, Bright XBAR (thanks to Øystein Steimler)
+ * New fixtures: AFX CLUB-MIX3 19x10W RGBW, Eurolite LED Theatre COB 200 RGB+WW (thanks to Florian Faber)
+ * New fixture: Chauvet COLORado Batten 72x (thanks to Greg Perrone)
+ * New fixtures: Talent SSL2, Cameo P2 FC
+ * New fixture: Tecshow Nebula 6 (thanks to Federico)
+ * New fixture: beamZ Radical II (thanks to Matt Muller)
+ * New fixtures: Eurolite LED PARty TCL spot, Expolite TourSpot 50 Mini, Fun Generation LED Pot 12x1W QCL RGB WW (thanks to Christian Prison)
+ * New fixtures: Chauvet COLORband Q3BT, Shehds LED Beam+Wash 19x15W RGBW Zoom (thanks to Paul Schuh)
+ * New fixtures: Eliminator Lighting Stealth Beam and Stealth Wash Zoom Lighting (thanks to Paul Schuh)
+ * New fixture: Varytec Blitz Bar 240 (thanks to Stefan Lohmann)
+ * New fixture: Eurolite LED KLS Scan Pro Next FX Light (thanks to Kevin)
+ * New fixtures: Elumen8 MP 60 Mk1, MP 60 Mk2, MP 120 (thanks to Keith Baker)
+ * New fixture: Elation Paladin (thanks to Nicholas Harvey)
+ * New fixtures: Acme Oxygen, Dotline180, Dotline260 and Super Dotline, Chauvet Intimidator Spot Duo 155 (thank to Michael Tosatto)
+ * New fixtures: Laserworld EL-900RGB, Chauvet COLORdash Par-Quad 18, Event Lighting StrobeX and StrobeX RGB (thank to Michael Tosatto)
+ * New fixture: Chauvet Wash FX Hex (thanks to Clément Delabroye)
+ * New fixture: Shehds Wash Zoom LED 36x18W RGBWA+UV (thanks to Ioannis Iliopoulos)
+ * New fixture: UKing ZQ-02319 (thanks to Mike Ubl)
+ * New fixtures: DTS Jack, Robe LEDBeam 350 (thanks to Tomas Hastings)
+
+ -- Massimo Callegari Sun, 17 Mar 2024 12:13:14 +0200
qlcplus (4.12.7) stable; urgency=low
@@ -487,538 +543,3 @@ qlcplus (4.12.0) stable; urgency=low
* New fixtures: Pro-Lights Genesis, BB5 Pix (thanks to Lorenzo Andreani)
-- Massimo Callegari Sat, 10 Nov 2018 12:13:14 +0200
-
-qlcplus (4.11.2) stable; urgency=low
-
- * engine: fix crash caused by an invalid IO mapping
- * engine: fix intensity override not considered during fade outs
- * UI/Function Manager: fixed keyboard shortcut conflicts and document them
- * UI/Function Manager: allow to import multiple of audio/video files at once
- * UI/Channel Groups: added expand/collapse all button helpers
- * UI/Chaser Editor: add a button to shuffle the selected Chaser steps (thanks to Felix Edelmann)
- * UI/RGBMatrix Editor: fix preview not updating on pattern change when play button is on
- * Show Manager: fix crash when adding a Sequence after deleting one
- * Show Manager: fix crash when editing a Sequence bound to a deleted Scene
- * Show Manager: fix items start time indication when dragging
- * Virtual Console/Slider: fix submaster initial value not applied and inverted mode
- * Virtual Console/Slider: added 'value catching' option for external controller faders (Lukas Jähn proposal)
- * Virtual Console/Slider: fix values range when switching between Slider and Knob appearance
- * Virtual Console/Slider: react on Scene flashing when in playback mode
- * Virtual Console/Knob: fix DMX values not updated when interacting with the mouse wheel
- * Virtual Console/Cue List: allow to select a step with next/previous buttons during pause
- * Virtual Console/Cue List: go to the right chaser step after a pause (thanks to Krzysztof Walo)
- * Virtual Console/Frame: fix regression preventing to send the disable feedback
- * Web Access: added support for VC Buttons in Flash mode (Sylvain Laugié)
- * Web Access: added support for VC Frame circular page scrolling (Sylvain Laugié)
- * Web Access: update AudioTriggers state when changed from QLC+ (Sylvain Laugié)
- * plugins/udmx: added 'channels' configuration parameter (see documentation)
- * plugins/E1.31: fix crash on wrong packet length (David Garyga)
- * New fixture: DTS XR7 Spot (thanks to Nicolò Zanon)
- * New fixture: Ledj Slimline 12Q5 Batten (thanks to Dean Clough)
- * New fixture: Ayra Compar Kit 1 (thanks to eigenaardiger)
- * New fixtures: GLP Impression X4 S, Eurolite LED KLS-2500 (thanks to Mitsch)
- * New fixture: Chauvet Intimidator Spot 255 IRC (thanks to Ham Sadler)
- * New fixtures: Chauvet Geyser RGB, Geyser P6 (thanks to Andrew)
- * New fixture: Chauvet Rotosphere Q3 (thanks to Eric Sherlock)
- * New fixture: Showtec Compact Par 7 Q4 (thanks to Alexander)
- * New fixture: Contest Delirium (thanks to Vincent)
- * New fixture: Solena Mini Par 12, Max Bar 28 RGB (thanks to Nathan Durnan)
- * New fixtures: Showtec Phantom 65, Laserworld PRO-800RGB (thanks to Piotr Nowik)
- * New fixture: Chauvet MiN Spot RGBW (thanks to Jungle Jim)
- * New fixtures: Showtec Shark Wash One, American DJ Vizi Hex Wash7 (thanks to Georg Müller)
- * New fixture: Showtec Shark Beam FX One (thanks to Mats Lourenco)
- * New fixture: Stairville novaWash Quad LED (thanks to Luke Bonett)
- * New fixtures: Eurolite Party TCL Spot RGB, Expolite TourSpot 60, Expolite TourStick 72 RGBWA (thanks to Dirk J)
- * New fixture: Chauvet Hemisphere 5.1, Trident, Scorpion Storm RGX (thanks to Francois Blanchette)
- * New fixture: Briteq COB Slim 100-RGB (thanks to Thierry)
- * New fixture: American DJ UB 12H (thanks to Jason R Johnston)
- * New fixture: American DJ Mega Hex Par (thanks to Ben C)
- * New fixture: Cameo Q SPOT 15 RGBW (thanks to Antoine Houbron)
- * New fixture: lightmaXX Platinum Line Flat Par COB (thanks to Leonardo)
- * New fixture: Stairville LED Blinder 2 COB 2x65W (thanks to chritoep)
- * New fixture: lightmaXX LED PAR 64 (thanks to Johannes Felber)
- * New fixture: Cameo Thunder Wash Series (thanks to JP)
- * New fixtures: Briteq BT 575S, Stairville MH-x30 LED Beam (thanks to Andres Robles)
- * New fixture: beamZ LED FlatPAR-154 (thanks to Jászberényi Szabolcs)
- * New fixtures: Eurolite THA-100F COB, Cameo Tribar 200 IR (thanks to David Morgenschweis)
- * New fixture: beamZ BT310 LED FlatPAR 12x8W 4-1 DMX IR (thanks to Mark)
- * New fixture: Fun Generation PicoWash 40 Pixel Quad LED (thanks to Harm Aldick)
- * New fixtures: American DJ Entourage, Elumen8 MS-700PE, Ibiza PAR LED 712IR (thanks to Tim Cullingworth)
- * New fixtures: Martin MAC 401 Dual RGB Zoom, MAC 401 Dual CT Zoom, Stairville MH-z720 (thanks to Tim Cullingworth)
- * New fixture: Fun Generation SePar Quad UV (thanks to Helmet)
-
- -- Massimo Callegari Thu, 19 Apr 2018 20:21:22 +0200
-
-qlcplus (4.11.1) stable; urgency=low
-
- * engine: fixed audio files detection by prioritizing sndfile over mad
- * engine: fixed HTP/LTP forced channels not set correctly
- * engine: keep track of input/output device lines even if they are disconnected
- * engine/Script: add blackout:on and blackout:off commands (Jano Svitok)
- * engine/Script: do not keep empty trailing lines when saving a workspace
- * UI: it is now possible to detach a QLC+ context tab on a separate window by double clicking on it
- * UI/RGB Panel: added RBG pixel type (thanks to Peter Marks)
- * UI/Remap: fixed RGB Panels remapping
- * UI/Input Output Manager: added a button to enable/disable USB hotplugging (disabled by default)
- * UI/Function Live Edit: restore basic live editing of Sequences
- * UI/RGB Matrix Editor: fixed save to Sequence feature
- * UI/Function Manager: when cloning a Sequence, clone the bound Scene too
- * Virtual Console/Button: highlight border with orange color when in "monitoring" state
- * Virtual Console/Slider: fix DMX values not updated when interacting with the mouse wheel, keyboard or Click And Go button
- * Virtual Console/Slider: fix level mode values range scaling
- * Virtual Console/XYPad: the speed of a running EFX preset can now be controlled by a Speed Dial widget
- * RGB Scripts: added "Noise", "3D Starfield", "Random pixel per row" and "Random pixel per row multicolor" (thanks to Doug Puckett)
- * Web access: added basic authentication support (thanks to Bartosz Grabias)
- * Web access: fixed solo frames collapse state
- * Web access: update feedbacks when a slider is moved
- * New fixtures: IMG Stageline BEAM-40 WS/RGBW, Fun-Generation LED Diamond Dome (thanks to Tolmino Muccitelli)
- * New fixture: Elation Cuepix Batten (thanks to Saul Vielmetti)
- * New fixture: Clay Paky Tiger Scan HMI 575/1200 (thanks to Daris Tomasoni)
- * New fixture: Litecraft WashX.21 (thanks to Hannes Braun)
- * New fixtures: Briteq Stagepainter 12, Nicols IP Wash 120, Showtec LED Powerline 16 Bar (thanks to Fredje Gallon)
- * New fixtures: Nicols Movelight, Nicols Birdy Wash 122, Briteq Giga Flash RGB (thanks to Fredje Gallon)
- * New fixtures: Litecraft PowerBar AT10.sx, Stairville MH-z1915 (thanks to Thorben / Fredje)
- * New fixtures: Martin MAC 700 Wash, ADB Warp M (thanks to Thorben)
- * New fixture: Laserworld CS-1000RGB Mk II (thanks to Piotr Nowik)
- * New fixture: Chauvet COLORrail IRC (thanks to Lane Parsons)
- * New fixtures: American DJ COB Cannon Wash DW, lightmaXX Vega Zoom Wash Beam (thanks to Florian Gerstenlauer)
- * New fixture: Martin Rush MH5 Profile (thanks to Falko)
- * New fixture: Cameo Flash Bar 150 (thanks to Kevin Wimmer)
- * New fixtures: Chauvet FXpar 9, IMG Stageline Wash-40 LED (thanks to PeterK)
- * New fixtures: JB Systems iRock 5C, JB Systems LED Devil (thanks to Andres Robles)
- * New fixtures: beamZ BAC406, Geni Mojo Color Moc (thanks to Mark Sy)
- * New fixtures: Stairville MH-250 S, Chauvet GigBAR 2, Pro-Lights Onyx (thanks to Freasy)
- * New fixtures: Coemar ProSpot 250 LX, Showtec Kanjo Spot 60 (thanks to Flo Edelmann)
-
- -- Massimo Callegari Sat, 28 Oct 2017 12:13:14 +0200
-
-qlcplus (4.11.0) stable; urgency=low
-
- * engine: fixed setting start/end color while a RGB Matrix is running
- * engine: fixed crash when pausing a Show with an unavailable audio file
- * engine: major rework of Sequences. Projects using them need to be migrated
- * UI: enabled Alt key combinations on macOS to behave like other platforms (thanks to Matt Mayfield)
- * UI/RGB Panel: added panel direction (thanks to Raivis Rengelis)
- * UI/Fixture Manager: added weight and power consumption information on fixtures/universe selection (Chris de Rock idea)
- * UI/Scene Editor: preserve fixture tab order when fixtures with no channels set are present
- * UI/RGB Matrix Editor: allow the preview to run even in operate mode
- * UI/Audio Editor: added the possibility to loop an audio file (thanks to Raivis Rengelis)
- * UI/Simple Desk: fixed crash when changing values from a channel group in "fixtures view" mode
- * Virtual Console: prevent unwanted feedbacks from widgets in inactive Frame pages (thanks to Lukas Jähn)
- * Virtual Console: fixed manual selection of input channels not considering Frame pages (thanks to Lukas Jähn)
- * Virtual Console: fixed input profiles channels not honored on frame pages other than the first (thanks to Lukas Jähn)
- * Virtual Console/Slider: improved level monitoring with the possibility to act like a Simple Desk slider (see documentation)
- * Virtual Console/Frame: fixed 4.10.5b regression disabling widgets when switching page in design mode
- * Virtual Console/Frame: fixed key controls not copied when cloning a frame (thanks to Lukas Jähn)
- * Virtual Console/Frame: added the possibility to jump directly to a page and assign page names (thanks to Lukas Jähn)
- * Virtual Console/Cue List: improved linked crossfade to perform an additive blending between steps (see documentation)
- * Virtual Console/Speed Dial: improved tap button blinking and feedbacks (thanks to Lukas Jähn)
- * Virtual Console/Speed Dial: it is now possible to copy/paste factors (thanks to Jan Dahms)
- * Virtual Console/Clock: added external input support for countdown and stopwatch modes (thanks to Lukas Jähn)
- * Plugins/OSC: added channel number calculator in configuration page to help integrating new controllers
- * Plugins/Loopback: fixed spurious values emitted when a lot of channels are looped
- * Web access: fixed VC Slider in percentage mode and inverted appearance (thanks to Bartosz Grabias)
- * Web access: support VC Slider reduced range when in level mode
- * Web access: improved getChannelsValues and added Simple Desk reset per-channel (sdResetChannel API)
- * Web access: implemented keypad increase/decrease buttons (thanks to Santiago Benejam Torres)
- * New input profile: Zoom R16 (thanks to Benedict Stein)
- * New MIDI template: Akai APC40 MK2 Ableton mode (thanks to Branson Matheson)
- * New fixture: American DJ FREQ 5 Strobe (thanks to Martin Bochenek)
- * New fixture: Martin Rush MH3 (thanks to Ed Middlebrooks)
- * New fixture: Eurolite LED ACS BAR-12 (thanks to Michael Horber)
- * New fixtures: Martin Rush MH6 Wash, Cameo Studio PAR 64 RGBWA UV 12W (thanks to Piotr Nowik)
- * New fixtures: ETEC Moving Spot 60E, Cameo CLM PAR COB 1, Showtec Compact Power Lightset COB (thanks to Freasy)
- * New fixture: Stairville Tri Flat PAR Profile 5x3W RGB (thanks to Freasy)
- * New fixture: American DJ Punch LED Pro (thanks to Benedict Stein)
- * New fixtures: Contest Mini-Head 10W, Contest Evora B2R (thanks to Fredje Gallon)
- * New fixture: Robe DJ Scan 150 XT (thanks to Allan Madsen)
- * New fixtures: Futurelight PRO Slim PAR-12 HCL, PRO Slim PAR-12 MK2 HCL, Showtec Power Spot 9 Q5 (thanks to Lukas Jähn)
- * New fixture: Showtec XS-1W Mini Moving Beam (thanks to Habefaro)
- * New fixtures: Stage Right Stage Wash 18Wx18 LED PAR, Stage Right 7x20W COB LED Theater PAR (thanks to Collin Ong)
- * New fixture: Cameo CLPIXBAR450PRO, CLPIXBAR650PRO (thanks to Jean-Daniel Garcia & Jeremie Odermatt)
- * New fixture: Clay Paky Alpha Beam 1500 (thanks to Louis Gutenschwager)
- * New fixture: Stairville AFH-600 (thanks to Hannes Braun)
- * New fixture: Involight LED MH77S (thanks to Jászberényi Szabolcs)
- * New fixtures: ETEC LED PAR 64 18x10W RGBWA, LED PAR 64 18x15W RGBWA Zoom (thanks to Simon Orlob)
- * New fixture: Chauvet Swarm Wash FX (thanks to Stephen Olah)
- * New fixture: Clay Paky Alpha Spot HPE 575 (thanks to Rohmer)
- * New fixture: Robe LED Blinder 196LT (thanks to Tim Cullingworth)
- * New fixture: Chauvet COLORband T3 USB (thanks to Ian Nault)
- * New fixtures: American DJ Dotz Matrix, Martin Jem Compact Hazer Pro,Geni Mojo Spin Master Series (thanks to Sam Brooks)
- * New fixture: American DJ XS 400 (thanks to Jared)
- * New fixture: Velleman VDP1500SM (thanks to Freddy Hoogstoel)
- * New fixture: Chauvet Intimidator Spot 355Z IRC (thanks to Michael Clements)
- * New fixture: CLF Tricolor Mini Par (thanks to Jaron Blazer)
- * New fixture: Varytec LED Easy Move Mini Beam & Wash RGBW (thanks to Erik)
- * New fixtures: Smoke Factory Tour-Hazer II, JB Systems Panther, Robe Spot 160 XT (thanks to Thierry Rodolfo)
- * New fixture: American DJ LED Trispot (thanks to Patrick)
- * New fixtures: Contest STB-520 1500W Strobe, Elumen8 COB Tri 4 Pixel Batten, Briteq Tornado 7 (thanks to Robert Box)
- * New fixtures: American DJ 5P Hex, Pro-Lights Moonstone, Chauvet Intimidator Hybrid 140SR (thanks to Robert Box)
- * New fixtures: Robe Robin DLX Spot (thanks to Robert Box)
- * New fixture: ETC ColorSource PAR (thanks to Jon Rosen)
- * New fixture: lightmaXX 5ive STAR LED (thanks to Thomas Weber)
- * New fixture: Talent BL252A (thanks to Massimiliano Palmieri)
- * New fixtures: Showtec: Infinity iW-1915, Infinity XPLO-15 LED Strobe (thanks to Daniele Fogale)
- * New fixtures: Showtec: Infinity iB-5R, Compact Par 18 MKII, Phantom 20 LED Beam (thanks to Nicolò Zanon)
- * New fixture: Griven Gobostorm Plus MK2 (thanks to Attilio Bongiorni)
- * New fixture: Chauvet Freedom Stick (thanks to Jay Szewczyk)
- * New fixture: Eurolite TMH-14, Chauvet Intimidator Trio (thanks to Chris de Rock)
- * New fixture: Chauvet Scorpion Dual (thanks to Alan Chavis)
- * New fixture: American DJ Ultra Hex Bar 12 (thanks to Rhavin)
- * New fixture: Equinox Photon
- * New fixture: QTX MHS-60 (thanks to Nerijus Mongirdas)
- * New fixture: Eurolite LED TMH FE-600, MARQ Colormax Par64, Stairville CLB2.4 Compact LED PAR System (thanks to Klaus Muth)
- * New fixture: Chauvet SlimPar Hex 6 (thanks to Yinon Sahar)
- * New fixture: IMG Stageline PARL 20 DMX (thanks to Felix Pickenäcker)
- * New fixtures: Pro-Lights SmartBatHEX, Fury FY250W, Fury FY250S (thanks to Lorenzo Andreani)
- * New fixture: American DJ Ikon Profile (thanks to Ham Sadler)
- * New fixture: HQ Power Aeron Wash 575, JB Systems Space Color Laser (thanks to Ricardo Mendes)
- * New fixture: American DJ VPar (thanks to Eric Eskam)
- * New fixtures: MARQ Gesture Beam/Wash 102, Colormax Bat, Gesture Spot 100 (thanks to John Yiannikakis)
- * New fixtures: Chauvet COLORado 3P, Legend 330SR Spot, SlimPar HEX 3 (thanks to Kevin Zepp)
- * New fixture: American DJ Mini Dekker (thanks to Chris Davis)
- * New fixtures: American DJ Vizi BSW 300, Blizzard Lighting Flurry 5 (thanks to George Qualley)
- * New fixtures: Pro-Lights PIXIEWASH, Accent1Q, CromoSpot300 (thanks to Tolmino Muccitelli)
- * New fixtures: Involight LED MH50S, LED PAR 180, SBL 2000 (thanks to Facek)
- * New fixture: Pro-Lights Miniruby (thanks to Dario Gonzalez)
- * New fixture: Sagitter Smart DL Wash (thanks to Simu)
- * New fixtures: Eurolite LED THA-250F, Pro-Lights StudioCOBFC (thanks to Andrea Ugolini)
- * New fixture: American DJ Stinger Spot (thanks to Jason R. Johnston)
- * New fixture: Stairville Blade Sting 8 RGBW Beam Mover
-
- -- Massimo Callegari Sat, 24 Jun 2017 12:13:14 +0200
-
-qlcplus (4.10.5b) stable; urgency=high
-
- * engine: fixed 4.10.5 regression on RGB Matrix preset step color calculation
- * Virtual Console/Frame: fixed widgets disable state when switching pages
- * Virtual Console: fixed SpeedDial and Animation widget presets feedbacks, and allow to use custom feedbacks
- * Plugins/DMX USB: fixed 4.10.5 regression preventing to receive data from PRO devices
- * Plugins/DMX USB: [Windows] fixed a long standing bug causing random crashes when receiving DMX data
- * Plugins/MIDI: [macOS] further changes to support virtual ports
- * New fixtures: Stairville M-Fog 1000 DMX, Cameo Superfly XS (thanks to Konni)
- * New fixture: ColorKey WaferPar Quad-W 12 (thanks to Taylor)
- * New fixture: Eurolite LED PARty RGBW (thanks to Heiko Fanieng)
- * New fixture: lightmaXX Platinum CLS-1 (thanks to Marc Geonet)
-
- -- Massimo Callegari Mon, 26 Dec 2016 12:13:14 +0200
-
-qlcplus (4.10.5a) stable; urgency=high
-
- * engine: fixed playback of a chaser within a chaser
-
- -- Massimo Callegari Mon, 12 Dec 2016 12:13:14 +0200
-
-qlcplus (4.10.5) stable; urgency=low
-
- * Engine: added indigo to fixture channel colors (thanks to Axel Metzke)
- * Engine: properly handle RGB Matrices with generic dimmers (Jano Svitok)
- * UI/Function Manager: fix crash when trying to clone a folder (David Garyga)
- * UI/RGB Matrix Editor: editor preview doesn't stop when testing the Function
- * UI/Collection Editor: allow multiple selection and added Function reordering buttons
- * UI/Remap: fixed universes list in target mapping
- * UI/Remap: fixed wrong Scene remapping when mixing cloned and new fixtures
- * UI/Remap: added remapping also of Fixture Groups
- * Virtual Console/Frame: Show page number when collapsed (thanks to Matthias Gubisch)
- * Virtual Console/Cue List: allow to choose playback buttons layout (Play/Pause + Stop or Play/Stop + Pause)
- * Plugins/DMX USB: fixed crash happening on PRO devices when receiving a full universe
- * Plugins/DMX USB: [MacOS] fixed regression caused by the Qt libraries on PRO devices
- * Plugins/MIDI: [MacOS] added support for virtual ports, show only active devices and properly handle hotplug
- * Fixture Editor: fixed minimum value of a new capability not updating correctly
- * RGB Scripts: added One by one (Jano Svitok)
- * New fixtures: Pro-Lights LumiPIX 12Q, Proel PLLEDMLBG (thanks to Andrea Ugolini)
- * New fixtures: Stairville DCL Flat Par 18x4W CW/WW, Cameo LED MultiPAR CLM-PAR-COB1 (thanks to Freasy)
- * New fixtures: High End Systems Studio Beam, lightmaXX EASY Wash 5IVE LED (thanks to Freasy)
- * New fixture: iSolution iColor 4 (thanks to withlime)
- * New fixtures: ETC ColorSource Spot, Blizzard Lighting LB-Par Hex (thanks to Robert Box)
- * New fixtures: American DJ: Chameleon QBar Pro,DJ Vizi Beam RXONE, XS 600, Focus Spot Three Z (thanks to Robert Box)
- * New fixture: JB-Lighting Varyscan P6, Cameo Wookie series, Cameo Hydrabeam series (thanks to Andres Robles)
- * New fixture: Chauvet RotoSphere LED (thanks to Carl Eisenbeis)
- * New fixture: Briteq Spectra 3D Laser (thanks to Robert Box + Freasy)
- * New fixture: Martin MH2 Wash (thanks to John Yiannikakis + Freasy)
- * New fixture: American DJ Flat Par Tri7X (thanks to Brian)
- * New fixtures: Ledj Stage Color 24, 59 7Q5 RGBW, 59 7Q5 RGBA (thanks to Paul Wilton)
- * New fixtures: American DJ: Inno Spot Elite, Stinger, Tri Phase (thanks to Piotr Nowik)
- * New fixtures: Showtec Phantom 95 LED Spot, Futurelight PHS-260 (thanks to Piotr Nowik)
- * New fixture: Blizzard Lighting Rocklite RGBAW (thanks to Larry Wall)
- * New fixture: American DJ Comscan LED (thanks to Chris)
- * New fixtures: PR Lighting XL 250/XL 700 Wash/XL 700 Spot, American DJ Accu Fog 1000 (thanks to István Király)
- * New fixtures: Equinox Ultra Scan LED, Kam Powercan84W, QTX HZ-3 (thanks to Chris Moses)
- * New fixture: Eurolite TMH-10 (thank to exmatrikulator)
- * New fixture: Eurolite LED SLS 5 BCL, Robe Fog 1500 FT (thanks to Christian Hollbjär)
- * New fixture: SGM Giotto Spot 400 (thanks to Mihai Andrei)
- * New fixture: Pulse LEDBAR 320 (thanks to Allan Rhynas)
- * New fixture: Equinox Swing Batten (thanks to Dean Clough)
- * New fixture: Cameo Pixbar 600 PRO, Chauvet COLORado 1 Quad Zoom Tour (thanks to Andrew Hallmark)
- * New fixture: Involight FM900 DMX (thanks to Jászberény Szabolcs)
- * New fixture: Showtec Stage Blinder Series (thanks to Antoni J. Canós)
- * New fixture: MARQ Gamut PAR H7 (thanks to Lance Lyda)
- * New fixtures: Chauvet: SlimPAR QUV12 USB, SlimPAR PRO H USB, Scorpion Bar RG (thanks to Pete Mueller)
- * New fixture: Stairville CLB8 Compact LED PAR System (thanks to Detlef Fossan)
- * New fixture: Chauvet Cubix 2.0 (thanks to Jungle Jim)
- * New fixture: Showtec Giant XL LED (thanks to Samuel Hofmann)
- * New fixtures: SGM: Idea Beam 300, Idea Led Bar 100, Idea Spot 700, Newton 1200 (thanks to Oscar Cervesato)
- * New fixtures: Pro Lights LumiPAR18QTour, Elation SIXPAR 200IP (thanks to Oscar Cervesato)
- * New fixture: Stairville Beam Moving Head B5R, American DJ Flat Par TW12, Varytec Easy Scan XT Mini (thanks to Thierry Rodolfo)
-
- -- Massimo Callegari Sat, 3 Dec 2016 12:13:14 +0200
-
-qlcplus (4.10.4) stable; urgency=low
-
- * Scripts: Fix 4.10.3a regression that breaks values parsing (David Garyga)
- * Engine: fix relative paths when opening a project from the command line
- * Engine: improved the start/stop mechanism of Functions within a Show
- * Chaser Editor: a newly created step is now selected automatically
- * Scene Editor: fixed the tab order of the fixtures
- * Show Manager: added the possibility to pause a Show leaving the lights on
- * Show Manager/Audio: allow to display the waveform preview while playing a file
- * UI/Function Selection: fix crash on workspaces where a scene ID is bigger than its sequence ID (David Garyga)
- * UI/Video: fixed the fullscreen positioning on Windows
- * Virtual Console/Animation: fix behavior issue when changing the associated function (David Garyga)
- * Virtual Console/Frames: send feedbacks for the enable button
- * Virtual Console/Frames: fix 4.10.3 regression causing frames to resize after configuration
- * Virtual Console/Cue List: playback can now be paused and resumed (see documentation)
- * Virtual Console/Cue List: added a dedicated stop button, with external controls
- * Virtual Console/XYPad: fixed computation of reversed fixture position (Luca Ugolini)
- * Plugins/OSC: fixed regression of receiving data from the wrong interface (David Garyga)
- * Plugins/OSC: fixed regression causing not receiving data anymore when changing the input profile (David Garyga)
- * Plugins/MIDI: distinguish MIDI beat clock start and stop (see documentation)
- * Input Profiles Editor: it is now possible to define button custom feedbacks in a profile (see documentation)
- * New input profile: Novation Launchpad Pro (thanks to David Giardi)
- * New RGB script: Balls (color) (thanks to Rob Nieuwenhuizen)
- * Fixture updated: Starway MaxKolor-18 (thanks to Thierry Rodolfo and Robert Box)
- * Fixture updated: Cameo LED RGBW PAR64 18x8W (thanks to Lukas)
- * New fixture: American DJ Mega QA Par38 (thanks to Nathan Durnan)
- * New fixture: Martin MAC 250 Wash (thanks to Robert Box)
- * New fixture: Luxibel LX161 (thanks to Freddy Hoogstoel)
- * New fixture: Stairville MH-X60th LED Spot (thanks to Jasper Zevering)
- * New fixture: Cameo CLHB400RGBW (thanks to Mihai Andrei)
- * New fixture: Showlite Flood Light Panel 144x10mm LED RGBW (thanks to Ex)
- * New fixture: Color Imagination LedSpot 90 (SI-052), Robe Spot 575 XT (thanks to DJ Ladonin)
- * New fixture: Chauvet Mini Kinta (thanks to Jonathan Wilson)
- * New fixture: Eurolite LED ML-56 QCL RGBW-RGBA 18x8W (thanks to Matthijs ten Berge)
- * New fixture: High End Systems TechnoSpot (thanks to Tom Moeller)
- * New fixtures: American DJ Inno Pocket Spot Twins, Fog Fury 3000 WiFly, Event Bar Pro (thanks to MaBonzo)
- * New fixtures: American DJ Galaxian Gem IR, Vizi Roller Beam 2R (thanks to MaBonzo)
- * New fixture: Ayra ERO 506 (thanks to Bert Heikamp)
- * New fixture: Ayrton Arcaline 100 RGB, Martin Magnum Hazer (thanks to Thierry Rodolfo)
- * New fixtures: American DJ Asteroid 1200, Eurolite GKF-60, Eurolite LED FE-700 (thanks to Flox Garden)
- * New fixtures: Antari X-310 Pro Fazer, lightmaXX CLS-2 (thanks to Flox Garden)
- * New fixture: Beamz MHL90 Wash 5x18W RGBAW-UV (thanks to Hans Erik Tjelum)
- * New fixture: PR Lighting Pilot 150 (thanks to David Read)
- * New fixture: lightmaXX Platinum CLS-1 (thanks to Marc Geonet)
-
- -- Massimo Callegari Sun, 29 May 2016 12:13:14 +0200
-
-qlcplus (4.10.3a) stable; urgency=low
-
- * Scripts: Fix 4.10.3 regression that breaks time values parsing (David Garyga)
- * RGBMatrix Editor: Fix 4.10.3 regression where QLC+ hangs on duration < 20ms (David Garyga)
-
- -- Massimo Callegari Wed, 9 Mar 2016 22:03:14 +0200
-
-qlcplus (4.10.3) stable; urgency=low
-
- * Engine: Fix intensity channels forced back to HTP after LTP not working correctly (David Garyga)
- * Engine: Fix functions with low intensity killing current fade outs (David Garyga)
- * Audio Capture: Fix crash when selecting another audio input while a capture is running (David Garyga)
- * Audio Capture: Fix crash when trying to use a wrongly configured audio input (David Garyga)
- * Scene Editor: Remember Channels Groups values when saving and loading a workspace (David Garyga)
- * Scene Editor: Remember fixtures even with no activated channel (David Garyga)
- * RGBMatrix Editor: Fix preview now working when fade in > 0 (David Garyga)
- * RGBMatrix Editor: Fix length of fadeout on the preview (David Garyga)
- * Show Manager: Fix crash when editing the total time of an empty chaser (David Garyga)
- * Show Manager/Function Selection: Fix sequences always displayed even with Chasers and Scenes both filtered out (David Garyga)
- * Speed Dials: Fix display and input of the milliseconds field, update precision from 10ms to 1ms (David Garyga)
- * Input/Output Manager: Forbid deleting universes in the middle of the list, this prevents a lot of bugs and crashes (David Garyga)
- * Simple Desk: the number of faders is now dynamic depending on the window size (unless forced via config file)
- * Plugins/ArtNet: Fix input and output initialization conflict that results in no input (David Garyga)
- * Plugins/ArtNet: Allow sending and receiving ArtNet on several different interfaces (David Garyga)
- * Plugins/ArtNet: Allow selecting a different ArtNet input universe (David Garyga)
- * Plugins/ArtNet: Fix configuration issue that prevents setting a parameter back to its default value (David Garyga)
- * Plugins/OSC: Fix configuration issue that prevents setting a parameter back to its default value (David Garyga)
- * Plugins/OSC: OSC Output values range from 0.0 to 1.0
- * Plugins/OSC: Properly handle OSC bundles (restores Lemur compatibility)
- * Virtual Console: Fix copy of a frame containing a submaster slider resulting in a broken submaster (David Garyga)
- * Virtual Console/Slider: Enable function filters in playback function selection (David Garyga)
- * Virtual Console/Slider: Allow to force to LTP color channels controlled by a Click & Go button
- * Virtual Console/Solo Frame: Fix sliders in playback mode not actually stopping the attached function when the slider reaches 0 (David Garyga, thanks to Tubby)
- * Virtual Console/Animation: Can now be used in solo frames (David Garyga)
- * Virtual Console/Frames: fix page cloning of a nested multipage frame
- * Virtual Console/Frames: fix disabling frame pages. Now widgets get actually deleted
- * Web access: fixed custom fixtures loading
- * Web access: added a DMX keypad that can be accessed from the Simple Desk (thanks to Santiago Benejam Torres)
- * Input profiles: added Behringer BCR2000 (thanks to Michael Trojacher)
- * Input profiles: added Lemur iPad Studio Combo
- * RGB Scripts: added Strobe script (thanks to Rob Nieuwenhuizen)
- * New fixtures: Stellar Labs ECO LED PAR56, Chauvet Colorpalette II (thanks to Jimmy Traylor)
- * New fixtures: Chauvet: COLORado 1 Solo, Ovation FD-165WW, Rogue RH1 Hybrid, COLORdash Par Hex 12, COLORdash Accent Quad (thanks to Robert Box)
- * New fixtures: Chauvet: Vue 1.1, Intimidator Spot 100 IRC, Abyss USB, COREpar 40 USB, COREpar UV USB (thanks to Robert Box)
- * New fixtures: Chauvet: Intimidator Scan 305 IRC, Intimidator Barrel 305 IRC, SlimPAR T6 USB, SlimBANK TRI-18 (thanks to Robert Box)
- * New fixtures: Eurolite LED CLS-9 QCL RGBW 9x8W 12, JB-Lighting A12 Tunable White, SGM G-Profile (thanks to Robert Box)
- * New fixtures: Coemar Par Lite LED RGB, OXO LED Funstrip DMX, American DJ Stinger II (thanks to Robert Box)
- * New fixture: Chauvet LED PAR 64 Tri-C (thanks to Jungle Jim and Robert Box)
- * New fixtures: American DJ VBar, American DJ Jellydome, Briteq LDP Powerbar 6TC/12TC (thanks to Thierry Rodolfo)
- * New fixture: Sagitter Slimpar 18 RGB (thanks to Daniele Fogale)
- * New fixture: Microh LED Tri Bar (thanks to Michael Tughan)
- * New fixture: American DJ 12P Hex Pearl (thanks to Ethan Moses)
- * New fixture: JB-Lighting JBLED A7 (thanks to BLACKsun)
- * New fixture: Chauvet COREpar 80 USB (thanks to Chris Gill)
- * New fixture: Stairville DJ Lase 25+25-G MK-II (thanks to galaris)
- * New fixtures: PR Lighting XR 230 Spot, PR Lighting XLED 1037 (thanks to Ovidijus Cepukas)
- * New fixtures: Futurelight DJ-Scan 600, Eurolite LED PAR-64 RGBW+UV (thanks to Ovidijus Cepukas)
- * New fixtures: Varytec LED Pad 7 BA-D, American DJ X-Scan LED Plus, Showtec Blade Runner (thanks to DjProWings)
- * New fixture: Involight LED CC60S (thanks to Stephane Hofman)
- * New fixture: Stairville MH-x200 Pro Spot (thanks to Mirek Škop)
- * New fixtures: Varytec LED Giga Bar 4 MKII, Eurolite LED KLS Laser Bar FX Light Set (thanks to Daniel Schauder)
- * New fixture: Chauvet Mayhem (thanks to Jonathan Wilson)
- * New fixture: Ayra TDC Agaricus (thanks to Rob Nieuwenhuizen)
- * New fixture: American DJ Pinspot LED Quad DMX (thanks to Christian Polzer)
- * New fixture: Stairville AF-180 LED Fogger Co2 FX (thanks to Johannes Uhl)
-
- -- Massimo Callegari Sun, 6 Mar 2016 20:21:22 +0200
-
-qlcplus (4.10.2) stable; urgency=low
-
- * Engine: added support for devices hotplug (DMX USB, MIDI, HID, Peperoni)
- * Engine: Universe passthrough data is now merged with QLC+ output, it is not affected by QLC+ processing
- (except for blackout) and it appears in the DMX monitor (Jano Svitok)
- * Audio: fixed playback of 24/32 bit wave files and Show Manager waveform preview
- * DMX Dump: it is now possible to dump DMX values on an existing Scene
- * ClickAndGo Widgets: Preset widgets now display the channel name on top of the capability list (David Garyga)
- * Function Manager: fix startup Function not cleared when deleting it (David Garyga)
- * Function Manager: highlight current startup Function when opening the Function selection dialog (David Garyga)
- * Function Selection: Don't lose the current selection when changing the function type filter (David Garyga)
- * Show Manager: fixed looped functions never stopping with certain durations (Jano Svitok)
- * Show Manager: fixed copy/paste of an existing Chaser
- * Show Manager: fix crashes when copying a sequence on an empty track (David Garyga)
- * Show Manager: repair conflicting sequences when loading a broken workspace (David Garyga)
- * EFX Editor: removed the intensity control. Please use separate Scenes for that
- * Virtual Console/Slider: fixed copy of the channel monitor mode (David Garyga)
- * Virtual Console/Slider: in level mode, activate only in operate mode and don't hold forced LTP channels (David Garyga)
- * Virtual Console/Slider: in playback mode, ignore the fade in/fade out of the attached Function (David Garyga)
- * Virtual Console/XYPad: added Fixture Group preset, to control a subgroup of Fixtures (see documentation)
- * Virtual Console/XYPad Properties: fixture ranges can now be set in degrees, percentage or DMX values
- * Virtual Console/XYPad Properties: fix manual input selection for presets (David Garyga)
- * Virtual Console/Cue List: improved mixed usage of crossfader and next/previous buttons (David Garyga)
- * Virtual Console/Cue List: fix effect of a submaster slider on a Cue List in crossfader mode (David Garyga)
- * Virtual Console/Cue List: fix crash when adding steps to the chaser being run by a Cue List (David Garyga)
- * Virtual Console/Audio Triggers: fix virtual console buttons triggering (David Garyga)
- * Virtual Console/Input Selection: allow custom feedbacks only on an assigned source and don't crash (David Garyga)
- * DMX Monitor: Fix strobing in 2D view (Jano Svitok)
- * Fixture Editor: Fix crash in the Channel Editor (David Garyga)
- * Plugins/uDMX: added support for AVLdiy.cn clone (thanks to Vitalii Husach)
- * Plugins/DMXUSB: (Linux) fixed data transmission of DMX4ALL NanoDMX
- * Input Profiles: added an option for Buttons to always generate a press/release event
- * New input profile: Touch OSC Automat5
- * New input profile: Novation Launch Control (thanks to Giacomo Gorini)
- * Updated fixture: Stairville xBrick Full-Colour 16X3W (thanks to Rico Hansen)
- * Updated fixture: Stairville MH-100 Beam 36x3 LED (thanks to Antoni J. Canos)
- * Updated fixture: American DJ Revo 3 (thanks to David Pilato)
- * New fixture: American DJ Dotz Flood
- * New fixture: Chauvet COLORdash Par Quad-7
- * New fixtures: Robe ColorWash 1200E AT, American DJ Starburst, Chauvet LED PAR 64 Tri-B (thanks to Robert Box)
- * New fixtures: Cameo Multi Par 3, HQ Power VDPL110CC LED Tri Spot, Showtec LED Pixel Track Pro (thanks to Robert Box)
- * New fixtures: American DJ: Inno Pocket Z4, On-X, WiFly EXR Dotz Par, WiFly EXR HEX5 IP, COB Cannon Wash Pearl (thanks to Robert Box)
- * New fixtures: BoomTone DJ Sky bar 288 LED, BoomToneDJ Strob LED 18, BoomToneDJ Froggy LED RGBW (thanks to Didou)
- * New fixture: iSolution iMove 250W (thanks to Thierry Rodolfo)
- * New fixture: Talent BL63 10" LED Bar (thanks to FooSchnickens)
- * New fixtures: Contest Oz-37x15QC, Evora DUO B2R, Evora Beam 5R, Evora Beam 15R (thanks to Jan Lachman)
- * New fixtures: Blizzard Lighting Lil G, Pixellicious, Lo-Pro CSI (thanks to Alton Olson)
- * New fixtures: Stairville LED Matrix Blinder 5x5, Showtec Power Spot 9 Q6 Tour V1 (thanks to Samuel)
- * New fixture: Blizzard Lighting StormChaser (thanks to Brent)
- * New fixtures: Showtec Explorer 250 Pro MKII, Showtec Pixel Bar 12 (thanks to Henk de Gunst)
- * New fixture: Philips Selecon PLProfile1 MkII (thanks to Freasy)
- * New fixture: PSL Strip Led RGB code K2014 (thanks to Lorenzo Andreani)
- * New fixture: Chauvet SlimPar Pro Tri (thank to Bulle)
- * New fixture: Chauvet GigBar IRC (thanks to JD-HP-DV7 and Jungle Jim)
- * New fixtures: Ghost Green 30, KOOLlight 3D RGB Laser, Mac Mah Mac FOG DMX (thanks to David Pilato)
-
- -- Massimo Callegari Sun, 13 Dec 2015 20:21:22 +0200
-
-qlcplus (4.10.1) stable; urgency=high
-
- * Virtual Console/Cue List: improved step fader behaviour (David Garyga)
- * Plugins/DMXUSB: Fixed regression affecting Linux users and OSX users using the libFTDI interface
- * Plugins/ArtNet/E1.31/OSC: Improved network interfaces detection
- * New fixture: Enterius EC-133DMX (thanks to Krzysztof Ratynski)
- * New fixture: Showtec Dragon F-350 (thanks to Jasper Zevering)
- * New fixtures: JB Systems Lounge Laser DMX, JB Systems Super Solar RGBW (thanks to Robert Box)
-
- -- Massimo Callegari Wed, 21 Oct 2015 20:21:22 +0200
-
-qlcplus (4.10.0) stable; urgency=low
-
- * Channel Groups: Fix crashes related to invalid channels (David Garyga)
- * Chaser: Fix flickering issue when chaser order is Random (David Garyga)
- * Engine: some more fixes on forced HTP/LTP channels
- * Engine: fixed 4.9.x regression causing QLC+ to hang at the end of audio playback
- * RGB Matrix Audio Spectrum: Fix crash when audio input volume is set to zero (David Garyga)
- * RGB Matrix: Fix 4.9.1 regression which makes fading of green and blue colors not smooth (David Garyga)
- * RGB Matrix: Introduced blending mode between matrices (see documentation)
- * Audio Input: Fix crashes when selecting another audio input device while an audio input driven function/widget is running (David Garyga)
- * Audio Input: It is now possible to select the audio input format (sample rate and channels)
- * Video: fixed playback from a time offset (where possible)
- * Video: (Windows) Videos now have a black background, like all the other platforms
- * Add Fixture dialog: Generic fixtures don't take the number of channels of the previously selected fixture (David Garyga)
- * Add Fixture dialog: Fix address 512 not usable by adding several fixtures at a time (David Garyga)
- * Scene Editor: correctly select the fixture tab when switching from tabbed/all channels view
- * EFX Editor: it is now possible to use an EFX on RGB channels (thanks to Giorgio Rebecchi)
- * Collection Editor: added preview button (thanks to Giorgio Rebecchi)
- * Fixture Remap: fixed remapping of EFX functions
- * Function Wizard: improved creation of color scenes for RGB panels
- * Function Wizard: automatically set a gobo picture (if available) on buttons attached to gobo Scenes
- * Show Manager: Fix some cursor teleportation issues (David Garyga)
- * Simple Desk: Fix cue playback on universes 2+ (David Garyga)
- * Simple Desk: Fix crash when selecting recently added universe (David Garyga)
- * Simple Desk: Added reset buttons to reset a single channel
- * Simple Desk: Fix page count when channels per page does not divide 512 (Jano Svitok, reported by Florian)
- * Virtual Console: fixed Grand Master not sending feedbacks
- * Virtual Console/Input Controls: implemented custom feebacks. For now used only by VC buttons
- * Virtual Console/Solo Frame: Option to allow mixing of sliders in playback mode (David Garyga)
- * Virtual Console/Speed Dial: Introduced multiplier/divisor, apply and presets buttons (see documentation)
- * Virtual Console/Button: allow to set a background picture
- * Virtual Console/Cue List: Options for the Next/Previous buttons behavior (David Garyga)
- * Virtual Console/Cue List: Fixed playback of a Chaser in reverse order (David Garyga)
- * Virtual Console/Cue List: Added a new "Steps" mode for the side faders (see documentation)
- * Virtual Console/Cue List: Allow to resize columns to 0 pixels, to completely hide them
- * Virtual Console/XYPad: Introduced presets, including the usage of existing EFX and Scenes (see documentation)
- * Virtual Console/XYPad: Fix DMX output not working when going to Operate mode while the XYPad is disabled (David Garyga, thanks to bestdani)
- * Input Profiles: added an option for MIDI profiles to feedback a Note Off or a Note On with 0 velocity. APCMini now works out of the box. (Jano Svitok)
- * Input Profiles: Improved BCF2000 Input profile (thanks to Lorenzo Andreani)
- * Plugins/MIDI: (Linux) Fixed data transmission to multiple devices (thanks to Adrian Kapka)
- * Plugins/MIDI: fixed Program Change handling on OSX and Windows
- * Plugins/MIDI: (Windows) do not close the device when sending SysEx data
- * Plugins/ArtNet: it is now possible to enter an arbitrary output IP
- * Plugins/OSC: it is now possible to enter an arbitrary output IP
- * Plugins/E1.31: added stream priority to configuration (thanks to Nathan Durnan)
- * Plugins/E1.31: added unicast support (David Garyga)
- * Plugins/DMXUSB: fixed close/open sequence on a Enttec Pro input line
- * Web Access: implemented frames collapse functionality
- * RGB Scripts: added Plasma Colors script (thanks to Nathan Durnan)
- * Fixture Editor: Channel capability editing is now done in a single window
- * Updated fixture: Showtec Indigo 6500 (thanks to Jochen Becker)
- * New fixture: Ayra ComPar 20 (thanks to Rob Nieuwenhuizen)
- * New fixture: XStatic X-240Bar RGB (thanks to Nathan Durnan)
- * New fixture: Venue ThinPAR 38 (thanks to Thierry Rodolfo)
- * New fixtures: Contest MiniCube-6TCb, Eurolite LED FE-1500, Lightronics FXLD618C2I, JB Systems COB-4BAR (thanks to Robert Box)
- * New fixtures: Eurolite LED KLS-401, Chauvet Intimidator Wash Zoom 350 IRC, Equinox Party Par LED PAR 56 (thanks to Robert Box)
- * New fixtures: Robe Robin MiniMe, PR Lighting Pilot 575, Stairville DJ Lase 150-RGY MkII, JB Systems Dynaspot (thanks to Robert Box)
- * New fixtures: American DJ Hyper Gem LED, Robe ColorSpot 575 AT, Kam iLink All Colour Models (thanks to Robert Box)
- * New fixtures: Chauvet Intimidator Wave 360 IRC, Varytec LED PAR56 (thanks to Habefaro)
- * New fixture: American DJ Fog Fury Jett (thanks to Dean Clough)
- * New fixtures: Eurolite TB-250, Futurelight DJ-HEAD 575 SPOT, GLX Lighting Power LED Beam 38 Narrow (thanks to Ovidijus Cepukas)
- * New fixture: Pro-Lights UVStrip18 (thanks to Alessandro Grechi)
- * New fixtures: American DJ Inno Pocket Beam Q4, Martin ZR24/7 Hazer, Blizzard Lighting Stimul-Eye (thanks to George Qualley)
- * New fixture: American DJ Mega TriPar Profile Plus (thanks to George Qualley)
- * New fixtures: Pro-Lights SmartBat, Robe ClubWash 600 CT (thanks to Lorenzo Andreani)
- * New fixture: Stairville Show Bar Tri 18x3W RGB (thanks to Udo Besenreuther)
- * New fixtures: Blizzard Lighting Rokbox Infiniwhite, Chauvet COREpar 80 (thanks to Chris Gill)
- * New fixture: Cameo CL Superfly HP (thanks to Stuart Brown)
- * New fixture: American DJ Event Bar Q4 (thanks to Maxime Bissonnette-Théorêt)
- * New fixture: Cameo LED Moving Head 60W CLMHR60W (thanks to Jasper Zevering)
- * New fixtures: Proel PLLED64RGB, Litecraft LED PAR 64 AT3, Robe Robin 300E Beam (thanks to Mihai Andrei)
- * New fixture: Electroconcept SPC029 (thanks to Bulle)
- * New fixture: Microh Plasmawave 1 RGB (thanks to Rommel)
-
- -- Massimo Callegari Sun, 18 Oct 2015 20:21:22 +0200
diff --git a/debian/changelog-old b/debian/changelog-old
index 118149f319..e02a5031a2 100644
--- a/debian/changelog-old
+++ b/debian/changelog-old
@@ -1,3 +1,538 @@
+qlcplus (4.11.2) stable; urgency=low
+
+ * engine: fix crash caused by an invalid IO mapping
+ * engine: fix intensity override not considered during fade outs
+ * UI/Function Manager: fixed keyboard shortcut conflicts and document them
+ * UI/Function Manager: allow to import multiple of audio/video files at once
+ * UI/Channel Groups: added expand/collapse all button helpers
+ * UI/Chaser Editor: add a button to shuffle the selected Chaser steps (thanks to Felix Edelmann)
+ * UI/RGBMatrix Editor: fix preview not updating on pattern change when play button is on
+ * Show Manager: fix crash when adding a Sequence after deleting one
+ * Show Manager: fix crash when editing a Sequence bound to a deleted Scene
+ * Show Manager: fix items start time indication when dragging
+ * Virtual Console/Slider: fix submaster initial value not applied and inverted mode
+ * Virtual Console/Slider: added 'value catching' option for external controller faders (Lukas Jähn proposal)
+ * Virtual Console/Slider: fix values range when switching between Slider and Knob appearance
+ * Virtual Console/Slider: react on Scene flashing when in playback mode
+ * Virtual Console/Knob: fix DMX values not updated when interacting with the mouse wheel
+ * Virtual Console/Cue List: allow to select a step with next/previous buttons during pause
+ * Virtual Console/Cue List: go to the right chaser step after a pause (thanks to Krzysztof Walo)
+ * Virtual Console/Frame: fix regression preventing to send the disable feedback
+ * Web Access: added support for VC Buttons in Flash mode (Sylvain Laugié)
+ * Web Access: added support for VC Frame circular page scrolling (Sylvain Laugié)
+ * Web Access: update AudioTriggers state when changed from QLC+ (Sylvain Laugié)
+ * plugins/udmx: added 'channels' configuration parameter (see documentation)
+ * plugins/E1.31: fix crash on wrong packet length (David Garyga)
+ * New fixture: DTS XR7 Spot (thanks to Nicolò Zanon)
+ * New fixture: Ledj Slimline 12Q5 Batten (thanks to Dean Clough)
+ * New fixture: Ayra Compar Kit 1 (thanks to eigenaardiger)
+ * New fixtures: GLP Impression X4 S, Eurolite LED KLS-2500 (thanks to Mitsch)
+ * New fixture: Chauvet Intimidator Spot 255 IRC (thanks to Ham Sadler)
+ * New fixtures: Chauvet Geyser RGB, Geyser P6 (thanks to Andrew)
+ * New fixture: Chauvet Rotosphere Q3 (thanks to Eric Sherlock)
+ * New fixture: Showtec Compact Par 7 Q4 (thanks to Alexander)
+ * New fixture: Contest Delirium (thanks to Vincent)
+ * New fixture: Solena Mini Par 12, Max Bar 28 RGB (thanks to Nathan Durnan)
+ * New fixtures: Showtec Phantom 65, Laserworld PRO-800RGB (thanks to Piotr Nowik)
+ * New fixture: Chauvet MiN Spot RGBW (thanks to Jungle Jim)
+ * New fixtures: Showtec Shark Wash One, American DJ Vizi Hex Wash7 (thanks to Georg Müller)
+ * New fixture: Showtec Shark Beam FX One (thanks to Mats Lourenco)
+ * New fixture: Stairville novaWash Quad LED (thanks to Luke Bonett)
+ * New fixtures: Eurolite Party TCL Spot RGB, Expolite TourSpot 60, Expolite TourStick 72 RGBWA (thanks to Dirk J)
+ * New fixture: Chauvet Hemisphere 5.1, Trident, Scorpion Storm RGX (thanks to Francois Blanchette)
+ * New fixture: Briteq COB Slim 100-RGB (thanks to Thierry)
+ * New fixture: American DJ UB 12H (thanks to Jason R Johnston)
+ * New fixture: American DJ Mega Hex Par (thanks to Ben C)
+ * New fixture: Cameo Q SPOT 15 RGBW (thanks to Antoine Houbron)
+ * New fixture: lightmaXX Platinum Line Flat Par COB (thanks to Leonardo)
+ * New fixture: Stairville LED Blinder 2 COB 2x65W (thanks to chritoep)
+ * New fixture: lightmaXX LED PAR 64 (thanks to Johannes Felber)
+ * New fixture: Cameo Thunder Wash Series (thanks to JP)
+ * New fixtures: Briteq BT 575S, Stairville MH-x30 LED Beam (thanks to Andres Robles)
+ * New fixture: beamZ LED FlatPAR-154 (thanks to Jászberényi Szabolcs)
+ * New fixtures: Eurolite THA-100F COB, Cameo Tribar 200 IR (thanks to David Morgenschweis)
+ * New fixture: beamZ BT310 LED FlatPAR 12x8W 4-1 DMX IR (thanks to Mark)
+ * New fixture: Fun Generation PicoWash 40 Pixel Quad LED (thanks to Harm Aldick)
+ * New fixtures: American DJ Entourage, Elumen8 MS-700PE, Ibiza PAR LED 712IR (thanks to Tim Cullingworth)
+ * New fixtures: Martin MAC 401 Dual RGB Zoom, MAC 401 Dual CT Zoom, Stairville MH-z720 (thanks to Tim Cullingworth)
+ * New fixture: Fun Generation SePar Quad UV (thanks to Helmet)
+
+ -- Massimo Callegari Thu, 19 Apr 2018 20:21:22 +0200
+
+qlcplus (4.11.1) stable; urgency=low
+
+ * engine: fixed audio files detection by prioritizing sndfile over mad
+ * engine: fixed HTP/LTP forced channels not set correctly
+ * engine: keep track of input/output device lines even if they are disconnected
+ * engine/Script: add blackout:on and blackout:off commands (Jano Svitok)
+ * engine/Script: do not keep empty trailing lines when saving a workspace
+ * UI: it is now possible to detach a QLC+ context tab on a separate window by double clicking on it
+ * UI/RGB Panel: added RBG pixel type (thanks to Peter Marks)
+ * UI/Remap: fixed RGB Panels remapping
+ * UI/Input Output Manager: added a button to enable/disable USB hotplugging (disabled by default)
+ * UI/Function Live Edit: restore basic live editing of Sequences
+ * UI/RGB Matrix Editor: fixed save to Sequence feature
+ * UI/Function Manager: when cloning a Sequence, clone the bound Scene too
+ * Virtual Console/Button: highlight border with orange color when in "monitoring" state
+ * Virtual Console/Slider: fix DMX values not updated when interacting with the mouse wheel, keyboard or Click And Go button
+ * Virtual Console/Slider: fix level mode values range scaling
+ * Virtual Console/XYPad: the speed of a running EFX preset can now be controlled by a Speed Dial widget
+ * RGB Scripts: added "Noise", "3D Starfield", "Random pixel per row" and "Random pixel per row multicolor" (thanks to Doug Puckett)
+ * Web access: added basic authentication support (thanks to Bartosz Grabias)
+ * Web access: fixed solo frames collapse state
+ * Web access: update feedbacks when a slider is moved
+ * New fixtures: IMG Stageline BEAM-40 WS/RGBW, Fun-Generation LED Diamond Dome (thanks to Tolmino Muccitelli)
+ * New fixture: Elation Cuepix Batten (thanks to Saul Vielmetti)
+ * New fixture: Clay Paky Tiger Scan HMI 575/1200 (thanks to Daris Tomasoni)
+ * New fixture: Litecraft WashX.21 (thanks to Hannes Braun)
+ * New fixtures: Briteq Stagepainter 12, Nicols IP Wash 120, Showtec LED Powerline 16 Bar (thanks to Fredje Gallon)
+ * New fixtures: Nicols Movelight, Nicols Birdy Wash 122, Briteq Giga Flash RGB (thanks to Fredje Gallon)
+ * New fixtures: Litecraft PowerBar AT10.sx, Stairville MH-z1915 (thanks to Thorben / Fredje)
+ * New fixtures: Martin MAC 700 Wash, ADB Warp M (thanks to Thorben)
+ * New fixture: Laserworld CS-1000RGB Mk II (thanks to Piotr Nowik)
+ * New fixture: Chauvet COLORrail IRC (thanks to Lane Parsons)
+ * New fixtures: American DJ COB Cannon Wash DW, lightmaXX Vega Zoom Wash Beam (thanks to Florian Gerstenlauer)
+ * New fixture: Martin Rush MH5 Profile (thanks to Falko)
+ * New fixture: Cameo Flash Bar 150 (thanks to Kevin Wimmer)
+ * New fixtures: Chauvet FXpar 9, IMG Stageline Wash-40 LED (thanks to PeterK)
+ * New fixtures: JB Systems iRock 5C, JB Systems LED Devil (thanks to Andres Robles)
+ * New fixtures: beamZ BAC406, Geni Mojo Color Moc (thanks to Mark Sy)
+ * New fixtures: Stairville MH-250 S, Chauvet GigBAR 2, Pro-Lights Onyx (thanks to Freasy)
+ * New fixtures: Coemar ProSpot 250 LX, Showtec Kanjo Spot 60 (thanks to Flo Edelmann)
+
+ -- Massimo Callegari Sat, 28 Oct 2017 12:13:14 +0200
+
+qlcplus (4.11.0) stable; urgency=low
+
+ * engine: fixed setting start/end color while a RGB Matrix is running
+ * engine: fixed crash when pausing a Show with an unavailable audio file
+ * engine: major rework of Sequences. Projects using them need to be migrated
+ * UI: enabled Alt key combinations on macOS to behave like other platforms (thanks to Matt Mayfield)
+ * UI/RGB Panel: added panel direction (thanks to Raivis Rengelis)
+ * UI/Fixture Manager: added weight and power consumption information on fixtures/universe selection (Chris de Rock idea)
+ * UI/Scene Editor: preserve fixture tab order when fixtures with no channels set are present
+ * UI/RGB Matrix Editor: allow the preview to run even in operate mode
+ * UI/Audio Editor: added the possibility to loop an audio file (thanks to Raivis Rengelis)
+ * UI/Simple Desk: fixed crash when changing values from a channel group in "fixtures view" mode
+ * Virtual Console: prevent unwanted feedbacks from widgets in inactive Frame pages (thanks to Lukas Jähn)
+ * Virtual Console: fixed manual selection of input channels not considering Frame pages (thanks to Lukas Jähn)
+ * Virtual Console: fixed input profiles channels not honored on frame pages other than the first (thanks to Lukas Jähn)
+ * Virtual Console/Slider: improved level monitoring with the possibility to act like a Simple Desk slider (see documentation)
+ * Virtual Console/Frame: fixed 4.10.5b regression disabling widgets when switching page in design mode
+ * Virtual Console/Frame: fixed key controls not copied when cloning a frame (thanks to Lukas Jähn)
+ * Virtual Console/Frame: added the possibility to jump directly to a page and assign page names (thanks to Lukas Jähn)
+ * Virtual Console/Cue List: improved linked crossfade to perform an additive blending between steps (see documentation)
+ * Virtual Console/Speed Dial: improved tap button blinking and feedbacks (thanks to Lukas Jähn)
+ * Virtual Console/Speed Dial: it is now possible to copy/paste factors (thanks to Jan Dahms)
+ * Virtual Console/Clock: added external input support for countdown and stopwatch modes (thanks to Lukas Jähn)
+ * Plugins/OSC: added channel number calculator in configuration page to help integrating new controllers
+ * Plugins/Loopback: fixed spurious values emitted when a lot of channels are looped
+ * Web access: fixed VC Slider in percentage mode and inverted appearance (thanks to Bartosz Grabias)
+ * Web access: support VC Slider reduced range when in level mode
+ * Web access: improved getChannelsValues and added Simple Desk reset per-channel (sdResetChannel API)
+ * Web access: implemented keypad increase/decrease buttons (thanks to Santiago Benejam Torres)
+ * New input profile: Zoom R16 (thanks to Benedict Stein)
+ * New MIDI template: Akai APC40 MK2 Ableton mode (thanks to Branson Matheson)
+ * New fixture: American DJ FREQ 5 Strobe (thanks to Martin Bochenek)
+ * New fixture: Martin Rush MH3 (thanks to Ed Middlebrooks)
+ * New fixture: Eurolite LED ACS BAR-12 (thanks to Michael Horber)
+ * New fixtures: Martin Rush MH6 Wash, Cameo Studio PAR 64 RGBWA UV 12W (thanks to Piotr Nowik)
+ * New fixtures: ETEC Moving Spot 60E, Cameo CLM PAR COB 1, Showtec Compact Power Lightset COB (thanks to Freasy)
+ * New fixture: Stairville Tri Flat PAR Profile 5x3W RGB (thanks to Freasy)
+ * New fixture: American DJ Punch LED Pro (thanks to Benedict Stein)
+ * New fixtures: Contest Mini-Head 10W, Contest Evora B2R (thanks to Fredje Gallon)
+ * New fixture: Robe DJ Scan 150 XT (thanks to Allan Madsen)
+ * New fixtures: Futurelight PRO Slim PAR-12 HCL, PRO Slim PAR-12 MK2 HCL, Showtec Power Spot 9 Q5 (thanks to Lukas Jähn)
+ * New fixture: Showtec XS-1W Mini Moving Beam (thanks to Habefaro)
+ * New fixtures: Stage Right Stage Wash 18Wx18 LED PAR, Stage Right 7x20W COB LED Theater PAR (thanks to Collin Ong)
+ * New fixture: Cameo CLPIXBAR450PRO, CLPIXBAR650PRO (thanks to Jean-Daniel Garcia & Jeremie Odermatt)
+ * New fixture: Clay Paky Alpha Beam 1500 (thanks to Louis Gutenschwager)
+ * New fixture: Stairville AFH-600 (thanks to Hannes Braun)
+ * New fixture: Involight LED MH77S (thanks to Jászberényi Szabolcs)
+ * New fixtures: ETEC LED PAR 64 18x10W RGBWA, LED PAR 64 18x15W RGBWA Zoom (thanks to Simon Orlob)
+ * New fixture: Chauvet Swarm Wash FX (thanks to Stephen Olah)
+ * New fixture: Clay Paky Alpha Spot HPE 575 (thanks to Rohmer)
+ * New fixture: Robe LED Blinder 196LT (thanks to Tim Cullingworth)
+ * New fixture: Chauvet COLORband T3 USB (thanks to Ian Nault)
+ * New fixtures: American DJ Dotz Matrix, Martin Jem Compact Hazer Pro,Geni Mojo Spin Master Series (thanks to Sam Brooks)
+ * New fixture: American DJ XS 400 (thanks to Jared)
+ * New fixture: Velleman VDP1500SM (thanks to Freddy Hoogstoel)
+ * New fixture: Chauvet Intimidator Spot 355Z IRC (thanks to Michael Clements)
+ * New fixture: CLF Tricolor Mini Par (thanks to Jaron Blazer)
+ * New fixture: Varytec LED Easy Move Mini Beam & Wash RGBW (thanks to Erik)
+ * New fixtures: Smoke Factory Tour-Hazer II, JB Systems Panther, Robe Spot 160 XT (thanks to Thierry Rodolfo)
+ * New fixture: American DJ LED Trispot (thanks to Patrick)
+ * New fixtures: Contest STB-520 1500W Strobe, Elumen8 COB Tri 4 Pixel Batten, Briteq Tornado 7 (thanks to Robert Box)
+ * New fixtures: American DJ 5P Hex, Pro-Lights Moonstone, Chauvet Intimidator Hybrid 140SR (thanks to Robert Box)
+ * New fixtures: Robe Robin DLX Spot (thanks to Robert Box)
+ * New fixture: ETC ColorSource PAR (thanks to Jon Rosen)
+ * New fixture: lightmaXX 5ive STAR LED (thanks to Thomas Weber)
+ * New fixture: Talent BL252A (thanks to Massimiliano Palmieri)
+ * New fixtures: Showtec: Infinity iW-1915, Infinity XPLO-15 LED Strobe (thanks to Daniele Fogale)
+ * New fixtures: Showtec: Infinity iB-5R, Compact Par 18 MKII, Phantom 20 LED Beam (thanks to Nicolò Zanon)
+ * New fixture: Griven Gobostorm Plus MK2 (thanks to Attilio Bongiorni)
+ * New fixture: Chauvet Freedom Stick (thanks to Jay Szewczyk)
+ * New fixture: Eurolite TMH-14, Chauvet Intimidator Trio (thanks to Chris de Rock)
+ * New fixture: Chauvet Scorpion Dual (thanks to Alan Chavis)
+ * New fixture: American DJ Ultra Hex Bar 12 (thanks to Rhavin)
+ * New fixture: Equinox Photon
+ * New fixture: QTX MHS-60 (thanks to Nerijus Mongirdas)
+ * New fixture: Eurolite LED TMH FE-600, MARQ Colormax Par64, Stairville CLB2.4 Compact LED PAR System (thanks to Klaus Muth)
+ * New fixture: Chauvet SlimPar Hex 6 (thanks to Yinon Sahar)
+ * New fixture: IMG Stageline PARL 20 DMX (thanks to Felix Pickenäcker)
+ * New fixtures: Pro-Lights SmartBatHEX, Fury FY250W, Fury FY250S (thanks to Lorenzo Andreani)
+ * New fixture: American DJ Ikon Profile (thanks to Ham Sadler)
+ * New fixture: HQ Power Aeron Wash 575, JB Systems Space Color Laser (thanks to Ricardo Mendes)
+ * New fixture: American DJ VPar (thanks to Eric Eskam)
+ * New fixtures: MARQ Gesture Beam/Wash 102, Colormax Bat, Gesture Spot 100 (thanks to John Yiannikakis)
+ * New fixtures: Chauvet COLORado 3P, Legend 330SR Spot, SlimPar HEX 3 (thanks to Kevin Zepp)
+ * New fixture: American DJ Mini Dekker (thanks to Chris Davis)
+ * New fixtures: American DJ Vizi BSW 300, Blizzard Lighting Flurry 5 (thanks to George Qualley)
+ * New fixtures: Pro-Lights PIXIEWASH, Accent1Q, CromoSpot300 (thanks to Tolmino Muccitelli)
+ * New fixtures: Involight LED MH50S, LED PAR 180, SBL 2000 (thanks to Facek)
+ * New fixture: Pro-Lights Miniruby (thanks to Dario Gonzalez)
+ * New fixture: Sagitter Smart DL Wash (thanks to Simu)
+ * New fixtures: Eurolite LED THA-250F, Pro-Lights StudioCOBFC (thanks to Andrea Ugolini)
+ * New fixture: American DJ Stinger Spot (thanks to Jason R. Johnston)
+ * New fixture: Stairville Blade Sting 8 RGBW Beam Mover
+
+ -- Massimo Callegari Sat, 24 Jun 2017 12:13:14 +0200
+
+qlcplus (4.10.5b) stable; urgency=high
+
+ * engine: fixed 4.10.5 regression on RGB Matrix preset step color calculation
+ * Virtual Console/Frame: fixed widgets disable state when switching pages
+ * Virtual Console: fixed SpeedDial and Animation widget presets feedbacks, and allow to use custom feedbacks
+ * Plugins/DMX USB: fixed 4.10.5 regression preventing to receive data from PRO devices
+ * Plugins/DMX USB: [Windows] fixed a long standing bug causing random crashes when receiving DMX data
+ * Plugins/MIDI: [macOS] further changes to support virtual ports
+ * New fixtures: Stairville M-Fog 1000 DMX, Cameo Superfly XS (thanks to Konni)
+ * New fixture: ColorKey WaferPar Quad-W 12 (thanks to Taylor)
+ * New fixture: Eurolite LED PARty RGBW (thanks to Heiko Fanieng)
+ * New fixture: lightmaXX Platinum CLS-1 (thanks to Marc Geonet)
+
+ -- Massimo Callegari Mon, 26 Dec 2016 12:13:14 +0200
+
+qlcplus (4.10.5a) stable; urgency=high
+
+ * engine: fixed playback of a chaser within a chaser
+
+ -- Massimo Callegari Mon, 12 Dec 2016 12:13:14 +0200
+
+qlcplus (4.10.5) stable; urgency=low
+
+ * Engine: added indigo to fixture channel colors (thanks to Axel Metzke)
+ * Engine: properly handle RGB Matrices with generic dimmers (Jano Svitok)
+ * UI/Function Manager: fix crash when trying to clone a folder (David Garyga)
+ * UI/RGB Matrix Editor: editor preview doesn't stop when testing the Function
+ * UI/Collection Editor: allow multiple selection and added Function reordering buttons
+ * UI/Remap: fixed universes list in target mapping
+ * UI/Remap: fixed wrong Scene remapping when mixing cloned and new fixtures
+ * UI/Remap: added remapping also of Fixture Groups
+ * Virtual Console/Frame: Show page number when collapsed (thanks to Matthias Gubisch)
+ * Virtual Console/Cue List: allow to choose playback buttons layout (Play/Pause + Stop or Play/Stop + Pause)
+ * Plugins/DMX USB: fixed crash happening on PRO devices when receiving a full universe
+ * Plugins/DMX USB: [MacOS] fixed regression caused by the Qt libraries on PRO devices
+ * Plugins/MIDI: [MacOS] added support for virtual ports, show only active devices and properly handle hotplug
+ * Fixture Editor: fixed minimum value of a new capability not updating correctly
+ * RGB Scripts: added One by one (Jano Svitok)
+ * New fixtures: Pro-Lights LumiPIX 12Q, Proel PLLEDMLBG (thanks to Andrea Ugolini)
+ * New fixtures: Stairville DCL Flat Par 18x4W CW/WW, Cameo LED MultiPAR CLM-PAR-COB1 (thanks to Freasy)
+ * New fixtures: High End Systems Studio Beam, lightmaXX EASY Wash 5IVE LED (thanks to Freasy)
+ * New fixture: iSolution iColor 4 (thanks to withlime)
+ * New fixtures: ETC ColorSource Spot, Blizzard Lighting LB-Par Hex (thanks to Robert Box)
+ * New fixtures: American DJ: Chameleon QBar Pro,DJ Vizi Beam RXONE, XS 600, Focus Spot Three Z (thanks to Robert Box)
+ * New fixture: JB-Lighting Varyscan P6, Cameo Wookie series, Cameo Hydrabeam series (thanks to Andres Robles)
+ * New fixture: Chauvet RotoSphere LED (thanks to Carl Eisenbeis)
+ * New fixture: Briteq Spectra 3D Laser (thanks to Robert Box + Freasy)
+ * New fixture: Martin MH2 Wash (thanks to John Yiannikakis + Freasy)
+ * New fixture: American DJ Flat Par Tri7X (thanks to Brian)
+ * New fixtures: Ledj Stage Color 24, 59 7Q5 RGBW, 59 7Q5 RGBA (thanks to Paul Wilton)
+ * New fixtures: American DJ: Inno Spot Elite, Stinger, Tri Phase (thanks to Piotr Nowik)
+ * New fixtures: Showtec Phantom 95 LED Spot, Futurelight PHS-260 (thanks to Piotr Nowik)
+ * New fixture: Blizzard Lighting Rocklite RGBAW (thanks to Larry Wall)
+ * New fixture: American DJ Comscan LED (thanks to Chris)
+ * New fixtures: PR Lighting XL 250/XL 700 Wash/XL 700 Spot, American DJ Accu Fog 1000 (thanks to István Király)
+ * New fixtures: Equinox Ultra Scan LED, Kam Powercan84W, QTX HZ-3 (thanks to Chris Moses)
+ * New fixture: Eurolite TMH-10 (thank to exmatrikulator)
+ * New fixture: Eurolite LED SLS 5 BCL, Robe Fog 1500 FT (thanks to Christian Hollbjär)
+ * New fixture: SGM Giotto Spot 400 (thanks to Mihai Andrei)
+ * New fixture: Pulse LEDBAR 320 (thanks to Allan Rhynas)
+ * New fixture: Equinox Swing Batten (thanks to Dean Clough)
+ * New fixture: Cameo Pixbar 600 PRO, Chauvet COLORado 1 Quad Zoom Tour (thanks to Andrew Hallmark)
+ * New fixture: Involight FM900 DMX (thanks to Jászberény Szabolcs)
+ * New fixture: Showtec Stage Blinder Series (thanks to Antoni J. Canós)
+ * New fixture: MARQ Gamut PAR H7 (thanks to Lance Lyda)
+ * New fixtures: Chauvet: SlimPAR QUV12 USB, SlimPAR PRO H USB, Scorpion Bar RG (thanks to Pete Mueller)
+ * New fixture: Stairville CLB8 Compact LED PAR System (thanks to Detlef Fossan)
+ * New fixture: Chauvet Cubix 2.0 (thanks to Jungle Jim)
+ * New fixture: Showtec Giant XL LED (thanks to Samuel Hofmann)
+ * New fixtures: SGM: Idea Beam 300, Idea Led Bar 100, Idea Spot 700, Newton 1200 (thanks to Oscar Cervesato)
+ * New fixtures: Pro Lights LumiPAR18QTour, Elation SIXPAR 200IP (thanks to Oscar Cervesato)
+ * New fixture: Stairville Beam Moving Head B5R, American DJ Flat Par TW12, Varytec Easy Scan XT Mini (thanks to Thierry Rodolfo)
+
+ -- Massimo Callegari Sat, 3 Dec 2016 12:13:14 +0200
+
+qlcplus (4.10.4) stable; urgency=low
+
+ * Scripts: Fix 4.10.3a regression that breaks values parsing (David Garyga)
+ * Engine: fix relative paths when opening a project from the command line
+ * Engine: improved the start/stop mechanism of Functions within a Show
+ * Chaser Editor: a newly created step is now selected automatically
+ * Scene Editor: fixed the tab order of the fixtures
+ * Show Manager: added the possibility to pause a Show leaving the lights on
+ * Show Manager/Audio: allow to display the waveform preview while playing a file
+ * UI/Function Selection: fix crash on workspaces where a scene ID is bigger than its sequence ID (David Garyga)
+ * UI/Video: fixed the fullscreen positioning on Windows
+ * Virtual Console/Animation: fix behavior issue when changing the associated function (David Garyga)
+ * Virtual Console/Frames: send feedbacks for the enable button
+ * Virtual Console/Frames: fix 4.10.3 regression causing frames to resize after configuration
+ * Virtual Console/Cue List: playback can now be paused and resumed (see documentation)
+ * Virtual Console/Cue List: added a dedicated stop button, with external controls
+ * Virtual Console/XYPad: fixed computation of reversed fixture position (Luca Ugolini)
+ * Plugins/OSC: fixed regression of receiving data from the wrong interface (David Garyga)
+ * Plugins/OSC: fixed regression causing not receiving data anymore when changing the input profile (David Garyga)
+ * Plugins/MIDI: distinguish MIDI beat clock start and stop (see documentation)
+ * Input Profiles Editor: it is now possible to define button custom feedbacks in a profile (see documentation)
+ * New input profile: Novation Launchpad Pro (thanks to David Giardi)
+ * New RGB script: Balls (color) (thanks to Rob Nieuwenhuizen)
+ * Fixture updated: Starway MaxKolor-18 (thanks to Thierry Rodolfo and Robert Box)
+ * Fixture updated: Cameo LED RGBW PAR64 18x8W (thanks to Lukas)
+ * New fixture: American DJ Mega QA Par38 (thanks to Nathan Durnan)
+ * New fixture: Martin MAC 250 Wash (thanks to Robert Box)
+ * New fixture: Luxibel LX161 (thanks to Freddy Hoogstoel)
+ * New fixture: Stairville MH-X60th LED Spot (thanks to Jasper Zevering)
+ * New fixture: Cameo CLHB400RGBW (thanks to Mihai Andrei)
+ * New fixture: Showlite Flood Light Panel 144x10mm LED RGBW (thanks to Ex)
+ * New fixture: Color Imagination LedSpot 90 (SI-052), Robe Spot 575 XT (thanks to DJ Ladonin)
+ * New fixture: Chauvet Mini Kinta (thanks to Jonathan Wilson)
+ * New fixture: Eurolite LED ML-56 QCL RGBW-RGBA 18x8W (thanks to Matthijs ten Berge)
+ * New fixture: High End Systems TechnoSpot (thanks to Tom Moeller)
+ * New fixtures: American DJ Inno Pocket Spot Twins, Fog Fury 3000 WiFly, Event Bar Pro (thanks to MaBonzo)
+ * New fixtures: American DJ Galaxian Gem IR, Vizi Roller Beam 2R (thanks to MaBonzo)
+ * New fixture: Ayra ERO 506 (thanks to Bert Heikamp)
+ * New fixture: Ayrton Arcaline 100 RGB, Martin Magnum Hazer (thanks to Thierry Rodolfo)
+ * New fixtures: American DJ Asteroid 1200, Eurolite GKF-60, Eurolite LED FE-700 (thanks to Flox Garden)
+ * New fixtures: Antari X-310 Pro Fazer, lightmaXX CLS-2 (thanks to Flox Garden)
+ * New fixture: Beamz MHL90 Wash 5x18W RGBAW-UV (thanks to Hans Erik Tjelum)
+ * New fixture: PR Lighting Pilot 150 (thanks to David Read)
+ * New fixture: lightmaXX Platinum CLS-1 (thanks to Marc Geonet)
+
+ -- Massimo Callegari Sun, 29 May 2016 12:13:14 +0200
+
+qlcplus (4.10.3a) stable; urgency=low
+
+ * Scripts: Fix 4.10.3 regression that breaks time values parsing (David Garyga)
+ * RGBMatrix Editor: Fix 4.10.3 regression where QLC+ hangs on duration < 20ms (David Garyga)
+
+ -- Massimo Callegari Wed, 9 Mar 2016 22:03:14 +0200
+
+qlcplus (4.10.3) stable; urgency=low
+
+ * Engine: Fix intensity channels forced back to HTP after LTP not working correctly (David Garyga)
+ * Engine: Fix functions with low intensity killing current fade outs (David Garyga)
+ * Audio Capture: Fix crash when selecting another audio input while a capture is running (David Garyga)
+ * Audio Capture: Fix crash when trying to use a wrongly configured audio input (David Garyga)
+ * Scene Editor: Remember Channels Groups values when saving and loading a workspace (David Garyga)
+ * Scene Editor: Remember fixtures even with no activated channel (David Garyga)
+ * RGBMatrix Editor: Fix preview now working when fade in > 0 (David Garyga)
+ * RGBMatrix Editor: Fix length of fadeout on the preview (David Garyga)
+ * Show Manager: Fix crash when editing the total time of an empty chaser (David Garyga)
+ * Show Manager/Function Selection: Fix sequences always displayed even with Chasers and Scenes both filtered out (David Garyga)
+ * Speed Dials: Fix display and input of the milliseconds field, update precision from 10ms to 1ms (David Garyga)
+ * Input/Output Manager: Forbid deleting universes in the middle of the list, this prevents a lot of bugs and crashes (David Garyga)
+ * Simple Desk: the number of faders is now dynamic depending on the window size (unless forced via config file)
+ * Plugins/ArtNet: Fix input and output initialization conflict that results in no input (David Garyga)
+ * Plugins/ArtNet: Allow sending and receiving ArtNet on several different interfaces (David Garyga)
+ * Plugins/ArtNet: Allow selecting a different ArtNet input universe (David Garyga)
+ * Plugins/ArtNet: Fix configuration issue that prevents setting a parameter back to its default value (David Garyga)
+ * Plugins/OSC: Fix configuration issue that prevents setting a parameter back to its default value (David Garyga)
+ * Plugins/OSC: OSC Output values range from 0.0 to 1.0
+ * Plugins/OSC: Properly handle OSC bundles (restores Lemur compatibility)
+ * Virtual Console: Fix copy of a frame containing a submaster slider resulting in a broken submaster (David Garyga)
+ * Virtual Console/Slider: Enable function filters in playback function selection (David Garyga)
+ * Virtual Console/Slider: Allow to force to LTP color channels controlled by a Click & Go button
+ * Virtual Console/Solo Frame: Fix sliders in playback mode not actually stopping the attached function when the slider reaches 0 (David Garyga, thanks to Tubby)
+ * Virtual Console/Animation: Can now be used in solo frames (David Garyga)
+ * Virtual Console/Frames: fix page cloning of a nested multipage frame
+ * Virtual Console/Frames: fix disabling frame pages. Now widgets get actually deleted
+ * Web access: fixed custom fixtures loading
+ * Web access: added a DMX keypad that can be accessed from the Simple Desk (thanks to Santiago Benejam Torres)
+ * Input profiles: added Behringer BCR2000 (thanks to Michael Trojacher)
+ * Input profiles: added Lemur iPad Studio Combo
+ * RGB Scripts: added Strobe script (thanks to Rob Nieuwenhuizen)
+ * New fixtures: Stellar Labs ECO LED PAR56, Chauvet Colorpalette II (thanks to Jimmy Traylor)
+ * New fixtures: Chauvet: COLORado 1 Solo, Ovation FD-165WW, Rogue RH1 Hybrid, COLORdash Par Hex 12, COLORdash Accent Quad (thanks to Robert Box)
+ * New fixtures: Chauvet: Vue 1.1, Intimidator Spot 100 IRC, Abyss USB, COREpar 40 USB, COREpar UV USB (thanks to Robert Box)
+ * New fixtures: Chauvet: Intimidator Scan 305 IRC, Intimidator Barrel 305 IRC, SlimPAR T6 USB, SlimBANK TRI-18 (thanks to Robert Box)
+ * New fixtures: Eurolite LED CLS-9 QCL RGBW 9x8W 12, JB-Lighting A12 Tunable White, SGM G-Profile (thanks to Robert Box)
+ * New fixtures: Coemar Par Lite LED RGB, OXO LED Funstrip DMX, American DJ Stinger II (thanks to Robert Box)
+ * New fixture: Chauvet LED PAR 64 Tri-C (thanks to Jungle Jim and Robert Box)
+ * New fixtures: American DJ VBar, American DJ Jellydome, Briteq LDP Powerbar 6TC/12TC (thanks to Thierry Rodolfo)
+ * New fixture: Sagitter Slimpar 18 RGB (thanks to Daniele Fogale)
+ * New fixture: Microh LED Tri Bar (thanks to Michael Tughan)
+ * New fixture: American DJ 12P Hex Pearl (thanks to Ethan Moses)
+ * New fixture: JB-Lighting JBLED A7 (thanks to BLACKsun)
+ * New fixture: Chauvet COREpar 80 USB (thanks to Chris Gill)
+ * New fixture: Stairville DJ Lase 25+25-G MK-II (thanks to galaris)
+ * New fixtures: PR Lighting XR 230 Spot, PR Lighting XLED 1037 (thanks to Ovidijus Cepukas)
+ * New fixtures: Futurelight DJ-Scan 600, Eurolite LED PAR-64 RGBW+UV (thanks to Ovidijus Cepukas)
+ * New fixtures: Varytec LED Pad 7 BA-D, American DJ X-Scan LED Plus, Showtec Blade Runner (thanks to DjProWings)
+ * New fixture: Involight LED CC60S (thanks to Stephane Hofman)
+ * New fixture: Stairville MH-x200 Pro Spot (thanks to Mirek Škop)
+ * New fixtures: Varytec LED Giga Bar 4 MKII, Eurolite LED KLS Laser Bar FX Light Set (thanks to Daniel Schauder)
+ * New fixture: Chauvet Mayhem (thanks to Jonathan Wilson)
+ * New fixture: Ayra TDC Agaricus (thanks to Rob Nieuwenhuizen)
+ * New fixture: American DJ Pinspot LED Quad DMX (thanks to Christian Polzer)
+ * New fixture: Stairville AF-180 LED Fogger Co2 FX (thanks to Johannes Uhl)
+
+ -- Massimo Callegari Sun, 6 Mar 2016 20:21:22 +0200
+
+qlcplus (4.10.2) stable; urgency=low
+
+ * Engine: added support for devices hotplug (DMX USB, MIDI, HID, Peperoni)
+ * Engine: Universe passthrough data is now merged with QLC+ output, it is not affected by QLC+ processing
+ (except for blackout) and it appears in the DMX monitor (Jano Svitok)
+ * Audio: fixed playback of 24/32 bit wave files and Show Manager waveform preview
+ * DMX Dump: it is now possible to dump DMX values on an existing Scene
+ * ClickAndGo Widgets: Preset widgets now display the channel name on top of the capability list (David Garyga)
+ * Function Manager: fix startup Function not cleared when deleting it (David Garyga)
+ * Function Manager: highlight current startup Function when opening the Function selection dialog (David Garyga)
+ * Function Selection: Don't lose the current selection when changing the function type filter (David Garyga)
+ * Show Manager: fixed looped functions never stopping with certain durations (Jano Svitok)
+ * Show Manager: fixed copy/paste of an existing Chaser
+ * Show Manager: fix crashes when copying a sequence on an empty track (David Garyga)
+ * Show Manager: repair conflicting sequences when loading a broken workspace (David Garyga)
+ * EFX Editor: removed the intensity control. Please use separate Scenes for that
+ * Virtual Console/Slider: fixed copy of the channel monitor mode (David Garyga)
+ * Virtual Console/Slider: in level mode, activate only in operate mode and don't hold forced LTP channels (David Garyga)
+ * Virtual Console/Slider: in playback mode, ignore the fade in/fade out of the attached Function (David Garyga)
+ * Virtual Console/XYPad: added Fixture Group preset, to control a subgroup of Fixtures (see documentation)
+ * Virtual Console/XYPad Properties: fixture ranges can now be set in degrees, percentage or DMX values
+ * Virtual Console/XYPad Properties: fix manual input selection for presets (David Garyga)
+ * Virtual Console/Cue List: improved mixed usage of crossfader and next/previous buttons (David Garyga)
+ * Virtual Console/Cue List: fix effect of a submaster slider on a Cue List in crossfader mode (David Garyga)
+ * Virtual Console/Cue List: fix crash when adding steps to the chaser being run by a Cue List (David Garyga)
+ * Virtual Console/Audio Triggers: fix virtual console buttons triggering (David Garyga)
+ * Virtual Console/Input Selection: allow custom feedbacks only on an assigned source and don't crash (David Garyga)
+ * DMX Monitor: Fix strobing in 2D view (Jano Svitok)
+ * Fixture Editor: Fix crash in the Channel Editor (David Garyga)
+ * Plugins/uDMX: added support for AVLdiy.cn clone (thanks to Vitalii Husach)
+ * Plugins/DMXUSB: (Linux) fixed data transmission of DMX4ALL NanoDMX
+ * Input Profiles: added an option for Buttons to always generate a press/release event
+ * New input profile: Touch OSC Automat5
+ * New input profile: Novation Launch Control (thanks to Giacomo Gorini)
+ * Updated fixture: Stairville xBrick Full-Colour 16X3W (thanks to Rico Hansen)
+ * Updated fixture: Stairville MH-100 Beam 36x3 LED (thanks to Antoni J. Canos)
+ * Updated fixture: American DJ Revo 3 (thanks to David Pilato)
+ * New fixture: American DJ Dotz Flood
+ * New fixture: Chauvet COLORdash Par Quad-7
+ * New fixtures: Robe ColorWash 1200E AT, American DJ Starburst, Chauvet LED PAR 64 Tri-B (thanks to Robert Box)
+ * New fixtures: Cameo Multi Par 3, HQ Power VDPL110CC LED Tri Spot, Showtec LED Pixel Track Pro (thanks to Robert Box)
+ * New fixtures: American DJ: Inno Pocket Z4, On-X, WiFly EXR Dotz Par, WiFly EXR HEX5 IP, COB Cannon Wash Pearl (thanks to Robert Box)
+ * New fixtures: BoomTone DJ Sky bar 288 LED, BoomToneDJ Strob LED 18, BoomToneDJ Froggy LED RGBW (thanks to Didou)
+ * New fixture: iSolution iMove 250W (thanks to Thierry Rodolfo)
+ * New fixture: Talent BL63 10" LED Bar (thanks to FooSchnickens)
+ * New fixtures: Contest Oz-37x15QC, Evora DUO B2R, Evora Beam 5R, Evora Beam 15R (thanks to Jan Lachman)
+ * New fixtures: Blizzard Lighting Lil G, Pixellicious, Lo-Pro CSI (thanks to Alton Olson)
+ * New fixtures: Stairville LED Matrix Blinder 5x5, Showtec Power Spot 9 Q6 Tour V1 (thanks to Samuel)
+ * New fixture: Blizzard Lighting StormChaser (thanks to Brent)
+ * New fixtures: Showtec Explorer 250 Pro MKII, Showtec Pixel Bar 12 (thanks to Henk de Gunst)
+ * New fixture: Philips Selecon PLProfile1 MkII (thanks to Freasy)
+ * New fixture: PSL Strip Led RGB code K2014 (thanks to Lorenzo Andreani)
+ * New fixture: Chauvet SlimPar Pro Tri (thank to Bulle)
+ * New fixture: Chauvet GigBar IRC (thanks to JD-HP-DV7 and Jungle Jim)
+ * New fixtures: Ghost Green 30, KOOLlight 3D RGB Laser, Mac Mah Mac FOG DMX (thanks to David Pilato)
+
+ -- Massimo Callegari Sun, 13 Dec 2015 20:21:22 +0200
+
+qlcplus (4.10.1) stable; urgency=high
+
+ * Virtual Console/Cue List: improved step fader behaviour (David Garyga)
+ * Plugins/DMXUSB: Fixed regression affecting Linux users and OSX users using the libFTDI interface
+ * Plugins/ArtNet/E1.31/OSC: Improved network interfaces detection
+ * New fixture: Enterius EC-133DMX (thanks to Krzysztof Ratynski)
+ * New fixture: Showtec Dragon F-350 (thanks to Jasper Zevering)
+ * New fixtures: JB Systems Lounge Laser DMX, JB Systems Super Solar RGBW (thanks to Robert Box)
+
+ -- Massimo Callegari Wed, 21 Oct 2015 20:21:22 +0200
+
+qlcplus (4.10.0) stable; urgency=low
+
+ * Channel Groups: Fix crashes related to invalid channels (David Garyga)
+ * Chaser: Fix flickering issue when chaser order is Random (David Garyga)
+ * Engine: some more fixes on forced HTP/LTP channels
+ * Engine: fixed 4.9.x regression causing QLC+ to hang at the end of audio playback
+ * RGB Matrix Audio Spectrum: Fix crash when audio input volume is set to zero (David Garyga)
+ * RGB Matrix: Fix 4.9.1 regression which makes fading of green and blue colors not smooth (David Garyga)
+ * RGB Matrix: Introduced blending mode between matrices (see documentation)
+ * Audio Input: Fix crashes when selecting another audio input device while an audio input driven function/widget is running (David Garyga)
+ * Audio Input: It is now possible to select the audio input format (sample rate and channels)
+ * Video: fixed playback from a time offset (where possible)
+ * Video: (Windows) Videos now have a black background, like all the other platforms
+ * Add Fixture dialog: Generic fixtures don't take the number of channels of the previously selected fixture (David Garyga)
+ * Add Fixture dialog: Fix address 512 not usable by adding several fixtures at a time (David Garyga)
+ * Scene Editor: correctly select the fixture tab when switching from tabbed/all channels view
+ * EFX Editor: it is now possible to use an EFX on RGB channels (thanks to Giorgio Rebecchi)
+ * Collection Editor: added preview button (thanks to Giorgio Rebecchi)
+ * Fixture Remap: fixed remapping of EFX functions
+ * Function Wizard: improved creation of color scenes for RGB panels
+ * Function Wizard: automatically set a gobo picture (if available) on buttons attached to gobo Scenes
+ * Show Manager: Fix some cursor teleportation issues (David Garyga)
+ * Simple Desk: Fix cue playback on universes 2+ (David Garyga)
+ * Simple Desk: Fix crash when selecting recently added universe (David Garyga)
+ * Simple Desk: Added reset buttons to reset a single channel
+ * Simple Desk: Fix page count when channels per page does not divide 512 (Jano Svitok, reported by Florian)
+ * Virtual Console: fixed Grand Master not sending feedbacks
+ * Virtual Console/Input Controls: implemented custom feebacks. For now used only by VC buttons
+ * Virtual Console/Solo Frame: Option to allow mixing of sliders in playback mode (David Garyga)
+ * Virtual Console/Speed Dial: Introduced multiplier/divisor, apply and presets buttons (see documentation)
+ * Virtual Console/Button: allow to set a background picture
+ * Virtual Console/Cue List: Options for the Next/Previous buttons behavior (David Garyga)
+ * Virtual Console/Cue List: Fixed playback of a Chaser in reverse order (David Garyga)
+ * Virtual Console/Cue List: Added a new "Steps" mode for the side faders (see documentation)
+ * Virtual Console/Cue List: Allow to resize columns to 0 pixels, to completely hide them
+ * Virtual Console/XYPad: Introduced presets, including the usage of existing EFX and Scenes (see documentation)
+ * Virtual Console/XYPad: Fix DMX output not working when going to Operate mode while the XYPad is disabled (David Garyga, thanks to bestdani)
+ * Input Profiles: added an option for MIDI profiles to feedback a Note Off or a Note On with 0 velocity. APCMini now works out of the box. (Jano Svitok)
+ * Input Profiles: Improved BCF2000 Input profile (thanks to Lorenzo Andreani)
+ * Plugins/MIDI: (Linux) Fixed data transmission to multiple devices (thanks to Adrian Kapka)
+ * Plugins/MIDI: fixed Program Change handling on OSX and Windows
+ * Plugins/MIDI: (Windows) do not close the device when sending SysEx data
+ * Plugins/ArtNet: it is now possible to enter an arbitrary output IP
+ * Plugins/OSC: it is now possible to enter an arbitrary output IP
+ * Plugins/E1.31: added stream priority to configuration (thanks to Nathan Durnan)
+ * Plugins/E1.31: added unicast support (David Garyga)
+ * Plugins/DMXUSB: fixed close/open sequence on a Enttec Pro input line
+ * Web Access: implemented frames collapse functionality
+ * RGB Scripts: added Plasma Colors script (thanks to Nathan Durnan)
+ * Fixture Editor: Channel capability editing is now done in a single window
+ * Updated fixture: Showtec Indigo 6500 (thanks to Jochen Becker)
+ * New fixture: Ayra ComPar 20 (thanks to Rob Nieuwenhuizen)
+ * New fixture: XStatic X-240Bar RGB (thanks to Nathan Durnan)
+ * New fixture: Venue ThinPAR 38 (thanks to Thierry Rodolfo)
+ * New fixtures: Contest MiniCube-6TCb, Eurolite LED FE-1500, Lightronics FXLD618C2I, JB Systems COB-4BAR (thanks to Robert Box)
+ * New fixtures: Eurolite LED KLS-401, Chauvet Intimidator Wash Zoom 350 IRC, Equinox Party Par LED PAR 56 (thanks to Robert Box)
+ * New fixtures: Robe Robin MiniMe, PR Lighting Pilot 575, Stairville DJ Lase 150-RGY MkII, JB Systems Dynaspot (thanks to Robert Box)
+ * New fixtures: American DJ Hyper Gem LED, Robe ColorSpot 575 AT, Kam iLink All Colour Models (thanks to Robert Box)
+ * New fixtures: Chauvet Intimidator Wave 360 IRC, Varytec LED PAR56 (thanks to Habefaro)
+ * New fixture: American DJ Fog Fury Jett (thanks to Dean Clough)
+ * New fixtures: Eurolite TB-250, Futurelight DJ-HEAD 575 SPOT, GLX Lighting Power LED Beam 38 Narrow (thanks to Ovidijus Cepukas)
+ * New fixture: Pro-Lights UVStrip18 (thanks to Alessandro Grechi)
+ * New fixtures: American DJ Inno Pocket Beam Q4, Martin ZR24/7 Hazer, Blizzard Lighting Stimul-Eye (thanks to George Qualley)
+ * New fixture: American DJ Mega TriPar Profile Plus (thanks to George Qualley)
+ * New fixtures: Pro-Lights SmartBat, Robe ClubWash 600 CT (thanks to Lorenzo Andreani)
+ * New fixture: Stairville Show Bar Tri 18x3W RGB (thanks to Udo Besenreuther)
+ * New fixtures: Blizzard Lighting Rokbox Infiniwhite, Chauvet COREpar 80 (thanks to Chris Gill)
+ * New fixture: Cameo CL Superfly HP (thanks to Stuart Brown)
+ * New fixture: American DJ Event Bar Q4 (thanks to Maxime Bissonnette-Théorêt)
+ * New fixture: Cameo LED Moving Head 60W CLMHR60W (thanks to Jasper Zevering)
+ * New fixtures: Proel PLLED64RGB, Litecraft LED PAR 64 AT3, Robe Robin 300E Beam (thanks to Mihai Andrei)
+ * New fixture: Electroconcept SPC029 (thanks to Bulle)
+ * New fixture: Microh Plasmawave 1 RGB (thanks to Rommel)
+
+ -- Massimo Callegari Sun, 18 Oct 2015 20:21:22 +0200
+
qlcplus (4.9.1) stable; urgency=high
* RGBMatrix: SingleShot RGBMatrix make use of FadeOut time (David Garyga)
diff --git a/engine/audio/src/CMakeLists.txt b/engine/audio/src/CMakeLists.txt
index 9aa7dd7bc4..cd51c39823 100644
--- a/engine/audio/src/CMakeLists.txt
+++ b/engine/audio/src/CMakeLists.txt
@@ -9,6 +9,7 @@ add_library(${module_name}
audioplugincache.cpp audioplugincache.h
audiorenderer.cpp audiorenderer.h
)
+set_property(TARGET ${module_name} PROPERTY POSITION_INDEPENDENT_CODE ON)
target_include_directories(${module_name} PUBLIC
../../../plugins/interfaces
../../src
diff --git a/engine/audio/src/audiocapture.h b/engine/audio/src/audiocapture.h
index 58a9484101..f6fefd73c0 100644
--- a/engine/audio/src/audiocapture.h
+++ b/engine/audio/src/audiocapture.h
@@ -135,6 +135,7 @@ class AudioCapture : public QThread
signals:
void dataProcessed(double *spectrumBands, int size, double maxMagnitude, quint32 power);
+ void volumeChanged(int volume);
protected:
/*!
diff --git a/engine/audio/src/audiocapture_qt5.cpp b/engine/audio/src/audiocapture_qt5.cpp
index 79920b42e3..cd9bb3fb78 100644
--- a/engine/audio/src/audiocapture_qt5.cpp
+++ b/engine/audio/src/audiocapture_qt5.cpp
@@ -113,9 +113,14 @@ qint64 AudioCaptureQt6::latency()
void AudioCaptureQt6::setVolume(qreal volume)
{
+ if (volume == m_volume)
+ return;
+
m_volume = volume;
if (m_audioInput != NULL)
m_audioInput->setVolume(volume);
+
+ emit volumeChanged(volume * 100.0);
}
void AudioCaptureQt6::suspend()
diff --git a/engine/audio/src/audiocapture_qt6.cpp b/engine/audio/src/audiocapture_qt6.cpp
index 3911c90301..6e3db68a61 100644
--- a/engine/audio/src/audiocapture_qt6.cpp
+++ b/engine/audio/src/audiocapture_qt6.cpp
@@ -113,9 +113,14 @@ qint64 AudioCaptureQt6::latency()
void AudioCaptureQt6::setVolume(qreal volume)
{
+ if (volume == m_volume)
+ return;
+
m_volume = volume;
if (m_audioSource != NULL)
m_audioSource->setVolume(volume);
+
+ emit volumeChanged(volume * 100.0);
}
void AudioCaptureQt6::suspend()
diff --git a/engine/src/CMakeLists.txt b/engine/src/CMakeLists.txt
index 428d31c089..54597e5a91 100644
--- a/engine/src/CMakeLists.txt
+++ b/engine/src/CMakeLists.txt
@@ -44,6 +44,7 @@ add_library(${module_name} SHARED
qlcfixturemode.cpp qlcfixturemode.h
qlci18n.cpp qlci18n.h
qlcinputchannel.cpp qlcinputchannel.h
+ qlcinputfeedback.cpp qlcinputfeedback.h
qlcinputprofile.cpp qlcinputprofile.h
qlcinputsource.cpp qlcinputsource.h
qlcmodifierscache.cpp qlcmodifierscache.h
diff --git a/engine/src/chaser.cpp b/engine/src/chaser.cpp
index 3a127628e9..a00b3b3f76 100644
--- a/engine/src/chaser.cpp
+++ b/engine/src/chaser.cpp
@@ -132,6 +132,7 @@ bool Chaser::addStep(const ChaserStep& step, int index)
}
emit changed(this->id());
+ emit stepsListChanged(this->id());
return true;
}
else
@@ -150,6 +151,7 @@ bool Chaser::removeStep(int index)
}
emit changed(this->id());
+ emit stepsListChanged(this->id());
return true;
}
else
diff --git a/engine/src/chaser.h b/engine/src/chaser.h
index e7d0ebc511..15bf706173 100644
--- a/engine/src/chaser.h
+++ b/engine/src/chaser.h
@@ -157,6 +157,7 @@ public slots:
signals:
void stepChanged(int index);
+ void stepsListChanged(quint32 fid);
protected:
QList m_steps;
diff --git a/engine/src/chaserrunner.cpp b/engine/src/chaserrunner.cpp
index 3ea2a8a436..ee2a495668 100644
--- a/engine/src/chaserrunner.cpp
+++ b/engine/src/chaserrunner.cpp
@@ -52,7 +52,7 @@ ChaserRunner::ChaserRunner(const Doc *doc, const Chaser *chaser, quint32 startTi
m_pendingAction.m_fadeMode = Chaser::FromFunction;
m_pendingAction.m_stepIndex = -1;
- if (m_chaser->type() == Function::SequenceType && startTime > 0)
+ if (startTime > 0)
{
qDebug() << "[ChaserRunner] startTime:" << startTime;
int idx = 0;
@@ -577,7 +577,7 @@ void ChaserRunner::startNewStep(int index, MasterTimer *timer, qreal mIntensity,
newStep->m_intensityOverrideId = func->requestAttributeOverride(Function::Intensity, mIntensity * sIntensity);
}
- // Start the fire up !
+ // Start the fire up!
func->start(timer, functionParent(), 0, newStep->m_fadeIn, newStep->m_fadeOut,
func->defaultSpeed(), m_chaser->tempoType());
m_runnerSteps.append(newStep);
diff --git a/engine/src/efx.cpp b/engine/src/efx.cpp
index b8ef16de40..3a3d408874 100644
--- a/engine/src/efx.cpp
+++ b/engine/src/efx.cpp
@@ -272,7 +272,7 @@ void EFX::preview(QPolygonF &polygon, Function::Direction direction, int startOf
}
}
-void EFX::calculatePoint(Function::Direction direction, int startOffset, float iterator, float* x, float* y) const
+void EFX::calculatePoint(Function::Direction direction, int startOffset, float iterator, float *x, float *y) const
{
iterator = calculateDirection(direction, iterator);
iterator += convertOffset(startOffset + getAttributeValue(StartOffset));
@@ -283,7 +283,7 @@ void EFX::calculatePoint(Function::Direction direction, int startOffset, float i
calculatePoint(iterator, x, y);
}
-void EFX::rotateAndScale(float* x, float* y) const
+void EFX::rotateAndScale(float *x, float *y) const
{
float xx = *x;
float yy = *y;
@@ -330,7 +330,7 @@ float EFX::calculateDirection(Function::Direction direction, float iterator) con
}
// this function should map from 0..M_PI * 2 -> -1..1
-void EFX::calculatePoint(float iterator, float* x, float* y) const
+void EFX::calculatePoint(float iterator, float *x, float *y) const
{
switch (algorithm())
{
@@ -1081,6 +1081,7 @@ QSharedPointer EFX::getFader(QList universes, quint32
fader->setBlendMode(blendMode());
fader->setName(name());
fader->setParentFunctionID(id());
+ fader->setHandleSecondary(true);
m_fadersMap[universeID] = fader;
}
@@ -1094,7 +1095,7 @@ void EFX::preRun(MasterTimer* timer)
QListIterator it(m_fixtures);
while (it.hasNext() == true)
{
- EFXFixture* ef = it.next();
+ EFXFixture *ef = it.next();
Q_ASSERT(ef != NULL);
ef->setSerialNumber(serialNumber++);
}
diff --git a/engine/src/efx.h b/engine/src/efx.h
index 3c248bdf94..1c7d3e6767 100644
--- a/engine/src/efx.h
+++ b/engine/src/efx.h
@@ -183,7 +183,7 @@ class EFX : public Function
* @param x Used to store the calculated X coordinate (output)
* @param y Used to store the calculated Y coordinate (output)
*/
- void calculatePoint(Function::Direction direction, int startOffset, float iterator, float* x, float* y) const;
+ void calculatePoint(Function::Direction direction, int startOffset, float iterator, float *x, float *y) const;
private:
diff --git a/engine/src/efxfixture.cpp b/engine/src/efxfixture.cpp
index 144b2ec524..45420be61e 100644
--- a/engine/src/efxfixture.cpp
+++ b/engine/src/efxfixture.cpp
@@ -53,6 +53,11 @@ EFXFixture::EFXFixture(const EFX* parent)
, m_started(false)
, m_elapsed(0)
, m_currentAngle(0)
+
+ , m_firstMsbChannel(QLCChannel::invalid())
+ , m_firstLsbChannel(QLCChannel::invalid())
+ , m_secondMsbChannel(QLCChannel::invalid())
+ , m_secondLsbChannel(QLCChannel::invalid())
{
Q_ASSERT(parent != NULL);
@@ -382,8 +387,52 @@ uint EFXFixture::timeOffset() const
* Running
*****************************************************************************/
-void EFXFixture::start()
+void EFXFixture::start(QSharedPointer fader)
{
+ Fixture *fxi = doc()->fixture(head().fxi);
+
+ /* Cache channels to reduce processing while running */
+ switch (m_mode)
+ {
+ case PanTilt:
+ {
+ m_firstMsbChannel = fxi->channelNumber(QLCChannel::Pan, QLCChannel::MSB, head().head);
+ m_firstLsbChannel = fxi->channelNumber(QLCChannel::Pan, QLCChannel::LSB, head().head);
+ m_secondMsbChannel = fxi->channelNumber(QLCChannel::Tilt, QLCChannel::MSB, head().head);
+ m_secondLsbChannel = fxi->channelNumber(QLCChannel::Tilt, QLCChannel::LSB, head().head);
+
+ /* Check for non-contiguous channels */
+ if ((m_firstLsbChannel != QLCChannel::invalid() && m_firstLsbChannel - m_firstMsbChannel != 1) ||
+ (m_secondLsbChannel != QLCChannel::invalid() && m_secondLsbChannel - m_secondMsbChannel != 1))
+ {
+ fader->setHandleSecondary(false);
+ }
+ }
+ break;
+
+ case RGB:
+ break;
+
+ case Dimmer:
+ {
+ m_firstMsbChannel = fxi->channelNumber(QLCChannel::Intensity, QLCChannel::MSB, head().head);
+ if (m_firstMsbChannel != QLCChannel::invalid())
+ {
+ m_firstLsbChannel = fxi->channelNumber(QLCChannel::Intensity, QLCChannel::LSB, head().head);
+
+ /* Check for non-contiguous channels */
+ if (m_firstLsbChannel != QLCChannel::invalid() && m_firstLsbChannel - m_firstMsbChannel != 1)
+ {
+ fader->setHandleSecondary(false);
+ }
+ }
+ else
+ {
+ m_firstMsbChannel = fxi->masterIntensityChannel();
+ }
+ }
+ break;
+ }
m_started = true;
}
@@ -432,7 +481,7 @@ void EFXFixture::nextStep(QList universes, QSharedPointerloopDuration();
@@ -463,7 +512,7 @@ void EFXFixture::nextStep(QList universes, QSharedPointersetStart(fc->current());
fc->setTarget(value);
@@ -475,80 +524,97 @@ void EFXFixture::updateFaderValues(FadeChannel *fc, uchar value)
void EFXFixture::setPointPanTilt(QList universes, QSharedPointer fader,
float pan, float tilt)
{
- Fixture* fxi = doc()->fixture(head().fxi);
- Q_ASSERT(fxi != NULL);
+ if (fader.isNull())
+ return;
+
Universe *uni = universes[universe()];
//qDebug() << "Pan value: " << pan << ", tilt value:" << tilt;
- quint32 panMsbChannel = fxi->channelNumber(QLCChannel::Pan, QLCChannel::MSB, head().head);
- quint32 panLsbChannel = fxi->channelNumber(QLCChannel::Pan, QLCChannel::LSB, head().head);
- quint32 tiltMsbChannel = fxi->channelNumber(QLCChannel::Tilt, QLCChannel::MSB, head().head);
- quint32 tiltLsbChannel = fxi->channelNumber(QLCChannel::Tilt, QLCChannel::LSB, head().head);
+ /* Check for outbound values */
+ if (pan < 0)
+ pan = 0;
+
+ if (tilt < 0)
+ tilt = 0;
- /* Write coarse point data to universes */
- if (panMsbChannel != QLCChannel::invalid() && !fader.isNull())
+ /* Write full 16bit point data to universes */
+ if (m_firstMsbChannel != QLCChannel::invalid())
{
- FadeChannel *fc = fader->getChannelFader(doc(), uni, fxi->id(), panMsbChannel);
+ quint32 panValue = quint32(pan);
+ FadeChannel *fc = fader->getChannelFader(doc(), uni, head().fxi, m_firstMsbChannel);
+ if (m_firstLsbChannel != QLCChannel::invalid())
+ {
+ if (fader->handleSecondary())
+ {
+ fc = fader->getChannelFader(doc(), uni, head().fxi, m_firstLsbChannel);
+ panValue = (panValue << 8) + quint32((pan - floor(pan)) * float(UCHAR_MAX));
+ }
+ else
+ {
+ FadeChannel *lsbFc = fader->getChannelFader(doc(), uni, head().fxi, m_firstLsbChannel);
+ updateFaderValues(lsbFc, quint32((pan - floor(pan)) * float(UCHAR_MAX)));
+ }
+ }
if (m_parent->isRelative())
fc->addFlag(FadeChannel::Relative);
- updateFaderValues(fc, static_cast(pan));
+
+ updateFaderValues(fc, panValue);
}
- if (tiltMsbChannel != QLCChannel::invalid() && !fader.isNull())
+ if (m_secondMsbChannel != QLCChannel::invalid())
{
- FadeChannel *fc = fader->getChannelFader(doc(), uni, fxi->id(), tiltMsbChannel);
+ quint32 tiltValue = quint32(tilt);
+ FadeChannel *fc = fader->getChannelFader(doc(), uni, head().fxi, m_secondMsbChannel);
+ if (m_secondLsbChannel != QLCChannel::invalid())
+ {
+ if (fader->handleSecondary())
+ {
+ fc = fader->getChannelFader(doc(), uni, head().fxi, m_secondLsbChannel);
+ tiltValue = (tiltValue << 8) + quint32((tilt - floor(tilt)) * float(UCHAR_MAX));
+ }
+ else
+ {
+ FadeChannel *lsbFc = fader->getChannelFader(doc(), uni, head().fxi, m_secondLsbChannel);
+ updateFaderValues(lsbFc, quint32((tilt - floor(tilt)) * float(UCHAR_MAX)));
+ }
+ }
if (m_parent->isRelative())
fc->addFlag(FadeChannel::Relative);
- updateFaderValues(fc, static_cast(tilt));
- }
-
- /* Write fine point data to universes if applicable */
- if (panLsbChannel != QLCChannel::invalid() && !fader.isNull())
- {
- /* Leave only the fraction */
- float value = ((pan - floor(pan)) * float(UCHAR_MAX));
- FadeChannel *fc = fader->getChannelFader(doc(), uni, fxi->id(), panLsbChannel);
- updateFaderValues(fc, static_cast(value));
- }
- if (tiltLsbChannel != QLCChannel::invalid() && !fader.isNull())
- {
- /* Leave only the fraction */
- float value = ((tilt - floor(tilt)) * float(UCHAR_MAX));
- FadeChannel *fc = fader->getChannelFader(doc(), uni, fxi->id(), tiltLsbChannel);
- updateFaderValues(fc, static_cast(value));
+ updateFaderValues(fc, tiltValue);
}
}
void EFXFixture::setPointDimmer(QList universes, QSharedPointer fader, float dimmer)
{
- Fixture *fxi = doc()->fixture(head().fxi);
- Q_ASSERT(fxi != NULL);
- Universe *uni = universes[universe()];
+ if (fader.isNull())
+ return;
- quint32 intChannel = fxi->channelNumber(QLCChannel::Intensity, QLCChannel::MSB, head().head);
+ Universe *uni = universes[universe()];
/* Don't write dimmer data directly to universes but use FadeChannel to avoid steps at EFX loop restart */
- if (intChannel != QLCChannel::invalid())
- {
- if (!fader.isNull())
- {
- FadeChannel *fc = fader->getChannelFader(doc(), uni, fxi->id(), intChannel);
- updateFaderValues(fc, dimmer);
- }
- }
- else if (fxi->masterIntensityChannel() != QLCChannel::invalid())
+ if (m_firstMsbChannel != QLCChannel::invalid())
{
- if (!fader.isNull())
+ quint32 dimmerValue = quint32(dimmer);
+ FadeChannel *fc = fader->getChannelFader(doc(), uni, head().fxi, m_firstMsbChannel);
+
+ if (m_firstLsbChannel != QLCChannel::invalid())
{
- FadeChannel *fc = fader->getChannelFader(doc(), uni, fxi->id(), fxi->masterIntensityChannel());
- updateFaderValues(fc, dimmer);
+ if (fader->handleSecondary())
+ {
+ fc = fader->getChannelFader(doc(), uni, head().fxi, m_firstLsbChannel);
+ dimmerValue = (dimmerValue << 8) + quint32((dimmer - floor(dimmer)) * float(UCHAR_MAX));
+ }
}
+ updateFaderValues(fc, dimmerValue);
}
}
void EFXFixture::setPointRGB(QList universes, QSharedPointer fader, float x, float y)
{
+ if (fader.isNull())
+ return;
+
Fixture* fxi = doc()->fixture(head().fxi);
Q_ASSERT(fxi != NULL);
Universe *uni = universes[universe()];
diff --git a/engine/src/efxfixture.h b/engine/src/efxfixture.h
index a2b52a2984..d98c8fa36a 100644
--- a/engine/src/efxfixture.h
+++ b/engine/src/efxfixture.h
@@ -188,19 +188,26 @@ class EFXFixture
* Running
*************************************************************************/
private:
- void start();
+ void start(QSharedPointer fader);
void stop();
/** Calculate the next step data for this fixture */
void nextStep(QList universes, QSharedPointer fader);
- void updateFaderValues(FadeChannel *fc, uchar value);
+ /** Set a 16bit value on a fader gotten from the engine */
+ void updateFaderValues(FadeChannel *fc, quint32 value);
/** Write this EFXFixture's channel data to universe faders */
void setPointPanTilt(QList universes, QSharedPointer fader, float pan, float tilt);
void setPointDimmer(QList universes, QSharedPointer fader, float dimmer);
void setPointRGB (QList universes, QSharedPointer fader, float x, float y);
+private:
+ quint32 m_firstMsbChannel;
+ quint32 m_firstLsbChannel;
+ quint32 m_secondMsbChannel;
+ quint32 m_secondLsbChannel;
+
private:
static QImage m_rgbGradient;
};
diff --git a/engine/src/fadechannel.cpp b/engine/src/fadechannel.cpp
index d1e3fd13dc..2e5a8755ee 100644
--- a/engine/src/fadechannel.cpp
+++ b/engine/src/fadechannel.cpp
@@ -20,6 +20,7 @@
#include
#include
+#include "qlcfixturemode.h"
#include "fadechannel.h"
#include "qlcchannel.h"
#include "universe.h"
@@ -29,8 +30,9 @@ FadeChannel::FadeChannel()
: m_flags(0)
, m_fixture(Fixture::invalidId())
, m_universe(Universe::invalid())
- , m_channel(QLCChannel::invalid())
+ , m_primaryChannel(QLCChannel::invalid())
, m_address(QLCChannel::invalid())
+ , m_channelRef(NULL)
, m_start(0)
, m_target(0)
, m_current(0)
@@ -44,8 +46,10 @@ FadeChannel::FadeChannel(const FadeChannel& ch)
: m_flags(ch.m_flags)
, m_fixture(ch.m_fixture)
, m_universe(ch.m_universe)
- , m_channel(ch.m_channel)
+ , m_primaryChannel(ch.m_primaryChannel)
+ , m_channels(ch.m_channels)
, m_address(ch.m_address)
+ , m_channelRef(ch.m_channelRef)
, m_start(ch.m_start)
, m_target(ch.m_target)
, m_current(ch.m_current)
@@ -59,7 +63,7 @@ FadeChannel::FadeChannel(const FadeChannel& ch)
FadeChannel::FadeChannel(const Doc *doc, quint32 fxi, quint32 channel)
: m_flags(0)
, m_fixture(fxi)
- , m_channel(channel)
+ , m_channelRef(NULL)
, m_start(0)
, m_target(0)
, m_current(0)
@@ -67,6 +71,7 @@ FadeChannel::FadeChannel(const Doc *doc, quint32 fxi, quint32 channel)
, m_fadeTime(0)
, m_elapsed(0)
{
+ m_channels.append(channel);
autoDetect(doc);
}
@@ -81,7 +86,9 @@ FadeChannel &FadeChannel::operator=(const FadeChannel &fc)
m_flags = fc.m_flags;
m_fixture = fc.m_fixture;
m_universe = fc.m_universe;
- m_channel = fc.m_channel;
+ m_primaryChannel = fc.m_primaryChannel;
+ m_channels = fc.m_channels;
+ m_channelRef = fc.m_channelRef;
m_address = fc.m_address;
m_start = fc.m_start;
m_target = fc.m_target;
@@ -96,7 +103,7 @@ FadeChannel &FadeChannel::operator=(const FadeChannel &fc)
bool FadeChannel::operator==(const FadeChannel& ch) const
{
- return (m_fixture == ch.m_fixture && m_channel == ch.m_channel);
+ return (m_fixture == ch.m_fixture && channel() == ch.channel());
}
int FadeChannel::flags() const
@@ -143,54 +150,48 @@ void FadeChannel::autoDetect(const Doc *doc)
}
else
{
+ QLCFixtureMode *mode = fixture->fixtureMode();
m_universe = fixture->universe();
m_address = fixture->address();
// if the fixture was invalid at the beginning of this method
// it means channel was an absolute address, so, fix it
if (fixtureWasInvalid)
- m_channel -= fixture->address();
+ m_channels[0] -= fixture->address();
- const QLCChannel *channel = fixture->channel(m_channel);
+ quint32 chIndex = channel();
+ m_primaryChannel = mode ? mode->primaryChannel(chIndex) : QLCChannel::invalid();
+ m_channelRef = fixture->channel(chIndex);
// non existing channel within fixture
- if (channel == NULL)
+ if (m_channelRef == NULL)
{
addFlag(FadeChannel::HTP | FadeChannel::Intensity | FadeChannel::CanFade);
return;
}
// autodetect the channel type
- if (fixture->channelCanFade(m_channel))
+ if (fixture->channelCanFade(chIndex))
addFlag(FadeChannel::CanFade);
- if (channel != NULL && channel->group() == QLCChannel::Intensity)
+ if (m_channelRef != NULL && m_channelRef->group() == QLCChannel::Intensity)
addFlag(FadeChannel::HTP | FadeChannel::Intensity);
else
addFlag(FadeChannel::LTP);
- if (fixture->forcedHTPChannels().contains(int(m_channel)))
+ if (fixture->forcedHTPChannels().contains(int(chIndex)))
{
removeFlag(FadeChannel::LTP);
addFlag(FadeChannel::HTP);
}
- else if (fixture->forcedLTPChannels().contains(int(m_channel)))
+ else if (fixture->forcedLTPChannels().contains(int(chIndex)))
{
removeFlag(FadeChannel::HTP);
addFlag(FadeChannel::LTP);
}
-
- if (channel != NULL && channel->controlByte() == QLCChannel::LSB)
- addFlag(FadeChannel::Fine);
}
}
-void FadeChannel::setFixture(const Doc *doc, quint32 id)
-{
- m_fixture = id;
- autoDetect(doc);
-}
-
quint32 FadeChannel::fixture() const
{
return m_fixture;
@@ -203,15 +204,42 @@ quint32 FadeChannel::universe() const
return m_universe;
}
-void FadeChannel::setChannel(const Doc *doc, quint32 num)
+void FadeChannel::addChannel(quint32 num)
{
- m_channel = num;
- autoDetect(doc);
+ m_channels.append(num);
+ qDebug() << "[FadeChannel] ADD channel" << num << "count:" << m_channels.count();
+
+ // on secondary channel, shift values 8bits up
+ if (m_channels.count() > 1)
+ {
+ m_start = m_start << 8;
+ m_target = m_target << 8;
+ m_current = m_current << 8;
+ }
+}
+
+int FadeChannel::channelCount() const
+{
+ if (m_channels.isEmpty())
+ return 1;
+
+ return m_channels.count();
}
quint32 FadeChannel::channel() const
{
- return m_channel;
+ return m_channels.isEmpty() ? QLCChannel::invalid() : m_channels.first();
+}
+
+int FadeChannel::channelIndex(quint32 channel)
+{
+ int idx = m_channels.indexOf(channel);
+ return idx < 0 ? 0 : idx;
+}
+
+quint32 FadeChannel::primaryChannel() const
+{
+ return m_primaryChannel;
}
quint32 FadeChannel::address() const
@@ -224,42 +252,85 @@ quint32 FadeChannel::address() const
quint32 FadeChannel::addressInUniverse() const
{
- return address() % UNIVERSE_SIZE;
+ quint32 addr = address();
+ if (addr == QLCChannel::invalid())
+ return QLCChannel::invalid();
+
+ return addr % UNIVERSE_SIZE;
}
-void FadeChannel::setStart(uchar value)
+/************************************************************************
+ * Values
+ ************************************************************************/
+
+void FadeChannel::setStart(uchar value, int index)
+{
+ ((uchar *)&m_start)[channelCount() - 1 - index] = value;
+}
+
+void FadeChannel::setStart(quint32 value)
{
m_start = value;
}
-uchar FadeChannel::start() const
+uchar FadeChannel::start(int index) const
+{
+ return ((uchar *)&m_start)[channelCount() - 1 - index];
+}
+
+quint32 FadeChannel::start() const
{
- return uchar(m_start);
+ return m_start;
}
-void FadeChannel::setTarget(uchar value)
+void FadeChannel::setTarget(uchar value, int index)
+{
+ ((uchar *)&m_target)[channelCount() - 1 - index] = value;
+}
+
+void FadeChannel::setTarget(quint32 value)
{
m_target = value;
}
-uchar FadeChannel::target() const
+uchar FadeChannel::target(int index) const
+{
+ return ((uchar *)&m_target)[channelCount() - 1 - index];
+}
+
+quint32 FadeChannel::target() const
+{
+ return m_target;
+}
+
+void FadeChannel::setCurrent(uchar value, int index)
{
- return uchar(m_target);
+ ((uchar *)&m_current)[channelCount() - 1 - index] = value;
}
-void FadeChannel::setCurrent(uchar value)
+void FadeChannel::setCurrent(quint32 value)
{
m_current = value;
}
-uchar FadeChannel::current() const
+uchar FadeChannel::current(int index) const
{
- return uchar(m_current);
+ return ((uchar *)&m_current)[channelCount() - 1 - index];
+}
+
+quint32 FadeChannel::current() const
+{
+ return m_current;
}
-uchar FadeChannel::current(qreal intensity) const
+uchar FadeChannel::current(qreal intensity, int index) const
{
- return uchar(floor((qreal(m_current) * intensity) + 0.5));
+ return uchar(floor((qreal(current(index)) * intensity) + 0.5));
+}
+
+quint32 FadeChannel::current(qreal intensity) const
+{
+ return quint32(floor((qreal(m_current) * intensity) + 0.5));
}
void FadeChannel::setReady(bool rdy)
@@ -301,6 +372,7 @@ uchar FadeChannel::nextStep(uint ms)
{
if (elapsed() < UINT_MAX)
setElapsed(elapsed() + ms);
+
return calculateCurrent(fadeTime(), elapsed());
}
@@ -319,14 +391,11 @@ uchar FadeChannel::calculateCurrent(uint fadeTime, uint elapsedTime)
}
else
{
- // 16 bit fading works as long as MSB and LSB channels
- // are targeting the same value. E.g. Red and Red Fine both at 158
- float val = (float(m_target - m_start) * (float(elapsedTime) / float(fadeTime))) + float(m_start);
- long rval = lrintf(val * 256);
- if (m_flags & Fine)
- m_current = rval & 0xff;
- else
- m_current = rval / 256;
+ bool rampUp = m_target > m_start ? true : false;
+ m_current = rampUp ? m_target - m_start : m_start - m_target;
+ m_current = m_current * (qreal(elapsedTime) / qreal(fadeTime));
+ m_current = rampUp ? m_start + m_current : m_start - m_current;
+ //qDebug() << "channel" << channel() << "start" << m_start << "target" << m_target << "current" << m_current << "fade" << fadeTime << "elapsed" << elapsedTime ;
}
return uchar(m_current);
diff --git a/engine/src/fadechannel.h b/engine/src/fadechannel.h
index 9f09fe05bb..01d129077f 100644
--- a/engine/src/fadechannel.h
+++ b/engine/src/fadechannel.h
@@ -81,59 +81,84 @@ class FadeChannel
void addFlag(int flag);
void removeFlag(int flag);
-protected:
- void autoDetect(const Doc *doc);
-
-private:
- /** Bitmask including the channel type
- * and, if needed, more flags */
- int m_flags;
-
- /************************************************************************
- * Values
- ************************************************************************/
-public:
- /** Set the Fixture that is being controlled. */
- void setFixture(const Doc *doc, quint32 id);
-
/** Get the Fixture that is being controlled. */
quint32 fixture() const;
/** Get the universe of the Fixture that is being controlled. */
quint32 universe() const;
- /** Set channel within the Fixture. */
- void setChannel(const Doc* doc, quint32 num);
+ /** Add another channel to be handled by this fader */
+ void addChannel(quint32 num);
- /** Get channel within the Fixture. */
+ /** Get the number of channels handled by this fader */
+ int channelCount() const;
+
+ /** Get the first (or master) channel handled by this fader */
quint32 channel() const;
+ /** Get the index of the provided $channel. This is useful only
+ * when multiple channels are handled and caller doesn't know
+ * if it is targeting primary or secondary */
+ int channelIndex(quint32 channel);
+
+ /** Get (if present) the index of the primary channel this fader relate to */
+ quint32 primaryChannel() const;
+
/** Get the absolute address for this channel. */
quint32 address() const;
/** Get the absolute address in its universe for this channel. */
quint32 addressInUniverse() const;
+protected:
+ void autoDetect(const Doc *doc);
+
+private:
+ /** Bitmask representing all the channel specificities
+ * such as fading, overriding, flashing, etc. */
+ int m_flags;
+
+ quint32 m_fixture;
+ quint32 m_universe;
+ quint32 m_primaryChannel;
+ QVector m_channels;
+ quint32 m_address;
+
+ /** Cache channel reference for faster lookup */
+ const QLCChannel *m_channelRef;
+
+ /************************************************************************
+ * Values
+ ************************************************************************/
+public:
+
/** Set starting value. */
- void setStart(uchar value);
+ void setStart(uchar value, int index);
+ void setStart(quint32 value);
/** Get starting value. */
- uchar start() const;
+ uchar start(int index) const;
+ quint32 start() const;
/** Set target value. */
- void setTarget(uchar value);
+ void setTarget(uchar value, int index);
+ void setTarget(quint32 value);
/** Get target value. */
- uchar target() const;
+ uchar target(int index) const;
+ quint32 target() const;
/** Set the current value. */
- void setCurrent(uchar value);
+ void setCurrent(uchar value, int index);
+ void setCurrent(quint32 value);
/** Get the current value. */
- uchar current() const;
+ uchar current(int index) const;
+ quint32 current() const;
/** Get the current value, modified by $intensity. */
- uchar current(qreal intensity) const;
+ uchar current(qreal intensity, int index) const;
+ quint32 current(qreal intensity) const;
/** Mark this channel as ready (useful for writing LTP values only once). */
void setReady(bool rdy);
@@ -177,14 +202,9 @@ class FadeChannel
uchar calculateCurrent(uint fadeTime, uint elapsedTime);
private:
- quint32 m_fixture;
- quint32 m_universe;
- quint32 m_channel;
- quint32 m_address;
-
- int m_start;
- int m_target;
- int m_current;
+ quint32 m_start;
+ quint32 m_target;
+ quint32 m_current;
bool m_ready;
uint m_fadeTime;
diff --git a/engine/src/genericfader.cpp b/engine/src/genericfader.cpp
index 0802b2073e..44f6461004 100644
--- a/engine/src/genericfader.cpp
+++ b/engine/src/genericfader.cpp
@@ -17,7 +17,6 @@
limitations under the License.
*/
-#include
#include
#include "genericfader.h"
@@ -28,6 +27,7 @@ GenericFader::GenericFader(QObject *parent)
: QObject(parent)
, m_fid(Function::invalidId())
, m_priority(Universe::Auto)
+ , m_handleSecondary(false)
, m_intensity(1.0)
, m_parentIntensity(1.0)
, m_paused(false)
@@ -73,6 +73,16 @@ void GenericFader::setPriority(int priority)
m_priority = priority;
}
+bool GenericFader::handleSecondary()
+{
+ return m_handleSecondary;
+}
+
+void GenericFader::setHandleSecondary(bool enable)
+{
+ m_handleSecondary = enable;
+}
+
quint32 GenericFader::channelHash(quint32 fixtureID, quint32 channel)
{
return ((fixtureID & 0x0000FFFF) << 16) | (channel & 0x0000FFFF);
@@ -130,15 +140,41 @@ void GenericFader::requestDelete()
FadeChannel *GenericFader::getChannelFader(const Doc *doc, Universe *universe, quint32 fixtureID, quint32 channel)
{
FadeChannel fc(doc, fixtureID, channel);
- quint32 hash = channelHash(fc.fixture(), fc.channel());
+ quint32 primary = fc.primaryChannel();
+ quint32 hash;
+
+ // calculate hash depending on primary channel presence
+ if (handleSecondary() && primary != QLCChannel::invalid())
+ hash = channelHash(fc.fixture(), primary);
+ else
+ hash = channelHash(fc.fixture(), fc.channel());
+
+ // search for existing FadeChannel
QHash::iterator channelIterator = m_channels.find(hash);
if (channelIterator != m_channels.end())
- return &channelIterator.value();
+ {
+ FadeChannel *fcFound = &channelIterator.value();
+
+ if (handleSecondary() &&
+ fcFound->channelCount() == 1 &&
+ primary != QLCChannel::invalid())
+ {
+ qDebug() << "Adding channel to primary" << channel;
+ fcFound->addChannel(channel);
+ if (universe)
+ fcFound->setCurrent(universe->preGMValue(fcFound->address() + 1), 1);
+ }
+ return fcFound;
+ }
- fc.setCurrent(universe->preGMValue(fc.address()));
+ // set current universe value
+ if (universe)
+ fc.setCurrent(universe->preGMValue(fc.address()));
+ // new channel. Add to GenericFader
m_channels[hash] = fc;
//qDebug() << "Added new fader with hash" << hash;
+
return &m_channels[hash];
}
@@ -159,26 +195,31 @@ void GenericFader::write(Universe *universe)
qreal compIntensity = intensity() * parentIntensity();
+ //qDebug() << "[GenericFader] writing channels: " << this << m_channels.count();
+
QMutableHashIterator it(m_channels);
while (it.hasNext() == true)
{
FadeChannel& fc(it.next().value());
int flags = fc.flags();
int address = int(fc.addressInUniverse());
- uchar value;
+ int channelCount = fc.channelCount();
+
+ // iterate through all the channels handled by this fader
if (flags & FadeChannel::SetTarget)
{
fc.removeFlag(FadeChannel::SetTarget);
fc.addFlag(FadeChannel::AutoRemove);
- fc.setTarget(universe->preGMValue(address));
+ for (int i = 0; i < channelCount; i++)
+ fc.setTarget(universe->preGMValue(address + i), i);
}
// Calculate the next step
- if (m_paused)
- value = fc.current();
- else
- value = fc.nextStep(MasterTimer::tick());
+ if (m_paused == false)
+ fc.nextStep(MasterTimer::tick());
+
+ quint32 value = fc.current();
// Apply intensity to channels that can fade
if (fc.canFade())
@@ -186,7 +227,7 @@ void GenericFader::write(Universe *universe)
if ((flags & FadeChannel::CrossFade) && fc.fadeTime() == 0)
{
// morph start <-> target depending on intensities
- value = uchar(((qreal(fc.target() - fc.start()) * intensity()) + fc.start()) * parentIntensity());
+ value = quint32(((qreal(fc.target() - fc.start()) * intensity()) + fc.start()) * parentIntensity());
}
else if (flags & FadeChannel::Intensity)
{
@@ -194,7 +235,7 @@ void GenericFader::write(Universe *universe)
}
}
- //qDebug() << "[GenericFader] >>> uni:" << universe->id() << ", address:" << address << ", value:" << value << "int:" << compIntensity;
+ //qDebug() << "[GenericFader] >>> uni:" << universe->id() << ", address:" << (address + i) << ", value:" << value << "int:" << compIntensity;
if (flags & FadeChannel::Override)
{
universe->write(address, value, true);
@@ -202,16 +243,17 @@ void GenericFader::write(Universe *universe)
}
else if (flags & FadeChannel::Relative)
{
- universe->writeRelative(address, value);
+ universe->writeRelative(address, value, channelCount);
}
else if (flags & FadeChannel::Flashing)
{
- universe->write(address, value, flags & FadeChannel::ForceLTP);
+ universe->writeMultiple(address, value, channelCount);
continue;
}
else
{
- universe->writeBlended(address, value, m_blendMode);
+ // treat value as a whole, so do this just once per FadeChannel
+ universe->writeBlended(address, value, channelCount, m_blendMode);
}
if (((flags & FadeChannel::Intensity) &&
@@ -287,24 +329,24 @@ void GenericFader::setFadeOut(bool enable, uint fadeTime)
{
m_fadeOut = enable;
- if (fadeTime)
+ if (fadeTime == 0)
+ return;
+
+ QMutableHashIterator it(m_channels);
+ while (it.hasNext() == true)
{
- QMutableHashIterator it(m_channels);
- while (it.hasNext() == true)
- {
- FadeChannel& fc(it.next().value());
-
- // non-intensity channels (eg LTP) should fade
- // to the current universe value
- if ((fc.flags() & FadeChannel::Intensity) == 0)
- fc.addFlag(FadeChannel::SetTarget);
-
- fc.setStart(fc.current());
- fc.setTarget(0);
- fc.setElapsed(0);
- fc.setReady(false);
- fc.setFadeTime(fc.canFade() ? fadeTime : 0);
- }
+ FadeChannel& fc(it.next().value());
+
+ // non-intensity channels (eg LTP) should fade
+ // to the current universe value
+ if ((fc.flags() & FadeChannel::Intensity) == 0)
+ fc.addFlag(FadeChannel::SetTarget);
+
+ fc.setStart(fc.current());
+ fc.setTarget(0);
+ fc.setElapsed(0);
+ fc.setReady(false);
+ fc.setFadeTime(fc.canFade() ? fadeTime : 0);
}
}
diff --git a/engine/src/genericfader.h b/engine/src/genericfader.h
index 7eba714329..4177c8d8fa 100644
--- a/engine/src/genericfader.h
+++ b/engine/src/genericfader.h
@@ -25,6 +25,7 @@
#include
#include "universe.h"
+#include "scenevalue.h"
class FadeChannel;
@@ -32,6 +33,14 @@ class FadeChannel;
* @{
*/
+/**
+ * GenericFader represents all the fading channels for one Function (or feature)
+ * and one Universe. For example a Scene will request one GenericFader for all the
+ * channels of all the fixtures on a specific Universe.
+ * In this way, Universes will handle a list of dedicated faders, without
+ * any lookup
+ */
+
class GenericFader : public QObject
{
Q_OBJECT
@@ -54,6 +63,11 @@ class GenericFader : public QObject
int priority() const;
void setPriority(int priority);
+ /** Get/Set if this fader should handle primary/secondary channels
+ * when a caller requests a FadeChannel */
+ bool handleSecondary();
+ void setHandleSecondary(bool enable);
+
/** Build a hash for a fader channel which is unique in a Universe.
* This is used to map channels and access them quickly */
static quint32 channelHash(quint32 fixtureID, quint32 channel);
@@ -137,6 +151,7 @@ class GenericFader : public QObject
/** Enable/disable universe monitoring before writing new data */
void setMonitoring(bool enable);
+ /** Remove the Crossfade flag from every fader handled by this class */
void resetCrossfade();
signals:
@@ -148,6 +163,7 @@ class GenericFader : public QObject
QString m_name;
quint32 m_fid;
int m_priority;
+ bool m_handleSecondary;
QHash m_channels;
qreal m_intensity;
qreal m_parentIntensity;
diff --git a/engine/src/inputoutputmap.cpp b/engine/src/inputoutputmap.cpp
index f4c6cae17f..1cf53b67f5 100644
--- a/engine/src/inputoutputmap.cpp
+++ b/engine/src/inputoutputmap.cpp
@@ -41,12 +41,11 @@
#include "qlcfile.h"
#include "doc.h"
-#include "../../plugins/midi/src/common/midiprotocol.h"
-
InputOutputMap::InputOutputMap(Doc *doc, quint32 universes)
: QObject(doc)
, m_blackout(false)
, m_universeChanged(false)
+ , m_currentBPM(0)
, m_beatTime(new QElapsedTimer())
{
m_grandMaster = new GrandMaster(this);
@@ -102,6 +101,9 @@ bool InputOutputMap::setBlackout(bool blackout)
if (op != NULL)
op->setBlackout(blackout);
}
+
+ const QByteArray postGM = universe->postGMValues()->mid(0, universe->usedChannels());
+ universe->dumpOutput(postGM, true);
}
emit blackoutChanged(m_blackout);
@@ -408,10 +410,10 @@ bool InputOutputMap::setInputPatch(quint32 universe, const QString &pluginName,
currProfile = currInPatch->profile();
disconnect(currInPatch, SIGNAL(inputValueChanged(quint32,quint32,uchar,const QString&)),
this, SIGNAL(inputValueChanged(quint32,quint32,uchar,const QString&)));
- if (currInPatch->pluginName() == "MIDI")
+ if (currInPatch->plugin()->capabilities() & QLCIOPlugin::Beats)
{
disconnect(currInPatch, SIGNAL(inputValueChanged(quint32,quint32,uchar,const QString&)),
- this, SLOT(slotMIDIBeat(quint32,quint32,uchar)));
+ this, SLOT(slotPluginBeat(quint32,quint32,uchar,const QString&)));
}
}
InputPatch *ip = NULL;
@@ -441,10 +443,10 @@ bool InputOutputMap::setInputPatch(quint32 universe, const QString &pluginName,
{
connect(ip, SIGNAL(inputValueChanged(quint32,quint32,uchar,const QString&)),
this, SIGNAL(inputValueChanged(quint32,quint32,uchar,const QString&)));
- if (ip->pluginName() == "MIDI")
+ if (ip->plugin()->capabilities() & QLCIOPlugin::Beats)
{
connect(ip, SIGNAL(inputValueChanged(quint32,quint32,uchar,const QString&)),
- this, SLOT(slotMIDIBeat(quint32,quint32,uchar)));
+ this, SLOT(slotPluginBeat(quint32,quint32,uchar,const QString&)));
}
}
}
@@ -749,7 +751,7 @@ QString InputOutputMap::outputPluginStatus(const QString& pluginName, quint32 ou
}
}
-bool InputOutputMap::sendFeedBack(quint32 universe, quint32 channel, uchar value, const QString& key)
+bool InputOutputMap::sendFeedBack(quint32 universe, quint32 channel, uchar value, const QVariant ¶ms)
{
if (universe >= universesCount())
return false;
@@ -758,7 +760,7 @@ bool InputOutputMap::sendFeedBack(quint32 universe, quint32 channel, uchar value
if (patch != NULL && patch->isPatched())
{
- patch->plugin()->sendFeedBack(universe, patch->output(), channel, value, key);
+ patch->plugin()->sendFeedBack(universe, patch->output(), channel, value, params);
return true;
}
else
@@ -997,7 +999,7 @@ void InputOutputMap::setBeatGeneratorType(InputOutputMap::BeatGeneratorType type
doc()->masterTimer()->setBeatSourceType(MasterTimer::Internal);
setBpmNumber(doc()->masterTimer()->bpmNumber());
break;
- case MIDI:
+ case Plugin:
doc()->masterTimer()->setBeatSourceType(MasterTimer::External);
// reset the current BPM number and detect it from the MIDI beats
setBpmNumber(0);
@@ -1024,6 +1026,29 @@ InputOutputMap::BeatGeneratorType InputOutputMap::beatGeneratorType() const
return m_beatGeneratorType;
}
+QString InputOutputMap::beatTypeToString(BeatGeneratorType type) const
+{
+ switch (type)
+ {
+ case Internal: return "Internal";
+ case Plugin: return "Plugin";
+ case Audio: return "Audio";
+ default: return "Disabled";
+ }
+}
+
+InputOutputMap::BeatGeneratorType InputOutputMap::stringToBeatType(QString str)
+{
+ if (str == "Internal")
+ return Internal;
+ else if (str == "Plugin")
+ return Plugin;
+ else if (str == "Audio")
+ return Audio;
+
+ return Disabled;
+}
+
void InputOutputMap::setBpmNumber(int bpm)
{
if (m_beatGeneratorType == Disabled || bpm == m_currentBPM)
@@ -1054,35 +1079,32 @@ void InputOutputMap::slotMasterTimerBeat()
emit beat();
}
-void InputOutputMap::slotMIDIBeat(quint32 universe, quint32 channel, uchar value)
+void InputOutputMap::slotPluginBeat(quint32 universe, quint32 channel, uchar value, const QString &key)
{
Q_UNUSED(universe)
- // not interested in synthetic release event or non-MBC ones
- if (m_beatGeneratorType != MIDI || value == 0 || channel < CHANNEL_OFFSET_MBC_PLAYBACK)
+ // not interested in synthetic release or non-beat event
+ if (m_beatGeneratorType != Plugin || value == 0 || key != "beat")
return;
- qDebug() << "MIDI MBC:" << channel << m_beatTime->elapsed();
+ qDebug() << "Plugin beat:" << channel << m_beatTime->elapsed();
// process the timer as first thing, to avoid wasting time
// with the operations below
int elapsed = m_beatTime->elapsed();
m_beatTime->restart();
- if (channel == CHANNEL_OFFSET_MBC_BEAT)
- {
- int bpm = qRound(60000.0 / (float)elapsed);
- float currBpmTime = 60000.0 / (float)m_currentBPM;
- // here we check if the difference between the current BPM duration
- // and the current time elapsed is within a range of +/-1ms.
- // If it isn't, then the BPM number has really changed, otherwise
- // it's just a tiny time drift
- if (qAbs((float)elapsed - currBpmTime) > 1)
- setBpmNumber(bpm);
-
- doc()->masterTimer()->requestBeat();
- emit beat();
- }
+ int bpm = qRound(60000.0 / (float)elapsed);
+ float currBpmTime = 60000.0 / (float)m_currentBPM;
+ // here we check if the difference between the current BPM duration
+ // and the current time elapsed is within a range of +/-1ms.
+ // If it isn't, then the BPM number has really changed, otherwise
+ // it's just a tiny time drift
+ if (qAbs((float)elapsed - currBpmTime) > 1)
+ setBpmNumber(bpm);
+
+ doc()->masterTimer()->requestBeat();
+ emit beat();
}
void InputOutputMap::slotAudioSpectrum(double *spectrumBands, int size, double maxMagnitude, quint32 power)
@@ -1270,6 +1292,18 @@ bool InputOutputMap::loadXML(QXmlStreamReader &root)
uni->loadXML(root, m_universeArray.count() - 1, this);
}
}
+ else if (root.name() == KXMLIOBeatGenerator)
+ {
+ QXmlStreamAttributes attrs = root.attributes();
+
+ if (attrs.hasAttribute(KXMLIOBeatType))
+ setBeatGeneratorType(stringToBeatType(attrs.value(KXMLIOBeatType).toString()));
+
+ if (attrs.hasAttribute(KXMLIOBeatsPerMinute))
+ setBpmNumber(attrs.value(KXMLIOBeatsPerMinute).toInt());
+
+ root.skipCurrentElement();
+ }
else
{
qWarning() << Q_FUNC_INFO << "Unknown IO Map tag:" << root.name();
@@ -1287,6 +1321,11 @@ bool InputOutputMap::saveXML(QXmlStreamWriter *doc) const
/* IO Map Instance entry */
doc->writeStartElement(KXMLIOMap);
+ doc->writeStartElement(KXMLIOBeatGenerator);
+ doc->writeAttribute(KXMLIOBeatType, beatTypeToString(m_beatGeneratorType));
+ doc->writeAttribute(KXMLIOBeatsPerMinute, QString::number(m_currentBPM));
+ doc->writeEndElement();
+
foreach (Universe *uni, m_universeArray)
uni->saveXML(doc);
@@ -1294,5 +1333,3 @@ bool InputOutputMap::saveXML(QXmlStreamWriter *doc) const
return true;
}
-
-
diff --git a/engine/src/inputoutputmap.h b/engine/src/inputoutputmap.h
index 82ea794025..2fa0b4c153 100644
--- a/engine/src/inputoutputmap.h
+++ b/engine/src/inputoutputmap.h
@@ -42,7 +42,10 @@ class Doc;
* @{
*/
-#define KXMLIOMap QString("InputOutputMap")
+#define KXMLIOMap QString("InputOutputMap")
+#define KXMLIOBeatGenerator QString("BeatGenerator")
+#define KXMLIOBeatType QString("BeatType")
+#define KXMLIOBeatsPerMinute QString("BPM")
class InputOutputMap : public QObject
{
@@ -499,7 +502,7 @@ class InputOutputMap : public QObject
* Send feedback value to the input profile e.g. to move a motorized
* sliders & knobs, set indicator leds etc.
*/
- bool sendFeedBack(quint32 universe, quint32 channel, uchar value, const QString& key = 0);
+ bool sendFeedBack(quint32 universe, quint32 channel, uchar value, const QVariant ¶ms);
private:
/** In case of duplicate strings, append a number to make them unique */
@@ -580,19 +583,22 @@ private slots:
{
Disabled, //! No one is generating beats
Internal, //! MasterTimer is the beat generator
- MIDI, //! A MIDI plugin is the beat generator
+ Plugin, //! A plugin is the beat generator
Audio //! An audio input device is the beat generator
};
void setBeatGeneratorType(BeatGeneratorType type);
BeatGeneratorType beatGeneratorType() const;
+ QString beatTypeToString(BeatGeneratorType type) const;
+ BeatGeneratorType stringToBeatType(QString str);
+
void setBpmNumber(int bpm);
int bpmNumber() const;
protected slots:
void slotMasterTimerBeat();
- void slotMIDIBeat(quint32 universe, quint32 channel, uchar value);
+ void slotPluginBeat(quint32 universe, quint32 channel, uchar value, const QString &key);
void slotAudioSpectrum(double *spectrumBands, int size, double maxMagnitude, quint32 power);
signals:
diff --git a/engine/src/qlcfixturemode.cpp b/engine/src/qlcfixturemode.cpp
index 08baaaf329..9ae993c882 100644
--- a/engine/src/qlcfixturemode.cpp
+++ b/engine/src/qlcfixturemode.cpp
@@ -252,6 +252,11 @@ quint32 QLCFixtureMode::masterIntensityChannel() const
return m_masterIntensityChannel;
}
+quint32 QLCFixtureMode::primaryChannel(quint32 chIndex)
+{
+ return m_secondaryMap.value(chIndex, QLCChannel::invalid());
+}
+
quint32 QLCFixtureMode::channelActsOn(quint32 chIndex)
{
return m_actsOnMap.value(chIndex, QLCChannel::invalid());
@@ -308,22 +313,39 @@ int QLCFixtureMode::headForChannel(quint32 chnum) const
void QLCFixtureMode::cacheHeads()
{
+ QLCChannel *lastChannel = NULL;
+
for (int i = 0; i < m_heads.size(); i++)
{
QLCFixtureHead& head(m_heads[i]);
head.cacheChannels(this);
}
- for (int i = 0; i < m_channels.size(); i++)
+ for (quint32 i = 0; i < quint32(m_channels.size()); i++)
{
- if (m_channels.at(i)->group() == QLCChannel::Intensity &&
- m_channels.at(i)->controlByte() == QLCChannel::MSB &&
- m_channels.at(i)->colour() == QLCChannel::NoColour &&
+ QLCChannel *channel = m_channels.at(i);
+
+ /** Auto-detect master intensity channel */
+ if (m_masterIntensityChannel == QLCChannel::invalid() &&
+ channel->group() == QLCChannel::Intensity &&
+ channel->controlByte() == QLCChannel::MSB &&
+ channel->colour() == QLCChannel::NoColour &&
headForChannel(i) == -1)
{
m_masterIntensityChannel = i;
- break;
}
+
+ /** Map secondary channels */
+ if (lastChannel != NULL &&
+ channel->group() == lastChannel->group() &&
+ lastChannel->controlByte() == QLCChannel::MSB &&
+ channel->controlByte() == QLCChannel::LSB)
+ {
+ //qDebug() << "Channel" << lastChannel->name() << "is primary and" << channel->name() << "is secondary";
+ m_secondaryMap[i] = i - 1;
+ }
+
+ lastChannel = channel;
}
}
diff --git a/engine/src/qlcfixturemode.h b/engine/src/qlcfixturemode.h
index adca973026..54485f3e1a 100644
--- a/engine/src/qlcfixturemode.h
+++ b/engine/src/qlcfixturemode.h
@@ -206,8 +206,13 @@ class QLCFixtureMode
*/
quint32 channelNumber(QLCChannel::Group group, QLCChannel::ControlByte cByte = QLCChannel::MSB) const;
+ /** Return the auto-detected channel index of the Fixture master dimmer for this mode */
quint32 masterIntensityChannel() const;
+ /** Return the index of the primary channel $chIndex relates to.
+ * Return invalid if not present */
+ quint32 primaryChannel(quint32 chIndex);
+
/** Return the channel index on which the given $chIndex acts on.
* Return invalid if not present */
quint32 channelActsOn(quint32 chIndex);
@@ -218,9 +223,14 @@ class QLCFixtureMode
QVector m_channels;
/** Map of channel indices that act on other channels.
- * These are stored as: < index, acts on index> */
+ * These are stored as: */
QMap m_actsOnMap;
+ /** Map of channel indices that relate to some other primary channel.
+ * For example Pan Fine vs Pan, Red Fine vs Red, etc
+ * These are stored as: */
+ QMap m_secondaryMap;
+
quint32 m_masterIntensityChannel;
/*********************************************************************
diff --git a/engine/src/qlcinputchannel.cpp b/engine/src/qlcinputchannel.cpp
index e4b8ee6f3c..db99554c19 100644
--- a/engine/src/qlcinputchannel.cpp
+++ b/engine/src/qlcinputchannel.cpp
@@ -34,8 +34,9 @@ QLCInputChannel::QLCInputChannel()
, m_movementType(Absolute)
, m_movementSensitivity(20)
, m_sendExtraPress(false)
- , m_lower(0)
- , m_upper(UCHAR_MAX)
+ , m_lowerValue(0)
+ , m_upperValue(UCHAR_MAX)
+ , m_lowerChannel(-1)
{
}
@@ -47,6 +48,7 @@ QLCInputChannel *QLCInputChannel::createCopy()
copy->setMovementType(this->movementType());
copy->setMovementSensitivity(this->movementSensitivity());
copy->setSendExtraPress(this->sendExtraPress());
+ copy->setLowerChannel(this->lowerChannel());
copy->setRange(this->lowerValue(), this->upperValue());
return copy;
@@ -244,32 +246,46 @@ void QLCInputChannel::setRange(uchar lower, uchar upper)
uchar QLCInputChannel::lowerValue() const
{
- return m_lower;
+ return m_lowerValue;
}
void QLCInputChannel::setLowerValue(const uchar value)
{
- if (value == m_lower)
+ if (value == m_lowerValue)
return;
-
- m_lower = value;
+
+ m_lowerValue = value;
emit lowerValueChanged();
}
uchar QLCInputChannel::upperValue() const
{
- return m_upper;
+ return m_upperValue;
}
void QLCInputChannel::setUpperValue(const uchar value)
{
- if (value == m_upper)
+ if (value == m_upperValue)
return;
-
- m_upper = value;
+
+ m_upperValue = value;
emit upperValueChanged();
}
+int QLCInputChannel::lowerChannel() const
+{
+ return m_lowerChannel;
+}
+
+void QLCInputChannel::setLowerChannel(const int channel)
+{
+ if (channel == m_lowerChannel)
+ return;
+
+ m_lowerChannel = channel;
+ emit midiChannelChanged();
+}
+
/****************************************************************************
* Load & Save
****************************************************************************/
@@ -305,16 +321,21 @@ bool QLCInputChannel::loadXML(QXmlStreamReader &root)
if (root.readElementText() == KXMLQLCInputChannelRelative)
setMovementType(Relative);
}
- else if (root.name() == KXMLQLCInputChannelFeedbacks)
+ else if (root.name() == KXMLQLCInputChannelFeedback)
{
+ QXmlStreamAttributes attrs = root.attributes();
uchar min = 0, max = UCHAR_MAX;
+ int fbChannel = -1;
- if (root.attributes().hasAttribute(KXMLQLCInputChannelLowerValue))
- min = uchar(root.attributes().value(KXMLQLCInputChannelLowerValue).toString().toUInt());
- if (root.attributes().hasAttribute(KXMLQLCInputChannelUpperValue))
- max = uchar(root.attributes().value(KXMLQLCInputChannelUpperValue).toString().toUInt());
+ if (attrs.hasAttribute(KXMLQLCInputChannelLowerValue))
+ min = uchar(attrs.value(KXMLQLCInputChannelLowerValue).toString().toUInt());
+ if (attrs.hasAttribute(KXMLQLCInputChannelUpperValue))
+ max = uchar(attrs.value(KXMLQLCInputChannelUpperValue).toString().toUInt());
+ if (attrs.hasAttribute(KXMLQLCInputChannelMidiChannel))
+ fbChannel = attrs.value(KXMLQLCInputChannelMidiChannel).toInt();
setRange(min, max);
+ setLowerChannel(fbChannel);
root.skipCurrentElement();
}
else
@@ -357,11 +378,13 @@ bool QLCInputChannel::saveXML(QXmlStreamWriter *doc, quint32 channelNumber) cons
}
else if (type() == Button && (lowerValue() != 0 || upperValue() != UCHAR_MAX))
{
- doc->writeStartElement(KXMLQLCInputChannelFeedbacks);
+ doc->writeStartElement(KXMLQLCInputChannelFeedback);
if (lowerValue() != 0)
doc->writeAttribute(KXMLQLCInputChannelLowerValue, QString::number(lowerValue()));
if (upperValue() != UCHAR_MAX)
doc->writeAttribute(KXMLQLCInputChannelUpperValue, QString::number(upperValue()));
+ if (lowerChannel() != -1)
+ doc->writeAttribute(KXMLQLCInputChannelMidiChannel, QString::number(lowerChannel()));
doc->writeEndElement();
}
diff --git a/engine/src/qlcinputchannel.h b/engine/src/qlcinputchannel.h
index 27edabae82..88dad0bc73 100644
--- a/engine/src/qlcinputchannel.h
+++ b/engine/src/qlcinputchannel.h
@@ -32,25 +32,26 @@ class QString;
* @{
*/
-#define KXMLQLCInputChannel QString("Channel")
-#define KXMLQLCInputChannelName QString("Name")
-#define KXMLQLCInputChannelType QString("Type")
-#define KXMLQLCInputChannelNumber QString("Number")
-#define KXMLQLCInputChannelSlider QString("Slider")
-#define KXMLQLCInputChannelKnob QString("Knob")
-#define KXMLQLCInputChannelEncoder QString("Encoder")
-#define KXMLQLCInputChannelButton QString("Button")
-#define KXMLQLCInputChannelPageUp QString("Next Page")
-#define KXMLQLCInputChannelPageDown QString("Previous Page")
-#define KXMLQLCInputChannelPageSet QString("Page Set")
-#define KXMLQLCInputChannelNone QString("None")
-#define KXMLQLCInputChannelMovement QString("Movement")
-#define KXMLQLCInputChannelRelative QString("Relative")
-#define KXMLQLCInputChannelSensitivity QString("Sensitivity")
-#define KXMLQLCInputChannelExtraPress QString("ExtraPress")
-#define KXMLQLCInputChannelFeedbacks QString("Feedbacks")
-#define KXMLQLCInputChannelLowerValue QString("LowerValue")
-#define KXMLQLCInputChannelUpperValue QString("UpperValue")
+#define KXMLQLCInputChannel QString("Channel")
+#define KXMLQLCInputChannelName QString("Name")
+#define KXMLQLCInputChannelType QString("Type")
+#define KXMLQLCInputChannelNumber QString("Number")
+#define KXMLQLCInputChannelSlider QString("Slider")
+#define KXMLQLCInputChannelKnob QString("Knob")
+#define KXMLQLCInputChannelEncoder QString("Encoder")
+#define KXMLQLCInputChannelButton QString("Button")
+#define KXMLQLCInputChannelPageUp QString("Next Page")
+#define KXMLQLCInputChannelPageDown QString("Previous Page")
+#define KXMLQLCInputChannelPageSet QString("Page Set")
+#define KXMLQLCInputChannelNone QString("None")
+#define KXMLQLCInputChannelMovement QString("Movement")
+#define KXMLQLCInputChannelRelative QString("Relative")
+#define KXMLQLCInputChannelSensitivity QString("Sensitivity")
+#define KXMLQLCInputChannelExtraPress QString("ExtraPress")
+#define KXMLQLCInputChannelFeedback QString("Feedback")
+#define KXMLQLCInputChannelLowerValue QString("LowerValue")
+#define KXMLQLCInputChannelUpperValue QString("UpperValue")
+#define KXMLQLCInputChannelMidiChannel QString("MidiChannel")
class QLCInputChannel : public QObject
{
@@ -190,14 +191,22 @@ class QLCInputChannel : public QObject
uchar upperValue() const;
void setUpperValue(const uchar value);
+ int lowerChannel() const;
+ void setLowerChannel(const int channel);
+
+ int upperChannel() const;
+ void setUpperChannel(const int channel);
+
signals:
void sendExtraPressChanged();
void lowerValueChanged();
void upperValueChanged();
+ void midiChannelChanged();
protected:
bool m_sendExtraPress;
- uchar m_lower, m_upper;
+ uchar m_lowerValue, m_upperValue;
+ int m_lowerChannel, m_upperChannel;
/********************************************************************
* Load & Save
diff --git a/engine/src/qlcinputfeedback.cpp b/engine/src/qlcinputfeedback.cpp
new file mode 100644
index 0000000000..48980fc2bb
--- /dev/null
+++ b/engine/src/qlcinputfeedback.cpp
@@ -0,0 +1,71 @@
+/*
+ Q Light Controller Plus
+ qlcinputfeedback.cpp
+
+ Copyright (c) Massimo Callegari
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0.txt
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+#include "qlcinputfeedback.h"
+
+
+QLCInputFeedback::QLCInputFeedback()
+ : m_type(Undefinded)
+ , m_value(0)
+{
+}
+
+QLCInputFeedback *QLCInputFeedback::createCopy()
+{
+ QLCInputFeedback *copy = new QLCInputFeedback();
+ copy->setType(this->type());
+ copy->setValue(this->value());
+ copy->setExtraParams(this->extraParams());
+
+ return copy;
+}
+
+QLCInputFeedback::~QLCInputFeedback()
+{
+}
+
+QLCInputFeedback::FeedbackType QLCInputFeedback::type() const
+{
+ return m_type;
+}
+
+void QLCInputFeedback::setType(FeedbackType type)
+{
+ m_type = type;
+}
+
+uchar QLCInputFeedback::value() const
+{
+ return m_value;
+}
+
+void QLCInputFeedback::setValue(uchar value)
+{
+ m_value = value;
+}
+
+QVariant QLCInputFeedback::extraParams() const
+{
+ return m_extraParams;
+}
+
+void QLCInputFeedback::setExtraParams(QVariant params)
+{
+ m_extraParams = params;
+}
diff --git a/engine/src/qlcinputfeedback.h b/engine/src/qlcinputfeedback.h
new file mode 100644
index 0000000000..360ff98e56
--- /dev/null
+++ b/engine/src/qlcinputfeedback.h
@@ -0,0 +1,70 @@
+/*
+ Q Light Controller Plus
+ qlcinputfeedback.h
+
+ Copyright (c) Massimo Callegari
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0.txt
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+#ifndef QLCINPUTFEEDBACK_H
+#define QLCINPUTFEEDBACK_H
+
+#include
+#include
+
+class QLCInputFeedback : public QObject
+{
+ Q_OBJECT
+
+ /********************************************************************
+ * Initialization
+ ********************************************************************/
+public:
+ /** Standard constructor */
+ QLCInputFeedback();
+
+ /** Copy constructor */
+ QLCInputFeedback *createCopy();
+
+ /** Destructor */
+ virtual ~QLCInputFeedback();
+
+ /** Feedback type */
+ enum FeedbackType
+ {
+ Undefinded = -1,
+ LowerValue = 0,
+ UpperValue = 1,
+ MonitorValue = 2
+ };
+#if QT_VERSION >= 0x050500
+ Q_ENUM(FeedbackType)
+#endif
+
+ FeedbackType type() const;
+ void setType(FeedbackType type);
+
+ uchar value() const;
+ void setValue(uchar value);
+
+ QVariant extraParams() const;
+ void setExtraParams(QVariant params);
+
+protected:
+ FeedbackType m_type;
+ uchar m_value;
+ QVariant m_extraParams;
+};
+
+#endif /* QLCINPUTFEEDBACK_H */
diff --git a/engine/src/qlcinputprofile.cpp b/engine/src/qlcinputprofile.cpp
index 3d1a8b2872..e0377de014 100644
--- a/engine/src/qlcinputprofile.cpp
+++ b/engine/src/qlcinputprofile.cpp
@@ -35,6 +35,9 @@
#define KXMLQLCInputProfileTypeDmx "DMX"
#define KXMLQLCInputProfileTypeEnttec "Enttec"
+#define KXMLQLCInputProfileValue "Value"
+#define KXMLQLCInputProfileLabel "Label"
+#define KXMLQLCInputProfileColorRGB "RGB"
/****************************************************************************
* Initialization
@@ -71,6 +74,23 @@ QLCInputProfile *QLCInputProfile::createCopy()
copy->insertChannel(it.key(), it.value()->createCopy());
}
+ /* Copy the other profile's color table */
+ QMapIterator > it2(this->colorTable());
+ while (it2.hasNext() == true)
+ {
+ it2.next();
+ QPair lc = it2.value();
+ copy->addColor(it2.key(), lc.first, lc.second);
+ }
+
+ /* Copy the other profile's MIDI channel tabel */
+ QMapIterator it3(this->midiChannelTable());
+ while (it3.hasNext() == true)
+ {
+ it3.next();
+ copy->addMidiChannel(it3.key(), it3.value());
+ }
+
return copy;
}
@@ -90,12 +110,29 @@ QLCInputProfile& QLCInputProfile::operator=(const QLCInputProfile& profile)
destroyChannels();
/* Copy the other profile's channels */
- QMapIterator it(profile.m_channels);
+ QMapIterator it(profile.m_channels);
while (it.hasNext() == true)
{
it.next();
insertChannel(it.key(), it.value()->createCopy());
}
+
+ /* Copy the other profile's color table */
+ QMapIterator > it2(profile.m_colorTable);
+ while (it2.hasNext() == true)
+ {
+ it2.next();
+ QPair lc = it2.value();
+ addColor(it2.key(), lc.first, lc.second);
+ }
+
+ /* Copy the other profile's MIDI channel tabel */
+ QMapIterator it3(profile.m_midiChannelTable);
+ while (it3.hasNext() == true)
+ {
+ it3.next();
+ addMidiChannel(it3.key(), it3.value());
+ }
}
return *this;
@@ -300,6 +337,19 @@ QMap QLCInputProfile::channels() const
return m_channels;
}
+QVariant QLCInputProfile::channelExtraParams(const QLCInputChannel* channel) const
+{
+ if (channel == NULL)
+ return QVariant();
+
+ switch (m_type)
+ {
+ case OSC: return channel->name();
+ case MIDI: return channel->lowerChannel();
+ default: return QVariant();
+ }
+}
+
void QLCInputProfile::destroyChannels()
{
/* Delete existing channels but leave the pointers there */
@@ -311,6 +361,53 @@ void QLCInputProfile::destroyChannels()
m_channels.clear();
}
+bool QLCInputProfile::hasColorTable()
+{
+ return m_colorTable.isEmpty() ? false : true;
+}
+
+void QLCInputProfile::addColor(uchar value, QString label, QColor color)
+{
+ QPair lc;
+ lc.first = label;
+ lc.second = color;
+ m_colorTable.insert(value, lc);
+}
+
+void QLCInputProfile::removeColor(uchar value)
+{
+ m_colorTable.remove(value);
+}
+
+QMap > QLCInputProfile::colorTable()
+{
+ return m_colorTable;
+}
+
+/********************************************************************
+ * MIDI Channel table
+ ********************************************************************/
+
+bool QLCInputProfile::hasMidiChannelTable()
+{
+ return m_midiChannelTable.isEmpty() ? false : true;
+}
+
+void QLCInputProfile::addMidiChannel(uchar channel, QString label)
+{
+ m_midiChannelTable.insert(channel, label);
+}
+
+void QLCInputProfile::removeMidiChannel(uchar channel)
+{
+ m_midiChannelTable.remove(channel);
+}
+
+QMap QLCInputProfile::midiChannelTable()
+{
+ return m_midiChannelTable;
+}
+
/****************************************************************************
* Load & Save
****************************************************************************/
@@ -345,6 +442,65 @@ QLCInputProfile* QLCInputProfile::loader(const QString& path)
return profile;
}
+bool QLCInputProfile::loadColorTableXML(QXmlStreamReader &tableRoot)
+{
+ if (tableRoot.name() != KXMLQLCInputProfileColorTable)
+ {
+ qWarning() << Q_FUNC_INFO << "Color table node not found";
+ return false;
+ }
+
+ tableRoot.readNextStartElement();
+
+ do
+ {
+ if (tableRoot.name() == KXMLQLCInputProfileColor)
+ {
+ /* get value & color */
+ uchar value = tableRoot.attributes().value(KXMLQLCInputProfileValue).toInt();
+ QString label = tableRoot.attributes().value(KXMLQLCInputProfileLabel).toString();
+ QColor color = QColor(tableRoot.attributes().value(KXMLQLCInputProfileColorRGB).toString());
+ addColor(value, label, color);
+ }
+ else
+ {
+ qWarning() << Q_FUNC_INFO << "Unknown color table tag:" << tableRoot.name().toString();
+ }
+ tableRoot.skipCurrentElement();
+ } while (tableRoot.readNextStartElement());
+
+ return true;
+}
+
+bool QLCInputProfile::loadMidiChannelTableXML(QXmlStreamReader &tableRoot)
+{
+ if (tableRoot.name() != KXMLQLCInputProfileMidiChannelTable)
+ {
+ qWarning() << Q_FUNC_INFO << "MIDI channel table node not found";
+ return false;
+ }
+
+ tableRoot.readNextStartElement();
+
+ do
+ {
+ if (tableRoot.name() == KXMLQLCInputProfileMidiChannel)
+ {
+ /* get value & color */
+ uchar value = tableRoot.attributes().value(KXMLQLCInputProfileValue).toInt();
+ QString label = tableRoot.attributes().value(KXMLQLCInputProfileLabel).toString();
+ addMidiChannel(value, label);
+ }
+ else
+ {
+ qWarning() << Q_FUNC_INFO << "Unknown MIDI channel table tag:" << tableRoot.name().toString();
+ }
+ tableRoot.skipCurrentElement();
+ } while (tableRoot.readNextStartElement());
+
+ return true;
+}
+
bool QLCInputProfile::loadXML(QXmlStreamReader& doc)
{
if (doc.readNextStartElement() == false)
@@ -393,6 +549,19 @@ bool QLCInputProfile::loadXML(QXmlStreamReader& doc)
else
doc.skipCurrentElement();
}
+ else if (doc.name() == KXMLQLCInputProfileColorTable)
+ {
+ loadColorTableXML(doc);
+ }
+ else if (doc.name() == KXMLQLCInputProfileMidiChannelTable)
+ {
+ loadMidiChannelTableXML(doc);
+ }
+ else
+ {
+ qWarning() << Q_FUNC_INFO << "Unknown input profile tag:" << doc.name().toString();
+ doc.skipCurrentElement();
+ }
}
return true;
@@ -433,10 +602,48 @@ bool QLCInputProfile::saveXML(const QString& fileName)
it.value()->saveXML(&doc, it.key());
}
+ if (hasColorTable())
+ {
+ doc.writeStartElement(KXMLQLCInputProfileColorTable);
+
+ QMapIterator > it(m_colorTable);
+ while (it.hasNext() == true)
+ {
+ it.next();
+ QPair lc = it.value();
+ doc.writeStartElement(KXMLQLCInputProfileColor);
+ doc.writeAttribute(KXMLQLCInputProfileValue, QString::number(it.key()));
+ doc.writeAttribute(KXMLQLCInputProfileLabel, lc.first);
+ doc.writeAttribute(KXMLQLCInputProfileColorRGB, lc.second.name());
+ doc.writeEndElement();
+ }
+
+ doc.writeEndElement();
+ }
+
+ if (hasMidiChannelTable())
+ {
+ doc.writeStartElement(KXMLQLCInputProfileMidiChannelTable);
+
+ QMapIterator it(m_midiChannelTable);
+ while (it.hasNext() == true)
+ {
+ it.next();
+ doc.writeStartElement(KXMLQLCInputProfileMidiChannel);
+ doc.writeAttribute(KXMLQLCInputProfileValue, QString::number(it.key()));
+ doc.writeAttribute(KXMLQLCInputProfileLabel, it.value());
+ doc.writeEndElement();
+
+ }
+ doc.writeEndElement();
+ }
+
m_path = fileName;
+
/* End the document and close all the open elements */
doc.writeEndDocument();
file.close();
return true;
}
+
diff --git a/engine/src/qlcinputprofile.h b/engine/src/qlcinputprofile.h
index 70027ce437..69c6aaa200 100644
--- a/engine/src/qlcinputprofile.h
+++ b/engine/src/qlcinputprofile.h
@@ -40,6 +40,10 @@ class QXmlStreamReader;
#define KXMLQLCInputProfileModel QString("Model")
#define KXMLQLCInputProfileType QString("Type")
#define KXMLQLCInputProfileMidiSendNoteOff QString("MIDISendNoteOff")
+#define KXMLQLCInputProfileColorTable QString("ColorTable")
+#define KXMLQLCInputProfileColor QString("Color")
+#define KXMLQLCInputProfileMidiChannelTable QString("MidiChannelTable")
+#define KXMLQLCInputProfileMidiChannel QString("Channel")
class QLCInputProfile : public QObject
{
@@ -165,7 +169,7 @@ class QLCInputProfile : public QObject
* @param channel The number of the channel to get.
* @return A QLCInputChannel* or NULL if not found.
*/
- QLCInputChannel* channel(quint32 channel) const;
+ QLCInputChannel *channel(quint32 channel) const;
/**
* Get the channel number for the given input channel.
@@ -180,6 +184,12 @@ class QLCInputProfile : public QObject
*/
QMap channels() const;
+ /**
+ * Retrieve additional parameters to be passed to plugins
+ * when sending feedback.
+ */
+ QVariant channelExtraParams(const QLCInputChannel *channel) const;
+
private:
/** Delete and remove all channels */
void destroyChannels();
@@ -189,6 +199,32 @@ class QLCInputProfile : public QObject
QList because not all channels might be present. */
QMap m_channels;
+ /********************************************************************
+ * Color Translation Table
+ ********************************************************************/
+public:
+ bool hasColorTable();
+ void addColor(uchar value, QString label, QColor color);
+ void removeColor(uchar value);
+
+ QMap> colorTable();
+
+protected:
+ QMap> m_colorTable;
+
+ /********************************************************************
+ * MIDI Channel table
+ ********************************************************************/
+public:
+ bool hasMidiChannelTable();
+ void addMidiChannel(uchar channel, QString label);
+ void removeMidiChannel(uchar channel);
+
+ QMap midiChannelTable();
+
+protected:
+ QMap m_midiChannelTable;
+
/********************************************************************
* Load & Save
********************************************************************/
@@ -199,6 +235,12 @@ class QLCInputProfile : public QObject
/** Save an input profile into a given file name */
bool saveXML(const QString& fileName);
+ /** Load an optional color table for RGB LED feedback */
+ bool loadColorTableXML(QXmlStreamReader &tableRoot);
+
+ /** Load an optional MIDI channel table */
+ bool loadMidiChannelTableXML(QXmlStreamReader &tableRoot);
+
/** Load an input profile from the given document */
bool loadXML(QXmlStreamReader &doc);
};
diff --git a/engine/src/qlcinputsource.cpp b/engine/src/qlcinputsource.cpp
index b418b1b39c..ad7eb151a2 100644
--- a/engine/src/qlcinputsource.cpp
+++ b/engine/src/qlcinputsource.cpp
@@ -36,8 +36,6 @@ QLCInputSource::QLCInputSource(QThread *parent)
, m_universe(invalidUniverse)
, m_channel(invalidChannel)
, m_id(invalidID)
- , m_lower(0)
- , m_upper(255)
, m_workingMode(Absolute)
, m_sensitivity(20)
, m_emitExtraPressRelease(false)
@@ -45,14 +43,18 @@ QLCInputSource::QLCInputSource(QThread *parent)
, m_outputValue(0)
, m_running(false)
{
+ m_lower.setType(QLCInputFeedback::LowerValue);
+ m_lower.setValue(0);
+ m_upper.setType(QLCInputFeedback::UpperValue);
+ m_upper.setValue(UCHAR_MAX);
+ m_monitor.setType(QLCInputFeedback::MonitorValue);
+ m_monitor.setValue(UCHAR_MAX);
}
QLCInputSource::QLCInputSource(quint32 universe, quint32 channel, QThread *parent)
: QThread(parent)
, m_universe(universe)
, m_channel(channel)
- , m_lower(0)
- , m_upper(255)
, m_workingMode(Absolute)
, m_sensitivity(20)
, m_emitExtraPressRelease(false)
@@ -60,6 +62,12 @@ QLCInputSource::QLCInputSource(quint32 universe, quint32 channel, QThread *paren
, m_outputValue(0)
, m_running(false)
{
+ m_lower.setType(QLCInputFeedback::LowerValue);
+ m_lower.setValue(0);
+ m_upper.setType(QLCInputFeedback::UpperValue);
+ m_upper.setValue(UCHAR_MAX);
+ m_monitor.setType(QLCInputFeedback::MonitorValue);
+ m_monitor.setValue(UCHAR_MAX);
}
QLCInputSource::~QLCInputSource()
@@ -120,20 +128,66 @@ quint32 QLCInputSource::id() const
return m_id;
}
-void QLCInputSource::setRange(uchar lower, uchar upper)
+/*********************************************************************
+ * Custom feedback
+ *********************************************************************/
+
+uchar QLCInputSource::feedbackValue(QLCInputFeedback::FeedbackType type) const
+{
+ switch (type)
+ {
+ case QLCInputFeedback::LowerValue: return m_lower.value();
+ case QLCInputFeedback::UpperValue: return m_upper.value();
+ case QLCInputFeedback::MonitorValue: return m_monitor.value();
+ default: return 0;
+ }
+}
+
+void QLCInputSource::setFeedbackValue(QLCInputFeedback::FeedbackType type, uchar value)
{
- m_lower = lower;
- m_upper = upper;
+ switch (type)
+ {
+ case QLCInputFeedback::LowerValue:
+ m_lower.setValue(value);
+ break;
+ case QLCInputFeedback::UpperValue:
+ m_upper.setValue(value);
+ break;
+ case QLCInputFeedback::MonitorValue:
+ m_monitor.setValue(value);
+ break;
+ default:
+ break;
+ }
}
-uchar QLCInputSource::lowerValue() const
+QVariant QLCInputSource::feedbackExtraParams(QLCInputFeedback::FeedbackType type) const
{
- return m_lower;
+ switch (type)
+ {
+ case QLCInputFeedback::LowerValue: return m_lower.extraParams();
+ case QLCInputFeedback::UpperValue: return m_upper.extraParams();
+ case QLCInputFeedback::MonitorValue: return m_monitor.extraParams();
+ default: return 0;
+ }
}
-uchar QLCInputSource::upperValue() const
+void QLCInputSource::setFeedbackExtraParams(QLCInputFeedback::FeedbackType type, QVariant params)
{
- return m_upper;
+ switch (type)
+ {
+ case QLCInputFeedback::LowerValue:
+ m_lower.setExtraParams(params);
+ break;
+ case QLCInputFeedback::UpperValue:
+ m_upper.setExtraParams(params);
+ break;
+ case QLCInputFeedback::MonitorValue:
+ m_monitor.setExtraParams(params);
+ break;
+ default:
+ break;
+ }
}
/*********************************************************************
@@ -210,8 +264,8 @@ void QLCInputSource::updateInputValue(uchar value)
else if (m_emitExtraPressRelease == true)
{
locker.unlock();
- emit inputValueChanged(m_universe, m_channel, m_upper);
- emit inputValueChanged(m_universe, m_channel, m_lower);
+ emit inputValueChanged(m_universe, m_channel, m_upper.value());
+ emit inputValueChanged(m_universe, m_channel, m_lower.value());
}
else
m_inputValue = value;
diff --git a/engine/src/qlcinputsource.h b/engine/src/qlcinputsource.h
index 42b762a98d..b1b7b52829 100644
--- a/engine/src/qlcinputsource.h
+++ b/engine/src/qlcinputsource.h
@@ -20,9 +20,12 @@
#ifndef QLCINPUTSOURCE_H
#define QLCINPUTSOURCE_H
+#include
#include
#include
+#include "qlcinputfeedback.h"
+
/** @addtogroup engine Engine
* @{
*/
@@ -76,12 +79,20 @@ class QLCInputSource: public QThread
* Custom feedback
*********************************************************************/
public:
- void setRange(uchar lower, uchar upper);
- uchar lowerValue() const;
- uchar upperValue() const;
+ uchar feedbackValue(QLCInputFeedback::FeedbackType type) const;
+ void setFeedbackValue(QLCInputFeedback::FeedbackType type, uchar value);
+
+ /** Get/set specific plugins params.
+ * OSC: a string with the command path
+ * MIDI: a channel modifier
+ */
+ QVariant feedbackExtraParams(QLCInputFeedback::FeedbackType type) const;
+ void setFeedbackExtraParams(QLCInputFeedback::FeedbackType type, QVariant params);
protected:
- uchar m_lower, m_upper;
+ QLCInputFeedback m_lower;
+ QLCInputFeedback m_upper;
+ QLCInputFeedback m_monitor;
/*********************************************************************
* Working mode
diff --git a/engine/src/rgbmatrix.cpp b/engine/src/rgbmatrix.cpp
index 7b551b2c11..fb63da39e5 100644
--- a/engine/src/rgbmatrix.cpp
+++ b/engine/src/rgbmatrix.cpp
@@ -168,6 +168,7 @@ bool RGBMatrix::copyFrom(const Function* function)
setAlgorithm(NULL);
setStartColor(mtx->startColor());
setEndColor(mtx->endColor());
+ setControlMode(mtx->controlMode());
return Function::copyFrom(function);
}
diff --git a/engine/src/scene.cpp b/engine/src/scene.cpp
index 688ff2fb25..25f6350554 100644
--- a/engine/src/scene.cpp
+++ b/engine/src/scene.cpp
@@ -161,7 +161,7 @@ void Scene::setValue(const SceneValue& scv, bool blind, bool checkHTP)
m_fadersMap[universe]->add(fc);
}
}
- }
+ }
}
emit changed(this->id());
@@ -723,12 +723,13 @@ void Scene::processValue(MasterTimer *timer, QList ua, uint fadeIn, S
fader->setBlendMode(blendMode());
fader->setName(name());
fader->setParentFunctionID(id());
- m_fadersMap[universe] = fader;
-
fader->setParentIntensity(getAttributeValue(ParentIntensity));
+ fader->setHandleSecondary(true);
+ m_fadersMap[universe] = fader;
}
FadeChannel *fc = fader->getChannelFader(doc(), ua[universe], scv.fxi, scv.channel);
+ int chIndex = fc->channelIndex(scv.channel);
/** If a blend Function has been set, check if this channel needs to
* be blended from a previous value. If so, mark it for crossfade
@@ -739,18 +740,18 @@ void Scene::processValue(MasterTimer *timer, QList ua, uint fadeIn, S
if (blendScene != NULL && blendScene->checkValue(scv))
{
fc->addFlag(FadeChannel::CrossFade);
- fc->setCurrent(blendScene->value(scv.fxi, scv.channel));
+ fc->setCurrent(blendScene->value(scv.fxi, scv.channel), chIndex);
qDebug() << "----- BLEND from Scene" << blendScene->name()
<< ", fixture:" << scv.fxi << ", channel:" << scv.channel << ", value:" << fc->current();
}
}
else
{
- qDebug() << "Scene" << name() << "add channel" << scv.channel << "from" << fc->current() << "to" << scv.value;
+ qDebug() << "Scene" << name() << "add channel" << scv.channel << "from" << fc->current(chIndex) << "to" << scv.value;
}
- fc->setStart(fc->current());
- fc->setTarget(scv.value);
+ fc->setStart(fc->current(chIndex), chIndex);
+ fc->setTarget(scv.value, chIndex);
if (fc->canFade() == false)
{
diff --git a/engine/src/scriptrunner.cpp b/engine/src/scriptrunner.cpp
index 7a883210c3..d41260367e 100644
--- a/engine/src/scriptrunner.cpp
+++ b/engine/src/scriptrunner.cpp
@@ -517,6 +517,18 @@ bool ScriptRunner::setBlackout(bool enable)
return true;
}
+bool ScriptRunner::setBPM(int bpm)
+{
+ if (m_running == false)
+ return false;
+
+ qDebug() << Q_FUNC_INFO;
+
+ m_doc->inputOutputMap()->setBpmNumber(bpm);
+
+ return true;
+}
+
int ScriptRunner::random(QString minTime, QString maxTime)
{
if (m_running == false)
diff --git a/engine/src/scriptrunner.h b/engine/src/scriptrunner.h
index 6afc23f024..f5e0c835b4 100644
--- a/engine/src/scriptrunner.h
+++ b/engine/src/scriptrunner.h
@@ -173,6 +173,14 @@ public slots:
*/
bool setBlackout(bool enable);
+ /**
+ * Set the BPM (beat per minute) number of the internal beat generator
+ *
+ * @param bpm the number of beats per minute requested
+ * @return true if successful. False on error.
+ */
+ bool setBPM(int bpm);
+
/**
* Handle "random" command (string version)
*
diff --git a/engine/src/showrunner.cpp b/engine/src/showrunner.cpp
index 37a37eaa59..ffe2eb1368 100644
--- a/engine/src/showrunner.cpp
+++ b/engine/src/showrunner.cpp
@@ -90,11 +90,11 @@ ShowRunner::ShowRunner(const Doc* doc, quint32 showID, quint32 startTime)
#if 1
qDebug() << "Ordered list of ShowFunctions (time):";
foreach (ShowFunction *sfunc, m_timeFunctions)
- qDebug() << "ID:" << sfunc->functionID() << "st:" << sfunc->startTime() << "dur:" << sfunc->duration(m_doc);
+ qDebug() << "[Show] Function ID:" << sfunc->functionID() << "start time:" << sfunc->startTime() << "duration:" << sfunc->duration(m_doc);
qDebug() << "Ordered list of ShowFunctions (beats):";
foreach (ShowFunction *sfunc, m_beatFunctions)
- qDebug() << "ID:" << sfunc->functionID() << "st:" << sfunc->startTime() << "dur:" << sfunc->duration(m_doc);
+ qDebug() << "[Show] Function ID:" << sfunc->functionID() << "start time:" << sfunc->startTime() << "duration:" << sfunc->duration(m_doc);
#endif
m_runningQueue.clear();
diff --git a/engine/src/src.pro b/engine/src/src.pro
index d6694d2039..3ac314b9b0 100644
--- a/engine/src/src.pro
+++ b/engine/src/src.pro
@@ -51,6 +51,7 @@ HEADERS += avolitesd4parser.h \
qlcfixturemode.h \
qlci18n.h \
qlcinputchannel.h \
+ qlcinputfeedback.h \
qlcinputprofile.h \
qlcinputsource.h \
qlcmodifierscache.h \
@@ -131,6 +132,7 @@ SOURCES += avolitesd4parser.cpp \
qlcfixturemode.cpp \
qlci18n.cpp \
qlcinputchannel.cpp \
+ qlcinputfeedback.cpp \
qlcinputprofile.cpp \
qlcinputsource.cpp \
qlcmodifierscache.cpp \
diff --git a/engine/src/universe.cpp b/engine/src/universe.cpp
index 1008e5e4c0..eceeeee709 100644
--- a/engine/src/universe.cpp
+++ b/engine/src/universe.cpp
@@ -35,7 +35,8 @@
#include "qlcfile.h"
#include "utils.h"
-#define RELATIVE_ZERO 127
+#define RELATIVE_ZERO_8BIT 0x7F
+#define RELATIVE_ZERO_16BIT 0x7F00
#define KXMLUniverseNormalBlend "Normal"
#define KXMLUniverseMaskBlend "Mask"
@@ -62,7 +63,6 @@ Universe::Universe(quint32 id, GrandMaster *gm, QObject *parent)
, m_blackoutValues(new QByteArray(UNIVERSE_SIZE, char(0)))
, m_passthroughValues()
{
- m_relativeValues.fill(0, UNIVERSE_SIZE);
m_modifiers.fill(NULL, UNIVERSE_SIZE);
m_name = QString("Universe %1").arg(id + 1);
@@ -228,7 +228,8 @@ QSharedPointer Universe::requestFader(Universe::FaderPriority prio
m_faders.insert(insertPos, fader);
}
- qDebug() << "Generic fader with priority" << fader->priority() << "registered at pos" << insertPos << ", count" << m_faders.count();
+ qDebug() << "[Universe]" << id() << ": Generic fader with priority" << fader->priority()
+ << "registered at pos" << insertPos << ", count" << m_faders.count();
return fader;
}
@@ -265,7 +266,8 @@ void Universe::requestFaderPriority(QSharedPointer fader, Universe
if (newPos != pos)
{
m_faders.move(pos, newPos);
- qDebug() << "Generic fader moved from" << pos << "to" << m_faders.indexOf(fader) << ". Count:" << m_faders.count();
+ qDebug() << "[Universe]" << id() << ": Generic fader moved from" << pos
+ << "to" << m_faders.indexOf(fader) << ". Count:" << m_faders.count();
}
}
@@ -296,7 +298,6 @@ void Universe::processFaders()
{
flushInput();
zeroIntensityChannels();
- zeroRelativeValues();
QMutableListIterator > it(m_faders);
while (it.hasNext())
@@ -364,14 +365,10 @@ void Universe::reset()
m_blackoutValues->fill(0);
if (m_passthrough)
- {
(*m_postGMValues) = (*m_passthroughValues);
- }
else
- {
m_postGMValues->fill(0);
- }
- zeroRelativeValues();
+
m_modifiers.fill(NULL, UNIVERSE_SIZE);
m_passthrough = false; // not releasing m_passthroughValues, see comment in setPassthrough
}
@@ -386,7 +383,6 @@ void Universe::reset(int address, int range)
memset(m_preGMValues->data() + address, 0, range * sizeof(*m_preGMValues->data()));
memset(m_blackoutValues->data() + address, 0, range * sizeof(*m_blackoutValues->data()));
- memset(m_relativeValues.data() + address, 0, range * sizeof(*m_relativeValues.data()));
memcpy(m_postGMValues->data() + address, m_modifiedZeroValues->data() + address, range * sizeof(*m_postGMValues->data()));
applyPassthroughValues(address, range);
@@ -443,11 +439,6 @@ const QByteArray* Universe::postGMValues() const
return m_postGMValues.data();
}
-void Universe::zeroRelativeValues()
-{
- memset(m_relativeValues.data(), 0, UNIVERSE_SIZE * sizeof(*m_relativeValues.data()));
-}
-
Universe::BlendMode Universe::stringToBlendMode(QString mode)
{
if (mode == KXMLUniverseNormalBlend)
@@ -495,17 +486,6 @@ uchar Universe::preGMValue(int address) const
return static_cast(m_preGMValues->at(address));
}
-uchar Universe::applyRelative(int channel, uchar value)
-{
- if (m_relativeValues[channel] != 0)
- {
- int val = m_relativeValues[channel] + value;
- return CLAMP(val, 0, (int)UCHAR_MAX);
- }
-
- return value;
-}
-
uchar Universe::applyGM(int channel, uchar value)
{
if ((m_grandMaster->channelMode() == GrandMaster::Intensity && m_channelsMask->at(channel) & Intensity) ||
@@ -546,8 +526,6 @@ void Universe::updatePostGMValue(int channel)
{
uchar value = preGMValue(channel);
- value = applyRelative(channel, value);
-
if (value != 0)
value = applyGM(channel, value);
@@ -661,21 +639,21 @@ bool Universe::setFeedbackPatch(QLCIOPlugin *plugin, quint32 output)
{
delete m_fbPatch;
m_fbPatch = NULL;
- emit hasFeedbacksChanged();
+ emit hasFeedbackChanged();
return true;
}
}
if (m_fbPatch != NULL)
{
bool result = m_fbPatch->set(plugin, output);
- emit hasFeedbacksChanged();
+ emit hasFeedbackChanged();
return result;
}
return false;
}
-bool Universe::hasFeedbacks() const
+bool Universe::hasFeedback() const
{
return m_fbPatch != NULL ? true : false;
}
@@ -925,97 +903,143 @@ void Universe::updateIntensityChannelsRanges()
* Writing
****************************************************************************/
-bool Universe::write(int channel, uchar value, bool forceLTP)
+bool Universe::write(int address, uchar value, bool forceLTP)
{
- Q_ASSERT(channel < UNIVERSE_SIZE);
-
- //qDebug() << "Universe write channel" << channel << ", value:" << value;
+ Q_ASSERT(address < UNIVERSE_SIZE);
- if (channel >= m_usedChannels)
- m_usedChannels = channel + 1;
+ //qDebug() << "[Universe]" << id() << ": write channel" << address << ", value:" << value;
- if ((m_channelsMask->at(channel) & HTP) == false)
- (*m_blackoutValues)[channel] = char(value);
+ if (address >= m_usedChannels)
+ m_usedChannels = address + 1;
- if (forceLTP == false && (m_channelsMask->at(channel) & HTP) && value < (uchar)m_preGMValues->at(channel))
+ if (m_channelsMask->at(address) & HTP)
{
- qDebug() << "[Universe] HTP check not passed" << channel << value;
- return false;
+ if (forceLTP == false && value < (uchar)m_preGMValues->at(address))
+ {
+ qDebug() << "[Universe] HTP check not passed" << address << value;
+ return false;
+ }
+ }
+ else
+ {
+ // preserve non HTP channels for blackout
+ (*m_blackoutValues)[address] = char(value);
}
- (*m_preGMValues)[channel] = char(value);
+ (*m_preGMValues)[address] = char(value);
- updatePostGMValue(channel);
+ updatePostGMValue(address);
return true;
}
-bool Universe::writeRelative(int channel, uchar value)
+bool Universe::writeMultiple(int address, quint32 value, int channelCount)
{
- Q_ASSERT(channel < UNIVERSE_SIZE);
+ for (int i = 0; i < channelCount; i++)
+ {
+ //qDebug() << "[Universe]" << id() << ": write channel" << (address + i) << ", value:" << QString::number(((uchar *)&value)[channelCount - 1 - i]);
- //qDebug() << "Write relative channel" << channel << value;
+ // preserve non HTP channels for blackout
+ if ((m_channelsMask->at(address + i) & HTP) == 0)
+ (*m_blackoutValues)[address + i] = ((uchar *)&value)[channelCount - 1 - i];
- if (channel >= m_usedChannels)
- m_usedChannels = channel + 1;
+ (*m_preGMValues)[address + i] = ((uchar *)&value)[channelCount - 1 - i];
- if (value == RELATIVE_ZERO)
- return true;
+ updatePostGMValue(address + i);
+ }
+
+ return true;
+}
+
+bool Universe::writeRelative(int address, quint32 value, int channelCount)
+{
+ Q_ASSERT(address < UNIVERSE_SIZE);
- m_relativeValues[channel] += value - RELATIVE_ZERO;
+ //qDebug() << "Write relative channel" << address << "value" << value;
- updatePostGMValue(channel);
+ if (address + channelCount >= m_usedChannels)
+ m_usedChannels = address + channelCount;
+
+ if (channelCount == 1)
+ {
+ short newVal = uchar((*m_preGMValues)[address]);
+ newVal += short(value) - RELATIVE_ZERO_8BIT;
+ (*m_preGMValues)[address] = char(CLAMP(newVal, 0, UCHAR_MAX));
+ (*m_blackoutValues)[address] = char(CLAMP(newVal, 0, UCHAR_MAX));
+ updatePostGMValue(address);
+ }
+ else
+ {
+ quint32 currentValue = 0;
+ for (int i = 0; i < channelCount; i++)
+ currentValue = (currentValue << 8) + uchar(m_preGMValues->at(address + i));
+
+ currentValue += (value - RELATIVE_ZERO_16BIT);
+
+ for (int i = 0; i < channelCount; i++)
+ {
+ (*m_preGMValues)[address + i] = ((uchar *)¤tValue)[channelCount - 1 - i];
+ (*m_blackoutValues)[address + i] = ((uchar *)¤tValue)[channelCount - 1 - i];
+ updatePostGMValue(address + i);
+ }
+ }
return true;
}
-bool Universe::writeBlended(int channel, uchar value, Universe::BlendMode blend)
+bool Universe::writeBlended(int address, quint32 value, int channelCount, Universe::BlendMode blend)
{
- if (channel >= m_usedChannels)
- m_usedChannels = channel + 1;
+ if (address + channelCount >= m_usedChannels)
+ m_usedChannels = address + channelCount;
+
+ quint32 currentValue = 0;
+ for (int i = 0; i < channelCount; i++)
+ currentValue = (currentValue << 8) + uchar(m_preGMValues->at(address + i));
switch (blend)
{
case NormalBlend:
- return write(channel, value);
-
+ {
+ if ((m_channelsMask->at(address) & HTP) && value < currentValue)
+ {
+ qDebug() << "[Universe] HTP check not passed" << address << value;
+ return false;
+ }
+ }
+ break;
case MaskBlend:
{
if (value)
{
- float currValue = (float)uchar(m_preGMValues->at(channel));
- if (currValue)
- value = currValue * ((float)value / 255.0);
+ qDebug() << "Current value" << currentValue << "value" << value;
+ if (currentValue)
+ value = float(currentValue) * (float(value) / pow(255.0, channelCount));
else
value = 0;
}
- (*m_preGMValues)[channel] = char(value);
}
break;
case AdditiveBlend:
{
- uchar currVal = uchar(m_preGMValues->at(channel));
//qDebug() << "Universe write additive channel" << channel << ", value:" << currVal << "+" << value;
- value = qMin(int(currVal) + value, 255);
- (*m_preGMValues)[channel] = char(value);
+ value = fmin(float(currentValue + value), pow(255.0, channelCount));
}
break;
case SubtractiveBlend:
{
- uchar currVal = uchar(m_preGMValues->at(channel));
- if (value >= currVal)
+ if (value >= currentValue)
value = 0;
else
- value = currVal - value;
- (*m_preGMValues)[channel] = char(value);
+ value = currentValue - value;
}
break;
default:
qDebug() << "[Universe] Blend mode not handled. Implement me!" << blend;
+ return false;
break;
}
- updatePostGMValue(channel);
+ writeMultiple(address, value, channelCount);
return true;
}
diff --git a/engine/src/universe.h b/engine/src/universe.h
index 6c4ad00c7a..a8571739ab 100644
--- a/engine/src/universe.h
+++ b/engine/src/universe.h
@@ -74,7 +74,7 @@ class Universe: public QThread
Q_PROPERTY(bool passthrough READ passthrough WRITE setPassthrough NOTIFY passthroughChanged)
Q_PROPERTY(InputPatch *inputPatch READ inputPatch NOTIFY inputPatchChanged)
Q_PROPERTY(int outputPatchesCount READ outputPatchesCount NOTIFY outputPatchesCountChanged)
- Q_PROPERTY(bool hasFeedbacks READ hasFeedbacks NOTIFY hasFeedbacksChanged)
+ Q_PROPERTY(bool hasFeedback READ hasFeedback NOTIFY hasFeedbackChanged)
public:
/** Construct a new Universe */
@@ -171,7 +171,6 @@ protected slots:
*/
uchar applyGM(int channel, uchar value);
- uchar applyRelative(int channel, uchar value);
uchar applyModifiers(int channel, uchar value);
void updatePostGMValue(int channel);
@@ -210,7 +209,7 @@ protected slots:
bool setFeedbackPatch(QLCIOPlugin *plugin, quint32 output);
/** Flag that indicates if this Universe has a patched feedback line */
- bool hasFeedbacks() const;
+ bool hasFeedback() const;
/**
* Get the reference to the input plugin associated to this universe.
@@ -259,7 +258,7 @@ protected slots:
void outputPatchesCountChanged();
/** Notify the listeners that a feedback line has been patched/unpatched */
- void hasFeedbacksChanged();
+ void hasFeedbackChanged();
private:
/** Reference to the input patch associated to this universe. */
@@ -437,9 +436,6 @@ public slots:
/** Return a list with intensity channels and their values */
QHash intensityChannels();
- /** Set all channel relative values to zero */
- void zeroRelativeValues();
-
protected:
void applyPassthroughValues(int address, int range);
@@ -488,8 +484,6 @@ public slots:
/** Array of values from input line, when passtrhough is enabled */
QScopedPointer m_passthroughValues;
- QVector m_relativeValues;
-
/* impl speedup */
void updateIntensityChannelsRanges();
@@ -518,36 +512,48 @@ public slots:
* Write a value to a DMX channel, taking Grand Master and HTP into
* account, if applicable.
*
- * @param channel The channel number to write to
+ * @param address The DMX start address to write to
* @param value The value to write
*
* @return true if successful, otherwise false
*/
- bool write(int channel, uchar value, bool forceLTP = false);
+ bool write(int address, uchar value, bool forceLTP = false);
+
+ /**
+ * Write a value representing one or multiple channels
+ *
+ * @param address The DMX start address to write to
+ * @param value the DMX value(s) to set
+ * @param channelCount number of channels that value represents
+ * @return always true
+ */
+ bool writeMultiple(int address, quint32 value, int channelCount);
/**
* Write a relative value to a DMX channel, taking Grand Master and HTP into
* account, if applicable.
*
- * @param channel The channel number to write to
+ * @param address The DMX start address to write to
* @param value The value to write
+ * @param channelCount number of channels that value represents
*
* @return true if successful, otherwise false
*/
- bool writeRelative(int channel, uchar value);
+ bool writeRelative(int address, quint32 value, int channelCount);
/**
* Write DMX values with the given blend mode.
* If blend == NormalBlend the generic write method is called
* and all the HTP/LTP checks are performed
*
- * @param channel The channel number to write to
+ * @param address The DMX start address to write to
* @param value The value to write
+ * @param channelCount The number of channels that value represents
* @param blend The blend mode to be used on $value
*
* @return true if successful, otherwise false
*/
- bool writeBlended(int channel, uchar value, BlendMode blend = NormalBlend);
+ bool writeBlended(int address, quint32 value, int channelCount, BlendMode blend);
/*********************************************************************
* Load & Save
diff --git a/engine/test/efxfixture/efxfixture_test.cpp b/engine/test/efxfixture/efxfixture_test.cpp
index ee3d638f1b..2da38bb912 100644
--- a/engine/test/efxfixture/efxfixture_test.cpp
+++ b/engine/test/efxfixture/efxfixture_test.cpp
@@ -454,6 +454,7 @@ void EFXFixture_Test::setPoint8bit()
Universe *universe = ua[0];
QSharedPointer fader = universe->requestFader();
+ ef.start(fader);
ef.setPointPanTilt(ua, fader, 5.4, 1.5); // PMSB: 5, PLSB: 0.4, TMSB: 1 (102), TLSB: 0.5(127)
QCOMPARE(fader->channels().count(), 2);
universe->processFaders();
@@ -474,6 +475,7 @@ void EFXFixture_Test::setPoint16bit()
Universe *universe = ua[0];
QSharedPointer fader = universe->requestFader();
+ ef.start(fader);
ef.setPointPanTilt(ua, fader, 5.4, 1.5); // PMSB: 5, PLSB: 0.4, TMSB: 1 (102), TLSB: 0.5(127)
QCOMPARE(fader->channels().count(), 4);
universe->processFaders();
@@ -493,6 +495,7 @@ void EFXFixture_Test::setPointPanOnly()
Universe *universe = ua[0];
QSharedPointer fader = universe->requestFader();
+ ef.start(fader);
ef.setPointPanTilt(ua, fader, 5.4, 1.5); // PMSB: 5, PLSB: 0.4, TMSB: 1 (102), TLSB: 0.5(127)
QCOMPARE(fader->channels().count(), 1);
universe->processFaders();
@@ -512,6 +515,7 @@ void EFXFixture_Test::setPointLedBar()
Universe *universe = ua[0];
QSharedPointer fader = universe->requestFader();
+ ef.start(fader);
ef.setPointPanTilt(ua, fader, 5.4, 1.5); // PMSB: 5, PLSB: 0.4, TMSB: 1 (102), TLSB: 0.5(127)
QCOMPARE(fader->channels().count(), 1);
universe->processFaders();
diff --git a/engine/test/fadechannel/fadechannel_test.cpp b/engine/test/fadechannel/fadechannel_test.cpp
index fcef4ef4f4..b52352b7c3 100644
--- a/engine/test/fadechannel/fadechannel_test.cpp
+++ b/engine/test/fadechannel/fadechannel_test.cpp
@@ -41,15 +41,14 @@ void FadeChannel_Test::address()
fxi->setChannels(5);
doc.addFixture(fxi);
- FadeChannel fc;
- fc.setChannel(&doc, 2);
+ FadeChannel fc(&doc, Fixture::invalidId(), 2);
QCOMPARE(fc.address(), quint32(2));
- fc.setFixture(&doc, fxi->id());
- QCOMPARE(fc.address(), quint32(402));
+ FadeChannel fc1(&doc, fxi->id(), 2);
+ QCOMPARE(fc1.address(), quint32(402));
- fc.setFixture(&doc, 12345);
- QCOMPARE(fc.address(), quint32(2));
+ FadeChannel fc2(&doc, 12345, QLCChannel::invalid());
+ QCOMPARE(fc2.address(), QLCChannel::invalid());
}
void FadeChannel_Test::addressInUniverse()
@@ -60,45 +59,34 @@ void FadeChannel_Test::addressInUniverse()
fxi->setChannels(5);
doc.addFixture(fxi);
- FadeChannel fc;
- fc.setChannel(&doc, 2);
+ FadeChannel fc(&doc, Fixture::invalidId(), 2);
QCOMPARE(fc.addressInUniverse(), quint32(2));
- fc.setFixture(&doc, fxi->id());
- QCOMPARE(fc.addressInUniverse(), quint32(2));
+ FadeChannel fc1(&doc, fxi->id(), QLCChannel::invalid());
+ QCOMPARE(fc1.addressInUniverse(), QLCChannel::invalid());
- fc.setFixture(&doc, 12345);
- QCOMPARE(fc.addressInUniverse(), quint32(2));
+ FadeChannel fc2(&doc, 12345, QLCChannel::invalid());
+ QCOMPARE(fc2.addressInUniverse(), QLCChannel::invalid());
}
void FadeChannel_Test::comparison()
{
Doc doc(this);
- FadeChannel ch1;
- ch1.setFixture(&doc, 0);
- ch1.setChannel(&doc, 0);
-
- FadeChannel ch2;
- ch2.setFixture(&doc, 1);
- ch2.setChannel(&doc, 0);
- QVERIFY((ch1 == ch2) == false);
-
- ch1.setFixture(&doc, 1);
- QVERIFY((ch1 == ch2) == true);
-
- ch1.setChannel(&doc, 1);
+ FadeChannel ch1(&doc, 0, 0);
+ FadeChannel ch2(&doc, 1, 0);
+ FadeChannel ch3(&doc, 0, 0);
QVERIFY((ch1 == ch2) == false);
+ QVERIFY((ch1 == ch3) == true);
}
void FadeChannel_Test::type()
{
Doc doc(this);
- FadeChannel fc;
+ FadeChannel fc(&doc, Fixture::invalidId(), 2);
// Only a channel given, no fixture at the address -> intensity
- fc.setChannel(&doc, 2);
QCOMPARE(fc.flags() & FadeChannel::HTP, (int)FadeChannel::HTP);
QCOMPARE(fc.flags() & FadeChannel::Intensity, (int)FadeChannel::Intensity);
QCOMPARE(fc.flags() & FadeChannel::CanFade, (int)FadeChannel::CanFade);
@@ -109,10 +97,10 @@ void FadeChannel_Test::type()
doc.addFixture(fxi);
// Fixture and channel given, fixture is a dimmer -> intensity
- fc.setFixture(&doc, fxi->id());
- QCOMPARE(fc.flags() & FadeChannel::HTP, (int)FadeChannel::HTP);
- QCOMPARE(fc.flags() & FadeChannel::Intensity, (int)FadeChannel::Intensity);
- QCOMPARE(fc.flags() & FadeChannel::CanFade, (int)FadeChannel::CanFade);
+ FadeChannel fc1(&doc, fxi->id(), 2);
+ QCOMPARE(fc1.flags() & FadeChannel::HTP, (int)FadeChannel::HTP);
+ QCOMPARE(fc1.flags() & FadeChannel::Intensity, (int)FadeChannel::Intensity);
+ QCOMPARE(fc1.flags() & FadeChannel::CanFade, (int)FadeChannel::CanFade);
QDir dir(INTERNAL_FIXTUREDIR);
dir.setFilter(QDir::Files);
@@ -131,44 +119,39 @@ void FadeChannel_Test::type()
doc.addFixture(fxi);
// Fixture and channel given, but channel is beyond fixture's channels -> intensity
- fc.setFixture(&doc, fxi->id());
- fc.setChannel(&doc, 50);
- QCOMPARE(fc.flags() & FadeChannel::HTP, (int)FadeChannel::HTP);
- QCOMPARE(fc.flags() & FadeChannel::Intensity, (int)FadeChannel::Intensity);
- QCOMPARE(fc.flags() & FadeChannel::CanFade, (int)FadeChannel::CanFade);
+ FadeChannel fc2(&doc, fxi->id(), 50);
+ QCOMPARE(fc2.flags() & FadeChannel::HTP, (int)FadeChannel::HTP);
+ QCOMPARE(fc2.flags() & FadeChannel::Intensity, (int)FadeChannel::Intensity);
+ QCOMPARE(fc2.flags() & FadeChannel::CanFade, (int)FadeChannel::CanFade);
// Only a channel given, no fixture given but a fixture occupies the address.
// Check that reverse address -> fixture lookup works.
- fc.setFixture(&doc, Fixture::invalidId());
- fc.setChannel(&doc, 2);
- QCOMPARE(fc.flags() & FadeChannel::LTP, (int)FadeChannel::LTP);
- QCOMPARE(fc.flags() & FadeChannel::CanFade, (int)FadeChannel::CanFade);
+ FadeChannel fc3(&doc, Fixture::invalidId(), 2);
+ QCOMPARE(fc3.flags() & FadeChannel::LTP, (int)FadeChannel::LTP);
+ QCOMPARE(fc3.flags() & FadeChannel::CanFade, (int)FadeChannel::CanFade);
// Fixture and channel given, but fixture doesn't exist -> intensity
- fc.setFixture(&doc, 12345);
- fc.setChannel(&doc, 2);
- QCOMPARE(fc.flags() & FadeChannel::HTP, (int)FadeChannel::HTP);
- QCOMPARE(fc.flags() & FadeChannel::Intensity, (int)FadeChannel::Intensity);
- QCOMPARE(fc.flags() & FadeChannel::CanFade, (int)FadeChannel::CanFade);
+ FadeChannel fc4(&doc, 12345, 2);
+ QCOMPARE(fc4.flags() & FadeChannel::HTP, (int)FadeChannel::HTP);
+ QCOMPARE(fc4.flags() & FadeChannel::Intensity, (int)FadeChannel::Intensity);
+ QCOMPARE(fc4.flags() & FadeChannel::CanFade, (int)FadeChannel::CanFade);
// channel 3 cannot fade
fxi->setChannelCanFade(3, false);
- fc.setFixture(&doc, fxi->id());
- fc.setChannel(&doc, 3);
- QCOMPARE(fc.flags() & FadeChannel::LTP, (int)FadeChannel::LTP);
- QCOMPARE(fc.flags() & FadeChannel::CanFade, 0);
+ FadeChannel fc5(&doc, fxi->id(), 3);
+ QCOMPARE(fc5.flags() & FadeChannel::LTP, (int)FadeChannel::LTP);
+ QCOMPARE(fc5.flags() & FadeChannel::CanFade, 0);
// force channel 0 (Pan) to be HTP
QList forced;
forced << 0;
fxi->setForcedHTPChannels(forced);
- fc.setFixture(&doc, fxi->id());
- fc.setChannel(&doc, 0);
- QCOMPARE(fc.flags() & FadeChannel::HTP, (int)FadeChannel::HTP);
- QCOMPARE(fc.flags() & FadeChannel::LTP, 0);
- QCOMPARE(fc.flags() & FadeChannel::CanFade, (int)FadeChannel::CanFade);
+ FadeChannel fc6(&doc, fxi->id(), 0);
+ QCOMPARE(fc6.flags() & FadeChannel::HTP, (int)FadeChannel::HTP);
+ QCOMPARE(fc6.flags() & FadeChannel::LTP, 0);
+ QCOMPARE(fc6.flags() & FadeChannel::CanFade, (int)FadeChannel::CanFade);
// add another generic dimmer
fxi = new Fixture(&doc);
@@ -180,11 +163,10 @@ void FadeChannel_Test::type()
fxi->setForcedLTPChannels(forced);
doc.addFixture(fxi);
- fc.setFixture(&doc, fxi->id());
- fc.setChannel(&doc, 2);
- QCOMPARE(fc.flags() & FadeChannel::LTP, (int)FadeChannel::LTP);
- QCOMPARE(fc.flags() & FadeChannel::HTP, 0);
- QCOMPARE(fc.flags() & FadeChannel::CanFade, (int)FadeChannel::CanFade);
+ FadeChannel fc7(&doc, fxi->id(), 2);
+ QCOMPARE(fc7.flags() & FadeChannel::LTP, (int)FadeChannel::LTP);
+ QCOMPARE(fc7.flags() & FadeChannel::HTP, 0);
+ QCOMPARE(fc7.flags() & FadeChannel::CanFade, (int)FadeChannel::CanFade);
// unset a flag
fc.removeFlag(FadeChannel::CanFade);
@@ -371,24 +353,24 @@ void FadeChannel_Test::calculateCurrent()
fch.setTarget(101);
fch.setReady(false);
QCOMPARE(fch.calculateCurrent(200, 0), uchar(245));
- QCOMPARE(fch.calculateCurrent(200, 1), uchar(244));
- QCOMPARE(fch.calculateCurrent(200, 2), uchar(243));
- QCOMPARE(fch.calculateCurrent(200, 3), uchar(242));
- QCOMPARE(fch.calculateCurrent(200, 4), uchar(242));
- QCOMPARE(fch.calculateCurrent(200, 5), uchar(241));
- QCOMPARE(fch.calculateCurrent(200, 6), uchar(240));
- QCOMPARE(fch.calculateCurrent(200, 7), uchar(239));
- QCOMPARE(fch.calculateCurrent(200, 8), uchar(239));
- QCOMPARE(fch.calculateCurrent(200, 9), uchar(238));
- QCOMPARE(fch.calculateCurrent(200, 10), uchar(237));
- QCOMPARE(fch.calculateCurrent(200, 11), uchar(237));
+ QCOMPARE(fch.calculateCurrent(200, 1), uchar(245));
+ QCOMPARE(fch.calculateCurrent(200, 2), uchar(244));
+ QCOMPARE(fch.calculateCurrent(200, 3), uchar(243));
+ QCOMPARE(fch.calculateCurrent(200, 4), uchar(243));
+ QCOMPARE(fch.calculateCurrent(200, 5), uchar(242));
+ QCOMPARE(fch.calculateCurrent(200, 6), uchar(241));
+ QCOMPARE(fch.calculateCurrent(200, 7), uchar(240));
+ QCOMPARE(fch.calculateCurrent(200, 8), uchar(240));
+ QCOMPARE(fch.calculateCurrent(200, 9), uchar(239));
+ QCOMPARE(fch.calculateCurrent(200, 10), uchar(238));
+ QCOMPARE(fch.calculateCurrent(200, 11), uchar(238));
// Skip...
QCOMPARE(fch.calculateCurrent(200, 100), uchar(173));
- QCOMPARE(fch.calculateCurrent(200, 101), uchar(172));
- QCOMPARE(fch.calculateCurrent(200, 102), uchar(171));
+ QCOMPARE(fch.calculateCurrent(200, 101), uchar(173));
+ QCOMPARE(fch.calculateCurrent(200, 102), uchar(172));
// Skip...
- QCOMPARE(fch.calculateCurrent(200, 198), uchar(102));
- QCOMPARE(fch.calculateCurrent(200, 199), uchar(101));
+ QCOMPARE(fch.calculateCurrent(200, 198), uchar(103));
+ QCOMPARE(fch.calculateCurrent(200, 199), uchar(102));
QCOMPARE(fch.calculateCurrent(200, 200), uchar(101));
}
diff --git a/engine/test/genericfader/genericfader_test.cpp b/engine/test/genericfader/genericfader_test.cpp
index a80c524e2d..3a26c1b214 100644
--- a/engine/test/genericfader/genericfader_test.cpp
+++ b/engine/test/genericfader/genericfader_test.cpp
@@ -67,12 +67,10 @@ void GenericFader_Test::addRemove()
QList ua = m_doc->inputOutputMap()->universes();
QSharedPointer fader = QSharedPointer(new GenericFader());
- FadeChannel fc;
- fc.setFixture(m_doc, 0);
- fc.setChannel(m_doc, 0);
-
- FadeChannel wrong;
- fc.setFixture(m_doc, 0);
+ FadeChannel fc(m_doc, 0, 0);
+ FadeChannel fc1(m_doc, 0, 1);
+ FadeChannel fc2(m_doc, 0, 2);
+ FadeChannel wrong(m_doc, 0, QLCChannel::invalid());
quint32 chHash = GenericFader::channelHash(fc.fixture(), fc.channel());
QCOMPARE(fader->m_channels.count(), 0);
@@ -86,22 +84,19 @@ void GenericFader_Test::addRemove()
QVERIFY(fader->m_channels.contains(chHash) == true);
QCOMPARE(fader->m_channels.count(), 1);
- FadeChannel *fc1 = fader->getChannelFader(m_doc, ua[0], 0, 0);
- fader->remove(fc1);
+ FadeChannel *fc3 = fader->getChannelFader(m_doc, ua[0], 0, 0);
+ fader->remove(fc3);
QVERIFY(fader->m_channels.contains(chHash) == false);
QCOMPARE(fader->m_channels.count(), 0);
- fc.setChannel(m_doc, 0);
fader->add(fc);
QVERIFY(fader->m_channels.contains(chHash) == true);
- fc.setChannel(m_doc, 1);
- fader->add(fc);
+ fader->add(fc1);
chHash = GenericFader::channelHash(fc.fixture(), fc.channel());
QVERIFY(fader->m_channels.contains(chHash) == true);
- fc.setChannel(m_doc, 2);
- fader->add(fc);
+ fader->add(fc2);
chHash = GenericFader::channelHash(fc.fixture(), fc.channel());
QVERIFY(fader->m_channels.contains(chHash) == true);
QCOMPARE(fader->m_channels.count(), 3);
@@ -109,8 +104,6 @@ void GenericFader_Test::addRemove()
fader->removeAll();
QCOMPARE(fader->m_channels.count(), 0);
- fc.setFixture(m_doc, 0);
- fc.setChannel(m_doc, 0);
fc.setTarget(127);
fader->add(fc);
chHash = GenericFader::channelHash(fc.fixture(), fc.channel());
@@ -133,9 +126,7 @@ void GenericFader_Test::writeZeroFade()
QList ua = m_doc->inputOutputMap()->universes();
QSharedPointer fader = ua[0]->requestFader();
- FadeChannel fc;
- fc.setFixture(m_doc, 0);
- fc.setChannel(m_doc, 5);
+ FadeChannel fc(m_doc, 0, 5);
fc.setStart(0);
fc.setTarget(255);
fc.setFadeTime(0);
@@ -151,9 +142,7 @@ void GenericFader_Test::writeLoop()
QList ua = m_doc->inputOutputMap()->universes();
QSharedPointer fader = ua[0]->requestFader();
- FadeChannel fc;
- fc.setFixture(m_doc, 0);
- fc.setChannel(m_doc, 5);
+ FadeChannel fc(m_doc, 0, 5);
fc.setStart(0);
fc.setTarget(250);
fc.setFadeTime(1000);
@@ -178,19 +167,20 @@ void GenericFader_Test::adjustIntensity()
QList ua = m_doc->inputOutputMap()->universes();
QSharedPointer fader = ua[0]->requestFader();
- FadeChannel fc;
+ FadeChannel fc(m_doc, 0, 5);
+ FadeChannel fc1(m_doc, 0, 0);
// HTP channel
- fc.setFixture(m_doc, 0);
- fc.setChannel(m_doc, 5);
fc.setStart(0);
fc.setTarget(250);
fc.setFadeTime(1000);
fader->add(fc);
// LTP channel
- fc.setChannel(m_doc, 0);
- fader->add(fc);
+ fc1.setStart(0);
+ fc1.setTarget(250);
+ fc1.setFadeTime(1000);
+ fader->add(fc1);
qreal intensity = 0.5;
fader->adjustIntensity(intensity);
diff --git a/engine/test/universe/universe_test.cpp b/engine/test/universe/universe_test.cpp
index 0702f3ed37..de42bfbdb9 100644
--- a/engine/test/universe/universe_test.cpp
+++ b/engine/test/universe/universe_test.cpp
@@ -112,26 +112,26 @@ void Universe_Test::blendModes()
QCOMPARE(quint8(m_uni->postGMValues()->at(11)), quint8(0));
/* check masking on 0 remains 0 */
- QVERIFY(m_uni->writeBlended(11, 128, Universe::MaskBlend) == true);
+ QVERIFY(m_uni->writeBlended(11, 128, 1, Universe::MaskBlend) == true);
QCOMPARE(quint8(m_uni->postGMValues()->at(11)), quint8(0));
/* check 180 masked on 128 gets halved */
- QVERIFY(m_uni->writeBlended(4, 180, Universe::MaskBlend) == true);
+ QVERIFY(m_uni->writeBlended(4, 180, 1, Universe::MaskBlend) == true);
QCOMPARE(quint8(m_uni->postGMValues()->at(4)), quint8(90));
/* chek adding 50 to 100 is actually 150 */
- QVERIFY(m_uni->writeBlended(9, 50, Universe::AdditiveBlend) == true);
+ QVERIFY(m_uni->writeBlended(9, 50, 1, Universe::AdditiveBlend) == true);
QCOMPARE(quint8(m_uni->postGMValues()->at(9)), quint8(150));
/* chek subtracting 55 to 255 is actually 200 */
- QVERIFY(m_uni->writeBlended(0, 55, Universe::SubtractiveBlend) == true);
+ QVERIFY(m_uni->writeBlended(0, 55, 1, Universe::SubtractiveBlend) == true);
QCOMPARE(quint8(m_uni->postGMValues()->at(0)), quint8(200));
- QVERIFY(m_uni->writeBlended(0, 255, Universe::SubtractiveBlend) == true);
+ QVERIFY(m_uni->writeBlended(0, 255, 1, Universe::SubtractiveBlend) == true);
QCOMPARE(quint8(m_uni->postGMValues()->at(0)), quint8(0));
/* check an unknown blend mode */
- QVERIFY(m_uni->writeBlended(9, 255, Universe::BlendMode(42)) == true);
+ QVERIFY(m_uni->writeBlended(9, 255, 1, Universe::BlendMode(42)) == false);
QCOMPARE(quint8(m_uni->postGMValues()->at(9)), quint8(150));
}
@@ -338,57 +338,46 @@ void Universe_Test::write()
void Universe_Test::writeRelative()
{
// 127 == 0
- QVERIFY(m_uni->writeRelative(9, 127) == true);
- QCOMPARE(m_uni->m_relativeValues[9], short(0));
- QCOMPARE(m_uni->m_relativeValues[4], short(0));
- QCOMPARE(m_uni->m_relativeValues[0], short(0));
+ QVERIFY(m_uni->writeRelative(9, 127, 1) == true);
QCOMPARE(quint8(m_uni->postGMValues()->at(9)), quint8(0));
QCOMPARE(quint8(m_uni->postGMValues()->at(4)), quint8(0));
QCOMPARE(quint8(m_uni->postGMValues()->at(0)), quint8(0));
// 255 == +128
- QVERIFY(m_uni->writeRelative(9, 255) == true);
- QCOMPARE(m_uni->m_relativeValues[9], short(128)); // 0 + 128
- QCOMPARE(m_uni->m_relativeValues[4], short(0));
- QCOMPARE(m_uni->m_relativeValues[0], short(0));
+ QVERIFY(m_uni->writeRelative(9, 255, 1) == true);
QCOMPARE(quint8(m_uni->postGMValues()->at(9)), quint8(128));
QCOMPARE(quint8(m_uni->postGMValues()->at(4)), quint8(0));
QCOMPARE(quint8(m_uni->postGMValues()->at(0)), quint8(0));
// 0 == -127
- QVERIFY(m_uni->writeRelative(9, 0) == true);
- QCOMPARE(m_uni->m_relativeValues[9], short(1)); // 128 - 127
- QCOMPARE(m_uni->m_relativeValues[4], short(0));
- QCOMPARE(m_uni->m_relativeValues[0], short(0));
+ QVERIFY(m_uni->writeRelative(9, 0, 1) == true);
QCOMPARE(quint8(m_uni->postGMValues()->at(9)), quint8(1));
QCOMPARE(quint8(m_uni->postGMValues()->at(4)), quint8(0));
QCOMPARE(quint8(m_uni->postGMValues()->at(0)), quint8(0));
m_uni->reset();
- QCOMPARE(m_uni->m_relativeValues[9], short(0));
QVERIFY(m_uni->write(9, 85) == true);
QCOMPARE(quint8(m_uni->postGMValues()->at(9)), quint8(85));
- QVERIFY(m_uni->writeRelative(9, 117) == true);
- QCOMPARE(m_uni->m_relativeValues[9], short(-10));
+ QVERIFY(m_uni->writeRelative(9, 117, 1) == true);
QCOMPARE(quint8(m_uni->postGMValues()->at(9)), quint8(75));
QVERIFY(m_uni->write(9, 65) == true);
- QCOMPARE(quint8(m_uni->postGMValues()->at(9)), quint8(55));
+ QCOMPARE(quint8(m_uni->postGMValues()->at(9)), quint8(65));
m_uni->reset();
QVERIFY(m_uni->write(9, 255) == true);
QCOMPARE(quint8(m_uni->postGMValues()->at(9)), quint8(255));
- QVERIFY(m_uni->writeRelative(9, 255) == true);
+ QVERIFY(m_uni->writeRelative(9, 255, 1) == true);
QCOMPARE(quint8(m_uni->postGMValues()->at(9)), quint8(255));
m_uni->reset();
QVERIFY(m_uni->write(9, 0) == true);
QCOMPARE(quint8(m_uni->postGMValues()->at(9)), quint8(0));
- QVERIFY(m_uni->writeRelative(9, 0) == true);
+ QVERIFY(m_uni->writeRelative(9, 0, 1) == true);
QCOMPARE(quint8(m_uni->postGMValues()->at(9)), quint8(0));
}
diff --git a/fixtureeditor/CMakeLists.txt b/fixtureeditor/CMakeLists.txt
index 94261267a3..489ee519a9 100644
--- a/fixtureeditor/CMakeLists.txt
+++ b/fixtureeditor/CMakeLists.txt
@@ -547,6 +547,8 @@ set(qlcui_resource_files
"../ui/src/../../resources/icons/png/laser.png"
"../ui/src/../../resources/icons/png/ledbar_beams.png"
"../ui/src/../../resources/icons/png/ledbar_pixels.png"
+ "../ui/src/../../resources/icons/png/lightning.png"
+ "../ui/src/../../resources/icons/png/lightning_off.png"
"../ui/src/../../resources/icons/png/liveedit.png"
"../ui/src/../../resources/icons/png/liveedit_vc.png"
"../ui/src/../../resources/icons/png/lock.png"
diff --git a/fixtureeditor/app.cpp b/fixtureeditor/app.cpp
index b34ccab0bf..8e79211004 100644
--- a/fixtureeditor/app.cpp
+++ b/fixtureeditor/app.cpp
@@ -106,7 +106,7 @@ App::~App()
QString App::longName()
{
- return QString("%1 - %2").arg(APPNAME).arg(FXEDNAME);
+ return QString("%1 - %2").arg(APPNAME, FXEDNAME);
}
QString App::version()
@@ -120,9 +120,9 @@ void App::loadFixtureDefinition(const QString& path)
/* Attempt to create a fixture definition from the selected file */
QString error(tr("Unrecognized file extension: %1").arg(path));
- if (path.toLower().endsWith(KExtFixture) == true)
+ if (path.endsWith(KExtFixture, Qt::CaseInsensitive) == true)
fixtureDef = loadQXF(path, error);
- else if (path.toLower().endsWith(KExtAvolitesFixture) == true)
+ else if (path.endsWith(KExtAvolitesFixture, Qt::CaseInsensitive) == true)
fixtureDef = loadD4(path, error);
else
fixtureDef = NULL;
@@ -138,6 +138,10 @@ void App::loadFixtureDefinition(const QString& path)
sub->setAttribute(Qt::WA_DeleteOnClose);
qobject_cast (centralWidget())->addSubWindow(sub);
+ // check if sub-window is outside main area
+ if (sub->x() >= this->width() || sub->y() >= this->height())
+ sub->setGeometry(0, 0, sub->width(), sub->height());
+
editor->show();
sub->show();
}
@@ -367,9 +371,7 @@ void App::slotFileOpen()
QVariant var = settings.value(SETTINGS_OPENDIALOGSTATE);
if (var.isValid() == true)
- {
dialog.restoreState(var.toByteArray());
- }
dialog.setDirectory(m_workingDirectory);
diff --git a/fixtureeditor/main.cpp b/fixtureeditor/main.cpp
index 38740f0026..96393292d6 100644
--- a/fixtureeditor/main.cpp
+++ b/fixtureeditor/main.cpp
@@ -90,33 +90,34 @@ void printUsage()
*
* @return true to continue with application launch; otherwise false
*/
-bool parseArgs(int argc, char **argv)
+bool parseArgs()
{
- for (int i = 1; i < argc; i++)
+ QStringListIterator it(QCoreApplication::arguments());
+ while (it.hasNext() == true)
{
- if (::strcmp(argv[i], "-v") == 0 ||
- ::strcmp(argv[i], "--version") == 0)
+ QString arg(it.next());
+
+ if (arg == "-v" || arg == "--version")
{
/* Don't print anything, since version is always
printed before anything else. Just make the app
exit by returning false. */
return false;
}
- else if (::strcmp(argv[i], "-h") == 0 ||
- ::strcmp(argv[i], "--help") == 0)
+ else if (arg == "-h" || arg == "--help")
{
printUsage();
return false;
}
- else if (::strcmp(argv[i], "-o") == 0 ||
- ::strcmp(argv[i], "--open") == 0)
+ else if (arg == "-o" || arg == "--open")
{
- FXEDArgs::fixture = QString(argv[++i]);
+ if (it.hasNext() == true)
+ FXEDArgs::fixture = it.next();
}
- else if (::strcmp(argv[i], "-l") == 0 ||
- ::strcmp(argv[i], "--locale") == 0)
+ else if (arg == "-l" || arg == "--locale")
{
- FXEDArgs::locale = QString(argv[++i]);
+ if (it.hasNext() == true)
+ FXEDArgs::locale = it.next();
}
}
@@ -168,7 +169,7 @@ int main(int argc, char** argv)
printVersion();
/* Parse command-line arguments */
- if (parseArgs(argc, argv) == false)
+ if (parseArgs() == false)
return 0;
/* Load translation for current locale */
diff --git a/hotplugmonitor/src/CMakeLists.txt b/hotplugmonitor/src/CMakeLists.txt
index 72ed66d7e6..957d313f63 100644
--- a/hotplugmonitor/src/CMakeLists.txt
+++ b/hotplugmonitor/src/CMakeLists.txt
@@ -1,6 +1,7 @@
add_library(hotplugmonitor
hotplugmonitor.cpp hotplugmonitor.h
)
+set_property(TARGET hotplugmonitor PROPERTY POSITION_INDEPENDENT_CODE ON)
target_link_libraries(hotplugmonitor PUBLIC
Qt${QT_MAJOR_VERSION}::Core
diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt
index 8a84ad7127..2c42d80c52 100644
--- a/launcher/CMakeLists.txt
+++ b/launcher/CMakeLists.txt
@@ -19,7 +19,7 @@ else()
qt5_add_translation(QM_FILES ${TS_FILES})
endif()
-add_executable(${module_name} WIN32 MACOSX_BUNDLE
+add_executable(${module_name} MACOSX_BUNDLE
launcher.cpp launcher.h
main.cpp
${QM_FILES}
@@ -36,7 +36,6 @@ target_link_libraries(${module_name} PRIVATE
Qt${QT_MAJOR_VERSION}::Widgets
)
-
# Resources:
set_source_files_properties("../resources/icons/png/qlcplus-fixtureeditor.png"
PROPERTIES QT_RESOURCE_ALIAS "qlcplus-fixtureeditor.png"
@@ -49,12 +48,20 @@ set(launcher_resource_files
"../resources/icons/png/qlcplus.png"
)
-qt_add_resources(${module_name} "launcher"
+if(QT_VERSION_MAJOR GREATER 5)
+ qt_add_resources(${module_name} "launcher"
PREFIX
"/"
+ BASE
+ "."
FILES
${launcher_resource_files}
)
+else()
+ qt5_add_resources($launcher.qrc)
+ target_sources(${module_name} PRIVATE
+ ${launcher_resource_files})
+endif()
install(TARGETS ${module_name}
DESTINATION ${INSTALLROOT}/${BINDIR}
diff --git a/main/main.cpp b/main/main.cpp
index b788e21355..7e505fe4dc 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -95,7 +95,7 @@ namespace QLCArgs
QRect closeButtonRect = QRect();
/** Debug output level */
- QtMsgType debugLevel = QtSystemMsg;
+ QtMsgType debugLevel = QtCriticalMsg;
/** Log to file flag */
bool logToFile = false;
diff --git a/platforms/android/.gitignore b/platforms/android/.gitignore
new file mode 100644
index 0000000000..f40fe0502b
--- /dev/null
+++ b/platforms/android/.gitignore
@@ -0,0 +1 @@
+assets
\ No newline at end of file
diff --git a/platforms/android/AndroidManifest.xml b/platforms/android/AndroidManifest.xml
index ce0538fc30..f0b5fee054 100644
--- a/platforms/android/AndroidManifest.xml
+++ b/platforms/android/AndroidManifest.xml
@@ -31,12 +31,10 @@
-
-
-
+
@@ -57,6 +55,7 @@
"applicationStateChanged(Qt::ApplicationSuspended)"
signal is sent! -->
+
diff --git a/platforms/android/build.gradle b/platforms/android/build.gradle
new file mode 100644
index 0000000000..d64b086a03
--- /dev/null
+++ b/platforms/android/build.gradle
@@ -0,0 +1,77 @@
+buildscript {
+ repositories {
+ google()
+ jcenter()
+ }
+
+ dependencies {
+ classpath 'com.android.tools.build:gradle:7.0.0'
+ }
+}
+
+repositories {
+ google()
+ jcenter()
+}
+
+apply plugin: 'com.android.application'
+
+dependencies {
+ implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
+}
+
+android {
+ /*******************************************************
+ * The following variables:
+ * - androidBuildToolsVersion,
+ * - androidCompileSdkVersion
+ * - qt5AndroidDir - holds the path to qt android files
+ * needed to build any Qt application
+ * on Android.
+ *
+ * are defined in gradle.properties file. This file is
+ * updated by QtCreator and androiddeployqt tools.
+ * Changing them manually might break the compilation!
+ *******************************************************/
+
+ compileSdkVersion androidCompileSdkVersion.toInteger()
+
+ // buildToolsVersion '28.0.3'
+
+ sourceSets {
+ main {
+ manifest.srcFile 'AndroidManifest.xml'
+ java.srcDirs = [qt5AndroidDir + '/src', 'src', 'java']
+ aidl.srcDirs = [qt5AndroidDir + '/src', 'src', 'aidl']
+ res.srcDirs = [qt5AndroidDir + '/res', 'res']
+ resources.srcDirs = ['resources']
+ renderscript.srcDirs = ['src']
+ assets.srcDirs = ['assets']
+ jniLibs.srcDirs = ['libs']
+ }
+ }
+
+ tasks.withType(JavaCompile) {
+ options.incremental = true
+ }
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ lintOptions {
+ abortOnError false
+ }
+
+ // Do not compress Qt binary resources file
+ aaptOptions {
+ noCompress 'rcc'
+ }
+
+ defaultConfig {
+ resConfig "en"
+ minSdkVersion = qtMinSdkVersion
+ targetSdkVersion = qtTargetSdkVersion
+ }
+}
diff --git a/platforms/android/gradle.properties b/platforms/android/gradle.properties
new file mode 100644
index 0000000000..fded106b17
--- /dev/null
+++ b/platforms/android/gradle.properties
@@ -0,0 +1,11 @@
+# Project-wide Gradle settings.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx2048m
+
+# Gradle caching allows reusing the build artifacts from a previous
+# build with the same inputs. However, over time, the cache size will
+# grow. Uncomment the following line to enable it.
+#org.gradle.caching=true
diff --git a/platforms/android/gradle/wrapper/gradle-wrapper.jar b/platforms/android/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000000..f6b961fd5a
Binary files /dev/null and b/platforms/android/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/platforms/android/gradle/wrapper/gradle-wrapper.properties b/platforms/android/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000000..fbce071a31
--- /dev/null
+++ b/platforms/android/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-all.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/platforms/android/gradlew b/platforms/android/gradlew
new file mode 100755
index 0000000000..cccdd3d517
--- /dev/null
+++ b/platforms/android/gradlew
@@ -0,0 +1,172 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/platforms/android/gradlew.bat b/platforms/android/gradlew.bat
new file mode 100644
index 0000000000..f9553162f1
--- /dev/null
+++ b/platforms/android/gradlew.bat
@@ -0,0 +1,84 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/platforms/android/res/drawable/icon.png b/platforms/android/res/drawable/icon.png
index a6f851d2b3..578101578a 100644
Binary files a/platforms/android/res/drawable/icon.png and b/platforms/android/res/drawable/icon.png differ
diff --git a/platforms/linux/CMakeLists.txt b/platforms/linux/CMakeLists.txt
index cc92fae4f0..ef1679b4ca 100644
--- a/platforms/linux/CMakeLists.txt
+++ b/platforms/linux/CMakeLists.txt
@@ -1,16 +1,12 @@
project(icons)
-set(desktop_FILES
- qlcplus.desktop
-)
+set(desktop_FILES qlcplus.desktop)
if(NOT qmlui)
- set(APPEND desktop_FILES qlcplus-fixtureeditor.desktop)
+ list(APPEND desktop_FILES qlcplus-fixtureeditor.desktop)
endif()
install(FILES ${desktop_FILES} DESTINATION ${INSTALLROOT}/share/applications/)
-set(icons_SRCS
- ../../resources/icons/png/qlcplus.png
-)
+set(icons_SRCS ../../resources/icons/png/qlcplus.png)
if(NOT qmlui)
list(APPEND icons_SRCS ../../resources/icons/png/qlcplus-fixtureeditor.png)
endif()
@@ -18,9 +14,7 @@ install(FILES ${icons_SRCS} DESTINATION ${INSTALLROOT}/share/pixmaps/)
install(FILES qlcplus.xml DESTINATION ${INSTALLROOT}/share/mime/packages)
-set(appdata_FILES
- org.qlcplus.QLCPlus.appdata.xml
-)
+set(appdata_FILES org.qlcplus.QLCPlus.appdata.xml)
if(NOT qmlui)
list(APPEND appdata_FILES org.qlcplus.QLCPlusFixtureEditor.appdata.xml)
endif()
@@ -32,6 +26,7 @@ if(NOT qmlui)
endif()
# install(FILES ../Sample.qxw DESTINATION ${INSTALLROOT}/${DATADIR})
+# install(FILES qlcplus-start.sh DESTINATION ${INSTALLROOT}/sbin)
if(appimage)
if (QT_DIR STREQUAL "/usr/lib/x86_64-linux-gnu/cmake/Qt5")
diff --git a/platforms/linux/qlcplus-start.sh b/platforms/linux/qlcplus-start.sh
new file mode 100644
index 0000000000..3d1a1b2616
--- /dev/null
+++ b/platforms/linux/qlcplus-start.sh
@@ -0,0 +1,49 @@
+#!/usr/bin/env bash
+#
+# Q Light Controller Plus
+# qlcplus-start.sh
+#
+# Copyright (c) 2024 Massimo Callegari
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0.txt
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# detect HDMI plug state
+QTPLATFORM="eglfs"
+kmsprint -m | grep connected > /dev/null
+if [ $? -eq 1 ]; then
+ QTPLATFORM="offscreen"
+fi
+
+QLCPLUS_OPTS="-platform $QTPLATFORM --nowm --web --web-auth --operate --overscan"
+
+if [ ! -f $HOME/.qlcplus/eglfs.json ]; then
+ mkdir -p $HOME/.qlcplus
+ echo '{ "device": "/dev/dri/card1" }' > $HOME/.qlcplus/eglfs.json
+fi
+
+if [ -f $HOME/.qlcplus/autostart.qxw ]; then
+ QLCPLUS_OPTS="$QLCPLUS_OPTS --open $HOME/.qlcplus/autostart.qxw"
+fi
+
+# if NTP hasn't done its job already, set the date to modern age...
+CURRDATE=`date +%Y`
+if [ "$CURRDATE" -lt "2024" ]; then
+ date +%Y%m%d -s "20240313"
+fi
+
+export QT_QPA_EGLFS_PHYSICAL_WIDTH=320
+export QT_QPA_EGLFS_PHYSICAL_HEIGHT=200
+export QT_QPA_EGLFS_ALWAYS_SET_MODE=1
+export QT_QPA_EGLFS_KMS_CONFIG=$HOME/.qlcplus/eglfs.json
+
+/usr/bin/qlcplus $QLCPLUS_OPTS
diff --git a/platforms/linux/qlcplus.service b/platforms/linux/qlcplus.service
new file mode 100644
index 0000000000..919be83f19
--- /dev/null
+++ b/platforms/linux/qlcplus.service
@@ -0,0 +1,14 @@
+[Unit]
+Description=Q Light Controller Plus
+Documentation=man:qlcplus(1)
+After=basic.target
+
+[Service]
+Type=simple
+User=pi
+Restart=on-failure
+RestartSec=3
+ExecStart=/usr/sbin/qlcplus-start.sh
+
+[Install]
+WantedBy=multi-user.target
diff --git a/platforms/macos/libsndfile-nametool.pri b/platforms/macos/libsndfile-nametool.pri
index 9c81804103..c57ecec527 100644
--- a/platforms/macos/libsndfile-nametool.pri
+++ b/platforms/macos/libsndfile-nametool.pri
@@ -5,7 +5,7 @@ LIBSNDFILE_FILEPATH = $$LIBSNDFILE_DIR/$$LIBSNDFILE_FILE
LIBOGG_FILE = libogg.0.dylib
LIBOGG_PATH = $$system("pkg-config --variable libdir ogg")
LIBOGG_FILEPATH = $$LIBOGG_PATH/$$LIBOGG_FILE
-LIBFLAC_FILE = libFLAC.8.dylib
+LIBFLAC_FILE = libFLAC.12.dylib
LIBFLAC_PATH = $$system("pkg-config --variable libdir flac")
LIBFLAC_FILEPATH = $$LIBFLAC_PATH/$$LIBFLAC_FILE
LIBVORBIS_FILE = libvorbis.0.dylib
diff --git a/platforms/macos/svg2icns.sh b/platforms/macos/svg2icns.sh
new file mode 100644
index 0000000000..9afdd8735b
--- /dev/null
+++ b/platforms/macos/svg2icns.sh
@@ -0,0 +1,30 @@
+#!/bin/sh -x
+
+set -e
+
+SIZES="
+16,16x16
+32,16x16@2x
+32,32x32
+64,32x32@2x
+128,128x128
+256,128x128@2x
+256,256x256
+512,256x256@2x
+512,512x512
+1024,512x512@2x
+"
+
+for SVG in "$@"; do
+ BASE=$(basename "$SVG" | sed 's/\.[^\.]*$//')
+ ICONSET="$BASE.iconset"
+ mkdir -p "$ICONSET"
+ for PARAMS in $SIZES; do
+ SIZE=$(echo $PARAMS | cut -d, -f1)
+ LABEL=$(echo $PARAMS | cut -d, -f2)
+ svg2png -w $SIZE -h $SIZE "$SVG" "$ICONSET"/icon_$LABEL.png
+ done
+
+ iconutil -c icns "$ICONSET"
+ rm -rf "$ICONSET"
+done
diff --git a/platforms/windows/CMakeLists.txt b/platforms/windows/CMakeLists.txt
index ca890c7256..660754c859 100644
--- a/platforms/windows/CMakeLists.txt
+++ b/platforms/windows/CMakeLists.txt
@@ -20,7 +20,7 @@ endif()
get_filename_component(QT_LIBS_PATH ${QT_DIR}/../../../bin ABSOLUTE)
get_filename_component(QT_PLUGINS_PATH ${QT_LIBS_PATH}/../share/${QT_P}/plugins ABSOLUTE)
get_filename_component(QT_QML_PATH ${QT_LIBS_PATH}/../share/${QT_P}/qml ABSOLUTE)
-set(SYS_LIBS_PATH $ENV{SystemDrive}/msys64/mingw32/bin)
+set(SYS_LIBS_PATH $ENV{SystemDrive}/msys64/mingw64/bin)
# set(SYS_LIBS_PATH D:/msys64/mingw32/bin)
# Qt library dependencies
@@ -172,7 +172,7 @@ endif()
# MSYS2 libraries
set(msys_path "${INSTALLROOT}/${LIBSDIR}")
set(msys_files "${SYS_LIBS_PATH}/libstdc++-6.dll"
- "${SYS_LIBS_PATH}/libgcc_s_dw2-1.dll"
+ "${SYS_LIBS_PATH}/libgcc_s_seh-1.dll"
"${SYS_LIBS_PATH}/libwinpthread-1.dll"
"${SYS_LIBS_PATH}/libicuin74.dll"
"${SYS_LIBS_PATH}/libicuuc74.dll"
diff --git a/platforms/windows/qlcplus4Qt5.nsi b/platforms/windows/qlcplus4Qt5.nsi
index 185a902d94..6a7271ee9e 100644
--- a/platforms/windows/qlcplus4Qt5.nsi
+++ b/platforms/windows/qlcplus4Qt5.nsi
@@ -4,7 +4,7 @@
;--------------------------------
;Defines
-!define QLCPLUS_HOME "c:\Qt\qlcplus"
+!define QLCPLUS_HOME "c:\projects\qlcplus"
!define MUI_ICON "${QLCPLUS_HOME}\resources\icons\qlcplus.ico"
!define MUI_UNICON "${NSISDIR}\Contrib\Graphics\Icons\nsis3-uninstall.ico"
!define MUI_HEADERIMAGE
@@ -15,7 +15,7 @@
;--------------------------------
;General
Name "Q Light Controller Plus"
-OutFile "QLC+_4.12.8.exe"
+OutFile "QLC+_4.13.1.exe"
InstallDir C:\QLC+
InstallDirRegKey HKCU "Software\qlcplus" "Install_Dir"
RequestExecutionLevel user
@@ -91,7 +91,6 @@ Section
File /r styles
File Sample.qxw
File *.qm
- File /r Documents
File /r Fixtures
File /r Gobos
File /r InputProfiles
@@ -133,7 +132,6 @@ Section "Uninstall"
RMDir /r $INSTDIR\styles
Delete $INSTDIR\Sample.qxw
Delete $INSTDIR\*.qm
- RMDir /r $INSTDIR\Documents
RMDir /r $INSTDIR\Fixtures
RMDir /r $INSTDIR\Gobos
RMDir /r $INSTDIR\InputProfiles
diff --git a/platforms/windows/qlcplus4Qt6.nsi b/platforms/windows/qlcplus4Qt6.nsi
index b60944b728..f211c12393 100644
--- a/platforms/windows/qlcplus4Qt6.nsi
+++ b/platforms/windows/qlcplus4Qt6.nsi
@@ -4,7 +4,7 @@
;--------------------------------
;Defines
-!define QLCPLUS_HOME "c:\Qt\qlcplus"
+!define QLCPLUS_HOME "c:\projects\qlcplus"
!define MUI_ICON "${QLCPLUS_HOME}\resources\icons\qlcplus.ico"
!define MUI_UNICON "${NSISDIR}\Contrib\Graphics\Icons\nsis3-uninstall.ico"
!define MUI_HEADERIMAGE
@@ -15,7 +15,7 @@
;--------------------------------
;General
Name "Q Light Controller Plus"
-OutFile "QLC+_4.12.8.exe"
+OutFile "QLC+_4.13.1.exe"
InstallDir C:\QLC+
InstallDirRegKey HKCU "Software\qlcplus" "Install_Dir"
RequestExecutionLevel user
@@ -90,7 +90,6 @@ Section
File /r styles
File Sample.qxw
File *.qm
- File /r Documents
File /r Fixtures
File /r Gobos
File /r InputProfiles
@@ -131,7 +130,6 @@ Section "Uninstall"
RMDir /r $INSTDIR\styles
Delete $INSTDIR\Sample.qxw
Delete $INSTDIR\*.qm
- RMDir /r $INSTDIR\Documents
RMDir /r $INSTDIR\Fixtures
RMDir /r $INSTDIR\Gobos
RMDir /r $INSTDIR\InputProfiles
diff --git a/platforms/windows/qlcplus5Qt5.nsi b/platforms/windows/qlcplus5Qt5.nsi
index 569017bfd3..3653be3645 100644
--- a/platforms/windows/qlcplus5Qt5.nsi
+++ b/platforms/windows/qlcplus5Qt5.nsi
@@ -4,7 +4,7 @@
;--------------------------------
;Defines
-!define QLCPLUS_HOME "c:\Qt\qlcplus"
+!define QLCPLUS_HOME "c:\projects\qlcplus"
!define MUI_ICON "${QLCPLUS_HOME}\resources\icons\qlcplus.ico"
!define MUI_UNICON "${NSISDIR}\Contrib\Graphics\Icons\nsis3-uninstall.ico"
!define MUI_HEADERIMAGE
@@ -15,7 +15,7 @@
;--------------------------------
;General
Name "Q Light Controller Plus"
-OutFile "QLC+_5.0.0_beta3.exe"
+OutFile "QLC+_5.0.0_beta4.exe"
InstallDir C:\QLC+5
InstallDirRegKey HKCU "Software\qlcplus" "Install_Dir"
RequestExecutionLevel user
diff --git a/plugins/artnet/src/artnetcontroller.cpp b/plugins/artnet/src/artnetcontroller.cpp
index 9ebe075bcf..08abf77b6f 100644
--- a/plugins/artnet/src/artnetcontroller.cpp
+++ b/plugins/artnet/src/artnetcontroller.cpp
@@ -24,7 +24,7 @@
#include
#include
-#define POLL_INTERVAL_MS 5000
+#define POLL_INTERVAL_MS 3000
#define SEND_INTERVAL_MS 2000
#define TRANSMIT_STANDARD "Standard"
@@ -429,9 +429,16 @@ bool ArtNetController::handleArtNetPoll(QByteArray const& datagram, QHostAddress
qDebug() << "[ArtNet] ArtPoll received";
#endif
QByteArray pollReplyPacket;
- m_packetizer->setupArtNetPollReply(pollReplyPacket, m_ipAddr, m_MACAddress);
- m_udpSocket->writeDatagram(pollReplyPacket, senderAddress, ARTNET_PORT);
- ++m_packetSent;
+ for (QMap::iterator it = m_universeMap.begin(); it != m_universeMap.end(); ++it)
+ {
+ quint32 universe = it.key();
+ UniverseInfo &info = it.value();
+ bool isInput = (info.type & Input) ? true : false;
+
+ m_packetizer->setupArtNetPollReply(pollReplyPacket, m_ipAddr, m_MACAddress, universe, isInput);
+ m_udpSocket->writeDatagram(pollReplyPacket, senderAddress, ARTNET_PORT);
+ ++m_packetSent;
+ }
++m_packetReceived;
return true;
}
@@ -451,7 +458,7 @@ bool ArtNetController::handleArtNetDmx(QByteArray const& datagram, QHostAddress
#if _DEBUG_RECEIVED_PACKETS
qDebug() << "[ArtNet] DMX data received. Universe:" << artnetUniverse << ", Data size:" << dmxData.size()
<< ", data[0]=" << (int)dmxData[0]
- << ", from=" << senderAddress.toString();
+ << ", from=" << QHostAddress(senderAddress.toIPv4Address()).toString();
#endif
for (QMap::iterator it = m_universeMap.begin(); it != m_universeMap.end(); ++it)
@@ -524,7 +531,7 @@ bool ArtNetController::handlePacket(QByteArray const& datagram, QHostAddress con
// return false;
#if _DEBUG_RECEIVED_PACKETS
- qDebug() << "Received packet with size: " << datagram.size() << ", host: " << senderAddress.toString();
+ qDebug() << "Received packet with size: " << datagram.size() << ", host: " << QHostAddress(senderAddress.toIPv4Address()).toString();
#endif
quint16 opCode = -1;
diff --git a/plugins/artnet/src/artnetpacketizer.cpp b/plugins/artnet/src/artnetpacketizer.cpp
index 98a60f49aa..969605d4ba 100644
--- a/plugins/artnet/src/artnetpacketizer.cpp
+++ b/plugins/artnet/src/artnetpacketizer.cpp
@@ -63,7 +63,7 @@ void ArtNetPacketizer::setupArtNetPoll(QByteArray& data)
data.append('\0'); // Priority
}
-void ArtNetPacketizer::setupArtNetPollReply(QByteArray &data, QHostAddress ipAddr, QString MACaddr)
+void ArtNetPacketizer::setupArtNetPollReply(QByteArray &data, QHostAddress ipAddr, QString MACaddr, quint32 universe, bool isInput)
{
int i = 0;
data.clear();
@@ -87,28 +87,42 @@ void ArtNetPacketizer::setupArtNetPollReply(QByteArray &data, QHostAddress ipAdd
data.append((char)0xF0); // Status1 - Ready and booted
data.append((char)0xFF); // ESTA Manufacturer MSB
data.append((char)0xFF); // ESTA Manufacturer LSB
+
data.append("QLC+"); // Short Name
for (i = 0; i < 14; i++)
data.append((char)0x00); // 14 bytes of stuffing
data.append("Q Light Controller Plus - ArtNet interface"); // Long Name
for (i = 0; i < 22; i++) // 64-42 bytes of stuffing. 42 is the length of the long name
data.append((char)0x00);
+
for (i = 0; i < 64; i++)
data.append((char)0x00); // Node report
- data.append((char)0x00); // NumPort MSB
- // FIXME: this should reflect the actual state of QLC+ output ports !
- data.append((char)0x01); // NumPort LSB
- data.append((char)0x80); // Port 1 type: can output DMX512 data
- data.append((char)0x80); // Port 2 type: can output DMX512 data
- data.append((char)0x80); // Port 3 type: can output DMX512 data
- data.append((char)0x80); // Port 4 type: can output DMX512 data
- // FIXME: this should reflect the actual state of QLC+ output ports !
- for (i = 0; i < 12; i++)
- data.append((char)0x00); // Set GoodInput[4], GoodOutput[4] and SwIn[4] all to unknown state
- data.append((char)0x00); // SwOut0 - output 0
- data.append((char)0x01); // SwOut1 - output 1
- data.append((char)0x02); // SwOut2 - output 2
- data.append((char)0x03); // SwOut3 - output 3
+ data.append((char)0x00); // NumPortsHi
+ data.append((char)0x01); // NumPortsLo
+ data.append(isInput ? (char)0x40 : (char)0x80); // PortTypes[0]: can input or output DMX512 data
+ data.append((char)0x00); // PortTypes[1]: nothing
+ data.append((char)0x00); // PortTypes[2]: nothing
+ data.append((char)0x00); // PortTypes[3]: nothing
+
+ data.append(isInput ? (char)0x80 : (char)0x00); // GoodInput[0] - input status port 1
+ data.append((char)0x00); // GoodInput[1] - input status port 2
+ data.append((char)0x00); // GoodInput[2] - input status port 3
+ data.append((char)0x00); // GoodInput[3] - input status port 4
+
+ data.append(isInput ? (char)0x00 : (char)0x80); // GoodOutputA[0] - output status port 1
+ data.append((char)0x00); // GoodOutputA[0] - output status port 2
+ data.append((char)0x00); // GoodOutputA[0] - output status port 3
+ data.append((char)0x00); // GoodOutputA[0] - output status port 4
+
+ data.append(isInput ? (char)universe : (char)0x00); // SwIn[0] - port 1
+ data.append((char)0x00); // SwIn[1] - port 2
+ data.append((char)0x00); // SwIn[2] - port 3
+ data.append((char)0x00); // SwIn[3] - port 4
+
+ data.append(isInput ? (char)0x00 : (char)universe); // SwOut[0] - port 1
+ data.append((char)0x00); // SwOut[1] - port 2
+ data.append((char)0x00); // SwOut[2] - port 3
+ data.append((char)0x00); // SwOut[3] - port 4
for (i = 0; i < 7; i++)
data.append((char)0x00); // SwVideo, SwMacro, SwRemote and 4 spare bytes
QStringList MAC = MACaddr.split(":");
@@ -208,7 +222,7 @@ bool ArtNetPacketizer::checkPacketAndCode(QByteArray const& data, quint16 &code)
if (data.at(7) != 0x00)
return false;
- code = ((int)data.at(9) << 8) + data.at(8);
+ code = (quint16(data.at(9)) << 8) + quint16(data.at(8));
return true;
}
diff --git a/plugins/artnet/src/artnetpacketizer.h b/plugins/artnet/src/artnetpacketizer.h
index 5409bf0f96..a7113636dd 100644
--- a/plugins/artnet/src/artnetpacketizer.h
+++ b/plugins/artnet/src/artnetpacketizer.h
@@ -86,7 +86,8 @@ class ArtNetPacketizer
void setupArtNetPoll(QByteArray& data);
/** Prepare an ArtNetPollReply packet */
- void setupArtNetPollReply(QByteArray &data, QHostAddress ipAddr, QString MACaddr);
+ void setupArtNetPollReply(QByteArray &data, QHostAddress ipAddr,
+ QString MACaddr, quint32 universe, bool isInput);
/** Prepare an ArtNetDmx packet */
void setupArtNetDmx(QByteArray& data, const int& universe, const QByteArray &values);
diff --git a/plugins/artnet/src/artnetplugin.cpp b/plugins/artnet/src/artnetplugin.cpp
index b866f63404..6f2ae27b72 100644
--- a/plugins/artnet/src/artnetplugin.cpp
+++ b/plugins/artnet/src/artnetplugin.cpp
@@ -441,7 +441,7 @@ void ArtNetPlugin::slotReadyRead()
void ArtNetPlugin::handlePacket(QByteArray const& datagram, QHostAddress const& senderAddress)
{
- // A firts filter: look for a controller on the same subnet as the sender.
+ // A first filter: look for a controller on the same subnet as the sender.
// This allows having the same ArtNet Universe on 2 different network interfaces.
foreach (ArtNetIO io, m_IOmapping)
{
diff --git a/plugins/artnet/src/configureartnet.cpp b/plugins/artnet/src/configureartnet.cpp
index 85b6ef7d0c..55a7584b29 100644
--- a/plugins/artnet/src/configureartnet.cpp
+++ b/plugins/artnet/src/configureartnet.cpp
@@ -95,7 +95,7 @@ void ConfigureArtNet::fillNodesTree()
it.next();
QTreeWidgetItem* nitem = new QTreeWidgetItem(pitem);
ArtNetNodeInfo nInfo = it.value();
- nitem->setText(KNodesColumnIP, it.key().toString());
+ nitem->setText(KNodesColumnIP, QHostAddress(it.key().toIPv4Address()).toString());
nitem->setText(KNodesColumnShortName, nInfo.shortName);
nitem->setText(KNodesColumnLongName, nInfo.longName);
}
diff --git a/plugins/dmxusb/src/CMakeLists.txt b/plugins/dmxusb/src/CMakeLists.txt
index 7c717d5aac..3d902bab46 100644
--- a/plugins/dmxusb/src/CMakeLists.txt
+++ b/plugins/dmxusb/src/CMakeLists.txt
@@ -27,8 +27,8 @@ add_library(${module_name}
)
if(WIN32)
- set(FTD2XXDIR "C:/Qt/D2XXSDK")
- target_link_libraries(${module_name} PRIVATE ${FTD2XXDIR}/i386/libftd2xx.a)
+ set(FTD2XXDIR "C:/projects/D2XXSDK")
+ target_link_libraries(${module_name} PRIVATE ${FTD2XXDIR}/amd64/libftd2xx.a)
target_include_directories(${module_name} PRIVATE ${FTD2XXDIR})
message("Building with FTD2xx support.")
set(WITH_D2XX TRUE)
@@ -151,11 +151,6 @@ if(WITH_D2XX)
target_compile_definitions(${module_name} PRIVATE FTD2XX)
endif()
-if((WITH_D2XX) AND (WIN32))
- target_include_directories(${module_name} PRIVATE ${FTD2XXDIR})
- target_link_libraries(${module_name} PRIVATE ${FTD2XXDIR}/i386/libftd2xx.a)
-endif()
-
if(WITH_LIBFTDI)
target_sources(${module_name} PUBLIC libftdi-interface.cpp libftdi-interface.h)
endif()
@@ -166,6 +161,9 @@ if (UNIX AND NOT APPLE)
DESTINATION ${UDEVRULESDIR})
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/org.qlcplus.QLCPlus.dmxusb.metainfo.xml"
DESTINATION ${METAINFODIR})
+elseif (WITH_D2XX)
+ install(FILES ${FTD2XXDIR}/amd64/ftd2xx64.dll
+ DESTINATION ${INSTALLROOT}/${LIBSDIR})
endif()
install(TARGETS ${module_name}
diff --git a/plugins/dmxusb/src/enttecdmxusbopen.cpp b/plugins/dmxusb/src/enttecdmxusbopen.cpp
index 1c1b160099..261bd07e3c 100644
--- a/plugins/dmxusb/src/enttecdmxusbopen.cpp
+++ b/plugins/dmxusb/src/enttecdmxusbopen.cpp
@@ -20,7 +20,6 @@
#include
#include
-#include
#include
#include "enttecdmxusbopen.h"
diff --git a/plugins/dmxusb/src/src.pro b/plugins/dmxusb/src/src.pro
index 2e775f3d02..aae7b4f956 100644
--- a/plugins/dmxusb/src/src.pro
+++ b/plugins/dmxusb/src/src.pro
@@ -40,7 +40,7 @@ CONFIG(ftd2xx) {
win32 {
# Windows target
- FTD2XXDIR = C:/Qt/D2XXSDK
+ FTD2XXDIR = C:/projects/D2XXSDK
LIBS += -L$$FTD2XXDIR/i386 -lftd2xx
LIBS += $$FTD2XXDIR/i386/libftd2xx.a
INCLUDEPATH += $$FTD2XXDIR
diff --git a/plugins/dmxusb/src/vinceusbdmx512.cpp b/plugins/dmxusb/src/vinceusbdmx512.cpp
index f033249711..563dee50e9 100644
--- a/plugins/dmxusb/src/vinceusbdmx512.cpp
+++ b/plugins/dmxusb/src/vinceusbdmx512.cpp
@@ -18,16 +18,20 @@
*/
#include
+
#include "vinceusbdmx512.h"
-VinceUSBDMX512::VinceUSBDMX512(DMXInterface *interface, quint32 outputLine)
- : DMXUSBWidget(interface, outputLine, DEFAULT_OUTPUT_FREQUENCY)
+VinceUSBDMX512::VinceUSBDMX512(DMXInterface *iface, quint32 outputLine)
+ : QThread(NULL)
+ , DMXUSBWidget(iface, outputLine, DEFAULT_OUTPUT_FREQUENCY)
+ , m_running(false)
{
// TODO: Check if DMX IN is available
}
VinceUSBDMX512::~VinceUSBDMX512()
{
+ stopOutputThread();
}
DMXUSBWidget::Type VinceUSBDMX512::type() const
@@ -35,26 +39,6 @@ DMXUSBWidget::Type VinceUSBDMX512::type() const
return DMXUSBWidget::VinceTX;
}
-/****************************************************************************
- * Name & Serial
- ****************************************************************************/
-
-QString VinceUSBDMX512::additionalInfo() const
-{
- QString info;
-
- info += QString("");
- info += QString("%1: %2 (%3)").arg(QObject::tr("Protocol"))
- .arg("Vince USB-DMX512")
- .arg(QObject::tr("Output"));
- info += QString("
");
- info += QString("%1: %2").arg(QObject::tr("Serial number"))
- .arg(serial());
- info += QString("
");
-
- return info;
-}
-
/****************************************************************************
* Open & Close
****************************************************************************/
@@ -74,104 +58,94 @@ bool VinceUSBDMX512::open(quint32 line, bool input)
if (iface()->write(QByteArray(2, 0x00)) == false)
return false;
- // Request start DMX command
- return this->writeData(VinceUSBDMX512::StartDMX);
+ QByteArray startSequence;
+
+ startSequence.append(QByteArray(2, VINCE_START_OF_MSG));
+ startSequence.append(VINCE_CMD_START_DMX);
+ startSequence.append(QByteArray(2, 0x00));
+ startSequence.append(VINCE_END_OF_MSG);
+
+ if (iface()->write(startSequence) == false)
+ qWarning() << Q_FUNC_INFO << name() << "START command failed";
+
+ start();
+
+ return true;
}
bool VinceUSBDMX512::close(quint32 line, bool input)
{
- Q_UNUSED(line)
Q_UNUSED(input)
- if (isOpen() == false)
- return true;
+ stopOutputThread();
+
+ QByteArray stopSequence;
- // Reqest stop DMX command
- if (this->writeData(VinceUSBDMX512::StopDMX) == true)
- return DMXUSBWidget::close();
+ stopSequence.append(QByteArray(2, VINCE_START_OF_MSG));
+ stopSequence.append(VINCE_CMD_STOP_DMX);
+ stopSequence.append(QByteArray(2, 0x00));
+ stopSequence.append(VINCE_END_OF_MSG);
- return false;
+ if (iface()->write(stopSequence) == false)
+ qWarning() << Q_FUNC_INFO << name() << "STOP command failed";
+
+ return DMXUSBWidget::close(line);
}
/****************************************************************************
- * Write & Read
+ * Inputs
****************************************************************************/
-bool VinceUSBDMX512::writeData(Command command, const QByteArray &data)
+int readData(DMXInterface *iface, QByteArray &payload)
{
- QByteArray message(1, command); // Command
- message.prepend(QByteArray(2, VINCE_START_OF_MSG)); // Start condition
- if (data.size() == 0)
- message.append(QByteArray(2, 0x00)); // Data length
- else
- {
- message.append(int((data.size() + 2) / 256)); // Data length
- message.append(int((data.size() + 2) % 256));
- message.append(QByteArray(2, 0x00)); // Gap with data
- message.append(data); // Data
- }
- message.append(VINCE_END_OF_MSG); // Stop condition
-
- return iface()->write(message);
-}
-
-QByteArray VinceUSBDMX512::readData(bool* ok)
-{
- uchar byte = 0;
+ bool ok;
+ char byte;
ushort dataLength = 0;
- QByteArray data = QByteArray();
// Read headers
for (int i = 0; i < 6; i++)
{
- *ok = false;
- // Attempt to read byte
- byte = iface()->readByte(ok);
- if (*ok == false)
- return data;
+ byte = iface->readByte(&ok);
+
+ if (ok == false)
+ return 0;
// Retrieve response (4th byte)
- if (i == 3 && byte != VINCE_RESP_OK)
+ if (i == 3)
{
- qWarning() << Q_FUNC_INFO << "Error" << byte << "in readed message";
- *ok = false;
+ if (byte != VINCE_RESP_OK)
+ {
+ qWarning() << Q_FUNC_INFO << "Unable to find start of next message";
+ return 0;
+ }
}
- // Retrieve length (5th & 6th bytes)
+ // Retrieve data length (5th & 6th bytes)
else if (i == 4)
dataLength = ushort(byte) * 256;
else if (i == 5)
dataLength += ushort(byte);
}
- // Read data
if (dataLength > 0)
{
qDebug() << Q_FUNC_INFO << "Attempt to read" << dataLength << "bytes";
- ushort i;
- for (i = 0; i < dataLength; i++)
- {
- byte = iface()->readByte(ok);
- if (*ok == false)
- {
- qWarning() << Q_FUNC_INFO << "No available byte to read (" << (dataLength - i) << "missing bytes)";
- return data;
- }
- data.append(byte);
- }
+ // Read the whole payload
+ payload.clear();
+ payload = iface->read(dataLength);
}
// Read end of message
- byte = iface()->readByte();
- if (byte != VINCE_END_OF_MSG)
- {
+ if ((byte = iface->readByte()) != VINCE_END_OF_MSG)
qWarning() << Q_FUNC_INFO << "Incorrect end of message received:" << byte;
- *ok = false;
- }
- return data;
+ return dataLength;
}
+/****************************************************************************
+ * Outputs
+ ****************************************************************************/
+
bool VinceUSBDMX512::writeUniverse(quint32 universe, quint32 output, const QByteArray& data, bool dataChanged)
{
Q_UNUSED(universe)
@@ -180,28 +154,89 @@ bool VinceUSBDMX512::writeUniverse(quint32 universe, quint32 output, const QByte
if (isOpen() == false)
return false;
- // Write only if universe has changed
- if (!dataChanged)
- return true;
+ if (m_outputLines[0].m_universeData.size() == 0)
+ {
+ m_outputLines[0].m_universeData.append(data);
+ m_outputLines[0].m_universeData.append(DMX_CHANNELS - data.size(), 0);
+ }
- if (writeData(VinceUSBDMX512::UpdateDMX, data) == false)
+ if (dataChanged)
+ m_outputLines[0].m_universeData.replace(0, data.size(), data);
+
+ return true;
+}
+
+void VinceUSBDMX512::stopOutputThread()
+{
+ if (isRunning() == true)
{
- qWarning() << Q_FUNC_INFO << name() << "will not accept DMX data";
- return false;
+ m_running = false;
+ wait();
}
- else
+}
+
+void VinceUSBDMX512::run()
+{
+ qDebug() << "OUTPUT thread started";
+
+ QElapsedTimer timer;
+
+ m_running = true;
+
+ while (m_running == true)
{
- bool ok = false;
- QByteArray resp = this->readData(&ok);
+ timer.restart();
+
+ int dataLen = m_outputLines[0].m_universeData.length();
- // Check the interface reponse
- if (ok == false || resp.size() > 0)
+ if (dataLen > 0)
{
- qWarning() << Q_FUNC_INFO << name() << "doesn't respond properly";
- return false;
+ QByteArray request;
+ request.append(QByteArray(2, VINCE_START_OF_MSG)); // Start byte
+ request.append(VINCE_CMD_UPDATE_DMX); // Command
+ request.append(int((dataLen + 2) / 256)); // Data length
+ request.append(int((dataLen + 2) % 256));
+ request.append(QByteArray(2, 0x00)); // Gap with data
+ request.append(m_outputLines[0].m_universeData);
+ request.append(VINCE_END_OF_MSG); // Stop byte
+
+ if (iface()->write(request) == false)
+ qWarning() << Q_FUNC_INFO << name() << "Will not accept DMX data";
+ else
+ {
+ QByteArray reply;
+
+ if (readData(iface(), reply) > 0)
+ qWarning() << Q_FUNC_INFO << name() << "Invalid response";
+ }
}
- m_universe = data;
- return true;
+ int timetoSleep = m_frameTimeUs - (timer.nsecsElapsed() / 1000);
+ if (timetoSleep < 0)
+ qWarning() << "DMX output is running late !";
+ else
+ usleep(timetoSleep);
}
+
+ qDebug() << "OUTPUT thread terminated";
+}
+
+/****************************************************************************
+ * Serial & name
+ ****************************************************************************/
+
+QString VinceUSBDMX512::additionalInfo() const
+{
+ QString info;
+
+ info += QString("");
+ info += QString("%1: %2 (%3)").arg(QObject::tr("Protocol"))
+ .arg("Vince USB-DMX512")
+ .arg(QObject::tr("Output"));
+ info += QString("
");
+ info += QString("%1: %2").arg(QObject::tr("Serial number"))
+ .arg(serial());
+ info += QString("
");
+
+ return info;
}
diff --git a/plugins/dmxusb/src/vinceusbdmx512.h b/plugins/dmxusb/src/vinceusbdmx512.h
index 08c3001f30..ab68d5aa4d 100644
--- a/plugins/dmxusb/src/vinceusbdmx512.h
+++ b/plugins/dmxusb/src/vinceusbdmx512.h
@@ -21,7 +21,7 @@
#define VINCEUSBDMX512_H
#include
-#include
+#include
#include "dmxusbwidget.h"
@@ -39,38 +39,18 @@
#define VINCE_RESP_IO_ERR char(0x10) //! CMD_IO_ERR
#define VINCE_RESP_PARAM_ERR char(0x11) //! CMD_PARAM_ERR
-/**
- * This is the base interface class for Vince USB-DMX512 widgets.
- */
-class VinceUSBDMX512 : public DMXUSBWidget
+class VinceUSBDMX512 : public QThread, public DMXUSBWidget
{
- /************************************************************************
- * Initialization
- ************************************************************************/
+ Q_OBJECT
+
public:
- VinceUSBDMX512(DMXInterface *interface, quint32 outputLine);
+ VinceUSBDMX512(DMXInterface *iface, quint32 outputLine);
+
virtual ~VinceUSBDMX512();
/** @reimp */
Type type() const;
-protected:
- /** Requests commands */
- enum Command
- {
- StartDMX = VINCE_CMD_START_DMX,
- StopDMX = VINCE_CMD_STOP_DMX,
- ResetDMX = VINCE_CMD_RESET_DMX,
- UpdateDMX = VINCE_CMD_UPDATE_DMX
- };
-
- /****************************************************************************
- * Name & Serial
- ****************************************************************************/
-public:
- /** @reimp */
- QString additionalInfo() const;
-
/****************************************************************************
* Open & Close
****************************************************************************/
@@ -79,38 +59,31 @@ class VinceUSBDMX512 : public DMXUSBWidget
bool open(quint32 line = 0, bool input = false);
/** @reimp */
- virtual bool close(quint32 line = 0, bool input = false);
-
- /************************************************************************
- * Write & Read
- ************************************************************************/
-protected:
- /**
- * Format and write data to the opened interface.
- *
- * @param command The request type
- * @param data The data to write
- * @return true if the values were sent successfully, otherwise false
- */
- bool writeData(enum Command command, const QByteArray& data = QByteArray());
-
- /**
- * Read and extract data from the opened interface.
- *
- * @param ok A pointer which tells if data was read or not
- * @return The available data
- */
- QByteArray readData(bool* ok = NULL);
-
- /************************************************************************
- * Write universe
- ************************************************************************/
+ bool close(quint32 line = 0, bool input = false);
+
+ /********************************************************************
+ * Outputs
+ ********************************************************************/
public:
/** @reimp */
bool writeUniverse(quint32 universe, quint32 output, const QByteArray& data, bool dataChanged);
private:
- QByteArray m_universe;
+ /** Stop the output thread */
+ void stopOutputThread();
+
+ /** Output thread worker method */
+ void run();
+
+private:
+ bool m_running;
+
+ /****************************************************************************
+ * Serial & name
+ ****************************************************************************/
+public:
+ /** @reimp */
+ QString additionalInfo() const;
};
#endif
diff --git a/plugins/dummy/dummyplugin.cpp b/plugins/dummy/dummyplugin.cpp
index 97bacc3a26..bf64e6a3dc 100644
--- a/plugins/dummy/dummyplugin.cpp
+++ b/plugins/dummy/dummyplugin.cpp
@@ -200,13 +200,13 @@ QString DummyPlugin::inputInfo(quint32 input)
return str;
}
-void DummyPlugin::sendFeedBack(quint32 universe, quint32 output, quint32 channel, uchar value, const QString &key)
+void DummyPlugin::sendFeedBack(quint32 universe, quint32 output, quint32 channel, uchar value, const QVariant ¶ms)
{
Q_UNUSED(universe)
Q_UNUSED(output)
Q_UNUSED(channel)
Q_UNUSED(value)
- Q_UNUSED(key)
+ Q_UNUSED(params)
/**
* If the device support this feature, this is the method to send data back for
diff --git a/plugins/dummy/dummyplugin.h b/plugins/dummy/dummyplugin.h
index 0811dfec3f..7a5e7f558a 100644
--- a/plugins/dummy/dummyplugin.h
+++ b/plugins/dummy/dummyplugin.h
@@ -85,7 +85,7 @@ class DummyPlugin : public QLCIOPlugin
QString inputInfo(quint32 input);
/** @reimp */
- void sendFeedBack(quint32 universe, quint32 output, quint32 channel, uchar value, const QString& key);
+ void sendFeedBack(quint32 universe, quint32 output, quint32 channel, uchar value, const QVariant ¶ms);
protected:
/** Place here the variables used by this plugin */
diff --git a/plugins/enttecwing/src/enttecwing.cpp b/plugins/enttecwing/src/enttecwing.cpp
index de77ac6e87..7d81253b50 100644
--- a/plugins/enttecwing/src/enttecwing.cpp
+++ b/plugins/enttecwing/src/enttecwing.cpp
@@ -169,7 +169,7 @@ QString EnttecWing::inputInfo(quint32 input)
}
void EnttecWing::sendFeedBack(quint32 universe, quint32 input,
- quint32 channel, uchar value, const QString &)
+ quint32 channel, uchar value, const QVariant &)
{
Q_UNUSED(universe)
diff --git a/plugins/enttecwing/src/enttecwing.h b/plugins/enttecwing/src/enttecwing.h
index 4f497016ca..34baf921e0 100644
--- a/plugins/enttecwing/src/enttecwing.h
+++ b/plugins/enttecwing/src/enttecwing.h
@@ -78,7 +78,7 @@ class EnttecWing : public QLCIOPlugin
QString inputInfo(quint32 input);
/** @reimp */
- void sendFeedBack(quint32 universe, quint32 input, quint32 channel, uchar value, const QString& key);
+ void sendFeedBack(quint32 universe, quint32 input, quint32 channel, uchar value, const QVariant ¶ms);
/*************************************************************************
* Outputs
diff --git a/plugins/gpio/gpioplugin.cpp b/plugins/gpio/gpioplugin.cpp
index 68dee2608e..bb2b134241 100644
--- a/plugins/gpio/gpioplugin.cpp
+++ b/plugins/gpio/gpioplugin.cpp
@@ -139,6 +139,7 @@ QString GPIOPlugin::outputInfo(quint32 output)
void GPIOPlugin::writeUniverse(quint32 universe, quint32 output, const QByteArray &data, bool dataChanged)
{
Q_UNUSED(universe)
+ Q_UNUSED(dataChanged)
if (output != 0)
return;
diff --git a/plugins/interfaces/qlcioplugin.cpp b/plugins/interfaces/qlcioplugin.cpp
index 03d2e6747b..2faabdd58f 100644
--- a/plugins/interfaces/qlcioplugin.cpp
+++ b/plugins/interfaces/qlcioplugin.cpp
@@ -86,13 +86,13 @@ QString QLCIOPlugin::inputInfo(quint32 input)
}
void QLCIOPlugin::sendFeedBack(quint32 universe, quint32 inputLine,
- quint32 channel, uchar value, const QString &key)
+ quint32 channel, uchar value, const QVariant ¶ms)
{
Q_UNUSED(universe)
Q_UNUSED(inputLine)
Q_UNUSED(channel)
Q_UNUSED(value)
- Q_UNUSED(key)
+ Q_UNUSED(params)
}
/*************************************************************************
diff --git a/plugins/interfaces/qlcioplugin.h b/plugins/interfaces/qlcioplugin.h
index 4b46f09cf0..72588f64f9 100644
--- a/plugins/interfaces/qlcioplugin.h
+++ b/plugins/interfaces/qlcioplugin.h
@@ -129,7 +129,8 @@ class QLCIOPlugin : public QObject
Input = 1 << 1,
Feedback = 1 << 2,
Infinite = 1 << 3,
- RDM = 1 << 4
+ RDM = 1 << 4,
+ Beats = 1 << 5
};
/**
@@ -263,7 +264,7 @@ class QLCIOPlugin : public QObject
* @param key a string to identify a channel by name (ATM used only by OSC)
*/
virtual void sendFeedBack(quint32 universe, quint32 inputLine,
- quint32 channel, uchar value, const QString& key = 0);
+ quint32 channel, uchar value, const QVariant ¶ms);
signals:
/**
diff --git a/plugins/loopback/src/loopback.cpp b/plugins/loopback/src/loopback.cpp
index 37e3335791..47b417a337 100644
--- a/plugins/loopback/src/loopback.cpp
+++ b/plugins/loopback/src/loopback.cpp
@@ -206,7 +206,7 @@ void Loopback::writeUniverse(quint32 universe, quint32 output, const QByteArray
}
}
-void Loopback::sendFeedBack(quint32 universe, quint32 input, quint32 channel, uchar value, const QString &)
+void Loopback::sendFeedBack(quint32 universe, quint32 input, quint32 channel, uchar value, const QVariant &)
{
if (!m_inputMap.contains(input))
return;
diff --git a/plugins/loopback/src/loopback.h b/plugins/loopback/src/loopback.h
index 546e0bb323..cc3979e875 100644
--- a/plugins/loopback/src/loopback.h
+++ b/plugins/loopback/src/loopback.h
@@ -86,7 +86,7 @@ class QLC_DECLSPEC Loopback : public QLCIOPlugin
QString inputInfo(quint32 input);
/** @reimp */
- void sendFeedBack(quint32 universe, quint32 input, quint32 channel, uchar value, const QString& key);
+ void sendFeedBack(quint32 universe, quint32 input, quint32 channel, uchar value, const QVariant ¶ms);
private:
//! loopback line -> channel data
diff --git a/plugins/midi/src/common/midiplugin.cpp b/plugins/midi/src/common/midiplugin.cpp
index 452a690b7d..8d04c9ba8b 100644
--- a/plugins/midi/src/common/midiplugin.cpp
+++ b/plugins/midi/src/common/midiplugin.cpp
@@ -66,7 +66,7 @@ QString MidiPlugin::name()
int MidiPlugin::capabilities() const
{
- return QLCIOPlugin::Output | QLCIOPlugin::Input | QLCIOPlugin::Feedback;
+ return QLCIOPlugin::Output | QLCIOPlugin::Input | QLCIOPlugin::Feedback | QLCIOPlugin::Beats;
}
/*****************************************************************************
@@ -285,7 +285,7 @@ QString MidiPlugin::inputInfo(quint32 input)
return str;
}
-void MidiPlugin::sendFeedBack(quint32 universe, quint32 output, quint32 channel, uchar value, const QString &)
+void MidiPlugin::sendFeedBack(quint32 universe, quint32 output, quint32 channel, uchar value, const QVariant ¶ms)
{
Q_UNUSED(universe)
@@ -297,7 +297,11 @@ void MidiPlugin::sendFeedBack(quint32 universe, quint32 output, quint32 channel,
qDebug() << "[sendFeedBack] Dev:" << dev->name() << ", channel:" << channel << ", value:" << value << dev->sendNoteOff();
uchar cmd = 0;
uchar data1 = 0, data2 = 0;
- if (QLCMIDIProtocol::feedbackToMidi(channel, value, dev->midiChannel(), dev->sendNoteOff(),
+ int midiChannel = dev->midiChannel();
+ if (params.isValid() && params.toInt() >= 0)
+ midiChannel += params.toInt();
+
+ if (QLCMIDIProtocol::feedbackToMidi(channel, value, midiChannel, dev->sendNoteOff(),
&cmd, &data1, &data2) == true)
{
qDebug() << "[sendFeedBack] cmd:" << cmd << "data1:" << data1 << "data2:" << data2;
@@ -330,7 +334,8 @@ void MidiPlugin::slotValueChanged(const QVariant& uid, ushort channel, uchar val
MidiInputDevice* dev = m_enumerator->inputDevices().at(i);
if (dev->uid() == uid)
{
- emit valueChanged(UINT_MAX, i, channel, value);
+ emit valueChanged(UINT_MAX, i, channel, value,
+ channel == CHANNEL_OFFSET_MBC_BEAT ? "beat" : "");
break;
}
}
diff --git a/plugins/midi/src/common/midiplugin.h b/plugins/midi/src/common/midiplugin.h
index cb00cc2415..b9764d53c8 100644
--- a/plugins/midi/src/common/midiplugin.h
+++ b/plugins/midi/src/common/midiplugin.h
@@ -110,7 +110,7 @@ class MidiPlugin : public QLCIOPlugin
QString inputInfo(quint32 input);
/** @reimp */
- void sendFeedBack(quint32 universe, quint32 output, quint32 channel, uchar value, const QString& key);
+ void sendFeedBack(quint32 universe, quint32 output, quint32 channel, uchar value, const QVariant ¶ms);
void sendSysEx(quint32 output, const QByteArray &data);
diff --git a/plugins/os2l/os2lplugin.cpp b/plugins/os2l/os2lplugin.cpp
index 7c18228ed6..75850d3f9b 100644
--- a/plugins/os2l/os2lplugin.cpp
+++ b/plugins/os2l/os2lplugin.cpp
@@ -48,7 +48,7 @@ QString OS2LPlugin::name()
int OS2LPlugin::capabilities() const
{
- return QLCIOPlugin::Input | QLCIOPlugin::Feedback;
+ return QLCIOPlugin::Input | QLCIOPlugin::Feedback | QLCIOPlugin::Beats;
}
QString OS2LPlugin::pluginInfo()
@@ -127,23 +127,6 @@ QString OS2LPlugin::inputInfo(quint32 input)
return str;
}
-void OS2LPlugin::sendFeedBack(quint32 universe, quint32 output, quint32 channel, uchar value, const QString &key)
-{
- Q_UNUSED(universe)
- Q_UNUSED(output)
- Q_UNUSED(channel)
- Q_UNUSED(value)
- Q_UNUSED(key)
-
- /**
- * If the device support this feature, this is the method to send data back for
- * visual feedback.
- * To implement such method, the plugin must have an input line corresponding
- * to the specified output line.
- * Basically feedback data must return to the same line where it came from
- */
-}
-
quint32 OS2LPlugin::universe() const
{
return m_inputUniverse;
diff --git a/plugins/os2l/os2lplugin.h b/plugins/os2l/os2lplugin.h
index 99ec246584..5c1ec5457c 100644
--- a/plugins/os2l/os2lplugin.h
+++ b/plugins/os2l/os2lplugin.h
@@ -70,9 +70,6 @@ class OS2LPlugin : public QLCIOPlugin
/** @reimp */
QString inputInfo(quint32 input);
- /** @reimp */
- void sendFeedBack(quint32 universe, quint32 output, quint32 channel, uchar value, const QString& key);
-
quint32 universe() const;
protected:
diff --git a/plugins/osc/oscplugin.cpp b/plugins/osc/oscplugin.cpp
index e807898b80..d3ce296b92 100644
--- a/plugins/osc/oscplugin.cpp
+++ b/plugins/osc/oscplugin.cpp
@@ -293,14 +293,14 @@ QString OSCPlugin::inputInfo(quint32 input)
}
void OSCPlugin::sendFeedBack(quint32 universe, quint32 input,
- quint32 channel, uchar value, const QString &key)
+ quint32 channel, uchar value, const QVariant ¶ms)
{
if (input >= (quint32)m_IOmapping.count())
return;
OSCController *controller = m_IOmapping[input].controller;
if (controller != NULL)
- controller->sendFeedback(universe, channel, value, key);
+ controller->sendFeedback(universe, channel, value, params.toString());
}
/*********************************************************************
diff --git a/plugins/osc/oscplugin.h b/plugins/osc/oscplugin.h
index 87f0db73b0..6050db1c1c 100644
--- a/plugins/osc/oscplugin.h
+++ b/plugins/osc/oscplugin.h
@@ -108,7 +108,7 @@ class OSCPlugin : public QLCIOPlugin
QString inputInfo(quint32 input);
/** @reimp */
- void sendFeedBack(quint32 universe, quint32 input, quint32 channel, uchar value, const QString& key);
+ void sendFeedBack(quint32 universe, quint32 input, quint32 channel, uchar value, const QVariant ¶ms);
/*********************************************************************
* Configuration
diff --git a/plugins/uart/uartplugin.cpp b/plugins/uart/uartplugin.cpp
index c4ef329617..d6fbd8918b 100644
--- a/plugins/uart/uartplugin.cpp
+++ b/plugins/uart/uartplugin.cpp
@@ -130,6 +130,7 @@ QString UARTPlugin::outputInfo(quint32 output)
void UARTPlugin::writeUniverse(quint32 universe, quint32 output, const QByteArray &data, bool dataChanged)
{
Q_UNUSED(universe)
+ Q_UNUSED(dataChanged)
if (output < quint32(m_widgets.count()))
m_widgets.at(output)->writeUniverse(data);
@@ -184,20 +185,3 @@ QString UARTPlugin::inputInfo(quint32 input)
return str;
}
-
-void UARTPlugin::sendFeedBack(quint32 universe, quint32 output, quint32 channel, uchar value, const QString &key)
-{
- Q_UNUSED(universe)
- Q_UNUSED(output)
- Q_UNUSED(channel)
- Q_UNUSED(value)
- Q_UNUSED(key)
-
- /**
- * If the device support this feature, this is the method to send data back for
- * visual feedback.
- * To implement such method, the plugin must have an input line corresponding
- * to the specified output line.
- * Basically feedback data must return to the same line where it came from
- */
-}
diff --git a/plugins/uart/uartplugin.h b/plugins/uart/uartplugin.h
index 02d6892bd3..1db50cc366 100644
--- a/plugins/uart/uartplugin.h
+++ b/plugins/uart/uartplugin.h
@@ -85,9 +85,6 @@ class UARTPlugin : public QLCIOPlugin
/** @reimp */
QString inputInfo(quint32 input);
-
- /** @reimp */
- void sendFeedBack(quint32 universe, quint32 output, quint32 channel, uchar value, const QString& key);
};
#endif
diff --git a/plugins/uart/uartwidget.cpp b/plugins/uart/uartwidget.cpp
index d2e5ee34fd..1e5a1522d4 100644
--- a/plugins/uart/uartwidget.cpp
+++ b/plugins/uart/uartwidget.cpp
@@ -18,7 +18,7 @@
*/
#include
-#include
+#include
#include
#include
@@ -141,7 +141,7 @@ void UARTWidget::run()
int frameTime = (int) floor(((double)1000 / 30) + (double)0.5);
m_granularity = Bad;
- QTime time;
+ QElapsedTimer time;
time.start();
usleep(1000);
if (time.elapsed() <= 3)
diff --git a/qlc.pro b/qlc.pro
index fe7a4452bd..6ae1c69626 100644
--- a/qlc.pro
+++ b/qlc.pro
@@ -47,7 +47,9 @@ win32:coverage.commands = @echo Get a better OS.
# Translations
translations.target = translate
-QMAKE_EXTRA_TARGETS += translations
+!android: {
+ QMAKE_EXTRA_TARGETS += translations
+}
qmlui: {
translations.commands += ./translate.sh "qmlui"
} else {
@@ -59,7 +61,9 @@ appimage: {
} else {
translations.path = $$INSTALLROOT/$$TRANSLATIONDIR
}
-INSTALLS += translations
+!android: {
+ INSTALLS += translations
+}
QMAKE_DISTCLEAN += $$translations.files
# run
diff --git a/qmlui/CMakeLists.txt b/qmlui/CMakeLists.txt
index ad1e0bf867..cf26f1828c 100644
--- a/qmlui/CMakeLists.txt
+++ b/qmlui/CMakeLists.txt
@@ -19,7 +19,7 @@ else()
qt5_add_translation(QM_FILES ${TS_FILES})
endif()
-add_executable(${module_name} WIN32 MACOSX_BUNDLE
+set(SRC_FILES
app.cpp app.h
audioeditor.cpp audioeditor.h
chasereditor.cpp chasereditor.h
@@ -63,6 +63,8 @@ add_executable(${module_name} WIN32 MACOSX_BUNDLE
uimanager.cpp uimanager.h
videoeditor.cpp videoeditor.h
videoprovider.cpp videoprovider.h
+ virtualconsole/vcanimation.cpp virtualconsole/vcanimation.h
+ virtualconsole/vcaudiotrigger.cpp virtualconsole/vcaudiotrigger.h
virtualconsole/vcbutton.cpp virtualconsole/vcbutton.h
virtualconsole/vcclock.cpp virtualconsole/vcclock.h
virtualconsole/vccuelist.cpp virtualconsole/vccuelist.h
@@ -72,10 +74,26 @@ add_executable(${module_name} WIN32 MACOSX_BUNDLE
virtualconsole/vcslider.cpp virtualconsole/vcslider.h
virtualconsole/vcsoloframe.cpp virtualconsole/vcsoloframe.h
virtualconsole/vcwidget.cpp virtualconsole/vcwidget.h
+ virtualconsole/vcxypad.cpp virtualconsole/vcxypad.h
+ virtualconsole/vcspeeddial.cpp virtualconsole/vcspeeddial.h
virtualconsole/virtualconsole.cpp virtualconsole/virtualconsole.h
- ${QM_FILES}
)
+if(ANDROID)
+ add_library(${module_name} SHARED
+ ${SRC_FILES}
+ ${QM_FILES}
+ )
+ set_target_properties(
+ ${module_name}
+ PROPERTIES LIBRARY_OUTPUT_NAME qlcplus)
+else()
+ add_executable(${module_name} WIN32 MACOSX_BUNDLE
+ ${SRC_FILES}
+ ${QM_FILES}
+ )
+endif()
+
if(WIN32)
target_sources(${module_name} PRIVATE
qmlui.rc
@@ -111,6 +129,17 @@ target_link_libraries(${module_name} PRIVATE
qlcplusengine
)
+if(ANDROID)
+ target_link_libraries(${module_name} PRIVATE
+ Qt${QT_MAJOR_VERSION}::Concurrent
+ Qt${QT_MAJOR_VERSION}::OpenGL
+ GLESv2
+ log
+ z
+ c++_shared
+ )
+endif()
+
if(lupdate_only)
target_sources(${module_name} PRIVATE
qml/*.qml
diff --git a/qmlui/contextmanager.cpp b/qmlui/contextmanager.cpp
index b06952bf70..a5752e4b49 100644
--- a/qmlui/contextmanager.cpp
+++ b/qmlui/contextmanager.cpp
@@ -1381,6 +1381,8 @@ void ContextManager::slotNewFixtureCreated(quint32 fxID, qreal x, qreal y, qreal
qDebug() << "[ContextManager] New fixture created" << fxID;
+ if (m_uniGridView->isEnabled())
+ m_monProps->setFixturePosition(fxID, 0, 0, QVector3D(0, 0, 0));
if (m_DMXView->isEnabled())
m_DMXView->createFixtureItem(fxID);
if (m_2DView->isEnabled())
@@ -1626,6 +1628,9 @@ void ContextManager::slotUniverseWritten(quint32 idx, const QByteArray &ua)
void ContextManager::slotFunctionEditingChanged(bool status)
{
+ if (status == m_editingEnabled)
+ return;
+
resetFixtureSelection();
m_editingEnabled = status;
}
@@ -1643,7 +1648,7 @@ void ContextManager::setDumpValue(quint32 fxID, quint32 channel, uchar value, bo
currentVal.setValue(SceneValue(fxID, channel, currDmxValue));
newVal.setValue(sValue);
- if (currentVal != newVal || value != currDmxValue)
+ //if (currentVal != newVal || value != currDmxValue)
{
if (output)
{
diff --git a/qmlui/fixtureeditor/editorview.cpp b/qmlui/fixtureeditor/editorview.cpp
index 5c864bc272..307a71fb61 100644
--- a/qmlui/fixtureeditor/editorview.cpp
+++ b/qmlui/fixtureeditor/editorview.cpp
@@ -374,6 +374,24 @@ QString EditorView::checkFixture()
errors.append(tr("Empty capability description provided in channel '%1'").arg(channel->name()));
}
}
+
+ for (QLCFixtureMode *mode : m_fixtureDef->modes())
+ {
+ if (mode->name().isEmpty())
+ errors.append(tr("Empty mode name provided"));
+
+ if (mode->channels().count() == 0)
+ errors.append(tr("Mode '%1' has no channels defined").arg(mode->name()));
+
+ quint32 chIndex = 0;
+ for (QLCChannel *channel : mode->channels())
+ {
+
+ if (mode->channelActsOn(chIndex) == chIndex)
+ errors.append(tr("In mode '%1', channel '%2' cannot act on itself").arg(mode->name(), channel->name()));
+ chIndex++;
+ }
+ }
}
if (m_fixtureDef->modes().count() == 0)
diff --git a/qmlui/fixtureeditor/modeedit.cpp b/qmlui/fixtureeditor/modeedit.cpp
index f30e235c6f..cead486ac2 100644
--- a/qmlui/fixtureeditor/modeedit.cpp
+++ b/qmlui/fixtureeditor/modeedit.cpp
@@ -100,6 +100,33 @@ bool ModeEdit::deleteChannel(QLCChannel *channel)
return res;
}
+QStringList ModeEdit::actsOnChannels()
+{
+ QStringList list;
+ list << "-";
+
+ for (QLCChannel *channel : m_mode->channels())
+ list << channel->name();
+
+ return list;
+}
+
+int ModeEdit::actsOnChannel(int index)
+{
+ quint32 actsOnChannelIndex = m_mode->channelActsOn(index);
+
+ if (actsOnChannelIndex != QLCChannel::invalid())
+ return actsOnChannelIndex + 1;
+ else
+ return 0;
+}
+
+void ModeEdit::setActsOnChannel(int sourceIndex, int destIndex)
+{
+ quint32 actsOnChannel = destIndex == 0 ? QLCChannel::invalid() : destIndex - 1;
+ m_mode->setChannelActsOn(sourceIndex, actsOnChannel);
+}
+
void ModeEdit::updateChannelList()
{
m_channelList->clear();
@@ -113,6 +140,7 @@ void ModeEdit::updateChannelList()
}
emit channelsChanged();
+ emit actsOnChannelsChanged();
}
/************************************************************************
diff --git a/qmlui/fixtureeditor/modeedit.h b/qmlui/fixtureeditor/modeedit.h
index 8fd9341397..ef6ebbd18a 100644
--- a/qmlui/fixtureeditor/modeedit.h
+++ b/qmlui/fixtureeditor/modeedit.h
@@ -37,6 +37,7 @@ class ModeEdit : public QObject
Q_PROPERTY(QVariant heads READ heads NOTIFY headsChanged)
Q_PROPERTY(bool useGlobalPhysical READ useGlobalPhysical CONSTANT)
Q_PROPERTY(PhysicalEdit *physical READ physical CONSTANT)
+ Q_PROPERTY(QStringList actsOnChannels READ actsOnChannels NOTIFY actsOnChannelsChanged)
public:
ModeEdit(QLCFixtureMode *mode, QObject *parent = nullptr);
@@ -73,11 +74,20 @@ class ModeEdit : public QObject
/** Delete the given $channel from the mode being edited */
Q_INVOKABLE bool deleteChannel(QLCChannel *channel);
+ /** Return a simple list with the possible channels to act on */
+ QStringList actsOnChannels();
+
+ /** Return the channel where channel at $index acts on */
+ Q_INVOKABLE int actsOnChannel(int index);
+
+ Q_INVOKABLE void setActsOnChannel(int sourceIndex, int destIndex);
+
private:
void updateChannelList();
signals:
void channelsChanged();
+ void actsOnChannelsChanged();
private:
/** Reference to a channel list usable in QML */
diff --git a/qmlui/fixturemanager.cpp b/qmlui/fixturemanager.cpp
index 5c8f6201d6..0cb0f0a2bd 100644
--- a/qmlui/fixturemanager.cpp
+++ b/qmlui/fixturemanager.cpp
@@ -274,7 +274,7 @@ bool FixtureManager::addFixture(QString manuf, QString model, QString mode, QStr
{
Fixture *fxi = new Fixture(m_doc);
//quint32 fxAddress = address + (i * channels) + (i * gap);
- if (fxAddress + channels >= UNIVERSE_SIZE)
+ if (fxAddress + channels > UNIVERSE_SIZE)
{
uniIdx++;
if (m_doc->inputOutputMap()->getUniverseID(uniIdx) == m_doc->inputOutputMap()->invalidUniverse())
@@ -1054,6 +1054,9 @@ void FixtureManager::slotFixtureAdded(quint32 id, QVector3D pos)
else
{
Fixture *fixture = m_doc->fixture(id);
+ if (fixture == nullptr)
+ return;
+
QStringList uniNames = m_doc->inputOutputMap()->universeNames();
QString universeName = uniNames.at(fixture->universe());
int matchMask = 0;
diff --git a/qmlui/inputoutputmanager.cpp b/qmlui/inputoutputmanager.cpp
index 1b2e3a813a..5e244b7587 100644
--- a/qmlui/inputoutputmanager.cpp
+++ b/qmlui/inputoutputmanager.cpp
@@ -93,6 +93,20 @@ QStringList InputOutputManager::universeNames() const
return m_ioMap->universeNames();
}
+QString InputOutputManager::universeName(quint32 universeId)
+{
+ if (universeId == Universe::invalid())
+ return tr("All universes");
+ else
+ {
+ Universe *uni = m_ioMap->universe(universeId);
+ if (uni != nullptr)
+ return uni->name();
+ }
+
+ return QString();
+}
+
QVariant InputOutputManager::universesListModel() const
{
QVariantList universesList;
@@ -718,20 +732,20 @@ QVariant InputOutputManager::beatGeneratorsList()
internalMap.insert("privateName", "");
genList.append(internalMap);
- // add the currently open MIDI input devices
+ // add the currently open input devices that support beats
foreach (Universe *uni, m_ioMap->universes())
{
InputPatch *ip = uni->inputPatch();
- if (ip == nullptr || ip->pluginName() != "MIDI")
+ if (ip == nullptr || (ip->plugin()->capabilities() & QLCIOPlugin::Beats) == 0)
continue;
- QVariantMap midiInMap;
- midiInMap.insert("type", "MIDI");
- midiInMap.insert("name", ip->inputName());
- midiInMap.insert("uni", uni->id());
- midiInMap.insert("line", ip->input());
- midiInMap.insert("privateName", "");
- genList.append(midiInMap);
+ QVariantMap pluginMap;
+ pluginMap.insert("type", "PLUGIN");
+ pluginMap.insert("name", ip->inputName());
+ pluginMap.insert("uni", uni->id());
+ pluginMap.insert("line", ip->input());
+ pluginMap.insert("privateName", ip->pluginName());
+ genList.append(pluginMap);
}
// add the currently selected audio input device
@@ -789,8 +803,8 @@ void InputOutputManager::setBeatType(QString beatType)
if (m_beatType == "INTERNAL")
m_ioMap->setBeatGeneratorType(InputOutputMap::Internal);
- else if (m_beatType == "MIDI")
- m_ioMap->setBeatGeneratorType(InputOutputMap::MIDI);
+ else if (m_beatType == "PLUGIN")
+ m_ioMap->setBeatGeneratorType(InputOutputMap::Plugin);
else if (m_beatType == "AUDIO")
m_ioMap->setBeatGeneratorType(InputOutputMap::Audio);
else
@@ -806,7 +820,7 @@ void InputOutputManager::slotBeatTypeChanged()
switch(m_ioMap->beatGeneratorType())
{
case InputOutputMap::Internal: m_beatType = "INTERNAL"; break;
- case InputOutputMap::MIDI: m_beatType = "MIDI"; break;
+ case InputOutputMap::Plugin: m_beatType = "PLUGIN"; break;
case InputOutputMap::Audio: m_beatType = "AUDIO"; break;
case InputOutputMap::Disabled:
default:
diff --git a/qmlui/inputoutputmanager.h b/qmlui/inputoutputmanager.h
index 5445e137c9..4bac8c40ee 100644
--- a/qmlui/inputoutputmanager.h
+++ b/qmlui/inputoutputmanager.h
@@ -70,6 +70,7 @@ protected slots:
public:
QVariant universes();
QStringList universeNames() const;
+ Q_INVOKABLE QString universeName(quint32 universeId);
QVariant universesListModel() const;
/** Get/Set the currently selected universe index */
diff --git a/qmlui/js/FixtureDrag.js b/qmlui/js/FixtureDrag.js
index ba38ec72f0..4563ced4a4 100644
--- a/qmlui/js/FixtureDrag.js
+++ b/qmlui/js/FixtureDrag.js
@@ -87,21 +87,19 @@ function handleDrag(mouse)
function endDrag(mouse)
{
if (draggedItem == null)
- {
return;
- }
var currContext = previewLoader.item.contextName;
var offset = 0;
- console.log("Current context: " + currContext);
+ console.log("[FixtureDrag] Current context: " + currContext);
+
if (currContext === "2D")
- {
offset = View2D.gridPosition.x;
- }
+
var x = draggedItem.x - leftSidePanel.width - offset;
var y = draggedItem.y - previewLoader.y - viewToolbar.height;
- console.log("Item x: " + x + ", y: " + y);
+ console.log("[FixtureDrag] Item x: " + x + ", y: " + y);
if (x >= 0 && y >= 0)
{
diff --git a/qmlui/js/GenericHelpers.js b/qmlui/js/GenericHelpers.js
index 0f317b16e5..051c9ff385 100644
--- a/qmlui/js/GenericHelpers.js
+++ b/qmlui/js/GenericHelpers.js
@@ -19,7 +19,7 @@
function pluginIconFromName(name)
{
- switch(name)
+ switch (name)
{
case "ArtNet": return "qrc:/artnetplugin.svg";
case "DMX USB": return "qrc:/dmxusbplugin.svg";
diff --git a/qmlui/main.cpp b/qmlui/main.cpp
index 005418dee6..391cf6c78e 100644
--- a/qmlui/main.cpp
+++ b/qmlui/main.cpp
@@ -99,6 +99,7 @@ int main(int argc, char *argv[])
parser.process(app);
+#if !defined Q_OS_ANDROID
if (!parser.isSet(threedSupportOption))
{
QSurfaceFormat format;
@@ -107,7 +108,7 @@ int main(int argc, char *argv[])
format.setProfile(QSurfaceFormat::CoreProfile);
QSurfaceFormat::setDefaultFormat(format);
}
-
+#endif
if (parser.isSet(debugOption))
qInstallMessageHandler(debugMessageHandler);
diff --git a/qmlui/mainview3d.cpp b/qmlui/mainview3d.cpp
index 6bcf45a857..cc1d8c0a02 100644
--- a/qmlui/mainview3d.cpp
+++ b/qmlui/mainview3d.cpp
@@ -90,6 +90,8 @@ MainView3D::MainView3D(QQuickView *view, Doc *doc, QObject *parent)
QStringList listRoles;
listRoles << "itemID" << "name" << "isSelected";
m_genericItemsList->setRoleNames(listRoles);
+
+ resetCameraPosition();
}
MainView3D::~MainView3D()
@@ -164,13 +166,11 @@ void MainView3D::resetItems()
{
it.next();
SceneItem *e = it.value();
- //if (e->m_headItem)
- // delete e->m_headItem;
- //if (e->m_armItem)
- // delete e->m_armItem;
delete e->m_goboTexture;
- // delete e->m_rootItem; // TODO: with this -> segfault
delete e->m_selectionBox;
+ // delete e->m_rootItem; // TODO: with this -> segfault
+ e->m_rootItem->setProperty("enabled", false); // workaround for the above
+ delete e;
}
//const auto end = m_entitiesMap.end();
@@ -196,6 +196,52 @@ void MainView3D::resetItems()
setFrameCountEnabled(false);
}
+void MainView3D::resetCameraPosition()
+{
+ setCameraPosition(QVector3D(0.0, 3.0, 7.5));
+ setCameraUpVector(QVector3D(0.0, 1.0, 0.0));
+ setCameraViewCenter(QVector3D(0.0, 1.0, 0.0));
+}
+
+QVector3D MainView3D::cameraPosition() const
+{
+ return m_cameraPosition;
+}
+
+void MainView3D::setCameraPosition(const QVector3D &newCameraPosition)
+{
+ if (m_cameraPosition == newCameraPosition)
+ return;
+ m_cameraPosition = newCameraPosition;
+ emit cameraPositionChanged();
+}
+
+QVector3D MainView3D::cameraUpVector() const
+{
+ return m_cameraUpVector;
+}
+
+void MainView3D::setCameraUpVector(const QVector3D &newCameraUpVector)
+{
+ if (m_cameraUpVector == newCameraUpVector)
+ return;
+ m_cameraUpVector = newCameraUpVector;
+ emit cameraUpVectorChanged();
+}
+
+QVector3D MainView3D::cameraViewCenter() const
+{
+ return m_cameraViewCenter;
+}
+
+void MainView3D::setCameraViewCenter(const QVector3D &newCameraViewCenter)
+{
+ if (m_cameraViewCenter == newCameraViewCenter)
+ return;
+ m_cameraViewCenter = newCameraViewCenter;
+ emit cameraViewCenterChanged();
+}
+
QString MainView3D::meshDirectory() const
{
QDir dir = QDir::cleanPath(QLCFile::systemDirectory(MESHESDIR).path());
@@ -1454,8 +1500,11 @@ void MainView3D::removeFixtureItem(quint32 itemID)
SceneItem *mesh = m_entitiesMap.take(itemID);
- delete mesh->m_rootItem;
+ delete mesh->m_goboTexture;
delete mesh->m_selectionBox;
+ delete mesh->m_rootTransform;
+// delete mesh->m_rootItem; // this will cause a segfault
+ mesh->m_rootItem->setProperty("enabled", false); // workaround for the above
delete mesh;
}
diff --git a/qmlui/mainview3d.h b/qmlui/mainview3d.h
index 477a3366b1..961360aece 100644
--- a/qmlui/mainview3d.h
+++ b/qmlui/mainview3d.h
@@ -94,6 +94,10 @@ class MainView3D : public PreviewContext
{
Q_OBJECT
+ Q_PROPERTY(QVector3D cameraPosition READ cameraPosition WRITE setCameraPosition NOTIFY cameraPositionChanged FINAL)
+ Q_PROPERTY(QVector3D cameraUpVector READ cameraUpVector WRITE setCameraUpVector NOTIFY cameraUpVectorChanged FINAL)
+ Q_PROPERTY(QVector3D cameraViewCenter READ cameraViewCenter WRITE setCameraViewCenter NOTIFY cameraViewCenterChanged FINAL)
+
Q_PROPERTY(RenderQuality renderQuality READ renderQuality WRITE setRenderQuality NOTIFY renderQualityChanged)
Q_PROPERTY(QString meshDirectory READ meshDirectory CONSTANT)
Q_PROPERTY(QStringList stagesList READ stagesList CONSTANT)
@@ -123,8 +127,24 @@ class MainView3D : public PreviewContext
/** @reimp */
void setUniverseFilter(quint32 universeFilter);
+ /** Cleanup all the items in the scene */
void resetItems();
+ /** Reset the camera position to initial values */
+ void resetCameraPosition();
+
+ /** Get set the scene camera position */
+ QVector3D cameraPosition() const;
+ void setCameraPosition(const QVector3D &newCameraPosition);
+
+ /** Get set the scene camera position */
+ QVector3D cameraUpVector() const;
+ void setCameraUpVector(const QVector3D &newCameraUpVector);
+
+ /** Get set the scene camera position */
+ QVector3D cameraViewCenter() const;
+ void setCameraViewCenter(const QVector3D &newCameraViewCenter);
+
protected:
/** Returns a string with the mesh location, suitable to be used by QML */
QString meshDirectory() const;
@@ -135,6 +155,11 @@ public slots:
/** @reimp */
void slotRefreshView();
+signals:
+ void cameraPositionChanged();
+ void cameraUpVectorChanged();
+ void cameraViewCenterChanged();
+
private:
/** Reference to the Doc Monitor properties */
MonitorProperties *m_monProps;
@@ -147,6 +172,10 @@ public slots:
QQmlComponent *m_fillGBufferLayer;
int m_createItemCount;
+ QVector3D m_cameraPosition;
+ QVector3D m_cameraUpVector;
+ QVector3D m_cameraViewCenter;
+
/*********************************************************************
* Frame counter
*********************************************************************/
diff --git a/qmlui/mainviewdmx.cpp b/qmlui/mainviewdmx.cpp
index a1aae20cc0..6084f5955b 100644
--- a/qmlui/mainviewdmx.cpp
+++ b/qmlui/mainviewdmx.cpp
@@ -103,6 +103,9 @@ void MainViewDMX::createFixtureItem(quint32 fxID)
MonitorProperties *monProps = m_doc->monitorProperties();
quint32 itemFlags = monProps->fixtureFlags(fxID, 0, 0);
+ if (monProps->containsFixture(fxID) == false)
+ monProps->setFixturePosition(fxID, 0, 0, QVector3D(0, 0, 0));
+
newFixtureItem->setParentItem(contextItem());
newFixtureItem->setProperty("fixtureObj", QVariant::fromValue(fixture));
if (itemFlags & MonitorProperties::HiddenFlag)
diff --git a/qmlui/qml/ActionsMenu.qml b/qmlui/qml/ActionsMenu.qml
index cc0f53c6bf..3d43d00fb4 100644
--- a/qmlui/qml/ActionsMenu.qml
+++ b/qmlui/qml/ActionsMenu.qml
@@ -161,6 +161,7 @@ Popup
border.width: 1
border.color: UISettings.bgStronger
color: UISettings.bgStrong
+ height: actionsMenuEntries.height
}
Column
@@ -307,6 +308,7 @@ Popup
ContextMenuEntry
{
Layout.fillWidth: true
+ Layout.fillHeight: true
imgSource: "qrc:/undo.svg"
entryText: qsTr("Undo")
onEntered: submenuItem = null
@@ -320,6 +322,7 @@ Popup
ContextMenuEntry
{
Layout.fillWidth: true
+ Layout.fillHeight: true
imgSource: "qrc:/redo.svg"
entryText: qsTr("Redo")
onEntered: submenuItem = null
diff --git a/qmlui/qml/BeatGeneratorsPanel.qml b/qmlui/qml/BeatGeneratorsPanel.qml
index 6c2a4b9d59..2e9acfa967 100644
--- a/qmlui/qml/BeatGeneratorsPanel.qml
+++ b/qmlui/qml/BeatGeneratorsPanel.qml
@@ -20,6 +20,8 @@
import QtQuick 2.6
import QtQuick.Controls 2.1
import QtQuick.Layouts 1.1
+
+import "GenericHelpers.js" as Helpers
import "."
Rectangle
@@ -89,6 +91,7 @@ Rectangle
width: parent.width
height: UISettings.bigItemHeight * 2
+ clip: true
boundsBehavior: Flickable.StopAtBounds
delegate:
@@ -99,11 +102,11 @@ Rectangle
Component.onCompleted:
{
- if (modelData.type === "MIDI")
+ if (modelData.type === "PLUGIN")
{
iconBox.color = "white"
iconBox.visible = true
- genIcon.source = "qrc:/midiplugin.svg"
+ genIcon.source = Helpers.pluginIconFromName(modelData.privateName)
}
else if (modelData.type === "AUDIO")
{
@@ -151,6 +154,7 @@ Rectangle
{
id: keyPadBox
width: parent.width
+ height: UISettings.iconSizeDefault * 6
showDMXcontrol: false
showTapButton: true
visible: ioManager.beatType === "INTERNAL"
diff --git a/qmlui/qml/ChannelToolLoader.qml b/qmlui/qml/ChannelToolLoader.qml
index 011b41d81a..c00368254f 100644
--- a/qmlui/qml/ChannelToolLoader.qml
+++ b/qmlui/qml/ChannelToolLoader.qml
@@ -23,10 +23,10 @@ import QtQuick.Controls 2.5
import org.qlcplus.classes 1.0
import "."
-Popup
+Item
{
- id: popupRoot
- padding: 0
+ id: itemRoot
+ visible: false
property int fixtureId: -1
property int channelType: -1
@@ -39,13 +39,14 @@ Popup
function loadChannelTool(cItem, fxId, chIdx, val)
{
channelType = fixtureManager.channelType(fxId, chIdx)
+
if (channelType === QLCChannel.NoGroup || channelType === QLCChannel.Nothing)
return
var map = cItem.mapToItem(parent, cItem.x, cItem.y)
toolLoader.source = ""
- x = map.x
- yPos = map.y
+ x = map.x + UISettings.iconSizeMedium
+ y = map.y
fixtureId = fxId
channelIndex = chIdx
channelValue = val
@@ -59,7 +60,6 @@ Popup
case QLCChannel.Tilt:
toolLoader.source = "qrc:/SingleAxisTool.qml"
break
-
case QLCChannel.Colour:
case QLCChannel.Gobo:
case QLCChannel.Speed:
@@ -82,34 +82,35 @@ Popup
onLoaded:
{
- popupRoot.y = popupRoot.yPos - height
- popupRoot.width = width
- popupRoot.height = height
+ itemRoot.width = width
+ itemRoot.height = height
item.showPalette = false
- //item.closeOnSelect = true
-
- popupRoot.open()
+ if (item.hasOwnProperty('dragTarget'))
+ item.dragTarget = itemRoot
if (channelType >= 0xFF)
{
- item.currentValue = popupRoot.channelValue
- item.targetColor = popupRoot.channelType
+ item.currentValue = itemRoot.channelValue
+ item.targetColor = itemRoot.channelType
}
else if (channelType == QLCChannel.Intensity)
{
- item.currentValue = popupRoot.channelValue
+ item.show(itemRoot.channelValue)
}
else if (channelType == QLCChannel.Pan ||
channelType == QLCChannel.Tilt)
{
- item.currentValue = popupRoot.channelValue
- item.maxDegrees = fixtureManager.channelDegrees(popupRoot.fixtureId, popupRoot.channelIndex)
+ item.currentValue = itemRoot.channelValue
+ item.maxDegrees = fixtureManager.channelDegrees(itemRoot.fixtureId, itemRoot.channelIndex)
}
else
{
- item.updatePresets(fixtureManager.presetChannel(popupRoot.fixtureId, popupRoot.channelIndex))
+ item.updatePresets(fixtureManager.presetChannel(itemRoot.fixtureId, itemRoot.channelIndex))
}
+
+ item.closeOnSelect = true
+ itemRoot.visible = true
}
Connections
@@ -118,7 +119,12 @@ Popup
target: toolLoader.item
function onValueChanged()
{
- popupRoot.valueChanged(popupRoot.fixtureId, popupRoot.channelIndex, target.currentValue)
+ itemRoot.valueChanged(itemRoot.fixtureId, itemRoot.channelIndex, target.currentValue)
+ }
+ function onClose()
+ {
+ itemRoot.visible = false
+ toolLoader.source = ""
}
}
}
diff --git a/qmlui/qml/ColorTool.qml b/qmlui/qml/ColorTool.qml
index 7cbd048994..5f92b432a6 100644
--- a/qmlui/qml/ColorTool.qml
+++ b/qmlui/qml/ColorTool.qml
@@ -33,6 +33,7 @@ Rectangle
color: UISettings.bgMedium
property bool closeOnSelect: false
+ property var dragTarget: null
property int colorsMask: 0
property color currentRGB
property color currentWAUV
@@ -164,7 +165,7 @@ Rectangle
{
Layout.fillWidth: true
height: colorToolBar.height
- drag.target: paletteBox.isEditing ? null : colorToolBox
+ drag.target: paletteBox.isEditing ? null : (colorToolBox.dragTarget ? colorToolBox.dragTarget : colorToolBox)
}
GenericButton
{
diff --git a/qmlui/qml/ColorToolFull.qml b/qmlui/qml/ColorToolFull.qml
index 1d9af3b3aa..8bd92eb0ba 100644
--- a/qmlui/qml/ColorToolFull.qml
+++ b/qmlui/qml/ColorToolFull.qml
@@ -125,6 +125,7 @@ Rectangle
bSpin.value = b*/
currentRGB = Qt.rgba(r / 255, g / 255, b / 255, 1.0)
+ colorChanged(currentRGB.r, currentRGB.g, currentRGB.b, currentWAUV.r, currentWAUV.g, currentWAUV.b)
}
onPressed: setPickedColor(mouse)
diff --git a/qmlui/qml/ColorToolPrimary.qml b/qmlui/qml/ColorToolPrimary.qml
index f3be683beb..5240ebe99e 100644
--- a/qmlui/qml/ColorToolPrimary.qml
+++ b/qmlui/qml/ColorToolPrimary.qml
@@ -38,6 +38,7 @@ Rectangle
property bool showPalette: false
signal valueChanged(int value)
+ signal close()
Canvas
{
@@ -92,7 +93,8 @@ Rectangle
{
anchors.fill: parent
- onClicked: {
+ function calculateValue(mouse)
+ {
var val = 0
if (mouse.x < width * 0.1)
@@ -112,6 +114,20 @@ Rectangle
boxRoot.currentValue = val
boxRoot.valueChanged(val)
}
+
+ onPressed: calculateValue()
+ onPositionChanged:
+ {
+ if (!pressed)
+ return
+
+ calculateValue(mouse)
+ }
+ onReleased:
+ {
+ if (closeOnSelect)
+ boxRoot.close()
+ }
}
}
}
diff --git a/qmlui/qml/ExternalControlDelegate.qml b/qmlui/qml/ExternalControlDelegate.qml
index ea01b2bc72..e7398a7963 100644
--- a/qmlui/qml/ExternalControlDelegate.qml
+++ b/qmlui/qml/ExternalControlDelegate.qml
@@ -155,7 +155,7 @@ Column
Layout.fillWidth: true
height: UISettings.listItemHeight
visible: customFeedback
- label: qsTr("Custom feedbacks")
+ label: qsTr("Custom feedback")
color: UISettings.bgMedium
}
diff --git a/qmlui/qml/GenericMultiDragItem.qml b/qmlui/qml/GenericMultiDragItem.qml
index e3ca4e4759..b354a9d6ee 100644
--- a/qmlui/qml/GenericMultiDragItem.qml
+++ b/qmlui/qml/GenericMultiDragItem.qml
@@ -1,6 +1,6 @@
/*
Q Light Controller Plus
- FunctionDragItem.qml
+ GenericMultiDragItem.qml
Copyright (c) Massimo Callegari
diff --git a/qmlui/qml/KeyPad.qml b/qmlui/qml/KeyPad.qml
index 217e1db541..ffa0f51c0e 100644
--- a/qmlui/qml/KeyPad.qml
+++ b/qmlui/qml/KeyPad.qml
@@ -34,7 +34,7 @@ Rectangle
property bool showDMXcontrol: true
property bool showTapButton: false
-
+
property alias commandString: commandBox.text
property real itemHeight: Math.max(UISettings.iconSizeDefault, keyPadRoot.height / keyPadGrid.rows) - 3
@@ -109,15 +109,15 @@ Rectangle
else
{
var currTime = new Date().getTime()
-
+
if (lastTap != 0 && currTime - lastTap < 1500)
{
var newTime = currTime - lastTap
-
+
tapHistory.push(newTime)
tapTimeValue = TimeUtils.calculateBPMByTapIntervals(tapHistory)
-
+
keyPadRoot.tapTimeChanged(tapTimeValue)
tapTimer.interval = tapTimeValue
tapTimer.restart()
diff --git a/qmlui/qml/MainView.qml b/qmlui/qml/MainView.qml
index 58ef1fc426..5204b48b1a 100644
--- a/qmlui/qml/MainView.qml
+++ b/qmlui/qml/MainView.qml
@@ -144,6 +144,7 @@ Rectangle
MenuBarEntry
{
id: actEntry
+ Layout.alignment: Qt.AlignTop
imgSource: "qrc:/qlcplus.svg"
entryText: qsTr("Actions")
onPressed: actionsMenu.open()
@@ -165,6 +166,7 @@ Rectangle
{
id: fnfEntry
property string ctxName: "FIXANDFUNC"
+ Layout.alignment: Qt.AlignTop
property string ctxRes: "qrc:/FixturesAndFunctions.qml"
imgSource: "qrc:/editor.svg"
@@ -180,6 +182,7 @@ Rectangle
MenuBarEntry
{
id: vcEntry
+ Layout.alignment: Qt.AlignTop
property string ctxName: "VC"
property string ctxRes: "qrc:/VirtualConsole.qml"
@@ -201,6 +204,7 @@ Rectangle
MenuBarEntry
{
id: sdEntry
+ Layout.alignment: Qt.AlignTop
property string ctxName: "SDESK"
property string ctxRes: "qrc:/SimpleDesk.qml"
@@ -222,6 +226,7 @@ Rectangle
MenuBarEntry
{
id: smEntry
+ Layout.alignment: Qt.AlignTop
property string ctxName: "SHOWMGR"
property string ctxRes: "qrc:/ShowManager.qml"
@@ -243,6 +248,7 @@ Rectangle
MenuBarEntry
{
id: ioEntry
+ Layout.alignment: Qt.AlignTop
property string ctxName: "IOMGR"
property string ctxRes: "qrc:/InputOutputManager.qml"
@@ -265,7 +271,7 @@ Rectangle
{
// acts like an horizontal spacer
Layout.fillWidth: true
- height: parent.height
+ implicitHeight: parent.height
color: "transparent"
}
RobotoText
@@ -273,6 +279,9 @@ Rectangle
label: "BPM: " + (ioManager.bpmNumber > 0 ? ioManager.bpmNumber : qsTr("Off"))
color: gsMouseArea.containsMouse ? UISettings.bgLight : "transparent"
fontSize: UISettings.textSizeDefault
+ Layout.alignment: Qt.AlignTop
+ implicitWidth: width
+ implicitHeight: parent.height
MouseArea
{
@@ -294,8 +303,9 @@ Rectangle
Rectangle
{
id: beatIndicator
- width: height
- height: parent.height * 0.5
+ implicitWidth: height
+ implicitHeight: parent.height * 0.5
+ Layout.alignment: Qt.AlignVCenter
radius: height / 2
border.width: 2
border.color: "#333"
@@ -324,8 +334,9 @@ Rectangle
IconButton
{
id: stopAllButton
- width: UISettings.iconSizeDefault
- height: UISettings.iconSizeDefault
+ implicitWidth: UISettings.iconSizeDefault
+ implicitHeight: UISettings.iconSizeDefault
+ Layout.alignment: Qt.AlignTop
enabled: runningCount ? true : false
bgColor: "transparent"
imgSource: "qrc:/stop.svg"
diff --git a/qmlui/qml/QLCPlusKnob.qml b/qmlui/qml/QLCPlusKnob.qml
index f9b1de0c46..f30cfffdf1 100644
--- a/qmlui/qml/QLCPlusKnob.qml
+++ b/qmlui/qml/QLCPlusKnob.qml
@@ -17,8 +17,8 @@
limitations under the License.
*/
-import QtQuick 2.0
-import QtQuick.Controls 2.0
+import QtQuick 2.14
+import QtQuick.Controls 2.14
import "CanvasDrawFunctions.js" as DrawFuncs
import "."
@@ -33,6 +33,8 @@ Dial
from: 0
to: 255
+ stepSize: 1.0
+ wheelEnabled: true
onPositionChanged: kCanvas.requestPaint()
onHeightChanged: kCanvas.requestPaint()
diff --git a/qmlui/qml/SimpleDesk.qml b/qmlui/qml/SimpleDesk.qml
index a9570025a8..0f0beb57d5 100644
--- a/qmlui/qml/SimpleDesk.qml
+++ b/qmlui/qml/SimpleDesk.qml
@@ -46,6 +46,7 @@ Rectangle
{
anchors.fill: parent
orientation: Qt.Vertical
+ z: 1
// Top view (faders)
Rectangle
@@ -254,7 +255,8 @@ Rectangle
from: 0
to: 255
value: model.chValue
- onMoved: {
+ onMoved:
+ {
model.isOverride = true
model.chValue = valueAt(position)
simpleDesk.setValue(fixtureObj ? fixtureObj.id : -1, fixtureObj ? model.chIndex : index, model.chValue)
@@ -274,7 +276,8 @@ Rectangle
padding: 0
horizontalAlignment: Qt.AlignHCenter
value: dmxValues ? model.chValue : (model.chValue / 255.0) * 100.0
- onValueModified: {
+ onValueModified:
+ {
model.isOverride = true
model.chValue = value * (dmxValues ? 1.0 : 2.55)
simpleDesk.setValue(fixtureObj ? fixtureObj.id : -1, fixtureObj ? model.chIndex : index, model.chValue)
diff --git a/qmlui/qml/SingleAxisTool.qml b/qmlui/qml/SingleAxisTool.qml
index 965492cd8f..9d773934d0 100644
--- a/qmlui/qml/SingleAxisTool.qml
+++ b/qmlui/qml/SingleAxisTool.qml
@@ -39,6 +39,7 @@ Rectangle
property bool showPalette: false
signal valueChanged(int value)
+ signal close()
GridLayout
{
@@ -60,6 +61,11 @@ Rectangle
currentValue = Math.round((valueAt(position) * 255.0) / maxDegrees)
boxRoot.valueChanged(currentValue)
}
+ onPressedChanged:
+ {
+ if (!pressed && closeOnSelect)
+ boxRoot.close()
+ }
}
RobotoText
diff --git a/qmlui/qml/TreeNodeDelegate.qml b/qmlui/qml/TreeNodeDelegate.qml
index f421b52760..1b9f1bea3c 100644
--- a/qmlui/qml/TreeNodeDelegate.qml
+++ b/qmlui/qml/TreeNodeDelegate.qml
@@ -103,6 +103,7 @@ Column
id: nodeLabel
width: nodeBgRect.width - x - 1
text: cRef ? cRef.name : textLabel
+ originalText: text
onTextConfirmed: nodeContainer.pathChanged(nodePath, text)
}
diff --git a/qmlui/qml/UISettingsEditor.qml b/qmlui/qml/UISettingsEditor.qml
index 664a6b9595..7afe49446b 100644
--- a/qmlui/qml/UISettingsEditor.qml
+++ b/qmlui/qml/UISettingsEditor.qml
@@ -30,13 +30,16 @@ Rectangle
anchors.fill: parent
color: "transparent"
- property real origItemHeight: UISettings.listItemHeight
- property real origIconMedium: UISettings.iconSizeMedium
+ property real origItemHeight: { origItemHeight = UISettings.listItemHeight }
+ property real origIconMedium: { origIconMedium = UISettings.iconSizeMedium }
+ property real origTextSizeDefault: { origTextSizeDefault = UISettings.textSizeDefault}
+ property real origIconDefault: {origIconDefault = UISettings.iconSizeDefault}
onVisibleChanged:
{
origItemHeight = UISettings.listItemHeight
origIconMedium = UISettings.iconSizeMedium
+ origTextSizeDefault = UISettings.textSizeDefault
sfRestore.origScaleFactor = qlcplus.uiScaleFactor
}
@@ -135,6 +138,7 @@ Rectangle
RobotoText
{
height: origItemHeight
+ fontSize: origTextSizeDefault
label: qsTr("Scaling factor")
}
RowLayout
@@ -153,6 +157,7 @@ Rectangle
RobotoText
{
height: origItemHeight
+ fontSize: origTextSizeDefault
label: sfSlider.value.toFixed(2) + "x"
}
@@ -181,6 +186,7 @@ Rectangle
RobotoText
{
height: origItemHeight
+ fontSize: origTextSizeDefault
label: qsTr("Background darker")
}
Loader
@@ -199,6 +205,7 @@ Rectangle
RobotoText
{
height: origItemHeight
+ fontSize: origTextSizeDefault
label: qsTr("Background dark")
}
Loader
@@ -218,6 +225,7 @@ Rectangle
RobotoText
{
height: origItemHeight
+ fontSize: origTextSizeDefault
label: qsTr("Background medium")
}
Loader
@@ -236,6 +244,7 @@ Rectangle
RobotoText
{
height: origItemHeight
+ fontSize: origTextSizeDefault
label: qsTr("Background light")
}
Loader
@@ -255,6 +264,7 @@ Rectangle
RobotoText
{
height: origItemHeight
+ fontSize: origTextSizeDefault
label: qsTr("Background lighter")
}
Loader
@@ -273,6 +283,7 @@ Rectangle
RobotoText
{
height: origItemHeight
+ fontSize: origTextSizeDefault
label: qsTr("Controls background")
}
Loader
@@ -292,6 +303,7 @@ Rectangle
RobotoText
{
height: origItemHeight
+ fontSize: origTextSizeDefault
label: qsTr("Foreground main")
}
Loader
@@ -310,6 +322,7 @@ Rectangle
RobotoText
{
height: origItemHeight
+ fontSize: origTextSizeDefault
label: qsTr("Foreground medium")
}
Loader
@@ -329,6 +342,7 @@ Rectangle
RobotoText
{
height: origItemHeight
+ fontSize: origTextSizeDefault
label: qsTr("Foreground light")
}
Loader
@@ -347,6 +361,7 @@ Rectangle
RobotoText
{
height: origItemHeight
+ fontSize: origTextSizeDefault
label: qsTr("Toolbar gradient start")
}
Loader
@@ -366,6 +381,7 @@ Rectangle
RobotoText
{
height: origItemHeight
+ fontSize: origTextSizeDefault
label: qsTr("Sub-toolbar gradient start")
}
Loader
@@ -384,6 +400,7 @@ Rectangle
RobotoText
{
height: origItemHeight
+ fontSize: origTextSizeDefault
label: qsTr("Toolbar gradient end")
}
Loader
@@ -403,6 +420,7 @@ Rectangle
RobotoText
{
height: origItemHeight
+ fontSize: origTextSizeDefault
label: qsTr("Toolbar hover gradient start")
}
Loader
@@ -422,6 +440,7 @@ Rectangle
RobotoText
{
height: origItemHeight
+ fontSize: origTextSizeDefault
label: qsTr("Toolbar hover gradient end")
}
Loader
@@ -441,6 +460,7 @@ Rectangle
RobotoText
{
height: origItemHeight
+ fontSize: origTextSizeDefault
label: qsTr("Toolbar selection")
}
Loader
@@ -460,6 +480,7 @@ Rectangle
RobotoText
{
height: origItemHeight
+ fontSize: origTextSizeDefault
label: qsTr("Sub-toolbar selection")
}
Loader
@@ -479,6 +500,7 @@ Rectangle
RobotoText
{
height: origItemHeight
+ fontSize: origTextSizeDefault
label: qsTr("Section header")
}
Loader
@@ -497,6 +519,7 @@ Rectangle
RobotoText
{
height: origItemHeight
+ fontSize: origTextSizeDefault
label: qsTr("Section header divider")
}
Loader
@@ -516,6 +539,7 @@ Rectangle
RobotoText
{
height: origItemHeight
+ fontSize: origTextSizeDefault
label: qsTr("Item highlight")
}
Loader
@@ -534,6 +558,7 @@ Rectangle
RobotoText
{
height: origItemHeight
+ fontSize: origTextSizeDefault
label: qsTr("Item highlight pressed")
}
Loader
@@ -553,6 +578,7 @@ Rectangle
RobotoText
{
height: origItemHeight
+ fontSize: origTextSizeDefault
label: qsTr("Item hover")
}
Loader
@@ -571,6 +597,7 @@ Rectangle
RobotoText
{
height: origItemHeight
+ fontSize: origTextSizeDefault
label: qsTr("Item selection")
}
Loader
@@ -590,6 +617,7 @@ Rectangle
RobotoText
{
height: origItemHeight
+ fontSize: origTextSizeDefault
label: qsTr("VC Frame drop area")
}
Loader
@@ -608,6 +636,7 @@ Rectangle
RobotoText
{
height: origItemHeight
+ fontSize: origTextSizeDefault
label: qsTr("Item dark border")
}
Loader
@@ -628,6 +657,8 @@ Rectangle
Layout.columnSpan: 4
Layout.alignment: Qt.AlignHCenter
width: origIconMedium * 10
+ height: origIconDefault
+ fontSize: origTextSizeDefault
label: qsTr("Save to file")
onClicked:
{
diff --git a/qmlui/qml/fixtureeditor/ModeEditor.qml b/qmlui/qml/fixtureeditor/ModeEditor.qml
index d3e05b8e1d..1deab6a26f 100644
--- a/qmlui/qml/fixtureeditor/ModeEditor.qml
+++ b/qmlui/qml/fixtureeditor/ModeEditor.qml
@@ -294,10 +294,15 @@ Rectangle
iSrc: mcDelegate.cRef ? mcDelegate.cRef.getIconNameFromGroup(mcDelegate.cRef.group, true) : ""
}
Rectangle { width: 1; height: UISettings.listItemHeight }
+
CustomComboBox
{
implicitWidth: UISettings.bigItemHeight * 2
height: UISettings.listItemHeight
+ model: mode ? mode.actsOnChannels : null
+ textRole: ""
+ currentIndex: mode ? mode.actsOnChannel(index) : -1
+ onCurrentIndexChanged: if (mode) mode.setActsOnChannel(index, currentIndex)
}
}
diff --git a/qmlui/qml/fixturesfunctions/3DView/3DView.qml b/qmlui/qml/fixturesfunctions/3DView/3DView.qml
index 393f6fb2cb..c88a28baee 100644
--- a/qmlui/qml/fixturesfunctions/3DView/3DView.qml
+++ b/qmlui/qml/fixturesfunctions/3DView/3DView.qml
@@ -323,16 +323,6 @@ Rectangle
objectName: "scene3DEntity"
Component.onCompleted: contextManager.enableContext("3D", true, scene3d)
-/*
- OrbitCameraController
- {
- id: camController
- camera: sceneEntity.camera
- linearSpeed: 40.0
- lookSpeed: 300.0
- }
-*/
-
// Global elements
Camera
{
@@ -343,9 +333,9 @@ Rectangle
aspectRatio: viewSize.width / viewSize.height
nearPlane: 1.0
farPlane: 1000.0
- position: Qt.vector3d(0.0, 3.0, 7.5)
- upVector: Qt.vector3d(0.0, 1.0, 0.0)
- viewCenter: Qt.vector3d(0.0, 1.0, 0.0)
+ position: View3D.cameraPosition
+ upVector: View3D.cameraUpVector
+ viewCenter: View3D.cameraViewCenter
function setZoom(amount)
{
@@ -455,6 +445,10 @@ Rectangle
viewCamera.panAboutViewCenter(-xDelta, Qt.vector3d(0, 1, 0))
if (!mouse.modifiers || (mouse.modifiers & Qt.ShiftModifier && direction == Qt.Vertical))
viewCamera.tiltAboutViewCenter(yDelta, Qt.vector3d(1, 0, 0))
+
+ View3D.cameraPosition = viewCamera.position
+ View3D.cameraUpVector = viewCamera.upVector
+ View3D.cameraViewCenter = viewCamera.viewCenter
}
else if (mouse.buttons === Qt.MiddleButton) // camera translation
{
@@ -462,6 +456,10 @@ Rectangle
viewCamera.translate(Qt.vector3d(-xDelta / 100, 0, 0))
if (!mouse.modifiers || (mouse.modifiers & Qt.ShiftModifier && direction == Qt.Vertical))
viewCamera.translate(Qt.vector3d(0, yDelta / 100, 0))
+
+ View3D.cameraPosition = viewCamera.position
+ View3D.cameraUpVector = viewCamera.upVector
+ View3D.cameraViewCenter = viewCamera.viewCenter
}
startPoint = Qt.point(mouse.x, mouse.y)
}
diff --git a/qmlui/qml/fixturesfunctions/DMXView.qml b/qmlui/qml/fixturesfunctions/DMXView.qml
index a4df0b86ab..f471b579d5 100644
--- a/qmlui/qml/fixturesfunctions/DMXView.qml
+++ b/qmlui/qml/fixturesfunctions/DMXView.qml
@@ -83,7 +83,7 @@ Rectangle
}
Component.onCompleted: contextManager.enableContext("DMX", true, flowLayout)
- Component.onDestruction: if(contextManager) contextManager.enableContext("DMX", false, flowLayout)
+ Component.onDestruction: if (contextManager) contextManager.enableContext("DMX", false, flowLayout)
}
ScrollBar.vertical: CustomScrollBar { }
diff --git a/qmlui/qml/fixturesfunctions/FixtureNodeDelegate.qml b/qmlui/qml/fixturesfunctions/FixtureNodeDelegate.qml
index 9a44a25cd9..739e084412 100644
--- a/qmlui/qml/fixturesfunctions/FixtureNodeDelegate.qml
+++ b/qmlui/qml/fixturesfunctions/FixtureNodeDelegate.qml
@@ -132,6 +132,7 @@ Column
id: nodeLabel
Layout.fillWidth: true
text: textLabel
+ originalText: text
onTextConfirmed:
{
diff --git a/qmlui/qml/fixturesfunctions/FixtureProperties.qml b/qmlui/qml/fixturesfunctions/FixtureProperties.qml
index 8633e93f17..e6a18e6cf4 100644
--- a/qmlui/qml/fixturesfunctions/FixtureProperties.qml
+++ b/qmlui/qml/fixturesfunctions/FixtureProperties.qml
@@ -18,7 +18,7 @@
*/
import QtQuick 2.3
-import QtQuick.Controls 2.1
+import QtQuick.Controls 2.14
import QtQuick.Layouts 1.1
import "."
diff --git a/qmlui/qml/fixturesfunctions/FixturesAndFunctions.qml b/qmlui/qml/fixturesfunctions/FixturesAndFunctions.qml
index 2593da1142..69dac04182 100644
--- a/qmlui/qml/fixturesfunctions/FixturesAndFunctions.qml
+++ b/qmlui/qml/fixturesfunctions/FixturesAndFunctions.qml
@@ -135,6 +135,8 @@ Rectangle
onCheckedChanged: loadContext(checked, "qrc:/UniverseGridView.qml", "UNIGRID")
onRightClicked:
{
+ if (checked)
+ dmxView.checked = true
uniView.visible = false
contextManager.detachContext("UNIGRID")
}
@@ -151,6 +153,8 @@ Rectangle
onCheckedChanged: loadContext(checked, "qrc:/DMXView.qml", "DMX")
onRightClicked:
{
+ if (checked)
+ uniView.checked = true
dmxView.visible = false
contextManager.detachContext("DMX")
}
@@ -168,6 +172,8 @@ Rectangle
onCheckedChanged: loadContext(checked, "qrc:/2DView.qml", "2D")
onRightClicked:
{
+ if (checked)
+ dmxView.checked = true
twodView.visible = false
contextManager.detachContext("2D")
}
@@ -193,6 +199,8 @@ Rectangle
}
onRightClicked:
{
+ if (checked)
+ twodView.checked = true
threedView.visible = false
contextManager.detachContext("3D")
}
diff --git a/qmlui/qml/fixturesfunctions/GridEditor.qml b/qmlui/qml/fixturesfunctions/GridEditor.qml
index c8e2eac887..165a6f7b27 100644
--- a/qmlui/qml/fixturesfunctions/GridEditor.qml
+++ b/qmlui/qml/fixturesfunctions/GridEditor.qml
@@ -222,17 +222,31 @@ Rectangle
onTriggered: calculateCellSize()
}
+ Text
+ {
+ id: ttText
+
+ property string tooltipText: ""
+ visible: false
+ x: gridMouseArea.mouseX
+ y: gridMouseArea.mouseY
+ ToolTip.visible: ttText.visible
+ ToolTip.timeout: 3000
+ ToolTip.text: tooltipText
+ }
+
Timer
{
id: ttTimer
+ repeat: false
interval: 1000
- running: gridMouseArea.containsMouse
+ running: false
onTriggered:
{
var xPos = parseInt(gridMouseArea.mouseX / cellSize)
var yPos = parseInt(gridMouseArea.mouseY / cellSize)
- var tooltip = getTooltip(xPos, yPos)
- Tooltip.showText(gridMouseArea, Qt.point(gridMouseArea.mouseX, gridMouseArea.mouseY), tooltip)
+ ttText.tooltipText = getTooltip(xPos, yPos)
+ ttText.visible = true
}
}
@@ -343,6 +357,7 @@ Rectangle
onPositionChanged:
{
+ ttText.visible = false
ttTimer.restart()
if (movingSelection == false)
@@ -368,8 +383,7 @@ Rectangle
updateViewSelection(selectionOffset)
}
- onExited: Tooltip.hideText()
- onCanceled: Tooltip.hideText()
+ onExited: ttTimer.stop()
}
DropArea
diff --git a/qmlui/qml/fixturesfunctions/IntensityTool.qml b/qmlui/qml/fixturesfunctions/IntensityTool.qml
index 61ebe7ca3f..8192a71950 100644
--- a/qmlui/qml/fixturesfunctions/IntensityTool.qml
+++ b/qmlui/qml/fixturesfunctions/IntensityTool.qml
@@ -35,6 +35,7 @@ Rectangle
property bool dmxValues: true
property bool closeOnSelect: false
+ property var dragTarget: null
property alias showPalette: paletteBox.visible
property alias currentValue: spinBox.value
@@ -55,34 +56,34 @@ Rectangle
else
{
var val = relativeValue ? currentValue - previousValue : currentValue
- intRoot.valueChanged(dmxValues ? val : val * 2.55)
- if (closeOnSelect)
- intRoot.visible = false
+ if (intRoot.visible)
+ intRoot.valueChanged(dmxValues ? val : val * 2.55)
+ //if (closeOnSelect)
+ // intRoot.close()
}
previousValue = currentValue
}
onVisibleChanged:
{
- if (visible)
+ if (!visible)
+ paletteBox.checked = false
+ }
+
+ function show(value)
+ {
+ previousValue = 0
+ if (value === -1)
{
- previousValue = 0
- var val = contextManager.getCurrentValue(QLCChannel.Intensity, false)
- if (val === -1)
- {
- relativeValue = true
- currentValue = 0
- }
- else
- {
- relativeValue = false
- currentValue = dmxValues ? Math.round(val) : Math.round(val / 2.55)
- }
+ relativeValue = true
+ currentValue = 0
}
else
{
- paletteBox.checked = false
+ relativeValue = false
+ currentValue = dmxValues ? Math.round(value) : Math.round(value / 2.55)
}
+ visible = true
}
function loadPalette(pId)
@@ -137,7 +138,7 @@ Rectangle
MouseArea
{
anchors.fill: parent
- drag.target: intRoot
+ drag.target: intRoot.dragTarget ? intRoot.dragTarget : intRoot
}
GenericButton
@@ -232,7 +233,7 @@ Rectangle
suffix: dmxValues ? "" : "%"
to: dmxValues ? 255 : 100
- onValueChanged: currentValue = value
+ onValueModified: currentValue = value
}
DMXPercentageButton
diff --git a/qmlui/qml/fixturesfunctions/LeftPanel.qml b/qmlui/qml/fixturesfunctions/LeftPanel.qml
index afab32e9f5..0d170aff97 100644
--- a/qmlui/qml/fixturesfunctions/LeftPanel.qml
+++ b/qmlui/qml/fixturesfunctions/LeftPanel.qml
@@ -99,7 +99,10 @@ SidePanel
onToggled:
{
if (checked == true)
+ {
loaderSource = "qrc:/FixtureGroupManager.qml"
+ fixtureManager.searchFilter = ""
+ }
animatePanel(checked)
}
}
@@ -133,7 +136,16 @@ SidePanel
tooltip: qsTr("Intensity")
counter: 0
ButtonGroup.group: capabilitiesGroup
- onCheckedChanged: intTool.visible = !intTool.visible
+ onCheckedChanged:
+ {
+ if (checked)
+ {
+ var val = contextManager.getCurrentValue(QLCChannel.Intensity, false)
+ intTool.show(val)
+ }
+ else
+ intTool.visible = false
+ }
onCounterChanged: if (counter == 0) intTool.visible = false
IntensityTool
diff --git a/qmlui/qml/fixturesfunctions/PresetCapabilityItem.qml b/qmlui/qml/fixturesfunctions/PresetCapabilityItem.qml
index a1a0a5d3af..922fe02773 100644
--- a/qmlui/qml/fixturesfunctions/PresetCapabilityItem.qml
+++ b/qmlui/qml/fixturesfunctions/PresetCapabilityItem.qml
@@ -27,6 +27,7 @@ Rectangle
id: iRoot
width: UISettings.bigItemHeight * 1.5
height: UISettings.iconSizeDefault * 1.2
+ color: capMouseArea.pressed ? UISettings.bgLight : "white"
border.width: 1
border.color: UISettings.borderColorDark
@@ -63,7 +64,7 @@ Rectangle
if (Qt.platform.os === "android")
pic.source = resArray[0]
else
- pic.source = "file:/" + resArray[0]
+ pic.source = "file:" + resArray[0]
}
}
@@ -137,14 +138,13 @@ Rectangle
}
MouseArea
{
+ id: capMouseArea
anchors.fill: parent
hoverEnabled: true
preventStealing: false
onPositionChanged: capBar.width = mouse.x
onExited: capBar.width = 0
- onPressed: iRoot.color = UISettings.bgLight
- onReleased: iRoot.color = "white"
onClicked:
{
var value = ((capability.max - capability.min) * capBar.width) / iRoot.width
diff --git a/qmlui/qml/fixturesfunctions/PresetsTool.qml b/qmlui/qml/fixturesfunctions/PresetsTool.qml
index 17d07bd963..f239becbda 100644
--- a/qmlui/qml/fixturesfunctions/PresetsTool.qml
+++ b/qmlui/qml/fixturesfunctions/PresetsTool.qml
@@ -38,6 +38,8 @@ Rectangle
property int selectedChannel: -1
property bool showPalette: false
property int currentValue: 0 // as DMX value
+ property int rangeLowLimit: 0
+ property int rangeHighLimit: 255
signal presetSelected(QLCCapability cap, int fxID, int chIdx, int value)
signal valueChanged(int value)
@@ -152,11 +154,13 @@ Rectangle
{
capability: modelData
capIndex: index + 1
+ visible: (capability.min <= toolRoot.rangeHighLimit || capability.max <= toolRoot.rangeLowLimit)
onValueChanged:
{
- toolRoot.currentValue = value
- toolRoot.presetSelected(capability, selectedFixture, selectedChannel, value)
- toolRoot.valueChanged(value)
+ var val = Math.min(Math.max(value, rangeLowLimit), rangeHighLimit)
+ toolRoot.currentValue = val
+ toolRoot.presetSelected(capability, selectedFixture, selectedChannel, val)
+ toolRoot.valueChanged(val)
if (closeOnSelect)
toolRoot.visible = false
}
diff --git a/qmlui/qml/fixturesfunctions/RGBPanelProperties.qml b/qmlui/qml/fixturesfunctions/RGBPanelProperties.qml
index c6251b69a9..bfcbee19fd 100644
--- a/qmlui/qml/fixturesfunctions/RGBPanelProperties.qml
+++ b/qmlui/qml/fixturesfunctions/RGBPanelProperties.qml
@@ -18,6 +18,7 @@
*/
import QtQuick 2.3
+import QtQuick.Controls 2.14
import QtQuick.Layouts 1.1
import org.qlcplus.classes 1.0
diff --git a/qmlui/qml/fixturesfunctions/UniverseGridView.qml b/qmlui/qml/fixturesfunctions/UniverseGridView.qml
index 1c6eb4c200..81694b84c0 100644
--- a/qmlui/qml/fixturesfunctions/UniverseGridView.qml
+++ b/qmlui/qml/fixturesfunctions/UniverseGridView.qml
@@ -30,8 +30,9 @@ Flickable
boundsBehavior: Flickable.StopAtBounds
contentHeight: uniGrid.height + topbar.height + UISettings.bigItemHeight
+ property alias contextItem: uniGrid
property string contextName: "UNIGRID"
- property int uniStartAddr: viewUniverseCombo.currentIndex * 512
+ property int uniStartAddr: contextManager.universeFilter * 512
property var fixtureClipboard: null
function hasSettings()
@@ -44,7 +45,7 @@ Flickable
id: errorPopup
standardButtons: Dialog.Ok
title: qsTr("Error")
- message: qsTr("Unable to perform the operation.\nThere is either not enough space or the target universe in invalid")
+ message: qsTr("Unable to perform the operation.\nThere is either not enough space or the target universe is invalid")
onAccepted: close()
}
@@ -60,7 +61,7 @@ Flickable
id: uniText
height: UISettings.textSizeDefault * 2
labelColor: UISettings.fgLight
- label: viewUniverseCombo.currentText
+ label: ioManager.universeName(contextManager.universeFilter)
fontSize: UISettings.textSizeDefault * 1.5
fontBold: true
}
@@ -101,13 +102,16 @@ Flickable
x: UISettings.iconSizeMedium
anchors.top: topbar.bottom
width: parent.width - (UISettings.iconSizeMedium * 3)
- height: cellSize * gridSize.height
+ height: 32 * gridSize.height
showIndices: 512
gridSize: Qt.size(24, 22)
gridLabels: fixtureManager.fixtureNamesMap
gridData: fixtureManager.fixturesMap
+ Component.onCompleted: contextManager.enableContext("UNIGRID", true, uniGrid)
+ Component.onDestruction: if (contextManager) contextManager.enableContext("UNIGRID", false, uniGrid)
+
property int prevFixtureID: -1
function getItemIcon(itemID, chNumber)
@@ -135,6 +139,7 @@ Flickable
if (multiSelection === 0)
contextManager.resetFixtureSelection()
+ console.log("prevFixtureID: " + prevFixtureID + "currentItemID: " + currentItemID)
if (prevFixtureID != currentItemID && multiSelection === 0)
contextManager.setFixtureIDSelection(prevFixtureID, false)
diff --git a/qmlui/qml/fixturesfunctions/qmldir b/qmlui/qml/fixturesfunctions/qmldir
index 9c33c9b773..c270f28276 100644
--- a/qmlui/qml/fixturesfunctions/qmldir
+++ b/qmlui/qml/fixturesfunctions/qmldir
@@ -7,6 +7,7 @@ ColorToolFull 0.1 ../ColorToolFull.qml
ContextMenuEntry 0.1 ../ContextMenuEntry.qml
CustomCheckBox 0.1 ../CustomCheckBox.qml
CustomComboBox 0.1 ../CustomComboBox.qml
+CustomPopupDialog 0.1 ../popup/CustomPopupDialog.qml
CustomScrollBar 0.1 ../CustomScrollBar.qml
CustomSpinBox 0.1 ../CustomSpinBox.qml
CustomDoubleSpinBox 0.1 ../CustomDoubleSpinBox.qml
diff --git a/qmlui/qml/inputoutput/UniverseIOItem.qml b/qmlui/qml/inputoutput/UniverseIOItem.qml
index 8a4816d87c..688ebc820d 100644
--- a/qmlui/qml/inputoutput/UniverseIOItem.qml
+++ b/qmlui/qml/inputoutput/UniverseIOItem.qml
@@ -151,7 +151,7 @@ Rectangle
z: 10
patchesNumber: inputPatchesNumber
- showFeedback: universe ? universe.hasFeedbacks : false
+ showFeedback: universe ? universe.hasFeedback : false
}
// Input patch drop area
@@ -266,8 +266,8 @@ Rectangle
checkedColor: "green"
imgSource: ""
checkable: true
- checked: universe ? universe.hasFeedbacks : false
- tooltip: qsTr("Enable/Disable feedbacks")
+ checked: universe ? universe.hasFeedback : false
+ tooltip: qsTr("Enable/Disable feedback")
onToggled:
{
if (universe)
diff --git a/qmlui/qml/popup/PopupAbout.qml b/qmlui/qml/popup/PopupAbout.qml
index 41b4fa9deb..5282488cbe 100644
--- a/qmlui/qml/popup/PopupAbout.qml
+++ b/qmlui/qml/popup/PopupAbout.qml
@@ -57,6 +57,8 @@ CustomPopupDialog
" " +
qsTr("Apache 2.0 license") + "."
onLinkActivated: Qt.openUrlExternally(link)
+ Layout.fillWidth: true
+ wrapMode: Text.WordWrap
MouseArea
{
diff --git a/qmlui/qml/virtualconsole/VCAnimationItem.qml b/qmlui/qml/virtualconsole/VCAnimationItem.qml
new file mode 100644
index 0000000000..c855c09ea1
--- /dev/null
+++ b/qmlui/qml/virtualconsole/VCAnimationItem.qml
@@ -0,0 +1,66 @@
+/*
+ Q Light Controller Plus
+ VCAnimationItem.qml
+
+ Copyright (c) Massimo Callegari
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0.txt
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+import QtQuick 2.0
+import QtQuick.Layouts 1.0
+import QtQuick.Controls 2.2
+
+import org.qlcplus.classes 1.0
+import "."
+
+VCWidgetItem
+{
+ id: animationRoot
+ property VCAnimation animationObj: null
+
+ clip: true
+
+ onAnimationObjChanged:
+ {
+ setCommonProperties(animationObj)
+ }
+
+ Row
+ {
+ anchors.fill: parent
+
+ // value text box
+ Text
+ {
+ width: parent.width
+ height: parent.height
+ color: "#bbb"
+ lineHeight: 0.8
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ textFormat: Text.RichText
+ wrapMode: Text.Wrap
+ text: "VCAnimation not implemented yet.
See QML Status"
+
+ onLinkActivated: Qt.openUrlExternally(link)
+
+ MouseArea
+ {
+ anchors.fill: parent
+ acceptedButtons: Qt.NoButton
+ cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
+ }
+ }
+ }
+}
diff --git a/qmlui/qml/virtualconsole/VCAnimationProperties.qml b/qmlui/qml/virtualconsole/VCAnimationProperties.qml
new file mode 100644
index 0000000000..d0b061f249
--- /dev/null
+++ b/qmlui/qml/virtualconsole/VCAnimationProperties.qml
@@ -0,0 +1,66 @@
+/*
+ Q Light Controller Plus
+ VCAnimationProperties.qml
+
+ Copyright (c) Massimo Callegari
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0.txt
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+import QtQuick 2.0
+import QtQuick.Layouts 1.1
+import QtQuick.Controls 2.1
+
+import org.qlcplus.classes 1.0
+import "."
+
+Rectangle
+{
+ id: propsRoot
+ color: "transparent"
+ height: animationPropsColumn.height
+
+ property VCAnimation widgetRef: null
+
+ property int gridItemsHeight: UISettings.listItemHeight
+
+ Column
+ {
+ id: animationPropsColumn
+ width: parent.width
+ spacing: 5
+
+ SectionBox
+ {
+ id: animationProp
+ sectionLabel: qsTr("Animation Properties")
+
+ sectionContents:
+ GridLayout
+ {
+ width: parent.width
+ columns: 2
+ columnSpacing: 5
+ rowSpacing: 4
+
+ // row 1
+ RobotoText
+ {
+ height: gridItemsHeight
+ Layout.fillWidth: true
+ label: "Not implemented."
+ }
+ } // GridLayout
+ } // SectionBox
+ } // Column
+}
diff --git a/qmlui/qml/virtualconsole/VCAudioTriggerItem.qml b/qmlui/qml/virtualconsole/VCAudioTriggerItem.qml
new file mode 100644
index 0000000000..f93186d298
--- /dev/null
+++ b/qmlui/qml/virtualconsole/VCAudioTriggerItem.qml
@@ -0,0 +1,66 @@
+/*
+ Q Light Controller Plus
+ VCAudioTriggerItem.qml
+
+ Copyright (c) Massimo Callegari
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0.txt
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+import QtQuick 2.0
+import QtQuick.Layouts 1.0
+import QtQuick.Controls 2.2
+
+import org.qlcplus.classes 1.0
+import "."
+
+VCWidgetItem
+{
+ id: audioTriggerRoot
+ property VCAudioTrigger audioTriggerObj: null
+
+ clip: true
+
+ onAudioTriggerObjChanged:
+ {
+ setCommonProperties(audioTriggerObj)
+ }
+
+ Row
+ {
+ anchors.fill: parent
+
+ // value text box
+ Text
+ {
+ width: parent.width
+ height: parent.height
+ color: "#bbb"
+ lineHeight: 0.8
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ textFormat: Text.RichText
+ wrapMode: Text.Wrap
+ text: "VCAudioTrigger not implemented yet.
See QML Status"
+
+ onLinkActivated: Qt.openUrlExternally(link)
+
+ MouseArea
+ {
+ anchors.fill: parent
+ acceptedButtons: Qt.NoButton
+ cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
+ }
+ }
+ }
+}
diff --git a/qmlui/qml/virtualconsole/VCAudioTriggerProperties.qml b/qmlui/qml/virtualconsole/VCAudioTriggerProperties.qml
new file mode 100644
index 0000000000..3e8dd5d1db
--- /dev/null
+++ b/qmlui/qml/virtualconsole/VCAudioTriggerProperties.qml
@@ -0,0 +1,66 @@
+/*
+ Q Light Controller Plus
+ VCAudioTriggerProperties.qml
+
+ Copyright (c) Massimo Callegari
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0.txt
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+import QtQuick 2.0
+import QtQuick.Layouts 1.1
+import QtQuick.Controls 2.1
+
+import org.qlcplus.classes 1.0
+import "."
+
+Rectangle
+{
+ id: propsRoot
+ color: "transparent"
+ height: audioTriggerPropsColumn.height
+
+ property VCAudioTrigger widgetRef: null
+
+ property int gridItemsHeight: UISettings.listItemHeight
+
+ Column
+ {
+ id: audioTriggerPropsColumn
+ width: parent.width
+ spacing: 5
+
+ SectionBox
+ {
+ id: audioTriggerProp
+ sectionLabel: qsTr("Audio Trigger Properties")
+
+ sectionContents:
+ GridLayout
+ {
+ width: parent.width
+ columns: 2
+ columnSpacing: 5
+ rowSpacing: 4
+
+ // row 1
+ RobotoText
+ {
+ height: gridItemsHeight
+ Layout.fillWidth: true
+ label: "Not implemented."
+ }
+ } // GridLayout
+ } // SectionBox
+ } // Column
+}
diff --git a/qmlui/qml/virtualconsole/VCSliderItem.qml b/qmlui/qml/virtualconsole/VCSliderItem.qml
index dcea323ffe..2209ca1954 100644
--- a/qmlui/qml/virtualconsole/VCSliderItem.qml
+++ b/qmlui/qml/virtualconsole/VCSliderItem.qml
@@ -17,7 +17,7 @@
limitations under the License.
*/
-import QtQuick 2.0
+import QtQuick 2.14
import QtQuick.Layouts 1.1
import org.qlcplus.classes 1.0
@@ -149,7 +149,7 @@ VCWidgetItem
to: sliderObj ? sliderObj.rangeHighLimit : 255
value: sliderValue
- onMoved: if (sliderObj) sliderObj.value = value // position * 255
+ onValueChanged: if (sliderObj) sliderObj.value = value
}
// widget name text box
@@ -194,6 +194,24 @@ VCWidgetItem
onClicked: if (sliderObj) sliderObj.isOverriding = false
}
+ IconButton
+ {
+ visible: sliderObj ? sliderObj.adjustFlashEnabled : false
+ Layout.alignment: Qt.AlignHCenter
+ imgSource: "qrc:/flash.svg"
+ tooltip: qsTr("Flash the controlled Function")
+ onPressed:
+ {
+ if (sliderObj)
+ sliderObj.flashFunction(true)
+ }
+ onReleased:
+ {
+ if (sliderObj)
+ sliderObj.flashFunction(false)
+ }
+ }
+
// Click & Go button
IconButton
{
@@ -230,7 +248,7 @@ VCWidgetItem
if (Qt.platform.os === "android")
presetImageBox.source = cngResource
else
- presetImageBox.source = "file:/" + cngResource
+ presetImageBox.source = "file:" + cngResource
}
onClicked: colorToolLoader.toggleVisibility()
@@ -285,6 +303,11 @@ VCWidgetItem
{
item.visible = false
item.closeOnSelect = true
+ if (sliderObj && clickAndGoButton.cngType == VCSlider.CnGPreset)
+ {
+ item.rangeLowLimit = sliderObj.rangeLowLimit
+ item.rangeHighLimit = sliderObj.rangeHighLimit
+ }
}
Connections
diff --git a/qmlui/qml/virtualconsole/VCSliderProperties.qml b/qmlui/qml/virtualconsole/VCSliderProperties.qml
index 33e77ab8f7..fd7c087e49 100644
--- a/qmlui/qml/virtualconsole/VCSliderProperties.qml
+++ b/qmlui/qml/virtualconsole/VCSliderProperties.qml
@@ -257,6 +257,21 @@ Rectangle
currentIndex: widgetRef ? widgetRef.controlledAttribute : 0
onCurrentIndexChanged: if (widgetRef) widgetRef.controlledAttribute = currentIndex
}
+
+ CustomCheckBox
+ {
+ implicitWidth: UISettings.iconSizeMedium
+ implicitHeight: implicitWidth
+ checked: widgetRef ? widgetRef.adjustFlashEnabled : false
+ onClicked: if (widgetRef) widgetRef.adjustFlashEnabled = checked
+ }
+
+ RobotoText
+ {
+ height: gridItemsHeight
+ Layout.fillWidth: true
+ label: qsTr("Show flash button")
+ }
} // GridLayout
} // SectionBox Function control
diff --git a/qmlui/qml/virtualconsole/VCSpeedDialItem.qml b/qmlui/qml/virtualconsole/VCSpeedDialItem.qml
new file mode 100644
index 0000000000..e653de4c4c
--- /dev/null
+++ b/qmlui/qml/virtualconsole/VCSpeedDialItem.qml
@@ -0,0 +1,66 @@
+/*
+ Q Light Controller Plus
+ VCSpeedDialItem.qml
+
+ Copyright (c) Massimo Callegari
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0.txt
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+import QtQuick 2.0
+import QtQuick.Layouts 1.0
+import QtQuick.Controls 2.2
+
+import org.qlcplus.classes 1.0
+import "."
+
+VCWidgetItem
+{
+ id: speedDialRoot
+ property VCSpeedDial speedDialObj: null
+
+ clip: true
+
+ onSpeedDialObjChanged:
+ {
+ setCommonProperties(speedDialObj)
+ }
+
+ Row
+ {
+ anchors.fill: parent
+
+ // value text box
+ Text
+ {
+ width: parent.width
+ height: parent.height
+ color: "#bbb"
+ lineHeight: 0.8
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ textFormat: Text.RichText
+ wrapMode: Text.Wrap
+ text: "VCSpeedDial not implemented yet.
See QML Status"
+
+ onLinkActivated: Qt.openUrlExternally(link)
+
+ MouseArea
+ {
+ anchors.fill: parent
+ acceptedButtons: Qt.NoButton
+ cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
+ }
+ }
+ }
+}
diff --git a/qmlui/qml/virtualconsole/VCSpeedDialProperties.qml b/qmlui/qml/virtualconsole/VCSpeedDialProperties.qml
new file mode 100644
index 0000000000..476993e100
--- /dev/null
+++ b/qmlui/qml/virtualconsole/VCSpeedDialProperties.qml
@@ -0,0 +1,66 @@
+/*
+ Q Light Controller Plus
+ VCSpeedDialProperties.qml
+
+ Copyright (c) Massimo Callegari
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0.txt
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+import QtQuick 2.0
+import QtQuick.Layouts 1.1
+import QtQuick.Controls 2.1
+
+import org.qlcplus.classes 1.0
+import "."
+
+Rectangle
+{
+ id: propsRoot
+ color: "transparent"
+ height: speedDialPropsColumn.height
+
+ property VCSpeedDial widgetRef: null
+
+ property int gridItemsHeight: UISettings.listItemHeight
+
+ Column
+ {
+ id: speedDialPropsColumn
+ width: parent.width
+ spacing: 5
+
+ SectionBox
+ {
+ id: speedDialProp
+ sectionLabel: qsTr("Speed Dial Properties")
+
+ sectionContents:
+ GridLayout
+ {
+ width: parent.width
+ columns: 2
+ columnSpacing: 5
+ rowSpacing: 4
+
+ // row 1
+ RobotoText
+ {
+ height: gridItemsHeight
+ Layout.fillWidth: true
+ label: "Not implemented."
+ }
+ } // GridLayout
+ } // SectionBox
+ } // Column
+}
diff --git a/qmlui/qml/virtualconsole/VCXYPadItem.qml b/qmlui/qml/virtualconsole/VCXYPadItem.qml
new file mode 100644
index 0000000000..bf3f813f89
--- /dev/null
+++ b/qmlui/qml/virtualconsole/VCXYPadItem.qml
@@ -0,0 +1,66 @@
+/*
+ Q Light Controller Plus
+ VCXYPadItem.qml
+
+ Copyright (c) Massimo Callegari
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0.txt
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+import QtQuick 2.0
+import QtQuick.Layouts 1.0
+import QtQuick.Controls 2.2
+
+import org.qlcplus.classes 1.0
+import "."
+
+VCWidgetItem
+{
+ id: xyPadRoot
+ property VCXYPad xyPadObj: null
+
+ clip: true
+
+ onXyPadObjChanged:
+ {
+ setCommonProperties(xyPadObj)
+ }
+
+ Row
+ {
+ anchors.fill: parent
+
+ // value text box
+ Text
+ {
+ width: parent.width
+ height: parent.height
+ color: "#bbb"
+ lineHeight: 0.8
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ textFormat: Text.RichText
+ wrapMode: Text.Wrap
+ text: "VCXYPad not implemented yet.
See QML Status"
+
+ onLinkActivated: Qt.openUrlExternally(link)
+
+ MouseArea
+ {
+ anchors.fill: parent
+ acceptedButtons: Qt.NoButton
+ cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
+ }
+ }
+ }
+}
diff --git a/qmlui/qml/virtualconsole/VCXYPadProperties.qml b/qmlui/qml/virtualconsole/VCXYPadProperties.qml
new file mode 100644
index 0000000000..519a51b4fb
--- /dev/null
+++ b/qmlui/qml/virtualconsole/VCXYPadProperties.qml
@@ -0,0 +1,66 @@
+/*
+ Q Light Controller Plus
+ VCXYPadProperties.qml
+
+ Copyright (c) Massimo Callegari
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0.txt
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+import QtQuick 2.0
+import QtQuick.Layouts 1.1
+import QtQuick.Controls 2.1
+
+import org.qlcplus.classes 1.0
+import "."
+
+Rectangle
+{
+ id: propsRoot
+ color: "transparent"
+ height: xyPadPropsColumn.height
+
+ property VCXYPad widgetRef: null
+
+ property int gridItemsHeight: UISettings.listItemHeight
+
+ Column
+ {
+ id: xyPadPropsColumn
+ width: parent.width
+ spacing: 5
+
+ SectionBox
+ {
+ id: xyPadProp
+ sectionLabel: qsTr("XY Pad Properties")
+
+ sectionContents:
+ GridLayout
+ {
+ width: parent.width
+ columns: 2
+ columnSpacing: 5
+ rowSpacing: 4
+
+ // row 1
+ RobotoText
+ {
+ height: gridItemsHeight
+ Layout.fillWidth: true
+ label: "Not implemented."
+ }
+ } // GridLayout
+ } // SectionBox
+ } // Column
+}
diff --git a/qmlui/qml/virtualconsole/qmldir b/qmlui/qml/virtualconsole/qmldir
index 15add3510b..2d813e6045 100644
--- a/qmlui/qml/virtualconsole/qmldir
+++ b/qmlui/qml/virtualconsole/qmldir
@@ -22,6 +22,7 @@ IconPopupButton 0.1 ../IconPopupButton.qml
IconTextEntry 0.1 ../IconTextEntry.qml
MenuBarEntry 0.1 ../MenuBarEntry.qml
QLCPlusFader 0.1 ../QLCPlusFader.qml
+QLCPlusKnob 0.1 ../QLCPlusKnob.qml
RobotoText 0.1 ../RobotoText.qml
SectionBox 0.1 ../SectionBox.qml
SidePanel 0.1 ../SidePanel.qml
diff --git a/qmlui/qmlui.pro b/qmlui/qmlui.pro
index 41473b6f3a..9eec79c66b 100644
--- a/qmlui/qmlui.pro
+++ b/qmlui/qmlui.pro
@@ -23,7 +23,14 @@ INCLUDEPATH += ../plugins/interfaces
INCLUDEPATH += ../plugins/midi/src/common
DEPENDPATH += ../engine/src
QMAKE_LIBDIR += ../engine/src
-LIBS += -lqlcplusengine
+
+android {
+ LIBS += -lqlcplusengine_$${QT_ARCH}
+} else {
+ LIBS += -lqlcplusengine
+}
+
+
#win32:QMAKE_LFLAGS += -shared
win32:RC_FILE = qmlui.rc
@@ -126,6 +133,10 @@ HEADERS += \
virtualconsole/vcbutton.h \
virtualconsole/vclabel.h \
virtualconsole/vcslider.h \
+ virtualconsole/vcanimation.h \
+ virtualconsole/vcaudiotrigger.h \
+ virtualconsole/vcxypad.h \
+ virtualconsole/vcspeeddial.h \
virtualconsole/vcclock.h \
virtualconsole/vccuelist.h
@@ -138,6 +149,10 @@ SOURCES += \
virtualconsole/vcbutton.cpp \
virtualconsole/vclabel.cpp \
virtualconsole/vcslider.cpp \
+ virtualconsole/vcanimation.cpp \
+ virtualconsole/vcaudiotrigger.cpp \
+ virtualconsole/vcxypad.cpp \
+ virtualconsole/vcspeeddial.cpp \
virtualconsole/vcclock.cpp \
virtualconsole/vccuelist.cpp
diff --git a/qmlui/qmlui.qrc b/qmlui/qmlui.qrc
index ec4b6dfdd8..e0c168ee2b 100644
--- a/qmlui/qmlui.qrc
+++ b/qmlui/qmlui.qrc
@@ -233,6 +233,14 @@
qml/virtualconsole/VCLabelItem.qml
qml/virtualconsole/VCSliderItem.qml
qml/virtualconsole/VCSliderProperties.qml
+ qml/virtualconsole/VCAnimationItem.qml
+ qml/virtualconsole/VCAnimationProperties.qml
+ qml/virtualconsole/VCAudioTriggerItem.qml
+ qml/virtualconsole/VCAudioTriggerProperties.qml
+ qml/virtualconsole/VCXYPadItem.qml
+ qml/virtualconsole/VCXYPadProperties.qml
+ qml/virtualconsole/VCSpeedDialItem.qml
+ qml/virtualconsole/VCSpeedDialProperties.qml
qml/virtualconsole/VCClockItem.qml
qml/virtualconsole/VCClockProperties.qml
qml/virtualconsole/VCCueListItem.qml
diff --git a/qmlui/treemodel.cpp b/qmlui/treemodel.cpp
index bbd8881ad3..b4ca42f743 100644
--- a/qmlui/treemodel.cpp
+++ b/qmlui/treemodel.cpp
@@ -136,7 +136,7 @@ TreeModelItem *TreeModel::addItem(QString label, QVariantList data, QString path
QStringList pathList = path.split(TreeModel::separator());
if (m_itemsPathMap.contains(pathList.at(0)))
{
- item = m_itemsPathMap[pathList.at(0)];
+ item = m_itemsPathMap.value(pathList.at(0), nullptr);
}
else
{
@@ -202,7 +202,10 @@ TreeModelItem *TreeModel::itemAtPath(QString path)
return nullptr;
}
- TreeModelItem *item = m_itemsPathMap[pathList.at(0)];
+ TreeModelItem *item = m_itemsPathMap.value(pathList.at(0), nullptr);
+ if (item == nullptr)
+ return nullptr;
+
QString subPath = path.mid(path.indexOf(TreeModel::separator()) + 1);
return item->children()->itemAtPath(subPath);
}
@@ -236,7 +239,10 @@ bool TreeModel::removeItem(QString path)
}
else
{
- TreeModelItem *item = m_itemsPathMap[pathList.at(0)];
+ TreeModelItem *item = m_itemsPathMap.value(pathList.at(0), nullptr);
+ if (item == nullptr)
+ return false;
+
QString subPath = path.mid(path.indexOf(TreeModel::separator()) + 1);
item->children()->removeItem(subPath);
}
@@ -270,7 +276,10 @@ void TreeModel::setItemRoleData(QString path, const QVariant &value, int role)
}
else
{
- TreeModelItem *item = m_itemsPathMap[pathList.at(0)];
+ TreeModelItem *item = m_itemsPathMap.value(pathList.at(0), nullptr);
+ if (item == nullptr)
+ return;
+
QString subPath = path.mid(path.indexOf(TreeModel::separator()) + 1);
item->children()->setItemRoleData(subPath, value, role);
}
@@ -300,18 +309,18 @@ void TreeModel::setPathData(QString path, QVariantList data)
return;
QStringList pathList = path.split(TreeModel::separator());
- if (m_itemsPathMap.contains(pathList.at(0)))
+ TreeModelItem *item = m_itemsPathMap.value(pathList.at(0), nullptr);
+ if (item == nullptr)
+ return;
+
+ if (pathList.count() == 1)
{
- TreeModelItem *item = m_itemsPathMap[pathList.at(0)];
- if (pathList.count() == 1)
- {
- item->setData(data);
- }
- else if (item->hasChildren())
- {
- QString subPath = path.mid(path.indexOf(TreeModel::separator()) + 1);
- item->children()->setPathData(subPath, data);
- }
+ item->setData(data);
+ }
+ else if (item->hasChildren())
+ {
+ QString subPath = path.mid(path.indexOf(TreeModel::separator()) + 1);
+ item->children()->setPathData(subPath, data);
}
}
diff --git a/qmlui/virtualconsole/vcanimation.cpp b/qmlui/virtualconsole/vcanimation.cpp
new file mode 100644
index 0000000000..1ddd37e6d9
--- /dev/null
+++ b/qmlui/virtualconsole/vcanimation.cpp
@@ -0,0 +1,169 @@
+/*
+ Q Light Controller Plus
+ vcanimation.cpp
+
+ Copyright (c) Massimo Callegari
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0.txt
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+#include
+#include
+#include
+
+#include "doc.h"
+#include "vcanimation.h"
+
+VCAnimation::VCAnimation(Doc *doc, QObject *parent)
+ : VCWidget(doc, parent)
+{
+ setType(VCWidget::AnimationWidget);
+}
+
+VCAnimation::~VCAnimation()
+{
+ if (m_item)
+ delete m_item;
+}
+
+QString VCAnimation::defaultCaption()
+{
+ return tr("Animation %1").arg(id() + 1);
+}
+
+void VCAnimation::setupLookAndFeel(qreal pixelDensity, int page)
+{
+ setPage(page);
+ QFont wFont = font();
+ wFont.setBold(true);
+ wFont.setPointSize(pixelDensity * 5.0);
+ setFont(wFont);
+}
+
+void VCAnimation::render(QQuickView *view, QQuickItem *parent)
+{
+ if (view == nullptr || parent == nullptr)
+ return;
+
+ QQmlComponent *component = new QQmlComponent(view->engine(), QUrl("qrc:/VCAnimationItem.qml"));
+
+ if (component->isError())
+ {
+ qDebug() << component->errors();
+ return;
+ }
+
+ m_item = qobject_cast(component->create());
+
+ m_item->setParentItem(parent);
+ m_item->setProperty("animationObj", QVariant::fromValue(this));
+}
+
+QString VCAnimation::propertiesResource() const
+{
+ return QString("qrc:/VCAnimationProperties.qml");
+}
+
+VCWidget *VCAnimation::createCopy(VCWidget *parent)
+{
+ Q_ASSERT(parent != nullptr);
+
+ VCAnimation *animation = new VCAnimation(m_doc, parent);
+ if (animation->copyFrom(this) == false)
+ {
+ delete animation;
+ animation = nullptr;
+ }
+
+ return animation;
+}
+
+bool VCAnimation::copyFrom(const VCWidget *widget)
+{
+ const VCAnimation *animation = qobject_cast (widget);
+ if (animation == nullptr)
+ return false;
+
+ /* Copy and set properties */
+
+ /* Copy object lists */
+
+ /* Common stuff */
+ return VCWidget::copyFrom(widget);
+}
+
+FunctionParent VCAnimation::functionParent() const
+{
+ return FunctionParent(FunctionParent::AutoVCWidget, id());
+}
+
+/*********************************************************************
+ * Load & Save
+ *********************************************************************/
+
+bool VCAnimation::loadXML(QXmlStreamReader &root)
+{
+ if (root.name() != KXMLQLCVCAnimation)
+ {
+ qWarning() << Q_FUNC_INFO << "Animation node not found";
+ return false;
+ }
+
+ QXmlStreamAttributes attrs = root.attributes();
+
+ /* Widget commons */
+ loadXMLCommon(root);
+
+ while (root.readNextStartElement())
+ {
+ if (root.name() == KXMLQLCWindowState)
+ {
+ bool visible = false;
+ int x = 0, y = 0, w = 0, h = 0;
+ loadXMLWindowState(root, &x, &y, &w, &h, &visible);
+ setGeometry(QRect(x, y, w, h));
+ }
+ else if (root.name() == KXMLQLCVCWidgetAppearance)
+ {
+ loadXMLAppearance(root);
+ }
+ else
+ {
+ qWarning() << Q_FUNC_INFO << "Unknown animation tag:" << root.name().toString();
+ root.skipCurrentElement();
+ }
+ }
+
+ return true;
+}
+
+bool VCAnimation::saveXML(QXmlStreamWriter *doc)
+{
+ Q_ASSERT(doc != nullptr);
+
+ /* VC object entry */
+ doc->writeStartElement(KXMLQLCVCAnimation);
+
+ saveXMLCommon(doc);
+
+ /* Window state */
+ saveXMLWindowState(doc);
+
+ /* Appearance */
+ saveXMLAppearance(doc);
+
+ /* Write the tag */
+ doc->writeEndElement();
+
+ return true;
+}
diff --git a/qmlui/virtualconsole/vcanimation.h b/qmlui/virtualconsole/vcanimation.h
new file mode 100644
index 0000000000..bf7259512a
--- /dev/null
+++ b/qmlui/virtualconsole/vcanimation.h
@@ -0,0 +1,103 @@
+/*
+ Q Light Controller Plus
+ vcanimation.h
+
+ Copyright (c) Massimo Callegari
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0.txt
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+#ifndef VCANIMATION_H
+#define VCANIMATION_H
+
+#include "vcwidget.h"
+
+#define KXMLQLCVCAnimation QString("Control")
+#define KXMLQLCVCAnimationID QString("ID")
+#define KXMLQLCVCAnimationType QString("Type")
+#define KXMLQLCVCAnimationColor QString("Color")
+#define KXMLQLCVCAnimationResource QString("Resource")
+#define KXMLQLCVCAnimationProperty QString("Property")
+#define KXMLQLCVCAnimationPropertyName QString("Name")
+
+class VCAnimation : public VCWidget
+{
+ Q_OBJECT
+
+ /*********************************************************************
+ * Initialization
+ *********************************************************************/
+public:
+ VCAnimation(Doc* doc = nullptr, QObject *parent = nullptr);
+ virtual ~VCAnimation();
+
+ /** @reimp */
+ QString defaultCaption();
+
+ /** @reimp */
+ void setupLookAndFeel(qreal pixelDensity, int page);
+
+ /** @reimp */
+ void render(QQuickView *view, QQuickItem *parent);
+
+ /** @reimp */
+ QString propertiesResource() const;
+
+ /** @reimp */
+ VCWidget *createCopy(VCWidget *parent);
+
+protected:
+ /** @reimp */
+ bool copyFrom(const VCWidget* widget);
+
+private:
+ FunctionParent functionParent() const;
+
+ /*********************************************************************
+ * Type
+ *********************************************************************/
+public:
+
+signals:
+
+private:
+
+ /*********************************************************************
+ * Data
+ *********************************************************************/
+public:
+
+protected slots:
+
+signals:
+
+private:
+
+ /*********************************************************************
+ * Functions connections
+ *********************************************************************/
+public:
+
+signals:
+
+private:
+
+ /*********************************************************************
+ * Load & Save
+ *********************************************************************/
+public:
+ bool loadXML(QXmlStreamReader &root);
+ bool saveXML(QXmlStreamWriter *doc);
+};
+
+#endif
diff --git a/qmlui/virtualconsole/vcaudiotrigger.cpp b/qmlui/virtualconsole/vcaudiotrigger.cpp
new file mode 100644
index 0000000000..3e5773bd60
--- /dev/null
+++ b/qmlui/virtualconsole/vcaudiotrigger.cpp
@@ -0,0 +1,169 @@
+/*
+ Q Light Controller Plus
+ vcaudiotrigger.cpp
+
+ Copyright (c) Massimo Callegari
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0.txt
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+#include
+#include
+#include
+
+#include "doc.h"
+#include "vcaudiotrigger.h"
+
+VCAudioTrigger::VCAudioTrigger(Doc *doc, QObject *parent)
+ : VCWidget(doc, parent)
+{
+ setType(VCWidget::AudioTriggersWidget);
+}
+
+VCAudioTrigger::~VCAudioTrigger()
+{
+ if (m_item)
+ delete m_item;
+}
+
+QString VCAudioTrigger::defaultCaption()
+{
+ return tr("Audio Trigger %1").arg(id() + 1);
+}
+
+void VCAudioTrigger::setupLookAndFeel(qreal pixelDensity, int page)
+{
+ setPage(page);
+ QFont wFont = font();
+ wFont.setBold(true);
+ wFont.setPointSize(pixelDensity * 5.0);
+ setFont(wFont);
+}
+
+void VCAudioTrigger::render(QQuickView *view, QQuickItem *parent)
+{
+ if (view == nullptr || parent == nullptr)
+ return;
+
+ QQmlComponent *component = new QQmlComponent(view->engine(), QUrl("qrc:/VCAudioTriggerItem.qml"));
+
+ if (component->isError())
+ {
+ qDebug() << component->errors();
+ return;
+ }
+
+ m_item = qobject_cast(component->create());
+
+ m_item->setParentItem(parent);
+ m_item->setProperty("audioTriggerObj", QVariant::fromValue(this));
+}
+
+QString VCAudioTrigger::propertiesResource() const
+{
+ return QString("qrc:/VCAudioTriggerProperties.qml");
+}
+
+VCWidget *VCAudioTrigger::createCopy(VCWidget *parent)
+{
+ Q_ASSERT(parent != nullptr);
+
+ VCAudioTrigger *audioTrigger = new VCAudioTrigger(m_doc, parent);
+ if (audioTrigger->copyFrom(this) == false)
+ {
+ delete audioTrigger;
+ audioTrigger = nullptr;
+ }
+
+ return audioTrigger;
+}
+
+bool VCAudioTrigger::copyFrom(const VCWidget *widget)
+{
+ const VCAudioTrigger *audioTrigger = qobject_cast (widget);
+ if (audioTrigger == nullptr)
+ return false;
+
+ /* Copy and set properties */
+
+ /* Copy object lists */
+
+ /* Common stuff */
+ return VCWidget::copyFrom(widget);
+}
+
+FunctionParent VCAudioTrigger::functionParent() const
+{
+ return FunctionParent(FunctionParent::AutoVCWidget, id());
+}
+
+/*********************************************************************
+ * Load & Save
+ *********************************************************************/
+
+bool VCAudioTrigger::loadXML(QXmlStreamReader &root)
+{
+ if (root.name() != KXMLQLCVCAudioTriggers)
+ {
+ qWarning() << Q_FUNC_INFO << "Audio trigger node not found";
+ return false;
+ }
+
+ QXmlStreamAttributes attrs = root.attributes();
+
+ /* Widget commons */
+ loadXMLCommon(root);
+
+ while (root.readNextStartElement())
+ {
+ if (root.name() == KXMLQLCWindowState)
+ {
+ bool visible = false;
+ int x = 0, y = 0, w = 0, h = 0;
+ loadXMLWindowState(root, &x, &y, &w, &h, &visible);
+ setGeometry(QRect(x, y, w, h));
+ }
+ else if (root.name() == KXMLQLCVCWidgetAppearance)
+ {
+ loadXMLAppearance(root);
+ }
+ else
+ {
+ qWarning() << Q_FUNC_INFO << "Unknown audio trigger tag:" << root.name().toString();
+ root.skipCurrentElement();
+ }
+ }
+
+ return true;
+}
+
+bool VCAudioTrigger::saveXML(QXmlStreamWriter *doc)
+{
+ Q_ASSERT(doc != nullptr);
+
+ /* VC object entry */
+ doc->writeStartElement(KXMLQLCVCAudioTriggers);
+
+ saveXMLCommon(doc);
+
+ /* Window state */
+ saveXMLWindowState(doc);
+
+ /* Appearance */
+ saveXMLAppearance(doc);
+
+ /* Write the tag */
+ doc->writeEndElement();
+
+ return true;
+}
diff --git a/qmlui/virtualconsole/vcaudiotrigger.h b/qmlui/virtualconsole/vcaudiotrigger.h
new file mode 100644
index 0000000000..c12b3bd7c8
--- /dev/null
+++ b/qmlui/virtualconsole/vcaudiotrigger.h
@@ -0,0 +1,97 @@
+/*
+ Q Light Controller Plus
+ vcaudiotrigger.h
+
+ Copyright (c) Massimo Callegari
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0.txt
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+#ifndef VCAUDIOTRIGGER_H
+#define VCAUDIOTRIGGER_H
+
+#include "vcwidget.h"
+
+#define KXMLQLCVCAudioTriggers QString("AudioTriggers")
+
+class VCAudioTrigger : public VCWidget
+{
+ Q_OBJECT
+
+ /*********************************************************************
+ * Initialization
+ *********************************************************************/
+public:
+ VCAudioTrigger(Doc* doc = nullptr, QObject *parent = nullptr);
+ virtual ~VCAudioTrigger();
+
+ /** @reimp */
+ QString defaultCaption();
+
+ /** @reimp */
+ void setupLookAndFeel(qreal pixelDensity, int page);
+
+ /** @reimp */
+ void render(QQuickView *view, QQuickItem *parent);
+
+ /** @reimp */
+ QString propertiesResource() const;
+
+ /** @reimp */
+ VCWidget *createCopy(VCWidget *parent);
+
+protected:
+ /** @reimp */
+ bool copyFrom(const VCWidget* widget);
+
+private:
+ FunctionParent functionParent() const;
+
+ /*********************************************************************
+ * Type
+ *********************************************************************/
+public:
+
+signals:
+
+private:
+
+ /*********************************************************************
+ * Data
+ *********************************************************************/
+public:
+
+protected slots:
+
+signals:
+
+private:
+
+ /*********************************************************************
+ * Functions connections
+ *********************************************************************/
+public:
+
+signals:
+
+private:
+
+ /*********************************************************************
+ * Load & Save
+ *********************************************************************/
+public:
+ bool loadXML(QXmlStreamReader &root);
+ bool saveXML(QXmlStreamWriter *doc);
+};
+
+#endif
diff --git a/qmlui/virtualconsole/vcbutton.cpp b/qmlui/virtualconsole/vcbutton.cpp
index 84debd96e8..740e05fdda 100644
--- a/qmlui/virtualconsole/vcbutton.cpp
+++ b/qmlui/virtualconsole/vcbutton.cpp
@@ -514,6 +514,8 @@ void VCButton::updateFeedback()
if (m_state == Inactive)
sendFeedback(0, INPUT_PRESSURE_ID, VCWidget::LowerValue);
+ else if (m_state == Monitoring)
+ sendFeedback(0, INPUT_PRESSURE_ID, VCWidget::MonitorValue);
else
sendFeedback(UCHAR_MAX, INPUT_PRESSURE_ID, VCWidget::UpperValue);
}
diff --git a/qmlui/virtualconsole/vccuelist.cpp b/qmlui/virtualconsole/vccuelist.cpp
index efc90202db..d7edee1be9 100644
--- a/qmlui/virtualconsole/vccuelist.cpp
+++ b/qmlui/virtualconsole/vccuelist.cpp
@@ -488,6 +488,8 @@ void VCCueList::setChaserID(quint32 fid)
this, SLOT(slotCurrentStepChanged(int)));
connect(function, SIGNAL(stepChanged(int)),
this, SLOT(slotStepChanged(int)));
+ connect(function, SIGNAL(stepsListChanged(quint32)),
+ this, SLOT(slotStepsListChanged(quint32)));
emit chaserIDChanged(fid);
}
@@ -524,6 +526,15 @@ void VCCueList::slotStepChanged(int index)
ChaserEditor::updateStepInListModel(m_doc, chaser(), m_stepsList, step, index);
}
+void VCCueList::slotStepsListChanged(quint32 fid)
+{
+ if (fid == m_chaserID)
+ {
+ ChaserEditor::updateStepsList(m_doc, chaser(), m_stepsList);
+ emit stepsListChanged();
+ }
+}
+
void VCCueList::slotFunctionNameChanged(quint32 fid)
{
if (fid == m_chaserID)
diff --git a/qmlui/virtualconsole/vccuelist.h b/qmlui/virtualconsole/vccuelist.h
index 8d7bfe6c63..a6db069d14 100644
--- a/qmlui/virtualconsole/vccuelist.h
+++ b/qmlui/virtualconsole/vccuelist.h
@@ -188,6 +188,7 @@ private slots:
void slotFunctionRemoved(quint32 fid);
void slotFunctionNameChanged(quint32 fid);
void slotStepChanged(int index);
+ void slotStepsListChanged(quint32 fid);
private:
FunctionParent functionParent() const;
diff --git a/qmlui/virtualconsole/vcframe.cpp b/qmlui/virtualconsole/vcframe.cpp
index b3b103a259..5679a65445 100644
--- a/qmlui/virtualconsole/vcframe.cpp
+++ b/qmlui/virtualconsole/vcframe.cpp
@@ -31,6 +31,10 @@
#include "vccuelist.h"
#include "vcsoloframe.h"
#include "simplecrypt.h"
+#include "vcanimation.h"
+#include "vcaudiotrigger.h"
+#include "vcxypad.h"
+#include "vcspeeddial.h"
#include "virtualconsole.h"
#define INPUT_NEXT_PAGE_ID 0
@@ -289,6 +293,54 @@ void VCFrame::addWidget(QQuickItem *parent, QString wType, QPoint pos)
slider->render(m_vc->view(), parent);
}
break;
+ case AnimationWidget:
+ {
+ VCAnimation *animation = new VCAnimation(m_doc, this);
+ QQmlEngine::setObjectOwnership(animation, QQmlEngine::CppOwnership);
+ m_vc->addWidgetToMap(animation);
+ Tardis::instance()->enqueueAction(Tardis::VCWidgetCreate, this->id(), QVariant(),
+ Tardis::instance()->actionToByteArray(Tardis::VCWidgetCreate, animation->id()));
+ animation->setGeometry(QRect(pos.x(), pos.y(), m_vc->pixelDensity() * 25, m_vc->pixelDensity() * 8));
+ setupWidget(animation, currentPage());
+ animation->render(m_vc->view(), parent);
+ }
+ break;
+ case AudioTriggersWidget:
+ {
+ VCAudioTrigger *audioTrigger = new VCAudioTrigger(m_doc, this);
+ QQmlEngine::setObjectOwnership(audioTrigger, QQmlEngine::CppOwnership);
+ m_vc->addWidgetToMap(audioTrigger);
+ Tardis::instance()->enqueueAction(Tardis::VCWidgetCreate, this->id(), QVariant(),
+ Tardis::instance()->actionToByteArray(Tardis::VCWidgetCreate, audioTrigger->id()));
+ audioTrigger->setGeometry(QRect(pos.x(), pos.y(), m_vc->pixelDensity() * 25, m_vc->pixelDensity() * 8));
+ setupWidget(audioTrigger, currentPage());
+ audioTrigger->render(m_vc->view(), parent);
+ }
+ break;
+ case XYPadWidget:
+ {
+ VCXYPad *xyPad = new VCXYPad(m_doc, this);
+ QQmlEngine::setObjectOwnership(xyPad, QQmlEngine::CppOwnership);
+ m_vc->addWidgetToMap(xyPad);
+ Tardis::instance()->enqueueAction(Tardis::VCWidgetCreate, this->id(), QVariant(),
+ Tardis::instance()->actionToByteArray(Tardis::VCWidgetCreate, xyPad->id()));
+ xyPad->setGeometry(QRect(pos.x(), pos.y(), m_vc->pixelDensity() * 25, m_vc->pixelDensity() * 8));
+ setupWidget(xyPad, currentPage());
+ xyPad->render(m_vc->view(), parent);
+ }
+ break;
+ case SpeedDialWidget:
+ {
+ VCSpeedDial *speedDial = new VCSpeedDial(m_doc, this);
+ QQmlEngine::setObjectOwnership(speedDial, QQmlEngine::CppOwnership);
+ m_vc->addWidgetToMap(speedDial);
+ Tardis::instance()->enqueueAction(Tardis::VCWidgetCreate, this->id(), QVariant(),
+ Tardis::instance()->actionToByteArray(Tardis::VCWidgetCreate, speedDial->id()));
+ speedDial->setGeometry(QRect(pos.x(), pos.y(), m_vc->pixelDensity() * 25, m_vc->pixelDensity() * 8));
+ setupWidget(speedDial, currentPage());
+ speedDial->render(m_vc->view(), parent);
+ }
+ break;
case ClockWidget:
{
VCClock *clock = new VCClock(m_doc, this);
@@ -1034,6 +1086,66 @@ bool VCFrame::loadWidgetXML(QXmlStreamReader &root, bool render)
slider->render(m_vc->view(), m_item);
}
}
+ else if (root.name() == KXMLQLCVCAnimation)
+ {
+ /* Create a new clock into its parent */
+ VCAnimation *animation = new VCAnimation(m_doc, this);
+ if (animation->loadXML(root) == false)
+ delete animation;
+ else
+ {
+ QQmlEngine::setObjectOwnership(animation, QQmlEngine::CppOwnership);
+ setupWidget(animation, animation->page());
+ m_vc->addWidgetToMap(animation);
+ if (render && m_item)
+ animation->render(m_vc->view(), m_item);
+ }
+ }
+ else if (root.name() == KXMLQLCVCAudioTriggers)
+ {
+ /* Create a new clock into its parent */
+ VCAudioTrigger *audioTrigger = new VCAudioTrigger(m_doc, this);
+ if (audioTrigger->loadXML(root) == false)
+ delete audioTrigger;
+ else
+ {
+ QQmlEngine::setObjectOwnership(audioTrigger, QQmlEngine::CppOwnership);
+ setupWidget(audioTrigger, audioTrigger->page());
+ m_vc->addWidgetToMap(audioTrigger);
+ if (render && m_item)
+ audioTrigger->render(m_vc->view(), m_item);
+ }
+ }
+ else if (root.name() == KXMLQLCVCSpeedDial)
+ {
+ /* Create a new speedDial into its parent */
+ VCSpeedDial *speedDial = new VCSpeedDial(m_doc, this);
+ if (speedDial->loadXML(root) == false)
+ delete speedDial;
+ else
+ {
+ QQmlEngine::setObjectOwnership(speedDial, QQmlEngine::CppOwnership);
+ setupWidget(speedDial, speedDial->page());
+ m_vc->addWidgetToMap(speedDial);
+ if (render && m_item)
+ speedDial->render(m_vc->view(), m_item);
+ }
+ }
+ else if (root.name() == KXMLQLCVCXYPad)
+ {
+ /* Create a new xyPad into its parent */
+ VCXYPad *xyPad = new VCXYPad(m_doc, this);
+ if (xyPad->loadXML(root) == false)
+ delete xyPad;
+ else
+ {
+ QQmlEngine::setObjectOwnership(xyPad, QQmlEngine::CppOwnership);
+ setupWidget(xyPad, xyPad->page());
+ m_vc->addWidgetToMap(xyPad);
+ if (render && m_item)
+ xyPad->render(m_vc->view(), m_item);
+ }
+ }
else if (root.name() == KXMLQLCVCClock)
{
/* Create a new clock into its parent */
diff --git a/qmlui/virtualconsole/vcslider.cpp b/qmlui/virtualconsole/vcslider.cpp
index 8d22e3902e..6747ca542d 100644
--- a/qmlui/virtualconsole/vcslider.cpp
+++ b/qmlui/virtualconsole/vcslider.cpp
@@ -38,6 +38,7 @@
#define INPUT_SLIDER_CONTROL_ID 0
#define INPUT_SLIDER_RESET_ID 1
+#define INPUT_SLIDER_FLASH_ID 2
VCSlider::VCSlider(Doc *doc, QObject *parent)
: VCWidget(doc, parent)
@@ -64,12 +65,14 @@ VCSlider::VCSlider(Doc *doc, QObject *parent)
, m_controlledAttributeId(Function::invalidAttributeId())
, m_attributeMinValue(0)
, m_attributeMaxValue(UCHAR_MAX)
+ , m_adjustFlashEnabled(false)
{
setType(VCWidget::SliderWidget);
setSliderMode(Adjust);
registerExternalControl(INPUT_SLIDER_CONTROL_ID, tr("Slider Control"), false);
registerExternalControl(INPUT_SLIDER_RESET_ID, tr("Reset Control"), false);
+ registerExternalControl(INPUT_SLIDER_FLASH_ID, tr("Flash Control"), true);
}
VCSlider::~VCSlider()
@@ -676,7 +679,7 @@ QVariantList VCSlider::clickAndGoPresetsList()
return prList;
/* Find the first valid channel and return it to QML */
- for (SceneValue scv : m_levelChannels)
+ for (SceneValue &scv : m_levelChannels)
{
Fixture *fixture = m_doc->fixture(scv.fxi);
if (fixture == nullptr)
@@ -849,7 +852,7 @@ void VCSlider::adjustIntensity(qreal val)
if (sliderMode() == Adjust)
{
- Function* function = m_doc->function(m_controlledFunctionId);
+ Function *function = m_doc->function(m_controlledFunctionId);
if (function == nullptr)
return;
@@ -882,7 +885,7 @@ void VCSlider::slotControlledFunctionStopped(quint32 fid)
if (m_controlledAttributeIndex == Function::Intensity)
setValue(0, false, true);
- Function* function = m_doc->function(fid);
+ Function *function = m_doc->function(fid);
function->releaseAttributeOverride(m_controlledAttributeId);
m_controlledAttributeId = Function::invalidAttributeId();
}
@@ -898,7 +901,7 @@ void VCSlider::setControlledAttribute(int attributeIndex)
if (m_controlledAttributeIndex == attributeIndex)
return;
- Function* function = m_doc->function(m_controlledFunctionId);
+ Function *function = m_doc->function(m_controlledFunctionId);
if (function == nullptr || attributeIndex >= function->attributes().count())
return;
@@ -944,15 +947,46 @@ void VCSlider::adjustFunctionAttribute(Function *f, qreal value)
f->adjustAttribute(value, m_controlledAttributeId);
}
+bool VCSlider::adjustFlashEnabled() const
+{
+ return m_adjustFlashEnabled;
+}
+
+void VCSlider::setAdjustFlashEnabled(bool enable)
+{
+ if (enable == m_adjustFlashEnabled)
+ return;
+
+ m_adjustFlashEnabled = enable;
+ emit adjustFlashEnabledChanged(enable);
+}
+
+void VCSlider::flashFunction(bool on)
+{
+ Function *function = m_doc->function(m_controlledFunctionId);
+ if (function == nullptr)
+ return;
+
+ if (on)
+ {
+ if (m_controlledAttributeId == Function::invalidAttributeId())
+ m_adjustFlashPreviousValue = 0;
+ else
+ m_adjustFlashPreviousValue = function->getAttributeValue(m_controlledAttributeIndex);
+ }
+
+ adjustFunctionAttribute(function, on ? 1.0 : m_adjustFlashPreviousValue);
+}
+
QStringList VCSlider::availableAttributes() const
{
QStringList list;
- Function* function = m_doc->function(m_controlledFunctionId);
+ Function *function = m_doc->function(m_controlledFunctionId);
if (function == nullptr)
return list;
- for (Attribute attr : function->attributes())
+ for (Attribute &attr : function->attributes())
list << attr.m_name;
return list;
@@ -1029,7 +1063,7 @@ void VCSlider::writeDMXLevel(MasterTimer* timer, QList universes)
if (clickAndGoType() == CnGColors)
{
- float f = SCALE(float(m_value), rangeLowLimit(), rangeHighLimit(), 0.0, 200.0);
+ float f = SCALE(float(modLevel), rangeLowLimit(), rangeHighLimit(), 0.0, 200.0);
if ((uchar)f != 0)
{
@@ -1053,7 +1087,7 @@ void VCSlider::writeDMXLevel(MasterTimer* timer, QList universes)
bool mixedDMXlevels = false;
int monitorSliderValue = -1;
- for (SceneValue scv : m_levelChannels)
+ for (SceneValue &scv : m_levelChannels)
{
Fixture* fxi = m_doc->fixture(scv.fxi);
if (fxi != nullptr)
@@ -1108,7 +1142,7 @@ void VCSlider::writeDMXLevel(MasterTimer* timer, QList universes)
if (m_levelValueChanged)
{
- for (SceneValue scv : m_levelChannels)
+ for (SceneValue &scv : m_levelChannels)
{
Fixture* fxi = m_doc->fixture(scv.fxi);
if (fxi == nullptr)
@@ -1136,6 +1170,10 @@ void VCSlider::writeDMXLevel(MasterTimer* timer, QList universes)
if (m_isOverriding)
fc->addFlag(FadeChannel::Override);
+ // request to autoremove LTP channels when set
+ if (! (chType & FadeChannel::Intensity))
+ fc->addFlag(FadeChannel::AutoRemove);
+
if (chType & FadeChannel::Intensity && clickAndGoType() == CnGColors)
{
const QLCChannel *qlcch = fxi->channel(scv.channel);
@@ -1176,7 +1214,7 @@ void VCSlider::writeDMXAdjust(MasterTimer* timer, QList ua)
if (m_adjustChangeCounter == 0)
return;
- Function* function = m_doc->function(m_controlledFunctionId);
+ Function *function = m_doc->function(m_controlledFunctionId);
if (function == nullptr)
return;
@@ -1233,15 +1271,22 @@ void VCSlider::updateFeedback()
void VCSlider::slotInputValueChanged(quint8 id, uchar value)
{
+ int scaledValue = SCALE(float(value), float(0), float(UCHAR_MAX),
+ float(rangeLowLimit()),
+ float(rangeHighLimit()));
+
switch (id)
{
case INPUT_SLIDER_CONTROL_ID:
- setValue(value, true, false);
+ setValue(scaledValue, true, false);
break;
case INPUT_SLIDER_RESET_ID:
if (value)
setIsOverriding(false);
break;
+ case INPUT_SLIDER_FLASH_ID:
+ flashFunction(value ? true : false);
+ break;
}
}
@@ -1323,6 +1368,11 @@ bool VCSlider::loadXML(QXmlStreamReader &root)
{
loadXMLAdjust(root);
}
+ else if (root.name() == KXMLQLCVCSliderFunctionFlash)
+ {
+ setAdjustFlashEnabled(true);
+ loadXMLSources(root, INPUT_SLIDER_FLASH_ID);
+ }
else if (root.name() == KXMLQLCVCSliderPlayback) // LEGACY
{
loadXMLLegacyPlayback(root);
@@ -1451,6 +1501,11 @@ bool VCSlider::loadXMLLegacyPlayback(QXmlStreamReader &pb_root)
setControlledFunction(pb_root.readElementText().toUInt());
setControlledAttribute(Function::Intensity);
}
+ else if (pb_root.name() == KXMLQLCVCSliderFunctionFlash)
+ {
+ setAdjustFlashEnabled(true);
+ loadXMLSources(pb_root, INPUT_SLIDER_FLASH_ID);
+ }
else
{
qWarning() << Q_FUNC_INFO << "Unknown slider playback tag:" << pb_root.name().toString();
@@ -1524,7 +1579,7 @@ bool VCSlider::saveXML(QXmlStreamWriter *doc)
doc->writeAttribute(KXMLQLCVCSliderLevelValue, QString::number(value()));
/* Level channels */
- for (SceneValue scv : m_levelChannels)
+ for (SceneValue &scv : m_levelChannels)
{
doc->writeStartElement(KXMLQLCVCSliderChannel);
doc->writeAttribute(KXMLQLCVCSliderChannelFixture, QString::number(scv.fxi));
@@ -1545,6 +1600,13 @@ bool VCSlider::saveXML(QXmlStreamWriter *doc)
doc->writeAttribute(KXMLQLCVCSliderControlledFunction, QString::number(controlledFunction()));
/* End the tag */
doc->writeEndElement();
+
+ if (adjustFlashEnabled())
+ {
+ //doc->writeStartElement(KXMLQLCVCSliderFunctionFlash);
+ saveXMLInputControl(doc, INPUT_SLIDER_FLASH_ID, KXMLQLCVCSliderFunctionFlash);
+ //doc->writeEndElement();
+ }
}
/* End the tag */
diff --git a/qmlui/virtualconsole/vcslider.h b/qmlui/virtualconsole/vcslider.h
index 573ee4ab1f..b624a271fe 100644
--- a/qmlui/virtualconsole/vcslider.h
+++ b/qmlui/virtualconsole/vcslider.h
@@ -44,6 +44,7 @@
#define KXMLQLCVCSliderLevelValue QString("Value")
#define KXMLQLCVCSliderLevelMonitor QString("Monitor")
#define KXMLQLCVCSliderOverrideReset QString("Reset")
+#define KXMLQLCVCSliderFunctionFlash QString("Flash")
#define KXMLQLCVCSliderChannel QString("Channel")
#define KXMLQLCVCSliderChannelFixture QString("Fixture")
@@ -75,6 +76,8 @@ class VCSlider : public VCWidget, public DMXSource
Q_PROPERTY(int monitorValue READ monitorValue NOTIFY monitorValueChanged)
Q_PROPERTY(bool isOverriding READ isOverriding WRITE setIsOverriding NOTIFY isOverridingChanged)
+ Q_PROPERTY(bool adjustFlashEnabled READ adjustFlashEnabled WRITE setAdjustFlashEnabled NOTIFY adjustFlashEnabledChanged)
+
Q_PROPERTY(quint32 controlledFunction READ controlledFunction WRITE setControlledFunction NOTIFY controlledFunctionChanged)
Q_PROPERTY(int controlledAttribute READ controlledAttribute WRITE setControlledAttribute NOTIFY controlledAttributeChanged)
Q_PROPERTY(QStringList availableAttributes READ availableAttributes NOTIFY availableAttributesChanged)
@@ -372,6 +375,12 @@ protected slots:
void adjustFunctionAttribute(Function *f, qreal value);
+ /** Get/Set the status of the flash button enablement */
+ bool adjustFlashEnabled() const;
+ void setAdjustFlashEnabled(bool enable);
+
+ Q_INVOKABLE void flashFunction(bool on);
+
/** Get the list of the available attributes for the Function to control */
QStringList availableAttributes() const;
@@ -388,6 +397,7 @@ protected slots:
signals:
void controlledFunctionChanged(quint32 fid);
void controlledAttributeChanged(int attr);
+ void adjustFlashEnabledChanged(bool enable);
void availableAttributesChanged();
void attributeMinValueChanged();
void attributeMaxValueChanged();
@@ -404,6 +414,9 @@ protected slots:
qreal m_attributeMinValue;
qreal m_attributeMaxValue;
+ bool m_adjustFlashEnabled;
+ qreal m_adjustFlashPreviousValue;
+
/*********************************************************************
* Submaster
*********************************************************************/
diff --git a/qmlui/virtualconsole/vcspeeddial.cpp b/qmlui/virtualconsole/vcspeeddial.cpp
new file mode 100644
index 0000000000..337ae16e36
--- /dev/null
+++ b/qmlui/virtualconsole/vcspeeddial.cpp
@@ -0,0 +1,169 @@
+/*
+ Q Light Controller Plus
+ vcspeeddial.cpp
+
+ Copyright (c) Massimo Callegari
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0.txt
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+#include
+#include
+#include
+
+#include "doc.h"
+#include "vcspeeddial.h"
+
+VCSpeedDial::VCSpeedDial(Doc *doc, QObject *parent)
+ : VCWidget(doc, parent)
+{
+ setType(VCWidget::SpeedDialWidget);
+}
+
+VCSpeedDial::~VCSpeedDial()
+{
+ if (m_item)
+ delete m_item;
+}
+
+QString VCSpeedDial::defaultCaption()
+{
+ return tr("Speed Dial %1").arg(id() + 1);
+}
+
+void VCSpeedDial::setupLookAndFeel(qreal pixelDensity, int page)
+{
+ setPage(page);
+ QFont wFont = font();
+ wFont.setBold(true);
+ wFont.setPointSize(pixelDensity * 5.0);
+ setFont(wFont);
+}
+
+void VCSpeedDial::render(QQuickView *view, QQuickItem *parent)
+{
+ if (view == nullptr || parent == nullptr)
+ return;
+
+ QQmlComponent *component = new QQmlComponent(view->engine(), QUrl("qrc:/VCSpeedDialItem.qml"));
+
+ if (component->isError())
+ {
+ qDebug() << component->errors();
+ return;
+ }
+
+ m_item = qobject_cast(component->create());
+
+ m_item->setParentItem(parent);
+ m_item->setProperty("speedDialObj", QVariant::fromValue(this));
+}
+
+QString VCSpeedDial::propertiesResource() const
+{
+ return QString("qrc:/VCSpeedDialProperties.qml");
+}
+
+VCWidget *VCSpeedDial::createCopy(VCWidget *parent)
+{
+ Q_ASSERT(parent != nullptr);
+
+ VCSpeedDial *speedDial = new VCSpeedDial(m_doc, parent);
+ if (speedDial->copyFrom(this) == false)
+ {
+ delete speedDial;
+ speedDial = nullptr;
+ }
+
+ return speedDial;
+}
+
+bool VCSpeedDial::copyFrom(const VCWidget *widget)
+{
+ const VCSpeedDial *speedDial = qobject_cast (widget);
+ if (speedDial == nullptr)
+ return false;
+
+ /* Copy and set properties */
+
+ /* Copy object lists */
+
+ /* Common stuff */
+ return VCWidget::copyFrom(widget);
+}
+
+FunctionParent VCSpeedDial::functionParent() const
+{
+ return FunctionParent(FunctionParent::AutoVCWidget, id());
+}
+
+/*********************************************************************
+ * Load & Save
+ *********************************************************************/
+
+bool VCSpeedDial::loadXML(QXmlStreamReader &root)
+{
+ if (root.name() != KXMLQLCVCSpeedDial)
+ {
+ qWarning() << Q_FUNC_INFO << "Speed dial node not found";
+ return false;
+ }
+
+ QXmlStreamAttributes attrs = root.attributes();
+
+ /* Widget commons */
+ loadXMLCommon(root);
+
+ while (root.readNextStartElement())
+ {
+ if (root.name() == KXMLQLCWindowState)
+ {
+ bool visible = false;
+ int x = 0, y = 0, w = 0, h = 0;
+ loadXMLWindowState(root, &x, &y, &w, &h, &visible);
+ setGeometry(QRect(x, y, w, h));
+ }
+ else if (root.name() == KXMLQLCVCWidgetAppearance)
+ {
+ loadXMLAppearance(root);
+ }
+ else
+ {
+ qWarning() << Q_FUNC_INFO << "Unknown speed dial tag:" << root.name().toString();
+ root.skipCurrentElement();
+ }
+ }
+
+ return true;
+}
+
+bool VCSpeedDial::saveXML(QXmlStreamWriter *doc)
+{
+ Q_ASSERT(doc != nullptr);
+
+ /* VC object entry */
+ doc->writeStartElement(KXMLQLCVCSpeedDial);
+
+ saveXMLCommon(doc);
+
+ /* Window state */
+ saveXMLWindowState(doc);
+
+ /* Appearance */
+ saveXMLAppearance(doc);
+
+ /* Write the tag */
+ doc->writeEndElement();
+
+ return true;
+}
diff --git a/qmlui/virtualconsole/vcspeeddial.h b/qmlui/virtualconsole/vcspeeddial.h
new file mode 100644
index 0000000000..0684b10227
--- /dev/null
+++ b/qmlui/virtualconsole/vcspeeddial.h
@@ -0,0 +1,118 @@
+/*
+ Q Light Controller Plus
+ vcspeeddial.h
+
+ Copyright (c) Massimo Callegari
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0.txt
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+#ifndef VCSPEEDDIAL_H
+#define VCSPEEDDIAL_H
+
+#include "vcwidget.h"
+
+#define KXMLQLCVCSpeedDial QString("SpeedDial")
+#define KXMLQLCVCSpeedDialSpeedTypes QString("SpeedTypes")
+#define KXMLQLCVCSpeedDialAbsoluteValue QString("AbsoluteValue")
+#define KXMLQLCVCSpeedDialAbsoluteValueMin QString("Minimum")
+#define KXMLQLCVCSpeedDialAbsoluteValueMax QString("Maximum")
+#define KXMLQLCVCSpeedDialTap QString("Tap")
+#define KXMLQLCVCSpeedDialMult QString("Mult")
+#define KXMLQLCVCSpeedDialDiv QString("Div")
+#define KXMLQLCVCSpeedDialMultDivReset QString("MultDivReset")
+#define KXMLQLCVCSpeedDialApply QString("Apply")
+#define KXMLQLCVCSpeedDialTapKey QString("Key")
+#define KXMLQLCVCSpeedDialMultKey QString("MultKey")
+#define KXMLQLCVCSpeedDialDivKey QString("DivKey")
+#define KXMLQLCVCSpeedDialMultDivResetKey QString("MultDivResetKey")
+#define KXMLQLCVCSpeedDialApplyKey QString("ApplyKey")
+#define KXMLQLCVCSpeedDialResetFactorOnDialChange QString("ResetFactorOnDialChange")
+#define KXMLQLCVCSpeedDialVisibilityMask QString("Visibility")
+#define KXMLQLCVCSpeedDialTime QString("Time")
+
+// Legacy: infinite checkbox
+#define KXMLQLCVCSpeedDialInfinite QString("Infinite")
+#define KXMLQLCVCSpeedDialInfiniteKey QString("InfiniteKey")
+
+class VCSpeedDial : public VCWidget
+{
+ Q_OBJECT
+
+ /*********************************************************************
+ * Initialization
+ *********************************************************************/
+public:
+ VCSpeedDial(Doc* doc = nullptr, QObject *parent = nullptr);
+ virtual ~VCSpeedDial();
+
+ /** @reimp */
+ QString defaultCaption();
+
+ /** @reimp */
+ void setupLookAndFeel(qreal pixelDensity, int page);
+
+ /** @reimp */
+ void render(QQuickView *view, QQuickItem *parent);
+
+ /** @reimp */
+ QString propertiesResource() const;
+
+ /** @reimp */
+ VCWidget *createCopy(VCWidget *parent);
+
+protected:
+ /** @reimp */
+ bool copyFrom(const VCWidget* widget);
+
+private:
+ FunctionParent functionParent() const;
+
+ /*********************************************************************
+ * Type
+ *********************************************************************/
+public:
+
+signals:
+
+private:
+
+ /*********************************************************************
+ * Data
+ *********************************************************************/
+public:
+
+protected slots:
+
+signals:
+
+private:
+
+ /*********************************************************************
+ * Functions connections
+ *********************************************************************/
+public:
+
+signals:
+
+private:
+
+ /*********************************************************************
+ * Load & Save
+ *********************************************************************/
+public:
+ bool loadXML(QXmlStreamReader &root);
+ bool saveXML(QXmlStreamWriter *doc);
+};
+
+#endif
diff --git a/qmlui/virtualconsole/vcwidget.cpp b/qmlui/virtualconsole/vcwidget.cpp
index 65fa66700f..ae49e1c482 100644
--- a/qmlui/virtualconsole/vcwidget.cpp
+++ b/qmlui/virtualconsole/vcwidget.cpp
@@ -120,7 +120,12 @@ bool VCWidget::copyFrom(const VCWidget* widget)
{
QSharedPointer dst(new QLCInputSource(src->universe(), src->channel()));
dst->setID(src->id());
- dst->setRange(src->lowerValue(), src->upperValue());
+ dst->setFeedbackValue(QLCInputFeedback::LowerValue, src->feedbackValue(QLCInputFeedback::LowerValue));
+ dst->setFeedbackValue(QLCInputFeedback::UpperValue, src->feedbackValue(QLCInputFeedback::UpperValue));
+ dst->setFeedbackValue(QLCInputFeedback::MonitorValue, src->feedbackValue(QLCInputFeedback::MonitorValue));
+ dst->setFeedbackExtraParams(QLCInputFeedback::LowerValue, src->feedbackExtraParams(QLCInputFeedback::LowerValue));
+ dst->setFeedbackExtraParams(QLCInputFeedback::UpperValue, src->feedbackExtraParams(QLCInputFeedback::UpperValue));
+ dst->setFeedbackExtraParams(QLCInputFeedback::MonitorValue, src->feedbackExtraParams(QLCInputFeedback::MonitorValue));
addInputSource(dst);
}
@@ -659,6 +664,16 @@ void VCWidget::addInputSource(QSharedPointer const& source)
QLCInputChannel *ich = ip->profile()->channel(source->channel() & 0x0000FFFF);
if (ich != nullptr)
{
+ QLCInputProfile *profile = ip->profile();
+
+ // retrieve plugin specific params for feedback
+ if (source->feedbackExtraParams(QLCInputFeedback::LowerValue).toInt() == -1)
+ source->setFeedbackExtraParams(QLCInputFeedback::LowerValue, profile->channelExtraParams(ich));
+ if (source->feedbackExtraParams(QLCInputFeedback::UpperValue).toInt() == -1)
+ source->setFeedbackExtraParams(QLCInputFeedback::UpperValue, profile->channelExtraParams(ich));
+ if (source->feedbackExtraParams(QLCInputFeedback::MonitorValue).toInt() == -1)
+ source->setFeedbackExtraParams(QLCInputFeedback::MonitorValue, profile->channelExtraParams(ich));
+
if (ich->movementType() == QLCInputChannel::Relative)
{
source->setWorkingMode(QLCInputSource::Relative);
@@ -682,9 +697,16 @@ void VCWidget::addInputSource(QSharedPointer const& source)
this, SLOT(slotInputSourceValueChanged(quint32,quint32,uchar)));
}
- // user custom feedbacks have precedence over input profile custom feedbacks
- source->setRange((source->lowerValue() != 0) ? source->lowerValue() : ich->lowerValue(),
- (source->upperValue() != UCHAR_MAX) ? source->upperValue() : ich->upperValue());
+ // user custom feedback have precedence over input profile custom feedback
+ uchar lower = source->feedbackValue(QLCInputFeedback::LowerValue) != 0 ?
+ source->feedbackValue(QLCInputFeedback::LowerValue) :
+ ich->lowerValue();
+ uchar upper = source->feedbackValue(QLCInputFeedback::UpperValue) != UCHAR_MAX ?
+ source->feedbackValue(QLCInputFeedback::UpperValue) :
+ ich->upperValue();
+
+ source->setFeedbackValue(QLCInputFeedback::LowerValue, lower);
+ source->setFeedbackValue(QLCInputFeedback::UpperValue, upper);
}
}
}
@@ -725,7 +747,8 @@ bool VCWidget::updateInputSourceRange(quint32 universe, quint32 channel, quint8
{
if (source->universe() == universe && source->channel() == channel)
{
- source->setRange(lower, upper);
+ source->setFeedbackValue(QLCInputFeedback::LowerValue, lower);
+ source->setFeedbackValue(QLCInputFeedback::UpperValue, upper);
return true;
}
}
@@ -787,16 +810,20 @@ QVariantList VCWidget::inputSourcesList()
}
QVariantMap sourceMap;
+ uchar lower = source->feedbackValue(QLCInputFeedback::LowerValue);
+ uchar upper = source->feedbackValue(QLCInputFeedback::UpperValue);
+
if (source->isValid() == false)
sourceMap.insert("invalid", true);
+
sourceMap.insert("type", Controller);
sourceMap.insert("id", source->id());
sourceMap.insert("uniString", uniName);
sourceMap.insert("chString", chName);
sourceMap.insert("universe", source->universe());
sourceMap.insert("channel", source->channel());
- sourceMap.insert("lower", source->lowerValue() != 0 ? source->lowerValue() : min);
- sourceMap.insert("upper", source->upperValue() != UCHAR_MAX ? source->upperValue() : max);
+ sourceMap.insert("lower", lower != 0 ? lower : min);
+ sourceMap.insert("upper", upper != UCHAR_MAX ? upper : max);
sourceMap.insert("customFeedback", supportCustomFeedback);
m_sourcesList.append(sourceMap);
}
@@ -863,9 +890,11 @@ void VCWidget::sendFeedback(int value, quint8 id, SourceValueType type)
continue;
if (type == LowerValue)
- value = source->lowerValue();
+ value = source->feedbackValue(QLCInputFeedback::LowerValue);
else if (type == UpperValue)
- value = source->upperValue();
+ value = source->feedbackValue(QLCInputFeedback::UpperValue);
+ else if (type == MonitorValue)
+ value = source->feedbackValue(QLCInputFeedback::MonitorValue);
// if in relative mode, send a "feedback" to this
// input source so it can continue to emit values
@@ -1084,7 +1113,7 @@ bool VCWidget::loadXMLInputSource(QXmlStreamReader &root, const quint8 &id)
quint32 uni = attrs.value(KXMLQLCVCWidgetInputUniverse).toString().toUInt();
quint32 ch = attrs.value(KXMLQLCVCWidgetInputChannel).toString().toUInt();
- uchar min = 0, max = UCHAR_MAX;
+ uchar min = 0, max = UCHAR_MAX, mon = UCHAR_MAX;
QSharedPointerinputSource = QSharedPointer(new QLCInputSource(uni, ch));
inputSource->setID(id);
@@ -1093,8 +1122,20 @@ bool VCWidget::loadXMLInputSource(QXmlStreamReader &root, const quint8 &id)
min = uchar(attrs.value(KXMLQLCVCWidgetInputLowerValue).toString().toUInt());
if (attrs.hasAttribute(KXMLQLCVCWidgetInputUpperValue))
max = uchar(attrs.value(KXMLQLCVCWidgetInputUpperValue).toString().toUInt());
+ if (attrs.hasAttribute(KXMLQLCVCWidgetInputMonitorValue))
+ mon = uchar(attrs.value(KXMLQLCVCWidgetInputMonitorValue).toString().toUInt());
+
+ inputSource->setFeedbackValue(QLCInputFeedback::LowerValue, min);
+ inputSource->setFeedbackValue(QLCInputFeedback::UpperValue, max);
+ inputSource->setFeedbackValue(QLCInputFeedback::MonitorValue, mon);
- inputSource->setRange(min, max);
+ // load feedback extra params
+ if (attrs.hasAttribute(KXMLQLCVCWidgetInputLowerParams))
+ inputSource->setFeedbackExtraParams(QLCInputFeedback::LowerValue, attrs.value(KXMLQLCVCWidgetInputLowerParams).toInt());
+ if (attrs.hasAttribute(KXMLQLCVCWidgetInputUpperParams))
+ inputSource->setFeedbackExtraParams(QLCInputFeedback::UpperValue, attrs.value(KXMLQLCVCWidgetInputUpperParams).toInt());
+ if (attrs.hasAttribute(KXMLQLCVCWidgetInputMonitorParams))
+ inputSource->setFeedbackExtraParams(QLCInputFeedback::MonitorValue, attrs.value(KXMLQLCVCWidgetInputMonitorParams).toInt());
addInputSource(inputSource);
@@ -1262,10 +1303,29 @@ bool VCWidget::saveXMLInputControl(QXmlStreamWriter *doc, quint8 controlId, QStr
doc->writeStartElement(KXMLQLCVCWidgetInput);
doc->writeAttribute(KXMLQLCVCWidgetInputUniverse, QString("%1").arg(source->universe()));
doc->writeAttribute(KXMLQLCVCWidgetInputChannel, QString("%1").arg(source->channel()));
- if (source->lowerValue() != 0)
- doc->writeAttribute(KXMLQLCVCWidgetInputLowerValue, QString::number(source->lowerValue()));
- if (source->upperValue() != UCHAR_MAX)
- doc->writeAttribute(KXMLQLCVCWidgetInputUpperValue, QString::number(source->upperValue()));
+ if (source->feedbackValue(QLCInputFeedback::LowerValue) != 0)
+ doc->writeAttribute(KXMLQLCVCWidgetInputLowerValue, QString::number(source->feedbackValue(QLCInputFeedback::LowerValue)));
+ if (source->feedbackValue(QLCInputFeedback::UpperValue) != UCHAR_MAX)
+ doc->writeAttribute(KXMLQLCVCWidgetInputUpperValue, QString::number(source->feedbackValue(QLCInputFeedback::UpperValue)));
+ if (source->feedbackValue(QLCInputFeedback::MonitorValue) != UCHAR_MAX)
+ doc->writeAttribute(KXMLQLCVCWidgetInputMonitorValue, QString::number(source->feedbackValue(QLCInputFeedback::MonitorValue)));
+
+ // save feedback extra params
+ QVariant extraParams = source->feedbackExtraParams(QLCInputFeedback::LowerValue);
+
+ if (extraParams.isValid() && extraParams.type() == QVariant::Int && extraParams.toInt() != -1)
+ doc->writeAttribute(KXMLQLCVCWidgetInputLowerParams, QString::number(extraParams.toInt()));
+
+ extraParams = source->feedbackExtraParams(QLCInputFeedback::UpperValue);
+
+ if (extraParams.isValid() && extraParams.type() == QVariant::Int && extraParams.toInt() != -1)
+ doc->writeAttribute(KXMLQLCVCWidgetInputUpperParams, QString::number(extraParams.toInt()));
+
+ extraParams = source->feedbackExtraParams(QLCInputFeedback::MonitorValue);
+
+ if (extraParams.isValid() && extraParams.type() == QVariant::Int && extraParams.toInt() != -1)
+ doc->writeAttribute(KXMLQLCVCWidgetInputMonitorParams, QString::number(extraParams.toInt()));
+
doc->writeEndElement();
}
diff --git a/qmlui/virtualconsole/vcwidget.h b/qmlui/virtualconsole/vcwidget.h
index e63ee77d70..67bf43e613 100644
--- a/qmlui/virtualconsole/vcwidget.h
+++ b/qmlui/virtualconsole/vcwidget.h
@@ -54,12 +54,16 @@
#define KXMLQLCWindowStateWidth QString("Width")
#define KXMLQLCWindowStateHeight QString("Height")
-#define KXMLQLCVCWidgetKey QString("Key")
-#define KXMLQLCVCWidgetInput QString("Input")
-#define KXMLQLCVCWidgetInputUniverse QString("Universe")
-#define KXMLQLCVCWidgetInputChannel QString("Channel")
-#define KXMLQLCVCWidgetInputLowerValue QString("LowerValue")
-#define KXMLQLCVCWidgetInputUpperValue QString("UpperValue")
+#define KXMLQLCVCWidgetKey QString("Key")
+#define KXMLQLCVCWidgetInput QString("Input")
+#define KXMLQLCVCWidgetInputUniverse QString("Universe")
+#define KXMLQLCVCWidgetInputChannel QString("Channel")
+#define KXMLQLCVCWidgetInputLowerValue QString("LowerValue")
+#define KXMLQLCVCWidgetInputUpperValue QString("UpperValue")
+#define KXMLQLCVCWidgetInputMonitorValue QString("MonitorValue")
+#define KXMLQLCVCWidgetInputLowerParams QString("LowerParams")
+#define KXMLQLCVCWidgetInputUpperParams QString("UpperParams")
+#define KXMLQLCVCWidgetInputMonitorParams QString("MonitorParams")
typedef struct
{
@@ -480,7 +484,7 @@ class VCWidget : public QObject
* Input sources
*********************************************************************/
public:
- enum SourceValueType { ExactValue, LowerValue, UpperValue };
+ enum SourceValueType { ExactValue, LowerValue, UpperValue, MonitorValue };
Q_ENUM(SourceValueType)
/**
diff --git a/qmlui/virtualconsole/vcxypad.cpp b/qmlui/virtualconsole/vcxypad.cpp
new file mode 100644
index 0000000000..539176aa14
--- /dev/null
+++ b/qmlui/virtualconsole/vcxypad.cpp
@@ -0,0 +1,169 @@
+/*
+ Q Light Controller Plus
+ vcxypad.cpp
+
+ Copyright (c) Massimo Callegari
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0.txt
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+#include
+#include
+#include
+
+#include "doc.h"
+#include "vcxypad.h"
+
+VCXYPad::VCXYPad(Doc *doc, QObject *parent)
+ : VCWidget(doc, parent)
+{
+ setType(VCWidget::XYPadWidget);
+}
+
+VCXYPad::~VCXYPad()
+{
+ if (m_item)
+ delete m_item;
+}
+
+QString VCXYPad::defaultCaption()
+{
+ return tr("XY Pad %1").arg(id() + 1);
+}
+
+void VCXYPad::setupLookAndFeel(qreal pixelDensity, int page)
+{
+ setPage(page);
+ QFont wFont = font();
+ wFont.setBold(true);
+ wFont.setPointSize(pixelDensity * 5.0);
+ setFont(wFont);
+}
+
+void VCXYPad::render(QQuickView *view, QQuickItem *parent)
+{
+ if (view == nullptr || parent == nullptr)
+ return;
+
+ QQmlComponent *component = new QQmlComponent(view->engine(), QUrl("qrc:/VCXYPadItem.qml"));
+
+ if (component->isError())
+ {
+ qDebug() << component->errors();
+ return;
+ }
+
+ m_item = qobject_cast(component->create());
+
+ m_item->setParentItem(parent);
+ m_item->setProperty("xyPadObj", QVariant::fromValue(this));
+}
+
+QString VCXYPad::propertiesResource() const
+{
+ return QString("qrc:/VCXYPadProperties.qml");
+}
+
+VCWidget *VCXYPad::createCopy(VCWidget *parent)
+{
+ Q_ASSERT(parent != nullptr);
+
+ VCXYPad *XYPad = new VCXYPad(m_doc, parent);
+ if (XYPad->copyFrom(this) == false)
+ {
+ delete XYPad;
+ XYPad = nullptr;
+ }
+
+ return XYPad;
+}
+
+bool VCXYPad::copyFrom(const VCWidget *widget)
+{
+ const VCXYPad *XYPad = qobject_cast (widget);
+ if (XYPad == nullptr)
+ return false;
+
+ /* Copy and set properties */
+
+ /* Copy object lists */
+
+ /* Common stuff */
+ return VCWidget::copyFrom(widget);
+}
+
+FunctionParent VCXYPad::functionParent() const
+{
+ return FunctionParent(FunctionParent::AutoVCWidget, id());
+}
+
+/*********************************************************************
+ * Load & Save
+ *********************************************************************/
+
+bool VCXYPad::loadXML(QXmlStreamReader &root)
+{
+ if (root.name() != KXMLQLCVCXYPad)
+ {
+ qWarning() << Q_FUNC_INFO << "XY Pad node not found";
+ return false;
+ }
+
+ QXmlStreamAttributes attrs = root.attributes();
+
+ /* Widget commons */
+ loadXMLCommon(root);
+
+ while (root.readNextStartElement())
+ {
+ if (root.name() == KXMLQLCWindowState)
+ {
+ bool visible = false;
+ int x = 0, y = 0, w = 0, h = 0;
+ loadXMLWindowState(root, &x, &y, &w, &h, &visible);
+ setGeometry(QRect(x, y, w, h));
+ }
+ else if (root.name() == KXMLQLCVCWidgetAppearance)
+ {
+ loadXMLAppearance(root);
+ }
+ else
+ {
+ qWarning() << Q_FUNC_INFO << "Unknown XY pad tag:" << root.name().toString();
+ root.skipCurrentElement();
+ }
+ }
+
+ return true;
+}
+
+bool VCXYPad::saveXML(QXmlStreamWriter *doc)
+{
+ Q_ASSERT(doc != nullptr);
+
+ /* VC object entry */
+ doc->writeStartElement(KXMLQLCVCXYPad);
+
+ saveXMLCommon(doc);
+
+ /* Window state */
+ saveXMLWindowState(doc);
+
+ /* Appearance */
+ saveXMLAppearance(doc);
+
+ /* Write the tag */
+ doc->writeEndElement();
+
+ return true;
+}
diff --git a/qmlui/virtualconsole/vcxypad.h b/qmlui/virtualconsole/vcxypad.h
new file mode 100644
index 0000000000..6cd6434eba
--- /dev/null
+++ b/qmlui/virtualconsole/vcxypad.h
@@ -0,0 +1,112 @@
+/*
+ Q Light Controller Plus
+ vcxypad.h
+
+ Copyright (c) Massimo Callegari
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0.txt
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+#ifndef VCXYPAD_H
+#define VCXYPAD_H
+
+#include "vcwidget.h"
+
+#define KXMLQLCVCXYPad QString("XYPad")
+#define KXMLQLCVCXYPadPan QString("Pan")
+#define KXMLQLCVCXYPadTilt QString("Tilt")
+#define KXMLQLCVCXYPadWidth QString("Width")
+#define KXMLQLCVCXYPadHeight QString("Height")
+#define KXMLQLCVCXYPadPosition QString("Position")
+#define KXMLQLCVCXYPadRangeWindow QString("Window")
+#define KXMLQLCVCXYPadRangeHorizMin QString("hMin")
+#define KXMLQLCVCXYPadRangeHorizMax QString("hMax")
+#define KXMLQLCVCXYPadRangeVertMin QString("vMin")
+#define KXMLQLCVCXYPadRangeVertMax QString("vMax")
+
+#define KXMLQLCVCXYPadPositionX "X" // Legacy
+#define KXMLQLCVCXYPadPositionY "Y" // Legacy
+
+#define KXMLQLCVCXYPadInvertedAppearance "InvertedAppearance"
+
+class VCXYPad : public VCWidget
+{
+ Q_OBJECT
+
+ /*********************************************************************
+ * Initialization
+ *********************************************************************/
+public:
+ VCXYPad(Doc* doc = nullptr, QObject *parent = nullptr);
+ virtual ~VCXYPad();
+
+ /** @reimp */
+ QString defaultCaption();
+
+ /** @reimp */
+ void setupLookAndFeel(qreal pixelDensity, int page);
+
+ /** @reimp */
+ void render(QQuickView *view, QQuickItem *parent);
+
+ /** @reimp */
+ QString propertiesResource() const;
+
+ /** @reimp */
+ VCWidget *createCopy(VCWidget *parent);
+
+protected:
+ /** @reimp */
+ bool copyFrom(const VCWidget* widget);
+
+private:
+ FunctionParent functionParent() const;
+
+ /*********************************************************************
+ * Type
+ *********************************************************************/
+public:
+
+signals:
+
+private:
+
+ /*********************************************************************
+ * Data
+ *********************************************************************/
+public:
+
+protected slots:
+
+signals:
+
+private:
+
+ /*********************************************************************
+ * Functions connections
+ *********************************************************************/
+public:
+
+signals:
+
+private:
+
+ /*********************************************************************
+ * Load & Save
+ *********************************************************************/
+public:
+ bool loadXML(QXmlStreamReader &root);
+ bool saveXML(QXmlStreamWriter *doc);
+};
+
+#endif
diff --git a/qmlui/virtualconsole/virtualconsole.cpp b/qmlui/virtualconsole/virtualconsole.cpp
index bc17556a08..92664ee582 100644
--- a/qmlui/virtualconsole/virtualconsole.cpp
+++ b/qmlui/virtualconsole/virtualconsole.cpp
@@ -33,6 +33,10 @@
#include "vcslider.h"
#include "vcframe.h"
#include "vclabel.h"
+#include "vcanimation.h"
+#include "vcaudiotrigger.h"
+#include "vcxypad.h"
+#include "vcspeeddial.h"
#include "vcclock.h"
#include "vcpage.h"
#include "tardis.h"
@@ -96,6 +100,10 @@ VirtualConsole::VirtualConsole(QQuickView *view, Doc *doc,
qmlRegisterType("org.qlcplus.classes", 1, 0, "VCButton");
qmlRegisterType("org.qlcplus.classes", 1, 0, "VCLabel");
qmlRegisterType("org.qlcplus.classes", 1, 0, "VCSlider");
+ qmlRegisterType("org.qlcplus.classes", 1, 0, "VCAnimation");
+ qmlRegisterType("org.qlcplus.classes", 1, 0, "VCAudioTrigger");
+ qmlRegisterType("org.qlcplus.classes", 1, 0, "VCXYPad");
+ qmlRegisterType("org.qlcplus.classes", 1, 0, "VCSpeedDial");
qmlRegisterType("org.qlcplus.classes", 1, 0, "VCClock");
qmlRegisterType("org.qlcplus.classes", 1, 0, "VCClockSchedule");
qmlRegisterType("org.qlcplus.classes", 1, 0, "VCCueList");
diff --git a/resources/CMakeLists.txt b/resources/CMakeLists.txt
index 01910ebf6d..42ff4fe251 100644
--- a/resources/CMakeLists.txt
+++ b/resources/CMakeLists.txt
@@ -7,9 +7,6 @@ add_subdirectory(miditemplates)
add_subdirectory(modifierstemplates)
add_subdirectory(rgbscripts)
add_subdirectory(samples)
-if(NOT qmlui)
- add_subdirectory(docs)
-endif()
if(qmlui)
add_subdirectory(colorfilters)
add_subdirectory(meshes)
diff --git a/resources/colorfilters/CMakeLists.txt b/resources/colorfilters/CMakeLists.txt
index 73aeb6c5b9..bbfb7df23e 100644
--- a/resources/colorfilters/CMakeLists.txt
+++ b/resources/colorfilters/CMakeLists.txt
@@ -2,4 +2,15 @@ project(colorfilters)
file(GLOB COLOR_FILTER_FILES "${CMAKE_CURRENT_SOURCE_DIR}/*.qxcf")
-install(FILES ${COLOR_FILTER_FILES} DESTINATION ${INSTALLROOT}/${COLORFILTERSDIR})
\ No newline at end of file
+if(ANDROID)
+ set(COLOR_FILTERS_ASSETS_DIR "${ANDROID_PACKAGE_SOURCE_DIR}/${COLORFILTERSDIR}")
+ # Create the assets directory if it doesn't exist
+ file(MAKE_DIRECTORY ${COLOR_FILTERS_ASSETS_DIR})
+
+ # Copy the color filter files to the assets directory
+ foreach(COLOR_FILTER_FILE ${COLOR_FILTER_FILES})
+ file(COPY ${COLOR_FILTER_FILE} DESTINATION ${COLOR_FILTERS_ASSETS_DIR})
+ endforeach()
+endif()
+
+install(FILES ${COLOR_FILTER_FILES} DESTINATION ${INSTALLROOT}/${COLORFILTERSDIR})
diff --git a/resources/docs/CMakeLists.txt b/resources/docs/CMakeLists.txt
index ddc718d5b3..5097d7837f 100644
--- a/resources/docs/CMakeLists.txt
+++ b/resources/docs/CMakeLists.txt
@@ -1,7 +1,28 @@
project(docs)
file(GLOB HTML_FILES "${CMAKE_CURRENT_SOURCE_DIR}/html_en_EN/*.html")
-install(FILES ${HTML_FILES} DESTINATION ${INSTALLROOT}/${DOCSDIR}/html)
-
file(GLOB IMAGE_FILES "${CMAKE_CURRENT_SOURCE_DIR}/images/*.png")
-install(FILES ${IMAGE_FILES} DESTINATION ${INSTALLROOT}/${DOCSDIR}/images)
\ No newline at end of file
+
+# Only perform the copy if we're building for Android
+if(ANDROID)
+ # Define the destination directories for the HTML and image files within the Android package
+ set(HTML_ASSETS_DIR "${ANDROID_PACKAGE_SOURCE_DIR}/${DOCSDIR}/html")
+ set(IMAGES_ASSETS_DIR "${ANDROID_PACKAGE_SOURCE_DIR}/${DOCSDIR}/images")
+
+ # Create the assets directories if they don't exist
+ file(MAKE_DIRECTORY ${HTML_ASSETS_DIR})
+ file(MAKE_DIRECTORY ${IMAGES_ASSETS_DIR})
+
+ # Copy the HTML files to the assets directory
+ foreach(HTML_FILE IN LISTS HTML_FILES)
+ file(COPY ${HTML_FILE} DESTINATION ${HTML_ASSETS_DIR})
+ endforeach()
+
+ # Copy the image files to the assets directory
+ foreach(IMAGE_FILE IN LISTS IMAGE_FILES)
+ file(COPY ${IMAGE_FILE} DESTINATION ${IMAGES_ASSETS_DIR})
+ endforeach()
+endif()
+
+install(FILES ${HTML_FILES} DESTINATION ${INSTALLROOT}/${DOCSDIR}/html)
+install(FILES ${IMAGE_FILES} DESTINATION ${INSTALLROOT}/${DOCSDIR}/images)
diff --git a/resources/docs/html_en_EN/fixturemanager.html b/resources/docs/html_en_EN/fixturemanager.html
index 70a3c1d41c..e92cc2332d 100644
--- a/resources/docs/html_en_EN/fixturemanager.html
+++ b/resources/docs/html_en_EN/fixturemanager.html
@@ -100,6 +100,13 @@ Controls
Opens the Fixtures remapping window.
+
+ |
+
+ Enables or disables the Highlight Fixtures features. When enabled, this will set the selected features to max brightness
+ so you can easily verify these fixture are patched correctly.
+ |
+