diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000000..e54db6e465 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,4 @@ +# These are supported funding model platforms + +github: [ mcallegari ] +custom: ["https://www.paypal.me/mcallegariqlcplus"] diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000000..adf35871ea --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,596 @@ +name: QLCplus Github Actions CI Build + +on: ["push", "pull_request"] + +jobs: + build-linux: + #if: false + runs-on: ubuntu-20.04 + name: QLCplus Linux ${{matrix.task}} + strategy: + #fail-fast: false + matrix: + task: [compile-qt5, compile-qt5qml, coverage-qt5] + env: + CI_REPO_SLUG: ${{ github.repository }} + CI_BRANCH: ${{ github.head_ref }} + CI_PULL_REQUEST: ${{ github.event.number }} + CC: gcc + CXX: g++ + PACKAGES_BASE: + gdb + libasound2-dev + libusb-1.0-0-dev + libftdi1-dev + shared-mime-info + libudev-dev + libmad0-dev + libsndfile1-dev + liblo-dev + libfftw3-dev + libgl1-mesa-dev + libxml2-utils + xvfb + ccache + wget + PACKAGES_QML_BASE: + libpulse-dev + PACKAGES_COVERAGE_BASE: + lcov + QT_MODULES: + qtscript + defaults: + run: + shell: bash + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: false + + - name: Set General ENV variables + run: | + echo "PACKAGES_QT5=$(echo ${PACKAGES_BASE})" >> $GITHUB_ENV + echo "PACKAGES_QML=$(echo ${PACKAGES_BASE} ${PACKAGES_QML_BASE})" >> $GITHUB_ENV + echo "PACKAGES_COVERAGE=$(echo ${PACKAGES_BASE} ${PACKAGES_QT5_BASE} ${PACKAGES_COVERAGE_BASE})" >> $GITHUB_ENV + echo "PACKAGES_ALL=$(echo ${PACKAGES_BASE} ${PACKAGES_QML_BASE} ${PACKAGES_COVERAGE_BASE})" >> $GITHUB_ENV + echo "CI_BRANCH=$(echo $GITHUB_REF | cut -d '/' -f 3)" >> $GITHUB_ENV + echo "CI_SECURE_ENV_VARS=$(if [ -z '${{ secrets.something }}' ]; then echo 'false'; else echo 'true'; fi)" >> $GITHUB_ENV + echo "CI_EVENT_TYPE=$(if [ 'schedule' == '${{ github.event_name }}' ]; then echo 'cron'; else echo '${{ github.event_name }}'; fi)" >> $GITHUB_ENV + echo "NPROC=$(nproc)" >> $GITHUB_ENV + echo "TASK=$(echo '${{matrix.task}}' | cut -d '-' -f 2)" >> $GITHUB_ENV + echo "INSTALL_ROOT=`pwd`/install_root" >> $GITHUB_ENV + echo "BUILD_DATE=`date -u '+%Y%m%d'`" >> $GITHUB_ENV + echo "GIT_REV=`git rev-parse --short HEAD`" >> $GITHUB_ENV + + - name: Set QT ENV variables (qt5) + if: ${{ endsWith( matrix.task, 'qt5') }} + run: | + echo "QT=${QT:-$(echo '${{matrix.task}}' | cut -d '-' -f 2)}" >> $GITHUB_ENV + echo "QT_INSTALL_DIR=/opt" >> $GITHUB_ENV + echo "QT_VERSION=5.14.2" >> $GITHUB_ENV + echo "QT_MODULES_INSTALL=$(echo ${QT_MODULES})" >> $GITHUB_ENV + echo "APPVERSION=`grep '^!qmlui' variables.pri | grep APPVERSION | sed 's/^.*= *//;s/ /_/g'`" >> $GITHUB_ENV + source $GITHUB_ENV && echo "QTDIR=${QT_INSTALL_DIR}/Qt/${QT_VERSION}/gcc_64" >> $GITHUB_ENV + source $GITHUB_ENV && echo "QMAKE=${QTDIR}/bin/qmake" >> $GITHUB_ENV + + - name: Set QT ENV variables (qt5qml) + if: ${{ endsWith( matrix.task, 'qt5qml') }} + run: | + echo "QT=${QT:-$(echo '${{matrix.task}}' | cut -d '-' -f 2)}" >> $GITHUB_ENV + echo "QT_INSTALL_DIR=/opt" >> $GITHUB_ENV + echo "QT_VERSION=5.14.2" >> $GITHUB_ENV + echo "QT_MODULES_INSTALL=$(echo ${QT_MODULES})" >> $GITHUB_ENV + echo "APPVERSION=`grep '^qmlui' variables.pri | grep APPVERSION | sed 's/^.*= *//;s/ /_/g'`" >> $GITHUB_ENV + source $GITHUB_ENV && echo "QTDIR=${QT_INSTALL_DIR}/Qt/${QT_VERSION}/gcc_64" >> $GITHUB_ENV + source $GITHUB_ENV && echo "QMAKE=${QTDIR}/bin/qmake" >> $GITHUB_ENV + + - name: Print ENV vars + run: | + echo "CI_BRANCH: ${CI_BRANCH}" + echo "CI_PULL_REQUEST: ${CI_PULL_REQUEST}" + echo "CI_REPO_SLUG: ${CI_REPO_SLUG}" + echo "CI_EVENT_TYPE: ${CI_EVENT_TYPE}" + echo "CI_SECURE_ENV_VARS: ${CI_SECURE_ENV_VARS}" + echo "PACKAGES_QT5: ${PACKAGES_QT5}" + echo "PACKAGES_QML: ${PACKAGES_QML}" + echo "PACKAGES_COVERAGE: ${PACKAGES_COVERAGE}" + echo "TASK: ${TASK}" + echo "QT: ${QT}" + echo "NPROC: ${NPROC}" + + - name: Download, cache and install packages + uses: awalsh128/cache-apt-pkgs-action@latest + with: + packages: ${{env.PACKAGES_ALL}} + version: ${{runner.os}}-apt + + - name: Install Qt through aqt (and python) + uses: jurplel/install-qt-action@v3 + with: + version: ${{ env.QT_VERSION }} + cache: true + dir: ${{ env.QT_INSTALL_DIR }} + modules: ${{ env.QT_MODULES_INSTALL }} + + - name: Install python-lxml for fixtures-tool # with the now-registered aqt python + run: pip install lxml + + - name: Setup ccache + uses: Chocobo1/setup-ccache-action@v1 + with: + update_packager_index: false + install_ccache: false + ccache_options: | + max_size=5G + compression=false + + - name: Setup ruby cache + uses: ruby/setup-ruby@v1 + with: + ruby-version: '2.7' + bundler-cache: true + + - name: Install coveralls-lcov + if: ${{ startsWith( matrix.task, 'coverage') }} + run: sudo gem install coveralls-lcov + + - name: Print program versions and username + run: | + echo "CXX:" + ${CXX} --version + echo "QMAKE:" + ${QMAKE} -v + echo "Python:" + python --version + echo "WHOAMI:" + whoami + + - name: Configure for QT5 build + if: ${{ matrix.task == 'compile-qt5' }} + run: | + ${QMAKE} QMAKE_CXX="${CXX}" QMAKE_CC="${CC}" QMAKE_LINK="${CXX}" QMAKE_LINK_SHLIB="${CXX}" CONFIG+=appimage FORCECONFIG=release + + - name: Configure for QT5QML build + if: ${{ matrix.task == 'compile-qt5qml' }} + run: | + $QMAKE QMAKE_CXX="$CXX" QMAKE_CC="$CC" QMAKE_LINK="$CXX" QMAKE_LINK_SHLIB="$CXX" CONFIG+=appimage CONFIG+=qmlui FORCECONFIG=release + + - name: Configure for QT5 coverage build + if: ${{ matrix.task == 'coverage-qt5' }} + run: | + $QMAKE QMAKE_CXX="$CXX" QMAKE_CC="$CC" QMAKE_LINK="$CXX" QMAKE_LINK_SHLIB="$CXX" CONFIG+=coverage + #QMAKE_CXXFLAGS+="-fno-sized-deallocation" + + - name: Build + run: make -j $NPROC + + - name: Test + if: ${{ ! startsWith( matrix.task, 'coverage') }} + run: make check + + - name: Test with Coverage + if: ${{ startsWith( matrix.task, 'coverage') }} + run: make lcov + + - name: Coveralls + if: ${{ startsWith( matrix.task, 'coverage') }} + uses: coverallsapp/github-action@v2 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + file: coverage/coverage.info + format: lcov + + - name: Install + if: ${{ ! startsWith( matrix.task, 'coverage') }} + run: | + make INSTALL_ROOT=${INSTALL_ROOT} install + cp -v resources/icons/svg/qlcplus.svg ${INSTALL_ROOT} + cp -v platforms/linux/qlcplus.desktop ${INSTALL_ROOT} + + - name: Adapt qlcplus for AppImage (qt5) + if: ${{ matrix.task == 'compile-qt5' }} + run: | + chrpath -r "../lib" ${INSTALL_ROOT}/usr/bin/qlcplus + + - name: Adapt qlcplus for AppImage (qt5qml) + if: ${{ matrix.task == 'compile-qt5qml' }} + run: | + chrpath -r "../lib" ${INSTALL_ROOT}/usr/bin/qlcplus-qml + sed -i -e 's/Exec=qlcplus --open %f/Exec=qlcplus-qml/g' ${INSTALL_ROOT}/qlcplus.desktop + + - name: Store original install artifacts before stripping and AppImage + # Activate for debugging + if: ${{ false && ! startsWith( matrix.task, 'coverage') }} + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.task }}-files + path: ${{ env.INSTALL_ROOT }} + + - name: Strip Binaries (qt5) + if: ${{ matrix.task == 'compile-qt5' }} + run: | + strip -v ${INSTALL_ROOT}/usr/bin/qlcplus + find ${INSTALL_ROOT}/usr/lib/ -name libqlcplusengine.so.1.0.0 -exec strip -v {} \; + + - name: Strip Binaries (qt5qml) + if: ${{ matrix.task == 'compile-qt5qml' }} + run: | + strip -v ${INSTALL_ROOT}/usr/bin/qlcplus-qml + find ${INSTALL_ROOT}/usr/lib/ -name libqlcplusengine.so.1.0.0 -exec strip -v {} \; + + - name: Delete unused files for AppImage + if: ${{ ! startsWith( matrix.task, 'coverage') }} + run: | + find ${INSTALL_ROOT}/usr/bin/ -name plugins.qmltypes -type f -delete + find ${INSTALL_ROOT}/usr/bin -name *.qmlc -type f -delete + rm -rf ${INSTALL_ROOT}/usr/bin/QtQuick/Extras QtQuick/Particles.2 QtQuick/XmlListModel + rm -rf ${INSTALL_ROOT}/usr/bin/QtQuick/Controls.2/designer QtQuick/Controls.2/Material + rm -rf ${INSTALL_ROOT}/usr/bin/QtQuick/Controls.2/Universal QtQuick/Controls.2/Fusion + rm -rf ${INSTALL_ROOT}/usr/bin/QtQuick/Controls.2/Imagine QtQuick/Controls.2/Scene2D + + - name: Build AppImage + if: ${{ ! startsWith( matrix.task, 'coverage') }} + run: | + wget -c https://github.com/AppImage/AppImageKit/releases/download/continuous/AppRun-x86_64 -O ${INSTALL_ROOT}/AppRun + chmod a+x ${INSTALL_ROOT}/AppRun + wget -c https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage -O ./appimagetool-x86_64.AppImage + chmod a+x ./appimagetool-x86_64.AppImage + ./appimagetool-x86_64.AppImage -v ${INSTALL_ROOT} + mv -v Q_Light_Controller_Plus-x86_64.AppImage qlcplus-${{env.TASK}}-${{env.APPVERSION}}-${{env.BUILD_DATE}}-${{env.GIT_REV}}.AppImage + + - name: Test Load AppImage + if: ${{ matrix.task == 'compile-qt5qml' }} + run: | + ./qlcplus-${{env.TASK}}-${{env.APPVERSION}}-${{env.BUILD_DATE}}-${{env.GIT_REV}}.AppImage --platform offscreen --version + + - name: Store AppImage artifacts + if: ${{ ! startsWith( matrix.task, 'coverage') }} + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.task }}-AppImage + path: qlcplus-${{env.TASK}}-${{env.APPVERSION}}-${{env.BUILD_DATE}}-${{env.GIT_REV}}.AppImage + + build-windows: + runs-on: windows-latest + name: QLCplus Windows ${{matrix.task}} + strategy: + fail-fast: false + matrix: + task: [compile-qt5, compile-qt5-32bit, compile-qt5qml] + env: + CI_REPO_SLUG: ${{ github.repository }} + CI_BRANCH: ${{ github.head_ref }} + CI_PULL_REQUEST: ${{ github.event.number }} + QMAKESPEC: win32-g++ + QT_MODULES: + qtscript + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: false + + - name: Set ENV variables + shell: bash + run: | + echo "CI_BRANCH=$(echo $GITHUB_REF | cut -d '/' -f 3)" >> $GITHUB_ENV + echo "CI_SECURE_ENV_VARS=$(if [ -z '${{ secrets.something }}' ]; then echo 'false'; else echo 'true'; fi)" >> $GITHUB_ENV + echo "CI_EVENT_TYPE=$(if [ 'schedule' == '${{ github.event_name }}' ]; then echo 'cron'; else echo '${{ github.event_name }}'; fi)" >> $GITHUB_ENV + echo "NPROC=$(nproc)" >> $GITHUB_ENV + echo "TASK=$(echo '${{matrix.task}}' | cut -d '-' -f 2)" >> $GITHUB_ENV + echo "QT=${QT:-$(echo '${{matrix.task}}' | cut -d '-' -f 2)}" >> $GITHUB_ENV + echo "INSTALL_ROOT=/c/" >> $GITHUB_ENV + echo "BUILD_DATE=`date -u '+%Y%m%d'`" >> $GITHUB_ENV + echo "GIT_REV=`git rev-parse --short HEAD`" >> $GITHUB_ENV + + - name: Set v4 ENV variables + shell: bash + if: ${{ matrix.task == 'compile-qt5' || matrix.task == 'compile-qt5-32bit' }} + run: | + echo "OUTFILE=`grep 'OutFile' platforms/windows/qlcplus4Qt5.nsi | cut -d'"' -f 2`" >> $GITHUB_ENV + echo "APPVERSION=`grep '^!qmlui' variables.pri | grep APPVERSION | sed 's/^.*= *//' | cut -d ' ' -f 1`" >> $GITHUB_ENV + echo "NSIS_SCRIPT=qlcplus4Qt5.nsi" >> $GITHUB_ENV + + - name: Set v5 ENV variables + shell: bash + if: ${{ matrix.task == 'compile-qt5qml' }} + run: | + echo "OUTFILE=`grep 'OutFile' platforms/windows/qlcplus5Qt5.nsi | cut -d'"' -f 2`" >> $GITHUB_ENV + echo "APPVERSION=`grep '^qmlui' variables.pri | grep APPVERSION | sed 's/^.*= *//' | cut -d ' ' -f 1`" >> $GITHUB_ENV + echo "NSIS_SCRIPT=qlcplus5Qt5.nsi" >> $GITHUB_ENV + + - name: Print ENV vars + shell: bash + run: | + echo "CI_BRANCH: ${CI_BRANCH}" + echo "CI_PULL_REQUEST: ${CI_PULL_REQUEST}" + echo "CI_REPO_SLUG: ${CI_REPO_SLUG}" + echo "CI_EVENT_TYPE: ${CI_EVENT_TYPE}" + echo "CI_SECURE_ENV_VARS: ${CI_SECURE_ENV_VARS}" + echo "TASK: ${TASK}" + echo "QT: ${QT}" + echo "NPROC: ${NPROC}" + + - name: Update and install MSYS2 (64bit) + uses: msys2/setup-msys2@v2 + if: ${{ matrix.task == 'compile-qt5' || matrix.task == 'compile-qt5qml' }} + with: + msystem: mingw64 + release: true + update: false + path-type: inherit + install: >- + wget + unzip + 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: Update and install MSYS2 (32 bit) + uses: msys2/setup-msys2@v2 + if: ${{ matrix.task == 'compile-qt5-32bit' }} + with: + msystem: mingw32 + 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-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-nsis + + - name: Install legacy libusb (32 bit) + shell: msys2 {0} + if: ${{ matrix.task == 'compile-qt5-32bit' }} + run: | + wget https://www.qlcplus.org/misc/mingw-w64-i686-libusb-1.0.26-1-any.pkg.tar.zst + pacman --noconfirm --needed -U mingw-w64-i686-libusb-1.0.26-1-any.pkg.tar.zst + + - name: D2XX SDK (64 bit) + shell: msys2 {0} + if: ${{ matrix.task == 'compile-qt5' || matrix.task == 'compile-qt5qml' }} + run: | + 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 amd64 + gendef.exe - ftd2xx64.dll > ftd2xx.def + dlltool -k --input-def ftd2xx.def --dllname ftd2xx64.dll --output-lib libftd2xx.a + + - name: D2XX SDK (32 bit) + shell: msys2 {0} + if: ${{ matrix.task == 'compile-qt5-32bit' }} + run: | + 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 + + - name: Print program versions + shell: msys2 {0} + run: | + echo "pwd:" + pwd + echo "CXX:" + which ${CXX} || true + ${CXX} -v || true + echo "cmake:" + which cmake || true + cmake --version || true + pkg-config --modversion libusb-1.0 + + - name: Fix build + shell: msys2 {0} + run: | + # 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\:\\projects/d:\\a\\qlcplus/g' platforms/windows/${{env.NSIS_SCRIPT}} + + - name: Fix 32 bit build + if: ${{ matrix.task == 'compile-qt5-32bit' }} + shell: msys2 {0} + run: | + # fix system libs path + sed -i -e 's/mingw64/mingw32/g' platforms/windows/CMakeLists.txt + # fix gcc lib + sed -i -e 's/libgcc_s_seh/libgcc_s_dw2/g' platforms/windows/CMakeLists.txt + # fix DMX USB path + sed -i -e 's/amd64/i386/g' plugins/dmxusb/src/CMakeLists.txt + sed -i -e 's/ftd2xx64.dll/ftd2xx.dll/g' plugins/dmxusb/src/CMakeLists.txt + + - name: Configure v4 build for Windows + shell: msys2 {0} + if: ${{ matrix.task == 'compile-qt5' || matrix.task == 'compile-qt5-32bit' }} + run: | + mkdir build + cd build + cmake -G "Unix Makefiles" .. + + - name: Configure v5 build for Windows + shell: msys2 {0} + if: ${{ matrix.task == 'compile-qt5qml' }} + run: | + mkdir build + cd build + cmake -G "Unix Makefiles" -Dqmlui=ON .. + + - name: Build for Windows + shell: msys2 {0} + run: | + cd build + make -j${NPROC} + + - name: Install on Windows + shell: msys2 {0} + run: | + cd build + make install/fast + cd .. + cp *.qm /c/qlcplus + + - name: Build installation package + shell: msys2 {0} + run: | + cd /c/qlcplus + echo 'Creating package...' + makensis -X'SetCompressor /FINAL lzma' ${{env.NSIS_SCRIPT}} + mv /c/qlcplus/${{env.OUTFILE}} /d/a/qlcplus/qlcplus/${{matrix.task}}-${{env.OUTFILE}} + + - name: Store executable artifact + uses: actions/upload-artifact@v4 + with: + name: QLC+-${{matrix.task}}-${{env.APPVERSION}}-${{env.BUILD_DATE}}-${{env.GIT_REV}}.exe + path: ${{matrix.task}}-${{env.OUTFILE}} + + build-macos: + runs-on: macos-12 + name: QLCplus macOS ${{matrix.task}} + strategy: + fail-fast: false + matrix: + task: [ compile-qt5 ] + env: + CI_REPO_SLUG: ${{ github.repository }} + CI_BRANCH: ${{ github.head_ref }} + CI_PULL_REQUEST: ${{ github.event.number }} + QT_VERSION: "5.15.2" + QT_MODULES: + qtscript + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: false + + - name: Add more ENV variables + shell: bash + run: | + echo "APPVERSION=`grep '^!qmlui' variables.pri | grep APPVERSION | sed 's/^.*= *//;s/ /_/g'`" >> $GITHUB_ENV + echo "BUILD_DATE=`date -u '+%Y%m%d'`" >> $GITHUB_ENV + echo "GIT_REV=`git rev-parse --short HEAD`" >> $GITHUB_ENV + echo "QTDIR=${{ github.workspace }}/qt/Qt/${{ env.QT_VERSION }}/clang_64" >> $GITHUB_ENV + echo "NPROC=`sysctl -n hw.ncpu`" >> $GITHUB_ENV + + - name: Print ENV vars + shell: bash + run: | + echo "CI_BRANCH: ${CI_BRANCH}" + echo "CI_PULL_REQUEST: ${CI_PULL_REQUEST}" + echo "CI_REPO_SLUG: ${CI_REPO_SLUG}" + echo "CI_EVENT_TYPE: ${CI_EVENT_TYPE}" + echo "CI_SECURE_ENV_VARS: ${CI_SECURE_ENV_VARS}" + echo "TASK: ${TASK}" + echo "QTDIR: ${QTDIR}" + echo "NPROC: ${NPROC}" + + - name: Dependencies + run: | + brew update + brew install fftw libftdi + + - name: Install Qt + uses: jurplel/install-qt-action@v3 + with: + version: ${{ env.QT_VERSION }} + dir: "${{ github.workspace }}/qt/" + modules: ${{ env.QT_MODULES }} + + - name: Configure build + shell: bash + run: |- + mkdir build + cd build + cmake -DCMAKE_PREFIX_PATH="${{ env.QTDIR }}/lib/cmake" .. + + - name: Build + shell: bash + run: |- + cd build + make -j${{ env.NPROC }} + + - name: Install + shell: bash + run: |- + cd build + make install/fast + + - name: Run macosdeploy + shell: bash + run: |- + ${{ env.QTDIR }}/bin/macdeployqt ~/QLC+.app + + - name: Fix mode deps + shell: bash + run: |- + install_name_tool -change /usr/local/opt/fftw/lib/libfftw3.3.dylib @executable_path/../Frameworks/libfftw3.3.dylib ~/QLC+.app/Contents/MacOS/qlcplus + install_name_tool -change /usr/local/opt/fftw/lib/libfftw3.3.dylib @executable_path/../Frameworks/libfftw3.3.dylib ~/QLC+.app/Contents/MacOS/qlcplus-fixtureeditor + + - name: create DMG + shell: bash + run: |- + OUTDIR=$PWD + cd platforms/macos/dmg + ./create-dmg --volname "Q Light Controller Plus ${{env.APPVERSION}}" \ + --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+_${{ matrix.task }}.dmg \ + ~/QLC+.app + + - name: Store DMG artifact + uses: actions/upload-artifact@v4 + with: + name: QLC+-${{env.APPVERSION}}-${{env.BUILD_DATE}}-${{env.GIT_REV}}.dmg + path: QLC+_${{ matrix.task }}.dmg diff --git a/.gitignore b/.gitignore index 9babd309f9..f2c2c9f3c4 100644 --- a/.gitignore +++ b/.gitignore @@ -48,3 +48,7 @@ coverage/ # Development IDEs .vscode/ + +# CMake build +CMakeLists.txt.user +build* diff --git a/.travis-ci.sh b/.travis-ci.sh deleted file mode 100755 index 22df5f121e..0000000000 --- a/.travis-ci.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/bash - -# This script is triggered from the script section of .travis.yml -# It runs the appropriate commands depending on the task requested. - -export QMAKE=/opt/qt514/bin/qmake - -if [ "$TASK" = "coverage" ]; then - gem install coveralls-lcov -fi - -# Report the compiler version -$CXX --version - -# Report the qmake version -$QMAKE -v - -# Otherwise compile and check as normal -if [ "$TASK" = "compile" ]; then - if [ "$QT" = "qt5" ]; then - $QMAKE QMAKE_CXX=$CXX QMAKE_CC=$CC QMAKE_LINK=$CXX QMAKE_LINK_SHLIB=$CXX && make && make check - exit $? - fi - if [ "$QT" = "qt5qml" ]; then - $QMAKE QMAKE_CXX=$CXX QMAKE_CC=$CC QMAKE_LINK=$CXX QMAKE_LINK_SHLIB=$CXX CONFIG+=qmlui && make && make check - exit $? - fi -fi -if [ "$TASK" = "coverage" ]; then - $QMAKE CONFIG+=coverage QMAKE_CXX=$CXX QMAKE_CC=$CC QMAKE_LINK=$CXX QMAKE_LINK_SHLIB=$CXX && make && make lcov - exit $? -fi diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 7807ccc756..0000000000 --- a/.travis.yml +++ /dev/null @@ -1,85 +0,0 @@ -language: cpp -sudo: required - -script: ./.travis-ci.sh - -addons: - apt: - packages: &base_pkg - #base packages for all builds - - gdb - - libasound2-dev - - libusb-1.0-0-dev - - libftdi1-dev - - shared-mime-info - - libudev-dev - - libmad0-dev - - libsndfile1-dev - - liblo-dev - - libfftw3-dev - - libgl1-mesa-dev - # Needed for `xmllint` - - libxml2-utils - packages: &qt5_pkg - - *base_pkg - - qt514-meta-minimal - - qt514script - - qt514multimedia - - qt514serialport - packages: &qmlui_pkg - - *base_pkg - - libpulse-dev - - qt514-meta-minimal - - qt514declarative - - qt514quickcontrols2 - - qt5143d - - qt514svg - - qt514multimedia - - qt514serialport - -matrix: - fast_finish: true - include: - - os: linux - dist: bionic - compiler: gcc - env: TASK='compile' QT='qt5' - services: xvfb - addons: - apt: - sources: - - ubuntu-toolchain-r-test - - sourceline: 'ppa:beineri/opt-qt-5.14.2-bionic' - packages: - - *qt5_pkg - - os: linux - dist: bionic - compiler: gcc - env: TASK='compile' QT='qt5qml' - services: xvfb - addons: - apt: - sources: - - ubuntu-toolchain-r-test - - sourceline: 'ppa:beineri/opt-qt-5.14.2-bionic' - packages: - - *qmlui_pkg - - os: linux - dist: bionic - compiler: gcc - env: TASK='coverage' QT='qt5' - services: xvfb - addons: - apt: - sources: - - ubuntu-toolchain-r-test - - sourceline: 'ppa:beineri/opt-qt-5.14.2-bionic' - packages: - - *qt5_pkg - - lcov - -cache: - apt: true - -after_success: - - if [ "$TASK" = "coverage" ]; then coveralls-lcov --repo-token ${COVERALLS_TOKEN} coverage/coverage.info; fi diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000000..874b02f6dc --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,221 @@ +cmake_minimum_required(VERSION 3.16) +project(qlcplus VERSION 4.13.1 LANGUAGES C CXX) + +# Set Release build type by default +if(NOT CMAKE_BUILD_TYPE) + 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 +# Refer to https://stackoverflow.com/questions/7970544/cmake-make-install-output-cant-find-shared-qt-libraries-under-redhat +SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) + +SET(INSTALL_ROOT "/" CACHE STRING "Installation root directory") + +if(UNIX) + if (APPLE) + set(iokit ON) + else() + set(udev ON) + 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 +# When using Qt 6.3, you can replace the code block below with +# qt_standard_project_setup() +set(CMAKE_AUTOMOC ON) +include(GNUInstallDirs) + +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}") + +if(QT_VERSION_MAJOR EQUAL 5) + find_package(Qt${QT_VERSION_MAJOR} OPTIONAL_COMPONENTS Script) +endif() + +find_package(PkgConfig REQUIRED) + +include(./variables.cmake) +include(./coverage.cmake) + +if((((QT_VERSION_MAJOR LESS 5)) AND (APPLE))) + pkg_check_modules(PORTAUDIO_2 IMPORTED_TARGET portaudio-2.0) +endif() + +if(WIN32) + # Prefix all shared libraries with ''. + set(CMAKE_SHARED_LIBRARY_PREFIX "") +endif() + +add_subdirectory(hotplugmonitor) +add_subdirectory(engine) +add_subdirectory(resources) +add_subdirectory(plugins) +if(qmlui) + message("Building QLC+ 5 QML UI") + add_subdirectory(qmlui) +else() + message("Building QLC+ 4 QtWidget UI") + add_subdirectory(ui) + add_subdirectory(webaccess) + add_subdirectory(main) + add_subdirectory(fixtureeditor) +endif() +if(APPLE AND NOT qmlui) + add_subdirectory(launcher) +endif() + +# Unit testing thru "make check" +if(qmlui) + if(WIN32) + add_custom_target(unittests + COMMAND unittest_cmake.bat "qmlui" ${CMAKE_CURRENT_BINARY_DIR} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + ) + else() + add_custom_target(unittests + COMMAND ./unittest_cmake.sh "qmlui" ${CMAKE_CURRENT_BINARY_DIR} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + ) + endif() +else() + if(WIN32) + add_custom_target(unittests + COMMAND unittest_cmake.bat "ui" ${CMAKE_CURRENT_BINARY_DIR} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + ) + else() + add_custom_target(unittests + COMMAND ./unittest_cmake.sh "ui" ${CMAKE_CURRENT_BINARY_DIR} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + ) + endif() +endif() + +add_custom_target(check + DEPENDS unittests +) + +# Unit test coverage measurement +if(WIN32) + add_custom_target(coverage + COMMAND @echo Get a better OS. + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + ) +else() + if(qmlui) + add_custom_target(coverage + COMMAND ./coverage_cmake.sh "qmlui" + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + ) + else() + add_custom_target(coverage + COMMAND ./coverage_cmake.sh "ui" + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + ) + endif() +endif() + +add_custom_target(lcov + DEPENDS coverage +) + +# Translations +if (qmlui) + add_custom_target(translations ALL + COMMAND ./translate.sh "qmlui" + COMMENT "Translating qmlui translations..." + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) +else () + add_custom_target(translations ALL + COMMAND ./translate.sh "ui" + COMMENT "Translating ui translations..." + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) +endif() + +## Add the generated qm files to the list +file(GLOB translations_qm_files "${CMAKE_CURRENT_SOURCE_DIR}/*.qm") + +## Install the qm files +if (appimage) + set(translations_install_path "${TARGET_DIR}/${INSTALLROOT}/${TRANSLATIONDIR}") +else () + set(translations_install_path "${INSTALLROOT}/${TRANSLATIONDIR}") +endif() + +install(FILES ${translations_qm_files} DESTINATION ${translations_install_path}) + +## Clean the generated files +set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES "${CMAKE_CURRENT_BINARY_DIR}/*.qm") + +# run +if(UNIX) + if(qmlui) + add_custom_target(run + COMMAND ${CMAKE_COMMAND} -E env LD_LIBRARY_PATH=engine/src:$ENV{LD_LIBRARY_PATH} qmlui/qlcplus-qml + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) + else() + add_custom_target(run + COMMAND ${CMAKE_COMMAND} -E env LD_LIBRARY_PATH=engine/src:ui/src:webaccess/src:$ENV{LD_LIBRARY_PATH} main/qlcplus + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) + endif() +endif() + +# run-fxe +if(UNIX AND NOT qmlui) + add_custom_target(run-fxe + COMMAND ${CMAKE_COMMAND} -E env LD_LIBRARY_PATH=engine/src:ui/src:webaccess/src:$ENV{LD_LIBRARY_PATH} ./fixtureeditor/qlcplus-fixtureeditor + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) +endif() + +# doxygen +if(UNIX) + add_custom_target(doxygen + COMMAND cd resources/doxygen && rm -rf html/ && doxygen qlcplus.dox + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + ) +endif() + +# uninstall target +if(NOT TARGET uninstall) + configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" + IMMEDIATE @ONLY) + + add_custom_target(uninstall + COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake) +endif() + +SET(CPACK_GENERATOR "DEB") +set(CPACK_PACKAGE_NAME "qlcplus") +SET(CPACK_DEBIAN_PACKAGE_MAINTAINER "Massimo Callegari") #required +INCLUDE(CPack) + +# Leave this on the last row of this file +add_subdirectory(platforms) diff --git a/README.md b/README.md index ed1a5f690c..7586247a78 100644 --- a/README.md +++ b/README.md @@ -1,106 +1,111 @@ -Q Light Controller Plus 4 -========================= - -![QLC+ LOGO](resources/icons/png/qlcplus.png) - -Copyright (c) Heikki Junnila, Massimo Callegari - -QLC+ homepage: https://www.qlcplus.org/ - -QLC+ on GitHub: https://github.com/mcallegari/qlcplus - -DEVELOPERS AT WORK ------------------- - -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. that -halt the compiler immediately. 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". - -Apache 2.0 License ------------------- - -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 - -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. - -Requirements - Linux --------------------- - -* Qt >= 5.0 development libraries & tools -* libudev-dev, libmad0-dev, libsndfile1-dev, libfftw3-dev -* DMX USB plugin: libftdi-dev, pkg-config -* HID plugin: No additional requirements -* MIDI plugin: libasound, libasound-dev, pkg-config -* ENTTEC Wing plugin: No additional requirements -* 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 for Linux -* OSC plugin: No additional requirements -* ArtNet plugin: No additional requirements -* E1.31 plugin: No additional requirements -* Loopback plugin: No additional requirements - -Requirements - Windows ----------------------- - -* MSYS2 environment (https://msys2.github.io/) -* DMX USB plugin: D2XX driver & development package (http://www.ftdichip.com/Drivers/D2XX.htm) -* HID plugin: No additional requirements -* MIDI plugin: No additional requirements -* ENTTEC Wing plugin: D2XX driver & development package (http://www.ftdichip.com/Drivers/D2XX.htm) -* OLA plugin: Not available -* uDMX plugin: No additional requirements -* Peperoni plugin: No additional requirements -* Velleman plugin: K8062 SDK from www.velleman.eu -* OSC plugin: No additional requirements -* ArtNet plugin: No additional requirements -* E1.31 plugin: No additional requirements -* Loopback plugin: No additional requirements - -Requirements - 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 -* HID plugin: No additional requirements -* MIDI plugin: No additional requirements -* ENTTEC Wing plugin: No additional requirements -* 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 -* OSC plugin: No additional requirements -* ArtNet plugin: No additional requirements -* E1.31 plugin: No additional requirements -* Loopback plugin: No additional requirements - -Compiling & Installation ------------------------- + + QLC Logo + + +# Q Light Controller Plus +[![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 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.12+) and the Raspberry Pi. + +Copyright © Heikki Junnila, Massimo Callegari +### 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 + + + + + + + + + + + + + + + + + + + + + + + + + +
Home PageStatic Badge
DocumentationStatic Badge
Official ForumStatic Badge
GitHub SponsorshipGitHub Sponsors
Official MerchStatic Badge
+ +### 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 🚧 + +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. + +[![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 Please refer to the online wiki pages: https://github.com/mcallegari/qlcplus/wiki - -Support & Bug Reports ---------------------- +## 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** + +### 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 + +### macOS + +* XCode (https://developer.apple.com/xcode/) +* Qt >= 5.15.x (https://www.qt.io/download-open-source) +* homebrew (https://brew.sh/) +* DMX USB plugin: libftdi, pkg-config +* OLA plugin: ola, pkg-config (see libs/olaout/README) +* uDMX plugin: libusb, pkg-config +* Peperoni plugin: libusb, pkg-config +* Velleman plugin: **Not available** + +## Support & Bug Reports For discussions, feedbacks, ideas and new fixtures, go to: https://www.qlcplus.org/forum/index.php @@ -108,57 +113,71 @@ https://www.qlcplus.org/forum/index.php For developers wiki and code patches, go to: https://github.com/mcallegari/qlcplus -Contributors ------------- - -### 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) - +## 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. + +![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) + +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 +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. + +---- +![C++](https://img.shields.io/badge/c++-%2300599C.svg?style=for-the-badge&logo=c%2B%2B&logoColor=white) ![Qt](https://img.shields.io/badge/Qt-%23217346.svg?style=for-the-badge&logo=Qt&logoColor=white) ![CMake](https://img.shields.io/badge/CMake-%23008FBA.svg?style=for-the-badge&logo=cmake&logoColor=white) ![JavaScript](https://img.shields.io/badge/javascript-%23323330.svg?style=for-the-badge&logo=javascript&logoColor=%23F7DF1E) diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index dfd8c658f6..0000000000 --- a/appveyor.yml +++ /dev/null @@ -1,75 +0,0 @@ -version: 4.12.5.{build} - -image: Visual Studio 2019 - -environment: - MSYSTEM: MSYS - PATH: C:\msys64\mingw32\bin;C:\msys64\usr\bin;C:\Windows\System32;C:\Windows;%PATH% - QMAKESPEC: win32-g++ - -platform: - - x86 - -configuration: - - Release - -install: - #- pacman --noconfirm -Sy - #- pacman --noconfirm --needed -S pacman-mirrors - - pacman --noconfirm -Syu - - pacman --noconfirm --needed -Sy unzip mingw32/mingw-w64-i686-libmad mingw32/mingw-w64-i686-libsndfile mingw32/mingw-w64-i686-flac mingw32/mingw-w64-i686-fftw mingw32/mingw-w64-i686-libusb mingw32/mingw-w64-i686-nsis - - wget http://repo.msys2.org/mingw/i686/mingw-w64-i686-gcc-libs-11.3.0-2-any.pkg.tar.zst -P /c/projects - - wget http://repo.msys2.org/mingw/i686/mingw-w64-i686-gcc-11.3.0-2-any.pkg.tar.zst -P /c/projects - - wget http://www.qlcplus.org/misc/mingw-w64-i686-qt5-5.15.2-7-any.pkg.tar.zst -P /c/projects - - pacman --noconfirm -Rdd mingw-w64-i686-gcc - - pacman --noconfirm -Rdd mingw-w64-i686-gcc-libs - - pacman --noconfirm --needed -U /c/projects/mingw-w64-i686-gcc-libs-11.3.0-2-any.pkg.tar.zst - - pacman --noconfirm --needed -U /c/projects/mingw-w64-i686-gcc-11.3.0-2-any.pkg.tar.zst - - pacman --noconfirm --needed -U /c/projects/mingw-w64-i686-qt5-5.15.2-7-any.pkg.tar.zst - -build_script: - - ps: >- - bash -c @' - set -e - # stdin seems to be invalid on appveyor, so set it to null - exec 0&1 - export - gcc -v - qmake -v - # get and prepare the D2XX SDK - mkdir -p /c/Qt/D2XXSDK - wget http://www.ftdichip.com/Drivers/CDM/CDM%20v2.12.36.4%20WHQL%20Certified.zip -O /c/Qt/D2XXSDK/cdm.zip - cd /c/Qt/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 /c/projects/qlcplus - # disable the test units, since we won't run them - sed -i -e 's/ SUBDIRS += test/#SUBDIRS += test/g' engine/engine.pro - sed -i -e 's/SUBDIRS += test/#SUBDIRS += test/g' ui/ui.pro - sed -i -e 's/ SUBDIRS += velleman/#SUBDIRS += velleman/g' plugins/plugins.pro - sed -i -e 's/ SUBDIRS += test/#SUBDIRS += test/g' plugins/artnet/artnet.pro - sed -i -e 's/SUBDIRS += test/#SUBDIRS += test/g' plugins/enttecwing/enttecwing.pro - sed -i -e 's/SUBDIRS += test/#SUBDIRS += test/g' plugins/midi/midi.pro - qmake FORCECONFIG=release - make - echo 'Silently installing QLC+...' - make install -s - cp *.qm /c/qlcplus - cd /c/qlcplus - echo 'Workaround Harfbuzz bug' - rm libharfbuzz-0.dll - wget http://www.qlcplus.org/misc/libharfbuzz-0.dll - sed -i -e 's/Qt/projects/g' qlcplus4Qt5.nsi - echo 'Creating package...' - makensis -X'SetCompressor /FINAL lzma' qlcplus4Qt5.nsi - #set CURRDATE=`date +%Y%m%d` - mv QLC+_*.exe /c/projects/qlcplus/QLC+_$APPVEYOR_BUILD_VERSION.exe - ls /c/projects/qlcplus/*.exe - exit 0 - '@ - -artifacts: - - path: QLC+_$(APPVEYOR_BUILD_VERSION).exe - name: qlcplus_4_12_5 diff --git a/cmake_uninstall.cmake.in b/cmake_uninstall.cmake.in new file mode 100644 index 0000000000..c2d34d4796 --- /dev/null +++ b/cmake_uninstall.cmake.in @@ -0,0 +1,21 @@ +if(NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt") + message(FATAL_ERROR "Cannot find install manifest: @CMAKE_BINARY_DIR@/install_manifest.txt") +endif() + +file(READ "@CMAKE_BINARY_DIR@/install_manifest.txt" files) +string(REGEX REPLACE "\n" ";" files "${files}") +foreach(file ${files}) + message(STATUS "Uninstalling $ENV{DESTDIR}${file}") + if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") + exec_program( + "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" + OUTPUT_VARIABLE rm_out + RETURN_VALUE rm_retval + ) + if(NOT "${rm_retval}" STREQUAL 0) + message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}") + endif() + else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") + message(STATUS "File $ENV{DESTDIR}${file} does not exist.") + endif() +endforeach() diff --git a/coverage.cmake b/coverage.cmake new file mode 100644 index 0000000000..f14ad4e4a3 --- /dev/null +++ b/coverage.cmake @@ -0,0 +1,6 @@ +if(coverage) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lgcov") + file(GLOB_RECURSE gcov_files "${CMAKE_BINARY_DIR}/*.gcno" "${CMAKE_BINARY_DIR}/*.gcda") + set_property(DIRECTORY APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES ${gcov_files}) +endif() diff --git a/coverage.sh b/coverage.sh index f02214cb06..e86a3f4184 100755 --- a/coverage.sh +++ b/coverage.sh @@ -5,7 +5,9 @@ # 1. qmake # 2. make distclean # 3. qmake CONFIG+=coverage -# 4. ./coverage.sh +# 4. make -j8 +# 5. ./coverage.sh ui|qmlui # OR +# 5. make lcov # # Human-readable HTML results are written under coverage/html. # @@ -54,6 +56,7 @@ tlen=${#test[@]} # arg1:srcdir arg2:testname function prepare { lcov -d ${1} -z || exit $? + rm -f ${1}/moc_*.gcno ${1}/moc_*.gcda lcov -d ${1} -c -i -o coverage/${2}-base.info } diff --git a/coverage_cmake.sh b/coverage_cmake.sh new file mode 100755 index 0000000000..095807ef02 --- /dev/null +++ b/coverage_cmake.sh @@ -0,0 +1,136 @@ +#!/bin/bash +# +# To measure unit test coverage, perform these steps: +# 0. export CCACHE_DISABLE=1 # (only if you use compiler cache) +# 1. rm -rf build && mkdir build # Remove original build directory and recreate it. You can skip this step if you want to preserve the build directory. +# 2. cd ./build && cmake -DCMAKE_PREFIX_PATH="/home//Qt/5.15.2/gcc_64/lib/cmake|/usr/lib/x86_64-linux-gnu/cmake/Qt5" [-Dqmlui=ON] -Dcoverage=ON .. +# 3. make -j8 +# 4. make lcov +# +# Human-readable HTML results are written under coverage/html. +# + +set -e +ARCH=$(uname) +THISCMD=`basename "$0"` + +TARGET=${1:-} + +if [ "$TARGET" != "ui" ] && [ "$TARGET" != "qmlui" ]; then + echo >&2 "Usage: $THISCMD ui|qmlui" + exit 1 +fi + + +############################################################################# +# Test directories to find coverage measurements from +############################################################################# + +DEST_DIR="build" # Do NOT change to "./build" or "build/" + +COUNT=0 +test[$COUNT]="$DEST_DIR/engine/src" +COUNT=$((COUNT+1)) +if [ "$TARGET" == "ui" ]; then + test[$COUNT]="$DEST_DIR/ui/src" +COUNT=$((COUNT+1)) +fi +test[$COUNT]="$DEST_DIR/plugins/artnet/test" +COUNT=$((COUNT+1)) +test[$COUNT]="$DEST_DIR/plugins/enttecwing/src" +COUNT=$((COUNT+1)) +#test[$COUNT]="$DEST_DIR/plugins/midiinput/common/src" +#COUNT=$((COUNT+1)) +if [ ${ARCH} != "Darwin" ]; then + test[$COUNT]="$DEST_DIR/plugins/velleman/src" + COUNT=$((COUNT+1)) +fi + +# Number of tests +tlen=${#test[@]} + +############################################################################# +# Functions +############################################################################# + +# arg1:srcdir arg2:testname +function prepare { + lcov -d ${1} -z || exit $? + lcov -d ${1} -c -i -o coverage/${2}-base.info +} + +# arg1:srcdir arg2:testname +function gather_data { + lcov -d ${1} -c -o coverage/${2}-test.info + lcov -a coverage/${2}-base.info -a coverage/${2}-test.info \ + -o coverage/${2}-merge.info +} + +############################################################################# +# Initialization +############################################################################# + +# Check if lcov is installed +if [ -z "$(which lcov)" ]; then + echo "Unable to produce coverage results; can't find lcov." +fi + +# Remove previous data +if [ -d coverage ]; then + rm -rf coverage +fi + +# Create directories for new coverage data +mkdir -p coverage/html + +############################################################################# +# Preparation +############################################################################# + +for ((i = 0; i < tlen; i++)) +do + prepare ${test[i]} $i || exit $? +done + +############################################################################# +# Run unit tests +############################################################################# + +./unittest_cmake.sh $TARGET +FAILED=$? +if [ ${FAILED} != 0 ]; then + echo "Will not measure coverage because ${FAILED} unit tests failed." + exit ${FAILED} +fi + +############################################################################# +# Gather results +############################################################################# + +for ((i = 0; i < tlen; i++)) +do + gather_data ${test[i]} $i +done + +############################################################################# +# All combined and HTMLized +############################################################################# + +for ((i = 0; i < tlen; i++)) +do + mergeargs="${mergeargs} -a coverage/${i}-merge.info" +done + +lcov ${mergeargs} -o coverage/coverage.info + +# Remove stuff that isn't part of QLC sources +lcov -r coverage/coverage.info *.h -o coverage/coverage.info # Q_OBJECT etc. +lcov -r coverage/coverage.info *moc_* -o coverage/coverage.info +lcov -r coverage/coverage.info *usr* -o coverage/coverage.info +lcov -r coverage/coverage.info *_test* -o coverage/coverage.info +lcov -r coverage/coverage.info */ui_* -o coverage/coverage.info +lcov -r coverage/coverage.info */$DEST_DIR/* -o coverage/coverage.info +lcov -r coverage/coverage.info *Library* -o coverage/coverage.info # OSX + +# Generate HTML report +genhtml -o coverage/html coverage/coverage.info diff --git a/create-appimage-cmake.sh b/create-appimage-cmake.sh new file mode 100755 index 0000000000..66312c2211 --- /dev/null +++ b/create-appimage-cmake.sh @@ -0,0 +1,75 @@ +#!/bin/bash +# +# Script to create a self contained AppImage using CMake +# Requires wget and chrpath +# If you want to use the official Qt packages, please export QTDIR before running this, like: +# export QTDIR=/home/user/Qt/5.15.2/gcc_64 +# Or you can use the system Qt libraries instead by not specifying QTDIR. + +# Exit on error +set -e + +TARGET_DIR=$HOME/qlcplus.AppDir + +# Compile translations +./translate.sh "qmlui" + +# Build +if [ -d build ]; then + rm -rf build +fi +mkdir build +cd build + +if [ -n "$QTDIR" ]; then + cmake -DCMAKE_PREFIX_PATH="$QTDIR/lib/cmake/" -Dqmlui=ON -Dappimage=ON -DINSTALL_ROOT=$TARGET_DIR .. +else + cmake -DCMAKE_PREFIX_PATH="/usr/lib/x86_64-linux-gnu/cmake/Qt5" -Dqmlui=ON -Dappimage=ON -DINSTALL_ROOT=$TARGET_DIR .. +fi + +NUM_CPUS=$(nproc) || true +if [ -z "$NUM_CPUS" ]; then + NUM_CPUS=8 +fi + +make -j$NUM_CPUS +make check + +if [ ! -d "$TARGET_DIR" ]; then + mkdir $TARGET_DIR +fi +make install + +strip $TARGET_DIR/usr/bin/qlcplus-qml +# see variables.pri, where to find the LIBSDIR +find $TARGET_DIR/usr/lib/ -name 'libqlcplusengine.so*' -exec strip -v {} \; + +# FIXME: no rpath or runpath tag found. +chrpath -r "../lib" $TARGET_DIR/usr/bin/qlcplus-qml || true + +pushd $TARGET_DIR/usr/bin +find . -name plugins.qmltypes -type f -delete +find . -name *.qmlc -type f -delete +rm -rf QtQuick/Extras QtQuick/Particles.2 QtQuick/XmlListModel +rm -rf QtQuick/Controls.2/designer QtQuick/Controls.2/Material +rm -rf QtQuick/Controls.2/Universal QtQuick/Controls.2/Fusion +rm -rf QtQuick/Controls.2/Imagine QtQuick/Controls.2/Scene2D +popd + +# There might be a new version of the tool available. +wget -c https://github.com/AppImage/AppImageKit/releases/download/continuous/AppRun-x86_64 -O $TARGET_DIR/AppRun +chmod a+x $TARGET_DIR/AppRun + +cp -v ../resources/icons/svg/qlcplus.svg $TARGET_DIR +cp -v ../platforms/linux/qlcplus.desktop $TARGET_DIR +sed -i -e 's/Exec=qlcplus --open %f/Exec=qlcplus-qml/g' $TARGET_DIR/qlcplus.desktop + +# There might be a new version of the tool available. +wget -c https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage -O /tmp/appimagetool-x86_64.AppImage +chmod a+x /tmp/appimagetool-x86_64.AppImage + +pushd $TARGET_DIR/.. +/tmp/appimagetool-x86_64.AppImage -v $TARGET_DIR +popd + +echo "The application is now available at ~/Q_Light_Controller_Plus-x86_64.AppImage" diff --git a/create-dmg-cmake.sh b/create-dmg-cmake.sh new file mode 100755 index 0000000000..7f028a039a --- /dev/null +++ b/create-dmg-cmake.sh @@ -0,0 +1,58 @@ +#!/bin/bash +#VERSION=$(head -1 debian/changelog | sed 's/.*(\(.*\)).*/\1/') +VERSION=$(grep -m 1 APPVERSION variables.pri | cut -d '=' -f 2 | sed -e 's/^[[:space:]]*//' | tr ' ' _ | tr -d '\r\n') + +rm -rf build +mkdir build +cd build + +# Build +if [ -n "$QTDIR" ]; then + cmake -DCMAKE_PREFIX_PATH="$QTDIR/lib/cmake" .. +else + echo "QTDIR not set. Aborting." + exit 1 +fi + +NUM_CPUS=`sysctl -n hw.ncpu` || true +if [ -z "$NUM_CPUS" ]; then + NUM_CPUS=4 +fi + +make -j$NUM_CPUS + +if [ ! $? -eq 0 ]; then + echo Compiler error. Aborting package creation. + exit $? +fi + +# Install to ~/QLC+.app/ +make install/fast +if [ ! $? -eq 0 ]; then + echo Installation error. Aborting package creation. + exit $? +fi + +cd .. + +echo "Run macdeployqt..." +$QTDIR/bin/macdeployqt ~/QLC+.app + +echo "Fix some more dependencies..." +install_name_tool -change /usr/local/opt/fftw/lib/libfftw3.3.dylib @executable_path/../Frameworks/libfftw3.3.dylib ~/QLC+.app/Contents/MacOS/qlcplus +install_name_tool -change /usr/local/opt/fftw/lib/libfftw3.3.dylib @executable_path/../Frameworks/libfftw3.3.dylib ~/QLC+.app/Contents/MacOS/qlcplus-fixtureeditor + +# Create Apple Disk iMaGe from ~/QLC+.app/ +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 +cd - 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 d2b1b49e90..41cd3a2961 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,227 @@ +qlcplus (4.13.2) stable; urgency=low + + * New fixtures: GLP Impression X5, Ayrton Rivale Profile (thanks to Masatoshi Fujino) + * New fixture: Eurolite LED Mini Strobe Cluster SMD 48 (thanks to Oliver) + * New fixture: Ayra Compar Kit 3 (thanks to Robert) + * New fixtures: Acme Pixel Line IP, Ayrton Domino LT (thanks to Yestalgia) + * New fixture: GLP JDC1 (thanks to Flo Edelmann) + * New fixture: Shehds 2 Eyes 200W LED COB Cool Warm White (thanks to Devsider) + + -- Massimo Callegari Sun, 21 Sep 2024 18:19:20 +0200 + +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 + * Show Manager: don't freeze on infinite duration Chasers/Sequences + * 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) + * Plugins/HID: add merger mode for DMX devices (thanks to qfulmina) + * Plugins/HID: improve devices naming (thanks to qfulmina) + * 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) + * New fixture: Ayrton Mistral (thanks to Masatoshi Fujino) + * New fixture: Chauvet COLORtube 3.0 EQ Controller (thanks to Fede79) + * New fixture: Shehds Big Bee Eyes LED Wash 19x40W RGBW (thanks to István Király) + * New fixture: Fun-Generation Mr. Beam 120 W (thanks to Mariano) + + -- Massimo Callegari Thu, 30 May 2024 18:19:20 +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 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 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) + * New fixtures: Laserworld CS-1000RGB Mk3, Chauvet Gobozap (thanks to Federico) + * New fixture: Eurolite LED Bar 2 RGBA 252/10 40° Indoor (thanks to Edgar Aichinger) + * New fixture: Rockville Battery Strip 24 (thanks to Ryan Lindsey) + * New fixture: beamZ LCB244 (thanks to Bjorn Roesbeke) + * New fixture: Involight NL410 (thanks to Vorona) + * New fixture: Flash-Butrym LED PAR 64 7x10W RGBW (thanks to Paolo Betti) + * New fixture: Eurolite LED KLS-180 (thanks to Claudio Filieri) + * New fixture: Shehds LED Flat Par 7x18W RGBWA+UV (thanks to Tiago) + * New fixtures: Martin Atomic 3000 LED, Ayrton NandoBeam S3 (thanks to Yestalgia) + * New fixture: lightmaXX Vega Arc Pro II MkII (thanks to João Gonçalves) + * New fixture: Shehds LED Flat Par 18x18W RGBWA+UV (thanks to Santiago Benejam) + * New fixture: Eurolite TMH-15 (thanks to Nicolas Rasor) + * New fixtures: Shehds JMS WEBB LED Wash Big Bee Eye 19X40W, LED 230W Beam Moving Head (thanks to István Király, Feiyu Shehds) + * 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 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) + * 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 + + * engine: improve audio fade in/out + * engine: consider EFX fade in + * engine: handle LTP channels fade out + * engine: make sure input/output device names are unique + * engine: fix crash when no IO plugin is found + * engine: fix blackout to leave LTP channels untouched + * engine: add volume control to audio function + * UI/Monitor: fix 2D view multiple heads not showing correctly + * UI/Audio Editor: do not stop audio function if not previewing + * UI/Fixture Remap: add button to import fixture lists (.qxfl) + * UI/Fixture Remap: fix wrong widgets remapping + * UI/Wizard: improved generated function names (thanks to netmindz) + * UI: remember state of Function selection dialog + * Plugins/OSC: fix broadcast packets reception (thanks to Jannis Achstetter) + * Plugins/E1.31: allow to set 2 digits of the IP address + * Plugins/MIDI: fix consecutive notes not notified on macOS (thanks to Nils Tijtgat) + * Virtual Console/Audio Triggers: stop sending DMX values on deactivation + * Virtual Console/Animation: include all available presets + * Virtual Console: submaster now affects widgets on all pages but only on active frames + * Web Access: add support for widget background images + * Web Access: improve button layout and text overflow (thanks to pomowunk) + * Application: fix qxf file association on Windows (if installed as admin) + * RGB scripts: added 'Marquee' script (thanks to Branson Matheson) + * Input profiles: added ADJ MIDICON-2 (thanks to David Thomas) + * Input profiles: added Akai APC Mini MK2 (thanks to Michael Mertens) + * Fixture updated: Blizzard Lighting Pixellicious (thanks to Yestalgia) + * New fixture: Vari-Lite VL4000 Spot (thanks to Håvard Ose Nordstrand) + * New fixtures: Clay Paky Tambora Batten, Tambora Flash, Tambora Linear 100, Sharpy X Frame, Volero Wave (thanks to Gianluca Baggi) + * New fixture: beamZ BAC302 (thanks to Matej Lazar) + * New fixture: Varytec Giga Bar HEX 3 (thanks to Niklas Larsson) + * New fixtures: Equinox Fusion Orbit MKII, QTX PAR-180 (thanks to Wilbur) + * New fixtures: Cameo ROOT Par 6, AFX BARLED200-FX (thanks to Florian Faber) + * New fixtures: BoomToneDJ Moving Wash 5X15 Speed, lightmaXX Vega Spot 60 (thanks to memrex) + * New fixture: Eurolite TSL-150 (thanks to Samuel Mueller) + * New fixtures: Ayra ALO Micro Scan, Eurolite EDX-4RT, JB Systems MINI-PAR 12RGBW, N-Gear Light Spotlight 12 (thanks to Frank Rosquin) + * New fixture: Elation E Spot III (thanks to Ludovic Martinez) + * New fixtures: Chauvet Intimidator Spot 375Z IRC, Eliminator Lighting LP 12 HEX (thanks to Andrew Pavlin) + * New fixtures: Chauvet SlimPAR T12BT, Q12ILS and Q12BT (thanks to Andrew Pavlin) + * New fixture: Lumeri Eco COB 15 (thanks to Robert Rieks) + * New fixtures: Martin ERA 400 Performance, ENTTEC SMART PXL 40/60 Dot, ENTTEC CVC4, UKing ZQ-B243 (thanks to Yestalgia) + * New fixtures: Varytec Hero Wash 712 Z RGBW Zoom, Cameo Studio PAR 64 Q 8W (thanks to Anton Luka Šijanec) + * New fixtures: American DJ Jolt 300, Showtec Star Dream 144 LED White (thanks to David Gouronc) + * New fixture: Antari Z-1000 MKII (thanks to Jérôme) + * New fixtures: Event Lighting PAR19x12O, PAR6x12OB2, PAR6x12OB, Pixbar 12x12W (thanks to Michael Tosatto) + * New fixture: U'King ZQ-B93 Pinspot RGBW (thanks to Jarosław Biernacki) + * New fixtures: Stage Right 200W COB LED Ellipsoidal, Stage Right 30W LED Spot (thanks to Dave Vecchio) + * New fixture: Rockville Rockwedge LED (thanks to Ryan Carter) + * New fixtures: Showtec Starforce LED, Equinox Fusion Spot MK III (thanks to gnomesenpai) + * New fixture: Chauvet SlimPAR Pro Pix (thanks to Ryan Carter) + * New fixture: DTS Scena LED 200 (thanks to Freddy Hoogstoel) + * New fixture: Chauvet Intimidator Beam Q60 (thanks to Nathan) + * New fixtures: Eurolite LED T-36 RGB Spot, Eurolite LED SLS-183/10 RGB, Stairville Stage PAR CX-2 RGBAW (thanks to Felix Hartnagel) + * New fixture: American DJ WiFly Bar QA5 (thanks to Edgar Aichinger) + * New fixtures: Laserworld EL-230RGB MK2, Eurolite LED Multi FX Laser Bar, UKing Mini Double Sided Moving Head (thanks to e-shock) + * New fixture: lightmaXX Vega Shiggy Beam Wash (thanks to Jarada) + * New fixture: Eurolite LED Super Strobe ABL (thanks to Sebastian Moeckel) + * New fixture: beamZ Fuze75B Beam (thanks to Marcin Kwiecien) + * New fixture: Eurolite LED FE-800 (thanks to Clausi4711) + * New fixtures: Varytec LED Bar 240/8 CW/WW, Equinox SpectraPix Batten, Betopper LPC-017 (thanks to Michel Sliepenbeek) + * New fixtures: Chauvet Intimidator Scan LED 100, Equinox Fusion Spot Max MKIII (thanks to Michel Sliepenbeek) + * New fixture: Elation SIX PAR Z19 IP (thanks to Sophia Rodriguez) + * New fixture: Betopper LPC007 (thanks to Romil Barticulo) + * New fixture: Betopper LM108 Wash Moving Head (thanks to Luca Giovannesi) + * New fixture: Starway Servo Beam 10R (thanks to David Talet) + * New fixture: Cameo Zenit B200 (thanks to ikr) + * New fixture: Stairville BEL4 - Battery Event Light 4x15W (thanks to Pascal) + * New fixture: U'King B117 Par Can 4in1 RGBW (thanks to Turning Point) + * New fixture: Chauvet Followspot 120ST (thanks to Scott Yarbrough) + * New fixture: Chauvet COLORband T3 BT (thanks to William Todd) + + -- Massimo Callegari Fri, 19 May 2023 12:13:14 +0200 + +qlcplus (4.12.6) stable; urgency=low + + * Engine: start Script functions detached (thanks to Thierry) + * Plugins/E1.31: fix CID on loopback device + * Plugins/uDMX: fix output not working correctly + * Web Access: fix VC Slider values disappearing on change (thanks to Thierry) + * RGB scripts: added 'Circular' script (thanks to Hans-Jürgen Tappe) + * Channel modifiers: added 'S-Curve" modifier (thanks to Giacomo Gorini) + * New fixture: Cameo F2 T PO (thanks to Hans-Jürgen Tappe) + * New fixtures: Ibiza PAR LED 710, AFX Spot 60 LED, Ghost Venum 12W RGBW (thanks to erdnaxe) + * New fixtures: ETC Source Four LED Series 2 Lustr, Robert Juliat D'Artagnan 934SNX (thanks to Giacomo Gorini) + * New fixture: DTS Katana (thanks to Federico) + * New fixture: beamZ SB200 Stage Blinder 2x50W (thanks to Tolmino Muccitelli) + * New fixture: U'King ZQ-B370 (thanks to Sidde Persson) + * New fixture: Equinox Butterfly Quad EQLED100 (thanks to Yestalgia) + * New fixture: Varytec Hero Spot 90 (thanks to Chris de Rock) + * New fixtures: Eurolite LED H2O Water Effect, Stairville All FX Bar (thanks to Rami Toivola) + + -- Massimo Callegari Sun, 28 Aug 2022 12:13:14 +0200 + qlcplus (4.12.5) stable; urgency=low * engine: add support for 16bit fading @@ -337,538 +561,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/debian/control b/debian/control index 59c59aede8..70fbdddc2c 100644 --- a/debian/control +++ b/debian/control @@ -9,8 +9,9 @@ Build-Depends: libfftw3-dev, libftdi1-dev (>= 0.17), libmad0-dev, - qtbase5-dev, qtscript5-dev, qtmultimedia5-dev, libsndfile1-dev, + qtbase5-dev, qtscript5-dev, + qtmultimedia5-dev, libqt5serialport5-dev, libstdc++-dev, libudev-dev, libusb-1.0-0-dev (>= 2:0.1.12), diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt new file mode 100644 index 0000000000..b1208fb0cc --- /dev/null +++ b/engine/CMakeLists.txt @@ -0,0 +1,9 @@ +project(engine VERSION 1.0.0) + +pkg_check_modules(FFTW3 IMPORTED_TARGET fftw3) + +add_subdirectory(audio) +add_subdirectory(src) +if(NOT ANDROID AND NOT IOS) + add_subdirectory(test) +endif() diff --git a/engine/audio/CMakeLists.txt b/engine/audio/CMakeLists.txt new file mode 100644 index 0000000000..79e341e634 --- /dev/null +++ b/engine/audio/CMakeLists.txt @@ -0,0 +1,4 @@ +project(audio) + +add_subdirectory(plugins) +add_subdirectory(src) diff --git a/engine/audio/plugins/CMakeLists.txt b/engine/audio/plugins/CMakeLists.txt new file mode 100644 index 0000000000..e9a54a0e5e --- /dev/null +++ b/engine/audio/plugins/CMakeLists.txt @@ -0,0 +1,13 @@ +project(plugins) + +if((((NOT ANDROID AND NOT IOS)))) + pkg_check_modules(MAD IMPORTED_TARGET mad) + if(${MAD_FOUND}) + add_subdirectory(mad) + endif() + + pkg_check_modules(SNDFILE IMPORTED_TARGET sndfile) + if(${SNDFILE_FOUND}) + add_subdirectory(sndfile) + endif() +endif() diff --git a/engine/audio/plugins/mad/CMakeLists.txt b/engine/audio/plugins/mad/CMakeLists.txt new file mode 100644 index 0000000000..3595d197de --- /dev/null +++ b/engine/audio/plugins/mad/CMakeLists.txt @@ -0,0 +1,32 @@ +if(NOT MAD_INCLUDE_DIRS) + find_path(MAD_INCLUDE_DIR mad.h PATH_SUFFIXES include) + if(NOT MAD_INCLUDE_DIR) + message(FATAL_ERROR "[ERROR] Could not find mad.h header file!") + endif() + set(MAD_INCLUDE_DIRS ${MAD_INCLUDE_DIR}) +endif() + +set(module_name "madplugin") + +add_library(${module_name} SHARED) +target_sources(${module_name} PRIVATE + ../../src/audiodecoder.cpp ../../src/audiodecoder.h + ../../src/audioparameters.cpp ../../src/audioparameters.h + audiodecoder_mad.cpp audiodecoder_mad.h +) + +target_include_directories(${module_name} PRIVATE + ../../src + ${MAD_INCLUDE_DIRS} +) + +target_link_libraries(${module_name} PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + ${MAD_LINK_LIBRARIES} +) + +install(TARGETS ${module_name} + LIBRARY DESTINATION ${INSTALLROOT}/${AUDIOPLUGINDIR} + RUNTIME DESTINATION ${INSTALLROOT}/${AUDIOPLUGINDIR} +) \ No newline at end of file diff --git a/engine/audio/plugins/mad/audiodecoder_mad.cpp b/engine/audio/plugins/mad/audiodecoder_mad.cpp index 003527fa83..a90a9d533d 100644 --- a/engine/audio/plugins/mad/audiodecoder_mad.cpp +++ b/engine/audio/plugins/mad/audiodecoder_mad.cpp @@ -249,7 +249,7 @@ bool AudioDecoderMAD::findHeader() if (mad_header_decode(&header, &m_stream) < 0) { - if(m_stream.error == MAD_ERROR_LOSTSYNC) + if (m_stream.error == MAD_ERROR_LOSTSYNC) { uint tagSize = findID3v2((uchar *)m_stream.this_frame, (ulong) (m_stream.bufend - m_stream.this_frame)); @@ -360,11 +360,11 @@ qint64 AudioDecoderMAD::read(char *data, qint64 size) { forever { - if(((m_stream.error == MAD_ERROR_BUFLEN) || !m_stream.buffer) && !m_eof) + if (((m_stream.error == MAD_ERROR_BUFLEN) || !m_stream.buffer) && !m_eof) { m_eof = !fillBuffer(); } - if(mad_frame_decode(&m_frame, &m_stream) < 0) + if (mad_frame_decode(&m_frame, &m_stream) < 0) { switch((int) m_stream.error) { @@ -381,7 +381,7 @@ qint64 AudioDecoderMAD::read(char *data, qint64 size) continue; } case MAD_ERROR_BUFLEN: - if(m_eof) + if (m_eof) return 0; continue; default: @@ -391,7 +391,7 @@ qint64 AudioDecoderMAD::read(char *data, qint64 size) continue; } } - if(m_skip_frames) + if (m_skip_frames) { m_skip_frames--; continue; @@ -402,7 +402,7 @@ qint64 AudioDecoderMAD::read(char *data, qint64 size) } void AudioDecoderMAD::seek(qint64 pos) { - if(m_totalTime > 0) + if (m_totalTime > 0) { qint64 seek_pos = qint64(pos * m_input.size() / m_totalTime); m_input.seek(seek_pos); @@ -437,7 +437,7 @@ bool AudioDecoderMAD::fillBuffer() qDebug("DecoderMAD: end of file"); return false; } - else if(len < 0) + else if (len < 0) { qWarning("DecoderMAD: error"); return false; @@ -546,7 +546,7 @@ qint64 AudioDecoderMAD::madOutput(char *data, qint64 size) m_output_at = 0; m_output_bytes = 0; - if(samples * channels * 2 > size) + if (samples * channels * 2 > size) { qWarning() << "DecoderMad: input buffer is too small. Required: " << (samples * channels * 2) << ", available: " << size; samples = size / channels / 2; diff --git a/engine/audio/plugins/sndfile/CMakeLists.txt b/engine/audio/plugins/sndfile/CMakeLists.txt new file mode 100644 index 0000000000..5a7430094e --- /dev/null +++ b/engine/audio/plugins/sndfile/CMakeLists.txt @@ -0,0 +1,23 @@ +set(module_name "sndfileplugin") + +add_library(${module_name} SHARED) +target_sources(${module_name} PRIVATE + ../../src/audiodecoder.cpp ../../src/audiodecoder.h + ../../src/audioparameters.cpp ../../src/audioparameters.h + audiodecoder_sndfile.cpp audiodecoder_sndfile.h +) +target_include_directories(${module_name} PRIVATE + ../../src + ${SNDFILE_INCLUDE_DIRS} +) + +target_link_libraries(${module_name} PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + ${SNDFILE_LINK_LIBRARIES} +) + +install(TARGETS ${module_name} + LIBRARY DESTINATION ${INSTALLROOT}/${AUDIOPLUGINDIR} + RUNTIME DESTINATION ${INSTALLROOT}/${AUDIOPLUGINDIR} +) \ No newline at end of file diff --git a/engine/audio/plugins/sndfile/audiodecoder_sndfile.cpp b/engine/audio/plugins/sndfile/audiodecoder_sndfile.cpp index 69ab00ae88..3eb960b870 100644 --- a/engine/audio/plugins/sndfile/audiodecoder_sndfile.cpp +++ b/engine/audio/plugins/sndfile/audiodecoder_sndfile.cpp @@ -78,7 +78,7 @@ bool AudioDecoderSndFile::initialize(const QString &path) m_totalTime = snd_info.frames * 1000 / m_freq; m_bitrate = QFileInfo(m_path).size () * 8.0 / m_totalTime + 0.5; - if((snd_info.format & SF_FORMAT_SUBMASK) == SF_FORMAT_FLOAT) + if ((snd_info.format & SF_FORMAT_SUBMASK) == SF_FORMAT_FLOAT) { qDebug() << "DecoderSndFile: Float audio format"; sf_command (m_sndfile, SFC_SET_SCALE_FLOAT_INT_READ, NULL, SF_TRUE); diff --git a/engine/audio/src/CMakeLists.txt b/engine/audio/src/CMakeLists.txt new file mode 100644 index 0000000000..cd51c39823 --- /dev/null +++ b/engine/audio/src/CMakeLists.txt @@ -0,0 +1,102 @@ +set(module_name "qlcplusaudio") + +add_library(${module_name} + STATIC + audio.cpp audio.h + audiocapture.cpp audiocapture.h + audiodecoder.cpp audiodecoder.h + audioparameters.cpp audioparameters.h + 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 +) + +target_link_libraries(${module_name} PUBLIC + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Multimedia +) + +if(APPLE) + set_target_properties(${module_name} PROPERTIES + MACOSX_BUNDLE FALSE + ) +endif() + +if(WIN32) + target_link_libraries(${module_name} PUBLIC + Qt${QT_MAJOR_VERSION}::Widgets + ) +endif() + +if(((QT_VERSION_MAJOR LESS 5)) AND (UNIX AND NOT APPLE)) + target_sources(${module_name} PRIVATE + audiocapture_alsa.cpp audiocapture_alsa.h + audiorenderer_alsa.cpp audiorenderer_alsa.h + ) +endif() + +if(((QT_VERSION_MAJOR LESS 5)) AND (WIN32)) + target_sources(${module_name} PRIVATE + audiocapture_wavein.cpp audiocapture_wavein.h + audiorenderer_waveout.cpp audiorenderer_waveout.h + ) +endif() + +if((QT_VERSION_MAJOR LESS 6)) + target_sources(${module_name} PRIVATE + audiocapture_qt5.cpp audiocapture_qt5.h + audiorenderer_qt5.cpp audiorenderer_qt5.h + ) +endif() + +if(NOT ((QT_VERSION_MAJOR LESS 6))) + target_sources(${module_name} PRIVATE + audiocapture_qt6.cpp audiocapture_qt6.h + audiorenderer_qt6.cpp audiorenderer_qt6.h + ) +endif() + +if((((QT_VERSION_MAJOR LESS 5)) AND (APPLE))) + if (${PORTAUDIO_2_FOUND}) + target_sources(${module_name} PRIVATE + audiocapture_portaudio.cpp audiocapture_portaudio.h + audiorenderer_portaudio.cpp audiorenderer_portaudio.h + ) + + target_compile_definitions(${module_name} PUBLIC + HAS_PORTAUDIO + ) + + target_include_directories(${module_name} PUBLIC + ${PORTAUDIO_2_INCLUDE_DIRS} + ) + + target_link_libraries(${module_name} PUBLIC + ${PORTAUDIO_2_LIBRARIES} + ) + endif() +endif() + +if((NOT ANDROID AND NOT IOS) AND (${FFTW3_FOUND})) + target_compile_definitions(${module_name} PUBLIC + HAS_FFTW3 + ) + + target_include_directories(${module_name} PUBLIC + ${FFTW3_INCLUDE_DIRS} + ) + target_link_libraries(${module_name} PUBLIC + ${FFTW3_LINK_LIBRARIES} + ) +endif() + +if(UNIX AND NOT ANDROID AND NOT IOS AND NOT APPLE) + target_link_libraries(${module_name} PUBLIC + asound + ) +endif() diff --git a/engine/audio/src/audio.cpp b/engine/audio/src/audio.cpp index c847b001a2..558f59384a 100644 --- a/engine/audio/src/audio.cpp +++ b/engine/audio/src/audio.cpp @@ -46,6 +46,7 @@ #define KXMLQLCAudioSource QString("Source") #define KXMLQLCAudioDevice QString("Device") +#define KXMLQLCAudioVolume QString("Volume") /***************************************************************************** * Initialization @@ -59,6 +60,7 @@ Audio::Audio(Doc* doc) , m_audioDevice(QString()) , m_sourceFileName("") , m_audioDuration(0) + , m_volume(1.0) { setName(tr("New Audio")); setRunOrder(Audio::SingleShot); @@ -173,6 +175,7 @@ bool Audio::setSourceFileName(QString filename) if (m_decoder == NULL) return false; + setDuration(m_decoder->totalTime()); setTotalDuration(m_decoder->totalTime()); emit changed(id()); @@ -195,6 +198,16 @@ void Audio::setAudioDevice(QString dev) m_audioDevice = dev; } +qreal Audio::volume() const +{ + return m_volume; +} + +void Audio::setVolume(qreal volume) +{ + m_volume = volume; +} + QString Audio::audioDevice() { return m_audioDevice; @@ -205,7 +218,7 @@ int Audio::adjustAttribute(qreal fraction, int attributeId) int attrIndex = Function::adjustAttribute(fraction, attributeId); if (m_audio_out != NULL && attrIndex == Intensity) - m_audio_out->adjustIntensity(getAttributeValue(Function::Intensity)); + m_audio_out->adjustIntensity(m_volume * getAttributeValue(Function::Intensity)); return attrIndex; } @@ -253,6 +266,9 @@ bool Audio::saveXML(QXmlStreamWriter *doc) if (m_audioDevice.isEmpty() == false) doc->writeAttribute(KXMLQLCAudioDevice, m_audioDevice); + if (m_volume != 1.0) + doc->writeAttribute(KXMLQLCAudioVolume, QString::number(m_volume)); + doc->writeCharacters(m_doc->normalizeComponentPath(m_sourceFileName)); doc->writeEndElement(); @@ -288,6 +304,8 @@ bool Audio::loadXML(QXmlStreamReader &root) if (attrs.hasAttribute(KXMLQLCAudioDevice)) setAudioDevice(attrs.value(KXMLQLCAudioDevice).toString()); + if (attrs.hasAttribute(KXMLQLCAudioVolume)) + setVolume(attrs.value(KXMLQLCAudioVolume).toString().toDouble()); setSourceFileName(m_doc->denormalizeComponentPath(root.readElementText())); } @@ -322,6 +340,15 @@ void Audio::preRun(MasterTimer* timer) { if (m_decoder != NULL) { + uint fadeIn = overrideFadeInSpeed() == defaultSpeed() ? fadeInSpeed() : overrideFadeInSpeed(); + + if (m_audio_out != NULL && m_audio_out->isRunning()) + { + m_audio_out->stop(); + m_audio_out->deleteLater(); + m_audio_out = NULL; + } + m_decoder->seek(elapsed()); AudioParameters ap = m_decoder->audioParameters(); #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) @@ -341,8 +368,8 @@ void Audio::preRun(MasterTimer* timer) #endif m_audio_out->setDecoder(m_decoder); m_audio_out->initialize(ap.sampleRate(), ap.channels(), ap.format()); - m_audio_out->adjustIntensity(getAttributeValue(Intensity)); - m_audio_out->setFadeIn(fadeInSpeed()); + m_audio_out->adjustIntensity(m_volume * getAttributeValue(Intensity)); + m_audio_out->setFadeIn(elapsed() ? 0 : fadeIn); m_audio_out->setLooped(runOrder() == Audio::Loop); m_audio_out->start(); connect(m_audio_out, SIGNAL(endOfStreamReached()), @@ -378,10 +405,15 @@ void Audio::write(MasterTimer* timer, QList universes) incrementElapsed(); - if (overrideFadeOutSpeed() == defaultSpeed()) + if (m_audio_out && !m_audio_out->isLooped()) { - if (m_audio_out != NULL && totalDuration() - elapsed() <= fadeOutSpeed()) - m_audio_out->setFadeOut(fadeOutSpeed()); + uint fadeout = overrideFadeOutSpeed() == defaultSpeed() ? fadeOutSpeed() : overrideFadeOutSpeed(); + + if (fadeout) + { + if (m_audio_out != NULL && totalDuration() - elapsed() <= fadeOutSpeed()) + m_audio_out->setFadeOut(fadeOutSpeed()); + } } } @@ -389,14 +421,16 @@ void Audio::postRun(MasterTimer* timer, QList universes) { // Check whether a fade out is needed "outside" of the natural playback // This is the case of a Chaser step - if (overrideFadeOutSpeed() == defaultSpeed()) + uint fadeout = overrideFadeOutSpeed() == defaultSpeed() ? fadeOutSpeed() : overrideFadeOutSpeed(); + + if (fadeout == 0) { slotEndOfStream(); } else { if (m_audio_out != NULL) - m_audio_out->setFadeOut(overrideFadeOutSpeed()); + m_audio_out->setFadeOut(fadeout); } Function::postRun(timer, universes); diff --git a/engine/audio/src/audio.h b/engine/audio/src/audio.h index da0c09f093..b1263438d6 100644 --- a/engine/audio/src/audio.h +++ b/engine/audio/src/audio.h @@ -109,6 +109,10 @@ public slots: */ void setAudioDevice(QString dev); + /** Get/Set the audio function startup volume */ + qreal volume() const; + void setVolume(qreal volume); + /** * Retrieve the audio device set for this function */ @@ -129,16 +133,12 @@ protected slots: AudioRenderer *m_audio_out; /** Audio device to use for rendering */ QString m_audioDevice; - /** Absolute start time of Audio over a timeline (in milliseconds) */ - quint32 m_startTime; - /** Color to use when displaying the audio object in the Show manager */ - QColor m_color; - /** Flag to indicate if a Audio item is locked in the Show Manager timeline */ - bool m_locked; /** Name of the source audio file */ QString m_sourceFileName; /** Duration of the media object */ qint64 m_audioDuration; + /** Startup volume of the audio file */ + qreal m_volume; /********************************************************************* * Save & Load diff --git a/engine/audio/src/audiocapture.cpp b/engine/audio/src/audiocapture.cpp index 17e6d4245d..809c5c5eba 100644 --- a/engine/audio/src/audiocapture.cpp +++ b/engine/audio/src/audiocapture.cpp @@ -235,7 +235,7 @@ void AudioCapture::processData() #endif // 5 ********* Calculate the average signal power - foreach(int barsNumber, m_fftMagnitudeMap.keys()) + foreach (int barsNumber, m_fftMagnitudeMap.keys()) { maxMagnitude = fillBandsData(barsNumber); pwrSum = 0.; 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_portaudio.cpp b/engine/audio/src/audiocapture_portaudio.cpp index 47c46acdce..a10840ee92 100644 --- a/engine/audio/src/audiocapture_portaudio.cpp +++ b/engine/audio/src/audiocapture_portaudio.cpp @@ -45,7 +45,7 @@ bool AudioCapturePortAudio::initialize() PaStreamParameters inputParameters; err = Pa_Initialize(); - if( err != paNoError ) + if (err != paNoError) return false; QSettings settings; @@ -64,18 +64,18 @@ bool AudioCapturePortAudio::initialize() inputParameters.channelCount = m_channels; inputParameters.sampleFormat = paInt16; - inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputParameters.device )->defaultLowInputLatency; + inputParameters.suggestedLatency = Pa_GetDeviceInfo(inputParameters.device)->defaultLowInputLatency; inputParameters.hostApiSpecificStreamInfo = NULL; // ensure initialize() has not been called multiple times Q_ASSERT(stream == NULL); /* -- setup stream -- */ - err = Pa_OpenStream( &stream, &inputParameters, NULL, m_sampleRate, paFramesPerBufferUnspecified, + err = Pa_OpenStream(&stream, &inputParameters, NULL, m_sampleRate, paFramesPerBufferUnspecified, paClipOff, /* we won't output out of range samples so don't bother clipping them */ NULL, /* no callback, use blocking API */ - NULL ); /* no callback, so no callback userData */ - if( err != paNoError ) + NULL); /* no callback, so no callback userData */ + if (err != paNoError) { qWarning("Cannot open audio input stream (%s)\n", Pa_GetErrorText(err)); Pa_Terminate(); @@ -83,11 +83,11 @@ bool AudioCapturePortAudio::initialize() } /* -- start capture -- */ - err = Pa_StartStream( stream ); - if( err != paNoError ) + err = Pa_StartStream(stream); + if (err != paNoError) { qWarning("Cannot start stream capture (%s)\n", Pa_GetErrorText(err)); - Pa_CloseStream( stream ); + Pa_CloseStream(stream); stream = NULL; Pa_Terminate(); return false; @@ -103,19 +103,19 @@ void AudioCapturePortAudio::uninitialize() PaError err; /* -- Now we stop the stream -- */ - err = Pa_StopStream( stream ); - if( err != paNoError ) - qDebug() << "PortAudio error: " << Pa_GetErrorText( err ); + err = Pa_StopStream(stream); + if (err != paNoError) + qDebug() << "PortAudio error: " << Pa_GetErrorText(err); /* -- don't forget to cleanup! -- */ - err = Pa_CloseStream( stream ); - if( err != paNoError ) - qDebug() << "PortAudio error: " << Pa_GetErrorText( err ); + err = Pa_CloseStream(stream); + if (err != paNoError) + qDebug() << "PortAudio error: " << Pa_GetErrorText(err); stream = NULL; err = Pa_Terminate(); - if( err != paNoError ) - qDebug() << "PortAudio error: " << Pa_GetErrorText( err ); + if (err != paNoError) + qDebug() << "PortAudio error: " << Pa_GetErrorText(err); } qint64 AudioCapturePortAudio::latency() @@ -135,10 +135,10 @@ bool AudioCapturePortAudio::readAudio(int maxSize) { Q_ASSERT(stream != NULL); - int err = Pa_ReadStream( stream, m_audioBuffer, maxSize ); - if( err ) + int err = Pa_ReadStream(stream, m_audioBuffer, maxSize); + if (err) { - qWarning("read from audio interface failed (%s)\n", Pa_GetErrorText (err)); + qWarning("read from audio interface failed (%s)\n", Pa_GetErrorText(err)); return false; } 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/audio/src/audiocapture_wavein.cpp b/engine/audio/src/audiocapture_wavein.cpp index c89fd85d8d..4c50fbd680 100644 --- a/engine/audio/src/audiocapture_wavein.cpp +++ b/engine/audio/src/audiocapture_wavein.cpp @@ -154,7 +154,7 @@ bool AudioCaptureWaveIn::readAudio(int maxSize) return false; } - while ( (waveHeaders[m_currentBufferIndex].dwFlags & WHDR_DONE) == 0) + while ((waveHeaders[m_currentBufferIndex].dwFlags & WHDR_DONE) == 0) usleep(100); memcpy(m_audioBuffer, m_internalBuffers[m_currentBufferIndex], maxSize * 2); diff --git a/engine/audio/src/audioplugincache.cpp b/engine/audio/src/audioplugincache.cpp index 617eaa0f8b..b8d6b0430d 100644 --- a/engine/audio/src/audioplugincache.cpp +++ b/engine/audio/src/audioplugincache.cpp @@ -28,7 +28,7 @@ #include "qlcfile.h" #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) - #if defined( __APPLE__) || defined(Q_OS_MAC) + #if defined(__APPLE__) || defined(Q_OS_MAC) #include "audiorenderer_portaudio.h" #elif defined(WIN32) || defined(Q_OS_WIN) #include "audiorenderer_waveout.h" @@ -44,32 +44,33 @@ AudioPluginCache::AudioPluginCache(QObject *parent) : QObject(parent) { +} + +AudioPluginCache::~AudioPluginCache() +{ +} + +void AudioPluginCache::load(const QDir &dir) +{ + qDebug() << Q_FUNC_INFO << dir.path(); + #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) - #if defined( __APPLE__) || defined(Q_OS_MAC) +#if defined(__APPLE__) || defined(Q_OS_MAC) m_audioDevicesList = AudioRendererPortAudio::getDevicesInfo(); - #elif defined(WIN32) || defined(Q_OS_WIN) +#elif defined(WIN32) || defined(Q_OS_WIN) m_audioDevicesList = AudioRendererWaveOut::getDevicesInfo(); - #else +#else m_audioDevicesList = AudioRendererAlsa::getDevicesInfo(); - #endif +#endif #else - #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) m_audioDevicesList = AudioRendererQt5::getDevicesInfo(); m_outputDevicesList = QAudioDeviceInfo::availableDevices(QAudio::AudioOutput); - #else +#else m_audioDevicesList = AudioRendererQt6::getDevicesInfo(); m_outputDevicesList = QMediaDevices::audioOutputs(); - #endif #endif -} - -AudioPluginCache::~AudioPluginCache() -{ -} - -void AudioPluginCache::load(const QDir &dir) -{ - qDebug() << Q_FUNC_INFO << dir.path(); +#endif /* Check that we can access the directory */ if (dir.exists() == false || dir.isReadable() == false) @@ -102,7 +103,7 @@ void AudioPluginCache::load(const QDir &dir) QStringList AudioPluginCache::getSupportedFormats() { QStringList caps; - foreach(QString path, m_pluginsMap.values()) + foreach (QString path, m_pluginsMap.values()) { QPluginLoader loader(path, this); AudioDecoder* ptr = qobject_cast (loader.instance()); @@ -123,7 +124,7 @@ AudioDecoder *AudioPluginCache::getDecoderForFile(const QString &filename) if (fn.exists() == false) return NULL; - foreach(QString path, m_pluginsMap.values()) + foreach (QString path, m_pluginsMap.values()) { QPluginLoader loader(path, this); AudioDecoder* ptr = qobject_cast (loader.instance()); diff --git a/engine/audio/src/audiorenderer.cpp b/engine/audio/src/audiorenderer.cpp index ccfdda4921..37e2f5421a 100644 --- a/engine/audio/src/audiorenderer.cpp +++ b/engine/audio/src/audiorenderer.cpp @@ -25,6 +25,7 @@ AudioRenderer::AudioRenderer (QObject* parent) : QThread (parent) + , m_looped(false) , m_fadeStep(0.0) , m_userStop(true) , m_pause(false) @@ -33,7 +34,6 @@ AudioRenderer::AudioRenderer (QObject* parent) , m_adec(NULL) , audioDataRead(0) , pendingAudioBytes(0) - , m_looped(false) { } @@ -47,8 +47,25 @@ void AudioRenderer::adjustIntensity(qreal fraction) m_intensity = CLAMP(fraction, 0.0, 1.0); } +bool AudioRenderer::isLooped() +{ + return m_looped; +} + +void AudioRenderer::setLooped(bool looped) +{ + m_looped = looped; +} + +/********************************************************************* + * Fade sequences + *********************************************************************/ + void AudioRenderer::setFadeIn(uint fadeTime) { + m_fadeStep = 0; + m_currentIntensity = 1.0; + if (fadeTime == 0 || m_adec == NULL) return; @@ -63,14 +80,13 @@ void AudioRenderer::setFadeIn(uint fadeTime) void AudioRenderer::setFadeOut(uint fadeTime) { - if (fadeTime == 0 || m_fadeStep != 0 || m_adec == NULL) + if (fadeTime == 0 || m_adec == NULL) return; quint32 sampleRate = m_adec->audioParameters().sampleRate(); int channels = m_adec->audioParameters().channels(); qreal stepsCount = (qreal)fadeTime * ((qreal)(sampleRate * channels) / 1000); m_fadeStep = -(m_intensity / stepsCount); - m_currentIntensity = m_intensity; qDebug() << Q_FUNC_INFO << "stepsCount:" << stepsCount << ", fadeStep:" << m_fadeStep; } @@ -84,10 +100,16 @@ void AudioRenderer::stop() m_currentIntensity = 1.0; } +/********************************************************************* + * Thread functions + *********************************************************************/ + void AudioRenderer::run() { + qint64 audioDataWritten; m_userStop = false; audioDataRead = 0; + int sampleSize = m_adec->audioParameters().sampleSize(); if (sampleSize > 2) sampleSize = 2; @@ -95,7 +117,7 @@ void AudioRenderer::run() while (!m_userStop) { QMutexLocker locker(&m_mutex); - qint64 audioDataWritten = 0; + if (m_pause == false) { //qDebug() << "Pending audio bytes: " << pendingAudioBytes; @@ -117,6 +139,8 @@ void AudioRenderer::run() } if (m_intensity != 1.0 || m_fadeStep != 0) { + //qDebug() << "Intensity" << m_intensity << ", current" << m_currentIntensity << ", fadeStep" << m_fadeStep; + for (int i = 0; i < audioDataRead; i+=sampleSize) { qreal scaleFactor = m_intensity; @@ -175,7 +199,7 @@ void AudioRenderer::run() if (audioDataWritten == 0) usleep(15000); } - //qDebug() << "[Cycle] read: " << audioDataRead << ", written: " << audioDataWritten; + //qDebug() << "[Cycle] read:" << audioDataRead << ", written:" << audioDataWritten << ", pending:" << pendingAudioBytes; } else { @@ -186,7 +210,3 @@ void AudioRenderer::run() reset(); } -void AudioRenderer::setLooped(bool looped) -{ - m_looped = looped; -} diff --git a/engine/audio/src/audiorenderer.h b/engine/audio/src/audiorenderer.h index 6c49e4a1e9..719965f33d 100644 --- a/engine/audio/src/audiorenderer.h +++ b/engine/audio/src/audiorenderer.h @@ -93,8 +93,12 @@ class AudioRenderer : public QThread void adjustIntensity(qreal fraction); + /* Get/Set the looping flag */ + bool isLooped(); void setLooped(bool looped); +private: + bool m_looped; /********************************************************************* * Fade sequences @@ -145,7 +149,6 @@ class AudioRenderer : public QThread unsigned char audioData[8 * 1024]; qint64 audioDataRead; qint64 pendingAudioBytes; - bool m_looped; }; /** @} */ diff --git a/engine/audio/src/audiorenderer_alsa.cpp b/engine/audio/src/audiorenderer_alsa.cpp index 043fdccaf1..6155a5f545 100644 --- a/engine/audio/src/audiorenderer_alsa.cpp +++ b/engine/audio/src/audiorenderer_alsa.cpp @@ -220,7 +220,7 @@ QList AudioRendererAlsa::getDevicesInfo() QList devList; int cardIdx = -1; - while( snd_card_next( &cardIdx ) == 0 && cardIdx >= 0 ) + while (snd_card_next(&cardIdx) == 0 && cardIdx >= 0) { snd_ctl_t *cardHandle; snd_ctl_card_info_t *cardInfo; @@ -249,31 +249,31 @@ QList AudioRendererAlsa::getDevicesInfo() qDebug() << "[getDevicesInfo] Card" << cardIdx << "=" << snd_ctl_card_info_get_name(cardInfo); - while( snd_ctl_pcm_next_device( cardHandle, &devIdx ) == 0 && devIdx >= 0 ) + while (snd_ctl_pcm_next_device(cardHandle, &devIdx) == 0 && devIdx >= 0) { snd_pcm_info_t *pcmInfo; int tmpCaps = 0; - snd_pcm_info_alloca( &pcmInfo ); + snd_pcm_info_alloca(&pcmInfo); - snprintf( str, sizeof (str), "plughw:%d,%d", cardIdx, devIdx ); + snprintf(str, sizeof (str), "plughw:%d,%d", cardIdx, devIdx); /* Obtain info about this particular device */ - snd_pcm_info_set_device( pcmInfo, devIdx ); - snd_pcm_info_set_subdevice( pcmInfo, 0 ); - snd_pcm_info_set_stream( pcmInfo, SND_PCM_STREAM_CAPTURE ); - if( snd_ctl_pcm_info( cardHandle, pcmInfo ) >= 0 ) + snd_pcm_info_set_device(pcmInfo, devIdx); + snd_pcm_info_set_subdevice(pcmInfo, 0); + snd_pcm_info_set_stream(pcmInfo, SND_PCM_STREAM_CAPTURE); + if (snd_ctl_pcm_info(cardHandle, pcmInfo) >= 0) tmpCaps |= AUDIO_CAP_INPUT; - snd_pcm_info_set_stream( pcmInfo, SND_PCM_STREAM_PLAYBACK ); - if( snd_ctl_pcm_info( cardHandle, pcmInfo ) >= 0 ) + snd_pcm_info_set_stream(pcmInfo, SND_PCM_STREAM_PLAYBACK); + if (snd_ctl_pcm_info(cardHandle, pcmInfo) >= 0) tmpCaps |= AUDIO_CAP_OUTPUT; if (tmpCaps != 0) { AudioDeviceInfo info; info.deviceName = QString(snd_ctl_card_info_get_name(cardInfo)) + " - " + - QString (snd_pcm_info_get_name( pcmInfo )); + QString(snd_pcm_info_get_name(pcmInfo)); info.privateName = QString(str); info.capabilities = tmpCaps; devList.append(info); @@ -297,7 +297,7 @@ qint64 AudioRendererAlsa::writeAudio(unsigned char *data, qint64 maxSize) if (pcm_handle == NULL || m_prebuf == NULL) return 0; - if((maxSize = qMin(maxSize, m_prebuf_size - m_prebuf_fill)) > 0) + if ((maxSize = qMin(maxSize, m_prebuf_size - m_prebuf_fill)) > 0) { memmove(m_prebuf + m_prebuf_fill, data, maxSize); m_prebuf_fill += maxSize; @@ -325,7 +325,7 @@ qint64 AudioRendererAlsa::writeAudio(unsigned char *data, qint64 maxSize) long AudioRendererAlsa::alsa_write(unsigned char *data, long size) { long m = snd_pcm_avail_update(pcm_handle); - if(m >= 0 && m < size) + if (m >= 0 && m < size) { snd_pcm_wait(pcm_handle, 500); return 0; diff --git a/engine/audio/src/audiorenderer_coreaudio.cpp b/engine/audio/src/audiorenderer_coreaudio.cpp index 7797bd7fa8..9570cd9d2f 100644 --- a/engine/audio/src/audiorenderer_coreaudio.cpp +++ b/engine/audio/src/audiorenderer_coreaudio.cpp @@ -106,7 +106,7 @@ qint64 AudioRendererCoreAudio::latency() qint64 AudioRendererCoreAudio::writeAudio(unsigned char *data, qint64 maxSize) { - if(m_buffersFilled == AUDIO_BUFFERS_NUM) + if (m_buffersFilled == AUDIO_BUFFERS_NUM) return 0; qint64 size = maxSize; diff --git a/engine/audio/src/audiorenderer_portaudio.cpp b/engine/audio/src/audiorenderer_portaudio.cpp index 25ea4484f6..9456966945 100644 --- a/engine/audio/src/audiorenderer_portaudio.cpp +++ b/engine/audio/src/audiorenderer_portaudio.cpp @@ -41,15 +41,15 @@ AudioRendererPortAudio::~AudioRendererPortAudio() PaError err; err = Pa_Terminate(); - if( err != paNoError ) - qDebug() << "PortAudio error: " << Pa_GetErrorText( err ); + if (err != paNoError) + qDebug() << "PortAudio error: " << Pa_GetErrorText(err); } -int AudioRendererPortAudio::dataCallback ( const void *, void *outputBuffer, - unsigned long frameCount, - const PaStreamCallbackTimeInfo*, - PaStreamCallbackFlags , - void *userData ) +int AudioRendererPortAudio::dataCallback(const void *, void *outputBuffer, + unsigned long frameCount, + const PaStreamCallbackTimeInfo*, + PaStreamCallbackFlags , + void *userData) { AudioRendererPortAudio *PAobj = (AudioRendererPortAudio *)userData; @@ -81,7 +81,7 @@ bool AudioRendererPortAudio::initialize(quint32 freq, int chan, AudioFormat form PaStreamFlags flags = paNoFlag; err = Pa_Initialize(); - if( err != paNoError ) + if (err != paNoError) return false; if (m_device.isEmpty()) @@ -105,7 +105,7 @@ bool AudioRendererPortAudio::initialize(quint32 freq, int chan, AudioFormat form m_channels = chan; outputParameters.channelCount = chan; /* stereo output */ - outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency; + outputParameters.suggestedLatency = Pa_GetDeviceInfo(outputParameters.device)->defaultLowOutputLatency; outputParameters.hostApiSpecificStreamInfo = NULL; switch (format) @@ -131,15 +131,15 @@ bool AudioRendererPortAudio::initialize(quint32 freq, int chan, AudioFormat form return false; } - err = Pa_OpenStream( &m_paStream, NULL, &outputParameters, - freq, paFramesPerBufferUnspecified, flags, dataCallback, this ); + err = Pa_OpenStream(&m_paStream, NULL, &outputParameters, + freq, paFramesPerBufferUnspecified, flags, dataCallback, this); - if( err != paNoError ) + if (err != paNoError) return false; - err = Pa_StartStream( m_paStream ); + err = Pa_StartStream(m_paStream); - if( err != paNoError ) + if (err != paNoError) return false; return true; @@ -157,19 +157,19 @@ QList AudioRendererPortAudio::getDevicesInfo() int numDevices, err, i; err = Pa_Initialize(); - if( err != paNoError ) + if (err != paNoError) return devList; numDevices = Pa_GetDeviceCount(); - if( numDevices < 0 ) + if (numDevices < 0) { - qWarning("ERROR: Pa_CountDevices returned 0x%x\n", numDevices ); + qWarning("ERROR: Pa_CountDevices returned 0x%x\n", numDevices); return devList; } for (i = 0; i < numDevices; i++) { - const PaDeviceInfo *deviceInfo = Pa_GetDeviceInfo( i ); + const PaDeviceInfo *deviceInfo = Pa_GetDeviceInfo(i); if (deviceInfo != NULL) { AudioDeviceInfo info; @@ -185,8 +185,8 @@ QList AudioRendererPortAudio::getDevicesInfo() } err = Pa_Terminate(); - if( err != paNoError ) - qDebug() << "PortAudio error: " << Pa_GetErrorText( err ); + if (err != paNoError) + qDebug() << "PortAudio error: " << Pa_GetErrorText(err); return devList; } @@ -211,16 +211,16 @@ void AudioRendererPortAudio::drain() void AudioRendererPortAudio::reset() { QMutexLocker locker(&m_paMutex); - if ( m_paStream == NULL) + if (m_paStream == NULL) return; PaError err; - err = Pa_StopStream( m_paStream ); - if( err != paNoError ) + err = Pa_StopStream(m_paStream); + if (err != paNoError) qDebug() << "PortAudio Error: Stop stream failed!"; - err = Pa_CloseStream( m_paStream ); - if( err != paNoError ) + err = Pa_CloseStream(m_paStream); + if (err != paNoError) qDebug() << "PortAudio Error: Close stream failed!"; m_buffer.clear(); m_paStream = NULL; @@ -232,5 +232,4 @@ void AudioRendererPortAudio::suspend() void AudioRendererPortAudio::resume() { - } diff --git a/engine/audio/src/audiorenderer_portaudio.h b/engine/audio/src/audiorenderer_portaudio.h index 40a6bb2136..b8d1c99059 100644 --- a/engine/audio/src/audiorenderer_portaudio.h +++ b/engine/audio/src/audiorenderer_portaudio.h @@ -63,11 +63,11 @@ class AudioRendererPortAudio : public AudioRenderer void resume(); private: - static int dataCallback ( const void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo* timeInfo, - PaStreamCallbackFlags statusFlags, - void *userData ); + static int dataCallback (const void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + const PaStreamCallbackTimeInfo* timeInfo, + PaStreamCallbackFlags statusFlags, + void *userData ); PaStream *m_paStream; QMutex m_paMutex; diff --git a/engine/audio/src/audiorenderer_qt5.cpp b/engine/audio/src/audiorenderer_qt5.cpp index cfa3928b16..165ce3efec 100644 --- a/engine/audio/src/audiorenderer_qt5.cpp +++ b/engine/audio/src/audiorenderer_qt5.cpp @@ -22,11 +22,10 @@ #include #include "doc.h" -#include "audiodecoder.h" #include "audiorenderer_qt5.h" #include "audioplugincache.h" -AudioRendererQt5::AudioRendererQt5(QString device, QObject * parent) +AudioRendererQt5::AudioRendererQt5(QString device, Doc *doc, QObject *parent) : AudioRenderer(parent) , m_audioOutput(NULL) , m_output(NULL) @@ -34,7 +33,6 @@ AudioRendererQt5::AudioRendererQt5(QString device, QObject * parent) { QSettings settings; QString devName = ""; - Doc *doc = qobject_cast(parent); QVariant var; if (m_device.isEmpty()) @@ -94,7 +92,6 @@ bool AudioRendererQt5::initialize(quint32 freq, int chan, AudioFormat format) { m_format = m_deviceInfo.nearestFormat(m_format); qWarning() << "Default format not supported - trying to use nearest" << m_format.sampleRate(); - } return true; @@ -108,7 +105,6 @@ qint64 AudioRendererQt5::latency() QList AudioRendererQt5::getDevicesInfo() { QList devList; - int i = 0; QStringList outDevs, inDevs; // create a preliminary list of input devices only @@ -121,7 +117,7 @@ QList AudioRendererQt5::getDevicesInfo() outDevs.append(deviceInfo.deviceName()); AudioDeviceInfo info; info.deviceName = deviceInfo.deviceName(); - info.privateName = deviceInfo.deviceName(); //QString::number(i); + info.privateName = deviceInfo.deviceName(); info.capabilities = 0; info.capabilities |= AUDIO_CAP_OUTPUT; if (inDevs.contains(deviceInfo.deviceName())) @@ -130,19 +126,17 @@ QList AudioRendererQt5::getDevicesInfo() inDevs.removeOne(deviceInfo.deviceName()); } devList.append(info); - i++; } // add the devices left in the input list. These don't have output capabilities - foreach(QString dev, inDevs) + foreach (QString dev, inDevs) { AudioDeviceInfo info; info.deviceName = dev; - info.privateName = dev; //QString::number(i); + info.privateName = dev; info.capabilities = 0; info.capabilities |= AUDIO_CAP_INPUT; devList.append(info); - i++; } return devList; @@ -188,7 +182,7 @@ void AudioRendererQt5::run() { m_audioOutput = new QAudioOutput(m_deviceInfo, m_format); - if(m_audioOutput == NULL) + if (m_audioOutput == NULL) { qWarning() << "Cannot open audio output stream from device" << m_deviceInfo.deviceName(); return; @@ -197,7 +191,7 @@ void AudioRendererQt5::run() m_audioOutput->setBufferSize(8192 * 8); m_output = m_audioOutput->start(); - if(m_audioOutput->error() != QAudio::NoError) + if (m_audioOutput->error() != QAudio::NoError) { qWarning() << "Cannot start audio output stream. Error:" << m_audioOutput->error(); return; diff --git a/engine/audio/src/audiorenderer_qt5.h b/engine/audio/src/audiorenderer_qt5.h index 335829b30f..df6377f408 100644 --- a/engine/audio/src/audiorenderer_qt5.h +++ b/engine/audio/src/audiorenderer_qt5.h @@ -21,11 +21,12 @@ #define AUDIORENDERER_QT5_H #include "audiorenderer.h" -#include "audiodecoder.h" #include #include +class Doc; + /** @addtogroup engine_audio Audio * @{ */ @@ -34,7 +35,7 @@ class AudioRendererQt5 : public AudioRenderer { Q_OBJECT public: - AudioRendererQt5(QString device, QObject * parent = 0); + AudioRendererQt5(QString device, Doc *doc, QObject *parent = 0); ~AudioRendererQt5(); /** @reimpl */ diff --git a/engine/audio/src/audiorenderer_qt6.cpp b/engine/audio/src/audiorenderer_qt6.cpp index 7fd49b44b2..dd12676de3 100644 --- a/engine/audio/src/audiorenderer_qt6.cpp +++ b/engine/audio/src/audiorenderer_qt6.cpp @@ -23,11 +23,10 @@ #include #include "doc.h" -#include "audiodecoder.h" #include "audiorenderer_qt6.h" #include "audioplugincache.h" -AudioRendererQt6::AudioRendererQt6(QString device, QObject * parent) +AudioRendererQt6::AudioRendererQt6(QString device, Doc *doc, QObject *parent) : AudioRenderer(parent) , m_audioSink(NULL) , m_output(NULL) @@ -35,7 +34,6 @@ AudioRendererQt6::AudioRendererQt6(QString device, QObject * parent) { QSettings settings; QString devName = ""; - Doc *doc = qobject_cast(parent); QVariant var; if (m_device.isEmpty()) @@ -99,7 +97,6 @@ qint64 AudioRendererQt6::latency() QList AudioRendererQt6::getDevicesInfo() { QList devList; - int i = 0; QStringList outDevs, inDevs; // create a preliminary list of input devices only @@ -121,11 +118,10 @@ QList AudioRendererQt6::getDevicesInfo() inDevs.removeOne(deviceInfo.description()); } devList.append(info); - i++; } // add the devices left in the input list. These don't have output capabilities - foreach(QString dev, inDevs) + foreach (QString dev, inDevs) { AudioDeviceInfo info; info.deviceName = dev; @@ -133,7 +129,6 @@ QList AudioRendererQt6::getDevicesInfo() info.capabilities = 0; info.capabilities |= AUDIO_CAP_INPUT; devList.append(info); - i++; } return devList; @@ -141,16 +136,25 @@ QList AudioRendererQt6::getDevicesInfo() qint64 AudioRendererQt6::writeAudio(unsigned char *data, qint64 maxSize) { - if (m_audioSink == NULL || m_audioSink->bytesFree() < maxSize) + qsizetype bFree = m_audioSink->bytesFree(); + + if (m_audioSink == NULL || bFree < maxSize) return 0; - //qDebug() << "writeAudio called !! - " << maxSize; - qint64 written = m_output->write((const char *)data, maxSize); + //qDebug() << "writeAudio called !! - " << maxSize << m_outputBuffer.length() << bFree; + + m_outputBuffer.append((char *)data, maxSize); + + if (m_outputBuffer.length() >= bFree) + { + qint64 written = m_output->write(m_outputBuffer.data(), bFree); - if (written != maxSize) - qDebug() << "[writeAudio] expexcted to write" << maxSize << "but wrote" << written; + if (written != bFree) + qDebug() << "[writeAudio] expexcted to write" << bFree << "but wrote" << written; - return written; + m_outputBuffer.remove(0, written); + } + return maxSize; } void AudioRendererQt6::drain() @@ -177,6 +181,8 @@ void AudioRendererQt6::run() { if (m_audioSink == NULL) { + qDebug() << "Creating audio sink on" << m_deviceInfo.description(); + m_audioSink = new QAudioSink(m_deviceInfo, m_format); if (m_audioSink == NULL) @@ -188,7 +194,7 @@ void AudioRendererQt6::run() m_audioSink->setBufferSize(8192 * 8); m_output = m_audioSink->start(); - if(m_audioSink->error() != QAudio::NoError) + if (m_audioSink->error() != QAudio::NoError) { qWarning() << "Cannot start audio output stream. Error:" << m_audioSink->error(); return; diff --git a/engine/audio/src/audiorenderer_qt6.h b/engine/audio/src/audiorenderer_qt6.h index 0a24992efb..5573844a8f 100644 --- a/engine/audio/src/audiorenderer_qt6.h +++ b/engine/audio/src/audiorenderer_qt6.h @@ -21,13 +21,14 @@ #define AUDIORENDERER_QT6_H #include "audiorenderer.h" -#include "audiodecoder.h" #include #include #include #include +class Doc; + /** @addtogroup engine_audio Audio * @{ */ @@ -36,7 +37,7 @@ class AudioRendererQt6 : public AudioRenderer { Q_OBJECT public: - AudioRendererQt6(QString device, QObject * parent = 0); + AudioRendererQt6(QString device, Doc *doc, QObject * parent = 0); ~AudioRendererQt6(); /** @reimpl */ @@ -76,6 +77,7 @@ class AudioRendererQt6 : public AudioRenderer QAudioFormat m_format; QString m_device; QAudioDevice m_deviceInfo; + QByteArray m_outputBuffer; }; /** @} */ diff --git a/engine/src/CMakeLists.txt b/engine/src/CMakeLists.txt new file mode 100644 index 0000000000..54597e5a91 --- /dev/null +++ b/engine/src/CMakeLists.txt @@ -0,0 +1,200 @@ +set(module_name "qlcplusengine") + +add_library(${module_name} SHARED + ../../plugins/interfaces/qlcioplugin.cpp ../../plugins/interfaces/qlcioplugin.h + avolitesd4parser.cpp avolitesd4parser.h + bus.cpp bus.h + channelmodifier.cpp channelmodifier.h + channelsgroup.cpp channelsgroup.h + chaser.cpp chaser.h + chaseraction.h + chaserrunner.cpp chaserrunner.h + chaserstep.cpp chaserstep.h + collection.cpp collection.h + cue.cpp cue.h + cuestack.cpp cuestack.h + dmxdumpfactoryproperties.cpp dmxdumpfactoryproperties.h + dmxsource.h + doc.cpp doc.h + efx.cpp efx.h + efxfixture.cpp efxfixture.h + fadechannel.cpp fadechannel.h + fixture.cpp fixture.h + fixturegroup.cpp fixturegroup.h + function.cpp function.h + genericdmxsource.cpp genericdmxsource.h + genericfader.cpp genericfader.h + gradient.cpp gradient.h + grandmaster.cpp grandmaster.h + grouphead.cpp grouphead.h + inputoutputmap.cpp inputoutputmap.h + inputpatch.cpp inputpatch.h + ioplugincache.cpp ioplugincache.h + keypadparser.cpp keypadparser.h + mastertimer.cpp mastertimer.h + monitorproperties.cpp monitorproperties.h + outputpatch.cpp outputpatch.h + qlccapability.cpp qlccapability.h + qlcchannel.cpp qlcchannel.h + qlcclipboard.cpp qlcclipboard.h + qlcfile.cpp qlcfile.h + qlcfixturedef.cpp qlcfixturedef.h + qlcfixturedefcache.cpp qlcfixturedefcache.h + qlcfixturehead.cpp qlcfixturehead.h + 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 + qlcpalette.cpp qlcpalette.h + qlcphysical.cpp qlcphysical.h + qlcpoint.cpp qlcpoint.h + rgbalgorithm.cpp rgbalgorithm.h + rgbaudio.cpp rgbaudio.h + rgbimage.cpp rgbimage.h + rgbmatrix.cpp rgbmatrix.h + rgbplain.cpp rgbplain.h + rgbscriptproperty.h + rgbscriptscache.cpp rgbscriptscache.h + rgbtext.cpp rgbtext.h + scene.cpp scene.h + scenevalue.cpp scenevalue.h + scriptwrapper.h + sequence.cpp sequence.h + show.cpp show.h + showfunction.cpp showfunction.h + showrunner.cpp showrunner.h + track.cpp track.h + universe.cpp universe.h + utils.h + video.cpp video.h +) +target_include_directories(${module_name} PUBLIC + ../../plugins/interfaces + ../audio/src +) + +target_link_libraries(${module_name} PUBLIC + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Multimedia +) + +target_link_libraries(${module_name} PRIVATE + qlcplusaudio +) + +if(NOT ANDROID AND NOT IOS) + target_include_directories(${module_name} PUBLIC + ../../hotplugmonitor/src + ) + + target_link_libraries(${module_name} PUBLIC + hotplugmonitor + ) + + target_link_directories(${module_name} PUBLIC + ${CMAKE_CURRENT_BINARY_DIR}/../../hotplugmonitor/src + ) +endif() + +if(UNIX AND NOT ANDROID AND NOT IOS AND NOT APPLE) + target_link_libraries(${module_name} PUBLIC + asound + ) +endif() + +if(WIN32) + target_sources(${module_name} PRIVATE + mastertimer-win32.cpp mastertimer-win32.h + ) + + target_include_directories(${module_name} PUBLIC + / + ) + + target_link_libraries(${module_name} PUBLIC + Qt${QT_MAJOR_VERSION}::Widgets + winmm + ) +endif() + +if((NOT ANDROID AND NOT IOS) AND (iokit)) + target_link_libraries(${module_name} PUBLIC + "-framework CoreFoundation" + "-framework IOKit" + ) +endif() + +if(APPLE) + set_target_properties(${module_name} PROPERTIES + MACOSX_BUNDLE FALSE + ) + target_link_libraries(${module_name} PUBLIC + "-framework AudioToolbox" + "-framework CoreAudio" + "-framework CoreFoundation" + ) +endif() + +if(qmlui OR (QT_VERSION_MAJOR GREATER 5)) + target_sources(${module_name} PRIVATE + rgbscriptv4.cpp rgbscriptv4.h + scriptrunner.cpp scriptrunner.h + scriptv4.cpp scriptv4.h + ) + + target_link_libraries(${module_name} PUBLIC + Qt${QT_MAJOR_VERSION}::Qml + ) +endif() + +if(NOT (qmlui OR (QT_VERSION_MAJOR GREATER 5))) + target_sources(${module_name} PRIVATE + rgbscript.cpp rgbscript.h + script.cpp script.h + ) + + target_link_libraries(${module_name} PUBLIC + Qt${QT_MAJOR_VERSION}::Script + ) +endif() + +if(UNIX) + target_sources(${module_name} PRIVATE + mastertimer-unix.cpp mastertimer-unix.h + ) +endif() + +if((NOT ANDROID AND NOT IOS) AND (${FFTW3_FOUND})) + target_compile_definitions(${module_name} PUBLIC + HAS_FFTW3 + ) +endif() + +if(((NOT ANDROID AND NOT IOS) AND (${FFTW3_FOUND}))) + target_link_directories(${module_name} PUBLIC + ${FFTW3_LIBRARY_DIRS} + ) + target_link_libraries(${module_name} PUBLIC + ${FFTW3_LIBRARIES} + ) +endif() + +set(CONFIGFILE ${CMAKE_SOURCE_DIR}/engine/src/qlcconfig.h) +set(LITERAL_HASH "#") + +if(WIN32 OR APPLE OR appimage) + configure_file(qlcconfig.h.noroot.in ${CONFIGFILE}) +elseif(UNIX OR ANDROID OR IOS) + configure_file(qlcconfig.h.in ${CONFIGFILE}) +endif() + +set_property(DIRECTORY APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES ${CONFIGFILE}) + +install(TARGETS ${module_name} + LIBRARY DESTINATION ${INSTALLROOT}/${LIBSDIR} + RUNTIME DESTINATION ${INSTALLROOT}/${LIBSDIR} +) diff --git a/engine/src/avolitesd4parser.cpp b/engine/src/avolitesd4parser.cpp index b465c6a0b0..98f2baf224 100644 --- a/engine/src/avolitesd4parser.cpp +++ b/engine/src/avolitesd4parser.cpp @@ -816,6 +816,8 @@ QLCFixtureDef::FixtureType AvolitesD4Parser::guessType(QLCFixtureDef *def) const return QLCFixtureDef::Strobe; // Duh. else if (smoke > 0) return QLCFixtureDef::Smoke; // Duh. + else if (haze > 0) + return QLCFixtureDef::Hazer; // Duh. else if (nocol > 0) return QLCFixtureDef::Dimmer; // Kinda..mmmmh.. else diff --git a/engine/src/channelmodifier.cpp b/engine/src/channelmodifier.cpp index 2524a8f156..6d23657b6b 100644 --- a/engine/src/channelmodifier.cpp +++ b/engine/src/channelmodifier.cpp @@ -16,14 +16,13 @@ See the License for the specific language governing permissions and limitations under the License. */ - -#include "channelmodifier.h" -#include "qlcfile.h" - #include #include #include +#include "channelmodifier.h" +#include "qlcfile.h" + ChannelModifier::ChannelModifier() { m_values.fill(0, 256); @@ -120,7 +119,7 @@ QFile::FileError ChannelModifier::saveXML(const QString &fileName) doc.writeTextElement(KXMLQLCChannelModName, m_name); qDebug() << "Got map with" << m_map.count() << "handlers"; - for(int i = 0; i < m_map.count(); i++) + for (int i = 0; i < m_map.count(); i++) { QPair mapElement = m_map.at(i); doc.writeStartElement(KXMLQLCChannelModHandler); @@ -177,7 +176,7 @@ QFile::FileError ChannelModifier::loadXML(const QString &fileName, Type type) { setName(doc->readElementText()); } - else if(doc->name() == KXMLQLCChannelModHandler) + else if (doc->name() == KXMLQLCChannelModHandler) { QPair dmxPair(0, 0); QXmlStreamAttributes attrs = doc->attributes(); diff --git a/engine/src/channelsgroup.cpp b/engine/src/channelsgroup.cpp index 7b165e25e7..62283a9005 100644 --- a/engine/src/channelsgroup.cpp +++ b/engine/src/channelsgroup.cpp @@ -263,7 +263,7 @@ bool ChannelsGroup::saveXML(QXmlStreamWriter *doc) Q_ASSERT(doc != NULL); QString str; - foreach(SceneValue value, this->getChannels()) + foreach (SceneValue value, this->getChannels()) { if (str.isEmpty() == false) str.append(","); diff --git a/engine/src/chaser.cpp b/engine/src/chaser.cpp index 1328248671..a00b3b3f76 100644 --- a/engine/src/chaser.cpp +++ b/engine/src/chaser.cpp @@ -25,16 +25,11 @@ #include #include -#include "qlcfixturedef.h" -#include "qlcfile.h" - #include "chaserrunner.h" #include "mastertimer.h" #include "chaserstep.h" #include "function.h" -#include "fixture.h" #include "chaser.h" -#include "scene.h" #include "doc.h" #include "bus.h" @@ -137,6 +132,7 @@ bool Chaser::addStep(const ChaserStep& step, int index) } emit changed(this->id()); + emit stepsListChanged(this->id()); return true; } else @@ -155,6 +151,7 @@ bool Chaser::removeStep(int index) } emit changed(this->id()); + emit stepsListChanged(this->id()); return true; } else @@ -173,6 +170,7 @@ bool Chaser::replaceStep(const ChaserStep& step, int index) } emit changed(this->id()); + emit stepChanged(index); return true; } else @@ -236,7 +234,7 @@ void Chaser::setTotalDuration(quint32 msec) { uint origDuration = m_steps[i].duration; m_steps[i].duration = ((double)m_steps[i].duration * msec) / dtDuration; - if(m_steps[i].hold) + if (m_steps[i].hold) m_steps[i].hold = ((double)m_steps[i].hold * (double)m_steps[i].duration) / (double)origDuration; m_steps[i].fadeIn = m_steps[i].duration - m_steps[i].hold; if (m_steps[i].fadeOut) @@ -580,7 +578,7 @@ bool Chaser::contains(quint32 functionId) Doc *doc = this->doc(); Q_ASSERT(doc != NULL); - foreach(ChaserStep step, m_steps) + foreach (ChaserStep step, m_steps) { Function *function = doc->function(step.fid); // contains() can be called during init, function may be NULL @@ -600,7 +598,7 @@ QList Chaser::components() { QList ids; - foreach(ChaserStep step, m_steps) + foreach (ChaserStep step, m_steps) ids.append(step.fid); return ids; diff --git a/engine/src/chaser.h b/engine/src/chaser.h index 01f1cf823d..15bf706173 100644 --- a/engine/src/chaser.h +++ b/engine/src/chaser.h @@ -155,6 +155,10 @@ public slots: */ void slotFunctionRemoved(quint32 fid); +signals: + void stepChanged(int index); + void stepsListChanged(quint32 fid); + protected: QList m_steps; QMutex m_stepListMutex; diff --git a/engine/src/chaserrunner.cpp b/engine/src/chaserrunner.cpp index 487c7ee4f8..ee2a495668 100644 --- a/engine/src/chaserrunner.cpp +++ b/engine/src/chaserrunner.cpp @@ -26,12 +26,9 @@ #include #include "chaserrunner.h" -#include "genericfader.h" #include "mastertimer.h" -#include "fadechannel.h" #include "chaserstep.h" #include "qlcmacros.h" -#include "fixture.h" #include "chaser.h" #include "scene.h" #include "doc.h" @@ -55,12 +52,12 @@ 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; quint32 stepsTime = 0; - foreach(ChaserStep step, chaser->steps()) + foreach (ChaserStep step, chaser->steps()) { uint duration = m_chaser->durationMode() == Chaser::Common ? m_chaser->duration() : step.duration; @@ -99,7 +96,7 @@ void ChaserRunner::slotChaserChanged() // Handle (possible) speed change on the next write() pass m_updateOverrideSpeeds = true; QList delList; - foreach(ChaserRunnerStep *step, m_runnerSteps) + foreach (ChaserRunnerStep *step, m_runnerSteps) { if (!m_chaser->steps().contains(ChaserStep(step->m_function->id()))) { @@ -114,11 +111,11 @@ void ChaserRunner::slotChaserChanged() step->m_duration = stepDuration(step->m_index); } } - foreach(ChaserRunnerStep *step, delList) + foreach (ChaserRunnerStep *step, delList) { step->m_function->stop(functionParent()); - delete step; m_runnerSteps.removeAll(step); + delete step; } } @@ -245,7 +242,7 @@ void ChaserRunner::setAction(ChaserAction &action) { bool stopped = false; - foreach(ChaserRunnerStep *step, m_runnerSteps) + foreach (ChaserRunnerStep *step, m_runnerSteps) { if (action.m_stepIndex == step->m_index) { @@ -431,7 +428,7 @@ void ChaserRunner::adjustStepIntensity(qreal fraction, int requestedStepIndex, i m_pendingAction.m_masterIntensity = fraction; } - foreach(ChaserRunnerStep *step, m_runnerSteps) + foreach (ChaserRunnerStep *step, m_runnerSteps) { if (stepIndex == step->m_index && step->m_function != NULL) { @@ -467,7 +464,7 @@ void ChaserRunner::adjustStepIntensity(qreal fraction, int requestedStepIndex, i void ChaserRunner::clearRunningList() { // empty the running queue - foreach(ChaserRunnerStep *step, m_runnerSteps) + foreach (ChaserRunnerStep *step, m_runnerSteps) { if (step->m_function) { @@ -580,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); @@ -605,6 +602,16 @@ int ChaserRunner::getNextStepIndex() m_chaser->direction() == Function::Backward) currentStepIndex = m_chaser->stepsCount(); + // Handle reverse Ping Pong at boundaries + if (m_chaser->runOrder() == Function::PingPong && + m_pendingAction.m_action == ChaserPreviousStep) + { + if (currentStepIndex == 0) + m_direction = Function::Backward; + else if (currentStepIndex == m_chaser->stepsCount() - 1) + m_direction = Function::Forward; + } + // Next step if (m_direction == Function::Forward) { @@ -705,7 +712,7 @@ void ChaserRunner::setPause(bool enable, QList universes) qDebug() << "[ChaserRunner] processing pause request:" << enable; - foreach(ChaserRunnerStep *step, m_runnerSteps) + foreach (ChaserRunnerStep *step, m_runnerSteps) step->m_function->setPause(enable); // there might be a Scene fading out, so request pause @@ -740,7 +747,11 @@ bool ChaserRunner::write(MasterTimer *timer, QList universes) if (m_pendingAction.m_stepIndex != -1) { clearRunningList(); - m_lastRunStepIdx = m_pendingAction.m_stepIndex; + if (m_chaser->runOrder() == Function::Random) + m_lastRunStepIdx = randomStepIndex(m_pendingAction.m_stepIndex); + else + m_lastRunStepIdx = m_pendingAction.m_stepIndex; + qDebug() << "[ChaserRunner] Starting from step" << m_lastRunStepIdx << "@ offset" << m_startOffset; startNewStep(m_lastRunStepIdx, timer, m_pendingAction.m_masterIntensity, m_pendingAction.m_stepIntensity, m_pendingAction.m_fadeMode); @@ -756,7 +767,7 @@ bool ChaserRunner::write(MasterTimer *timer, QList universes) quint32 prevStepRoundElapsed = 0; - foreach(ChaserRunnerStep *step, m_runnerSteps) + foreach (ChaserRunnerStep *step, m_runnerSteps) { if (m_chaser->tempoType() == Function::Beats && timer->isBeat()) { @@ -773,8 +784,8 @@ bool ChaserRunner::write(MasterTimer *timer, QList universes) m_lastFunctionID = step->m_function->type() == Function::SceneType ? step->m_function->id() : Function::invalidId(); step->m_function->stop(functionParent(), m_chaser->type() == Function::SequenceType); - delete step; m_runnerSteps.removeOne(step); + delete step; } else { diff --git a/engine/src/chaserstep.cpp b/engine/src/chaserstep.cpp index 750b9fb4af..78cbae3cd1 100644 --- a/engine/src/chaserstep.cpp +++ b/engine/src/chaserstep.cpp @@ -284,7 +284,7 @@ bool ChaserStep::saveXML(QXmlStreamWriter *doc, int stepNumber, bool isSequence) doc->writeAttribute(KXMLQLCSequenceSceneValues, QString::number(values.count())); QString stepValues; quint32 fixtureID = Fixture::invalidId(); - foreach(SceneValue scv, values) + foreach (SceneValue scv, values) { // step values are saved as a string with the following syntax: // fixtureID:channel,value,channel,value:fixtureID:channel,value ... etc diff --git a/engine/src/collection.cpp b/engine/src/collection.cpp index 818a6e8abd..894eb3acfd 100644 --- a/engine/src/collection.cpp +++ b/engine/src/collection.cpp @@ -26,8 +26,6 @@ #include #include -#include "qlcfile.h" - #include "mastertimer.h" #include "collection.h" #include "function.h" @@ -63,7 +61,7 @@ quint32 Collection::totalDuration() { quint32 totalDuration = 0; - foreach(QVariant fid, functions()) + foreach (QVariant fid, functions()) { Function* function = doc()->function(fid.toUInt()); totalDuration += function->totalDuration(); diff --git a/engine/src/doc.cpp b/engine/src/doc.cpp index ef21c04e00..9e285771cc 100644 --- a/engine/src/doc.cpp +++ b/engine/src/doc.cpp @@ -32,7 +32,6 @@ #include "qlcfixturemode.h" #include "qlcfixturedef.h" -#include "qlcfile.h" #include "monitorproperties.h" #include "audioplugincache.h" @@ -45,9 +44,7 @@ #include "sequence.h" #include "fixture.h" #include "chaser.h" -#include "scene.h" #include "show.h" -#include "efx.h" #include "doc.h" #include "bus.h" @@ -538,7 +535,7 @@ bool Doc::replaceFixtures(QList newFixturesList) m_latestFixtureId = 0; m_addresses.clear(); - foreach(Fixture *fixture, newFixturesList) + foreach (Fixture *fixture, newFixturesList) { quint32 id = fixture->id(); // create a copy of the original cause remapping will @@ -579,6 +576,9 @@ bool Doc::replaceFixtures(QList newFixturesList) } newFixture->setExcludeFadeChannels(fixture->excludeFadeChannels()); + newFixture->setForcedHTPChannels(fixture->forcedHTPChannels()); + newFixture->setForcedLTPChannels(fixture->forcedLTPChannels()); + m_fixtures.insert(id, newFixture); m_fixturesListCacheUpToDate = false; @@ -1046,7 +1046,7 @@ QList Doc::functions() const QList Doc::functionsByType(Function::Type type) const { QList list; - foreach(Function *f, m_functions) + foreach (Function *f, m_functions) { if (f != NULL && f->type() == type) list.append(f); @@ -1054,6 +1054,16 @@ QList Doc::functionsByType(Function::Type type) const return list; } +Function *Doc::functionByName(QString name) +{ + foreach (Function *f, m_functions) + { + if (f != NULL && f->name() == name) + return f; + } + return NULL; +} + bool Doc::deleteFunction(quint32 id) { if (m_functions.contains(id) == true) @@ -1177,7 +1187,7 @@ QList Doc::getUsage(quint32 fid) Show *s = qobject_cast(f); foreach (Track *t, s->tracks()) { - foreach(ShowFunction *sf, t->showFunctions()) + foreach (ShowFunction *sf, t->showFunctions()) { if (sf->functionID() == fid) { @@ -1224,7 +1234,7 @@ MonitorProperties *Doc::monitorProperties() * Load & Save *****************************************************************************/ -bool Doc::loadXML(QXmlStreamReader &doc) +bool Doc::loadXML(QXmlStreamReader &doc, bool loadIO) { clearErrorLog(); @@ -1274,7 +1284,7 @@ bool Doc::loadXML(QXmlStreamReader &doc) /* LEGACY */ Bus::instance()->loadXML(doc); } - else if (doc.name() == KXMLIOMap) + else if (doc.name() == KXMLIOMap && loadIO) { m_ioMap->loadXML(doc); } diff --git a/engine/src/doc.h b/engine/src/doc.h index 858f06be4f..4da3ee6804 100644 --- a/engine/src/doc.h +++ b/engine/src/doc.h @@ -500,6 +500,13 @@ private slots: */ QList functionsByType(Function::Type type) const; + /** + * Get a pointer to a Function with the given name + * @param name lookup Function name + * @return pointer to Function or null if not found + */ + Function *functionByName(QString name); + /** * Delete the given function * @@ -605,9 +612,10 @@ private slots: * Load contents from the given XML document * * @param root The Engine XML root node to load from + * @param loadIO Parse the InputOutputMap tag too * @return true if successful, otherwise false */ - bool loadXML(QXmlStreamReader &doc); + bool loadXML(QXmlStreamReader &doc, bool loadIO = true); /** * Save contents to the given XML file. diff --git a/engine/src/efx.cpp b/engine/src/efx.cpp index 8046edf194..3a3d408874 100644 --- a/engine/src/efx.cpp +++ b/engine/src/efx.cpp @@ -26,16 +26,9 @@ #include -#include "qlcfixturemode.h" -#include "qlcfixturedef.h" #include "genericfader.h" -#include "qlcchannel.h" -#include "qlcmacros.h" -#include "qlcfile.h" - #include "mastertimer.h" -#include "fixture.h" -#include "scene.h" +#include "qlcmacros.h" #include "doc.h" #include "efx.h" #include "bus.h" @@ -142,12 +135,19 @@ void EFX::setDuration(uint ms) { Function::setDuration(ms); - for(int i = 0; i < m_fixtures.size(); ++i) + for (int i = 0; i < m_fixtures.size(); ++i) m_fixtures[i]->durationChanged(); emit durationChanged(ms); } +uint EFX::loopDuration() const +{ + uint fadeIn = overrideFadeInSpeed() == defaultSpeed() ? fadeInSpeed() : overrideFadeInSpeed(); + + return duration() - fadeIn; +} + /***************************************************************************** * Algorithm *****************************************************************************/ @@ -180,6 +180,7 @@ QStringList EFX::algorithmList() list << algorithmToString(EFX::Diamond); list << algorithmToString(EFX::Square); list << algorithmToString(EFX::SquareChoppy); + list << algorithmToString(EFX::SquareTrue); list << algorithmToString(EFX::Leaf); list << algorithmToString(EFX::Lissajous); return list; @@ -204,6 +205,8 @@ QString EFX::algorithmToString(EFX::Algorithm algo) return QString(KXMLQLCEFXSquareAlgorithmName); case EFX::SquareChoppy: return QString(KXMLQLCEFXSquareChoppyAlgorithmName); + case EFX::SquareTrue: + return QString(KXMLQLCEFXSquareTrueAlgorithmName); case EFX::Leaf: return QString(KXMLQLCEFXLeafAlgorithmName); case EFX::Lissajous: @@ -225,6 +228,8 @@ EFX::Algorithm EFX::stringToAlgorithm(const QString& str) return EFX::Square; else if (str == QString(KXMLQLCEFXSquareChoppyAlgorithmName)) return EFX::SquareChoppy; + else if (str == QString(KXMLQLCEFXSquareTrueAlgorithmName)) + return EFX::SquareTrue; else if (str == QString(KXMLQLCEFXLeafAlgorithmName)) return EFX::Leaf; else if (str == QString(KXMLQLCEFXLissajousAlgorithmName)) @@ -267,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)); @@ -278,15 +283,27 @@ 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; float w = getAttributeValue(Width); float h = getAttributeValue(Height); + float fadeScale = 1.0; - *x = (getAttributeValue(XOffset)) + xx * m_cosR * w + yy * m_sinR * h; - *y = (getAttributeValue(YOffset)) + -xx * m_sinR * w + yy * m_cosR * h; + if (isRunning()) + { + uint fadeIn = overrideFadeInSpeed() == defaultSpeed() ? fadeInSpeed() : overrideFadeInSpeed(); + if (fadeIn > 0 && elapsed() <= fadeIn) + { + fadeScale = SCALE(float(elapsed()), + float(0), float(fadeIn), + float(0), float(1.0)); + } + } + + *x = (getAttributeValue(XOffset)) + xx * m_cosR * (w * fadeScale) + yy * m_sinR * (h * fadeScale); + *y = (getAttributeValue(YOffset)) + -xx * m_sinR * (w * fadeScale) + yy * m_cosR * (h * fadeScale); } float EFX::calculateDirection(Function::Direction direction, float iterator) const @@ -303,6 +320,7 @@ float EFX::calculateDirection(Function::Direction direction, float iterator) con case Diamond: case Square: case SquareChoppy: + case SquareTrue: case Leaf: case Lissajous: return (M_PI * 2.0) - iterator; @@ -312,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()) { @@ -369,6 +387,29 @@ void EFX::calculatePoint(float iterator, float* x, float* y) const *x = round(cos(iterator)); *y = round(sin(iterator)); break; + + case SquareTrue: + if (iterator < M_PI / 2) + { + *x = 1; + *y = 1; + } + else if (M_PI / 2 <= iterator && iterator < M_PI) + { + *x = 1; + *y = -1; + } + else if (M_PI <= iterator && iterator < M_PI * 3 / 2) + { + *x = -1; + *y = -1; + } + else // M_PI * 3 / 2 <= iterator + { + *x = -1; + *y = 1; + } + break; case Leaf: *x = pow(cos(iterator + M_PI_2), 5); @@ -602,7 +643,7 @@ bool EFX::addFixture(EFXFixture* ef) * not prevent multiple entries because a fixture can have multiple efx. */ //! @todo Prevent multiple entries using head & mode int i; - for(i = 0; i < m_fixtures.size (); i++) + for (i = 0; i < m_fixtures.size (); i++) { if (m_fixtures[i]->head() == ef->head()) { @@ -612,7 +653,7 @@ bool EFX::addFixture(EFXFixture* ef) } /* If not inserted, put the EFXFixture object into our list */ - if(i >= m_fixtures.size()) + if (i >= m_fixtures.size()) m_fixtures.append(ef); emit changed(this->id()); @@ -620,6 +661,15 @@ bool EFX::addFixture(EFXFixture* ef) return true; } +bool EFX::addFixture(quint32 fxi, int head) +{ + EFXFixture *ef = new EFXFixture(this); + GroupHead gHead(fxi, head); + ef->setHead(gHead); + + return addFixture(ef); +} + bool EFX::removeFixture(EFXFixture* ef) { Q_ASSERT(ef != NULL); @@ -1031,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; } @@ -1044,16 +1095,11 @@ 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++); } - //Q_ASSERT(m_fader == NULL); - //m_fader = new GenericFader(doc()); - //m_fader->adjustIntensity(getAttributeValue(Intensity)); - //m_fader->setBlendMode(blendMode()); - Function::preRun(timer); } @@ -1061,32 +1107,31 @@ void EFX::write(MasterTimer *timer, QList universes) { Q_UNUSED(timer); - int ready = 0; - if (isPaused()) return; + int done = 0; + QListIterator it(m_fixtures); while (it.hasNext() == true) { EFXFixture *ef = it.next(); - if (ef->isReady() == false) + if (ef->isDone() == false) { QSharedPointer fader = getFader(universes, ef->universe()); ef->nextStep(universes, fader); } else { - ready++; + done++; } } incrementElapsed(); /* Check for stop condition */ - if (ready == m_fixtures.count()) + if (done == m_fixtures.count()) stop(FunctionParent::master()); - //m_fader->write(universes); } void EFX::postRun(MasterTimer *timer, QList universes) diff --git a/engine/src/efx.h b/engine/src/efx.h index 6d92327a0d..1c7d3e6767 100644 --- a/engine/src/efx.h +++ b/engine/src/efx.h @@ -64,6 +64,7 @@ class Fixture; #define KXMLQLCEFXDiamondAlgorithmName QString("Diamond") #define KXMLQLCEFXSquareAlgorithmName QString("Square") #define KXMLQLCEFXSquareChoppyAlgorithmName QString("SquareChoppy") +#define KXMLQLCEFXSquareTrueAlgorithmName QString("SquareTrue") #define KXMLQLCEFXLeafAlgorithmName QString("Leaf") #define KXMLQLCEFXLissajousAlgorithmName QString("Lissajous") @@ -116,6 +117,8 @@ class EFX : public Function /** Set the duration in milliseconds */ virtual void setDuration(uint ms); + uint loopDuration() const; + signals: void durationChanged(uint ms); @@ -132,6 +135,7 @@ class EFX : public Function Diamond, Square, SquareChoppy, + SquareTrue, Leaf, Lissajous }; @@ -179,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: @@ -477,6 +481,9 @@ class EFX : public Function /** Add a new fixture to this EFX */ bool addFixture(EFXFixture *ef); + /** Add the provided fixture id and head to this EFX */ + bool addFixture(quint32 fxi, int head = 0); + /** Remove the designated fixture from this EFX but don't delete it */ bool removeFixture(EFXFixture *ef); @@ -514,9 +521,9 @@ public slots: public: enum PropagationMode { - Parallel, /**< All fixtures move in unison (el-cheapo) */ - Serial, /**< Pattern propagates to the next fixture after a delay */ - Asymmetric /**< All fixtures move with an offset */ + Parallel, /**< All fixtures move in unison (el-cheapo) */ + Serial, /**< Pattern propagates to the next fixture after a delay */ + Asymmetric /**< All fixtures move with an offset */ }; /** Set the EFX's fixture propagation mode (see the enum above) */ diff --git a/engine/src/efxfixture.cpp b/engine/src/efxfixture.cpp index 9c652b11f8..45420be61e 100644 --- a/engine/src/efxfixture.cpp +++ b/engine/src/efxfixture.cpp @@ -24,16 +24,15 @@ #include #include "genericfader.h" +#include "fadechannel.h" #include "mastertimer.h" #include "efxfixture.h" #include "qlcmacros.h" #include "function.h" #include "universe.h" -#include "scene.h" +#include "gradient.h" #include "efx.h" #include "doc.h" -#include "gradient.h" - /***************************************************************************** * Initialization @@ -50,14 +49,19 @@ EFXFixture::EFXFixture(const EFX* parent) , m_serialNumber(0) , m_runTimeDirection(Function::Forward) - , m_ready(false) + , m_done(false) , 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); - if(m_rgbGradient.isNull ()) + if (m_rgbGradient.isNull ()) m_rgbGradient = Gradient::getRGBGradient (256, 256); } @@ -73,7 +77,7 @@ void EFXFixture::copyFrom(const EFXFixture* ef) m_serialNumber = ef->m_serialNumber; m_runTimeDirection = ef->m_runTimeDirection; - m_ready = ef->m_ready; + m_done = ef->m_done; m_started = ef->m_started; m_elapsed = ef->m_elapsed; m_currentAngle = ef->m_currentAngle; @@ -187,14 +191,14 @@ void EFXFixture::durationChanged() // new duration. m_elapsed = SCALE(float(m_currentAngle), float(0), float(M_PI * 2), - float(0), float(m_parent->duration())); + float(0), float(m_parent->loopDuration())); // Serial or Asymmetric propagation mode: // we must substract the offset from the current position if (timeOffset()) { if (m_elapsed < timeOffset()) - m_elapsed += m_parent->duration(); + m_elapsed += m_parent->loopDuration(); m_elapsed -= timeOffset(); } } @@ -206,15 +210,15 @@ QStringList EFXFixture::modeList() QStringList modes; - if(fxi->channelNumber(QLCChannel::Pan, QLCChannel::MSB, head().head) != QLCChannel::invalid() || + if (fxi->channelNumber(QLCChannel::Pan, QLCChannel::MSB, head().head) != QLCChannel::invalid() || fxi->channelNumber(QLCChannel::Tilt, QLCChannel::MSB, head().head) != QLCChannel::invalid()) modes << KXMLQLCEFXFixtureModePanTilt; - if(fxi->masterIntensityChannel() != QLCChannel::invalid() || + if (fxi->masterIntensityChannel() != QLCChannel::invalid() || fxi->channelNumber(QLCChannel::Intensity, QLCChannel::MSB, head().head) != QLCChannel::invalid()) modes << KXMLQLCEFXFixtureModeDimmer; - if(fxi->rgbChannels(head().head).size() >= 3) + if (fxi->rgbChannels(head().head).size() >= 3) modes << KXMLQLCEFXFixtureModeRGB; return modes; @@ -354,16 +358,16 @@ int EFXFixture::serialNumber() const void EFXFixture::reset() { - m_ready = false; + m_done = false; m_runTimeDirection = m_direction; m_started = false; m_elapsed = 0; m_currentAngle = 0; } -bool EFXFixture::isReady() const +bool EFXFixture::isDone() const { - return m_ready; + return m_done; } uint EFXFixture::timeOffset() const @@ -371,7 +375,7 @@ uint EFXFixture::timeOffset() const if (m_parent->propagationMode() == EFX::Asymmetric || m_parent->propagationMode() == EFX::Serial) { - return m_parent->duration() / (m_parent->fixtures().size() + 1) * serialNumber(); + return m_parent->loopDuration() / (m_parent->fixtures().size() + 1) * serialNumber(); } else { @@ -383,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; } @@ -395,79 +443,76 @@ void EFXFixture::stop() void EFXFixture::nextStep(QList universes, QSharedPointer fader) { - m_elapsed += MasterTimer::tick(); + // Nothing to do + if (m_parent->loopDuration() == 0) + return; // Bail out without doing anything if this fixture is ready (after single-shot) // or it has no pan&tilt channels (not valid). - if (m_ready == true || isValid() == false) + if (m_done == true || isValid() == false) return; + m_elapsed += MasterTimer::tick(); + + // Check time wrapping + if (m_elapsed > m_parent->loopDuration()) + { + if (m_parent->runOrder() == Function::PingPong) + { + /* Reverse direction for ping-pong EFX. */ + if (m_runTimeDirection == Function::Forward) + m_runTimeDirection = Function::Backward; + else + m_runTimeDirection = Function::Forward; + } + else if (m_parent->runOrder() == Function::SingleShot) + { + /* De-initialize the fixture and mark as ready. */ + m_done = true; + stop(); + } + + m_elapsed = 0; + } + // Bail out without doing anything if this fixture is waiting for its turn. if (m_parent->propagationMode() == EFX::Serial && m_elapsed < timeOffset() && !m_started) return; // Fade in if (m_started == false) - start(); - - // Nothing to do - if (m_parent->duration() == 0) - return; + start(fader); // Scale from elapsed time in relation to overall duration to a point in a circle - uint pos = (m_elapsed + timeOffset()) % m_parent->duration(); + uint pos = (m_elapsed + timeOffset()) % m_parent->loopDuration(); m_currentAngle = SCALE(float(pos), - float(0), float(m_parent->duration()), + float(0), float(m_parent->loopDuration()), float(0), float(M_PI * 2)); float valX = 0; float valY = 0; - if ((m_parent->propagationMode() == EFX::Serial && - m_elapsed < (m_parent->duration() + timeOffset())) - || m_elapsed < m_parent->duration()) - { - m_parent->calculatePoint(m_runTimeDirection, m_startOffset, m_currentAngle, &valX, &valY); + m_parent->calculatePoint(m_runTimeDirection, m_startOffset, m_currentAngle, &valX, &valY); - /* Prepare faders on universes */ - switch(m_mode) - { - case PanTilt: - setPointPanTilt(universes, fader, valX, valY); - break; - - case RGB: - setPointRGB(universes, fader, valX, valY); - break; - - case Dimmer: - //Use Y for coherence with RGB gradient. - setPointDimmer(universes, fader, valY); - break; - } - } - else + /* Set target values on faders/universes */ + switch (m_mode) { - if (m_parent->runOrder() == Function::PingPong) - { - /* Reverse direction for ping-pong EFX. */ - if (m_runTimeDirection == Function::Forward) - m_runTimeDirection = Function::Backward; - else - m_runTimeDirection = Function::Forward; - } - else if (m_parent->runOrder() == Function::SingleShot) - { - /* De-initialize the fixture and mark as ready. */ - m_ready = true; - stop(); - } + case PanTilt: + setPointPanTilt(universes, fader, valX, valY); + break; - m_elapsed %= m_parent->duration(); + case RGB: + setPointRGB(universes, fader, valX, valY); + break; + + case Dimmer: + //Use Y for coherence with RGB gradient. + setPointDimmer(universes, fader, valY); + break; } } -void EFXFixture::updateFaderValues(FadeChannel *fc, uchar value) +void EFXFixture::updateFaderValues(FadeChannel *fc, quint32 value) { fc->setStart(fc->current()); fc->setTarget(value); @@ -479,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; - /* Write coarse point data to universes */ - 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; - 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 (m_firstMsbChannel != QLCChannel::invalid()) { - if (!fader.isNull()) - { - FadeChannel *fc = fader->getChannelFader(doc(), uni, fxi->id(), intChannel); - updateFaderValues(fc, dimmer); - } - } - else if (fxi->masterIntensityChannel() != 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 25a602b83c..d98c8fa36a 100644 --- a/engine/src/efxfixture.h +++ b/engine/src/efxfixture.h @@ -157,9 +157,9 @@ class EFXFixture /** Reset the fixture when the EFX is stopped */ void reset(); - /** Check, whether this EFXFixture is ready (no more events). + /** Check, whether this EFXFixture is done (no more events). This can happen basically only if SingleShot mode is enabled. */ - bool isReady() const; + bool isDone() const; /** Get this fixture's time offset (in serial and asymmetric modes) */ uint timeOffset() const; @@ -171,9 +171,9 @@ class EFXFixture /** This fixture's current run-time direction */ Function::Direction m_runTimeDirection; - /** When running in single shot mode, the fixture is marked ready + /** When running in single shot mode, the fixture is marked done after it has completed a full cycle. */ - bool m_ready; + bool m_done; /** Indicates, whether start() has been called for this fixture */ bool m_started; @@ -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 537b0fd9fe..2e5a8755ee 100644 --- a/engine/src/fadechannel.cpp +++ b/engine/src/fadechannel.cpp @@ -20,9 +20,9 @@ #include #include +#include "qlcfixturemode.h" #include "fadechannel.h" #include "qlcchannel.h" -#include "qlcmacros.h" #include "universe.h" #include "fixture.h" @@ -30,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) @@ -45,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) @@ -60,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) @@ -68,6 +71,7 @@ FadeChannel::FadeChannel(const Doc *doc, quint32 fxi, quint32 channel) , m_fadeTime(0) , m_elapsed(0) { + m_channels.append(channel); autoDetect(doc); } @@ -82,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; @@ -97,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 @@ -144,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; @@ -204,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 @@ -225,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; +} + +/************************************************************************ + * Values + ************************************************************************/ + +void FadeChannel::setStart(uchar value, int index) +{ + ((uchar *)&m_start)[channelCount() - 1 - index] = value; } -void FadeChannel::setStart(uchar value) +void FadeChannel::setStart(quint32 value) { m_start = value; } -uchar FadeChannel::start() const +uchar FadeChannel::start(int index) const { - return uchar(m_start); + return ((uchar *)&m_start)[channelCount() - 1 - index]; } -void FadeChannel::setTarget(uchar value) +quint32 FadeChannel::start() const +{ + return m_start; +} + +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]; } -uchar FadeChannel::current(qreal intensity) const +quint32 FadeChannel::current() const { - return uchar(floor((qreal(m_current) * intensity) + 0.5)); + return m_current; +} + +uchar FadeChannel::current(qreal intensity, int index) const +{ + 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) @@ -302,6 +372,7 @@ uchar FadeChannel::nextStep(uint ms) { if (elapsed() < UINT_MAX) setElapsed(elapsed() + ms); + return calculateCurrent(fadeTime(), elapsed()); } @@ -320,17 +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); - if (m_flags & Fine) - { - m_current = ((val - floor(val)) * float(UCHAR_MAX)); - } - else - { - m_current = val; - } + 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 5d1450bcd8..01d129077f 100644 --- a/engine/src/fadechannel.h +++ b/engine/src/fadechannel.h @@ -22,8 +22,6 @@ #include -#include "qlcchannel.h" -#include "fixture.h" #include "doc.h" /** @addtogroup engine Engine @@ -52,8 +50,10 @@ class FadeChannel Flashing = (1 << 5), /** Is flashing */ Relative = (1 << 6), /** Relative position */ Override = (1 << 7), /** Override the current universe value */ - Autoremove = (1 << 8), /** Automatically remove the channel once value is written */ - CrossFade = (1 << 9) /** Channel subject to crossfade */ + SetTarget = (1 << 8), /** Set target to current universe value */ + AutoRemove = (1 << 9), /** Automatically remove the channel once target is reached */ + CrossFade = (1 << 10), /** Channel subject to crossfade */ + ForceLTP = (1 << 11) /** Force LTP for flashing scenes */ }; /** Create a new FadeChannel with empty/invalid values */ @@ -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/fixture.cpp b/engine/src/fixture.cpp index ec3cdeb17c..792ace2f47 100644 --- a/engine/src/fixture.cpp +++ b/engine/src/fixture.cpp @@ -310,7 +310,7 @@ QVector Fixture::cmyChannels(int head) const return m_fixtureMode->heads().at(head).cmyChannels(); } -QList Fixture::positionToValues(int type, int degrees) const +QList Fixture::positionToValues(int type, int degrees, bool isRelative) { QList posList; // cache a list of channels processed, to avoid duplicates @@ -320,7 +320,9 @@ QList Fixture::positionToValues(int type, int degrees) const return posList; QLCPhysical phy = fixtureMode()->physical(); - float maxDegrees; + qreal headDegrees = degrees, maxDegrees; + float msbValue = 0, lsbValue = 0; + if (type == QLCChannel::Pan) { maxDegrees = phy.focusPanMax(); @@ -331,22 +333,29 @@ QList Fixture::positionToValues(int type, int degrees) const quint32 panMSB = channelNumber(QLCChannel::Pan, QLCChannel::MSB, i); if (panMSB == QLCChannel::invalid() || chDone.contains(panMSB)) continue; + quint32 panLSB = channelNumber(QLCChannel::Pan, QLCChannel::LSB, i); - float dmxValue = (float)(degrees * UCHAR_MAX) / maxDegrees; - posList.append(SceneValue(id(), panMSB, static_cast(qFloor(dmxValue)))); + if (isRelative) + { + // degrees is a relative value upon the current value. + // Recalculate absolute degrees here + float chDegrees = (qreal(phy.focusPanMax()) / 256.0) * channelValueAt(panMSB); + headDegrees = qBound(0.0, chDegrees + headDegrees, maxDegrees); - qDebug() << "[getFixturePosition] Pan MSB:" << dmxValue; + if (panLSB != QLCChannel::invalid()) + { + chDegrees = (qreal(phy.focusPanMax()) / 65536.0) * channelValueAt(panLSB); + headDegrees = qBound(0.0, chDegrees + headDegrees, maxDegrees); + } + } - quint32 panLSB = channelNumber(QLCChannel::Pan, QLCChannel::LSB, i); + quint16 degToDmx = (headDegrees * 65535.0) / qreal(phy.focusPanMax()); + posList.append(SceneValue(id(), panMSB, static_cast(degToDmx >> 8))); if (panLSB != QLCChannel::invalid()) - { - float lsbDegrees = (float)maxDegrees / (float)UCHAR_MAX; - float lsbValue = (float)((dmxValue - qFloor(dmxValue)) * UCHAR_MAX) / lsbDegrees; - posList.append(SceneValue(id(), panLSB, static_cast(lsbValue))); + posList.append(SceneValue(id(), panLSB, static_cast(degToDmx & 0x00FF))); - qDebug() << "[getFixturePosition] Pan LSB:" << lsbValue; - } + qDebug() << "[positionToValues] Pan MSB:" << msbValue << "LSB:" << lsbValue; chDone.append(panMSB); } @@ -361,22 +370,29 @@ QList Fixture::positionToValues(int type, int degrees) const quint32 tiltMSB = channelNumber(QLCChannel::Tilt, QLCChannel::MSB, i); if (tiltMSB == QLCChannel::invalid() || chDone.contains(tiltMSB)) continue; + quint32 tiltLSB = channelNumber(QLCChannel::Tilt, QLCChannel::LSB, i); - float dmxValue = (float)(degrees * UCHAR_MAX) / maxDegrees; - posList.append(SceneValue(id(), tiltMSB, static_cast(qFloor(dmxValue)))); + if (isRelative) + { + // degrees is a relative value upon the current value. + // Recalculate absolute degrees here + float chDegrees = (qreal(phy.focusTiltMax()) / 256.0) * channelValueAt(tiltMSB); + headDegrees = qBound(0.0, chDegrees + headDegrees, maxDegrees); - qDebug() << "[getFixturePosition] Tilt MSB:" << dmxValue; + if (tiltLSB != QLCChannel::invalid()) + { + chDegrees = (qreal(phy.focusPanMax()) / 65536.0) * channelValueAt(tiltLSB); + headDegrees = qBound(0.0, chDegrees + headDegrees, maxDegrees); + } + } - quint32 tiltLSB = channelNumber(QLCChannel::Tilt, QLCChannel::LSB, i); + quint16 degToDmx = (headDegrees * 65535.0) / qreal(phy.focusTiltMax()); + posList.append(SceneValue(id(), tiltMSB, static_cast(degToDmx >> 8))); if (tiltLSB != QLCChannel::invalid()) - { - float lsbDegrees = (float)maxDegrees / (float)UCHAR_MAX; - float lsbValue = (float)((dmxValue - qFloor(dmxValue)) * UCHAR_MAX) / lsbDegrees; - posList.append(SceneValue(id(), tiltLSB, static_cast(lsbValue))); + posList.append(SceneValue(id(), tiltLSB, static_cast(degToDmx & 0x00FF))); - qDebug() << "[getFixturePosition] Tilt LSB:" << lsbValue; - } + qDebug() << "[positionToValues] Tilt MSB:" << msbValue << "LSB:" << lsbValue; chDone.append(tiltMSB); } @@ -386,7 +402,7 @@ QList Fixture::positionToValues(int type, int degrees) const return posList; } -QList Fixture::zoomToValues(float degrees) const +QList Fixture::zoomToValues(float degrees, bool isRelative) { QList chList; @@ -394,8 +410,13 @@ QList Fixture::zoomToValues(float degrees) const return chList; QLCPhysical phy = fixtureMode()->physical(); - float msbValue = ((degrees - phy.lensDegreesMin()) * (float)UCHAR_MAX) / - (phy.lensDegreesMax() - phy.lensDegreesMin()); + if (!isRelative) + degrees = qBound(float(phy.lensDegreesMin()), degrees, float(phy.lensDegreesMax())); + + float deltaDegrees = phy.lensDegreesMax() - phy.lensDegreesMin(); + // delta : 0xFFFF = deg : x + quint16 degToDmx = ((degrees - (isRelative ? 0 : float(phy.lensDegreesMin()))) * 65535.0) / deltaDegrees; + //qDebug() << "Degrees" << degrees << "DMX" << QString::number(degToDmx, 16); for (quint32 i = 0; i < quint32(m_fixtureMode->channels().size()); i++) { @@ -409,19 +430,29 @@ QList Fixture::zoomToValues(float degrees) const ch->preset() != QLCChannel::BeamZoomFine) continue; + if (isRelative) + { + // degrees is a relative value upon the current value. + // Recalculate absolute degrees here + qreal divider = ch->controlByte() == QLCChannel::MSB ? 256.0 : 65536.0; + float chDegrees = float((phy.lensDegreesMax() - phy.lensDegreesMin()) / divider) * float(channelValueAt(i)); + + //qDebug() << "Relative channel degrees:" << chDegrees << "MSB?" << ch->controlByte(); + + quint16 currDmxVal = (chDegrees * 65535.0) / deltaDegrees; + degToDmx += currDmxVal; + } + if (ch->controlByte() == QLCChannel::MSB) { if (ch->preset() == QLCChannel::BeamZoomBigSmall) - chList.append(SceneValue(id(), i, static_cast(qFloor((float)UCHAR_MAX - msbValue)))); + chList.append(SceneValue(id(), i, static_cast(UCHAR_MAX - (degToDmx >> 8)))); else - chList.append(SceneValue(id(), i, static_cast(qFloor(msbValue)))); + chList.append(SceneValue(id(), i, static_cast(degToDmx >> 8))); } - - if (ch->controlByte() == QLCChannel::LSB) + else if (ch->controlByte() == QLCChannel::LSB) { - float lsbDegrees = (float)(phy.lensDegreesMax() - phy.lensDegreesMin()) / (float)UCHAR_MAX; - float lsbValue = (float)((msbValue - qFloor(msbValue)) * (float)UCHAR_MAX) / lsbDegrees; - chList.append(SceneValue(id(), i, static_cast(lsbValue))); + chList.append(SceneValue(id(), i, static_cast(degToDmx & 0x00FF))); } } @@ -572,7 +603,7 @@ void Fixture::checkAlias(int chIndex, uchar value) // If the channel @chIndex has aliases, check // if replacements are to be done QLCCapability *cap = m_fixtureMode->channel(chIndex)->searchCapability(value); - if (cap == m_aliasInfo[chIndex].m_currCap) + if (cap == NULL || cap == m_aliasInfo[chIndex].m_currCap) return; // first, revert any channel replaced to the original channel set diff --git a/engine/src/fixture.h b/engine/src/fixture.h index cbea1e810c..8dc2a49a42 100644 --- a/engine/src/fixture.h +++ b/engine/src/fixture.h @@ -277,10 +277,10 @@ class Fixture : public QObject /** Return a list of DMX values based on the given position degrees * and the provided type (Pan or Tilt) */ - QList positionToValues(int type, int degrees) const; + QList positionToValues(int type, int degrees, bool isRelative = false); /** Return a list of DMX values based on the given zoom degrees */ - QList zoomToValues(float degrees) const; + QList zoomToValues(float degrees, bool isRelative); /** Set a list of channel indices to exclude from fade transitions */ void setExcludeFadeChannels(QList indices); diff --git a/engine/src/fixturegroup.cpp b/engine/src/fixturegroup.cpp index 55d478e7f8..94b20fde91 100644 --- a/engine/src/fixturegroup.cpp +++ b/engine/src/fixturegroup.cpp @@ -379,7 +379,7 @@ bool FixtureGroup::saveXML(QXmlStreamWriter *doc) /* Fixture heads */ QList pointsList = m_heads.keys(); - foreach(QLCPoint pt, pointsList) + foreach (QLCPoint pt, pointsList) { GroupHead head = m_heads[pt]; doc->writeStartElement(KXMLQLCFixtureGroupHead); diff --git a/engine/src/function.cpp b/engine/src/function.cpp index 518ff70774..f201115e58 100644 --- a/engine/src/function.cpp +++ b/engine/src/function.cpp @@ -26,7 +26,6 @@ #include #include "qlcmacros.h" -#include "qlcfile.h" #include "scriptwrapper.h" #include "mastertimer.h" @@ -521,6 +520,7 @@ void Function::setTempoType(const Function::TempoType &type) } emit changed(m_id); + emit tempoTypeChanged(); } Function::TempoType Function::tempoType() const @@ -959,12 +959,16 @@ QList Function::components() * Flash *****************************************************************************/ -void Function::flash(MasterTimer *timer) +void Function::flash(MasterTimer *timer, bool shouldOverride, bool forceLTP) { Q_UNUSED(timer); + Q_UNUSED(shouldOverride); + Q_UNUSED(forceLTP); if (m_flashing == false) + { emit flashing(m_id, true); + } m_flashing = true; } @@ -1267,7 +1271,7 @@ int Function::requestAttributeOverride(int attributeIndex, qreal value) attributeID = m_lastOverrideAttributeId; m_overrideMap[attributeID] = override; - qDebug() << name() << "Override requested for attribute" << attributeIndex << "value" << value << "new ID" << attributeID; + qDebug() << name() << "Override requested for new attribute" << attributeIndex << "value" << value << "new ID" << attributeID; calculateOverrideValue(attributeIndex); @@ -1275,7 +1279,7 @@ int Function::requestAttributeOverride(int attributeIndex, qreal value) } else { - qDebug() << name() << "Override requested for attribute" << attributeIndex << "value" << value << "single ID" << attributeID; + qDebug() << name() << "Override requested for existing attribute" << attributeIndex << "value" << value << "single ID" << attributeID; } // actually apply the new override value @@ -1334,13 +1338,13 @@ int Function::adjustAttribute(qreal value, int attributeId) if (attributeId >= m_attributes.count() || m_attributes[attributeId].m_value == value) return -1; - // Adjust the original value of an attribute. Only Function editors should do this ! + // Adjust the original value of an attribute. Only Function editors should do this! m_attributes[attributeId].m_value = CLAMP(value, m_attributes[attributeId].m_min, m_attributes[attributeId].m_max); attrIndex = attributeId; } else { - if (m_overrideMap.contains(attributeId) == false || m_overrideMap[attributeId].m_value == value) + if (m_overrideMap.contains(attributeId) == false) return -1; // Adjust an attribute override value and recalculate the final overridden value @@ -1382,7 +1386,7 @@ int Function::getAttributeIndex(QString name) const for (int i = 0; i < m_attributes.count(); i++) { Attribute attr = m_attributes.at(i); - if(attr.m_name == name) + if (attr.m_name == name) return i; } return -1; diff --git a/engine/src/function.h b/engine/src/function.h index 1c48e63388..2200ec9b23 100644 --- a/engine/src/function.h +++ b/engine/src/function.h @@ -99,6 +99,7 @@ class Function : public QObject Q_PROPERTY(Type type READ type CONSTANT) Q_PROPERTY(quint32 totalDuration READ totalDuration WRITE setTotalDuration NOTIFY totalDurationChanged) Q_PROPERTY(RunOrder runOrder READ runOrder WRITE setRunOrder NOTIFY runOrderChanged) + Q_PROPERTY(TempoType tempoType READ tempoType WRITE setTempoType NOTIFY tempoTypeChanged FINAL) public: /** @@ -404,8 +405,18 @@ class Function : public QObject * Tempo type *********************************************************************/ public: - enum TempoType { Original = -1, Time = 0, Beats = 1 }; - enum FractionsType { NoFractions = 0, ByTwoFractions, AllFractions }; + enum TempoType + { + Original = -1, + Time = 0, + Beats = 1 + }; + enum FractionsType + { + NoFractions = 0, + ByTwoFractions, + AllFractions + }; #if QT_VERSION >= 0x050500 Q_ENUM(TempoType) Q_ENUM(FractionsType) @@ -452,6 +463,9 @@ class Function : public QObject /** Set the override speed type (done by a Chaser) */ void setOverrideTempoType(TempoType type); +signals: + void tempoTypeChanged(); + protected slots: /** * This slot is connected to the Master Timer and it is invoked @@ -628,7 +642,7 @@ public slots: *********************************************************************/ public: /** Flash the function */ - virtual void flash(MasterTimer* timer); + virtual void flash(MasterTimer* timer, bool shouldOverride, bool forceLTP); /** UnFlash the function */ virtual void unFlash(MasterTimer* timer); diff --git a/engine/src/genericdmxsource.cpp b/engine/src/genericdmxsource.cpp index fe667f8be0..0a61691dd0 100644 --- a/engine/src/genericdmxsource.cpp +++ b/engine/src/genericdmxsource.cpp @@ -21,7 +21,6 @@ #include "genericfader.h" #include "mastertimer.h" #include "fadechannel.h" -#include "qlcchannel.h" #include "universe.h" #include "doc.h" diff --git a/engine/src/genericfader.cpp b/engine/src/genericfader.cpp index caf7e6e770..f1f5cece85 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,19 +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); + 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()) @@ -179,7 +227,10 @@ 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()); + bool rampUp = fc.target() > fc.start() ? true : false; + value = rampUp ? fc.target() - fc.start() : fc.start() - fc.target(); + value = qreal(value) * intensity(); + value = qreal(rampUp ? fc.start() + value : fc.start() - value) * parentIntensity(); } else if (flags & FadeChannel::Intensity) { @@ -195,11 +246,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->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) && @@ -212,7 +269,7 @@ void GenericFader::write(Universe *universe) it.remove(); } - if (flags & FadeChannel::Autoremove) + if (flags & FadeChannel::AutoRemove && value == fc.target()) it.remove(); } @@ -275,25 +332,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()); + FadeChannel& fc(it.next().value()); - if ((fc.flags() & FadeChannel::Intensity) == 0) - { - fc.addFlag(FadeChannel::Autoremove); - continue; - } + // 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); - } + 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/gradient.cpp b/engine/src/gradient.cpp index 562d4e9535..bd06cb2579 100644 --- a/engine/src/gradient.cpp +++ b/engine/src/gradient.cpp @@ -17,13 +17,13 @@ limitations under the License. */ -#include "gradient.h" - #include #include #include #include +#include "gradient.h" + QImage Gradient::m_rgb = QImage(); QImage Gradient::getRGBGradient() @@ -59,7 +59,7 @@ void Gradient::fillWithGradient(int r, int g, int b, QPainter *painter, int x) void Gradient::initialize() { - if( m_rgb.isNull() == false ) + if (m_rgb.isNull() == false) return; m_rgb = QImage(256, 256, QImage::Format_RGB32); diff --git a/engine/src/grandmaster.cpp b/engine/src/grandmaster.cpp index aee6d73627..a60f8c6077 100644 --- a/engine/src/grandmaster.cpp +++ b/engine/src/grandmaster.cpp @@ -17,6 +17,11 @@ limitations under the License. */ +#include + +#include "grandmaster.h" +#include "qlcmacros.h" + #define KXMLQLCGMValueModeLimit "Limit" #define KXMLQLCGMValueModeReduce "Reduce" #define KXMLQLCGMChannelModeAllChannels "All" @@ -24,11 +29,6 @@ #define KXMLQLCGMSliderModeNormal "Normal" #define KXMLQLCGMSliderModeInverted "Inverted" -#include - -#include "grandmaster.h" -#include "qlcmacros.h" - GrandMaster::GrandMaster(QObject *parent) : QObject(parent) , m_valueMode(Reduce) diff --git a/engine/src/inputoutputmap.cpp b/engine/src/inputoutputmap.cpp index ffe6d89a0d..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); @@ -323,7 +325,7 @@ void InputOutputMap::setGrandMasterChannelMode(GrandMaster::ChannelMode mode) { Q_ASSERT(m_grandMaster != NULL); - if(m_grandMaster->channelMode() != mode) + if (m_grandMaster->channelMode() != mode) { m_grandMaster->setChannelMode(mode); m_universeChanged = true; @@ -341,7 +343,7 @@ void InputOutputMap::setGrandMasterValueMode(GrandMaster::ValueMode mode) { Q_ASSERT(m_grandMaster != NULL); - if(m_grandMaster->valueMode() != mode) + if (m_grandMaster->valueMode() != mode) { m_grandMaster->setValueMode(mode); m_universeChanged = true; @@ -408,16 +410,16 @@ 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; QLCIOPlugin *plugin = doc()->ioPluginCache()->plugin(pluginName); - if (!inputUID.isEmpty()) + if (!inputUID.isEmpty() && plugin != NULL) { QStringList inputs = plugin->inputs(); int lIdx = inputs.indexOf(inputUID); @@ -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&))); } } } @@ -491,7 +493,7 @@ bool InputOutputMap::setOutputPatch(quint32 universe, const QString &pluginName, QMutexLocker locker(&m_universeMutex); QLCIOPlugin *plugin = doc()->ioPluginCache()->plugin(pluginName); - if (!outputUID.isEmpty()) + if (!outputUID.isEmpty() && plugin != NULL) { QStringList inputs = plugin->outputs(); int lIdx = inputs.indexOf(outputUID); @@ -581,9 +583,13 @@ quint32 InputOutputMap::outputMapping(const QString &pluginName, quint32 output) { for (quint32 uni = 0; uni < universesCount(); uni++) { - const OutputPatch* p = m_universeArray.at(uni)->outputPatch(); - if (p != NULL && p->pluginName() == pluginName && p->output() == output) - return uni; + Universe *universe = m_universeArray.at(uni); + for (int i = 0; i < universe->outputPatchesCount(); i++) + { + const OutputPatch* p = universe->outputPatch(i); + if (p != NULL && p->pluginName() == pluginName && p->output() == output) + return uni; + } } return QLCIOPlugin::invalidLine(); @@ -608,6 +614,26 @@ QString InputOutputMap::pluginDescription(const QString &pluginName) return ""; } +void InputOutputMap::removeDuplicates(QStringList &list) +{ + if (list.count() == 1) + return; + + int c = 2; + + for (int i = 1; i < list.count(); i++) + { + for (int j = 0; j < i; j++) + { + if (list.at(i) == list.at(j)) + { + list.replace(i, QString("%1 %2").arg(list.at(j)).arg(c)); + c++; + } + } + } +} + QStringList InputOutputMap::inputPluginNames() { QStringList list; @@ -640,7 +666,11 @@ QStringList InputOutputMap::pluginInputs(const QString& pluginName) if (ip == NULL) return QStringList(); else - return ip->inputs(); + { + QStringList iList = ip->inputs(); + removeDuplicates(iList); + return iList; + } } QStringList InputOutputMap::pluginOutputs(const QString& pluginName) @@ -649,7 +679,11 @@ QStringList InputOutputMap::pluginOutputs(const QString& pluginName) if (op == NULL) return QStringList(); else - return op->outputs(); + { + QStringList oList = op->outputs(); + removeDuplicates(oList); + return oList; + } } bool InputOutputMap::pluginSupportsFeedback(const QString& pluginName) @@ -717,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; @@ -726,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 @@ -741,11 +775,15 @@ void InputOutputMap::slotPluginConfigurationChanged(QLCIOPlugin* plugin) bool success = true; for (quint32 i = 0; i < universesCount(); i++) { - OutputPatch* op = m_universeArray.at(i)->outputPatch(); - - if (op != NULL && op->plugin() == plugin) + Universe *universe = m_universeArray.at(i); + for (int oi = 0; oi < universe->outputPatchesCount(); oi++) { - /*success = */ op->reconnect(); + OutputPatch* op = universe->outputPatch(oi); + + if (op != NULL && op->plugin() == plugin) + { + /*success = */ op->reconnect(); + } } InputPatch* ip = m_universeArray.at(i)->inputPatch(); @@ -961,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); @@ -988,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) @@ -1018,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) @@ -1234,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(); @@ -1251,12 +1321,15 @@ bool InputOutputMap::saveXML(QXmlStreamWriter *doc) const /* IO Map Instance entry */ doc->writeStartElement(KXMLIOMap); - foreach(Universe *uni, m_universeArray) + 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); doc->writeEndElement(); return true; } - - diff --git a/engine/src/inputoutputmap.h b/engine/src/inputoutputmap.h index 296834ebc7..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 { @@ -355,7 +358,7 @@ class InputOutputMap : public QObject * @param inputUID Unique plugin output line identifier as string * @param output A universe provided by the plugin to patch to * @param isFeedback Determine if this line is a feedback output - * @param index the output patch index + * @param index The output patch index * * @return true if successful, otherwise false */ @@ -499,7 +502,11 @@ 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 */ + void removeDuplicates(QStringList &list); private slots: /** Slot that catches plugin configuration change notifications from UIPluginCache */ @@ -576,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/inputpatch.cpp b/engine/src/inputpatch.cpp index 3481003c5d..b0878dcf72 100644 --- a/engine/src/inputpatch.cpp +++ b/engine/src/inputpatch.cpp @@ -140,7 +140,7 @@ bool InputPatch::reconnect() bool ret = m_plugin->openInput(m_pluginLine, m_universe); if (ret == true) { - foreach(QString par, m_parametersCache.keys()) + foreach (QString par, m_parametersCache.keys()) { qDebug() << "[InputPatch] restoring parameter:" << par << m_parametersCache[par]; m_plugin->setParameter(m_universe, m_pluginLine, QLCIOPlugin::Input, par, m_parametersCache[par]); diff --git a/engine/src/keypadparser.cpp b/engine/src/keypadparser.cpp index bc2d64cbc6..d22ef00eef 100644 --- a/engine/src/keypadparser.cpp +++ b/engine/src/keypadparser.cpp @@ -17,8 +17,11 @@ limitations under the License. */ +#include + #include "keypadparser.h" #include "qlcmacros.h" +#include "universe.h" KeyPadParser::KeyPadParser() { @@ -111,6 +114,9 @@ QList KeyPadParser::parseCommand(Doc *doc, QString command, { case CommandNone: // no command: this is a channel number + if (number <= 0) + break; + fromChannel = number; toChannel = fromChannel; channelSet = true; @@ -185,7 +191,10 @@ QList KeyPadParser::parseCommand(Doc *doc, QString command, uchar uniValue = 0; SceneValue scv; - if (quint32(uniData.length()) >= i) + if (i >= UNIVERSE_SIZE) + continue; + + if (quint32(uniData.length()) > i) uniValue = uchar(uniData.at(i)); scv.channel = i; @@ -194,9 +203,9 @@ QList KeyPadParser::parseCommand(Doc *doc, QString command, else if (lastCommand == CommandMinus) scv.value = CLAMP(uniValue - toValue, 0, 255); else if (lastCommand == CommandPlusPercent) - scv.value = CLAMP(uniValue * (1.0 + toValue), 0, 255); + scv.value = CLAMP(lrintf(uniValue * (1.0 + toValue)), 0, 255); else if (lastCommand == CommandMinusPercent) - scv.value = CLAMP(uniValue - (float(uniValue) * toValue), 0, 255); + scv.value = CLAMP(lrintf(uniValue - (float(uniValue) * toValue)), 0, 255); else if (lastCommand == CommandZERO) scv.value = 0; else if (lastCommand == CommandFULL) diff --git a/engine/src/mastertimer-unix.cpp b/engine/src/mastertimer-unix.cpp index fb69458a7d..f994ae836d 100644 --- a/engine/src/mastertimer-unix.cpp +++ b/engine/src/mastertimer-unix.cpp @@ -40,7 +40,7 @@ MasterTimerPrivate::MasterTimerPrivate(MasterTimer* masterTimer) , m_run(false) { Q_ASSERT(masterTimer != NULL); -#if defined(Q_OS_OSX) || defined(Q_OS_IOS) +#if defined(Q_OS_MACOS) || defined(Q_OS_IOS) host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock); #endif } @@ -56,7 +56,7 @@ void MasterTimerPrivate::stop() wait(); } -#if defined(Q_OS_OSX) || defined(Q_OS_IOS) +#if defined(Q_OS_MACOS) || defined(Q_OS_IOS) int MasterTimerPrivate::compareTime(mach_timespec_t *time1, mach_timespec_t *time2) #else int MasterTimerPrivate::compareTime(struct timespec *time1, struct timespec *time2) @@ -97,7 +97,7 @@ void MasterTimerPrivate::run() int ret = 0; /* Allocate all the memory at the start so we don't waste any time */ -#if defined(Q_OS_OSX) || defined(Q_OS_IOS) +#if defined(Q_OS_MACOS) || defined(Q_OS_IOS) mach_timespec_t* finish = static_cast (malloc(sizeof(mach_timespec_t))); mach_timespec_t* current = static_cast (malloc(sizeof(mach_timespec_t))); #else @@ -110,7 +110,7 @@ void MasterTimerPrivate::run() sleepTime->tv_sec = 0; /* This is the start time for the timer */ -#if defined(Q_OS_OSX) || defined(Q_OS_IOS) +#if defined(Q_OS_MACOS) || defined(Q_OS_IOS) ret = clock_get_time(cclock, finish); #else ret = clock_gettime(CLOCK_MONOTONIC, finish); @@ -132,7 +132,7 @@ void MasterTimerPrivate::run() finish->tv_sec += (finish->tv_nsec + nsTickTime) / 1000000000L; finish->tv_nsec = (finish->tv_nsec + nsTickTime) % 1000000000L; -#if defined(Q_OS_OSX) || defined(Q_OS_IOS) +#if defined(Q_OS_MACOS) || defined(Q_OS_IOS) ret = clock_get_time(cclock, current); #else ret = clock_gettime(CLOCK_MONOTONIC, current); @@ -153,7 +153,7 @@ void MasterTimerPrivate::run() /* No need to sleep. Immediately process the next tick */ mt->timerTick(); /* Now the finish time needs to be recalibrated */ -#if defined(Q_OS_OSX) || defined(Q_OS_IOS) +#if defined(Q_OS_MACOS) || defined(Q_OS_IOS) clock_get_time(cclock, finish); #else clock_gettime(CLOCK_MONOTONIC, finish); @@ -185,7 +185,7 @@ void MasterTimerPrivate::run() #if 0 /* Now take full CPU for precision (only a few nanoseconds, at maximum 100 nanoseconds) */ -#if defined(Q_OS_OSX) || defined(Q_OS_IOS) +#if defined(Q_OS_MACOS) || defined(Q_OS_IOS) ret = clock_get_time(cclock, current); #else ret = clock_gettime(CLOCK_MONOTONIC, current); @@ -194,7 +194,7 @@ void MasterTimerPrivate::run() while (sleepTime->tv_nsec > 5) { -#if defined(Q_OS_OSX) || defined(Q_OS_IOS) +#if defined(Q_OS_MACOS) || defined(Q_OS_IOS) ret = clock_get_time(cclock, current); #else ret = clock_gettime(CLOCK_MONOTONIC, current); diff --git a/engine/src/mastertimer-unix.h b/engine/src/mastertimer-unix.h index 78aded770e..a377801268 100644 --- a/engine/src/mastertimer-unix.h +++ b/engine/src/mastertimer-unix.h @@ -23,7 +23,7 @@ #include -#if defined(Q_OS_OSX) || defined(Q_OS_IOS) +#if defined(Q_OS_MACOS) || defined(Q_OS_IOS) #include #include #endif @@ -44,7 +44,7 @@ class MasterTimerPrivate : public QThread private: void run(); -#if defined(Q_OS_OSX) || defined(Q_OS_IOS) +#if defined(Q_OS_MACOS) || defined(Q_OS_IOS) int compareTime(mach_timespec_t *time1, mach_timespec_t *time2); #else int compareTime(struct timespec *time1, struct timespec *time2); @@ -52,7 +52,7 @@ class MasterTimerPrivate : public QThread private: bool m_run; -#if defined(Q_OS_OSX) || defined(Q_OS_IOS) +#if defined(Q_OS_MACOS) || defined(Q_OS_IOS) clock_serv_t cclock; #endif }; diff --git a/engine/src/mastertimer.cpp b/engine/src/mastertimer.cpp index f51fc85b3d..a2c170e86a 100644 --- a/engine/src/mastertimer.cpp +++ b/engine/src/mastertimer.cpp @@ -32,10 +32,8 @@ #include "inputoutputmap.h" #include "genericfader.h" -#include "fadechannel.h" #include "mastertimer.h" #include "dmxsource.h" -#include "qlcmacros.h" #include "function.h" #include "universe.h" #include "doc.h" @@ -266,6 +264,8 @@ void MasterTimer::timerTickFunctions(QList universes) removeList << i; // Don't remove the item from the list just yet. functionListHasChanged = true; stoppedAFunction = true; + + emit functionStopped(function->id()); } } } diff --git a/engine/src/mastertimer.h b/engine/src/mastertimer.h index 4fa3e78417..2a840e4ec3 100644 --- a/engine/src/mastertimer.h +++ b/engine/src/mastertimer.h @@ -114,6 +114,9 @@ class MasterTimer : public QObject /** Emitted when a Function is started */ void functionStarted(quint32 id); + /** Emitted when a Function has just been stopped */ + void functionStopped(quint32 id); + private: /** Execute one timer tick for each registered Function */ void timerTickFunctions(QList universes); diff --git a/engine/src/monitorproperties.cpp b/engine/src/monitorproperties.cpp index 11d376257e..8ee1bee6e2 100644 --- a/engine/src/monitorproperties.cpp +++ b/engine/src/monitorproperties.cpp @@ -707,7 +707,7 @@ bool MonitorProperties::saveXML(QXmlStreamWriter *doc, const Doc *mainDocument) doc->writeTextElement(KXMLQLCMonitorCommonBackground, mainDocument->normalizeComponentPath(commonBackgroundImage())); } - else if(customBackgroundList().isEmpty() == false) + else if (customBackgroundList().isEmpty() == false) { QMapIterator it(customBackgroundList()); while (it.hasNext() == true) @@ -801,7 +801,7 @@ bool MonitorProperties::saveXML(QXmlStreamWriter *doc, const Doc *mainDocument) // * write generic items information * // *********************************************************** QMapIterator it(m_genericItems); - while(it.hasNext()) + while (it.hasNext()) { it.next(); quint32 itemID = it.key(); diff --git a/engine/src/outputpatch.cpp b/engine/src/outputpatch.cpp index 5823bb8730..bce2ae056b 100644 --- a/engine/src/outputpatch.cpp +++ b/engine/src/outputpatch.cpp @@ -97,7 +97,7 @@ bool OutputPatch::reconnect() bool ret = m_plugin->openOutput(m_pluginLine, m_universe); if (ret == true) { - foreach(QString par, m_parametersCache.keys()) + foreach (QString par, m_parametersCache.keys()) m_plugin->setParameter(m_universe, m_pluginLine, QLCIOPlugin::Output, par, m_parametersCache[par]); } return ret; @@ -191,7 +191,7 @@ void OutputPatch::setBlackout(bool blackout) emit blackoutChanged(m_blackout); } -void OutputPatch::dump(quint32 universe, const QByteArray& data) +void OutputPatch::dump(quint32 universe, const QByteArray& data, bool dataChanged) { /* Don't do anything if there is no plugin and/or output line. */ if (m_plugin != NULL && m_pluginLine != QLCIOPlugin::invalidLine()) @@ -201,11 +201,11 @@ void OutputPatch::dump(quint32 universe, const QByteArray& data) if (m_pauseBuffer.isNull()) m_pauseBuffer.append(data); - m_plugin->writeUniverse(universe, m_pluginLine, m_pauseBuffer); + m_plugin->writeUniverse(universe, m_pluginLine, m_pauseBuffer, dataChanged); } else { - m_plugin->writeUniverse(universe, m_pluginLine, data); + m_plugin->writeUniverse(universe, m_pluginLine, data, dataChanged); } } } diff --git a/engine/src/outputpatch.h b/engine/src/outputpatch.h index f85067cce2..43d57ca187 100644 --- a/engine/src/outputpatch.h +++ b/engine/src/outputpatch.h @@ -118,7 +118,7 @@ class OutputPatch : public QObject /** Write the contents of a 512 channel value buffer to the plugin. * Called periodically by OutputMap. No need to call manually. */ - void dump(quint32 universe, const QByteArray &data); + void dump(quint32 universe, const QByteArray &data, bool dataChanged); signals: void pausedChanged(bool paused); diff --git a/engine/src/qlccapability.cpp b/engine/src/qlccapability.cpp index 8a89bd311e..f3b456ab4c 100644 --- a/engine/src/qlccapability.cpp +++ b/engine/src/qlccapability.cpp @@ -469,7 +469,7 @@ bool QLCCapability::loadXML(QXmlStreamReader &doc) /* ************************* LEGACY ATTRIBUTES ************************* */ /* Get (optional) resource name for gobo/effect/... */ - if(attrs.hasAttribute(KXMLQLCCapabilityResource)) + if (attrs.hasAttribute(KXMLQLCCapabilityResource)) { QString path = attrs.value(KXMLQLCCapabilityResource).toString(); if (QFileInfo(path).isRelative()) diff --git a/engine/src/qlcchannel.h b/engine/src/qlcchannel.h index 20763c9da4..b9e714b585 100644 --- a/engine/src/qlcchannel.h +++ b/engine/src/qlcchannel.h @@ -22,6 +22,7 @@ #define QLCCHANNEL_H #include +#include #include #include #include diff --git a/engine/src/qlcclipboard.cpp b/engine/src/qlcclipboard.cpp index 508ee40b02..f93b073e60 100644 --- a/engine/src/qlcclipboard.cpp +++ b/engine/src/qlcclipboard.cpp @@ -18,8 +18,7 @@ */ #include "qlcclipboard.h" -#include "chaser.h" -#include "scene.h" +#include "doc.h" QLCClipboard::QLCClipboard(Doc *doc) : m_doc(doc) diff --git a/engine/src/qlcconfig.h.in b/engine/src/qlcconfig.h.in new file mode 100644 index 0000000000..f9a0fe79fe --- /dev/null +++ b/engine/src/qlcconfig.h.in @@ -0,0 +1,31 @@ +#ifndef QLCCONFIG_H +#define QLCCONFIG_H + +#define APPNAME "${APPNAME}" +#define FXEDNAME "${FXEDNAME}" +#define APPVERSION "${APPVERSION}" +#define DOCSDIR "${INSTALLROOT}/${DOCSDIR}" +#define INPUTPROFILEDIR "${INSTALLROOT}/${INPUTPROFILEDIR}" +#define USERQLCPLUSDIR "${USERDATADIR}" +#define USERINPUTPROFILEDIR "${USERINPUTPROFILEDIR}" +#define MIDITEMPLATEDIR "${INSTALLROOT}/${MIDITEMPLATEDIR}" +#define USERMIDITEMPLATEDIR "${USERMIDITEMPLATEDIR}" +#define MODIFIERSTEMPLATEDIR "${INSTALLROOT}/${MODIFIERSTEMPLATEDIR}" +#define USERMODIFIERSTEMPLATEDIR "${USERMODIFIERSTEMPLATEDIR}" +#define FIXTUREDIR "${INSTALLROOT}/${FIXTUREDIR}" +#define USERFIXTUREDIR "${USERFIXTUREDIR}" +#define PLUGINDIR "${INSTALLROOT}/${PLUGINDIR}" +#define AUDIOPLUGINDIR "${INSTALLROOT}/${AUDIOPLUGINDIR}" +#define TRANSLATIONDIR "${INSTALLROOT}/${TRANSLATIONDIR}" +#define RGBSCRIPTDIR "${INSTALLROOT}/${RGBSCRIPTDIR}" +#define USERRGBSCRIPTDIR "${USERRGBSCRIPTDIR}" +#define GOBODIR "${INSTALLROOT}/${GOBODIR}" +#define WEBFILESDIR "${INSTALLROOT}/${WEBFILESDIR}" + +#ifdef QMLUI +#define MESHESDIR "${INSTALLROOT}/${MESHESDIR}" +#define COLORFILTERSDIR "${INSTALLROOT}/${COLORFILTERSDIR}" +#define USERCOLORFILTERSDIR "${USERCOLORFILTERSDIR}" +#endif /* QMLUI */ + +#endif /* QLCCONFIG_H */ diff --git a/engine/src/qlcconfig.h.noroot.in b/engine/src/qlcconfig.h.noroot.in new file mode 100644 index 0000000000..e52c284f40 --- /dev/null +++ b/engine/src/qlcconfig.h.noroot.in @@ -0,0 +1,31 @@ +#ifndef QLCCONFIG_H +#define QLCCONFIG_H + +#define APPNAME "${APPNAME}" +#define FXEDNAME "${FXEDNAME}" +#define APPVERSION "${APPVERSION}" +#define DOCSDIR "${DOCSDIR}" +#define INPUTPROFILEDIR "${INPUTPROFILEDIR}" +#define USERQLCPLUSDIR "${USERDATADIR}" +#define USERINPUTPROFILEDIR "${USERINPUTPROFILEDIR}" +#define MIDITEMPLATEDIR "${MIDITEMPLATEDIR}" +#define USERMIDITEMPLATEDIR "${USERMIDITEMPLATEDIR}" +#define MODIFIERSTEMPLATEDIR "${MODIFIERSTEMPLATEDIR}" +#define USERMODIFIERSTEMPLATEDIR "${USERMODIFIERSTEMPLATEDIR}" +#define FIXTUREDIR "${FIXTUREDIR}" +#define USERFIXTUREDIR "${USERFIXTUREDIR}" +#define PLUGINDIR "${PLUGINDIR}" +#define AUDIOPLUGINDIR "${AUDIOPLUGINDIR}" +#define TRANSLATIONDIR "${TRANSLATIONDIR}" +#define RGBSCRIPTDIR "${RGBSCRIPTDIR}" +#define USERRGBSCRIPTDIR "${USERRGBSCRIPTDIR}" +#define GOBODIR "${GOBODIR}" +#define WEBFILESDIR "${WEBFILESDIR}" + +#ifdef QMLUI +#define MESHESDIR "${MESHESDIR}" +#define COLORFILTERSDIR "${COLORFILTERSDIR}" +#define USERCOLORFILTERSDIR "${USERCOLORFILTERSDIR}" +#endif /* QMLUI */ + +#endif /* QLCCONFIG_H */ diff --git a/engine/src/qlcfile.cpp b/engine/src/qlcfile.cpp index cc68eeb092..051a16c80c 100644 --- a/engine/src/qlcfile.cpp +++ b/engine/src/qlcfile.cpp @@ -149,7 +149,7 @@ QString QLCFile::currentUserName() DWORD length = UNLEN + 1; TCHAR name[length]; if (GetUserName(name, &length)) - return QString::fromUtf16((ushort*) name); + return QString::fromUtf16(reinterpret_cast(name)); else return QString("Unknown windows user"); #else @@ -224,7 +224,7 @@ QDir QLCFile::userDirectory(QString path, QString fallBackPath, QStringList exte LPTSTR home = (LPTSTR) malloc(256 * sizeof(TCHAR)); GetEnvironmentVariable(TEXT("UserProfile"), home, 256); dir.setPath(QString("%1/%2") - .arg(QString::fromUtf16(reinterpret_cast (home))) + .arg(QString::fromUtf16(reinterpret_cast(home))) .arg(path)); free(home); #endif diff --git a/engine/src/qlcfile.h b/engine/src/qlcfile.h index d70f85ed4f..8c53e6e476 100644 --- a/engine/src/qlcfile.h +++ b/engine/src/qlcfile.h @@ -60,6 +60,9 @@ class QString; #define KXMLQLCCreatorVersion QString("Version") #define KXMLQLCCreatorAuthor QString("Author") +// share fixture list tag +#define KXMLQLCFixturesList QString("FixtureList") + // True and false #define KXMLQLCTrue "True" #define KXMLQLCFalse "False" @@ -126,7 +129,7 @@ class QLCFile * @param extension * @return */ - static QDir systemDirectory(QString path, QString extension = QString() ); + static QDir systemDirectory(QString path, QString extension = QString()); /** * @brief systemDirectory returns a system dependant QDir based diff --git a/engine/src/qlcfixturedef.cpp b/engine/src/qlcfixturedef.cpp index 31c22d8be8..cba214b395 100644 --- a/engine/src/qlcfixturedef.cpp +++ b/engine/src/qlcfixturedef.cpp @@ -27,9 +27,7 @@ #include "qlcfixturemode.h" #include "qlcfixturedef.h" -#include "qlccapability.h" #include "qlcchannel.h" -#include "qlcconfig.h" #include "qlcfile.h" #include "fixture.h" @@ -206,18 +204,24 @@ void QLCFixtureDef::checkLoaded(QString mapPath) } if (m_fileAbsolutePath.isEmpty()) { - qWarning() << Q_FUNC_INFO << "Empty file path provided ! This is a trouble."; + qWarning() << Q_FUNC_INFO << "Empty file path provided! This is a trouble."; return; } - QString absPath = QString("%1%2%3").arg(mapPath).arg(QDir::separator()).arg(m_fileAbsolutePath); - qDebug() << "Loading fixture definition now... " << absPath; - bool error = loadXML(absPath); + // check if path is relative (from map) or absolute (user def) + QDir defPath(m_fileAbsolutePath); + if (defPath.isRelative()) + m_fileAbsolutePath = QString("%1%2%3").arg(mapPath).arg(QDir::separator()).arg(m_fileAbsolutePath); + + qDebug() << "Loading fixture definition now... " << m_fileAbsolutePath; + bool error = loadXML(m_fileAbsolutePath); if (error == false) - { m_isLoaded = true; - m_fileAbsolutePath = QString(); - } +} + +void QLCFixtureDef::setLoaded(bool loaded) +{ + m_isLoaded = loaded; } bool QLCFixtureDef::isUser() const diff --git a/engine/src/qlcfixturedef.h b/engine/src/qlcfixturedef.h index 8b187c3af5..183341fa75 100644 --- a/engine/src/qlcfixturedef.h +++ b/engine/src/qlcfixturedef.h @@ -149,6 +149,7 @@ class QLCFixtureDef /** Check if the full definition has been loaded */ void checkLoaded(QString mapPath); + void setLoaded(bool loaded); /** Get/Set if the definition is user-made */ bool isUser() const; diff --git a/engine/src/qlcfixturedefcache.cpp b/engine/src/qlcfixturedefcache.cpp index 4f11a16ee6..83fb3a9fc3 100644 --- a/engine/src/qlcfixturedefcache.cpp +++ b/engine/src/qlcfixturedefcache.cpp @@ -149,6 +149,23 @@ bool QLCFixtureDefCache::storeFixtureDef(QString filename, QString data) return true; } +bool QLCFixtureDefCache::reloadFixtureDef(QLCFixtureDef *fixtureDef) +{ + int idx = m_defs.indexOf(fixtureDef); + if (idx == -1) + return false; + + QLCFixtureDef *def = m_defs.takeAt(idx); + QString absPath = def->definitionSourceFile(); + delete def; + + QLCFixtureDef *origDef = new QLCFixtureDef(); + origDef->loadXML(absPath); + m_defs << origDef; + + return true; +} + bool QLCFixtureDefCache::load(const QDir& dir) { qDebug() << Q_FUNC_INFO << dir.path(); @@ -361,6 +378,8 @@ bool QLCFixtureDefCache::loadQXF(const QString& path, bool isUser) if (error == QFile::NoError) { fxi->setIsUser(isUser); + fxi->setDefinitionSourceFile(path); + fxi->setLoaded(true); /* Delete the def if it's a duplicate. */ if (addFixtureDef(fxi) == false) @@ -392,6 +411,8 @@ bool QLCFixtureDefCache::loadD4(const QString& path) // a D4 personality is always a user-made fixture fxi->setIsUser(true); + fxi->setDefinitionSourceFile(path); + fxi->setLoaded(true); /* Delete the def if it's a duplicate. */ if (addFixtureDef(fxi) == false) diff --git a/engine/src/qlcfixturedefcache.h b/engine/src/qlcfixturedefcache.h index 316d75eedd..e195827dad 100644 --- a/engine/src/qlcfixturedefcache.h +++ b/engine/src/qlcfixturedefcache.h @@ -97,7 +97,7 @@ class QLCFixtureDefCache * @param fixtureDef The fixture definition to add * @return true, if $fixtureDef was added, otherwise false */ - bool addFixtureDef(QLCFixtureDef* fixtureDef); + bool addFixtureDef(QLCFixtureDef *fixtureDef); /** * Store a fixture in the fixtures user data folder @@ -110,6 +110,14 @@ class QLCFixtureDefCache */ bool storeFixtureDef(QString filename, QString data); + /** + * Realod from file a definition with the provided reference + * + * @param fixtureDef The fixture definition to remove + * @return true, if $fixtureDef was found and removed, otherwise false + */ + bool reloadFixtureDef(QLCFixtureDef *fixtureDef); + /** * Load fixture definitions from the given path. Ignores duplicates. * Returns true even if $fixturePath doesn't contain any fixtures, diff --git a/engine/src/qlcfixturehead.cpp b/engine/src/qlcfixturehead.cpp index 1173abe22d..11e5d47637 100644 --- a/engine/src/qlcfixturehead.cpp +++ b/engine/src/qlcfixturehead.cpp @@ -174,7 +174,7 @@ void QLCFixtureHead::cacheChannels(const QLCFixtureMode* mode) m_shutterChannels.clear(); m_channelsMap.clear(); - foreach(quint32 i, m_channels) + foreach (quint32 i, m_channels) { if ((int)i >= mode->channels().size()) { @@ -264,7 +264,7 @@ bool QLCFixtureHead::saveXML(QXmlStreamWriter *doc) const doc->writeStartElement(KXMLQLCFixtureHead); - foreach(quint32 index, m_channels) + foreach (quint32 index, m_channels) doc->writeTextElement(KXMLQLCFixtureHeadChannel, QString::number(index)); doc->writeEndElement(); diff --git a/engine/src/qlcfixturemode.cpp b/engine/src/qlcfixturemode.cpp index 1a5a7546eb..9ae993c882 100644 --- a/engine/src/qlcfixturemode.cpp +++ b/engine/src/qlcfixturemode.cpp @@ -30,7 +30,7 @@ #include "qlcchannel.h" #include "qlcphysical.h" -QLCFixtureMode::QLCFixtureMode(QLCFixtureDef* fixtureDef) +QLCFixtureMode::QLCFixtureMode(QLCFixtureDef *fixtureDef) : m_fixtureDef(fixtureDef) , m_masterIntensityChannel(QLCChannel::invalid()) , m_useGlobalPhysical(true) @@ -38,9 +38,8 @@ QLCFixtureMode::QLCFixtureMode(QLCFixtureDef* fixtureDef) Q_ASSERT(fixtureDef != NULL); } -QLCFixtureMode::QLCFixtureMode(QLCFixtureDef* fixtureDef, const QLCFixtureMode* mode) +QLCFixtureMode::QLCFixtureMode(QLCFixtureDef *fixtureDef, const QLCFixtureMode *mode) : m_fixtureDef(fixtureDef) - , m_actsOnChannelsList(mode->actsOnChannelsList()) , m_masterIntensityChannel(QLCChannel::invalid()) , m_useGlobalPhysical(true) { @@ -64,7 +63,14 @@ QLCFixtureMode& QLCFixtureMode::operator=(const QLCFixtureMode& mode) m_physical = mode.m_physical; m_heads = mode.m_heads; m_masterIntensityChannel = QLCChannel::invalid(); - m_actsOnChannelsList = mode.actsOnChannelsList(); + + m_actsOnMap.clear(); + QMapIterator ait(mode.m_actsOnMap); + while (ait.hasNext()) + { + ait.next(); + m_actsOnMap.insert(ait.key(), ait.value()); + } /* Clear the existing list of channels */ m_channels.clear(); @@ -220,7 +226,7 @@ QVector QLCFixtureMode::channels() const return m_channels; } -quint32 QLCFixtureMode::channelNumber(QLCChannel* channel) const +quint32 QLCFixtureMode::channelNumber(QLCChannel *channel) const { if (channel == NULL) return QLCChannel::invalid(); @@ -246,14 +252,23 @@ quint32 QLCFixtureMode::masterIntensityChannel() const return m_masterIntensityChannel; } -void QLCFixtureMode::updateActsOnChannel(QLCChannel *mainChannel, QLCChannel *actsOnChannel) +quint32 QLCFixtureMode::primaryChannel(quint32 chIndex) { - m_actsOnChannelsList.insert(mainChannel, actsOnChannel); + return m_secondaryMap.value(chIndex, QLCChannel::invalid()); } -QHash QLCFixtureMode::actsOnChannelsList() const +quint32 QLCFixtureMode::channelActsOn(quint32 chIndex) { - return m_actsOnChannelsList; + return m_actsOnMap.value(chIndex, QLCChannel::invalid()); +} + +void QLCFixtureMode::setChannelActsOn(quint32 chIndex, quint32 actsOnIndex) +{ + if (m_actsOnMap.contains(chIndex)) + m_actsOnMap.remove(chIndex); + + if (actsOnIndex != QLCChannel::invalid()) + m_actsOnMap[chIndex] = actsOnIndex; } /***************************************************************************** @@ -298,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; } } @@ -369,9 +401,6 @@ bool QLCFixtureMode::loadXML(QXmlStreamReader &doc) setName(str); } - /* Temporary list with mode's channels pointer and acts on indexes. */ - QList listChannelsWithActsOnIndex; - /* Subtags */ while (doc.readNextStartElement()) { @@ -381,21 +410,17 @@ bool QLCFixtureMode::loadXML(QXmlStreamReader &doc) Q_ASSERT(m_fixtureDef != NULL); str = doc.attributes().value(KXMLQLCFixtureModeChannelNumber).toString(); - int actsOnChannelIndex = -1; + quint32 actsOnChannelIndex = QLCChannel::invalid(); if (doc.attributes().hasAttribute(KXMLQLCFixtureModeChannelActsOn)) - { - actsOnChannelIndex = doc.attributes().value(KXMLQLCFixtureModeChannelActsOn).toInt(); - } + actsOnChannelIndex = doc.attributes().value(KXMLQLCFixtureModeChannelActsOn).toUInt(); QLCChannel *currentChannel = m_fixtureDef->channel(doc.readElementText()); - ChannelActsOnData channelActsData(currentChannel, actsOnChannelIndex); + if (actsOnChannelIndex != QLCChannel::invalid()) + m_actsOnMap[str.toInt()] = actsOnChannelIndex; - listChannelsWithActsOnIndex.append(channelActsData); - - insertChannel(currentChannel, - str.toInt()); + insertChannel(currentChannel, str.toInt()); } else if (doc.name() == KXMLQLCFixtureHead) { @@ -418,19 +443,6 @@ bool QLCFixtureMode::loadXML(QXmlStreamReader &doc) } } - // Set acts on channels - - foreach (ChannelActsOnData channelSctsOnData, listChannelsWithActsOnIndex) - { - if(m_channels.contains(channelSctsOnData.channel) && - channelSctsOnData.actsOnIndex >= 0 && - m_channels.size() > channelSctsOnData.actsOnIndex) - { - m_actsOnChannelsList.insert(channelSctsOnData.channel, - m_channels.at(channelSctsOnData.actsOnIndex)); - } - } - // Cache all head channels cacheHeads(); @@ -454,18 +466,13 @@ bool QLCFixtureMode::saveXML(QXmlStreamWriter *doc) QVectorIterator it(m_channels); while (it.hasNext() == true) { - QLCChannel* channel = it.next(); + QLCChannel *channel = it.next(); + quint32 actsOnIndex = m_actsOnMap.value(i, QLCChannel::invalid()); doc->writeStartElement(KXMLQLCFixtureModeChannel); doc->writeAttribute(KXMLQLCFixtureModeChannelNumber, QString::number(i++)); - - if (m_actsOnChannelsList.contains(channel)) - { - QLCChannel *ChannelActsOn = m_actsOnChannelsList.value(channel); - if(ChannelActsOn != NULL){ - doc->writeAttribute(KXMLQLCFixtureModeChannelActsOn, QString::number(m_channels.indexOf(ChannelActsOn))); - } - } + if (actsOnIndex != QLCChannel::invalid()) + doc->writeAttribute(KXMLQLCFixtureModeChannelActsOn, QString::number(actsOnIndex)); doc->writeCharacters(channel->name()); doc->writeEndElement(); @@ -480,8 +487,3 @@ bool QLCFixtureMode::saveXML(QXmlStreamWriter *doc) return true; } - -QLCFixtureMode::ChannelActsOnData::ChannelActsOnData(QLCChannel *newChannel, int newAcsOnIndex) : - channel(newChannel), - actsOnIndex(newAcsOnIndex) -{} diff --git a/engine/src/qlcfixturemode.h b/engine/src/qlcfixturemode.h index bfb1ff60d8..54485f3e1a 100644 --- a/engine/src/qlcfixturemode.h +++ b/engine/src/qlcfixturemode.h @@ -206,31 +206,30 @@ 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; - /*! - * \brief The ChannelActsOnData struct - * - * Contains channel pointer and acts on channel index. - * - */ + /** Return the index of the primary channel $chIndex relates to. + * Return invalid if not present */ + quint32 primaryChannel(quint32 chIndex); - struct ChannelActsOnData - { - QLCChannel *channel; - int actsOnIndex; - - ChannelActsOnData(QLCChannel *newChannel, int newAcsOnIndex); - }; - - void updateActsOnChannel(QLCChannel *mainChannel, QLCChannel *actsOnChannel); + /** Return the channel index on which the given $chIndex acts on. + * Return invalid if not present */ + quint32 channelActsOn(quint32 chIndex); + void setChannelActsOn(quint32 chIndex, quint32 actsOnIndex); protected: /** List of channels (pointers are not owned) */ QVector m_channels; - /** List of acts on channels */ - QHash m_actsOnChannelsList; + /** Map of channel indices that act on other channels. + * 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 9cb952c967..db99554c19 100644 --- a/engine/src/qlcinputchannel.cpp +++ b/engine/src/qlcinputchannel.cpp @@ -24,7 +24,6 @@ #include #include "qlcinputchannel.h" -#include "qlcinputprofile.h" /**************************************************************************** * Initialization @@ -35,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) { } @@ -48,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; @@ -63,11 +64,16 @@ QLCInputChannel::~QLCInputChannel() void QLCInputChannel::setType(Type type) { + if (type == m_type) + return; + m_type = type; if (type == Encoder) m_movementSensitivity = 1; else m_movementSensitivity = 20; + + emit typeChanged(); } QLCInputChannel::Type QLCInputChannel::type() const @@ -95,7 +101,12 @@ QString QLCInputChannel::typeToString(Type type) return KXMLQLCInputChannelPageSet; default: return KXMLQLCInputChannelNone; - } + } +} + +QString QLCInputChannel::typeString() +{ + return typeToString(type()); } QLCInputChannel::Type QLCInputChannel::stringToType(const QString& type) @@ -133,17 +144,7 @@ QStringList QLCInputChannel::types() QIcon QLCInputChannel::typeToIcon(Type type) { - switch (type) - { - case Button: return QIcon(":/button.png"); - case Knob: return QIcon(":/knob.png"); - case Encoder: return QIcon(":/knob.png"); - case Slider: return QIcon(":/slider.png"); - case PrevPage: return QIcon(":/forward.png"); - case NextPage: return QIcon(":/back.png"); - case PageSet: return QIcon(":/star.png"); - default: return QIcon(); - } + return QIcon(iconResource(type)); } QIcon QLCInputChannel::stringToIcon(const QString& str) @@ -151,6 +152,26 @@ QIcon QLCInputChannel::stringToIcon(const QString& str) return typeToIcon(stringToType(str)); } +QString QLCInputChannel::iconResource(Type type, bool svg) +{ + QString prefix = svg ? "qrc" : ""; + QString ext = svg ? "svg" : "png"; + + switch(type) + { + case Button: return QString("%1:/button.%2").arg(prefix, ext); + case Knob: return QString("%1:/knob.%2").arg(prefix, ext); + case Encoder: return QString("%1:/knob.%2").arg(prefix, ext); + case Slider: return QString("%1:/slider.%2").arg(prefix, ext); + case PrevPage: return QString("%1:/forward.%2").arg(prefix, ext); + case NextPage: return QString("%1:/back.%2").arg(prefix, ext); + case PageSet: return QString("%1:/star.%2").arg(prefix, ext); + default: return QString(); + } + + return QString("%1:/other.%2").arg(prefix, ext); +} + QIcon QLCInputChannel::icon() const { return typeToIcon(type()); @@ -162,7 +183,12 @@ QIcon QLCInputChannel::icon() const void QLCInputChannel::setName(const QString& name) { + if (name == m_name) + return; + m_name = name; + + emit nameChanged(); } QString QLCInputChannel::name() const @@ -200,7 +226,11 @@ void QLCInputChannel::setMovementSensitivity(int value) void QLCInputChannel::setSendExtraPress(bool enable) { + if (enable == m_sendExtraPress) + return; + m_sendExtraPress = enable; + emit sendExtraPressChanged(); } bool QLCInputChannel::sendExtraPress() const @@ -210,18 +240,50 @@ bool QLCInputChannel::sendExtraPress() const void QLCInputChannel::setRange(uchar lower, uchar upper) { - m_lower = lower; - m_upper = upper; + setLowerValue(lower); + setUpperValue(upper); } uchar QLCInputChannel::lowerValue() const { - return m_lower; + return m_lowerValue; +} + +void QLCInputChannel::setLowerValue(const uchar value) +{ + if (value == m_lowerValue) + return; + + m_lowerValue = value; + emit lowerValueChanged(); } uchar QLCInputChannel::upperValue() const { - return m_upper; + return m_upperValue; +} + +void QLCInputChannel::setUpperValue(const uchar value) +{ + if (value == m_upperValue) + return; + + 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(); } /**************************************************************************** @@ -259,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 @@ -311,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 91ffb06594..88dad0bc73 100644 --- a/engine/src/qlcinputchannel.h +++ b/engine/src/qlcinputchannel.h @@ -21,6 +21,7 @@ #define QLCINPUTCHANNEL_H #include +#include class QXmlStreamWriter; class QXmlStreamReader; @@ -31,32 +32,41 @@ 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 { Q_OBJECT Q_DISABLE_COPY(QLCInputChannel) - Q_PROPERTY(Type type READ type CONSTANT) + Q_PROPERTY(Type type READ type WRITE setType NOTIFY typeChanged FINAL) + Q_PROPERTY(QString typeString READ typeString CONSTANT) + Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged FINAL) + + Q_PROPERTY(bool sendExtraPress READ sendExtraPress WRITE setSendExtraPress NOTIFY sendExtraPressChanged FINAL) + Q_PROPERTY(MovementType movementType READ movementType WRITE setMovementType NOTIFY movementTypeChanged FINAL) + Q_PROPERTY(int movementSensitivity READ movementSensitivity WRITE setMovementSensitivity NOTIFY movementSensitivityChanged FINAL) + Q_PROPERTY(uchar lowerValue READ lowerValue WRITE setLowerValue NOTIFY lowerValueChanged FINAL) + Q_PROPERTY(uchar upperValue READ upperValue WRITE setUpperValue NOTIFY upperValueChanged FINAL) /******************************************************************** * Initialization @@ -99,6 +109,7 @@ class QLCInputChannel : public QObject /** Convert the given QLCInputChannel::Type to a QString */ static QString typeToString(Type type); + QString typeString(); /** Convert the given QString to a QLCInputChannel::Type */ static Type stringToType(const QString& type); @@ -112,8 +123,13 @@ class QLCInputChannel : public QObject /** Get icon for a type */ static QIcon stringToIcon(const QString& str); + Q_INVOKABLE static QString iconResource(QLCInputChannel::Type type, bool svg = false); + QIcon icon() const; +signals: + void typeChanged(); + protected: Type m_type; @@ -127,6 +143,9 @@ class QLCInputChannel : public QObject /** Get the name of this channel */ QString name() const; +signals: + void nameChanged(); + protected: QString m_name; @@ -135,10 +154,14 @@ class QLCInputChannel : public QObject *********************************************************************/ public: /** Movement behaviour */ - enum MovementType { + enum MovementType + { Absolute = 0, Relative = 1 }; +#if QT_VERSION >= 0x050500 + Q_ENUM(MovementType) +#endif MovementType movementType() const; void setMovementType(MovementType type); @@ -146,6 +169,10 @@ class QLCInputChannel : public QObject int movementSensitivity() const; void setMovementSensitivity(int value); +signals: + void movementTypeChanged(); + void movementSensitivityChanged(); + protected: MovementType m_movementType; int m_movementSensitivity; @@ -156,13 +183,30 @@ class QLCInputChannel : public QObject public: void setSendExtraPress(bool enable); bool sendExtraPress() const; + void setRange(uchar lower, uchar upper); uchar lowerValue() const; + void setLowerValue(const uchar value); + 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 1511dce9ab..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 @@ -49,14 +52,46 @@ QLCInputProfile::QLCInputProfile() { } -QLCInputProfile::QLCInputProfile(const QLCInputProfile& profile) +QLCInputProfile::~QLCInputProfile() { - *this = profile; + destroyChannels(); } -QLCInputProfile::~QLCInputProfile() +QLCInputProfile *QLCInputProfile::createCopy() { - destroyChannels(); + QLCInputProfile *copy = new QLCInputProfile(); + copy->setManufacturer(this->manufacturer()); + copy->setModel(this->model()); + copy->setType(this->type()); + copy->setPath(this->path()); + copy->setMidiSendNoteOff(this->midiSendNoteOff()); + + /* Copy the other profile's channels */ + QMapIterator it(this->channels()); + while (it.hasNext() == true) + { + it.next(); + 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; } QLCInputProfile& QLCInputProfile::operator=(const QLCInputProfile& profile) @@ -75,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; @@ -115,6 +167,11 @@ QString QLCInputProfile::name() const return QString("%1 %2").arg(m_manufacturer).arg(m_model); } +void QLCInputProfile::setPath(QString path) +{ + m_path = path; +} + QString QLCInputProfile::path() const { return m_path; @@ -280,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 */ @@ -291,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 ****************************************************************************/ @@ -325,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) @@ -373,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; @@ -413,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 f58a0465fb..69c6aaa200 100644 --- a/engine/src/qlcinputprofile.h +++ b/engine/src/qlcinputprofile.h @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -39,9 +40,15 @@ 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 +class QLCInputProfile : public QObject { + Q_OBJECT + /******************************************************************** * Initialization ********************************************************************/ @@ -49,12 +56,11 @@ class QLCInputProfile /** Standard constructor */ QLCInputProfile(); - /** Copy constructor */ - QLCInputProfile(const QLCInputProfile& profile); - /** Destructor */ virtual ~QLCInputProfile(); + QLCInputProfile *createCopy(); + /** Assignment operator */ QLCInputProfile& operator=(const QLCInputProfile& profile); @@ -73,17 +79,21 @@ class QLCInputProfile /** Get the path where the profile is stored in. Don't use this as a unique ID since this varies between platforms. */ + void setPath(QString path); QString path() const; enum Type { - MIDI, + MIDI = 0, OS2L, OSC, HID, DMX, Enttec, }; +#if QT_VERSION >= 0x050500 + Q_ENUM(Type) +#endif void setType(Type type); @@ -159,7 +169,7 @@ class QLCInputProfile * @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. @@ -174,6 +184,12 @@ class QLCInputProfile */ 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(); @@ -183,6 +199,32 @@ class QLCInputProfile 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 ********************************************************************/ @@ -193,6 +235,12 @@ class QLCInputProfile /** 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 05b9a5432c..ad7eb151a2 100644 --- a/engine/src/qlcinputsource.cpp +++ b/engine/src/qlcinputsource.cpp @@ -24,7 +24,6 @@ #include #endif -#include "qlcinputchannel.h" #include "qlcinputsource.h" #include "qlcmacros.h" @@ -37,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) @@ -46,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) @@ -61,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() @@ -121,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; + } } /********************************************************************* @@ -211,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/qlcpalette.cpp b/engine/src/qlcpalette.cpp index cf7ebda7c8..14f4e85f3d 100644 --- a/engine/src/qlcpalette.cpp +++ b/engine/src/qlcpalette.cpp @@ -22,6 +22,7 @@ #include #include +#include "monitorproperties.h" #include "qlcpalette.h" #include "qlcchannel.h" #include "scenevalue.h" @@ -40,7 +41,7 @@ QLCPalette::QLCPalette(QLCPalette::PaletteType type, QObject *parent) , m_id(QLCPalette::invalidId()) , m_type(type) , m_fanningType(Flat) - , m_fanningLayout(LeftToRight) + , m_fanningLayout(XAscending) , m_fanningAmount(100) { } @@ -243,10 +244,30 @@ QList QLCPalette::valuesFromFixtures(Doc *doc, QList fixtur QList list; int fxCount = fixtures.count(); - // nomralized progress in [ 0.0, 1.0 ] range + // normalized progress in [ 0.0, 1.0 ] range qreal progress = 0.0; int intFanValue = fanningValue().toInt(); FanningType fType = fanningType(); + FanningLayout fLayout = fanningLayout(); + MonitorProperties *mProps = doc->monitorProperties(); + + // sort the fixtures list based on selected layout + std::sort(fixtures.begin(), fixtures.end(), + [fLayout, mProps](quint32 a, quint32 b) { + QVector3D posA = mProps->fixturePosition(a, 0, 0); + QVector3D posB = mProps->fixturePosition(b, 0, 0); + + switch(fLayout) + { + case XAscending: return posA.x() < posB.x(); + case XDescending: return posB.x() < posA.x(); + case YAscending: return posA.y() < posB.y(); + case YDescending: return posB.y() < posA.y(); + case ZAscending: return posA.z() < posB.z(); + case ZDescending: return posB.z() < posA.z(); + default: return false; + } + }); foreach (quint32 id, fixtures) { @@ -261,7 +282,8 @@ QList QLCPalette::valuesFromFixtures(Doc *doc, QList fixtur case Dimmer: { int dValue = value().toInt(); - quint32 intCh = fixture->masterIntensityChannel(); + quint32 intCh = fixture->type() == QLCFixtureDef::Dimmer ? + 0 : fixture->masterIntensityChannel(); if (intCh != QLCChannel::invalid()) { @@ -371,7 +393,7 @@ QList QLCPalette::valuesFromFixtures(Doc *doc, QList fixtur QList QLCPalette::valuesFromFixtureGroups(Doc *doc, QList groups) { - QList list; + QList fixturesList; foreach (quint32 id, groups) { @@ -379,10 +401,10 @@ QList QLCPalette::valuesFromFixtureGroups(Doc *doc, QList g if (group == NULL) continue; - list << valuesFromFixtures(doc, group->fixtureList()); + fixturesList.append(group->fixtureList()); } - return list; + return valuesFromFixtures(doc, fixturesList); } qreal QLCPalette::valueFactor(qreal progress) @@ -500,11 +522,15 @@ QString QLCPalette::fanningLayoutToString(QLCPalette::FanningLayout layout) { switch (layout) { - case LeftToRight: return "LeftToRight"; - case RightToLeft: return "RightToLeft"; - case TopToBottom: return "TopToBottom"; - case BottomToTop: return "BottomToTop"; - case Centered: return "Centered"; + case XAscending: return "XAscending"; + case XDescending: return "XDescending"; + case XCentered: return "XCentered"; + case YAscending: return "YAscending"; + case YDescending: return "YDescending"; + case YCentered: return "YCentered"; + case ZAscending: return "ZAscending"; + case ZDescending: return "ZDescending"; + case ZCentered: return "ZCentered"; } return ""; @@ -512,18 +538,26 @@ QString QLCPalette::fanningLayoutToString(QLCPalette::FanningLayout layout) QLCPalette::FanningLayout QLCPalette::stringToFanningLayout(const QString &str) { - if (str == "LeftToRight") - return LeftToRight; - else if (str == "RightToLeft") - return RightToLeft; - else if (str == "TopToBottom") - return TopToBottom; - else if (str == "BottomToTop") - return BottomToTop; - else if (str == "Centered") - return Centered; - - return LeftToRight; + if (str == "XAscending") + return XAscending; + else if (str == "XDescending") + return XDescending; + else if (str == "XCentered") + return XCentered; + else if (str == "YAscending") + return YAscending; + else if (str == "YDescending") + return YDescending; + else if (str == "YCentered") + return YCentered; + else if (str == "ZAscending") + return ZAscending; + else if (str == "ZDescending") + return ZDescending; + else if (str == "ZCentered") + return ZCentered; + + return XAscending; } int QLCPalette::fanningAmount() const diff --git a/engine/src/qlcpalette.h b/engine/src/qlcpalette.h index df72fcc868..1a86da31d8 100644 --- a/engine/src/qlcpalette.h +++ b/engine/src/qlcpalette.h @@ -164,11 +164,15 @@ class QLCPalette : public QObject enum FanningLayout { - LeftToRight, - RightToLeft, - TopToBottom, - BottomToTop, - Centered + XAscending, + XDescending, + XCentered, + YAscending, + YDescending, + YCentered, + ZAscending, + ZDescending, + ZCentered }; #if QT_VERSION >= 0x050500 Q_ENUM(FanningLayout) diff --git a/engine/src/rgbimage.cpp b/engine/src/rgbimage.cpp index 259f0e70cd..78d53b3c39 100644 --- a/engine/src/rgbimage.cpp +++ b/engine/src/rgbimage.cpp @@ -45,7 +45,7 @@ RGBImage::RGBImage(Doc * doc) } RGBImage::RGBImage(const RGBImage& i) - : RGBAlgorithm( i.doc()) + : RGBAlgorithm(i.doc()) , m_filename(i.filename()) , m_animatedSource(i.animatedSource()) , m_animationStyle(i.animationStyle()) diff --git a/engine/src/rgbmatrix.cpp b/engine/src/rgbmatrix.cpp index 7af1c822c9..fb63da39e5 100644 --- a/engine/src/rgbmatrix.cpp +++ b/engine/src/rgbmatrix.cpp @@ -26,14 +26,12 @@ #include #include +#include "rgbscriptscache.h" #include "qlcfixturehead.h" #include "fixturegroup.h" #include "genericfader.h" #include "fadechannel.h" #include "rgbmatrix.h" -#include "qlcmacros.h" -#include "rgbaudio.h" -#include "rgbscriptscache.h" #include "doc.h" #define KXMLQLCRGBMatrixStartColor QString("MonoColor") @@ -170,6 +168,7 @@ bool RGBMatrix::copyFrom(const Function* function) setAlgorithm(NULL); setStartColor(mtx->startColor()); setEndColor(mtx->endColor()); + setControlMode(mtx->controlMode()); return Function::copyFrom(function); } @@ -218,7 +217,7 @@ void RGBMatrix::setAlgorithm(RGBAlgorithm* algo) { RGBScript *script = static_cast (m_algorithm); QHashIterator it(m_properties); - while(it.hasNext()) + while (it.hasNext()) { it.next(); if (script->setProperty(it.key(), it.value()) == false) @@ -478,7 +477,7 @@ bool RGBMatrix::saveXML(QXmlStreamWriter *doc) /* Properties */ QHashIterator it(m_properties); - while(it.hasNext()) + while (it.hasNext()) { it.next(); doc->writeStartElement(KXMLQLCRGBMatrixProperty); @@ -533,7 +532,7 @@ void RGBMatrix::preRun(MasterTimer *timer) { RGBScript *script = static_cast (m_algorithm); QHashIterator it(m_properties); - while(it.hasNext()) + while (it.hasNext()) { it.next(); script->setProperty(it.key(), it.value()); diff --git a/engine/src/rgbplain.cpp b/engine/src/rgbplain.cpp index a9b972009f..83f4f97924 100644 --- a/engine/src/rgbplain.cpp +++ b/engine/src/rgbplain.cpp @@ -22,7 +22,6 @@ #include #include "rgbplain.h" -#include "audiocapture.h" #include "doc.h" RGBPlain::RGBPlain(Doc * doc) diff --git a/engine/src/rgbscript.cpp b/engine/src/rgbscript.cpp index 0a23270bba..7620fbd62f 100644 --- a/engine/src/rgbscript.cpp +++ b/engine/src/rgbscript.cpp @@ -36,8 +36,6 @@ #include "rgbscript.h" #include "rgbscriptscache.h" -#include "qlcconfig.h" -#include "qlcfile.h" QScriptEngine* RGBScript::s_engine = NULL; #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) @@ -63,7 +61,7 @@ RGBScript::RGBScript(const RGBScript& s) , m_apiVersion(0) { evaluate(); - foreach(RGBScriptProperty cap, s.m_properties) + foreach (RGBScriptProperty cap, s.m_properties) { setProperty(cap.m_name, s.property(cap.m_name)); } @@ -81,7 +79,7 @@ RGBScript &RGBScript::operator=(const RGBScript &s) m_contents = s.m_contents; m_apiVersion = s.m_apiVersion; evaluate(); - foreach(RGBScriptProperty cap, s.m_properties) + foreach (RGBScriptProperty cap, s.m_properties) { setProperty(cap.m_name, s.property(cap.m_name)); } @@ -213,6 +211,18 @@ void RGBScript::initEngine() Q_ASSERT(s_engine != NULL); } +void RGBScript::displayError(QScriptValue e, const QString& fileName) +{ + if (e.isError()) + { + QString msg("%1: Exception at line %2. Error: %3"); + qWarning() << msg.arg(fileName) + .arg(e.property("lineNumber").toInt32()) + .arg(e.toString()); + qDebug() << "Stack: " << e.property("stack").toString(); + } +} + /**************************************************************************** * Script API ****************************************************************************/ @@ -227,8 +237,16 @@ int RGBScript::rgbMapStepCount(const QSize& size) QScriptValueList args; args << size.width() << size.height(); QScriptValue value = m_rgbMapStepCount.call(QScriptValue(), args); - int ret = value.isNumber() ? value.toInteger() : -1; - return ret; + if (value.isError()) + { + displayError(value, m_fileName); + return -1; + } + else + { + int ret = value.isNumber() ? value.toInteger() : -1; + return ret; + } } void RGBScript::rgbMap(const QSize& size, uint rgb, int step, RGBMap &map) @@ -241,7 +259,11 @@ void RGBScript::rgbMap(const QSize& size, uint rgb, int step, RGBMap &map) QScriptValueList args; args << size.width() << size.height() << rgb << step; QScriptValue yarray = m_rgbMap.call(QScriptValue(), args); - if (yarray.isArray() == true) + + if (yarray.isError()) + displayError(yarray, m_fileName); + + if (yarray.isArray()) { int ylen = yarray.property("length").toInteger(); map.resize(ylen); @@ -342,15 +364,21 @@ QHash RGBScript::propertiesAsStrings() QMutexLocker engineLocker(s_engineMutex); QHash properties; - foreach(RGBScriptProperty cap, m_properties) + foreach (RGBScriptProperty cap, m_properties) { QScriptValue readMethod = m_script.property(cap.m_readMethod); if (readMethod.isFunction()) { QScriptValueList args; QScriptValue value = readMethod.call(QScriptValue(), args); - if (value.isValid()) + if (value.isError()) + { + displayError(value, m_fileName); + } + else if (value.isValid()) + { properties.insert(cap.m_name, value.toString()); + } } } return properties; @@ -360,7 +388,7 @@ bool RGBScript::setProperty(QString propertyName, QString value) { QMutexLocker engineLocker(s_engineMutex); - foreach(RGBScriptProperty cap, m_properties) + foreach (RGBScriptProperty cap, m_properties) { if (cap.m_name == propertyName) { @@ -372,8 +400,16 @@ bool RGBScript::setProperty(QString propertyName, QString value) } QScriptValueList args; args << value; - writeMethod.call(QScriptValue(), args); - return true; + QScriptValue written = writeMethod.call(QScriptValue(), args); + if (written.isError()) + { + displayError(written, m_fileName); + return false; + } + else + { + return true; + } } } return false; @@ -383,7 +419,7 @@ QString RGBScript::property(QString propertyName) const { QMutexLocker engineLocker(s_engineMutex); - foreach(RGBScriptProperty cap, m_properties) + foreach (RGBScriptProperty cap, m_properties) { if (cap.m_name == propertyName) { @@ -395,10 +431,19 @@ QString RGBScript::property(QString propertyName) const } QScriptValueList args; QScriptValue value = readMethod.call(QScriptValue(), args); - if (value.isValid()) + if (value.isError()) + { + displayError(value, m_fileName); + return QString(); + } + else if (value.isValid()) + { return value.toString(); + } else + { return QString(); + } } } return QString(); @@ -429,7 +474,7 @@ bool RGBScript::loadProperties() RGBScriptProperty newCap; QStringList propsList = cap.split("|"); - foreach(QString prop, propsList) + foreach (QString prop, propsList) { QStringList keyValue = prop.split(":"); if (keyValue.length() < 2) @@ -446,7 +491,7 @@ bool RGBScript::loadProperties() else if (key == "type") { if (value == "list") newCap.m_type = RGBScriptProperty::List; - else if (value == "integer") newCap.m_type = RGBScriptProperty::Integer; + else if (value == "float") newCap.m_type = RGBScriptProperty::Float; else if (value == "range") newCap.m_type = RGBScriptProperty::Range; else if (value == "string") newCap.m_type = RGBScriptProperty::String; } diff --git a/engine/src/rgbscript.h b/engine/src/rgbscript.h index 2d2bcdebbb..6ee168b43f 100644 --- a/engine/src/rgbscript.h +++ b/engine/src/rgbscript.h @@ -80,6 +80,9 @@ class RGBScript : public RGBAlgorithm /** Init engine, engine mutex, and scripts map */ static void initEngine(); + /** Handle an error after evaluate() or call() of a script */ + static void displayError(QScriptValue e, const QString& fileName); + /************************************************************************ * RGBAlgorithm API ************************************************************************/ diff --git a/engine/src/rgbscriptproperty.h b/engine/src/rgbscriptproperty.h index c604be3844..0e7a79211e 100644 --- a/engine/src/rgbscriptproperty.h +++ b/engine/src/rgbscriptproperty.h @@ -44,7 +44,7 @@ class RGBScriptProperty None, List, Range, - Integer, + Float, String }; diff --git a/engine/src/rgbscriptv4.cpp b/engine/src/rgbscriptv4.cpp index 7b9908b9de..c703b36e8a 100644 --- a/engine/src/rgbscriptv4.cpp +++ b/engine/src/rgbscriptv4.cpp @@ -53,7 +53,7 @@ RGBScript::RGBScript(const RGBScript& s) , m_apiVersion(0) { evaluate(); - foreach(RGBScriptProperty cap, s.m_properties) + foreach (RGBScriptProperty cap, s.m_properties) { setProperty(cap.m_name, s.property(cap.m_name)); } @@ -71,7 +71,7 @@ RGBScript &RGBScript::operator=(const RGBScript &s) m_contents = s.m_contents; m_apiVersion = s.m_apiVersion; evaluate(); - foreach(RGBScriptProperty cap, s.m_properties) + foreach (RGBScriptProperty cap, s.m_properties) { setProperty(cap.m_name, s.property(cap.m_name)); } @@ -148,11 +148,7 @@ bool RGBScript::evaluate() m_script = s_engine->evaluate(m_contents, m_fileName); if (m_script.isError()) { - QString msg("%1: Uncaught exception at line %2. Error: %3"); - qWarning() << msg.arg(m_fileName) - .arg(m_script.property("lineNumber").toInt()) - .arg(m_script.toString()); - qDebug() << "Stack: " << m_script.property("stack").toString(); + displayError(m_script, m_fileName); return false; } @@ -199,6 +195,18 @@ void RGBScript::initEngine() Q_ASSERT(s_engine != NULL); } +void RGBScript::displayError(QJSValue e, const QString& fileName) +{ + if (e.isError()) + { + QString msg("%1: Exception at line %2. Error: %3"); + qWarning() << msg.arg(fileName) + .arg(e.property("lineNumber").toInt()) + .arg(e.toString()); + qDebug() << "Stack: " << e.property("stack").toString(); + } +} + /**************************************************************************** * Script API ****************************************************************************/ @@ -213,8 +221,16 @@ int RGBScript::rgbMapStepCount(const QSize& size) QJSValueList args; args << size.width() << size.height(); QJSValue value = m_rgbMapStepCount.call(args); - int ret = value.isNumber() ? value.toInt() : -1; - return ret; + if (value.isError()) + { + displayError(value, m_fileName); + return -1; + } + else + { + int ret = value.isNumber() ? value.toInt() : -1; + return ret; + } } void RGBScript::rgbMap(const QSize& size, uint rgb, int step, RGBMap &map) @@ -227,8 +243,10 @@ void RGBScript::rgbMap(const QSize& size, uint rgb, int step, RGBMap &map) QJSValueList args; args << size.width() << size.height() << rgb << step; QJSValue yarray(m_rgbMap.call(args)); + if (yarray.isError()) + displayError(yarray, m_fileName); - if (yarray.isArray() == true) + if (yarray.isArray()) { QVariantList yvArray = yarray.toVariant().toList(); int ylen = yvArray.length(); @@ -329,14 +347,16 @@ QHash RGBScript::propertiesAsStrings() QMutexLocker engineLocker(s_engineMutex); QHash properties; - foreach(RGBScriptProperty cap, m_properties) + foreach (RGBScriptProperty cap, m_properties) { QJSValue readMethod = m_script.property(cap.m_readMethod); if (readMethod.isCallable()) { QJSValueList args; QJSValue value = readMethod.call(args); - if (!value.isUndefined()) + if (value.isError()) + displayError(value, m_fileName); + else if (!value.isUndefined()) properties.insert(cap.m_name, value.toString()); } } @@ -347,7 +367,7 @@ bool RGBScript::setProperty(QString propertyName, QString value) { QMutexLocker engineLocker(s_engineMutex); - foreach(RGBScriptProperty cap, m_properties) + foreach (RGBScriptProperty cap, m_properties) { if (cap.m_name == propertyName) { @@ -359,8 +379,16 @@ bool RGBScript::setProperty(QString propertyName, QString value) } QJSValueList args; args << value; - writeMethod.call(args); - return true; + QJSValue written = writeMethod.call(args); + if (written.isError()) + { + displayError(written, m_fileName); + return false; + } + else + { + return true; + } } } return false; @@ -370,7 +398,7 @@ QString RGBScript::property(QString propertyName) const { QMutexLocker engineLocker(s_engineMutex); - foreach(RGBScriptProperty cap, m_properties) + foreach (RGBScriptProperty cap, m_properties) { if (cap.m_name == propertyName) { @@ -382,10 +410,19 @@ QString RGBScript::property(QString propertyName) const } QJSValueList args; QJSValue value = readMethod.call(args); - if (!value.isUndefined()) + if (value.isError()) + { + displayError(value, m_fileName); + return QString(); + } + else if (!value.isUndefined()) + { return value.toString(); + } else + { return QString(); + } } } return QString(); @@ -416,7 +453,7 @@ bool RGBScript::loadProperties() RGBScriptProperty newCap; QStringList propsList = cap.split("|"); - foreach(QString prop, propsList) + foreach (QString prop, propsList) { QStringList keyValue = prop.split(":"); if (keyValue.length() < 2) @@ -433,7 +470,7 @@ bool RGBScript::loadProperties() else if (key == "type") { if (value == "list") newCap.m_type = RGBScriptProperty::List; - else if (value == "integer") newCap.m_type = RGBScriptProperty::Integer; + else if (value == "float") newCap.m_type = RGBScriptProperty::Float; else if (value == "range") newCap.m_type = RGBScriptProperty::Range; else if (value == "string") newCap.m_type = RGBScriptProperty::String; } diff --git a/engine/src/rgbscriptv4.h b/engine/src/rgbscriptv4.h index 6f00f71473..3a89ce93b1 100644 --- a/engine/src/rgbscriptv4.h +++ b/engine/src/rgbscriptv4.h @@ -71,6 +71,9 @@ class RGBScript : public RGBAlgorithm /** Init engine, engine mutex, and scripts map */ static void initEngine(); + /** Handle an error after evaluate() or call() of a script */ + static void displayError(QJSValue e, const QString& fileName); + private: static QJSEngine* s_engine; //! The engine that runs all scripts #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) diff --git a/engine/src/scene.cpp b/engine/src/scene.cpp index d9d352cdc0..25f6350554 100644 --- a/engine/src/scene.cpp +++ b/engine/src/scene.cpp @@ -25,8 +25,6 @@ #include #include "qlcfixturedef.h" -#include "qlcmacros.h" -#include "qlcfile.h" #include "qlccapability.h" #include "genericfader.h" @@ -43,6 +41,8 @@ Scene::Scene(Doc* doc) : Function(doc, Function::SceneType) , m_legacyFadeBus(Bus::invalid()) + , m_flashOverrides(false) + , m_flashForceLTP(false) , m_blendFunctionID(Function::invalidId()) { setName(tr("New Scene")); @@ -161,7 +161,7 @@ void Scene::setValue(const SceneValue& scv, bool blind, bool checkHTP) m_fadersMap[universe]->add(fc); } } - } + } } emit changed(this->id()); @@ -206,7 +206,7 @@ QList Scene::components() { QList ids; - foreach(SceneValue scv, m_values.keys()) + foreach (SceneValue scv, m_values.keys()) { if (ids.contains(scv.fxi) == false) ids.append(scv.fxi); @@ -222,7 +222,7 @@ QColor Scene::colorValue(quint32 fxi) bool found = false; QColor CMYcol; - foreach(SceneValue scv, m_values.keys()) + foreach (SceneValue scv, m_values.keys()) { if (fxi != Fixture::invalidId() && fxi != scv.fxi) continue; @@ -540,7 +540,7 @@ bool Scene::loadXML(QXmlStreamReader &root) if (chGrpIDs.isEmpty() == false) { QStringList grpArray = chGrpIDs.split(","); - foreach(QString grp, grpArray) + foreach (QString grp, grpArray) { m_channelGroups.append(grp.toUInt()); m_channelGroupsLevels.append(0); @@ -634,13 +634,16 @@ void Scene::postLoad() * Flashing ****************************************************************************/ -void Scene::flash(MasterTimer *timer) +void Scene::flash(MasterTimer *timer, bool shouldOverride, bool forceLTP) { if (flashing() == true) return; + m_flashOverrides = shouldOverride; + m_flashForceLTP = forceLTP; + Q_ASSERT(timer != NULL); - Function::flash(timer); + Function::flash(timer, shouldOverride, forceLTP); timer->registerDMXSource(this); } @@ -673,7 +676,8 @@ void Scene::writeDMX(MasterTimer *timer, QList ua) QSharedPointer fader = m_fadersMap.value(universe, QSharedPointer()); if (fader.isNull()) { - fader = ua[universe]->requestFader(); + fader = ua[universe]->requestFader(m_flashOverrides ? Universe::Flashing : Universe::Auto); + fader->adjustIntensity(getAttributeValue(Intensity)); fader->setBlendMode(blendMode()); fader->setName(name()); @@ -681,6 +685,8 @@ void Scene::writeDMX(MasterTimer *timer, QList ua) m_fadersMap[universe] = fader; } + if (m_flashForceLTP) + fc.addFlag(FadeChannel::ForceLTP); fc.setTarget(sv.value); fc.addFlag(FadeChannel::Flashing); fader->add(fc); @@ -717,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 @@ -733,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/scene.h b/engine/src/scene.h index 1b979b9c0c..203871dec8 100644 --- a/engine/src/scene.h +++ b/engine/src/scene.h @@ -245,7 +245,7 @@ public slots: *********************************************************************/ public: /** @reimp */ - void flash(MasterTimer *timer); + void flash(MasterTimer *timer, bool shouldOverride, bool forceLTP); /** @reimp */ void unFlash(MasterTimer *timer); @@ -253,6 +253,10 @@ public slots: /** @reimp from DMXSource */ void writeDMX(MasterTimer *timer, QList ua); +private: + bool m_flashOverrides; + bool m_flashForceLTP; + /********************************************************************* * Running *********************************************************************/ diff --git a/engine/src/script.cpp b/engine/src/script.cpp index 819fbbc54a..dc22b636ee 100644 --- a/engine/src/script.cpp +++ b/engine/src/script.cpp @@ -32,7 +32,6 @@ #include "genericfader.h" #include "fadechannel.h" #include "mastertimer.h" -#include "qlcmacros.h" #include "universe.h" #include "script.h" #include "doc.h" @@ -308,7 +307,7 @@ bool Script::saveXML(QXmlStreamWriter *doc) saveXMLRunOrder(doc); /* Contents */ - foreach(QString cmd, dataLines()) + foreach (QString cmd, dataLines()) { doc->writeTextElement(KXMLQLCScriptCommand, QUrl::toPercentEncoding(cmd)); } @@ -707,7 +706,13 @@ QString Script::handleSystemCommand(const QList &tokens) programArgs << tokens[i][1]; #if !defined(Q_OS_IOS) QProcess *newProcess = new QProcess(); - newProcess->start(programName, programArgs); + + // startDetached() enables to delete QProcess object without killing actual process + qint64 pid; + newProcess->setProgram(programName); + newProcess->setArguments(programArgs); + newProcess->startDetached(&pid); + delete newProcess; #endif return QString(); } diff --git a/engine/src/scriptrunner.cpp b/engine/src/scriptrunner.cpp index d3a3d6f1b7..d41260367e 100644 --- a/engine/src/scriptrunner.cpp +++ b/engine/src/scriptrunner.cpp @@ -17,6 +17,7 @@ limitations under the License. */ +#include #include #include #include @@ -61,14 +62,13 @@ void ScriptRunner::stop() if (m_running == false) return; - m_running = false; -/* if (m_engine) { - delete m_engine; + m_engine->setInterrupted(true); + m_engine->deleteLater(); m_engine = NULL; } -*/ + // Stop all functions started by this script foreach (quint32 fID, m_startedFunctions) { @@ -87,6 +87,8 @@ void ScriptRunner::stop() fader->requestDelete(); } m_fadersMap.clear(); + + m_running = false; } QStringList ScriptRunner::collectScriptData() @@ -95,6 +97,7 @@ QStringList ScriptRunner::collectScriptData() QJSEngine *engine = new QJSEngine(); QJSValue objectValue = engine->newQObject(this); engine->globalObject().setProperty("Engine", objectValue); + QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); QJSValue script = engine->evaluate("(function run() { " + m_content + " })"); if (script.isError()) @@ -210,6 +213,7 @@ void ScriptRunner::run() m_engine = new QJSEngine(); QJSValue objectValue = m_engine->newQObject(this); m_engine->globalObject().setProperty("Engine", objectValue); + QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); QJSValue script = m_engine->evaluate("(function run() { " + m_content + " })"); @@ -228,16 +232,12 @@ void ScriptRunner::run() .arg(ret.toString()); } } -/* - if (m_engine) - { - delete m_engine; - m_engine = NULL; - } -*/ - qDebug() << "ScriptRunner thread over."; - // this thread is done. The calling Script can stop - m_running = false; + + qDebug() << "[ScriptRunner] Code executed"; + + // this thread is done. Wait for the calling Script to stop + while (m_running) + msleep(50); } /************************************************************************ @@ -454,8 +454,11 @@ bool ScriptRunner::systemCommand(QString command) } #if !defined(Q_OS_IOS) + qint64 pid; QProcess *newProcess = new QProcess(); - newProcess->start(programName, programArgs); + newProcess->setProgram(programName); + newProcess->setArguments(programArgs); + newProcess->startDetached(&pid); #endif return true; @@ -514,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/scriptv4.cpp b/engine/src/scriptv4.cpp index d72fe43056..c85458c8c8 100644 --- a/engine/src/scriptv4.cpp +++ b/engine/src/scriptv4.cpp @@ -306,7 +306,7 @@ bool Script::saveXML(QXmlStreamWriter *doc) saveXMLRunOrder(doc); /* Contents */ - foreach(QString cmd, dataLines()) + foreach (QString cmd, dataLines()) { doc->writeTextElement(KXMLQLCScriptCommand, QUrl::toPercentEncoding(cmd)); } diff --git a/engine/src/show.cpp b/engine/src/show.cpp index afd53aeefc..c14bd652b9 100644 --- a/engine/src/show.cpp +++ b/engine/src/show.cpp @@ -24,12 +24,8 @@ #include #include -#include "qlcfile.h" -#include "qlcmacros.h" - #include "showrunner.h" #include "function.h" -#include "chaser.h" #include "show.h" #include "doc.h" @@ -42,10 +38,11 @@ *****************************************************************************/ Show::Show(Doc* doc) : Function(doc, Function::ShowType) - , m_timeDivType(QString("Time")) - , m_timeDivBPM(120) - , m_latestTrackId(0) - , m_runner(NULL) + , m_timeDivisionType(Time) + , m_timeDivisionBPM(120) + , m_latestTrackId(0) + , m_latestShowFunctionID(0) + , m_runner(NULL) { setName(tr("New Show")); @@ -68,9 +65,9 @@ quint32 Show::totalDuration() { quint32 totalDuration = 0; - foreach(Track *track, tracks()) + foreach (Track *track, tracks()) { - foreach(ShowFunction *sf, track->showFunctions()) + foreach (ShowFunction *sf, track->showFunctions()) { if (sf->startTime() + sf->duration(doc()) > totalDuration) totalDuration = sf->startTime() + sf->duration(doc()); @@ -109,20 +106,20 @@ bool Show::copyFrom(const Function* function) if (show == NULL) return false; - m_timeDivType = show->m_timeDivType; - m_timeDivBPM = show->m_timeDivBPM; + m_timeDivisionType = show->m_timeDivisionType; + m_timeDivisionBPM = show->m_timeDivisionBPM; m_latestTrackId = show->m_latestTrackId; // create a copy of each track - foreach(Track *track, show->tracks()) + foreach (Track *track, show->tracks()) { quint32 sceneID = track->getSceneID(); - Track* newTrack = new Track(sceneID); + Track* newTrack = new Track(sceneID, this); newTrack->setName(track->name()); addTrack(newTrack); // create a copy of each sequence/audio in a track - foreach(ShowFunction *sfunc, track->showFunctions()) + foreach (ShowFunction *sfunc, track->showFunctions()) { Function* function = doc()->function(sfunc->functionID()); if (function == NULL) @@ -149,21 +146,71 @@ bool Show::copyFrom(const Function* function) * Time division *********************************************************************/ -void Show::setTimeDivision(QString type, int BPM) +void Show::setTimeDivision(Show::TimeDivision type, int BPM) { qDebug() << "[setTimeDivision] type:" << type << ", BPM:" << BPM; - m_timeDivType = type; - m_timeDivBPM = BPM; + m_timeDivisionType = type; + m_timeDivisionBPM = BPM; +} + +Show::TimeDivision Show::timeDivisionType() +{ + return m_timeDivisionType; +} + +int Show::beatsDivision() +{ + switch(m_timeDivisionType) + { + case BPM_2_4: return 2; + case BPM_3_4: return 3; + case BPM_4_4: return 4; + default: return 0; + } +} + +void Show::setTimeDivisionType(TimeDivision type) +{ + m_timeDivisionType = type; +} + +int Show::timeDivisionBPM() +{ + return m_timeDivisionBPM; } -QString Show::getTimeDivisionType() +void Show::setTimeDivisionBPM(int BPM) { - return m_timeDivType; + m_timeDivisionBPM = BPM; +} + +QString Show::tempoToString(Show::TimeDivision type) +{ + switch(type) + { + case Time: return QString("Time"); break; + case BPM_4_4: return QString("BPM_4_4"); break; + case BPM_3_4: return QString("BPM_3_4"); break; + case BPM_2_4: return QString("BPM_2_4"); break; + case Invalid: + default: + return QString("Invalid"); break; + } + return QString(); } -int Show::getTimeDivisionBPM() +Show::TimeDivision Show::stringToTempo(QString tempo) { - return m_timeDivBPM; + if (tempo == "Time") + return Time; + else if (tempo == "BPM_4_4") + return BPM_4_4; + else if (tempo == "BPM_3_4") + return BPM_3_4; + else if (tempo == "BPM_2_4") + return BPM_2_4; + else + return Invalid; } /***************************************************************************** @@ -218,7 +265,7 @@ Track* Show::track(quint32 id) const Track* Show::getTrackFromSceneID(quint32 id) { - foreach(Track *track, m_tracks) + foreach (Track *track, m_tracks) { if (track->getSceneID() == id) return track; @@ -226,6 +273,15 @@ Track* Show::getTrackFromSceneID(quint32 id) return NULL; } +Track *Show::getTrackFromShowFunctionID(quint32 id) +{ + foreach (Track *track, m_tracks) + if (track->showFunction(id) != NULL) + return track; + + return NULL; +} + int Show::getTracksCount() { return m_tracks.size(); @@ -244,7 +300,7 @@ void Show::moveTrack(Track *track, int direction) qint32 swapID = -1; if (direction > 0) swapID = INT_MAX; - foreach(quint32 id, m_tracks.keys()) + foreach (quint32 id, m_tracks.keys()) { qint32 signedID = (qint32)id; if (signedID > maxID) maxID = signedID; @@ -281,6 +337,27 @@ quint32 Show::createTrackId() return m_latestTrackId; } +/********************************************************************* + * Show Functions + *********************************************************************/ + +quint32 Show::getLatestShowFunctionId() +{ + return m_latestTrackId++; +} + +ShowFunction *Show::showFunction(quint32 id) +{ + foreach (Track *track, m_tracks) + { + ShowFunction *sf = track->showFunction(id); + if (sf != NULL) + return sf; + } + + return NULL; +} + /***************************************************************************** * Load & Save *****************************************************************************/ @@ -296,11 +373,11 @@ bool Show::saveXML(QXmlStreamWriter *doc) saveXMLCommon(doc); doc->writeStartElement(KXMLQLCShowTimeDivision); - doc->writeAttribute(KXMLQLCShowTimeType, m_timeDivType); - doc->writeAttribute(KXMLQLCShowTimeBPM, QString::number(m_timeDivBPM)); + doc->writeAttribute(KXMLQLCShowTimeType, tempoToString(m_timeDivisionType)); + doc->writeAttribute(KXMLQLCShowTimeBPM, QString::number(m_timeDivisionBPM)); doc->writeEndElement(); - foreach(Track *track, m_tracks) + foreach (Track *track, m_tracks) track->saveXML(doc); /* End the tag */ @@ -330,12 +407,12 @@ bool Show::loadXML(QXmlStreamReader &root) { QString type = root.attributes().value(KXMLQLCShowTimeType).toString(); int bpm = root.attributes().value(KXMLQLCShowTimeBPM).toString().toInt(); - setTimeDivision(type, bpm); + setTimeDivision(stringToTempo(type), bpm); root.skipCurrentElement(); } else if (root.name() == KXMLQLCTrack) { - Track *trk = new Track(); + Track *trk = new Track(Function::invalidId(), this); if (trk->loadXML(root) == true) addTrack(trk, trk->id()); } @@ -401,7 +478,7 @@ void Show::preRun(MasterTimer* timer) m_runner = new ShowRunner(doc(), this->id(), elapsed()); int i = 0; - foreach(Track *track, m_tracks.values()) + foreach (Track *track, m_tracks.values()) m_runner->adjustIntensity(getAttributeValue(i++), track); connect(m_runner, SIGNAL(timeChanged(quint32)), this, SIGNAL(timeChanged(quint32))); @@ -419,12 +496,11 @@ void Show::setPause(bool enable) void Show::write(MasterTimer* timer, QList universes) { Q_UNUSED(universes); - Q_UNUSED(timer); if (isPaused()) return; - m_runner->write(); + m_runner->write(timer); } void Show::postRun(MasterTimer* timer, QList universes) diff --git a/engine/src/show.h b/engine/src/show.h index 76509c7ab2..c4699a26bc 100644 --- a/engine/src/show.h +++ b/engine/src/show.h @@ -65,15 +65,33 @@ class Show : public Function /********************************************************************* * Time division *********************************************************************/ +public: + enum TimeDivision + { + Time = 0, + BPM_4_4, + BPM_3_4, + BPM_2_4, + Invalid + }; + Q_ENUM(TimeDivision) + /** Set the show time division type (Time, BPM) */ - void setTimeDivision(QString type, int BPM); + void setTimeDivision(Show::TimeDivision type, int BPM); + + Show::TimeDivision timeDivisionType(); + void setTimeDivisionType(Show::TimeDivision type); + int beatsDivision(); - QString getTimeDivisionType(); - int getTimeDivisionBPM(); + int timeDivisionBPM(); + void setTimeDivisionBPM(int BPM); + + static QString tempoToString(Show::TimeDivision type); + static Show::TimeDivision stringToTempo(QString tempo); private: - QString m_timeDivType; - int m_timeDivBPM; + TimeDivision m_timeDivisionType; + int m_timeDivisionBPM; /********************************************************************* * Tracks @@ -98,10 +116,13 @@ class Show : public Function bool removeTrack(quint32 id); /** Get a track by id */ - Track* track(quint32 id) const; + Track *track(quint32 id) const; + + /** Get a reference to a Track from the provided Scene ID */ + Track *getTrackFromSceneID(quint32 id); - /** Get pointer to a Track from a Scene ID */ - Track* getTrackFromSceneID(quint32 id); + /** Get a reference to a Track from the provided ShowFunction ID */ + Track *getTrackFromShowFunctionID(quint32 id); /** Get the number of tracks in the Show */ int getTracksCount(); @@ -123,6 +144,20 @@ class Show : public Function /** Latest assigned track ID */ quint32 m_latestTrackId; + /********************************************************************* + * Show Functions + *********************************************************************/ +public: + /** Get a unique ID for the creation of a new ShowFunction */ + quint32 getLatestShowFunctionId(); + + /** Get a reference to a ShowFunction from the provided uinique ID */ + ShowFunction *showFunction(quint32 id); + +protected: + /** Latest assigned unique ShowFunction ID */ + quint32 m_latestShowFunctionID; + /********************************************************************* * Save & Load *********************************************************************/ diff --git a/engine/src/showfunction.cpp b/engine/src/showfunction.cpp index 03aae6baba..e01f3273ff 100644 --- a/engine/src/showfunction.cpp +++ b/engine/src/showfunction.cpp @@ -31,9 +31,10 @@ #define KXMLShowFunctionColor "Color" #define KXMLShowFunctionLocked "Locked" -ShowFunction::ShowFunction(QObject *parent) +ShowFunction::ShowFunction(quint32 id, QObject *parent) : QObject(parent) - , m_id(Function::invalidId()) + , m_id(id) + , m_functionId(Function::invalidId()) , m_startTime(UINT_MAX) , m_duration(0) , m_color(QColor()) @@ -42,18 +43,23 @@ ShowFunction::ShowFunction(QObject *parent) { } +quint32 ShowFunction::id() const +{ + return m_id; +} + void ShowFunction::setFunctionID(quint32 id) { - if (id == m_id) + if (id == m_functionId) return; - m_id = id; + m_functionId = id; emit functionIDChanged(); } quint32 ShowFunction::functionID() const { - return m_id; + return m_functionId; } void ShowFunction::setStartTime(quint32 time) @@ -92,7 +98,7 @@ quint32 ShowFunction::duration(const Doc *doc) const if (doc == NULL) return 0; - Function *f = doc->function(m_id); + Function *f = doc->function(m_functionId); if (f == NULL) return 0; @@ -180,7 +186,7 @@ bool ShowFunction::loadXML(QXmlStreamReader &root) return true; } -bool ShowFunction::saveXML(QXmlStreamWriter *doc) const +bool ShowFunction::saveXML(QXmlStreamWriter *doc, quint32 trackId) const { Q_ASSERT(doc != NULL); @@ -188,6 +194,11 @@ bool ShowFunction::saveXML(QXmlStreamWriter *doc) const doc->writeStartElement(KXMLShowFunction); /* Attributes */ + if (trackId != UINT_MAX) + { + doc->writeAttribute(KXMLShowFunctionUid, QString::number(m_id)); + doc->writeAttribute(KXMLShowFunctionTrackId, QString::number(trackId)); + } doc->writeAttribute(KXMLShowFunctionID, QString::number(functionID())); doc->writeAttribute(KXMLShowFunctionStartTime, QString::number(startTime())); if (m_duration) diff --git a/engine/src/showfunction.h b/engine/src/showfunction.h index db2e76e5c2..f6e6baf457 100644 --- a/engine/src/showfunction.h +++ b/engine/src/showfunction.h @@ -32,12 +32,15 @@ class Doc; */ #define KXMLShowFunction QString("ShowFunction") +#define KXMLShowFunctionUid QString("UID") +#define KXMLShowFunctionTrackId QString("TrackID") class ShowFunction: public QObject { Q_OBJECT Q_DISABLE_COPY(ShowFunction) + Q_PROPERTY(int id READ id CONSTANT) Q_PROPERTY(int functionID READ functionID WRITE setFunctionID NOTIFY functionIDChanged) Q_PROPERTY(int startTime READ startTime WRITE setStartTime NOTIFY startTimeChanged) Q_PROPERTY(int duration READ duration WRITE setDuration NOTIFY durationChanged) @@ -45,9 +48,12 @@ class ShowFunction: public QObject Q_PROPERTY(bool locked READ isLocked WRITE setLocked NOTIFY lockedChanged) public: - ShowFunction(QObject *parent = 0); + ShowFunction(quint32 id, QObject *parent = 0); virtual ~ShowFunction() {} + /** Get the ShowFunction unique identifier in a Show */ + quint32 id() const; + /** Get/Set the Function ID this class represents */ void setFunctionID(quint32 id); quint32 functionID() const; @@ -85,9 +91,12 @@ class ShowFunction: public QObject void lockedChanged(); private: - /** ID of the QLC+ Function this class represents */ + /** ID of this class to uniquely identify it within a Show */ quint32 m_id; + /** ID of the QLC+ Function this class represents */ + quint32 m_functionId; + /** Start time of the Function in milliseconds */ quint32 m_startTime; @@ -112,7 +121,7 @@ class ShowFunction: public QObject bool loadXML(QXmlStreamReader &root); /** Save ShowFunction contents to $doc */ - bool saveXML(QXmlStreamWriter *doc) const; + bool saveXML(QXmlStreamWriter *doc, quint32 trackId = UINT_MAX) const; }; /** @} */ diff --git a/engine/src/showrunner.cpp b/engine/src/showrunner.cpp index 13df0a22f3..ffe2eb1368 100644 --- a/engine/src/showrunner.cpp +++ b/engine/src/showrunner.cpp @@ -21,13 +21,8 @@ #include #include "showrunner.h" -#include "chaserstep.h" #include "function.h" -#include "chaser.h" #include "track.h" -#include "scene.h" -#include "audio.h" -#include "video.h" #include "show.h" #define TIMER_INTERVAL 50 @@ -42,9 +37,12 @@ static bool compareShowFunctions(const ShowFunction *sf1, const ShowFunction *sf ShowRunner::ShowRunner(const Doc* doc, quint32 showID, quint32 startTime) : QObject(NULL) , m_doc(doc) + , m_currentTimeFunctionIndex(0) , m_elapsedTime(startTime) + , m_currentBeatFunctionIndex(0) + , m_elapsedBeats(0) + , beatSynced(false) , m_totalRunTime(0) - , m_currentFunctionIndex(0) { Q_ASSERT(m_doc != NULL); Q_ASSERT(showID != Show::invalidId()); @@ -53,7 +51,7 @@ ShowRunner::ShowRunner(const Doc* doc, quint32 showID, quint32 startTime) if (m_show == NULL) return; - foreach(Track *track, m_show->tracks()) + foreach (Track *track, m_show->tracks()) { // some sanity checks if (track == NULL || @@ -64,7 +62,7 @@ ShowRunner::ShowRunner(const Doc* doc, quint32 showID, quint32 startTime) continue; // get all the functions of the track and append them to the runner queue - foreach(ShowFunction *sfunc, track->showFunctions()) + foreach (ShowFunction *sfunc, track->showFunctions()) { if (sfunc->startTime() + sfunc->duration(m_doc) <= startTime) continue; @@ -73,7 +71,10 @@ ShowRunner::ShowRunner(const Doc* doc, quint32 showID, quint32 startTime) if (f == NULL) continue; - m_functions.append(sfunc); + if (f->tempoType() == Function::Time) + m_timeFunctions.append(sfunc); + else + m_beatFunctions.append(sfunc); if (sfunc->startTime() + sfunc->duration(m_doc) > m_totalRunTime) m_totalRunTime = sfunc->startTime() + sfunc->duration(m_doc); @@ -83,12 +84,17 @@ ShowRunner::ShowRunner(const Doc* doc, quint32 showID, quint32 startTime) m_intensityMap[track->id()] = 1.0; } - std::sort(m_functions.begin(), m_functions.end(), compareShowFunctions); + std::sort(m_timeFunctions.begin(), m_timeFunctions.end(), compareShowFunctions); + std::sort(m_beatFunctions.begin(), m_beatFunctions.end(), compareShowFunctions); #if 1 - qDebug() << "Ordered list of ShowFunctions:"; - foreach (ShowFunction *sfunc, m_functions) - qDebug() << "ID:" << sfunc->functionID() << "st:" << sfunc->startTime() << "dur:" << sfunc->duration(m_doc); + qDebug() << "Ordered list of ShowFunctions (time):"; + foreach (ShowFunction *sfunc, m_timeFunctions) + 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() << "[Show] Function ID:" << sfunc->functionID() << "start time:" << sfunc->startTime() << "duration:" << sfunc->duration(m_doc); #endif m_runningQueue.clear(); @@ -116,7 +122,10 @@ void ShowRunner::setPause(bool enable) void ShowRunner::stop() { m_elapsedTime = 0; - m_currentFunctionIndex = 0; + m_elapsedBeats = 0; + m_currentTimeFunctionIndex = 0; + m_currentBeatFunctionIndex = 0; + for (int i = 0; i < m_runningQueue.count(); i++) { Function *f = m_runningQueue.at(i).first; @@ -132,21 +141,42 @@ FunctionParent ShowRunner::functionParent() const return FunctionParent(FunctionParent::Function, m_show->id()); } -void ShowRunner::write() +void ShowRunner::write(MasterTimer *timer) { //qDebug() << Q_FUNC_INFO << "elapsed:" << m_elapsedTime << ", total:" << m_totalRunTime; // Phase 1. Check all the Functions that need to be started - // m_functions is ordered by startup time, so when we found an entry + // m_timeFunctions is ordered by startup time, so when we found an entry // with start time greater than m_elapsed, this phase is over - bool startupDone = false; + bool startFunctionsDone = false; + + // check synchronization to beats (if show is beat-based) + if (m_show->tempoType() == Function::Beats) + { + //qDebug() << Q_FUNC_INFO << "isBeat:" << timer->isBeat() << ", elapsed beats:" << m_elapsedBeats; + + if (timer->isBeat()) + { + if (beatSynced == false) + { + beatSynced = true; + qDebug() << "Beat synced"; + } + else + m_elapsedBeats += 1000; + } + + if (beatSynced == false) + return; + } - while(startupDone == false) + // check if there are time-based functions to start + while (startFunctionsDone == false) { - if (m_currentFunctionIndex == m_functions.count()) + if (m_currentTimeFunctionIndex == m_timeFunctions.count()) break; - ShowFunction *sf = m_functions.at(m_currentFunctionIndex); + ShowFunction *sf = m_timeFunctions.at(m_currentTimeFunctionIndex); quint32 funcStartTime = sf->startTime(); quint32 functionTimeOffset = 0; Function *f = m_doc->function(sf->functionID()); @@ -172,23 +202,64 @@ void ShowRunner::write() f->start(m_doc->masterTimer(), functionParent(), functionTimeOffset); m_runningQueue.append(QPair(f, sf->startTime() + sf->duration(m_doc))); - m_currentFunctionIndex++; + m_currentTimeFunctionIndex++; + } + else + startFunctionsDone = true; + } + + startFunctionsDone = false; + + // check if there are beat-based functions to start + while (startFunctionsDone == false) + { + if (m_currentBeatFunctionIndex == m_beatFunctions.count()) + break; + + ShowFunction *sf = m_beatFunctions.at(m_currentBeatFunctionIndex); + quint32 funcStartTime = sf->startTime(); + quint32 functionTimeOffset = 0; + Function *f = m_doc->function(sf->functionID()); + + // this should happen only when a Show is not started from 0 + if (m_elapsedBeats > funcStartTime) + { + functionTimeOffset = m_elapsedBeats - funcStartTime; + funcStartTime = m_elapsedBeats; + } + if (m_elapsedBeats >= funcStartTime) + { + foreach (Track *track, m_show->tracks()) + { + if (track->showFunctions().contains(sf)) + { + int intOverrideId = f->requestAttributeOverride(Function::Intensity, m_intensityMap[track->id()]); + //f->adjustAttribute(m_intensityMap[track->id()], Function::Intensity); + sf->setIntensityOverrideId(intOverrideId); + break; + } + } + + f->start(m_doc->masterTimer(), functionParent(), functionTimeOffset); + m_runningQueue.append(QPair(f, sf->startTime() + sf->duration(m_doc))); + m_currentBeatFunctionIndex++; } else - startupDone = true; + startFunctionsDone = true; } // Phase 2. Check if we need to stop some running Functions // It is done in reverse order for two reasons: // 1- m_runningQueue is not ordered by stop time // 2- to avoid messing up with indices when an entry is removed - for(int i = m_runningQueue.count() - 1; i >= 0; i--) + for (int i = m_runningQueue.count() - 1; i >= 0; i--) { Function *func = m_runningQueue.at(i).first; quint32 stopTime = m_runningQueue.at(i).second; + quint32 currTime = func->tempoType() == Function::Time ? m_elapsedTime : m_elapsedBeats; // if we passed the function stop time - if (m_elapsedTime >= stopTime) + if (currTime >= stopTime) { // stop the function func->stop(functionParent()); diff --git a/engine/src/showrunner.h b/engine/src/showrunner.h index e90be17814..a51cb841a5 100644 --- a/engine/src/showrunner.h +++ b/engine/src/showrunner.h @@ -41,7 +41,7 @@ class ShowRunner : public QObject Q_OBJECT public: - ShowRunner(const Doc* doc, quint32 showID, quint32 startTime = 0); + ShowRunner(const Doc *doc, quint32 showID, quint32 startTime = 0); ~ShowRunner(); /** Start the runner */ @@ -53,29 +53,41 @@ class ShowRunner : public QObject /** Stop the runner */ void stop(); - void write(); + void write(MasterTimer *timer); private: - const Doc* m_doc; + const Doc *m_doc; /** The reference of the show to play */ Show* m_show; - /** The list of Functions of the show to play */ - QList m_functions; + /** The list of time-based Functions the Show needs to play */ + QList m_timeFunctions; - /** Elapsed time since runner start. Used also to move the cursor in MultiTrackView */ + /** Index of the item in m_timeFunctions to be considered for playback */ + int m_currentTimeFunctionIndex; + + /** Elapsed time since runner start. Used also to move the cursor in the track view */ quint32 m_elapsedTime; + /** The list of beat-based Functions the Show needs to play */ + QList m_beatFunctions; + + /** Index of the item in m_beatFunctions to be considered for playback */ + int m_currentBeatFunctionIndex; + + /** Elapsed beats since runner start */ + quint32 m_elapsedBeats; + + /** Flag used to sinchronize playback to beats */ + bool beatSynced; + /** Total time the runner has to run */ quint32 m_totalRunTime; /** List of the currently running Functions and their stop time */ QList < QPair > m_runningQueue; - /** Index of the item in m_functions to be considered for playback */ - int m_currentFunctionIndex; - private: FunctionParent functionParent() const; 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/track.cpp b/engine/src/track.cpp index 39c1405203..d9bb6b9485 100644 --- a/engine/src/track.cpp +++ b/engine/src/track.cpp @@ -24,21 +24,21 @@ #include "sequence.h" #include "track.h" #include "scene.h" +#include "show.h" #include "doc.h" -#define KXMLQLCTrackID QString("ID") #define KXMLQLCTrackName QString("Name") #define KXMLQLCTrackSceneID QString("SceneID") #define KXMLQLCTrackIsMute QString("isMute") #define KXMLQLCTrackFunctions QString("Functions") -Track::Track(quint32 sceneID) - : m_id(Track::invalidId()) +Track::Track(quint32 sceneID, QObject *parent) + : QObject(parent) + , m_id(Track::invalidId()) , m_showId(Function::invalidId()) , m_sceneID(sceneID) , m_isMute(false) - { setName(tr("New Track")); } @@ -67,6 +67,11 @@ quint32 Track::invalidId() return UINT_MAX; } +quint32 Track::showId() +{ + return m_showId; +} + void Track::setShowId(quint32 id) { m_showId = id; @@ -105,7 +110,12 @@ quint32 Track::getSceneID() *********************************************************************/ void Track::setMute(bool state) { + if (m_isMute == state) + return; + m_isMute = state; + + emit muteChanged(state); } bool Track::isMute() @@ -117,10 +127,12 @@ bool Track::isMute() * Sequences *********************************************************************/ -ShowFunction* Track::createShowFunction(quint32 id) +ShowFunction *Track::createShowFunction(quint32 functionID) { - ShowFunction *func = new ShowFunction(); - func->setFunctionID(id); + Show *show = qobject_cast(parent()); + quint32 uId = show == NULL ? 0 : show->getLatestShowFunctionId(); + ShowFunction *func = new ShowFunction(uId); + func->setFunctionID(functionID); m_functions.append(func); return func; @@ -136,13 +148,22 @@ bool Track::addShowFunction(ShowFunction *func) return true; } +ShowFunction *Track::showFunction(quint32 id) +{ + foreach (ShowFunction *sf, m_functions) + if (sf->id() == id) + return sf; + + return NULL; +} + bool Track::removeShowFunction(ShowFunction *function, bool performDelete) { if (m_functions.contains(function) == false) return false; ShowFunction *func = m_functions.takeAt(m_functions.indexOf(function)); - if (performDelete) + if (performDelete && func) delete func; return true; @@ -171,7 +192,7 @@ bool Track::saveXML(QXmlStreamWriter *doc) /* Save the list of Functions if any is present */ if (m_functions.isEmpty() == false) { - foreach(ShowFunction *func, showFunctions()) + foreach (ShowFunction *func, showFunctions()) func->saveXML(doc); } @@ -228,7 +249,9 @@ bool Track::loadXML(QXmlStreamReader &root) { if (root.name() == KXMLShowFunction) { - ShowFunction *newFunc = new ShowFunction(); + Show *show = qobject_cast(parent()); + quint32 uId = show == NULL ? 0 : show->getLatestShowFunctionId(); + ShowFunction *newFunc = new ShowFunction(uId); newFunc->loadXML(root); if (addShowFunction(newFunc) == false) delete newFunc; diff --git a/engine/src/track.h b/engine/src/track.h index eded91e486..c59d3f359f 100644 --- a/engine/src/track.h +++ b/engine/src/track.h @@ -32,20 +32,22 @@ class QXmlStreamReader; * @{ */ -#define KXMLQLCTrack QString("Track") +#define KXMLQLCTrack QString("Track") +#define KXMLQLCTrackID QString("ID") class Track : public QObject { Q_OBJECT Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) + Q_PROPERTY(bool mute READ isMute WRITE setMute NOTIFY muteChanged) /************************************************************************ * Initialization ************************************************************************/ public: /** Create a new Track and associate it to a Scene */ - Track(quint32 sceneID = Scene::invalidId()); + Track(quint32 sceneID = Function::invalidId(), QObject *parent = 0); /** destroy this Track */ ~Track(); @@ -67,12 +69,12 @@ class Track : public QObject /** Get an invalid track id */ static quint32 invalidId(); -private: - quint32 m_id; - -public: + /** Get/Set the Show ID this Track belongs to */ + quint32 showId(); void setShowId(quint32 id); + private: + quint32 m_id; quint32 m_showId; /************************************************************************ @@ -118,6 +120,9 @@ class Track : public QObject /** Return the mute state of the track */ bool isMute(); +signals: + void muteChanged(bool mute); + private: /** Flag to mute/unmute this track */ bool m_isMute; @@ -127,11 +132,11 @@ class Track : public QObject *********************************************************************/ public: /** - * Add a ShowFunction with the given ID to the track. + * Add a ShowFunction with the given Function ID to the track. * If the function doesn't exist, it creates it. * In any case it returns the ShowFunction pointer */ - ShowFunction *createShowFunction(quint32 id); + ShowFunction *createShowFunction(quint32 functionID); /** remove a function ID association from this track */ bool removeShowFunction(ShowFunction *function, bool performDelete = true); @@ -139,6 +144,10 @@ class Track : public QObject /** add a ShowFunction element to this track */ bool addShowFunction(ShowFunction *func); + /** Get a reference to a ShowFunction with the provided ID */ + ShowFunction *showFunction(quint32 id); + + /** Returns the list of ShowFunctions added to this Track */ QList showFunctions() const; private: diff --git a/engine/src/universe.cpp b/engine/src/universe.cpp index 3df45a2be9..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" @@ -59,9 +60,9 @@ Universe::Universe(quint32 id, GrandMaster *gm, QObject *parent) , m_preGMValues(new QByteArray(UNIVERSE_SIZE, char(0))) , m_postGMValues(new QByteArray(UNIVERSE_SIZE, char(0))) , m_lastPostGMValues(new QByteArray(UNIVERSE_SIZE, char(0))) + , 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); @@ -227,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; } @@ -264,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(); } } @@ -295,7 +298,6 @@ void Universe::processFaders() { flushInput(); zeroIntensityChannels(); - zeroRelativeValues(); QMutableListIterator > it(m_faders); while (it.hasNext()) @@ -321,10 +323,11 @@ void Universe::processFaders() fader->write(this); } + bool dataChanged = hasChanged(); const QByteArray postGM = m_postGMValues->mid(0, m_usedChannels); - dumpOutput(postGM); + dumpOutput(postGM, dataChanged); - if (hasChanged()) + if (dataChanged) emit universeWritten(id(), postGM); } @@ -335,7 +338,7 @@ void Universe::run() qDebug() << "Universe thread started" << id(); - while(m_running) + while (m_running) { if (m_semaphore.tryAcquire(1, timeout) == false) { @@ -359,15 +362,13 @@ void Universe::run() void Universe::reset() { m_preGMValues->fill(0); + 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 } @@ -381,7 +382,7 @@ void Universe::reset(int address, int range) range = UNIVERSE_SIZE - address; memset(m_preGMValues->data() + address, 0, range * sizeof(*m_preGMValues->data())); - memset(m_relativeValues.data() + address, 0, range * sizeof(*m_relativeValues.data())); + memset(m_blackoutValues->data() + address, 0, range * sizeof(*m_blackoutValues->data())); memcpy(m_postGMValues->data() + address, m_modifiedZeroValues->data() + address, range * sizeof(*m_postGMValues->data())); applyPassthroughValues(address, range); @@ -438,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) @@ -490,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) || @@ -541,18 +526,10 @@ void Universe::updatePostGMValue(int channel) { uchar value = preGMValue(channel); - value = applyRelative(channel, value); - - if (value == 0) - { - value = static_cast(m_modifiedZeroValues->at(channel)); - } - else - { + if (value != 0) value = applyGM(channel, value); - value = applyModifiers(channel, value); - } + value = applyModifiers(channel, value); value = applyPassthrough(channel, value); (*m_postGMValues)[channel] = static_cast(value); @@ -662,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; } @@ -704,7 +681,7 @@ OutputPatch *Universe::feedbackPatch() const return m_fbPatch; } -void Universe::dumpOutput(const QByteArray &data) +void Universe::dumpOutput(const QByteArray &data, bool dataChanged) { if (m_outputPatchList.count() == 0) return; @@ -715,23 +692,13 @@ void Universe::dumpOutput(const QByteArray &data) op->setPluginParameter(PLUGIN_UNIVERSECHANNELS, m_totalChannels); if (op->blackout()) - op->dump(m_id, *m_modifiedZeroValues); + op->dump(m_id, *m_blackoutValues, dataChanged); else - op->dump(m_id, data); + op->dump(m_id, data, dataChanged); } m_totalChannelsChanged = false; } -void Universe::dumpBlackout() -{ - dumpOutput(*m_modifiedZeroValues); -} - -const QByteArray& Universe::blackoutData() -{ - return *m_modifiedZeroValues; -} - void Universe::flushInput() { if (m_inputPatch == NULL) @@ -795,7 +762,7 @@ void Universe::disconnectInputPatch() void Universe::setChannelCapability(ushort channel, QLCChannel::Group group, ChannelType forcedType) { - if (channel >= (ushort)m_channelsMask->count()) + if (channel >= (ushort)m_channelsMask->length()) return; if (Utils::vectorRemove(m_intensityChannels, channel)) @@ -849,7 +816,7 @@ void Universe::setChannelCapability(ushort channel, QLCChannel::Group group, Cha uchar Universe::channelCapabilities(ushort channel) { - if (channel >= (ushort)m_channelsMask->count()) + if (channel >= (ushort)m_channelsMask->length()) return Undefined; return m_channelsMask->at(channel); @@ -936,94 +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); + Q_ASSERT(address < UNIVERSE_SIZE); - //qDebug() << "Universe write channel" << channel << ", value:" << value; + //qDebug() << "[Universe]" << id() << ": write channel" << address << ", value:" << value; - if (channel >= m_usedChannels) - m_usedChannels = channel + 1; + 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); + } - m_relativeValues[channel] += value - RELATIVE_ZERO; + return true; +} - updatePostGMValue(channel); +bool Universe::writeRelative(int address, quint32 value, int channelCount) +{ + Q_ASSERT(address < UNIVERSE_SIZE); + + //qDebug() << "Write relative channel" << address << "value" << value; + + 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; } @@ -1040,6 +1056,8 @@ bool Universe::loadXML(QXmlStreamReader &root, int index, InputOutputMap *ioMap) return false; } + int outputIndex = 0; + QXmlStreamAttributes attrs = root.attributes(); if (attrs.hasAttribute(KXMLQLCUniverseName)) @@ -1089,7 +1107,7 @@ bool Universe::loadXML(QXmlStreamReader &root, int index, InputOutputMap *ioMap) if (tType == QXmlStreamReader::StartElement) { if (root.name() == KXMLQLCUniversePluginParameters) - loadXMLPluginParameters(root, InputPatchTag); + loadXMLPluginParameters(root, InputPatchTag, 0); root.skipCurrentElement(); } } @@ -1107,7 +1125,7 @@ bool Universe::loadXML(QXmlStreamReader &root, int index, InputOutputMap *ioMap) outputLine = pAttrs.value(KXMLQLCUniverseLine).toString().toUInt(); // apply the parameters just loaded - ioMap->setOutputPatch(index, plugin, outputUID, outputLine, false); + ioMap->setOutputPatch(index, plugin, outputUID, outputLine, false, outputIndex); QXmlStreamReader::TokenType tType = root.readNext(); if (tType == QXmlStreamReader::Characters) @@ -1117,9 +1135,11 @@ bool Universe::loadXML(QXmlStreamReader &root, int index, InputOutputMap *ioMap) if (tType == QXmlStreamReader::StartElement) { if (root.name() == KXMLQLCUniversePluginParameters) - loadXMLPluginParameters(root, OutputPatchTag); + loadXMLPluginParameters(root, OutputPatchTag, outputIndex); root.skipCurrentElement(); } + + outputIndex++; } else if (root.name() == KXMLQLCUniverseFeedbackPatch) { @@ -1145,7 +1165,7 @@ bool Universe::loadXML(QXmlStreamReader &root, int index, InputOutputMap *ioMap) if (tType == QXmlStreamReader::StartElement) { if (root.name() == KXMLQLCUniversePluginParameters) - loadXMLPluginParameters(root, FeedbackPatchTag); + loadXMLPluginParameters(root, FeedbackPatchTag, 0); root.skipCurrentElement(); } } @@ -1159,7 +1179,7 @@ bool Universe::loadXML(QXmlStreamReader &root, int index, InputOutputMap *ioMap) return true; } -bool Universe::loadXMLPluginParameters(QXmlStreamReader &root, PatchTagType currentTag) +bool Universe::loadXMLPluginParameters(QXmlStreamReader &root, PatchTagType currentTag, int patchIndex) { if (root.name() != KXMLQLCUniversePluginParameters) { @@ -1179,7 +1199,7 @@ bool Universe::loadXMLPluginParameters(QXmlStreamReader &root, PatchTagType curr } else if (currentTag == OutputPatchTag) { - OutputPatch *op = outputPatch(); + OutputPatch *op = outputPatch(patchIndex); if (op != NULL) op->setPluginParameter(attr.name().toString(), attr.value().toString()); } @@ -1211,10 +1231,10 @@ bool Universe::saveXML(QXmlStreamWriter *doc) const savePatchXML(doc, KXMLQLCUniverseInputPatch, inputPatch()->pluginName(), inputPatch()->inputName(), inputPatch()->input(), inputPatch()->profileName(), inputPatch()->getPluginParameters()); } - if (outputPatch() != NULL) + foreach (OutputPatch *op, m_outputPatchList) { - savePatchXML(doc, KXMLQLCUniverseOutputPatch, outputPatch()->pluginName(), outputPatch()->outputName(), - outputPatch()->output(), "", outputPatch()->getPluginParameters()); + savePatchXML(doc, KXMLQLCUniverseOutputPatch, op->pluginName(), op->outputName(), + op->output(), "", op->getPluginParameters()); } if (feedbackPatch() != NULL) { @@ -1262,7 +1282,7 @@ bool Universe::savePluginParametersXML(QXmlStreamWriter *doc, doc->writeStartElement(KXMLQLCUniversePluginParameters); QMapIterator it(parameters); - while(it.hasNext()) + while (it.hasNext()) { it.next(); QString pName = it.key(); diff --git a/engine/src/universe.h b/engine/src/universe.h index 8965546d86..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. @@ -222,7 +221,7 @@ protected slots: * Get the reference to the output plugin associated to this universe. * If not present NULL is returned. */ - Q_INVOKABLE OutputPatch *outputPatch(int index = 0) const; + Q_INVOKABLE OutputPatch *outputPatch(int index) const; /** Return the number of output patches associated to this Universe */ int outputPatchesCount() const; @@ -235,19 +234,9 @@ protected slots: /** * This is the actual function that writes data to an output patch + * A flag indicates if data has changed since previous iteration */ - void dumpOutput(const QByteArray& data); - - /** - * @brief dumpBlackout - */ - void dumpBlackout(); - - /** - * @brief blackoutData - * @return - */ - const QByteArray& blackoutData(); + void dumpOutput(const QByteArray& data, bool dataChanged); void flushInput(); @@ -269,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. */ @@ -342,6 +331,7 @@ protected slots: { Auto = 0, Override, + Flashing, /** Priority to override slider values and running chasers by flash scene */ SimpleDesk }; @@ -446,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); @@ -491,12 +478,12 @@ public slots: QScopedPointer m_postGMValues; /** Array of the last preGM values written before the zeroIntensityChannels call */ QScopedPointer m_lastPostGMValues; + /** Array of non-intensity only values */ + QScopedPointer m_blackoutValues; /** Array of values from input line, when passtrhough is enabled */ QScopedPointer m_passthroughValues; - QVector m_relativeValues; - /* impl speedup */ void updateIntensityChannelsRanges(); @@ -525,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 @@ -567,6 +566,8 @@ public slots: * Load a universe contents from the given XML node. * * @param root An XML subtree containing the universe contents + * @param index The QLC+ Universe index + * @param ioMap Reference to the QLC+ Input/Output map class * @return true if the Universe was loaded successfully, otherwise false */ bool loadXML(QXmlStreamReader &root, int index, InputOutputMap *ioMap); @@ -574,10 +575,11 @@ public slots: /** * Load an optional tag defining the plugin specific parameters * @param root An XML subtree containing the plugin parameters contents - * @param currentTag the type of Patch where the parameters should be set + * @param currentTag The type of Patch where the parameters should be set + * @param patchIndex Index of the patch to configure (ATM used only for output) * @return true if the parameters were loaded successfully, otherwise false */ - bool loadXMLPluginParameters(QXmlStreamReader &root, PatchTagType currentTag); + bool loadXMLPluginParameters(QXmlStreamReader &root, PatchTagType currentTag, int patchIndex); /** * Save the universe instance into an XML document, under the given diff --git a/engine/src/video.cpp b/engine/src/video.cpp index ea910f469e..885f05cd3a 100644 --- a/engine/src/video.cpp +++ b/engine/src/video.cpp @@ -132,7 +132,7 @@ QStringList Video::getVideoCapabilities() { qDebug() << "Supported video types:" << mimeTypes; - foreach(QString mime, mimeTypes) + foreach (QString mime, mimeTypes) { if (mime.startsWith("video/")) { diff --git a/engine/test/CMakeLists.txt b/engine/test/CMakeLists.txt new file mode 100644 index 0000000000..3218ff08af --- /dev/null +++ b/engine/test/CMakeLists.txt @@ -0,0 +1,50 @@ +project(test) + +add_subdirectory(bus) +add_subdirectory(chaser) +add_subdirectory(chaserrunner) +add_subdirectory(chaserstep) +add_subdirectory(collection) +add_subdirectory(cue) +add_subdirectory(cuestack) +add_subdirectory(doc) +add_subdirectory(efx) +add_subdirectory(efxfixture) +add_subdirectory(fadechannel) +add_subdirectory(fixture) +add_subdirectory(fixturegroup) +add_subdirectory(function) +add_subdirectory(genericfader) +add_subdirectory(grandmaster) +add_subdirectory(inputoutputmap) +add_subdirectory(inputpatch) +add_subdirectory(keypadparser) +add_subdirectory(mastertimer) +add_subdirectory(outputpatch) +add_subdirectory(qlccapability) +add_subdirectory(qlcchannel) +add_subdirectory(qlcfile) +add_subdirectory(qlcfixturedef) +add_subdirectory(qlcfixturedefcache) +add_subdirectory(qlcfixturehead) +add_subdirectory(qlcfixturemode) +add_subdirectory(qlci18n) +add_subdirectory(qlcinputchannel) +add_subdirectory(qlcinputprofile) +add_subdirectory(qlcmacros) +add_subdirectory(qlcpalette) +add_subdirectory(qlcphysical) +add_subdirectory(qlcpoint) +add_subdirectory(rgbalgorithm) +add_subdirectory(rgbmatrix) +add_subdirectory(rgbscript) +add_subdirectory(rgbtext) +add_subdirectory(scene) +add_subdirectory(scenevalue) +add_subdirectory(script) +add_subdirectory(sequence) +add_subdirectory(show) +add_subdirectory(showfunction) +add_subdirectory(track) +add_subdirectory(universe) +add_subdirectory(iopluginstub) diff --git a/engine/test/bus/CMakeLists.txt b/engine/test/bus/CMakeLists.txt new file mode 100644 index 0000000000..8d158d1d0c --- /dev/null +++ b/engine/test/bus/CMakeLists.txt @@ -0,0 +1,18 @@ +add_executable(bus_test WIN32 + bus_test.cpp bus_test.h +) +target_include_directories(bus_test PRIVATE + ../../../plugins/interfaces + ../../src +) + +target_link_libraries(bus_test PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Test + qlcplusengine +) + +# Consider using qt_generate_deploy_app_script() for app deployment if +# the project can use Qt 6.3. In that case rerun qmake2cmake with +# --min-qt-version=6.3. diff --git a/engine/test/chaser/CMakeLists.txt b/engine/test/chaser/CMakeLists.txt new file mode 100644 index 0000000000..77f2d74f14 --- /dev/null +++ b/engine/test/chaser/CMakeLists.txt @@ -0,0 +1,21 @@ +add_executable(chaser_test WIN32 + ../common/resource_paths.h + ../mastertimer/mastertimer_stub.cpp ../mastertimer/mastertimer_stub.h + chaser_test.cpp chaser_test.h +) +target_include_directories(chaser_test PRIVATE + ../../../plugins/interfaces + ../../src + ../mastertimer +) + +target_link_libraries(chaser_test PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Test + qlcplusengine +) + +# Consider using qt_generate_deploy_app_script() for app deployment if +# the project can use Qt 6.3. In that case rerun qmake2cmake with +# --min-qt-version=6.3. diff --git a/engine/test/chaser/chaser_test.cpp b/engine/test/chaser/chaser_test.cpp index 4ebb2f23d7..d7b4802c29 100644 --- a/engine/test/chaser/chaser_test.cpp +++ b/engine/test/chaser/chaser_test.cpp @@ -58,6 +58,11 @@ void Chaser_Test::cleanupTestCase() void Chaser_Test::init() { m_doc = new Doc(this); + + QDir dir(INTERNAL_FIXTUREDIR); + dir.setFilter(QDir::Files); + dir.setNameFilters(QStringList() << QString("*%1").arg(KExtFixture)); + QVERIFY(m_doc->fixtureDefCache()->loadMap(dir) == true); } void Chaser_Test::cleanup() @@ -878,7 +883,7 @@ void Chaser_Test::preRun() c->postRun(&timer, ua); } -void Chaser_Test::write() +void Chaser_Test::writeHTP() { Fixture* fxi = new Fixture(m_doc); fxi->setAddress(0); @@ -907,6 +912,66 @@ void Chaser_Test::write() c->start(&timer, FunctionParent::master()); timer.timerTick(); + + // check step 1 + for (uint i = MasterTimer::tick(); i < c->duration(); i += MasterTimer::tick()) + { + timer.timerTick(); + QVERIFY(c->isRunning() == true); + QVERIFY(c->stopped() == false); + QVERIFY(s1->isRunning() == true); + QVERIFY(s2->isRunning() == false); + } + + // check step 2 + for (uint i = 0; i < c->duration(); i += MasterTimer::tick()) + { + timer.timerTick(); + QVERIFY(c->isRunning() == true); + QVERIFY(c->stopped() == false); + QVERIFY(s1->isRunning() == false); + QVERIFY(s2->isRunning() == true); + } +} + +void Chaser_Test::writeLTP() +{ + QLCFixtureDef* def = m_doc->fixtureDefCache()->fixtureDef("Clay Paky", "Sharpy"); + QVERIFY(def != NULL); + + QLCFixtureMode* mode = def->mode("Standard"); + QVERIFY(mode != NULL); + + Fixture* fxi = new Fixture(m_doc); + fxi->setFixtureDefinition(def, mode); + QCOMPARE(fxi->channels(), quint32(16)); + m_doc->addFixture(fxi); + + Chaser* c = new Chaser(m_doc); + c->setDuration(MasterTimer::tick() * 10); + m_doc->addFunction(c); + + Scene* s1 = new Scene(m_doc); + s1->setValue(fxi->id(), 0, 20); + m_doc->addFunction(s1); + c->addStep(s1->id()); + + Scene* s2 = new Scene(m_doc); + s2->setValue(fxi->id(), 0, 142); + m_doc->addFunction(s2); + c->addStep(s2->id()); + + MasterTimer timer(m_doc); + QList ua; + ua.append(new Universe(0, new GrandMaster())); + + QVERIFY(c->isRunning() == false); + QVERIFY(c->stopped() == true); + c->start(&timer, FunctionParent::master()); + + timer.timerTick(); + + // check step 1 for (uint i = MasterTimer::tick(); i < c->duration(); i += MasterTimer::tick()) { timer.timerTick(); @@ -914,8 +979,13 @@ void Chaser_Test::write() QVERIFY(c->stopped() == false); QVERIFY(s1->isRunning() == true); QVERIFY(s2->isRunning() == false); + ua = m_doc->inputOutputMap()->claimUniverses(); + ua[0]->processFaders(); + QVERIFY(ua[0]->preGMValues()[0] == (char)20); + m_doc->inputOutputMap()->releaseUniverses(false); } + // check step 2 for (uint i = 0; i < c->duration(); i += MasterTimer::tick()) { timer.timerTick(); @@ -923,6 +993,24 @@ void Chaser_Test::write() QVERIFY(c->stopped() == false); QVERIFY(s1->isRunning() == false); QVERIFY(s2->isRunning() == true); + ua = m_doc->inputOutputMap()->claimUniverses(); + ua[0]->processFaders(); + QVERIFY(ua[0]->preGMValues()[0] == (char)142); + m_doc->inputOutputMap()->releaseUniverses(false); + } + + // check step 1 again + for (uint i = 0; i < c->duration(); i += MasterTimer::tick()) + { + timer.timerTick(); + QVERIFY(c->isRunning() == true); + QVERIFY(c->stopped() == false); + QVERIFY(s1->isRunning() == true); + QVERIFY(s2->isRunning() == false); + ua = m_doc->inputOutputMap()->claimUniverses(); + ua[0]->processFaders(); + QVERIFY(ua[0]->preGMValues()[0] == (char)20); + m_doc->inputOutputMap()->releaseUniverses(false); } } diff --git a/engine/test/chaser/chaser_test.h b/engine/test/chaser/chaser_test.h index b9d0743d3d..980e3ce87c 100644 --- a/engine/test/chaser/chaser_test.h +++ b/engine/test/chaser/chaser_test.h @@ -51,7 +51,8 @@ private slots: void tap(); void preRun(); - void write(); + void writeHTP(); + void writeLTP(); void postRun(); void adjustIntensity(); diff --git a/engine/test/chaserrunner/CMakeLists.txt b/engine/test/chaserrunner/CMakeLists.txt new file mode 100644 index 0000000000..4f1523816d --- /dev/null +++ b/engine/test/chaserrunner/CMakeLists.txt @@ -0,0 +1,21 @@ +add_executable(chaserrunner_test WIN32 + ../common/resource_paths.h + ../mastertimer/mastertimer_stub.cpp ../mastertimer/mastertimer_stub.h + chaserrunner_test.cpp chaserrunner_test.h +) +target_include_directories(chaserrunner_test PRIVATE + ../../../plugins/interfaces + ../../src + ../mastertimer +) + +target_link_libraries(chaserrunner_test PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Test + qlcplusengine +) + +# Consider using qt_generate_deploy_app_script() for app deployment if +# the project can use Qt 6.3. In that case rerun qmake2cmake with +# --min-qt-version=6.3. diff --git a/engine/test/chaserstep/CMakeLists.txt b/engine/test/chaserstep/CMakeLists.txt new file mode 100644 index 0000000000..9a21416cf1 --- /dev/null +++ b/engine/test/chaserstep/CMakeLists.txt @@ -0,0 +1,18 @@ +add_executable(chaserstep_test WIN32 + chaserstep_test.cpp chaserstep_test.h +) +target_include_directories(chaserstep_test PRIVATE + ../../../plugins/interfaces + ../../src +) + +target_link_libraries(chaserstep_test PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Test + qlcplusengine +) + +# Consider using qt_generate_deploy_app_script() for app deployment if +# the project can use Qt 6.3. In that case rerun qmake2cmake with +# --min-qt-version=6.3. diff --git a/engine/test/collection/CMakeLists.txt b/engine/test/collection/CMakeLists.txt new file mode 100644 index 0000000000..54916f2bd3 --- /dev/null +++ b/engine/test/collection/CMakeLists.txt @@ -0,0 +1,20 @@ +add_executable(collection_test WIN32 + ../mastertimer/mastertimer_stub.cpp ../mastertimer/mastertimer_stub.h + collection_test.cpp collection_test.h +) +target_include_directories(collection_test PRIVATE + ../../../plugins/interfaces + ../../src + ../mastertimer +) + +target_link_libraries(collection_test PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Test + qlcplusengine +) + +# Consider using qt_generate_deploy_app_script() for app deployment if +# the project can use Qt 6.3. In that case rerun qmake2cmake with +# --min-qt-version=6.3. diff --git a/engine/test/cue/CMakeLists.txt b/engine/test/cue/CMakeLists.txt new file mode 100644 index 0000000000..f514b88bae --- /dev/null +++ b/engine/test/cue/CMakeLists.txt @@ -0,0 +1,18 @@ +add_executable(cue_test WIN32 + cue_test.cpp cue_test.h +) +target_include_directories(cue_test PRIVATE + ../../../plugins/interfaces + ../../src +) + +target_link_libraries(cue_test PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Test + qlcplusengine +) + +# Consider using qt_generate_deploy_app_script() for app deployment if +# the project can use Qt 6.3. In that case rerun qmake2cmake with +# --min-qt-version=6.3. diff --git a/engine/test/cuestack/CMakeLists.txt b/engine/test/cuestack/CMakeLists.txt new file mode 100644 index 0000000000..6a0e76a170 --- /dev/null +++ b/engine/test/cuestack/CMakeLists.txt @@ -0,0 +1,19 @@ +add_executable(cuestack_test WIN32 + ../common/resource_paths.h + cuestack_test.cpp cuestack_test.h +) +target_include_directories(cuestack_test PRIVATE + ../../../plugins/interfaces + ../../src +) + +target_link_libraries(cuestack_test PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Test + qlcplusengine +) + +# Consider using qt_generate_deploy_app_script() for app deployment if +# the project can use Qt 6.3. In that case rerun qmake2cmake with +# --min-qt-version=6.3. diff --git a/engine/test/doc/CMakeLists.txt b/engine/test/doc/CMakeLists.txt new file mode 100644 index 0000000000..87127391c3 --- /dev/null +++ b/engine/test/doc/CMakeLists.txt @@ -0,0 +1,19 @@ +add_executable(doc_test WIN32 + ../common/resource_paths.h + doc_test.cpp doc_test.h +) +target_include_directories(doc_test PRIVATE + ../../../plugins/interfaces + ../../src +) + +target_link_libraries(doc_test PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Test + qlcplusengine +) + +# Consider using qt_generate_deploy_app_script() for app deployment if +# the project can use Qt 6.3. In that case rerun qmake2cmake with +# --min-qt-version=6.3. diff --git a/engine/test/efx/CMakeLists.txt b/engine/test/efx/CMakeLists.txt new file mode 100644 index 0000000000..85477eb61e --- /dev/null +++ b/engine/test/efx/CMakeLists.txt @@ -0,0 +1,27 @@ +add_executable(efx_test WIN32 + ../common/resource_paths.h + ../mastertimer/mastertimer_stub.cpp ../mastertimer/mastertimer_stub.h + efx_test.cpp efx_test.h +) +target_include_directories(efx_test PRIVATE + ../../../plugins/interfaces + ../../src + ../mastertimer +) + +target_link_libraries(efx_test PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Test + qlcplusengine +) + +if(APPLE) + set_target_properties(efx_test PROPERTIES + MACOSX_BUNDLE FALSE + ) +endif() + +# Consider using qt_generate_deploy_app_script() for app deployment if +# the project can use Qt 6.3. In that case rerun qmake2cmake with +# --min-qt-version=6.3. diff --git a/engine/test/efx/efx_test.cpp b/engine/test/efx/efx_test.cpp index 8817f6e617..be0e2592eb 100644 --- a/engine/test/efx/efx_test.cpp +++ b/engine/test/efx/efx_test.cpp @@ -102,7 +102,7 @@ void EFX_Test::initial() void EFX_Test::algorithmNames() { QStringList list = EFX::algorithmList(); - QCOMPARE(list.size(), 9); + QCOMPARE(list.size(), 10); QVERIFY(list.contains("Circle")); QVERIFY(list.contains("Eight")); QVERIFY(list.contains("Line")); @@ -110,6 +110,7 @@ void EFX_Test::algorithmNames() QVERIFY(list.contains("Diamond")); QVERIFY(list.contains("Square")); QVERIFY(list.contains("SquareChoppy")); + QVERIFY(list.contains("SquareTrue")); QVERIFY(list.contains("Leaf")); QVERIFY(list.contains("Lissajous")); @@ -133,6 +134,9 @@ void EFX_Test::algorithmNames() e.setAlgorithm(EFX::SquareChoppy); QCOMPARE(e.algorithm(), EFX::SquareChoppy); + e.setAlgorithm(EFX::SquareTrue); + QCOMPARE(e.algorithm(), EFX::SquareTrue); + e.setAlgorithm(EFX::Leaf); QCOMPARE(e.algorithm(), EFX::Leaf); @@ -153,6 +157,7 @@ void EFX_Test::stringToAlgorithm() QCOMPARE(EFX::stringToAlgorithm("Diamond"), EFX::Diamond); QCOMPARE(EFX::stringToAlgorithm("Square"), EFX::Square); QCOMPARE(EFX::stringToAlgorithm("SquareChoppy"), EFX::SquareChoppy); + QCOMPARE(EFX::stringToAlgorithm("SquareTrue"), EFX::SquareTrue); QCOMPARE(EFX::stringToAlgorithm("Leaf"), EFX::Leaf); QCOMPARE(EFX::stringToAlgorithm("Lissajous"), EFX::Lissajous); QCOMPARE(EFX::stringToAlgorithm("Foobar"), EFX::Circle); @@ -1552,6 +1557,145 @@ void EFX_Test::previewSquareChoppy() QCOMPARE(poly[127].toPoint(), QPoint(254,127)); } +void EFX_Test::previewSquareTrue() +{ + EFX e(m_doc); + e.setAlgorithm(EFX::SquareTrue); + + QPolygonF poly; + e.preview(poly); + QCOMPARE(poly.size(), 128); + + QCOMPARE(poly[0].toPoint(), QPoint(254,254)); + QCOMPARE(poly[1].toPoint(), QPoint(254,254)); + QCOMPARE(poly[2].toPoint(), QPoint(254,254)); + QCOMPARE(poly[3].toPoint(), QPoint(254,254)); + QCOMPARE(poly[4].toPoint(), QPoint(254,254)); + QCOMPARE(poly[5].toPoint(), QPoint(254,254)); + QCOMPARE(poly[6].toPoint(), QPoint(254,254)); + QCOMPARE(poly[7].toPoint(), QPoint(254,254)); + QCOMPARE(poly[8].toPoint(), QPoint(254,254)); + QCOMPARE(poly[9].toPoint(), QPoint(254,254)); + QCOMPARE(poly[10].toPoint(), QPoint(254,254)); + QCOMPARE(poly[11].toPoint(), QPoint(254,254)); + QCOMPARE(poly[12].toPoint(), QPoint(254,254)); + QCOMPARE(poly[13].toPoint(), QPoint(254,254)); + QCOMPARE(poly[14].toPoint(), QPoint(254,254)); + QCOMPARE(poly[15].toPoint(), QPoint(254,254)); + QCOMPARE(poly[16].toPoint(), QPoint(254,254)); + QCOMPARE(poly[17].toPoint(), QPoint(254,254)); + QCOMPARE(poly[18].toPoint(), QPoint(254,254)); + QCOMPARE(poly[19].toPoint(), QPoint(254,254)); + QCOMPARE(poly[20].toPoint(), QPoint(254,254)); + QCOMPARE(poly[21].toPoint(), QPoint(254,254)); + QCOMPARE(poly[22].toPoint(), QPoint(254,254)); + QCOMPARE(poly[23].toPoint(), QPoint(254,254)); + QCOMPARE(poly[24].toPoint(), QPoint(254,254)); + QCOMPARE(poly[25].toPoint(), QPoint(254,254)); + QCOMPARE(poly[26].toPoint(), QPoint(254,254)); + QCOMPARE(poly[27].toPoint(), QPoint(254,254)); + QCOMPARE(poly[28].toPoint(), QPoint(254,254)); + QCOMPARE(poly[29].toPoint(), QPoint(254,254)); + QCOMPARE(poly[30].toPoint(), QPoint(254,254)); + QCOMPARE(poly[31].toPoint(), QPoint(254,254)); + QCOMPARE(poly[32].toPoint(), QPoint(254,0)); + QCOMPARE(poly[33].toPoint(), QPoint(254,0)); + QCOMPARE(poly[34].toPoint(), QPoint(254,0)); + QCOMPARE(poly[35].toPoint(), QPoint(254,0)); + QCOMPARE(poly[36].toPoint(), QPoint(254,0)); + QCOMPARE(poly[37].toPoint(), QPoint(254,0)); + QCOMPARE(poly[38].toPoint(), QPoint(254,0)); + QCOMPARE(poly[39].toPoint(), QPoint(254,0)); + QCOMPARE(poly[40].toPoint(), QPoint(254,0)); + QCOMPARE(poly[41].toPoint(), QPoint(254,0)); + QCOMPARE(poly[42].toPoint(), QPoint(254,0)); + QCOMPARE(poly[43].toPoint(), QPoint(254,0)); + QCOMPARE(poly[44].toPoint(), QPoint(254,0)); + QCOMPARE(poly[45].toPoint(), QPoint(254,0)); + QCOMPARE(poly[46].toPoint(), QPoint(254,0)); + QCOMPARE(poly[47].toPoint(), QPoint(254,0)); + QCOMPARE(poly[48].toPoint(), QPoint(254,0)); + QCOMPARE(poly[49].toPoint(), QPoint(254,0)); + QCOMPARE(poly[50].toPoint(), QPoint(254,0)); + QCOMPARE(poly[51].toPoint(), QPoint(254,0)); + QCOMPARE(poly[52].toPoint(), QPoint(254,0)); + QCOMPARE(poly[53].toPoint(), QPoint(254,0)); + QCOMPARE(poly[54].toPoint(), QPoint(254,0)); + QCOMPARE(poly[55].toPoint(), QPoint(254,0)); + QCOMPARE(poly[56].toPoint(), QPoint(254,0)); + QCOMPARE(poly[57].toPoint(), QPoint(254,0)); + QCOMPARE(poly[58].toPoint(), QPoint(254,0)); + QCOMPARE(poly[59].toPoint(), QPoint(254,0)); + QCOMPARE(poly[60].toPoint(), QPoint(254,0)); + QCOMPARE(poly[61].toPoint(), QPoint(254,0)); + QCOMPARE(poly[62].toPoint(), QPoint(254,0)); + QCOMPARE(poly[63].toPoint(), QPoint(254,0)); + QCOMPARE(poly[64].toPoint(), QPoint(254,0)); + QCOMPARE(poly[65].toPoint(), QPoint(0,0)); + QCOMPARE(poly[66].toPoint(), QPoint(0,0)); + QCOMPARE(poly[67].toPoint(), QPoint(0,0)); + QCOMPARE(poly[68].toPoint(), QPoint(0,0)); + QCOMPARE(poly[69].toPoint(), QPoint(0,0)); + QCOMPARE(poly[70].toPoint(), QPoint(0,0)); + QCOMPARE(poly[71].toPoint(), QPoint(0,0)); + QCOMPARE(poly[72].toPoint(), QPoint(0,0)); + QCOMPARE(poly[73].toPoint(), QPoint(0,0)); + QCOMPARE(poly[74].toPoint(), QPoint(0,0)); + QCOMPARE(poly[75].toPoint(), QPoint(0,0)); + QCOMPARE(poly[76].toPoint(), QPoint(0,0)); + QCOMPARE(poly[77].toPoint(), QPoint(0,0)); + QCOMPARE(poly[78].toPoint(), QPoint(0,0)); + QCOMPARE(poly[79].toPoint(), QPoint(0,0)); + QCOMPARE(poly[80].toPoint(), QPoint(0,0)); + QCOMPARE(poly[81].toPoint(), QPoint(0,0)); + QCOMPARE(poly[82].toPoint(), QPoint(0,0)); + QCOMPARE(poly[83].toPoint(), QPoint(0,0)); + QCOMPARE(poly[84].toPoint(), QPoint(0,0)); + QCOMPARE(poly[85].toPoint(), QPoint(0,0)); + QCOMPARE(poly[86].toPoint(), QPoint(0,0)); + QCOMPARE(poly[87].toPoint(), QPoint(0,0)); + QCOMPARE(poly[88].toPoint(), QPoint(0,0)); + QCOMPARE(poly[89].toPoint(), QPoint(0,0)); + QCOMPARE(poly[90].toPoint(), QPoint(0,0)); + QCOMPARE(poly[91].toPoint(), QPoint(0,0)); + QCOMPARE(poly[92].toPoint(), QPoint(0,0)); + QCOMPARE(poly[93].toPoint(), QPoint(0,0)); + QCOMPARE(poly[94].toPoint(), QPoint(0,0)); + QCOMPARE(poly[95].toPoint(), QPoint(0,0)); + QCOMPARE(poly[96].toPoint(), QPoint(0,0)); + QCOMPARE(poly[97].toPoint(), QPoint(0,254)); + QCOMPARE(poly[98].toPoint(), QPoint(0,254)); + QCOMPARE(poly[99].toPoint(), QPoint(0,254)); + QCOMPARE(poly[100].toPoint(), QPoint(0,254)); + QCOMPARE(poly[101].toPoint(), QPoint(0,254)); + QCOMPARE(poly[102].toPoint(), QPoint(0,254)); + QCOMPARE(poly[103].toPoint(), QPoint(0,254)); + QCOMPARE(poly[104].toPoint(), QPoint(0,254)); + QCOMPARE(poly[105].toPoint(), QPoint(0,254)); + QCOMPARE(poly[106].toPoint(), QPoint(0,254)); + QCOMPARE(poly[107].toPoint(), QPoint(0,254)); + QCOMPARE(poly[108].toPoint(), QPoint(0,254)); + QCOMPARE(poly[109].toPoint(), QPoint(0,254)); + QCOMPARE(poly[110].toPoint(), QPoint(0,254)); + QCOMPARE(poly[111].toPoint(), QPoint(0,254)); + QCOMPARE(poly[112].toPoint(), QPoint(0,254)); + QCOMPARE(poly[113].toPoint(), QPoint(0,254)); + QCOMPARE(poly[114].toPoint(), QPoint(0,254)); + QCOMPARE(poly[115].toPoint(), QPoint(0,254)); + QCOMPARE(poly[116].toPoint(), QPoint(0,254)); + QCOMPARE(poly[117].toPoint(), QPoint(0,254)); + QCOMPARE(poly[118].toPoint(), QPoint(0,254)); + QCOMPARE(poly[119].toPoint(), QPoint(0,254)); + QCOMPARE(poly[120].toPoint(), QPoint(0,254)); + QCOMPARE(poly[121].toPoint(), QPoint(0,254)); + QCOMPARE(poly[122].toPoint(), QPoint(0,254)); + QCOMPARE(poly[123].toPoint(), QPoint(0,254)); + QCOMPARE(poly[124].toPoint(), QPoint(0,254)); + QCOMPARE(poly[125].toPoint(), QPoint(0,254)); + QCOMPARE(poly[126].toPoint(), QPoint(0,254)); + QCOMPARE(poly[127].toPoint(), QPoint(0,254)); +} + void EFX_Test::previewLeaf() { EFX e(m_doc); diff --git a/engine/test/efx/efx_test.h b/engine/test/efx/efx_test.h index ef87074c92..3a51625140 100644 --- a/engine/test/efx/efx_test.h +++ b/engine/test/efx/efx_test.h @@ -55,6 +55,7 @@ private slots: void previewDiamond(); void previewSquare(); void previewSquareChoppy(); + void previewSquareTrue(); void previewLeaf(); void previewLissajous(); diff --git a/engine/test/efxfixture/CMakeLists.txt b/engine/test/efxfixture/CMakeLists.txt new file mode 100644 index 0000000000..937e1bea3c --- /dev/null +++ b/engine/test/efxfixture/CMakeLists.txt @@ -0,0 +1,21 @@ +add_executable(efxfixture_test WIN32 + ../common/resource_paths.h + ../mastertimer/mastertimer_stub.cpp ../mastertimer/mastertimer_stub.h + efxfixture_test.cpp efxfixture_test.h +) +target_include_directories(efxfixture_test PRIVATE + ../../../plugins/interfaces + ../../src + ../mastertimer +) + +target_link_libraries(efxfixture_test PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Test + qlcplusengine +) + +# Consider using qt_generate_deploy_app_script() for app deployment if +# the project can use Qt 6.3. In that case rerun qmake2cmake with +# --min-qt-version=6.3. diff --git a/engine/test/efxfixture/efxfixture_test.cpp b/engine/test/efxfixture/efxfixture_test.cpp index 56734b4120..2da38bb912 100644 --- a/engine/test/efxfixture/efxfixture_test.cpp +++ b/engine/test/efxfixture/efxfixture_test.cpp @@ -137,10 +137,10 @@ void EFXFixture_Test::initial() QVERIFY(ef.direction() == EFX::Forward); QVERIFY(ef.serialNumber() == 0); QVERIFY(ef.isValid() == false); - QVERIFY(ef.isReady() == false); + QVERIFY(ef.isDone() == false); QVERIFY(ef.m_runTimeDirection == EFX::Forward); - QVERIFY(ef.m_ready == false); + QVERIFY(ef.m_done == false); QVERIFY(ef.m_elapsed == 0); } @@ -154,7 +154,7 @@ void EFXFixture_Test::copyFrom() ef.m_direction = EFX::Backward; ef.m_serialNumber = 25; ef.m_runTimeDirection = EFX::Backward; - ef.m_ready = true; + ef.m_done = true; ef.m_elapsed = 31337; EFXFixture copy(&e); @@ -164,7 +164,7 @@ void EFXFixture_Test::copyFrom() QVERIFY(copy.m_direction == EFX::Backward); QVERIFY(copy.m_serialNumber == 25); QVERIFY(copy.m_runTimeDirection == EFX::Backward); - QVERIFY(copy.m_ready == true); + QVERIFY(copy.m_done == true); QVERIFY(copy.m_elapsed == 31337); } @@ -367,7 +367,7 @@ void EFXFixture_Test::reset() ef1->setHead(GroupHead(1,0)); ef1->setSerialNumber(0); ef1->m_runTimeDirection = EFX::Forward; - ef1->m_ready = true; + ef1->m_done = true; ef1->m_elapsed = 1337; e.addFixture(ef1); @@ -375,7 +375,7 @@ void EFXFixture_Test::reset() ef2->setHead(GroupHead(2,0)); ef2->setSerialNumber(1); ef2->m_runTimeDirection = EFX::Forward; - ef2->m_ready = true; + ef2->m_done = true; ef2->m_elapsed = 13; e.addFixture(ef2); @@ -384,7 +384,7 @@ void EFXFixture_Test::reset() ef3->setSerialNumber(2); ef3->setDirection(EFX::Forward); ef3->m_runTimeDirection = EFX::Backward; - ef3->m_ready = true; + ef3->m_done = true; ef3->m_elapsed = 69; e.addFixture(ef3); @@ -393,7 +393,7 @@ void EFXFixture_Test::reset() ef4->setSerialNumber(3); ef4->setDirection(EFX::Forward); ef4->m_runTimeDirection = EFX::Backward; - ef4->m_ready = true; + ef4->m_done = true; ef4->m_elapsed = 42; e.addFixture(ef4); @@ -402,7 +402,7 @@ void EFXFixture_Test::reset() QVERIFY(ef1->m_direction == EFX::Forward); QVERIFY(ef1->m_serialNumber == 0); QVERIFY(ef1->m_runTimeDirection == EFX::Forward); - QVERIFY(ef1->m_ready == false); + QVERIFY(ef1->m_done == false); QVERIFY(ef1->m_elapsed == 0); ef2->reset(); @@ -410,7 +410,7 @@ void EFXFixture_Test::reset() QVERIFY(ef2->m_direction == EFX::Forward); QVERIFY(ef2->m_serialNumber == 1); QVERIFY(ef2->m_runTimeDirection == EFX::Forward); - QVERIFY(ef2->m_ready == false); + QVERIFY(ef2->m_done == false); QVERIFY(ef2->m_elapsed == 0); ef3->reset(); @@ -418,7 +418,7 @@ void EFXFixture_Test::reset() QVERIFY(ef3->m_direction == EFX::Forward); QVERIFY(ef3->m_serialNumber == 2); QVERIFY(ef3->m_runTimeDirection == EFX::Forward); - QVERIFY(ef3->m_ready == false); + QVERIFY(ef3->m_done == false); QVERIFY(ef3->m_elapsed == 0); ef4->reset(); @@ -426,7 +426,7 @@ void EFXFixture_Test::reset() QVERIFY(ef4->m_direction == EFX::Forward); QVERIFY(ef4->m_serialNumber == 3); QVERIFY(ef4->m_runTimeDirection == EFX::Forward); - QVERIFY(ef4->m_ready == false); + QVERIFY(ef4->m_done == false); QVERIFY(ef4->m_elapsed == 0); } @@ -437,7 +437,7 @@ void EFXFixture_Test::startOffset() ef.setHead(GroupHead(0,0)); QCOMPARE(0, ef.startOffset()); - for(int i = 0; i < 360; i += 90) + for (int i = 0; i < 360; i += 90) { ef.setStartOffset(i); QCOMPARE(i, ef.startOffset()); @@ -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(); @@ -540,20 +544,20 @@ void EFXFixture_Test::nextStepLoop() /* Initialize the EFXFixture so that it can do the math */ ef->setSerialNumber(0); QVERIFY(ef->isValid() == true); - QVERIFY(ef->isReady() == false); + QVERIFY(ef->isDone() == false); QVERIFY(ef->m_elapsed == 0); e.preRun(&mts); /* Run two cycles (2 * tickms * freq) to see that Loop never quits */ - uint max = MasterTimer::tick() * MasterTimer::frequency(); + uint max = (MasterTimer::tick() * MasterTimer::frequency()) + MasterTimer::tick(); uint i = MasterTimer::tick(); for (uint times = 0; times < 2; times++) { for (; i < max; i += MasterTimer::tick()) { ef->nextStep(ua, fader); - QVERIFY(ef->isReady() == false); // Loop is never ready + QVERIFY(ef->isDone() == false); // Loop is never ready QCOMPARE(ef->m_elapsed, i); } @@ -580,21 +584,21 @@ void EFXFixture_Test::nextStepLoopZeroDuration() /* Initialize the EFXFixture so that it can do math */ ef->setSerialNumber(0); QVERIFY(ef->isValid() == true); - QVERIFY(ef->isReady() == false); + QVERIFY(ef->isDone() == false); QVERIFY(ef->m_elapsed == 0); e.preRun(&mts); /* Run two cycles (2 * tickms * freq) to see that Loop never quits */ - uint max = MasterTimer::tick() * MasterTimer::frequency(); + uint max = (MasterTimer::tick() * MasterTimer::frequency()) + MasterTimer::tick(); uint i = MasterTimer::tick(); for (uint times = 0; times < 2; times++) { for (; i < max; i += MasterTimer::tick()) { ef->nextStep(ua, fader); - QVERIFY(ef->isReady() == false); // Loop is never ready - QCOMPARE(ef->m_elapsed, i); + QVERIFY(ef->isDone() == false); // Loop is never ready + QVERIFY(ef->m_elapsed == 0); // elapsed is never increased } // m_elapsed is NOT zeroed since there are no "rounds" when duration == 0 @@ -621,7 +625,7 @@ void EFXFixture_Test::nextStepSingleShot() /* Initialize the EFXFixture so that it can do math */ ef->setSerialNumber(0); QVERIFY(ef->isValid() == true); - QVERIFY(ef->isReady() == false); + QVERIFY(ef->isDone() == false); QVERIFY(ef->m_elapsed == 0); e.preRun(&mts); @@ -629,18 +633,18 @@ void EFXFixture_Test::nextStepSingleShot() ef->reset(); /* Run one cycle (50 steps) */ - uint max = MasterTimer::tick() * MasterTimer::frequency(); + uint max = (MasterTimer::tick() * MasterTimer::frequency()) + MasterTimer::tick(); for (uint i = MasterTimer::tick(); i < max; i += MasterTimer::tick()) { ef->nextStep(ua, fader); - QVERIFY(ef->isReady() == false); + QVERIFY(ef->isDone() == false); QCOMPARE(ef->m_elapsed, i); } ef->nextStep(ua, fader); /* Single-shot EFX should now be ready */ - QVERIFY(ef->isReady() == true); + QVERIFY(ef->isDone() == true); e.postRun(&mts, ua); } diff --git a/engine/test/fadechannel/CMakeLists.txt b/engine/test/fadechannel/CMakeLists.txt new file mode 100644 index 0000000000..87557d89dd --- /dev/null +++ b/engine/test/fadechannel/CMakeLists.txt @@ -0,0 +1,19 @@ +add_executable(fadechannel_test WIN32 + ../common/resource_paths.h + fadechannel_test.cpp fadechannel_test.h +) +target_include_directories(fadechannel_test PRIVATE + ../../../plugins/interfaces + ../../src +) + +target_link_libraries(fadechannel_test PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Test + qlcplusengine +) + +# Consider using qt_generate_deploy_app_script() for app deployment if +# the project can use Qt 6.3. In that case rerun qmake2cmake with +# --min-qt-version=6.3. 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/fixture/CMakeLists.txt b/engine/test/fixture/CMakeLists.txt new file mode 100644 index 0000000000..65c3822e9e --- /dev/null +++ b/engine/test/fixture/CMakeLists.txt @@ -0,0 +1,19 @@ +add_executable(fixture_test WIN32 + ../common/resource_paths.h + fixture_test.cpp fixture_test.h +) +target_include_directories(fixture_test PRIVATE + ../../../plugins/interfaces + ../../src +) + +target_link_libraries(fixture_test PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Test + qlcplusengine +) + +# Consider using qt_generate_deploy_app_script() for app deployment if +# the project can use Qt 6.3. In that case rerun qmake2cmake with +# --min-qt-version=6.3. diff --git a/engine/test/fixture/fixture_test.cpp b/engine/test/fixture/fixture_test.cpp index 12787e1725..7684136c33 100644 --- a/engine/test/fixture/fixture_test.cpp +++ b/engine/test/fixture/fixture_test.cpp @@ -553,7 +553,7 @@ void Fixture_Test::degrees() // verify fine Pan QCOMPARE(pos.at(1).fxi, quint32(99)); QCOMPARE(pos.at(1).channel, quint32(8)); - QCOMPARE(pos.at(1).value, uchar(60)); + QCOMPARE(pos.at(1).value, uchar(170)); pos = fxi.positionToValues(QLCChannel::Tilt, 45); QCOMPARE(pos.count(), 2); @@ -564,7 +564,7 @@ void Fixture_Test::degrees() // verify fine Tilt QCOMPARE(pos.at(1).fxi, quint32(99)); QCOMPARE(pos.at(1).channel, quint32(10)); - QCOMPARE(pos.at(1).value, uchar(120)); + QCOMPARE(pos.at(1).value, uchar(170)); } void Fixture_Test::heads() diff --git a/engine/test/fixturegroup/CMakeLists.txt b/engine/test/fixturegroup/CMakeLists.txt new file mode 100644 index 0000000000..0883210c35 --- /dev/null +++ b/engine/test/fixturegroup/CMakeLists.txt @@ -0,0 +1,19 @@ +add_executable(fixturegroup_test WIN32 + ../common/resource_paths.h + fixturegroup_test.cpp fixturegroup_test.h +) +target_include_directories(fixturegroup_test PRIVATE + ../../../plugins/interfaces + ../../src +) + +target_link_libraries(fixturegroup_test PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Test + qlcplusengine +) + +# Consider using qt_generate_deploy_app_script() for app deployment if +# the project can use Qt 6.3. In that case rerun qmake2cmake with +# --min-qt-version=6.3. diff --git a/engine/test/function/CMakeLists.txt b/engine/test/function/CMakeLists.txt new file mode 100644 index 0000000000..967154d125 --- /dev/null +++ b/engine/test/function/CMakeLists.txt @@ -0,0 +1,19 @@ +add_executable(function_test WIN32 + function_stub.cpp function_stub.h + function_test.cpp function_test.h +) +target_include_directories(function_test PRIVATE + ../../../plugins/interfaces + ../../src +) + +target_link_libraries(function_test PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Test + qlcplusengine +) + +# Consider using qt_generate_deploy_app_script() for app deployment if +# the project can use Qt 6.3. In that case rerun qmake2cmake with +# --min-qt-version=6.3. diff --git a/engine/test/function/function_test.cpp b/engine/test/function/function_test.cpp index 95fe871c0e..84b0fe743f 100644 --- a/engine/test/function/function_test.cpp +++ b/engine/test/function/function_test.cpp @@ -146,10 +146,10 @@ void Function_Test::flashUnflash() QSignalSpy spy(stub, SIGNAL(flashing(quint32,bool))); QVERIFY(stub->flashing() == false); - stub->flash(NULL); + stub->flash(NULL, false, false); QCOMPARE(spy.size(), 1); QVERIFY(stub->flashing() == true); - stub->flash(NULL); + stub->flash(NULL, false, false); QCOMPARE(spy.size(), 1); QVERIFY(stub->flashing() == true); stub->unFlash(NULL); diff --git a/engine/test/genericfader/CMakeLists.txt b/engine/test/genericfader/CMakeLists.txt new file mode 100644 index 0000000000..851d984450 --- /dev/null +++ b/engine/test/genericfader/CMakeLists.txt @@ -0,0 +1,19 @@ +add_executable(genericfader_test WIN32 + ../common/resource_paths.h + genericfader_test.cpp genericfader_test.h +) +target_include_directories(genericfader_test PRIVATE + ../../../plugins/interfaces + ../../src +) + +target_link_libraries(genericfader_test PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Test + qlcplusengine +) + +# Consider using qt_generate_deploy_app_script() for app deployment if +# the project can use Qt 6.3. In that case rerun qmake2cmake with +# --min-qt-version=6.3. 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/grandmaster/CMakeLists.txt b/engine/test/grandmaster/CMakeLists.txt new file mode 100644 index 0000000000..d2e4303f1e --- /dev/null +++ b/engine/test/grandmaster/CMakeLists.txt @@ -0,0 +1,18 @@ +add_executable(grandmaster_test WIN32 + grandmaster_test.cpp grandmaster_test.h +) +target_include_directories(grandmaster_test PRIVATE + ../../../plugins/interfaces + ../../src +) + +target_link_libraries(grandmaster_test PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Test + qlcplusengine +) + +# Consider using qt_generate_deploy_app_script() for app deployment if +# the project can use Qt 6.3. In that case rerun qmake2cmake with +# --min-qt-version=6.3. diff --git a/engine/test/inputoutputmap/CMakeLists.txt b/engine/test/inputoutputmap/CMakeLists.txt new file mode 100644 index 0000000000..cbf93358dd --- /dev/null +++ b/engine/test/inputoutputmap/CMakeLists.txt @@ -0,0 +1,42 @@ +set(module_name "inputoutputmap_test") + +add_executable(${module_name} WIN32 + ../common/resource_paths.h + ${module_name}.cpp ${module_name}.h +) +target_include_directories(${module_name} PRIVATE + ../../../plugins/interfaces + ../../src + ../iopluginstub +) + +target_link_libraries(${module_name} PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Test + qlcplusengine +) + +if ("$ENV{TRAVIS}" STREQUAL "true") + target_compile_definitions(${module_name} PRIVATE + SKIP_TEST + ) +endif() + +#if(WIN32) +# set(CURRUSR $ENV{USERNAME}) +#else() +# set(CURRUSR $ENV{USER}) +#endif() + +#string(STRIP ${CURRUSR} CURRUSR) +#string(FIND ${CURRUSR} "build" IS_BUILDBOT) +#if(IS_BUILDBOT GREATER -1) +# target_compile_definitions(${module_name} PRIVATE +# SKIP_TEST +# ) +#endif() + +# Consider using qt_generate_deploy_app_script() for app deployment if +# the project can use Qt 6.3. In that case rerun qmake2cmake with +# --min-qt-version=6.3. diff --git a/engine/test/inputoutputmap/inputoutputmap_test.cpp b/engine/test/inputoutputmap/inputoutputmap_test.cpp index 8ff0953e40..b271422185 100644 --- a/engine/test/inputoutputmap/inputoutputmap_test.cpp +++ b/engine/test/inputoutputmap/inputoutputmap_test.cpp @@ -657,7 +657,7 @@ void InputOutputMap_Test::claimReleaseDumpReset() foreach (Universe *universe, unis) { const QByteArray postGM = universe->postGMValues()->mid(0, universe->usedChannels()); - universe->dumpOutput(postGM); + universe->dumpOutput(postGM, true); } for (int i = 0; i < 512; i++) @@ -694,6 +694,11 @@ void InputOutputMap_Test::blackout() iom.setOutputPatch(3, stub->name(), stub->outputs().at(3), 3); QList unis = iom.claimUniverses(); + unis[0]->setChannelCapability(42, QLCChannel::Intensity); + unis[1]->setChannelCapability(42, QLCChannel::Intensity); + unis[2]->setChannelCapability(42, QLCChannel::Intensity); + unis[3]->setChannelCapability(42, QLCChannel::Intensity); + for (int i = 0; i < 512; i++) unis[0]->write(i, 'a'); for (int i = 0; i < 512; i++) @@ -707,7 +712,7 @@ void InputOutputMap_Test::blackout() foreach (Universe *universe, unis) { const QByteArray postGM = universe->postGMValues()->mid(0, universe->usedChannels()); - universe->dumpOutput(postGM); + universe->dumpOutput(postGM, true); } iom.setBlackout(true); @@ -716,11 +721,29 @@ void InputOutputMap_Test::blackout() foreach (Universe *universe, unis) { const QByteArray postGM = universe->postGMValues()->mid(0, universe->usedChannels()); - universe->dumpOutput(postGM); + universe->dumpOutput(postGM, true); } - for (int i = 0; i < 2048; i++) - QVERIFY(stub->m_universe[i] == (char) 0); + int offset = 0; + + for (int u = 0; u < 4; u++) + { + for (int i = 0; i < 512; i++) + { + if (i == 42) + QVERIFY(stub->m_universe[offset + i] == (char) 0); + else if (u == 0) + QVERIFY(stub->m_universe[offset + i] == (char) 'a'); + else if (u == 1) + QVERIFY(stub->m_universe[offset + i] == (char) 'b'); + else if (u == 2) + QVERIFY(stub->m_universe[offset + i] == (char) 'c'); + else if (u == 3) + QVERIFY(stub->m_universe[offset + i] == (char) 'd'); + } + + offset += 512; + } iom.setBlackout(true); QVERIFY(iom.blackout() == true); @@ -728,11 +751,29 @@ void InputOutputMap_Test::blackout() foreach (Universe *universe, unis) { const QByteArray postGM = universe->postGMValues()->mid(0, universe->usedChannels()); - universe->dumpOutput(postGM); + universe->dumpOutput(postGM, true); } - for (int i = 0; i < 2048; i++) - QVERIFY(stub->m_universe[i] == (char) 0); + offset = 0; + + for (int u = 0; u < 4; u++) + { + for (int i = 0; i < 512; i++) + { + if (i == 42) + QVERIFY(stub->m_universe[offset + i] == (char) 0); + else if (u == 0) + QVERIFY(stub->m_universe[offset + i] == (char) 'a'); + else if (u == 1) + QVERIFY(stub->m_universe[offset + i] == (char) 'b'); + else if (u == 2) + QVERIFY(stub->m_universe[offset + i] == (char) 'c'); + else if (u == 3) + QVERIFY(stub->m_universe[offset + i] == (char) 'd'); + } + + offset += 512; + } iom.toggleBlackout(); QVERIFY(iom.blackout() == false); @@ -740,7 +781,7 @@ void InputOutputMap_Test::blackout() foreach (Universe *universe, unis) { const QByteArray postGM = universe->postGMValues()->mid(0, universe->usedChannels()); - universe->dumpOutput(postGM); + universe->dumpOutput(postGM, true); } for (int i = 0; i < 512; i++) @@ -758,7 +799,7 @@ void InputOutputMap_Test::blackout() foreach (Universe *universe, unis) { const QByteArray postGM = universe->postGMValues()->mid(0, universe->usedChannels()); - universe->dumpOutput(postGM); + universe->dumpOutput(postGM, true); } for (int i = 0; i < 512; i++) @@ -776,11 +817,29 @@ void InputOutputMap_Test::blackout() foreach (Universe *universe, unis) { const QByteArray postGM = universe->postGMValues()->mid(0, universe->usedChannels()); - universe->dumpOutput(postGM); + universe->dumpOutput(postGM, true); } - for (int i = 0; i < 2048; i++) - QVERIFY(stub->m_universe[i] == (char) 0); + offset = 0; + + for (int u = 0; u < 4; u++) + { + for (int i = 0; i < 512; i++) + { + if (i == 42) + QVERIFY(stub->m_universe[offset + i] == (char) 0); + else if (u == 0) + QVERIFY(stub->m_universe[offset + i] == (char) 'a'); + else if (u == 1) + QVERIFY(stub->m_universe[offset + i] == (char) 'b'); + else if (u == 2) + QVERIFY(stub->m_universe[offset + i] == (char) 'c'); + else if (u == 3) + QVERIFY(stub->m_universe[offset + i] == (char) 'd'); + } + + offset += 512; + } } void InputOutputMap_Test::grandMaster() diff --git a/engine/test/inputpatch/CMakeLists.txt b/engine/test/inputpatch/CMakeLists.txt new file mode 100644 index 0000000000..e1c9cd97ee --- /dev/null +++ b/engine/test/inputpatch/CMakeLists.txt @@ -0,0 +1,19 @@ +add_executable(inputpatch_test WIN32 + inputpatch_test.cpp inputpatch_test.h +) +target_include_directories(inputpatch_test PRIVATE + ../../../plugins/interfaces + ../../src + ../iopluginstub +) + +target_link_libraries(inputpatch_test PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Test + qlcplusengine +) + +# Consider using qt_generate_deploy_app_script() for app deployment if +# the project can use Qt 6.3. In that case rerun qmake2cmake with +# --min-qt-version=6.3. diff --git a/engine/test/iopluginstub/CMakeLists.txt b/engine/test/iopluginstub/CMakeLists.txt new file mode 100644 index 0000000000..4ac05b8333 --- /dev/null +++ b/engine/test/iopluginstub/CMakeLists.txt @@ -0,0 +1,14 @@ + +add_library(iopluginstub SHARED) +target_sources(iopluginstub PRIVATE + ../../../plugins/interfaces/qlcioplugin.cpp ../../../plugins/interfaces/qlcioplugin.h + iopluginstub.cpp iopluginstub.h +) +target_include_directories(iopluginstub PRIVATE + ../../../plugins/interfaces +) + +target_link_libraries(iopluginstub PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui +) diff --git a/engine/test/iopluginstub/iopluginstub.cpp b/engine/test/iopluginstub/iopluginstub.cpp index 663c90088b..3725eb05bd 100644 --- a/engine/test/iopluginstub/iopluginstub.cpp +++ b/engine/test/iopluginstub/iopluginstub.cpp @@ -86,9 +86,10 @@ QString IOPluginStub::outputInfo(quint32 output) return QString("This is a plugin stub for testing."); } -void IOPluginStub::writeUniverse(quint32 universe, quint32 output, const QByteArray &data) +void IOPluginStub::writeUniverse(quint32 universe, quint32 output, const QByteArray &data, bool dataChanged) { Q_UNUSED(universe) + Q_UNUSED(dataChanged) m_universe = m_universe.replace(output * 512, data.size(), data); } diff --git a/engine/test/iopluginstub/iopluginstub.h b/engine/test/iopluginstub/iopluginstub.h index a46955181d..14f4be843f 100644 --- a/engine/test/iopluginstub/iopluginstub.h +++ b/engine/test/iopluginstub/iopluginstub.h @@ -68,7 +68,7 @@ class IOPluginStub : public QLCIOPlugin QString outputInfo(quint32 output); /** @reimp */ - void writeUniverse(quint32 universe, quint32 output, const QByteArray& data); + void writeUniverse(quint32 universe, quint32 output, const QByteArray& data, bool dataChanged); public: /** List of outputs that have been opened */ diff --git a/engine/test/keypadparser/CMakeLists.txt b/engine/test/keypadparser/CMakeLists.txt new file mode 100644 index 0000000000..43d1ad86f0 --- /dev/null +++ b/engine/test/keypadparser/CMakeLists.txt @@ -0,0 +1,18 @@ +add_executable(keypadparser_test WIN32 + keypadparser_test.cpp keypadparser_test.h +) +target_include_directories(keypadparser_test PRIVATE + ../../../plugins/interfaces + ../../src +) + +target_link_libraries(keypadparser_test PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Test + qlcplusengine +) + +# Consider using qt_generate_deploy_app_script() for app deployment if +# the project can use Qt 6.3. In that case rerun qmake2cmake with +# --min-qt-version=6.3. diff --git a/engine/test/mastertimer/CMakeLists.txt b/engine/test/mastertimer/CMakeLists.txt new file mode 100644 index 0000000000..e0cbe958ce --- /dev/null +++ b/engine/test/mastertimer/CMakeLists.txt @@ -0,0 +1,45 @@ +set(module_name "mastertimer_test") + +add_executable(${module_name} WIN32 + # ../../../plugins/interfaces/qlcioplugin.cpp + ../common/resource_paths.h + ../function/function_stub.cpp ../function/function_stub.h + dmxsource_stub.cpp dmxsource_stub.h + ${module_name}.cpp ${module_name}.h +) +target_include_directories(${module_name} PRIVATE + ../../../plugins/interfaces + ../../src + ../function +) + +target_link_libraries(${module_name} PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Test + qlcplusengine +) + +if ("$ENV{TRAVIS}" STREQUAL "true") + target_compile_definitions(${module_name} PRIVATE + SKIP_TEST + ) +endif() + +#if(WIN32) +# set(CURRUSR $ENV{USERNAME}) +#else() +# set(CURRUSR $ENV{USER}) +#endif() + +#string(STRIP ${CURRUSR} CURRUSR) +#string(FIND ${CURRUSR} "build" IS_BUILDBOT) +#if(IS_BUILDBOT GREATER -1) +# target_compile_definitions(${module_name} PRIVATE +# SKIP_TEST +# ) +#endif() + +# Consider using qt_generate_deploy_app_script() for app deployment if +# the project can use Qt 6.3. In that case rerun qmake2cmake with +# --min-qt-version=6.3. diff --git a/engine/test/outputpatch/CMakeLists.txt b/engine/test/outputpatch/CMakeLists.txt new file mode 100644 index 0000000000..6b32d9a3c7 --- /dev/null +++ b/engine/test/outputpatch/CMakeLists.txt @@ -0,0 +1,19 @@ +add_executable(outputpatch_test WIN32 + outputpatch_test.cpp outputpatch_test.h +) +target_include_directories(outputpatch_test PRIVATE + ../../../plugins/interfaces + ../../src + ../iopluginstub +) + +target_link_libraries(outputpatch_test PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Test + qlcplusengine +) + +# Consider using qt_generate_deploy_app_script() for app deployment if +# the project can use Qt 6.3. In that case rerun qmake2cmake with +# --min-qt-version=6.3. diff --git a/engine/test/outputpatch/outputpatch_test.cpp b/engine/test/outputpatch/outputpatch_test.cpp index 4ddef9eb19..3c79ef6061 100644 --- a/engine/test/outputpatch/outputpatch_test.cpp +++ b/engine/test/outputpatch/outputpatch_test.cpp @@ -114,14 +114,14 @@ void OutputPatch_Test::dump() QVERIFY(stub->m_universe[169] == (char) 0); QVERIFY(stub->m_universe[511] == (char) 0); - op->dump(0, uni); + op->dump(0, uni, true); QVERIFY(stub->m_universe[0] == (char) 100); QVERIFY(stub->m_universe[169] == (char) 50); QVERIFY(stub->m_universe[511] == (char) 25); /* Test the pause state */ op->setPaused(true); - op->dump(0, uni); + op->dump(0, uni, true); QVERIFY(stub->m_universe[0] == (char) 100); QVERIFY(stub->m_universe[169] == (char) 50); QVERIFY(stub->m_universe[511] == (char) 25); @@ -130,13 +130,13 @@ void OutputPatch_Test::dump() uni[169] = 2; uni[511] = 3; - op->dump(0, uni); + op->dump(0, uni, true); QVERIFY(stub->m_universe[0] == (char) 100); QVERIFY(stub->m_universe[169] == (char) 50); QVERIFY(stub->m_universe[511] == (char) 25); op->setPaused(false); - op->dump(0, uni); + op->dump(0, uni, true); QVERIFY(stub->m_universe[0] == (char) 1); QVERIFY(stub->m_universe[169] == (char) 2); QVERIFY(stub->m_universe[511] == (char) 3); diff --git a/engine/test/qlccapability/CMakeLists.txt b/engine/test/qlccapability/CMakeLists.txt new file mode 100644 index 0000000000..0e3737066c --- /dev/null +++ b/engine/test/qlccapability/CMakeLists.txt @@ -0,0 +1,18 @@ +add_executable(qlccapability_test WIN32 + qlccapability_test.cpp qlccapability_test.h +) +target_include_directories(qlccapability_test PRIVATE + ../../../plugins/interfaces + ../../src +) + +target_link_libraries(qlccapability_test PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Test + qlcplusengine +) + +# Consider using qt_generate_deploy_app_script() for app deployment if +# the project can use Qt 6.3. In that case rerun qmake2cmake with +# --min-qt-version=6.3. diff --git a/engine/test/qlcchannel/CMakeLists.txt b/engine/test/qlcchannel/CMakeLists.txt new file mode 100644 index 0000000000..a04a5d0ce2 --- /dev/null +++ b/engine/test/qlcchannel/CMakeLists.txt @@ -0,0 +1,18 @@ +add_executable(qlcchannel_test WIN32 + qlcchannel_test.cpp qlcchannel_test.h +) +target_include_directories(qlcchannel_test PRIVATE + ../../../plugins/interfaces + ../../src +) + +target_link_libraries(qlcchannel_test PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Test + qlcplusengine +) + +# Consider using qt_generate_deploy_app_script() for app deployment if +# the project can use Qt 6.3. In that case rerun qmake2cmake with +# --min-qt-version=6.3. diff --git a/engine/test/qlcfile/CMakeLists.txt b/engine/test/qlcfile/CMakeLists.txt new file mode 100644 index 0000000000..88e6b2f1b2 --- /dev/null +++ b/engine/test/qlcfile/CMakeLists.txt @@ -0,0 +1,18 @@ +add_executable(qlcfile_test WIN32 + qlcfile_test.cpp qlcfile_test.h +) +target_include_directories(qlcfile_test PRIVATE + ../../../plugins/interfaces + ../../src +) + +target_link_libraries(qlcfile_test PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Test + qlcplusengine +) + +# Consider using qt_generate_deploy_app_script() for app deployment if +# the project can use Qt 6.3. In that case rerun qmake2cmake with +# --min-qt-version=6.3. diff --git a/engine/test/qlcfixturedef/CMakeLists.txt b/engine/test/qlcfixturedef/CMakeLists.txt new file mode 100644 index 0000000000..73c7853397 --- /dev/null +++ b/engine/test/qlcfixturedef/CMakeLists.txt @@ -0,0 +1,18 @@ +add_executable(qlcfixturedef_test WIN32 + qlcfixturedef_test.cpp qlcfixturedef_test.h +) +target_include_directories(qlcfixturedef_test PRIVATE + ../../../plugins/interfaces + ../../src +) + +target_link_libraries(qlcfixturedef_test PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Test + qlcplusengine +) + +# Consider using qt_generate_deploy_app_script() for app deployment if +# the project can use Qt 6.3. In that case rerun qmake2cmake with +# --min-qt-version=6.3. diff --git a/engine/test/qlcfixturedefcache/CMakeLists.txt b/engine/test/qlcfixturedefcache/CMakeLists.txt new file mode 100644 index 0000000000..7bc424faf9 --- /dev/null +++ b/engine/test/qlcfixturedefcache/CMakeLists.txt @@ -0,0 +1,41 @@ +set(module_name "qlcfixturedefcache_test") + +add_executable(${module_name} WIN32 + ../common/resource_paths.h + ${module_name}.cpp ${module_name}.h +) +target_include_directories(${module_name} PRIVATE + ../../../plugins/interfaces + ../../src +) + +target_link_libraries(${module_name} PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Test + qlcplusengine +) + +if ("$ENV{TRAVIS}" STREQUAL "true") + target_compile_definitions(${module_name} PRIVATE + SKIP_TEST + ) +endif() + +#if(WIN32) +# set(CURRUSR $ENV{USERNAME}) +#else() +# set(CURRUSR $ENV{USER}) +#endif() + +#string(STRIP ${CURRUSR} CURRUSR) +#string(FIND ${CURRUSR} "build" IS_BUILDBOT) +#if(IS_BUILDBOT GREATER -1) +# target_compile_definitions(${module_name} PRIVATE +# SKIP_TEST +# ) +#endif() + +# Consider using qt_generate_deploy_app_script() for app deployment if +# the project can use Qt 6.3. In that case rerun qmake2cmake with +# --min-qt-version=6.3. diff --git a/engine/test/qlcfixturedefcache/qlcfixturedefcache_test.cpp b/engine/test/qlcfixturedefcache/qlcfixturedefcache_test.cpp index 5546d2f915..91cf449edf 100644 --- a/engine/test/qlcfixturedefcache/qlcfixturedefcache_test.cpp +++ b/engine/test/qlcfixturedefcache/qlcfixturedefcache_test.cpp @@ -57,6 +57,9 @@ void QLCFixtureDefCache_Test::duplicates() void QLCFixtureDefCache_Test::add() { + QVERIFY(cache.fixtureCache().isEmpty() == false); + QVERIFY(cache.fixtureCache()["Martin"]["MAC250"] == false); + QVERIFY(cache.addFixtureDef(NULL) == false); QVERIFY(cache.manufacturers().count() != 0); @@ -130,6 +133,19 @@ void QLCFixtureDefCache_Test::add() QVERIFY(cache.models("Yoyodyne").contains("MAC250") == true); } +void QLCFixtureDefCache_Test::reload() +{ + QLCFixtureDef *def = cache.fixtureDef("Botex", "SP-1500"); + QLCChannel *channel = def->channel("Control"); + + QVERIFY(def->channels().count() == 5); + def->removeChannel(channel); + QVERIFY(def->channels().count() == 4); + + cache.reloadFixtureDef(def); + QVERIFY(def->channels().count() == 5); +} + void QLCFixtureDefCache_Test::fixtureDef() { // check the content of a cached fixture relative path @@ -141,8 +157,10 @@ void QLCFixtureDefCache_Test::fixtureDef() // request a fixture cached but not yet loaded QLCFixtureDef *def = cache.fixtureDef("Futurelight", "CY-200"); - // check that once loaded, the relative path is reset - QVERIFY(def->definitionSourceFile().isEmpty()); + + // check that once loaded, the relative path becomes absolute + QDir absDir(def->definitionSourceFile()); + QVERIFY(absDir.isAbsolute() == true); cache.clear(); @@ -215,4 +233,18 @@ void QLCFixtureDefCache_Test::defDirectories() } +void QLCFixtureDefCache_Test::storeDef() +{ + QLCFixtureDef *def = cache.fixtureDef("Futurelight", "CY-200"); + QFile defFile(def->definitionSourceFile()); + defFile.open(QIODevice::ReadOnly | QIODevice::Text); + QString defBuffer = defFile.readAll(); + defFile.close(); + QVERIFY(cache.storeFixtureDef("storeTest.qxf", defBuffer) == true); + + QDir dir = QLCFixtureDefCache::userDefinitionDirectory(); + QFile file (dir.absoluteFilePath("storeTest.qxf")); + file.remove(); +} + QTEST_APPLESS_MAIN(QLCFixtureDefCache_Test) diff --git a/engine/test/qlcfixturedefcache/qlcfixturedefcache_test.h b/engine/test/qlcfixturedefcache/qlcfixturedefcache_test.h index 927f44e6fa..0364f3f6f3 100644 --- a/engine/test/qlcfixturedefcache/qlcfixturedefcache_test.h +++ b/engine/test/qlcfixturedefcache/qlcfixturedefcache_test.h @@ -33,9 +33,11 @@ private slots: void duplicates(); void add(); + void reload(); void fixtureDef(); void load(); void defDirectories(); + void storeDef(); private: QLCFixtureDefCache cache; diff --git a/engine/test/qlcfixturehead/CMakeLists.txt b/engine/test/qlcfixturehead/CMakeLists.txt new file mode 100644 index 0000000000..16799da204 --- /dev/null +++ b/engine/test/qlcfixturehead/CMakeLists.txt @@ -0,0 +1,18 @@ +add_executable(qlcfixturehead_test WIN32 + qlcfixturehead_test.cpp qlcfixturehead_test.h +) +target_include_directories(qlcfixturehead_test PRIVATE + ../../../plugins/interfaces + ../../src +) + +target_link_libraries(qlcfixturehead_test PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Test + qlcplusengine +) + +# Consider using qt_generate_deploy_app_script() for app deployment if +# the project can use Qt 6.3. In that case rerun qmake2cmake with +# --min-qt-version=6.3. diff --git a/engine/test/qlcfixturemode/CMakeLists.txt b/engine/test/qlcfixturemode/CMakeLists.txt new file mode 100644 index 0000000000..beb59aae5f --- /dev/null +++ b/engine/test/qlcfixturemode/CMakeLists.txt @@ -0,0 +1,18 @@ +add_executable(qlcfixturemode_test WIN32 + qlcfixturemode_test.cpp qlcfixturemode_test.h +) +target_include_directories(qlcfixturemode_test PRIVATE + ../../../plugins/interfaces + ../../src +) + +target_link_libraries(qlcfixturemode_test PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Test + qlcplusengine +) + +# Consider using qt_generate_deploy_app_script() for app deployment if +# the project can use Qt 6.3. In that case rerun qmake2cmake with +# --min-qt-version=6.3. diff --git a/engine/test/qlci18n/.gitignore b/engine/test/qlci18n/.gitignore deleted file mode 100644 index 1b9c9f2eda..0000000000 --- a/engine/test/qlci18n/.gitignore +++ /dev/null @@ -1 +0,0 @@ -qlci18n_fi_FI.ts diff --git a/engine/test/qlci18n/CMakeLists.txt b/engine/test/qlci18n/CMakeLists.txt new file mode 100644 index 0000000000..5bd98a8eaf --- /dev/null +++ b/engine/test/qlci18n/CMakeLists.txt @@ -0,0 +1,28 @@ +set(module_name "qlci18n_test") + +set(TS_FILES + qlci18n_fi_FI.ts +) + +if(QT_VERSION_MAJOR GREATER 5) + qt_add_translation(QM_FILES ${TS_FILES}) +else() + qt5_add_translation(QM_FILES ${TS_FILES}) +endif() + +add_executable(${module_name} WIN32 + ${module_name}.cpp ${module_name}.h + ${QM_FILES} +) + +target_include_directories(${module_name} PRIVATE + ../../../plugins/interfaces + ../../src +) + +target_link_libraries(${module_name} PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Test + qlcplusengine +) diff --git a/engine/test/qlci18n/qlci18n_fi_FI.ts_ b/engine/test/qlci18n/qlci18n_fi_FI.ts similarity index 88% rename from engine/test/qlci18n/qlci18n_fi_FI.ts_ rename to engine/test/qlci18n/qlci18n_fi_FI.ts index c3be372f50..a5d4aa8e16 100644 --- a/engine/test/qlci18n/qlci18n_fi_FI.ts_ +++ b/engine/test/qlci18n/qlci18n_fi_FI.ts @@ -1,6 +1,6 @@ - + QLCi18n_Test diff --git a/engine/test/qlcinputchannel/CMakeLists.txt b/engine/test/qlcinputchannel/CMakeLists.txt new file mode 100644 index 0000000000..553fdc5dd0 --- /dev/null +++ b/engine/test/qlcinputchannel/CMakeLists.txt @@ -0,0 +1,18 @@ +add_executable(qlcinputchannel_test WIN32 + qlcinputchannel_test.cpp qlcinputchannel_test.h +) +target_include_directories(qlcinputchannel_test PRIVATE + ../../../plugins/interfaces + ../../src +) + +target_link_libraries(qlcinputchannel_test PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Test + qlcplusengine +) + +# Consider using qt_generate_deploy_app_script() for app deployment if +# the project can use Qt 6.3. In that case rerun qmake2cmake with +# --min-qt-version=6.3. diff --git a/engine/test/qlcinputprofile/CMakeLists.txt b/engine/test/qlcinputprofile/CMakeLists.txt new file mode 100644 index 0000000000..92f8aa5d60 --- /dev/null +++ b/engine/test/qlcinputprofile/CMakeLists.txt @@ -0,0 +1,19 @@ +add_executable(qlcinputprofile_test WIN32 + ../common/resource_paths.h + qlcinputprofile_test.cpp qlcinputprofile_test.h +) +target_include_directories(qlcinputprofile_test PRIVATE + ../../../plugins/interfaces + ../../src +) + +target_link_libraries(qlcinputprofile_test PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Test + qlcplusengine +) + +# Consider using qt_generate_deploy_app_script() for app deployment if +# the project can use Qt 6.3. In that case rerun qmake2cmake with +# --min-qt-version=6.3. diff --git a/engine/test/qlcinputprofile/qlcinputprofile_test.cpp b/engine/test/qlcinputprofile/qlcinputprofile_test.cpp index d5cb0f5390..2617df698f 100644 --- a/engine/test/qlcinputprofile/qlcinputprofile_test.cpp +++ b/engine/test/qlcinputprofile/qlcinputprofile_test.cpp @@ -224,28 +224,28 @@ void QLCInputProfile_Test::copy() ich4->setName("Channel 4"); ip.insertChannel(9000, ich4); - QLCInputProfile copy = ip; - QVERIFY(copy.manufacturer() == "Behringer"); - QVERIFY(copy.model() == "BCF2000"); + QLCInputProfile *copy = ip.createCopy(); + QVERIFY(copy->manufacturer() == "Behringer"); + QVERIFY(copy->model() == "BCF2000"); - QVERIFY(copy.channels().size() == 4); + QVERIFY(copy->channels().size() == 4); /* Verify that it's a deep copy */ - QVERIFY(copy.channel(0) != ich1); - QVERIFY(copy.channel(0) != NULL); - QVERIFY(copy.channel(0)->name() == "Channel 1"); + QVERIFY(copy->channel(0) != ich1); + QVERIFY(copy->channel(0) != NULL); + QVERIFY(copy->channel(0)->name() == "Channel 1"); - QVERIFY(copy.channel(5) != ich2); - QVERIFY(copy.channel(5) != NULL); - QVERIFY(copy.channel(5)->name() == "Channel 2"); + QVERIFY(copy->channel(5) != ich2); + QVERIFY(copy->channel(5) != NULL); + QVERIFY(copy->channel(5)->name() == "Channel 2"); - QVERIFY(copy.channel(2) != ich3); - QVERIFY(copy.channel(2) != NULL); - QVERIFY(copy.channel(2)->name() == "Channel 3"); + QVERIFY(copy->channel(2) != ich3); + QVERIFY(copy->channel(2) != NULL); + QVERIFY(copy->channel(2)->name() == "Channel 3"); - QVERIFY(copy.channel(9000) != ich4); - QVERIFY(copy.channel(9000) != NULL); - QVERIFY(copy.channel(9000)->name() == "Channel 4"); + QVERIFY(copy->channel(9000) != ich4); + QVERIFY(copy->channel(9000) != NULL); + QVERIFY(copy->channel(9000)->name() == "Channel 4"); } void QLCInputProfile_Test::assign() diff --git a/engine/test/qlcmacros/CMakeLists.txt b/engine/test/qlcmacros/CMakeLists.txt new file mode 100644 index 0000000000..e98579ed00 --- /dev/null +++ b/engine/test/qlcmacros/CMakeLists.txt @@ -0,0 +1,18 @@ +add_executable(qlcmacros_test WIN32 + qlcmacros_test.cpp qlcmacros_test.h +) +target_include_directories(qlcmacros_test PRIVATE + ../../../plugins/interfaces + ../../src +) + +target_link_libraries(qlcmacros_test PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Test + qlcplusengine +) + +# Consider using qt_generate_deploy_app_script() for app deployment if +# the project can use Qt 6.3. In that case rerun qmake2cmake with +# --min-qt-version=6.3. diff --git a/engine/test/qlcpalette/CMakeLists.txt b/engine/test/qlcpalette/CMakeLists.txt new file mode 100644 index 0000000000..a35a739c4a --- /dev/null +++ b/engine/test/qlcpalette/CMakeLists.txt @@ -0,0 +1,18 @@ +add_executable(qlcpalette_test WIN32 + qlcpalette_test.cpp qlcpalette_test.h +) +target_include_directories(qlcpalette_test PRIVATE + ../../../plugins/interfaces + ../../src +) + +target_link_libraries(qlcpalette_test PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Test + qlcplusengine +) + +# Consider using qt_generate_deploy_app_script() for app deployment if +# the project can use Qt 6.3. In that case rerun qmake2cmake with +# --min-qt-version=6.3. diff --git a/engine/test/qlcpalette/qlcpalette_test.cpp b/engine/test/qlcpalette/qlcpalette_test.cpp index 2a4068a869..e8092d7dc0 100644 --- a/engine/test/qlcpalette/qlcpalette_test.cpp +++ b/engine/test/qlcpalette/qlcpalette_test.cpp @@ -31,7 +31,7 @@ void QLCPalette_Test::initialization() QVERIFY(p.type() == QLCPalette::Undefined); QVERIFY(p.id() == QLCPalette::invalidId()); QVERIFY(p.fanningType() == QLCPalette::Flat); - QVERIFY(p.fanningLayout() == QLCPalette::LeftToRight); + QVERIFY(p.fanningLayout() == QLCPalette::XAscending); QVERIFY(p.fanningAmount() == 100); QVERIFY(p.fanningValue() == QVariant()); @@ -128,18 +128,26 @@ void QLCPalette_Test::fanning() QVERIFY(QLCPalette::stringToFanningType("Square") == QLCPalette::Square); QVERIFY(QLCPalette::stringToFanningType("Saw") == QLCPalette::Saw); - QVERIFY(QLCPalette::fanningLayoutToString(QLCPalette::LeftToRight) == "LeftToRight"); - QVERIFY(QLCPalette::fanningLayoutToString(QLCPalette::RightToLeft) == "RightToLeft"); - QVERIFY(QLCPalette::fanningLayoutToString(QLCPalette::TopToBottom) == "TopToBottom"); - QVERIFY(QLCPalette::fanningLayoutToString(QLCPalette::BottomToTop) == "BottomToTop"); - QVERIFY(QLCPalette::fanningLayoutToString(QLCPalette::Centered) == "Centered"); - - QVERIFY(QLCPalette::stringToFanningLayout("Foo") == QLCPalette::LeftToRight); - QVERIFY(QLCPalette::stringToFanningLayout("LeftToRight") == QLCPalette::LeftToRight); - QVERIFY(QLCPalette::stringToFanningLayout("RightToLeft") == QLCPalette::RightToLeft); - QVERIFY(QLCPalette::stringToFanningLayout("TopToBottom") == QLCPalette::TopToBottom); - QVERIFY(QLCPalette::stringToFanningLayout("BottomToTop") == QLCPalette::BottomToTop); - QVERIFY(QLCPalette::stringToFanningLayout("Centered") == QLCPalette::Centered); + QVERIFY(QLCPalette::fanningLayoutToString(QLCPalette::XAscending) == "XAscending"); + QVERIFY(QLCPalette::fanningLayoutToString(QLCPalette::XDescending) == "XDescending"); + QVERIFY(QLCPalette::fanningLayoutToString(QLCPalette::XCentered) == "XCentered"); + QVERIFY(QLCPalette::fanningLayoutToString(QLCPalette::YAscending) == "YAscending"); + QVERIFY(QLCPalette::fanningLayoutToString(QLCPalette::YDescending) == "YDescending"); + QVERIFY(QLCPalette::fanningLayoutToString(QLCPalette::YCentered) == "YCentered"); + QVERIFY(QLCPalette::fanningLayoutToString(QLCPalette::ZAscending) == "ZAscending"); + QVERIFY(QLCPalette::fanningLayoutToString(QLCPalette::ZDescending) == "ZDescending"); + QVERIFY(QLCPalette::fanningLayoutToString(QLCPalette::ZCentered) == "ZCentered"); + + QVERIFY(QLCPalette::stringToFanningLayout("Foo") == QLCPalette::XAscending); + QVERIFY(QLCPalette::stringToFanningLayout("XAscending") == QLCPalette::XAscending); + QVERIFY(QLCPalette::stringToFanningLayout("XDescending") == QLCPalette::XDescending); + QVERIFY(QLCPalette::stringToFanningLayout("XCentered") == QLCPalette::XCentered); + QVERIFY(QLCPalette::stringToFanningLayout("YAscending") == QLCPalette::YAscending); + QVERIFY(QLCPalette::stringToFanningLayout("YDescending") == QLCPalette::YDescending); + QVERIFY(QLCPalette::stringToFanningLayout("YCentered") == QLCPalette::YCentered); + QVERIFY(QLCPalette::stringToFanningLayout("ZAscending") == QLCPalette::ZAscending); + QVERIFY(QLCPalette::stringToFanningLayout("ZDescending") == QLCPalette::ZDescending); + QVERIFY(QLCPalette::stringToFanningLayout("ZCentered") == QLCPalette::ZCentered); QLCPalette p(QLCPalette::Dimmer); p.setFanningAmount(75); @@ -199,7 +207,7 @@ void QLCPalette_Test::load() QVERIFY(p.name() == "Lavender"); QVERIFY(p.value().toString() == "#AABBCCDDEEFF"); QVERIFY(p.fanningType() == QLCPalette::Linear); - QVERIFY(p.fanningLayout() == QLCPalette::LeftToRight); + QVERIFY(p.fanningLayout() == QLCPalette::XAscending); QVERIFY(p.fanningAmount() == 42); QVERIFY(p.fanningValue().toString() == "#00ff00"); } diff --git a/engine/test/qlcphysical/CMakeLists.txt b/engine/test/qlcphysical/CMakeLists.txt new file mode 100644 index 0000000000..5f694259d9 --- /dev/null +++ b/engine/test/qlcphysical/CMakeLists.txt @@ -0,0 +1,18 @@ +add_executable(qlcphysical_test WIN32 + qlcphysical_test.cpp qlcphysical_test.h +) +target_include_directories(qlcphysical_test PRIVATE + ../../../plugins/interfaces + ../../src +) + +target_link_libraries(qlcphysical_test PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Test + qlcplusengine +) + +# Consider using qt_generate_deploy_app_script() for app deployment if +# the project can use Qt 6.3. In that case rerun qmake2cmake with +# --min-qt-version=6.3. diff --git a/engine/test/qlcpoint/CMakeLists.txt b/engine/test/qlcpoint/CMakeLists.txt new file mode 100644 index 0000000000..b0354303e1 --- /dev/null +++ b/engine/test/qlcpoint/CMakeLists.txt @@ -0,0 +1,18 @@ +add_executable(qlcpoint_test WIN32 + qlcpoint_test.cpp qlcpoint_test.h +) +target_include_directories(qlcpoint_test PRIVATE + ../../../plugins/interfaces + ../../src +) + +target_link_libraries(qlcpoint_test PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Test + qlcplusengine +) + +# Consider using qt_generate_deploy_app_script() for app deployment if +# the project can use Qt 6.3. In that case rerun qmake2cmake with +# --min-qt-version=6.3. diff --git a/engine/test/rgbalgorithm/CMakeLists.txt b/engine/test/rgbalgorithm/CMakeLists.txt new file mode 100644 index 0000000000..73149a140e --- /dev/null +++ b/engine/test/rgbalgorithm/CMakeLists.txt @@ -0,0 +1,32 @@ +add_executable(rgbalgorithm_test WIN32 + ../common/resource_paths.h + rgbalgorithm_test.cpp rgbalgorithm_test.h +) +target_include_directories(rgbalgorithm_test PRIVATE + ../../../plugins/interfaces + ../../src + ../mastertimer +) + +target_link_libraries(rgbalgorithm_test PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Test + qlcplusengine +) + +if(qmlui OR (QT_VERSION_MAJOR GREATER 5)) + target_link_libraries(rgbalgorithm_test PRIVATE + Qt${QT_MAJOR_VERSION}::Qml + ) +endif() + +if(NOT (qmlui OR (QT_VERSION_MAJOR GREATER 5))) + target_link_libraries(rgbalgorithm_test PRIVATE + Qt${QT_MAJOR_VERSION}::Script + ) +endif() + +# Consider using qt_generate_deploy_app_script() for app deployment if +# the project can use Qt 6.3. In that case rerun qmake2cmake with +# --min-qt-version=6.3. diff --git a/engine/test/rgbmatrix/CMakeLists.txt b/engine/test/rgbmatrix/CMakeLists.txt new file mode 100644 index 0000000000..552596d428 --- /dev/null +++ b/engine/test/rgbmatrix/CMakeLists.txt @@ -0,0 +1,33 @@ +add_executable(rgbmatrix_test WIN32 + ../common/resource_paths.h + ../mastertimer/mastertimer_stub.cpp ../mastertimer/mastertimer_stub.h + rgbmatrix_test.cpp rgbmatrix_test.h +) +target_include_directories(rgbmatrix_test PRIVATE + ../../../plugins/interfaces + ../../src + ../mastertimer +) + +target_link_libraries(rgbmatrix_test PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Test + qlcplusengine +) + +if(qmlui OR (QT_VERSION_MAJOR GREATER 5)) + target_link_libraries(rgbmatrix_test PRIVATE + Qt${QT_MAJOR_VERSION}::Qml + ) +endif() + +if(NOT (qmlui OR (QT_VERSION_MAJOR GREATER 5))) + target_link_libraries(rgbmatrix_test PRIVATE + Qt${QT_MAJOR_VERSION}::Script + ) +endif() + +# Consider using qt_generate_deploy_app_script() for app deployment if +# the project can use Qt 6.3. In that case rerun qmake2cmake with +# --min-qt-version=6.3. diff --git a/engine/test/rgbscript/CMakeLists.txt b/engine/test/rgbscript/CMakeLists.txt new file mode 100644 index 0000000000..6aec6caf7c --- /dev/null +++ b/engine/test/rgbscript/CMakeLists.txt @@ -0,0 +1,32 @@ +add_executable(rgbscript_test WIN32 + ../common/resource_paths.h + rgbscript_test.cpp rgbscript_test.h +) +target_include_directories(rgbscript_test PRIVATE + ../../../plugins/interfaces + ../../src + ../mastertimer +) + +target_link_libraries(rgbscript_test PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Test + qlcplusengine +) + +if(qmlui OR (QT_VERSION_MAJOR GREATER 5)) + target_link_libraries(rgbscript_test PRIVATE + Qt${QT_MAJOR_VERSION}::Qml + ) +endif() + +if(NOT (qmlui OR (QT_VERSION_MAJOR GREATER 5))) + target_link_libraries(rgbscript_test PRIVATE + Qt${QT_MAJOR_VERSION}::Script + ) +endif() + +# Consider using qt_generate_deploy_app_script() for app deployment if +# the project can use Qt 6.3. In that case rerun qmake2cmake with +# --min-qt-version=6.3. diff --git a/engine/test/rgbscript/rgbscript_test.cpp b/engine/test/rgbscript/rgbscript_test.cpp index cd05e96e36..65ea72283c 100644 --- a/engine/test/rgbscript/rgbscript_test.cpp +++ b/engine/test/rgbscript/rgbscript_test.cpp @@ -58,7 +58,7 @@ void RGBScript_Test::directories() QDir dir = RGBScriptsCache::systemScriptsDirectory(); QCOMPARE(dir.filter(), QDir::Files); QCOMPARE(dir.nameFilters(), QStringList() << QString("*.js")); -#if defined( __APPLE__) || defined(Q_OS_MAC) +#if defined(__APPLE__) || defined(Q_OS_MAC) QString path("%1/../%2"); QCOMPARE(dir.path(), path.arg(QCoreApplication::applicationDirPath()) .arg("Resources/RGBScripts")); @@ -71,7 +71,7 @@ void RGBScript_Test::directories() dir = RGBScriptsCache::userScriptsDirectory(); QCOMPARE(dir.filter(), QDir::Files); QCOMPARE(dir.nameFilters(), QStringList() << QString("*.js")); -#if defined( __APPLE__) || defined(Q_OS_MAC) +#if defined(__APPLE__) || defined(Q_OS_MAC) QVERIFY(dir.path().endsWith("Library/Application Support/QLC+/RGBScripts")); #elif defined(WIN32) || defined(Q_OS_WIN) QVERIFY(dir.path().endsWith("RGBScripts")); @@ -87,8 +87,39 @@ void RGBScript_Test::scripts() dir.setNameFilters(QStringList() << QString("*.js")); QVERIFY(dir.entryList().size() > 0); + // Prepare check that file is registered for delivery + QString proFilePath = dir.filePath("rgbscripts.pro"); + QFile proFile(proFilePath); + proFile.open(QIODevice::ReadWrite); + QTextStream pro (&proFile); + + // Catch syntax / JS engine errors explicitly in the test. + foreach (QString file, dir.entryList()) { + RGBScript* script = new RGBScript(m_doc); + QVERIFY(script->load(dir, file)); + + qDebug() << "Searching 'scripts.files += " + file + "' in rgbscripts.pro"; + + // Check that the script is listed in the pro file. + // scripts.files += noise.js + if (file != "empty.js") { + QString searchString = "scripts.files += " + file; + QString line; + bool foundInProFile = false; + do { + line = pro.readLine(); + if (line.contains(searchString, Qt::CaseSensitive)) { + foundInProFile = true; + } + } while (!line.isNull() && foundInProFile == false); + + QVERIFY(foundInProFile); + } + } + proFile.close(); + QVERIFY(m_doc->rgbScriptsCache()->load(dir)); - QVERIFY(m_doc->rgbScriptsCache()->names().size() >= 0); + QVERIFY(m_doc->rgbScriptsCache()->names().size() > 0); } void RGBScript_Test::script() @@ -211,7 +242,7 @@ void RGBScript_Test::runScripts() // Iterate the list of scripts QStringList names = m_doc->rgbScriptsCache()->names(); - foreach(QString name, names) + foreach (QString name, names) { qDebug() << "Evaluating script" << name; RGBScript s = m_doc->rgbScriptsCache()->script(name); @@ -334,7 +365,7 @@ void RGBScript_Test::runScripts() // Unknown, new and RGBScriptProperty::None are not valid QVERIFY(property.m_type == RGBScriptProperty::List || property.m_type == RGBScriptProperty::Range || - property.m_type == RGBScriptProperty::Integer || + property.m_type == RGBScriptProperty::Float || property.m_type == RGBScriptProperty::String); // Check property specificities switch (property.m_type) @@ -414,7 +445,7 @@ void RGBScript_Test::runScripts() } } break; - case RGBScriptProperty::Integer: + case RGBScriptProperty::Float: // Test with an integer value s.setProperty(property.m_name, QString::number(-1024)); qDebug() << " Readback: " << s.property(property.m_name); diff --git a/engine/test/rgbtext/CMakeLists.txt b/engine/test/rgbtext/CMakeLists.txt new file mode 100644 index 0000000000..ab83a0ec53 --- /dev/null +++ b/engine/test/rgbtext/CMakeLists.txt @@ -0,0 +1,19 @@ +add_executable(rgbtext_test WIN32 + rgbtext_test.cpp rgbtext_test.h +) +target_include_directories(rgbtext_test PRIVATE + ../../../plugins/interfaces + ../../src + ../mastertimer +) + +target_link_libraries(rgbtext_test PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Test + qlcplusengine +) + +# Consider using qt_generate_deploy_app_script() for app deployment if +# the project can use Qt 6.3. In that case rerun qmake2cmake with +# --min-qt-version=6.3. diff --git a/engine/test/scene/CMakeLists.txt b/engine/test/scene/CMakeLists.txt new file mode 100644 index 0000000000..53355076b3 --- /dev/null +++ b/engine/test/scene/CMakeLists.txt @@ -0,0 +1,22 @@ +add_executable(scene_test WIN32 + ../common/resource_paths.h + ../mastertimer/mastertimer_stub.cpp ../mastertimer/mastertimer_stub.h + scene_test.cpp scene_test.h +) +target_include_directories(scene_test PRIVATE + ../../../plugins/interfaces + ../../src + ../iopluginstub + ../mastertimer +) + +target_link_libraries(scene_test PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Test + qlcplusengine +) + +# Consider using qt_generate_deploy_app_script() for app deployment if +# the project can use Qt 6.3. In that case rerun qmake2cmake with +# --min-qt-version=6.3. diff --git a/engine/test/scene/scene_test.cpp b/engine/test/scene/scene_test.cpp index db3f0ecae5..93ac685cb8 100644 --- a/engine/test/scene/scene_test.cpp +++ b/engine/test/scene/scene_test.cpp @@ -623,7 +623,7 @@ void Scene_Test::flashUnflash() QVERIFY(timer.m_dmxSourceList.size() == 0); - s1->flash(&timer); + s1->flash(&timer, false, false); QVERIFY(timer.m_dmxSourceList.size() == 1); QVERIFY(s1->stopped() == true); QVERIFY(s1->flashing() == true); @@ -636,7 +636,7 @@ void Scene_Test::flashUnflash() QVERIFY(ua[0]->preGMValues()[2] == char(67)); doc->inputOutputMap()->releaseUniverses(false); - s1->flash(&timer); + s1->flash(&timer, false, false); QVERIFY(timer.m_dmxSourceList.size() == 1); QVERIFY(s1->stopped() == true); QVERIFY(s1->flashing() == true); diff --git a/engine/test/scenevalue/CMakeLists.txt b/engine/test/scenevalue/CMakeLists.txt new file mode 100644 index 0000000000..84ce9a67c6 --- /dev/null +++ b/engine/test/scenevalue/CMakeLists.txt @@ -0,0 +1,18 @@ +add_executable(scenevalue_test WIN32 + scenevalue_test.cpp scenevalue_test.h +) +target_include_directories(scenevalue_test PRIVATE + ../../../plugins/interfaces + ../../src +) + +target_link_libraries(scenevalue_test PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Test + qlcplusengine +) + +# Consider using qt_generate_deploy_app_script() for app deployment if +# the project can use Qt 6.3. In that case rerun qmake2cmake with +# --min-qt-version=6.3. diff --git a/engine/test/script/CMakeLists.txt b/engine/test/script/CMakeLists.txt new file mode 100644 index 0000000000..7369b75c27 --- /dev/null +++ b/engine/test/script/CMakeLists.txt @@ -0,0 +1,20 @@ +add_executable(script_test WIN32 + ../mastertimer/mastertimer_stub.cpp ../mastertimer/mastertimer_stub.h + script_test.cpp script_test.h +) +target_include_directories(script_test PRIVATE + ../../../plugins/interfaces + ../../src + ../mastertimer +) + +target_link_libraries(script_test PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Test + qlcplusengine +) + +# Consider using qt_generate_deploy_app_script() for app deployment if +# the project can use Qt 6.3. In that case rerun qmake2cmake with +# --min-qt-version=6.3. diff --git a/engine/test/sequence/CMakeLists.txt b/engine/test/sequence/CMakeLists.txt new file mode 100644 index 0000000000..9a2e7c6c33 --- /dev/null +++ b/engine/test/sequence/CMakeLists.txt @@ -0,0 +1,20 @@ +add_executable(sequence_test WIN32 + ../mastertimer/mastertimer_stub.cpp ../mastertimer/mastertimer_stub.h + sequence_test.cpp sequence_test.h +) +target_include_directories(sequence_test PRIVATE + ../../../plugins/interfaces + ../../src + ../mastertimer +) + +target_link_libraries(sequence_test PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Test + qlcplusengine +) + +# Consider using qt_generate_deploy_app_script() for app deployment if +# the project can use Qt 6.3. In that case rerun qmake2cmake with +# --min-qt-version=6.3. diff --git a/engine/test/show/CMakeLists.txt b/engine/test/show/CMakeLists.txt new file mode 100644 index 0000000000..fcbea53fb0 --- /dev/null +++ b/engine/test/show/CMakeLists.txt @@ -0,0 +1,18 @@ +add_executable(show_test WIN32 + show_test.cpp show_test.h +) +target_include_directories(show_test PRIVATE + ../../../plugins/interfaces + ../../src +) + +target_link_libraries(show_test PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Test + qlcplusengine +) + +# Consider using qt_generate_deploy_app_script() for app deployment if +# the project can use Qt 6.3. In that case rerun qmake2cmake with +# --min-qt-version=6.3. diff --git a/engine/test/show/show.pro b/engine/test/show/show.pro new file mode 100644 index 0000000000..2d117a8035 --- /dev/null +++ b/engine/test/show/show.pro @@ -0,0 +1,17 @@ +include(../../../variables.pri) +include(../../../coverage.pri) +TEMPLATE = app +LANGUAGE = C++ +TARGET = show_test + +QT += testlib +CONFIG -= app_bundle + +DEPENDPATH += ../../src +INCLUDEPATH += ../../../plugins/interfaces +INCLUDEPATH += ../../src +QMAKE_LIBDIR += ../../src +LIBS += -lqlcplusengine + +SOURCES += show_test.cpp +HEADERS += show_test.h diff --git a/engine/test/show/show_test.cpp b/engine/test/show/show_test.cpp new file mode 100644 index 0000000000..263d18578e --- /dev/null +++ b/engine/test/show/show_test.cpp @@ -0,0 +1,334 @@ +/* + Q Light Controller Plus - Unit test + show_test.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 "show_test.h" +#include "show.h" + +void Show_Test::initTestCase() +{ + m_doc = new Doc(this); +} + +void Show_Test::cleanupTestCase() +{ + delete m_doc; +} + +void Show_Test::defaults() +{ + Show s(m_doc); + + // check defaults + QCOMPARE(s.type(), Function::ShowType); + QCOMPARE(s.id(), Function::invalidId()); + QCOMPARE(s.name(), "New Show"); + QCOMPARE(s.attributes().count(), 0); +} + +void Show_Test::copy() +{ + Show show(m_doc); + show.setID(123); + show.setTimeDivision(Show::BPM_3_4, 123); + + Scene *scene = new Scene(m_doc); + m_doc->addFunction(scene); + + Track *t = new Track(123, &show); + t->setSceneID(456); + t->setName("Original track"); + + ShowFunction *sf = new ShowFunction(show.getLatestShowFunctionId()); + sf->setFunctionID(scene->id()); + sf->setStartTime(1000); + sf->setDuration(2000); + sf->setLocked(true); + + t->addShowFunction(sf); + show.addTrack(t); + + QVERIFY(show.showFunction(666) == NULL); + QVERIFY(show.showFunction(0) == sf); + + Show showCopy(m_doc); + showCopy.copyFrom(&show); + + QVERIFY(show.timeDivisionType() == Show::BPM_3_4); + QVERIFY(show.timeDivisionBPM() == 123); + QVERIFY(show.totalDuration() == 3000); + + QVERIFY(showCopy.getTracksCount() == show.getTracksCount()); + + Track *copyTrack = showCopy.tracks().first(); + + QVERIFY(copyTrack->getSceneID() == 456); + QVERIFY(copyTrack->name() == "Original track"); + QVERIFY(copyTrack->showFunctions().count() == 1); + + ShowFunction *copySF = copyTrack->showFunctions().first(); + QVERIFY(copySF->functionID() != scene->id()); + QVERIFY(copySF->startTime() == 1000); + QVERIFY(copySF->duration() == 2000); + QVERIFY(copySF->isLocked() == true); + + QVERIFY(show.contains(123) == true); + QVERIFY(show.contains(124) == false); + QVERIFY(show.contains(0) == true); + + QVERIFY(show.components().count() == 1); +} + +void Show_Test::timeDivision() +{ + Show s(m_doc); + QCOMPARE(s.timeDivisionType(), Show::Time); + QCOMPARE(s.timeDivisionBPM(), 120); + + s.setTimeDivision(Show::BPM_4_4, 111); + QCOMPARE(s.timeDivisionType(), Show::BPM_4_4); + QCOMPARE(s.timeDivisionBPM(), 111); + + QCOMPARE(s.beatsDivision(), 4); + s.setTimeDivisionType(Show::BPM_2_4); + QCOMPARE(s.beatsDivision(), 2); + s.setTimeDivisionType(Show::BPM_3_4); + QCOMPARE(s.beatsDivision(), 3); + s.setTimeDivisionType(Show::Time); + QCOMPARE(s.beatsDivision(), 0); + + QCOMPARE(s.stringToTempo("Time"), Show::Time); + QCOMPARE(s.stringToTempo("BPM_4_4"), Show::BPM_4_4); + QCOMPARE(s.stringToTempo("BPM_3_4"), Show::BPM_3_4); + QCOMPARE(s.stringToTempo("BPM_2_4"), Show::BPM_2_4); + + QCOMPARE(s.tempoToString(Show::Time), "Time"); + QCOMPARE(s.tempoToString(Show::BPM_4_4), "BPM_4_4"); + QCOMPARE(s.tempoToString(Show::BPM_3_4), "BPM_3_4"); + QCOMPARE(s.tempoToString(Show::BPM_2_4), "BPM_2_4"); +} + +void Show_Test::tracks() +{ + Show s(m_doc); + s.setID(123); + + QCOMPARE(s.tracks().count(), 0); + + Track *t = new Track(123, &s); + t->setName("First track"); + + Track *t2 = new Track(321, &s); + t2->setName("Second track"); + + QVERIFY(s.addTrack(t) == true); + QCOMPARE(s.getTracksCount(), 1); + QCOMPARE(s.tracks().count(), 1); + + QVERIFY(s.track(456) == NULL); + QVERIFY(s.getTrackFromSceneID(456) == NULL); + + QVERIFY(s.track(0) == t); + QVERIFY(s.getTrackFromSceneID(123) == t); + + // check sutomatic ID assignment + QVERIFY(t->id() == 0); + QVERIFY(t->showId() == 123); + + // check automatic attribute registration + QCOMPARE(s.attributes().count(), 1); + QVERIFY(s.attributes().at(0).m_name == "First track"); + + // add a second track and move it up + QVERIFY(s.addTrack(t2) == true); + QCOMPARE(s.getTracksCount(), 2); + + s.moveTrack(t2, -1); + QVERIFY(s.tracks().at(0)->name() == "Second track"); + QVERIFY(s.tracks().at(1)->name() == "First track"); + + // no change + s.moveTrack(t2, -1); + QVERIFY(s.tracks().at(0)->name() == "Second track"); + QVERIFY(s.tracks().at(1)->name() == "First track"); + + // move back as original + s.moveTrack(t, -1); + QVERIFY(s.tracks().at(0)->name() == "First track"); + QVERIFY(s.tracks().at(1)->name() == "Second track"); + + // check invalid track removal + QVERIFY(s.removeTrack(456) == false); + QCOMPARE(s.tracks().count(), 2); + + // check valid track removal + QVERIFY(s.removeTrack(0) == true); + QCOMPARE(s.tracks().count(), 1); + QCOMPARE(s.attributes().count(), 1); + + QVERIFY(s.removeTrack(1) == true); + QCOMPARE(s.tracks().count(), 0); + QCOMPARE(s.attributes().count(), 0); +} + +void Show_Test::duration() +{ + Show show(m_doc); + show.setID(123); + + Scene *scene = new Scene(m_doc); + m_doc->addFunction(scene); + + Track *t = new Track(123, &show); + ShowFunction *sf = new ShowFunction(show.getLatestShowFunctionId()); + sf->setFunctionID(scene->id()); + sf->setStartTime(1000); + sf->setDuration(2000); + + t->addShowFunction(sf); + show.addTrack(t); + + QVERIFY(show.totalDuration() == 3000); + +} + +void Show_Test::load() +{ + QBuffer buffer; + buffer.open(QIODevice::WriteOnly | QIODevice::Text); + QXmlStreamWriter xmlWriter(&buffer); + + xmlWriter.writeStartElement("Function"); + xmlWriter.writeAttribute("Type", "Show"); + + xmlWriter.writeStartElement("TimeDivision"); + xmlWriter.writeAttribute("Type", "BPM_2_4"); + xmlWriter.writeAttribute("BPM", "222"); + xmlWriter.writeEndElement(); + + xmlWriter.writeStartElement("Track"); + xmlWriter.writeAttribute("ID", "0"); + xmlWriter.writeAttribute("SceneID", "111"); + xmlWriter.writeAttribute("Name", "Read track 1"); + xmlWriter.writeAttribute("isMute", "0"); + xmlWriter.writeEndElement(); + + xmlWriter.writeStartElement("Track"); + xmlWriter.writeAttribute("ID", "1"); + xmlWriter.writeAttribute("SceneID", "222"); + xmlWriter.writeAttribute("Name", "Read track 2"); + xmlWriter.writeAttribute("isMute", "1"); + xmlWriter.writeEndElement(); + + xmlWriter.writeEndElement(); + + xmlWriter.writeEndDocument(); + xmlWriter.setDevice(NULL); + buffer.close(); + + buffer.open(QIODevice::ReadOnly | QIODevice::Text); + QXmlStreamReader xmlReader(&buffer); + xmlReader.readNextStartElement(); + + Show s(m_doc); + QVERIFY(s.loadXML(xmlReader) == true); + + QCOMPARE(s.timeDivisionType(), Show::BPM_2_4); + QCOMPARE(s.timeDivisionBPM(), 222); + + QCOMPARE(s.getTracksCount(), 2); + + Track *t, *t2; + t = s.track(111); + QVERIFY(t == NULL); + + t = s.track(0); + t2 = s.track(1); + + QCOMPARE(t->name(), "Read track 1"); + QVERIFY(t->getSceneID() == 111); + QCOMPARE(t->isMute(), false); + + QCOMPARE(t2->name(), "Read track 2"); + QVERIFY(t2->getSceneID() == 222); + QCOMPARE(t2->isMute(), true); +} + +void Show_Test::save() +{ + Show s(m_doc); + s.setID(123); + s.setName("Test Show"); + s.setTimeDivision(Show::BPM_3_4, 111); + + Track *t = new Track(456, &s); + t->setName("First track"); + + Track *t2 = new Track(789, &s); + t2->setName("Second track"); + t2->setMute(true); + + s.addTrack(t); + s.addTrack(t2); + + QBuffer buffer; + buffer.open(QIODevice::WriteOnly | QIODevice::Text); + QXmlStreamWriter xmlWriter(&buffer); + + QVERIFY(s.saveXML(&xmlWriter) == true); + xmlWriter.setDevice(NULL); + buffer.close(); + + buffer.open(QIODevice::ReadOnly | QIODevice::Text); + QXmlStreamReader xmlReader(&buffer); + + + xmlReader.readNextStartElement(); + QVERIFY(xmlReader.name().toString() == "Function"); + QVERIFY(xmlReader.attributes().value("Type").toString() == "Show"); + QVERIFY(xmlReader.attributes().value("ID").toString() == "123"); + QVERIFY(xmlReader.attributes().value("Name").toString() == "Test Show"); + + xmlReader.readNextStartElement(); + QVERIFY(xmlReader.name().toString() == "TimeDivision"); + QVERIFY(xmlReader.attributes().value("Type").toString() == "BPM_3_4"); + QVERIFY(xmlReader.attributes().value("BPM").toString() == "111"); + xmlReader.skipCurrentElement(); + + xmlReader.readNextStartElement(); + QVERIFY(xmlReader.name().toString() == "Track"); + QVERIFY(xmlReader.attributes().value("ID").toString() == "0"); + QVERIFY(xmlReader.attributes().value("SceneID").toString() == "456"); + QVERIFY(xmlReader.attributes().value("Name").toString() == "First track"); + QVERIFY(xmlReader.attributes().value("isMute").toString() == "0"); + xmlReader.skipCurrentElement(); + + xmlReader.readNextStartElement(); + QVERIFY(xmlReader.name().toString() == "Track"); + QVERIFY(xmlReader.attributes().value("ID").toString() == "1"); + QVERIFY(xmlReader.attributes().value("SceneID").toString() == "789"); + QVERIFY(xmlReader.attributes().value("Name").toString() == "Second track"); + QVERIFY(xmlReader.attributes().value("isMute").toString() == "1"); +} + + +QTEST_APPLESS_MAIN(Show_Test) diff --git a/engine/test/show/show_test.h b/engine/test/show/show_test.h new file mode 100644 index 0000000000..8420c79c82 --- /dev/null +++ b/engine/test/show/show_test.h @@ -0,0 +1,46 @@ +/* + Q Light Controller Plus - Unit test + show_test.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 SHOW_TEST_H +#define SHOW_TEST_H + +#include + +class Doc; + +class Show_Test : public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + void cleanupTestCase(); + void defaults(); + void copy(); + void timeDivision(); + void tracks(); + void duration(); + void load(); + void save(); + +private: + Doc *m_doc; +}; + +#endif diff --git a/engine/test/show/test.sh b/engine/test/show/test.sh new file mode 100755 index 0000000000..72f2946b83 --- /dev/null +++ b/engine/test/show/test.sh @@ -0,0 +1,4 @@ +#!/bin/sh +export LD_LIBRARY_PATH=../../src +export DYLD_FALLBACK_LIBRARY_PATH=../../src +./show_test diff --git a/engine/test/showfunction/CMakeLists.txt b/engine/test/showfunction/CMakeLists.txt new file mode 100644 index 0000000000..260fa205d5 --- /dev/null +++ b/engine/test/showfunction/CMakeLists.txt @@ -0,0 +1,18 @@ +add_executable(showfunction_test WIN32 + showfunction_test.cpp showfunction_test.h +) +target_include_directories(showfunction_test PRIVATE + ../../../plugins/interfaces + ../../src +) + +target_link_libraries(showfunction_test PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Test + qlcplusengine +) + +# Consider using qt_generate_deploy_app_script() for app deployment if +# the project can use Qt 6.3. In that case rerun qmake2cmake with +# --min-qt-version=6.3. diff --git a/engine/test/showfunction/showfunction.pro b/engine/test/showfunction/showfunction.pro new file mode 100644 index 0000000000..56427ecd8d --- /dev/null +++ b/engine/test/showfunction/showfunction.pro @@ -0,0 +1,17 @@ +include(../../../variables.pri) +include(../../../coverage.pri) +TEMPLATE = app +LANGUAGE = C++ +TARGET = showfunction_test + +QT += testlib +CONFIG -= app_bundle + +DEPENDPATH += ../../src +INCLUDEPATH += ../../../plugins/interfaces +INCLUDEPATH += ../../src +QMAKE_LIBDIR += ../../src +LIBS += -lqlcplusengine + +SOURCES += showfunction_test.cpp +HEADERS += showfunction_test.h diff --git a/engine/test/showfunction/showfunction_test.cpp b/engine/test/showfunction/showfunction_test.cpp new file mode 100644 index 0000000000..d6aa3405fa --- /dev/null +++ b/engine/test/showfunction/showfunction_test.cpp @@ -0,0 +1,127 @@ +/* + Q Light Controller Plus - Unit test + ShowFunction_Test.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 "showfunction_test.h" +#include "showfunction.h" + +void ShowFunction_Test::defaults() +{ + ShowFunction sf(123); + + // check defaults + QVERIFY(sf.id() == 123); + QVERIFY(sf.functionID() == Function::invalidId()); + QVERIFY(sf.startTime() == UINT_MAX); + QVERIFY(sf.duration() == 0); + QCOMPARE(sf.color(), QColor::Invalid); + QCOMPARE(sf.isLocked(), false); + QVERIFY(sf.intensityOverrideId() == -1); + + QCOMPARE(sf.defaultColor(Function::SceneType), QColor(100, 100, 100)); + QCOMPARE(sf.defaultColor(Function::ChaserType), QColor(85, 107, 128)); + QCOMPARE(sf.defaultColor(Function::AudioType), QColor(96, 128, 83)); + QCOMPARE(sf.defaultColor(Function::RGBMatrixType), QColor(101, 155, 155)); + QCOMPARE(sf.defaultColor(Function::EFXType), QColor(128, 60, 60)); + QCOMPARE(sf.defaultColor(Function::VideoType), QColor(147, 140, 20)); + + // set & check base params + sf.setFunctionID(123); + sf.setStartTime(445566); + sf.setDuration(778899); + sf.setColor(QColor(Qt::red)); + sf.setLocked(true); + sf.setIntensityOverrideId(468); + + QVERIFY(sf.functionID() == 123); + QVERIFY(sf.startTime() == 445566); + QVERIFY(sf.duration() == 778899); + QCOMPARE(sf.color(), QColor(Qt::red)); + QCOMPARE(sf.isLocked(), true); + QCOMPARE(sf.intensityOverrideId(), 468); +} + +void ShowFunction_Test::load() +{ + QBuffer buffer; + buffer.open(QIODevice::WriteOnly | QIODevice::Text); + QXmlStreamWriter xmlWriter(&buffer); + + xmlWriter.writeStartElement("ShowFunction"); + xmlWriter.writeAttribute("ID", "321"); + xmlWriter.writeAttribute("StartTime", "665544"); + xmlWriter.writeAttribute("Duration", "998877"); + xmlWriter.writeAttribute("Color", "#AABBCC"); + xmlWriter.writeAttribute("Locked", "1"); + xmlWriter.writeEndElement(); + + xmlWriter.writeEndDocument(); + xmlWriter.setDevice(NULL); + buffer.close(); + + buffer.open(QIODevice::ReadOnly | QIODevice::Text); + QXmlStreamReader xmlReader(&buffer); + xmlReader.readNextStartElement(); + + ShowFunction sf(456); + QVERIFY(sf.loadXML(xmlReader) == true); + + QVERIFY(sf.functionID() == 321); + QVERIFY(sf.startTime() == 665544); + QVERIFY(sf.duration() == 998877); + QCOMPARE(sf.color(), QColor(0xAA, 0xBB, 0xCC)); + QCOMPARE(sf.isLocked(), true); +} + +void ShowFunction_Test::save() +{ + ShowFunction sf(789); + sf.setFunctionID(123); + sf.setStartTime(445566); + sf.setDuration(778899); + sf.setColor(QColor(Qt::red)); + sf.setLocked(true); + sf.setIntensityOverrideId(468); + + QBuffer buffer; + buffer.open(QIODevice::WriteOnly | QIODevice::Text); + QXmlStreamWriter xmlWriter(&buffer); + + QVERIFY(sf.saveXML(&xmlWriter) == true); + + xmlWriter.setDevice(NULL); + buffer.close(); + + buffer.open(QIODevice::ReadOnly | QIODevice::Text); + QXmlStreamReader xmlReader(&buffer); + + xmlReader.readNextStartElement(); + QVERIFY(xmlReader.name().toString() == "ShowFunction"); + + QVERIFY(xmlReader.attributes().value("ID").toString() == "123"); + QVERIFY(xmlReader.attributes().value("StartTime").toString() == "445566"); + QVERIFY(xmlReader.attributes().value("Duration").toString() == "778899"); + QVERIFY(xmlReader.attributes().value("Color").toString() == "#ff0000"); + QVERIFY(xmlReader.attributes().value("Locked").toString() == "1"); +} + +QTEST_APPLESS_MAIN(ShowFunction_Test) diff --git a/engine/test/showfunction/showfunction_test.h b/engine/test/showfunction/showfunction_test.h new file mode 100644 index 0000000000..2546f9dfdc --- /dev/null +++ b/engine/test/showfunction/showfunction_test.h @@ -0,0 +1,35 @@ +/* + Q Light Controller Plus - Unit test + showfunction_test.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 SHOWFUNCTION_TEST_H +#define SHOWFUNCTION_TEST_H + +#include + +class ShowFunction_Test : public QObject +{ + Q_OBJECT + +private slots: + void defaults(); + void load(); + void save(); +}; + +#endif diff --git a/engine/test/showfunction/test.sh b/engine/test/showfunction/test.sh new file mode 100755 index 0000000000..449af2d05f --- /dev/null +++ b/engine/test/showfunction/test.sh @@ -0,0 +1,4 @@ +#!/bin/sh +export LD_LIBRARY_PATH=../../src +export DYLD_FALLBACK_LIBRARY_PATH=../../src +./showfunction_test diff --git a/engine/test/test.pro b/engine/test/test.pro index 5d50267f3e..822399f983 100644 --- a/engine/test/test.pro +++ b/engine/test/test.pro @@ -42,6 +42,9 @@ SUBDIRS += scene SUBDIRS += scenevalue SUBDIRS += script SUBDIRS += sequence +SUBDIRS += show +SUBDIRS += showfunction +SUBDIRS += track SUBDIRS += universe # Stubs diff --git a/engine/test/track/CMakeLists.txt b/engine/test/track/CMakeLists.txt new file mode 100644 index 0000000000..368bf5ca50 --- /dev/null +++ b/engine/test/track/CMakeLists.txt @@ -0,0 +1,18 @@ +add_executable(track_test WIN32 + track_test.cpp track_test.h +) +target_include_directories(track_test PRIVATE + ../../../plugins/interfaces + ../../src +) + +target_link_libraries(track_test PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Test + qlcplusengine +) + +# Consider using qt_generate_deploy_app_script() for app deployment if +# the project can use Qt 6.3. In that case rerun qmake2cmake with +# --min-qt-version=6.3. diff --git a/engine/test/track/test.sh b/engine/test/track/test.sh new file mode 100755 index 0000000000..04774ff97c --- /dev/null +++ b/engine/test/track/test.sh @@ -0,0 +1,4 @@ +#!/bin/sh +export LD_LIBRARY_PATH=../../src +export DYLD_FALLBACK_LIBRARY_PATH=../../src +./track_test diff --git a/engine/test/track/track.pro b/engine/test/track/track.pro new file mode 100644 index 0000000000..acd37ac46e --- /dev/null +++ b/engine/test/track/track.pro @@ -0,0 +1,17 @@ +include(../../../variables.pri) +include(../../../coverage.pri) +TEMPLATE = app +LANGUAGE = C++ +TARGET = track_test + +QT += testlib +CONFIG -= app_bundle + +DEPENDPATH += ../../src +INCLUDEPATH += ../../../plugins/interfaces +INCLUDEPATH += ../../src +QMAKE_LIBDIR += ../../src +LIBS += -lqlcplusengine + +SOURCES += track_test.cpp +HEADERS += track_test.h diff --git a/engine/test/track/track_test.cpp b/engine/test/track/track_test.cpp new file mode 100644 index 0000000000..404054ca49 --- /dev/null +++ b/engine/test/track/track_test.cpp @@ -0,0 +1,231 @@ +/* + Q Light Controller Plus - Unit test + track_test.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 "track_test.h" +#include "sequence.h" +#include "track.h" + +void Track_Test::initTestCase() +{ + m_doc = new Doc(this); + m_showFunc = new ShowFunction(123, this); +} + +void Track_Test::cleanupTestCase() +{ + delete m_showFunc; +} + +void Track_Test::defaults() +{ + Track t; + + // check defaults + QVERIFY(t.id() == Track::invalidId()); + QVERIFY(t.getSceneID() == Scene::invalidId()); + QVERIFY(t.showId() == Function::invalidId()); + QCOMPARE(t.name(), "New Track"); + + // set & check base params + t.setId(321); + t.setShowId(567); + t.setSceneID(890); + t.setName("Foo Track"); + + QVERIFY(t.id() == 321); + QVERIFY(t.showId() == 567); + QVERIFY(t.getSceneID() == 890); + QCOMPARE(t.name(), "Foo Track"); + + Track t2(123); + QVERIFY(t2.getSceneID() == 123); +} + +void Track_Test::mute() +{ + Track t; + QCOMPARE(t.isMute(), false); + + t.setMute(true); + QCOMPARE(t.isMute(), true); + + t.setMute(false); + QCOMPARE(t.isMute(), false); +} + +void Track_Test::showFunctions() +{ + Track t; + QCOMPARE(t.showFunctions().count(), 0); + + QVERIFY(t.createShowFunction(123) != nullptr); + QCOMPARE(t.showFunctions().count(), 1); + + QVERIFY(t.addShowFunction(nullptr) == false); + QVERIFY(t.addShowFunction(m_showFunc) == false); + QCOMPARE(t.showFunctions().count(), 1); + + m_showFunc->setFunctionID(123); + QVERIFY(t.addShowFunction(m_showFunc) == true); + QCOMPARE(t.showFunctions().count(), 2); + + QVERIFY(t.removeShowFunction(nullptr) == false); + QVERIFY(t.removeShowFunction(m_showFunc, true) == true); + QCOMPARE(t.showFunctions().count(), 1); +} + +void Track_Test::load() +{ + QBuffer buffer; + buffer.open(QIODevice::WriteOnly | QIODevice::Text); + QXmlStreamWriter xmlWriter(&buffer); + + xmlWriter.writeStartElement("Track"); + xmlWriter.writeAttribute("ID", "123"); + xmlWriter.writeAttribute("SceneID", "456"); + xmlWriter.writeAttribute("Name", "Sequence Cue"); + xmlWriter.writeAttribute("isMute", "1"); + + xmlWriter.writeStartElement("ShowFunction"); + xmlWriter.writeAttribute("ID", "789"); + xmlWriter.writeAttribute("Duration", "112233"); + xmlWriter.writeEndElement(); + + xmlWriter.writeEndElement(); + + xmlWriter.writeEndDocument(); + xmlWriter.setDevice(NULL); + buffer.close(); + + buffer.open(QIODevice::ReadOnly | QIODevice::Text); + QXmlStreamReader xmlReader(&buffer); + xmlReader.readNextStartElement(); + + Track t; + QVERIFY(t.loadXML(xmlReader) == true); + + QVERIFY(t.id() == 123); + QVERIFY(t.getSceneID() == 456); + QCOMPARE(t.name(), "Sequence Cue"); + QCOMPARE(t.isMute(), true); + + QVERIFY(t.showFunctions().count() == 1); + ShowFunction *sf = t.showFunctions().first(); + + QVERIFY(sf->functionID() == 789); + QVERIFY(sf->duration() == 112233); +} + +void Track_Test::functions() +{ + Track t; + t.setId(321); + t.setShowId(567); + t.setName("Foo Track"); + + Scene *s = new Scene(m_doc); + m_doc->addFunction(s, 10); + + Sequence *sq = new Sequence(m_doc); + sq->setBoundSceneID(890); + m_doc->addFunction(sq, 20); + + // invalid ShowFunction + ShowFunction *sf1 = new ShowFunction(123); + sf1->setFunctionID(666); + + // Valid ShowFunction + ShowFunction *sf2 = new ShowFunction(456); + sf2->setFunctionID(s->id()); + QVERIFY(sf2->color() == QColor()); + + ShowFunction *sf3 = new ShowFunction(789); + sf3->setFunctionID(sq->id()); + + t.addShowFunction(sf1); + t.addShowFunction(sf2); + t.addShowFunction(sf3); + + QVERIFY(t.showFunctions().count() == 3); + QVERIFY(t.showFunction(111) == NULL); + QVERIFY(t.showFunction(123) == sf1); + QVERIFY(t.showFunction(456) == sf2); + QVERIFY(t.showFunction(789) == sf3); + QVERIFY(t.postLoad(m_doc) == true); + + // invalid ShowFunction has been removed + QVERIFY(t.showFunctions().count() == 2); + // invalid color has been fixed + QVERIFY(sf2->color() == QColor(100, 100, 100)); + // check SceneID set from sequence + //QVERIFY(t.getSceneID() == 890); + + //QVERIFY(t.contains(m_doc, 890) == true); + QVERIFY(t.contains(m_doc, 666) == false); + QVERIFY(t.contains(m_doc, 10) == true); + QVERIFY(t.contains(m_doc, 20) == true); + + QVERIFY(t.components().count() == 2); + QVERIFY(t.components().at(0) == 10); + QVERIFY(t.components().at(1) == 20); +} + +void Track_Test::save() +{ + Track t(321); + t.setId(654); + t.setName("Audio Cue"); + t.setMute(true); + + m_showFunc = new ShowFunction(123, this); + m_showFunc->setFunctionID(987); + + t.addShowFunction(m_showFunc); + + QBuffer buffer; + buffer.open(QIODevice::WriteOnly | QIODevice::Text); + QXmlStreamWriter xmlWriter(&buffer); + + QVERIFY(t.saveXML(&xmlWriter) == true); + + xmlWriter.setDevice(NULL); + buffer.close(); + + buffer.open(QIODevice::ReadOnly | QIODevice::Text); + QXmlStreamReader xmlReader(&buffer); + + xmlReader.readNextStartElement(); + QVERIFY(xmlReader.name().toString() == "Track"); + + QVERIFY(xmlReader.attributes().value("ID").toString() == "654"); + QVERIFY(xmlReader.attributes().value("SceneID").toString() == "321"); + QVERIFY(xmlReader.attributes().value("Name").toString() == "Audio Cue"); + QVERIFY(xmlReader.attributes().value("isMute").toString() == "1"); + + xmlReader.readNextStartElement(); + QVERIFY(xmlReader.name().toString() == "ShowFunction"); + QVERIFY(xmlReader.attributes().value("ID").toString() == "987"); +} + + +QTEST_APPLESS_MAIN(Track_Test) diff --git a/engine/test/track/track_test.h b/engine/test/track/track_test.h new file mode 100644 index 0000000000..6ee26dc5bb --- /dev/null +++ b/engine/test/track/track_test.h @@ -0,0 +1,47 @@ +/* + Q Light Controller Plus - Unit test + track_test.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 TRACK_TEST_H +#define TRACK_TEST_H + +#include + +class Doc; +class ShowFunction; + +class Track_Test : public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + void cleanupTestCase(); + void defaults(); + void mute(); + void showFunctions(); + void load(); + void functions(); + void save(); + +private: + Doc *m_doc; + ShowFunction *m_showFunc; +}; + +#endif diff --git a/engine/test/universe/CMakeLists.txt b/engine/test/universe/CMakeLists.txt new file mode 100644 index 0000000000..37f34aec63 --- /dev/null +++ b/engine/test/universe/CMakeLists.txt @@ -0,0 +1,18 @@ +add_executable(universe_test WIN32 + universe_test.cpp universe_test.h +) +target_include_directories(universe_test PRIVATE + ../../../plugins/interfaces + ../../src +) + +target_link_libraries(universe_test PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Test + qlcplusengine +) + +# Consider using qt_generate_deploy_app_script() for app deployment if +# the project can use Qt 6.3. In that case rerun qmake2cmake with +# --min-qt-version=6.3. diff --git a/engine/test/universe/universe_test.cpp b/engine/test/universe/universe_test.cpp index 2b5b11a5d4..de42bfbdb9 100644 --- a/engine/test/universe/universe_test.cpp +++ b/engine/test/universe/universe_test.cpp @@ -49,19 +49,19 @@ void Universe_Test::initial() QCOMPARE(m_uni->hasChanged(), false); QCOMPARE(m_uni->passthrough(), false); QVERIFY(m_uni->inputPatch() == NULL); - QVERIFY(m_uni->outputPatch() == NULL); + QVERIFY(m_uni->outputPatch(0) == NULL); QVERIFY(m_uni->feedbackPatch() == NULL); QVERIFY(m_uni->intensityChannels().isEmpty()); QByteArray const preGM = m_uni->preGMValues(); - QCOMPARE(preGM.count(), 512); + QCOMPARE(preGM.length(), 512); QByteArray const *postGM = m_uni->postGMValues(); QVERIFY(postGM != NULL); - QCOMPARE(postGM->count(), 512); + QCOMPARE(postGM->length(), 512); - for(ushort i = 0; i < 512; ++i) + for (ushort i = 0; i < 512; ++i) { QVERIFY(m_uni->channelCapabilities(i) == Universe::Undefined); QCOMPARE(int(preGM.at(i)), 0); @@ -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 new file mode 100644 index 0000000000..c88a5e5dd4 --- /dev/null +++ b/fixtureeditor/CMakeLists.txt @@ -0,0 +1,637 @@ +set(module_name "qlcplus-fixtureeditor") + +set(TS_FILES + fixtureeditor_fi_FI.ts + fixtureeditor_fr_FR.ts + fixtureeditor_de_DE.ts + fixtureeditor_es_ES.ts + fixtureeditor_it_IT.ts + fixtureeditor_nl_NL.ts + fixtureeditor_cz_CZ.ts + fixtureeditor_pt_BR.ts + fixtureeditor_ca_ES.ts + fixtureeditor_ja_JP.ts +) + +if(QT_VERSION_MAJOR GREATER 5) + qt_add_translation(QM_FILES ${TS_FILES}) +else() + qt5_add_translation(QM_FILES ${TS_FILES}) +endif() + +add_executable(${module_name} WIN32 + ../ui/src/aboutbox.cpp ../ui/src/aboutbox.h ../ui/src/aboutbox.ui + ../ui/src/apputil.cpp ../ui/src/apputil.h + ../ui/src/docbrowser.cpp ../ui/src/docbrowser.h + addchannelsdialog.cpp addchannelsdialog.h addchannelsdialog.ui + app.cpp app.h + capabilitywizard.cpp capabilitywizard.h capabilitywizard.ui + editchannel.cpp editchannel.h editchannel.ui + edithead.cpp edithead.h edithead.ui + editmode.cpp editmode.h editmode.ui + editphysical.cpp editphysical.h editphysical.ui + fixtureeditor.cpp fixtureeditor.h fixtureeditor.ui + main.cpp + util.h + ${QM_FILES} +) + +target_include_directories(${module_name} PRIVATE + ../engine/src + ../plugins/interfaces + ../ui/src +) + +target_link_libraries(${module_name} PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Widgets + qlcplusengine +) + + +# Resources: +set_source_files_properties("../ui/src/../../resources/icons/png/add_dump.png" + PROPERTIES QT_RESOURCE_ALIAS "add_dump.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/animation.png" + PROPERTIES QT_RESOURCE_ALIAS "animation.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/attach.png" + PROPERTIES QT_RESOURCE_ALIAS "attach.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/audio.png" + PROPERTIES QT_RESOURCE_ALIAS "audio.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/audioinput.png" + PROPERTIES QT_RESOURCE_ALIAS "audioinput.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/autostart.png" + PROPERTIES QT_RESOURCE_ALIAS "autostart.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/back.png" + PROPERTIES QT_RESOURCE_ALIAS "back.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/beam.png" + PROPERTIES QT_RESOURCE_ALIAS "beam.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/blackout.png" + PROPERTIES QT_RESOURCE_ALIAS "blackout.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/blind.png" + PROPERTIES QT_RESOURCE_ALIAS "blind.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/button.png" + PROPERTIES QT_RESOURCE_ALIAS "button.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/buttonmatrix.png" + PROPERTIES QT_RESOURCE_ALIAS "buttonmatrix.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/chaser.png" + PROPERTIES QT_RESOURCE_ALIAS "chaser.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/check.png" + PROPERTIES QT_RESOURCE_ALIAS "check.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/checkbox_empty.png" + PROPERTIES QT_RESOURCE_ALIAS "checkbox_empty.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/checkbox_full.png" + PROPERTIES QT_RESOURCE_ALIAS "checkbox_full.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/clock.png" + PROPERTIES QT_RESOURCE_ALIAS "clock.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/collection.png" + PROPERTIES QT_RESOURCE_ALIAS "collection.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/color.png" + PROPERTIES QT_RESOURCE_ALIAS "color.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/colorwheel.png" + PROPERTIES QT_RESOURCE_ALIAS "colorwheel.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/configure.png" + PROPERTIES QT_RESOURCE_ALIAS "configure.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/cuelist.png" + PROPERTIES QT_RESOURCE_ALIAS "cuelist.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/current.png" + PROPERTIES QT_RESOURCE_ALIAS "current.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/delete.png" + PROPERTIES QT_RESOURCE_ALIAS "delete.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/design.png" + PROPERTIES QT_RESOURCE_ALIAS "design.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/detach.png" + PROPERTIES QT_RESOURCE_ALIAS "detach.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/dimmer.png" + PROPERTIES QT_RESOURCE_ALIAS "dimmer.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/diptool.png" + PROPERTIES QT_RESOURCE_ALIAS "diptool.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/down.png" + PROPERTIES QT_RESOURCE_ALIAS "down.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/ds_border.png" + PROPERTIES QT_RESOURCE_ALIAS "ds_border.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/ds_bottom.png" + PROPERTIES QT_RESOURCE_ALIAS "ds_bottom.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/ds_off.png" + PROPERTIES QT_RESOURCE_ALIAS "ds_off.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/ds_on.png" + PROPERTIES QT_RESOURCE_ALIAS "ds_on.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/ds_top.png" + PROPERTIES QT_RESOURCE_ALIAS "ds_top.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/edit.png" + PROPERTIES QT_RESOURCE_ALIAS "edit.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/edit_add.png" + PROPERTIES QT_RESOURCE_ALIAS "edit_add.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/edit_remove.png" + PROPERTIES QT_RESOURCE_ALIAS "edit_remove.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/editclear.png" + PROPERTIES QT_RESOURCE_ALIAS "editclear.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/editcopy.png" + PROPERTIES QT_RESOURCE_ALIAS "editcopy.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/editcopyall.png" + PROPERTIES QT_RESOURCE_ALIAS "editcopyall.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/editcut.png" + PROPERTIES QT_RESOURCE_ALIAS "editcut.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/editdelete.png" + PROPERTIES QT_RESOURCE_ALIAS "editdelete.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/editpaste.png" + PROPERTIES QT_RESOURCE_ALIAS "editpaste.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/effect.png" + PROPERTIES QT_RESOURCE_ALIAS "effect.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/efx.png" + PROPERTIES QT_RESOURCE_ALIAS "efx.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/exit.png" + PROPERTIES QT_RESOURCE_ALIAS "exit.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/expand.png" + PROPERTIES QT_RESOURCE_ALIAS "expand.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/fade.png" + PROPERTIES QT_RESOURCE_ALIAS "fade.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/fan.png" + PROPERTIES QT_RESOURCE_ALIAS "fan.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/fileclose.png" + PROPERTIES QT_RESOURCE_ALIAS "fileclose.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/fileexport.png" + PROPERTIES QT_RESOURCE_ALIAS "fileexport.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/fileimport.png" + PROPERTIES QT_RESOURCE_ALIAS "fileimport.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/filenew.png" + PROPERTIES QT_RESOURCE_ALIAS "filenew.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/fileopen.png" + PROPERTIES QT_RESOURCE_ALIAS "fileopen.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/filesave.png" + PROPERTIES QT_RESOURCE_ALIAS "filesave.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/filesaveas.png" + PROPERTIES QT_RESOURCE_ALIAS "filesaveas.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/fixture.png" + PROPERTIES QT_RESOURCE_ALIAS "fixture.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/flash.png" + PROPERTIES QT_RESOURCE_ALIAS "flash.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/flower.png" + PROPERTIES QT_RESOURCE_ALIAS "flower.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/folder.png" + PROPERTIES QT_RESOURCE_ALIAS "folder.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/fontcolor.png" + PROPERTIES QT_RESOURCE_ALIAS "fontcolor.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/fonts.png" + PROPERTIES QT_RESOURCE_ALIAS "fonts.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/forward.png" + PROPERTIES QT_RESOURCE_ALIAS "forward.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/frame.png" + PROPERTIES QT_RESOURCE_ALIAS "frame.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/frameraised.png" + PROPERTIES QT_RESOURCE_ALIAS "frameraised.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/framesunken.png" + PROPERTIES QT_RESOURCE_ALIAS "framesunken.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/fullscreen.png" + PROPERTIES QT_RESOURCE_ALIAS "fullscreen.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/function.png" + PROPERTIES QT_RESOURCE_ALIAS "function.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/global.png" + PROPERTIES QT_RESOURCE_ALIAS "global.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/gobo.png" + PROPERTIES QT_RESOURCE_ALIAS "gobo.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/grid.png" + PROPERTIES QT_RESOURCE_ALIAS "grid.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/group.png" + PROPERTIES QT_RESOURCE_ALIAS "group.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/hazer.png" + PROPERTIES QT_RESOURCE_ALIAS "hazer.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/help.png" + PROPERTIES QT_RESOURCE_ALIAS "help.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/image.png" + PROPERTIES QT_RESOURCE_ALIAS "image.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/input.png" + PROPERTIES QT_RESOURCE_ALIAS "input.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/input_output.png" + PROPERTIES QT_RESOURCE_ALIAS "input_output.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/intensity.png" + PROPERTIES QT_RESOURCE_ALIAS "intensity.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/key_bindings.png" + PROPERTIES QT_RESOURCE_ALIAS "key_bindings.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/knob.png" + PROPERTIES QT_RESOURCE_ALIAS "knob.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/label.png" + PROPERTIES QT_RESOURCE_ALIAS "label.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/laser.png" + PROPERTIES QT_RESOURCE_ALIAS "laser.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/ledbar_beams.png" + PROPERTIES QT_RESOURCE_ALIAS "ledbar_beams.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/ledbar_pixels.png" + PROPERTIES QT_RESOURCE_ALIAS "ledbar_pixels.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/liveedit.png" + PROPERTIES QT_RESOURCE_ALIAS "liveedit.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/liveedit_vc.png" + PROPERTIES QT_RESOURCE_ALIAS "liveedit_vc.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/lock.png" + PROPERTIES QT_RESOURCE_ALIAS "lock.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/monitor.png" + PROPERTIES QT_RESOURCE_ALIAS "monitor.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/movinghead.png" + PROPERTIES QT_RESOURCE_ALIAS "movinghead.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/operate.png" + PROPERTIES QT_RESOURCE_ALIAS "operate.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/other.png" + PROPERTIES QT_RESOURCE_ALIAS "other.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/pan.png" + PROPERTIES QT_RESOURCE_ALIAS "pan.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/panic.png" + PROPERTIES QT_RESOURCE_ALIAS "panic.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/player_pause.png" + PROPERTIES QT_RESOURCE_ALIAS "player_pause.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/player_play.png" + PROPERTIES QT_RESOURCE_ALIAS "player_play.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/player_stop.png" + PROPERTIES QT_RESOURCE_ALIAS "player_stop.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/prism.png" + PROPERTIES QT_RESOURCE_ALIAS "prism.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/qlcplus-fixtureeditor.png" + PROPERTIES QT_RESOURCE_ALIAS "qlcplus-fixtureeditor.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/qlcplus.png" + PROPERTIES QT_RESOURCE_ALIAS "qlcplus.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/qt.png" + PROPERTIES QT_RESOURCE_ALIAS "qt.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/rainbow.png" + PROPERTIES QT_RESOURCE_ALIAS "rainbow.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/random.png" + PROPERTIES QT_RESOURCE_ALIAS "random.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/record.png" + PROPERTIES QT_RESOURCE_ALIAS "record.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/refresh.png" + PROPERTIES QT_RESOURCE_ALIAS "refresh.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/remap.png" + PROPERTIES QT_RESOURCE_ALIAS "remap.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/resize.png" + PROPERTIES QT_RESOURCE_ALIAS "resize.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/rgbmatrix.png" + PROPERTIES QT_RESOURCE_ALIAS "rgbmatrix.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/rgbpanel.png" + PROPERTIES QT_RESOURCE_ALIAS "rgbpanel.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/scanner.png" + PROPERTIES QT_RESOURCE_ALIAS "scanner.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/scene.png" + PROPERTIES QT_RESOURCE_ALIAS "scene.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/script.png" + PROPERTIES QT_RESOURCE_ALIAS "script.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/sequence.png" + PROPERTIES QT_RESOURCE_ALIAS "sequence.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/show.png" + PROPERTIES QT_RESOURCE_ALIAS "show.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/shutter.png" + PROPERTIES QT_RESOURCE_ALIAS "shutter.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/slider.png" + PROPERTIES QT_RESOURCE_ALIAS "slider.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/slidermatrix.png" + PROPERTIES QT_RESOURCE_ALIAS "slidermatrix.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/smoke.png" + PROPERTIES QT_RESOURCE_ALIAS "smoke.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/soloframe.png" + PROPERTIES QT_RESOURCE_ALIAS "soloframe.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/speed.png" + PROPERTIES QT_RESOURCE_ALIAS "speed.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/square.png" + PROPERTIES QT_RESOURCE_ALIAS "square.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/star.png" + PROPERTIES QT_RESOURCE_ALIAS "star.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/strobe.png" + PROPERTIES QT_RESOURCE_ALIAS "strobe.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/tabview.png" + PROPERTIES QT_RESOURCE_ALIAS "tabview.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/tilt.png" + PROPERTIES QT_RESOURCE_ALIAS "tilt.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/uncheck.png" + PROPERTIES QT_RESOURCE_ALIAS "uncheck.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/undo.png" + PROPERTIES QT_RESOURCE_ALIAS "undo.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/ungroup.png" + PROPERTIES QT_RESOURCE_ALIAS "ungroup.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/unlock.png" + PROPERTIES QT_RESOURCE_ALIAS "unlock.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/up.png" + PROPERTIES QT_RESOURCE_ALIAS "up.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/video.png" + PROPERTIES QT_RESOURCE_ALIAS "video.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/virtualconsole.png" + PROPERTIES QT_RESOURCE_ALIAS "virtualconsole.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/wizard.png" + PROPERTIES QT_RESOURCE_ALIAS "wizard.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/wizard_256.png" + PROPERTIES QT_RESOURCE_ALIAS "wizard_256.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/xypad-point-blue.png" + PROPERTIES QT_RESOURCE_ALIAS "xypad-point-blue.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/xypad-point-yellow.png" + PROPERTIES QT_RESOURCE_ALIAS "xypad-point-yellow.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/xypad-point.png" + PROPERTIES QT_RESOURCE_ALIAS "xypad-point.png" +) +set_source_files_properties("../ui/src/../../resources/icons/png/xypad.png" + PROPERTIES QT_RESOURCE_ALIAS "xypad.png" +) +set(qlcui_resource_files + "../ui/src/../../resources/icons/png/add_dump.png" + "../ui/src/../../resources/icons/png/animation.png" + "../ui/src/../../resources/icons/png/attach.png" + "../ui/src/../../resources/icons/png/audio.png" + "../ui/src/../../resources/icons/png/audioinput.png" + "../ui/src/../../resources/icons/png/autostart.png" + "../ui/src/../../resources/icons/png/back.png" + "../ui/src/../../resources/icons/png/beam.png" + "../ui/src/../../resources/icons/png/blackout.png" + "../ui/src/../../resources/icons/png/blind.png" + "../ui/src/../../resources/icons/png/button.png" + "../ui/src/../../resources/icons/png/buttonmatrix.png" + "../ui/src/../../resources/icons/png/chaser.png" + "../ui/src/../../resources/icons/png/check.png" + "../ui/src/../../resources/icons/png/checkbox_empty.png" + "../ui/src/../../resources/icons/png/checkbox_full.png" + "../ui/src/../../resources/icons/png/clock.png" + "../ui/src/../../resources/icons/png/collection.png" + "../ui/src/../../resources/icons/png/color.png" + "../ui/src/../../resources/icons/png/colorwheel.png" + "../ui/src/../../resources/icons/png/configure.png" + "../ui/src/../../resources/icons/png/cuelist.png" + "../ui/src/../../resources/icons/png/current.png" + "../ui/src/../../resources/icons/png/delete.png" + "../ui/src/../../resources/icons/png/design.png" + "../ui/src/../../resources/icons/png/detach.png" + "../ui/src/../../resources/icons/png/dimmer.png" + "../ui/src/../../resources/icons/png/diptool.png" + "../ui/src/../../resources/icons/png/down.png" + "../ui/src/../../resources/icons/png/ds_border.png" + "../ui/src/../../resources/icons/png/ds_bottom.png" + "../ui/src/../../resources/icons/png/ds_off.png" + "../ui/src/../../resources/icons/png/ds_on.png" + "../ui/src/../../resources/icons/png/ds_top.png" + "../ui/src/../../resources/icons/png/edit.png" + "../ui/src/../../resources/icons/png/edit_add.png" + "../ui/src/../../resources/icons/png/edit_remove.png" + "../ui/src/../../resources/icons/png/editclear.png" + "../ui/src/../../resources/icons/png/editcopy.png" + "../ui/src/../../resources/icons/png/editcopyall.png" + "../ui/src/../../resources/icons/png/editcut.png" + "../ui/src/../../resources/icons/png/editdelete.png" + "../ui/src/../../resources/icons/png/editpaste.png" + "../ui/src/../../resources/icons/png/effect.png" + "../ui/src/../../resources/icons/png/efx.png" + "../ui/src/../../resources/icons/png/exit.png" + "../ui/src/../../resources/icons/png/expand.png" + "../ui/src/../../resources/icons/png/fade.png" + "../ui/src/../../resources/icons/png/fan.png" + "../ui/src/../../resources/icons/png/fileclose.png" + "../ui/src/../../resources/icons/png/fileexport.png" + "../ui/src/../../resources/icons/png/fileimport.png" + "../ui/src/../../resources/icons/png/filenew.png" + "../ui/src/../../resources/icons/png/fileopen.png" + "../ui/src/../../resources/icons/png/filesave.png" + "../ui/src/../../resources/icons/png/filesaveas.png" + "../ui/src/../../resources/icons/png/fixture.png" + "../ui/src/../../resources/icons/png/flash.png" + "../ui/src/../../resources/icons/png/flower.png" + "../ui/src/../../resources/icons/png/folder.png" + "../ui/src/../../resources/icons/png/fontcolor.png" + "../ui/src/../../resources/icons/png/fonts.png" + "../ui/src/../../resources/icons/png/forward.png" + "../ui/src/../../resources/icons/png/frame.png" + "../ui/src/../../resources/icons/png/frameraised.png" + "../ui/src/../../resources/icons/png/framesunken.png" + "../ui/src/../../resources/icons/png/fullscreen.png" + "../ui/src/../../resources/icons/png/function.png" + "../ui/src/../../resources/icons/png/global.png" + "../ui/src/../../resources/icons/png/gobo.png" + "../ui/src/../../resources/icons/png/grid.png" + "../ui/src/../../resources/icons/png/group.png" + "../ui/src/../../resources/icons/png/hazer.png" + "../ui/src/../../resources/icons/png/help.png" + "../ui/src/../../resources/icons/png/image.png" + "../ui/src/../../resources/icons/png/input.png" + "../ui/src/../../resources/icons/png/input_output.png" + "../ui/src/../../resources/icons/png/intensity.png" + "../ui/src/../../resources/icons/png/key_bindings.png" + "../ui/src/../../resources/icons/png/knob.png" + "../ui/src/../../resources/icons/png/label.png" + "../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/liveedit.png" + "../ui/src/../../resources/icons/png/liveedit_vc.png" + "../ui/src/../../resources/icons/png/lock.png" + "../ui/src/../../resources/icons/png/monitor.png" + "../ui/src/../../resources/icons/png/movinghead.png" + "../ui/src/../../resources/icons/png/operate.png" + "../ui/src/../../resources/icons/png/other.png" + "../ui/src/../../resources/icons/png/pan.png" + "../ui/src/../../resources/icons/png/panic.png" + "../ui/src/../../resources/icons/png/player_pause.png" + "../ui/src/../../resources/icons/png/player_play.png" + "../ui/src/../../resources/icons/png/player_stop.png" + "../ui/src/../../resources/icons/png/prism.png" + "../ui/src/../../resources/icons/png/qlcplus-fixtureeditor.png" + "../ui/src/../../resources/icons/png/qlcplus.png" + "../ui/src/../../resources/icons/png/qt.png" + "../ui/src/../../resources/icons/png/rainbow.png" + "../ui/src/../../resources/icons/png/random.png" + "../ui/src/../../resources/icons/png/record.png" + "../ui/src/../../resources/icons/png/refresh.png" + "../ui/src/../../resources/icons/png/remap.png" + "../ui/src/../../resources/icons/png/resize.png" + "../ui/src/../../resources/icons/png/rgbmatrix.png" + "../ui/src/../../resources/icons/png/rgbpanel.png" + "../ui/src/../../resources/icons/png/scanner.png" + "../ui/src/../../resources/icons/png/scene.png" + "../ui/src/../../resources/icons/png/script.png" + "../ui/src/../../resources/icons/png/sequence.png" + "../ui/src/../../resources/icons/png/show.png" + "../ui/src/../../resources/icons/png/shutter.png" + "../ui/src/../../resources/icons/png/slider.png" + "../ui/src/../../resources/icons/png/slidermatrix.png" + "../ui/src/../../resources/icons/png/smoke.png" + "../ui/src/../../resources/icons/png/soloframe.png" + "../ui/src/../../resources/icons/png/speed.png" + "../ui/src/../../resources/icons/png/square.png" + "../ui/src/../../resources/icons/png/star.png" + "../ui/src/../../resources/icons/png/strobe.png" + "../ui/src/../../resources/icons/png/tabview.png" + "../ui/src/../../resources/icons/png/tilt.png" + "../ui/src/../../resources/icons/png/uncheck.png" + "../ui/src/../../resources/icons/png/undo.png" + "../ui/src/../../resources/icons/png/ungroup.png" + "../ui/src/../../resources/icons/png/unlock.png" + "../ui/src/../../resources/icons/png/up.png" + "../ui/src/../../resources/icons/png/video.png" + "../ui/src/../../resources/icons/png/virtualconsole.png" + "../ui/src/../../resources/icons/png/wizard.png" + "../ui/src/../../resources/icons/png/wizard_256.png" + "../ui/src/../../resources/icons/png/xypad-point-blue.png" + "../ui/src/../../resources/icons/png/xypad-point-yellow.png" + "../ui/src/../../resources/icons/png/xypad-point.png" + "../ui/src/../../resources/icons/png/xypad.png" +) + +if(WIN32) + target_sources(${module_name} PRIVATE + fixtureeditor.rc + ) +endif() + +if(qmlui OR (QT_VERSION_MAJOR GREATER 5)) + target_link_libraries(${module_name} PRIVATE + Qt${QT_MAJOR_VERSION}::Qml + ) + + qt_add_resources(${module_name} "qlcui" + PREFIX + "/" + BASE + "../ui/src" + FILES + ${qlcui_resource_files} + ) +else() + target_link_libraries(${module_name} PRIVATE + Qt${QT_MAJOR_VERSION}::Script + ) + qt5_add_resources(qlcplusui_resource_files ../ui/src/qlcui.qrc) + target_sources(${module_name} PRIVATE + ${qlcplusui_resource_files} + ) +endif() + +install(TARGETS ${module_name} + DESTINATION ${INSTALLROOT}/${BINDIR} +) + diff --git a/fixtureeditor/addchannelsdialog.cpp b/fixtureeditor/addchannelsdialog.cpp index abcdd247ee..487d357e21 100644 --- a/fixtureeditor/addchannelsdialog.cpp +++ b/fixtureeditor/addchannelsdialog.cpp @@ -17,10 +17,14 @@ limitations under the License. */ +#include + #include "qlcchannel.h" #include "addchannelsdialog.h" #include "ui_addchannelsdialog.h" +#define SETTINGS_GEOMETRY "addchannelsdialog/geometry" + AddChannelsDialog::AddChannelsDialog(QList allList, QVector modeList, QWidget *parent) : QDialog(parent) , m_channelsList(allList) @@ -37,6 +41,11 @@ AddChannelsDialog::AddChannelsDialog(QList allList, QVectorsetDropIndicatorShown(true); m_modeTree->setSelectionMode(QAbstractItemView::ExtendedSelection); + QSettings settings; + QVariant geometrySettings = settings.value(SETTINGS_GEOMETRY); + if (geometrySettings.isValid() == true) + restoreGeometry(geometrySettings.toByteArray()); + connect(m_addChannel, SIGNAL(clicked()), this, SLOT(slotAddChannel())); connect(m_removeChannel, SIGNAL(clicked()), @@ -47,7 +56,8 @@ AddChannelsDialog::AddChannelsDialog(QList allList, QVector AddChannelsDialog::getModeChannelsList() @@ -98,7 +108,7 @@ void AddChannelsDialog::slotAddChannel() if (selection.count() == 0) return; - foreach(QTreeWidgetItem *item, selection) + foreach (QTreeWidgetItem *item, selection) { QTreeWidgetItem *newItem = item->clone(); m_modeTree->addTopLevelItem(newItem); @@ -112,7 +122,7 @@ void AddChannelsDialog::slotRemoveChannel() if (selection.count() == 0) return; - foreach(QTreeWidgetItem *item, selection) + foreach (QTreeWidgetItem *item, selection) { QTreeWidgetItem *newItem = item->clone(); m_allTree->addTopLevelItem(newItem); 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/editchannel.cpp b/fixtureeditor/editchannel.cpp index 1719e3df48..3818f03090 100644 --- a/fixtureeditor/editchannel.cpp +++ b/fixtureeditor/editchannel.cpp @@ -42,7 +42,6 @@ #include "capabilitywizard.h" #include "editchannel.h" #include "util.h" -#include "app.h" #define SETTINGS_GEOMETRY "editchannel/geometry" #define PROP_PTR Qt::UserRole @@ -241,7 +240,7 @@ void EditChannel::slotPresetActivated(int index) } } - foreach(QLCCapability *cap, m_channel->capabilities()) + foreach (QLCCapability *cap, m_channel->capabilities()) m_channel->removeCapability(cap); m_channel->addPresetCapability(); diff --git a/fixtureeditor/edithead.cpp b/fixtureeditor/edithead.cpp index 79198986e9..b73d064e02 100644 --- a/fixtureeditor/edithead.cpp +++ b/fixtureeditor/edithead.cpp @@ -21,12 +21,15 @@ #include #include #include +#include #include "qlcfixturehead.h" #include "qlcfixturemode.h" #include "qlcchannel.h" #include "edithead.h" +#define SETTINGS_GEOMETRY "edithead/geometry" + EditHead::EditHead(QWidget* parent, const QLCFixtureHead& head, const QLCFixtureMode* mode) : QDialog(parent) , m_head(head) @@ -40,12 +43,19 @@ EditHead::EditHead(QWidget* parent, const QLCFixtureHead& head, const QLCFixture fillChannelTree(mode); + QSettings settings; + QVariant geometrySettings = settings.value(SETTINGS_GEOMETRY); + if (geometrySettings.isValid() == true) + restoreGeometry(geometrySettings.toByteArray()); + connect(m_tree, SIGNAL(itemChanged(QTreeWidgetItem*,int)), this, SLOT(slotItemChanged(QTreeWidgetItem*,int))); } EditHead::~EditHead() { + QSettings settings; + settings.setValue(SETTINGS_GEOMETRY, saveGeometry()); } QLCFixtureHead EditHead::head() const diff --git a/fixtureeditor/editmode.cpp b/fixtureeditor/editmode.cpp index 774a2b3a9d..4cff8549bb 100644 --- a/fixtureeditor/editmode.cpp +++ b/fixtureeditor/editmode.cpp @@ -43,7 +43,6 @@ #include "editmode.h" #include "edithead.h" #include "util.h" -#include "app.h" #define KSettingsGeometry "editmode/geometry" @@ -226,17 +225,6 @@ void EditMode::slotLowerChannelClicked() selectChannel(ch->name()); } -void EditMode::slotActsOnChannelChanged(QLCChannel *newActsOnChannel) -{ - QLCChannel *channel = currentChannel(); - - if (channel == NULL) - return; - - m_mode->updateActsOnChannel(channel, newActsOnChannel); - //refreshChannelList(); -} - void EditMode::refreshChannelList() { m_channelList->clear(); @@ -245,8 +233,7 @@ void EditMode::refreshChannelList() { QTreeWidgetItem *item = new QTreeWidgetItem(m_channelList); QLCChannel *ch = m_mode->channel(i); - - int actsOnChannelIndex = m_mode->channels().indexOf(m_mode->actsOnChannelsList().value(ch)); + quint32 actsOnChannelIndex = m_mode->channelActsOn(i); Q_ASSERT(ch != NULL); @@ -269,10 +256,8 @@ void EditMode::refreshChannelList() QComboBox *comboBox = new QComboBox(this); comboBox->addItems(comboList); - if (actsOnChannelIndex >= 0) + if (actsOnChannelIndex != QLCChannel::invalid()) comboBox->setCurrentIndex(actsOnChannelIndex + 1); - else - comboBox->setCurrentIndex(0); m_channelList->setItemWidget(item, COL_ACTS_ON, comboBox); @@ -311,14 +296,10 @@ void EditMode::selectChannel(const QString &name) void EditMode::setActsOnChannel(int index) { - int channelNumber = index - 1; - - QLCChannel *actsOnChannel = NULL; - - if (channelNumber >= 0) - actsOnChannel = m_mode->channels().at(channelNumber); + quint32 chIndex = m_mode->channelNumber(currentChannel()); + quint32 actsOnChannel = index == 0 ? QLCChannel::invalid() : index - 1; - slotActsOnChannelChanged(actsOnChannel); + m_mode->setChannelActsOn(chIndex, actsOnChannel); } /**************************************************************************** diff --git a/fixtureeditor/editmode.h b/fixtureeditor/editmode.h index 45a581893d..7f9f4cf70e 100644 --- a/fixtureeditor/editmode.h +++ b/fixtureeditor/editmode.h @@ -71,7 +71,6 @@ protected slots: void slotRemoveChannelClicked(); void slotRaiseChannelClicked(); void slotLowerChannelClicked(); - void slotActsOnChannelChanged(QLCChannel *); protected: void refreshChannelList(); diff --git a/fixtureeditor/fixtureeditor.cpp b/fixtureeditor/fixtureeditor.cpp index 57b675ece3..2dee2f4be9 100644 --- a/fixtureeditor/fixtureeditor.cpp +++ b/fixtureeditor/fixtureeditor.cpp @@ -41,7 +41,6 @@ #include "qlccapability.h" #include "qlcphysical.h" #include "qlcchannel.h" -#include "qlcconfig.h" #include "qlcfile.h" #ifdef Q_WS_X11 @@ -517,12 +516,10 @@ void QLCFixtureEditor::slotRemoveChannel() for (int i = 0; i < m_modeList->topLevelItemCount(); ++i) { QTreeWidgetItem *item = m_modeList->topLevelItem(i); - QLCFixtureMode *mode = (QLCFixtureMode*)item->data(MODE_COL_NAME, PROP_PTR).toULongLong(); - mode->actsOnChannelsList().remove(channel); - - QLCChannel *mainChannel = mode->actsOnChannelsList().value(channel); - mode->actsOnChannelsList()[mainChannel] = NULL; + quint32 chIndex = mode->channelNumber(channel); + if (chIndex != QLCChannel::invalid()) + mode->setChannelActsOn(chIndex, QLCChannel::invalid()); } refreshModeList(); @@ -842,6 +839,8 @@ void QLCFixtureEditor::slotEditMode() if (mode == NULL) return; + QString origName = mode->name(); + EditMode em(this, mode); connect(&em, SIGNAL(copyToClipboard(QLCPhysical)), this, SLOT(slotCopyPhysicalClipboard(QLCPhysical))); @@ -851,7 +850,17 @@ void QLCFixtureEditor::slotEditMode() item = m_modeList->currentItem(); updateModeItem(mode, item); + + // if mode name has changed, update + // all aliases referring to the old name + if (mode->name() != origName) + { + updateAliasModeName(origName, mode->name()); + refreshAliasTree(); + } + refreshAliasModes(); + setModified(); m_modeList->header()->resizeSections(QHeaderView::ResizeToContents); } @@ -1067,6 +1076,32 @@ void QLCFixtureEditor::refreshAliasModes() refreshAliasModeChannels(); } +void QLCFixtureEditor::updateAliasModeName(QString oldName, QString newName) +{ + QListIterator it(m_fixtureDef->channels()); + while (it.hasNext() == true) + { + QLCChannel *channel = it.next(); + foreach (QLCCapability *cap, channel->capabilities()) + { + if (cap->preset() != QLCCapability::Alias) + continue; + + QList aliasList = cap->aliasList(); + for (int i = 0; i < aliasList.count(); i++) + { + AliasInfo info = aliasList.at(i); + if (info.targetMode == oldName) + { + info.targetMode = newName; + aliasList.replace(i, info); + } + } + cap->replaceAliases(aliasList); + } + } +} + void QLCFixtureEditor::refreshAliasModeChannels() { m_modeChannels->clear(); diff --git a/fixtureeditor/fixtureeditor.h b/fixtureeditor/fixtureeditor.h index e8c24071d6..43383e626c 100644 --- a/fixtureeditor/fixtureeditor.h +++ b/fixtureeditor/fixtureeditor.h @@ -127,6 +127,7 @@ protected slots: void slotAddAliasClicked(); void slotRemoveAliasClicked(); void refreshAliasModes(); + void updateAliasModeName(QString oldName, QString newName); void refreshAliasModeChannels(); protected: diff --git a/fixtureeditor/fixtureeditor.ui b/fixtureeditor/fixtureeditor.ui index 28ac58265a..e928a4397a 100644 --- a/fixtureeditor/fixtureeditor.ui +++ b/fixtureeditor/fixtureeditor.ui @@ -223,7 +223,7 @@ - LED Bar (Beams) + LED Bar (Beams) @@ -232,7 +232,7 @@ - LED Bar (Pixels) + LED Bar (Pixels) diff --git a/fixtureeditor/fixtureeditor_ca_ES.ts b/fixtureeditor/fixtureeditor_ca_ES.ts index 614a96cc61..0939b18f32 100644 --- a/fixtureeditor/fixtureeditor_ca_ES.ts +++ b/fixtureeditor/fixtureeditor_ca_ES.ts @@ -56,108 +56,108 @@ App - + Unrecognized file extension: %1 Extensió d'arxiu desconeguda: %1 - + Fixture loading failed Error al carregar el Fixture - + Unable to load fixture definition: - No es pot carregar la definició del fixture: + No es pot carregar la definició del fixture: - + &New &Nou - + CTRL+N File|New CTRL+N - + &Open &Obrir - + CTRL+O File|Open CTRL+O - + &Save &Desar - + CTRL+S File|Save CTRL+S - + Save &As... Desar &com... - + CTRL+SHIFT+S File|Save As... CTRL+SHIFT+S - + &Quit &Sortir - + CTRL+Q File|Quit CTRL+Q - + Index Índex - + SHIFT+F1 Help|Index SHIFT+F1 - + About Fixture Definition Editor... Quant a l'Editor de definicions de Fixture... - + About Qt... Quant a QT... - + &File &Arxiu - + &Help &Ajuda - + Open a fixture definition Obrir una definició de fixture @@ -233,32 +233,32 @@ DocBrowser - + %1 - Document Browser %1 - Cercador de documents - + Backward Enrere - + Forward Endavant - + Index Índex - + About Qt Quant a QT - + Close this window Tanca aquesta finestra @@ -273,7 +273,7 @@ <font color="red"><b>Capability overlapping detected. Please fix.</b></font> - + <font color="red"><b>S'ha detectat una superposició de capacitats. Siu us plau corregiu-ho.</b></font> @@ -283,18 +283,18 @@ Default value - + Valor per defecte Type - Tipus + Tipus Preset - + Preset @@ -304,32 +304,32 @@ Role - + Rol Color 1 - + Color 1 Color 2 - + Color 2 Value 1 - + Valor 1 Value 2 - + Valor 2 Preview - + Vista Prèvia @@ -387,7 +387,7 @@ Edit Channel: - Editar Canal: + Editar Canal: @@ -414,7 +414,9 @@ Some gobos are missing: - Alguns gobos falten: + Alguns gobos falten: + + @@ -458,84 +460,89 @@ Canals - + Number Nombre - + Name Nom - + Add channel(s) to this mode Afegir canal(s) a aques mode - + Remove the selected channel Treure el canal seleccionat - + Raise the selected channel Pujar el canal seleccionat - + Lower the selected channel Baixar el canal seleccionat - + + Acts On + Actua Sobre + + + Heads Capçals - + Head Capçal - + Compose a new head Compondre un nou capçal - + Remove the selected head Treure el capçal seleccionat - + Edit the channels that belong to the selected head Editar els canals que pertanyen al capçal seleccionat - + Raise the selected head Pujar el capçal sleccionat - + Lower the selected head Baixar el capçal seleccionat - + Physical Físic - + Use global settings - + Usa ajustos globals - + Override global settings - + Sobreescriu la configuració global @@ -601,7 +608,7 @@ Head(s) - + Capçal(s) @@ -637,7 +644,8 @@ Layout (Columns x Rows) - + Disposició +(Columnes x Files) @@ -712,16 +720,6 @@ The general type of this fixture El tipus general d'aquest fixture - - - LED Bar (Beams) - - - - - LED Bar (Pixels) - - Author @@ -817,58 +815,58 @@ Aliases - + Alias In mode - + En mode Add a new alias - + Afegeix un àlies nou with - + Amb replace - + reemplaça Alias - + Àlies Mode - + Mode Base channel - + Canal Base Override channel - + Sobreescriu el canal Remove the currently selected alias - + Elimina els àlies seleccionats Physical - Físic + Físic @@ -933,25 +931,25 @@ No es pot desar el fixture. - + Channel already exists El canal ja existeix - + A channel by the name "%1" already exists! Un canal amb el nombre "%1" ja existeix! - + Channel has no name El canal no te nom - + You must give the channel a descriptive name! Te que donar un nom descriptiu al canal! @@ -961,94 +959,94 @@ No es pot desar el fixture. Esteu segur que vol eliminar el canal: %1 ? - + Are you sure you wish to remove mode: %1? Eteu segur que vol eliminar el mode: %1 ? - - - + + + Edit Editar - - + + Copy Copiar - - + + Paste Enganxar - - - + + + Remove Eliminar - + Channels Canals - - + + Unable to add mode Impossible afegir un mode - + Another mode by that name already exists Un altre mode amb aquest nom ja existeix - + You must give a name to the mode Necessita donar un nom al mode - + Remove Mode Eliminar mode - + Rename new mode Reanomenar el nou mode - + Give a unique name for the mode Trieu un nom únic per el mode - + Copy of %1 Copia de %1 - + Invalid name Nom incorrecte - + Another mode by that name already exists. Un altre mode amb aquest nom ja existeix. - + Clone Clonar - + Modes Modes diff --git a/fixtureeditor/fixtureeditor_cz_CZ.ts b/fixtureeditor/fixtureeditor_cz_CZ.ts index 3bc1450863..b5846b9ab6 100644 --- a/fixtureeditor/fixtureeditor_cz_CZ.ts +++ b/fixtureeditor/fixtureeditor_cz_CZ.ts @@ -57,108 +57,108 @@ App - + Unrecognized file extension: %1 Neznámý typ souboru: %1 - + Fixture loading failed Načítání zařízení selhalo - + Unable to load fixture definition: Nelze načíst definici zařízení: - + &New &Nový - + CTRL+N File|New CTRL+N - + &Open &Otevřít - + CTRL+O File|Open CTRL+O - + &Save &Uložit - + CTRL+S File|Save CTRL+S - + Save &As... Uložit &Jako... - + CTRL+SHIFT+S File|Save As... CTRL+SHIFT+S - + &Quit &Quit - + CTRL+Q File|Quit CTRL+Q - + Index Obsah - + SHIFT+F1 Help|Index SHIFT+F1 - + About Fixture Definition Editor... O aplikaci Editor definice zařízení... - + About Qt... O aplikaci Qt... - + &File &Soubor - + &Help &Nápověda - + Open a fixture definition Otevřít definici zařízení @@ -234,32 +234,32 @@ DocBrowser - + %1 - Document Browser %1 - Prohlížeč dokumentů - + Backward Dozadu - + Forward Dopředu - + Index Obsah - + About Qt O aplikaci Qt - + Close this window @@ -459,82 +459,87 @@ Kanály - + Number Číslo - + Name Název - + Add channel(s) to this mode Přidat kanál(y) do tohoto režimu - + Remove the selected channel Odebrat zvolený kanál - + Raise the selected channel Posunout kanál výše - + Lower the selected channel Posunout kanál níže - + + Acts On + + + + Heads Hlavy - + Head Hlava - + Compose a new head Zvolte novou hlavu - + Remove the selected head Odebrat zvolenou hlavu - + Edit the channels that belong to the selected head Upravit kanály které patří ke zvolené hlavě - + Raise the selected head Posunout zvolenou hlavu výše - + Lower the selected head Posunout zvolenou hlavu níže - + Physical Fyzické vlastnosti - + Use global settings - + Override global settings @@ -713,16 +718,6 @@ The general type of this fixture Obecný typ tohoto zařízení - - - LED Bar (Beams) - - - - - LED Bar (Pixels) - - Author @@ -934,25 +929,25 @@ Nelze uložit zařízení. - + Channel already exists Kanál již existuje - + A channel by the name "%1" already exists! Kanál se jménem "%1" již existuje! - + Channel has no name Kanál nemá jméno - + You must give the channel a descriptive name! Musíte pojmenovat tento kanál! @@ -962,94 +957,94 @@ Nelze uložit zařízení. Opravdu chcete odebrat kanál: %1? - + Are you sure you wish to remove mode: %1? Opravdu chcete odebrat režim: %1? - - - + + + Edit Upravit - - + + Copy Kopírovat - - + + Paste Vložit - - - + + + Remove Odebrat - + Channels Kanály - - + + Unable to add mode Režim nelze přidat - + Another mode by that name already exists Režim stejného jména již existuje - + You must give a name to the mode Musíte zadat nejaké jméno pro tento režim - + Remove Mode Odebrat režim - + Rename new mode Přejmenovat nový režim - + Give a unique name for the mode Zadejte jedinečné jméno pro tento režim - + Copy of %1 Kopie z %1 - + Invalid name Neplatné jméno - + Another mode by that name already exists. Jiný režim tohoto jména již existuje. - + Clone Duplikovat - + Modes Režimy diff --git a/fixtureeditor/fixtureeditor_de_DE.ts b/fixtureeditor/fixtureeditor_de_DE.ts index e07c9d5a53..907bfdf002 100644 --- a/fixtureeditor/fixtureeditor_de_DE.ts +++ b/fixtureeditor/fixtureeditor_de_DE.ts @@ -57,109 +57,109 @@ App - + Unrecognized file extension: %1 Unbekannte Dateinamenerweiterung: %1 - + Fixture loading failed Laden fehlgeschlagen - + Unable to load fixture definition: Gerätedefinition konnte nicht geladen werden: - + &New &Neu - + CTRL+N File|New CTRL+N - + &Open &Öffnen - + CTRL+O File|Open CTRL+O - + &Save &Speichern - + CTRL+S File|Save CTRL+S - + Save &As... Save &As Speichern &unter… - + CTRL+SHIFT+S File|Save As... CTRL+SHIFT+S - + &Quit &Beenden - + CTRL+Q File|Quit CTRL+Q - + Index Handbuch - + SHIFT+F1 Help|Index F1 - + About Fixture Definition Editor... Über Fixture Definition Editor… - + About Qt... Über Qt… - + &File &Datei - + &Help &Hilfe - + Open a fixture definition Eine Gerätedefinition öffnen @@ -235,32 +235,32 @@ DocBrowser - + %1 - Document Browser %1 - Handbuch - + Backward Zurück - + Forward Vorwärts - + Index Inhaltsverzeichnis - + About Qt Über Qt - + Close this window Dieses Fenster schließen @@ -465,82 +465,87 @@ Kanäle - + Number Nummer - + Name Name - + Add channel(s) to this mode Kanal/Kanäle zum Modus hinzufügen - + Remove the selected channel Ausgewählten Kanal entfernen - + Raise the selected channel Ausgewählten Kanal nach oben verschieben - + Lower the selected channel Ausgewählten Kanal nach unten verschieben - + + Acts On + Beeinflusst + + + Heads Heads - + Head Head - + Compose a new head Neuen Head erstellen - + Remove the selected head Ausgewählten Head entfernen - + Edit the channels that belong to the selected head Zugewiesene Kanäle des ausgewählten Heads bearbeiten - + Raise the selected head Ausgewählten Head nach oben verschieben - + Lower the selected head Ausgewählten Head nach unten verschieben - + Physical Physisch - + Use global settings Globale Einstellungen verwenden - + Override global settings Globale Einstellungen überschreiben @@ -630,10 +635,6 @@ Max Degrees Max. Grad - - Prism faces - Prisma-Flächen - Pan Max Degrees @@ -724,16 +725,6 @@ The general type of this fixture Der Gerätetyp (z.B.: Moving Head) - - - LED Bar (Beams) - LED Bar (Strahlen) - - - - LED Bar (Pixels) - LED Bar (Pixel) - Author @@ -947,25 +938,25 @@ Kann Gerät nicht speichern. - + Channel already exists Kanal existiert bereits - + A channel by the name "%1" already exists! Ein Kanal mit dem Namen "%1" existiert bereits! - + Channel has no name Kanal hat keinen Namen - + You must give the channel a descriptive name! Du musst dem Kanal einen beschreibenden Namen geben! @@ -975,94 +966,94 @@ Kann Gerät nicht speichern. Bist du dir sicher, dass du den Kanal %1 entfernen möchtest? - + Are you sure you wish to remove mode: %1? Bist du dir sicher, dass du den Modus %1 entfernen möchtest? - - - + + + Edit Bearbeiten - - + + Copy Kopieren - - + + Paste Einfügen - - - + + + Remove Entfernen - + Channels Kanäle - - + + Unable to add mode Modus kann nicht hinzugefügt werden - + Another mode by that name already exists Ein anderer Modus mit diesem Namen existiert bereits - + You must give a name to the mode Du musst dem Modus einen Namen geben - + Remove Mode Modus entfernen - + Rename new mode Neuen Modus umbenennen - + Give a unique name for the mode Gib dem Modus einen eindeutigen Namen - + Copy of %1 Kopie von %1 - + Invalid name Ungültiger Name - + Another mode by that name already exists. Ein anderer Modus mit diesem Namen existiert bereits. - + Clone Klonen - + Modes Modi diff --git a/fixtureeditor/fixtureeditor_es_ES.ts b/fixtureeditor/fixtureeditor_es_ES.ts index 31837b3c7d..79e9ee0d75 100644 --- a/fixtureeditor/fixtureeditor_es_ES.ts +++ b/fixtureeditor/fixtureeditor_es_ES.ts @@ -57,108 +57,108 @@ App - + Unrecognized file extension: %1 Extensión de archivo no reconocida: %1 - + Fixture loading failed Fallo al cargar Fixture - + Unable to load fixture definition: - Imposible cargar definición de fixture: + Imposible cargar definición de fixture: - + &New &Nuevo - + CTRL+N File|New - + &Open &Abrir - + CTRL+O File|Open - + &Save &Guardar - + CTRL+S File|Save - + Save &As... Guardar &como... - + CTRL+SHIFT+S File|Save As... - + &Quit &Salir - + CTRL+Q File|Quit - + Index Índice - + SHIFT+F1 Help|Index - + About Fixture Definition Editor... Sobre el Editor de Definiciones de Fixture... - + About Qt... Sobre QT... - + &File &Archivo - + &Help A&yuda - + Open a fixture definition Abrir una definición de fixture @@ -234,32 +234,32 @@ DocBrowser - + %1 - Document Browser %1 - Buscador de Documentos - + Backward Atrás - + Forward Adelante - + Index Índice - + About Qt Acerca de Qt - + Close this window Cerrar esta ventana @@ -461,82 +461,87 @@ Canales - + Number Número - + Name Nombre - + Add channel(s) to this mode Añadir canal(es) a este modo - + Remove the selected channel Quitar el canal seleccionado - + Raise the selected channel Subir el canal seleccionado - + Lower the selected channel Bajar el canal seleccionado - + + Acts On + Actua Sobre + + + Heads Cabezas - + Head Cabeza - + Compose a new head Componer una nueva cabeza - + Remove the selected head Quitar la cabeza seleccionada - + Edit the channels that belong to the selected head Editar los canales que pertenecen a la cabeza seleccionada - + Raise the selected head Subir la cabeza seleccionada - + Lower the selected head Bajar la cabeza seleccionado - + Physical Físico - + Use global settings Usar ajustes globales - + Override global settings Anular ajustes globales @@ -626,10 +631,6 @@ Max Degrees Grados Máximos - - Prism faces - Facetas del prisma - Pan Max Degrees @@ -644,7 +645,8 @@ Layout (Columns x Rows) - + Disposición +(Columnas x Filas) @@ -719,16 +721,6 @@ The general type of this fixture El tipo general de este fixture - - - LED Bar (Beams) - - - - - LED Bar (Pixels) - - Author @@ -942,25 +934,25 @@ No se puede guardar el fixture. - + Channel already exists El canal ya existe - + A channel by the name "%1" already exists! ¡Un canal con el nombre "%1" ya existe! - + Channel has no name El canal no tiene nombre - + You must give the channel a descriptive name! ¡Tiene que dar al canal un nombre descriptivo! @@ -970,94 +962,94 @@ No se puede guardar el fixture. ¿Está seguro que quiere quitar el canal: %1? - + Are you sure you wish to remove mode: %1? ¿Está seguro que quiere quitar el modo: %1? - - - + + + Edit Editar - - + + Copy Copiar - - + + Paste Pegar - - - + + + Remove Eliminar - + Channels Canales - - + + Unable to add mode Imposible añadir modo - + Another mode by that name already exists Otro modo con ese nombre ya existe - + You must give a name to the mode Necesita dar un nombre al modo - + Remove Mode Eliminar modo - + Rename new mode Renombrar el nuevo modo - + Give a unique name for the mode Elija un nombre único para el modo - + Copy of %1 Copia de %1 - + Invalid name Nombre incorrecto - + Another mode by that name already exists. Otro modo con ese nombre ya existe. - + Clone Clonar - + Modes Modos diff --git a/fixtureeditor/fixtureeditor_fi_FI.ts b/fixtureeditor/fixtureeditor_fi_FI.ts index 025420ba03..b98c4b8c47 100644 --- a/fixtureeditor/fixtureeditor_fi_FI.ts +++ b/fixtureeditor/fixtureeditor_fi_FI.ts @@ -57,108 +57,108 @@ App - + Unrecognized file extension: %1 - + Fixture loading failed Valaisimen lataus epäonnistui - + Unable to load fixture definition: Valaisinmäärittelyä ei löydy: - + &New &Uusi - + CTRL+N File|New - + &Open A&vaa - + CTRL+O File|Open - + &Save &Tallenna - + CTRL+S File|Save - + Save &As... Tallenna nimellä... - + CTRL+SHIFT+S File|Save As... - + &Quit &Poistu - + CTRL+Q File|Quit - + Index Hakemisto - + SHIFT+F1 Help|Index - + About Fixture Definition Editor... Tietoja Fixture Definition Editor-sovelluksesta... - + About Qt... Tietoja Qt:sta... - + &File &Tiedosto - + &Help &Apua - + Open a fixture definition Avaa valaisinmäärittely @@ -234,32 +234,32 @@ DocBrowser - + %1 - Document Browser %1 - Dokumenttiselain - + Backward Taaksepäin - + Forward Eteenpäin - + Index Hakemisto - + About Qt - + Close this window @@ -459,82 +459,87 @@ Kanavat - + Number Numero - + Name Nimi - + Add channel(s) to this mode Lisää kanavia tähän tilaan - + Remove the selected channel Poista valittu kanava - + Raise the selected channel Nosta valittua kanavaa - + Lower the selected channel Laske valittua kanavaa - + + Acts On + + + + Heads - + Head - + Compose a new head - + Remove the selected head - + Edit the channels that belong to the selected head - + Raise the selected head - + Lower the selected head - + Physical Fyysiset ominaisuudet - + Use global settings - + Override global settings @@ -713,16 +718,6 @@ The general type of this fixture Valaisimen yleisluontoinen tyyppi - - - LED Bar (Beams) - - - - - LED Bar (Pixels) - - Author @@ -935,25 +930,25 @@ Valaisinta ei voida tallentaa. - + Channel already exists Kanava on jo olemassa - + A channel by the name "%1" already exists! Kanava nimeltä "%1" on jo olemassa! - + Channel has no name Kanavalla ei ole nimeä - + You must give the channel a descriptive name! Kanavalle täytyy antaa jokin kuvaava nimi! @@ -963,94 +958,94 @@ Valaisinta ei voida tallentaa. - + Are you sure you wish to remove mode: %1? - - - + + + Edit Muokkaa - - + + Copy Kopioi - - + + Paste Liitä - - - + + + Remove Poista - + Channels Kanavat - - + + Unable to add mode Tilaa ei voida lisätä - + Another mode by that name already exists Toinen samanniminen tila on jo olemassa - + You must give a name to the mode Tilalle on annettava nimi - + Remove Mode Poista tila - + Rename new mode Nimeä tila uudelleen - + Give a unique name for the mode Anna tilalle yksilöllinen nimi - + Copy of %1 %1:n kopio - + Invalid name Epäkelpo nimi - + Another mode by that name already exists. Toinen samanniminen tila on jo olemassa. - + Clone Kloonaa - + Modes Tilat diff --git a/fixtureeditor/fixtureeditor_fr_FR.ts b/fixtureeditor/fixtureeditor_fr_FR.ts index 7d0b5c42fb..cc2c3fd394 100644 --- a/fixtureeditor/fixtureeditor_fr_FR.ts +++ b/fixtureeditor/fixtureeditor_fr_FR.ts @@ -57,108 +57,108 @@ App - + Unrecognized file extension: %1 Extension de fichier inconnue : %1 - + Fixture loading failed Échec du chargement de l'appareil - + Unable to load fixture definition: Impossible de charger la définition de l'appareil : - + &New &Nouveau - + CTRL+N File|New - + &Open &Ouvrir - + CTRL+O File|Open - + &Save Enregistrer (&S) - + CTRL+S File|Save - + Save &As... Enregistrer sous... (&A) - + CTRL+SHIFT+S File|Save As... - + &Quit &Quitter - + CTRL+Q File|Quit - + Index Aide - + SHIFT+F1 Help|Index - + About Fixture Definition Editor... À propos de Fixture Definition Editor... - + About Qt... À propos de Qt... - + &File &Fichier - + &Help Aide (&H) - + Open a fixture definition Ouvrir la définition d'un appareil @@ -234,32 +234,32 @@ DocBrowser - + %1 - Document Browser %1 - Explorateur de documents - + Backward Avant - + Forward Arrière - + Index Aide - + About Qt À propos de Qt - + Close this window Fermer cette fenêtre @@ -461,82 +461,87 @@ Canaux - + Number Numéro - + Name Nom - + Add channel(s) to this mode Ajouter un ou plusieurs canaux à ce mode - + Remove the selected channel Supprimer le canal sélectionné - + Raise the selected channel Monter le canal sélectionné - + Lower the selected channel Descendre le canal sélectionné - + + Acts On + + + + Heads Têtes - + Head Tête - + Compose a new head Définir une nouvelle tête - + Remove the selected head Supprimer la tête sélectionnée - + Edit the channels that belong to the selected head Éditer les canaux qui appartiennent à la tête sélectionnée - + Raise the selected head Monter la tête sélectionnée - + Lower the selected head Descendre la tête sélectionnée - + Physical Données physiques - + Use global settings Utiliser les paramètres globaux - + Override global settings Ne pas utiliser les paramètres globaux @@ -716,16 +721,6 @@ The general type of this fixture Le type de l'appareil - - - LED Bar (Beams) - Barre LED (Beams) - - - - LED Bar (Pixels) - Barre LED (Pixels) - Author @@ -939,26 +934,26 @@ Impossible d'enregistrer l'appareil. - + Channel already exists Canal existant - + A channel by the name "%1" already exists! Un canal avec le nom "%1" existe déjà ! - + Channel has no name Nom du canal manquant - + You must give the channel a descriptive name! Veuillez donner un nom au canal ! @@ -969,94 +964,94 @@ Impossible d'enregistrer l'appareil. Êtes-vous sûr(e) de vouloir supprimer le canal "%1" ? - + Are you sure you wish to remove mode: %1? Êtes-vous sûr(e) de vouloir supprimer le mode "%1" ? - - - + + + Edit Éditer - - + + Copy Copier - - + + Paste Coller - - - + + + Remove Supprimer - + Channels Canaux - - + + Unable to add mode Impossible d'ajouter un mode - + Another mode by that name already exists Un mode portant ce nom existe déjà - + You must give a name to the mode Veuillez donner un nom au mode - + Remove Mode Suppression du mode - + Rename new mode Nommage du nouveau mode - + Give a unique name for the mode Donnez un nom unique au nouveau mode - + Copy of %1 Copie de %1 - + Invalid name Nom invalide - + Another mode by that name already exists. Un mode portant ce nom existe déjà. - + Clone Dupliquer - + Modes Modes diff --git a/fixtureeditor/fixtureeditor_it_IT.ts b/fixtureeditor/fixtureeditor_it_IT.ts index 411efd8484..7268e9cce2 100644 --- a/fixtureeditor/fixtureeditor_it_IT.ts +++ b/fixtureeditor/fixtureeditor_it_IT.ts @@ -57,108 +57,108 @@ App - + Unrecognized file extension: %1 Estensione non riconosciuta: %1 - + Fixture loading failed Errore nel caricamento del Fixture - + Unable to load fixture definition: Impossibile caricare la definizione della fixture: - + &New &Nuovo - + CTRL+N File|New - + &Open &Apri - + CTRL+O File|Open - + &Save &Salvare - + CTRL+S File|Save - + Save &As... Salva &Con Nome... - + CTRL+SHIFT+S File|Save As... - + &Quit &Quit - + CTRL+Q File|Quit - + Index Indice - + SHIFT+F1 Help|Index - + About Fixture Definition Editor... Informazioni sull'Editor di Fixture... - + About Qt... Informazioni su QT... - + &File &File - + &Help &Aiuto - + Open a fixture definition Apri un file-Fixture @@ -234,32 +234,32 @@ DocBrowser - + %1 - Document Browser %1 - Esplora Risorse - + Backward Indietro - + Forward Avanti - + Index Indice - + About Qt Informazioni su Qt - + Close this window Chiudi questa finestra @@ -461,82 +461,87 @@ Canali - + Number Numero - + Name Nome - + Add channel(s) to this mode Aggiungi canali a questa modalità - + Remove the selected channel Rimuove il canale selezionato - + Raise the selected channel Sposta in su il canale selezionato - + Lower the selected channel Sposta in giù il canale selezionato - + + Acts On + Agisce su + + + Heads Teste - + Head Testa - + Compose a new head Componi una nuova testa - + Remove the selected head Rimuovi la testa selezionata - + Edit the channels that belong to the selected head Modifica il canale che appartiene alla testa selezionata - + Raise the selected head Sposta su la testa selezionata - + Lower the selected head Sposta in giù la testa selezionata - + Physical Fisico - + Use global settings Usa le impostazioni globali - + Override global settings Usa al posto delle impostazioni globali @@ -626,10 +631,6 @@ Max Degrees Gradi massimi - - Prism faces - Facce del prisma - Pan Max Degrees @@ -644,7 +645,7 @@ Layout (Columns x Rows) - + Disposizione (Colonne x Righe) @@ -719,16 +720,6 @@ The general type of this fixture Tipo di proiettore - - - LED Bar (Beams) - - - - - LED Bar (Pixels) - - Author @@ -942,25 +933,25 @@ Impossibile Salvare. - + Channel already exists Il canale già esiste - + A channel by the name "%1" already exists! il canale "%1" già esiste! - + Channel has no name Canale senza nome - + You must give the channel a descriptive name! Devi dare al canale un nome descrittivo! @@ -970,94 +961,94 @@ Impossibile Salvare. Sei sicuro di vuoler rimuovere il canale: %1? - + Are you sure you wish to remove mode: %1? Sei sicuro di voler rimuovere la modalità: %1? - - - + + + Edit Edita - - + + Copy Copia - - + + Paste Incolla - - - + + + Remove Eliminare - + Channels Canali - - + + Unable to add mode Impossibile aggiungere modalità - + Another mode by that name already exists Esiste già una modalità con questo stesso nome - + You must give a name to the mode Devi dare un nome alla modalità - + Remove Mode Elimina Modalità - + Rename new mode Rinomina nuova Modalità - + Give a unique name for the mode Inserisci un nome unico per la modalità - + Copy of %1 Copia di %1 - + Invalid name Nome Invalido - + Another mode by that name already exists. Esiste già una modalità con questo nome. - + Clone Clona - + Modes Modalità diff --git a/fixtureeditor/fixtureeditor_ja_JP.ts b/fixtureeditor/fixtureeditor_ja_JP.ts index 64e0af8331..7d2c2dda8c 100644 --- a/fixtureeditor/fixtureeditor_ja_JP.ts +++ b/fixtureeditor/fixtureeditor_ja_JP.ts @@ -57,108 +57,108 @@ App - + Unrecognized file extension: %1 Unrecognized file extension: %1 - + Fixture loading failed 機器データの読み込みに失敗 - + Unable to load fixture definition: 以下の機器データを読み込めませんでした: - + &New 新規 - + CTRL+N File|New - + &Open 開く - + CTRL+O File|Open - + &Save 上書き保存 - + CTRL+S File|Save - + Save &As... 名前を付けて保存 - + CTRL+SHIFT+S File|Save As... - + &Quit 終了 - + CTRL+Q File|Quit - + Index ヘルプ - + SHIFT+F1 Help|Index - + About Fixture Definition Editor... Fixture Definition Editor について... - + About Qt... Qt について - + &File ファイル - + &Help ヘルプ - + Open a fixture definition 機器データの読み込み @@ -234,32 +234,32 @@ DocBrowser - + %1 - Document Browser %1 - ヘルプ - + Backward 前へ - + Forward 次へ - + Index Index - + About Qt Qt について - + Close this window @@ -459,82 +459,87 @@ チャンネル - + Number 番号 - + Name 名前 - + Add channel(s) to this mode このモードで使うチャンネルを追加 - + Remove the selected channel 選択したチャンネルの削除 - + Raise the selected channel 選択したチャンネルを上へ - + Lower the selected channel 選択したチャンネルを下へ - + + Acts On + + + + Heads Heads - + Head Head - + Compose a new head Compose a new head - + Remove the selected head Remove the selected head - + Edit the channels that belong to the selected head Edit the channels that belong to the selected head - + Raise the selected head Raise the selected head - + Lower the selected head Lower the selected head - + Physical 機種情報 - + Use global settings - + Override global settings @@ -713,16 +718,6 @@ The general type of this fixture この機器の種別 - - - LED Bar (Beams) - - - - - LED Bar (Pixels) - - Author @@ -936,25 +931,25 @@ Unable to save fixture. - + Channel already exists そのチャンネルは既に存在しています - + A channel by the name "%1" already exists! "%1" という名前のチャンネルはすでに存在しています。 - + Channel has no name チャンネル名未入力 - + You must give the channel a descriptive name! チャンネルの名前を決めてください。 @@ -964,94 +959,94 @@ Unable to save fixture. チャンネル %1 を削除しますか? - + Are you sure you wish to remove mode: %1? 以下のモードを削除しますか: %1 ? - - - + + + Edit 編集 - - + + Copy コピー - - + + Paste ペースト - - - + + + Remove 削除 - + Channels チャンネル - - + + Unable to add mode モードを追加できません - + Another mode by that name already exists その名前のモードは既に存在しています - + You must give a name to the mode モード名を決めてください - + Remove Mode モードの削除 - + Rename new mode モード名の変更 - + Give a unique name for the mode モードには固有の名前を付けてください - + Copy of %1 Copy of %1 - + Invalid name 無効な名前 - + Another mode by that name already exists. その名前のモードは既に存在しています。 - + Clone 複製 - + Modes モード diff --git a/fixtureeditor/fixtureeditor_nl_NL.ts b/fixtureeditor/fixtureeditor_nl_NL.ts index da20eeb5d5..5811f46973 100644 --- a/fixtureeditor/fixtureeditor_nl_NL.ts +++ b/fixtureeditor/fixtureeditor_nl_NL.ts @@ -57,114 +57,114 @@ App - + Unrecognized file extension: %1 Onbekende bestandsextensie: %1 - + Fixture loading failed Fout bij inladen fixture - + Unable to load fixture definition: Inladen fixturedefinitie niet gelukt: - + &New &Nieuw - + CTRL+N File|New Bestand|Nieuw CTRL+N - + &Open &Open - + CTRL+O File|Open Bestand|Openen CTRL+O - + &Save Op&slaan - + CTRL+S File|Save Bestand|Opslaan CTRL+S - + Save &As... Opslaan &Als... - + CTRL+SHIFT+S File|Save As... Bestand|Opslaan als... CTRL+SHIFT+S - + &Quit S&luiten - + CTRL+Q File|Quit Bestand|Sluiten CTRL+L - + Index Index - + SHIFT+F1 Help|Index Help|Index SHIFT+F1 - + About Fixture Definition Editor... Over Fixture Definition Editor... - + About Qt... Over Qt... - + &File &Bestand - + &Help &Help - + Open a fixture definition Open een fixture definitie @@ -240,32 +240,32 @@ DocBrowser - + %1 - Document Browser %1 - Documentenbrowser - + Backward Vorige - + Forward Volgende - + Index Index - + About Qt Over Qt - + Close this window @@ -465,82 +465,87 @@ Kanalen - + Number Nummer - + Name Naam - + Add channel(s) to this mode Voeg een of meerdere kanalen toe aan deze mode - + Remove the selected channel Verwijder het geselecteerde kanaal - + Raise the selected channel Verplaats dit kanaal naar boven - + Lower the selected channel Verplaats dit kanaal naar beneden - + + Acts On + + + + Heads Heads - + Head Head - + Compose a new head Maak een nieuwe head - + Remove the selected head Verwijder de geselecteerde head - + Edit the channels that belong to the selected head Wijzig de kanalen die toebehoren aan de geselecteerde head - + Raise the selected head Verplaats de geselecteerde head naar boven - + Lower the selected head Verplaats de geselecteerde head naar beneden - + Physical Fysiek - + Use global settings - + Override global settings @@ -719,16 +724,6 @@ The general type of this fixture De soort fixture in het algemeen - - - LED Bar (Beams) - - - - - LED Bar (Pixels) - - Author @@ -935,25 +930,25 @@ Unable to save fixture. - + Channel already exists Kanaal bestaat al - + A channel by the name "%1" already exists! Een kanaal met de naam "%1" bestaat al! - + Channel has no name Naam van het kanaal ontbreekt - + You must give the channel a descriptive name! Je moet het kanaal een naam geven! @@ -963,94 +958,94 @@ Unable to save fixture. Kanaal verwijderen: %1? - + Are you sure you wish to remove mode: %1? Verwijder mode %1? - - - + + + Edit Wijzig - - + + Copy Kopiëren - - + + Paste Plakken - - - + + + Remove Verwijderen - + Channels Kanalen - - + + Unable to add mode Toevoegen mode niet mogelijk - + Another mode by that name already exists Een mode met deze naam bestaat al - + You must give a name to the mode De mode moet een naam hebben - + Remove Mode Verwijder mode - + Rename new mode Hernoem nieuwe mode - + Give a unique name for the mode Geef de mode een unieke naam - + Copy of %1 Kopie van %1 - + Invalid name Ongeldige naam - + Another mode by that name already exists. Een mode met dezelfde naam bestaat al. - + Clone Klonen - + Modes Modes diff --git a/fixtureeditor/fixtureeditor_pt_BR.ts b/fixtureeditor/fixtureeditor_pt_BR.ts index 8fcdca4bc6..476af49e08 100644 --- a/fixtureeditor/fixtureeditor_pt_BR.ts +++ b/fixtureeditor/fixtureeditor_pt_BR.ts @@ -57,108 +57,108 @@ App - + Unrecognized file extension: %1 Extensão de ficheiro desconhecida: %1 - + Fixture loading failed Falha a carregar Fixture - + Unable to load fixture definition: Não é possível carregar definições de fixture: - + &New &Novo - + CTRL+N File|New CTRL+N - + &Open &Abrir - + CTRL+O File|Open CTRL+O - + &Save &Guardar - + CTRL+S File|Save CTRL+S - + Save &As... Guardar &como... - + CTRL+SHIFT+S File|Save As... CTRL+SHIFT+S - + &Quit &Sair - + CTRL+Q File|Quit CTRL+Q - + Index Índice - + SHIFT+F1 Help|Index SHIFT+F1 - + About Fixture Definition Editor... Sobre o Editor de Definições de Fixture... - + About Qt... Sobre QT... - + &File &Ficheiro - + &Help &Ajuda - + Open a fixture definition Abrir definição de fixture @@ -235,32 +235,32 @@ DocBrowser - + %1 - Document Browser %1 - Procurar Documentos - + Backward Atrás - + Forward Frente - + Index Índice - + About Qt Acerca de Qt - + Close this window @@ -460,82 +460,87 @@ Canais - + Number Número - + Name Nome - + Add channel(s) to this mode Adicionar canal(is) a este modo - + Remove the selected channel Remover o canal seleccionado - + Raise the selected channel Subir o canal seleccionado - + Lower the selected channel Baixar o canal seleccionado - + + Acts On + + + + Heads Cabeças - + Head Cabeça - + Compose a new head Compor nova cabeça - + Remove the selected head Remover a cabeça seleccionada - + Edit the channels that belong to the selected head Editar os canais que pertencem à cabeça seleccionada - + Raise the selected head Subir a cabeça seleccionada - + Lower the selected head Baixar a cabeça seleccionada - + Physical Físico - + Use global settings - + Override global settings @@ -714,16 +719,6 @@ The general type of this fixture O tipo genérico deste fixture - - - LED Bar (Beams) - - - - - LED Bar (Pixels) - - Author @@ -937,25 +932,25 @@ Não se pode guardar o fixture. - + Channel already exists Canal já existe - + A channel by the name "%1" already exists! O canal com o nome "%1" já existe! - + Channel has no name O canal não tem nome - + You must give the channel a descriptive name! Tem de dar um nome descritivo ao canal! @@ -965,94 +960,94 @@ Não se pode guardar o fixture. Tem a certezaque pretende remover o canal: %1 ? - + Are you sure you wish to remove mode: %1? Tem a certeza que pretende eliminar o modo: %1 ? - - - + + + Edit Editar - - + + Copy Copiar - - + + Paste Colar - - - + + + Remove Eliminar - + Channels Canais - - + + Unable to add mode Incapaz de adicionar modo - + Another mode by that name already exists Já existe outro modo com esse nome - + You must give a name to the mode Tem de atribuir um nome ao modo - + Remove Mode Eliminar modo - + Rename new mode Renomear o novo modo - + Give a unique name for the mode Atribua um nome único ao modo - + Copy of %1 Cópia de %1 - + Invalid name Nome inválido - + Another mode by that name already exists. Já existeoutro modo com esse nome. - + Clone Clonar - + Modes Modos 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/CMakeLists.txt b/hotplugmonitor/CMakeLists.txt new file mode 100644 index 0000000000..0902d38815 --- /dev/null +++ b/hotplugmonitor/CMakeLists.txt @@ -0,0 +1,6 @@ +project(hotplugmonitor) + +if(NOT ANDROID AND NOT IOS) + add_subdirectory(src) + add_subdirectory(test) +endif() diff --git a/hotplugmonitor/src/CMakeLists.txt b/hotplugmonitor/src/CMakeLists.txt new file mode 100644 index 0000000000..957d313f63 --- /dev/null +++ b/hotplugmonitor/src/CMakeLists.txt @@ -0,0 +1,35 @@ +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 + Qt${QT_MAJOR_VERSION}::Gui +) + +if(udev) + target_sources(hotplugmonitor PRIVATE + hpmprivate-udev.cpp hpmprivate-udev.h + ) + target_link_libraries(hotplugmonitor PRIVATE + udev + ) +endif() + +if(iokit) + target_sources(hotplugmonitor PRIVATE + hpmprivate-iokit.cpp hpmprivate-iokit.h + ) + + target_link_libraries(hotplugmonitor PUBLIC + "-framework CoreFoundation" + "-framework IOKit" + ) +endif() + +if(WIN32) + target_sources(hotplugmonitor PRIVATE + hpmprivate-win32.cpp hpmprivate-win32.h + ) +endif() diff --git a/hotplugmonitor/src/hotplugmonitor.cpp b/hotplugmonitor/src/hotplugmonitor.cpp index 55c194ab3a..aca42f1558 100644 --- a/hotplugmonitor/src/hotplugmonitor.cpp +++ b/hotplugmonitor/src/hotplugmonitor.cpp @@ -26,7 +26,7 @@ #if defined(WIN32) || defined(Q_OS_WIN) # include "hpmprivate-win32.h" -#elif defined( __APPLE__) || defined(Q_OS_MAC) +#elif defined(__APPLE__) || defined(Q_OS_MAC) # include "hpmprivate-iokit.h" #else # include "hpmprivate-udev.h" diff --git a/hotplugmonitor/test/CMakeLists.txt b/hotplugmonitor/test/CMakeLists.txt new file mode 100644 index 0000000000..c8c8354a9d --- /dev/null +++ b/hotplugmonitor/test/CMakeLists.txt @@ -0,0 +1,25 @@ +add_executable(test WIN32 MACOSX_BUNDLE + hpmtest.cpp hpmtest.h + main.cpp +) +target_include_directories(test PRIVATE + ../src +) + +target_link_libraries(test PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Widgets + hotplugmonitor +) + +if(iokit) + target_link_libraries(test PRIVATE + "-framework CoreFoundation" + "-framework IOKit" + ) +endif() + +# Consider using qt_generate_deploy_app_script() for app deployment if +# the project can use Qt 6.3. In that case rerun qmake2cmake with +# --min-qt-version=6.3. diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt new file mode 100644 index 0000000000..1719b3e26f --- /dev/null +++ b/launcher/CMakeLists.txt @@ -0,0 +1,68 @@ +set(module_name "qlcplus-launcher") + +set(TS_FILES + launcher_fi_FI.ts + launcher_de_DE.ts + launcher_fr_FR.ts + launcher_es_ES.ts + launcher_it_IT.ts + launcher_nl_NL.ts + launcher_cz_CZ.ts + launcher_pt_BR.ts + launcher_ca_ES.ts + launcher_ja_JP.ts +) + +if(QT_VERSION_MAJOR GREATER 5) + qt_add_translation(QM_FILES ${TS_FILES}) +else() + qt5_add_translation(QM_FILES ${TS_FILES}) +endif() + +add_executable(${module_name} + launcher.cpp launcher.h + main.cpp + ${QM_FILES} +) + +target_include_directories(${module_name} PRIVATE + ../engine/src + ../plugins/interfaces +) + +target_link_libraries(${module_name} PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Widgets +) + +# Resources: +set_source_files_properties("../resources/icons/png/qlcplus-fixtureeditor.png" + PROPERTIES QT_RESOURCE_ALIAS "qlcplus-fixtureeditor.png" +) +set_source_files_properties("../resources/icons/png/qlcplus.png" + PROPERTIES QT_RESOURCE_ALIAS "qlcplus.png" +) +set(launcher_resource_files + "../resources/icons/png/qlcplus-fixtureeditor.png" + "../resources/icons/png/qlcplus.png" +) + +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/CMakeLists.txt b/main/CMakeLists.txt new file mode 100644 index 0000000000..abe2216ac4 --- /dev/null +++ b/main/CMakeLists.txt @@ -0,0 +1,59 @@ +set(module_name "qlcplus") + +add_executable(${module_name} WIN32 + main.cpp +) + +target_include_directories(${module_name} PRIVATE + ../engine/src + ../ui/src + ../ui/src/virtualconsole + ../webaccess/src +) + +target_link_libraries(${module_name} PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Widgets + qlcplusengine + qlcplusui + qlcpluswebaccess +) + + +if(QT_VERSION_MAJOR EQUAL 5) + qt5_add_resources(qlcplusui_resource_files ${CMAKE_SOURCE_DIR}/ui/src/qlcui.qrc) + target_sources(${module_name} PRIVATE + ${qlcplusui_resource_files} + ) +endif() + +if(WIN32 OR APPLE) + target_sources(${module_name} PRIVATE + ../ui/src/debugbox.cpp ../ui/src/debugbox.h ../ui/src/debugbox.ui + ) +endif() + +if(WIN32) + target_sources(${module_name} PRIVATE + main.rc + ) +endif() + +if(qmlui OR (QT_VERSION_MAJOR GREATER 5)) + target_link_libraries(${module_name} PRIVATE + Qt${QT_MAJOR_VERSION}::Qml + ) +else() + target_link_libraries(${module_name} PRIVATE + Qt${QT_MAJOR_VERSION}::Script + ) +endif() + +install(TARGETS ${module_name} + DESTINATION ${INSTALLROOT}/${BINDIR} +) + +# Consider using qt_generate_deploy_app_script() for app deployment if +# the project can use Qt 6.3. In that case rerun qmake2cmake with +# --min-qt-version=6.3. diff --git a/main/main.cpp b/main/main.cpp index c0643d31e2..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; @@ -274,9 +274,9 @@ bool parseArgs() QLCArgs::enableWebAccess = true; QLCArgs::enableWebAuth = true; } - else if(arg == "-a" || arg == "--web-auth-file") + else if (arg == "-a" || arg == "--web-auth-file") { - if(it.hasNext()) + if (it.hasNext()) QLCArgs::webAccessPasswordFile = it.next(); } else if (arg == "-v" || arg == "--version") diff --git a/platforms/CMakeLists.txt b/platforms/CMakeLists.txt new file mode 100644 index 0000000000..f79aa81b03 --- /dev/null +++ b/platforms/CMakeLists.txt @@ -0,0 +1,11 @@ +project(platforms) + +if(UNIX AND NOT APPLE) + add_subdirectory(linux) +endif() +if(WIN32) + add_subdirectory(windows) +endif() +if(APPLE) + add_subdirectory(macos) +endif() 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 cea1111b9d..f0b5fee054 100644 --- a/platforms/android/AndroidManifest.xml +++ b/platforms/android/AndroidManifest.xml @@ -31,12 +31,10 @@ - - - + @@ -57,10 +55,11 @@ "applicationStateChanged(Qt::ApplicationSuspended)" signal is sent! --> + - + Device Manufacturer: " << manufName; + if (iface->readLabel(DMXKING_USB_DEVICE_NAME, DEV_ID, deviceName) == false) + return false; + + qDebug() << "--------> Device Name: " << deviceName; + qDebug() << "--------> ESTA Code: " << QString::number(ESTA_ID, 16) << ", Device ID: " << QString::number(DEV_ID, 16); + + if (ESTA_ID == DMXKING_ESTA_ID) + return true; + + return false; +} + QList DMXUSBWidget::widgets() { QList widgetList; @@ -90,9 +110,9 @@ QList DMXUSBWidget::widgets() { QString productName = iface->name().toUpper(); + // check if protocol must be forced on an interface if (types.contains(iface->serial()) == true) { - // Force a widget with a specific serial to either type DMXUSBWidget::Type type = (DMXUSBWidget::Type) types[iface->serial()].toInt(); switch (type) { @@ -127,7 +147,7 @@ QList DMXUSBWidget::widgets() case DMXUSBWidget::VinceTX: widgetList << new VinceUSBDMX512(iface, output_id++); break; -#if defined(Q_WS_X11) || defined(Q_OS_LINUX) || defined(Q_OS_OSX) +#if defined(Q_WS_X11) || defined(Q_OS_LINUX) || defined(Q_OS_MACOS) case DMXUSBWidget::Eurolite: widgetList << new EuroliteUSBDMXPro(iface, output_id++); break; @@ -147,17 +167,32 @@ QList DMXUSBWidget::widgets() input_id += 2; widgetList << promkii; } + else if (iface->vendorID() == DMXInterface::NXPVID && iface->productID() == DMXInterface::DMXKINGMAXPID) + { + int ESTAID = 0, DEVID = 0, outNumber; + QString manName, devName; + bool isDmxKing = detectDMXKingDevice(iface, manName, devName, ESTAID, DEVID); + + // read also ports count + if (isDmxKing && iface->readLabel(DMXKING_DMX_PORT_COUNT, outNumber, manName)) + { + qDebug() << "Number of outputs detected:" << outNumber; + + EnttecDMXUSBPro *ultra = new EnttecDMXUSBPro(iface, output_id, input_id++); + ultra->setOutputsNumber(outNumber); + ultra->setDMXKingMode(); + ultra->setRealName(devName); + output_id += outNumber; + widgetList << ultra; + } + } else if (productName.contains("DMX USB PRO") || productName.contains("ULTRADMX")) { - /** Check if the device responds to label 77 and 78, so it might be a DMXking adapter */ - int ESTAID = 0; - int DEVID = 0; - QString manName = iface->readLabel(DMXKING_USB_DEVICE_MANUFACTURER, &ESTAID); - qDebug() << "--------> Device Manufacturer: " << manName; - QString devName = iface->readLabel(DMXKING_USB_DEVICE_NAME, &DEVID); - qDebug() << "--------> Device Name: " << devName; - qDebug() << "--------> ESTA Code: " << QString::number(ESTAID, 16) << ", Device ID: " << QString::number(DEVID, 16); - if (ESTAID == DMXKING_ESTA_ID) + int ESTAID = 0, DEVID = 0; + QString manName, devName; + bool isDmxKing = detectDMXKingDevice(iface, manName, devName, ESTAID, DEVID); + + if (isDmxKing) { if (DEVID == ULTRADMX_PRO_DEV_ID) { @@ -199,7 +234,7 @@ QList DMXUSBWidget::widgets() { widgetList << new Stageprofi(iface, output_id++); } -#if defined(Q_WS_X11) || defined(Q_OS_LINUX) || defined(Q_OS_OSX) +#if defined(Q_WS_X11) || defined(Q_OS_LINUX) || defined(Q_OS_MACOS) else if (iface->vendorID() == DMXInterface::ATMELVID && iface->productID() == DMXInterface::NANODMXPID) { @@ -507,11 +542,12 @@ bool DMXUSBWidget::sendRDMCommand(quint32 universe, quint32 line, uchar command, * Write universe ****************************************************************************/ -bool DMXUSBWidget::writeUniverse(quint32 universe, quint32 output, const QByteArray& data) +bool DMXUSBWidget::writeUniverse(quint32 universe, quint32 output, const QByteArray& data, bool dataChanged) { Q_UNUSED(universe) Q_UNUSED(output) Q_UNUSED(data) + Q_UNUSED(dataChanged) return false; } diff --git a/plugins/dmxusb/src/dmxusbwidget.h b/plugins/dmxusb/src/dmxusbwidget.h index 4cd86a95a4..25a55c5303 100644 --- a/plugins/dmxusb/src/dmxusbwidget.h +++ b/plugins/dmxusb/src/dmxusbwidget.h @@ -33,6 +33,7 @@ #include "qtserial-interface.h" #endif +#define DMX_CHANNELS 512 #define DEFAULT_OUTPUT_FREQUENCY 44 // 44 Hertz, according to the DMX specs typedef struct @@ -59,7 +60,7 @@ class DMXUSBWidget * @param interface The widget's DMXInterface instance * @param outputLine the specific output line this widget is going to control */ - DMXUSBWidget(DMXInterface *interface, quint32 outputLine, int frequency); + DMXUSBWidget(DMXInterface *iface, quint32 outputLine, int frequency); virtual ~DMXUSBWidget(); @@ -87,11 +88,15 @@ class DMXUSBWidget virtual Type type() const = 0; /** Get the DMXInterface instance */ - DMXInterface *interface() const; + DMXInterface *iface() const; /** Get the DMXInterface driver in use as a string */ QString interfaceTypeString() const; + static bool detectDMXKingDevice(DMXInterface *iface, + QString &manufName, QString &deviceName, + int &ESTA_ID, int &DEV_ID); + static QList widgets(); bool forceInterfaceDriver(DMXInterface::Type type); @@ -251,7 +256,7 @@ class DMXUSBWidget * @param universe The DMX universe to send * @return true if the values were sent successfully, otherwise false */ - virtual bool writeUniverse(quint32 universe, quint32 output, const QByteArray& data); + virtual bool writeUniverse(quint32 universe, quint32 output, const QByteArray& data, bool dataChanged); }; #endif diff --git a/plugins/dmxusb/src/enttecdmxusbopen.cpp b/plugins/dmxusb/src/enttecdmxusbopen.cpp index 42007c632a..383fc2c67c 100644 --- a/plugins/dmxusb/src/enttecdmxusbopen.cpp +++ b/plugins/dmxusb/src/enttecdmxusbopen.cpp @@ -20,7 +20,6 @@ #include #include -#include #include #include "enttecdmxusbopen.h" @@ -28,7 +27,6 @@ #define DMX_MAB 16 #define DMX_BREAK 110 -#define DMX_CHANNELS 512 #define DEFAULT_OPEN_DMX_FREQUENCY 30 // crap #define SETTINGS_CHANNELS "enttecdmxusbopen/channels" @@ -36,10 +34,10 @@ * Initialization ****************************************************************************/ -EnttecDMXUSBOpen::EnttecDMXUSBOpen(DMXInterface *interface, +EnttecDMXUSBOpen::EnttecDMXUSBOpen(DMXInterface *iface, quint32 outputLine, QObject* parent) : QThread(parent) - , DMXUSBWidget(interface, outputLine, DEFAULT_OPEN_DMX_FREQUENCY) + , DMXUSBWidget(iface, outputLine, DEFAULT_OPEN_DMX_FREQUENCY) , m_running(false) , m_granularity(Unknown) { @@ -61,8 +59,8 @@ EnttecDMXUSBOpen::EnttecDMXUSBOpen(DMXInterface *interface, // on macOS, QtSerialPort cannot handle an OpenDMX device // so, unfortunately, we need to switch back to libftdi -#if defined(Q_OS_OSX) && defined(QTSERIAL) && (defined(LIBFTDI1) || defined(LIBFTDI)) - if (interface->type() == DMXInterface::QtSerial) +#if defined(Q_OS_MACOS) && defined(QTSERIAL) && (defined(LIBFTDI1) || defined(LIBFTDI)) + if (iface->type() == DMXInterface::QtSerial) forceInterfaceDriver(DMXInterface::libFTDI); #endif } @@ -85,12 +83,12 @@ bool EnttecDMXUSBOpen::open(quint32 line, bool input) { Q_UNUSED(input) - if (interface()->type() != DMXInterface::QtSerial) + if (iface()->type() != DMXInterface::QtSerial) { if (DMXUSBWidget::open(line) == false) return close(line); - if (interface()->clearRts() == false) + if (iface()->clearRts() == false) return close(line); } start(QThread::TimeCriticalPriority); @@ -142,13 +140,14 @@ QString EnttecDMXUSBOpen::additionalInfo() const * Thread ****************************************************************************/ -bool EnttecDMXUSBOpen::writeUniverse(quint32 universe, quint32 output, const QByteArray& data) +bool EnttecDMXUSBOpen::writeUniverse(quint32 universe, quint32 output, const QByteArray& data, bool dataChanged) { Q_UNUSED(universe) Q_UNUSED(output) - m_outputLines[0].m_universeData.replace(1, MIN(data.size(), m_outputLines[0].m_universeData.size() - 1), - data.constData(), MIN(data.size(), m_outputLines[0].m_universeData.size() - 1)); + if (dataChanged) + m_outputLines[0].m_universeData.replace(1, MIN(data.size(), m_outputLines[0].m_universeData.size() - 1), + data.constData(), MIN(data.size(), m_outputLines[0].m_universeData.size() - 1)); return true; } @@ -173,7 +172,7 @@ void EnttecDMXUSBOpen::run() else m_granularity = Good; - if (interface()->type() == DMXInterface::QtSerial) + if (iface()->type() == DMXInterface::QtSerial) { if (DMXUSBWidget::open(0) == false) { @@ -181,7 +180,7 @@ void EnttecDMXUSBOpen::run() return; } - if (interface()->clearRts() == false) + if (iface()->clearRts() == false) { close(0); return; @@ -194,19 +193,19 @@ void EnttecDMXUSBOpen::run() // Measure how much time passes during these calls time.restart(); - if (interface()->setBreak(true) == false) + if (iface()->setBreak(true) == false) goto framesleep; if (m_granularity == Good) usleep(DMX_BREAK); - if (interface()->setBreak(false) == false) + if (iface()->setBreak(false) == false) goto framesleep; if (m_granularity == Good) usleep(DMX_MAB); - if (interface()->write(m_outputLines[0].m_universeData) == false) + if (iface()->write(m_outputLines[0].m_universeData) == false) goto framesleep; framesleep: diff --git a/plugins/dmxusb/src/enttecdmxusbopen.h b/plugins/dmxusb/src/enttecdmxusbopen.h index 9e73fba4ba..ccd7a94baa 100644 --- a/plugins/dmxusb/src/enttecdmxusbopen.h +++ b/plugins/dmxusb/src/enttecdmxusbopen.h @@ -43,7 +43,7 @@ class EnttecDMXUSBOpen : public QThread, public DMXUSBWidget * @param id The device's unique ID (FTD2XX only) * @param parent The owner of this object */ - EnttecDMXUSBOpen(DMXInterface *interface, + EnttecDMXUSBOpen(DMXInterface *iface, quint32 outputLine, QObject* parent = 0); /** Destructor */ @@ -74,7 +74,7 @@ class EnttecDMXUSBOpen : public QThread, public DMXUSBWidget ************************************************************************/ public: /** @reimp */ - bool writeUniverse(quint32 universe, quint32 output, const QByteArray& data); + bool writeUniverse(quint32 universe, quint32 output, const QByteArray& data, bool dataChanged); protected: enum TimerGranularity { Unknown, Good, Bad }; diff --git a/plugins/dmxusb/src/enttecdmxusbpro.cpp b/plugins/dmxusb/src/enttecdmxusbpro.cpp index 67cacec06d..12b6c16d49 100644 --- a/plugins/dmxusb/src/enttecdmxusbpro.cpp +++ b/plugins/dmxusb/src/enttecdmxusbpro.cpp @@ -28,9 +28,9 @@ * Initialization ****************************************************************************/ -EnttecDMXUSBPro::EnttecDMXUSBPro(DMXInterface *interface, quint32 outputLine, quint32 inputLine) +EnttecDMXUSBPro::EnttecDMXUSBPro(DMXInterface *iface, quint32 outputLine, quint32 inputLine) : QThread(NULL) - , DMXUSBWidget(interface, outputLine, DEFAULT_OUTPUT_FREQUENCY) + , DMXUSBWidget(iface, outputLine, DEFAULT_OUTPUT_FREQUENCY) , m_dmxKingMode(false) , m_inputThread(NULL) , m_outputRunning(false) @@ -69,7 +69,7 @@ DMXUSBWidget::Type EnttecDMXUSBPro::type() const { if (name().toUpper().contains("PRO MK2") == true) return ProMk2; - else if(m_dmxKingMode) + else if (m_dmxKingMode) return UltraPro; else return ProRXTX; @@ -147,7 +147,7 @@ bool EnttecDMXUSBPro::configureLine(ushort dmxLine, bool isMidi) request.append(ENTTEC_PRO_END_OF_MSG); // Stop byte /* Write "Set API Key Request" message */ - if (interface()->write(request) == false) + if (iface()->write(request) == false) { qWarning() << Q_FUNC_INFO << name() << "FTDI write filed (DMX2 port config)"; return false; @@ -166,7 +166,7 @@ bool EnttecDMXUSBPro::configureLine(ushort dmxLine, bool isMidi) request.append(ENTTEC_PRO_END_OF_MSG); // Stop byte /* Write "Set Port Assignment Request" message */ - if (interface()->write(request) == false) + if (iface()->write(request) == false) { qWarning() << Q_FUNC_INFO << name() << "FTDI write filed (DMX1 port config)"; return false; @@ -181,7 +181,7 @@ bool EnttecDMXUSBPro::open(quint32 line, bool input) if (DMXUSBWidget::open(line, input) == false) return close(line, input); - if (interface()->clearRts() == false) + if (iface()->clearRts() == false) return close(line, input); // specific port configuration are needed only by ENTTEC @@ -211,7 +211,7 @@ bool EnttecDMXUSBPro::open(quint32 line, bool input) else if (input == true && m_inputThread == NULL) { // create (therefore start) the input thread - m_inputThread = new EnttecDMXUSBProInput(interface()); + m_inputThread = new EnttecDMXUSBProInput(iface()); connect(m_inputThread, SIGNAL(dataReady(QByteArray,bool)), this, SLOT(slotDataReceived(QByteArray,bool))); } @@ -241,17 +241,17 @@ bool EnttecDMXUSBPro::close(quint32 line, bool input) * Input ************************************************************************/ -int readData(DMXInterface *interface, QByteArray &payload, bool &isMIDI, bool needRDM) +int readData(DMXInterface *iface, QByteArray &payload, bool &isMIDI, bool needRDM) { bool ok = false; uchar byte = 0; // Skip bytes until we find the start of the next message - if ((byte = interface->readByte(&ok)) != ENTTEC_PRO_START_OF_MSG) + if ((byte = iface->readByte(&ok)) != ENTTEC_PRO_START_OF_MSG) return 0; // Check the message type - byte = interface->readByte(); + byte = iface->readByte(); if (byte == ENTTEC_PRO_MIDI_IN_MSG) { isMIDI = true; @@ -260,7 +260,7 @@ int readData(DMXInterface *interface, QByteArray &payload, bool &isMIDI, bool ne { qDebug() << "Got RDM timeout"; // read end byte - interface->readByte(); + iface->readByte(); return 0; } else if (byte != ENTTEC_PRO_RECV_DMX_PKT && byte != ENTTEC_PRO_READ_SERIAL) @@ -270,13 +270,13 @@ int readData(DMXInterface *interface, QByteArray &payload, bool &isMIDI, bool ne } // Get payload length - ushort dataLength = (ushort) interface->readByte() | ((ushort) interface->readByte() << 8); + ushort dataLength = (ushort) iface->readByte() | ((ushort) iface->readByte() << 8); //qDebug() << "Packet data length:" << dataLength; if (isMIDI == false) { // Check status bytes - byte = interface->readByte(); + byte = iface->readByte(); if (byte & char(0x01)) qWarning() << Q_FUNC_INFO << "Widget receive queue overflowed"; else if (byte & char(0x02)) @@ -285,7 +285,7 @@ int readData(DMXInterface *interface, QByteArray &payload, bool &isMIDI, bool ne if (needRDM == false) { // Check DMX startcode - byte = interface->readByte(); + byte = iface->readByte(); if (byte != char(0)) qWarning() << Q_FUNC_INFO << "Non-standard DMX startcode received:" << (uchar) byte; dataLength -= 2; @@ -294,10 +294,10 @@ int readData(DMXInterface *interface, QByteArray &payload, bool &isMIDI, bool ne // Read the whole payload payload.clear(); - payload = interface->read(dataLength); + payload = iface->read(dataLength); // read end byte - interface->readByte(); + iface->readByte(); #ifdef DEBUG_RDM if (needRDM) @@ -346,15 +346,15 @@ bool EnttecDMXUSBPro::extractSerial() request.append(ENTTEC_PRO_DMX_ZERO); // data length MSB request.append(ENTTEC_PRO_END_OF_MSG); - interface()->open(); - interface()->clearRts(); + iface()->open(); + iface()->clearRts(); - if (interface()->write(request) == true) + if (iface()->write(request) == true) { msleep(50); QByteArray reply; bool notUsed; - int bytesRead = readData(interface(), reply, notUsed, false); + int bytesRead = readData(iface(), reply, notUsed, false); if (bytesRead != 4) { @@ -384,7 +384,7 @@ bool EnttecDMXUSBPro::extractSerial() qWarning() << Q_FUNC_INFO << name() << "will not accept serial request"; } - interface()->close(); + iface()->close(); return result; } @@ -422,7 +422,7 @@ void EnttecDMXUSBPro::slotDataReceived(QByteArray data, bool isMidi) //qDebug() << "MIDI byte:" << byte; if (midiCounter == 0) { - if(MIDI_IS_CMD(byte)) + if (MIDI_IS_CMD(byte)) { midiCmd = byte; midiCounter++; @@ -469,7 +469,7 @@ void EnttecDMXUSBPro::stopOutputThread() } } -bool EnttecDMXUSBPro::writeUniverse(quint32 universe, quint32 output, const QByteArray& data) +bool EnttecDMXUSBPro::writeUniverse(quint32 universe, quint32 output, const QByteArray& data, bool dataChanged) { Q_UNUSED(universe) @@ -484,8 +484,12 @@ bool EnttecDMXUSBPro::writeUniverse(quint32 universe, quint32 output, const QByt return false; if (m_outputLines[devLine].m_universeData.size() == 0) + { m_outputLines[devLine].m_universeData.append(data); - else + m_outputLines[devLine].m_universeData.append(DMX_CHANNELS - data.size(), 0); + } + + if (dataChanged) m_outputLines[devLine].m_universeData.replace(0, data.size(), data); return true; @@ -524,7 +528,7 @@ void EnttecDMXUSBPro::run() // send only values that changed for (int j = 0; j < m_outputLines[i].m_universeData.length(); j++) { - uchar val = uchar(m_outputLines[i].m_universeData[j]); + char val = m_outputLines[i].m_universeData[j]; if (val == m_outputLines[i].m_compareData[j]) continue; @@ -551,7 +555,7 @@ void EnttecDMXUSBPro::run() request.append(data2); request.append(ENTTEC_PRO_END_OF_MSG); // Stop byte m_outputMutex.lock(); - if (interface()->write(request) == false) + if (iface()->write(request) == false) { qWarning() << Q_FUNC_INFO << name() << "will not accept MIDI data"; m_outputMutex.unlock(); @@ -566,20 +570,11 @@ void EnttecDMXUSBPro::run() QByteArray request; request.append(ENTTEC_PRO_START_OF_MSG); // Start byte - if (i == 1) - { - if (m_dmxKingMode) - request.append(DMXKING_SEND_DMX_PORT2); // Command - second port - else - request.append(ENTTEC_PRO_SEND_DMX_RQ2); // Command - second port - } + // Command - port selection + if (m_dmxKingMode) + request.append(DMXKING_SEND_DMX_PORT1 + i); else - { - if (m_dmxKingMode) - request.append(DMXKING_SEND_DMX_PORT1); // Command - first port - else - request.append(ENTTEC_PRO_SEND_DMX_RQ); // Command - first port - } + request.append(i == 0 ? ENTTEC_PRO_SEND_DMX_RQ : ENTTEC_PRO_SEND_DMX_RQ2); request.append((dataLen + 1) & 0xff); // Data length LSB request.append(((dataLen + 1) >> 8) & 0xff); // Data length MSB @@ -592,7 +587,7 @@ void EnttecDMXUSBPro::run() /* Write "Output Only Send DMX Packet Request" message */ m_outputMutex.lock(); - if (interface()->write(request) == false) + if (iface()->write(request) == false) { qWarning() << Q_FUNC_INFO << name() << "will not accept DMX data"; m_outputMutex.unlock(); @@ -663,7 +658,7 @@ bool EnttecDMXUSBPro::sendRDMCommand(quint32 universe, quint32 line, uchar comma #endif QMutexLocker locker(&m_outputMutex); - if (interface()->write(ba) == false) + if (iface()->write(ba) == false) { qWarning() << Q_FUNC_INFO << name() << "will not accept RDM data"; return false; @@ -673,7 +668,7 @@ bool EnttecDMXUSBPro::sendRDMCommand(quint32 universe, quint32 line, uchar comma { QByteArray reply; bool isMIDI = false; - int bytesRead = readData(interface(), reply, isMIDI, true); + int bytesRead = readData(iface(), reply, isMIDI, true); if (bytesRead) { @@ -687,6 +682,8 @@ bool EnttecDMXUSBPro::sendRDMCommand(quint32 universe, quint32 line, uchar comma if (result == true) { + discoveryFailureCount = 0; + discoveryNoReplyCount = 0; emit rdmValueChanged(universe, line, values); break; } @@ -728,11 +725,11 @@ bool EnttecDMXUSBPro::sendRDMCommand(quint32 universe, quint32 line, uchar comma * Input thread implementation ************************************************************************/ -EnttecDMXUSBProInput::EnttecDMXUSBProInput(DMXInterface *interface) - : m_interface(interface) +EnttecDMXUSBProInput::EnttecDMXUSBProInput(DMXInterface *iface) + : m_interface(iface) , m_running(false) { - Q_ASSERT(interface != NULL); + Q_ASSERT(iface != NULL); // start the event loop immediately start(); diff --git a/plugins/dmxusb/src/enttecdmxusbpro.h b/plugins/dmxusb/src/enttecdmxusbpro.h index 886435c671..dd3a20b497 100644 --- a/plugins/dmxusb/src/enttecdmxusbpro.h +++ b/plugins/dmxusb/src/enttecdmxusbpro.h @@ -57,6 +57,7 @@ #define DMXKING_USB_DEVICE_MANUFACTURER 0x4D #define DMXKING_USB_DEVICE_NAME 0x4E +#define DMXKING_DMX_PORT_COUNT 0x62 #define DMXKING_SEND_DMX_PORT1 char(0x64) #define DMXKING_SEND_DMX_PORT2 char(0x65) @@ -69,7 +70,7 @@ class EnttecDMXUSBProInput : public QThread Q_OBJECT public: - EnttecDMXUSBProInput(DMXInterface *interface); + EnttecDMXUSBProInput(DMXInterface *iface); ~EnttecDMXUSBProInput(); private: @@ -98,7 +99,7 @@ class EnttecDMXUSBPro : public QThread, public DMXUSBWidget * Initialization ************************************************************************/ public: - EnttecDMXUSBPro(DMXInterface *interface, + EnttecDMXUSBPro(DMXInterface *iface, quint32 outputLine, quint32 inputLine = 0); virtual ~EnttecDMXUSBPro(); @@ -163,7 +164,7 @@ protected slots: ************************************************************************/ public: /** @reimp */ - bool writeUniverse(quint32 universe, quint32 output, const QByteArray& data); + bool writeUniverse(quint32 universe, quint32 output, const QByteArray& data, bool dataChanged); private: /** Stop output thread */ diff --git a/plugins/dmxusb/src/euroliteusbdmxpro.cpp b/plugins/dmxusb/src/euroliteusbdmxpro.cpp index 8bfb4f6419..07db6ed5dc 100644 --- a/plugins/dmxusb/src/euroliteusbdmxpro.cpp +++ b/plugins/dmxusb/src/euroliteusbdmxpro.cpp @@ -22,8 +22,8 @@ #include #include -EuroliteUSBDMXPro::EuroliteUSBDMXPro(DMXInterface *interface, quint32 outputLine) - : DMXUSBWidget(interface, outputLine, DEFAULT_OUTPUT_FREQUENCY) +EuroliteUSBDMXPro::EuroliteUSBDMXPro(DMXInterface *iface, quint32 outputLine) + : DMXUSBWidget(iface, outputLine, DEFAULT_OUTPUT_FREQUENCY) , m_running(false) { } @@ -56,7 +56,7 @@ QString EuroliteUSBDMXPro::getDeviceName() foreach (QString dir, devDirs) { - if (dir.startsWith(QString::number(interface()->busLocation())) && + if (dir.startsWith(QString::number(iface()->busLocation())) && dir.contains(":") == false) { // 2- Match the product name @@ -84,7 +84,7 @@ QString EuroliteUSBDMXPro::getDeviceName() if (ttyDir.exists()) { QStringList ttyList = ttyDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); - foreach(QString ttyName, ttyList) + foreach (QString ttyName, ttyList) { qDebug() << "This EuroliteUSBDMXPro adapter will use" << QString("/dev/" + ttyName); return QString("/dev/" + ttyName); @@ -186,7 +186,7 @@ QString EuroliteUSBDMXPro::additionalInfo() const * Write universe data ****************************************************************************/ -bool EuroliteUSBDMXPro::writeUniverse(quint32 universe, quint32 output, const QByteArray& data) +bool EuroliteUSBDMXPro::writeUniverse(quint32 universe, quint32 output, const QByteArray& data, bool dataChanged) { Q_UNUSED(universe) Q_UNUSED(output) @@ -200,8 +200,12 @@ bool EuroliteUSBDMXPro::writeUniverse(quint32 universe, quint32 output, const QB #endif if (m_outputLines[0].m_universeData.size() == 0) + { m_outputLines[0].m_universeData.append(data); - else + m_outputLines[0].m_universeData.append(DMX_CHANNELS - data.size(), 0); + } + + if (dataChanged) m_outputLines[0].m_universeData.replace(0, data.size(), data); return true; @@ -241,14 +245,14 @@ void EuroliteUSBDMXPro::run() request.append(EUROLITE_USB_DMX_PRO_END_OF_MSG); // Stop byte #ifdef QTSERIAL - if (interface()->write(request) == false) + if (iface()->write(request) == false) #else if (m_file.write(request) == false) #endif { qWarning() << Q_FUNC_INFO << name() << "will not accept DMX data"; #ifdef QTSERIAL - interface()->purgeBuffers(); + iface()->purgeBuffers(); #endif } framesleep: diff --git a/plugins/dmxusb/src/euroliteusbdmxpro.h b/plugins/dmxusb/src/euroliteusbdmxpro.h index 49f23bc79e..a94426602c 100644 --- a/plugins/dmxusb/src/euroliteusbdmxpro.h +++ b/plugins/dmxusb/src/euroliteusbdmxpro.h @@ -35,7 +35,7 @@ class EuroliteUSBDMXPro : public QThread, public DMXUSBWidget * Initialization ************************************************************************/ public: - EuroliteUSBDMXPro(DMXInterface *interface, quint32 outputLine); + EuroliteUSBDMXPro(DMXInterface *iface, quint32 outputLine); virtual ~EuroliteUSBDMXPro(); /** @reimp */ @@ -58,7 +58,7 @@ class EuroliteUSBDMXPro : public QThread, public DMXUSBWidget QString additionalInfo() const; /** @reimp */ - bool writeUniverse(quint32 universe, quint32 output, const QByteArray& data); + bool writeUniverse(quint32 universe, quint32 output, const QByteArray& data, bool dataChanged); protected: /** Stop the writer thread */ diff --git a/plugins/dmxusb/src/ftd2xx-interface.cpp b/plugins/dmxusb/src/ftd2xx-interface.cpp index f540a95f6e..cee702e295 100644 --- a/plugins/dmxusb/src/ftd2xx-interface.cpp +++ b/plugins/dmxusb/src/ftd2xx-interface.cpp @@ -87,24 +87,24 @@ FTD2XXInterface::~FTD2XXInterface() close(); } -QString FTD2XXInterface::readLabel(uchar label, int *ESTA_code) +bool FTD2XXInterface::readLabel(uchar label, int &intParam, QString &strParam) { FT_HANDLE ftdi = NULL; if (FT_Open(id(), &ftdi) != FT_OK) - return QString(); + return false; - if(FT_ResetDevice(ftdi) != FT_OK) - return QString(); + if (FT_ResetDevice(ftdi) != FT_OK) + return false; - if(FT_SetBaudRate(ftdi, 250000) != FT_OK) - return QString(); + if (FT_SetBaudRate(ftdi, 250000) != FT_OK) + return false; - if(FT_SetDataCharacteristics(ftdi, FT_BITS_8, FT_STOP_BITS_2, FT_PARITY_NONE) != FT_OK) - return QString(); + if (FT_SetDataCharacteristics(ftdi, FT_BITS_8, FT_STOP_BITS_2, FT_PARITY_NONE) != FT_OK) + return false; - if(FT_SetFlowControl(ftdi, 0, 0, 0) != FT_OK) - return QString(); + if (FT_SetFlowControl(ftdi, 0, 0, 0) != FT_OK) + return false; QByteArray request; request.append(ENTTEC_PRO_START_OF_MSG); @@ -115,33 +115,62 @@ QString FTD2XXInterface::readLabel(uchar label, int *ESTA_code) DWORD written = 0; if (FT_Write(ftdi, (char*) request.data(), request.size(), &written) != FT_OK) - return QString(); + { + qDebug() << Q_FUNC_INFO << "Cannot write data to device" << id(); + FT_Close(ftdi); + return false; + } if (written == 0) { - qDebug() << Q_FUNC_INFO << "Cannot write data to device"; - return QString(); + qDebug() << Q_FUNC_INFO << "Cannot write data to device" << id(); + FT_Close(ftdi); + return false; } - uchar* buffer = (uchar*) malloc(sizeof(uchar) * 40); - Q_ASSERT(buffer != NULL); - + uchar buffer[40]; int read = 0; QByteArray array; + FT_SetTimeouts(ftdi, 500,0); FT_Read(ftdi, buffer, 40, (LPDWORD) &read); - qDebug() << Q_FUNC_INFO << "----- Read: " << read << " ------"; - for (int i = 0; i < read; i++) - array.append((char) buffer[i]); + array = QByteArray::fromRawData((char*) buffer, read); + + if (array.size() == 0) + { + FT_Close(ftdi); + return false; + } if (array[0] != ENTTEC_PRO_START_OF_MSG) + { qDebug() << Q_FUNC_INFO << "Reply message wrong start code: " << QString::number(array[0], 16); - *ESTA_code = (array[5] << 8) | array[4]; + FT_Close(ftdi); + return false; + } + + // start | label | data length + if (array.size() < 4) + { + FT_Close(ftdi); + return false; + } + + int dataLen = (array[3] << 8) | array[2]; + if (dataLen == 1) + { + intParam = array[4]; + FT_Close(ftdi); + return true; + } + + intParam = (array[5] << 8) | array[4]; array.remove(0, 6); // 4 bytes of Enttec protocol + 2 of ESTA ID array.replace(ENTTEC_PRO_END_OF_MSG, '\0'); // replace Enttec termination with string termination + strParam = QString(array); FT_Close(ftdi); - return QString(array); + return true; } DMXInterface::Type FTD2XXInterface::type() @@ -178,7 +207,7 @@ QList FTD2XXInterface::interfaces(QList discover for (DWORD i = 0; i < num; i++) { QString vendor, name, serial; - quint16 VID, PID; + quint16 VID = 0, PID = 0; FT_STATUS s = get_interface_info(i, vendor, name, serial, VID, PID); if (s != FT_OK || name.isEmpty() || serial.isEmpty()) { @@ -222,7 +251,7 @@ bool FTD2XXInterface::open() FT_STATUS status = FT_Open(id(), &m_handle); if (status != FT_OK) { - qWarning() << Q_FUNC_INFO << "Error opening" << name() << status; + qWarning() << Q_FUNC_INFO << "Error opening" << name() << "id:" << id() << "status:" << status; return false; } diff --git a/plugins/dmxusb/src/ftd2xx-interface.h b/plugins/dmxusb/src/ftd2xx-interface.h index c101cb19b0..7c6af86b89 100644 --- a/plugins/dmxusb/src/ftd2xx-interface.h +++ b/plugins/dmxusb/src/ftd2xx-interface.h @@ -37,7 +37,7 @@ class FTD2XXInterface : public DMXInterface static QList interfaces(QList discoveredList); /** @reimpl */ - QString readLabel(uchar label, int *ESTA_code); + bool readLabel(uchar label, int &intParam, QString &strParam); /************************************************************************ * DMX/Serial Interface Methods diff --git a/plugins/dmxusb/src/libftdi-interface.cpp b/plugins/dmxusb/src/libftdi-interface.cpp index 6d3df59402..f127928e93 100644 --- a/plugins/dmxusb/src/libftdi-interface.cpp +++ b/plugins/dmxusb/src/libftdi-interface.cpp @@ -37,6 +37,9 @@ LibFTDIInterface::LibFTDIInterface(const QString& serial, const QString& name, c { bzero(&m_handle, sizeof(struct ftdi_context)); ftdi_init(&m_handle); +#ifdef LIBFTDI1_5 + m_handle.module_detach_mode = AUTO_DETACH_REATACH_SIO_MODULE; +#endif } LibFTDIInterface::~LibFTDIInterface() @@ -46,24 +49,24 @@ LibFTDIInterface::~LibFTDIInterface() ftdi_deinit(&m_handle); } -QString LibFTDIInterface::readLabel(uchar label, int *ESTA_code) +bool LibFTDIInterface::readLabel(uchar label, int &intParam, QString &strParam) { if (ftdi_usb_open_desc(&m_handle, DMXInterface::FTDIVID, DMXInterface::FTDIPID, name().toLatin1().data(), serial().toLatin1().data()) < 0) - return QString(); + return false; if (ftdi_usb_reset(&m_handle) < 0) - return QString(); + return false; if (ftdi_set_baudrate(&m_handle, 250000) < 0) - return QString(); + return false; if (ftdi_set_line_property(&m_handle, BITS_8, STOP_BIT_2, NONE) < 0) - return QString(); + return false; if (ftdi_setflowctrl(&m_handle, SIO_DISABLE_FLOW_CTRL) < 0) - return QString(); + return false; QByteArray request; request.append(ENTTEC_PRO_START_OF_MSG); @@ -75,29 +78,50 @@ QString LibFTDIInterface::readLabel(uchar label, int *ESTA_code) if (ftdi_write_data(&m_handle, (uchar*) request.data(), request.size()) < 0) { qDebug() << Q_FUNC_INFO << "Cannot write data to device"; - return QString(); + return false; } - uchar *buffer = (uchar*) malloc(sizeof(uchar) * 40); - Q_ASSERT(buffer != NULL); + uchar buffer[40]; + + for (int i = 0; i < 3; i++) + { + QByteArray array = read(40, buffer); + if (array.size() == 0) + return false; + + if (array[0] != ENTTEC_PRO_START_OF_MSG) + { + qDebug() << Q_FUNC_INFO << "Reply message wrong start code: " << QString::number(array[0], 16); + return false; + } + + // start | label | data length + if (array.size() < 4) + return false; + + int dataLen = (array[3] << 8) | array[2]; + if (dataLen == 1) + { + intParam = array[4]; + return true; + } + + intParam = (array[5] << 8) | array[4]; + array.remove(0, 6); // 4 bytes of Enttec protocol + 2 of ESTA ID + array.replace(ENTTEC_PRO_END_OF_MSG, '\0'); // replace Enttec termination with string termination + strParam = QString(array); + + ftdi_usb_close(&m_handle); + + return true; + + // retry in case no data is read immediately + usleep(100000); + } - QByteArray array; - usleep(300000); // give some time to the device to respond - int read = ftdi_read_data(&m_handle, buffer, 40); - //qDebug() << Q_FUNC_INFO << "Data read: " << read; - array = QByteArray::fromRawData((char*) buffer, read); - - if (array[0] != ENTTEC_PRO_START_OF_MSG) - qDebug() << Q_FUNC_INFO << "Reply message wrong start code: " << QString::number(array[0], 16); - *ESTA_code = (array[5] << 8) | array[4]; - array.remove(0, 6); // 4 bytes of Enttec protocol + 2 of ESTA ID - array.replace(ENTTEC_PRO_END_OF_MSG, '\0'); // replace Enttec termination with string termination - - //for (int i = 0; i < array.size(); i++) - // qDebug() << "-Data: " << array[i]; ftdi_usb_close(&m_handle); - return QString(array); + return false; } void LibFTDIInterface::setBusLocation(quint8 location) @@ -174,6 +198,9 @@ QList LibFTDIInterface::interfaces(QList discove if (validInterface(dev_descriptor.idVendor, dev_descriptor.idProduct) == false) continue; + if (dev_descriptor.idVendor != DMXInterface::FTDIVID) + continue; + char ser[256]; memset(ser, 0, 256); char nme[256]; diff --git a/plugins/dmxusb/src/libftdi-interface.h b/plugins/dmxusb/src/libftdi-interface.h index 254ed42855..5cb4336b61 100644 --- a/plugins/dmxusb/src/libftdi-interface.h +++ b/plugins/dmxusb/src/libftdi-interface.h @@ -38,7 +38,7 @@ class LibFTDIInterface : public DMXInterface static QList interfaces(QList discoveredList); /** @reimpl */ - QString readLabel(uchar label, int *ESTA_code); + bool readLabel(uchar label, int &intParam, QString &strParam); void setBusLocation(quint8 location); diff --git a/plugins/dmxusb/src/nanodmx.cpp b/plugins/dmxusb/src/nanodmx.cpp index 888e43e604..c34b2d2009 100644 --- a/plugins/dmxusb/src/nanodmx.cpp +++ b/plugins/dmxusb/src/nanodmx.cpp @@ -52,7 +52,7 @@ bool NanoDMX::checkReply() bool ok = false; uchar res; - res = interface()->readByte(&ok); + res = iface()->readByte(&ok); if (ok == false || res != 0x47) return false; @@ -60,7 +60,7 @@ bool NanoDMX::checkReply() #else QByteArray reply = m_file.readAll(); //qDebug() << Q_FUNC_INFO << "Reply: " << QString::number(reply[0], 16); - for (int i = 0; i < reply.count(); i++) + for (int i = 0; i < reply.length(); i++) { if (reply[i] == 'G') { @@ -79,7 +79,7 @@ bool NanoDMX::sendChannelValue(int channel, uchar value) QByteArray chanMsg; QString msg; chanMsg.append(msg.asprintf("C%03dL%03d", channel, value).toUtf8()); - return interface()->write(chanMsg); + return iface()->write(chanMsg); } #ifndef QTSERIAL @@ -91,7 +91,7 @@ QString NanoDMX::getDeviceName() // 1- scan all the devices in the device bus foreach (QString dir, devDirs) { - if (dir.startsWith(QString::number(interface()->busLocation())) && + if (dir.startsWith(QString::number(iface()->busLocation())) && dir.contains(".") && dir.contains(":") == false) { @@ -120,7 +120,7 @@ QString NanoDMX::getDeviceName() if (ttyDir.exists()) { QStringList ttyList = ttyDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); - foreach(QString ttyName, ttyList) + foreach (QString ttyName, ttyList) { qDebug() << "This NanoDMX adapter will use" << QString("/dev/" + ttyName); return QString("/dev/" + ttyName); @@ -169,7 +169,7 @@ bool NanoDMX::open(quint32 line, bool input) /* Check connection */ initSequence.append("C?"); #ifdef QTSERIAL - if (interface()->write(initSequence) == true) + if (iface()->write(initSequence) == true) #else if (m_file.write(initSequence) == true) #endif @@ -184,7 +184,7 @@ bool NanoDMX::open(quint32 line, bool input) initSequence.clear(); initSequence.append("N511"); #ifdef QTSERIAL - if (interface()->write(initSequence) == true) + if (iface()->write(initSequence) == true) #else if (m_file.write(initSequence) == true) #endif @@ -251,7 +251,7 @@ QString NanoDMX::additionalInfo() const * Write universe data ****************************************************************************/ -bool NanoDMX::writeUniverse(quint32 universe, quint32 output, const QByteArray& data) +bool NanoDMX::writeUniverse(quint32 universe, quint32 output, const QByteArray& data, bool dataChanged) { Q_UNUSED(universe) Q_UNUSED(output) @@ -267,8 +267,12 @@ bool NanoDMX::writeUniverse(quint32 universe, quint32 output, const QByteArray& //qDebug() << "Writing universe..."; if (m_outputLines[0].m_universeData.size() == 0) + { m_outputLines[0].m_universeData.append(data); - else + m_outputLines[0].m_universeData.append(DMX_CHANNELS - data.size(), 0); + } + + if (dataChanged) m_outputLines[0].m_universeData.replace(0, data.size(), data); return true; @@ -303,7 +307,7 @@ void NanoDMX::run() for (int i = 0; i < m_outputLines[0].m_universeData.length(); i++) { - uchar val = uchar(m_outputLines[0].m_universeData[i]); + char val = m_outputLines[0].m_universeData[i]; if (val == m_outputLines[0].m_compareData[i]) continue; @@ -322,14 +326,14 @@ void NanoDMX::run() } fastTrans.append(val); #ifdef QTSERIAL - if (interface()->write(fastTrans) == false) + if (iface()->write(fastTrans) == false) #else if (m_file.write(fastTrans) <= 0) #endif { qWarning() << Q_FUNC_INFO << name() << "will not accept DMX data"; #ifdef QTSERIAL - interface()->purgeBuffers(); + iface()->purgeBuffers(); #endif continue; } @@ -338,7 +342,7 @@ void NanoDMX::run() m_outputLines[0].m_compareData[i] = val; #ifdef QTSERIAL if (checkReply() == false) - interface()->purgeBuffers(); + iface()->purgeBuffers(); #endif } } diff --git a/plugins/dmxusb/src/nanodmx.h b/plugins/dmxusb/src/nanodmx.h index 55056420b9..e715d84571 100644 --- a/plugins/dmxusb/src/nanodmx.h +++ b/plugins/dmxusb/src/nanodmx.h @@ -53,7 +53,7 @@ class NanoDMX : public QThread, public DMXUSBWidget QString additionalInfo() const; /** @reimp */ - bool writeUniverse(quint32 universe, quint32 output, const QByteArray& data); + bool writeUniverse(quint32 universe, quint32 output, const QByteArray& data, bool dataChanged); protected: /** Stop the writer thread */ diff --git a/plugins/dmxusb/src/qtserial-interface.cpp b/plugins/dmxusb/src/qtserial-interface.cpp index bdf8bb3aa1..6311e43e82 100644 --- a/plugins/dmxusb/src/qtserial-interface.cpp +++ b/plugins/dmxusb/src/qtserial-interface.cpp @@ -42,13 +42,14 @@ QtSerialInterface::~QtSerialInterface() close(); } -QString QtSerialInterface::readLabel(uchar label, int *ESTA_code) +bool QtSerialInterface::readLabel(uchar label, int &intParam, QString &strParam) { QSerialPort serial; serial.setPort(m_info); if (serial.open(QIODevice::ReadWrite) == false) - return QString(); + return false; + serial.setReadBufferSize(1024); serial.setDataBits(QSerialPort::Data8); @@ -67,11 +68,11 @@ QString QtSerialInterface::readLabel(uchar label, int *ESTA_code) if (serial.write(request) < 0) { qDebug() << Q_FUNC_INFO << "Cannot write data to device"; - return QString(); + return false; } serial.waitForBytesWritten(20); - char *buffer = (char*) malloc(sizeof(char) * 40); + char buffer[40]; Q_ASSERT(buffer != NULL); QByteArray array; @@ -81,17 +82,36 @@ QString QtSerialInterface::readLabel(uchar label, int *ESTA_code) //qDebug() << Q_FUNC_INFO << "Data read: " << read; array = QByteArray::fromRawData((char*) buffer, read); + if (array.size() == 0) + return false; + if (array[0] != ENTTEC_PRO_START_OF_MSG) + { qDebug() << Q_FUNC_INFO << "Reply message wrong start code: " << QString::number(array[0], 16); - *ESTA_code = (array[5] << 8) | array[4]; - array.remove(0, 6); // 4 bytes of Enttec protocol + 2 of ESTA ID + return false; + } + + // start | label | data length + if (array.size() < 4) + return false; + + int dataLen = (array[3] << 8) | array[2]; + if (dataLen == 1) + { + intParam = array[4]; + return true; + } + + intParam = (array[5] << 8) | array[4]; + array.remove(0, 6); // 4 bytes of Enttec protocol + 2 byte of ESTA ID array.replace(ENTTEC_PRO_END_OF_MSG, '\0'); // replace Enttec termination with string termination + strParam = QString(array); //for (int i = 0; i < array.size(); i++) // qDebug() << "-Data: " << array[i]; serial.close(); - return QString(array); + return true; } DMXInterface::Type QtSerialInterface::type() @@ -121,7 +141,10 @@ QList QtSerialInterface::interfaces(QList discov if (validInterface(info.vendorIdentifier(), info.productIdentifier()) == false) continue; -#if defined(Q_OS_OSX) + if (info.vendorIdentifier() == DMXInterface::FTDIVID) + continue; + +#if defined(Q_OS_MACOS) /* Qt 5.6+ reports the same device as "cu" and "tty". Only the first will be considered */ if (info.portName().startsWith("tty")) continue; diff --git a/plugins/dmxusb/src/qtserial-interface.h b/plugins/dmxusb/src/qtserial-interface.h index 858d6e7ba5..78b4b71fba 100644 --- a/plugins/dmxusb/src/qtserial-interface.h +++ b/plugins/dmxusb/src/qtserial-interface.h @@ -39,7 +39,7 @@ class QtSerialInterface : public DMXInterface void setInfo(QSerialPortInfo info); /** @reimpl */ - QString readLabel(uchar label, int *ESTA_code); + bool readLabel(uchar label, int &intParam, QString &strParam); /************************************************************************ * DMX/Serial Interface Methods diff --git a/plugins/dmxusb/src/src.pro b/plugins/dmxusb/src/src.pro index 08a667261b..aae7b4f956 100644 --- a/plugins/dmxusb/src/src.pro +++ b/plugins/dmxusb/src/src.pro @@ -22,15 +22,11 @@ unix: { CONFIG += libftdi } -macx: { - CONFIG += qtserial -} - -CONFIG(qtserial) { - message(Building with QtSerialport support.) - DEFINES += QTSERIAL - QT += serialport -} +# always include QtSerial for DMXKing MAX devices +CONFIG += qtserial +message(Building with QtSerialport support.) +DEFINES += QTSERIAL +QT += serialport CONFIG(ftd2xx) { # FTD2XX is a proprietary interface by FTDI Ltd. and would therefore taint the @@ -44,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/stageprofi.cpp b/plugins/dmxusb/src/stageprofi.cpp index ef50424ae6..f76c7992b3 100644 --- a/plugins/dmxusb/src/stageprofi.cpp +++ b/plugins/dmxusb/src/stageprofi.cpp @@ -43,7 +43,7 @@ bool Stageprofi::checkReply() bool ok = false; uchar res; - res = interface()->readByte(&ok); + res = iface()->readByte(&ok); if (ok == false || res != 0x47) return false; @@ -72,7 +72,7 @@ bool Stageprofi::sendChannelValue(int channel, uchar value) QByteArray chanMsg; QString msg; chanMsg.append(msg.asprintf("C%03dL%03d", channel, value).toUtf8()); - return interface()->write(chanMsg); + return iface()->write(chanMsg); } /**************************************************************************** @@ -91,7 +91,7 @@ bool Stageprofi::open(quint32 line, bool input) /* Check connection */ initSequence.append("C?"); - if (interface()->write(initSequence) == true) + if (iface()->write(initSequence) == true) { if (checkReply() == false) qWarning() << Q_FUNC_INFO << name() << "Initialization failed"; @@ -102,7 +102,7 @@ bool Stageprofi::open(quint32 line, bool input) /* set the DMX OUT channels number */ initSequence.clear(); initSequence.append("N511"); - if (interface()->write(initSequence) == true) + if (iface()->write(initSequence) == true) { if (checkReply() == false) qWarning() << Q_FUNC_INFO << name() << "Channels initialization failed"; @@ -156,7 +156,7 @@ QString Stageprofi::additionalInfo() const * Write universe data ****************************************************************************/ -bool Stageprofi::writeUniverse(quint32 universe, quint32 output, const QByteArray& data) +bool Stageprofi::writeUniverse(quint32 universe, quint32 output, const QByteArray& data, bool dataChanged) { Q_UNUSED(universe) Q_UNUSED(output) @@ -165,8 +165,12 @@ bool Stageprofi::writeUniverse(quint32 universe, quint32 output, const QByteArra return false; if (m_outputLines[0].m_universeData.size() == 0) + { m_outputLines[0].m_universeData.append(data); - else + m_outputLines[0].m_universeData.append(DMX_CHANNELS - data.size(), 0); + } + + if (dataChanged) m_outputLines[0].m_universeData.replace(0, data.size(), data); return true; @@ -201,7 +205,7 @@ void Stageprofi::run() for (int i = 0; i < m_outputLines[0].m_universeData.length(); i++) { - uchar val = uchar(m_outputLines[0].m_universeData[i]); + char val = m_outputLines[0].m_universeData[i]; if (val == m_outputLines[0].m_compareData[i]) continue; @@ -219,17 +223,17 @@ void Stageprofi::run() } fastTrans.append(val); - if (interface()->write(fastTrans) == false) + if (iface()->write(fastTrans) == false) { qWarning() << Q_FUNC_INFO << name() << "will not accept DMX data"; - interface()->purgeBuffers(); + iface()->purgeBuffers(); continue; } else { m_outputLines[0].m_compareData[i] = val; if (checkReply() == false) - interface()->purgeBuffers(); + iface()->purgeBuffers(); } } diff --git a/plugins/dmxusb/src/stageprofi.h b/plugins/dmxusb/src/stageprofi.h index 5f3fdac9b6..f33d29205e 100644 --- a/plugins/dmxusb/src/stageprofi.h +++ b/plugins/dmxusb/src/stageprofi.h @@ -51,7 +51,7 @@ class Stageprofi : public QThread, public DMXUSBWidget QString additionalInfo() const; /** @reimp */ - bool writeUniverse(quint32 universe, quint32 output, const QByteArray& data); + bool writeUniverse(quint32 universe, quint32 output, const QByteArray& data, bool dataChanged); protected: /** Stop the writer thread */ diff --git a/plugins/dmxusb/src/vinceusbdmx512.cpp b/plugins/dmxusb/src/vinceusbdmx512.cpp index d95e6da168..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 ****************************************************************************/ @@ -67,112 +51,102 @@ bool VinceUSBDMX512::open(quint32 line, bool input) if (DMXUSBWidget::open() == false) return false; - if (interface()->clearRts() == false) + if (iface()->clearRts() == false) return false; // Write two null bytes - if (interface()->write(QByteArray(2, 0x00)) == false) + 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 interface()->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 = interface()->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 = interface()->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 = interface()->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; } -bool VinceUSBDMX512::writeUniverse(quint32 universe, quint32 output, const QByteArray& data) +/**************************************************************************** + * Outputs + ****************************************************************************/ + +bool VinceUSBDMX512::writeUniverse(quint32 universe, quint32 output, const QByteArray& data, bool dataChanged) { Q_UNUSED(universe) Q_UNUSED(output) @@ -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 (data == m_universe) - 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 7ddc58d591..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 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/dmxusb/src/z65-dmxusb.rules b/plugins/dmxusb/src/z65-dmxusb.rules index affec80fee..36c4a47072 100644 --- a/plugins/dmxusb/src/z65-dmxusb.rules +++ b/plugins/dmxusb/src/z65-dmxusb.rules @@ -1,8 +1,9 @@ # These rules should work on newer udev architecture as well as the older one. # Basically they watch for all "usb" subsystem add/change events, that occur -# for devices with VID==0403 and PID==6001 (meaning FTDI-based devices), and +# for devices with specific VID/PID (e.g. FTDI, Atmel, NXP, etc), and # set their device nodes' permissions so that ALL users can read and write to # them. The devices nodes are found under /dev/bus/usb/xxx/yyy. +# You might need to add your user to the 'dialout' group as well. # Generic FTDI Products SUBSYSTEM=="usb*", ACTION=="add|change", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", MODE="0666" @@ -13,3 +14,6 @@ SUBSYSTEM=="usb*", ACTION=="add|change", ATTRS{idVendor}=="03eb", ATTRS{idProduc # EUROLITE Product SUBSYSTEM=="usb*", ACTION=="add|change", ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="fa63", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1" + +# DMXKing MAX Products +SUBSYSTEM=="usb*", ACTION=="add|change", ATTRS{idVendor}=="1fc9", ATTRS{idProduct}=="0094", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1" diff --git a/plugins/dummy/CMakeLists.txt b/plugins/dummy/CMakeLists.txt new file mode 100644 index 0000000000..af0bce41b2 --- /dev/null +++ b/plugins/dummy/CMakeLists.txt @@ -0,0 +1,48 @@ +set(module_name "dummy") + +set(TS_FILES + Dummy_de_DE.ts + Dummy_es_ES.ts + Dummy_fi_FI.ts + Dummy_fr_FR.ts + Dummy_it_IT.ts + Dummy_nl_NL.ts + Dummy_cz_CZ.ts + Dummy_pt_BR.ts + Dummy_ca_ES.ts + Dummy_ja_JP.ts +) + +if(QT_VERSION_MAJOR GREATER 5) + qt_add_translation(QM_FILES ${TS_FILES}) +else() + qt5_add_translation(QM_FILES ${TS_FILES}) +endif() + +project(${module_name}) + +find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Gui Widgets) + +add_library(${module_name} SHARED + ${QM_FILES} +) + +target_sources(${module_name} PRIVATE + ../interfaces/qlcioplugin.cpp ../interfaces/qlcioplugin.h + dummyconfiguration.cpp dummyconfiguration.h dummyconfiguration.ui + dummyplugin.cpp dummyplugin.h +) +target_include_directories(${module_name} PRIVATE + ../interfaces +) + +target_link_libraries(${module_name} PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Widgets +) + +install(TARGETS ${module_name} + LIBRARY DESTINATION ${INSTALLROOT}/${PLUGINDIR} + RUNTIME DESTINATION ${INSTALLROOT}/${PLUGINDIR} +) diff --git a/plugins/dummy/dummyconfiguration.cpp b/plugins/dummy/dummyconfiguration.cpp index d466bd38e3..9c735034d5 100644 --- a/plugins/dummy/dummyconfiguration.cpp +++ b/plugins/dummy/dummyconfiguration.cpp @@ -17,9 +17,13 @@ limitations under the License. */ +#include + #include "dummyconfiguration.h" #include "dummyplugin.h" +#define SETTINGS_GEOMETRY "dummyconfiguration/geometry" + /***************************************************************************** * Initialization *****************************************************************************/ @@ -33,6 +37,11 @@ DummyConfiguration::DummyConfiguration(DummyPlugin* plugin, QWidget* parent) /* Setup UI controls */ setupUi(this); + QSettings settings; + QVariant geometrySettings = settings.value(SETTINGS_GEOMETRY); + if (geometrySettings.isValid() == true) + restoreGeometry(geometrySettings.toByteArray()); + /** * Do the dialog initializations here. * E.g.: fill combo boxes, set spin boxes values, fill lists, etc... @@ -41,7 +50,8 @@ DummyConfiguration::DummyConfiguration(DummyPlugin* plugin, QWidget* parent) DummyConfiguration::~DummyConfiguration() { - /** Cleanup the allocated resources, if any */ + QSettings settings; + settings.setValue(SETTINGS_GEOMETRY, saveGeometry()); } /***************************************************************************** diff --git a/plugins/dummy/dummyplugin.cpp b/plugins/dummy/dummyplugin.cpp index a557143dd1..bf64e6a3dc 100644 --- a/plugins/dummy/dummyplugin.cpp +++ b/plugins/dummy/dummyplugin.cpp @@ -126,11 +126,12 @@ QString DummyPlugin::outputInfo(quint32 output) return str; } -void DummyPlugin::writeUniverse(quint32 universe, quint32 output, const QByteArray &data) +void DummyPlugin::writeUniverse(quint32 universe, quint32 output, const QByteArray &data, bool dataChanged) { Q_UNUSED(universe) Q_UNUSED(output) Q_UNUSED(data) + Q_UNUSED(dataChanged) /** Check for output index validity and, in case, return. * @@ -199,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 d7e640ab79..7a5e7f558a 100644 --- a/plugins/dummy/dummyplugin.h +++ b/plugins/dummy/dummyplugin.h @@ -65,7 +65,7 @@ class DummyPlugin : public QLCIOPlugin QString outputInfo(quint32 output); /** @reimp */ - void writeUniverse(quint32 universe, quint32 output, const QByteArray& data); + void writeUniverse(quint32 universe, quint32 output, const QByteArray& data, bool dataChanged); /************************************************************************* * Inputs - If the plugin doesn't provide input @@ -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/CMakeLists.txt b/plugins/enttecwing/CMakeLists.txt new file mode 100644 index 0000000000..6c2b4a92ad --- /dev/null +++ b/plugins/enttecwing/CMakeLists.txt @@ -0,0 +1,4 @@ +project(enttecwing) + +add_subdirectory(src) +add_subdirectory(test) diff --git a/plugins/enttecwing/src/CMakeLists.txt b/plugins/enttecwing/src/CMakeLists.txt new file mode 100644 index 0000000000..549d07a9ca --- /dev/null +++ b/plugins/enttecwing/src/CMakeLists.txt @@ -0,0 +1,60 @@ +set(module_name "enttecwing") + +set(TS_FILES + ENTTEC_Wing_fi_FI.ts + ENTTEC_Wing_de_DE.ts + ENTTEC_Wing_es_ES.ts + ENTTEC_Wing_fr_FR.ts + ENTTEC_Wing_it_IT.ts + ENTTEC_Wing_nl_NL.ts + ENTTEC_Wing_cz_CZ.ts + ENTTEC_Wing_pt_BR.ts + ENTTEC_Wing_ca_ES.ts + ENTTEC_Wing_ja_JP.ts +) + +if(QT_VERSION_MAJOR GREATER 5) + qt_add_translation(QM_FILES ${TS_FILES}) +else() + qt5_add_translation(QM_FILES ${TS_FILES}) +endif() + +add_library(${module_name} + SHARED + ${QM_FILES} +) + +target_sources(${module_name} PRIVATE + ../../interfaces/qlcioplugin.cpp ../../interfaces/qlcioplugin.h + ${module_name}.cpp ${module_name}.h + playbackwing.cpp playbackwing.h + programwing.cpp programwing.h + shortcutwing.cpp shortcutwing.h + wing.cpp wing.h +) +target_include_directories(${module_name} PRIVATE + ../../interfaces +) + +target_link_libraries(${module_name} PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Network + Qt${QT_MAJOR_VERSION}::Widgets +) + +if(WIN32) + target_compile_definitions(${module_name} PRIVATE + QLC_EXPORT + ) +endif() + +install(TARGETS ${module_name} + LIBRARY DESTINATION ${INSTALLROOT}/${PLUGINDIR} + RUNTIME DESTINATION ${INSTALLROOT}/${PLUGINDIR} +) + +if (UNIX AND NOT APPLE) + install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/org.qlcplus.QLCPlus.enttecwing.metainfo.xml" + DESTINATION "${METAINFODIR}") +endif() 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/enttecwing/src/playbackwing.cpp b/plugins/enttecwing/src/playbackwing.cpp index b42fc0759e..f7ddcc6a26 100644 --- a/plugins/enttecwing/src/playbackwing.cpp +++ b/plugins/enttecwing/src/playbackwing.cpp @@ -193,7 +193,7 @@ QString PlaybackWing::name() const void PlaybackWing::parseData(const QByteArray& data) { - if (data.size() < WING_PLAYBACK_PACKET_SIZE ) + if (data.size() < WING_PLAYBACK_PACKET_SIZE) { qWarning() << Q_FUNC_INFO << "Expected at least" << WING_PLAYBACK_PACKET_SIZE << "bytes for buttons but got only" << data.size(); @@ -254,7 +254,7 @@ void PlaybackWing::parseData(const QByteArray& data) //check sync status int curdiff = quint8(m_feedbackValues[page()][slider]) - quint8(data[WING_PLAYBACK_BYTE_SLIDER + slider]); - // send input after crossing widget values ( sign of diff is changing) + // send input after crossing widget values (sign of diff is changing) if (curdiff == 0 || (curdiff > 0 && diff < 0) || (curdiff < 0 && diff > 0)) { setCacheValue(slider, value); @@ -273,7 +273,7 @@ void PlaybackWing::parseData(const QByteArray& data) void PlaybackWing::applyExtraButtons(const QByteArray& data) { /* Check that there's enough data for flags */ - if (data.size() < WING_PLAYBACK_PACKET_SIZE ) + if (data.size() < WING_PLAYBACK_PACKET_SIZE) return; // WING_PLAYBACK_BIT_PAGEUP diff --git a/plugins/enttecwing/test/CMakeLists.txt b/plugins/enttecwing/test/CMakeLists.txt new file mode 100644 index 0000000000..40345bf0ad --- /dev/null +++ b/plugins/enttecwing/test/CMakeLists.txt @@ -0,0 +1,32 @@ + +add_executable(enttecwing_test WIN32 MACOSX_BUNDLE + main.cpp + playbackwing_test.cpp playbackwing_test.h + programwing_test.cpp programwing_test.h + shortcutwing_test.cpp shortcutwing_test.h + wing_test.cpp wing_test.h +) +target_include_directories(enttecwing_test PRIVATE + ../../interfaces + ../src +) + +target_link_libraries(enttecwing_test PRIVATE + # Remove: L../src + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Network + Qt${QT_MAJOR_VERSION}::Test + Qt${QT_MAJOR_VERSION}::Widgets + enttecwing +) + +if(WIN32) + target_compile_definitions(enttecwing_test PRIVATE + QLC_EXPORT + ) +endif() + +# Consider using qt_generate_deploy_app_script() for app deployment if +# the project can use Qt 6.3. In that case rerun qmake2cmake with +# --min-qt-version=6.3. diff --git a/plugins/gpio/CMakeLists.txt b/plugins/gpio/CMakeLists.txt new file mode 100644 index 0000000000..3b963bedca --- /dev/null +++ b/plugins/gpio/CMakeLists.txt @@ -0,0 +1,48 @@ +set(module_name "gpio") + +set(TS_FILES + GPIO_de_DE.ts + GPIO_es_ES.ts + GPIO_fi_FI.ts + GPIO_fr_FR.ts + GPIO_it_IT.ts + GPIO_nl_NL.ts + GPIO_cz_CZ.ts + GPIO_pt_BR.ts + GPIO_ca_ES.ts + GPIO_ja_JP.ts +) + +if(QT_VERSION_MAJOR GREATER 5) + qt_add_translation(QM_FILES ${TS_FILES}) +else() + qt5_add_translation(QM_FILES ${TS_FILES}) +endif() + +project(${module_name}) + +find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Gui Widgets) + +add_library(${module_name} SHARED + ${QM_FILES} +) +target_sources(${module_name} PRIVATE + ../interfaces/qlcioplugin.cpp ../interfaces/qlcioplugin.h + gpioconfiguration.cpp gpioconfiguration.h gpioconfiguration.ui + gpioplugin.cpp gpioplugin.h + gpioreaderthread.cpp gpioreaderthread.h +) +target_include_directories(${module_name} PRIVATE + ../interfaces +) + +target_link_libraries(${module_name} PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Widgets +) + +install(TARGETS ${module_name} + LIBRARY DESTINATION ${INSTALLROOT}/${PLUGINDIR} + RUNTIME DESTINATION ${INSTALLROOT}/${PLUGINDIR} +) diff --git a/plugins/gpio/gpioconfiguration.cpp b/plugins/gpio/gpioconfiguration.cpp index c9eeffbf56..018efeaf06 100644 --- a/plugins/gpio/gpioconfiguration.cpp +++ b/plugins/gpio/gpioconfiguration.cpp @@ -18,6 +18,7 @@ */ #include +#include #include "gpioconfiguration.h" #include "gpioplugin.h" @@ -25,6 +26,8 @@ #define KColumnGPIONumber 0 #define KColumnGPIOUsage 1 +#define SETTINGS_GEOMETRY "gpioconfiguration/geometry" + /***************************************************************************** * Initialization *****************************************************************************/ @@ -38,17 +41,23 @@ GPIOConfiguration::GPIOConfiguration(GPIOPlugin* plugin, QWidget* parent) /* Setup UI controls */ setupUi(this); + QSettings settings; + QVariant geometrySettings = settings.value(SETTINGS_GEOMETRY); + if (geometrySettings.isValid() == true) + restoreGeometry(geometrySettings.toByteArray()); + fillTree(); } GPIOConfiguration::~GPIOConfiguration() { - /** Cleanup the allocated resources, if any */ + QSettings settings; + settings.setValue(SETTINGS_GEOMETRY, saveGeometry()); } void GPIOConfiguration::fillTree() { - foreach(GPIOPinInfo* gpio, m_plugin->gpioList()) + foreach (GPIOPinInfo* gpio, m_plugin->gpioList()) { QTreeWidgetItem* item = new QTreeWidgetItem(m_treeWidget); item->setText(KColumnGPIONumber, QString::number(gpio->m_number)); @@ -73,7 +82,7 @@ void GPIOConfiguration::accept() { QList gpioList = m_plugin->gpioList(); - for(int i = 0; i < m_treeWidget->topLevelItemCount(); i++) + for (int i = 0; i < m_treeWidget->topLevelItemCount(); i++) { QTreeWidgetItem *item = m_treeWidget->topLevelItem(i); diff --git a/plugins/gpio/gpioplugin.cpp b/plugins/gpio/gpioplugin.cpp index d7d71143e9..bb2b134241 100644 --- a/plugins/gpio/gpioplugin.cpp +++ b/plugins/gpio/gpioplugin.cpp @@ -136,9 +136,10 @@ QString GPIOPlugin::outputInfo(quint32 output) return str; } -void GPIOPlugin::writeUniverse(quint32 universe, quint32 output, const QByteArray &data) +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/gpio/gpioplugin.h b/plugins/gpio/gpioplugin.h index fe68397bbc..abba0051e5 100644 --- a/plugins/gpio/gpioplugin.h +++ b/plugins/gpio/gpioplugin.h @@ -106,7 +106,7 @@ class GPIOPlugin : public QLCIOPlugin QString outputInfo(quint32 output); /** @reimp */ - void writeUniverse(quint32 universe, quint32 output, const QByteArray& data); + void writeUniverse(quint32 universe, quint32 output, const QByteArray& data, bool dataChanged); /************************************************************************* * Inputs diff --git a/plugins/gpio/gpioreaderthread.cpp b/plugins/gpio/gpioreaderthread.cpp index 3b23094940..f4f07201cb 100644 --- a/plugins/gpio/gpioreaderthread.cpp +++ b/plugins/gpio/gpioreaderthread.cpp @@ -99,7 +99,7 @@ void ReadThread::run() QMutexLocker locker(&m_mutex); - foreach(GPIOPinInfo *gpio, m_readList) + foreach (GPIOPinInfo *gpio, m_readList) { if (gpio->m_file == NULL || gpio->m_file->isOpen() == false) continue; diff --git a/plugins/hid/CMakeLists.txt b/plugins/hid/CMakeLists.txt new file mode 100644 index 0000000000..0b4e743aa8 --- /dev/null +++ b/plugins/hid/CMakeLists.txt @@ -0,0 +1,94 @@ +set(module_name "hidplugin") + +set(TS_FILES + HID_fi_FI.ts + HID_de_DE.ts + HID_es_ES.ts + HID_fr_FR.ts + HID_it_IT.ts + HID_nl_NL.ts + HID_cz_CZ.ts + HID_pt_BR.ts + HID_ca_ES.ts + HID_ja_JP.ts +) + +if(QT_VERSION_MAJOR GREATER 5) + qt_add_translation(QM_FILES ${TS_FILES}) +else() + qt5_add_translation(QM_FILES ${TS_FILES}) +endif() + +add_library(${module_name} SHARED + ${QM_FILES} +) + +target_sources(${module_name} PRIVATE + ../interfaces/qlcioplugin.cpp ../interfaces/qlcioplugin.h + configurehid.cpp configurehid.h configurehid.ui + hiddevice.cpp hiddevice.h + hiddmxdevice.cpp hiddmxdevice.h + hidjsdevice.cpp hidjsdevice.h + ${module_name}.cpp ${module_name}.h +) +target_include_directories(${module_name} PRIVATE + ../interfaces +) + +target_link_libraries(${module_name} PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Widgets +) + +if(UNIX AND NOT APPLE) + target_sources(${module_name} PRIVATE + linux/hidapi.cpp + linux/hidlinuxjoystick.cpp linux/hidlinuxjoystick.h + ) + + target_include_directories(${module_name} PRIVATE + linux + ) +endif() + +if(APPLE) + target_sources(${module_name} PRIVATE + macx/hidapi.cpp + macx/hidosxjoystick.cpp macx/hidosxjoystick.h + ) + + target_include_directories(${module_name} PRIVATE + macx + ) + + target_link_libraries(${module_name} PRIVATE + "-framework CoreFoundation" + "-framework IOKit" + ) +endif() + +if(WIN32) + target_sources(${module_name} PRIVATE + win32/hidapi.cpp + win32/hidwindowsjoystick.cpp win32/hidwindowsjoystick.h + ) + + target_link_libraries(${module_name} PRIVATE + hid + setupapi + winmm + ) +endif() + +if (UNIX AND NOT APPLE) + install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/linux/z65-fx5-hid.rules" + DESTINATION ${UDEVRULESDIR}) + install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/linux/org.qlcplus.QLCPlus.hid.metainfo.xml" + DESTINATION ${METAINFODIR}) +endif() + +install(TARGETS ${module_name} + LIBRARY DESTINATION ${INSTALLROOT}/${PLUGINDIR} + RUNTIME DESTINATION ${INSTALLROOT}/${PLUGINDIR} +) diff --git a/plugins/hid/HID_ca_ES.ts b/plugins/hid/HID_ca_ES.ts index 79f9032a3c..2654059929 100644 --- a/plugins/hid/HID_ca_ES.ts +++ b/plugins/hid/HID_ca_ES.ts @@ -19,7 +19,12 @@ Nom - + + Merger Mode + + + + Refresh Actualitzar diff --git a/plugins/hid/HID_cz_CZ.ts b/plugins/hid/HID_cz_CZ.ts index 3fec60b4d1..8ef15b65c3 100644 --- a/plugins/hid/HID_cz_CZ.ts +++ b/plugins/hid/HID_cz_CZ.ts @@ -19,7 +19,12 @@ Název - + + Merger Mode + + + + Refresh Obnovit diff --git a/plugins/hid/HID_de_DE.ts b/plugins/hid/HID_de_DE.ts index 1d8c2a0152..f96f950390 100644 --- a/plugins/hid/HID_de_DE.ts +++ b/plugins/hid/HID_de_DE.ts @@ -19,7 +19,12 @@ Name - + + Merger Mode + + + + Refresh Aktualisieren diff --git a/plugins/hid/HID_es_ES.ts b/plugins/hid/HID_es_ES.ts index 51f452f055..278f677dbf 100644 --- a/plugins/hid/HID_es_ES.ts +++ b/plugins/hid/HID_es_ES.ts @@ -19,7 +19,12 @@ Nombre - + + Merger Mode + + + + Refresh Actualizar diff --git a/plugins/hid/HID_fi_FI.ts b/plugins/hid/HID_fi_FI.ts index 8a078a71bd..6d5aa64f2c 100644 --- a/plugins/hid/HID_fi_FI.ts +++ b/plugins/hid/HID_fi_FI.ts @@ -19,7 +19,12 @@ Nimi - + + Merger Mode + + + + Refresh Päivitä diff --git a/plugins/hid/HID_fr_FR.ts b/plugins/hid/HID_fr_FR.ts index bf918450fb..e4e26ddce4 100644 --- a/plugins/hid/HID_fr_FR.ts +++ b/plugins/hid/HID_fr_FR.ts @@ -19,7 +19,12 @@ Nom - + + Merger Mode + + + + Refresh Rafraîchir diff --git a/plugins/hid/HID_it_IT.ts b/plugins/hid/HID_it_IT.ts index 81dbbd0f99..c6c4e088d9 100644 --- a/plugins/hid/HID_it_IT.ts +++ b/plugins/hid/HID_it_IT.ts @@ -19,7 +19,12 @@ Nome - + + Merger Mode + + + + Refresh Aggiorna diff --git a/plugins/hid/HID_ja_JP.ts b/plugins/hid/HID_ja_JP.ts index b0eb15f06d..5d550bf16b 100644 --- a/plugins/hid/HID_ja_JP.ts +++ b/plugins/hid/HID_ja_JP.ts @@ -19,7 +19,12 @@ - + + Merger Mode + + + + Refresh 更新 diff --git a/plugins/hid/HID_nl_NL.ts b/plugins/hid/HID_nl_NL.ts index abadd9988a..c84905f9c1 100644 --- a/plugins/hid/HID_nl_NL.ts +++ b/plugins/hid/HID_nl_NL.ts @@ -19,7 +19,12 @@ Naam - + + Merger Mode + + + + Refresh Ververs diff --git a/plugins/hid/HID_pt_BR.ts b/plugins/hid/HID_pt_BR.ts index a36f1c079e..cbcb0dda4c 100644 --- a/plugins/hid/HID_pt_BR.ts +++ b/plugins/hid/HID_pt_BR.ts @@ -19,7 +19,12 @@ Nome - + + Merger Mode + + + + Refresh Actualizar diff --git a/plugins/hid/configurehid.cpp b/plugins/hid/configurehid.cpp index 1445cc220f..e9dda7f2b1 100644 --- a/plugins/hid/configurehid.cpp +++ b/plugins/hid/configurehid.cpp @@ -24,6 +24,8 @@ #include #include #include +#include +#include #include "configurehid.h" #include "hiddevice.h" @@ -31,6 +33,10 @@ #define KColumnNumber 0 #define KColumnName 1 +#define KColumnMerger 2 +#define PROP_DEV "dev" + +#define SETTINGS_GEOMETRY "configurehid/geometry" /***************************************************************************** * Initialization @@ -45,6 +51,11 @@ ConfigureHID::ConfigureHID(QWidget* parent, HIDPlugin* plugin) /* Setup UI controls */ setupUi(this); + QSettings settings; + QVariant geometrySettings = settings.value(SETTINGS_GEOMETRY); + if (geometrySettings.isValid() == true) + restoreGeometry(geometrySettings.toByteArray()); + connect(m_refreshButton, SIGNAL(clicked()), this, SLOT(slotRefreshClicked())); @@ -59,6 +70,8 @@ ConfigureHID::ConfigureHID(QWidget* parent, HIDPlugin* plugin) ConfigureHID::~ConfigureHID() { + QSettings settings; + settings.setValue(SETTINGS_GEOMETRY, saveGeometry()); } /***************************************************************************** @@ -90,6 +103,13 @@ void ConfigureHID::refreshList() item->setText(KColumnNumber, s.setNum(i + 1)); item->setText(KColumnName, dev->name()); item->setFlags(item->flags() | Qt::ItemIsUserCheckable); + + if (dev->hasMergerMode()) + { + QWidget* widget = createMergerModeWidget(dev->isMergerModeEnabled()); + widget->setProperty(PROP_DEV, (qulonglong) dev); + m_list->setItemWidget(item, KColumnMerger, widget); + } } m_list->header()->resizeSections(QHeaderView::ResizeToContents); } @@ -114,3 +134,33 @@ void ConfigureHID::slotDeviceRemoved(HIDDevice* device) } } } + +QWidget* ConfigureHID::createMergerModeWidget(bool mergerModeEnabled) +{ + QCheckBox* checkbox = new QCheckBox; + + if (mergerModeEnabled) + checkbox->setCheckState(Qt::Checked); + else + checkbox->setCheckState(Qt::Unchecked); + + connect(checkbox, SIGNAL(stateChanged(int)), this, SLOT(slotMergerModeChanged(int))); + + return checkbox; +} + +void ConfigureHID::slotMergerModeChanged(int state) +{ + QCheckBox* checkbox = qobject_cast (QObject::sender()); + Q_ASSERT(checkbox != NULL); + + QVariant var = checkbox->property(PROP_DEV); + Q_ASSERT(var.isValid() == true); + + HIDDevice* dev = (HIDDevice*) var.toULongLong(); + Q_ASSERT(dev != NULL); + + bool mergerModeEnabled = (state == Qt::Checked); + + dev->enableMergerMode(mergerModeEnabled); +} \ No newline at end of file diff --git a/plugins/hid/configurehid.h b/plugins/hid/configurehid.h index 09daf611ba..cc29d9d26f 100644 --- a/plugins/hid/configurehid.h +++ b/plugins/hid/configurehid.h @@ -52,9 +52,15 @@ private slots: /** Callback for HIDInput::deviceRemoved() signals. */ void slotDeviceRemoved(HIDDevice* device); + /** Change the merger mode. */ + void slotMergerModeChanged(int state); + private: /** Refresh the interface list */ void refreshList(); + + /** Checkbox for merger mode (de-)activation. */ + QWidget* createMergerModeWidget(bool mergerModeEnabled); }; #endif diff --git a/plugins/hid/configurehid.ui b/plugins/hid/configurehid.ui index cbc2b7e5b7..55d46d6483 100644 --- a/plugins/hid/configurehid.ui +++ b/plugins/hid/configurehid.ui @@ -57,6 +57,11 @@ Name + + + Merger Mode + + diff --git a/plugins/hid/hiddevice.cpp b/plugins/hid/hiddevice.cpp index 5be784a6d4..e6bc88683d 100644 --- a/plugins/hid/hiddevice.cpp +++ b/plugins/hid/hiddevice.cpp @@ -25,7 +25,7 @@ HIDDevice::HIDDevice(HIDPlugin* parent, quint32 line, const QString &name, const QString& path) : QThread(parent) { - m_name = name; + m_name = name; m_filename = path; m_file.setFileName(path); m_line = line; @@ -45,6 +45,21 @@ HIDDevice::~HIDDevice() /***************************************************************************** * File operations *****************************************************************************/ +bool HIDDevice::hasMergerMode() +{ + return false; //usual HIDDevices don't offer a merger mode +} + +bool HIDDevice::isMergerModeEnabled() +{ + return false; //never enabled when not offered +} + +void HIDDevice::enableMergerMode(bool mergerModeEnabled) +{ + Q_UNUSED(mergerModeEnabled); +} + bool HIDDevice::openInput() { diff --git a/plugins/hid/hiddevice.h b/plugins/hid/hiddevice.h index 6d352e9d22..a821b1366f 100644 --- a/plugins/hid/hiddevice.h +++ b/plugins/hid/hiddevice.h @@ -41,6 +41,29 @@ class HIDDevice : public QThread * File operations *************************************************************************/ public: + /** + * Check if the device offers a built-in merger mode. + * + * Merger mode means that all DMX data from the device's input port is + * copied to its output port and - if output from QLC+ is enabled - + * merged with this output in a HTP manner on the device itself. + * + * @return true if the device offers a merger mode, false otherwise + */ + virtual bool hasMergerMode(); + + /** + * Check if device's built-in merger mode is enabled. + * + * @return true if the device's merger mode is enabled, false otherwise + */ + virtual bool isMergerModeEnabled(); + + /** + * Enable or disable the built-in merger mode. + */ + virtual void enableMergerMode(bool mergerModeEnabled); + /** * Attempt to open the HID device as input in RW mode and fall back * to RO if that fails. diff --git a/plugins/hid/hiddmxdevice.cpp b/plugins/hid/hiddmxdevice.cpp index 4524dd3a4d..5a643aaa47 100644 --- a/plugins/hid/hiddmxdevice.cpp +++ b/plugins/hid/hiddmxdevice.cpp @@ -75,6 +75,20 @@ void HIDDMXDevice::init() * File operations *****************************************************************************/ +bool HIDDMXDevice::isMergerModeEnabled() +{ + return (m_mode & DMX_MODE_MERGER); +} + +void HIDDMXDevice::enableMergerMode(bool mergerModeEnabled) +{ + if (mergerModeEnabled) + m_mode |= DMX_MODE_MERGER; + else + m_mode &= ~DMX_MODE_MERGER; + updateMode(); +} + bool HIDDMXDevice::openInput() { m_mode |= DMX_MODE_INPUT; @@ -116,7 +130,7 @@ QString HIDDMXDevice::infoText() { QString info; - info += QString("%1

").arg(m_name); + info += QString("

%1

").arg(m_name); return info; } @@ -134,7 +148,7 @@ void HIDDMXDevice::feedBack(quint32 channel, uchar value) void HIDDMXDevice::run() { - while(m_running == true) + while (m_running == true) { unsigned char buffer[35]; int size; @@ -147,9 +161,9 @@ void HIDDMXDevice::run() * from, the nth chunk starts at address n * 32 * [1]-[32] = channel values, where the nth value is the offset + n */ - while(size > 0) + while (size > 0) { - if(size == 33) + if (size == 33) { unsigned short startOff = buffer[0] * 32; if (buffer[0] < 16) @@ -183,10 +197,12 @@ void HIDDMXDevice::outputDMX(const QByteArray &universe, bool forceWrite) int startOff = i * 32; if (startOff >= universe.size()) return; + QByteArray chunk = universe.mid(startOff, 32); if (chunk.size() < 32) chunk.append(QByteArray(32 - chunk.size(), (char)0x0)); - if(forceWrite == true || chunk != m_dmx_cmp.mid(startOff, 32)) + + if (forceWrite == true || chunk != m_dmx_cmp.mid(startOff, 32)) { /** Save different data to m_dmx_cmp */ m_dmx_cmp.replace(startOff, 32, chunk); @@ -214,6 +230,8 @@ void HIDDMXDevice::updateMode() driver_mode += 2; if (m_mode & DMX_MODE_INPUT) driver_mode += 4; + if (m_mode & DMX_MODE_MERGER) + driver_mode += 1; unsigned char buffer[34]; diff --git a/plugins/hid/hiddmxdevice.h b/plugins/hid/hiddmxdevice.h index fb87206281..ed345a93b7 100644 --- a/plugins/hid/hiddmxdevice.h +++ b/plugins/hid/hiddmxdevice.h @@ -64,10 +64,20 @@ class HIDDMXDevice : public HIDDevice /** @reimp */ bool hasOutput() { return true; } + /** @reimp */ + bool hasMergerMode() { return true; /*DE, FX5, and Nodle have a merger mode*/ } + /********************************************************************* * File operations *********************************************************************/ public: + + /** @reimp */ + bool isMergerModeEnabled(); + + /** @reimp */ + void enableMergerMode(bool mergerModeEnabled); + /** @reimp */ bool openInput(); @@ -117,7 +127,8 @@ class HIDDMXDevice : public HIDDevice { DMX_MODE_NONE = 1 << 0, DMX_MODE_OUTPUT = 1 << 1, - DMX_MODE_INPUT = 1 << 2 + DMX_MODE_INPUT = 1 << 2, + DMX_MODE_MERGER = 1 << 3 }; /** mode selection function */ diff --git a/plugins/hid/hidjsdevice.cpp b/plugins/hid/hidjsdevice.cpp index c04b288d84..100d8207da 100644 --- a/plugins/hid/hidjsdevice.cpp +++ b/plugins/hid/hidjsdevice.cpp @@ -26,10 +26,38 @@ #include "hidjsdevice.h" #include "hidplugin.h" +/** +* Helper for constructing the name from the device info delivered by info +* (e.g. manufacturer name, product name, PID, VID, or serial number) +*/ +static QString assembleDevicesName(struct hid_device_info *info) +{ + + QString name_part = QString::fromWCharArray(info->manufacturer_string) + " " + + QString::fromWCharArray(info->product_string); + + if (name_part.trimmed().isEmpty()) + { + //use the vendor and product_id combination if name is empty + name_part = "HID Input Device (" + + QString::number(info->vendor_id, 16).toUpper() + ":" + + QString::number(info->product_id, 16).toUpper() + ")"; + } + + QString serial_number_part = QString::fromWCharArray(info->serial_number); + + if (!serial_number_part.isEmpty()) + { + //use serial number in parenthesis, if available + serial_number_part = " (" + serial_number_part + ")"; + } + + return name_part + serial_number_part; +} + HIDJsDevice::HIDJsDevice(HIDPlugin* parent, quint32 line, struct hid_device_info *info) : HIDDevice(parent, line, - QString::fromWCharArray(info->manufacturer_string) + " " + - QString::fromWCharArray(info->product_string), + assembleDevicesName(info), QString(info->path)) { m_dev_info = (struct hid_device_info*) malloc (sizeof(struct hid_device_info)); @@ -84,7 +112,7 @@ QString HIDJsDevice::infoText() { QString info; - info += QString("%1

").arg(m_name); + info += QString("

%1

").arg(m_name); info += tr("Axes: %1").arg(m_axesNumber); info += QString("
"); info += tr("Buttons: %1").arg(m_buttonsNumber); diff --git a/plugins/hid/hidplugin.cpp b/plugins/hid/hidplugin.cpp index 062d606799..2477787489 100644 --- a/plugins/hid/hidplugin.cpp +++ b/plugins/hid/hidplugin.cpp @@ -209,13 +209,14 @@ QString HIDPlugin::outputInfo(quint32 output) return str; } -void HIDPlugin::writeUniverse(quint32 universe, quint32 output, const QByteArray &data) +void HIDPlugin::writeUniverse(quint32 universe, quint32 output, const QByteArray &data, bool dataChanged) { - Q_UNUSED(universe); + Q_UNUSED(universe) + Q_UNUSED(dataChanged) if (output != QLCIOPlugin::invalidLine()) { - HIDDevice* dev = deviceOutput(output); + HIDDevice *dev = deviceOutput(output); if (dev != NULL) dev->outputDMX(data); } @@ -264,7 +265,7 @@ void HIDPlugin::rescanDevices() /** Device already exists, delete from remove list */ destroyList.removeAll(dev); } - else if((cur_dev->vendor_id == HID_DMX_INTERFACE_VENDOR_ID + else if ((cur_dev->vendor_id == HID_DMX_INTERFACE_VENDOR_ID && cur_dev->product_id == HID_DMX_INTERFACE_PRODUCT_ID) || (cur_dev->vendor_id == HID_DMX_INTERFACE_VENDOR_ID_2 && cur_dev->product_id == HID_DMX_INTERFACE_PRODUCT_ID_2) || @@ -276,7 +277,8 @@ void HIDPlugin::rescanDevices() /* Device is a USB DMX Interface, add it */ dev = new HIDDMXDevice(this, line++, QString::fromWCharArray(cur_dev->manufacturer_string) + " " + - QString::fromWCharArray(cur_dev->product_string), + QString::fromWCharArray(cur_dev->product_string) + " " + + "(" + QString::fromWCharArray(cur_dev->serial_number) + ")", QString(cur_dev->path)); addDevice(dev); } @@ -285,11 +287,11 @@ void HIDPlugin::rescanDevices() { dev = new HIDLinuxJoystick(this, line++, cur_dev); #elif defined(WIN32) || defined (Q_OS_WIN) - else if(HIDWindowsJoystick::isJoystick(cur_dev->vendor_id, cur_dev->product_id) == true) + else if (HIDWindowsJoystick::isJoystick(cur_dev->vendor_id, cur_dev->product_id) == true) { dev = new HIDWindowsJoystick(this, line++, cur_dev); #elif defined (__APPLE__) || defined(Q_OS_MACX) - else if(HIDOSXJoystick::isJoystick(cur_dev->usage) == true) + else if (HIDOSXJoystick::isJoystick(cur_dev->usage) == true) { dev = new HIDOSXJoystick(this, line++, cur_dev); #endif diff --git a/plugins/hid/hidplugin.h b/plugins/hid/hidplugin.h index e38a03b5f5..f860cde693 100644 --- a/plugins/hid/hidplugin.h +++ b/plugins/hid/hidplugin.h @@ -93,7 +93,7 @@ class HIDPlugin : public QLCIOPlugin QString outputInfo(quint32 output); /** @reimp */ - void writeUniverse(quint32 universe, quint32 output, const QByteArray& data); + void writeUniverse(quint32 universe, quint32 output, const QByteArray& data, bool dataChanged); /********************************************************************* * Configuration diff --git a/plugins/hid/linux/hidapi.cpp b/plugins/hid/linux/hidapi.cpp index 56b6a10967..c7446d46c9 100644 --- a/plugins/hid/linux/hidapi.cpp +++ b/plugins/hid/linux/hidapi.cpp @@ -414,7 +414,7 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, devices = udev_enumerate_get_list_entry(enumerate); /* For each item, see if it matches the vid/pid, and if so create a udev_device record for it */ - udev_list_entry_foreach(dev_list_entry, devices) { + udev_list_entry_foreach (dev_list_entry, devices) { const char *sysfs_path; const char *dev_path; const char *str; @@ -802,7 +802,7 @@ int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *s return get_device_string(dev, DEVICE_STRING_SERIAL, string, maxlen); } -int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *, int , wchar_t *, size_t ) +int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *, int , wchar_t *, size_t) { return -1; } diff --git a/plugins/hid/macx/hidapi.cpp b/plugins/hid/macx/hidapi.cpp index b3217e0639..e3419f0a9b 100644 --- a/plugins/hid/macx/hidapi.cpp +++ b/plugins/hid/macx/hidapi.cpp @@ -47,15 +47,15 @@ typedef struct pthread_barrier { static int pthread_barrier_init(pthread_barrier_t *barrier, const pthread_barrierattr_t *, unsigned int count) { - if(count == 0) { + if (count == 0) { errno = EINVAL; return -1; } - if(pthread_mutex_init(&barrier->mutex, 0) < 0) { + if (pthread_mutex_init(&barrier->mutex, 0) < 0) { return -1; } - if(pthread_cond_init(&barrier->cond, 0) < 0) { + if (pthread_cond_init(&barrier->cond, 0) < 0) { pthread_mutex_destroy(&barrier->mutex); return -1; } @@ -76,7 +76,7 @@ static int pthread_barrier_wait(pthread_barrier_t *barrier) { pthread_mutex_lock(&barrier->mutex); ++(barrier->count); - if(barrier->count >= barrier->trip_count) + if (barrier->count >= barrier->trip_count) { barrier->count = 0; pthread_cond_broadcast(&barrier->cond); @@ -282,8 +282,7 @@ static int get_string_property_utf8(IOHIDDeviceRef device, CFStringRef prop, cha range.location = 0; range.length = str_len; CFIndex used_buf_len; - CFIndex chars_copied; - chars_copied = CFStringGetBytes(str, + CFStringGetBytes(str, range, kCFStringEncodingUTF8, (char)'?', @@ -402,7 +401,7 @@ static void process_pending_events(void) { SInt32 res; do { res = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.001, FALSE); - } while(res != kCFRunLoopRunFinished && res != kCFRunLoopRunTimedOut); + } while (res != kCFRunLoopRunFinished && res != kCFRunLoopRunTimedOut); } struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, unsigned short product_id) @@ -448,7 +447,6 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, (product_id == 0x0 || product_id == dev_pid)) { struct hid_device_info *tmp; - size_t len; char cbuf[BUF_LEN]; /* VID/PID match. Create the record. */ @@ -467,7 +465,7 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, /* Fill out the record */ cur_dev->next = NULL; - len = make_path(dev, cbuf, sizeof(cbuf)); + make_path(dev, cbuf, sizeof(cbuf)); cur_dev->path = strdup(cbuf); /* Serial Number */ @@ -704,10 +702,9 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path) CFSetGetValues(device_set, (const void **) device_array); for (i = 0; i < num_devices; i++) { char cbuf[BUF_LEN]; - size_t len; IOHIDDeviceRef os_dev = device_array[i]; - len = make_path(os_dev, cbuf, sizeof(cbuf)); + make_path(os_dev, cbuf, sizeof(cbuf)); if (!strcmp(cbuf, path)) { /* Matched Paths. Open this Device. */ IOReturn ret = IOHIDDeviceOpen(os_dev, kIOHIDOptionsTypeSeizeDevice); @@ -725,7 +722,7 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path) /* Create the Run Loop Mode for this device. printing the reference seems to work. */ - sprintf(str, "HIDAPI_%p", os_dev); + snprintf(str, 32, "HIDAPI_%p", os_dev); dev->run_loop_mode = CFStringCreateWithCString(NULL, str, kCFStringEncodingASCII); @@ -1028,7 +1025,7 @@ int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *s return get_serial_number(dev->device_handle, string, maxlen); } -int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *, int , wchar_t *, size_t ) +int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *, int , wchar_t *, size_t) { /* TODO: */ diff --git a/plugins/hid/macx/hidosxjoystick.cpp b/plugins/hid/macx/hidosxjoystick.cpp index b565cdacee..e8f7e4d0cf 100644 --- a/plugins/hid/macx/hidosxjoystick.cpp +++ b/plugins/hid/macx/hidosxjoystick.cpp @@ -85,10 +85,10 @@ void HIDOSXJoystick::init() return; // to return all elements for a device - CFArrayRef elementsArray = IOHIDDeviceCopyMatchingElements( m_IOKitDevice, NULL, kIOHIDOptionsTypeNone ); + CFArrayRef elementsArray = IOHIDDeviceCopyMatchingElements(m_IOKitDevice, NULL, kIOHIDOptionsTypeNone); if (elementsArray) { - CFIndex count = CFArrayGetCount( elementsArray ); + CFIndex count = CFArrayGetCount(elementsArray); //qDebug() << "Device" << QString::fromWCharArray(m_dev_info->product_string) << "has elements:" << count; @@ -99,7 +99,7 @@ void HIDOSXJoystick::init() unsigned int usage = IOHIDElementGetUsage(elem); unsigned int usagePage = IOHIDElementGetUsagePage(elem); - if(elemType == kIOHIDElementTypeInput_Misc || + if (elemType == kIOHIDElementTypeInput_Misc || elemType == kIOHIDElementTypeInput_Button || elemType == kIOHIDElementTypeInput_Axis || elemType == kIOHIDElementTypeInput_ScanCodes) diff --git a/plugins/hid/win32/hidwindowsjoystick.cpp b/plugins/hid/win32/hidwindowsjoystick.cpp index 76f00061f1..d0f7efe18d 100644 --- a/plugins/hid/win32/hidwindowsjoystick.cpp +++ b/plugins/hid/win32/hidwindowsjoystick.cpp @@ -34,12 +34,12 @@ bool HIDWindowsJoystick::isJoystick(unsigned short vid, unsigned short pid) for (UINT i = 0; i < joyGetNumDevs(); i++) { - memset( &caps, 0, sizeof( JOYCAPS ) ); + memset(&caps, 0, sizeof(JOYCAPS)); - MMRESULT error = joyGetDevCapsW( i, &caps, sizeof(JOYCAPS)); + MMRESULT error = joyGetDevCapsW(i, &caps, sizeof(JOYCAPS)); if (error == JOYERR_NOERROR && vid == caps.wMid && pid == caps.wPid) { - if( joyGetPos(i, & joyInfo) == JOYERR_NOERROR ) + if (joyGetPos(i, & joyInfo) == JOYERR_NOERROR) return true; } } @@ -49,7 +49,7 @@ bool HIDWindowsJoystick::isJoystick(unsigned short vid, unsigned short pid) void HIDWindowsJoystick::init() { m_info.dwFlags = JOY_RETURNALL; - m_info.dwSize = sizeof( m_info ); + m_info.dwSize = sizeof(m_info); QString devPath = path(); bool ok; @@ -58,16 +58,16 @@ void HIDWindowsJoystick::init() for (UINT i = 0; i < joyGetNumDevs(); i++) { - memset( &m_caps, 0, sizeof( m_caps ) ); + memset(&m_caps, 0, sizeof(m_caps)); - MMRESULT error = joyGetDevCapsW( i, &m_caps, sizeof(JOYCAPS)); + MMRESULT error = joyGetDevCapsW(i, &m_caps, sizeof(JOYCAPS)); if (error == JOYERR_NOERROR && VID == m_caps.wMid && PID == m_caps.wPid) { /* Windows joystick drivers may provide any combination of * X,Y,Z,R,U,V,POV - not necessarily the first n of these. */ - if( m_caps.wCaps & JOYCAPS_HASV ) + if (m_caps.wCaps & JOYCAPS_HASV) { m_axesNumber = 6; //joy->min[ 7 ] = -1.0; joy->max[ 7 ] = 1.0; /* POV Y */ @@ -92,12 +92,12 @@ void HIDWindowsJoystick::init() bool HIDWindowsJoystick::readEvent() { - MMRESULT status = joyGetPosEx( m_windId, &m_info ); + MMRESULT status = joyGetPosEx(m_windId, &m_info); - if ( status != JOYERR_NOERROR ) + if (status != JOYERR_NOERROR) return false; - if ( m_buttonsNumber ) + if (m_buttonsNumber) { for (int i = 0; i < m_buttonsNumber; ++i) { @@ -113,7 +113,7 @@ bool HIDWindowsJoystick::readEvent() m_buttonsMask = m_info.dwButtons; } - if ( m_axesNumber ) + if (m_axesNumber) { QList cmpVals; cmpVals.append(m_info.dwXpos); @@ -134,4 +134,3 @@ bool HIDWindowsJoystick::readEvent() } return true; } - diff --git a/plugins/interfaces/qlcioplugin.cpp b/plugins/interfaces/qlcioplugin.cpp index bb0b697a64..2faabdd58f 100644 --- a/plugins/interfaces/qlcioplugin.cpp +++ b/plugins/interfaces/qlcioplugin.cpp @@ -49,11 +49,12 @@ QString QLCIOPlugin::outputInfo(quint32 output) return QString(); } -void QLCIOPlugin::writeUniverse(quint32 universe, quint32 output, const QByteArray &data) +void QLCIOPlugin::writeUniverse(quint32 universe, quint32 output, const QByteArray &data, bool dataChanged) { Q_UNUSED(universe) Q_UNUSED(output) Q_UNUSED(data) + Q_UNUSED(dataChanged) } /************************************************************************* @@ -85,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) } /************************************************************************* @@ -171,7 +172,7 @@ void QLCIOPlugin::addToMap(quint32 universe, quint32 line, { desc.inputLine = line; } - else if(type == Output) + else if (type == Output) { desc.outputLine = line; } @@ -190,7 +191,7 @@ void QLCIOPlugin::removeFromMap(quint32 universe, quint32 line, QLCIOPlugin::Cap m_universesMap[universe].inputParameters.clear(); return; } - else if(type == Output && m_universesMap[universe].outputLine == line) + else if (type == Output && m_universesMap[universe].outputLine == line) { m_universesMap[universe].outputLine = UINT_MAX; m_universesMap[universe].outputParameters.clear(); diff --git a/plugins/interfaces/qlcioplugin.h b/plugins/interfaces/qlcioplugin.h index 8a537c8c59..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 }; /** @@ -202,7 +203,7 @@ class QLCIOPlugin : public QObject * @param output The output universe to write to * @param universe The universe data to write */ - virtual void writeUniverse(quint32 universe, quint32 output, const QByteArray& data); + virtual void writeUniverse(quint32 universe, quint32 output, const QByteArray& data, bool dataChanged); /************************************************************************* * Inputs @@ -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/interfaces/rdmprotocol.cpp b/plugins/interfaces/rdmprotocol.cpp index d08a141e47..e55d1c1539 100644 --- a/plugins/interfaces/rdmprotocol.cpp +++ b/plugins/interfaces/rdmprotocol.cpp @@ -23,7 +23,7 @@ RDMProtocol::RDMProtocol() : m_estaID(QLCPLUS_ESTA_ID) - , m_deviceID(0x01090709) + , m_deviceID(QLCPLUS_DEVICE_ID) , m_transactionNum(0x01) { } @@ -109,19 +109,22 @@ bool RDMProtocol::packetizeCommand(ushort command, QVariantList params, bool sta quint16 pid = params.at(1).toUInt(); buffer.append(shortToByteArray(pid)); - if (params.length() > 2) + if (params.length() > 3) { - switch(pid) + uchar size = params.at(2).toUInt(); + buffer.append(size); // add PDL + switch (size) { - case PID_DMX_PERSONALITY_DESCRIPTION: - default: - buffer.append(char(1)); // append PDL - buffer.append(char(params.at(2).toUInt())); + case 1: + buffer.append(uchar(params.at(3).toUInt())); + break; + case 2: + buffer.append(shortToByteArray(params.at(3).toUInt())); + break; + case 4: + buffer.append(longToByteArray(params.at(3).toUInt())); break; - case PID_PARAMETER_DESCRIPTION: - case PID_SLOT_DESCRIPTION: - buffer.append(char(2)); // append PDL - buffer.append(shortToByteArray(params.at(2).toUInt())); + default: break; } } @@ -148,6 +151,14 @@ bool RDMProtocol::packetizeCommand(ushort command, QVariantList params, bool sta { int size = params.at(i).toInt(); + // special case for byte arrays + if (size == 99) + { + QByteArray ba = params.at(i + 1).toByteArray(); + buffer.append(ba); + break; + } + switch (size) { case 1: @@ -159,11 +170,6 @@ bool RDMProtocol::packetizeCommand(ushort command, QVariantList params, bool sta case 4: buffer.append(longToByteArray(params.at(i + 1).toUInt())); break; - case 99: - { - QByteArray ba = params.at(i + 1).toByteArray(); - buffer.append(ba); - } default: break; } @@ -285,6 +291,10 @@ bool RDMProtocol::parsePacket(const QByteArray &buffer, QVariantMap &values) QString sourceUID = byteArrayToUID(buffer.mid(i, 6), ESTAId, deviceId); i += 6; + // check if we are reading our own request + if (ESTAId == m_estaID && deviceId == m_deviceID) + return false; + values.insert("UID_INFO", sourceUID); // transaction number @@ -339,12 +349,17 @@ bool RDMProtocol::parsePacket(const QByteArray &buffer, QVariantMap &values) case PID_SUPPORTED_PARAMETERS: { QVector pidList; - +#ifdef DEBUG_RDM + QDebug out = qDebug(); + out.nospace().noquote() << "Supported PIDs list: "; +#endif for (int n = 0; n < PDL; n += 2) { quint16 pid = byteArrayToShort(buffer, i + n); pidList.append(pid); - qDebug().nospace().noquote() << "Supported PID: 0x" << QString::number(pid, 16); +#ifdef DEBUG_RDM + out << "0x" << QString::number(pid, 16) << ", "; +#endif } values.insert("PID_LIST", QVariant::fromValue(pidList)); } diff --git a/plugins/interfaces/rdmprotocol.h b/plugins/interfaces/rdmprotocol.h index 46378a9773..d0c8d66bef 100644 --- a/plugins/interfaces/rdmprotocol.h +++ b/plugins/interfaces/rdmprotocol.h @@ -20,7 +20,7 @@ #include #include -#define DEBUG_RDM +//#define DEBUG_RDM #define RDM_START_CODE 0xCC #define RDM_SC_SUB_MESSAGE 0x01 @@ -146,6 +146,7 @@ #define PID_POWER_ON_SELF_TEST 0x1044 #define QLCPLUS_ESTA_ID 0x7FF8 +#define QLCPLUS_DEVICE_ID 0x01090709 #define BROADCAST_ESTA_ID 0xFFFF #define BROADCAST_DEVICE_ID 0xFFFFFFFF @@ -180,6 +181,12 @@ class RDMProtocol /** Return a PID as a string */ static QString pidToString(quint16 pid); + /** Return the RDM command reply as a string */ + static QString responseToString(quint8 response); + + /** Return the device info category as string */ + static QString categoryToString(quint16 category); + private: QByteArray UIDToByteArray(quint16 ESTAId, quint32 deviceId); QByteArray shortToByteArray(quint16 data); @@ -187,8 +194,6 @@ class RDMProtocol quint16 byteArrayToShort(const QByteArray &buffer, int index); quint32 byteArrayToLong(const QByteArray &buffer, int index); quint16 calculateChecksum(bool startCode, const QByteArray &ba, int len); - QString responseToString(quint8 response); - QString categoryToString(quint16 category); protected: quint16 m_estaID; diff --git a/plugins/loopback/CMakeLists.txt b/plugins/loopback/CMakeLists.txt new file mode 100644 index 0000000000..a64b5b873f --- /dev/null +++ b/plugins/loopback/CMakeLists.txt @@ -0,0 +1,3 @@ +project(loopback) + +add_subdirectory(src) diff --git a/plugins/loopback/src/CMakeLists.txt b/plugins/loopback/src/CMakeLists.txt new file mode 100644 index 0000000000..ef5c1c4fd5 --- /dev/null +++ b/plugins/loopback/src/CMakeLists.txt @@ -0,0 +1,54 @@ +set(module_name "loopback") + +set(TS_FILES + loopback_fi_FI.ts + loopback_de_DE.ts + loopback_es_ES.ts + loopback_fr_FR.ts + loopback_it_IT.ts + loopback_nl_NL.ts + loopback_cz_CZ.ts + loopback_pt_BR.ts + loopback_ca_ES.ts + loopback_ja_JP.ts +) + +if(QT_VERSION_MAJOR GREATER 5) + qt_add_translation(QM_FILES ${TS_FILES}) +else() + qt5_add_translation(QM_FILES ${TS_FILES}) +endif() + +add_library(${module_name} + SHARED + ${QM_FILES} +) + +target_sources(${module_name} PRIVATE + ../../interfaces/qlcioplugin.cpp ../../interfaces/qlcioplugin.h + ${module_name}.cpp ${module_name}.h +) +target_include_directories(${module_name} PRIVATE + ../../interfaces +) + +target_link_libraries(${module_name} PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui +) + +if(WIN32) + target_compile_definitions(${module_name} PRIVATE + QLC_EXPORT + ) +endif() + +install(TARGETS ${module_name} + LIBRARY DESTINATION ${INSTALLROOT}/${PLUGINDIR} + RUNTIME DESTINATION ${INSTALLROOT}/${PLUGINDIR} +) + +if (UNIX AND NOT APPLE) + install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/org.qlcplus.QLCPlus.loopback.metainfo.xml" + DESTINATION ${METAINFODIR}) +endif() diff --git a/plugins/loopback/src/loopback.cpp b/plugins/loopback/src/loopback.cpp index b6504b071f..47b417a337 100644 --- a/plugins/loopback/src/loopback.cpp +++ b/plugins/loopback/src/loopback.cpp @@ -130,7 +130,7 @@ QString Loopback::pluginInfo() str += QString("

"); str += QString("

%1

").arg(name()); - str += tr("This plugin provides DMX loopback. Data written to each output is forwarded to the respective input." ); + str += tr("This plugin provides DMX loopback. Data written to each output is forwarded to the respective input."); str += QString("

"); return str; @@ -180,9 +180,10 @@ QString Loopback::inputInfo(quint32 input) return str; } -void Loopback::writeUniverse(quint32 universe, quint32 output, const QByteArray &data) +void Loopback::writeUniverse(quint32 universe, quint32 output, const QByteArray &data, bool dataChanged) { - Q_UNUSED(universe); + Q_UNUSED(universe) + Q_UNUSED(dataChanged) if (!m_outputMap.contains(output)) return; @@ -205,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 f5005119e9..cc3979e875 100644 --- a/plugins/loopback/src/loopback.h +++ b/plugins/loopback/src/loopback.h @@ -67,7 +67,7 @@ class QLC_DECLSPEC Loopback : public QLCIOPlugin QString outputInfo(quint32 output); /** @reimp */ - void writeUniverse(quint32 universe, quint32 output, const QByteArray& data); + void writeUniverse(quint32 universe, quint32 output, const QByteArray& data, bool dataChanged); /************************************************************************* * Inputs @@ -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/loopback/src/loopback_ca_ES.ts b/plugins/loopback/src/loopback_ca_ES.ts index a0be2426ba..b0819570fb 100644 --- a/plugins/loopback/src/loopback_ca_ES.ts +++ b/plugins/loopback/src/loopback_ca_ES.ts @@ -3,12 +3,6 @@ Loopback - - - - Loopback - Loopback - This plugin provides DMX loopback. Data written to each output is forwarded to the respective input. diff --git a/plugins/loopback/src/loopback_cz_CZ.ts b/plugins/loopback/src/loopback_cz_CZ.ts index 47a6c21eac..1d3854febd 100644 --- a/plugins/loopback/src/loopback_cz_CZ.ts +++ b/plugins/loopback/src/loopback_cz_CZ.ts @@ -3,12 +3,6 @@ Loopback - - - - Loopback - Zpětná smyčka (loopback) - This plugin provides DMX loopback. Data written to each output is forwarded to the respective input. diff --git a/plugins/loopback/src/loopback_de_DE.ts b/plugins/loopback/src/loopback_de_DE.ts index 3fb9d05559..9e4bd64e10 100644 --- a/plugins/loopback/src/loopback_de_DE.ts +++ b/plugins/loopback/src/loopback_de_DE.ts @@ -3,12 +3,6 @@ Loopback - - - - Loopback - Rückschleife - This plugin provides DMX loopback. Data written to each output is forwarded to the respective input. diff --git a/plugins/loopback/src/loopback_es_ES.ts b/plugins/loopback/src/loopback_es_ES.ts index 1a0d53ca93..874198cf60 100644 --- a/plugins/loopback/src/loopback_es_ES.ts +++ b/plugins/loopback/src/loopback_es_ES.ts @@ -3,12 +3,6 @@ Loopback - - - - Loopback - Loopback - This plugin provides DMX loopback. Data written to each output is forwarded to the respective input. diff --git a/plugins/loopback/src/loopback_fi_FI.ts b/plugins/loopback/src/loopback_fi_FI.ts index 2b65ab1d6e..5d38ca0c0d 100644 --- a/plugins/loopback/src/loopback_fi_FI.ts +++ b/plugins/loopback/src/loopback_fi_FI.ts @@ -3,12 +3,6 @@ Loopback - - - - Loopback - - This plugin provides DMX loopback. Data written to each output is forwarded to the respective input. diff --git a/plugins/loopback/src/loopback_fr_FR.ts b/plugins/loopback/src/loopback_fr_FR.ts index ca1902fdda..1eebad73f0 100644 --- a/plugins/loopback/src/loopback_fr_FR.ts +++ b/plugins/loopback/src/loopback_fr_FR.ts @@ -3,12 +3,6 @@ Loopback - - - - Loopback - Bouclage - This plugin provides DMX loopback. Data written to each output is forwarded to the respective input. diff --git a/plugins/loopback/src/loopback_it_IT.ts b/plugins/loopback/src/loopback_it_IT.ts index a913ecfa7a..121bcc1303 100644 --- a/plugins/loopback/src/loopback_it_IT.ts +++ b/plugins/loopback/src/loopback_it_IT.ts @@ -3,12 +3,6 @@ Loopback - - - - Loopback - Loopback - This plugin provides DMX loopback. Data written to each output is forwarded to the respective input. diff --git a/plugins/loopback/src/loopback_ja_JP.ts b/plugins/loopback/src/loopback_ja_JP.ts index cbb67ade32..914d5d708a 100644 --- a/plugins/loopback/src/loopback_ja_JP.ts +++ b/plugins/loopback/src/loopback_ja_JP.ts @@ -3,12 +3,6 @@ Loopback - - - - Loopback - - This plugin provides DMX loopback. Data written to each output is forwarded to the respective input. diff --git a/plugins/loopback/src/loopback_nl_NL.ts b/plugins/loopback/src/loopback_nl_NL.ts index a0ce65afcb..c65e1848e3 100644 --- a/plugins/loopback/src/loopback_nl_NL.ts +++ b/plugins/loopback/src/loopback_nl_NL.ts @@ -3,12 +3,6 @@ Loopback - - - - Loopback - Loopback - This plugin provides DMX loopback. Data written to each output is forwarded to the respective input. diff --git a/plugins/loopback/src/loopback_pt_BR.ts b/plugins/loopback/src/loopback_pt_BR.ts index 8be8704737..9d7cfe91df 100644 --- a/plugins/loopback/src/loopback_pt_BR.ts +++ b/plugins/loopback/src/loopback_pt_BR.ts @@ -3,12 +3,6 @@ Loopback - - - - Loopback - - This plugin provides DMX loopback. Data written to each output is forwarded to the respective input. diff --git a/plugins/midi/CMakeLists.txt b/plugins/midi/CMakeLists.txt new file mode 100644 index 0000000000..991d71213b --- /dev/null +++ b/plugins/midi/CMakeLists.txt @@ -0,0 +1,6 @@ +project(midi) + +add_subdirectory(src) +if(NOT ANDROID AND NOT IOS) + add_subdirectory(test) +endif() diff --git a/plugins/midi/src/CMakeLists.txt b/plugins/midi/src/CMakeLists.txt new file mode 100644 index 0000000000..ed05a8461a --- /dev/null +++ b/plugins/midi/src/CMakeLists.txt @@ -0,0 +1,11 @@ +project(src) + +if(APPLE) + add_subdirectory(macx) +endif() +if(UNIX AND NOT APPLE) + add_subdirectory(alsa) +endif() +if(WIN32) + add_subdirectory(win32) +endif() diff --git a/plugins/midi/src/MIDI_ca_ES.ts b/plugins/midi/src/MIDI_ca_ES.ts index af3bef14e7..37b5d55224 100644 --- a/plugins/midi/src/MIDI_ca_ES.ts +++ b/plugins/midi/src/MIDI_ca_ES.ts @@ -52,55 +52,55 @@ MidiPlugin - + This plugin provides input/output support for MIDI devices. Aquest plugin proveeix suport de entrada/sortida per dispositius MIDI. - + No output support available. Suport de sortida no disponible. - + Output Sortida - - + + Open Obrir - - + + Not Open No obert - - + + Status Estat - + Invalid Output Sortida invàlida - + No input support available. Suport d'entrada no disponible. - + Input Entrada - + Invalid Input Entrada Invàlida diff --git a/plugins/midi/src/MIDI_cz_CZ.ts b/plugins/midi/src/MIDI_cz_CZ.ts index 90a00b70bc..cf0351ce71 100644 --- a/plugins/midi/src/MIDI_cz_CZ.ts +++ b/plugins/midi/src/MIDI_cz_CZ.ts @@ -52,55 +52,55 @@ MidiPlugin - + This plugin provides input/output support for MIDI devices. Tento plugin přidává podporu vstupu/výstupu pro zařízení MIDI. - + No output support available. Podpora výstupu není k dispozici. - + Output Výstup - - + + Open Otevřený - - + + Not Open Neotevřený - - + + Status Stav - + Invalid Output Chybný výstup - + No input support available. Podpora vstupu není k dispozici. - + Input Vstup - + Invalid Input Chybný vstup diff --git a/plugins/midi/src/MIDI_de_DE.ts b/plugins/midi/src/MIDI_de_DE.ts index 961d0969a2..9008ac705c 100644 --- a/plugins/midi/src/MIDI_de_DE.ts +++ b/plugins/midi/src/MIDI_de_DE.ts @@ -52,55 +52,55 @@ MidiPlugin - + This plugin provides input/output support for MIDI devices. Dieses Plugin bietet Eingabe/Ausgabe Unterstützung für MIDI-Geräte. - + No output support available. Keine Ausgabe Unterstützung verfügbar. - + Output Ausgang - - + + Open Geöffnet - - + + Not Open Nicht geöffnet - - + + Status Status - + Invalid Output Falscher Ausgang - + No input support available. Keine Eingangs Untestützung verfügbar. - + Input Eingang - + Invalid Input Falscher Eingang diff --git a/plugins/midi/src/MIDI_es_ES.ts b/plugins/midi/src/MIDI_es_ES.ts index a537986399..7c00757183 100644 --- a/plugins/midi/src/MIDI_es_ES.ts +++ b/plugins/midi/src/MIDI_es_ES.ts @@ -52,55 +52,55 @@ MidiPlugin - + This plugin provides input/output support for MIDI devices. Este provee soporte de Entrada/Salida para dispositivos MIDI. - + No output support available. Soporte de Salida no disponible. - + Output Salida - - + + Open Abierto - - + + Not Open No Abierto - - + + Status Estado - + Invalid Output Salida inválida - + No input support available. Soporte de Entrada no disponible. - + Input Entrada - + Invalid Input Entrada inválida diff --git a/plugins/midi/src/MIDI_fi_FI.ts b/plugins/midi/src/MIDI_fi_FI.ts index beb2741267..0476c67e7a 100644 --- a/plugins/midi/src/MIDI_fi_FI.ts +++ b/plugins/midi/src/MIDI_fi_FI.ts @@ -52,55 +52,55 @@ MidiPlugin - + This plugin provides input/output support for MIDI devices. - + No output support available. - + Output - - + + Open - - + + Not Open - - + + Status - + Invalid Output - + No input support available. - + Input - + Invalid Input diff --git a/plugins/midi/src/MIDI_fr_FR.ts b/plugins/midi/src/MIDI_fr_FR.ts index a5422bf872..40c1aa2ba0 100644 --- a/plugins/midi/src/MIDI_fr_FR.ts +++ b/plugins/midi/src/MIDI_fr_FR.ts @@ -52,55 +52,55 @@ MidiPlugin - + This plugin provides input/output support for MIDI devices. Ce plugin offre le support des périphériques MIDI. - + No output support available. Support de la sortie indisponible. - + Output Sortie - - + + Open Ouvert - - + + Not Open Fermé - - + + Status État - + Invalid Output Sortie invalide - + No input support available. Support de l'entrée indisponible. - + Input Entrée - + Invalid Input Entrée invalide diff --git a/plugins/midi/src/MIDI_it_IT.ts b/plugins/midi/src/MIDI_it_IT.ts index 7bba230391..9dec810a55 100644 --- a/plugins/midi/src/MIDI_it_IT.ts +++ b/plugins/midi/src/MIDI_it_IT.ts @@ -52,55 +52,55 @@ MidiPlugin - + This plugin provides input/output support for MIDI devices. Questa plugin fornisce supporto di ingresso/uscita per dispositivi MIDI. - + No output support available. Nessuna uscita disponibile. - + Output Uscita - - + + Open Aperto - - + + Not Open Chiuso - - + + Status Stato - + Invalid Output Uscita non valida - + No input support available. Nessun ingresso disponibile. - + Input Ingresso - + Invalid Input Ingresso non valido diff --git a/plugins/midi/src/MIDI_ja_JP.ts b/plugins/midi/src/MIDI_ja_JP.ts index e384e97150..4bea3ff8e0 100644 --- a/plugins/midi/src/MIDI_ja_JP.ts +++ b/plugins/midi/src/MIDI_ja_JP.ts @@ -52,55 +52,55 @@ MidiPlugin - + This plugin provides input/output support for MIDI devices. このプラグインは、MIDIデバイスとの間でMIDI信号の送受信を行います。 - + No output support available. No output support available. - + Output 出力 - - + + Open Open - - + + Not Open Not Open - - + + Status 状態 - + Invalid Output 無効な出力 - + No input support available. No input support available. - + Input 入力 - + Invalid Input 無効な入力 diff --git a/plugins/midi/src/MIDI_nl_NL.ts b/plugins/midi/src/MIDI_nl_NL.ts index 3f3e346365..a328ffe3a2 100644 --- a/plugins/midi/src/MIDI_nl_NL.ts +++ b/plugins/midi/src/MIDI_nl_NL.ts @@ -52,55 +52,55 @@ MidiPlugin - + This plugin provides input/output support for MIDI devices. Deze plugin verzorgt input/output voor MIDI apparaten. - + No output support available. Output niet ondersteund. - + Output Output - - + + Open Open - - + + Not Open Ongeopend - - + + Status Status - + Invalid Output Ongeldige output - + No input support available. Input niet ondersteund. - + Input Input - + Invalid Input Input niet ondersteund diff --git a/plugins/midi/src/MIDI_pt_BR.ts b/plugins/midi/src/MIDI_pt_BR.ts index 2a3f07a3e6..aba4afd914 100644 --- a/plugins/midi/src/MIDI_pt_BR.ts +++ b/plugins/midi/src/MIDI_pt_BR.ts @@ -52,55 +52,55 @@ MidiPlugin - + This plugin provides input/output support for MIDI devices. Este plugin fornece suporte de Entrada/Saída para dispositivos MIDI. - + No output support available. Suporte de saída não disponível - + Output Saída - - + + Open Aberto - - + + Not Open Não aberto - - + + Status Estado - + Invalid Output Saída inválida - + No input support available. Suporte de entrada não disponível - + Input Entrada - + Invalid Input Entrada inválida diff --git a/plugins/midi/src/alsa/CMakeLists.txt b/plugins/midi/src/alsa/CMakeLists.txt new file mode 100644 index 0000000000..e29250931b --- /dev/null +++ b/plugins/midi/src/alsa/CMakeLists.txt @@ -0,0 +1,68 @@ + +set(module_name "midiplugin") + +set(TS_FILES + ../MIDI_de_DE.ts + ../MIDI_es_ES.ts + ../MIDI_fi_FI.ts + ../MIDI_fr_FR.ts + ../MIDI_it_IT.ts + ../MIDI_nl_NL.ts + ../MIDI_cz_CZ.ts + ../MIDI_pt_BR.ts + ../MIDI_ca_ES.ts + ../MIDI_ja_JP.ts +) + +if(QT_VERSION_MAJOR GREATER 5) + qt_add_translation(QM_FILES ${TS_FILES}) +else() + qt5_add_translation(QM_FILES ${TS_FILES}) +endif() + +add_library(${module_name} SHARED + ${QM_FILES} +) + +target_sources(${module_name} PRIVATE + ../../../../engine/src/qlcfile.cpp ../../../../engine/src/qlcfile.h + ../../../interfaces/qlcioplugin.cpp ../../../interfaces/qlcioplugin.h + ../common/configuremidiplugin.cpp ../common/configuremidiplugin.h ../common/configuremidiplugin.ui + ../common/mididevice.cpp ../common/mididevice.h + ../common/midienumerator.h + ../common/midiinputdevice.cpp ../common/midiinputdevice.h + ../common/midioutputdevice.cpp ../common/midioutputdevice.h + ../common/${module_name}.cpp ../common/${module_name}.h + ../common/midiprotocol.cpp ../common/midiprotocol.h + ../common/miditemplate.cpp ../common/miditemplate.h + alsamidienumerator.cpp alsamidienumerator.h + alsamidiinputdevice.cpp alsamidiinputdevice.h + alsamidiinputthread.cpp alsamidiinputthread.h + alsamidioutputdevice.cpp alsamidioutputdevice.h + alsamidiutil.cpp alsamidiutil.h +) + +target_include_directories(${module_name} PRIVATE + ../../../../engine/src + ../../../interfaces + ../common +) + +pkg_check_modules(ALSA IMPORTED_TARGET alsa) + +target_link_libraries(${module_name} PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Widgets +) + +if (ALSA_FOUND) + target_link_libraries(${module_name} PRIVATE + ${ALSA_LIBRARIES} + ) +endif() + +install(TARGETS ${module_name} + LIBRARY DESTINATION ${INSTALLROOT}/${PLUGINDIR} + RUNTIME DESTINATION ${INSTALLROOT}/${PLUGINDIR} +) diff --git a/plugins/midi/src/alsa/alsamidiinputthread.cpp b/plugins/midi/src/alsa/alsamidiinputthread.cpp index 4ecbe7b82c..c3ed98a1f9 100644 --- a/plugins/midi/src/alsa/alsamidiinputthread.cpp +++ b/plugins/midi/src/alsa/alsamidiinputthread.cpp @@ -273,11 +273,11 @@ void AlsaMidiInputThread::readEvent() continue; if (ev->type == SND_SEQ_EVENT_START) cmd = MIDI_BEAT_START; - else if(ev->type == SND_SEQ_EVENT_STOP) + else if (ev->type == SND_SEQ_EVENT_STOP) cmd = MIDI_BEAT_STOP; - else if(ev->type == SND_SEQ_EVENT_CONTINUE) + else if (ev->type == SND_SEQ_EVENT_CONTINUE) cmd = MIDI_BEAT_CONTINUE; - else if(ev->type == SND_SEQ_EVENT_CLOCK) + else if (ev->type == SND_SEQ_EVENT_CLOCK) cmd = MIDI_BEAT_CLOCK; qDebug() << "MIDI clock: " << cmd; diff --git a/plugins/midi/src/alsa/alsamidioutputdevice.cpp b/plugins/midi/src/alsa/alsamidioutputdevice.cpp index bef4d0a6d2..9f5f368a28 100644 --- a/plugins/midi/src/alsa/alsamidioutputdevice.cpp +++ b/plugins/midi/src/alsa/alsamidioutputdevice.cpp @@ -244,7 +244,7 @@ void AlsaMidiOutputDevice::writeFeedback(uchar cmd, uchar data1, uchar data2) void AlsaMidiOutputDevice::writeSysEx(QByteArray message) { - if(message.isEmpty()) + if (message.isEmpty()) return; if (isOpen() == false) @@ -256,7 +256,7 @@ void AlsaMidiOutputDevice::writeSysEx(QByteArray message) //snd_seq_ev_set_subs(&ev); snd_seq_ev_set_direct(&ev); - snd_seq_ev_set_sysex (&ev, message.count(), message.data()); + snd_seq_ev_set_sysex (&ev, message.length(), message.data()); if (snd_seq_event_output(m_alsa, &ev) < 0) qDebug() << "snd_seq_event_output ERROR"; diff --git a/plugins/midi/src/common/configuremidiplugin.cpp b/plugins/midi/src/common/configuremidiplugin.cpp index 634cda176a..a47f801774 100644 --- a/plugins/midi/src/common/configuremidiplugin.cpp +++ b/plugins/midi/src/common/configuremidiplugin.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include "configuremidiplugin.h" #include "midioutputdevice.h" @@ -37,6 +38,8 @@ #define COL_MODE 2 #define COL_INITMESSAGE 3 +#define SETTINGS_GEOMETRY "configuremidiplugin/geometry" + ConfigureMidiPlugin::ConfigureMidiPlugin(MidiPlugin* plugin, QWidget* parent) : QDialog(parent) , m_plugin(plugin) @@ -44,12 +47,19 @@ ConfigureMidiPlugin::ConfigureMidiPlugin(MidiPlugin* plugin, QWidget* parent) Q_ASSERT(plugin != NULL); setupUi(this); + QSettings settings; + QVariant geometrySettings = settings.value(SETTINGS_GEOMETRY); + if (geometrySettings.isValid() == true) + restoreGeometry(geometrySettings.toByteArray()); + connect(plugin, SIGNAL(configurationChanged()), this, SLOT(slotUpdateTree())); slotUpdateTree(); } ConfigureMidiPlugin::~ConfigureMidiPlugin() { + QSettings settings; + settings.setValue(SETTINGS_GEOMETRY, saveGeometry()); } void ConfigureMidiPlugin::slotRefresh() diff --git a/plugins/midi/src/common/midiplugin.cpp b/plugins/midi/src/common/midiplugin.cpp index 5326c2bd84..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; } /***************************************************************************** @@ -178,12 +178,12 @@ QString MidiPlugin::outputInfo(quint32 output) return str; } -void MidiPlugin::writeUniverse(quint32 universe, quint32 output, const QByteArray &data) +void MidiPlugin::writeUniverse(quint32 universe, quint32 output, const QByteArray &data, bool dataChanged) { Q_UNUSED(universe) MidiOutputDevice* dev = outputDevice(output); - if (dev != NULL) + if (dev != NULL && dataChanged) dev->writeUniverse(data); } @@ -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; } } @@ -348,7 +353,7 @@ void MidiPlugin::configure() // walk the universe map to update/add the // plugin custom parameters - foreach(quint32 universe, m_universesMap.keys()) + foreach (quint32 universe, m_universesMap.keys()) { m_universesMap[universe].inputParameters.clear(); diff --git a/plugins/midi/src/common/midiplugin.h b/plugins/midi/src/common/midiplugin.h index 930e0d6b5e..b9764d53c8 100644 --- a/plugins/midi/src/common/midiplugin.h +++ b/plugins/midi/src/common/midiplugin.h @@ -87,7 +87,7 @@ class MidiPlugin : public QLCIOPlugin QString outputInfo(quint32 output); /** @reimp */ - void writeUniverse(quint32 universe, quint32 output, const QByteArray& data); + void writeUniverse(quint32 universe, quint32 output, const QByteArray& data, bool dataChanged); private: /** Get an output device by its output index */ @@ -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/midi/src/common/miditemplate.cpp b/plugins/midi/src/common/miditemplate.cpp index cf0a518192..78db1475d1 100644 --- a/plugins/midi/src/common/miditemplate.cpp +++ b/plugins/midi/src/common/miditemplate.cpp @@ -143,7 +143,7 @@ bool MidiTemplate::loadXML(QXmlStreamReader& doc) initMessage.append((char)byte); } setInitMessage(initMessage); - qDebug() << Q_FUNC_INFO << "Loaded message with size:" << initMessage.count(); + qDebug() << Q_FUNC_INFO << "Loaded message with size:" << initMessage.length(); } } diff --git a/plugins/midi/src/macx/CMakeLists.txt b/plugins/midi/src/macx/CMakeLists.txt new file mode 100644 index 0000000000..5067c06e76 --- /dev/null +++ b/plugins/midi/src/macx/CMakeLists.txt @@ -0,0 +1,60 @@ + +set(module_name "midiplugin") + +set(TS_FILES + ../MIDI_de_DE.ts + ../MIDI_es_ES.ts + ../MIDI_fi_FI.ts + ../MIDI_fr_FR.ts + ../MIDI_it_IT.ts + ../MIDI_nl_NL.ts + ../MIDI_cz_CZ.ts + ../MIDI_pt_BR.ts + ../MIDI_ca_ES.ts + ../MIDI_ja_JP.ts +) + +if(QT_VERSION_MAJOR GREATER 5) + qt_add_translation(QM_FILES ${TS_FILES}) +else() + qt5_add_translation(QM_FILES ${TS_FILES}) +endif() + +add_library(${module_name} SHARED + ${QM_FILES} +) + +target_sources(${module_name} PRIVATE + ../../../../engine/src/qlcfile.cpp ../../../../engine/src/qlcfile.h + ../../../interfaces/qlcioplugin.cpp ../../../interfaces/qlcioplugin.h + ../common/configuremidiplugin.cpp ../common/configuremidiplugin.h ../common/configuremidiplugin.ui + ../common/mididevice.cpp ../common/mididevice.h + ../common/midienumerator.h + ../common/midiinputdevice.cpp ../common/midiinputdevice.h + ../common/midioutputdevice.cpp ../common/midioutputdevice.h + ../common/${module_name}.cpp ../common/${module_name}.h + ../common/midiprotocol.cpp ../common/midiprotocol.h + ../common/miditemplate.cpp ../common/miditemplate.h + coremidienumerator.cpp + coremidienumeratorprivate.h + coremidiinputdevice.cpp coremidiinputdevice.h + coremidioutputdevice.cpp coremidioutputdevice.h +) +target_include_directories(${module_name} PRIVATE + ../../../../engine/src + ../../../interfaces + ../common ../common +) + +target_link_libraries(${module_name} PRIVATE + "-framework CoreFoundation" + "-framework CoreMIDI" + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Widgets +) + +install(TARGETS ${module_name} + LIBRARY DESTINATION ${INSTALLROOT}/${PLUGINDIR} + RUNTIME DESTINATION ${INSTALLROOT}/${PLUGINDIR} +) diff --git a/plugins/midi/src/macx/coremidiinputdevice.cpp b/plugins/midi/src/macx/coremidiinputdevice.cpp index dc96c4b978..0fa77d2eb7 100644 --- a/plugins/midi/src/macx/coremidiinputdevice.cpp +++ b/plugins/midi/src/macx/coremidiinputdevice.cpp @@ -49,7 +49,7 @@ static void MidiInProc(const MIDIPacketList* pktList, void* readProcRefCon, uchar value = 0; // MIDI Command - cmd = packet->data[0]; + cmd = packet->data[i]; if (!MIDI_IS_CMD(cmd)) continue; // Not a MIDI command. Skip to the next byte. if (cmd == MIDI_SYSEX) diff --git a/plugins/midi/src/macx/coremidioutputdevice.cpp b/plugins/midi/src/macx/coremidioutputdevice.cpp index c1743855e4..bd468e66be 100644 --- a/plugins/midi/src/macx/coremidioutputdevice.cpp +++ b/plugins/midi/src/macx/coremidioutputdevice.cpp @@ -201,7 +201,7 @@ void CoreMidiOutputDevice::writeFeedback(uchar cmd, uchar data1, uchar data2) void CoreMidiOutputDevice::writeSysEx(QByteArray message) { - if(message.isEmpty()) + if (message.isEmpty()) return; if (isOpen() == false) diff --git a/plugins/midi/src/win32/CMakeLists.txt b/plugins/midi/src/win32/CMakeLists.txt new file mode 100644 index 0000000000..d73b832a29 --- /dev/null +++ b/plugins/midi/src/win32/CMakeLists.txt @@ -0,0 +1,59 @@ +set(module_name "midiplugin") + +set(TS_FILES + ../MIDI_de_DE.ts + ../MIDI_es_ES.ts + ../MIDI_fi_FI.ts + ../MIDI_fr_FR.ts + ../MIDI_it_IT.ts + ../MIDI_nl_NL.ts + ../MIDI_cz_CZ.ts + ../MIDI_pt_BR.ts + ../MIDI_ca_ES.ts + ../MIDI_ja_JP.ts +) + +if(QT_VERSION_MAJOR GREATER 5) + qt_add_translation(QM_FILES ${TS_FILES}) +else() + qt5_add_translation(QM_FILES ${TS_FILES}) +endif() + +add_library(${module_name} + SHARED + ${QM_FILES} +) + +target_sources(${module_name} PRIVATE + ../../../../engine/src/qlcfile.cpp ../../../../engine/src/qlcfile.h + ../../../interfaces/qlcioplugin.cpp ../../../interfaces/qlcioplugin.h + ../common/configuremidiplugin.cpp ../common/configuremidiplugin.h ../common/configuremidiplugin.ui + ../common/mididevice.cpp ../common/mididevice.h + ../common/midienumerator.h + ../common/midiinputdevice.cpp ../common/midiinputdevice.h + ../common/midioutputdevice.cpp ../common/midioutputdevice.h + ../common/${module_name}.cpp ../common/${module_name}.h + ../common/midiprotocol.cpp ../common/midiprotocol.h + ../common/miditemplate.cpp ../common/miditemplate.h + win32midienumerator.cpp + win32midienumeratorprivate.h + win32midiinputdevice.cpp win32midiinputdevice.h + win32midioutputdevice.cpp win32midioutputdevice.h +) +target_include_directories(${module_name} PRIVATE + ../../../../engine/src + ../../../interfaces + ../common ../common +) + +target_link_libraries(${module_name} PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Widgets + winmm +) + +install(TARGETS ${module_name} + LIBRARY DESTINATION ${INSTALLROOT}/${PLUGINDIR} + RUNTIME DESTINATION ${INSTALLROOT}/${PLUGINDIR} +) diff --git a/plugins/midi/src/win32/win32midienumerator.cpp b/plugins/midi/src/win32/win32midienumerator.cpp index 9c6d7ffee3..1aae6b725e 100644 --- a/plugins/midi/src/win32/win32midienumerator.cpp +++ b/plugins/midi/src/win32/win32midienumerator.cpp @@ -62,7 +62,7 @@ QString MidiEnumeratorPrivate::extractInputName(UINT id) if (result == MMSYSERR_NOERROR) { #ifdef UNICODE - return QString::fromUtf16((ushort*) caps.szPname); + return QString::fromUtf16(reinterpret_cast(caps.szPname)); #else return QString::fromLocal8Bit(caps.szPname); #endif @@ -80,7 +80,7 @@ QString MidiEnumeratorPrivate::extractOutputName(UINT id) if (result == MMSYSERR_NOERROR) { #ifdef UNICODE - return QString::fromUtf16((ushort*) caps.szPname); + return QString::fromUtf16(reinterpret_cast(caps.szPname)); #else return QString::fromLocal8Bit(caps.szPname); #endif diff --git a/plugins/midi/src/win32/win32midioutputdevice.cpp b/plugins/midi/src/win32/win32midioutputdevice.cpp index ff775fd842..167216bee1 100644 --- a/plugins/midi/src/win32/win32midioutputdevice.cpp +++ b/plugins/midi/src/win32/win32midioutputdevice.cpp @@ -170,7 +170,7 @@ void Win32MidiOutputDevice::sendData(BYTE command, BYTE channel, BYTE value) void Win32MidiOutputDevice::writeSysEx(QByteArray message) { - if(message.isEmpty()) + if (message.isEmpty()) return; if (isOpen() == false) @@ -182,7 +182,7 @@ void Win32MidiOutputDevice::writeSysEx(QByteArray message) midiHdr.lpData = (LPSTR)message.data(); /* Store its size in the MIDIHDR */ - midiHdr.dwBufferLength = message.count(); + midiHdr.dwBufferLength = message.length(); /* Flags must be set to 0 */ midiHdr.dwFlags = 0; diff --git a/plugins/midi/test/CMakeLists.txt b/plugins/midi/test/CMakeLists.txt new file mode 100644 index 0000000000..adf0abb2e8 --- /dev/null +++ b/plugins/midi/test/CMakeLists.txt @@ -0,0 +1,19 @@ +add_executable(midi_test WIN32 MACOSX_BUNDLE + ../../interfaces/qlcioplugin.cpp ../../interfaces/qlcioplugin.h + ../src/common/midiprotocol.cpp ../src/common/midiprotocol.h + midi_test.cpp midi_test.h +) +target_include_directories(midi_test PRIVATE + ../../interfaces + ../src/common +) + +target_link_libraries(midi_test PRIVATE + # Remove: L../src + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Test +) + +# Consider using qt_generate_deploy_app_script() for app deployment if +# the project can use Qt 6.3. In that case rerun qmake2cmake with +# --min-qt-version=6.3. diff --git a/plugins/ola/CMakeLists.txt b/plugins/ola/CMakeLists.txt new file mode 100644 index 0000000000..e8f573d7e5 --- /dev/null +++ b/plugins/ola/CMakeLists.txt @@ -0,0 +1,91 @@ +set(module_name "olaio") + +set(TS_FILES + OLA_fi_FI.ts + OLA_de_DE.ts + OLA_es_ES.ts + OLA_fr_FR.ts + OLA_it_IT.ts + OLA_nl_NL.ts + OLA_cz_CZ.ts + OLA_pt_BR.ts + OLA_ca_ES.ts + OLA_ja_JP.ts +) + +if(QT_VERSION_MAJOR GREATER 5) + qt_add_translation(QM_FILES ${TS_FILES}) +else() + qt5_add_translation(QM_FILES ${TS_FILES}) +endif() + +add_library(${module_name} + SHARED + ${QM_FILES} +) + +target_sources(${module_name} PRIVATE + ../interfaces/qlcioplugin.cpp ../interfaces/qlcioplugin.h + configureolaio.cpp configureolaio.h configureolaio.ui + ${module_name}.cpp ${module_name}.h + olaoutthread.cpp olaoutthread.h + qlclogdestination.cpp qlclogdestination.h +) +target_include_directories(${module_name} PRIVATE + ../interfaces +) + +target_link_libraries(${module_name} PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Widgets +) + +if(APPLE) + set_target_properties(${module_name} PROPERTIES + MACOSX_BUNDLE FALSE + ) + if(${LIBOLASERVER_FOUND}) + target_include_directories(${module_name} PRIVATE + ${LIBOLASERVER_INCLUDE_DIRS} + ) + + target_link_directories(${module_name} PRIVATE + ${LIBOLASERVER_LIBRARY_DIRS} + ) + + target_link_libraries(${module_name} PRIVATE + ${LIBOLASERVER_LIBRARIES} + ) + else() + target_include_directories(${module_name} PRIVATE + /opt/local/include + ) + + target_link_libraries(${module_name} PRIVATE + # Remove: L/opt/local/lib + ola + olacommon + olaserver + ) + endif() +endif() + +if(NOT APPLE) + target_link_libraries(${module_name} PRIVATE + # Remove: L/usr/local/lib + ola + olacommon + olaserver + ) +endif() + +if (UNIX AND NOT APPLE) + install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/org.qlcplus.QLCPlus.ola.metainfo.xml" + DESTINATION ${METAINFODIR}) +endif() + +install(TARGETS ${module_name} + LIBRARY DESTINATION ${INSTALLROOT}/${PLUGINDIR} + RUNTIME DESTINATION ${INSTALLROOT}/${PLUGINDIR} +) diff --git a/plugins/ola/configureolaio.cpp b/plugins/ola/configureolaio.cpp index ad52bcf58b..d16343067a 100644 --- a/plugins/ola/configureolaio.cpp +++ b/plugins/ola/configureolaio.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include "configureolaio.h" #include "olaio.h" @@ -31,6 +32,8 @@ #define COL_NAME 0 #define COL_LINE 1 +#define SETTINGS_GEOMETRY "configureolaio/geometry" + /***************************************************************************** * Initialization *****************************************************************************/ @@ -45,11 +48,19 @@ ConfigureOlaIO::ConfigureOlaIO(OlaIO* plugin, QWidget* parent) populateOutputList(); m_standaloneCheck->setChecked(m_plugin->isServerEmbedded()); + + QSettings settings; + QVariant geometrySettings = settings.value(SETTINGS_GEOMETRY); + if (geometrySettings.isValid() == true) + restoreGeometry(geometrySettings.toByteArray()); } ConfigureOlaIO::~ConfigureOlaIO() { m_plugin->setServerEmbedded(m_standaloneCheck->isChecked()); + + QSettings settings; + settings.setValue(SETTINGS_GEOMETRY, saveGeometry()); } void ConfigureOlaIO::populateOutputList() diff --git a/plugins/ola/olaio.cpp b/plugins/ola/olaio.cpp index 9f14cd4540..1096232f66 100644 --- a/plugins/ola/olaio.cpp +++ b/plugins/ola/olaio.cpp @@ -183,9 +183,10 @@ QString OlaIO::outputInfo(quint32 output) return str; } -void OlaIO::writeUniverse(quint32 universe, quint32 output, const QByteArray &data) +void OlaIO::writeUniverse(quint32 universe, quint32 output, const QByteArray &data, bool dataChanged) { Q_UNUSED(universe) + Q_UNUSED(dataChanged) if (output > UNIVERSE_COUNT || !m_thread) return; diff --git a/plugins/ola/olaio.h b/plugins/ola/olaio.h index a6925b4154..eba470b3b5 100644 --- a/plugins/ola/olaio.h +++ b/plugins/ola/olaio.h @@ -82,7 +82,7 @@ class OlaIO : public QLCIOPlugin QString outputInfo(quint32 output); /** @reimp */ - void writeUniverse(quint32 universe, quint32 output, const QByteArray& data); + void writeUniverse(quint32 universe, quint32 output, const QByteArray& data, bool dataChanged); private: /** Return the output: universe mapping */ diff --git a/plugins/os2l/CMakeLists.txt b/plugins/os2l/CMakeLists.txt new file mode 100644 index 0000000000..5753f40e21 --- /dev/null +++ b/plugins/os2l/CMakeLists.txt @@ -0,0 +1,50 @@ +set(module_name "os2l") + +set(TS_FILES + OS2L_de_DE.ts + OS2L_es_ES.ts + # OS2L_fi_FI.ts + OS2L_fr_FR.ts + OS2L_it_IT.ts + OS2L_nl_NL.ts + # OS2L_cz_CZ.ts + # OS2L_pt_BR.ts + OS2L_ca_ES.ts + OS2L_ja_JP.ts +) + +if(QT_VERSION_MAJOR GREATER 5) + qt_add_translation(QM_FILES ${TS_FILES}) +else() + qt5_add_translation(QM_FILES ${TS_FILES}) +endif() + +add_library(${module_name} SHARED + ${QM_FILES} +) + +target_sources(${module_name} PRIVATE + ../interfaces/qlcioplugin.cpp ../interfaces/qlcioplugin.h + os2lconfiguration.cpp os2lconfiguration.h os2lconfiguration.ui + os2lplugin.cpp os2lplugin.h +) +target_include_directories(${module_name} PRIVATE + ../interfaces +) + +target_link_libraries(${module_name} PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Network + Qt${QT_MAJOR_VERSION}::Widgets +) + +install(TARGETS ${module_name} + LIBRARY DESTINATION ${INSTALLROOT}/${PLUGINDIR} + RUNTIME DESTINATION ${INSTALLROOT}/${PLUGINDIR} +) + +if (UNIX AND NOT APPLE) + install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/org.qlcplus.QLCPlus.os2l.metainfo.xml" + DESTINATION ${METAINFODIR}) +endif() diff --git a/plugins/os2l/os2lconfiguration.cpp b/plugins/os2l/os2lconfiguration.cpp index b4660cd331..c910281b18 100644 --- a/plugins/os2l/os2lconfiguration.cpp +++ b/plugins/os2l/os2lconfiguration.cpp @@ -17,9 +17,13 @@ limitations under the License. */ +#include + #include "os2lconfiguration.h" #include "os2lplugin.h" +#define SETTINGS_GEOMETRY "os2lconfiguration/geometry" + /***************************************************************************** * Initialization *****************************************************************************/ @@ -37,11 +41,17 @@ OS2LConfiguration::OS2LConfiguration(OS2LPlugin* plugin, QWidget* parent) m_hostGroup->hide(); else m_activateLabel->hide(); + + QSettings settings; + QVariant geometrySettings = settings.value(SETTINGS_GEOMETRY); + if (geometrySettings.isValid() == true) + restoreGeometry(geometrySettings.toByteArray()); } OS2LConfiguration::~OS2LConfiguration() { - /** Cleanup the allocated resources, if any */ + QSettings settings; + settings.setValue(SETTINGS_GEOMETRY, saveGeometry()); } /***************************************************************************** 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/CMakeLists.txt b/plugins/osc/CMakeLists.txt new file mode 100644 index 0000000000..edd1cc13b0 --- /dev/null +++ b/plugins/osc/CMakeLists.txt @@ -0,0 +1,53 @@ +set(module_name "osc") + +set(TS_FILES + OSC_de_DE.ts + OSC_es_ES.ts + OSC_fi_FI.ts + OSC_fr_FR.ts + OSC_it_IT.ts + OSC_nl_NL.ts + OSC_cz_CZ.ts + OSC_pt_BR.ts + OSC_ca_ES.ts + OSC_ja_JP.ts +) + +if(QT_VERSION_MAJOR GREATER 5) + qt_add_translation(QM_FILES ${TS_FILES}) +else() + qt5_add_translation(QM_FILES ${TS_FILES}) +endif() + +add_library(${module_name} + SHARED + ${QM_FILES} +) + +target_sources(${module_name} PRIVATE + ../interfaces/qlcioplugin.cpp ../interfaces/qlcioplugin.h + configureosc.cpp configureosc.h configureosc.ui + osccontroller.cpp osccontroller.h + oscpacketizer.cpp oscpacketizer.h + oscplugin.cpp oscplugin.h +) +target_include_directories(${module_name} PRIVATE + ../interfaces +) + +target_link_libraries(${module_name} PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Network + Qt${QT_MAJOR_VERSION}::Widgets +) + +install(TARGETS ${module_name} + LIBRARY DESTINATION ${INSTALLROOT}/${PLUGINDIR} + RUNTIME DESTINATION ${INSTALLROOT}/${PLUGINDIR} +) + +if (UNIX AND NOT APPLE) + install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/org.qlcplus.QLCPlus.osc.metainfo.xml" + DESTINATION ${METAINFODIR}) +endif() diff --git a/plugins/osc/OSC_ca_ES.ts b/plugins/osc/OSC_ca_ES.ts index 97c6c4a9ec..8fd328d435 100644 --- a/plugins/osc/OSC_ca_ES.ts +++ b/plugins/osc/OSC_ca_ES.ts @@ -40,79 +40,84 @@ Port de Sortida - + + Seconds to wait for an interface to be ready + Segons a esperar que estigui preparada una interfície + + + Channel number calculator - + Calculadora de número de canal - + OSC path - + Ruta OSC - + Channel number - + Número de canal - + Inputs Entrades - + Outputs Sortides - + Invalid IP IP Invàlida - + %1 is not a valid IP. Please fix it before confirming. %1 no es una IP vàlida. -Si us plau arreglar-ho abans de confirmar. +Si us plau arreglar-ho abans de confirmar. OSCPlugin - + This plugin provides input for devices supporting the OSC transmission protocol. Aquest plugin proveeix entrada per dispositius que suporten el protocol de tansmissió OSC. - - + + Status: Not open Estat: No obert - - + + Status: Open Estat: Obert - + Packets sent: - Paquets enviats: + Paquets enviats: - + Packets received: - Paquets rebuts: + Paquets rebuts: - + Output Sortida - + Input Entrada diff --git a/plugins/osc/OSC_cz_CZ.ts b/plugins/osc/OSC_cz_CZ.ts index a9dd49cfd6..abe463f458 100644 --- a/plugins/osc/OSC_cz_CZ.ts +++ b/plugins/osc/OSC_cz_CZ.ts @@ -40,37 +40,42 @@ Výstupní port - + + Seconds to wait for an interface to be ready + + + + Channel number calculator - + OSC path - + Channel number - + Inputs Vstupy - + Outputs Výstupy - + Invalid IP Neplatná IP adresa - + %1 is not a valid IP. Please fix it before confirming. %1 není platná IP adresa. @@ -80,39 +85,39 @@ Prosím opravte zadání před potvrzením. OSCPlugin - + This plugin provides input for devices supporting the OSC transmission protocol. Tento plugin přidává vstupy pro zařízení podporující přenosový OSC protokol. - - + + Status: Not open Stav: Není otevřen - - + + Status: Open Stav: Otevřen - + Packets sent: Poslaných paketů: - + Packets received: Přijatých paketů: - + Output Výstup - + Input Vstup diff --git a/plugins/osc/OSC_de_DE.ts b/plugins/osc/OSC_de_DE.ts index 253a1178e6..965ecc3a16 100644 --- a/plugins/osc/OSC_de_DE.ts +++ b/plugins/osc/OSC_de_DE.ts @@ -40,37 +40,42 @@ Ausgangsport - + + Seconds to wait for an interface to be ready + Sekunden um auf die Bereitschaft eines Interfaces zu warten + + + Channel number calculator Kanalnummer-Rechner - + OSC path OSC-Pfad - + Channel number Kanal-Nummer - + Inputs Eingänge - + Outputs Ausgänge - + Invalid IP Ungültige IP - + %1 is not a valid IP. Please fix it before confirming. %1 ist keine gültige IP. @@ -80,39 +85,39 @@ Bitte vor Bestätigung korrigieren. OSCPlugin - + This plugin provides input for devices supporting the OSC transmission protocol. Dieses Plugin bietet Eingabunterstützung für Geräte mit dem OSC-Protokoll. - - + + Status: Not open Status: Nicht geöffnet - - + + Status: Open Status: Geöffnet - + Packets sent: Pakete gesendet: - + Packets received: Pakete empfangen: - + Output Ausgang - + Input Eingang diff --git a/plugins/osc/OSC_es_ES.ts b/plugins/osc/OSC_es_ES.ts index 6a9e8ab07e..d6a5533dc8 100644 --- a/plugins/osc/OSC_es_ES.ts +++ b/plugins/osc/OSC_es_ES.ts @@ -40,79 +40,84 @@ Puerto de Salida - + + Seconds to wait for an interface to be ready + Segundos a esperar que esté preparada una interfaz + + + Channel number calculator - + Calculadora de número de canal - + OSC path - + Ruta OSC - + Channel number - + Número de canal - + Inputs Entradas - + Outputs Salidas - + Invalid IP IP Inválido - + %1 is not a valid IP. Please fix it before confirming. - %1 no es un IP válidoo. + %1 no es un IP válido. Por favor arréglelo antes de confirmar. OSCPlugin - + This plugin provides input for devices supporting the OSC transmission protocol. Este plugin provee Entrada para dispositivos que soportan el protocolo de transmisión OSC. - - + + Status: Not open Estado: No abierto - - + + Status: Open Estado: Abierto - + Packets sent: Paquetes enviados: - + Packets received: Paquetes recibidos: - + Output Salida - + Input Entrada diff --git a/plugins/osc/OSC_fi_FI.ts b/plugins/osc/OSC_fi_FI.ts index 7efe9d9297..83cffa38be 100644 --- a/plugins/osc/OSC_fi_FI.ts +++ b/plugins/osc/OSC_fi_FI.ts @@ -40,37 +40,42 @@ - + + Seconds to wait for an interface to be ready + + + + Channel number calculator - + OSC path - + Channel number - + Inputs - + Outputs - + Invalid IP - + %1 is not a valid IP. Please fix it before confirming. @@ -79,39 +84,39 @@ Please fix it before confirming. OSCPlugin - + This plugin provides input for devices supporting the OSC transmission protocol. - - + + Status: Not open - - + + Status: Open - + Packets sent: - + Packets received: - + Output - + Input diff --git a/plugins/osc/OSC_fr_FR.ts b/plugins/osc/OSC_fr_FR.ts index e01bba7740..3c633324f1 100644 --- a/plugins/osc/OSC_fr_FR.ts +++ b/plugins/osc/OSC_fr_FR.ts @@ -40,37 +40,42 @@ Port de sortie - + + Seconds to wait for an interface to be ready + + + + Channel number calculator Calculateur de numéro de canal - + OSC path Chemin OSC - + Channel number Numéro de canal - + Inputs Entrées - + Outputs Sorties - + Invalid IP IP invalide - + %1 is not a valid IP. Please fix it before confirming. %1 n'est pas une IP valide. @@ -80,39 +85,39 @@ Veuillez la corriger avant de valider. OSCPlugin - + This plugin provides input for devices supporting the OSC transmission protocol. Ce plugin offre le support des périphériques supportant le protocole OSC. - - + + Status: Not open Status : fermé - - + + Status: Open Status : ouvert - + Packets sent: Paquets envoyés : - + Packets received: Paquets reçus : - + Output Sortie - + Input Entrée diff --git a/plugins/osc/OSC_it_IT.ts b/plugins/osc/OSC_it_IT.ts index 05fe99df6a..8645ce2451 100644 --- a/plugins/osc/OSC_it_IT.ts +++ b/plugins/osc/OSC_it_IT.ts @@ -40,37 +40,42 @@ Porta di trasmissione - + + Seconds to wait for an interface to be ready + Secondi di attesa affinchè un'interfaccia sia pronta + + + Channel number calculator Calcolatore numero di canale - + OSC path Percorso OSC - + Channel number Numero di canale - + Inputs Ingressi - + Outputs Uscite - + Invalid IP Indirizzo IP non valido - + %1 is not a valid IP. Please fix it before confirming. %1 non è un indirizzo IP valido. @@ -80,39 +85,39 @@ E' necessario sistemare prima di confermare. OSCPlugin - + This plugin provides input for devices supporting the OSC transmission protocol. Questa plugin permette la ricezione di segnale da dispositivi che supportano il protocollo OSC. - - + + Status: Not open Stato: Non aperto - - + + Status: Open Stato: Aperto - + Packets sent: Pacchetti inviati: - + Packets received: Pacchetti ricevuti: - + Output Uscita - + Input Ingresso diff --git a/plugins/osc/OSC_ja_JP.ts b/plugins/osc/OSC_ja_JP.ts index 180a272c67..41c365b775 100644 --- a/plugins/osc/OSC_ja_JP.ts +++ b/plugins/osc/OSC_ja_JP.ts @@ -40,37 +40,42 @@ - + + Seconds to wait for an interface to be ready + + + + Channel number calculator - + OSC path - + Channel number - + Inputs - + Outputs - + Invalid IP - + %1 is not a valid IP. Please fix it before confirming. @@ -79,39 +84,39 @@ Please fix it before confirming. OSCPlugin - + This plugin provides input for devices supporting the OSC transmission protocol. このプラグインはOSCを使ったMIDI信号の送受信を行います。(TouchOSCなど) - - + + Status: Not open - - + + Status: Open - + Packets sent: - + Packets received: - + Output - + Input diff --git a/plugins/osc/OSC_nl_NL.ts b/plugins/osc/OSC_nl_NL.ts index 427de03b69..e9b311903f 100644 --- a/plugins/osc/OSC_nl_NL.ts +++ b/plugins/osc/OSC_nl_NL.ts @@ -40,37 +40,42 @@ Output Poort - + + Seconds to wait for an interface to be ready + + + + Channel number calculator - + OSC path - + Channel number - + Inputs Inputs - + Outputs Outputs - + Invalid IP Ongeldig IP - + %1 is not a valid IP. Please fix it before confirming. %1 is geen geldig IP. @@ -80,39 +85,39 @@ Pas het IP Adres aan voordat u doorgaat. OSCPlugin - + This plugin provides input for devices supporting the OSC transmission protocol. Deze plugin verzorgt DMX output voor apparaten die het OSC transmissie protocol ondersteunen. - - + + Status: Not open Status: Niet open - - + + Status: Open Status: Open - + Packets sent: Pakketten verzonden: - + Packets received: Pakketten ontvangen: - + Output Output - + Input Input diff --git a/plugins/osc/OSC_pt_BR.ts b/plugins/osc/OSC_pt_BR.ts index 91cd0c62cc..28a7142b2b 100644 --- a/plugins/osc/OSC_pt_BR.ts +++ b/plugins/osc/OSC_pt_BR.ts @@ -40,37 +40,42 @@ - + + Seconds to wait for an interface to be ready + + + + Channel number calculator - + OSC path - + Channel number - + Inputs - + Outputs - + Invalid IP - + %1 is not a valid IP. Please fix it before confirming. @@ -79,39 +84,39 @@ Please fix it before confirming. OSCPlugin - + This plugin provides input for devices supporting the OSC transmission protocol. Este plugin fornece suporte de entrada para dispositivos que usam o protocolo de transmissão OSC. - - + + Status: Not open - - + + Status: Open - + Packets sent: - + Packets received: - + Output Saída - + Input Entrada diff --git a/plugins/osc/configureosc.cpp b/plugins/osc/configureosc.cpp index 5f821e7bbc..ff9392df71 100644 --- a/plugins/osc/configureosc.cpp +++ b/plugins/osc/configureosc.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include "configureosc.h" #include "oscplugin.h" @@ -38,6 +39,8 @@ #define PROP_LINE (Qt::UserRole + 1) #define PROP_TYPE (Qt::UserRole + 2) +#define SETTINGS_GEOMETRY "configureosc/geometry" + /***************************************************************************** * Initialization *****************************************************************************/ @@ -55,10 +58,20 @@ ConfigureOSC::ConfigureOSC(OSCPlugin* plugin, QWidget* parent) this, SLOT(slotOSCPathChanged(QString))); fillMappingTree(); + + QSettings settings; + QVariant value = settings.value(SETTINGS_IFACE_WAIT_TIME); + if (value.isValid() == true) + m_waitReadySpin->setValue(value.toInt()); + QVariant geometrySettings = settings.value(SETTINGS_GEOMETRY); + if (geometrySettings.isValid() == true) + restoreGeometry(geometrySettings.toByteArray()); } ConfigureOSC::~ConfigureOSC() { + QSettings settings; + settings.setValue(SETTINGS_GEOMETRY, saveGeometry()); } void ConfigureOSC::fillMappingTree() @@ -67,7 +80,7 @@ void ConfigureOSC::fillMappingTree() QTreeWidgetItem* outputItem = NULL; QList IOmap = m_plugin->getIOMapping(); - foreach(OSCIO io, IOmap) + foreach (OSCIO io, IOmap) { if (io.controller == NULL) continue; @@ -89,7 +102,7 @@ void ConfigureOSC::fillMappingTree() outputItem->setText(KMapColumnInterface, tr("Outputs")); outputItem->setExpanded(true); } - foreach(quint32 universe, controller->universesList()) + foreach (quint32 universe, controller->universesList()) { UniverseInfo *info = controller->getUniverseInfo(universe); QString networkIP = controller->getNetworkIP().toString(); @@ -177,10 +190,10 @@ void ConfigureOSC::showIPAlert(QString ip) void ConfigureOSC::accept() { - for(int i = 0; i < m_uniMapTree->topLevelItemCount(); i++) + for (int i = 0; i < m_uniMapTree->topLevelItemCount(); i++) { QTreeWidgetItem *topItem = m_uniMapTree->topLevelItem(i); - for(int c = 0; c < topItem->childCount(); c++) + for (int c = 0; c < topItem->childCount(); c++) { QTreeWidgetItem *item = topItem->child(c); if (item->data(KMapColumnInterface, PROP_UNIVERSE).isValid() == false) @@ -224,6 +237,13 @@ void ConfigureOSC::accept() } } + QSettings settings; + int waitTime = m_waitReadySpin->value(); + if (waitTime == 0) + settings.remove(SETTINGS_IFACE_WAIT_TIME); + else + settings.setValue(SETTINGS_IFACE_WAIT_TIME, waitTime); + QDialog::accept(); } diff --git a/plugins/osc/configureosc.ui b/plugins/osc/configureosc.ui index 252d000d2b..04b729fbc1 100644 --- a/plugins/osc/configureosc.ui +++ b/plugins/osc/configureosc.ui @@ -82,6 +82,40 @@
+ + + + + + Seconds to wait for an interface to be ready + + + + + + + + 0 + 0 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + @@ -140,7 +174,6 @@ - diff --git a/plugins/osc/osccontroller.cpp b/plugins/osc/osccontroller.cpp index 8939ceb267..b79146295e 100644 --- a/plugins/osc/osccontroller.cpp +++ b/plugins/osc/osccontroller.cpp @@ -120,14 +120,14 @@ bool OSCController::setInputPort(quint32 universe, quint16 port) QSharedPointer OSCController::getInputSocket(quint16 port) { - foreach(UniverseInfo const& info, m_universeMap) + foreach (UniverseInfo const& info, m_universeMap) { if (info.inputSocket && info.inputPort == port) return info.inputSocket; } QSharedPointer inputSocket(new QUdpSocket(this)); - inputSocket->bind(m_ipAddr, port, QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint); + inputSocket->bind(QHostAddress::Any, port, QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint); connect(inputSocket.data(), SIGNAL(readyRead()), this, SLOT(processPendingPackets())); return inputSocket; @@ -199,7 +199,7 @@ UniverseInfo* OSCController::getUniverseInfo(quint32 universe) OSCController::Type OSCController::type() const { int type = Unknown; - foreach(UniverseInfo info, m_universeMap.values()) + foreach (UniverseInfo info, m_universeMap.values()) { type |= info.type; } @@ -314,19 +314,19 @@ void OSCController::sendFeedback(const quint32 universe, quint32 channel, uchar } values = m_universeMap[universe].multipartCache[path]; - if (values.count() <= valIdx) + if (values.length() <= valIdx) values.resize(valIdx + 1); values[valIdx] = (char)value; m_universeMap[universe].multipartCache[path] = values; //qDebug() << "Values to send:" << QString::number((uchar)values.at(0)) << - // QString::number((uchar)values.at(1)) << values.count(); + // QString::number((uchar)values.at(1)) << values.length(); } else values.append((char)value); // single value path QString pTypes; - pTypes.fill('f', values.count()); + pTypes.fill('f', values.length()); m_packetizer->setupOSCGeneric(oscPacket, path, pTypes, values); qint64 sent = m_outputSocket->writeDatagram(oscPacket.data(), oscPacket.size(), @@ -358,7 +358,7 @@ void OSCController::handlePacket(QUdpSocket* socket, QByteArray const& datagram, QString path = msg.first; QByteArray values = msg.second; - qDebug() << "[OSC] message has path:" << path << "values:" << values.count(); + qDebug() << "[OSC] message has path:" << path << "values:" << values.length(); if (values.isEmpty()) continue; @@ -368,10 +368,10 @@ void OSCController::handlePacket(QUdpSocket* socket, QByteArray const& datagram, UniverseInfo& info = it.value(); if (info.inputSocket == socket) { - if (values.count() > 1) + if (values.length() > 1) { info.multipartCache[path] = values; - for(int i = 0; i < values.count(); i++) + for (int i = 0; i < values.length(); i++) { QString modPath = QString("%1_%2").arg(path).arg(i); emit valueChanged(universe, m_line, getHash(modPath), (uchar)values.at(i), modPath); diff --git a/plugins/osc/osccontroller.h b/plugins/osc/osccontroller.h index 93ac47f163..3578b03fc5 100644 --- a/plugins/osc/osccontroller.h +++ b/plugins/osc/osccontroller.h @@ -36,7 +36,7 @@ #include "oscpacketizer.h" -typedef struct +typedef struct _uinfo { QSharedPointer inputSocket; diff --git a/plugins/osc/oscplugin.cpp b/plugins/osc/oscplugin.cpp index ae7f16eff1..d3ce296b92 100644 --- a/plugins/osc/oscplugin.cpp +++ b/plugins/osc/oscplugin.cpp @@ -17,13 +17,11 @@ limitations under the License. */ -#include "oscplugin.h" -#include "configureosc.h" - #include #include -#define MAX_INIT_RETRY 10 +#include "oscplugin.h" +#include "configureosc.h" bool addressCompare(const OSCIO &v1, const OSCIO &v2) { @@ -36,9 +34,16 @@ OSCPlugin::~OSCPlugin() void OSCPlugin::init() { - foreach(QNetworkInterface interface, QNetworkInterface::allInterfaces()) + QSettings settings; + QVariant value = settings.value(SETTINGS_IFACE_WAIT_TIME); + if (value.isValid() == true) + m_ifaceWaitTime = value.toInt(); + else + m_ifaceWaitTime = 0; + + foreach (QNetworkInterface iface, QNetworkInterface::allInterfaces()) { - foreach (QNetworkAddressEntry entry, interface.addressEntries()) + foreach (QNetworkAddressEntry entry, iface.addressEntries()) { QHostAddress addr = entry.ip(); if (addr.protocol() != QAbstractSocket::IPv6Protocol) @@ -48,7 +53,7 @@ void OSCPlugin::init() tmpIO.controller = NULL; bool alreadyInList = false; - for(int j = 0; j < m_IOmapping.count(); j++) + for (int j = 0; j < m_IOmapping.count(); j++) { if (m_IOmapping.at(j).IPAddress == tmpIO.IPAddress) { @@ -94,16 +99,19 @@ QString OSCPlugin::pluginInfo() return str; } -bool OSCPlugin::requestLine(quint32 line, int retries) +bool OSCPlugin::requestLine(quint32 line) { int retryCount = 0; while (line >= (quint32)m_IOmapping.length()) { qDebug() << "[OSC] cannot open line" << line << "(available:" << m_IOmapping.length() << ")"; - Sleep(1000); - init(); - if (retryCount++ == retries) + if (m_ifaceWaitTime) + { + Sleep(1000); + init(); + } + if (retryCount++ >= m_ifaceWaitTime) return false; } @@ -153,7 +161,7 @@ QString OSCPlugin::outputInfo(quint32 output) bool OSCPlugin::openOutput(quint32 output, quint32 universe) { - if (requestLine(output, MAX_INIT_RETRY) == false) + if (requestLine(output) == false) return false; qDebug() << "[OSC] Open output with address :" << m_IOmapping.at(output).IPAddress; @@ -190,8 +198,10 @@ void OSCPlugin::closeOutput(quint32 output, quint32 universe) } } -void OSCPlugin::writeUniverse(quint32 universe, quint32 output, const QByteArray &data) +void OSCPlugin::writeUniverse(quint32 universe, quint32 output, const QByteArray &data, bool dataChanged) { + Q_UNUSED(dataChanged) + if (output >= (quint32)m_IOmapping.count()) return; @@ -217,7 +227,7 @@ QStringList OSCPlugin::inputs() bool OSCPlugin::openInput(quint32 input, quint32 universe) { - if (requestLine(input, MAX_INIT_RETRY) == false) + if (requestLine(input) == false) return false; qDebug() << "[OSC] Open input on address :" << m_IOmapping.at(input).IPAddress; @@ -283,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 f6af6714af..6050db1c1c 100644 --- a/plugins/osc/oscplugin.h +++ b/plugins/osc/oscplugin.h @@ -30,7 +30,7 @@ #include "qlcioplugin.h" #include "osccontroller.h" -typedef struct +typedef struct _oio { QString IPAddress; OSCController* controller; @@ -42,6 +42,7 @@ typedef struct #define OSC_OUTPUTIP "outputIP" #define OSC_OUTPUTPORT "outputPort" +#define SETTINGS_IFACE_WAIT_TIME "OSCPlugin/ifacewait" class OSCPlugin : public QLCIOPlugin { @@ -69,7 +70,7 @@ class OSCPlugin : public QLCIOPlugin QString pluginInfo(); private: - bool requestLine(quint32 line, int retries); + bool requestLine(quint32 line); /********************************************************************* * Outputs @@ -88,7 +89,7 @@ class OSCPlugin : public QLCIOPlugin QString outputInfo(quint32 output); /** @reimp */ - void writeUniverse(quint32 universe, quint32 output, const QByteArray& data); + void writeUniverse(quint32 universe, quint32 output, const QByteArray& data, bool dataChanged); /************************************************************************* * Inputs @@ -107,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 @@ -128,6 +129,9 @@ class OSCPlugin : public QLCIOPlugin private: /** Map of the OSC plugin Input/Output lines */ QListm_IOmapping; + + /** Time to wait (in seconds) for interfaces to be ready */ + int m_ifaceWaitTime; }; #endif diff --git a/plugins/peperoni/CMakeLists.txt b/plugins/peperoni/CMakeLists.txt new file mode 100644 index 0000000000..2817517ee3 --- /dev/null +++ b/plugins/peperoni/CMakeLists.txt @@ -0,0 +1,8 @@ +project(peperoni) + +if(WIN32) + add_subdirectory(win32) +endif() +if(UNIX) + add_subdirectory(unix) +endif() diff --git a/plugins/peperoni/Peperoni_ca_ES.ts b/plugins/peperoni/Peperoni_ca_ES.ts index 708c24efef..fdf5c49d2a 100644 --- a/plugins/peperoni/Peperoni_ca_ES.ts +++ b/plugins/peperoni/Peperoni_ca_ES.ts @@ -4,23 +4,23 @@ Peperoni - + This plugin provides DMX output support for Peperoni DMX devices. Aquest plugin proveeix suport de sortida DMX per dispositius Peperoni. - + The shared library usbdmx.dll could not be found or is too old to be used with QLC. La llibrería compartida usbdmc.dll no spha trobat o es massa vell per ser emprat amb QLC+. - - + + Do you wish to re-scan your hardware? Vol tornar a escanejar el seu maquinari? - + This plugin provides DMX input and output support for Peperoni DMX devices. Aquest plugin proveeix suport d'entrada i sortida DMX per dispositius Peperoni. @@ -29,7 +29,7 @@ PeperoniDevice - + Device is working correctly. El dispositiu està treballant correctament. @@ -45,49 +45,49 @@ - + Unknown Desconegut - + Universe Univers - + Firmware version: %1 Revisió del Firmware: %1 - + Unknown device Dispositiu desconegut - + Cannot connect to USB device. No es pot connectar al dispositiu USB. - + Input line Línia d'entrada - - + + Open Obre - - + + Close Tanca - + Output line Línia de sortida diff --git a/plugins/peperoni/Peperoni_cz_CZ.ts b/plugins/peperoni/Peperoni_cz_CZ.ts index 3bf025063e..a31d345313 100644 --- a/plugins/peperoni/Peperoni_cz_CZ.ts +++ b/plugins/peperoni/Peperoni_cz_CZ.ts @@ -4,23 +4,23 @@ Peperoni - + This plugin provides DMX output support for Peperoni DMX devices. Tento plugin přidává podporu DMX výstupu pro Peperoni DMX zařízení. - + The shared library usbdmx.dll could not be found or is too old to be used with QLC. Sdílená knihovna usbdmx.dll nebyla nalezena nebo je příliž zastaralá pro použití s QLC. - - + + Do you wish to re-scan your hardware? Přejete si prohledat Váš hardware? - + This plugin provides DMX input and output support for Peperoni DMX devices. Tento plugin přidává podporu DMX vstupu avýstupu pro Peperoni DMX zařízení. @@ -29,7 +29,7 @@ PeperoniDevice - + Device is working correctly. Zařízení pracuje správně. @@ -45,49 +45,49 @@ - + Unknown Neznámý - + Universe Větev - + Firmware version: %1 Verze firmware: %1 - + Unknown device Neznámé zařízení - + Cannot connect to USB device. Nelze se připojit k USB zařízení. - + Input line Linka vstupu - - + + Open Otevřít - - + + Close Zavřít - + Output line Linka výstupu diff --git a/plugins/peperoni/Peperoni_de_DE.ts b/plugins/peperoni/Peperoni_de_DE.ts index e20c518941..9f8d600002 100644 --- a/plugins/peperoni/Peperoni_de_DE.ts +++ b/plugins/peperoni/Peperoni_de_DE.ts @@ -4,23 +4,23 @@ Peperoni - + This plugin provides DMX output support for Peperoni DMX devices. Dieses Plugin bietet DMX-Output für Peperoni DMX-Geräte. - + The shared library usbdmx.dll could not be found or is too old to be used with QLC. Die Bibliothek usbdmx.dll konnte nicht gefunden werden, oder ist zu alt um mit QLC verwendet zu werden. - - + + Do you wish to re-scan your hardware? Nach neuer Hardware suchen? - + This plugin provides DMX input and output support for Peperoni DMX devices. Dieses Plugin bietet DMX Ein- und Ausgabe für Peperoni DMX-Geräte. @@ -29,7 +29,7 @@ PeperoniDevice - + Device is working correctly. Gerät ist funktionsbereit. @@ -45,49 +45,49 @@ - + Unknown Unbekannt - + Universe Universum - + Firmware version: %1 Firmware Version: %1 - + Unknown device Unbekanntes Gerät - + Cannot connect to USB device. Kann nicht mit dem USB-Gerät verbinden. - + Input line Eingabezeile - - + + Open Öffnen - - + + Close Schließen - + Output line Ausgabezeile diff --git a/plugins/peperoni/Peperoni_es_ES.ts b/plugins/peperoni/Peperoni_es_ES.ts index e1d4ca132d..6718a08595 100644 --- a/plugins/peperoni/Peperoni_es_ES.ts +++ b/plugins/peperoni/Peperoni_es_ES.ts @@ -4,23 +4,23 @@ Peperoni - + This plugin provides DMX output support for Peperoni DMX devices. Este plugin provee soporte para Salida DMX para los dispositivos Peperoni DMX. - + The shared library usbdmx.dll could not be found or is too old to be used with QLC. La librería compartida usbdmx.dll no puede ser encontrada o es muy antigua para ser usada con QLC+. - - + + Do you wish to re-scan your hardware? ¿Desea volver a escanear su hardware? - + This plugin provides DMX input and output support for Peperoni DMX devices. Este plugin proporciona soporte de entrada y salida DMX para dispositivos Peperoni. @@ -29,7 +29,7 @@ PeperoniDevice - + Device is working correctly. El dispositivo funciona correctamente. @@ -45,49 +45,49 @@ - + Unknown Desconocido - + Universe Universo - + Firmware version: %1 Versión de Frimware: %1 - + Unknown device Dispositivo desconocido - + Cannot connect to USB device. No se puede conectar con el dispositivo USB. - + Input line Línea de entrada - - + + Open Abrir - - + + Close Cerrar - + Output line Línea de salida diff --git a/plugins/peperoni/Peperoni_fi_FI.ts b/plugins/peperoni/Peperoni_fi_FI.ts index 668a62420e..78a1874f99 100644 --- a/plugins/peperoni/Peperoni_fi_FI.ts +++ b/plugins/peperoni/Peperoni_fi_FI.ts @@ -4,23 +4,23 @@ Peperoni - + This plugin provides DMX output support for Peperoni DMX devices. Tämä liitännäinen tuottaa DMX-ulostulotuen Peperoni DMX-laitteille. - + The shared library usbdmx.dll could not be found or is too old to be used with QLC. Jaettua kirjastoa usbdmx.dll ei löytynyt tai nykyinen versio on liian vanha QLC:n kanssa käytetäväksi. - - + + Do you wish to re-scan your hardware? Etsitäänkö lisää laitteita? - + This plugin provides DMX input and output support for Peperoni DMX devices. @@ -29,7 +29,7 @@ PeperoniDevice - + Device is working correctly. Laite toimii oikein. @@ -45,49 +45,49 @@ - + Unknown Tuntematon - + Universe - + Firmware version: %1 Laitteen ohjelmistoversio: %1 - + Unknown device Tuntematon laite - + Cannot connect to USB device. USB-laitteeseen ei saada yhteyttä. - + Input line - - + + Open - - + + Close - + Output line diff --git a/plugins/peperoni/Peperoni_fr_FR.ts b/plugins/peperoni/Peperoni_fr_FR.ts index 53e7dd0389..19cc425550 100644 --- a/plugins/peperoni/Peperoni_fr_FR.ts +++ b/plugins/peperoni/Peperoni_fr_FR.ts @@ -4,23 +4,23 @@ Peperoni - + This plugin provides DMX output support for Peperoni DMX devices. Ce plugin offre le support de la sortie des interfaces Peperoni. - + The shared library usbdmx.dll could not be found or is too old to be used with QLC. Impossible de trouver la bibliothèque usbdmx.dll ou celle-ci est trop ancienne pour être utilisée avec QLC. - - + + Do you wish to re-scan your hardware? Souhaitez-vous redétecter le matériel ? - + This plugin provides DMX input and output support for Peperoni DMX devices. Ce plugin offre le support de l'entrée et la sortie des interfaces Peperoni. @@ -29,7 +29,7 @@ PeperoniDevice - + Device is working correctly. Le périphérique fonctionne correctement. @@ -45,49 +45,49 @@ - + Unknown Inconnu - + Universe Univers - + Firmware version: %1 Version du firmware : %1 - + Unknown device Périphérique inconnu - + Cannot connect to USB device. Impossible de se connecter au périphérique USB. - + Input line Ligne d'entrée - - + + Open Ouvert - - + + Close Fermé - + Output line Ligne de sortie diff --git a/plugins/peperoni/Peperoni_it_IT.ts b/plugins/peperoni/Peperoni_it_IT.ts index 3c23ee2ac6..6c37b77c7c 100644 --- a/plugins/peperoni/Peperoni_it_IT.ts +++ b/plugins/peperoni/Peperoni_it_IT.ts @@ -4,23 +4,23 @@ Peperoni - + This plugin provides DMX output support for Peperoni DMX devices. Questa plugin permette la trasmissione di segnale DMX su interfacce Peperoni. - + The shared library usbdmx.dll could not be found or is too old to be used with QLC. La libreria dinamica usbdmx.dll non è stata trovata o è troppo vecchia per essere usata da QLC+. - - + + Do you wish to re-scan your hardware? Vuoi rifare la scansione delle tue interfacce? - + This plugin provides DMX input and output support for Peperoni DMX devices. Questa plugin permette la ricezione e la trasmissione di segnale DMX su interfacce Peperoni Lighting. @@ -29,7 +29,7 @@ PeperoniDevice - + Device is working correctly. L'interfaccia funziona correttamente. @@ -45,49 +45,49 @@ - + Unknown Sconosciuto - + Universe Universo - + Firmware version: %1 Versione Firmware: %1 - + Unknown device Dispositivo sconosciuto - + Cannot connect to USB device. Non posso connettere l'interfaccia USB. - + Input line Linea di ingresso - - + + Open Aperta - - + + Close Chiusa - + Output line Linea di uscita diff --git a/plugins/peperoni/Peperoni_ja_JP.ts b/plugins/peperoni/Peperoni_ja_JP.ts index 72f15c8f1c..9e7aca01dd 100644 --- a/plugins/peperoni/Peperoni_ja_JP.ts +++ b/plugins/peperoni/Peperoni_ja_JP.ts @@ -4,23 +4,23 @@ Peperoni - + This plugin provides DMX output support for Peperoni DMX devices. このプラグインは、Peperoni DMX デバイスにDMX信号を送信します。 - + The shared library usbdmx.dll could not be found or is too old to be used with QLC. - - + + Do you wish to re-scan your hardware? ハードウェアを再スキャンしますか? - + This plugin provides DMX input and output support for Peperoni DMX devices. このプラグインは、Peperoni DMX デバイスとの間でDMX信号を送受信します。 @@ -29,7 +29,7 @@ PeperoniDevice - + Device is working correctly. デバイスは正常に動作しています。 @@ -45,49 +45,49 @@ - + Unknown - + Universe - + Firmware version: %1 ファームウェアバージョン: %1 - + Unknown device 不明なデバイス - + Cannot connect to USB device. USBデバイスに接続できません。 - + Input line - - + + Open - - + + Close - + Output line diff --git a/plugins/peperoni/Peperoni_nl_NL.ts b/plugins/peperoni/Peperoni_nl_NL.ts index 67aa55f504..e67d55d397 100644 --- a/plugins/peperoni/Peperoni_nl_NL.ts +++ b/plugins/peperoni/Peperoni_nl_NL.ts @@ -4,23 +4,23 @@ Peperoni - + This plugin provides DMX output support for Peperoni DMX devices. Deze plugin verzorgt DMX output voor Peperoni DMX apparaten. - + The shared library usbdmx.dll could not be found or is too old to be used with QLC. Het bestand USBDMX.DLL kon niet worden gevonden of is te oud om met QLC+ te gebruiken. - - + + Do you wish to re-scan your hardware? Hardware opnieuw scannen? - + This plugin provides DMX input and output support for Peperoni DMX devices. Deze plugin verzorgt DMX input en output voor Peperoni DMX apparaten. @@ -29,7 +29,7 @@ PeperoniDevice - + Device is working correctly. Apparaat werkt correct. @@ -45,49 +45,49 @@ - + Unknown Onbekend - + Universe Universe - + Firmware version: %1 Firmwareversie: %1 - + Unknown device Onbekend apparaat - + Cannot connect to USB device. Verbinden met USB apparaat mislukt. - + Input line Input lijn - - + + Open Open - - + + Close Gesloten - + Output line Output lijn diff --git a/plugins/peperoni/Peperoni_pt_BR.ts b/plugins/peperoni/Peperoni_pt_BR.ts index 44fbb361eb..b0ee99ac54 100644 --- a/plugins/peperoni/Peperoni_pt_BR.ts +++ b/plugins/peperoni/Peperoni_pt_BR.ts @@ -4,23 +4,23 @@ Peperoni - + This plugin provides DMX output support for Peperoni DMX devices. Este plugin fornece suporte de saída DMX para os dispositivos Peperoni DMX. - + The shared library usbdmx.dll could not be found or is too old to be used with QLC. A livraria partilhada usbdmx.dll não foi encontrada ou é muito antiga para ser usada com o QLC+. - - + + Do you wish to re-scan your hardware? Deseja voltar a examinar o seu hardware? - + This plugin provides DMX input and output support for Peperoni DMX devices. @@ -29,7 +29,7 @@ PeperoniDevice - + Device is working correctly. O diispositivo funciona correctamente. @@ -45,49 +45,49 @@ - + Unknown Desconhecido - + Universe - + Firmware version: %1 Versão de Firmware: %1 - + Unknown device Dispositivo desconhecido - + Cannot connect to USB device. Não consegue ligar ao dispositivo USB. - + Input line - - + + Open - - + + Close - + Output line diff --git a/plugins/peperoni/common/CMakeLists.txt b/plugins/peperoni/common/CMakeLists.txt new file mode 100644 index 0000000000..093a88840b --- /dev/null +++ b/plugins/peperoni/common/CMakeLists.txt @@ -0,0 +1,48 @@ +project(common) + +find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Gui) + +add_executable(common WIN32 MACOSX_BUNDLE + inputdevice.cpp inputdevice.h + iodevice.cpp iodevice.h + ioenumerator.cpp ioenumerator.h + main.cpp + outputdevice.cpp outputdevice.h +) + +target_link_libraries(common PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui +) + +if(WIN32) + target_sources(common PRIVATE + ../win32/peperoni/usbdmx-dynamic.cpp ../win32/peperoni/usbdmx-dynamic.h + win32ioenumerator.cpp win32ioenumerator.h + win32peperonidevice.cpp win32peperonidevice.h + ) + + set_target_properties(common PROPERTIES + WIN32_EXECUTABLE FALSE + ) + target_include_directories(common PRIVATE + ../win32/peperoni + ) +endif() + +if(APPLE) + set_target_properties(common PROPERTIES + MACOSX_BUNDLE FALSE + ) +endif() + +if(UNIX) + target_sources(common PRIVATE + unixioenumerator.cpp unixioenumerator.h + unixpeperonidevice.cpp unixpeperonidevice.h + ) +endif() + +# Consider using qt_generate_deploy_app_script() for app deployment if +# the project can use Qt 6.3. In that case rerun qmake2cmake with +# --min-qt-version=6.3. diff --git a/plugins/peperoni/unix/CMakeLists.txt b/plugins/peperoni/unix/CMakeLists.txt new file mode 100644 index 0000000000..85a0df0cfe --- /dev/null +++ b/plugins/peperoni/unix/CMakeLists.txt @@ -0,0 +1,68 @@ + +set(module_name "peperoni") + +set(TS_FILES + ../Peperoni_fi_FI.ts + ../Peperoni_de_DE.ts + ../Peperoni_es_ES.ts + ../Peperoni_fr_FR.ts + ../Peperoni_it_IT.ts + ../Peperoni_nl_NL.ts + ../Peperoni_cz_CZ.ts + ../Peperoni_pt_BR.ts + ../Peperoni_ca_ES.ts + ../Peperoni_ja_JP.ts +) + +if(QT_VERSION_MAJOR GREATER 5) + qt_add_translation(QM_FILES ${TS_FILES}) +else() + qt5_add_translation(QM_FILES ${TS_FILES}) +endif() + +add_library(${module_name} + SHARED + ${QM_FILES} +) + +target_sources(${module_name} PRIVATE + ../../interfaces/qlcioplugin.cpp ../../interfaces/qlcioplugin.h + ${module_name}.cpp ${module_name}.h + peperonidevice.cpp peperonidevice.h +) +target_include_directories(${module_name} PRIVATE + ../../interfaces + ../common +) + +target_link_libraries(${module_name} PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Widgets +) + +if(${LIBUSB_1_FOUND}) + target_include_directories(${module_name} PRIVATE + ${LIBUSB_1_INCLUDE_DIRS} + ) + + target_link_libraries(${module_name} PRIVATE + ${LIBUSB_1_LINK_LIBRARIES} + ) +endif() + +install(TARGETS ${module_name} + LIBRARY DESTINATION ${INSTALLROOT}/${PLUGINDIR} + RUNTIME DESTINATION ${INSTALLROOT}/${PLUGINDIR} +) + +if (NOT APPLE) + install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/z65-peperoni.rules" + DESTINATION ${UDEVRULESDIR}) +endif() + +if (UNIX AND NOT APPLE) + # Rules to make USB DMX devices readable & writable by normal users + install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/org.qlcplus.QLCPlus.peperoni.metainfo.xml" + DESTINATION ${METAINFODIR}) +endif() diff --git a/plugins/peperoni/unix/peperoni.cpp b/plugins/peperoni/unix/peperoni.cpp index 6fcebe37cc..582e5d5119 100644 --- a/plugins/peperoni/unix/peperoni.cpp +++ b/plugins/peperoni/unix/peperoni.cpp @@ -81,7 +81,7 @@ QStringList Peperoni::outputs() int i = 0; QList devList = m_devices.values(); - foreach(PeperoniDevice* dev, devList) + foreach (PeperoniDevice* dev, devList) list << dev->name(i++); return list; @@ -125,9 +125,10 @@ QString Peperoni::outputInfo(quint32 output) return str; } -void Peperoni::writeUniverse(quint32 universe, quint32 output, const QByteArray &data) +void Peperoni::writeUniverse(quint32 universe, quint32 output, const QByteArray &data, bool dataChanged) { Q_UNUSED(universe) + Q_UNUSED(dataChanged) if (m_devices.contains(output) == false) return; @@ -174,7 +175,7 @@ QStringList Peperoni::inputs() int i = 0; QList devList = m_devices.values(); - foreach(PeperoniDevice* dev, devList) + foreach (PeperoniDevice* dev, devList) list << dev->name(i++); return list; @@ -272,7 +273,7 @@ void Peperoni::rescanDevices() //qDebug() << "[Peperoni] Need to destroy" << destroyList.count() << "devices"; QHashIterator it(destroyList); - while(it.hasNext()) + while (it.hasNext()) { it.next(); PeperoniDevice *dev = m_devices.take(it.key()); diff --git a/plugins/peperoni/unix/peperoni.h b/plugins/peperoni/unix/peperoni.h index 34fabe1d13..f79f216aa2 100644 --- a/plugins/peperoni/unix/peperoni.h +++ b/plugins/peperoni/unix/peperoni.h @@ -75,7 +75,7 @@ class Peperoni : public QLCIOPlugin QString outputInfo(quint32 output); /** @reimp */ - void writeUniverse(quint32 universe, quint32 output, const QByteArray& data); + void writeUniverse(quint32 universe, quint32 output, const QByteArray& data, bool dataChanged); /************************************************************************* * Inputs diff --git a/plugins/peperoni/unix/peperonidevice.cpp b/plugins/peperoni/unix/peperonidevice.cpp index 5f463bf350..874589c348 100644 --- a/plugins/peperoni/unix/peperonidevice.cpp +++ b/plugins/peperoni/unix/peperonidevice.cpp @@ -364,7 +364,7 @@ void PeperoniDevice::run() qDebug() << "[Peperoni] input thread started correctly"; - while(m_running == true) + while (m_running == true) { QByteArray tmpBuffer(512, 0); diff --git a/plugins/peperoni/win32/CMakeLists.txt b/plugins/peperoni/win32/CMakeLists.txt new file mode 100644 index 0000000000..2657eef66c --- /dev/null +++ b/plugins/peperoni/win32/CMakeLists.txt @@ -0,0 +1,47 @@ +set(module_name "peperoni") + +set(TS_FILES + ../Peperoni_fi_FI.ts + ../Peperoni_de_DE.ts + ../Peperoni_es_ES.ts + ../Peperoni_fr_FR.ts + ../Peperoni_it_IT.ts + ../Peperoni_nl_NL.ts + ../Peperoni_cz_CZ.ts + ../Peperoni_pt_BR.ts + ../Peperoni_ca_ES.ts + ../Peperoni_ja_JP.ts +) + +if(QT_VERSION_MAJOR GREATER 5) + qt_add_translation(QM_FILES ${TS_FILES}) +else() + qt5_add_translation(QM_FILES ${TS_FILES}) +endif() + +add_library(${module_name} + SHARED + ${QM_FILES} +) + +target_sources(${module_name} PRIVATE + ../../interfaces/qlcioplugin.cpp ../../interfaces/qlcioplugin.h + ${module_name}.cpp ${module_name}.h + ${module_name}/usbdmx-dynamic.cpp ${module_name}/usbdmx-dynamic.h + peperonidevice.cpp peperonidevice.h +) +target_include_directories(${module_name} PRIVATE + ../../interfaces + ${module_name} +) + +target_link_libraries(${module_name} PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Widgets +) + +install(TARGETS ${module_name} + LIBRARY DESTINATION ${INSTALLROOT}/${PLUGINDIR} + RUNTIME DESTINATION ${INSTALLROOT}/${PLUGINDIR} +) diff --git a/plugins/peperoni/win32/peperoni.cpp b/plugins/peperoni/win32/peperoni.cpp index 4e25ab8f77..495754ec0b 100644 --- a/plugins/peperoni/win32/peperoni.cpp +++ b/plugins/peperoni/win32/peperoni.cpp @@ -149,9 +149,10 @@ QString Peperoni::outputInfo(quint32 output) return str; } -void Peperoni::writeUniverse(quint32 universe, quint32 output, const QByteArray &data) +void Peperoni::writeUniverse(quint32 universe, quint32 output, const QByteArray &data, bool dataChanged) { Q_UNUSED(universe) + Q_UNUSED(dataChanged) if (output < quint32(m_devices.size())) m_devices.at(output)->outputDMX(data); diff --git a/plugins/peperoni/win32/peperoni.h b/plugins/peperoni/win32/peperoni.h index fab156a6fe..4684b1ac43 100644 --- a/plugins/peperoni/win32/peperoni.h +++ b/plugins/peperoni/win32/peperoni.h @@ -75,7 +75,7 @@ class Peperoni : public QLCIOPlugin QString outputInfo(quint32 output); /** @reimp */ - void writeUniverse(quint32 universe, quint32 output, const QByteArray& data); + void writeUniverse(quint32 universe, quint32 output, const QByteArray& data, bool dataChanged); /** Attempt to find all connected Peperoni devices */ void rescanDevices(); diff --git a/plugins/spi/CMakeLists.txt b/plugins/spi/CMakeLists.txt new file mode 100644 index 0000000000..ccb34b2090 --- /dev/null +++ b/plugins/spi/CMakeLists.txt @@ -0,0 +1,55 @@ +set(module_name "spi") + +set(TS_FILES + SPI_de_DE.ts + SPI_es_ES.ts + SPI_fi_FI.ts + SPI_fr_FR.ts + SPI_it_IT.ts + SPI_nl_NL.ts + SPI_cz_CZ.ts + SPI_pt_BR.ts + SPI_ca_ES.ts + SPI_ja_JP.ts +) + +if(QT_VERSION_MAJOR GREATER 5) + qt_add_translation(QM_FILES ${TS_FILES}) +else() + qt5_add_translation(QM_FILES ${TS_FILES}) +endif() + +add_library(${module_name} + SHARED + ${QM_FILES} +) + +target_sources(${module_name} PRIVATE + ../interfaces/qlcioplugin.cpp ../interfaces/qlcioplugin.h + spiconfiguration.cpp spiconfiguration.h spiconfiguration.ui + spioutthread.cpp spioutthread.h + spiplugin.cpp spiplugin.h +) +target_include_directories(${module_name} PRIVATE + $SYSROOT/usr/include + ../interfaces +) + +target_link_libraries(${module_name} PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Widgets +) + +install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/z65-spi.rules" + DESTINATION ${UDEVRULESDIR}) + +install(TARGETS ${module_name} + LIBRARY DESTINATION ${INSTALLROOT}/${PLUGINDIR} + RUNTIME DESTINATION ${INSTALLROOT}/${PLUGINDIR} +) + +if (UNIX AND NOT APPLE) + install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/org.qlcplus.QLCPlus.spi.metainfo.xml" + DESTINATION ${METAINFODIR}) +endif() diff --git a/plugins/spi/spiconfiguration.cpp b/plugins/spi/spiconfiguration.cpp index 7e23db334e..c5f63c7048 100644 --- a/plugins/spi/spiconfiguration.cpp +++ b/plugins/spi/spiconfiguration.cpp @@ -23,6 +23,8 @@ #include "spiconfiguration.h" #include "spiplugin.h" +#define SETTINGS_GEOMETRY "spiconfiguration/geometry" + /***************************************************************************** * Initialization *****************************************************************************/ @@ -37,7 +39,7 @@ SPIConfiguration::SPIConfiguration(SPIPlugin* plugin, QWidget* parent) setupUi(this); QSettings settings; - QVariant value = settings.value("SPIPlugin/frequency"); + QVariant value = settings.value(SETTINGS_OUTPUT_FREQUENCY); if (value.isValid() == true) { int speed = value.toUInt(); @@ -48,10 +50,15 @@ SPIConfiguration::SPIConfiguration(SPIPlugin* plugin, QWidget* parent) case 8000000: m_freqCombo->setCurrentIndex(3); break; } } + QVariant geometrySettings = settings.value(SETTINGS_GEOMETRY); + if (geometrySettings.isValid() == true) + restoreGeometry(geometrySettings.toByteArray()); } SPIConfiguration::~SPIConfiguration() { + QSettings settings; + settings.setValue(SETTINGS_GEOMETRY, saveGeometry()); } /***************************************************************************** diff --git a/plugins/spi/spioutthread.cpp b/plugins/spi/spioutthread.cpp index e0f6bd80c1..079224cb06 100644 --- a/plugins/spi/spioutthread.cpp +++ b/plugins/spi/spioutthread.cpp @@ -48,15 +48,15 @@ void SPIOutThread::runThread(int fd, int speed) int status = -1; status = ioctl (m_spifd, SPI_IOC_WR_MODE, &mode); - if(status < 0) + if (status < 0) qWarning() << "Could not set SPIMode (WR)...ioctl fail"; status = ioctl (m_spifd, SPI_IOC_WR_BITS_PER_WORD, &m_bitsPerWord); - if(status < 0) + if (status < 0) qWarning() << "Could not set SPI bitsPerWord (WR)...ioctl fail"; status = ioctl (m_spifd, SPI_IOC_WR_MAX_SPEED_HZ, &m_speed); - if(status < 0) + if (status < 0) qWarning() << "Could not set SPI speed (WR)...ioctl fail"; m_isRunning = true; @@ -129,7 +129,7 @@ void SPIOutThread::writeData(const QByteArray &data) m_pluginData = data; if (m_dataSize != data.size()) { - // Data size has changed ! I need to estimate the + // Data size has changed! I need to estimate the // time that the SPI writes will take on the wire. // The estimation is very unprecise and it is based // on Simon Newton's measurements on a Raspberry Pi diff --git a/plugins/spi/spiplugin.cpp b/plugins/spi/spiplugin.cpp index fe10144eb2..7809483534 100644 --- a/plugins/spi/spiplugin.cpp +++ b/plugins/spi/spiplugin.cpp @@ -79,7 +79,7 @@ bool SPIPlugin::openOutput(quint32 output, quint32 universe) return true; m_spifd = open(SPI_DEFAULT_DEVICE, O_RDWR); - if(m_spifd < 0) + if (m_spifd < 0) { qWarning() << "Cannot open SPI device!"; return false; @@ -87,7 +87,7 @@ bool SPIPlugin::openOutput(quint32 output, quint32 universe) QSettings settings; int speed = 1000000; - QVariant value = settings.value("SPIPlugin/frequency"); + QVariant value = settings.value(SETTINGS_OUTPUT_FREQUENCY); if (value.isValid() == true) speed = value.toUInt(); @@ -181,8 +181,10 @@ QString SPIPlugin::outputInfo(quint32 output) return str; } -void SPIPlugin::writeUniverse(quint32 universe, quint32 output, const QByteArray &data) +void SPIPlugin::writeUniverse(quint32 universe, quint32 output, const QByteArray &data, bool dataChanged) { + Q_UNUSED(dataChanged) + if (output != 0 || m_spifd == -1) return; @@ -223,7 +225,7 @@ void SPIPlugin::configure() if (conf.exec() == QDialog::Accepted) { QSettings settings; - settings.setValue("SPIPlugin/frequency", QVariant(conf.frequency())); + settings.setValue(SETTINGS_OUTPUT_FREQUENCY, QVariant(conf.frequency())); if (m_outThread != NULL) m_outThread->setSpeed(conf.frequency()); } diff --git a/plugins/spi/spiplugin.h b/plugins/spi/spiplugin.h index b24c1c79b2..797c399f15 100644 --- a/plugins/spi/spiplugin.h +++ b/plugins/spi/spiplugin.h @@ -27,6 +27,8 @@ #include "qlcioplugin.h" +#define SETTINGS_OUTPUT_FREQUENCY "SPIPlugin/frequency" + typedef struct { /** number of channels used in a universe */ @@ -86,7 +88,7 @@ class SPIPlugin : public QLCIOPlugin QString outputInfo(quint32 output); /** @reimp */ - void writeUniverse(quint32 universe, quint32 output, const QByteArray& data); + void writeUniverse(quint32 universe, quint32 output, const QByteArray& data, bool dataChanged); protected: /** File handle for /dev/spidev0.0 */ diff --git a/plugins/uart/CMakeLists.txt b/plugins/uart/CMakeLists.txt new file mode 100644 index 0000000000..916cf23a3a --- /dev/null +++ b/plugins/uart/CMakeLists.txt @@ -0,0 +1,49 @@ +project(uart) + +find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Gui SerialPort Widgets) + +set(module_name "uart") + +set(TS_FILES + UART_de_DE.ts + UART_es_ES.ts + UART_fi_FI.ts + UART_fr_FR.ts + UART_it_IT.ts + UART_nl_NL.ts + UART_cz_CZ.ts + UART_pt_BR.ts + UART_ca_ES.ts + UART_ja_JP.ts +) + +if(QT_VERSION_MAJOR GREATER 5) + qt_add_translation(QM_FILES ${TS_FILES}) +else() + qt5_add_translation(QM_FILES ${TS_FILES}) +endif() + +add_library(${module_name} SHARED + ${QM_FILES} +) + +target_sources(${module_name} PRIVATE + ../interfaces/qlcioplugin.cpp ../interfaces/qlcioplugin.h + uartplugin.cpp uartplugin.h + uartwidget.cpp uartwidget.h +) +target_include_directories(${module_name} PRIVATE + ../interfaces +) + +target_link_libraries(${module_name} PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::SerialPort + Qt${QT_MAJOR_VERSION}::Widgets +) + +install(TARGETS ${module_name} + LIBRARY DESTINATION ${INSTALLROOT}/${PLUGINDIR} + RUNTIME DESTINATION ${INSTALLROOT}/${PLUGINDIR} +) diff --git a/plugins/uart/uartplugin.cpp b/plugins/uart/uartplugin.cpp index 72c80d8c38..d6fbd8918b 100644 --- a/plugins/uart/uartplugin.cpp +++ b/plugins/uart/uartplugin.cpp @@ -127,9 +127,10 @@ QString UARTPlugin::outputInfo(quint32 output) return str; } -void UARTPlugin::writeUniverse(quint32 universe, quint32 output, const QByteArray &data) +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 a4e1974a49..1db50cc366 100644 --- a/plugins/uart/uartplugin.h +++ b/plugins/uart/uartplugin.h @@ -68,7 +68,7 @@ class UARTPlugin : public QLCIOPlugin QString outputInfo(quint32 output); /** @reimp */ - void writeUniverse(quint32 universe, quint32 output, const QByteArray& data); + void writeUniverse(quint32 universe, quint32 output, const QByteArray& data, bool dataChanged); /************************************************************************* * Inputs @@ -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 f187d85d3e..1e5a1522d4 100644 --- a/plugins/uart/uartwidget.cpp +++ b/plugins/uart/uartwidget.cpp @@ -18,7 +18,7 @@ */ #include -#include +#include #include #include @@ -70,7 +70,7 @@ bool UARTWidget::close(UARTWidget::WidgetMode mode) void UARTWidget::updateMode() { - if(m_mode != Closed && m_running == false) + if (m_mode != Closed && m_running == false) start(); else if (m_mode == Closed && m_running == true) stop(); @@ -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) @@ -155,10 +155,10 @@ void UARTWidget::run() if (m_mode & Output) { m_serialPort->setBreakEnabled(true); - if(m_granularity == Good) + if (m_granularity == Good) usleep(DMX_BREAK); m_serialPort->setBreakEnabled(false); - if(m_granularity == Good) + if (m_granularity == Good) usleep(DMX_MAB); if (m_serialPort->write(m_outputBuffer) == 0) diff --git a/plugins/udmx/CMakeLists.txt b/plugins/udmx/CMakeLists.txt new file mode 100644 index 0000000000..dd0c1bf08f --- /dev/null +++ b/plugins/udmx/CMakeLists.txt @@ -0,0 +1,3 @@ +project(udmx VERSION 1.0 LANGUAGES C CXX) + +add_subdirectory(src) diff --git a/plugins/udmx/src/CMakeLists.txt b/plugins/udmx/src/CMakeLists.txt new file mode 100644 index 0000000000..c10320ecfd --- /dev/null +++ b/plugins/udmx/src/CMakeLists.txt @@ -0,0 +1,62 @@ +find_package(PkgConfig REQUIRED) + +set(module_name "udmx") + +set(TS_FILES + uDMX_fi_FI.ts + uDMX_de_DE.ts + uDMX_es_ES.ts + uDMX_fr_FR.ts + uDMX_it_IT.ts + uDMX_nl_NL.ts + uDMX_cz_CZ.ts + uDMX_pt_BR.ts + uDMX_ca_ES.ts + uDMX_ja_JP.ts +) + +if(QT_VERSION_MAJOR GREATER 5) + qt_add_translation(QM_FILES ${TS_FILES}) +else() + qt5_add_translation(QM_FILES ${TS_FILES}) +endif() + +add_library(${module_name} SHARED ${QM_FILES}) + +target_sources(${module_name} PRIVATE + ../../interfaces/qlcioplugin.cpp ../../interfaces/qlcioplugin.h +) + +target_include_directories(${module_name} PRIVATE + ../../interfaces +) + +target_link_libraries(${module_name} PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Widgets +) + +target_sources(${module_name} PRIVATE + ${module_name}.cpp ${module_name}.h + udmxdevice.cpp udmxdevice.h +) + +pkg_check_modules(LIBUSB1 IMPORTED_TARGET libusb-1.0) + +if(${LIBUSB1_FOUND}) + target_include_directories(${module_name} PRIVATE ${LIBUSB_1_INCLUDE_DIRS}) + target_link_libraries(${module_name} PRIVATE ${LIBUSB_1_LINK_LIBRARIES}) +endif() + +install(TARGETS ${module_name} + LIBRARY DESTINATION ${INSTALLROOT}/${PLUGINDIR} + RUNTIME DESTINATION ${INSTALLROOT}/${PLUGINDIR} +) + +if (UNIX AND NOT APPLE) + install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/z65-anyma-udmx.rules" + DESTINATION ${UDEVRULESDIR}) + install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/org.qlcplus.QLCPlus.udmx.metainfo.xml" + DESTINATION ${METAINFODIR}) +endif() diff --git a/plugins/udmx/src/libusb_dyn.c b/plugins/udmx/src/libusb_dyn.c deleted file mode 100644 index 679d1f08dd..0000000000 --- a/plugins/udmx/src/libusb_dyn.c +++ /dev/null @@ -1,485 +0,0 @@ -/* - LIBUSB-WIN32, Generic Windows USB Library - - Copyright (c) 2002-2005 Stephan Meyer - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -#include -#include - -#include "libusb_dyn.h" - -#define LIBUSB_DLL_NAME TEXT("libusb0.dll") - -typedef usb_dev_handle * (*usb_open_t)(struct usb_device *dev); -typedef int (*usb_close_t)(usb_dev_handle *dev); -typedef int (*usb_get_string_t)(usb_dev_handle *dev, int index, int langid, - char *buf, size_t buflen); -typedef int (*usb_get_string_simple_t)(usb_dev_handle *dev, int index, - char *buf, size_t buflen); -typedef int (*usb_get_descriptor_by_endpoint_t)(usb_dev_handle *udev, int ep, - unsigned char type, - unsigned char index, - void *buf, int size); -typedef int (*usb_get_descriptor_t)(usb_dev_handle *udev, unsigned char type, - unsigned char index, void *buf, int size); -typedef int (*usb_bulk_write_t)(usb_dev_handle *dev, int ep, char *bytes, - int size, int timeout); -typedef int (*usb_bulk_read_t)(usb_dev_handle *dev, int ep, char *bytes, - int size, int timeout); -typedef int (*usb_interrupt_write_t)(usb_dev_handle *dev, int ep, char *bytes, - int size, int timeout); -typedef int (*usb_interrupt_read_t)(usb_dev_handle *dev, int ep, char *bytes, - int size, int timeout); -typedef int (*usb_control_msg_t)(usb_dev_handle *dev, int requesttype, - int request, int value, int index, - char *bytes, int size, int timeout); -typedef int (*usb_set_configuration_t)(usb_dev_handle *dev, int configuration); -typedef int (*usb_claim_interface_t)(usb_dev_handle *dev, int interface); -typedef int (*usb_release_interface_t)(usb_dev_handle *dev, int interface); -typedef int (*usb_set_altinterface_t)(usb_dev_handle *dev, int alternate); -typedef int (*usb_resetep_t)(usb_dev_handle *dev, unsigned int ep); -typedef int (*usb_clear_halt_t)(usb_dev_handle *dev, unsigned int ep); -typedef int (*usb_reset_t)(usb_dev_handle *dev); -typedef char * (*usb_strerror_t)(void); -typedef void (*usb_init_t)(void); -typedef void (*usb_set_debug_t)(int level); -typedef int (*usb_find_busses_t)(void); -typedef int (*usb_find_devices_t)(void); -typedef struct usb_device * (*usb_device_t)(usb_dev_handle *dev); -typedef struct usb_bus * (*usb_get_busses_t)(void); -typedef int (*usb_install_service_np_t)(void); -typedef int (*usb_uninstall_service_np_t)(void); -typedef int (*usb_install_driver_np_t)(const char *inf_file); -typedef const struct usb_version * (*usb_get_version_t)(void); -typedef int (*usb_isochronous_setup_async_t)(usb_dev_handle *dev, - void **context, - unsigned char ep, int pktsize); -typedef int (*usb_bulk_setup_async_t)(usb_dev_handle *dev, void **context, - unsigned char ep); -typedef int (*usb_interrupt_setup_async_t)(usb_dev_handle *dev, void **context, - unsigned char ep); -typedef int (*usb_submit_async_t)(void *context, char *bytes, int size); -typedef int (*usb_reap_async_t)(void *context, int timeout); -typedef int (*usb_free_async_t)(void **context); - - -static usb_open_t _usb_open = NULL; -static usb_close_t _usb_close = NULL; -static usb_get_string_t _usb_get_string = NULL; -static usb_get_string_simple_t _usb_get_string_simple = NULL; -static usb_get_descriptor_by_endpoint_t _usb_get_descriptor_by_endpoint = NULL; -static usb_get_descriptor_t _usb_get_descriptor = NULL; -static usb_bulk_write_t _usb_bulk_write = NULL; -static usb_bulk_read_t _usb_bulk_read = NULL; -static usb_interrupt_write_t _usb_interrupt_write = NULL; -static usb_interrupt_read_t _usb_interrupt_read = NULL; -static usb_control_msg_t _usb_control_msg = NULL; -static usb_set_configuration_t _usb_set_configuration = NULL; -static usb_claim_interface_t _usb_claim_interface = NULL; -static usb_release_interface_t _usb_release_interface = NULL; -static usb_set_altinterface_t _usb_set_altinterface = NULL; -static usb_resetep_t _usb_resetep = NULL; -static usb_clear_halt_t _usb_clear_halt = NULL; -static usb_reset_t _usb_reset = NULL; -static usb_strerror_t _usb_strerror = NULL; -static usb_init_t _usb_init = NULL; -static usb_set_debug_t _usb_set_debug = NULL; -static usb_find_busses_t _usb_find_busses = NULL; -static usb_find_devices_t _usb_find_devices = NULL; -static usb_device_t _usb_device = NULL; -static usb_get_busses_t _usb_get_busses = NULL; -static usb_install_service_np_t _usb_install_service_np = NULL; -static usb_uninstall_service_np_t _usb_uninstall_service_np = NULL; -static usb_install_driver_np_t _usb_install_driver_np = NULL; -static usb_get_version_t _usb_get_version = NULL; -static usb_isochronous_setup_async_t _usb_isochronous_setup_async = NULL; -static usb_bulk_setup_async_t _usb_bulk_setup_async = NULL; -static usb_interrupt_setup_async_t _usb_interrupt_setup_async = NULL; -static usb_submit_async_t _usb_submit_async = NULL; -static usb_reap_async_t _usb_reap_async = NULL; -static usb_free_async_t _usb_free_async = NULL; - - - - -void usb_init(void) -{ - HINSTANCE libusb_dll = LoadLibrary(LIBUSB_DLL_NAME); - - if(!libusb_dll) - return; - - _usb_open = (usb_open_t) - GetProcAddress(libusb_dll, "usb_open"); - _usb_close = (usb_close_t) - GetProcAddress(libusb_dll, "usb_close"); - _usb_get_string = (usb_get_string_t) - GetProcAddress(libusb_dll, "usb_get_string"); - _usb_get_string_simple = (usb_get_string_simple_t) - GetProcAddress(libusb_dll, "usb_get_string_simple"); - _usb_get_descriptor_by_endpoint = (usb_get_descriptor_by_endpoint_t) - GetProcAddress(libusb_dll, "usb_get_descriptor_by_endpoint"); - _usb_get_descriptor = (usb_get_descriptor_t) - GetProcAddress(libusb_dll, "usb_get_descriptor"); - _usb_bulk_write = (usb_bulk_write_t) - GetProcAddress(libusb_dll, "usb_bulk_write"); - _usb_bulk_read = (usb_bulk_read_t) - GetProcAddress(libusb_dll, "usb_bulk_read"); - _usb_interrupt_write = (usb_interrupt_write_t) - GetProcAddress(libusb_dll, "usb_interrupt_write"); - _usb_interrupt_read = (usb_interrupt_read_t) - GetProcAddress(libusb_dll, "usb_interrupt_read"); - _usb_control_msg = (usb_control_msg_t) - GetProcAddress(libusb_dll, "usb_control_msg"); - _usb_set_configuration = (usb_set_configuration_t) - GetProcAddress(libusb_dll, "usb_set_configuration"); - _usb_claim_interface = (usb_claim_interface_t) - GetProcAddress(libusb_dll, "usb_claim_interface"); - _usb_release_interface = (usb_release_interface_t) - GetProcAddress(libusb_dll, "usb_release_interface"); - _usb_set_altinterface = (usb_set_altinterface_t) - GetProcAddress(libusb_dll, "usb_set_altinterface"); - _usb_resetep = (usb_resetep_t) - GetProcAddress(libusb_dll, "usb_resetep"); - _usb_clear_halt = (usb_clear_halt_t) - GetProcAddress(libusb_dll, "usb_clear_halt"); - _usb_reset = (usb_reset_t) - GetProcAddress(libusb_dll, "usb_reset"); - _usb_strerror = (usb_strerror_t) - GetProcAddress(libusb_dll, "usb_strerror"); - _usb_init = (usb_init_t) - GetProcAddress(libusb_dll, "usb_init"); - _usb_set_debug = (usb_set_debug_t) - GetProcAddress(libusb_dll, "usb_set_debug"); - _usb_find_busses = (usb_find_busses_t) - GetProcAddress(libusb_dll, "usb_find_busses"); - _usb_find_devices = (usb_find_devices_t) - GetProcAddress(libusb_dll, "usb_find_devices"); - _usb_device = (usb_device_t) - GetProcAddress(libusb_dll, "usb_device"); - _usb_get_busses = (usb_get_busses_t) - GetProcAddress(libusb_dll, "usb_get_busses"); - _usb_install_service_np = (usb_install_service_np_t) - GetProcAddress(libusb_dll, "usb_install_service_np"); - _usb_uninstall_service_np = (usb_uninstall_service_np_t) - GetProcAddress(libusb_dll, "usb_uninstall_service_np"); - _usb_install_driver_np = (usb_install_driver_np_t) - GetProcAddress(libusb_dll, "usb_install_driver_np"); - _usb_get_version = (usb_get_version_t) - GetProcAddress(libusb_dll, "usb_get_version"); - _usb_isochronous_setup_async = (usb_isochronous_setup_async_t) - GetProcAddress(libusb_dll, "usb_isochronous_setup_async"); - _usb_bulk_setup_async = (usb_bulk_setup_async_t) - GetProcAddress(libusb_dll, "usb_bulk_setup_async"); - _usb_interrupt_setup_async = (usb_interrupt_setup_async_t) - GetProcAddress(libusb_dll, "usb_interrupt_setup_async"); - _usb_submit_async = (usb_submit_async_t) - GetProcAddress(libusb_dll, "usb_submit_async"); - _usb_reap_async = (usb_reap_async_t) - GetProcAddress(libusb_dll, "usb_reap_async"); - _usb_free_async = (usb_free_async_t) - GetProcAddress(libusb_dll, "usb_free_async"); - - if(_usb_init) - _usb_init(); -} - -usb_dev_handle *usb_open(struct usb_device *dev) -{ - if(_usb_open) - return _usb_open(dev); - else - return NULL; -} - -int usb_close(usb_dev_handle *dev) -{ - if(_usb_close) - return _usb_close(dev); - else - return -ENOFILE; -} - -int usb_get_string(usb_dev_handle *dev, int index, int langid, char *buf, - size_t buflen) -{ - if(_usb_get_string) - return _usb_get_string(dev, index, langid, buf, buflen); - else - return -ENOFILE; -} - -int usb_get_string_simple(usb_dev_handle *dev, int index, char *buf, - size_t buflen) -{ - if(_usb_get_string_simple) - return _usb_get_string_simple(dev, index, buf, buflen); - else - return -ENOFILE; -} - -int usb_get_descriptor_by_endpoint(usb_dev_handle *udev, int ep, - unsigned char type, unsigned char index, - void *buf, int size) -{ - if(_usb_get_descriptor_by_endpoint) - return _usb_get_descriptor_by_endpoint(udev, ep, type, index, buf, size); - else - return -ENOFILE; -} - -int usb_get_descriptor(usb_dev_handle *udev, unsigned char type, - unsigned char index, void *buf, int size) -{ - if(_usb_get_descriptor) - return _usb_get_descriptor(udev, type, index, buf, size); - else - return -ENOFILE; -} - -int usb_bulk_write(usb_dev_handle *dev, int ep, char *bytes, int size, - int timeout) -{ - if(_usb_bulk_write) - return _usb_bulk_write(dev, ep, bytes, size, timeout); - else - return -ENOFILE; -} - -int usb_bulk_read(usb_dev_handle *dev, int ep, char *bytes, int size, - int timeout) -{ - if(_usb_bulk_read) - return _usb_bulk_read(dev, ep, bytes, size, timeout); - else - return -ENOFILE; -} - -int usb_interrupt_write(usb_dev_handle *dev, int ep, char *bytes, int size, - int timeout) -{ - if(_usb_interrupt_write) - return _usb_interrupt_write(dev, ep, bytes, size, timeout); - else - return -ENOFILE; -} - -int usb_interrupt_read(usb_dev_handle *dev, int ep, char *bytes, int size, - int timeout) -{ - if(_usb_interrupt_read) - return _usb_interrupt_read(dev, ep, bytes, size, timeout); - else - return -ENOFILE; -} - -int usb_control_msg(usb_dev_handle *dev, int requesttype, int request, - int value, int index, char *bytes, int size, - int timeout) -{ - if(_usb_control_msg) - return _usb_control_msg(dev, requesttype, request, value, index, bytes, - size, timeout); - else - return -ENOFILE; -} - -int usb_set_configuration(usb_dev_handle *dev, int configuration) -{ - if(_usb_set_configuration) - return _usb_set_configuration(dev, configuration); - else - return -ENOFILE; -} - -int usb_claim_interface(usb_dev_handle *dev, int interface) -{ - if(_usb_claim_interface) - return _usb_claim_interface(dev, interface); - else - return -ENOFILE; -} - -int usb_release_interface(usb_dev_handle *dev, int interface) -{ - if(_usb_release_interface) - return _usb_release_interface(dev, interface); - else - return -ENOFILE; -} - -int usb_set_altinterface(usb_dev_handle *dev, int alternate) -{ - if(_usb_set_altinterface) - return _usb_set_altinterface(dev, alternate); - else - return -ENOFILE; -} - -int usb_resetep(usb_dev_handle *dev, unsigned int ep) -{ - if(_usb_resetep) - return _usb_resetep(dev, ep); - else - return -ENOFILE; -} - -int usb_clear_halt(usb_dev_handle *dev, unsigned int ep) -{ - if(_usb_clear_halt) - return _usb_clear_halt(dev, ep); - else - return -ENOFILE; -} - -int usb_reset(usb_dev_handle *dev) -{ - if(_usb_reset) - return _usb_reset(dev); - else - return -ENOFILE; -} - -char *usb_strerror(void) -{ - if(_usb_strerror) - return _usb_strerror(); - else - return NULL; -} - -void usb_set_debug(int level) -{ - if(_usb_set_debug) - return _usb_set_debug(level); -} - -int usb_find_busses(void) -{ - if(_usb_find_busses) - return _usb_find_busses(); - else - return -ENOFILE; -} - -int usb_find_devices(void) -{ - if(_usb_find_devices) - return _usb_find_devices(); - else - return -ENOFILE; -} - -struct usb_device *usb_device(usb_dev_handle *dev) -{ - if(_usb_device) - return _usb_device(dev); - else - return NULL; -} - -struct usb_bus *usb_get_busses(void) -{ - if(_usb_get_busses) - return _usb_get_busses(); - else - return NULL; -} - -int usb_install_service_np(void) -{ - if(_usb_install_service_np) - return _usb_install_service_np(); - else - return -ENOFILE; -} - -int usb_uninstall_service_np(void) -{ - if(_usb_uninstall_service_np) - return _usb_uninstall_service_np(); - else - return -ENOFILE; -} - -int usb_install_driver_np(const char *inf_file) -{ - if(_usb_install_driver_np) - return _usb_install_driver_np(inf_file); - else - return -ENOFILE; -} - -const struct usb_version *usb_get_version(void) -{ - if(_usb_get_version) - return _usb_get_version(); - else - return NULL; -} - -int usb_isochronous_setup_async(usb_dev_handle *dev, void **context, - unsigned char ep, int pktsize) -{ - if(_usb_isochronous_setup_async) - return _usb_isochronous_setup_async(dev, context, ep, pktsize); - else - return -ENOFILE; -} - -int usb_bulk_setup_async(usb_dev_handle *dev, void **context, - unsigned char ep) -{ - if(_usb_bulk_setup_async) - return _usb_bulk_setup_async(dev, context, ep); - else - return -ENOFILE; -} - -int usb_interrupt_setup_async(usb_dev_handle *dev, void **context, - unsigned char ep) -{ - if(_usb_interrupt_setup_async) - return _usb_interrupt_setup_async(dev, context, ep); - else - return -ENOFILE; -} - -int usb_submit_async(void *context, char *bytes, int size) -{ - if(_usb_submit_async) - return _usb_submit_async(context, bytes, size); - else - return -ENOFILE; -} - -int usb_reap_async(void *context, int timeout) -{ - if(_usb_reap_async) - return _usb_reap_async(context, timeout); - else - return -ENOFILE; -} - -int usb_free_async(void **context) -{ - if(_usb_free_async) - return _usb_free_async(context); - else - return -ENOFILE; -} diff --git a/plugins/udmx/src/libusb_dyn.h b/plugins/udmx/src/libusb_dyn.h deleted file mode 100644 index b60b574ce0..0000000000 --- a/plugins/udmx/src/libusb_dyn.h +++ /dev/null @@ -1,413 +0,0 @@ -/* - LIBUSB-WIN32, Generic Windows USB Library - - Copyright (c) 2002-2005 Stephan Meyer - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -#ifndef __LIBUSB_DYN_H__ -#define __LIBUSB_DYN_H__ - -#include -#include - -/* - * 'interface' is defined somewhere in the Windows header files. This macro - * is deleted here to avoid conflicts and compile errors. - */ - -#ifdef interface -#undef interface -#endif - -/* - * PATH_MAX from limits.h can't be used on Windows if the dll and - * import libraries are build/used by different compilers - */ - -#define LIBUSB_PATH_MAX 512 - - -/* - * USB spec information - * - * This is all stuff grabbed from various USB specs and is pretty much - * not subject to change - */ - -/* - * Device and/or Interface Class codes - */ -#define USB_CLASS_PER_INTERFACE 0 /* for DeviceClass */ -#define USB_CLASS_AUDIO 1 -#define USB_CLASS_COMM 2 -#define USB_CLASS_HID 3 -#define USB_CLASS_PRINTER 7 -#define USB_CLASS_MASS_STORAGE 8 -#define USB_CLASS_HUB 9 -#define USB_CLASS_DATA 10 -#define USB_CLASS_VENDOR_SPEC 0xff - -/* - * Descriptor types - */ -#define USB_DT_DEVICE 0x01 -#define USB_DT_CONFIG 0x02 -#define USB_DT_STRING 0x03 -#define USB_DT_INTERFACE 0x04 -#define USB_DT_ENDPOINT 0x05 - -#define USB_DT_HID 0x21 -#define USB_DT_REPORT 0x22 -#define USB_DT_PHYSICAL 0x23 -#define USB_DT_HUB 0x29 - -/* - * Descriptor sizes per descriptor type - */ -#define USB_DT_DEVICE_SIZE 18 -#define USB_DT_CONFIG_SIZE 9 -#define USB_DT_INTERFACE_SIZE 9 -#define USB_DT_ENDPOINT_SIZE 7 -#define USB_DT_ENDPOINT_AUDIO_SIZE 9 /* Audio extension */ -#define USB_DT_HUB_NONVAR_SIZE 7 - - -/* ensure byte-packed structures */ -#include - - -/* All standard descriptors have these 2 fields in common */ -struct usb_descriptor_header { - unsigned char bLength; - unsigned char bDescriptorType; -}; - -/* String descriptor */ -struct usb_string_descriptor { - unsigned char bLength; - unsigned char bDescriptorType; - unsigned short wData[1]; -}; - -/* HID descriptor */ -struct usb_hid_descriptor { - unsigned char bLength; - unsigned char bDescriptorType; - unsigned short bcdHID; - unsigned char bCountryCode; - unsigned char bNumDescriptors; -}; - -/* Endpoint descriptor */ -#define USB_MAXENDPOINTS 32 -struct usb_endpoint_descriptor { - unsigned char bLength; - unsigned char bDescriptorType; - unsigned char bEndpointAddress; - unsigned char bmAttributes; - unsigned short wMaxPacketSize; - unsigned char bInterval; - unsigned char bRefresh; - unsigned char bSynchAddress; - - unsigned char *extra; /* Extra descriptors */ - int extralen; -}; - -#define USB_ENDPOINT_ADDRESS_MASK 0x0f /* in bEndpointAddress */ -#define USB_ENDPOINT_DIR_MASK 0x80 - -#define USB_ENDPOINT_TYPE_MASK 0x03 /* in bmAttributes */ -#define USB_ENDPOINT_TYPE_CONTROL 0 -#define USB_ENDPOINT_TYPE_ISOCHRONOUS 1 -#define USB_ENDPOINT_TYPE_BULK 2 -#define USB_ENDPOINT_TYPE_INTERRUPT 3 - -/* Interface descriptor */ -#define USB_MAXINTERFACES 32 -struct usb_interface_descriptor { - unsigned char bLength; - unsigned char bDescriptorType; - unsigned char bInterfaceNumber; - unsigned char bAlternateSetting; - unsigned char bNumEndpoints; - unsigned char bInterfaceClass; - unsigned char bInterfaceSubClass; - unsigned char bInterfaceProtocol; - unsigned char iInterface; - - struct usb_endpoint_descriptor *endpoint; - - unsigned char *extra; /* Extra descriptors */ - int extralen; -}; - -#define USB_MAXALTSETTING 128 /* Hard limit */ - -struct usb_interface { - struct usb_interface_descriptor *altsetting; - - int num_altsetting; -}; - -/* Configuration descriptor information.. */ -#define USB_MAXCONFIG 8 -struct usb_config_descriptor { - unsigned char bLength; - unsigned char bDescriptorType; - unsigned short wTotalLength; - unsigned char bNumInterfaces; - unsigned char bConfigurationValue; - unsigned char iConfiguration; - unsigned char bmAttributes; - unsigned char MaxPower; - - struct usb_interface *interface; - - unsigned char *extra; /* Extra descriptors */ - int extralen; -}; - -/* Device descriptor */ -struct usb_device_descriptor { - unsigned char bLength; - unsigned char bDescriptorType; - unsigned short bcdUSB; - unsigned char bDeviceClass; - unsigned char bDeviceSubClass; - unsigned char bDeviceProtocol; - unsigned char bMaxPacketSize0; - unsigned short idVendor; - unsigned short idProduct; - unsigned short bcdDevice; - unsigned char iManufacturer; - unsigned char iProduct; - unsigned char iSerialNumber; - unsigned char bNumConfigurations; -}; - -struct usb_ctrl_setup { - unsigned char bRequestType; - unsigned char bRequest; - unsigned short wValue; - unsigned short wIndex; - unsigned short wLength; -}; - -/* - * Standard requests - */ -#define USB_REQ_GET_STATUS 0x00 -#define USB_REQ_CLEAR_FEATURE 0x01 -/* 0x02 is reserved */ -#define USB_REQ_SET_FEATURE 0x03 -/* 0x04 is reserved */ -#define USB_REQ_SET_ADDRESS 0x05 -#define USB_REQ_GET_DESCRIPTOR 0x06 -#define USB_REQ_SET_DESCRIPTOR 0x07 -#define USB_REQ_GET_CONFIGURATION 0x08 -#define USB_REQ_SET_CONFIGURATION 0x09 -#define USB_REQ_GET_INTERFACE 0x0A -#define USB_REQ_SET_INTERFACE 0x0B -#define USB_REQ_SYNCH_FRAME 0x0C - -#define USB_TYPE_STANDARD (0x00 << 5) -#define USB_TYPE_CLASS (0x01 << 5) -#define USB_TYPE_VENDOR (0x02 << 5) -#define USB_TYPE_RESERVED (0x03 << 5) - -#define USB_RECIP_DEVICE 0x00 -#define USB_RECIP_INTERFACE 0x01 -#define USB_RECIP_ENDPOINT 0x02 -#define USB_RECIP_OTHER 0x03 - -/* - * Various libusb API related stuff - */ - -#define USB_ENDPOINT_IN 0x80 -#define USB_ENDPOINT_OUT 0x00 - -/* Error codes */ -#define USB_ERROR_BEGIN 500000 - -/* - * This is supposed to look weird. This file is generated from autoconf - * and I didn't want to make this too complicated. - */ -#define USB_LE16_TO_CPU(x) - -/* Data types */ -/* struct usb_device; */ -/* struct usb_bus; */ - -struct usb_device { - struct usb_device *next, *prev; - - char filename[LIBUSB_PATH_MAX]; - - struct usb_bus *bus; - - struct usb_device_descriptor descriptor; - struct usb_config_descriptor *config; - - void *dev; /* Darwin support */ - - unsigned char devnum; - - unsigned char num_children; - struct usb_device **children; -}; - -struct usb_bus { - struct usb_bus *next, *prev; - - char dirname[LIBUSB_PATH_MAX]; - - struct usb_device *devices; - unsigned long location; - - struct usb_device *root_dev; -}; - -/* Version information, Windows specific */ -struct usb_version { - struct { - int major; - int minor; - int micro; - int nano; - } dll; - struct { - int major; - int minor; - int micro; - int nano; - } driver; -}; - - -struct usb_dev_handle; -typedef struct usb_dev_handle usb_dev_handle; - -/* Variables */ -#ifndef __USB_C__ -#define usb_busses usb_get_busses() -#endif - - - -#include - - -#ifdef __cplusplus -extern "C" { -#endif - - /* Function prototypes */ - - /* usb.c */ - usb_dev_handle *usb_open(struct usb_device *dev); - int usb_close(usb_dev_handle *dev); - int usb_get_string(usb_dev_handle *dev, int index, int langid, char *buf, - size_t buflen); - int usb_get_string_simple(usb_dev_handle *dev, int index, char *buf, - size_t buflen); - - /* descriptors.c */ - int usb_get_descriptor_by_endpoint(usb_dev_handle *udev, int ep, - unsigned char type, unsigned char index, - void *buf, int size); - int usb_get_descriptor(usb_dev_handle *udev, unsigned char type, - unsigned char index, void *buf, int size); - - /* .c */ - int usb_bulk_write(usb_dev_handle *dev, int ep, char *bytes, int size, - int timeout); - int usb_bulk_read(usb_dev_handle *dev, int ep, char *bytes, int size, - int timeout); - int usb_interrupt_write(usb_dev_handle *dev, int ep, char *bytes, int size, - int timeout); - int usb_interrupt_read(usb_dev_handle *dev, int ep, char *bytes, int size, - int timeout); - int usb_control_msg(usb_dev_handle *dev, int requesttype, int request, - int value, int index, char *bytes, int size, - int timeout); - int usb_set_configuration(usb_dev_handle *dev, int configuration); - int usb_claim_interface(usb_dev_handle *dev, int interface); - int usb_release_interface(usb_dev_handle *dev, int interface); - int usb_set_altinterface(usb_dev_handle *dev, int alternate); - int usb_resetep(usb_dev_handle *dev, unsigned int ep); - int usb_clear_halt(usb_dev_handle *dev, unsigned int ep); - int usb_reset(usb_dev_handle *dev); - - char *usb_strerror(void); - - void usb_init(void); - void usb_set_debug(int level); - int usb_find_busses(void); - int usb_find_devices(void); - struct usb_device *usb_device(usb_dev_handle *dev); - struct usb_bus *usb_get_busses(void); - - - /* Windows specific functions */ - -#define LIBUSB_HAS_INSTALL_SERVICE_NP 1 - int usb_install_service_np(void); - void CALLBACK usb_install_service_np_rundll(HWND wnd, HINSTANCE instance, - LPSTR cmd_line, int cmd_show); - -#define LIBUSB_HAS_UNINSTALL_SERVICE_NP 1 - int usb_uninstall_service_np(void); - void CALLBACK usb_uninstall_service_np_rundll(HWND wnd, HINSTANCE instance, - LPSTR cmd_line, int cmd_show); - -#define LIBUSB_HAS_INSTALL_DRIVER_NP 1 - int usb_install_driver_np(const char *inf_file); - void CALLBACK usb_install_driver_np_rundll(HWND wnd, HINSTANCE instance, - LPSTR cmd_line, int cmd_show); - -#define LIBUSB_HAS_TOUCH_INF_FILE_NP 1 - int usb_touch_inf_file_np(const char *inf_file); - void CALLBACK usb_touch_inf_file_np_rundll(HWND wnd, HINSTANCE instance, - LPSTR cmd_line, int cmd_show); - -#define LIBUSB_HAS_INSTALL_NEEDS_RESTART_NP 1 - int usb_install_needs_restart_np(void); - - const struct usb_version *usb_get_version(void); - - int usb_isochronous_setup_async(usb_dev_handle *dev, void **context, - unsigned char ep, int pktsize); - int usb_bulk_setup_async(usb_dev_handle *dev, void **context, - unsigned char ep); - int usb_interrupt_setup_async(usb_dev_handle *dev, void **context, - unsigned char ep); - - int usb_submit_async(void *context, char *bytes, int size); - int usb_reap_async(void *context, int timeout); - int usb_reap_async_nocancel(void *context, int timeout); - int usb_cancel_async(void *context); - int usb_free_async(void **context); - - -#ifdef __cplusplus -} -#endif - -#endif /* __LIBUSB_DYN_H__ */ diff --git a/plugins/udmx/src/src.pro b/plugins/udmx/src/src.pro index acf3fe0b99..1595a30548 100644 --- a/plugins/udmx/src/src.pro +++ b/plugins/udmx/src/src.pro @@ -22,11 +22,6 @@ SOURCES += ../../interfaces/qlcioplugin.cpp SOURCES += udmxdevice.cpp \ udmx.cpp -win32 { - HEADERS += libusb_dyn.h - SOURCES += libusb_dyn.c -} - TRANSLATIONS += uDMX_fi_FI.ts TRANSLATIONS += uDMX_de_DE.ts TRANSLATIONS += uDMX_es_ES.ts diff --git a/plugins/udmx/src/uDMX_ca_ES.ts b/plugins/udmx/src/uDMX_ca_ES.ts index dd5a2bf41e..61bfdea504 100644 --- a/plugins/udmx/src/uDMX_ca_ES.ts +++ b/plugins/udmx/src/uDMX_ca_ES.ts @@ -4,12 +4,12 @@ UDMX - + This plugin provides DMX output support for Anyma uDMX devices. Aquest plugin proveeix suport de sortida DMX per dispositius Anyma uDMX. - + Do you wish to re-scan your hardware? Vol reescanejar el seu maquinari? @@ -17,49 +17,49 @@ UDMXDevice - + Unknown Desconegut - + + Device name + Nom del dispositiu + + + DMX Channels - + Canals DMX - + DMX Frame Frequency Freqüència DMX - + Bad Dolent - + Good Bo - + Patch this device to a universe to find out. Assigni aquest dispositiu a un univers per trobarlo. - + System Timer Accuracy Precisió del rellotge del sistema - - Unknown device - Dispositiu desconegut - - - - Cannot connect to USB device. - No es pot connectar al dispositiu USB. + + Device not in use + El dispositiu no està en ús
diff --git a/plugins/udmx/src/uDMX_cz_CZ.ts b/plugins/udmx/src/uDMX_cz_CZ.ts index 64968cca18..cba90a47ab 100644 --- a/plugins/udmx/src/uDMX_cz_CZ.ts +++ b/plugins/udmx/src/uDMX_cz_CZ.ts @@ -4,12 +4,12 @@ UDMX - + This plugin provides DMX output support for Anyma uDMX devices. Tento plugin přidává podporu DMX výstupu pro Anyma uDMX zařízení. - + Do you wish to re-scan your hardware? Přejete si znovu prohledat Váš hardware? @@ -17,49 +17,49 @@ UDMXDevice - + Unknown Neznámý - + + Device name + + + + DMX Channels - + DMX Frame Frequency Frekvence DMX rámce - + Bad Špatné - + Good Dobré - + Patch this device to a universe to find out. Připojit toto zařízení pro vyhledání ve větvi. - + System Timer Accuracy Přesnost systémového času - - Unknown device - Neznámé zařízení - - - - Cannot connect to USB device. - Nelze se připojit k USB zařízení. + + Device not in use +
diff --git a/plugins/udmx/src/uDMX_de_DE.ts b/plugins/udmx/src/uDMX_de_DE.ts index d2d827bda6..65ea7460a3 100644 --- a/plugins/udmx/src/uDMX_de_DE.ts +++ b/plugins/udmx/src/uDMX_de_DE.ts @@ -4,12 +4,12 @@ UDMX - + This plugin provides DMX output support for Anyma uDMX devices. Dieses Plugin bietet DMX-Output für Anyma uDMX Geräte. - + Do you wish to re-scan your hardware? Nach neuer Hardware suchen? @@ -17,49 +17,49 @@ UDMXDevice - + Unknown Unbekannt - + + Device name + Gerätename + + + DMX Channels DMX Kanäle - + DMX Frame Frequency DMX-Rahmen-Frequenz - + Bad Schlecht - + Good Gut - + Patch this device to a universe to find out. Patche dieses Gerät zu einem herauszufindendem Universum. - + System Timer Accuracy System-Timer Genauigkeit - - Unknown device - Unbekanntes Gerät - - - - Cannot connect to USB device. - Kann nicht mit dem USB-Gerät verbinden. + + Device not in use + Gerät nicht in Verwendung diff --git a/plugins/udmx/src/uDMX_es_ES.ts b/plugins/udmx/src/uDMX_es_ES.ts index fba7671837..21241ac390 100644 --- a/plugins/udmx/src/uDMX_es_ES.ts +++ b/plugins/udmx/src/uDMX_es_ES.ts @@ -4,12 +4,12 @@ UDMX - + This plugin provides DMX output support for Anyma uDMX devices. Este plugin provee soporte de Salida DMX para dispositivos Anyma uDMX. - + Do you wish to re-scan your hardware? ¿Desea volver a escanear su hardware? @@ -17,49 +17,49 @@ UDMXDevice - + Unknown Desconocido - + + Device name + Nombre del dispositivo + + + DMX Channels - + Canal DMX - + DMX Frame Frequency Frecuencia de Frames DMX - + Bad Mal - + Good Bien - + Patch this device to a universe to find out. Patchear este dispositivo a un universo a encontrar. - + System Timer Accuracy Precisión del Reloj de Sistema - - Unknown device - Dispositivo desconocido - - - - Cannot connect to USB device. - No se puede conectar con dispositivo USB. + + Device not in use + El dispositivo no está en uso diff --git a/plugins/udmx/src/uDMX_fi_FI.ts b/plugins/udmx/src/uDMX_fi_FI.ts index e6204542c0..4116b4433d 100644 --- a/plugins/udmx/src/uDMX_fi_FI.ts +++ b/plugins/udmx/src/uDMX_fi_FI.ts @@ -4,12 +4,12 @@ UDMX - + This plugin provides DMX output support for Anyma uDMX devices. Tämä liitännäinen tuottaa DMX-ulostulotuen Anyma uDMX-laitteille. - + Do you wish to re-scan your hardware? Etsitäänkö lisää laitteita? @@ -17,49 +17,49 @@ UDMXDevice - + Unknown Tuntematon - + + Device name + + + + DMX Channels - + DMX Frame Frequency - + Bad - + Good - + Patch this device to a universe to find out. - + System Timer Accuracy - - Unknown device - Tuntematon laite - - - - Cannot connect to USB device. - USB-laitteeseen ei saada yhteyttä. + + Device not in use + diff --git a/plugins/udmx/src/uDMX_fr_FR.ts b/plugins/udmx/src/uDMX_fr_FR.ts index 5aeae53be2..d69e0680a3 100644 --- a/plugins/udmx/src/uDMX_fr_FR.ts +++ b/plugins/udmx/src/uDMX_fr_FR.ts @@ -4,12 +4,12 @@ UDMX - + This plugin provides DMX output support for Anyma uDMX devices. Ce plugin offre le support de la sortie des périphériques Anyma uDMX. - + Do you wish to re-scan your hardware? Souhaitez-vous redétecter le matériel ? @@ -17,49 +17,49 @@ UDMXDevice - + Unknown Inconnu - + + Device name + + + + DMX Channels Canaux DMX - + DMX Frame Frequency Fréquence de trame DMX - + Bad Mauvaise - + Good Bonne - + Patch this device to a universe to find out. Patchez ce plugin à un univers pour la découvrir. - + System Timer Accuracy Précision de l'horloge système - - Unknown device - Périphérique inconnu - - - - Cannot connect to USB device. - Impossible de se connecter au périphérique USB. + + Device not in use + diff --git a/plugins/udmx/src/uDMX_it_IT.ts b/plugins/udmx/src/uDMX_it_IT.ts index 267a05e4b7..3ef77e8331 100644 --- a/plugins/udmx/src/uDMX_it_IT.ts +++ b/plugins/udmx/src/uDMX_it_IT.ts @@ -4,12 +4,12 @@ UDMX - + This plugin provides DMX output support for Anyma uDMX devices. Questa plugin permette la trasmissione di segnale DMX su interfacce Anyma uDMX. - + Do you wish to re-scan your hardware? Vuoi rifare la scansione dei tuoi dispositivi? @@ -17,49 +17,49 @@ UDMXDevice - + Unknown Sconosciuto - + + Device name + Nome del dispositivo + + + DMX Channels - + Canali DMX - + DMX Frame Frequency Frequenza pacchetti DMX - + Bad Scarsa - + Good Buona - + Patch this device to a universe to find out. Collega questo dispositivo ad un universo per rilevarla. - + System Timer Accuracy Precisione timer di sistema - - Unknown device - Dispositivo sconosciuto - - - - Cannot connect to USB device. - Non riesco a connettere l'interfaccia USB. + + Device not in use + Dispositivo non in uso diff --git a/plugins/udmx/src/uDMX_ja_JP.ts b/plugins/udmx/src/uDMX_ja_JP.ts index 908e24bf92..7fbd44dfc2 100644 --- a/plugins/udmx/src/uDMX_ja_JP.ts +++ b/plugins/udmx/src/uDMX_ja_JP.ts @@ -4,12 +4,12 @@ UDMX - + This plugin provides DMX output support for Anyma uDMX devices. このプラグインは、uDMX(μDMX)デバイスにDMX信号を送信します。 - + Do you wish to re-scan your hardware? ハードウェアを再スキャンしますか? @@ -17,49 +17,49 @@ UDMXDevice - + Unknown Unknown - + + Device name + + + + DMX Channels - + DMX Frame Frequency DMX Frame Frequency - + Bad Bad - + Good Good - + Patch this device to a universe to find out. Patch this device to a universe to find out. - + System Timer Accuracy System Timer Accuracy - - Unknown device - Unknown device - - - - Cannot connect to USB device. - Cannot connect to USB device. + + Device not in use + diff --git a/plugins/udmx/src/uDMX_nl_NL.ts b/plugins/udmx/src/uDMX_nl_NL.ts index e7b8e2dd84..69505b929d 100644 --- a/plugins/udmx/src/uDMX_nl_NL.ts +++ b/plugins/udmx/src/uDMX_nl_NL.ts @@ -4,12 +4,12 @@ UDMX - + This plugin provides DMX output support for Anyma uDMX devices. Deze plugin verzorgt DMX output voor Anyma uDMX apparaten. - + Do you wish to re-scan your hardware? Hardware opnieuw scannen? @@ -17,49 +17,49 @@ UDMXDevice - + Unknown Onbekend - + + Device name + + + + DMX Channels - + DMX Frame Frequency DMX Frame frequentie - + Bad Foutief - + Good Goed - + Patch this device to a universe to find out. Patch dit apparaat aan een universe. - + System Timer Accuracy Systeemklok precisie - - Unknown device - Onbekend apparaat - - - - Cannot connect to USB device. - Verbinden met USB apparaat mislukt. + + Device not in use + diff --git a/plugins/udmx/src/uDMX_pt_BR.ts b/plugins/udmx/src/uDMX_pt_BR.ts index 5604d8d140..51da879114 100644 --- a/plugins/udmx/src/uDMX_pt_BR.ts +++ b/plugins/udmx/src/uDMX_pt_BR.ts @@ -4,12 +4,12 @@ UDMX - + This plugin provides DMX output support for Anyma uDMX devices. Este plugin fornece suporte de saída DMX para dispositivos Anyma uDMX. - + Do you wish to re-scan your hardware? Deseja voltar a examinar o seu hardware? @@ -17,49 +17,49 @@ UDMXDevice - + Unknown Desconhecido - + + Device name + + + + DMX Channels - + DMX Frame Frequency Frequência de Frames DMX - + Bad - + Good Boa - + Patch this device to a universe to find out. Efectuar patch deste dispositivo a um universo para descobrir. - + System Timer Accuracy Precisão do relógio de sistema - - Unknown device - Dispositivo desconhecido - - - - Cannot connect to USB device. - Não se consegue ligar a um dispositivo USB. + + Device not in use + diff --git a/plugins/udmx/src/udmx.cpp b/plugins/udmx/src/udmx.cpp index 54261195db..734523830c 100644 --- a/plugins/udmx/src/udmx.cpp +++ b/plugins/udmx/src/udmx.cpp @@ -118,11 +118,11 @@ QString UDMX::outputInfo(quint32 output) return str; } -void UDMX::writeUniverse(quint32 universe, quint32 output, const QByteArray &data) +void UDMX::writeUniverse(quint32 universe, quint32 output, const QByteArray &data, bool dataChanged) { Q_UNUSED(universe) - if (output < quint32(m_devices.size())) + if (output < quint32(m_devices.size()) && dataChanged) m_devices.at(output)->outputDMX(data); } diff --git a/plugins/udmx/src/udmx.h b/plugins/udmx/src/udmx.h index 5e147d20eb..a1d7d5896b 100644 --- a/plugins/udmx/src/udmx.h +++ b/plugins/udmx/src/udmx.h @@ -71,7 +71,7 @@ class UDMX : public QLCIOPlugin QString outputInfo(quint32 output); /** @reimp */ - void writeUniverse(quint32 universe, quint32 output, const QByteArray& data); + void writeUniverse(quint32 universe, quint32 output, const QByteArray& data, bool dataChanged); private: /** Attempt to find all uDMX devices */ diff --git a/plugins/udmx/src/udmxdevice.cpp b/plugins/udmx/src/udmxdevice.cpp index d9e6c6d78b..ac2163edf1 100644 --- a/plugins/udmx/src/udmxdevice.cpp +++ b/plugins/udmx/src/udmxdevice.cpp @@ -207,7 +207,9 @@ const struct libusb_device* UDMXDevice::device() const void UDMXDevice::outputDMX(const QByteArray& universe) { - m_universe.replace(0, qMin(universe.size(), m_universe.size()), universe.constData()); + int offset = 0; + m_universe.replace(offset, qMin(universe.size(), m_universe.size()), universe.constData(), + qMin(universe.size(), m_universe.size())); } void UDMXDevice::stop() diff --git a/plugins/velleman/CMakeLists.txt b/plugins/velleman/CMakeLists.txt new file mode 100644 index 0000000000..41261bd861 --- /dev/null +++ b/plugins/velleman/CMakeLists.txt @@ -0,0 +1,4 @@ +project(velleman) + +add_subdirectory(src) +add_subdirectory(test) diff --git a/plugins/velleman/src/CMakeLists.txt b/plugins/velleman/src/CMakeLists.txt new file mode 100644 index 0000000000..c7e04dbf26 --- /dev/null +++ b/plugins/velleman/src/CMakeLists.txt @@ -0,0 +1,67 @@ +set(module_name "velleman") + +set(TS_FILES + Velleman_fi_FI.ts + Velleman_de_DE.ts + Velleman_es_ES.ts + Velleman_fr_FR.ts + Velleman_it_IT.ts + Velleman_nl_NL.ts + Velleman_cz_CZ.ts + Velleman_pt_BR.ts + Velleman_ca_ES.ts + Velleman_ja_JP.ts +) + +if(QT_VERSION_MAJOR GREATER 5) + qt_add_translation(QM_FILES ${TS_FILES}) +else() + qt5_add_translation(QM_FILES ${TS_FILES}) +endif() + +add_library(${module_name} + SHARED + ${QM_FILES} +) + +target_sources(${module_name} PRIVATE + ../../interfaces/qlcioplugin.cpp ../../interfaces/qlcioplugin.h + ${module_name}.cpp ${module_name}.h +) +target_include_directories(${module_name} PRIVATE + ../../interfaces +) + +target_link_libraries(${module_name} PRIVATE + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui +) + +if(WIN32) + target_compile_definitions(${module_name} PRIVATE + QLC_EXPORT + ) + + set(K8062DDIR "C:/Qt/K8062D") + target_link_directories(${module_name} PRIVATE + ${K8062DDIR} + ) + target_link_libraries(${module_name} PRIVATE + K8062D + ) + target_link_libraries(${module_name} PRIVATE + ${K8062DDIR}/K8062D.a + ) + target_include_directories(${module_name} PRIVATE + ${K8062DDIR} + ) +else() + target_sources(${module_name} PRIVATE + velleman_mock.cpp + ) +endif() + +install(TARGETS ${module_name} + LIBRARY DESTINATION ${INSTALLROOT}/${PLUGINDIR} + RUNTIME DESTINATION ${INSTALLROOT}/${PLUGINDIR} +) \ No newline at end of file diff --git a/plugins/velleman/src/velleman.cpp b/plugins/velleman/src/velleman.cpp index 788e8c5604..9faa44233e 100644 --- a/plugins/velleman/src/velleman.cpp +++ b/plugins/velleman/src/velleman.cpp @@ -146,9 +146,10 @@ QString Velleman::outputInfo(quint32 output) return str; } -void Velleman::writeUniverse(quint32 universe, quint32 output, const QByteArray &data) +void Velleman::writeUniverse(quint32 universe, quint32 output, const QByteArray &data, bool dataChanged) { Q_UNUSED(universe) + Q_UNUSED(dataChanged) if (output != 0 || m_currentlyOpen == false || data.isEmpty()) return; diff --git a/plugins/velleman/src/velleman.h b/plugins/velleman/src/velleman.h index 9a051b41cd..176420d622 100644 --- a/plugins/velleman/src/velleman.h +++ b/plugins/velleman/src/velleman.h @@ -68,7 +68,7 @@ class QLC_DECLSPEC Velleman : public QLCIOPlugin QString outputInfo(quint32 output); /** @reimp */ - void writeUniverse(quint32 universe, quint32 output, const QByteArray& data); + void writeUniverse(quint32 universe, quint32 output, const QByteArray& data, bool dataChanged); private: bool m_currentlyOpen; diff --git a/plugins/velleman/test/CMakeLists.txt b/plugins/velleman/test/CMakeLists.txt new file mode 100644 index 0000000000..b11fa91479 --- /dev/null +++ b/plugins/velleman/test/CMakeLists.txt @@ -0,0 +1,28 @@ +set(module_name "velleman_test") + +add_executable(${module_name} WIN32 + ../../interfaces/qlcioplugin.cpp ../../interfaces/qlcioplugin.h + ../src/velleman_mock.cpp + ${module_name}.cpp ${module_name}.h +) +target_include_directories(${module_name} PRIVATE + ../../interfaces + ../src +) + +target_link_libraries(${module_name} PRIVATE + # Remove: L../src + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Test + velleman +) + +if(WIN32) + target_compile_definitions(${module_name} PRIVATE + QLC_EXPORT + ) +endif() + +# Consider using qt_generate_deploy_app_script() for app deployment if +# the project can use Qt 6.3. In that case rerun qmake2cmake with +# --min-qt-version=6.3. diff --git a/plugins/velleman/test/velleman_test.cpp b/plugins/velleman/test/velleman_test.cpp index 0e280af78e..2c969c713b 100644 --- a/plugins/velleman/test/velleman_test.cpp +++ b/plugins/velleman/test/velleman_test.cpp @@ -129,16 +129,16 @@ void Velleman_Test::writeUniverse() data[127] = 42; data[511] = 96; - vo.writeUniverse(0, 0, data); + vo.writeUniverse(0, 0, data, true); QVERIFY(_ChannelCount == 0); QVERIFY(_SetAllData == NULL); vo.openOutput(0, 0); - vo.writeUniverse(0, 1, data); + vo.writeUniverse(0, 1, data, true); QVERIFY(_ChannelCount == 0); QVERIFY(_SetAllData == NULL); - vo.writeUniverse(0, 0, data); + vo.writeUniverse(0, 0, data, true); QVERIFY(_ChannelCount == data.size()); QVERIFY(_SetAllData != NULL); QCOMPARE(_SetAllData[0], 0); diff --git a/qlc.pro b/qlc.pro index fcdd07abd9..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 @@ -71,6 +75,14 @@ unix:run.commands += LD_LIBRARY_PATH=engine/src:\$\$LD_LIBRARY_PATH qmlui/qlcplu unix:run.commands += LD_LIBRARY_PATH=engine/src:ui/src:webaccess/src:\$\$LD_LIBRARY_PATH main/qlcplus } +# run-fxe +run-fxe.target = run-fxe +QMAKE_EXTRA_TARGETS += run-fxe +qmlui: { +} else { +unix:run-fxe.commands += LD_LIBRARY_PATH=engine/src:ui/src:webaccess/src:\$\$LD_LIBRARY_PATH ./fixtureeditor/qlcplus-fixtureeditor +} + # doxygen doxygen.target = doxygen QMAKE_EXTRA_TARGETS += doxygen diff --git a/qmake2cmake.md b/qmake2cmake.md new file mode 100644 index 0000000000..a8e67d92a8 --- /dev/null +++ b/qmake2cmake.md @@ -0,0 +1,239 @@ +# Build QLCPlus using CMake + +## Build on Windows using MSYS2 + +### Prepare the build system (MSYS2) + +Please refer to https://github.com/mcallegari/qlcplus/wiki/Windows-Build-Qt5#prepare-the-build-system-msys2 + +In addition, we need to install `cmake` by running the following command in MSYS2 shell. + +```bash +pacman -S mingw32/mingw-w64-i686-cmake +``` + +### Acquire the QLC+ sources + +Please refer to https://github.com/mcallegari/qlcplus/wiki/Windows-Build-Qt5#acquire-the-qlc-sources + +### Build QLC+ + +Close the MSYS2 shell, and from now on we will use `ming32.exe` in the installed `MSYS64` directory. +First, we need to create `build` directory and change directory into it to split source codes and binary object files. + +```bash +cd +mkdir build +cd build +``` + +We can use GCC or Ninja to compile our project + +#### Using GCC + +```bash +cmake -G "Unix Makefiles" .. +make +``` + +#### Using Ninja + +```bash +cmake -G Ninja .. +ninja +``` + +We can generally use `cmake --build .` instead of `ninja` or `mingw32-make`. + +### Build QLC+ 5 + +If you want to build `QLC+ 5` using `qmlui`, you can simply add `-Dqmlui=ON` parameter in the CMake command like the following. + +```bash +cmake -G "Unix Makefiles" -Dqmlui=ON .. +make +``` + +## Build on Windows using CMake, MinGW and QT + +This method is not recommended since it can not find the 3rd party libraries such as `libusb` or `libfftw3` + +### Prepare the build system (CMake, Qt) + +First, we need to install CMake for Windows. +We can download CMake from https://cmake.org/download/. + +Next, we need to install QT creator with MinGW on Windows. +We recommend to download QT from the official website https://www.qt.io/download and check `MinGW` compilers when we install it. + +### Building + +After we download the source code of QLCPlus we can build it using embedded MinGW in Qt. +We can compile QLC+ in 64bit mode using the 64bit MinGW compiler. + +```cmd +cd build +cmake -DCMAKE_PREFIX_PATH="C:/Qt/Qt5.12.12/5.12.12/mingw73_64/lib/cmake" -G"MinGW Makefiles" -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER="C:/Qt/Qt5.12.12/Tools/mingw730_64/bin/g++.exe" -DCMAKE_C_COMPILER="C:/Qt/Qt5.12.12/Tools/mingw730_64/bin/gcc.exe" -DCMAKE_MAKE_PROGRAM="C:/Qt/Qt5.12.12/Tools/mingw730_64/bin/mingw32-make.exe" .. +"C:/Qt/Qt5.12.12/Tools/mingw730_64/bin/mingw32-make.exe" +``` + +And we can also compile QLC+ in 32bit mode using the 32bit MinGW compiler. + +```cmd +cmake -DCMAKE_PREFIX_PATH="C:/Qt/Qt5.12.12/5.12.12/mingw73_32/lib/cmake" -G"MinGW Makefiles" -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER="C:/Qt/Qt5.12.12/Tools/mingw730_32/bin/c++.exe" -DCMAKE_C_COMPILER="C:/Qt/Qt5.12.12/Tools/mingw730_32/bin/gcc.exe" -DCMAKE_MAKE_PROGRAM="C:/Qt/Qt5.12.12/Tools/mingw730_32/bin/mingw32-make.exe" .. +"C:/Qt/Qt5.12.12/Tools/mingw730_32/bin/mingw32-make.exe" +``` + +Compiling QLC+5 is similar to the section above, we just need to add `-Dqmlui=ON` parameter in the CMake command. + +## Build on Linux + +### Prepare the build system (Qt) + +We can use either of the official Qt packages or Debian Qt packages. + +#### Install the official Qt packages + +If you want to use official Qt for building QLC, you can install it from the [official link](https://www.qt.io/download-qt-installer-oss). + +You can select several versions of QT to install. + +Qt will be installed in the `Qt` directory in the user `home` directory i.e. `~/Qt` by default. + +#### Install the Debian Qt packages + +If you want to use the Debian Qt packages instead of the official Qt packages, you will need to install it by running the following command in Ubuntu. + +```bash +sudo apt install -y qtcreator qtbase5-dev qt5-qmake libqt5svg5-dev qt3d5-dev qtdeclarative5-dev qttools5-dev qt3d-defaultgeometryloader-plugin qt3d-assimpsceneimport-plugin qml-module-qt3d qml-module-qtmultimedia +``` + +for installing Qt5 packages or + +```bash +sudo apt install -y qt6-base-dev qt6-tools-dev qt6-tools-dev-tools qt6-l10n-tools qt6-multimedia-dev qt6-declarative-dev qt6-3d-dev libqt6svg6-dev libqt6serialport6-dev +``` + +for installing Qt6 packages. + +### Build QLC+ 4 on Linux + +After you download (or clone) the source code of QLC+, we also need to create `build` directory and you can simply compile it by specifying the `CMAKE_PREFIX_PATH` to the appropriate path. + +#### Build QLC+ 4 using the official Qt packages + +The `CMAKE_PREFIX_PATH` can be found in the installed Qt directory. + +```bash +cd /build +cmake -DCMAKE_PREFIX_PATH="/home//Qt/5.15.2/gcc_64/lib/cmake" .. +make +``` + +If you want to compile it using Qt6, you can just simply specify the CMake path of Qt6. + +```bash +cmake -DCMAKE_PREFIX_PATH="/home//Qt/6.5.0/gcc_64/lib/cmake" .. +``` + +#### Build QLC+ 4 using the Debian Qt packages + +```bash +cd /build +cmake -DCMAKE_PREFIX_PATH="/usr/lib/x86_64-linux-gnu/cmake/Qt5" .. +make +``` + +If you want to compile it using Qt6, you can just simply replace Qt5 with Qt6 in CMake command. + +```bash +cmake -DCMAKE_PREFIX_PATH="/usr/lib/x86_64-linux-gnu/cmake/Qt6" .. +``` + +### Create a QLC+ Debian package + +Just run the following command from the build folder: +```bash +cpack -G DEB +``` + +### Build QLC+ 5 on Linux + +Building QLC+ 5 is similar to building QLC+ 4, we can just add `-Dqmlui=ON` option to build QLC+5 with `qmlui`. + +```bash +cmake -Dqmlui=ON -DCMAKE_PREFIX_PATH="/usr/lib/x86_64-linux-gnu/cmake/Qt5" .. +make +``` + +You can select other appropriate `CMAKE_PREFIX_PATH` as described in the section above. + +### Running and installing `qlcplus` + +After you build `qlcplus`, you can simply run it with `make run` command. + +And you can also install it with `sudo make install` command. + +But when you build `qlcplus` with the official Qt installation which is not installed in system path like `/usr/lib/`, you will fail to run the installed `qlcplus`. +In this case, you will need to specify `LD_LIBRARY_PATH` so that the `qlcplus` program can find its dependencies like `libQt5Core.so.5`. + +```bash +export LD_LIBRARY_PATH=/home//Qt/5.15.2/gcc_64/lib/ +qlcplus +``` + +## Work History (For developer notes) + +### qmake2cmake + +I used `qmake2cmake` project to convert Qt `.pro` files to `CMake` files. + +Please refer to https://www.qt.io/blog/introducing-qmake2cmake + +First of all, I downloaded `qmake2cmake` by running: + +```cmd +python -m pip install qmake2cmake +``` + +Next in the root directory of the qlcplus source code, run the following command + +```cmd +cd +qmake2cmake_all.exe . --min-qt-version 5.10 +``` + +The following files were not successfully converted (2 of 141): +".\platforms\macos\macos.pro" +".\plugins\dmxusb\src\src.pro" +So need to convert these 2 project files manually. + +### Modify auto-generated `CMakeList.txt` files manually + +Next, I tried to `cmake` using MSVC2017. + +```bash +mkdir build +cd build +cmake -DCMAKE_PREFIX_PATH="C:\Qt\Qt5.12.12\5.12.12\msvc2017\lib\cmake" .. +``` + +Next, since we need a compatibility with Qt5 and Qt6, I needed to replace some directives in `CMakeList.txt` files like the following. + +```text +qt_add_library => add_library +qt_add_executable => add_executable +qt_add_plugin => add_library + +if(CONFIG(win32)) => if(WIN32) +if(CONFIG(coverage)) => if(coverage) +if(CONFIG(udev)) => if(udev) +if(CONFIG(iokit)) => if(iokit) +``` + +If you want to define a Macro like `CONFIG(udev)` in Qt \*.pro file, you can add `-Dudev=ON` as a CMake parameter +E.g. + +```cmd +cmake -DCMAKE_PREFIX_PATH="C:\Qt\Qt5.12.12\5.12.12\msvc2017\lib\cmake" -Dudev=ON .. +``` diff --git a/qmlui/CMakeLists.txt b/qmlui/CMakeLists.txt new file mode 100644 index 0000000000..1887e66002 --- /dev/null +++ b/qmlui/CMakeLists.txt @@ -0,0 +1,168 @@ +set(module_name "qlcplus-qml") + +set(TS_FILES + qlcplus_ca_ES.ts + qlcplus_de_DE.ts + qlcplus_es_ES.ts + qlcplus_fr_FR.ts + qlcplus_it_IT.ts + qlcplus_ja_JP.ts + qlcplus_nl_NL.ts + qlcplus_pl_PL.ts + qlcplus_ru_RU.ts + qlcplus_uk_UA.ts +) + +if(QT_VERSION_MAJOR GREATER 5) + qt_add_translation(QM_FILES ${TS_FILES}) +else() + qt5_add_translation(QM_FILES ${TS_FILES}) +endif() + +set(SRC_FILES + app.cpp app.h + audioeditor.cpp audioeditor.h + chasereditor.cpp chasereditor.h + collectioneditor.cpp collectioneditor.h + colorfilters.cpp colorfilters.h + contextmanager.cpp contextmanager.h + efxeditor.cpp efxeditor.h + fixturebrowser.cpp fixturebrowser.h + fixtureeditor/channeledit.cpp fixtureeditor/channeledit.h + fixtureeditor/editorview.cpp fixtureeditor/editorview.h + fixtureeditor/fixtureeditor.cpp fixtureeditor/fixtureeditor.h + fixtureeditor/modeedit.cpp fixtureeditor/modeedit.h + fixtureeditor/physicaledit.cpp fixtureeditor/physicaledit.h + fixturegroupeditor.cpp fixturegroupeditor.h + fixturemanager.cpp fixturemanager.h + fixtureutils.cpp fixtureutils.h + functioneditor.cpp functioneditor.h + functionmanager.cpp functionmanager.h + importmanager.cpp importmanager.h + inputoutputmanager.cpp inputoutputmanager.h + inputprofileeditor.cpp inputprofileeditor.h + listmodel.cpp listmodel.h + main.cpp + mainview2d.cpp mainview2d.h + mainview3d.cpp mainview3d.h + mainviewdmx.cpp mainviewdmx.h + modelselector.cpp modelselector.h + palettemanager.cpp palettemanager.h + previewcontext.cpp previewcontext.h + rgbmatrixeditor.cpp rgbmatrixeditor.h + sceneeditor.cpp sceneeditor.h + scripteditor.cpp scripteditor.h + showmanager.cpp showmanager.h + simpledesk.cpp simpledesk.h + tardis/networkmanager.cpp tardis/networkmanager.h + tardis/networkpacketizer.cpp tardis/networkpacketizer.h + tardis/simplecrypt.cpp tardis/simplecrypt.h + tardis/tardis.cpp tardis/tardis.h + treemodel.cpp treemodel.h + treemodelitem.cpp treemodelitem.h + 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 + virtualconsole/vcframe.cpp virtualconsole/vcframe.h + virtualconsole/vclabel.cpp virtualconsole/vclabel.h + virtualconsole/vcpage.cpp virtualconsole/vcpage.h + 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 +) + +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 + ${SRC_FILES} + ${QM_FILES} + ) +endif() + +if(WIN32) + target_sources(${module_name} PRIVATE + qmlui.rc + ) +endif() + +target_include_directories(${module_name} PRIVATE + ../engine/audio/src + ../engine/src + ../plugins/interfaces + ../plugins/midi/src/common + fixtureeditor + tardis + virtualconsole +) + +target_link_libraries(${module_name} PRIVATE + Qt${QT_MAJOR_VERSION}::3DCore + Qt${QT_MAJOR_VERSION}::3DInput + Qt${QT_MAJOR_VERSION}::3DQuick + Qt${QT_MAJOR_VERSION}::3DQuickExtras + Qt${QT_MAJOR_VERSION}::3DRender + Qt${QT_MAJOR_VERSION}::3DExtras + Qt${QT_MAJOR_VERSION}::Core + Qt${QT_MAJOR_VERSION}::Gui + Qt${QT_MAJOR_VERSION}::Multimedia + Qt${QT_MAJOR_VERSION}::MultimediaWidgets + Qt${QT_MAJOR_VERSION}::PrintSupport + Qt${QT_MAJOR_VERSION}::Qml + Qt${QT_MAJOR_VERSION}::Quick + Qt${QT_MAJOR_VERSION}::Svg + Qt${QT_MAJOR_VERSION}::Widgets + 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 + qml/fixturesfunctions/*.qml + qml/inputoutput/*.qml + qml/popup/*.qml + qml/showmanager/*.qml + qml/virtualconsole/*.qml + ) +endif() + +if(QT_VERSION_MAJOR EQUAL 5) + qt5_add_resources(qlcplusqmlui_resource_files qmlui.qrc ../resources/icons/svg/svgicons.qrc ../resources/fonts/fonts.qrc) + target_sources(${module_name} PUBLIC + ${qlcplusqmlui_resource_files} + ) +else() + qt_add_resources(qlcplusqmlui_resource_files qmlui.qrc ../resources/icons/svg/svgicons.qrc ../resources/fonts/fonts.qrc) + target_sources(${module_name} PUBLIC + ${qlcplusqmlui_resource_files} + ) +endif() + +install(TARGETS ${module_name} + DESTINATION ${INSTALLROOT}/${BINDIR} +) diff --git a/qmlui/app.cpp b/qmlui/app.cpp index 00d385d5af..daf8b5240c 100644 --- a/qmlui/app.cpp +++ b/qmlui/app.cpp @@ -33,9 +33,10 @@ #include #include #include +#include #include "app.h" -#include "mainview2d.h" +#include "uimanager.h" #include "simpledesk.h" #include "showmanager.h" #include "fixtureeditor.h" @@ -57,7 +58,6 @@ #include "qlcfixturedefcache.h" #include "audioplugincache.h" #include "rgbscriptscache.h" -#include "qlcfixturedef.h" #include "qlcconfig.h" #include "qlcfile.h" @@ -77,6 +77,8 @@ App::App() , m_showManager(nullptr) , m_simpleDesk(nullptr) , m_videoProvider(nullptr) + , m_networkManager(nullptr) + , m_uiManager(nullptr) , m_doc(nullptr) , m_docLoaded(false) , m_printItem(nullptr) @@ -115,10 +117,10 @@ QString App::appVersion() const void App::startup() { + qmlRegisterUncreatableType("org.qlcplus.classes", 1, 0, "App", "Can't create an App!"); qmlRegisterUncreatableType("org.qlcplus.classes", 1, 0, "Fixture", "Can't create a Fixture!"); qmlRegisterUncreatableType("org.qlcplus.classes", 1, 0, "QLCFunction", "Can't create a Function!"); qmlRegisterType("org.qlcplus.classes", 1, 0, "ModelSelector"); - qmlRegisterUncreatableType("org.qlcplus.classes", 1, 0, "App", "Can't create an App!"); setTitle(APPNAME); setIcon(QIcon(":/qlcplus.svg")); @@ -131,20 +133,19 @@ void App::startup() rootContext()->setContextProperty("qlcplus", this); - m_pixelDensity = qMax(screen()->physicalDotsPerInch() * 0.039370, (qreal)screen()->size().height() / 220.0); - qDebug() << "Pixel density:" << m_pixelDensity << "size:" << screen()->physicalSize(); - - rootContext()->setContextProperty("screenPixelDensity", m_pixelDensity); + slotScreenChanged(screen()); initDoc(); + m_uiManager = new UiManager(this, m_doc); + rootContext()->setContextProperty("uiManager", m_uiManager); m_ioManager = new InputOutputManager(this, m_doc); m_fixtureBrowser = new FixtureBrowser(this, m_doc); m_fixtureManager = new FixtureManager(this, m_doc); m_fixtureGroupEditor = new FixtureGroupEditor(this, m_doc, m_fixtureManager); m_functionManager = new FunctionManager(this, m_doc); - m_simpleDesk = new SimpleDesk(this, m_doc); - m_contextManager = new ContextManager(this, m_doc, m_fixtureManager, m_functionManager, m_simpleDesk); + m_simpleDesk = new SimpleDesk(this, m_doc, m_functionManager); + m_contextManager = new ContextManager(this, m_doc, m_fixtureManager, m_functionManager); m_paletteManager = new PaletteManager(this, m_doc, m_contextManager); m_virtualConsole = new VirtualConsole(this, m_doc, m_contextManager); @@ -174,6 +175,8 @@ void App::startup() // Start up in non-modified state m_doc->resetModified(); + m_uiManager->initialize(); + // and here we go ! setSource(QUrl("qrc:/MainView.qml")); } @@ -218,6 +221,11 @@ void App::setLanguage(QString locale) engine()->retranslate(); } +QString App::goboSystemPath() const +{ + return QLCFile::systemDirectory(GOBODIR).absolutePath(); +} + void App::show() { QScreen *currScreen = screen(); @@ -317,8 +325,12 @@ void App::slotSceneGraphInitialized() void App::slotScreenChanged(QScreen *screen) { - m_pixelDensity = qMax(screen->physicalDotsPerInch() * 0.039370, (qreal)screen->size().height() / 220.0); - qDebug() << "Screen changed to" << screen->name() << ". New pixel density:" << m_pixelDensity; + bool isLandscape = (screen->orientation() == Qt::LandscapeOrientation || + screen->orientation() == Qt::InvertedLandscapeOrientation) ? true : false; + qreal sSize = isLandscape ? screen->size().height() : screen->size().width(); + m_pixelDensity = qMax(screen->physicalDotsPerInch() * 0.039370, sSize / 220.0); + qDebug() << "Screen changed to" << screen->name() << ", pixel density:" << m_pixelDensity + << ", physical size:" << screen->physicalSize(); rootContext()->setContextProperty("screenPixelDensity", m_pixelDensity); } @@ -342,30 +354,17 @@ void App::slotAccessMaskChanged(int mask) setAccessMask(mask); } -void App::clearDocument() +/********************************************************************* + * Doc + *********************************************************************/ +Doc *App::doc() { - if (m_videoProvider) - { - delete m_videoProvider; - m_videoProvider = nullptr; - } - - m_doc->masterTimer()->stop(); - m_doc->clearContents(); - m_virtualConsole->resetContents(); - //SimpleDesk::instance()->clearContents(); - m_showManager->resetContents(); - m_tardis->resetHistory(); - m_doc->inputOutputMap()->resetUniverses(); - setFileName(QString()); - m_doc->resetModified(); - m_doc->inputOutputMap()->startUniverses(); - m_doc->masterTimer()->start(); + return m_doc; } -Doc *App::doc() +bool App::docLoaded() { - return m_doc; + return m_docLoaded; } bool App::docModified() const @@ -379,6 +378,8 @@ void App::initDoc() m_doc = new Doc(this); connect(m_doc, SIGNAL(modified(bool)), this, SIGNAL(docModifiedChanged())); + connect(m_doc->masterTimer(), SIGNAL(functionListChanged()), + this, SIGNAL(runningFunctionsCountChanged())); /* Load user fixtures first so that they override system fixtures */ m_doc->fixtureDefCache()->load(QLCFixtureDefCache::userDefinitionDirectory()); @@ -419,6 +420,44 @@ void App::initDoc() m_doc->masterTimer()->start(); } +void App::clearDocument() +{ + if (m_videoProvider) + { + delete m_videoProvider; + m_videoProvider = nullptr; + } + + m_contextManager->resetFixtureSelection(); + //m_simpleDesk->resetContents(); // TODO + m_showManager->resetContents(); + m_virtualConsole->resetContents(); + + m_doc->masterTimer()->stop(); + m_doc->clearContents(); + + m_tardis->resetHistory(); + m_doc->inputOutputMap()->resetUniverses(); + setFileName(QString()); + m_doc->resetModified(); + m_doc->inputOutputMap()->startUniverses(); + m_doc->masterTimer()->start(); +} + +int App::runningFunctionsCount() const +{ + return m_doc->masterTimer()->runningFunctions(); +} + +void App::stopAllFunctions() +{ + // first, gracefully stop via Function Manager (if that's the case) + m_functionManager->setPreviewEnabled(false); + + // then, brutally kill the rest (could be started from VC, etc) + m_doc->masterTimer()->stopAllFunctions(); +} + void App::enableKioskMode() { // enable Virtual console only @@ -449,7 +488,7 @@ void App::slotItemReadyForPrinting() { QPrinter printer; QPrintDialog *dlg = new QPrintDialog(&printer); - if(dlg->exec() == QDialog::Accepted) + if (dlg->exec() == QDialog::Accepted) { QRectF pageRect = printer.pageLayout().paintRect(); QSize imgSize = m_printerImage->image().size(); @@ -472,7 +511,7 @@ void App::slotItemReadyForPrinting() } // handle multi-page printing - while(totalHeight > 0) + while (totalHeight > 0) { painter.drawImage(QPoint(0, 0), img, QRectF(0, yOffset, actualWidth, pageRect.height())); yOffset += pageRect.height(); @@ -880,6 +919,7 @@ void App::importFromWorkspace() return; m_importManager->apply(); + m_paletteManager->updatePaletteList(); delete m_importManager; m_importManager = nullptr; @@ -916,14 +956,27 @@ void App::loadFixture(QString fileName) void App::editFixture(QString manufacturer, QString model) { + bool switchToEditor = false; + if (m_fixtureEditor == nullptr) { m_fixtureEditor = new FixtureEditor(this, m_doc); + switchToEditor = true; + } + + if (m_fixtureEditor->editDefinition(manufacturer, model) == false) + { + delete m_fixtureEditor; + m_fixtureEditor = nullptr; + return; + } + + if (switchToEditor) + { QMetaObject::invokeMethod(rootObject(), "switchToContext", Q_ARG(QVariant, "FXEDITOR"), Q_ARG(QVariant, "qrc:/FixtureEditor.qml")); } - m_fixtureEditor->editDefinition(manufacturer, model); } void App::closeFixtureEditor() diff --git a/qmlui/app.h b/qmlui/app.h index 544b4f8458..ce25e6de3b 100644 --- a/qmlui/app.h +++ b/qmlui/app.h @@ -29,6 +29,7 @@ class MainView2D; class ShowManager; class SimpleDesk; +class UiManager; class ActionManager; class FixtureBrowser; class FixtureManager; @@ -56,6 +57,7 @@ class App : public QQuickView Q_PROPERTY(QStringList recentFiles READ recentFiles NOTIFY recentFilesChanged) Q_PROPERTY(QString workingPath READ workingPath WRITE setWorkingPath NOTIFY workingPathChanged) Q_PROPERTY(int accessMask READ accessMask WRITE setAccessMask NOTIFY accessMaskChanged) + Q_PROPERTY(int runningFunctionsCount READ runningFunctionsCount NOTIFY runningFunctionsCountChanged) Q_PROPERTY(QString appName READ appName CONSTANT) Q_PROPERTY(QString appVersion READ appVersion CONSTANT) @@ -95,6 +97,23 @@ class App : public QQuickView }; Q_ENUM(DragItemTypes) + enum ChannelType + { + DimmerType = (1 << QLCChannel::Intensity), + ColorMacroType = (1 << QLCChannel::Colour), // Color wheels, color macros + GoboType = (1 << QLCChannel::Gobo), + SpeedType = (1 << QLCChannel::Speed), + PanType = (1 << QLCChannel::Pan), + TiltType = (1 << QLCChannel::Tilt), + ShutterType = (1 << QLCChannel::Shutter), + PrismType = (1 << QLCChannel::Prism), + BeamType = (1 << QLCChannel::Beam), + EffectType = (1 << QLCChannel::Effect), + MaintenanceType = (1 << QLCChannel::Maintenance), + ColorType = (1 << (QLCChannel::Maintenance + 1)) // RGB/CMY/WAUV + }; + Q_ENUM(ChannelType) + enum ChannelColors { Red = (1 << 0), @@ -131,6 +150,8 @@ class App : public QQuickView Q_INVOKABLE void setLanguage(QString locale); + Q_INVOKABLE QString goboSystemPath() const; + void enableKioskMode(); void createKioskCloseButton(const QRect& rect); @@ -166,7 +187,7 @@ protected slots: void accessMaskChanged(int mask); private: - /** The number of pixels in one millimiter */ + /** The number of pixels in one millimeter */ qreal m_pixelDensity; /** Bitmask to enable/disable UI functionalities */ @@ -187,26 +208,38 @@ protected slots: ActionManager *m_actionManager; VideoProvider *m_videoProvider; NetworkManager *m_networkManager; + UiManager *m_uiManager; Tardis *m_tardis; /********************************************************************* * Doc *********************************************************************/ public: - void clearDocument(); - + /** Return a reference to the Doc instance */ Doc *doc(); - bool docLoaded() { return m_docLoaded; } + /** Return if the current Doc instance has been loaded */ + bool docLoaded(); + /** Return the Doc instance modified flag */ bool docModified() const; + /** Reset the currently loaded Doc instance */ + void clearDocument(); + + /** Return the number of currently running Functions */ + int runningFunctionsCount() const; + + /** Stop all the currently running Functions */ + Q_INVOKABLE void stopAllFunctions(); + private: void initDoc(); signals: void docLoadedChanged(); void docModifiedChanged(); + void runningFunctionsCountChanged(); private: Doc *m_doc; diff --git a/qmlui/audioeditor.cpp b/qmlui/audioeditor.cpp index d642e07718..d5e1e9ef2d 100644 --- a/qmlui/audioeditor.cpp +++ b/qmlui/audioeditor.cpp @@ -110,9 +110,31 @@ void AudioEditor::setLooped(bool looped) m_audio->setRunOrder(Audio::Loop); else m_audio->setRunOrder(Audio::SingleShot); + + emit loopedChanged(); } } +qreal AudioEditor::volume() +{ + if (m_audio != nullptr) + return m_audio->volume() * 100; + + return 100; +} + +void AudioEditor::setVolume(qreal volume) +{ + if (m_audio == nullptr) + return; + + Tardis::instance()->enqueueAction(Tardis::AudioSetVolume, m_audio->id(), m_audio->volume(), volume / 100.0); + + m_audio->setVolume(volume / 100); + + emit volumeChanged(); +} + int AudioEditor::cardLineIndex() const { if (m_audio == nullptr || m_audio->audioDevice().isEmpty()) @@ -122,7 +144,7 @@ int AudioEditor::cardLineIndex() const int i = 1; QString device = m_audio->audioDevice(); - foreach(AudioDeviceInfo info, devList) + foreach (AudioDeviceInfo info, devList) { if (info.capabilities & AUDIO_CAP_OUTPUT) { @@ -152,7 +174,7 @@ void AudioEditor::setCardLineIndex(int cardLineIndex) QList devList = m_doc->audioPluginCache()->audioDevicesList(); int i = 1; - foreach(AudioDeviceInfo info, devList) + foreach (AudioDeviceInfo info, devList) { if (info.capabilities & AUDIO_CAP_OUTPUT) { diff --git a/qmlui/audioeditor.h b/qmlui/audioeditor.h index 1729c95e5a..ece4b41d1e 100644 --- a/qmlui/audioeditor.h +++ b/qmlui/audioeditor.h @@ -33,6 +33,7 @@ class AudioEditor : public FunctionEditor Q_PROPERTY(QStringList audioExtensions READ audioExtensions CONSTANT) Q_PROPERTY(QVariant mediaInfo READ mediaInfo NOTIFY mediaInfoChanged) Q_PROPERTY(bool looped READ isLooped WRITE setLooped NOTIFY loopedChanged) + Q_PROPERTY(qreal volume READ volume WRITE setVolume NOTIFY volumeChanged) Q_PROPERTY(int cardLineIndex READ cardLineIndex WRITE setCardLineIndex NOTIFY cardLineIndexChanged) public: @@ -55,6 +56,10 @@ class AudioEditor : public FunctionEditor bool isLooped(); void setLooped(bool looped); + /** Get/Set the Audio function volume */ + qreal volume(); + void setVolume(qreal volume); + /** Get/Set the audio card line used to play this Audio function */ int cardLineIndex() const; void setCardLineIndex(int cardLineIndex); @@ -63,6 +68,7 @@ class AudioEditor : public FunctionEditor void sourceFileNameChanged(QString sourceFileName); void mediaInfoChanged(); void loopedChanged(); + void volumeChanged(); void cardLineIndexChanged(int cardLineIndex); private: diff --git a/qmlui/chasereditor.cpp b/qmlui/chasereditor.cpp index 464bfa062b..3e95898178 100644 --- a/qmlui/chasereditor.cpp +++ b/qmlui/chasereditor.cpp @@ -40,12 +40,12 @@ ChaserEditor::ChaserEditor(QQuickView *view, Doc *doc, QObject *parent) void ChaserEditor::setFunctionID(quint32 ID) { if (m_chaser) - disconnect(m_chaser, &Chaser::currentStepChanged, this, &ChaserEditor::slotStepChanged); + disconnect(m_chaser, &Chaser::currentStepChanged, this, &ChaserEditor::slotStepIndexChanged); m_chaser = qobject_cast(m_doc->function(ID)); FunctionEditor::setFunctionID(ID); if (m_chaser != nullptr) - connect(m_chaser, &Chaser::currentStepChanged, this, &ChaserEditor::slotStepChanged); + connect(m_chaser, &Chaser::currentStepChanged, this, &ChaserEditor::slotStepIndexChanged); updateStepsList(m_doc, m_chaser, m_stepsList); emit stepsListChanged(); @@ -159,23 +159,77 @@ bool ChaserEditor::moveSteps(QVariantList indicesList, int insertIndex) return false; QVectorsortedList; + bool firstDecreased = false; if (insertIndex == -1) insertIndex = m_chaser->stepsCount() - 1; + int insIdx = insertIndex; + // create a list of ordered step indices - for (QVariant vIndex : indicesList) + for (QVariant &vIndex : indicesList) { int idx = vIndex.toInt(); sortedList.append(idx); } std::sort(sortedList.begin(), sortedList.end()); - for (int index : sortedList) + for (int i = 0; i < sortedList.count(); i++) + { + int index = sortedList.at(i); + + // when moving an item down, every other step with index < destination + // needs to have their index decreased by one + if (index < insIdx) + { + if (firstDecreased == false) + { + insIdx--; + firstDecreased = true; + } + + for (int j = i + 1; j < sortedList.count(); j++) + sortedList[j]--; + insertIndex--; + } + + qDebug() << "Moving step from" << index << "to" << insIdx; + Tardis::instance()->enqueueAction(Tardis::ChaserMoveStep, m_chaser->id(), index, insIdx); + m_chaser->moveStep(index, insIdx); + + if (index > insIdx) + insIdx++; + } + + updateStepsList(m_doc, m_chaser, m_stepsList); + emit stepsListChanged(); + + QQuickItem *chaserWidget = qobject_cast(m_view->rootObject()->findChild("chaserEditorWidget")); + + for (int i = 0; i < indicesList.length(); i++) { - qDebug() << "Moving step from" << index << "to" << insertIndex; - m_chaser->moveStep(index, insertIndex); - // TODO: tardis + QMetaObject::invokeMethod(chaserWidget, "selectStep", + Q_ARG(QVariant, insertIndex + i), + Q_ARG(QVariant, i == 0 ? false : true)); + } + + return true; +} + +bool ChaserEditor::duplicateSteps(QVariantList indicesList) +{ + if (m_chaser == nullptr || indicesList.count() == 0) + return false; + + for (QVariant &vIndex : indicesList) + { + int stepIndex = vIndex.toInt(); + ChaserStep *sourceStep = m_chaser->stepAt(stepIndex); + ChaserStep step(*sourceStep); + m_chaser->addStep(step); + + Tardis::instance()->enqueueAction(Tardis::ChaserAddStep, m_chaser->id(), QVariant(), + Tardis::instance()->actionToByteArray(Tardis::ChaserAddStep, m_chaser->id(), m_chaser->stepsCount() - 1)); } updateStepsList(m_doc, m_chaser, m_stepsList); @@ -206,14 +260,15 @@ void ChaserEditor::setPlaybackIndex(int playbackIndex) if (m_playbackIndex == playbackIndex) return; - if (m_chaser != nullptr && m_chaser->type() == Function::SequenceType && playbackIndex >= 0) + if (m_chaser != nullptr && m_previewEnabled == false && + m_chaser->type() == Function::SequenceType && playbackIndex >= 0) { Sequence *sequence = qobject_cast(m_chaser); Scene *currScene = qobject_cast (m_doc->function(sequence->boundSceneID())); if (currScene != nullptr) { - for(SceneValue scv : m_chaser->stepAt(playbackIndex)->values) + for (SceneValue &scv : m_chaser->stepAt(playbackIndex)->values) currScene->setValue(scv); } } @@ -229,12 +284,35 @@ void ChaserEditor::setPreviewEnabled(bool enable) ChaserAction action; action.m_action = ChaserSetStepIndex; action.m_stepIndex = m_playbackIndex; + action.m_masterIntensity = 1.0; + action.m_stepIntensity = 1.0; + action.m_fadeMode = Chaser::FromFunction; m_chaser->setAction(action); } FunctionEditor::setPreviewEnabled(enable); } +void ChaserEditor::gotoPreviousStep() +{ + ChaserAction action; + action.m_action = ChaserPreviousStep; + action.m_masterIntensity = 1.0; + action.m_stepIntensity = 1.0; + action.m_fadeMode = Chaser::FromFunction; + m_chaser->setAction(action); +} + +void ChaserEditor::gotoNextStep() +{ + ChaserAction action; + action.m_action = ChaserNextStep; + action.m_masterIntensity = 1.0; + action.m_stepIntensity = 1.0; + action.m_fadeMode = Chaser::FromFunction; + m_chaser->setAction(action); +} + void ChaserEditor::deleteItems(QVariantList list) { if (m_chaser == nullptr) @@ -262,12 +340,42 @@ void ChaserEditor::deleteItems(QVariantList list) emit stepsListChanged(); } -void ChaserEditor::slotStepChanged(int index) +void ChaserEditor::removeFixtures(QVariantList list) +{ + if (m_chaser == nullptr) + return; + + Sequence *sequence = qobject_cast(m_chaser); + Scene *scene = qobject_cast(m_doc->function(sequence->boundSceneID())); + if (scene == nullptr) + return; + + // transform the list of fixture indices into a list of fixture IDs + QList sceneFixtureList = scene->fixtures(); + QList fixtureIdList; + for (QVariant &fIndex : list) + fixtureIdList.append(sceneFixtureList.at(fIndex.toInt())); + + // run though steps and search for matching fixture IDs + for (int i = 0; i < m_chaser->stepsCount(); i++) + { + ChaserStep *step = m_chaser->stepAt(i); + QMutableListIterator it(step->values); + while (it.hasNext()) + { + SceneValue scv = it.next(); + if (fixtureIdList.contains(scv.fxi)) + it.remove(); + } + } +} + +void ChaserEditor::slotStepIndexChanged(int index) { setPlaybackIndex(index); } -void ChaserEditor::addStepToListModel(Doc *doc, Chaser *chaser, ListModel *stepsList, ChaserStep *step) +QVariantMap ChaserEditor::stepDataMap(Doc *doc, Chaser *chaser, ChaserStep *step) { QVariantMap stepMap; Function *func = doc->function(step->fid); @@ -322,9 +430,23 @@ void ChaserEditor::addStepToListModel(Doc *doc, Chaser *chaser, ListModel *steps } stepMap.insert("note", step->note); + + return stepMap; +} + +void ChaserEditor::addStepToListModel(Doc *doc, Chaser *chaser, ListModel *stepsList, ChaserStep *step) +{ + QVariantMap stepMap = stepDataMap(doc, chaser, step); stepsList->addDataMap(stepMap); } +void ChaserEditor::updateStepInListModel(Doc *doc, Chaser *chaser, ListModel *stepsList, ChaserStep *step, int index) +{ + QVariantMap stepMap = stepDataMap(doc, chaser, step); + QModelIndex idx = stepsList->index(index, 0, QModelIndex()); + stepsList->setDataMap(idx, stepMap); +} + void ChaserEditor::updateStepsList(Doc *doc, Chaser *chaser, ListModel *stepsList) { if (doc == nullptr || chaser == nullptr || stepsList == nullptr) @@ -350,8 +472,8 @@ void ChaserEditor::setSelectedValue(Function::PropType type, QString param, uint for (quint32 i = 0; i < quint32(m_chaser->stepsCount()); i++) { QModelIndex idx = m_stepsList->index(int(i), 0, QModelIndex()); - QVariant isSelected = true; + if (selectedOnly == true) isSelected = m_stepsList->data(idx, "isSelected"); @@ -469,7 +591,7 @@ void ChaserEditor::setTempoType(int tempoType) int beatDuration = m_doc->masterTimer()->beatTimeDuration(); quint32 index = 0; - for (ChaserStep step : m_chaser->steps()) + for (ChaserStep &step : m_chaser->steps()) { UIntPair oldDuration(index, step.duration); UIntPair oldFadeIn(index, step.fadeIn); @@ -640,8 +762,10 @@ void ChaserEditor::setStepNote(int index, QString text) if (m_chaser == nullptr || index < 0 || index >= m_chaser->stepsCount()) return; + ChaserStep step = m_chaser->steps().at(int(index)); + step.note = text; + m_chaser->replaceStep(step, index); + QModelIndex mIdx = m_stepsList->index(index, 0, QModelIndex()); - ChaserStep *step = m_chaser->stepAt(index); - step->note = text; m_stepsList->setDataWithRole(mIdx, "note", text); } diff --git a/qmlui/chasereditor.h b/qmlui/chasereditor.h index 2c71782fa8..473766ed2e 100644 --- a/qmlui/chasereditor.h +++ b/qmlui/chasereditor.h @@ -48,8 +48,12 @@ class ChaserEditor : public FunctionEditor /** Returns if the Chaser being edited is a Sequence */ bool isSequence() const; + /** Static method to update the whole steps list */ static void updateStepsList(Doc *doc, Chaser *chaser, ListModel *stepsList); + /** Static method to update the data of a specific step */ + static void updateStepInListModel(Doc *doc, Chaser *chaser, ListModel *stepsList, ChaserStep *step, int index); + /** Return the Chaser step list formatted as explained in m_stepsList */ QVariant stepsList() const; @@ -82,7 +86,19 @@ class ChaserEditor : public FunctionEditor */ Q_INVOKABLE bool moveSteps(QVariantList indicesList, int insertIndex = -1); + /** Duplicate the selected steps at the end of the existing steps + * + * @param indicesList A list of step indices to move + * + * @return true if successful, otherwise false + */ + Q_INVOKABLE bool duplicateSteps(QVariantList indicesList); + + /** @reimp */ + void deleteItems(QVariantList list); + void setSequenceStepValue(SceneValue& scv); + void removeFixtures(QVariantList list); /** Get/Set the Chaser playback start index */ int playbackIndex() const; @@ -91,8 +107,8 @@ class ChaserEditor : public FunctionEditor /** @reimp */ void setPreviewEnabled(bool enable); - /** @reimp */ - void deleteItems(QVariantList list); + Q_INVOKABLE void gotoPreviousStep(); + Q_INVOKABLE void gotoNextStep(); protected: /** Set the steps $param to $value. @@ -100,11 +116,13 @@ class ChaserEditor : public FunctionEditor * otherwise it will be applied to all the steps */ void setSelectedValue(Function::PropType type, QString param, uint value, bool selectedOnly = true); + static QVariantMap stepDataMap(Doc *doc, Chaser *chaser, ChaserStep *step); + static void addStepToListModel(Doc *doc, Chaser *chaser, ListModel *stepsList, ChaserStep *step); protected slots: /** Slot invoked during Chaser playback when the step index changes */ - void slotStepChanged(int index); + void slotStepIndexChanged(int index); signals: void stepsListChanged(); @@ -113,11 +131,13 @@ protected slots: private: /** Reference of the Chaser currently being edited */ Chaser *m_chaser; + /** Reference to a ListModel representing the steps list for the QML UI, * organized as follows: * funcID | isSelected | fadeIn | fadeOut | hold | duration | note */ ListModel *m_stepsList; + /** Index of the current step being played. -1 when stopped */ int m_playbackIndex; diff --git a/qmlui/collectioneditor.cpp b/qmlui/collectioneditor.cpp index 0588f8ea57..c4ac0c0f1e 100644 --- a/qmlui/collectioneditor.cpp +++ b/qmlui/collectioneditor.cpp @@ -51,7 +51,7 @@ bool CollectionEditor::addFunction(quint32 fid, int insertIndex) { if (m_collection != nullptr) { - if(m_collection->addFunction(fid, insertIndex) == true) + if (m_collection->addFunction(fid, insertIndex) == true) { Tardis::instance()->enqueueAction(Tardis::CollectionAddFunction, m_collection->id(), QVariant(), QVariant::fromValue(UIntPair(fid, insertIndex))); diff --git a/qmlui/contextmanager.cpp b/qmlui/contextmanager.cpp index 8bc4a66df7..a5752e4b49 100644 --- a/qmlui/contextmanager.cpp +++ b/qmlui/contextmanager.cpp @@ -28,17 +28,19 @@ #include "functionmanager.h" #include "fixturemanager.h" #include "qlcfixturemode.h" +#include "qlccapability.h" #include "fixtureutils.h" #include "mainviewdmx.h" #include "mainview2d.h" #include "mainview3d.h" -#include "simpledesk.h" +#include "qlcchannel.h" #include "tardis.h" +#include "app.h" #include "doc.h" ContextManager::ContextManager(QQuickView *view, Doc *doc, FixtureManager *fxMgr, - FunctionManager *funcMgr, SimpleDesk *sDesk, + FunctionManager *funcMgr, QObject *parent) : QObject(parent) , m_view(view) @@ -46,7 +48,6 @@ ContextManager::ContextManager(QQuickView *view, Doc *doc, , m_monProps(doc->monitorProperties()) , m_fixtureManager(fxMgr) , m_functionManager(funcMgr) - , m_simpleDesk(sDesk) , m_multipleSelection(false) , m_positionPicking(false) , m_universeFilter(Universe::invalid()) @@ -88,10 +89,6 @@ ContextManager::ContextManager(QQuickView *view, Doc *doc, connect(m_fixtureManager, &FixtureManager::fixtureFlagsChanged, this, &ContextManager::slotFixtureFlagsChanged); connect(m_fixtureManager, &FixtureManager::channelValueChanged, this, &ContextManager::slotChannelValueChanged); - connect(m_simpleDesk, &SimpleDesk::channelValueChanged, this, &ContextManager::slotSimpleDeskValueChanged); - connect(m_fixtureManager, SIGNAL(channelTypeValueChanged(int, quint8)), - this, SLOT(slotChannelTypeValueChanged(int, quint8))); - connect(m_fixtureManager, &FixtureManager::colorChanged, this, &ContextManager::slotColorChanged); connect(m_fixtureManager, &FixtureManager::presetChanged, this, &ContextManager::slotPresetChanged); connect(m_doc->inputOutputMap(), SIGNAL(universeWritten(quint32,QByteArray)), this, SLOT(slotUniverseWritten(quint32,QByteArray))); @@ -227,6 +224,16 @@ QString ContextManager::currentContext() const return m_view->rootObject()->property("currentContext").toString(); } +MainView2D *ContextManager::get2DView() +{ + return m_2DView; +} + +MainView3D *ContextManager::get3DView() +{ + return m_3DView; +} + QVector3D ContextManager::environmentSize() const { return m_monProps->gridSize(); @@ -244,9 +251,9 @@ void ContextManager::setEnvironmentSize(QVector3D environmentSize) m_2DView->setGridSize(environmentSize); if (m_3DView->isEnabled()) { - for(Fixture *fixture : m_doc->fixtures()) // C++11 + for (Fixture *fixture : m_doc->fixtures()) // C++11 { - for (quint32 subID : m_monProps->fixtureIDList(fixture->id())) + for (quint32 &subID : m_monProps->fixtureIDList(fixture->id())) { quint16 headIndex = m_monProps->fixtureHeadIndex(subID); quint16 linkedIndex = m_monProps->fixtureLinkedIndex(subID); @@ -292,9 +299,11 @@ void ContextManager::setPositionPickPoint(QVector3D point) if (positionPicking() == false) return; - point = QVector3D(point.x() + m_monProps->gridSize().x() / 2, point.y(), point.z() + m_monProps->gridSize().z() / 2); + point = QVector3D(point.x() + m_monProps->gridSize().x() / 2, + point.y(), + point.z() + m_monProps->gridSize().z() / 2); - for (quint32 itemID : m_selectedFixtures) + for (quint32 &itemID : m_selectedFixtures) { quint32 fxID = FixtureUtils::itemFixtureID(itemID); @@ -304,6 +313,9 @@ void ContextManager::setPositionPickPoint(QVector3D point) quint32 panMSB = fixture->channelNumber(QLCChannel::Pan, QLCChannel::MSB); quint32 tiltMSB = fixture->channelNumber(QLCChannel::Tilt, QLCChannel::MSB); + int linkedIndex = FixtureUtils::itemLinkedIndex(itemID); + int headIndex = FixtureUtils::itemHeadIndex(itemID); + quint32 itemFlags = m_monProps->fixtureFlags(fxID, headIndex, linkedIndex); // don't even bother if the fixture doesn't have PAN/TILT channels if (panMSB == QLCChannel::invalid() && tiltMSB == QLCChannel::invalid()) @@ -344,13 +356,20 @@ void ContextManager::setPositionPickPoint(QVector3D point) panDeg = 90.0 + (90.0 - panDeg); else if (!xLeft && !zBack) panDeg = 180.0 + panDeg; - else if(!xLeft && zBack) + else if (!xLeft && zBack) panDeg = 270.0 + (90.0 - panDeg); + if (itemFlags & MonitorProperties::InvertedPanFlag) + { + QLCPhysical phy = fixture->fixtureMode()->physical(); + double maxPanDeg = phy.focusPanMax() ? phy.focusPanMax() : 360; + panDeg = maxPanDeg - panDeg; + } + qDebug() << "Fixture" << fxID << "pan degrees:" << panDeg; - QList svList = m_fixtureManager->getFixturePosition(fxID, QLCChannel::Pan, panDeg); - for (SceneValue posSv : svList) + QList svList = fixture->positionToValues(QLCChannel::Pan, panDeg); + for (SceneValue &posSv : svList) { if (m_editingEnabled == false) setDumpValue(posSv.fxi, posSv.channel, posSv.value); @@ -367,7 +386,6 @@ void ContextManager::setPositionPickPoint(QVector3D point) QVector3D ya = QVector3D(res.x(), res.y(), res.z()); qreal tiltDeg = qRadiansToDegrees(qAcos(QVector3D::dotProduct(dir, ya))); - QLCPhysical phy = fixture->fixtureMode()->physical(); // clamp the tilt. @@ -377,12 +395,15 @@ void ContextManager::setPositionPickPoint(QVector3D point) if (tiltDeg > phy.focusTiltMax() / 2) tiltDeg = phy.focusTiltMax() / 2; - tiltDeg = phy.focusTiltMax() / 2 - tiltDeg; + if (itemFlags & MonitorProperties::InvertedTiltFlag) + tiltDeg = phy.focusTiltMax() / 2 + tiltDeg; + else + tiltDeg = phy.focusTiltMax() / 2 - tiltDeg; qDebug() << "Fixture" << fxID << "tilt degrees:" << tiltDeg; - QList svList = m_fixtureManager->getFixturePosition(fxID, QLCChannel::Tilt, tiltDeg); - for (SceneValue posSv : svList) + QList svList = fixture->positionToValues(QLCChannel::Tilt, tiltDeg); + for (SceneValue &posSv : svList) { if (m_editingEnabled == false) setDumpValue(posSv.fxi, posSv.channel, posSv.value); @@ -399,7 +420,7 @@ void ContextManager::resetContexts() { m_channelsMap.clear(); resetDumpValues(); - for (quint32 itemID : m_selectedFixtures) + for (quint32 &itemID : m_selectedFixtures) setFixtureSelection(itemID, -1, false); m_selectedFixtures.clear(); @@ -434,11 +455,14 @@ void ContextManager::handleKeyPress(QKeyEvent *e) case Qt::Key_A: toggleFixturesSelection(); break; + case Qt::Key_P: + setPositionPicking(true); + break; case Qt::Key_R: resetDumpValues(); break; - case Qt::Key_P: - setPositionPicking(true); + case Qt::Key_S: + QMetaObject::invokeMethod(m_view->rootObject(), "saveProject"); break; case Qt::Key_Z: if (e->modifiers() & Qt::ShiftModifier) @@ -452,7 +476,7 @@ void ContextManager::handleKeyPress(QKeyEvent *e) } - for(PreviewContext *context : m_contextsMap.values()) // C++11 + for (PreviewContext *context : m_contextsMap.values()) // C++11 context->handleKeyEvent(e, true); } @@ -466,7 +490,7 @@ void ContextManager::handleKeyRelease(QKeyEvent *e) qDebug() << "Key release event received:" << e->text(); - for(PreviewContext *context : m_contextsMap.values()) // C++11 + for (PreviewContext *context : m_contextsMap.values()) // C++11 context->handleKeyEvent(e, false); } @@ -521,6 +545,7 @@ void ContextManager::setItemSelection(quint32 itemID, bool enable, int keyModifi { setFixtureSelection(itemID, -1, enable); } + m_fixtureManager->setItemRoleData(itemID, enable ? 2 : 0, TreeModel::IsSelectedRole); } void ContextManager::setFixtureSelection(quint32 itemID, int headIndex, bool enable) @@ -555,13 +580,17 @@ void ContextManager::setFixtureSelection(quint32 itemID, int headIndex, bool ena emit dumpValuesCountChanged(); Fixture *fixture = m_doc->fixture(fixtureID); + if (fixture == nullptr) + return; + + m_fixtureManager->setItemRoleData(itemID, enable ? 2 : 0, TreeModel::IsSelectedRole); if (m_DMXView->isEnabled()) m_DMXView->updateFixtureSelection(fixtureID, enable); if (headIndex == -1 && fixture->type() == QLCFixtureDef::Dimmer) { - for (quint32 subID : m_monProps->fixtureIDList(fixtureID)) + for (quint32 &subID : m_monProps->fixtureIDList(fixtureID)) { quint16 hIndex = m_monProps->fixtureHeadIndex(subID); quint16 lIndex = m_monProps->fixtureLinkedIndex(subID); @@ -586,7 +615,7 @@ void ContextManager::setFixtureSelection(quint32 itemID, int headIndex, bool ena } QMultiHash channels = m_fixtureManager->getFixtureCapabilities(itemID, headIndex, enable); - if(channels.keys().isEmpty()) + if (channels.keys().isEmpty()) return; qDebug() << "[ContextManager] found" << channels.keys().count() << "capabilities"; @@ -595,7 +624,7 @@ void ContextManager::setFixtureSelection(quint32 itemID, int headIndex, bool ena #else QMultiHashIterator it(channels); #endif - while(it.hasNext()) + while (it.hasNext()) { it.next(); quint32 chType = it.key(); @@ -616,7 +645,7 @@ void ContextManager::setFixtureSelection(quint32 itemID, int headIndex, bool ena void ContextManager::setFixtureIDSelection(quint32 fixtureID, bool enable) { - for (quint32 subID : m_monProps->fixtureIDList(fixtureID)) + for (quint32 &subID : m_monProps->fixtureIDList(fixtureID)) { quint16 headIndex = m_monProps->fixtureHeadIndex(subID); quint16 linkedIndex = m_monProps->fixtureLinkedIndex(subID); @@ -636,7 +665,7 @@ void ContextManager::resetFixtureSelection() if (fixture == nullptr) continue; - for (quint32 subID : m_monProps->fixtureIDList(fixture->id())) + for (quint32 &subID : m_monProps->fixtureIDList(fixture->id())) { quint16 headIndex = m_monProps->fixtureHeadIndex(subID); quint16 linkedIndex = m_monProps->fixtureLinkedIndex(subID); @@ -651,9 +680,9 @@ void ContextManager::toggleFixturesSelection() bool selectAll = true; int visibleCount = 0; - for (quint32 fixtureID : m_monProps->fixtureItemsID()) + for (quint32 &fixtureID : m_monProps->fixtureItemsID()) { - for (quint32 subID : m_monProps->fixtureIDList(fixtureID)) + for (quint32 &subID : m_monProps->fixtureIDList(fixtureID)) { quint16 headIndex = m_monProps->fixtureHeadIndex(subID); quint16 linkedIndex = m_monProps->fixtureLinkedIndex(subID); @@ -671,7 +700,7 @@ void ContextManager::toggleFixturesSelection() if (fixture == nullptr) continue; - for (quint32 subID : m_monProps->fixtureIDList(fixture->id())) + for (quint32 &subID : m_monProps->fixtureIDList(fixture->id())) { quint16 headIndex = m_monProps->fixtureHeadIndex(subID); quint16 linkedIndex = m_monProps->fixtureLinkedIndex(subID); @@ -691,12 +720,47 @@ void ContextManager::setRectangleSelection(qreal x, qreal y, qreal width, qreal if (m_2DView->isEnabled()) fxIDList = m_2DView->selectFixturesRect(QRectF(x, y, width, height)); - for (quint32 itemID : fxIDList) + for (quint32 itemID : qAsConst(fxIDList)) setFixtureSelection(itemID, -1, true); emit selectedFixturesChanged(); } +QVariantList ContextManager::selectedFixtureAddress() +{ + QVariantList addresses; + for (quint32 &itemID : m_selectedFixtures) + { + quint32 fxID = FixtureUtils::itemFixtureID(itemID); + Fixture *fixture = m_doc->fixture(fxID); + if (fixture == nullptr) + continue; + + quint32 startAddr = fixture->address(); + for (quint32 i = 0; i < fixture->channels(); i++) + addresses.append(startAddr + i); + } + + std::sort(addresses.begin(), addresses.end(), + [](QVariant a, QVariant b) { + return a.toUInt() < b.toUInt(); + }); + + return addresses; +} + +QVariantList ContextManager::selectedFixtureIDVariantList() +{ + QVariantList list; + for (quint32 &itemID : m_selectedFixtures) + { + quint32 fxID = FixtureUtils::itemFixtureID(itemID); + list.append(fxID); + } + + return list; +} + int ContextManager::selectedFixturesCount() { return m_selectedFixtures.count(); @@ -726,7 +790,7 @@ void ContextManager::setFixturePosition(quint32 itemID, qreal x, qreal y, qreal void ContextManager::setFixturesOffset(qreal x, qreal y) { - for (quint32 itemID : m_selectedFixtures) + for (quint32 &itemID : m_selectedFixtures) { quint32 fxID = FixtureUtils::itemFixtureID(itemID); quint16 headIndex = FixtureUtils::itemHeadIndex(itemID); @@ -795,7 +859,7 @@ void ContextManager::setFixturesPosition(QVector3D position) else { // relative position change - for (quint32 itemID : m_selectedFixtures) + for (quint32 &itemID : m_selectedFixtures) { quint32 fxID = FixtureUtils::itemFixtureID(itemID); quint16 headIndex = FixtureUtils::itemHeadIndex(itemID); @@ -816,7 +880,7 @@ void ContextManager::setFixturesPosition(QVector3D position) void ContextManager::setFixturesGelColor(QColor color) { QByteArray ba; - for (quint32 itemID : m_selectedFixtures) + for (quint32 &itemID : m_selectedFixtures) { quint32 fxID = FixtureUtils::itemFixtureID(itemID); quint16 headIndex = FixtureUtils::itemHeadIndex(itemID); @@ -829,6 +893,7 @@ void ContextManager::setFixturesGelColor(QColor color) if (m_3DView->isEnabled()) m_3DView->updateFixtureItem(fixture, headIndex, linkedIndex, ba); } + m_doc->setModified(); } void ContextManager::setFixturesAlignment(int alignment) @@ -841,7 +906,7 @@ void ContextManager::setFixturesAlignment(int alignment) quint16 firstLinkedIndex = FixtureUtils::itemLinkedIndex(m_selectedFixtures.first()); QVector3D firstPos = m_monProps->fixturePosition(firstFxID, firstHeadIndex, firstLinkedIndex); - for (quint32 itemID : m_selectedFixtures) + for (quint32 &itemID : m_selectedFixtures) { quint32 fxID = FixtureUtils::itemFixtureID(itemID); quint16 headIndex = FixtureUtils::itemHeadIndex(itemID); @@ -855,6 +920,7 @@ void ContextManager::setFixturesAlignment(int alignment) if (m_3DView->isEnabled()) m_3DView->updateFixturePosition(itemID, fxPos); } + m_doc->setModified(); } void ContextManager::setFixturesDistribution(int direction) @@ -874,7 +940,7 @@ void ContextManager::setFixturesDistribution(int direction) * 2- sort the fixture IDs from the leftmost/topmost item * 3- detect the minimum and maximum items position */ - for (quint32 itemID : m_selectedFixtures) + for (quint32 &itemID : m_selectedFixtures) { quint32 fxID = FixtureUtils::itemFixtureID(itemID); quint16 headIndex = FixtureUtils::itemHeadIndex(itemID); @@ -971,6 +1037,7 @@ void ContextManager::setFixturesDistribution(int direction) newPos += size + gap; } + m_doc->setModified(); } void ContextManager::setLinkedFixture(quint32 itemID) @@ -1004,7 +1071,7 @@ void ContextManager::setLinkedFixture(quint32 itemID) return; // 1- iterate through Fixture subitems to find a new linked index - for (quint32 subID : m_monProps->fixtureIDList(fixtureID)) + for (quint32 &subID : m_monProps->fixtureIDList(fixtureID)) { quint16 hIdx = m_monProps->fixtureHeadIndex(subID); quint16 lIdx = m_monProps->fixtureLinkedIndex(subID); @@ -1023,7 +1090,7 @@ void ContextManager::setLinkedFixture(quint32 itemID) pos.setY(pos.y() + phy.height() + 50); // 3- add the new item to monitor properties - QString newName = QString("%1 (%2 %3)").arg(fixture->name()).arg(tr("linked")).arg(newIndex); + QString newName = QString("%1 (%2 %3)").arg(fixture->name(), tr("linked")).arg(newIndex); m_monProps->setFixturePosition(fixtureID, headIndex, newIndex, pos); m_monProps->setFixtureName(fixtureID, headIndex, newIndex, newName); @@ -1041,10 +1108,136 @@ void ContextManager::setLinkedFixture(quint32 itemID) void ContextManager::updateFixturesCapabilities() { - for (quint32 itemID : m_selectedFixtures) + for (quint32 &itemID : m_selectedFixtures) m_fixtureManager->getFixtureCapabilities(itemID, -1, true); } +qreal ContextManager::getCurrentValue(int type, bool degrees) +{ + qreal currMsbValue = -1; + qreal currLsbValue = -1; + qreal currValue = -1; + + QList svList = m_channelsMap.values(type); + for (SceneValue &sv : svList) + { + Fixture *fixture = m_doc->fixture(sv.fxi); + if (fixture == nullptr) + continue; + + const QLCChannel *ch = fixture->channel(sv.channel); + if (ch == nullptr) + continue; + + qreal chValue = fixture->channelValueAt(sv.channel); + qreal divider = ch->controlByte() == QLCChannel::MSB ? 256.0 : 65536.0; + + if (degrees) + { + QLCFixtureMode *fxMode = fixture->fixtureMode(); + QLCPhysical phy = fxMode->physical(); + switch (type) + { + case QLCChannel::Pan: + chValue = (qreal(phy.focusPanMax()) / divider) * chValue; + break; + case QLCChannel::Tilt: + chValue = (qreal(phy.focusTiltMax()) / divider) * chValue; + break; + case QLCChannel::Beam: + chValue = qreal((phy.lensDegreesMax() - phy.lensDegreesMin()) / divider) * chValue; + if (ch->controlByte() == QLCChannel::MSB) + chValue += phy.lensDegreesMin(); + break; + } + } + + if (ch->controlByte() == QLCChannel::MSB) + { + if (currMsbValue != -1 && currMsbValue != chValue) + return -1; + + currMsbValue = chValue; + } + else if (ch->controlByte() == QLCChannel::LSB) + { + if (currLsbValue != -1 && currLsbValue != chValue) + return -1; + + currLsbValue = chValue; + } + } + + qDebug() << "Channel type" << type << "MSB" << currMsbValue << "LSB" << currLsbValue; + currValue = currMsbValue + (currLsbValue == -1 ? 0 : currLsbValue); + + return currValue; +} + +void ContextManager::getCurrentColors(QQuickItem *item) +{ + int rgbDiffCount = 0; + int wauvDiffCount = 0; + QColor rgbColor; + QColor wauvColor; + + for (quint32 &itemID : m_selectedFixtures) + { + quint32 fxID = FixtureUtils::itemFixtureID(itemID); + quint16 headIndex = FixtureUtils::itemHeadIndex(itemID); + + Fixture *fixture = m_doc->fixture(fxID); + if (fixture == nullptr) + continue; + + QColor itemRgbColor; + QColor itemWauvColor; + + QVector rgbCh = fixture->rgbChannels(headIndex); + if (rgbCh.size() == 3) + { + itemRgbColor.setRgb(fixture->channelValueAt(rgbCh.at(0)), + fixture->channelValueAt(rgbCh.at(1)), + fixture->channelValueAt(rgbCh.at(2))); + } + + QVector cmyCh = fixture->cmyChannels(headIndex); + if (cmyCh.size() == 3) + { + itemRgbColor.setCmyk(fixture->channelValueAt(cmyCh.at(0)), + fixture->channelValueAt(cmyCh.at(1)), + fixture->channelValueAt(cmyCh.at(2)), 0); + } + + if (rgbDiffCount == 0 || itemRgbColor == rgbColor) + rgbColor = itemRgbColor; + else + rgbDiffCount++; + + quint32 white = fixture->channelNumber(QLCChannel::White, QLCChannel::MSB, headIndex); + quint32 amber = fixture->channelNumber(QLCChannel::Amber, QLCChannel::MSB, headIndex); + quint32 UV = fixture->channelNumber(QLCChannel::UV, QLCChannel::MSB, headIndex); + + if (white != QLCChannel::invalid()) + itemWauvColor.setRed(fixture->channelValueAt(white)); + if (amber != QLCChannel::invalid()) + itemWauvColor.setGreen(fixture->channelValueAt(amber)); + if (UV != QLCChannel::invalid()) + itemWauvColor.setBlue(fixture->channelValueAt(UV)); + + if (wauvDiffCount == 0 || itemWauvColor == wauvColor) + wauvColor = itemWauvColor; + else + wauvDiffCount++; + } + + QMetaObject::invokeMethod(item, "updateColors", + Q_ARG(QVariant, rgbDiffCount ? false : true), + Q_ARG(QVariant, rgbColor), + Q_ARG(QVariant, wauvDiffCount ? false : true), + Q_ARG(QVariant, wauvColor)); +} + void ContextManager::createFixtureGroup() { if (m_selectedFixtures.isEmpty()) @@ -1074,26 +1267,31 @@ void ContextManager::setFixturesRotation(QVector3D degrees) { if (m_selectedFixtures.count() == 1) { - quint32 fxID = FixtureUtils::itemFixtureID(m_selectedFixtures.first()); - quint16 headIndex = FixtureUtils::itemHeadIndex(m_selectedFixtures.first()); - quint16 linkedIndex = FixtureUtils::itemLinkedIndex(m_selectedFixtures.first()); + quint32 itemID = m_selectedFixtures.first(); + quint32 fxID = FixtureUtils::itemFixtureID(itemID); + quint16 headIndex = FixtureUtils::itemHeadIndex(itemID); + quint16 linkedIndex = FixtureUtils::itemLinkedIndex(itemID); + QVector3D rotation = m_monProps->fixtureRotation(fxID, headIndex, linkedIndex); + + Tardis::instance()->enqueueAction(Tardis::FixtureSetRotation, itemID, QVariant(rotation), QVariant(degrees)); // absolute rotation change m_monProps->setFixtureRotation(fxID, headIndex, linkedIndex, degrees); if (m_2DView->isEnabled()) - m_2DView->updateFixtureRotation(m_selectedFixtures.first(), degrees); + m_2DView->updateFixtureRotation(itemID, degrees); if (m_3DView->isEnabled()) - m_3DView->updateFixtureRotation(m_selectedFixtures.first(), degrees); + m_3DView->updateFixtureRotation(itemID, degrees); } else { // relative rotation change - for (quint32 itemID : m_selectedFixtures) + for (quint32 &itemID : m_selectedFixtures) { quint32 fxID = FixtureUtils::itemFixtureID(itemID); quint16 headIndex = FixtureUtils::itemHeadIndex(itemID); quint16 linkedIndex = FixtureUtils::itemLinkedIndex(itemID); - QVector3D newRot = m_monProps->fixtureRotation(fxID, headIndex, linkedIndex) + degrees; + QVector3D rotation = m_monProps->fixtureRotation(fxID, headIndex, linkedIndex); + QVector3D newRot = rotation + degrees; // normalize back to a 0-359 range if (newRot.x() < 0) newRot.setX(newRot.x() + 360); @@ -1105,6 +1303,8 @@ void ContextManager::setFixturesRotation(QVector3D degrees) if (newRot.z() < 0) newRot.setZ(newRot.z() + 360); else if (newRot.z() >= 360) newRot.setZ(newRot.z() - 360); + Tardis::instance()->enqueueAction(Tardis::FixtureSetRotation, itemID, QVariant(rotation), QVariant(newRot)); + m_monProps->setFixtureRotation(fxID, headIndex, linkedIndex, newRot); if (m_2DView->isEnabled()) m_2DView->updateFixtureRotation(itemID, newRot); @@ -1116,6 +1316,23 @@ void ContextManager::setFixturesRotation(QVector3D degrees) emit fixturesRotationChanged(); } +void ContextManager::setFixtureRotation(quint32 itemID, QVector3D degrees) +{ + quint32 fxID = FixtureUtils::itemFixtureID(itemID); + quint16 headIndex = FixtureUtils::itemHeadIndex(itemID); + quint16 linkedIndex = FixtureUtils::itemLinkedIndex(itemID); + QVector3D rotation = m_monProps->fixtureRotation(fxID, headIndex, linkedIndex); + + Tardis::instance()->enqueueAction(Tardis::FixtureSetRotation, itemID, QVariant(rotation), QVariant(degrees)); + + // absolute rotation change + m_monProps->setFixtureRotation(fxID, headIndex, linkedIndex, degrees); + if (m_2DView->isEnabled()) + m_2DView->updateFixtureRotation(itemID, degrees); + if (m_3DView->isEnabled()) + m_3DView->updateFixtureRotation(itemID, degrees); +} + void ContextManager::setFixtureGroupSelection(quint32 id, bool enable, bool isUniverse) { if (isUniverse) @@ -1124,7 +1341,7 @@ void ContextManager::setFixtureGroupSelection(quint32 id, bool enable, bool isUn { if (fixture->universe() == id) { - for (quint32 subID : m_monProps->fixtureIDList(fixture->id())) + for (quint32 &subID : m_monProps->fixtureIDList(fixture->id())) { quint16 headIndex = m_monProps->fixtureHeadIndex(subID); quint16 linkedIndex = m_monProps->fixtureLinkedIndex(subID); @@ -1140,13 +1357,13 @@ void ContextManager::setFixtureGroupSelection(quint32 id, bool enable, bool isUn if (group == nullptr) return; - for (quint32 fxID : group->fixtureList()) + for (quint32 &fxID : group->fixtureList()) { Fixture *fixture = m_doc->fixture(fxID); if (fixture == nullptr) continue; - for (quint32 subID : m_monProps->fixtureIDList(fxID)) + for (quint32 &subID : m_monProps->fixtureIDList(fxID)) { quint16 headIndex = m_monProps->fixtureHeadIndex(subID); quint16 linkedIndex = m_monProps->fixtureLinkedIndex(subID); @@ -1164,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()) @@ -1205,53 +1424,72 @@ void ContextManager::slotChannelValueChanged(quint32 fxID, quint32 channel, quin m_functionManager->setChannelValue(fxID, channel, uchar(value)); } -void ContextManager::slotChannelTypeValueChanged(int type, quint8 value, quint32 channel) +void ContextManager::setColorValue(QColor col, QColor wauv) +{ + setChannelValueByType((int)QLCChannel::Red, col.red()); + setChannelValueByType((int)QLCChannel::Green, col.green()); + setChannelValueByType((int)QLCChannel::Blue, col.blue()); + + setChannelValueByType((int)QLCChannel::White, wauv.red()); + setChannelValueByType((int)QLCChannel::Amber, wauv.green()); + setChannelValueByType((int)QLCChannel::UV, wauv.blue()); + + QColor cmykColor = col.toCmyk(); + setChannelValueByType((int)QLCChannel::Cyan, cmykColor.cyan()); + setChannelValueByType((int)QLCChannel::Magenta, cmykColor.magenta()); + setChannelValueByType((int)QLCChannel::Yellow, cmykColor.yellow()); +} + +void ContextManager::setChannelValueByType(int type, int value, bool isRelative, quint32 channel) { - //qDebug() << "type:" << type << "value:" << value << "channel:" << channel; + //qDebug() << "[setChannelValueByType] type:" << type << "value:" << value << "relative:" << isRelative << "channel:" << channel; QList svList = m_channelsMap.values(type); - for (SceneValue sv : svList) + for (SceneValue &sv : svList) { if (channel == UINT_MAX || channel == sv.channel) { + uchar val = value; + + if (isRelative) + { + Fixture *fixture = m_doc->fixture(sv.fxi); + if (fixture == nullptr) + continue; + + const QLCChannel *ch = fixture->channel(sv.channel); + if (ch == nullptr) + continue; + + val = qBound(0, fixture->channelValueAt(sv.channel) + value, 255); + } + if (m_editingEnabled == false) - setDumpValue(sv.fxi, sv.channel, uchar(value)); + setDumpValue(sv.fxi, sv.channel, val); else - m_functionManager->setChannelValue(sv.fxi, sv.channel, uchar(value)); + m_functionManager->setChannelValue(sv.fxi, sv.channel, val); } } } -void ContextManager::slotColorChanged(QColor col, QColor wauv) -{ - slotChannelTypeValueChanged((int)QLCChannel::Red, (quint8)col.red()); - slotChannelTypeValueChanged((int)QLCChannel::Green, (quint8)col.green()); - slotChannelTypeValueChanged((int)QLCChannel::Blue, (quint8)col.blue()); - - slotChannelTypeValueChanged((int)QLCChannel::White, (quint8)wauv.red()); - slotChannelTypeValueChanged((int)QLCChannel::Amber, (quint8)wauv.green()); - slotChannelTypeValueChanged((int)QLCChannel::UV, (quint8)wauv.blue()); - - QColor cmykColor = col.toCmyk(); - slotChannelTypeValueChanged((int)QLCChannel::Cyan, (quint8)cmykColor.cyan()); - slotChannelTypeValueChanged((int)QLCChannel::Magenta, (quint8)cmykColor.magenta()); - slotChannelTypeValueChanged((int)QLCChannel::Yellow, (quint8)cmykColor.yellow()); -} - -void ContextManager::setPositionValue(int type, int degrees) +void ContextManager::setPositionValue(int type, int degrees, bool isRelative) { // list to keep track of the already processed Fixture IDs QListfxIDs; QList typeList = m_channelsMap.values(type); - for (SceneValue sv : typeList) + for (SceneValue &sv : typeList) { if (fxIDs.contains(sv.fxi) == true) continue; fxIDs.append(sv.fxi); - QList svList = m_fixtureManager->getFixturePosition(sv.fxi, type, degrees); - for (SceneValue posSv : svList) + Fixture *fixture = m_doc->fixture(sv.fxi); + if (fixture == nullptr || fixture->fixtureMode() == nullptr) + continue; + + QList svList = fixture->positionToValues(type, degrees, isRelative); + for (SceneValue &posSv : svList) { if (m_editingEnabled == false) setDumpValue(posSv.fxi, posSv.channel, posSv.value); @@ -1261,21 +1499,31 @@ void ContextManager::setPositionValue(int type, int degrees) } } -void ContextManager::setBeamDegrees(float degrees) +void ContextManager::setPositionCenter() +{ + setChannelValueByType((int)QLCChannel::Pan, 127); + setChannelValueByType((int)QLCChannel::Tilt, 127); +} + +void ContextManager::setBeamDegrees(float degrees, bool isRelative) { // list to keep track of the already processed Fixture IDs QListfxIDs; QList typeList = m_channelsMap.values(QLCChannel::Beam); - for (SceneValue sv : typeList) + for (SceneValue &sv : typeList) { if (fxIDs.contains(sv.fxi) == true) continue; fxIDs.append(sv.fxi); - QList svList = m_fixtureManager->getFixtureZoom(sv.fxi, degrees); - for (SceneValue zSv : svList) + Fixture *fixture = m_doc->fixture(sv.fxi); + if (fixture == nullptr || fixture->fixtureMode() == nullptr) + continue; + + QList svList = fixture->zoomToValues(degrees, isRelative); + for (SceneValue &zSv : svList) { if (m_editingEnabled == false) setDumpValue(zSv.fxi, zSv.channel, zSv.value); @@ -1285,9 +1533,45 @@ void ContextManager::setBeamDegrees(float degrees) } } +void ContextManager::highlightFixtureSelection() +{ + setChannelValueByType((int)QLCChannel::Red, UCHAR_MAX); + setChannelValueByType((int)QLCChannel::Green, UCHAR_MAX); + setChannelValueByType((int)QLCChannel::Blue, UCHAR_MAX); + setChannelValueByType((int)QLCChannel::White, UCHAR_MAX); + + setChannelValueByType((int)QLCChannel::Intensity, UCHAR_MAX); + + // search for shutter open and lamp on + for (quint32 &itemID : m_selectedFixtures) + { + quint32 fxID = FixtureUtils::itemFixtureID(itemID); + Fixture *fixture = m_doc->fixture(fxID); + if (fixture == nullptr) + continue; + + for (quint32 i = 0; i < fixture->channels(); i++) + { + const QLCChannel *channel = fixture->channel(i); + for (QLCCapability *cap : channel->capabilities()) + { + if (cap->preset() == QLCCapability::ShutterOpen || + cap->preset() == QLCCapability::LampOn) + { + if (m_editingEnabled == false) + setDumpValue(fxID, i, cap->middle()); + else + m_functionManager->setChannelValue(fxID, i, cap->middle()); + break; + } + } + } + } +} + void ContextManager::setChannelValues(QList values) { - for (SceneValue sv : values) + for (SceneValue &sv : values) { if (m_editingEnabled == false) setDumpValue(sv.fxi, sv.channel, sv.value); @@ -1298,7 +1582,7 @@ void ContextManager::setChannelValues(QList values) void ContextManager::slotPresetChanged(const QLCChannel *channel, quint8 value) { - for (quint32 itemID : m_selectedFixtures) + for (quint32 &itemID : m_selectedFixtures) { quint32 fixtureID = FixtureUtils::itemFixtureID(itemID); Fixture *fixture = m_doc->fixture(fixtureID); @@ -1309,7 +1593,7 @@ void ContextManager::slotPresetChanged(const QLCChannel *channel, quint8 value) { quint32 chIdx = fixture->fixtureMode()->channelNumber((QLCChannel *)channel); if (chIdx != QLCChannel::invalid()) - slotChannelTypeValueChanged((int)channel->group(), value, chIdx); + setChannelValueByType((int)channel->group(), value, false, chIdx); } } } @@ -1344,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; } @@ -1361,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) { @@ -1385,9 +1672,9 @@ void ContextManager::setDumpValue(quint32 fxID, quint32 channel, uchar value, bo if (ch->group() == QLCChannel::Intensity) { if (ch->colour() == QLCChannel::NoColour) - m_dumpChannelMask |= DimmerType; + m_dumpChannelMask |= App::DimmerType; else - m_dumpChannelMask |= ColorType; + m_dumpChannelMask |= App::ColorType; } else { @@ -1461,7 +1748,7 @@ void ContextManager::dumpDmxChannels(quint32 sceneID, quint32 mask) void ContextManager::resetDumpValues() { - for (SceneValue sv : m_dumpValues) + for (SceneValue &sv : m_dumpValues) m_source->unset(sv.fxi, sv.channel); m_source->unsetAll(); @@ -1477,3 +1764,4 @@ GenericDMXSource *ContextManager::dmxSource() const { return m_source; } + diff --git a/qmlui/contextmanager.h b/qmlui/contextmanager.h index 6d242007de..e0aa068c97 100644 --- a/qmlui/contextmanager.h +++ b/qmlui/contextmanager.h @@ -56,7 +56,7 @@ class ContextManager : public QObject public: explicit ContextManager(QQuickView *view, Doc *doc, FixtureManager *fxMgr, FunctionManager *funcMgr, - SimpleDesk *sDesk, QObject *parent = 0); + QObject *parent = 0); ~ContextManager(); /** Register/Unregister a context to the map of known contexts */ @@ -79,6 +79,9 @@ class ContextManager : public QObject /** Return the currently active context */ QString currentContext() const; + MainView2D *get2DView(); + MainView3D *get3DView(); + /** Get/Set the environment width/height/depth size */ QVector3D environmentSize() const; void setEnvironmentSize(QVector3D environmentSize); @@ -130,8 +133,6 @@ public slots: FixtureManager *m_fixtureManager; /** Reference to the Function Manager */ FunctionManager *m_functionManager; - /** Reference to the Simple Desk context */ - SimpleDesk *m_simpleDesk; QMap m_contextsMap; @@ -178,7 +179,13 @@ public slots: /** Select the fixtures that intersects the provided rectangle coordinates in a 2D environment */ Q_INVOKABLE void setRectangleSelection(qreal x, qreal y, qreal width, qreal height, int keyModifiers); - /** Returns if at least one fixture is currently selected */ + /** Returns a list of the selected fixture addresses */ + Q_INVOKABLE QVariantList selectedFixtureAddress(); + + /** Returns a list of the selected Fixture IDs as QVariant */ + Q_INVOKABLE QVariantList selectedFixtureIDVariantList(); + + /** Returns the number of currently selected fixtures */ int selectedFixturesCount(); /** Returns if the fixture with $fxID is currently selected */ @@ -208,20 +215,38 @@ public slots: Q_INVOKABLE void updateFixturesCapabilities(); + /** Get the DMX/degrees value of the current fixture selection + * for the requested channel type. + * Returns -1 in case of mixed values */ + Q_INVOKABLE qreal getCurrentValue(int type, bool degrees); + + /** Get the RGB color of the current fixture selection */ + Q_INVOKABLE void getCurrentColors(QQuickItem *item); + Q_INVOKABLE void createFixtureGroup(); /** Set/Get the rotation of the currently selected fixtures */ QVector3D fixturesRotation() const; void setFixturesRotation(QVector3D degrees); + void setFixtureRotation(quint32 itemID, QVector3D degrees); /** Select/Deselect all the fixtures of the Group/Universe with the provided $id */ Q_INVOKABLE void setFixtureGroupSelection(quint32 id, bool enable, bool isUniverse); + Q_INVOKABLE void setChannelValueByType(int type, int value, bool isRelative = false, quint32 channel = UINT_MAX); + + Q_INVOKABLE void setColorValue(QColor col, QColor wauv); + /** Set a Pan/Tilt position in degrees */ - Q_INVOKABLE void setPositionValue(int type, int degrees); + Q_INVOKABLE void setPositionValue(int type, int degrees, bool isRelative); + + /** Set Pan/Tilt values at half position */ + Q_INVOKABLE void setPositionCenter(); /** Set a zoom channel in degrees */ - Q_INVOKABLE void setBeamDegrees(float degrees); + Q_INVOKABLE void setBeamDegrees(float degrees, bool isRelative); + + Q_INVOKABLE void highlightFixtureSelection(); void setChannelValues(QList values); @@ -231,9 +256,6 @@ protected slots: void slotFixtureFlagsChanged(quint32 itemID, quint32 flags); void slotChannelValueChanged(quint32 fxID, quint32 channel, quint8 value); - void slotChannelTypeValueChanged(int type, quint8 value, quint32 channel = UINT_MAX); - void slotColorChanged(QColor col, QColor wauv); - void slotPresetChanged(const QLCChannel *channel, quint8 value); void slotSimpleDeskValueChanged(quint32 fxID, quint32 channel, quint8 value); @@ -268,23 +290,6 @@ protected slots: * DMX channels dump *********************************************************************/ public: - enum ChannelType - { - DimmerType = (1 << QLCChannel::Intensity), - ColorMacroType = (1 << QLCChannel::Colour), // Color wheels, color macros - GoboType = (1 << QLCChannel::Gobo), - SpeedType = (1 << QLCChannel::Speed), - PanType = (1 << QLCChannel::Pan), - TiltType = (1 << QLCChannel::Tilt), - ShutterType = (1 << QLCChannel::Shutter), - PrismType = (1 << QLCChannel::Prism), - BeamType = (1 << QLCChannel::Beam), - EffectType = (1 << QLCChannel::Effect), - MaintenanceType = (1 << QLCChannel::Maintenance), - ColorType = (1 << (QLCChannel::Maintenance + 1)) // RGB/CMY/WAUV - }; - Q_ENUM(ChannelType) - /** Store a channel value for Scene dumping */ Q_INVOKABLE void setDumpValue(quint32 fxID, quint32 channel, uchar value, bool output = true); diff --git a/qmlui/efxeditor.cpp b/qmlui/efxeditor.cpp index 5fef69644c..24283c9a65 100644 --- a/qmlui/efxeditor.cpp +++ b/qmlui/efxeditor.cpp @@ -41,7 +41,7 @@ EFXEditor::EFXEditor(QQuickView *view, Doc *doc, QObject *parent) m_fixtureList = new ListModel(this); QStringList listRoles; - listRoles << "name" << "fxID" << "head" << "isSelected" << "reverse" << "offset"; + listRoles << "name" << "fxID" << "head" << "isSelected" << "mode" << "reverse" << "offset"; m_fixtureList->setRoleNames(listRoles); } @@ -457,31 +457,29 @@ void EFXEditor::addFixture(QVariant reference) return; Fixture *fixture = reference.value(); + int count = 0; for (int headIdx = 0; headIdx < fixture->heads(); headIdx++) { - quint32 panCh = fixture->channelNumber(QLCChannel::Pan, QLCChannel::MSB, headIdx); - quint32 tiltCh = fixture->channelNumber(QLCChannel::Tilt, QLCChannel::MSB, headIdx); + EFXFixture *ef = new EFXFixture(m_efx); + GroupHead head(fixture->id(), headIdx); + ef->setHead(head); - if (panCh != QLCChannel::invalid() || tiltCh != QLCChannel::invalid()) + if (m_efx->addFixture(ef) == false) { - EFXFixture *ef = new EFXFixture(m_efx); - GroupHead head(fixture->id(), headIdx); - ef->setHead(head); - - if (m_efx->addFixture(ef) == false) - { - delete ef; - } - else - { - Tardis::instance()->enqueueAction(Tardis::EFXAddFixture, m_efx->id(), QVariant(), - Tardis::instance()->actionToByteArray(Tardis::EFXAddFixture, m_efx->id(), - QVariant::fromValue((void *)ef))); - updateFixtureList(); - } + delete ef; + } + else + { + Tardis::instance()->enqueueAction(Tardis::EFXAddFixture, m_efx->id(), QVariant(), + Tardis::instance()->actionToByteArray(Tardis::EFXAddFixture, m_efx->id(), + QVariant::fromValue((void *)ef))); + count++; } } + + if (count) + updateFixtureList(); } void EFXEditor::addHead(int fixtureID, int headIndex) @@ -506,6 +504,44 @@ void EFXEditor::addHead(int fixtureID, int headIndex) } } +void EFXEditor::removeHeads(QVariantList heads) +{ + if (m_efx == nullptr) + return; + + for (QVariant vIdx : heads) + { + QModelIndex idx = m_fixtureList->index(vIdx.toInt(), 0, QModelIndex()); + QVariant fixtureID = m_fixtureList->data(idx, "fxID"); + QVariant headIndex = m_fixtureList->data(idx, "head"); + + qDebug() << "Removing fixture" << fixtureID << "head" << headIndex; + + EFXFixture *ef = m_efx->fixture(fixtureID.toUInt(), headIndex.toInt()); + if (ef == nullptr) + continue; + + Tardis::instance()->enqueueAction(Tardis::EFXRemoveFixture, m_efx->id(), + Tardis::instance()->actionToByteArray(Tardis::EFXAddFixture, m_efx->id(), + QVariant::fromValue((void *)ef)), + QVariant()); + + m_efx->removeFixture(ef); + } + updateFixtureList(); +} + +void EFXEditor::setFixtureMode(quint32 fixtureID, int headIndex, int modeIndex) +{ + if (m_efx == nullptr) + return; + + EFXFixture *ef = m_efx->fixture(fixtureID, headIndex); + + if (ef != nullptr) + ef->setMode(EFXFixture::Mode(modeIndex)); +} + void EFXEditor::setFixtureReversed(quint32 fixtureID, int headIndex, bool reversed) { if (m_efx == nullptr) @@ -544,7 +580,7 @@ void EFXEditor::updateFixtureList() qreal oldPanDegrees = m_maxPanDegrees; qreal oldTiltDegrees = m_maxTiltDegrees; - // listRoles << "name" << "fxID" << "head" << "isSelected" << "reverse" << "offset"; + // listRoles << "name" << "fxID" << "head" << "isSelected" << "mode" << "reverse" << "offset"; for (EFXFixture *ef : m_efx->fixtures()) // C++11 { @@ -568,6 +604,7 @@ void EFXEditor::updateFixtureList() fxMap.insert("fxID", head.fxi); fxMap.insert("head", head.head); fxMap.insert("isSelected", false); + fxMap.insert("mode", ef->mode()); fxMap.insert("reverse", ef->direction() == Function::Backward ? true : false); fxMap.insert("offset", ef->startOffset()); diff --git a/qmlui/efxeditor.h b/qmlui/efxeditor.h index e15de560eb..8fb86fe904 100644 --- a/qmlui/efxeditor.h +++ b/qmlui/efxeditor.h @@ -157,6 +157,10 @@ protected slots: Q_INVOKABLE void addHead(int fixtureID, int headIndex); + Q_INVOKABLE void removeHeads(QVariantList heads); + + Q_INVOKABLE void setFixtureMode(quint32 fixtureID, int headIndex, int modeIndex); + Q_INVOKABLE void setFixtureReversed(quint32 fixtureID, int headIndex, bool reversed); Q_INVOKABLE void setFixtureOffset(quint32 fixtureID, int headIndex, int offset); diff --git a/qmlui/fixturebrowser.cpp b/qmlui/fixturebrowser.cpp index 9bcd333f42..ce4d99f33d 100644 --- a/qmlui/fixturebrowser.cpp +++ b/qmlui/fixturebrowser.cpp @@ -39,7 +39,6 @@ FixtureBrowser::FixtureBrowser(QQuickView *view, Doc *doc, QObject *parent) , m_fixtureName(QString()) , m_selectedMode(QString()) , m_modeChannelsCount(1) - , m_definition(nullptr) , m_mode(nullptr) , m_searchFilter(QString()) { @@ -150,12 +149,12 @@ QStringList FixtureBrowser::modesList() QStringList modesList; - m_definition = m_doc->fixtureDefCache()->fixtureDef(m_selectedManufacturer, m_selectedModel); + QLCFixtureDef *definition = fixtureDefinition(); - if (m_definition != nullptr) + if (definition != nullptr) { - QList fxModesList = m_definition->modes(); - foreach(QLCFixtureMode *mode, fxModesList) + QList fxModesList = definition->modes(); + foreach (QLCFixtureMode *mode, fxModesList) { modesList.append(mode->name()); if (m_selectedMode.isEmpty()) @@ -181,10 +180,11 @@ void FixtureBrowser::setSelectedMode(QString selectedMode) return; m_selectedMode = selectedMode; + QLCFixtureDef *definition = fixtureDefinition(); - if (m_definition != nullptr) + if (definition != nullptr) { - m_mode = m_definition->mode(m_selectedMode); + m_mode = definition->mode(m_selectedMode); if (m_mode) m_modeChannelsCount = m_mode->channels().count(); } @@ -195,9 +195,11 @@ void FixtureBrowser::setSelectedMode(QString selectedMode) int FixtureBrowser::modeChannelsCount() { - if (m_definition != nullptr) + QLCFixtureDef *definition = fixtureDefinition(); + + if (definition != nullptr) { - m_mode = m_definition->mode(m_selectedMode); + m_mode = definition->mode(m_selectedMode); if (m_mode != nullptr) return m_mode->channels().count(); @@ -278,7 +280,7 @@ int FixtureBrowser::availableChannel(quint32 uniIdx, int channels, int quantity, absAddress = uniFilter << 9; for (int i = 0; i < 512; i++) { - if(m_doc->fixtureForAddress(absAddress + i) != Fixture::invalidId()) + if (m_doc->fixtureForAddress(absAddress + i) != Fixture::invalidId()) { freeCounter = 0; validAddr = i + 1; @@ -312,7 +314,7 @@ int FixtureBrowser::availableChannel(quint32 fixtureID, int requested) for (quint32 i = 0; i < channels; i++) { quint32 fxIDOnAddr = m_doc->fixtureForAddress(absAddress + i); - if(fxIDOnAddr != Fixture::invalidId() && fxIDOnAddr != fixtureID) + if (fxIDOnAddr != Fixture::invalidId() && fxIDOnAddr != fixtureID) { isAvailable = false; break; @@ -368,16 +370,17 @@ void FixtureBrowser::updateSearchTree() QStringList mfList = m_doc->fixtureDefCache()->manufacturers(); mfList.sort(); + QString searchFilter = m_searchFilter.toLower(); - for(QString manufacturer : mfList) // C++11 + for (QString &manufacturer : mfList) // C++11 { QStringList modelsList = m_doc->fixtureDefCache()->models(manufacturer); modelsList.sort(); - for(QString model : modelsList) + for (QString &model : modelsList) { - if (manufacturer.toLower().contains(m_searchFilter) || - model.toLower().contains(m_searchFilter)) + if (manufacturer.toLower().contains(searchFilter) || + model.toLower().contains(searchFilter)) { QVariantList params; TreeModelItem *item = m_searchTree->addItem(model, params, manufacturer); @@ -388,3 +391,8 @@ void FixtureBrowser::updateSearchTree() emit searchListChanged(); } +QLCFixtureDef *FixtureBrowser::fixtureDefinition() +{ + return m_doc->fixtureDefCache()->fixtureDef(m_selectedManufacturer, m_selectedModel); +} + diff --git a/qmlui/fixturebrowser.h b/qmlui/fixturebrowser.h index 1142b91791..f12b531e29 100644 --- a/qmlui/fixturebrowser.h +++ b/qmlui/fixturebrowser.h @@ -118,6 +118,7 @@ class FixtureBrowser : public QObject private: void updateSearchTree(); + QLCFixtureDef *fixtureDefinition(); private: Doc *m_doc; @@ -138,8 +139,6 @@ class FixtureBrowser : public QObject /** The currently selected mode channels number. * If no mode is available this can be defined by the user */ int m_modeChannelsCount; - /** Reference of the currently selected fixture definition */ - QLCFixtureDef *m_definition; /** Reference of the currently selected fixture mode */ QLCFixtureMode *m_mode; /** Reference to the tree model used for searches */ diff --git a/qmlui/fixtureeditor/channeledit.cpp b/qmlui/fixtureeditor/channeledit.cpp index a1c95f2a7c..0afb686a64 100644 --- a/qmlui/fixtureeditor/channeledit.cpp +++ b/qmlui/fixtureeditor/channeledit.cpp @@ -17,6 +17,8 @@ limitations under the License. */ +#include + #include "qlcfixturedef.h" #include "qlccapability.h" @@ -29,11 +31,12 @@ ChannelEdit::ChannelEdit(QLCChannel *channel, QObject *parent) if (m_channel->capabilities().count() == 0) { QLCCapability *cap = new QLCCapability(0, UCHAR_MAX); + QQmlEngine::setObjectOwnership(cap, QQmlEngine::CppOwnership); cap->setWarning(QLCCapability::EmptyName); m_channel->addCapability(cap); } - connect(m_channel, SIGNAL(presetChanged()), this, SIGNAL(channelChanged())); + connect(m_channel, SIGNAL(presetChanged()), this, SLOT(setupPreset())); connect(m_channel, SIGNAL(nameChanged()), this, SIGNAL(channelChanged())); connect(m_channel, SIGNAL(defaultValueChanged()), this, SIGNAL(channelChanged())); connect(m_channel, SIGNAL(controlByteChanged()), this, SIGNAL(channelChanged())); @@ -177,12 +180,30 @@ void ChannelEdit::updateCapabilities() emit capabilitiesChanged(); } +void ChannelEdit::setupPreset() +{ + if (m_channel->preset() == QLCChannel::Custom) + { + emit channelChanged(); + return; + } + + for (QLCCapability *cap : m_channel->capabilities()) + m_channel->removeCapability(cap); + + m_channel->addPresetCapability(); + + updateCapabilities(); + + emit channelChanged(); +} + QVariantList ChannelEdit::capabilities() const { return m_capabilities; } -QLCCapability *ChannelEdit::addCapability() +QLCCapability *ChannelEdit::addNewCapability() { int min = 0; if (m_channel->capabilities().count()) @@ -191,13 +212,50 @@ QLCCapability *ChannelEdit::addCapability() min = last->max() + 1; } QLCCapability *cap = new QLCCapability(min, UCHAR_MAX); + QQmlEngine::setObjectOwnership(cap, QQmlEngine::CppOwnership); cap->setWarning(QLCCapability::EmptyName); if (m_channel->addCapability(cap)) + { updateCapabilities(); + } + else + { + delete cap; + return nullptr; + } + + return cap; +} + +QLCCapability *ChannelEdit::addCapability(int min, int max, QString name) +{ + QLCCapability *cap = new QLCCapability(min, max); + QQmlEngine::setObjectOwnership(cap, QQmlEngine::CppOwnership); + cap->setName(name); + if (m_channel->addCapability(cap)) + { + updateCapabilities(); + } + else + { + delete cap; + return nullptr; + } return cap; } +void ChannelEdit::removeCapabilityAtIndex(int index) +{ + QList caps = m_channel->capabilities(); + + if (index < 0 || index >= caps.count()) + return; + + if (m_channel->removeCapability(caps[index])) + updateCapabilities(); +} + int ChannelEdit::getCapabilityPresetAtIndex(int index) { QList caps = m_channel->capabilities(); @@ -208,6 +266,17 @@ int ChannelEdit::getCapabilityPresetAtIndex(int index) return caps.at(index)->preset(); } +void ChannelEdit::setCapabilityPresetAtIndex(int index, int preset) +{ + QList caps = m_channel->capabilities(); + + if (index < 0 || index >= caps.count()) + return; + + QLCCapability *cap = caps.at(index); + cap->setPreset(QLCCapability::Preset(preset)); +} + int ChannelEdit::getCapabilityPresetType(int index) { QList caps = m_channel->capabilities(); @@ -238,6 +307,16 @@ QVariant ChannelEdit::getCapabilityValueAt(int index, int vIndex) return caps.at(index)->resource(vIndex); } +void ChannelEdit::setCapabilityValueAt(int index, int vIndex, QVariant value) +{ + QList caps = m_channel->capabilities(); + + if (index < 0 || index >= caps.count()) + return; + + caps.at(index)->setResource(vIndex, value); +} + void ChannelEdit::checkCapabilities() { QVectorallocation; @@ -260,5 +339,3 @@ void ChannelEdit::checkCapabilities() } } } - - diff --git a/qmlui/fixtureeditor/channeledit.h b/qmlui/fixtureeditor/channeledit.h index bdee87f0f9..329f1669dd 100644 --- a/qmlui/fixtureeditor/channeledit.h +++ b/qmlui/fixtureeditor/channeledit.h @@ -54,10 +54,16 @@ class ChannelEdit : public QObject /** Get the list of capabilities for the channel being edited */ QVariantList capabilities() const; - Q_INVOKABLE QLCCapability *addCapability(); + /** Methods to add a new capability */ + Q_INVOKABLE QLCCapability *addNewCapability(); + Q_INVOKABLE QLCCapability *addCapability(int min, int max, QString name); - /** Get the selected preset for a capability at the given index */ + /** Delete the capability at the given index */ + Q_INVOKABLE void removeCapabilityAtIndex(int index); + + /** Get/Set a preset for a capability at the given index */ Q_INVOKABLE int getCapabilityPresetAtIndex(int index); + Q_INVOKABLE void setCapabilityPresetAtIndex(int index, int preset); /** Get the type of preset for a capability at the given index */ Q_INVOKABLE int getCapabilityPresetType(int index); @@ -65,8 +71,9 @@ class ChannelEdit : public QObject /** Get the units of a preset for a capability at the given index */ Q_INVOKABLE QString getCapabilityPresetUnits(int index); - /** Get the value/resource of a preset for a capability at the given index */ + /** Get/Set the value/resource of a preset for a capability at the given index */ Q_INVOKABLE QVariant getCapabilityValueAt(int index, int vIndex); + Q_INVOKABLE void setCapabilityValueAt(int index, int vIndex, QVariant value); /** Perform a check on a recently modified capability for overlapping and integrity */ Q_INVOKABLE void checkCapabilities(); @@ -74,6 +81,9 @@ class ChannelEdit : public QObject private: void updateCapabilities(); +protected slots: + void setupPreset(); + signals: void channelChanged(); void groupChanged(); diff --git a/qmlui/fixtureeditor/editorview.cpp b/qmlui/fixtureeditor/editorview.cpp index eb7ad7700b..307a71fb61 100644 --- a/qmlui/fixtureeditor/editorview.cpp +++ b/qmlui/fixtureeditor/editorview.cpp @@ -17,9 +17,11 @@ limitations under the License. */ +#include + #include "qlcfixturemode.h" #include "qlcfixturedef.h" -#include "qlcchannel.h" +#include "qlccapability.h" #include "channeledit.h" #include "editorview.h" @@ -74,6 +76,11 @@ int EditorView::id() const return m_id; } +QLCFixtureDef *EditorView::fixtureDefinition() +{ + return m_fixtureDef; +} + bool EditorView::isUser() const { return m_fixtureDef->isUser(); @@ -181,9 +188,11 @@ ChannelEdit *EditorView::requestChannelEditor(QString name) if (ch == nullptr) { ch = new QLCChannel(); + QQmlEngine::setObjectOwnership(ch, QQmlEngine::CppOwnership); ch->setName(tr("New channel %1").arg(m_fixtureDef->channels().count() + 1)); m_fixtureDef->addChannel(ch); updateChannelList(); + setModified(true); } m_channelEdit = new ChannelEdit(ch); connect(m_channelEdit, SIGNAL(channelChanged()), this, SLOT(setModified())); @@ -191,14 +200,100 @@ ChannelEdit *EditorView::requestChannelEditor(QString name) return m_channelEdit; } +void EditorView::addPresetChannel(QString name, int group) +{ + QLCChannel *channel = new QLCChannel(); + channel->setName(name); + if (group > QLCChannel::Nothing) + { + channel->setGroup(QLCChannel::Intensity); + channel->setColour(QLCChannel::PrimaryColour(group)); + + switch (QLCChannel::PrimaryColour(group)) + { + case QLCChannel::Red: + channel->setPreset(QLCChannel::IntensityRed); + break; + case QLCChannel::Green: + channel->setPreset(QLCChannel::IntensityGreen); + break; + case QLCChannel::Blue: + channel->setPreset(QLCChannel::IntensityBlue); + break; + case QLCChannel::White: + channel->setPreset(QLCChannel::IntensityWhite); + break; + case QLCChannel::Amber: + channel->setPreset(QLCChannel::IntensityAmber); + break; + case QLCChannel::UV: + channel->setPreset(QLCChannel::IntensityUV); + break; + default: + break; + } + } + else + { + channel->setGroup(QLCChannel::Group(group)); + + switch (QLCChannel::Group(group)) + { + case QLCChannel::Intensity: + channel->setPreset(QLCChannel::IntensityDimmer); + break; + case QLCChannel::Pan: + channel->setPreset(QLCChannel::PositionPan); + break; + case QLCChannel::Tilt: + channel->setPreset(QLCChannel::PositionTilt); + break; + case QLCChannel::Colour: + channel->setPreset(QLCChannel::ColorMacro); + break; + case QLCChannel::Shutter: + channel->setPreset(QLCChannel::ShutterStrobeSlowFast); + break; + case QLCChannel::Beam: + channel->setPreset(QLCChannel::NoFunction); + break; + case QLCChannel::Effect: + channel->setPreset(QLCChannel::NoFunction); + break; + default: + break; + } + } + channel->addPresetCapability(); + m_fixtureDef->addChannel(channel); + updateChannelList(); + setModified(true); +} + bool EditorView::deleteChannel(QLCChannel *channel) { // TODO: Tardis bool res = m_fixtureDef->removeChannel(channel); - updateChannelList(); + setModified(true); return res; } +bool EditorView::deleteChannels(QVariantList channels) +{ + bool res; + for (int i = 0; i < channels.count(); i++) + { + QLCChannel *channel = channels.at(i).value(); + res = deleteChannel(channel); + if (res == false) + return false; + } + if (channels.count()) + updateChannelList(); + + return true; +} + /************************************************************************ * Modes ************************************************************************/ @@ -250,7 +345,7 @@ void EditorView::updateModeList() void EditorView::modeNameChanged() { - setModified(); + setModified(true); updateModeList(); } @@ -258,30 +353,85 @@ void EditorView::modeNameChanged() * Load & Save *********************************************************************/ -bool EditorView::save() +QString EditorView::checkFixture() +{ + QString errors; + + if (m_fixtureDef->channels().count() == 0) + { + errors.append(tr("
  • No channels provided
  • ")); + } + else + { + for (QLCChannel *channel : m_fixtureDef->channels()) + { + if (channel->capabilities().isEmpty()) + errors.append(tr("
  • No capability provided in channel '%1'
  • ").arg(channel->name())); + + for (QLCCapability *cap : channel->capabilities()) + { + if (cap->name().isEmpty()) + 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) + errors.append(tr("
  • No modes provided. Without modes, this fixture will not appear in the list!
  • ")); + + return errors; +} + +QString EditorView::save() { + QString errors; + if (m_fileName.isEmpty()) setFilenameFromModel(); - //m_fixtureDef->setPhysical(m_phyEdit->physical()); + m_fixtureDef->setPhysical(m_globalPhy->physical()); + + errors.append(checkFixture()); + QFile::FileError error = m_fixtureDef->saveXML(m_fileName); if (error != QFile::NoError) - return false; + return tr("Could not save file! (%1)").arg(QLCFile::errorString(error)); setModified(false); - return true; + return errors; } -bool EditorView::saveAs(QString path) +QString EditorView::saveAs(QString path) { QString localFilename = path; if (localFilename.startsWith("file:")) localFilename = QUrl(path).toLocalFile(); + /* Always use the fixture suffix */ + if (localFilename.right(4) != KExtFixture) + localFilename += KExtFixture; + m_fileName = localFilename; - save(); - return true; + return save(); } QString EditorView::fileName() diff --git a/qmlui/fixtureeditor/editorview.h b/qmlui/fixtureeditor/editorview.h index 3c4232bd24..c47bf4b6b9 100644 --- a/qmlui/fixtureeditor/editorview.h +++ b/qmlui/fixtureeditor/editorview.h @@ -23,10 +23,10 @@ #include #include "physicaledit.h" +#include "qlcchannel.h" class QLCFixtureDef; class ChannelEdit; -class QLCChannel; class ListModel; class ModeEdit; @@ -53,8 +53,12 @@ class EditorView : public QObject EditorView(QQuickView *view, int id, QLCFixtureDef *fixtureDef, QObject *parent = nullptr); ~EditorView(); + /** Get the unique ID of this editor */ int id() const; + /** Get the fixture definition reference being edited */ + QLCFixtureDef *fixtureDefinition(); + /** Get if the definition is user or system */ bool isUser() const; @@ -99,6 +103,14 @@ class EditorView : public QObject * Channels ************************************************************************/ public: + enum CompositeChannelTypes + { + RGBChannel = QLCChannel::Nothing + 100, + RGBWChannel, + RGBAWChannel + }; + Q_ENUM(CompositeChannelTypes) + /** Get a list of all the available channels in the definition */ QVariant channels() const; @@ -106,9 +118,13 @@ class EditorView : public QObject * If $name is empty, a new channel is added */ Q_INVOKABLE ChannelEdit *requestChannelEditor(QString name); + Q_INVOKABLE void addPresetChannel(QString name, int group); + /** Delete the given $channel from the definition */ Q_INVOKABLE bool deleteChannel(QLCChannel *channel); + Q_INVOKABLE bool deleteChannels(QVariantList channels); + private: void updateChannelList(); @@ -153,8 +169,8 @@ protected slots: * Load & Save *********************************************************************/ public: - Q_INVOKABLE bool save(); - Q_INVOKABLE bool saveAs(QString path); + Q_INVOKABLE QString save(); + Q_INVOKABLE QString saveAs(QString path); QString fileName(); void setFilenameFromModel(); @@ -162,6 +178,9 @@ protected slots: /** Get the definition modification flag */ bool isModified() const; +private: + QString checkFixture(); + protected slots: void setModified(bool modified = true); diff --git a/qmlui/fixtureeditor/fixtureeditor.cpp b/qmlui/fixtureeditor/fixtureeditor.cpp index f1a4c826a2..a224acdd31 100644 --- a/qmlui/fixtureeditor/fixtureeditor.cpp +++ b/qmlui/fixtureeditor/fixtureeditor.cpp @@ -130,13 +130,17 @@ bool FixtureEditor::loadDefinition(QString fileName) return true; } -void FixtureEditor::editDefinition(QString manufacturer, QString model) +bool FixtureEditor::editDefinition(QString manufacturer, QString model) { QLCFixtureDef *def = m_doc->fixtureDefCache()->fixtureDef(manufacturer, model); + if (def == nullptr) + return false; + m_editors[m_lastId] = new EditorView(m_view, m_lastId, def); m_lastId++; emit editorsListChanged(); + return true; } QVariantList FixtureEditor::editorsList() const @@ -165,6 +169,12 @@ void FixtureEditor::deleteEditor(int id) } EditorView *editor = m_editors.take(id); + + // reload fixture definition from disk + QLCFixtureDef *def = editor->fixtureDefinition(); + if (def != nullptr) + m_doc->fixtureDefCache()->reloadFixtureDef(def); + delete editor; emit editorsListChanged(); } diff --git a/qmlui/fixtureeditor/fixtureeditor.h b/qmlui/fixtureeditor/fixtureeditor.h index 2f98714221..bd93b45353 100644 --- a/qmlui/fixtureeditor/fixtureeditor.h +++ b/qmlui/fixtureeditor/fixtureeditor.h @@ -49,7 +49,7 @@ class FixtureEditor : public QObject Q_INVOKABLE bool loadDefinition(QString fileName); /** Edit an existing fixture definition */ - void editDefinition(QString manufacturer, QString model); + bool editDefinition(QString manufacturer, QString model); /** Returns a list of the created editors */ QVariantList editorsList() const; 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/fixtureeditor/physicaledit.cpp b/qmlui/fixtureeditor/physicaledit.cpp index a4bef276e4..2df967f874 100644 --- a/qmlui/fixtureeditor/physicaledit.cpp +++ b/qmlui/fixtureeditor/physicaledit.cpp @@ -32,6 +32,11 @@ PhysicalEdit::~PhysicalEdit() } +QLCPhysical PhysicalEdit::physical() +{ + return m_phy; +} + QString PhysicalEdit::bulbType() const { return m_phy.bulbType(); diff --git a/qmlui/fixtureeditor/physicaledit.h b/qmlui/fixtureeditor/physicaledit.h index ae2ef28d45..7111a426d0 100644 --- a/qmlui/fixtureeditor/physicaledit.h +++ b/qmlui/fixtureeditor/physicaledit.h @@ -53,6 +53,8 @@ class PhysicalEdit : public QObject PhysicalEdit(QLCPhysical phy, QObject *parent = nullptr); ~PhysicalEdit(); + QLCPhysical physical(); + public: QString bulbType() const; void setBulbType(const QString type); diff --git a/qmlui/fixturegroupeditor.cpp b/qmlui/fixturegroupeditor.cpp index 316f2e13d1..c717635a78 100644 --- a/qmlui/fixturegroupeditor.cpp +++ b/qmlui/fixturegroupeditor.cpp @@ -52,7 +52,7 @@ QVariant FixtureGroupEditor::groupsListModel() { QVariantList groupsList; - foreach(FixtureGroup *grp, m_doc->fixtureGroups()) + foreach (FixtureGroup *grp, m_doc->fixtureGroups()) { QVariantMap grpMap; grpMap.insert("mIcon", "qrc:/group.svg"); diff --git a/qmlui/fixturemanager.cpp b/qmlui/fixturemanager.cpp index 994efd87a2..0cb0f0a2bd 100644 --- a/qmlui/fixturemanager.cpp +++ b/qmlui/fixturemanager.cpp @@ -26,6 +26,7 @@ #include #include "monitorproperties.h" +#include "channelmodifier.h" #include "fixturemanager.h" #include "qlcfixturemode.h" #include "qlccapability.h" @@ -56,6 +57,7 @@ FixtureManager::FixtureManager(QQuickView *view, Doc *doc, QObject *parent) , m_maxBeamDegrees(0) , m_invertedZoom(false) , m_colorsMask(0) + , m_selectedChannelModifier(nullptr) { Q_ASSERT(m_doc != nullptr); @@ -272,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()) @@ -307,10 +309,12 @@ bool FixtureManager::addFixture(QString manuf, QString model, QString mode, QStr fxi->setFixtureDefinition(fxiDef, fxiMode); - m_doc->addFixture(fxi); - Tardis::instance()->enqueueAction(Tardis::FixtureCreate, fxi->id(), QVariant(), - Tardis::instance()->actionToByteArray(Tardis::FixtureCreate, fxi->id())); - slotFixtureAdded(fxi->id(), QVector3D(xPos, yPos, 0)); + if (m_doc->addFixture(fxi) == true) + { + Tardis::instance()->enqueueAction(Tardis::FixtureCreate, fxi->id(), QVariant(), + Tardis::instance()->actionToByteArray(Tardis::FixtureCreate, fxi->id())); + slotFixtureAdded(fxi->id(), QVector3D(xPos, yPos, 0)); + } fxAddress += (channels + gap); } @@ -538,6 +542,26 @@ void FixtureManager::setItemRoleData(int itemID, int index, QString role, QVaria m_fixtureTree->setItemRoleData(path, value, roleIndex); } +void FixtureManager::setItemRoleData(int itemID, QVariant value, int role) +{ + if (m_fixtureTree == nullptr) + return; + + quint32 fixtureID = FixtureUtils::itemFixtureID(itemID); + + Fixture *fixture = m_doc->fixture(fixtureID); + if (fixture == nullptr) + return; + + QString fxName = fixture->name(); + QStringList uniNames = m_doc->inputOutputMap()->universeNames(); + + QString path = QString("%1%2%3").arg(uniNames.at(fixture->universe())) + .arg(TreeModel::separator()).arg(fxName); + + m_fixtureTree->setItemRoleData(path, value, role); +} + bool FixtureManager::compareFixtures(Fixture *left, Fixture *right) { return *left < *right; @@ -658,8 +682,8 @@ void FixtureManager::addFixtureNode(Doc *doc, TreeModel *treeModel, Fixture *fix if (showFlags & ShowModifier) { - // TODO - chParams.append(""); + ChannelModifier *cm = fixture->channelModifier(chIdx); + chParams.append(cm != nullptr ? cm->name() : ""); } treeModel->addItem(channel->name(), chParams, fxPath, flags); @@ -840,6 +864,63 @@ QString FixtureManager::fixtureIcon(quint32 fixtureID) return fixture->iconResource(true); } +QStringList FixtureManager::fixtureModes(quint32 itemID) +{ + QStringList modes; + quint32 fixtureID = FixtureUtils::itemFixtureID(itemID); + Fixture *fixture = m_doc->fixture(fixtureID); + if (fixture == nullptr) + return modes; + + for (QLCFixtureMode *mode : fixture->fixtureDef()->modes()) + modes.append(mode->name()); + + return modes; +} + +int FixtureManager::fixtureModeIndex(quint32 itemID) +{ + quint32 fixtureID = FixtureUtils::itemFixtureID(itemID); + Fixture *fixture = m_doc->fixture(fixtureID); + if (fixture == nullptr) + return -1; + + QLCFixtureMode *currMode = fixture->fixtureMode(); + QList modes = fixture->fixtureDef()->modes(); + + return modes.indexOf(currMode); +} + +bool FixtureManager::setFixtureModeIndex(quint32 itemID, int index) +{ + quint32 fixtureID = FixtureUtils::itemFixtureID(itemID); + Fixture *fixture = m_doc->fixture(fixtureID); + if (fixture == nullptr) + return false; + + QList modes = fixture->fixtureDef()->modes(); + if (index < 0 || index >= modes.count()) + return false; + + QLCFixtureMode *newMode = modes.at(index); + + // check if new channels are available + int chNum = newMode->channels().count(); + + for (quint32 i = fixture->universeAddress(); i < fixture->universeAddress() + chNum; i++) + { + quint32 id = m_doc->fixtureForAddress(i); + if (id != fixture->id() && id != Fixture::invalidId()) + return false; + } + + fixture->setFixtureDefinition(fixture->fixtureDef(), newMode); + + emit fixturesMapChanged(); + + return true; +} + int FixtureManager::fixtureIDfromItemID(quint32 itemID) { return FixtureUtils::itemFixtureID(itemID); @@ -853,8 +934,8 @@ int FixtureManager::fixtureLinkedIndex(quint32 itemID) void FixtureManager::updateLinkedFixtureNode(quint32 itemID, bool add) { quint32 fixtureID = FixtureUtils::itemFixtureID(itemID); - int headIndex = FixtureUtils::itemHeadIndex(itemID); - int linkedIndex = FixtureUtils::itemLinkedIndex(itemID); + quint16 headIndex = FixtureUtils::itemHeadIndex(itemID); + quint16 linkedIndex = FixtureUtils::itemLinkedIndex(itemID); MonitorProperties *monProps = m_doc->monitorProperties(); QStringList uniNames = m_doc->inputOutputMap()->universeNames(); @@ -973,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; @@ -1312,26 +1396,6 @@ bool FixtureManager::addRGBPanel(QString name, qreal xPos, qreal yPos) * Universe Grid Editing *********************************************************************/ -QVariantList FixtureManager::fixtureSelection(quint32 address) -{ - QVariantList list; - quint32 uniFilter = m_universeFilter == Universe::invalid() ? 0 : m_universeFilter; - - quint32 fxID = m_doc->fixtureForAddress((uniFilter << 9) | address); - if (fxID == Fixture::invalidId()) - return list; - - Fixture *fixture = m_doc->fixture(fxID); - if (fixture == nullptr) - return list; - - quint32 startAddr = fixture->address(); - for (quint32 i = 0; i < fixture->channels(); i++) - list.append(startAddr + i); - - return list; -} - QVariantList FixtureManager::fixtureNamesMap() { return m_fixtureNamesMap; @@ -1383,7 +1447,7 @@ QVariantList FixtureManager::fixturesMap() continue; quint32 startAddress = fx->address(); - for(quint32 cn = 0; cn < fx->channels(); cn++) + for (quint32 cn = 0; cn < fx->channels(); cn++) { m_fixturesMap.append(fx->id()); m_fixturesMap.append(startAddress + cn); @@ -1407,11 +1471,85 @@ QVariantList FixtureManager::fixturesMap() m_fixtureNamesMap.append(fx->name()); } + emit fixturesMapChanged(); emit fixtureNamesMapChanged(); return m_fixturesMap; } +int FixtureManager::pasteFromClipboard(QVariantList fixtureIDs) +{ + if (fixtureIDs.isEmpty()) + return 0; + + // check destination universe + Fixture *fixture = m_doc->fixture(fixtureIDs.first().toUInt()); + if (fixture == nullptr) + return -1; + + if (fixture->universe() == m_universeFilter || + m_universeFilter == Universe::invalid()) + return -1; + + quint32 absAddress = (m_universeFilter << 9); + int fixtureIndex = 0; + QList newAddresses; + int availableAddress = 0; + quint32 freeCounter = 0; + + // check available space + for (int i = 0; i < 512; i++) + { + if (m_doc->fixtureForAddress(absAddress + i) != Fixture::invalidId()) + { + freeCounter = 0; + availableAddress = i + 1; + } + else + { + freeCounter++; + if (freeCounter == fixture->channels()) + { + // save the new address for this fixture + newAddresses.append(availableAddress); + fixtureIndex++; + freeCounter = 0; + availableAddress = i + 1; + + // all fixtures processed. Nothing more to do + if (fixtureIndex == fixtureIDs.count()) + break; + + fixture = m_doc->fixture(fixtureIDs.at(fixtureIndex).toUInt()); + if (fixture == nullptr) + return -1; + } + } + } + + // not enough space to paste all the fixtures + if (fixtureIndex != fixtureIDs.count()) + return -2; + + for (int f = 0; f < fixtureIDs.count(); f++) + { + Fixture *fxi = m_doc->fixture(fixtureIDs.at(f).toUInt()); + fxi->blockSignals(true); + fxi->setUniverse(m_universeFilter); + fxi->setAddress(newAddresses.at(f)); + fxi->blockSignals(false); + + // trigger one single changed signal + fxi->setID(fxi->id()); + } + + updateGroupsTree(m_doc, m_fixtureTree, m_searchFilter); + emit groupsTreeModelChanged(); + emit fixturesMapChanged(); + + return 0; +} + /********************************************************************* * Color filters *********************************************************************/ @@ -1468,7 +1606,7 @@ bool FixtureManager::loadColorFilters(const QDir &dir, bool user) void FixtureManager::resetColorFilters() { - while(!m_colorFilters.isEmpty()) + while (!m_colorFilters.isEmpty()) { ColorFilters *cf = m_colorFilters.takeLast(); delete cf; @@ -1553,17 +1691,6 @@ void FixtureManager::setChannelValue(quint32 fixtureID, quint32 channelIndex, qu emit channelValueChanged(fixtureID, channelIndex, value); } -void FixtureManager::setIntensityValue(quint8 value) -{ - emit channelTypeValueChanged(QLCChannel::Intensity, value); -} - -void FixtureManager::setColorValue(quint8 red, quint8 green, quint8 blue, - quint8 white, quint8 amber, quint8 uv) -{ - emit colorChanged(QColor(red, green, blue), QColor(white, amber, uv)); -} - void FixtureManager::setPresetValue(quint32 fixtureID, int chIndex, quint8 value) { qDebug() << "[FixtureManager] setPresetValue - fixture:" << fixtureID << ", channel:" << chIndex << "value:" << value; @@ -1648,7 +1775,7 @@ QMultiHash FixtureManager::getFixtureCapabilities(quint32 itemI for (quint32 ch : channelIndices) { const QLCChannel* channel(fixture->channel(ch)); - if(channel == nullptr) + if (channel == nullptr) continue; int chType = channel->group(); @@ -1688,7 +1815,7 @@ QMultiHash FixtureManager::getFixtureCapabilities(quint32 itemI case QLCChannel::Tilt: { hasPosition = true; - if(fixture->fixtureMode() != nullptr) + if (fixture->fixtureMode() != nullptr) { int panDeg = phy.focusPanMax(); int tiltDeg = phy.focusTiltMax(); @@ -1766,7 +1893,8 @@ QMultiHash FixtureManager::getFixtureCapabilities(quint32 itemI case QLCChannel::Beam: { if (channel->preset() != QLCChannel::BeamZoomBigSmall && - channel->preset() != QLCChannel::BeamZoomSmallBig) + channel->preset() != QLCChannel::BeamZoomSmallBig && + channel->preset() != QLCChannel::BeamZoomFine) break; hasBeam = true; @@ -1832,7 +1960,7 @@ QList FixtureManager::getFixtureZoom(quint32 fxID, float degrees) if (fixture == nullptr || fixture->fixtureMode() == nullptr) return QList(); - return fixture->zoomToValues(degrees); + return fixture->zoomToValues(degrees, false); } QVariantList FixtureManager::presetsChannels(QLCChannel::Group group) @@ -1983,3 +2111,77 @@ int FixtureManager::colorsMask() const return m_colorsMask; } +/********************************************************************* + * Channel modifiers + *********************************************************************/ + +QStringList FixtureManager::channelModifiersList() const +{ + QList names = m_doc->modifiersCache()->templateNames(); + names.sort(); + names.prepend("None"); + + return names; +} + +void FixtureManager::selectChannelModifier(QString name) +{ + m_selectedChannelModifier = m_doc->modifiersCache()->modifier(name); + + emit channelModifierValuesChanged(); +} + +void FixtureManager::setChannelModifier(quint32 itemID, quint32 channelIndex) +{ + quint32 fixtureID = FixtureUtils::itemFixtureID(itemID); + Fixture *fixture = m_doc->fixture(fixtureID); + if (fixture == nullptr) + return; + + fixture->setChannelModifier(channelIndex, m_selectedChannelModifier); + + // update UI tree + setItemRoleData(itemID, channelIndex, "modifier", m_selectedChannelModifier == nullptr ? + "None" : m_selectedChannelModifier->name()); +} + +void FixtureManager::showModifierEditor(quint32 itemID, quint32 channelIndex) +{ + quint32 fixtureID = FixtureUtils::itemFixtureID(itemID); + Fixture *fixture = m_doc->fixture(fixtureID); + if (fixture == nullptr) + return; + + ChannelModifier *cm = fixture->channelModifier(channelIndex); + + QQuickItem *fgmItem = qobject_cast(m_view->rootObject()->findChild("fixtureGroupManager")); + if (fgmItem == nullptr) + return; + + selectChannelModifier(cm == nullptr ? "" : cm->name()); + + QMetaObject::invokeMethod(fgmItem, "showChannelModifierEditor", + Q_ARG(QVariant, itemID), + Q_ARG(QVariant, channelIndex), + Q_ARG(QVariant, cm == nullptr ? "" : cm->name())); +} + +QVariantList FixtureManager::channelModifierValues() const +{ + QVariantList values; + + if (m_selectedChannelModifier != nullptr) + { + QList< QPair > map = m_selectedChannelModifier->modifierMap(); + for (int i = 0; i < map.count(); i++) + { + QPair dmxPair = map.at(i); + values.append(dmxPair.first); + values.append(dmxPair.second); + } + } + + return values; +} + + diff --git a/qmlui/fixturemanager.h b/qmlui/fixturemanager.h index 5809015594..6d31865269 100644 --- a/qmlui/fixturemanager.h +++ b/qmlui/fixturemanager.h @@ -59,6 +59,9 @@ class FixtureManager : public QObject Q_PROPERTY(int colorFilterFileIndex READ colorFilterFileIndex WRITE setColorFilterFileIndex NOTIFY colorFilterFileIndexChanged) Q_PROPERTY(ColorFilters *selectedFilters READ selectedFilters NOTIFY selectedFiltersChanged) + Q_PROPERTY(QStringList channelModifiersList READ channelModifiersList NOTIFY channelModifiersListChanged) + Q_PROPERTY(QVariantList channelModifierValues READ channelModifierValues NOTIFY channelModifierValuesChanged) + public: FixtureManager(QQuickView *view, Doc *doc, QObject *parent = nullptr); ~FixtureManager(); @@ -182,6 +185,8 @@ public slots: Q_INVOKABLE void setItemRoleData(int itemID, int index, QString role, QVariant value); + void setItemRoleData(int itemID, QVariant value, int role); + static void addFixtureNode(Doc *doc, TreeModel *treeModel, Fixture *fixture, QString basePath, quint32 nodeSubID, int &matchMask, QString searchFilter = QString(), int showFlags = ShowGroups | ShowLinked | ShowHeads, QList checkedChannels = QList()); @@ -198,6 +203,14 @@ public slots: /** Return the type as string of the Fixture with ID $fixtureID */ Q_INVOKABLE QString fixtureIcon(quint32 fixtureID); + /** Return the list of modes available for the item with the provided $itemID */ + Q_INVOKABLE QStringList fixtureModes(quint32 itemID); + + /** Get/Set the currently selected fixture mode index + * for the item with the provided $itemID */ + Q_INVOKABLE int fixtureModeIndex(quint32 itemID); + Q_INVOKABLE bool setFixtureModeIndex(quint32 itemID, int index); + /** Return the Fixture ID of the provided $itemID */ Q_INVOKABLE int fixtureIDfromItemID(quint32 itemID); @@ -309,10 +322,6 @@ public slots: * Universe Grid Editing *********************************************************************/ public: - /** Returns a list of the universe indices occupied by a Fixture - at the requested $address */ - Q_INVOKABLE QVariantList fixtureSelection(quint32 address); - /** Returns a list of fixture names for representation in a GridEditor QML component */ QVariantList fixtureNamesMap(); @@ -322,6 +331,8 @@ public slots: /** Returns data for representation in a GridEditor QML component */ QVariantList fixturesMap(); + Q_INVOKABLE int pasteFromClipboard(QVariantList fixtureIDs); + signals: /** Notify the listeners that the fixture names map has changed */ void fixtureNamesMapChanged(); @@ -384,9 +395,6 @@ public slots: /** Wrapper methods to emit a signal to listeners interested in changes of * channel values per capability */ - Q_INVOKABLE void setIntensityValue(quint8 value); - Q_INVOKABLE void setColorValue(quint8 red, quint8 green, quint8 blue, - quint8 white, quint8 amber, quint8 uv); Q_INVOKABLE void setPresetValue(quint32 fixtureID, int chIndex, quint8 value); /** @@ -436,10 +444,6 @@ public slots: /** Notify the listeners that $value of $channelIndex of $fixtureID has changed */ void channelValueChanged(quint32 fixtureID, quint32 channelIndex, quint8 value); - /** Notify the listeners that channels of the specified $type should - * be set to the provided $value */ - void channelTypeValueChanged(int type, quint8 value); - /** Notify the listeners that a color has been picked in the ColorTool. * It emits all the possible components: RGB, White, Amber and UV */ void colorChanged(QColor rgb, QColor wauv); @@ -492,6 +496,33 @@ public slots: int m_colorsMask; /** A map of the currently available colors and their counters */ QMap m_colorCounters; + + /********************************************************************* + * Channel modifiers + *********************************************************************/ +public: + /** Return a list of the available channel modifiers */ + QStringList channelModifiersList() const; + + /** Request the UI to open the channel modifier editor */ + Q_INVOKABLE void showModifierEditor(quint32 itemID, quint32 channelIndex); + + /** Select the current channel modifier to display */ + Q_INVOKABLE void selectChannelModifier(QString name); + + /** Assign the currently selected channel modifier to the given fixture's channel */ + Q_INVOKABLE void setChannelModifier(quint32 itemID, quint32 channelIndex); + + /** Return a list of values to render the currently selected channel + * modifier in the UI. Values are stored as original,modified */ + QVariantList channelModifierValues() const; + +signals: + void channelModifiersListChanged(); + void channelModifierValuesChanged(); + +private: + ChannelModifier *m_selectedChannelModifier; }; #endif // FIXTUREMANAGER_H diff --git a/qmlui/fixtureutils.cpp b/qmlui/fixtureutils.cpp index b4035ea351..cacdd7bba0 100644 --- a/qmlui/fixtureutils.cpp +++ b/qmlui/fixtureutils.cpp @@ -271,7 +271,7 @@ QPointF FixtureUtils::available2DPosition(Doc *doc, int pointOfView, QRectF fxRe qreal itemHeight = fxSize.height(); // store the next Y row in case we need to lower down - if (itemYPos + itemHeight > maxYOffset ) + if (itemYPos + itemHeight > maxYOffset) maxYOffset = itemYPos + itemHeight; QRectF itemRect(itemXPos, itemYPos, itemWidth, itemHeight); @@ -315,9 +315,13 @@ QColor FixtureUtils::blendColors(QColor a, QColor b, float mix) return QColor(mr * 255.0, mg * 255.0, mb * 255.0); } -QColor FixtureUtils::headColor(Fixture *fixture, int headIndex) +QColor FixtureUtils::headColor(Fixture *fixture, bool hasDimmer, int headIndex) { - QColor finalColor = Qt::white; + if (fixture == nullptr) + return QColor(); + + QColor finalColor = hasDimmer ? Qt::black : Qt::white; + bool colorFound = false; QVector rgbCh = fixture->rgbChannels(headIndex); if (rgbCh.size() == 3) @@ -325,6 +329,7 @@ QColor FixtureUtils::headColor(Fixture *fixture, int headIndex) finalColor.setRgb(fixture->channelValueAt(rgbCh.at(0)), fixture->channelValueAt(rgbCh.at(1)), fixture->channelValueAt(rgbCh.at(2))); + colorFound = true; } QVector cmyCh = fixture->cmyChannels(headIndex); @@ -333,6 +338,7 @@ QColor FixtureUtils::headColor(Fixture *fixture, int headIndex) finalColor.setCmyk(fixture->channelValueAt(cmyCh.at(0)), fixture->channelValueAt(cmyCh.at(1)), fixture->channelValueAt(cmyCh.at(2)), 0); + colorFound = true; } quint32 white = fixture->channelNumber(QLCChannel::White, QLCChannel::MSB, headIndex); @@ -356,6 +362,15 @@ QColor FixtureUtils::headColor(Fixture *fixture, int headIndex) if (indigo != QLCChannel::invalid() && fixture->channelValueAt(indigo)) finalColor = blendColors(finalColor, QColor(0xFF4B0082), (float)fixture->channelValueAt(indigo) / 255.0); + if (white != QLCChannel::invalid() || amber != QLCChannel::invalid() || UV != QLCChannel::invalid() || + lime != QLCChannel::invalid() || indigo != QLCChannel::invalid()) + colorFound = true; + + //qDebug() << "fixture" << fixture->name() << "head" << headIndex << "hasdimmer" << hasDimmer; + + if (colorFound == false) + return Qt::white; + return finalColor; } diff --git a/qmlui/fixtureutils.h b/qmlui/fixtureutils.h index 87fdc0d0c4..b5f974b61b 100644 --- a/qmlui/fixtureutils.h +++ b/qmlui/fixtureutils.h @@ -62,7 +62,7 @@ class FixtureUtils /** Return the color of the head with $headIndex of $fixture. * This considers: RGB / CMY / WAUVLI channels, dimmers and gel color */ - static QColor headColor(Fixture *fixture, int headIndex = 0); + static QColor headColor(Fixture *fixture, bool hasDimmer, int headIndex = 0); static QColor applyColorFilter(QColor source, QColor filter); diff --git a/qmlui/functioneditor.cpp b/qmlui/functioneditor.cpp index 7f6adc029a..0d819aab76 100644 --- a/qmlui/functioneditor.cpp +++ b/qmlui/functioneditor.cpp @@ -212,7 +212,7 @@ int FunctionEditor::holdSpeed() const if (m_function == nullptr) return Function::defaultSpeed(); - return m_function->duration(); + return m_function->duration() - m_function->fadeInSpeed(); } void FunctionEditor::setHoldSpeed(int holdSpeed) diff --git a/qmlui/functionmanager.cpp b/qmlui/functionmanager.cpp index 24e2e2bff8..90e887e5f5 100644 --- a/qmlui/functionmanager.cpp +++ b/qmlui/functionmanager.cpp @@ -22,13 +22,10 @@ #include #include "audioplugincache.h" -#include "genericdmxsource.h" #include "collectioneditor.h" #include "functionmanager.h" #include "rgbmatrixeditor.h" -#include "contextmanager.h" #include "treemodelitem.h" -#include "scriptwrapper.h" #include "chasereditor.h" #include "scripteditor.h" #include "sceneeditor.h" @@ -40,6 +37,7 @@ #include "rgbmatrix.h" #include "function.h" #include "sequence.h" +#include "script.h" #include "chaser.h" #include "scene.h" #include "audio.h" @@ -238,7 +236,7 @@ quint32 FunctionManager::addFunctiontoDoc(Function *func, QString name, bool sel return Function::invalidId(); } -quint32 FunctionManager::createFunction(int type, QStringList fileList) +quint32 FunctionManager::createFunction(int type, QVariantList fixturesList) { Function* f = nullptr; QString name; @@ -249,6 +247,12 @@ quint32 FunctionManager::createFunction(int type, QStringList fileList) { f = new Scene(m_doc); name = tr("New Scene"); + if (fixturesList.count()) + { + Scene *scene = qobject_cast(f); + for (QVariant fixtureID : fixturesList) + scene->addFixture(fixtureID.toUInt()); + } m_sceneCount++; emit sceneCountChanged(); } @@ -263,6 +267,13 @@ quint32 FunctionManager::createFunction(int type, QStringList fileList) * that awful effect of playing steps with 0 duration */ Chaser *chaser = qobject_cast(f); chaser->setDuration(1000); + + for (QVariant &fId : m_selectedIDList) + { + ChaserStep chs; + chs.fid = fId.toUInt(); + chaser->addStep(chs); + } } m_chaserCount++; emit chaserCountChanged(); @@ -292,6 +303,19 @@ quint32 FunctionManager::createFunction(int type, QStringList fileList) { f = new EFX(m_doc); name = tr("New EFX"); + if (fixturesList.count()) + { + EFX *efx = qobject_cast(f); + for (QVariant fixtureID : fixturesList) + { + Fixture *fixture = m_doc->fixture(fixtureID.toUInt()); + if (fixture == nullptr) + continue; + + for (int headIdx = 0; headIdx < fixture->heads(); headIdx++) + efx->addFixture(fixture->id(), headIdx); + } + } m_efxCount++; emit efxCountChanged(); } @@ -335,6 +359,20 @@ quint32 FunctionManager::createFunction(int type, QStringList fileList) emit showCountChanged(); } break; + default: + break; + } + + return addFunctiontoDoc(f, name, true); +} + +quint32 FunctionManager::createAudioVideoFunction(int type, QStringList fileList) +{ + Function* f = nullptr; + QString name; + + switch(type) + { case Function::AudioType: { name = tr("New Audio"); @@ -399,8 +437,6 @@ quint32 FunctionManager::createFunction(int type, QStringList fileList) } } break; - default: - break; } return addFunctiontoDoc(f, name, true); @@ -441,13 +477,18 @@ QString FunctionManager::functionPath(quint32 id) void FunctionManager::clearTree() { - setPreview(false); + setPreviewEnabled(false); m_selectedIDList.clear(); m_selectedFolderList.clear(); m_functionTree->clear(); } -void FunctionManager::setPreview(bool enable) +bool FunctionManager::previewEnabled() const +{ + return m_previewEnabled; +} + +void FunctionManager::setPreviewEnabled(bool enable) { if (m_currentEditor != nullptr) { @@ -463,14 +504,13 @@ void FunctionManager::setPreview(bool enable) if (enable == false) f->stop(FunctionParent::master()); else - { f->start(m_doc->masterTimer(), FunctionParent::master()); - } } } } m_previewEnabled = enable; + emit previewEnabledChanged(); } void FunctionManager::selectFunctionID(quint32 fID, bool multiSelection) @@ -665,7 +705,7 @@ void FunctionManager::deleteFunction(quint32 fid) return; if (f->isRunning()) - f->stop(FunctionParent::master()); + f->stopAndWait(); Tardis::instance()->enqueueAction(Tardis::FunctionDelete, f->id(), Tardis::instance()->actionToByteArray(Tardis::FunctionDelete, f->id()), @@ -811,6 +851,19 @@ void FunctionManager::deleteEditorItems(QVariantList list) m_currentEditor->deleteItems(list); } +void FunctionManager::deleteSequenceFixtures(QVariantList list) +{ + if (m_sceneEditor == nullptr) + return; + + // First remove fixtures from the Sequence steps + ChaserEditor *chaserEditor = qobject_cast(m_currentEditor); + chaserEditor->removeFixtures(list); + + // Then delete the fixtures from the Scene + m_sceneEditor->deleteItems(list); +} + void FunctionManager::renameSelectedItems(QString newName, bool numbering, int startNumber, int digits) { if (m_selectedIDList.isEmpty() && m_selectedFolderList.isEmpty()) @@ -980,7 +1033,7 @@ void FunctionManager::createFolder() if (m_emptyFolderList.contains(fName) == false) break; index++; - } while(1); + } while (1); // check if there is some selected folder if (m_selectedFolderList.count()) @@ -1060,9 +1113,9 @@ quint32 FunctionManager::getChannelTypeMask(quint32 fxID, quint32 channel) if (ch->group() == QLCChannel::Intensity) { if (ch->colour() == QLCChannel::NoColour) - chTypeBit |= ContextManager::DimmerType; + chTypeBit |= App::DimmerType; else - chTypeBit |= ContextManager::ColorType; + chTypeBit |= App::ColorType; } else { @@ -1098,7 +1151,7 @@ void FunctionManager::dumpOnNewScene(QList dumpValues, QListaddFunction(newScene) == true) { - setPreview(false); + setPreviewEnabled(false); Tardis::instance()->enqueueAction(Tardis::FunctionCreate, newScene->id(), QVariant(), Tardis::instance()->actionToByteArray(Tardis::FunctionCreate, newScene->id())); } @@ -1279,7 +1332,7 @@ void FunctionManager::updateFunctionsTree() void FunctionManager::slotDocLoaded() { - setPreview(false); + setPreviewEnabled(false); updateFunctionsTree(); } diff --git a/qmlui/functionmanager.h b/qmlui/functionmanager.h index efbba80dba..2b8861b1d6 100644 --- a/qmlui/functionmanager.h +++ b/qmlui/functionmanager.h @@ -69,6 +69,8 @@ class FunctionManager : public QObject Q_PROPERTY(QStringList pictureExtensions READ pictureExtensions CONSTANT) Q_PROPERTY(QStringList videoExtensions READ videoExtensions CONSTANT) + Q_PROPERTY(bool previewEnabled READ previewEnabled WRITE setPreviewEnabled NOTIFY previewEnabledChanged) + public: FunctionManager(QQuickView *view, Doc *doc, QObject *parent = 0); ~FunctionManager(); @@ -103,8 +105,16 @@ class FunctionManager : public QObject QString searchFilter() const; void setSearchFilter(QString searchFilter); - /** Create a new Function with the specified $type */ - Q_INVOKABLE quint32 createFunction(int type, QStringList fileList = QStringList()); + /** Create a new Function with the specified $type + * If the optional fixturesList is provided, fixture IDs + * will be added to the Function where possible. + */ + Q_INVOKABLE quint32 createFunction(int type, QVariantList fixturesList = QVariantList()); + + /** Create a new Audio/Video Function for each + * file path provided in fileList. + */ + Q_INVOKABLE quint32 createAudioVideoFunction(int type, QStringList fileList = QStringList()); /** Return a reference to a Function with the specified $id */ Q_INVOKABLE Function *getFunction(quint32 id); @@ -115,7 +125,8 @@ class FunctionManager : public QObject Q_INVOKABLE QString functionPath(quint32 id); /** Enable/disable the Function preview feature */ - Q_INVOKABLE void setPreview(bool enable); + bool previewEnabled() const; + void setPreviewEnabled(bool enable); /** Add $fID to the list of the currently selected Function IDs, * considering $multiSelection as an append/replace action */ @@ -150,6 +161,10 @@ class FunctionManager : public QObject * This happens AFTER a popup confirmation */ Q_INVOKABLE void deleteEditorItems(QVariantList list); + /** Specific method to delete fixtures from the currently edited Sequence. + * This happens AFTER a popup confirmation */ + Q_INVOKABLE void deleteSequenceFixtures(QVariantList list); + /** Rename the currently selected items (functions and/or folders) * with the provided $newName. * If $numbering is true, then $startNumber and $digits will compose @@ -198,6 +213,7 @@ class FunctionManager : public QObject void audioCountChanged(); void videoCountChanged(); void selectedFunctionCountChanged(int count); + void previewEnabledChanged(); void isEditingChanged(bool editing); void viewPositionChanged(int viewPosition); @@ -217,6 +233,7 @@ public slots: /** Flag that hold if Functions preview is enabled or not */ bool m_previewEnabled; + /** List of the Function IDs currently selected * and previewed, if preview is enabled */ QVariantList m_selectedIDList; diff --git a/qmlui/importmanager.cpp b/qmlui/importmanager.cpp index 58aa79942e..f16293f355 100644 --- a/qmlui/importmanager.cpp +++ b/qmlui/importmanager.cpp @@ -23,20 +23,19 @@ #include "importmanager.h" #include "treemodelitem.h" #include "fixturemanager.h" -#include "functionmanager.h" #include "qlcfixturedefcache.h" -#include "audioplugincache.h" -#include "rgbscriptscache.h" +#include "monitorproperties.h" #include "qlcfixturemode.h" #include "qlcfixturedef.h" -#include "scriptwrapper.h" #include "fixtureutils.h" +#include "qlcpalette.h" #include "collection.h" #include "rgbmatrix.h" #include "sequence.h" #include "qlcfile.h" #include "chaser.h" +#include "script.h" #include "scene.h" #include "efx.h" #include "doc.h" @@ -76,6 +75,12 @@ ImportManager::~ImportManager() * this is shared with the original Doc */ m_importDoc->setFixtureDefinitionCache(nullptr); delete m_importDoc; + + m_functionTree->clear(); + m_fixtureTree->clear(); + + delete m_functionTree; + delete m_fixtureTree; } bool ImportManager::loadWorkspace(const QString &fileName) @@ -133,6 +138,8 @@ void ImportManager::apply() std::sort(m_fixtureIDList.begin(), m_fixtureIDList.end()); importFixtures(); + importPalettes(); + /* Functions need to be imported respecting their * dependency order. Otherwise ID remapping will be * messed up */ @@ -157,7 +164,7 @@ bool ImportManager::loadXML(QXmlStreamReader &doc) { if (doc.name() == KXMLQLCEngine) { - m_importDoc->loadXML(doc); + m_importDoc->loadXML(doc, false); } /* else if (doc.name() == KXMLQLCVirtualConsole) @@ -200,6 +207,10 @@ void ImportManager::getAvailableFixtureAddress(int channels, int &universe, int void ImportManager::importFixtures() { + MonitorProperties *importMonProps = m_importDoc->monitorProperties(); + MonitorProperties *monProps = m_doc->monitorProperties(); + + /* ************************ Import fixtures ************************ */ for (quint32 importID : m_fixtureIDList) { bool matchFound = false; @@ -230,8 +241,10 @@ void ImportManager::importFixtures() * in m_doc, which implies finding an available address and ID remapping */ if (matchFound == false) { - int uniIdx = 0; - int address = 0; + // Attempt to preserve original universe/address. + // Will be checked later if available + int uniIdx = importFixture->universe(); + int address = importFixture->address(); QLCFixtureDef *importDef = importFixture->fixtureDef(); QLCFixtureMode *importMode = importFixture->fixtureMode(); @@ -250,7 +263,7 @@ void ImportManager::importFixtures() if (fxiDef == nullptr && fxiMode == nullptr) { - if (importDef->model() == "Generic Dimmer") + if (importDef->model() == "Generic") { fxiDef = fxi->genericDimmerDef(importFixture->channels()); fxiMode = fxi->genericDimmerMode(fxiDef, importFixture->channels()); @@ -277,7 +290,24 @@ void ImportManager::importFixtures() } } } + /* ******************** Import linked fixtures ********************* */ + for (quint32 itemID : m_itemIDList) + { + quint32 fixtureID = FixtureUtils::itemFixtureID(itemID); + quint16 headIndex = FixtureUtils::itemHeadIndex(itemID); + quint16 linkedIndex = FixtureUtils::itemLinkedIndex(itemID); + + // if no original fixture was selected, skip linked + if (m_fixtureIDList.contains(fixtureID) == false) + continue; + + QString name = importMonProps->fixtureName(fixtureID, headIndex, linkedIndex); + //quint32 remappedID = FixtureUtils::fixtureItemID(m_fixtureIDRemap[fixtureID], headIndex, linkedIndex); + monProps->setFixtureName(m_fixtureIDRemap[fixtureID], headIndex, linkedIndex, name); + } + + /* ********************* Import fixture groups ********************* */ for (quint32 groupID : m_fixtureGroupIDList) { bool matchFound = false; @@ -332,6 +362,57 @@ void ImportManager::importFixtures() } } +void ImportManager::importPalettes() +{ + for (quint32 paletteID : m_paletteIDList) + { + QLCPalette *importPalette = m_importDoc->palette(paletteID); + bool matchFound = false; + + qDebug() << "Import palette" << importPalette->name(); + + /* Check if a Palette with the same name already exists in m_doc. + * If it does, check also if the ID needs to be remapped */ + for (QLCPalette *docPalette : m_doc->palettes()) + { + if (docPalette->name() == importPalette->name()) + { + if (docPalette->id() != paletteID) + { + qDebug() << "Match found. Palette" << importPalette->name() + << "with ID" << paletteID << "will be remapped to ID" << docPalette->id(); + } + + m_fixtureIDRemap[paletteID] = docPalette->id(); + matchFound = true; + break; + } + } + + if (matchFound == false) + { + QLCPalette *palette = new QLCPalette(importPalette->type()); + palette->setName(importPalette->name()); + + palette->setValues(importPalette->values()); + palette->setFanningType(importPalette->fanningType()); + palette->setFanningLayout(importPalette->fanningLayout()); + palette->setFanningAmount(importPalette->fanningAmount()); + palette->setFanningValue(importPalette->fanningValue()); + + if (m_doc->addPalette(palette) == true) + { + m_paletteIDRemap[paletteID] = palette->id(); + } + else + { + qWarning() << "ERROR: Failed to add Palette" << palette->name(); + delete palette; + } + } + } +} + void ImportManager::importFunctionID(quint32 funcID) { Function *importFunction = m_importDoc->function(funcID); @@ -377,10 +458,28 @@ void ImportManager::importFunctionID(quint32 funcID) case Function::SceneType: { Scene *scene = qobject_cast(docFunction); - // create a copy of the existing values + // create a copy of the existing components QList sceneValues = scene->values(); - // point of no return. Delete all values + QList fixtureGroupList = scene->fixtureGroups(); + QList paletteList = scene->palettes(); + + // point of no return. Delete everything scene->clear(); + + // add referenced fixture groups + for (quint32 groupID : fixtureGroupList) + { + if (m_fixtureGroupIDRemap.contains(groupID)) + scene->addFixtureGroup(m_fixtureGroupIDRemap[groupID]); + } + + // add referenced palettes + for (quint32 paletteID : paletteList) + { + if (m_paletteIDRemap.contains(paletteID)) + scene->addPalette(m_paletteIDRemap[paletteID]); + } + // remap values against existing/remapped fixtures for (SceneValue scv : sceneValues) { @@ -513,12 +612,12 @@ QVariant ImportManager::groupsTreeModel() m_fixtureTree = new TreeModel(this); QQmlEngine::setObjectOwnership(m_fixtureTree, QQmlEngine::CppOwnership); QStringList treeColumns; - treeColumns << "classRef" << "type" << "id" << "subid" << "chIdx"; + treeColumns << "classRef" << "type" << "id" << "subid" << "chIdx" << "inGroup"; m_fixtureTree->setColumnNames(treeColumns); m_fixtureTree->enableSorting(false); FixtureManager::updateGroupsTree(m_importDoc, m_fixtureTree, m_fixtureSearchFilter, - FixtureManager::ShowCheckBoxes | FixtureManager::ShowGroups); + FixtureManager::ShowCheckBoxes | FixtureManager::ShowHeads | FixtureManager::ShowLinked); connect(m_fixtureTree, SIGNAL(roleChanged(TreeModelItem*,int,const QVariant&)), this, SLOT(slotFixtureTreeDataChanged(TreeModelItem*,int,const QVariant&))); @@ -545,8 +644,8 @@ void ImportManager::slotFixtureTreeDataChanged(TreeModelItem *item, int role, co setChildrenChecked(item->children(), checked); QVariantList itemData = item->data(); - // itemData must be "classRef" << "type" << "id" << "subid" << "chIdx"; - if (itemData.count() != 5) + // itemData must be "classRef" << "type" << "id" << "subid" << "chIdx" << "inGroup"; + if (itemData.count() != 6) return; int itemType = itemData.at(1).toInt(); @@ -555,15 +654,25 @@ void ImportManager::slotFixtureTreeDataChanged(TreeModelItem *item, int role, co if (itemType == App::FixtureDragItem) { quint32 fixtureID = FixtureUtils::itemFixtureID(itemID); + quint16 linkedIndex = FixtureUtils::itemLinkedIndex(itemID); if (checked) { if (m_fixtureIDList.contains(fixtureID) == false) m_fixtureIDList.append(fixtureID); + + if (linkedIndex > 0 && m_itemIDList.contains(itemID) == false) + { + m_itemIDList.append(itemID); + checkFixtureTree(m_fixtureTree); + } } else { - m_fixtureIDList.removeOne(fixtureID); + if (linkedIndex > 0) + m_itemIDList.removeOne(itemID); + else + m_fixtureIDList.removeOne(fixtureID); } } else if (itemType == App::FixtureGroupDragItem) @@ -620,12 +729,14 @@ void ImportManager::checkFixtureTree(TreeModel *tree) { QVariantList itemData = item->data(); - // itemData must be "classRef" << "type" << "id" << "subid" << "chIdx"; - if (itemData.count() == 5 && itemData.at(1).toInt() == App::FixtureDragItem) + // itemData must be "classRef" << "type" << "id" << "subid" << "chIdx" << "inGroup"; + if (itemData.count() == 6 && itemData.at(1).toInt() == App::FixtureDragItem) { - quint32 fixtureID = FixtureUtils::itemFixtureID(itemData.at(2).toUInt()); + quint32 itemID = itemData.at(2).toUInt(); + quint32 fixtureID = FixtureUtils::itemFixtureID(itemID); + quint16 linkedIndex = FixtureUtils::itemLinkedIndex(itemID); - if (m_fixtureIDList.contains(fixtureID)) + if (m_fixtureIDList.contains(fixtureID) && linkedIndex == 0) tree->setItemRoleData(item, true, TreeModel::IsCheckedRole); } @@ -642,7 +753,7 @@ void ImportManager::updateFunctionsTree() { m_functionTree->clear(); - for(Function *func : m_importDoc->functions()) // C++11 + for (Function *func : m_importDoc->functions()) // C++11 { if (func == nullptr || func->isVisible() == false) return; @@ -668,7 +779,7 @@ QVariant ImportManager::functionsTreeModel() if (m_functionTree == nullptr) { m_functionTree = new TreeModel(this); - QQmlEngine::setObjectOwnership(m_fixtureTree, QQmlEngine::CppOwnership); + QQmlEngine::setObjectOwnership(m_functionTree, QQmlEngine::CppOwnership); QStringList treeColumns; treeColumns << "classRef"; m_functionTree->setColumnNames(treeColumns); @@ -691,6 +802,9 @@ void ImportManager::checkFunctionTree(TreeModel *tree) for (TreeModelItem *item : tree->items()) { + if (item->data().count() == 0) + continue; + QVariant cRef = item->data().first(); if (cRef.canConvert()) { @@ -785,11 +899,22 @@ void ImportManager::checkFunctionDependency(quint32 fid) QList funcList; QList fxList; + QList fxGroupList; + QList paletteList; switch (func->type()) { - // these are 'leaves' and will return only Fixture IDs + // a Scene can reference fixtures, fixture groups and palettes case Function::SceneType: + { + Scene *scene = qobject_cast(func); + fxList = scene->components(); + fxGroupList = scene->fixtureGroups(); + paletteList = scene->palettes(); + } + break; + + // EFX needs only fixtures case Function::EFXType: fxList = func->components(); break; @@ -797,22 +922,10 @@ void ImportManager::checkFunctionDependency(quint32 fid) // RGB Matrix requires a fixture group case Function::RGBMatrixType: { - fxList = func->components(); RGBMatrix *rgbm = qobject_cast(func); + fxList = rgbm->components(); quint32 groupID = rgbm->fixtureGroup(); - if (groupID != FixtureGroup::invalidId() && - m_fixtureGroupIDList.contains(groupID) == false) - { - FixtureGroup *group = m_importDoc->fixtureGroup(groupID); - // include the fixture group to the import - m_fixtureGroupIDList.append(groupID); - // include also all the group fixtures - for (quint32 id : group->fixtureList()) - { - if (m_fixtureIDList.contains(id)) - m_fixtureIDList.append(id); - } - } + fxGroupList.append(groupID); } break; @@ -837,18 +950,43 @@ void ImportManager::checkFunctionDependency(quint32 fid) break; } - for (quint32 id : fxList) + for (quint32 groupID : fxGroupList) + { + if (groupID != FixtureGroup::invalidId() && + m_fixtureGroupIDList.contains(groupID) == false) + { + FixtureGroup *group = m_importDoc->fixtureGroup(groupID); + m_fixtureGroupIDList.append(groupID); + + for (quint32 id : group->fixtureList()) + { + if (m_fixtureIDList.contains(id) == false) + m_fixtureIDList.append(id); + } + } + } + + for (quint32 paletteID : paletteList) + { + if (paletteID != QLCPalette::invalidId() && + m_paletteIDList.contains(paletteID) == false) + { + m_paletteIDList.append(paletteID); + } + } + + for (quint32 fixtureID : fxList) { - if (m_fixtureIDList.contains(id) == false) - m_fixtureIDList.append(id); + if (m_fixtureIDList.contains(fixtureID) == false) + m_fixtureIDList.append(fixtureID); } - for (quint32 id : funcList) + for (quint32 functionID : funcList) { - if (m_functionIDList.contains(id) == false) + if (m_functionIDList.contains(functionID) == false) { - m_functionIDList.append(id); - checkFunctionDependency(id); + m_functionIDList.append(functionID); + checkFunctionDependency(functionID); } } } diff --git a/qmlui/importmanager.h b/qmlui/importmanager.h index cc15adeff8..ed220df843 100644 --- a/qmlui/importmanager.h +++ b/qmlui/importmanager.h @@ -59,6 +59,9 @@ class ImportManager : public QObject /** Perform the actual import of Fixtures */ void importFixtures(); + /** Perform the actual import of Palettes */ + void importPalettes(); + /** Recursive method that imports a Function ID * satisfying the Function dependecies first */ void importFunctionID(quint32 funcID); @@ -74,6 +77,11 @@ class ImportManager : public QObject /** Reference to the project where to import from */ Doc *m_importDoc; + /** The list of selected Palette IDs */ + QList m_paletteIDList; + /** A map of the Palette IDs that need to be remapped */ + QMap m_paletteIDRemap; + /********************************************************************* * Fixture tree *********************************************************************/ @@ -99,6 +107,8 @@ protected slots: QString m_fixtureSearchFilter; /** The list of selected Fixture IDs */ QList m_fixtureIDList; + /** A list of item IDs holding basically linked fixtures */ + QList m_itemIDList; /** A map of the Fixture IDs that need to be remapped */ QMap m_fixtureIDRemap; diff --git a/qmlui/inputoutputmanager.cpp b/qmlui/inputoutputmanager.cpp index d986c8e65a..5e244b7587 100644 --- a/qmlui/inputoutputmanager.cpp +++ b/qmlui/inputoutputmanager.cpp @@ -22,19 +22,14 @@ #include #include "inputoutputmanager.h" +#include "inputprofileeditor.h" #include "monitorproperties.h" #include "audioplugincache.h" -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - #include "audiorenderer_qt5.h" - #include "audiocapture_qt5.h" -#else -#include "audiorenderer_qt6.h" -#include "audiocapture_qt6.h" -#endif #include "qlcioplugin.h" #include "outputpatch.h" #include "inputpatch.h" #include "universe.h" +#include "qlcfile.h" #include "tardis.h" #include "doc.h" @@ -42,6 +37,8 @@ InputOutputManager::InputOutputManager(QQuickView *view, Doc *doc, QObject *pare : PreviewContext(view, doc, "IOMGR", parent) , m_selectedUniverseIndex(-1) , m_blackout(false) + , m_profileEditor(nullptr) + , m_editProfile(nullptr) , m_beatType("INTERNAL") { Q_ASSERT(m_doc != nullptr); @@ -55,6 +52,8 @@ InputOutputManager::InputOutputManager(QQuickView *view, Doc *doc, QObject *pare qmlRegisterType("org.qlcplus.classes", 1, 0, "Universe"); qmlRegisterType("org.qlcplus.classes", 1, 0, "InputPatch"); qmlRegisterType("org.qlcplus.classes", 1, 0, "OutputPatch"); + qmlRegisterUncreatableType("org.qlcplus.classes", 1, 0, "QLCInputProfile", "Can't create a QLCInputProfile!"); + qmlRegisterUncreatableType("org.qlcplus.classes", 1, 0, "InputProfEditor", "Can't create a InputProfileEditor!"); connect(m_doc, SIGNAL(loaded()), this, SLOT(slotDocLoaded())); connect(m_ioMap, SIGNAL(universeAdded(quint32)), this, SIGNAL(universesListModelChanged())); @@ -94,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; @@ -237,7 +250,7 @@ QVariant InputOutputManager::audioInputDevice() } QList devList = m_doc->audioPluginCache()->audioDevicesList(); - foreach(AudioDeviceInfo info, devList) + foreach (AudioDeviceInfo info, devList) { if (info.capabilities & AUDIO_CAP_INPUT && info.deviceName == devName) @@ -269,7 +282,7 @@ QVariant InputOutputManager::audioOutputDevice() } QList devList = m_doc->audioPluginCache()->audioDevicesList(); - foreach(AudioDeviceInfo info, devList) + foreach (AudioDeviceInfo info, devList) { if (info.capabilities & AUDIO_CAP_OUTPUT && info.deviceName == devName) @@ -298,7 +311,7 @@ QVariant InputOutputManager::audioInputSources() const inputSources.append(defAudioMap); int i = 0; - for (AudioDeviceInfo info : devList) + for (AudioDeviceInfo &info : devList) { if (info.capabilities & AUDIO_CAP_INPUT) { @@ -331,7 +344,7 @@ QVariant InputOutputManager::audioOutputSources() const outputSources.append(defAudioMap); int i = 0; - for (AudioDeviceInfo info : devList) + for (AudioDeviceInfo &info : devList) { if (info.capabilities & AUDIO_CAP_OUTPUT) { @@ -389,11 +402,11 @@ QVariant InputOutputManager::universeInputSources(int universe) currLine = ip->input(); } - foreach(QString pluginName, m_ioMap->inputPluginNames()) + foreach (QString pluginName, m_ioMap->inputPluginNames()) { QLCIOPlugin *plugin = m_doc->ioPluginCache()->plugin(pluginName); int i = 0; - foreach(QString pLine, m_ioMap->pluginInputs(pluginName)) + foreach (QString pLine, m_ioMap->pluginInputs(pluginName)) { if (pluginName == currPlugin && i == currLine) { @@ -430,11 +443,11 @@ QVariant InputOutputManager::universeOutputSources(int universe) currLine = op->output(); } - foreach(QString pluginName, m_ioMap->outputPluginNames()) + foreach (QString pluginName, m_ioMap->outputPluginNames()) { QLCIOPlugin *plugin = m_doc->ioPluginCache()->plugin(pluginName); int i = 0; - foreach(QString pLine, m_ioMap->pluginOutputs(pluginName)) + foreach (QString pLine, m_ioMap->pluginOutputs(pluginName)) { if (pluginName == currPlugin && i == currLine) { @@ -459,37 +472,6 @@ QVariant InputOutputManager::universeOutputSources(int universe) return QVariant::fromValue(outputSources); } -QVariant InputOutputManager::universeInputProfiles(int universe) -{ - QVariantList profilesList; - QString currentProfile = KInputNone; - QStringList profileNames = m_ioMap->profileNames(); - profileNames.sort(); - - if (m_ioMap->inputPatch(universe) != nullptr) - currentProfile = m_ioMap->inputPatch(universe)->profileName(); - - foreach(QString name, profileNames) - { - QLCInputProfile *ip = m_ioMap->profile(name); - if (ip != nullptr) - { - QString type = ip->typeToString(ip->type()); - if (name != currentProfile) - { - QVariantMap profileMap; - profileMap.insert("universe", universe); - profileMap.insert("name", name); - profileMap.insert("line", name); - profileMap.insert("plugin", type); - profilesList.append(profileMap); - } - } - } - - return QVariant::fromValue(profilesList); -} - void InputOutputManager::setOutputPatch(int universe, QString plugin, QString line, int index) { m_ioMap->setOutputPatch(universe, plugin, "", line.toUInt(), false, index); @@ -591,6 +573,139 @@ int InputOutputManager::outputPatchesCount(int universe) const return m_ioMap->outputPatchesCount(universe); } +/********************************************************************* + * Input Profiles + *********************************************************************/ + +QString InputOutputManager::profileUserFolder() +{ + return m_ioMap->userProfileDirectory().absolutePath(); +} + +void InputOutputManager::createInputProfile() +{ + if (m_editProfile != nullptr) + delete m_editProfile; + + m_editProfile = new QLCInputProfile(); + + if (m_profileEditor == nullptr) + { + m_profileEditor = new InputProfileEditor(m_editProfile, m_doc); + view()->rootContext()->setContextProperty("profileEditor", m_profileEditor); + } +} + +bool InputOutputManager::editInputProfile(QString name) +{ + QLCInputProfile *ip = m_ioMap->profile(name); + if (ip == nullptr) + return false; + + // create a copy first + if (m_editProfile != nullptr) + delete m_editProfile; + + m_editProfile = ip->createCopy(); + + qDebug() << "Profile TYPE:" << m_editProfile->type(); + + if (m_profileEditor == nullptr) + { + m_profileEditor = new InputProfileEditor(m_editProfile, m_doc); + view()->rootContext()->setContextProperty("profileEditor", m_profileEditor); + } + + qDebug() << "Edit profile" << ip->path(); + + return true; +} + +bool InputOutputManager::saveInputProfile() +{ + if (m_editProfile == nullptr) + return false; + + QDir dir(InputOutputMap::userProfileDirectory()); + QString absPath = QString("%1/%2-%3%4").arg(dir.absolutePath()) + .arg(m_editProfile->manufacturer()) + .arg(m_editProfile->model()) + .arg(KExtInputProfile); + + bool profileExists = QFileInfo::exists(absPath); + + m_editProfile->saveXML(absPath); + m_profileEditor->setModified(false); + + if (profileExists == false) + m_doc->inputOutputMap()->addProfile(m_editProfile); + + return true; +} + +void InputOutputManager::finishInputProfile() +{ + if (m_editProfile != nullptr) + { + delete m_editProfile; + m_editProfile = nullptr; + } + + if (m_profileEditor != nullptr) + { + view()->rootContext()->setContextProperty("profileEditor", nullptr); + delete m_profileEditor; + m_profileEditor = nullptr; + } +} + +bool InputOutputManager::removeInputProfile(QString name) +{ + QLCInputProfile *profile = m_ioMap->profile(name); + if (profile == nullptr) + return false; + + QFile file(profile->path()); + if (file.remove() == true) + { + m_ioMap->removeProfile(name); + return true; + } + + qDebug() << "Failed to remove input profile" << profile->path(); + + return false; +} + +QVariant InputOutputManager::universeInputProfiles(int universe) +{ + QVariantList profilesList; + QStringList profileNames = m_ioMap->profileNames(); + profileNames.sort(Qt::CaseInsensitive); + QDir pSysPath = m_ioMap->systemProfileDirectory(); + + foreach (QString name, profileNames) + { + QLCInputProfile *ip = m_ioMap->profile(name); + if (ip != nullptr) + { + QString type = ip->typeToString(ip->type()); + QVariantMap profileMap; + profileMap.insert("universe", universe); + profileMap.insert("name", name); + profileMap.insert("line", name); + profileMap.insert("plugin", type); + if (ip->path().startsWith(pSysPath.absolutePath())) + profileMap.insert("isUser", false); + else + profileMap.insert("isUser", true); + profilesList.append(profileMap); + } + } + + return QVariant::fromValue(profilesList); +} + /********************************************************************* * Beats *********************************************************************/ @@ -617,20 +732,20 @@ QVariant InputOutputManager::beatGeneratorsList() internalMap.insert("privateName", ""); genList.append(internalMap); - // add the currently open MIDI input devices - foreach(Universe *uni, m_ioMap->universes()) + // 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 @@ -653,7 +768,7 @@ QVariant InputOutputManager::beatGeneratorsList() else { QList devList = m_doc->audioPluginCache()->audioDevicesList(); - foreach(AudioDeviceInfo info, devList) + foreach (AudioDeviceInfo info, devList) { if (info.capabilities & AUDIO_CAP_INPUT && info.deviceName == devName) @@ -688,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 @@ -705,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 d35293721c..4bac8c40ee 100644 --- a/qmlui/inputoutputmanager.h +++ b/qmlui/inputoutputmanager.h @@ -29,6 +29,8 @@ class Doc; class Universe; class InputOutputMap; +class QLCInputProfile; +class InputProfileEditor; class InputOutputManager : public PreviewContext { @@ -51,6 +53,8 @@ class InputOutputManager : public PreviewContext Q_PROPERTY(QString beatType READ beatType WRITE setBeatType NOTIFY beatTypeChanged) Q_PROPERTY(int bpmNumber READ bpmNumber WRITE setBpmNumber NOTIFY bpmNumberChanged) + Q_PROPERTY(QString profileUserFolder READ profileUserFolder CONSTANT) + public: InputOutputManager(QQuickView *view, Doc *doc, QObject *parent = 0); @@ -58,7 +62,7 @@ protected slots: void slotDocLoaded(); private: - InputOutputMap* m_ioMap; + InputOutputMap *m_ioMap; /********************************************************************* * Universes @@ -66,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 */ @@ -119,7 +124,6 @@ protected slots: public: Q_INVOKABLE QVariant universeInputSources(int universe); Q_INVOKABLE QVariant universeOutputSources(int universe); - Q_INVOKABLE QVariant universeInputProfiles(int universe); Q_INVOKABLE int outputPatchesCount(int universe) const; Q_INVOKABLE void setOutputPatch(int universe, QString plugin, QString line, int index); @@ -142,6 +146,23 @@ protected slots: void clearInputList(); void clearOutputList(); + /********************************************************************* + * Input Profiles + *********************************************************************/ +public: + QString profileUserFolder(); + + Q_INVOKABLE void createInputProfile(); + Q_INVOKABLE bool editInputProfile(QString name); + Q_INVOKABLE bool saveInputProfile(); + Q_INVOKABLE void finishInputProfile(); + Q_INVOKABLE bool removeInputProfile(QString name); + Q_INVOKABLE QVariant universeInputProfiles(int universe); + +private: + InputProfileEditor *m_profileEditor; + QLCInputProfile *m_editProfile; + /********************************************************************* * Beats *********************************************************************/ diff --git a/qmlui/inputprofileeditor.cpp b/qmlui/inputprofileeditor.cpp new file mode 100644 index 0000000000..ec40e23ef2 --- /dev/null +++ b/qmlui/inputprofileeditor.cpp @@ -0,0 +1,262 @@ +/* + Q Light Controller Plus + inputprofileeditor.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 "inputprofileeditor.h" +#include "qlcinputchannel.h" +#include "inputoutputmap.h" +#include "doc.h" + +InputProfileEditor::InputProfileEditor(QLCInputProfile *profile, Doc *doc, + QObject *parent) + : QObject(parent) + , m_doc(doc) + , m_profile(profile) + , m_modified(false) + , m_detection(false) + , m_editChannel(nullptr) +{ +} + +InputProfileEditor::~InputProfileEditor() +{ +} + +bool InputProfileEditor::modified() const +{ + return m_modified; +} + +void InputProfileEditor::setModified(bool newModified) +{ + if (m_modified == newModified) + return; + m_modified = newModified; + emit modifiedChanged(); +} + +QString InputProfileEditor::manufacturer() const +{ + return m_profile == nullptr ? "" : m_profile->manufacturer(); +} + +void InputProfileEditor::setManufacturer(const QString &newManufacturer) +{ + if (m_profile == nullptr || m_profile->manufacturer() == newManufacturer) + return; + + m_profile->setManufacturer(newManufacturer); + setModified(); + emit manufacturerChanged(); +} + +QString InputProfileEditor::model() const +{ + return m_profile == nullptr ? "" : m_profile->model(); +} + +void InputProfileEditor::setModel(const QString &newModel) +{ + if (m_profile == nullptr || m_profile->model() == newModel) + return; + + m_profile->setModel(newModel); + setModified(); + emit modelChanged(); +} + +QLCInputProfile::Type InputProfileEditor::type() +{ + return m_profile == nullptr ? QLCInputProfile::MIDI : m_profile->type(); +} + +void InputProfileEditor::setType(const QLCInputProfile::Type &newType) +{ + if (m_profile == nullptr || m_profile->type() == newType) + return; + + m_profile->setType(QLCInputProfile::Type(newType)); + setModified(); + emit typeChanged(); +} + +bool InputProfileEditor::midiNoteOff() +{ + return m_profile == nullptr ? 0 : m_profile->midiSendNoteOff(); +} + +void InputProfileEditor::setMidiNoteOff(const bool &newNoteOff) +{ + if (m_profile == nullptr || m_profile->midiSendNoteOff() == newNoteOff) + return; + + m_profile->setMidiSendNoteOff(newNoteOff); + setModified(); + emit midiNoteOffChanged(); +} + +void InputProfileEditor::toggleDetection() +{ + + if (m_detection == false) + { + /* Listen to input data */ + connect(m_doc->inputOutputMap(), &InputOutputMap::inputValueChanged, + this, &InputProfileEditor::slotInputValueChanged); + } + else + { + disconnect(m_doc->inputOutputMap(), &InputOutputMap::inputValueChanged, + this, &InputProfileEditor::slotInputValueChanged); + } + m_detection = !m_detection; +} + +QVariant InputProfileEditor::channels() +{ + if (m_profile == nullptr) + return QVariant(); + + QVariantList chList; + + QMapIterator it(m_profile->channels()); + while (it.hasNext() == true) + { + it.next(); + QVariantMap chMap; + QLCInputChannel *ich = it.value(); + chMap.insert("chNumber", it.key() + 1); + chMap.insert("cRef", QVariant::fromValue(ich)); + chList.append(chMap); + } + + return QVariant::fromValue(chList); +} + +QVariantList InputProfileEditor::channelTypeModel() +{ + QVariantList types; + + for (QString &type : QLCInputChannel::types()) + { + QVariantMap typeMap; + QLCInputChannel::Type typeEnum = QLCInputChannel::stringToType(type); + typeMap.insert("mLabel", type); + typeMap.insert("mValue", typeEnum); + typeMap.insert("mIcon", QLCInputChannel::iconResource(typeEnum, true)); + types.append(typeMap); + } + + return types; +} + +QLCInputChannel *InputProfileEditor::getEditChannel(int channelNumber) +{ + if (m_profile == nullptr) + return nullptr; + + QLCInputChannel *ich = m_profile->channel(channelNumber); + if (ich == nullptr) + m_editChannel = new QLCInputChannel(); + else + m_editChannel = ich->createCopy(); + + return m_editChannel; +} + +int InputProfileEditor::saveChannel(int originalChannelNumber, int channelNumber) +{ + if (m_profile == nullptr) + return -3; + + if (originalChannelNumber >= 0 && originalChannelNumber != channelNumber) + { + QLCInputChannel *ich = m_profile->channel(originalChannelNumber); + if (ich != nullptr) + return -1; + } + + if (m_editChannel->name().isEmpty()) + return -2; + + m_profile->removeChannel(originalChannelNumber); + m_profile->insertChannel(channelNumber, m_editChannel); + setModified(); + + emit channelsChanged(); + + return 0; +} + +bool InputProfileEditor::removeChannel(int channelNumber) +{ + if (m_profile == nullptr) + return false; + + if (m_profile->removeChannel(channelNumber)) + { + emit channelsChanged(); + return true; + } + + return false; +} + +void InputProfileEditor::slotInputValueChanged(quint32 universe, quint32 channel, uchar value, const QString &key) +{ + Q_UNUSED(universe) + + //qDebug() << "Got input value" << universe << channel << value << key; + QLCInputChannel *ich = m_profile->channel(channel); + if (ich == nullptr) + { + ich = new QLCInputChannel(); + if (key.isEmpty()) + ich->setName(tr("Button %1").arg(channel + 1)); + else + ich->setName(key); + ich->setType(QLCInputChannel::Button); + m_profile->insertChannel(channel, ich); + m_channelsMap[channel].push_back(value); + emit channelsChanged(); + } + else + { + QVector vect = m_channelsMap[channel]; + if (vect.length() < 3) + { + if (!vect.contains(value)) + m_channelsMap[channel].push_back(value); + } + else if (vect.length() == 3) + { + if (ich->type() == QLCInputChannel::Button) + { + ich->setType(QLCInputChannel::Slider); + if (key.isEmpty()) + ich->setName(tr("Slider %1").arg(channel + 1)); + else + ich->setName(key); + emit channelsChanged(); + } + } + } +} + diff --git a/qmlui/inputprofileeditor.h b/qmlui/inputprofileeditor.h new file mode 100644 index 0000000000..df0a7e2ed4 --- /dev/null +++ b/qmlui/inputprofileeditor.h @@ -0,0 +1,154 @@ +/* + Q Light Controller Plus + inputprofileeditor.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 INPUTPROFILEEDITOR_H +#define INPUTPROFILEEDITOR_H + +#include +#include +#include +#include + +#include "qlcinputprofile.h" +#include "midiprotocol.h" + +class Doc; +class QLCInputChannel; + +class InputProfileEditor : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY(InputProfileEditor) + + Q_PROPERTY(bool modified READ modified WRITE setModified NOTIFY modifiedChanged FINAL) + Q_PROPERTY(QString manufacturer READ manufacturer WRITE setManufacturer NOTIFY manufacturerChanged FINAL) + Q_PROPERTY(QString model READ model WRITE setModel NOTIFY modelChanged FINAL) + Q_PROPERTY(QLCInputProfile::Type type READ type WRITE setType NOTIFY typeChanged FINAL) + Q_PROPERTY(bool midiNoteOff READ midiNoteOff WRITE setMidiNoteOff NOTIFY midiNoteOffChanged FINAL) + Q_PROPERTY(QVariant channels READ channels NOTIFY channelsChanged FINAL) + Q_PROPERTY(QVariantList channelTypeModel READ channelTypeModel CONSTANT) + + /************************************************************************ + * Initialization + ************************************************************************/ +public: + InputProfileEditor(QObject *parent = nullptr); + InputProfileEditor(QLCInputProfile *profile, Doc *doc, QObject *parent = nullptr); + ~InputProfileEditor(); + + /* Get/Set the profile modified state */ + bool modified() const; + void setModified(bool newModified = true); + + /* Get/Set the manufacturer of the profile currently being edited */ + QString manufacturer() const; + void setManufacturer(const QString &newManufacturer); + + /* Get/Set the model of the profile currently being edited */ + QString model() const; + void setModel(const QString &newModel); + + /* Get/Set the type of the profile currently being edited */ + QLCInputProfile::Type type(); + void setType(const QLCInputProfile::Type &newType); + + /* Get/Set MIDI Note Off setting */ + bool midiNoteOff(); + void setMidiNoteOff(const bool &newNoteOff); + +protected slots: + void slotInputValueChanged(quint32 universe, quint32 channel, uchar value, const QString& key); + +signals: + void modifiedChanged(); + void manufacturerChanged(); + void modelChanged(); + void typeChanged(); + void midiNoteOffChanged(); + +private: + Doc *m_doc; + QLCInputProfile *m_profile; + bool m_modified; + bool m_detection; + + /************************************************************************ + * Channels + ************************************************************************/ +public: + enum MIDIMessageType + { + ControlChange = 0, + NoteOnOff, + NoteAftertouch, + ProgramChange, + ChannelAfterTouch, + PitchWheel, + MBCPlayback, + MBCBeat, + MBCStop + }; + Q_ENUM(MIDIMessageType) + + enum MIDIMessageOffset + { + ControlChangeOffset = CHANNEL_OFFSET_CONTROL_CHANGE, + NoteOffset = CHANNEL_OFFSET_NOTE, + NoteAfterTouchOffset = CHANNEL_OFFSET_NOTE_AFTERTOUCH, + ProgramChangeOffset = CHANNEL_OFFSET_PROGRAM_CHANGE, + ChannelAfterTouchOffset = CHANNEL_OFFSET_CHANNEL_AFTERTOUCH, + PitchWheelOffset = CHANNEL_OFFSET_PITCH_WHEEL, + MBCPlaybackOffset = CHANNEL_OFFSET_MBC_PLAYBACK, + MBCBeatOffset = CHANNEL_OFFSET_MBC_BEAT, + MBCStopOffset = CHANNEL_OFFSET_MBC_STOP + }; + Q_ENUM(MIDIMessageOffset) + + /* Enable/Disable input detection */ + Q_INVOKABLE void toggleDetection(); + + /* Return a QML-ready list of channels of the profile + * currently being edited */ + QVariant channels(); + + /* Return a QML-ready list of channel types to be + * used by a combo box component */ + QVariantList channelTypeModel(); + + /* Get a copy of the channel with the provided number */ + Q_INVOKABLE QLCInputChannel *getEditChannel(int channelNumber); + + /* Check if a channel number already exists */ + Q_INVOKABLE int saveChannel(int originalChannelNumber, int channelNumber); + + /* Remove the input channel with the provided channel number */ + Q_INVOKABLE bool removeChannel(int channelNumber); + +signals: + void channelsChanged(); + +private: + // map of used to detect if + // an input signal comes from a button or a fader + QMap> m_channelsMap; + + QLCInputChannel *m_editChannel; +}; + +#endif 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/js/TimeUtils.js b/qmlui/js/TimeUtils.js index fbc0b9892b..24c237b64d 100644 --- a/qmlui/js/TimeUtils.js +++ b/qmlui/js/TimeUtils.js @@ -43,11 +43,22 @@ function msToString(ms) finalTime += ((m < 10) ? "0" + m : m) + ":"; finalTime += ((s < 10) ? "0" + s : s); if (ms) { - finalTime += "." + ((ms < 10) ? "0" + parseInt(ms) : parseInt(ms)); + finalTime += "."; + if (ms < 10) finalTime += "00"; + else if (ms < 100) finalTime += "0"; + + finalTime += parseInt(ms); } return finalTime; } +function beatsToString(beats, division) +{ + var bar = Math.floor(beats / division); + var beat = Math.floor(beats - (bar * division)); + return "" + bar + "." + beat; +} + /** * Returns a string from the provided time in milliseconds, * considering the requested precision to display miliseconds. @@ -257,6 +268,24 @@ function posToMs(x, timescale, tickSize) return parseInt(x * (1000 * timescale) / tickSize); } +/** Return a value in beats for the given + * position in pixels + */ +function posToBeat(x, tickSize, beatsDivision) +{ + return Math.round(x / (tickSize / beatsDivision)) * 1000 +} + +/** Return a value in milliseconds for the given position + * translated into a beat-based timeline, considering + * ticksize and BPM number and division + */ +function posToBeatMs(x, tickSize, bpmNumber, beatsDivision) +{ + // (bpmNumber / beatsDivision) * tickSize : 60000 = x : currentTime + return (x * 60000) / ((bpmNumber / beatsDivision) * tickSize); +} + /** * Return a value in pixels, for the given * time in milliseconds and the given timescale, @@ -266,3 +295,67 @@ function timeToSize(time, timescale, tickSize) { return ((time * tickSize) / 1000) / timescale; } + +/** Return a value in pixel, for a time + based on BPM and the tick size */ +function timeToBeatPosition(currentTime, tickSize, bpmNumber, beatsDivision) +{ + // (bpmNumber / beatsDivision) * tickSize : 60000 = x : currentTime + return (bpmNumber / beatsDivision) * tickSize * (currentTime / 60000); +} + +function beatsToSize(time, tickSize, beatsDivision) +{ + return (tickSize / beatsDivision) * (time / 1000); +} + +/** + * Return a value in pixels representing + * a time in milliseconds over a beat-based timeline + * where tickSize corresponds to a bar (e.g. 2, 3 or 4 beats) + */ +function timeToBeatSize(time, bpmNumber, beatsDivision, tickSize) +{ + var barDuration = (60000 / bpmNumber) * beatsDivision; + // tickSize : barDuration = x : time + return (tickSize * time) / barDuration; +} + + +/** + * Return the average time between two taps given by a list of tap times. + * It caculates the linear regression of the recorded tap times. The slope of the resulting + * linear function represents the average time between two taps. + */ +function calculateBPMByTapIntervals(tapHistory) +{ + var tapHistorySorted = [] + + //reduce size to only 16 taps + while (tapHistory.length > 16) tapHistory.splice(0,1) + + //copy tap history to sort it + tapHistorySorted = tapHistory.slice() + tapHistorySorted.sort() + + // Find the median time between taps, assume that the tempo is +-40% of this + var tapHistoryMedian = tapHistorySorted[Math.floor(tapHistorySorted.length/2)] + + //init needed variables + var n = 1, tapx = 0, tapy = 0, sum_x = 0, sum_y = 0, sum_xx = 0, sum_xy = 0 + + for (var i = 0; i < tapHistory.length; i++) + { + var intervalMs = tapHistory[i] + n++ + // Divide by tapHistoryMedian to determine if a tap was skipped during input + tapx += Math.floor((tapHistoryMedian/2 + intervalMs) / tapHistoryMedian) + tapy += intervalMs + sum_x += tapx + sum_y += tapy + sum_xx += tapx * tapx + sum_xy += tapx * tapy + } + + return (n * sum_xy - sum_x * sum_y) / (n * sum_xx - sum_x * sum_x) +} diff --git a/qmlui/listmodel.cpp b/qmlui/listmodel.cpp index d50fca4838..bac7e1c403 100644 --- a/qmlui/listmodel.cpp +++ b/qmlui/listmodel.cpp @@ -126,6 +126,17 @@ void ListModel::addDataMap(QVariantMap data) endInsertRows(); } +void ListModel::setDataMap(const QModelIndex &index, QVariantMap data) +{ + int itemRow = index.row(); + if (itemRow >= m_data.count()) + return; + + m_data[itemRow] = data; + + emit dataChanged(index, index); +} + QHash ListModel::roleNames() const { QHash roles; diff --git a/qmlui/listmodel.h b/qmlui/listmodel.h index 1cdf153056..e72ddcd9d2 100644 --- a/qmlui/listmodel.h +++ b/qmlui/listmodel.h @@ -51,6 +51,8 @@ class ListModel : public QAbstractListModel void addDataMap(QVariantMap data); + void setDataMap(const QModelIndex &index, QVariantMap data); + protected: QStringList m_roles; QHash roleNames() const; 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/mainview2d.cpp b/qmlui/mainview2d.cpp index e8769a661c..99ca782831 100644 --- a/qmlui/mainview2d.cpp +++ b/qmlui/mainview2d.cpp @@ -68,7 +68,7 @@ void MainView2D::setUniverseFilter(quint32 universeFilter) { PreviewContext::setUniverseFilter(universeFilter); QMapIterator it(m_itemsMap); - while(it.hasNext()) + while (it.hasNext()) { it.next(); quint32 itemID = it.key(); @@ -89,7 +89,7 @@ void MainView2D::setUniverseFilter(quint32 universeFilter) void MainView2D::resetItems() { QMapIterator it(m_itemsMap); - while(it.hasNext()) + while (it.hasNext()) { it.next(); delete it.value(); @@ -390,18 +390,24 @@ void MainView2D::updateFixtureItem(Fixture *fixture, quint16 headIndex, quint16 } quint32 masterDimmerChannel = fixture->masterIntensityChannel(); - qreal masterDimmerValue = qreal(fixture->channelValueAt(int(masterDimmerChannel))) / 255.0; + qreal masterDimmerValue = masterDimmerChannel != QLCChannel::invalid() ? + qreal(fixture->channelValueAt(int(masterDimmerChannel))) / 255.0 : 1.0; for (int headIdx = 0; headIdx < fixture->heads(); headIdx++) { quint32 headDimmerChannel = fixture->channelNumber(QLCChannel::Intensity, QLCChannel::MSB, headIdx); if (headDimmerChannel == QLCChannel::invalid()) - headDimmerChannel = fixture->masterIntensityChannel(); + headDimmerChannel = masterDimmerChannel; - //qDebug() << "Head" << headIdx << "dimmer channel:" << mdIndex; + //qDebug() << "Head" << headIdx << "dimmer channel:" << headDimmerChannel; qreal intensityValue = 1.0; + bool hasDimmer = false; + if (headDimmerChannel != QLCChannel::invalid()) + { intensityValue = (qreal)fixture->channelValueAt(headDimmerChannel) / 255; + hasDimmer = true; + } if (headDimmerChannel != masterDimmerChannel) intensityValue *= masterDimmerValue; @@ -410,7 +416,7 @@ void MainView2D::updateFixtureItem(Fixture *fixture, quint16 headIndex, quint16 Q_ARG(QVariant, headIdx), Q_ARG(QVariant, intensityValue)); - color = FixtureUtils::headColor(fixture, headIdx); + color = FixtureUtils::headColor(fixture, hasDimmer, headIdx); QMetaObject::invokeMethod(fxItem, "setHeadRGBColor", Q_ARG(QVariant, headIdx), @@ -486,7 +492,7 @@ void MainView2D::updateFixtureItem(Fixture *fixture, quint16 headIndex, quint16 break; case QLCChannel::Gobo: { - if (goboSet || (previous.count() && value == uchar(previous.at(i)))) + if (goboSet || (previous.length() && value == uchar(previous.at(i)))) break; QLCCapability *cap = ch->searchCapability(value); @@ -521,7 +527,7 @@ void MainView2D::updateFixtureItem(Fixture *fixture, quint16 headIndex, quint16 break; case QLCChannel::Shutter: { - if (previous.count() && value == uchar(previous.at(i))) + if (previous.length() && value == uchar(previous.at(i))) break; int high = 200, low = 800; @@ -570,13 +576,13 @@ void MainView2D::selectFixture(QQuickItem *fxItem, bool enable) void MainView2D::updateFixtureSelection(QList fixtures) { QMapIterator it(m_itemsMap); - while(it.hasNext()) + while (it.hasNext()) { it.next(); quint32 fxID = it.key(); bool enable = false; - if(fixtures.contains(fxID)) + if (fixtures.contains(fxID)) enable = true; selectFixture(it.value(), enable); @@ -760,6 +766,24 @@ void MainView2D::setPointOfView(int pointOfView) slotRefreshView(); } +QString MainView2D::backgroundImage() +{ + return m_monProps->commonBackgroundImage(); +} + +void MainView2D::setBackgroundImage(QString image) +{ + QString strippedPath = image.replace("file://", ""); + QString currentImage = m_monProps->commonBackgroundImage(); + + if (strippedPath == currentImage) + return; + + Tardis::instance()->enqueueAction(Tardis::EnvironmentBackgroundImage, 0, QVariant(currentImage), QVariant(strippedPath)); + m_monProps->setCommonBackgroundImage(strippedPath); + emit backgroundImageChanged(); +} + diff --git a/qmlui/mainview2d.h b/qmlui/mainview2d.h index d1e8864aac..06ce869662 100644 --- a/qmlui/mainview2d.h +++ b/qmlui/mainview2d.h @@ -40,6 +40,7 @@ class MainView2D : public PreviewContext Q_PROPERTY(qreal gridScale READ gridScale WRITE setGridScale NOTIFY gridScaleChanged) Q_PROPERTY(qreal cellPixels READ cellPixels WRITE setCellPixels NOTIFY cellPixelsChanged) Q_PROPERTY(int pointOfView READ pointOfView WRITE setPointOfView NOTIFY pointOfViewChanged) + Q_PROPERTY(QString backgroundImage READ backgroundImage WRITE setBackgroundImage NOTIFY backgroundImageChanged) public: explicit MainView2D(QQuickView *view, Doc *doc, QObject *parent = 0); @@ -114,6 +115,10 @@ class MainView2D : public PreviewContext int pointOfView() const; void setPointOfView(int pointOfView); + /** Get/Set the main background image */ + QString backgroundImage(); + void setBackgroundImage(QString image); + protected: /** First time 2D view variables initializations */ bool initialize2DProperties(); @@ -128,6 +133,7 @@ class MainView2D : public PreviewContext void gridScaleChanged(qreal gridScale); void cellPixelsChanged(qreal cellPixels); void pointOfViewChanged(int pointOfView); + void backgroundImageChanged(); public slots: /** @reimp */ diff --git a/qmlui/mainview3d.cpp b/qmlui/mainview3d.cpp index 2f27381cee..22c88051e2 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() @@ -160,17 +162,16 @@ void MainView3D::resetItems() QMetaObject::invokeMethod(m_scene3D, "updateFrameGraph", Q_ARG(QVariant, false)); QMapIterator it(m_entitiesMap); - while(it.hasNext()) + while (it.hasNext()) { 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 + if (e->m_rootItem) + e->m_rootItem->setProperty("enabled", false); // workaround for the above + delete e; } //const auto end = m_entitiesMap.end(); @@ -179,7 +180,7 @@ void MainView3D::resetItems() m_entitiesMap.clear(); QMapIterator it2(m_genericMap); - while(it2.hasNext()) + while (it2.hasNext()) { it2.next(); SceneItem *e = it2.value(); @@ -196,6 +197,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()); @@ -214,7 +261,7 @@ void MainView3D::setUniverseFilter(quint32 universeFilter) PreviewContext::setUniverseFilter(universeFilter); QMapIterator it(m_entitiesMap); - while(it.hasNext()) + while (it.hasNext()) { it.next(); quint32 itemID = it.key(); @@ -225,7 +272,10 @@ void MainView3D::setUniverseFilter(quint32 universeFilter) if (fixture == nullptr) return; - SceneItem *meshRef = m_entitiesMap.value(itemID); + SceneItem *meshRef = m_entitiesMap.value(itemID, nullptr); + + if (meshRef == nullptr || meshRef->m_rootItem == nullptr) + continue; if (universeFilter == Universe::invalid() || fixture->universe() == (quint32)universeFilter) { @@ -472,6 +522,7 @@ void MainView3D::createFixtureItem(quint32 fxID, quint16 headIndex, quint16 link if (fixture == nullptr) return; + QLCFixtureMode *fxMode = fixture->fixtureMode(); QString meshPath = meshDirectory() + "fixtures" + QDir::separator(); QString openGobo = goboDirectory() + QDir::separator() + "Others/open.svg"; quint32 itemID = FixtureUtils::fixtureItemID(fxID, headIndex, linkedIndex); @@ -500,6 +551,13 @@ void MainView3D::createFixtureItem(quint32 fxID, quint16 headIndex, quint16 link return; } newItem->setProperty("headsNumber", fixture->heads()); + + if (fxMode != nullptr) + { + QLCPhysical phy = fxMode->physical(); + if (phy.layoutSize() != QSize(1, 1)) + newItem->setProperty("headsLayout", phy.layoutSize()); + } } else if (fixture->type() == QLCFixtureDef::LEDBarPixels) { @@ -516,6 +574,13 @@ void MainView3D::createFixtureItem(quint32 fxID, quint16 headIndex, quint16 link return; } newItem->setProperty("headsNumber", fixture->heads()); + + if (fxMode != nullptr) + { + QLCPhysical phy = fxMode->physical(); + if (phy.layoutSize() != QSize(1, 1)) + newItem->setProperty("headsLayout", phy.layoutSize()); + } } else { @@ -578,6 +643,9 @@ void MainView3D::setFixtureFlags(quint32 itemID, quint32 flags) meshRef->m_rootItem->setProperty("enabled", (flags & MonitorProperties::HiddenFlag) ? false : true); meshRef->m_selectionBox->setProperty("enabled", (flags & MonitorProperties::HiddenFlag) ? false : true); + + meshRef->m_rootItem->setProperty("invertedPan", (flags & MonitorProperties::InvertedPanFlag) ? true : false); + meshRef->m_rootItem->setProperty("invertedTilt", (flags & MonitorProperties::InvertedTiltFlag) ? true : false); } Qt3DCore::QTransform *MainView3D::getTransform(QEntity *entity) @@ -1033,6 +1101,12 @@ void MainView3D::initializeFixture(quint32 itemID, QEntity *fxEntity, QSceneLoad meshRef->m_selectionBox->setProperty("enabled", false); } + if (itemFlags & MonitorProperties::InvertedPanFlag) + meshRef->m_rootItem->setProperty("invertedPan", true); + + if (itemFlags & MonitorProperties::InvertedTiltFlag) + meshRef->m_rootItem->setProperty("invertedTilt", true); + m_createItemCount--; // Update the Scene Graph only when the last fixture has been added to the Scene @@ -1102,7 +1176,8 @@ void MainView3D::updateFixtureItem(Fixture *fixture, quint16 headIndex, quint16 } quint32 masterDimmerChannel = fixture->masterIntensityChannel(); - qreal masterDimmerValue = qreal(fixture->channelValueAt(int(masterDimmerChannel))) / 255.0; + qreal masterDimmerValue = masterDimmerChannel != QLCChannel::invalid() ? + qreal(fixture->channelValueAt(int(masterDimmerChannel))) / 255.0 : 1.0; for (int headIdx = 0; headIdx < fixture->heads(); headIdx++) { @@ -1111,8 +1186,13 @@ void MainView3D::updateFixtureItem(Fixture *fixture, quint16 headIndex, quint16 headDimmerChannel = masterDimmerChannel; qreal intensityValue = 1.0; + bool hasDimmer = false; + if (headDimmerChannel != QLCChannel::invalid()) + { intensityValue = qreal(fixture->channelValueAt(int(headDimmerChannel))) / 255.0; + hasDimmer = true; + } if (headDimmerChannel != masterDimmerChannel) intensityValue *= masterDimmerValue; @@ -1123,7 +1203,7 @@ void MainView3D::updateFixtureItem(Fixture *fixture, quint16 headIndex, quint16 Q_ARG(QVariant, headIdx), Q_ARG(QVariant, intensityValue)); - color = FixtureUtils::headColor(fixture, headIdx); + color = FixtureUtils::headColor(fixture, hasDimmer, headIdx); QMetaObject::invokeMethod(fixtureItem, "setHeadRGBColor", Q_ARG(QVariant, headIdx), @@ -1166,7 +1246,7 @@ void MainView3D::updateFixtureItem(Fixture *fixture, quint16 headIndex, quint16 break; case QLCChannel::Speed: { - if (previous.count() && value == uchar(previous.at(i))) + if (previous.length() && value == uchar(previous.at(i))) break; int panSpeed, tiltSpeed; @@ -1202,7 +1282,7 @@ void MainView3D::updateFixtureItem(Fixture *fixture, quint16 headIndex, quint16 break; case QLCChannel::Beam: { - if (previous.count() && value == uchar(previous.at(i))) + if (previous.length() && value == uchar(previous.at(i))) break; switch (ch->preset()) @@ -1220,7 +1300,7 @@ void MainView3D::updateFixtureItem(Fixture *fixture, quint16 headIndex, quint16 break; case QLCChannel::Gobo: { - if (previous.count() && value == uchar(previous.at(i))) + if (previous.length() && value == uchar(previous.at(i))) break; QLCCapability *cap = ch->searchCapability(value); @@ -1264,7 +1344,7 @@ void MainView3D::updateFixtureItem(Fixture *fixture, quint16 headIndex, quint16 break; case QLCChannel::Shutter: { - if (previous.count() && value == uchar(previous.at(i))) + if (previous.length() && value == uchar(previous.at(i))) break; int high = 200, low = 800; @@ -1291,13 +1371,16 @@ void MainView3D::updateFixtureItem(Fixture *fixture, quint16 headIndex, quint16 void MainView3D::updateFixtureSelection(QList fixtures) { QMapIterator it(m_entitiesMap); - while(it.hasNext()) + while (it.hasNext()) { it.next(); quint32 fxID = it.key(); - SceneItem *meshRef = m_entitiesMap.value(fxID); + SceneItem *meshRef = m_entitiesMap.value(fxID, nullptr); + + if (meshRef == nullptr || meshRef->m_rootItem == nullptr) + return; - if(fixtures.contains(fxID)) + if (fixtures.contains(fxID)) { meshRef->m_rootItem->setProperty("isSelected", true); meshRef->m_selectionBox->setProperty("isSelected", true); @@ -1315,7 +1398,7 @@ void MainView3D::updateFixtureSelection(quint32 itemID, bool enable) qDebug() << "[View3D] item" << itemID << "selected:" << enable; SceneItem *meshRef = m_entitiesMap.value(itemID, nullptr); - if (meshRef) + if (meshRef && meshRef->m_rootItem) { meshRef->m_rootItem->setProperty("isSelected", enable); meshRef->m_selectionBox->setProperty("isSelected", enable); @@ -1439,8 +1522,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; } @@ -1612,7 +1698,7 @@ void MainView3D::setItemSelection(int itemID, bool enable, int keyModifiers) if (enable && keyModifiers == 0) { - for (int id : m_genericSelectedItems) + for (int &id : m_genericSelectedItems) { SceneItem *meshRef = m_genericMap.value(id, nullptr); if (meshRef) @@ -1647,7 +1733,7 @@ int MainView3D::genericSelectedCount() const void MainView3D::removeSelectedGenericItems() { - for (int id : m_genericSelectedItems) + for (int &id : m_genericSelectedItems) { SceneItem *meshRef = m_genericMap.take(id); if (meshRef) @@ -1664,7 +1750,7 @@ void MainView3D::removeSelectedGenericItems() void MainView3D::normalizeSelectedGenericItems() { - for (int id : m_genericSelectedItems) + for (int &id : m_genericSelectedItems) { SceneItem *meshRef = m_genericMap.value(id, nullptr); if (meshRef) @@ -1696,7 +1782,7 @@ void MainView3D::updateGenericItemsList() { m_genericItemsList->clear(); - for (quint32 itemID : m_monProps->genericItemsID()) + for (quint32 &itemID : m_monProps->genericItemsID()) { QVariantMap itemMap; itemMap.insert("itemID", itemID); @@ -1718,6 +1804,9 @@ void MainView3D::updateGenericItemPosition(quint32 itemID, QVector3D pos) if (isEnabled() == false) return; + QVector3D currPos = m_monProps->itemPosition(itemID); + Tardis::instance()->enqueueAction(Tardis::GenericItemSetPosition, itemID, QVariant(currPos), QVariant(pos)); + m_monProps->setItemPosition(itemID, pos); SceneItem *item = m_genericMap.value(itemID, nullptr); @@ -1750,7 +1839,7 @@ void MainView3D::setGenericItemsPosition(QVector3D pos) else { // relative position change - for (int itemID : m_genericSelectedItems) + for (int &itemID : m_genericSelectedItems) { QVector3D newPos = m_monProps->itemPosition(itemID) + pos; updateGenericItemPosition(itemID, newPos); @@ -1765,6 +1854,9 @@ void MainView3D::updateGenericItemRotation(quint32 itemID, QVector3D rot) if (isEnabled() == false) return; + QVector3D currRot = m_monProps->itemRotation(itemID); + Tardis::instance()->enqueueAction(Tardis::GenericItemSetRotation, itemID, QVariant(currRot), QVariant(rot)); + m_monProps->setItemRotation(itemID, rot); SceneItem *item = m_genericMap.value(itemID, nullptr); if (item == nullptr || item->m_rootTransform == nullptr) @@ -1797,7 +1889,7 @@ void MainView3D::setGenericItemsRotation(QVector3D rot) else { // relative position change - for (int itemID : m_genericSelectedItems) + for (int &itemID : m_genericSelectedItems) { QVector3D newRot = m_monProps->itemRotation(itemID) + rot; @@ -1822,6 +1914,9 @@ void MainView3D::updateGenericItemScale(quint32 itemID, QVector3D scale) if (isEnabled() == false) return; + QVector3D currScale = m_monProps->itemScale(itemID); + Tardis::instance()->enqueueAction(Tardis::GenericItemSetScale, itemID, QVariant(currScale), QVariant(scale)); + m_monProps->setItemScale(itemID, scale); SceneItem *item = m_genericMap.value(itemID, nullptr); if (item == nullptr || item->m_rootTransform == nullptr) @@ -1855,7 +1950,7 @@ void MainView3D::setGenericItemsScale(QVector3D scale) } else { - for (int itemID : m_genericSelectedItems) + for (int &itemID : m_genericSelectedItems) { QVector3D newScale = m_monProps->itemScale(itemID) + normScale; updateGenericItemScale(itemID, newScale); 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 f8dcc0c685..6084f5955b 100644 --- a/qmlui/mainviewdmx.cpp +++ b/qmlui/mainviewdmx.cpp @@ -58,7 +58,7 @@ void MainViewDMX::setUniverseFilter(quint32 universeFilter) { PreviewContext::setUniverseFilter(universeFilter); QMapIterator it(m_itemsMap); - while(it.hasNext()) + while (it.hasNext()) { it.next(); quint32 fxID = it.key(); @@ -77,7 +77,7 @@ void MainViewDMX::setUniverseFilter(quint32 universeFilter) void MainViewDMX::reset() { QMapIterator it(m_itemsMap); - while(it.hasNext()) + while (it.hasNext()) { it.next(); Fixture *fixture = m_doc->fixture(it.key()); @@ -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) @@ -150,7 +153,7 @@ void MainViewDMX::updateFixture(Fixture *fixture) void MainViewDMX::updateFixtureSelection(QListfixtures) { QMapIterator it(m_itemsMap); - while(it.hasNext()) + while (it.hasNext()) { it.next(); quint32 itemID = FixtureUtils::fixtureItemID(it.key(), 0, 0); diff --git a/qmlui/modelselector.cpp b/qmlui/modelselector.cpp index 7a9a8bf4f4..e641d1ab8f 100644 --- a/qmlui/modelselector.cpp +++ b/qmlui/modelselector.cpp @@ -22,57 +22,81 @@ ModelSelector::ModelSelector(QObject *parent) : QObject(parent) + , m_previousIndex(-1) , m_itemsCount(0) { } ModelSelector::~ModelSelector() { - m_selectedIndices.clear(); } -void ModelSelector::selectItem(quint32 index, ListModel *model, bool multiSelection) +void ModelSelector::selectSingleItem(int index, ListModel *model) { if (model == nullptr) return; - //qDebug() << "select item with index:" << index; - if (multiSelection == false) - { - for (quint32 sidx : m_selectedIndices) - { - QModelIndex idx = model->index(int(sidx), 0, QModelIndex()); - model->setDataWithRole(idx, "isSelected", false); - } - - m_selectedIndices.clear(); - m_itemsCount = 0; - } - - QModelIndex idx = model->index(int(index), 0, QModelIndex()); + QModelIndex idx = model->index(index, 0, QModelIndex()); model->setDataWithRole(idx, "isSelected", true); m_selectedIndices.append(index); m_itemsCount++; +} + +void ModelSelector::selectItem(int index, ListModel *model, int keyModifiers) +{ + if (model == nullptr) + return; + + //qDebug() << "select item with index:" << index; + if (keyModifiers == 0) + resetSelection(model); + + // handle multirow selection + if (keyModifiers & Qt::ShiftModifier) + { + if (index == m_previousIndex) + return; + + int startIndex = index > m_previousIndex ? m_previousIndex + 1 : index; + int endIndex = index > m_previousIndex ? index : m_previousIndex - 1; + + for (int i = startIndex; i <= endIndex; i++) + selectSingleItem(i, model); + } + else + { + // Ctrl + select a single item + selectSingleItem(index, model); + m_previousIndex = index; + } emit itemsCountChanged(m_itemsCount); } +void ModelSelector::resetSelection(ListModel *model) +{ + for (quint32 &sidx : m_selectedIndices) + { + QModelIndex idx = model->index(int(sidx), 0, QModelIndex()); + model->setDataWithRole(idx, "isSelected", false); + } + + m_selectedIndices.clear(); + m_itemsCount = 0; + m_previousIndex = -1; +} + QVariantList ModelSelector::itemsList() { QVariantList list; - for (quint32 sidx : m_selectedIndices) + for (quint32 &sidx : m_selectedIndices) list.append(sidx); return list; } -void ModelSelector::resetSelection() -{ - //qDebug() << "[ModelSelector] resetSelection"; - m_selectedIndices.clear(); -} - int ModelSelector::itemsCount() const { return m_itemsCount; } + diff --git a/qmlui/modelselector.h b/qmlui/modelselector.h index 64d8482eb4..2c9f99e1a9 100644 --- a/qmlui/modelselector.h +++ b/qmlui/modelselector.h @@ -38,13 +38,21 @@ class ModelSelector : public QObject /** Add an entry to the selected items list. * If $multiSelection is false, every previous item in the list will be * deselected. */ - Q_INVOKABLE void selectItem(quint32 index, ListModel *model, bool multiSelection); + Q_INVOKABLE void selectItem(int index, ListModel *model, int keyModifiers); + /** Reset the currently active selection */ + Q_INVOKABLE void resetSelection(ListModel *model); + + /** Return the list of the currently selected + * item indices, as list of QVariant */ Q_INVOKABLE QVariantList itemsList(); - Q_INVOKABLE void resetSelection(); + /** Return the number of items currently selected */ int itemsCount() const; +private: + void selectSingleItem(int index, ListModel *model); + signals: void itemsCountChanged(int itemsCount); @@ -52,6 +60,9 @@ class ModelSelector : public QObject /** List of the currently selected item indices */ QList m_selectedIndices; + /** The rpviously selected item index */ + int m_previousIndex; + /** The number of items currently selected (e.g. Functions, Fixtures, etc..) */ int m_itemsCount; }; diff --git a/qmlui/palettemanager.cpp b/qmlui/palettemanager.cpp index 787a107bac..b68aed5e6c 100644 --- a/qmlui/palettemanager.cpp +++ b/qmlui/palettemanager.cpp @@ -18,6 +18,7 @@ */ #include +#include #include "palettemanager.h" #include "contextmanager.h" @@ -58,7 +59,9 @@ QVariant PaletteManager::paletteList() QLCPalette *PaletteManager::getPalette(quint32 id) { - return m_doc->palette(id); + QLCPalette *palette = m_doc->palette(id); + QQmlEngine::setObjectOwnership(palette, QQmlEngine::CppOwnership); + return palette; } QLCPalette *PaletteManager::getEditingPalette(int type) @@ -66,11 +69,27 @@ QLCPalette *PaletteManager::getEditingPalette(int type) qDebug() << "Requesting a palette for editing. Type" << type; if (m_editingMap.contains(type) == false) - m_editingMap[type] = new QLCPalette(QLCPalette::PaletteType(type)); + { + QLCPalette *palette = new QLCPalette(QLCPalette::PaletteType(type)); + m_editingMap[type] = palette; + QQmlEngine::setObjectOwnership(palette, QQmlEngine::CppOwnership); + } return m_editingMap.value(type); } +bool PaletteManager::releaseEditingPalette(int type) +{ + if (m_editingMap.contains(type)) + { + QLCPalette *palette = m_editingMap.take(type); + delete palette; + return true; + } + + return false; +} + quint32 PaletteManager::createPalette(QLCPalette *palette, QString name) { if (palette == nullptr) diff --git a/qmlui/palettemanager.h b/qmlui/palettemanager.h index ed8a6c3635..3dd10e2c60 100644 --- a/qmlui/palettemanager.h +++ b/qmlui/palettemanager.h @@ -58,6 +58,9 @@ class PaletteManager : public QObject * The returned palette is mapped by type on m_editingMap */ Q_INVOKABLE QLCPalette *getEditingPalette(int type); + /** Request the removal of a previously requested palette */ + Q_INVOKABLE bool releaseEditingPalette(int type); + /** Create a new palette, get a new ID and add it * to the current project */ Q_INVOKABLE quint32 createPalette(QLCPalette *palette, QString name); @@ -89,7 +92,6 @@ class PaletteManager : public QObject int colorCount() const { return m_colorCount; } int positionCount() const { return m_positionCount; } -protected: void updatePaletteList(); signals: diff --git a/qmlui/qlcplus_ca_ES.ts b/qmlui/qlcplus_ca_ES.ts index f9fe728f03..3d0e93858f 100644 --- a/qmlui/qlcplus_ca_ES.ts +++ b/qmlui/qlcplus_ca_ES.ts @@ -4,54 +4,58 @@ ActionsMenu - Open a project - Obrir un projecte + Obrir un projecte - - + + Project files Fitxers de Projecte - - - + + + All files Tots els Fitxers - + + Open a file + Obrir un arxiu + + + QLC+ files Arxius QLC+ - - + + Import from project Importa des del projecte - - + + Save project as... Desar projecte com... - + Your project has changes El vostre projecte té canvis - + Do you wish to save the current project first? Changes will be lost if you don't save them. Voleu desar primer el projecte actual? Es perdran els canvis si no els guarda. - + New project Nou projecte @@ -60,117 +64,122 @@ Es perdran els canvis si no els guarda. Obrir projecte - + Open file Obrir un arxiu - + Save project Desar projecte - + Undo Desfer - + Redo Refer - + Network Xarxa - + Server setup Configuració del servidor - + Client setup Configuració del client - + Address tool Eina d'adreces - + DMX Address tool Eina d'adreces DMX - + + UI Settings + Configuració de la interfície d'usuari + + + Toggle fullscreen Commutar a pantalla completa - + Language Llengua - + Catalan Català - + Dutch Holandés - + English Anglès - + French Francès - + German Alemany - + Italian Italià - + Japanese Japonès - + Polish Polac - + Russian Rus - + Spanish Espanyol - + Ukrainian Ucraïnès - + About Quant a @@ -291,12 +300,17 @@ Es perdran els canvis si no els guarda. Dispositiu de Sortida - + + Volume + Volum + + + Fade in Fade in - + Fade out Fade out @@ -312,20 +326,25 @@ Es perdran els canvis si no els guarda. BeamTool - + Beam Raig - + Beam degrees Graus del Raig - + Distance Distància + + + Projected diameter + Diàmetre projectat + BottomPanel @@ -338,8 +357,8 @@ Es perdran els canvis si no els guarda. ChannelEdit - - + + Custom Personalitzat @@ -347,88 +366,118 @@ Es perdran els canvis si no els guarda. ChannelEditor - + + Open a picture file + Obrir un fitxer d'imatge + + + + Gobo pictures + Imatges de gobo + + + + All files + Tots els fitxers + + + Name Nom - + Preset Predefinit - - + + Type Tipus - + Role Rol - + Coarse (MSB) Coarse (MSB) - + Fine (LSB) Fine (LSB) - + Default value Valor per defecte - + Delete the selected capabilities Eliminar les capacitats seleccionades - + + Capability wizard + Assistent de capacitats + + + + Empty description provided + S'ha proporcionat una descripció buida + + + + Overlapping with another capability + Superposició amb una altra capacitat + + + From De - + To A - + Description Descripció - + Preview Previsualització - + Primary color Color primari - + Secondary color Color secundari - + Value(s) Valor(s) - + Value 1 Valor 1 - + Value 2 Valor 2 @@ -436,122 +485,137 @@ Es perdran els canvis si no els guarda. ChaserEditor - + Add a new step Afegir un nou pas - + Remove the selected steps Eliminar el pas seleccionat - + Delete steps Eliminar passos - + Are you sure you want to remove the selected steps? Esteu segur que voleu eliminar els passos seleccionats? - + + Preview the previous step + Previsualitza el pas anterior + + + + Preview the next step + Previsualitza el pas següent + + + + Duplicate the selected step(s) + Duplicar els passos seleccionats + + + Print the Chaser steps Imprimiu els pasos del Chaser - + Run properties Propietats d'Execució - + Loop Bucle - + Single Shot Un sol cop - + Ping Pong Ping Pong - + Random Aleatòri - + Run Order Ordre d'Execució - + Forward Avançar - + Backward Enrere - + Direction Direcció - + Time Temps - + Beats Beats - + Tempo Tempo - - + + Default Defecte - - - + + + Common Comú - - - + + + Per Step Per Pas - + Fade In Fade In - + Fade Out Fade Out - + Duration Durada @@ -559,32 +623,32 @@ Es perdran els canvis si no els guarda. ChaserWidget - + Function Funció - + Fade In Fade In - + Hold Manté - + Fade Out Fade Out - + Duration Durada - + Note Nota @@ -592,22 +656,22 @@ Es perdran els canvis si no els guarda. CollectionEditor - + Add a function Afegir una funció - + Remove the selected function Eliminar la funció seleccionada - + Delete functions Eliminar funcions - + Are you sure you want to remove the selected functions? Esteu segur que voleu eliminar la funció seleccionada? @@ -615,17 +679,17 @@ Es perdran els canvis si no els guarda. ColorTool - + Basic Bàsic - + Full Complet - + Filters Filtres @@ -633,7 +697,7 @@ Es perdran els canvis si no els guarda. ColorToolBasic - + Selected color Color seleccionat @@ -641,87 +705,87 @@ Es perdran els canvis si no els guarda. ColorToolFilters - + Open filters menu Obrir el menú de filtres - + Cyan Cian - + Red Vermell - + White Blanc - + Magenta Magenta - + Green Verd - + Amber Àmbar - + Yellow Groc - + Blue Blau - + UV UV - + CMY CMY - + Add a new color filters file Afegir un nou fitxer de filtres de color - + Rename the current color filters file Reanomena el fitxer de filtres actual - + Save the current color filters file Desar el fitxer de filtres actual - + Add a new filter Afegir un nou filtre - + Delete the selected filter Eliminar el filtre seleccionat - + Paste the latest picked color as new filter Enganxar el darrer color seleccionat com a nou filtre @@ -729,37 +793,37 @@ Es perdran els canvis si no els guarda. ColorToolFull - + Red Vermell - + Green Verd - + Blue Blau - + White Blanc - + Amber Àmbar - + UV UV - + Selected color Color seleccionat @@ -767,12 +831,12 @@ Es perdran els canvis si no els guarda. ContextManager - + Universe Grid View Vista de graella d'Univers - + linked enllaçat @@ -803,154 +867,174 @@ Es perdran els canvis si no els guarda. EFXEditor - + Fixtures Fixtures - + Add a fixture/head Afegir un fixture/capçal - + Remove the selected fixture head(s) Eliminar el fixture seleccionat - + Fixture Fixture - + + Mode + Mode + + + Reverse Invertir - - + + Start offset Compensació d'inici - + + Position + Posició + + + + Dimmer + Dimmer + + + + RGB + RGB + + + Add a new fixture Afegir un nou fixture - - + + Pattern Patró - + Relative movement Moviment relatiu - + Width Amplada - + Height Alçada - + X offset Compensació X - + Y offset Compensació Y - + Rotation Rotació - + X frequency Frecüència X - + Y frequency Freqüència Y - + X phase Fase X - + Y phase Fase Y - + Speed Velocitat - + Fade in Fade In - Hold - Manté + Manté - + Fade out Fade Out - + Order and direction Ordre i direcció - + + Loop Bucle - + Single Shot Un sol cop - + Ping Pong Ping Pong - + Run Order Ordre d'Execució - + Forward Avançar - + Backward Enrere - + Direction Direcció @@ -958,7 +1042,7 @@ Es perdran els canvis si no els guarda. EditorTopBar - + Go back to the previous view Go back to the Function Manager Torna al Gestor de Funcions @@ -977,77 +1061,82 @@ Es perdran els canvis si no els guarda. !! Avís !! - + General General - + Manufacturer Fabricant - + Type Tipus - + Model Model - + Author Autor - + Physical properties Propietats físiques - + Channels Canals - + Add a new channel Afegir un canal nou - + Remove the selected channel(s) Elimina el(s) canal(s) seleccionat(s) - + + Channel wizard + Assistent de canals + + + Modes Modes - + Add a new mode Afegir un nou mode - + Remove the selected mode(s) Eliminar els mode(s) seleccionat(s) - + Aliases Àlies - + New channel %1 Nou canal %1 - + New mode Nou mode @@ -1060,37 +1149,37 @@ Es perdran els canvis si no els guarda. Control - + Universe Univers - + Activate auto detection Activar auto detecció - + Channel Canal - + Remove this input source Eliminar aquesta font d'entrada - + Custom feedbacks Retro alimentació personalitzada - + Lower Inferior - + Upper Superior @@ -1116,13 +1205,13 @@ Es perdran els canvis si no els guarda. FixtureBrowser - + Create a new fixture definition Add a new fixture definition Crea una nova definició de fixture - + Edit the selected fixture definition Edita la definició de fixture seleccionada @@ -1130,22 +1219,22 @@ Es perdran els canvis si no els guarda. FixtureChannelDelegate - + Auto (HTP) Auto (HTP) - + Auto (LTP) Auto (LTP) - + Forced HTP HTP forçat - + Forced LTP LTP forçat @@ -1176,7 +1265,7 @@ Es perdran els canvis si no els guarda. - + Save definition as... Desa la definició com... @@ -1186,27 +1275,32 @@ Es perdran els canvis si no els guarda. Error - + + Warning + Atenció + + + Back to QLC+ Torna a QLC+ - + New definition Nova definició - + Open definition Obre una definició - + Save definition Desa la defininció - + Unknown Desconegut @@ -1229,32 +1323,32 @@ Es perdran els canvis si no els guarda. Suprimeix els elements seleccionats - + Reset the entire group Reinicia el grup sencer - + Rotate 90° clockwise Gira 90º en sentit horari - + Rotate 180° clockwise Gira 180º en sentit horari - + Rotate 270° clockwise Gira 270º en sentit horari - + Flip horizontally Inverteix horizontalment - + Flip vertically Inverteix verticalment @@ -1262,67 +1356,77 @@ Es perdran els canvis si no els guarda. FixtureGroupManager - + + Error + Error + + + Add a new fixture group Afegir un nou grup de fixtures - + Remove the selected items Elimina els elements seleccionats - + Set a Group/Fixture/Channel search filter Estableix un filtre de cerca Grup/Fixture/Canal - + Rename the selected items Reanomena els elements seleccionats - + Rename items Reanomena els elements - + Inspect the selected item Inspecciona l'element seleccionat - + Toggle fixtures and channels properties Activar/Desactivar les propietats de fixtures i canals - + Add/Remove a linked fixture Afegir/Eliminar un fixture enllaçat - + Name Nom - + + Mode + Mode + + + Flags Indicadors - + Can fade Pot fer fade - + Behaviour Comportament - + Modifier Modificadors @@ -1330,67 +1434,85 @@ Es perdran els canvis si no els guarda. FixtureManager - - - + + + Head Capçal - + New group %1 Nou grup %1 - + %1 - Row %2 %1 .Columna %2 - + New filters %1 Nous filtres %1 + + FixtureNodeDelegate + + + Show/Hide this fixture + Mostra/Amaga aquest fixture + + + + Invert Pan + Invertir panoràmica + + + + Invert Tilt + Invertir inclinació + + FixtureProperties - + Fixture properties Propietats del fixture - + Name Nom - + Universe Univers - + Address Adreça - + Quantity Quantitat - + Channels Canals - + Gap Interval - + Mode Mode @@ -1622,67 +1744,67 @@ Es perdran els canvis si no els guarda. Estableix filtre de cerca de Funció - + <None> <Cap> - + New Scene Nova Escena - + New Chaser Nou Chaser - + New Sequence Nova Seqüència - + New EFX Nou EFX - + New Collection Nova Colecció - + New RGB Matrix Nova Matriu RGB - + New Script Nou Script - + New Show Nou Show - + New Audio Nou Àudio - + New Video Nou Vídeo - + (Copy) (Copia) - + New folder Nova carpeta @@ -1746,31 +1868,31 @@ Es perdran els canvis si no els guarda. InputOutputManager - + Input/Output Manager Gestor de Entrada/Sortida - + All universes Tots els universos - - - - - + + + + + Default device Dispositiu per defecte - + Disabled Desactivat - + Internal generator Generador intern @@ -1783,10 +1905,124 @@ Es perdran els canvis si no els guarda. Eliminar aquest perfil d'entrada + + InputProfileEditor + + + + + !! Warning !! + !! Avís !! + + + + Channel wizard activated + Assistent de canals activat + + + + You have enabled the input channel wizard. After clicking OK, wiggle your mapped input profile's controls. They should appear into the list. Click the wizard button again to stop channel auto-detection.<br><br>Note that the wizard cannot tell the difference between a knob and a slider so you will have to do the change manually. + Heu habilitat l'assistent del canal d'entrada. Després de fer clic a D'acord, moveu els controls del vostre perfil d'entrada assignat. Haurien d'aparèixer a la llista. Feu clic de nou al botó de l'assistent per aturar la detecció automàtica del canal.<br><br>Tingueu en compte que l'assistent no pot distingir entre un botó i un control lliscant, de manera que haureu de fer el canvi manualment. + + + + Unsaved changes + Canvis no desats + + + + Do you wish to save the current profile first? +Changes will be lost if you don't save them. + Voleu desar primer el perfil actual? +Els canvis es perdran si no els deseu. + + + + Manufacturer + Fabricant + + + + Model + Model + + + + + Type + Tipus + + + + MIDI Global Settings + Configuració Global MIDI + + + + When MIDI notes are used, send a Note Off when value is 0 + Quan s'utilitzen notes MIDI, envieu una Note Off quan el valor sigui 0 + + + + Channel + Canal + + + + Name + Nom + + + + + Behaviour + Comportament + + + + Generate an extra Press/Release when toggled + Genera un ordre adicional de Premer/Alliberar quan s'activa + + + + Movement + Moviment + + + + Sensitivity + Sensibilitat + + + + Custom Feedback + Feedback Personalitzat + + + + Lower value + Valor inferior + + + + Upper value + Valor superior + + + + Button %1 + Botó %1 + + + + Slider %1 + Slider %1 + + IntensityTool - + Intensity Intensitat @@ -1807,17 +2043,17 @@ Es perdran els canvis si no els guarda. Control - + Combination Combinació - + Activate auto detection Activa la detecció automàtica - + Remove this keyboard combination Elimina aquesta combinació de teclat @@ -1830,62 +2066,62 @@ Es perdran els canvis si no els guarda. Afegir Fixtures - + Fixture Groups Grups de Fixtures - + Palettes Paletes - + Intensity Intensitat - + Shutter Obturador - + Position Posició - + Color Color - + Color Wheel Roda de Color - + Gobos Gobos - + Beam Raig - + Pick a 3D point Trieu un punt 3D - + Toggle multiple item selection Activa/Desactiva la selecció de multiples elements - + Select/Deselect all fixtures Seleccionar/Desseleccionar tots els fixtures @@ -1893,40 +2129,45 @@ Es perdran els canvis si no els guarda. MainView - + Actions Accions - + Fixtures & Functions Fixtures i Funcions - + Virtual Console Consola Virtual - + Simple Desk Taula Simple - + Show Manager Gestor de Shows - + Input/Output Entrada/Sortida - + Off Desactivat + + + Stop all the running functions + Atura totes les funcions + MainView2D @@ -1939,27 +2180,27 @@ Es perdran els canvis si no els guarda. MainView3D - + 3D View Vista 3D - + Simple ground Terra simple - + Simple box Caixa simple - + Rock stage Escenari de rock - + Theatre stage Escenari teatral @@ -1975,38 +2216,62 @@ Es perdran els canvis si no els guarda. ModeEditor - + Name Nom - - + + Channels Canals - + + Create a new emitter + Crear un nou emissor + + + + Remove the selected channel(s) + Elimina el(s) canal(s) seleccionat(s) + + + + Drop channels here + Deixa anar els canals aquí + + + Acts on Actua sobre - + + Emitters + Emissors + + + + Remove the selected emitter(s) + Suprimeix els emissors seleccionats + + Heads - Capçal + Capçal - + Physical Físic - + Use global settings Empra els ajustos globals - + Override global settings Sobreescriu els ajustos globals @@ -2027,97 +2292,112 @@ Es perdran els canvis si no els guarda. PaletteFanningBox - - + Flat Pla - - + Linear Lineal - - + Square Quadrat - - + Saw Serra - - + Sine Sinusoidal - - Left to right - Esquerra a Dreta + Esquerra a Dreta - - Right to left - Dreta a Esquerra + Dreta a Esquerra - - Top to bottom - Dalt a Baix + Dalt a Baix - - Bottom to top - Baix a Dalt + Baix a Dalt - - Centered - Centrat + Centrat - + Show/Hide fanning options Mostra/Amaga opcions de ventall - + Create a new palette Crea una nova paleta - + Type Tipus - + Layout Disposició - + + X Ascending + X Ascendent + + + + X Descending + X Descendent + + + + Y Ascending + Y Ascendent + + + + Y Descending + Y Descendent + + + + Z Ascending + Z Ascendent + + + + Z Descending + Z Descendent + + + Amount Quantitat - + Value Valor - + Pick the selected color Tria el color seleccionat @@ -2150,12 +2430,12 @@ Es perdran els canvis si no els guarda. Suprimeix la(les) paleta(s) selecionada(s) - + Are you sure you want to delete the following items? Esteu segur que vols eliminar els següents elements? - + Delete items Elimina elements @@ -2169,8 +2449,8 @@ Es perdran els canvis si no els guarda. - - + + Type Tipus @@ -2180,84 +2460,84 @@ Es perdran els canvis si no els guarda. Lúmens - + Colour Temp (K) Temperatura de color (K) - + Lens Lent - + Min Degrees Graus mínims - + Max Degrees Graus màxims - + Head(s) Capçal(s) - + Pan Max Degrees Graus màxims panorama - + Tilt Max Degrees Graus màxim inclinació - + Layout (Columns x Rows) Disposició (Columnes x Files) - + Dimensions Dimensions - + Weight Pes - + Width Amplada - + Height Alçada - + Depth Profunditat - + Electrical Elèctric - + Power Consumption Consum energètic - + DMX Connector Connector DMX @@ -2290,65 +2570,241 @@ Es perdran els canvis si no els guarda. llicència Apache 2.0 + + PopupChannelModifiers + + + Channel Modifiers Editor + Editor de Modificadors de Canal + + + + Insert a modified value after the selected + Insereix un valor modificat després de la selecció + + + + Delete the selected modifier value + Suprimeix el valor del modificador seleccionat + + + + Rename the selected modifier template + Canvia el nom de la plantilla de modificador seleccionada + + + + Save the selected modifier template + Desa la plantilla de modificador seleccionada + + + + Templates + Plantilles + + + + Original DMX value + Valor DMX Original + + + + Modified DMX value + Valor DMX Modificat + + + + PopupChannelWizard + + + Fixture Editor Wizard + Assistent del Editor de Fixtures + + + + Properties + Propiedtats + + + + Start + Inici + + + + Width + Amplada + + + + Amount + Quantitat + + + + Type + Tipus + + + + Red + Vermell + + + + Green + Verd + + + + Blue + Blau + + + + White + Blanc + + + + Amber + Ambre + + + + UV + UV + + + + RGB + RGB + + + + RGBW + RGBW + + + + RGBAW + RGBAW + + + + Dimmer + Dimmer + + + + Pan + Pan + + + + Tilt + Tilt + + + + Color Macro + Color de Macro + + + + Shutter + Obturador + + + + Beam + Raig + + + + Effect + Efecte + + + + Label + Etiqueta + + + + Capability # + Capacitat # + + + + Channel # + Canal # + + + + Preview + Previsualització + + PopupCreatePalette - + Create a new palette Crea una nova paleta - + Dimmer Dimmer - + Color Color - + Position Posició - + Shutter Obturador - + Gobo Gobo - + Palette name Nom de la Paleta - + New Palette Nova Paleta - + Type Tipus - + Also create a Scene Crea també una escena - + Scene name Nom de la escena - + New Scene Nova Escena @@ -2356,87 +2812,87 @@ Es perdran els canvis si no els guarda. PopupDMXDump - + Enter a name for the scene Introduïu un nom per a l'escena - + Scene name Nom de la escena - + New Scene Nova Escena - + Don't ask again No tornis a preguntar - + Available channel types Tipus de canals disponibles - + Intensity Intensitat - + RGB/CMY/WAUV RGB/CMY/WAUV - + Color macros Macros de colors - + Gobo Gobo - + Pan Pan - + Tilt Tilt - + Speed Velocitat - + Shutter/Strobe Shutter/Strobo - + Prism Prisma - + Beam Raig - + Effect Efecte - + Maintenance Manteniment @@ -2444,7 +2900,7 @@ Es perdran els canvis si no els guarda. PopupDisclaimer - + Disclaimer Avís @@ -2467,6 +2923,54 @@ Es perdran els canvis si no els guarda. Funcions + + PopupInputChannelEditor + + + Input Channel Editor + Editor de Canals d'Entrada + + + + Input Channel + Canal d'Entrada + + + + Number + Nombre + + + + Name + Nom + + + + Type + Tipus + + + + Channel + Canal + + + + Message + Missatge + + + + Parameter + Paràmetre + + + + Note + Nota + + PopupManualInputSource @@ -2531,27 +3035,27 @@ Es perdran els canvis si no els guarda. PopupNetworkClient - + Disconnected Desconnectat - + Waiting for access Esperant per accedir - + Downloading project Descarrega projecte - + Connected Connectat - + QLC+ client setup Configuració client QLC+ @@ -2594,7 +3098,7 @@ Es perdran els canvis si no els guarda. PopupNetworkConnect - + Client access request Petició d'accés d'un client @@ -2727,12 +3231,12 @@ Nivell d'accés: PopupPINRequest - + Page PIN PIN de la pàgina - + Remember for this session Recorda per aquesta sessió @@ -2740,22 +3244,22 @@ Nivell d'accés: PopupPINSetup - + Current PIN PIN Actual - + New PIN Nou PIN - + Confirm PIN Confirma PIN - + New PIN mismatch El nou PIN no coincideix @@ -2763,22 +3267,22 @@ Nivell d'accés: PopupRenameItems - + New name Nou nom - + Enable numbering Habilita numeració - + Start number Nombre d'inici - + Digits Dígits @@ -2786,28 +3290,76 @@ Nivell d'accés: PositionTool - + Position Posició - + Rotate 90° clockwise Gira 90º en sentit horari - - + + Snap to the previous value Salta al valor anterior - - + + Snap to the next value Salta al pròxim valor + + ProfilesList + + + !! Warning !! + !! Avís !! + + + + Save this profile + Desa aquest perfil + + + + Toggle the automatic detection procedure + Commuta el procediment de detecció automàtica + + + + Add a new channel + Afegir un canal nou + + + + Create a new input profile + Crea un perfil d'entrada nou + + + + Edit the selected channel + Edita el canal seleccionat + + + + Edit the selected input profile + Edita el perfil d'entrada seleccionat + + + + Delete the selected channel + Suprimeix el canal seleccionat + + + + Delete the selected input profile(s) + Suprimeix el/s perfil/s d'entrada seleccionat/s + + RGBMatrixEditor @@ -2821,214 +3373,214 @@ Nivell d'accés: Patró - + Blend mode Mode de barreja - + Default (HTP) Defecte (HTP) - + Mask Màscara - + Additive Additiu - + Subtractive Substractiu - + Color mode Mode de color - + Default (RGB) Per defecte (RGB) - + White Blanc - + Amber Ambre - + UV UV - + Dimmer Dimmer - + Shutter Obturador - + Colors Colors - + Parameters Paràmetres - + Speed Velocitat - + Steps fade in Fade In del pas - + Steps hold Manteniment del pas - + Steps fade out Fade Out del pas - + Tempo type Tipus de Tempo - + Time Temps - + Beats Beats - + Order and direction Ordre i direcció - + Loop Bucle - + Single Shot Un sol cop - + Ping Pong Ping Pong - + Run Order Ordre d'execució - + Forward Endavant - + Backward Enrere - + Direction Direcció - + Text Text - + Please choose a font Trieu una font si us plau - - - + + + Animation Animació - + Letters Lletres - - + + Horizontal Horitzontal - - + + Vertical Vertical - - + + Offset Offset - - + + X X - - + + Y Y - + Image Imatge - + Select an image Seleccioni una imatge - + Static Estàtic @@ -3036,17 +3588,17 @@ Nivell d'accés: RGBPanelProperties - + RGB panel properties Propietats del npanell RGB - + Name Nom - + Universe Univers @@ -3205,7 +3757,7 @@ Nivell d'accés: Desa a una nova escena - + Function Preview Previsualització de la Funció @@ -3218,43 +3770,43 @@ Nivell d'accés: SceneEditor - + Add a fixture/group Afegeix un fixture/grup - + Add a palette Afegeix una paleta - + Remove the selected items Remove the selected fixtures Elimina els elements seleccionats - + Delete items Elimina elements - + Are you sure you want to remove the selected items? Esteu segur que vols eliminar els elements sleccionats? - + Speed Velocitat - + Fade in Fade in - + Fade out Fade out @@ -3262,67 +3814,67 @@ Nivell d'accés: ScriptEditor - + Add a method call at cursor position Afegeix una funció a la posició del cursor - + Show/hide functions tree Mostra/Oculta l'arbre de funcions - + Show/hide fixture tree Mostra/Oculta l'arbre de fixtures - + Check the script syntax Verifica la sintaxi del script - + Syntax check Verificació de sintaxi - + Start function Inicia funció - + Stop function Atura funció - + Set fixture channel Estableix el canal - + Wait time Temps d'espera - + Random number Nombre aleatòri - + Blackout Blackout - + System command Ordre del sistema - + File path Camí al fitxer @@ -3350,12 +3902,12 @@ Nivell d'accés: Elimina els fixtures seleccionats - + Steps Passos - + Fixtures Fixtures @@ -3363,107 +3915,122 @@ Nivell d'accés: SettingsView2D - + Environment Entorn - + Width Amplada - + Height Alçada - + Depth Profunditat - + Grid units Unitats de la quadrícula - + Meters Metres - + Feet Peus - + Point of view Punt de vista - + Top view Vista Superior - + Front view Vista Frontal - + Right side view Vista costat dret - + Left side view Vista costat esquerra + Custom Background + Fons personalitzat + + + + Select an image + Seleccioneu una imatge + + + + Reset background + Restableix el fons + + + Selected fixtures Fixtures seleccionats - + Gel color Color del filtre - + Rotation Rotació - + Alignment Alineació - + Align the selected items to the left Alinea els elements seleccionats a l'esquerra - + Align the selected items to the top Alinea els elements seleccionats a la part superior - + Distribution Distribució - + Equally distribute horizontally the selected items Distribueix horitzontalment els elements seleccionats - + Equally distribute vertically the selected items Distribueix verticalment els elements seleccionats @@ -3471,122 +4038,126 @@ Nivell d'accés: SettingsView3D - + Environment Entorn - + Type Tipus - + Width Amplada - + Height Alçada - + Depth Profunditat - + Rendering Renderizat - + Quality Qualitat - + Low Baixa - + Medium Mitja - + High Alta - + Ultra Ultra - + Ambient light Llum ambiental - + Smoke amount Quantitat de fum - + Show FPS Mostra FPS - + Scale Escala - + Custom items Elements personalitzats - + Select a mesh file Selecionar un fitxer mesh - + 3D files Fitxers 3D - + All files Tots els fitxers - + + Normalize the selected items + Normalitza els elements seleccionats + + Actions - Accions + Accions - + Add a new item to the scene Afegir un element nou a l'escena - + Remove the selected items Elimina els elements seleccionats - + Position Posició - + Rotation Rotació @@ -3612,16 +4183,16 @@ Nivell d'accés: ShowItem - - - + + + Position: Posició: - - - + + + Duration: Durada: @@ -3639,74 +4210,74 @@ Nivell d'accés: Mostra els ítems de color - + Unlock the selected items Desbloqueja els elements seleccionats - + Lock the selected items Bloqueja els elements seleccionats - + Snap to grid Ajusta a la graella - + Stretch the original function Estirar la funció original - + Remove the selected items Elimina els elements seleccionats - + Are you sure you want to remove the following items? (Note that the original functions will not be deleted) Esteu segur que voleu eliminar els elements següents? (Tingueu en compte que les funcions originals no se suprimiran) - + Delete show items Elimina elements del show - + Copy the selected items in the clipboard Copieu els elements seleccionats al porta-retalls - + Paste items in the clipboard at cursor position Enganxa elements del porta-retalls a la posició del cursor - + Play or resume Reprodueix o reprèn - + Stop or rewind Atura o rebobina - + Move the selected track up Mou la pista seleccionada cap amunt - + Move the selected track down Mou la pista seleccionada cap avall - + Create a new track Crea una nova pista @@ -3716,19 +4287,19 @@ Nivell d'accés: Show Manager - + New Show Nou Show - - + + Track %1 Pista %1 - - + + (Copy) (Copiar) @@ -3736,37 +4307,37 @@ Nivell d'accés: SimpleDesk - + Universe Univers - + Reset the whole universe Restableix tot l'univers - + Dump on a new Scene Bolcar a una nova escena - + Reset the channel Restableix el canal - + Fixture List Llista de fixtures - + Commands history Historial de comandaments - + Simple Desk Taula Simple @@ -3782,25 +4353,203 @@ Nivell d'accés: TrackDelegate - + Solo this track Només aquesta pista - + Mute this track Enmudeix aquesta pista + + UISettingsEditor + + + + Reset to default + Restableix al valor per defecte + + + + Scaling factor + Factor d'escala + + + + Background darker + Fons més fosc + + + + Background dark + Fons fosc + + + + Background medium + Fons mitjà + + + + Background light + Fons clar + + + + Background lighter + Fons mes clar + + + + Controls background + Controls del fons + + + + Foreground main + Primer pla principal + + + + Foreground medium + Primer pla mitjà + + + + Foreground light + Primer pla clar + + + + Toolbar gradient start + Barra d'eines del Inici del degradat + + + + Sub-toolbar gradient start + Subbarra d'eines del inici del degradat + + + + Toolbar gradient end + Barra d'eines de fi del degradat + + + + Toolbar hover gradient start + Barra d'eines Inici del degradat en passar per sobre + + + + Toolbar hover gradient end + Barra d'eines Fi del degradat en passar per sobre + + + + Toolbar selection + Barra d'eines de selecció + + + + Sub-toolbar selection + Sub-barra d'eines de selecció + + + + Section header + Capçalera de la secció + + + + Section header divider + Divisor de capçalera de secció + + + + Item highlight + Ressaltat d'elements + + + + Item highlight pressed + Element ressaltat premut + + + + Item hover + Element en passar-hi per sobre + + + + Item selection + Selecció d'elements + + + + VC Frame drop area + Àrea de caiguda del marc VC + + + + Item dark border + Vora fosca del element + + + + Save to file + Desa a un fitxer + + + + Operation completed + Operació completada + + + + Error + Error + + + + UniverseGridView + + + Error + Error + + + + Unable to perform the operation. +There is either not enough space or the target universe in invalid + No s'ha pogut realitzar l'operació. +No hi ha prou espai o l'univers objectiu en un valor no vàlid + + + + Cut the selected items into clipboard + Retalla el elements selecionats al porta retalls + + + + Paste items in the clipboard at the first available position + Enganxa els elements del porta-retalls a la primera posició disponible + + UniverseIOItem - Passthrough - Passthrough + Passthrough + + + + Enable/Disable passthrough + Activa/Desactiva el pas a traves - + Enable/Disable feedbacks Habilita/Deshabilita feedback @@ -4012,74 +4761,74 @@ Nivell d'accés: Afegeix una funció programada - + Remove this schedule Elimina aquesta funció - + Start time Hora d'inici - + Stop time Hora d'aturada - + Enable the stop time Habilita l'hora d'aturada - + M As in Monday Di - + T As in Tuesday Dt - + W As in Wednesday Dc - + T As in Thursday Dj - + F As in Friday Dv - + S As in Saturday Ds - + S As in Sunday Dg - + Repeat weekly Repetir setmanalment - + Add a new schedule Afegir una nova programació @@ -4087,17 +4836,17 @@ Nivell d'accés: VCCueList - + Next Cue Pròxim pas - + Previous Cue Pas anterior - + Play/Stop/Pause Reprodueix/Atura/Pausa @@ -4110,17 +4859,17 @@ Nivell d'accés: Right Crossfade - + Stop/Pause Atura/Pausa - + Side Fader Fader Lateral - + Cue List %1 Llista d'accions %1 @@ -4128,27 +4877,32 @@ Nivell d'accés: VCCueListItem - + Play/Pause Atura/Pausa - + + Play/Stop + Reprodueix/Atura + + + Pause Pausa - + Stop Atura - + Previous cue Pas anterior - + Next cue Pròxim pas @@ -4241,30 +4995,35 @@ Nivell d'accés: VCFrame - + Next Page Pròxima pàgina - + Previous Page Pàgina anterior - + Enable Habilitar - + Collapse Redueix - + Frame %1 Marc %1 + + + Page %1 + Pàgina %1 + VCFrameItem @@ -4279,17 +5038,16 @@ Nivell d'accés: Habilitar/Inhabilita aquest marc - + Previous page Pàgina anterior - Page - Pàgina + Pàgina - + Next page Pàgina següent @@ -4332,10 +5090,21 @@ Nivell d'accés: Nombre de Pàgines - + Clone first page widgets Clona el contingut de la primera pàgina + + + + Shortcuts + Dreceres + + + + Shortcut name + Nom de la drecera + VCLabel @@ -4348,12 +5117,12 @@ Nivell d'accés: VCPage - + Page %1 Pàgina %1 - + Virtual Console Page %1 Pàgina %1 Consola Virtual @@ -4361,57 +5130,57 @@ Nivell d'accés: VCPageProperties - + Width Amplada - + Height Alçada - + Security Seguretat - + Set a PIN Estableix un PIN - + Error Error - + The entered PINs are either invalid or incorrect Els PINs entrats son invàlids o incorrectes - + Add page to the left Afegeix una pàgina a l'esquerra - + Add page to the right Afegeix una pàgina a la dreta - + Delete this page Suprimeix aquesta pàgina - + Delete page Suprimeix pàgina - + Are you sure you want to delete the selected page? Estàs segur que vols esborrar la pàgina seleccionada? @@ -4476,12 +5245,12 @@ Nivell d'accés: Control del reset - + Slider %1 Slider %1 - + Knob %1 Perilla %1 @@ -4554,82 +5323,82 @@ Nivell d'accés: Atributs - + Level mode Mode Nivell - + Channels Canals - + Add/Remove channels Afegeix/Suprimeix canals - + Click & Go button Botó Click & Go - + None Cap - + RGB/CMY RGB/CMY - + Gobo/Effect/Macro Gobo/Efecte/Macro - + Monitor channel levels Seguiment nivells dels canals - + Values range Rang de valors - + Upper limit Límit superior - + Lower limit Límit inferior - + Grand Master mode Mode del Gran Master - + Reduce values Reduir els valor - + Limit values Limita els valors - + Intensity channels Canals d'intensitat - + All channels Tos els canals @@ -4667,7 +5436,7 @@ Nivell d'accés: Speed Dial - Speed Dial + Selector de velocitat @@ -4706,8 +5475,8 @@ Nivell d'accés: Desconegut - - + + None Cap @@ -4715,87 +5484,87 @@ Nivell d'accés: VCWidgetProperties - + Select a widget first Selecciona un widget primer - + Settings Ajustos - + External controls Controls externs - + Basic properties Propietats bàsiques - + Label Etiqueta - + Background color Color de fons - + Foreground color Color primer pla - + Font Font - + Please choose a font Si us plau tria una font - + Background image Imatge de fons - + Select an image Selecciona una imatge - + Alignment Alineació - + Align the selected widgets to the left Alinea els widgets seleccionats a l'esquerra - + Align the selected widgets to the right Alinea els widgets seleccionat a la dreta - + Align the selected widgets to the top Alinea els widgets seleccionats a la part superior - + Align the selected widgets to the bottom Alinea els widgets seleccionats a la part inferior - + External Controls Controls externs @@ -4873,77 +5642,77 @@ Nivell d'accés: Pantalla de sortida - + Output mode Mode de sortida - + Windowed En finestra - + Fullscreen Pantalla completa - + Geometry Geometria - + Original Original - + Custom Personalitzat - + Position Posició - + Size Mida - + W W - + H H - + Rotation Rotació - + X X - + Y Y - + Z Z - + Layer Capa @@ -4951,72 +5720,72 @@ Nivell d'accés: VirtualConsole - + Error Error - + Invalid PIN entered El PIN entrat no es correcte - + Enable/Disable widgets snapping Habilita/Inhabilita l'ancoratge del widget - + Widget matrix setup Configuració de la matriu de widget - + Columns Columnes - + Rows Files - + Width Amplada - + Height Alçada - + Frame type Tipus de marc - + Normal Normal - + Solo Exclusiu - + Virtual Console Consola Virtual - + <None> <Cap> - + Page %1 Pàgina %1 @@ -5066,7 +5835,7 @@ Nivell d'accés: Speed Dial - Speed Dial + Selector de velocitat diff --git a/qmlui/qlcplus_de_DE.ts b/qmlui/qlcplus_de_DE.ts index 54e9ea24b9..cf99f3ab0d 100644 --- a/qmlui/qlcplus_de_DE.ts +++ b/qmlui/qlcplus_de_DE.ts @@ -4,54 +4,58 @@ ActionsMenu - Open a project - Ein Projekt öffnen + Ein Projekt öffnen - - + + Project files Projektdateien - - - + + + All files Alle Dateien - + + Open a file + + + + QLC+ files QLC+-Dateien - - + + Import from project Aus anderem Projekt importieren - - + + Save project as... Projekt speichern unter … - + Your project has changes Dein Projekt hat ungesicherte Änderungen - + Do you wish to save the current project first? Changes will be lost if you don't save them. Willst du das aktuelle Projekt erst speichern? Ungesicherte Änderungen gehen verloren, wenn du sie nicht speicherst. - + New project Neues Projekt @@ -60,117 +64,122 @@ Ungesicherte Änderungen gehen verloren, wenn du sie nicht speicherst.Projekt öffnen - + Open file Datei öffnen - + Save project Projekt speichern - + Undo Rückgängig - + Redo Wiederherstellen - + Network Netzwerk - + Server setup Serverkonfiguration - + Client setup Clientkonfiguration - + Address tool Adress-Werkzeug - + DMX Address tool DMX-Adress-Werkzeug - + + UI Settings + + + + Toggle fullscreen Vollbild umschalten - + Language Sprache - + Catalan Katalanisch - + Dutch Niederländisch - + English Englisch - + French Französisch - + German Deutsch - + Italian Italienisch - + Japanese Japanisch - + Polish Polnisch - + Russian Russisch - + Spanish Spanisch - + Ukrainian Ukrainisch - + About Über QLC+ @@ -292,12 +301,17 @@ Ungesicherte Änderungen gehen verloren, wenn du sie nicht speicherst.Audiogerät - + + Volume + + + + Fade in Einblenden - + Fade out Ausblenden @@ -313,20 +327,25 @@ Ungesicherte Änderungen gehen verloren, wenn du sie nicht speicherst. BeamTool - + Beam Lichtstrahl - + Beam degrees Strahlungswinkel - + Distance Distanz + + + Projected diameter + + BottomPanel @@ -339,8 +358,8 @@ Ungesicherte Änderungen gehen verloren, wenn du sie nicht speicherst. ChannelEdit - - + + Custom Angepasst @@ -348,88 +367,118 @@ Ungesicherte Änderungen gehen verloren, wenn du sie nicht speicherst. ChannelEditor - + + Open a picture file + + + + + Gobo pictures + + + + + All files + Alle Dateien + + + Name Name - + Preset Voreinstellung - - + + Type Typ - + Role Rolle - + Coarse (MSB) Grob (MSB) - + Fine (LSB) Fein (LSB) - + Default value Standardwert - + Delete the selected capabilities Ausgewählte Fähigkeiten löschen - + + Capability wizard + + + + + Empty description provided + + + + + Overlapping with another capability + + + + From Von - + To Bis - + Description Beschreibung - + Preview Vorschau - + Primary color primäre Farbe - + Secondary color sekundäre Farbe - + Value(s) Wert(e) - + Value 1 Wert 1 - + Value 2 Wert 2 @@ -437,122 +486,137 @@ Ungesicherte Änderungen gehen verloren, wenn du sie nicht speicherst. ChaserEditor - + Add a new step Neuen Schritt hinzufügen - + Remove the selected steps Ausgewählte Schritte entfernen - + Delete steps Schritte entfernen - + Are you sure you want to remove the selected steps? Willst du alle ausgewählten Schritte entfernen? - + + Preview the previous step + + + + + Preview the next step + + + + + Duplicate the selected step(s) + + + + Print the Chaser steps Chaser-Schritte drucken - + Run properties Abspieleigenschaften - + Loop Wiederholen - + Single Shot Einmal - + Ping Pong Hin und her - + Random Zufällig - + Run Order Ablauf - + Forward Vorwärts - + Backward Rückwärts - + Direction Richtung - + Time Zeit - + Beats Takt - + Tempo Tempo - - + + Default Standard - - - + + + Common Gemeinsam - - - + + + Per Step Pro Schritt - + Fade In Einblenden - + Fade Out Ausblenden - + Duration Dauer @@ -560,32 +624,32 @@ Ungesicherte Änderungen gehen verloren, wenn du sie nicht speicherst. ChaserWidget - + Function Funktion - + Fade In Einblenden - + Hold Halten - + Fade Out Ausblenden - + Duration Dauer - + Note Notiz @@ -593,22 +657,22 @@ Ungesicherte Änderungen gehen verloren, wenn du sie nicht speicherst. CollectionEditor - + Add a function Funktion hinzufügen - + Remove the selected function Ausgewählte Funktionen entfernen - + Delete functions Funktionen entfernen - + Are you sure you want to remove the selected functions? Willst du alle ausgewählten Funktionen entfernen? @@ -616,17 +680,17 @@ Ungesicherte Änderungen gehen verloren, wenn du sie nicht speicherst. ColorTool - + Basic Einfach - + Full Alle - + Filters Farbfilter @@ -634,7 +698,7 @@ Ungesicherte Änderungen gehen verloren, wenn du sie nicht speicherst. ColorToolBasic - + Selected color Ausgewählte Farbe @@ -642,88 +706,88 @@ Ungesicherte Änderungen gehen verloren, wenn du sie nicht speicherst. ColorToolFilters - + Open filters menu Filter-Menü öffnen - + Cyan Cyan - + Red Rot - + White Weiß - + Magenta Magenta - + Green Grün - + Amber Nicht "Bernstein", da "Amber" auch im Deutschen die übliche LED-Bezeichnung ist. Amber - + Yellow Gelb - + Blue Blau - + UV UV - + CMY CMY - + Add a new color filters file Neue Farbfilterdatei hinzufügen - + Rename the current color filters file Aktuelle Farbfilterdatei umbenennen - + Save the current color filters file Aktuelle Farbfilterdatei speichern - + Add a new filter Neuen Farbfilter hinzufügen - + Delete the selected filter Ausgewählten Farbfilter entfernen - + Paste the latest picked color as new filter Zuletzt gewählte Farbe als neuen Filter hinzufügen @@ -731,38 +795,38 @@ Ungesicherte Änderungen gehen verloren, wenn du sie nicht speicherst. ColorToolFull - + Red Rot - + Green Grün - + Blue Blau - + White Weiß - + Amber Nicht "Bernstein", da "Amber" auch im Deutschen die übliche LED-Bezeichnung ist. Amber - + UV UV - + Selected color Ausgewählte Farbe @@ -770,12 +834,12 @@ Ungesicherte Änderungen gehen verloren, wenn du sie nicht speicherst. ContextManager - + Universe Grid View Universum-Gitteransicht - + linked verknüpft @@ -806,154 +870,174 @@ Ungesicherte Änderungen gehen verloren, wenn du sie nicht speicherst. EFXEditor - + Fixtures Geräte - + Add a fixture/head Gerät/Head hinzufügen - + Remove the selected fixture head(s) Ausgewählte Geräte/Heads entfernen - + Fixture Gerät - + + Mode + Modus + + + Reverse Umdrehen - - + + Start offset Startversatz - + + Position + Position + + + + Dimmer + Dimmer + + + + RGB + + + + Add a new fixture Neues Gerät hinzufügen - - + + Pattern Figur - + Relative movement Relative Bewegung - + Width Breite - + Height Höhe - + X offset X-Versatz - + Y offset Y-Versatz - + Rotation Rotation - + X frequency X-Frequenz - + Y frequency Y-Frequenz - + X phase X-Phase - + Y phase Y-Phase - + Speed Geschwindigkeit - + Fade in Einblenden - Hold - Halten + Halten - + Fade out Ausblenden - + Order and direction Ablauf und Richtung - + + Loop Wiederholen - + Single Shot Einmal - + Ping Pong Hin und her - + Run Order Ablauf - + Forward Vorwärts - + Backward Rückwärts - + Direction Richtung @@ -961,7 +1045,7 @@ Ungesicherte Änderungen gehen verloren, wenn du sie nicht speicherst. EditorTopBar - + Go back to the previous view Go back to the Function Manager Zurück zur vorigen Ansicht @@ -980,77 +1064,82 @@ Ungesicherte Änderungen gehen verloren, wenn du sie nicht speicherst.!! Warnung !! - + General Allgemein - + Manufacturer Hersteller - + Type Typ - + Model Modell - + Author Autor - + Physical properties Physikalische Eigenschaften - + Channels Kanäle - + Add a new channel Neuen Kanal hinzufügen - + Remove the selected channel(s) Ausgewählte Kanäle entfernen - + + Channel wizard + + + + Modes Modi - + Add a new mode Neuen Modus hinzufügen - + Remove the selected mode(s) Ausgewählte Modi entfernen - + Aliases Aliase - + New channel %1 Neuer Kanal %1 - + New mode Neuer Modus @@ -1063,37 +1152,37 @@ Ungesicherte Änderungen gehen verloren, wenn du sie nicht speicherst.Verhalten - + Universe Universum - + Activate auto detection Automatische Erkennung - + Channel Kanal - + Remove this input source Eingangsquelle entfernen - + Custom feedbacks Eigene Rückmeldungen - + Lower Unterer Wert - + Upper Oberer Wert @@ -1119,13 +1208,13 @@ Ungesicherte Änderungen gehen verloren, wenn du sie nicht speicherst. FixtureBrowser - + Create a new fixture definition Add a new fixture definition Neue Gerätedefinition erstellen - + Edit the selected fixture definition Ausgewählte Gerätedefinition bearbeiten @@ -1133,22 +1222,22 @@ Ungesicherte Änderungen gehen verloren, wenn du sie nicht speicherst. FixtureChannelDelegate - + Auto (HTP) Auto (HTP) - + Auto (LTP) Auto (LTP) - + Forced HTP HTP erzwingen - + Forced LTP LTP erzwingen @@ -1179,7 +1268,7 @@ Ungesicherte Änderungen gehen verloren, wenn du sie nicht speicherst. - + Save definition as... Definition speichern als... @@ -1189,27 +1278,32 @@ Ungesicherte Änderungen gehen verloren, wenn du sie nicht speicherst.Fehler - + + Warning + + + + Back to QLC+ Zurück zu QLC+ - + New definition Neue Definition - + Open definition Definition öffnen - + Save definition Definition speichern - + Unknown Unbekannt @@ -1232,32 +1326,32 @@ Ungesicherte Änderungen gehen verloren, wenn du sie nicht speicherst.Ausgewählte Elemente löschen - + Reset the entire group Gesamte Gruppe zurücksetzen - + Rotate 90° clockwise Um 90° nach rechts drehen - + Rotate 180° clockwise Um 180° drehen - + Rotate 270° clockwise Um 90° nach links drehen - + Flip horizontally Horizontal spiegeln - + Flip vertically Vertikal spiegeln @@ -1265,67 +1359,77 @@ Ungesicherte Änderungen gehen verloren, wenn du sie nicht speicherst. FixtureGroupManager - + + Error + Fehler + + + Add a new fixture group Neue Gerätegruppe hinzufügen - + Remove the selected items Ausgewählte Elemente entfernen - + Set a Group/Fixture/Channel search filter Gruppen-/Gerät-/Kanal-Suchfilter setzen - + Rename the selected items Ausgewählte Elemente umbenennen - + Rename items Elemente umbenennen - + Inspect the selected item Ausgewähltes Element untersuchen - + Toggle fixtures and channels properties Geräte- und Kanaleigenschaften umschalten - + Add/Remove a linked fixture Verknüftes Gerät hinzufügen/entfernen - + Name Name - + + Mode + Modus + + + Flags Markierungen - + Can fade Kann überblenden - + Behaviour Verhalten - + Modifier Modifikator @@ -1333,67 +1437,85 @@ Ungesicherte Änderungen gehen verloren, wenn du sie nicht speicherst. FixtureManager - - - + + + Head Head - + New group %1 Neue Gruppe %1 - + %1 - Row %2 %1 – Reihe %2 - + New filters %1 Neue Filter %1 + + FixtureNodeDelegate + + + Show/Hide this fixture + + + + + Invert Pan + + + + + Invert Tilt + + + FixtureProperties - + Fixture properties Geräteeigenschaften - + Name Name - + Universe Universum - + Address Adresse - + Quantity Anzahl - + Channels Kanäle - + Gap Adressabstand - + Mode Modus @@ -1625,67 +1747,67 @@ Ungesicherte Änderungen gehen verloren, wenn du sie nicht speicherst.Funktion suchen - + <None> <Keine> - + New Scene Neue Szene - + New Chaser Neuer Chaser - + New Sequence Neue Sequenz - + New EFX Neuer Effekt - + New Collection Neue Sammlung - + New RGB Matrix Neuer RGB-Matrixeffekt - + New Script Neues Skript - + New Show Neue Show - + New Audio Neues Audio - + New Video Neues Video - + (Copy) (Kopie) - + New folder Neuer Ordner @@ -1749,31 +1871,31 @@ Ungesicherte Änderungen gehen verloren, wenn du sie nicht speicherst. InputOutputManager - + Input/Output Manager Eingänge/Ausgänge - + All universes Alle Universen - - - - - + + + + + Default device Standardgerät - + Disabled Deaktiviert - + Internal generator Interner Taktgenerator @@ -1786,10 +1908,123 @@ Ungesicherte Änderungen gehen verloren, wenn du sie nicht speicherst.Eingangsprofil entfernen + + InputProfileEditor + + + + + !! Warning !! + !! Warnung !! + + + + Channel wizard activated + + + + + You have enabled the input channel wizard. After clicking OK, wiggle your mapped input profile's controls. They should appear into the list. Click the wizard button again to stop channel auto-detection.<br><br>Note that the wizard cannot tell the difference between a knob and a slider so you will have to do the change manually. + + + + + Unsaved changes + + + + + Do you wish to save the current profile first? +Changes will be lost if you don't save them. + + + + + Manufacturer + Hersteller + + + + Model + Modell + + + + + Type + Typ + + + + MIDI Global Settings + + + + + When MIDI notes are used, send a Note Off when value is 0 + + + + + Channel + Kanal + + + + Name + Name + + + + + Behaviour + Verhalten + + + + Generate an extra Press/Release when toggled + + + + + Movement + + + + + Sensitivity + + + + + Custom Feedback + + + + + Lower value + + + + + Upper value + + + + + Button %1 + Knopf %1 + + + + Slider %1 + Schieberegler %1 + + IntensityTool - + Intensity Intensität @@ -1810,17 +2045,17 @@ Ungesicherte Änderungen gehen verloren, wenn du sie nicht speicherst.Verhalten - + Combination Kombination - + Activate auto detection Automatische Erkennung - + Remove this keyboard combination Tastenkombination entfernen @@ -1833,62 +2068,62 @@ Ungesicherte Änderungen gehen verloren, wenn du sie nicht speicherst.Geräte hinzufügen - + Fixture Groups Gerätegruppen - + Palettes Paletten - + Intensity Intensität - + Shutter Shutter - + Position Position - + Color Farbe - + Color Wheel Farbrad - + Gobos Gobos - + Beam Lichtstrahl - + Pick a 3D point 3D-Punkt auswählen - + Toggle multiple item selection Mehrfachauswahl umschalten - + Select/Deselect all fixtures Alle Geräte an-/abwählen @@ -1896,40 +2131,45 @@ Ungesicherte Änderungen gehen verloren, wenn du sie nicht speicherst. MainView - + Actions Aktionen - + Fixtures & Functions Geräte & Funktionen - + Virtual Console Virtuelle Konsole - + Simple Desk Einfaches Mischpult - + Show Manager Shows - + Input/Output Eingänge/Ausgänge - + Off Aus + + + Stop all the running functions + + MainView2D @@ -1942,27 +2182,27 @@ Ungesicherte Änderungen gehen verloren, wenn du sie nicht speicherst. MainView3D - + 3D View 3D-Ansicht - + Simple ground Einfacher Boden - + Simple box Einfache Box - + Rock stage Rockkonzertbühne - + Theatre stage Theaterbühne @@ -1978,38 +2218,62 @@ Ungesicherte Änderungen gehen verloren, wenn du sie nicht speicherst. ModeEditor - + Name Name - - + + Channels Kanäle - + + Create a new emitter + + + + + Remove the selected channel(s) + Ausgewählte Kanäle entfernen + + + + Drop channels here + + + + Acts on Wirkt auf - + + Emitters + + + + + Remove the selected emitter(s) + + + Heads - Köpfe + Köpfe - + Physical Physische Daten - + Use global settings Globale Einstellungen benutzen - + Override global settings Globale Einstellungen überschreiben @@ -2030,97 +2294,112 @@ Ungesicherte Änderungen gehen verloren, wenn du sie nicht speicherst. PaletteFanningBox - - + Flat Flach - - + Linear Linear - - + Square Rechteck - - + Saw Sägezahn - - + Sine Sinus - - Left to right - Von link nach rechts + Von link nach rechts - - Right to left - Von rechts nach links + Von rechts nach links - - Top to bottom - Von oben nach unten + Von oben nach unten - - Bottom to top - Von unten nach oben + Von unten nach oben - - Centered - Zentriert + Zentriert - + Show/Hide fanning options Zeige/Verstecke Fächer-Optionen - + Create a new palette Eine Palette anlegen - + Type Typ - + Layout Anordnung - + + X Ascending + + + + + X Descending + + + + + Y Ascending + + + + + Y Descending + + + + + Z Ascending + + + + + Z Descending + + + + Amount Menge - + Value Wert - + Pick the selected color Wähle die ausgewählte Farbe @@ -2153,12 +2432,12 @@ Ungesicherte Änderungen gehen verloren, wenn du sie nicht speicherst.Ausgewählte Palette(n) löschen - + Are you sure you want to delete the following items? Willst du alle folgenden Elemente löschen? - + Delete items Elemente löschen @@ -2172,8 +2451,8 @@ Ungesicherte Änderungen gehen verloren, wenn du sie nicht speicherst. - - + + Type Typ @@ -2183,84 +2462,84 @@ Ungesicherte Änderungen gehen verloren, wenn du sie nicht speicherst.Lumen - + Colour Temp (K) Farbtemperatur (K) - + Lens Linse - + Min Degrees Minimaler Winkel - + Max Degrees Maximaler Winkel - + Head(s) Köpfe - + Pan Max Degrees Maximaler Panwinkel - + Tilt Max Degrees Maximaler Tiltwinkel - + Layout (Columns x Rows) Layout (Spalten x Reihen) - + Dimensions Abmessungen - + Weight Gewicht - + Width Breite - + Height Höhe - + Depth Tiefe - + Electrical Elektrische Eigenschaften - + Power Consumption Leistungsbedarf - + DMX Connector DMX-Anschluss @@ -2293,65 +2572,241 @@ Ungesicherte Änderungen gehen verloren, wenn du sie nicht speicherst.Apache-2.0-Lizenz + + PopupChannelModifiers + + + Channel Modifiers Editor + + + + + Insert a modified value after the selected + + + + + Delete the selected modifier value + + + + + Rename the selected modifier template + + + + + Save the selected modifier template + + + + + Templates + + + + + Original DMX value + + + + + Modified DMX value + + + + + PopupChannelWizard + + + Fixture Editor Wizard + + + + + Properties + + + + + Start + + + + + Width + Breite + + + + Amount + Menge + + + + Type + Typ + + + + Red + Rot + + + + Green + Grün + + + + Blue + Blau + + + + White + Weiß + + + + Amber + Amber + + + + UV + UV + + + + RGB + + + + + RGBW + + + + + RGBAW + + + + + Dimmer + Dimmer + + + + Pan + Pan + + + + Tilt + Tilt + + + + Color Macro + + + + + Shutter + Shutter + + + + Beam + Lichtstrahl + + + + Effect + Effekt + + + + Label + Beschriftung + + + + Capability # + + + + + Channel # + + + + + Preview + Vorschau + + PopupCreatePalette - + Create a new palette Neue Palette erzeugen - + Dimmer Dimmer - + Color Farbe - + Position Position - + Shutter Shutter - + Gobo Gobo - + Palette name Name der Palette - + New Palette Neue Palette - + Type Typ - + Also create a Scene Ebenfalls eine Szene erstellen - + Scene name Szenenname - + New Scene Neue Szene @@ -2359,87 +2814,87 @@ Ungesicherte Änderungen gehen verloren, wenn du sie nicht speicherst. PopupDMXDump - + Enter a name for the scene Namen für die Szene eingeben - + Scene name Szenenname - + New Scene Neue Szene - + Don't ask again Nicht erneut fragen - + Available channel types Verfügbare Kanaltypen - + Intensity Intensität - + RGB/CMY/WAUV RGB/CMY/WAUV - + Color macros Farbmakros - + Gobo Gobo - + Pan Pan - + Tilt Tilt - + Speed Tempo - + Shutter/Strobe Shutter/Strobo - + Prism Prisma - + Beam Lichtstrahl - + Effect Effekt - + Maintenance Wartung @@ -2447,7 +2902,7 @@ Ungesicherte Änderungen gehen verloren, wenn du sie nicht speicherst. PopupDisclaimer - + Disclaimer Haftungsausschluss @@ -2470,6 +2925,54 @@ Ungesicherte Änderungen gehen verloren, wenn du sie nicht speicherst.Funktionen + + PopupInputChannelEditor + + + Input Channel Editor + + + + + Input Channel + + + + + Number + Nummer + + + + Name + Name + + + + Type + Typ + + + + Channel + Kanal + + + + Message + + + + + Parameter + + + + + Note + Notiz + + PopupManualInputSource @@ -2534,27 +3037,27 @@ Ungesicherte Änderungen gehen verloren, wenn du sie nicht speicherst. PopupNetworkClient - + Disconnected Getrennt - + Waiting for access Warte auf Zugriff - + Downloading project Projekt wird heruntergeladen - + Connected Verbunden - + QLC+ client setup QLC+ Client-Einrichtung @@ -2597,7 +3100,7 @@ Ungesicherte Änderungen gehen verloren, wenn du sie nicht speicherst. PopupNetworkConnect - + Client access request Zugriffsanforderung eines Clients @@ -2730,12 +3233,12 @@ Zugriffsstufe: PopupPINRequest - + Page PIN PIN für die Seite - + Remember for this session Für diese Sitzung merken @@ -2743,22 +3246,22 @@ Zugriffsstufe: PopupPINSetup - + Current PIN Aktueller PIN - + New PIN Neuer PIN - + Confirm PIN PIN bestätigen - + New PIN mismatch Neuer PIN stimmt nicht überein @@ -2766,22 +3269,22 @@ Zugriffsstufe: PopupRenameItems - + New name Neuer Name - + Enable numbering Nummerierung aktivieren - + Start number Startnummer - + Digits Ziffern @@ -2789,28 +3292,76 @@ Zugriffsstufe: PositionTool - + Position Position - + Rotate 90° clockwise Um 90° nach rechts drehen - - + + Snap to the previous value Auf den letzten Wert einrasten - - + + Snap to the next value Auf den nächsten Wert einrasten + + ProfilesList + + + !! Warning !! + !! Warnung !! + + + + Save this profile + + + + + Toggle the automatic detection procedure + + + + + Add a new channel + Neuen Kanal hinzufügen + + + + Create a new input profile + + + + + Edit the selected channel + + + + + Edit the selected input profile + + + + + Delete the selected channel + + + + + Delete the selected input profile(s) + + + RGBMatrixEditor @@ -2824,215 +3375,215 @@ Zugriffsstufe: Figur - + Blend mode Alternativ: "Mischmodus" oder "Überblendungsmodus" (evt. technisch nicht 100% korrekt) Mischverhalten - + Default (HTP) Standard (HTP) - + Mask Maske - + Additive Additiv - + Subtractive Subtraktiv - + Color mode Farbmodus - + Default (RGB) Standard (RGB) - + White Weiß - + Amber Amber - + UV UV - + Dimmer Dimmer - + Shutter Shutter - + Colors Farben - + Parameters Parameter - + Speed Geschwindigkeit - + Steps fade in Schritte blenden ein - + Steps hold Schritte halten - + Steps fade out Schritte blenden aus - + Tempo type Geschwindigkeitstyp - + Time Zeit - + Beats Takte - + Order and direction Ablauf und Richtung - + Loop Wiederholen - + Single Shot Einmal - + Ping Pong Hin und her - + Run Order Ablauf - + Forward Vorwärts - + Backward Rückwärts - + Direction Richtung - + Text Text - + Please choose a font Bitte eine Schriftart wählen - - - + + + Animation Animation - + Letters Buchstaben - - + + Horizontal Horizontal - - + + Vertical Vertikal - - + + Offset Versatz - - + + X X - - + + Y Y - + Image Bild - + Select an image Ein Bild auswählen - + Static Statisch @@ -3040,18 +3591,18 @@ Zugriffsstufe: RGBPanelProperties - + RGB panel properties Panel ist gebräuchlich, "Tafel" die näheste Übersetzung Eigenschaften des RGB-Panels - + Name Name - + Universe Universum @@ -3210,7 +3761,7 @@ Zugriffsstufe: In eine neue Szene speichern - + Function Preview Funktionsvorschau @@ -3223,43 +3774,43 @@ Zugriffsstufe: SceneEditor - + Add a fixture/group Neues Gerät/Gruppe hinzufügen - + Add a palette Neue Palette hinzufügen - + Remove the selected items Remove the selected fixtures Ausgewählte Elemente entfernen - + Delete items Elemente löschen - + Are you sure you want to remove the selected items? Ausgewählte Elemente wirklich löschen? - + Speed Geschwindigkeit - + Fade in Einblenden - + Fade out Ausblenden @@ -3267,67 +3818,67 @@ Zugriffsstufe: ScriptEditor - + Add a method call at cursor position Methodenaufruf an Cursorposition einfügen - + Show/hide functions tree Funktionsbaum zeigen/verstecken - + Show/hide fixture tree Gerätebaum zeigen/verstecken - + Check the script syntax Syntax des Skripts prüfen - + Syntax check Syntaxprüfung - + Start function Funktion starten - + Stop function Funktion stoppen - + Set fixture channel Gerätekanal setzen - + Wait time Wartezeit - + Random number Zufallszahl - + Blackout Blackout - + System command Systembefehl - + File path Dateipfad @@ -3355,12 +3906,12 @@ Zugriffsstufe: Ausgewählte Geräte entfernen - + Steps Schritte - + Fixtures Geräte @@ -3368,107 +3919,122 @@ Zugriffsstufe: SettingsView2D - + Environment Umgebung - + Width Breite - + Height Höhe - + Depth Tiefe - + Grid units Gittereinheiten - + Meters Meter - + Feet Fuß - + Point of view Blickpunkt - + Top view Von oben - + Front view Von unten - + Right side view Von rechts - + Left side view Von links + Custom Background + + + + + Select an image + Ein Bild auswählen + + + + Reset background + + + + Selected fixtures Ausgewählte Geräte - + Gel color Farbe der Folie - + Rotation Rotation - + Alignment Ausrichtung - + Align the selected items to the left Ausgewählte Elemente links ausrichten - + Align the selected items to the top Ausgewählte Elemente oben ausrichten - + Distribution Verteilung - + Equally distribute horizontally the selected items Ausgewählte Elemente gleichmäßig horizontal anordnen - + Equally distribute vertically the selected items Ausgewählte Elemente gleichmäßig vertikal anordnen @@ -3476,122 +4042,126 @@ Zugriffsstufe: SettingsView3D - + Environment Umgebung - + Type Typ - + Width Breite - + Height Höhe - + Depth Tiefe - + Rendering Rendering - + Quality Qualität - + Low Niedrig - + Medium Mittel - + High Hoch - + Ultra Ultra - + Ambient light Umgebungslicht - + Smoke amount Rauchdichte - + Show FPS Frames pro Sekunde anzeigen - + Scale Skalierung - + Custom items Eigene Elemente - + Select a mesh file Eine Mesh-Datei auswählen - + 3D files 3D-Dateien - + All files Alle Dateien - + + Normalize the selected items + + + Actions - Aktionen + Aktionen - + Add a new item to the scene Ein neues Element zur Szene hinzufügen - + Remove the selected items Ausgewählte Elemente entfernen - + Position Position - + Rotation Rotation @@ -3617,16 +4187,16 @@ Zugriffsstufe: ShowItem - - - + + + Position: Position: - - - + + + Duration: Dauer: @@ -3644,74 +4214,74 @@ Zugriffsstufe: Farben der Elemente anzeigen - + Unlock the selected items Ausgewählte Elemente entsperren - + Lock the selected items Ausgewählte Elemente sperren - + Snap to grid Am Gitter einrasten - + Stretch the original function Ursprüngliche Funktion strecken - + Remove the selected items Ausgewählte Elemente entfernen - + Are you sure you want to remove the following items? (Note that the original functions will not be deleted) Willst du alle folgenden Elemente löschen? (Die ursprünglichen Funktionen werden nicht gelöscht) - + Delete show items Showelemente löschen - + Copy the selected items in the clipboard Ausgewählte Elemente in die Zwischenablage kopieren - + Paste items in the clipboard at cursor position Elemente aus der Zwischenablage an Cursorposition einfügen - + Play or resume Abspielen oder fortsetzen - + Stop or rewind Anhalten oder zurückspulen - + Move the selected track up Ausgewählte Spur nach oben verschieben - + Move the selected track down Ausgewählte Spur nach unten verschieben - + Create a new track Neue Spur anlegen @@ -3721,19 +4291,19 @@ Zugriffsstufe: Shows - + New Show Neue Show - - + + Track %1 Spur %1 - - + + (Copy) (Kopie) @@ -3741,37 +4311,37 @@ Zugriffsstufe: SimpleDesk - + Universe Universum - + Reset the whole universe Gesamtes Universum zurücksetzen - + Dump on a new Scene In eine neue Szene speichern - + Reset the channel Kanal zurücksetzen - + Fixture List Geräteliste - + Commands history Kommandohistorie - + Simple Desk Einfaches Mischpult @@ -3787,25 +4357,202 @@ Zugriffsstufe: TrackDelegate - + Solo this track Diese Spur solo schalten - + Mute this track Diese Spur stumm schalten + + UISettingsEditor + + + + Reset to default + + + + + Scaling factor + + + + + Background darker + + + + + Background dark + + + + + Background medium + + + + + Background light + + + + + Background lighter + + + + + Controls background + + + + + Foreground main + + + + + Foreground medium + + + + + Foreground light + + + + + Toolbar gradient start + + + + + Sub-toolbar gradient start + + + + + Toolbar gradient end + + + + + Toolbar hover gradient start + + + + + Toolbar hover gradient end + + + + + Toolbar selection + + + + + Sub-toolbar selection + + + + + Section header + + + + + Section header divider + + + + + Item highlight + + + + + Item highlight pressed + + + + + Item hover + + + + + Item selection + + + + + VC Frame drop area + + + + + Item dark border + + + + + Save to file + + + + + Operation completed + + + + + Error + Fehler + + + + UniverseGridView + + + Error + Fehler + + + + Unable to perform the operation. +There is either not enough space or the target universe in invalid + + + + + Cut the selected items into clipboard + + + + + Paste items in the clipboard at the first available position + + + UniverseIOItem - Passthrough - Durchreichen + Durchreichen - + + Enable/Disable passthrough + + + + Enable/Disable feedbacks Rückmeldungen aktivieren/deaktivieren @@ -4018,74 +4765,74 @@ Zugriffsstufe: Zeitsteuerung für Funktion hinzufügen - + Remove this schedule Diese Zeitsteuerung entfernen - + Start time Startzeit - + Stop time Stopzeit - + Enable the stop time Stopzeit aktivieren - + M As in Monday Mo - + T As in Tuesday Di - + W As in Wednesday Mi - + T As in Thursday Do - + F As in Friday Fr - + S As in Saturday Sa - + S As in Sunday So - + Repeat weekly Wöchentlich wiederholen - + Add a new schedule Neue Zeitsteuerung hinzufügen @@ -4093,17 +4840,17 @@ Zugriffsstufe: VCCueList - + Next Cue Nächster Cue - + Previous Cue Vorheriger Cue - + Play/Stop/Pause Abspielen/Stop/Pause @@ -4116,17 +4863,17 @@ Zugriffsstufe: Rechte Überblendung - + Stop/Pause Stop/Pause - + Side Fader Seitenfader - + Cue List %1 Cue-Liste %1 @@ -4134,27 +4881,32 @@ Zugriffsstufe: VCCueListItem - + Play/Pause Abspielen/Pause - + + Play/Stop + + + + Pause Pause - + Stop Stop - + Previous cue Vorheriger Cue - + Next cue Nächster Cue @@ -4247,30 +4999,35 @@ Zugriffsstufe: VCFrame - + Next Page Nächste Seite - + Previous Page Vorherige Seite - + Enable Aktivieren - + Collapse Einklappen - + Frame %1 Rahmen %1 + + + Page %1 + Seite %1 + VCFrameItem @@ -4285,17 +5042,16 @@ Zugriffsstufe: Diesen Rahmen aktivieren/deaktivieren - + Previous page Vorherige Seite - Page - Seite + Seite - + Next page Nächste Seite @@ -4338,10 +5094,21 @@ Zugriffsstufe: Seitennummer - + Clone first page widgets Widgets der ersten Seite klonen + + + + Shortcuts + + + + + Shortcut name + + VCLabel @@ -4354,12 +5121,12 @@ Zugriffsstufe: VCPage - + Page %1 Seite %1 - + Virtual Console Page %1 Seite %1 der Virtuellen Konsole @@ -4367,57 +5134,57 @@ Zugriffsstufe: VCPageProperties - + Width Breite - + Height Höhe - + Security Sicherheit - + Set a PIN Eine PIN setzen - + Error Fehler - + The entered PINs are either invalid or incorrect Die eingegebenen PINs sind entweder ungültig oder inkorrekt - + Add page to the left Seite links hinzufügen - + Add page to the right Seite rechts hinzufügen - + Delete this page Diese Seite löschen - + Delete page Seite löschen - + Are you sure you want to delete the selected page? Willst du die ausgewählte Seite entfernen? @@ -4482,12 +5249,12 @@ Zugriffsstufe: Steuerung zurücksetzen - + Slider %1 Schieberegler %1 - + Knob %1 Drehknopf %1 @@ -4560,82 +5327,82 @@ Zugriffsstufe: Eigenschaft - + Level mode Stufenmodus - + Channels Kanäle - + Add/Remove channels Kanäle hinzufügen/entfernen - + Click & Go button Klick & Los-Knopf - + None Keine - + RGB/CMY RGB/CMY - + Gobo/Effect/Macro Gobo/Effekt/Makro - + Monitor channel levels Kanalstufen beobachten - + Values range Wertebereich - + Upper limit Obere Grenze - + Lower limit Untere Grenze - + Grand Master mode Modus des Grand Masters - + Reduce values Werte reduzieren - + Limit values Werte begrenzen - + Intensity channels Intensitätskanäle - + All channels Alle Kanäle @@ -4712,8 +5479,8 @@ Zugriffsstufe: Unbekannt - - + + None Keine @@ -4721,87 +5488,87 @@ Zugriffsstufe: VCWidgetProperties - + Select a widget first Wähle zuerst ein Widget aus - + Settings Einstellungen - + External controls Externe Steuerungen - + Basic properties Grundlegende Eigenschaften - + Label Beschriftung - + Background color Hintergrundfarbe - + Foreground color Vordergrundfarbe - + Font Schriftart - + Please choose a font Bitte eine Schriftart wählen - + Background image Hintergrundsbild - + Select an image Ein Bild auswählen - + Alignment Ausrichtung - + Align the selected widgets to the left Ausgewählte Elemente links ausrichten - + Align the selected widgets to the right Ausgewählte Elemente rechts ausrichten - + Align the selected widgets to the top Ausgewählte Elemente oben ausrichten - + Align the selected widgets to the bottom Ausgewählte Elemente unten ausrichten - + External Controls Externe Steuerungen @@ -4879,77 +5646,77 @@ Zugriffsstufe: Ausgabebildschirm - + Output mode Ausgabemodus - + Windowed Fenster - + Fullscreen Vollbild - + Geometry Geometrie - + Original Original - + Custom Angepasst - + Position Position - + Size Größe - + W B - + H H - + Rotation Rotation - + X X - + Y Y - + Z Z - + Layer Ebene @@ -4957,72 +5724,72 @@ Zugriffsstufe: VirtualConsole - + Error Fehler - + Invalid PIN entered Ungültige PIN eingegeben - + Enable/Disable widgets snapping Einrasten der Elemente aktivieren/deaktivieren - + Widget matrix setup Elemente-Matrix einrichten - + Columns Spalten - + Rows Zeilen - + Width Breite - + Height Höhe - + Frame type Rahmentyp - + Normal Normal - + Solo Solo - + Virtual Console Virtuelle Konsole - + <None> <Keine> - + Page %1 Seite %1 diff --git a/qmlui/qlcplus_es_ES.ts b/qmlui/qlcplus_es_ES.ts index a785f972ed..bd9ac4eaa1 100644 --- a/qmlui/qlcplus_es_ES.ts +++ b/qmlui/qlcplus_es_ES.ts @@ -4,54 +4,58 @@ ActionsMenu - Open a project - Abrir un proyecto + Abrir un proyecto - - + + Project files Archivos de proyecto - - - + + + All files Todos los archivos - + + Open a file + Abrir un archivo + + + QLC+ files Archivos QLC+ - - + + Import from project Importar desde proyecto - - + + Save project as... Guardar proyecto como... - + Your project has changes Su proyecto tiene cambios - + Do you wish to save the current project first? Changes will be lost if you don't save them. ¿Desea guardar primero el proyecto? Los cambios que no guarde se perderán. - + New project Nuevo proyecto @@ -60,117 +64,122 @@ Los cambios que no guarde se perderán. Abrir proyecto - + Open file Abrir un archivo - + Save project Guardar proyecto - + Undo Deshacer - + Redo Rehacer - + Network Red - + Server setup Configuración de server - + Client setup Configuración de cliente - + Address tool Herramienta de direccionamiento - + DMX Address tool Herramienta de direccionamiento DMX - + + UI Settings + Configuración de la interfaz de usuario + + + Toggle fullscreen Pasar a pantalla completa - + Language Idioma - + Catalan Catalán - + Dutch Holandés - + English Inglés - + French Francés - + German Alemán - + Italian Italiano - + Japanese Japonés - + Polish Polaco - + Russian Ruso - + Spanish Español - + Ukrainian Ucraniano - + About Acerca @@ -291,12 +300,17 @@ Los cambios que no guarde se perderán. Dispositivo de salida - + + Volume + Volumen + + + Fade in Fade in - + Fade out Fade out @@ -312,20 +326,25 @@ Los cambios que no guarde se perderán. BeamTool - + Beam Haz - + Beam degrees Grados del haz - + Distance Distancia + + + Projected diameter + Diametro proyectado + BottomPanel @@ -338,8 +357,8 @@ Los cambios que no guarde se perderán. ChannelEdit - - + + Custom Persponalizado @@ -347,88 +366,118 @@ Los cambios que no guarde se perderán. ChannelEditor - + + Open a picture file + Abrir un archivo de imagen + + + + Gobo pictures + Imagenes de gobo + + + + All files + Todos los archivos + + + Name Nombre - + Preset Predefinido - - + + Type Tipo - + Role Rol - + Coarse (MSB) Coarse (MSB) - + Fine (LSB) Fine (MSB) - + Default value Valor por defecto - + Delete the selected capabilities Eliminar las capacidades seleccionadas - + + Capability wizard + Asistente de capacidades + + + + Empty description provided + Se ha proporcionado una descripción vacía + + + + Overlapping with another capability + Superposición con otra capacidad + + + From Desde - + To Hasta - + Description Descripción - + Preview Previsualización - + Primary color Color primario - + Secondary color Color secundario - + Value(s) Valor(es) - + Value 1 Valor 1 - + Value 2 Valor 2 @@ -436,122 +485,137 @@ Los cambios que no guarde se perderán. ChaserEditor - + Add a new step Añadir un nuevo paso - + Remove the selected steps Eliminar los pasos seleccionados - + Delete steps Eliminar pasos - + Are you sure you want to remove the selected steps? ¿Está seguro que quiere eliminar los pasos seleccionados? - + + Preview the previous step + Previsualiza el paso anterior + + + + Preview the next step + Previsualiza el paso siguiente + + + + Duplicate the selected step(s) + Duplicar los pasos seleccionados + + + Print the Chaser steps Imprime los pasos del Chaser - + Run properties Propiedades de ejecución - + Loop Loop - + Single Shot Una sola vez - + Ping Pong Ping Pong - + Random Aleatorio - + Run Order Orden de Ejecución - + Forward Adelante - + Backward Atrás - + Direction Dirección - + Time Tiempo - + Beats Beats - + Tempo Tempo - - + + Default Por Defecto - - - + + + Common Común - - - + + + Per Step Por Paso - + Fade In Fade In - + Fade Out Fade Out - + Duration Duración @@ -559,32 +623,32 @@ Los cambios que no guarde se perderán. ChaserWidget - + Function Función - + Fade In Fade In - + Hold Espera - + Fade Out Fade Out - + Duration Duración - + Note Nota @@ -592,22 +656,22 @@ Los cambios que no guarde se perderán. CollectionEditor - + Add a function Añadir una función - + Remove the selected function Eliminar la función seleccionada - + Delete functions Eliminar funciones - + Are you sure you want to remove the selected functions? ¿Está seguro que quiere eliminar las funciones seleccionadas? @@ -615,17 +679,17 @@ Los cambios que no guarde se perderán. ColorTool - + Basic Básico - + Full Entero - + Filters Filtros @@ -633,7 +697,7 @@ Los cambios que no guarde se perderán. ColorToolBasic - + Selected color Color seleccionado @@ -641,87 +705,87 @@ Los cambios que no guarde se perderán. ColorToolFilters - + Open filters menu Abrir menú de filtros - + Cyan Cyan - + Red Rojo - + White Blanco - + Magenta Magenta - + Green Verde - + Amber Ambar - + Yellow Amarillo - + Blue Azul - + UV UV - + CMY CMY - + Add a new color filters file Añadir un nuevo archivo de filtros de color - + Rename the current color filters file Renombrar el archivo actual de filtros de color - + Save the current color filters file Guardar el archivo de filtros de color - + Add a new filter Añadir un nuevo filtro - + Delete the selected filter Eliminar el filtro seleccionado - + Paste the latest picked color as new filter Pegar el último color seleccionado como un nuevo filtro @@ -729,37 +793,37 @@ Los cambios que no guarde se perderán. ColorToolFull - + Red Rojo - + Green Verde - + Blue Azul - + White Blanco - + Amber Ámbar - + UV UV - + Selected color Color seleccionado @@ -767,12 +831,12 @@ Los cambios que no guarde se perderán. ContextManager - + Universe Grid View Vista de grilla del universo - + linked enlazado @@ -803,154 +867,174 @@ Los cambios que no guarde se perderán. EFXEditor - + Fixtures Fixtures - + Add a fixture/head Añadir un fixture/cabeza - + Remove the selected fixture head(s) Eliminar las cabezas de fixture seleccionadas - + Fixture Fixture - + + Mode + Modo + + + Reverse Invertir - - + + Start offset Desfase de Inicio - + + Position + Posición + + + + Dimmer + Dimmer + + + + RGB + RGB + + + Add a new fixture Añadir un nuevo fixture - - + + Pattern Patrón - + Relative movement Movimiento relativo - + Width Ancho - + Height Alto - + X offset Desfase en X - + Y offset Desfase en Y - + Rotation Rotación - + X frequency Frecuencia de X - + Y frequency Frecuencia de Y - + X phase Fase de X - + Y phase Fase de Y - + Speed Velocidad - + Fade in Fade in - Hold - Espera + Espera - + Fade out Fade out - + Order and direction Orden y dirección - + + Loop Loop - + Single Shot Una sola vez - + Ping Pong Ping Pong - + Run Order Orden de Ejecución - + Forward Adelante - + Backward Atrás - + Direction Dirección @@ -958,7 +1042,7 @@ Los cambios que no guarde se perderán. EditorTopBar - + Go back to the previous view Go back to the Function Manager Volver al Administrador de Funciones @@ -977,77 +1061,82 @@ Los cambios que no guarde se perderán. !! Advertencia !! - + General General - + Manufacturer Fabricante - + Type Tipo - + Model Modelo - + Author Autor - + Physical properties Propiedades físicas - + Channels Canales - + Add a new channel Añadir un canal nuevo - + Remove the selected channel(s) Eliminar el(los) canal(es) seleccionado(s) - + + Channel wizard + Asistente de canales + + + Modes Modos - + Add a new mode Añadir un nuevo modo - + Remove the selected mode(s) Eliminar el(los) modo(s) seleccionado(s) - + Aliases Alias - + New channel %1 Nuevo canal %1 - + New mode Nuevo modo @@ -1060,37 +1149,37 @@ Los cambios que no guarde se perderán. Control - + Universe Universo - + Activate auto detection Activar auto detección - + Channel Canal - + Remove this input source Eliminar esta fuente de entrada - + Custom feedbacks Feedbacks personalizados - + Lower Inferior - + Upper Superior @@ -1116,13 +1205,13 @@ Los cambios que no guarde se perderán. FixtureBrowser - + Create a new fixture definition Add a new fixture definition Crear una nueva definición de fixture - + Edit the selected fixture definition Editar la definición de fixture seleccionada @@ -1130,22 +1219,22 @@ Los cambios que no guarde se perderán. FixtureChannelDelegate - + Auto (HTP) Auto (HTP) - + Auto (LTP) Auto (LTP) - + Forced HTP HTP forzado - + Forced LTP LTP forzado @@ -1176,7 +1265,7 @@ Los cambios que no guarde se perderán. - + Save definition as... Guardar la definición como... @@ -1186,27 +1275,32 @@ Los cambios que no guarde se perderán. Error - + + Warning + Atención + + + Back to QLC+ Volver a QLC+ - + New definition Nueva definición - + Open definition Abrir una definición - + Save definition Guardar la definición - + Unknown Desconocido @@ -1229,32 +1323,32 @@ Los cambios que no guarde se perderán. Eliminar elementos seleccionados - + Reset the entire group Reestablecer el grupo entero - + Rotate 90° clockwise Rotar 90° en sentido horario - + Rotate 180° clockwise Rotar 180° en sentido horario - + Rotate 270° clockwise Rotar 270° en sentido horario - + Flip horizontally Invertir horizontalmente - + Flip vertically Invertir verticalmente @@ -1262,67 +1356,77 @@ Los cambios que no guarde se perderán. FixtureGroupManager - + + Error + Error + + + Add a new fixture group Añadir un nuevo grupo de fixtures - + Remove the selected items Eliminar los elementos seleccionados - + Set a Group/Fixture/Channel search filter Establecer un filtro de búsqueda de Grupo/Fixture/Canal - + Rename the selected items Renombrar los elementos seleccionados - + Rename items Renombrar los elementos - + Inspect the selected item Inspeccionar el elemento seleccionado - + Toggle fixtures and channels properties Activar/Desactivar propiedades de fixtures y canales - + Add/Remove a linked fixture Añadir/Eliminar un fixture enlazado - + Name Nombre - + + Mode + Modo + + + Flags Indicadores - + Can fade Puede hacer fade - + Behaviour Comportamiento - + Modifier @@ -1330,67 +1434,85 @@ Los cambios que no guarde se perderán. FixtureManager - - - + + + Head Cabeza - + New group %1 Nuevo grupo %1 - + %1 - Row %2 %1 - Fila %2 - + New filters %1 Nuevos filtros %1 + + FixtureNodeDelegate + + + Show/Hide this fixture + Mostrar/Ocultar este fixture + + + + Invert Pan + Invertir panorámica + + + + Invert Tilt + Invertir inclinación + + FixtureProperties - + Fixture properties Propiedades del fixture - + Name Nombre - + Universe Universo - + Address Dirección - + Quantity Cantidad - + Channels Canales - + Gap Intérvalo - + Mode Modo @@ -1622,67 +1744,67 @@ Los cambios que no guarde se perderán. Establecer un filtro de búsqueda de Funciones - + <None> <Ninguno> - + New Scene Nueva Escena - + New Chaser Nuevo Chaser - + New Sequence Nueva Secuencia - + New EFX Nuevo EFX - + New Collection Nueva Colección - + New RGB Matrix Nueva Matriz RGB - + New Script Nuevo Script - + New Show Nuevo Show - + New Audio Nuevo Audio - + New Video Nuevo Video - + (Copy) (Copiar) - + New folder Nueva Carpeta @@ -1746,31 +1868,31 @@ Los cambios que no guarde se perderán. InputOutputManager - + Input/Output Manager Administrador de Entradas/Salidas - + All universes Todos los universos - - - - - + + + + + Default device Dispositivo por defecto - + Disabled Deshabilitado - + Internal generator Generador interno @@ -1783,10 +1905,124 @@ Los cambios que no guarde se perderán. Elimina este perfil de entrada + + InputProfileEditor + + + + + !! Warning !! + !! Advertencia !! + + + + Channel wizard activated + Asistente de canales activado + + + + You have enabled the input channel wizard. After clicking OK, wiggle your mapped input profile's controls. They should appear into the list. Click the wizard button again to stop channel auto-detection.<br><br>Note that the wizard cannot tell the difference between a knob and a slider so you will have to do the change manually. + Ha habilitado el asistente de canales de entrada. Después de hacer clic en Aceptar, mueva los controles de su perfil de entrada asignado. Deberían aparecer en la lista. Haga clic en el botón del asistente nuevamente para detener la detección automática de canales.<br><br>Tenga en cuenta que el asistente no puede distinguir entre una perilla y un control deslizante, por lo que tendrá que realizar el cambio manualmente. + + + + Unsaved changes + Cambios no guardados + + + + Do you wish to save the current profile first? +Changes will be lost if you don't save them. + ¿Desea guardar primero el perfil actual? +Los cambios se perderán si no los guarda. + + + + Manufacturer + Fabricante + + + + Model + Modelo + + + + + Type + Tipo + + + + MIDI Global Settings + Configuración Global MIDI + + + + When MIDI notes are used, send a Note Off when value is 0 + Cuando se usen notas MIDI, envie una Note Off cuando el valor sea 0 + + + + Channel + Canal + + + + Name + Nombre + + + + + Behaviour + Comportamiento + + + + Generate an extra Press/Release when toggled + Genera una orden adicional de Pulsar/Liberar cuando se activa + + + + Movement + Movimiento + + + + Sensitivity + Sensibilidad + + + + Custom Feedback + Feedback personalizado + + + + Lower value + Valor inferior + + + + Upper value + Valor superior + + + + Button %1 + Botón %1 + + + + Slider %1 + Slider %1 + + IntensityTool - + Intensity Intensidad @@ -1807,17 +2043,17 @@ Los cambios que no guarde se perderán. Control - + Combination Combinación - + Activate auto detection Activar detección automática - + Remove this keyboard combination Eliminar esta combinación de teclado @@ -1830,62 +2066,62 @@ Los cambios que no guarde se perderán. Añadir fixture - + Fixture Groups Grupo de Fixtures - + Palettes Paletas - + Intensity Intensidad - + Shutter Obturador - + Position Posición - + Color Color - + Color Wheel Rueda de color - + Gobos Gobos - + Beam Haz - + Pick a 3D point Elegir un punto 3D - + Toggle multiple item selection Activar/Desactivar selección de elementos múltiples - + Select/Deselect all fixtures Seleccionar/Deseleccionar todos los fixtures @@ -1893,40 +2129,45 @@ Los cambios que no guarde se perderán. MainView - + Actions Acciones - + Fixtures & Functions Fixtures y Funciones - + Virtual Console Consola Virtual - + Simple Desk Mesa Simple - + Show Manager Administrador de Shows - + Input/Output Entrada/Salida - + Off Apagado + + + Stop all the running functions + Parar todas la funciones + MainView2D @@ -1939,27 +2180,27 @@ Los cambios que no guarde se perderán. MainView3D - + 3D View Vista 3D - + Simple ground Suelo simple - + Simple box Cubo simple - + Rock stage Escenario de Rock - + Theatre stage Escenario de Teatro @@ -1975,38 +2216,62 @@ Los cambios que no guarde se perderán. ModeEditor - + Name Nombre - - + + Channels Canales - + + Create a new emitter + Crear un nuevo emisor + + + + Remove the selected channel(s) + Eliminar el(los) canal(es) seleccionado(s) + + + + Drop channels here + Soltar canales aquí + + + Acts on Actúa sobre - + + Emitters + Emisores + + + + Remove the selected emitter(s) + Borrar los emisores seleccionados + + Heads - Cabeza + Cabeza - + Physical Tamaño Físico - + Use global settings Usa los ajustes globales - + Override global settings Sobreescribir los ajustes globales @@ -2027,97 +2292,112 @@ Los cambios que no guarde se perderán. PaletteFanningBox - - + Flat Plano - - + Linear Lineal - - + Square Cuadrado - - + Saw Sierra - - + Sine Senoidal - - Left to right - Izquierda a Derecha + Izquierda a Derecha - - Right to left - Derecha a Izquierda + Derecha a Izquierda - - Top to bottom - Arriba a Abajo + Arriba a Abajo - - Bottom to top - Abajo a Arriba + Abajo a Arriba - - Centered - Centrado + Centrado - + Show/Hide fanning options Muestra/Esconde opciones de abanico - + Create a new palette Crea una nueva paleta - + Type Tipo - + Layout Disposición - + + X Ascending + X Ascendente + + + + X Descending + X Descendente + + + + Y Ascending + Y Ascendente + + + + Y Descending + Y Descendente + + + + Z Ascending + Z Ascendente + + + + Z Descending + Z Descendente + + + Amount Cantidad - + Value Valor - + Pick the selected color Escoge el color seleccionado @@ -2150,12 +2430,12 @@ Los cambios que no guarde se perderán. Suprimir la(s) paletas selecionada(s) - + Are you sure you want to delete the following items? Está seguro que quiere eliminar los siguientes elementos? - + Delete items Borrar ítems @@ -2169,8 +2449,8 @@ Los cambios que no guarde se perderán. - - + + Type Tipo @@ -2180,84 +2460,84 @@ Los cambios que no guarde se perderán. Lúmenes - + Colour Temp (K) Temperatura de color (K) - + Lens Lente - + Min Degrees Grados mínimos - + Max Degrees Grados máximos - + Head(s) Cabeza(s) - + Pan Max Degrees Grados máximos panorama - + Tilt Max Degrees Grados máximos inclinación - + Layout (Columns x Rows) Disposición (Columnas x Filas) - + Dimensions Dimensiones - + Weight Peso - + Width Ancho - + Height Alto - + Depth Profundidad - + Electrical Eléctrico - + Power Consumption Consumo de energía - + DMX Connector Conector DMX @@ -2290,65 +2570,241 @@ Los cambios que no guarde se perderán. licencia Apache 2.0 + + PopupChannelModifiers + + + Channel Modifiers Editor + Editor de Modificadores de Canal + + + + Insert a modified value after the selected + Inserir un valor modificado después de la selección + + + + Delete the selected modifier value + Suprimir el valor del modificador seleccionado + + + + Rename the selected modifier template + Cambia el nombre de la plantilla de modificador seleccionada + + + + Save the selected modifier template + Guarda la plantilla de modificador seleccionada + + + + Templates + Plantillas + + + + Original DMX value + Valor DMX Original + + + + Modified DMX value + + + + + PopupChannelWizard + + + Fixture Editor Wizard + Asistente del Editor de Fixtures + + + + Properties + Proppiedades + + + + Start + Inicio + + + + Width + Ancho + + + + Amount + Cantidad + + + + Type + Tipo + + + + Red + Rojo + + + + Green + Verde + + + + Blue + Azul + + + + White + Blanco + + + + Amber + Ambar + + + + UV + UV + + + + RGB + RGB + + + + RGBW + RGBW + + + + RGBAW + RGBAW + + + + Dimmer + Dimmer + + + + Pan + Pan + + + + Tilt + Tilt + + + + Color Macro + Color de Macro + + + + Shutter + Obturador + + + + Beam + Haz + + + + Effect + Efecto + + + + Label + Etiqueta + + + + Capability # + Capacidad + + + + Channel # + Canal # + + + + Preview + Previsualización + + PopupCreatePalette - + Create a new palette Crea una nueva paleta - + Dimmer Dimmer - + Color Color - + Position Posición - + Shutter Obturador - + Gobo Gobo - + Palette name Nombre de la Paleta - + New Palette Nueva Paleta - + Type Tipo - + Also create a Scene Crea también una escena - + Scene name Nombre de la escena - + New Scene Nueva Escena @@ -2356,87 +2812,87 @@ Los cambios que no guarde se perderán. PopupDMXDump - + Enter a name for the scene Introducir un nombre para la escena - + Scene name Nombre de la escena - + New Scene Nueva Escena - + Don't ask again No volver a preguntar - + Available channel types Tipos de canales disponibles - + Intensity Intensidad - + RGB/CMY/WAUV RGB/CMY/WAUV - + Color macros Macros de color - + Gobo Gobo - + Pan Pan - + Tilt Tilt - + Speed Velocidad - + Shutter/Strobe Obturador/Strobo - + Prism Prisma - + Beam Haz - + Effect Efecto - + Maintenance Mantenimiento @@ -2444,7 +2900,7 @@ Los cambios que no guarde se perderán. PopupDisclaimer - + Disclaimer Aviso @@ -2467,6 +2923,54 @@ Los cambios que no guarde se perderán. Funciones + + PopupInputChannelEditor + + + Input Channel Editor + Editor de Canales de Entrada + + + + Input Channel + Canal de Entrada + + + + Number + Número + + + + Name + Nombre + + + + Type + Tipo + + + + Channel + Canal + + + + Message + Mensaje + + + + Parameter + Parámetro + + + + Note + Nota + + PopupManualInputSource @@ -2531,27 +3035,27 @@ Los cambios que no guarde se perderán. PopupNetworkClient - + Disconnected Desconectado - + Waiting for access Esperando para acceder - + Downloading project Descargando proyecto - + Connected Conectado - + QLC+ client setup Configuración de cliente QLC+ @@ -2594,7 +3098,7 @@ Los cambios que no guarde se perderán. PopupNetworkConnect - + Client access request Petición de acceso de cliente @@ -2727,12 +3231,12 @@ Nivel de acceso: PopupPINRequest - + Page PIN PIN de la página - + Remember for this session Recordar para esta sesión @@ -2740,22 +3244,22 @@ Nivel de acceso: PopupPINSetup - + Current PIN PIN actual - + New PIN Nuevo PIN - + Confirm PIN Confirmar PIN - + New PIN mismatch El nuevo PIN no corresponede @@ -2763,22 +3267,22 @@ Nivel de acceso: PopupRenameItems - + New name Nuevo nombre - + Enable numbering Habilitar numeración - + Start number Número de inicio - + Digits Dígitos @@ -2786,28 +3290,76 @@ Nivel de acceso: PositionTool - + Position Posición - + Rotate 90° clockwise Rotar 90° en sentido horario - - + + Snap to the previous value Ajustat al valor previo - - + + Snap to the next value Ajustar al valor siguiente + + ProfilesList + + + !! Warning !! + !! Advertencia !! + + + + Save this profile + Guarda este perfil + + + + Toggle the automatic detection procedure + Conmuta el procedimiento de detección automática + + + + Add a new channel + Añadir un canal nuevo + + + + Create a new input profile + Crea un perfil de entrada nuevo + + + + Edit the selected channel + Edita el canal seleccionado + + + + Edit the selected input profile + Edita el perfil de entrada seleccionado + + + + Delete the selected channel + Borra el canal seleccionado + + + + Delete the selected input profile(s) + Suprime lo/s perfil/es de entrada seleccionado/s + + RGBMatrixEditor @@ -2821,214 +3373,214 @@ Nivel de acceso: Patrón - + Blend mode Modo de Mezcla - + Default (HTP) Por defecto (HTP) - + Mask Máscara - + Additive Aditivo - + Subtractive Substractivo - + Color mode Modo de color - + Default (RGB) Por defecto (RGB) - + White Blanco - + Amber Ámbar - + UV UV - + Dimmer Dimmer - + Shutter Obturador - + Colors Colores - + Parameters Parámetros - + Speed Velocidad - + Steps fade in Fade In del paso - + Steps hold Espera del paso - + Steps fade out Fade Out del paso - + Tempo type Tipo de tempo - + Time Tiempo - + Beats Beats - + Order and direction Orden y dirección - + Loop Loop - + Single Shot Una sola vez - + Ping Pong Ping Pong - + Run Order Orden de Ejecución - + Forward Adelante - + Backward Atrás - + Direction Dirección - + Text Texto - + Please choose a font Por favor elegir una fuente - - - + + + Animation Animación - + Letters Letras - - + + Horizontal Horizontal - - + + Vertical Vertical - - + + Offset Desfase - - + + X X - - + + Y Y - + Image Imagen - + Select an image Seleccionar una imagen - + Static Estático @@ -3036,17 +3588,17 @@ Nivel de acceso: RGBPanelProperties - + RGB panel properties Propiedades del panel RGB - + Name Nombre - + Universe Universo @@ -3205,7 +3757,7 @@ Nivel de acceso: Volcar en una nueva escena - + Function Preview Previsualizar función @@ -3218,43 +3770,43 @@ Nivel de acceso: SceneEditor - + Add a fixture/group Añadir un fixture/grupo - + Add a palette Añadir una paleta - + Remove the selected items Remove the selected fixtures Eliminar los elementos seleccionados - + Delete items Borrar ítems - + Are you sure you want to remove the selected items? Esta seguro que quieres eliminar loes elementos seleccionados? - + Speed Velocidad - + Fade in Fade in - + Fade out Fade out @@ -3262,67 +3814,67 @@ Nivel de acceso: ScriptEditor - + Add a method call at cursor position Añadir una llamada de método en la posición de cursor - + Show/hide functions tree Mostrar/ocultar árbol de funciones - + Show/hide fixture tree Mostrar/ocultar árbol de fixtures - + Check the script syntax Verificar sintáxis del script - + Syntax check Verificación de sintaxis - + Start function Iniciar función - + Stop function Detener función - + Set fixture channel Establecer canal de fixture - + Wait time Tiempo de espera - + Random number Número aleatorio - + Blackout Blackout - + System command Comando de sistema - + File path Ruta de archivo @@ -3350,12 +3902,12 @@ Nivel de acceso: Eliminar los fixtures seleccionados - + Steps Pasos - + Fixtures Fixtures @@ -3363,107 +3915,122 @@ Nivel de acceso: SettingsView2D - + Environment Entorno - + Width Ancho - + Height Alto - + Depth Profundidad - + Grid units Unidades de la cuadrícula - + Meters Metros - + Feet Pies - + Point of view Punto de vista - + Top view Vista superior - + Front view Vista frontal - + Right side view Vista lateral derecha - + Left side view Vista lateral izquierda + Custom Background + Fondo personalizado + + + + Select an image + Seleccione una imagen + + + + Reset background + Restablecer el fondo + + + Selected fixtures Fixtures seleccionados - + Gel color Color del filtro - + Rotation Rotación - + Alignment Alineación - + Align the selected items to the left Alinear los elementos seleccionados a la izquierda - + Align the selected items to the top Alinear los elementos seleccionados hacia arriba - + Distribution Distribución - + Equally distribute horizontally the selected items Distribuir horizontalmente los elementos seleccionados - + Equally distribute vertically the selected items Distribuir verticalmente los elementos seleccionados @@ -3471,122 +4038,126 @@ Nivel de acceso: SettingsView3D - + Environment Entorno - + Type Tipo - + Width Ancho - + Height Alto - + Depth Profundidad - + Rendering Renderizado - + Quality Calidad - + Low Baja - + Medium Media - + High Alta - + Ultra Ultra - + Ambient light Luz ambiente - + Smoke amount Cantidad de humo - + Show FPS Mostrar FPS - + Scale Escala - + Custom items Elementos personalizados - + Select a mesh file Seleccionar un archivo mesh - + 3D files Archivos 3D - + All files Todos los archivos - + + Normalize the selected items + Normaliza los elementos seleccionados + + Actions - Acciones + Acciones - + Add a new item to the scene Añadir un nuevo elemento a la escena - + Remove the selected items Eliminar los elementos seleccionados - + Position Posición - + Rotation Rotación @@ -3612,16 +4183,16 @@ Nivel de acceso: ShowItem - - - + + + Position: Posición: - - - + + + Duration: Duración: @@ -3639,74 +4210,74 @@ Nivel de acceso: Mostrar color de elementos - + Unlock the selected items Desbloquear elementos seleccionados - + Lock the selected items Bloquear elementos seleccionados - + Snap to grid Ajustar a cuadrícula - + Stretch the original function Estirar la función original - + Remove the selected items Eliminar los elementos seleccionados - + Are you sure you want to remove the following items? (Note that the original functions will not be deleted) ¿Está seguro que quiere eliminar los elementos siguientes? (Las funciones originales NO se eliminarán) - + Delete show items Eliminar elementos del show - + Copy the selected items in the clipboard Copiar los elementos seleccionados al portapapeles - + Paste items in the clipboard at cursor position Pegar elementos del portapapeles en la posición del cursor - + Play or resume Reproducir o reiniciar - + Stop or rewind Deterer o rebobinar - + Move the selected track up Mover el track seleccionado hacia arriba - + Move the selected track down Mover el track seleccionado hacia abajo - + Create a new track Crear un nuevo track @@ -3716,19 +4287,19 @@ Nivel de acceso: Show manager - + New Show Nuevo Show - - + + Track %1 Pista %1 - - + + (Copy) (Copiar) @@ -3736,37 +4307,37 @@ Nivel de acceso: SimpleDesk - + Universe Universo - + Reset the whole universe Restablecer todo el universo - + Dump on a new Scene Volcar en una nueva escena - + Reset the channel Restablecer el canal - + Fixture List Lista de fixtures - + Commands history Historial de comandos - + Simple Desk Mesa Simple @@ -3782,25 +4353,202 @@ Nivel de acceso: TrackDelegate - + Solo this track Solo este track - + Mute this track Enmudecer este track + + UISettingsEditor + + + + Reset to default + Restablecer a los valores por defecto + + + + Scaling factor + Factor de escala + + + + Background darker + Fondo mas oscuro + + + + Background dark + Fondo oscuro + + + + Background medium + Fondo medio + + + + Background light + Fondo claro + + + + Background lighter + Fondo mas claro + + + + Controls background + Controles del fondo + + + + Foreground main + Principal del primer plano + + + + Foreground medium + Primer plano medio + + + + Foreground light + Primer plano claro + + + + Toolbar gradient start + Barra de herramientas del Inicio del degradado + + + + Sub-toolbar gradient start + Subbarra de herramientas del inicio del degradado + + + + Toolbar gradient end + Barra de herramientas del fin del degradado + + + + Toolbar hover gradient start + Barra de herramientas inicio del degradado pasar por encima + + + + Toolbar hover gradient end + Barra de herramientas fin del degradado al pasar por encima + + + + Toolbar selection + Barra de herramientas de selección + + + + Sub-toolbar selection + SubBarra de herramientas de selección + + + + Section header + Cabecera de la sección + + + + Section header divider + Divisor de cabecera de sección + + + + Item highlight + Resaltado de elementos + + + + Item highlight pressed + Elemento resaltado pulsado + + + + Item hover + Elemento en pasar por encima + + + + Item selection + Selección de elementos + + + + VC Frame drop area + Area de caida del marco VC + + + + Item dark border + Borde oscuro del elemento + + + + Save to file + Guarda a un archivo + + + + Operation completed + Operación completada + + + + Error + Error + + + + UniverseGridView + + + Error + Error + + + + Unable to perform the operation. +There is either not enough space or the target universe in invalid + No se ha podido realizar el operació.no hay bastante espacio o el universo objetivo en un valor no válido + + + + Cut the selected items into clipboard + Corta los elementos seleccionados al portapapeles + + + + Paste items in the clipboard at the first available position + Pega los elementos del portapapeles a la primera posición disponible + + UniverseIOItem - Passthrough - Passthrough + Passthrough + + + + Enable/Disable passthrough + Activa/Desactiva el paso a través - + Enable/Disable feedbacks Habilitar/Deshabilitar feedbacks @@ -4012,74 +4760,74 @@ Nivel de acceso: Añadir un cronograma de funciones - + Remove this schedule Eliminar este cronograma - + Start time Hora de inicio - + Stop time Hora de finalización - + Enable the stop time Habilitar la hora de finalización - + M As in Monday L - + T As in Tuesday M - + W As in Wednesday M - + T As in Thursday J - + F As in Friday V - + S As in Saturday S - + S As in Sunday D - + Repeat weekly Repetir semanalmente - + Add a new schedule Añadir un nuevo cronograma @@ -4087,17 +4835,17 @@ Nivel de acceso: VCCueList - + Next Cue Siguiente Cue - + Previous Cue Cue anterior - + Play/Stop/Pause Reproducir/Detener/Pausa @@ -4110,17 +4858,17 @@ Nivel de acceso: Crossfade derecho - + Stop/Pause Detener/Pausa - + Side Fader Fader Lateral - + Cue List %1 Lista de Cues %1 @@ -4128,27 +4876,32 @@ Nivel de acceso: VCCueListItem - + Play/Pause Reproducir/Pausa - + + Play/Stop + Reproduce/Para + + + Pause Pausa - + Stop Detener - + Previous cue Cue Anterior - + Next cue Siguiente Cue @@ -4241,30 +4994,35 @@ Nivel de acceso: VCFrame - + Next Page Págna siguiente - + Previous Page Página anterior - + Enable Habilitar - + Collapse Colapsar - + Frame %1 Marco %1 + + + Page %1 + Página %1 + VCFrameItem @@ -4279,17 +5037,16 @@ Nivel de acceso: Habilitar/Deshabilitar este marco - + Previous page Página Anterior - Page - Página + Página - + Next page Página Siguiente @@ -4332,10 +5089,21 @@ Nivel de acceso: Número de páginas - + Clone first page widgets Clonar los widgets de la primera página + + + + Shortcuts + Atajos + + + + Shortcut name + Nombre del atajo + VCLabel @@ -4348,12 +5116,12 @@ Nivel de acceso: VCPage - + Page %1 Página %1 - + Virtual Console Page %1 Pàgina %1 de la Consola Virtual @@ -4361,57 +5129,57 @@ Nivel de acceso: VCPageProperties - + Width Ancho - + Height Alto - + Security Seguridad - + Set a PIN Establecer un PIN - + Error Error - + The entered PINs are either invalid or incorrect Los PINs son inválidos o oincorrectos - + Add page to the left Añadir una página a la izquierda - + Add page to the right Añadir una página a la derecha - + Delete this page Eliminar esta página - + Delete page Eliminar página - + Are you sure you want to delete the selected page? ¿Está seguro que quiere eliminar la página seleccionada? @@ -4476,12 +5244,12 @@ Nivel de acceso: Reestablecer Control - + Slider %1 Slider %1 - + Knob %1 Perilla %1 @@ -4554,82 +5322,82 @@ Nivel de acceso: Atributo - + Level mode Modo Nivel - + Channels Canales - + Add/Remove channels Añadir/Eliminar canales - + Click & Go button Boton Click & Go - + None Ninguno - + RGB/CMY RGB/CMY - + Gobo/Effect/Macro Gobo/Efecto/Macro - + Monitor channel levels Monitorear nivel de canales - + Values range Rango de valores - + Upper limit Límite superior - + Lower limit Límite inferior - + Grand Master mode Modo Gran Master - + Reduce values Reducir valores - + Limit values Limitar valores - + Intensity channels Canales de intensidad - + All channels Todos los canales @@ -4706,8 +5474,8 @@ Nivel de acceso: Desconocido - - + + None Ninguno @@ -4715,87 +5483,87 @@ Nivel de acceso: VCWidgetProperties - + Select a widget first Seleccionar primero un widget - + Settings Ajustes - + External controls Controles externos - + Basic properties Propiedades básicas - + Label Etiqueta - + Background color Color de fondo - + Foreground color Color de primer plano - + Font Fuente - + Please choose a font Por favor elija una fuente - + Background image Imagen de fondo - + Select an image Seleccione una imagen - + Alignment Alineamiento - + Align the selected widgets to the left Alinear los widgets seleccionados a la izquierda - + Align the selected widgets to the right Alinear los widgets seleccionados a la derecha - + Align the selected widgets to the top Alinear los widgets seleccionados hacia arriba - + Align the selected widgets to the bottom Alinear los widgets seleccionados hacia abajo - + External Controls Controles externos @@ -4873,77 +5641,77 @@ Nivel de acceso: Pantalla de salida - + Output mode Modo de salida - + Windowed En Ventana - + Fullscreen En Pantalla completa - + Geometry Geometría - + Original Original - + Custom Persponalizado - + Position Posición - + Size Tamaño - + W W - + H H - + Rotation Rotación - + X X - + Y Y - + Z Z - + Layer Capa @@ -4951,72 +5719,72 @@ Nivel de acceso: VirtualConsole - + Error Error - + Invalid PIN entered PIN incorrecto - + Enable/Disable widgets snapping Habilitar/Deshabilitar anclaje de widgets - + Widget matrix setup Configuración de la matriz de widgets - + Columns Columnas - + Rows Filas - + Width Ancho - + Height Alto - + Frame type Tipo de marco - + Normal Normal - + Solo Solo - + Virtual Console Consola Virtual - + <None> <Ninguno> - + Page %1 Página %1 diff --git a/qmlui/qlcplus_fr_FR.ts b/qmlui/qlcplus_fr_FR.ts index 2712a22f35..86d213814b 100644 --- a/qmlui/qlcplus_fr_FR.ts +++ b/qmlui/qlcplus_fr_FR.ts @@ -4,55 +4,55 @@ ActionsMenu - + Open a file Open a project Ouvrir un fichier - - + + Project files Fichiers du projet - - - + + + All files Tous les fichiers - + QLC+ files Fichiers QLC+ - - + + Import from project Importer depuis le projet - - + + Save project as... Enregistrer le projet sous... - + Your project has changes Votre projet comporte des modifications - + Do you wish to save the current project first? Changes will be lost if you don't save them. Souhaitez vous enregistrer le projet actuel ? Les modifications seront perdues si vous ne les enregistrez pas. - + New project Nouveau projet @@ -61,117 +61,122 @@ Les modifications seront perdues si vous ne les enregistrez pas. Ouvrir un projet - + Open file Ouvrir - + Save project Enregistrer le projet - + Undo Annuler - + Redo Rétablir - + Network Réseau - + Server setup Configuration du serveur - + Client setup Configuration du client - + Address tool Outil d'adressage - + DMX Address tool Outil d'adressage DMX - + + UI Settings + + + + Toggle fullscreen Plein écran - + Language Langue - + Catalan Català - + Dutch Nederlands - + English English - + French Français - + German Deutsch - + Italian Italiano - + Japanese 日本語 - + Polish Polski - + Russian русский - + Spanish Español - + Ukrainian Українська - + About À propos @@ -292,12 +297,17 @@ Les modifications seront perdues si vous ne les enregistrez pas. Périphérique de sortie - + + Volume + + + + Fade in Fondu en entrée - + Fade out Fondu en sortie @@ -313,20 +323,25 @@ Les modifications seront perdues si vous ne les enregistrez pas. BeamTool - + Beam Rayon - + Beam degrees Angle du rayon - + Distance Distance + + + Projected diameter + + BottomPanel @@ -339,8 +354,8 @@ Les modifications seront perdues si vous ne les enregistrez pas. ChannelEdit - - + + Custom Personnalisée @@ -348,98 +363,118 @@ Les modifications seront perdues si vous ne les enregistrez pas. ChannelEditor - + + Open a picture file + + + + + Gobo pictures + + + + + All files + Tous les fichiers + + + Name Nom - + Preset - - + + Type Type - + Role Rôle - + Coarse (MSB) Large (MSB) - + Fine (LSB) Fin (LSB) - + Default value Valeur par défaut - + Delete the selected capabilities - + + Capability wizard + + + + Empty description provided Description vide - + Overlapping with another capability Superposition avec une autre ... - + From De - + To À - + Description Description - + Preview Prévisualisation - + Primary color Couleur principale - + Secondary color Couleur secondaire - + Value(s) Valeur(s) - + Value 1 Valeur 1 - + Value 2 Valeur 2 @@ -447,122 +482,137 @@ Les modifications seront perdues si vous ne les enregistrez pas. ChaserEditor - + Add a new step Ajouter une nouvelle étape - + Remove the selected steps Supprimer les étapes sélectionnées - + Delete steps Supprimer les étapes - + Are you sure you want to remove the selected steps? Êtes-vous sûr de vouloir supprimer les étapes sélectionnées ? - + + Preview the previous step + + + + + Preview the next step + + + + + Duplicate the selected step(s) + + + + Print the Chaser steps Afficher les étapes du chaser - + Run properties Propriétés d'exécution - + Loop Boucle - + Single Shot Unique - + Ping Pong Ping Pong - + Random Aléatoire - + Run Order Ordre d'exécution - + Forward Avant - + Backward Arrière - + Direction Direction - + Time Temps - + Beats Battements - + Tempo Tempo - - + + Default Par défaut - - - + + + Common Commun - - - + + + Per Step Par étape - + Fade In Fondu en entrée - + Fade Out Fondu en sortie - + Duration Durée @@ -570,32 +620,32 @@ Les modifications seront perdues si vous ne les enregistrez pas. ChaserWidget - + Function Fonction - + Fade In Fondu en entrée - + Hold Maintien - + Fade Out Fondu en sortie - + Duration Durée - + Note Note @@ -603,22 +653,22 @@ Les modifications seront perdues si vous ne les enregistrez pas. CollectionEditor - + Add a function Ajouter une fonction - + Remove the selected function Supprimer la fonction sélectionnée - + Delete functions Supprimer les fonctions - + Are you sure you want to remove the selected functions? Êtes-vous sûr de vouloir supprimer les fonctions sélectionnées ? @@ -626,17 +676,17 @@ Les modifications seront perdues si vous ne les enregistrez pas. ColorTool - + Basic Basique - + Full Complet - + Filters Filtres @@ -644,7 +694,7 @@ Les modifications seront perdues si vous ne les enregistrez pas. ColorToolBasic - + Selected color Couleur sélectionnée @@ -740,37 +790,37 @@ Les modifications seront perdues si vous ne les enregistrez pas. ColorToolFull - + Red Rouge - + Green Vert - + Blue Bleu - + White Blanc - + Amber Ambre - + UV UV - + Selected color Couleur sélectionnée @@ -778,12 +828,12 @@ Les modifications seront perdues si vous ne les enregistrez pas. ContextManager - + Universe Grid View Vue en grille de l'univers - + linked lié @@ -814,154 +864,174 @@ Les modifications seront perdues si vous ne les enregistrez pas. EFXEditor - + Fixtures Appareils - + Add a fixture/head Ajouter un appareil/tête - + Remove the selected fixture head(s) Supprimer la ou les têtes sélectionnées - + Fixture Appareil - + + Mode + Mode + + + Reverse Inverser - - + + Start offset Décalage de début - + + Position + Position + + + + Dimmer + + + + + RGB + + + + Add a new fixture Ajouter un nouvel appareil - - + + Pattern Motif - + Relative movement Mouvement relatif - + Width Largeur - + Height Hauteur - + X offset Décalage en X - + Y offset Décalage en Y - + Rotation Rotation - + X frequency Fréquence en X - + Y frequency Fréquence en Y - + X phase Phase en X - + Y phase Phase en Y - + Speed Vitesse - + Fade in Fondu en entrée - Hold - Maintien + Maintien - + Fade out Fondu en sortie - + Order and direction Ordre et direction - + + Loop Boucle - + Single Shot Unique - + Ping Pong Ping Pong - + Run Order Ordre d'exécution - + Forward Avant - + Backward Arrière - + Direction Direction @@ -969,7 +1039,7 @@ Les modifications seront perdues si vous ne les enregistrez pas. EditorTopBar - + Go back to the previous view Go back to the Function Manager Retourner à la vue précédente @@ -988,77 +1058,82 @@ Les modifications seront perdues si vous ne les enregistrez pas. !! Attention !! - + General Général - + Manufacturer Fabricant - + Type Type - + Model Modèle - + Author Auteur - + Physical properties Propriétés physiques - + Channels Canaux - + Add a new channel Ajouter un canal - + Remove the selected channel(s) Retirer les canaux sélectionnés - + + Channel wizard + + + + Modes Modes - + Add a new mode Ajouter un nouveau mode - + Remove the selected mode(s) Supprimer les modes sélectionnées - + Aliases Alias - + New channel %1 Nouveau canal %1 - + New mode Nouveau mode @@ -1071,37 +1146,37 @@ Les modifications seront perdues si vous ne les enregistrez pas. Contrôle - + Universe Univers - + Activate auto detection Activer l'auto-détection - + Channel Canal - + Remove this input source Supprimer cette source d'entrée - + Custom feedbacks Retour personnalisé - + Lower Inférieur - + Upper Supérieur @@ -1127,13 +1202,13 @@ Les modifications seront perdues si vous ne les enregistrez pas. FixtureBrowser - + Create a new fixture definition Add a new fixture definition Créer une nouvelle définition d'appareil - + Edit the selected fixture definition Modifier la définition d'appareil sélectionnée @@ -1141,22 +1216,22 @@ Les modifications seront perdues si vous ne les enregistrez pas. FixtureChannelDelegate - + Auto (HTP) Auto (HTP) - + Auto (LTP) Auto (LTP) - + Forced HTP HTP forcé - + Forced LTP LTP forcé @@ -1245,32 +1320,32 @@ Les modifications seront perdues si vous ne les enregistrez pas. Supprimer les éléments sélectionnés - + Reset the entire group Réinitialiser le groupe - + Rotate 90° clockwise Tourner de 90° (horaire) - + Rotate 180° clockwise Tourner de 180° (horaire) - + Rotate 270° clockwise Tourner de 180° (horaire) - + Flip horizontally Retourner horizontalement - + Flip vertically Retourner verticalement @@ -1278,67 +1353,77 @@ Les modifications seront perdues si vous ne les enregistrez pas. FixtureGroupManager - + + Error + Erreur + + + Add a new fixture group Ajouter un nouveau groupe d'appareils - + Remove the selected items Supprimer les éléments sélectionnés - + Set a Group/Fixture/Channel search filter Définir un filtre de recherche de groupe/appareil/canaux - + Rename the selected items Renommer les éléments sélectionnés - + Rename items Renommer les éléments - + Inspect the selected item Inspecter l'élément sélectiuonné - + Toggle fixtures and channels properties Basculer les propriétés des appareils et des canaux - + Add/Remove a linked fixture Ajouter/supprimer un appareil lié - + Name Nom - + + Mode + Mode + + + Flags Drapeaux - + Can fade Avec fondu - + Behaviour Comportement - + Modifier Modificateur @@ -1346,67 +1431,85 @@ Les modifications seront perdues si vous ne les enregistrez pas. FixtureManager - - - + + + Head Tête - + New group %1 Nouveau groupe %1 - + %1 - Row %2 %1 - Ligne %2 - + New filters %1 Nouveaux filtres %1 + + FixtureNodeDelegate + + + Show/Hide this fixture + + + + + Invert Pan + + + + + Invert Tilt + + + FixtureProperties - + Fixture properties Propriétés de l'appareil - + Name Nom - + Universe Univers - + Address Adresse - + Quantity Quantité - + Channels Canaux - + Gap Saut - + Mode Mode @@ -1638,67 +1741,67 @@ Les modifications seront perdues si vous ne les enregistrez pas. Définir un filtre de recherche de fonction - + <None> <None> - + New Scene Nouvelle scène - + New Chaser Nouveau chaser - + New Sequence Nouvelle séquence - + New EFX Nouvel EFX - + New Collection Nouvelle collection - + New RGB Matrix Nouvelle matrice RGB - + New Script Nouveau script - + New Show Nouveau spectacle - + New Audio Nouvelle piste audio - + New Video Nouvelle piste vidéo - + (Copy) (Copie) - + New folder Nouveau dossier @@ -1762,31 +1865,31 @@ Les modifications seront perdues si vous ne les enregistrez pas. InputOutputManager - + Input/Output Manager Gestionnaire d'entrée/sortie - + All universes Tous les univers - - - - - + + + + + Default device Périphérique par défaut - + Disabled Désactivé - + Internal generator Générateur interne @@ -1799,10 +1902,123 @@ Les modifications seront perdues si vous ne les enregistrez pas. Supprimer ce profil d'entrée + + InputProfileEditor + + + + + !! Warning !! + !! Attention !! + + + + Channel wizard activated + + + + + You have enabled the input channel wizard. After clicking OK, wiggle your mapped input profile's controls. They should appear into the list. Click the wizard button again to stop channel auto-detection.<br><br>Note that the wizard cannot tell the difference between a knob and a slider so you will have to do the change manually. + + + + + Unsaved changes + + + + + Do you wish to save the current profile first? +Changes will be lost if you don't save them. + + + + + Manufacturer + Fabricant + + + + Model + Modèle + + + + + Type + Type + + + + MIDI Global Settings + + + + + When MIDI notes are used, send a Note Off when value is 0 + + + + + Channel + Canal + + + + Name + Nom + + + + + Behaviour + Comportement + + + + Generate an extra Press/Release when toggled + + + + + Movement + + + + + Sensitivity + + + + + Custom Feedback + + + + + Lower value + + + + + Upper value + + + + + Button %1 + Bouton %1 + + + + Slider %1 + Fader %1 + + IntensityTool - + Intensity Intensité @@ -1823,17 +2039,17 @@ Les modifications seront perdues si vous ne les enregistrez pas. Contrôle - + Combination Combinaison - + Activate auto detection Activer l'auto-détection - + Remove this keyboard combination Supprimer cette combinaison de touches @@ -1846,62 +2062,62 @@ Les modifications seront perdues si vous ne les enregistrez pas. Ajouter des appareils - + Fixture Groups Groupes d'appareils - + Palettes Palettes - + Intensity Intensité - + Shutter Obturateur - + Position Position - + Color Couleur - + Color Wheel Roue chromatique - + Gobos Gobos - + Beam Rayon - + Pick a 3D point Choisir un point 3D - + Toggle multiple item selection Basculer la sélection multiple - + Select/Deselect all fixtures Sélectionner/désélectionner tous les appareils @@ -1909,40 +2125,45 @@ Les modifications seront perdues si vous ne les enregistrez pas. MainView - + Actions Actions - + Fixtures & Functions Appareils & fonctions - + Virtual Console Console virtuelle - + Simple Desk Console simple - + Show Manager Gestionnaire de spectacle - + Input/Output Entrée/Sortie - + Off Éteint + + + Stop all the running functions + + MainView2D @@ -1955,27 +2176,27 @@ Les modifications seront perdues si vous ne les enregistrez pas. MainView3D - + 3D View Vue 3D - + Simple ground Sol simple - + Simple box Boîte simple - + Rock stage Scène de rock - + Theatre stage Scène de théâtre @@ -2063,97 +2284,112 @@ Les modifications seront perdues si vous ne les enregistrez pas. PaletteFanningBox - - + Flat Plat - - + Linear Linéaire - - + Square Carré - - + Saw Dents de scie - - + Sine Sinusoidal - - Left to right - Gauche à droite + Gauche à droite - - Right to left - Droite à gauche + Droite à gauche - - Top to bottom - Haut en bas + Haut en bas - - Bottom to top - Bas en haut + Bas en haut - - Centered - Centré + Centré - + Show/Hide fanning options Afficher/masquer les options de fanning - + Create a new palette Créer une nouvelle palette - + Type Type - + Layout Disposition - + + X Ascending + + + + + X Descending + + + + + Y Ascending + + + + + Y Descending + + + + + Z Ascending + + + + + Z Descending + + + + Amount Quantité - + Value Valeur - + Pick the selected color Choisir la couleur @@ -2186,12 +2422,12 @@ Les modifications seront perdues si vous ne les enregistrez pas. Supprimer les palettes sélectionnées - + Are you sure you want to delete the following items? Êtes-vous sûr de vouloir supprimer les éléments suivants ? - + Delete items Supprimer les éléments @@ -2288,103 +2524,279 @@ Les modifications seront perdues si vous ne les enregistrez pas. - - Power Consumption - Consommation électrique + + Power Consumption + Consommation électrique + + + + DMX Connector + Connecteur DMX + + + + PopupAbout + + + Information + Information + + + + and contributors + et contributeurs + + + + Website + Site web + + + + This application is licensed under the terms of the + Cette application est sous licence + + + + Apache 2.0 license + Apache 2.0 license + + + + PopupChannelModifiers + + + Channel Modifiers Editor + + + + + Insert a modified value after the selected + + + + + Delete the selected modifier value + + + + + Rename the selected modifier template + + + + + Save the selected modifier template + + + + + Templates + + + + + Original DMX value + + + + + Modified DMX value + + + + + PopupChannelWizard + + + Fixture Editor Wizard + + + + + Properties + + + + + Start + + + + + Width + Largeur + + + + Amount + Quantité + + + + Type + Type + + + + Red + Rouge + + + + Green + Vert + + + + Blue + Bleu + + + + White + Blanc + + + + Amber + Ambre + + + + UV + UV + + + + RGB + + + + + RGBW + + + + + RGBAW + + + + + Dimmer + + + + + Pan + Pan + + + + Tilt + Tilt + + + + Color Macro + + + + + Shutter + Obturateur - - DMX Connector - Connecteur DMX + + Beam + Rayon - - - PopupAbout - - Information - Information + + Effect + Effet - - and contributors - et contributeurs + + Label + Label - - Website - Site web + + Capability # + - - This application is licensed under the terms of the - Cette application est sous licence + + Channel # + - - Apache 2.0 license - Apache 2.0 license + + Preview + Prévisualisation PopupCreatePalette - + Create a new palette Créer une nouvelle palette - + Dimmer - + Color Couleur - + Position Position - + Shutter Obturateur - + Gobo Gobo - + Palette name Nom de palette - + New Palette Nouvelle palette - + Type Type - + Also create a Scene - + Scene name Nom de la scène - + New Scene Nouvelle scène @@ -2392,87 +2804,87 @@ Les modifications seront perdues si vous ne les enregistrez pas. PopupDMXDump - + Enter a name for the scene Entrer un nom pour la scène - + Scene name Nom de la scène - + New Scene Nouvelle scène - + Don't ask again Ne plus me demander - + Available channel types Types de canaux disponibles - + Intensity Intensité - + RGB/CMY/WAUV RVB/CMJ/WAUV - + Color macros Macros de couleur - + Gobo Gobo - + Pan Pan - + Tilt Tilt - + Speed Vitesse - + Shutter/Strobe Obturateur/Stroboscope - + Prism Prisme - + Beam Rayon - + Effect Effet - + Maintenance Maintenance @@ -2480,7 +2892,7 @@ Les modifications seront perdues si vous ne les enregistrez pas. PopupDisclaimer - + Disclaimer Avertissement @@ -2503,6 +2915,54 @@ Les modifications seront perdues si vous ne les enregistrez pas. Fonctions + + PopupInputChannelEditor + + + Input Channel Editor + + + + + Input Channel + + + + + Number + Nombre + + + + Name + Nom + + + + Type + Type + + + + Channel + Canal + + + + Message + + + + + Parameter + + + + + Note + Note + + PopupManualInputSource @@ -2567,27 +3027,27 @@ Les modifications seront perdues si vous ne les enregistrez pas. PopupNetworkClient - + Disconnected Déconnecté - + Waiting for access En attente d'accès - + Downloading project Téléchargement du projet - + Connected Connecté - + QLC+ client setup Installation du client QLC+ @@ -2630,7 +3090,7 @@ Les modifications seront perdues si vous ne les enregistrez pas. PopupNetworkConnect - + Client access request Requête d'accès client @@ -2760,12 +3220,12 @@ Niveau d'accès : PopupPINRequest - + Page PIN PIN de page - + Remember for this session Se souvenir pour cette session @@ -2773,22 +3233,22 @@ Niveau d'accès : PopupPINSetup - + Current PIN PIN actuel - + New PIN Nouveau PIN - + Confirm PIN Confirmer le PIN - + New PIN mismatch Le nouveau PIN ne correspond pas @@ -2796,22 +3256,22 @@ Niveau d'accès : PopupRenameItems - + New name Nouveau nom - + Enable numbering Activer la numérotation - + Start number Premier nombre - + Digits Chiffres @@ -2819,28 +3279,76 @@ Niveau d'accès : PositionTool - + Position Position - + Rotate 90° clockwise Tourner de 90° (horaire) - - + + Snap to the previous value Rammener à la valeur précédente - - + + Snap to the next value Rammener à la valeur suivante + + ProfilesList + + + !! Warning !! + !! Attention !! + + + + Save this profile + + + + + Toggle the automatic detection procedure + + + + + Add a new channel + Ajouter un canal + + + + Create a new input profile + + + + + Edit the selected channel + + + + + Edit the selected input profile + + + + + Delete the selected channel + + + + + Delete the selected input profile(s) + + + RGBMatrixEditor @@ -2919,149 +3427,149 @@ Niveau d'accès : Couleurs - + Parameters Paramètres - + Speed Vitesse - + Steps fade in Fondu en entrée des étapes - + Steps hold Maintien des étapes - + Steps fade out Fondu en sortie des étapes - + Tempo type Type de tempo - + Time Temps - + Beats Battements - + Order and direction Ordre et direction - + Loop Boucle - + Single Shot Unique - + Ping Pong Ping Pong - + Run Order Ordre d'exécution - + Forward Avant - + Backward Arrière - + Direction Direction - + Text Texte - + Please choose a font Veuillez choisir une police de caractères - - - + + + Animation Animation - + Letters Lettres - - + + Horizontal Horizontal - - + + Vertical Vertical - - + + Offset Décalage - - + + X X - - + + Y Y - + Image Image - + Select an image Sélectionner une image - + Static Statique @@ -3069,17 +3577,17 @@ Niveau d'accès : RGBPanelProperties - + RGB panel properties Propriétés du panneau RVB - + Name Nom - + Universe Univers @@ -3238,7 +3746,7 @@ Niveau d'accès : Capturer dans une nouvelle scène - + Function Preview Prévisualiser la fonction @@ -3251,43 +3759,43 @@ Niveau d'accès : SceneEditor - + Add a fixture/group Ajouter un appareil/groupe - + Add a palette Ajouter une palette - + Remove the selected items Remove the selected fixtures Supprimer les éléments sélectionnés - + Delete items Supprimer les éléments - + Are you sure you want to remove the selected items? Êtes-vous sûr de vouloir supprimer les éléments sélectionnés ? - + Speed Vitesse - + Fade in Fondu en entrée - + Fade out Fondu en sortie @@ -3295,67 +3803,67 @@ Niveau d'accès : ScriptEditor - + Add a method call at cursor position Ajouter un appel de méthode à la position du curseur - + Show/hide functions tree Afficher/masquer l'arbre de fonctions - + Show/hide fixture tree Afficher/masquer l'arbre d'appareils - + Check the script syntax Vérifier la syntaxe du script - + Syntax check Vérifier la syntaxe - + Start function Démarrer la fonction - + Stop function Arrêter la fonction - + Set fixture channel Définir le canal de l'appareil - + Wait time Temps d'attente - + Random number Nombre aléatoire - + Blackout Blackout - + System command Commande système - + File path Chemin du fichier @@ -3383,12 +3891,12 @@ Niveau d'accès : Supprimer les appareils sélectionnés - + Steps Étapes - + Fixtures Appareils @@ -3396,107 +3904,122 @@ Niveau d'accès : SettingsView2D - + Environment Environnement - + Width Largeur - + Height Hauteur - + Depth Profondeur - + Grid units Unité de grille - + Meters Mètres - + Feet Pieds - + Point of view Point de vue - + Top view Vue de dessus - + Front view Vue de face - + Right side view Vue de droite - + Left side view Vue de gauche + Custom Background + + + + + Select an image + Sélectionner une image + + + + Reset background + + + + Selected fixtures Appareils sélectionnés - + Gel color Couleur de gélatine - + Rotation Rotation - + Alignment Alignement - + Align the selected items to the left Aligner les éléments sélectionnés à gauche - + Align the selected items to the top Aligner les éléments sélectionnés en haut - + Distribution Distribution - + Equally distribute horizontally the selected items Distribuer horizontalement et également les éléments sélectionnés - + Equally distribute vertically the selected items Distribuer verticalement et également les éléments sélectionnés @@ -3504,126 +4027,130 @@ Niveau d'accès : SettingsView3D - + Environment Environnement - + Type Type - + Width Largeur - + Height Hauteur - + Depth Profondeur - + Rendering Rendu - + Quality Qualité - + Low rang de qualité Basse - + Medium rang de qualité Moyenne - + High rang de qualité Haute - + Ultra rang de qualité Extra - + Ambient light Lumière ambiante - + Smoke amount Quantité de fumée - + Show FPS Voir les FPS - + Scale Échelle - + Custom items Éléments personnalisés - + Select a mesh file Sélectionner un fichier de modèle 3D - + 3D files Fichiers 3D - + All files Tous les fichiers - + + Normalize the selected items + + + Actions - Actions + Actions - + Add a new item to the scene Ajouter un nouvel élément à la scène - + Remove the selected items Supprimer les éléments sélectionnés - + Position Position - + Rotation Rotation @@ -3649,16 +4176,16 @@ Niveau d'accès : ShowItem - - - + + + Position: Position : - - - + + + Duration: Durée : @@ -3676,74 +4203,74 @@ Niveau d'accès : Montrer la couleur des éléments - + Unlock the selected items Déverrouiller les éléments sélectionnés - + Lock the selected items Verrouiller les éléments sélectionnés - + Snap to grid Grille magnétique - + Stretch the original function Étendre la fonction d'origine - + Remove the selected items Supprimer les éléments sélectionnés - + Are you sure you want to remove the following items? (Note that the original functions will not be deleted) Êtes-vous sûr de vouloir supprimer les éléments suivants ? (Les fonctions originales ne seront pas supprimées) - + Delete show items Supprimer les éléments de spectacle - + Copy the selected items in the clipboard Copier les éléments sélectionnés dans le presse-papiers - + Paste items in the clipboard at cursor position Coller les éléments du presse-papiers à la position sélectionnée - + Play or resume Lire ou reprendre - + Stop or rewind Arrêter ou rembobiner - + Move the selected track up Déplacer la piste vers le haut - + Move the selected track down Déplacer la piste vers le bas - + Create a new track Créer une nouvelle piste @@ -3753,19 +4280,19 @@ Niveau d'accès : Gestionnaire de spectacle - + New Show Nouveau spectacle - - + + Track %1 Piste %1 - - + + (Copy) (Copie) @@ -3773,37 +4300,37 @@ Niveau d'accès : SimpleDesk - + Universe Univers - + Reset the whole universe Réinitialiser l'univers - + Dump on a new Scene Capturer dans une nouvelle scène - + Reset the channel Réinitialiser le canal - + Fixture List Liste d'appareils - + Commands history Historique des commandes - + Simple Desk Console simple @@ -3819,25 +4346,202 @@ Niveau d'accès : TrackDelegate - + Solo this track Piste solo - + Mute this track Piste muette + + UISettingsEditor + + + + Reset to default + + + + + Scaling factor + + + + + Background darker + + + + + Background dark + + + + + Background medium + + + + + Background light + + + + + Background lighter + + + + + Controls background + + + + + Foreground main + + + + + Foreground medium + + + + + Foreground light + + + + + Toolbar gradient start + + + + + Sub-toolbar gradient start + + + + + Toolbar gradient end + + + + + Toolbar hover gradient start + + + + + Toolbar hover gradient end + + + + + Toolbar selection + + + + + Sub-toolbar selection + + + + + Section header + + + + + Section header divider + + + + + Item highlight + + + + + Item highlight pressed + + + + + Item hover + + + + + Item selection + + + + + VC Frame drop area + + + + + Item dark border + + + + + Save to file + + + + + Operation completed + + + + + Error + Erreur + + + + UniverseGridView + + + Error + Erreur + + + + Unable to perform the operation. +There is either not enough space or the target universe in invalid + + + + + Cut the selected items into clipboard + + + + + Paste items in the clipboard at the first available position + + + UniverseIOItem - Passthrough - Intermédiaire + Intermédiaire + + + + Enable/Disable passthrough + - + Enable/Disable feedbacks Activer/désactiver le retour d'informations @@ -4049,81 +4753,81 @@ Niveau d'accès : Ajouter une fonction au planning - + Remove this schedule Supprimer ce planning - + Start time Temps de début - + Stop time Temps de fin - + Enable the stop time Activer le temps de fin - + M As in Monday Comme lundi L - + T As in Tuesday Comme mardi M - + W As in Wednesday Comme mercredi M - + T As in Thursday Comme jeudi J - + F As in Friday Comme vendredi V - + S As in Saturday Comme samedi S - + S As in Sunday Comme dimanche D - + Repeat weekly Répétition hébdomadaire - + Add a new schedule Ajouter un nouveau planning @@ -4131,17 +4835,17 @@ Niveau d'accès : VCCueList - + Next Cue Cue suivante - + Previous Cue Cue précédente - + Play/Stop/Pause Lecture/Arrêt/Pause @@ -4154,17 +4858,17 @@ Niveau d'accès : Fondu enchaîné droit - + Stop/Pause Arrêt/Pause - + Side Fader Faders sur le flanc - + Cue List %1 Séquenceur %1 @@ -4172,27 +4876,32 @@ Niveau d'accès : VCCueListItem - + Play/Pause Lecture/Pause - + + Play/Stop + + + + Pause Pause - + Stop Arrêt - + Previous cue Cue précédente - + Next cue Cue suivante @@ -4285,30 +4994,35 @@ Niveau d'accès : VCFrame - + Next Page Page suivante - + Previous Page Page précédente - + Enable Activer - + Collapse Plier - + Frame %1 Cadre %1 + + + Page %1 + Page %1 + VCFrameItem @@ -4323,17 +5037,16 @@ Niveau d'accès : Activer/désactiver ce cadre - + Previous page Page précédente - Page - Page + Page - + Next page Page suivante @@ -4376,10 +5089,21 @@ Niveau d'accès : Numéros de pages - + Clone first page widgets Cloner les widgets de la première page + + + + Shortcuts + + + + + Shortcut name + + VCLabel @@ -4392,12 +5116,12 @@ Niveau d'accès : VCPage - + Page %1 Page %1 - + Virtual Console Page %1 Page %1 de console virtuelle @@ -4405,57 +5129,57 @@ Niveau d'accès : VCPageProperties - + Width Largeur - + Height Hauteur - + Security Sécurité - + Set a PIN Définir un PIN - + Error Erreur - + The entered PINs are either invalid or incorrect Les PINs entrés sont invalides ou incorrects - + Add page to the left Ajouter une page à gauche - + Add page to the right Ajouter une page à droite - + Delete this page Supprimer cette page - + Delete page Supprimer la page - + Are you sure you want to delete the selected page? Êtes-vous sûr de vouloir supprimer la page sélectionnée ? @@ -4520,12 +5244,12 @@ Niveau d'accès : Réinitialiser les contrôles - + Slider %1 Fader %1 - + Knob %1 Potard %1 @@ -4613,67 +5337,67 @@ Niveau d'accès : Ajouter/supprimer des canaux - + Click & Go button Bouton d'accès rapide - + None Aucun - + RGB/CMY RVB/CMJ - + Gobo/Effect/Macro Gobo/Effet/Macro - + Monitor channel levels Surveiller les niveaux des canaux - + Values range Gamme de valeurs - + Upper limit Limite haute - + Lower limit Limite basse - + Grand Master mode Mode Grand master - + Reduce values Réduire les valeurs - + Limit values Limiter les valeurs - + Intensity channels Canaux d'intensité - + All channels Tous les canaux @@ -4750,8 +5474,8 @@ Niveau d'accès : Inconnu - - + + None Aucun @@ -4759,87 +5483,87 @@ Niveau d'accès : VCWidgetProperties - + Select a widget first Sélectionner d'abord un widget - + Settings Paramètres - + External controls Contrôles externes - + Basic properties Propriétés de base - + Label Label - + Background color Couleur d'arrière-plan - + Foreground color Couleur de premier plan - + Font Police - + Please choose a font Veuillez choisir une police de caractères - + Background image Image d'arrière-plan - + Select an image Sélectionner une image - + Alignment Alignement - + Align the selected widgets to the left Aligner les éléments sélectionnés à gauche - + Align the selected widgets to the right Aligner les éléments sélectionnés à droite - + Align the selected widgets to the top Aligner les éléments sélectionnés en haut - + Align the selected widgets to the bottom Aligner les éléments sélectionnés en bas - + External Controls Contrôles externes @@ -4997,72 +5721,72 @@ Niveau d'accès : VirtualConsole - + Error Erreur - + Invalid PIN entered Le PIN entré est invalide - + Enable/Disable widgets snapping Activer/désactiver le magnétisme des widgets - + Widget matrix setup Propriétés de la matrice de widgets - + Columns Colonnes - + Rows Lignes - + Width Largeur - + Height Hauteur - + Frame type Type de cadre - + Normal Normal - + Solo Solo - + Virtual Console Console virtuelle - + <None> <None> - + Page %1 Page %1 diff --git a/qmlui/qlcplus_it_IT.ts b/qmlui/qlcplus_it_IT.ts index 76f7950c8b..10b03fbae9 100644 --- a/qmlui/qlcplus_it_IT.ts +++ b/qmlui/qlcplus_it_IT.ts @@ -4,54 +4,58 @@ ActionsMenu - Open a project - Apri un progetto + Apri un progetto - - + + Project files File di progetto - - - + + + All files Tutti i file - + + Open a file + Apri un file + + + QLC+ files File di QLC+ - - + + Import from project Importa da progetto - - + + Save project as... Salva il progetto come... - + Your project has changes Il progetto è stato modificato - + Do you wish to save the current project first? Changes will be lost if you don't save them. Vuoi salvare il progetto corrente? I cambiamenti verranno perduti se non salvati. - + New project Nuovo progetto @@ -60,117 +64,122 @@ I cambiamenti verranno perduti se non salvati. Apri un progetto - + Open file Apri un file - + Save project Salva il progetto - + Undo Annulla - + Redo Rifai - + Network Rete - + Server setup Configurazione del server - + Client setup Configurazione del client - + Address tool Strumento indirizzi - + DMX Address tool Strumento indirizzi DMX - + + UI Settings + Impostazioni interfaccia + + + Toggle fullscreen Schermo intero - + Language Lingua - + Catalan Catalano - + Dutch Olandese - + English Inglese - + French Francese - + German Tedesco - + Italian Italiano - + Japanese Giapponese - + Polish Polacco - + Russian Russo - + Spanish Spagnolo - + Ukrainian Ucraino - + About Informazioni @@ -291,12 +300,17 @@ I cambiamenti verranno perduti se non salvati. Dispositivo di uscita - + + Volume + Volume + + + Fade in Fade in - + Fade out Fade out @@ -312,20 +326,25 @@ I cambiamenti verranno perduti se non salvati. BeamTool - + Beam Fascio - + Beam degrees Gradi del fascio - + Distance Distanza + + + Projected diameter + Diametro proiettato + BottomPanel @@ -338,8 +357,8 @@ I cambiamenti verranno perduti se non salvati. ChannelEdit - - + + Custom Personalizzato @@ -347,88 +366,118 @@ I cambiamenti verranno perduti se non salvati. ChannelEditor - + + Open a picture file + Apri un file di immagine + + + + Gobo pictures + Immagini gobo + + + + All files + Tutti i file + + + Name Nome - + Preset Preset - - + + Type Tipo - + Role Ruolo - + Coarse (MSB) Grossolano (MSB) - + Fine (LSB) Fine (LSB) - + Default value Valore di default - + Delete the selected capabilities Elimina le caratteristiche selezionate - + + Capability wizard + Assistente per le proprietà + + + + Empty description provided + Inserita una descrizione vuota + + + + Overlapping with another capability + Sovrapposizione con un'altra proprietà + + + From Da - + To A - + Description Descrizione - + Preview Anteprima - + Primary color Colore primario - + Secondary color Colore secondario - + Value(s) Valore(i) - + Value 1 Valore 1 - + Value 2 Valore 2 @@ -436,122 +485,137 @@ I cambiamenti verranno perduti se non salvati. ChaserEditor - + Add a new step Aggiungi un nuovo step - + Remove the selected steps Rimuovi gli step selezionati - + Delete steps Rimozione degli step - + Are you sure you want to remove the selected steps? Sei sicuro di voler rimuovere gli step selezionati? - + + Preview the previous step + Anteprima dello step precedente + + + + Preview the next step + Anteprima dello step successivo + + + + Duplicate the selected step(s) + Duplica gli step selezionati + + + Print the Chaser steps Stampa gli step del Chaser - + Run properties Proprietà di esecuzione - + Loop Ciclica - + Single Shot Singola riproduzione - + Ping Pong Ping pong - + Random Casuale - + Run Order Ordine di esecuzione - + Forward Avanti - + Backward Indietro - + Direction Direzione - + Time Tempo - + Beats Battiti - + Tempo Tempo - - + + Default Predefinito - - - + + + Common Comune - - - + + + Per Step Per step - + Fade In Fade in - + Fade Out Fade out - + Duration Durata @@ -559,32 +623,32 @@ I cambiamenti verranno perduti se non salvati. ChaserWidget - + Function Funzione - + Fade In Fade in - + Hold Hold - + Fade Out Fade out - + Duration Durata - + Note Note @@ -592,22 +656,22 @@ I cambiamenti verranno perduti se non salvati. CollectionEditor - + Add a function Aggiungi una funzione - + Remove the selected function Rimuovi le funzioni selezionate - + Delete functions Rimozione funzioni - + Are you sure you want to remove the selected functions? Sei sicuro di voler rimuovere le funzioni selezionate? @@ -615,17 +679,17 @@ I cambiamenti verranno perduti se non salvati. ColorTool - + Basic Base - + Full Completo - + Filters Filtri @@ -633,7 +697,7 @@ I cambiamenti verranno perduti se non salvati. ColorToolBasic - + Selected color Colore selezionato @@ -641,87 +705,87 @@ I cambiamenti verranno perduti se non salvati. ColorToolFilters - + Open filters menu Apri il menu dei filtri - + Cyan Ciano - + Red Rosso - + White Bianco - + Magenta Magenta - + Green Verde - + Amber Ambra - + Yellow Giallo - + Blue Blu - + UV UV - + CMY CMY - + Add a new color filters file Aggiungi un nuovo file di filtri - + Rename the current color filters file Rinomina il file corrente di filtri colore - + Save the current color filters file Salva il file di filtri colore corrente - + Add a new filter Aggiungi un nuovo file di filtri - + Delete the selected filter Cancella il filtro selezionato - + Paste the latest picked color as new filter Incolla l'ultimo colore selezionato come nuovo filtro @@ -729,37 +793,37 @@ I cambiamenti verranno perduti se non salvati. ColorToolFull - + Red Rosso - + Green Verde - + Blue Blu - + White Bianco - + Amber Ambra - + UV UV - + Selected color Colore selezionato @@ -767,12 +831,12 @@ I cambiamenti verranno perduti se non salvati. ContextManager - + Universe Grid View Vista universo a griglia - + linked in cascata @@ -803,155 +867,175 @@ I cambiamenti verranno perduti se non salvati. EFXEditor - + Fixtures Fixture - + Add a fixture/head Add a fixture head Aggiungi fixture/sezioni - + Remove the selected fixture head(s) Rimuovi le sezioni di fixture selezionate - + Fixture Fixture - + + Mode + Modalità + + + Reverse Invertito - - + + Start offset Posizione di partenza - + + Position + Posizione + + + + Dimmer + Dimmer + + + + RGB + RGB + + + Add a new fixture Aggiungi una nuova fixture - - + + Pattern Percorso - + Relative movement Movimento relativo - + Width Larghezza - + Height Altezza - + X offset Posizione X - + Y offset Posizione Y - + Rotation Rotazione - + X frequency Frequenza X - + Y frequency Frequenza Y - + X phase Fase X - + Y phase Fase Y - + Speed Velocità - + Fade in Fade in - Hold - Hold + Hold - + Fade out Fade out - + Order and direction Ordine e direzione - + + Loop Ciclica - + Single Shot Singola riproduzione - + Ping Pong Ping pong - + Run Order Ordine di esecuzione - + Forward Avanti - + Backward Indietro - + Direction Direzione @@ -959,7 +1043,7 @@ I cambiamenti verranno perduti se non salvati. EditorTopBar - + Go back to the previous view Go back to the Function Manager Torna alla vista precedente @@ -978,77 +1062,82 @@ I cambiamenti verranno perduti se non salvati. !! Attenzione !! - + General Generale - + Manufacturer Produttore - + Type Tipo - + Model Modello - + Author Autore - + Physical properties Proprietà fisiche - + Channels Canali - + Add a new channel Aggiungi un nuovo canale - + Remove the selected channel(s) Elimina i canali selezionati - + + Channel wizard + Assistente dei canali + + + Modes Modalità - + Add a new mode Aggiungi una nuova modalità - + Remove the selected mode(s) Elimina le modalità selezionate - + Aliases - + Alias - + New channel %1 Nuovo canale %1 - + New mode Nuova modalità @@ -1061,37 +1150,37 @@ I cambiamenti verranno perduti se non salvati. Controllo - + Universe Universo - + Activate auto detection Abilita rilevamento automatico - + Channel Canale - + Remove this input source Rimuove questa sorgente di ingresso - + Custom feedbacks Feedback personalizzati - + Lower Inferiore - + Upper Superiore @@ -1117,13 +1206,13 @@ I cambiamenti verranno perduti se non salvati. FixtureBrowser - + Create a new fixture definition Add a new fixture definition Crea una nuova definizione di fixture - + Edit the selected fixture definition Modifica la definizione di fixture selezionata @@ -1131,22 +1220,22 @@ I cambiamenti verranno perduti se non salvati. FixtureChannelDelegate - + Auto (HTP) Automatico (HTP) - + Auto (LTP) Automatico (LTP) - + Forced HTP Forzato HTP - + Forced LTP Forzato LTP @@ -1177,7 +1266,7 @@ I cambiamenti verranno perduti se non salvati. - + Save definition as... Salva la definizione come... @@ -1187,27 +1276,32 @@ I cambiamenti verranno perduti se non salvati. Errore - + + Warning + Attenzione + + + Back to QLC+ Torna a QLC+ - + New definition Nuova definizione - + Open definition Apri definizione - + Save definition Salva definizione - + Unknown Sconosciuto @@ -1230,32 +1324,32 @@ I cambiamenti verranno perduti se non salvati. Rimuovi gli elementi selezionati - + Reset the entire group Ripulisci l'intero gruppo - + Rotate 90° clockwise Ruota di 90° in senso orario - + Rotate 180° clockwise Ruota di 180° in senso orario - + Rotate 270° clockwise Ruota di 270° in senso orario - + Flip horizontally Ribalta in orizzontale - + Flip vertically Ribalta in verticale @@ -1263,67 +1357,77 @@ I cambiamenti verranno perduti se non salvati. FixtureGroupManager - + + Error + Errore + + + Add a new fixture group Aggiungi un nuovo gruppo di fixture - + Remove the selected items Rimuovi gli elementi selezionati - + Set a Group/Fixture/Channel search filter Imposta un filtro di ricerca di gruppo/fixture/canale - + Rename the selected items Rinomina gli elementi selezionati - + Rename items Rinomina elementi - + Inspect the selected item Analizza l'elemento selezionato - + Toggle fixtures and channels properties Mostra le proprietà delle fixture e dei canali - + Add/Remove a linked fixture Aggiungi/rimuovi una fixture in cascata - + Name Nome - + + Mode + Modalità + + + Flags Flag - + Can fade Consenti fade - + Behaviour Comportamento - + Modifier Modificatore @@ -1331,67 +1435,85 @@ I cambiamenti verranno perduti se non salvati. FixtureManager - - - + + + Head Testa - + New group %1 Nuovo gruppo %1 - + %1 - Row %2 %1 - Riga %2 - + New filters %1 Nuovo filtro %1 + + FixtureNodeDelegate + + + Show/Hide this fixture + Mostra/Nascondi questa fixture + + + + Invert Pan + Inverti Pan + + + + Invert Tilt + Inverti Tilt + + FixtureProperties - + Fixture properties Proprietà della fixture - + Name Nome - + Universe Universo - + Address Indirizzo - + Quantity Quantità - + Channels Canali - + Gap Intervallo - + Mode Modalità @@ -1623,67 +1745,67 @@ I cambiamenti verranno perduti se non salvati. Imposta un filtro di ricerca di funzioni - + <None> <Nessuno> - + New Scene Nuova scena - + New Chaser Nuovo chaser - + New Sequence Nuova sequenza - + New EFX Nuovo EFX - + New Collection Nuova collezione - + New RGB Matrix Nuova matrice RGB - + New Script Nuovo script - + New Show Nuovo show - + New Audio Nuovo audio - + New Video Nuovo video - + (Copy) (Copia) - + New folder Nuova cartella @@ -1747,31 +1869,31 @@ I cambiamenti verranno perduti se non salvati. InputOutputManager - + Input/Output Manager Manager di ingressi/uscite - + All universes Tutti gli universi - - - - - + + + + + Default device Dispositivo predefinito - + Disabled Disabilitato - + Internal generator Generatore interno @@ -1784,10 +1906,124 @@ I cambiamenti verranno perduti se non salvati. Rimuovi questo profilo di ingresso + + InputProfileEditor + + + + + !! Warning !! + !! Attenzione !! + + + + Channel wizard activated + Assistente dei canali attivato + + + + You have enabled the input channel wizard. After clicking OK, wiggle your mapped input profile's controls. They should appear into the list. Click the wizard button again to stop channel auto-detection.<br><br>Note that the wizard cannot tell the difference between a knob and a slider so you will have to do the change manually. + È stata attivata la procedura guidata per la creazione dei canali di ingresso. Dopo aver cliccato OK, muovere i controlli del tuo dispositivo di ingresso selezionato. I canali dovrebbero apparire nella lista. Fare clic sul pulsante della procedura guidata nuovamente per fermare il rilevamento automatico dei canali.<br><br>Si noti che la procedura guidata non può distinguere una manopola da un fader per cui, in caso, dovrai aggiustarli manualmente. + + + + Unsaved changes + Modifiche non salvate + + + + Do you wish to save the current profile first? +Changes will be lost if you don't save them. + Vuoi salvare il profilo corrente? +Le modifiche saranno perse se non le salvi. + + + + Manufacturer + Produttore + + + + Model + Modello + + + + + Type + Tipo + + + + MIDI Global Settings + Impostazioni globali MIDI + + + + When MIDI notes are used, send a Note Off when value is 0 + Quando vengono usate note MIDI, invia una Nota Off quando il valore è 0 + + + + Channel + Canale + + + + Name + Nome + + + + + Behaviour + Comportamento + + + + Generate an extra Press/Release when toggled + Genera un evento aggiuntivo di pressione/rilascio quando attivato + + + + Movement + Movimento + + + + Sensitivity + Sensibilità + + + + Custom Feedback + Feedback personalizzato + + + + Lower value + Valore inferiore + + + + Upper value + Valore superiore + + + + Button %1 + Pulsante %1 + + + + Slider %1 + Slider %1 + + IntensityTool - + Intensity Intensità @@ -1808,17 +2044,17 @@ I cambiamenti verranno perduti se non salvati. Controllo - + Combination Combinazione - + Activate auto detection Abilita rilevamento automatico - + Remove this keyboard combination Rimuovi questa combinazione di tasti @@ -1831,62 +2067,62 @@ I cambiamenti verranno perduti se non salvati. Aggiungi fixture - + Fixture Groups Gruppi di fixture - + Palettes Palette - + Intensity Intensità - + Shutter Shutter - + Position Posizione - + Color Colore - + Color Wheel Ruota colori - + Gobos Gobo - + Beam Fascio - + Pick a 3D point Seleziona un punto in 3D - + Toggle multiple item selection Selezione multipla - + Select/Deselect all fixtures Seleziona/deseleziona tutte le fixture @@ -1894,40 +2130,45 @@ I cambiamenti verranno perduti se non salvati. MainView - + Actions Azioni - + Fixtures & Functions Fixture & Funzioni - + Virtual Console Console Virtuale - + Simple Desk Banco Semplice - + Show Manager Show Manager - + Input/Output Ingressi/Uscite - + Off Off + + + Stop all the running functions + Ferma tutte le funzioni in esecuzione + MainView2D @@ -1940,27 +2181,27 @@ I cambiamenti verranno perduti se non salvati. MainView3D - + 3D View Vista 3D - + Simple ground Palco semplice - + Simple box Stanza semplice - + Rock stage Palco rock - + Theatre stage Palco teatro @@ -1976,38 +2217,58 @@ I cambiamenti verranno perduti se non salvati. ModeEditor - + Name Nome - - + + Channels Canali - + + Create a new emitter + Crea un nuovo emettitore + + + + Remove the selected channel(s) + Elimina i canali selezionati + + + + Drop channels here + Trascina i canali qui + + + Acts on Agisce su - - Heads - + + Emitters + Emettitori - + + Remove the selected emitter(s) + Rimuovi gli emettitori selezionati + + + Physical Dimensioni fisiche - + Use global settings Usa le impostazioni globali - + Override global settings Usa al posto delle impostazioni globali @@ -2028,97 +2289,112 @@ I cambiamenti verranno perduti se non salvati. PaletteFanningBox - - + Flat Piatto - - + Linear Lineare - - + Square Onda quadra - - + Saw Onda dente di sega - - + Sine Sinusoide - - Left to right - Sinistra verso destra + Sinistra verso destra - - Right to left - Destra verso sinistra + Destra verso sinistra - - Top to bottom - Alto verso basso + Alto verso basso - - Bottom to top - Basso verso alto + Basso verso alto - - Centered - Centrato + Centrato - + Show/Hide fanning options Mostra/Nascondi le opzioni di fanning - + Create a new palette Crea una nuova palette - + Type Tipo - + Layout Disposizione - + + X Ascending + X Ascendente + + + + X Descending + X Discendente + + + + Y Ascending + Y Ascendente + + + + Y Descending + Y Discendente + + + + Z Ascending + Z Ascendente + + + + Z Descending + Z Discendente + + + Amount Contributo - + Value Valore - + Pick the selected color Cattura il colore selezionato @@ -2151,12 +2427,12 @@ I cambiamenti verranno perduti se non salvati. Elimina le palette selezionate - + Are you sure you want to delete the following items? Sei sicuro di voler eliminare i seguenti elementi? - + Delete items Elimina elementi @@ -2170,8 +2446,8 @@ I cambiamenti verranno perduti se non salvati. - - + + Type Tipo @@ -2181,84 +2457,84 @@ I cambiamenti verranno perduti se non salvati. Lumen - + Colour Temp (K) Temperatura colore (K) - + Lens Lente - + Min Degrees Gradi minimi - + Max Degrees Gradi massimi - + Head(s) - + Emettitori - + Pan Max Degrees Gradi massimi di Pan - + Tilt Max Degrees Gradi massimi di Tilt - + Layout (Columns x Rows) Disposizione (Colonne x righe) - + Dimensions Dimensioni - + Weight Peso - + Width Larghezza - + Height Altezza - + Depth Profondità - + Electrical Elettrico - + Power Consumption Consumo - + DMX Connector Connettore DMX @@ -2291,65 +2567,241 @@ I cambiamenti verranno perduti se non salvati. licenza Apache 2.0 + + PopupChannelModifiers + + + Channel Modifiers Editor + Editor di modificatori di canali + + + + Insert a modified value after the selected + Inserisci un modificatore dopo quello selezionato + + + + Delete the selected modifier value + Elimina il modificatore selezionato + + + + Rename the selected modifier template + Rinomina il template di modificatori selezionato + + + + Save the selected modifier template + Salva il template di modificatori selezionato + + + + Templates + Template + + + + Original DMX value + Valore DMX originale + + + + Modified DMX value + Valore DMX modificato + + + + PopupChannelWizard + + + Fixture Editor Wizard + Assistente di creazione di fixture + + + + Properties + Proprietà + + + + Start + Inizio + + + + Width + Larghezza + + + + Amount + Contributo + + + + Type + Tipo + + + + Red + Rosso + + + + Green + Verde + + + + Blue + Blu + + + + White + Bianco + + + + Amber + Ambra + + + + UV + UV + + + + RGB + RGB + + + + RGBW + RGBW + + + + RGBAW + RGBAW + + + + Dimmer + Dimmer + + + + Pan + Pan + + + + Tilt + Tilt + + + + Color Macro + Macro di colore + + + + Shutter + Shutter + + + + Beam + Fascio + + + + Effect + Effetto + + + + Label + Etichetta + + + + Capability # + Proprietà # + + + + Channel # + Canale # + + + + Preview + Anteprima + + PopupCreatePalette - + Create a new palette Crea una nuova palette - + Dimmer Dimmer - + Color Colore - + Position Posizione - + Shutter Shutter - + Gobo Gobo - + Palette name Nome della palette - + New Palette Nuova palette - + Type Tipo - + Also create a Scene Crea anche una scena - + Scene name Nome della scena - + New Scene Nuova scena @@ -2357,87 +2809,87 @@ I cambiamenti verranno perduti se non salvati. PopupDMXDump - + Enter a name for the scene Inserisci un nome per la scena - + Scene name Nome della scena - + New Scene Nuova scena - + Don't ask again Non chiedere più - + Available channel types Tipi di canali disponibili - + Intensity Intensità - + RGB/CMY/WAUV RGB/CMY/WAUV - + Color macros Macro colore - + Gobo Gobo - + Pan Pan - + Tilt Tilt - + Speed Velocità - + Shutter/Strobe Shutter/Strobo - + Prism Prisma - + Beam Fascio - + Effect Effetto - + Maintenance Manutenzione @@ -2445,7 +2897,7 @@ I cambiamenti verranno perduti se non salvati. PopupDisclaimer - + Disclaimer Avviso @@ -2468,6 +2920,54 @@ I cambiamenti verranno perduti se non salvati. Funzioni + + PopupInputChannelEditor + + + Input Channel Editor + Editor di canali di ingresso + + + + Input Channel + Canale di ingresso + + + + Number + Numero + + + + Name + Nome + + + + Type + Tipo + + + + Channel + Canale + + + + Message + Messaggio + + + + Parameter + Parametro + + + + Note + Nota + + PopupManualInputSource @@ -2532,27 +3032,27 @@ I cambiamenti verranno perduti se non salvati. PopupNetworkClient - + Disconnected Disconnesso - + Waiting for access In attesa di accesso - + Downloading project Scaricamento progetto - + Connected Connesso - + QLC+ client setup Configurazione client QLC+ @@ -2595,7 +3095,7 @@ I cambiamenti verranno perduti se non salvati. PopupNetworkConnect - + Client access request Richiesta di accesso di un client @@ -2728,12 +3228,12 @@ Livello di accesso: PopupPINRequest - + Page PIN PIN della pagina - + Remember for this session Ricorda per questa sessione @@ -2741,22 +3241,22 @@ Livello di accesso: PopupPINSetup - + Current PIN PIN corrente - + New PIN Nuovo PIN - + Confirm PIN Conferma PIN - + New PIN mismatch Il nuovo PIN non corrisponde @@ -2764,22 +3264,22 @@ Livello di accesso: PopupRenameItems - + New name Nuovo nome - + Enable numbering Abilita numerazione - + Start number Numero iniziale - + Digits Cifre @@ -2787,28 +3287,76 @@ Livello di accesso: PositionTool - + Position Posizione - + Rotate 90° clockwise Ruota di 90° in senso orario - - + + Snap to the previous value Salta al valore precedente - - + + Snap to the next value Salta al prossimo valore + + ProfilesList + + + !! Warning !! + !! Attenzione !! + + + + Save this profile + Salva questo profilo + + + + Toggle the automatic detection procedure + Attiva/disattiva la procedura automatica di rilevamento + + + + Add a new channel + Aggiungi un nuovo canale + + + + Create a new input profile + Crea un nuovo profilo di ingresso + + + + Edit the selected channel + Modifica il canale selezionato + + + + Edit the selected input profile + Modifica il profilo di ingresso selezionato + + + + Delete the selected channel + Elimina il canale selezionato + + + + Delete the selected input profile(s) + Elimina il profilo di ingresso selezionato + + RGBMatrixEditor @@ -2822,214 +3370,214 @@ Livello di accesso: Percorso - + Blend mode Fusione - + Default (HTP) Predefinito (HTP) - + Mask Maschera - + Additive Additiva - + Subtractive Sottrattiva - + Color mode Modalità colore - + Default (RGB) Default (RGB) - + White Bianco - + Amber Ambra - + UV UV - + Dimmer Dimmer - + Shutter Shutter - + Colors Colori - + Parameters Parametri - + Speed Velocità - + Steps fade in Fade in degli step - + Steps hold Hold degli step - + Steps fade out Fade out degli step - + Tempo type Tipo di tempo - + Time Tempo - + Beats Battiti - + Order and direction Ordine e direzione - + Loop Ciclica - + Single Shot Singola riproduzione - + Ping Pong Ping pong - + Run Order Ordine di esecuzione - + Forward Avanti - + Backward Indietro - + Direction Direzione - + Text Testo - + Please choose a font Selezione font - - - + + + Animation Animazione - + Letters Lettere - - + + Horizontal Orizzontale - - + + Vertical Verticale - - + + Offset Offset - - + + X X - - + + Y Y - + Image Immagine - + Select an image - + Static Statica @@ -3037,17 +3585,17 @@ Livello di accesso: RGBPanelProperties - + RGB panel properties Proprietà del pannello RGB - + Name Nome - + Universe Universo @@ -3206,7 +3754,7 @@ Livello di accesso: Salva su una nuova scena - + Function Preview Preview della funzione @@ -3219,43 +3767,43 @@ Livello di accesso: SceneEditor - + Add a fixture/group Aggiungi una fixture/gruppo - + Add a palette Aggiungi una palette - + Remove the selected items Remove the selected fixtures Rimuovi le fixture selezionate - + Delete items Elimina elementi - + Are you sure you want to remove the selected items? Sei sicuro di voler rimuovere gli elementi selezionati? - + Speed Velocità - + Fade in Fade in - + Fade out Fade out @@ -3263,67 +3811,67 @@ Livello di accesso: ScriptEditor - + Add a method call at cursor position Aggiungi una funzione alla posizione del cursore - + Show/hide functions tree Mostra/nascondi l'albero delle funzioni - + Show/hide fixture tree Mostra/nascondi l'albero delle fixture - + Check the script syntax Verifica la sintassi dello script - + Syntax check Verifica sintassi - + Start function Avvia funzione - + Stop function Interrompi funzione - + Set fixture channel Imposta il canale di una fixture - + Wait time Tempo di attesa - + Random number Numero casuale - + Blackout Blackout - + System command Comando di sistema - + File path Persorso di un file @@ -3351,12 +3899,12 @@ Livello di accesso: Rimuovi le fixture selezionate - + Steps Step - + Fixtures Fixture @@ -3364,107 +3912,122 @@ Livello di accesso: SettingsView2D - + Environment Ambiente - + Width Larghezza - + Height Altezza - + Depth Profondità - + Grid units Unità della griglia - + Meters Metri - + Feet Piedi - + Point of view Punto di vista - + Top view Vista dall'alto - + Front view Vista frontale - + Right side view Vista lato destro - + Left side view Vista lato sinistro + Custom Background + Sfondo personalizzato + + + + Select an image + Seleziona una immagine + + + + Reset background + Reimposta lo sfondo + + + Selected fixtures Fixture selezionate - + Gel color Colore della gelatina - + Rotation Rotazione - + Alignment Allineamento - + Align the selected items to the left Allinea a sinistra gli elementi selezionati - + Align the selected items to the top Allinea in alto gli elementi selezionati - + Distribution Distribuzione - + Equally distribute horizontally the selected items Distribuisci equamente in orizzontale gli elementi selezionati - + Equally distribute vertically the selected items Distribuisci equamente in verticale gli elementi selezionati @@ -3472,122 +4035,126 @@ Livello di accesso: SettingsView3D - + Environment Ambiente - + Type Tipo - + Width Larghezza - + Height Altezza - + Depth Profondità - + Rendering Rendering - + Quality Qualità - + Low Bassa - + Medium Media - + High Alta - + Ultra Ultra - + Ambient light Luce ambientale - + Smoke amount Quantità di fumo - + Show FPS Mostra gli FPS - + Scale Scalamento - + Custom items Oggetti personalizzati - + Select a mesh file Seleziona un file 3D - + 3D files File 3D - + All files Tutti i file - + + Normalize the selected items + Normalizza gli elementi selezionati + + Actions - Azioni + Azioni - + Add a new item to the scene Aggiungi un nuovo elemento alla scena - + Remove the selected items Rimuovi gli elementi selezionati - + Position Posizione - + Rotation Rotazione @@ -3613,16 +4180,16 @@ Livello di accesso: ShowItem - - - + + + Position: Posizione: - - - + + + Duration: Durata: @@ -3640,74 +4207,74 @@ Livello di accesso: Colore elementi dello show - + Unlock the selected items Sblocca gli elementi selezionati - + Lock the selected items Blocca gli elementi selezionati - + Snap to grid Allinea alla griglia - + Stretch the original function Modifica la durata della funzione originale - + Remove the selected items Rimuovi gli elementi selezionati - + Are you sure you want to remove the following items? (Note that the original functions will not be deleted) Sei sicuro di voler eliminare gli elementi seguenti? (Le funzioni originali non verranno eliminate) - + Delete show items Elimina elementi dello show - + Copy the selected items in the clipboard Copia gli elementi selezionati in memoria - + Paste items in the clipboard at cursor position Incolla gli elementi in memoria alla posizione del cursore - + Play or resume Riproduci o continua - + Stop or rewind Interrompi o riavvolgi - + Move the selected track up Sposta in su la traccia selezionata - + Move the selected track down Sposta in giù la traccia selezionata - + Create a new track Crea una nuova traccia @@ -3717,19 +4284,19 @@ Livello di accesso: Show Manager - + New Show Nuovo show - - + + Track %1 Traccia %1 - - + + (Copy) (Copia) @@ -3737,37 +4304,37 @@ Livello di accesso: SimpleDesk - + Universe Universo - + Reset the whole universe Reimposta l'intero universo - + Dump on a new Scene Salva su una nuova scena - + Reset the channel Reimposta il canale - + Fixture List Lista di fixture - + Commands history Storico dei comandi - + Simple Desk Banco Semplice @@ -3783,25 +4350,203 @@ Livello di accesso: TrackDelegate - + Solo this track Traccia esclusiva - + Mute this track Traccia silenziata + + UISettingsEditor + + + + Reset to default + Reimposta a predefinito + + + + Scaling factor + Fattore di scala + + + + Background darker + Sfondo più scuro + + + + Background dark + Sfondo scuro + + + + Background medium + Sfondo medio + + + + Background light + Sfondo chiaro + + + + Background lighter + Sfondo più chiaro + + + + Controls background + Sfondo dei controlli + + + + Foreground main + Primo piano principale + + + + Foreground medium + Primo piano medio + + + + Foreground light + Primo piano chiaro + + + + Toolbar gradient start + Inizio gradiente barra degli strumenti + + + + Sub-toolbar gradient start + Inizio gradiente barra secondaria degli strumenti + + + + Toolbar gradient end + Fine gradiente barra degli strumenti + + + + Toolbar hover gradient start + Inizio gradiente barra degli strumenti evidenziata + + + + Toolbar hover gradient end + Fine gradiente barra degli strumenti evidenziata + + + + Toolbar selection + Selezione barra degli strumenti + + + + Sub-toolbar selection + Selezione barra degli strumenti secondaria + + + + Section header + Intestazione di sezione + + + + Section header divider + Divisore intestazione di sezione + + + + Item highlight + Elemento evidenziato + + + + Item highlight pressed + Elemento evidenziato e premuto + + + + Item hover + Elemento con cursore sopra + + + + Item selection + Elemento selezionato + + + + VC Frame drop area + Area di rilascio frame VC + + + + Item dark border + Bordo scuro elemento + + + + Save to file + Salva su file + + + + Operation completed + Operazione completata + + + + Error + Errore + + + + UniverseGridView + + + Error + Errore + + + + Unable to perform the operation. +There is either not enough space or the target universe in invalid + Impossibile eseguire l'operazione. +Non c'è abbastanza spazio o l'universo di destinazione è invalido + + + + Cut the selected items into clipboard + Taglia gli elementi selezionati negli appunti + + + + Paste items in the clipboard at the first available position + Incolla gli elementi degli appunti alla prima posizione disponibile + + UniverseIOItem - Passthrough - Passthrough + Passthrough + + + + Enable/Disable passthrough + Abilita/disabilita passthrough - + Enable/Disable feedbacks Abilita/Disabilita feedback @@ -4013,74 +4758,74 @@ Livello di accesso: Aggiungi l'avvio programmato di una funzione - + Remove this schedule Rimuovi questa programmazione - + Start time Ora di inizio - + Stop time Ora di termine - + Enable the stop time Abilita l'ora di termine - + M As in Monday L - + T As in Tuesday M - + W As in Wednesday M - + T As in Thursday G - + F As in Friday V - + S As in Saturday S - + S As in Sunday D - + Repeat weekly Ripeti ogni settimana - + Add a new schedule Aggiungi una nuova programmazione @@ -4088,17 +4833,17 @@ Livello di accesso: VCCueList - + Next Cue Step successivo - + Previous Cue Step precedente - + Play/Stop/Pause Play/Stop/Pausa @@ -4111,17 +4856,17 @@ Livello di accesso: Fader destro - + Stop/Pause Stop/Pausa - + Side Fader Fader laterale - + Cue List %1 Lista di azioni %1 @@ -4129,27 +4874,32 @@ Livello di accesso: VCCueListItem - + Play/Pause Play/Pausa - + + Play/Stop + Avvia/Interrompi + + + Pause Pausa - + Stop Stop - + Previous cue Step precedente - + Next cue Step successivo @@ -4242,30 +4992,35 @@ Livello di accesso: VCFrame - + Next Page Pagina successiva - + Previous Page Pagina precedente - + Enable Abilita - + Collapse Contrai - + Frame %1 Frame %1 + + + Page %1 + Pagina %1 + VCFrameItem @@ -4280,17 +5035,16 @@ Livello di accesso: Abilita/Disabilita questo frame - + Previous page Pagina precedente - Page - Pagina + Pagina - + Next page Pagina successiva @@ -4333,10 +5087,21 @@ Livello di accesso: Numero di pagine - + Clone first page widgets Clona il contenuto della prima pagina + + + + Shortcuts + Scorciatoie + + + + Shortcut name + Nome della scorciatoia + VCLabel @@ -4349,12 +5114,12 @@ Livello di accesso: VCPage - + Page %1 Pagina %1 - + Virtual Console Page %1 Pagina %1 Console Virtuale @@ -4362,57 +5127,57 @@ Livello di accesso: VCPageProperties - + Width Larghezza - + Height Altezza - + Security Sicurezza - + Set a PIN Imposta un PIN - + Error Errore - + The entered PINs are either invalid or incorrect I PIN inseriti sono invalidi o errati - + Add page to the left Aggiungi pagina a sinistra - + Add page to the right Aggiungi pagina a destra - + Delete this page Elimina questa pagina - + Delete page Elimina pagina - + Are you sure you want to delete the selected page? Sei sicuro di voler eliminare la pagina selezionata? @@ -4477,12 +5242,12 @@ Livello di accesso: Controllo del reset - + Slider %1 Slider %1 - + Knob %1 Manopola %1 @@ -4555,82 +5320,82 @@ Livello di accesso: Attributo - + Level mode Modalità livello - + Channels Canali - + Add/Remove channels Aggiungi/Rimuovi canali - + Click & Go button Pulsante Click & Go - + None Nessuno - + RGB/CMY RGB/CMY - + Gobo/Effect/Macro Gobo/Effetto/Macro - + Monitor channel levels Monitoring livello canali - + Values range Intervallo di valori - + Upper limit Limite superiore - + Lower limit Limite inferiore - + Grand Master mode Modalità grand master - + Reduce values Scala valori - + Limit values Limita valori - + Intensity channels Canali di intensità - + All channels Tutti i canali @@ -4707,8 +5472,8 @@ Livello di accesso: Sconosciuto - - + + None Nessuno @@ -4716,87 +5481,87 @@ Livello di accesso: VCWidgetProperties - + Select a widget first Seleziona almeno un widget - + Settings Impostazioni - + External controls Controlli esterni - + Basic properties Proprietà di base - + Label Etichetta - + Background color Colore sfondo - + Foreground color Colore primo piano - + Font Carattere - + Please choose a font Seleziona un carattere - + Background image Immagine di sfondo - + Select an image Seleziona una immagine - + Alignment Allineamento - + Align the selected widgets to the left Allinea a sinistra i widget selezionati - + Align the selected widgets to the right Allinea a destra i widget selezionati - + Align the selected widgets to the top Allinea in alto i widget selezionati - + Align the selected widgets to the bottom Allinea in basso i widget selezionati - + External Controls Controlli esterni @@ -4874,77 +5639,77 @@ Livello di accesso: Schermo di uscita - + Output mode Modalità di uscita - + Windowed In finestra - + Fullscreen Pieno schermo - + Geometry Geometria - + Original Originale - + Custom Personalizzata - + Position Posizione - + Size Dimensione - + W L - + H A - + Rotation Rotazione - + X X - + Y Y - + Z Z - + Layer Layer @@ -4952,72 +5717,72 @@ Livello di accesso: VirtualConsole - + Error Errore - + Invalid PIN entered Il PIN inserito non è corretto - + Enable/Disable widgets snapping Abilita/Disabilita l'ancoraggio dei widget - + Widget matrix setup Configurazione matrice di widget - + Columns Colonne - + Rows Righe - + Width Larghezza - + Height Altezza - + Frame type Tipo di frame - + Normal Normale - + Solo Esclusivo - + Virtual Console Console Virtuale - + <None> <Nessuno> - + Page %1 Pagina %1 diff --git a/qmlui/qlcplus_ja_JP.ts b/qmlui/qlcplus_ja_JP.ts index d0904c50b5..a9d6f02c4f 100644 --- a/qmlui/qlcplus_ja_JP.ts +++ b/qmlui/qlcplus_ja_JP.ts @@ -4,47 +4,51 @@ ActionsMenu - Open a project - プロジェクトファイルを開く + プロジェクトファイルを開く - - + + Project files プロジェクトファイル - - - + + + All files 全てのファイル - + + Open a file + + + + QLC+ files - - + + Import from project プロジェクトの読み込み - - + + Save project as... 保存 - + Your project has changes プロジェクトデータが未保存です - + Do you wish to save the current project first? Changes will be lost if you don't save them. Do you wish to save the current project first ? @@ -52,7 +56,7 @@ Changes will be lost if you don't save them. プロジェクトに未保存の編集があります\nプロジェクトを保存しますか? - + New project 新規作成 @@ -61,117 +65,122 @@ Changes will be lost if you don't save them. 開く - + Open file - + Save project 保存 - + Undo Undo - + Redo Redo - + Network ネットワーク - + Server setup サーバー設定 - + Client setup クライアント設定 - + Address tool アドレス設定支援ツール - + DMX Address tool アドレス設定支援ツール - + + UI Settings + + + + Toggle fullscreen フルスクリーン切り替え - + Language 言語設定 - + Catalan カタルーニャ語 - + Dutch オランダ語 - + English 英語 - + French フランス語 - + German ドイツ語 - + Italian イタリア語 - + Japanese 日本語 - + Polish - + Russian - + Spanish スペイン語 - + Ukrainian - + About QLC+について @@ -292,12 +301,17 @@ Changes will be lost if you don't save them. 出力デバイス - + + Volume + + + + Fade in フェードイン - + Fade out フェードアウト @@ -313,20 +327,25 @@ Changes will be lost if you don't save them. BeamTool - + Beam ビーム - + Beam degrees ビーム角 - + Distance 距離 + + + Projected diameter + + BottomPanel @@ -339,8 +358,8 @@ Changes will be lost if you don't save them. ChannelEdit - - + + Custom カスタム @@ -348,88 +367,118 @@ Changes will be lost if you don't save them. ChannelEditor - + + Open a picture file + + + + + Gobo pictures + + + + + All files + 全てのファイル + + + Name 名前 - + Preset - - + + Type - + Role - + Coarse (MSB) - + Fine (LSB) - + Default value - + Delete the selected capabilities - + + Capability wizard + + + + + Empty description provided + + + + + Overlapping with another capability + + + + From - + To - + Description - + Preview - + Primary color - + Secondary color - + Value(s) - + Value 1 - + Value 2 @@ -437,123 +486,138 @@ Changes will be lost if you don't save them. ChaserEditor - + Add a new step ステップの追加 - + Remove the selected steps 選択したステップを削除 - + Delete steps ステップの削除 - + Are you sure you want to remove the selected steps? Are you sure you want to remove the selected steps ? 選択したステップを削除しますか? - + + Preview the previous step + + + + + Preview the next step + + + + + Duplicate the selected step(s) + + + + Print the Chaser steps - + Run properties 再生設定 - + Loop ループ - + Single Shot 一度再生 - + Ping Pong 往復 - + Random ランダム - + Run Order 再生方向 - + Forward 順再生 - + Backward 逆再生 - + Direction 再生方向 - + Time 時間指定 - + Beats テンポ指定 - + Tempo テンポ - - + + Default デフォルト - - - + + + Common 共通 - - - + + + Per Step 個別 - + Fade In フェードイン - + Fade Out フェードアウト - + Duration ウェイトタイム @@ -561,32 +625,32 @@ Changes will be lost if you don't save them. ChaserWidget - + Function ファンクション - + Fade In フェードイン - + Hold ウェイトタイム - + Fade Out フェードアウト - + Duration ウェイトタイム - + Note メモ @@ -594,22 +658,22 @@ Changes will be lost if you don't save them. CollectionEditor - + Add a function ファンクションの追加 - + Remove the selected function 選択したファンクションを削除します - + Delete functions ファンクションの削除 - + Are you sure you want to remove the selected functions? Are you sure you want to remove the selected functions ? ファンクションを削除しますか? @@ -618,17 +682,17 @@ Changes will be lost if you don't save them. ColorTool - + Basic 基本色 - + Full カラーピッカ - + Filters カラーフィルタ @@ -636,7 +700,7 @@ Changes will be lost if you don't save them. ColorToolBasic - + Selected color 選択中の色 @@ -644,87 +708,87 @@ Changes will be lost if you don't save them. ColorToolFilters - + Open filters menu カラーフィルターメニューを開く - + Cyan シアン - + Red - + White - + Magenta マゼンタ - + Green - + Amber アンバー - + Yellow - + Blue - + UV UV - + CMY CMY - + Add a new color filters file カラーフィルタパレットを作る - + Rename the current color filters file カラーフィルタパレット名を変更 - + Save the current color filters file カラーフィルタパレットを保存 - + Add a new filter 色を追加 - + Delete the selected filter 選択した色を削除 - + Paste the latest picked color as new filter 直前に作った色をペースト @@ -732,37 +796,37 @@ Changes will be lost if you don't save them. ColorToolFull - + Red Red - + Green Green - + Blue Blue - + White White - + Amber Amber - + UV UV - + Selected color 選択中の色 @@ -770,12 +834,12 @@ Changes will be lost if you don't save them. ContextManager - + Universe Grid View 出力モニター - + linked linked @@ -806,154 +870,174 @@ Changes will be lost if you don't save them. EFXEditor - + Fixtures フィクスチャー - + Add a fixture/head フィクスチャーの追加 - + Remove the selected fixture head(s) 選択したフィクスチャーの削除 - + Fixture フィクスチャー - + + Mode + モード + + + Reverse 反転 - - + + Start offset スタートオフセット - + + Position + + + + + Dimmer + + + + + RGB + + + + Add a new fixture フィクスチャーの追加 - - + + Pattern パターン - + Relative movement 相対ポジション - + Width - + Height 高さ - + X offset Xオフセット - + Y offset Yオフセット - + Rotation 回転 - + X frequency X周波数 - + Y frequency Y周波数 - + X phase X位相差 - + Y phase Y位相差 - + Speed スピード - + Fade in フェードイン - Hold - ウェイトタイム + ウェイトタイム - + Fade out フェードアウト - + Order and direction 再生オプション - + + Loop ループ - + Single Shot 一回再生 - + Ping Pong 往復 - + Run Order 再生方向 - + Forward 順方向 - + Backward 逆方向 - + Direction 方向 @@ -961,7 +1045,7 @@ Changes will be lost if you don't save them. EditorTopBar - + Go back to the previous view Go back to the Function Manager ファンクションマネージャーに戻る @@ -980,77 +1064,82 @@ Changes will be lost if you don't save them. - + General - + Manufacturer メーカー名 - + Type - + Model - + Author 作者 - + Physical properties - + Channels チャンネル - + Add a new channel - + Remove the selected channel(s) - + + Channel wizard + + + + Modes - + Add a new mode - + Remove the selected mode(s) - + Aliases - + New channel %1 - + New mode @@ -1063,37 +1152,37 @@ Changes will be lost if you don't save them. コントロール - + Universe ユニバース - + Activate auto detection 自動検知 - + Channel チャンネル - + Remove this input source 入力を削除 - + Custom feedbacks カスタムフィードバック - + Lower 下へ - + Upper 上へ @@ -1119,13 +1208,13 @@ Changes will be lost if you don't save them. FixtureBrowser - + Create a new fixture definition Add a new fixture definition - + Edit the selected fixture definition @@ -1133,22 +1222,22 @@ Changes will be lost if you don't save them. FixtureChannelDelegate - + Auto (HTP) Auto (HTP) - + Auto (LTP) Auto (LTP) - + Forced HTP Forced HTP - + Forced LTP Forced LTP @@ -1179,7 +1268,7 @@ Changes will be lost if you don't save them. - + Save definition as... @@ -1189,27 +1278,32 @@ Changes will be lost if you don't save them. エラー - + + Warning + + + + Back to QLC+ - + New definition - + Open definition - + Save definition - + Unknown Unknown @@ -1232,32 +1326,32 @@ Changes will be lost if you don't save them. - + Reset the entire group 全てのグループをリセット - + Rotate 90° clockwise 90°回転 - + Rotate 180° clockwise 180°回転 - + Rotate 270° clockwise 270°回転 - + Flip horizontally 左右反転 - + Flip vertically 上下反転 @@ -1265,67 +1359,77 @@ Changes will be lost if you don't save them. FixtureGroupManager - + + Error + エラー + + + Add a new fixture group フィクスチャーグループを追加 - + Remove the selected items 選択したフィクスチャーを削除 - + Set a Group/Fixture/Channel search filter Set a Group/Fixture/Channel search filter - + Rename the selected items 選択したアイテムをリネームします - + Rename items リネーム - + Inspect the selected item 選択したフィクスチャーの設定 - + Toggle fixtures and channels properties フィクスチャーのチャンネル設定 - + Add/Remove a linked fixture 同じチャンネルでフィクスチャーを追加/削除 - + Name 名前 - + + Mode + モード + + + Flags フラグ - + Can fade フェード - + Behaviour 動作 - + Modifier チャンネル @@ -1333,67 +1437,85 @@ Changes will be lost if you don't save them. FixtureManager - - - + + + Head ヘッド - + New group %1 新しいグループ %1 - + %1 - Row %2 %1 #%2 - + New filters %1 新しいカラーフィルタ %1 + + FixtureNodeDelegate + + + Show/Hide this fixture + + + + + Invert Pan + + + + + Invert Tilt + + + FixtureProperties - + Fixture properties フィクスチャーの設定 - + Name 名前 - + Universe ユニバース - + Address アドレス - + Quantity - + Channels チャンネル - + Gap 間隔 - + Mode モード @@ -1625,67 +1747,67 @@ Changes will be lost if you don't save them. ファンクションの絞り込み - + <None> <None> - + New Scene 新しいシーン - + New Chaser 新しいチェイス - + New Sequence 新しいシーケンス - + New EFX 新しいEFX - + New Collection 新しいコレクション - + New RGB Matrix 新しいRGBスクリプト - + New Script 新しいスクリプト - + New Show 新しいタイムライン - + New Audio 新しいオーディオ - + New Video 新しいビデオ - + (Copy) (コピー) - + New folder 新しいフォルダ @@ -1749,31 +1871,31 @@ Changes will be lost if you don't save them. InputOutputManager - + Input/Output Manager 入出力設定 - + All universes 全てのユニバース - - - - - + + + + + Default device デフォルト - + Disabled 停止 - + Internal generator 内部ジェネレータ @@ -1786,10 +1908,123 @@ Changes will be lost if you don't save them. 入力プロファイルの削除 + + InputProfileEditor + + + + + !! Warning !! + + + + + Channel wizard activated + + + + + You have enabled the input channel wizard. After clicking OK, wiggle your mapped input profile's controls. They should appear into the list. Click the wizard button again to stop channel auto-detection.<br><br>Note that the wizard cannot tell the difference between a knob and a slider so you will have to do the change manually. + + + + + Unsaved changes + + + + + Do you wish to save the current profile first? +Changes will be lost if you don't save them. + + + + + Manufacturer + メーカー名 + + + + Model + + + + + + Type + + + + + MIDI Global Settings + + + + + When MIDI notes are used, send a Note Off when value is 0 + + + + + Channel + チャンネル + + + + Name + 名前 + + + + + Behaviour + 動作 + + + + Generate an extra Press/Release when toggled + + + + + Movement + + + + + Sensitivity + + + + + Custom Feedback + + + + + Lower value + + + + + Upper value + + + + + Button %1 + ボタン %1 + + + + Slider %1 + フェダー %1 + + IntensityTool - + Intensity 光量 @@ -1810,17 +2045,17 @@ Changes will be lost if you don't save them. モード - + Combination ショートカット - + Activate auto detection キー入力の自動検知 - + Remove this keyboard combination ショートカットの削除 @@ -1833,62 +2068,62 @@ Changes will be lost if you don't save them. フィクスチャーの追加 - + Fixture Groups フィクスチャーグループ - + Palettes - + Intensity 光量 - + Shutter シャッター - + Position ポジション - + Color - + Color Wheel カラーホイール - + Gobos ゴボ - + Beam ビーム - + Pick a 3D point クリック位置に向ける - + Toggle multiple item selection 複数選択 - + Select/Deselect all fixtures 全選択 @@ -1896,40 +2131,45 @@ Changes will be lost if you don't save them. MainView - + Actions メニュー - + Fixtures & Functions フィクスチャー・ファンクション - + Virtual Console バーチャルコンソール - + Simple Desk シンプル卓 - + Show Manager タイムライン - + Input/Output 入出力設定 - + Off Off + + + Stop all the running functions + + MainView2D @@ -1942,27 +2182,27 @@ Changes will be lost if you don't save them. MainView3D - + 3D View 3D ビュー - + Simple ground - + Simple box - + Rock stage ロックステージ - + Theatre stage 劇場 @@ -1978,38 +2218,58 @@ Changes will be lost if you don't save them. ModeEditor - + Name 名前 - - + + Channels チャンネル - + + Create a new emitter + + + + + Remove the selected channel(s) + + + + + Drop channels here + + + + Acts on - - Heads + + Emitters - + + Remove the selected emitter(s) + + + + Physical - + Use global settings - + Override global settings @@ -2030,97 +2290,92 @@ Changes will be lost if you don't save them. PaletteFanningBox - - + Flat - - + Linear - - + Square - - + Saw - - + Sine - - - Left to right + + Show/Hide fanning options - - - Right to left + + Create a new palette - - - Top to bottom + + Type - - - Bottom to top + + Layout - - - Centered + + X Ascending - - Show/Hide fanning options + + X Descending - - Create a new palette + + Y Ascending - - Type + + Y Descending - - Layout + + Z Ascending - + + Z Descending + + + + Amount - + Value - + Pick the selected color @@ -2153,12 +2408,12 @@ Changes will be lost if you don't save them. - + Are you sure you want to delete the following items? 選択したアイテムを削除しますか? - + Delete items 削除 @@ -2172,8 +2427,8 @@ Changes will be lost if you don't save them. - - + + Type @@ -2183,83 +2438,83 @@ Changes will be lost if you don't save them. - + Colour Temp (K) - + Lens レンズ - + Min Degrees - + Max Degrees - + Head(s) - + Pan Max Degrees - + Tilt Max Degrees - + Layout (Columns x Rows) - + Dimensions - + Weight 重さ - + Width - + Height 高さ - + Depth 奥行き - + Electrical - + Power Consumption - + DMX Connector @@ -2272,85 +2527,261 @@ Changes will be lost if you don't save them. QLC+について - - and contributors - 開発者 + + and contributors + 開発者 + + + + Website + ウェブサイト + + + + This application is licensed under the terms of the + This application is licensed under the terms of the + + + + Apache 2.0 license + Apache 2.0 license + + + + PopupChannelModifiers + + + Channel Modifiers Editor + + + + + Insert a modified value after the selected + + + + + Delete the selected modifier value + + + + + Rename the selected modifier template + + + + + Save the selected modifier template + + + + + Templates + + + + + Original DMX value + + + + + Modified DMX value + + + + + PopupChannelWizard + + + Fixture Editor Wizard + + + + + Properties + + + + + Start + + + + + Width + + + + + Amount + + + + + Type + + + + + Red + + + + + Green + + + + + Blue + + + + + White + + + + + Amber + + + + + UV + UV + + + + RGB + + + + + RGBW + + + + + RGBAW + + + + + Dimmer + + + + + Pan + パン + + + + Tilt + チルト + + + + Color Macro + + + + + Shutter + シャッター + + + + Beam + ビーム + + + + Effect + エフェクト + + + + Label + ラベル - - Website - ウェブサイト + + Capability # + - - This application is licensed under the terms of the - This application is licensed under the terms of the + + Channel # + - - Apache 2.0 license - Apache 2.0 license + + Preview + PopupCreatePalette - + Create a new palette - + Dimmer - + Color - + Position - + Shutter シャッター - + Gobo ゴボ - + Palette name - + New Palette - + Type - + Also create a Scene - + Scene name シーン名 - + New Scene 新しいシーン @@ -2358,87 +2789,87 @@ Changes will be lost if you don't save them. PopupDMXDump - + Enter a name for the scene シーン名を決めてください - + Scene name シーン名 - + New Scene 新しいシーン - + Don't ask again 次回から表示しない - + Available channel types 有効なチャンネルタイプ - + Intensity 光量 - + RGB/CMY/WAUV RGB/CMY/WAUV - + Color macros カラーマクロ - + Gobo ゴボ - + Pan パン - + Tilt チルト - + Speed スピード - + Shutter/Strobe シャッター/ストロボ - + Prism プリズム - + Beam ビーム - + Effect エフェクト - + Maintenance メンテナンス @@ -2446,7 +2877,7 @@ Changes will be lost if you don't save them. PopupDisclaimer - + Disclaimer 免責条項 @@ -2469,6 +2900,54 @@ Changes will be lost if you don't save them. ファンクション + + PopupInputChannelEditor + + + Input Channel Editor + + + + + Input Channel + + + + + Number + + + + + Name + 名前 + + + + Type + + + + + Channel + チャンネル + + + + Message + + + + + Parameter + + + + + Note + メモ + + PopupManualInputSource @@ -2533,27 +3012,27 @@ Changes will be lost if you don't save them. PopupNetworkClient - + Disconnected 未接続 - + Waiting for access アクセスを待っています - + Downloading project プロジェクトデータを転送中です - + Connected 接続 - + QLC+ client setup QLC+ クライアント設定 @@ -2596,7 +3075,7 @@ Changes will be lost if you don't save them. PopupNetworkConnect - + Client access request クライアントが接続要求をしています @@ -2727,12 +3206,12 @@ Access level: PopupPINRequest - + Page PIN パスワード - + Remember for this session セッションの削除 @@ -2740,22 +3219,22 @@ Access level: PopupPINSetup - + Current PIN 現在のパスワード - + New PIN 新しいパスワード - + Confirm PIN もう一度入力してください - + New PIN mismatch 2つの入力が一致しません @@ -2763,22 +3242,22 @@ Access level: PopupRenameItems - + New name 新しい名前 - + Enable numbering 連番 - + Start number 開始番号 - + Digits @@ -2786,28 +3265,76 @@ Access level: PositionTool - + Position ポジション - + Rotate 90° clockwise 90°回転 - - + + Snap to the previous value 戻る - - + + Snap to the next value 進む + + ProfilesList + + + !! Warning !! + + + + + Save this profile + + + + + Toggle the automatic detection procedure + + + + + Add a new channel + + + + + Create a new input profile + + + + + Edit the selected channel + + + + + Edit the selected input profile + + + + + Delete the selected channel + + + + + Delete the selected input profile(s) + + + RGBMatrixEditor @@ -2821,214 +3348,214 @@ Access level: パターン - + Blend mode ブラインドモード - + Default (HTP) デフォルト(HTP) - + Mask マスクモード - + Additive 加算 - + Subtractive 減算 - + Color mode - + Default (RGB) - + White - + Amber - + UV UV - + Dimmer - + Shutter シャッター - + Colors - + Parameters パラメータ - + Speed スピード - + Steps fade in ステップフェードイン - + Steps hold ウェイトタイム - + Steps fade out ステップフェードアウト - + Tempo type テンポタイプ - + Time 時間 - + Beats ビート - + Order and direction 再生設定 - + Loop ループ - + Single Shot 一回再生 - + Ping Pong 往復 - + Run Order 再生方向 - + Forward 順方向 - + Backward 逆方向 - + Direction 方向 - + Text テキスト - + Please choose a font フォント - - - + + + Animation アニメーション - + Letters 1文字づつ - - + + Horizontal 横スクロール - - + + Vertical 縦スクロール - - + + Offset オフセット - - + + X X - - + + Y Y - + Image 画像 - + Select an image 画像の選択 - + Static 固定 @@ -3036,17 +3563,17 @@ Access level: RGBPanelProperties - + RGB panel properties RGBパネル設定 - + Name 名前 - + Universe ユニバース @@ -3206,7 +3733,7 @@ Access level: スナップショット - + Function Preview プレビュー @@ -3219,43 +3746,43 @@ Access level: SceneEditor - + Add a fixture/group - + Add a palette - + Remove the selected items Remove the selected fixtures 選択したフィクスチャーを削除します - + Delete items 削除 - + Are you sure you want to remove the selected items? - + Speed スピード - + Fade in フェードイン - + Fade out フェードアウト @@ -3263,67 +3790,67 @@ Access level: ScriptEditor - + Add a method call at cursor position カーソル位置に関数を追加します - + Show/hide functions tree ファンクションツリーの表示/非表示 - + Show/hide fixture tree フィクスチャーツリーの表示/非表示 - + Check the script syntax シンタックスチェック - + Syntax check シンタックスチェック - + Start function ファンクションの再生 - + Stop function ファンクションの停止 - + Set fixture channel チャンネル出力 - + Wait time ウェイト - + Random number 乱数 - + Blackout ブラックアウト - + System command システムコマンド - + File path ファイルパス @@ -3351,12 +3878,12 @@ Access level: 選択したフィクスチャーを削除します - + Steps ステップ - + Fixtures フィクスチャー @@ -3364,107 +3891,122 @@ Access level: SettingsView2D - + Environment 設定 - + Width - + Height 高さ - + Depth 奥行き - + Grid units グリッド単位 - + Meters メートル - + Feet フィート - + Point of view 視点切り替え - + Top view Topビュー - + Front view Frontビュー - + Right side view Rightビュー - + Left side view Leftビュー + Custom Background + + + + + Select an image + + + + + Reset background + + + + Selected fixtures フィクスチャーの選択 - + Gel color - + Rotation 回転 - + Alignment 整列 - + Align the selected items to the left 選択したフィクスチャーを左に揃えます - + Align the selected items to the top 選択したフィクスチャーを上に揃えます - + Distribution 等間隔配置 - + Equally distribute horizontally the selected items 選択したフィクスチャーの横を等間隔で配置します - + Equally distribute vertically the selected items 選択したフィクスチャーの縦を等間隔で配置します @@ -3472,122 +4014,126 @@ Access level: SettingsView3D - + Environment 設定 - + Type ステージ - + Width - + Height 高さ - + Depth 奥行き - + Rendering - + Quality - + Low - + Medium - + High - + Ultra - + Ambient light 会場灯 - + Smoke amount - + Show FPS - + Scale - + Custom items - + Select a mesh file - + 3D files - + All files 全てのファイル - + + Normalize the selected items + + + Actions - メニュー + メニュー - + Add a new item to the scene - + Remove the selected items - + Position 位置 - + Rotation 回転 @@ -3613,16 +4159,16 @@ Access level: ShowItem - - - + + + Position: 位置: - - - + + + Duration: Time: @@ -3640,32 +4186,32 @@ Access level: 背景色 - + Unlock the selected items ロック解除 - + Lock the selected items ロック - + Snap to grid グリッド - + Stretch the original function 元のファンクションの時間を変更する - + Remove the selected items 選択したアイテムを削除 - + Are you sure you want to remove the following items? (Note that the original functions will not be deleted) Are you sure you want to remove the following items ? @@ -3673,42 +4219,42 @@ Access level: 削除しますか?(タイムライン上のアイテムは削除されますが、ファンクションは削除されません) - + Delete show items 削除 - + Copy the selected items in the clipboard 選択したアイテムをクリップボートにコピーします - + Paste items in the clipboard at cursor position カーソル位置にペーストします - + Play or resume 再生/一時停止 - + Stop or rewind 停止/先頭へ戻る - + Move the selected track up 選択したトラックを上に - + Move the selected track down 選択したトラックを下に - + Create a new track 新しいトラックを作成します @@ -3718,19 +4264,19 @@ Access level: タイムライン - + New Show 新しいタイムライン - - + + Track %1 トラック %1 - - + + (Copy) (コピー) @@ -3738,37 +4284,37 @@ Access level: SimpleDesk - + Universe ユニバース - + Reset the whole universe - + Dump on a new Scene スナップショット - + Reset the channel - + Fixture List - + Commands history - + Simple Desk シンプル卓 @@ -3784,25 +4330,202 @@ Access level: TrackDelegate - + Solo this track ソロ - + Mute this track ミュート + + UISettingsEditor + + + + Reset to default + + + + + Scaling factor + + + + + Background darker + + + + + Background dark + + + + + Background medium + + + + + Background light + + + + + Background lighter + + + + + Controls background + + + + + Foreground main + + + + + Foreground medium + + + + + Foreground light + + + + + Toolbar gradient start + + + + + Sub-toolbar gradient start + + + + + Toolbar gradient end + + + + + Toolbar hover gradient start + + + + + Toolbar hover gradient end + + + + + Toolbar selection + + + + + Sub-toolbar selection + + + + + Section header + + + + + Section header divider + + + + + Item highlight + + + + + Item highlight pressed + + + + + Item hover + + + + + Item selection + + + + + VC Frame drop area + + + + + Item dark border + + + + + Save to file + + + + + Operation completed + + + + + Error + エラー + + + + UniverseGridView + + + Error + エラー + + + + Unable to perform the operation. +There is either not enough space or the target universe in invalid + + + + + Cut the selected items into clipboard + + + + + Paste items in the clipboard at the first available position + + + UniverseIOItem - Passthrough - パススルー + パススルー + + + + Enable/Disable passthrough + - + Enable/Disable feedbacks フィードバックのOn/Off @@ -4014,74 +4737,74 @@ Access level: ファンクションをスケジューラーに追加 - + Remove this schedule 削除 - + Start time 開始時間 - + Stop time 停止時間 - + Enable the stop time 停止時間の有効化 - + M As in Monday 月曜 - + T As in Tuesday 火曜日 - + W As in Wednesday 水曜日 - + T As in Thursday 木曜日 - + F As in Friday 金曜日 - + S As in Saturday 土曜日 - + S As in Sunday 日曜日 - + Repeat weekly 毎週 - + Add a new schedule スケジュールの追加 @@ -4089,17 +4812,17 @@ Access level: VCCueList - + Next Cue 新しいキュー - + Previous Cue 前のキュー - + Play/Stop/Pause 再生/停止/一時停止 @@ -4112,17 +4835,17 @@ Access level: クロスフェード(右) - + Stop/Pause 停止/一時停止 - + Side Fader - + Cue List %1 キューリスト %1 @@ -4130,27 +4853,32 @@ Access level: VCCueListItem - + Play/Pause 再生/一時停止 - + + Play/Stop + + + + Pause 一時停止 - + Stop 停止 - + Previous cue 前のキュー - + Next cue 次のキュー @@ -4242,30 +4970,35 @@ Access level: VCFrame - + Next Page 次のページ - + Previous Page 前のページ - + Enable 有効化 - + Collapse しまう - + Frame %1 フレーム %1 + + + Page %1 + ページ %1 + VCFrameItem @@ -4280,17 +5013,16 @@ Access level: 有効化/無効化 - + Previous page 前のページ - Page - ページ + ページ - + Next page 次のページ @@ -4333,10 +5065,21 @@ Access level: ページ番号 - + Clone first page widgets 最初のページにクローンする + + + + Shortcuts + + + + + Shortcut name + + VCLabel @@ -4349,12 +5092,12 @@ Access level: VCPage - + Page %1 ページ %1 - + Virtual Console Page %1 バーチャルコンソールページ %1 @@ -4362,57 +5105,57 @@ Access level: VCPageProperties - + Width - + Height 高さ - + Security セキュリティ - + Set a PIN パスワードを設定 - + Error エラー - + The entered PINs are either invalid or incorrect パスフレーズが一致しません - + Add page to the left 左にページを追加 - + Add page to the right 右にページを追加 - + Delete this page ページを削除 - + Delete page ページを削除 - + Are you sure you want to delete the selected page? Are you sure you want to delete the selected page ? 選択したページを削除しますか? @@ -4479,12 +5222,12 @@ Access level: リセット - + Slider %1 フェダー %1 - + Knob %1 ツマミ %1 @@ -4557,82 +5300,82 @@ Access level: 属性 - + Level mode レベルモード - + Channels チャンネル - + Add/Remove channels チャンネルの編集 - + Click & Go button Click & Goボタン - + None なし - + RGB/CMY RGB/CMY - + Gobo/Effect/Macro Gobo/Effect/Macro - + Monitor channel levels モニターチャンネルレベル - + Values range 範囲指定 - + Upper limit 上限 - + Lower limit 下限 - + Grand Master mode グランドマスターモード - + Reduce values 値を戻す - + Limit values 制限値 - + Intensity channels 光量チャンネル - + All channels 全てのチャンネル @@ -4709,8 +5452,8 @@ Access level: Unknown - - + + None なし @@ -4718,87 +5461,87 @@ Access level: VCWidgetProperties - + Select a widget first ウィジェットを選択してください - + Settings - + External controls - + Basic properties 基本設定 - + Label ラベル - + Background color 背景色 - + Foreground color 文字色 - + Font ほふx - + Please choose a font フォント - + Background image 背景画像 - + Select an image 画像を選択 - + Alignment 位置揃え - + Align the selected widgets to the left 左に揃える - + Align the selected widgets to the right 右に揃える - + Align the selected widgets to the top 上に揃える - + Align the selected widgets to the bottom 下に揃える - + External Controls 外部入力 @@ -4876,77 +5619,77 @@ Access level: 出力画面 - + Output mode 再生モード - + Windowed ウィンドウ - + Fullscreen フルスクリーン - + Geometry 変形 - + Original ソース元に合わせる - + Custom カスタム - + Position ポジション - + Size サイズ - + W - + H 高さ - + Rotation 回転 - + X X - + Y Y - + Z Z - + Layer @@ -4954,72 +5697,72 @@ Access level: VirtualConsole - + Error エラー - + Invalid PIN entered パスワードが一致しません - + Enable/Disable widgets snapping グリッド - + Widget matrix setup グリッド設定 - + Columns - + Rows - + Width - + Height 高さ - + Frame type フレームタイプ - + Normal ノーマル - + Solo ソロ - + Virtual Console バーチャルコンソール - + <None> <なし> - + Page %1 ページ %1 diff --git a/qmlui/qlcplus_nl_NL.ts b/qmlui/qlcplus_nl_NL.ts index 9b5df019f7..3c9c33addd 100644 --- a/qmlui/qlcplus_nl_NL.ts +++ b/qmlui/qlcplus_nl_NL.ts @@ -4,53 +4,57 @@ ActionsMenu - Open a project - Project openen + Project openen - - + + Project files Project bestanden - - - + + + All files Alle bestanden - + + Open a file + + + + QLC+ files - - + + Import from project Importeer van project - - + + Save project as... Project opslaan als... - + Your project has changes Je project bevat wijzigingen - + Do you wish to save the current project first? Changes will be lost if you don't save them. Wil je het huidig project eerst opslaan? Wijzigingen gaan verloren als je ze niet eerst opslaat - + New project Nieuw project @@ -59,117 +63,122 @@ Changes will be lost if you don't save them. Project openen - + Open file - + Save project Project bewaren - + Undo Ongedaan maken - + Redo Opnieuw uitvoeren - + Network Netwerk - + Server setup Server configuratie - + Client setup Client configuratie - + Address tool Adres tool - + DMX Address tool DMX Adres tool - + + UI Settings + + + + Toggle fullscreen Volledig scherm - + Language Taal - + Catalan Catalaans - + Dutch Nederlands - + English Engels - + French Frans - + German Duits - + Italian Italiaans - + Japanese Japans - + Polish - + Russian - + Spanish Spaans - + Ukrainian - + About Over @@ -290,12 +299,17 @@ Changes will be lost if you don't save them. Uitvoer apparaat - + + Volume + + + + Fade in Fade in - + Fade out Fade out @@ -311,20 +325,25 @@ Changes will be lost if you don't save them. BeamTool - + Beam Lichtstraal - + Beam degrees Hoek lichtstraal - + Distance Afstand + + + Projected diameter + + BottomPanel @@ -337,8 +356,8 @@ Changes will be lost if you don't save them. ChannelEdit - - + + Custom Aangepast @@ -346,88 +365,118 @@ Changes will be lost if you don't save them. ChannelEditor - + + Open a picture file + + + + + Gobo pictures + + + + + All files + Alle bestanden + + + Name Naam - + Preset - - + + Type Type - + Role - + Coarse (MSB) - + Fine (LSB) - + Default value - + Delete the selected capabilities - + + Capability wizard + + + + + Empty description provided + + + + + Overlapping with another capability + + + + From - + To - + Description - + Preview - + Primary color - + Secondary color - + Value(s) - + Value 1 - + Value 2 @@ -435,122 +484,137 @@ Changes will be lost if you don't save them. ChaserEditor - + Add a new step Voeg een nieuwe stap toe - + Remove the selected steps Verwijder de geselecteerde stap - + Delete steps Verwijder stappen - + Are you sure you want to remove the selected steps? Weet je zeker dat je de geselecteerde stap wilt verwijderen? - + + Preview the previous step + + + + + Preview the next step + + + + + Duplicate the selected step(s) + + + + Print the Chaser steps - + Run properties Uitvoer eigenschappen - + Loop Herhalen - + Single Shot Enkel Shot - + Ping Pong Ping Pong - + Random Willekeurig - + Run Order Volgorde van uitvoeren - + Forward Volgende - + Backward Terug - + Direction Richting - + Time Tijd - + Beats Slagen - + Tempo Tempo - - + + Default Standaard - - - + + + Common Algemeen - - - + + + Per Step Per Stap - + Fade In Fade In - + Fade Out Fade Out - + Duration Duur @@ -558,32 +622,32 @@ Changes will be lost if you don't save them. ChaserWidget - + Function Functie - + Fade In Fade In - + Hold Vastzetten - + Fade Out Fade Out - + Duration Duur - + Note Noot @@ -591,22 +655,22 @@ Changes will be lost if you don't save them. CollectionEditor - + Add a function Voeg functie toe - + Remove the selected function Verwijder geselecteerde functie - + Delete functions Verwijder functies - + Are you sure you want to remove the selected functions? Weet je zeker dat je de geselecteerde functie wilt verwijderen? @@ -614,17 +678,17 @@ Changes will be lost if you don't save them. ColorTool - + Basic Basis - + Full Volledig - + Filters Filters @@ -632,7 +696,7 @@ Changes will be lost if you don't save them. ColorToolBasic - + Selected color Geselecteerde kleur @@ -640,87 +704,87 @@ Changes will be lost if you don't save them. ColorToolFilters - + Open filters menu Open filter menu - + Cyan Cyaan - + Red Rood - + White Wit - + Magenta Magenta - + Green Groen - + Amber Amber - + Yellow Geel - + Blue Blauw - + UV UV - + CMY CMG - + Add a new color filters file Voeg een nieuw kleur filter bestand toe - + Rename the current color filters file Hernoem het huidig kleur filter bestand - + Save the current color filters file Bewaar het huidig kleur filter bestand - + Add a new filter Voeg een nieuw filter toe - + Delete the selected filter Verwijder het geselecteerde filter - + Paste the latest picked color as new filter Plak de laatste geselecteerde kleur als een nieuwe filter @@ -728,37 +792,37 @@ Changes will be lost if you don't save them. ColorToolFull - + Red Rood - + Green Groen - + Blue Blauw - + White Wit - + Amber Amber - + UV UV - + Selected color Geselecteerde kleur @@ -766,12 +830,12 @@ Changes will be lost if you don't save them. ContextManager - + Universe Grid View Universele Grid weergave - + linked Gelinkt @@ -802,154 +866,174 @@ Changes will be lost if you don't save them. EFXEditor - + Fixtures Apparaten - + Add a fixture/head Voeg een apparaat toe - + Remove the selected fixture head(s) Verwijder geselecteerd(e) appara(a)t(en) - + Fixture Apparaat - + + Mode + Mode + + + Reverse Inverteer - - + + Start offset Start Offset - + + Position + Positie + + + + Dimmer + + + + + RGB + + + + Add a new fixture Voeg een nieuw appraat toe - - + + Pattern Patroon - + Relative movement Relatieve beweging - + Width Breedte - + Height Hoogte - + X offset X offset - + Y offset Y offset - + Rotation Rotatie - + X frequency X frequentie - + Y frequency Y frequentie - + X phase X fase - + Y phase Y fase - + Speed Snelheid - + Fade in Fade in - Hold - Vastzetten + Vastzetten - + Fade out Fade out - + Order and direction Volgorde en richting - + + Loop Herhalen - + Single Shot Enkel Shot - + Ping Pong Ping Pong - + Run Order Volgorde - + Forward Vooruit - + Backward Achteruit - + Direction Richting @@ -957,7 +1041,7 @@ Changes will be lost if you don't save them. EditorTopBar - + Go back to the previous view Go back to the Function Manager Terug naar Functie beheer @@ -976,77 +1060,82 @@ Changes will be lost if you don't save them. - + General - + Manufacturer Fabrikant - + Type Type - + Model - + Author Auteur - + Physical properties - + Channels Kanalen - + Add a new channel - + Remove the selected channel(s) - + + Channel wizard + + + + Modes - + Add a new mode - + Remove the selected mode(s) - + Aliases - + New channel %1 - + New mode @@ -1059,37 +1148,37 @@ Changes will be lost if you don't save them. Bediening - + Universe Universum - + Activate auto detection Activeer auto detectie - + Channel Kanaal - + Remove this input source Verwijder deze invoer bron - + Custom feedbacks Aangepaste terugkoppeling - + Lower Lager - + Upper Hoger @@ -1115,13 +1204,13 @@ Changes will be lost if you don't save them. FixtureBrowser - + Create a new fixture definition Add a new fixture definition - + Edit the selected fixture definition @@ -1129,22 +1218,22 @@ Changes will be lost if you don't save them. FixtureChannelDelegate - + Auto (HTP) Auto (HTP) - + Auto (LTP) Auto (LTP) - + Forced HTP HTP geforceerd - + Forced LTP LTP geforceerd @@ -1175,7 +1264,7 @@ Changes will be lost if you don't save them. - + Save definition as... @@ -1185,27 +1274,32 @@ Changes will be lost if you don't save them. Fout - + + Warning + + + + Back to QLC+ - + New definition - + Open definition - + Save definition - + Unknown Onbekend @@ -1228,22 +1322,22 @@ Changes will be lost if you don't save them. - + Reset the entire group Reset de volledige groep - + Rotate 90° clockwise - + Rotate 180° clockwise - + Rotate 270° clockwise @@ -1260,12 +1354,12 @@ Changes will be lost if you don't save them. Roteer 270Р(kloksgewijs) - + Flip horizontally Flip horizontaal - + Flip vertically FLip verticaal @@ -1273,67 +1367,77 @@ Changes will be lost if you don't save them. FixtureGroupManager - + + Error + Fout + + + Add a new fixture group Voeg een nieuwe groep apparaten toe - + Remove the selected items Verwijder de geselecteerde items - + Set a Group/Fixture/Channel search filter Maak een Groep/Apparaat/Kanaal zoek filter aan - + Rename the selected items Hernoem de geselecteerde items - + Rename items Hernoem items - + Inspect the selected item Inspecteer het geselecteerde item - + Toggle fixtures and channels properties Wijzig Apparaten en kanaal eigenschappen - + Add/Remove a linked fixture Voeg toe/verwijder gelinkte apparaten - + Name Naam - + + Mode + Mode + + + Flags Flags - + Can fade Kan dimmen - + Behaviour Gedrag - + Modifier Wijziger @@ -1341,67 +1445,85 @@ Changes will be lost if you don't save them. FixtureManager - - - + + + Head Hoofd - + New group %1 Nieuwe groep %1 - + %1 - Row %2 %1 - Rij %2 - + New filters %1 Nieuwe filters %1 + + FixtureNodeDelegate + + + Show/Hide this fixture + + + + + Invert Pan + + + + + Invert Tilt + + + FixtureProperties - + Fixture properties Eigenschappen van apparaat - + Name Naam - + Universe Universum - + Address Adres - + Quantity Aantal - + Channels Kanalen - + Gap Gat - + Mode Mode @@ -1633,67 +1755,67 @@ Changes will be lost if you don't save them. Maak een functie zoekfilter - + <None> <Geen> - + New Scene Nieuwe sc鯥 - + New Chaser Nieuwe chaser - + New Sequence Nieuwe sequentie - + New EFX Nieuw EFX - + New Collection Nieuwe collectie - + New RGB Matrix Nieuwe RGB Matrix - + New Script Nieuw script - + New Show Nieuwe show - + New Audio Nieuwe audio - + New Video Nieuwe video - + (Copy) (Kopie) - + New folder Nieuwe map @@ -1757,31 +1879,31 @@ Changes will be lost if you don't save them. InputOutputManager - + Input/Output Manager Beheer Invoer/Uitvoer - + All universes Alle universums - - - - - + + + + + Default device Standaard apparaat - + Disabled Uitgeschakeld - + Internal generator Interne generator @@ -1794,10 +1916,123 @@ Changes will be lost if you don't save them. Verwijder dit invoer profiel + + InputProfileEditor + + + + + !! Warning !! + + + + + Channel wizard activated + + + + + You have enabled the input channel wizard. After clicking OK, wiggle your mapped input profile's controls. They should appear into the list. Click the wizard button again to stop channel auto-detection.<br><br>Note that the wizard cannot tell the difference between a knob and a slider so you will have to do the change manually. + + + + + Unsaved changes + + + + + Do you wish to save the current profile first? +Changes will be lost if you don't save them. + + + + + Manufacturer + Fabrikant + + + + Model + + + + + + Type + Type + + + + MIDI Global Settings + + + + + When MIDI notes are used, send a Note Off when value is 0 + + + + + Channel + Kanaal + + + + Name + Naam + + + + + Behaviour + Gedrag + + + + Generate an extra Press/Release when toggled + + + + + Movement + + + + + Sensitivity + + + + + Custom Feedback + + + + + Lower value + + + + + Upper value + + + + + Button %1 + Knop %1 + + + + Slider %1 + Fader %1 + + IntensityTool - + Intensity Intensiteit @@ -1818,17 +2053,17 @@ Changes will be lost if you don't save them. Bediening - + Combination Combinatie - + Activate auto detection Activeer auto detectie - + Remove this keyboard combination Verwijder deze toetsenbord combinatie @@ -1841,62 +2076,62 @@ Changes will be lost if you don't save them. Voeg apparaat toe - + Fixture Groups Apparaat groepen - + Palettes - + Intensity Intensiteit - + Shutter Shutter - + Position Positie - + Color Kleur - + Color Wheel Kleur wiel - + Gobos Gobos - + Beam Lichtstraal - + Pick a 3D point Kies een 3D punt - + Toggle multiple item selection Selecteer meerdere items - + Select/Deselect all fixtures Selecteer/Deselecteer alle apparaten @@ -1904,40 +2139,45 @@ Changes will be lost if you don't save them. MainView - + Actions Acties - + Fixtures & Functions Apparaten & Functies - + Virtual Console Virtuele Console - + Simple Desk Simpele console - + Show Manager Show beheren - + Input/Output Invoer/Uitvoer - + Off Uit + + + Stop all the running functions + + MainView2D @@ -1950,27 +2190,27 @@ Changes will be lost if you don't save them. MainView3D - + 3D View 3D weergave - + Simple ground Eenvoudige vloer - + Simple box Eenvoduige box - + Rock stage Rock stage - + Theatre stage Theater stage @@ -1986,38 +2226,58 @@ Changes will be lost if you don't save them. ModeEditor - + Name Naam - - + + Channels Kanalen - + + Create a new emitter + + + + + Remove the selected channel(s) + + + + + Drop channels here + + + + Acts on - - Heads + + Emitters + + + + + Remove the selected emitter(s) - + Physical Fysiek - + Use global settings - + Override global settings @@ -2038,97 +2298,92 @@ Changes will be lost if you don't save them. PaletteFanningBox - - + Flat - - + Linear - - + Square - - + Saw - - + Sine - - - Left to right + + Show/Hide fanning options - - - Right to left + + Create a new palette - - - Top to bottom + + Type + Type + + + + Layout - - - Bottom to top + + X Ascending - - - Centered + + X Descending - - Show/Hide fanning options + + Y Ascending - - Create a new palette + + Y Descending - - Type - Type + + Z Ascending + - - Layout + + Z Descending - + Amount - + Value - + Pick the selected color @@ -2161,12 +2416,12 @@ Changes will be lost if you don't save them. - + Are you sure you want to delete the following items? Weet je zeker dat je de volgende items wilt verwijderen? - + Delete items Verwijder items @@ -2180,8 +2435,8 @@ Changes will be lost if you don't save them. - - + + Type Type @@ -2191,83 +2446,83 @@ Changes will be lost if you don't save them. - + Colour Temp (K) - + Lens Lens - + Min Degrees - + Max Degrees - + Head(s) - + Pan Max Degrees - + Tilt Max Degrees - + Layout (Columns x Rows) - + Dimensions - + Weight Gewicht - + Width Breedte - + Height Hoogte - + Depth Diepte - + Electrical - + Power Consumption - + DMX Connector @@ -2280,85 +2535,261 @@ Changes will be lost if you don't save them. Informatie - - and contributors - en medewerkers + + and contributors + en medewerkers + + + + Website + Website + + + + This application is licensed under the terms of the + Deze applicatie is gelicentieerd onder de bepalingen van + + + + Apache 2.0 license + Apache 2.0 license + + + + PopupChannelModifiers + + + Channel Modifiers Editor + + + + + Insert a modified value after the selected + + + + + Delete the selected modifier value + + + + + Rename the selected modifier template + + + + + Save the selected modifier template + + + + + Templates + + + + + Original DMX value + + + + + Modified DMX value + + + + + PopupChannelWizard + + + Fixture Editor Wizard + + + + + Properties + + + + + Start + + + + + Width + Breedte + + + + Amount + + + + + Type + Type + + + + Red + Rood + + + + Green + Groen + + + + Blue + Blauw + + + + White + Wit + + + + Amber + Amber + + + + UV + UV + + + + RGB + + + + + RGBW + + + + + RGBAW + + + + + Dimmer + + + + + Pan + Pan + + + + Tilt + Tilt + + + + Color Macro + + + + + Shutter + Shutter + + + + Beam + Lichtstraal + + + + Effect + Effect + + + + Label + Label - - Website - Website + + Capability # + - - This application is licensed under the terms of the - Deze applicatie is gelicentieerd onder de bepalingen van + + Channel # + - - Apache 2.0 license - Apache 2.0 license + + Preview + PopupCreatePalette - + Create a new palette - + Dimmer - + Color Kleur - + Position Positie - + Shutter Shutter - + Gobo Gobo - + Palette name - + New Palette - + Type Type - + Also create a Scene - + Scene name Sc鯥 naam - + New Scene Nieuwe sc鯥 @@ -2366,87 +2797,87 @@ Changes will be lost if you don't save them. PopupDMXDump - + Enter a name for the scene Geef een naam in voor deze sc鯥 - + Scene name Sc鯥 naam - + New Scene Nieuwe sc鯥 - + Don't ask again Niet meer opnieuw vragen - + Available channel types Beschikbare kanaal types - + Intensity Intensiteit - + RGB/CMY/WAUV RGB/CMJ/WAUV - + Color macros Kleur macros - + Gobo Gobo - + Pan Pan - + Tilt Tilt - + Speed Snelheid - + Shutter/Strobe Shutter/Strobe - + Prism Prisma - + Beam Lichtstraal - + Effect Effect - + Maintenance Onderhoud @@ -2454,7 +2885,7 @@ Changes will be lost if you don't save them. PopupDisclaimer - + Disclaimer Disclaimer @@ -2477,6 +2908,54 @@ Changes will be lost if you don't save them. Foncties + + PopupInputChannelEditor + + + Input Channel Editor + + + + + Input Channel + + + + + Number + Nummer + + + + Name + Naam + + + + Type + Type + + + + Channel + Kanaal + + + + Message + + + + + Parameter + + + + + Note + Noot + + PopupManualInputSource @@ -2541,27 +3020,27 @@ Changes will be lost if you don't save them. PopupNetworkClient - + Disconnected Niet verbonden - + Waiting for access Wachten op toegang - + Downloading project Project downloaden - + Connected Verbonden - + QLC+ client setup QLC+ client installatie @@ -2604,7 +3083,7 @@ Changes will be lost if you don't save them. PopupNetworkConnect - + Client access request Client toegang vragen @@ -2733,12 +3212,12 @@ vraagt toegang tot deze sessie. Toegangsniveau: PopupPINRequest - + Page PIN PIN pagina - + Remember for this session Onthouden voor deze sessie @@ -2746,22 +3225,22 @@ vraagt toegang tot deze sessie. Toegangsniveau: PopupPINSetup - + Current PIN Huidige PIN - + New PIN Nieuwe PIN - + Confirm PIN Bevestig PIN - + New PIN mismatch Nieuwe pin komt niet overeen @@ -2769,22 +3248,22 @@ vraagt toegang tot deze sessie. Toegangsniveau: PopupRenameItems - + New name Nieuwe naam - + Enable numbering Nummering inschakelen - + Start number Start nummer - + Digits Cijfers @@ -2792,7 +3271,7 @@ vraagt toegang tot deze sessie. Toegangsniveau: PositionTool - + Position Positie @@ -2801,23 +3280,71 @@ vraagt toegang tot deze sessie. Toegangsniveau: Roteer 90Р(kloksgewijs) - + Rotate 90° clockwise - - + + Snap to the previous value Gebruik vorige waarde - - + + Snap to the next value Gebruik volgende waarde + + ProfilesList + + + !! Warning !! + + + + + Save this profile + + + + + Toggle the automatic detection procedure + + + + + Add a new channel + + + + + Create a new input profile + + + + + Edit the selected channel + + + + + Edit the selected input profile + + + + + Delete the selected channel + + + + + Delete the selected input profile(s) + + + RGBMatrixEditor @@ -2831,214 +3358,214 @@ vraagt toegang tot deze sessie. Toegangsniveau: Patroon - + Blend mode Mix mode - + Default (HTP) Standaard (HTP) - + Mask Masker - + Additive Additief - + Subtractive Aftrekken - + Color mode - + Default (RGB) - + White Wit - + Amber Amber - + UV UV - + Dimmer - + Shutter Shutter - + Colors Kleuren - + Parameters Parameters - + Speed Snelheid - + Steps fade in Fade in stappen - + Steps hold Stappen vasthouden - + Steps fade out Fade out stappen - + Tempo type Tempo type - + Time Tijd - + Beats Beats - + Order and direction Volgorde en richting - + Loop Herhalen - + Single Shot Enkel Shot - + Ping Pong Ping Pong - + Run Order Uitvoer volgorde - + Forward Voorwaarts - + Backward Achterwaarts - + Direction Richting - + Text Tekst - + Please choose a font Gelieve een lettertype te kiezen - - - + + + Animation Animatie - + Letters Letters - - + + Horizontal Horizontaal - - + + Vertical Verticaal - - + + Offset Offset - - + + X X - - + + Y Y - + Image Afbeelding - + Select an image Sꭥcteer een afbeelding - + Static Statisch @@ -3046,17 +3573,17 @@ vraagt toegang tot deze sessie. Toegangsniveau: RGBPanelProperties - + RGB panel properties Eigenschappen RGB paneel - + Name Naam - + Universe Universum @@ -3215,7 +3742,7 @@ vraagt toegang tot deze sessie. Toegangsniveau: Dump een nieuwe scene - + Function Preview Functie preview @@ -3228,43 +3755,43 @@ vraagt toegang tot deze sessie. Toegangsniveau: SceneEditor - + Add a fixture/group - + Add a palette - + Remove the selected items Remove the selected fixtures Verwijder de geselecteerde apparaten - + Delete items Verwijder items - + Are you sure you want to remove the selected items? - + Speed Snelheid - + Fade in Fade in - + Fade out Fade out @@ -3272,67 +3799,67 @@ vraagt toegang tot deze sessie. Toegangsniveau: ScriptEditor - + Add a method call at cursor position Voeg een methode oproep toe op plaats cursor - + Show/hide functions tree Toon/Verberg functie overzicht - + Show/hide fixture tree Toon/Verberg apparaat overzicht - + Check the script syntax Check de script syntax - + Syntax check Check syntax - + Start function Start functie - + Stop function Stop functie - + Set fixture channel Zet apparaat kanaal - + Wait time Wavhttijd - + Random number Willekeurig getal - + Blackout Blackout - + System command Systeem commando - + File path Bestandspad @@ -3360,12 +3887,12 @@ vraagt toegang tot deze sessie. Toegangsniveau: Verwijder de geselecteerde apparaten - + Steps Stappen - + Fixtures Apparaten @@ -3373,107 +3900,122 @@ vraagt toegang tot deze sessie. Toegangsniveau: SettingsView2D - + Environment Omgeving - + Width Breedte - + Height Hoogte - + Depth Diepte - + Grid units Grid eenheden - + Meters Meters - + Feet Voet - + Point of view Weergavepunt - + Top view Top weergave - + Front view Frontale weergave - + Right side view Rechterzijde weergave - + Left side view Linkerzijde weergave + Custom Background + + + + + Select an image + + + + + Reset background + + + + Selected fixtures Geselecteerde apparaten - + Gel color - + Rotation Rotatie - + Alignment Opstelling - + Align the selected items to the left Stel de geselecteerde items op naar links - + Align the selected items to the top Stel de geselecteerde items op naar boven - + Distribution Distributie - + Equally distribute horizontally the selected items Verdeel de geselecteerde items gelijkmatig horizontaal - + Equally distribute vertically the selected items Verdeel de geselecteerde items gelijkmatig verticaal @@ -3481,122 +4023,126 @@ vraagt toegang tot deze sessie. Toegangsniveau: SettingsView3D - + Environment Omgeving - + Type Type - + Width Breedte - + Height Hoogte - + Depth Diepte - + Rendering Rendering - + Quality Kwaliteit - + Low Laag - + Medium Medium - + High Hoog - + Ultra Ultra - + Ambient light Lumi鳥 ambiante - + Smoke amount Hoeveelheid rook - + Show FPS Toon FPS - + Scale Schaal - + Custom items EIgen items - + Select a mesh file Selecteer een mesh bestand - + 3D files 3D bestanden - + All files Alle bestanden - + + Normalize the selected items + + + Actions - Acties + Acties - + Add a new item to the scene Voeg een nieuw item toe aan de scene - + Remove the selected items Verwijder de geselecteerde items - + Position Positie - + Rotation Rotatie @@ -3622,16 +4168,16 @@ vraagt toegang tot deze sessie. Toegangsniveau: ShowItem - - - + + + Position: Positie: - - - + + + Duration: Duur: @@ -3649,74 +4195,74 @@ vraagt toegang tot deze sessie. Toegangsniveau: Toon items kleur - + Unlock the selected items Ontgrendel de geselecteerde items - + Lock the selected items Vergrendel de geselecteerde items - + Snap to grid Aan grid vastmaken - + Stretch the original function Rek de originele functie - + Remove the selected items Verwijder de geselecteerde items - + Are you sure you want to remove the following items? (Note that the original functions will not be deleted) Weet je zeker dat je volgende items wilt verwijderen? (Originele functies zullen niet worden verwijderd) - + Delete show items Verwijder show items - + Copy the selected items in the clipboard Kopieer de geselecteerde items naar het klembord - + Paste items in the clipboard at cursor position Plak items uit het klembord op de cursor positie - + Play or resume Speel af of hervat - + Stop or rewind Stop of spoel terug - + Move the selected track up Verplaats de geselecteerde track naar boven - + Move the selected track down DVerplaats de geselecteerde track naar beneden - + Create a new track Maak een nieuwe track aan @@ -3726,19 +4272,19 @@ vraagt toegang tot deze sessie. Toegangsniveau: Show beheerder - + New Show Nieuwe show - - + + Track %1 Track %1 - - + + (Copy) (Kopie) @@ -3746,37 +4292,37 @@ vraagt toegang tot deze sessie. Toegangsniveau: SimpleDesk - + Universe Universum - + Reset the whole universe - + Dump on a new Scene Dump een nieuwe scene - + Reset the channel - + Fixture List - + Commands history - + Simple Desk Simpele console @@ -3792,25 +4338,202 @@ vraagt toegang tot deze sessie. Toegangsniveau: TrackDelegate - + Solo this track Enkel deze track - + Mute this track Mute deze track + + UISettingsEditor + + + + Reset to default + + + + + Scaling factor + + + + + Background darker + + + + + Background dark + + + + + Background medium + + + + + Background light + + + + + Background lighter + + + + + Controls background + + + + + Foreground main + + + + + Foreground medium + + + + + Foreground light + + + + + Toolbar gradient start + + + + + Sub-toolbar gradient start + + + + + Toolbar gradient end + + + + + Toolbar hover gradient start + + + + + Toolbar hover gradient end + + + + + Toolbar selection + + + + + Sub-toolbar selection + + + + + Section header + + + + + Section header divider + + + + + Item highlight + + + + + Item highlight pressed + + + + + Item hover + + + + + Item selection + + + + + VC Frame drop area + + + + + Item dark border + + + + + Save to file + + + + + Operation completed + + + + + Error + Fout + + + + UniverseGridView + + + Error + Fout + + + + Unable to perform the operation. +There is either not enough space or the target universe in invalid + + + + + Cut the selected items into clipboard + + + + + Paste items in the clipboard at the first available position + + + UniverseIOItem - Passthrough - Passthrough + Passthrough + + + + Enable/Disable passthrough + - + Enable/Disable feedbacks Schakel feedback in/uit @@ -4022,81 +4745,81 @@ vraagt toegang tot deze sessie. Toegangsniveau: Voeg een functie planning toe - + Remove this schedule Verwijder deze planning - + Start time Starttijd - + Stop time Eindtijd - + Enable the stop time Schakel de stoptijd in - + M As in Monday Zoals in maandag M - + T As in Tuesday Zoals in dinsdag D - + W As in Wednesday Zoals in woensdag W - + T As in Thursday Zoals in donderdag D - + F As in Friday Zoals in vrijdag V - + S As in Saturday Zoals in zaterdag Z - + S As in Sunday ZOals in zondag Z - + Repeat weekly Wekelijks herhalen - + Add a new schedule Voeg een nieuwe planning toe @@ -4104,17 +4827,17 @@ vraagt toegang tot deze sessie. Toegangsniveau: VCCueList - + Next Cue Volgende cue - + Previous Cue Vorige cue - + Play/Stop/Pause Start/Stop/Pauze @@ -4127,17 +4850,17 @@ vraagt toegang tot deze sessie. Toegangsniveau: Rechter Crossfade - + Stop/Pause Stop/Pauze - + Side Fader - + Cue List %1 Cue lijst %1 @@ -4145,27 +4868,32 @@ vraagt toegang tot deze sessie. Toegangsniveau: VCCueListItem - + Play/Pause Start/Pauze - + + Play/Stop + + + + Pause Pauze - + Stop Stop - + Previous cue Vorige cue - + Next cue Volgende cue @@ -4258,30 +4986,35 @@ vraagt toegang tot deze sessie. Toegangsniveau: VCFrame - + Next Page Volgende pagina - + Previous Page Vorige pagina - + Enable Inschakelen - + Collapse Inklappen - + Frame %1 Frame %1 + + + Page %1 + Pagina %1 + VCFrameItem @@ -4296,17 +5029,16 @@ vraagt toegang tot deze sessie. Toegangsniveau: Schakel dit frame in/uit - + Previous page vorige pagina - Page - Pagina + Pagina - + Next page Volgende pagina @@ -4349,10 +5081,21 @@ vraagt toegang tot deze sessie. Toegangsniveau: Pagina nummer - + Clone first page widgets Kloon widgets van eerste pagina + + + + Shortcuts + + + + + Shortcut name + + VCLabel @@ -4365,12 +5108,12 @@ vraagt toegang tot deze sessie. Toegangsniveau: VCPage - + Page %1 Pagina %1 - + Virtual Console Page %1 Virtuele console pagina %1 @@ -4378,57 +5121,57 @@ vraagt toegang tot deze sessie. Toegangsniveau: VCPageProperties - + Width Breedte - + Height Hoogte - + Security Beveiliging - + Set a PIN Stel een PIN in - + Error Fout - + The entered PINs are either invalid or incorrect De ingevoerde PINs zijn ofwel ongeldig of verkeerd - + Add page to the left Voeg pagina aan de linkerkant toe - + Add page to the right Voeg pagina aan de rechterkant toe - + Delete this page Verwijder deze pagina - + Delete page Verwijder pagina - + Are you sure you want to delete the selected page? Weet je zeker dat je de geselecteerde pagina wilt verwijderen? @@ -4493,12 +5236,12 @@ vraagt toegang tot deze sessie. Toegangsniveau: - + Slider %1 Fader %1 - + Knob %1 Knop %1 @@ -4571,82 +5314,82 @@ vraagt toegang tot deze sessie. Toegangsniveau: Attribuut - + Level mode Level mode - + Channels Kanalen - + Add/Remove channels Voeg toe/verwijder kanalen - + Click & Go button Sneele toegang button - + None Geen - + RGB/CMY RGB/CMG - + Gobo/Effect/Macro Gobo/Effect/Macro - + Monitor channel levels Kanaal niveau monitor - + Values range Waarde bereik - + Upper limit Bovengrens - + Lower limit Ondergrens - + Grand Master mode Algemene mode - + Reduce values beperk waarden - + Limit values Limiteer waarden - + Intensity channels Intensiteit kanalen - + All channels Alle kanalen @@ -4723,8 +5466,8 @@ vraagt toegang tot deze sessie. Toegangsniveau: Onbekend - - + + None Niks @@ -4732,87 +5475,87 @@ vraagt toegang tot deze sessie. Toegangsniveau: VCWidgetProperties - + Select a widget first Selecteer eerst een widget - + Settings Instellingen - + External controls Externe sturingen - + Basic properties Basis eigenschappen - + Label Label - + Background color Achtergrond kleur - + Foreground color Voorgrond kleur - + Font Lettertype - + Please choose a font Gelieve een lettertype te kiezen - + Background image Achtergrond afbeelding - + Select an image Selecteer een afbeelding - + Alignment Uitlijning - + Align the selected widgets to the left Lijn de geslecteerde widgets uit naar links - + Align the selected widgets to the right Lijn de geslecteerde widgets uit naar rechts - + Align the selected widgets to the top Lijn de geslecteerde widgets uit naar boven - + Align the selected widgets to the bottom Lijn de geslecteerde widgets uit naar beneden - + External Controls Externe sturingen @@ -4890,79 +5633,79 @@ vraagt toegang tot deze sessie. Toegangsniveau: Uitvoer scherm - + Output mode Uitvoer mode - + Windowed Venster - + Fullscreen Volledig scherm - + Geometry Geometrie - + Original Origineel - + Custom Aangepast - + Position Positie - + Size Grootte - + W Breedte B - + H Hoogte H - + Rotation Rotatie - + X X - + Y Y - + Z Z - + Layer @@ -4970,72 +5713,72 @@ vraagt toegang tot deze sessie. Toegangsniveau: VirtualConsole - + Error Fout - + Invalid PIN entered Ongeldige PIN ingevoerd - + Enable/Disable widgets snapping Schakel wiget snapping in/uit - + Widget matrix setup Widget matrix setup - + Columns Kolommen - + Rows Rijen - + Width Breedte - + Height Hoogte - + Frame type Frame type - + Normal Normaal - + Solo Solo - + Virtual Console Virtuele Console - + <None> <Geen> - + Page %1 Pagina %1 diff --git a/qmlui/qlcplus_pl_PL.ts b/qmlui/qlcplus_pl_PL.ts index 541fdd34f8..5210ed3b8f 100644 --- a/qmlui/qlcplus_pl_PL.ts +++ b/qmlui/qlcplus_pl_PL.ts @@ -4,54 +4,58 @@ ActionsMenu - Open a project - Otwórz projekt + Otwórz projekt - - + + Project files Pliki projektów - - - + + + All files Wszystkie pliki - + + Open a file + + + + QLC+ files - - + + Import from project Importuj z projektu - - + + Save project as... Zapisz projekt jako... - + Your project has changes Masz niezapisane zmiany - + Do you wish to save the current project first? Changes will be lost if you don't save them. Czy chcesz najpierw zapisać swój projekt? Jeśli go nie zapiszesz, dokonane zmiany przepadną. - + New project Nowy projekt @@ -60,117 +64,122 @@ Jeśli go nie zapiszesz, dokonane zmiany przepadną. Otwórz projekt - + Open file - + Save project Zapisz projekt - + Undo Cofnij - + Redo Ponów - + Network Sieć - + Server setup Konfiguracja Serwera - + Client setup Konfiguracja klienta - + Address tool Narzędzie do adresowania - + DMX Address tool Narzędzie do adresowania DMX - + + UI Settings + + + + Toggle fullscreen Pełny ekran - + Language Język - + Catalan Kataloński - + Dutch Holenderski - + English Angielski - + French Francuski - + German Niemiecki - + Italian Włoski - + Japanese Japoński - + Polish Polski - + Russian Rosyjski - + Spanish Hiszpański - + Ukrainian Ukraiński - + About O programie @@ -291,12 +300,17 @@ Jeśli go nie zapiszesz, dokonane zmiany przepadną. Urządzenie wyjściowe - + + Volume + + + + Fade in Płynne wejście - + Fade out Płynne wyjście @@ -312,20 +326,25 @@ Jeśli go nie zapiszesz, dokonane zmiany przepadną. BeamTool - + Beam Wiązka - + Beam degrees Szerokość wiązki - + Distance Odległość + + + Projected diameter + + BottomPanel @@ -338,8 +357,8 @@ Jeśli go nie zapiszesz, dokonane zmiany przepadną. ChannelEdit - - + + Custom Własna @@ -347,88 +366,118 @@ Jeśli go nie zapiszesz, dokonane zmiany przepadną. ChannelEditor - + + Open a picture file + + + + + Gobo pictures + + + + + All files + Wszystkie pliki + + + Name Nazwa - + Preset - - + + Type Typ - + Role - + Coarse (MSB) - + Fine (LSB) - + Default value - + Delete the selected capabilities - + + Capability wizard + + + + + Empty description provided + + + + + Overlapping with another capability + + + + From - + To - + Description - + Preview - + Primary color - + Secondary color - + Value(s) - + Value 1 - + Value 2 @@ -436,122 +485,137 @@ Jeśli go nie zapiszesz, dokonane zmiany przepadną. ChaserEditor - + Add a new step Dodaj nowy krok - + Remove the selected steps Usuń zaznaczone kroki - + Delete steps Usuwanie kroków - + Are you sure you want to remove the selected steps? Czy jesteś pewien, że chcesz usunąć zaznaczone kroki? - + + Preview the previous step + + + + + Preview the next step + + + + + Duplicate the selected step(s) + + + + Print the Chaser steps Wydrukuj kroki Chasera - + Run properties Parametry - + Loop Pętla - + Single Shot Jedno przejście - + Ping Pong Ping Pong - + Random Losowo - + Run Order Kolejność uruchamiania - + Forward W przód - + Backward W tył - + Direction Kierunek - + Time Czas - + Beats Takty - + Tempo Tempo - - + + Default Domyślne - - - + + + Common Wspólne - - - + + + Per Step Indywidualne - + Fade In Płynne wejście - + Fade Out Płynne wyjście - + Duration Czas trwania @@ -559,32 +623,32 @@ Jeśli go nie zapiszesz, dokonane zmiany przepadną. ChaserWidget - + Function Funkcja - + Fade In Płynne wejście - + Hold Wstrzymanie - + Fade Out Płynne wyjście - + Duration Czas trwania - + Note Uwagi @@ -592,22 +656,22 @@ Jeśli go nie zapiszesz, dokonane zmiany przepadną. CollectionEditor - + Add a function Dodaj funkcję - + Remove the selected function Usuń zaznaczone funkcje - + Delete functions Usuń funkcje - + Are you sure you want to remove the selected functions? Czy jesteś pewien, że chcesz usunąć zaznaczone funkcje? @@ -615,17 +679,17 @@ Jeśli go nie zapiszesz, dokonane zmiany przepadną. ColorTool - + Basic Podstawowe - + Full Pełne - + Filters Filtry @@ -633,7 +697,7 @@ Jeśli go nie zapiszesz, dokonane zmiany przepadną. ColorToolBasic - + Selected color Wybrany kolor @@ -641,87 +705,87 @@ Jeśli go nie zapiszesz, dokonane zmiany przepadną. ColorToolFilters - + Open filters menu Otwórz menu filtrów - + Cyan Cyjan - + Red Czerwony - + White Biały - + Magenta Fuksja - + Green Zielony - + Amber Bursztynowy - + Yellow Żółty - + Blue Niebieski - + UV UV - + CMY CMY - + Add a new color filters file Dodaj plik filtrów - + Rename the current color filters file Zmień nazwę pliku filtrów - + Save the current color filters file Zapisz obecny plik filtrów - + Add a new filter Dodaj nowy filtr - + Delete the selected filter Usuń zaznaczony filtr - + Paste the latest picked color as new filter Wklej ostatnio używany kolor jako nowy filtr @@ -729,37 +793,37 @@ Jeśli go nie zapiszesz, dokonane zmiany przepadną. ColorToolFull - + Red Czerwony - + Green Zielony - + Blue Niebieski - + White Biały - + Amber Bursztynowy - + UV UV - + Selected color Wybrany kolor @@ -767,12 +831,12 @@ Jeśli go nie zapiszesz, dokonane zmiany przepadną. ContextManager - + Universe Grid View Widok siatki przestrzeni - + linked powiązanie @@ -803,154 +867,174 @@ Jeśli go nie zapiszesz, dokonane zmiany przepadną. EFXEditor - + Fixtures Urządzenia - + Add a fixture/head Dodaj urządzenie/źródło światła - + Remove the selected fixture head(s) Usuń zaznaczone urządzenie(-a) - + Fixture Urządzenie - + + Mode + Tryb + + + Reverse Odwróć - - + + Start offset Przesunięcie początku - + + Position + Pozycja + + + + Dimmer + Dimmer + + + + RGB + + + + Add a new fixture Dodaj nowe urządzenie - - + + Pattern Kształt - + Relative movement Ruch względny - + Width Szerokość - + Height Wysokość - + X offset Przesunięcie X - + Y offset Przesunięcie Y - + Rotation Obrót - + X frequency Częstotliwość X - + Y frequency Częstotliwość Y - + X phase Przesunięcie fazowe X - + Y phase Przesunięcie fazowe Y - + Speed Szybkość - + Fade in Płynne wejście - Hold - Wstrzymanie + Wstrzymanie - + Fade out Płynne wyjście - + Order and direction Kolejność i kierunek - + + Loop Pętla - + Single Shot Jedno przejście - + Ping Pong Ping Pong - + Run Order Kolejność uruchamiania - + Forward W przód - + Backward W tył - + Direction Kierunek @@ -958,7 +1042,7 @@ Jeśli go nie zapiszesz, dokonane zmiany przepadną. EditorTopBar - + Go back to the previous view Wróć do poprzedniego widoku @@ -976,77 +1060,82 @@ Jeśli go nie zapiszesz, dokonane zmiany przepadną. - + General - + Manufacturer Producent - + Type Typ - + Model Model - + Author Autor - + Physical properties - + Channels Kanały - + Add a new channel - + Remove the selected channel(s) - + + Channel wizard + + + + Modes - + Add a new mode - + Remove the selected mode(s) - + Aliases - + New channel %1 - + New mode @@ -1059,37 +1148,37 @@ Jeśli go nie zapiszesz, dokonane zmiany przepadną. Control - + Universe Przestrzeń - + Activate auto detection Wykryj automatycznie - + Channel Kanał - + Remove this input source Usuń to źródło - + Custom feedbacks Dostosuj informacje zwrotne - + Lower Dolny - + Upper Górny @@ -1115,13 +1204,13 @@ Jeśli go nie zapiszesz, dokonane zmiany przepadną. FixtureBrowser - + Create a new fixture definition Add a new fixture definition - + Edit the selected fixture definition @@ -1129,22 +1218,22 @@ Jeśli go nie zapiszesz, dokonane zmiany przepadną. FixtureChannelDelegate - + Auto (HTP) Automatyczny (HTP) - + Auto (LTP) Automatyczny (LTP) - + Forced HTP Wymuś HTP - + Forced LTP Wymuś LTP @@ -1175,7 +1264,7 @@ Jeśli go nie zapiszesz, dokonane zmiany przepadną. - + Save definition as... @@ -1185,27 +1274,32 @@ Jeśli go nie zapiszesz, dokonane zmiany przepadną. Błąd - + + Warning + + + + Back to QLC+ - + New definition - + Open definition - + Save definition - + Unknown Nieznane @@ -1228,32 +1322,32 @@ Jeśli go nie zapiszesz, dokonane zmiany przepadną. Usuń zaznaczone elementy - + Reset the entire group Zresetuj grupę - + Rotate 90° clockwise Obróć o 90º - + Rotate 180° clockwise Obróć o 180º - + Rotate 270° clockwise Obróć o 270º - + Flip horizontally Odbij w poziomie - + Flip vertically Odbij w pionie @@ -1261,67 +1355,77 @@ Jeśli go nie zapiszesz, dokonane zmiany przepadną. FixtureGroupManager - + + Error + Błąd + + + Add a new fixture group Dodaj nową grupę urządzeń - + Remove the selected items Usuń zaznaczone elementy - + Set a Group/Fixture/Channel search filter Filtruj Grupy/Urządzenia/Kanały - + Rename the selected items Zmień nazwy zaznaczonych elementów - + Rename items Zmień nazwy - + Inspect the selected item Podejrzyj zaznaczony element - + Toggle fixtures and channels properties Zmień właściwości urządzeń i kanałów - + Add/Remove a linked fixture Dodaj/Usuń powiązane urządzenie - + Name Nazwa - + + Mode + Tryb + + + Flags Atrybuty - + Can fade Płynne przejścia - + Behaviour Zachowanie - + Modifier Modyfikator @@ -1329,67 +1433,85 @@ Jeśli go nie zapiszesz, dokonane zmiany przepadną. FixtureManager - - - + + + Head Źródło światła - + New group %1 Nowa grupa %1 - + %1 - Row %2 %1 - Rząd %2 - + New filters %1 Nowe filtry %1 + + FixtureNodeDelegate + + + Show/Hide this fixture + + + + + Invert Pan + + + + + Invert Tilt + + + FixtureProperties - + Fixture properties Właściwości urządzenia - + Name Nazwa - + Universe Przestrzeń - + Address Adres - + Quantity Ilość - + Channels Kanały - + Gap Odstęp - + Mode Tryb @@ -1621,67 +1743,67 @@ Jeśli go nie zapiszesz, dokonane zmiany przepadną. Filtruj funkcje - + <None> <Brak> - + New Scene Nowa Scena - + New Chaser Nowy Chaser - + New Sequence Nowa Sekwencja - + New EFX Nowy EFX - + New Collection Nowa Kolekcja - + New RGB Matrix Nowa Matryca RGB - + New Script Nowy Skrypt - + New Show Nowy Pokaz - + New Audio Nowy Dźwięk - + New Video Nowe Wideo - + (Copy) (Kopia) - + New folder Nowy folder @@ -1745,31 +1867,31 @@ Jeśli go nie zapiszesz, dokonane zmiany przepadną. InputOutputManager - + Input/Output Manager Menedżer Wejść/Wyjść - + All universes Wszystkie przestrzenie - - - - - + + + + + Default device Domyślne urządzenie - + Disabled Wyłączone - + Internal generator Generator wewnętrzny @@ -1782,10 +1904,123 @@ Jeśli go nie zapiszesz, dokonane zmiany przepadną. Usuń profil wejścia + + InputProfileEditor + + + + + !! Warning !! + + + + + Channel wizard activated + + + + + You have enabled the input channel wizard. After clicking OK, wiggle your mapped input profile's controls. They should appear into the list. Click the wizard button again to stop channel auto-detection.<br><br>Note that the wizard cannot tell the difference between a knob and a slider so you will have to do the change manually. + + + + + Unsaved changes + + + + + Do you wish to save the current profile first? +Changes will be lost if you don't save them. + + + + + Manufacturer + Producent + + + + Model + Model + + + + + Type + Typ + + + + MIDI Global Settings + + + + + When MIDI notes are used, send a Note Off when value is 0 + + + + + Channel + Kanał + + + + Name + Nazwa + + + + + Behaviour + Zachowanie + + + + Generate an extra Press/Release when toggled + + + + + Movement + + + + + Sensitivity + + + + + Custom Feedback + + + + + Lower value + + + + + Upper value + + + + + Button %1 + Przycisk %1 + + + + Slider %1 + Suwak %1 + + IntensityTool - + Intensity Intensywność @@ -1806,17 +2041,17 @@ Jeśli go nie zapiszesz, dokonane zmiany przepadną. Działanie - + Combination Kombinacja klawiszy - + Activate auto detection Wykryj automatycznie - + Remove this keyboard combination Usuń skrót @@ -1829,62 +2064,62 @@ Jeśli go nie zapiszesz, dokonane zmiany przepadną. Dodaj Urządzenia - + Fixture Groups Grupy Urządzeń - + Palettes Palety - + Intensity Intensywność - + Shutter Migawka - + Position Pozycja - + Color Kolor - + Color Wheel Tarcza Kolorów - + Gobos Gobo - + Beam Wiązka - + Pick a 3D point Wybierz punkt w 3D - + Toggle multiple item selection Zaznaczaj wiele elementów - + Select/Deselect all fixtures Zaznacz/Odznacz wszystkie urządzenia @@ -1892,40 +2127,45 @@ Jeśli go nie zapiszesz, dokonane zmiany przepadną. MainView - + Actions Akcje - + Fixtures & Functions Urządzenia i Funkcje - + Virtual Console Konsola Wirtualna - + Simple Desk Stół DMX - + Show Manager Menedżer Pokazów - + Input/Output Wejście/Wyjście - + Off Wyłącz + + + Stop all the running functions + + MainView2D @@ -1938,27 +2178,27 @@ Jeśli go nie zapiszesz, dokonane zmiany przepadną. MainView3D - + 3D View Widok 3D - + Simple ground Płaska powierzchnia - + Simple box Pomieszczenie - + Rock stage Scena rockowa - + Theatre stage Scena teatralna @@ -1974,38 +2214,58 @@ Jeśli go nie zapiszesz, dokonane zmiany przepadną. ModeEditor - + Name Nazwa - - + + Channels Kanały - + + Create a new emitter + + + + + Remove the selected channel(s) + + + + + Drop channels here + + + + Acts on - - Heads + + Emitters + + + + + Remove the selected emitter(s) - + Physical - + Use global settings - + Override global settings @@ -2026,97 +2286,112 @@ Jeśli go nie zapiszesz, dokonane zmiany przepadną. PaletteFanningBox - - + Flat Płaski - - + Linear Liniowy - - + Square Kwadrat - - + Saw Zębaty - - + Sine Sinusoida - - Left to right - Od lewej do prawej + Od lewej do prawej - - Right to left - Od prawej do lewej + Od prawej do lewej - - Top to bottom - Z góry na dół + Z góry na dół - - Bottom to top - Z dołu na górę + Z dołu na górę - - Centered - Wyśrodkowane + Wyśrodkowane - + Show/Hide fanning options Pokaż/Ukryj opcje wachlowania - + Create a new palette Stwórz nową paletę - + Type Typ - + Layout Układ - + + X Ascending + + + + + X Descending + + + + + Y Ascending + + + + + Y Descending + + + + + Z Ascending + + + + + Z Descending + + + + Amount Ilość - + Value Wartość - + Pick the selected color Wybierz zaznaczony kolor @@ -2149,12 +2424,12 @@ Jeśli go nie zapiszesz, dokonane zmiany przepadną. Usuń zaznaczone palety - + Are you sure you want to delete the following items? Czy jesteś pewien, że chcesz usunąć zaznaczone elementy? - + Delete items Usuń elementy @@ -2168,8 +2443,8 @@ Jeśli go nie zapiszesz, dokonane zmiany przepadną. - - + + Type Typ @@ -2179,83 +2454,83 @@ Jeśli go nie zapiszesz, dokonane zmiany przepadną. - + Colour Temp (K) - + Lens Soczewka - + Min Degrees - + Max Degrees - + Head(s) - + Pan Max Degrees - + Tilt Max Degrees - + Layout (Columns x Rows) - + Dimensions - + Weight Waga - + Width Szerokość - + Height Wysokość - + Depth Głębokość - + Electrical - + Power Consumption - + DMX Connector @@ -2268,85 +2543,261 @@ Jeśli go nie zapiszesz, dokonane zmiany przepadną. Informacje - - and contributors - i współtwórcy + + and contributors + i współtwórcy + + + + Website + Strona internetowa + + + + This application is licensed under the terms of the + Ten program jest chroniony + + + + Apache 2.0 license + licencją Apache 2.0 + + + + PopupChannelModifiers + + + Channel Modifiers Editor + + + + + Insert a modified value after the selected + + + + + Delete the selected modifier value + + + + + Rename the selected modifier template + + + + + Save the selected modifier template + + + + + Templates + + + + + Original DMX value + + + + + Modified DMX value + + + + + PopupChannelWizard + + + Fixture Editor Wizard + + + + + Properties + + + + + Start + + + + + Width + Szerokość + + + + Amount + Ilość + + + + Type + Typ + + + + Red + Czerwony + + + + Green + Zielony + + + + Blue + Niebieski + + + + White + Biały + + + + Amber + Bursztynowy + + + + UV + UV + + + + RGB + + + + + RGBW + + + + + RGBAW + + + + + Dimmer + Dimmer + + + + Pan + Pan + + + + Tilt + Tilt + + + + Color Macro + + + + + Shutter + Migawka + + + + Beam + Wiązka + + + + Effect + Efekt + + + + Label + Podpis - - Website - Strona internetowa + + Capability # + - - This application is licensed under the terms of the - Ten program jest chroniony + + Channel # + - - Apache 2.0 license - licencją Apache 2.0 + + Preview + PopupCreatePalette - + Create a new palette Stwórz nową paletę - + Dimmer Dimmer - + Color Kolor - + Position Pozycja - + Shutter Migawka - + Gobo Gobo - + Palette name Nazwa palety - + New Palette Nowa Paleta - + Type Typ - + Also create a Scene Stwórz również scenę - + Scene name Nazwa sceny - + New Scene Nowa Scena @@ -2354,87 +2805,87 @@ Jeśli go nie zapiszesz, dokonane zmiany przepadną. PopupDMXDump - + Enter a name for the scene Podaj nazwę sceny - + Scene name Nazwa sceny - + New Scene Nowa Scena - + Don't ask again Nie pytaj ponownie - + Available channel types Dostępne typy kanałów - + Intensity Intensywność - + RGB/CMY/WAUV RGB/CMY/WAUV - + Color macros Makra kolorów - + Gobo Gobo - + Pan Pan - + Tilt Tilt - + Speed Szybkość - + Shutter/Strobe Migawka/Stroboskop - + Prism Pryzma - + Beam Wiązka - + Effect Efekt - + Maintenance Obsługa @@ -2442,7 +2893,7 @@ Jeśli go nie zapiszesz, dokonane zmiany przepadną. PopupDisclaimer - + Disclaimer Ostrzeżenie @@ -2465,6 +2916,54 @@ Jeśli go nie zapiszesz, dokonane zmiany przepadną. Funkcje + + PopupInputChannelEditor + + + Input Channel Editor + + + + + Input Channel + + + + + Number + Numer + + + + Name + Nazwa + + + + Type + Typ + + + + Channel + Kanał + + + + Message + + + + + Parameter + + + + + Note + Uwagi + + PopupManualInputSource @@ -2529,27 +3028,27 @@ Jeśli go nie zapiszesz, dokonane zmiany przepadną. PopupNetworkClient - + Disconnected Rozłączono - + Waiting for access Oczekiwanie na dostęp - + Downloading project Pobieranie projektu - + Connected Połączono - + QLC+ client setup Konfiguracja klienta QLC+ @@ -2592,7 +3091,7 @@ Jeśli go nie zapiszesz, dokonane zmiany przepadną. PopupNetworkConnect - + Client access request Prośba o dostęp @@ -2722,12 +3221,12 @@ Poziom dostępu: PopupPINRequest - + Page PIN PIN do strony - + Remember for this session Zapamiętaj dla tej sesji @@ -2735,22 +3234,22 @@ Poziom dostępu: PopupPINSetup - + Current PIN Obecny PIN - + New PIN Nowy PIN - + Confirm PIN Potwierdź nowy PIN - + New PIN mismatch Kod PIN i jego potwierdzenie nie są identyczne @@ -2758,22 +3257,22 @@ Poziom dostępu: PopupRenameItems - + New name Nowa nazwa - + Enable numbering Włącz numerację - + Start number Numer początkowy - + Digits Liczba cyfr @@ -2781,28 +3280,76 @@ Poziom dostępu: PositionTool - + Position Pozycja - + Rotate 90° clockwise Obróć o 90° - - + + Snap to the previous value Skocz do poprzedniej wartości - - + + Snap to the next value Skocz do następnej wartości + + ProfilesList + + + !! Warning !! + + + + + Save this profile + + + + + Toggle the automatic detection procedure + + + + + Add a new channel + + + + + Create a new input profile + + + + + Edit the selected channel + + + + + Edit the selected input profile + + + + + Delete the selected channel + + + + + Delete the selected input profile(s) + + + RGBMatrixEditor @@ -2816,214 +3363,214 @@ Poziom dostępu: Wzór - + Blend mode Tryb mieszania barw - + Default (HTP) Domyślny (HTP) - + Mask Maska - + Additive Suma - + Subtractive Różnica - + Color mode - + Default (RGB) - + White Biały - + Amber Bursztynowy - + UV UV - + Dimmer Dimmer - + Shutter Migawka - + Colors Kolory - + Parameters Parametry - + Speed Szybkość - + Steps fade in Płynne wejście - + Steps hold Czas wstrzymania - + Steps fade out Płynne wyjście - + Tempo type Rodzaj tempa - + Time Czas - + Beats Takty - + Order and direction Kolejność i kierunek - + Loop Pętla - + Single Shot Jedno przejście - + Ping Pong Ping Pong - + Run Order Kolejność uruchamiania - + Forward W przód - + Backward W tył - + Direction Kierunek - + Text Tekst - + Please choose a font Wybierz czcionkę - - - + + + Animation Animacja - + Letters Litery - - + + Horizontal Poziomo - - + + Vertical Pionowo - - + + Offset Przesunięcie - - + + X X - - + + Y Y - + Image Obraz - + Select an image Wybierz obraz - + Static Statycznie @@ -3031,17 +3578,17 @@ Poziom dostępu: RGBPanelProperties - + RGB panel properties Właściwości panelu RGB - + Name Nazwa - + Universe Przestrzeń @@ -3200,7 +3747,7 @@ Poziom dostępu: Zachowaj do nowej Sceny - + Function Preview Podgląd Funkcji @@ -3213,42 +3760,42 @@ Poziom dostępu: SceneEditor - + Add a fixture/group - + Add a palette - + Remove the selected items Usuń zaznaczone elementy - + Delete items Usuń elementy - + Are you sure you want to remove the selected items? Czy jesteś pewien, że chcesz usunąć zaznaczone elementy? - + Speed Szybkość - + Fade in Płynne wejście - + Fade out Płynne wyjście @@ -3256,67 +3803,67 @@ Poziom dostępu: ScriptEditor - + Add a method call at cursor position Dodaj wywołanie funkcji w pozycji kursora - + Show/hide functions tree Pokaż/Ukryj drzewo funkcji - + Show/hide fixture tree Pokaż/Ukryj drzewo urządzeń - + Check the script syntax Sprawdź składnię skryptu - + Syntax check Sprawdzanie składni - + Start function Uruchom funkcję - + Stop function Zatrzymaj funkcję - + Set fixture channel Ustaw kanał urządzenia - + Wait time Czekaj - + Random number Losowa liczba - + Blackout Wygaś - + System command Polecenie systemowe - + File path Ścieżka pliku @@ -3344,12 +3891,12 @@ Poziom dostępu: Usuń zaznaczone urządzenia - + Steps Kroki - + Fixtures Urządzenia @@ -3357,107 +3904,122 @@ Poziom dostępu: SettingsView2D - + Environment Środowisko - + Width Szerokość - + Height Wysokość - + Depth Głębokość - + Grid units Jednostki siatki - + Meters Metry - + Feet Stopy - + Point of view Punkt widzenia - + Top view Z góry - + Front view Z przodu - + Right side view Z prawej - + Left side view Z lewej + Custom Background + + + + + Select an image + Wybierz obraz + + + + Reset background + + + + Selected fixtures Zaznaczone urządzenia - + Gel color Gel color - + Rotation Obrót - + Alignment Wyrównanie - + Align the selected items to the left Wyrównaj zaznaczone do lewej - + Align the selected items to the top Wyrównaj zaznaczone do góry - + Distribution Rozmieszczenie - + Equally distribute horizontally the selected items Rozmieść równo w poziomie - + Equally distribute vertically the selected items Rozmieść równo w pionie @@ -3465,122 +4027,126 @@ Poziom dostępu: SettingsView3D - + Environment Środowisko - + Type Typ - + Width Szerokość - + Height Wysokość - + Depth Głębokość - + Rendering Renderowanie - + Quality Jakość - + Low Niska - + Medium Średnia - + High Wysoka - + Ultra Ultra - + Ambient light Światło otoczenia - + Smoke amount Ilość dymu - + Show FPS Pokaż FPS - + Position Pozycja - + Rotation Obrót - + Scale Skala - + Custom items Obiekty użytkownika - + Select a mesh file Wybierz plik siatki - + 3D files Pliki 3D - + All files Wszystkie pliki - + + Normalize the selected items + + + Actions - Akcje + Akcje - + Add a new item to the scene Dodaj element do sceny - + Remove the selected items Usuń zaznaczone elementy @@ -3606,16 +4172,16 @@ Poziom dostępu: ShowItem - - - + + + Position: Pozycja: - - - + + + Duration: Czas trwania: @@ -3633,74 +4199,74 @@ Poziom dostępu: Pokaż kolor elementów - + Unlock the selected items Odblokuj zaznaczone elementy - + Lock the selected items Zablokuj zaznaczone elementy - + Snap to grid Przyciągaj do siatki - + Stretch the original function Rozciągnij oryginalną funkcję - + Remove the selected items Usuń zaznaczone obiekty - + Are you sure you want to remove the following items? (Note that the original functions will not be deleted) Czy jesteś pewien, że chcesz usunąć następujące elementy? (Oryginalne funkcje nie zostaną usunięte) - + Delete show items Usuń elementy pokazu - + Copy the selected items in the clipboard Skopiuj zaznaczone elementy do schowka - + Paste items in the clipboard at cursor position Wklej elementy ze schowka w miejscu kursora - + Play or resume Odtwórz/Wznów - + Stop or rewind Zatrzymaj/Przewiń - + Move the selected track up Przenieś ścieżkę w górę - + Move the selected track down Przenieś ścieżkę w dół - + Create a new track Utwórz nową ścieżkę @@ -3710,19 +4276,19 @@ Poziom dostępu: Menedżer Pokazów - + New Show Nowy Pokaz - - + + Track %1 Ścieżka %1 - - + + (Copy) (Kopia) @@ -3730,37 +4296,37 @@ Poziom dostępu: SimpleDesk - + Universe Przestrzeń - + Reset the whole universe - + Dump on a new Scene Zachowaj do nowej Sceny - + Reset the channel - + Fixture List - + Commands history - + Simple Desk Stół DMX @@ -3776,25 +4342,202 @@ Poziom dostępu: TrackDelegate - + Solo this track Włącz tylko tą ścieżkę - + Mute this track Wycisz tę ścieżkę + + UISettingsEditor + + + + Reset to default + + + + + Scaling factor + + + + + Background darker + + + + + Background dark + + + + + Background medium + + + + + Background light + + + + + Background lighter + + + + + Controls background + + + + + Foreground main + + + + + Foreground medium + + + + + Foreground light + + + + + Toolbar gradient start + + + + + Sub-toolbar gradient start + + + + + Toolbar gradient end + + + + + Toolbar hover gradient start + + + + + Toolbar hover gradient end + + + + + Toolbar selection + + + + + Sub-toolbar selection + + + + + Section header + + + + + Section header divider + + + + + Item highlight + + + + + Item highlight pressed + + + + + Item hover + + + + + Item selection + + + + + VC Frame drop area + + + + + Item dark border + + + + + Save to file + + + + + Operation completed + + + + + Error + Błąd + + + + UniverseGridView + + + Error + Błąd + + + + Unable to perform the operation. +There is either not enough space or the target universe in invalid + + + + + Cut the selected items into clipboard + + + + + Paste items in the clipboard at the first available position + + + UniverseIOItem - Passthrough - Przepuść bez zmian + Przepuść bez zmian + + + + Enable/Disable passthrough + - + Enable/Disable feedbacks Włącz/Wyłącz informacje zwrotne @@ -4006,74 +4749,74 @@ Poziom dostępu: Zaplanuj funkcję - + Remove this schedule Usuń plan - + Start time Czas rozpoczęcia - + Stop time Czas zakończenia - + Enable the stop time Uwzględnij czas zakończenia - + M As in Monday Pon - + T As in Tuesday Wt - + W As in Wednesday Śr - + T As in Thursday Czw - + F As in Friday Pt - + S As in Saturday So - + S As in Sunday Nd - + Repeat weekly Powtarzaj co tydzień - + Add a new schedule Dodaj nowy plan @@ -4081,32 +4824,32 @@ Poziom dostępu: VCCueList - + Next Cue Następne Cue - + Previous Cue Poprzednie Cue - + Play/Stop/Pause Odtwórz/Stop/Wstrzymaj - + Stop/Pause Stop/Wstrzymaj - + Side Fader Przenikanie na boki - + Cue List %1 Lista Cue %1 @@ -4114,27 +4857,32 @@ Poziom dostępu: VCCueListItem - + Play/Pause Odtwórz/Wstrzymaj - + + Play/Stop + + + + Pause Wstrzymaj - + Stop Stop - + Previous cue Poprzednie cue - + Next cue Następne cue @@ -4227,30 +4975,35 @@ Gdy chaser jest zatrzymany VCFrame - + Next Page Następna Strona - + Previous Page Poprzednia Strona - + Enable Włącz - + Collapse Zwiń - + Frame %1 Ramka %1 + + + Page %1 + Strona %1 + VCFrameItem @@ -4265,17 +5018,16 @@ Gdy chaser jest zatrzymany Włącz/Wyłącz ramkę - + Previous page Poprzednia strona - Page - Strona + Strona - + Next page Następna strona @@ -4318,10 +5070,21 @@ Gdy chaser jest zatrzymany Numery stron - + Clone first page widgets Duplikuj widgety z pierwszej strony + + + + Shortcuts + + + + + Shortcut name + + VCLabel @@ -4334,12 +5097,12 @@ Gdy chaser jest zatrzymany VCPage - + Page %1 Strona %1 - + Virtual Console Page %1 Strona %1 Konsoli Wirtualnej @@ -4347,57 +5110,57 @@ Gdy chaser jest zatrzymany VCPageProperties - + Width Szerokość - + Height Wysokość - + Security Zabezpieczenia - + Set a PIN Ustaw PIN - + Error Błąd - + The entered PINs are either invalid or incorrect Wprowadzone kody PIN są niepoprawne - + Add page to the left Dodaj stronę po lewej - + Add page to the right Dodaj stronę po prawej - + Delete this page Usuń tą stronę - + Delete page Usuwanie strony - + Are you sure you want to delete the selected page? Czy jesteś pewien, że chcesz usunąć zaznaczoną stronę? @@ -4458,12 +5221,12 @@ Gdy chaser jest zatrzymany Zresetuj sterowanie - + Slider %1 Suwak %1 - + Knob %1 Pokrętło %1 @@ -4536,82 +5299,82 @@ Gdy chaser jest zatrzymany Atrybut - + Level mode Tryb poziomu - + Channels Kanały - + Add/Remove channels Dodaj/usuń kanały - + Click & Go button Przycisk Click & Go - + None Brak - + RGB/CMY RGB/CMY - + Gobo/Effect/Macro Gobo/Efekt/Makro - + Monitor channel levels Monitoruj poziomy - + Values range Zakres wartości - + Upper limit Górna granica - + Lower limit Dolna granica - + Grand Master mode Tryb sumy - + Reduce values Redukuj wartości - + Limit values Ograniczaj wartości - + Intensity channels Kanały intensywności - + All channels Wszystkie kanały @@ -4688,8 +5451,8 @@ Gdy chaser jest zatrzymany Nieznane - - + + None Brak @@ -4697,87 +5460,87 @@ Gdy chaser jest zatrzymany VCWidgetProperties - + Select a widget first Najpierw wybierz widget - + Settings Ustawienia - + External controls Sterowanie zewnętrzne - + Basic properties Podstawowe właściwości - + Label Podpis - + Background color Kolor tła - + Foreground color Kolor pierwszoplanowy - + Font Czcionka - + Please choose a font Wybierz czcionkę - + Background image Obraz tła - + Select an image Wybierz obraz - + Alignment Wyrównanie - + Align the selected widgets to the left Wyrównaj zaznaczone widgety do lewej - + Align the selected widgets to the right Wyrównaj zaznaczone widgety do prawej - + Align the selected widgets to the top Wyrównaj zaznaczone widgety do góry - + Align the selected widgets to the bottom Wyrównaj zaznaczone widgety do dołu - + External Controls Sterowanie zewnętrzne @@ -4855,77 +5618,77 @@ Gdy chaser jest zatrzymany Ekran wyjściowy - + Output mode Tryb wyjścia - + Windowed W oknie - + Fullscreen Pełny ekran - + Geometry Geometria - + Original Oryginalna - + Custom Własna - + Position Pozycja - + Size Rozmiar - + W Sz - + H Wys - + Rotation Obrót - + X X - + Y Y - + Z Z - + Layer Warstwa @@ -4933,72 +5696,72 @@ Gdy chaser jest zatrzymany VirtualConsole - + Error Błąd - + Invalid PIN entered Wprowadzono błędny PIN - + Enable/Disable widgets snapping Włącz/Wyłącz przyciąganie widgetów - + Widget matrix setup Konfiguracja zestawu widgetów - + Columns Kolumny - + Rows Wiersze - + Width Szerokość - + Height Wysokość - + Frame type Typ ramki - + Normal Zwykła - + Solo Solo - + Virtual Console Konsola Wirtualna - + <None> <Brak> - + Page %1 Strona %1 diff --git a/qmlui/qlcplus_ru_RU.ts b/qmlui/qlcplus_ru_RU.ts index abdd6d54be..3202190d92 100644 --- a/qmlui/qlcplus_ru_RU.ts +++ b/qmlui/qlcplus_ru_RU.ts @@ -4,54 +4,58 @@ ActionsMenu - Open a project - Открыть проект + Открыть проект - - + + Project files Файлы проекта - - - + + + All files Все файлы - + + Open a file + + + + QLC+ files - - + + Import from project Импортировать из проекта - - + + Save project as... Сохранить проект как... - + Your project has changes Ваш проект имеет несохранённые изменения - + Do you wish to save the current project first? Changes will be lost if you don't save them. Вы хотите сначала сохранить текущий проект? Изменения будут потеряны, если вы их не сохраните. - + New project Новый проект @@ -60,117 +64,122 @@ Changes will be lost if you don't save them. Открыть проект - + Open file - + Save project Сохранить проект - + Undo Отменить - + Redo Повторить - + Network Сеть - + Server setup Настройки сервера - + Client setup Настройки клиента - + Address tool Адресный помощник - + DMX Address tool Помощник адресов DMX - + + UI Settings + + + + Toggle fullscreen Переключить полноэкранный режим - + Language Язык - + Catalan Каталанский - + Dutch Нидерландский - + English Английский - + French Французский - + German Немецкий - + Italian Итальянский - + Japanese Японский - + Polish - + Russian - + Spanish Испанский - + Ukrainian - + About О программе @@ -291,12 +300,17 @@ Changes will be lost if you don't save them. Выходное устройство - + + Volume + + + + Fade in Плавное появление - + Fade out Плавное исчезание @@ -312,20 +326,25 @@ Changes will be lost if you don't save them. BeamTool - + Beam Луч - + Beam degrees Раскрытие луча - + Distance Расстояние + + + Projected diameter + + BottomPanel @@ -338,8 +357,8 @@ Changes will be lost if you don't save them. ChannelEdit - - + + Custom Настроенный @@ -347,88 +366,118 @@ Changes will be lost if you don't save them. ChannelEditor - + + Open a picture file + + + + + Gobo pictures + + + + + All files + Все файлы + + + Name Имя - + Preset - - + + Type Тип - + Role - + Coarse (MSB) - + Fine (LSB) - + Default value - + Delete the selected capabilities - + + Capability wizard + + + + + Empty description provided + + + + + Overlapping with another capability + + + + From - + To - + Description - + Preview - + Primary color - + Secondary color - + Value(s) - + Value 1 - + Value 2 @@ -436,122 +485,137 @@ Changes will be lost if you don't save them. ChaserEditor - + Add a new step Добавить новый шаг - + Remove the selected steps Удалить выделенные шаги - + Delete steps Удалить шаги - + Are you sure you want to remove the selected steps? Вы действительно хотите удалить выделенные шаги? - + + Preview the previous step + + + + + Preview the next step + + + + + Duplicate the selected step(s) + + + + Print the Chaser steps Показать шаги Чейзера - + Run properties Свойства запуска - + Loop Цикл - + Single Shot Одиночный - + Ping Pong Пинг Понг - + Random Случайно - + Run Order Порядок запуска - + Forward Вперед - + Backward Назад - + Direction Направление - + Time Время - + Beats Удары - + Tempo Темп - - + + Default По умолчанию - - - + + + Common Общий - - - + + + Per Step За шаг - + Fade In Плавное появление - + Fade Out Плавное затухание - + Duration Продолжительность @@ -559,32 +623,32 @@ Changes will be lost if you don't save them. ChaserWidget - + Function Функция - + Fade In Плавное появление - + Hold Закрепить - + Fade Out Плавное затухание - + Duration Продолжительность - + Note Примечание @@ -592,22 +656,22 @@ Changes will be lost if you don't save them. CollectionEditor - + Add a function Добавить функцию - + Remove the selected function Удалить выделенные функции - + Delete functions Удалить функции - + Are you sure you want to remove the selected functions? Вы действительно хотите удалить выделенные функции? @@ -615,17 +679,17 @@ Changes will be lost if you don't save them. ColorTool - + Basic Базовый - + Full Полный - + Filters Фильтры @@ -633,7 +697,7 @@ Changes will be lost if you don't save them. ColorToolBasic - + Selected color Выделенные цвета @@ -641,88 +705,88 @@ Changes will be lost if you don't save them. ColorToolFilters - + Open filters menu Открыть меню фильтров - + Cyan Бирюзовый - + Red Красный - + White Белый - + Magenta Пурпурный - + Green Зелёный - + Amber Янтарный - + Yellow Жёлтый - + Blue Синий - + UV Ультрафиолет УФ - + CMY CMY - + Add a new color filters file Добавить новый файл цветовых фильтров - + Rename the current color filters file Переименовать текущий файл цветовых фильтров - + Save the current color filters file Сохранить текущий файл цветовых фильтров - + Add a new filter Добавить новый фильтр - + Delete the selected filter Удалить выделенные фильтры - + Paste the latest picked color as new filter Вставить последний выбранный цвет в качестве нового фильтра @@ -730,38 +794,38 @@ Changes will be lost if you don't save them. ColorToolFull - + Red Красный - + Green Зелёный - + Blue Синий - + White Белый - + Amber Янтарный - + UV Ультрафиолет УФ - + Selected color Выделенный цвет @@ -769,12 +833,12 @@ Changes will be lost if you don't save them. ContextManager - + Universe Grid View Просмотр сетки Области DMX - + linked связан @@ -805,154 +869,174 @@ Changes will be lost if you don't save them. EFXEditor - + Fixtures Световые приборы - + Add a fixture/head Добавить прибор - + Remove the selected fixture head(s) Удалить выделенные прибор(ы) - + Fixture Световой прибор - + + Mode + Режим + + + Reverse Обратный - - + + Start offset Смещение начала - + + Position + Положение + + + + Dimmer + + + + + RGB + + + + Add a new fixture Добавить новый прибор - - + + Pattern Шаблон - + Relative movement Относительное движение - + Width Ширина - + Height Высота - + X offset X смещение - + Y offset Y смещение - + Rotation Вращение - + X frequency X частота - + Y frequency Y частота - + X phase X фаза - + Y phase Y фаза - + Speed Скорость - + Fade in Плавное появление - Hold - Закрепить + Закрепить - + Fade out Плавное затухание - + Order and direction Порядок и направление - + + Loop Цикл - + Single Shot Одиночный - + Ping Pong Пинг Понг - + Run Order Порядок запуска - + Forward Вперед - + Backward Назад - + Direction Направление @@ -960,7 +1044,7 @@ Changes will be lost if you don't save them. EditorTopBar - + Go back to the previous view Go back to the Function Manager Вернуться назад в Менеджер Функций @@ -979,77 +1063,82 @@ Changes will be lost if you don't save them. - + General - + Manufacturer Производитель - + Type Тип - + Model Модель - + Author Автор - + Physical properties - + Channels Каналы - + Add a new channel - + Remove the selected channel(s) - + + Channel wizard + + + + Modes - + Add a new mode - + Remove the selected mode(s) - + Aliases - + New channel %1 - + New mode @@ -1062,37 +1151,37 @@ Changes will be lost if you don't save them. Управление - + Universe Область DMX - + Activate auto detection Активировать автоматическое обнаружение - + Channel Канал - + Remove this input source Удалить этот источник входного сигнала - + Custom feedbacks Настраиваемая обратная связь - + Lower Ниже - + Upper Выше @@ -1118,13 +1207,13 @@ Changes will be lost if you don't save them. FixtureBrowser - + Create a new fixture definition Add a new fixture definition - + Edit the selected fixture definition @@ -1132,22 +1221,22 @@ Changes will be lost if you don't save them. FixtureChannelDelegate - + Auto (HTP) Автоматически (Наивысшее значение) - + Auto (LTP) Автоматически (Последнее значение) - + Forced HTP Принудительно наивысшее значение - + Forced LTP Принудительно последнее значение @@ -1178,7 +1267,7 @@ Changes will be lost if you don't save them. - + Save definition as... @@ -1188,27 +1277,32 @@ Changes will be lost if you don't save them. Ошибка - + + Warning + + + + Back to QLC+ - + New definition - + Open definition - + Save definition - + Unknown Неизвестно @@ -1231,32 +1325,32 @@ Changes will be lost if you don't save them. - + Reset the entire group Сбросить всю группу - + Rotate 90° clockwise Повернуть на 90° по часовой стрелке - + Rotate 180° clockwise Повернуть на 180° по часовой стрелке - + Rotate 270° clockwise Повернуть на 270° по часовой стрелке - + Flip horizontally Отразить по горизонтали - + Flip vertically Отразить по вертикали @@ -1264,67 +1358,77 @@ Changes will be lost if you don't save them. FixtureGroupManager - + + Error + Ошибка + + + Add a new fixture group Добавить новую группу приборов - + Remove the selected items Удалить выделенные элементы - + Set a Group/Fixture/Channel search filter Установить фильтр поиска Группы/Прибора/Канала - + Rename the selected items Переименовать выделенные элементы - + Rename items Переименовать элементы - + Inspect the selected item Обследовать выделенный элемент - + Toggle fixtures and channels properties Переключить свойства приборов и каналов - + Add/Remove a linked fixture Добавить/Удалить связанный прибор - + Name Имя - + + Mode + Режим + + + Flags Флаги - + Can fade Может изменяться плавно - + Behaviour Поведение - + Modifier Модификатор @@ -1332,67 +1436,85 @@ Changes will be lost if you don't save them. FixtureManager - - - + + + Head Заголовок - + New group %1 Новая группа %1 - + %1 - Row %2 %1 - Строка %2 - + New filters %1 Новые фильтры %1 + + FixtureNodeDelegate + + + Show/Hide this fixture + + + + + Invert Pan + + + + + Invert Tilt + + + FixtureProperties - + Fixture properties Свойства прибора - + Name Имя - + Universe Область DMX - + Address Адрес - + Quantity Количество - + Channels Каналы - + Gap Промежуток - + Mode Режим @@ -1624,67 +1746,67 @@ Changes will be lost if you don't save them. Установить фильтр поиска Функции - + <None> <Отсутствует> - + New Scene Новая Сцена - + New Chaser Новый Чейзер - + New Sequence Новая Секвенция - + New EFX Новый EFX - + New Collection Новая Коллекция - + New RGB Matrix Новая RGB Матрица - + New Script Новый Скрипт - + New Show Новое Шоу - + New Audio Новое Аудио - + New Video Новое Видео - + (Copy) (Копия) - + New folder Новая папка @@ -1748,31 +1870,31 @@ Changes will be lost if you don't save them. InputOutputManager - + Input/Output Manager Менеджер Ввода/Вывода - + All universes Все Области DMX - - - - - + + + + + Default device Устройство по умолчанию - + Disabled Выключено - + Internal generator Внутренний генератор @@ -1785,10 +1907,123 @@ Changes will be lost if you don't save them. Удалить этот профиль ввода + + InputProfileEditor + + + + + !! Warning !! + + + + + Channel wizard activated + + + + + You have enabled the input channel wizard. After clicking OK, wiggle your mapped input profile's controls. They should appear into the list. Click the wizard button again to stop channel auto-detection.<br><br>Note that the wizard cannot tell the difference between a knob and a slider so you will have to do the change manually. + + + + + Unsaved changes + + + + + Do you wish to save the current profile first? +Changes will be lost if you don't save them. + + + + + Manufacturer + Производитель + + + + Model + Модель + + + + + Type + Тип + + + + MIDI Global Settings + + + + + When MIDI notes are used, send a Note Off when value is 0 + + + + + Channel + Канал + + + + Name + Имя + + + + + Behaviour + Поведение + + + + Generate an extra Press/Release when toggled + + + + + Movement + + + + + Sensitivity + + + + + Custom Feedback + + + + + Lower value + + + + + Upper value + + + + + Button %1 + Кнопка %1 + + + + Slider %1 + Слайдер %1 + + IntensityTool - + Intensity Яркость @@ -1809,17 +2044,17 @@ Changes will be lost if you don't save them. Управление - + Combination Комбинация - + Activate auto detection Активировать автоматическое обнаружение - + Remove this keyboard combination Удалить эту комбинацию клавиш @@ -1832,62 +2067,62 @@ Changes will be lost if you don't save them. Добавить приборы - + Fixture Groups Группы приборов - + Palettes - + Intensity Яркость - + Shutter Затвор - + Position Положение - + Color Цвета - + Color Wheel Колесо цвета - + Gobos Гобо - + Beam Луч - + Pick a 3D point Выбрать трёхмерную точку - + Toggle multiple item selection Переключить множественный выбор элементов - + Select/Deselect all fixtures Выбрать/Отменить выбор всех приборов @@ -1895,40 +2130,45 @@ Changes will be lost if you don't save them. MainView - + Actions Действия - + Fixtures & Functions Приборы и Функции - + Virtual Console Виртуальная консоль - + Simple Desk Простая панель - + Show Manager Менеджер Шоу - + Input/Output Ввод/Вывод - + Off Выкл + + + Stop all the running functions + + MainView2D @@ -1941,27 +2181,27 @@ Changes will be lost if you don't save them. MainView3D - + 3D View 3D Просмотр - + Simple ground Просто земля - + Simple box Просто короб - + Rock stage Рок сцена - + Theatre stage Театральная сцена @@ -1977,38 +2217,58 @@ Changes will be lost if you don't save them. ModeEditor - + Name Имя - - + + Channels Каналы - + + Create a new emitter + + + + + Remove the selected channel(s) + + + + + Drop channels here + + + + Acts on - - Heads + + Emitters + + + + + Remove the selected emitter(s) - + Physical - + Use global settings - + Override global settings @@ -2029,97 +2289,92 @@ Changes will be lost if you don't save them. PaletteFanningBox - - + Flat - - + Linear - - + Square - - + Saw - - + Sine - - - Left to right + + Show/Hide fanning options - - - Right to left + + Create a new palette - - - Top to bottom + + Type + Тип + + + + Layout - - - Bottom to top + + X Ascending - - - Centered + + X Descending - - Show/Hide fanning options + + Y Ascending - - Create a new palette + + Y Descending - - Type - Тип + + Z Ascending + - - Layout + + Z Descending - + Amount - + Value - + Pick the selected color @@ -2152,12 +2407,12 @@ Changes will be lost if you don't save them. - + Are you sure you want to delete the following items? Вы уверены, что хотите удалить следующие элементы? - + Delete items Удалить элементы @@ -2171,8 +2426,8 @@ Changes will be lost if you don't save them. - - + + Type Тип @@ -2182,83 +2437,83 @@ Changes will be lost if you don't save them. - + Colour Temp (K) - + Lens Объектив - + Min Degrees - + Max Degrees - + Head(s) - + Pan Max Degrees - + Tilt Max Degrees - + Layout (Columns x Rows) - + Dimensions - + Weight Вес - + Width Ширина - + Height Высота - + Depth Глубина - + Electrical - + Power Consumption - + DMX Connector @@ -2271,85 +2526,261 @@ Changes will be lost if you don't save them. Информация - - and contributors - и помощники + + and contributors + и помощники + + + + Website + Веб-сайт + + + + This application is licensed under the terms of the + Это приложение лицензируется в соответствии с условиями + + + + Apache 2.0 license + лицензии Apache 2.0 + + + + PopupChannelModifiers + + + Channel Modifiers Editor + + + + + Insert a modified value after the selected + + + + + Delete the selected modifier value + + + + + Rename the selected modifier template + + + + + Save the selected modifier template + + + + + Templates + + + + + Original DMX value + + + + + Modified DMX value + + + + + PopupChannelWizard + + + Fixture Editor Wizard + + + + + Properties + + + + + Start + + + + + Width + Ширина + + + + Amount + + + + + Type + Тип + + + + Red + Красный + + + + Green + Зелёный + + + + Blue + Синий + + + + White + Белый + + + + Amber + Янтарный + + + + UV + УФ + + + + RGB + + + + + RGBW + + + + + RGBAW + + + + + Dimmer + + + + + Pan + Панорама + + + + Tilt + Наклон + + + + Color Macro + + + + + Shutter + Затвор + + + + Beam + Луч + + + + Effect + Эффект - - Website - Веб-сайт + + Label + Название - - This application is licensed under the terms of the - Это приложение лицензируется в соответствии с условиями + + Capability # + - - Apache 2.0 license - лицензии Apache 2.0 + + Channel # + + + + + Preview + PopupCreatePalette - + Create a new palette - + Dimmer - + Color - + Position Положение - + Shutter Затвор - + Gobo Гобо - + Palette name - + New Palette - + Type Тип - + Also create a Scene - + Scene name Имя сцены - + New Scene @@ -2357,87 +2788,87 @@ Changes will be lost if you don't save them. PopupDMXDump - + Enter a name for the scene Введите имя сцены - + Scene name Имя сцены - + New Scene Новая Сцена - + Don't ask again Не спрашивать снова - + Available channel types Доступные типы каналов - + Intensity Яркость - + RGB/CMY/WAUV RGB/CMY/WAUV - + Color macros Цветовые макросы - + Gobo Гобо - + Pan Панорама - + Tilt Наклон - + Speed Скорость - + Shutter/Strobe Затвор/Стробоскоп - + Prism Призма - + Beam Луч - + Effect Эффект - + Maintenance Обслуживание @@ -2445,7 +2876,7 @@ Changes will be lost if you don't save them. PopupDisclaimer - + Disclaimer Отказ от отвественности @@ -2468,6 +2899,54 @@ Changes will be lost if you don't save them. Функции + + PopupInputChannelEditor + + + Input Channel Editor + + + + + Input Channel + + + + + Number + Номер + + + + Name + Имя + + + + Type + Тип + + + + Channel + Канал + + + + Message + + + + + Parameter + + + + + Note + Примечание + + PopupManualInputSource @@ -2532,27 +3011,27 @@ Changes will be lost if you don't save them. PopupNetworkClient - + Disconnected Отключен - + Waiting for access Ожидание доступа - + Downloading project Загрузка проекта - + Connected Подключен - + QLC+ client setup Настройка клиента QLC+ @@ -2595,7 +3074,7 @@ Changes will be lost if you don't save them. PopupNetworkConnect - + Client access request Клиентский запрос доступа @@ -2725,12 +3204,12 @@ Access level: PopupPINRequest - + Page PIN PIN страницы - + Remember for this session Запомнить для этой сессии @@ -2738,22 +3217,22 @@ Access level: PopupPINSetup - + Current PIN Текущий PIN - + New PIN Новый PIN - + Confirm PIN Потвердить PIN - + New PIN mismatch Новый PIN не совпадает @@ -2761,22 +3240,22 @@ Access level: PopupRenameItems - + New name Новое имя - + Enable numbering Включить нумерацию - + Start number Начальный номер - + Digits Цифры @@ -2784,28 +3263,76 @@ Access level: PositionTool - + Position Положение - + Rotate 90° clockwise Повернуть на 90° по часовой стрелке - - + + Snap to the previous value Привязать к предыдущему значению - - + + Snap to the next value Привязать к следующему значению + + ProfilesList + + + !! Warning !! + + + + + Save this profile + + + + + Toggle the automatic detection procedure + + + + + Add a new channel + + + + + Create a new input profile + + + + + Edit the selected channel + + + + + Edit the selected input profile + + + + + Delete the selected channel + + + + + Delete the selected input profile(s) + + + RGBMatrixEditor @@ -2819,214 +3346,214 @@ Access level: Шаблон - + Blend mode Режим смешивания - + Default (HTP) По умолчанию (Наивысшее значение) - + Mask Маска - + Additive Сложение - + Subtractive Вычитание - + Color mode - + Default (RGB) - + White Белый - + Amber Янтарный - + UV УФ - + Dimmer - + Shutter Затвор - + Colors Цвета - + Parameters Параметры - + Speed Скорость - + Steps fade in Пошаговое появление - + Steps hold Пошаговое удержание - + Steps fade out Пошаговое исчезание - + Tempo type Тип темпа - + Time Время - + Beats Удары - + Order and direction Порядок и направление - + Loop Цикл - + Single Shot Одиночный - + Ping Pong Пинг Понг - + Run Order Порядок запуска - + Forward Вперед - + Backward Назад - + Direction Направление - + Text Текст - + Please choose a font Пожалуйста, выберите шрифт - - - + + + Animation Анимация - + Letters Буквы - - + + Horizontal Горизонтально - - + + Vertical Вертикально - - + + Offset Смещение - - + + X X - - + + Y Y - + Image Изображение - + Select an image Выберите изображение - + Static Статично @@ -3034,17 +3561,17 @@ Access level: RGBPanelProperties - + RGB panel properties Свойства RGB панели - + Name Имя - + Universe Область DMX @@ -3203,7 +3730,7 @@ Access level: Скопировать на новую Сцену - + Function Preview Предпросмотр Функции @@ -3216,43 +3743,43 @@ Access level: SceneEditor - + Add a fixture/group - + Add a palette - + Remove the selected items Remove the selected fixtures Удалить выбранные приборы - + Delete items Удалить элементы - + Are you sure you want to remove the selected items? - + Speed Скорость - + Fade in Появление - + Fade out Исчезание @@ -3260,67 +3787,67 @@ Access level: ScriptEditor - + Add a method call at cursor position Добавить вызов метода в позицию курсора - + Show/hide functions tree Показать/скрыть дерево функций - + Show/hide fixture tree Показать/скрыть дерево приборов - + Check the script syntax Проверить синтаксис скрипта - + Syntax check Проверка синтаксиса - + Start function Запустить функцию - + Stop function Остановить функцию - + Set fixture channel Назначить канал прибора - + Wait time Время ожидания - + Random number Случайное число - + Blackout Полная темнота - + System command Системная команда - + File path Путь к файлу @@ -3348,12 +3875,12 @@ Access level: Удалить выделенные приборы - + Steps Шаги - + Fixtures Приборы @@ -3361,107 +3888,122 @@ Access level: SettingsView2D - + Environment Окружение - + Width Ширина - + Height Высота - + Depth Глубина - + Grid units Единица измерения сетки - + Meters Метры - + Feet Футы - + Point of view Точка обзора - + Top view Сверху - + Front view Вид спереди - + Right side view Вид справа - + Left side view Вид слева + Custom Background + + + + + Select an image + Выберите изображение + + + + Reset background + + + + Selected fixtures Выделенные приборы - + Gel color Цвет геля - + Rotation Поворот - + Alignment Выравнивание - + Align the selected items to the left Выровнять выделенные элементы влево - + Align the selected items to the top Выровнять выделенные элементы вверх - + Distribution Распределение - + Equally distribute horizontally the selected items Равномерно распределить выбранные элементы по горизонтали - + Equally distribute vertically the selected items Равномерно распределить выбранные элементы по вертикали @@ -3469,122 +4011,126 @@ Access level: SettingsView3D - + Environment Окружение - + Type Тип - + Width Ширина - + Height Высота - + Depth Глубина - + Rendering - + Quality - + Low - + Medium - + High - + Ultra - + Ambient light - + Smoke amount - + Show FPS - + Position Положение - + Rotation - + Scale - + Custom items - + Select a mesh file - + 3D files - + All files Все файлы - + + Normalize the selected items + + + Actions - Действия + Действия - + Add a new item to the scene - + Remove the selected items @@ -3610,16 +4156,16 @@ Access level: ShowItem - - - + + + Position: Положение: - - - + + + Duration: Продолжительность: @@ -3637,74 +4183,74 @@ Access level: Показать цвет элементов - + Unlock the selected items Разблокировать выделенные элементы - + Lock the selected items Заблокировать выделенные элементы - + Snap to grid Привязка к сетке - + Stretch the original function Растянуть исходную функцию - + Remove the selected items Удалить выделенные элементы - + Are you sure you want to remove the following items? (Note that the original functions will not be deleted) Вы действительно хотите удалить следующие элементы? (Обратите внимание, что исходные функции не будут удалены) - + Delete show items Удалить отображаемые элементы - + Copy the selected items in the clipboard Скопировать выделенные элементы в буфер обмена - + Paste items in the clipboard at cursor position Вставить элементы из буфера обмена в позицию курсора - + Play or resume Воспроизвести или продолжить - + Stop or rewind Остановить или перемотать - + Move the selected track up Переместить выбранную дорожку вверх - + Move the selected track down Переместить выбранную дорожку вниз - + Create a new track Создать новую дорожку @@ -3714,19 +4260,19 @@ Access level: Менеджер Шоу - + New Show Новое Шоу - - + + Track %1 Дорожка %1 - - + + (Copy) (Копия) @@ -3734,37 +4280,37 @@ Access level: SimpleDesk - + Universe Область DMX - + Reset the whole universe - + Dump on a new Scene Скопировать на новую Сцену - + Reset the channel - + Fixture List - + Commands history - + Simple Desk @@ -3780,25 +4326,202 @@ Access level: TrackDelegate - + Solo this track Соло - + Mute this track Отключить эту дорожку + + UISettingsEditor + + + + Reset to default + + + + + Scaling factor + + + + + Background darker + + + + + Background dark + + + + + Background medium + + + + + Background light + + + + + Background lighter + + + + + Controls background + + + + + Foreground main + + + + + Foreground medium + + + + + Foreground light + + + + + Toolbar gradient start + + + + + Sub-toolbar gradient start + + + + + Toolbar gradient end + + + + + Toolbar hover gradient start + + + + + Toolbar hover gradient end + + + + + Toolbar selection + + + + + Sub-toolbar selection + + + + + Section header + + + + + Section header divider + + + + + Item highlight + + + + + Item highlight pressed + + + + + Item hover + + + + + Item selection + + + + + VC Frame drop area + + + + + Item dark border + + + + + Save to file + + + + + Operation completed + + + + + Error + Ошибка + + + + UniverseGridView + + + Error + Ошибка + + + + Unable to perform the operation. +There is either not enough space or the target universe in invalid + + + + + Cut the selected items into clipboard + + + + + Paste items in the clipboard at the first available position + + + UniverseIOItem - Passthrough - Сквозной + Сквозной + + + + Enable/Disable passthrough + - + Enable/Disable feedbacks Включить/Отключить обратную связь @@ -4010,74 +4733,74 @@ Access level: Добавить расписание фукнций - + Remove this schedule Удалить это расписание - + Start time Время начала - + Stop time Время остановки - + Enable the stop time Включить время остановки - + M As in Monday Пн - + T As in Tuesday Вт - + W As in Wednesday Ср - + T As in Thursday Чт - + F As in Friday Пт - + S As in Saturday Сб - + S As in Sunday Вс - + Repeat weekly Повторять еженедельно - + Add a new schedule Добавить новое расписание @@ -4085,17 +4808,17 @@ Access level: VCCueList - + Next Cue Следующий Cue - + Previous Cue Предыдущий Cue - + Play/Stop/Pause Воспроизведение/Стоп/Пауза @@ -4108,17 +4831,17 @@ Access level: Правый Кроссфейд - + Stop/Pause Стоп/Пауза - + Side Fader - + Cue List %1 Список Cue %1 @@ -4126,27 +4849,32 @@ Access level: VCCueListItem - + Play/Pause Воспроизведение/Пауза - + + Play/Stop + + + + Pause Пауза - + Stop Стоп - + Previous cue Предыдущий Cue - + Next cue Следующий Cue @@ -4239,30 +4967,35 @@ Access level: VCFrame - + Next Page Следущая Страница - + Previous Page Предыдущая Страница - + Enable Включено - + Collapse Свернуть - + Frame %1 Кадр %1 + + + Page %1 + Страница %1 + VCFrameItem @@ -4277,17 +5010,16 @@ Access level: Включить/отключить этот кадр - + Previous page Предыдущая страница - Page - Страница + Страница - + Next page Следующая страница @@ -4330,10 +5062,21 @@ Access level: Номера страниц - + Clone first page widgets Склонировать виджеты первой страницы + + + + Shortcuts + + + + + Shortcut name + + VCLabel @@ -4346,12 +5089,12 @@ Access level: VCPage - + Page %1 Страница %1 - + Virtual Console Page %1 Виртуальная Консоль Страница %1 @@ -4359,57 +5102,57 @@ Access level: VCPageProperties - + Width Ширина - + Height Высота - + Security Безопасность - + Set a PIN Установите PIN - + Error Ошибка - + The entered PINs are either invalid or incorrect Введённый PIN некорректен или неверен - + Add page to the left Добавить страницу влево - + Add page to the right Добавить страницу вправо - + Delete this page Удалить эту страницу - + Delete page Удалить страницу - + Are you sure you want to delete the selected page? Вы действительно хотите удалить выделенную страницу? @@ -4474,12 +5217,12 @@ Access level: Сброс - + Slider %1 Слайдер %1 - + Knob %1 Ручка %1 @@ -4552,82 +5295,82 @@ Access level: Атрибут - + Level mode Режим уровня - + Channels Каналы - + Add/Remove channels Добавить/Удалить каналы - + Click & Go button Кнопка Click & Go - + None Ничего - + RGB/CMY RGB/CMY - + Gobo/Effect/Macro Гобо/Эффект/Макро - + Monitor channel levels Мониторинг уровней каналов - + Values range Диапазон значений - + Upper limit Верхний предел - + Lower limit Нижний предел - + Grand Master mode Режим Гранд Мастер - + Reduce values Уменьшить значения - + Limit values Предельные значения - + Intensity channels Каналы интенсивности - + All channels Все каналы @@ -4704,8 +5447,8 @@ Access level: Неизвестно - - + + None Ничего @@ -4713,87 +5456,87 @@ Access level: VCWidgetProperties - + Select a widget first Сначала выберите виджет - + Settings Настройки - + External controls Внешние элементы управления - + Basic properties Базовые компоненты - + Label Название - + Background color Фоновый цвет - + Foreground color Цвет переднего плана - + Font Шрифт - + Please choose a font Выберите шрифт - + Background image Фоновое изображение - + Select an image Выберите изображение - + Alignment Выравнивание - + Align the selected widgets to the left Выровнять выбранные виджеты влево - + Align the selected widgets to the right Выровнять выбранные виджеты вправо - + Align the selected widgets to the top Выровнять выбранные виджеты вверх - + Align the selected widgets to the bottom Выровнять выбранные виджеты вниз - + External Controls Внешние Элементы управления @@ -4871,77 +5614,77 @@ Access level: Экран вывода - + Output mode Режим вывода - + Windowed Оконный - + Fullscreen Полноэкранный - + Geometry Геометрия - + Original Оригинал - + Custom Настроенный - + Position Положение - + Size Размер - + W Ш - + H В - + Rotation Поворот - + X X - + Y Y - + Z Z - + Layer @@ -4949,72 +5692,72 @@ Access level: VirtualConsole - + Error Ошибка - + Invalid PIN entered Введён неверный PIN - + Enable/Disable widgets snapping Включить/отключить привязку виджетов - + Widget matrix setup Настройка матрицы виджета - + Columns Столбцы - + Rows Строки - + Width Ширина - + Height Высота - + Frame type Тип рамки - + Normal Нормальный - + Solo Соло - + Virtual Console Виртуальная Консоль - + <None> <Ничего> - + Page %1 Страница %1 diff --git a/qmlui/qlcplus_uk_UA.ts b/qmlui/qlcplus_uk_UA.ts index b0e4c369ab..7917560204 100644 --- a/qmlui/qlcplus_uk_UA.ts +++ b/qmlui/qlcplus_uk_UA.ts @@ -4,54 +4,58 @@ ActionsMenu - Open a project - Відкрити проект + Відкрити проект - - + + Project files Файли проекту - - - + + + All files Усі файлы - + + Open a file + + + + QLC+ files - - + + Import from project Імпорт з проекту - - + + Save project as... Зберегти проект як... - + Your project has changes Ваш проект має незбереженні зміни - + Do you wish to save the current project first? Changes will be lost if you don't save them. Ви бажаєте спочатку зберегти проект? Зміни будуть втрачені, якщо їх не зберегти. - + New project Новий проект @@ -60,117 +64,122 @@ Changes will be lost if you don't save them. Відкрити проект - + Open file - + Save project Зберегти проект - + Undo Відмінити - + Redo Повторити - + Network Мережа - + Server setup Налаштування сервера - + Client setup Налаштування клієнта - + Address tool Адресний помічник - + DMX Address tool Помічник адрес DMX - + + UI Settings + + + + Toggle fullscreen Переключити повноекранний режим - + Language Мова - + Catalan Каталонська - + Dutch Нідерландська - + English Англійська - + French Французька - + German Німецька - + Italian Італійська - + Japanese Японська - + Polish - + Russian - + Spanish Іспанська - + Ukrainian - + About Що до програми @@ -291,12 +300,17 @@ Changes will be lost if you don't save them. Вхідний пристрій - + + Volume + + + + Fade in Плавна поява - + Fade out Плавне зникнення @@ -312,20 +326,25 @@ Changes will be lost if you don't save them. BeamTool - + Beam Промінь - + Beam degrees Розкриття проміня - + Distance Відстань + + + Projected diameter + + BottomPanel @@ -338,8 +357,8 @@ Changes will be lost if you don't save them. ChannelEdit - - + + Custom Налаштований @@ -347,88 +366,118 @@ Changes will be lost if you don't save them. ChannelEditor - + + Open a picture file + + + + + Gobo pictures + + + + + All files + + + + Name Ім'я - + Preset - - + + Type Тип - + Role - + Coarse (MSB) - + Fine (LSB) - + Default value - + Delete the selected capabilities - + + Capability wizard + + + + + Empty description provided + + + + + Overlapping with another capability + + + + From - + To - + Description - + Preview - + Primary color - + Secondary color - + Value(s) - + Value 1 - + Value 2 @@ -436,122 +485,137 @@ Changes will be lost if you don't save them. ChaserEditor - + Add a new step Додати новий крок - + Remove the selected steps Видалити виділені кроки - + Delete steps Видалити кроки - + Are you sure you want to remove the selected steps? Ви дійсно бажаєте видалити виділені кроки? - + + Preview the previous step + + + + + Preview the next step + + + + + Duplicate the selected step(s) + + + + Print the Chaser steps Показати кроки Чейзера - + Run properties Властивості запуску - + Loop Цикл - + Single Shot Одине проведення - + Ping Pong Пинг Понг - + Random Випадково - + Run Order Порядок запуску - + Forward Уперед - + Backward Назад - + Direction Напрям - + Time Час - + Beats Удари - + Tempo Темп - - + + Default За замовчуванням - - - + + + Common Загальний - - - + + + Per Step За крок - + Fade In Плавна поява - + Fade Out Плавне зникнення - + Duration Тривалість @@ -559,32 +623,32 @@ Changes will be lost if you don't save them. ChaserWidget - + Function Функція - + Fade In Плавна поява - + Hold Закрепити - + Fade Out Плавне зникнення - + Duration Тривалість - + Note Примітка @@ -592,22 +656,22 @@ Changes will be lost if you don't save them. CollectionEditor - + Add a function Додати функцію - + Remove the selected function Видалити виділені функції - + Delete functions Видалити функцію - + Are you sure you want to remove the selected functions? Ви дійсно бажаєте видалити виділені функції? @@ -615,17 +679,17 @@ Changes will be lost if you don't save them. ColorTool - + Basic Базовий - + Full Повний - + Filters Фільтри @@ -633,7 +697,7 @@ Changes will be lost if you don't save them. ColorToolBasic - + Selected color Виділені кольори @@ -641,88 +705,88 @@ Changes will be lost if you don't save them. ColorToolFilters - + Open filters menu Відкрити меню фільтров - + Cyan Бірюзовий - + Red Червоний - + White Білий - + Magenta Пурпурний - + Green Зелений - + Amber Бурштиновий - + Yellow Жовтий - + Blue Синій - + UV Ультрафіолет УФ - + CMY CMY - + Add a new color filters file Додати новий файл колірних фільтрів - + Rename the current color filters file Перейменувати поточний файл колірних фільтрів - + Save the current color filters file Зберегти поточний файл колірних фільтрів - + Add a new filter Додати новий фільтр - + Delete the selected filter Видалити виділені фільтри - + Paste the latest picked color as new filter Вставити останній вибраний колір в якості нового фільтра @@ -730,38 +794,38 @@ Changes will be lost if you don't save them. ColorToolFull - + Red Червоний - + Green Зелений - + Blue Синій - + White Білий - + Amber Бурштиновий - + UV Ультрафіолет УФ - + Selected color Виділений колір @@ -769,12 +833,12 @@ Changes will be lost if you don't save them. ContextManager - + Universe Grid View Передгяд мережі Universe - + linked з'єднаний @@ -805,154 +869,174 @@ Changes will be lost if you don't save them. EFXEditor - + Fixtures Світлові прилади - + Add a fixture/head Додати прилад - + Remove the selected fixture head(s) Видалити виділений прилад(и) - + Fixture Світлові прилади - + + Mode + Режим + + + Reverse Зворотній - - + + Start offset Зсув початку - + + Position + Положення + + + + Dimmer + + + + + RGB + + + + Add a new fixture Додати новий прилад - - + + Pattern Шаблон - + Relative movement Відносний рух - + Width Ширина - + Height Висота - + X offset X зміщення - + Y offset Y зміщення - + Rotation Обертання - + X frequency X частота - + Y frequency Y частота - + X phase X фаза - + Y phase Y фаза - + Speed Швидкість - + Fade in Плавна поява - Hold - Закріпити + Закріпити - + Fade out Плавне зникнення - + Order and direction Порядок та напрямок - + + Loop Цикл - + Single Shot Одне проведення - + Ping Pong Пинг Понг - + Run Order Порядок запуску - + Forward Уперед - + Backward Назад - + Direction Напрямок @@ -960,7 +1044,7 @@ Changes will be lost if you don't save them. EditorTopBar - + Go back to the previous view Go back to the Function Manager Повернутися назад до Менеджеру Функцій @@ -979,77 +1063,82 @@ Changes will be lost if you don't save them. - + General - + Manufacturer Виробник - + Type Тип - + Model Модель - + Author Автор - + Physical properties - + Channels Канали - + Add a new channel - + Remove the selected channel(s) - + + Channel wizard + + + + Modes - + Add a new mode - + Remove the selected mode(s) - + Aliases - + New channel %1 - + New mode @@ -1062,37 +1151,37 @@ Changes will be lost if you don't save them. Управління - + Universe Universe - + Activate auto detection Активувати автоматичне виявлення - + Channel Канал - + Remove this input source Видалити це джерело вхідного сигналу - + Custom feedbacks Налаштований зворотній зв'язок - + Lower Нижче - + Upper Вище @@ -1118,13 +1207,13 @@ Changes will be lost if you don't save them. FixtureBrowser - + Create a new fixture definition Add a new fixture definition - + Edit the selected fixture definition @@ -1132,22 +1221,22 @@ Changes will be lost if you don't save them. FixtureChannelDelegate - + Auto (HTP) Автоматично (Найвище значення HTP) - + Auto (LTP) Автоматично (Останнє значення LTP) - + Forced HTP Примусово найвище значення HTP - + Forced LTP Примусово останнє значення LTP @@ -1178,7 +1267,7 @@ Changes will be lost if you don't save them. - + Save definition as... @@ -1188,27 +1277,32 @@ Changes will be lost if you don't save them. Помилка - + + Warning + + + + Back to QLC+ - + New definition - + Open definition - + Save definition - + Unknown Невідомо @@ -1231,32 +1325,32 @@ Changes will be lost if you don't save them. - + Reset the entire group Скинути всю групу - + Rotate 90° clockwise Повернути на 90 ° за годинниковою стрілкою - + Rotate 180° clockwise Повернути на 180 ° за годинниковою стрілкою - + Rotate 270° clockwise Повернути на 270 ° за годинниковою стрілкою - + Flip horizontally Відобразити по горизонталі - + Flip vertically Відобразити по вертикалі @@ -1264,67 +1358,77 @@ Changes will be lost if you don't save them. FixtureGroupManager - + + Error + Помилка + + + Add a new fixture group Додати нову групу приладів - + Remove the selected items Видалити виділені елементи - + Set a Group/Fixture/Channel search filter Встановити фільтр пошуку Групи / Прилада / Каналу - + Rename the selected items Перейменувати виділені елементи - + Rename items Перейменувати елементи - + Inspect the selected item Обстежити виділений елемент - + Toggle fixtures and channels properties Переключити властивості приладів та каналів - + Add/Remove a linked fixture Додати / Видалити з'єднаний прилад - + Name Ім'я - + + Mode + Режим + + + Flags Прапори - + Can fade Може змінюватися повільно - + Behaviour Поведінка - + Modifier Модефікатор @@ -1332,67 +1436,85 @@ Changes will be lost if you don't save them. FixtureManager - - - + + + Head Голова - + New group %1 Нова група %1 - + %1 - Row %2 %1 - Рядок %2 - + New filters %1 Нові фільтри %1 + + FixtureNodeDelegate + + + Show/Hide this fixture + + + + + Invert Pan + + + + + Invert Tilt + + + FixtureProperties - + Fixture properties Властивості приладу - + Name Ім'я - + Universe Universe - + Address Адреса - + Quantity Кількість - + Channels Канали - + Gap Проміжок - + Mode Режим @@ -1624,67 +1746,67 @@ Changes will be lost if you don't save them. Встановіти фільтр пошуку функцій - + <None> <Відсутнє> - + New Scene Нова Сцена - + New Chaser Новий Чейзер - + New Sequence Нова Послідовність - + New EFX Новий EFX - + New Collection Нова Колекція - + New RGB Matrix Нова RGB Матриця - + New Script Новий Скрипт - + New Show Нове Шоу - + New Audio Нове Аудіо - + New Video Нове Відео - + (Copy) (Копія) - + New folder Нова папка @@ -1748,31 +1870,31 @@ Changes will be lost if you don't save them. InputOutputManager - + Input/Output Manager Вхідний/Вихідний Менеджер - + All universes Усі Universe - - - - - + + + + + Default device Пристрій за замовчуванням - + Disabled Вимкнено - + Internal generator Внутрішній генератор @@ -1785,10 +1907,123 @@ Changes will be lost if you don't save them. Видалити цей вхідний профіль + + InputProfileEditor + + + + + !! Warning !! + + + + + Channel wizard activated + + + + + You have enabled the input channel wizard. After clicking OK, wiggle your mapped input profile's controls. They should appear into the list. Click the wizard button again to stop channel auto-detection.<br><br>Note that the wizard cannot tell the difference between a knob and a slider so you will have to do the change manually. + + + + + Unsaved changes + + + + + Do you wish to save the current profile first? +Changes will be lost if you don't save them. + + + + + Manufacturer + Виробник + + + + Model + Модель + + + + + Type + Тип + + + + MIDI Global Settings + + + + + When MIDI notes are used, send a Note Off when value is 0 + + + + + Channel + Канал + + + + Name + Ім'я + + + + + Behaviour + Поведінка + + + + Generate an extra Press/Release when toggled + + + + + Movement + + + + + Sensitivity + + + + + Custom Feedback + + + + + Lower value + + + + + Upper value + + + + + Button %1 + Кнопка %1 + + + + Slider %1 + Слайдер %1 + + IntensityTool - + Intensity Яскравість @@ -1809,17 +2044,17 @@ Changes will be lost if you don't save them. Управління - + Combination Комбінація - + Activate auto detection Активувати автоматичне виявлення - + Remove this keyboard combination Видалити цю комбінацію клавіш @@ -1832,62 +2067,62 @@ Changes will be lost if you don't save them. Додати прилади - + Fixture Groups Групи приладів - + Palettes - + Intensity Яскравість - + Shutter Затвор - + Position Положення - + Color Колір - + Color Wheel Колесо коліру - + Gobos Гобо - + Beam Промінь - + Pick a 3D point Выбрати трьохмірну точку - + Toggle multiple item selection Переключити множинний вибір елементів - + Select/Deselect all fixtures Вибрати / Скасувати вибір всіх приладів @@ -1895,40 +2130,45 @@ Changes will be lost if you don't save them. MainView - + Actions Дійство - + Fixtures & Functions Прилади та Функції - + Virtual Console Віртуальна консоль - + Simple Desk Проста панель - + Show Manager Менеджер Шоу - + Input/Output Вхідні/Вихідні - + Off Вимкнути + + + Stop all the running functions + + MainView2D @@ -1941,27 +2181,27 @@ Changes will be lost if you don't save them. MainView3D - + 3D View 3D Перегляд - + Simple ground Просто земля - + Simple box Проста коробка - + Rock stage Рок сцена - + Theatre stage Театральна сцена @@ -1977,38 +2217,58 @@ Changes will be lost if you don't save them. ModeEditor - + Name Ім'я - - + + Channels Канали - + + Create a new emitter + + + + + Remove the selected channel(s) + + + + + Drop channels here + + + + Acts on - - Heads + + Emitters + + + + + Remove the selected emitter(s) - + Physical Фізичні властивості - + Use global settings - + Override global settings @@ -2029,97 +2289,92 @@ Changes will be lost if you don't save them. PaletteFanningBox - - + Flat - - + Linear - - + Square - - + Saw - - + Sine - - - Left to right + + Show/Hide fanning options - - - Right to left + + Create a new palette - - - Top to bottom + + Type + Тип + + + + Layout - - - Bottom to top + + X Ascending - - - Centered + + X Descending - - Show/Hide fanning options + + Y Ascending - - Create a new palette + + Y Descending - - Type - Тип + + Z Ascending + - - Layout + + Z Descending - + Amount - + Value - + Pick the selected color @@ -2152,12 +2407,12 @@ Changes will be lost if you don't save them. - + Are you sure you want to delete the following items? Ви впевнені, що бажаєте видалити наступні елементи? - + Delete items Видалити елементи @@ -2171,8 +2426,8 @@ Changes will be lost if you don't save them. - - + + Type Тип @@ -2182,83 +2437,83 @@ Changes will be lost if you don't save them. - + Colour Temp (K) - + Lens Об'єктив - + Min Degrees - + Max Degrees - + Head(s) - + Pan Max Degrees - + Tilt Max Degrees - + Layout (Columns x Rows) - + Dimensions - + Weight Вага - + Width Ширина - + Height Висота - + Depth Глибина - + Electrical - + Power Consumption - + DMX Connector @@ -2271,85 +2526,261 @@ Changes will be lost if you don't save them. Информація - - and contributors - та помічники + + and contributors + та помічники + + + + Website + Веб-сайт + + + + This application is licensed under the terms of the + Ця програма ліцензується відповідно до умов + + + + Apache 2.0 license + лицензії Apache 2.0 + + + + PopupChannelModifiers + + + Channel Modifiers Editor + + + + + Insert a modified value after the selected + + + + + Delete the selected modifier value + + + + + Rename the selected modifier template + + + + + Save the selected modifier template + + + + + Templates + + + + + Original DMX value + + + + + Modified DMX value + + + + + PopupChannelWizard + + + Fixture Editor Wizard + + + + + Properties + + + + + Start + + + + + Width + Ширина + + + + Amount + + + + + Type + Тип + + + + Red + Червоний + + + + Green + Зелений + + + + Blue + Синій + + + + White + Білий + + + + Amber + Бурштиновий + + + + UV + УФ + + + + RGB + + + + + RGBW + + + + + RGBAW + + + + + Dimmer + + + + + Pan + Панорама + + + + Tilt + Нахил + + + + Color Macro + + + + + Shutter + Затвор + + + + Beam + Промінь + + + + Effect + Ефект + + + + Label + - - Website - Веб-сайт + + Capability # + - - This application is licensed under the terms of the - Ця програма ліцензується відповідно до умов + + Channel # + - - Apache 2.0 license - лицензії Apache 2.0 + + Preview + PopupCreatePalette - + Create a new palette - + Dimmer - + Color Колір - + Position Положення - + Shutter Затвор - + Gobo Гобо - + Palette name - + New Palette - + Type Тип - + Also create a Scene - + Scene name Ім'я сцени - + New Scene @@ -2357,87 +2788,87 @@ Changes will be lost if you don't save them. PopupDMXDump - + Enter a name for the scene Введіть ім'я сцени - + Scene name Ім'я сцени - + New Scene Нова Сцена - + Don't ask again Не питати знову - + Available channel types Доступні типи каналів - + Intensity Яскравість - + RGB/CMY/WAUV RGB/CMY/WAUV - + Color macros Макроси кольору - + Gobo Гобо - + Pan Панорама - + Tilt Нахил - + Speed Швидкість - + Shutter/Strobe Затвор/Стробоскоп - + Prism Призма - + Beam Промінь - + Effect Ефект - + Maintenance Технічне Обслуговування @@ -2445,7 +2876,7 @@ Changes will be lost if you don't save them. PopupDisclaimer - + Disclaimer Відмома від відповідальності @@ -2468,6 +2899,54 @@ Changes will be lost if you don't save them. Функції + + PopupInputChannelEditor + + + Input Channel Editor + + + + + Input Channel + + + + + Number + Нумер + + + + Name + Ім'я + + + + Type + Тип + + + + Channel + Канал + + + + Message + + + + + Parameter + + + + + Note + Примітка + + PopupManualInputSource @@ -2532,27 +3011,27 @@ Changes will be lost if you don't save them. PopupNetworkClient - + Disconnected Відключен - + Waiting for access Очікування доступу - + Downloading project Завантаження проекту - + Connected Підключен - + QLC+ client setup Налаштування клієнта QLC + @@ -2595,7 +3074,7 @@ Changes will be lost if you don't save them. PopupNetworkConnect - + Client access request Клієнтський запит доступу @@ -2725,12 +3204,12 @@ Access level: PopupPINRequest - + Page PIN PIN-код сторінки - + Remember for this session Запом'ятати для цієї сесії @@ -2738,22 +3217,22 @@ Access level: PopupPINSetup - + Current PIN Поточний PIN-код - + New PIN Новий PIN-код - + Confirm PIN Підтвердити PIN-код - + New PIN mismatch Новий PIN-код не збігається @@ -2761,22 +3240,22 @@ Access level: PopupRenameItems - + New name Нове ім'я - + Enable numbering Увімкнути нумерацію - + Start number Початковий номер - + Digits Цифри @@ -2784,28 +3263,76 @@ Access level: PositionTool - + Position Положення - + Rotate 90° clockwise Повернути на 90 ° за годинниковою стрілкою - - + + Snap to the previous value Прив'язати до попереднього значення - - + + Snap to the next value Прив'язати до наступного значення + + ProfilesList + + + !! Warning !! + + + + + Save this profile + + + + + Toggle the automatic detection procedure + + + + + Add a new channel + + + + + Create a new input profile + + + + + Edit the selected channel + + + + + Edit the selected input profile + + + + + Delete the selected channel + + + + + Delete the selected input profile(s) + + + RGBMatrixEditor @@ -2819,187 +3346,187 @@ Access level: Шаблон - + Blend mode Режим змішування - + Default (HTP) За замовчуванням (Найвище значення HTP) - + Mask Маска - + Additive Додавання - + Subtractive Віднімання - + Color mode - + Default (RGB) - + White Білий - + Amber Бурштиновий - + UV УФ - + Dimmer - + Shutter Затвор - + Colors Кольори - + Parameters Параметри - + Speed Швидкість - + Steps fade in Покрокова поява - + Steps hold Покрокове утримання - + Steps fade out Покрокове зникнення - + Tempo type Тип темпу - + Time Час - + Beats Удари - + Order and direction Порядок та напрям - + Loop Цикл - + Single Shot Одне проведення - + Ping Pong Пинг Понг - + Run Order Порядок запуску - + Forward Уперед - + Backward Назад - + Direction Напрям - + Text Текст - + Please choose a font Будь ласка, виберіть шрифт - - - + + + Animation Анімація - + Letters Літери - - + + Horizontal Горизонтально - - + + Vertical Вертикально - - + + Offset @@ -3008,19 +3535,19 @@ Access level: Зсув - - + + X X - - + + Y Y - + Image Зображення @@ -3030,12 +3557,12 @@ Access level: Виберіть зображення - + Select an image Выберить зображення - + Static Статично @@ -3043,17 +3570,17 @@ Access level: RGBPanelProperties - + RGB panel properties Властивості панелі RGB - + Name Ім'я - + Universe Universe @@ -3212,7 +3739,7 @@ Access level: Скопіювати на нову Сцену - + Function Preview Попередній перегляд Функції @@ -3225,43 +3752,43 @@ Access level: SceneEditor - + Add a fixture/group - + Add a palette - + Remove the selected items Remove the selected fixtures Видалити вибрані прилади - + Delete items Видалити елементи - + Are you sure you want to remove the selected items? - + Speed Швидкість - + Fade in Плавна Поява - + Fade out Плавне зникнення @@ -3269,67 +3796,67 @@ Access level: ScriptEditor - + Add a method call at cursor position Додати виклик методу у позиції курсора - + Show/hide functions tree Показати / сховати дерево функцій - + Show/hide fixture tree Показати / сховати дерево приладів - + Check the script syntax Перевірте синтаксис скрипту - + Syntax check Перевірка синтаксису - + Start function Запустить функцію - + Stop function Зупинити функцію - + Set fixture channel Встановити канал приладу - + Wait time Час очикування - + Random number Випадкове число - + Blackout Blackout - + System command Системна команда - + File path Шлях до файлу @@ -3357,12 +3884,12 @@ Access level: Видалити виділені прилади - + Steps Кроки - + Fixtures Прилади @@ -3370,107 +3897,122 @@ Access level: SettingsView2D - + Environment Оточення - + Width Ширина - + Height Висота - + Depth Глибина - + Grid units Елементи сітки - + Meters Метри - + Feet Фути - + Point of view Точка огляду - + Top view Вигляд зверху - + Front view Вигляд спереду - + Right side view Вигляд з правого боку - + Left side view Вигляд з лівого боку + Custom Background + + + + + Select an image + Выберить зображення + + + + Reset background + + + + Selected fixtures Вибрані прилади - + Gel color Колір Гелю - + Rotation Обертання - + Alignment Вирівнювання - + Align the selected items to the left Вирівнювати вибрані елементи ліворуч - + Align the selected items to the top Вирівнювати вибрані елементи зверху - + Distribution Розповсюдження - + Equally distribute horizontally the selected items Рівномірно розподілити вибрані елементи по горизонталі - + Equally distribute vertically the selected items Рівномірно розподілити вибрані елементи по вертикалі @@ -3478,122 +4020,126 @@ Access level: SettingsView3D - + Environment Оточення - + Type Тип - + Width Ширина - + Height Висота - + Depth Глибина - + Rendering - + Quality - + Low - + Medium - + High - + Ultra - + Ambient light - + Smoke amount - + Show FPS - + Position Положення - + Rotation Обертання - + Scale - + Custom items - + Select a mesh file - + 3D files - + All files - + + Normalize the selected items + + + Actions - Дійство + Дійство - + Add a new item to the scene - + Remove the selected items @@ -3619,16 +4165,16 @@ Access level: ShowItem - - - + + + Position: Положення: - - - + + + Duration: Тривалість: @@ -3646,74 +4192,74 @@ Access level: Показати колір елементів - + Unlock the selected items Розблокувати вибрані елементи - + Lock the selected items Заблокувати вибрані елементи - + Snap to grid Прив'язати до сітки - + Stretch the original function Розтягнути оригінальну функцію - + Remove the selected items Видалити вибрані елементи - + Are you sure you want to remove the following items? (Note that the original functions will not be deleted) Ви впевнені, що хочете видалити наступні елементи? (Зауважте, що вихідні функції не будуть видалені) - + Delete show items Видалити видимі елементи - + Copy the selected items in the clipboard Скопіювати вибрані елементи в буфер обміну - + Paste items in the clipboard at cursor position Вставити елементи в буфер обміну за позицією курсора - + Play or resume Грайте або відновлюйте - + Stop or rewind Зупинити або перемотати - + Move the selected track up Перенести вибрану композицію до верху - + Move the selected track down Перенести вибрану композицію до низу - + Create a new track Створити нову композицію @@ -3723,19 +4269,19 @@ Access level: Менеджер Шоу - + New Show Нове Шоу - - + + Track %1 Композиція %1 - - + + (Copy) (Копія) @@ -3743,37 +4289,37 @@ Access level: SimpleDesk - + Universe Universe - + Reset the whole universe - + Dump on a new Scene Скопіювати на нову Сцену - + Reset the channel - + Fixture List - + Commands history - + Simple Desk @@ -3789,25 +4335,202 @@ Access level: TrackDelegate - + Solo this track Соло - + Mute this track Вимкнути цю композицію + + UISettingsEditor + + + + Reset to default + + + + + Scaling factor + + + + + Background darker + + + + + Background dark + + + + + Background medium + + + + + Background light + + + + + Background lighter + + + + + Controls background + + + + + Foreground main + + + + + Foreground medium + + + + + Foreground light + + + + + Toolbar gradient start + + + + + Sub-toolbar gradient start + + + + + Toolbar gradient end + + + + + Toolbar hover gradient start + + + + + Toolbar hover gradient end + + + + + Toolbar selection + + + + + Sub-toolbar selection + + + + + Section header + + + + + Section header divider + + + + + Item highlight + + + + + Item highlight pressed + + + + + Item hover + + + + + Item selection + + + + + VC Frame drop area + + + + + Item dark border + + + + + Save to file + + + + + Operation completed + + + + + Error + Помилка + + + + UniverseGridView + + + Error + Помилка + + + + Unable to perform the operation. +There is either not enough space or the target universe in invalid + + + + + Cut the selected items into clipboard + + + + + Paste items in the clipboard at the first available position + + + UniverseIOItem - Passthrough - Наскрізь + Наскрізь + + + + Enable/Disable passthrough + - + Enable/Disable feedbacks Увімкнути / вимкнути зворотні зв'язки @@ -4019,74 +4742,74 @@ Access level: Додайте розклад функцій - + Remove this schedule Видалити цей розклад - + Start time Час початку - + Stop time Час зупинки - + Enable the stop time Увімкнути час зупинки - + M As in Monday Як у Понеділок - + T As in Tuesday Як у Вівторок - + W As in Wednesday Як у Середу - + T As in Thursday Як у Четверг - + F As in Friday Як у П'ятницю - + S As in Saturday Як у Суботу - + S As in Sunday Як у Неділю - + Repeat weekly Повторювати щотижня - + Add a new schedule Додати новий розклад @@ -4094,17 +4817,17 @@ Access level: VCCueList - + Next Cue Наступна Cue - + Previous Cue Попередня Cue - + Play/Stop/Pause Відтворення/Зупинка/Пауза @@ -4117,17 +4840,17 @@ Access level: Правий Кроссфейд - + Stop/Pause Зупинка/Пауза - + Side Fader - + Cue List %1 Список Cue %1 @@ -4135,27 +4858,32 @@ Access level: VCCueListItem - + Play/Pause Відтворення/Пауза - + + Play/Stop + + + + Pause Пауза - + Stop Зупинка - + Previous cue Попередній Cue - + Next cue Наступний Cue @@ -4248,30 +4976,35 @@ Access level: VCFrame - + Next Page Наступна Сторінка - + Previous Page Попередня Сторінка - + Enable Увімкнено - + Collapse Згорнути - + Frame %1 Кадр %1 + + + Page %1 + Сторінка %1 + VCFrameItem @@ -4286,17 +5019,16 @@ Access level: Увімкнути / Вимкнути цей кадр - + Previous page Наступна сторінка - Page - Сторінка + Сторінка - + Next page Наступна сторінка @@ -4339,10 +5071,21 @@ Access level: Нумер сторінок - + Clone first page widgets Склонувати виджети першої сторінки + + + + Shortcuts + + + + + Shortcut name + + VCLabel @@ -4355,12 +5098,12 @@ Access level: VCPage - + Page %1 Сторінка %1 - + Virtual Console Page %1 Віртуальна Консоль Сторінка %1 @@ -4368,57 +5111,57 @@ Access level: VCPageProperties - + Width Ширина - + Height Висота - + Security Безпека - + Set a PIN Встановити PIN-код - + Error Помилка - + The entered PINs are either invalid or incorrect Введені PIN-коди недійсні або невірні - + Add page to the left Додати сторінку ліворуч - + Add page to the right Додати сторінку праворуч - + Delete this page Видалити цю сторінку - + Delete page Видалити сторінку - + Are you sure you want to delete the selected page? Ви впевнені, що бажаєте видалити вибрану сторінку? @@ -4483,12 +5226,12 @@ Access level: Скидання - + Slider %1 Слайдер %1 - + Knob %1 Ручка %1 @@ -4561,82 +5304,82 @@ Access level: Атрібут - + Level mode Режим рівня - + Channels Канали - + Add/Remove channels Додати/Видалити канали - + Click & Go button Кнопка Click & Go - + None Нічого - + RGB/CMY RGB/CMY - + Gobo/Effect/Macro Гобо/Эфект/Макро - + Monitor channel levels Моніторінг рівней каналів - + Values range Діапазон значень - + Upper limit Верхня межа - + Lower limit Нижня межа - + Grand Master mode Режим Grand Master - + Reduce values Зменьшити значення - + Limit values Граничні значення - + Intensity channels Інтенсивність каналів - + All channels Усі канали @@ -4713,8 +5456,8 @@ Access level: Невідомо - - + + None Нічого @@ -4722,87 +5465,87 @@ Access level: VCWidgetProperties - + Select a widget first Спочатку виберіть віджет - + Settings Налаштування - + External controls Зовнішні елементи управління - + Basic properties Базові компоненти - + Label Назва - + Background color Колір фону - + Foreground color Колір переднього плану - + Font Шрифт - + Please choose a font Виберить шрифт - + Background image Фонове зображення - + Select an image Выберить зображення - + Alignment Вирівнювання - + Align the selected widgets to the left Вирівняти вибрані віджети ліворуч - + Align the selected widgets to the right Вирівняти вибрані віджети праворуч - + Align the selected widgets to the top Вирівняти вибрані віджети вгору - + Align the selected widgets to the bottom Вирівняти вибрані віджети донизу - + External Controls Зовнішні Элементи управління @@ -4880,77 +5623,77 @@ Access level: Вихідний екран - + Output mode Режим виведення - + Windowed Віконний - + Fullscreen Повноекранний - + Geometry Геометрія - + Original Оригінал - + Custom Налаштований - + Position Положення - + Size Розмір - + W Ш - + H В - + Rotation Обертання - + X X - + Y Y - + Z Z - + Layer @@ -4958,72 +5701,72 @@ Access level: VirtualConsole - + Error Помилка - + Invalid PIN entered Введено невірний PIN-код - + Enable/Disable widgets snapping Увімкнути / Вимкнути захоплення віджетів - + Widget matrix setup Налаштування матриці віджетів - + Columns Стовбці - + Rows Строки - + Width Ширина - + Height Висота - + Frame type Тип кадру - + Normal Нормальний - + Solo Соло - + Virtual Console Віртуальна Консоль - + <None> <Нічого> - + Page %1 Сторінка %1 diff --git a/qmlui/qml/ActionsMenu.qml b/qmlui/qml/ActionsMenu.qml index dcc96c44ee..3d43d00fb4 100644 --- a/qmlui/qml/ActionsMenu.qml +++ b/qmlui/qml/ActionsMenu.qml @@ -34,6 +34,14 @@ Popup onClosed: submenuItem = null + function handleSaveAction() + { + if (qlcplus.fileName()) + qlcplus.saveWorkspace(qlcplus.fileName()) + else + saveDialog.open() + } + function saveBeforeExit() { saveFirstPopup.action = "#EXIT" @@ -75,7 +83,10 @@ Popup onAccepted: { if (qlcplus.loadImportWorkspace(fileUrl) === true) + { + importLoader.source = "" importLoader.source = "qrc:/PopupImportProject.qml" + } } } @@ -150,6 +161,7 @@ Popup border.width: 1 border.color: UISettings.bgStronger color: UISettings.bgStrong + height: actionsMenuEntries.height } Column @@ -242,11 +254,7 @@ Popup onClicked: { - if (qlcplus.fileName()) - qlcplus.saveWorkspace(qlcplus.fileName()) - else - saveDialog.open() - + handleSaveAction() menuRoot.close() } } @@ -291,13 +299,16 @@ Popup } } - Row + RowLayout { height: UISettings.iconSizeDefault + width: parent.width + spacing: 0 ContextMenuEntry { - width: actionsMenuEntries.width / 2 + Layout.fillWidth: true + Layout.fillHeight: true imgSource: "qrc:/undo.svg" entryText: qsTr("Undo") onEntered: submenuItem = null @@ -310,7 +321,8 @@ Popup } ContextMenuEntry { - width: actionsMenuEntries.width / 2 + Layout.fillWidth: true + Layout.fillHeight: true imgSource: "qrc:/redo.svg" entryText: qsTr("Redo") onEntered: submenuItem = null @@ -400,10 +412,25 @@ Popup CustomPopupDialog { id: addrToolDialog + width: mainView.width / 3.5 title: qsTr("DMX Address tool") standardButtons: Dialog.Close - contentItem: DMXAddressTool { } + contentItem: + DMXAddressTool { } + } + } + + ContextMenuEntry + { + id: uiConfig + imgSource: "qrc:/configure.svg" + entryText: qsTr("UI Settings") + onEntered: submenuItem = null + onClicked: + { + menuRoot.close() + mainView.loadResource("qrc:/UISettingsEditor.qml") } } diff --git a/qmlui/qml/BeatGeneratorsPanel.qml b/qmlui/qml/BeatGeneratorsPanel.qml index ad855bc88f..2e9acfa967 100644 --- a/qmlui/qml/BeatGeneratorsPanel.qml +++ b/qmlui/qml/BeatGeneratorsPanel.qml @@ -20,13 +20,15 @@ import QtQuick 2.6 import QtQuick.Controls 2.1 import QtQuick.Layouts 1.1 + +import "GenericHelpers.js" as Helpers import "." Rectangle { id: beatChooserBox width: UISettings.bigItemHeight * 3 - height: contentsColumn.height + 30 + height: toolbar.height + contentsColumn.height + 10 color: UISettings.bgMedium border.color: UISettings.bgLight border.width: 2 @@ -39,43 +41,57 @@ Rectangle ButtonGroup { id: selGeneratorGroup } + Rectangle + { + id: toolbar + x: 2 + y: 2 + z: 1 + width: parent.width - 4 + height: UISettings.listItemHeight + + gradient: + Gradient + { + id: cBarGradient + GradientStop { position: 0; color: UISettings.toolbarStartSub } + GradientStop { position: 1; color: UISettings.toolbarEnd } + } + + // allow the tool to be dragged around + // by holding it on the title bar + MouseArea + { + anchors.fill: parent + drag.target: beatChooserBox + } + GenericButton + { + width: height + height: parent.height + anchors.right: parent.right + border.color: UISettings.bgMedium + useFontawesome: true + label: FontAwesome.fa_times + onClicked: beatChooserBox.visible = false + } + } + Column { id: contentsColumn spacing: 3 x: 5 - y: 5 + y: toolbar.height + 7 width: parent.width - 10 - Rectangle - { - id: toolbar - width: parent.width - height: UISettings.listItemHeight - z: 1 - gradient: - Gradient - { - id: cBarGradient - GradientStop { position: 0; color: UISettings.toolbarStartSub } - GradientStop { position: 1; color: UISettings.toolbarEnd } - } - - // allow the tool to be dragged around - // by holding it on the title bar - MouseArea - { - anchors.fill: parent - drag.target: beatChooserBox - } - } - ListView { id: generatorsList width: parent.width height: UISettings.bigItemHeight * 2 + clip: true boundsBehavior: Flickable.StopAtBounds delegate: @@ -86,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") { @@ -138,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 58128f9c5a..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,41 +82,50 @@ 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 { ignoreUnknownSignals: true target: toolLoader.item - onValueChanged: popupRoot.valueChanged(popupRoot.fixtureId, popupRoot.channelIndex, value) + function onValueChanged() + { + itemRoot.valueChanged(itemRoot.fixtureId, itemRoot.channelIndex, target.currentValue) + } + function onClose() + { + itemRoot.visible = false + toolLoader.source = "" + } } } } diff --git a/qmlui/qml/ChaserStepDelegate.qml b/qmlui/qml/ChaserStepDelegate.qml index e432851fa8..4a0fa58c3a 100644 --- a/qmlui/qml/ChaserStepDelegate.qml +++ b/qmlui/qml/ChaserStepDelegate.qml @@ -33,7 +33,6 @@ Rectangle property int functionID: -1 property QLCFunction func property bool showFunctionName: true - property string stepLabel property string stepFadeIn property string stepHold property string stepFadeOut @@ -62,7 +61,6 @@ Rectangle onFunctionIDChanged: { func = functionManager.getFunction(functionID) - stepLabel = func.name funcIconName.functionType = func.type } @@ -177,7 +175,7 @@ Rectangle width: col2Width height: 35 anchors.verticalCenter: parent.verticalCenter - tLabel: stepLabel + tLabel: func ? func.name : "" tLabelColor: stepDelegate.labelColor } Rectangle { visible: showFunctionName; height: parent.height; width: 1; color: UISettings.fgMedium } diff --git a/qmlui/qml/ChaserWidget.qml b/qmlui/qml/ChaserWidget.qml index 80a8d6d91a..65f55ef238 100644 --- a/qmlui/qml/ChaserWidget.qml +++ b/qmlui/qml/ChaserWidget.qml @@ -17,7 +17,7 @@ limitations under the License. */ -import QtQuick 2.0 +import QtQuick 2.12 import QtQuick.Layouts 1.1 import QtQuick.Controls 2.1 @@ -30,7 +30,7 @@ Column id: widgetRoot property bool isSequence: false property alias model: cStepsList.model - property alias playbackIndex: cStepsList.currentIndex + property int playbackIndex: -1 property int nextIndex: -1 property alias speedType: timeEditTool.speedType property int tempoType: QLCFunction.Time @@ -41,6 +41,7 @@ Column property int editStepIndex: -1 property int editStepType + property int selectionRequestIndex: -1 signal indexChanged(int index) signal stepValueChanged(int index, int value, int type) @@ -50,6 +51,9 @@ Column signal requestEditor(int funcID) signal dragEntered(var item) signal dragExited(var item) + signal enterPressed(int index) + + onPlaybackIndexChanged: ceSelector.selectItem(playbackIndex, cStepsList.model, false) function editStepTime(stepIndex, stepItem, type) { @@ -94,10 +98,15 @@ Column timeEditTool.show(-1, stepItem.mapToItem(mainView, 0, 0).y, title, timeValueString, type) } + function selectStep(stepIndex, multiSelect) + { + ceSelector.selectItem(stepIndex, cStepsList.model, multiSelect) + } + ModelSelector { id: ceSelector - onItemsCountChanged: console.log("Chaser Editor selected items changed!") + onItemsCountChanged: console.log("Chaser Editor selected items: " + itemsCount) } TimeEditTool @@ -196,8 +205,8 @@ Column } onPositionChanged: { - if (drag.target === null) - return; + if (drag.target == null) + return nameCol.width = nameColDrag.x - nameCol.x - 1 } onReleased: drag.target = null @@ -234,8 +243,8 @@ Column } onPositionChanged: { - if (drag.target === null) - return; + if (drag.target == null) + return fInCol.width = fInColDrag.x - fInCol.x - 1 } onReleased: drag.target = null @@ -272,8 +281,8 @@ Column } onPositionChanged: { - if (drag.target === null) - return; + if (drag.target == null) + return holdCol.width = holdColDrag.x - holdCol.x - 1 } onReleased: drag.target = null @@ -310,8 +319,8 @@ Column } onPositionChanged: { - if (drag.target === null) - return; + if (drag.target == null) + return fOutCol.width = fOutColDrag.x - fOutCol.x - 1 } onReleased: drag.target = null @@ -348,8 +357,8 @@ Column } onPositionChanged: { - if (drag.target === null) - return; + if (drag.target == null) + return durCol.width = durColDrag.x - durCol.x - 1 } onReleased: drag.target = null @@ -377,9 +386,10 @@ Column boundsBehavior: Flickable.StopAtBounds clip: true + property bool dragActive: false property int dragInsertIndex: -1 - onCurrentIndexChanged: ceSelector.selectItem(currentIndex, model, 0) + //onCurrentIndexChanged: ceSelector.selectItem(currentIndex, model, false) CustomTextEdit { @@ -405,45 +415,80 @@ Column editStepIndex = -1 visible = false } + + Keys.onPressed: + { + if (event.key === Qt.Key_Escape) + { + editStepIndex = -1 + visible = false + } + } } delegate: Item { + id: itemRoot width: cStepsList.width height: UISettings.listItemHeight + Keys.onPressed: + { + if (event.key === Qt.Key_Return || + event.key === Qt.Key_Enter) + { + widgetRoot.enterPressed(index) + event.accepted = true + } + } + MouseArea { id: delegateRoot width: cStepsList.width height: parent.height - drag.target: csDelegate - drag.threshold: height / 2 + drag.target: csDragItem + drag.threshold: height / 4 + + property bool dragActive: drag.active onPressed: { + var posInList = delegateRoot.mapToItem(widgetRoot, mouse.x, mouse.y) + csDragItem.parent = widgetRoot + csDragItem.x = posInList.x + csDragItem.y = posInList.y + csDragItem.z = 10 + + if (model.isSelected) + return + ceSelector.selectItem(index, cStepsList.model, mouse.modifiers & Qt.ControlModifier) - console.log("mouse mods: " + mouse.modifiers) - if ((mouse.modifiers & Qt.ControlModifier) == 0) + if (mouse.modifiers == 0) + { widgetRoot.indexChanged(index) + csDragItem.itemsList = [] + } + + csDragItem.itemsList = ceSelector.itemsList() + itemRoot.forceActiveFocus() } onDoubleClicked: csDelegate.handleDoubleClick(mouse.x, mouse.y) - onReleased: + onDragActiveChanged: { - if (csDelegate.Drag.target === cwDropArea) + if (dragActive) { - csDelegate.Drag.drop() + csDragItem.itemLabel = csDelegate.func.name + csDragItem.itemIcon = functionManager.functionIcon(csDelegate.func.type) + cStepsList.dragActive = true } else { - // return the dragged item to its original position - parent = delegateRoot - csDelegate.x = 0 - csDelegate.y = 0 + csDragItem.Drag.drop() } } @@ -473,10 +518,6 @@ Column highlightEditTime: editStepIndex === index ? editStepType : -1 nextIndex: widgetRoot.nextIndex - Drag.active: delegateRoot.drag.active - Drag.source: csDelegate - Drag.keys: [ "function" ] - onDoubleClicked: { console.log("Double clicked: " + indexInList + ", " + type) @@ -491,6 +532,19 @@ Column } // MouseArea } // Item + GenericMultiDragItem + { + id: csDragItem + + property bool fromFunctionManager: false + + visible: cStepsList.dragActive + + Drag.active: cStepsList.dragActive + Drag.source: csDragItem + Drag.keys: [ "function" ] + } + DropArea { id: cwDropArea @@ -510,22 +564,30 @@ Column console.log("Item dropped here. x: " + drag.x + " y: " + drag.y) /* Check if the dragging was started from a Function Manager */ - if (drag.source.hasOwnProperty("fromFunctionManager")) + if (drag.source.fromFunctionManager === true) { widgetRoot.addFunctions(drag.source.itemsList, cStepsList.dragInsertIndex) } else { widgetRoot.moveSteps(ceSelector.itemsList(), cStepsList.dragInsertIndex) + cStepsList.currentIndex = -1 } cStepsList.dragInsertIndex = -1 + cStepsList.dragActive = false } onPositionChanged: { var idx = cStepsList.indexAt(drag.x, drag.y) + var item = cStepsList.itemAt(drag.x, drag.y) + var itemY = item.mapToItem(cStepsList, 0, 0).y //console.log("Item index:" + idx) - cStepsList.dragInsertIndex = idx + + if (drag.y < (itemY + item.height) / 2) + cStepsList.dragInsertIndex = idx + else + cStepsList.dragInsertIndex = idx + 1 } } ScrollBar.vertical: CustomScrollBar { } diff --git a/qmlui/qml/ColorTool.qml b/qmlui/qml/ColorTool.qml index eda3bfbce1..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 @@ -40,6 +41,25 @@ Rectangle property alias showPalette: paletteBox.visible signal colorChanged(real r, real g, real b, real w, real a, real uv) + signal close() + + onVisibleChanged: + { + if (visible) + { + // invoke a method that will invoke + // the updateColors function below + contextManager.getCurrentColors(colorToolBox) + } + } + + function updateColors(validRgb, rgb, validWauv, wauv) + { + if (validRgb) + currentRGB = rgb + if (validWauv) + currentWAUV = wauv + } function loadPalette(id) { @@ -145,7 +165,16 @@ Rectangle { Layout.fillWidth: true height: colorToolBar.height - drag.target: paletteBox.isEditing ? null : colorToolBox + drag.target: paletteBox.isEditing ? null : (colorToolBox.dragTarget ? colorToolBox.dragTarget : colorToolBox) + } + GenericButton + { + width: height + height: parent.height + border.color: UISettings.bgMedium + useFontawesome: true + label: FontAwesome.fa_times + onClicked: colorToolBox.close() } } } @@ -172,7 +201,8 @@ Rectangle { target: toolLoader.item ignoreUnknownSignals: true - onColorChanged: + + function onColorChanged(r, g, b, w, a, uv) { paletteBox.updateValue(currentRGB) @@ -183,6 +213,8 @@ Rectangle } else { + //console.log("MAIN r:"+r+" g:"+g+" b:"+b) + //console.log("MAIN w:"+w+" a:"+a+" uv:"+uv) currentRGB = Qt.rgba(r, g, b, 1.0) currentWAUV = Qt.rgba(w, a, uv, 1.0) colorToolBox.colorChanged(r, g, b, w, a, uv) @@ -191,7 +223,11 @@ Rectangle if (paletteBox.isEditing || paletteBox.checked) paletteBox.updatePreview() } - onReleased: if (closeOnSelect) colorToolBox.visible = false + function onReleased() + { + if (closeOnSelect) + colorToolBox.visible = false + } } } diff --git a/qmlui/qml/ColorToolBasic.qml b/qmlui/qml/ColorToolBasic.qml index cce00c51ee..ac0c172a4a 100644 --- a/qmlui/qml/ColorToolBasic.qml +++ b/qmlui/qml/ColorToolBasic.qml @@ -31,6 +31,7 @@ Rectangle property int colorsMask: 0 property color currentRGB + property color currentWAUV property int cellSize: width / 9 signal colorChanged(real r, real g, real b, real w, real a, real uv) @@ -77,14 +78,14 @@ Rectangle width: cellSize height: cellSize border.width: 1 - border.color: "#222" - color: getHTMLColor(index * 36, index * 36, index * 36) + border.color: UISettings.borderColorDark + color: getHTMLColor(Math.round(index * 36.4285), Math.round(index * 36.4285), Math.round(index * 36.4285)) MouseArea { anchors.fill: parent onClicked: { - rootBox.colorChanged(color.r, color.g, color.b, 0, 0, 0) + rootBox.colorChanged(color.r, color.g, color.b, currentWAUV.r, currentWAUV.g, currentWAUV.b) rootBox.released() } } @@ -111,7 +112,7 @@ Rectangle width: cellSize height: cellSize border.width: 1 - border.color: "#222" + border.color: UISettings.borderColorDark color: getBaseHTMLColor(index) MouseArea @@ -119,7 +120,7 @@ Rectangle anchors.fill: parent onClicked: { - rootBox.colorChanged(color.r, color.g, color.b, 0, 0, 0) + rootBox.colorChanged(color.r, color.g, color.b, currentWAUV.r, currentWAUV.g, currentWAUV.b) rootBox.released() } } @@ -155,7 +156,7 @@ Rectangle width: cellSize height: cellSize border.width: 1 - border.color: "#222" + border.color: UISettings.borderColorDark color: getShadedColor(colIndex, index) MouseArea @@ -163,7 +164,7 @@ Rectangle anchors.fill: parent onClicked: { - rootBox.colorChanged(color.r, color.g, color.b, 0, 0, 0) + rootBox.colorChanged(color.r, color.g, color.b, currentWAUV.r, currentWAUV.g, currentWAUV.b) rootBox.released() } } @@ -186,11 +187,12 @@ Rectangle height: UISettings.listItemHeight label: qsTr("Selected color") } - Rectangle + MultiColorBox { width: UISettings.mediumItemHeight height: UISettings.listItemHeight - color: currentRGB + primary: currentRGB + secondary: currentWAUV } } diff --git a/qmlui/qml/ColorToolFull.qml b/qmlui/qml/ColorToolFull.qml index 7d37415197..8bd92eb0ba 100644 --- a/qmlui/qml/ColorToolFull.qml +++ b/qmlui/qml/ColorToolFull.qml @@ -45,26 +45,7 @@ Rectangle onCurrentRGBChanged: { - rSpin.value = currentRGB.r * 255 - gSpin.value = currentRGB.g * 255 - bSpin.value = currentRGB.b * 255 htmlText.text = Helpers.getHTMLColor(currentRGB.r * 255, currentRGB.g * 255, currentRGB.b * 255) - emitCurrentColor() - } - onCurrentWAUVChanged: - { - wSpin.value = currentWAUV.r * 255 - aSpin.value = currentWAUV.g * 255 - uvSpin.value = currentWAUV.b * 255 - wSlider.value = currentWAUV.r * 255 - aSlider.value = currentWAUV.g * 255 - uvSlider.value = currentWAUV.b * 255 - emitCurrentColor() - } - - function emitCurrentColor() - { - colorChanged(currentRGB.r, currentRGB.g, currentRGB.b, currentWAUV.r, currentWAUV.g, currentWAUV.b) } Canvas @@ -144,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) @@ -173,7 +155,9 @@ Rectangle height: UISettings.listItemHeight from: 0 to: 255 - onValueChanged: currentRGB = Qt.rgba(value / 255, currentRGB.g, currentRGB.b, 1.0) + value: currentRGB.r * 255 + onValueModified: colorChanged(value / 255, currentRGB.g, currentRGB.b, + currentWAUV.r, currentWAUV.g, currentWAUV.b) } RobotoText @@ -189,7 +173,9 @@ Rectangle height: UISettings.listItemHeight from: 0 to: 255 - onValueChanged: currentRGB = Qt.rgba(currentRGB.r, value / 255, currentRGB.b, 1.0) + value: currentRGB.g * 255 + onValueModified: colorChanged(currentRGB.r, value / 255, currentRGB.b, + currentWAUV.r, currentWAUV.g, currentWAUV.b) } RobotoText @@ -205,7 +191,9 @@ Rectangle height: UISettings.listItemHeight from: 0 to: 255 - onValueChanged: currentRGB = Qt.rgba(currentRGB.r, currentRGB.g, value / 255, 1.0) + value: currentRGB.b * 255 + onValueModified: colorChanged(currentRGB.r, currentRGB.g, value / 255, + currentWAUV.r, currentWAUV.g, currentWAUV.b) } RobotoText @@ -244,7 +232,9 @@ Rectangle Layout.fillWidth: true from: 0 to: 255 - onPositionChanged: currentWAUV = Qt.rgba(valueAt(position) / 255, currentWAUV.g, currentWAUV.b, 1.0) + value: currentWAUV.r * 255 + onMoved: colorChanged(currentRGB.r, currentRGB.g, currentRGB.b, + valueAt(position) / 255, currentWAUV.g, currentWAUV.b) } CustomSpinBox @@ -255,7 +245,9 @@ Rectangle height: UISettings.listItemHeight from: 0 to: 255 - onValueChanged: currentWAUV = Qt.rgba(value / 255, currentWAUV.g, currentWAUV.b, 1.0) + value: currentWAUV.r * 255 + onValueModified: colorChanged(currentRGB.r, currentRGB.g, currentRGB.b, + value / 255, currentWAUV.g, currentWAUV.b) } RobotoText @@ -272,7 +264,9 @@ Rectangle Layout.fillWidth: true from: 0 to: 255 - onPositionChanged: currentWAUV = Qt.rgba(currentWAUV.r, valueAt(position) / 255, currentWAUV.b, 1.0) + value: currentWAUV.g * 255 + onMoved: colorChanged(currentRGB.r, currentRGB.g, currentRGB.b, + currentWAUV.r, valueAt(position) / 255, currentWAUV.b) } CustomSpinBox @@ -283,7 +277,9 @@ Rectangle height: UISettings.listItemHeight from: 0 to: 255 - onValueChanged: currentWAUV = Qt.rgba(currentWAUV.r, value / 255, currentWAUV.b, 1.0) + value: currentWAUV.g * 255 + onValueModified: colorChanged(currentRGB.r, currentRGB.g, currentRGB.b, + currentWAUV.r, value / 255, currentWAUV.b) } RobotoText @@ -300,7 +296,9 @@ Rectangle Layout.fillWidth: true from: 0 to: 255 - onPositionChanged: currentWAUV = Qt.rgba(currentWAUV.r, currentWAUV.g, valueAt(position) / 255, 1.0) + value: currentWAUV.b * 255 + onMoved: colorChanged(currentRGB.r, currentRGB.g, currentRGB.b, + currentWAUV.r, currentWAUV.g, valueAt(position) / 255) } CustomSpinBox @@ -312,7 +310,8 @@ Rectangle from: 0 to: 255 value: currentWAUV.b * 255 - onValueChanged: currentWAUV = Qt.rgba(currentWAUV.r, currentWAUV.g, value / 255, 1.0) + onValueModified: colorChanged(currentRGB.r, currentRGB.g, currentRGB.b, + currentWAUV.r, currentWAUV.g, value / 255) } } @@ -329,7 +328,6 @@ Rectangle MultiColorBox { - id: previewBox width: UISettings.mediumItemHeight height: UISettings.listItemHeight primary: currentRGB diff --git a/qmlui/qml/ColorToolPrimary.qml b/qmlui/qml/ColorToolPrimary.qml index 59f7d99971..5240ebe99e 100644 --- a/qmlui/qml/ColorToolPrimary.qml +++ b/qmlui/qml/ColorToolPrimary.qml @@ -33,11 +33,12 @@ Rectangle border.width: 2 property int targetColor: QLCChannel.NoColour - property int currentValue: 0 + property int currentValue: 0 // as DMX value property bool closeOnSelect: false 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) @@ -109,8 +111,23 @@ Rectangle val = ((mouse.x - (width * 0.1)) * 255.0) / (width * 0.8) } + boxRoot.currentValue = val boxRoot.valueChanged(val) } + + onPressed: calculateValue() + onPositionChanged: + { + if (!pressed) + return + + calculateValue(mouse) + } + onReleased: + { + if (closeOnSelect) + boxRoot.close() + } } } } diff --git a/qmlui/qml/ContextMenuEntry.qml b/qmlui/qml/ContextMenuEntry.qml index dcdf80586c..748c6aaac6 100644 --- a/qmlui/qml/ContextMenuEntry.qml +++ b/qmlui/qml/ContextMenuEntry.qml @@ -18,7 +18,6 @@ */ import QtQuick 2.2 -import QtQuick.Controls 1.2 import "." @@ -26,6 +25,7 @@ Rectangle { id: baseMenuEntry width: parent ? (parent.width > itemWidth) ? parent.width : itemWidth : 400 + implicitWidth: itemWidth height: iconHeight + 4 property int iconHeight: UISettings.iconSizeDefault diff --git a/qmlui/qml/CustomCheckBox.qml b/qmlui/qml/CustomCheckBox.qml index 909387e99b..7c0218f26e 100644 --- a/qmlui/qml/CustomCheckBox.qml +++ b/qmlui/qml/CustomCheckBox.qml @@ -54,7 +54,7 @@ RadioButton background: Rectangle { - color: UISettings.bgMain + color: UISettings.bgMedium border.width: 1 border.color: UISettings.bgLight } diff --git a/qmlui/qml/CustomComboBox.qml b/qmlui/qml/CustomComboBox.qml index d79e596c21..6315eb22b4 100644 --- a/qmlui/qml/CustomComboBox.qml +++ b/qmlui/qml/CustomComboBox.qml @@ -48,6 +48,7 @@ ComboBox property int currValue property int delegateHeight: UISettings.listItemHeight property bool isUpdating: false + property int contentsMaxWidth: 0 signal valueChanged(int value) @@ -145,6 +146,13 @@ ComboBox label: text height: delegateHeight fontSize: UISettings.textSizeDefault + + onWidthChanged: + { + var w = width + (itemIcon ? delegateHeight : 0) + 15 + if (w > contentsMaxWidth) + contentsMaxWidth = w + } } } @@ -184,7 +192,8 @@ ComboBox Row { spacing: 2 - leftPadding: 3 + leftPadding: 6 + clip: true Image { @@ -222,7 +231,7 @@ ComboBox Popup { y: control.height - width: control.width + width: Math.min(UISettings.bigItemHeight * 3, Math.max(control.width, contentsMaxWidth)) height: Math.min(contentItem.implicitHeight, control.Window.height - topMargin - bottomMargin) topMargin: 0 bottomMargin: 0 diff --git a/qmlui/qml/CustomScrollBar.qml b/qmlui/qml/CustomScrollBar.qml index 5cb7291987..797e45b09b 100644 --- a/qmlui/qml/CustomScrollBar.qml +++ b/qmlui/qml/CustomScrollBar.qml @@ -47,8 +47,8 @@ ScrollBar Rectangle { anchors.centerIn: parent - width: parent.width * 0.8 - height: 5 + width: control.orientation == Qt.Vertical ? parent.width * 0.8 : 5 + height: control.orientation == Qt.Vertical ? 5 : parent.height * 0.8 color: UISettings.bgLight } } diff --git a/qmlui/qml/CustomTextInput.qml b/qmlui/qml/CustomTextInput.qml new file mode 100644 index 0000000000..de0f28483a --- /dev/null +++ b/qmlui/qml/CustomTextInput.qml @@ -0,0 +1,111 @@ +/* + Q Light Controller Plus + CustomTextInput.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.7 +import "." + +TextInput +{ + id: controlRoot + z: 0 + width: 100 + height: UISettings.listItemHeight + readOnly: true + text: "" + horizontalAlignment: TextInput.AlignLeft + verticalAlignment: TextInput.AlignVCenter + color: UISettings.fgMain + font.family: UISettings.robotoFontName + font.pixelSize: UISettings.textSizeDefault + echoMode: TextInput.Normal + wrapMode: TextInput.NoWrap + selectByMouse: true + selectionColor: UISettings.highlightPressed + selectedTextColor: UISettings.fgMain + + property string originalText + property bool allowDoubleClick: false + + signal clicked() + signal textConfirmed(string text) + + function setEditingStatus(enable) + { + z = enable ? 5 : 0 + readOnly = enable ? false : true + cursorVisible = enable ? true : false + + if (enable) + { + originalText = text + cursorPosition = text.length + controlRoot.selectAll() + } + else + { + select(0, 0) + + } + } + + Keys.onPressed: + { + switch(event.key) + { + case Qt.Key_F2: + setEditingStatus(true) + break; + case Qt.Key_Escape: + setEditingStatus(false) + controlRoot.text = originalText + break; + default: + event.accepted = false + return + } + + event.accepted = true + } + + onEditingFinished: + { + if (readOnly) + return + setEditingStatus(false) + controlRoot.textConfirmed(text) + } + + MouseArea + { + anchors.fill: parent + + onClicked: + { + controlRoot.forceActiveFocus() + controlRoot.clicked() + } + onDoubleClicked: + { + if (allowDoubleClick === false) + return + + setEditingStatus(true) + } + } +} diff --git a/qmlui/qml/DMXAddressWidget.qml b/qmlui/qml/DMXAddressWidget.qml index 73d2dbd976..a6c482f4bc 100644 --- a/qmlui/qml/DMXAddressWidget.qml +++ b/qmlui/qml/DMXAddressWidget.qml @@ -51,11 +51,7 @@ Rectangle { id: dipRow spacing: 10 - anchors - { - top: flipVertically ? undefined : actText.bottom - bottom: flipVertically ? actText.top : undefined - } + y: flipVertically ? 3: parent.height - height - 3 anchors.horizontalCenter: parent.horizontalCenter Repeater diff --git a/qmlui/qml/EditableTextBox.qml b/qmlui/qml/EditableTextBox.qml deleted file mode 100644 index 5126413052..0000000000 --- a/qmlui/qml/EditableTextBox.qml +++ /dev/null @@ -1,115 +0,0 @@ -/* - Q Light Controller Plus - EditableTextBox.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.7 -import "." - -Rectangle -{ - id: etbRoot - width: wrapText ? 100 : etbTextEdit.paintedWidth + 5 - height: (maximumHeight && maximumHeight < etbTextEdit.paintedHeight) ? - maximumHeight : etbTextEdit.paintedHeight - clip: true - radius: 3 - color: UISettings.bgMedium - - property real maximumHeight - - property alias inputFocus: etbTextEdit.focus - property alias inputText: etbTextEdit.text - property alias readOnly: etbTextEdit.readOnly - property int fontSize: UISettings.textSizeDefault - property int textAlignment: TextInput.AlignLeft - property bool wrapText: true - - signal clicked() - signal textChanged(var text) - - property string originalText - - function enableEditing(enable) - { - if (enable === true) - { - originalText = etbTextEdit.text - etbTextEdit.forceActiveFocus() - etbTextEdit.cursorVisible = true - etbTextEdit.cursorPosition = etbTextEdit.text.length - } - else - { - etbTextEdit.select(0, 0) - } - - etbTextEdit.readOnly = !enable - etbMouseArea.enabled = !enable - } - - TextEdit - { - id: etbTextEdit - width: wrapText ? parent.width : Text.paintedWidth - height: wrapText ? parent.height : Text.paintedHeight - readOnly: true - color: UISettings.fgMain - selectionColor: UISettings.highlightPressed - //clip: true - horizontalAlignment: textAlignment - font.family: UISettings.robotoFontName - font.pixelSize: fontSize - selectByMouse: true - mouseSelectionMode: TextEdit.SelectWords - wrapMode: wrapText ? Text.Wrap : Text.NoWrap - - //onTextChanged: etbRoot.textChanged(text) - - onEditingFinished: - { - etbRoot.enableEditing(false) - if (text !== "") - etbRoot.textChanged(text) - else - text = etbRoot.originalText - } - Keys.onReturnPressed: - { - etbRoot.enableEditing(false) - if (text !== "") - etbRoot.textChanged(text) - else - text = etbRoot.originalText - } - Keys.onEscapePressed: - { - etbRoot.enableEditing(false) - text = etbRoot.originalText - } - } - - MouseArea - { - id: etbMouseArea - anchors.fill: parent - z: 1 - - onClicked: etbRoot.clicked() - onDoubleClicked: etbRoot.enableEditing(true) - } -} diff --git a/qmlui/qml/ExternalControlDelegate.qml b/qmlui/qml/ExternalControlDelegate.qml index 0111edb1ad..e7398a7963 100644 --- a/qmlui/qml/ExternalControlDelegate.qml +++ b/qmlui/qml/ExternalControlDelegate.qml @@ -30,7 +30,7 @@ Column property var dObjRef: null property bool invalid: false property int controlID - property alias controlIndex: controlsCombo.currentIndex + property alias inputModel: controlsCombo.model property var universe property var channel property string uniName @@ -58,8 +58,6 @@ Column Layout.fillWidth: true Layout.columnSpan: 2 height: UISettings.listItemHeight - model: dObjRef ? dObjRef.externalControlsList : null - currValue: controlID onValueChanged: { @@ -157,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/ExternalControls.qml b/qmlui/qml/ExternalControls.qml index 342d2c2b13..fb38d1a6bb 100644 --- a/qmlui/qml/ExternalControls.qml +++ b/qmlui/qml/ExternalControls.qml @@ -139,8 +139,8 @@ Column onLoaded: { item.dObjRef = objRef + item.inputModel = objRef.externalControlsList item.controlID = modelData.id - item.controlIndex = modelData.cIndex if (modelData.invalid) item.invalid = modelData.invalid diff --git a/qmlui/qml/FixtureConsole.qml b/qmlui/qml/FixtureConsole.qml index 8193627731..0ab730f138 100644 --- a/qmlui/qml/FixtureConsole.qml +++ b/qmlui/qml/FixtureConsole.qml @@ -146,7 +146,7 @@ Rectangle id: chDelegate color: "transparent" border.width: 1 - border.color: "#111" + border.color: UISettings.borderColorDark width: UISettings.iconSizeDefault height: channelsRow.height diff --git a/qmlui/qml/FunctionDelegate.qml b/qmlui/qml/FunctionDelegate.qml index 2508c9814e..3f59a39407 100644 --- a/qmlui/qml/FunctionDelegate.qml +++ b/qmlui/qml/FunctionDelegate.qml @@ -51,7 +51,6 @@ Rectangle signal toggled signal destruction(int ID, var qItem) - signal mouseEvent(int type, int iID, int iType, var qItem, int mouseMods) Component.onDestruction: diff --git a/qmlui/qml/GenericMultiDragItem.qml b/qmlui/qml/GenericMultiDragItem.qml index 4638e7af7d..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 @@ -28,7 +28,6 @@ Item property string itemIcon /** Generic list of items that this component represents */ property var itemsList: [] - property bool multipleItems: itemsList.length > 1 ? true : false property int modifiers: 0 Rectangle @@ -53,7 +52,7 @@ Item } Rectangle { - visible: multipleItems + visible: itemsList.length > 1 width: topItem.width height: topItem.height x: topItem.height / 5 @@ -65,7 +64,7 @@ Item } Rectangle { - visible: multipleItems + visible: itemsList.length > 2 width: topItem.width height: topItem.height x: (topItem.height / 5) * 2 diff --git a/qmlui/qml/IconButton.qml b/qmlui/qml/IconButton.qml index c8882def3e..2cd64f1a94 100644 --- a/qmlui/qml/IconButton.qml +++ b/qmlui/qml/IconButton.qml @@ -44,7 +44,7 @@ Button property alias border: contentBody.border property alias radius: contentBody.radius property string imgSource: "" - property int imgMargins: 4 + property int imgMargins: 6 property string faSource: "" property color faColor: UISettings.bgStrong @@ -76,7 +76,7 @@ Button background: Rectangle { - color: UISettings.bgMain + color: UISettings.bgMedium border.width: 1 border.color: UISettings.bgLight } @@ -110,7 +110,7 @@ Button anchors.centerIn: parent color: faColor font.family: "FontAwesome" - font.pixelSize: control.height - imgMargins + font.pixelSize: control.height - imgMargins - 2 text: faSource } } diff --git a/qmlui/qml/KeyPad.qml b/qmlui/qml/KeyPad.qml index 49bfdc6ba7..ffa0f51c0e 100644 --- a/qmlui/qml/KeyPad.qml +++ b/qmlui/qml/KeyPad.qml @@ -20,6 +20,8 @@ import QtQuick 2.0 import QtQuick.Layouts 1.1 +import "TimeUtils.js" as TimeUtils + import "." Rectangle @@ -32,10 +34,16 @@ Rectangle property bool showDMXcontrol: true property bool showTapButton: false - property double tapTimeValue: 0 + property alias commandString: commandBox.text property real itemHeight: Math.max(UISettings.iconSizeDefault, keyPadRoot.height / keyPadGrid.rows) - 3 + //needed for bpm tapping + property double tapTimeValue: 0 + property int tapCount: 0 + property double lastTap: 0 + property var tapHistory: [] + onVisibleChanged: if (visible) commandBox.selectAndFocus() signal executeCommand(string cmd) @@ -95,18 +103,31 @@ Rectangle { tapTimer.stop() tapButton.border.color = UISettings.bgMedium - tapTimeValue = 0 + lastTap = 0 + tapHistory = [] } else { var currTime = new Date().getTime() - if (tapTimeValue != 0) + + if (lastTap != 0 && currTime - lastTap < 1500) { - keyPadRoot.tapTimeChanged(currTime - tapTimeValue) - tapTimer.interval = currTime - tapTimeValue + var newTime = currTime - lastTap + + tapHistory.push(newTime) + + tapTimeValue = TimeUtils.calculateBPMByTapIntervals(tapHistory) + + keyPadRoot.tapTimeChanged(tapTimeValue) + tapTimer.interval = tapTimeValue tapTimer.restart() } - tapTimeValue = currTime + else + { + lastTap = 0 + tapHistory = [] + } + lastTap = currTime } } } diff --git a/qmlui/qml/KeyboardSequenceDelegate.qml b/qmlui/qml/KeyboardSequenceDelegate.qml index a588366981..6f61848ef5 100644 --- a/qmlui/qml/KeyboardSequenceDelegate.qml +++ b/qmlui/qml/KeyboardSequenceDelegate.qml @@ -29,7 +29,7 @@ Column property var dObjRef: null property int controlID - property alias controlIndex: controlsCombo.currentIndex + property alias inputModel: controlsCombo.model property string sequence property bool invalid: false @@ -51,8 +51,7 @@ Column Layout.fillWidth: true Layout.columnSpan: 3 height: UISettings.listItemHeight - model: dObjRef ? dObjRef.externalControlsList : null - //currentValue: controlID + currValue: controlID onValueChanged: { if (dObjRef && value != controlID) diff --git a/qmlui/qml/MainView.qml b/qmlui/qml/MainView.qml index 1d1de0a8e5..5204b48b1a 100644 --- a/qmlui/qml/MainView.qml +++ b/qmlui/qml/MainView.qml @@ -31,7 +31,7 @@ Rectangle width: 800 height: 600 anchors.fill: parent - color: UISettings.bgMain + color: UISettings.bgMedium property string currentContext: "" @@ -94,12 +94,22 @@ Rectangle clientAccessPopup.open() } + function saveProject() + { + actionsMenu.handleSaveAction() + } + function saveBeforeExit() { //actionsMenu.open() actionsMenu.saveBeforeExit() } + function loadResource(qmlRes) + { + mainViewLoader.source = qmlRes + } + FontLoader { source: "qrc:/RobotoCondensed-Regular.ttf" @@ -134,6 +144,7 @@ Rectangle MenuBarEntry { id: actEntry + Layout.alignment: Qt.AlignTop imgSource: "qrc:/qlcplus.svg" entryText: qsTr("Actions") onPressed: actionsMenu.open() @@ -155,6 +166,7 @@ Rectangle { id: fnfEntry property string ctxName: "FIXANDFUNC" + Layout.alignment: Qt.AlignTop property string ctxRes: "qrc:/FixturesAndFunctions.qml" imgSource: "qrc:/editor.svg" @@ -170,6 +182,7 @@ Rectangle MenuBarEntry { id: vcEntry + Layout.alignment: Qt.AlignTop property string ctxName: "VC" property string ctxRes: "qrc:/VirtualConsole.qml" @@ -191,6 +204,7 @@ Rectangle MenuBarEntry { id: sdEntry + Layout.alignment: Qt.AlignTop property string ctxName: "SDESK" property string ctxRes: "qrc:/SimpleDesk.qml" @@ -212,6 +226,7 @@ Rectangle MenuBarEntry { id: smEntry + Layout.alignment: Qt.AlignTop property string ctxName: "SHOWMGR" property string ctxRes: "qrc:/ShowManager.qml" @@ -233,6 +248,7 @@ Rectangle MenuBarEntry { id: ioEntry + Layout.alignment: Qt.AlignTop property string ctxName: "IOMGR" property string ctxRes: "qrc:/InputOutputManager.qml" @@ -255,7 +271,7 @@ Rectangle { // acts like an horizontal spacer Layout.fillWidth: true - height: parent.height + implicitHeight: parent.height color: "transparent" } RobotoText @@ -263,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 { @@ -284,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" @@ -305,7 +325,48 @@ Rectangle { id: beatSignal target: ioManager - onBeat: cAnim.restart() + function onBeat() + { + cAnim.restart() + } + } + } + IconButton + { + id: stopAllButton + implicitWidth: UISettings.iconSizeDefault + implicitHeight: UISettings.iconSizeDefault + Layout.alignment: Qt.AlignTop + enabled: runningCount ? true : false + bgColor: "transparent" + imgSource: "qrc:/stop.svg" + tooltip: qsTr("Stop all the running functions") + onClicked: qlcplus.stopAllFunctions() + + property int runningCount: qlcplus.runningFunctionsCount + + onRunningCountChanged: console.log("Functions running: " + runningCount) + + Rectangle + { + x: parent.width / 2 + y: parent.height / 2 + width: parent.width * 0.4 + height: width + color: UISettings.highlight + border.width: 1 + border.color: UISettings.fgMain + radius: 3 + clip: true + visible: stopAllButton.runningCount + + RobotoText + { + anchors.centerIn: parent + height: parent.height * 0.7 + label: stopAllButton.runningCount + fontSize: height + } } } diff --git a/qmlui/qml/PaletteFanningBox.qml b/qmlui/qml/PaletteFanningBox.qml index 1ed11c6613..393b82fe63 100644 --- a/qmlui/qml/PaletteFanningBox.qml +++ b/qmlui/qml/PaletteFanningBox.qml @@ -45,6 +45,12 @@ Item palette = paletteManager.getEditingPalette(paletteType) } + Component.onDestruction: + { + paletteManager.releaseEditingPalette(paletteType) + palette = null + } + function updateValue(value) { paletteManager.updatePalette(palette, value) @@ -114,32 +120,6 @@ Item palette.fanningValue = color } - function stringFromType() - { - switch(typeButton.currentValue) - { - case QLCPalette.Flat: return qsTr("Flat") - case QLCPalette.Linear: return qsTr("Linear") - case QLCPalette.Square: return qsTr("Square") - case QLCPalette.Saw: return qsTr("Saw") - case QLCPalette.Sine: return qsTr("Sine") - } - return "" - } - - function stringFromLayout() - { - switch(layoutButton.currentValue) - { - case QLCPalette.LeftToRight: return qsTr("Left to right") - case QLCPalette.RightToLeft: return qsTr("Right to left") - case QLCPalette.TopToBottom: return qsTr("Top to bottom") - case QLCPalette.BottomToTop: return qsTr("Bottom to top") - case QLCPalette.Centered: return qsTr("Centered") - } - return "" - } - PopupCreatePalette { id: palettePopup @@ -233,7 +213,7 @@ Item RobotoText { height: UISettings.iconSizeMedium - label: stringFromType() + label: typeButton.currentText } } @@ -257,15 +237,19 @@ Item ListModel { id: layoutModel - ListElement { mLabel: qsTr("Left to right"); mIcon: "qrc:/layout-ltr.svg"; mValue: QLCPalette.LeftToRight } - ListElement { mLabel: qsTr("Right to left"); mIcon: "qrc:/layout-rtl.svg"; mValue: QLCPalette.RightToLeft } - ListElement { mLabel: qsTr("Top to bottom"); mIcon: "qrc:/layout-ttb.svg"; mValue: QLCPalette.TopToBottom } - ListElement { mLabel: qsTr("Bottom to top"); mIcon: "qrc:/layout-btt.svg"; mValue: QLCPalette.BottomToTop } - ListElement { mLabel: qsTr("Centered"); mIcon: "qrc:/layout-center.svg"; mValue: QLCPalette.Centered } + ListElement { mLabel: qsTr("X Ascending"); mIcon: "qrc:/layout-ltr.svg"; mValue: QLCPalette.XAscending } + ListElement { mLabel: qsTr("X Descending"); mIcon: "qrc:/layout-rtl.svg"; mValue: QLCPalette.XDescending } + //ListElement { mLabel: qsTr("X Centered"); mIcon: "qrc:/layout-center.svg"; mValue: QLCPalette.XCentered } + ListElement { mLabel: qsTr("Y Ascending"); mIcon: "qrc:/layout-btt.svg"; mValue: QLCPalette.YAscending } + ListElement { mLabel: qsTr("Y Descending"); mIcon: "qrc:/layout-ttb.svg"; mValue: QLCPalette.YDescending } + //ListElement { mLabel: qsTr("Y Centered"); mIcon: "qrc:/layout-center.svg"; mValue: QLCPalette.YCentered } + ListElement { mLabel: qsTr("Z Ascending"); mIcon: "qrc:/layout-ftb.svg"; mValue: QLCPalette.ZAscending } + ListElement { mLabel: qsTr("Z Descending"); mIcon: "qrc:/layout-btf.svg"; mValue: QLCPalette.ZDescending } + //ListElement { mLabel: qsTr("Z Centered"); mIcon: "qrc:/layout-center.svg"; mValue: QLCPalette.ZCentered } } model: layoutModel - currValue: boxRoot.palette ? boxRoot.palette.fanningLayout : QLCPalette.LeftToRight + currValue: boxRoot.palette ? boxRoot.palette.fanningLayout : QLCPalette.XAscending onValueChanged: { if (boxRoot.palette) @@ -279,7 +263,7 @@ Item RobotoText { height: UISettings.iconSizeMedium - label: stringFromLayout() + label: layoutButton.currentText } } 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/RobotoText.qml b/qmlui/qml/RobotoText.qml index 972b1647ef..a5dd4a1b6a 100644 --- a/qmlui/qml/RobotoText.qml +++ b/qmlui/qml/RobotoText.qml @@ -23,7 +23,7 @@ import "." Rectangle { id: rtRoot - width: wrapText ? 100 : textBox.paintedWidth + rightMargin + width: wrapText ? 100 : leftMargin + textBox.paintedWidth + rightMargin height: UISettings.iconSizeDefault color: "transparent" diff --git a/qmlui/qml/SectionBox.qml b/qmlui/qml/SectionBox.qml index babf5a8154..c9dc4a4347 100644 --- a/qmlui/qml/SectionBox.qml +++ b/qmlui/qml/SectionBox.qml @@ -69,6 +69,14 @@ Rectangle onClicked: boxRoot.isExpanded = !boxRoot.isExpanded } + + Rectangle + { + width: parent.width + height: 1 + y: parent.height - 1 + color: UISettings.sectionHeaderDiv + } } Loader diff --git a/qmlui/qml/SidePanel.qml b/qmlui/qml/SidePanel.qml index 5e9f0f7b71..84df2897c0 100644 --- a/qmlui/qml/SidePanel.qml +++ b/qmlui/qml/SidePanel.qml @@ -76,7 +76,7 @@ Rectangle { ignoreUnknownSignals: true target: viewLoader.item - onRequestView: + function onRequestView(ID, qmlSrc) { console.log("SidePanel loader ID requested: " + ID) itemID = ID diff --git a/qmlui/qml/SimpleDesk.qml b/qmlui/qml/SimpleDesk.qml index 059eafb36b..0f0beb57d5 100644 --- a/qmlui/qml/SimpleDesk.qml +++ b/qmlui/qml/SimpleDesk.qml @@ -34,10 +34,19 @@ Rectangle property bool dmxValues: true property real maxValue: 255 + ChannelToolLoader + { + id: channelToolLoader + z: 2 + + onValueChanged: simpleDesk.setValue(fixtureID, channelIndex, value) + } + SplitView { anchors.fill: parent orientation: Qt.Vertical + z: 1 // Top view (faders) Rectangle @@ -88,7 +97,6 @@ Rectangle tooltip: qsTr("Reset the whole universe") onClicked: { - contextManager.resetDumpValues() simpleDesk.resetUniverse(viewUniverseCombo.currentIndex) } } @@ -102,7 +110,7 @@ Rectangle z: 2 imgSource: "qrc:/dmxdump.svg" tooltip: qsTr("Dump on a new Scene") - counter: contextManager ? contextManager.dumpValuesCount && (qlcplus.accessMask & App.AC_FunctionEditing) : 0 + counter: simpleDesk ? simpleDesk.dumpValuesCount && (qlcplus.accessMask & App.AC_FunctionEditing) : 0 onClicked: { @@ -129,7 +137,7 @@ Rectangle { anchors.centerIn: parent height: parent.height * 0.7 - label: contextManager ? contextManager.dumpValuesCount : "" + label: simpleDesk ? simpleDesk.dumpValuesCount : "" fontSize: height } } @@ -138,8 +146,9 @@ Rectangle { id: dmxDumpDialog implicitWidth: Math.min(UISettings.bigItemHeight * 4, mainView.width / 3) + channelsMask: simpleDesk ? simpleDesk.dumpChannelMask : 0 - onAccepted: contextManager.dumpDmxChannels(sceneName, getChannelsMask()) + onAccepted: simpleDesk.dumpDmxChannels(sceneName, getChannelsMask()) } } @@ -190,10 +199,12 @@ Rectangle { id: chRoot implicitWidth: UISettings.iconSizeDefault - height: parent.height - sbar.height + height: channelView.height - sbar.height color: { if (isOverride) - return "red"; + { + return "red" + } else { switch(chDisplay) @@ -229,6 +240,8 @@ Rectangle onClicked: { + if (fixtureObj) + channelToolLoader.loadChannelTool(this, fixtureObj.id, model.chIndex, model.chValue) } } @@ -242,10 +255,11 @@ Rectangle from: 0 to: 255 value: model.chValue - onMoved: { + onMoved: + { model.isOverride = true model.chValue = valueAt(position) - simpleDesk.setValue(fixtureObj ? fixtureObj.id : -1, index, model.chValue) + simpleDesk.setValue(fixtureObj ? fixtureObj.id : -1, fixtureObj ? model.chIndex : index, model.chValue) } } @@ -262,10 +276,11 @@ 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, index, model.chValue) + simpleDesk.setValue(fixtureObj ? fixtureObj.id : -1, fixtureObj ? model.chIndex : index, model.chValue) } } @@ -289,7 +304,7 @@ Rectangle onClicked: { var channel = index - (fixtureObj ? fixtureObj.address : 0) - contextManager.unsetDumpValue(fixtureObj ? fixtureObj.id : -1, channel) + simpleDesk.unsetDumpValue(fixtureObj ? fixtureObj.id : -1, channel) simpleDesk.resetChannel(index) } } @@ -366,7 +381,7 @@ Rectangle x: parent.width - width height: UISettings.listItemHeight rightMargin: 5 - label: (fixtureObj.address + 1) + " - " + (fixtureObj.address + fixtureObj.channels + 1) + label: (fixtureObj.address + 1) + " - " + (fixtureObj.address + fixtureObj.channels) } } } diff --git a/qmlui/qml/SingleAxisTool.qml b/qmlui/qml/SingleAxisTool.qml index 5c1aff7435..9d773934d0 100644 --- a/qmlui/qml/SingleAxisTool.qml +++ b/qmlui/qml/SingleAxisTool.qml @@ -33,12 +33,13 @@ Rectangle border.width: 2 property real maxDegrees: 360 - property int currentValue: 0 // in DMX values + property int currentValue: 0 // as DMX value property int currentDegrees: Math.round((currentValue * maxDegrees) / 255.0) property bool closeOnSelect: false 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/TimeEditTool.qml b/qmlui/qml/TimeEditTool.qml index 748996ac06..d6124e4a8b 100644 --- a/qmlui/qml/TimeEditTool.qml +++ b/qmlui/qml/TimeEditTool.qml @@ -42,6 +42,10 @@ GridLayout /* The TAP time counter */ property double tapTimeValue: 0 + //needed for bpm tapping + property int tapCount: 0 + property double lastTap: 0 + property var tapHistory: [] /* If needed, this property can be used to recognize which type of speed value is being edited */ @@ -177,23 +181,36 @@ GridLayout onClicked: { /* right click resets the current TAP time */ - if (mouseButton === Qt.RightButton) - { - tapTimer.stop() - tapButton.border.color = UISettings.bgMedium - tapTimeValue = 0 - } - else - { - var currTime = new Date().getTime() - if (tapTimeValue != 0) + if (mouseButton === Qt.RightButton) { - updateTime(currTime - tapTimeValue, "") - tapTimer.interval = timeValue - tapTimer.restart() + tapTimer.stop() + tapButton.border.color = UISettings.bgMedium + lastTap = 0 + tapHistory = [] + } + else + { + var currTime = new Date().getTime() + + if (lastTap != 0 && currTime - lastTap < 1500) + { + var newTime = currTime - lastTap + + tapHistory.push(newTime) + + tapTimeValue = TimeUtils.calculateBPMByTapIntervals(tapHistory) + + updateTime(tapTimeValue, "") + tapTimer.interval = timeValue + tapTimer.restart() + } + else + { + lastTap = 0 + tapHistory = [] + } + lastTap = currTime } - tapTimeValue = currTime - } } } diff --git a/qmlui/qml/TreeNodeDelegate.qml b/qmlui/qml/TreeNodeDelegate.qml index dfd7f6ef92..1b9f1bea3c 100644 --- a/qmlui/qml/TreeNodeDelegate.qml +++ b/qmlui/qml/TreeNodeDelegate.qml @@ -98,63 +98,14 @@ Column sourceSize: Qt.size(width, height) } - TextInput + CustomTextInput { - property string originalText - id: nodeLabel - z: 0 width: nodeBgRect.width - x - 1 - height: UISettings.listItemHeight - readOnly: true text: cRef ? cRef.name : textLabel - verticalAlignment: TextInput.AlignVCenter - color: UISettings.fgMain - font.family: UISettings.robotoFontName - font.pixelSize: UISettings.textSizeDefault - echoMode: TextInput.Normal - selectByMouse: true - selectionColor: "#4DB8FF" - selectedTextColor: "#111" - - function disableEditing() - { - z = 0 - select(0, 0) - readOnly = true - cursorVisible = false - } - - Keys.onPressed: - { - switch(event.key) - { - case Qt.Key_F2: - originalText = textLabel - z = 5 - readOnly = false - cursorPosition = text.length - cursorVisible = true - break; - case Qt.Key_Escape: - disableEditing() - nodeLabel.text = originalText - break; - default: - event.accepted = false - return - } + originalText: text - event.accepted = true - } - - onEditingFinished: - { - if (readOnly) - return - disableEditing() - nodeContainer.pathChanged(nodePath, text) - } + onTextConfirmed: nodeContainer.pathChanged(nodePath, text) } } // Row @@ -231,7 +182,7 @@ Column if (model.classRef !== undefined && item.hasOwnProperty('cRef')) item.cRef = classRef - if (item.hasOwnProperty('itemID')) + if (item.hasOwnProperty('itemID') && model.classRef !== undefined) item.itemID = id if (item.hasOwnProperty('inGroup')) @@ -263,13 +214,13 @@ Column Connections { target: item - onMouseEvent: + function onMouseEvent(type, iID, iType, qItem, mouseMods) { console.log("Got generic tree node mouse event") switch (type) { case App.Clicked: - if (qItem == item) + if (qItem === item) { model.isSelected = (mouseMods & Qt.ControlModifier) ? 2 : 1 if (model.hasChildren) @@ -277,11 +228,11 @@ Column } break; case App.Checked: - if (qItem == item) + if (qItem === item) model.isChecked = iType break; case App.DragStarted: - if (qItem == item && !model.isSelected) + if (qItem === item && !model.isSelected) { model.isSelected = 1 // invalidate the modifiers to force a single selection @@ -298,13 +249,19 @@ Column { ignoreUnknownSignals: true target: item - onPathChanged: nodeContainer.pathChanged(oldPath, newPath) + function onPathChanged(oldPath, newPath) + { + nodeContainer.pathChanged(oldPath, newPath) + } } Connections { ignoreUnknownSignals: true target: item - onItemsDropped: nodeContainer.itemsDropped(path) + function onItemsDropped(path) + { + nodeContainer.itemsDropped(path) + } } } } diff --git a/qmlui/qml/UISettings.qml b/qmlui/qml/UISettings.qml index 734b05ab21..8318576475 100644 --- a/qmlui/qml/UISettings.qml +++ b/qmlui/qml/UISettings.qml @@ -25,8 +25,9 @@ QtObject { property string robotoFontName: "Roboto Condensed" + property real scalingFactor: 1.0 + /* Colors */ - property color bgMain: "#303030" property color bgStronger: "#161616" property color bgStrong: "#232323" property color bgMedium: "#333" @@ -39,6 +40,7 @@ QtObject property color fgLight: "#aaa" property color sectionHeader: "#31456B" + property color sectionHeaderDiv: "#22304a" property color highlight: "#0978FF" property color highlightPressed: "#044089" property color hover: "#B6B6B6" @@ -56,18 +58,19 @@ QtObject property color toolbarSelectionSub: "yellow" /* Sizes */ - property int textSizeDefault: screenPixelDensity * 4.5 - property real iconSizeDefault: screenPixelDensity * 10 // more or less the size of a finger - property real iconSizeMedium: screenPixelDensity * 8 - property real listItemHeight: screenPixelDensity * 7 - property real mediumItemHeight: screenPixelDensity * 15 - property real bigItemHeight: screenPixelDensity * 25 - property real scrollBarWidth: screenPixelDensity * 6 - property real sidePanelWidth: 350 + property int textSizeDefault: screenPixelDensity * scalingFactor * 4.5 + property real iconSizeDefault: screenPixelDensity * scalingFactor * 10 // more or less the size of a finger + property real iconSizeMedium: screenPixelDensity * scalingFactor * 8 + property real listItemHeight: screenPixelDensity * scalingFactor * 7 + property real mediumItemHeight: screenPixelDensity * scalingFactor * 15 + property real bigItemHeight: screenPixelDensity * scalingFactor * 25 + property real scrollBarWidth: screenPixelDensity * scalingFactor * 6 + property real sidePanelWidth: screenPixelDensity * scalingFactor * 50 // channel properties column widths + property real chPropsModesWidth: bigItemHeight * 1.2 property real chPropsFlagsWidth: bigItemHeight - property real chPropsCanFadeWidth: bigItemHeight * 0.9 + property real chPropsCanFadeWidth: bigItemHeight * 0.7 property real chPropsPrecedenceWidth: bigItemHeight * 1.2 property real chPropsModifierWidth: bigItemHeight } diff --git a/qmlui/qml/UISettingsEditor.qml b/qmlui/qml/UISettingsEditor.qml new file mode 100644 index 0000000000..7afe49446b --- /dev/null +++ b/qmlui/qml/UISettingsEditor.qml @@ -0,0 +1,690 @@ +/* + Q Light Controller Plus + PopupUISettings.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.2 + +import org.qlcplus.classes 1.0 +import "." + +Rectangle +{ + id: editorRoot + anchors.fill: parent + color: "transparent" + + 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 + } + + ColorTool + { + id: colorTool + z: 2 + x: editorGrid.width + //parent: mainView + visible: false + showPalette: false + + property Item rectItem + property Item selectedItem + + onColorChanged: + { + rectItem.color = Qt.rgba(r, g, b, 1.0) + selectedItem.updateColor(Qt.rgba(r, g, b, 1.0)) + } + onClose: visible = false + } + + CustomPopupDialog + { + id: messagePopup + standardButtons: Dialog.Ok + onAccepted: close() + } + + Component + { + id: colorSelector + + RowLayout + { + height: origIconMedium + + property color origColor + property Item pItem + + function init(item, original, col) + { + pItem = item + origColor = original + colorRect.color = col + } + + Rectangle + { + id: colorRect + height: origIconMedium + width: height * 4 + border.color: csMouseArea.containsMouse ? "white" : "gray" + border.width: 2 + + MouseArea + { + id: csMouseArea + anchors.fill: parent + hoverEnabled: true + onClicked: + { + colorTool.rectItem = colorRect + colorTool.selectedItem = pItem + colorTool.visible = true + } + } + } + + IconButton + { + width: origIconMedium + height: width + imgSource: "qrc:/undo.svg" + tooltip: qsTr("Reset to default") + + onClicked: + { + pItem.updateColor(origColor) + colorRect.color = origColor + } + } + } + } + + GridLayout + { + id: editorGrid + columnSpacing: origIconMedium + rowSpacing: 10 + columns: 4 + z: 1 + + // Row 1 + RobotoText + { + height: origItemHeight + fontSize: origTextSizeDefault + label: qsTr("Scaling factor") + } + RowLayout + { + Slider + { + id: sfSlider + orientation: Qt.Horizontal + from: 0.3 + to: 2 + value: UISettings.scalingFactor + wheelEnabled: true + onMoved: uiManager.setModified("scalingFactor", value) + } + + RobotoText + { + height: origItemHeight + fontSize: origTextSizeDefault + label: sfSlider.value.toFixed(2) + "x" + } + + IconButton + { + id: sfRestore + width: origIconMedium + height: width + imgSource: "qrc:/undo.svg" + tooltip: qsTr("Reset to default") + + property real origScaleFactor + + onClicked: uiManager.setModified("scalingFactor", 1.0) + } + } + + Rectangle + { + Layout.columnSpan: 2 + height: origItemHeight + color: "transparent" + } + + // Row 2 + RobotoText + { + height: origItemHeight + fontSize: origTextSizeDefault + label: qsTr("Background darker") + } + Loader + { + property string kName: "bgStronger" + sourceComponent: colorSelector + onLoaded: item.init(this, uiManager.getDefault(kName), + uiManager.getModified(kName)) + + function updateColor(col) + { + uiManager.setModified(kName, col) + } + } + + RobotoText + { + height: origItemHeight + fontSize: origTextSizeDefault + label: qsTr("Background dark") + } + Loader + { + property string kName: "bgStrong" + sourceComponent: colorSelector + onLoaded: item.init(this, uiManager.getDefault(kName), + uiManager.getModified(kName)) + + function updateColor(col) + { + uiManager.setModified(kName, col) + } + } + + //Row 3 + RobotoText + { + height: origItemHeight + fontSize: origTextSizeDefault + label: qsTr("Background medium") + } + Loader + { + property string kName: "bgMedium" + sourceComponent: colorSelector + onLoaded: item.init(this, uiManager.getDefault(kName), + uiManager.getModified(kName)) + + function updateColor(col) + { + uiManager.setModified(kName, col) + } + } + + RobotoText + { + height: origItemHeight + fontSize: origTextSizeDefault + label: qsTr("Background light") + } + Loader + { + property string kName: "bgLight" + sourceComponent: colorSelector + onLoaded: item.init(this, uiManager.getDefault(kName), + uiManager.getModified(kName)) + + function updateColor(col) + { + uiManager.setModified(kName, col) + } + } + + //Row 4 + RobotoText + { + height: origItemHeight + fontSize: origTextSizeDefault + label: qsTr("Background lighter") + } + Loader + { + property string kName: "bgLighter" + sourceComponent: colorSelector + onLoaded: item.init(this, uiManager.getDefault(kName), + uiManager.getModified(kName)) + + function updateColor(col) + { + uiManager.setModified(kName, col) + } + } + + RobotoText + { + height: origItemHeight + fontSize: origTextSizeDefault + label: qsTr("Controls background") + } + Loader + { + property string kName: "bgControl" + sourceComponent: colorSelector + onLoaded: item.init(this, uiManager.getDefault(kName), + uiManager.getModified(kName)) + + function updateColor(col) + { + uiManager.setModified(kName, col) + } + } + + //Row 5 + RobotoText + { + height: origItemHeight + fontSize: origTextSizeDefault + label: qsTr("Foreground main") + } + Loader + { + property string kName: "fgMain" + sourceComponent: colorSelector + onLoaded: item.init(this, uiManager.getDefault(kName), + uiManager.getModified(kName)) + + function updateColor(col) + { + uiManager.setModified(kName, col) + } + } + + RobotoText + { + height: origItemHeight + fontSize: origTextSizeDefault + label: qsTr("Foreground medium") + } + Loader + { + property string kName: "fgMedium" + sourceComponent: colorSelector + onLoaded: item.init(this, uiManager.getDefault(kName), + uiManager.getModified(kName)) + + function updateColor(col) + { + uiManager.setModified(kName, col) + } + } + + //Row 6 + RobotoText + { + height: origItemHeight + fontSize: origTextSizeDefault + label: qsTr("Foreground light") + } + Loader + { + property string kName: "fgLight" + sourceComponent: colorSelector + onLoaded: item.init(this, uiManager.getDefault(kName), + uiManager.getModified(kName)) + + function updateColor(col) + { + uiManager.setModified(kName, col) + } + } + + RobotoText + { + height: origItemHeight + fontSize: origTextSizeDefault + label: qsTr("Toolbar gradient start") + } + Loader + { + property string kName: "toolbarStartMain" + sourceComponent: colorSelector + onLoaded: item.init(this, uiManager.getDefault(kName), + uiManager.getModified(kName)) + + function updateColor(col) + { + uiManager.setModified(kName, col) + } + } + + //Row 7 + RobotoText + { + height: origItemHeight + fontSize: origTextSizeDefault + label: qsTr("Sub-toolbar gradient start") + } + Loader + { + property string kName: "toolbarStartSub" + sourceComponent: colorSelector + onLoaded: item.init(this, uiManager.getDefault(kName), + uiManager.getModified(kName)) + + function updateColor(col) + { + uiManager.setModified(kName, col) + } + } + + RobotoText + { + height: origItemHeight + fontSize: origTextSizeDefault + label: qsTr("Toolbar gradient end") + } + Loader + { + property string kName: "toolbarEnd" + sourceComponent: colorSelector + onLoaded: item.init(this, uiManager.getDefault(kName), + uiManager.getModified(kName)) + + function updateColor(col) + { + uiManager.setModified(kName, col) + } + } + + //Row 8 + RobotoText + { + height: origItemHeight + fontSize: origTextSizeDefault + label: qsTr("Toolbar hover gradient start") + } + Loader + { + property string kName: "toolbarHoverStart" + sourceComponent: colorSelector + onLoaded: item.init(this, uiManager.getDefault(kName), + uiManager.getModified(kName)) + + function updateColor(col) + { + uiManager.setModified(kName, col) + } + } + + + RobotoText + { + height: origItemHeight + fontSize: origTextSizeDefault + label: qsTr("Toolbar hover gradient end") + } + Loader + { + property string kName: "toolbarHoverEnd" + sourceComponent: colorSelector + onLoaded: item.init(this, uiManager.getDefault(kName), + uiManager.getModified(kName)) + + function updateColor(col) + { + uiManager.setModified(kName, col) + } + } + + //Row 9 + RobotoText + { + height: origItemHeight + fontSize: origTextSizeDefault + label: qsTr("Toolbar selection") + } + Loader + { + property string kName: "toolbarSelectionMain" + sourceComponent: colorSelector + onLoaded: item.init(this, uiManager.getDefault(kName), + uiManager.getModified(kName)) + + function updateColor(col) + { + uiManager.setModified(kName, col) + } + } + + + RobotoText + { + height: origItemHeight + fontSize: origTextSizeDefault + label: qsTr("Sub-toolbar selection") + } + Loader + { + property string kName: "toolbarSelectionSub" + sourceComponent: colorSelector + onLoaded: item.init(this, uiManager.getDefault(kName), + uiManager.getModified(kName)) + + function updateColor(col) + { + uiManager.setModified(kName, col) + } + } + + //Row 10 + RobotoText + { + height: origItemHeight + fontSize: origTextSizeDefault + label: qsTr("Section header") + } + Loader + { + property string kName: "sectionHeader" + sourceComponent: colorSelector + onLoaded: item.init(this, uiManager.getDefault(kName), + uiManager.getModified(kName)) + + function updateColor(col) + { + uiManager.setModified(kName, col) + } + } + + RobotoText + { + height: origItemHeight + fontSize: origTextSizeDefault + label: qsTr("Section header divider") + } + Loader + { + property string kName: "sectionHeaderDiv" + sourceComponent: colorSelector + onLoaded: item.init(this, uiManager.getDefault(kName), + uiManager.getModified(kName)) + + function updateColor(col) + { + uiManager.setModified(kName, col) + } + } + + //Row 11 + RobotoText + { + height: origItemHeight + fontSize: origTextSizeDefault + label: qsTr("Item highlight") + } + Loader + { + property string kName: "highlight" + sourceComponent: colorSelector + onLoaded: item.init(this, uiManager.getDefault(kName), + uiManager.getModified(kName)) + + function updateColor(col) + { + uiManager.setModified(kName, col) + } + } + + RobotoText + { + height: origItemHeight + fontSize: origTextSizeDefault + label: qsTr("Item highlight pressed") + } + Loader + { + property string kName: "highlightPressed" + sourceComponent: colorSelector + onLoaded: item.init(this, uiManager.getDefault(kName), + uiManager.getModified(kName)) + + function updateColor(col) + { + uiManager.setModified(kName, col) + } + } + + //Row 12 + RobotoText + { + height: origItemHeight + fontSize: origTextSizeDefault + label: qsTr("Item hover") + } + Loader + { + property string kName: "hover" + sourceComponent: colorSelector + onLoaded: item.init(this, uiManager.getDefault(kName), + uiManager.getModified(kName)) + + function updateColor(col) + { + uiManager.setModified(kName, col) + } + } + + RobotoText + { + height: origItemHeight + fontSize: origTextSizeDefault + label: qsTr("Item selection") + } + Loader + { + property string kName: "selection" + sourceComponent: colorSelector + onLoaded: item.init(this, uiManager.getDefault(kName), + uiManager.getModified(kName)) + + function updateColor(col) + { + uiManager.setModified(kName, col) + } + } + + //Row 13 + RobotoText + { + height: origItemHeight + fontSize: origTextSizeDefault + label: qsTr("VC Frame drop area") + } + Loader + { + property string kName: "activeDropArea" + sourceComponent: colorSelector + onLoaded: item.init(this, uiManager.getDefault(kName), + uiManager.getModified(kName)) + + function updateColor(col) + { + uiManager.setModified(kName, col) + } + } + + RobotoText + { + height: origItemHeight + fontSize: origTextSizeDefault + label: qsTr("Item dark border") + } + Loader + { + property string kName: "borderColorDark" + sourceComponent: colorSelector + onLoaded: item.init(this, uiManager.getDefault(kName), + uiManager.getModified(kName)) + + function updateColor(col) + { + uiManager.setModified(kName, col) + } + } + + GenericButton + { + Layout.columnSpan: 4 + Layout.alignment: Qt.AlignHCenter + width: origIconMedium * 10 + height: origIconDefault + fontSize: origTextSizeDefault + label: qsTr("Save to file") + onClicked: + { + var fPath = uiManager.userConfFilepath() + if (uiManager.saveSettings() === true) + { + messagePopup.title = qsTr("Operation completed") + messagePopup.message = qsTr("File successfully saved to:" + "
    " + fPath) + } + else + { + messagePopup.title = qsTr("Error") + messagePopup.message = qsTr("Unable to save file:" + "
    " + fPath) + } + messagePopup.open() + } + + Image + { + x: parent.width * 0.05 + anchors.verticalCenter: parent.verticalCenter + width: parent.height * 0.75 + height: width + source: "qrc:/filesave.svg" + sourceSize: Qt.size(width, height) + } + } + } +} diff --git a/qmlui/qml/WindowLoader.qml b/qmlui/qml/WindowLoader.qml index 84615ea715..630df62b69 100644 --- a/qmlui/qml/WindowLoader.qml +++ b/qmlui/qml/WindowLoader.qml @@ -32,7 +32,7 @@ Rectangle { id: mainView anchors.fill: parent - color: UISettings.bgMain + color: UISettings.bgMedium function closeWindow() { diff --git a/qmlui/qml/fixtureeditor/ChannelEditor.qml b/qmlui/qml/fixtureeditor/ChannelEditor.qml index 64a2a64708..e338fe0e7d 100644 --- a/qmlui/qml/fixtureeditor/ChannelEditor.qml +++ b/qmlui/qml/fixtureeditor/ChannelEditor.qml @@ -20,13 +20,15 @@ import QtQuick 2.14 import QtQuick.Layouts 1.14 import QtQuick.Controls 2.14 +import QtQuick.Dialogs 1.3 import org.qlcplus.classes 1.0 import "." GridLayout { - columns: 4 + columns: 2 + rowSpacing: 5 property EditorRef editorView: null property ChannelEdit editor: null @@ -39,31 +41,79 @@ GridLayout nameEdit.selectAndFocus() } + function updatePresetBox(capIndex) + { + capPresetCombo.currentIndex = editor.getCapabilityPresetAtIndex(capIndex) + presetBox.presetType = editor.getCapabilityPresetType(capIndex) + + switch (presetBox.presetType) + { + case QLCCapability.SingleColor: + colorPreview.biColor = false + colorPreview.primary = editor.getCapabilityValueAt(capIndex, 0) + colorPreview.secondary = "black" + break + case QLCCapability.DoubleColor: + colorPreview.biColor = true + colorPreview.primary = editor.getCapabilityValueAt(capIndex, 0) + colorPreview.secondary = editor.getCapabilityValueAt(capIndex, 1) + break + case QLCCapability.Picture: + goboPicture.source = "file://" + editor.getCapabilityValueAt(capIndex, 0) + break + case QLCCapability.SingleValue: + pValueSpin.value = editor.getCapabilityValueAt(capIndex, 0) + pValueSpin.suffix = editor.getCapabilityPresetUnits(capIndex) + break + case QLCCapability.DoubleValue: + pValueSpin.value = editor.getCapabilityValueAt(capIndex, 0) + pValueSpin.suffix = editor.getCapabilityPresetUnits(capIndex) + sValueSpin.value = editor.getCapabilityValueAt(capIndex, 1) + sValueSpin.suffix = pValueSpin.suffix + break + default: + break + } + } + + FileDialog + { + id: openDialog + visible: false + title: qsTr("Open a picture file") + folder: "file://" + qlcplus.goboSystemPath() + nameFilters: [ qsTr("Gobo pictures") + " (*.jpg *.jpeg *.png *.bmp *.svg)", qsTr("All files") + " (*)" ] + + onAccepted: + { + var str = fileUrl.toString().slice(7) + editor.setCapabilityValueAt(editItem.indexInList, 0, str) + updatePresetBox(editItem.indexInList) + } + } + // row 1 - RobotoText { label: qsTr("Name") } + RobotoText { label: qsTr("Name"); height: UISettings.listItemHeight } CustomTextEdit { id: nameEdit Layout.fillWidth: true - Layout.columnSpan: 3 text: channel ? channel.name : "" onTextChanged: if (channel) channel.name = text } // row 2 - RobotoText { label: qsTr("Preset") } + RobotoText { label: qsTr("Preset"); height: UISettings.listItemHeight } CustomComboBox { Layout.fillWidth: true - Layout.columnSpan: 3 - model: editor ? editor.channelPresetList : null currentIndex: channel ? channel.preset : 0 onCurrentIndexChanged: if (channel) channel.preset = currentIndex } // row 3 - RobotoText { label: qsTr("Type") } + RobotoText { label: qsTr("Type"); height: UISettings.listItemHeight } CustomComboBox { Layout.fillWidth: true @@ -73,7 +123,8 @@ GridLayout onValueChanged: if (editor) editor.group = value } - RobotoText { label: qsTr("Role") } + // row 4 + RobotoText { label: qsTr("Role"); height: UISettings.listItemHeight } RowLayout { Layout.fillWidth: true @@ -89,7 +140,7 @@ GridLayout checked: channel ? !channel.controlByte : false onToggled: if (channel) channel.controlByte = 0 } - RobotoText { label: qsTr("Coarse (MSB)") } + RobotoText { label: qsTr("Coarse (MSB)"); height: UISettings.listItemHeight } CustomCheckBox { implicitHeight: UISettings.listItemHeight @@ -99,38 +150,63 @@ GridLayout checked: channel ? channel.controlByte : false onToggled: if (channel) channel.controlByte = 1 } - RobotoText { label: qsTr("Fine (LSB)") } - } - - // row 4 - RobotoText { label: qsTr("Default value") } - CustomSpinBox - { - Layout.columnSpan: 2 - from: 0 - to: 255 - stepSize: 1 - value: channel ? channel.defaultValue : 0 - onValueChanged: if (channel) channel.defaultValue = value + RobotoText { label: qsTr("Fine (LSB)"); height: UISettings.listItemHeight } } + // row 5 + RobotoText { label: qsTr("Default value"); height: UISettings.listItemHeight } RowLayout { Layout.fillWidth: true Layout.alignment: Qt.AlignRight + CustomSpinBox + { + from: 0 + to: 255 + stepSize: 1 + value: channel ? channel.defaultValue : 0 + onValueChanged: if (channel) channel.defaultValue = value + } + + Rectangle + { + Layout.fillWidth: true + height: UISettings.listItemHeight + color: "transparent" + } + IconButton { id: removeCapButton imgSource: "qrc:/remove.svg" tooltip: qsTr("Delete the selected capabilities") + onClicked: { + editItem.visible = false + editor.removeCapabilityAtIndex(editItem.indexInList) + } + } + + IconButton + { + id: chWizButton + imgSource: "qrc:/wizard.svg" + tooltip: qsTr("Capability wizard") + onClicked: wizardPopup.open() + + PopupChannelWizard + { + id: wizardPopup + chEdit: editor + capabilityWizard: true + } } } // row 5 - capability editor Rectangle { - Layout.columnSpan: 4 + Layout.columnSpan: 2 Layout.fillHeight: true Layout.fillWidth: true color: "transparent" @@ -163,7 +239,7 @@ GridLayout else if (index === capsList.count) { // create a new capability - editor.addCapability() + editor.addNewCapability() } var item = capsList.itemAtIndex(index) @@ -175,35 +251,7 @@ GridLayout editItem.focusItem(fieldIndex) // setup the capability preset items - capPresetCombo.currentIndex = editor.getCapabilityPresetAtIndex(index) - presetBox.presetType = editor.getCapabilityPresetType(index) - - switch (presetBox.presetType) - { - case QLCCapability.SingleColor: - colorPreview.primary = editor.getCapabilityValueAt(index, 0) - break - case QLCCapability.DoubleColor: - colorPreview.primary = editor.getCapabilityValueAt(index, 0) - colorPreview.secondary = editor.getCapabilityValueAt(index, 1) - break - case QLCCapability.Picture: - goboPicture.source = "file://" + editor.getCapabilityValueAt(index, 0) - break - case QLCCapability.SingleValue: - pValueSpin.value = editor.getCapabilityValueAt(index, 0) - pValueSpin.suffix = editor.getCapabilityPresetUnits(index) - break - case QLCCapability.DoubleValue: - pValueSpin.value = editor.getCapabilityValueAt(index, 0) - pValueSpin.suffix = editor.getCapabilityPresetUnits(index) - sValueSpin.value = editor.getCapabilityValueAt(index, 1) - sValueSpin.suffix = pValueSpin.suffix - break - default: - break - } - + updatePresetBox(index) editItem.visible = true } @@ -245,7 +293,7 @@ GridLayout label: qsTr("From") color: UISettings.sectionHeader } - Rectangle { width: 1; height: UISettings.listItemHeight } + Rectangle { width: 1; height: UISettings.listItemHeight; color: UISettings.fgMedium } RobotoText { @@ -254,7 +302,7 @@ GridLayout label: qsTr("To") color: UISettings.sectionHeader } - Rectangle { width: 1; height: UISettings.listItemHeight } + Rectangle { width: 1; height: UISettings.listItemHeight; color: UISettings.fgMedium } RobotoText { @@ -287,39 +335,48 @@ GridLayout id: minValBox width: UISettings.bigItemHeight height: UISettings.listItemHeight - label: cap.min + label: cap ? cap.min : 0 } - Rectangle { width: 1; height: UISettings.listItemHeight } + Rectangle { width: 1; height: UISettings.listItemHeight; color: UISettings.fgMedium } RobotoText { id: maxValBox width: UISettings.bigItemHeight height: UISettings.listItemHeight - label: cap.max + label: cap ? cap.max : 255 } - Rectangle { width: 1; height: UISettings.listItemHeight } + Rectangle { width: 1; height: UISettings.listItemHeight; color: UISettings.fgMedium } RobotoText { id: capDescription Layout.fillWidth: true height: UISettings.listItemHeight - label: cap.name + label: cap ? cap.name : "" } IconButton { - visible: cap.warning + visible: cap ? cap.warning : false height: UISettings.listItemHeight width: height border.width: 0 faSource: FontAwesome.fa_warning faColor: "yellow" - tooltip: capsList.warningDescription(cap.warning) + tooltip: visible ? capsList.warningDescription(cap.warning) : "" } } + Rectangle + { + width: capsList.width + height: 1 + y: UISettings.listItemHeight - 1 + color: UISettings.fgMedium + + } + MouseArea { anchors.fill: parent @@ -430,7 +487,7 @@ GridLayout GroupBox { //title: qsTr("Preset") - Layout.columnSpan: 3 + Layout.columnSpan: 2 Layout.fillWidth: true //font.family: UISettings.robotoFontName //font.pixelSize: UISettings.textSizeDefault @@ -451,7 +508,11 @@ GridLayout id: capPresetCombo Layout.fillWidth: true model: editor ? editor.capabilityPresetList : null - //currValue: channel ? channel.group : 0 + onValueChanged: + { + editor.setCapabilityPresetAtIndex(editItem.indexInList, value) + updatePresetBox(editItem.indexInList) + } } GroupBox @@ -488,7 +549,10 @@ GridLayout closeOnSelect: true showPalette: false - //onColorChanged: fixtureManager.setColorValue(r * 255, g * 255, b * 255, w * 255, a * 255, uv * 255) + onColorChanged: { + editor.setCapabilityValueAt(editItem.indexInList, 0, Qt.rgba(r, g, b, 1.0)) + updatePresetBox(editItem.indexInList) + } } } @@ -507,6 +571,7 @@ GridLayout width: UISettings.iconSizeMedium height: UISettings.iconSizeMedium label: "..." + onClicked: openDialog.open() } Rectangle @@ -546,7 +611,10 @@ GridLayout closeOnSelect: true showPalette: false - //onColorChanged: fixtureManager.setColorValue(r * 255, g * 255, b * 255, w * 255, a * 255, uv * 255) + onColorChanged: { + editor.setCapabilityValueAt(editItem.indexInList, 1, Qt.rgba(r, g, b, 1.0)) + updatePresetBox(editItem.indexInList) + } } } } @@ -578,6 +646,8 @@ GridLayout Layout.fillWidth: true from: -1000 to: 1000 + + onValueChanged: editor.setCapabilityValueAt(editItem.indexInList, 0, value) } RobotoText @@ -593,6 +663,8 @@ GridLayout Layout.fillWidth: true from: -1000 to: 1000 + + onValueChanged: editor.setCapabilityValueAt(editItem.indexInList, 1, value) } } } diff --git a/qmlui/qml/fixtureeditor/EditorView.qml b/qmlui/qml/fixtureeditor/EditorView.qml index e7960f0624..12eb525884 100644 --- a/qmlui/qml/fixtureeditor/EditorView.qml +++ b/qmlui/qml/fixtureeditor/EditorView.qml @@ -55,29 +55,32 @@ Rectangle return } + var errors = "" + if (path) - editorView.saveAs(path) + errors = editorView.saveAs(path) else - editorView.save() + errors = editorView.save() + + if (errors != "") + { + messagePopup.message = qsTr("The following errors have been detected:") + "
      " + errors + "
    " + messagePopup.open() + } } CustomPopupDialog { id: messagePopup + width: mainView.width / 2 standardButtons: Dialog.Ok title: qsTr("!! Warning !!") onAccepted: close() } - ModelSelector - { - id: chanSelector - } - SplitView { anchors.fill: parent - orientation: Qt.Horizontal // left view: definition sections Flickable @@ -118,6 +121,7 @@ Rectangle Layout.fillWidth: true text: editorView ? editorView.manufacturer : "" onTextChanged: if (editorView) editorView.manufacturer = text + KeyNavigation.tab: modelEdit } // row 1 @@ -135,28 +139,28 @@ Rectangle ListModel { id: typeModel - ListElement { mLabel: "Color Changer"; mIcon: "qrc:/fixture.svg" } - ListElement { mLabel: "Dimmer"; mIcon: "qrc:/dimmer.svg" } - ListElement { mLabel: "Effect"; mIcon: "qrc:/effect.svg" } - ListElement { mLabel: "Fan"; mIcon: "qrc:/fan.svg" } - ListElement { mLabel: "Flower"; mIcon: "qrc:/flower.svg" } - ListElement { mLabel: "Hazer"; mIcon: "qrc:/hazer.svg" } - ListElement { mLabel: "Laser"; mIcon: "qrc:/laser.svg" } - ListElement { mLabel: "LED Bar (Beams)"; mIcon: "qrc:/ledbar_beams.svg" } - ListElement { mLabel: "LED Bar (Pixels)"; mIcon: "qrc:/ledbar_pixels.svg" } - ListElement { mLabel: "Moving Head"; mIcon: "qrc:/movinghead.svg" } - ListElement { mLabel: "Other"; mIcon: "qrc:/other.svg" } - ListElement { mLabel: "Scanner"; mIcon: "qrc:/scanner.svg" } - ListElement { mLabel: "Smoke"; mIcon: "qrc:/smoke.svg" } - ListElement { mLabel: "Strobe"; mIcon: "qrc:/strobe.svg" } + ListElement { mLabel: "Color Changer"; mIcon: "qrc:/fixture.svg"; mValue: 0 } + ListElement { mLabel: "Dimmer"; mIcon: "qrc:/dimmer.svg"; mValue: 1 } + ListElement { mLabel: "Effect"; mIcon: "qrc:/effect.svg"; mValue: 2 } + ListElement { mLabel: "Fan"; mIcon: "qrc:/fan.svg"; mValue: 3 } + ListElement { mLabel: "Flower"; mIcon: "qrc:/flower.svg"; mValue: 4 } + ListElement { mLabel: "Hazer"; mIcon: "qrc:/hazer.svg"; mValue: 5 } + ListElement { mLabel: "Laser"; mIcon: "qrc:/laser.svg"; mValue: 6 } + ListElement { mLabel: "LED Bar (Beams)"; mIcon: "qrc:/ledbar_beams.svg"; mValue: 7 } + ListElement { mLabel: "LED Bar (Pixels)"; mIcon: "qrc:/ledbar_pixels.svg"; mValue: 8 } + ListElement { mLabel: "Moving Head"; mIcon: "qrc:/movinghead.svg"; mValue: 9 } + ListElement { mLabel: "Other"; mIcon: "qrc:/other.svg"; mValue: 10 } + ListElement { mLabel: "Scanner"; mIcon: "qrc:/scanner.svg"; mValue: 11 } + ListElement { mLabel: "Smoke"; mIcon: "qrc:/smoke.svg"; mValue: 12 } + ListElement { mLabel: "Strobe"; mIcon: "qrc:/strobe.svg"; mValue: 13 } } implicitHeight: UISettings.bigItemHeight delegateHeight: UISettings.listItemHeight width: UISettings.bigItemHeight model: typeModel - currentIndex: editorView ? editorView.productType : 0 - onCurrentIndexChanged: if (editorView) editorView.productType = currentIndex + currValue: editorView ? editorView.productType : 0 + onValueChanged: if (editorView) editorView.productType = value } } @@ -168,12 +172,14 @@ Rectangle Layout.fillWidth: true text: editorView ? editorView.model : "" onTextChanged: if (editorView) editorView.model = text + KeyNavigation.tab: authorEdit } // row 4 RobotoText { label: qsTr("Author") } CustomTextEdit { + id: authorEdit Layout.fillWidth: true text: editorView ? editorView.author : "" onTextChanged: if (editorView) editorView.author = text @@ -244,9 +250,15 @@ Rectangle enabled: chanSelector.itemsCount onClicked: { - for (var i = 0; i < cDragItem.itemsList.length; i++) - editorView.deleteChannel(cDragItem.itemsList[i].cRef) + // retrieve selected indices from model selector and + // channel references from the ListView items + var refsArray = [] + var selItems = chanSelector.itemsList() + for (var i = 0; i < selItems.length; i++) + refsArray.push(channelList.itemAtIndex(selItems[i]).cRef) + + editorView.deleteChannels(refsArray) cDragItem.itemsList = [] } } @@ -256,6 +268,20 @@ Rectangle Layout.fillWidth: true color: "transparent" } + + IconButton + { + id: chWizButton + imgSource: "qrc:/wizard.svg" + tooltip: qsTr("Channel wizard") + onClicked: wizardPopup.open() + + PopupChannelWizard + { + id: wizardPopup + editorView: editorRoot.editorView + } + } } } // Rectangle - toolbar @@ -273,9 +299,13 @@ Rectangle delegate: Item { + id: itemRoot width: channelList.width height: UISettings.listItemHeight + property QLCChannel cRef: model.cRef + property alias chanDelegate: delegateRoot.channelDelegate + MouseArea { id: delegateRoot @@ -283,8 +313,8 @@ Rectangle height: parent.height propagateComposedEvents: true - property QLCChannel cRef: model.cRef property bool dragActive: drag.active + property Item channelDelegate: cDelegate drag.target: cDragItem drag.threshold: height / 2 @@ -296,22 +326,17 @@ Rectangle cDragItem.x = posnInWindow.x - (cDragItem.width / 4) cDragItem.y = posnInWindow.y - (cDragItem.height / 4) cDragItem.z = 10 + } - if (model.isSelected) - return - - chanSelector.selectItem(index, channelList.model, mouse.modifiers & Qt.ControlModifier) - - if ((mouse.modifiers & Qt.ControlModifier) == 0) - cDragItem.itemsList = [] - - cDragItem.itemsList.push(cDelegate) + onClicked: + { + chanSelector.selectItem(index, channelList.model, mouse.modifiers) } onDoubleClicked: { sideEditor.active = false - sideEditor.itemName = delegateRoot.cRef.name + sideEditor.itemName = itemRoot.cRef.name sideEditor.source = "qrc:/ChannelEditor.qml" sideEditor.active = true } @@ -342,7 +367,7 @@ Rectangle color: "transparent" //property int itemType: App.ChannelDragItem - property QLCChannel cRef: delegateRoot.cRef + property QLCChannel cRef: itemRoot.cRef Rectangle { @@ -372,6 +397,22 @@ Rectangle } } + ModelSelector + { + id: chanSelector + onItemsCountChanged: + { + cDragItem.itemsList = [] + var selItems = itemsList() + + for (var i = 0; i < selItems.length; i++) + { + var item = channelList.itemAtIndex(selItems[i]) + cDragItem.itemsList.push(item.chanDelegate) + } + } + } + GenericMultiDragItem { id: cDragItem diff --git a/qmlui/qml/fixtureeditor/FixtureEditor.qml b/qmlui/qml/fixtureeditor/FixtureEditor.qml index bfa4c44b57..62303a12b0 100644 --- a/qmlui/qml/fixtureeditor/FixtureEditor.qml +++ b/qmlui/qml/fixtureeditor/FixtureEditor.qml @@ -20,7 +20,7 @@ import QtQuick 2.2 import QtQuick.Layouts 1.1 import QtQuick.Controls 2.1 -import QtQuick.Dialogs 1.2 +import QtQuick.Dialogs 1.3 import org.qlcplus.classes 1.0 import "." @@ -30,7 +30,7 @@ Rectangle width: 800 height: 600 anchors.fill: parent - color: UISettings.bgMain + color: UISettings.bgMedium FontLoader { @@ -92,9 +92,9 @@ Rectangle { id: saveBeforeExitPopup title: qsTr("Warning") - message: qsTr("Do you wish to save the following definition first?
    " + - "" + editRef.manufacturer + " - " + editRef.model + "
    " + - "Changes will be lost if you don't save them.") + message: editRef ? qsTr("Do you wish to save the following definition first?
    " + + "" + editRef.manufacturer + " - " + editRef.model + "
    " + + "Changes will be lost if you don't save them.") : "" standardButtons: Dialog.Yes | Dialog.No | Dialog.Cancel property var editRef diff --git a/qmlui/qml/fixtureeditor/ModeEditor.qml b/qmlui/qml/fixtureeditor/ModeEditor.qml index d0d279d84c..1deab6a26f 100644 --- a/qmlui/qml/fixtureeditor/ModeEditor.qml +++ b/qmlui/qml/fixtureeditor/ModeEditor.qml @@ -246,7 +246,7 @@ Rectangle if (model.isSelected) return - modeChanSelector.selectItem(index, channelList.model, mouse.modifiers & Qt.ControlModifier) + modeChanSelector.selectItem(index, channelList.model, mouse.modifiers) if ((mouse.modifiers & Qt.ControlModifier) == 0) mcDragItem.itemsList = [] @@ -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) } } @@ -364,9 +369,18 @@ Rectangle } onPositionChanged: { - var idx = channelList.indexAt(drag.x, drag.y) + var yInList = drag.y - chEditToolbar.height - UISettings.listItemHeight + var idx = channelList.indexAt(drag.x, yInList) + var item = channelList.itemAt(drag.x, yInList) + if (item === null) + return + var itemY = item.mapToItem(channelList, 0, 0).y + //console.log("Item index:" + idx) - channelList.dragInsertIndex = idx + if (drag.y < (itemY + item.height) / 2) + channelList.dragInsertIndex = idx + else + channelList.dragInsertIndex = idx + 1 } } } @@ -461,7 +475,7 @@ Rectangle anchors.fill: parent onClicked: { - modeHeadSelector.selectItem(index, headList.model, mouse.modifiers & Qt.ControlModifier) + modeHeadSelector.selectItem(index, headList.model, mouse.modifiers) } } diff --git a/qmlui/qml/fixtureeditor/PhysicalProperties.qml b/qmlui/qml/fixtureeditor/PhysicalProperties.qml index 8e3aecfc96..f7e6f5bfbd 100644 --- a/qmlui/qml/fixtureeditor/PhysicalProperties.qml +++ b/qmlui/qml/fixtureeditor/PhysicalProperties.qml @@ -51,12 +51,12 @@ GridLayout palette.base: UISettings.bgControl palette.window: UISettings.bgControl palette.text: UISettings.fgMain - palette.highlightedText: UISettings.bgMain + palette.highlightedText: UISettings.bgMedium model: ["LED", "CDM 70W", "CDM 150W", "CP29 5000W", "CP41 2000W", "CP60 1000W", "CP61 1000W", "CP62 1000W", "CP86 500W", "CP87 500W", "CP88 500W", "EFP 100W", "EFP 150W", "EFR 100W", "EFR 150W", "ELC 250W", - "HMI 150W", "HMI 250W", "HMI 400W", "HMI 575W", "HMI 700W", + "HMI 150W", "HMI 250W", "HMI 400W", "HMI 575W", "HMI 700W", "HMI 1200W", "HMI 4000W", "HSD 150W", "HSD 200W", "HSD 250W", "HSD 575W", "HTI 150W", "HTI 250W", "HTI 300W", "HTI 400W", "HTI 575W", "HTI 700W", "HTI 1200W", "HTI 2500W", "MSD 200W", @@ -64,6 +64,9 @@ GridLayout "MSR 575W", "MSR 700W", "MSR 1200W"] editable: true + editText: phy ? phy.bulbType : "" + onEditTextChanged: if (phy) phy.bulbType = editText + Rectangle { anchors.fill: parent @@ -118,9 +121,13 @@ GridLayout palette.base: UISettings.bgControl palette.window: UISettings.bgControl palette.text: UISettings.fgMain - palette.highlightedText: UISettings.bgMain + palette.highlightedText: UISettings.bgMedium model: ["Other", "PC", "Fresnel"] editable: true + + editText: phy ? phy.lensType : "" + onEditTextChanged: if (phy) phy.lensType = editText + Rectangle { anchors.fill: parent @@ -177,9 +184,13 @@ GridLayout palette.base: UISettings.bgControl palette.window: UISettings.bgControl palette.text: UISettings.fgMain - palette.highlightedText: UISettings.bgMain + palette.highlightedText: UISettings.bgMedium model: ["Fixed", "Head", "Mirror", "Barrel"] editable: true + + editText: phy ? phy.focusType : "" + onEditTextChanged: if (phy) phy.focusType = editText + Rectangle { anchors.fill: parent @@ -259,16 +270,16 @@ GridLayout columns: 2 RobotoText { label: qsTr("Weight") } - CustomSpinBox + CustomDoubleSpinBox { Layout.fillWidth: true enabled: controlRoot.enabled - from: 0 - to: 999999 + realFrom: 0 + realTo: 999999 stepSize: 1 suffix: "kg" - value: phy ? phy.weight : 0 - onValueModified: if (phy) phy.weight = value + realValue: phy ? phy.weight : 0 + onRealValueChanged: if (phy) phy.weight = realValue } RobotoText { label: qsTr("Width") } CustomSpinBox @@ -343,10 +354,13 @@ GridLayout palette.base: UISettings.bgControl palette.window: UISettings.bgControl palette.text: UISettings.fgMain - palette.highlightedText: UISettings.bgMain + palette.highlightedText: UISettings.bgMedium model: ["3-pin", "5-pin", "3-pin and 5-pin", "3.5 mm stereo jack", "Other"] editable: true - //currentText: phy ? phy.dmxConnector : "" + + editText: phy ? phy.dmxConnector : "" + onEditTextChanged: if (phy) phy.dmxConnector = editText + Rectangle { anchors.fill: parent diff --git a/qmlui/qml/fixtureeditor/qmldir b/qmlui/qml/fixtureeditor/qmldir index b86e0d30ef..34f69bcc32 100644 --- a/qmlui/qml/fixtureeditor/qmldir +++ b/qmlui/qml/fixtureeditor/qmldir @@ -7,13 +7,18 @@ 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 +CustomDoubleSpinBox 0.1 ../CustomDoubleSpinBox.qml CustomSpinBox 0.1 ../CustomSpinBox.qml CustomTextEdit 0.1 ../CustomTextEdit.qml +GenericButton 0.1 ../GenericButton.qml +GenericMultiDragItem 0.1 ../GenericMultiDragItem.qml IconButton 0.1 ../IconButton.qml IconPopupButton 0.1 ../IconPopupButton.qml IconTextEntry 0.1 ../IconTextEntry.qml MenuBarEntry 0.1 ../MenuBarEntry.qml +PopupChannelWizard 0.1 ../popup/PopupChannelWizard.qml QLCPlusFader 0.1 ../QLCPlusFader.qml RobotoText 0.1 ../RobotoText.qml SectionBox 0.1 ../SectionBox.qml diff --git a/qmlui/qml/fixturesfunctions/2DView.qml b/qmlui/qml/fixturesfunctions/2DView.qml index db2ff87a6a..491c91ae81 100644 --- a/qmlui/qml/fixturesfunctions/2DView.qml +++ b/qmlui/qml/fixturesfunctions/2DView.qml @@ -80,6 +80,7 @@ Rectangle * The stacking order of this view is very important and delicate * From bottom to top: * Flickable (z = 1): main scrollable area + * Image (z = 0): Main background image * Canvas (z = 0): The actual grid graphics view * DropArea (z = 0): allow to drop items from fixture browser * Rectangle (z = 1): multiple drag layer as big as the Canvas layer @@ -142,6 +143,17 @@ Rectangle twoDContents.requestPaint() } + Image + { + x: twoDContents.xOffset + y: twoDContents.yOffset + width: twoDView.contentWidth - (x * 2) + height: twoDView.contentHeight - (y * 2) + source: View2D.backgroundImage ? "file://" + View2D.backgroundImage : "" + sourceSize: Qt.size(width, height) + fillMode: Image.PreserveAspectFit + } + Canvas { id: twoDContents @@ -175,7 +187,7 @@ Rectangle var context = getContext("2d") context.globalAlpha = 1.0 context.strokeStyle = "#5F5F5F" - context.fillStyle = "black" + context.fillStyle = "transparent" context.lineWidth = 1 context.beginPath() @@ -183,6 +195,7 @@ Rectangle context.fillRect(0, 0, width, height) context.rect(xOffset, yOffset, width - (xOffset * 2), height - (yOffset * 2)) + // draw the view grid for (var vl = 1; vl < twoDView.gridSize.width; vl++) { var xPos = (cellSize * vl) + xOffset @@ -202,8 +215,8 @@ Rectangle MouseArea { - width: twoDSettings.visible ? twoDView.width - twoDSettings.width : twoDView.width - height: twoDView.height + width: twoDView.contentWidth + height: twoDView.contentHeight z: 2 property int initialXPos @@ -321,8 +334,8 @@ Rectangle { id: contentsDragArea objectName: "contentsDragArea" - width: twoDSettings.visible ? twoDView.width - twoDSettings.width : twoDView.width - height: twoDView.height + width: twoDView.contentWidth + height: twoDView.contentHeight color: "transparent" /* // enable for debug diff --git a/qmlui/qml/fixturesfunctions/3DView/3DView.qml b/qmlui/qml/fixturesfunctions/3DView/3DView.qml index 1a17106c20..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) } @@ -795,7 +793,7 @@ Rectangle visible: View3D.frameCountEnabled z: 4 opacity: 0.6 - color: UISettings.bgMain + color: UISettings.bgMedium width: height height: UISettings.bigItemHeight diff --git a/qmlui/qml/fixturesfunctions/3DView/3DViewUnsupported.qml b/qmlui/qml/fixturesfunctions/3DView/3DViewUnsupported.qml index 28d8a74263..db013b7db3 100644 --- a/qmlui/qml/fixturesfunctions/3DView/3DViewUnsupported.qml +++ b/qmlui/qml/fixturesfunctions/3DView/3DViewUnsupported.qml @@ -24,7 +24,7 @@ Rectangle { id: viewRoot anchors.fill: parent - color: UISettings.bgMain + color: UISettings.bgMedium function hasSettings() { diff --git a/qmlui/qml/fixturesfunctions/3DView/Fixture3DItem.qml b/qmlui/qml/fixturesfunctions/3DView/Fixture3DItem.qml index 2bf59f5fd7..0f696de6d6 100644 --- a/qmlui/qml/fixturesfunctions/3DView/Fixture3DItem.qml +++ b/qmlui/qml/fixturesfunctions/3DView/Fixture3DItem.qml @@ -45,6 +45,8 @@ Entity /* **************** Pan/Tilt properties **************** */ property real panMaxDegrees: 360 property real tiltMaxDegrees: 270 + property bool invertedPan: false + property bool invertedTilt: false property real panSpeed: 4000 // in milliseconds property real tiltSpeed: 4000 // in milliseconds @@ -107,7 +109,10 @@ Entity /* ********************** Light matrices ********************** */ property matrix4x4 lightMatrix property matrix4x4 lightViewMatrix: - Math3D.getLightViewMatrix(lightMatrix, panRotation, tiltRotation, lightPos) + Math3D.getLightViewMatrix(lightMatrix, + invertedPan ? panMaxDegrees - panRotation : panRotation, + invertedTilt ? tiltMaxDegrees - tiltRotation : tiltRotation, + lightPos) property matrix4x4 lightProjectionMatrix: Math3D.getLightProjectionMatrix(distCutoff, coneBottomRadius, coneTopRadius, headLength, cutoffAngle) property matrix4x4 lightViewProjectionMatrix: lightProjectionMatrix.times(lightViewMatrix) @@ -125,7 +130,9 @@ Entity console.log("Binding pan ----") fixtureEntity.panTransform = t fixtureEntity.panMaxDegrees = maxDegrees - t.rotationY = Qt.binding(function() { return panRotation }) + t.rotationY = Qt.binding(function() { + return invertedPan ? panMaxDegrees - panRotation : panRotation + }) } function bindTiltTransform(t, maxDegrees) @@ -134,7 +141,9 @@ Entity fixtureEntity.tiltTransform = t fixtureEntity.tiltMaxDegrees = maxDegrees tiltRotation = maxDegrees / 2 - t.rotationX = Qt.binding(function() { return tiltRotation }) + t.rotationX = Qt.binding(function() { + return invertedTilt ? tiltMaxDegrees - tiltRotation : tiltRotation + }) } function getHead(headIndex) diff --git a/qmlui/qml/fixturesfunctions/3DView/LightEntity.qml b/qmlui/qml/fixturesfunctions/3DView/LightEntity.qml index ed831afc53..14fef26e52 100644 --- a/qmlui/qml/fixturesfunctions/3DView/LightEntity.qml +++ b/qmlui/qml/fixturesfunctions/3DView/LightEntity.qml @@ -45,8 +45,6 @@ Entity property real headLength property real coneBottomRadius property real coneTopRadius - property real tiltRotation - property real panRotation: 0 property Texture2D goboTexture property real goboRotation: 0 @@ -54,16 +52,6 @@ Entity readonly property Layer outputDepthLayer: Layer { } readonly property Layer spotlightScatteringLayer: Layer { } - /* ********************** Light matrices ********************** */ - property matrix4x4 lightMatrix - property matrix4x4 lightViewMatrix: - Math3D.getLightViewMatrix(lightMatrix, 0, tiltRotation, lightPos) - property matrix4x4 lightProjectionMatrix: - Math3D.getLightProjectionMatrix(distCutoff, coneBottomRadius, coneTopRadius, headLength, cutoffAngle) - property matrix4x4 lightViewProjectionMatrix: lightProjectionMatrix.times(lightViewMatrix) - property matrix4x4 lightViewProjectionScaleAndOffsetMatrix: - Math3D.getLightViewProjectionScaleOffsetMatrix(lightViewProjectionMatrix) - property Transform headTransform: Transform { translation: Qt.vector3d(0.1 * headIndex, 0, 0) } function setupScattering(sceneEntity) diff --git a/qmlui/qml/fixturesfunctions/3DView/MultiBeams3DItem.qml b/qmlui/qml/fixturesfunctions/3DView/MultiBeams3DItem.qml index dfc6426e4e..4c10966604 100644 --- a/qmlui/qml/fixturesfunctions/3DView/MultiBeams3DItem.qml +++ b/qmlui/qml/fixturesfunctions/3DView/MultiBeams3DItem.qml @@ -35,7 +35,8 @@ Entity property int itemID: fixtureManager.invalidFixture() property bool isSelected: false - property int headsNumber: 0 + property int headsNumber: 1 + property size headsLayout: Qt.size(1, 1) property vector3d phySize: Qt.vector3d(1, 0.1, 0.1) onItemIDChanged: diff --git a/qmlui/qml/fixturesfunctions/3DView/PixelBar3DItem.qml b/qmlui/qml/fixturesfunctions/3DView/PixelBar3DItem.qml index 86f9da80a9..0b80436d65 100644 --- a/qmlui/qml/fixturesfunctions/3DView/PixelBar3DItem.qml +++ b/qmlui/qml/fixturesfunctions/3DView/PixelBar3DItem.qml @@ -36,6 +36,7 @@ Entity property int itemID: fixtureManager.invalidFixture() property bool isSelected: false property int headsNumber: 1 + property size headsLayout: Qt.size(1, 1) property vector3d phySize: Qt.vector3d(1, 0.1, 0.1) property bool useScattering: false property bool useShadows: false @@ -114,7 +115,8 @@ Entity id: headDelegate property real dimmerValue: 0 property real lightIntensity: dimmerValue * shutterValue - property real headWidth: phySize.x / fixtureEntity.headsNumber + property real headWidth: phySize.x / headsLayout.width + property real headHeight: phySize.z / headsLayout.height property color lightColor: Qt.rgba(0, 0, 0, 1) enabled: lightIntensity === 0 || lightColor === Qt.rgba(0, 0, 0, 1) ? false : true @@ -123,15 +125,23 @@ Entity { id: headMesh width: headWidth - height: phySize.z + height: headHeight meshResolution: Qt.size(2, 2) } property Transform headTransform: Transform { - translation: Qt.vector3d(-(phySize.x / 2) + (headWidth * index) + (headWidth / 2), - (phySize.y / 2) + 0.001, 0) + translation: { + var row = Math.floor(index / headsLayout.width) + var column = index % headsLayout.width + const xPos = column * headWidth; + const zPos = row * headHeight; + + //return Qt.vector3d(-(phySize.x / 2) + (headWidth * index) + (headWidth / 2), + // (phySize.y / 2) + 0.001, 0) + return Qt.vector3d(-(phySize.x / 2) + xPos, (phySize.y / 2) + 0.001, -(phySize.z / 2) + zPos) + } } property Material headMaterial: diff --git a/qmlui/qml/fixturesfunctions/3DView/SettingsView3D.qml b/qmlui/qml/fixturesfunctions/3DView/SettingsView3D.qml index 15e0febca2..ecf7340e15 100644 --- a/qmlui/qml/fixturesfunctions/3DView/SettingsView3D.qml +++ b/qmlui/qml/fixturesfunctions/3DView/SettingsView3D.qml @@ -19,7 +19,7 @@ import QtQuick 2.0 import QtQuick.Layouts 1.1 -import QtQuick.Dialogs 1.2 +import QtQuick.Dialogs 1.3 import QtQuick.Controls 2.1 import org.qlcplus.classes 1.0 @@ -718,7 +718,7 @@ Rectangle anchors.fill: parent onClicked: { - giSelector.selectItem(index, itemsList.model, mouse.modifiers & Qt.ControlModifier) + giSelector.selectItem(index, itemsList.model, mouse.modifiers) View3D.setItemSelection(itemID, isSelected, mouse.modifiers) } } diff --git a/qmlui/qml/fixturesfunctions/3DView/SpotlightConeEntity.qml b/qmlui/qml/fixturesfunctions/3DView/SpotlightConeEntity.qml index eccd7b5cb0..735859574f 100644 --- a/qmlui/qml/fixturesfunctions/3DView/SpotlightConeEntity.qml +++ b/qmlui/qml/fixturesfunctions/3DView/SpotlightConeEntity.qml @@ -50,15 +50,20 @@ Entity Parameter { name: "raymarchSteps"; value: mtl.fxItem ? mtl.fxItem.raymarchSteps : 0 }, Parameter { name: "customModelMatrix"; value: { - var m = Qt.matrix4x4(); + var m = Qt.matrix4x4() + if (mtl.fxItem === null) - return m; - m.translate(mtl.fxItem.lightPos.times(+1.0)); + return m + + var panRot = mtl.fxItem.invertedPan ? mtl.fxItem.panMaxDegrees - mtl.fxItem.panRotation : mtl.fxItem.panRotation + var tiltRot = mtl.fxItem.invertedTilt ? mtl.fxItem.tiltMaxDegrees - mtl.fxItem.tiltRotation : mtl.fxItem.tiltRotation + + m.translate(mtl.fxItem.lightPos.times(+1.0)) m = m.times(mtl.fxItem.lightMatrix) - m.rotate(mtl.fxItem.panRotation, Qt.vector3d(0, 1, 0)); - m.rotate(mtl.fxItem.tiltRotation, Qt.vector3d(1, 0, 0)); - m.translate(Qt.vector3d(0, -0.5 * mtl.fxItem.distCutoff - 0.5 * mtl.fxItem.headLength, 0)); - return m; + m.rotate(panRot, Qt.vector3d(0, 1, 0)) + m.rotate(tiltRot, Qt.vector3d(1, 0, 0)) + m.translate(Qt.vector3d(0, -0.5 * mtl.fxItem.distCutoff - 0.5 * mtl.fxItem.headLength, 0)) + return m }}, Parameter { name: "coneTopRadius"; value: mtl.fxItem ? mtl.fxItem.coneTopRadius : 0 }, Parameter { name: "coneBottomRadius"; value: mtl.fxItem ? mtl.fxItem.coneBottomRadius : 0 }, diff --git a/qmlui/qml/fixturesfunctions/3DView/StageBox.qml b/qmlui/qml/fixturesfunctions/3DView/StageBox.qml index d219ce0c86..3bf228f88b 100644 --- a/qmlui/qml/fixturesfunctions/3DView/StageBox.qml +++ b/qmlui/qml/fixturesfunctions/3DView/StageBox.qml @@ -75,7 +75,7 @@ Entity ObjectPicker { - id: stagePicker + id: groundPicker onClicked: contextManager.setPositionPickPoint(pick.worldIntersection) } @@ -83,7 +83,7 @@ Entity groundMesh, stage.material, transform, - stagePicker, + groundPicker, stage.sceneLayer ] } @@ -95,10 +95,17 @@ Entity (size.y / 2) - (groundMesh.yExtent / 2), 0) } + ObjectPicker + { + id: leftPicker + onClicked: contextManager.setPositionPickPoint(pick.worldIntersection) + } + components: [ sideMesh, stage.material, transform, + leftPicker, stage.sceneLayer ] } @@ -109,11 +116,17 @@ Entity property Transform transform: Transform { translation: Qt.vector3d((size.x / 2) + (groundMesh.yExtent / 2), (size.y / 2) - (groundMesh.yExtent / 2), 0) } + ObjectPicker + { + id: rightPicker + onClicked: contextManager.setPositionPickPoint(pick.worldIntersection) + } components: [ sideMesh, stage.material, transform, + rightPicker, stage.sceneLayer ] } @@ -124,11 +137,17 @@ Entity property Transform transform: Transform { translation: Qt.vector3d(0, (size.y / 2) - (groundMesh.yExtent / 2), (-size.z / 2) - (groundMesh.yExtent / 2)) } + ObjectPicker + { + id: backPicker + onClicked: contextManager.setPositionPickPoint(pick.worldIntersection) + } components: [ backMesh, stage.material, transform, + backPicker, stage.sceneLayer ] } diff --git a/qmlui/qml/fixturesfunctions/AudioEditor.qml b/qmlui/qml/fixturesfunctions/AudioEditor.qml index 96724fcfe2..d08af47e40 100644 --- a/qmlui/qml/fixturesfunctions/AudioEditor.qml +++ b/qmlui/qml/fixturesfunctions/AudioEditor.qml @@ -237,7 +237,6 @@ Rectangle } CustomComboBox { - id: audioCardsCombo height: UISettings.listItemHeight Layout.fillWidth: true model: ioManager.audioOutputSources @@ -247,6 +246,23 @@ Rectangle // row 8 RobotoText + { + label: qsTr("Volume") + height: UISettings.listItemHeight + } + CustomSpinBox + { + height: UISettings.listItemHeight + Layout.fillWidth: true + from: 0 + to: 100 + value: audioEditor.volume + suffix: "%" + onValueChanged: audioEditor.volume = value + } + + // row 9 + RobotoText { id: fiLabel label: qsTr("Fade in") @@ -276,7 +292,7 @@ Rectangle } } - // row 9 + // row 10 RobotoText { id: foLabel diff --git a/qmlui/qml/fixturesfunctions/BeamTool.qml b/qmlui/qml/fixturesfunctions/BeamTool.qml index e93bad9fa2..086556ed7f 100644 --- a/qmlui/qml/fixturesfunctions/BeamTool.qml +++ b/qmlui/qml/fixturesfunctions/BeamTool.qml @@ -20,6 +20,7 @@ import QtQuick 2.0 import QtQuick.Layouts 1.0 +import org.qlcplus.classes 1.0 import "." Rectangle @@ -35,21 +36,61 @@ Rectangle property real maxDegrees: 0 property bool invertedZoom: false property real projectedDiameter: 0 + property bool isUpdating: false + + property alias currentDegrees: beamSpinBox.realValue + property int previousDegrees: 0 + property bool relativeValue: false + + signal close() onMinDegreesChanged: gCanvas.requestPaint() onMaxDegreesChanged: gCanvas.requestPaint() + onVisibleChanged: + { + if (visible) + { + previousDegrees = 0 + var val = contextManager.getCurrentValue(QLCChannel.Beam, true) + if (val === -1) + { + relativeValue = true + currentDegrees = 0 + } + else + { + relativeValue = false + currentDegrees = val + } + } + } + + onCurrentDegreesChanged: + { + if (isUpdating) + return + + var val = relativeValue ? currentDegrees - previousDegrees : currentDegrees + previousDegrees = currentDegrees + + beamSpinBox.value = currentDegrees * Math.pow(10, beamSpinBox.decimals) + contextManager.setBeamDegrees(val, relativeValue) + calculateProjection() + gCanvas.requestPaint() + } + function setZoomRange(min, max, inverted) { if (max === maxDegrees && min === minDegrees) return + isUpdating = true maxDegrees = max minDegrees = min invertedZoom = inverted - beamSpinBox.realValue = inverted ? maxDegrees : minDegrees - beamSpinBox.value = beamSpinBox.realValue * Math.pow(10, beamSpinBox.decimals) - gCanvas.requestPaint() + currentDegrees = inverted ? maxDegrees : minDegrees + isUpdating = false } function calculateProjection() @@ -95,6 +136,16 @@ Rectangle anchors.fill: parent drag.target: toolRoot } + GenericButton + { + width: height + height: parent.height + anchors.right: parent.right + border.color: UISettings.bgMedium + useFontawesome: true + label: FontAwesome.fa_times + onClicked: toolRoot.close() + } } Canvas @@ -160,16 +211,8 @@ Rectangle CustomDoubleSpinBox { id: beamSpinBox - realFrom: minDegrees + realFrom: relativeValue ? -maxDegrees : minDegrees realTo: maxDegrees - //realValue: invertedZoom ? maxDegrees : minDegrees - - onRealValueChanged: - { - contextManager.setBeamDegrees(realValue) - calculateProjection() - gCanvas.requestPaint() - } } RobotoText { label: qsTr("Distance") } diff --git a/qmlui/qml/fixturesfunctions/ChaserEditor.qml b/qmlui/qml/fixturesfunctions/ChaserEditor.qml index 453a13cede..11e0fa3312 100644 --- a/qmlui/qml/fixturesfunctions/ChaserEditor.qml +++ b/qmlui/qml/fixturesfunctions/ChaserEditor.qml @@ -19,7 +19,7 @@ import QtQuick 2.0 import QtQuick.Layouts 1.1 -import QtQuick.Controls 1.2 +import QtQuick.Controls 2.13 import org.qlcplus.classes 1.0 import "TimeUtils.js" as TimeUtils @@ -36,6 +36,19 @@ Rectangle signal requestView(int ID, string qmlSrc) + function deleteSelectedItems() + { + deleteItemsPopup.open() + } + + CustomPopupDialog + { + id: deleteItemsPopup + title: qsTr("Delete steps") + message: qsTr("Are you sure you want to remove the selected steps?") + onAccepted: functionManager.deleteEditorItems(chWidget.selector.itemsList()) + } + SplitView { anchors.fill: parent @@ -43,8 +56,9 @@ Rectangle Loader { id: funcMgrLoader - visible: width - width: 0 + width: UISettings.sidePanelWidth + SplitView.preferredWidth: UISettings.sidePanelWidth + visible: false height: ceContainer.height source: "" @@ -59,7 +73,7 @@ Rectangle Column { - Layout.fillWidth: true + SplitView.fillWidth: true EditorTopBar { @@ -70,11 +84,11 @@ Rectangle onBackClicked: { - if (funcMgrLoader.width) + if (funcMgrLoader.visible) { funcMgrLoader.source = "" - funcMgrLoader.width = 0 - rightSidePanel.width = rightSidePanel.width / 2 + funcMgrLoader.visible = false + rightSidePanel.width -= funcMgrLoader.width } var prevID = chaserEditor.previousID @@ -82,6 +96,26 @@ Rectangle requestView(prevID, functionManager.getEditorResource(prevID)) } + IconButton + { + width: height + height: UISettings.iconSizeMedium - 2 + imgSource: "qrc:/back.svg" + tooltip: qsTr("Preview the previous step") + visible: chaserEditor.previewEnabled + onClicked: chaserEditor.gotoPreviousStep() + } + + IconButton + { + width: height + height: UISettings.iconSizeMedium - 2 + imgSource: "qrc:/forward.svg" + tooltip: qsTr("Preview the next step") + visible: chaserEditor.previewEnabled + onClicked: chaserEditor.gotoNextStep() + } + IconButton { id: addFunc @@ -89,25 +123,37 @@ Rectangle height: UISettings.iconSizeMedium - 2 imgSource: "qrc:/add.svg" checkable: true + enabled: !chaserEditor.previewEnabled tooltip: qsTr("Add a new step") onCheckedChanged: { if (checked) { - rightSidePanel.width += mainView.width / 3 - funcMgrLoader.width = mainView.width / 3 + if (!funcMgrLoader.visible) + rightSidePanel.width += UISettings.sidePanelWidth + funcMgrLoader.visible = true funcMgrLoader.source = "qrc:/FunctionManager.qml" } else { - rightSidePanel.width = rightSidePanel.width - funcMgrLoader.width + rightSidePanel.width -= funcMgrLoader.width funcMgrLoader.source = "" - funcMgrLoader.width = 0 + funcMgrLoader.visible = false } } } + IconButton + { + width: height + height: UISettings.iconSizeMedium - 2 + imgSource: "qrc:/edit-copy.svg" + tooltip: qsTr("Duplicate the selected step(s)") + enabled: !chaserEditor.previewEnabled && chWidget.selector.itemsCount + onClicked: chaserEditor.duplicateSteps(chWidget.selector.itemsList()) + } + IconButton { id: removeFunc @@ -115,15 +161,8 @@ Rectangle height: UISettings.iconSizeMedium - 2 imgSource: "qrc:/remove.svg" tooltip: qsTr("Remove the selected steps") - onClicked: deleteItemsPopup.open() - - CustomPopupDialog - { - id: deleteItemsPopup - title: qsTr("Delete steps") - message: qsTr("Are you sure you want to remove the selected steps?") - onAccepted: functionManager.deleteEditorItems(chWidget.selector.itemsList()) - } + enabled: !chaserEditor.previewEnabled && chWidget.selector.itemsCount + onClicked: deleteSelectedItems() } IconButton @@ -144,6 +183,7 @@ Rectangle ChaserWidget { id: chWidget + objectName: "chaserEditorWidget" isSequence: ceContainer.isSequence width: ceContainer.width height: ceContainer.height - (topbar.visible ? topbar.height : 0) - chModes.height diff --git a/qmlui/qml/fixturesfunctions/CollectionEditor.qml b/qmlui/qml/fixturesfunctions/CollectionEditor.qml index d6933c87db..c5563fdf0a 100644 --- a/qmlui/qml/fixturesfunctions/CollectionEditor.qml +++ b/qmlui/qml/fixturesfunctions/CollectionEditor.qml @@ -19,7 +19,7 @@ import QtQuick 2.0 import QtQuick.Layouts 1.1 -import QtQuick.Controls 1.2 +import QtQuick.Controls 2.13 import org.qlcplus.classes 1.0 import "." @@ -46,8 +46,9 @@ Rectangle Loader { id: funcMgrLoader - visible: width - width: 0 + width: UISettings.sidePanelWidth + SplitView.preferredWidth: UISettings.sidePanelWidth + visible: false height: ceContainer.height source: "" @@ -68,20 +69,20 @@ Rectangle Column { - Layout.fillWidth: true + SplitView.fillWidth: true EditorTopBar { - text: collectionEditor.functionName + text: collectionEditor ? collectionEditor.functionName : "" onTextChanged: collectionEditor.functionName = text onBackClicked: { - if (funcMgrLoader.width) + if (funcMgrLoader.visible) { funcMgrLoader.source = "" - funcMgrLoader.width = 0 - rightSidePanel.width = rightSidePanel.width / 2 + funcMgrLoader.visible = false + rightSidePanel.width -= funcMgrLoader.width } var prevID = collectionEditor.previousID @@ -101,15 +102,16 @@ Rectangle { if (checked) { - rightSidePanel.width += UISettings.sidePanelWidth - funcMgrLoader.width = UISettings.sidePanelWidth + if (!funcMgrLoader.visible) + rightSidePanel.width += UISettings.sidePanelWidth + funcMgrLoader.visible = true funcMgrLoader.source = "qrc:/FunctionManager.qml" } else { - rightSidePanel.width = rightSidePanel.width - funcMgrLoader.width + rightSidePanel.width -= funcMgrLoader.width funcMgrLoader.source = "" - funcMgrLoader.width = 0 + funcMgrLoader.visible = false } } } @@ -143,7 +145,7 @@ Rectangle property int dragInsertIndex: -1 - model: collectionEditor.functionsList + model: collectionEditor ? collectionEditor.functionsList : null delegate: Item { @@ -159,7 +161,7 @@ Rectangle drag.target: cfDelegate drag.threshold: height / 2 - onPressed: ceSelector.selectItem(index, cFunctionList.model, mouse.modifiers & Qt.ControlModifier) + onPressed: ceSelector.selectItem(index, cFunctionList.model, mouse.modifiers) onDoubleClicked: { functionManager.setEditorFunction(model.funcID, false, false) diff --git a/qmlui/qml/fixturesfunctions/DMXView.qml b/qmlui/qml/fixturesfunctions/DMXView.qml index 55150ce557..f471b579d5 100644 --- a/qmlui/qml/fixturesfunctions/DMXView.qml +++ b/qmlui/qml/fixturesfunctions/DMXView.qml @@ -26,7 +26,7 @@ Rectangle { id: dmxViewRoot anchors.fill: parent - color: UISettings.bgMain + color: UISettings.bgMedium property alias contextItem: flowLayout property int viewMargin: 20 @@ -55,11 +55,10 @@ Rectangle anchors.fill: parent anchors.leftMargin: viewMargin anchors.topMargin: viewMargin - //anchors.bottomMargin: viewMargin contentHeight: flowLayout.height contentWidth: flowLayout.width - interactive: false + //interactive: false boundsBehavior: Flickable.StopAtBounds @@ -77,8 +76,14 @@ Rectangle channelToolLoader.loadChannelTool(item, fixtureID, chIndex, value) } + function itemWidthChanged(width) + { + if (fixtureDMXView.contentWidth < width) + fixtureDMXView.contentWidth = width + (viewMargin * 2) + } + 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/EFXEditor.qml b/qmlui/qml/fixturesfunctions/EFXEditor.qml index dcea7e79c7..47a5561bc6 100644 --- a/qmlui/qml/fixturesfunctions/EFXEditor.qml +++ b/qmlui/qml/fixturesfunctions/EFXEditor.qml @@ -19,8 +19,7 @@ import QtQuick 2.0 import QtQuick.Layouts 1.2 -import QtQuick.Controls 1.2 as QC1 -import QtQuick.Controls 2.1 +import QtQuick.Controls 2.13 import org.qlcplus.classes 1.0 @@ -63,15 +62,16 @@ Rectangle } } - QC1.SplitView + SplitView { anchors.fill: parent Loader { id: fxTreeLoader - visible: width - width: 0 + width: UISettings.sidePanelWidth + SplitView.preferredWidth: UISettings.sidePanelWidth + visible: false height: efxeContainer.height source: "" @@ -88,13 +88,13 @@ Rectangle width: 2 height: parent.height x: parent.width - 2 - color: UISettings.bgLighter + color: UISettings.bgLight } } Rectangle { - Layout.fillWidth: true + SplitView.fillWidth: true color: "transparent" EditorTopBar @@ -105,6 +105,13 @@ Rectangle onBackClicked: { + if (fxTreeLoader.visible) + { + fxTreeLoader.source = "" + fxTreeLoader.visible = false + rightSidePanel.width -= fxTreeLoader.width + } + var prevID = efxEditor.previousID functionManager.setEditorFunction(prevID, false, true) requestView(prevID, functionManager.getEditorResource(prevID)) @@ -161,7 +168,7 @@ Rectangle GridLayout { width: parent.width - columns: 4 + columns: 5 columnSpacing: 0 rowSpacing: 0 @@ -169,7 +176,7 @@ Rectangle Rectangle { Layout.fillWidth: true - Layout.columnSpan: 4 + Layout.columnSpan: 5 color: UISettings.bgMedium height: UISettings.listItemHeight @@ -188,16 +195,17 @@ Rectangle { if (checked) { - rightSidePanel.width += UISettings.sidePanelWidth - fxTreeLoader.width = UISettings.sidePanelWidth + if (!fxTreeLoader.visible) + rightSidePanel.width += UISettings.sidePanelWidth + fxTreeLoader.visible = true fxTreeLoader.modelProvider = efxEditor fxTreeLoader.source = "qrc:/FixtureGroupManager.qml" } else { - rightSidePanel.width = rightSidePanel.width - fxTreeLoader.width + rightSidePanel.width -= fxTreeLoader.width fxTreeLoader.source = "" - fxTreeLoader.width = 0 + fxTreeLoader.visible = false } } } @@ -210,6 +218,7 @@ Rectangle height: parent.height imgSource: "qrc:/remove.svg" tooltip: qsTr("Remove the selected fixture head(s)") + onClicked: efxEditor.removeHeads(eeSelector.itemsList()) } } @@ -248,6 +257,22 @@ Rectangle } } + RobotoText + { + id: modeCol + height: UISettings.listItemHeight + width: UISettings.bigItemHeight + label: qsTr("Mode") + + Rectangle + { + height: UISettings.listItemHeight + width: 1 + anchors.right: parent.right + color: UISettings.fgMedium + } + } + RobotoText { id: reverseCol @@ -275,7 +300,7 @@ Rectangle { id: fixtureListView Layout.fillWidth: true - Layout.columnSpan: 4 + Layout.columnSpan: 5 model: efxEditor.fixtureList implicitHeight: count * UISettings.listItemHeight delegate: @@ -285,6 +310,8 @@ Rectangle height: UISettings.listItemHeight color: "transparent" + property int headMode: model.mode + // Highlight rectangle Rectangle { @@ -301,8 +328,7 @@ Rectangle anchors.fill: parent onClicked: { - eeSelector.selectItem(index, fixtureListView.model, - mouse.modifiers & Qt.ControlModifier) + eeSelector.selectItem(index, fixtureListView.model, mouse.modifiers) } } @@ -311,6 +337,7 @@ Rectangle //width: fixtureListView.width height: UISettings.listItemHeight + /* Head number */ RobotoText { width: numCol.width @@ -325,6 +352,7 @@ Rectangle color: UISettings.fgMedium } } + /* Head name */ RobotoText { width: fxNameCol.width @@ -339,6 +367,32 @@ Rectangle color: UISettings.fgMedium } } + /* Head mode */ + CustomComboBox + { + height: editorColumn.itemsHeight + width: modeCol.width + + ListModel + { + id: modeModel + ListElement { mLabel: qsTr("Position"); } + ListElement { mLabel: qsTr("Dimmer"); } + ListElement { mLabel: qsTr("RGB"); } + } + model: modeModel + currentIndex: headMode + onCurrentIndexChanged: efxEditor.setFixtureMode(fxID, head, currentIndex) + + Rectangle + { + height: UISettings.listItemHeight + width: 1 + anchors.right: parent.right + color: UISettings.fgMedium + } + } + /* Head reverse flag */ Rectangle { color: "transparent" @@ -361,6 +415,7 @@ Rectangle color: UISettings.fgMedium } } + /* Head offset */ CustomSpinBox { width: offsetCol.width @@ -719,7 +774,7 @@ Rectangle { id: hLabel height: UISettings.listItemHeight - label: qsTr("Hold") + label: qsTr("Loop") } Rectangle diff --git a/qmlui/qml/fixturesfunctions/EditorTopBar.qml b/qmlui/qml/fixturesfunctions/EditorTopBar.qml index dd206a2186..6c31141fa3 100644 --- a/qmlui/qml/fixturesfunctions/EditorTopBar.qml +++ b/qmlui/qml/fixturesfunctions/EditorTopBar.qml @@ -19,7 +19,6 @@ import QtQuick 2.0 import QtQuick.Layouts 1.1 -import QtQuick.Controls 1.2 import org.qlcplus.classes 1.0 import "." diff --git a/qmlui/qml/fixturesfunctions/FixtureBrowser.qml b/qmlui/qml/fixturesfunctions/FixtureBrowser.qml index 3773365674..f644537b08 100644 --- a/qmlui/qml/fixturesfunctions/FixtureBrowser.qml +++ b/qmlui/qml/fixturesfunctions/FixtureBrowser.qml @@ -26,7 +26,6 @@ import "." Rectangle { - id: fxBrowserBox anchors.fill: parent color: "transparent" @@ -44,15 +43,15 @@ Rectangle { Layout.fillWidth: true Layout.fillHeight: true - color: UISettings.bgMain + color: UISettings.bgMedium radius: 5 border.width: 2 - border.color: "#111" + border.color: UISettings.borderColorDark Text { id: searchIcon - x: 3 + x: 6 width: height height: parent.height - 6 anchors.verticalCenter: parent.verticalCenter @@ -64,10 +63,10 @@ Rectangle TextInput { - x: searchIcon.width + 13 + x: searchIcon.width + 14 y: 3 height: parent.height - 6 - width: parent.width - searchIcon.width - 10 + width: parent.width - x color: UISettings.fgMain text: fixtureBrowser.searchFilter font.family: "Roboto Condensed" @@ -311,7 +310,7 @@ Rectangle Connections { target: item - onMouseEvent: + function onMouseEvent(type, iID, iType, qItem, mouseMods) { if (type === App.Clicked) { diff --git a/qmlui/qml/fixturesfunctions/FixtureChannelDelegate.qml b/qmlui/qml/fixturesfunctions/FixtureChannelDelegate.qml index f5773d625e..1f11d4f784 100644 --- a/qmlui/qml/fixturesfunctions/FixtureChannelDelegate.qml +++ b/qmlui/qml/fixturesfunctions/FixtureChannelDelegate.qml @@ -19,6 +19,7 @@ import QtQuick 2.0 import QtQuick.Layouts 1.0 +import QtQuick.Controls 2.14 import org.qlcplus.classes 1.0 import "." @@ -42,7 +43,7 @@ Rectangle property int chIndex property int itemFlags property bool canFade: true - property int precedence + property alias precedence: precedenceCombo.currValue property string modifier property Item dragItem @@ -142,6 +143,7 @@ Rectangle // precedence combo CustomComboBox { + id: precedenceCombo visible: showFlags implicitWidth: UISettings.chPropsPrecedenceWidth height: parent.height - 2 @@ -155,7 +157,6 @@ Rectangle ListElement { mLabel: qsTr("Forced LTP"); mValue: FixtureManager.ForcedLTP } } model: precModel - currentIndex: chDelegate.precedence onValueChanged: fixtureManager.setItemRoleData(itemID, chIndex, "precedence", value) } @@ -168,12 +169,44 @@ Rectangle } // modifier - Rectangle + Button { + id: cmBtn visible: showFlags - width: UISettings.chPropsModifierWidth - height: parent.height - color: "transparent" + implicitWidth: UISettings.chPropsModifierWidth + implicitHeight: parent.height + padding: 0 + text: modifier === "" ? "..." : modifier + hoverEnabled: true + + ToolTip.delay: 1000 + ToolTip.timeout: 5000 + ToolTip.visible: hovered + ToolTip.text: text + + onClicked: fixtureManager.showModifierEditor(itemID, chIndex) + + contentItem: + Text + { + text: cmBtn.text + color: UISettings.fgMain + font.family: UISettings.robotoFontName + font.pixelSize: UISettings.textSizeDefault + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + + background: + Rectangle + { + implicitWidth: cmBtn.implicitWidth + height: parent.height + border.width: 2 + border.color: UISettings.bgStrong + color: cmBtn.hovered ? (cmBtn.down ? UISettings.highlightPressed : UISettings.highlight) : UISettings.bgControl + } } } diff --git a/qmlui/qml/fixturesfunctions/FixtureDMXItem.qml b/qmlui/qml/fixturesfunctions/FixtureDMXItem.qml index 4d56c03a91..6cf0339c43 100644 --- a/qmlui/qml/fixturesfunctions/FixtureDMXItem.qml +++ b/qmlui/qml/fixturesfunctions/FixtureDMXItem.qml @@ -57,6 +57,11 @@ Rectangle border.width: 1 border.color: "#222" + onWidthChanged: + { + dmxItemRoot.parent.itemWidthChanged(width) + } + Column { id: fxColumn @@ -190,8 +195,11 @@ Rectangle Connections { target: consoleLoader.item - onClicked: clickTimer.start() - onDoubleClicked: + function onClicked() + { + clickTimer.start() + } + function onDoubleClicked() { clickTimer.stop() consoleLoader.source = "" @@ -199,22 +207,22 @@ Rectangle dmxItemRoot.height = fxColumn.height fxColumn.visible = true } - onSizeChanged: + function onSizeChanged(w, h) { - if (w != 0 && h != 0) + if (w !== 0 && h !== 0) { dmxItemRoot.width = w dmxItemRoot.height = h //console.log("2- Item width: " + w + ", height: " + h) } } - onValueChanged: + function onValueChanged(fixtureID, chIndex, value) { //console.log("Channel " + chIndex + " value changed " + value) channelsRpt.itemAt(chIndex).dmxValue = value } - onRequestTool: + function onRequestTool(item, fixtureID, chIndex, value) { //dmxItemRoot.requestTool(item, fixtureID, chIndex, value) dmxItemRoot.parent.loadTool(item, fixtureID, chIndex, value) diff --git a/qmlui/qml/fixturesfunctions/FixtureGroupEditor.qml b/qmlui/qml/fixturesfunctions/FixtureGroupEditor.qml index 184193bb69..a2dd77cf8d 100644 --- a/qmlui/qml/fixturesfunctions/FixtureGroupEditor.qml +++ b/qmlui/qml/fixturesfunctions/FixtureGroupEditor.qml @@ -222,7 +222,7 @@ Rectangle gridSize: fixtureGroupEditor.groupSize fillDirection: Qt.Horizontal | Qt.Vertical - mininumCellSize: UISettings.iconSizeDefault * 1.5 + minimumCellSize: UISettings.iconSizeDefault * 1.5 labelsFontSize: cellSize / 6 gridData: fixtureGroupEditor.groupMap gridLabels: fixtureGroupEditor.groupLabels @@ -237,7 +237,7 @@ Rectangle fixtureGroupEditor.deleteSelection() var empty = [] setSelectionData(empty) - event.accepted = true; + event.accepted = true } } @@ -270,7 +270,7 @@ Rectangle { gridFlickable.interactive = true - if (currentItemID === -1) + if (currentItemID === -1 || offset === 0) return if (externalDrag == false) diff --git a/qmlui/qml/fixturesfunctions/FixtureGroupManager.qml b/qmlui/qml/fixturesfunctions/FixtureGroupManager.qml index ac8a60f6ea..dc827b164d 100644 --- a/qmlui/qml/fixturesfunctions/FixtureGroupManager.qml +++ b/qmlui/qml/fixturesfunctions/FixtureGroupManager.qml @@ -27,6 +27,7 @@ import "." Rectangle { id: fgmContainer + objectName: "fixtureGroupManager" anchors.fill: parent color: "transparent" @@ -81,6 +82,31 @@ Rectangle } } + function showChannelModifierEditor(itemID, channelIndex, modifierName) + { + chModifierEditor.itemID = itemID + chModifierEditor.chIndex = channelIndex + chModifierEditor.modName = modifierName + chModifierEditor.open() + } + + CustomPopupDialog + { + id: fmGenericPopup + visible: false + title: qsTr("Error") + message: "" + onAccepted: {} + } + + PopupChannelModifiers + { + id: chModifierEditor + visible: false + + onAccepted: fixtureManager.setChannelModifier(itemID, chIndex) + } + ColumnLayout { anchors.fill: parent @@ -155,7 +181,10 @@ Rectangle } if (fxDeleteList.length) + { + contextManager.resetFixtureSelection() fixtureManager.deleteFixtures(fxDeleteList) + } if (fxGroupDeleteList.length) fixtureManager.deleteFixtureGroups(fxGroupDeleteList) @@ -168,7 +197,7 @@ Rectangle z: 2 width: height height: topBar.height - 2 - bgColor: UISettings.bgMain + bgColor: UISettings.bgMedium faColor: checked ? "white" : "gray" faSource: FontAwesome.fa_search checkable: true @@ -313,7 +342,7 @@ Rectangle implicitHeight: UISettings.iconSizeMedium implicitWidth: fgmContainer.width - (gEditScrollBar.visible ? gEditScrollBar.width : 0) z: 5 - color: UISettings.bgMain + color: UISettings.bgMedium RowLayout { @@ -321,6 +350,8 @@ Rectangle RobotoText { label: qsTr("Name"); Layout.fillWidth: true; height: parent.height } Rectangle { width: 1; height: parent.height } + RobotoText { label: qsTr("Mode"); width: UISettings.chPropsModesWidth; height: parent.height } + Rectangle { width: 1; height: parent.height } RobotoText { label: qsTr("Flags"); width: UISettings.chPropsFlagsWidth; height: parent.height } Rectangle { width: 1; height: parent.height } RobotoText { label: qsTr("Can fade"); width: UISettings.chPropsCanFadeWidth; height: parent.height } @@ -338,10 +369,10 @@ Rectangle width: fgmContainer.width implicitHeight: UISettings.iconSizeMedium z: 5 - color: UISettings.bgMain + color: UISettings.bgMedium radius: 5 border.width: 2 - border.color: "#111" + border.color: UISettings.borderColorDark TextInput { @@ -397,7 +428,7 @@ Rectangle if (type) { item.itemType = type - if (type == App.UniverseDragItem) + if (type === App.UniverseDragItem) isExpanded = true } item.isExpanded = isExpanded @@ -411,7 +442,7 @@ Rectangle { target: item - onMouseEvent: + function onMouseEvent(type, iID, iType, qItem, mouseMods) { switch (type) { @@ -436,11 +467,10 @@ Rectangle else gfhcDragItem.itemIcon = "" } - gfhcDragItem.multipleItems = gfhcDragItem.itemsList.length > 1 ? true : false } break; case App.Clicked: - if (qItem == item) + if (qItem === item) { model.isSelected = (mouseMods & Qt.ControlModifier) ? 2 : 1 if (model.hasChildren) @@ -479,7 +509,7 @@ Rectangle fgmContainer.doubleClicked(iID, qItem.itemType) break; case App.DragStarted: - if (qItem == item && !model.isSelected) + if (qItem === item && !model.isSelected) { model.isSelected = 1 // invalidate the modifiers to force a single selection diff --git a/qmlui/qml/fixturesfunctions/FixtureNodeDelegate.qml b/qmlui/qml/fixturesfunctions/FixtureNodeDelegate.qml index d2b134ba92..739e084412 100644 --- a/qmlui/qml/fixturesfunctions/FixtureNodeDelegate.qml +++ b/qmlui/qml/fixturesfunctions/FixtureNodeDelegate.qml @@ -127,62 +127,15 @@ Column text: FontAwesome.fa_link } - TextInput + CustomTextInput { - property string originalText - id: nodeLabel Layout.fillWidth: true - z: 0 - //width: nodeBgRect.width - x - 1 - height: UISettings.listItemHeight - readOnly: true text: textLabel - verticalAlignment: TextInput.AlignVCenter - color: UISettings.fgMain - font.family: UISettings.robotoFontName - font.pixelSize: UISettings.textSizeDefault - echoMode: TextInput.Normal - selectByMouse: true - selectionColor: "#4DB8FF" - selectedTextColor: "#111" - - function disableEditing() - { - z = 0 - select(0, 0) - readOnly = true - cursorVisible = false - } + originalText: text - Keys.onPressed: + onTextConfirmed: { - switch(event.key) - { - case Qt.Key_F2: - originalText = textLabel - z = 5 - readOnly = false - cursorPosition = text.length - cursorVisible = true - break; - case Qt.Key_Escape: - disableEditing() - nodeLabel.text = originalText - break; - default: - event.accepted = false - return - } - - event.accepted = true - } - - onEditingFinished: - { - if (readOnly) - return - disableEditing() nodeContainer.pathChanged(nodePath, text) fixtureManager.renameFixture(itemID, text) } @@ -205,6 +158,49 @@ Column height: parent.height } + // fixture modes + Rectangle + { + id: fxModes + visible: showFlags + width: UISettings.chPropsModesWidth + height: parent.height + color: "transparent" + z: 1 + + CustomComboBox + { + visible: showFlags + implicitWidth: parent.width + height: UISettings.listItemHeight + textRole: "" + model: showFlags ? fixtureManager.fixtureModes(itemID) : null + currentIndex: showFlags ? fixtureManager.fixtureModeIndex(itemID) : -1 + + onActivated: + { + if (!visible) + return + + if (fixtureManager.setFixtureModeIndex(itemID, index) === false) + { + // show error popup on failure + fmGenericPopup.message = qsTr("Mode <" + currentText + "> overlaps with another fixture!") + fmGenericPopup.open() + currentIndex = fixtureManager.fixtureModeIndex(itemID) + } + } + } + } + + // divider + Rectangle + { + visible: showFlags + width: 1 + height: parent.height + } + // fixture flags Rectangle { @@ -231,6 +227,7 @@ Column checkedColor: "transparent" checkable: true checked: itemFlags & MonitorProperties.HiddenFlag ? false : true + tooltip: qsTr("Show/Hide this fixture") onToggled: { if (itemFlags & MonitorProperties.HiddenFlag) @@ -251,6 +248,7 @@ Column checkedColor: "transparent" checkable: true checked: itemFlags & MonitorProperties.InvertedPanFlag ? true : false + tooltip: qsTr("Invert Pan") onToggled: { if (itemFlags & MonitorProperties.InvertedPanFlag) @@ -272,6 +270,7 @@ Column checkedColor: "transparent" checkable: true checked: itemFlags & MonitorProperties.InvertedTiltFlag ? true : false + tooltip: qsTr("Invert Tilt") onToggled: { if (itemFlags & MonitorProperties.InvertedTiltFlag) @@ -293,7 +292,7 @@ Column MouseArea { - width: showFlags ? fxFlags.x : parent.width + width: showFlags ? fxModes.x : parent.width height: parent.height property bool dragActive: drag.active @@ -376,13 +375,13 @@ Column Connections { target: item - onMouseEvent: + function onMouseEvent(type, iID, iType, qItem, mouseMods) { console.log("Got fixture tree node child mouse event") switch (type) { case App.Clicked: - if (qItem == item) + if (qItem === item) { model.isSelected = (mouseMods & Qt.ControlModifier) ? 2 : 1 if (model.hasChildren) @@ -390,14 +389,14 @@ Column } break; case App.Checked: - if (qItem == item) + if (qItem === item) { console.log("Channel " + index + " got checked") model.isChecked = iType } break; case App.DragStarted: - if (qItem == item && !model.isSelected) + if (qItem === item && !model.isSelected) { model.isSelected = 1 // invalidate the modifiers to force a single selection @@ -414,7 +413,10 @@ Column { ignoreUnknownSignals: true target: item - onPathChanged: nodeContainer.pathChanged(oldPath, newPath) + function onPathChanged(oldPath, newPath) + { + nodeContainer.pathChanged(oldPath, newPath) + } } } } diff --git a/qmlui/qml/fixturesfunctions/FixtureProperties.qml b/qmlui/qml/fixturesfunctions/FixtureProperties.qml index 2348436761..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 "." @@ -41,6 +41,7 @@ Rectangle onFxModeChanged: updateAvailableAddress() onFxCountChanged: updateAvailableAddress() + onFxQuantityChanged: updateAvailableAddress() function updateAvailableAddress() { 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/FunctionManager.qml b/qmlui/qml/fixturesfunctions/FunctionManager.qml index cb58fe0c35..bb703b19f5 100644 --- a/qmlui/qml/fixturesfunctions/FunctionManager.qml +++ b/qmlui/qml/fixturesfunctions/FunctionManager.qml @@ -217,7 +217,7 @@ Rectangle z: 2 width: height height: topBar.height - 2 - bgColor: UISettings.bgMain + bgColor: UISettings.bgMedium faColor: checked ? "white" : "gray" faSource: FontAwesome.fa_search checkable: true @@ -239,10 +239,10 @@ Rectangle width: fmContainer.width height: UISettings.iconSizeMedium z: 5 - color: UISettings.bgMain + color: UISettings.bgMedium radius: 5 border.width: 2 - border.color: "#111" + border.color: UISettings.borderColorDark TextInput { @@ -307,7 +307,7 @@ Rectangle Connections { target: item - onMouseEvent: + function onMouseEvent(type, iID, iType, qItem, mouseMods) { //console.log("Got a mouse event in Function Manager: " + type) switch (type) @@ -320,7 +320,7 @@ Rectangle fDragItem.modifiers = mouseMods break; case App.Clicked: - if (qItem == item) + if (qItem === item) { model.isSelected = (mouseMods & Qt.ControlModifier) ? 2 : 1 if (model.hasChildren) @@ -338,14 +338,14 @@ Rectangle fmContainer.doubleClicked(iID, iType) break; case App.DragStarted: - if (qItem == item && !model.isSelected) + if (qItem === item && !model.isSelected) { model.isSelected = 1 // invalidate the modifiers to force a single selection mouseMods = -1 } - if (mouseMods == -1) + if (mouseMods === -1) functionManager.selectFunctionID(iID, false) fDragItem.itemsList = functionManager.selectedFunctionsID() @@ -370,13 +370,19 @@ Rectangle { ignoreUnknownSignals: true target: item - onPathChanged: functionManager.setFolderPath(oldPath, newPath, true) + function onPathChanged(oldPath, newPath) + { + functionManager.setFolderPath(oldPath, newPath, true) + } } Connections { ignoreUnknownSignals: true target: item - onItemsDropped: functionManager.moveFunctions(path) + function onItemsDropped(path) + { + functionManager.moveFunctions(path) + } } } // Loader } // Component @@ -422,7 +428,6 @@ Rectangle var funcRef = functionManager.getFunction(itemsList[0]) itemLabel = funcRef.name itemIcon = functionManager.functionIcon(funcRef.type) - //multipleItems = itemsList.length > 1 ? true : false } } } diff --git a/qmlui/qml/fixturesfunctions/GridEditor.qml b/qmlui/qml/fixturesfunctions/GridEditor.qml index dfc33f7539..165a6f7b27 100644 --- a/qmlui/qml/fixturesfunctions/GridEditor.qml +++ b/qmlui/qml/fixturesfunctions/GridEditor.qml @@ -19,8 +19,7 @@ import QtQuick 2.0 import QtQuick.Layouts 1.2 -import QtQuick.Controls 1.2 -import QtQuick.Controls.Private 1.0 +import QtQuick.Controls 2.13 import "." @@ -39,7 +38,7 @@ Rectangle property int fillDirection: Qt.Horizontal /** The minimum size in pixels of a cell */ - property int mininumCellSize: 0 + property int minimumCellSize: 0 /** An array of data organized as follows: Item ID | Absolute Index | isOdd | item type */ property variant gridData @@ -68,7 +67,11 @@ Rectangle property bool externalDrag: itemDropArea.containsDrag onGridSizeChanged: calculateCellSize() - onGridDataChanged: { selectionData = null; updateViewData() } + onGridDataChanged: + { + setSelectionData(null) + updateViewData() + } onGridLabelsChanged: updateViewLabels() onWidthChanged: repaintTimer.restart() @@ -96,10 +99,10 @@ Rectangle var horSize = width / gridSize.width var vertSize = height / gridSize.height - if (mininumCellSize) + if (minimumCellSize) { - horSize = Math.max(horSize, mininumCellSize) - vertSize = Math.max(vertSize, mininumCellSize) + horSize = Math.max(horSize, minimumCellSize) + vertSize = Math.max(vertSize, minimumCellSize) } if (fillDirection == Qt.Vertical) @@ -154,6 +157,7 @@ Rectangle { item.color = "#7F7F7F" item.icon = "" + item.itemID = -1 } } } @@ -218,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 } } @@ -254,7 +272,7 @@ Rectangle implicitWidth: cellSize z: (gridSize.width * gridSize.height) - index - property int itemID + property int itemID: -1 property alias overColor: colorLayer.color property alias icon: itemIcon.source property alias label: itemLabel.label @@ -318,8 +336,7 @@ Rectangle startY = parseInt(mouse.y / cellSize) var absStart = (startY * gridSize.width) + startX var item = gridItems.itemAt(absStart) - if (item !== null) - currentItemID = item.itemID + currentItemID = item !== null ? item.itemID : -1 selectionOffset = 0 movingSelection = true gridRoot.pressed(startX, startY, mouse.modifiers) @@ -329,6 +346,8 @@ Rectangle { if (selectionOffset != 0) gridRoot.released(lastX, lastY, selectionOffset, mouse.modifiers) + else + gridRoot.released(-1, -1, 0, mouse.modifiers) movingSelection = false validSelection = true @@ -338,6 +357,7 @@ Rectangle onPositionChanged: { + ttText.visible = false ttTimer.restart() if (movingSelection == false) @@ -363,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 a30bfa3c89..8192a71950 100644 --- a/qmlui/qml/fixturesfunctions/IntensityTool.qml +++ b/qmlui/qml/fixturesfunctions/IntensityTool.qml @@ -27,7 +27,7 @@ import "." Rectangle { id: intRoot - width: UISettings.bigItemHeight * 1.5 + width: UISettings.bigItemHeight * (paletteBox.checked ? 2 : 1.5) height: (UISettings.bigItemHeight * 3) + paletteBox.height color: UISettings.bgMedium //border.color: UISettings.bgLight @@ -35,10 +35,15 @@ Rectangle property bool dmxValues: true property bool closeOnSelect: false + property var dragTarget: null property alias showPalette: paletteBox.visible + property alias currentValue: spinBox.value + property real previousValue: 0 + property bool relativeValue: false signal valueChanged(int value) + signal close() onCurrentValueChanged: { @@ -50,13 +55,36 @@ Rectangle } else { - intRoot.valueChanged(dmxValues ? currentValue : currentValue * 2.55) - if (closeOnSelect) - intRoot.visible = false + var val = relativeValue ? currentValue - previousValue : currentValue + if (intRoot.visible) + intRoot.valueChanged(dmxValues ? val : val * 2.55) + //if (closeOnSelect) + // intRoot.close() } + previousValue = currentValue + } + + onVisibleChanged: + { + if (!visible) + paletteBox.checked = false } - onVisibleChanged: if(!visible) paletteBox.checked = false + function show(value) + { + previousValue = 0 + if (value === -1) + { + relativeValue = true + currentValue = 0 + } + else + { + relativeValue = false + currentValue = dmxValues ? Math.round(value) : Math.round(value / 2.55) + } + visible = true + } function loadPalette(pId) { @@ -110,7 +138,18 @@ Rectangle MouseArea { anchors.fill: parent - drag.target: intRoot + drag.target: intRoot.dragTarget ? intRoot.dragTarget : intRoot + } + + GenericButton + { + width: height + height: parent.height + anchors.right: parent.right + border.color: UISettings.bgMedium + useFontawesome: true + label: FontAwesome.fa_times + onClicked: intRoot.close() } } @@ -143,21 +182,20 @@ Rectangle { id: rectMask color: "transparent" - width: Math.round((parent.height * currentValue) / (dmxValues ? 256.0 : 100.0)) - y: parent.height - height: parent.width - transformOrigin: Item.TopLeft - rotation: -90 + anchors.bottom: parent.bottom + width: parent.width + height: { + var range = relativeValue ? 512 : (dmxValues ? 256.0 : 100.0) + var multValue = relativeValue ? currentValue + 255 : currentValue + return Math.round((parent.height * multValue) / range) + } clip: true Image { - id: intForegroundImg - y: -height + anchors.bottom: parent.bottom width: intBackgroundImg.width height: intBackgroundImg.height - transformOrigin: Item.BottomLeft - rotation: 90 source: "qrc:/dimmer-fill.svg" sourceSize: Qt.size(width, height) } @@ -165,14 +203,14 @@ Rectangle Slider { - id: slider anchors.fill: parent orientation: Qt.Vertical - from: 0 + from: relativeValue ? (dmxValues ? -255 : -100) : 0 to: dmxValues ? 255 : 100 stepSize: 1.0 background: Rectangle { color: "transparent" } handle: Rectangle { color: "transparent" } + wheelEnabled: true value: currentValue onPositionChanged: currentValue = valueAt(position) @@ -191,11 +229,11 @@ Rectangle id: spinBox Layout.fillWidth: true height: UISettings.listItemHeight - from: 0 + from: relativeValue ? (dmxValues ? -255 : -100) : 0 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 41fe59ab9b..0d170aff97 100644 --- a/qmlui/qml/fixturesfunctions/LeftPanel.qml +++ b/qmlui/qml/fixturesfunctions/LeftPanel.qml @@ -74,6 +74,16 @@ SidePanel loaderSource = "qrc:/FixtureBrowser.qml" animatePanel(checked) } + + Image + { + x: parent.width - width - 3 + y: 3 + width: parent.height / 3 + height: width + source: "qrc:/add.svg" + sourceSize: Qt.size(width, height) + } } IconButton @@ -89,7 +99,10 @@ SidePanel onToggled: { if (checked == true) + { loaderSource = "qrc:/FixtureGroupManager.qml" + fixtureManager.searchFilter = "" + } animatePanel(checked) } } @@ -114,6 +127,7 @@ SidePanel IconButton { + id: intToolButton objectName: "capIntensity" width: iconSize height: iconSize @@ -122,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 @@ -133,7 +156,8 @@ SidePanel y: UISettings.bigItemHeight visible: false - onValueChanged: fixtureManager.setIntensityValue(value) + onValueChanged: contextManager.setChannelValueByType(QLCChannel.Intensity, value, relativeValue) + onClose: intToolButton.toggle() } } @@ -177,8 +201,8 @@ SidePanel onCheckedChanged: posTool.visible = !posTool.visible onCounterChanged: if (counter == 0) posTool.visible = false - property int panDegrees: 360 - property int tiltDegrees: 270 + property alias panDegrees: posTool.panMaxDegrees + property alias tiltDegrees: posTool.tiltMaxDegrees PositionTool { @@ -187,13 +211,13 @@ SidePanel x: leftSidePanel.width y: UISettings.bigItemHeight visible: false - panMaxDegrees: posToolButton.panDegrees - tiltMaxDegrees: posToolButton.tiltDegrees + onClose: posToolButton.toggle() } } IconButton { + id: colorToolButton objectName: "capColor" width: iconSize height: iconSize @@ -214,7 +238,8 @@ SidePanel visible: false colorsMask: fixtureManager.colorsMask - onColorChanged: fixtureManager.setColorValue(r * 255, g * 255, b * 255, w * 255, a * 255, uv * 255) + onColorChanged: contextManager.setColorValue(Qt.rgba(r, g, b, 1.0), Qt.rgba(w, a, uv, 1.0)) + onClose: colorToolButton.toggle() } } @@ -296,6 +321,7 @@ SidePanel x: leftSidePanel.width y: UISettings.bigItemHeight visible: false + onClose: beamToolButton.toggle() } } @@ -307,6 +333,16 @@ SidePanel color: "transparent" } + IconButton + { + width: iconSize + height: iconSize + faSource: FontAwesome.fa_bolt + tooltip: qsTr("Highlight") + counter: contextManager.selectedFixturesCount + onClicked: contextManager.highlightFixtureSelection() + } + IconButton { property bool pickingActive: contextManager ? contextManager.positionPicking : false diff --git a/qmlui/qml/fixturesfunctions/PaletteManager.qml b/qmlui/qml/fixturesfunctions/PaletteManager.qml index 0000ddf6ce..9df70337e7 100644 --- a/qmlui/qml/fixturesfunctions/PaletteManager.qml +++ b/qmlui/qml/fixturesfunctions/PaletteManager.qml @@ -76,7 +76,7 @@ Rectangle z: 2 width: height height: topBar.height - 2 - bgColor: UISettings.bgMain + bgColor: UISettings.bgMedium faColor: checked ? "white" : "gray" faSource: FontAwesome.fa_search checkable: true @@ -135,10 +135,13 @@ Rectangle visible: allowEditing imgSource: "qrc:/remove.svg" tooltip: qsTr("Delete the selected palette(s)") - //counter: paletteManager.positionCount + enabled: pDragItem.itemsList.length onClicked: { - var selNames = paletteManager.selectedItemNames(pmSelector.itemsList()) + var selNames = [] + for (var i = 0; i < pDragItem.itemsList.length; i++) + selNames.push(pDragItem.itemsList[i].cRef.name) + //console.log(selNames) deleteItemsPopup.message = qsTr("Are you sure you want to delete the following items?") + "\n" + selNames deleteItemsPopup.open() @@ -148,7 +151,15 @@ Rectangle { id: deleteItemsPopup title: qsTr("Delete items") - onAccepted: paletteManager.deletePalettes(pmSelector.itemsList()) + onAccepted: + { + var idList = [] + for (var i = 0; i < pDragItem.itemsList.length; i++) + idList.push(pDragItem.itemsList[i].cRef.id) + + paletteManager.deletePalettes(idList) + pDragItem.itemsList = [] + } } } } // RowLayout @@ -161,10 +172,10 @@ Rectangle width: pmContainer.width height: UISettings.iconSizeMedium z: 5 - color: UISettings.bgMain + color: UISettings.bgMedium radius: 5 border.width: 2 - border.color: "#111" + border.color: UISettings.borderColorDark TextInput { @@ -193,6 +204,8 @@ Rectangle property bool dragActive: false + Component.onDestruction: pmSelector.resetSelection(pListView.model) + model: paletteManager.paletteList delegate: Item @@ -222,12 +235,15 @@ Rectangle pDragItem.y = posnInWindow.y - (pDragItem.height / 4) pDragItem.z = 10 - pmSelector.selectItem(index, pListView.model, mouse.modifiers & Qt.ControlModifier) + pmSelector.selectItem(index, pListView.model, mouse.modifiers) if ((mouse.modifiers & Qt.ControlModifier) == 0) pDragItem.itemsList = [] - pDragItem.itemsList.push(pDelegate) + // workaround array length notification + var arr = pDragItem.itemsList + arr.push(pDelegate) + pDragItem.itemsList = arr } onDoubleClicked: { diff --git a/qmlui/qml/fixturesfunctions/PositionTool.qml b/qmlui/qml/fixturesfunctions/PositionTool.qml index 66319199ba..8aa9f3f910 100644 --- a/qmlui/qml/fixturesfunctions/PositionTool.qml +++ b/qmlui/qml/fixturesfunctions/PositionTool.qml @@ -18,9 +18,7 @@ */ import QtQuick 2.0 -import QtQuick.Layouts 1.0 -import QtQuick.Controls 1.2 -import QtQuick.Controls.Styles 1.2 +import QtQuick.Layouts 1.1 import org.qlcplus.classes 1.0 import "CanvasDrawFunctions.js" as DrawFuncs @@ -38,23 +36,80 @@ Rectangle property int panMaxDegrees: 360 property int tiltMaxDegrees: 270 - property int panDegrees: 0 - property int tiltDegrees: 0 + property alias panDegrees: panSpinBox.value + property int previousPanDegrees: 0 + property bool relativePanValue: false + + property alias tiltDegrees: tiltSpinBox.value + property int previousTiltDegrees: 0 + property bool relativeTiltValue: false property alias showPalette: paletteBox.visible property bool isLoading: false + signal close() + + function updatePanTiltDegrees() + { + previousPanDegrees = 0 + previousTiltDegrees = 0 + + var pan = contextManager.getCurrentValue(QLCChannel.Pan, true) + if (pan === -1) + { + relativePanValue = true + panDegrees = 0 + } + else + { + relativePanValue = false + panDegrees = Math.round(pan) + } + + var tilt = contextManager.getCurrentValue(QLCChannel.Tilt, true) + if (tilt === -1) + { + relativeTiltValue = true + tiltDegrees = 0 + } + else + { + relativeTiltValue = false + tiltDegrees = Math.round(tilt) + } + } + + onVisibleChanged: + { + if (visible) + { + updatePanTiltDegrees() + } + else + { + paletteBox.checked = false + } + } + onPanDegreesChanged: { if (isLoading) return - paletteBox.updateValues(panDegrees, tiltDegrees) - - if (paletteBox.isEditing || paletteBox.checked) - paletteBox.updatePreview() + if (relativePanValue) + { + contextManager.setPositionValue(QLCChannel.Pan, panDegrees - previousPanDegrees, true) + previousPanDegrees = panDegrees + } else - contextManager.setPositionValue(QLCChannel.Pan, panDegrees) + { + paletteBox.updateValues(panDegrees, tiltDegrees) + + if (paletteBox.isEditing || paletteBox.checked) + paletteBox.updatePreview() + else + contextManager.setPositionValue(QLCChannel.Pan, panDegrees, false) + } } onTiltDegreesChanged: @@ -62,12 +117,20 @@ Rectangle if (isLoading) return - paletteBox.updateValues(panDegrees, tiltDegrees) - - if (paletteBox.isEditing || paletteBox.checked) - paletteBox.updatePreview() + if (relativeTiltValue) + { + contextManager.setPositionValue(QLCChannel.Tilt, tiltDegrees - previousTiltDegrees, true) + previousTiltDegrees = tiltDegrees + } else - contextManager.setPositionValue(QLCChannel.Tilt, tiltDegrees) + { + paletteBox.updateValues(panDegrees, tiltDegrees) + + if (paletteBox.isEditing || paletteBox.checked) + paletteBox.updatePreview() + else + contextManager.setPositionValue(QLCChannel.Tilt, tiltDegrees, false) + } } onPanMaxDegreesChanged: gCanvas.requestPaint() @@ -100,16 +163,16 @@ Rectangle if (palette.type === QLCPalette.Pan) { - panSpinBox.value = palette.intValue1 + panDegrees = palette.intValue1 } else if (palette.type === QLCPalette.Tilt) { - tiltSpinBox.value = palette.intValue1 + tiltDegrees = palette.intValue1 } else if (palette.type === QLCPalette.PanTilt) { - panSpinBox.value = palette.intValue1 - tiltSpinBox.value = palette.intValue2 + panDegrees = palette.intValue1 + tiltDegrees = palette.intValue2 } } isLoading = false @@ -149,6 +212,16 @@ Rectangle anchors.fill: parent drag.target: posToolRoot } + GenericButton + { + width: height + height: parent.height + anchors.right: parent.right + border.color: UISettings.bgMedium + useFontawesome: true + label: FontAwesome.fa_times + onClicked: posToolRoot.close() + } } EditorTopBar @@ -161,12 +234,11 @@ Rectangle IconButton { - id: rotateButton x: parent.width - width - 2 y: posToolBar.height z: 2 imgSource: "qrc:/rotate-right.svg" - tooltip: qsTr("Rotate 90° clockwise") + tooltip: qsTr("Rotate preview 90° clockwise") onClicked: { gCanvas.rotation += 90 @@ -175,6 +247,29 @@ Rectangle } } + Timer + { + id: updTimer + running: false + interval: 100 + repeat: false + onTriggered: updatePanTiltDegrees() + } + + IconButton + { + x: parent.width - width - 2 + y: gCanvas.y + gCanvas.height - width + z: 2 + faSource: FontAwesome.fa_bullseye + tooltip: qsTr("Center Pan/Tilt halfway") + onClicked: + { + contextManager.setPositionCenter() + updTimer.restart() + } + } + Canvas { id: gCanvas @@ -236,16 +331,16 @@ Rectangle if (Math.abs(mouse.x - initialXPos) > Math.abs(mouse.y - initialYPos)) { if (mouse.x < initialXPos) - panSpinBox.value++ + panDegrees++ else - panSpinBox.value-- + panDegrees-- } else { if (mouse.y < initialYPos) - tiltSpinBox.value++ + tiltDegrees++ else - tiltSpinBox.value-- + tiltDegrees-- } } } @@ -270,16 +365,12 @@ Rectangle { id: panSpinBox Layout.fillWidth: true - from: 0 + from: relativePanValue ? -panMaxDegrees : 0 to: panMaxDegrees value: 0 suffix: "°" - onValueChanged: - { - panDegrees = value - gCanvas.requestPaint() - } + onValueChanged: gCanvas.requestPaint() } IconButton @@ -320,16 +411,12 @@ Rectangle { id: tiltSpinBox Layout.fillWidth: true - from: 0 + from: relativeTiltValue ? -tiltMaxDegrees : 0 to: tiltMaxDegrees value: 0 suffix: "°" - onValueChanged: - { - tiltDegrees = value - gCanvas.requestPaint() - } + onValueChanged: gCanvas.requestPaint() } IconButton diff --git a/qmlui/qml/fixturesfunctions/PresetCapabilityItem.qml b/qmlui/qml/fixturesfunctions/PresetCapabilityItem.qml index 30f545b443..922fe02773 100644 --- a/qmlui/qml/fixturesfunctions/PresetCapabilityItem.qml +++ b/qmlui/qml/fixturesfunctions/PresetCapabilityItem.qml @@ -27,9 +27,10 @@ Rectangle id: iRoot width: UISettings.bigItemHeight * 1.5 height: UISettings.iconSizeDefault * 1.2 + color: capMouseArea.pressed ? UISettings.bgLight : "white" border.width: 1 - border.color: "#111" + border.color: UISettings.borderColorDark property QLCCapability capability property int capIndex @@ -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] } } @@ -76,7 +77,7 @@ Rectangle width: UISettings.iconSizeDefault height: width border.width: 1 - border.color: "#111" + border.color: UISettings.borderColorDark Rectangle { @@ -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 437ef26b99..f239becbda 100644 --- a/qmlui/qml/fixturesfunctions/PresetsTool.qml +++ b/qmlui/qml/fixturesfunctions/PresetsTool.qml @@ -37,6 +37,9 @@ Rectangle property int selectedFixture: -1 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) @@ -151,10 +154,13 @@ Rectangle { capability: modelData capIndex: index + 1 + visible: (capability.min <= toolRoot.rangeHighLimit || capability.max <= toolRoot.rangeLowLimit) onValueChanged: { - 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/RGBMatrixEditor.qml b/qmlui/qml/fixturesfunctions/RGBMatrixEditor.qml index 1127a4f642..90494132cd 100644 --- a/qmlui/qml/fixturesfunctions/RGBMatrixEditor.qml +++ b/qmlui/qml/fixturesfunctions/RGBMatrixEditor.qml @@ -273,7 +273,7 @@ Rectangle radius: 5 border.color: scMouseArea.containsMouse ? "white" : UISettings.bgLight border.width: 2 - color: startColTool.selectedColor + color: rgbMatrixEditor.startColor visible: rgbMatrixEditor.algoColors > 0 ? true : false MouseArea @@ -287,9 +287,9 @@ Rectangle ColorTool { id: startColTool - parent: mainView - x: rightSidePanel.x - width - y: rightSidePanel.y + parent: rgbmeContainer + x: -width - (UISettings.iconSizeDefault * 1.25) + y: UISettings.bigItemHeight visible: false closeOnSelect: true currentRGB: rgbMatrixEditor.startColor @@ -299,6 +299,7 @@ Rectangle startColButton.color = Qt.rgba(r, g, b, 1.0) rgbMatrixEditor.startColor = startColButton.color } + onClose: visible = false } } Rectangle @@ -317,20 +318,21 @@ Rectangle id: ecMouseArea anchors.fill: parent hoverEnabled: true - onClicked: endColTool.visible = !startColTool.visible + onClicked: endColTool.visible = !endColTool.visible } ColorTool { id: endColTool - parent: mainView - x: rightSidePanel.x - width - y: rightSidePanel.y + parent: rgbmeContainer + x: -width - (UISettings.iconSizeDefault * 1.25) + y: UISettings.bigItemHeight visible: false closeOnSelect: true currentRGB: rgbMatrixEditor.endColor onColorChanged: rgbMatrixEditor.endColor = Qt.rgba(r, g, b, 1.0) + onClose: visible = false } } IconButton @@ -886,6 +888,22 @@ Rectangle console.log("Spin component is not ready !!") } + function addDoubleSpinBox(propName, currentValue) + { + doubleSpinComponent.createObject(scriptAlgoGrid, + {"propName": propName, "realValue": currentValue }); + if (spinComponent.status !== Component.Ready) + console.log("Double spin component is not ready !!") + } + + function addTextEdit(propName, currentText) + { + textEditComponent.createObject(scriptAlgoGrid, + {"propName": propName, "text": currentText }); + if (comboComponent.status !== Component.Ready) + console.log("TextEdit component is not ready !!") + } + Component.onCompleted: { rgbMatrixEditor.createScriptObjects(scriptAlgoGrid) @@ -936,4 +954,33 @@ Rectangle } } + // Script algorithm float box property + Component + { + id: doubleSpinComponent + + CustomDoubleSpinBox + { + Layout.fillWidth: true + property string propName + + decimals: 3 + suffix: "" + onRealValueChanged: rgbMatrixEditor.setScriptFloatProperty(propName, realValue) + } + } + + // Script algorithm combo box property + Component + { + id: textEditComponent + + CustomTextEdit + { + Layout.fillWidth: true + property string propName + + onTextChanged: rgbMatrixEditor.setScriptStringProperty(propName, text) + } + } } diff --git a/qmlui/qml/fixturesfunctions/RGBPanelProperties.qml b/qmlui/qml/fixturesfunctions/RGBPanelProperties.qml index ea37a7f60c..bfcbee19fd 100644 --- a/qmlui/qml/fixturesfunctions/RGBPanelProperties.qml +++ b/qmlui/qml/fixturesfunctions/RGBPanelProperties.qml @@ -18,7 +18,7 @@ */ import QtQuick 2.3 -import QtQuick.Controls 1.2 +import QtQuick.Controls 2.14 import QtQuick.Layouts 1.1 import org.qlcplus.classes 1.0 @@ -116,6 +116,7 @@ Rectangle height: propsGrid.itemsHeight Layout.columnSpan: 3 Layout.fillWidth: true + textRole: "" model: ioManager.universeNames } diff --git a/qmlui/qml/fixturesfunctions/RightPanel.qml b/qmlui/qml/fixturesfunctions/RightPanel.qml index 44e532bf39..7cc0880b70 100644 --- a/qmlui/qml/fixturesfunctions/RightPanel.qml +++ b/qmlui/qml/fixturesfunctions/RightPanel.qml @@ -71,7 +71,7 @@ SidePanel return } - var newFuncID = functionManager.createFunction(fType) + var newFuncID = functionManager.createFunction(fType, contextManager.selectedFixtureIDVariantList()) var fEditor = functionManager.getEditorResource(newFuncID) functionManager.setEditorFunction(newFuncID, false, false) @@ -127,13 +127,13 @@ SidePanel if (strArray.length === 1) { - itemID = functionManager.createFunction(fType, strArray) + itemID = functionManager.createAudioVideoFunction(fType, strArray) functionManager.setEditorFunction(itemID, false, false) loaderSource = functionManager.getEditorResource(itemID) } else { - functionManager.createFunction(fType, strArray) + functionManager.createAudioVideoFunction(fType, strArray) loaderSource = "qrc:/FunctionManager.qml" } @@ -422,6 +422,7 @@ SidePanel { id: dmxDumpDialog implicitWidth: Math.min(UISettings.bigItemHeight * 4, mainView.width / 3) + channelsMask: contextManager ? contextManager.dumpChannelMask : 0 property int sceneID: -1 @@ -440,16 +441,15 @@ SidePanel IconButton { - id: previewFunc - objectName: "previewButton" z: 2 width: iconSize height: iconSize imgSource: "qrc:/play.svg" tooltip: qsTr("Function Preview") checkable: true + checked: functionManager.previewEnabled counter: functionManager.selectedFunctionCount - onToggled: functionManager.setPreview(checked) + onToggled: functionManager.previewEnabled = checked } /* filler object */ diff --git a/qmlui/qml/fixturesfunctions/SceneEditor.qml b/qmlui/qml/fixturesfunctions/SceneEditor.qml index 299b9668c6..cc7bd4daae 100644 --- a/qmlui/qml/fixturesfunctions/SceneEditor.qml +++ b/qmlui/qml/fixturesfunctions/SceneEditor.qml @@ -19,8 +19,7 @@ import QtQuick 2.0 import QtQuick.Layouts 1.1 -import QtQuick.Controls 1.2 as QC1 -import QtQuick.Controls 2.1 +import QtQuick.Controls 2.13 import org.qlcplus.classes 1.0 import "TimeUtils.js" as TimeUtils @@ -33,10 +32,29 @@ Rectangle color: "transparent" property int functionID - property bool showToolBar: true + property bool boundToSequence: false signal requestView(int ID, string qmlSrc) + function deleteSelectedItems() + { + deleteItemsPopup.open() + } + + CustomPopupDialog + { + id: deleteItemsPopup + title: qsTr("Delete items") + message: qsTr("Are you sure you want to remove the selected items?") + onAccepted: + { + if (boundToSequence) + functionManager.deleteSequenceFixtures(seSelector.itemsList()) + else + functionManager.deleteEditorItems(seSelector.itemsList()) + } + } + ModelSelector { id: seSelector @@ -64,14 +82,16 @@ Rectangle } } - QC1.SplitView + SplitView { anchors.fill: parent + Loader { id: sideLoader - visible: width - width: 0 + width: UISettings.sidePanelWidth + SplitView.preferredWidth: UISettings.sidePanelWidth + visible: false height: seContainer.height source: "" @@ -86,26 +106,28 @@ Rectangle width: 2 height: parent.height x: parent.width - 2 - color: UISettings.bgLighter + color: UISettings.bgLight } } Column { + SplitView.fillWidth: true + EditorTopBar { id: toolbar - visible: showToolBar + visible: !boundToSequence text: sceneEditor ? sceneEditor.functionName : "" onTextChanged: sceneEditor.functionName = text onBackClicked: { - if (sideLoader.width) + if (sideLoader.visible) { sideLoader.source = "" - sideLoader.width = 0 - rightSidePanel.width = rightSidePanel.width / 2 + sideLoader.visible = false + rightSidePanel.width -= sideLoader.width } var prevID = sceneEditor.previousID @@ -136,15 +158,16 @@ Rectangle { if (checked) { - rightSidePanel.width += UISettings.sidePanelWidth - sideLoader.width = UISettings.sidePanelWidth + if (!sideLoader.visible) + rightSidePanel.width += UISettings.sidePanelWidth + sideLoader.visible = true sideLoader.source = "qrc:/FixtureGroupManager.qml" } else { - rightSidePanel.width = rightSidePanel.width - sideLoader.width + rightSidePanel.width -= sideLoader.width sideLoader.source = "" - sideLoader.width = 0 + sideLoader.visible = false } } } @@ -172,15 +195,16 @@ Rectangle { if (checked) { - rightSidePanel.width += UISettings.sidePanelWidth - sideLoader.width = UISettings.sidePanelWidth + if (!sideLoader.visible) + rightSidePanel.width += UISettings.sidePanelWidth + sideLoader.visible = true sideLoader.source = "qrc:/PaletteManager.qml" } else { - rightSidePanel.width = rightSidePanel.width - sideLoader.width + rightSidePanel.width -= sideLoader.width sideLoader.source = "" - sideLoader.width = 0 + sideLoader.visible = false } } } @@ -193,15 +217,7 @@ Rectangle height: UISettings.iconSizeMedium imgSource: "qrc:/remove.svg" tooltip: qsTr("Remove the selected items") - onClicked: deleteItemsPopup.open() - - CustomPopupDialog - { - id: deleteItemsPopup - title: qsTr("Delete items") - message: qsTr("Are you sure you want to remove the selected items?") - onAccepted: functionManager.deleteEditorItems(seSelector.itemsList()) - } + onClicked: deleteSelectedItems() } } @@ -257,7 +273,7 @@ Rectangle onClicked: { - seSelector.selectItem(index, sfxList.model, mouse.modifiers & Qt.ControlModifier) + seSelector.selectItem(index, sfxList.model, mouse.modifiers) if (compDelegate.itemType === App.FixtureDragItem) { @@ -284,7 +300,7 @@ Rectangle { if (type === App.Clicked) { - seSelector.selectItem(index, sfxList.model, mouseMods & Qt.ControlModifier) + seSelector.selectItem(index, sfxList.model, mouseMods) if (!(mouseMods & Qt.ControlModifier)) contextManager.resetFixtureSelection() diff --git a/qmlui/qml/fixturesfunctions/SceneFixtureConsole.qml b/qmlui/qml/fixturesfunctions/SceneFixtureConsole.qml index c59f42ec55..fea80e72e5 100644 --- a/qmlui/qml/fixturesfunctions/SceneFixtureConsole.qml +++ b/qmlui/qml/fixturesfunctions/SceneFixtureConsole.qml @@ -64,7 +64,7 @@ Rectangle { height: parent.height width: fxConsole.width + 4 - color: UISettings.bgMain + color: UISettings.bgMedium Component.onCompleted: sceneEditor.registerFixtureConsole(index, fxConsole) Component.onDestruction: sceneEditor.unRegisterFixtureConsole(index) diff --git a/qmlui/qml/fixturesfunctions/ScriptEditor.qml b/qmlui/qml/fixturesfunctions/ScriptEditor.qml index 5deeb45d54..d5687334eb 100644 --- a/qmlui/qml/fixturesfunctions/ScriptEditor.qml +++ b/qmlui/qml/fixturesfunctions/ScriptEditor.qml @@ -20,8 +20,7 @@ import QtQuick 2.0 import QtQuick.Layouts 1.1 import QtQuick.Dialogs 1.1 -import QtQuick.Controls 2.1 -import QtQuick.Controls 1.2 as QC1 +import QtQuick.Controls 2.13 import org.qlcplus.classes 1.0 import "TimeUtils.js" as TimeUtils @@ -54,7 +53,7 @@ Rectangle onTriggered: scriptEditor.scriptContent = scriptEdit.text } - QC1.SplitView + SplitView { width: parent.width height: parent.height - topBar.height @@ -62,15 +61,20 @@ Rectangle Loader { id: sideLoader - visible: width - width: 0 + width: UISettings.sidePanelWidth + SplitView.preferredWidth: UISettings.sidePanelWidth + visible: false + height: seContainer.height source: "" onLoaded: { if (source) + { item.allowEditing = false + item.width = Qt.binding(function() { return sideLoader.width - 2 }) + } } Rectangle @@ -78,14 +82,14 @@ Rectangle width: 2 height: parent.height x: parent.width - 2 - color: UISettings.bgLighter + color: UISettings.bgLight } Connections { ignoreUnknownSignals: true target: sideLoader.item - onDoubleClicked: + function onDoubleClicked() { if (fixtureTreeButton.checked) ID = fixtureManager.fixtureIDfromItemID(ID) @@ -96,7 +100,7 @@ Rectangle Column { - Layout.fillWidth: true + SplitView.fillWidth: true EditorTopBar { @@ -106,6 +110,13 @@ Rectangle onBackClicked: { + if (sideLoader.visible) + { + sideLoader.source = "" + sideLoader.visible = false + rightSidePanel.width -= sideLoader.width + } + var prevID = scriptEditor.previousID functionManager.setEditorFunction(prevID, false, true) requestView(prevID, functionManager.getEditorResource(prevID)) @@ -137,15 +148,16 @@ Rectangle if (checked) { - rightSidePanel.width += UISettings.sidePanelWidth - sideLoader.width = UISettings.sidePanelWidth + if (!sideLoader.visible) + rightSidePanel.width += UISettings.sidePanelWidth + sideLoader.visible = true sideLoader.source = "qrc:/FunctionManager.qml" } else { - rightSidePanel.width = rightSidePanel.width - sideLoader.width + rightSidePanel.width -= sideLoader.width sideLoader.source = "" - sideLoader.width = 0 + sideLoader.visible = false } } } @@ -166,15 +178,16 @@ Rectangle if (checked) { - rightSidePanel.width += UISettings.sidePanelWidth - sideLoader.width = UISettings.sidePanelWidth + if (!sideLoader.visible) + rightSidePanel.width += UISettings.sidePanelWidth + sideLoader.visible = true sideLoader.source = "qrc:/FixtureGroupManager.qml" } else { rightSidePanel.width = rightSidePanel.width - sideLoader.width sideLoader.source = "" - sideLoader.width = 0 + sideLoader.visible = false } } } @@ -209,7 +222,7 @@ Rectangle height: parent.height - topBar.height boundsBehavior: Flickable.StopAtBounds - contentWidth: scriptEdit.paintedWidth + contentWidth: width //scriptEdit.paintedWidth contentHeight: scriptEdit.paintedHeight clip: true @@ -225,9 +238,28 @@ Rectangle contentY = r.y + r.height - height; } + DropArea + { + anchors.fill: parent + onDropped: + { + if (drag.source.itemsList[0].itemType === App.FixtureDragItem) + { + //console.log("Fixture ID: " + drag.source.itemsList[0].cRef.id) + scriptEdit.insert(scriptEdit.cursorPosition, drag.source.itemsList[0].cRef.id) + } + else if (drag.source.hasOwnProperty("fromFunctionManager")) + { + //console.log("Function ID: " + drag.source.itemsList[0]) + scriptEdit.insert(scriptEdit.cursorPosition, drag.source.itemsList[0]) + } + } + } + TextEdit { id: scriptEdit + width: parent.width focus: true font.family: "Roboto Mono" font.pixelSize: UISettings.textSizeDefault * 0.8 @@ -242,7 +274,7 @@ Rectangle ScrollBar.vertical: CustomScrollBar { } ScrollBar.horizontal: CustomScrollBar { } - } + } // Flickable } // Column } // SplitView diff --git a/qmlui/qml/fixturesfunctions/SequenceEditor.qml b/qmlui/qml/fixturesfunctions/SequenceEditor.qml index 7f2098acba..43af9969f0 100644 --- a/qmlui/qml/fixturesfunctions/SequenceEditor.qml +++ b/qmlui/qml/fixturesfunctions/SequenceEditor.qml @@ -66,7 +66,17 @@ Rectangle height: UISettings.iconSizeMedium - 2 imgSource: "qrc:/remove.svg" tooltip: stepsView.checked ? qsTr("Remove the selected steps") : qsTr("Remove the selected fixtures") - onClicked: { } + onClicked: + { + if (stepsView.checked) + { + chaserEditorLoader.item.deleteSelectedItems() + } + else + { + sceneEditorLoader.item.deleteSelectedItems() + } + } } } @@ -135,7 +145,7 @@ Rectangle onLoaded: { - item.showToolBar = false + item.boundToSequence = true } } } diff --git a/qmlui/qml/fixturesfunctions/SettingsView2D.qml b/qmlui/qml/fixturesfunctions/SettingsView2D.qml index c01dc096bc..e801e2f318 100644 --- a/qmlui/qml/fixturesfunctions/SettingsView2D.qml +++ b/qmlui/qml/fixturesfunctions/SettingsView2D.qml @@ -19,6 +19,7 @@ import QtQuick 2.0 import QtQuick.Layouts 1.1 +import QtQuick.Dialogs 1.3 import org.qlcplus.classes 1.0 import "." @@ -85,6 +86,7 @@ Rectangle visible: false onColorChanged: contextManager.setFixturesGelColor(Qt.rgba(r, g, b, 1.0)) + onClose: visible = false } Column @@ -202,140 +204,189 @@ Rectangle } } } // GridLayout - } // SectionBox + } // SectionBox - SectionBox - { - visible: fxPropsVisible - width: parent.width - isExpanded: fxPropsVisible - sectionLabel: qsTr("Selected fixtures") - - sectionContents: - GridLayout + SectionBox + { + width: parent.width + sectionLabel: qsTr("Custom Background") + sectionContents: + GridLayout + { + IconButton { - width: parent.width - columns: 2 - columnSpacing: 5 - rowSpacing: 2 + id: imgButton + width: UISettings.iconSizeMedium + height: width + imgSource: "qrc:/background.svg" - // row 1 - RobotoText - { - height: UISettings.listItemHeight - label: qsTr("Gel color") - } - Rectangle + onClicked: fileDialog.open() + + FileDialog { - Layout.fillWidth: true - height: UISettings.listItemHeight - color: gelColorTool.currentRGB + id: fileDialog + visible: false + title: qsTr("Select an image") + nameFilters: [ "Image files (*.png *.bmp *.jpg *.jpeg *.gif *.svg)", "All files (*)" ] - MouseArea + onAccepted: { - anchors.fill: parent - onClicked: gelColorTool.visible = !gelColorTool.visible + View2D.backgroundImage = fileDialog.fileUrl } } + } + + CustomTextEdit + { + Layout.fillWidth: true + height: UISettings.iconSizeMedium + text: View2D.backgroundImage + } + + IconButton + { + z: 2 + width: UISettings.iconSizeMedium + height: width + faSource: FontAwesome.fa_times + tooltip: qsTr("Reset background") + onClicked: View2D.backgroundImage = "" + } + } + } + + SectionBox + { + visible: fxPropsVisible + width: parent.width + isExpanded: fxPropsVisible + sectionLabel: qsTr("Selected fixtures") + + sectionContents: + GridLayout + { + width: parent.width + columns: 2 + columnSpacing: 5 + rowSpacing: 2 + + // row 1 + RobotoText + { + height: UISettings.listItemHeight + label: qsTr("Gel color") + } + Rectangle + { + Layout.fillWidth: true + height: UISettings.listItemHeight + color: gelColorTool.currentRGB - // row 2 - RobotoText + MouseArea { - height: UISettings.listItemHeight - label: qsTr("Rotation") + anchors.fill: parent + onClicked: gelColorTool.visible = !gelColorTool.visible } - CustomSpinBox + } + + // row 2 + RobotoText + { + height: UISettings.listItemHeight + label: qsTr("Rotation") + } + CustomSpinBox + { + id: fxRotSpin + Layout.fillWidth: true + height: UISettings.listItemHeight + from: -359 + to: 359 + suffix: "°" + value: { - id: fxRotSpin - Layout.fillWidth: true - height: UISettings.listItemHeight - from: -359 - to: 359 - suffix: "°" - value: + switch (View2D.pointOfView) { - switch (View2D.pointOfView) - { - case MonitorProperties.LeftSideView: - case MonitorProperties.RightSideView: - return fxRotation.x - - case MonitorProperties.FrontView: - return fxRotation.z - - default: - return fxRotation.y - } + case MonitorProperties.LeftSideView: + case MonitorProperties.RightSideView: + return fxRotation.x + + case MonitorProperties.FrontView: + return fxRotation.z + + default: + return fxRotation.y } - onValueModified: updateRotation(value) } + onValueModified: updateRotation(value) + } - // row 3 - RobotoText + // row 3 + RobotoText + { + height: UISettings.listItemHeight; + label: qsTr("Alignment") + } + + Row + { + Layout.fillWidth: true + + IconButton { - height: UISettings.listItemHeight; - label: qsTr("Alignment") + id: alignLeftBtn + width: UISettings.iconSizeDefault + height: width + bgColor: UISettings.bgLighter + imgSource: "qrc:/align-left.svg" + tooltip: qsTr("Align the selected items to the left") + onClicked: contextManager.setFixturesAlignment(Qt.AlignLeft) } - - Row + IconButton { - Layout.fillWidth: true - - IconButton - { - id: alignLeftBtn - width: UISettings.iconSizeDefault - height: width - bgColor: UISettings.bgLighter - imgSource: "qrc:/align-left.svg" - tooltip: qsTr("Align the selected items to the left") - onClicked: contextManager.setFixturesAlignment(Qt.AlignLeft) - } - IconButton - { - id: alignTopBtn - width: UISettings.iconSizeDefault - height: width - bgColor: UISettings.bgLighter - imgSource: "qrc:/align-top.svg" - tooltip: qsTr("Align the selected items to the top") - onClicked: contextManager.setFixturesAlignment(Qt.AlignTop) - } + id: alignTopBtn + width: UISettings.iconSizeDefault + height: width + bgColor: UISettings.bgLighter + imgSource: "qrc:/align-top.svg" + tooltip: qsTr("Align the selected items to the top") + onClicked: contextManager.setFixturesAlignment(Qt.AlignTop) } + } + + // row 3 + RobotoText + { + height: UISettings.listItemHeight; + label: qsTr("Distribution") + } - // row 3 - RobotoText + Row + { + Layout.fillWidth: true + + IconButton { - height: UISettings.listItemHeight; - label: qsTr("Distribution") + id: distributeXBtn + width: UISettings.iconSizeDefault + height: width + bgColor: UISettings.bgLighter + imgSource: "qrc:/distribute-x.svg" + tooltip: qsTr("Equally distribute horizontally the selected items") + onClicked: contextManager.setFixturesDistribution(Qt.Horizontal) } - - Row + IconButton { - Layout.fillWidth: true - - IconButton - { - id: distributeXBtn - width: UISettings.iconSizeDefault - height: width - bgColor: UISettings.bgLighter - imgSource: "qrc:/distribute-x.svg" - tooltip: qsTr("Equally distribute horizontally the selected items") - onClicked: contextManager.setFixturesDistribution(Qt.Horizontal) - } - IconButton - { - id: distributeYBtn - width: UISettings.iconSizeDefault - height: width - bgColor: UISettings.bgLighter - imgSource: "qrc:/distribute-y.svg" - tooltip: qsTr("Equally distribute vertically the selected items") - onClicked: contextManager.setFixturesDistribution(Qt.Vertical) - } + id: distributeYBtn + width: UISettings.iconSizeDefault + height: width + bgColor: UISettings.bgLighter + imgSource: "qrc:/distribute-y.svg" + tooltip: qsTr("Equally distribute vertically the selected items") + onClicked: contextManager.setFixturesDistribution(Qt.Vertical) } - } // GridLayout - } // SectionBox + } + } // GridLayout + } // SectionBox } // Column } diff --git a/qmlui/qml/fixturesfunctions/UniverseGridView.qml b/qmlui/qml/fixturesfunctions/UniverseGridView.qml index d253383d00..81694b84c0 100644 --- a/qmlui/qml/fixturesfunctions/UniverseGridView.qml +++ b/qmlui/qml/fixturesfunctions/UniverseGridView.qml @@ -18,47 +18,102 @@ */ import QtQuick 2.0 +import QtQuick.Dialogs 1.2 +import QtQuick.Layouts 1.14 + import "." Flickable { id: universeGridView anchors.fill: parent - anchors.margins: 20 boundsBehavior: Flickable.StopAtBounds + contentHeight: uniGrid.height + topbar.height + UISettings.bigItemHeight - contentHeight: uniGrid.height + uniText.height - + 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() { return false; } - RobotoText + CustomPopupDialog + { + 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 is invalid") + onAccepted: close() + } + + RowLayout { - id: uniText - height: UISettings.textSizeDefault * 2 - labelColor: UISettings.fgLight - label: viewUniverseCombo.currentText - fontSize: UISettings.textSizeDefault * 1.5 - fontBold: true + id: topbar + x: UISettings.iconSizeMedium + height: UISettings.iconSizeDefault * 1.5 + width: parent.width - (UISettings.iconSizeMedium * 2) + + RobotoText + { + id: uniText + height: UISettings.textSizeDefault * 2 + labelColor: UISettings.fgLight + label: ioManager.universeName(contextManager.universeFilter) + fontSize: UISettings.textSizeDefault * 1.5 + fontBold: true + } + + // filler + Rectangle + { + Layout.fillWidth: true + height: parent.height + color: "transparent" + } + + IconButton + { + id: cutBtn + imgSource: "qrc:/edit-cut.svg" + tooltip: qsTr("Cut the selected items into clipboard") + onClicked: fixtureClipboard = contextManager.selectedFixtureIDVariantList() + } + + IconButton + { + id: pasteBtn + enabled: fixtureClipboard && fixtureClipboard.length + imgSource: "qrc:/edit-paste.svg" + tooltip: qsTr("Paste items in the clipboard at the first available position") + onClicked: + { + if (fixtureManager.pasteFromClipboard(fixtureClipboard) !== 0) + errorPopup.open() + } + } } GridEditor { id: uniGrid - anchors.top: uniText.bottom - width: parent.width - 40 - height: cellSize * gridSize.height + x: UISettings.iconSizeMedium + anchors.top: topbar.bottom + width: parent.width - (UISettings.iconSizeMedium * 3) + 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) { return fixtureManager.channelIcon(itemID, chNumber) @@ -74,22 +129,36 @@ Flickable { universeGridView.interactive = false var uniAddress = (yPos * gridSize.width) + xPos + var multiSelection = (contextManager.multipleSelection || mods) + if (selectionData && selectionData.indexOf(uniAddress) >= 0) return - if (contextManager.multipleSelection === false && mods == 0) - { - var empty = [] - setSelectionData(empty) - } - console.log("Fixture pressed at address: " + uniAddress) - setSelectionData(fixtureManager.fixtureSelection(uniAddress)) + + // no modifiers means exclusive selection: + // start from an empty selection + if (multiSelection === 0) + contextManager.resetFixtureSelection() + + console.log("prevFixtureID: " + prevFixtureID + "currentItemID: " + currentItemID) + if (prevFixtureID != currentItemID && multiSelection === 0) + contextManager.setFixtureIDSelection(prevFixtureID, false) + + if (currentItemID != -1) + contextManager.setFixtureIDSelection(currentItemID, true) + + console.log("Fixture pressed at address: " + uniAddress + ", itemID: " + currentItemID) + setSelectionData(contextManager.selectedFixtureAddress()) + + prevFixtureID = currentItemID } onReleased: { universeGridView.interactive = true - if (currentItemID === -1 || validSelection == false) - return; + + if (currentItemID === -1 || validSelection === false || offset === 0) + return + var uniAddress = (yPos * gridSize.width) + xPos fixtureManager.moveFixture(currentItemID, selectionData[0] + offset) } diff --git a/qmlui/qml/fixturesfunctions/qmldir b/qmlui/qml/fixturesfunctions/qmldir index cc06cca23e..c270f28276 100644 --- a/qmlui/qml/fixturesfunctions/qmldir +++ b/qmlui/qml/fixturesfunctions/qmldir @@ -7,16 +7,18 @@ 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 CustomTextEdit 0.1 ../CustomTextEdit.qml +CustomTextInput 0.1 ../CustomTextInput.qml DMXPercentageButton 0.1 ../DMXPercentageButton.qml DMXAddressWidget 0.1 ../DMXAddressWidget.qml -EditableTextBox 0.1 ../EditableTextBox.qml FixtureConsole 0.1 ../FixtureConsole.qml FixtureDelegate 0.1 ../FixtureDelegate.qml FunctionDelegate 0.1 ../FunctionDelegate.qml +GenericButton 0.1 ../GenericButton.qml IconButton 0.1 ../IconButton.qml IconPopupButton 0.1 ../IconPopupButton.qml IconTextEntry 0.1 ../IconTextEntry.qml diff --git a/qmlui/qml/inputoutput/AudioCardsList.qml b/qmlui/qml/inputoutput/AudioCardsList.qml index e0c4e60044..e7deb8eab8 100644 --- a/qmlui/qml/inputoutput/AudioCardsList.qml +++ b/qmlui/qml/inputoutput/AudioCardsList.qml @@ -71,7 +71,6 @@ Rectangle { // return the dragged item to its original position parent = root - acDelegate.color = "transparent" } acDelegate.x = 3 acDelegate.y = 0 @@ -83,7 +82,7 @@ Rectangle x: 3 width: aclContainer.width height: UISettings.listItemHeight * 1.7 - color: delegateRoot.pressed ? "#444" : "transparent" + color: delegateRoot.pressed ? UISettings.highlightPressed : "transparent" // this key must match the one in AudioIOItem, to avoid dragging // an audio input on output and vice-versa @@ -120,7 +119,7 @@ Rectangle width: acDelegate.width height: 1 y: acDelegate.height - 1 - color: "#555" + color: UISettings.bgLight } } }// MouseArea diff --git a/qmlui/qml/inputoutput/AudioDeviceItem.qml b/qmlui/qml/inputoutput/AudioDeviceItem.qml index 619a1d4633..61a039f8df 100644 --- a/qmlui/qml/inputoutput/AudioDeviceItem.qml +++ b/qmlui/qml/inputoutput/AudioDeviceItem.qml @@ -39,7 +39,7 @@ Rectangle radius: 3 color: UISettings.bgLighter border.width: 2 - border.color: "#111" + border.color: UISettings.borderColorDark Row { diff --git a/qmlui/qml/inputoutput/AudioIOItem.qml b/qmlui/qml/inputoutput/AudioIOItem.qml index 305135e4f9..aac35a9435 100644 --- a/qmlui/qml/inputoutput/AudioIOItem.qml +++ b/qmlui/qml/inputoutput/AudioIOItem.qml @@ -91,7 +91,7 @@ Rectangle GradientStop { position: 1 ; color: "#3C832B" } } border.width: 2 - border.color: "#111" + border.color: UISettings.borderColorDark RobotoText { diff --git a/qmlui/qml/inputoutput/InputPatchItem.qml b/qmlui/qml/inputoutput/InputPatchItem.qml index 515126f60f..e1aa1c5325 100644 --- a/qmlui/qml/inputoutput/InputPatchItem.qml +++ b/qmlui/qml/inputoutput/InputPatchItem.qml @@ -44,7 +44,7 @@ Rectangle visible: patch ? (patch.profileName === "None" ? false : true) : false border.width: 2 - border.color: "#222" + border.color: UISettings.borderColorDark color: "#269ABA" radius: 10 @@ -83,7 +83,7 @@ Rectangle radius: 3 color: UISettings.bgLighter border.width: 2 - border.color: "#111" + border.color: UISettings.borderColorDark /* LED kind-of signal indicator */ Rectangle @@ -96,14 +96,14 @@ Rectangle height: width radius: height / 2 border.width: 2 - border.color: "#333" - color: "#666" + border.color: UISettings.bgMedium + color: UISettings.bgLight ColorAnimation on color { id: cAnim from: "#00FF00" - to: "#666" + to: UISettings.bgLight duration: 500 running: false } @@ -112,7 +112,10 @@ Rectangle { id: valChangedSignal target: patch - onInputValueChanged: cAnim.restart() + function onInputValueChanged(inputUniverse, channel, value, key) + { + cAnim.restart() + } } } diff --git a/qmlui/qml/inputoutput/InputProfileEditor.qml b/qmlui/qml/inputoutput/InputProfileEditor.qml new file mode 100644 index 0000000000..4c4d463baf --- /dev/null +++ b/qmlui/qml/inputoutput/InputProfileEditor.qml @@ -0,0 +1,489 @@ +/* + Q Light Controller Plus + InputProfileEditor.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.13 +import QtQml.Models 2.13 + +import org.qlcplus.classes 1.0 +import "." + +ColumnLayout +{ + id: peContainer + + property bool isEditing: false + + function showWarning() + { + messagePopup.title = qsTr("!! Warning !!") + messagePopup.message = qsTr("You are trying to edit a bundled input profile.
    " + + "If you modify and save it, a new file will be stored in
    " + + ioManager.profileUserFolder + "
    and will override the bundled file.") + messagePopup.standardButtons = Dialog.Ok + messagePopup.open() + } + + function showWizard() + { + messagePopup.title = qsTr("Channel wizard activated") + messagePopup.message = qsTr("You have enabled the input channel wizard. After " + + "clicking OK, wiggle your mapped input profile's " + + "controls. They should appear into the list. " + + "Click the wizard button again to stop channel " + + "auto-detection.

    Note that the wizard cannot " + + "tell the difference between a knob and a slider " + + "so you will have to do the change manually.") + messagePopup.standardButtons = Dialog.Ok + messagePopup.open() + } + + function showSaveFirst() + { + messagePopup.title = qsTr("Unsaved changes") + messagePopup.message = qsTr("Do you wish to save the current profile first?\nChanges will be lost if you don't save them.") + messagePopup.standardButtons = Dialog.Yes | Dialog.No | Dialog.Cancel + messagePopup.open() + } + + function updateOptions(type) + { + var extraPress = false + var movement = false + var absRel = false + var feedback = false + + switch (type) + { + case QLCInputChannel.Button: + extraPress = true + feedback = true + break + case QLCInputChannel.Slider: + case QLCInputChannel.Knob: + movement = true + absRel = true + sensitivitySpin.from = 10 + sensitivitySpin.to = 100 + break + case QLCInputChannel.Encoder: + movement = true + sensitivitySpin.from = 1 + sensitivitySpin.to = 20 + break + default: + break + } + + extraPressGroup.visible = extraPress + movementGroup.visible = movement + movementCombo.visible = absRel + movementLabel.visible = absRel + feedbackGroup.visible = feedback + } + + function selectedChannel() + { + return channelList.selectedChannelNumber; + } + + function addNewChannel() + { + channelList.selectedChannelNumber = -1 + inputChannelEditor.open() + inputChannelEditor.initialize(-1, profileEditor.type) + } + + function editSelectedChannel() + { + inputChannelEditor.open() + inputChannelEditor.initialize(channelList.selectedChannelNumber - 1, profileEditor.type) + } + + function removeSelectedChannel() + { + profileEditor.removeChannel(channelList.selectedChannelNumber - 1) + channelList.selectedChannelNumber = -1 + } + + CustomPopupDialog + { + id: messagePopup + standardButtons: Dialog.Ok + title: qsTr("!! Warning !!") + + onClicked: + { + if (role === Dialog.Yes) + { + ioManager.saveInputProfile() + } + else if (role === Dialog.No) + { + ioManager.finishInputProfile() + isEditing = false + close() + } + else if (role === Dialog.Ok || role === Dialog.Cancel) + { + close() + } + } + } + + PopupInputChannelEditor + { + id: inputChannelEditor + + onAccepted: + { + if (profileEditor.saveChannel(channelList.selectedChannelNumber - 1, currentChannelNumber) < 0) + { + messagePopup.title = qsTr("!! Warning !!") + messagePopup.message = qsTr("Channel " + (currentChannelNumber + 1) + " already exists!") + messagePopup.standardButtons = Dialog.Ok + messagePopup.open() + } + } + } + + Rectangle + { + implicitWidth: peContainer.width + implicitHeight: infoBox.height + z: 2 + color: UISettings.bgMedium + + GridLayout + { + id: infoBox + width: peContainer.width + columns: 2 + + RobotoText + { + implicitHeight: UISettings.listItemHeight + label: qsTr("Manufacturer") + } + CustomTextEdit + { + Layout.fillWidth: true + text: peContainer.visible ? profileEditor.manufacturer : "" + onTextChanged: profileEditor.manufacturer = text + } + RobotoText + { + implicitHeight: UISettings.listItemHeight + label: qsTr("Model") + } + CustomTextEdit + { + Layout.fillWidth: true + text: peContainer.visible ? profileEditor.model : "" + onTextChanged: profileEditor.model = text + } + RobotoText + { + implicitHeight: UISettings.listItemHeight + label: qsTr("Type") + } + CustomComboBox + { + ListModel + { + id: profTypeModel + ListElement { mLabel: "MIDI"; mValue: 0 } + ListElement { mLabel: "OS2L"; mValue: 1 } + ListElement { mLabel: "OSC"; mValue: 2 } + ListElement { mLabel: "HID"; mValue: 3 } + ListElement { mLabel: "DMX"; mValue: 4 } + ListElement { mLabel: "ENTTEC"; mValue: 5 } + } + + Layout.fillWidth: true + model: profTypeModel + currValue: peContainer.visible ? profileEditor.type : 0 + onValueChanged: profileEditor.type = currentValue + } + + GroupBox + { + title: qsTr("MIDI Global Settings") + Layout.columnSpan: 2 + Layout.fillWidth: true + visible: peContainer.visible ? profileEditor.type === QLCInputProfile.MIDI : false + font.family: UISettings.robotoFontName + font.pixelSize: UISettings.textSizeDefault + palette.windowText: UISettings.fgMain + + RowLayout + { + width: parent.width + + CustomCheckBox + { + implicitHeight: UISettings.listItemHeight + implicitWidth: implicitHeight + checked: peContainer.visible ? profileEditor.midiNoteOff : false + onToggled: profileEditor.midiNoteOff = checked + } + RobotoText + { + Layout.fillWidth: true + wrapText: true + label: qsTr("When MIDI notes are used, send a Note Off when value is 0") + } + } + } // GroupBox + } // GridLayout + } // Rectangle + + ListView + { + id: channelList + implicitWidth: peContainer.width + Layout.fillHeight: true + boundsBehavior: Flickable.StopAtBounds + clip: true + z: 1 + + model: peContainer.visible ? profileEditor.channels : null + + ScrollBar.vertical: CustomScrollBar { } + + property int selectedChannelNumber: -1 + property QLCInputChannel selectedChannel: null + + header: + RowLayout + { + width: channelList.width + height: UISettings.listItemHeight + + RobotoText + { + width: UISettings.bigItemHeight + height: UISettings.listItemHeight + label: qsTr("Channel") + color: UISettings.sectionHeader + } + Rectangle { width: 1; height: UISettings.listItemHeight; color: UISettings.fgMedium } + + RobotoText + { + Layout.fillWidth: true + height: UISettings.listItemHeight + label: qsTr("Name") + color: UISettings.sectionHeader + } + Rectangle { width: 1; height: UISettings.listItemHeight; color: UISettings.fgMedium } + + RobotoText + { + width: UISettings.bigItemHeight * 1.5 + height: UISettings.listItemHeight + label: qsTr("Type") + color: UISettings.sectionHeader + } + } + + delegate: + Item + { + width: channelList.width + height: UISettings.listItemHeight + + property QLCInputChannel channel: modelData.cRef + + Rectangle + { + anchors.fill: parent + color: UISettings.highlight + visible: channelList.selectedChannelNumber === modelData.chNumber + } + + RowLayout + { + anchors.fill: parent + + RobotoText + { + width: UISettings.bigItemHeight + height: UISettings.listItemHeight + label: modelData.chNumber + } + Rectangle { width: 1; height: UISettings.listItemHeight; color: UISettings.fgMedium } + + RobotoText + { + Layout.fillWidth: true + height: UISettings.listItemHeight + label: channel.name + } + Rectangle { width: 1; height: UISettings.listItemHeight; color: UISettings.fgMedium } + + IconTextEntry + { + width: UISettings.bigItemHeight * 1.5 + height: UISettings.listItemHeight + tLabel: channel.typeString + iSrc: channel.iconResource(channel.type, true) + } + } + + MouseArea + { + anchors.fill: parent + onClicked: + { + channelList.selectedChannelNumber = modelData.chNumber + channelList.selectedChannel = channel + updateOptions(channel.type) + } + } + } + } // ListView + + GroupBox + { + id: extraPressGroup + title: qsTr("Behaviour") + Layout.fillWidth: true + visible: false + font.family: UISettings.robotoFontName + font.pixelSize: UISettings.textSizeDefault + palette.windowText: UISettings.fgMain + + RowLayout + { + width: parent.width + + CustomCheckBox + { + implicitHeight: UISettings.listItemHeight + implicitWidth: implicitHeight + checked: channelList.selectedChannel ? channelList.selectedChannel.sendExtraPress : false + onToggled: channelList.selectedChannel.sendExtraPress = checked + } + RobotoText + { + Layout.fillWidth: true + wrapText: true + label: qsTr("Generate an extra Press/Release when toggled") + } + } + } // GroupBox + + GroupBox + { + id: movementGroup + title: qsTr("Behaviour") + Layout.fillWidth: true + visible: false + font.family: UISettings.robotoFontName + font.pixelSize: UISettings.textSizeDefault + palette.windowText: UISettings.fgMain + + RowLayout + { + width: parent.width + + RobotoText + { + id: movementLabel + implicitHeight: UISettings.listItemHeight + label: qsTr("Movement") + } + CustomComboBox + { + id: movementCombo + ListModel + { + id: moveTypeModel + ListElement { mLabel: "Absolute"; mValue: QLCInputChannel.Absolute } + ListElement { mLabel: "Relative"; mValue: QLCInputChannel.Relative } + } + + implicitHeight: UISettings.listItemHeight + model: moveTypeModel + currentIndex: channelList.selectedChannel ? channelList.selectedChannel.movementType : QLCInputChannel.Absolute + onValueChanged: channelList.selectedChannel.movementType = currentValue + } + RobotoText + { + implicitHeight: UISettings.listItemHeight + label: qsTr("Sensitivity") + } + CustomSpinBox + { + id: sensitivitySpin + implicitHeight: UISettings.listItemHeight + from: 10 + to: 100 + value: channelList.selectedChannel ? channelList.selectedChannel.movementSensitivity : 20 + onValueModified: channelList.selectedChannel.movementSensitivity = value + } + } + } // GroupBox + + GroupBox + { + id: feedbackGroup + title: qsTr("Custom Feedback") + Layout.fillWidth: true + visible: false + font.family: UISettings.robotoFontName + font.pixelSize: UISettings.textSizeDefault + palette.windowText: UISettings.fgMain + + RowLayout + { + width: parent.width + + RobotoText + { + implicitHeight: UISettings.listItemHeight + label: qsTr("Lower value") + } + CustomSpinBox + { + implicitHeight: UISettings.listItemHeight + from: 0 + to: 255 + value: channelList.selectedChannel ? channelList.selectedChannel.lowerValue : 0 + onValueModified: channelList.selectedChannel.lowerValue = value + } + RobotoText + { + implicitHeight: UISettings.listItemHeight + label: qsTr("Upper value") + } + CustomSpinBox + { + implicitHeight: UISettings.listItemHeight + from: 0 + to: 255 + value: channelList.selectedChannel ? channelList.selectedChannel.upperValue : 255 + onValueModified: channelList.selectedChannel.upperValue = value + } + } + } // GroupBox + +} // ColumnLayout diff --git a/qmlui/qml/inputoutput/OutputPatchItem.qml b/qmlui/qml/inputoutput/OutputPatchItem.qml index 0e1ed8abb7..e47cce18d8 100644 --- a/qmlui/qml/inputoutput/OutputPatchItem.qml +++ b/qmlui/qml/inputoutput/OutputPatchItem.qml @@ -43,7 +43,7 @@ Rectangle radius: 3 color: UISettings.bgLighter border.width: 2 - border.color: "#111" + border.color: UISettings.borderColorDark RowLayout { diff --git a/qmlui/qml/inputoutput/PatchWireBox.qml b/qmlui/qml/inputoutput/PatchWireBox.qml index 88b137152d..5d3e7259d8 100644 --- a/qmlui/qml/inputoutput/PatchWireBox.qml +++ b/qmlui/qml/inputoutput/PatchWireBox.qml @@ -73,24 +73,25 @@ Canvas yPos += yDelta } - } - if (showFeedback) - { - yPos = vCenter + (UISettings.bigItemHeight * 0.4) - (UISettings.iconSizeMedium * 0.4) - context.strokeStyle = "#00FF00" - context.fillStyle = "#00FF00" - context.ellipse(0, yPos - halfNode, nodeSize, nodeSize) - context.fill() + if (showFeedback) + { + yPos = vCenter + (UISettings.bigItemHeight * 0.4) - (UISettings.iconSizeMedium * 0.4) + context.strokeStyle = "green" + context.fillStyle = "green" - context.beginPath() - context.moveTo(context.lineWidth, yPos) - context.lineTo(targetX, yPos) - context.stroke() + context.ellipse(0, yPos - halfNode, nodeSize, nodeSize) + context.fill() - context.beginPath() - context.ellipse(targetX, yPos - halfNode, nodeSize, nodeSize) - context.fill() + context.beginPath() + context.moveTo(context.lineWidth, yPos) + context.lineTo(targetX, yPos) + context.stroke() + + context.beginPath() + context.ellipse(targetX, yPos - halfNode, nodeSize, nodeSize) + context.fill() + } } } } diff --git a/qmlui/qml/inputoutput/PluginsList.qml b/qmlui/qml/inputoutput/PluginsList.qml index c5d0bda85c..42e34de37e 100644 --- a/qmlui/qml/inputoutput/PluginsList.qml +++ b/qmlui/qml/inputoutput/PluginsList.qml @@ -84,7 +84,6 @@ Rectangle { // return the dragged item to its original position parent = root - pluginItem.color = "transparent" } pluginItem.x = 3 pluginItem.y = 0 @@ -94,7 +93,7 @@ Rectangle { id: pluginItem x: 3 - color: delegateRoot.pressed ? "#444" : "transparent" + color: delegateRoot.pressed ? UISettings.highlightPressed : "transparent" // this key must match the one in UniverseIOItem, to avoid dragging // an input plugin on output and vice-versa @@ -117,7 +116,7 @@ Rectangle width: parent.width - 6 height: 1 y: parent.height - 1 - color: "#555" + color: UISettings.bgLight } } // PluginDragItem } // MouseArea diff --git a/qmlui/qml/inputoutput/ProfilesList.qml b/qmlui/qml/inputoutput/ProfilesList.qml index af583c2889..54cc3b5bad 100644 --- a/qmlui/qml/inputoutput/ProfilesList.qml +++ b/qmlui/qml/inputoutput/ProfilesList.qml @@ -18,6 +18,10 @@ */ import QtQuick 2.0 +import QtQuick.Layouts 1.1 +import QtQuick.Controls 2.2 + +import org.qlcplus.classes 1.0 import "." Rectangle @@ -38,75 +42,279 @@ Rectangle profListView.model = ioManager.universeInputProfiles(universeIndex) } - ListView + CustomPopupDialog + { + id: deletePopup + standardButtons: Dialog.Ok | Dialog.Cancel + title: qsTr("!! Warning !!") + message: qsTr("Do you wish to permanently delete profile '" + profListView.selectedName + "'?") + onAccepted: + { + ioManager.removeInputProfile(profListView.selectedName) + profListView.model = ioManager.universeInputProfiles(universeIndex) + profListView.selectedIndex = -1 + } + } + + ColumnLayout { - id: profListView - anchors.fill: parent - boundsBehavior: Flickable.StopAtBounds - delegate: - Item + implicitWidth: profilesContainer.width + height: profilesContainer.height + z: 2 + spacing: 3 + + Rectangle + { + id: topBar + implicitWidth: profilesContainer.width + implicitHeight: UISettings.iconSizeMedium + z: 5 + gradient: Gradient + { + GradientStop { position: 0; color: UISettings.toolbarStartSub } + GradientStop { position: 1; color: UISettings.toolbarEnd } + } + + RowLayout { - id: root - height: UISettings.listItemHeight * 2 - width: profilesContainer.width + width: parent.width + height: parent.height + y: 1 - MouseArea + IconButton { - id: delegateRoot - width: profilesContainer.width - height: parent.height + width: height + height: topBar.height - 2 + visible: profEditor.isEditing + enabled: profEditor.isEditing && profileEditor.modified + imgSource: "qrc:/filesave.svg" + tooltip: qsTr("Save this profile") - drag.target: profileItem - drag.threshold: 30 + onClicked: + { + ioManager.saveInputProfile() + profListView.model = ioManager.universeInputProfiles(universeIndex) + } + } + + IconButton + { + width: height + height: topBar.height - 2 + visible: profEditor.isEditing + checkable: true + imgSource: "qrc:/wizard.svg" + tooltip: qsTr("Toggle the automatic detection procedure") - onPressed: profileItem.color = "#444" - onReleased: + onToggled: { - profileItem.x = 3 - profileItem.y = 0 + if (profEditor.isEditing) + { + if (checked) + profEditor.showWizard() + profileEditor.toggleDetection() + } + } + } + + IconButton + { + width: height + height: topBar.height - 2 + imgSource: "qrc:/add.svg" + tooltip: profEditor.isEditing ? qsTr("Add a new channel") : qsTr("Create a new input profile") + + onClicked: + { + if (profEditor.isEditing) + { + profEditor.addNewChannel() + } + else + { + ioManager.createInputProfile() + profEditor.isEditing = true + } + } + } + + IconButton + { + width: height + height: topBar.height - 2 + imgSource: "qrc:/edit.svg" + tooltip: profEditor.isEditing ? qsTr("Edit the selected channel") : qsTr("Edit the selected input profile") + enabled: profEditor.isEditing ? profEditor.selectedChannel() >= 0 : profListView.selectedIndex >= 0 + + onClicked: + { + if (profEditor.isEditing) + { + profEditor.editSelectedChannel() + } + else + { + ioManager.editInputProfile(profListView.selectedName) + profEditor.isEditing = true + if (profListView.selectedIsUser == false) + profEditor.showWarning() + } + } + } - if (profileItem.Drag.target !== null) + IconButton + { + width: height + height: topBar.height - 2 + imgSource: "qrc:/remove.svg" + tooltip: profEditor.isEditing ? qsTr("Delete the selected channel") : qsTr("Delete the selected input profile(s)") + enabled: profEditor.isEditing ? profEditor.selectedChannel() >= 0 : profListView.selectedIndex >= 0 && profListView.selectedIsUser + + onClicked: + { + if (profEditor.isEditing) { - ioManager.setInputProfile(profileItem.pluginUniverse, profileItem.lineName) - profListView.model = ioManager.universeInputProfiles(universeIndex) + profEditor.removeSelectedChannel() } else { - // return the dragged item to its original position - parent = root - profileItem.color = "transparent" + if (profListView.selectedIsUser) + deletePopup.open() } } + } + + Rectangle { Layout.fillWidth: true } - PluginDragItem + GenericButton + { + width: height + height: topBar.height - 2 + visible: profEditor.isEditing + border.color: UISettings.bgMedium + useFontawesome: true + label: FontAwesome.fa_times + onClicked: { - id: profileItem - x: 3 + if (profileEditor.modified) + { + profEditor.showSaveFirst() + } + else + { + ioManager.finishInputProfile() + profEditor.isEditing = false + } + } + } + } + } - // this key must match the one in UniverseIOItem, to avoid dragging - // an input profile in the wrong place + ListView + { + id: profListView + implicitWidth: profilesContainer.width + Layout.fillHeight: true + z: 1 + boundsBehavior: Flickable.StopAtBounds + visible: !profEditor.isEditing - pluginUniverse: modelData.universe - pluginName: modelData.plugin - lineName: modelData.name - pluginLine: modelData.line + property int selectedIndex: -1 + property string selectedName + property bool selectedIsUser: false - Drag.active: delegateRoot.drag.active - Drag.source: delegateRoot - Drag.hotSpot.x: width / 2 - Drag.hotSpot.y: height / 2 - Drag.keys: [ "profile-" + universeIndex ] + delegate: + Item + { + id: itemRoot + height: UISettings.listItemHeight * 2 + width: profilesContainer.width + + MouseArea + { + id: delegateRoot + width: profilesContainer.width + height: parent.height + + drag.target: profileItem + drag.threshold: 30 - // line divider - Rectangle + onClicked: { - width: parent.width - 6 - height: 1 - y: parent.height - 1 - color: "#555" + profListView.selectedIndex = index + profListView.selectedName = profileItem.lineName + profListView.selectedIsUser = modelData.isUser + } + + onReleased: + { + profileItem.x = 3 + profileItem.y = 0 + + if (profileItem.Drag.target !== null) + { + ioManager.setInputProfile(profileItem.pluginUniverse, profileItem.lineName) + profListView.model = ioManager.universeInputProfiles(universeIndex) + } + else + { + // return the dragged item to its original position + parent = itemRoot + } } - } // PluginDragItem - } // MouseArea - } // Item - } // ListView + + PluginDragItem + { + id: profileItem + x: 3 + height: UISettings.listItemHeight * 2 + color: delegateRoot.pressed ? UISettings.highlightPressed : + (profListView.selectedIndex === index ? UISettings.highlight : "transparent") + + pluginUniverse: modelData.universe + pluginName: modelData.plugin + lineName: modelData.name + pluginLine: modelData.line + + Drag.active: delegateRoot.drag.active + Drag.source: delegateRoot + Drag.hotSpot.x: width / 2 + Drag.hotSpot.y: height / 2 + // this key must match the one in UniverseIOItem, to avoid dragging + // an input profile in the wrong place + Drag.keys: [ "profile-" + universeIndex ] + + Text + { + visible: modelData.isUser + anchors.right: parent.right + anchors.rightMargin: 5 + anchors.verticalCenter: parent.verticalCenter + color: UISettings.fgMain + font.family: "FontAwesome" + font.pixelSize: parent.height / 2 + text: FontAwesome.fa_user + } + + // line divider + Rectangle + { + width: parent.width - 6 + height: 1 + y: parent.height - 1 + color: UISettings.bgLight + } + } // PluginDragItem + } // MouseArea + } // Item + } // ListView + + InputProfileEditor + { + id: profEditor + width: profilesContainer.width + Layout.fillHeight: true + visible: isEditing + } + } // ColumnLayout } diff --git a/qmlui/qml/inputoutput/UniverseIOItem.qml b/qmlui/qml/inputoutput/UniverseIOItem.qml index d31fcfadaf..688ebc820d 100644 --- a/qmlui/qml/inputoutput/UniverseIOItem.qml +++ b/qmlui/qml/inputoutput/UniverseIOItem.qml @@ -46,7 +46,7 @@ Rectangle onIsSelectedChanged: { if (isSelected == false) - uniNameEdit.enableEditing(false) + uniNameEdit.setEditingStatus(false) } // area containing the input patches @@ -151,7 +151,7 @@ Rectangle z: 10 patchesNumber: inputPatchesNumber - showFeedback: universe ? universe.hasFeedbacks : false + showFeedback: universe ? universe.hasFeedback : false } // Input patch drop area @@ -193,27 +193,25 @@ Rectangle GradientStop { position: 1 ; color: UISettings.highlight } } border.width: 2 - border.color: "#111" + border.color: UISettings.borderColorDark - EditableTextBox + CustomTextInput { id: uniNameEdit anchors.centerIn: parent width: parent.width - maximumHeight: parent.height - color: "transparent" - inputText: universe ? universe.name : "" - textAlignment: Text.AlignHCenter + height: parent.height + text: universe ? universe.name : "" + horizontalAlignment: Text.AlignHCenter + wrapMode: TextInput.Wrap + allowDoubleClick: true + onTextConfirmed: if(universe) universe.name = text onClicked: { - enableEditing(false) - ioManager.selectedIndex = universe.id - uniItem.selected(universe.id); + uniItem.selected(universe.id) } - - onTextChanged: if (universe) universe.name = text } Canvas @@ -252,7 +250,7 @@ Rectangle height: UISettings.iconSizeMedium * 0.8 faSource: FontAwesome.fa_long_arrow_right checkable: true - tooltip: qsTr("Passthrough") + tooltip: qsTr("Enable/Disable passthrough") checked: universe ? universe.passthrough : false onToggled: if (universe) universe.passthrough = checked } @@ -268,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/inputoutput/qmldir b/qmlui/qml/inputoutput/qmldir index 7a175ecf09..6abd003920 100644 --- a/qmlui/qml/inputoutput/qmldir +++ b/qmlui/qml/inputoutput/qmldir @@ -4,18 +4,21 @@ singleton FontAwesome 1.0 ../FontAwesomeVariables.qml ContextMenuEntry 0.1 ../ContextMenuEntry.qml CustomCheckBox 0.1 ../CustomCheckBox.qml CustomComboBox 0.1 ../CustomComboBox.qml +CustomPopupDialog 0.1 ../popup/CustomPopupDialog.qml CustomSpinBox 0.1 ../CustomSpinBox.qml CustomScrollBar 0.1 ../CustomScrollBar.qml CustomTextEdit 0.1 ../CustomTextEdit.qml +CustomTextInput 0.1 ../CustomTextInput.qml DMXPercentageButton 0.1 ../DMXPercentageButton.qml -EditableTextBox 0.1 ../EditableTextBox.qml FixtureConsole 0.1 ../FixtureConsole.qml FixtureDelegate 0.1 ../FixtureDelegate.qml FunctionDelegate 0.1 ../FunctionDelegate.qml +GenericButton 0.1 ../GenericButton.qml IconButton 0.1 ../IconButton.qml IconPopupButton 0.1 ../IconPopupButton.qml IconTextEntry 0.1 ../IconTextEntry.qml MenuBarEntry 0.1 ../MenuBarEntry.qml +PopupInputChannelEditor 0.1 ../popup/PopupInputChannelEditor.qml QLCPlusFader 0.1 ../QLCPlusFader.qml RobotoText 0.1 ../RobotoText.qml SidePanel 0.1 ../SidePanel.qml diff --git a/qmlui/qml/popup/CustomPopupDialog.qml b/qmlui/qml/popup/CustomPopupDialog.qml index a7d8d5cdc1..fe8fc87be2 100644 --- a/qmlui/qml/popup/CustomPopupDialog.qml +++ b/qmlui/qml/popup/CustomPopupDialog.qml @@ -28,10 +28,11 @@ Dialog id: control x: (mainView.width - width) / 2 y: (mainView.height - height) / 2 - //width: mainView.width / 2 + width: mainView.width / 3 parent: mainView modal: true + closePolicy: Popup.CloseOnEscape title: "" standardButtons: Dialog.Ok | Dialog.Cancel onVisibleChanged: mainView.setDimScreen(visible) @@ -82,6 +83,7 @@ Dialog font.family: UISettings.robotoFontName font.pixelSize: UISettings.textSizeDefault color: UISettings.fgMain + wrapMode: Text.Wrap text: message } diff --git a/qmlui/qml/popup/PopupAbout.qml b/qmlui/qml/popup/PopupAbout.qml index b236be6509..5282488cbe 100644 --- a/qmlui/qml/popup/PopupAbout.qml +++ b/qmlui/qml/popup/PopupAbout.qml @@ -27,7 +27,7 @@ import "." CustomPopupDialog { id: popupRoot - + width: mainView.width / 2 title: qsTr("Information") standardButtons: Dialog.Close @@ -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/popup/PopupChannelModifiers.qml b/qmlui/qml/popup/PopupChannelModifiers.qml new file mode 100644 index 0000000000..a14fe1451c --- /dev/null +++ b/qmlui/qml/popup/PopupChannelModifiers.qml @@ -0,0 +1,262 @@ +/* + Q Light Controller Plus + PopupChannelModifiers.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.14 +import QtQuick.Layouts 1.14 +import QtQuick.Controls 2.14 + +import org.qlcplus.classes 1.0 +import "." + +CustomPopupDialog +{ + id: popupRoot + width: mainView.width / 2 + title: qsTr("Channel Modifiers Editor") + standardButtons: Dialog.Ok | Dialog.Cancel + + property int itemID + property int chIndex + property string modName: "None" + property variant modValues: fixtureManager.channelModifierValues + + onModValuesChanged: modPreview.requestPaint() + + contentItem: + GridLayout + { + width: popupRoot.width + columns: 3 + columnSpacing: UISettings.iconSizeMedium + rowSpacing: 5 + + // row 1 + RowLayout + { + Layout.columnSpan: 3 + height: UISettings.iconSizeMedium + + IconButton + { + width: UISettings.iconSizeMedium + height: width + imgSource: "qrc:/add.svg" + tooltip: qsTr("Insert a modified value after the selected") + onClicked: {} + } + + IconButton + { + width: UISettings.iconSizeMedium + height: width + imgSource: "qrc:/remove.svg" + tooltip: qsTr("Delete the selected modifier value") + onClicked: {} + } + Rectangle + { + Layout.fillWidth: true + height: UISettings.iconSizeMedium + color: "transparent" + } + IconButton + { + width: UISettings.iconSizeMedium + height: width + imgSource: "qrc:/rename.svg" + tooltip: qsTr("Rename the selected modifier template") + onClicked: {} + } + IconButton + { + width: UISettings.iconSizeMedium + height: width + imgSource: "qrc:/filesave.svg" + tooltip: qsTr("Save the selected modifier template") + onClicked: {} + } + } + + // row 2 + Canvas + { + id: modPreview + Layout.columnSpan: 2 + Layout.fillWidth: true + height: UISettings.bigItemHeight * 2 + antialiasing: true + contextType: "2d" + + property real dX: width / 256.0 + property real dY: height / 256.0 + property int selectedPointIndex: -1 + + onPaint: + { + context.globalAlpha = 1.0 + context.fillStyle = UISettings.bgStronger + context.strokeStyle = "yellow" + context.lineWidth = 1 + + var x1, y1, x2, y2 + + context.fillRect(0, 0, width, height) + + if (modValues.length) + { + context.beginPath() + + for (var i = 0; i < modValues.length - 2; i+= 2) + { + x1 = modValues[i] + y1 = modValues[i + 1] + x2 = modValues[i + 2] + y2 = modValues[i + 3] + context.moveTo(x1 * dX, height - (y1 * dY)) + context.lineTo(x2 * dX, height - (y2 * dY)) + } + + context.stroke() + context.closePath() + } + } + + Repeater + { + model: modValues.length / 2 + + Rectangle + { + property int origDMX: modValues[index * 2] + property int modDMX: modValues[(index * 2) + 1] + + width: 14 + height: 14 + x: (origDMX * modPreview.dX) - (width / 2) + y: modPreview.height - (modDMX * modPreview.dY) - (height / 2) + radius: width / 2 + color: modPreview.selectedPointIndex === index ? UISettings.highlight : "yellow" + + MouseArea + { + anchors.fill: parent + onClicked: + { + modPreview.selectedPointIndex = index + origValueSpin.value = origDMX + modValueSpin.value = modDMX + } + } + } + } + } + + ListView + { + id: modList + z: 1 + Layout.rowSpan: 4 + Layout.fillWidth: true + Layout.fillHeight: true + clip: true + boundsBehavior: Flickable.StopAtBounds + headerPositioning: ListView.OverlayHeader + + model: fixtureManager.channelModifiersList + + header: + RobotoText + { + z: 2 + width: modList.width + height: UISettings.listItemHeight + label: qsTr("Templates") + color: UISettings.sectionHeader + } + + delegate: + Rectangle + { + height: UISettings.listItemHeight + width: modList.width + color: modelData === popupRoot.modName ? UISettings.highlight : "transparent" + + RobotoText + { + height: parent.height + label: modelData + } + MouseArea + { + anchors.fill: parent + onClicked: + { + modPreview.selectedPointIndex = -1 + popupRoot.modName = modelData + fixtureManager.selectChannelModifier(modelData) + } + } + + Rectangle + { + y: parent.height - 1 + height: 1 + width: parent.width + color: UISettings.fgMedium + } + } + + ScrollBar.vertical: CustomScrollBar {} + } + + Rectangle + { + Layout.columnSpan: 2 + Layout.fillWidth: true + height: 10 + color: "transparent" + } + + // row 3 + RobotoText + { + height: UISettings.listItemHeight + label: qsTr("Original DMX value") + } + CustomSpinBox + { + id: origValueSpin + from: 0 + to: 255 + } + + // row 4 + RobotoText + { + height: UISettings.listItemHeight + label: qsTr("Modified DMX value") + } + CustomSpinBox + { + id: modValueSpin + from: 0 + to: 255 + } + } +} diff --git a/qmlui/qml/popup/PopupChannelWizard.qml b/qmlui/qml/popup/PopupChannelWizard.qml new file mode 100644 index 0000000000..4f06c83066 --- /dev/null +++ b/qmlui/qml/popup/PopupChannelWizard.qml @@ -0,0 +1,265 @@ +/* + Q Light Controller Plus + PopupChannelWizard.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.2 + +import org.qlcplus.classes 1.0 +import "." + +CustomPopupDialog +{ + id: popupRoot + width: mainView.width / 2 + title: qsTr("Fixture Editor Wizard") + + property EditorRef editorView: null + property ChannelEdit chEdit: null + property bool capabilityWizard: false + property var itemsList: [] + + function updateItemsList(create) + { + pListModel.clear() + + for (var i = 0; i < amountSpin.value; i++) + { + var nStr = nameInputBox.text.replace(/#/g, i + 1) + + if (capabilityWizard) + { + var addrMin = startSpin.value + (widthSpin.value * i) + var addrMax = addrMin + widthSpin.value - 1 + + if (create) + { + chEdit.addCapability(addrMin, addrMax, nStr) + } + else + { + nStr = "[" + addrMin + " - " + addrMax + "] " + nStr + pListModel.append({"name": nStr}) + } + } + else + { + var chType = chTypesCombo.currValue + var compNum = 1 + var compTypes = [ chType ] + var compNames = [ nStr ] + + switch (chType) + { + case EditorRef.RGBChannel: + compNum = 3 + compTypes = [ QLCChannel.Red, QLCChannel.Green, QLCChannel.Blue ] + compNames = [ "Red", "Green", "Blue" ] + break + case EditorRef.RGBWChannel: + compNum = 4 + compTypes = [ QLCChannel.Red, QLCChannel.Green, QLCChannel.Blue, QLCChannel.White ] + compNames = [ "Red", "Green", "Blue", "White" ] + break + case EditorRef.RGBAWChannel: + compNum = 5 + compTypes = [ QLCChannel.Red, QLCChannel.Green, QLCChannel.Blue, QLCChannel.Amber, QLCChannel.White ] + compNames = [ "Red", "Green", "Blue", "Amber", "White" ] + break + } + + for (var j = 0; j < compNum; j++) + { + var str = (compNum == 1) ? nStr : compNames[j] + " " + (i + 1) + var type = (compNum == 1) ? chType : compTypes[j] + + if (create) + { + editorView.addPresetChannel(str, type) + } + else + { + pListModel.append({"name": str}) + } + } + } + } + } + + onOpened: updateItemsList(false) + + onAccepted: + { + updateItemsList(true) + } + + contentItem: + GridLayout + { + columns: 1 + columnSpacing: 5 + + GroupBox + { + title: qsTr("Properties") + Layout.fillWidth: true + font.family: UISettings.robotoFontName + font.pixelSize: UISettings.textSizeDefault + palette.windowText: UISettings.fgMain + + RowLayout + { + RobotoText + { + height: UISettings.listItemHeight + visible: capabilityWizard + label: qsTr("Start") + } + + CustomSpinBox + { + id: startSpin + visible: capabilityWizard + from: 0 + to: 254 + onValueChanged: updateItemsList(false) + } + + RobotoText + { + height: UISettings.listItemHeight + visible: capabilityWizard + label: qsTr("Width") + } + + CustomSpinBox + { + id: widthSpin + visible: capabilityWizard + from: 1 + to: 255 + onValueChanged: updateItemsList(false) + } + + RobotoText + { + height: UISettings.listItemHeight + label: qsTr("Amount") + } + + CustomSpinBox + { + id: amountSpin + from: 1 + to: 1000 + onValueChanged: updateItemsList(false) + } + + RobotoText + { + visible: !capabilityWizard + height: UISettings.listItemHeight + label: qsTr("Type") + } + + CustomComboBox + { + id: chTypesCombo + visible: !capabilityWizard + + ListModel + { + id: chTypesModel + ListElement { mLabel: qsTr("Red"); mIcon: "qrc:/red.svg"; mValue: QLCChannel.Red } + ListElement { mLabel: qsTr("Green"); mIcon: "qrc:/green.svg"; mValue: QLCChannel.Green } + ListElement { mLabel: qsTr("Blue"); mIcon: "qrc:/blue.svg"; mValue: QLCChannel.Blue } + ListElement { mLabel: qsTr("White"); mIcon: "qrc:/white.svg"; mValue: QLCChannel.White } + ListElement { mLabel: qsTr("Amber"); mIcon: "qrc:/amber.svg"; mValue: QLCChannel.Amber } + ListElement { mLabel: qsTr("UV"); mIcon: "qrc:/uv.svg"; mValue: QLCChannel.UV } + ListElement { mLabel: qsTr("RGB"); mIcon: "qrc:/color.svg"; mValue: EditorRef.RGBChannel } + ListElement { mLabel: qsTr("RGBW"); mIcon: "qrc:/color.svg"; mValue: EditorRef.RGBWChannel } + ListElement { mLabel: qsTr("RGBAW"); mIcon: "qrc:/color.svg"; mValue: EditorRef.RGBAWChannel } + ListElement { mLabel: qsTr("Dimmer"); mIcon: "qrc:/dimmer.svg"; mValue: QLCChannel.Intensity } + ListElement { mLabel: qsTr("Pan"); mIcon: "qrc:/pan.svg"; mValue: QLCChannel.Pan } + ListElement { mLabel: qsTr("Tilt"); mIcon: "qrc:/tilt.svg"; mValue: QLCChannel.Tilt } + ListElement { mLabel: qsTr("Color Macro"); mIcon: "qrc:/colorwheel.svg"; mValue: QLCChannel.Colour } + ListElement { mLabel: qsTr("Shutter"); mIcon: "qrc:/shutter.svg"; mValue: QLCChannel.Shutter } + ListElement { mLabel: qsTr("Beam"); mIcon: "qrc:/beam.svg"; mValue: QLCChannel.Beam } + ListElement { mLabel: qsTr("Effect"); mIcon: "qrc:/star.svg"; mValue: QLCChannel.Effect } + + } + model: capabilityWizard ? null : chTypesModel + currValue: capabilityWizard ? 0 : QLCChannel.Red + onValueChanged: + { + currValue = value + updateItemsList(false) + } + } + } // RowLayout + } + + GroupBox + { + title: qsTr("Label") + Layout.fillWidth: true + font.family: UISettings.robotoFontName + font.pixelSize: UISettings.textSizeDefault + palette.windowText: UISettings.fgMain + + CustomTextEdit + { + id: nameInputBox + Layout.fillWidth: true + text: capabilityWizard ? qsTr("Capability #") : qsTr("Channel #") + onAccepted: popupRoot.accept() + onTextChanged: updateItemsList(false) + } + } + + GroupBox + { + title: qsTr("Preview") + Layout.fillWidth: true + font.family: UISettings.robotoFontName + font.pixelSize: UISettings.textSizeDefault + palette.windowText: UISettings.fgMain + + ListView + { + id: previewList + width: parent.width + implicitHeight: UISettings.bigItemHeight * 2 + clip: true + boundsBehavior: Flickable.StopAtBounds + model: ListModel { id: pListModel } + + delegate: + RobotoText + { + height: UISettings.listItemHeight + width: previewList.width + label: modelData + } + + ScrollBar.vertical: CustomScrollBar { } + } + } + } // GridLayout +} diff --git a/qmlui/qml/popup/PopupCreatePalette.qml b/qmlui/qml/popup/PopupCreatePalette.qml index a7259815d0..3178dcf636 100644 --- a/qmlui/qml/popup/PopupCreatePalette.qml +++ b/qmlui/qml/popup/PopupCreatePalette.qml @@ -27,6 +27,7 @@ import "." CustomPopupDialog { id: popupRoot + width: mainView.width / 2 title: qsTr("Create a new palette") property QLCPalette paletteObj diff --git a/qmlui/qml/popup/PopupDMXDump.qml b/qmlui/qml/popup/PopupDMXDump.qml index ef6b75c483..4fc8d376eb 100644 --- a/qmlui/qml/popup/PopupDMXDump.qml +++ b/qmlui/qml/popup/PopupDMXDump.qml @@ -27,10 +27,11 @@ import "." CustomPopupDialog { id: popupRoot + width: mainView.width / 2 title: qsTr("Enter a name for the scene") property bool show: !dontAskCheck.checked - property int channelsMask: contextManager ? contextManager.dumpChannelMask : 0 + property int channelsMask: 0 property alias sceneName: nameInputBox.text function getChannelsMask() @@ -38,29 +39,29 @@ CustomPopupDialog var mask = 0 if (intTypeCheck.checked) - mask |= ContextManager.DimmerType + mask |= App.DimmerType if (colTypeCheck.checked) - mask |= ContextManager.ColorType + mask |= App.ColorType if (colMacroTypeCheck.checked) - mask |= ContextManager.ColorMacroType + mask |= App.ColorMacroType if (goboTypeCheck.checked) - mask |= ContextManager.GoboType + mask |= App.GoboType if (panTypeCheck.checked) - mask |= ContextManager.PanType + mask |= App.PanType if (tiltTypeCheck.checked) - mask |= ContextManager.TiltType + mask |= App.TiltType if (speedTypeCheck.checked) - mask |= ContextManager.SpeedType + mask |= App.SpeedType if (shutterTypeCheck.checked) - mask |= ContextManager.ShutterType + mask |= App.ShutterType if (prismTypeCheck.checked) - mask |= ContextManager.PrismType + mask |= App.PrismType if (beamTypeCheck.checked) - mask |= ContextManager.BeamType + mask |= App.BeamType if (effectTypeCheck.checked) - mask |= ContextManager.EffectType + mask |= App.EffectType if (maintTypeCheck.checked) - mask |= ContextManager.MaintenanceType + mask |= App.MaintenanceType return mask } @@ -122,17 +123,17 @@ CustomPopupDialog CustomCheckBox { id: intTypeCheck - visible: channelsMask & ContextManager.DimmerType + visible: channelsMask & App.DimmerType implicitHeight: UISettings.listItemHeight implicitWidth: implicitHeight Layout.alignment: Qt.AlignRight autoExclusive: false - checked: channelsMask & ContextManager.DimmerType + checked: channelsMask & App.DimmerType } IconTextEntry { - visible: channelsMask & ContextManager.DimmerType + visible: channelsMask & App.DimmerType Layout.fillWidth: true iSrc: "qrc:/intensity.svg" tLabel: qsTr("Intensity") @@ -141,16 +142,16 @@ CustomPopupDialog CustomCheckBox { id: colTypeCheck - visible: channelsMask & ContextManager.ColorType + visible: channelsMask & App.ColorType implicitHeight: UISettings.listItemHeight implicitWidth: implicitHeight Layout.alignment: Qt.AlignRight autoExclusive: false - checked: channelsMask & ContextManager.ColorType + checked: channelsMask & App.ColorType } IconTextEntry { - visible: channelsMask & ContextManager.ColorType + visible: channelsMask & App.ColorType Layout.fillWidth: true iSrc: "qrc:/color.svg" tLabel: qsTr("RGB/CMY/WAUV") @@ -160,16 +161,16 @@ CustomPopupDialog CustomCheckBox { id: colMacroTypeCheck - visible: channelsMask & ContextManager.ColorMacroType + visible: channelsMask & App.ColorMacroType implicitHeight: UISettings.listItemHeight implicitWidth: implicitHeight Layout.alignment: Qt.AlignRight autoExclusive: false - checked: channelsMask & ContextManager.ColorMacroType + checked: channelsMask & App.ColorMacroType } IconTextEntry { - visible: channelsMask & ContextManager.ColorMacroType + visible: channelsMask & App.ColorMacroType Layout.fillWidth: true iSrc: "qrc:/colorwheel.svg" tLabel: qsTr("Color macros") @@ -178,16 +179,16 @@ CustomPopupDialog CustomCheckBox { id: goboTypeCheck - visible: channelsMask & ContextManager.GoboType + visible: channelsMask & App.GoboType implicitHeight: UISettings.listItemHeight implicitWidth: implicitHeight Layout.alignment: Qt.AlignRight autoExclusive: false - checked: channelsMask & ContextManager.GoboType + checked: channelsMask & App.GoboType } IconTextEntry { - visible: channelsMask & ContextManager.GoboType + visible: channelsMask & App.GoboType Layout.fillWidth: true iSrc: "qrc:/gobo.svg" tLabel: qsTr("Gobo") @@ -197,16 +198,16 @@ CustomPopupDialog CustomCheckBox { id: panTypeCheck - visible: channelsMask & ContextManager.PanType + visible: channelsMask & App.PanType implicitHeight: UISettings.listItemHeight implicitWidth: implicitHeight Layout.alignment: Qt.AlignRight autoExclusive: false - checked: channelsMask & ContextManager.PanType + checked: channelsMask & App.PanType } IconTextEntry { - visible: channelsMask & ContextManager.PanType + visible: channelsMask & App.PanType Layout.fillWidth: true iSrc: "qrc:/pan.svg" tLabel: qsTr("Pan") @@ -215,16 +216,16 @@ CustomPopupDialog CustomCheckBox { id: tiltTypeCheck - visible: channelsMask & ContextManager.TiltType + visible: channelsMask & App.TiltType implicitHeight: UISettings.listItemHeight implicitWidth: implicitHeight Layout.alignment: Qt.AlignRight autoExclusive: false - checked: channelsMask & ContextManager.TiltType + checked: channelsMask & App.TiltType } IconTextEntry { - visible: channelsMask & ContextManager.TiltType + visible: channelsMask & App.TiltType Layout.fillWidth: true iSrc: "qrc:/tilt.svg" tLabel: qsTr("Tilt") @@ -234,16 +235,16 @@ CustomPopupDialog CustomCheckBox { id: speedTypeCheck - visible: channelsMask & ContextManager.SpeedType + visible: channelsMask & App.SpeedType implicitHeight: UISettings.listItemHeight implicitWidth: implicitHeight Layout.alignment: Qt.AlignRight autoExclusive: false - checked: channelsMask & ContextManager.SpeedType + checked: channelsMask & App.SpeedType } IconTextEntry { - visible: channelsMask & ContextManager.SpeedType + visible: channelsMask & App.SpeedType Layout.fillWidth: true iSrc: "qrc:/speed.svg" tLabel: qsTr("Speed") @@ -252,16 +253,16 @@ CustomPopupDialog CustomCheckBox { id: shutterTypeCheck - visible: channelsMask & ContextManager.ShutterType + visible: channelsMask & App.ShutterType implicitHeight: UISettings.listItemHeight implicitWidth: implicitHeight Layout.alignment: Qt.AlignRight autoExclusive: false - checked: channelsMask & ContextManager.ShutterType + checked: channelsMask & App.ShutterType } IconTextEntry { - visible: channelsMask & ContextManager.ShutterType + visible: channelsMask & App.ShutterType Layout.fillWidth: true iSrc: "qrc:/shutter.svg" tLabel: qsTr("Shutter/Strobe") @@ -271,16 +272,16 @@ CustomPopupDialog CustomCheckBox { id: prismTypeCheck - visible: channelsMask & ContextManager.PrismType + visible: channelsMask & App.PrismType implicitHeight: UISettings.listItemHeight implicitWidth: implicitHeight Layout.alignment: Qt.AlignRight autoExclusive: false - checked: channelsMask & ContextManager.PrismType + checked: channelsMask & App.PrismType } IconTextEntry { - visible: channelsMask & ContextManager.PrismType + visible: channelsMask & App.PrismType Layout.fillWidth: true iSrc: "qrc:/prism.svg" tLabel: qsTr("Prism") @@ -289,16 +290,16 @@ CustomPopupDialog CustomCheckBox { id: beamTypeCheck - visible: channelsMask & ContextManager.BeamType + visible: channelsMask & App.BeamType implicitHeight: UISettings.listItemHeight implicitWidth: implicitHeight Layout.alignment: Qt.AlignRight autoExclusive: false - checked: channelsMask & ContextManager.BeamType + checked: channelsMask & App.BeamType } IconTextEntry { - visible: channelsMask & ContextManager.BeamType + visible: channelsMask & App.BeamType Layout.fillWidth: true iSrc: "qrc:/beam.svg" tLabel: qsTr("Beam") @@ -308,16 +309,16 @@ CustomPopupDialog CustomCheckBox { id: effectTypeCheck - visible: channelsMask & ContextManager.EffectType + visible: channelsMask & App.EffectType implicitHeight: UISettings.listItemHeight implicitWidth: implicitHeight Layout.alignment: Qt.AlignRight autoExclusive: false - checked: channelsMask & ContextManager.EffectType + checked: channelsMask & App.EffectType } IconTextEntry { - visible: channelsMask & ContextManager.EffectType + visible: channelsMask & App.EffectType Layout.fillWidth: true iSrc: "qrc:/star.svg" tLabel: qsTr("Effect") @@ -326,16 +327,16 @@ CustomPopupDialog CustomCheckBox { id: maintTypeCheck - visible: channelsMask & ContextManager.MaintenanceType + visible: channelsMask & App.MaintenanceType implicitHeight: UISettings.listItemHeight implicitWidth: implicitHeight Layout.alignment: Qt.AlignRight autoExclusive: false - checked: channelsMask & ContextManager.MaintenanceType + checked: channelsMask & App.MaintenanceType } IconTextEntry { - visible: channelsMask & ContextManager.MaintenanceType + visible: channelsMask & App.MaintenanceType Layout.fillWidth: true iSrc: "qrc:/configure.svg" tLabel: qsTr("Maintenance") diff --git a/qmlui/qml/popup/PopupDisclaimer.qml b/qmlui/qml/popup/PopupDisclaimer.qml index 78cced3f5c..3e08f2df36 100644 --- a/qmlui/qml/popup/PopupDisclaimer.qml +++ b/qmlui/qml/popup/PopupDisclaimer.qml @@ -28,6 +28,7 @@ CustomPopupDialog id: popupRoot visible: true + width: mainView.width / 2 title: qsTr("Disclaimer") standardButtons: Dialog.Ok diff --git a/qmlui/qml/popup/PopupImportProject.qml b/qmlui/qml/popup/PopupImportProject.qml index 35b9d31b80..acbbd4c67b 100644 --- a/qmlui/qml/popup/PopupImportProject.qml +++ b/qmlui/qml/popup/PopupImportProject.qml @@ -27,7 +27,7 @@ import "." CustomPopupDialog { id: popupRoot - + width: mainView.width / 2 title: qsTr("Import from project") standardButtons: Dialog.Cancel | Dialog.Apply @@ -68,20 +68,34 @@ CustomPopupDialog width: mainView.width / 3 height: UISettings.iconSizeMedium z: 5 - color: UISettings.bgMain + color: UISettings.bgMedium radius: 5 border.width: 2 - border.color: "#111" + border.color: UISettings.borderColorDark + + Text + { + id: fxSearchIcon + x: 6 + width: height + height: parent.height - 6 + anchors.verticalCenter: parent.verticalCenter + color: "gray" + font.family: "FontAwesome" + font.pixelSize: height - 6 + text: FontAwesome.fa_search + } TextInput { + x: fxSearchIcon.width + 14 y: 3 height: parent.height - 6 - width: parent.width + width: parent.width - x color: UISettings.fgMain text: importManager.fixtureSearchFilter font.family: "Roboto Condensed" - font.pixelSize: parent.height - 6 + font.pixelSize: height - 6 selectionColor: UISettings.highlightPressed selectByMouse: true @@ -94,20 +108,34 @@ CustomPopupDialog width: mainView.width / 3 height: UISettings.iconSizeMedium z: 5 - color: UISettings.bgMain + color: UISettings.bgMedium radius: 5 border.width: 2 - border.color: "#111" + border.color: UISettings.borderColorDark + + Text + { + id: funcSearchIcon + x: 6 + width: height + height: parent.height - 6 + anchors.verticalCenter: parent.verticalCenter + color: "gray" + font.family: "FontAwesome" + font.pixelSize: height - 6 + text: FontAwesome.fa_search + } TextInput { + x: funcSearchIcon.width + 14 y: 3 height: parent.height - 6 - width: parent.width + width: parent.width - x color: UISettings.fgMain text: importManager.functionSearchFilter font.family: "Roboto Condensed" - font.pixelSize: parent.height - 6 + font.pixelSize: height - 6 selectionColor: UISettings.highlightPressed selectByMouse: true @@ -127,7 +155,7 @@ CustomPopupDialog property bool dragActive: false - model: importManager.groupsTreeModel + model: popupRoot.visible ? importManager.groupsTreeModel : null delegate: Component { @@ -138,6 +166,7 @@ CustomPopupDialog onLoaded: { //console.log("[groupEditor] Item " + label + " has children: " + hasChildren) + item.width = Qt.binding(function() { return groupListView.width - (gEditScrollBar.visible ? gEditScrollBar.width : 0) }) item.cRef = classRef item.textLabel = label item.isSelected = Qt.binding(function() { return isSelected }) @@ -150,11 +179,12 @@ CustomPopupDialog if (type) { item.itemType = type - if (type == App.UniverseDragItem) + if (type === App.UniverseDragItem) isExpanded = true } item.nodePath = path item.isExpanded = isExpanded + item.subTreeDelegate = "qrc:/FixtureNodeDelegate.qml" item.childrenDelegate = "qrc:/FixtureDelegate.qml" item.nodeChildren = childrenModel } @@ -163,12 +193,12 @@ CustomPopupDialog { target: item - onMouseEvent: + function onMouseEvent(type, iID, iType, qItem, mouseMods) { switch (type) { case App.Clicked: - if (qItem == item) + if (qItem === item) { model.isSelected = (mouseMods & Qt.ControlModifier) ? 2 : 1 if (model.hasChildren) @@ -176,11 +206,9 @@ CustomPopupDialog } break; case App.Checked: - console.log("Item checked " + qItem + " " + item) - if (qItem == item) - { - model.isChecked = iType - } + console.log("Item checked " + iType) + if (qItem === item) + item.isChecked = iType break; } } @@ -200,7 +228,7 @@ CustomPopupDialog boundsBehavior: Flickable.StopAtBounds - model: importManager.functionsTreeModel + model: popupRoot.visible ? importManager.functionsTreeModel : null delegate: Component { @@ -211,6 +239,7 @@ CustomPopupDialog onLoaded: { + item.width = Qt.binding(function() { return functionsListView.width - (fMgrScrollBar.visible ? fMgrScrollBar.width : 0) }) item.textLabel = label item.isSelected = Qt.binding(function() { return isSelected }) item.isCheckable = isCheckable @@ -236,12 +265,12 @@ CustomPopupDialog { target: item - onMouseEvent: + function onMouseEvent(type, iID, iType, qItem, mouseMods) { switch (type) { case App.Clicked: - if (qItem == item) + if (qItem === item) { model.isSelected = (mouseMods & Qt.ControlModifier) ? 2 : 1 if (model.hasChildren) @@ -249,10 +278,8 @@ CustomPopupDialog } break; case App.Checked: - if (qItem == item) - { + if (qItem === item) model.isChecked = iType - } break; } } diff --git a/qmlui/qml/popup/PopupInputChannelEditor.qml b/qmlui/qml/popup/PopupInputChannelEditor.qml new file mode 100644 index 0000000000..165aedf72b --- /dev/null +++ b/qmlui/qml/popup/PopupInputChannelEditor.qml @@ -0,0 +1,262 @@ +/* + Q Light Controller Plus + PopupInputChannelEditor.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.2 + +import org.qlcplus.classes 1.0 +import "." + +CustomPopupDialog +{ + id: popupRoot + width: mainView.width / 3 + title: qsTr("Input Channel Editor") + standardButtons: Dialog.Cancel | Dialog.Ok + + property QLCInputChannel editChannel: null + property int currentChannelNumber + property bool isMIDI: false + property int midiChannelOffset: 4096 + + function initialize(chNum, profType) + { + if (chNum === -1) + { + currentChannelNumber = 0 + channelSpin.value = 1 + editChannel = profileEditor.getEditChannel(-1) + } + else + { + currentChannelNumber = chNum + channelSpin.value = chNum + 1 + editChannel = profileEditor.getEditChannel(chNum) + } + chTypeCombo.currValue = editChannel.type + isMIDI = (profType === QLCInputProfile.MIDI) ? true : false + updateMIDIInfo() + } + + function updateMIDIInfo() + { + var chNum = channelSpin.value - 1 + var midiChannel = chNum / midiChannelOffset + 1 + var midiParam = 0 + var midiNote = "--" + midiChannelSpin.value = parseInt(midiChannel) + + chNum = parseInt(chNum % midiChannelOffset) + + if (chNum < InputProfEditor.NoteOffset) + { + midiMessageCombo.currValue = InputProfEditor.ControlChange + midiParam = chNum - InputProfEditor.ControlChangeOffset + } + else if (chNum < InputProfEditor.NoteAfterTouchOffset) + { + midiMessageCombo.currValue = InputProfEditor.NoteOnOff + midiParam = chNum - InputProfEditor.NoteOffset + var notes = [ "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"] + var octave = parseInt(midiParam / 12) - 1 + var pitch = parseInt(midiParam % 12) + midiNote = notes[pitch] + "" + octave + } + else if (chNum < InputProfEditor.ProgramChangeOffset) + { + midiMessageCombo.currValue = InputProfEditor.NoteAftertouch + midiParam = chNum - InputProfEditor.NoteAfterTouchOffset + } + else if (chNum < InputProfEditor.ChannelAfterTouchOffset) + { + midiMessageCombo.currValue = InputProfEditor.ProgramChange + midiParam = chNum - InputProfEditor.ProgramChangeOffset + } + else if (chNum < InputProfEditor.PitchWheelOffset) + midiMessageCombo.currValue = InputProfEditor.ChannelAfterTouch + else if (chNum < InputProfEditor.MBCPlaybackOffset) + midiMessageCombo.currValue = InputProfEditor.PitchWheel + else if (chNum < InputProfEditor.MBCBeatOffset) + midiMessageCombo.currValue = InputProfEditor.MBCPlayback + else if (chNum < InputProfEditor.MBCStopOffset) + midiMessageCombo.currValue = InputProfEditor.MBCBeat + else + midiMessageCombo.currValue = InputProfEditor.MBCStop + + midiParamSpin.value = midiParam + midiNoteLabel.label = midiNote + } + + function updateChannel() + { + var midiChannel = midiChannelSpin.value + var midiMessage = midiMessageCombo.currentValue + var midiParam = midiParamSpin.value + var chNum + + switch (midiMessage) + { + case InputProfEditor.ControlChange: + chNum = (midiChannel - 1) * midiChannelOffset + InputProfEditor.ControlChangeOffset + midiParam + break + case InputProfEditor.NoteOnOff: + chNum = (midiChannel - 1) * midiChannelOffset + InputProfEditor.NoteOffset + midiParam + break + case InputProfEditor.NoteAfterTouch: + chNum = (midiChannel - 1) * midiChannelOffset + InputProfEditor.NoteAfterTouchOffset + midiParam + break + case InputProfEditor.ProgramChange: + chNum = (midiChannel - 1) * midiChannelOffset + InputProfEditor.ProgramChangeOffset + midiParam + break + case InputProfEditor.ChannelAfterTouch: + chNum = (midiChannel - 1) * midiChannelOffset + InputProfEditor.ChannelAfterTouchOffset + break + case InputProfEditor.PitchWheel: + chNum = (midiChannel - 1) * midiChannelOffset + InputProfEditor.PitchWheelOffset + break + case InputProfEditor.MBCPlayback: + chNum = (midiChannel - 1) * midiChannelOffset + InputProfEditor.MBCPlaybackOffset + break + case InputProfEditor.MBCBeat: + chNum = (midiChannel - 1) * midiChannelOffset + InputProfEditor.MBCBeatOffset + break + case InputProfEditor.MBCStop: + chNum = (midiChannel - 1) * midiChannelOffset + InputProfEditor.MBCStopOffset + break + } + + channelSpin.value = chNum + 1 + } + + contentItem: + ColumnLayout + { + GroupBox + { + id: channelGroup + title: qsTr("Input Channel") + Layout.fillWidth: true + font.family: UISettings.robotoFontName + font.pixelSize: UISettings.textSizeDefault + palette.windowText: UISettings.fgMain + + GridLayout + { + columns: 3 + + RobotoText { height: UISettings.listItemHeight; label: qsTr("Number") } + RobotoText { height: UISettings.listItemHeight; label: qsTr("Name") } + RobotoText { height: UISettings.listItemHeight; label: qsTr("Type") } + + CustomSpinBox + { + id: channelSpin + from: 1 + to: 65535 + onValueModified: + { + currentChannelNumber = value - 1 + updateMIDIInfo() + } + } + CustomTextEdit + { + id: channelName + Layout.fillWidth: true + text: editChannel ? editChannel.name : "" + onTextEdited: editChannel.name = text + } + CustomComboBox + { + id: chTypeCombo + model: popupRoot.visible ? profileEditor.channelTypeModel : null + currValue: -1 + onValueChanged: editChannel.type = currentValue + } + } + } + + GroupBox + { + id: midiGroup + visible: isMIDI + title: "MIDI" + Layout.fillWidth: true + font.family: UISettings.robotoFontName + font.pixelSize: UISettings.textSizeDefault + palette.windowText: UISettings.fgMain + + GridLayout + { + columns: 4 + + RobotoText { height: UISettings.listItemHeight; label: qsTr("Channel") } + RobotoText { height: UISettings.listItemHeight; label: qsTr("Message") } + RobotoText { height: UISettings.listItemHeight; label: qsTr("Parameter") } + RobotoText { height: UISettings.listItemHeight; label: qsTr("Note") } + + CustomSpinBox + { + id: midiChannelSpin + from: 1 + to: 16 + onValueModified: updateChannel() + } + + CustomComboBox + { + id: midiMessageCombo + ListModel + { + id: midiTypeModel + ListElement { mLabel: "Control Change"; mValue: InputProfEditor.ControlChange } + ListElement { mLabel: "Note On/Off"; mValue: InputProfEditor.NoteOnOff } + ListElement { mLabel: "Note Aftertouch"; mValue: InputProfEditor.NoteAftertouch } + ListElement { mLabel: "Program Change"; mValue: InputProfEditor.ProgramChange } + ListElement { mLabel: "Channel Aftertouch"; mValue: InputProfEditor.ChannelAfterTouch } + ListElement { mLabel: "Pitch Wheel"; mValue: InputProfEditor.PitchWheel } + ListElement { mLabel: "Beat Clock: Start/Stop/Continue"; mValue: InputProfEditor.MBCPlayback } + ListElement { mLabel: "Beat Clock: Beat"; mValue: InputProfEditor.MBCBeat } + } + + Layout.fillWidth: true + model: midiTypeModel + onValueChanged: updateChannel() + } + + CustomSpinBox + { + id: midiParamSpin + from: 0 + to: 127 + onValueModified: updateChannel() + } + + RobotoText + { + id: midiNoteLabel + height: UISettings.listItemHeight + Layout.fillWidth: true + label: "--" + } + } + } + } +} diff --git a/qmlui/qml/popup/PopupManualInputSource.qml b/qmlui/qml/popup/PopupManualInputSource.qml index 45358c892f..d0a81d9210 100644 --- a/qmlui/qml/popup/PopupManualInputSource.qml +++ b/qmlui/qml/popup/PopupManualInputSource.qml @@ -27,7 +27,7 @@ import "." CustomPopupDialog { id: popupRoot - + width: mainView.width / 2 title: qsTr("Manual input source selection") standardButtons: Dialog.Cancel | Dialog.Ok @@ -140,12 +140,12 @@ CustomPopupDialog { target: item - onMouseEvent: + function onMouseEvent(type, iID, iType, qItem, mouseMods) { switch (type) { case App.Clicked: - if (qItem == item) + if (qItem === item) { model.isSelected = (mouseMods & Qt.ControlModifier) ? 2 : 1 if (model.hasChildren) diff --git a/qmlui/qml/popup/PopupMonitor.qml b/qmlui/qml/popup/PopupMonitor.qml index 86bded677c..0e251c58cd 100644 --- a/qmlui/qml/popup/PopupMonitor.qml +++ b/qmlui/qml/popup/PopupMonitor.qml @@ -27,7 +27,7 @@ import "." CustomPopupDialog { id: popupRoot - + width: mainView.width / 3 title: qsTr("2D Point of view selection") standardButtons: Dialog.Ok diff --git a/qmlui/qml/popup/PopupNetworkClient.qml b/qmlui/qml/popup/PopupNetworkClient.qml index 2beeea89e9..7e22aac599 100644 --- a/qmlui/qml/popup/PopupNetworkClient.qml +++ b/qmlui/qml/popup/PopupNetworkClient.qml @@ -27,6 +27,8 @@ import "." CustomPopupDialog { id: popupRoot + width: mainView.width / 3 + title: qsTr("QLC+ client setup") property string serverAddress: "192.168.0.1" property int clientStatus: networkManager.clientStatus @@ -70,8 +72,6 @@ CustomPopupDialog } } - title: qsTr("QLC+ client setup") - contentItem: GridLayout { diff --git a/qmlui/qml/popup/PopupNetworkConnect.qml b/qmlui/qml/popup/PopupNetworkConnect.qml index 6031bd2013..582fc8ee7b 100644 --- a/qmlui/qml/popup/PopupNetworkConnect.qml +++ b/qmlui/qml/popup/PopupNetworkConnect.qml @@ -26,11 +26,11 @@ import "." CustomPopupDialog { id: popupRoot + width: mainView.width / 3 + title: qsTr("Client access request") property string clientName: "" - title: qsTr("Client access request") - contentItem: GridLayout { diff --git a/qmlui/qml/popup/PopupNetworkServer.qml b/qmlui/qml/popup/PopupNetworkServer.qml index e52a15dbe9..243cfa192a 100644 --- a/qmlui/qml/popup/PopupNetworkServer.qml +++ b/qmlui/qml/popup/PopupNetworkServer.qml @@ -26,7 +26,7 @@ import "." CustomPopupDialog { id: popupRoot - + width: mainView.width / 3 title: qsTr("QLC+ server setup") contentItem: diff --git a/qmlui/qml/popup/PopupPINRequest.qml b/qmlui/qml/popup/PopupPINRequest.qml index 0d9c15d399..ab26b6e17c 100644 --- a/qmlui/qml/popup/PopupPINRequest.qml +++ b/qmlui/qml/popup/PopupPINRequest.qml @@ -25,6 +25,8 @@ import "." CustomPopupDialog { + width: mainView.width / 3 + property string currentPIN: "" property alias sessionValidate: validateCheck.checked diff --git a/qmlui/qml/popup/PopupPINSetup.qml b/qmlui/qml/popup/PopupPINSetup.qml index b50b9f12bf..866d366413 100644 --- a/qmlui/qml/popup/PopupPINSetup.qml +++ b/qmlui/qml/popup/PopupPINSetup.qml @@ -26,6 +26,7 @@ import "." CustomPopupDialog { id: pinDialogRoot + width: mainView.width / 3 property string currentPIN: "" property string newPIN: "" diff --git a/qmlui/qml/popup/PopupRenameItems.qml b/qmlui/qml/popup/PopupRenameItems.qml index 841bdfb850..d7ea9c0076 100644 --- a/qmlui/qml/popup/PopupRenameItems.qml +++ b/qmlui/qml/popup/PopupRenameItems.qml @@ -25,6 +25,8 @@ import "." CustomPopupDialog { + width: mainView.width / 3 + property alias editText: newNameEdit.text property bool showNumbering: false property alias numberingEnabled: numCheckBox.checked diff --git a/qmlui/qml/popup/qmldir b/qmlui/qml/popup/qmldir new file mode 100644 index 0000000000..27225cf5d4 --- /dev/null +++ b/qmlui/qml/popup/qmldir @@ -0,0 +1,25 @@ +singleton UISettings 1.0 ../UISettings.qml +singleton FontAwesome 1.0 ../FontAwesomeVariables.qml + +ContextMenuEntry 0.1 ../ContextMenuEntry.qml +CustomCheckBox 0.1 ../CustomCheckBox.qml +CustomComboBox 0.1 ../CustomComboBox.qml +CustomSpinBox 0.1 ../CustomSpinBox.qml +CustomScrollBar 0.1 ../CustomScrollBar.qml +CustomTextEdit 0.1 ../CustomTextEdit.qml +CustomTextInput 0.1 ../CustomTextInput.qml +DMXPercentageButton 0.1 ../DMXPercentageButton.qml +FixtureConsole 0.1 ../FixtureConsole.qml +FixtureDelegate 0.1 ../FixtureDelegate.qml +FunctionDelegate 0.1 ../FunctionDelegate.qml +GenericButton 0.1 ../GenericButton.qml +IconButton 0.1 ../IconButton.qml +IconPopupButton 0.1 ../IconPopupButton.qml +IconTextEntry 0.1 ../IconTextEntry.qml +MenuBarEntry 0.1 ../MenuBarEntry.qml +QLCPlusFader 0.1 ../QLCPlusFader.qml +RobotoText 0.1 ../RobotoText.qml +SidePanel 0.1 ../SidePanel.qml +TreeNodeDelegate 0.1 ../TreeNodeDelegate.qml +WindowLoader 0.1 ../WindowLoader.qml + diff --git a/qmlui/qml/qmldir b/qmlui/qml/qmldir index 3c0aa69526..bdeb543e7c 100644 --- a/qmlui/qml/qmldir +++ b/qmlui/qml/qmldir @@ -15,11 +15,11 @@ CustomSlider 0.1 CustomSlider.qml CustomSpinBox 0.1 CustomSpinBox.qml CustomDoubleSpinBox 0.1 CustomDoubleSpinBox.qml CustomTextEdit 0.1 CustomTextEdit.qml +CustomTextInput 0.1 CustomTextInput.qml DayTimeTool 0.1 DayTimeTool.qml DMXAddressTool 0.1 DMXAddressTool.qml DMXAddressWidget 0.1 DMXAddressWidget.qml DMXPercentageButton 0.1 DMXPercentageButton.qml -EditableTextBox 0.1 EditableTextBox.qml ExternalControls.qml 0.1 ExternalControls.qml ExternalControlDelegate.qml 0.1 ExternalControlDelegate.qml FixtureConsole 0.1 FixtureConsole.qml diff --git a/qmlui/qml/showmanager/HeaderAndCursor.qml b/qmlui/qml/showmanager/HeaderAndCursor.qml index 3350d668de..8c8231ef0a 100644 --- a/qmlui/qml/showmanager/HeaderAndCursor.qml +++ b/qmlui/qml/showmanager/HeaderAndCursor.qml @@ -19,6 +19,8 @@ import QtQuick 2.0 +import org.qlcplus.classes 1.0 + import "TimeUtils.js" as TimeUtils import "." @@ -37,6 +39,9 @@ Rectangle property real timeScale: showManager.timeScale property real tickSize: showManager.tickSize property int currentTime: showManager.currentTime + property int timeDivision: showManager.timeDivision + property int bpmNumber: ioManager.bpmNumber + property int beatsDivision: showManager.beatsDivision property bool showTimeMarkers: true signal clicked(int mouseX, int mouseY) @@ -69,10 +74,17 @@ Rectangle } } + onTimeDivisionChanged: timeHeader.requestPaint() + onCurrentTimeChanged: { if (cursorHeight) - cursor.x = TimeUtils.timeToSize(currentTime, timeScale, tickSize) + { + if (timeDivision === Show.Time) + cursor.x = TimeUtils.timeToSize(currentTime, timeScale, tickSize) + else + cursor.x = TimeUtils.timeToBeatPosition(currentTime, tickSize, bpmNumber, beatsDivision) + } } onDurationChanged: @@ -129,6 +141,7 @@ Rectangle onPaint: { var fontSize = headerHeight * 0.55 + var subDividers = showManager.beatsDivision context.globalAlpha = 1.0 context.lineWidth = 1 @@ -148,6 +161,7 @@ Rectangle var divNum = width / tickSize var xPos = parseInt((x + width) / tickSize) * tickSize var msTime = TimeUtils.posToMs(xPos, timeScale, tickSize) + var barNumber = parseInt(xPos / tickSize) xPos -= x //console.log("xPos: " + xPos + ", msTime: " + msTime) @@ -155,19 +169,37 @@ Rectangle context.beginPath() context.fillStyle = "white" + // paint bars and text markers from the end to the beginning for (var i = 0; i < divNum; i++) { // don't even bother to paint if we're outside the timeline if (msTime >= 0) { + if (subDividers > 1) + { + var subX = xPos - (tickSize / subDividers) + for (var s = 0; s < subDividers - 1; s++) + { + context.moveTo(subX, height / 2) + context.lineTo(subX, height) + subX -= (tickSize / subDividers) + } + } + context.moveTo(xPos, 0) context.lineTo(xPos, height) if (showTimeMarkers) - context.fillText(TimeUtils.msToString(msTime), xPos + 3, height - fontSize) + { + if (timeDivision === Show.Time) + context.fillText(TimeUtils.msToString(msTime), xPos + 3, height - fontSize) + else + context.fillText(barNumber, xPos + 3, height - fontSize) + } } xPos -= tickSize msTime -= timeScale * 1000 + barNumber-- //console.log("xPos: " + xPos + ", msTime: " + msTime) } diff --git a/qmlui/qml/showmanager/ShowItem.qml b/qmlui/qml/showmanager/ShowItem.qml index d9cae36257..e94a004f6d 100644 --- a/qmlui/qml/showmanager/ShowItem.qml +++ b/qmlui/qml/showmanager/ShowItem.qml @@ -18,8 +18,7 @@ */ import QtQuick 2.0 -import QtQuick.Controls 1.2 -import QtQuick.Controls.Private 1.0 +import QtQuick.Controls 2.13 import org.qlcplus.classes 1.0 import "TimeUtils.js" as TimeUtils @@ -37,19 +36,20 @@ Item property int startTime: sfRef ? sfRef.startTime : -1 property int duration: sfRef ? sfRef.duration : -1 property int trackIndex: -1 + property int timeDivision: showManager.timeDivision property real timeScale: showManager.timeScale property real tickSize: showManager.tickSize + property int beatsDivision: showManager.beatsDivision property bool isSelected: false + property bool isDragging: false property color globalColor: showManager.itemsColor property string infoText: "" + property string toolTipText: "" - onStartTimeChanged: x = TimeUtils.timeToSize(startTime, timeScale, tickSize) - onDurationChanged: width = TimeUtils.timeToSize(duration, timeScale, tickSize) - onTimeScaleChanged: - { - x = TimeUtils.timeToSize(startTime, timeScale, tickSize) - width = TimeUtils.timeToSize(duration, timeScale, tickSize) - } + onStartTimeChanged: updateGeometry() + onDurationChanged: updateGeometry() + onTimeScaleChanged: updateGeometry() + onTimeDivisionChanged: updateGeometry() onGlobalColorChanged: { @@ -57,6 +57,51 @@ Item sfRef.color = globalColor } + onFuncRefChanged: + { + updateGeometry() + updateTooltipText() + } + + function updateGeometry() + { + if (isDragging || funcRef == null) + return + + if (timeDivision === Show.Time) + { + x = TimeUtils.timeToSize(startTime, timeScale, tickSize) + width = TimeUtils.timeToSize(duration, timeScale, tickSize) + } + else + { + x = TimeUtils.beatsToSize(startTime, tickSize, beatsDivision) + width = TimeUtils.beatsToSize(duration, tickSize, beatsDivision) + } + } + + function updateTooltipText() + { + var tooltip = funcRef ? funcRef.name + "\n" : "" + var pos = 0 + var dur = 0 + + if (timeDivision === Show.Time) + { + pos = TimeUtils.msToString(TimeUtils.posToMs(itemRoot.x + showItemBody.x, timeScale, tickSize)) + dur = TimeUtils.msToString(TimeUtils.posToMs(itemRoot.width, timeScale, tickSize)) + } + else + { + pos = TimeUtils.beatsToString((itemRoot.x + showItemBody.x) / (tickSize / beatsDivision), beatsDivision) + dur = TimeUtils.beatsToString(itemRoot.width / (tickSize / beatsDivision), beatsDivision) + } + + tooltip += qsTr("Position: ") + pos + tooltip += "\n" + qsTr("Duration: ") + dur + toolTipText = tooltip + } + /* Locker image */ Image { @@ -98,44 +143,59 @@ Item var lastTime = 0 var xPos = 0 + var stepsCount = 0 - if (previewData[0] === ShowManager.RepeatingDuration) - { - var loopCount = funcRef.totalDuration ? Math.floor(sfRef.duration / funcRef.totalDuration) : 0 - for (var l = 0; l < loopCount; l++) - { - lastTime += previewData[1] - xPos = TimeUtils.timeToSize(lastTime, timeScale, tickSize) - context.moveTo(xPos, 0) - context.lineTo(xPos, itemRoot.height) - } - context.stroke() - return - } - - for (var i = 0; i < previewData.length; i+=2) + for (var i = 0; i < previewData.length; i += 2) { if (i + 1 >= previewData.length) break - switch(previewData[i]) + switch (previewData[i]) { + case ShowManager.RepeatingDuration: + var loopCount = funcRef.totalDuration ? Math.floor(sfRef.duration / funcRef.totalDuration) : 0 + for (var l = 0; l < loopCount; l++) + { + lastTime += previewData[1] + if (timeDivision === Show.Time) + xPos = TimeUtils.timeToSize(lastTime, timeScale, tickSize) + else + xPos = TimeUtils.beatsToSize(lastTime, tickSize, beatsDivision) + context.moveTo(xPos, 0) + context.lineTo(xPos, itemRoot.height) + } + context.stroke() + lastTime = 0 + xPos = 0 + break case ShowManager.FadeIn: - var fiEnd = TimeUtils.timeToSize(lastTime + previewData[i + 1], timeScale, tickSize) + var fiEnd + if (timeDivision === Show.Time) + fiEnd = TimeUtils.timeToSize(lastTime + previewData[i + 1], timeScale, tickSize) + else + fiEnd = TimeUtils.beatsToSize(lastTime + previewData[i + 1], tickSize, beatsDivision) context.moveTo(xPos, itemRoot.height) context.lineTo(fiEnd, 0) - break; + break case ShowManager.StepDivider: lastTime = previewData[i + 1] - xPos = TimeUtils.timeToSize(lastTime, timeScale, tickSize) + if (timeDivision === Show.Time) + xPos = TimeUtils.timeToSize(lastTime, timeScale, tickSize) + else + xPos = TimeUtils.beatsToSize(lastTime, tickSize, beatsDivision) context.moveTo(xPos, 0) context.lineTo(xPos, itemRoot.height) - break; + stepsCount++ + break case ShowManager.FadeOut: - var foEnd = TimeUtils.timeToSize(lastTime + previewData[i + 1], timeScale, tickSize) - context.moveTo(xPos, 0) - context.lineTo(foEnd, itemRoot.height) - break; + var foEnd + if (timeDivision === Show.Time) + foEnd = TimeUtils.timeToSize(lastTime + previewData[i + 1], timeScale, tickSize) + else + foEnd = TimeUtils.beatsToSize(lastTime + previewData[i + 1], tickSize, beatsDivision) + context.moveTo(stepsCount ? xPos : itemRoot.width - foEnd, 0) + context.lineTo(stepsCount ? foEnd : itemRoot.width, itemRoot.height) + break } } @@ -212,12 +272,19 @@ Item itemRoot.z++ infoTextBox.height = itemRoot.height / 4 infoTextBox.textHAlign = Text.AlignLeft + isDragging = true } onPositionChanged: { if (drag.target !== null) { - infoText = qsTr("Position: ") + TimeUtils.msToString(TimeUtils.posToMs(itemRoot.x + showItemBody.x, timeScale, tickSize)) + var txt + if (timeDivision === Show.Time) + txt = TimeUtils.msToString(TimeUtils.posToMs(itemRoot.x + showItemBody.x, timeScale, tickSize)) + else + txt = TimeUtils.beatsToString((itemRoot.x + showItemBody.x) / (tickSize / beatsDivision), beatsDivision) + + infoText = qsTr("Position: ") + txt } } onReleased: @@ -231,9 +298,13 @@ Item drag.target = null infoText = "" - var newTime = TimeUtils.posToMs(itemRoot.x + showItemBody.x, timeScale, tickSize) - var newTrackIdx = Math.round((itemRoot.y + showItemBody.y) / itemRoot.height) + var newTime + if (timeDivision === Show.Time) + newTime = TimeUtils.posToMs(itemRoot.x + showItemBody.x, timeScale, tickSize) + else + newTime = TimeUtils.posToBeat(itemRoot.x + showItemBody.x, tickSize, beatsDivision) + var newTrackIdx = Math.round((itemRoot.y + showItemBody.y) / itemRoot.height) // dragging to 0 might not be accurate... if (newTime < 0) newTime = 0 @@ -253,6 +324,9 @@ Item } itemRoot.z-- showManager.enableFlicking(true) + updateTooltipText() + isDragging = false + updateGeometry() } onClicked: @@ -262,22 +336,14 @@ Item } onDoubleClicked: functionManager.setEditorFunction(sfRef.functionID, true, false) + } - onExited: Tooltip.hideText() - onCanceled: Tooltip.hideText() - - Timer - { - interval: 1000 - running: sfMouseArea.containsMouse - onTriggered: - { - var tooltip = funcRef ? funcRef.name + "\n" : "0" - tooltip += qsTr("Position: ") + TimeUtils.msToString(TimeUtils.posToMs(itemRoot.x + showItemBody.x, timeScale, tickSize)) - tooltip += "\n" + qsTr("Duration: ") + TimeUtils.msToString(TimeUtils.posToMs(itemRoot.width, timeScale, tickSize)) - Tooltip.showText(sfMouseArea, Qt.point(sfMouseArea.mouseX, sfMouseArea.mouseY), tooltip) - } - } + Text + { + anchors.fill: parent + ToolTip.visible: sfMouseArea.containsMouse + ToolTip.delay: 1000 + ToolTip.text: toolTipText } /* horizontal left handler */ @@ -302,6 +368,8 @@ Item drag.axis: Drag.XAxis drag.maximumX: horRightHandler.x + onPressed: isDragging = true + onPositionChanged: { if (drag.active == true) @@ -311,8 +379,7 @@ Item itemRoot.x = hdlPos.x - mouse.x infoTextBox.height = itemRoot.height / 2 infoTextBox.textHAlign = Text.AlignLeft - infoText = qsTr("Position: ") + TimeUtils.msToString(TimeUtils.posToMs(itemRoot.x + showItemBody.x, timeScale, tickSize)) - infoText += "\n" + qsTr("Duration: ") + TimeUtils.msToString(TimeUtils.posToMs(itemRoot.width, timeScale, tickSize)) + updateTooltipText() horLeftHandler.x = 0 } } @@ -337,8 +404,24 @@ Item itemRoot.width += (currX - itemRoot.x) } - sfRef.startTime = TimeUtils.posToMs(itemRoot.x, timeScale, tickSize) - sfRef.duration = TimeUtils.posToMs(itemRoot.width, timeScale, tickSize) + var newDuration, newStartTime + + if (timeDivision === Show.Time) + { + newStartTime = TimeUtils.posToMs(itemRoot.x, timeScale, tickSize) + newDuration = TimeUtils.posToMs(itemRoot.width, timeScale, tickSize) + } + else + { + newStartTime = TimeUtils.posToBeat(itemRoot.x, tickSize, beatsDivision) + newDuration = TimeUtils.posToBeat(itemRoot.width, tickSize, beatsDivision) + } + + if (showManager.setShowItemStartTime(sfRef, newStartTime) === true) + showManager.setShowItemDuration(sfRef, newDuration) + else + updateGeometry() + if (funcRef && showManager.stretchFunctions === true) funcRef.totalDuration = sfRef.duration @@ -346,6 +429,8 @@ Item } infoText = "" horLeftHandler.x = 0 + isDragging = false + updateGeometry() } } } @@ -373,6 +458,8 @@ Item drag.axis: Drag.XAxis drag.minimumX: horLeftHandler.x + width + onPressed: isDragging = true + onPositionChanged: { //var mp = mapToItem(itemRoot, mouseX, mouseY) @@ -384,7 +471,7 @@ Item itemRoot.width = obj.x + (horRightHdlMa.width - mouse.x) infoTextBox.height = itemRoot.height / 4 infoTextBox.textHAlign = Text.AlignRight - infoText = qsTr("Duration: ") + TimeUtils.msToString(TimeUtils.posToMs(itemRoot.width, timeScale, tickSize)) + updateTooltipText() } } onReleased: @@ -401,13 +488,24 @@ Item itemRoot.width = snappedEndPos - itemRoot.x } - sfRef.duration = TimeUtils.posToMs(itemRoot.width, timeScale, tickSize) + var newDuration + + if (timeDivision === Show.Time) + newDuration = TimeUtils.posToMs(itemRoot.width, timeScale, tickSize) + else + newDuration = (Math.round(itemRoot.width / (tickSize / beatsDivision)) * 1000) + + if (showManager.setShowItemDuration(sfRef, newDuration) === false) + updateGeometry() + if (funcRef && showManager.stretchFunctions === true) funcRef.totalDuration = sfRef.duration prCanvas.requestPaint() } infoText = "" + isDragging = false + updateGeometry() } } } diff --git a/qmlui/qml/showmanager/ShowManager.qml b/qmlui/qml/showmanager/ShowManager.qml index dc55e092e2..30eb51d2a5 100644 --- a/qmlui/qml/showmanager/ShowManager.qml +++ b/qmlui/qml/showmanager/ShowManager.qml @@ -64,7 +64,7 @@ Rectangle Rectangle { id: topBar - width: showMgrContainer.width + width: showMgrContainer.width - rightPanel.width height: UISettings.iconSizeDefault z: 5 gradient: Gradient @@ -88,6 +88,7 @@ Rectangle width: showMgrContainer.width / 5 height: parent.height - 10 text: showManager.showName + enabled: showManager.isEditing onTextChanged: showManager.showName = text } @@ -100,6 +101,7 @@ Rectangle height: width imgSource: "qrc:/color.svg" checkable: true + enabled: showManager.isEditing tooltip: qsTr("Show items color") onCheckedChanged: colTool.visible = !colTool.visible ColorTool @@ -112,6 +114,7 @@ Rectangle visible: false onColorChanged: showManager.itemsColor = Qt.rgba(r, g, b, 1.0) + onClose: colPickButton.toggle() } } @@ -192,7 +195,8 @@ Rectangle { var selNames = showManager.selectedItemNames() //console.log(selNames) - deleteItemsPopup.message = qsTr("Are you sure you want to remove the following items?\n(Note that the original functions will not be deleted)") + "\n" + selNames, + deleteItemsPopup.message = qsTr("Are you sure you want to remove the following items?\n" + + "(Note that the original functions will not be deleted)") + "\n" + selNames deleteItemsPopup.open() } @@ -226,9 +230,20 @@ Rectangle onClicked: showManager.pasteFromClipboard() } + // filler + Rectangle + { + Layout.fillWidth: true + } + RobotoText { id: timeBox + radius: height / 5 + border.color: UISettings.fgMedium + border.width: 1 + leftMargin: UISettings.textSizeDefault + rightMargin: UISettings.textSizeDefault property int currentTime: showManager.currentTime label: "00:00:00.00" @@ -247,6 +262,7 @@ Rectangle imgSource: "qrc:/play.svg" tooltip: qsTr("Play or resume") checkable: true + enabled: showManager.isEditing onToggled: { if (checked) @@ -263,6 +279,7 @@ Rectangle imgSource: "qrc:/stop.svg" tooltip: qsTr("Stop or rewind") checkable: false + enabled: showManager.isEditing onClicked: { playbackBtn.checked = false @@ -270,12 +287,60 @@ Rectangle } } + // filler Rectangle { Layout.fillWidth: true } + + RobotoText + { + label: qsTr("Markers") + } + + CustomComboBox + { + ListModel + { + id: divModel + ListElement { mLabel: qsTr("Time"); mValue: Show.Time } + ListElement { mLabel: qsTr("BPM 4/4"); mValue: Show.BPM_4_4 } + ListElement { mLabel: qsTr("BPM 3/4"); mValue: Show.BPM_3_4 } + ListElement { mLabel: qsTr("BPM 2/4"); mValue: Show.BPM_2_4 } + } + + model: divModel + enabled: showManager.isEditing + currValue: showManager.timeDivision + onValueChanged: showManager.timeDivision = currentValue + } + + ZoomItem + { + implicitWidth: UISettings.mediumItemHeight * 1.3 + implicitHeight: parent.height - 2 + fontColor: "#222" + + onZoomOutClicked: + { + if (showManager.timeScale >= 1.0) + showManager.timeScale += 1.0 + else + showManager.timeScale += 0.1 + centerView() + } + + onZoomInClicked: + { + if (showManager.timeScale > 1.0) + showManager.timeScale -= 1.0 + else + showManager.timeScale -= 0.1 + centerView() + } + } } - } + } // top bar RightPanel { @@ -309,20 +374,32 @@ Rectangle IconButton { - visible: showManager.selectedTrack > 0 ? true : false + visible: showManager.selectedTrackIndex > 0 ? true : false height: parent.height - 2 width: height imgSource: "qrc:/up.svg" tooltip: qsTr("Move the selected track up") + onClicked: + { + showManager.moveTrack(showManager.selectedTrackIndex, -1) + showManager.selectedTrackIndex-- + renderAndCenter() + } } IconButton { - visible: showManager.selectedTrack >= 0 ? true : false + visible: showManager.selectedTrackIndex < tracksBox.count - 1 ? true : false height: parent.height - 2 width: height imgSource: "qrc:/down.svg" tooltip: qsTr("Move the selected track down") + onClicked: + { + showManager.moveTrack(showManager.selectedTrackIndex, 1) + showManager.selectedTrackIndex++ + renderAndCenter() + } } // layout filler @@ -331,30 +408,6 @@ Rectangle Layout.fillWidth: true color: "transparent" } - ZoomItem - { - implicitWidth: UISettings.mediumItemHeight * 1.3 - implicitHeight: parent.height - 2 - fontColor: "#222" - - onZoomOutClicked: - { - if (showManager.timeScale >= 1.0) - showManager.timeScale += 1.0 - else - showManager.timeScale += 0.1 - centerView() - } - - onZoomInClicked: - { - if (showManager.timeScale > 1.0) - showManager.timeScale -= 1.0 - else - showManager.timeScale -= 0.1 - centerView() - } - } Rectangle { @@ -374,7 +427,7 @@ Rectangle y: topBar.height z: 4 height: showMgrContainer.headerHeight - width: showMgrContainer.width - trackWidth - rightPanel.width + width: showMgrContainer.width - trackWidth - verticalDivider.width - rightPanel.width boundsBehavior: Flickable.StopAtBounds flickableDirection: Flickable.HorizontalFlick @@ -397,7 +450,10 @@ Rectangle onClicked: { - showManager.currentTime = TimeUtils.posToMs(mouseX, timeScale, tickSize) + if (timeDivision === Show.Time) + showManager.currentTime = TimeUtils.posToMs(mouseX, timeScale, tickSize) + else + showManager.currentTime = TimeUtils.posToBeatMs(mouseX, tickSize, ioManager.bpmNumber, showManager.beatsDivision) showManager.resetItemsSelection() } } @@ -421,13 +477,13 @@ Rectangle contentHeight: totalTracksHeight > height ? totalTracksHeight : height //contentWidth: timelineHeader.contentWidth - property real totalTracksHeight: (tracksBox.count + 1) * trackHeight + property real totalTracksHeight: (tracksBox.count + 2) * trackHeight Rectangle { width: trackWidth height: parent.height - color: UISettings.bgMain + color: UISettings.bgMedium z: 2 Column @@ -447,7 +503,7 @@ Rectangle height: trackHeight trackRef: modelData trackIndex: index - isSelected: showManager.selectedTrack === index ? true : false + isSelected: showManager.selectedTrackIndex === index ? true : false } } } @@ -474,12 +530,13 @@ Rectangle z: 1 width: showMgrContainer.width - trackWidth height: parent.height + clip: true boundsBehavior: Flickable.StopAtBounds contentHeight: showContents.contentHeight contentWidth: timelineHeader.contentWidth contentX: xViewOffset - ScrollBar.horizontal: CustomScrollBar { orientation: Qt.Horizontal } + ScrollBar.horizontal: horScrollBar onContentXChanged: xViewOffset = contentX @@ -537,8 +594,12 @@ Rectangle if (drag.source.hasOwnProperty("fromFunctionManager")) { var trackIdx = (itemsArea.contentY + drag.y) / trackHeight - var fTime = TimeUtils.posToMs(itemsArea.contentX + drag.x, timeScale, tickSize) - console.log("Drop on time: " + fTime) + var fTime + if (showManager.timeDivision === Show.Time) + fTime = TimeUtils.posToMs(itemsArea.contentX + drag.x, timeScale, tickSize) + else + fTime = TimeUtils.posToBeat(itemsArea.contentX + drag.x, tickSize, showManager.beatsDivision) + console.log("Drop on time1: " + fTime) showManager.addItems(itemsArea.contentItem, trackIdx, fTime, drag.source.itemsList) } /* @@ -606,26 +667,45 @@ Rectangle /* Check if the dragging was started from a Function Manager */ if (drag.source.hasOwnProperty("fromFunctionManager")) { - var fTime = TimeUtils.posToMs(xViewOffset + drag.x, timeScale, tickSize) - console.log("Drop on time: " + fTime) + var fTime + + if (showManager.timeDivision === Show.Time) + fTime = TimeUtils.posToMs(xViewOffset + drag.x, timeScale, tickSize) + else + fTime = TimeUtils.posToBeat(xViewOffset + drag.x, tickSize, showManager.beatsDivision) + + console.log("Drop on time2: " + fTime) showManager.addItems(itemsArea.contentItem, -1, fTime, drag.source.itemsList) } } } } + } // Flickable (horizontal) + } // Flickable (vertical) + + Rectangle + { + anchors.centerIn: parent + width: parent.width / 3 + height: UISettings.bigItemHeight + visible: !showManager.isEditing + radius: height / 4 + color: UISettings.bgLight + + RobotoText + { + anchors.centerIn: parent + label: qsTr("Create/Edit a Show function or\ndrag a function on the timeline") } } -/* + CustomScrollBar { - id: horScrollbar - z: 4 + id: horScrollBar + x: timelineHeader.x + y: showMgrContainer.height - height + z: 10 + width: timelineHeader.width orientation: Qt.Horizontal - anchors.bottom: parent.bottom - x: trackWidth - flickable: timelineHeader } - - CustomScrollBar { z: 5; flickable: showContents; doubleBars: true } -*/ } diff --git a/qmlui/qml/showmanager/TrackDelegate.qml b/qmlui/qml/showmanager/TrackDelegate.qml index 5dca1465a7..be34b8ef39 100644 --- a/qmlui/qml/showmanager/TrackDelegate.qml +++ b/qmlui/qml/showmanager/TrackDelegate.qml @@ -34,13 +34,16 @@ Rectangle property int trackIndex property bool isSelected: false - RobotoText + CustomTextInput { x: 2 width: parent.width - 4 height: parent.height - label: trackRef ? trackRef.name : "" - wrapText: true + text: trackRef ? trackRef.name : "" + wrapMode: TextInput.Wrap + allowDoubleClick: true + + onTextConfirmed: if(trackRef) trackRef.name = text } Rectangle @@ -64,9 +67,7 @@ Rectangle imgSource: "" checkable: true tooltip: qsTr("Solo this track") - onToggled: - { - } + onToggled: showManager.setTrackSolo(trackIndex, checked) RobotoText { @@ -89,12 +90,11 @@ Rectangle height: parent.height * 0.3 bgColor: "#8191A0" checkedColor: "red" + checked: trackRef ? trackRef.mute : false imgSource: "" checkable: true tooltip: qsTr("Mute this track") - onToggled: - { - } + onToggled: if(trackRef) trackRef.mute = checked RobotoText { @@ -110,6 +110,11 @@ Rectangle MouseArea { anchors.fill: parent - onClicked: showManager.selectedTrack = trackIndex + propagateComposedEvents: true + onClicked: + { + showManager.selectedTrackIndex = trackIndex + mouse.accepted = false + } } } diff --git a/qmlui/qml/showmanager/qmldir b/qmlui/qml/showmanager/qmldir index 12b5c1ae43..955a9787ac 100644 --- a/qmlui/qml/showmanager/qmldir +++ b/qmlui/qml/showmanager/qmldir @@ -10,6 +10,7 @@ CustomComboBox 0.1 ../CustomComboBox.qml CustomScrollBar 0.1 ../CustomScrollBar.qml CustomSpinBox 0.1 ../CustomSpinBox.qml CustomTextEdit 0.1 ../CustomTextEdit.qml +CustomTextInput 0.1 ../CustomTextInput.qml DMXPercentageButton 0.1 ../DMXPercentageButton.qml FixtureConsole 0.1 ../FixtureConsole.qml FixtureDelegate 0.1 ../FixtureDelegate.qml 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/VCButtonItem.qml b/qmlui/qml/virtualconsole/VCButtonItem.qml index 4edbe24613..886fc1ae6f 100644 --- a/qmlui/qml/virtualconsole/VCButtonItem.qml +++ b/qmlui/qml/virtualconsole/VCButtonItem.qml @@ -29,6 +29,7 @@ VCWidgetItem property int btnState: buttonObj ? buttonObj.state : VCButton.Inactive property int btnAction: buttonObj ? buttonObj.actionType : VCButton.Toggle + property string activeColor: buttonObj.flashOverrides || buttonObj.flashForceLTP ? "#FF0000" : "#00FF00" radius: 4 @@ -76,7 +77,7 @@ VCWidgetItem height: parent.height - 2 color: "transparent" border.width: (buttonRoot.width > 80) ? 3 : 2 - border.color: btnState === VCButton.Active ? "#00FF00" : btnState === VCButton.Monitoring ? "orange" : "#A0A0A0" + border.color: btnState === VCButton.Active ? activeColor : btnState === VCButton.Monitoring ? "orange" : "#A0A0A0" radius: 3 Rectangle @@ -133,6 +134,7 @@ VCWidgetItem MouseArea { anchors.fill: parent + enabled: buttonObj && !buttonObj.isDisabled onClicked: { if (virtualConsole.editMode) @@ -169,6 +171,7 @@ VCWidgetItem MultiPointTouchArea { anchors.fill: parent + enabled: buttonObj && !buttonObj.isDisabled mouseEnabled: false maximumTouchPoints: 1 diff --git a/qmlui/qml/virtualconsole/VCButtonProperties.qml b/qmlui/qml/virtualconsole/VCButtonProperties.qml index e7dc99df87..e94207d15c 100644 --- a/qmlui/qml/virtualconsole/VCButtonProperties.qml +++ b/qmlui/qml/virtualconsole/VCButtonProperties.qml @@ -274,5 +274,52 @@ Rectangle } } + + SectionBox + { + id: flashProperties + visible: widgetRef ? widgetRef.actionType === VCButton.Flash : false + sectionLabel: qsTr("Flash properties") + + sectionContents: + RowLayout + { + width: parent.width + spacing: 10 + + RobotoText + { + id: flashOverrideLabel + height: UISettings.listItemHeight + label: qsTr("Override priority") + } + + CustomCheckBox + { + id: flashOverrideCheckBox + implicitWidth: UISettings.iconSizeMedium + implicitHeight: implicitWidth + checked: widgetRef.flashOverrides + onClicked: widgetRef.flashOverrides = checked + } + + RobotoText + { + id: flashForceLTPLabel + height: UISettings.listItemHeight + label: qsTr("Force LTP") + } + + CustomCheckBox + { + id: flashForceLTPCheckBox + implicitWidth: UISettings.iconSizeMedium + implicitHeight: implicitWidth + checked: widgetRef.flashForceLTP + onClicked: widgetRef.flashForceLTP = checked + } + } + + } } // Column } diff --git a/qmlui/qml/virtualconsole/VCClockProperties.qml b/qmlui/qml/virtualconsole/VCClockProperties.qml index 2ca32cc353..b21bd976fc 100644 --- a/qmlui/qml/virtualconsole/VCClockProperties.qml +++ b/qmlui/qml/virtualconsole/VCClockProperties.qml @@ -148,15 +148,16 @@ Rectangle { if (checked) { - rightSidePanel.width += mainView.width / 3 - sideLoader.width = mainView.width / 3 + if (!sideLoader.visible) + rightSidePanel.width += UISettings.sidePanelWidth + sideLoader.visible = true sideLoader.source = "qrc:/FunctionManager.qml" } else { - rightSidePanel.width = rightSidePanel.width - sideLoader.width + rightSidePanel.width -= sideLoader.width sideLoader.source = "" - sideLoader.width = 0 + sideLoader.visible = false } } } diff --git a/qmlui/qml/virtualconsole/VCCueListItem.qml b/qmlui/qml/virtualconsole/VCCueListItem.qml index 659bc1e261..f6179ace47 100644 --- a/qmlui/qml/virtualconsole/VCCueListItem.qml +++ b/qmlui/qml/virtualconsole/VCCueListItem.qml @@ -19,6 +19,7 @@ import QtQuick 2.0 import QtQuick.Layouts 1.1 +import QtQuick.Controls 2.14 import org.qlcplus.classes 1.0 @@ -26,10 +27,15 @@ VCWidgetItem { id: cueListRoot property VCCueList cueListObj: null + property int contentWidth: width - (sideFaderLayout.visible ? sideFaderLayout.width : 0) property int buttonsLayout: cueListObj ? cueListObj.playbackLayout : VCCueList.PlayPauseStop property int playbackStatus: cueListObj ? cueListObj.playbackStatus : VCCueList.Stopped property int sideFaderMode: cueListObj ? cueListObj.sideFaderMode : VCCueList.None + property int progressStatus: VCCueList.ProgressIdle + property real progressValue: 0 + property string progressText: "" + clip: true onCueListObjChanged: @@ -174,7 +180,7 @@ VCWidgetItem ColumnLayout { - width: parent.width - (sideFaderLayout.visible ? sideFaderLayout.width : 0) + width: contentWidth height: parent.height x: sideFaderLayout.visible ? sideFaderLayout.width : 0 spacing: 2 @@ -193,7 +199,9 @@ VCWidgetItem onIndexChanged: if (cueListObj) cueListObj.playbackIndex = index //onStepValueChanged: chaserEditor.setStepSpeed(index, value, type) + onNoteTextChanged: if (cueListObj) cueListObj.setStepNote(index, text) onAddFunctions: if (cueListObj) cueListObj.addFunctions(list, index) + onEnterPressed: if (cueListObj) cueListObj.playCurrentStep() states: [ State @@ -208,6 +216,40 @@ VCWidgetItem ] } + ProgressBar + { + id: progressBar + value: progressValue + //padding: 2 + + background: Rectangle { + implicitWidth: contentWidth + implicitHeight: UISettings.iconSizeMedium / 2 + color: UISettings.bgControl + radius: 3 + } + + contentItem: Item { + implicitWidth: contentWidth + implicitHeight: UISettings.iconSizeMedium / 2 + + Rectangle { + width: progressBar.visualPosition * parent.width + height: parent.height + radius: 2 + color: progressStatus == VCCueList.ProgressIdle ? "transparent" : + progressStatus == VCCueList.ProgressFadeIn ? "#477f07" : "#0f76c5"; + } + + RobotoText + { + anchors.centerIn: parent + fontSize: UISettings.textSizeDefault * 0.6 + label: progressText + } + } + } + Row { id: controlsRow @@ -216,8 +258,9 @@ VCWidgetItem IconButton { id: playbackBtn - width: cueListRoot.width / 4 + width: contentWidth / 4 height: UISettings.iconSizeMedium + enabled: visible && !cueListObj.isDisabled imgSource: (cueListObj && cueListObj.playbackLayout == VCCueList.PlayPauseStop) ? (cueListRoot.playbackStatus === VCCueList.Stopped || cueListRoot.playbackStatus === VCCueList.Paused ? "qrc:/play.svg" : "qrc:/pause.svg") : @@ -228,8 +271,9 @@ VCWidgetItem IconButton { id: stopBtn - width: cueListRoot.width / 4 + width: contentWidth / 4 height: UISettings.iconSizeMedium + enabled: visible && !cueListObj.isDisabled imgSource: (cueListObj && cueListObj.playbackLayout == VCCueList.PlayStopPause) ? "qrc:/pause.svg" : "qrc:/stop.svg" tooltip: (cueListObj && cueListObj.playbackLayout == VCCueList.PlayStopPause) ? qsTr("Pause") : qsTr("Stop") onClicked: if (cueListObj) cueListObj.stopClicked() @@ -237,8 +281,9 @@ VCWidgetItem IconButton { id: previousBtn - width: cueListRoot.width / 4 + width: contentWidth / 4 height: UISettings.iconSizeMedium + enabled: visible && !cueListObj.isDisabled imgSource: "qrc:/back.svg" tooltip: qsTr("Previous cue") onClicked: if (cueListObj) cueListObj.previousClicked() @@ -246,8 +291,9 @@ VCWidgetItem IconButton { id: nextBtn - width: cueListRoot.width / 4 + width: contentWidth / 4 height: UISettings.iconSizeMedium + enabled: visible && !cueListObj.isDisabled imgSource: "qrc:/forward.svg" tooltip: qsTr("Next cue") onClicked: if (cueListObj) cueListObj.nextClicked() diff --git a/qmlui/qml/virtualconsole/VCFrameItem.qml b/qmlui/qml/virtualconsole/VCFrameItem.qml index dab653b8db..ccad3f9b85 100644 --- a/qmlui/qml/virtualconsole/VCFrameItem.qml +++ b/qmlui/qml/virtualconsole/VCFrameItem.qml @@ -121,12 +121,11 @@ VCWidgetItem } // multi page controls - Rectangle + RowLayout { visible: frameObj ? frameObj.multiPageMode : false - width: 168 height: parent.height - color: "transparent" + spacing: 2 IconButton { @@ -139,22 +138,20 @@ VCWidgetItem imgMargins: 1 onClicked: frameObj.gotoPreviousPage() } - Rectangle + CustomComboBox { - x: parent.height + 2 - width: 100 + id: pageSelector + width: UISettings.bigItemHeight height: parent.height - radius: 3 - color: "black" - - Text + textRole: "" + model: frameObj ? frameObj.pageLabels : null + currentIndex: frameObj ? frameObj.currentPage : 0 + onCurrentIndexChanged: { - anchors.centerIn: parent - font.family: UISettings.robotoFontName - font.pixelSize: UISettings.textSizeDefault - font.bold: true - text: qsTr("Page") + " " + (frameObj ? frameObj.currentPage + 1 : "1") - color: "red" + if (frameObj) + frameObj.currentPage = currentIndex + // binding got broken, so restore it + currentIndex = Qt.binding(function() { return frameObj.currentPage }) } } IconButton @@ -188,11 +185,17 @@ VCWidgetItem onExited: frameRoot.dropActive = false onDropped: { - if (frameObj === null || frameRoot.dropActive === false) + if (frameObj == null || frameRoot.dropActive === false) return frameRoot.dropActive = false + if (drag.source.wObj === frameObj) + { + console.log("ERROR: Source and target are the same!!!") + return + } + var pos = drag.source.mapToItem(frameRoot, 0, 0) console.log("Item dropped in frame " + frameObj.id + " at pos " + pos) diff --git a/qmlui/qml/virtualconsole/VCFrameProperties.qml b/qmlui/qml/virtualconsole/VCFrameProperties.qml index bab2096fb3..b7f23d3b8c 100644 --- a/qmlui/qml/virtualconsole/VCFrameProperties.qml +++ b/qmlui/qml/virtualconsole/VCFrameProperties.qml @@ -153,10 +153,65 @@ Rectangle GenericButton { + enabled: widgetRef ? widgetRef.totalPagesNumber > 1 : false Layout.columnSpan: 2 Layout.fillWidth: true height: gridItemsHeight label: qsTr("Clone first page widgets") + onClicked: if (widgetRef) widgetRef.cloneFirstPage() + } + } + } + + SectionBox + { + sectionLabel: qsTr("Shortcuts") + visible: widgetRef ? widgetRef.totalPagesNumber > 1 : false + + sectionContents: + GridLayout + { + width: parent.width + columns: 2 + columnSpacing: 5 + rowSpacing: 3 + + // row 1 + RobotoText + { + height: gridItemsHeight + Layout.fillWidth: true + label: qsTr("Shortcuts") + } + CustomComboBox + { + id: shortcutList + Layout.fillWidth: true + height: gridItemsHeight + textRole: "" + model: widgetRef ? widgetRef.pageLabels : null + } + + // row 2 + RobotoText + { + height: gridItemsHeight + Layout.fillWidth: true + label: qsTr("Shortcut name") + } + CustomTextEdit + { + id: shortcutEdit + Layout.fillWidth: true + text: shortcutList.currentText + onTextChanged: + { + var idx = shortcutList.currentIndex + if (widgetRef) + widgetRef.setShortcutName(shortcutList.currentIndex, text) + // combo model has changed. Restore selected index + shortcutList.currentIndex = idx + } } } } diff --git a/qmlui/qml/virtualconsole/VCPageProperties.qml b/qmlui/qml/virtualconsole/VCPageProperties.qml index 6d3faacabf..54e0b45474 100644 --- a/qmlui/qml/virtualconsole/VCPageProperties.qml +++ b/qmlui/qml/virtualconsole/VCPageProperties.qml @@ -19,7 +19,6 @@ import QtQuick 2.0 import QtQuick.Layouts 1.1 -import QtQuick.Controls 1.2 import org.qlcplus.classes 1.0 import "." diff --git a/qmlui/qml/virtualconsole/VCRightPanel.qml b/qmlui/qml/virtualconsole/VCRightPanel.qml index 65c1525bd9..de25ca944d 100644 --- a/qmlui/qml/virtualconsole/VCRightPanel.qml +++ b/qmlui/qml/virtualconsole/VCRightPanel.qml @@ -158,7 +158,6 @@ SidePanel imgSource: "qrc:/edit-paste.svg" tooltip: qsTr("Paste widgets from clipboard") counter: virtualConsole.clipboardItemsCount - onClicked: virtualConsole.pasteFromClipboard() Rectangle diff --git a/qmlui/qml/virtualconsole/VCSliderItem.qml b/qmlui/qml/virtualconsole/VCSliderItem.qml index 2e8c8ec31d..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 @@ -111,7 +111,7 @@ VCWidgetItem height: UISettings.listItemHeight font: sliderObj ? sliderObj.font : "" text: sliderObj ? (sliderObj.valueDisplayStyle === VCSlider.DMXValue ? - sliderValue : parseInt((sliderValue * 100) / 255) + "%") : sliderValue + sliderValue : Math.round((sliderValue * 100.0) / 255.0) + "%") : sliderValue color: sliderObj ? sliderObj.foregroundColor : "white" } @@ -120,7 +120,7 @@ VCWidgetItem { id: slFader visible: sliderObj ? sliderObj.widgetStyle === VCSlider.WSlider : false - enabled: visible + enabled: visible && !sliderObj.isDisabled Layout.alignment: Qt.AlignHCenter Layout.fillHeight: true width: parent.width @@ -141,15 +141,15 @@ VCWidgetItem { id: slKnob visible: sliderObj ? sliderObj.widgetStyle === VCSlider.WKnob : false - enabled: visible + enabled: visible && !sliderObj.isDisabled Layout.alignment: Qt.AlignHCenter Layout.fillHeight: true - //width: parent.width + Layout.fillWidth: true from: sliderObj ? sliderObj.rangeLowLimit : 0 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() @@ -275,34 +293,41 @@ VCWidgetItem item.visible = !item.visible if (sliderObj && clickAndGoButton.cngType == VCSlider.CnGPreset) item.updatePresets(sliderObj.clickAndGoPresetsList) + item.parent = virtualConsole.currentPageItem() + var posInPage = clickAndGoButton.mapToItem(item.parent, 0, clickAndGoButton.height) + item.x = posInPage.x + item.y = posInPage.y } onLoaded: { - item.y = parent.height item.visible = false item.closeOnSelect = true + if (sliderObj && clickAndGoButton.cngType == VCSlider.CnGPreset) + { + item.rangeLowLimit = sliderObj.rangeLowLimit + item.rangeHighLimit = sliderObj.rangeHighLimit + } } Connections { ignoreUnknownSignals: true target: colorToolLoader.item - onColorChanged: + function onColorChanged(r, g, b, w, a, uv) { if (sliderObj) sliderObj.setClickAndGoColors(Qt.rgba(r, g, b, 1.0), Qt.rgba(w, a, uv, 1.0)) } - } - Connections - { - ignoreUnknownSignals: true - target: colorToolLoader.item - onPresetSelected: + function onPresetSelected(cap, fxID, chIdx, value) { if (sliderObj) sliderObj.setClickAndGoPresetValue(value) } + function onClose() + { + target.visible = false + } } } } diff --git a/qmlui/qml/virtualconsole/VCSliderProperties.qml b/qmlui/qml/virtualconsole/VCSliderProperties.qml index 06e176f901..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 @@ -299,16 +314,17 @@ Rectangle { if (checked) { - rightSidePanel.width += UISettings.sidePanelWidth - sideLoader.width = UISettings.sidePanelWidth + if (!sideLoader.visible) + rightSidePanel.width += UISettings.sidePanelWidth + sideLoader.visible = true sideLoader.modelProvider = widgetRef sideLoader.source = "qrc:/FixtureGroupManager.qml" } else { - rightSidePanel.width = rightSidePanel.width - sideLoader.width + rightSidePanel.width -= sideLoader.width sideLoader.source = "" - sideLoader.width = 0 + sideLoader.visible = false } } } 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/VCWidgetItem.qml b/qmlui/qml/virtualconsole/VCWidgetItem.qml index 8a01d0cd20..f00f5eef6e 100644 --- a/qmlui/qml/virtualconsole/VCWidgetItem.qml +++ b/qmlui/qml/virtualconsole/VCWidgetItem.qml @@ -66,11 +66,6 @@ Rectangle bgImage.anchors.margins = m } - function checkSnapping() - { - - } - function updateGeometry(d) { d.target = null @@ -84,6 +79,11 @@ Rectangle height = Math.round(height / snappingSize) * snappingSize } + if (height < UISettings.iconSizeMedium) + height = UISettings.iconSizeMedium + if (width < UISettings.iconSizeMedium) + width = UISettings.iconSizeMedium + wObj.geometry = Qt.rect(x, y, width, height) x = Qt.binding(function() { return wObj ? wObj.geometry.x : 0 }) @@ -190,7 +190,7 @@ Rectangle } onPositionChanged: { - if (drag.target === null) + if (drag.target == null) return; drag.maximumX = wRoot.width - handleSize drag.maximumY = wRoot.height - handleSize @@ -227,7 +227,7 @@ Rectangle } onPositionChanged: { - if (drag.target === null) + if (drag.target == null) return; drag.maximumY = wRoot.height - handleSize wRoot.width = trHandle.x + trHandle.width @@ -262,7 +262,7 @@ Rectangle } onPositionChanged: { - if (drag.target === null) + if (drag.target == null) return; wRoot.width = brHandle.x + brHandle.width wRoot.height = brHandle.y + brHandle.height @@ -295,7 +295,7 @@ Rectangle } onPositionChanged: { - if (drag.target === null) + if (drag.target == null) return; drag.maximumX = wRoot.width - handleSize wRoot.height = blHandle.y + blHandle.height diff --git a/qmlui/qml/virtualconsole/VCWidgetProperties.qml b/qmlui/qml/virtualconsole/VCWidgetProperties.qml index 996707cfe3..6c136022f8 100644 --- a/qmlui/qml/virtualconsole/VCWidgetProperties.qml +++ b/qmlui/qml/virtualconsole/VCWidgetProperties.qml @@ -20,8 +20,7 @@ import QtQuick 2.0 import QtQuick.Layouts 1.1 import QtQuick.Dialogs 1.1 -import QtQuick.Controls 1.2 as QC1 -import QtQuick.Controls 2.1 +import QtQuick.Controls 2.13 import org.qlcplus.classes 1.0 import "." @@ -43,9 +42,10 @@ Rectangle wPropsLoader.active = false wPropsLoader.source = wObj ? wObj.propertiesResource : "" wPropsLoader.active = true - rightSidePanel.width = rightSidePanel.width - sideLoader.width + if (sideLoader.visible) + rightSidePanel.width -= sideLoader.width sideLoader.source = "" - sideLoader.width = 0 + sideLoader.visible = false } onSelectedWidgetsCountChanged: @@ -73,6 +73,7 @@ Rectangle else virtualConsole.setWidgetsBackgroundColor(Qt.rgba(r, g, b, 1.0)) } + onClose: visible = false } ColorTool @@ -92,16 +93,19 @@ Rectangle else virtualConsole.setWidgetsForegroundColor(Qt.rgba(r, g, b, 1.0)) } + onClose: visible = false } - QC1.SplitView + SplitView { anchors.fill: parent + Loader { id: sideLoader - visible: width - width: 0 + width: UISettings.sidePanelWidth + SplitView.preferredWidth: UISettings.sidePanelWidth + visible: false height: wPropsRoot.height source: "" @@ -125,7 +129,7 @@ Rectangle Rectangle { - Layout.fillWidth: true + SplitView.fillWidth: true height: wPropsRoot.height color: "transparent" 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/VirtualConsole.qml b/qmlui/qml/virtualconsole/VirtualConsole.qml index 85ac157b61..aba2fa50ae 100644 --- a/qmlui/qml/virtualconsole/VirtualConsole.qml +++ b/qmlui/qml/virtualconsole/VirtualConsole.qml @@ -38,6 +38,7 @@ Rectangle onDocLoadedChanged: { // force a reload of the selected page + virtualConsole.selectedPage = 0 pageLoader.active = false pageLoader.active = true } @@ -158,8 +159,10 @@ Rectangle else { pinErrorPopup.open() + // invalidate page selection so nothing + // is displayed on screen + virtualConsole.selectedPage = -1 } - } } @@ -195,8 +198,8 @@ Rectangle onZoomInClicked: { virtualConsole.setPageScale(0.1) } } } - } + } Loader { id: pageLoader @@ -214,6 +217,7 @@ Rectangle } } + CustomPopupDialog { id: addMatrixPopup diff --git a/qmlui/qml/virtualconsole/WidgetDragItem.qml b/qmlui/qml/virtualconsole/WidgetDragItem.qml index 4c6e827c35..b28aff7b5c 100644 --- a/qmlui/qml/virtualconsole/WidgetDragItem.qml +++ b/qmlui/qml/virtualconsole/WidgetDragItem.qml @@ -49,7 +49,7 @@ Rectangle GradientStop { position: 1 ; color: "#222" } } border.width: 2 - border.color: "#111" + border.color: UISettings.borderColorDark x: 5 y: 2 diff --git a/qmlui/qml/virtualconsole/WidgetsList.qml b/qmlui/qml/virtualconsole/WidgetsList.qml index 3ea74969eb..941ac4eefc 100644 --- a/qmlui/qml/virtualconsole/WidgetsList.qml +++ b/qmlui/qml/virtualconsole/WidgetsList.qml @@ -105,7 +105,7 @@ Rectangle width: parent.width - 6 height: 1 y: parent.height - 1 - color: "#555" + color: UISettings.bgLight visible: widgetItem.reduced ? false : true } } // WidgetDragItem 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 a65d267ad4..9eec79c66b 100644 --- a/qmlui/qmlui.pro +++ b/qmlui/qmlui.pro @@ -20,9 +20,17 @@ INCLUDEPATH += virtualconsole INCLUDEPATH += fixtureeditor INCLUDEPATH += tardis 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 @@ -42,6 +50,7 @@ HEADERS += \ functionmanager.h \ importmanager.h \ inputoutputmanager.h \ + inputprofileeditor.h \ listmodel.h \ mainview2d.h \ mainview3d.h \ @@ -56,6 +65,7 @@ HEADERS += \ simpledesk.h \ treemodel.h \ treemodelitem.h \ + uimanager.h \ videoeditor.h \ videoprovider.h @@ -75,6 +85,7 @@ SOURCES += main.cpp \ functionmanager.cpp \ importmanager.cpp \ inputoutputmanager.cpp \ + inputprofileeditor.cpp \ listmodel.cpp \ mainview2d.cpp \ mainview3d.cpp \ @@ -89,6 +100,7 @@ SOURCES += main.cpp \ simpledesk.cpp \ treemodel.cpp \ treemodelitem.cpp \ + uimanager.cpp \ videoeditor.cpp \ videoprovider.cpp @@ -121,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 @@ -133,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 5b94450eaf..e0c168ee2b 100644 --- a/qmlui/qmlui.qrc +++ b/qmlui/qmlui.qrc @@ -21,11 +21,11 @@ qml/CustomSpinBox.qml qml/CustomDoubleSpinBox.qml qml/CustomTextEdit.qml + qml/CustomTextInput.qml qml/DayTimeTool.qml qml/DMXAddressTool.qml qml/DMXAddressWidget.qml qml/DMXPercentageButton.qml - qml/EditableTextBox.qml qml/ExternalControls.qml qml/ExternalControlDelegate.qml qml/FixtureConsole.qml @@ -54,6 +54,7 @@ qml/TimeEditTool.qml qml/TreeNodeDelegate.qml qml/UISettings.qml + qml/UISettingsEditor.qml qml/UsageList.qml qml/WidgetDelegate.qml qml/WindowLoader.qml @@ -62,6 +63,8 @@ qml/popup/CustomPopupDialog.qml qml/popup/PopupAbout.qml + qml/popup/PopupChannelModifiers.qml + qml/popup/PopupChannelWizard.qml qml/popup/PopupCreatePalette.qml qml/popup/PopupDisclaimer.qml qml/popup/PopupImportProject.qml @@ -74,6 +77,7 @@ qml/popup/PopupNetworkConnect.qml qml/popup/PopupNetworkServer.qml qml/popup/PopupManualInputSource.qml + qml/popup/PopupInputChannelEditor.qml qml/fixturesfunctions/FixturesAndFunctions.qml @@ -207,6 +211,7 @@ qml/inputoutput/AudioDeviceItem.qml qml/inputoutput/PluginsList.qml qml/inputoutput/ProfilesList.qml + qml/inputoutput/InputProfileEditor.qml qml/inputoutput/PluginDragItem.qml qml/inputoutput/OutputPatchItem.qml qml/inputoutput/InputPatchItem.qml @@ -228,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/rgbmatrixeditor.cpp b/qmlui/rgbmatrixeditor.cpp index 6865f19678..c2d6cd012a 100644 --- a/qmlui/rgbmatrixeditor.cpp +++ b/qmlui/rgbmatrixeditor.cpp @@ -125,7 +125,7 @@ void RGBMatrixEditor::setAlgorithmIndex(int algoIndex) { qDebug() << "Set algorithm:" << algoIndex; QStringList algoList = algorithms(); - if(algoIndex < 0 || algoIndex >= algorithms().count()) + if (algoIndex < 0 || algoIndex >= algorithms().count()) return; RGBAlgorithm *algo = RGBAlgorithm::algorithm(m_doc, algoList.at(algoIndex)); @@ -408,11 +408,12 @@ void RGBMatrixEditor::createScriptObjects(QQuickItem *parent) RGBScript *script = static_cast (m_matrix->algorithm()); QList properties = script->properties(); - foreach(RGBScriptProperty prop, properties) + foreach (RGBScriptProperty prop, properties) { // always create a label first QMetaObject::invokeMethod(parent, "addLabel", Q_ARG(QVariant, prop.m_displayName)); + QString pValue = m_matrix->property(prop.m_name); switch(prop.m_type) { @@ -421,9 +422,8 @@ void RGBMatrixEditor::createScriptObjects(QQuickItem *parent) QVariantList valList; int idx = 0; int currIdx = 0; - QString pValue = m_matrix->property(prop.m_name); - foreach(QString val, prop.m_listValues) + foreach (QString val, prop.m_listValues) { if (val == pValue) currIdx = idx; @@ -443,8 +443,6 @@ void RGBMatrixEditor::createScriptObjects(QQuickItem *parent) break; case RGBScriptProperty::Range: { - QString pValue = m_matrix->property(prop.m_name); - QMetaObject::invokeMethod(parent, "addSpinBox", Q_ARG(QVariant, prop.m_name), Q_ARG(QVariant, prop.m_rangeMinValue), @@ -452,6 +450,20 @@ void RGBMatrixEditor::createScriptObjects(QQuickItem *parent) Q_ARG(QVariant, pValue.toInt())); } break; + case RGBScriptProperty::Float: + { + QMetaObject::invokeMethod(parent, "addDoubleSpinBox", + Q_ARG(QVariant, prop.m_name), + Q_ARG(QVariant, pValue.toDouble())); + } + break; + case RGBScriptProperty::String: + { + QMetaObject::invokeMethod(parent, "addTextEdit", + Q_ARG(QVariant, prop.m_name), + Q_ARG(QVariant, pValue)); + } + break; default: qWarning() << "Type" << prop.m_type << "not handled yet"; break; @@ -487,6 +499,20 @@ void RGBMatrixEditor::setScriptIntProperty(QString paramName, int value) m_matrix->setProperty(paramName, QString::number(value)); } +void RGBMatrixEditor::setScriptFloatProperty(QString paramName, double value) +{ + if (m_matrix == nullptr || m_matrix->algorithm() == nullptr || + m_matrix->algorithm()->type() != RGBAlgorithm::Script) + return; + + qDebug() << "[setScriptIntProperty] param:" << paramName << ", value:" << value; + + StringDoublePair oldValue(paramName, m_matrix->property(paramName).toDouble()); + Tardis::instance()->enqueueAction(Tardis::RGBMatrixSetScriptDoubleValue, m_matrix->id(), QVariant::fromValue(oldValue), + QVariant::fromValue(StringDoublePair(paramName, value))); + m_matrix->setProperty(paramName, QString::number(value)); +} + /************************************************************************ * Blend mode ************************************************************************/ @@ -598,7 +624,7 @@ void RGBMatrixEditor::slotPreviewTimeout() return; QMapIterator it(m_group->headsMap()); - while(it.hasNext()) + while (it.hasNext()) { it.next(); diff --git a/qmlui/rgbmatrixeditor.h b/qmlui/rgbmatrixeditor.h index f530a3e7a1..e78e60731a 100644 --- a/qmlui/rgbmatrixeditor.h +++ b/qmlui/rgbmatrixeditor.h @@ -125,6 +125,7 @@ class RGBMatrixEditor : public FunctionEditor Q_INVOKABLE void setScriptStringProperty(QString paramName, QString value); Q_INVOKABLE void setScriptIntProperty(QString paramName, int value); + Q_INVOKABLE void setScriptFloatProperty(QString paramName, double value); signals: void algorithmIndexChanged(); diff --git a/qmlui/sceneeditor.cpp b/qmlui/sceneeditor.cpp index 045fb82dff..1f3ca2bd50 100644 --- a/qmlui/sceneeditor.cpp +++ b/qmlui/sceneeditor.cpp @@ -108,7 +108,7 @@ void SceneEditor::setPreviewEnabled(bool enable) if (enable == true) { - foreach(SceneValue sv, m_scene->values()) + foreach (SceneValue sv, m_scene->values()) m_source->set(sv.fxi, sv.channel, sv.value); } else @@ -147,7 +147,7 @@ void SceneEditor::registerFixtureConsole(int index, QQuickItem *item) QVariantList dmxValues; QByteArray values = m_channelsCache[fixtureID]; - for (int i = 0; i < values.count(); i++) + for (int i = 0; i < values.length(); i++) dmxValues.append(QString::number((uchar)values.at(i))); item->setProperty("values", QVariant::fromValue(dmxValues)); @@ -194,17 +194,22 @@ void SceneEditor::slotSceneValueChanged(SceneValue scv) { connect(fixture, SIGNAL(aliasChanged()), this, SLOT(slotAliasChanged())); + // Add the fixture to the side panel list QVariantMap fxMap; fxMap.insert("cRef", QVariant::fromValue(fixture)); fxMap.insert("isSelected", false); m_fixtureList->addDataMap(fxMap); m_fixtureIDs.append(scv.fxi); + + // update the fixture list for the UI updateLists(); } + // update the channel values cache + setCacheChannelValue(scv); + if (m_sceneConsole) { - if (m_fxConsoleMap.contains(fxIndex)) { QMetaObject::invokeMethod(m_fxConsoleMap[fxIndex], "setChannelValue", @@ -266,20 +271,21 @@ void SceneEditor::addComponent(int type, quint32 id) switch(type) { case App::UniverseDragItem: - { - // TODO - } break; case App::FixtureGroupDragItem: + Tardis::instance()->enqueueAction(Tardis::SceneAddFixtureGroup, m_scene->id(), QVariant(), id); m_scene->addFixtureGroup(id); + m_doc->setModified(); break; case App::FixtureDragItem: + Tardis::instance()->enqueueAction(Tardis::SceneAddFixture, m_scene->id(), QVariant(), id); m_scene->addFixture(id); + m_doc->setModified(); break; case App::PaletteDragItem: - { + Tardis::instance()->enqueueAction(Tardis::SceneAddPalette, m_scene->id(), QVariant(), id); m_scene->addPalette(id); - } + m_doc->setModified(); break; default: break; @@ -290,10 +296,10 @@ void SceneEditor::addComponent(int type, quint32 id) void SceneEditor::deleteItems(QVariantList list) { - if (m_scene == nullptr) + if (m_scene == nullptr || list.isEmpty()) return; - for (QVariant vIdx : list) + for (QVariant &vIdx : list) { int index = vIdx.toInt(); QVariantMap dataMap = m_componentList->itemAt(index).toMap(); @@ -304,16 +310,28 @@ void SceneEditor::deleteItems(QVariantList list) case App::FixtureDragItem: { Fixture *fixture = dataMap["cRef"].value(); - qDebug() << "removing fixture with ID" << fixture->id(); - // TODO: tardis - m_scene->removeFixture(fixture->id()); + quint32 fixtureID = fixture->id(); + qDebug() << "removing fixture with ID" << fixtureID; + + for (SceneValue &scv : m_scene->values()) + { + if (scv.fxi == fixtureID) + { + QVariant currentVal; + currentVal.setValue(scv); + Tardis::instance()->enqueueAction(Tardis::SceneUnsetChannelValue, m_scene->id(), currentVal, QVariant()); + m_scene->unsetValue(fixtureID, scv.channel); + } + } + Tardis::instance()->enqueueAction(Tardis::SceneRemoveFixture, m_scene->id(), fixtureID, QVariant()); + m_scene->removeFixture(fixtureID); } break; case App::FixtureGroupDragItem: { FixtureGroup *group = dataMap["cRef"].value(); qDebug() << "removing fixture group with ID" << group->id(); - // TODO: tardis + Tardis::instance()->enqueueAction(Tardis::SceneRemoveFixtureGroup, m_scene->id(), group->id(), QVariant()); m_scene->removeFixtureGroup(group->id()); } break; @@ -321,13 +339,15 @@ void SceneEditor::deleteItems(QVariantList list) { QLCPalette *palette = dataMap["cRef"].value(); qDebug() << "removing palette with ID" << palette->id(); - // TODO: tardis + Tardis::instance()->enqueueAction(Tardis::SceneRemovePalette, m_scene->id(), palette->id(), QVariant()); m_scene->removePalette(palette->id()); } break; } } + m_doc->setModified(); + updateLists(); } diff --git a/qmlui/showmanager.cpp b/qmlui/showmanager.cpp index 531491a6ec..d071085ff1 100644 --- a/qmlui/showmanager.cpp +++ b/qmlui/showmanager.cpp @@ -31,16 +31,17 @@ ShowManager::ShowManager(QQuickView *view, Doc *doc, QObject *parent) : PreviewContext(view, doc, "SHOWMGR", parent) , m_currentShow(nullptr) - , m_timeScale(5.0) , m_stretchFunctions(false) , m_gridEnabled(false) + , m_timeScale(5.0) , m_currentTime(0) - , m_selectedTrack(-1) + , m_selectedTrackIndex(-1) , m_itemsColor(Qt::gray) { view->rootContext()->setContextProperty("showManager", this); + qmlRegisterUncreatableType("org.qlcplus.classes", 1, 0, "Show", "Can't create a Show"); qmlRegisterType("org.qlcplus.classes", 1, 0, "Track"); - qmlRegisterType("org.qlcplus.classes", 1, 0, "ShowFunction"); + qmlRegisterUncreatableType("org.qlcplus.classes", 1, 0, "ShowFunction", "Can't create a ShowFunction"); setContextResource("qrc:/ShowManager.qml"); setContextTitle(tr("Show Manager")); @@ -57,9 +58,20 @@ int ShowManager::currentShowID() const { if (m_currentShow == nullptr) return Function::invalidId(); + return m_currentShow->id(); } +Show *ShowManager::currentShow() const +{ + return m_currentShow; +} + +bool ShowManager::isEditing() +{ + return m_currentShow == nullptr ? false : true; +} + void ShowManager::setCurrentShowID(int currentShowID) { if (m_currentShow != nullptr) @@ -71,6 +83,8 @@ void ShowManager::setCurrentShowID(int currentShowID) m_currentShow = qobject_cast(m_doc->function(currentShowID)); emit currentShowIDChanged(currentShowID); + emit isEditingChanged(); + if (m_currentShow != nullptr) { connect(m_currentShow, SIGNAL(timeChanged(quint32)), this, SLOT(slotTimeChanged(quint32))); @@ -104,27 +118,77 @@ void ShowManager::setShowName(QString showName) emit showNameChanged(showName); } -QVariant ShowManager::tracks() +bool ShowManager::stretchFunctions() const { - m_tracksList.clear(); - if (m_currentShow) - m_tracksList = m_currentShow->tracks(); + return m_stretchFunctions; +} - return QVariant::fromValue(m_tracksList); +void ShowManager::setStretchFunctions(bool stretchFunctions) +{ + if (m_stretchFunctions == stretchFunctions) + return; + + m_stretchFunctions = stretchFunctions; + emit stretchFunctionsChanged(stretchFunctions); } -int ShowManager::selectedTrack() const +bool ShowManager::gridEnabled() const { - return m_selectedTrack; + return m_gridEnabled; } -void ShowManager::setSelectedTrack(int selectedTrack) +void ShowManager::setGridEnabled(bool gridEnabled) { - if (m_selectedTrack == selectedTrack) + if (m_gridEnabled == gridEnabled) return; - m_selectedTrack = selectedTrack; - emit selectedTrackChanged(selectedTrack); + m_gridEnabled = gridEnabled; + emit gridEnabledChanged(m_gridEnabled); +} + +/********************************************************************* + * Time + ********************************************************************/ + +Show::TimeDivision ShowManager::timeDivision() +{ + if (m_currentShow == nullptr) + return Show::Time; + + return m_currentShow->timeDivisionType(); +} + +void ShowManager::setTimeDivision(Show::TimeDivision division) +{ + if (m_currentShow == nullptr) + return; + + if (division == m_currentShow->timeDivisionType()) + return; + + if (division == Show::Time) + { + setTimeScale(5.0); + m_currentShow->setTempoType(Function::Time); + } + else + { + setTimeScale(1.0); + m_currentShow->setTempoType(Function::Beats); + } + m_currentShow->setTimeDivisionType(division); + emit timeDivisionChanged(division); + + if (division != Show::Time) + emit beatsDivisionChanged(m_currentShow->beatsDivision()); +} + +int ShowManager::beatsDivision() +{ + if (m_currentShow == nullptr) + return 0; + + return m_currentShow->beatsDivision(); } float ShowManager::timeScale() const @@ -138,6 +202,12 @@ void ShowManager::setTimeScale(float timeScale) return; m_timeScale = timeScale; + float tickScale = timeDivision() == Show::Time ? 1.0 : timeScale; + + App *app = qobject_cast(m_view); + m_tickSize = app->pixelDensity() * (18 * tickScale); + + emit tickSizeChanged(m_tickSize); emit timeScaleChanged(timeScale); } @@ -146,32 +216,74 @@ float ShowManager::tickSize() const return m_tickSize; } -bool ShowManager::stretchFunctions() const +int ShowManager::currentTime() const { - return m_stretchFunctions; + return m_currentTime; } -void ShowManager::setStretchFunctions(bool stretchFunctions) +void ShowManager::setCurrentTime(int currentTime) { - if (m_stretchFunctions == stretchFunctions) + if (m_currentTime == currentTime) return; - m_stretchFunctions = stretchFunctions; - emit stretchFunctionsChanged(stretchFunctions); + m_currentTime = currentTime; + emit currentTimeChanged(currentTime); } -bool ShowManager::gridEnabled() const +/********************************************************************* + * Tracks + ********************************************************************/ + +QVariant ShowManager::tracks() { - return m_gridEnabled; + m_tracksList.clear(); + if (m_currentShow) + m_tracksList = m_currentShow->tracks(); + + return QVariant::fromValue(m_tracksList); } -void ShowManager::setGridEnabled(bool gridEnabled) +int ShowManager::selectedTrackIndex() const { - if (m_gridEnabled == gridEnabled) + return m_selectedTrackIndex; +} + +void ShowManager::setSelectedTrackIndex(int index) +{ + if (m_selectedTrackIndex == index) return; - m_gridEnabled = gridEnabled; - emit gridEnabledChanged(m_gridEnabled); + m_selectedTrackIndex = index; + emit selectedTrackIndexChanged(index); +} + +void ShowManager::setTrackSolo(int index, bool solo) +{ + QList tracks = m_currentShow->tracks(); + + if (index < 0 || index >= tracks.count()) + return; + + for (int i = 0; i < tracks.count(); i++) + { + if (i == index) + tracks.at(i)->setMute(false); + else + tracks.at(i)->setMute(solo); + } +} + +void ShowManager::moveTrack(int index, int direction) +{ + QList tracks = m_currentShow->tracks(); + + if (index < 0 || index >= tracks.count()) + return; + + m_currentShow->moveTrack(tracks.at(index), direction); + m_doc->setModified(); + + emit tracksChanged(); } /********************************************************************* @@ -196,9 +308,14 @@ void ShowManager::addItems(QQuickItem *parent, int trackIdx, int startTime, QVar m_currentShow = nullptr; return; } - connect(m_currentShow,SIGNAL(timeChanged(quint32)), this, SLOT(slotTimeChanged(quint32))); + + Tardis::instance()->enqueueAction(Tardis::FunctionCreate, m_currentShow->id(), QVariant(), + Tardis::instance()->actionToByteArray(Tardis::FunctionCreate, m_currentShow->id())); + + connect(m_currentShow, SIGNAL(timeChanged(quint32)), this, SLOT(slotTimeChanged(quint32))); emit currentShowIDChanged(m_currentShow->id()); emit showNameChanged(m_currentShow->name()); + emit isEditingChanged(); } Track *selectedTrack = nullptr; @@ -206,9 +323,14 @@ void ShowManager::addItems(QQuickItem *parent, int trackIdx, int startTime, QVar // if no Track index is provided, then add a new one if (trackIdx == -1) { - selectedTrack = new Track(); + selectedTrack = new Track(Function::invalidId(), m_currentShow); selectedTrack->setName(tr("Track %1").arg(m_currentShow->tracks().count() + 1)); m_currentShow->addTrack(selectedTrack); + + Tardis::instance()->enqueueAction( + Tardis::ShowManagerAddTrack, m_currentShow->id(), QVariant(), + Tardis::instance()->actionToByteArray(Tardis::ShowManagerAddTrack, m_currentShow->id(), selectedTrack->id())); + trackIdx = m_currentShow->tracks().count() - 1; emit tracksChanged(); } @@ -222,7 +344,7 @@ void ShowManager::addItems(QQuickItem *parent, int trackIdx, int startTime, QVar selectedTrack = m_currentShow->tracks().at(trackIdx); } - for (QVariant vID : idsList) // C++11 + for (QVariant &vID : idsList) // C++11 { quint32 functionID = vID.toUInt(); if (functionID == m_currentShow->id()) @@ -237,10 +359,26 @@ void ShowManager::addItems(QQuickItem *parent, int trackIdx, int startTime, QVar continue; ShowFunction *showFunc = selectedTrack->createShowFunction(functionID); + + if (timeDivision() == Show::Time) + { + func->setTempoType(Function::Time); + showFunc->setDuration(func->totalDuration() ? func->totalDuration() : 5000); + } + else + { + func->setTempoType(Function::Beats); + if (func->type() == Function::AudioType || func->type() == Function::VideoType) + func->setTotalDuration(func->duration()); + showFunc->setDuration(func->totalDuration() ? func->totalDuration() : 4000); + } showFunc->setStartTime(startTime); - showFunc->setDuration(func->totalDuration() ? func->totalDuration() : 5000); showFunc->setColor(ShowFunction::defaultColor(func->type())); + Tardis::instance()->enqueueAction( + Tardis::ShowManagerAddFunction, m_currentShow->id(), QVariant(), + Tardis::instance()->actionToByteArray(Tardis::ShowManagerAddFunction, m_currentShow->id(), showFunc->id())); + QQuickItem *newItem = qobject_cast(siComponent->create()); newItem->setParentItem(parent); @@ -248,23 +386,35 @@ void ShowManager::addItems(QQuickItem *parent, int trackIdx, int startTime, QVar newItem->setProperty("sfRef", QVariant::fromValue(showFunc)); newItem->setProperty("funcRef", QVariant::fromValue(func)); - quint32 itemIndex = m_itemsMap.isEmpty() ? 0 : m_itemsMap.lastKey() + 1; - quint32 itemID = trackIdx << 16 | itemIndex; - m_itemsMap[itemID] = newItem; + m_itemsMap[showFunc->id()] = newItem; startTime += showFunc->duration(); } emit showDurationChanged(m_currentShow->totalDuration()); } +void ShowManager::addShowItem(ShowFunction *sf, quint32 trackId) +{ + QQuickItem *itemsArea = qobject_cast(m_view->rootObject()->findChild("showItemsArea")); + QQuickItem *contentItem = qobject_cast(itemsArea->findChild("contentItem")); + QQuickItem *newItem = qobject_cast(siComponent->create()); + Function *func = m_doc->function(sf->functionID()); + + newItem->setParentItem(contentItem); + newItem->setProperty("trackIndex", trackId); + newItem->setProperty("sfRef", QVariant::fromValue(sf)); + newItem->setProperty("funcRef", QVariant::fromValue(func)); + m_itemsMap[sf->id()] = newItem; +} + void ShowManager::deleteShowItems(QVariantList data) { - Q_UNUSED(data) + Q_UNUSED(data); if (m_currentShow == nullptr) return; - foreach(SelectedShowItem ssi, m_selectedItems) + foreach (SelectedShowItem ssi, m_selectedItems) { quint32 trackIndex = ssi.m_trackIndex; qDebug() << "Selected item has track index:" << trackIndex; @@ -277,12 +427,11 @@ void ShowManager::deleteShowItems(QVariantList data) } Track *track = m_currentShow->tracks().at(trackIndex); + quint32 sfId = ssi.m_showFunc->id(); track->removeShowFunction(ssi.m_showFunc, true); if (ssi.m_item != nullptr) { - quint32 key = m_itemsMap.key(ssi.m_item, UINT_MAX); - if (key != UINT_MAX) - m_itemsMap.remove(key); + m_itemsMap.remove(sfId); delete ssi.m_item; } } @@ -291,6 +440,17 @@ void ShowManager::deleteShowItems(QVariantList data) emit selectedItemsCountChanged(0); } +void ShowManager::deleteShowItem(ShowFunction *sf) +{ + quint32 sfId = sf->id(); + QQuickItem *item = m_itemsMap.value(sfId, nullptr); + if (item != nullptr) + { + m_itemsMap.remove(sfId); + delete item; + } +} + bool ShowManager::checkAndMoveItem(ShowFunction *sf, int originalTrackIdx, int newTrackIdx, int newStartTime) { if (m_currentShow == nullptr || sf == nullptr) @@ -304,7 +464,7 @@ bool ShowManager::checkAndMoveItem(ShowFunction *sf, int originalTrackIdx, int n if (newTrackIdx >= m_currentShow->tracks().count()) { // create a new track here - dstTrack = new Track(); + dstTrack = new Track(Function::invalidId(), m_currentShow); dstTrack->setName(tr("Track %1").arg(m_currentShow->tracks().count() + 1)); m_currentShow->addTrack(dstTrack); emit tracksChanged(); @@ -318,21 +478,23 @@ bool ShowManager::checkAndMoveItem(ShowFunction *sf, int originalTrackIdx, int n return false; } + int newTime = newStartTime; + if (m_gridEnabled) { // calculate the X position from time and time scale - float xPos = ((float)newStartTime * m_tickSize) / (m_timeScale * 1000.0); // timescale * 1000 : tickSize = time : x + // timescale * 1000 : tickSize = time : x + float xPos = ((float)newStartTime * m_tickSize) / (m_timeScale * 1000.0); // round to the nearest snap position xPos = qRound(xPos / m_tickSize) * m_tickSize; // recalculate the time from pixels - float time = xPos * (1000 * m_timeScale) / m_tickSize; // xPos : time = tickSize : timescale * 1000 - sf->setStartTime(time); - } - else - { - sf->setStartTime(newStartTime); + // xPos : time = tickSize : timescale * 1000 + newTime = xPos * (1000 * m_timeScale) / m_tickSize; } + Tardis::instance()->enqueueAction(Tardis::ShowManagerItemSetStartTime, sf->id(), sf->startTime(), newTime); + sf->setStartTime(newTime); + // check if we need to move the ShowFunction to a different Track if (newTrackIdx != originalTrackIdx) { @@ -346,11 +508,49 @@ bool ShowManager::checkAndMoveItem(ShowFunction *sf, int originalTrackIdx, int n return true; } +bool ShowManager::setShowItemStartTime(ShowFunction *sf, int startTime) +{ + if (sf == nullptr) + return false; + + Track *track = m_currentShow->getTrackFromShowFunctionID(sf->id()); + if (track == nullptr) + return false; + + bool overlapping = checkOverlapping(track, sf, startTime, sf->duration()); + if (overlapping) + return false; + + Tardis::instance()->enqueueAction(Tardis::ShowManagerItemSetStartTime, sf->id(), sf->startTime(), startTime); + sf->setStartTime(startTime); + + return true; +} + +bool ShowManager::setShowItemDuration(ShowFunction *sf, int duration) +{ + if (sf == nullptr) + return false; + + Track *track = m_currentShow->getTrackFromShowFunctionID(sf->id()); + if (track == nullptr) + return false; + + bool overlapping = checkOverlapping(track, sf, sf->startTime(), duration); + if (overlapping) + return false; + + Tardis::instance()->enqueueAction(Tardis::ShowManagerItemSetDuration, sf->id(), sf->duration(), duration); + sf->setDuration(duration); + + return true; +} + void ShowManager::resetContents() { resetView(); m_currentTime = 0; - m_selectedTrack = -1; + m_selectedTrackIndex = -1; emit currentTimeChanged(m_currentTime); m_currentShow = nullptr; } @@ -358,7 +558,7 @@ void ShowManager::resetContents() void ShowManager::resetView() { QMapIterator it(m_itemsMap); - while(it.hasNext()) + while (it.hasNext()) { it.next(); delete it.value(); @@ -377,11 +577,9 @@ void ShowManager::renderView(QQuickItem *parent) int trkIdx = 0; - foreach(Track *track, m_currentShow->tracks()) + foreach (Track *track, m_currentShow->tracks()) { - int itemIndex = 0; - - foreach(ShowFunction *sf, track->showFunctions()) + foreach (ShowFunction *sf, track->showFunctions()) { Function *func = m_doc->function(sf->functionID()); if (func == nullptr) @@ -394,9 +592,7 @@ void ShowManager::renderView(QQuickItem *parent) newItem->setProperty("sfRef", QVariant::fromValue(sf)); newItem->setProperty("funcRef", QVariant::fromValue(func)); - quint32 itemID = trkIdx << 16 | itemIndex; - m_itemsMap[itemID] = newItem; - itemIndex++; + m_itemsMap[sf->id()] = newItem; } trkIdx++; @@ -417,20 +613,6 @@ int ShowManager::showDuration() const return m_currentShow->totalDuration(); } -int ShowManager::currentTime() const -{ - return m_currentTime; -} - -void ShowManager::setCurrentTime(int currentTime) -{ - if (m_currentTime == currentTime) - return; - - m_currentTime = currentTime; - emit currentTimeChanged(currentTime); -} - void ShowManager::playShow() { if (m_currentShow == nullptr) @@ -505,7 +687,7 @@ void ShowManager::setItemSelection(int trackIdx, ShowFunction *sf, QQuickItem *i void ShowManager::resetItemsSelection() { - foreach(SelectedShowItem ssi, m_selectedItems) + foreach (SelectedShowItem ssi, m_selectedItems) { if (ssi.m_item != nullptr) ssi.m_item->setProperty("isSelected", false); @@ -569,7 +751,7 @@ bool ShowManager::checkOverlapping(Track *track, ShowFunction *sourceFunc, if (track == nullptr) return false; - foreach(ShowFunction *sf, track->showFunctions()) + foreach (ShowFunction *sf, track->showFunctions()) { if (sf == sourceFunc) continue; @@ -635,9 +817,22 @@ QVariantList ShowManager::previewData(Function *f) const break; /* All the other Function types */ + case Function::AudioType: + case Function::VideoType: + { + data.append(RepeatingDuration); + data.append(f->totalDuration()); + data.append(FadeIn); + data.append(f->fadeInSpeed()); + data.append(FadeOut); + data.append(f->fadeOutSpeed()); + } + break; default: + { data.append(RepeatingDuration); data.append(f->totalDuration()); + } break; } diff --git a/qmlui/showmanager.h b/qmlui/showmanager.h index 32a9f4e089..f311d40413 100644 --- a/qmlui/showmanager.h +++ b/qmlui/showmanager.h @@ -24,9 +24,9 @@ #include #include "previewcontext.h" +#include "show.h" class Doc; -class Show; class Track; class Function; class ShowFunction; @@ -43,17 +43,23 @@ class ShowManager : public PreviewContext Q_OBJECT Q_PROPERTY(int currentShowID READ currentShowID WRITE setCurrentShowID NOTIFY currentShowIDChanged) + Q_PROPERTY(bool isEditing READ isEditing NOTIFY isEditingChanged) Q_PROPERTY(QString showName READ showName WRITE setShowName NOTIFY showNameChanged) Q_PROPERTY(QColor itemsColor READ itemsColor WRITE setItemsColor NOTIFY itemsColorChanged) - Q_PROPERTY(float timeScale READ timeScale WRITE setTimeScale NOTIFY timeScaleChanged) - Q_PROPERTY(float tickSize READ tickSize CONSTANT) + Q_PROPERTY(bool stretchFunctions READ stretchFunctions WRITE setStretchFunctions NOTIFY stretchFunctionsChanged) Q_PROPERTY(bool gridEnabled READ gridEnabled WRITE setGridEnabled NOTIFY gridEnabledChanged) - Q_PROPERTY(int currentTime READ currentTime WRITE setCurrentTime NOTIFY currentTimeChanged) Q_PROPERTY(bool isPlaying READ isPlaying NOTIFY isPlayingChanged) Q_PROPERTY(int showDuration READ showDuration NOTIFY showDurationChanged) + + Q_PROPERTY(Show::TimeDivision timeDivision READ timeDivision WRITE setTimeDivision NOTIFY timeDivisionChanged) + Q_PROPERTY(int beatsDivision READ beatsDivision NOTIFY beatsDivisionChanged) + Q_PROPERTY(float timeScale READ timeScale WRITE setTimeScale NOTIFY timeScaleChanged) + Q_PROPERTY(float tickSize READ tickSize NOTIFY tickSizeChanged) + Q_PROPERTY(int currentTime READ currentTime WRITE setCurrentTime NOTIFY currentTimeChanged) + Q_PROPERTY(QVariant tracks READ tracks NOTIFY tracksChanged) - Q_PROPERTY(int selectedTrack READ selectedTrack WRITE setSelectedTrack NOTIFY selectedTrackChanged) + Q_PROPERTY(int selectedTrackIndex READ selectedTrackIndex WRITE setSelectedTrackIndex NOTIFY selectedTrackIndexChanged) Q_PROPERTY(int selectedItemsCount READ selectedItemsCount NOTIFY selectedItemsCountChanged) public: @@ -62,6 +68,12 @@ class ShowManager : public PreviewContext /** Return the ID of the Show Function being edited */ int currentShowID() const; + /** Return a reference of the Show currently being edited */ + Show *currentShow() const; + + /** Flag to indicate if a Show is currently being edited */ + bool isEditing(); + /** Set the ID of the Show Function to edit */ void setCurrentShowID(int currentShowID); @@ -71,13 +83,6 @@ class ShowManager : public PreviewContext /** Set the name of the Show Function to edit */ void setShowName(QString showName); - /** Return a list of Track objects suitable for QML */ - QVariant tracks(); - - /** Get/Set the selected track index */ - int selectedTrack() const; - void setSelectedTrack(int selectedTrack); - /** Reset the Show Manager contents to an initial state */ void resetContents(); @@ -92,13 +97,6 @@ class ShowManager : public PreviewContext /** Return the current Show total duration in milliseconds */ int showDuration() const; - /** Get/Set the current time scale of the Show Manager timeline */ - float timeScale() const; - void setTimeScale(float timeScale); - - /** Get the size in pixels of the Show header time division */ - float tickSize() const; - /** Get/Set the Function stretch flag */ bool stretchFunctions() const; void setStretchFunctions(bool stretchFunctions); @@ -107,10 +105,6 @@ class ShowManager : public PreviewContext bool gridEnabled() const; void setGridEnabled(bool gridEnabled); - /** Get/Set the current time of the Show (aka cursor position) */ - int currentTime() const; - void setCurrentTime(int currentTime); - /** Play or resume the Show playback */ Q_INVOKABLE void playShow(); @@ -122,26 +116,17 @@ class ShowManager : public PreviewContext signals: void currentShowIDChanged(int currentShowID); + void isEditingChanged(); void showNameChanged(QString showName); - void timeScaleChanged(float timeScale); void stretchFunctionsChanged(bool stretchFunction); void gridEnabledChanged(bool gridEnabled); - void currentTimeChanged(int currentTime); void isPlayingChanged(bool playing); void showDurationChanged(int showDuration); - void tracksChanged(); - void selectedTrackChanged(int selectedTrack); private: /** A reference to the Show Function being edited */ Show *m_currentShow; - /** The current time scale of the Show Manager timeline */ - float m_timeScale; - - /** Size in pixels of the Show Manager time division */ - float m_tickSize; - /** Flag that indicates if a Function should be stretched * when the corresponding Show Item duration changes */ bool m_stretchFunctions; @@ -150,14 +135,69 @@ class ShowManager : public PreviewContext * snapped to the closest grid divisor */ bool m_gridEnabled; + /********************************************************************* + * Time + ********************************************************************/ +public: + /** Get/Set the Show time division */ + Show::TimeDivision timeDivision(); + void setTimeDivision(Show::TimeDivision division); + int beatsDivision(); + + /** Get/Set the current time scale of the Show Manager timeline */ + float timeScale() const; + void setTimeScale(float timeScale); + + /** Get the size in pixels of the Show header time division */ + float tickSize() const; + + /** Get/Set the current time of the Show (aka cursor position) */ + int currentTime() const; + void setCurrentTime(int currentTime); + +signals: + void timeDivisionChanged(Show::TimeDivision division); + void beatsDivisionChanged(int beatsDivision); + void timeScaleChanged(float timeScale); + void tickSizeChanged(float tickSize); + void currentTimeChanged(int currentTime); + +private: + /** The current time scale of the Show Manager timeline */ + float m_timeScale; + + /** Size in pixels of the Show Manager time division */ + float m_tickSize; + /** The current time position of the Show in ms */ int m_currentTime; + /********************************************************************* + * Tracks + ********************************************************************/ +public: + /** Return a list of Track objects suitable for QML */ + QVariant tracks(); + + /** Get/Set the selected track index */ + int selectedTrackIndex() const; + void setSelectedTrackIndex(int index); + + Q_INVOKABLE void setTrackSolo(int index, bool solo); + + /** Move the track with the provided index in the provided direction */ + Q_INVOKABLE void moveTrack(int index, int direction); + +signals: + void tracksChanged(); + void selectedTrackIndexChanged(int index); + +private: /** A list of references to the selected Show Tracks */ QList m_tracksList; /** The index of the currently selected track */ - int m_selectedTrack; + int m_selectedTrackIndex; /********************************************************************* * Show Items @@ -187,12 +227,19 @@ class ShowManager : public PreviewContext /** Add a new Item to the timeline. * This happens when dragging an existing Function from the Function Manager. * If the current Show is NULL, a new Show is created. - * If the provided $trackIdx is no valid, a new Track is created + * If the provided $trackIdx is not valid, a new Track is created */ Q_INVOKABLE void addItems(QQuickItem *parent, int trackIdx, int startTime, QVariantList idsList); + /** Add a Show item from an existing ShowFunction reference and Track Id */ + void addShowItem(ShowFunction *sf, quint32 trackId); + + /** Delete the currently selected show items */ Q_INVOKABLE void deleteShowItems(QVariantList data); + /** Delete the item referencing the provided ShowFunction from the QML view */ + void deleteShowItem(ShowFunction *sf); + /** Method invoked when moving an existing Show Item on the timeline. * The new position is checked for overlapping against existing items on the * provided $newTrackIdx. On overlapping, false is returned and the UI @@ -203,6 +250,12 @@ class ShowManager : public PreviewContext Q_INVOKABLE bool checkAndMoveItem(ShowFunction *sf, int originalTrackIdx, int newTrackIdx, int newStartTime); + /** Set the start time of a ShowFunction item (if not overlapping) */ + Q_INVOKABLE bool setShowItemStartTime(ShowFunction *sf, int startTime); + + /** Set the duration of a ShowFunction item (if not overlapping) */ + Q_INVOKABLE bool setShowItemDuration(ShowFunction *sf, int duration); + /** Returns the number of the currently selected Show items */ int selectedItemsCount() const; diff --git a/qmlui/simpledesk.cpp b/qmlui/simpledesk.cpp index 137fdd7c7b..b660be76e2 100644 --- a/qmlui/simpledesk.cpp +++ b/qmlui/simpledesk.cpp @@ -22,10 +22,12 @@ #include "simpledesk.h" #include "keypadparser.h" #include "genericfader.h" +#include "functionmanager.h" #include "fadechannel.h" #include "scenevalue.h" #include "listmodel.h" #include "tardis.h" +#include "app.h" #include "doc.h" #define UserRoleClassReference (Qt::UserRole + 1) @@ -36,8 +38,11 @@ #define MAX_KEYPAD_HISTORY 10 -SimpleDesk::SimpleDesk(QQuickView *view, Doc *doc, QObject *parent) +SimpleDesk::SimpleDesk(QQuickView *view, Doc *doc, + FunctionManager *funcMgr, QObject *parent) : PreviewContext(view, doc, "SDESK", parent) + , m_functionManager(funcMgr) + , m_dumpChannelMask(0) { Q_ASSERT(m_doc != nullptr); @@ -64,6 +69,7 @@ SimpleDesk::SimpleDesk(QQuickView *view, Doc *doc, QObject *parent) connect(m_doc, SIGNAL(loaded()), this, SLOT(updateChannelList())); connect(m_doc, SIGNAL(fixtureAdded(quint32)), this, SLOT(updateChannelList())); connect(m_doc, SIGNAL(fixtureRemoved(quint32)), this, SLOT(updateChannelList())); + connect(m_doc, SIGNAL(fixtureChanged(quint32)), this, SLOT(updateChannelList())); connect(m_doc->inputOutputMap(), SIGNAL(universeAdded(quint32)), this, SIGNAL(universesListModelChanged())); connect(m_doc->inputOutputMap(), SIGNAL(universeRemoved(quint32)), @@ -121,42 +127,42 @@ void SimpleDesk::updateChannelList() { quint32 chIndex = 0; quint32 chValue = currUni.at(i); - bool override = false; + bool isOverriding = false; - Fixture *fxi = m_doc->fixture(m_doc->fixtureForAddress(start + i)); - if (fxi != nullptr) + Fixture *fixture = m_doc->fixture(m_doc->fixtureForAddress(start + i)); + if (fixture != nullptr) { - if (fxi->id() != prevID) + if (fixture->id() != prevID) { status = (status == Odd) ? Even : Odd; - prevID = fxi->id(); + prevID = fixture->id(); } - chIndex = i - fxi->address(); + chIndex = i - fixture->address(); if (hasChannel(i)) { chValue = value(i); - override = true; + isOverriding = true; } else { - chValue = fxi->channelValueAt(chIndex); + chValue = fixture->channelValueAt(chIndex); } } else { if (hasChannel(i)) { - override = true; + isOverriding = true; chValue = value(i); } } QVariantMap chMap; - chMap.insert("cRef", QVariant::fromValue(fxi)); + chMap.insert("cRef", QVariant::fromValue(fixture)); chMap.insert("chIndex", chIndex); chMap.insert("chValue", chValue); chMap.insert("chDisplay", status); - chMap.insert("isOverride", override); + chMap.insert("isOverride", isOverriding); m_channelList->addDataMap(chMap); } @@ -189,7 +195,15 @@ void SimpleDesk::setValue(quint32 fixtureID, uint channel, uchar value) quint32 start = (m_universeFilter * 512); QVariant currentVal, newVal; SceneValue currScv; + Fixture *fixture = nullptr; + qDebug() << "[Simple Desk] set value for fixture" << fixtureID << "channel" << channel << "value" << value; + + if (fixtureID != Fixture::invalidId()) + { + fixture = m_doc->fixture(fixtureID); + channel += fixture->address(); + } if (m_values.contains(start + channel)) { //currScv.fxi = fixtureID; @@ -207,11 +221,10 @@ void SimpleDesk::setValue(quint32 fixtureID, uint channel, uchar value) newVal.setValue(SceneValue(Fixture::invalidId(), channel, value)); Tardis::instance()->enqueueAction(Tardis::SimpleDeskSetChannel, 0, currentVal, newVal); - if (fixtureID != Fixture::invalidId()) + if (fixture != nullptr) { - Fixture *fixture = m_doc->fixture(fixtureID); quint32 relCh = channel - fixture->address(); - emit channelValueChanged(fixtureID, relCh, value); + setDumpValue(fixtureID, relCh, value); } setChanged(true); @@ -312,6 +325,101 @@ void SimpleDesk::slotUniverseWritten(quint32 idx, const QByteArray& ua) m_prevUniverseValues[idx].replace(0, ua.length(), ua); } +/********************************************************************* + * DMX channels dump + *********************************************************************/ + +void SimpleDesk::setDumpValue(quint32 fxID, quint32 channel, uchar value) +{ + QVariant currentVal, newVal; + SceneValue sValue(fxID, channel, value); + int valIndex = m_dumpValues.indexOf(sValue); + uchar currDmxValue = valIndex >= 0 ? m_dumpValues.at(valIndex).value : 0; + currentVal.setValue(SceneValue(fxID, channel, currDmxValue)); + newVal.setValue(sValue); + + if (currentVal != newVal || value != currDmxValue) + { + if (valIndex >= 0) + { + m_dumpValues.replace(valIndex, sValue); + } + else + { + m_dumpValues.append(sValue); + emit dumpValuesCountChanged(); + + const QLCChannel *ch = m_doc->fixture(fxID)->channel(channel); + if (ch != nullptr) + { + if (ch->group() == QLCChannel::Intensity) + { + if (ch->colour() == QLCChannel::NoColour) + m_dumpChannelMask |= App::DimmerType; + else + m_dumpChannelMask |= App::ColorType; + } + else + { + m_dumpChannelMask |= (1 << ch->group()); + } + emit dumpChannelMaskChanged(); + } + } + + Fixture *fixture = m_doc->fixture(fxID); + if (fixture) + fixture->checkAlias(channel, value); + } +} + +void SimpleDesk::unsetDumpValue(quint32 fxID, quint32 channel) +{ + SceneValue sValue(fxID, channel, 0); + int valIndex = m_dumpValues.indexOf(sValue); + + if (valIndex >= 0) + { + m_dumpValues.removeAt(valIndex); + emit dumpValuesCountChanged(); + } +} + +int SimpleDesk::dumpValuesCount() const +{ + int i = 0; + QList fixtureList; + + for (SceneValue scv : m_dumpValues) + if (!fixtureList.contains(scv.fxi)) + fixtureList.append(scv.fxi); + + if (fixtureList.isEmpty()) + return m_dumpValues.count(); + + for (SceneValue sv : m_dumpValues) + if (fixtureList.contains(sv.fxi)) + i++; + + return i; +} + +int SimpleDesk::dumpChannelMask() const +{ + return m_dumpChannelMask; +} + +void SimpleDesk::dumpDmxChannels(QString name, quint32 mask) +{ + QList fixtureList; + + for (SceneValue scv : m_dumpValues) + if (!fixtureList.contains(scv.fxi)) + fixtureList.append(scv.fxi); + + m_functionManager->dumpOnNewScene(m_dumpValues, fixtureList, mask, name); +} + /************************************************************************ * Keypad ************************************************************************/ @@ -324,7 +432,11 @@ void SimpleDesk::sendKeypadCommand(QString command) for (SceneValue scv : scvList) { quint32 fxID = m_doc->fixtureForAddress((m_universeFilter * 512) + scv.channel); - setValue(fxID, scv.channel, scv.value); + Fixture *fixture = m_doc->fixture(fxID); + if (fixture != nullptr) + setValue(fxID, scv.channel - fixture->address(), scv.value); + else + setValue(fxID, scv.channel, scv.value); QModelIndex mIndex = m_channelList->index(int(scv.channel), 0, QModelIndex()); m_channelList->setData(mIndex, QVariant(scv.value), UserRoleChannelValue); } @@ -347,6 +459,8 @@ QStringList SimpleDesk::commandHistory() const FadeChannel *SimpleDesk::getFader(QList universes, quint32 universeID, quint32 fixtureID, quint32 channel) { + qDebug() << "[Simple Desk] get fader for universe" << universeID << "fixture" << fixtureID << "channel" << channel; + // get the universe Fader first. If doesn't exist, create it QSharedPointer fader = m_fadersMap.value(universeID, QSharedPointer()); if (fader.isNull()) @@ -375,7 +489,7 @@ void SimpleDesk::writeDMX(MasterTimer *timer, QList ua) if (universe >= (quint32)ua.count()) continue; - ua[universe]->reset(0, 512); + //ua[universe]->reset(0, 512); QSharedPointer fader = m_fadersMap.value(universe, QSharedPointer()); if (!fader.isNull()) @@ -398,10 +512,21 @@ void SimpleDesk::writeDMX(MasterTimer *timer, QList ua) ua[universe]->setChannelDefaultValue(fixture->address() + chIndex, ch->defaultValue()); } } + else + { + ua[universe]->reset(chIndex, 1); + } } ua[universe]->dismissFader(fader); m_fadersMap.remove(universe); } + + // reset DMX dump as well + m_dumpValues.clear(); + emit dumpValuesCountChanged(); + + m_dumpChannelMask = 0; + emit dumpChannelMaskChanged(); } else if (command.first == ResetChannel) { @@ -444,7 +569,16 @@ void SimpleDesk::writeDMX(MasterTimer *timer, QList ua) int uni = it.key() >> 9; int address = it.key(); uchar value = it.value(); - FadeChannel *fc = getFader(ua, uni, Fixture::invalidId(), address); + quint32 fxID = m_doc->fixtureForAddress((m_universeFilter * 512) + address); + quint32 channel = address; + + if (fxID != Fixture::invalidId()) + { + Fixture *fixture = m_doc->fixture(fxID); + if (fixture != nullptr) + channel = address - fixture->address(); + } + FadeChannel *fc = getFader(ua, uni, fxID, channel); fc->setCurrent(value); fc->setTarget(value); fc->addFlag(FadeChannel::Override); diff --git a/qmlui/simpledesk.h b/qmlui/simpledesk.h index 9e73d4a5ac..695bc975d5 100644 --- a/qmlui/simpledesk.h +++ b/qmlui/simpledesk.h @@ -21,10 +21,12 @@ #define SIMPLEDESK_H #include "previewcontext.h" +#include "scenevalue.h" #include "dmxsource.h" #include +class FunctionManager; class GenericFader; class KeyPadParser; class FadeChannel; @@ -36,11 +38,14 @@ class SimpleDesk : public PreviewContext, public DMXSource Q_PROPERTY(QVariant universesListModel READ universesListModel NOTIFY universesListModelChanged) Q_PROPERTY(QVariant channelList READ channelList NOTIFY channelListChanged) + Q_PROPERTY(int dumpValuesCount READ dumpValuesCount NOTIFY dumpValuesCountChanged) + Q_PROPERTY(quint32 dumpChannelMask READ dumpChannelMask NOTIFY dumpChannelMaskChanged) Q_PROPERTY(QVariantList fixtureList READ fixtureList NOTIFY fixtureListChanged) Q_PROPERTY(QStringList commandHistory READ commandHistory NOTIFY commandHistoryChanged) public: - SimpleDesk(QQuickView *view, Doc *doc, QObject *parent = 0); + SimpleDesk(QQuickView *view, Doc *doc, + FunctionManager *funcMgr, QObject *parent = 0); ~SimpleDesk(); QVariant universesListModel() const; @@ -65,6 +70,9 @@ protected slots: void fixtureListChanged(); private: + /** Reference to the Function Manager */ + FunctionManager *m_functionManager; + /** QML ready model to hold channel values and changes */ ListModel *m_channelList; @@ -121,6 +129,36 @@ protected slots: * This is used to sync reset requests with mastertimer ticks */ QList< QPair > m_commandQueue; + /********************************************************************* + * DMX channels dump + *********************************************************************/ +public: + /** Store a channel value for Scene dumping */ + Q_INVOKABLE void setDumpValue(quint32 fxID, quint32 channel, uchar value); + + /** Remove a channel from the Scene dumping list */ + Q_INVOKABLE void unsetDumpValue(quint32 fxID, quint32 channel); + + /** Return the number of DMX channels currently available for dumping */ + int dumpValuesCount() const; + + /** Return the current DMX dump channel type mask */ + int dumpChannelMask() const; + + Q_INVOKABLE void dumpDmxChannels(QString name, quint32 mask); + +signals: + void dumpValuesCountChanged(); + void dumpChannelMaskChanged(); + +private: + /** List of the values available for dumping to a Scene */ + QList m_dumpValues; + + /** Bitmask representing the available channel types for + * the DMX channels ready for dumping */ + quint32 m_dumpChannelMask; + /************************************************************************ * Keypad ************************************************************************/ diff --git a/qmlui/tardis/networkmanager.cpp b/qmlui/tardis/networkmanager.cpp index 111f395271..9bafa10168 100644 --- a/qmlui/tardis/networkmanager.cpp +++ b/qmlui/tardis/networkmanager.cpp @@ -166,7 +166,7 @@ bool NetworkManager::sendTCPPacket(QTcpSocket *socket, QByteArray &packet, bool if (encrypt) { QByteArray encPacket = m_packetizer->encryptPacket(packet, m_crypt); - while(totalBytesSent < (quint64)encPacket.length()) + while (totalBytesSent < (quint64)encPacket.length()) { sent = socket->write(encPacket.data() + totalBytesSent, encPacket.length() - totalBytesSent); totalBytesSent += sent; @@ -176,7 +176,7 @@ bool NetworkManager::sendTCPPacket(QTcpSocket *socket, QByteArray &packet, bool } else { - while(totalBytesSent < (quint64)packet.length()) + while (totalBytesSent < (quint64)packet.length()) { sent = socket->write(packet.data() + totalBytesSent, packet.length() - totalBytesSent); totalBytesSent += sent; @@ -329,7 +329,7 @@ bool NetworkManager::sendWorkspaceToClient(QString hostName, QString filename) m_packetizer->addSection(packet, QVariant((int)workspace.size())); } - else if(data.count() < WORKSPACE_CHUNK_SIZE) + else if (data.length() < WORKSPACE_CHUNK_SIZE) { m_packetizer->addSection(packet, QVariant(2)); } @@ -410,7 +410,7 @@ bool NetworkManager::initializeClient() m_packetizer->addSection(packet, QVariant(m_hostName)); /* now send the packet on every network interface */ - foreach(QNetworkInterface iface, QNetworkInterface::allInterfaces()) + foreach (QNetworkInterface iface, QNetworkInterface::allInterfaces()) { foreach (QNetworkAddressEntry entry, iface.addressEntries()) { diff --git a/qmlui/tardis/networkpacketizer.cpp b/qmlui/tardis/networkpacketizer.cpp index 5dc187bbde..eeb55ed8ad 100644 --- a/qmlui/tardis/networkpacketizer.cpp +++ b/qmlui/tardis/networkpacketizer.cpp @@ -180,6 +180,18 @@ void NetworkPacketizer::addSection(QByteArray &packet, QVariant value) packet.append((char)(pairVal.second >> 8)); // MSB1 packet.append((char)(pairVal.second & 0x00FF)); // LSB } + else if (value.canConvert()) + { + StringDoublePair pairVal = value.value(); + packet.append(StringDoublePairType); + QByteArray strVal = pairVal.first.toUtf8(); + packet.append((char)(strVal.length() >> 8)); // string length MSB + packet.append((char)(strVal.length() & 0x00FF)); // string length LSB + packet.append(strVal); + char *byteArray = (char*)&pairVal.second; + for (int ds = 0; ds < int(sizeof(double)); ds++) + packet.append(byteArray[ds]); + } else if (value.canConvert()) { StringStringPair pairVal = value.value(); @@ -404,6 +416,27 @@ int NetworkPacketizer::decodePacket(QByteArray &packet, int &opCode, QVariantLis sections.append(var); } break; + case StringDoublePairType: + { + StringDoublePair pairVal; + QString strVal; + quint16 sLength = ((quint16)ba.at(bytes_read) << 8) + (quint16)ba.at(bytes_read + 1); + bytes_read += 2; + + strVal.append(ba.mid(bytes_read, sLength)); + pairVal.first = strVal; + bytes_read += sLength; + + QByteArray dba = ba.mid(bytes_read, sizeof(double)); + + pairVal.second = *((double*)dba.data()); + bytes_read += dba.length(); + + QVariant var; + var.setValue(pairVal); + sections.append(var); + } + break; case StringStringPairType: { StringStringPair pairVal; diff --git a/qmlui/tardis/networkpacketizer.h b/qmlui/tardis/networkpacketizer.h index a3e6e02a8e..3354d34daa 100644 --- a/qmlui/tardis/networkpacketizer.h +++ b/qmlui/tardis/networkpacketizer.h @@ -47,6 +47,7 @@ class NetworkPacketizer SceneValueType, UIntPairType, StringIntPairType, + StringDoublePairType, StringStringPairType }; diff --git a/qmlui/tardis/simplecrypt.cpp b/qmlui/tardis/simplecrypt.cpp index 600179c49e..d10ae56bf2 100644 --- a/qmlui/tardis/simplecrypt.cpp +++ b/qmlui/tardis/simplecrypt.cpp @@ -94,7 +94,7 @@ QByteArray SimpleCrypt::encryptToByteArray(QByteArray plaintext) flags |= CryptoFlagCompression; } else if (m_compressionMode == CompressionAuto) { QByteArray compressed = qCompress(ba, 9); - if (compressed.count() < ba.count()) { + if (compressed.length() < ba.length()) { ba = compressed; flags |= CryptoFlagCompression; } @@ -125,7 +125,7 @@ QByteArray SimpleCrypt::encryptToByteArray(QByteArray plaintext) int pos(0); char lastChar(0); - int cnt = ba.count(); + int cnt = ba.length(); while (pos < cnt) { ba[pos] = ba.at(pos) ^ m_keyParts.at(pos % 8) ^ lastChar; @@ -192,7 +192,7 @@ QByteArray SimpleCrypt::decryptToByteArray(QByteArray cypher) QByteArray ba = cypher; - if( cypher.count() < 3 ) + if (cypher.length() < 3) return QByteArray(); char version = ba.at(0); @@ -207,7 +207,7 @@ QByteArray SimpleCrypt::decryptToByteArray(QByteArray cypher) ba = ba.mid(2); int pos(0); - int cnt(ba.count()); + int cnt(ba.length()); char lastChar = 0; while (pos < cnt) { diff --git a/qmlui/tardis/tardis.cpp b/qmlui/tardis/tardis.cpp index a7b6453a23..1e5c2ae56e 100644 --- a/qmlui/tardis/tardis.cpp +++ b/qmlui/tardis/tardis.cpp @@ -28,7 +28,9 @@ #include "fixturemanager.h" #include "functionmanager.h" #include "contextmanager.h" -#include "functioneditor.h" +#include "showmanager.h" +#include "mainview2d.h" +#include "mainview3d.h" #include "simpledesk.h" #include "collection.h" #include "rgbmatrix.h" @@ -44,6 +46,7 @@ #include "scene.h" #include "audio.h" #include "video.h" +#include "show.h" #include "efx.h" #include "doc.h" @@ -118,7 +121,10 @@ void Tardis::enqueueAction(int code, quint32 objID, QVariant oldVal, QVariant ne } // inform the thread an action is available m_queueSem.release(); - m_doc->setModified(); + + // set modify flag for non-live actions + if (action.m_action < VCButtonSetPressed) + m_doc->setModified(); } QString Tardis::actionToString(int action) @@ -200,6 +206,9 @@ void Tardis::redoAction() void Tardis::resetHistory() { m_history.clear(); + m_historyIndex = -1; + m_historyCount = 0; + m_busy = false; } void Tardis::forwardActionToNetwork(int code, TardisAction &action) @@ -367,6 +376,23 @@ QByteArray Tardis::actionToByteArray(int code, quint32 objID, QVariant data) fixture->saveXML(&xmlWriter); } break; + case ShowManagerAddTrack: + case ShowManagerDeleteTrack: + { + Show *show = qobject_cast(m_doc->function(objID)); + Track *track = show->track(data.toInt()); + track->saveXML(&xmlWriter); + } + break; + case ShowManagerAddFunction: + case ShowManagerDeleteFunction: + { + Show *show = qobject_cast(m_doc->function(objID)); + ShowFunction *sf = show->showFunction(data.toUInt()); + Track *track = show->getTrackFromShowFunctionID(sf->id()); + sf->saveXML(&xmlWriter, track->id()); + } + break; case VCWidgetCreate: case VCWidgetDelete: { @@ -463,7 +489,9 @@ bool Tardis::processBufferedAction(int action, quint32 objID, QVariant &value) case ChaserRemoveStep: { Chaser *chaser = qobject_cast(m_doc->function(objID)); - chaser->removeStep(value.toInt()); + QXmlStreamAttributes attrs = xmlReader.attributes(); + if (attrs.hasAttribute(KXMLQLCFunctionNumber)) + chaser->removeStep(attrs.value(KXMLQLCFunctionNumber).toUInt()); } break; case EFXAddFixture: @@ -485,6 +513,51 @@ bool Tardis::processBufferedAction(int action, quint32 objID, QVariant &value) delete ef; } break; + case ShowManagerAddTrack: + { + Show *show = qobject_cast(m_doc->function(objID)); + Track *track = new Track(); + track->loadXML(xmlReader); + show->addTrack(track, track->id()); + } + break; + case ShowManagerDeleteTrack: + { + QXmlStreamAttributes attrs = xmlReader.attributes(); + Show *show = qobject_cast(m_doc->function(objID)); + if (attrs.hasAttribute(KXMLQLCTrackID)) + show->removeTrack(attrs.value(KXMLQLCTrackID).toUInt()); + } + break; + case ShowManagerAddFunction: + { + QXmlStreamAttributes attrs = xmlReader.attributes(); + Show *show = qobject_cast(m_doc->function(objID)); + ShowFunction *sf = new ShowFunction(show->getLatestShowFunctionId()); + sf->loadXML(xmlReader); + quint32 trackId = attrs.value(KXMLShowFunctionTrackId).toUInt(); + Track *track = show->track(trackId); + track->addShowFunction(sf); + m_showManager->addShowItem(sf, trackId); + } + break; + case ShowManagerDeleteFunction: + { + QXmlStreamAttributes attrs = xmlReader.attributes(); + Show *show = qobject_cast(m_doc->function(objID)); + if (attrs.hasAttribute(KXMLShowFunctionUid)) + { + quint32 sfUID = attrs.value(KXMLShowFunctionUid).toUInt(); + Track *track = show->getTrackFromShowFunctionID(sfUID); + if (track != nullptr) + { + ShowFunction *sf = track->showFunction(sfUID); + m_showManager->deleteShowItem(sf); + track->removeShowFunction(sf); + } + } + } + break; case VCWidgetCreate: { VCFrame *frame = qobject_cast(m_virtualConsole->widget(objID)); @@ -532,12 +605,44 @@ int Tardis::processAction(TardisAction &action, bool undo) switch(action.m_action) { - /* *********************** Global settings actions ************************ */ + /* *********************** Preview settings actions ************************ */ case EnvironmentSetSize: { m_contextManager->setEnvironmentSize(value->value()); } break; + case EnvironmentBackgroundImage: + { + m_contextManager->get2DView()->setBackgroundImage(value->toString()); + } + break; + case FixtureSetPosition: + { + QVector3D pos = value->value(); + m_contextManager->setFixturePosition(action.m_objID, pos.x(), pos.y(), pos.z()); + } + break; + case FixtureSetRotation: + { + QVector3D rotation = value->value(); + m_contextManager->setFixtureRotation(action.m_objID, rotation); + } + break; + case GenericItemSetPosition: + { + m_contextManager->get3DView()->updateGenericItemPosition(action.m_objID, value->value()); + } + break; + case GenericItemSetRotation: + { + m_contextManager->get3DView()->updateGenericItemRotation(action.m_objID, value->value()); + } + break; + case GenericItemSetScale: + { + m_contextManager->get3DView()->updateGenericItemScale(action.m_objID, value->value()); + } + break; /* *********************** Input/Output manager actions ************************ */ case IOAddUniverse: @@ -567,12 +672,6 @@ int Tardis::processAction(TardisAction &action, bool undo) m_fixtureManager->renameFixture(action.m_objID, value->toString()); } break; - case FixtureSetPosition: - { - QVector3D pos = value->value(); - m_contextManager->setFixturePosition(action.m_objID, pos.x(), pos.y(), pos.z()); - } - break; case FixtureSetDumpValue: { SceneValue scv = value->value(); @@ -661,6 +760,70 @@ int Tardis::processAction(TardisAction &action, bool undo) } break; + case SceneAddFixture: + { + SceneValue scv = value->value(); + Scene *scene = qobject_cast(m_doc->function(action.m_objID)); + + if (undo) + scene->removeFixture(scv.fxi); + else + scene->addFixture(scv.fxi); + } + break; + + case SceneRemoveFixture: + { + SceneValue scv = value->value(); + Scene *scene = qobject_cast(m_doc->function(action.m_objID)); + + if (undo) + scene->addFixture(scv.fxi); + else + scene->removeFixture(scv.fxi); + } + break; + + case SceneAddFixtureGroup: + { + Scene *scene = qobject_cast(m_doc->function(action.m_objID)); + if (undo) + scene->removeFixtureGroup(value->toUInt()); + else + scene->addFixtureGroup(value->toUInt()); + } + break; + + case SceneRemoveFixtureGroup: + { + Scene *scene = qobject_cast(m_doc->function(action.m_objID)); + if (undo) + scene->addFixtureGroup(value->toUInt()); + else + scene->removeFixtureGroup(value->toUInt()); + } + break; + + case SceneAddPalette: + { + Scene *scene = qobject_cast(m_doc->function(action.m_objID)); + if (undo) + scene->removePalette(value->toUInt()); + else + scene->addPalette(value->toUInt()); + } + break; + + case SceneRemovePalette: + { + Scene *scene = qobject_cast(m_doc->function(action.m_objID)); + if (undo) + scene->addPalette(value->toUInt()); + else + scene->removePalette(value->toUInt()); + } + break; + /* *********************** Chaser editing actions *********************** */ case ChaserAddStep: @@ -671,6 +834,14 @@ int Tardis::processAction(TardisAction &action, bool undo) processBufferedAction(undo ? ChaserAddStep : ChaserRemoveStep, action.m_objID, action.m_oldValue); return undo ? ChaserAddStep : ChaserRemoveStep; + case ChaserMoveStep: + { + Chaser *chaser = qobject_cast(m_doc->function(action.m_objID)); + chaser->moveStep(undo ? action.m_newValue.toInt() : action.m_oldValue.toInt(), + undo ? action.m_oldValue.toInt() : action.m_newValue.toInt()); + } + break; + case ChaserSetStepFadeIn: { Chaser *chaser = qobject_cast(m_doc->function(action.m_objID)); @@ -849,6 +1020,13 @@ int Tardis::processAction(TardisAction &action, bool undo) matrix->setProperty(pairValue.first, QString::number(pairValue.second)); } break; + case RGBMatrixSetScriptDoubleValue: + { + RGBMatrix *matrix = qobject_cast(m_doc->function(action.m_objID)); + StringDoublePair pairValue = value->value(); // param name on first, value on second + matrix->setProperty(pairValue.first, QString::number(pairValue.second)); + } + break; case RGBMatrixSetScriptStringValue: { RGBMatrix *matrix = qobject_cast(m_doc->function(action.m_objID)); @@ -921,6 +1099,13 @@ int Tardis::processAction(TardisAction &action, bool undo) } break; + case AudioSetVolume: + { + auto member = std::mem_fn(&Audio::setVolume); + member(qobject_cast