From 374da2749e2583c751c398594c67dc2114ec25ae Mon Sep 17 00:00:00 2001 From: Rob Kahle Date: Thu, 26 Jul 2018 00:52:27 -0400 Subject: [PATCH 001/212] Completed project tasks for adding button monitor feedback --- engine/src/qlcinputsource.cpp | 10 ++++++++++ engine/src/qlcinputsource.h | 4 +++- ui/src/inputselectionwidget.cpp | 15 ++++++++++++++- ui/src/inputselectionwidget.h | 1 + ui/src/virtualconsole/vcbutton.cpp | 11 +++++++---- ui/src/virtualconsole/vcwidget.cpp | 8 +++++++- ui/src/virtualconsole/vcwidget.h | 1 + 7 files changed, 43 insertions(+), 7 deletions(-) diff --git a/engine/src/qlcinputsource.cpp b/engine/src/qlcinputsource.cpp index 05b9a5432c..995c05f894 100644 --- a/engine/src/qlcinputsource.cpp +++ b/engine/src/qlcinputsource.cpp @@ -127,6 +127,11 @@ void QLCInputSource::setRange(uchar lower, uchar upper) m_upper = upper; } +void QLCInputSource::setMonitor(uchar monitor) +{ + m_monitor = monitor; +} + uchar QLCInputSource::lowerValue() const { return m_lower; @@ -137,6 +142,11 @@ uchar QLCInputSource::upperValue() const return m_upper; } +uchar QLCInputSource::monitorValue() const +{ + return m_monitor; +} + /********************************************************************* * Working mode *********************************************************************/ diff --git a/engine/src/qlcinputsource.h b/engine/src/qlcinputsource.h index 42b762a98d..7044d8970f 100644 --- a/engine/src/qlcinputsource.h +++ b/engine/src/qlcinputsource.h @@ -79,9 +79,11 @@ class QLCInputSource: public QThread void setRange(uchar lower, uchar upper); uchar lowerValue() const; uchar upperValue() const; + void setMonitor(uchar monitor); + uchar monitorValue() const; protected: - uchar m_lower, m_upper; + uchar m_lower, m_upper, m_monitor; /********************************************************************* * Working mode diff --git a/ui/src/inputselectionwidget.cpp b/ui/src/inputselectionwidget.cpp index f28c2290f5..130de1b07f 100644 --- a/ui/src/inputselectionwidget.cpp +++ b/ui/src/inputselectionwidget.cpp @@ -41,6 +41,7 @@ InputSelectionWidget::InputSelectionWidget(Doc *doc, QWidget *parent) m_feedbackGroup->setVisible(false); m_lowerSpin->setEnabled(false); m_upperSpin->setEnabled(false); + m_monitorSpin->setEnabled(false); connect(m_attachKey, SIGNAL(clicked()), this, SLOT(slotAttachKey())); connect(m_detachKey, SIGNAL(clicked()), this, SLOT(slotDetachKey())); @@ -56,6 +57,8 @@ InputSelectionWidget::InputSelectionWidget(Doc *doc, QWidget *parent) this, SLOT(slotLowerSpinValueChanged(int))); connect(m_upperSpin, SIGNAL(valueChanged(int)), this, SLOT(slotUpperSpinValueChanged(int))); + connect(m_monitorSpin, SIGNAL(valueChanged(int)), + this, SLOT(slotMonitorSpinValueChanged(int))); } InputSelectionWidget::~InputSelectionWidget() @@ -196,6 +199,11 @@ void InputSelectionWidget::slotUpperSpinValueChanged(int value) m_inputSource->setRange(uchar(m_lowerSpin->value()), uchar(value)); } +void InputSelectionWidget::slotMonitorSpinValueChanged(int value) +{ + m_inputSource->setMonitor(uchar(value)); +} + void InputSelectionWidget::updateInputSource() { QString uniName; @@ -207,6 +215,7 @@ void InputSelectionWidget::updateInputSource() chName = KInputNone; m_lowerSpin->setEnabled(false); m_upperSpin->setEnabled(false); + m_monitorSpin->setEnabled(false); m_customFbButton->setChecked(false); m_feedbackGroup->setVisible(false); } @@ -214,8 +223,9 @@ void InputSelectionWidget::updateInputSource() { m_lowerSpin->blockSignals(true); m_upperSpin->blockSignals(true); + m_monitorSpin->blockSignals(true); - uchar min = 0, max = UCHAR_MAX; + uchar min = 0, max = UCHAR_MAX, mon = UCHAR_MAX; InputPatch *ip = m_doc->inputOutputMap()->inputPatch(m_inputSource->universe()); if (ip != NULL && ip->profile() != NULL) @@ -229,6 +239,7 @@ void InputSelectionWidget::updateInputSource() } m_lowerSpin->setValue((m_inputSource->lowerValue() != 0) ? m_inputSource->lowerValue() : min); m_upperSpin->setValue((m_inputSource->upperValue() != UCHAR_MAX) ? m_inputSource->upperValue() : max); + m_monitorSpin->setValue((m_inputSource->monitorValue() != UCHAR_MAX) ? m_inputSource->monitorValue() : mon); if (m_lowerSpin->value() != 0 || m_upperSpin->value() != UCHAR_MAX) { m_customFbButton->setChecked(true); @@ -240,8 +251,10 @@ void InputSelectionWidget::updateInputSource() } m_lowerSpin->blockSignals(false); m_upperSpin->blockSignals(false); + m_monitorSpin->blockSignals(false); m_lowerSpin->setEnabled(true); m_upperSpin->setEnabled(true); + m_monitorSpin->setEnabled(true); } m_inputUniverseEdit->setText(uniName); diff --git a/ui/src/inputselectionwidget.h b/ui/src/inputselectionwidget.h index 8c6384e885..ba7a8a1285 100644 --- a/ui/src/inputselectionwidget.h +++ b/ui/src/inputselectionwidget.h @@ -61,6 +61,7 @@ protected slots: void slotCustomFeedbackToggled(bool checked); void slotLowerSpinValueChanged(int value); void slotUpperSpinValueChanged(int value); + void slotMonitorSpinValueChanged(int value); signals: void autoDetectToggled(bool checked); diff --git a/ui/src/virtualconsole/vcbutton.cpp b/ui/src/virtualconsole/vcbutton.cpp index d4bfc2b95c..6efd8ca597 100644 --- a/ui/src/virtualconsole/vcbutton.cpp +++ b/ui/src/virtualconsole/vcbutton.cpp @@ -513,16 +513,19 @@ void VCButton::slotKeyReleased(const QKeySequence& keySequence) void VCButton::updateFeedback() { - if (m_state == Monitoring) - return; + //if (m_state == Monitoring) + // return; QSharedPointer src = inputSource(); if (!src.isNull() && src->isValid() == true) { - if (m_state == Inactive) + if (m_state == Inactive) { sendFeedback(src->lowerValue()); - else + } else if (m_state == Monitoring) { + sendFeedback(src->monitorValue()); + } else { sendFeedback(src->upperValue()); + } } } diff --git a/ui/src/virtualconsole/vcwidget.cpp b/ui/src/virtualconsole/vcwidget.cpp index c2372a6316..4e37add266 100644 --- a/ui/src/virtualconsole/vcwidget.cpp +++ b/ui/src/virtualconsole/vcwidget.cpp @@ -875,15 +875,18 @@ QSharedPointer VCWidget::getXMLInput(QXmlStreamReader &root) quint32 uni = attrs.value(KXMLQLCVCWidgetInputUniverse).toString().toUInt(); quint32 ch = attrs.value(KXMLQLCVCWidgetInputChannel).toString().toUInt(); - uchar min = 0, max = UCHAR_MAX; + uchar min = 0, max = UCHAR_MAX, mon = UCHAR_MAX; QSharedPointernewSrc = QSharedPointer(new QLCInputSource(uni, ch)); if (attrs.hasAttribute(KXMLQLCVCWidgetInputLowerValue)) min = uchar(attrs.value(KXMLQLCVCWidgetInputLowerValue).toString().toUInt()); if (attrs.hasAttribute(KXMLQLCVCWidgetInputUpperValue)) max = uchar(attrs.value(KXMLQLCVCWidgetInputUpperValue).toString().toUInt()); + if (attrs.hasAttribute(KXMLQLCVCWidgetInputMonitorValue)) + mon = uchar(attrs.value(KXMLQLCVCWidgetInputMonitorValue).toString().toUInt()); newSrc->setRange(min, max); + newSrc->setMonitor(mon); return newSrc; } @@ -1031,6 +1034,9 @@ bool VCWidget::saveXMLInput(QXmlStreamWriter *doc, doc->writeAttribute(KXMLQLCVCWidgetInputLowerValue, QString::number(src->lowerValue())); if (src->upperValue() != UCHAR_MAX) doc->writeAttribute(KXMLQLCVCWidgetInputUpperValue, QString::number(src->upperValue())); + if (src->monitorValue() != UCHAR_MAX) + doc->writeAttribute(KXMLQLCVCWidgetInputMonitorValue, QString::number(src->monitorValue())); + doc->writeEndElement(); } diff --git a/ui/src/virtualconsole/vcwidget.h b/ui/src/virtualconsole/vcwidget.h index 2f199bb252..aa15898760 100644 --- a/ui/src/virtualconsole/vcwidget.h +++ b/ui/src/virtualconsole/vcwidget.h @@ -65,6 +65,7 @@ class QFile; #define KXMLQLCVCWidgetInputChannel "Channel" #define KXMLQLCVCWidgetInputLowerValue "LowerValue" #define KXMLQLCVCWidgetInputUpperValue "UpperValue" +#define KXMLQLCVCWidgetInputMonitorValue "MonitorValue" #define KXMLQLCWindowState "WindowState" #define KXMLQLCWindowStateVisible "Visible" From b1170289af58d040a7db3fe80aaf6a95184cf20c Mon Sep 17 00:00:00 2001 From: Rob Kahle Date: Thu, 26 Jul 2018 02:01:56 -0400 Subject: [PATCH 002/212] Fixed the input selection widget UI file so it builds successfully --- ui/src/inputselectionwidget.ui | 66 ++++++++++++++++++++++++---------- 1 file changed, 48 insertions(+), 18 deletions(-) diff --git a/ui/src/inputselectionwidget.ui b/ui/src/inputselectionwidget.ui index 81ea90607c..586653c3d0 100644 --- a/ui/src/inputselectionwidget.ui +++ b/ui/src/inputselectionwidget.ui @@ -171,21 +171,39 @@ Custom feedback - - - 3 - - - 3 - - + + + + + 10 + + + + Lower + + + + + + + 255 + + + + + + + + 10 + + - Lower value + Upper - + 0 @@ -198,21 +216,33 @@ - - - - 255 + + + + + 10 + + + + Monitor - - - - Upper value + + + + 255 + m_upperSpin + m_lowerSpin + label_2 + label + m_monitorSpin + label_3 + m_keyInputGroup From f850f27ae07852dafa2883ee706b426c4f7d3ddb Mon Sep 17 00:00:00 2001 From: Lachlan Hicks <42596763+yestalgia@users.noreply.github.com> Date: Fri, 5 Jan 2024 15:48:00 +1100 Subject: [PATCH 003/212] Fix: Link to Release Add: release date --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5361556bc8..6402fc8357 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,8 @@ # Q Light Controller Plus -![GitHub release)](https://img.shields.io/github/v/release/mcallegari/qlcplus) -![QLC+ Github Actions CI Build](https://github.com/mcallegari/qlcplus/actions/workflows/build.yml/badge.svg) +[![GitHub release](https://img.shields.io/github/v/release/mcallegari/qlcplus) +![GitHub Release Date - Published_At](https://img.shields.io/github/release-date/mcallegari/qlcplus)](https://github.com/mcallegari/qlcplus/releases/latest) ## Introduction QLC+ is a powerful and user-friendly software designed for lighting control. Whether you're an experienced lighting professional or just getting started, QLC+ empowers you to take control of your lighting fixtures with ease. The primary goal of this project is to bring QLC+ to the level of available commercial software. From 97f509d9bad45273e4c0d905e5a798557e63144c Mon Sep 17 00:00:00 2001 From: Lachlan Hicks <42596763+yestalgia@users.noreply.github.com> Date: Sat, 6 Jan 2024 21:36:26 +1100 Subject: [PATCH 004/212] Add: Features --- README.md | 44 +++++++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 6402fc8357..84b44870a5 100644 --- a/README.md +++ b/README.md @@ -6,12 +6,20 @@ [![GitHub release](https://img.shields.io/github/v/release/mcallegari/qlcplus) ![GitHub Release Date - Published_At](https://img.shields.io/github/release-date/mcallegari/qlcplus)](https://github.com/mcallegari/qlcplus/releases/latest) +https://www.qlcplus.org/download + ## Introduction -QLC+ is a powerful and user-friendly software designed for lighting control. Whether you're an experienced lighting professional or just getting started, QLC+ empowers you to take control of your lighting fixtures with ease. The primary goal of this project is to bring QLC+ to the level of available commercial software. +QLC+ is powerful and user-friendly software designed to control lighting. Whether you're an experienced lighting professional or just getting started, QLC+ empowers you to take control of your lighting fixtures with ease. The primary goal of this project is to bring QLC+ to the level of available commercial software. QLC+ runs on Linux, Windows (7+), macOS (10.7+) and the Raspberry Pi. Copyright © Heikki Junnila, Massimo Callegari - +### Supported Protocols +![MIDI](https://img.shields.io/badge/MIDI-%23323330.svg?style=for-the-badge&logo=midi&logoColor=%23F7DF1E) +![OSC](https://img.shields.io/badge/OSC-%23323330.svg?style=for-the-badge&logo=aiohttp&logoColor=%23F7DF1E) +![HID](https://img.shields.io/badge/HID-%23323330.svg?style=for-the-badge&logo=applearcade&logoColor=%23F7DF1E) +![DMX](https://img.shields.io/badge/DMX-%23323330.svg?style=for-the-badge&logo=amazonec2&logoColor=%23F7DF1E) +![ArtNet](https://img.shields.io/badge/ArtNet-%23323330.svg?style=for-the-badge&logo=aiohttp&logoColor=%23F7DF1E) +![E1.31](https://img.shields.io/badge/E1.31-%23323330.svg?style=for-the-badge&logo=aiohttp&logoColor=%23F7DF1E) ### Key Resources: @@ -52,25 +60,20 @@ We welcome contributions from the community to help make QLC+ even better. Befor 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) + +### 🚧 Developers at work 🚧 -### 🚧 DEVELOPERS AT WORK 🚧 +If you're regularly updating QLC+ sources with git pull, you may encounter compiler warnings, errors, or unresolved symbols. This is because the source package is still in development. We strive to keep the GIT master branch free of critical errors; However, dependencies between objects can sometimes cause issues, requiring a full package recompilation rather than just updating recent changes. +[![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) -If you're compiling QLC+ from sources and you regularly do "git pull" -to get the latest sources, you probably end up seeing some -compiler warnings and errors from time to time. Since the whole source package -is under development, you might even encounter unresolved symbols etc. If such a thing occurs, you should do a "make -distclean" on qlcplus (top-most source directory) and then "qmake" and "make" -again. We attempt to keep the GIT master free of fatal errors and it should -compile all the time. However, some inter-object dependencies do get mixed up -sometimes and you need to compile the whole package instead of just the latest -changes. Sometimes even that doesn't work, because QLC+ installs its common -libraries to system directories, where (at least unixes) fetch them instead -of the source directory. In those cases, you might try going to the libs -directory, compile it with "make" and install with "make install" and then -attempt to re-compile the whole package with "make". -## Compiling And Installation +## Compiling and installation Please refer to the online wiki pages: https://github.com/mcallegari/qlcplus/wiki ## Requirements @@ -116,8 +119,10 @@ For developers wiki and code patches, go to: https://github.com/mcallegari/qlcplus ## Contributors + QLC+ owes its success to the dedication and expertise of numerous individuals who have generously contributed their time and skills. The following list recognizes those whose remarkable contributions have played a pivotal role in shaping QLC+ into what it is today. +![GitHub contributors](https://img.shields.io/github/contributors/mcallegari/qlcplus) ### QLC+ 5: * Eric Arnebäck (3D preview features) @@ -169,6 +174,10 @@ QLC+ owes its success to the dedication and expertise of numerous individuals wh * Matthew Jaggard (Velleman plugin) * Ptit Vachon (French translation) + + + + ## Apache 2.0 ![GitHub License](https://img.shields.io/github/license/mcallegari/qlcplus) @@ -177,3 +186,4 @@ Unless required by applicable law or agreed to in writing, software distributed ---- ![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) + From e43706eee10353193086160a65b46a21924e4c35 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Mon, 8 Jan 2024 21:17:52 +0100 Subject: [PATCH 005/212] engine: implement proper 16bit fading support --- engine/src/fadechannel.cpp | 139 ++++++++++++------ engine/src/fadechannel.h | 74 +++++----- engine/src/genericfader.cpp | 134 +++++++++++------ engine/src/genericfader.h | 16 ++ engine/src/qlcfixturemode.cpp | 32 +++- engine/src/qlcfixturemode.h | 12 +- engine/src/scene.cpp | 6 +- engine/test/fadechannel/fadechannel_test.cpp | 130 +++++++--------- .../test/genericfader/genericfader_test.cpp | 42 ++---- 9 files changed, 350 insertions(+), 235 deletions(-) diff --git a/engine/src/fadechannel.cpp b/engine/src/fadechannel.cpp index d1e3fd13dc..e7c417848a 100644 --- a/engine/src/fadechannel.cpp +++ b/engine/src/fadechannel.cpp @@ -20,6 +20,7 @@ #include #include +#include "qlcfixturemode.h" #include "fadechannel.h" #include "qlcchannel.h" #include "universe.h" @@ -29,8 +30,9 @@ FadeChannel::FadeChannel() : m_flags(0) , m_fixture(Fixture::invalidId()) , m_universe(Universe::invalid()) - , m_channel(QLCChannel::invalid()) + , m_primaryChannel(QLCChannel::invalid()) , m_address(QLCChannel::invalid()) + , m_channelRef(NULL) , m_start(0) , m_target(0) , m_current(0) @@ -44,8 +46,10 @@ FadeChannel::FadeChannel(const FadeChannel& ch) : m_flags(ch.m_flags) , m_fixture(ch.m_fixture) , m_universe(ch.m_universe) - , m_channel(ch.m_channel) + , m_primaryChannel(ch.m_primaryChannel) + , m_channels(ch.m_channels) , m_address(ch.m_address) + , m_channelRef(ch.m_channelRef) , m_start(ch.m_start) , m_target(ch.m_target) , m_current(ch.m_current) @@ -59,7 +63,7 @@ FadeChannel::FadeChannel(const FadeChannel& ch) FadeChannel::FadeChannel(const Doc *doc, quint32 fxi, quint32 channel) : m_flags(0) , m_fixture(fxi) - , m_channel(channel) + , m_channelRef(NULL) , m_start(0) , m_target(0) , m_current(0) @@ -67,6 +71,7 @@ FadeChannel::FadeChannel(const Doc *doc, quint32 fxi, quint32 channel) , m_fadeTime(0) , m_elapsed(0) { + m_channels.append(channel); autoDetect(doc); } @@ -81,7 +86,9 @@ FadeChannel &FadeChannel::operator=(const FadeChannel &fc) m_flags = fc.m_flags; m_fixture = fc.m_fixture; m_universe = fc.m_universe; - m_channel = fc.m_channel; + m_primaryChannel = fc.m_primaryChannel; + m_channels = fc.m_channels; + m_channelRef = fc.m_channelRef; m_address = fc.m_address; m_start = fc.m_start; m_target = fc.m_target; @@ -96,7 +103,7 @@ FadeChannel &FadeChannel::operator=(const FadeChannel &fc) bool FadeChannel::operator==(const FadeChannel& ch) const { - return (m_fixture == ch.m_fixture && m_channel == ch.m_channel); + return (m_fixture == ch.m_fixture && channel() == ch.channel()); } int FadeChannel::flags() const @@ -143,54 +150,48 @@ void FadeChannel::autoDetect(const Doc *doc) } else { + QLCFixtureMode *mode = fixture->fixtureMode(); m_universe = fixture->universe(); m_address = fixture->address(); // if the fixture was invalid at the beginning of this method // it means channel was an absolute address, so, fix it if (fixtureWasInvalid) - m_channel -= fixture->address(); + m_channels[0] -= fixture->address(); - const QLCChannel *channel = fixture->channel(m_channel); + quint32 chIndex = channel(); + m_primaryChannel = mode ? mode->primaryChannel(chIndex) : QLCChannel::invalid(); + m_channelRef = fixture->channel(chIndex); // non existing channel within fixture - if (channel == NULL) + if (m_channelRef == NULL) { addFlag(FadeChannel::HTP | FadeChannel::Intensity | FadeChannel::CanFade); return; } // autodetect the channel type - if (fixture->channelCanFade(m_channel)) + if (fixture->channelCanFade(chIndex)) addFlag(FadeChannel::CanFade); - if (channel != NULL && channel->group() == QLCChannel::Intensity) + if (m_channelRef != NULL && m_channelRef->group() == QLCChannel::Intensity) addFlag(FadeChannel::HTP | FadeChannel::Intensity); else addFlag(FadeChannel::LTP); - if (fixture->forcedHTPChannels().contains(int(m_channel))) + if (fixture->forcedHTPChannels().contains(int(chIndex))) { removeFlag(FadeChannel::LTP); addFlag(FadeChannel::HTP); } - else if (fixture->forcedLTPChannels().contains(int(m_channel))) + else if (fixture->forcedLTPChannels().contains(int(chIndex))) { removeFlag(FadeChannel::HTP); addFlag(FadeChannel::LTP); } - - if (channel != NULL && channel->controlByte() == QLCChannel::LSB) - addFlag(FadeChannel::Fine); } } -void FadeChannel::setFixture(const Doc *doc, quint32 id) -{ - m_fixture = id; - autoDetect(doc); -} - quint32 FadeChannel::fixture() const { return m_fixture; @@ -203,15 +204,33 @@ 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() +{ + return m_channels.count(); } quint32 FadeChannel::channel() const { - return m_channel; + return m_channels.isEmpty() ? QLCChannel::invalid() : m_channels.first(); +} + +quint32 FadeChannel::primaryChannel() const +{ + return m_primaryChannel; } quint32 FadeChannel::address() const @@ -224,42 +243,68 @@ quint32 FadeChannel::address() const quint32 FadeChannel::addressInUniverse() const { - return address() % UNIVERSE_SIZE; + quint32 addr = address(); + if (addr == QLCChannel::invalid()) + return QLCChannel::invalid(); + + return addr % UNIVERSE_SIZE; } -void FadeChannel::setStart(uchar value) +/************************************************************************ + * Values + ************************************************************************/ + +void FadeChannel::setStart(uchar value, int index) { - m_start = value; + if (m_channels.count() == 1) + m_start = value; + else + ((uchar *)&m_start)[index] = value; } -uchar FadeChannel::start() const +uchar FadeChannel::start(int index) const { - return uchar(m_start); + if (index >= m_channels.count()) + return uchar(m_start); + + return uchar(m_start >> (8 * (m_channels.count() - 1 - index))); } -void FadeChannel::setTarget(uchar value) +void FadeChannel::setTarget(uchar value, int index) { - m_target = value; + if (m_channels.count() == 1) + m_target = value; + else + ((uchar *)&m_target)[index] = value; } -uchar FadeChannel::target() const +uchar FadeChannel::target(int index) const { - return uchar(m_target); + if (index >= m_channels.count()) + return uchar(m_target); + + return uchar(m_target >> (8 * (m_channels.count() - 1 - index))); } -void FadeChannel::setCurrent(uchar value) +void FadeChannel::setCurrent(uchar value, int index) { - m_current = value; + if (m_channels.count() == 1) + m_current = value; + else + ((uchar *)&m_current)[index] = value; } -uchar FadeChannel::current() const +uchar FadeChannel::current(int index) const { - return uchar(m_current); + if (index >= m_channels.count()) + return uchar(m_current); + + return uchar(m_current >> (8 * (m_channels.count() - 1 - index))); } -uchar FadeChannel::current(qreal intensity) const +uchar FadeChannel::current(qreal intensity, int index) const { - return uchar(floor((qreal(m_current) * intensity) + 0.5)); + return uchar(floor((qreal(current(index)) * intensity) + 0.5)); } void FadeChannel::setReady(bool rdy) @@ -301,6 +346,7 @@ uchar FadeChannel::nextStep(uint ms) { if (elapsed() < UINT_MAX) setElapsed(elapsed() + ms); + return calculateCurrent(fadeTime(), elapsed()); } @@ -319,14 +365,11 @@ uchar FadeChannel::calculateCurrent(uint fadeTime, uint elapsedTime) } else { - // 16 bit fading works as long as MSB and LSB channels - // are targeting the same value. E.g. Red and Red Fine both at 158 - float val = (float(m_target - m_start) * (float(elapsedTime) / float(fadeTime))) + float(m_start); - long rval = lrintf(val * 256); - if (m_flags & Fine) - m_current = rval & 0xff; - else - m_current = rval / 256; + bool rampUp = m_target > m_start ? true : false; + m_current = rampUp ? m_target - m_start : m_start - m_target; + m_current = m_current * (qreal(elapsedTime) / qreal(fadeTime)); + m_current = rampUp ? m_start + m_current : m_start - m_current; + //qDebug() << "channel" << channel() << "start" << m_start << "target" << m_target << "current" << m_current << "fade" << fadeTime << "elapsed" << elapsedTime ; } return uchar(m_current); diff --git a/engine/src/fadechannel.h b/engine/src/fadechannel.h index 9f09fe05bb..75bd069ee0 100644 --- a/engine/src/fadechannel.h +++ b/engine/src/fadechannel.h @@ -81,59 +81,72 @@ 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(); + + /** Get the first (or master) channel handled by this fader */ quint32 channel() const; + /** 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 = 0); /** Get starting value. */ - uchar start() const; + uchar start(int index = 0) const; /** Set target value. */ - void setTarget(uchar value); + void setTarget(uchar value, int index = 0); /** Get target value. */ - uchar target() const; + uchar target(int index = 0) const; /** Set the current value. */ - void setCurrent(uchar value); + void setCurrent(uchar value, int index = 0); /** Get the current value. */ - uchar current() const; + uchar current(int index = 0) const; /** Get the current value, modified by $intensity. */ - uchar current(qreal intensity) const; + uchar current(qreal intensity, int index = 0) const; /** Mark this channel as ready (useful for writing LTP values only once). */ void setReady(bool rdy); @@ -177,14 +190,9 @@ class FadeChannel uchar calculateCurrent(uint fadeTime, uint elapsedTime); private: - quint32 m_fixture; - quint32 m_universe; - quint32 m_channel; - quint32 m_address; - - int m_start; - int m_target; - int m_current; + quint32 m_start; + quint32 m_target; + quint32 m_current; bool m_ready; uint m_fadeTime; diff --git a/engine/src/genericfader.cpp b/engine/src/genericfader.cpp index 0802b2073e..c8c3ce9760 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,38 @@ 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); + } + return fcFound; + } - fc.setCurrent(universe->preGMValue(fc.address())); + // new channel. Add to GenericFader + if (universe) + fc.setCurrent(universe->preGMValue(fc.address())); m_channels[hash] = fc; //qDebug() << "Added new fader with hash" << hash; + return &m_channels[hash]; } @@ -174,57 +207,68 @@ void GenericFader::write(Universe *universe) fc.setTarget(universe->preGMValue(address)); } - // Calculate the next step - if (m_paused) - value = fc.current(); - else - value = fc.nextStep(MasterTimer::tick()); + // counter used at the end to detect channels to remove + int removeCount = fc.channelCount(); - // Apply intensity to channels that can fade - if (fc.canFade()) + // iterate through all the channels handled by this fader + for (int i = 0; i < fc.channelCount(); i++) { - if ((flags & FadeChannel::CrossFade) && fc.fadeTime() == 0) + // Calculate the next step + if (i == 0 && m_paused == false) + fc.nextStep(MasterTimer::tick()); + + value = fc.current(i); + + // Apply intensity to channels that can fade + if (fc.canFade()) + { + if ((flags & FadeChannel::CrossFade) && fc.fadeTime() == 0) + { + // morph start <-> target depending on intensities + value = uchar(((qreal(fc.target(i) - fc.start(i)) * intensity()) + fc.start(i)) * parentIntensity()); + } + else if (flags & FadeChannel::Intensity) + { + value = fc.current(compIntensity, i); + } + } + + //qDebug() << "[GenericFader] >>> uni:" << universe->id() << ", address:" << (address + i) << ", value:" << value << "int:" << compIntensity; + if (flags & FadeChannel::Override) { - // morph start <-> target depending on intensities - value = uchar(((qreal(fc.target() - fc.start()) * intensity()) + fc.start()) * parentIntensity()); + universe->write(address + i, value, true); + continue; } - else if (flags & FadeChannel::Intensity) + else if (flags & FadeChannel::Relative) { - value = fc.current(compIntensity); + universe->writeRelative(address + i, value); + } + else if (flags & FadeChannel::Flashing) + { + universe->write(address + i, value, flags & FadeChannel::ForceLTP); + continue; + } + else + { + universe->writeBlended(address + i, value, m_blendMode); } - } - //qDebug() << "[GenericFader] >>> uni:" << universe->id() << ", address:" << address << ", value:" << value << "int:" << compIntensity; - if (flags & FadeChannel::Override) - { - universe->write(address, value, true); - continue; - } - else if (flags & FadeChannel::Relative) - { - universe->writeRelative(address, value); - } - else if (flags & FadeChannel::Flashing) - { - universe->write(address, value, flags & FadeChannel::ForceLTP); - continue; - } - else - { - universe->writeBlended(address, value, m_blendMode); - } + if (((flags & FadeChannel::Intensity) && + (flags & FadeChannel::HTP) && + m_blendMode == Universe::NormalBlend) || m_fadeOut) + { + // Remove all channels that reach their target _zero_ value. + // They have no effect either way so removing them saves a bit of CPU. + if (fc.current(i) == 0 && fc.target(i) == 0 && fc.isReady()) + removeCount--; + } - if (((flags & FadeChannel::Intensity) && - (flags & FadeChannel::HTP) && - m_blendMode == Universe::NormalBlend) || m_fadeOut) - { - // Remove all channels that reach their target _zero_ value. - // They have no effect either way so removing them saves a bit of CPU. - if (fc.current() == 0 && fc.target() == 0 && fc.isReady()) - it.remove(); + if (flags & FadeChannel::AutoRemove && value == fc.target(i)) + removeCount--; } - if (flags & FadeChannel::AutoRemove && value == fc.target()) + // check fader removal + if (removeCount == 0) it.remove(); } 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/qlcfixturemode.cpp b/engine/src/qlcfixturemode.cpp index 08baaaf329..9ae993c882 100644 --- a/engine/src/qlcfixturemode.cpp +++ b/engine/src/qlcfixturemode.cpp @@ -252,6 +252,11 @@ quint32 QLCFixtureMode::masterIntensityChannel() const return m_masterIntensityChannel; } +quint32 QLCFixtureMode::primaryChannel(quint32 chIndex) +{ + return m_secondaryMap.value(chIndex, QLCChannel::invalid()); +} + quint32 QLCFixtureMode::channelActsOn(quint32 chIndex) { return m_actsOnMap.value(chIndex, QLCChannel::invalid()); @@ -308,22 +313,39 @@ int QLCFixtureMode::headForChannel(quint32 chnum) const void QLCFixtureMode::cacheHeads() { + QLCChannel *lastChannel = NULL; + for (int i = 0; i < m_heads.size(); i++) { QLCFixtureHead& head(m_heads[i]); head.cacheChannels(this); } - for (int i = 0; i < m_channels.size(); i++) + for (quint32 i = 0; i < quint32(m_channels.size()); i++) { - if (m_channels.at(i)->group() == QLCChannel::Intensity && - m_channels.at(i)->controlByte() == QLCChannel::MSB && - m_channels.at(i)->colour() == QLCChannel::NoColour && + QLCChannel *channel = m_channels.at(i); + + /** Auto-detect master intensity channel */ + if (m_masterIntensityChannel == QLCChannel::invalid() && + channel->group() == QLCChannel::Intensity && + channel->controlByte() == QLCChannel::MSB && + channel->colour() == QLCChannel::NoColour && headForChannel(i) == -1) { m_masterIntensityChannel = i; - break; } + + /** Map secondary channels */ + if (lastChannel != NULL && + channel->group() == lastChannel->group() && + lastChannel->controlByte() == QLCChannel::MSB && + channel->controlByte() == QLCChannel::LSB) + { + //qDebug() << "Channel" << lastChannel->name() << "is primary and" << channel->name() << "is secondary"; + m_secondaryMap[i] = i - 1; + } + + lastChannel = channel; } } diff --git a/engine/src/qlcfixturemode.h b/engine/src/qlcfixturemode.h index adca973026..54485f3e1a 100644 --- a/engine/src/qlcfixturemode.h +++ b/engine/src/qlcfixturemode.h @@ -206,8 +206,13 @@ class QLCFixtureMode */ quint32 channelNumber(QLCChannel::Group group, QLCChannel::ControlByte cByte = QLCChannel::MSB) const; + /** Return the auto-detected channel index of the Fixture master dimmer for this mode */ quint32 masterIntensityChannel() const; + /** Return the index of the primary channel $chIndex relates to. + * Return invalid if not present */ + quint32 primaryChannel(quint32 chIndex); + /** Return the channel index on which the given $chIndex acts on. * Return invalid if not present */ quint32 channelActsOn(quint32 chIndex); @@ -218,9 +223,14 @@ class QLCFixtureMode QVector m_channels; /** Map of channel indices that act on other channels. - * These are stored as: < index, acts on index> */ + * These are stored as: */ QMap m_actsOnMap; + /** Map of channel indices that relate to some other primary channel. + * For example Pan Fine vs Pan, Red Fine vs Red, etc + * These are stored as: */ + QMap m_secondaryMap; + quint32 m_masterIntensityChannel; /********************************************************************* diff --git a/engine/src/scene.cpp b/engine/src/scene.cpp index 688ff2fb25..8a708d91d0 100644 --- a/engine/src/scene.cpp +++ b/engine/src/scene.cpp @@ -161,7 +161,7 @@ void Scene::setValue(const SceneValue& scv, bool blind, bool checkHTP) m_fadersMap[universe]->add(fc); } } - } + } } emit changed(this->id()); @@ -723,9 +723,9 @@ 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); diff --git a/engine/test/fadechannel/fadechannel_test.cpp b/engine/test/fadechannel/fadechannel_test.cpp index fcef4ef4f4..b52352b7c3 100644 --- a/engine/test/fadechannel/fadechannel_test.cpp +++ b/engine/test/fadechannel/fadechannel_test.cpp @@ -41,15 +41,14 @@ void FadeChannel_Test::address() fxi->setChannels(5); doc.addFixture(fxi); - FadeChannel fc; - fc.setChannel(&doc, 2); + FadeChannel fc(&doc, Fixture::invalidId(), 2); QCOMPARE(fc.address(), quint32(2)); - fc.setFixture(&doc, fxi->id()); - QCOMPARE(fc.address(), quint32(402)); + FadeChannel fc1(&doc, fxi->id(), 2); + QCOMPARE(fc1.address(), quint32(402)); - fc.setFixture(&doc, 12345); - QCOMPARE(fc.address(), quint32(2)); + FadeChannel fc2(&doc, 12345, QLCChannel::invalid()); + QCOMPARE(fc2.address(), QLCChannel::invalid()); } void FadeChannel_Test::addressInUniverse() @@ -60,45 +59,34 @@ void FadeChannel_Test::addressInUniverse() fxi->setChannels(5); doc.addFixture(fxi); - FadeChannel fc; - fc.setChannel(&doc, 2); + FadeChannel fc(&doc, Fixture::invalidId(), 2); QCOMPARE(fc.addressInUniverse(), quint32(2)); - fc.setFixture(&doc, fxi->id()); - QCOMPARE(fc.addressInUniverse(), quint32(2)); + FadeChannel fc1(&doc, fxi->id(), QLCChannel::invalid()); + QCOMPARE(fc1.addressInUniverse(), QLCChannel::invalid()); - fc.setFixture(&doc, 12345); - QCOMPARE(fc.addressInUniverse(), quint32(2)); + FadeChannel fc2(&doc, 12345, QLCChannel::invalid()); + QCOMPARE(fc2.addressInUniverse(), QLCChannel::invalid()); } void FadeChannel_Test::comparison() { Doc doc(this); - FadeChannel ch1; - ch1.setFixture(&doc, 0); - ch1.setChannel(&doc, 0); - - FadeChannel ch2; - ch2.setFixture(&doc, 1); - ch2.setChannel(&doc, 0); - QVERIFY((ch1 == ch2) == false); - - ch1.setFixture(&doc, 1); - QVERIFY((ch1 == ch2) == true); - - ch1.setChannel(&doc, 1); + FadeChannel ch1(&doc, 0, 0); + FadeChannel ch2(&doc, 1, 0); + FadeChannel ch3(&doc, 0, 0); QVERIFY((ch1 == ch2) == false); + QVERIFY((ch1 == ch3) == true); } void FadeChannel_Test::type() { Doc doc(this); - FadeChannel fc; + FadeChannel fc(&doc, Fixture::invalidId(), 2); // Only a channel given, no fixture at the address -> intensity - fc.setChannel(&doc, 2); QCOMPARE(fc.flags() & FadeChannel::HTP, (int)FadeChannel::HTP); QCOMPARE(fc.flags() & FadeChannel::Intensity, (int)FadeChannel::Intensity); QCOMPARE(fc.flags() & FadeChannel::CanFade, (int)FadeChannel::CanFade); @@ -109,10 +97,10 @@ void FadeChannel_Test::type() doc.addFixture(fxi); // Fixture and channel given, fixture is a dimmer -> intensity - fc.setFixture(&doc, fxi->id()); - QCOMPARE(fc.flags() & FadeChannel::HTP, (int)FadeChannel::HTP); - QCOMPARE(fc.flags() & FadeChannel::Intensity, (int)FadeChannel::Intensity); - QCOMPARE(fc.flags() & FadeChannel::CanFade, (int)FadeChannel::CanFade); + FadeChannel fc1(&doc, fxi->id(), 2); + QCOMPARE(fc1.flags() & FadeChannel::HTP, (int)FadeChannel::HTP); + QCOMPARE(fc1.flags() & FadeChannel::Intensity, (int)FadeChannel::Intensity); + QCOMPARE(fc1.flags() & FadeChannel::CanFade, (int)FadeChannel::CanFade); QDir dir(INTERNAL_FIXTUREDIR); dir.setFilter(QDir::Files); @@ -131,44 +119,39 @@ void FadeChannel_Test::type() doc.addFixture(fxi); // Fixture and channel given, but channel is beyond fixture's channels -> intensity - fc.setFixture(&doc, fxi->id()); - fc.setChannel(&doc, 50); - QCOMPARE(fc.flags() & FadeChannel::HTP, (int)FadeChannel::HTP); - QCOMPARE(fc.flags() & FadeChannel::Intensity, (int)FadeChannel::Intensity); - QCOMPARE(fc.flags() & FadeChannel::CanFade, (int)FadeChannel::CanFade); + FadeChannel fc2(&doc, fxi->id(), 50); + QCOMPARE(fc2.flags() & FadeChannel::HTP, (int)FadeChannel::HTP); + QCOMPARE(fc2.flags() & FadeChannel::Intensity, (int)FadeChannel::Intensity); + QCOMPARE(fc2.flags() & FadeChannel::CanFade, (int)FadeChannel::CanFade); // Only a channel given, no fixture given but a fixture occupies the address. // Check that reverse address -> fixture lookup works. - fc.setFixture(&doc, Fixture::invalidId()); - fc.setChannel(&doc, 2); - QCOMPARE(fc.flags() & FadeChannel::LTP, (int)FadeChannel::LTP); - QCOMPARE(fc.flags() & FadeChannel::CanFade, (int)FadeChannel::CanFade); + FadeChannel fc3(&doc, Fixture::invalidId(), 2); + QCOMPARE(fc3.flags() & FadeChannel::LTP, (int)FadeChannel::LTP); + QCOMPARE(fc3.flags() & FadeChannel::CanFade, (int)FadeChannel::CanFade); // Fixture and channel given, but fixture doesn't exist -> intensity - fc.setFixture(&doc, 12345); - fc.setChannel(&doc, 2); - QCOMPARE(fc.flags() & FadeChannel::HTP, (int)FadeChannel::HTP); - QCOMPARE(fc.flags() & FadeChannel::Intensity, (int)FadeChannel::Intensity); - QCOMPARE(fc.flags() & FadeChannel::CanFade, (int)FadeChannel::CanFade); + FadeChannel fc4(&doc, 12345, 2); + QCOMPARE(fc4.flags() & FadeChannel::HTP, (int)FadeChannel::HTP); + QCOMPARE(fc4.flags() & FadeChannel::Intensity, (int)FadeChannel::Intensity); + QCOMPARE(fc4.flags() & FadeChannel::CanFade, (int)FadeChannel::CanFade); // channel 3 cannot fade fxi->setChannelCanFade(3, false); - fc.setFixture(&doc, fxi->id()); - fc.setChannel(&doc, 3); - QCOMPARE(fc.flags() & FadeChannel::LTP, (int)FadeChannel::LTP); - QCOMPARE(fc.flags() & FadeChannel::CanFade, 0); + FadeChannel fc5(&doc, fxi->id(), 3); + QCOMPARE(fc5.flags() & FadeChannel::LTP, (int)FadeChannel::LTP); + QCOMPARE(fc5.flags() & FadeChannel::CanFade, 0); // force channel 0 (Pan) to be HTP QList forced; forced << 0; fxi->setForcedHTPChannels(forced); - fc.setFixture(&doc, fxi->id()); - fc.setChannel(&doc, 0); - QCOMPARE(fc.flags() & FadeChannel::HTP, (int)FadeChannel::HTP); - QCOMPARE(fc.flags() & FadeChannel::LTP, 0); - QCOMPARE(fc.flags() & FadeChannel::CanFade, (int)FadeChannel::CanFade); + FadeChannel fc6(&doc, fxi->id(), 0); + QCOMPARE(fc6.flags() & FadeChannel::HTP, (int)FadeChannel::HTP); + QCOMPARE(fc6.flags() & FadeChannel::LTP, 0); + QCOMPARE(fc6.flags() & FadeChannel::CanFade, (int)FadeChannel::CanFade); // add another generic dimmer fxi = new Fixture(&doc); @@ -180,11 +163,10 @@ void FadeChannel_Test::type() fxi->setForcedLTPChannels(forced); doc.addFixture(fxi); - fc.setFixture(&doc, fxi->id()); - fc.setChannel(&doc, 2); - QCOMPARE(fc.flags() & FadeChannel::LTP, (int)FadeChannel::LTP); - QCOMPARE(fc.flags() & FadeChannel::HTP, 0); - QCOMPARE(fc.flags() & FadeChannel::CanFade, (int)FadeChannel::CanFade); + FadeChannel fc7(&doc, fxi->id(), 2); + QCOMPARE(fc7.flags() & FadeChannel::LTP, (int)FadeChannel::LTP); + QCOMPARE(fc7.flags() & FadeChannel::HTP, 0); + QCOMPARE(fc7.flags() & FadeChannel::CanFade, (int)FadeChannel::CanFade); // unset a flag fc.removeFlag(FadeChannel::CanFade); @@ -371,24 +353,24 @@ void FadeChannel_Test::calculateCurrent() fch.setTarget(101); fch.setReady(false); QCOMPARE(fch.calculateCurrent(200, 0), uchar(245)); - QCOMPARE(fch.calculateCurrent(200, 1), uchar(244)); - QCOMPARE(fch.calculateCurrent(200, 2), uchar(243)); - QCOMPARE(fch.calculateCurrent(200, 3), uchar(242)); - QCOMPARE(fch.calculateCurrent(200, 4), uchar(242)); - QCOMPARE(fch.calculateCurrent(200, 5), uchar(241)); - QCOMPARE(fch.calculateCurrent(200, 6), uchar(240)); - QCOMPARE(fch.calculateCurrent(200, 7), uchar(239)); - QCOMPARE(fch.calculateCurrent(200, 8), uchar(239)); - QCOMPARE(fch.calculateCurrent(200, 9), uchar(238)); - QCOMPARE(fch.calculateCurrent(200, 10), uchar(237)); - QCOMPARE(fch.calculateCurrent(200, 11), uchar(237)); + QCOMPARE(fch.calculateCurrent(200, 1), uchar(245)); + QCOMPARE(fch.calculateCurrent(200, 2), uchar(244)); + QCOMPARE(fch.calculateCurrent(200, 3), uchar(243)); + QCOMPARE(fch.calculateCurrent(200, 4), uchar(243)); + QCOMPARE(fch.calculateCurrent(200, 5), uchar(242)); + QCOMPARE(fch.calculateCurrent(200, 6), uchar(241)); + QCOMPARE(fch.calculateCurrent(200, 7), uchar(240)); + QCOMPARE(fch.calculateCurrent(200, 8), uchar(240)); + QCOMPARE(fch.calculateCurrent(200, 9), uchar(239)); + QCOMPARE(fch.calculateCurrent(200, 10), uchar(238)); + QCOMPARE(fch.calculateCurrent(200, 11), uchar(238)); // Skip... QCOMPARE(fch.calculateCurrent(200, 100), uchar(173)); - QCOMPARE(fch.calculateCurrent(200, 101), uchar(172)); - QCOMPARE(fch.calculateCurrent(200, 102), uchar(171)); + QCOMPARE(fch.calculateCurrent(200, 101), uchar(173)); + QCOMPARE(fch.calculateCurrent(200, 102), uchar(172)); // Skip... - QCOMPARE(fch.calculateCurrent(200, 198), uchar(102)); - QCOMPARE(fch.calculateCurrent(200, 199), uchar(101)); + QCOMPARE(fch.calculateCurrent(200, 198), uchar(103)); + QCOMPARE(fch.calculateCurrent(200, 199), uchar(102)); QCOMPARE(fch.calculateCurrent(200, 200), uchar(101)); } diff --git a/engine/test/genericfader/genericfader_test.cpp b/engine/test/genericfader/genericfader_test.cpp index a80c524e2d..3a26c1b214 100644 --- a/engine/test/genericfader/genericfader_test.cpp +++ b/engine/test/genericfader/genericfader_test.cpp @@ -67,12 +67,10 @@ void GenericFader_Test::addRemove() QList ua = m_doc->inputOutputMap()->universes(); QSharedPointer fader = QSharedPointer(new GenericFader()); - FadeChannel fc; - fc.setFixture(m_doc, 0); - fc.setChannel(m_doc, 0); - - FadeChannel wrong; - fc.setFixture(m_doc, 0); + FadeChannel fc(m_doc, 0, 0); + FadeChannel fc1(m_doc, 0, 1); + FadeChannel fc2(m_doc, 0, 2); + FadeChannel wrong(m_doc, 0, QLCChannel::invalid()); quint32 chHash = GenericFader::channelHash(fc.fixture(), fc.channel()); QCOMPARE(fader->m_channels.count(), 0); @@ -86,22 +84,19 @@ void GenericFader_Test::addRemove() QVERIFY(fader->m_channels.contains(chHash) == true); QCOMPARE(fader->m_channels.count(), 1); - FadeChannel *fc1 = fader->getChannelFader(m_doc, ua[0], 0, 0); - fader->remove(fc1); + FadeChannel *fc3 = fader->getChannelFader(m_doc, ua[0], 0, 0); + fader->remove(fc3); QVERIFY(fader->m_channels.contains(chHash) == false); QCOMPARE(fader->m_channels.count(), 0); - fc.setChannel(m_doc, 0); fader->add(fc); QVERIFY(fader->m_channels.contains(chHash) == true); - fc.setChannel(m_doc, 1); - fader->add(fc); + fader->add(fc1); chHash = GenericFader::channelHash(fc.fixture(), fc.channel()); QVERIFY(fader->m_channels.contains(chHash) == true); - fc.setChannel(m_doc, 2); - fader->add(fc); + fader->add(fc2); chHash = GenericFader::channelHash(fc.fixture(), fc.channel()); QVERIFY(fader->m_channels.contains(chHash) == true); QCOMPARE(fader->m_channels.count(), 3); @@ -109,8 +104,6 @@ void GenericFader_Test::addRemove() fader->removeAll(); QCOMPARE(fader->m_channels.count(), 0); - fc.setFixture(m_doc, 0); - fc.setChannel(m_doc, 0); fc.setTarget(127); fader->add(fc); chHash = GenericFader::channelHash(fc.fixture(), fc.channel()); @@ -133,9 +126,7 @@ void GenericFader_Test::writeZeroFade() QList ua = m_doc->inputOutputMap()->universes(); QSharedPointer fader = ua[0]->requestFader(); - FadeChannel fc; - fc.setFixture(m_doc, 0); - fc.setChannel(m_doc, 5); + FadeChannel fc(m_doc, 0, 5); fc.setStart(0); fc.setTarget(255); fc.setFadeTime(0); @@ -151,9 +142,7 @@ void GenericFader_Test::writeLoop() QList ua = m_doc->inputOutputMap()->universes(); QSharedPointer fader = ua[0]->requestFader(); - FadeChannel fc; - fc.setFixture(m_doc, 0); - fc.setChannel(m_doc, 5); + FadeChannel fc(m_doc, 0, 5); fc.setStart(0); fc.setTarget(250); fc.setFadeTime(1000); @@ -178,19 +167,20 @@ void GenericFader_Test::adjustIntensity() QList ua = m_doc->inputOutputMap()->universes(); QSharedPointer fader = ua[0]->requestFader(); - FadeChannel fc; + FadeChannel fc(m_doc, 0, 5); + FadeChannel fc1(m_doc, 0, 0); // HTP channel - fc.setFixture(m_doc, 0); - fc.setChannel(m_doc, 5); fc.setStart(0); fc.setTarget(250); fc.setFadeTime(1000); fader->add(fc); // LTP channel - fc.setChannel(m_doc, 0); - fader->add(fc); + fc1.setStart(0); + fc1.setTarget(250); + fc1.setFadeTime(1000); + fader->add(fc1); qreal intensity = 0.5; fader->adjustIntensity(intensity); From 175fc2df8e009d1837047679232a7be511d79a80 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Tue, 9 Jan 2024 19:32:09 +0100 Subject: [PATCH 006/212] engine: fix 16bit blending from another Scene --- engine/src/fadechannel.cpp | 6 ++++++ engine/src/fadechannel.h | 5 +++++ engine/src/scene.cpp | 3 ++- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/engine/src/fadechannel.cpp b/engine/src/fadechannel.cpp index e7c417848a..4d2e1668da 100644 --- a/engine/src/fadechannel.cpp +++ b/engine/src/fadechannel.cpp @@ -228,6 +228,12 @@ quint32 FadeChannel::channel() const 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; diff --git a/engine/src/fadechannel.h b/engine/src/fadechannel.h index 75bd069ee0..d89970d71f 100644 --- a/engine/src/fadechannel.h +++ b/engine/src/fadechannel.h @@ -96,6 +96,11 @@ class FadeChannel /** 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; diff --git a/engine/src/scene.cpp b/engine/src/scene.cpp index 8a708d91d0..3732ba67fc 100644 --- a/engine/src/scene.cpp +++ b/engine/src/scene.cpp @@ -738,8 +738,9 @@ void Scene::processValue(MasterTimer *timer, QList ua, uint fadeIn, S Scene *blendScene = qobject_cast(doc()->function(blendFunctionID())); if (blendScene != NULL && blendScene->checkValue(scv)) { + int chIndex = fc->channelIndex(scv.channel); 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(); } From 40d210c78e254ca6b93797ad7e4b4c9eef7011a9 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Tue, 9 Jan 2024 19:54:32 +0100 Subject: [PATCH 007/212] engine: fix 16bit Scene fade out --- engine/src/genericfader.cpp | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/engine/src/genericfader.cpp b/engine/src/genericfader.cpp index c8c3ce9760..889ad78dc6 100644 --- a/engine/src/genericfader.cpp +++ b/engine/src/genericfader.cpp @@ -331,24 +331,27 @@ void GenericFader::setFadeOut(bool enable, uint fadeTime) { m_fadeOut = enable; - if (fadeTime) + if (fadeTime == 0) + return; + + QMutableHashIterator it(m_channels); + while (it.hasNext() == true) { - QMutableHashIterator it(m_channels); - while (it.hasNext() == true) + FadeChannel& fc(it.next().value()); + + // non-intensity channels (eg LTP) should fade + // to the current universe value + if ((fc.flags() & FadeChannel::Intensity) == 0) + fc.addFlag(FadeChannel::SetTarget); + + for (int i = 0; i < fc.channelCount(); i++) { - FadeChannel& fc(it.next().value()); - - // non-intensity channels (eg LTP) should fade - // to the current universe value - if ((fc.flags() & FadeChannel::Intensity) == 0) - fc.addFlag(FadeChannel::SetTarget); - - fc.setStart(fc.current()); - fc.setTarget(0); - fc.setElapsed(0); - fc.setReady(false); - fc.setFadeTime(fc.canFade() ? fadeTime : 0); + fc.setStart(fc.current(), i); + fc.setTarget(0, i); } + fc.setElapsed(0); + fc.setReady(false); + fc.setFadeTime(fc.canFade() ? fadeTime : 0); } } From ceec55dc4e2968a553c726a31e9cddc225205751 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Tue, 9 Jan 2024 21:04:46 +0100 Subject: [PATCH 008/212] engine: fix 16bit byte order --- engine/src/fadechannel.cpp | 35 ++++++++++------------------------- engine/src/fadechannel.h | 2 +- engine/src/genericfader.cpp | 16 ++++++++-------- engine/src/scene.cpp | 8 ++++---- 4 files changed, 23 insertions(+), 38 deletions(-) diff --git a/engine/src/fadechannel.cpp b/engine/src/fadechannel.cpp index 4d2e1668da..3ae301f832 100644 --- a/engine/src/fadechannel.cpp +++ b/engine/src/fadechannel.cpp @@ -218,8 +218,11 @@ void FadeChannel::addChannel(quint32 num) } } -int FadeChannel::channelCount() +int FadeChannel::channelCount() const { + if (m_channels.isEmpty()) + return 1; + return m_channels.count(); } @@ -262,50 +265,32 @@ quint32 FadeChannel::addressInUniverse() const void FadeChannel::setStart(uchar value, int index) { - if (m_channels.count() == 1) - m_start = value; - else - ((uchar *)&m_start)[index] = value; + ((uchar *)&m_start)[channelCount() - 1 - index] = value; } uchar FadeChannel::start(int index) const { - if (index >= m_channels.count()) - return uchar(m_start); - - return uchar(m_start >> (8 * (m_channels.count() - 1 - index))); + return ((uchar *)&m_start)[channelCount() - 1 - index]; } void FadeChannel::setTarget(uchar value, int index) { - if (m_channels.count() == 1) - m_target = value; - else - ((uchar *)&m_target)[index] = value; + ((uchar *)&m_target)[channelCount() - 1 - index] = value; } uchar FadeChannel::target(int index) const { - if (index >= m_channels.count()) - return uchar(m_target); - - return uchar(m_target >> (8 * (m_channels.count() - 1 - index))); + return ((uchar *)&m_target)[channelCount() - 1 - index]; } void FadeChannel::setCurrent(uchar value, int index) { - if (m_channels.count() == 1) - m_current = value; - else - ((uchar *)&m_current)[index] = value; + ((uchar *)&m_current)[channelCount() - 1 - index] = value; } uchar FadeChannel::current(int index) const { - if (index >= m_channels.count()) - return uchar(m_current); - - return uchar(m_current >> (8 * (m_channels.count() - 1 - index))); + return ((uchar *)&m_current)[channelCount() - 1 - index]; } uchar FadeChannel::current(qreal intensity, int index) const diff --git a/engine/src/fadechannel.h b/engine/src/fadechannel.h index d89970d71f..8a8af121aa 100644 --- a/engine/src/fadechannel.h +++ b/engine/src/fadechannel.h @@ -91,7 +91,7 @@ class FadeChannel void addChannel(quint32 num); /** Get the number of channels handled by this fader */ - int channelCount(); + int channelCount() const; /** Get the first (or master) channel handled by this fader */ quint32 channel() const; diff --git a/engine/src/genericfader.cpp b/engine/src/genericfader.cpp index 889ad78dc6..f6452df8f2 100644 --- a/engine/src/genericfader.cpp +++ b/engine/src/genericfader.cpp @@ -200,19 +200,19 @@ void GenericFader::write(Universe *universe) int address = int(fc.addressInUniverse()); uchar value; - if (flags & FadeChannel::SetTarget) - { - fc.removeFlag(FadeChannel::SetTarget); - fc.addFlag(FadeChannel::AutoRemove); - fc.setTarget(universe->preGMValue(address)); - } - // counter used at the end to detect channels to remove int removeCount = fc.channelCount(); // iterate through all the channels handled by this fader for (int i = 0; i < fc.channelCount(); i++) { + if (flags & FadeChannel::SetTarget) + { + fc.removeFlag(FadeChannel::SetTarget); + fc.addFlag(FadeChannel::AutoRemove); + fc.setTarget(universe->preGMValue(address + i), i); + } + // Calculate the next step if (i == 0 && m_paused == false) fc.nextStep(MasterTimer::tick()); @@ -346,7 +346,7 @@ void GenericFader::setFadeOut(bool enable, uint fadeTime) for (int i = 0; i < fc.channelCount(); i++) { - fc.setStart(fc.current(), i); + fc.setStart(fc.current(i), i); fc.setTarget(0, i); } fc.setElapsed(0); diff --git a/engine/src/scene.cpp b/engine/src/scene.cpp index 3732ba67fc..25f6350554 100644 --- a/engine/src/scene.cpp +++ b/engine/src/scene.cpp @@ -729,6 +729,7 @@ void Scene::processValue(MasterTimer *timer, QList ua, uint fadeIn, S } 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 @@ -738,7 +739,6 @@ void Scene::processValue(MasterTimer *timer, QList ua, uint fadeIn, S Scene *blendScene = qobject_cast(doc()->function(blendFunctionID())); if (blendScene != NULL && blendScene->checkValue(scv)) { - int chIndex = fc->channelIndex(scv.channel); fc->addFlag(FadeChannel::CrossFade); fc->setCurrent(blendScene->value(scv.fxi, scv.channel), chIndex); qDebug() << "----- BLEND from Scene" << blendScene->name() @@ -747,11 +747,11 @@ void Scene::processValue(MasterTimer *timer, QList ua, uint fadeIn, S } 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) { From ce0526580a89ca3375e237706488417f2d43d529 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Thu, 11 Jan 2024 00:41:05 +0100 Subject: [PATCH 009/212] engine: fix 16bit LSB initialization --- engine/src/genericfader.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/engine/src/genericfader.cpp b/engine/src/genericfader.cpp index f6452df8f2..dd75e67237 100644 --- a/engine/src/genericfader.cpp +++ b/engine/src/genericfader.cpp @@ -161,14 +161,17 @@ FadeChannel *GenericFader::getChannelFader(const Doc *doc, Universe *universe, q { qDebug() << "Adding channel to primary" << channel; fcFound->addChannel(channel); + if (universe) + fcFound->setCurrent(universe->preGMValue(fcFound->address() + 1), 1); } return fcFound; } - // new channel. Add to GenericFader + // 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; From 6611b14a0e57ee66f7e5bf9e83fb7606ce231b67 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Thu, 11 Jan 2024 00:41:32 +0100 Subject: [PATCH 010/212] engine: clip EFX negative values --- engine/src/efx.cpp | 6 +++--- engine/src/efx.h | 2 +- engine/src/efxfixture.cpp | 8 +++++++- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/engine/src/efx.cpp b/engine/src/efx.cpp index b8ef16de40..ac303d744a 100644 --- a/engine/src/efx.cpp +++ b/engine/src/efx.cpp @@ -272,7 +272,7 @@ void EFX::preview(QPolygonF &polygon, Function::Direction direction, int startOf } } -void EFX::calculatePoint(Function::Direction direction, int startOffset, float iterator, float* x, float* y) const +void EFX::calculatePoint(Function::Direction direction, int startOffset, float iterator, float *x, float *y) const { iterator = calculateDirection(direction, iterator); iterator += convertOffset(startOffset + getAttributeValue(StartOffset)); @@ -283,7 +283,7 @@ void EFX::calculatePoint(Function::Direction direction, int startOffset, float i calculatePoint(iterator, x, y); } -void EFX::rotateAndScale(float* x, float* y) const +void EFX::rotateAndScale(float *x, float *y) const { float xx = *x; float yy = *y; @@ -330,7 +330,7 @@ float EFX::calculateDirection(Function::Direction direction, float iterator) con } // this function should map from 0..M_PI * 2 -> -1..1 -void EFX::calculatePoint(float iterator, float* x, float* y) const +void EFX::calculatePoint(float iterator, float *x, float *y) const { switch (algorithm()) { diff --git a/engine/src/efx.h b/engine/src/efx.h index 3c248bdf94..1c7d3e6767 100644 --- a/engine/src/efx.h +++ b/engine/src/efx.h @@ -183,7 +183,7 @@ class EFX : public Function * @param x Used to store the calculated X coordinate (output) * @param y Used to store the calculated Y coordinate (output) */ - void calculatePoint(Function::Direction direction, int startOffset, float iterator, float* x, float* y) const; + void calculatePoint(Function::Direction direction, int startOffset, float iterator, float *x, float *y) const; private: diff --git a/engine/src/efxfixture.cpp b/engine/src/efxfixture.cpp index 144b2ec524..5ea670a520 100644 --- a/engine/src/efxfixture.cpp +++ b/engine/src/efxfixture.cpp @@ -475,7 +475,7 @@ void EFXFixture::updateFaderValues(FadeChannel *fc, uchar value) void EFXFixture::setPointPanTilt(QList universes, QSharedPointer fader, float pan, float tilt) { - Fixture* fxi = doc()->fixture(head().fxi); + Fixture *fxi = doc()->fixture(head().fxi); Q_ASSERT(fxi != NULL); Universe *uni = universes[universe()]; @@ -486,6 +486,12 @@ void EFXFixture::setPointPanTilt(QList universes, QSharedPointerchannelNumber(QLCChannel::Tilt, QLCChannel::MSB, head().head); quint32 tiltLsbChannel = fxi->channelNumber(QLCChannel::Tilt, QLCChannel::LSB, head().head); + if (pan < 0) + pan = 0; + + if (tilt < 0) + tilt = 0; + /* Write coarse point data to universes */ if (panMsbChannel != QLCChannel::invalid() && !fader.isNull()) { From 1ba6ce0db5073f48e7c53bafa575fade5046960c Mon Sep 17 00:00:00 2001 From: Lachlan Hicks <42596763+yestalgia@users.noreply.github.com> Date: Fri, 12 Jan 2024 21:04:42 +1100 Subject: [PATCH 011/212] Fix: Links for Protocol Badges Add: OS2L badge --- README.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 84b44870a5..51c44cb083 100644 --- a/README.md +++ b/README.md @@ -14,12 +14,14 @@ QLC+ runs on Linux, Windows (7+), macOS (10.7+) 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) -![OSC](https://img.shields.io/badge/OSC-%23323330.svg?style=for-the-badge&logo=aiohttp&logoColor=%23F7DF1E) -![HID](https://img.shields.io/badge/HID-%23323330.svg?style=for-the-badge&logo=applearcade&logoColor=%23F7DF1E) -![DMX](https://img.shields.io/badge/DMX-%23323330.svg?style=for-the-badge&logo=amazonec2&logoColor=%23F7DF1E) -![ArtNet](https://img.shields.io/badge/ArtNet-%23323330.svg?style=for-the-badge&logo=aiohttp&logoColor=%23F7DF1E) -![E1.31](https://img.shields.io/badge/E1.31-%23323330.svg?style=for-the-badge&logo=aiohttp&logoColor=%23F7DF1E) +[![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:
From 2071830cd769c5d975ea3a672ff08646c187e0e9 Mon Sep 17 00:00:00 2001 From: Lachlan Hicks <42596763+yestalgia@users.noreply.github.com> Date: Sat, 13 Jan 2024 00:04:03 +1100 Subject: [PATCH 012/212] Style: Markdown bullet list 3 spaces per codacy --- README.md | 126 +++++++++++++++++++++++++++--------------------------- 1 file changed, 63 insertions(+), 63 deletions(-) diff --git a/README.md b/README.md index 51c44cb083..3a2b658d68 100644 --- a/README.md +++ b/README.md @@ -82,34 +82,34 @@ Please refer to the online wiki pages: https://github.com/mcallegari/qlcplus/wik ### Linux -* Qt >= 5.0 development libraries & tools -* libudev-dev, libmad0-dev, libsndfile1-dev, libfftw3-dev -* DMX USB plugin: libftdi-dev, pkg-config -* MIDI plugin: libasound, libasound-dev, pkg-config -* OLA plugin: libola, ola-dev, pkg-config (see libs/olaout/README) -* uDMX plugin: libusb, libusb-dev, pkg-config -* Peperoni plugin: libusb, libusb-dev, pkg-config -* Velleman plugin: **Not available** +* Qt >= 5.0 development libraries & tools +* libudev-dev, libmad0-dev, libsndfile1-dev, libfftw3-dev +* DMX USB plugin: libftdi-dev, pkg-config +* MIDI plugin: libasound, libasound-dev, pkg-config +* OLA plugin: libola, ola-dev, pkg-config (see libs/olaout/README) +* uDMX plugin: libusb, libusb-dev, pkg-config +* Peperoni plugin: libusb, libusb-dev, pkg-config +* Velleman plugin: **Not available** ### Windows -* MSYS2 environment (https://msys2.github.io/) -* DMX USB plugin: D2XX driver & development package (http://www.ftdichip.com/Drivers/D2XX.htm) -* ENTTEC Wing plugin: D2XX driver & development package (http://www.ftdichip.com/Drivers/D2XX.htm) -* OLA plugin: **Not available** -* Velleman plugin: K8062 SDK from www.velleman.eu +* MSYS2 environment (https://msys2.github.io/) +* DMX USB plugin: D2XX driver & development package (http://www.ftdichip.com/Drivers/D2XX.htm) +* ENTTEC Wing plugin: D2XX driver & development package (http://www.ftdichip.com/Drivers/D2XX.htm) +* OLA plugin: **Not available** +* Velleman plugin: K8062 SDK from www.velleman.eu ### Mac OS X -* XCode (http://developer.apple.com/technologies/tools/xcode.html) -* Qt >= 5.0.x (http://download.qt.io/official_releases/qt/) -* macports (https://www.macports.org/) -* DMX USB plugin: macports, libftdi-dev, pkg-config -* OLA plugin: libola, ola-dev, pkg-config (see libs/olaout/README) -* uDMX plugin: macports, libusb-compat, pkg-config -* Peperoni plugin: macports, libusb-compat, pkg-config -* Velleman plugin: **Not available** +* XCode (http://developer.apple.com/technologies/tools/xcode.html) +* Qt >= 5.0.x (http://download.qt.io/official_releases/qt/) +* macports (https://www.macports.org/) +* DMX USB plugin: macports, libftdi-dev, pkg-config +* OLA plugin: libola, ola-dev, pkg-config (see libs/olaout/README) +* uDMX plugin: macports, libusb-compat, pkg-config +* Peperoni plugin: macports, libusb-compat, pkg-config +* Velleman plugin: **Not available** ## Support & Bug Reports @@ -127,54 +127,54 @@ QLC+ owes its success to the dedication and expertise of numerous individuals wh ![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) +* 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) +* 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) +* 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) From ba97565808aa3fca1c4942a715d8278e2d4fe2f9 Mon Sep 17 00:00:00 2001 From: Lachlan Hicks <42596763+yestalgia@users.noreply.github.com> Date: Sat, 13 Jan 2024 00:05:24 +1100 Subject: [PATCH 013/212] Style: remove colons from headings --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 3a2b658d68..18c263d40c 100644 --- a/README.md +++ b/README.md @@ -125,7 +125,7 @@ https://github.com/mcallegari/qlcplus 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: +### QLC+ 5 * Eric Arnebäck (3D preview features) * Santiago Benejam Torres (Catalan translation) @@ -139,7 +139,7 @@ QLC+ owes its success to the dedication and expertise of numerous individuals wh * Vadim Syniuhin (Ukrainian translation) * Mateusz Kędzierski (Polish translation) -### QLC+ 4: +### QLC+ 4 * Jano Svitok (bugfix, new features and improvements) * David Garyga (bugfix, new features and improvements) @@ -166,7 +166,7 @@ QLC+ owes its success to the dedication and expertise of numerous individuals wh * Santiago Benejam Torres (Catalan translation) * Koichiro Saito, Dai Suetake (Japanese translation) -### QLC: +### Q Light Controller * Stefan Krumm (Bugfixes, new features) * Christian Suehs (Bugfixes, new features) From 4826dbe421f5d4831c7f25733e8a5a11be50684e Mon Sep 17 00:00:00 2001 From: Lachlan Hicks <42596763+yestalgia@users.noreply.github.com> Date: Sat, 13 Jan 2024 00:15:00 +1100 Subject: [PATCH 014/212] Style: Heading line breaks --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index 18c263d40c..53d702617a 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ We welcome contributions from the community to help make QLC+ even better. Befor Further guidelines are available in the [CONTRIBUTING.md](CONTRIBUTING.md) document. -### Help wanted! +### 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) @@ -74,14 +74,12 @@ If you're regularly updating QLC+ sources with git pull, you may encounter compi [![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 ## Requirements ### Linux - * Qt >= 5.0 development libraries & tools * libudev-dev, libmad0-dev, libsndfile1-dev, libfftw3-dev * DMX USB plugin: libftdi-dev, pkg-config From f992a5351e475762addf0776d3d779a7b38962ba Mon Sep 17 00:00:00 2001 From: Lachlan Hicks <42596763+yestalgia@users.noreply.github.com> Date: Sat, 13 Jan 2024 00:33:03 +1100 Subject: [PATCH 015/212] Style: non-consecutive-blank lines and trailing : in headings --- README.md | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 53d702617a..c9c2b617e6 100644 --- a/README.md +++ b/README.md @@ -22,8 +22,7 @@ Copyright © Heikki Junnila, Massimo Callegari [![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: +### Key Resources
@@ -51,12 +50,10 @@ Copyright © Heikki Junnila, Massimo Callegari
-### QLC+ Social Media: +### QLC+ Social Media [![Instagram](https://img.shields.io/badge/Instagram-%23E4405F.svg?style=for-the-badge&logo=Instagram&logoColor=white)](https://www.instagram.com/qlcplus/) [![YouTube](https://img.shields.io/badge/YouTube-%23FF0000.svg?style=for-the-badge&logo=YouTube&logoColor=white)](https://www.youtube.com/watch?v=I9bccwcYQpM&list=PLHT-wIriuitDiW4A9oKSDr__Z_jcmMVdi) [![Facebook](https://img.shields.io/badge/Facebook-%231877F2.svg?style=for-the-badge&logo=Facebook&logoColor=white)](https://www.facebook.com/qlcplus) - - ## Contributing We welcome contributions from the community to help make QLC+ even better. Before diving into coding, we encourage you to start a discussion in our [Software Development](https://www.qlcplus.org/forum/viewforum.php?f=12) forum if you're considering adding a new feature or making significant changes. This provides an opportunity for feedback, collaboration, and ensuring alignment with the project's goals. @@ -97,7 +94,6 @@ Please refer to the online wiki pages: https://github.com/mcallegari/qlcplus/wik * OLA plugin: **Not available** * Velleman plugin: K8062 SDK from www.velleman.eu - ### Mac OS X * XCode (http://developer.apple.com/technologies/tools/xcode.html) @@ -109,7 +105,6 @@ Please refer to the online wiki pages: https://github.com/mcallegari/qlcplus/wik * Peperoni plugin: macports, libusb-compat, pkg-config * Velleman plugin: **Not available** - ## Support & Bug Reports For discussions, feedbacks, ideas and new fixtures, go to: @@ -185,5 +180,4 @@ Licensed under the Apache License, Version 2.0 (the "License"); you may not use 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) - +![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) \ No newline at end of file From 52986f65b94d1fa01910c0b0a0e9b55ef9e64d44 Mon Sep 17 00:00:00 2001 From: Nils Tijtgat Date: Thu, 18 Jan 2024 09:52:47 +0100 Subject: [PATCH 016/212] Add Fixture Tester --- ui/src/fixturemanager.cpp | 120 ++++++++++++++++++++++++++++++++++++-- ui/src/fixturemanager.h | 15 +++++ 2 files changed, 130 insertions(+), 5 deletions(-) diff --git a/ui/src/fixturemanager.cpp b/ui/src/fixturemanager.cpp index 45ed7f9158..edb8b19ef8 100644 --- a/ui/src/fixturemanager.cpp +++ b/ui/src/fixturemanager.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include "qlcfixturemode.h" #include "qlcfixturedef.h" @@ -45,6 +46,7 @@ #include "createfixturegroup.h" #include "fixturegroupeditor.h" #include "fixturetreewidget.h" +#include "genericdmxsource.h" #include "channelsselection.h" #include "addchannelsgroup.h" #include "fixturemanager.h" @@ -75,6 +77,7 @@ FixtureManager* FixtureManager::s_instance = NULL; FixtureManager::FixtureManager(QWidget* parent, Doc* doc) : QWidget(parent) , m_doc(doc) + , m_fixtureTestEnabled(true) , m_splitter(NULL) , m_fixtures_tree(NULL) , m_channel_groups_tree(NULL) @@ -151,6 +154,11 @@ FixtureManager::~FixtureManager() settings.setValue(SETTINGS_SPLITTER, m_splitter->saveState()); FixtureManager::s_instance = NULL; + QHash::iterator it; + for (it = m_selectedFixtureHash.begin(); it != m_selectedFixtureHash.end(); ++it) { + delete it.value(); + } + s_instance = NULL; } @@ -212,6 +220,7 @@ void FixtureManager::slotChannelsGroupRemoved(quint32 id) void FixtureManager::slotModeChanged(Doc::Mode mode) { + // TO-DO: hier moeje die check shit toevoegen ojo! if (mode == Doc::Design) { int selected = m_fixtures_tree->selectedItems().size(); @@ -269,6 +278,8 @@ void FixtureManager::slotModeChanged(Doc::Mode mode) m_fadeConfigAction->setEnabled(true); else m_fadeConfigAction->setEnabled(false); + + m_testFixturesAction->setEnabled(true); } else { @@ -279,7 +290,10 @@ void FixtureManager::slotModeChanged(Doc::Mode mode) m_fadeConfigAction->setEnabled(false); m_groupAction->setEnabled(false); m_unGroupAction->setEnabled(false); + m_testFixturesAction->setEnabled(false); } + + updateTestFixtures(); } void FixtureManager::slotFixtureGroupRemoved(quint32 id) @@ -494,6 +508,84 @@ void FixtureManager::updateRDMView() m_remapAction->setEnabled(false); } +void FixtureManager::updateTestFixtures() +{ + + qDebug() << "Run updateTestFixtures()"; + + QList m_selectedFixtures = m_fixtures_tree->selectedItems(); + QSet selectedFixtureIds; + foreach (QTreeWidgetItem* item , m_selectedFixtures) + { + selectedFixtureIds.insert(item->data(KColumnName, PROP_ID).toUInt()); + } + + QSet newlySelectedFixtureIds = selectedFixtureIds.subtract(m_lastSelectedFixtureIds); + QSet deselectedFixtureIds = m_lastSelectedFixtureIds.subtract(selectedFixtureIds); + + // TO-DO: check that no other conditions have to be set! + if(m_doc->mode() == Doc::Design && m_fixtureTestEnabled) + { + // Turn on all selected fixtures + QSet::const_iterator it; + for (it = newlySelectedFixtureIds.constBegin(); it != newlySelectedFixtureIds.constEnd(); ++it) { + testFixture(*it); + } + + // Turn all deselected fixtures off + for (it = deselectedFixtureIds.constBegin(); it != deselectedFixtureIds.constEnd(); ++it) { + untestFixture(*it); + } + } else { + // Turn all fixtures off + QHash::iterator it; + foreach(const quint32 &fixtureId, m_selectedFixtureHash.keys()) { + untestFixture(fixtureId); + } + } + + // TO-DO: terugkeren uit een selectie tijdens de niet edit fase werkt nog nie! + m_lastSelectedFixtureIds = selectedFixtureIds; + +} + +void FixtureManager::testFixture(quint32 id) +{ + Fixture* fxi = m_doc->fixture(id); + if (fxi == NULL) + return; + + GenericDMXSource* source = new GenericDMXSource(m_doc); + m_selectedFixtureHash.insert(id, source); + + // TO-DO: make fixture white! + + // Set the Intensity for every Head to 255 + for (int i = 0; i < fxi->heads(); i++) + { + QLCFixtureHead head= fxi->head(i); + quint32 jo = head.channelNumber(QLCChannel::Intensity, QLCChannel::MSB); + source->set(fxi->id(), jo, 255); + } + + source->setOutputEnabled(true); +} + +void FixtureManager::untestFixture(quint32 id) +{ + Fixture* fxi = m_doc->fixture(id); + if (fxi == NULL) + return; + + if(!m_selectedFixtureHash.contains(id)) + return; + + delete m_selectedFixtureHash.value(id); + m_selectedFixtureHash.remove(id); + + qDebug() << "Untest fixture" << id; +} + void FixtureManager::fixtureSelected(quint32 id) { Fixture* fxi = m_doc->fixture(id); @@ -504,11 +596,8 @@ void FixtureManager::fixtureSelected(quint32 id) createInfo(); m_info->setText(QString("%1%2") - .arg(fixtureInfoStyleSheetHeader()) - .arg(fxi->status())); - - // Enable/disable actions - slotModeChanged(m_doc->mode()); + .arg(fixtureInfoStyleSheetHeader()) + .arg(fxi->status())); } void FixtureManager::fixtureGroupSelected(FixtureGroup* grp) @@ -936,6 +1025,11 @@ void FixtureManager::initActions() tr("Remap fixtures..."), this); connect(m_remapAction, SIGNAL(triggered(bool)), this, SLOT(slotRemap())); + + m_testFixturesAction = new QAction(QIcon(":/fixture.png"), + tr("Enable fixture tester..."), this); + connect(m_testFixturesAction, SIGNAL(triggered(bool)), + this, SLOT(slotTestFixtures())); } void FixtureManager::updateGroupMenu() @@ -985,6 +1079,7 @@ void FixtureManager::initToolBar() toolbar->addAction(m_importAction); toolbar->addAction(m_exportAction); toolbar->addAction(m_remapAction); + toolbar->addAction(m_testFixturesAction); QToolButton* btn = qobject_cast (toolbar->widgetForAction(m_groupAction)); Q_ASSERT(btn != NULL); @@ -1515,6 +1610,21 @@ void FixtureManager::slotRemap() updateView(); } +void FixtureManager::slotTestFixtures() +{ + // Update the icon + if(m_fixtureTestEnabled) + { + m_testFixturesAction->setIcon(QIcon(":/remap.png")); + } else + { + m_testFixturesAction->setIcon(QIcon(":/fixture.png")); + } + + m_fixtureTestEnabled = !m_fixtureTestEnabled; + slotModeChanged(m_doc->mode()); +} + void FixtureManager::slotUnGroup() { if (QMessageBox::question(this, tr("Ungroup fixtures?"), diff --git a/ui/src/fixturemanager.h b/ui/src/fixturemanager.h index d0a5bed521..f908f622ec 100644 --- a/ui/src/fixturemanager.h +++ b/ui/src/fixturemanager.h @@ -25,6 +25,7 @@ #include "function.h" #include "fixture.h" #include "doc.h" +#include "genericdmxsource.h" class QLCFixtureDefCache; class FixtureGroupEditor; @@ -88,6 +89,9 @@ public slots: private: Doc* m_doc; + bool m_fixtureTestEnabled; + QSet m_lastSelectedFixtureIds; + QHash m_selectedFixtureHash; /******************************************************************** * Data view @@ -118,6 +122,15 @@ public slots: /** Construct the list view and data view */ void initDataView(); + /** TO-DO: DESCRIPTION */ + void updateTestFixtures(); + + /** TO-DO: Test fixture when Fixture Tester is enabled */ + void testFixture(quint32 id); + + /** TO-DO: Test fixture when Fixture Tester is enabled */ + void untestFixture(quint32 id); + /** Handle single fixture selection */ void fixtureSelected(quint32 id); @@ -199,6 +212,7 @@ private slots: void slotProperties(); void slotFadeConfig(); void slotRemap(); + void slotTestFixtures(); void slotUnGroup(); void slotGroupSelected(QAction* action); void slotMoveGroupUp(); @@ -216,6 +230,7 @@ private slots: QAction* m_propertiesAction; QAction* m_fadeConfigAction; QAction* m_remapAction; + QAction* m_testFixturesAction; QAction* m_groupAction; QAction* m_unGroupAction; QAction* m_newGroupAction; From abba88365a0f11921c63798f182f1ef0fd140982 Mon Sep 17 00:00:00 2001 From: Nils Tijtgat Date: Thu, 18 Jan 2024 18:09:14 +0100 Subject: [PATCH 017/212] Refine implementation --- resources/icons/png/fixture_off.png | Bin 0 -> 1418 bytes resources/icons/svg/fixture_off.svg | 133 ++++++++++++++++++++++++++++ ui/src/fixturemanager.cpp | 47 +++++----- ui/src/fixturemanager.h | 16 ++-- 4 files changed, 162 insertions(+), 34 deletions(-) create mode 100644 resources/icons/png/fixture_off.png create mode 100644 resources/icons/svg/fixture_off.svg diff --git a/resources/icons/png/fixture_off.png b/resources/icons/png/fixture_off.png new file mode 100644 index 0000000000000000000000000000000000000000..86eb77b1a8fa14ce9550c14aea1fed369323cc6d GIT binary patch literal 1418 zcmV;51$Fv~P)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2iOGx z2ro2$UO|@t00jz3L_t(o!_AjlY!p=($A4!oyR)6`W!i4n-h^(K?8dqkDVCN}9uO#@ zDyE?HO`9Nz661p*rXe&K#YiwlLbV}CVu;}dc_EUN21QUY5HMhBpwtCpY0T z2d8ygOD|H?FF850d(NEiKmYG~;6D~I{$N=a0Fg*U5<(z^hzKE~_sM`{KMyYy00m&f zh7GdE<0@3tYEnWyA_4oH{KoT$pSbzp311hi)xVvC1$g*4swClS5w#(%z2YkR1 z;Bg=mh}CsH&gb(L08PN|yu7>xGlK-20EWF@@2WZM-v!(eLc}f@0>d!20j3b*)!Fvy zy1o<`1o9W^>)E<>>(@Y^Wm%8R_CiffO@ky!KL9(P2kw`>V6h#7@hGC?5Jf2EPlJ>dX z?p1)Qs%k3G3hWpU-+M7^7)JJyBS&TjwQSk4ML?(1>AYTEUj7PD2ebg)b|{+md_Fcd zwpfy+YT(OoIP99?*aV{3WY+)fKeb~=YZ%r0*n9`fa>Dn z;%ByR-yS@0;6U6o$L2=?zXE&=toYlmy1F_M3WZ(<_5x-g5I8Q&a(9&Csl%Kyx`3;| zr@(VSTw!5hqQl`h81*5hJ%6uhTKpsc>FMb{;5XnP(BXEw>r+!xFU|lUGUd={hDL2? zzGE|Gs)}eTT9I^1*Oj|~sjBMk!-o(5xyan zoFxHhnl_Gn5QqWBWBd8i($cQ^DhGz%-xW%ey^e9o74negU*@*|KH%EMS5Q!p_>a2f`<3SAX8!l` Y2SA7N&&CE#w*UYD07*qoM6N<$f~IezDgXcg literal 0 HcmV?d00001 diff --git a/resources/icons/svg/fixture_off.svg b/resources/icons/svg/fixture_off.svg new file mode 100644 index 0000000000..0b2683261e --- /dev/null +++ b/resources/icons/svg/fixture_off.svg @@ -0,0 +1,133 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + diff --git a/ui/src/fixturemanager.cpp b/ui/src/fixturemanager.cpp index edb8b19ef8..5f517f2b7a 100644 --- a/ui/src/fixturemanager.cpp +++ b/ui/src/fixturemanager.cpp @@ -77,7 +77,7 @@ FixtureManager* FixtureManager::s_instance = NULL; FixtureManager::FixtureManager(QWidget* parent, Doc* doc) : QWidget(parent) , m_doc(doc) - , m_fixtureTestEnabled(true) + , m_testFixturesEnabled(true) , m_splitter(NULL) , m_fixtures_tree(NULL) , m_channel_groups_tree(NULL) @@ -155,7 +155,7 @@ FixtureManager::~FixtureManager() FixtureManager::s_instance = NULL; QHash::iterator it; - for (it = m_selectedFixtureHash.begin(); it != m_selectedFixtureHash.end(); ++it) { + for (it = m_fixtureToSourceMap.begin(); it != m_fixtureToSourceMap.end(); ++it) { delete it.value(); } @@ -293,7 +293,7 @@ void FixtureManager::slotModeChanged(Doc::Mode mode) m_testFixturesAction->setEnabled(false); } - updateTestFixtures(); + runTestFixtures(); } void FixtureManager::slotFixtureGroupRemoved(quint32 id) @@ -508,14 +508,11 @@ void FixtureManager::updateRDMView() m_remapAction->setEnabled(false); } -void FixtureManager::updateTestFixtures() +void FixtureManager::runTestFixtures() { - - qDebug() << "Run updateTestFixtures()"; - - QList m_selectedFixtures = m_fixtures_tree->selectedItems(); + QList selectedFixtures = m_fixtures_tree->selectedItems(); QSet selectedFixtureIds; - foreach (QTreeWidgetItem* item , m_selectedFixtures) + foreach (QTreeWidgetItem* item , selectedFixtures) { selectedFixtureIds.insert(item->data(KColumnName, PROP_ID).toUInt()); } @@ -523,42 +520,40 @@ void FixtureManager::updateTestFixtures() QSet newlySelectedFixtureIds = selectedFixtureIds.subtract(m_lastSelectedFixtureIds); QSet deselectedFixtureIds = m_lastSelectedFixtureIds.subtract(selectedFixtureIds); - // TO-DO: check that no other conditions have to be set! - if(m_doc->mode() == Doc::Design && m_fixtureTestEnabled) + if(m_doc->mode() == Doc::Design && m_testFixturesEnabled) { // Turn on all selected fixtures QSet::const_iterator it; for (it = newlySelectedFixtureIds.constBegin(); it != newlySelectedFixtureIds.constEnd(); ++it) { - testFixture(*it); + turnFixtureOn(*it); } // Turn all deselected fixtures off for (it = deselectedFixtureIds.constBegin(); it != deselectedFixtureIds.constEnd(); ++it) { - untestFixture(*it); + turnFixtureOff(*it); } } else { // Turn all fixtures off QHash::iterator it; - foreach(const quint32 &fixtureId, m_selectedFixtureHash.keys()) { - untestFixture(fixtureId); + foreach(const quint32 &fixtureId, m_fixtureToSourceMap.keys()) { + turnFixtureOff(fixtureId); } } - // TO-DO: terugkeren uit een selectie tijdens de niet edit fase werkt nog nie! m_lastSelectedFixtureIds = selectedFixtureIds; - } -void FixtureManager::testFixture(quint32 id) +void FixtureManager::turnFixtureOn(quint32 id) { Fixture* fxi = m_doc->fixture(id); if (fxi == NULL) return; GenericDMXSource* source = new GenericDMXSource(m_doc); - m_selectedFixtureHash.insert(id, source); + m_fixtureToSourceMap.insert(id, source); // TO-DO: make fixture white! + // TO-DO: check if this works for complex lights (Mac Quantum for instance) // Set the Intensity for every Head to 255 for (int i = 0; i < fxi->heads(); i++) @@ -571,17 +566,17 @@ void FixtureManager::testFixture(quint32 id) source->setOutputEnabled(true); } -void FixtureManager::untestFixture(quint32 id) +void FixtureManager::turnFixtureOff(quint32 id) { Fixture* fxi = m_doc->fixture(id); if (fxi == NULL) return; - if(!m_selectedFixtureHash.contains(id)) + if(!m_fixtureToSourceMap.contains(id)) return; - delete m_selectedFixtureHash.value(id); - m_selectedFixtureHash.remove(id); + delete m_fixtureToSourceMap.value(id); + m_fixtureToSourceMap.remove(id); qDebug() << "Untest fixture" << id; } @@ -1613,15 +1608,15 @@ void FixtureManager::slotRemap() void FixtureManager::slotTestFixtures() { // Update the icon - if(m_fixtureTestEnabled) + if(m_testFixturesEnabled) { - m_testFixturesAction->setIcon(QIcon(":/remap.png")); + m_testFixturesAction->setIcon(QIcon(":/fixture_off.png")); } else { m_testFixturesAction->setIcon(QIcon(":/fixture.png")); } - m_fixtureTestEnabled = !m_fixtureTestEnabled; + m_testFixturesEnabled = !m_testFixturesEnabled; slotModeChanged(m_doc->mode()); } diff --git a/ui/src/fixturemanager.h b/ui/src/fixturemanager.h index f908f622ec..1881540b15 100644 --- a/ui/src/fixturemanager.h +++ b/ui/src/fixturemanager.h @@ -89,9 +89,9 @@ public slots: private: Doc* m_doc; - bool m_fixtureTestEnabled; + bool m_testFixturesEnabled; QSet m_lastSelectedFixtureIds; - QHash m_selectedFixtureHash; + QHash m_fixtureToSourceMap; /******************************************************************** * Data view @@ -122,14 +122,14 @@ public slots: /** Construct the list view and data view */ void initDataView(); - /** TO-DO: DESCRIPTION */ - void updateTestFixtures(); + /** Checks if any fixtures have to be turned on/off if Test Fixtures is enabled */ + void runTestFixtures(); - /** TO-DO: Test fixture when Fixture Tester is enabled */ - void testFixture(quint32 id); + /** Creates a GenericDmxSource and turns the fixture on */ + void turnFixtureOn(quint32 id); - /** TO-DO: Test fixture when Fixture Tester is enabled */ - void untestFixture(quint32 id); + /** Turns the fixture off and removes the related GenericDmxSource */ + void turnFixtureOff(quint32 id); /** Handle single fixture selection */ void fixtureSelected(quint32 id); From 3255743d64dcd7dbdd9ef67c958f9100d37e10ba Mon Sep 17 00:00:00 2001 From: Nils Tijtgat Date: Thu, 18 Jan 2024 19:39:33 +0100 Subject: [PATCH 018/212] Add fixture_off images --- resources/icons/png/fixture_off.png | Bin 1418 -> 3795 bytes resources/icons/svg/fixture_off.svg | 177 +++++++--------------------- 2 files changed, 44 insertions(+), 133 deletions(-) diff --git a/resources/icons/png/fixture_off.png b/resources/icons/png/fixture_off.png index 86eb77b1a8fa14ce9550c14aea1fed369323cc6d..4ce1d8ddb4d576f8ae5876c925b8589579b238f1 100644 GIT binary patch literal 3795 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 delta 1380 zcmV-q1)KWQ9f}K(Bpd;AQb$4nuFf3k00006VoOIv0RI600RN!9r<0K(7k>Z;bV*G` z2iOGx2ro2$UO|@t00jz3L_t(o!_AjlY!p=($A4!oyR)6`W!i4n-h^(K?8dqkDVCN} z9uO#@DyE?HO`9Nz661p*rXe&K#YiwlLbV}CVu;}dc_EUN21QUY5HMhBpwtCpY0T2d8ygOD|H?FF850dw=+Nf++!XSQ$O9z1a1K-@IP=0^d)0(=at_}i|!x;haGg@0ZK_5x-g5I8Q&a(9&C zsl%Kyx`3;|r@(VSTw!5hqQl`h81*5hJ%6uhTKpsc>FMb{;5XnP(BXEw>r+!xFU|lU zGUd={hDL2?zGE|Gs)}eTT9I^1*Oj|~sjBMk!-o(5G`%)%H7G zXHTwnX`1|S^y2E3@fOfynx=Dt0o!IG%Bj?rmX@^9(SOlFMNuw9Q95TSS>wIz_qnsD z{-2WUj&tjZY5SZd0ce^wj(iY^0mftd`O?zTuK6klhTh+sn{D?Hlf>bW9!fMEI^diD zP+3`dNl}zWz-`Cuh^eR=xg^`2Jl*2pklE1GJkV}u(mzd5RaJF6 z7!2;R8GndPN=iBfylWW7g|@b~J5jcsi$vBGr;MnIG;O*5y<;T7vWSyq`OE|WA%uD8 z(4kX)zyB>D-!x4h@VaGLG1snLdvU%DlsxJ6+-vd28$F|dZkpzeXeXqosA#CRw)TUZ zoSc_>dwZRL*45RO2N?5E4uCgByJz?w`>d(Uu759slK=qt{r=(l`ue(xii-8Asi`$U zpB)uVcJ9A52UA#jjM6u){Rhpi&rjb?R#o*xRaI5rjHmSW_QtqeuEln5=(NG=z|+7- zKwDHAo|KkP9$R_E>ey^e9o74negU*@*|KH%EM9Sr?azDevklUyQdfddYAaz zza$!tU+oh8T}eqv!UMnS8HS-bozCM?bv}eKy5;+o=H_Pp_wolohw{(H22HmB0000 - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + From 2a0dceaeb6a5b8512515cbe5e63ccfcb56f9afe4 Mon Sep 17 00:00:00 2001 From: Nils Tijtgat Date: Fri, 19 Jan 2024 00:08:27 +0100 Subject: [PATCH 019/212] More fixes --- fixtureeditor/CMakeLists.txt | 1 + ui/src/CMakeLists.txt | 3 +++ ui/src/fixturemanager.cpp | 46 +++++++++++++++++++++++++----------- ui/src/qlcui.qrc | 1 + 4 files changed, 37 insertions(+), 14 deletions(-) diff --git a/fixtureeditor/CMakeLists.txt b/fixtureeditor/CMakeLists.txt index 94261267a3..1261c6ff27 100644 --- a/fixtureeditor/CMakeLists.txt +++ b/fixtureeditor/CMakeLists.txt @@ -520,6 +520,7 @@ set(qlcui_resource_files "../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/fixture_off.png" "../ui/src/../../resources/icons/png/flash.png" "../ui/src/../../resources/icons/png/flower.png" "../ui/src/../../resources/icons/png/folder.png" diff --git a/ui/src/CMakeLists.txt b/ui/src/CMakeLists.txt index 21c4e8a265..c06e42a28a 100644 --- a/ui/src/CMakeLists.txt +++ b/ui/src/CMakeLists.txt @@ -353,6 +353,9 @@ set_source_files_properties("../../resources/icons/png/filesaveas.png" set_source_files_properties("../../resources/icons/png/fixture.png" PROPERTIES QT_RESOURCE_ALIAS "fixture.png" ) +set_source_files_properties("../../resources/icons/png/fixture_off.png" + PROPERTIES QT_RESOURCE_ALIAS "fixture_off.png" +) set_source_files_properties("../../resources/icons/png/flash.png" PROPERTIES QT_RESOURCE_ALIAS "flash.png" ) diff --git a/ui/src/fixturemanager.cpp b/ui/src/fixturemanager.cpp index 5f517f2b7a..87a4ea6fa2 100644 --- a/ui/src/fixturemanager.cpp +++ b/ui/src/fixturemanager.cpp @@ -77,7 +77,7 @@ FixtureManager* FixtureManager::s_instance = NULL; FixtureManager::FixtureManager(QWidget* parent, Doc* doc) : QWidget(parent) , m_doc(doc) - , m_testFixturesEnabled(true) + , m_testFixturesEnabled(false) , m_splitter(NULL) , m_fixtures_tree(NULL) , m_channel_groups_tree(NULL) @@ -220,7 +220,6 @@ void FixtureManager::slotChannelsGroupRemoved(quint32 id) void FixtureManager::slotModeChanged(Doc::Mode mode) { - // TO-DO: hier moeje die check shit toevoegen ojo! if (mode == Doc::Design) { int selected = m_fixtures_tree->selectedItems().size(); @@ -524,18 +523,21 @@ void FixtureManager::runTestFixtures() { // Turn on all selected fixtures QSet::const_iterator it; - for (it = newlySelectedFixtureIds.constBegin(); it != newlySelectedFixtureIds.constEnd(); ++it) { + for (it = newlySelectedFixtureIds.constBegin(); it != newlySelectedFixtureIds.constEnd(); ++it) + { turnFixtureOn(*it); } // Turn all deselected fixtures off - for (it = deselectedFixtureIds.constBegin(); it != deselectedFixtureIds.constEnd(); ++it) { + for (it = deselectedFixtureIds.constBegin(); it != deselectedFixtureIds.constEnd(); ++it) + { turnFixtureOff(*it); } } else { // Turn all fixtures off QHash::iterator it; - foreach(const quint32 &fixtureId, m_fixtureToSourceMap.keys()) { + foreach(const quint32 &fixtureId, m_fixtureToSourceMap.keys()) + { turnFixtureOff(fixtureId); } } @@ -552,15 +554,29 @@ void FixtureManager::turnFixtureOn(quint32 id) GenericDMXSource* source = new GenericDMXSource(m_doc); m_fixtureToSourceMap.insert(id, source); - // TO-DO: make fixture white! // TO-DO: check if this works for complex lights (Mac Quantum for instance) - // Set the Intensity for every Head to 255 + // Set the color of every Fixture to white and intensity to 255 for (int i = 0; i < fxi->heads(); i++) { - QLCFixtureHead head= fxi->head(i); - quint32 jo = head.channelNumber(QLCChannel::Intensity, QLCChannel::MSB); - source->set(fxi->id(), jo, 255); + QLCFixtureHead head = fxi->head(i); + + quint32 whiteChannel = head.channelNumber(QLCChannel::White, QLCChannel::MSB); + quint32 redChannel = head.channelNumber(QLCChannel::Red, QLCChannel::MSB); + quint32 greenChannel = head.channelNumber(QLCChannel::Green, QLCChannel::MSB); + quint32 blueChannel = head.channelNumber(QLCChannel::Blue, QLCChannel::MSB); + + if(whiteChannel != QLCChannel::invalid()) + { + source->set(fxi->id(), whiteChannel, 255); + } else if(redChannel != QLCChannel::invalid() && greenChannel != QLCChannel::invalid() && blueChannel != QLCChannel::invalid()) { + source->set(fxi->id(), redChannel, 255); + source->set(fxi->id(), greenChannel, 255); + source->set(fxi->id(), blueChannel, 255); + } + + quint32 intensityChannel = head.channelNumber(QLCChannel::Intensity, QLCChannel::MSB); + source->set(fxi->id(), intensityChannel, 255); } source->setOutputEnabled(true); @@ -577,8 +593,6 @@ void FixtureManager::turnFixtureOff(quint32 id) delete m_fixtureToSourceMap.value(id); m_fixtureToSourceMap.remove(id); - - qDebug() << "Untest fixture" << id; } void FixtureManager::fixtureSelected(quint32 id) @@ -1610,10 +1624,14 @@ void FixtureManager::slotTestFixtures() // Update the icon if(m_testFixturesEnabled) { - m_testFixturesAction->setIcon(QIcon(":/fixture_off.png")); + m_testFixturesAction->setIcon(QIcon(":/fixture.png")); + m_testFixturesAction->setIconText(tr("Enable fixture tester")); + m_testFixturesAction->setToolTip(tr("Enable fixture tester")); } else { - m_testFixturesAction->setIcon(QIcon(":/fixture.png")); + m_testFixturesAction->setIcon(QIcon(":/fixture_off.png")); + m_testFixturesAction->setIconText(tr("Disable fixture tester")); + m_testFixturesAction->setToolTip(tr("Disable fixture tester")); } m_testFixturesEnabled = !m_testFixturesEnabled; diff --git a/ui/src/qlcui.qrc b/ui/src/qlcui.qrc index d6195acb38..d818447939 100644 --- a/ui/src/qlcui.qrc +++ b/ui/src/qlcui.qrc @@ -57,6 +57,7 @@ ../../resources/icons/png/filesave.png ../../resources/icons/png/filesaveas.png ../../resources/icons/png/fixture.png + ../../resources/icons/png/fixture_off.png ../../resources/icons/png/folder.png ../../resources/icons/png/qlcplus-fixtureeditor.png ../../resources/icons/png/flash.png From 2b1267c38f830d148036858595d81a53dbb3a3c6 Mon Sep 17 00:00:00 2001 From: Nils Tijtgat Date: Fri, 19 Jan 2024 00:20:23 +0100 Subject: [PATCH 020/212] Small fix --- ui/src/fixturemanager.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/ui/src/fixturemanager.cpp b/ui/src/fixturemanager.cpp index 87a4ea6fa2..297319246e 100644 --- a/ui/src/fixturemanager.cpp +++ b/ui/src/fixturemanager.cpp @@ -533,7 +533,9 @@ void FixtureManager::runTestFixtures() { turnFixtureOff(*it); } - } else { + } + else + { // Turn all fixtures off QHash::iterator it; foreach(const quint32 &fixtureId, m_fixtureToSourceMap.keys()) @@ -569,7 +571,9 @@ void FixtureManager::turnFixtureOn(quint32 id) if(whiteChannel != QLCChannel::invalid()) { source->set(fxi->id(), whiteChannel, 255); - } else if(redChannel != QLCChannel::invalid() && greenChannel != QLCChannel::invalid() && blueChannel != QLCChannel::invalid()) { + } + else if(redChannel != QLCChannel::invalid() && greenChannel != QLCChannel::invalid() && blueChannel != QLCChannel::invalid()) + { source->set(fxi->id(), redChannel, 255); source->set(fxi->id(), greenChannel, 255); source->set(fxi->id(), blueChannel, 255); @@ -1621,7 +1625,6 @@ void FixtureManager::slotRemap() void FixtureManager::slotTestFixtures() { - // Update the icon if(m_testFixturesEnabled) { m_testFixturesAction->setIcon(QIcon(":/fixture.png")); From ccdd17876faefc5e5898f8b11e0268e89787cc58 Mon Sep 17 00:00:00 2001 From: Nils Tijtgat Date: Fri, 19 Jan 2024 08:43:30 +0100 Subject: [PATCH 021/212] Only enable Highlight when FixtureManager tab is active --- ui/src/fixturemanager.cpp | 15 ++++++++++++--- ui/src/fixturemanager.h | 8 ++++++-- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/ui/src/fixturemanager.cpp b/ui/src/fixturemanager.cpp index 297319246e..f7d0f3fb57 100644 --- a/ui/src/fixturemanager.cpp +++ b/ui/src/fixturemanager.cpp @@ -74,8 +74,9 @@ FixtureManager* FixtureManager::s_instance = NULL; * Initialization *****************************************************************************/ -FixtureManager::FixtureManager(QWidget* parent, Doc* doc) +FixtureManager::FixtureManager(QTabWidget* parent, Doc* doc) : QWidget(parent) + , m_parent(parent) , m_doc(doc) , m_testFixturesEnabled(false) , m_splitter(NULL) @@ -119,6 +120,9 @@ FixtureManager::FixtureManager(QWidget* parent, Doc* doc) if (grpItem != NULL) grpItem->setExpanded(true); + connect(m_parent, SIGNAL(currentChanged(int)), + this, SLOT(slotParentTabChanged())); + /* Connect fixture list change signals from the new document object */ connect(m_doc, SIGNAL(fixtureRemoved(quint32)), this, SLOT(slotFixtureRemoved(quint32))); @@ -168,9 +172,14 @@ FixtureManager* FixtureManager::instance() } /***************************************************************************** - * Doc signal handlers + * Signal handlers *****************************************************************************/ +void FixtureManager::slotParentTabChanged() +{ + runTestFixtures(); +} + void FixtureManager::slotFixtureRemoved(quint32 id) { QList groupsToDelete; @@ -519,7 +528,7 @@ void FixtureManager::runTestFixtures() QSet newlySelectedFixtureIds = selectedFixtureIds.subtract(m_lastSelectedFixtureIds); QSet deselectedFixtureIds = m_lastSelectedFixtureIds.subtract(selectedFixtureIds); - if(m_doc->mode() == Doc::Design && m_testFixturesEnabled) + if(m_doc->mode() == Doc::Design && m_testFixturesEnabled && m_parent->currentIndex() == 0) { // Turn on all selected fixtures QSet::const_iterator it; diff --git a/ui/src/fixturemanager.h b/ui/src/fixturemanager.h index 1881540b15..59eedc8e49 100644 --- a/ui/src/fixturemanager.h +++ b/ui/src/fixturemanager.h @@ -55,7 +55,7 @@ class FixtureManager : public QWidget * Initialization ********************************************************************/ public: - FixtureManager(QWidget* parent, Doc* doc); + FixtureManager(QTabWidget* parent, Doc* doc); ~FixtureManager(); /** Get the singleton instance */ @@ -66,9 +66,12 @@ class FixtureManager : public QWidget static FixtureManager* s_instance; /******************************************************************** - * Doc signal handlers + * Signal handlers ********************************************************************/ public slots: + /** Callback for QTabWidget::currentChanged() signals */ + void slotParentTabChanged(); + /** Callback for Doc::fixtureRemoved() signals */ void slotFixtureRemoved(quint32 id); @@ -88,6 +91,7 @@ public slots: void slotDocLoaded(); private: + QTabWidget* m_parent; Doc* m_doc; bool m_testFixturesEnabled; QSet m_lastSelectedFixtureIds; From c8c3a24b309d3b50ad0d1ba5e73e97109f67216c Mon Sep 17 00:00:00 2001 From: Nils Tijtgat Date: Fri, 19 Jan 2024 08:46:01 +0100 Subject: [PATCH 022/212] Rename feature to Highlight Fixtures --- ui/src/fixturemanager.cpp | 42 +++++++++++++++++++-------------------- ui/src/fixturemanager.h | 8 ++++---- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/ui/src/fixturemanager.cpp b/ui/src/fixturemanager.cpp index f7d0f3fb57..07698ed341 100644 --- a/ui/src/fixturemanager.cpp +++ b/ui/src/fixturemanager.cpp @@ -78,7 +78,7 @@ FixtureManager::FixtureManager(QTabWidget* parent, Doc* doc) : QWidget(parent) , m_parent(parent) , m_doc(doc) - , m_testFixturesEnabled(false) + , m_highlightFixturesEnabled(false) , m_splitter(NULL) , m_fixtures_tree(NULL) , m_channel_groups_tree(NULL) @@ -177,7 +177,7 @@ FixtureManager* FixtureManager::instance() void FixtureManager::slotParentTabChanged() { - runTestFixtures(); + runHighlightFixtures(); } void FixtureManager::slotFixtureRemoved(quint32 id) @@ -287,7 +287,7 @@ void FixtureManager::slotModeChanged(Doc::Mode mode) else m_fadeConfigAction->setEnabled(false); - m_testFixturesAction->setEnabled(true); + m_highlightFixturesAction->setEnabled(true); } else { @@ -298,10 +298,10 @@ void FixtureManager::slotModeChanged(Doc::Mode mode) m_fadeConfigAction->setEnabled(false); m_groupAction->setEnabled(false); m_unGroupAction->setEnabled(false); - m_testFixturesAction->setEnabled(false); + m_highlightFixturesAction->setEnabled(false); } - runTestFixtures(); + runHighlightFixtures(); } void FixtureManager::slotFixtureGroupRemoved(quint32 id) @@ -516,7 +516,7 @@ void FixtureManager::updateRDMView() m_remapAction->setEnabled(false); } -void FixtureManager::runTestFixtures() +void FixtureManager::runHighlightFixtures() { QList selectedFixtures = m_fixtures_tree->selectedItems(); QSet selectedFixtureIds; @@ -528,7 +528,7 @@ void FixtureManager::runTestFixtures() QSet newlySelectedFixtureIds = selectedFixtureIds.subtract(m_lastSelectedFixtureIds); QSet deselectedFixtureIds = m_lastSelectedFixtureIds.subtract(selectedFixtureIds); - if(m_doc->mode() == Doc::Design && m_testFixturesEnabled && m_parent->currentIndex() == 0) + if(m_doc->mode() == Doc::Design && m_highlightFixturesEnabled && m_parent->currentIndex() == 0) { // Turn on all selected fixtures QSet::const_iterator it; @@ -1048,10 +1048,10 @@ void FixtureManager::initActions() connect(m_remapAction, SIGNAL(triggered(bool)), this, SLOT(slotRemap())); - m_testFixturesAction = new QAction(QIcon(":/fixture.png"), - tr("Enable fixture tester..."), this); - connect(m_testFixturesAction, SIGNAL(triggered(bool)), - this, SLOT(slotTestFixtures())); + m_highlightFixturesAction = new QAction(QIcon(":/fixture.png"), + tr("Enable highlight fixtures..."), this); + connect(m_highlightFixturesAction, SIGNAL(triggered(bool)), + this, SLOT(slotHighlightFixtures())); } void FixtureManager::updateGroupMenu() @@ -1101,7 +1101,7 @@ void FixtureManager::initToolBar() toolbar->addAction(m_importAction); toolbar->addAction(m_exportAction); toolbar->addAction(m_remapAction); - toolbar->addAction(m_testFixturesAction); + toolbar->addAction(m_highlightFixturesAction); QToolButton* btn = qobject_cast (toolbar->widgetForAction(m_groupAction)); Q_ASSERT(btn != NULL); @@ -1632,21 +1632,21 @@ void FixtureManager::slotRemap() updateView(); } -void FixtureManager::slotTestFixtures() +void FixtureManager::slotHighlightFixtures() { - if(m_testFixturesEnabled) + if(m_highlightFixturesEnabled) { - m_testFixturesAction->setIcon(QIcon(":/fixture.png")); - m_testFixturesAction->setIconText(tr("Enable fixture tester")); - m_testFixturesAction->setToolTip(tr("Enable fixture tester")); + m_highlightFixturesAction->setIcon(QIcon(":/fixture.png")); + m_highlightFixturesAction->setIconText(tr("Enable highlight fixtures")); + m_highlightFixturesAction->setToolTip(tr("Enable highlight fixtures")); } else { - m_testFixturesAction->setIcon(QIcon(":/fixture_off.png")); - m_testFixturesAction->setIconText(tr("Disable fixture tester")); - m_testFixturesAction->setToolTip(tr("Disable fixture tester")); + m_highlightFixturesAction->setIcon(QIcon(":/fixture_off.png")); + m_highlightFixturesAction->setIconText(tr("Disable highlight fixtures")); + m_highlightFixturesAction->setToolTip(tr("Disable highlight fixtures")); } - m_testFixturesEnabled = !m_testFixturesEnabled; + m_highlightFixturesEnabled = !m_highlightFixturesEnabled; slotModeChanged(m_doc->mode()); } diff --git a/ui/src/fixturemanager.h b/ui/src/fixturemanager.h index 59eedc8e49..2e1a58b348 100644 --- a/ui/src/fixturemanager.h +++ b/ui/src/fixturemanager.h @@ -93,7 +93,7 @@ public slots: private: QTabWidget* m_parent; Doc* m_doc; - bool m_testFixturesEnabled; + bool m_highlightFixturesEnabled; QSet m_lastSelectedFixtureIds; QHash m_fixtureToSourceMap; @@ -127,7 +127,7 @@ public slots: void initDataView(); /** Checks if any fixtures have to be turned on/off if Test Fixtures is enabled */ - void runTestFixtures(); + void runHighlightFixtures(); /** Creates a GenericDmxSource and turns the fixture on */ void turnFixtureOn(quint32 id); @@ -216,7 +216,7 @@ private slots: void slotProperties(); void slotFadeConfig(); void slotRemap(); - void slotTestFixtures(); + void slotHighlightFixtures(); void slotUnGroup(); void slotGroupSelected(QAction* action); void slotMoveGroupUp(); @@ -234,7 +234,7 @@ private slots: QAction* m_propertiesAction; QAction* m_fadeConfigAction; QAction* m_remapAction; - QAction* m_testFixturesAction; + QAction* m_highlightFixturesAction; QAction* m_groupAction; QAction* m_unGroupAction; QAction* m_newGroupAction; From 7307713caada4a24bb006e06fcee3d7844999a18 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Fri, 19 Jan 2024 19:52:17 +0100 Subject: [PATCH 023/212] qmlui: add acts on channels in fixture editor --- qmlui/fixtureeditor/editorview.cpp | 18 +++++++++++++++++ qmlui/fixtureeditor/modeedit.cpp | 28 ++++++++++++++++++++++++++ qmlui/fixtureeditor/modeedit.h | 10 +++++++++ qmlui/qml/fixtureeditor/ModeEditor.qml | 5 +++++ 4 files changed, 61 insertions(+) diff --git a/qmlui/fixtureeditor/editorview.cpp b/qmlui/fixtureeditor/editorview.cpp index 5c864bc272..307a71fb61 100644 --- a/qmlui/fixtureeditor/editorview.cpp +++ b/qmlui/fixtureeditor/editorview.cpp @@ -374,6 +374,24 @@ QString EditorView::checkFixture() errors.append(tr("
  • Empty capability description provided in channel '%1'
  • ").arg(channel->name())); } } + + for (QLCFixtureMode *mode : m_fixtureDef->modes()) + { + if (mode->name().isEmpty()) + errors.append(tr("
  • Empty mode name provided
  • ")); + + if (mode->channels().count() == 0) + errors.append(tr("
  • Mode '%1' has no channels defined
  • ").arg(mode->name())); + + quint32 chIndex = 0; + for (QLCChannel *channel : mode->channels()) + { + + if (mode->channelActsOn(chIndex) == chIndex) + errors.append(tr("
  • In mode '%1', channel '%2' cannot act on itself
  • ").arg(mode->name(), channel->name())); + chIndex++; + } + } } if (m_fixtureDef->modes().count() == 0) diff --git a/qmlui/fixtureeditor/modeedit.cpp b/qmlui/fixtureeditor/modeedit.cpp index f30e235c6f..cead486ac2 100644 --- a/qmlui/fixtureeditor/modeedit.cpp +++ b/qmlui/fixtureeditor/modeedit.cpp @@ -100,6 +100,33 @@ bool ModeEdit::deleteChannel(QLCChannel *channel) return res; } +QStringList ModeEdit::actsOnChannels() +{ + QStringList list; + list << "-"; + + for (QLCChannel *channel : m_mode->channels()) + list << channel->name(); + + return list; +} + +int ModeEdit::actsOnChannel(int index) +{ + quint32 actsOnChannelIndex = m_mode->channelActsOn(index); + + if (actsOnChannelIndex != QLCChannel::invalid()) + return actsOnChannelIndex + 1; + else + return 0; +} + +void ModeEdit::setActsOnChannel(int sourceIndex, int destIndex) +{ + quint32 actsOnChannel = destIndex == 0 ? QLCChannel::invalid() : destIndex - 1; + m_mode->setChannelActsOn(sourceIndex, actsOnChannel); +} + void ModeEdit::updateChannelList() { m_channelList->clear(); @@ -113,6 +140,7 @@ void ModeEdit::updateChannelList() } emit channelsChanged(); + emit actsOnChannelsChanged(); } /************************************************************************ diff --git a/qmlui/fixtureeditor/modeedit.h b/qmlui/fixtureeditor/modeedit.h index 8fd9341397..ef6ebbd18a 100644 --- a/qmlui/fixtureeditor/modeedit.h +++ b/qmlui/fixtureeditor/modeedit.h @@ -37,6 +37,7 @@ class ModeEdit : public QObject Q_PROPERTY(QVariant heads READ heads NOTIFY headsChanged) Q_PROPERTY(bool useGlobalPhysical READ useGlobalPhysical CONSTANT) Q_PROPERTY(PhysicalEdit *physical READ physical CONSTANT) + Q_PROPERTY(QStringList actsOnChannels READ actsOnChannels NOTIFY actsOnChannelsChanged) public: ModeEdit(QLCFixtureMode *mode, QObject *parent = nullptr); @@ -73,11 +74,20 @@ class ModeEdit : public QObject /** Delete the given $channel from the mode being edited */ Q_INVOKABLE bool deleteChannel(QLCChannel *channel); + /** Return a simple list with the possible channels to act on */ + QStringList actsOnChannels(); + + /** Return the channel where channel at $index acts on */ + Q_INVOKABLE int actsOnChannel(int index); + + Q_INVOKABLE void setActsOnChannel(int sourceIndex, int destIndex); + private: void updateChannelList(); signals: void channelsChanged(); + void actsOnChannelsChanged(); private: /** Reference to a channel list usable in QML */ diff --git a/qmlui/qml/fixtureeditor/ModeEditor.qml b/qmlui/qml/fixtureeditor/ModeEditor.qml index d3e05b8e1d..1deab6a26f 100644 --- a/qmlui/qml/fixtureeditor/ModeEditor.qml +++ b/qmlui/qml/fixtureeditor/ModeEditor.qml @@ -294,10 +294,15 @@ Rectangle iSrc: mcDelegate.cRef ? mcDelegate.cRef.getIconNameFromGroup(mcDelegate.cRef.group, true) : "" } Rectangle { width: 1; height: UISettings.listItemHeight } + CustomComboBox { implicitWidth: UISettings.bigItemHeight * 2 height: UISettings.listItemHeight + model: mode ? mode.actsOnChannels : null + textRole: "" + currentIndex: mode ? mode.actsOnChannel(index) : -1 + onCurrentIndexChanged: if (mode) mode.setActsOnChannel(index, currentIndex) } } From 5ee5ffd27edee051bfe8f9a07ac0d264fa09874a Mon Sep 17 00:00:00 2001 From: Nils Tijtgat Date: Sat, 20 Jan 2024 10:20:36 +0100 Subject: [PATCH 024/212] Set pan en tilt when highlighting fixtures --- ui/src/fixturemanager.cpp | 22 +++++++++++++++++----- ui/src/fixturemanager.h | 4 ++-- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/ui/src/fixturemanager.cpp b/ui/src/fixturemanager.cpp index 07698ed341..315f55fd1b 100644 --- a/ui/src/fixturemanager.cpp +++ b/ui/src/fixturemanager.cpp @@ -534,13 +534,13 @@ void FixtureManager::runHighlightFixtures() QSet::const_iterator it; for (it = newlySelectedFixtureIds.constBegin(); it != newlySelectedFixtureIds.constEnd(); ++it) { - turnFixtureOn(*it); + highlightFixture(*it); } // Turn all deselected fixtures off for (it = deselectedFixtureIds.constBegin(); it != deselectedFixtureIds.constEnd(); ++it) { - turnFixtureOff(*it); + unHighlightFixture(*it); } } else @@ -549,14 +549,14 @@ void FixtureManager::runHighlightFixtures() QHash::iterator it; foreach(const quint32 &fixtureId, m_fixtureToSourceMap.keys()) { - turnFixtureOff(fixtureId); + unHighlightFixture(fixtureId); } } m_lastSelectedFixtureIds = selectedFixtureIds; } -void FixtureManager::turnFixtureOn(quint32 id) +void FixtureManager::highlightFixture(quint32 id) { Fixture* fxi = m_doc->fixture(id); if (fxi == NULL) @@ -588,6 +588,18 @@ void FixtureManager::turnFixtureOn(quint32 id) source->set(fxi->id(), blueChannel, 255); } + quint32 panChannel = head.channelNumber(QLCChannel::Pan, QLCChannel::MSB); + if(panChannel != QLCChannel::invalid()) + { + source->set(fxi->id(), panChannel, 127); + } + + quint32 tiltChannel = head.channelNumber(QLCChannel::Tilt, QLCChannel::MSB); + if(tiltChannel != QLCChannel::invalid()) + { + source->set(fxi->id(), tiltChannel, 127); + } + quint32 intensityChannel = head.channelNumber(QLCChannel::Intensity, QLCChannel::MSB); source->set(fxi->id(), intensityChannel, 255); } @@ -595,7 +607,7 @@ void FixtureManager::turnFixtureOn(quint32 id) source->setOutputEnabled(true); } -void FixtureManager::turnFixtureOff(quint32 id) +void FixtureManager::unHighlightFixture(quint32 id) { Fixture* fxi = m_doc->fixture(id); if (fxi == NULL) diff --git a/ui/src/fixturemanager.h b/ui/src/fixturemanager.h index 2e1a58b348..623c04e495 100644 --- a/ui/src/fixturemanager.h +++ b/ui/src/fixturemanager.h @@ -130,10 +130,10 @@ public slots: void runHighlightFixtures(); /** Creates a GenericDmxSource and turns the fixture on */ - void turnFixtureOn(quint32 id); + void highlightFixture(quint32 id); /** Turns the fixture off and removes the related GenericDmxSource */ - void turnFixtureOff(quint32 id); + void unHighlightFixture(quint32 id); /** Handle single fixture selection */ void fixtureSelected(quint32 id); From cb24f4e8012129303462946d45ebcea593cda0d8 Mon Sep 17 00:00:00 2001 From: Nils Tijtgat Date: Sat, 20 Jan 2024 11:21:50 +0100 Subject: [PATCH 025/212] Codestyle fixes and initial shutter open implementation --- ui/src/fixturemanager.cpp | 37 ++++++++++++++++++++++-------- ui/src/virtualconsole/vcbutton.cpp | 4 ++-- 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/ui/src/fixturemanager.cpp b/ui/src/fixturemanager.cpp index 315f55fd1b..02f4dab1cd 100644 --- a/ui/src/fixturemanager.cpp +++ b/ui/src/fixturemanager.cpp @@ -41,6 +41,7 @@ #include "qlcfixturemode.h" #include "qlcfixturedef.h" #include "qlcchannel.h" +#include "qlccapability.h" #include "qlcfile.h" #include "createfixturegroup.h" @@ -528,7 +529,7 @@ void FixtureManager::runHighlightFixtures() QSet newlySelectedFixtureIds = selectedFixtureIds.subtract(m_lastSelectedFixtureIds); QSet deselectedFixtureIds = m_lastSelectedFixtureIds.subtract(selectedFixtureIds); - if(m_doc->mode() == Doc::Design && m_highlightFixturesEnabled && m_parent->currentIndex() == 0) + if (m_doc->mode() == Doc::Design && m_highlightFixturesEnabled && m_parent->currentIndex() == 0) { // Turn on all selected fixtures QSet::const_iterator it; @@ -565,9 +566,24 @@ void FixtureManager::highlightFixture(quint32 id) GenericDMXSource* source = new GenericDMXSource(m_doc); m_fixtureToSourceMap.insert(id, source); - // TO-DO: check if this works for complex lights (Mac Quantum for instance) + for (quint32 i = 0; i < fxi->channels(); i++) + { + const QLCChannel *channel = fxi->channel(i); + for (QLCCapability *cap : channel->capabilities()) + { + if (cap->preset() == QLCCapability::ShutterOpen || + cap->preset() == QLCCapability::LampOn) + { + // TO-DO: I'm not sure if this is correct if a fixture has multiple heads, will this open the shutter for all heads? + source->set(fxi->id(), i, cap->middle()); + + break; + } - // Set the color of every Fixture to white and intensity to 255 + } + } + + // Set the color of every Fixture to white, pan en tilt to 127, shutter to open and intensity to 255 for (int i = 0; i < fxi->heads(); i++) { QLCFixtureHead head = fxi->head(i); @@ -577,11 +593,11 @@ void FixtureManager::highlightFixture(quint32 id) quint32 greenChannel = head.channelNumber(QLCChannel::Green, QLCChannel::MSB); quint32 blueChannel = head.channelNumber(QLCChannel::Blue, QLCChannel::MSB); - if(whiteChannel != QLCChannel::invalid()) + if (whiteChannel != QLCChannel::invalid()) { source->set(fxi->id(), whiteChannel, 255); } - else if(redChannel != QLCChannel::invalid() && greenChannel != QLCChannel::invalid() && blueChannel != QLCChannel::invalid()) + else if (redChannel != QLCChannel::invalid() && greenChannel != QLCChannel::invalid() && blueChannel != QLCChannel::invalid()) { source->set(fxi->id(), redChannel, 255); source->set(fxi->id(), greenChannel, 255); @@ -589,13 +605,13 @@ void FixtureManager::highlightFixture(quint32 id) } quint32 panChannel = head.channelNumber(QLCChannel::Pan, QLCChannel::MSB); - if(panChannel != QLCChannel::invalid()) + if (panChannel != QLCChannel::invalid()) { source->set(fxi->id(), panChannel, 127); } quint32 tiltChannel = head.channelNumber(QLCChannel::Tilt, QLCChannel::MSB); - if(tiltChannel != QLCChannel::invalid()) + if (tiltChannel != QLCChannel::invalid()) { source->set(fxi->id(), tiltChannel, 127); } @@ -613,7 +629,7 @@ void FixtureManager::unHighlightFixture(quint32 id) if (fxi == NULL) return; - if(!m_fixtureToSourceMap.contains(id)) + if (!m_fixtureToSourceMap.contains(id)) return; delete m_fixtureToSourceMap.value(id); @@ -1646,12 +1662,13 @@ void FixtureManager::slotRemap() void FixtureManager::slotHighlightFixtures() { - if(m_highlightFixturesEnabled) + if (m_highlightFixturesEnabled) { m_highlightFixturesAction->setIcon(QIcon(":/fixture.png")); m_highlightFixturesAction->setIconText(tr("Enable highlight fixtures")); m_highlightFixturesAction->setToolTip(tr("Enable highlight fixtures")); - } else + } + else { m_highlightFixturesAction->setIcon(QIcon(":/fixture_off.png")); m_highlightFixturesAction->setIconText(tr("Disable highlight fixtures")); diff --git a/ui/src/virtualconsole/vcbutton.cpp b/ui/src/virtualconsole/vcbutton.cpp index 0fa251f0a0..38fc71d2bc 100644 --- a/ui/src/virtualconsole/vcbutton.cpp +++ b/ui/src/virtualconsole/vcbutton.cpp @@ -1115,7 +1115,7 @@ void VCButton::paintEvent(QPaintEvent* e) if (state() == Active) { - if(m_flashForceLTP || m_flashOverrides) + if (m_flashForceLTP || m_flashOverrides) painter.setBrush(QBrush(QColor(230, 0, 0, 255))); else painter.setBrush(QBrush(QColor(0, 230, 0, 255))); @@ -1147,7 +1147,7 @@ void VCButton::paintEvent(QPaintEvent* e) painter.setPen(QPen(QColor(255, 170, 0, 255), borderWidth)); else { - if(m_flashForceLTP || m_flashOverrides) + if (m_flashForceLTP || m_flashOverrides) painter.setPen(QPen(QColor(230, 0, 0, 255), borderWidth)); else painter.setPen(QPen(QColor(0, 230, 0, 255), borderWidth)); From 059db12cd1e7eb89a2367b2bd6b155a239409f9a Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Sat, 20 Jan 2024 12:21:43 +0100 Subject: [PATCH 026/212] qmlui: fix grid editor tooltips --- qmlui/qml/fixturesfunctions/GridEditor.qml | 24 +++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/qmlui/qml/fixturesfunctions/GridEditor.qml b/qmlui/qml/fixturesfunctions/GridEditor.qml index c8e2eac887..165a6f7b27 100644 --- a/qmlui/qml/fixturesfunctions/GridEditor.qml +++ b/qmlui/qml/fixturesfunctions/GridEditor.qml @@ -222,17 +222,31 @@ Rectangle onTriggered: calculateCellSize() } + Text + { + id: ttText + + property string tooltipText: "" + visible: false + x: gridMouseArea.mouseX + y: gridMouseArea.mouseY + ToolTip.visible: ttText.visible + ToolTip.timeout: 3000 + ToolTip.text: tooltipText + } + Timer { id: ttTimer + repeat: false interval: 1000 - running: gridMouseArea.containsMouse + running: false onTriggered: { var xPos = parseInt(gridMouseArea.mouseX / cellSize) var yPos = parseInt(gridMouseArea.mouseY / cellSize) - var tooltip = getTooltip(xPos, yPos) - Tooltip.showText(gridMouseArea, Qt.point(gridMouseArea.mouseX, gridMouseArea.mouseY), tooltip) + ttText.tooltipText = getTooltip(xPos, yPos) + ttText.visible = true } } @@ -343,6 +357,7 @@ Rectangle onPositionChanged: { + ttText.visible = false ttTimer.restart() if (movingSelection == false) @@ -368,8 +383,7 @@ Rectangle updateViewSelection(selectionOffset) } - onExited: Tooltip.hideText() - onCanceled: Tooltip.hideText() + onExited: ttTimer.stop() } DropArea From 196fae69e0861f1b03bd8443e701351fc20c52d4 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Sat, 20 Jan 2024 12:22:34 +0100 Subject: [PATCH 027/212] qmlui: fix universe view context detaching --- qmlui/inputoutputmanager.cpp | 14 ++++++++++++++ qmlui/inputoutputmanager.h | 1 + .../qml/fixturesfunctions/FixturesAndFunctions.qml | 8 ++++++++ qmlui/qml/fixturesfunctions/UniverseGridView.qml | 9 +++++---- qmlui/qml/fixturesfunctions/qmldir | 1 + 5 files changed, 29 insertions(+), 4 deletions(-) diff --git a/qmlui/inputoutputmanager.cpp b/qmlui/inputoutputmanager.cpp index 1b2e3a813a..13e40a96a0 100644 --- a/qmlui/inputoutputmanager.cpp +++ b/qmlui/inputoutputmanager.cpp @@ -93,6 +93,20 @@ QStringList InputOutputManager::universeNames() const return m_ioMap->universeNames(); } +QString InputOutputManager::universeName(quint32 universeId) +{ + if (universeId == Universe::invalid()) + return tr("All universes"); + else + { + Universe *uni = m_ioMap->universe(universeId); + if (uni != nullptr) + return uni->name(); + } + + return QString(); +} + QVariant InputOutputManager::universesListModel() const { QVariantList universesList; diff --git a/qmlui/inputoutputmanager.h b/qmlui/inputoutputmanager.h index 5445e137c9..4bac8c40ee 100644 --- a/qmlui/inputoutputmanager.h +++ b/qmlui/inputoutputmanager.h @@ -70,6 +70,7 @@ protected slots: public: QVariant universes(); QStringList universeNames() const; + Q_INVOKABLE QString universeName(quint32 universeId); QVariant universesListModel() const; /** Get/Set the currently selected universe index */ diff --git a/qmlui/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/UniverseGridView.qml b/qmlui/qml/fixturesfunctions/UniverseGridView.qml index 1c6eb4c200..ad780bf84c 100644 --- a/qmlui/qml/fixturesfunctions/UniverseGridView.qml +++ b/qmlui/qml/fixturesfunctions/UniverseGridView.qml @@ -30,8 +30,9 @@ Flickable boundsBehavior: Flickable.StopAtBounds contentHeight: uniGrid.height + topbar.height + UISettings.bigItemHeight + property alias contextItem: uniGrid property string contextName: "UNIGRID" - property int uniStartAddr: viewUniverseCombo.currentIndex * 512 + property int uniStartAddr: contextManager.universeFilter * 512 property var fixtureClipboard: null function hasSettings() @@ -44,7 +45,7 @@ Flickable id: errorPopup standardButtons: Dialog.Ok title: qsTr("Error") - message: qsTr("Unable to perform the operation.\nThere is either not enough space or the target universe in invalid") + message: qsTr("Unable to perform the operation.\nThere is either not enough space or the target universe is invalid") onAccepted: close() } @@ -60,7 +61,7 @@ Flickable id: uniText height: UISettings.textSizeDefault * 2 labelColor: UISettings.fgLight - label: viewUniverseCombo.currentText + label: ioManager.universeName(contextManager.universeFilter) fontSize: UISettings.textSizeDefault * 1.5 fontBold: true } @@ -101,7 +102,7 @@ Flickable x: UISettings.iconSizeMedium anchors.top: topbar.bottom width: parent.width - (UISettings.iconSizeMedium * 3) - height: cellSize * gridSize.height + height: 32 * gridSize.height showIndices: 512 gridSize: Qt.size(24, 22) diff --git a/qmlui/qml/fixturesfunctions/qmldir b/qmlui/qml/fixturesfunctions/qmldir index 9c33c9b773..c270f28276 100644 --- a/qmlui/qml/fixturesfunctions/qmldir +++ b/qmlui/qml/fixturesfunctions/qmldir @@ -7,6 +7,7 @@ ColorToolFull 0.1 ../ColorToolFull.qml ContextMenuEntry 0.1 ../ContextMenuEntry.qml CustomCheckBox 0.1 ../CustomCheckBox.qml CustomComboBox 0.1 ../CustomComboBox.qml +CustomPopupDialog 0.1 ../popup/CustomPopupDialog.qml CustomScrollBar 0.1 ../CustomScrollBar.qml CustomSpinBox 0.1 ../CustomSpinBox.qml CustomDoubleSpinBox 0.1 ../CustomDoubleSpinBox.qml From 614cfa57176a8067ca86184ad2767123ee8959b6 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Sat, 20 Jan 2024 13:54:00 +0100 Subject: [PATCH 028/212] qmlui: remember 3D view camera position Reported: https://www.qlcplus.org/forum/viewtopic.php?t=16608 --- qmlui/mainview3d.cpp | 48 +++++++++++++++++++ qmlui/mainview3d.h | 29 +++++++++++ qmlui/qml/fixturesfunctions/3DView/3DView.qml | 14 ++++-- 3 files changed, 88 insertions(+), 3 deletions(-) diff --git a/qmlui/mainview3d.cpp b/qmlui/mainview3d.cpp index 6bcf45a857..55f8e28c34 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() @@ -196,6 +198,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()); 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/qml/fixturesfunctions/3DView/3DView.qml b/qmlui/qml/fixturesfunctions/3DView/3DView.qml index 393f6fb2cb..a4d5034f8d 100644 --- a/qmlui/qml/fixturesfunctions/3DView/3DView.qml +++ b/qmlui/qml/fixturesfunctions/3DView/3DView.qml @@ -343,9 +343,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 +455,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 +466,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) } From 0b8d2e8451bad5a652e8eca0d404bed5e79ba844 Mon Sep 17 00:00:00 2001 From: Nils Tijtgat Date: Sat, 20 Jan 2024 17:37:27 +0100 Subject: [PATCH 029/212] Fix shutter open for multi-head fixtures --- ui/src/fixturemanager.cpp | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/ui/src/fixturemanager.cpp b/ui/src/fixturemanager.cpp index 02f4dab1cd..6c6239628a 100644 --- a/ui/src/fixturemanager.cpp +++ b/ui/src/fixturemanager.cpp @@ -566,23 +566,6 @@ void FixtureManager::highlightFixture(quint32 id) GenericDMXSource* source = new GenericDMXSource(m_doc); m_fixtureToSourceMap.insert(id, source); - for (quint32 i = 0; i < fxi->channels(); i++) - { - const QLCChannel *channel = fxi->channel(i); - for (QLCCapability *cap : channel->capabilities()) - { - if (cap->preset() == QLCCapability::ShutterOpen || - cap->preset() == QLCCapability::LampOn) - { - // TO-DO: I'm not sure if this is correct if a fixture has multiple heads, will this open the shutter for all heads? - source->set(fxi->id(), i, cap->middle()); - - break; - } - - } - } - // Set the color of every Fixture to white, pan en tilt to 127, shutter to open and intensity to 255 for (int i = 0; i < fxi->heads(); i++) { @@ -616,6 +599,26 @@ void FixtureManager::highlightFixture(quint32 id) source->set(fxi->id(), tiltChannel, 127); } + QLCFixtureMode* mode = fxi->fixtureMode(); + foreach (quint32 shutter, head.shutterChannels()) + { + QLCChannel *ch = mode->channel(shutter); + if (ch == NULL) + continue; + + foreach (QLCCapability *cap, ch->capabilities()) + { + if (cap->preset() == QLCCapability::ShutterOpen || + cap->preset() == QLCCapability::LampOn) + { + source->set(fxi->id(), i, cap->middle()); + goto setIntensity; + } + + } + } + + setIntensity: quint32 intensityChannel = head.channelNumber(QLCChannel::Intensity, QLCChannel::MSB); source->set(fxi->id(), intensityChannel, 255); } From 5d9cd018f92eb2b052b7cdc52aba8314e232fe02 Mon Sep 17 00:00:00 2001 From: Oystein Steimler Date: Sun, 21 Jan 2024 02:11:19 +0100 Subject: [PATCH 030/212] resources: Add fixture definition for Showtec LED Par 64 Short V2 --- resources/fixtures/FixturesMap.xml | 1 + .../Showtec/Showtec-LED-Par-64-Short-V2.qxf | 66 +++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 resources/fixtures/Showtec/Showtec-LED-Par-64-Short-V2.qxf diff --git a/resources/fixtures/FixturesMap.xml b/resources/fixtures/FixturesMap.xml index ec99ea65aa..57ed37a3c8 100644 --- a/resources/fixtures/FixturesMap.xml +++ b/resources/fixtures/FixturesMap.xml @@ -1488,6 +1488,7 @@ + diff --git a/resources/fixtures/Showtec/Showtec-LED-Par-64-Short-V2.qxf b/resources/fixtures/Showtec/Showtec-LED-Par-64-Short-V2.qxf new file mode 100644 index 0000000000..494f5a917a --- /dev/null +++ b/resources/fixtures/Showtec/Showtec-LED-Par-64-Short-V2.qxf @@ -0,0 +1,66 @@ + + + + + Q Light Controller Plus + 4.12.7 + Oystein Steimler + + Showtec + LED Par 64 Short V2 + Color Changer + + + + + + Shutter + No Function + Full on + Strobe (Slow to fast) + + + Effect + No function + Program 1: 7-color fading + Program 2: 7-color jump + Program 3: Color dreaming + Program 4: Red fading + Program 5: Green fading + Program 6: Blue fading + Sound active (Ch 2 is audio sensivity) + + + Speed + Speed from slow to fast / Audio sensitivity from low to high + + + + + Red + Green + Blue + Dimmer + Strobe with dead zone + + + Built-in programs / Sound-controlled + Speed / Sensitivity + Strobe + + + Red + Green + Blue + + + Macro Colors + + + + + + + + + From 07c858dbbc37252dc17c32f8bdadd3be599256eb Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Sun, 21 Jan 2024 11:10:10 +0100 Subject: [PATCH 031/212] Update Showtec-LED-Par-64-Short-V2.qxf --- resources/fixtures/Showtec/Showtec-LED-Par-64-Short-V2.qxf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/fixtures/Showtec/Showtec-LED-Par-64-Short-V2.qxf b/resources/fixtures/Showtec/Showtec-LED-Par-64-Short-V2.qxf index 494f5a917a..ebc1cf2832 100644 --- a/resources/fixtures/Showtec/Showtec-LED-Par-64-Short-V2.qxf +++ b/resources/fixtures/Showtec/Showtec-LED-Par-64-Short-V2.qxf @@ -16,8 +16,8 @@ Shutter No Function - Full on - Strobe (Slow to fast) + Full on + Strobe (Slow to fast) Effect From fa5df81c8c34d2ee7736ac6ce35cf83096467e35 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Sun, 21 Jan 2024 12:13:02 +0100 Subject: [PATCH 032/212] qmlui: fix fixture add from universe or dmx view Reported: https://www.qlcplus.org/forum/viewtopic.php?t=16833 --- debian/changelog | 2 ++ qmlui/contextmanager.cpp | 2 ++ qmlui/js/FixtureDrag.js | 10 ++++------ qmlui/mainviewdmx.cpp | 3 +++ qmlui/qml/fixturesfunctions/DMXView.qml | 2 +- qmlui/qml/fixturesfunctions/UniverseGridView.qml | 4 ++++ 6 files changed, 16 insertions(+), 7 deletions(-) diff --git a/debian/changelog b/debian/changelog index d6ff84904f..ddf10b4082 100644 --- a/debian/changelog +++ b/debian/changelog @@ -5,6 +5,7 @@ qlcplus (4.12.8) stable; urgency=low * 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) * 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 @@ -51,6 +52,7 @@ qlcplus (4.12.8) stable; urgency=low * New fixture: Robe Spiider (thanks to Nicolò) * New fixture: Betopper LB230 (thanks to Viktor) * New fixtures: Varytec Typhoon True Kid 720Z RGBW IP65, Showtec Performer 2000 RGBAL (thanks to Clément Delabroye) + * New fixture: Showtec LED Par 64 Short V2 (thanks to Øystein Steimler) -- Massimo Callegari Sun, 17 Dec 2023 12:13:14 +0200 diff --git a/qmlui/contextmanager.cpp b/qmlui/contextmanager.cpp index b06952bf70..db8e49464e 100644 --- a/qmlui/contextmanager.cpp +++ b/qmlui/contextmanager.cpp @@ -1381,6 +1381,8 @@ void ContextManager::slotNewFixtureCreated(quint32 fxID, qreal x, qreal y, qreal qDebug() << "[ContextManager] New fixture created" << fxID; + if (m_uniGridView->isEnabled()) + m_monProps->setFixturePosition(fxID, 0, 0, QVector3D(0, 0, 0)); if (m_DMXView->isEnabled()) m_DMXView->createFixtureItem(fxID); if (m_2DView->isEnabled()) 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/mainviewdmx.cpp b/qmlui/mainviewdmx.cpp index a1aae20cc0..6084f5955b 100644 --- a/qmlui/mainviewdmx.cpp +++ b/qmlui/mainviewdmx.cpp @@ -103,6 +103,9 @@ void MainViewDMX::createFixtureItem(quint32 fxID) MonitorProperties *monProps = m_doc->monitorProperties(); quint32 itemFlags = monProps->fixtureFlags(fxID, 0, 0); + if (monProps->containsFixture(fxID) == false) + monProps->setFixturePosition(fxID, 0, 0, QVector3D(0, 0, 0)); + newFixtureItem->setParentItem(contextItem()); newFixtureItem->setProperty("fixtureObj", QVariant::fromValue(fixture)); if (itemFlags & MonitorProperties::HiddenFlag) diff --git a/qmlui/qml/fixturesfunctions/DMXView.qml b/qmlui/qml/fixturesfunctions/DMXView.qml index a4df0b86ab..f471b579d5 100644 --- a/qmlui/qml/fixturesfunctions/DMXView.qml +++ b/qmlui/qml/fixturesfunctions/DMXView.qml @@ -83,7 +83,7 @@ Rectangle } Component.onCompleted: contextManager.enableContext("DMX", true, flowLayout) - Component.onDestruction: if(contextManager) contextManager.enableContext("DMX", false, flowLayout) + Component.onDestruction: if (contextManager) contextManager.enableContext("DMX", false, flowLayout) } ScrollBar.vertical: CustomScrollBar { } diff --git a/qmlui/qml/fixturesfunctions/UniverseGridView.qml b/qmlui/qml/fixturesfunctions/UniverseGridView.qml index ad780bf84c..81694b84c0 100644 --- a/qmlui/qml/fixturesfunctions/UniverseGridView.qml +++ b/qmlui/qml/fixturesfunctions/UniverseGridView.qml @@ -109,6 +109,9 @@ Flickable 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) @@ -136,6 +139,7 @@ Flickable if (multiSelection === 0) contextManager.resetFixtureSelection() + console.log("prevFixtureID: " + prevFixtureID + "currentItemID: " + currentItemID) if (prevFixtureID != currentItemID && multiSelection === 0) contextManager.setFixtureIDSelection(prevFixtureID, false) From ed5ef78fbd605d00633bd504131355940f406374 Mon Sep 17 00:00:00 2001 From: Nils Tijtgat Date: Sun, 21 Jan 2024 18:17:53 +0100 Subject: [PATCH 033/212] Update documentation --- resources/docs/html_en_EN/fixturemanager.html | 7 +++++++ resources/icons/svg/lightning.svg | 3 +++ 2 files changed, 10 insertions(+) create mode 100644 resources/icons/svg/lightning.svg diff --git a/resources/docs/html_en_EN/fixturemanager.html b/resources/docs/html_en_EN/fixturemanager.html index 70a3c1d41c..83921e0a0a 100644 --- a/resources/docs/html_en_EN/fixturemanager.html +++ b/resources/docs/html_en_EN/fixturemanager.html @@ -100,6 +100,13 @@

    Controls

    Opens the
    Fixtures remapping window. + + + + Enables or disables the Highlight Fixtures features. When enabled, this will set the selected features to max brightness + so you can easily verify these fixture are patched correctly. + + diff --git a/resources/icons/svg/lightning.svg b/resources/icons/svg/lightning.svg new file mode 100644 index 0000000000..ab0a5b219f --- /dev/null +++ b/resources/icons/svg/lightning.svg @@ -0,0 +1,3 @@ + + + From 92b3d694da32b48db9567d35e0e68c2319b4e1e8 Mon Sep 17 00:00:00 2001 From: Nils Tijtgat Date: Sun, 21 Jan 2024 18:28:23 +0100 Subject: [PATCH 034/212] Add configuration to load lightning icon --- fixtureeditor/CMakeLists.txt | 3 ++- resources/icons/png/fixture_off.png | Bin 3795 -> 0 bytes ui/src/CMakeLists.txt | 11 ++++++++--- ui/src/fixturemanager.cpp | 6 +++--- ui/src/qlcui.qrc | 3 ++- 5 files changed, 15 insertions(+), 8 deletions(-) delete mode 100644 resources/icons/png/fixture_off.png diff --git a/fixtureeditor/CMakeLists.txt b/fixtureeditor/CMakeLists.txt index 1261c6ff27..489ee519a9 100644 --- a/fixtureeditor/CMakeLists.txt +++ b/fixtureeditor/CMakeLists.txt @@ -520,7 +520,6 @@ set(qlcui_resource_files "../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/fixture_off.png" "../ui/src/../../resources/icons/png/flash.png" "../ui/src/../../resources/icons/png/flower.png" "../ui/src/../../resources/icons/png/folder.png" @@ -548,6 +547,8 @@ set(qlcui_resource_files "../ui/src/../../resources/icons/png/laser.png" "../ui/src/../../resources/icons/png/ledbar_beams.png" "../ui/src/../../resources/icons/png/ledbar_pixels.png" + "../ui/src/../../resources/icons/png/lightning.png" + "../ui/src/../../resources/icons/png/lightning_off.png" "../ui/src/../../resources/icons/png/liveedit.png" "../ui/src/../../resources/icons/png/liveedit_vc.png" "../ui/src/../../resources/icons/png/lock.png" diff --git a/resources/icons/png/fixture_off.png b/resources/icons/png/fixture_off.png deleted file mode 100644 index 4ce1d8ddb4d576f8ae5876c925b8589579b238f1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3795 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 diff --git a/ui/src/CMakeLists.txt b/ui/src/CMakeLists.txt index c06e42a28a..5f57c1bdf2 100644 --- a/ui/src/CMakeLists.txt +++ b/ui/src/CMakeLists.txt @@ -353,9 +353,6 @@ set_source_files_properties("../../resources/icons/png/filesaveas.png" set_source_files_properties("../../resources/icons/png/fixture.png" PROPERTIES QT_RESOURCE_ALIAS "fixture.png" ) -set_source_files_properties("../../resources/icons/png/fixture_off.png" - PROPERTIES QT_RESOURCE_ALIAS "fixture_off.png" -) set_source_files_properties("../../resources/icons/png/flash.png" PROPERTIES QT_RESOURCE_ALIAS "flash.png" ) @@ -437,6 +434,12 @@ set_source_files_properties("../../resources/icons/png/ledbar_beams.png" set_source_files_properties("../../resources/icons/png/ledbar_pixels.png" PROPERTIES QT_RESOURCE_ALIAS "ledbar_pixels.png" ) +set_source_files_properties("../../resources/icons/png/lightning.png" + PROPERTIES QT_RESOURCE_ALIAS "lightning.png" +) +set_source_files_properties("../../resources/icons/png/lightning_off.png" + PROPERTIES QT_RESOURCE_ALIAS "lightning_off.png" +) set_source_files_properties("../../resources/icons/png/liveedit.png" PROPERTIES QT_RESOURCE_ALIAS "liveedit.png" ) @@ -681,6 +684,8 @@ set(qlcui_resource_files "../../resources/icons/png/laser.png" "../../resources/icons/png/ledbar_beams.png" "../../resources/icons/png/ledbar_pixels.png" + "../../resources/icons/png/lightning.png" + "../../resources/icons/png/lightning_off.png" "../../resources/icons/png/liveedit.png" "../../resources/icons/png/liveedit_vc.png" "../../resources/icons/png/lock.png" diff --git a/ui/src/fixturemanager.cpp b/ui/src/fixturemanager.cpp index 6c6239628a..9ce6b82c8e 100644 --- a/ui/src/fixturemanager.cpp +++ b/ui/src/fixturemanager.cpp @@ -1079,7 +1079,7 @@ void FixtureManager::initActions() connect(m_remapAction, SIGNAL(triggered(bool)), this, SLOT(slotRemap())); - m_highlightFixturesAction = new QAction(QIcon(":/fixture.png"), + m_highlightFixturesAction = new QAction(QIcon(":/lightning.png"), tr("Enable highlight fixtures..."), this); connect(m_highlightFixturesAction, SIGNAL(triggered(bool)), this, SLOT(slotHighlightFixtures())); @@ -1667,13 +1667,13 @@ void FixtureManager::slotHighlightFixtures() { if (m_highlightFixturesEnabled) { - m_highlightFixturesAction->setIcon(QIcon(":/fixture.png")); + m_highlightFixturesAction->setIcon(QIcon(":/lightning.png")); m_highlightFixturesAction->setIconText(tr("Enable highlight fixtures")); m_highlightFixturesAction->setToolTip(tr("Enable highlight fixtures")); } else { - m_highlightFixturesAction->setIcon(QIcon(":/fixture_off.png")); + m_highlightFixturesAction->setIcon(QIcon(":/lightning_off.png")); m_highlightFixturesAction->setIconText(tr("Disable highlight fixtures")); m_highlightFixturesAction->setToolTip(tr("Disable highlight fixtures")); } diff --git a/ui/src/qlcui.qrc b/ui/src/qlcui.qrc index d818447939..ab3a2c33b0 100644 --- a/ui/src/qlcui.qrc +++ b/ui/src/qlcui.qrc @@ -57,7 +57,6 @@ ../../resources/icons/png/filesave.png ../../resources/icons/png/filesaveas.png ../../resources/icons/png/fixture.png - ../../resources/icons/png/fixture_off.png ../../resources/icons/png/folder.png ../../resources/icons/png/qlcplus-fixtureeditor.png ../../resources/icons/png/flash.png @@ -83,6 +82,8 @@ ../../resources/icons/png/laser.png ../../resources/icons/png/ledbar_beams.png ../../resources/icons/png/ledbar_pixels.png + ../../resources/icons/png/lightning.png + ../../resources/icons/png/lightning_off.png ../../resources/icons/png/liveedit.png ../../resources/icons/png/liveedit_vc.png ../../resources/icons/png/lock.png From f3e4f5e655800ba1e9ee2fc78b05e7e3ae56e39c Mon Sep 17 00:00:00 2001 From: Nils Tijtgat Date: Sun, 21 Jan 2024 23:21:57 +0100 Subject: [PATCH 035/212] Add new lightning icon --- resources/icons/png/lightning.png | Bin 0 -> 381 bytes resources/icons/png/lightning_off.png | Bin 0 -> 1150 bytes resources/icons/svg/lightning_off.svg | 31 ++++++++++++++++++++++++++ 3 files changed, 31 insertions(+) create mode 100644 resources/icons/png/lightning.png create mode 100644 resources/icons/png/lightning_off.png create mode 100644 resources/icons/svg/lightning_off.svg diff --git a/resources/icons/png/lightning.png b/resources/icons/png/lightning.png new file mode 100644 index 0000000000000000000000000000000000000000..98654812257c1d1f9fc610f463322e0dcebe4187 GIT binary patch literal 381 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyegQrquI>dspPn*M3$2o3-wHHY zvLwham?2{?|ApWFvJYM@WfPA3qA}6Z@buZ%*KiBwCCtLgFx1E)*74?@*n)$e~;_riSzaz&d zSn4*MH!RMxo*mx%A>`(r$pw6@S0-1inZEg21i#h3uL~Y8Rqu4BqpPoX| zw{KtuPy{S^NtplhONQsu=6rqw62G{d1tk80;b}(3Np|k1oxO045XO04-WP3cFSE0O z%3e&0dsbR{&72!3byJZ4aYoj04vte?TyHYdj8`TK`<=S4Gvi(rMBL^V{*;#aZk6r({KVHw1mB;Q ze<-bR%ZmMmCeNF-CO7o7UbXta-KO{E7~@T8-upRRuNGTBN)LP#8}eeV>AUc#H*ukF z>q8&baoloYe>zF<{c+X1vD|l~x!;{vCXR zPA&=Z3uZ8V$a-NZpYZFSnYVnzq|Y(EIDb+_r#-Ra)5OE}=QN^AlES`kGB5gRB5EbQ z=U!y<9jUvDyYF8_n0yhH?}xQ=|0W4B)V z@LJNpZ_XLYA0Gb>auC*7l;!hDO!i@oepjKybsj#0vg;?7m|O_Da8jUlfy#P@3%ml# zo}cdS`*Q2@zv;Ji?!3Qhzp1Li=Hq9B!z=kO+GdLXlb)e!G%1*fECy^MtbasKxT)DGwH3xcIE-=2aDz@F4BPf?fOm?J1C3^M3M&*3_>W z8OI9y%D;%kA6az4Fn!Ti9hU8X1?TM#FPd!EZeXvS9oTUHkXHT+P1$2-(i49hefFNV zy-D5X{R=($=U-Ml5dX9@#_o{2)UKXOclJ*f|C8k^<1Z({dg^3B^uxK`pQq>A9k!XJ zsms++oczh>&etD{B)1D(wOT&!;f}M5l9t`;y6Z7px?xw8aozGxsae}Lv%Sw}{GHYy VdUsW$1TZZ!c)I$ztaD0e0sy!RQwsn9 literal 0 HcmV?d00001 diff --git a/resources/icons/svg/lightning_off.svg b/resources/icons/svg/lightning_off.svg new file mode 100644 index 0000000000..97138ea414 --- /dev/null +++ b/resources/icons/svg/lightning_off.svg @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + From ea23d00ec9353aba96620a9df89e54f8fc0377af Mon Sep 17 00:00:00 2001 From: Nils Tijtgat Date: Sun, 21 Jan 2024 23:56:32 +0100 Subject: [PATCH 036/212] Final fixes --- resources/docs/html_en_EN/fixturemanager.html | 2 +- resources/icons/svg/fixture_off.svg | 44 ------------------- ui/src/fixturemanager.h | 2 +- 3 files changed, 2 insertions(+), 46 deletions(-) delete mode 100644 resources/icons/svg/fixture_off.svg diff --git a/resources/docs/html_en_EN/fixturemanager.html b/resources/docs/html_en_EN/fixturemanager.html index 83921e0a0a..e92cc2332d 100644 --- a/resources/docs/html_en_EN/fixturemanager.html +++ b/resources/docs/html_en_EN/fixturemanager.html @@ -101,7 +101,7 @@

    Controls

    - + Enables or disables the Highlight Fixtures features. When enabled, this will set the selected features to max brightness so you can easily verify these fixture are patched correctly. diff --git a/resources/icons/svg/fixture_off.svg b/resources/icons/svg/fixture_off.svg deleted file mode 100644 index f28c24d454..0000000000 --- a/resources/icons/svg/fixture_off.svg +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ui/src/fixturemanager.h b/ui/src/fixturemanager.h index 623c04e495..4533f0860b 100644 --- a/ui/src/fixturemanager.h +++ b/ui/src/fixturemanager.h @@ -126,7 +126,7 @@ public slots: /** Construct the list view and data view */ void initDataView(); - /** Checks if any fixtures have to be turned on/off if Test Fixtures is enabled */ + /** Checks if any fixtures have to be highlighted */ void runHighlightFixtures(); /** Creates a GenericDmxSource and turns the fixture on */ From 1c00560dac99a3884182a3de00e895959f1cb2e0 Mon Sep 17 00:00:00 2001 From: Oystein Steimler Date: Sun, 21 Jan 2024 01:43:37 +0100 Subject: [PATCH 037/212] resources: Add fixture definition for Bright XBAR --- resources/fixtures/Bright/Bright-XBAR.qxf | 612 ++++++++++++++++++++++ 1 file changed, 612 insertions(+) create mode 100644 resources/fixtures/Bright/Bright-XBAR.qxf diff --git a/resources/fixtures/Bright/Bright-XBAR.qxf b/resources/fixtures/Bright/Bright-XBAR.qxf new file mode 100644 index 0000000000..abc7c75a49 --- /dev/null +++ b/resources/fixtures/Bright/Bright-XBAR.qxf @@ -0,0 +1,612 @@ + + + + + Q Light Controller Plus + 4.12.7 + Øystein Steimler + + Bright + XBAR + LED Bar (Beams) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Shutter + Shutter closed + Shutter open + Strobe (Slow -> fast / 1-25Hz) + Shutter open + Strobe random (Slow -> fast) + Shutter open + Strobe Audio (Slow -> fast) + Shutter open + + + Maintenance + Instant Response + Short rise-time + Medium rise-time + Long rise-time + Extra-long rise-time + [Reserved] + [Reserved] + [Reserved] + + + Colour + Straw + Warm white + White + Cool white + Blue + Magenta + Red + Orange + Yellow + Green + Teal + Cyan + Blue + UV + + + Colour + Straw + Warm white + White + Cool white + Blue + Magenta + Red + Orange + Yellow + Green + Teal + Cyan + Blue + UV + Black + + + Gobo + No function (Cell color ignored) + 4 Step 1 -> 4 + Single 1 -> 12 + Twin 1 -> 12 + Quad 1 -> 12 + Random Few -> many + All cells + + + Dimmer + Color + + + Red + Green + Blue + + + Red + Green + Blue + White + Amber + UV + Dimmer + Dimmer fine + Strobe + Curve + Cell Color + Cell select + + + Dimmer1 + Dimmer fine1 + Dimmer2 + Dimmer fine2 + Dimmer3 + Dimmer fine3 + Dimmer4 + Dimmer fine4 + Dimmer5 + Dimmer fine5 + Dimmer6 + Dimmer fine6 + Dimmer7 + Dimmer fine7 + Dimmer8 + Dimmer fine8 + Dimmer9 + Dimmer fine9 + Dimmer10 + Dimmer fine10 + Dimmer11 + Dimmer fine11 + Dimmer12 + Dimmer fine12 + Red + Green + Blue + White + Amber + UV + Strobe + Curve + + + Red1 + Green1 + Blue1 + Dimmer1 + Dimmer fine1 + Red2 + Green2 + Blue2 + Dimmer2 + Dimmer fine2 + Red3 + Green3 + Blue3 + Dimmer3 + Dimmer fine3 + Red4 + Green4 + Blue4 + Dimmer4 + Dimmer fine4 + Red5 + Green5 + Blue5 + Dimmer5 + Dimmer fine5 + Red6 + Green6 + Blue6 + Dimmer6 + Dimmer fine6 + Red7 + Green7 + Blue7 + Dimmer7 + Dimmer fine7 + Red8 + Green8 + Blue8 + Dimmer8 + Dimmer fine8 + Red9 + Green9 + Blue9 + Dimmer9 + Dimmer fine9 + Red10 + Green10 + Blue10 + Dimmer10 + Dimmer fine10 + Red11 + Green11 + Blue11 + Dimmer11 + Dimmer fine11 + Red12 + Green12 + Blue12 + Dimmer12 + Dimmer fine12 + + 0 + 1 + 2 + 3 + 4 + + + 5 + 6 + 7 + 8 + 9 + + + 10 + 11 + 12 + 13 + 14 + + + 15 + 16 + 17 + 18 + 19 + + + 20 + 21 + 22 + 23 + 24 + + + 25 + 26 + 27 + 28 + 29 + + + 30 + 31 + 32 + 33 + 34 + + + 35 + 36 + 37 + 38 + 39 + + + 40 + 41 + 42 + 43 + 44 + + + 45 + 46 + 47 + 48 + 49 + + + 50 + 51 + 52 + 53 + 54 + + + 55 + 56 + 57 + 58 + 59 + + + + Red1 + Green1 + Blue1 + White1 + Amber1 + UV1 + Dimmer1 + Dimmer fine1 + Red2 + Green2 + Blue2 + White2 + Amber2 + UV2 + Dimmer2 + Dimmer fine2 + Red3 + Green3 + Blue3 + White3 + Amber3 + UV3 + Dimmer3 + Dimmer fine3 + Red4 + Green4 + Blue4 + White4 + Amber4 + UV4 + Dimmer4 + Dimmer fine4 + Red5 + Green5 + Blue5 + White5 + Amber5 + UV5 + Dimmer5 + Dimmer fine5 + Red6 + Green6 + Blue6 + White6 + Amber6 + UV6 + Dimmer6 + Dimmer fine6 + Red7 + Green7 + Blue7 + White7 + Amber7 + UV7 + Dimmer7 + Dimmer fine7 + Red8 + Green8 + Blue8 + White8 + Amber8 + UV8 + Dimmer8 + Dimmer fine8 + Red9 + Green9 + Blue9 + White9 + Amber9 + UV9 + Dimmer9 + Dimmer fine9 + Red10 + Green10 + Blue10 + White10 + Amber10 + UV10 + Dimmer10 + Dimmer fine10 + Red11 + Green11 + Blue11 + White11 + Amber11 + UV11 + Dimmer11 + Dimmer fine11 + Red12 + Green12 + Blue12 + White12 + Amber12 + UV12 + Dimmer12 + Dimmer fine12 + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + + + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + + + 16 + 17 + 18 + 19 + 20 + 21 + 22 + 23 + + + 24 + 25 + 26 + 27 + 28 + 29 + 30 + 31 + + + 32 + 33 + 34 + 35 + 36 + 37 + 38 + 39 + + + 40 + 41 + 42 + 43 + 44 + 45 + 46 + 47 + + + 48 + 49 + 50 + 51 + 52 + 53 + 54 + 55 + + + 56 + 57 + 58 + 59 + 60 + 61 + 62 + 63 + + + 64 + 65 + 66 + 67 + 68 + 69 + 70 + 71 + + + 72 + 73 + 74 + 75 + 76 + 77 + 78 + 79 + + + 80 + 81 + 82 + 83 + 84 + 85 + 86 + 87 + + + 88 + 89 + 90 + 91 + 92 + 93 + 94 + 95 + + + + + + + + + + + From cae010986b072cff04c40d822b9a5a29f1962248 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Mon, 22 Jan 2024 20:28:41 +0100 Subject: [PATCH 038/212] vcaudiotriggers: fix attached VC Slider not updating values (fix #1509) --- debian/changelog | 5 +++-- resources/fixtures/FixturesMap.xml | 3 +++ ui/src/audiobar.cpp | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/debian/changelog b/debian/changelog index ddf10b4082..5f31814ad2 100644 --- a/debian/changelog +++ b/debian/changelog @@ -12,6 +12,7 @@ qlcplus (4.12.8) stable; urgency=low * 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/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 * 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 @@ -52,9 +53,9 @@ qlcplus (4.12.8) stable; urgency=low * New fixture: Robe Spiider (thanks to Nicolò) * New fixture: Betopper LB230 (thanks to Viktor) * New fixtures: Varytec Typhoon True Kid 720Z RGBW IP65, Showtec Performer 2000 RGBAL (thanks to Clément Delabroye) - * New fixture: Showtec LED Par 64 Short V2 (thanks to Øystein Steimler) + * New fixtures: Showtec LED Par 64 Short V2, Bright XBAR (thanks to Øystein Steimler) - -- Massimo Callegari Sun, 17 Dec 2023 12:13:14 +0200 + -- Massimo Callegari Sun, 18 Feb 2024 12:13:14 +0200 qlcplus (4.12.7) stable; urgency=low diff --git a/resources/fixtures/FixturesMap.xml b/resources/fixtures/FixturesMap.xml index 57ed37a3c8..15f21d4f86 100644 --- a/resources/fixtures/FixturesMap.xml +++ b/resources/fixtures/FixturesMap.xml @@ -280,6 +280,9 @@ + + + diff --git a/ui/src/audiobar.cpp b/ui/src/audiobar.cpp index ec46f5fc98..b092198b76 100644 --- a/ui/src/audiobar.cpp +++ b/ui/src/audiobar.cpp @@ -191,7 +191,7 @@ void AudioBar::checkWidgetFunctionality() else if (m_widget->type() == VCWidget::SliderWidget) { VCSlider *slider = (VCSlider *)m_widget; - slider->setSliderValue(m_value); + slider->setSliderValue(m_value, true, true); } else if (m_widget->type() == VCWidget::SpeedDialWidget) { From bf9a1d2ed8e83063a4daa8dbe551095db6bdbdcf Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Sat, 27 Jan 2024 22:34:54 +0100 Subject: [PATCH 039/212] qmlui: improve beat capability from plugins --- engine/src/inputoutputmap.cpp | 45 ++++++++++++-------------- engine/src/inputoutputmap.h | 4 +-- plugins/interfaces/qlcioplugin.h | 3 +- plugins/midi/src/common/midiplugin.cpp | 5 +-- plugins/os2l/os2lplugin.cpp | 2 +- qmlui/inputoutputmanager.cpp | 6 ++-- 6 files changed, 31 insertions(+), 34 deletions(-) diff --git a/engine/src/inputoutputmap.cpp b/engine/src/inputoutputmap.cpp index f4c6cae17f..24c2d5630f 100644 --- a/engine/src/inputoutputmap.cpp +++ b/engine/src/inputoutputmap.cpp @@ -408,10 +408,10 @@ bool InputOutputMap::setInputPatch(quint32 universe, const QString &pluginName, currProfile = currInPatch->profile(); disconnect(currInPatch, SIGNAL(inputValueChanged(quint32,quint32,uchar,const QString&)), this, SIGNAL(inputValueChanged(quint32,quint32,uchar,const QString&))); - if (currInPatch->pluginName() == "MIDI") + if (currInPatch->plugin()->capabilities() & QLCIOPlugin::Beats) { disconnect(currInPatch, SIGNAL(inputValueChanged(quint32,quint32,uchar,const QString&)), - this, SLOT(slotMIDIBeat(quint32,quint32,uchar))); + this, SLOT(slotPluginBeat(quint32,quint32,uchar,const QString&))); } } InputPatch *ip = NULL; @@ -441,10 +441,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&))); } } } @@ -997,7 +997,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); @@ -1054,35 +1054,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) @@ -1294,5 +1291,3 @@ bool InputOutputMap::saveXML(QXmlStreamWriter *doc) const return true; } - - diff --git a/engine/src/inputoutputmap.h b/engine/src/inputoutputmap.h index 82ea794025..7e63928f6e 100644 --- a/engine/src/inputoutputmap.h +++ b/engine/src/inputoutputmap.h @@ -580,7 +580,7 @@ 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 }; @@ -592,7 +592,7 @@ private slots: 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/plugins/interfaces/qlcioplugin.h b/plugins/interfaces/qlcioplugin.h index 4b46f09cf0..cb36f97cc6 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 }; /** diff --git a/plugins/midi/src/common/midiplugin.cpp b/plugins/midi/src/common/midiplugin.cpp index 452a690b7d..4536cfbc4c 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; } /***************************************************************************** @@ -330,7 +330,8 @@ void MidiPlugin::slotValueChanged(const QVariant& uid, ushort channel, uchar val MidiInputDevice* dev = m_enumerator->inputDevices().at(i); if (dev->uid() == uid) { - emit valueChanged(UINT_MAX, i, channel, value); + emit valueChanged(UINT_MAX, i, channel, value, + channel == CHANNEL_OFFSET_MBC_BEAT ? "beat" : ""); break; } } diff --git a/plugins/os2l/os2lplugin.cpp b/plugins/os2l/os2lplugin.cpp index 7c18228ed6..6b65af1274 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() diff --git a/qmlui/inputoutputmanager.cpp b/qmlui/inputoutputmanager.cpp index 13e40a96a0..90f20b15a0 100644 --- a/qmlui/inputoutputmanager.cpp +++ b/qmlui/inputoutputmanager.cpp @@ -803,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 @@ -820,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: From 0923c1a27a71e37e3ceb5caaba93f3268ccff388 Mon Sep 17 00:00:00 2001 From: Lachlan Hicks <42596763+yestalgia@users.noreply.github.com> Date: Sun, 28 Jan 2024 17:57:35 +1100 Subject: [PATCH 040/212] Fix: "Issue Confirmed" badge link --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c9c2b617e6..a6685dcf0f 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ Further guidelines are available in the [CONTRIBUTING.md](CONTRIBUTING.md) docum ### 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) +[![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 🚧 @@ -180,4 +180,4 @@ Licensed under the Apache License, Version 2.0 (the "License"); you may not use 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) \ No newline at end of file +![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) From 4c79a3a3f18e0829f0a10f34c8289a5ef856826d Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Sun, 28 Jan 2024 13:12:15 +0100 Subject: [PATCH 041/212] qmlui: improve beat panel UI --- qmlui/inputoutputmanager.cpp | 18 +++++++++--------- qmlui/js/GenericHelpers.js | 2 +- qmlui/qml/BeatGeneratorsPanel.qml | 7 +++++-- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/qmlui/inputoutputmanager.cpp b/qmlui/inputoutputmanager.cpp index 90f20b15a0..5e244b7587 100644 --- a/qmlui/inputoutputmanager.cpp +++ b/qmlui/inputoutputmanager.cpp @@ -732,20 +732,20 @@ QVariant InputOutputManager::beatGeneratorsList() internalMap.insert("privateName", ""); genList.append(internalMap); - // add the currently open MIDI input devices + // add the currently open input devices that support beats foreach (Universe *uni, m_ioMap->universes()) { InputPatch *ip = uni->inputPatch(); - if (ip == nullptr || ip->pluginName() != "MIDI") + if (ip == nullptr || (ip->plugin()->capabilities() & QLCIOPlugin::Beats) == 0) continue; - QVariantMap midiInMap; - midiInMap.insert("type", "MIDI"); - midiInMap.insert("name", ip->inputName()); - midiInMap.insert("uni", uni->id()); - midiInMap.insert("line", ip->input()); - midiInMap.insert("privateName", ""); - genList.append(midiInMap); + QVariantMap pluginMap; + pluginMap.insert("type", "PLUGIN"); + pluginMap.insert("name", ip->inputName()); + pluginMap.insert("uni", uni->id()); + pluginMap.insert("line", ip->input()); + pluginMap.insert("privateName", ip->pluginName()); + genList.append(pluginMap); } // add the currently selected audio input device 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/qml/BeatGeneratorsPanel.qml b/qmlui/qml/BeatGeneratorsPanel.qml index 6c2a4b9d59..f67496d3af 100644 --- a/qmlui/qml/BeatGeneratorsPanel.qml +++ b/qmlui/qml/BeatGeneratorsPanel.qml @@ -20,6 +20,8 @@ import QtQuick 2.6 import QtQuick.Controls 2.1 import QtQuick.Layouts 1.1 + +import "GenericHelpers.js" as Helpers import "." Rectangle @@ -89,6 +91,7 @@ Rectangle width: parent.width height: UISettings.bigItemHeight * 2 + clip: true boundsBehavior: Flickable.StopAtBounds delegate: @@ -99,11 +102,11 @@ Rectangle Component.onCompleted: { - if (modelData.type === "MIDI") + if (modelData.type === "PLUGIN") { iconBox.color = "white" iconBox.visible = true - genIcon.source = "qrc:/midiplugin.svg" + genIcon.source = Helpers.pluginIconFromName(modelData.privateName) } else if (modelData.type === "AUDIO") { From e0fe51b5003168939fd531c458b6c4d3cdb5674e Mon Sep 17 00:00:00 2001 From: Itay-Lifshitz <127589280+Itay-Lifshitz@users.noreply.github.com> Date: Sun, 28 Jan 2024 20:57:50 +0800 Subject: [PATCH 042/212] Webaccess frame enable disable v2 (#1485) * Synchronization of frame enable/disable state between web page and application Synchronization of frame header caption and page selector * change button enable/disable state when Frame enable check/uncheck * change slider enable/disable state when Frame enable check/uncheck * change knob enable/disable state when Frame enable check/uncheck * change label enable/disable state when Frame enable check/uncheck * change clock enable/disable state when Frame enable check/uncheck * change cue enable/disable state when Frame enable check/uncheck * remove console log in virtualconsole.js * minor fix * Style: Selector order * Style: Remove unit from 0 values * Style: Comment whitespace * Style: Shorten simple colour hex values * Style: Always provide base when using parseInt() * Add: Better logging for websocket errors * Style: Missed hex length and units for 0 values --------- Co-authored-by: Lachlan Hicks <42596763+yestalgia@users.noreply.github.com> --- ui/src/virtualconsole/vcframe.cpp | 2 + ui/src/virtualconsole/vcframe.h | 3 + ui/src/virtualconsole/vcwidget.cpp | 2 + ui/src/virtualconsole/vcwidget.h | 3 + webaccess/res/simpledesk.js | 3 +- webaccess/res/virtualconsole.css | 73 ++++++- webaccess/res/virtualconsole.js | 219 +++++++++++++++---- webaccess/res/websocket.js | 12 + webaccess/src/webaccess.cpp | 337 ++++++++++++++++++++++------- webaccess/src/webaccess.h | 6 + 10 files changed, 531 insertions(+), 129 deletions(-) diff --git a/ui/src/virtualconsole/vcframe.cpp b/ui/src/virtualconsole/vcframe.cpp index 571f725e1b..2437b629b7 100644 --- a/ui/src/virtualconsole/vcframe.cpp +++ b/ui/src/virtualconsole/vcframe.cpp @@ -123,6 +123,8 @@ void VCFrame::setDisableState(bool disable) } m_disableState = disable; + + emit disableStateChanged(disable); updateFeedback(); } diff --git a/ui/src/virtualconsole/vcframe.h b/ui/src/virtualconsole/vcframe.h index 0769ebf07b..be79eb197e 100644 --- a/ui/src/virtualconsole/vcframe.h +++ b/ui/src/virtualconsole/vcframe.h @@ -120,6 +120,9 @@ class VCFrame : public VCWidget QSize originalSize() const; +signals: + void disableStateChanged(bool disable); + protected slots: void slotCollapseButtonToggled(bool toggle); diff --git a/ui/src/virtualconsole/vcwidget.cpp b/ui/src/virtualconsole/vcwidget.cpp index a23643c81f..ac5457d330 100644 --- a/ui/src/virtualconsole/vcwidget.cpp +++ b/ui/src/virtualconsole/vcwidget.cpp @@ -192,6 +192,8 @@ void VCWidget::setDisableState(bool disable) setEnabled(!disable); enableWidgetUI(!disable); } + + emit disableStateChanged(m_disableState); } void VCWidget::enableWidgetUI(bool enable) diff --git a/ui/src/virtualconsole/vcwidget.h b/ui/src/virtualconsole/vcwidget.h index 63574764b4..d229e323ab 100644 --- a/ui/src/virtualconsole/vcwidget.h +++ b/ui/src/virtualconsole/vcwidget.h @@ -167,6 +167,9 @@ class VCWidget : public QWidget bool isDisabled(); +signals: + void disableStateChanged(bool disable); + protected: bool m_disableState; diff --git a/webaccess/res/simpledesk.js b/webaccess/res/simpledesk.js index 415cdbaac1..b09bb072b6 100644 --- a/webaccess/res/simpledesk.js +++ b/webaccess/res/simpledesk.js @@ -39,8 +39,9 @@ function connect() { }, 1000); }; - websocket.onerror = function() { + websocket.onerror = function(ev) { console.error("QLC+ connection encountered error. Closing socket"); + console.error("Error: " + ev.data) ws.close(); }; diff --git a/webaccess/res/virtualconsole.css b/webaccess/res/virtualconsole.css index 98287ecc06..0c40332e83 100644 --- a/webaccess/res/virtualconsole.css +++ b/webaccess/res/virtualconsole.css @@ -23,6 +23,10 @@ form { text-align: center; } +.vcbutton-disabled { + color: #A0A0A0!important; +} + .vccuelist { position: absolute; border: 1px solid #777777; @@ -47,6 +51,11 @@ form { text-align: center; } +.vccuelistButton-disabled { + background: #888; + opacity: 0.5; +} + .vccuelistButtonPaused { background: #5B81FF!important; } @@ -62,6 +71,11 @@ form { width: 27px; } +.vccuelistFadeButton-disabled { + background: #888; + opacity: 0.5; +} + .vccuelistProgress { margin-top: 2px; width: 100%; @@ -106,6 +120,14 @@ table.hovertable tr { background-color:#ffffff; } +table.cell-disabled tr { + background-color: #eee; + } + +table.hovertable tr:hover { + background-color:#CCD9FF; +} + table.hovertable td { border-width: 1px; padding: 3px; @@ -128,16 +150,20 @@ table.hovertable td { background: -webkit-gradient(linear, left top, left bottom, color-stop(0, #666666), color-stop(1, #000000)); background: -webkit-linear-gradient(top, #666666 0%, #000000 100%); border-radius: 3px; - margin: 2px 2px 0 36px; + margin: 2px 0 0 2px; padding: 0 0 0 3px; height: 32px; } .vcFrameText { - display: table-cell; - height: 32px; - vertical-align: middle; - font:normal 18px/1.0em sans-serif; + height: 30px; + font:normal 14px/1.0em sans-serif; + white-space: nowrap; + overflow: hidden; + text-overflow: clip; + display: flex; + align-items: center; + flex-direction: row; } .vcframeButton { @@ -158,7 +184,7 @@ table.hovertable td { display: inline-block; background: #000000; border-radius: 3px; - margin: 2px 0 0 0; + margin: 2px 0 0 2px; height: 32px; width: 100px; text-align: center; @@ -183,7 +209,7 @@ table.hovertable td { background: -webkit-gradient(linear, left top, left bottom, color-stop(0, #BC0A0A), color-stop(1, #370303)); background: -webkit-linear-gradient(top, #BC0A0A 0%, #370303 100%); border-radius: 3px; - margin: 2px 2px 0 36px; + margin: 2px 2px 0 2px; padding: 0 0 0 3px; height: 32px; } @@ -199,6 +225,10 @@ table.hovertable td { vertical-align: middle; } +.vclabel-disabled { + color: #A0A0A0!important; +} + .vcclock, .vcclockcount { font-size: 28px; font-weight: bold; @@ -217,9 +247,17 @@ table.hovertable td { font:normal 16px sans-serif; } +.vcslLabel-disabled { + color: #A0A0A0!important; +} + +input[type=range]{ + -webkit-appearance: none; +} + +/* Chrome, Firefox */ input[type="range"].vVertical { --rotate: 270; - -webkit-appearance: none; height: 4px; border: 1px solid #8E8A86; background-color: #888888; @@ -233,6 +271,10 @@ input[type="range"].vVertical { transform-origin:0% 50%; } +input[type="range"].vVertical-disabled { + background-color: #ccc!important; +} + input[type="range"]::-webkit-slider-thumb { -webkit-appearance: none; background-color: #999999; @@ -242,6 +284,21 @@ input[type="range"]::-webkit-slider-thumb { height: 36px; } +input[type="range"]:disabled::-webkit-slider-thumb { + -webkit-appearance: none; + background-color: #ccc; +} + +/* Firefox */ +input[type=range]::-moz-range-thumb { + background-color: #ccc; +} + +input[type="range"]:disabled::-webkit-slider-thumb { + -webkit-appearance: none; + background-color: #ccc; +} + .vcaudiotriggers { position: absolute; border: 1px solid #777777; diff --git a/webaccess/res/virtualconsole.js b/webaccess/res/virtualconsole.js index cecbc0d8a1..d6a273288c 100644 --- a/webaccess/res/virtualconsole.js +++ b/webaccess/res/virtualconsole.js @@ -58,38 +58,54 @@ window.addEventListener("load",() => { } }); +function setButtonDisableState(id, disable) { + var btnObj = document.getElementById(id); + if (disable === "1") { + btnObj.removeAttribute("onmousedown"); + btnObj.removeAttribute("onmouseup"); + btnObj.classList.add('vcbutton-disabled'); + } else { + btnObj.setAttribute("onmousedown", "buttonPress("+id+");"); + btnObj.setAttribute("onmouseup", "buttonRelease("+id+");"); + btnObj.classList.remove('vcbutton-disabled'); + } +} + +/* VCLabel */ +function setLabelDisableState(id, disable) { + var lblObj = document.getElementById("lbl" + id); + if (disable === "1") { + lblObj.classList.add('vclabel-disabled'); + } else { + lblObj.classList.remove('vclabel-disabled'); + } +} + /* VCCueList */ var cueListsIndices = new Array(); var showPanel = new Array(); +var isDisableCue = new Array(); function setCueIndex(id, idx) { var oldIdx = cueListsIndices[id]; if (oldIdx != undefined && oldIdx !== "-1") { var oldCueObj = document.getElementById(id + "_" + oldIdx); - oldCueObj.style.backgroundColor="#FFFFFF"; + oldCueObj.style.backgroundColor = ""; } cueListsIndices[id] = idx; var currCueObj = document.getElementById(id + "_" + idx); if (idx !== "-1") { - currCueObj.style.backgroundColor="#5E7FDF"; + currCueObj.style.setProperty("background-color", "#5E7FDF", "important"); } } function sendCueCmd(id, cmd) { + if (isDisableCue[id]) return; websocket.send(id + "|" + cmd); } -function checkMouseOut(id, idx) { - var obj = document.getElementById(id + "_" + idx); - if(idx == cueListsIndices[id]) { - obj.style.backgroundColor="#5E7FDF"; - } - else { - obj.style.backgroundColor="#FFFFFF"; - } -} - function enableCue(id, idx) { + if (isDisableCue[id]) return; setCueIndex(id, idx); websocket.send(id + "|STEP|" + idx); } @@ -107,7 +123,7 @@ function setCueProgress(id, percent, text) { function showSideFaderPanel(id, checked) { var progressBarObj = document.getElementById("fadePanel" + id); - showPanel[id] = parseInt(checked); + showPanel[id] = parseInt(checked, 10); if (checked === "1") { progressBarObj.style.display="block"; } else { @@ -127,6 +143,7 @@ function setCueButtonStyle(id, playImage, playPaused, stopImage, stopPaused) { } function wsShowCrossfadePanel(id) { + if (isDisableCue[id]) return; websocket.send(id + "|CUE_SHOWPANEL|" + showPanel[id]); } @@ -153,48 +170,103 @@ function setCueSideFaderValues(id, topPercent, bottomPercent, topStep, bottomSte } function cueCVchange(id) { + if (isDisableCue[id]) return; var cueCVObj = document.getElementById("cueC" + id); var msg = id + "|CUE_SIDECHANGE|" + cueCVObj.value; websocket.send(msg); } +function setCueDisableState(id, disable) { + isDisableCue[id] = parseInt(disable, 10); + var cueTable = document.getElementById("cueTable" + id); + var fadeObj = document.getElementById("fade" + id); + var playObj = document.getElementById("play" + id); + var stopObj = document.getElementById("stop" + id); + var nextObj = document.getElementById("next" + id); + var prevObj = document.getElementById("prev" + id); + var cueCObj = document.getElementById("cueC" + id); + var cueCTPObj = document.getElementById("cueCTP" + id); + var cueCBPObj = document.getElementById("cueCBP" + id); + + if (disable === "1") { + fadeObj.classList.add('vccuelistFadeButton-disabled'); + cueTable.classList.add('cell-disabled'); + playObj.classList.add('vccuelistButton-disabled'); + stopObj.classList.add('vccuelistButton-disabled'); + nextObj.classList.add('vccuelistButton-disabled'); + prevObj.classList.add('vccuelistButton-disabled'); + cueCObj.setAttribute("disabled", "diabled"); + cueCObj.classList.add('vVertical-disabled'); + cueCTPObj.classList.add('vcslLabel-disabled'); + cueCBPObj.classList.add('vcslLabel-disabled'); + } else { + fadeObj.classList.remove('vccuelistFadeButton-disabled'); + cueTable.classList.remove('cell-disabled'); + playObj.classList.remove('vccuelistButton-disabled'); + stopObj.classList.remove('vccuelistButton-disabled'); + nextObj.classList.remove('vccuelistButton-disabled'); + prevObj.classList.remove('vccuelistButton-disabled'); + cueCObj.removeAttribute("disabled"); + cueCObj.classList.remove('vVertical-disabled'); + cueCTPObj.classList.remove('vcslLabel-disabled'); + cueCBPObj.classList.remove('vcslLabel-disabled'); + } +} + /* VCFrame */ var framesWidth = new Array(); var framesHeight = new Array(); var framesTotalPages = new Array(); var framesCurrentPage = new Array(); +var frameDisableState = new Array(); +var frameCaption = new Array(); function updateFrameLabel(id) { - var framePageObj = document.getElementById("fr" + id + "Page"); - var newLabel = "Page " + (framesCurrentPage[id] + 1); - framePageObj.innerHTML = newLabel; + var framePageObj = document.getElementById("fr" + id + "Page"); + var newLabel = "Page " + (framesCurrentPage[id] + 1); + framePageObj.innerHTML = newLabel; + + var frameCaptionObj = document.getElementById("fr" + id + "Caption"); + var frMpHdr = document.getElementById("frMpHdr" + id); + var newCaption = frameCaption[id]; + if (frMpHdr) { // if multi page mode + newCaption = frameCaption[id] ? frameCaption[id] + " - " + newLabel : newLabel; + } + frameCaptionObj.innerHTML = newCaption; } function frameToggleCollapse(id) { var frameObj = document.getElementById("fr" + id); - var mpHeader = document.getElementById("frMpHdr" + id); + var vcframeHeader = document.getElementById("vcframeHeader" + id); + var frEnBtn = document.getElementById("frEnBtn" + id); + var frMpHdrPrev = document.getElementById("frMpHdrPrev" + id); + var frMpHdrNext = document.getElementById("frMpHdrNext" + id); + var frPglbl = document.getElementById("frPglbl" + id); + var origWidth = framesWidth[id]; var origHeight = framesHeight[id]; - if (frameObj.clientWidth === origWidth) - { + var ew = frEnBtn ? 36 : 0; + var pw = 0; + + if (frameObj.clientWidth === origWidth) { + pw = frMpHdrPrev && frMpHdrNext ? 64 : 0; frameObj.style.width = "200px"; - if (mpHeader) { - mpHeader.style.visibility = "hidden"; - } - } - else - { + if (frPglbl) frPglbl.style.width = "60px"; + if (frMpHdrPrev) frMpHdrPrev.style.display = "none"; + if (frMpHdrNext) frMpHdrNext.style.display = "none"; + vcframeHeader.style.width = (200 - pw - ew - 36) + "px"; + } else { + pw = frMpHdrPrev && frMpHdrNext ? 168 : 0; frameObj.style.width = origWidth + "px"; - if (mpHeader) { - mpHeader.style.visibility = "visible"; - } + if (frPglbl) frPglbl.style.width = "100px"; + if (frMpHdrPrev) frMpHdrPrev.style.display = "block"; + if (frMpHdrNext) frMpHdrNext.style.display = "block"; + vcframeHeader.style.width = (origWidth - pw - ew - 36) + "px"; } - if (frameObj.clientHeight === origHeight) { frameObj.style.height = "36px"; - } - else { + } else { frameObj.style.height = origHeight + "px"; } } @@ -208,7 +280,7 @@ function framePreviousPage(id) { } function setFramePage(id, page) { - var iPage = parseInt(page); + var iPage = parseInt(page, 10); if (framesCurrentPage[id] === iPage || iPage >= framesTotalPages[id]) { return; } var framePageObj = document.getElementById("fp" + id + "_" + framesCurrentPage[id]); framePageObj.style.visibility = "hidden"; @@ -218,8 +290,24 @@ function setFramePage(id, page) { updateFrameLabel(id); } +function setFrameDisableState(id, disable) { + var frameObj = document.getElementById("frEnBtn" + id); + if (disable === "1") { + frameDisableState[id] = 1; + frameObj.style.background = "#E0DFDF"; + } else { + frameDisableState[id] = 0; + frameObj.style.background = "#D7DE75"; + } +} + +function frameDisableStateChange(id) { + websocket.send(id + "|FRAME_DISABLE|" + frameDisableState[id]); +} + /* VCSlider with Knob */ var isDragging = new Array(); +var isDisableKnob = new Array(); var maxVal = new Array(); var minVal = new Array(); var initVal = new Array(); @@ -241,6 +329,44 @@ function wsSetSliderValue(id, sliderValue, displayValue) { getPositionFromValue(sliderValue, id); } +function setSliderDisableState(id, disable) { + var sliderObj = document.getElementById(id); + var slvObj = document.getElementById("slv" + id); + var slnObj = document.getElementById("sln" + id); + var pie = document.getElementById("pie" + id); + isDisableKnob[id] = parseInt(disable, 10); + isDragging[id] = false; + if (disable === "1") { + if (pie) { + if (inverted[id]) { + pie.style.setProperty('--color1', '#555'); + pie.style.setProperty('--color2', '#c0c0c0'); + } else { + pie.style.setProperty('--color1', '#c0c0c0'); + pie.style.setProperty('--color2', '#555'); + } + } + sliderObj.setAttribute("disabled", "diabled"); + sliderObj.classList.add('vVertical-disabled'); + slvObj.classList.add('vcslLabel-disabled'); + slnObj.classList.add('vcslLabel-disabled'); + } else { + if (pie) { + if (inverted[id]) { + pie.style.setProperty('--color1', '#555'); + pie.style.setProperty('--color2', 'lime'); + } else { + pie.style.setProperty('--color1', 'lime'); + pie.style.setProperty('--color2', '#555'); + } + } + sliderObj.removeAttribute("disabled"); + sliderObj.classList.remove('vVertical-disabled'); + slvObj.classList.remove('vcslLabel-disabled'); + slnObj.classList.remove('vcslLabel-disabled'); + } +} + function getPositionFromValue(val, id) { var knobRect = document.getElementById("knob" + id).getBoundingClientRect(); var pie = document.getElementById("pie" + id); @@ -255,14 +381,15 @@ function getPositionFromValue(val, id) { pie.style.setProperty('--degValue', Math.round(angle)); if (inverted[id]) { pie.style.setProperty('--color1', '#555'); - pie.style.setProperty('--color2', 'lime'); + pie.style.setProperty('--color2', isDisableKnob[id] ? '#c0c0c0' : 'lime'); } else { - pie.style.setProperty('--color1', 'lime'); + pie.style.setProperty('--color1', isDisableKnob[id] ? '#c0c0c0' : 'lime'); pie.style.setProperty('--color2', '#555'); } } function onMouseMove(e) { + if (isDisableKnob[selectedID]) return; if (isDragging[selectedID]) { pie = document.getElementById("pie" + selectedID); knob = document.getElementById("knob" + selectedID); @@ -297,7 +424,8 @@ function onMouseMove(e) { } } } -function onMouseUp() { +function onMouseUp() { + if (isDisableKnob[selectedID]) return; isDragging[selectedID] = false; var knob = document.getElementById("knob" + selectedID); knob.style.transition = "transform 0.2s ease"; @@ -385,15 +513,29 @@ function wsUpdateClockTime(id, time) { var obj = document.getElementById(id); var s = time; var h, m; - h = parseInt(s / 3600); + h = parseInt(s / 3600, 10); s -= (h * 3600); - m = parseInt(s / 60); + m = parseInt(s / 60, 10); s -= (m * 60); var timeString = hmsToString(h, m, s); obj.innerHTML = timeString; } +function setClockDisableState(id, disable) { + var clockObj = document.getElementById(id); + + if (disable === "1") { + clockObj.removeAttribute("href"); + clockObj.removeAttribute("oncontextmenu"); + clockObj.classList.add('vclabel-disabled'); + } else { + clockObj.setAttribute("href", "javascript:controlWatch("+id+", 'S');"); + clockObj.setAttribute("oncontextmenu", "javascript:controlWatch("+id+", 'R'); return false;"); + clockObj.classList.remove('vclabel-disabled'); + } +} + /* VCMatrix */ var matrixID = 0; var m_isDragging = new Array(); @@ -430,7 +572,6 @@ function setMatrixComboValue(id, comboValue) { function matrixStartColorChange(id) { var colorObj = document.querySelector("#msc" + id); var colorMsg = id + "|MATRIX_COLOR_CHANGE|START|" + hexToUint(colorObj.value); - console.log(colorMsg); websocket.send(colorMsg); } @@ -521,7 +662,7 @@ window.addEventListener("load", (event) => { }); function setMatrixControlKnobValue(controlID, value) { - getPositionFromValueForMatrix(parseInt(value), parseInt(controlID)); + getPositionFromValueForMatrix(parseInt(value, 10), parseInt(controlID, 10)); } function wcMatrixPushButtonClicked(controlID) { diff --git a/webaccess/res/websocket.js b/webaccess/res/websocket.js index 6c1e0291ee..4abfdbd04a 100644 --- a/webaccess/res/websocket.js +++ b/webaccess/res/websocket.js @@ -48,17 +48,29 @@ function connect() { var msgParams = ev.data.split("|"); if (msgParams[1] === "BUTTON") { wsSetButtonState(msgParams[0], msgParams[2]); + } else if (msgParams[1] === "BUTTON_DISABLE") { + setButtonDisableState(msgParams[0], msgParams[2]); + } else if (msgParams[1] === "LABEL_DISABLE") { + setLabelDisableState(msgParams[0], msgParams[2]); } else if (msgParams[1] === "SLIDER") { // Slider message is |SLIDER|| wsSetSliderValue(msgParams[0], msgParams[2], msgParams[3]); + } else if (msgParams[1] === "SLIDER_DISABLE") { + setSliderDisableState(msgParams[0], msgParams[2]); } else if (msgParams[1] === "AUDIOTRIGGERS") { wsSetAudioTriggersEnabled(msgParams[0], msgParams[2]); } else if (msgParams[1] === "CUE") { wsSetCueIndex(msgParams[0], msgParams[2]); + } else if (msgParams[1] === "CUE_DISABLE") { + setCueDisableState(msgParams[0], msgParams[2]); } else if (msgParams[1] === "CLOCK") { wsUpdateClockTime(msgParams[0], msgParams[2]); + } else if (msgParams[1] === "CLOCK_DISABLE") { + setClockDisableState(msgParams[0], msgParams[2]); } else if (msgParams[1] === "FRAME") { setFramePage(msgParams[0], msgParams[2]); + } else if (msgParams[1] === "FRAME_DISABLE") { + setFrameDisableState(msgParams[0], msgParams[2]); } else if (msgParams[0] === "ALERT") { alert(msgParams[1]); } else if (msgParams[1] === "CUE_PROGRESS") { diff --git a/webaccess/src/webaccess.cpp b/webaccess/src/webaccess.cpp index 0c6eff9a93..d97637b4ef 100644 --- a/webaccess/src/webaccess.cpp +++ b/webaccess/src/webaccess.cpp @@ -787,6 +787,8 @@ void WebAccess::slotHandleWebSocketRequest(QHttpConnection *conn, QString data) frame->slotNextPage(); else if (cmdList[1] == "PREV_PG") frame->slotPreviousPage(); + else if (cmdList[1] == "FRAME_DISABLE") + frame->setDisableState(cmdList[2] == "1" ? false : true); } break; case VCWidget::ClockWidget: @@ -915,52 +917,102 @@ void WebAccess::slotFramePageChanged(int pageNum) sendWebSocketMessage(ba); } +void WebAccess::slotFrameDisableStateChanged(bool disable) +{ + VCWidget *frame = qobject_cast(sender()); + if (frame == NULL) + return; + + QString wsMessage = QString("%1|FRAME_DISABLE|%2").arg(frame->id()).arg(disable); + QByteArray ba = wsMessage.toUtf8(); + + sendWebSocketMessage(ba); +} + + QString WebAccess::getFrameHTML(VCFrame *frame) { QColor border(90, 90, 90); QSize origSize = frame->originalSize(); int w = frame->isCollapsed() ? 200 : origSize.width(); int h = frame->isCollapsed() ? 36 : origSize.height(); + // page select component width + margin + int pw = frame->multipageMode() ? frame->isCollapsed() ? 64 : 168 : 0; + // enable button width + margin + int ew = frame->isEnableButtonVisible() ? 36 : 0; + // collapse button width + margin + int cw = 36; + // header width + int hw = w - pw - ew - cw; + // header caption + QString caption = ""; + if (frame->multipageMode()) { + caption = QString(frame->caption()) != "" + ? QString("%1 - Page: %2").arg(frame->caption()).arg(frame->currentPage() + 1) + : QString("Page: %1").arg(frame->currentPage() + 1); + } else { + caption = QString(frame->caption()); + } QString str = "
    id()) + "\" " - "style=\"left: " + QString::number(frame->x()) + - "px; top: " + QString::number(frame->y()) + "px; width: " + QString::number(w) + - "px; height: " + QString::number(h) + "px; " - "background-color: " + frame->backgroundColor().name() + "; " + - getWidgetBackgroundImage(frame) + - "border: 1px solid " + border.name() + ";\">\n"; + "style=\"left: " + QString::number(frame->x()) + + "px; top: " + QString::number(frame->y()) + "px; width: " + QString::number(w) + + "px; height: " + QString::number(h) + "px; " + "background-color: " + frame->backgroundColor().name() + "; " + getWidgetBackgroundImage(frame) + + "border: 1px solid " + border.name() + ";\">\n"; str += getChildrenHTML(frame, frame->totalPagesNumber(), frame->currentPage()); if (frame->isHeaderVisible()) { - str += "id()) + ");\">\n"; - str += "
    foregroundColor().name() + ";\">
    " + frame->caption() + "
    \n"; + str += "
    "; + str += "id()) + ");\">\n"; + + str += "
    id()) + "\" style=\"color:" + + frame->foregroundColor().name() + "; width: "+ QString::number(hw) +"px \">"; + str += "
    id()) + "Caption\">" +(caption) + "
    \n"; + str += "
    \n"; + + m_JScode += "frameCaption[" + QString::number(frame->id()) + "] = \"" + QString(frame->caption()) + "\";\n"; + + if (frame->isEnableButtonVisible()) { + str += "id()) +"\" " + + "style=\" background-color: " + QString((frame->isDisabled() ? "#E0DFDF" : "#D7DE75" )) + "; \" " + + "href=\"javascript:frameDisableStateChange(" + QString::number(frame->id()) + ");\">" + + "\n"; + + m_JScode += "frameDisableState[" + QString::number(frame->id()) + "] = " + QString::number(frame->isDisabled() ? 1 : 0) + ";\n"; + connect(frame, SIGNAL(disableStateChanged(bool)), this, SLOT(slotFrameDisableStateChanged(bool))); + } m_JScode += "framesWidth[" + QString::number(frame->id()) + "] = " + QString::number(origSize.width()) + ";\n"; m_JScode += "framesHeight[" + QString::number(frame->id()) + "] = " + QString::number(origSize.height()) + ";\n"; if (frame->multipageMode()) { - str += "
    id()) + "\""; - str += "style=\"position: absolute; top: 0; right: 2px;\">\n"; - str += "id()) + ");\">"; - str += ""; - str += "
    id()) + "Page\">"; - str += QString ("%1 %2").arg(tr("Page")).arg(frame->currentPage() + 1) + "
    "; - str += "id()) + ");\">"; - str += "\n"; + str += "
    id()) + "\" style=\"display:flex; align-items:center; justify-content:center; flex-direction:row; margin-right: 2px;\">\n"; + + str += "id()) + "\" href=\"javascript:framePreviousPage(" + + QString::number(frame->id()) + ");\" style=\"display: " + QString(!frame->isCollapsed() ? "block" : "none") + "\">" + + ""; + + str += "
    id()) + "\" style=\"width: "+QString::number(frame->isCollapsed() ? 60 : 100)+"px; \" >"+ + "
    id()) + "Page\">" + + QString("Page: %1").arg(frame->currentPage() + 1) + "
    \n"; + + str += "id()) + "\" href=\"javascript:frameNextPage(" + + QString::number(frame->id()) + ");\" style=\"display: " + QString(!frame->isCollapsed() ? "block" : "none") + "\">" + + "\n"; + + str += "
    \n"; m_JScode += "framesCurrentPage[" + QString::number(frame->id()) + "] = " + QString::number(frame->currentPage()) + ";\n"; m_JScode += "framesTotalPages[" + QString::number(frame->id()) + "] = " + QString::number(frame->totalPagesNumber()) + ";\n\n"; - connect(frame, SIGNAL(pageChanged(int)), - this, SLOT(slotFramePageChanged(int))); + connect(frame, SIGNAL(pageChanged(int)), this, SLOT(slotFramePageChanged(int))); } + str += "
    \n"; } str += "
    \n"; @@ -974,46 +1026,83 @@ QString WebAccess::getSoloFrameHTML(VCSoloFrame *frame) QSize origSize = frame->originalSize(); int w = frame->isCollapsed() ? 200 : origSize.width(); int h = frame->isCollapsed() ? 36 : origSize.height(); + // page select component width + margin + int pw = frame->multipageMode() ? frame->isCollapsed() ? 64 : 168 : 0; + // enable button width + margin + int ew = frame->isEnableButtonVisible() ? 36 : 0; + // collapse button width + margin + int cw = 36; + // header width + int hw = w - pw - ew - cw; + // header caption + QString caption = ""; + if (frame->multipageMode()) { + caption = QString(frame->caption()) != "" + ? QString("%1 - Page: %2").arg(frame->caption()).arg(frame->currentPage() + 1) + : QString("Page: %1").arg(frame->currentPage() + 1); + } else { + caption = QString(frame->caption()); + } QString str = "
    id()) + "\" " - "style=\"left: " + QString::number(frame->x()) + - "px; top: " + QString::number(frame->y()) + "px; width: " + QString::number(w) + - "px; height: " + QString::number(h) + "px; " - "background-color: " + frame->backgroundColor().name() + "; " + - getWidgetBackgroundImage(frame) + - "border: 1px solid " + border.name() + ";\">\n"; + "style=\"left: " + QString::number(frame->x()) + + "px; top: " + QString::number(frame->y()) + "px; width: " + QString::number(w) + + "px; height: " + QString::number(h) + "px; " + "background-color: " + frame->backgroundColor().name() + "; " + getWidgetBackgroundImage(frame) + + "border: 1px solid " + border.name() + ";\">\n"; str += getChildrenHTML(frame, frame->totalPagesNumber(), frame->currentPage()); if (frame->isHeaderVisible()) { - str += "id()) + ");\">\n"; - str += "
    foregroundColor().name() + ";\">
    " + frame->caption() + "
    \n"; + str += "
    "; + str += "id()) + ");\">\n"; + + str += "
    id()) + "\" style=\"color:" + + frame->foregroundColor().name() + "; width: "+ QString::number(hw) +"px \">"; + str += "
    id()) + "Caption\">" +(caption) + "
    \n"; + str += "
    \n"; + + m_JScode += "frameCaption[" + QString::number(frame->id()) + "] = \"" + QString(frame->caption()) + "\";\n"; + + if (frame->isEnableButtonVisible()) { + str += "id()) +"\" " + + "style=\" background-color: " + QString((frame->isDisabled() ? "#E0DFDF" : "#D7DE75")) + "; \" " + + "href=\"javascript:frameDisableStateChange(" + QString::number(frame->id()) + ");\">" + + "\n"; + + m_JScode += "frameDisableState[" + QString::number(frame->id()) + "] = " + QString::number(frame->isDisabled() ? 1 : 0) + ";\n"; + connect(frame, SIGNAL(disableStateChanged(bool)), this, SLOT(slotFrameDisableStateChanged(bool))); + } m_JScode += "framesWidth[" + QString::number(frame->id()) + "] = " + QString::number(origSize.width()) + ";\n"; m_JScode += "framesHeight[" + QString::number(frame->id()) + "] = " + QString::number(origSize.height()) + ";\n"; if (frame->multipageMode()) { - str += "
    id()) + "\""; - str += "style=\"position: absolute; top: 0; right: 2px;\">\n"; - str += "id()) + ");\">"; - str += ""; - str += "
    id()) + "Page\">"; - str += QString ("%1 %2").arg(tr("Page")).arg(frame->currentPage() + 1) + "
    "; - str += "id()) + ");\">"; - str += "\n"; + str += "
    id()) + "\" style=\"display:flex; align-items:center; justify-content:center; flex-direction:row; margin-right: 2px;\">\n"; + + str += "id()) + "\" href=\"javascript:framePreviousPage(" + + QString::number(frame->id()) + ");\" style=\"display: " + QString(!frame->isCollapsed() ? "block" : "none") + "\">" + + ""; + + str += "
    id()) + "\" style=\"width: "+QString::number(frame->isCollapsed() ? 60 : 100)+"px; \" >"+ + "
    id()) + "Page\">" + + QString("Page: %1").arg(frame->currentPage() + 1) + "
    \n"; + + str += "id()) + "\" href=\"javascript:frameNextPage(" + + QString::number(frame->id()) + ");\" style=\"display: " + QString(!frame->isCollapsed() ? "block" : "none") + "\">" + + "\n"; + + str += "
    \n"; m_JScode += "framesCurrentPage[" + QString::number(frame->id()) + "] = " + QString::number(frame->currentPage()) + ";\n"; m_JScode += "framesTotalPages[" + QString::number(frame->id()) + "] = " + QString::number(frame->totalPagesNumber()) + ";\n\n"; - connect(frame, SIGNAL(pageChanged(int)), - this, SLOT(slotFramePageChanged(int))); + connect(frame, SIGNAL(pageChanged(int)), this, SLOT(slotFramePageChanged(int))); } + str += "
    \n"; } str += "
    \n"; @@ -1040,6 +1129,18 @@ void WebAccess::slotButtonStateChanged(int state) sendWebSocketMessage(wsMessage.toUtf8()); } +void WebAccess::slotButtonDisableStateChanged(bool disable) +{ + VCButton *btn = qobject_cast(sender()); + if (btn == NULL) + return; + + QString wsMessage = QString("%1|BUTTON_DISABLE|%2").arg(btn->id()).arg(disable); + QByteArray ba = wsMessage.toUtf8(); + + sendWebSocketMessage(ba); +} + QString WebAccess::getButtonHTML(VCButton *btn) { QString onCSS = ""; @@ -1051,11 +1152,13 @@ QString WebAccess::getButtonHTML(VCButton *btn) QString str = "
    x()) + "px; " "top: " + QString::number(btn->y()) + "px;\">\n"; - str += "id()) + "\" " - "href=\"javascript:void(0);\" " - "onmousedown=\"buttonPress(" + QString::number(btn->id()) + ");\" " - "onmouseup=\"buttonRelease(" + QString::number(btn->id()) + ");\" " - "style=\"" + str += "isDisabled() ? " vcbutton-disabled" : "") + "\" " + " id=\"" + QString::number(btn->id()) + "\" href=\"javascript:void(0);\" "; + if (!btn->isDisabled()) { + str += "onmousedown=\"buttonPress(" + QString::number(btn->id()) + ");\" " + "onmouseup=\"buttonRelease(" + QString::number(btn->id()) + ");\" "; + } + str += "style=\"" "width: " + QString::number(btn->width()) + "px; " "height: " + QString::number(btn->height()) + "px; " "color: " + btn->foregroundColor().name() + "; " + @@ -1065,6 +1168,8 @@ QString WebAccess::getButtonHTML(VCButton *btn) connect(btn, SIGNAL(stateChanged(int)), this, SLOT(slotButtonStateChanged(int))); + connect(btn, SIGNAL(disableStateChanged(bool)), + this, SLOT(slotButtonDisableStateChanged(bool))); return str; } @@ -1081,6 +1186,18 @@ void WebAccess::slotSliderValueChanged(QString val) sendWebSocketMessage(wsMessage.toUtf8()); } +void WebAccess::slotSliderDisableStateChanged(bool disable) +{ + VCSlider *slider = qobject_cast(sender()); + if (slider == NULL) + return; + + QString wsMessage = QString("%1|SLIDER_DISABLE|%2").arg(slider->id()).arg(disable); + QByteArray ba = wsMessage.toUtf8(); + + sendWebSocketMessage(ba); +} + QString WebAccess::getSliderHTML(VCSlider *slider) { QString slID = QString::number(slider->id()); @@ -1095,7 +1212,7 @@ QString WebAccess::getSliderHTML(VCSlider *slider) str += "
    "; - str += "
    " + slider->topLabelText() + "
    \n"; + str += "
    isDisabled() ? " vcslLabel-disabled" : "") + "\">" + slider->topLabelText() + "
    \n"; int mt = slider->invertedAppearance() ? -slider->height() + 50 : slider->height() - 50; int rotate = slider->invertedAppearance() ? 90 : 270; @@ -1106,7 +1223,7 @@ QString WebAccess::getSliderHTML(VCSlider *slider) max = slider->levelHighLimit(); } - str += "isDisabled() ? " vVertical-disabled" : "") + "\" " "id=\"" + slID + "\" " "oninput=\"slVchange(" + slID + ");\" ontouchmove=\"slVchange(" + slID + ");\" " "style=\"display: "+(slider->widgetStyle() == VCSlider::SliderWidgetStyle::WSlider ? "block" : "none") +"; " @@ -1115,7 +1232,10 @@ QString WebAccess::getSliderHTML(VCSlider *slider) "margin-left: " + QString::number(slider->width() / 2) + "px; " "--rotate: "+QString::number(rotate)+"\" " "min=\""+QString::number(min)+"\" max=\""+QString::number(max)+"\" " - "step=\"1\" value=\"" + QString::number(slider->sliderValue()) + "\">\n"; + "step=\"1\" value=\"" + QString::number(slider->sliderValue()) + "\""; + if (slider->isDisabled()) + str += " disabled "; + str += ">\n"; if (slider->widgetStyle() == VCSlider::SliderWidgetStyle::WKnob) { int shortSide = slider->width() > slider->height() ? slider->height() : slider->width(); @@ -1128,7 +1248,7 @@ QString WebAccess::getSliderHTML(VCSlider *slider) if (spotWidth < 6) spotWidth = 6; str += "
    "; - str += "
    "; + str += "
    isDisabled() ? "#c0c0c0" : "lime")+";--pieWidth: "+QString::number(pieWidth)+"px;\">"; str += "
    "; str += "
    "; str += "
    "; @@ -1139,32 +1259,53 @@ QString WebAccess::getSliderHTML(VCSlider *slider) m_JScode += "initVal[" + slID + "] = " + QString::number(slider->sliderValue()) + "; \n"; m_JScode += "inverted[" + slID + "] = " + QString::number(slider->invertedAppearance()) + "; \n"; m_JScode += "isDragging[" + slID + "] = false;\n"; + m_JScode += "isDisableKnob[" + slID + "] = "+QString::number(slider->isDisabled() ? 1 : 0)+";\n"; } - str += "
    " +slider->caption() + "
    "; + str += "
    isDisabled() ? " vcslLabel-disabled" : "") + "\">" +slider->caption() + "
    "; str += "
    \n"; str += "
    \n"; connect(slider, SIGNAL(valueChanged(QString)), this, SLOT(slotSliderValueChanged(QString))); + connect(slider, SIGNAL(disableStateChanged(bool)), + this, SLOT(slotSliderDisableStateChanged(bool))); return str; } +void WebAccess::slotLabelDisableStateChanged(bool disable) +{ + VCLabel *label = qobject_cast(sender()); + if (label == NULL) + return; + + QString wsMessage = QString("%1|LABEL_DISABLE|%2").arg(label->id()).arg(disable); + QByteArray ba = wsMessage.toUtf8(); + + sendWebSocketMessage(ba); +} + QString WebAccess::getLabelHTML(VCLabel *label) { QString str = "
    x()) + "px; " "top: " + QString::number(label->y()) + "px;\">\n"; - str += "
    width()) + "px; " - "height: " + QString::number(label->height()) + "px; " + str += "
    id()) + "\" " + "class=\"vclabel" + QString(label->isDisabled() ? " vclabel-disabled" : "") + "\" " + "style=\"width: " + QString::number(label->width()) + "px; "; + if (m_doc->mode() != Doc::Design) + str += "border: none!important; "; + str += "height: " + QString::number(label->height()) + "px; " "color: " + label->foregroundColor().name() + "; " "background-color: " + label->backgroundColor().name() + "; " + getWidgetBackgroundImage(label) + "\">" + label->caption() + "
    \n
    \n"; + connect(label, SIGNAL(disableStateChanged(bool)), + this, SLOT(slotLabelDisableStateChanged(bool))); + return str; } @@ -1302,6 +1443,18 @@ void WebAccess::slotCuePlaybackStateChanged() sendWebSocketMessage(wsMessage.toUtf8()); } +void WebAccess::slotCueDisableStateChanged(bool disable) +{ + VCCueList *cue = qobject_cast(sender()); + if (cue == NULL) + return; + + QString wsMessage = QString("%1|CUE_DISABLE|%2").arg(cue->id()).arg(disable); + QByteArray ba = wsMessage.toUtf8(); + + sendWebSocketMessage(ba); +} + QString WebAccess::getCueListHTML(VCCueList *cue) { QString str = "
    id()) + "\" " @@ -1342,36 +1495,36 @@ QString WebAccess::getCueListHTML(VCCueList *cue) if (cue->sideFaderMode() == VCCueList::FaderMode::Crossfade) { str += "
    "; - str += "
    id())+"\" class=\"vcslLabel\" style=\"top:0px;\">" + + str += "
    id())+"\" class=\"vcslLabel" + QString(cue->isDisabled() ? " vcslLabel-disabled" : "") + "\" style=\"top:0px;\">" + cue->topPercentageValue() + "
    \n"; str += "
    id())+"\" class=\"vcslLabel\" " "style=\"top:25px; border: solid 1px #aaa; background-color: "+ topStepBgColor +" \">" + cue->topStepValue() + "
    \n"; - str += "id())+"\" " + str += "isDisabled() ? " vVertical-disabled" : "") + "\" id=\"cueC"+QString::number(cue->id())+"\" " "oninput=\"cueCVchange("+QString::number(cue->id())+");\" ontouchmove=\"cueCVchange("+QString::number(cue->id())+");\" " "style=\"width: " + QString::number(cue->height() - 100) + "px; margin-top: " + QString::number(cue->height() - 100) + "px; margin-left: 22px;\" "; - str += "min=\"0\" max=\"100\" step=\"1\" value=\"" + QString::number(cue->sideFaderValue()) + "\">\n"; + str += "min=\"0\" max=\"100\" step=\"1\" value=\"" + QString::number(cue->sideFaderValue()) + "\" " + QString(cue->isDisabled() ? "disabled" : "") + " >\n"; str += "
    id())+"\" class=\"vcslLabel\" " "style=\"bottom:25px; border: solid 1px #aaa; background-color: "+ bottomStepBgColor +"\">" + cue->bottomStepValue() + "
    \n"; - str += "
    id())+"\" class=\"vcslLabel\" style=\"bottom:0px;\">" + + str += "
    id())+"\" class=\"vcslLabel" + QString(cue->isDisabled() ? " vcslLabel-disabled" : "") + "\" style=\"bottom:0px;\">" + cue->bottomPercentageValue() + "
    \n"; str += "
    "; } if (cue->sideFaderMode() == VCCueList::FaderMode::Steps) { str += "
    "; - str += "
    id())+"\" class=\"vcslLabel\" style=\"top:0px;\">" + + str += "
    id())+"\" class=\"vcslLabel" + QString(cue->isDisabled() ? " vcslLabel-disabled" : "") + "\" style=\"top:0px;\">" + cue->topPercentageValue() + "
    \n"; - str += "id())+"\" " + str += "isDisabled() ? " vVertical-disabled" : "") + "\" id=\"cueC"+QString::number(cue->id())+"\" " "oninput=\"cueCVchange("+QString::number(cue->id())+");\" ontouchmove=\"cueCVchange("+QString::number(cue->id())+");\" " "style=\"width: " + QString::number(cue->height() - 50) + "px; margin-top: " + QString::number(cue->height() - 50) + "px; margin-left: 22px;\" "; - str += "min=\"0\" max=\"255\" step=\"1\" value=\"" + QString::number(cue->sideFaderValue()) + "\">\n"; + str += "min=\"0\" max=\"255\" step=\"1\" value=\"" + QString::number(cue->sideFaderValue()) + "\" " + QString(cue->isDisabled() ? "disabled" : "") + " >\n"; str += "
    id())+"\" class=\"vcslLabel\" style=\"bottom:25px; border: solid 1px #aaa; \">" + cue->bottomStepValue() + "
    \n"; @@ -1383,7 +1536,7 @@ QString WebAccess::getCueListHTML(VCCueList *cue) str += "
    height() - 54) + "px; overflow: scroll;\" >\n"; - str += "\n"; + str += "
    isDisabled() ? " cell-disabled" : "")+"\" id=\"cueTable"+QString::number(cue->id())+"\" style=\"width: 100%;\">\n"; str += ""; str += ""; str += ""; @@ -1396,9 +1549,8 @@ QString WebAccess::getCueListHTML(VCCueList *cue) { QString stepID = QString::number(cue->id()) + "_" + QString::number(i); str += "id()) + ", " + QString::number(i) + ");\" " - "onmouseover=\"this.style.backgroundColor='#CCD9FF';\" " - "onmouseout=\"checkMouseOut(" + QString::number(cue->id()) + ", " + QString::number(i) + ");\">\n"; + "onclick=\"enableCue(" + QString::number(cue->id()) + ", " + QString::number(i) + ");\">\n"; + ChaserStep *step = chaser->stepAt(i); str += ""; Function* function = doc->function(step->fid); @@ -1499,7 +1651,7 @@ QString WebAccess::getCueListHTML(VCCueList *cue) if (cue->sideFaderMode() != VCCueList::FaderMode::None) { str += "
    "; - str += "id()) + "\" "; + str += "isDisabled() ? " vccuelistFadeButton-disabled" : "")+"\" id=\"fade" + QString::number(cue->id()) + "\" "; str += "href=\"javascript:wsShowCrossfadePanel(" + QString::number(cue->id()) + ");\">\n"; str += "\n"; } @@ -1533,19 +1685,19 @@ QString WebAccess::getCueListHTML(VCCueList *cue) stopButtonImage = "player_pause.png"; } - str += "id()) + "\" "; + str += "isDisabled() ? " vccuelistButton-disabled" : "") + QString(playbackButtonPaused ? " vccuelistButtonPaused" : "")+"\" id=\"play" + QString::number(cue->id()) + "\" "; str += "href=\"javascript:sendCueCmd(" + QString::number(cue->id()) + ", 'PLAY');\">\n"; str += "\n"; - str += "id()) + "\" "; + str += "isDisabled() ? " vccuelistButton-disabled" : "") + QString(stopButtonPaused ? " vccuelistButtonPaused" : "")+"\" id=\"stop" + QString::number(cue->id()) + "\" "; str += "href=\"javascript:sendCueCmd(" + QString::number(cue->id()) + ", 'STOP');\">\n"; str += "\n"; - str += "isDisabled() ? " vccuelistButton-disabled" : "") + "\" id=\"prev" + QString::number(cue->id()) + "\" href=\"javascript:sendCueCmd("; str += QString::number(cue->id()) + ", 'PREV');\">\n"; str += "\n"; - str += "isDisabled() ? " vccuelistButton-disabled" : "") + "\" id=\"next" + QString::number(cue->id()) + "\" href=\"javascript:sendCueCmd("; str += QString::number(cue->id()) + ", 'NEXT');\" style=\"margin-right: 0px!important;\">\n"; str += "\n"; @@ -1559,6 +1711,8 @@ QString WebAccess::getCueListHTML(VCCueList *cue) str += "
    \n"; + m_JScode += "isDisableCue[" + QString::number(cue->id()) + "] = " + QString::number(cue->isDisabled()) + ";\n"; + connect(cue, SIGNAL(stepChanged(int)), this, SLOT(slotCueIndexChanged(int))); connect(cue, SIGNAL(progressStateChanged()), @@ -1575,6 +1729,8 @@ QString WebAccess::getCueListHTML(VCCueList *cue) this, SLOT(slotCuePlaybackStateChanged())); connect(cue, SIGNAL(playbackStatusChanged()), this, SLOT(slotCuePlaybackStateChanged())); + connect(cue, SIGNAL(disableStateChanged(bool)), + this, SLOT(slotCueDisableStateChanged(bool))); return str; } @@ -1589,17 +1745,29 @@ void WebAccess::slotClockTimeChanged(quint32 time) sendWebSocketMessage(wsMessage.toUtf8()); } +void WebAccess::slotClockDisableStateChanged(bool disable) +{ + VCClock *clock = qobject_cast(sender()); + if (clock == NULL) + return; + + QString wsMessage = QString("%1|CLOCK_DISABLE|%2").arg(clock->id()).arg(disable); + QByteArray ba = wsMessage.toUtf8(); + + sendWebSocketMessage(ba); +} + QString WebAccess::getClockHTML(VCClock *clock) { QString str = "\n"; + connect(clock, SIGNAL(disableStateChanged(bool)), + this, SLOT(slotClockDisableStateChanged(bool))); + return str; } @@ -1910,8 +2085,8 @@ QString WebAccess::getVCHTML() { m_CSScode = "\n"; m_CSScode += "\n"; - m_JScode = "\n" - "\n" + m_JScode = "\n" + "\n" "\n"; - QString str = HTML_HEADER + m_CSScode + m_JScode + "\n\n" + widgetsHTML + "\n\n"; + QString str = HTML_HEADER + m_CSScode + "\n\n" + widgetsHTML + "\n\n" + m_JScode + ""; return str; } diff --git a/webaccess/src/webaccess.h b/webaccess/src/webaccess.h index 998ee56de3..ef90124043 100644 --- a/webaccess/src/webaccess.h +++ b/webaccess/src/webaccess.h @@ -88,15 +88,21 @@ protected slots: void slotVCLoaded(); void slotButtonStateChanged(int state); + void slotButtonDisableStateChanged(bool disable); + void slotLabelDisableStateChanged(bool disable); void slotSliderValueChanged(QString val); + void slotSliderDisableStateChanged(bool disable); void slotAudioTriggersToggled(bool toggle); void slotCueIndexChanged(int idx); void slotCueProgressStateChanged(); void slotCueShowSideFaderPanel(); void slotCueSideFaderValueChanged(); void slotCuePlaybackStateChanged(); + void slotCueDisableStateChanged(bool disable); void slotClockTimeChanged(quint32 time); + void slotClockDisableStateChanged(bool disable); void slotFramePageChanged(int pageNum); + void slotFrameDisableStateChanged(bool disable); void slotMatrixSliderValueChanged(int value); void slotMatrixStartColorChanged(); void slotMatrixEndColorChanged(); From 34091e8f05072c4176f5c9e6d0c61f1fa6a40c6d Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Sun, 28 Jan 2024 16:56:39 +0100 Subject: [PATCH 043/212] resources: 5 new fixtures (see changelog) --- debian/changelog | 4 + .../AFX/AFX-CLUB-MIX3-19x10W-RGBW.qxf | 167 ++++ .../Chauvet/Chauvet-COLORado-Batten-72x.qxf | 814 ++++++++++++++++++ .../Chauvet-Intimidator-Spot-375Z-IRC.qxf | 16 +- .../Chauvet-Intimidator-Spot-475ZX.qxf | 188 ++++ .../Chauvet/Chauvet-Legend-330SR-Spot.qxf | 4 +- .../Chauvet/Chauvet-Rogue-R2-Spot.qxf | 4 +- resources/fixtures/EK/EK-R3-Wash.qxf | 197 +++++ .../fixtures/Elumen8/Elumen8-MS-700PE.qxf | 6 +- .../Eurolite-LED-Theatre-COB-200-RGB+WW.qxf | 128 +++ resources/fixtures/FixturesMap.xml | 5 + resources/gobos/Chauvet/gobo00051.svg | 1 + resources/gobos/Chauvet/gobo00060.png | Bin 3478 -> 0 bytes resources/gobos/Chauvet/gobo00060.svg | 1 + 14 files changed, 1520 insertions(+), 15 deletions(-) create mode 100644 resources/fixtures/AFX/AFX-CLUB-MIX3-19x10W-RGBW.qxf create mode 100644 resources/fixtures/Chauvet/Chauvet-COLORado-Batten-72x.qxf create mode 100644 resources/fixtures/Chauvet/Chauvet-Intimidator-Spot-475ZX.qxf create mode 100644 resources/fixtures/EK/EK-R3-Wash.qxf create mode 100644 resources/fixtures/Eurolite/Eurolite-LED-Theatre-COB-200-RGB+WW.qxf create mode 100644 resources/gobos/Chauvet/gobo00051.svg delete mode 100644 resources/gobos/Chauvet/gobo00060.png create mode 100644 resources/gobos/Chauvet/gobo00060.svg diff --git a/debian/changelog b/debian/changelog index 5f31814ad2..323761a584 100644 --- a/debian/changelog +++ b/debian/changelog @@ -20,6 +20,7 @@ qlcplus (4.12.8) stable; urgency=low * 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 Slider knob appearance (thanks to Itay Lifshitz) + * Web Access: add support for VC Frame disable button (thanks to Itay Lifshitz) * Web Access: add VC Animation widget support (thanks to Itay Lifshitz) * Web Access: add event to notify Function start/stop * Input profiles: added PMJ 9 Faders Controller, Circus and MidiKey @@ -52,8 +53,11 @@ qlcplus (4.12.8) stable; urgency=low * 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 (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) -- Massimo Callegari Sun, 18 Feb 2024 12:13:14 +0200 diff --git a/resources/fixtures/AFX/AFX-CLUB-MIX3-19x10W-RGBW.qxf b/resources/fixtures/AFX/AFX-CLUB-MIX3-19x10W-RGBW.qxf new file mode 100644 index 0000000000..dd3765e445 --- /dev/null +++ b/resources/fixtures/AFX/AFX-CLUB-MIX3-19x10W-RGBW.qxf @@ -0,0 +1,167 @@ + + + + + Q Light Controller Plus + 4.12.8 GIT + Florian Faber + + AFX + CLUB-MIX3 19x10W RGBW + Color Changer + + + Shutter + Shutter closed + Shutter open + Strobe effect slow to fast + Shutter open + Pulse-effect in sequences slow to fast + Shutter open + Random strobe effect slow to fast + Shutter open + + + + + + + Colour + No function + Red + Green + Blue + White + Red + Green + Red + Blue + Red + White + Green + Blue + Green + White + Blue + White + R + G + B + R + G + B + W + 2700K + 3200K + 3500K + 5000K + 5500K + 6000K + 6500K + 7000K + 8000K + + + Colour + No Function + Below 3200K + 3200K-3500K + 3500K-5000K + 5000K-5500K + 5500K-6000K + 6000K-6500K + 6500K-7000K + 7000K-8000K + + + Effect + No Function + Macro Run 1 + Macro Run 2 + Macro Run 3 + Macro Run 4 + Macro Run 5 + Macro Run 6 + Macro Run 7 + Macro Run 8 + Macro Sound 1 + Macro Sound 2 + Macro Sound 3 + Macro Sound 4 + Macro Sound 5 + Macro Sound 7 + + + Speed + Macro Speed from Slow to Fast + + + + + + + + + + + + + + + Master dimmer + Strobe + Red + Green + Blue + White + Macro Color + Color Temp + Macro Run + Macro Speed + + + Master dimmer + Strobe + Red + Green + Blue + White + Red 1 + Green 1 + Blue 1 + White 1 + Red 2 + Green 2 + Blue 2 + White 2 + Red 3 + Green 3 + Blue 3 + White 3 + Macro Color + Color Temp + Macro Run + Macro Speed + + 2 + 3 + 4 + 5 + + + 6 + 7 + 8 + 9 + + + 10 + 11 + 12 + 13 + + + 14 + 16 + 15 + 17 + + + + + + + + + + + diff --git a/resources/fixtures/Chauvet/Chauvet-COLORado-Batten-72x.qxf b/resources/fixtures/Chauvet/Chauvet-COLORado-Batten-72x.qxf new file mode 100644 index 0000000000..17bcb89f9e --- /dev/null +++ b/resources/fixtures/Chauvet/Chauvet-COLORado-Batten-72x.qxf @@ -0,0 +1,814 @@ + + + + + Q Light Controller Plus + 4.12.8 GIT + Greg Perrone + + Chauvet + COLORado Batten 72x + Color Changer + + + + + + Shutter + No Function + Strobe, slow to fast + No Function + Pulse Strobe, slow to fast + No Function + Random strobe, slow to fast + + + Colour + No function + R:100%, G:0-100%, B:0, A:0%, W:0% + R:100-0%, G:100%, B:0%, A:0%, W:0% + R:0%, G:100%, B:0-100%, A:0%, W:0% + R:0%, G:100-0%, B:100%, A:0%, W:0% + R:0-100%, G:0%, B:100%, A:0%, W:0% + R:100%, G:0%, B:100-0%, A:0%, W:0% + R:100%, G:100-0%, B:0-100%, A:0%, W:0% + R:100-0%, G:100-0%, B:100%, A:0%, W:0% + R:100%, G:100%, B:100%, A:100%, W:100% + + + Intensity + White + No function + White 1 + White 2 + White 3 + White 4 + White 5 + White 6 + White 7 + White 8 + White 9 + White 10 + White 11 + White 12 + White 13 + White 14 + + + + + + + + + + Shutter + No Function + Strobe, slow to fast + No Function + Pulse Strobe, slow to fast + No Function + Random strobe, slow to fast + + + Shutter + No Function + Strobe, slow to fast + No Function + Pulse Strobe, slow to fast + No Function + Random strobe, slow to fast + + + Colour + No function + R:100%, G:0-100%, B:0, A:0%, W:0% + R:100-0%, G:100%, B:0%, A:0%, W:0% + R:0%, G:100%, B:0-100%, A:0%, W:0% + R:0%, G:100-0%, B:100%, A:0%, W:0% + R:0-100%, G:0%, B:100%, A:0%, W:0% + R:100%, G:0%, B:100-0%, A:0%, W:0% + R:100%, G:100-0%, B:0-100%, A:0%, W:0% + R:100-0%, G:100-0%, B:100%, A:0%, W:0% + R:100%, G:100%, B:100%, A:100%, W:100% + + + Colour + No function + R:100%, G:0-100%, B:0, A:0%, W:0% + R:100-0%, G:100%, B:0%, A:0%, W:0% + R:0%, G:100%, B:0-100%, A:0%, W:0% + R:0%, G:100-0%, B:100%, A:0%, W:0% + R:0-100%, G:0%, B:100%, A:0%, W:0% + R:100%, G:0%, B:100-0%, A:0%, W:0% + R:100%, G:100-0%, B:0-100%, A:0%, W:0% + R:100-0%, G:100-0%, B:100%, A:0%, W:0% + R:100%, G:100%, B:100%, A:100%, W:100% + + + Intensity + White + No function + White 1 + White 2 + White 3 + White 4 + White 5 + White 6 + White 7 + White 8 + White 9 + White 10 + White 11 + White 12 + White 13 + White 14 + + + Intensity + White + No function + White 1 + White 2 + White 3 + White 4 + White 5 + White 6 + White 7 + White 8 + White 9 + White 10 + White 11 + White 12 + White 13 + White 14 + + + + Speed + Preset dimmer speed from display menu + Dimmer speed mode off + Dimmer speed mode 1 (fastest) + Dimmer speed mode 2 + Dimmer speed mode 3 + Dimmer speed mode 4 (slowest) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Shutter + No Function + Strobe, slow to fast + No Function + Pulse Strobe, slow to fast + No Function + Random strobe, slow to fast + + + + + + + + Shutter + No Function + Strobe, slow to fast + No Function + Pulse Strobe, slow to fast + No Function + Random strobe, slow to fast + + + Colour + No function + R:100%, G:0-100%, B:0, A:0%, W:0% + R:100-0%, G:100%, B:0%, A:0%, W:0% + R:0%, G:100%, B:0-100%, A:0%, W:0% + R:0%, G:100-0%, B:100%, A:0%, W:0% + R:0-100%, G:0%, B:100%, A:0%, W:0% + R:100%, G:0%, B:100-0%, A:0%, W:0% + R:100%, G:100-0%, B:0-100%, A:0%, W:0% + R:100-0%, G:100-0%, B:100%, A:0%, W:0% + R:100%, G:100%, B:100%, A:100%, W:100% + + + Intensity + White + No function + White 1 + White 2 + White 3 + White 4 + White 5 + White 6 + White 7 + White 8 + White 9 + White 10 + White 11 + White 12 + White 13 + White 14 + + + + + + + + + + + + + Dimmer 1 + Hue 1 + Fine Hue 1 + Saturation 1 + Strobe 1 + Color Macros 1 + Color Temperature 1 + Dimmer 2 + Hue 2 + Fine Hue 2 + Saturation 2 + Strobe 2 + Color Macros 2 + Color Temperature 2 + Dimmer 3 + Hue 3 + Fine Hue 3 + Saturation 3 + Strobe 3 + Color Macros 3 + Color Temperature 3 + Dimmer Speed + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + + + 7 + 8 + 9 + 10 + 11 + 12 + 13 + + + 14 + 15 + 16 + 17 + 18 + 19 + 20 + + + + Dimmer + Fine Dimmer + Red 1 + Fine Red 1 + Green 1 + Fine Green 1 + Blue 1 + Fine Blue 1 + Amber 1 + Fine Amber 1 + White 1 + Fine White 1 + Color Macros 1 + Color Temperature 1 + Strobe 1 + Red 2 + Fine Red 2 + Green 2 + Fine Green 2 + Blue 2 + Fine Blue 2 + Amber 2 + Fine Amber 2 + White 2 + Fine White 2 + Color Macros 2 + Color Temperature 2 + Strobe 2 + Red 3 + Fine Red 3 + Green 3 + Fine Green 3 + Blue 3 + Fine Blue 3 + Amber 3 + Fine Amber 3 + White 3 + Fine White 3 + Color Macros 3 + Color Temperature 3 + Strobe 3 + Strobe All + Dimmer Speed + + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + + + 15 + 16 + 17 + 18 + 19 + 20 + 21 + 27 + 26 + 25 + 24 + 23 + 22 + + + 40 + 39 + 38 + 37 + 36 + 35 + 34 + 33 + 32 + 31 + 30 + 29 + 28 + + + + Red 1 + Fine Red 1 + Green 1 + Fine Green 1 + Blue 1 + Fine Blue 1 + Amber 1 + Fine Amber 1 + White 1 + Fine White 1 + Red 2 + Fine Red 2 + Green 2 + Fine Green 2 + Blue 2 + Fine Blue 2 + Amber 2 + Fine Amber 2 + White 2 + Fine White 2 + Red 3 + Fine Red 3 + Green 3 + Fine Green 3 + Blue 3 + Fine Blue 3 + Amber 3 + Fine Amber 3 + White 3 + Fine White 3 + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + + + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + + + 20 + 29 + 28 + 27 + 26 + 25 + 24 + 23 + 22 + 21 + + + + Dimmer + Fine Dimmer + Red 1 + Green 1 + Blue 1 + Amber 1 + White 1 + Color Macros 1 + Color Temperature 1 + Strobe 1 + Red 2 + Green 2 + Blue 2 + Amber 2 + White 2 + Color Macros 2 + Color Temperature 2 + Strobe 2 + Red 3 + Green 3 + Blue 3 + Amber 3 + White 3 + Color Macros 3 + Color Temperature 3 + Strobe 3 + Strobe All + + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + + + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17 + + + 18 + 19 + 20 + 21 + 22 + 23 + 24 + 25 + + + + Red 1 + Green 1 + Blue 1 + Amber 1 + White 1 + Red 2 + Green 2 + Blue 2 + Amber 2 + White 2 + Red 3 + Green 3 + Blue 3 + Amber 3 + White 3 + + 0 + 1 + 2 + 3 + 4 + + + 5 + 6 + 7 + 8 + 9 + + + 10 + 11 + 12 + 13 + 14 + + + + Dimmer + Fine Dimmer + Red 1 + Green 1 + Blue 1 + Amber 1 + Color Macros 1 + Color Temperature 1 + Strobe 1 + Red 2 + Green 2 + Blue 2 + Amber 2 + Color Macros 2 + Color Temperature 2 + Strobe 2 + Red 3 + Green 3 + Blue 3 + Amber 3 + Color Macros 3 + Color Temperature 3 + Strobe 3 + Strobe All + + 2 + 3 + 4 + 5 + 6 + 7 + 8 + + + 9 + 10 + 11 + 12 + 13 + 14 + 15 + + + 16 + 17 + 18 + 19 + 20 + 21 + 22 + + + + Red 1 + Green 1 + Blue 1 + Amber 1 + Red 2 + Green 2 + Blue 2 + Amber 2 + Red 3 + Green 3 + Blue 3 + Amber 3 + + 0 + 1 + 2 + 3 + + + 4 + 5 + 6 + 7 + + + 8 + 9 + 10 + 11 + + + + Dimmer + Fine Dimmer + Red 1 + Green 1 + Blue 1 + Color Macros 1 + Color Temperature 1 + Strobe 1 + Red 2 + Green 2 + Blue 2 + Color Macros 2 + Color Temperature 2 + Strobe 2 + Red 3 + Green 3 + Blue 3 + Color Macros 3 + Color Temperature 3 + Strobe 3 + Strobe All + + 2 + 3 + 4 + 5 + 6 + 7 + + + 8 + 9 + 10 + 11 + 12 + 13 + + + 14 + 15 + 16 + 17 + 18 + 19 + + + + Red 1 + Green 1 + Blue 1 + Red 2 + Green 2 + Blue 2 + Red 3 + Green 3 + Blue 3 + + 0 + 1 + 2 + + + 3 + 4 + 5 + + + 6 + 7 + 8 + + + + Dimmer + Hue + Fine Hue + Saturation + Strobe + Color Macros + Color Temperature + Dimmer Speed + + + Dimmer + Fine Dimmer + Red + Fine Red + Green + Fine Green + Blue + Fine Blue + Amber + Fine Amber + White + Fine White + Color Macros + Color Temperature + Strobe + Dimmer Speed + + + Red + Fine Red + Green + Fine Green + Blue + Fine Blue + Amber + Fine Amber + White + Fine White + + + Dimmer + Fine Dimmer + Red + Green + Blue + Amber + White + Color Macros + Color Temperature + Strobe + + + Red + Green + Blue + Amber + White + + + Dimmer + Fine Dimmer + Red + Green + Blue + Amber + Color Macros + Color Temperature + Strobe + + + Red + Green + Blue + Amber + + + Dimmer + Fine Dimmer + Red + Green + Blue + Color Macros + Color Temperature + Strobe + + + Red + Green + Blue + + + + + + + + + + diff --git a/resources/fixtures/Chauvet/Chauvet-Intimidator-Spot-375Z-IRC.qxf b/resources/fixtures/Chauvet/Chauvet-Intimidator-Spot-375Z-IRC.qxf index bfc92b07bd..a9a1d77223 100644 --- a/resources/fixtures/Chauvet/Chauvet-Intimidator-Spot-375Z-IRC.qxf +++ b/resources/fixtures/Chauvet/Chauvet-Intimidator-Spot-375Z-IRC.qxf @@ -3,7 +3,7 @@ Q Light Controller Plus - 4.12.6 + 4.12.8 GIT Andrew Pavlin Chauvet @@ -40,13 +40,13 @@ Gobo 5 Gobo 6 Gobo 7 - Gobo 7 shake, slow to fast - Gobo 6 shake, slow to fast - Gobo 5 shake, slow to fast - Gobo 4 shake, slow to fast - Gobo 3 shake, slow to fast - Gobo 2 shake, slow to fast - Gobo 1 shake, slow to fast + Gobo 7 shake, slow to fast + Gobo 6 shake, slow to fast + Gobo 5 shake, slow to fast + Gobo 4 shake, slow to fast + Gobo 3 shake, slow to fast + Gobo 2 shake, slow to fast + Gobo 1 shake, slow to fast Open Cycle gobos, slow to fast Stop diff --git a/resources/fixtures/Chauvet/Chauvet-Intimidator-Spot-475ZX.qxf b/resources/fixtures/Chauvet/Chauvet-Intimidator-Spot-475ZX.qxf new file mode 100644 index 0000000000..18e8d48662 --- /dev/null +++ b/resources/fixtures/Chauvet/Chauvet-Intimidator-Spot-475ZX.qxf @@ -0,0 +1,188 @@ + + + + + Q Light Controller Plus + 4.12.8 GIT + Harrison Bostock + + Chauvet + Intimidator Spot 475ZX + Moving Head + + + + + + + Colour + White + Orange + Lime green + Cyan + Red + Green + Magenta + Yellow + White + Color indexing + Color cycling rainbox, fast to slow + Stop + Color cycling rainbox, slow to fast + + + Gobo + Open + Gobo 1 + Gobo 2 + Gobo 3 + Gobo 4 + Gobo 5 + Gobo 6 + Gobo 7 + Gobo 7 shake, slow to fast + Gobo 6 shake, slow to fast + Gobo 5 shake, slow to fast + Gobo 4 shake, slow to fast + Gobo 3 shake, slow to fast + Gobo 2 shake, slow to fast + Gobo 1 shake, slow to fast + Open + Cycle gobos, slow to fast + Stop + Reverse cycle gobos, slow to fast + + + Gobo + Gobo Indexing + Gobo rotation, slow to fast + Stop + Reverse gobo rotation, slow to fast + Gobo bounce, slow to fast + + + Gobo + Open + Gobo 1 + Gobo 2 + Gobo 3 + Gobo 4 + Gobo 5 + Gobo 6 + Gobo 7 + Gobo 8 + Gobo 8 Shake Slow to fast + Gobo 7 Shake Slow to fast + Gobo 6 Shake Slow to fast + Gobo 5 Shake Slow to fast + Gobo 4 Shake Slow to fast + Gobo 3 Shake Slow to fast + Gobo 2 Shake Slow to fast + Gobo 1 Shake Slow to fast + Open + Reverse Cycle Effect Slow to fast + Cycle Effect slow to fast + + + Prism + Nothing + Prism 1 Round + Rotation Slow to fast + Reverse Rotation Slow to fast + Prism 1 Round + No function + Prism 2 Linear + Rotation Slow to fast + Reverse rotation Slow to fast + Prism 2 Linear + + + + + + Shutter + Off + On + Strobe, Slow to fast + Pulse Strobe slow to fast + Random Strobe slow to fast + On + + + Maintenance + Nothing + Blackout on Pantilt + Blackout on Colour Wheel move + Blackout on Gobo Wheels move + Blackout on Pan/tilt/colour wheel move + Blackout on pan/tilt/gobo wheels move + Blackout on pan/tilt/colour wheel/gobo wheels move + Nothing + Pan Reset + Tilt reset + Colour wheel reset + Gobo wheels reset + Nothing + Prism reset + Nothing + All Reset + Nothing + + + Effect + Nothing + Movement macro 1 + Movement Macro 2 + Movement Macro 3 + Movement Macro 4 + Movement Macro 5 + Movement Macro 6 + Movement Macro 7 + Movement Macro 8 + Sound-Active movement macro 1 + Sound-Active movement macro 2 + Sound-Active movement macro 3 + Sound-Active movement macro 4 + Sound-Active movement macro 5 + Sound-Active movement macro 6 + Sound-Active movement macro 7 + Sound-Active movement macro 8 + + + Pan + Pan fine + Tilt + Tilt fine + Pan/Tilt speed + Color wheel + Gobo wheel + Gobo Rotation + Static Gobo 2 + Prism + Focus + Zoom + Dimmer + Strobe + Control + Movement Macros + + + Pan + Tilt + Color wheel + Gobo wheel + Gobo Rotation + Static Gobo 2 + Prism + Focus + Zoom + Strobe + + + + + + + + + diff --git a/resources/fixtures/Chauvet/Chauvet-Legend-330SR-Spot.qxf b/resources/fixtures/Chauvet/Chauvet-Legend-330SR-Spot.qxf index b72072fa51..ca1035e8b2 100644 --- a/resources/fixtures/Chauvet/Chauvet-Legend-330SR-Spot.qxf +++ b/resources/fixtures/Chauvet/Chauvet-Legend-330SR-Spot.qxf @@ -1,4 +1,4 @@ - + @@ -54,7 +54,7 @@ Gobo Open Gobo 1 - Gobo 2 + Gobo 2 Gobo 3 Gobo 4 Gobo 5 diff --git a/resources/fixtures/Chauvet/Chauvet-Rogue-R2-Spot.qxf b/resources/fixtures/Chauvet/Chauvet-Rogue-R2-Spot.qxf index 7fd1e3fc61..e286b47708 100644 --- a/resources/fixtures/Chauvet/Chauvet-Rogue-R2-Spot.qxf +++ b/resources/fixtures/Chauvet/Chauvet-Rogue-R2-Spot.qxf @@ -1,4 +1,4 @@ - + @@ -76,7 +76,7 @@ Gobo 4 Gobo 5 Gobo 6 - Gobo 7 + Gobo 7 Gobo Shake 1-7 (Slow -> Fast) Open Clockwise Gobo Scroll (Slow -> Fast) diff --git a/resources/fixtures/EK/EK-R3-Wash.qxf b/resources/fixtures/EK/EK-R3-Wash.qxf new file mode 100644 index 0000000000..e14dc9c6d3 --- /dev/null +++ b/resources/fixtures/EK/EK-R3-Wash.qxf @@ -0,0 +1,197 @@ + + + + + Q Light Controller Plus + 4.12.8 GIT + Harrison Bostock + + EK + R3 Wash + Moving Head + + + + + + + + + + Colour + OFF + 8000K~7000K + 7000K~6000K + 6000K~5600K + 5000K~4000K + 4000K~3200K + 3200K~2500K + 2500K + + + Colour + OFF + Red + Green + Blue + Cyan + Yellow + Magenta + White 7000K + White 3700K + White 5000K + black + Medium Yellow + Straw Ting + Surprise Peach + Fire + Medium Amber + Gold Amber + Dark Amber + Sunrise red + Light Pink + Medium Ping + Pink Carnation + Light Lavender + Lavender + Sky Blue + Just Blue + Dark Yellow Green + Sping Yellow + Light Amber + Stra + Deep Amber + Orange + Light Rose + English rose + Light Salmon + Middle Rose + Dark Pink + Magenta + Peacock Blue + Med Blu Green + Steel Blue + Light Blue + Dark Blue + Leaf Green + Dark Green + Mauve + Bright Pink + Medium Blue + Deep golden Amber + Pale Lavender + Sepecial Lavender + Primary Green + Bright Blue + Apricot + Pale Gold + Deep orange + Bastard Amber + Flame Red + Daylight Blue + Lilac Tint + Deep Lavender + Dark Steel Blue + Congo Blue + Alice Blue + Dirty White + White + + + Shutter + closed + slow to fast Strobe 1-25hz + open + Slow to fast Pulse + Open + Random Slow Strobe + Randum Medium Strobe + Random Fast Strobe + Open + + + + + + + + + + Maintenance + none + Fast P&T speed + Normal P&T speed + Dimmer Curve 1 (D) + Dimmer Curve 2 + Dimmer Curve 3 + Dimmer Curve 4 + Raw color Channels Gamma 1 + Raw color Channels Gamma 1.5 + Raw color Channels Gamma 2.2 (D) + Color Calibration OFF (D) + Color Calibration Adjust + Color Calibration Calibration (Only Base mode0 + FREE + PWM frequency=600Hz + PWM frequency=1200Hz (Default) + PWM frequency=2000Hz + PWM frequency=4000Hz + PWM frequency=6000Hz + PWM frequency=25000Hz + Default function recall + + + Maintenance + none + Zoom Reset HOLD 5s + Pan/Tilt Reset HOLD 5s + Complete Reset HOLD 5s + + + Red + Green + Blue + White + CTO + Macro + Strobe + Master dimmer + Master dimmer fine + Pan + Pan fine + Tilt + Tilt fine + Zoom + Function + Reset + + + Red + Red fine + Green + Green fine + Blue + Blue fine + White + White fine + CTO + Macro + Strobe + Master dimmer + Master dimmer fine + Pan + Pan fine + Tilt + Tilt fine + Zoom + Function + Reset + + + + + + + + + diff --git a/resources/fixtures/Elumen8/Elumen8-MS-700PE.qxf b/resources/fixtures/Elumen8/Elumen8-MS-700PE.qxf index 6672dbe1b5..256fca3b8f 100644 --- a/resources/fixtures/Elumen8/Elumen8-MS-700PE.qxf +++ b/resources/fixtures/Elumen8/Elumen8-MS-700PE.qxf @@ -1,4 +1,4 @@ - + @@ -79,7 +79,7 @@ Gobo Open Gobo 1 - Gobo 2 + Gobo 2 Gobo 3 Gobo 4 Gobo 5 @@ -87,7 +87,7 @@ Gobo 7 Gobo 8 Gobo 1 Shaking - Gobo 2 Shaking + Gobo 2 Shaking Gobo 3 Shaking Gobo 4 Shaking Gobo 5 Shaking diff --git a/resources/fixtures/Eurolite/Eurolite-LED-Theatre-COB-200-RGB+WW.qxf b/resources/fixtures/Eurolite/Eurolite-LED-Theatre-COB-200-RGB+WW.qxf new file mode 100644 index 0000000000..f2b4aab4c3 --- /dev/null +++ b/resources/fixtures/Eurolite/Eurolite-LED-Theatre-COB-200-RGB+WW.qxf @@ -0,0 +1,128 @@ + + + + + Q Light Controller Plus + 4.12.8 GIT + Florian Faber + + Eurolite + LED Theatre COB 200 RGB+WW + Color Changer + + + + + + + Shutter + Sound Control + Strobe + + + Colour + No function + 1800K + 2200K + 2700K + 3200K + 4300K + 5600K + 6500K + 8000K + + + Colour + No function + Red + Green + Blue + Warm white + Yellow + Magenta + Cyan + Dark orange + Green yellow + Salmon + Turquoise + Light green + Orange + Straw + Lavender + Light blue + Dark blue + Pink + Full on + + + Effect + No function + Auto program 1 + Auto program 2 + Auto program 3 + Auto program 4 + Auto program 5 + Auto program 6 + Auto program 7 + Auto program 8 + Auto program 9 + Sound control program 1 + Sound control program 2 + Sound control program 3 + Sound control program 4 + Sound control program 5 + Sound control program 6 + Sound control program 7 + Sound control program 8 + Sound control program 9 + + + Speed + Speed internal programs, increasing + + + Red + Green + Blue + White + Master dimmer + Strobe + + + Red + Green + Blue + White + + + Master dimmer + Strobe + Red + Green + Blue + White + Preset Colors + Internal programs + Speed Internal programs + Color temperature + + + Red + Green + Blue + + + Master dimmer + Strobe + Preset Colors + Internal programs + Speed Internal programs + + + + + + + + + diff --git a/resources/fixtures/FixturesMap.xml b/resources/fixtures/FixturesMap.xml index 15f21d4f86..f2f19c7698 100644 --- a/resources/fixtures/FixturesMap.xml +++ b/resources/fixtures/FixturesMap.xml @@ -11,6 +11,7 @@ + @@ -391,6 +392,7 @@ + @@ -455,6 +457,7 @@ + @@ -644,6 +647,7 @@ + @@ -777,6 +781,7 @@ + diff --git a/resources/gobos/Chauvet/gobo00051.svg b/resources/gobos/Chauvet/gobo00051.svg new file mode 100644 index 0000000000..5088f33e52 --- /dev/null +++ b/resources/gobos/Chauvet/gobo00051.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/resources/gobos/Chauvet/gobo00060.png b/resources/gobos/Chauvet/gobo00060.png deleted file mode 100644 index 1b13975e6c6b946125155550de3435b85c472304..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3478 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 \ No newline at end of file From 4679d6e2330a8dd83221e16ddefc862dfb69d243 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Mon, 29 Jan 2024 20:51:17 +0100 Subject: [PATCH 044/212] engine: save/load beat generator info into project file --- engine/src/inputoutputmap.cpp | 40 +++++++++++++++++++++++++++++++++++ engine/src/inputoutputmap.h | 8 ++++++- 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/engine/src/inputoutputmap.cpp b/engine/src/inputoutputmap.cpp index 24c2d5630f..a73ba55cc6 100644 --- a/engine/src/inputoutputmap.cpp +++ b/engine/src/inputoutputmap.cpp @@ -1024,6 +1024,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) @@ -1267,6 +1290,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(); @@ -1284,6 +1319,11 @@ bool InputOutputMap::saveXML(QXmlStreamWriter *doc) const /* IO Map Instance entry */ doc->writeStartElement(KXMLIOMap); + doc->writeStartElement(KXMLIOBeatGenerator); + doc->writeAttribute(KXMLIOBeatType, beatTypeToString(m_beatGeneratorType)); + doc->writeAttribute(KXMLIOBeatsPerMinute, QString::number(m_currentBPM)); + doc->writeEndElement(); + foreach (Universe *uni, m_universeArray) uni->saveXML(doc); diff --git a/engine/src/inputoutputmap.h b/engine/src/inputoutputmap.h index 7e63928f6e..9bcca578e6 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 { @@ -587,6 +590,9 @@ private slots: void setBeatGeneratorType(BeatGeneratorType type); BeatGeneratorType beatGeneratorType() const; + QString beatTypeToString(BeatGeneratorType type) const; + BeatGeneratorType stringToBeatType(QString str); + void setBpmNumber(int bpm); int bpmNumber() const; From 49aba44d38bf7b22187b6e0fec2e5c3482a042c4 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Wed, 31 Jan 2024 22:30:49 +0100 Subject: [PATCH 045/212] engine: add 'setBPM' API to v5 Script engine --- engine/src/scriptrunner.cpp | 12 ++++++++++++ engine/src/scriptrunner.h | 8 ++++++++ 2 files changed, 20 insertions(+) diff --git a/engine/src/scriptrunner.cpp b/engine/src/scriptrunner.cpp index 7a883210c3..d41260367e 100644 --- a/engine/src/scriptrunner.cpp +++ b/engine/src/scriptrunner.cpp @@ -517,6 +517,18 @@ bool ScriptRunner::setBlackout(bool enable) return true; } +bool ScriptRunner::setBPM(int bpm) +{ + if (m_running == false) + return false; + + qDebug() << Q_FUNC_INFO; + + m_doc->inputOutputMap()->setBpmNumber(bpm); + + return true; +} + int ScriptRunner::random(QString minTime, QString maxTime) { if (m_running == false) diff --git a/engine/src/scriptrunner.h b/engine/src/scriptrunner.h index 6afc23f024..f5e0c835b4 100644 --- a/engine/src/scriptrunner.h +++ b/engine/src/scriptrunner.h @@ -173,6 +173,14 @@ public slots: */ bool setBlackout(bool enable); + /** + * Set the BPM (beat per minute) number of the internal beat generator + * + * @param bpm the number of beats per minute requested + * @return true if successful. False on error. + */ + bool setBPM(int bpm); + /** * Handle "random" command (string version) * From 40e639bc30d20197961ded115fccad0a788236cf Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Thu, 1 Feb 2024 00:53:50 +0100 Subject: [PATCH 046/212] vc/audiotriggers: fix loading XML with DMX bars with no channels set Reported: https://www.qlcplus.org/forum/viewtopic.php?t=16992 --- debian/changelog | 1 + engine/src/inputoutputmap.cpp | 1 + ui/src/audiobar.cpp | 11 ++++++++++- 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/debian/changelog b/debian/changelog index 323761a584..d4a849a1fe 100644 --- a/debian/changelog +++ b/debian/changelog @@ -13,6 +13,7 @@ qlcplus (4.12.8) stable; urgency=low * Virtual Console/Button: Scene flashing can force LTP and override (thanks to Dennis Suermann) * 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 * 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 diff --git a/engine/src/inputoutputmap.cpp b/engine/src/inputoutputmap.cpp index a73ba55cc6..1a5d2acb0b 100644 --- a/engine/src/inputoutputmap.cpp +++ b/engine/src/inputoutputmap.cpp @@ -47,6 +47,7 @@ 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); diff --git a/ui/src/audiobar.cpp b/ui/src/audiobar.cpp index b092198b76..33b47c817a 100644 --- a/ui/src/audiobar.cpp +++ b/ui/src/audiobar.cpp @@ -270,7 +270,16 @@ bool AudioBar::loadXML(QXmlStreamReader &root, Doc *doc) break; case AudioBar::DMXBar: { - root.readNextStartElement(); + QXmlStreamReader::TokenType tType = root.readNext(); + + if (tType == QXmlStreamReader::EndElement) + { + root.readNext(); + return true; + } + + if (tType == QXmlStreamReader::Characters) + root.readNext(); if (root.name() == KXMLQLCAudioBarDMXChannels) { From 937e51440575fb2179ac1040d4e870a9f766c648 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Sat, 3 Feb 2024 16:06:34 +0100 Subject: [PATCH 047/212] macos: improve bundle icon (fix #1512) Add script to generate iconset (requires homebrew) --- create-dmg.sh | 18 +++++++------- platforms/macos/libsndfile-nametool.pri | 2 +- platforms/macos/svg2icns.sh | 30 ++++++++++++++++++++++++ resources/icons/qlcplus.icns | Bin 338872 -> 196855 bytes 4 files changed, 40 insertions(+), 10 deletions(-) create mode 100644 platforms/macos/svg2icns.sh 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/platforms/macos/libsndfile-nametool.pri b/platforms/macos/libsndfile-nametool.pri index 9c81804103..c57ecec527 100644 --- a/platforms/macos/libsndfile-nametool.pri +++ b/platforms/macos/libsndfile-nametool.pri @@ -5,7 +5,7 @@ LIBSNDFILE_FILEPATH = $$LIBSNDFILE_DIR/$$LIBSNDFILE_FILE LIBOGG_FILE = libogg.0.dylib LIBOGG_PATH = $$system("pkg-config --variable libdir ogg") LIBOGG_FILEPATH = $$LIBOGG_PATH/$$LIBOGG_FILE -LIBFLAC_FILE = libFLAC.8.dylib +LIBFLAC_FILE = libFLAC.12.dylib LIBFLAC_PATH = $$system("pkg-config --variable libdir flac") LIBFLAC_FILEPATH = $$LIBFLAC_PATH/$$LIBFLAC_FILE LIBVORBIS_FILE = libvorbis.0.dylib diff --git a/platforms/macos/svg2icns.sh b/platforms/macos/svg2icns.sh new file mode 100644 index 0000000000..9afdd8735b --- /dev/null +++ b/platforms/macos/svg2icns.sh @@ -0,0 +1,30 @@ +#!/bin/sh -x + +set -e + +SIZES=" +16,16x16 +32,16x16@2x +32,32x32 +64,32x32@2x +128,128x128 +256,128x128@2x +256,256x256 +512,256x256@2x +512,512x512 +1024,512x512@2x +" + +for SVG in "$@"; do + BASE=$(basename "$SVG" | sed 's/\.[^\.]*$//') + ICONSET="$BASE.iconset" + mkdir -p "$ICONSET" + for PARAMS in $SIZES; do + SIZE=$(echo $PARAMS | cut -d, -f1) + LABEL=$(echo $PARAMS | cut -d, -f2) + svg2png -w $SIZE -h $SIZE "$SVG" "$ICONSET"/icon_$LABEL.png + done + + iconutil -c icns "$ICONSET" + rm -rf "$ICONSET" +done diff --git a/resources/icons/qlcplus.icns b/resources/icons/qlcplus.icns index c57461df103d5a058969d9bcb965dd20dbf62395..a8bb999afebac4ad305d4aa11424ed3befa54ddd 100644 GIT binary patch literal 196855 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%0A?TI`n2tP34=MZFqm=N=#l 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�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%0D&h9>Qu;lp&g^b824 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 Date: Sun, 4 Feb 2024 12:02:51 +0100 Subject: [PATCH 048/212] plugins: fix to build with recent GCC --- plugins/dmxusb/src/enttecdmxusbopen.cpp | 1 - plugins/gpio/gpioplugin.cpp | 1 + plugins/uart/uartplugin.cpp | 1 + plugins/uart/uartwidget.cpp | 4 ++-- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/plugins/dmxusb/src/enttecdmxusbopen.cpp b/plugins/dmxusb/src/enttecdmxusbopen.cpp index 1c1b160099..261bd07e3c 100644 --- a/plugins/dmxusb/src/enttecdmxusbopen.cpp +++ b/plugins/dmxusb/src/enttecdmxusbopen.cpp @@ -20,7 +20,6 @@ #include #include -#include #include #include "enttecdmxusbopen.h" diff --git a/plugins/gpio/gpioplugin.cpp b/plugins/gpio/gpioplugin.cpp index 68dee2608e..bb2b134241 100644 --- a/plugins/gpio/gpioplugin.cpp +++ b/plugins/gpio/gpioplugin.cpp @@ -139,6 +139,7 @@ QString GPIOPlugin::outputInfo(quint32 output) void GPIOPlugin::writeUniverse(quint32 universe, quint32 output, const QByteArray &data, bool dataChanged) { Q_UNUSED(universe) + Q_UNUSED(dataChanged) if (output != 0) return; diff --git a/plugins/uart/uartplugin.cpp b/plugins/uart/uartplugin.cpp index c4ef329617..7b4643f49f 100644 --- a/plugins/uart/uartplugin.cpp +++ b/plugins/uart/uartplugin.cpp @@ -130,6 +130,7 @@ QString UARTPlugin::outputInfo(quint32 output) void UARTPlugin::writeUniverse(quint32 universe, quint32 output, const QByteArray &data, bool dataChanged) { Q_UNUSED(universe) + Q_UNUSED(dataChanged) if (output < quint32(m_widgets.count())) m_widgets.at(output)->writeUniverse(data); diff --git a/plugins/uart/uartwidget.cpp b/plugins/uart/uartwidget.cpp index d2e5ee34fd..1e5a1522d4 100644 --- a/plugins/uart/uartwidget.cpp +++ b/plugins/uart/uartwidget.cpp @@ -18,7 +18,7 @@ */ #include -#include +#include #include #include @@ -141,7 +141,7 @@ void UARTWidget::run() int frameTime = (int) floor(((double)1000 / 30) + (double)0.5); m_granularity = Bad; - QTime time; + QElapsedTimer time; time.start(); usleep(1000); if (time.elapsed() <= 3) From 37beeaeac2a2b3e3950bd5579afe962e18bcc9bd Mon Sep 17 00:00:00 2001 From: Binary-Vanguard-12138 <103393933+Binary-Vanguard-12138@users.noreply.github.com> Date: Sun, 4 Feb 2024 22:50:51 +0800 Subject: [PATCH 049/212] Added web interface for cue list notes live editing. --- ui/src/virtualconsole/vccuelist.cpp | 12 ++++++++++++ ui/src/virtualconsole/vccuelist.h | 5 +++++ webaccess/res/virtualconsole.css | 4 ++++ webaccess/res/virtualconsole.js | 27 +++++++++++++++++++++++++++ webaccess/res/websocket.js | 2 ++ webaccess/src/webaccess.cpp | 23 +++++++++++++++++++++-- webaccess/src/webaccess.h | 1 + 7 files changed, 72 insertions(+), 2 deletions(-) diff --git a/ui/src/virtualconsole/vccuelist.cpp b/ui/src/virtualconsole/vccuelist.cpp index 3c5e4ad7f5..5b2e027764 100644 --- a/ui/src/virtualconsole/vccuelist.cpp +++ b/ui/src/virtualconsole/vccuelist.cpp @@ -880,6 +880,18 @@ void VCCueList::slotItemChanged(QTreeWidgetItem *item, int column) step.note = itemText; ch->replaceStep(step, idx); + + emit stepNoteChanged(idx, itemText); +} + +void VCCueList::slotStepNoteChanged(int idx, QString note) +{ + Chaser *ch = chaser(); + if (ch == NULL) + return; + ChaserStep step = ch->steps().at(idx); + step.note = note; + ch->replaceStep(step, idx); } void VCCueList::slotFunctionRunning(quint32 fid) diff --git a/ui/src/virtualconsole/vccuelist.h b/ui/src/virtualconsole/vccuelist.h index a084832ca2..13cbaec78e 100644 --- a/ui/src/virtualconsole/vccuelist.h +++ b/ui/src/virtualconsole/vccuelist.h @@ -180,6 +180,9 @@ public slots: /** Skip to the previous cue */ void slotPreviousCue(); + /** Update cue step note */ + void slotStepNoteChanged(int idx, QString note); + signals: /** progress percent value and text */ void progressStateChanged(); @@ -405,6 +408,8 @@ protected slots: /** Signal to webaccess */ void stepChanged(int idx); + void stepNoteChanged(int idx, QString note); + private: FunctionParent functionParent() const; diff --git a/webaccess/res/virtualconsole.css b/webaccess/res/virtualconsole.css index 0c40332e83..0951f7187b 100644 --- a/webaccess/res/virtualconsole.css +++ b/webaccess/res/virtualconsole.css @@ -6,6 +6,10 @@ form { visibility: hidden; } +input:focus-visible { + outline: none; +} + .vcbutton-wrapper { position: absolute; } diff --git a/webaccess/res/virtualconsole.js b/webaccess/res/virtualconsole.js index d6a273288c..94bdec1cfd 100644 --- a/webaccess/res/virtualconsole.js +++ b/webaccess/res/virtualconsole.js @@ -121,6 +121,33 @@ function setCueProgress(id, percent, text) { progressValObj.innerHTML = text; } +function changeCueNoteToEditMode(id, idx) { + var cueNoteSpanObj = document.getElementById("cueNoteSpan" + id + "_" + idx); + var cueNoteInputObj = document.getElementById("cueNoteInput" + id + "_" + idx); + cueNoteSpanObj.style.display = "none"; + cueNoteInputObj.style.display = "block"; + cueNoteInputObj.focus(); +} + +function changeCueNoteToTextMode(id, idx) { + var cueNoteSpanObj = document.getElementById("cueNoteSpan" + id + "_" + idx); + var cueNoteInputObj = document.getElementById("cueNoteInput" + id + "_" + idx); + cueNoteSpanObj.style.display = "block"; + cueNoteInputObj.style.display = "none"; + var newNote = cueNoteInputObj.value; + cueNoteSpanObj.innerHTML = newNote; + websocket.send(id + "|CUE_STEP_NOTE|" + idx + "|" + newNote); +} + +function setCueStepNote(id, idx, note) { + var cueNoteSpanObj = document.getElementById("cueNoteSpan" + id + "_" + idx); + var cueNoteInputObj = document.getElementById("cueNoteInput" + id + "_" + idx); + cueNoteSpanObj.style.display = "block"; + cueNoteInputObj.style.display = "none"; + cueNoteSpanObj.innerHTML = note; + cueNoteInputObj.value = note; +} + function showSideFaderPanel(id, checked) { var progressBarObj = document.getElementById("fadePanel" + id); showPanel[id] = parseInt(checked, 10); diff --git a/webaccess/res/websocket.js b/webaccess/res/websocket.js index 4abfdbd04a..4a2fd3f9a6 100644 --- a/webaccess/res/websocket.js +++ b/webaccess/res/websocket.js @@ -73,6 +73,8 @@ function connect() { setFrameDisableState(msgParams[0], msgParams[2]); } else if (msgParams[0] === "ALERT") { alert(msgParams[1]); + } else if (msgParams[1] === "CUE_STEP_NOTE") { + setCueStepNote(msgParams[0], msgParams[2], msgParams[3]); } else if (msgParams[1] === "CUE_PROGRESS") { // CUE message is |CUE_PERCENT|| setCueProgress(msgParams[0], msgParams[2], msgParams[3]); diff --git a/webaccess/src/webaccess.cpp b/webaccess/src/webaccess.cpp index d97637b4ef..ba11661214 100644 --- a/webaccess/src/webaccess.cpp +++ b/webaccess/src/webaccess.cpp @@ -773,6 +773,8 @@ void WebAccess::slotHandleWebSocketRequest(QHttpConnection *conn, QString data) cue->slotNextCue(); else if (cmdList[1] == "STEP") cue->playCueAtIndex(cmdList[2].toInt()); + else if (cmdList[1] == "CUE_STEP_NOTE") + cue->slotStepNoteChanged(cmdList[2].toInt(), cmdList[3]); else if (cmdList[1] == "CUE_SHOWPANEL") cue->slotSideFaderButtonChecked(cmdList[2] == "1" ? false : true); else if (cmdList[1] == "CUE_SIDECHANGE") @@ -1360,6 +1362,17 @@ void WebAccess::slotCueIndexChanged(int idx) sendWebSocketMessage(wsMessage.toUtf8()); } +void WebAccess::slotCueStepNoteChanged(int idx, QString note) +{ + VCCueList *cue = qobject_cast(sender()); + if (cue == NULL) + return; + + QString wsMessage = QString("%1|CUE_STEP_NOTE|%2|%3").arg(cue->id()).arg(idx).arg(note); + + sendWebSocketMessage(wsMessage.toUtf8()); +} + void WebAccess::slotCueProgressStateChanged() { VCCueList *cue = qobject_cast(sender()); @@ -1549,7 +1562,7 @@ QString WebAccess::getCueListHTML(VCCueList *cue) { QString stepID = QString::number(cue->id()) + "_" + QString::number(i); str += "

    id()) + ", " + QString::number(i) + ");\">\n"; + "onclick=\"setCueIndex(" + QString::number(cue->id()) + ", " + QString::number(i) + ");\">\n"; ChaserStep *step = chaser->stepAt(i); str += ""; @@ -1631,7 +1644,11 @@ QString WebAccess::getCueListHTML(VCCueList *cue) str += ""; } - str += "\n"; + str += "\n"; } str += "\n"; } @@ -1715,6 +1732,8 @@ QString WebAccess::getCueListHTML(VCCueList *cue) connect(cue, SIGNAL(stepChanged(int)), this, SLOT(slotCueIndexChanged(int))); + connect(cue, SIGNAL(stepNoteChanged(int, QString)), + this, SLOT(slotCueStepNoteChanged(int, QString))); connect(cue, SIGNAL(progressStateChanged()), this, SLOT(slotCueProgressStateChanged())); connect(cue, SIGNAL(sideFaderButtonChecked()), diff --git a/webaccess/src/webaccess.h b/webaccess/src/webaccess.h index ef90124043..8e0e9b63b6 100644 --- a/webaccess/src/webaccess.h +++ b/webaccess/src/webaccess.h @@ -94,6 +94,7 @@ protected slots: void slotSliderDisableStateChanged(bool disable); void slotAudioTriggersToggled(bool toggle); void slotCueIndexChanged(int idx); + void slotCueStepNoteChanged(int idx, QString note); void slotCueProgressStateChanged(); void slotCueShowSideFaderPanel(); void slotCueSideFaderValueChanged(); From d245b682ccd80d99cde41f47608f98eaa9a66659 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Sun, 4 Feb 2024 17:07:28 +0100 Subject: [PATCH 050/212] Linux: get architecture from cmake --- variables.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/variables.cmake b/variables.cmake index 3a89857f8f..cb1bd24e2c 100644 --- a/variables.cmake +++ b/variables.cmake @@ -89,7 +89,7 @@ if (WIN32) elseif (APPLE) set(LIBSDIR "Frameworks") elseif (UNIX) - set(LIBSDIR "lib/x86_64-linux-gnu") + set(LIBSDIR "lib/${CMAKE_C_LIBRARY_ARCHITECTURE}") endif () if (ANDROID) From fa51c43fb8718446d2cd5a5b84ccd42254a0b8ab Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Sun, 4 Feb 2024 17:08:55 +0100 Subject: [PATCH 051/212] webaccess: port network configuration to NetworkManager --- webaccess/res/networkconfig.js | 10 +- webaccess/src/webaccess.cpp | 2 +- webaccess/src/webaccessnetwork.cpp | 498 +++++++++-------------------- webaccess/src/webaccessnetwork.h | 12 +- 4 files changed, 154 insertions(+), 368 deletions(-) diff --git a/webaccess/res/networkconfig.js b/webaccess/res/networkconfig.js index fa792221e5..da6c515e5a 100644 --- a/webaccess/res/networkconfig.js +++ b/webaccess/res/networkconfig.js @@ -23,13 +23,9 @@ function systemCmd(cmd, iface, mode, addr, mask, gw, ssid, wpapsk) } function showStatic(iface, enable) { - var divName = iface + "StaticFields"; - var obj=document.getElementById(divName); - if (enable === true) { - obj.style.visibility="visible"; - } else { - obj.style.visibility="hidden"; - } + document.getElementById(iface + "IPaddr").disabled = !enable; + document.getElementById(iface + "Netmask").disabled = !enable; + document.getElementById(iface + "Gateway").disabled = !enable; } function applyParams(iface) { diff --git a/webaccess/src/webaccess.cpp b/webaccess/src/webaccess.cpp index d97637b4ef..4556a91b4d 100644 --- a/webaccess/src/webaccess.cpp +++ b/webaccess/src/webaccess.cpp @@ -466,7 +466,7 @@ void WebAccess::slotHandleWebSocketRequest(QHttpConnection *conn, QString data) if (cmdList.at(1) == "NETWORK") { - if (m_netConfig->updateNetworkFile(cmdList) == true) + if (m_netConfig->updateNetworkSettings(cmdList) == true) { QString wsMessage = QString("ALERT|" + tr("Network configuration changed. Reboot to apply the changes.")); conn->webSocketWrite(QHttpConnection::TextFrame, wsMessage.toUtf8()); diff --git a/webaccess/src/webaccessnetwork.cpp b/webaccess/src/webaccessnetwork.cpp index 0790aa2499..dc3488ced8 100644 --- a/webaccess/src/webaccessnetwork.cpp +++ b/webaccess/src/webaccessnetwork.cpp @@ -18,8 +18,10 @@ */ #include +#include #include #include +#include #include #include @@ -38,7 +40,9 @@ WebAccessNetwork::WebAccessNetwork(QObject *parent) : void WebAccessNetwork::resetInterface(InterfaceInfo *iface) { - iface->name = ""; + iface->devName = ""; + iface->connName = ""; + iface->connUUID = ""; iface->isStatic = false; iface->isWireless = false; iface->address = ""; @@ -54,12 +58,12 @@ void WebAccessNetwork::resetInterface(InterfaceInfo *iface) void WebAccessNetwork::appendInterface(InterfaceInfo iface) { - if (iface.name.contains("wlan") || iface.name.contains("ra")) + if (iface.devName.contains("wlan") || iface.devName.contains("ra")) iface.isWireless = true; for (int i = 0; i < m_interfaces.count(); i++) { - if (m_interfaces.at(i).name == iface.name) + if (m_interfaces.at(i).devName == iface.devName) { m_interfaces[i].isStatic = iface.isStatic; m_interfaces[i].isWireless = iface.isWireless; @@ -93,215 +97,155 @@ QString WebAccessNetwork::getInterfaceHTML(InterfaceInfo *iface) { QString dhcpChk = iface->isStatic ? QString() : QString("checked"); QString staticChk = iface->isStatic ? QString("checked") : QString(); - QString visibility = iface->isStatic ? QString("visible") : QString("hidden"); + QString editable = iface->isStatic ? QString("") : QString("disabled"); QString html = "
    \n"; html += "
    "; - html += tr("Network interface: ") + iface->name + "
    \n"; + html += tr("Network interface: ") + iface->devName + "
    \n"; html += "
    \n"; if (iface->isWireless) { html += tr("Access point name (SSID): ") + "name + "SSID\" size=\"15\" value=\"" + iface->ssid + "\">
    \n"; + iface->devName + "SSID\" size=\"15\" value=\"" + iface->ssid + "\">
    \n"; html += tr("WPA-PSK Password: ") + "name + "WPAPSK\" size=\"15\" value=\"" + iface->wpaPass + "\">
    \n"; + iface->devName + "WPAPSK\" size=\"15\" value=\"" + iface->wpaPass + "\">
    \n"; } /** IP mode radio buttons */ - html += "name + "', false);\" value=\"dhcp\" " + dhcpChk + ">" + tr("Dynamic (DHCP)") + "
    \n"; - html += "name + "', true);\" value=\"static\" " + staticChk + ">" + tr("Static") + "
    \n"; + html += "devName + "', false);\" value=\"dhcp\" " + dhcpChk + ">" + tr("Dynamic (DHCP)") + "
    \n"; + html += "devName + "', true);\" value=\"static\" " + staticChk + ">" + tr("Static") + "
    \n"; /** Static IP fields */ - html += "
    name + "StaticFields\" style=\"padding: 5px 30px; visibility:" + visibility + ";\">\n"; + html += "
    devName + "StaticFields\" style=\"padding: 5px 30px;\">\n"; html += tr("IP Address: ") + "name + "IPaddr\" size=\"15\" value=\"" + iface->address + "\">
    \n"; + iface->devName + "IPaddr\" size=\"15\" value=\"" + iface->address + "\" " + editable + ">
    \n"; html += tr("Netmask: ") + "name + "Netmask\" size=\"15\" value=\"" + iface->netmask + "\">
    \n"; + iface->devName + "Netmask\" size=\"15\" value=\"" + iface->netmask + "\" " + editable + ">
    \n"; html += tr("Gateway: ") + "name + "Gateway\" value=\"" + iface->gateway + "\">
    \n"; + iface->devName + "Gateway\" value=\"" + iface->gateway + "\" " + editable + ">
    \n"; html += "
    \n"; - html += "name + "');\" >\n"; + html += "devName + "');\" >\n"; html += "
    "; return html; } -QString WebAccessNetwork::getNetworkHTML() +QStringList WebAccessNetwork::getNmcliOutput(QStringList args, bool verbose) { - /* The complete picture of the current network interfaces status - * come from several places: - * 1- Qt network interfaces - * 2- /etc/network/interfaces - * 3- /etc/dhcpcd.conf - * 4- /etc/wpa_supplicant/wpa_supplicant.conf - * - * It is necessary to parse all of them and, only at the end, - * produce the HTML code to be sent to a web browser - */ - - QString html = ""; - m_interfaces.clear(); - InterfaceInfo currInterface; - resetInterface(&currInterface); - - // 1- gather the active network interface names with Qt - QStringList systemDevs; - foreach (QNetworkInterface interface, QNetworkInterface::allInterfaces()) - { - qDebug() << "Qt detected interface:" << interface.name(); - if (interface.name() != "lo") - systemDevs.append(interface.name()); - } + QStringList outputLines; + QProcess process; - // 2- parse the interfaces file - QFile interfacesFile(IFACES_SYSTEM_FILE); - if (interfacesFile.open(QIODevice::ReadOnly | QIODevice::Text) == false) - return ""; + qDebug() << "Executing command line: nmcli" << args.join(' '); + process.start("nmcli", args); - QTextStream ifacesQTS(&interfacesFile); - while (!ifacesQTS.atEnd()) + if (process.waitForFinished()) { - QString line = ifacesQTS.readLine(); - line = line.simplified(); - // skip comments - if (line.startsWith('#')) - continue; - - QStringList ifaceRow = line.split(" "); - if (ifaceRow.count() == 0) - continue; - - QString keyword = ifaceRow.at(0); - if (keyword == "iface") + process.setReadChannel(QProcess::StandardOutput); + while (process.canReadLine()) { - if (currInterface.name.isEmpty() == false) - { - appendInterface(currInterface); - resetInterface(&currInterface); - } - - if (ifaceRow.count() < 4) - continue; + QString line = process.readLine().simplified(); + if (verbose) + qDebug() << "Output::" << line; - currInterface.name = ifaceRow.at(1); - if (systemDevs.contains(currInterface.name)) - currInterface.enabled = true; - - if (ifaceRow.at(3) == "static") - currInterface.isStatic = true; - } - else if (keyword == "wpa-conf") - { - currInterface.wpaConfFile = ifaceRow.at(1); - parseWPAConfFile(&currInterface); + outputLines << line; } - else if (keyword == "address") - currInterface.address = ifaceRow.at(1); - else if (keyword == "netmask") - currInterface.netmask = ifaceRow.at(1); - else if (keyword == "gateway") - currInterface.gateway = ifaceRow.at(1); - else if (keyword == "wpa-ssid") - currInterface.ssid = ifaceRow.at(1); - else if (keyword == "wpa-psk") - currInterface.wpaPass = ifaceRow.at(1); } - if (currInterface.name.isEmpty() == false) - { - appendInterface(currInterface); - resetInterface(&currInterface); - } + return outputLines; +} - interfacesFile.close(); +void WebAccessNetwork::refreshConnectionsList() +{ + InterfaceInfo currInterface; - // 3- parse the dhcpcd.conf file - bool qlcplusSectionFound = false; - QFile dhcpcdFile(DHCPCD_CONF_FILE); - if (dhcpcdFile.open(QIODevice::ReadOnly | QIODevice::Text) == false) - return ""; + m_interfaces.clear(); + resetInterface(&currInterface); - m_dhcpcdConfCache.clear(); + // execute "nmcli -t device status" to list all avilable devices + QStringList devStatusOutput = getNmcliOutput(QStringList() << "-t" << "device" << "status"); - QTextStream dhcpQTS(&dhcpcdFile); - while (!dhcpQTS.atEnd()) + foreach (QString dLine, devStatusOutput) { - QString line = dhcpQTS.readLine(); - line = line.simplified(); - if (line.contains("QLC+")) - qlcplusSectionFound = true; + QStringList devTokens = dLine.split(':'); + qDebug() << "output " << devTokens.at(0) << devTokens.at(3); + + if (!currInterface.devName.isEmpty()) + appendInterface(currInterface); - // cache the original file BEFORE the QLC+ section - if (qlcplusSectionFound == false) + resetInterface(&currInterface); + currInterface.enabled = true; + currInterface.devName = devTokens.at(0); + currInterface.connName = devTokens.at(3); + + // skip loopback and disconnected interfaces + if (currInterface.devName == "lo" || currInterface.devName.contains("p2p")) { - m_dhcpcdConfCache.append(line); + currInterface.devName = ""; continue; } - line = line.simplified(); - - QStringList ifaceRow = line.split(" "); - if (ifaceRow.count() < 2) + if (currInterface.connName.isEmpty()) continue; - QString keyword = ifaceRow.at(0); - if (keyword == "interface") + // run "nmcli -t con show CONN_NAME" to retrieve everything about a connection + QStringList conShowOuput = getNmcliOutput(QStringList() << "-t" << "con" << "show" << currInterface.connName); + foreach (QString cLine, conShowOuput) { - if (currInterface.name.isEmpty() == false) + QStringList params = cLine.split(':'); + if (params.at(0) == "connection.uuid") { - appendInterface(currInterface); - resetInterface(&currInterface); + currInterface.connUUID = params.at(1); } - - currInterface.name = ifaceRow.at(1); - if (systemDevs.contains(currInterface.name)) - currInterface.enabled = true; - } - else if (keyword == "static") - { - QStringList params = ifaceRow.at(1).split("="); - if (params.count() < 2) - continue; - - currInterface.isStatic = true; - - QString paramKey = params.at(0); - - if (paramKey == "ip_address") + else if (params.at(0) == "ipv4.method") + { + currInterface.isStatic = params.at(1) == "auto" ? false : true; + } + else if (params.at(0).startsWith("IP4.ADDRESS")) { - QStringList ipAddrVals = params.at(1).split("/"); - if (ipAddrVals.count() < 2) - continue; - currInterface.address = ipAddrVals.at(0); - currInterface.netmask = netmaskToString(ipAddrVals.at(1).toInt()); + QStringList ipAddrTokens = params.at(1).split("/"); + if (ipAddrTokens.count() == 2) + { + currInterface.address = ipAddrTokens.at(0); + unsigned long mask = (0xFFFFFFFF << (32 - ipAddrTokens.at(1).toUInt())) & 0xFFFFFFFF; + currInterface.netmask = QString::number(mask >> 24) + '.' + + QString::number((mask >> 16) & 0xFF) + '.' + + QString::number((mask >> 8) & 0xFF) + '.' + + QString::number(mask & 0xFF); + } } - else if (paramKey == "routers") + else if (params.at(0).startsWith("IP4.GATEWAY")) { currInterface.gateway = params.at(1); } - else if (paramKey == "domain_name_servers") + else if (params.at(0).startsWith("IP4.DNS")) + { + if (currInterface.dns1.isEmpty()) + currInterface.dns1 = params.at(1); + else + currInterface.dns2 = params.at(1); + } + else if (params.at(0) == "802-11-wireless.ssid") { - currInterface.dns1 = params.at(1); - if (ifaceRow.count() == 3) - currInterface.dns2 = ifaceRow.at(2); + currInterface.ssid = params.at(1); } } } +} - if (currInterface.name.isEmpty() == false) - { - appendInterface(currInterface); - resetInterface(&currInterface); - } +QString WebAccessNetwork::getNetworkHTML() +{ + QString html = ""; + + refreshConnectionsList(); foreach (InterfaceInfo info, m_interfaces) { if (info.enabled) html += getInterfaceHTML(&info); - qDebug() << "Interface:" << info.name << "isstatic:" << info.isStatic << "address:" << info.address + qDebug() << "Interface:" << info.devName << "isStatic:" << info.isStatic << "address:" << info.address << "netmask:" << info.netmask << "gateway:" << info.gateway; } @@ -354,233 +298,79 @@ QString WebAccessNetwork::getHTML() return str; } -bool WebAccessNetwork::updateNetworkFile(QStringList cmdList) +bool WebAccessNetwork::updateNetworkSettings(QStringList cmdList) { for (int i = 0; i < m_interfaces.count(); i++) { - if (m_interfaces.at(i).name == cmdList.at(2)) + if (m_interfaces.at(i).devName == cmdList.at(2)) { - m_interfaces[i].enabled = true; - if (cmdList.at(3) == "static") - m_interfaces[i].isStatic = true; - else - m_interfaces[i].isStatic = false; - m_interfaces[i].address = cmdList.at(4); - m_interfaces[i].netmask = cmdList.at(5); - m_interfaces[i].gateway = cmdList.at(6); - if (m_interfaces[i].isWireless == true) + if (!m_interfaces[i].connName.isEmpty()) { - m_interfaces[i].ssid = cmdList.at(7); - m_interfaces[i].wpaPass = cmdList.at(8); + // first off, delete the current connection profile + getNmcliOutput(QStringList() << "con" << "del" << m_interfaces[i].connName); } - return writeNetworkFile(); - } - } - return false; -} - -void WebAccessNetwork::parseWPAConfFile(InterfaceInfo *iface) -{ - bool inNetwork = false; - - if (iface == NULL || iface->wpaConfFile.isEmpty()) - return; - - qDebug() << "Parsing WPA conf file" << iface->wpaConfFile; - - QFile wpaConfFile(iface->wpaConfFile); - if (wpaConfFile.open(QIODevice::ReadOnly | QIODevice::Text) == false) - return; - - QTextStream wpaConfQTS(&wpaConfFile); - while (!wpaConfQTS.atEnd()) - { - QString line = wpaConfQTS.readLine(); - line = line.simplified(); - if (line.startsWith("network")) - { - inNetwork = true; - continue; - } - - if (inNetwork) - { - if (line.contains("}")) - { - inNetwork = false; - continue; - } + m_interfaces[i].enabled = true; + bool staticRequest = cmdList.at(3) == "static" ? true : false; + QString args = "con add con-name qlcplus" + m_interfaces[i].devName + " ifname " + m_interfaces[i].devName; - QStringList tokens = line.split("="); - if (tokens.count() == 2) + if (staticRequest) { - QString param = tokens.at(0); - QString value = tokens.at(1); + // convert netmask to bitwise notation + uint32_t intNetMask; + uint32_t bitCount = 0; - //qDebug() << "Tokens:"<< param << value; - - if (param == "ssid") - iface->ssid = value.remove(QChar('"')); - else if (param == "psk") - iface->wpaPass = value.remove(QChar('"')); - } - } - - } + if (inet_pton(AF_INET, cmdList.at(5).toUtf8().constData(), &intNetMask) == 0) + { + qDebug() << "Invalid netmask"; + return false; + } - wpaConfFile.close(); -} + while (intNetMask > 0) + { + intNetMask = intNetMask >> 1; + bitCount++; + } -bool WebAccessNetwork::writeNetworkFile() -{ - /* Here 3 things again: - * 1- the /etc/network/interfaces file is left untouched - * 2- /etc/dhcpcd.conf is written only if there are static IPs set - * 3- the wpa_supplicant.conf file(s) are written for each wireless adapter - */ - - bool dhcpcdCacheWritten = false; - QFile dhcpcdFile(DHCPCD_CONF_FILE); - if (dhcpcdFile.open(QIODevice::WriteOnly | QIODevice::Text) == false) - return false; - - foreach (InterfaceInfo iface, m_interfaces) - { - if (iface.enabled == false) - continue; + if (m_interfaces[i].isWireless) + args = args + " type wifi ssid " + cmdList.at(7); + else + args = args + " type ethernet"; - if (iface.isStatic == true) - { - if (dhcpcdCacheWritten == false && m_dhcpcdConfCache.isEmpty() == false) + args = args + " ip4 " + cmdList.at(4) + "/" + QString::number(bitCount) + " gw4 " + cmdList.at(6); + } + else // DHCP { - foreach (QString line, m_dhcpcdConfCache) + if (m_interfaces[i].isWireless) { - dhcpcdFile.write(line.toLatin1()); - dhcpcdFile.write("\n"); + //m_interfaces[i].ssid = cmdList.at(7); + //m_interfaces[i].wpaPass = cmdList.at(8); + args = args + " type wifi ssid " + cmdList.at(7); + } + else + { + args += " type ethernet"; } - dhcpcdFile.write("\n######### QLC+ parameters. Do not edit #########\n\n"); - dhcpcdCacheWritten = true; } - else - qDebug() << "[writeNetworkFile] ERROR. No dhcpcd cache found!"; - - dhcpcdFile.write((QString("interface %1\n").arg(iface.name)).toLatin1()); - dhcpcdFile.write((QString("static ip_address=%1/%2\n").arg(iface.address).arg(stringToNetmask(iface.netmask))).toLatin1()); - dhcpcdFile.write((QString("static routers=%1\n").arg(iface.gateway)).toLatin1()); - if (iface.dns1.isEmpty() == false) - dhcpcdFile.write((QString("static domain_name_servers=%1\n\n").arg(iface.dns1)).toLatin1()); - else - dhcpcdFile.write(QString("static domain_name_servers=127.0.0.1\n\n").toLatin1()); - } - if (iface.isWireless) - { - QString wpaConfName = iface.wpaConfFile.isEmpty() ? WPA_SUPP_CONF_FILE : iface.wpaConfFile; - qDebug() << "[writeNetworkFile] Writing wpa conf file:" << wpaConfName; - QFile wpaConfFile(wpaConfName); - if (wpaConfFile.open(QIODevice::WriteOnly | QIODevice::Text) == false) + // add the new/updated connection profile + getNmcliOutput(args.split(" ")); + + // if a password is set, modify the just created connection + if (m_interfaces[i].isWireless && !cmdList.at(8).isEmpty()) { - qDebug() << "[writeNetworkFile] Error opening file" << wpaConfName; - return false; + args = "con mod qlcplus" + m_interfaces[i].devName + " wifi-sec.key-mgmt wpa-psk wifi-sec.psk " + cmdList.at(8); + getNmcliOutput(args.split(" ")); } - wpaConfFile.write(QString("ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev\n").toLatin1()); - wpaConfFile.write(QString("update_config=1\n\n").toLatin1()); - wpaConfFile.write(QString("network={\n").toLatin1()); - wpaConfFile.write(QString("scan_ssid=1\n").toLatin1()); - wpaConfFile.write((QString("ssid=\"%1\"\n").arg(iface.ssid)).toLatin1()); - wpaConfFile.write((QString("psk=\"%1\"\n").arg(iface.wpaPass)).toLatin1()); - wpaConfFile.write(QString("}\n").toLatin1()); - wpaConfFile.close(); - } - } - - dhcpcdFile.close(); + // finally, activate the connection + args = "con up qlcplus" + m_interfaces[i].devName; + getNmcliOutput(args.split(" ")); - return true; -} - -#if 0 // old Wheezy configuration -bool WebAccessNetwork::writeNetworkFile() -{ - QFile netFile(IFACES_SYSTEM_FILE); - if (netFile.open(QIODevice::WriteOnly | QIODevice::Text) == false) - return false; - - netFile.write(QString("auto lo\n").toLatin1()); - netFile.write(QString("iface lo inet loopback\n").toLatin1()); - netFile.write(QString("allow-hotplug eth0\n").toLatin1()); - - foreach (InterfaceInfo iface, m_interfaces) - { - if (iface.enabled == false) - continue; - - if (iface.isWireless) - netFile.write((QString("auto %1\n").arg(iface.name)).toLatin1()); + refreshConnectionsList(); - if (iface.isStatic == false) - netFile.write((QString("iface %1 inet dhcp\n").arg(iface.name)).toLatin1()); - else - { - netFile.write((QString("iface %1 inet static\n").arg(iface.name)).toLatin1()); - netFile.write((QString(" address %1\n").arg(iface.address)).toLatin1()); - netFile.write((QString(" netmask %1\n").arg(iface.netmask)).toLatin1()); - netFile.write((QString(" gateway %1\n").arg(iface.gateway)).toLatin1()); - } - if (iface.isWireless) - { - if (iface.ssid.isEmpty() == false) - netFile.write((QString(" wpa-ssid %1\n").arg(iface.ssid)).toLatin1()); - if (iface.wpaPass.isEmpty() == false) - netFile.write((QString(" wpa-psk %1\n").arg(iface.wpaPass)).toLatin1()); + return true; } } - - netFile.close(); - - return true; -} -#endif - -QString WebAccessNetwork::netmaskToString(int mask) -{ - QString nmString; - - quint32 bitmask = 0; - for (int i = 0; i < mask; i++) - bitmask |= (1 << (31 - i)); - - for (int n = 0; n < 4; n++) - { - if (nmString.isEmpty() == false) - nmString.prepend("."); - nmString.prepend(QString::number((bitmask >> (8 * n)) & 0x00FF)); - } - return nmString; -} - -int WebAccessNetwork::stringToNetmask(QString mask) -{ - quint32 lMask = 0; - int nMask = 0; - - QStringList nibbles = mask.split("."); - if (nibbles.count() != 4) - return 24; - - for (int i = 0; i < 4; i++) - lMask |= (nibbles.at(i).toInt() << (8 * (3 - i))); - - for (int b = 0; b < 32; b++) - { - if (lMask & (1 << (31 - b))) - nMask++; - else - break; - } - - return nMask; + return false; } - diff --git a/webaccess/src/webaccessnetwork.h b/webaccess/src/webaccessnetwork.h index cc1883f879..af65d98ae6 100644 --- a/webaccess/src/webaccessnetwork.h +++ b/webaccess/src/webaccessnetwork.h @@ -25,7 +25,9 @@ typedef struct { bool enabled; - QString name; + QString devName; + QString connName; + QString connUUID; bool isStatic; bool isWireless; QString address; @@ -49,13 +51,11 @@ class WebAccessNetwork: public QObject QString getNetworkHTML(); QString getHTML(); - bool updateNetworkFile(QStringList cmdList); + bool updateNetworkSettings(QStringList cmdList); protected: - void parseWPAConfFile(InterfaceInfo *iface); - bool writeNetworkFile(); - QString netmaskToString(int mask); - int stringToNetmask(QString mask); + QStringList getNmcliOutput(QStringList args, bool verbose = false); + void refreshConnectionsList(); protected: QListm_interfaces; From 2b0e772532098f251fe2c2a9f5619155dc391116 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Mon, 5 Feb 2024 20:14:54 +0100 Subject: [PATCH 052/212] vc/inputselection: complete and improve PR #1103 --- debian/changelog | 1 + engine/src/qlcinputsource.cpp | 2 +- engine/src/qlcinputsource.h | 2 +- ui/src/inputselectionwidget.cpp | 10 +- ui/src/inputselectionwidget.h | 1 + ui/src/inputselectionwidget.ui | 51 +++---- ui/src/virtualconsole/vcbuttonproperties.cpp | 1 + ui/src/virtualconsole/vcsliderproperties.ui | 152 +++++++++---------- ui/src/virtualconsole/vcwidget.cpp | 2 +- 9 files changed, 116 insertions(+), 106 deletions(-) diff --git a/debian/changelog b/debian/changelog index d4a849a1fe..bfb9fa4f81 100644 --- a/debian/changelog +++ b/debian/changelog @@ -6,6 +6,7 @@ qlcplus (4.12.8) stable; urgency=low * 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) + * Virtual Console: add monitoring feedback value to custom feedbacks (thanks to ditcheshurt) * 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 diff --git a/engine/src/qlcinputsource.cpp b/engine/src/qlcinputsource.cpp index a1d0f06fc6..9a626c6a18 100644 --- a/engine/src/qlcinputsource.cpp +++ b/engine/src/qlcinputsource.cpp @@ -126,7 +126,7 @@ void QLCInputSource::setRange(uchar lower, uchar upper) m_upper = upper; } -void QLCInputSource::setMonitor(uchar monitor) +void QLCInputSource::setMonitorValue(uchar monitor) { m_monitor = monitor; } diff --git a/engine/src/qlcinputsource.h b/engine/src/qlcinputsource.h index 7044d8970f..c8d0bab6b1 100644 --- a/engine/src/qlcinputsource.h +++ b/engine/src/qlcinputsource.h @@ -79,7 +79,7 @@ class QLCInputSource: public QThread void setRange(uchar lower, uchar upper); uchar lowerValue() const; uchar upperValue() const; - void setMonitor(uchar monitor); + void setMonitorValue(uchar monitor); uchar monitorValue() const; protected: diff --git a/ui/src/inputselectionwidget.cpp b/ui/src/inputselectionwidget.cpp index 1cc8ccdede..1693ef0daa 100644 --- a/ui/src/inputselectionwidget.cpp +++ b/ui/src/inputselectionwidget.cpp @@ -40,6 +40,8 @@ InputSelectionWidget::InputSelectionWidget(Doc *doc, QWidget *parent) m_customFbButton->setVisible(false); m_feedbackGroup->setVisible(false); + m_monitorLabel->setVisible(false); + m_monitorSpin->setVisible(false); m_lowerSpin->setEnabled(false); m_upperSpin->setEnabled(false); m_monitorSpin->setEnabled(false); @@ -76,6 +78,12 @@ void InputSelectionWidget::setCustomFeedbackVisibility(bool visible) m_customFbButton->setVisible(visible); } +void InputSelectionWidget::setMonitoringVisibility(bool visible) +{ + m_monitorLabel->setVisible(visible); + m_monitorSpin->setVisible(visible); +} + void InputSelectionWidget::setTitle(QString title) { m_extInputGroup->setTitle(title); @@ -202,7 +210,7 @@ void InputSelectionWidget::slotUpperSpinValueChanged(int value) void InputSelectionWidget::slotMonitorSpinValueChanged(int value) { - m_inputSource->setMonitor(uchar(value)); + m_inputSource->setMonitorValue(uchar(value)); } void InputSelectionWidget::updateInputSource() diff --git a/ui/src/inputselectionwidget.h b/ui/src/inputselectionwidget.h index ba7a8a1285..dc1e557ca9 100644 --- a/ui/src/inputselectionwidget.h +++ b/ui/src/inputselectionwidget.h @@ -38,6 +38,7 @@ class InputSelectionWidget : public QWidget, public Ui_InputSelectionWidget void setKeyInputVisibility(bool visible); void setCustomFeedbackVisibility(bool visible); + void setMonitoringVisibility(bool visible); void setTitle(QString title); void setWidgetPage(int page); bool isAutoDetecting(); diff --git a/ui/src/inputselectionwidget.ui b/ui/src/inputselectionwidget.ui index f4ff3a509b..8d9e82a826 100644 --- a/ui/src/inputselectionwidget.ui +++ b/ui/src/inputselectionwidget.ui @@ -7,14 +7,14 @@ Copyright (c) 2015 Massimo Callegari - Licensed under the Apache License, Version 2.0 (the "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.txt Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, + 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. @@ -26,7 +26,7 @@ 0 0 504 - 185 + 231 @@ -171,8 +171,8 @@ Custom feedback - - + + @@ -180,30 +180,18 @@ - Lower + Lower Value - + 255 - - - - - 10 - - - - Upper - - - - + 0 @@ -216,19 +204,31 @@ - - + + + + + 10 + + + + Upper Value + + + + + 10 - Monitor + Monitor Value - + 255 @@ -240,9 +240,8 @@ m_lowerSpin label_2 label + m_monitorLabel m_monitorSpin - label_3 - m_keyInputGroup diff --git a/ui/src/virtualconsole/vcbuttonproperties.cpp b/ui/src/virtualconsole/vcbuttonproperties.cpp index 5b8359688c..22352efdaf 100644 --- a/ui/src/virtualconsole/vcbuttonproperties.cpp +++ b/ui/src/virtualconsole/vcbuttonproperties.cpp @@ -49,6 +49,7 @@ VCButtonProperties::VCButtonProperties(VCButton* button, Doc* doc) m_inputSelWidget = new InputSelectionWidget(m_doc, this); m_inputSelWidget->setCustomFeedbackVisibility(true); + m_inputSelWidget->setMonitoringVisibility(true); m_inputSelWidget->setKeySequence(m_button->keySequence()); m_inputSelWidget->setInputSource(m_button->inputSource()); m_inputSelWidget->setWidgetPage(m_button->page()); diff --git a/ui/src/virtualconsole/vcsliderproperties.ui b/ui/src/virtualconsole/vcsliderproperties.ui index 15791f33f8..8a1ba59bd9 100644 --- a/ui/src/virtualconsole/vcsliderproperties.ui +++ b/ui/src/virtualconsole/vcsliderproperties.ui @@ -46,63 +46,33 @@ General - - - - - - Widget name - - - - - - - Name of the slider - - - - - - - - - Slider movement + + + + QFrame::NoFrame - - - - - Normal - - - true - - - + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + - - - Inverted - - + - - - - Qt::Vertical - - - - 20 - 40 - - - - @@ -146,29 +116,53 @@ - - - - QFrame::StyledPanel + + + + Catch up with the external controller input value - - QFrame::Raised + + + + + + + + Widget name + + + + + + + Name of the slider + + + + + + + + + Slider movement - - - 0 - - - 0 - - - 0 - - - 0 - + - + + + Normal + + + true + + + + + + + Inverted + + @@ -205,12 +199,18 @@ - - - - Catch up with the external controller input value + + + + Qt::Vertical - + + + 20 + 40 + + + diff --git a/ui/src/virtualconsole/vcwidget.cpp b/ui/src/virtualconsole/vcwidget.cpp index a54adc549c..cdd0a13c0b 100644 --- a/ui/src/virtualconsole/vcwidget.cpp +++ b/ui/src/virtualconsole/vcwidget.cpp @@ -896,7 +896,7 @@ QSharedPointer VCWidget::getXMLInput(QXmlStreamReader &root) mon = uchar(attrs.value(KXMLQLCVCWidgetInputMonitorValue).toString().toUInt()); newSrc->setRange(min, max); - newSrc->setMonitor(mon); + newSrc->setMonitorValue(mon); return newSrc; } From 380d7bb294618a787408bb7e18e6b973bbbe7330 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20M=C3=BCllner?= Date: Mon, 5 Feb 2024 20:45:10 +0100 Subject: [PATCH 053/212] fixtures: Add Cameo P2 FC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Cameo P2 FC has 12 different modes. This patch adds support for 7 of them, which don't depend on the device setting channel. Signed-off-by: Christoph Müllner --- resources/fixtures/Cameo/Cameo-P2-FC.qxf | 107 +++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 resources/fixtures/Cameo/Cameo-P2-FC.qxf diff --git a/resources/fixtures/Cameo/Cameo-P2-FC.qxf b/resources/fixtures/Cameo/Cameo-P2-FC.qxf new file mode 100644 index 0000000000..b14574cf1d --- /dev/null +++ b/resources/fixtures/Cameo/Cameo-P2-FC.qxf @@ -0,0 +1,107 @@ + + + + + Q Light Controller Plus + 4.12.7 + Christoph Müllner + + Cameo + P2 FC + Color Changer + + + + Colour + Warm white + Warm white -> 2700K + Bulb white (2700K) + 2700K -> 3200K + Halogen white (3200K) + 3200K -> 4000K + Neutral white (4000K) + 4000K -> 5600K + Studio white (5600K) + 5600K -> 6500K + Daylight white (6500K) + 6500K -> cold daylight + Cold daylight + + + + + + + + + + + + + + + Colour + Off + Magenta -> neutral + Neutral + Neutral -> green + + + Dimmer + Colour Temperature + + + Red + Green + Blue + + + Red + Green + Blue + Amber + Lime + + + Red + Red fine + Green + Green fine + Blue + Blue fine + Amber + Amber fine + Lime + Lime fine + + + Dimmer + Dimmer fine + Colour Temperature + Tint + + + Dimmer + Dimmer fine + Hue + Saturation + Colour Temperature + Tint + + + Dimmer + Dimmer fine + Red + Green + Blue + Colour Temperature + Tint + + + + + + + + + From 85bdca6f8e1f805bf5c704292c835ba864b183dc Mon Sep 17 00:00:00 2001 From: Binary-Vanguard-12138 <103393933+Binary-Vanguard-12138@users.noreply.github.com> Date: Wed, 7 Feb 2024 01:42:58 +0100 Subject: [PATCH 054/212] Fixing android build issue caused by engine --- platforms/platforms.pro | 2 +- qmlui/qmlui.pro | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/platforms/platforms.pro b/platforms/platforms.pro index 8b27d802a9..edadd8480f 100644 --- a/platforms/platforms.pro +++ b/platforms/platforms.pro @@ -2,7 +2,7 @@ TEMPLATE = subdirs #android: SUBDIRS += android #ios: SUBDIRS += ios -unix:!macx: SUBDIRS += linux +unix:!macx:!android: SUBDIRS += linux macx: SUBDIRS += macos win32: SUBDIRS += windows diff --git a/qmlui/qmlui.pro b/qmlui/qmlui.pro index 41473b6f3a..40fbd74788 100644 --- a/qmlui/qmlui.pro +++ b/qmlui/qmlui.pro @@ -23,7 +23,14 @@ INCLUDEPATH += ../plugins/interfaces INCLUDEPATH += ../plugins/midi/src/common DEPENDPATH += ../engine/src QMAKE_LIBDIR += ../engine/src -LIBS += -lqlcplusengine + +android { + LIBS += -lqlcplusengine_$${first(ANDROID_ABIS)} +} else { + LIBS += -lqlcplusengine +} + + #win32:QMAKE_LFLAGS += -shared win32:RC_FILE = qmlui.rc From 1a4863e0c031bd7d203ee67fdf4da99d5c369912 Mon Sep 17 00:00:00 2001 From: Binary-Vanguard-12138 <103393933+Binary-Vanguard-12138@users.noreply.github.com> Date: Wed, 7 Feb 2024 01:43:14 +0100 Subject: [PATCH 055/212] Added gradle files, updated AndroidManifest.xml --- platforms/android/AndroidManifest.xml | 5 +- platforms/android/build.gradle | 77 ++++++++ platforms/android/gradle.properties | 11 ++ .../android/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 54329 bytes .../gradle/wrapper/gradle-wrapper.properties | 5 + platforms/android/gradlew | 172 ++++++++++++++++++ platforms/android/gradlew.bat | 84 +++++++++ platforms/platforms.pro | 2 +- qlc.pro | 8 +- qmlui/qmlui.pro | 2 +- 10 files changed, 359 insertions(+), 7 deletions(-) create mode 100644 platforms/android/build.gradle create mode 100644 platforms/android/gradle.properties create mode 100644 platforms/android/gradle/wrapper/gradle-wrapper.jar create mode 100644 platforms/android/gradle/wrapper/gradle-wrapper.properties create mode 100755 platforms/android/gradlew create mode 100644 platforms/android/gradlew.bat diff --git a/platforms/android/AndroidManifest.xml b/platforms/android/AndroidManifest.xml index ce0538fc30..f0b5fee054 100644 --- a/platforms/android/AndroidManifest.xml +++ b/platforms/android/AndroidManifest.xml @@ -31,12 +31,10 @@ - - - + @@ -57,6 +55,7 @@ "applicationStateChanged(Qt::ApplicationSuspended)" signal is sent! --> + diff --git a/platforms/android/build.gradle b/platforms/android/build.gradle new file mode 100644 index 0000000000..d64b086a03 --- /dev/null +++ b/platforms/android/build.gradle @@ -0,0 +1,77 @@ +buildscript { + repositories { + google() + jcenter() + } + + dependencies { + classpath 'com.android.tools.build:gradle:7.0.0' + } +} + +repositories { + google() + jcenter() +} + +apply plugin: 'com.android.application' + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar']) +} + +android { + /******************************************************* + * The following variables: + * - androidBuildToolsVersion, + * - androidCompileSdkVersion + * - qt5AndroidDir - holds the path to qt android files + * needed to build any Qt application + * on Android. + * + * are defined in gradle.properties file. This file is + * updated by QtCreator and androiddeployqt tools. + * Changing them manually might break the compilation! + *******************************************************/ + + compileSdkVersion androidCompileSdkVersion.toInteger() + + // buildToolsVersion '28.0.3' + + sourceSets { + main { + manifest.srcFile 'AndroidManifest.xml' + java.srcDirs = [qt5AndroidDir + '/src', 'src', 'java'] + aidl.srcDirs = [qt5AndroidDir + '/src', 'src', 'aidl'] + res.srcDirs = [qt5AndroidDir + '/res', 'res'] + resources.srcDirs = ['resources'] + renderscript.srcDirs = ['src'] + assets.srcDirs = ['assets'] + jniLibs.srcDirs = ['libs'] + } + } + + tasks.withType(JavaCompile) { + options.incremental = true + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + lintOptions { + abortOnError false + } + + // Do not compress Qt binary resources file + aaptOptions { + noCompress 'rcc' + } + + defaultConfig { + resConfig "en" + minSdkVersion = qtMinSdkVersion + targetSdkVersion = qtTargetSdkVersion + } +} diff --git a/platforms/android/gradle.properties b/platforms/android/gradle.properties new file mode 100644 index 0000000000..fded106b17 --- /dev/null +++ b/platforms/android/gradle.properties @@ -0,0 +1,11 @@ +# Project-wide Gradle settings. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m + +# Gradle caching allows reusing the build artifacts from a previous +# build with the same inputs. However, over time, the cache size will +# grow. Uncomment the following line to enable it. +#org.gradle.caching=true diff --git a/platforms/android/gradle/wrapper/gradle-wrapper.jar b/platforms/android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..f6b961fd5a86aa5fbfe90f707c3138408be7c718 GIT binary patch literal 54329 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

    <5G2M1WZP5 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 F{|7XC?P&l2 literal 0 HcmV?d00001 diff --git a/platforms/android/gradle/wrapper/gradle-wrapper.properties b/platforms/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000000..fbce071a31 --- /dev/null +++ b/platforms/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-all.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/platforms/android/gradlew b/platforms/android/gradlew new file mode 100755 index 0000000000..cccdd3d517 --- /dev/null +++ b/platforms/android/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/platforms/android/gradlew.bat b/platforms/android/gradlew.bat new file mode 100644 index 0000000000..f9553162f1 --- /dev/null +++ b/platforms/android/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/platforms/platforms.pro b/platforms/platforms.pro index edadd8480f..8b27d802a9 100644 --- a/platforms/platforms.pro +++ b/platforms/platforms.pro @@ -2,7 +2,7 @@ TEMPLATE = subdirs #android: SUBDIRS += android #ios: SUBDIRS += ios -unix:!macx:!android: SUBDIRS += linux +unix:!macx: SUBDIRS += linux macx: SUBDIRS += macos win32: SUBDIRS += windows diff --git a/qlc.pro b/qlc.pro index fe7a4452bd..6ae1c69626 100644 --- a/qlc.pro +++ b/qlc.pro @@ -47,7 +47,9 @@ win32:coverage.commands = @echo Get a better OS. # Translations translations.target = translate -QMAKE_EXTRA_TARGETS += translations +!android: { + QMAKE_EXTRA_TARGETS += translations +} qmlui: { translations.commands += ./translate.sh "qmlui" } else { @@ -59,7 +61,9 @@ appimage: { } else { translations.path = $$INSTALLROOT/$$TRANSLATIONDIR } -INSTALLS += translations +!android: { + INSTALLS += translations +} QMAKE_DISTCLEAN += $$translations.files # run diff --git a/qmlui/qmlui.pro b/qmlui/qmlui.pro index 40fbd74788..4eeb47644e 100644 --- a/qmlui/qmlui.pro +++ b/qmlui/qmlui.pro @@ -25,7 +25,7 @@ DEPENDPATH += ../engine/src QMAKE_LIBDIR += ../engine/src android { - LIBS += -lqlcplusengine_$${first(ANDROID_ABIS)} + LIBS += -lqlcplusengine_$${QT_ARCH} } else { LIBS += -lqlcplusengine } From 5c63a96014efd514f6cf1573f3795c50785ea46d Mon Sep 17 00:00:00 2001 From: Binary-Vanguard-12138 <103393933+Binary-Vanguard-12138@users.noreply.github.com> Date: Wed, 7 Feb 2024 01:35:56 +0100 Subject: [PATCH 056/212] Minor fix for android build --- engine/src/src.pro | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/engine/src/src.pro b/engine/src/src.pro index d6694d2039..ac7d0bdfbd 100644 --- a/engine/src/src.pro +++ b/engine/src/src.pro @@ -34,7 +34,11 @@ INCLUDEPATH += ../../hotplugmonitor/src LIBS += -L../../hotplugmonitor/src -lhotplugmonitor } -LIBS += -L../audio/src -lqlcplusaudio +android { + LIBS += -L../audio/src -lqlcplusaudio_$${QT_ARCH} +} else { + LIBS += -L../audio/src -lqlcplusaudio +} ############################################################################# # Sources From 515733475a7e8dafa764f8304bb4bb42604fa283 Mon Sep 17 00:00:00 2001 From: Binary-Vanguard-12138 <103393933+Binary-Vanguard-12138@users.noreply.github.com> Date: Thu, 8 Feb 2024 08:35:17 +0800 Subject: [PATCH 057/212] webaccess virtual console grand master is supported. --- ui/src/grandmasterslider.cpp | 2 + webaccess/res/virtualconsole.js | 12 +++++ webaccess/res/websocket.js | 4 ++ webaccess/src/webaccess.cpp | 77 +++++++++++++++++++++++++++++++-- webaccess/src/webaccess.h | 3 ++ 5 files changed, 94 insertions(+), 4 deletions(-) diff --git a/ui/src/grandmasterslider.cpp b/ui/src/grandmasterslider.cpp index c55f3e11a6..52f60af013 100644 --- a/ui/src/grandmasterslider.cpp +++ b/ui/src/grandmasterslider.cpp @@ -188,6 +188,8 @@ void GrandMasterSlider::slotGrandMasterValueChanged(uchar value) m_slider->blockSignals(true); m_slider->setValue(value); m_slider->blockSignals(false); + + updateDisplayValue(); } void GrandMasterSlider::slotGrandMasterValueModeChanged(GrandMaster::ValueMode mode) diff --git a/webaccess/res/virtualconsole.js b/webaccess/res/virtualconsole.js index d6a273288c..788406e3c5 100644 --- a/webaccess/res/virtualconsole.js +++ b/webaccess/res/virtualconsole.js @@ -21,6 +21,18 @@ function initVirtualConsole() { updateTime(); } +function grandMasterValueChanged(value, displayValue) { + obj = document.getElementById("vcGMSlider"); + obj.value = value; + var labelObj = document.getElementById("vcGMSliderLabel"); + labelObj.innerHTML = displayValue; +} + +function grandMasterValueChange() { + obj = document.getElementById("vcGMSlider"); + websocket.send("GM_VALUE|" + obj.value); +} + /* VCButton */ function buttonPress(id) { websocket.send(id + "|255"); diff --git a/webaccess/res/websocket.js b/webaccess/res/websocket.js index 4abfdbd04a..459576e30f 100644 --- a/webaccess/res/websocket.js +++ b/webaccess/res/websocket.js @@ -46,6 +46,10 @@ function connect() { websocket.onmessage = function (ev) { //console.log(ev.data); var msgParams = ev.data.split("|"); + if (msgParams[0] === "GM_VALUE") { + grandMasterValueChanged(msgParams[1], msgParams[2]); + } + if (msgParams[1] === "BUTTON") { wsSetButtonState(msgParams[0], msgParams[2]); } else if (msgParams[1] === "BUTTON_DISABLE") { diff --git a/webaccess/src/webaccess.cpp b/webaccess/src/webaccess.cpp index d97637b4ef..764a7f7bd4 100644 --- a/webaccess/src/webaccess.cpp +++ b/webaccess/src/webaccess.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include "webaccess.h" @@ -48,6 +49,7 @@ #include "qlcfile.h" #include "chaser.h" #include "doc.h" +#include "grandmaster.h" #include "audiocapture.h" #include "audiorenderer.h" @@ -712,6 +714,12 @@ void WebAccess::slotHandleWebSocketRequest(QHttpConnection *conn, QString data) return; } + else if (cmdList[0] == "GM_VALUE") + { + uchar value = cmdList[1].toInt(); + m_doc->inputOutputMap()->setGrandMasterValue(value); + + } else if (cmdList[0] == "POLL") return; @@ -2081,6 +2089,66 @@ QString WebAccess::getChildrenHTML(VCWidget *frame, int pagesNum, int currentPag return unifiedHTML; } +void WebAccess::slotGrandMasterValueChanged(uchar value) +{ + GrandMaster::ValueMode value_mode = m_vc->properties().grandMasterValueMode(); + QString g_value_show; + if (value_mode == GrandMaster::Limit) + { + g_value_show = QString("%1").arg(value, 3, 10, QChar('0')); + } + else + { + int p = floor(((double(value) / double(UCHAR_MAX)) * double(100)) + 0.5); + g_value_show = QString("%1%").arg(p, 2, 10, QChar('0')); + } + QString wsMessage = QString("GM_VALUE|%1|%2").arg(value).arg(g_value_show); + sendWebSocketMessage(wsMessage.toUtf8()); +} + +QString WebAccess::getGrandMasterSliderHTML() +{ + GrandMaster::ValueMode value_mode = m_vc->properties().grandMasterValueMode(); + GrandMaster::SliderMode slider_mode = m_vc->properties().grandMasterSlideMode(); + uchar g_value = m_doc->inputOutputMap()->grandMasterValue(); + + QString g_value_show; + if (value_mode == GrandMaster::Limit) + { + g_value_show = QString("%1").arg(g_value, 3, 10, QChar('0')); + } + else + { + int p = floor(((double(g_value) / double(UCHAR_MAX)) * double(100)) + 0.5); + g_value_show = QString("%1%").arg(p, 2, 10, QChar('0')); + } + + QString str = "

    \n"; + str += "
    "; + str += "
    "+g_value_show+"
    \n"; + + int rotate = slider_mode == GrandMaster::SliderMode::Inverted ? 90 : 270; + QString mt = slider_mode == GrandMaster::SliderMode::Inverted ? "calc(-100vh + 120px)" : "calc(100vh - 120px)"; + int min = 0; + int max = 255; + + str += "\n"; + str += "
    GM
    "; + str += "
    \n"; + str += "
    \n"; + + connect(m_doc->inputOutputMap(), SIGNAL(grandMasterValueChanged(uchar)), + this, SLOT(slotGrandMasterValueChanged(uchar))); + + return str; +} + QString WebAccess::getVCHTML() { m_CSScode = "\n"; @@ -2098,7 +2166,7 @@ QString WebAccess::getVCHTML() "\n" "\n" - "
    \n" + "
    \n" "\n" "" + tr("Load project") + "\n" @@ -2107,11 +2175,12 @@ QString WebAccess::getVCHTML() "" + tr("Configuration") + "\n" "
    " + QString(APPNAME) + " " + QString(APPVERSION) + "
    " - "
    \n" - "
    \n"; + widgetsHTML += "
    "+getGrandMasterSliderHTML()+"
    "; + widgetsHTML += "
    backgroundColor().name() + ";\">\n"; + "background-color: " + mainFrame->backgroundColor().name() + "; top: 40px; left: 40px;\">\n"; widgetsHTML += getChildrenHTML(mainFrame, 0, 0); diff --git a/webaccess/src/webaccess.h b/webaccess/src/webaccess.h index ef90124043..f0afade685 100644 --- a/webaccess/src/webaccess.h +++ b/webaccess/src/webaccess.h @@ -72,6 +72,7 @@ class WebAccess : public QObject QString getCueListHTML(VCCueList *cue); QString getClockHTML(VCClock *clock); QString getMatrixHTML(VCMatrix *matrix); + QString getGrandMasterSliderHTML(); QString getChildrenHTML(VCWidget *frame, int pagesNum, int currentPageIdx); QString getVCHTML(); @@ -109,6 +110,8 @@ protected slots: void slotMatrixAnimationValueChanged(QString name); void slotMatrixControlKnobValueChanged(int controlID, int value); + void slotGrandMasterValueChanged(uchar value); + protected: QString m_JScode; QString m_CSScode; From be970146e456c7a4cfc8a6a4fd678853d7762882 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Thu, 8 Feb 2024 13:09:54 +0100 Subject: [PATCH 058/212] resources: add Talent SSL2 fixture --- debian/changelog | 1 + resources/fixtures/FixturesMap.xml | 1 + resources/fixtures/Talent/Talent-SSL2.qxf | 83 +++++++++++++++++++++++ 3 files changed, 85 insertions(+) create mode 100644 resources/fixtures/Talent/Talent-SSL2.qxf diff --git a/debian/changelog b/debian/changelog index bfb9fa4f81..86d4d35939 100644 --- a/debian/changelog +++ b/debian/changelog @@ -60,6 +60,7 @@ qlcplus (4.12.8) stable; urgency=low * 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 fixture: Talent SSL2 -- Massimo Callegari Sun, 18 Feb 2024 12:13:14 +0200 diff --git a/resources/fixtures/FixturesMap.xml b/resources/fixtures/FixturesMap.xml index f2f19c7698..dbad519de7 100644 --- a/resources/fixtures/FixturesMap.xml +++ b/resources/fixtures/FixturesMap.xml @@ -1677,6 +1677,7 @@ + diff --git a/resources/fixtures/Talent/Talent-SSL2.qxf b/resources/fixtures/Talent/Talent-SSL2.qxf new file mode 100644 index 0000000000..1eb55c0bcb --- /dev/null +++ b/resources/fixtures/Talent/Talent-SSL2.qxf @@ -0,0 +1,83 @@ + + + + + Q Light Controller Plus + 4.12.8 GIT + Ken Dreyer + + Talent + SSL2 + Moving Head + + + + Shutter + off + dimming + strobe flash + constant on + + + + + + + + + + Colour + 231 kinds of color choosing + full color jump + + + Speed + Color jumping speed + + + Effect + off + fast auto + slow auto + sound active mode + + + Maintenance + Nothing + Reset + + + Pan + Tilt + Functions control + Red + Green + Blue + White + Speed + Reset + + + Pan + Pan fine + Tilt + Tilt fine + Speed + Functions control + Red + Green + Blue + White + Color macros + Color jumping speed + Auto programs + Reset + + + + + + + + + From 8061b45d0daccb0dd395de22e6f5c1522a471a30 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Thu, 8 Feb 2024 13:14:28 +0100 Subject: [PATCH 059/212] Bump to version 4.13.0 GIT --- CMakeLists.txt | 2 +- debian/changelog | 4 ++-- platforms/windows/qlcplus4Qt5.nsi | 2 +- platforms/windows/qlcplus4Qt6.nsi | 2 +- variables.cmake | 2 +- variables.pri | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a6c93a4e84..5bb4681a89 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.16) -project(qlcplus VERSION 4.12.8 LANGUAGES C CXX) +project(qlcplus VERSION 4.13.0 LANGUAGES C CXX) # Set Release build type by default if(NOT CMAKE_BUILD_TYPE) diff --git a/debian/changelog b/debian/changelog index 86d4d35939..ee39c64361 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,4 +1,4 @@ -qlcplus (4.12.8) stable; urgency=low +qlcplus (4.13.0) stable; urgency=low * engine: fix Chaser random startup (thanks to Dennis Suermann) * engine: do not fade out looped audio @@ -62,7 +62,7 @@ qlcplus (4.12.8) stable; urgency=low * New fixture: Chauvet COLORado Batten 72x (thanks to Greg Perrone) * New fixture: Talent SSL2 - -- Massimo Callegari Sun, 18 Feb 2024 12:13:14 +0200 + -- Massimo Callegari Sun, 10 Mar 2024 12:13:14 +0200 qlcplus (4.12.7) stable; urgency=low diff --git a/platforms/windows/qlcplus4Qt5.nsi b/platforms/windows/qlcplus4Qt5.nsi index 185a902d94..1322b3032d 100644 --- a/platforms/windows/qlcplus4Qt5.nsi +++ b/platforms/windows/qlcplus4Qt5.nsi @@ -15,7 +15,7 @@ ;-------------------------------- ;General Name "Q Light Controller Plus" -OutFile "QLC+_4.12.8.exe" +OutFile "QLC+_4.13.0.exe" InstallDir C:\QLC+ InstallDirRegKey HKCU "Software\qlcplus" "Install_Dir" RequestExecutionLevel user diff --git a/platforms/windows/qlcplus4Qt6.nsi b/platforms/windows/qlcplus4Qt6.nsi index b60944b728..e46265ad3d 100644 --- a/platforms/windows/qlcplus4Qt6.nsi +++ b/platforms/windows/qlcplus4Qt6.nsi @@ -15,7 +15,7 @@ ;-------------------------------- ;General Name "Q Light Controller Plus" -OutFile "QLC+_4.12.8.exe" +OutFile "QLC+_4.13.0.exe" InstallDir C:\QLC+ InstallDirRegKey HKCU "Software\qlcplus" "Install_Dir" RequestExecutionLevel user diff --git a/variables.cmake b/variables.cmake index cb1bd24e2c..045211b6ab 100644 --- a/variables.cmake +++ b/variables.cmake @@ -21,7 +21,7 @@ if(qmlui) add_definitions(-DQMLUI) set(APPVERSION "5.0.0 Beta 3") else() - set(APPVERSION "4.12.8 GIT") + set(APPVERSION "4.13.0 GIT") endif() if(UNIX) diff --git a/variables.pri b/variables.pri index 11f5adbb74..0afd15dd94 100644 --- a/variables.pri +++ b/variables.pri @@ -4,7 +4,7 @@ APPNAME = Q Light Controller Plus FXEDNAME = Fixture Definition Editor -!qmlui: APPVERSION = 4.12.8 GIT +!qmlui: APPVERSION = 4.13.0 GIT qmlui: APPVERSION = 5.0.0 Beta 3 # Disable these if you don't want to see GIT short hash in the About Box From 65e1906d5c3badcfbbeb3ea1eeb2f2ca4bce7a82 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Thu, 8 Feb 2024 13:27:20 +0100 Subject: [PATCH 060/212] ui: remove docs from deployment --- platforms/windows/qlcplus4Qt5.nsi | 2 -- platforms/windows/qlcplus4Qt6.nsi | 2 -- resources/CMakeLists.txt | 3 --- ui/src/CMakeLists.txt | 1 - ui/src/app.cpp | 3 +-- ui/src/src.pro | 2 -- 6 files changed, 1 insertion(+), 12 deletions(-) diff --git a/platforms/windows/qlcplus4Qt5.nsi b/platforms/windows/qlcplus4Qt5.nsi index 1322b3032d..0176177cc7 100644 --- a/platforms/windows/qlcplus4Qt5.nsi +++ b/platforms/windows/qlcplus4Qt5.nsi @@ -91,7 +91,6 @@ Section File /r styles File Sample.qxw File *.qm - File /r Documents File /r Fixtures File /r Gobos File /r InputProfiles @@ -133,7 +132,6 @@ Section "Uninstall" RMDir /r $INSTDIR\styles Delete $INSTDIR\Sample.qxw Delete $INSTDIR\*.qm - RMDir /r $INSTDIR\Documents RMDir /r $INSTDIR\Fixtures RMDir /r $INSTDIR\Gobos RMDir /r $INSTDIR\InputProfiles diff --git a/platforms/windows/qlcplus4Qt6.nsi b/platforms/windows/qlcplus4Qt6.nsi index e46265ad3d..77d056fd19 100644 --- a/platforms/windows/qlcplus4Qt6.nsi +++ b/platforms/windows/qlcplus4Qt6.nsi @@ -90,7 +90,6 @@ Section File /r styles File Sample.qxw File *.qm - File /r Documents File /r Fixtures File /r Gobos File /r InputProfiles @@ -131,7 +130,6 @@ Section "Uninstall" RMDir /r $INSTDIR\styles Delete $INSTDIR\Sample.qxw Delete $INSTDIR\*.qm - RMDir /r $INSTDIR\Documents RMDir /r $INSTDIR\Fixtures RMDir /r $INSTDIR\Gobos RMDir /r $INSTDIR\InputProfiles diff --git a/resources/CMakeLists.txt b/resources/CMakeLists.txt index 01910ebf6d..42ff4fe251 100644 --- a/resources/CMakeLists.txt +++ b/resources/CMakeLists.txt @@ -7,9 +7,6 @@ add_subdirectory(miditemplates) add_subdirectory(modifierstemplates) add_subdirectory(rgbscripts) add_subdirectory(samples) -if(NOT qmlui) - add_subdirectory(docs) -endif() if(qmlui) add_subdirectory(colorfilters) add_subdirectory(meshes) diff --git a/ui/src/CMakeLists.txt b/ui/src/CMakeLists.txt index 21c4e8a265..bf88abf93f 100644 --- a/ui/src/CMakeLists.txt +++ b/ui/src/CMakeLists.txt @@ -61,7 +61,6 @@ add_library(${module_name} ctkrangeslider.cpp ctkrangeslider.h cuestackmodel.cpp cuestackmodel.h dmxdumpfactory.cpp dmxdumpfactory.h dmxdumpfactory.ui - docbrowser.cpp docbrowser.h efxeditor.cpp efxeditor.h efxeditor.ui efxpreviewarea.cpp efxpreviewarea.h fixtureconsole.cpp fixtureconsole.h diff --git a/ui/src/app.cpp b/ui/src/app.cpp index 03a44b6c32..70d06416ca 100644 --- a/ui/src/app.cpp +++ b/ui/src/app.cpp @@ -39,7 +39,6 @@ #include "mastertimer.h" #include "addresstool.h" #include "simpledesk.h" -#include "docbrowser.h" #include "aboutbox.h" #include "monitor.h" #include "vcframe.h" @@ -1198,7 +1197,7 @@ void App::slotControlFullScreen(bool usingGeometry) void App::slotHelpIndex() { - DocBrowser::createAndShow(this); + QDesktopServices::openUrl(QUrl("https://docs.qlcplus.org/")); } void App::slotHelpAbout() diff --git a/ui/src/src.pro b/ui/src/src.pro index 33a6a456c7..8f4d9a5876 100644 --- a/ui/src/src.pro +++ b/ui/src/src.pro @@ -68,7 +68,6 @@ HEADERS += aboutbox.h \ createfixturegroup.h \ ctkrangeslider.h \ cuestackmodel.h \ - docbrowser.h \ dmxdumpfactory.h \ efxeditor.h \ efxpreviewarea.h \ @@ -249,7 +248,6 @@ SOURCES += aboutbox.cpp \ createfixturegroup.cpp \ ctkrangeslider.cpp \ cuestackmodel.cpp \ - docbrowser.cpp \ dmxdumpfactory.cpp \ efxeditor.cpp \ efxpreviewarea.cpp \ From 2abb366eba0eafcdeab13f8dee08505b5ef0400b Mon Sep 17 00:00:00 2001 From: Binary-Vanguard-12138 <103393933+Binary-Vanguard-12138@users.noreply.github.com> Date: Thu, 8 Feb 2024 20:44:22 +0800 Subject: [PATCH 061/212] code style update --- webaccess/src/webaccess.cpp | 40 ++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/webaccess/src/webaccess.cpp b/webaccess/src/webaccess.cpp index 764a7f7bd4..ac88134ada 100644 --- a/webaccess/src/webaccess.cpp +++ b/webaccess/src/webaccess.cpp @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include "webaccess.h" @@ -2091,44 +2091,44 @@ QString WebAccess::getChildrenHTML(VCWidget *frame, int pagesNum, int currentPag void WebAccess::slotGrandMasterValueChanged(uchar value) { - GrandMaster::ValueMode value_mode = m_vc->properties().grandMasterValueMode(); - QString g_value_show; - if (value_mode == GrandMaster::Limit) + GrandMaster::ValueMode gmValueMode = m_vc->properties().grandMasterValueMode(); + QString gmDisplayValue; + if (gmValueMode == GrandMaster::Limit) { - g_value_show = QString("%1").arg(value, 3, 10, QChar('0')); + gmDisplayValue = QString("%1").arg(value, 3, 10, QChar('0')); } else { - int p = floor(((double(value) / double(UCHAR_MAX)) * double(100)) + 0.5); - g_value_show = QString("%1%").arg(p, 2, 10, QChar('0')); + int p = qFloor(((double(value) / double(UCHAR_MAX)) * double(100)) + 0.5); + gmDisplayValue = QString("%1%").arg(p, 2, 10, QChar('0')); } - QString wsMessage = QString("GM_VALUE|%1|%2").arg(value).arg(g_value_show); + QString wsMessage = QString("GM_VALUE|%1|%2").arg(value).arg(gmDisplayValue); sendWebSocketMessage(wsMessage.toUtf8()); } QString WebAccess::getGrandMasterSliderHTML() { - GrandMaster::ValueMode value_mode = m_vc->properties().grandMasterValueMode(); - GrandMaster::SliderMode slider_mode = m_vc->properties().grandMasterSlideMode(); - uchar g_value = m_doc->inputOutputMap()->grandMasterValue(); + GrandMaster::ValueMode gmValueMode = m_vc->properties().grandMasterValueMode(); + GrandMaster::SliderMode gmSliderMode = m_vc->properties().grandMasterSlideMode(); + uchar gmValue = m_doc->inputOutputMap()->grandMasterValue(); - QString g_value_show; - if (value_mode == GrandMaster::Limit) + QString gmDisplayValue; + if (gmValueMode == GrandMaster::Limit) { - g_value_show = QString("%1").arg(g_value, 3, 10, QChar('0')); + gmDisplayValue = QString("%1").arg(gmValue, 3, 10, QChar('0')); } else { - int p = floor(((double(g_value) / double(UCHAR_MAX)) * double(100)) + 0.5); - g_value_show = QString("%1%").arg(p, 2, 10, QChar('0')); + int p = qFloor(((double(gmValue) / double(UCHAR_MAX)) * double(100)) + 0.5); + gmDisplayValue = QString("%1%").arg(p, 2, 10, QChar('0')); } QString str = "
    \n"; str += "
    "; - str += "
    "+g_value_show+"
    \n"; + str += "
    "+gmDisplayValue+"
    \n"; - int rotate = slider_mode == GrandMaster::SliderMode::Inverted ? 90 : 270; - QString mt = slider_mode == GrandMaster::SliderMode::Inverted ? "calc(-100vh + 120px)" : "calc(100vh - 120px)"; + int rotate = gmSliderMode == GrandMaster::SliderMode::Inverted ? 90 : 270; + QString mt = gmSliderMode == GrandMaster::SliderMode::Inverted ? "calc(-100vh + 120px)" : "calc(100vh - 120px)"; int min = 0; int max = 255; @@ -2138,7 +2138,7 @@ QString WebAccess::getGrandMasterSliderHTML() "margin-left: 20px; " "--rotate: "+QString::number(rotate)+"\" " "min=\""+QString::number(min)+"\" max=\""+QString::number(max)+"\" " - "step=\"1\" value=\"" + QString::number(g_value) + "\">\n"; + "step=\"1\" value=\"" + QString::number(gmValue) + "\">\n"; str += "
    GM
    "; str += "
    \n"; str += "
    \n"; From 9016d9b6c28b0cfacfba88b7cfd965359d017948 Mon Sep 17 00:00:00 2001 From: Binary-Vanguard-12138 <103393933+Binary-Vanguard-12138@users.noreply.github.com> Date: Fri, 9 Feb 2024 08:35:23 +0100 Subject: [PATCH 062/212] qlcplusaudio does not have to be ABI --- engine/src/src.pro | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/engine/src/src.pro b/engine/src/src.pro index ac7d0bdfbd..d6694d2039 100644 --- a/engine/src/src.pro +++ b/engine/src/src.pro @@ -34,11 +34,7 @@ INCLUDEPATH += ../../hotplugmonitor/src LIBS += -L../../hotplugmonitor/src -lhotplugmonitor } -android { - LIBS += -L../audio/src -lqlcplusaudio_$${QT_ARCH} -} else { - LIBS += -L../audio/src -lqlcplusaudio -} +LIBS += -L../audio/src -lqlcplusaudio ############################################################################# # Sources From 42de356e38166b04b2b332108c75d4631d8265f1 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Fri, 9 Feb 2024 18:59:07 +0100 Subject: [PATCH 063/212] Complete Cameo-P2-FC --- resources/fixtures/Cameo/Cameo-P2-FC.qxf | 205 +++++++++++++++++++++-- 1 file changed, 195 insertions(+), 10 deletions(-) diff --git a/resources/fixtures/Cameo/Cameo-P2-FC.qxf b/resources/fixtures/Cameo/Cameo-P2-FC.qxf index b14574cf1d..22d4e74e98 100644 --- a/resources/fixtures/Cameo/Cameo-P2-FC.qxf +++ b/resources/fixtures/Cameo/Cameo-P2-FC.qxf @@ -3,8 +3,8 @@ Q Light Controller Plus - 4.12.7 - Christoph Müllner + 4.13.0 GIT + Christoph Müllner, Massimo Callegari Cameo P2 FC @@ -46,23 +46,156 @@ Neutral Neutral -> green - + + Shutter + Strobe open + Strobe closed + Ramp up/down slow to fast + Ramp up/down random, slow to fast + Ramp up, slow to fast + Ramp up random, slow to fast + Ramp down, slow to fast + Ramp down random, slow to fast + Random strobe effect, slow to fast + Strobe break effect, 5s...1s (short burst with break) + Strobe slow to fast < 1Hz - 20Hz + Strobe open + + + Colour + No function + 46 Dark Magenta + 29 Plasa Red + 26 Bright Red + 127 Smokey Pink + 36 Medium Pink + 19 Fire + 135 Deep Golden Amber + 778 Millennium Gold + 21 Gold Amber + 157 Pink + 110 Middle Rose + 109 Light Salmon + 35 Light Pink + 134 Golden Amber + 17 Surprise Peach + 746 Brown + 105 Orange + 20 Medium Amber + 768 Egg Yolk Yellow + 15 Deep Straw + 767 Oklahoma Yellow + 101 Yellow + 100 Spring Yellow + 88 Lime Green + 121 LEE Green + 738 Jas Green + 89 Moss Green + 139 Primary Green + 124 Dark Green + 323 Jade + 354 Special Steel Blue + 116 Medium Blue-Green + 183 Moonlight Blue + 132 Medium Blue + 119 Dark Blue + 716 Mikkel Blue + 71 Tokyo Blue + 181 Congo Blue + 799 Special KH Lavender + 707 Ultimate Violet + 343 Special Medium Lavender + 798 Chrysalis Pink + 701 Provence + 797 Deep Purple + 48 Rose Purple + 345 Fuchsia Pink + 795 Magical Magenta + 128 Bright Pink + 2 Rose Pink + User Colour_1 + User Colour_2 + User Colour_3 + User Colour_4 + User Colour_5 + User Colour_6 + User Colour_7 + User Colour_8 + No function + + + Maintenance + No function + Record User Colour 1 (hold 3s) + Record User Colour 2 (hold 3s) + Record User Colour 3 (hold 3s) + Record User Colour 4 (hold 3s) + Record User Colour 5 (hold 3s) + Record User Colour 6 (hold 3s) + Record User Colour 7 (hold 3s) + Record User Colour 8 (hold 3s) + No function + Dimmer Response LED (hold 3s) + Dimmer Response Halogen (hold 3s) + No function + DTW (Redshift) on (hold 1,5s) + DTW (Redshift) off (hold 1,5s) + No function + Auto Fan (hold 3s) + Fan Off (hold 3s) + Constant Low Fan (hold 3s) + Constant Mid Fan (hold 3s) + Constant High Fan (hold 3s) + No function + LED Frequency 600Hz (hold 3s) + LED Frequency 1200Hz (hold 3s) + LED Frequency 2000Hz (hold 3s) + LED Frequency 4000Hz (hold 3s) + LED Frequency 6000Hz (hold 3s) + LED Frequency 18.9kHz (hold 3s) + LED Frequency 25kHz (hold 3s) + RAW (hold 3s) + Calibrated (hold 3s) + User Calibrated (hold 3s) + Smart Calibration (hold 3s) + Display on (hold 3s) + Display off (hold 3s) + No function + Dimmer Curve Linear (hold 3s) + Dimmer Curve Exponential (hold 3s) + Dimmer Curve Logarithmic (hold 3s) + Dimmer Curve S-Curve (hold 3s) + No function + Default set (except DMX-Address, DMX-Mode) (hold 3s) + Default set (except DMX-Address, DMX-Mode and User +Colour/Loops) (hold 3s) + No function + + + Effect + 0s + 0,1s - 10s (0,1s Steps) + 11s - 119s (1s Steps) + 2m - 4m50s (10s Steps) + 5m - 15m (1m Steps) + + Dimmer Colour Temperature - + Red Green Blue - + Red Green Blue Amber Lime - + Red Red fine Green @@ -74,13 +207,13 @@ Lime Lime fine - + Dimmer Dimmer fine Colour Temperature Tint - + Dimmer Dimmer fine Hue @@ -88,7 +221,7 @@ Colour Temperature Tint - + Dimmer Dimmer fine Red @@ -97,10 +230,62 @@ Colour Temperature Tint + + Dimmer + + + Dimmer + Dimmer fine + + + Dimmer + Dimmer fine + Strobe Functions + Red + Green + Blue + Amber + Lime + Colour Temperature + Tint + Device Settings + + + Dimmer + Dimmer fine + Strobe Functions + Hue + Saturation + Colour Temperature + Tint + Colour Presets + Colour Preset Crossfade + Device Settings + + + Dimmer + Dimmer fine + Strobe Functions + Red + Red fine + Green + Green fine + Blue + Blue fine + Amber + Amber fine + Lime + Lime fine + Colour Temperature + Tint + Colour Presets + Colour Preset Crossfade + Device Settings + - + From 70d81c87f15feea418fa30aa0b0ff0259ad6e011 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Sat, 10 Feb 2024 16:33:36 +0100 Subject: [PATCH 064/212] engine: add color table support to input profiles --- engine/src/qlcinputprofile.cpp | 101 ++++++++++++++++++++++- engine/src/qlcinputprofile.h | 17 ++++ resources/inputprofiles/Akai-APCMini.qxi | 12 ++- 3 files changed, 128 insertions(+), 2 deletions(-) diff --git a/engine/src/qlcinputprofile.cpp b/engine/src/qlcinputprofile.cpp index 3d1a8b2872..af122f77e1 100644 --- a/engine/src/qlcinputprofile.cpp +++ b/engine/src/qlcinputprofile.cpp @@ -35,6 +35,9 @@ #define KXMLQLCInputProfileTypeDmx "DMX" #define KXMLQLCInputProfileTypeEnttec "Enttec" +#define KXMLQLCInputProfileColorValue "Value" +#define KXMLQLCInputProfileColorLabel "Label" +#define KXMLQLCInputProfileColorRGB "RGB" /**************************************************************************** * Initialization @@ -71,6 +74,15 @@ QLCInputProfile *QLCInputProfile::createCopy() copy->insertChannel(it.key(), it.value()->createCopy()); } + /* Copy the other profile's color table */ + QMapIterator > it2(this->colorTable()); + while (it2.hasNext() == true) + { + it2.next(); + QPair lc = it2.value(); + copy->addColor(it2.key(), lc.first, lc.second); + } + return copy; } @@ -90,12 +102,21 @@ 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); + } } return *this; @@ -311,6 +332,29 @@ 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; +} + /**************************************************************************** * Load & Save ****************************************************************************/ @@ -345,6 +389,36 @@ 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(KXMLQLCInputProfileColorValue).toInt(); + QString label = tableRoot.attributes().value(KXMLQLCInputProfileColorLabel).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::loadXML(QXmlStreamReader& doc) { if (doc.readNextStartElement() == false) @@ -393,6 +467,10 @@ bool QLCInputProfile::loadXML(QXmlStreamReader& doc) else doc.skipCurrentElement(); } + else if (doc.name() == KXMLQLCInputProfileColorTable) + { + loadColorTableXML(doc); + } } return true; @@ -433,10 +511,31 @@ bool QLCInputProfile::saveXML(const QString& fileName) it.value()->saveXML(&doc, it.key()); } + if (!m_colorTable.empty()) + { + doc.writeStartElement(KXMLQLCInputProfileColorTable); + + QMapIterator > it(m_colorTable); + while (it.hasNext() == true) + { + it.next(); + QPair lc = it.value(); + doc.writeStartElement(KXMLQLCInputProfileColor); + doc.writeAttribute(KXMLQLCInputProfileColorValue, QString::number(it.key())); + doc.writeAttribute(KXMLQLCInputProfileColorLabel, lc.first); + doc.writeAttribute(KXMLQLCInputProfileColorRGB, lc.second.name()); + doc.writeEndElement(); + } + + doc.writeEndElement(); + } + m_path = fileName; + /* End the document and close all the open elements */ doc.writeEndDocument(); file.close(); return true; } + diff --git a/engine/src/qlcinputprofile.h b/engine/src/qlcinputprofile.h index 70027ce437..f5ec6cd0c9 100644 --- a/engine/src/qlcinputprofile.h +++ b/engine/src/qlcinputprofile.h @@ -40,6 +40,8 @@ class QXmlStreamReader; #define KXMLQLCInputProfileModel QString("Model") #define KXMLQLCInputProfileType QString("Type") #define KXMLQLCInputProfileMidiSendNoteOff QString("MIDISendNoteOff") +#define KXMLQLCInputProfileColorTable QString("ColorTable") +#define KXMLQLCInputProfileColor QString("Color") class QLCInputProfile : public QObject { @@ -189,6 +191,19 @@ class QLCInputProfile : public QObject QList because not all channels might be present. */ QMap m_channels; + /******************************************************************** + * Color Translation Table + ********************************************************************/ +public: + bool hasColorTable(); + void addColor(uchar value, QString label, QColor color); + void removeColor(uchar value); + + QMap> colorTable(); + +protected: + QMap> m_colorTable; + /******************************************************************** * Load & Save ********************************************************************/ @@ -199,6 +214,8 @@ class QLCInputProfile : public QObject /** Save an input profile into a given file name */ bool saveXML(const QString& fileName); + bool loadColorTableXML(QXmlStreamReader &tableRoot); + /** Load an input profile from the given document */ bool loadXML(QXmlStreamReader &doc); }; diff --git a/resources/inputprofiles/Akai-APCMini.qxi b/resources/inputprofiles/Akai-APCMini.qxi index 25ba13734e..1af7e7918e 100644 --- a/resources/inputprofiles/Akai-APCMini.qxi +++ b/resources/inputprofiles/Akai-APCMini.qxi @@ -3,7 +3,7 @@ Q Light Controller Plus - 4.8.0 + 4.13.0 Maikel Boerebach Akai @@ -370,4 +370,14 @@ Shift Button Button + + + + + + + + + + From 95e64356ad5c110ede2de62e984afac4489212b5 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Sat, 10 Feb 2024 16:35:23 +0100 Subject: [PATCH 065/212] ui: move custom feedbacks to a dedicated dialog Add color table editing to Input Profile editor --- ui/src/CMakeLists.txt | 1 + ui/src/addchannelsgroup.h | 5 +- ui/src/customfeedbacksdialog.cpp | 160 +++++++++++++++ ui/src/customfeedbacksdialog.h | 62 ++++++ ui/src/customfeedbacksdialog.ui | 204 +++++++++++++++++++ ui/src/inputprofileeditor.cpp | 63 +++++- ui/src/inputprofileeditor.h | 4 + ui/src/inputprofileeditor.ui | 103 ++++++++-- ui/src/inputselectionwidget.cpp | 87 +------- ui/src/inputselectionwidget.h | 12 +- ui/src/inputselectionwidget.ui | 123 ++--------- ui/src/src.pro | 2 + ui/src/virtualconsole/vcbuttonproperties.cpp | 2 +- 13 files changed, 623 insertions(+), 205 deletions(-) create mode 100644 ui/src/customfeedbacksdialog.cpp create mode 100644 ui/src/customfeedbacksdialog.h create mode 100644 ui/src/customfeedbacksdialog.ui diff --git a/ui/src/CMakeLists.txt b/ui/src/CMakeLists.txt index bf88abf93f..b258c724f3 100644 --- a/ui/src/CMakeLists.txt +++ b/ui/src/CMakeLists.txt @@ -60,6 +60,7 @@ add_library(${module_name} createfixturegroup.cpp createfixturegroup.h createfixturegroup.ui ctkrangeslider.cpp ctkrangeslider.h cuestackmodel.cpp cuestackmodel.h + customfeedbacksdialog.cpp customfeedbacksdialog.h dmxdumpfactory.cpp dmxdumpfactory.h dmxdumpfactory.ui efxeditor.cpp efxeditor.h efxeditor.ui efxpreviewarea.cpp efxpreviewarea.h diff --git a/ui/src/addchannelsgroup.h b/ui/src/addchannelsgroup.h index 4482deb9a9..fd9f2c7a6d 100644 --- a/ui/src/addchannelsgroup.h +++ b/ui/src/addchannelsgroup.h @@ -23,7 +23,6 @@ #include #include "ui_addchannelsgroup.h" -#include "qlcinputsource.h" class InputSelectionWidget; class ChannelsGroup; @@ -55,8 +54,8 @@ class AddChannelsGroup : public QDialog, public Ui_AddChannelsGroup void accept(); private: - Doc* m_doc; - ChannelsGroup* m_chansGroup; + Doc *m_doc; + ChannelsGroup *m_chansGroup; InputSelectionWidget *m_inputSelWidget; protected: diff --git a/ui/src/customfeedbacksdialog.cpp b/ui/src/customfeedbacksdialog.cpp new file mode 100644 index 0000000000..90a9f1ff23 --- /dev/null +++ b/ui/src/customfeedbacksdialog.cpp @@ -0,0 +1,160 @@ +/* + Q Light Controller Plus + customfeedbacksdialog.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 "customfeedbacksdialog.h" +#include "qlcinputchannel.h" +#include "qlcinputsource.h" +#include "doc.h" + +CustomFeedbacksDialog::CustomFeedbacksDialog(Doc *doc, const QSharedPointer &source, QWidget *parent) + : QDialog(parent) + , m_doc(doc) + , m_profile(NULL) + , m_inputSource(source) + , m_selectedFeedback(None) +{ + setupUi(this); + + bool enableControls = source.isNull() ? false : true; + + if (enableControls) + { + m_lowerSpin->setValue(m_inputSource->lowerValue()); + m_upperSpin->setValue(m_inputSource->upperValue()); + m_monitorSpin->setValue(m_inputSource->monitorValue()); + } + + m_lowerSpin->setEnabled(enableControls); + m_upperSpin->setEnabled(enableControls); + + m_monitorLabel->setVisible(false); + m_monitorSpin->setVisible(false); + m_profileColorsTree->setVisible(false); + + if (enableControls) + { + InputPatch *ip = m_doc->inputOutputMap()->inputPatch(m_inputSource->universe()); + if (ip != NULL && ip->profile() != NULL) + { + m_profile = ip->profile(); + if (m_profile->hasColorTable()) + { + m_lowerColor->setVisible(true); + m_upperColor->setVisible(true); + + QMapIterator > it(m_profile->colorTable()); + while (it.hasNext() == true) + { + it.next(); + QPair lc = it.value(); + QTreeWidgetItem *item = new QTreeWidgetItem(m_profileColorsTree); + item->setText(0, QString::number(it.key())); + item->setText(1, lc.first); + + QLabel *colLabel = new QLabel(); + colLabel->setStyleSheet(QString("background-color: %1").arg(lc.second.name())); + + if (it.key() == m_inputSource->lowerValue()) + m_lowerColor->setStyleSheet(QString("background-color: %1").arg(lc.second.name())); + + if (it.key() == m_inputSource->upperValue()) + m_upperColor->setStyleSheet(QString("background-color: %1").arg(lc.second.name())); + + if (it.key() == m_inputSource->monitorValue()) + m_monitorColor->setStyleSheet(QString("background-color: %1").arg(lc.second.name())); + + m_profileColorsTree->setItemWidget(item, 2, colLabel); + } + } + } + } + + // connect signals + connect(m_lowerColor, SIGNAL(clicked()), + this, SLOT(slotLowerColorButtonClicked())); + connect(m_upperColor, SIGNAL(clicked()), + this, SLOT(slotUpperColorButtonClicked())); + connect(m_monitorColor, SIGNAL(clicked()), + this, SLOT(slotMonitorColorButtonClicked())); + connect(m_profileColorsTree, SIGNAL(itemClicked(QTreeWidgetItem *, int)), + this, SLOT(slotColorSelected(QTreeWidgetItem *))); +} + +CustomFeedbacksDialog::~CustomFeedbacksDialog() +{ +} + +void CustomFeedbacksDialog::setMonitoringVisibility(bool visible) +{ + m_monitorLabel->setVisible(visible); + m_monitorSpin->setVisible(visible); +} + +void CustomFeedbacksDialog::accept() +{ + if (m_inputSource.isNull()) + return; + + m_inputSource->setRange(m_lowerSpin->value(), m_upperSpin->value()); + if (m_monitorSpin->isVisible()) + m_inputSource->setMonitorValue(m_monitorSpin->value()); + + QDialog::accept(); +} + +void CustomFeedbacksDialog::slotLowerColorButtonClicked() +{ + m_selectedFeedback = LowerValue; + m_profileColorsTree->setVisible(true); +} + +void CustomFeedbacksDialog::slotUpperColorButtonClicked() +{ + m_selectedFeedback = UpperValue; + m_profileColorsTree->setVisible(true); +} + +void CustomFeedbacksDialog::slotMonitorColorButtonClicked() +{ + m_selectedFeedback = MonitoringValue; + m_profileColorsTree->setVisible(true); +} + +void CustomFeedbacksDialog::slotColorSelected(QTreeWidgetItem *item) +{ + QLabel *label = qobject_cast(m_profileColorsTree->itemWidget(item, 2)); + + if (m_selectedFeedback == LowerValue) + { + m_lowerSpin->setValue(item->text(0).toInt()); + m_lowerColor->setStyleSheet(label->styleSheet()); + } + else if (m_selectedFeedback == UpperValue) + { + m_upperSpin->setValue(item->text(0).toInt()); + m_upperColor->setStyleSheet(label->styleSheet()); + } + else if (m_selectedFeedback == MonitoringValue) + { + m_monitorSpin->setValue(item->text(0).toInt()); + m_monitorColor->setStyleSheet(label->styleSheet()); + } + m_profileColorsTree->setVisible(false); + m_selectedFeedback = None; +} diff --git a/ui/src/customfeedbacksdialog.h b/ui/src/customfeedbacksdialog.h new file mode 100644 index 0000000000..8ca639c0d3 --- /dev/null +++ b/ui/src/customfeedbacksdialog.h @@ -0,0 +1,62 @@ +/* + Q Light Controller Plus + customfeedbacksdialog.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 CUSTOMFEEDBACKSDIALOG_H +#define CUSTOMFEEDBACKSDIALOG_H + +#include + +#include "ui_customfeedbacksdialog.h" + +class Doc; +class QLCInputSource; +class QLCInputProfile; + +class CustomFeedbacksDialog : public QDialog, public Ui_CustomFeedbacksDialog +{ + Q_OBJECT + + /********************************************************************* + * Initialization + *********************************************************************/ +public: + explicit CustomFeedbacksDialog(Doc *doc, QSharedPointer const& source, QWidget *parent = nullptr); + ~CustomFeedbacksDialog(); + + enum SelectedFeedback { None, LowerValue, UpperValue, MonitoringValue }; + + void setMonitoringVisibility(bool visible); + + /** @reimp */ + void accept(); + +protected slots: + void slotLowerColorButtonClicked(); + void slotUpperColorButtonClicked(); + void slotMonitorColorButtonClicked(); + void slotColorSelected(QTreeWidgetItem *item); + +private: + Doc *m_doc; + QLCInputProfile *m_profile; + QSharedPointer m_inputSource; + SelectedFeedback m_selectedFeedback; +}; + +#endif // CUSTOMFEEDBACKSDIALOG_H diff --git a/ui/src/customfeedbacksdialog.ui b/ui/src/customfeedbacksdialog.ui new file mode 100644 index 0000000000..cdb732ef49 --- /dev/null +++ b/ui/src/customfeedbacksdialog.ui @@ -0,0 +1,204 @@ + + + CustomFeedbacksDialog + + + + 0 + 0 + 595 + 535 + + + + Custom Feedbacks Configuration + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + 0 + 0 + + + + Values + + + + + + + 10 + + + + Lower Value + + + + + + + 255 + + + + + + + 255 + + + + + + + + 10 + + + + Monitor Value + + + + + + + + 10 + + + + Upper Value + + + + + + + 0 + + + 255 + + + 255 + + + + + + + Color Selection + + + + + + + + + + Color Selection + + + + + + + + + + Color Selection + + + + + + + + + + + + + + Value + + + + + Label + + + + + Color + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + CustomFeedbacksDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + CustomFeedbacksDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/ui/src/inputprofileeditor.cpp b/ui/src/inputprofileeditor.cpp index 778be451e7..980e225eb9 100644 --- a/ui/src/inputprofileeditor.cpp +++ b/ui/src/inputprofileeditor.cpp @@ -18,10 +18,13 @@ */ #include +#include +#include #include #include #include #include +#include #include #include #include @@ -90,6 +93,11 @@ InputProfileEditor::InputProfileEditor(QWidget* parent, QLCInputProfile* profile connect(m_upperSpin, SIGNAL(valueChanged(int)), this, SLOT(slotUpperValueSpinChanged(int))); + connect(m_addColorButton, SIGNAL(clicked()), + this, SLOT(slotAddColor())); + connect(m_removeColorButton, SIGNAL(clicked()), + this, SLOT(slotRemoveColor())); + /* Listen to input data */ connect(m_ioMap, SIGNAL(inputValueChanged(quint32, quint32, uchar, const QString&)), this, SLOT(slotInputValueChanged(quint32, quint32, uchar, const QString&))); @@ -135,9 +143,13 @@ InputProfileEditor::InputProfileEditor(QWidget* parent, QLCInputProfile* profile m_behaviourBox->hide(); m_feedbackGroup->hide(); + /* Fill up the tree with profile's channels */ fillTree(); + /* Fill up the tree with color table */ + updateColorsTree(); + /* Timer that clears the input data icon after a while */ m_timer = new QTimer(this); m_timer->setSingleShot(true); @@ -171,8 +183,28 @@ void InputProfileEditor::fillTree() m_tree->header()->resizeSections(QHeaderView::ResizeToContents); } -void InputProfileEditor::updateChannelItem(QTreeWidgetItem* item, - QLCInputChannel* ch) +void InputProfileEditor::updateColorsTree() +{ + m_colorTableTree->clear(); + + QMapIterator > it(m_profile->colorTable()); + while (it.hasNext() == true) + { + it.next(); + QPair lc = it.value(); + QTreeWidgetItem *item = new QTreeWidgetItem(m_colorTableTree); + item->setText(0, QString::number(it.key())); + item->setText(1, lc.first); + + QLabel *colLabel = new QLabel(); + colLabel->setStyleSheet(QString("background-color: %1").arg(lc.second.name())); + + m_colorTableTree->setItemWidget(item, 2, colLabel); + } +} + +void InputProfileEditor::updateChannelItem(QTreeWidgetItem *item, + QLCInputChannel *ch) { quint32 num; @@ -256,7 +288,7 @@ void InputProfileEditor::accept() /* Check that we have at least the bare necessities to save the profile */ if (m_profile->manufacturer().isEmpty() == true || - m_profile->model().isEmpty() == true) + m_profile->model().isEmpty() == true) { QMessageBox::warning(this, tr("Missing information"), tr("Manufacturer and/or model name is missing.")); @@ -571,6 +603,31 @@ void InputProfileEditor::slotUpperValueSpinChanged(int value) } } +void InputProfileEditor::slotAddColor() +{ + bool ok; + int val = QInputDialog::getInt(this, tr("Enter value"), tr("Feedback value"), 0, 0, 255, 1, &ok); + + if (ok) + { + QColor color = QColorDialog::getColor(); + + QString label = QInputDialog::getText(this, tr("Enter label"), tr("Color label")); + m_profile->addColor(val, label, color); + updateColorsTree(); + } +} + +void InputProfileEditor::slotRemoveColor() +{ + foreach (QTreeWidgetItem *item, m_colorTableTree->selectedItems()) + { + uchar value = uchar(item->text(0).toInt()); + m_profile->removeColor(value); + } + updateColorsTree(); +} + void InputProfileEditor::slotInputValueChanged(quint32 universe, quint32 channel, uchar value, diff --git a/ui/src/inputprofileeditor.h b/ui/src/inputprofileeditor.h index 72d0a61304..56ec69193a 100644 --- a/ui/src/inputprofileeditor.h +++ b/ui/src/inputprofileeditor.h @@ -47,6 +47,7 @@ class InputProfileEditor : public QDialog, public Ui_InputProfileEditor protected: void fillTree(); + void updateColorsTree(); void updateChannelItem(QTreeWidgetItem *item, QLCInputChannel *ch); void setOptionsVisibility(QLCInputChannel::Type type); @@ -81,6 +82,9 @@ protected slots: void slotLowerValueSpinChanged(int value); void slotUpperValueSpinChanged(int value); + void slotAddColor(); + void slotRemoveColor(); + void slotInputValueChanged(quint32 universe, quint32 channel, uchar value, const QString& key = 0); void slotTimerTimeout(); diff --git a/ui/src/inputprofileeditor.ui b/ui/src/inputprofileeditor.ui index cf90c17e43..bf74513c57 100644 --- a/ui/src/inputprofileeditor.ui +++ b/ui/src/inputprofileeditor.ui @@ -36,8 +36,18 @@ :/input_output.png:/input_output.png - - + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + 0 @@ -387,16 +397,85 @@ - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - + + + Colors + + + + + + Remove the selected color + + + ... + + + + :/edit_remove.png:/edit_remove.png + + + + 32 + 32 + + + + + + + + Add a new color + + + ... + + + + :/edit_add.png:/edit_add.png + + + + 32 + 32 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + Value + + + + + Label + + + + + Color + + + + + + diff --git a/ui/src/inputselectionwidget.cpp b/ui/src/inputselectionwidget.cpp index 1693ef0daa..633c38baac 100644 --- a/ui/src/inputselectionwidget.cpp +++ b/ui/src/inputselectionwidget.cpp @@ -19,6 +19,7 @@ #include +#include "customfeedbacksdialog.h" #include "inputselectionwidget.h" #include "selectinputchannel.h" #include "qlcinputchannel.h" @@ -26,12 +27,12 @@ #include "inputpatch.h" #include "doc.h" - InputSelectionWidget::InputSelectionWidget(Doc *doc, QWidget *parent) : QWidget(parent) , m_doc(doc) , m_widgetPage(0) , m_emitOdd(false) + , m_supportMonitoring(false) , m_signalsReceived(0) { Q_ASSERT(doc != NULL); @@ -39,12 +40,6 @@ InputSelectionWidget::InputSelectionWidget(Doc *doc, QWidget *parent) setupUi(this); m_customFbButton->setVisible(false); - m_feedbackGroup->setVisible(false); - m_monitorLabel->setVisible(false); - m_monitorSpin->setVisible(false); - m_lowerSpin->setEnabled(false); - m_upperSpin->setEnabled(false); - m_monitorSpin->setEnabled(false); connect(m_attachKey, SIGNAL(clicked()), this, SLOT(slotAttachKey())); connect(m_detachKey, SIGNAL(clicked()), this, SLOT(slotDetachKey())); @@ -54,14 +49,8 @@ InputSelectionWidget::InputSelectionWidget(Doc *doc, QWidget *parent) connect(m_chooseInputButton, SIGNAL(clicked()), this, SLOT(slotChooseInputClicked())); - connect(m_customFbButton, SIGNAL(toggled(bool)), - this, SLOT(slotCustomFeedbackToggled(bool))); - connect(m_lowerSpin, SIGNAL(valueChanged(int)), - this, SLOT(slotLowerSpinValueChanged(int))); - connect(m_upperSpin, SIGNAL(valueChanged(int)), - this, SLOT(slotUpperSpinValueChanged(int))); - connect(m_monitorSpin, SIGNAL(valueChanged(int)), - this, SLOT(slotMonitorSpinValueChanged(int))); + connect(m_customFbButton, SIGNAL(clicked(bool)), + this, SLOT(slotCustomFeedbackClicked())); } InputSelectionWidget::~InputSelectionWidget() @@ -78,10 +67,9 @@ void InputSelectionWidget::setCustomFeedbackVisibility(bool visible) m_customFbButton->setVisible(visible); } -void InputSelectionWidget::setMonitoringVisibility(bool visible) +void InputSelectionWidget::setMonitoringSupport(bool enable) { - m_monitorLabel->setVisible(visible); - m_monitorSpin->setVisible(visible); + m_supportMonitoring = enable; } void InputSelectionWidget::setTitle(QString title) @@ -193,24 +181,11 @@ void InputSelectionWidget::slotChooseInputClicked() } } -void InputSelectionWidget::slotCustomFeedbackToggled(bool checked) -{ - m_feedbackGroup->setVisible(checked); -} - -void InputSelectionWidget::slotLowerSpinValueChanged(int value) -{ - m_inputSource->setRange(uchar(value), uchar(m_upperSpin->value())); -} - -void InputSelectionWidget::slotUpperSpinValueChanged(int value) +void InputSelectionWidget::slotCustomFeedbackClicked() { - m_inputSource->setRange(uchar(m_lowerSpin->value()), uchar(value)); -} - -void InputSelectionWidget::slotMonitorSpinValueChanged(int value) -{ - m_inputSource->setMonitorValue(uchar(value)); + CustomFeedbacksDialog cfDialog(m_doc, m_inputSource, this); + cfDialog.setMonitoringVisibility(m_supportMonitoring); + cfDialog.exec(); } void InputSelectionWidget::updateInputSource() @@ -222,48 +197,6 @@ void InputSelectionWidget::updateInputSource() { uniName = KInputNone; chName = KInputNone; - m_lowerSpin->setEnabled(false); - m_upperSpin->setEnabled(false); - m_monitorSpin->setEnabled(false); - m_customFbButton->setChecked(false); - m_feedbackGroup->setVisible(false); - } - else - { - m_lowerSpin->blockSignals(true); - m_upperSpin->blockSignals(true); - m_monitorSpin->blockSignals(true); - - uchar min = 0, max = UCHAR_MAX, mon = UCHAR_MAX; - - InputPatch *ip = m_doc->inputOutputMap()->inputPatch(m_inputSource->universe()); - if (ip != NULL && ip->profile() != NULL) - { - QLCInputChannel *ich = ip->profile()->channel(m_inputSource->channel()); - if (ich != NULL && ich->type() == QLCInputChannel::Button) - { - min = ich->lowerValue(); - max = ich->upperValue(); - } - } - m_lowerSpin->setValue((m_inputSource->lowerValue() != 0) ? m_inputSource->lowerValue() : min); - m_upperSpin->setValue((m_inputSource->upperValue() != UCHAR_MAX) ? m_inputSource->upperValue() : max); - m_monitorSpin->setValue((m_inputSource->monitorValue() != UCHAR_MAX) ? m_inputSource->monitorValue() : mon); - if (m_lowerSpin->value() != 0 || m_upperSpin->value() != UCHAR_MAX) - { - m_customFbButton->setChecked(true); - } - else - { - m_customFbButton->setChecked(false); - m_feedbackGroup->setVisible(false); - } - m_lowerSpin->blockSignals(false); - m_upperSpin->blockSignals(false); - m_monitorSpin->blockSignals(false); - m_lowerSpin->setEnabled(true); - m_upperSpin->setEnabled(true); - m_monitorSpin->setEnabled(true); } m_inputUniverseEdit->setText(uniName); diff --git a/ui/src/inputselectionwidget.h b/ui/src/inputselectionwidget.h index dc1e557ca9..02323925c2 100644 --- a/ui/src/inputselectionwidget.h +++ b/ui/src/inputselectionwidget.h @@ -33,12 +33,12 @@ class InputSelectionWidget : public QWidget, public Ui_InputSelectionWidget Q_OBJECT public: - InputSelectionWidget(Doc* doc, QWidget *parent = 0); + InputSelectionWidget(Doc *doc, QWidget *parent = 0); ~InputSelectionWidget(); void setKeyInputVisibility(bool visible); void setCustomFeedbackVisibility(bool visible); - void setMonitoringVisibility(bool visible); + void setMonitoringSupport(bool enable); void setTitle(QString title); void setWidgetPage(int page); bool isAutoDetecting(); @@ -59,10 +59,7 @@ protected slots: void slotInputValueChanged(quint32 universe, quint32 channel); void slotChooseInputClicked(); - void slotCustomFeedbackToggled(bool checked); - void slotLowerSpinValueChanged(int value); - void slotUpperSpinValueChanged(int value); - void slotMonitorSpinValueChanged(int value); + void slotCustomFeedbackClicked(); signals: void autoDetectToggled(bool checked); @@ -73,11 +70,12 @@ protected slots: void updateInputSource(); private: - Doc* m_doc; + Doc *m_doc; QKeySequence m_keySequence; QSharedPointer m_inputSource; int m_widgetPage; bool m_emitOdd; + bool m_supportMonitoring; quint32 m_signalsReceived; }; diff --git a/ui/src/inputselectionwidget.ui b/ui/src/inputselectionwidget.ui index 8d9e82a826..182bdb237d 100644 --- a/ui/src/inputselectionwidget.ui +++ b/ui/src/inputselectionwidget.ui @@ -26,7 +26,7 @@ 0 0 504 - 231 + 134 @@ -149,102 +149,7 @@ 4 - - - - Input Universe - - - - - - - The input universe that sends data to this widget - - - true - - - - - - - Custom feedback - - - - - - - 10 - - - - Lower Value - - - - - - - 255 - - - - - - - 0 - - - 255 - - - 255 - - - - - - - - 10 - - - - Upper Value - - - - - - - - 10 - - - - Monitor Value - - - - - - - 255 - - - - - m_upperSpin - m_lowerSpin - label_2 - label - m_monitorLabel - m_monitorSpin - - - + The particular input channel within the input universe that sends data to this widget @@ -261,16 +166,20 @@ - + + + + Input Universe + + + + - + Custom Feedback - - true - @@ -323,6 +232,16 @@ + + + + The input universe that sends data to this widget + + + true + + + diff --git a/ui/src/src.pro b/ui/src/src.pro index 8f4d9a5876..89dd41c7dc 100644 --- a/ui/src/src.pro +++ b/ui/src/src.pro @@ -68,6 +68,7 @@ HEADERS += aboutbox.h \ createfixturegroup.h \ ctkrangeslider.h \ cuestackmodel.h \ + customfeedbacksdialog.h \ dmxdumpfactory.h \ efxeditor.h \ efxpreviewarea.h \ @@ -248,6 +249,7 @@ SOURCES += aboutbox.cpp \ createfixturegroup.cpp \ ctkrangeslider.cpp \ cuestackmodel.cpp \ + customfeedbacksdialog.cpp \ dmxdumpfactory.cpp \ efxeditor.cpp \ efxpreviewarea.cpp \ diff --git a/ui/src/virtualconsole/vcbuttonproperties.cpp b/ui/src/virtualconsole/vcbuttonproperties.cpp index 22352efdaf..9e6ed9c3a8 100644 --- a/ui/src/virtualconsole/vcbuttonproperties.cpp +++ b/ui/src/virtualconsole/vcbuttonproperties.cpp @@ -49,7 +49,7 @@ VCButtonProperties::VCButtonProperties(VCButton* button, Doc* doc) m_inputSelWidget = new InputSelectionWidget(m_doc, this); m_inputSelWidget->setCustomFeedbackVisibility(true); - m_inputSelWidget->setMonitoringVisibility(true); + m_inputSelWidget->setMonitoringSupport(true); m_inputSelWidget->setKeySequence(m_button->keySequence()); m_inputSelWidget->setInputSource(m_button->inputSource()); m_inputSelWidget->setWidgetPage(m_button->page()); From 37ffd5b1bb9455966c7ac0b7baf71590d3a33c9f Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Sat, 10 Feb 2024 16:45:47 +0100 Subject: [PATCH 066/212] Update changelog and fixture map --- debian/changelog | 5 +++-- resources/fixtures/FixturesMap.xml | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/debian/changelog b/debian/changelog index ee39c64361..164f11848a 100644 --- a/debian/changelog +++ b/debian/changelog @@ -6,12 +6,13 @@ qlcplus (4.13.0) stable; urgency=low * 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) - * Virtual Console: add monitoring feedback value to custom feedbacks (thanks to ditcheshurt) + * UI: add color lookup table to input profiles and a dedicated dialog for custom feedbacks * 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 feedbacks (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 @@ -60,7 +61,7 @@ qlcplus (4.13.0) stable; urgency=low * 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 fixture: Talent SSL2 + * New fixtures: Talent SSL2, Cameo P2 FC -- Massimo Callegari Sun, 10 Mar 2024 12:13:14 +0200 diff --git a/resources/fixtures/FixturesMap.xml b/resources/fixtures/FixturesMap.xml index dbad519de7..45af518799 100644 --- a/resources/fixtures/FixturesMap.xml +++ b/resources/fixtures/FixturesMap.xml @@ -352,6 +352,7 @@ + From f1427cad37821936cc0e1a81e88bed4b9219e2b0 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Sat, 10 Feb 2024 16:55:25 +0100 Subject: [PATCH 067/212] Add missing ui to build files --- ui/src/CMakeLists.txt | 2 +- ui/src/src.pro | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/ui/src/CMakeLists.txt b/ui/src/CMakeLists.txt index b258c724f3..4f412b1ba0 100644 --- a/ui/src/CMakeLists.txt +++ b/ui/src/CMakeLists.txt @@ -60,7 +60,7 @@ add_library(${module_name} createfixturegroup.cpp createfixturegroup.h createfixturegroup.ui ctkrangeslider.cpp ctkrangeslider.h cuestackmodel.cpp cuestackmodel.h - customfeedbacksdialog.cpp customfeedbacksdialog.h + customfeedbacksdialog.cpp customfeedbacksdialog.h customfeedbacksdialog.ui dmxdumpfactory.cpp dmxdumpfactory.h dmxdumpfactory.ui efxeditor.cpp efxeditor.h efxeditor.ui efxpreviewarea.cpp efxpreviewarea.h diff --git a/ui/src/src.pro b/ui/src/src.pro index 89dd41c7dc..2f938b1cb0 100644 --- a/ui/src/src.pro +++ b/ui/src/src.pro @@ -187,6 +187,7 @@ FORMS += aboutbox.ui \ channelsselection.ui \ collectioneditor.ui \ createfixturegroup.ui \ + customfeedbacksdialog.ui \ dmxdumpfactory.ui \ efxeditor.ui \ fixturegroupeditor.ui \ From 8601d3fed5f57aa1d907e59b75a9fdab60d930a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20M=C3=BCllner?= Date: Mon, 12 Feb 2024 21:49:13 +0100 Subject: [PATCH 068/212] inputprofiles: Adding Worlde Easypad.12 MIDI pad MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Worlde Easypad.12 is a relatively cheap MIDI pad, that can be used as MIDI input. All controls that standard MIDI messages are supported here. However, there are a few missing features: * The slider sends SYSEX commands * The bank button sends SYSEX commands * Unclear how to set LED color Using the feedback mechanism did not change anything Signed-off-by: Christoph Müllner --- .../html_en_EN/supported-input-devices.html | 10 ++ resources/inputprofiles/Worlde-Easypad.12.qxi | 92 +++++++++++++++++++ 2 files changed, 102 insertions(+) create mode 100644 resources/inputprofiles/Worlde-Easypad.12.qxi diff --git a/resources/docs/html_en_EN/supported-input-devices.html b/resources/docs/html_en_EN/supported-input-devices.html index d514f394d4..c8eb035b88 100644 --- a/resources/docs/html_en_EN/supported-input-devices.html +++ b/resources/docs/html_en_EN/supported-input-devices.html @@ -336,6 +336,16 @@

    Supported Input Devices

    + + + + + + + + + + diff --git a/resources/inputprofiles/Worlde-Easypad.12.qxi b/resources/inputprofiles/Worlde-Easypad.12.qxi new file mode 100644 index 0000000000..63f75f1883 --- /dev/null +++ b/resources/inputprofiles/Worlde-Easypad.12.qxi @@ -0,0 +1,92 @@ + + + + + Q Light Controller Plus + 4.13.0 GIT + Christoph Müllner + + Worlde + Easypad.12 + MIDI + + Record + Button + + + Play + Button + + + Stop + Button + + + Back + Button + + + Next + Button + + + Refresh + Button + + + Left + Button + + + Right + Button + + + Pad 7 + Button + + + Pad 8 + Button + + + Pad 1 + Button + + + Pad 9 + Button + + + Pad 10 + Button + + + Pad 4 + Button + + + Pad 11 + Button + + + Pad 3 + Button + + + Pad 12 + Button + + + Pad 2 + Button + + + Pad 6 + Button + + + Pad 5 + Button + + From ff731ee77b424e39ca7d8ae9b3479098782654ea Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Fri, 16 Feb 2024 00:45:03 +0100 Subject: [PATCH 069/212] vc/audiotriggers: fix enable button feedback to external device --- debian/changelog | 4 +++- ui/src/virtualconsole/vcaudiotriggers.cpp | 13 +++++++++++++ ui/src/virtualconsole/vcaudiotriggers.h | 2 +- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/debian/changelog b/debian/changelog index 164f11848a..a832e5f81e 100644 --- a/debian/changelog +++ b/debian/changelog @@ -16,6 +16,7 @@ qlcplus (4.13.0) stable; urgency=low * 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 @@ -24,7 +25,8 @@ qlcplus (4.13.0) stable; urgency=low * Web Access: add support for Cue List side fader and buttons layout (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 VC Animation widget support (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 * New fixture: Ibiza Mini Moving Star Wash (thanks to Chris Shucksmith) diff --git a/ui/src/virtualconsole/vcaudiotriggers.cpp b/ui/src/virtualconsole/vcaudiotriggers.cpp index 8372727080..0d8e6af0b9 100644 --- a/ui/src/virtualconsole/vcaudiotriggers.cpp +++ b/ui/src/virtualconsole/vcaudiotriggers.cpp @@ -224,6 +224,7 @@ void VCAudioTriggers::slotEnableButtonToggled(bool toggle) return; enableCapture(toggle); + updateFeedback(); } void VCAudioTriggers::slotDisplaySpectrum(double *spectrumBands, int size, @@ -362,6 +363,18 @@ void VCAudioTriggers::slotKeyPressed(const QKeySequence& keySequence) } } +void VCAudioTriggers::updateFeedback() +{ + QSharedPointer src = inputSource(); + if (!src.isNull() && src->isValid() == true) + { + if (m_button->isChecked()) + sendFeedback(src->upperValue()); + else + sendFeedback(src->lowerValue()); + } +} + void VCAudioTriggers::slotInputValueChanged(quint32 universe, quint32 channel, uchar value) { /* Don't let input data through in design mode or if disabled */ diff --git a/ui/src/virtualconsole/vcaudiotriggers.h b/ui/src/virtualconsole/vcaudiotriggers.h index 0284a0f87c..492116bdb8 100644 --- a/ui/src/virtualconsole/vcaudiotriggers.h +++ b/ui/src/virtualconsole/vcaudiotriggers.h @@ -124,7 +124,7 @@ protected slots: * External Input *************************************************************************/ public: - void updateFeedback() { } + void updateFeedback(); protected slots: void slotInputValueChanged(quint32 universe, quint32 channel, uchar value); From 78b0a8c4922702b45713bb1e47c887c8c8e7c469 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Fri, 16 Feb 2024 13:08:23 +0100 Subject: [PATCH 070/212] plugins: improve feedback method with a QVariant parameter This allows to send any type of data for plugin-specific features --- engine/src/inputoutputmap.cpp | 4 ++-- engine/src/inputoutputmap.h | 2 +- plugins/dummy/dummyplugin.cpp | 4 ++-- plugins/dummy/dummyplugin.h | 2 +- plugins/enttecwing/src/enttecwing.cpp | 2 +- plugins/enttecwing/src/enttecwing.h | 2 +- plugins/interfaces/qlcioplugin.cpp | 4 ++-- plugins/interfaces/qlcioplugin.h | 2 +- plugins/loopback/src/loopback.cpp | 2 +- plugins/loopback/src/loopback.h | 2 +- plugins/midi/src/common/midiplugin.cpp | 8 ++++++-- plugins/midi/src/common/midiplugin.h | 2 +- plugins/os2l/os2lplugin.cpp | 17 ----------------- plugins/os2l/os2lplugin.h | 3 --- plugins/osc/oscplugin.cpp | 4 ++-- plugins/osc/oscplugin.h | 2 +- plugins/uart/uartplugin.cpp | 17 ----------------- plugins/uart/uartplugin.h | 3 --- 18 files changed, 23 insertions(+), 59 deletions(-) diff --git a/engine/src/inputoutputmap.cpp b/engine/src/inputoutputmap.cpp index 1a5d2acb0b..39367b8a36 100644 --- a/engine/src/inputoutputmap.cpp +++ b/engine/src/inputoutputmap.cpp @@ -750,7 +750,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; @@ -759,7 +759,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 diff --git a/engine/src/inputoutputmap.h b/engine/src/inputoutputmap.h index 9bcca578e6..2fa0b4c153 100644 --- a/engine/src/inputoutputmap.h +++ b/engine/src/inputoutputmap.h @@ -502,7 +502,7 @@ class InputOutputMap : public QObject * Send feedback value to the input profile e.g. to move a motorized * sliders & knobs, set indicator leds etc. */ - bool sendFeedBack(quint32 universe, quint32 channel, uchar value, const QString& key = 0); + bool sendFeedBack(quint32 universe, quint32 channel, uchar value, const QVariant ¶ms); private: /** In case of duplicate strings, append a number to make them unique */ diff --git a/plugins/dummy/dummyplugin.cpp b/plugins/dummy/dummyplugin.cpp index 97bacc3a26..bf64e6a3dc 100644 --- a/plugins/dummy/dummyplugin.cpp +++ b/plugins/dummy/dummyplugin.cpp @@ -200,13 +200,13 @@ QString DummyPlugin::inputInfo(quint32 input) return str; } -void DummyPlugin::sendFeedBack(quint32 universe, quint32 output, quint32 channel, uchar value, const QString &key) +void DummyPlugin::sendFeedBack(quint32 universe, quint32 output, quint32 channel, uchar value, const QVariant ¶ms) { Q_UNUSED(universe) Q_UNUSED(output) Q_UNUSED(channel) Q_UNUSED(value) - Q_UNUSED(key) + Q_UNUSED(params) /** * If the device support this feature, this is the method to send data back for diff --git a/plugins/dummy/dummyplugin.h b/plugins/dummy/dummyplugin.h index 0811dfec3f..7a5e7f558a 100644 --- a/plugins/dummy/dummyplugin.h +++ b/plugins/dummy/dummyplugin.h @@ -85,7 +85,7 @@ class DummyPlugin : public QLCIOPlugin QString inputInfo(quint32 input); /** @reimp */ - void sendFeedBack(quint32 universe, quint32 output, quint32 channel, uchar value, const QString& key); + void sendFeedBack(quint32 universe, quint32 output, quint32 channel, uchar value, const QVariant ¶ms); protected: /** Place here the variables used by this plugin */ diff --git a/plugins/enttecwing/src/enttecwing.cpp b/plugins/enttecwing/src/enttecwing.cpp index de77ac6e87..7d81253b50 100644 --- a/plugins/enttecwing/src/enttecwing.cpp +++ b/plugins/enttecwing/src/enttecwing.cpp @@ -169,7 +169,7 @@ QString EnttecWing::inputInfo(quint32 input) } void EnttecWing::sendFeedBack(quint32 universe, quint32 input, - quint32 channel, uchar value, const QString &) + quint32 channel, uchar value, const QVariant &) { Q_UNUSED(universe) diff --git a/plugins/enttecwing/src/enttecwing.h b/plugins/enttecwing/src/enttecwing.h index 4f497016ca..34baf921e0 100644 --- a/plugins/enttecwing/src/enttecwing.h +++ b/plugins/enttecwing/src/enttecwing.h @@ -78,7 +78,7 @@ class EnttecWing : public QLCIOPlugin QString inputInfo(quint32 input); /** @reimp */ - void sendFeedBack(quint32 universe, quint32 input, quint32 channel, uchar value, const QString& key); + void sendFeedBack(quint32 universe, quint32 input, quint32 channel, uchar value, const QVariant ¶ms); /************************************************************************* * Outputs diff --git a/plugins/interfaces/qlcioplugin.cpp b/plugins/interfaces/qlcioplugin.cpp index 03d2e6747b..2faabdd58f 100644 --- a/plugins/interfaces/qlcioplugin.cpp +++ b/plugins/interfaces/qlcioplugin.cpp @@ -86,13 +86,13 @@ QString QLCIOPlugin::inputInfo(quint32 input) } void QLCIOPlugin::sendFeedBack(quint32 universe, quint32 inputLine, - quint32 channel, uchar value, const QString &key) + quint32 channel, uchar value, const QVariant ¶ms) { Q_UNUSED(universe) Q_UNUSED(inputLine) Q_UNUSED(channel) Q_UNUSED(value) - Q_UNUSED(key) + Q_UNUSED(params) } /************************************************************************* diff --git a/plugins/interfaces/qlcioplugin.h b/plugins/interfaces/qlcioplugin.h index cb36f97cc6..72588f64f9 100644 --- a/plugins/interfaces/qlcioplugin.h +++ b/plugins/interfaces/qlcioplugin.h @@ -264,7 +264,7 @@ class QLCIOPlugin : public QObject * @param key a string to identify a channel by name (ATM used only by OSC) */ virtual void sendFeedBack(quint32 universe, quint32 inputLine, - quint32 channel, uchar value, const QString& key = 0); + quint32 channel, uchar value, const QVariant ¶ms); signals: /** diff --git a/plugins/loopback/src/loopback.cpp b/plugins/loopback/src/loopback.cpp index 37e3335791..47b417a337 100644 --- a/plugins/loopback/src/loopback.cpp +++ b/plugins/loopback/src/loopback.cpp @@ -206,7 +206,7 @@ void Loopback::writeUniverse(quint32 universe, quint32 output, const QByteArray } } -void Loopback::sendFeedBack(quint32 universe, quint32 input, quint32 channel, uchar value, const QString &) +void Loopback::sendFeedBack(quint32 universe, quint32 input, quint32 channel, uchar value, const QVariant &) { if (!m_inputMap.contains(input)) return; diff --git a/plugins/loopback/src/loopback.h b/plugins/loopback/src/loopback.h index 546e0bb323..cc3979e875 100644 --- a/plugins/loopback/src/loopback.h +++ b/plugins/loopback/src/loopback.h @@ -86,7 +86,7 @@ class QLC_DECLSPEC Loopback : public QLCIOPlugin QString inputInfo(quint32 input); /** @reimp */ - void sendFeedBack(quint32 universe, quint32 input, quint32 channel, uchar value, const QString& key); + void sendFeedBack(quint32 universe, quint32 input, quint32 channel, uchar value, const QVariant ¶ms); private: //! loopback line -> channel data diff --git a/plugins/midi/src/common/midiplugin.cpp b/plugins/midi/src/common/midiplugin.cpp index 4536cfbc4c..8d04c9ba8b 100644 --- a/plugins/midi/src/common/midiplugin.cpp +++ b/plugins/midi/src/common/midiplugin.cpp @@ -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; diff --git a/plugins/midi/src/common/midiplugin.h b/plugins/midi/src/common/midiplugin.h index cb00cc2415..b9764d53c8 100644 --- a/plugins/midi/src/common/midiplugin.h +++ b/plugins/midi/src/common/midiplugin.h @@ -110,7 +110,7 @@ class MidiPlugin : public QLCIOPlugin QString inputInfo(quint32 input); /** @reimp */ - void sendFeedBack(quint32 universe, quint32 output, quint32 channel, uchar value, const QString& key); + void sendFeedBack(quint32 universe, quint32 output, quint32 channel, uchar value, const QVariant ¶ms); void sendSysEx(quint32 output, const QByteArray &data); diff --git a/plugins/os2l/os2lplugin.cpp b/plugins/os2l/os2lplugin.cpp index 6b65af1274..75850d3f9b 100644 --- a/plugins/os2l/os2lplugin.cpp +++ b/plugins/os2l/os2lplugin.cpp @@ -127,23 +127,6 @@ QString OS2LPlugin::inputInfo(quint32 input) return str; } -void OS2LPlugin::sendFeedBack(quint32 universe, quint32 output, quint32 channel, uchar value, const QString &key) -{ - Q_UNUSED(universe) - Q_UNUSED(output) - Q_UNUSED(channel) - Q_UNUSED(value) - Q_UNUSED(key) - - /** - * If the device support this feature, this is the method to send data back for - * visual feedback. - * To implement such method, the plugin must have an input line corresponding - * to the specified output line. - * Basically feedback data must return to the same line where it came from - */ -} - quint32 OS2LPlugin::universe() const { return m_inputUniverse; diff --git a/plugins/os2l/os2lplugin.h b/plugins/os2l/os2lplugin.h index 99ec246584..5c1ec5457c 100644 --- a/plugins/os2l/os2lplugin.h +++ b/plugins/os2l/os2lplugin.h @@ -70,9 +70,6 @@ class OS2LPlugin : public QLCIOPlugin /** @reimp */ QString inputInfo(quint32 input); - /** @reimp */ - void sendFeedBack(quint32 universe, quint32 output, quint32 channel, uchar value, const QString& key); - quint32 universe() const; protected: diff --git a/plugins/osc/oscplugin.cpp b/plugins/osc/oscplugin.cpp index e807898b80..d3ce296b92 100644 --- a/plugins/osc/oscplugin.cpp +++ b/plugins/osc/oscplugin.cpp @@ -293,14 +293,14 @@ QString OSCPlugin::inputInfo(quint32 input) } void OSCPlugin::sendFeedBack(quint32 universe, quint32 input, - quint32 channel, uchar value, const QString &key) + quint32 channel, uchar value, const QVariant ¶ms) { if (input >= (quint32)m_IOmapping.count()) return; OSCController *controller = m_IOmapping[input].controller; if (controller != NULL) - controller->sendFeedback(universe, channel, value, key); + controller->sendFeedback(universe, channel, value, params.toString()); } /********************************************************************* diff --git a/plugins/osc/oscplugin.h b/plugins/osc/oscplugin.h index 87f0db73b0..6050db1c1c 100644 --- a/plugins/osc/oscplugin.h +++ b/plugins/osc/oscplugin.h @@ -108,7 +108,7 @@ class OSCPlugin : public QLCIOPlugin QString inputInfo(quint32 input); /** @reimp */ - void sendFeedBack(quint32 universe, quint32 input, quint32 channel, uchar value, const QString& key); + void sendFeedBack(quint32 universe, quint32 input, quint32 channel, uchar value, const QVariant ¶ms); /********************************************************************* * Configuration diff --git a/plugins/uart/uartplugin.cpp b/plugins/uart/uartplugin.cpp index 7b4643f49f..d6fbd8918b 100644 --- a/plugins/uart/uartplugin.cpp +++ b/plugins/uart/uartplugin.cpp @@ -185,20 +185,3 @@ QString UARTPlugin::inputInfo(quint32 input) return str; } - -void UARTPlugin::sendFeedBack(quint32 universe, quint32 output, quint32 channel, uchar value, const QString &key) -{ - Q_UNUSED(universe) - Q_UNUSED(output) - Q_UNUSED(channel) - Q_UNUSED(value) - Q_UNUSED(key) - - /** - * If the device support this feature, this is the method to send data back for - * visual feedback. - * To implement such method, the plugin must have an input line corresponding - * to the specified output line. - * Basically feedback data must return to the same line where it came from - */ -} diff --git a/plugins/uart/uartplugin.h b/plugins/uart/uartplugin.h index 02d6892bd3..1db50cc366 100644 --- a/plugins/uart/uartplugin.h +++ b/plugins/uart/uartplugin.h @@ -85,9 +85,6 @@ class UARTPlugin : public QLCIOPlugin /** @reimp */ QString inputInfo(quint32 input); - - /** @reimp */ - void sendFeedBack(quint32 universe, quint32 output, quint32 channel, uchar value, const QString& key); }; #endif From 6ea3a84438613eeb6a2f470597c59446e9bb079d Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Fri, 16 Feb 2024 13:21:00 +0100 Subject: [PATCH 071/212] engine: add midi channel to input source, channel and profile --- engine/src/qlcinputchannel.cpp | 31 +++++++-- engine/src/qlcinputchannel.h | 44 +++++++----- engine/src/qlcinputprofile.cpp | 122 +++++++++++++++++++++++++++++++-- engine/src/qlcinputprofile.h | 27 +++++++- engine/src/qlcinputsource.cpp | 36 +++++++--- engine/src/qlcinputsource.h | 14 +++- 6 files changed, 231 insertions(+), 43 deletions(-) diff --git a/engine/src/qlcinputchannel.cpp b/engine/src/qlcinputchannel.cpp index e4b8ee6f3c..7dca1a2c94 100644 --- a/engine/src/qlcinputchannel.cpp +++ b/engine/src/qlcinputchannel.cpp @@ -36,6 +36,7 @@ QLCInputChannel::QLCInputChannel() , m_sendExtraPress(false) , m_lower(0) , m_upper(UCHAR_MAX) + , m_midiChannel(-1) { } @@ -47,6 +48,7 @@ QLCInputChannel *QLCInputChannel::createCopy() copy->setMovementType(this->movementType()); copy->setMovementSensitivity(this->movementSensitivity()); copy->setSendExtraPress(this->sendExtraPress()); + copy->setMidiChannel(this->midiChannel()); copy->setRange(this->lowerValue(), this->upperValue()); return copy; @@ -270,6 +272,20 @@ void QLCInputChannel::setUpperValue(const uchar value) emit upperValueChanged(); } +int QLCInputChannel::midiChannel() const +{ + return m_midiChannel; +} + +void QLCInputChannel::setMidiChannel(const int channel) +{ + if (channel == m_midiChannel) + return; + + m_midiChannel = channel; + emit midiChannelChanged(); +} + /**************************************************************************** * Load & Save ****************************************************************************/ @@ -307,14 +323,19 @@ bool QLCInputChannel::loadXML(QXmlStreamReader &root) } else if (root.name() == KXMLQLCInputChannelFeedbacks) { + 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); + setMidiChannel(fbChannel); root.skipCurrentElement(); } else @@ -362,6 +383,8 @@ bool QLCInputChannel::saveXML(QXmlStreamWriter *doc, quint32 channelNumber) cons doc->writeAttribute(KXMLQLCInputChannelLowerValue, QString::number(lowerValue())); if (upperValue() != UCHAR_MAX) doc->writeAttribute(KXMLQLCInputChannelUpperValue, QString::number(upperValue())); + if (midiChannel() != -1) + doc->writeAttribute(KXMLQLCInputChannelMidiChannel, QString::number(midiChannel())); doc->writeEndElement(); } diff --git a/engine/src/qlcinputchannel.h b/engine/src/qlcinputchannel.h index 27edabae82..92060e1b97 100644 --- a/engine/src/qlcinputchannel.h +++ b/engine/src/qlcinputchannel.h @@ -32,25 +32,26 @@ class QString; * @{ */ -#define KXMLQLCInputChannel QString("Channel") -#define KXMLQLCInputChannelName QString("Name") -#define KXMLQLCInputChannelType QString("Type") -#define KXMLQLCInputChannelNumber QString("Number") -#define KXMLQLCInputChannelSlider QString("Slider") -#define KXMLQLCInputChannelKnob QString("Knob") -#define KXMLQLCInputChannelEncoder QString("Encoder") -#define KXMLQLCInputChannelButton QString("Button") -#define KXMLQLCInputChannelPageUp QString("Next Page") -#define KXMLQLCInputChannelPageDown QString("Previous Page") -#define KXMLQLCInputChannelPageSet QString("Page Set") -#define KXMLQLCInputChannelNone QString("None") -#define KXMLQLCInputChannelMovement QString("Movement") -#define KXMLQLCInputChannelRelative QString("Relative") -#define KXMLQLCInputChannelSensitivity QString("Sensitivity") -#define KXMLQLCInputChannelExtraPress QString("ExtraPress") -#define KXMLQLCInputChannelFeedbacks QString("Feedbacks") -#define KXMLQLCInputChannelLowerValue QString("LowerValue") -#define KXMLQLCInputChannelUpperValue QString("UpperValue") +#define KXMLQLCInputChannel QString("Channel") +#define KXMLQLCInputChannelName QString("Name") +#define KXMLQLCInputChannelType QString("Type") +#define KXMLQLCInputChannelNumber QString("Number") +#define KXMLQLCInputChannelSlider QString("Slider") +#define KXMLQLCInputChannelKnob QString("Knob") +#define KXMLQLCInputChannelEncoder QString("Encoder") +#define KXMLQLCInputChannelButton QString("Button") +#define KXMLQLCInputChannelPageUp QString("Next Page") +#define KXMLQLCInputChannelPageDown QString("Previous Page") +#define KXMLQLCInputChannelPageSet QString("Page Set") +#define KXMLQLCInputChannelNone QString("None") +#define KXMLQLCInputChannelMovement QString("Movement") +#define KXMLQLCInputChannelRelative QString("Relative") +#define KXMLQLCInputChannelSensitivity QString("Sensitivity") +#define KXMLQLCInputChannelExtraPress QString("ExtraPress") +#define KXMLQLCInputChannelFeedbacks QString("Feedbacks") +#define KXMLQLCInputChannelLowerValue QString("LowerValue") +#define KXMLQLCInputChannelUpperValue QString("UpperValue") +#define KXMLQLCInputChannelMidiChannel QString("MidiChannel") class QLCInputChannel : public QObject { @@ -190,14 +191,19 @@ class QLCInputChannel : public QObject uchar upperValue() const; void setUpperValue(const uchar value); + int midiChannel() const; + void setMidiChannel(const int channel); + signals: void sendExtraPressChanged(); void lowerValueChanged(); void upperValueChanged(); + void midiChannelChanged(); protected: bool m_sendExtraPress; uchar m_lower, m_upper; + int m_midiChannel; /******************************************************************** * Load & Save diff --git a/engine/src/qlcinputprofile.cpp b/engine/src/qlcinputprofile.cpp index af122f77e1..16d5e549e8 100644 --- a/engine/src/qlcinputprofile.cpp +++ b/engine/src/qlcinputprofile.cpp @@ -35,8 +35,8 @@ #define KXMLQLCInputProfileTypeDmx "DMX" #define KXMLQLCInputProfileTypeEnttec "Enttec" -#define KXMLQLCInputProfileColorValue "Value" -#define KXMLQLCInputProfileColorLabel "Label" +#define KXMLQLCInputProfileValue "Value" +#define KXMLQLCInputProfileLabel "Label" #define KXMLQLCInputProfileColorRGB "RGB" /**************************************************************************** @@ -83,6 +83,14 @@ QLCInputProfile *QLCInputProfile::createCopy() 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; } @@ -117,6 +125,14 @@ QLCInputProfile& QLCInputProfile::operator=(const QLCInputProfile& profile) 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; @@ -321,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->midiChannel(); + default: return QVariant(); + } +} + void QLCInputProfile::destroyChannels() { /* Delete existing channels but leave the pointers there */ @@ -355,6 +384,30 @@ 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 ****************************************************************************/ @@ -404,8 +457,8 @@ bool QLCInputProfile::loadColorTableXML(QXmlStreamReader &tableRoot) if (tableRoot.name() == KXMLQLCInputProfileColor) { /* get value & color */ - uchar value = tableRoot.attributes().value(KXMLQLCInputProfileColorValue).toInt(); - QString label = tableRoot.attributes().value(KXMLQLCInputProfileColorLabel).toString(); + 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); } @@ -419,6 +472,35 @@ bool QLCInputProfile::loadColorTableXML(QXmlStreamReader &tableRoot) 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) @@ -471,6 +553,15 @@ bool QLCInputProfile::loadXML(QXmlStreamReader& doc) { 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; @@ -511,7 +602,7 @@ bool QLCInputProfile::saveXML(const QString& fileName) it.value()->saveXML(&doc, it.key()); } - if (!m_colorTable.empty()) + if (hasColorTable()) { doc.writeStartElement(KXMLQLCInputProfileColorTable); @@ -521,8 +612,8 @@ bool QLCInputProfile::saveXML(const QString& fileName) it.next(); QPair lc = it.value(); doc.writeStartElement(KXMLQLCInputProfileColor); - doc.writeAttribute(KXMLQLCInputProfileColorValue, QString::number(it.key())); - doc.writeAttribute(KXMLQLCInputProfileColorLabel, lc.first); + doc.writeAttribute(KXMLQLCInputProfileValue, QString::number(it.key())); + doc.writeAttribute(KXMLQLCInputProfileLabel, lc.first); doc.writeAttribute(KXMLQLCInputProfileColorRGB, lc.second.name()); doc.writeEndElement(); } @@ -530,6 +621,23 @@ bool QLCInputProfile::saveXML(const QString& fileName) 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 */ diff --git a/engine/src/qlcinputprofile.h b/engine/src/qlcinputprofile.h index f5ec6cd0c9..23c00867b2 100644 --- a/engine/src/qlcinputprofile.h +++ b/engine/src/qlcinputprofile.h @@ -42,6 +42,8 @@ class QXmlStreamReader; #define KXMLQLCInputProfileMidiSendNoteOff QString("MIDISendNoteOff") #define KXMLQLCInputProfileColorTable QString("ColorTable") #define KXMLQLCInputProfileColor QString("Color") +#define KXMLQLCInputProfileMidiChannelTable QString("MidiChannelTable") +#define KXMLQLCInputProfileMidiChannel QString("Channel") class QLCInputProfile : public QObject { @@ -167,7 +169,7 @@ class QLCInputProfile : public QObject * @param channel The number of the channel to get. * @return A QLCInputChannel* or NULL if not found. */ - QLCInputChannel* channel(quint32 channel) const; + QLCInputChannel *channel(quint32 channel) const; /** * Get the channel number for the given input channel. @@ -182,6 +184,12 @@ class QLCInputProfile : public QObject */ QMap channels() const; + /** + * Retrieve additional parameters to be passed to plugins + * when sending feedbacks. + */ + QVariant channelExtraParams(const QLCInputChannel *channel) const; + private: /** Delete and remove all channels */ void destroyChannels(); @@ -204,6 +212,19 @@ class QLCInputProfile : public QObject 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 ********************************************************************/ @@ -214,8 +235,12 @@ class QLCInputProfile : public QObject /** Save an input profile into a given file name */ bool saveXML(const QString& fileName); + /** Load an optional color table for RGB LED feedbacks */ 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 9a626c6a18..6fa1424d27 100644 --- a/engine/src/qlcinputsource.cpp +++ b/engine/src/qlcinputsource.cpp @@ -37,7 +37,8 @@ QLCInputSource::QLCInputSource(QThread *parent) , m_channel(invalidChannel) , m_id(invalidID) , m_lower(0) - , m_upper(255) + , m_upper(UCHAR_MAX) + , m_monitor(UCHAR_MAX) , m_workingMode(Absolute) , m_sensitivity(20) , m_emitExtraPressRelease(false) @@ -52,7 +53,8 @@ QLCInputSource::QLCInputSource(quint32 universe, quint32 channel, QThread *paren , m_universe(universe) , m_channel(channel) , m_lower(0) - , m_upper(255) + , m_upper(UCHAR_MAX) + , m_monitor(UCHAR_MAX) , m_workingMode(Absolute) , m_sensitivity(20) , m_emitExtraPressRelease(false) @@ -120,30 +122,44 @@ quint32 QLCInputSource::id() const return m_id; } +/********************************************************************* + * Custom feedback + *********************************************************************/ + +uchar QLCInputSource::lowerValue() const +{ + return m_lower; +} + +uchar QLCInputSource::upperValue() const +{ + return m_upper; +} + void QLCInputSource::setRange(uchar lower, uchar upper) { m_lower = lower; m_upper = upper; } -void QLCInputSource::setMonitorValue(uchar monitor) +uchar QLCInputSource::monitorValue() const { - m_monitor = monitor; + return m_monitor; } -uchar QLCInputSource::lowerValue() const +void QLCInputSource::setMonitorValue(uchar monitor) { - return m_lower; + m_monitor = monitor; } -uchar QLCInputSource::upperValue() const +QVariant QLCInputSource::extraParams() const { - return m_upper; + return m_extraParams; } -uchar QLCInputSource::monitorValue() const +void QLCInputSource::setExtraParams(QVariant params) { - return m_monitor; + m_extraParams = params; } /********************************************************************* diff --git a/engine/src/qlcinputsource.h b/engine/src/qlcinputsource.h index c8d0bab6b1..0304dd4429 100644 --- a/engine/src/qlcinputsource.h +++ b/engine/src/qlcinputsource.h @@ -20,6 +20,7 @@ #ifndef QLCINPUTSOURCE_H #define QLCINPUTSOURCE_H +#include #include #include @@ -76,14 +77,23 @@ class QLCInputSource: public QThread * Custom feedback *********************************************************************/ public: - void setRange(uchar lower, uchar upper); uchar lowerValue() const; uchar upperValue() const; - void setMonitorValue(uchar monitor); + void setRange(uchar lower, uchar upper); + uchar monitorValue() const; + void setMonitorValue(uchar monitor); + + /** Get/set specific plugins params. + * OSC: a string with the command path + * MIDI: a channel modifier + */ + QVariant extraParams() const; + void setExtraParams(QVariant params); protected: uchar m_lower, m_upper, m_monitor; + QVariant m_extraParams; /********************************************************************* * Working mode From c1602d22b10c3c99cca480b1c0b4c8c967a4f3b9 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Fri, 16 Feb 2024 13:23:05 +0100 Subject: [PATCH 072/212] ui: handle custom midi channel when sending feedbacks --- ui/src/customfeedbacksdialog.cpp | 17 +++++ ui/src/customfeedbacksdialog.ui | 94 ++++++++++++++++--------- ui/src/inputprofileeditor.cpp | 87 ++++++++++++++++++++++- ui/src/inputprofileeditor.h | 5 ++ ui/src/inputprofileeditor.ui | 106 ++++++++++++++++++++++++++--- ui/src/virtualconsole/vcwidget.cpp | 34 ++++----- ui/src/virtualconsole/vcwidget.h | 15 ++-- 7 files changed, 289 insertions(+), 69 deletions(-) diff --git a/ui/src/customfeedbacksdialog.cpp b/ui/src/customfeedbacksdialog.cpp index 90a9f1ff23..f3ce325a63 100644 --- a/ui/src/customfeedbacksdialog.cpp +++ b/ui/src/customfeedbacksdialog.cpp @@ -46,6 +46,7 @@ CustomFeedbacksDialog::CustomFeedbacksDialog(Doc *doc, const QSharedPointersetVisible(false); m_monitorSpin->setVisible(false); m_profileColorsTree->setVisible(false); + m_midiChannelGroup->hide(); if (enableControls) { @@ -82,6 +83,20 @@ CustomFeedbacksDialog::CustomFeedbacksDialog(Doc *doc, const QSharedPointersetItemWidget(item, 2, colLabel); } } + if (m_profile->type() == QLCInputProfile::MIDI && m_profile->hasMidiChannelTable()) + { + m_midiChannelGroup->show(); + m_midiChannelCombo->addItem(tr("From plugin settings")); + + QMapIterator it(m_profile->midiChannelTable()); + while (it.hasNext() == true) + { + it.next(); + m_midiChannelCombo->addItem(it.value()); + } + if (m_inputSource->extraParams().isValid()) + m_midiChannelCombo->setCurrentIndex(m_inputSource->extraParams().toInt() + 1); + } } } @@ -114,6 +129,8 @@ void CustomFeedbacksDialog::accept() m_inputSource->setRange(m_lowerSpin->value(), m_upperSpin->value()); if (m_monitorSpin->isVisible()) m_inputSource->setMonitorValue(m_monitorSpin->value()); + if (m_midiChannelGroup->isVisible()) + m_inputSource->setExtraParams(m_midiChannelCombo->currentIndex() - 1); QDialog::accept(); } diff --git a/ui/src/customfeedbacksdialog.ui b/ui/src/customfeedbacksdialog.ui index cdb732ef49..6dd1ecf5ff 100644 --- a/ui/src/customfeedbacksdialog.ui +++ b/ui/src/customfeedbacksdialog.ui @@ -14,18 +14,24 @@ Custom Feedbacks Configuration - - - - Qt::Vertical - - - - 20 - 40 - - - + + + + + Value + + + + + Label + + + + + Color + + + @@ -135,26 +141,20 @@ - - - - - Value - - - - - Label - - - - - Color - - - + + + + Qt::Vertical + + + + 20 + 40 + + + - + Qt::Horizontal @@ -164,6 +164,38 @@ + + + + MIDI Channel + + + + + + + 0 + 0 + + + + Channel + + + + + + + + 0 + 0 + + + + + + + diff --git a/ui/src/inputprofileeditor.cpp b/ui/src/inputprofileeditor.cpp index 980e225eb9..b3d3011992 100644 --- a/ui/src/inputprofileeditor.cpp +++ b/ui/src/inputprofileeditor.cpp @@ -66,6 +66,7 @@ InputProfileEditor::InputProfileEditor(QWidget* parent, QLCInputProfile* profile setupUi(this); m_midiGroupSettings->setVisible(false); + connect(m_typeCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(slotTypeComboChanged(int))); @@ -92,12 +93,19 @@ InputProfileEditor::InputProfileEditor(QWidget* parent, QLCInputProfile* profile this, SLOT(slotLowerValueSpinChanged(int))); connect(m_upperSpin, SIGNAL(valueChanged(int)), this, SLOT(slotUpperValueSpinChanged(int))); + connect(m_midiChannelCombo, SIGNAL(currentIndexChanged(int)), + this, SLOT(slotMidiChannelComboChanged(int))); connect(m_addColorButton, SIGNAL(clicked()), this, SLOT(slotAddColor())); connect(m_removeColorButton, SIGNAL(clicked()), this, SLOT(slotRemoveColor())); + connect(m_addMidiChannelButton, SIGNAL(clicked()), + this, SLOT(slotAddMidiChannel())); + connect(m_removeMidiChannelButton, SIGNAL(clicked()), + this, SLOT(slotRemoveMidiChannel())); + /* Listen to input data */ connect(m_ioMap, SIGNAL(inputValueChanged(quint32, quint32, uchar, const QString&)), this, SLOT(slotInputValueChanged(quint32, quint32, uchar, const QString&))); @@ -150,6 +158,9 @@ InputProfileEditor::InputProfileEditor(QWidget* parent, QLCInputProfile* profile /* Fill up the tree with color table */ updateColorsTree(); + /* Fill up the tree with MIDI channel table */ + updateMidiChannelTree(); + /* Timer that clears the input data icon after a while */ m_timer = new QTimer(this); m_timer->setSingleShot(true); @@ -203,6 +214,35 @@ void InputProfileEditor::updateColorsTree() } } +void InputProfileEditor::updateMidiChannelTree() +{ + m_midiChannelsTree->clear(); + m_midiChannelCombo->clear(); + + if (m_profile->hasMidiChannelTable()) + { + m_midiChannelCombo->show(); + m_midiChannelLabel->show(); + m_midiChannelCombo->addItem(tr("From plugin settings")); + } + else + { + m_midiChannelCombo->hide(); + m_midiChannelLabel->hide(); + } + + QMapIterator it(m_profile->midiChannelTable()); + while (it.hasNext() == true) + { + it.next(); + QTreeWidgetItem *item = new QTreeWidgetItem(m_midiChannelsTree); + item->setText(0, QString::number(it.key() + 1)); + item->setText(1, it.value()); + + m_midiChannelCombo->addItem(it.value()); + } +} + void InputProfileEditor::updateChannelItem(QTreeWidgetItem *item, QLCInputChannel *ch) { @@ -254,10 +294,15 @@ void InputProfileEditor::setOptionsVisibility(QLCInputChannel::Type type) void InputProfileEditor::slotTypeComboChanged(int) { + bool showMidiSettings = false; + if (currentProfileType() == QLCInputProfile::MIDI) - m_midiGroupSettings->setVisible(true); - else - m_midiGroupSettings->setVisible(false); + { + showMidiSettings = true; + updateMidiChannelTree(); + } + + m_midiGroupSettings->setVisible(showMidiSettings); } /**************************************************************************** @@ -533,10 +578,13 @@ void InputProfileEditor::slotItemClicked(QTreeWidgetItem *item, int col) m_extraPressCheck->setChecked(ich->sendExtraPress()); m_lowerSpin->blockSignals(true); m_upperSpin->blockSignals(true); + m_midiChannelCombo->blockSignals(true); m_lowerSpin->setValue(ich->lowerValue()); m_upperSpin->setValue(ich->upperValue()); + m_midiChannelCombo->setCurrentIndex(ich->midiChannel() + 1); m_lowerSpin->blockSignals(false); m_upperSpin->blockSignals(false); + m_midiChannelCombo->blockSignals(false); } } else @@ -603,6 +651,15 @@ void InputProfileEditor::slotUpperValueSpinChanged(int value) } } +void InputProfileEditor::slotMidiChannelComboChanged(int index) +{ + foreach (QLCInputChannel *channel, selectedChannels()) + { + if (channel->type() == QLCInputChannel::Button) + channel->setMidiChannel(index - 1); + } +} + void InputProfileEditor::slotAddColor() { bool ok; @@ -615,6 +672,7 @@ void InputProfileEditor::slotAddColor() QString label = QInputDialog::getText(this, tr("Enter label"), tr("Color label")); m_profile->addColor(val, label, color); updateColorsTree(); + m_colorTableTree->scrollToBottom(); } } @@ -628,6 +686,29 @@ void InputProfileEditor::slotRemoveColor() updateColorsTree(); } +void InputProfileEditor::slotAddMidiChannel() +{ + bool ok; + int val = QInputDialog::getInt(this, tr("Enter value"), tr("MIDI channel"), 1, 1, 16, 1, &ok); + + if (ok) + { + QString label = QInputDialog::getText(this, tr("Enter label"), tr("MIDI channel label")); + m_profile->addMidiChannel(val - 1, label); + updateMidiChannelTree(); + } +} + +void InputProfileEditor::slotRemoveMidiChannel() +{ + foreach (QTreeWidgetItem *item, m_midiChannelsTree->selectedItems()) + { + uchar value = uchar(item->text(0).toInt()); + m_profile->removeMidiChannel(value); + } + updateMidiChannelTree(); +} + void InputProfileEditor::slotInputValueChanged(quint32 universe, quint32 channel, uchar value, diff --git a/ui/src/inputprofileeditor.h b/ui/src/inputprofileeditor.h index 56ec69193a..38122a5a1c 100644 --- a/ui/src/inputprofileeditor.h +++ b/ui/src/inputprofileeditor.h @@ -48,6 +48,7 @@ class InputProfileEditor : public QDialog, public Ui_InputProfileEditor protected: void fillTree(); void updateColorsTree(); + void updateMidiChannelTree(); void updateChannelItem(QTreeWidgetItem *item, QLCInputChannel *ch); void setOptionsVisibility(QLCInputChannel::Type type); @@ -81,10 +82,14 @@ protected slots: void slotExtraPressChecked(bool checked); void slotLowerValueSpinChanged(int value); void slotUpperValueSpinChanged(int value); + void slotMidiChannelComboChanged(int index); void slotAddColor(); void slotRemoveColor(); + void slotAddMidiChannel(); + void slotRemoveMidiChannel(); + void slotInputValueChanged(quint32 universe, quint32 channel, uchar value, const QString& key = 0); void slotTimerTimeout(); diff --git a/ui/src/inputprofileeditor.ui b/ui/src/inputprofileeditor.ui index bf74513c57..86edda917b 100644 --- a/ui/src/inputprofileeditor.ui +++ b/ui/src/inputprofileeditor.ui @@ -26,7 +26,7 @@ 0 0 520 - 476 + 492 @@ -131,7 +131,7 @@ - Channels + Input Mapping @@ -358,6 +358,20 @@ 3 + + + + 255 + + + + + + + Lower value + + + @@ -378,19 +392,15 @@ - - + + - Lower value + MIDI channel - - - - 255 - - + + @@ -476,6 +486,80 @@ + + + MIDI Channels + + + + + + Add a new MIDI channel + + + + + + + :/edit_add.png:/edit_add.png + + + + 32 + 32 + + + + + + + + Remove the selected MIDI channel + + + + + + + :/edit_remove.png:/edit_remove.png + + + + 32 + 32 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + Channel + + + + + Name + + + + + + diff --git a/ui/src/virtualconsole/vcwidget.cpp b/ui/src/virtualconsole/vcwidget.cpp index cdd0a13c0b..a9e9f17193 100644 --- a/ui/src/virtualconsole/vcwidget.cpp +++ b/ui/src/virtualconsole/vcwidget.cpp @@ -40,7 +40,6 @@ #include #include #include -#include #include "qlcinputsource.h" #include "qlcfile.h" @@ -258,6 +257,7 @@ bool VCWidget::copyFrom(const VCWidget* widget) quint8 id = it.key(); QSharedPointer src(new QLCInputSource(it.value()->universe(), it.value()->channel())); src->setRange(it.value()->lowerValue(), it.value()->upperValue()); + src->setExtraParams(it.value()->extraParams()); setInputSource(src, id); } @@ -609,12 +609,17 @@ void VCWidget::setInputSource(QSharedPointer const& source, quin InputPatch *ip = m_doc->inputOutputMap()->inputPatch(source->universe()); if (ip != NULL) { - if (ip->profile() != NULL) + QLCInputProfile *profile = ip->profile(); + if (profile != NULL) { // Do not care about the page since input profiles don't do either - QLCInputChannel *ich = ip->profile()->channel(source->channel() & 0xFFFF); + QLCInputChannel *ich = profile->channel(source->channel() & 0xFFFF); if (ich != NULL) { + // retrieve plugin specific params for feedbacks + if (source->extraParams().toInt() == -1) + source->setExtraParams(profile->channelExtraParams(ich)); + if (ich->movementType() == QLCInputChannel::Relative) { source->setWorkingMode(QLCInputSource::Relative); @@ -693,20 +698,7 @@ void VCWidget::sendFeedback(int value, QSharedPointer src) if (acceptsInput() == false) return; - QString chName = QString(); - - InputPatch* pat = m_doc->inputOutputMap()->inputPatch(src->universe()); - if (pat != NULL) - { - QLCInputProfile* profile = pat->profile(); - if (profile != NULL) - { - QLCInputChannel* ich = profile->channel(src->channel()); - if (ich != NULL) - chName = ich->name(); - } - } - m_doc->inputOutputMap()->sendFeedBack(src->universe(), src->channel(), value, chName); + m_doc->inputOutputMap()->sendFeedBack(src->universe(), src->channel(), value, src->extraParams()); } void VCWidget::slotInputValueChanged(quint32 universe, quint32 channel, uchar value) @@ -886,6 +878,7 @@ QSharedPointer VCWidget::getXMLInput(QXmlStreamReader &root) quint32 uni = attrs.value(KXMLQLCVCWidgetInputUniverse).toString().toUInt(); quint32 ch = attrs.value(KXMLQLCVCWidgetInputChannel).toString().toUInt(); uchar min = 0, max = UCHAR_MAX, mon = UCHAR_MAX; + int fbChannel = -1; QSharedPointernewSrc = QSharedPointer(new QLCInputSource(uni, ch)); if (attrs.hasAttribute(KXMLQLCVCWidgetInputLowerValue)) @@ -894,9 +887,12 @@ QSharedPointer VCWidget::getXMLInput(QXmlStreamReader &root) max = uchar(attrs.value(KXMLQLCVCWidgetInputUpperValue).toString().toUInt()); if (attrs.hasAttribute(KXMLQLCVCWidgetInputMonitorValue)) mon = uchar(attrs.value(KXMLQLCVCWidgetInputMonitorValue).toString().toUInt()); + if (attrs.hasAttribute(KXMLQLCVCWidgetInputFeedbackChannel)) + fbChannel = attrs.value(KXMLQLCVCWidgetInputFeedbackChannel).toInt(); newSrc->setRange(min, max); newSrc->setMonitorValue(mon); + newSrc->setExtraParams(fbChannel); return newSrc; } @@ -1047,6 +1043,10 @@ bool VCWidget::saveXMLInput(QXmlStreamWriter *doc, if (src->monitorValue() != UCHAR_MAX) doc->writeAttribute(KXMLQLCVCWidgetInputMonitorValue, QString::number(src->monitorValue())); + QVariant extraParams = src->extraParams(); + if (extraParams.isValid() && extraParams.type() == QVariant::Int && extraParams.toInt() != -1) + doc->writeAttribute(KXMLQLCVCWidgetInputFeedbackChannel, QString::number(extraParams.toInt())); + doc->writeEndElement(); } diff --git a/ui/src/virtualconsole/vcwidget.h b/ui/src/virtualconsole/vcwidget.h index 6b1f81a98c..e5cc61c1a9 100644 --- a/ui/src/virtualconsole/vcwidget.h +++ b/ui/src/virtualconsole/vcwidget.h @@ -59,13 +59,14 @@ class QFile; #define KVCFrameStyleRaised (QFrame::Panel | QFrame::Raised) #define KVCFrameStyleNone (QFrame::NoFrame) -#define KXMLQLCVCWidgetKey QString("Key") -#define KXMLQLCVCWidgetInput QString("Input") -#define KXMLQLCVCWidgetInputUniverse QString("Universe") -#define KXMLQLCVCWidgetInputChannel QString("Channel") -#define KXMLQLCVCWidgetInputLowerValue QString("LowerValue") -#define KXMLQLCVCWidgetInputUpperValue QString("UpperValue") -#define KXMLQLCVCWidgetInputMonitorValue QString("MonitorValue") +#define KXMLQLCVCWidgetKey QString("Key") +#define KXMLQLCVCWidgetInput QString("Input") +#define KXMLQLCVCWidgetInputUniverse QString("Universe") +#define KXMLQLCVCWidgetInputChannel QString("Channel") +#define KXMLQLCVCWidgetInputLowerValue QString("LowerValue") +#define KXMLQLCVCWidgetInputUpperValue QString("UpperValue") +#define KXMLQLCVCWidgetInputMonitorValue QString("MonitorValue") +#define KXMLQLCVCWidgetInputFeedbackChannel QString("FeedbackChannel") #define KXMLQLCWindowState QString("WindowState") #define KXMLQLCWindowStateVisible QString("Visible") From fcbacdf9504ffe4221f5735966476126355b6c4d Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Fri, 16 Feb 2024 13:23:43 +0100 Subject: [PATCH 073/212] resources: improve AKAI APC Mini MK2 profile with color table and custom MIDI channel --- resources/inputprofiles/Akai-APCMini-mk2.qxi | 276 ++++++++++++++----- 1 file changed, 212 insertions(+), 64 deletions(-) diff --git a/resources/inputprofiles/Akai-APCMini-mk2.qxi b/resources/inputprofiles/Akai-APCMini-mk2.qxi index 0a3e727d1d..24c198ff41 100644 --- a/resources/inputprofiles/Akai-APCMini-mk2.qxi +++ b/resources/inputprofiles/Akai-APCMini-mk2.qxi @@ -48,322 +48,322 @@ Button 1-8 Button - + Button 2-8 Button - + Button 3-8 Button - + Button 4-8 Button - + Button 5-8 Button - + Button 6-8 Button - + Button 7-8 Button - + Button 8-8 Button - + Button 1-7 Button - + Button 2-7 Button - + Button 3-7 Button - + Button 4-7 Button - + Button 5-7 Button - + Button 6-7 Button - + Button 7-7 Button - + Button 8-7 Button - + Button 1-6 Button - + Button 2-6 Button - + Button 3-6 Button - + Button 4-6 Button - + Button 5-6 Button - + Button 6-6 Button - + Button 7-6 Button - + Button 8-6 Button - + Button 1-5 Button - + Button 2-5 Button - + Button 3-5 Button - + Button 4-5 Button - + Button 5-5 Button - + Button 6-5 Button - + Button 7-5 Button - + Button 8-5 Button - + Button 1-4 Button - + Button 2-4 Button - + Button 3-4 Button - + Button 4-4 Button - + Button 5-4 Button - + Button 6-4 Button - + Button 7-4 Button - + Button 8-4 Button - + Button 1-3 Button - + Button 2-3 Button - + Button 3-3 Button - + Button 4-3 Button - + Button 5-3 Button - + Button 6-3 Button - + Button 7-3 Button - + Button 8-3 Button - + Button 1-2 Button - + Button 2-2 Button - + Button 3-2 Button - + Button 4-2 Button - + Button 5-2 Button - + Button 6-2 Button - + Button 7-2 Button - + Button 8-2 Button - + Button 1-1 Button - + Button 2-1 Button - + Button 3-1 Button - + Button 4-1 Button - + Button 5-1 Button - + Button 6-1 Button - + Button 7-1 Button - + Button 8-1 Button - + Volume Button @@ -450,4 +450,152 @@ Button + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 08e4a693e99b731f189a246d8326179f67eb22c4 Mon Sep 17 00:00:00 2001 From: Binary-Vanguard-12138 <103393933+Binary-Vanguard-12138@users.noreply.github.com> Date: Sun, 18 Feb 2024 16:20:48 +0800 Subject: [PATCH 074/212] Cuelist webaccess setCueIndex -> enableCue --- webaccess/src/webaccess.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webaccess/src/webaccess.cpp b/webaccess/src/webaccess.cpp index ba11661214..3e78163950 100644 --- a/webaccess/src/webaccess.cpp +++ b/webaccess/src/webaccess.cpp @@ -1562,7 +1562,7 @@ QString WebAccess::getCueListHTML(VCCueList *cue) { QString stepID = QString::number(cue->id()) + "_" + QString::number(i); str += "id()) + ", " + QString::number(i) + ");\">\n"; + "onclick=\"enableCue(" + QString::number(cue->id()) + ", " + QString::number(i) + ");\">\n"; ChaserStep *step = chaser->stepAt(i); str += ""; From 2d24337e84f6d6f53bf741cff6e95b4992600652 Mon Sep 17 00:00:00 2001 From: Binary-Vanguard-12138 <103393933+Binary-Vanguard-12138@users.noreply.github.com> Date: Sun, 18 Feb 2024 09:41:23 +0100 Subject: [PATCH 075/212] Android build with CMake --- CMakeLists.txt | 7 +++++++ qmlui/CMakeLists.txt | 32 +++++++++++++++++++++++++++++--- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a6c93a4e84..2bf1d4f6c0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,6 +20,10 @@ if(UNIX) endif() endif() +if (ANDROID OR IOS) + set(qmlui ON) +endif() + set(CMAKE_INCLUDE_CURRENT_DIR ON) # Set up AUTOMOC and some sensible defaults for runtime execution @@ -32,6 +36,9 @@ find_package(QT NAMES Qt5 Qt6 REQUIRED COMPONENTS Core) find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Gui Multimedia MultimediaWidgets Network PrintSupport Qml Quick Svg Test Widgets LinguistTools) if(qmlui) find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS 3DCore 3DInput 3DQuick 3DQuickExtras 3DRender) + if(ANDROID) + find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Concurrent OpenGL) + endif() endif() message("Found Qt version ${QT_VERSION_MAJOR}: ${QT_DIR}") diff --git a/qmlui/CMakeLists.txt b/qmlui/CMakeLists.txt index ad1e0bf867..8149167be5 100644 --- a/qmlui/CMakeLists.txt +++ b/qmlui/CMakeLists.txt @@ -1,4 +1,8 @@ -set(module_name "qlcplus-qml") +if(ANDROID) + set(module_name "qlcplus") +else() + set(module_name "qlcplus-qml") +endif() set(TS_FILES qlcplus_ca_ES.ts @@ -19,7 +23,7 @@ else() qt5_add_translation(QM_FILES ${TS_FILES}) endif() -add_executable(${module_name} WIN32 MACOSX_BUNDLE +set(SRC_FILES app.cpp app.h audioeditor.cpp audioeditor.h chasereditor.cpp chasereditor.h @@ -73,9 +77,20 @@ add_executable(${module_name} WIN32 MACOSX_BUNDLE virtualconsole/vcsoloframe.cpp virtualconsole/vcsoloframe.h virtualconsole/vcwidget.cpp virtualconsole/vcwidget.h virtualconsole/virtualconsole.cpp virtualconsole/virtualconsole.h - ${QM_FILES} ) +if(ANDROID) + add_library(${module_name} SHARED + ${SRC_FILES} + ${QM_FILES} + ) +else() + add_executable(${module_name} WIN32 MACOSX_BUNDLE + ${SRC_FILES} + ${QM_FILES} + ) +endif() + if(WIN32) target_sources(${module_name} PRIVATE qmlui.rc @@ -111,6 +126,17 @@ target_link_libraries(${module_name} PRIVATE qlcplusengine ) +if(ANDROID) + target_link_libraries(${module_name} PRIVATE + Qt${QT_MAJOR_VERSION}::Concurrent + Qt${QT_MAJOR_VERSION}::OpenGL + GLESv2 + log + z + c++_shared + ) +endif() + if(lupdate_only) target_sources(${module_name} PRIVATE qml/*.qml From d48c0046b4929ec9df55d037122c10474e939c9a Mon Sep 17 00:00:00 2001 From: Binary-Vanguard-12138 <103393933+Binary-Vanguard-12138@users.noreply.github.com> Date: Sun, 18 Feb 2024 10:32:16 +0100 Subject: [PATCH 076/212] Fix issues in Android build with CMake --- CMakeLists.txt | 8 ++++++++ qmlui/CMakeLists.txt | 9 ++++----- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2bf1d4f6c0..e269e4381d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,6 +24,14 @@ 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 diff --git a/qmlui/CMakeLists.txt b/qmlui/CMakeLists.txt index 8149167be5..6c4d7e08a2 100644 --- a/qmlui/CMakeLists.txt +++ b/qmlui/CMakeLists.txt @@ -1,8 +1,4 @@ -if(ANDROID) - set(module_name "qlcplus") -else() - set(module_name "qlcplus-qml") -endif() +set(module_name "qlcplus-qml") set(TS_FILES qlcplus_ca_ES.ts @@ -84,6 +80,9 @@ if(ANDROID) ${SRC_FILES} ${QM_FILES} ) + set_target_properties( + ${module_name} + PROPERTIES LIBRARY_OUTPUT_NAME qlcplus) else() add_executable(${module_name} WIN32 MACOSX_BUNDLE ${SRC_FILES} From 3bc67b90cdbe019b85fde19bf59744621f242369 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Fri, 23 Feb 2024 13:39:41 +0100 Subject: [PATCH 077/212] webaccess: prevent playback on Cue list step selection --- debian/changelog | 2 ++ ui/src/virtualconsole/vccuelist.h | 6 +++--- webaccess/src/webaccess.cpp | 24 ++++++++++++------------ 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/debian/changelog b/debian/changelog index a832e5f81e..f683ea647e 100644 --- a/debian/changelog +++ b/debian/changelog @@ -23,12 +23,14 @@ qlcplus (4.13.0) stable; urgency=low * 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) * 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) diff --git a/ui/src/virtualconsole/vccuelist.h b/ui/src/virtualconsole/vccuelist.h index 13cbaec78e..0349c196d6 100644 --- a/ui/src/virtualconsole/vccuelist.h +++ b/ui/src/virtualconsole/vccuelist.h @@ -180,6 +180,9 @@ public slots: /** Skip to the previous cue */ void slotPreviousCue(); + /** Called when m_runner skips to another step */ + void slotCurrentStepChanged(int stepNumber); + /** Update cue step note */ void slotStepNoteChanged(int idx, QString note); @@ -200,9 +203,6 @@ private slots: /** Update the step list at m_updateTimer timeout */ void slotUpdateStepList(); - /** Called when m_runner skips to another step */ - void slotCurrentStepChanged(int stepNumber); - /** Slot that is called whenever the current item changes (either by pressing the key binding or clicking an item with mouse) */ void slotItemActivated(QTreeWidgetItem *item); diff --git a/webaccess/src/webaccess.cpp b/webaccess/src/webaccess.cpp index 7d670964ce..a1343cb50a 100644 --- a/webaccess/src/webaccess.cpp +++ b/webaccess/src/webaccess.cpp @@ -780,7 +780,7 @@ void WebAccess::slotHandleWebSocketRequest(QHttpConnection *conn, QString data) else if (cmdList[1] == "NEXT") cue->slotNextCue(); else if (cmdList[1] == "STEP") - cue->playCueAtIndex(cmdList[2].toInt()); + cue->slotCurrentStepChanged(cmdList[2].toInt()); else if (cmdList[1] == "CUE_STEP_NOTE") cue->slotStepNoteChanged(cmdList[2].toInt(), cmdList[3]); else if (cmdList[1] == "CUE_SHOWPANEL") @@ -1258,10 +1258,10 @@ QString WebAccess::getSliderHTML(VCSlider *slider) if (spotWidth < 6) spotWidth = 6; str += "
    "; - str += "
    isDisabled() ? "#c0c0c0" : "lime")+";--pieWidth: "+QString::number(pieWidth)+"px;\">"; - str += "
    "; - str += "
    "; - str += "
    "; + str += "
    isDisabled() ? "#c0c0c0" : "lime") + ";--pieWidth: " + QString::number(pieWidth) + "px;\">"; + str += "
    "; + str += "
    "; + str += "
    "; str += "
    \n
    \n
    \n
    \n"; m_JScode += "maxVal[" + slID + "] = " + QString::number(max) + "; \n"; @@ -1541,8 +1541,8 @@ QString WebAccess::getCueListHTML(VCCueList *cue) str += "
    id())+"\" class=\"vcslLabel" + QString(cue->isDisabled() ? " vcslLabel-disabled" : "") + "\" style=\"top:0px;\">" + cue->topPercentageValue() + "
    \n"; - str += "isDisabled() ? " vVertical-disabled" : "") + "\" id=\"cueC"+QString::number(cue->id())+"\" " - "oninput=\"cueCVchange("+QString::number(cue->id())+");\" ontouchmove=\"cueCVchange("+QString::number(cue->id())+");\" " + str += "isDisabled() ? " vVertical-disabled" : "") + "\" id=\"cueC" + QString::number(cue->id()) + "\" " + "oninput=\"cueCVchange(" + QString::number(cue->id()) + ");\" ontouchmove=\"cueCVchange(" + QString::number(cue->id())+");\" " "style=\"width: " + QString::number(cue->height() - 50) + "px; margin-top: " + QString::number(cue->height() - 50) + "px; margin-left: 22px;\" "; str += "min=\"0\" max=\"255\" step=\"1\" value=\"" + QString::number(cue->sideFaderValue()) + "\" " + QString(cue->isDisabled() ? "disabled" : "") + " >\n"; @@ -1557,7 +1557,7 @@ QString WebAccess::getCueListHTML(VCCueList *cue) str += "
    height() - 54) + "px; overflow: scroll;\" >\n"; - str += "
    #" + tr("Name") + "" + tr("Fade In") + "" + tr("Fade Out") + "
    " + QString::number(i + 1) + "
    " + QString::number(i + 1) + "" + step->note + "id()) + ", " + QString::number(i) + ");\">" + + "" + step->note + "" + + "note + "\" style=\"display: none; width: 60px;\" " + + "onfocusout=\"changeCueNoteToTextMode(" + QString::number(cue->id()) + ", " + QString::number(i) + ");\" />" + "
    Worlde Easypad.12MIDI1Worlde Easypad.12nonenoNo bank button and slider (require SYSEX support)
    Zoom R16 MIDI
    " + QString::number(i + 1) + "
    isDisabled() ? " cell-disabled" : "")+"\" id=\"cueTable"+QString::number(cue->id())+"\" style=\"width: 100%;\">\n"; + str += "
    isDisabled() ? " cell-disabled" : "") + "\" id=\"cueTable" + QString::number(cue->id()) + "\" style=\"width: 100%;\">\n"; str += ""; str += ""; str += ""; @@ -1666,9 +1666,9 @@ QString WebAccess::getCueListHTML(VCCueList *cue) // progress bar str += "
    "; - str += "
    id())+"\" style=\"width: " + - QString::number(cue->progressPercent())+ "%; \">
    "; - str += "
    id())+"\">" + + str += "
    id()) + "\" style=\"width: " + + QString::number(cue->progressPercent()) + "%; \">
    "; + str += "
    id())+"\">" + QString(cue->progressText()) + "
    "; str += "
    "; @@ -1676,7 +1676,7 @@ QString WebAccess::getCueListHTML(VCCueList *cue) if (cue->sideFaderMode() != VCCueList::FaderMode::None) { str += "
    "; - str += "isDisabled() ? " vccuelistFadeButton-disabled" : "")+"\" id=\"fade" + QString::number(cue->id()) + "\" "; + str += "isDisabled() ? " vccuelistFadeButton-disabled" : "") + "\" id=\"fade" + QString::number(cue->id()) + "\" "; str += "href=\"javascript:wsShowCrossfadePanel(" + QString::number(cue->id()) + ");\">\n"; str += "\n"; } From 3188c91a18bf07a6fc57b5c74aecb997d4525e83 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Fri, 23 Feb 2024 23:12:51 +0100 Subject: [PATCH 078/212] engine: perform HTP check on whole 16bit values --- engine/src/fadechannel.cpp | 35 ++++++++ engine/src/fadechannel.h | 21 +++-- engine/src/genericfader.cpp | 115 ++++++++++++------------- engine/src/universe.cpp | 81 +++++++++++------ engine/src/universe.h | 13 ++- engine/test/universe/universe_test.cpp | 12 +-- 6 files changed, 176 insertions(+), 101 deletions(-) diff --git a/engine/src/fadechannel.cpp b/engine/src/fadechannel.cpp index 3ae301f832..2e5a8755ee 100644 --- a/engine/src/fadechannel.cpp +++ b/engine/src/fadechannel.cpp @@ -268,36 +268,71 @@ void FadeChannel::setStart(uchar value, int index) ((uchar *)&m_start)[channelCount() - 1 - index] = value; } +void FadeChannel::setStart(quint32 value) +{ + m_start = value; +} + uchar FadeChannel::start(int index) const { return ((uchar *)&m_start)[channelCount() - 1 - index]; } +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(int index) const { return ((uchar *)&m_target)[channelCount() - 1 - index]; } +quint32 FadeChannel::target() const +{ + return m_target; +} + void FadeChannel::setCurrent(uchar value, int index) { ((uchar *)&m_current)[channelCount() - 1 - index] = value; } +void FadeChannel::setCurrent(quint32 value) +{ + m_current = value; +} + uchar FadeChannel::current(int index) const { return ((uchar *)&m_current)[channelCount() - 1 - index]; } +quint32 FadeChannel::current() const +{ + 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) { m_ready = rdy; diff --git a/engine/src/fadechannel.h b/engine/src/fadechannel.h index 8a8af121aa..01d129077f 100644 --- a/engine/src/fadechannel.h +++ b/engine/src/fadechannel.h @@ -133,25 +133,32 @@ class FadeChannel public: /** Set starting value. */ - void setStart(uchar value, int index = 0); + void setStart(uchar value, int index); + void setStart(quint32 value); /** Get starting value. */ - uchar start(int index = 0) const; + uchar start(int index) const; + quint32 start() const; /** Set target value. */ - void setTarget(uchar value, int index = 0); + void setTarget(uchar value, int index); + void setTarget(quint32 value); /** Get target value. */ - uchar target(int index = 0) const; + uchar target(int index) const; + quint32 target() const; /** Set the current value. */ - void setCurrent(uchar value, int index = 0); + void setCurrent(uchar value, int index); + void setCurrent(quint32 value); /** Get the current value. */ - uchar current(int index = 0) const; + uchar current(int index) const; + quint32 current() const; /** Get the current value, modified by $intensity. */ - uchar current(qreal intensity, int index = 0) 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); diff --git a/engine/src/genericfader.cpp b/engine/src/genericfader.cpp index dd75e67237..f7fe2596c5 100644 --- a/engine/src/genericfader.cpp +++ b/engine/src/genericfader.cpp @@ -195,83 +195,79 @@ 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; - - // counter used at the end to detect channels to remove - int removeCount = fc.channelCount(); + int channelCount = fc.channelCount(); // iterate through all the channels handled by this fader - for (int i = 0; i < fc.channelCount(); i++) + + if (flags & FadeChannel::SetTarget) { - if (flags & FadeChannel::SetTarget) - { - fc.removeFlag(FadeChannel::SetTarget); - fc.addFlag(FadeChannel::AutoRemove); + 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 (i == 0 && m_paused == false) - fc.nextStep(MasterTimer::tick()); + } - value = fc.current(i); + // Calculate the next step + if (m_paused == false) + fc.nextStep(MasterTimer::tick()); - // Apply intensity to channels that can fade - if (fc.canFade()) - { - if ((flags & FadeChannel::CrossFade) && fc.fadeTime() == 0) - { - // morph start <-> target depending on intensities - value = uchar(((qreal(fc.target(i) - fc.start(i)) * intensity()) + fc.start(i)) * parentIntensity()); - } - else if (flags & FadeChannel::Intensity) - { - value = fc.current(compIntensity, i); - } - } + quint32 value = fc.current(); - //qDebug() << "[GenericFader] >>> uni:" << universe->id() << ", address:" << (address + i) << ", value:" << value << "int:" << compIntensity; - if (flags & FadeChannel::Override) - { - universe->write(address + i, value, true); - continue; - } - else if (flags & FadeChannel::Relative) - { - universe->writeRelative(address + i, value); - } - else if (flags & FadeChannel::Flashing) + // Apply intensity to channels that can fade + if (fc.canFade()) + { + if ((flags & FadeChannel::CrossFade) && fc.fadeTime() == 0) { - universe->write(address + i, value, flags & FadeChannel::ForceLTP); - continue; + // morph start <-> target depending on intensities + value = quint32(((qreal(fc.target() - fc.start()) * intensity()) + fc.start()) * parentIntensity()); } - else + else if (flags & FadeChannel::Intensity) { - universe->writeBlended(address + i, value, m_blendMode); + value = fc.current(compIntensity); } + } - if (((flags & FadeChannel::Intensity) && - (flags & FadeChannel::HTP) && - m_blendMode == Universe::NormalBlend) || m_fadeOut) - { - // Remove all channels that reach their target _zero_ value. - // They have no effect either way so removing them saves a bit of CPU. - if (fc.current(i) == 0 && fc.target(i) == 0 && fc.isReady()) - removeCount--; - } + //qDebug() << "[GenericFader] >>> uni:" << universe->id() << ", address:" << (address + i) << ", value:" << value << "int:" << compIntensity; + if (flags & FadeChannel::Override) + { + universe->write(address, value, true); + continue; + } + else if (flags & FadeChannel::Relative) + { + for (int i = 0; i < channelCount; i++) + universe->writeRelative(address + i, ((uchar *)&value)[channelCount - 1 - i]); + } + else if (flags & FadeChannel::Flashing) + { + universe->write(address, value, flags & FadeChannel::ForceLTP); + continue; + } + else + { + // treat value as a whole, so do this just once per FadeChannel + universe->writeBlended(address, value, channelCount, m_blendMode); + } - if (flags & FadeChannel::AutoRemove && value == fc.target(i)) - removeCount--; + if (((flags & FadeChannel::Intensity) && + (flags & FadeChannel::HTP) && + m_blendMode == Universe::NormalBlend) || m_fadeOut) + { + // Remove all channels that reach their target _zero_ value. + // They have no effect either way so removing them saves a bit of CPU. + if (fc.current() == 0 && fc.target() == 0 && fc.isReady()) + it.remove(); } - // check fader removal - if (removeCount == 0) + if (flags & FadeChannel::AutoRemove && value == fc.target()) it.remove(); } @@ -347,11 +343,8 @@ void GenericFader::setFadeOut(bool enable, uint fadeTime) if ((fc.flags() & FadeChannel::Intensity) == 0) fc.addFlag(FadeChannel::SetTarget); - for (int i = 0; i < fc.channelCount(); i++) - { - fc.setStart(fc.current(i), i); - fc.setTarget(0, i); - } + fc.setStart(fc.current()); + fc.setTarget(0); fc.setElapsed(0); fc.setReady(false); fc.setFadeTime(fc.canFade() ? fadeTime : 0); diff --git a/engine/src/universe.cpp b/engine/src/universe.cpp index 1008e5e4c0..cc53713a0c 100644 --- a/engine/src/universe.cpp +++ b/engine/src/universe.cpp @@ -228,7 +228,8 @@ QSharedPointer Universe::requestFader(Universe::FaderPriority prio m_faders.insert(insertPos, fader); } - qDebug() << "Generic fader with priority" << fader->priority() << "registered at pos" << insertPos << ", count" << m_faders.count(); + qDebug() << "[Universe]" << id() << ": Generic fader with priority" << fader->priority() + << "registered at pos" << insertPos << ", count" << m_faders.count(); return fader; } @@ -265,7 +266,8 @@ void Universe::requestFaderPriority(QSharedPointer fader, Universe if (newPos != pos) { m_faders.move(pos, newPos); - qDebug() << "Generic fader moved from" << pos << "to" << m_faders.indexOf(fader) << ". Count:" << m_faders.count(); + qDebug() << "[Universe]" << id() << ": Generic fader moved from" << pos + << "to" << m_faders.indexOf(fader) << ". Count:" << m_faders.count(); } } @@ -929,18 +931,23 @@ bool Universe::write(int channel, uchar value, bool forceLTP) { Q_ASSERT(channel < UNIVERSE_SIZE); - //qDebug() << "Universe write channel" << channel << ", value:" << value; + //qDebug() << "[Universe]" << id() << ": write channel" << channel << ", value:" << value; if (channel >= m_usedChannels) m_usedChannels = channel + 1; - if ((m_channelsMask->at(channel) & HTP) == false) - (*m_blackoutValues)[channel] = char(value); - - if (forceLTP == false && (m_channelsMask->at(channel) & HTP) && value < (uchar)m_preGMValues->at(channel)) + if (m_channelsMask->at(channel) & HTP) { - qDebug() << "[Universe] HTP check not passed" << channel << value; - return false; + if (forceLTP == false && value < (uchar)m_preGMValues->at(channel)) + { + qDebug() << "[Universe] HTP check not passed" << channel << value; + return false; + } + } + else + { + // preserve non HTP channels for blackout + (*m_blackoutValues)[channel] = char(value); } (*m_preGMValues)[channel] = char(value); @@ -950,6 +957,22 @@ bool Universe::write(int channel, uchar value, bool forceLTP) return true; } +bool Universe::writeMultiple(int address, quint32 value, int channelCount) +{ + for (int i = 0; i < channelCount; i++) + { + // preserve non HTP channels for blackout + if ((m_channelsMask->at(address + i) & HTP) == 0) + (*m_blackoutValues)[address + i] = ((uchar *)&value)[channelCount - 1 - i]; + + (*m_preGMValues)[address + i] = ((uchar *)&value)[channelCount - 1 - i]; + + updatePostGMValue(address + i); + } + + return true; +} + bool Universe::writeRelative(int channel, uchar value) { Q_ASSERT(channel < UNIVERSE_SIZE); @@ -969,53 +992,59 @@ bool Universe::writeRelative(int channel, uchar value) return true; } -bool Universe::writeBlended(int channel, uchar value, Universe::BlendMode blend) +bool Universe::writeBlended(int channel, quint32 value, int channelCount, Universe::BlendMode blend) { - if (channel >= m_usedChannels) - m_usedChannels = channel + 1; + if (channel + channelCount - 1 >= m_usedChannels) + m_usedChannels = channel + channelCount; + + quint32 currentValue = 0; + for (int i = 0; i < channelCount; i++) + currentValue = (currentValue << 8) + uchar(m_preGMValues->at(channel + i)); switch (blend) { case NormalBlend: - return write(channel, value); - + { + if ((m_channelsMask->at(channel) & HTP) && value < currentValue) + { + qDebug() << "[Universe] HTP check not passed" << channel << 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(channel, value, channelCount); return true; } diff --git a/engine/src/universe.h b/engine/src/universe.h index 6c4ad00c7a..7503c149fb 100644 --- a/engine/src/universe.h +++ b/engine/src/universe.h @@ -525,6 +525,16 @@ public slots: */ bool write(int channel, uchar value, bool forceLTP = false); + /** + * Write a value representing one or multiple channels + * + * @param address the DMX start address + * @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. @@ -543,11 +553,12 @@ public slots: * * @param channel The channel number 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 channel, quint32 value, int channelCount, BlendMode blend); /********************************************************************* * Load & Save diff --git a/engine/test/universe/universe_test.cpp b/engine/test/universe/universe_test.cpp index 0702f3ed37..48ede51aa0 100644 --- a/engine/test/universe/universe_test.cpp +++ b/engine/test/universe/universe_test.cpp @@ -112,26 +112,26 @@ void Universe_Test::blendModes() QCOMPARE(quint8(m_uni->postGMValues()->at(11)), quint8(0)); /* check masking on 0 remains 0 */ - QVERIFY(m_uni->writeBlended(11, 128, Universe::MaskBlend) == true); + QVERIFY(m_uni->writeBlended(11, 128, 1, Universe::MaskBlend) == true); QCOMPARE(quint8(m_uni->postGMValues()->at(11)), quint8(0)); /* check 180 masked on 128 gets halved */ - QVERIFY(m_uni->writeBlended(4, 180, Universe::MaskBlend) == true); + QVERIFY(m_uni->writeBlended(4, 180, 1, Universe::MaskBlend) == true); QCOMPARE(quint8(m_uni->postGMValues()->at(4)), quint8(90)); /* chek adding 50 to 100 is actually 150 */ - QVERIFY(m_uni->writeBlended(9, 50, Universe::AdditiveBlend) == true); + QVERIFY(m_uni->writeBlended(9, 50, 1, Universe::AdditiveBlend) == true); QCOMPARE(quint8(m_uni->postGMValues()->at(9)), quint8(150)); /* chek subtracting 55 to 255 is actually 200 */ - QVERIFY(m_uni->writeBlended(0, 55, Universe::SubtractiveBlend) == true); + QVERIFY(m_uni->writeBlended(0, 55, 1, Universe::SubtractiveBlend) == true); QCOMPARE(quint8(m_uni->postGMValues()->at(0)), quint8(200)); - QVERIFY(m_uni->writeBlended(0, 255, Universe::SubtractiveBlend) == true); + QVERIFY(m_uni->writeBlended(0, 255, 1, Universe::SubtractiveBlend) == true); QCOMPARE(quint8(m_uni->postGMValues()->at(0)), quint8(0)); /* check an unknown blend mode */ - QVERIFY(m_uni->writeBlended(9, 255, Universe::BlendMode(42)) == true); + QVERIFY(m_uni->writeBlended(9, 255, 1, Universe::BlendMode(42)) == false); QCOMPARE(quint8(m_uni->postGMValues()->at(9)), quint8(150)); } From 52c0e8378c1f3a5598d45026342d01d035bc69a8 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Sun, 25 Feb 2024 10:29:56 +0100 Subject: [PATCH 079/212] resources: add Worlde Orca PAD16 input profile --- debian/changelog | 1 + resources/inputprofiles/Worlde-OrcaPAD16.qxi | 101 +++++++++++++++++++ 2 files changed, 102 insertions(+) create mode 100644 resources/inputprofiles/Worlde-OrcaPAD16.qxi diff --git a/debian/changelog b/debian/changelog index f683ea647e..a202aade72 100644 --- a/debian/changelog +++ b/debian/changelog @@ -31,6 +31,7 @@ qlcplus (4.13.0) stable; urgency=low * 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) diff --git a/resources/inputprofiles/Worlde-OrcaPAD16.qxi b/resources/inputprofiles/Worlde-OrcaPAD16.qxi new file mode 100644 index 0000000000..d25fd99081 --- /dev/null +++ b/resources/inputprofiles/Worlde-OrcaPAD16.qxi @@ -0,0 +1,101 @@ + + + + + Q Light Controller Plus + 4.13.0 + Massimo Callegari + + Worlde + Orca PAD16 + MIDI + False + + R1 + Knob + + + R2 + Knob + + + R3 + Knob + + + R4 + Knob + + + R5 + Knob + + + R6 + Knob + + + PAD1 + Button + + + PAD2 + Button + + + PAD3 + Button + + + PAD4 + Button + + + PAD5 + Button + + + PAD6 + Button + + + PAD7 + Button + + + PAD8 + Button + + + PAD9 + Button + + + PAD10 + Button + + + PAD11 + Button + + + PAD12 + Button + + + PAD13 + Button + + + PAD14 + Button + + + PAD15 + Button + + + PAD16 + Button + + From 47808ec32af9fbcc4b6749d83cc8e143e43f3f1a Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Sun, 25 Feb 2024 10:48:20 +0100 Subject: [PATCH 080/212] Remove some more Qt5 checks --- engine/src/inputoutputmap.cpp | 2 -- ui/src/app.h | 7 +------ ui/src/functionselection.cpp | 2 -- ui/src/functionselection.h | 2 -- ui/src/showmanager/multitrackview.h | 2 -- ui/src/virtualconsole/vcaudiotriggers.cpp | 2 -- ui/src/virtualconsole/vcaudiotriggers.h | 2 -- 7 files changed, 1 insertion(+), 18 deletions(-) diff --git a/engine/src/inputoutputmap.cpp b/engine/src/inputoutputmap.cpp index 39367b8a36..677ae9744d 100644 --- a/engine/src/inputoutputmap.cpp +++ b/engine/src/inputoutputmap.cpp @@ -41,8 +41,6 @@ #include "qlcfile.h" #include "doc.h" -#include "../../plugins/midi/src/common/midiprotocol.h" - InputOutputMap::InputOutputMap(Doc *doc, quint32 universes) : QObject(doc) , m_blackout(false) diff --git a/ui/src/app.h b/ui/src/app.h index 4a6a27b4c5..600e762266 100644 --- a/ui/src/app.h +++ b/ui/src/app.h @@ -31,6 +31,7 @@ #include "doc.h" class QProgressDialog; +class VideoProvider; class QMessageBox; class QToolButton; class QFileDialog; @@ -42,10 +43,6 @@ class QAction; class QLabel; class App; -#if QT_VERSION >= 0x050000 -class VideoProvider; -#endif - /** @addtogroup ui UI * @{ */ @@ -212,9 +209,7 @@ public slots: *********************************************************************/ private: DmxDumpFactoryProperties *m_dumpProperties; -#if QT_VERSION >= 0x050000 VideoProvider *m_videoProvider; -#endif /********************************************************************* * Load & Save diff --git a/ui/src/functionselection.cpp b/ui/src/functionselection.cpp index 2aaf8dce08..acccaeb275 100644 --- a/ui/src/functionselection.cpp +++ b/ui/src/functionselection.cpp @@ -445,7 +445,6 @@ void FunctionSelection::slotAudioChecked(bool state) refillTree(); } -#if QT_VERSION >= 0x050000 void FunctionSelection::slotVideoChecked(bool state) { if (state == true) @@ -454,4 +453,3 @@ void FunctionSelection::slotVideoChecked(bool state) m_filter = (m_filter & ~Function::VideoType); refillTree(); } -#endif diff --git a/ui/src/functionselection.h b/ui/src/functionselection.h index c78de680dc..952f5cd159 100644 --- a/ui/src/functionselection.h +++ b/ui/src/functionselection.h @@ -141,9 +141,7 @@ protected slots: void slotRGBMatrixChecked(bool state); void slotShowChecked(bool state); void slotAudioChecked(bool state); -#if QT_VERSION >= 0x050000 void slotVideoChecked(bool state); -#endif private: int m_filter; diff --git a/ui/src/showmanager/multitrackview.h b/ui/src/showmanager/multitrackview.h index eca3ac08fe..d3aba100a3 100644 --- a/ui/src/showmanager/multitrackview.h +++ b/ui/src/showmanager/multitrackview.h @@ -32,9 +32,7 @@ #include "trackitem.h" #include "audioitem.h" #include "efxitem.h" -#if QT_VERSION >= 0x050000 #include "videoitem.h" -#endif #include "chaser.h" #include "track.h" diff --git a/ui/src/virtualconsole/vcaudiotriggers.cpp b/ui/src/virtualconsole/vcaudiotriggers.cpp index 0d8e6af0b9..f7c2c8932d 100644 --- a/ui/src/virtualconsole/vcaudiotriggers.cpp +++ b/ui/src/virtualconsole/vcaudiotriggers.cpp @@ -255,12 +255,10 @@ void VCAudioTriggers::slotDisplaySpectrum(double *spectrumBands, int size, } } -#if QT_VERSION >= 0x050000 void VCAudioTriggers::slotVolumeChanged(int volume) { m_doc->audioInputCapture()->setVolume(intensity() * (qreal)volume / 100); } -#endif /********************************************************************* * DMXSource diff --git a/ui/src/virtualconsole/vcaudiotriggers.h b/ui/src/virtualconsole/vcaudiotriggers.h index 492116bdb8..1f593e32e2 100644 --- a/ui/src/virtualconsole/vcaudiotriggers.h +++ b/ui/src/virtualconsole/vcaudiotriggers.h @@ -81,9 +81,7 @@ public slots: protected slots: void slotDisplaySpectrum(double *spectrumBands, int size, double maxMagnitude, quint32 power); -#if QT_VERSION >= 0x050000 void slotVolumeChanged(int volume); -#endif protected: QHBoxLayout *m_hbox; From 3aacd2bed87c619e924fbb936ccaee545d3bf949 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Sun, 25 Feb 2024 11:07:16 +0100 Subject: [PATCH 081/212] ui: sync multiple audio triggers volume faders (fix #1510) --- engine/audio/src/audiocapture.h | 1 + engine/audio/src/audiocapture_qt5.cpp | 5 ++++ engine/audio/src/audiocapture_qt6.cpp | 5 ++++ ui/src/virtualconsole/vcaudiotriggers.cpp | 31 +++++++++++++++-------- ui/src/virtualconsole/vcaudiotriggers.h | 1 + 5 files changed, 32 insertions(+), 11 deletions(-) diff --git a/engine/audio/src/audiocapture.h b/engine/audio/src/audiocapture.h index 58a9484101..f6fefd73c0 100644 --- a/engine/audio/src/audiocapture.h +++ b/engine/audio/src/audiocapture.h @@ -135,6 +135,7 @@ class AudioCapture : public QThread signals: void dataProcessed(double *spectrumBands, int size, double maxMagnitude, quint32 power); + void volumeChanged(int volume); protected: /*! diff --git a/engine/audio/src/audiocapture_qt5.cpp b/engine/audio/src/audiocapture_qt5.cpp index 79920b42e3..cd9bb3fb78 100644 --- a/engine/audio/src/audiocapture_qt5.cpp +++ b/engine/audio/src/audiocapture_qt5.cpp @@ -113,9 +113,14 @@ qint64 AudioCaptureQt6::latency() void AudioCaptureQt6::setVolume(qreal volume) { + if (volume == m_volume) + return; + m_volume = volume; if (m_audioInput != NULL) m_audioInput->setVolume(volume); + + emit volumeChanged(volume * 100.0); } void AudioCaptureQt6::suspend() diff --git a/engine/audio/src/audiocapture_qt6.cpp b/engine/audio/src/audiocapture_qt6.cpp index 3911c90301..6e3db68a61 100644 --- a/engine/audio/src/audiocapture_qt6.cpp +++ b/engine/audio/src/audiocapture_qt6.cpp @@ -113,9 +113,14 @@ qint64 AudioCaptureQt6::latency() void AudioCaptureQt6::setVolume(qreal volume) { + if (volume == m_volume) + return; + m_volume = volume; if (m_audioSource != NULL) m_audioSource->setVolume(volume); + + emit volumeChanged(volume * 100.0); } void AudioCaptureQt6::suspend() diff --git a/ui/src/virtualconsole/vcaudiotriggers.cpp b/ui/src/virtualconsole/vcaudiotriggers.cpp index f7c2c8932d..ac75a4b71e 100644 --- a/ui/src/virtualconsole/vcaudiotriggers.cpp +++ b/ui/src/virtualconsole/vcaudiotriggers.cpp @@ -181,6 +181,8 @@ void VCAudioTriggers::enableCapture(bool enable) { connect(m_inputCapture, SIGNAL(dataProcessed(double*,int,double,quint32)), this, SLOT(slotDisplaySpectrum(double*,int,double,quint32))); + connect(m_inputCapture, SIGNAL(volumeChanged(int)), + this, SLOT(slotUpdateVolumeSlider(int))); m_inputCapture->registerBandsNumber(m_spectrum->barsNumber()); m_button->blockSignals(true); @@ -199,6 +201,8 @@ void VCAudioTriggers::enableCapture(bool enable) m_inputCapture->unregisterBandsNumber(m_spectrum->barsNumber()); disconnect(m_inputCapture, SIGNAL(dataProcessed(double*,int,double,quint32)), this, SLOT(slotDisplaySpectrum(double*,int,double,quint32))); + disconnect(m_inputCapture, SIGNAL(volumeChanged(int)), + this, SLOT(slotUpdateVolumeSlider(int))); } m_button->blockSignals(true); @@ -230,7 +234,7 @@ void VCAudioTriggers::slotEnableButtonToggled(bool toggle) void VCAudioTriggers::slotDisplaySpectrum(double *spectrumBands, int size, double maxMagnitude, quint32 power) { - qDebug() << "Display spectrum ----- bars:" << size; + //qDebug() << "Display spectrum ----- bars:" << size; if (size != m_spectrum->barsNumber()) return; @@ -257,7 +261,12 @@ void VCAudioTriggers::slotDisplaySpectrum(double *spectrumBands, int size, void VCAudioTriggers::slotVolumeChanged(int volume) { - m_doc->audioInputCapture()->setVolume(intensity() * (qreal)volume / 100); + m_doc->audioInputCapture()->setVolume(intensity() * qreal(volume) / 100.0); +} + +void VCAudioTriggers::slotUpdateVolumeSlider(int volume) +{ + m_volumeSlider->setValue(volume); } /********************************************************************* @@ -418,7 +427,6 @@ bool VCAudioTriggers::copyFrom(const VCWidget *widget) return VCWidget::copyFrom(widget); } - /************************************************************************* * VCWidget-inherited *************************************************************************/ @@ -540,7 +548,6 @@ void VCAudioTriggers::setSpectrumBarType(int index, int type) } } - void VCAudioTriggers::editProperties() { // make a backup copy of the current bars @@ -550,8 +557,7 @@ void VCAudioTriggers::editProperties() tmpSpectrumBars.append(bar->createCopy()); int barsNumber = m_spectrumBars.count(); - AudioTriggersConfiguration atc(this, m_doc, barsNumber, - AudioCapture::maxFrequency()); + AudioTriggersConfiguration atc(this, m_doc, barsNumber, AudioCapture::maxFrequency()); if (atc.exec() == QDialog::Rejected) { @@ -562,7 +568,9 @@ void VCAudioTriggers::editProperties() foreach (AudioBar *bar, tmpSpectrumBars) m_spectrumBars.append(bar); } + m_spectrum->setBarsNumber(m_spectrumBars.count()); + if (barsNumber != m_spectrumBars.count()) { QSharedPointer capture(m_doc->audioInputCapture()); @@ -573,15 +581,20 @@ void VCAudioTriggers::editProperties() { if (!captureIsNew) m_inputCapture->unregisterBandsNumber(barsNumber); + m_inputCapture->registerBandsNumber(m_spectrumBars.count()); + if (captureIsNew) + { connect(m_inputCapture, SIGNAL(dataProcessed(double*,int,double,quint32)), this, SLOT(slotDisplaySpectrum(double*,int,double,quint32))); + connect(m_inputCapture, SIGNAL(volumeChanged(qreal)), + this, SLOT(slotUpdateVolumeSlider(int))); + } } } } - void VCAudioTriggers::adjustIntensity(qreal val) { VCWidget::adjustIntensity(val); @@ -719,7 +732,3 @@ bool VCAudioTriggers::saveXML(QXmlStreamWriter *doc) return true; } - - - - diff --git a/ui/src/virtualconsole/vcaudiotriggers.h b/ui/src/virtualconsole/vcaudiotriggers.h index 1f593e32e2..f838f2afef 100644 --- a/ui/src/virtualconsole/vcaudiotriggers.h +++ b/ui/src/virtualconsole/vcaudiotriggers.h @@ -82,6 +82,7 @@ public slots: protected slots: void slotDisplaySpectrum(double *spectrumBands, int size, double maxMagnitude, quint32 power); void slotVolumeChanged(int volume); + void slotUpdateVolumeSlider(int volume); protected: QHBoxLayout *m_hbox; From 5738347e3822038690d07d9bddc6882e1070b8aa Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Sun, 25 Feb 2024 12:35:22 +0100 Subject: [PATCH 082/212] resources: 8 new fixtures (see changelog) --- debian/changelog | 8 +- .../fixtures/Briteq/Briteq-BT-Theatre-HD2.qxf | 221 +++++++++++++++++ .../Chauvet/Chauvet-COLORband-Q3BT.qxf | 108 ++++++++ .../Chauvet-Intimidator-Wash-Zoom-450-IRC.qxf | 234 ++++++++++++++++++ .../Eurolite/Eurolite-LED-PARty-TCL-spot.qxf | 37 +++ .../Expolite/Expolite-TourSpot-50-Mini.qxf | 117 +++++++++ resources/fixtures/FixturesMap.xml | 10 + ...un-Generation-LED-Pot-12x1W-QCL-RGB-WW.qxf | 91 +++++++ .../fixtures/Tecshow/Tecshow-Nebula-6.qxf | 66 +++++ resources/fixtures/beamZ/beamZ-Radical-II.qxf | 74 ++++++ 10 files changed, 964 insertions(+), 2 deletions(-) create mode 100644 resources/fixtures/Briteq/Briteq-BT-Theatre-HD2.qxf create mode 100644 resources/fixtures/Chauvet/Chauvet-COLORband-Q3BT.qxf create mode 100644 resources/fixtures/Chauvet/Chauvet-Intimidator-Wash-Zoom-450-IRC.qxf create mode 100644 resources/fixtures/Eurolite/Eurolite-LED-PARty-TCL-spot.qxf create mode 100644 resources/fixtures/Expolite/Expolite-TourSpot-50-Mini.qxf create mode 100644 resources/fixtures/Fun-Generation/Fun-Generation-LED-Pot-12x1W-QCL-RGB-WW.qxf create mode 100644 resources/fixtures/Tecshow/Tecshow-Nebula-6.qxf create mode 100644 resources/fixtures/beamZ/beamZ-Radical-II.qxf diff --git a/debian/changelog b/debian/changelog index a202aade72..db3d78b012 100644 --- a/debian/changelog +++ b/debian/changelog @@ -57,16 +57,20 @@ qlcplus (4.13.0) stable; urgency=low * New fixture: Shehds GalaxyJet Waterproof IP65 380W 19R Beam Moving Head (thanks to István Király, Feiyu Shehds) * New fixture: Martin Ego X6 (thanks to Michael Tosatto) * New fixture: Blizzard Lighting LB Hex Unplugged (thanks to David Sparks) - * New fixture: Eurolite LED Strobe SMD PRO 132 DMX RGB (thanks to Fede79) + * New fixtures: Eurolite LED Strobe SMD PRO 132 DMX RGB, Briteq BT Theatre HD2 (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 (thanks to Harrison Bostock) + * 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 fixture: Chauvet COLORband Q3BT (thanks to Paul Schuh) -- Massimo Callegari Sun, 10 Mar 2024 12:13:14 +0200 diff --git a/resources/fixtures/Briteq/Briteq-BT-Theatre-HD2.qxf b/resources/fixtures/Briteq/Briteq-BT-Theatre-HD2.qxf new file mode 100644 index 0000000000..84fbc9b523 --- /dev/null +++ b/resources/fixtures/Briteq/Briteq-BT-Theatre-HD2.qxf @@ -0,0 +1,221 @@ + + + + + Q Light Controller Plus + 4.12.7 + Fede79 + + Briteq + BT Theatre HD2 + Color Changer + + + + + Colour + No function + 1800K + 2200K + 2700K + 3000K + 3200K + 4000K + 4500K + 5000K + 5600K + 6000K + 6500K + 7000K + 8000K + 10000K + + + Shutter + No function + Strobe from slow to fast 0-25Hz + No function + Thunder Strobe + No function + Random Strobe + + + + + + + + + + Effect + No function + L176 Loving Amber + R40 Light Salmon + L024 Scarlet + L164 Flame Red + L747 Easy White + R303 Warm Peach + L008 Dark Salmon + L025 Sunset Red + L004 Medium Bastard Amber + L237 C.I.D. (To Tungsten) + R321 Soft Golden Amber + L652 Urban Sodium + L212 L.C.T. Yellow (Y1) + L765 LEE Yellow + L513 Ice and a Slice + L100 Spring Yellow + L244 LEE Plus Green + R4430 Cal Color 30 Green + L122 Fern Green + L090 Dark Yellow Green + L243 LEE Fluorescent 3600K + R92 Turquoise + R94 Kelly Green + L327 Forest Green + L191 Cosmetic Aqua Blue + L728 Steel Green + L117 Steel Blue + L354 Special Steel Blue + L053 Paler Lavender + L501 New Color Blue + L174 Dark Steel Blue + L165 Daylight Blue + L136 Pale Lavender + L194 Surprise Pink + L142 Pale Violet + L700 Perfect Lavender + L035 Light Pink + L794 Pretty n Pink + L328 Follies Pink + L795 Magical Magenta + L154 Pale Rose + L127 Smokey Pink + L192 Flesh Pink + L332 Special Rose Pink + L790 Moroccan Pink + L157 Pink + R332 Cherry Rose + L128 Bright Pink + No Function + + + Effect + No function + Auto 1 + Auto 2 + Auto3 + Auto4 + Auto5 + Auto6 + Auto7 + Auto8 + Auto9 + Auto10 + Program 1 + Program 2 + No Function + + + Speed + Auto Speed Adjustement + + + + + + + + + Maintenance + No Function + Reserved + Reserved + Reserved + Reserved + Reserved + Fan Mode = Live + Fan Mode = Studio + Fan Mode = Power + Reserved + Dimmer Mode = Off + Dimmer Mode = Dim4 + Reserved + Reserved + Reserved + LED PWM = 1200Hz + LED PWM = 2400Hz + LED PWM = 4000Hz + LED PWM = 6000Hz + LED PWM = 25000Hz + All Reset + Zoom Reset + Reserved + Reserved + Reserved + + + Master Dimmer + Hue + Hue Fine + Saturation + CCT + Strobe + Zoom + Control + + + Master Dimmer + Red + Green + Blue + Amber + Lime + Strobe + Zoom + Control + + + Master Dimmer + Red + Green + Blue + Amber + Lime + Preset Color + CCT + Strobe + Zoom + Auto + Auto Speed Adjustment + Control + + + Master Dimmer + Master Dimmer Fine + Red + Red Fine + Green + Green Fine + Blue + Blue Fine + Amber + Amber Fine + Lime + Lime Fine + Preset Color + CCT + Strobe + Zoom + Auto + Auto Speed Adjustment + Control + + + + + + + + + diff --git a/resources/fixtures/Chauvet/Chauvet-COLORband-Q3BT.qxf b/resources/fixtures/Chauvet/Chauvet-COLORband-Q3BT.qxf new file mode 100644 index 0000000000..222fcbe3ba --- /dev/null +++ b/resources/fixtures/Chauvet/Chauvet-COLORband-Q3BT.qxf @@ -0,0 +1,108 @@ + + + + + Q Light Controller Plus + 4.13.0 GIT + Paul Schuh + + Chauvet + COLORband Q3BT + LED Bar (Beams) + + + + + + + + + + + + + + Shutter + No function + Strobe - slow to fast + + + Effect + No Function + Auto Program 1 + Auto Program 2 + Auto Program 3 + Auto Program 4 + Auto Program 5 + Auto Program 6 + Auto Program 7 + Auto Program 8 + + + Speed + Program Speed - slow to fast + Sound active mode + + + + + + + + Red 1 + Green 1 + Blue 1 + Amber 1 + Red 2 + Green 2 + Blue 2 + Amber 2 + Red 3 + Green 3 + Blue 3 + Amber 3 + Strobe + Auto Programs + Auto Speed + Dimmer + + 0 + 1 + 2 + 3 + + + 4 + 5 + 6 + 7 + + + 8 + 9 + 10 + 11 + + + + Red + Green + Blue + Amber + Dimmer + Strobe + + + Red + Green + Blue + Amber + + + + + + + + + diff --git a/resources/fixtures/Chauvet/Chauvet-Intimidator-Wash-Zoom-450-IRC.qxf b/resources/fixtures/Chauvet/Chauvet-Intimidator-Wash-Zoom-450-IRC.qxf new file mode 100644 index 0000000000..574db54d51 --- /dev/null +++ b/resources/fixtures/Chauvet/Chauvet-Intimidator-Wash-Zoom-450-IRC.qxf @@ -0,0 +1,234 @@ + + + + + Q Light Controller Plus + 4.13.0 GIT + Harrison Bostock + + Chauvet + Intimidator Wash Zoom 450 IRC + Moving Head + + + + + + + + + + + + + + + + + + + + + + + Colour + No function + Color 1 + Color 2 + Color 3 + Color 4 + Color 5 + Color 6 + Color 7 + Color 8 + Color 9 + Color 10 + Color 11 + Color 12 + Color 13 + Color 14 + Color 15 + Color 16 + Color 17 + Color 18 + Color 19 + Color 20 + Color 21 + Color 22 + Color 23 + Color 24 + Color 25 + Color 26 + Color 27 + Color 28 + Color 29 + Color 30 + Color 31 + Color 32 + Color 33 + Color 34 + No function + Clockwise color change (fast to slow) + Stop (color stays in the current color) + Counter-clockwise color change (fast to slow) + No function + Color jump (fast to slow) + Sound-Active colors + + + Maintenance + Nothing + Zone macro 1 + Zone macro 2 + Zone macro 3 + Zone macro 4 + Zone macro 5 + Zone macro 6 + Zone macro 7 (macro 1-6) + Built-in Auto 1 + Built-in Auto 2 + Built-in Auto 3 + Built-in Auto 4 + Built-in Auto 5 + Built-in Auto 6 + Built-in Auto 7 + Built-in Auto 8 (Built-in Auto 1-7) + + + Speed + Zone macros and Built-in auto speed (slow to fast) + + + + Shutter + Closed + Open + Strobe Fast-slow + Open + Fast on slow off (fast slow) + Open + Slow on, Fast off (fast to slow) + Open + Random shutter (fast to slow) + Open + Random fast on slow off (fast to slow) + Open + Random slow on fast off (fast to slow) + Open + pulse effect 1 (fast to slow) + Open + Pulse effect 2 (fast to slow) + Open + Gradually on and off (fast to slow) + Open + pulse effect 3 (fast to slow) + Open + + + + Maintenance + Nothing + Blackout while panning/tilting + Nothing + Reserved for future use + Reset pan + Reset tilt + Reset zoom + Reset rotation + Reset all + Nothing + Reverse pan/tilt + Reverse pan + Reverse tilt + Cancel reverse pan + Cancel reverse tilt + Cancel reverse pan/tilt + Pan/tilt normal speed + Pan/tilt fast speed + Pan/tilt slow speed + Fan full speed + Fan auto speed + Dimmer fast + Dimmer smooth + Nothing + + + + Pan + Pan fine + Tilt + Tilt fine + Pan/Tilt speed + Red + Green + Blue + White + Red 1 + Green 1 + Blue 1 + White 1 + Red 2 + Green 2 + Blue 2 + White 2 + Red 3 + Green 3 + Blue 3 + White 3 + Color macro + Builtin Control + Builtin Speed + Dimmer + Shutter + Zoom + Control function + No function + + 5 + 6 + 7 + 8 + + + 9 + 10 + 11 + 12 + + + 13 + 14 + 15 + 16 + + + 17 + 18 + 19 + 20 + + + + Pan + Tilt + Pan/Tilt speed + Red + Green + Blue + White + Builtin Control + Builtin Speed + Dimmer + Shutter + Zoom + Control function + No function + + + + + + + + + diff --git a/resources/fixtures/Eurolite/Eurolite-LED-PARty-TCL-spot.qxf b/resources/fixtures/Eurolite/Eurolite-LED-PARty-TCL-spot.qxf new file mode 100644 index 0000000000..bf9100df8e --- /dev/null +++ b/resources/fixtures/Eurolite/Eurolite-LED-PARty-TCL-spot.qxf @@ -0,0 +1,37 @@ + + + + + Q Light Controller Plus + 4.13.0 GIT + Christian Prison + + Eurolite + LED PARty TCL spot + Color Changer + + + + + + Effect + No function + Sound control + No function + Strobe (slow to fast) + + + Red + Green + Blue + Dimmer + Functions + + + + + + + + + diff --git a/resources/fixtures/Expolite/Expolite-TourSpot-50-Mini.qxf b/resources/fixtures/Expolite/Expolite-TourSpot-50-Mini.qxf new file mode 100644 index 0000000000..e2b4afdd59 --- /dev/null +++ b/resources/fixtures/Expolite/Expolite-TourSpot-50-Mini.qxf @@ -0,0 +1,117 @@ + + + + + Q Light Controller Plus + 4.13.0 GIT + Christian Prison + + Expolite + TourSpot 50 Mini + Moving Head + + + + + + + Gobo + No function + Forward gobo rotation (fast to slow) + Gobo rotation stop + Backward gobo rotation (slow to fast) + + + Shutter + Shutter closed + Shutter open + Strobe effect (slow to fast) + Shutter open + Pulse-effect in sequences (slow to fast) + Shutter open + Random strobe effect (slow to fast) + Shutter open + + + Speed + speed (min to max) + blackout by movement + blackout by all wheel changing + no function + + + + + Prism + OFF + ON + rotating (slow to fast) + + + Maintenance + no function + reset all motors + reset scan motor + reset color motor + reset gobo motor + reset shutter&dimmer motor + reset other motors + internal program 1 + internal program 2 + internal program 3 + internal program 4 + internal program 5 + internal program 6 + internal program 7 + internal sound program 1 + + + Colour + Open / white + Red + Cyan + Green + Yellow + Rose carmine + Blue + Orange + Forward rainbow effect (fast..slow) + Color rotation stop + Backwards rainbow effect (slow..fast) + + + Pan + Pan fine + Tilt + Tilt fine + Speed pan/tilt + Color Wheel + Gobo wheel + Gobo rotation + Shutter + Dimmer + Focus + Prism & Rotating + Special Function + + + Pan + Tilt + Speed pan/tilt + Color Wheel + Gobo wheel + Gobo rotation + Shutter + Dimmer + Focus + Prism & Rotating + Special Function + + + + + + + + + diff --git a/resources/fixtures/FixturesMap.xml b/resources/fixtures/FixturesMap.xml index 45af518799..36a7bd05b9 100644 --- a/resources/fixtures/FixturesMap.xml +++ b/resources/fixtures/FixturesMap.xml @@ -234,6 +234,7 @@ + @@ -293,6 +294,7 @@ + @@ -397,6 +399,7 @@ + @@ -466,6 +469,7 @@ + @@ -760,6 +764,7 @@ + @@ -843,6 +848,7 @@ + @@ -859,6 +865,7 @@ + @@ -1680,6 +1687,9 @@ + + + diff --git a/resources/fixtures/Fun-Generation/Fun-Generation-LED-Pot-12x1W-QCL-RGB-WW.qxf b/resources/fixtures/Fun-Generation/Fun-Generation-LED-Pot-12x1W-QCL-RGB-WW.qxf new file mode 100644 index 0000000000..20861cc347 --- /dev/null +++ b/resources/fixtures/Fun-Generation/Fun-Generation-LED-Pot-12x1W-QCL-RGB-WW.qxf @@ -0,0 +1,91 @@ + + + + + Q Light Controller Plus + 4.13.0 GIT + Christian Prison + + Fun Generation + LED Pot 12x1W QCL RGB WW + Color Changer + + + + + + + Effect + No function + Program 1 + Program 2 + Program 3 + Program 4 + Program 5 + Program 6 + Program 7 + Program 8 + Program 9 + Program 10 + Program 11 + Program 12 + Program 13 + Program 14 + Music controlled mode + + + Colour + RGBW 0000 if ch6=1..16 + RGBW 255000 if ch6=1..16 + RGBW 025500 if ch6=1..16 + RGBW 002550 if ch6=1..16 + RGBW 000255 if ch6=1..16 + RGBW 25515000 if ch6=1..16 + RGBW 25518000 if ch6=1..16 + RGBW 25525500 if ch6=1..16 + RGBW 25502550 if ch6=1..16 + RGBW 25501400 if ch6=1..16 + RGBW 02552550 if ch6=1..16 + RGBW 25500210 if ch6=1..16 + RGBW 02550210 if ch6=1..16 + RGBW 00255210 if ch6=1..16 + RGBW 255200040090 if ch6=1..16 + RGB 255255255255 if ch6=1..16 + + + Shutter + no function + Strobe effect (0 ... 100%) + + + Dimmer + Red + Green + Blue + White + Program selection + Colour macro program 01 + Strobe + + + Red + Green + Blue + White + + + Dimmer + Red + Green + Blue + White + Strobe + + + + + + + + + diff --git a/resources/fixtures/Tecshow/Tecshow-Nebula-6.qxf b/resources/fixtures/Tecshow/Tecshow-Nebula-6.qxf new file mode 100644 index 0000000000..6a9267330d --- /dev/null +++ b/resources/fixtures/Tecshow/Tecshow-Nebula-6.qxf @@ -0,0 +1,66 @@ + + + + + Q Light Controller Plus + 4.13.0 GIT + Mariano Olmedo (edit by Fedegh) + + Tecshow + Nebula 6 + Color Changer + + + + + + + + + Shutter + No function + Strobe (from slow to fast) + Strobe (from fast to slow) + Strobe effect 1 + Strobe effect 2 + Strobe, the fastest strobe effect + + + Colour + No function + RGBAUvW + + + Effect + No function + Color auto speed + Sound control + + + Red + Green + Blue + White + Amber + UV + Master dimmer + Strobe + Color walking + Sound Control + + + Red + Green + Blue + White + Amber + UV + + + + + + + + + diff --git a/resources/fixtures/beamZ/beamZ-Radical-II.qxf b/resources/fixtures/beamZ/beamZ-Radical-II.qxf new file mode 100644 index 0000000000..2eca2c5982 --- /dev/null +++ b/resources/fixtures/beamZ/beamZ-Radical-II.qxf @@ -0,0 +1,74 @@ + + + + + Q Light Controller Plus + 4.13.0 GIT + Matt Muller + + beamZ + Radical II + Scanner + + Effect + No Function + Laser motor clockwise rotation(From slow to fast) + Stop + Laser motor counter clockwise rotation(From slow to fast) + Stop + back and forward movement with increasing speed + + + Pan + Motor location + back and forward movement with increasing speed + + + Colour + No Fuction + Red + Green + Blue + White + Red + Green + Green + Blue + Blue + White + Red + White + Red + Blue + Green + White + Blue + Green + White + Red + Blue + White + Red + Green + White + Red + Green + Blue + All LED on + Rainbow Group effect 1 + Rainbow Group effect 2 + Rainbow Group effect 3 + Rainbow Group effect 4 + Rainbow Group effect 5 + Rainbow Group effect with strobe 1 + Rainbow Group effect with strobe 2 + Rainbow Group effect with strobe 3 + Rainbow Group effect with strobe 4 + Rainbow Group effect with strobe 5 + + + Effect + No function + Auto DMX ( From slow to fast ) + Sound DMX ( From slow to fast ) + + + Laser + Motor rotation + Colours macro + Auto/Sound + + + + + + + + + From 7cb12df20bf1cc5083bb906a2c8a62e2b5661949 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Mon, 26 Feb 2024 12:55:24 +0100 Subject: [PATCH 083/212] resources: 6 new fixtures (see changelog) --- debian/changelog | 6 +- .../BoomToneDJ/BoomToneDJ-KUB-500-RGB.qxf | 97 ++ .../Eliminator-Lighting-Stealth-Beam.qxf | 120 ++ .../Eliminator-Lighting-Stealth-Wash-Zoom.qxf | 106 ++ .../fixtures/Eurolite/Eurolite-KLS-180-6.qxf | 600 +++++++++ resources/fixtures/FixturesMap.xml | 6 + .../Shehds-LED-Beam+Wash-19x15W-RGBW-Zoom.qxf | 128 ++ .../Varytec/Varytec-Blitz-Bar-240.qxf | 1109 +++++++++++++++++ 8 files changed, 2170 insertions(+), 2 deletions(-) create mode 100644 resources/fixtures/BoomToneDJ/BoomToneDJ-KUB-500-RGB.qxf create mode 100644 resources/fixtures/Eliminator_Lighting/Eliminator-Lighting-Stealth-Beam.qxf create mode 100644 resources/fixtures/Eliminator_Lighting/Eliminator-Lighting-Stealth-Wash-Zoom.qxf create mode 100644 resources/fixtures/Eurolite/Eurolite-KLS-180-6.qxf create mode 100644 resources/fixtures/Shehds/Shehds-LED-Beam+Wash-19x15W-RGBW-Zoom.qxf create mode 100644 resources/fixtures/Varytec/Varytec-Blitz-Bar-240.qxf diff --git a/debian/changelog b/debian/changelog index db3d78b012..f4b588d0d8 100644 --- a/debian/changelog +++ b/debian/changelog @@ -57,7 +57,7 @@ qlcplus (4.13.0) stable; urgency=low * New fixture: Shehds GalaxyJet Waterproof IP65 380W 19R Beam Moving Head (thanks to István Király, Feiyu Shehds) * New fixture: Martin Ego X6 (thanks to Michael Tosatto) * New fixture: Blizzard Lighting LB Hex Unplugged (thanks to David Sparks) - * New fixtures: Eurolite LED Strobe SMD PRO 132 DMX RGB, Briteq BT Theatre HD2 (thanks to Fede79) + * New fixtures: Eurolite LED Strobe SMD PRO 132 DMX RGB, Briteq BT Theatre HD2, Eurolite KLS-180-6, BoomToneDJ KUB 500 RGB (thanks to Fede79) * New fixture: Eurolite LED PLL-480 CW/WW (thanks to Benjamin Drung) * New fixture: Robe Spiider (thanks to Nicolò) * New fixture: Betopper LB230 (thanks to Viktor) @@ -70,7 +70,9 @@ qlcplus (4.13.0) stable; urgency=low * 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 fixture: Chauvet COLORband Q3BT (thanks to Paul Schuh) + * 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) -- Massimo Callegari Sun, 10 Mar 2024 12:13:14 +0200 diff --git a/resources/fixtures/BoomToneDJ/BoomToneDJ-KUB-500-RGB.qxf b/resources/fixtures/BoomToneDJ/BoomToneDJ-KUB-500-RGB.qxf new file mode 100644 index 0000000000..a2f7b4ee8c --- /dev/null +++ b/resources/fixtures/BoomToneDJ/BoomToneDJ-KUB-500-RGB.qxf @@ -0,0 +1,97 @@ + + + + + Q Light Controller Plus + 4.13.0 GIT + Fede79 + + BoomToneDJ + KUB 500 RGB + Laser + + Maintenance + Laser light off + DMX control mode + Automatic control mode + Line effect automatic control mode + Animation effect sound control mode + Automatic effect sound control mode + Manual control sound mode + + + Gobo + Pattern group A + + + Gobo + Pattern group B + + + Gobo + Rotation angle adjustment + Rotation clockwise rotation(from slow to fast) + Rotation anti-clockwise rotation(from slow to fast) + + + Gobo + Left and right rotation angle selection + Left and right reverse rotation speed (from slow to fast) + + + Gobo + Up and down rotation angle selection + Up and down reverse rotation speed adjustment(from slow to fast) + + + Gobo + Left and right moving angle selection + Left and right moving speed (from slow to fast) + + + Gobo + Up and down moving angle selection + Up and down moving speed(from slow to fast) + + + Beam + Size adjustment + Size zooming speed (from big to small) + Size zooming speed (from small to big) + Zooming (from slow to fast) + + + Effect + Running gradual drawing effect(from slow to fast) + + + Speed + Scanning speed adjustment + Running dot effect + + + Colour + Laser color selection + + + Pattern controlling mode + Pattern selection A + Pattern selection B + Pattern rotation + Pattern left and right reverse rotation + Pattern up and down reverse rotation + Pattern left and right movement + Pattern up and down movement + Pattern size + Pattern gradual drawing + Pattern controlling + Color selection + + + + + + + + + diff --git a/resources/fixtures/Eliminator_Lighting/Eliminator-Lighting-Stealth-Beam.qxf b/resources/fixtures/Eliminator_Lighting/Eliminator-Lighting-Stealth-Beam.qxf new file mode 100644 index 0000000000..fee62c2818 --- /dev/null +++ b/resources/fixtures/Eliminator_Lighting/Eliminator-Lighting-Stealth-Beam.qxf @@ -0,0 +1,120 @@ + + + + + Q Light Controller Plus + 4.13.0 GIT + Paul Schuh + + Eliminator Lighting + Stealth Beam + Moving Head + + + + Colour + Nothing + Red + Green + Blue + White + Red/Green + Green/Blue + Blue/White + Red/White + Red/Green/Blue + Green/Blue/White + Red/Blue/White + Red/Green/White + Red/Green/Blue/White + Macro Color Change + Macro Color Line + Sound Active Change + + + Speed + Slow - Fast + + + Effect + Nothing + Program 1 + Program 2 + Program 3 + Program 4 + Program 5 + Program 6 + Program 7 + Program 8 + Program 9 + Sond active + + + + + + + + + + Shutter + Shutter Closed + Strobing Slow-Fast + Shutter Open + + + Effect + Nothing + Pan moves clockwise (slow to fast) + Pan moves counter-clockwise (fast to slow) + + + Effect + Nothing + Tilt moves clockwise (slow to fast) + Tilt moves counter-clockwise (fast to slow) + + + Speed + Nothing + Speed mode to high (10s) + Nothing + Speed mode to low (10s) + Nothing + Reset (will take 10s to reset) + + + + Pan + Tilt + Color Effect + Color change/Fade Speed + Pan/Tilt Programs + + + Pan + Pan Fine + Tilt + Tilt Fine + Red + Green + Blue + White + Color Effect + Master Dimmer + Shutter/Strobe + Pan Movement + Pan Movement Speed + Tilt Movement + Color change/Fade Speed + Pan/Tilt Programs + Pan/Tilt Program Speed + + + + + + + + + diff --git a/resources/fixtures/Eliminator_Lighting/Eliminator-Lighting-Stealth-Wash-Zoom.qxf b/resources/fixtures/Eliminator_Lighting/Eliminator-Lighting-Stealth-Wash-Zoom.qxf new file mode 100644 index 0000000000..90aebec270 --- /dev/null +++ b/resources/fixtures/Eliminator_Lighting/Eliminator-Lighting-Stealth-Wash-Zoom.qxf @@ -0,0 +1,106 @@ + + + + + Q Light Controller Plus + 4.13.0 GIT + Paul Schuh + + Eliminator Lighting + Stealth Wash Zoom + Moving Head + + + + Colour + Nothing + Red + Green + Blue + White + Red + Green + Green + Blue + Blue + White + Red + Blue + Green + White + Red + White + Red + Green + Blue + Red + Green + White + Green + Blue + White + Red + Green + Blue + White + Color Change - Slow to Fast + Nothing + Color fade - Slow to Fast + + + Effect + Nothing + Program 1 + Program 2 + Program 3 + Program 4 + Program 5 + Program 6 + Program 7 + Program 8 + Nothing + Motor Reset + Nothing + + + + + + + + + + + Shutter + Shutter Closed + Strobing - Slow to Fast + Shutter Open + + + Colour + Color Temperatures + + + Speed + Nothing + Color fade - slow to fast + + + + Pan + Tilt + Colors + Internal Programs + Focus + + + Pan + Pan Fine + Tilt + Tilt Fine + Red + Green + Blue + White + Master Dimmer + Shutter/Strobing + Focus + Color Temperatures + Colors + Color Fade + Pan/Tilt Speed + Internal Programs + + + + + + + + + diff --git a/resources/fixtures/Eurolite/Eurolite-KLS-180-6.qxf b/resources/fixtures/Eurolite/Eurolite-KLS-180-6.qxf new file mode 100644 index 0000000000..07720b5f24 --- /dev/null +++ b/resources/fixtures/Eurolite/Eurolite-KLS-180-6.qxf @@ -0,0 +1,600 @@ + + + + + Q Light Controller Plus + 4.13.0 GIT + Fede79 + + Eurolite + KLS-180-6 + Color Changer + + + + + + + Shutter + No function + Strobe slow to fast + + + Colour + No function + Red + Green + Blue + White + Yellow + Magenta + Cyan + Dark orange + Green yellow + Salmon + Turquoise + Light green + Orange + Straw + Lavender + Light blue + Dark blue + Pink + All on + + + Shutter + No function + Strobe slow to fast + + + Effect + No function + Different patterns + Different patterns with increasing speed + + + Effect + No function + Auto program 1,slow > fast + Auto program 2,slow > fast + Auto program 3,slow > fast + Auto program 4,slow > fast + Sound program 1, slow > fast + Sound program 2, slow > fast + Sound program 3, slow > fast + Sound program 4, slow > fast + + + Colour + No function + Red + Green + Blue + White + Yellow + Magenta + Cyan + Dark orange + Green yellow + Salmon + Turquoise + Light green + Orange + Straw + Lavender + Light blue + Dark blue + Pink + All on + + + Colour + No function + Red + Green + Blue + White + Yellow + Magenta + Cyan + Dark orange + Green yellow + Salmon + Turquoise + Light green + Orange + Straw + Lavender + Light blue + Dark blue + Pink + All on + + + Colour + No function + Red + Green + Blue + White + Yellow + Magenta + Cyan + Dark orange + Green yellow + Salmon + Turquoise + Light green + Orange + Straw + Lavender + Light blue + Dark blue + Pink + All on + + + Colour + No function + Red + Green + Blue + White + Yellow + Magenta + Cyan + Dark orange + Green yellow + Salmon + Turquoise + Light green + Orange + Straw + Lavender + Light blue + Dark blue + Pink + All on + + + Effect + No function + Auto mode via DMX with increasing speed + Sound control via DMX with increasing speed + + + Colour + No function + Red + Green + Blue + White + Yellow + Magenta + Cyan + Dark orange + Green yellow + Salmon + Turquoise + Light green + Orange + Straw + Lavender + Light blue + Dark blue + Pink + All on + + + Colour + No function + Red + Green + Blue + White + Yellow + Magenta + Cyan + Dark orange + Green yellow + Salmon + Turquoise + Light green + Orange + Straw + Lavender + Light blue + Dark blue + Pink + All on + + + Colour + No function + Red + Green + Blue + White + Yellow + Magenta + Cyan + Dark orange + Green yellow + Salmon + Turquoise + Light green + Orange + Straw + Lavender + Light blue + Dark blue + Pink + All on + + + Colour + No function + Red + Green + Blue + White + Yellow + Magenta + Cyan + Dark orange + Green yellow + Salmon + Turquoise + Light green + Orange + Straw + Lavender + Light blue + Dark blue + Pink + All on + + + Effect + Internal program with increasing speed + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Red + Green + Blue + + + Red + Green + Blue + White + + + Master dimmer + Spots Strobe Effect + Spots Preset colors + Bar Strobe Effect + Bar + Internal programs + + + + + + + + + + + Master dimmer + Spots Strobe Effect + Spot 1 Preset colors + Spot 2+3 Preset colors + Spot 4+5 Preset colors + Spot 6 Preset colors + Bar Strobe Effect + Programs via DMX + + 2 + + + 3 + + + 4 + + + 5 + + + + Master dimmer + Spots Strobe Effect + Red + Green + Blue + White + Bar Strobe Effect + Bar + Internal programs + + + + + + + + + + + Master dimmer + Spots Strobe Effect + Spot 1 Preset colors + Spot 2 Preset colors + Spot 3 Preset colors + Spot 4 Preset colors + Spot 5 Preset colors + Spot 6 Preset colors + Bar Strobe Effect + Bar + Internal programs + + 2 + + + 3 + + + 4 + + + 5 + + + 6 + + + 7 + + + + + + + + + + + + Master dimmer + Spots Strobe Effect + Bar Program + Bar Strobe Effect + Red Spot 1 + Green Spot 1 + Blue Spot 1 + White Spot 1 + Red Spot 2+3 + Green Spot 2+3 + Blue Spot 2+3 + White Spot 2+3 + Red Spot 4+5 + Green Spot 4+5 + Blue Spot 4+5 + White Spot 4+5 + Red Spot 6 + Green Spot 6 + Blue Spot 6 + White Spot 6 + Programs via DMX + + 4 + 5 + 6 + 7 + + + 8 + 9 + 10 + 11 + + + 12 + 13 + 14 + 15 + + + 16 + 17 + 18 + 19 + + + + + + + + + + + + Red Spot 1 + Green Spot 1 + Blue Spot 1 + White Spot 1 + Red Spot 2 + Green Spot 2 + Blue Spot 2 + White Spot 2 + Red Spot 3 + Green Spot 3 + Blue Spot 3 + White Spot 3 + Red Spot 4 + Green Spot 4 + Blue Spot 4 + White Spot 4 + Red Spot 5 + Green Spot 5 + Blue Spot 5 + White Spot 5 + Red Spot 6 + Green Spot 6 + Blue Spot 6 + White Spot 6 + + 0 + 1 + 2 + 3 + + + 4 + 5 + 6 + 7 + + + 8 + 9 + 10 + 11 + + + 12 + 13 + 14 + 15 + + + 16 + 17 + 18 + 19 + + + 20 + 21 + 22 + 23 + + + + + + + + + + + + Master dimmer + Spots Strobe Effect + Red Spot 1 + Green Spot 1 + Blue Spot 1 + White Spot 1 + Red Spot 2 + Green Spot 2 + Blue Spot 2 + White Spot 2 + Red Spot 3 + Green Spot 3 + Blue Spot 3 + White Spot 3 + Red Spot 4 + Green Spot 4 + Blue Spot 4 + White Spot 4 + Red Spot 5 + Green Spot 5 + Blue Spot 5 + White Spot 5 + Red Spot 6 + Green Spot 6 + Blue Spot 6 + White Spot 6 + Bar Strobe Effect + Bar + Internal programs + + 2 + 3 + 4 + 5 + + + 6 + 7 + 8 + 9 + + + 10 + 11 + 12 + 13 + + + 14 + 15 + 16 + 17 + + + 18 + 19 + 20 + 21 + + + 22 + 23 + 24 + 25 + + + + + + + + + + diff --git a/resources/fixtures/FixturesMap.xml b/resources/fixtures/FixturesMap.xml index 36a7bd05b9..c371251876 100644 --- a/resources/fixtures/FixturesMap.xml +++ b/resources/fixtures/FixturesMap.xml @@ -270,6 +270,7 @@ + @@ -685,6 +686,8 @@ + + @@ -733,6 +736,7 @@ + @@ -1463,6 +1467,7 @@ + @@ -1724,6 +1729,7 @@ + diff --git a/resources/fixtures/Shehds/Shehds-LED-Beam+Wash-19x15W-RGBW-Zoom.qxf b/resources/fixtures/Shehds/Shehds-LED-Beam+Wash-19x15W-RGBW-Zoom.qxf new file mode 100644 index 0000000000..0cd8bc3804 --- /dev/null +++ b/resources/fixtures/Shehds/Shehds-LED-Beam+Wash-19x15W-RGBW-Zoom.qxf @@ -0,0 +1,128 @@ + + + + + Q Light Controller Plus + 4.13.0 GIT + Paul Schuh + + Shehds + LED Beam+Wash 19x15W RGBW Zoom Lighting + Moving Head + + + + + + + + + + + + + + + + + + + + + + Effect + Preset Color + Program Jump + Program Gradient + Program Pulse + + + Speed + Program Speed Adjustment + + + Effect + No Function + Program Activation + Self-propelled mode (1) + Self-propelled mode (2) + Audio control mode + + + Maintenance + Reset + + + + + + + X axis rotation + X-axis fine-tuning + Y axis rotation + Y-axis fine-tuning + XY speed adjustment + Master Dimmer + Strobe + Red Dimmer + Green Dimmer + Blue Dimmer + White Dimmer + Focus + Program Selection + Program Speed + Program Activation + Reset + + + X axis rotation + X-axis fine-tuning + Y axis rotation + Y-axis fine-tuning + XY speed adjustment + Focus + Master Dimmer + Strobe + Red 1 Dimmer + Green 1 Dimmer + Blue 1 Dimmer + White 1 Dimmer + Red 2 Dimmer + Green 2 Dimmer + Blue 2 Dimmer + White 2 Dimmer + Red 3 Dimmer + Green 3 Dimmer + Blue 3 Dimmer + White 3 Dimmer + Program Selection + Program Speed + Program Activation + Reset + + 8 + 9 + 10 + 11 + + + 12 + 13 + 14 + 15 + + + 16 + 17 + 18 + 19 + + + + + + + + + + diff --git a/resources/fixtures/Varytec/Varytec-Blitz-Bar-240.qxf b/resources/fixtures/Varytec/Varytec-Blitz-Bar-240.qxf new file mode 100644 index 0000000000..fdea131838 --- /dev/null +++ b/resources/fixtures/Varytec/Varytec-Blitz-Bar-240.qxf @@ -0,0 +1,1109 @@ + + + + + Q Light Controller Plus + 4.13.0 GIT + Stefan Lohmann + + Varytec + Blitz Bar 240 + LED Bar (Pixels) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Intensity + Red + Red + + + Intensity + Green + Green + + + Intensity + Blue + Blue + + + + Effect + No function + Automatic show white 1 + Automatic show white 2 + Automatic show white 3 + Automatic show white 4 + Automatic show white 5 + Automatic show white 6 + Automatic show white 7 + Automatic show white 8 + Automatic show white 9 + Automatic show white 10 + Automatic show white 11 + Automatic show white 12 + Automatic show white 13 + Automatic show white 14 + Automatic show white 15 + Automatic show white 16 + Automatic show white 17 + Automatic show white 18 + Automatic show white 19 + Automatic show white 20 + + + Speed + Running speed of automatic show white, increasing + + + Effect + No function + Automatic show RGB 1 + Automatic show RGB 2 + Automatic show RGB 3 + Automatic show RGB 4 + Automatic show RGB 5 + Automatic show RGB 6 + Automatic show RGB 7 + Automatic show RGB 8 + Automatic show RGB 9 + Automatic show RGB 10 + Automatic show RGB 11 + Automatic show RGB 12 + Automatic show RGB 13 + Automatic show RGB 14 + Automatic show RGB 15 + Automatic show RGB 16 + Automatic show RGB 17 + Automatic show RGB 18 + Automatic show RGB 19 + Automatic show RGB 20 + Automatic show RGB 21 + Automatic show RGB 22 + Automatic show RGB 23 + Automatic show RGB 24 + Automatic show RGB 25 + Automatic show RGB 26 + Automatic show RGB 27 + Automatic show RGB 28 + Automatic show RGB 29 + Automatic show RGB 30 + Automatic show RGB 31 + Automatic show RGB 32 + Automatic show RGB 33 + Automatic show RGB 34 + Automatic show RGB 35 + Automatic show RGB 36 + Automatic show RGB 37 + Automatic show RGB 38 + Automatic show RGB 39 + Automatic show RGB 40 + Automatic show RGB 41 + Automatic show RGB 42 + Automatic show RGB 43 + Automatic show RGB 44 + Automatic show RGB 45 + Automatic show RGB 46 + Automatic show RGB 47 + Automatic show RGB 48 + Automatic show RGB 49 + Automatic show RGB 50 + Automatic show RGB 51 + Automatic show RGB 52 + Automatic show RGB 53 + Automatic show RGB 54 + Automatic show RGB 55 + Automatic show RGB 56 + Automatic show RGB 57 + Automatic show RGB 58 + Automatic show RGB 59 + Automatic show RGB 60 + Automatic show RGB 61 + Automatic show RGB 62 + + + Speed + Running speed of automatic show RGB, increasing + + + Intensity + White + White + + + Shutter + No function + Strobe, increasing speed + + + Shutter + No function + RGB strobe, increasing speed + + + Intensity + Red + Background red + + + Intensity + Green + Background green + + + Intensity + Blue + Background blue + + + Effect + Direction normal + Direction inverted + + + + + + + + + + + White 1 + White 2 + White 3 + White 4 + White 5 + White 6 + White 7 + White 8 + White 9 + White 10 + White 11 + White 12 + White 13 + White 14 + White 15 + White 16 + White 17 + White 18 + White 19 + White 20 + White 21 + White 22 + White 23 + White 24 + White 25 + White 26 + White 27 + White 28 + White 29 + White 30 + White 31 + White 32 + White 33 + White 34 + White 35 + White 36 + White 37 + White 38 + White 39 + White 40 + Red 1 + Green 1 + Blue 1 + Red 2 + Green 2 + Blue 2 + Red 3 + Green 3 + Blue 3 + Red 4 + Green 4 + Blue 4 + Red 5 + Green 5 + Blue 5 + Red 6 + Green 6 + Blue 6 + Red 7 + Green 7 + Blue 7 + Red 8 + Green 8 + Blue 8 + Red 9 + Green 9 + Blue 9 + Red 10 + Green 10 + Blue 10 + Red 11 + Green 11 + Blue 11 + Red 12 + Green 12 + Blue 12 + Red 13 + Green 13 + Blue 13 + Red 14 + Green 14 + Blue 14 + Red 15 + Green 15 + Blue 15 + Red 16 + Green 16 + Blue 16 + Red 17 + Green 17 + Blue 17 + Red 18 + Green 18 + Blue 18 + Red 19 + Green 19 + Blue 19 + Red 20 + Green 20 + Blue 20 + Red 21 + Green 21 + Blue 21 + Red 22 + Green 22 + Blue 22 + Red 23 + Green 23 + Blue 23 + Red 24 + Green 24 + Blue 24 + Red 25 + Green 25 + Blue 25 + Red 26 + Green 26 + Blue 26 + Red 27 + Green 27 + Blue 27 + Red 28 + Green 28 + Blue 28 + Red 29 + Green 29 + Blue 29 + Red 30 + Green 30 + Blue 30 + Red 31 + Green 31 + Blue 31 + Red 32 + Green 32 + Blue 32 + Red 33 + Green 33 + Blue 33 + Red 34 + Green 34 + Blue 34 + Red 35 + Green 35 + Blue 35 + Red 36 + Green 36 + Blue 36 + Red 37 + Green 37 + Blue 37 + Red 38 + Green 38 + Blue 38 + Red 39 + Green 39 + Blue 39 + Red 40 + Green 40 + Blue 40 + Red 41 + Green 41 + Blue 41 + Red 42 + Green 42 + Blue 42 + Red 43 + Green 43 + Blue 43 + Red 44 + Green 44 + Blue 44 + Red 45 + Green 45 + Blue 45 + Red 46 + Green 46 + Blue 46 + Red 47 + Green 47 + Blue 47 + Red 48 + Green 48 + Blue 48 + Red 49 + Green 49 + Blue 49 + Red 50 + Green 50 + Blue 50 + Red 51 + Green 51 + Blue 51 + Red 52 + Green 52 + Blue 52 + Red 53 + Green 53 + Blue 53 + Red 54 + Green 54 + Blue 54 + Red 55 + Green 55 + Blue 55 + Red 56 + Green 56 + Blue 56 + Red 57 + Green 57 + Blue 57 + Red 58 + Green 58 + Blue 58 + Red 59 + Green 59 + Blue 59 + Red 60 + Green 60 + Blue 60 + Red 61 + Green 61 + Blue 61 + Red 62 + Green 62 + Blue 62 + Red 63 + Green 63 + Blue 63 + Red 64 + Green 64 + Blue 64 + + 0 + + + 1 + + + 2 + + + 3 + + + 4 + + + 5 + + + 6 + + + 7 + + + 8 + + + 9 + + + 10 + + + 11 + + + 12 + + + 13 + + + 14 + + + 15 + + + 16 + + + 17 + + + 18 + + + 19 + + + 20 + + + 21 + + + 22 + + + 23 + + + 24 + + + 25 + + + 26 + + + 27 + + + 28 + + + 29 + + + 30 + + + 31 + + + 32 + + + 33 + + + 34 + + + 35 + + + 36 + + + 37 + + + 38 + + + 39 + + + 40 + 41 + 42 + + + 43 + 44 + 45 + + + 46 + 47 + 48 + + + 49 + 50 + 51 + + + 52 + 53 + 54 + + + 55 + 56 + 57 + + + 58 + 59 + 60 + + + 61 + 62 + 63 + + + 64 + 65 + 66 + + + 67 + 68 + 69 + + + 70 + 71 + 72 + + + 73 + 74 + 75 + + + 76 + 77 + 78 + + + 79 + 80 + 81 + + + 82 + 83 + 84 + + + 85 + 86 + 87 + + + 88 + 89 + 90 + + + 91 + 92 + 93 + + + 94 + 95 + 96 + + + 97 + 98 + 99 + + + 100 + 101 + 102 + + + 103 + 104 + 105 + + + 106 + 107 + 108 + + + 109 + 110 + 111 + + + 112 + 113 + 114 + + + 115 + 116 + 117 + + + 118 + 119 + 120 + + + 121 + 122 + 123 + + + 124 + 125 + 126 + + + 127 + 128 + 129 + + + 130 + 131 + 132 + + + 133 + 134 + 135 + + + 136 + 137 + 138 + + + 139 + 140 + 141 + + + 142 + 143 + 144 + + + 145 + 146 + 147 + + + 148 + 149 + 150 + + + 151 + 152 + 153 + + + 154 + 155 + 156 + + + 157 + 158 + 159 + + + 160 + 161 + 162 + + + 163 + 164 + 165 + + + 166 + 167 + 168 + + + 169 + 170 + 171 + + + 172 + 173 + 174 + + + 175 + 176 + 177 + + + 178 + 179 + 180 + + + 181 + 182 + 183 + + + 184 + 185 + 186 + + + 187 + 188 + 189 + + + 190 + 191 + 192 + + + 193 + 194 + 195 + + + 196 + 197 + 198 + + + 199 + 200 + 201 + + + 202 + 203 + 204 + + + 205 + 206 + 207 + + + 208 + 209 + 210 + + + 211 + 212 + 213 + + + 214 + 215 + 216 + + + 217 + 218 + 219 + + + 220 + 221 + 222 + + + 223 + 224 + 225 + + + 226 + 227 + 228 + + + 229 + 230 + 231 + + + + Red + Green + Blue + Strobe, colours + Automatic show white + Running speed of automatic show white + Automatic show RGB + Running speed of automatic show RGB + + + Red + Green + Blue + White + Automatic show white + Running speed of automatic show white + Strobe White + Automatic show RGB + Running speed of automatic show RGB + Strobe RGB + Background Red + Background Green + Background Blue + RGB Direction + + + + + + + + + + From 03b6b8a8a0df4b6eea2e2a9d5239f5cd134d1544 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Mon, 26 Feb 2024 19:07:32 +0100 Subject: [PATCH 084/212] resources: complete Worlde Easypad 12 profile --- resources/inputprofiles/Worlde-Easypad.12.qxi | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/resources/inputprofiles/Worlde-Easypad.12.qxi b/resources/inputprofiles/Worlde-Easypad.12.qxi index 63f75f1883..3c715e5581 100644 --- a/resources/inputprofiles/Worlde-Easypad.12.qxi +++ b/resources/inputprofiles/Worlde-Easypad.12.qxi @@ -9,6 +9,10 @@ Worlde Easypad.12 MIDI + + XY Fader + Slider + Record Button From 4c97ce8930a1dc9e38531423ad4d8b53fab45960 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Thu, 29 Feb 2024 22:33:14 +0100 Subject: [PATCH 085/212] engine: move feedback data into a dedicated class --- engine/src/CMakeLists.txt | 1 + engine/src/qlcinputchannel.cpp | 42 +++++++-------- engine/src/qlcinputchannel.h | 11 ++-- engine/src/qlcinputfeedback.cpp | 71 +++++++++++++++++++++++++ engine/src/qlcinputfeedback.h | 70 +++++++++++++++++++++++++ engine/src/qlcinputprofile.cpp | 2 +- engine/src/qlcinputsource.cpp | 92 +++++++++++++++++++++------------ engine/src/qlcinputsource.h | 19 ++++--- engine/src/src.pro | 2 + 9 files changed, 242 insertions(+), 68 deletions(-) create mode 100644 engine/src/qlcinputfeedback.cpp create mode 100644 engine/src/qlcinputfeedback.h diff --git a/engine/src/CMakeLists.txt b/engine/src/CMakeLists.txt index 428d31c089..54597e5a91 100644 --- a/engine/src/CMakeLists.txt +++ b/engine/src/CMakeLists.txt @@ -44,6 +44,7 @@ add_library(${module_name} SHARED qlcfixturemode.cpp qlcfixturemode.h qlci18n.cpp qlci18n.h qlcinputchannel.cpp qlcinputchannel.h + qlcinputfeedback.cpp qlcinputfeedback.h qlcinputprofile.cpp qlcinputprofile.h qlcinputsource.cpp qlcinputsource.h qlcmodifierscache.cpp qlcmodifierscache.h diff --git a/engine/src/qlcinputchannel.cpp b/engine/src/qlcinputchannel.cpp index 7dca1a2c94..b4336ce36e 100644 --- a/engine/src/qlcinputchannel.cpp +++ b/engine/src/qlcinputchannel.cpp @@ -34,9 +34,9 @@ QLCInputChannel::QLCInputChannel() , m_movementType(Absolute) , m_movementSensitivity(20) , m_sendExtraPress(false) - , m_lower(0) - , m_upper(UCHAR_MAX) - , m_midiChannel(-1) + , m_lowerValue(0) + , m_upperValue(UCHAR_MAX) + , m_lowerChannel(-1) { } @@ -48,7 +48,7 @@ QLCInputChannel *QLCInputChannel::createCopy() copy->setMovementType(this->movementType()); copy->setMovementSensitivity(this->movementSensitivity()); copy->setSendExtraPress(this->sendExtraPress()); - copy->setMidiChannel(this->midiChannel()); + copy->setLowerChannel(this->lowerChannel()); copy->setRange(this->lowerValue(), this->upperValue()); return copy; @@ -246,43 +246,43 @@ void QLCInputChannel::setRange(uchar lower, uchar upper) uchar QLCInputChannel::lowerValue() const { - return m_lower; + return m_lowerValue; } void QLCInputChannel::setLowerValue(const uchar value) { - if (value == m_lower) + if (value == m_lowerValue) return; - - m_lower = value; + + m_lowerValue = value; emit lowerValueChanged(); } uchar QLCInputChannel::upperValue() const { - return m_upper; + return m_upperValue; } void QLCInputChannel::setUpperValue(const uchar value) { - if (value == m_upper) + if (value == m_upperValue) return; - - m_upper = value; + + m_upperValue = value; emit upperValueChanged(); } -int QLCInputChannel::midiChannel() const +int QLCInputChannel::lowerChannel() const { - return m_midiChannel; + return m_lowerChannel; } -void QLCInputChannel::setMidiChannel(const int channel) +void QLCInputChannel::setLowerChannel(const int channel) { - if (channel == m_midiChannel) + if (channel == m_lowerChannel) return; - - m_midiChannel = channel; + + m_lowerChannel = channel; emit midiChannelChanged(); } @@ -335,7 +335,7 @@ bool QLCInputChannel::loadXML(QXmlStreamReader &root) fbChannel = attrs.value(KXMLQLCInputChannelMidiChannel).toInt(); setRange(min, max); - setMidiChannel(fbChannel); + setLowerChannel(fbChannel); root.skipCurrentElement(); } else @@ -383,8 +383,8 @@ bool QLCInputChannel::saveXML(QXmlStreamWriter *doc, quint32 channelNumber) cons doc->writeAttribute(KXMLQLCInputChannelLowerValue, QString::number(lowerValue())); if (upperValue() != UCHAR_MAX) doc->writeAttribute(KXMLQLCInputChannelUpperValue, QString::number(upperValue())); - if (midiChannel() != -1) - doc->writeAttribute(KXMLQLCInputChannelMidiChannel, QString::number(midiChannel())); + if (lowerChannel() != -1) + doc->writeAttribute(KXMLQLCInputChannelMidiChannel, QString::number(lowerChannel())); doc->writeEndElement(); } diff --git a/engine/src/qlcinputchannel.h b/engine/src/qlcinputchannel.h index 92060e1b97..741ab46106 100644 --- a/engine/src/qlcinputchannel.h +++ b/engine/src/qlcinputchannel.h @@ -191,8 +191,11 @@ class QLCInputChannel : public QObject uchar upperValue() const; void setUpperValue(const uchar value); - int midiChannel() const; - void setMidiChannel(const int channel); + int lowerChannel() const; + void setLowerChannel(const int channel); + + int upperChannel() const; + void setUpperChannel(const int channel); signals: void sendExtraPressChanged(); @@ -202,8 +205,8 @@ class QLCInputChannel : public QObject protected: bool m_sendExtraPress; - uchar m_lower, m_upper; - int m_midiChannel; + 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 16d5e549e8..e0377de014 100644 --- a/engine/src/qlcinputprofile.cpp +++ b/engine/src/qlcinputprofile.cpp @@ -345,7 +345,7 @@ QVariant QLCInputProfile::channelExtraParams(const QLCInputChannel* channel) con switch (m_type) { case OSC: return channel->name(); - case MIDI: return channel->midiChannel(); + case MIDI: return channel->lowerChannel(); default: return QVariant(); } } diff --git a/engine/src/qlcinputsource.cpp b/engine/src/qlcinputsource.cpp index 6fa1424d27..ad7eb151a2 100644 --- a/engine/src/qlcinputsource.cpp +++ b/engine/src/qlcinputsource.cpp @@ -36,9 +36,6 @@ QLCInputSource::QLCInputSource(QThread *parent) , m_universe(invalidUniverse) , m_channel(invalidChannel) , m_id(invalidID) - , m_lower(0) - , m_upper(UCHAR_MAX) - , m_monitor(UCHAR_MAX) , m_workingMode(Absolute) , m_sensitivity(20) , m_emitExtraPressRelease(false) @@ -46,15 +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(UCHAR_MAX) - , m_monitor(UCHAR_MAX) , m_workingMode(Absolute) , m_sensitivity(20) , m_emitExtraPressRelease(false) @@ -62,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() @@ -126,40 +132,62 @@ quint32 QLCInputSource::id() const * Custom feedback *********************************************************************/ -uchar QLCInputSource::lowerValue() const +uchar QLCInputSource::feedbackValue(QLCInputFeedback::FeedbackType type) const { - return m_lower; -} - -uchar QLCInputSource::upperValue() const -{ - return m_upper; -} - -void QLCInputSource::setRange(uchar lower, uchar upper) -{ - m_lower = lower; - m_upper = upper; -} - -uchar QLCInputSource::monitorValue() const -{ - return m_monitor; + 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::setMonitorValue(uchar monitor) +void QLCInputSource::setFeedbackValue(QLCInputFeedback::FeedbackType type, uchar value) { - m_monitor = monitor; + 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; + } } -QVariant QLCInputSource::extraParams() const +QVariant QLCInputSource::feedbackExtraParams(QLCInputFeedback::FeedbackType type) const { - return m_extraParams; + 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; + } } -void QLCInputSource::setExtraParams(QVariant params) +void QLCInputSource::setFeedbackExtraParams(QLCInputFeedback::FeedbackType type, QVariant params) { - m_extraParams = params; + 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; + } } /********************************************************************* @@ -236,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 0304dd4429..b1b7b52829 100644 --- a/engine/src/qlcinputsource.h +++ b/engine/src/qlcinputsource.h @@ -24,6 +24,8 @@ #include #include +#include "qlcinputfeedback.h" + /** @addtogroup engine Engine * @{ */ @@ -77,23 +79,20 @@ class QLCInputSource: public QThread * Custom feedback *********************************************************************/ public: - uchar lowerValue() const; - uchar upperValue() const; - void setRange(uchar lower, uchar upper); - - uchar monitorValue() const; - void setMonitorValue(uchar monitor); + 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 extraParams() const; - void setExtraParams(QVariant params); + QVariant feedbackExtraParams(QLCInputFeedback::FeedbackType type) const; + void setFeedbackExtraParams(QLCInputFeedback::FeedbackType type, QVariant params); protected: - uchar m_lower, m_upper, m_monitor; - QVariant m_extraParams; + QLCInputFeedback m_lower; + QLCInputFeedback m_upper; + QLCInputFeedback m_monitor; /********************************************************************* * Working mode 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 \ From 3b19c5da6eef2a32dd6d42213ac384287472e4ea Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Thu, 29 Feb 2024 22:33:47 +0100 Subject: [PATCH 086/212] ui: fully support feedback data --- ui/src/customfeedbacksdialog.cpp | 49 ++++++++---- ui/src/customfeedbacksdialog.ui | 24 +++++- ui/src/inputprofileeditor.cpp | 4 +- ui/src/inputselectionwidget.ui | 40 +++++----- ui/src/virtualconsole/vcaudiotriggers.cpp | 4 +- ui/src/virtualconsole/vcbutton.cpp | 13 ++-- ui/src/virtualconsole/vcframe.cpp | 8 +- ui/src/virtualconsole/vcmatrix.cpp | 4 +- ui/src/virtualconsole/vcmatrixcontrol.cpp | 4 +- ui/src/virtualconsole/vcspeeddial.cpp | 4 +- ui/src/virtualconsole/vcspeeddialpreset.cpp | 4 +- ui/src/virtualconsole/vcwidget.cpp | 82 +++++++++++++++------ ui/src/virtualconsole/vcwidget.h | 6 +- ui/src/virtualconsole/vcxypad.cpp | 14 ++-- ui/src/virtualconsole/vcxypadpreset.cpp | 4 +- 15 files changed, 175 insertions(+), 89 deletions(-) diff --git a/ui/src/customfeedbacksdialog.cpp b/ui/src/customfeedbacksdialog.cpp index f3ce325a63..f3e19b18c7 100644 --- a/ui/src/customfeedbacksdialog.cpp +++ b/ui/src/customfeedbacksdialog.cpp @@ -35,9 +35,9 @@ CustomFeedbacksDialog::CustomFeedbacksDialog(Doc *doc, const QSharedPointersetValue(m_inputSource->lowerValue()); - m_upperSpin->setValue(m_inputSource->upperValue()); - m_monitorSpin->setValue(m_inputSource->monitorValue()); + m_lowerSpin->setValue(m_inputSource->feedbackValue(QLCInputFeedback::LowerValue)); + m_upperSpin->setValue(m_inputSource->feedbackValue(QLCInputFeedback::UpperValue)); + m_monitorSpin->setValue(m_inputSource->feedbackValue(QLCInputFeedback::MonitorValue)); } m_lowerSpin->setEnabled(enableControls); @@ -45,6 +45,7 @@ CustomFeedbacksDialog::CustomFeedbacksDialog(Doc *doc, const QSharedPointersetVisible(false); m_monitorSpin->setVisible(false); + m_monitorChannelCombo->setVisible(false); m_profileColorsTree->setVisible(false); m_midiChannelGroup->hide(); @@ -71,13 +72,13 @@ CustomFeedbacksDialog::CustomFeedbacksDialog(Doc *doc, const QSharedPointersetStyleSheet(QString("background-color: %1").arg(lc.second.name())); - if (it.key() == m_inputSource->lowerValue()) + if (it.key() == m_inputSource->feedbackValue(QLCInputFeedback::LowerValue)) m_lowerColor->setStyleSheet(QString("background-color: %1").arg(lc.second.name())); - if (it.key() == m_inputSource->upperValue()) + if (it.key() == m_inputSource->feedbackValue(QLCInputFeedback::UpperValue)) m_upperColor->setStyleSheet(QString("background-color: %1").arg(lc.second.name())); - if (it.key() == m_inputSource->monitorValue()) + if (it.key() == m_inputSource->feedbackValue(QLCInputFeedback::MonitorValue)) m_monitorColor->setStyleSheet(QString("background-color: %1").arg(lc.second.name())); m_profileColorsTree->setItemWidget(item, 2, colLabel); @@ -86,16 +87,30 @@ CustomFeedbacksDialog::CustomFeedbacksDialog(Doc *doc, const QSharedPointertype() == QLCInputProfile::MIDI && m_profile->hasMidiChannelTable()) { m_midiChannelGroup->show(); - m_midiChannelCombo->addItem(tr("From plugin settings")); + m_lowerChannelCombo->addItem(tr("From plugin settings")); + m_upperChannelCombo->addItem(tr("From plugin settings")); + m_monitorChannelCombo->addItem(tr("From plugin settings")); QMapIterator it(m_profile->midiChannelTable()); while (it.hasNext() == true) { it.next(); - m_midiChannelCombo->addItem(it.value()); + m_lowerChannelCombo->addItem(it.value()); + m_upperChannelCombo->addItem(it.value()); + m_monitorChannelCombo->addItem(it.value()); } - if (m_inputSource->extraParams().isValid()) - m_midiChannelCombo->setCurrentIndex(m_inputSource->extraParams().toInt() + 1); + + QVariant extraParams = m_inputSource->feedbackExtraParams(QLCInputFeedback::LowerValue); + if (extraParams.isValid()) + m_lowerChannelCombo->setCurrentIndex(extraParams.toInt() + 1); + + extraParams = m_inputSource->feedbackExtraParams(QLCInputFeedback::UpperValue); + if (extraParams.isValid()) + m_upperChannelCombo->setCurrentIndex(extraParams.toInt() + 1); + + extraParams = m_inputSource->feedbackExtraParams(QLCInputFeedback::MonitorValue); + if (extraParams.isValid()) + m_monitorChannelCombo->setCurrentIndex(extraParams.toInt() + 1); } } } @@ -119,6 +134,7 @@ void CustomFeedbacksDialog::setMonitoringVisibility(bool visible) { m_monitorLabel->setVisible(visible); m_monitorSpin->setVisible(visible); + m_monitorChannelCombo->setVisible(visible); } void CustomFeedbacksDialog::accept() @@ -126,11 +142,18 @@ void CustomFeedbacksDialog::accept() if (m_inputSource.isNull()) return; - m_inputSource->setRange(m_lowerSpin->value(), m_upperSpin->value()); + m_inputSource->setFeedbackValue(QLCInputFeedback::LowerValue, m_lowerSpin->value()); + m_inputSource->setFeedbackValue(QLCInputFeedback::UpperValue, m_upperSpin->value()); if (m_monitorSpin->isVisible()) - m_inputSource->setMonitorValue(m_monitorSpin->value()); + m_inputSource->setFeedbackValue(QLCInputFeedback::MonitorValue, m_monitorSpin->value()); + if (m_midiChannelGroup->isVisible()) - m_inputSource->setExtraParams(m_midiChannelCombo->currentIndex() - 1); + { + m_inputSource->setFeedbackExtraParams(QLCInputFeedback::LowerValue, m_lowerChannelCombo->currentIndex() - 1); + m_inputSource->setFeedbackExtraParams(QLCInputFeedback::UpperValue, m_upperChannelCombo->currentIndex() - 1); + if (m_monitorSpin->isVisible()) + m_inputSource->setFeedbackExtraParams(QLCInputFeedback::MonitorValue, m_monitorChannelCombo->currentIndex() - 1); + } QDialog::accept(); } diff --git a/ui/src/customfeedbacksdialog.ui b/ui/src/customfeedbacksdialog.ui index 6dd1ecf5ff..9d1490e0d2 100644 --- a/ui/src/customfeedbacksdialog.ui +++ b/ui/src/customfeedbacksdialog.ui @@ -170,6 +170,13 @@ MIDI Channel + + + + Upper Channel + + + @@ -179,12 +186,12 @@ - Channel + Lower Channel - + 0 @@ -193,6 +200,19 @@ + + + + + + + Monitor Channel + + + + + + diff --git a/ui/src/inputprofileeditor.cpp b/ui/src/inputprofileeditor.cpp index b3d3011992..095cb556d9 100644 --- a/ui/src/inputprofileeditor.cpp +++ b/ui/src/inputprofileeditor.cpp @@ -581,7 +581,7 @@ void InputProfileEditor::slotItemClicked(QTreeWidgetItem *item, int col) m_midiChannelCombo->blockSignals(true); m_lowerSpin->setValue(ich->lowerValue()); m_upperSpin->setValue(ich->upperValue()); - m_midiChannelCombo->setCurrentIndex(ich->midiChannel() + 1); + m_midiChannelCombo->setCurrentIndex(ich->lowerChannel() + 1); m_lowerSpin->blockSignals(false); m_upperSpin->blockSignals(false); m_midiChannelCombo->blockSignals(false); @@ -656,7 +656,7 @@ void InputProfileEditor::slotMidiChannelComboChanged(int index) foreach (QLCInputChannel *channel, selectedChannels()) { if (channel->type() == QLCInputChannel::Button) - channel->setMidiChannel(index - 1); + channel->setLowerChannel(index - 1); } } diff --git a/ui/src/inputselectionwidget.ui b/ui/src/inputselectionwidget.ui index 182bdb237d..1249966048 100644 --- a/ui/src/inputselectionwidget.ui +++ b/ui/src/inputselectionwidget.ui @@ -175,26 +175,6 @@ - - - - Custom Feedback - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - @@ -230,6 +210,26 @@ + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Custom Feedback + + + diff --git a/ui/src/virtualconsole/vcaudiotriggers.cpp b/ui/src/virtualconsole/vcaudiotriggers.cpp index ac75a4b71e..106186041b 100644 --- a/ui/src/virtualconsole/vcaudiotriggers.cpp +++ b/ui/src/virtualconsole/vcaudiotriggers.cpp @@ -376,9 +376,9 @@ void VCAudioTriggers::updateFeedback() if (!src.isNull() && src->isValid() == true) { if (m_button->isChecked()) - sendFeedback(src->upperValue()); + sendFeedback(src->feedbackValue(QLCInputFeedback::UpperValue)); else - sendFeedback(src->lowerValue()); + sendFeedback(src->feedbackValue(QLCInputFeedback::LowerValue)); } } diff --git a/ui/src/virtualconsole/vcbutton.cpp b/ui/src/virtualconsole/vcbutton.cpp index b7e0b472db..616c0e64c6 100644 --- a/ui/src/virtualconsole/vcbutton.cpp +++ b/ui/src/virtualconsole/vcbutton.cpp @@ -522,13 +522,12 @@ void VCButton::updateFeedback() QSharedPointer src = inputSource(); if (!src.isNull() && src->isValid() == true) { - if (m_state == Inactive) { - sendFeedback(src->lowerValue()); - } else if (m_state == Monitoring) { - sendFeedback(src->monitorValue()); - } else { - sendFeedback(src->upperValue()); - } + if (m_state == Inactive) + sendFeedback(src->feedbackValue(QLCInputFeedback::LowerValue), src, src->feedbackExtraParams(QLCInputFeedback::LowerValue)); + else if (m_state == Monitoring) + sendFeedback(src->feedbackValue(QLCInputFeedback::MonitorValue), src, src->feedbackExtraParams(QLCInputFeedback::MonitorValue)); + else + sendFeedback(src->feedbackValue(QLCInputFeedback::UpperValue), src, src->feedbackExtraParams(QLCInputFeedback::UpperValue)); } } diff --git a/ui/src/virtualconsole/vcframe.cpp b/ui/src/virtualconsole/vcframe.cpp index 2437b629b7..889e41edf9 100644 --- a/ui/src/virtualconsole/vcframe.cpp +++ b/ui/src/virtualconsole/vcframe.cpp @@ -750,14 +750,14 @@ void VCFrame::updateFeedback() { if (m_disableState == false) { - sendFeedback(src->upperValue(), enableInputSourceId); + sendFeedback(src->feedbackValue(QLCInputFeedback::UpperValue), enableInputSourceId); } else { // temporarily revert the disabled state otherwise this // feedback will never go through (cause of acceptsInput) m_disableState = false; - sendFeedback(src->lowerValue(), enableInputSourceId); + sendFeedback(src->feedbackValue(QLCInputFeedback::LowerValue), enableInputSourceId); m_disableState = true; } } @@ -768,9 +768,9 @@ void VCFrame::updateFeedback() if (!src.isNull() && src->isValid() == true) { if (m_currentPage == shortcut->m_page) - sendFeedback(src->upperValue(), src); + sendFeedback(src->feedbackValue(QLCInputFeedback::UpperValue), src); else - sendFeedback(src->lowerValue(), src); + sendFeedback(src->feedbackValue(QLCInputFeedback::LowerValue), src); } } diff --git a/ui/src/virtualconsole/vcmatrix.cpp b/ui/src/virtualconsole/vcmatrix.cpp index bfc872f435..e57f81ce40 100644 --- a/ui/src/virtualconsole/vcmatrix.cpp +++ b/ui/src/virtualconsole/vcmatrix.cpp @@ -1013,8 +1013,8 @@ void VCMatrix::updateFeedback() { QPushButton* button = reinterpret_cast(it.key()); sendFeedback(button->isDown() ? - control->m_inputSource->upperValue() : - control->m_inputSource->lowerValue(), + control->m_inputSource->feedbackValue(QLCInputFeedback::UpperValue) : + control->m_inputSource->feedbackValue(QLCInputFeedback::LowerValue), control->m_inputSource); } } diff --git a/ui/src/virtualconsole/vcmatrixcontrol.cpp b/ui/src/virtualconsole/vcmatrixcontrol.cpp index 25879a5224..12472c373f 100644 --- a/ui/src/virtualconsole/vcmatrixcontrol.cpp +++ b/ui/src/virtualconsole/vcmatrixcontrol.cpp @@ -51,7 +51,9 @@ VCMatrixControl &VCMatrixControl::operator=(const VCMatrixControl &vcmc) { m_inputSource = QSharedPointer(new QLCInputSource(vcmc.m_inputSource->universe(), vcmc.m_inputSource->channel())); - m_inputSource->setRange(vcmc.m_inputSource->lowerValue(), vcmc.m_inputSource->upperValue()); + + m_inputSource->setFeedbackValue(QLCInputFeedback::LowerValue, vcmc.m_inputSource->feedbackValue(QLCInputFeedback::LowerValue)); + m_inputSource->setFeedbackValue(QLCInputFeedback::UpperValue, vcmc.m_inputSource->feedbackValue(QLCInputFeedback::UpperValue)); } } diff --git a/ui/src/virtualconsole/vcspeeddial.cpp b/ui/src/virtualconsole/vcspeeddial.cpp index 68ab4222af..524eead2f8 100644 --- a/ui/src/virtualconsole/vcspeeddial.cpp +++ b/ui/src/virtualconsole/vcspeeddial.cpp @@ -571,8 +571,8 @@ void VCSpeedDial::updateFeedback() QPushButton* button = reinterpret_cast(it.key()); if (preset->m_inputSource.isNull() == false) sendFeedback(button->isDown() ? - preset->m_inputSource->upperValue() : - preset->m_inputSource->lowerValue(), + preset->m_inputSource->feedbackValue(QLCInputFeedback::UpperValue) : + preset->m_inputSource->feedbackValue(QLCInputFeedback::LowerValue), preset->m_inputSource); } } diff --git a/ui/src/virtualconsole/vcspeeddialpreset.cpp b/ui/src/virtualconsole/vcspeeddialpreset.cpp index d16958fe13..6de7a11d7a 100644 --- a/ui/src/virtualconsole/vcspeeddialpreset.cpp +++ b/ui/src/virtualconsole/vcspeeddialpreset.cpp @@ -52,7 +52,9 @@ VCSpeedDialPreset &VCSpeedDialPreset::operator=(const VCSpeedDialPreset &preset) { m_inputSource = QSharedPointer(new QLCInputSource(preset.m_inputSource->universe(), preset.m_inputSource->channel())); - m_inputSource->setRange(preset.m_inputSource->lowerValue(), preset.m_inputSource->upperValue()); + + m_inputSource->setFeedbackValue(QLCInputFeedback::LowerValue, preset.m_inputSource->feedbackValue(QLCInputFeedback::LowerValue)); + m_inputSource->setFeedbackValue(QLCInputFeedback::UpperValue, preset.m_inputSource->feedbackValue(QLCInputFeedback::UpperValue)); } } return *this; diff --git a/ui/src/virtualconsole/vcwidget.cpp b/ui/src/virtualconsole/vcwidget.cpp index a9e9f17193..3c59e4325d 100644 --- a/ui/src/virtualconsole/vcwidget.cpp +++ b/ui/src/virtualconsole/vcwidget.cpp @@ -256,8 +256,12 @@ bool VCWidget::copyFrom(const VCWidget* widget) it.next(); quint8 id = it.key(); QSharedPointer src(new QLCInputSource(it.value()->universe(), it.value()->channel())); - src->setRange(it.value()->lowerValue(), it.value()->upperValue()); - src->setExtraParams(it.value()->extraParams()); + src->setFeedbackValue(QLCInputFeedback::LowerValue, it.value()->feedbackValue(QLCInputFeedback::LowerValue)); + src->setFeedbackValue(QLCInputFeedback::UpperValue, it.value()->feedbackValue(QLCInputFeedback::UpperValue)); + src->setFeedbackValue(QLCInputFeedback::MonitorValue, it.value()->feedbackValue(QLCInputFeedback::MonitorValue)); + src->setFeedbackExtraParams(QLCInputFeedback::LowerValue, it.value()->feedbackExtraParams(QLCInputFeedback::LowerValue)); + src->setFeedbackExtraParams(QLCInputFeedback::UpperValue, it.value()->feedbackExtraParams(QLCInputFeedback::UpperValue)); + src->setFeedbackExtraParams(QLCInputFeedback::MonitorValue, it.value()->feedbackExtraParams(QLCInputFeedback::MonitorValue)); setInputSource(src, id); } @@ -617,8 +621,12 @@ void VCWidget::setInputSource(QSharedPointer const& source, quin if (ich != NULL) { // retrieve plugin specific params for feedbacks - if (source->extraParams().toInt() == -1) - source->setExtraParams(profile->channelExtraParams(ich)); + if (source->feedbackExtraParams(QLCInputFeedback::LowerValue).toInt() == -1) + source->setFeedbackExtraParams(QLCInputFeedback::LowerValue, profile->channelExtraParams(ich)); + if (source->feedbackExtraParams(QLCInputFeedback::UpperValue).toInt() == -1) + source->setFeedbackExtraParams(QLCInputFeedback::UpperValue, profile->channelExtraParams(ich)); + if (source->feedbackExtraParams(QLCInputFeedback::MonitorValue).toInt() == -1) + source->setFeedbackExtraParams(QLCInputFeedback::MonitorValue, profile->channelExtraParams(ich)); if (ich->movementType() == QLCInputChannel::Relative) { @@ -644,8 +652,15 @@ void VCWidget::setInputSource(QSharedPointer const& source, quin } // user custom feedbacks have precedence over input profile custom feedbacks - source->setRange((source->lowerValue() != 0) ? source->lowerValue() : ich->lowerValue(), - (source->upperValue() != UCHAR_MAX) ? source->upperValue() : ich->upperValue()); + uchar lower = source->feedbackValue(QLCInputFeedback::LowerValue) != 0 ? + source->feedbackValue(QLCInputFeedback::LowerValue) : + ich->lowerValue(); + uchar upper = source->feedbackValue(QLCInputFeedback::UpperValue) != UCHAR_MAX ? + source->feedbackValue(QLCInputFeedback::UpperValue) : + ich->upperValue(); + + source->setFeedbackValue(QLCInputFeedback::LowerValue, lower); + source->setFeedbackValue(QLCInputFeedback::UpperValue, upper); } } } @@ -684,7 +699,7 @@ void VCWidget::sendFeedback(int value, quint8 id) sendFeedback(value, src); } -void VCWidget::sendFeedback(int value, QSharedPointer src) +void VCWidget::sendFeedback(int value, QSharedPointer src, QVariant extraParams) { if (src.isNull() || src->isValid() == false) return; @@ -698,7 +713,11 @@ void VCWidget::sendFeedback(int value, QSharedPointer src) if (acceptsInput() == false) return; - m_doc->inputOutputMap()->sendFeedBack(src->universe(), src->channel(), value, src->extraParams()); + qDebug() << "Send feedback to uni" << src->universe() << "," << src->channel() << ", param" << extraParams; + + m_doc->inputOutputMap()->sendFeedBack( + src->universe(), src->channel(), value, + extraParams.isValid() ? extraParams : src->feedbackExtraParams(QLCInputFeedback::UpperValue)); } void VCWidget::slotInputValueChanged(quint32 universe, quint32 channel, uchar value) @@ -878,7 +897,6 @@ QSharedPointer VCWidget::getXMLInput(QXmlStreamReader &root) quint32 uni = attrs.value(KXMLQLCVCWidgetInputUniverse).toString().toUInt(); quint32 ch = attrs.value(KXMLQLCVCWidgetInputChannel).toString().toUInt(); uchar min = 0, max = UCHAR_MAX, mon = UCHAR_MAX; - int fbChannel = -1; QSharedPointernewSrc = QSharedPointer(new QLCInputSource(uni, ch)); if (attrs.hasAttribute(KXMLQLCVCWidgetInputLowerValue)) @@ -887,12 +905,18 @@ QSharedPointer VCWidget::getXMLInput(QXmlStreamReader &root) max = uchar(attrs.value(KXMLQLCVCWidgetInputUpperValue).toString().toUInt()); if (attrs.hasAttribute(KXMLQLCVCWidgetInputMonitorValue)) mon = uchar(attrs.value(KXMLQLCVCWidgetInputMonitorValue).toString().toUInt()); - if (attrs.hasAttribute(KXMLQLCVCWidgetInputFeedbackChannel)) - fbChannel = attrs.value(KXMLQLCVCWidgetInputFeedbackChannel).toInt(); - newSrc->setRange(min, max); - newSrc->setMonitorValue(mon); - newSrc->setExtraParams(fbChannel); + newSrc->setFeedbackValue(QLCInputFeedback::LowerValue, min); + newSrc->setFeedbackValue(QLCInputFeedback::UpperValue, max); + newSrc->setFeedbackValue(QLCInputFeedback::MonitorValue, mon); + + // load feedback extra params + if (attrs.hasAttribute(KXMLQLCVCWidgetInputLowerParams)) + newSrc->setFeedbackExtraParams(QLCInputFeedback::LowerValue, attrs.value(KXMLQLCVCWidgetInputLowerParams).toInt()); + if (attrs.hasAttribute(KXMLQLCVCWidgetInputUpperParams)) + newSrc->setFeedbackExtraParams(QLCInputFeedback::UpperValue, attrs.value(KXMLQLCVCWidgetInputUpperParams).toInt()); + if (attrs.hasAttribute(KXMLQLCVCWidgetInputMonitorParams)) + newSrc->setFeedbackExtraParams(QLCInputFeedback::MonitorValue, attrs.value(KXMLQLCVCWidgetInputMonitorParams).toInt()); return newSrc; } @@ -1036,16 +1060,28 @@ bool VCWidget::saveXMLInput(QXmlStreamWriter *doc, doc->writeStartElement(KXMLQLCVCWidgetInput); doc->writeAttribute(KXMLQLCVCWidgetInputUniverse, QString("%1").arg(src->universe())); doc->writeAttribute(KXMLQLCVCWidgetInputChannel, QString("%1").arg(src->channel())); - if (src->lowerValue() != 0) - doc->writeAttribute(KXMLQLCVCWidgetInputLowerValue, QString::number(src->lowerValue())); - if (src->upperValue() != UCHAR_MAX) - doc->writeAttribute(KXMLQLCVCWidgetInputUpperValue, QString::number(src->upperValue())); - if (src->monitorValue() != UCHAR_MAX) - doc->writeAttribute(KXMLQLCVCWidgetInputMonitorValue, QString::number(src->monitorValue())); - - QVariant extraParams = src->extraParams(); + if (src->feedbackValue(QLCInputFeedback::LowerValue) != 0) + doc->writeAttribute(KXMLQLCVCWidgetInputLowerValue, QString::number(src->feedbackValue(QLCInputFeedback::LowerValue))); + if (src->feedbackValue(QLCInputFeedback::UpperValue) != UCHAR_MAX) + doc->writeAttribute(KXMLQLCVCWidgetInputUpperValue, QString::number(src->feedbackValue(QLCInputFeedback::UpperValue))); + if (src->feedbackValue(QLCInputFeedback::MonitorValue) != UCHAR_MAX) + doc->writeAttribute(KXMLQLCVCWidgetInputMonitorValue, QString::number(src->feedbackValue(QLCInputFeedback::MonitorValue))); + + // save feedback extra params + QVariant extraParams = src->feedbackExtraParams(QLCInputFeedback::LowerValue); + + if (extraParams.isValid() && extraParams.type() == QVariant::Int && extraParams.toInt() != -1) + doc->writeAttribute(KXMLQLCVCWidgetInputLowerParams, QString::number(extraParams.toInt())); + + extraParams = src->feedbackExtraParams(QLCInputFeedback::UpperValue); + + if (extraParams.isValid() && extraParams.type() == QVariant::Int && extraParams.toInt() != -1) + doc->writeAttribute(KXMLQLCVCWidgetInputUpperParams, QString::number(extraParams.toInt())); + + extraParams = src->feedbackExtraParams(QLCInputFeedback::MonitorValue); + if (extraParams.isValid() && extraParams.type() == QVariant::Int && extraParams.toInt() != -1) - doc->writeAttribute(KXMLQLCVCWidgetInputFeedbackChannel, QString::number(extraParams.toInt())); + doc->writeAttribute(KXMLQLCVCWidgetInputMonitorParams, QString::number(extraParams.toInt())); doc->writeEndElement(); } diff --git a/ui/src/virtualconsole/vcwidget.h b/ui/src/virtualconsole/vcwidget.h index e5cc61c1a9..66dd28519c 100644 --- a/ui/src/virtualconsole/vcwidget.h +++ b/ui/src/virtualconsole/vcwidget.h @@ -66,7 +66,9 @@ class QFile; #define KXMLQLCVCWidgetInputLowerValue QString("LowerValue") #define KXMLQLCVCWidgetInputUpperValue QString("UpperValue") #define KXMLQLCVCWidgetInputMonitorValue QString("MonitorValue") -#define KXMLQLCVCWidgetInputFeedbackChannel QString("FeedbackChannel") +#define KXMLQLCVCWidgetInputLowerParams QString("LowerParams") +#define KXMLQLCVCWidgetInputUpperParams QString("UpperParams") +#define KXMLQLCVCWidgetInputMonitorParams QString("MonitorParams") #define KXMLQLCWindowState QString("WindowState") #define KXMLQLCWindowStateVisible QString("Visible") @@ -448,7 +450,7 @@ class VCWidget : public QWidget * @param value value from 0 to 255 to be sent * @param src the QLCInputSource reference to send the feedback to */ - void sendFeedback(int value, QSharedPointer src); + void sendFeedback(int value, QSharedPointer src, QVariant extraParams = QVariant()); /** * Send the feedback data again, e.g. after page flip diff --git a/ui/src/virtualconsole/vcxypad.cpp b/ui/src/virtualconsole/vcxypad.cpp index e7b4004e44..c7fe797cfe 100644 --- a/ui/src/virtualconsole/vcxypad.cpp +++ b/ui/src/virtualconsole/vcxypad.cpp @@ -736,7 +736,7 @@ void VCXYPad::slotPresetClicked(bool checked) { cBtn->setChecked(false); if (cPr->m_inputSource.isNull() == false) - sendFeedback(cPr->m_inputSource->lowerValue(), cPr->m_inputSource); + sendFeedback(cPr->m_inputSource->feedbackValue(QLCInputFeedback::LowerValue), cPr->m_inputSource); } } else if (cPr->m_type == VCXYPadPreset::EFX || @@ -746,7 +746,7 @@ void VCXYPad::slotPresetClicked(bool checked) { cBtn->setChecked(false); if (cPr->m_inputSource.isNull() == false) - sendFeedback(cPr->m_inputSource->lowerValue(), cPr->m_inputSource); + sendFeedback(cPr->m_inputSource->feedbackValue(QLCInputFeedback::LowerValue), cPr->m_inputSource); } } else @@ -755,12 +755,12 @@ void VCXYPad::slotPresetClicked(bool checked) { cBtn->setDown(false); if (cPr->m_inputSource.isNull() == false) - sendFeedback(cPr->m_inputSource->lowerValue(), cPr->m_inputSource); + sendFeedback(cPr->m_inputSource->feedbackValue(QLCInputFeedback::LowerValue), cPr->m_inputSource); } } cBtn->blockSignals(false); if (cPr->m_inputSource.isNull() == false) - sendFeedback(cPr->m_inputSource->lowerValue(), cPr->m_inputSource); + sendFeedback(cPr->m_inputSource->feedbackValue(QLCInputFeedback::LowerValue), cPr->m_inputSource); } if (preset->m_type == VCXYPadPreset::EFX) @@ -801,7 +801,7 @@ void VCXYPad::slotPresetClicked(bool checked) connect(m_efx, SIGNAL(durationChanged(uint)), this, SLOT(slotEFXDurationChanged(uint))); if (preset->m_inputSource.isNull() == false) - sendFeedback(preset->m_inputSource->upperValue(), preset->m_inputSource); + sendFeedback(preset->m_inputSource->feedbackValue(QLCInputFeedback::UpperValue), preset->m_inputSource); } else if (preset->m_type == VCXYPadPreset::Scene) { @@ -843,7 +843,7 @@ void VCXYPad::slotPresetClicked(bool checked) m_scene->start(m_doc->masterTimer(), functionParent()); if (preset->m_inputSource.isNull() == false) - sendFeedback(preset->m_inputSource->upperValue(), preset->m_inputSource); + sendFeedback(preset->m_inputSource->feedbackValue(QLCInputFeedback::UpperValue), preset->m_inputSource); } else if (preset->m_type == VCXYPadPreset::Position) { @@ -854,7 +854,7 @@ void VCXYPad::slotPresetClicked(bool checked) m_area->setPosition(preset->m_dmxPos); m_area->repaint(); if (preset->m_inputSource.isNull() == false) - sendFeedback(preset->m_inputSource->upperValue(), preset->m_inputSource); + sendFeedback(preset->m_inputSource->feedbackValue(QLCInputFeedback::UpperValue), preset->m_inputSource); btn->blockSignals(true); btn->setDown(true); btn->blockSignals(false); diff --git a/ui/src/virtualconsole/vcxypadpreset.cpp b/ui/src/virtualconsole/vcxypadpreset.cpp index cc9f2795da..20f8fcf60d 100644 --- a/ui/src/virtualconsole/vcxypadpreset.cpp +++ b/ui/src/virtualconsole/vcxypadpreset.cpp @@ -103,7 +103,9 @@ VCXYPadPreset &VCXYPadPreset::operator=(const VCXYPadPreset &vcpp) { m_inputSource = QSharedPointer(new QLCInputSource(vcpp.m_inputSource->universe(), vcpp.m_inputSource->channel())); - m_inputSource->setRange(vcpp.m_inputSource->lowerValue(), vcpp.m_inputSource->upperValue()); + + m_inputSource->setFeedbackValue(QLCInputFeedback::LowerValue, vcpp.m_inputSource->feedbackValue(QLCInputFeedback::LowerValue)); + m_inputSource->setFeedbackValue(QLCInputFeedback::UpperValue, vcpp.m_inputSource->feedbackValue(QLCInputFeedback::UpperValue)); } } return *this; From ba5c3065b90c20fdfcc12410c8976a1b0d2f1b9f Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Thu, 29 Feb 2024 23:11:05 +0100 Subject: [PATCH 087/212] qmlui: fix to build with latest changes --- qmlui/contextmanager.cpp | 2 +- qmlui/virtualconsole/vcbutton.cpp | 2 + qmlui/virtualconsole/vcwidget.cpp | 88 +++++++++++++++++++++++++----- qmlui/virtualconsole/vcwidget.h | 18 +++--- ui/src/virtualconsole/vcwidget.cpp | 2 +- 5 files changed, 89 insertions(+), 23 deletions(-) diff --git a/qmlui/contextmanager.cpp b/qmlui/contextmanager.cpp index db8e49464e..2f429f4c24 100644 --- a/qmlui/contextmanager.cpp +++ b/qmlui/contextmanager.cpp @@ -1645,7 +1645,7 @@ void ContextManager::setDumpValue(quint32 fxID, quint32 channel, uchar value, bo currentVal.setValue(SceneValue(fxID, channel, currDmxValue)); newVal.setValue(sValue); - if (currentVal != newVal || value != currDmxValue) + //if (currentVal != newVal || value != currDmxValue) { if (output) { diff --git a/qmlui/virtualconsole/vcbutton.cpp b/qmlui/virtualconsole/vcbutton.cpp index 84debd96e8..740e05fdda 100644 --- a/qmlui/virtualconsole/vcbutton.cpp +++ b/qmlui/virtualconsole/vcbutton.cpp @@ -514,6 +514,8 @@ void VCButton::updateFeedback() if (m_state == Inactive) sendFeedback(0, INPUT_PRESSURE_ID, VCWidget::LowerValue); + else if (m_state == Monitoring) + sendFeedback(0, INPUT_PRESSURE_ID, VCWidget::MonitorValue); else sendFeedback(UCHAR_MAX, INPUT_PRESSURE_ID, VCWidget::UpperValue); } diff --git a/qmlui/virtualconsole/vcwidget.cpp b/qmlui/virtualconsole/vcwidget.cpp index 65fa66700f..119008f068 100644 --- a/qmlui/virtualconsole/vcwidget.cpp +++ b/qmlui/virtualconsole/vcwidget.cpp @@ -120,7 +120,12 @@ bool VCWidget::copyFrom(const VCWidget* widget) { QSharedPointer dst(new QLCInputSource(src->universe(), src->channel())); dst->setID(src->id()); - dst->setRange(src->lowerValue(), src->upperValue()); + dst->setFeedbackValue(QLCInputFeedback::LowerValue, src->feedbackValue(QLCInputFeedback::LowerValue)); + dst->setFeedbackValue(QLCInputFeedback::UpperValue, src->feedbackValue(QLCInputFeedback::UpperValue)); + dst->setFeedbackValue(QLCInputFeedback::MonitorValue, src->feedbackValue(QLCInputFeedback::MonitorValue)); + dst->setFeedbackExtraParams(QLCInputFeedback::LowerValue, src->feedbackExtraParams(QLCInputFeedback::LowerValue)); + dst->setFeedbackExtraParams(QLCInputFeedback::UpperValue, src->feedbackExtraParams(QLCInputFeedback::UpperValue)); + dst->setFeedbackExtraParams(QLCInputFeedback::MonitorValue, src->feedbackExtraParams(QLCInputFeedback::MonitorValue)); addInputSource(dst); } @@ -659,6 +664,16 @@ void VCWidget::addInputSource(QSharedPointer const& source) QLCInputChannel *ich = ip->profile()->channel(source->channel() & 0x0000FFFF); if (ich != nullptr) { + QLCInputProfile *profile = ip->profile(); + + // retrieve plugin specific params for feedbacks + if (source->feedbackExtraParams(QLCInputFeedback::LowerValue).toInt() == -1) + source->setFeedbackExtraParams(QLCInputFeedback::LowerValue, profile->channelExtraParams(ich)); + if (source->feedbackExtraParams(QLCInputFeedback::UpperValue).toInt() == -1) + source->setFeedbackExtraParams(QLCInputFeedback::UpperValue, profile->channelExtraParams(ich)); + if (source->feedbackExtraParams(QLCInputFeedback::MonitorValue).toInt() == -1) + source->setFeedbackExtraParams(QLCInputFeedback::MonitorValue, profile->channelExtraParams(ich)); + if (ich->movementType() == QLCInputChannel::Relative) { source->setWorkingMode(QLCInputSource::Relative); @@ -683,8 +698,15 @@ void VCWidget::addInputSource(QSharedPointer const& source) } // user custom feedbacks have precedence over input profile custom feedbacks - source->setRange((source->lowerValue() != 0) ? source->lowerValue() : ich->lowerValue(), - (source->upperValue() != UCHAR_MAX) ? source->upperValue() : ich->upperValue()); + uchar lower = source->feedbackValue(QLCInputFeedback::LowerValue) != 0 ? + source->feedbackValue(QLCInputFeedback::LowerValue) : + ich->lowerValue(); + uchar upper = source->feedbackValue(QLCInputFeedback::UpperValue) != UCHAR_MAX ? + source->feedbackValue(QLCInputFeedback::UpperValue) : + ich->upperValue(); + + source->setFeedbackValue(QLCInputFeedback::LowerValue, lower); + source->setFeedbackValue(QLCInputFeedback::UpperValue, upper); } } } @@ -725,7 +747,8 @@ bool VCWidget::updateInputSourceRange(quint32 universe, quint32 channel, quint8 { if (source->universe() == universe && source->channel() == channel) { - source->setRange(lower, upper); + source->setFeedbackValue(QLCInputFeedback::LowerValue, lower); + source->setFeedbackValue(QLCInputFeedback::UpperValue, upper); return true; } } @@ -787,16 +810,20 @@ QVariantList VCWidget::inputSourcesList() } QVariantMap sourceMap; + uchar lower = source->feedbackValue(QLCInputFeedback::LowerValue); + uchar upper = source->feedbackValue(QLCInputFeedback::UpperValue); + if (source->isValid() == false) sourceMap.insert("invalid", true); + sourceMap.insert("type", Controller); sourceMap.insert("id", source->id()); sourceMap.insert("uniString", uniName); sourceMap.insert("chString", chName); sourceMap.insert("universe", source->universe()); sourceMap.insert("channel", source->channel()); - sourceMap.insert("lower", source->lowerValue() != 0 ? source->lowerValue() : min); - sourceMap.insert("upper", source->upperValue() != UCHAR_MAX ? source->upperValue() : max); + sourceMap.insert("lower", lower != 0 ? lower : min); + sourceMap.insert("upper", upper != UCHAR_MAX ? upper : max); sourceMap.insert("customFeedback", supportCustomFeedback); m_sourcesList.append(sourceMap); } @@ -863,9 +890,11 @@ void VCWidget::sendFeedback(int value, quint8 id, SourceValueType type) continue; if (type == LowerValue) - value = source->lowerValue(); + value = source->feedbackValue(QLCInputFeedback::LowerValue); else if (type == UpperValue) - value = source->upperValue(); + value = source->feedbackValue(QLCInputFeedback::UpperValue); + else if (type == MonitorValue) + value = source->feedbackValue(QLCInputFeedback::MonitorValue); // if in relative mode, send a "feedback" to this // input source so it can continue to emit values @@ -1084,7 +1113,7 @@ bool VCWidget::loadXMLInputSource(QXmlStreamReader &root, const quint8 &id) quint32 uni = attrs.value(KXMLQLCVCWidgetInputUniverse).toString().toUInt(); quint32 ch = attrs.value(KXMLQLCVCWidgetInputChannel).toString().toUInt(); - uchar min = 0, max = UCHAR_MAX; + uchar min = 0, max = UCHAR_MAX, mon = UCHAR_MAX; QSharedPointerinputSource = QSharedPointer(new QLCInputSource(uni, ch)); inputSource->setID(id); @@ -1093,8 +1122,20 @@ bool VCWidget::loadXMLInputSource(QXmlStreamReader &root, const quint8 &id) min = uchar(attrs.value(KXMLQLCVCWidgetInputLowerValue).toString().toUInt()); if (attrs.hasAttribute(KXMLQLCVCWidgetInputUpperValue)) max = uchar(attrs.value(KXMLQLCVCWidgetInputUpperValue).toString().toUInt()); + if (attrs.hasAttribute(KXMLQLCVCWidgetInputMonitorValue)) + mon = uchar(attrs.value(KXMLQLCVCWidgetInputMonitorValue).toString().toUInt()); + + inputSource->setFeedbackValue(QLCInputFeedback::LowerValue, min); + inputSource->setFeedbackValue(QLCInputFeedback::UpperValue, max); + inputSource->setFeedbackValue(QLCInputFeedback::MonitorValue, mon); - inputSource->setRange(min, max); + // load feedback extra params + if (attrs.hasAttribute(KXMLQLCVCWidgetInputLowerParams)) + inputSource->setFeedbackExtraParams(QLCInputFeedback::LowerValue, attrs.value(KXMLQLCVCWidgetInputLowerParams).toInt()); + if (attrs.hasAttribute(KXMLQLCVCWidgetInputUpperParams)) + inputSource->setFeedbackExtraParams(QLCInputFeedback::UpperValue, attrs.value(KXMLQLCVCWidgetInputUpperParams).toInt()); + if (attrs.hasAttribute(KXMLQLCVCWidgetInputMonitorParams)) + inputSource->setFeedbackExtraParams(QLCInputFeedback::MonitorValue, attrs.value(KXMLQLCVCWidgetInputMonitorParams).toInt()); addInputSource(inputSource); @@ -1262,10 +1303,29 @@ bool VCWidget::saveXMLInputControl(QXmlStreamWriter *doc, quint8 controlId, QStr doc->writeStartElement(KXMLQLCVCWidgetInput); doc->writeAttribute(KXMLQLCVCWidgetInputUniverse, QString("%1").arg(source->universe())); doc->writeAttribute(KXMLQLCVCWidgetInputChannel, QString("%1").arg(source->channel())); - if (source->lowerValue() != 0) - doc->writeAttribute(KXMLQLCVCWidgetInputLowerValue, QString::number(source->lowerValue())); - if (source->upperValue() != UCHAR_MAX) - doc->writeAttribute(KXMLQLCVCWidgetInputUpperValue, QString::number(source->upperValue())); + if (source->feedbackValue(QLCInputFeedback::LowerValue) != 0) + doc->writeAttribute(KXMLQLCVCWidgetInputLowerValue, QString::number(source->feedbackValue(QLCInputFeedback::LowerValue))); + if (source->feedbackValue(QLCInputFeedback::UpperValue) != UCHAR_MAX) + doc->writeAttribute(KXMLQLCVCWidgetInputUpperValue, QString::number(source->feedbackValue(QLCInputFeedback::UpperValue))); + if (source->feedbackValue(QLCInputFeedback::MonitorValue) != UCHAR_MAX) + doc->writeAttribute(KXMLQLCVCWidgetInputMonitorValue, QString::number(source->feedbackValue(QLCInputFeedback::MonitorValue))); + + // save feedback extra params + QVariant extraParams = source->feedbackExtraParams(QLCInputFeedback::LowerValue); + + if (extraParams.isValid() && extraParams.type() == QVariant::Int && extraParams.toInt() != -1) + doc->writeAttribute(KXMLQLCVCWidgetInputLowerParams, QString::number(extraParams.toInt())); + + extraParams = source->feedbackExtraParams(QLCInputFeedback::UpperValue); + + if (extraParams.isValid() && extraParams.type() == QVariant::Int && extraParams.toInt() != -1) + doc->writeAttribute(KXMLQLCVCWidgetInputUpperParams, QString::number(extraParams.toInt())); + + extraParams = source->feedbackExtraParams(QLCInputFeedback::MonitorValue); + + if (extraParams.isValid() && extraParams.type() == QVariant::Int && extraParams.toInt() != -1) + doc->writeAttribute(KXMLQLCVCWidgetInputMonitorParams, QString::number(extraParams.toInt())); + doc->writeEndElement(); } diff --git a/qmlui/virtualconsole/vcwidget.h b/qmlui/virtualconsole/vcwidget.h index e63ee77d70..67bf43e613 100644 --- a/qmlui/virtualconsole/vcwidget.h +++ b/qmlui/virtualconsole/vcwidget.h @@ -54,12 +54,16 @@ #define KXMLQLCWindowStateWidth QString("Width") #define KXMLQLCWindowStateHeight QString("Height") -#define KXMLQLCVCWidgetKey QString("Key") -#define KXMLQLCVCWidgetInput QString("Input") -#define KXMLQLCVCWidgetInputUniverse QString("Universe") -#define KXMLQLCVCWidgetInputChannel QString("Channel") -#define KXMLQLCVCWidgetInputLowerValue QString("LowerValue") -#define KXMLQLCVCWidgetInputUpperValue QString("UpperValue") +#define KXMLQLCVCWidgetKey QString("Key") +#define KXMLQLCVCWidgetInput QString("Input") +#define KXMLQLCVCWidgetInputUniverse QString("Universe") +#define KXMLQLCVCWidgetInputChannel QString("Channel") +#define KXMLQLCVCWidgetInputLowerValue QString("LowerValue") +#define KXMLQLCVCWidgetInputUpperValue QString("UpperValue") +#define KXMLQLCVCWidgetInputMonitorValue QString("MonitorValue") +#define KXMLQLCVCWidgetInputLowerParams QString("LowerParams") +#define KXMLQLCVCWidgetInputUpperParams QString("UpperParams") +#define KXMLQLCVCWidgetInputMonitorParams QString("MonitorParams") typedef struct { @@ -480,7 +484,7 @@ class VCWidget : public QObject * Input sources *********************************************************************/ public: - enum SourceValueType { ExactValue, LowerValue, UpperValue }; + enum SourceValueType { ExactValue, LowerValue, UpperValue, MonitorValue }; Q_ENUM(SourceValueType) /** diff --git a/ui/src/virtualconsole/vcwidget.cpp b/ui/src/virtualconsole/vcwidget.cpp index 3c59e4325d..d418b79cc7 100644 --- a/ui/src/virtualconsole/vcwidget.cpp +++ b/ui/src/virtualconsole/vcwidget.cpp @@ -713,7 +713,7 @@ void VCWidget::sendFeedback(int value, QSharedPointer src, QVari if (acceptsInput() == false) return; - qDebug() << "Send feedback to uni" << src->universe() << "," << src->channel() << ", param" << extraParams; + //qDebug() << "[VCWidget] Send feedback to uni" << src->universe() << "," << src->channel() << ", param" << extraParams; m_doc->inputOutputMap()->sendFeedBack( src->universe(), src->channel(), value, From 83790cbda289e7740ff220a53cf23e6f0fd478bd Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Sun, 3 Mar 2024 21:57:44 +0100 Subject: [PATCH 088/212] engine: fix 16bit relative EFX (#1484) Cache EFXFixture channel addresses to save CPU at each write call --- engine/src/efx.cpp | 3 +- engine/src/efxfixture.cpp | 158 ++++++++++++++------- engine/src/efxfixture.h | 11 +- engine/src/genericfader.cpp | 5 +- engine/src/universe.cpp | 101 ++++++------- engine/src/universe.h | 21 ++- engine/test/efxfixture/efxfixture_test.cpp | 5 +- engine/test/universe/universe_test.cpp | 25 +--- 8 files changed, 188 insertions(+), 141 deletions(-) diff --git a/engine/src/efx.cpp b/engine/src/efx.cpp index ac303d744a..3a3d408874 100644 --- a/engine/src/efx.cpp +++ b/engine/src/efx.cpp @@ -1081,6 +1081,7 @@ QSharedPointer EFX::getFader(QList universes, quint32 fader->setBlendMode(blendMode()); fader->setName(name()); fader->setParentFunctionID(id()); + fader->setHandleSecondary(true); m_fadersMap[universeID] = fader; } @@ -1094,7 +1095,7 @@ void EFX::preRun(MasterTimer* timer) QListIterator it(m_fixtures); while (it.hasNext() == true) { - EFXFixture* ef = it.next(); + EFXFixture *ef = it.next(); Q_ASSERT(ef != NULL); ef->setSerialNumber(serialNumber++); } diff --git a/engine/src/efxfixture.cpp b/engine/src/efxfixture.cpp index 5ea670a520..45420be61e 100644 --- a/engine/src/efxfixture.cpp +++ b/engine/src/efxfixture.cpp @@ -53,6 +53,11 @@ EFXFixture::EFXFixture(const EFX* parent) , m_started(false) , m_elapsed(0) , m_currentAngle(0) + + , m_firstMsbChannel(QLCChannel::invalid()) + , m_firstLsbChannel(QLCChannel::invalid()) + , m_secondMsbChannel(QLCChannel::invalid()) + , m_secondLsbChannel(QLCChannel::invalid()) { Q_ASSERT(parent != NULL); @@ -382,8 +387,52 @@ uint EFXFixture::timeOffset() const * Running *****************************************************************************/ -void EFXFixture::start() +void EFXFixture::start(QSharedPointer fader) { + Fixture *fxi = doc()->fixture(head().fxi); + + /* Cache channels to reduce processing while running */ + switch (m_mode) + { + case PanTilt: + { + m_firstMsbChannel = fxi->channelNumber(QLCChannel::Pan, QLCChannel::MSB, head().head); + m_firstLsbChannel = fxi->channelNumber(QLCChannel::Pan, QLCChannel::LSB, head().head); + m_secondMsbChannel = fxi->channelNumber(QLCChannel::Tilt, QLCChannel::MSB, head().head); + m_secondLsbChannel = fxi->channelNumber(QLCChannel::Tilt, QLCChannel::LSB, head().head); + + /* Check for non-contiguous channels */ + if ((m_firstLsbChannel != QLCChannel::invalid() && m_firstLsbChannel - m_firstMsbChannel != 1) || + (m_secondLsbChannel != QLCChannel::invalid() && m_secondLsbChannel - m_secondMsbChannel != 1)) + { + fader->setHandleSecondary(false); + } + } + break; + + case RGB: + break; + + case Dimmer: + { + m_firstMsbChannel = fxi->channelNumber(QLCChannel::Intensity, QLCChannel::MSB, head().head); + if (m_firstMsbChannel != QLCChannel::invalid()) + { + m_firstLsbChannel = fxi->channelNumber(QLCChannel::Intensity, QLCChannel::LSB, head().head); + + /* Check for non-contiguous channels */ + if (m_firstLsbChannel != QLCChannel::invalid() && m_firstLsbChannel - m_firstMsbChannel != 1) + { + fader->setHandleSecondary(false); + } + } + else + { + m_firstMsbChannel = fxi->masterIntensityChannel(); + } + } + break; + } m_started = true; } @@ -432,7 +481,7 @@ void EFXFixture::nextStep(QList universes, QSharedPointerloopDuration(); @@ -463,7 +512,7 @@ void EFXFixture::nextStep(QList universes, QSharedPointersetStart(fc->current()); fc->setTarget(value); @@ -475,86 +524,97 @@ void EFXFixture::updateFaderValues(FadeChannel *fc, uchar value) void EFXFixture::setPointPanTilt(QList universes, QSharedPointer fader, float pan, float tilt) { - Fixture *fxi = doc()->fixture(head().fxi); - Q_ASSERT(fxi != NULL); + if (fader.isNull()) + return; + Universe *uni = universes[universe()]; //qDebug() << "Pan value: " << pan << ", tilt value:" << tilt; - quint32 panMsbChannel = fxi->channelNumber(QLCChannel::Pan, QLCChannel::MSB, head().head); - quint32 panLsbChannel = fxi->channelNumber(QLCChannel::Pan, QLCChannel::LSB, head().head); - quint32 tiltMsbChannel = fxi->channelNumber(QLCChannel::Tilt, QLCChannel::MSB, head().head); - quint32 tiltLsbChannel = fxi->channelNumber(QLCChannel::Tilt, QLCChannel::LSB, head().head); - + /* Check for outbound values */ if (pan < 0) pan = 0; if (tilt < 0) tilt = 0; - /* Write coarse point data to universes */ - if (panMsbChannel != QLCChannel::invalid() && !fader.isNull()) + /* Write full 16bit point data to universes */ + if (m_firstMsbChannel != QLCChannel::invalid()) { - FadeChannel *fc = fader->getChannelFader(doc(), uni, fxi->id(), panMsbChannel); + quint32 panValue = quint32(pan); + FadeChannel *fc = fader->getChannelFader(doc(), uni, head().fxi, m_firstMsbChannel); + if (m_firstLsbChannel != QLCChannel::invalid()) + { + if (fader->handleSecondary()) + { + fc = fader->getChannelFader(doc(), uni, head().fxi, m_firstLsbChannel); + panValue = (panValue << 8) + quint32((pan - floor(pan)) * float(UCHAR_MAX)); + } + else + { + FadeChannel *lsbFc = fader->getChannelFader(doc(), uni, head().fxi, m_firstLsbChannel); + updateFaderValues(lsbFc, quint32((pan - floor(pan)) * float(UCHAR_MAX))); + } + } if (m_parent->isRelative()) fc->addFlag(FadeChannel::Relative); - updateFaderValues(fc, static_cast(pan)); + + updateFaderValues(fc, panValue); } - if (tiltMsbChannel != QLCChannel::invalid() && !fader.isNull()) + if (m_secondMsbChannel != QLCChannel::invalid()) { - FadeChannel *fc = fader->getChannelFader(doc(), uni, fxi->id(), tiltMsbChannel); + quint32 tiltValue = quint32(tilt); + FadeChannel *fc = fader->getChannelFader(doc(), uni, head().fxi, m_secondMsbChannel); + if (m_secondLsbChannel != QLCChannel::invalid()) + { + if (fader->handleSecondary()) + { + fc = fader->getChannelFader(doc(), uni, head().fxi, m_secondLsbChannel); + tiltValue = (tiltValue << 8) + quint32((tilt - floor(tilt)) * float(UCHAR_MAX)); + } + else + { + FadeChannel *lsbFc = fader->getChannelFader(doc(), uni, head().fxi, m_secondLsbChannel); + updateFaderValues(lsbFc, quint32((tilt - floor(tilt)) * float(UCHAR_MAX))); + } + } if (m_parent->isRelative()) fc->addFlag(FadeChannel::Relative); - updateFaderValues(fc, static_cast(tilt)); - } - - /* Write fine point data to universes if applicable */ - if (panLsbChannel != QLCChannel::invalid() && !fader.isNull()) - { - /* Leave only the fraction */ - float value = ((pan - floor(pan)) * float(UCHAR_MAX)); - FadeChannel *fc = fader->getChannelFader(doc(), uni, fxi->id(), panLsbChannel); - updateFaderValues(fc, static_cast(value)); - } - if (tiltLsbChannel != QLCChannel::invalid() && !fader.isNull()) - { - /* Leave only the fraction */ - float value = ((tilt - floor(tilt)) * float(UCHAR_MAX)); - FadeChannel *fc = fader->getChannelFader(doc(), uni, fxi->id(), tiltLsbChannel); - updateFaderValues(fc, static_cast(value)); + updateFaderValues(fc, tiltValue); } } void EFXFixture::setPointDimmer(QList universes, QSharedPointer fader, float dimmer) { - Fixture *fxi = doc()->fixture(head().fxi); - Q_ASSERT(fxi != NULL); - Universe *uni = universes[universe()]; + if (fader.isNull()) + return; - quint32 intChannel = fxi->channelNumber(QLCChannel::Intensity, QLCChannel::MSB, head().head); + Universe *uni = universes[universe()]; /* Don't write dimmer data directly to universes but use FadeChannel to avoid steps at EFX loop restart */ - if (intChannel != QLCChannel::invalid()) - { - if (!fader.isNull()) - { - FadeChannel *fc = fader->getChannelFader(doc(), uni, fxi->id(), intChannel); - updateFaderValues(fc, dimmer); - } - } - else if (fxi->masterIntensityChannel() != QLCChannel::invalid()) + if (m_firstMsbChannel != QLCChannel::invalid()) { - if (!fader.isNull()) + quint32 dimmerValue = quint32(dimmer); + FadeChannel *fc = fader->getChannelFader(doc(), uni, head().fxi, m_firstMsbChannel); + + if (m_firstLsbChannel != QLCChannel::invalid()) { - FadeChannel *fc = fader->getChannelFader(doc(), uni, fxi->id(), fxi->masterIntensityChannel()); - updateFaderValues(fc, dimmer); + if (fader->handleSecondary()) + { + fc = fader->getChannelFader(doc(), uni, head().fxi, m_firstLsbChannel); + dimmerValue = (dimmerValue << 8) + quint32((dimmer - floor(dimmer)) * float(UCHAR_MAX)); + } } + updateFaderValues(fc, dimmerValue); } } void EFXFixture::setPointRGB(QList universes, QSharedPointer fader, float x, float y) { + if (fader.isNull()) + return; + Fixture* fxi = doc()->fixture(head().fxi); Q_ASSERT(fxi != NULL); Universe *uni = universes[universe()]; diff --git a/engine/src/efxfixture.h b/engine/src/efxfixture.h index a2b52a2984..d98c8fa36a 100644 --- a/engine/src/efxfixture.h +++ b/engine/src/efxfixture.h @@ -188,19 +188,26 @@ class EFXFixture * Running *************************************************************************/ private: - void start(); + void start(QSharedPointer fader); void stop(); /** Calculate the next step data for this fixture */ void nextStep(QList universes, QSharedPointer fader); - void updateFaderValues(FadeChannel *fc, uchar value); + /** Set a 16bit value on a fader gotten from the engine */ + void updateFaderValues(FadeChannel *fc, quint32 value); /** Write this EFXFixture's channel data to universe faders */ void setPointPanTilt(QList universes, QSharedPointer fader, float pan, float tilt); void setPointDimmer(QList universes, QSharedPointer fader, float dimmer); void setPointRGB (QList universes, QSharedPointer fader, float x, float y); +private: + quint32 m_firstMsbChannel; + quint32 m_firstLsbChannel; + quint32 m_secondMsbChannel; + quint32 m_secondLsbChannel; + private: static QImage m_rgbGradient; }; diff --git a/engine/src/genericfader.cpp b/engine/src/genericfader.cpp index f7fe2596c5..44f6461004 100644 --- a/engine/src/genericfader.cpp +++ b/engine/src/genericfader.cpp @@ -243,12 +243,11 @@ void GenericFader::write(Universe *universe) } else if (flags & FadeChannel::Relative) { - for (int i = 0; i < channelCount; i++) - universe->writeRelative(address + i, ((uchar *)&value)[channelCount - 1 - i]); + universe->writeRelative(address, value, channelCount); } else if (flags & FadeChannel::Flashing) { - universe->write(address, value, flags & FadeChannel::ForceLTP); + universe->writeMultiple(address, value, channelCount); continue; } else diff --git a/engine/src/universe.cpp b/engine/src/universe.cpp index cc53713a0c..ee2572d36a 100644 --- a/engine/src/universe.cpp +++ b/engine/src/universe.cpp @@ -35,7 +35,8 @@ #include "qlcfile.h" #include "utils.h" -#define RELATIVE_ZERO 127 +#define RELATIVE_ZERO_8BIT 0x7F +#define RELATIVE_ZERO_16BIT 0x7F00 #define KXMLUniverseNormalBlend "Normal" #define KXMLUniverseMaskBlend "Mask" @@ -62,7 +63,6 @@ Universe::Universe(quint32 id, GrandMaster *gm, QObject *parent) , m_blackoutValues(new QByteArray(UNIVERSE_SIZE, char(0))) , m_passthroughValues() { - m_relativeValues.fill(0, UNIVERSE_SIZE); m_modifiers.fill(NULL, UNIVERSE_SIZE); m_name = QString("Universe %1").arg(id + 1); @@ -298,7 +298,6 @@ void Universe::processFaders() { flushInput(); zeroIntensityChannels(); - zeroRelativeValues(); QMutableListIterator > it(m_faders); while (it.hasNext()) @@ -366,14 +365,10 @@ void Universe::reset() m_blackoutValues->fill(0); if (m_passthrough) - { (*m_postGMValues) = (*m_passthroughValues); - } else - { m_postGMValues->fill(0); - } - zeroRelativeValues(); + m_modifiers.fill(NULL, UNIVERSE_SIZE); m_passthrough = false; // not releasing m_passthroughValues, see comment in setPassthrough } @@ -388,7 +383,6 @@ void Universe::reset(int address, int range) memset(m_preGMValues->data() + address, 0, range * sizeof(*m_preGMValues->data())); memset(m_blackoutValues->data() + address, 0, range * sizeof(*m_blackoutValues->data())); - memset(m_relativeValues.data() + address, 0, range * sizeof(*m_relativeValues.data())); memcpy(m_postGMValues->data() + address, m_modifiedZeroValues->data() + address, range * sizeof(*m_postGMValues->data())); applyPassthroughValues(address, range); @@ -445,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) @@ -497,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) || @@ -548,8 +526,6 @@ void Universe::updatePostGMValue(int channel) { uchar value = preGMValue(channel); - value = applyRelative(channel, value); - if (value != 0) value = applyGM(channel, value); @@ -927,32 +903,32 @@ 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]" << id() << ": 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 (m_channelsMask->at(channel) & HTP) + if (m_channelsMask->at(address) & HTP) { - if (forceLTP == false && value < (uchar)m_preGMValues->at(channel)) + if (forceLTP == false && value < (uchar)m_preGMValues->at(address)) { - qDebug() << "[Universe] HTP check not passed" << channel << value; + qDebug() << "[Universe] HTP check not passed" << address << value; return false; } } else { // preserve non HTP channels for blackout - (*m_blackoutValues)[channel] = char(value); + (*m_blackoutValues)[address] = char(value); } - (*m_preGMValues)[channel] = char(value); + (*m_preGMValues)[address] = char(value); - updatePostGMValue(channel); + updatePostGMValue(address); return true; } @@ -961,6 +937,8 @@ bool Universe::writeMultiple(int address, quint32 value, int channelCount) { for (int i = 0; i < channelCount; i++) { + //qDebug() << "[Universe]" << id() << ": write channel" << (address + i) << ", value:" << QString::number(((uchar *)&value)[channelCount - 1 - i]); + // preserve non HTP channels for blackout if ((m_channelsMask->at(address + i) & HTP) == 0) (*m_blackoutValues)[address + i] = ((uchar *)&value)[channelCount - 1 - i]; @@ -973,41 +951,56 @@ bool Universe::writeMultiple(int address, quint32 value, int channelCount) return true; } -bool Universe::writeRelative(int channel, uchar value) +bool Universe::writeRelative(int address, quint32 value, int channelCount) { - Q_ASSERT(channel < UNIVERSE_SIZE); + Q_ASSERT(address < UNIVERSE_SIZE); - //qDebug() << "Write relative channel" << channel << value; + //qDebug() << "Write relative channel" << address << "value" << value; - if (channel >= m_usedChannels) - m_usedChannels = channel + 1; + if (address + channelCount >= m_usedChannels) + m_usedChannels = address + channelCount; - if (value == RELATIVE_ZERO) - return true; + if (channelCount == 1) + { + short newVal = uchar((*m_preGMValues)[address]); + newVal += short(value) - RELATIVE_ZERO_8BIT; + (*m_preGMValues)[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)); - m_relativeValues[channel] += value - RELATIVE_ZERO; + currentValue += (value - RELATIVE_ZERO_16BIT); - updatePostGMValue(channel); + for (int i = 0; i < channelCount; i++) + { + (*m_preGMValues)[address + i] = ((uchar *)¤tValue)[channelCount - 1 - i]; + updatePostGMValue(address + i); + } + } return true; } -bool Universe::writeBlended(int channel, quint32 value, int channelCount, Universe::BlendMode blend) +bool Universe::writeBlended(int address, quint32 value, int channelCount, Universe::BlendMode blend) { - if (channel + channelCount - 1 >= m_usedChannels) - m_usedChannels = channel + channelCount; + 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(channel + i)); + currentValue = (currentValue << 8) + uchar(m_preGMValues->at(address + i)); switch (blend) { case NormalBlend: { - if ((m_channelsMask->at(channel) & HTP) && value < currentValue) + if ((m_channelsMask->at(address) & HTP) && value < currentValue) { - qDebug() << "[Universe] HTP check not passed" << channel << value; + qDebug() << "[Universe] HTP check not passed" << address << value; return false; } } @@ -1044,7 +1037,7 @@ bool Universe::writeBlended(int channel, quint32 value, int channelCount, Univer break; } - writeMultiple(channel, value, channelCount); + writeMultiple(address, value, channelCount); return true; } diff --git a/engine/src/universe.h b/engine/src/universe.h index 7503c149fb..bffc109cea 100644 --- a/engine/src/universe.h +++ b/engine/src/universe.h @@ -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); @@ -437,9 +436,6 @@ public slots: /** Return a list with intensity channels and their values */ QHash intensityChannels(); - /** Set all channel relative values to zero */ - void zeroRelativeValues(); - protected: void applyPassthroughValues(int address, int range); @@ -488,8 +484,6 @@ public slots: /** Array of values from input line, when passtrhough is enabled */ QScopedPointer m_passthroughValues; - QVector m_relativeValues; - /* impl speedup */ void updateIntensityChannelsRanges(); @@ -518,17 +512,17 @@ 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 + * @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 @@ -539,26 +533,27 @@ public slots: * 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, quint32 value, int channelCount, BlendMode blend); + bool writeBlended(int address, quint32 value, int channelCount, BlendMode blend); /********************************************************************* * Load & Save diff --git a/engine/test/efxfixture/efxfixture_test.cpp b/engine/test/efxfixture/efxfixture_test.cpp index ee3d638f1b..09081da611 100644 --- a/engine/test/efxfixture/efxfixture_test.cpp +++ b/engine/test/efxfixture/efxfixture_test.cpp @@ -30,7 +30,6 @@ #include "qlcfixturemode.h" #include "qlcfixturedef.h" #include "genericfader.h" -#include "fadechannel.h" #include "efxfixture.h" #include "qlcchannel.h" #include "universe.h" @@ -454,6 +453,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 +474,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 +494,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 +514,7 @@ void EFXFixture_Test::setPointLedBar() Universe *universe = ua[0]; QSharedPointer fader = universe->requestFader(); + ef.start(fader); ef.setPointPanTilt(ua, fader, 5.4, 1.5); // PMSB: 5, PLSB: 0.4, TMSB: 1 (102), TLSB: 0.5(127) QCOMPARE(fader->channels().count(), 1); universe->processFaders(); diff --git a/engine/test/universe/universe_test.cpp b/engine/test/universe/universe_test.cpp index 48ede51aa0..de42bfbdb9 100644 --- a/engine/test/universe/universe_test.cpp +++ b/engine/test/universe/universe_test.cpp @@ -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)); } From 9a4229903ed0d0817bcb48702a13d102e6cabd4b Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Sat, 9 Mar 2024 19:46:18 +0100 Subject: [PATCH 089/212] resources: 9 new fixtures (see changelog) --- debian/changelog | 5 + resources/fixtures/Acme/Acme-Dotline180.qxf | 187 ++ resources/fixtures/Acme/Acme-Dotline360.qxf | 409 +++ resources/fixtures/Acme/Acme-Oxygen.qxf | 363 +++ .../fixtures/Acme/Acme-Super-Dotline.qxf | 2484 +++++++++++++++++ .../fixtures/Elation/Elation-Paladin.qxf | 309 ++ resources/fixtures/Elumen8/Elumen8-MP-120.qxf | 27 + .../fixtures/Elumen8/Elumen8-MP-60-Mk1.qxf | 23 + .../fixtures/Elumen8/Elumen8-MP-60-Mk2.qxf | 23 + ...urolite-LED-KLS-Scan-Pro-Next-FX-Light.qxf | 491 ++++ resources/fixtures/FixturesMap.xml | 11 + 11 files changed, 4332 insertions(+) create mode 100644 resources/fixtures/Acme/Acme-Dotline180.qxf create mode 100644 resources/fixtures/Acme/Acme-Dotline360.qxf create mode 100644 resources/fixtures/Acme/Acme-Oxygen.qxf create mode 100644 resources/fixtures/Acme/Acme-Super-Dotline.qxf create mode 100644 resources/fixtures/Elation/Elation-Paladin.qxf create mode 100644 resources/fixtures/Elumen8/Elumen8-MP-120.qxf create mode 100644 resources/fixtures/Elumen8/Elumen8-MP-60-Mk1.qxf create mode 100644 resources/fixtures/Elumen8/Elumen8-MP-60-Mk2.qxf create mode 100644 resources/fixtures/Eurolite/Eurolite-LED-KLS-Scan-Pro-Next-FX-Light.qxf diff --git a/debian/changelog b/debian/changelog index f4b588d0d8..90e7a42acb 100644 --- a/debian/changelog +++ b/debian/changelog @@ -2,6 +2,7 @@ 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 @@ -73,6 +74,10 @@ qlcplus (4.13.0) stable; urgency=low * 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 (thank to Michael Tosatto) -- Massimo Callegari Sun, 10 Mar 2024 12:13:14 +0200 diff --git a/resources/fixtures/Acme/Acme-Dotline180.qxf b/resources/fixtures/Acme/Acme-Dotline180.qxf new file mode 100644 index 0000000000..b67157debb --- /dev/null +++ b/resources/fixtures/Acme/Acme-Dotline180.qxf @@ -0,0 +1,187 @@ + + + + + Q Light Controller Plus + 4.13.0 GIT + Michael Tosatto + + Acme + Dotline180 + Moving Head + + + + Speed + Auto speed + Tilt (Slow to fast) + Auto speed + + + + + + Shutter + Shutter closed + Shutter open + Strobe effect (Fast to slow) + Shutter open + Pulse-effect in sequences + Shutter open + Random strobe effect (Slow to fast) + Shutter open + + + + + + + + + + + + + + + + + + + + + + + + + + + Maintenance + No function + Black out when Tilt moving + Disable Black out when Tilt moving + Reset All Motor + Reset Tilt Motor + Reset Zoom Motor + Dimmer Speed: Smooth + Dimmer Speed: Fast + No function + + + Effect + No function + Macro 1 + Macro 2 + Macro 3 + Macro 4 + Macro 5 + Macro 6 + Macro 7 + + + Colour + Foreground Color + + + Colour + Background Color + + + + + + + Tilt + Tilt fine + Tilt speed + Zoom + Dimmer + Dimmer fine + Strobe + LED1 Red + LED1 Green + LED1 Blue + LED1 White + LED2 Red + LED2 Green + LED2 Blue + LED2 White + LED3 Red + LED3 Green + LED3 Blue + LED3 White + LED4 Red + LED4 Green + LED4 Blue + LED4 White + LED5 Red + LED5 Green + LED5 Blue + LED5 White + LED6 Red + LED6 Green + LED6 Blue + LED6 White + Special Function + + 7 + 8 + 9 + 10 + + + 11 + 12 + 13 + 14 + + + 15 + 16 + 17 + 18 + + + 19 + 20 + 21 + 22 + + + 23 + 24 + 25 + 26 + + + 27 + 28 + 29 + 30 + + + + Tilt + Tilt fine + Zoom + Dimmer + Dimmer fine + Strobe + Macro + Foreground Color + Background Color + Red + Green + Blue + White + Special Function + + + + + + + + + + diff --git a/resources/fixtures/Acme/Acme-Dotline360.qxf b/resources/fixtures/Acme/Acme-Dotline360.qxf new file mode 100644 index 0000000000..a6a44680a5 --- /dev/null +++ b/resources/fixtures/Acme/Acme-Dotline360.qxf @@ -0,0 +1,409 @@ + + + + + Q Light Controller Plus + 4.13.0 GIT + Michael Tosatto + + Acme + Dotline360 + Moving Head + + + + Speed + Auto speed + Tilt (Slow to fast) + Auto speed + + + + + + Shutter + Shutter closed + Shutter open + Strobe effect (Fast to slow) + Shutter open + Pulse-effect in sequences + Shutter open + Random strobe effect (Slow to fast) + Shutter open + + + + + + + + + + + + + + + + + + + + + + + + + + + Maintenance + No function + Black out when Tilt moving + Disable Black out when Tilt moving + Reset All Motor + Reset Tilt Motor + Reset Zoom Motor + Dimmer Speed: Smooth + Dimmer Speed: Fast + Invert Pixel Order: No + Invert Pixel Order: Yes + No function + + + Effect + No function + Macro 1 + Macro 2 + Macro 3 + Macro 4 + Macro 5 + Macro 6 + Macro 7 + + + Colour + Foreground Color + + + Colour + Background Color + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Tilt + Tilt fine + Tilt speed + Zoom 1 + Zoom 2 + Dimmer + Dimmer fine + Strobe + LED1 Red + LED1 Green + LED1 Blue + LED1 White + LED2 Red + LED2 Green + LED2 Blue + LED2 White + LED3 Red + LED3 Green + LED3 Blue + LED3 White + LED4 Red + LED4 Green + LED4 Blue + LED4 White + LED5 Red + LED5 Green + LED5 Blue + LED5 White + LED6 Red + LED6 Green + LED6 Blue + LED6 White + LED7 Red + LED7 Green + LED7 Blue + LED7 White + LED8 Red + LED8 Green + LED8 Blue + LED8 White + LED9 Red + LED9 Green + LED9 Blue + LED9 White + LED10 Red + LED10 Green + LED10 Blue + LED10 White + LED11 Red + LED11 Green + LED11 Blue + LED11 White + LED12 Red + LED12 Green + LED12 Blue + LED12 White + Special Function + + 8 + 9 + 10 + 11 + + + 12 + 13 + 14 + 15 + + + 16 + 17 + 18 + 19 + + + 20 + 21 + 22 + 23 + + + 24 + 25 + 26 + 27 + + + 28 + 29 + 30 + 31 + + + 34 + 33 + 32 + 35 + + + 39 + 38 + 37 + 36 + + + 43 + 42 + 41 + 40 + + + 47 + 46 + 45 + 44 + + + 51 + 50 + 49 + 48 + + + 55 + 54 + 53 + 52 + + + + Tilt + Tilt fine + Zoom + Dimmer + Dimmer fine + Strobe + Macro + Foreground Color + Background Color + Red + Green + Blue + White + Special Function + + + Tilt + Tilt fine + Tilt speed + Zoom 1 + Zoom 2 + Dimmer + Dimmer fine + Strobe + Special Function + + + LED1 Red + LED1 Green + LED1 Blue + LED1 White + LED2 Red + LED2 Green + LED2 Blue + LED2 White + LED3 Red + LED3 Green + LED3 Blue + LED3 White + LED4 Red + LED4 Green + LED4 Blue + LED4 White + LED5 Red + LED5 Green + LED5 Blue + LED5 White + LED6 Red + LED6 Green + LED6 Blue + LED6 White + LED7 Red + LED7 Green + LED7 Blue + LED7 White + LED8 Red + LED8 Green + LED8 Blue + LED8 White + LED9 Red + LED9 Green + LED9 Blue + LED9 White + LED10 Red + LED10 Green + LED10 Blue + LED10 White + LED11 Red + LED11 Green + LED11 Blue + LED11 White + LED12 Red + LED12 Green + LED12 Blue + LED12 White + + 0 + 1 + 2 + 3 + + + 4 + 5 + 6 + 7 + + + 8 + 9 + 10 + 11 + + + 12 + 13 + 14 + 15 + + + 16 + 17 + 18 + 19 + + + 20 + 21 + 22 + 23 + + + 24 + 25 + 26 + 27 + + + 28 + 29 + 30 + 31 + + + 32 + 33 + 34 + 35 + + + 36 + 37 + 38 + 39 + + + 40 + 41 + 42 + 43 + + + 45 + 46 + 47 + 44 + + + + + + + + + + + diff --git a/resources/fixtures/Acme/Acme-Oxygen.qxf b/resources/fixtures/Acme/Acme-Oxygen.qxf new file mode 100644 index 0000000000..11429cba75 --- /dev/null +++ b/resources/fixtures/Acme/Acme-Oxygen.qxf @@ -0,0 +1,363 @@ + + + + + Q Light Controller Plus + 4.13.0 GIT + Michael Tosatto + + Acme + Oxygen + Moving Head + + + + + + + + + Shutter + Close + Open + Strobe (Slow to fast) + Open + Fast Close Slow Open (Slow to fast) + Open + Fast Open Slow Close (Slow to fast) + Open + Random Strobe (Slow to fast) + Open + + + + + + + Colour + Null + 8000K + 7900K + 7800K + 7700K + 7600K + 7500K + 7400K + 7300K + 7200K + 7100K + 7000K + 6900K + 6800K + 6700K + 6600K + 6500K + 6400K + 6300K + 6200K + 6100K + 6000K + 5900K + 5800K + 5700K + 5600K + 5500K + 5400K + 5300K + 5200K + 5100K + 5000K + 4900K + 4800K + 4700K + 4600K + 4500K + 4400K + 4300K + 4200K + 4100K + 4000K + 3900K + 3800K + 3700K + 3600K + 3500K + 3400K + 3300K + 3200K + 3100K + 3000K + 2900K + 2800K + 2700K + 2600K + 2500K + + + Colour + Null + Color 1 + Color 2 + Color 3 + Color 4 + Color 5 + Color 6 + Color 7 + Color 8 + Color 9 + Color 10 + Color 11 + Color 12 + Color 13 + Color 14 + Color 15 + Color 16 + Color 17 + Color 18 + Color 19 + Color 20 + Color 21 + Color 22 + Color 23 + Color 24 + Color 25 + Color 26 + Color 27 + Color 28 + Color 29 + Color 30 + Color 31 + Color 32 + Clockwise Rotation, Fast to Slow + Counter-Clockwise Rotation, Slow to Fast + Red>Green (Fast to slow) + Red>Blue (Fast to slow) + Red>White (Fast to slow) + Green>Blue (Fast to slow) + Green>White (Fast to slow) + Blue>White (Fast to slow) + + + Beam + Open + Pattern 1 + Pattern 2 + Pattern 3 + Pattern 4 + Pattern 5 + Pattern 6 + Pattern 7 + Pattern 8 + Pattern 9 + Pattern 10 + Pattern 11 + Pattern 12 + Pattern 13 + Pattern 14 + Pattern 15 + Pattern 16 + Pattern 17 + Pattern 18 + Pattern 19 + Pattern 20 + Pattern 21 + Pattern 22 + Pattern 23 + Pattern 24 + Pattern 25 + Pattern 26 + Pattern 27 + Pattern 28 + Pattern 29 + Pattern 30 + Pattern 31 + Pattern 32 + Pattern 33 + Pattern 34 + Pattern 35 + Pattern 36 + Pattern 37 + Pattern 38 + Pattern 39 + Pattern 40 + Pattern 41 + Pattern 42 + Pattern 43 + Pattern 44 + Pattern 45 + Pattern 46 + Pattern 47 + Pattern 48 + Pattern 49 + Pattern 50 + Pattern 51 + Pattern 52 + Pattern 53 + Pattern 54 + Pattern 55 + Pattern 56 + Pattern 57 + Pattern 58 + Pattern 59 + Pattern 60 + Random (Pattern 1-7) + Pattern 61 + Open + + + Effect + Null + Clockwise rotation (Fast to slow) + Null + Counter-Clockwise Rotation (Slow to fast) + Null + + + Maintenance + Null + Dimmer Speed: Smooth + Dimmer Speed: Fast + Null + Pan/Tilt Reset + Effect Reset + Null + Reset All + Null + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Pan + Pan fine + Tilt + Tilt fine + Zoom + Dimmer + Dimmer fine + Strobe + Red + Green + Blue + White + CTO + Color macro + Pixel select + Pixel rotation + Function + + + Pan + Pan fine + Tilt + Tilt fine + Zoom + Dimmer + Dimmer fine + Strobe + Red 1 + Green 1 + Blue 1 + White 1 + Red 2 + Green 2 + Blue 2 + White 2 + Red 3 + Green 3 + Blue 3 + White 3 + Red 4 + Green 4 + Blue 4 + White 4 + Red 5 + Green 5 + Blue 5 + White 5 + Red 6 + Green 6 + Blue 6 + White 6 + Red 7 + Green 7 + Blue 7 + White 7 + Function + + 8 + 9 + 10 + 11 + + + 14 + 13 + 12 + 15 + + + 19 + 18 + 17 + 16 + + + 20 + 21 + 22 + 23 + + + 24 + 25 + 27 + 26 + + + 28 + 29 + 30 + 31 + + + 32 + 33 + 34 + 35 + + + + + + + + + + + diff --git a/resources/fixtures/Acme/Acme-Super-Dotline.qxf b/resources/fixtures/Acme/Acme-Super-Dotline.qxf new file mode 100644 index 0000000000..fff8798f29 --- /dev/null +++ b/resources/fixtures/Acme/Acme-Super-Dotline.qxf @@ -0,0 +1,2484 @@ + + + + + Q Light Controller Plus + 4.13.0 GIT + Michael Tosatto + + Acme + Super Dotline + Moving Head + + + + + + Shutter + Close + Open + Strobe (Slow to fast) + Open + Slow Open Fast Close (Slow to fast) + Open + Fast Open Slow Close (Slow to fast) + Open + Random Strobe (Slow to fast) + Open + + + + + + + + + + + + + + + + + + + + + + + + + + + Maintenance + Null + Dimmer Curve: Linear + Dimmer Curve: Square Law + Dimmer Curve: Inv SQ Law + Dimmer Curve: S Curve + Power Mode: Standard + Null + Power Mode: Quiet + LED Frequency Setting Enable + LED Frequency Setting Disable + Null + 900Hz + 1000Hz + 1100Hz + 1200Hz + 1300Hz + 1400Hz + 1500Hz + 2500Hz + 4000Hz + 5000Hz + 6000Hz + 10KHz + 15KHz + 20KHz + 25KHz + Null + Tilt Reset + Effect Reset + CCT Calibration: On + CCT Calibration: Off + Null + All Reset + Dimmer Speed: Fast + Dimmer Speed: Smooth + Line Power Mode: Off + Line Power Mode: On + Null + + + Effect + No function + Macro 1 + Macro 2 + Macro 3 + Macro 4 + Macro 5 + Macro 6 + Macro 7 + + + + + + + + + + + + + + + + + + + + + + + Shutter + Close + Open + Strobe (Slow to fast) + Open + Slow Open Fast Close (Slow to fast) + Open + Fast Open Slow Close (Slow to fast) + Open + Random Strobe (Slow to fast) + Open + + + Shutter + Close + Open + Strobe (Slow to fast) + Open + Slow Open Fast Close (Slow to fast) + Open + Fast Open Slow Close (Slow to fast) + Open + Random Strobe (Slow to fast) + Open + + + + + + + + Colour + Null + 8000K + 7900K + 7800K + 7700K + 7600K + 7500K + 7400K + 7300K + 7200K + 7100K + 7000K + 6900K + 6800K + 6700K + 6600K + 6500K + 6400K + 6300K + 6200K + 6100K + 6000K + 5900K + 5800K + 5700K + 5600K + 5500K + 5400K + 5300K + 5200K + 5100K + 5000K + 4900K + 4800K + 4700K + 4600K + 4500K + 4400K + 4300K + 4200K + 4100K + 4000K + 3900K + 3800K + 3700K + 3600K + 3500K + 3400K + 3300K + 3200K + 3100K + 3000K + 2900K + 2800K + 2700K + 2600K + 2500K + + + Colour + Null + Color 1 + Color 2 + Color 3 + Color 4 + Color 5 + Color 6 + Color 7 + Color 8 + Color 9 + Color 10 + Color 11 + Color 12 + Color 13 + Color 14 + Color 15 + Color 16 + Color 17 + Color 18 + Color 19 + Color 20 + Color 21 + Color 22 + Color 23 + Color 24 + Color 25 + Color 26 + Color 27 + Color 28 + Color 29 + Color 30 + Color 31 + + + Effect + Null + Built-in Effect 1 + Built-in Effect 2 + Built-in Effect 3 + Built-in Effect 4 + Built-in Effect 5 + Built-in Effect 6 + Built-in Effect 7 + Built-in Effect 8 + Built-in Effect 9 + Built-in Effect 10 + Built-in Effect 11 + Built-in Effect 12 + Built-in Effect 13 + Built-in Effect 14 + Built-in Effect 15 + Built-in Effect 16 + Built-in Effect 17 + Built-in Effect 18 + Built-in Effect 19 + Built-in Effect 20 + Built-in Effect 21 + Built-in Effect 22 + Built-in Effect 23 + Null + + + Speed + Slow to Fast without Fade + Slow to Fast with Fade + + + Colour + Null + 6500K + 6400K + 6300K + 6200K + 6100K + 6000K + 5900K + 5800K + 5700K + 5600K + 5500K + 5400K + 5300K + 5200K + 5100K + 5000K + 4900K + 4800K + 4700K + 4600K + 4500K + 4400K + 4300K + 4200K + 4100K + 4000K + 3900K + 3800K + 3700K + 3600K + 3500K + 3400K + 3300K + 3200K + 3100K + 3000K + 2900K + 2800K + 2700K + 2600K + 2500K + + + Effect + Null + Built-in Effect 1 + Built-in Effect 2 + Built-in Effect 3 + Built-in Effect 4 + Built-in Effect 5 + Built-in Effect 6 + Built-in Effect 7 + Built-in Effect 8 + Built-in Effect 9 + Built-in Effect 10 + Built-in Effect 11 + Built-in Effect 12 + Built-in Effect 13 + Built-in Effect 14 + Built-in Effect 15 + Built-in Effect 16 + Built-in Effect 17 + Built-in Effect 18 + Built-in Effect 19 + Built-in Effect 20 + Built-in Effect 21 + Built-in Effect 22 + Built-in Effect 23 + Null + + + Speed + Slow to Fast without Fade + Slow to Fast with Fade + + + Colour + Null + Color 1 + Color 2 + Color 3 + Color 4 + Color 5 + Color 6 + Color 7 + Color 8 + Color 9 + Color 10 + Color 11 + Color 12 + Color 13 + Color 14 + Color 15 + Color 16 + Color 17 + Color 18 + Color 19 + Color 20 + Color 21 + Color 22 + Color 23 + Color 24 + Color 25 + Color 26 + Color 27 + Color 28 + Color 29 + Color 30 + Color 31 + + + Effect + Null + Built-in Effect 1 + Built-in Effect 2 + Built-in Effect 3 + Built-in Effect 4 + Built-in Effect 5 + Built-in Effect 6 + Built-in Effect 7 + Built-in Effect 8 + Built-in Effect 9 + Built-in Effect 10 + Built-in Effect 11 + Built-in Effect 12 + Built-in Effect 13 + Built-in Effect 14 + Built-in Effect 15 + Built-in Effect 16 + Built-in Effect 17 + Built-in Effect 18 + Built-in Effect 19 + Built-in Effect 20 + Built-in Effect 21 + Built-in Effect 22 + Built-in Effect 23 + Null + + + Speed + Slow to Fast without Fade + Slow to Fast with Fade + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Tilt + Tilt fine + Zoom + Dimmer + Main Strobe + LINE_W Strobe + LINE_RGB Strobe + Special Function + Main Red + Main Green + Main Blue + Main White + LINE_W CW + LINE_W WW + LINE_RGB Red + LINE_RGB Green + LINE_RGB Blue + + 4 + 8 + 9 + 10 + 11 + 2 + + + 5 + 13 + 12 + + + 6 + 16 + 15 + 14 + + + + + + + + + + + + Tilt + Tilt fine + Zoom + Dimmer + Main Strobe + LINE_W Strobe + LINE_RGB Strobe + Special Function + Main Red + Main Green + Main Blue + Main White + Main CCT + Main Color + Main Macro + Main Macro Speed + LINE_W WW + LINE_W CW + LINE_W CCT + LINE_W Macro + LINE_W Macro Speed + LINE_RGB Red + LINE_RGB Green + LINE_RGB Blue + LINE_RGB Color + LINE_RGB Macro + LINE_RGB Macro Speed + + 8 + 9 + 10 + 11 + 4 + 15 + 14 + 13 + 12 + 2 + + + 5 + 16 + 17 + 18 + 19 + 20 + + + 6 + 21 + 22 + 23 + 24 + 25 + 26 + + + + Tilt + Tilt fine + Zoom + Dimmer + Main Strobe + LINE_W Strobe + LINE_RGB Strobe + Special Function + Main Red 1 + Main Green 1 + Main Blue 1 + Main White 1 + Main Red 2 + Main Green 2 + Main Blue 2 + Main White 2 + Main Red 3 + Main Green 3 + Main Blue 3 + Main White 3 + Main Red 4 + Main Green 4 + Main Blue 4 + Main White 4 + Main Red 5 + Main Green 5 + Main Blue 5 + Main White 5 + Main Red 6 + Main Green 6 + Main Blue 6 + Main White 6 + Main Red 7 + Main Green 7 + Main Blue 7 + Main White 7 + Main Red 8 + Main Green 8 + Main Blue 8 + Main White 8 + Main Red 9 + Main Green 9 + Main Blue 9 + Main White 9 + Main Red 10 + Main Green 10 + Main Blue 10 + Main White 10 + LINE_W CW 1 + LINE_W WW 1 + LINE_W CW 2 + LINE_W WW 2 + LINE_W CW 3 + LINE_W WW 3 + LINE_W CW 4 + LINE_W WW 4 + LINE_W CW 5 + LINE_W WW 5 + LINE_W CW 6 + LINE_W WW 6 + LINE_W CW 7 + LINE_W WW 7 + LINE_W CW 8 + LINE_W WW 8 + LINE_W CW 9 + LINE_W WW 9 + LINE_W CW 10 + LINE_W WW 10 + LINE_W CW 11 + LINE_W WW 11 + LINE_W CW 12 + LINE_W WW 12 + LINE_W CW 13 + LINE_W WW 13 + LINE_W CW 14 + LINE_W WW 14 + LINE_W CW 15 + LINE_W WW 15 + LINE_W CW 16 + LINE_W WW 16 + LINE_W CW 17 + LINE_W WW 17 + LINE_W CW 18 + LINE_W WW 18 + LINE_W CW 19 + LINE_W WW 19 + LINE_W CW 20 + LINE_W WW 20 + LINE_W CW 21 + LINE_W WW 21 + LINE_W CW 22 + LINE_W WW 22 + LINE_W CW 23 + LINE_W WW 23 + LINE_W CW 24 + LINE_W WW 24 + LINE_RGB Red + LINE_RGB Green + LINE_RGB Blue + + 8 + 9 + 10 + 11 + + + 12 + 13 + 14 + 15 + + + 16 + 17 + 18 + 19 + + + 20 + 21 + 22 + 23 + + + 24 + 25 + 26 + 27 + + + 28 + 29 + 30 + 31 + + + 32 + 33 + 34 + 35 + + + 36 + 37 + 38 + 39 + + + 40 + 41 + 42 + 43 + + + 44 + 45 + 46 + 47 + + + 48 + 49 + + + 50 + 51 + + + 52 + 53 + + + 54 + 55 + + + 56 + 57 + + + 58 + 59 + + + 60 + 61 + + + 62 + 63 + + + 64 + 65 + + + 66 + 67 + + + 68 + 69 + + + 70 + 71 + + + 72 + 73 + + + 74 + 75 + + + 76 + 77 + + + 78 + 79 + + + 80 + 81 + + + 82 + 83 + + + 84 + 85 + + + 86 + 87 + + + 88 + 89 + + + 90 + 91 + + + 92 + 93 + + + 94 + 95 + + + 96 + 97 + 98 + + + + Tilt + Tilt fine + Zoom + Dimmer + Main Strobe + LINE_W Strobe + LINE_RGB Strobe + Special Function + Main Red 1 + Main Green 1 + Main Blue 1 + Main White 1 + Main Red 2 + Main Green 2 + Main Blue 2 + Main White 2 + Main Red 3 + Main Green 3 + Main Blue 3 + Main White 3 + Main Red 4 + Main Green 4 + Main Blue 4 + Main White 4 + Main Red 5 + Main Green 5 + Main Blue 5 + Main White 5 + Main Red 6 + Main Green 6 + Main Blue 6 + Main White 6 + Main Red 7 + Main Green 7 + Main Blue 7 + Main White 7 + Main Red 8 + Main Green 8 + Main Blue 8 + Main White 8 + Main Red 9 + Main Green 9 + Main Blue 9 + Main White 9 + Main Red 10 + Main Green 10 + Main Blue 10 + Main White 10 + LINE_W CW 1 + LINE_W WW 1 + LINE_W CW 2 + LINE_W WW 2 + LINE_W CW 3 + LINE_W WW 3 + LINE_W CW 4 + LINE_W WW 4 + LINE_W CW 5 + LINE_W WW 5 + LINE_W CW 6 + LINE_W WW 6 + LINE_W CW 7 + LINE_W WW 7 + LINE_W CW 8 + LINE_W WW 8 + LINE_W CW 9 + LINE_W WW 9 + LINE_W CW 10 + LINE_W WW 10 + LINE_W CW 11 + LINE_W WW 11 + LINE_W CW 12 + LINE_W WW 12 + LINE_RGB Red 1 + LINE_RGB Green 1 + LINE_RGB Blue 1 + LINE_RGB Red 2 + LINE_RGB Green 2 + LINE_RGB Blue 2 + LINE_RGB Red 3 + LINE_RGB Green 3 + LINE_RGB Blue 3 + LINE_RGB Red 4 + LINE_RGB Green 4 + LINE_RGB Blue 4 + LINE_RGB Red 5 + LINE_RGB Green 5 + LINE_RGB Blue 5 + LINE_RGB Red 6 + LINE_RGB Green 6 + LINE_RGB Blue 6 + LINE_RGB Red 7 + LINE_RGB Green 7 + LINE_RGB Blue 7 + LINE_RGB Red 8 + LINE_RGB Green 8 + LINE_RGB Blue 8 + LINE_RGB Red 9 + LINE_RGB Green 9 + LINE_RGB Blue 9 + LINE_RGB Red 10 + LINE_RGB Green 10 + LINE_RGB Blue 10 + LINE_RGB Red 11 + LINE_RGB Green 11 + LINE_RGB Blue 11 + LINE_RGB Red 12 + LINE_RGB Green 12 + LINE_RGB Blue 12 + + 8 + 9 + 10 + 11 + + + 12 + 13 + 14 + 15 + + + 16 + 17 + 18 + 19 + + + 20 + 21 + 22 + 23 + + + 27 + 26 + 25 + 24 + + + 28 + 29 + 30 + 31 + + + 32 + 33 + 34 + 35 + + + 36 + 37 + 38 + 39 + + + 40 + 41 + 42 + 43 + + + 44 + 45 + 46 + 47 + + + 48 + 49 + + + 50 + 51 + + + 52 + 53 + + + 54 + 55 + + + 56 + 57 + + + 58 + 59 + + + 60 + 61 + + + 62 + 63 + + + 64 + 65 + + + 66 + 67 + + + 68 + 69 + + + 70 + 71 + + + 72 + 73 + 74 + + + 75 + 76 + 77 + + + 78 + 79 + 80 + + + 81 + 82 + 83 + + + 84 + 85 + 86 + + + 87 + 88 + 89 + + + 90 + 91 + 92 + + + 93 + 94 + 95 + + + 96 + 97 + 98 + + + 99 + 100 + 101 + + + 102 + 103 + 104 + + + 105 + 106 + 107 + + + + + + + + + + + + Tilt + Tilt fine + Zoom + Dimmer + Main Strobe + LINE_W Strobe + LINE_RGB Strobe + Special Function + Main Red 1 + Main Green 1 + Main Blue 1 + Main White 1 + Main Red 2 + Main Green 2 + Main Blue 2 + Main White 2 + Main Red 3 + Main Green 3 + Main Blue 3 + Main White 3 + Main Red 4 + Main Green 4 + Main Blue 4 + Main White 4 + Main Red 5 + Main Green 5 + Main Blue 5 + Main White 5 + Main Red 6 + Main Green 6 + Main Blue 6 + Main White 6 + Main Red 7 + Main Green 7 + Main Blue 7 + Main White 7 + Main Red 8 + Main Green 8 + Main Blue 8 + Main White 8 + Main Red 9 + Main Green 9 + Main Blue 9 + Main White 9 + Main Red 10 + Main Green 10 + Main Blue 10 + Main White 10 + LINE_W CW 1 + LINE_W WW 1 + LINE_W CW 2 + LINE_W WW 2 + LINE_W CW 3 + LINE_W WW 3 + LINE_W CW 4 + LINE_W WW 4 + LINE_W CW 5 + LINE_W WW 5 + LINE_W CW 6 + LINE_W WW 6 + LINE_W CW 7 + LINE_W WW 7 + LINE_W CW 8 + LINE_W WW 8 + LINE_W CW 9 + LINE_W WW 9 + LINE_W CW 10 + LINE_W WW 10 + LINE_W CW 11 + LINE_W WW 11 + LINE_W CW 12 + LINE_W WW 12 + LINE_W CW 13 + LINE_W WW 13 + LINE_W CW 14 + LINE_W WW 14 + LINE_W CW 15 + LINE_W WW 15 + LINE_W CW 16 + LINE_W WW 16 + LINE_W CW 17 + LINE_W WW 17 + LINE_W CW 18 + LINE_W WW 18 + LINE_W CW 19 + LINE_W WW 19 + LINE_W CW 20 + LINE_W WW 20 + LINE_W CW 21 + LINE_W WW 21 + LINE_W CW 22 + LINE_W WW 22 + LINE_W CW 23 + LINE_W WW 23 + LINE_W CW 24 + LINE_W WW 24 + LINE_RGB Red 1 + LINE_RGB Green 1 + LINE_RGB Blue 1 + LINE_RGB Red 2 + LINE_RGB Green 2 + LINE_RGB Blue 2 + LINE_RGB Red 3 + LINE_RGB Green 3 + LINE_RGB Blue 3 + LINE_RGB Red 4 + LINE_RGB Green 4 + LINE_RGB Blue 4 + LINE_RGB Red 5 + LINE_RGB Green 5 + LINE_RGB Blue 5 + LINE_RGB Red 6 + LINE_RGB Green 6 + LINE_RGB Blue 6 + LINE_RGB Red 7 + LINE_RGB Green 7 + LINE_RGB Blue 7 + LINE_RGB Red 8 + LINE_RGB Green 8 + LINE_RGB Blue 8 + LINE_RGB Red 9 + LINE_RGB Green 9 + LINE_RGB Blue 9 + LINE_RGB Red 10 + LINE_RGB Green 10 + LINE_RGB Blue 10 + LINE_RGB Red 11 + LINE_RGB Green 11 + LINE_RGB Blue 11 + LINE_RGB Red 12 + LINE_RGB Green 12 + LINE_RGB Blue 12 + LINE_RGB Red 13 + LINE_RGB Green 13 + LINE_RGB Blue 13 + LINE_RGB Red 14 + LINE_RGB Green 14 + LINE_RGB Blue 14 + LINE_RGB Red 15 + LINE_RGB Green 15 + LINE_RGB Blue 15 + LINE_RGB Red 16 + LINE_RGB Green 16 + LINE_RGB Blue 16 + LINE_RGB Red 17 + LINE_RGB Green 17 + LINE_RGB Blue 17 + LINE_RGB Red 18 + LINE_RGB Green 18 + LINE_RGB Blue 18 + LINE_RGB Red 19 + LINE_RGB Green 19 + LINE_RGB Blue 19 + LINE_RGB Red 20 + LINE_RGB Green 20 + LINE_RGB Blue 20 + LINE_RGB Red 21 + LINE_RGB Green 21 + LINE_RGB Blue 21 + LINE_RGB Red 22 + LINE_RGB Green 22 + LINE_RGB Blue 22 + LINE_RGB Red 23 + LINE_RGB Green 23 + LINE_RGB Blue 23 + LINE_RGB Red 24 + LINE_RGB Green 24 + LINE_RGB Blue 24 + + 8 + 9 + 10 + 11 + + + 12 + 13 + 14 + 15 + + + 16 + 17 + 18 + 19 + + + 20 + 21 + 22 + 23 + + + 24 + 25 + 26 + 27 + + + 28 + 29 + 30 + 31 + + + 33 + 32 + 34 + 35 + + + 36 + 37 + 38 + 39 + + + 40 + 41 + 42 + 43 + + + 44 + 45 + 46 + 47 + + + 48 + 49 + + + 50 + 51 + + + 52 + 53 + + + 54 + 55 + + + 56 + 57 + + + 58 + 59 + + + 60 + 61 + + + 62 + 63 + + + 64 + 65 + + + 66 + 67 + + + 68 + 69 + + + 70 + 71 + + + 72 + 73 + + + 74 + 75 + + + 76 + 77 + + + 78 + 79 + + + 80 + 81 + + + 82 + 83 + + + 84 + 85 + + + 86 + 87 + + + 88 + 89 + + + 90 + 91 + + + 92 + 93 + + + 94 + 95 + + + 96 + 97 + 98 + + + 99 + 100 + 101 + + + 102 + 103 + 104 + + + 105 + 106 + 107 + + + 108 + 109 + 110 + + + 111 + 112 + 113 + + + 114 + 115 + 116 + + + 117 + 118 + 119 + + + 120 + 121 + 122 + + + 123 + 124 + 125 + + + 126 + 127 + 128 + + + 129 + 130 + 131 + + + 132 + 133 + 134 + + + 135 + 136 + 137 + + + 138 + 139 + 140 + + + 141 + 142 + 143 + + + 144 + 145 + 146 + + + 147 + 148 + 149 + + + 150 + 151 + 152 + + + 153 + 154 + 155 + + + 156 + 157 + 158 + + + 159 + 160 + 161 + + + 162 + 163 + 164 + + + 165 + 166 + 167 + + + + + + + + + + + + Tilt + Tilt fine + Zoom + Dimmer + Main Strobe + LINE_W Strobe + LINE_RGB Strobe + Special Function + + 4 + 2 + + + 5 + + + 6 + + + + Main Red 1 + Main Green 1 + Main Blue 1 + Main White 1 + Main Red 2 + Main Green 2 + Main Blue 2 + Main White 2 + Main Red 3 + Main Green 3 + Main Blue 3 + Main White 3 + Main Red 4 + Main Green 4 + Main Blue 4 + Main White 4 + Main Red 5 + Main Green 5 + Main Blue 5 + Main White 5 + Main Red 6 + Main Green 6 + Main Blue 6 + Main White 6 + Main Red 7 + Main Green 7 + Main Blue 7 + Main White 7 + Main Red 8 + Main Green 8 + Main Blue 8 + Main White 8 + Main Red 9 + Main Green 9 + Main Blue 9 + Main White 9 + Main Red 10 + Main Green 10 + Main Blue 10 + Main White 10 + LINE_W CW 1 + LINE_W WW 1 + LINE_W CW 2 + LINE_W WW 2 + LINE_W CW 3 + LINE_W WW 3 + LINE_W CW 4 + LINE_W WW 4 + LINE_W CW 5 + LINE_W WW 5 + LINE_W CW 6 + LINE_W WW 6 + LINE_W CW 7 + LINE_W WW 7 + LINE_W CW 8 + LINE_W WW 8 + LINE_W CW 9 + LINE_W WW 9 + LINE_W CW 10 + LINE_W WW 10 + LINE_W CW 11 + LINE_W WW 11 + LINE_W CW 12 + LINE_W WW 12 + LINE_RGB Red 1 + LINE_RGB Green 1 + LINE_RGB Blue 1 + LINE_RGB Red 2 + LINE_RGB Green 2 + LINE_RGB Blue 2 + LINE_RGB Red 3 + LINE_RGB Green 3 + LINE_RGB Blue 3 + LINE_RGB Red 4 + LINE_RGB Green 4 + LINE_RGB Blue 4 + LINE_RGB Red 5 + LINE_RGB Green 5 + LINE_RGB Blue 5 + LINE_RGB Red 6 + LINE_RGB Green 6 + LINE_RGB Blue 6 + LINE_RGB Red 7 + LINE_RGB Green 7 + LINE_RGB Blue 7 + LINE_RGB Red 8 + LINE_RGB Green 8 + LINE_RGB Blue 8 + LINE_RGB Red 9 + LINE_RGB Green 9 + LINE_RGB Blue 9 + LINE_RGB Red 10 + LINE_RGB Green 10 + LINE_RGB Blue 10 + LINE_RGB Red 11 + LINE_RGB Green 11 + LINE_RGB Blue 11 + LINE_RGB Red 12 + LINE_RGB Green 12 + LINE_RGB Blue 12 + + 0 + 1 + 2 + 3 + + + 4 + 5 + 6 + 7 + + + 8 + 9 + 10 + 11 + + + 12 + 13 + 14 + 15 + + + 16 + 17 + 18 + 19 + + + 20 + 21 + 22 + 23 + + + 24 + 25 + 26 + 27 + + + 28 + 29 + 30 + 31 + + + 32 + 33 + 34 + 35 + + + 36 + 37 + 38 + 39 + + + 40 + 41 + + + 42 + 43 + + + 44 + 45 + + + 46 + 47 + + + 48 + 49 + + + 50 + 51 + + + 52 + 53 + + + 54 + 55 + + + 56 + 57 + + + 58 + 59 + + + 60 + 61 + + + 62 + 63 + + + 64 + 65 + 66 + + + 68 + 67 + 69 + + + 70 + 71 + 72 + + + 73 + 74 + 75 + + + 76 + 77 + 78 + + + 79 + 80 + 81 + + + 82 + 83 + 84 + + + 85 + 86 + 87 + + + 88 + 89 + 90 + + + 91 + 92 + 93 + + + 94 + 95 + 96 + + + 97 + 98 + 99 + + + + + + + + + + + + Tilt + Tilt fine + Zoom + Dimmer + Main Strobe + LINE_W Strobe + LINE_RGB Strobe + Special Function + + 4 + 2 + + + 5 + + + 6 + + + + + + + + + + + + Main Red 1 + Main Green 1 + Main Blue 1 + Main White 1 + Main Red 2 + Main Green 2 + Main Blue 2 + Main White 2 + Main Red 3 + Main Green 3 + Main Blue 3 + Main White 3 + Main Red 4 + Main Green 4 + Main Blue 4 + Main White 4 + Main Red 5 + Main Green 5 + Main Blue 5 + Main White 5 + Main Red 6 + Main Green 6 + Main Blue 6 + Main White 6 + Main Red 7 + Main Green 7 + Main Blue 7 + Main White 7 + Main Red 8 + Main Green 8 + Main Blue 8 + Main White 8 + Main Red 9 + Main Green 9 + Main Blue 9 + Main White 9 + Main Red 10 + Main Green 10 + Main Blue 10 + Main White 10 + LINE_W CW 1 + LINE_W WW 1 + LINE_W CW 2 + LINE_W WW 2 + LINE_W CW 3 + LINE_W WW 3 + LINE_W CW 4 + LINE_W WW 4 + LINE_W CW 5 + LINE_W WW 5 + LINE_W CW 6 + LINE_W WW 6 + LINE_W CW 7 + LINE_W WW 7 + LINE_W CW 8 + LINE_W WW 8 + LINE_W CW 9 + LINE_W WW 9 + LINE_W CW 10 + LINE_W WW 10 + LINE_W CW 11 + LINE_W WW 11 + LINE_W CW 12 + LINE_W WW 12 + LINE_W CW 13 + LINE_W WW 13 + LINE_W CW 14 + LINE_W WW 14 + LINE_W CW 15 + LINE_W WW 15 + LINE_W CW 16 + LINE_W WW 16 + LINE_W CW 17 + LINE_W WW 17 + LINE_W CW 18 + LINE_W WW 18 + LINE_W CW 19 + LINE_W WW 19 + LINE_W CW 20 + LINE_W WW 20 + LINE_W CW 21 + LINE_W WW 21 + LINE_W CW 22 + LINE_W WW 22 + LINE_W CW 23 + LINE_W WW 23 + LINE_W CW 24 + LINE_W WW 24 + LINE_RGB Red 1 + LINE_RGB Green 1 + LINE_RGB Blue 1 + LINE_RGB Red 2 + LINE_RGB Green 2 + LINE_RGB Blue 2 + LINE_RGB Red 3 + LINE_RGB Green 3 + LINE_RGB Blue 3 + LINE_RGB Red 4 + LINE_RGB Green 4 + LINE_RGB Blue 4 + LINE_RGB Red 5 + LINE_RGB Green 5 + LINE_RGB Blue 5 + LINE_RGB Red 6 + LINE_RGB Green 6 + LINE_RGB Blue 6 + LINE_RGB Red 7 + LINE_RGB Green 7 + LINE_RGB Blue 7 + LINE_RGB Red 8 + LINE_RGB Green 8 + LINE_RGB Blue 8 + LINE_RGB Red 9 + LINE_RGB Green 9 + LINE_RGB Blue 9 + LINE_RGB Red 10 + LINE_RGB Green 10 + LINE_RGB Blue 10 + LINE_RGB Red 11 + LINE_RGB Green 11 + LINE_RGB Blue 11 + LINE_RGB Red 12 + LINE_RGB Green 12 + LINE_RGB Blue 12 + LINE_RGB Red 13 + LINE_RGB Green 13 + LINE_RGB Blue 13 + LINE_RGB Red 14 + LINE_RGB Green 14 + LINE_RGB Blue 14 + LINE_RGB Red 15 + LINE_RGB Green 15 + LINE_RGB Blue 15 + LINE_RGB Red 16 + LINE_RGB Green 16 + LINE_RGB Blue 16 + LINE_RGB Red 17 + LINE_RGB Green 17 + LINE_RGB Blue 17 + LINE_RGB Red 18 + LINE_RGB Green 18 + LINE_RGB Blue 18 + LINE_RGB Red 19 + LINE_RGB Green 19 + LINE_RGB Blue 19 + LINE_RGB Red 20 + LINE_RGB Green 20 + LINE_RGB Blue 20 + LINE_RGB Red 21 + LINE_RGB Green 21 + LINE_RGB Blue 21 + LINE_RGB Red 22 + LINE_RGB Green 22 + LINE_RGB Blue 22 + LINE_RGB Red 23 + LINE_RGB Green 23 + LINE_RGB Blue 23 + LINE_RGB Red 24 + LINE_RGB Green 24 + LINE_RGB Blue 24 + + 8 + 9 + 10 + 11 + + + 12 + 13 + 14 + 15 + + + 16 + 17 + 18 + 19 + + + 20 + 21 + 22 + 23 + + + 24 + 25 + 26 + 27 + + + 28 + 29 + 30 + 31 + + + 33 + 32 + 34 + 35 + + + 36 + 37 + 38 + 39 + + + 40 + 41 + 42 + 43 + + + 44 + 45 + 46 + 47 + + + 48 + 49 + + + 50 + 51 + + + 52 + 53 + + + 54 + 55 + + + 56 + 57 + + + 58 + 59 + + + 60 + 61 + + + 62 + 63 + + + 64 + 65 + + + 66 + 67 + + + 68 + 69 + + + 70 + 71 + + + 72 + 73 + + + 74 + 75 + + + 76 + 77 + + + 78 + 79 + + + 80 + 81 + + + 82 + 83 + + + 84 + 85 + + + 86 + 87 + + + 88 + 89 + + + 90 + 91 + + + 92 + 93 + + + 94 + 95 + + + 96 + 97 + 98 + + + 99 + 100 + 101 + + + 102 + 103 + 104 + + + 105 + 106 + 107 + + + 108 + 109 + 110 + + + 111 + 112 + 113 + + + 114 + 115 + 116 + + + 117 + 118 + 119 + + + 120 + 121 + 122 + + + 123 + 124 + 125 + + + 126 + 127 + 128 + + + 129 + 130 + 131 + + + 132 + 133 + 134 + + + 135 + 136 + 137 + + + 138 + 139 + 140 + + + 141 + 142 + 143 + + + 144 + 145 + 146 + + + 147 + 148 + 149 + + + 150 + 151 + 152 + + + 153 + 154 + 155 + + + 156 + 157 + 158 + + + 159 + 160 + 161 + + + 162 + 163 + 164 + + + 165 + 166 + 167 + + + + Tilt + Tilt fine + Zoom + Dimmer + Main Strobe + LINE_W Strobe + LINE_RGB Strobe + Special Function + Main Red + Main Green + Main Blue + Main White + LINE_W CW + LINE_W WW + LINE_RGB Red + LINE_RGB Green + LINE_RGB Blue + Main Red 11&12 + Main Green 11&12 + Main Blue 11&12 + Main White 11&12 + + 8 + 9 + 10 + 11 + + + 12 + 13 + + + 14 + 15 + 16 + + + 17 + 18 + 19 + 20 + + + + + + + + + + + diff --git a/resources/fixtures/Elation/Elation-Paladin.qxf b/resources/fixtures/Elation/Elation-Paladin.qxf new file mode 100644 index 0000000000..dd6b0e9bdc --- /dev/null +++ b/resources/fixtures/Elation/Elation-Paladin.qxf @@ -0,0 +1,309 @@ + + + + + Q Light Controller Plus + 4.13.0 GIT + Nicholas Harvey + + Elation + Paladin + Color Changer + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Shutter + LED Off + LED On + Strobe Effect Slow to Fast + LED On + Pulse Effect in Sequences + LED On + Random Strobe Effect Slow to Fast + LED On + + + + + + Effect + LEDs On + LEDs Off + Program Macro 1 + Program Macro 2 + Program Macro 3 + Program Macro 4 + Program Macro 5 + Program Macro 6 + Program Macro 7 + Program Macro 8 + Program Macro 9 + Program Macro 10 + Program Macro 11 + Program Macro 12 + Program Macro 13 + Program Macro 14 + Program Macro 15 + + + Speed + Program Macro Speed Slow to Fast + + + Effect + Program Macro Fade Slow to Fast + + + Colour + Off + Color Preset 1 + Color Preset 2 + Color Preset 3 + Color Preset 4 + Color Preset 5 + Color Preset 6 + Color Preset 7 + Color Preset 8 + Color Preset 9 + Color Preset 10 + Color Preset 11 + Color Preset 12 + Color Preset 13 + Color Preset 14 + Color Preset 15 + Color Preset 16 + Color Preset 17 + Color Preset 18 + Color Preset 19 + Color Preset 20 + Color Preset 21 + Color Preset 22 + Color Preset 23 + Color Preset 24 + Color Preset 25 + Color Preset 26 + Color Preset 27 + Color Preset 28 + Color Preset 29 + Color Preset 30 + Color Preset 31 + Color Preset 32 + Color Preset 33 + Color Preset 34 + Color Preset 35 + Color Preset 36 + Color Preset 37 + Color Preset 38 + Color Preset 39 + Color Preset 40 + Color Preset 41 + Color Preset 42 + Color Preset 43 + Color Preset 44 + Color Preset 45 + Color Preset 46 + Color Preset 47 + Color Preset 48 + Color Preset 49 + Color Preset 50 + Color Preset 51 + Color Preset 52 + Color Preset 53 + Color Preset 54 + Color Preset 55 + Color Preset 56 + Color Preset 57 + Color Preset 58 + Color Preset 59 + Color Preset 60 + Color Preset 61 + Color Preset 62 + Color Preset 63 + Color Preset 64 + + + Maintenance + Standard + Stage + TV + Architectural + Theatre + Default to Unit Setting + + + Red (All LEDs) + Green (All LEDs) + Blue (All LEDs) + White (All LEDs) + Shutter / Strobe + Dimmer Intensity + Zoom + Program Macros + Program Macro Speed + Program Macro Fade + Color Presets + Dimming Curve Modes + + + + + + + + + + + Red (Pixel Set #1) + Green (Pixel Set #1) + Blue (Pixel Set #1) + White (Pixel Set #1) + Red (Pixel Set #2) + Green (Pixel Set #2) + Blue (Pixel Set #2) + White (Pixel Set #2) + Red (Pixel Set #3) + Green (Pixel Set #3) + Blue (Pixel Set #3) + White (Pixel Set #3) + Shutter / Strobe + Dimmer Intensity + Dimmer Intensity Fine + Zoom + Program Macros + Program Macro Speed + Program Macro Fade + Color Presets + Dimming Curve Modes + + 0 + 1 + 2 + 3 + + + 4 + 5 + 6 + 7 + + + 8 + 9 + 10 + 11 + + + + + + + + + + + + Red (Pixel Set #1) + Green (Pixel Set #1) + Blue (Pixel Set #1) + White (Pixel Set #1) + Red (Pixel Set #2) + Green (Pixel Set #2) + Blue (Pixel Set #2) + White (Pixel Set #2) + Red (Pixel Set #3) + Green (Pixel Set #3) + Blue (Pixel Set #3) + White (Pixel Set #3) + Red (Pixel Set #4) + Green (Pixel Set #4) + Blue (Pixel Set #4) + White (Pixel Set #4) + Red (Pixel Set #5) + Green (Pixel Set #5) + Blue (Pixel Set #5) + White (Pixel Set #5) + Red (Pixel Set #6) + Green (Pixel Set #6) + Blue (Pixel Set #6) + White (Pixel Set #6) + Shutter / Strobe + Dimmer Intensity + Dimmer Intensity Fine + Zoom + Program Macros + Program Macro Speed + Program Macro Fade + Color Presets + Dimming Curve Modes + + 0 + 1 + 2 + 3 + + + 4 + 5 + 6 + 7 + + + 8 + 9 + 10 + 11 + + + 12 + 13 + 14 + 15 + + + 16 + 17 + 18 + 19 + + + 20 + 21 + 22 + 23 + + + + + + + + + + + diff --git a/resources/fixtures/Elumen8/Elumen8-MP-120.qxf b/resources/fixtures/Elumen8/Elumen8-MP-120.qxf new file mode 100644 index 0000000000..c7c62e3cea --- /dev/null +++ b/resources/fixtures/Elumen8/Elumen8-MP-120.qxf @@ -0,0 +1,27 @@ + + + + + Q Light Controller Plus + 4.13.0 GIT + Keith Baker + + Elumen8 + MP 120 + Color Changer + + + + + Master Dimmer + Fine Dimmer + Strobe + + + + + + + + + diff --git a/resources/fixtures/Elumen8/Elumen8-MP-60-Mk1.qxf b/resources/fixtures/Elumen8/Elumen8-MP-60-Mk1.qxf new file mode 100644 index 0000000000..4b89e13ec1 --- /dev/null +++ b/resources/fixtures/Elumen8/Elumen8-MP-60-Mk1.qxf @@ -0,0 +1,23 @@ + + + + + Q Light Controller Plus + 4.13.0 GIT + Keith Baker + + Elumen8 + MP 60 Mk1 + Color Changer + + + Master dimmer + + + + + + + + + diff --git a/resources/fixtures/Elumen8/Elumen8-MP-60-Mk2.qxf b/resources/fixtures/Elumen8/Elumen8-MP-60-Mk2.qxf new file mode 100644 index 0000000000..5f67d12ec7 --- /dev/null +++ b/resources/fixtures/Elumen8/Elumen8-MP-60-Mk2.qxf @@ -0,0 +1,23 @@ + + + + + Q Light Controller Plus + 4.12.2 + Keith Baker + + Elumen8 + MP 60 Mk2 + Color Changer + + + Master dimmer + + + + + + + + + diff --git a/resources/fixtures/Eurolite/Eurolite-LED-KLS-Scan-Pro-Next-FX-Light.qxf b/resources/fixtures/Eurolite/Eurolite-LED-KLS-Scan-Pro-Next-FX-Light.qxf new file mode 100644 index 0000000000..32ca212c46 --- /dev/null +++ b/resources/fixtures/Eurolite/Eurolite-LED-KLS-Scan-Pro-Next-FX-Light.qxf @@ -0,0 +1,491 @@ + + + + + Q Light Controller Plus + 4.13.0 GIT + Kevin + + Eurolite + LED KLS Scan Pro Next FX Light + Other + + + Effect + No function + Automatic / sound program 1 + Automatic / sound program 2 + Automatic / sound program 3 + Automatic / sound program 4 + Automatic / sound program 5 + Automatic / sound program 6 + Automatic / sound program 7 + Automatic / sound program 8 + Automatic / sound program 9 + Automatic / sound program 10 + Automatic / sound program 11 + Automatic / sound program 12 + Automatic / sound program 13 + Automatic / sound program 14 + Automatic / sound program 15 + Automatic / sound program 16 + Automatic / sound program 17 + Automatic / sound program 18 + Automatic / sound program 19 + Automatic / sound program 20 + + + Effect + Automatic program with color change + Automatic program with color fade + Sound program with color change + Sound program with color fade + + + + + + + Effect + No function + Program 1 (slow to fast) + Program 2 (slow to fast) + Program 3 (slow to fast) + Program 4 (slow to fast) + Program 5 (slow to fast) + + + Effect + No function + Pan-/Tilt Program 1 + Pan-/Tilt Program 2 + Pan-/Tilt Program 3 + Pan-/Tilt Program 4 + Pan-/Tilt Program 5 + Pan-/Tilt Program 6 + Pan-/Tilt Program 7 + Pan-/Tilt Program 8 + + + Shutter + No function + On + Strobe speed (slow to fast) + + + Effect + Stop + Rotation clockwise (slow to fast) + Stop + Rotation counterclockwise (slow to fast) + + + + + + Colour + Open + Violet + Green + Yellow + Cyan + Red + Orange + Blue + Open + Violet + Green + Yellow + Cyan + Red + Orange + Blue + Open + Green + Violet + Yellow + Cyan + Orange + Red + Blue + + + Gobo + Open + Gobo 1 + Gobo 2 + Gobo 3 + Gobo 4 + Gobo 5 + Gobo 6 + Gobo 7 + Gobo open+1 + Gobo 2+3 + Gobo 4+5 + Gobo 6+7 + Gobo open+7 + Gobo 1+6 + Gobo 2+5 + Gobo 3+4 + + + + Shutter + On + Off + Strobe effect (slow to fast) + Pulsating strobe effect (slow to fast) + Random strobe effect (slow to fast) + On + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Shutter + No function + Strobe effect 1 (slow to fast) + Strobe effect 2 (slow to fast) + Strobe effect 3 (slow to fast) + Strobe effect 4 (slow to fast) + Strobe effect 5 (slow to fast) + + + + + + Colour + Open + Violet + Green + Yellow + Cyan + Red + Orange + Blue + Open + Violet + Green + Yellow + Cyan + Red + Orange + Blue + Open + Green + Violet + Yellow + Cyan + Orange + Red + Blue + + + Gobo + Open + Gobo 1 + Gobo 2 + Gobo 3 + Gobo 4 + Gobo 5 + Gobo 6 + Gobo 7 + Gobo open+1 + Gobo 2+3 + Gobo 4+5 + Gobo 6+7 + Gobo open+7 + Gobo 1+6 + Gobo 2+5 + Gobo 3+4 + + + + Shutter + On + Off + Strobe effect (slow to fast) + Pulsating strobe effect (slow to fast) + Random strobe effect (slow to fast) + On + + + + + + Colour + Open + Violet + Green + Yellow + Cyan + Red + Orange + Blue + Open + Violet + Green + Yellow + Cyan + Red + Orange + Blue + Open + Green + Violet + Yellow + Cyan + Orange + Red + Blue + + + Gobo + Open + Gobo 1 + Gobo 2 + Gobo 3 + Gobo 4 + Gobo 5 + Gobo 6 + Gobo 7 + Gobo open+1 + Gobo 2+3 + Gobo 4+5 + Gobo 6+7 + Gobo open+7 + Gobo 1+6 + Gobo 2+5 + Gobo 3+4 + + + + Shutter + On + Off + Strobe effect (slow to fast) + Pulsating strobe effect (slow to fast) + Random strobe effect (slow to fast) + On + + + Dimmer Spots + Derby + Integrated show programs + Automatic Mode / Sound Control + + + Spots Red + Spots Green + Spots Blue + Spots White + Derby + Scanner + Strobe + + + Spots Red + Spots Green + Spots Blue + Spots White + Derby motor + Scanner Pan + Scanner Tilt + Scanner Pan/Tilt Speed + Scanner Color wheel + Scanner Gobos + Scanner Dimmer + Scanner strobe effect + UV Dimmer + + + Spots 1+4 Red + Spots 1+4 Green + Spots 1+4 Blue + Spots 1+4 White + Spots 2+3 Red + Spots 2+3 Green + Spots 2+3 Blue + Spots 2+3 White + Derby Red + Derby Green + Derby Blue + Derby motor + Scanner + UV Dimmer + + + Spots Red + Spots Green + Spots Blue + Spots White + Derby motor + Scanner Pan + Scanner Tilt + Scanner Pan/Tilt Speed + Scanner Color wheel + Scanner Gobos + Scanner Dimmer + Scanner strobe effect + UV Dimmer + Integrated show programs + Automatic Mode / Sound Control + + + Spots 1+4 Red + Spots 1+4 Green + Spots 1+4 Blue + Spots 1+4 White + Spots 2+3 Red + Spots 2+3 Green + Spots 2+3 Blue + Spots 2+3 White + Derby Red + Derby Green + Derby Blue + Derby motor + Scanner + Scanner Color wheel + Scanner Gobos + Master dimmer + Scanner strobe effect + UV Dimmer + + + Spot 1 Red + Spot 1 Green + Spot 1 Blue + Spot 1 White + Spot 2 Red + Spot 2 Green + Spot 2 Blue + Spot 2 White + Spot 3 Red + Spot 3 Green + Spot 3 Blue + Spot 3 White + Spot 4 Red + Spot 4 Green + Spot 4 Blue + Spot 4 White + Derby + Scanner + UV Dimmer + + + Spot 1 Red + Spot 1 Green + Spot 1 Blue + Spot 1 White + Spot 2 Red + Spot 2 Green + Spot 2 Blue + Spot 2 White + Spot 3 Red + Spot 3 Green + Spot 3 Blue + Spot 3 White + Spot 4 Red + Spot 4 Green + Spot 4 Blue + Spot 4 White + Derby Red + Derby Green + Derby Blue + Derby motor + Master dimmer + Strobe effect + Scanner Pan + Scanner Tilt + Scanner Pan/Tilt Speed + Scanner Color wheel + Scanner Gobos + Scanner Dimmer + Scanner strobe effect + UV Dimmer + + + Spot 1 Red + Spot 1 Green + Spot 1 Blue + Spot 1 White + Spot 2 Red + Spot 2 Green + Spot 2 Blue + Spot 2 White + Spot 3 Red + Spot 3 Green + Spot 3 Blue + Spot 3 White + Spot 4 Red + Spot 4 Green + Spot 4 Blue + Spot 4 White + Derby Red + Derby Green + Derby Blue + Derby motor + Master dimmer + Strobe effect + Scanner Pan + Scanner Tilt + Scanner Pan/Tilt Speed + Scanner Color wheel + Scanner Gobos + Scanner Dimmer + Scanner strobe effect + UV Dimmer + Integrated show programs + Automatic Mode / Sound Control + + + Spot 1 Red + Spot 1 Green + Spot 1 Blue + Spot 1 White + Spot 2 Red + Spot 2 Green + Spot 2 Blue + Spot 2 White + Spot 3 Red + Spot 3 Green + Spot 3 Blue + Spot 3 White + Spot 4 Red + Spot 4 Green + Spot 4 Blue + Spot 4 White + Derby Red + Derby Green + Derby Blue + Derby motor + Master dimmer + Strobe effect + Scanner 1 Pan + Scanner 1 Tilt + Scanner 1 Pan/Tilt Speed + Scanner 1 Color wheel + Scanner 1 Gobos + Scanner 1 Dimmer + Scanner 1 strobe effect + Scanner 2 Pan + Scanner 2 Tilt + Scanner 2 Pan/Tilt Speed + Scanner 2 Color wheel + Scanner 2 Gobos + Scanner 2 Dimmer + Scanner 2 strobe effect + UV Dimmer + Integrated show programs + Automatic Mode / Sound Control + + + + + + + + + + diff --git a/resources/fixtures/FixturesMap.xml b/resources/fixtures/FixturesMap.xml index c371251876..f7374d1fb2 100644 --- a/resources/fixtures/FixturesMap.xml +++ b/resources/fixtures/FixturesMap.xml @@ -5,6 +5,12 @@ + + + + + + @@ -665,6 +671,7 @@ + @@ -691,6 +698,9 @@ + + + @@ -757,6 +767,7 @@ + From a760d2748000be6542d1504b0021e9afff5f516e Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Sun, 10 Mar 2024 11:39:59 +0100 Subject: [PATCH 090/212] fixtureeditor: fix warnings and outbound sub-window --- fixtureeditor/app.cpp | 12 +++++++----- fixtureeditor/main.cpp | 27 ++++++++++++++------------- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/fixtureeditor/app.cpp b/fixtureeditor/app.cpp index b34ccab0bf..8e79211004 100644 --- a/fixtureeditor/app.cpp +++ b/fixtureeditor/app.cpp @@ -106,7 +106,7 @@ App::~App() QString App::longName() { - return QString("%1 - %2").arg(APPNAME).arg(FXEDNAME); + return QString("%1 - %2").arg(APPNAME, FXEDNAME); } QString App::version() @@ -120,9 +120,9 @@ void App::loadFixtureDefinition(const QString& path) /* Attempt to create a fixture definition from the selected file */ QString error(tr("Unrecognized file extension: %1").arg(path)); - if (path.toLower().endsWith(KExtFixture) == true) + if (path.endsWith(KExtFixture, Qt::CaseInsensitive) == true) fixtureDef = loadQXF(path, error); - else if (path.toLower().endsWith(KExtAvolitesFixture) == true) + else if (path.endsWith(KExtAvolitesFixture, Qt::CaseInsensitive) == true) fixtureDef = loadD4(path, error); else fixtureDef = NULL; @@ -138,6 +138,10 @@ void App::loadFixtureDefinition(const QString& path) sub->setAttribute(Qt::WA_DeleteOnClose); qobject_cast (centralWidget())->addSubWindow(sub); + // check if sub-window is outside main area + if (sub->x() >= this->width() || sub->y() >= this->height()) + sub->setGeometry(0, 0, sub->width(), sub->height()); + editor->show(); sub->show(); } @@ -367,9 +371,7 @@ void App::slotFileOpen() QVariant var = settings.value(SETTINGS_OPENDIALOGSTATE); if (var.isValid() == true) - { dialog.restoreState(var.toByteArray()); - } dialog.setDirectory(m_workingDirectory); diff --git a/fixtureeditor/main.cpp b/fixtureeditor/main.cpp index 38740f0026..96393292d6 100644 --- a/fixtureeditor/main.cpp +++ b/fixtureeditor/main.cpp @@ -90,33 +90,34 @@ void printUsage() * * @return true to continue with application launch; otherwise false */ -bool parseArgs(int argc, char **argv) +bool parseArgs() { - for (int i = 1; i < argc; i++) + QStringListIterator it(QCoreApplication::arguments()); + while (it.hasNext() == true) { - if (::strcmp(argv[i], "-v") == 0 || - ::strcmp(argv[i], "--version") == 0) + QString arg(it.next()); + + if (arg == "-v" || arg == "--version") { /* Don't print anything, since version is always printed before anything else. Just make the app exit by returning false. */ return false; } - else if (::strcmp(argv[i], "-h") == 0 || - ::strcmp(argv[i], "--help") == 0) + else if (arg == "-h" || arg == "--help") { printUsage(); return false; } - else if (::strcmp(argv[i], "-o") == 0 || - ::strcmp(argv[i], "--open") == 0) + else if (arg == "-o" || arg == "--open") { - FXEDArgs::fixture = QString(argv[++i]); + if (it.hasNext() == true) + FXEDArgs::fixture = it.next(); } - else if (::strcmp(argv[i], "-l") == 0 || - ::strcmp(argv[i], "--locale") == 0) + else if (arg == "-l" || arg == "--locale") { - FXEDArgs::locale = QString(argv[++i]); + if (it.hasNext() == true) + FXEDArgs::locale = it.next(); } } @@ -168,7 +169,7 @@ int main(int argc, char** argv) printVersion(); /* Parse command-line arguments */ - if (parseArgs(argc, argv) == false) + if (parseArgs() == false) return 0; /* Load translation for current locale */ From 83e07c9276ae1fc1daafa9aad0dc6198858e1143 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Sun, 10 Mar 2024 13:16:51 +0100 Subject: [PATCH 091/212] resources: 7 new fixtures (see changelog) --- debian/changelog | 5 +- .../Chauvet/Chauvet-COLORdash-Par-Quad-18.qxf | 100 +++++ .../Chauvet-Intimidator-Spot-Duo-155.qxf | 363 ++++++++++++++++++ .../fixtures/Chauvet/Chauvet-Wash-FX-Hex.qxf | 354 +++++++++++++++++ resources/fixtures/EK/EK-R3-Wash.qxf | 132 +++---- .../Event-Lighting-StrobeX-RGB.qxf | 282 ++++++++++++++ .../Event_Lighting/Event-Lighting-StrobeX.qxf | 221 +++++++++++ resources/fixtures/FixturesMap.xml | 7 + .../Laserworld/Laserworld-EL-900RGB.qxf | 110 ++++++ .../Shehds/Shehds-Wash-Zoom-36x18W.qxf | 98 +++++ 10 files changed, 1605 insertions(+), 67 deletions(-) create mode 100644 resources/fixtures/Chauvet/Chauvet-COLORdash-Par-Quad-18.qxf create mode 100644 resources/fixtures/Chauvet/Chauvet-Intimidator-Spot-Duo-155.qxf create mode 100644 resources/fixtures/Chauvet/Chauvet-Wash-FX-Hex.qxf create mode 100644 resources/fixtures/Event_Lighting/Event-Lighting-StrobeX-RGB.qxf create mode 100644 resources/fixtures/Event_Lighting/Event-Lighting-StrobeX.qxf create mode 100644 resources/fixtures/Laserworld/Laserworld-EL-900RGB.qxf create mode 100644 resources/fixtures/Shehds/Shehds-Wash-Zoom-36x18W.qxf diff --git a/debian/changelog b/debian/changelog index 90e7a42acb..543adac309 100644 --- a/debian/changelog +++ b/debian/changelog @@ -77,7 +77,10 @@ qlcplus (4.13.0) stable; urgency=low * 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 (thank to Michael Tosatto) + * 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) -- Massimo Callegari Sun, 10 Mar 2024 12:13:14 +0200 diff --git a/resources/fixtures/Chauvet/Chauvet-COLORdash-Par-Quad-18.qxf b/resources/fixtures/Chauvet/Chauvet-COLORdash-Par-Quad-18.qxf new file mode 100644 index 0000000000..e3597cc322 --- /dev/null +++ b/resources/fixtures/Chauvet/Chauvet-COLORdash-Par-Quad-18.qxf @@ -0,0 +1,100 @@ + + + + + Q Light Controller Plus + 4.13.0 GIT + Michael Tosatto + + Chauvet + COLORdash Par-Quad 18 + Color Changer + + + + + + + Shutter + No Function + Strobe (Slow to Fast) + + + Effect + No Function + R: 100% G: 0-100% B: 0 + R: 100%-0 G:100% B: 0 + R: 0 G: 100% B: 0-100% + R: 0 G: 100%-0 B: 100% + R: 0-100% G: 0 B: 100% + R: 100% G: 0 B: 100%-0 + R: 100% G: 0-100% B: 0-100% + R: 100%-0 G: 100%-0 B: 100% + R: 100% G: 100% B: 100% A: 100% + White 1 + White 2 + White 3 + White 4 + White 5 + White 6 + White 7 + White 8 + White 9 + White 10 + White 11 + + + Effect + No Function + Auto 1 + Auto 2 + Auto 3 + Auto 4 + Auto 5 + + + Speed + Slow to Fast + + + Speed + Preset Dimmer + Linear Dimmer + Nonlinear Dimming Curve 1 (Fastest) + Nonlinear Dimming Curve 2 + Nonlinear Dimming Curve 3 (Slowest) + + + Red + Green + Blue + Amber + + + Dimmer + Red + Green + Blue + Amber + Strobe + + + Dimmer + Red + Green + Blue + Amber + Strobe + Color Macro + White Balance + Auto Program + Auto Speed + Dimmer Speed + + + + + + + + + diff --git a/resources/fixtures/Chauvet/Chauvet-Intimidator-Spot-Duo-155.qxf b/resources/fixtures/Chauvet/Chauvet-Intimidator-Spot-Duo-155.qxf new file mode 100644 index 0000000000..3adc162a78 --- /dev/null +++ b/resources/fixtures/Chauvet/Chauvet-Intimidator-Spot-Duo-155.qxf @@ -0,0 +1,363 @@ + + + + + Q Light Controller Plus + 4.13.0 GIT + Michael Tosatto + + Chauvet + Intimidator Spot Duo 155 + Moving Head + + + + + + + Colour + White + Yellow + Magenta + Green + Red + Cyan + Orange + Blue + Light green + Amber + White + yellow + Yellow + magenta + Magenta + green + Green + red + Red + cyan + Cyan + orange + Orange + blue + Blue + light green + Light green + amber + Amber + white + Color cycling rainbow with increasing speed + Reverse color cycling rainbow with increasing speed + + + Gobo + Open + Gobo 1 + Gobo 2 + Gobo 3 + Gobo 4 + Gobo 5 + Gobo 6 + Gobo 7 + Gobo 8 + Gobo 9 + Gobo 9 shake, from slow to fast + Gobo 8 shake, from slow to fast + Gobo 7 shake, from slow to fast + Gobo 6 shake, from slow to fast + Gobo 5 shake, from slow to fast + Gobo 4 shake, from slow to fast + Gobo 3 shake, from slow to fast + Gobo 2 shake, from slow to fast + Gobo 1 shake, from slow to fast + Open + Cycle effect with increasing speed + Reverse cycle effect with increasing speed + + + Shutter + Closed + Open + Strobe effect 0-17 Hz + Pulse strobe (Slow to fast) + Random strobe (Slow to fast) + Open + + + + Maintenance + No function + Pan/Tilt blackout + Color blackout + Gobo blackout + Pan/Tilt/Color blackout + Pan/Tilt/Gobo blackout + Color/Gobo blackout + Pan/Tilt/Color/Gobo blackout + No function + First two heads setting + Latter two heads setting + Cancel four heads + No function + Reset + No function + Reverse pan/tilt for both heads + Reverse pan for head 1 + Reverse pan for head 2 + Reverse tilt for head 1 + Reverse tilt for head 2 + Cancel reverse pan for head 1 + Cancel reverse pan for head 2 + Cancel reverse tilt for head 1 + Cancel reverse tilt for head 2 + Cancel reverse pan/tilt for both heads + No function + Strone pattern 1 (Slow to fast) + Strobe pattern 2 (Slow to fast) + Strobe pattern 3 (Slow to fast) + + + Maintenance + No function + Movement macro 1 + Movement macro 2 + Movement macro 3 + Movement macro 4 + Movement macro 5 + Movement macro 6 + Movement macro 7 + Movement macro 8 + Movement macro 9 + Movement macro 10 + Movement macro 11 + Movement macro 12 + Movement macro 13 + Movement macro 14 + Movement macro 15 + All movement macros 1-15 + Sound-Active Mode + + + + + + + + + + + Colour + White + Yellow + Magenta + Green + Red + Cyan + Orange + Blue + Light green + Amber + White + yellow + Yellow + magenta + Magenta + green + Green + red + Red + cyan + Cyan + orange + Orange + blue + Blue + light green + Light green + amber + Amber + white + Color cycling rainbow with increasing speed + Reverse color cycling rainbow with increasing speed + + + Colour + White + Yellow + Magenta + Green + Red + Cyan + Orange + Blue + Light green + Amber + White + yellow + Yellow + magenta + Magenta + green + Green + red + Red + cyan + Cyan + orange + Orange + blue + Blue + light green + Light green + amber + Amber + white + Color cycling rainbow with increasing speed + Reverse color cycling rainbow with increasing speed + + + Gobo + Open + Gobo 1 + Gobo 2 + Gobo 3 + Gobo 4 + Gobo 5 + Gobo 6 + Gobo 7 + Gobo 8 + Gobo 9 + Gobo 9 shake, from slow to fast + Gobo 8 shake, from slow to fast + Gobo 7 shake, from slow to fast + Gobo 6 shake, from slow to fast + Gobo 5 shake, from slow to fast + Gobo 4 shake, from slow to fast + Gobo 3 shake, from slow to fast + Gobo 2 shake, from slow to fast + Gobo 1 shake, from slow to fast + Open + Cycle effect with increasing speed + Reverse cycle effect with increasing speed + + + Gobo + Open + Gobo 1 + Gobo 2 + Gobo 3 + Gobo 4 + Gobo 5 + Gobo 6 + Gobo 7 + Gobo 8 + Gobo 9 + Gobo 9 shake, from slow to fast + Gobo 8 shake, from slow to fast + Gobo 7 shake, from slow to fast + Gobo 6 shake, from slow to fast + Gobo 5 shake, from slow to fast + Gobo 4 shake, from slow to fast + Gobo 3 shake, from slow to fast + Gobo 2 shake, from slow to fast + Gobo 1 shake, from slow to fast + Open + Cycle effect with increasing speed + Reverse cycle effect with increasing speed + + + + + Speed + Movement Macro Speed (Fast to slow) + + + Shutter + Closed + Open + Strobe effect 0-17 Hz + Pulse strobe (Slow to fast) + Random strobe (Slow to fast) + Open + + + Shutter + Closed + Open + Strobe effect 0-17 Hz + Pulse strobe (Slow to fast) + Random strobe (Slow to fast) + Open + + + Pan 1 + Fine Pan 1 + Tilt 1 + Fine Tilt 1 + Pan 2 + Fine Pan 2 + Tilt 2 + Fine Tilt 2 + Speed + Color Wheel 1 + Color Wheel 2 + Gobo Wheel 1 + Gobo Wheel 2 + Dimmer 1 + Dimmer 2 + Shutter 1 + Shutter 2 + Control Functions + Movement Macros + Movement Macro Speed + + 0 + 1 + 2 + 3 + 9 + 11 + 13 + 15 + + + 4 + 5 + 6 + 7 + 10 + 12 + 14 + 16 + + + + Pan 1 + Fine Pan 1 + Tilt 1 + Fine Tilt 1 + Pan 2 + Fine Pan 2 + Tilt 2 + Fine Tilt 2 + Speed + Color Wheel + Gobo Wheel + Dimmer 1 + Dimmer 2 + Shutter + Control Functions + Movement Macros + Movement Macro Speed + + 0 + 1 + 2 + 3 + 11 + + + 4 + 5 + 6 + 7 + 12 + + + + Pan + Tilt + Color Wheel + Gobo Wheel + Dimmer 1 + Dimmer 2 + Shutter + Control Functions + Movement Macros + Movement Macro Speed + + 4 + + + 5 + + + + + + + + + + + diff --git a/resources/fixtures/Chauvet/Chauvet-Wash-FX-Hex.qxf b/resources/fixtures/Chauvet/Chauvet-Wash-FX-Hex.qxf new file mode 100644 index 0000000000..58b468e814 --- /dev/null +++ b/resources/fixtures/Chauvet/Chauvet-Wash-FX-Hex.qxf @@ -0,0 +1,354 @@ + + + + + Q Light Controller Plus + 4.13.0 GIT + Clément Delabroye + + Chauvet + Wash FX Hex + Flower + + Effect + No function + Auto program 1 + Auto program 2 + Auto program 3 + Auto program 4 + Auto program 5 + Auto program 6 + Auto program 7 + Auto program 8 + Auto program 9 + Auto program 10 + Auto program 11 + Auto program 12 + Auto program 13 + Auto program 14 + Auto program 15 + Auto program 16 + + + Speed + Speed, slow to fast + Sound-active Mode + + + + + + + + + + Effect + No function + Zone macro + + + + Intensity + Red + 0 -100 % + + + Intensity + Red + 0 -100 % + + + Intensity + Red + 0 -100 % + + + Intensity + Red + 0 -100 % + + + Intensity + Red + 0 -100 % + + + Intensity + Red + 0 -100 % + + + Intensity + Green + 0 -100 % + + + Intensity + Green + 0 -100 % + + + Intensity + Green + 0 -100 % + + + Intensity + Green + 0 -100 % + + + Intensity + Green + 0 -100 % + + + Intensity + Green + 0 -100 % + + + Intensity + Blue + 0 -100 % + + + Intensity + Blue + 0 -100 % + + + Intensity + Blue + 0 -100 % + + + Intensity + Blue + 0 -100 % + + + Intensity + Blue + 0 -100 % + + + Intensity + Blue + 0 -100 % + + + Intensity + Amber + 0 -100 % + + + Intensity + Amber + 0 -100 % + + + Intensity + Amber + 0 -100 % + + + Intensity + Amber + 0 -100 % + + + Intensity + Amber + 0 -100 % + + + Intensity + Amber + 0 -100 % + + + Intensity + White + 0 -100 % + + + Intensity + White + 0 -100 % + + + Intensity + White + 0 -100 % + + + Intensity + White + 0 -100 % + + + Intensity + White + 0 -100 % + + + Intensity + White + 0 -100 % + + + Intensity + UV + 0 -100 % + + + Intensity + UV + 0 -100 % + + + Intensity + UV + 0 -100 % + + + Intensity + UV + 0 -100 % + + + Intensity + UV + 0 -100 % + + + Intensity + UV + 0 -100 % + + + Auto programs + Auto program speed + Dimmer + + + Red + Green + Blue + Amber + White + UV + + + Red + Green + Blue + Amber + White + UV + Strobe + Auto programs + Zone macro + Auto program speed + Dimmer + + + Auto programs + Auto program speed + Dimmer + Strobe + Red zone 1 + Green zone 1 + Blue zone 1 + Amber zone 1 + White zone 1 + UV zone 1 + Red zone 2 + Green zone 2 + Blue zone 2 + Amber zone 2 + White zone 2 + UV zone 2 + Red zone 3 + Green zone 3 + Blue zone 3 + Amber zone 3 + White zone 3 + UV zone 3 + Red zone 4 + Green zone 4 + Blue zone 4 + Amber zone 4 + White zone 4 + UV zone 4 + Red zone 5 + Green zone 5 + Blue zone 5 + Amber zone 5 + White zone 5 + UV zone 5 + Red zone 6 + Green zone 6 + Blue zone 6 + Amber zone 6 + White zone 6 + UV zone 6 + + 4 + 5 + 6 + 7 + 8 + 9 + + + 10 + 11 + 12 + 13 + 14 + 15 + + + 16 + 17 + 18 + 19 + 20 + 21 + + + 22 + 23 + 24 + 25 + 26 + 27 + + + 28 + 29 + 30 + 31 + 32 + 33 + + + 34 + 35 + 36 + 37 + 38 + 39 + + + + + + + + + + + diff --git a/resources/fixtures/EK/EK-R3-Wash.qxf b/resources/fixtures/EK/EK-R3-Wash.qxf index e14dc9c6d3..c01c048219 100644 --- a/resources/fixtures/EK/EK-R3-Wash.qxf +++ b/resources/fixtures/EK/EK-R3-Wash.qxf @@ -30,72 +30,72 @@ Colour - OFF - Red - Green - Blue - Cyan - Yellow - Magenta - White 7000K - White 3700K - White 5000K - black - Medium Yellow - Straw Ting - Surprise Peach - Fire - Medium Amber - Gold Amber - Dark Amber - Sunrise red - Light Pink - Medium Ping - Pink Carnation - Light Lavender - Lavender - Sky Blue - Just Blue - Dark Yellow Green - Sping Yellow - Light Amber - Stra - Deep Amber - Orange - Light Rose - English rose - Light Salmon - Middle Rose - Dark Pink - Magenta - Peacock Blue - Med Blu Green - Steel Blue - Light Blue - Dark Blue - Leaf Green - Dark Green - Mauve - Bright Pink - Medium Blue - Deep golden Amber - Pale Lavender - Sepecial Lavender - Primary Green - Bright Blue - Apricot - Pale Gold - Deep orange - Bastard Amber - Flame Red - Daylight Blue - Lilac Tint - Deep Lavender - Dark Steel Blue - Congo Blue - Alice Blue - Dirty White - White + Macro Color OFF + Red + Green + Blue + Cyan + Yellow + Magenta + White 7000 K + White 3700 K + White 5000 K + Black + Medium Yellow + Straw Tint + Surprise Peach + Fire + Medium Amber + Gold Amber + Dark Amber + Sunrise Red + Light Pink + Medium Pink + Pink Carnation + Light Lavender + Lavender + Sky Blue + Just Blue + Dark Yellow Green + Spring Yellow + Light Amber + Straw + Deep Amber + Orange + Light Rose + English Rose + Light Salmon + Middle Rose + Dark Pink + Magenta2 + Peacock Blue + Med Blue Green + Steel Blue + Light Blue + Dark Blue + Leaf Green + Dark Green + Mauve + Bright Pink + Medium Blue + Deep Golden Amber + Pale Lavender + Special Lavender + Primary Green + Bright Blue + Apricot + Pale Gold + Deep Orange + Bastard Amber + Flame Red + Daylight Blue + Lilac Tint + Deep Lavender + Dark Steel Blue + Congo Blue + Alice Blue + Dirty White + White Shutter diff --git a/resources/fixtures/Event_Lighting/Event-Lighting-StrobeX-RGB.qxf b/resources/fixtures/Event_Lighting/Event-Lighting-StrobeX-RGB.qxf new file mode 100644 index 0000000000..f5a1fa5d02 --- /dev/null +++ b/resources/fixtures/Event_Lighting/Event-Lighting-StrobeX-RGB.qxf @@ -0,0 +1,282 @@ + + + + + Q Light Controller Plus + 4.13.0 GIT + Michael Tosatto + + Event Lighting + StrobeX RGB + Strobe + + Intensity + No Function + Master Dimmer (0 - 100%) + + + Shutter + No Function + Strobe speed (Slow to fast) + + + Shutter + Strobe On Duration Short to Long + + + + + + Shutter + Random Strobe (Slow to fast) + + + Effect + Special Effect + + + Speed + Speed of Special Effect + + + Shutter + Open + Strobe (Slow to Fast) + + + Shutter + Open + Strobe (Slow to Fast) + + + Shutter + Open + Strobe (Slow to Fast) + + + Colour + No function + Red + Red + Green + Green + Green + Blue + Blue + Red + Blue + Red + Green + Blue + + + + + + + + + + + + + + + Shutter + Open + Strobe (Slow to Fast) + + + Shutter + Open + Strobe (Slow to Fast) + + + Shutter + Open + Strobe (Slow to Fast) + + + Shutter + Open + Strobe (Slow to Fast) + + + Shutter + Open + Strobe (Slow to Fast) + + + Shutter + Open + Strobe (Slow to Fast) + + + Shutter + Open + Strobe (Slow to Fast) + + + Shutter + Open + Strobe (Slow to Fast) + + + Shutter + Open + Strobe (Slow to Fast) + + + Shutter + Open + Strobe (Slow to Fast) + + + Shutter + Open + Strobe (Slow to Fast) + + + Shutter + Open + Strobe (Slow to Fast) + + + Red + Green + Blue + + + Master Dimmer + Strobe On Duration + Strobe Rate + Random Strobe + Special Effect + Speed of Special Effect + + + Master Dimmer + Red + Green + Blue + Strobe On Duration + Strobe Rate + Random Strobe + Special Effect + Speed of Special Effect + + + Master Dimmer + Red + Strobe Rate for Red + Green + Strobe Rate for Green + Blue + Strobe Rate for Blue + Color Macro + Special Effect + Speed of Special Effect + + + Master Dimmer + Block 1 Red + Block 1 Green + Block 1 Blue + Block 2 Red + Block 2 Green + Block 2 Blue + Block 3 Red + Block 3 Green + Block 3 Blue + Block 4 Red + Block 4 Green + Block 4 Blue + Strobe On Duration + Strobe Rate + Random Strobe + Special Effect + Speed of Special Effect + + 1 + 2 + 3 + + + 4 + 5 + 6 + + + 7 + 8 + 9 + + + 10 + 11 + 12 + + + + Master Dimmer + Block 1 Red + Strobe Rate for Red 1 + Block 1 Green + Strobe Rate for Green 1 + Block 1 Blue + Strobe Rate for Blue 1 + Block 2 Red + Strobe Rate for Red 2 + Block 2 Green + Strobe Rate for Green 2 + Block 2 Blue + Strobe Rate for Blue 2 + Block 3 Red + Strobe Rate for Red 3 + Block 3 Green + Strobe Rate for Green 3 + Block 3 Blue + Strobe Rate for Blue 3 + Block 4 Red + Strobe Rate for Red 4 + Block 4 Green + Strobe Rate for Green 4 + Block 4 Blue + Strobe Rate for Blue 4 + + 1 + 2 + 3 + 4 + 5 + 6 + + + 7 + 8 + 9 + 10 + 11 + 12 + + + 13 + 14 + 15 + 16 + 17 + 18 + + + 19 + 20 + 21 + 22 + 23 + 24 + + + + + + + + + + + diff --git a/resources/fixtures/Event_Lighting/Event-Lighting-StrobeX.qxf b/resources/fixtures/Event_Lighting/Event-Lighting-StrobeX.qxf new file mode 100644 index 0000000000..85d301120b --- /dev/null +++ b/resources/fixtures/Event_Lighting/Event-Lighting-StrobeX.qxf @@ -0,0 +1,221 @@ + + + + + Q Light Controller Plus + 4.13.0 GIT + Michael Tosatto + + Event Lighting + StrobeX + Strobe + + Intensity + No Function + Master Dimmer (0 - 100%) + + + Intensity + No Function + Intensity (0 - 100%) + + + Speed + No Function + Strobe speed (Slow to fast) + + + Shutter + No Function + Strobe speed (Slow to fast) + + + Shutter + No Function + Strobe speed (Slow to fast) + + + Shutter + No Function + Strobe speed (Slow to fast) + + + Shutter + No Function + Strobe speed (Slow to fast) + + + Intensity + No Function + Intensity (0 - 100%) + + + Intensity + No Function + Intensity (0 - 100%) + + + Intensity + No Function + Intensity (0 - 100%) + + + Intensity + No Function + Intensity (0 - 100%) + + + Shutter + Strobe On Duration Short to Long + + + Shutter + No Function + Strobe effect 1 + Strobe effect 2 + Strobe effect 3 + + + Effect + No Function + Block 1 only + Block 2 only + Block 3 only + Block 4 only + Block 1, 2, 4 only + Block 1, 3 only + Block 3, 4 only + Auto chase 1 + Auto chase 2 + Auto chase 3 + Auto combination + + + Speed + Auto Run Speed (Slow to fast) + + + Effect + No Function + Auto chase 1 + Auto chase 2 + Auto chase 3 + Auto combination + + + + + + + + + + Strobe Speed + + + + + + + + + + Master Dimmer + Strobe Speed + + + + + + + + + + Master Dimmer + Intensity + Strobe Speed + + + Block 1 Strobe + Block 2 Strobe + Block 3 Strobe + Block 4 Strobe + + 0 + + + 1 + + + 2 + + + 3 + + + + Block 1 Dimmer + Block 2 Dimmer + Block 3 Dimmer + Block 4 Dimmer + + 0 + + + 1 + + + 2 + + + 3 + + + + + + + + + + + Master Dimmer + Strobe On Duration + Strobe Speed + Strobe Effect + Run Mode + Auto Run Speed + + + Master Dimmer + Block 1 Dimmer + Block 2 Dimmer + Block 3 Dimmer + Block 4 Dimmer + Strobe On Duration + Strobe Speed + Strobe Effect + Auto Mode + Auto Run Speed + + 1 + + + 2 + + + 3 + + + 4 + + + + + + + + + + + diff --git a/resources/fixtures/FixturesMap.xml b/resources/fixtures/FixturesMap.xml index f7374d1fb2..3978a7ff43 100644 --- a/resources/fixtures/FixturesMap.xml +++ b/resources/fixtures/FixturesMap.xml @@ -415,6 +415,7 @@ + @@ -469,6 +470,7 @@ + @@ -552,6 +554,7 @@ + @@ -855,6 +858,8 @@ + + @@ -1091,6 +1096,7 @@ + @@ -1481,6 +1487,7 @@ + diff --git a/resources/fixtures/Laserworld/Laserworld-EL-900RGB.qxf b/resources/fixtures/Laserworld/Laserworld-EL-900RGB.qxf new file mode 100644 index 0000000000..2c47bf8e7b --- /dev/null +++ b/resources/fixtures/Laserworld/Laserworld-EL-900RGB.qxf @@ -0,0 +1,110 @@ + + + + + Q Light Controller Plus + 4.13.0 GIT + Michael Tosatto + + Laserworld + EL-900RGB + Laser + + Effect + Laser off + Sound mode + Automatic mode + Static patterns of DMX mode + Dynamic patterns of DMX mode + + + Gobo + Circle Fast + Circle Slow + Scan Horizontal Fast + Scan Horizontal Slow + Scan Vertical Fast + Scan Vertical Slow + Fan Diagonal In Fast + Fan Diagonal In Slow + Fan Diagonal Out Fast + Fan Diagonal Out Slow + A Fast (HIGH) + A Slow (HIGH) + V Fast (LOW) + V Slow (LOW) + Triangle Up Fast (HIGH) + Triangle Up Slow (HIGH) + Triangle Down Fast (LOW) + Triangle Down Slow (LOW) + Square Fast + Square Slow + Square Tall Fast + Square Tall Slow + Square Wide Fast + Square Wide Slow + + + x + Fan Horizontal Fast + Fan Horizonttal Slow + Fan Shake Horizontal Fast + Fan Shake Horizontal Slow + Fan Shake Vertical Fast 1 + Fan Shake Vertical Fast 2 + Fan Shake Vertical Medium + Fan Shake Vertical Slow + Square Diagonal In + Square Diagonal Out + Vertical Boxes 1 + Vertical Boxes 2 + Ninja Star + + Ninja Star x + Star 1 + Star 1 U-S Down + Star 2 Fast + Star 2 Slow + Waves Fast + Waves Slow + Spiral + + + + Colour + White color + Yellow color + Purple color + Cyan color + Red color + Green color + Blue color + + + + + Speed + Scan speed + + + Speed + Dynamic patterns speed + + + + Mode + Pattern selection + Strobe effect + Color + X Axis Positioning + Y Axis Positioning + Scan Speed + Dynamic Patterns Speed + Zoom + + + + + + + + + diff --git a/resources/fixtures/Shehds/Shehds-Wash-Zoom-36x18W.qxf b/resources/fixtures/Shehds/Shehds-Wash-Zoom-36x18W.qxf new file mode 100644 index 0000000000..2da951fa60 --- /dev/null +++ b/resources/fixtures/Shehds/Shehds-Wash-Zoom-36x18W.qxf @@ -0,0 +1,98 @@ + + + + + Q Light Controller Plus + 4.13.0 GIT + Ioannis Iliopoulos + + Shehds + Wash Zoom LED 36x18W RGBWA+UV + Moving Head + + + + + + + + + + + + Shutter + Open + Off + Strobe + + + + Effect + No Function + Pan Auto Mode + Tilt Auto Mode + Pan & Tilt Auto Mode + Sound Control Mode + + + Colour + No Function + Gradient Color Mode + Pulse Color Mode + Jump Color Mode + + + + + Maintenance + No Function + Reset System + + + Colour + No Function + CTO + Macro Color + + + Pan + Tilt + Pan/Tilt speed - Pan/Tilt time + Dimmer + Red + Green + Blue + White + Amber + Violet + Strobe + Zoom + + + Pan + Tilt + Pan/Tilt speed - Pan/Tilt time + Dimmer + Red + Green + Blue + White + Amber + Violet + Strobe + Zoom + Pan/Tilt Auto + Color Auto + Pan Fine + Tilt Fine + Reset System + Macro Color + + + + + + + + + From 7a4deea0bed21e3251048d60494221540c36259c Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Sun, 10 Mar 2024 14:07:43 +0100 Subject: [PATCH 092/212] resources: 4 new fixtures (see changelog) --- debian/changelog | 4 +- resources/fixtures/DTS/DTS-Jack.qxf | 278 ++++++++++++++++++ resources/fixtures/FixturesMap.xml | 4 + resources/fixtures/Robe/Robe-LEDBeam-350.qxf | 163 ++++++++++ resources/fixtures/UKing/UKing-ZQ-02319.qxf | 57 ++++ .../lightmaXX-Vector-ARC-Flood-II.qxf | 44 +++ 6 files changed, 549 insertions(+), 1 deletion(-) create mode 100644 resources/fixtures/DTS/DTS-Jack.qxf create mode 100644 resources/fixtures/Robe/Robe-LEDBeam-350.qxf create mode 100644 resources/fixtures/UKing/UKing-ZQ-02319.qxf create mode 100644 resources/fixtures/lightmaXX/lightmaXX-Vector-ARC-Flood-II.qxf diff --git a/debian/changelog b/debian/changelog index 543adac309..ff4f66ca50 100644 --- a/debian/changelog +++ b/debian/changelog @@ -38,7 +38,7 @@ qlcplus (4.13.0) stable; urgency=low * New fixture: Varytec Hero Spot 60 (thanks to Hans-Jürgen Tappe) * New fixture: beamZ BAC503 (thanks to archlinette) * New fixtures: Cameo Flat Pro 7, 12 and 18 (thanks to Janosch Frank) - * New fixture: Eurolite LED TMH-X4 (thanks to Tolmino Muccitelli) + * New fixtures: Eurolite LED TMH-X4, lightmaXX Vector ARC Flood II (thanks to Tolmino Muccitelli) * New fixtures: Cameo Q-Spot 40 RGBW, Varytec LED PAR 14x8W, Varytec LED Typhoon PAR Outdoor (12x10) (thanks to Jochen Becker) * New fixtures: Audibax Iowa 70, Pro-Lights CromoWash100 (thanks to Cristian) * New fixtures: Showtec Spectral M1000 Q4, Showtec Kanjo Wash RGB (thanks to Michel Sliepenbeek) @@ -81,6 +81,8 @@ qlcplus (4.13.0) stable; urgency=low * 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, 10 Mar 2024 12:13:14 +0200 diff --git a/resources/fixtures/DTS/DTS-Jack.qxf b/resources/fixtures/DTS/DTS-Jack.qxf new file mode 100644 index 0000000000..4e85bf9f12 --- /dev/null +++ b/resources/fixtures/DTS/DTS-Jack.qxf @@ -0,0 +1,278 @@ + + + + + Q Light Controller Plus + 4.13.0 GIT + Tomas Hastings + + DTS + Jack + Moving Head + + + + + + Speed + Standard + Fast Movement + Vector Mode from Fast to Slow + Variable time reaction to DMX Signal (fast to slow) + Silent Movement + + + Pan + Pan Far Settings + + + + Shutter + Blackout + Open + Blackout + Strobe random speed + Strobe speed 1 + Strobe speed 2 + Strobe speed 3 + Strobe speed 4 + Strobe speed 5 + Strobe speed 6 + Flash open speed 1 + Flash open speed 2 + Flash open speed 3 + Flash open speed 4 + Flash closed speed 1 + Flash closed speed 2 + Flash closed speed 3 + Flash closed speed 4 + Colours / Gobo in black-out + Pan / Tilt in black-out + Open + + + Colour + Colour 1 + Colour 2 + Colour 3 + Colour 4 + Colour 5 + Colour 6 + Colour 7 + Colour 8 + Colour 9 + Colour 10 + Colour 11 + Colour 12 + Colour 13 + Colour 14 + Colour 15 + Colour 16 + Colour 17 + Colour 18 + + + Colour + Full Colour + Half Colour + Proportional Colour + Rainbow + + + Gobo + Open + Gobo 1 + Gobo 2 + Gobo 3 + Gobo 4 + Gobo 5 + Gobo 6 + Gobo 7 + Gobo 8 + Gobo 9 + Speed Rotation 1 (min) + Speed Rotation 2 + Speed Rotation 3 + Speed Rotation 4 + Speed Rotation 5 + Speed Rotation 6 + Speed Rotation 7 + Speed Rotation 8 (max) + + + Gobo + Gobo Rotation Mode + Gobo Index Mode + + + Gobo + Stop + Left Rotation (max to min) + Stop + Right Rotation (min to max) + + + Gobo + Gobo Index Fine + + + Gobo + Stop + Gobo Shake R-L Speed 1 + Gobo Shake R-L Speed 2 + Gobo Shake R-L Speed 3 + Gobo Shake R-L Speed 4 + Gobo Shake R-L Speed 5 + Gobo Shake R-L Speed 6 + Gobo Shake R-L Speed 7 + Gobo Shake R-L Speed 8 + Gobo Shake R-L Speed 9 + Stop + Gobo Shake L-R Speed 1 + Gobo Shake L-R Speed 2 + Gobo Shake L-R Speed 3 + Gobo Shake L-R Speed 4 + Gobo Shake L-R Speed 5 + Gobo Shake L-R Speed 6 + Gobo Shake L-R Speed 7 + Gobo Shake L-R Speed 8 + Gobo Shake L-R Speed 9 + + + Gobo + Open + Gobo 1 + Gobo 2 + Gobo 3 + Gobo 4 + Gobo 5 + Gobo 6 + Gobo 7 + Gobo 8 + Gobo 9 + Gobo 10 + Speed Rotation 1 (min) + Speed Rotation 2 + Speed Rotation 3 + Speed Rotation 4 + Speed Rotation 5 + Speed Rotation 6 + Speed Rotation 7 (max) + + + Gobo + Stop + Gobo shake R-L speed 1 + Gobo shake R-L speed 2 + Gobo shake R-L speed 3 + Gobo shake R-L speed 4 + Gobo shake R-L speed 5 + Gobo shake R-L speed 6 + Gobo shake R-L speed 7 + Gobo shake R-L speed 8 + Gobo shake R-L speed 9 + Stop + Gobo shake L-R speed 1 + Gobo shake L-R speed 2 + Gobo shake L-R speed 3 + Gobo shake L-R speed 4 + Gobo shake L-R speed 5 + Gobo shake L-R speed 6 + Gobo shake L-R speed 7 + Gobo shake L-R speed 8 + Gobo shake L-R speed 9 + + + Shutter + Open + Linear iris from open to closed + Closed + + + Maintenance + No effect + Iris pulse at different speed from min to max + Iris pulse with flash closing from min to max + Iris pulse with flash opening from min to max + Iris pulse with flash closing combined with zoom from min to max + Iris pulse with flash opening combined with zoom from min to max + + + Maintenance + Iris Macros + Beam Macros + Wash Macros + Spot Macros + + + Prism + No Effect + Prism Inserted + + + + + + Maintenance + No Effect + Lamp Off (3 sec) + No Effect + Lamp On (3 sec) + No Effect + Internal Motor Reset + Total Reset + + + Pan + Pan Fine + Tilt + Tilt Fine + Pan/Tilt speed + Pan Far + Dimmer + Shutter + Colour + Colour Mode + Gobo + Gobo Mode + Gobo Rotation + Gobo Index Fine + Gobo Shake + Fixed Gobo + Fixed Gobo Shake + Iris + Macros + Macros Mode + Prism + Focus + Focus Fine + Zoom + Reset + Lamp + + + Pan + Pan Fine + Tilt + Tilt Fine + Pan/Tilt speed + Pan Far + Dimmer + Shutter + Colour + Gobo + Gobo Rotation + Fixed Gobo + Iris + Prism + Focus + Zoom + Reset + Lamp + + + + + + + + + diff --git a/resources/fixtures/FixturesMap.xml b/resources/fixtures/FixturesMap.xml index 3978a7ff43..183476fe5c 100644 --- a/resources/fixtures/FixturesMap.xml +++ b/resources/fixtures/FixturesMap.xml @@ -647,6 +647,7 @@ + @@ -1143,6 +1144,7 @@ + @@ -1412,6 +1414,7 @@ + @@ -1731,6 +1734,7 @@ + diff --git a/resources/fixtures/Robe/Robe-LEDBeam-350.qxf b/resources/fixtures/Robe/Robe-LEDBeam-350.qxf new file mode 100644 index 0000000000..55fc474562 --- /dev/null +++ b/resources/fixtures/Robe/Robe-LEDBeam-350.qxf @@ -0,0 +1,163 @@ + + + + + Q Light Controller Plus + 4.13.0 GIT + Tomas Hastings + + Robe + LEDBeam 350 + Moving Head + + + + + + Speed + Standard Mode + Max Speed Mode + Speed from Max to Min + + + Effect + Reserved - To Activate: Shutter/Strobe (Ch 20/15/22)@(0-31) off, stop at value for 3s. + Display On + Display Off + RGBW Colour Mixing Mode + CMY Colour Mixing Mode + Pan/Tilt Speed Mode + Pan/Tilt Time Mode + Blackout While Pan/Tilt Moving + Disabled Blackout While Pan/Tilt Moving + Dimmer Curve - Square Law + Dimmer Curve - Linear + Fan Mode: Auto + Fan Mode: High + White Point 8000K ON + White Point 8000K OFF + Reserved + Pan 540deg (Full) + Pan 450deg (Reduced) + Quiet Mode: Fans On @ Blackout + Quiet Mode: Fans Off @ Blackout + Reserved - New Function Menu + Reserved + Pan/Tile Reset + Zoom Reset + Reserved + Tungsten effect simulation (750W) On + Tungsten effect simulation (1000W) On + Tungsten effect simulation (1200W) On + Tungsten effect simulation (2000W) On + Tungsten effect simulation (2500W) On + Tungsten effect simulation Off + Reserved + Total Fixture Reset + Reserved + RoboSpot Enabled + RoboSpot Disabled - Except Handle Faders and Pan/Tilt + RoboSpot Fully Disabled + Reserved + Disabled Quiet Mode + Quiet Mode - Fan Control From Min to Max + + + Effect + PWM Frequency from Display Menu + 300Hz + 600Hz (10=default) + 1200Hz + 2400Hz + High + Reserved (fixture utilises PWM frequency set in the display menu item Frequency Setup) + + + Effect + Selected Frequency + LED Frequency Steps (-126 - 126) + Selected Frequency + + + Colour + Preset Filters + Raw DMX + Rainbow Effect (with fade time slow -> fast) + Rainbow Effect (without fade time slow -> fast) + + + + + + + + + + + Colour + Col. temperature correction from 8000K to 2700K -for whites only + + + Colour + Virtual Colours + Maximum Mode (Highest values have priority) + Minimum Mode (Lowest values have priority) + Multiply Mode (Multiply Virtual and Colour mix) + Addition Mode (Virtual + Colour Mix, 45 default) + Substration Mode (Virtual - Colour Mix) + Subtraction Mode (Colour - Virtual Mix) + Reserved + Virtual Colours + Crossfade between Virtual and Colour Mix + Colour Channels + + + + + Shutter + Shutter Closed + Shutter Open + Strobe Effect Slow -> Fast + Shutter Open + Opening Pulse in Sequences from Slow -> Fast + Closing Pulse in Sequences from Fast -> Slow + Shutter Open + Random Strobe Effect from Slow -> Fast + Shutter Open + + + + + Pan + Pan Fine + Tilt + Tilt Fine + Pan/Tilt speed + Power / Special Functions + LED Frequency Selection + LED Frequency Fine Adjusting + Virtual Colour Wheel + Red + Red Fine + Green + Green fine + Blue + Blue Fine + White + White Fine + Colour Temperature Correction + Colour Mix Control + Zoom + Zoom fine + Shutter / Strobe + Dimmer + Dimmer Fine + + + + + + + + + diff --git a/resources/fixtures/UKing/UKing-ZQ-02319.qxf b/resources/fixtures/UKing/UKing-ZQ-02319.qxf new file mode 100644 index 0000000000..e20b9577e6 --- /dev/null +++ b/resources/fixtures/UKing/UKing-ZQ-02319.qxf @@ -0,0 +1,57 @@ + + + + + Q Light Controller Plus + 4.13.0 GIT + Mike Ubl + + UKing + ZQ-02319 + Moving Head + + + + + + + + + + + + + Effect + No effect + Auto Mode 1 + Auto Mode 2 + Sound Control + + + Maintenance + No Effect + Reset + + + Pan + Pan-Fine + Tilt + Tilt-Fine + Speed + Dimming + Strobe + Red + Green + Blue + White + Auto-Mode + Reset + + + + + + + + + diff --git a/resources/fixtures/lightmaXX/lightmaXX-Vector-ARC-Flood-II.qxf b/resources/fixtures/lightmaXX/lightmaXX-Vector-ARC-Flood-II.qxf new file mode 100644 index 0000000000..877fe53fa5 --- /dev/null +++ b/resources/fixtures/lightmaXX/lightmaXX-Vector-ARC-Flood-II.qxf @@ -0,0 +1,44 @@ + + + + + Q Light Controller Plus + 4.13.0 GIT + Tolmino Muccitelli + + lightmaXX + Vector ARC Flood II + Color Changer + + + + + + + + + + + Speed + Macro function Speed adjust + + + Master dimmer + Red + Green + Blue + White + Amber + UV + Strobe + Color macro + Macro function Speed adjust + + + + + + + + + From 23e2a1cd2695deee33b5af0788b0e34d3b78a9d5 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Sun, 10 Mar 2024 23:03:00 +0100 Subject: [PATCH 093/212] Fix feedbacks -> feedback (fix #1525) --- engine/src/qlcinputchannel.cpp | 4 +- engine/src/qlcinputchannel.h | 2 +- engine/src/qlcinputprofile.h | 4 +- engine/src/universe.cpp | 6 +- engine/src/universe.h | 6 +- qmlui/qml/ExternalControlDelegate.qml | 2 +- qmlui/qml/inputoutput/UniverseIOItem.qml | 6 +- qmlui/virtualconsole/vcwidget.cpp | 4 +- resources/inputprofiles/Akai-APC40-mkII.qxi | 82 ++++----- resources/inputprofiles/Akai-APCMini-mk2.qxi | 162 +++++++++--------- .../Novation-LaunchPadMiniMK3.qxi | 162 +++++++++--------- ui/src/CMakeLists.txt | 2 +- ...cksdialog.cpp => customfeedbackdialog.cpp} | 20 +-- ...edbacksdialog.h => customfeedbackdialog.h} | 16 +- ...backsdialog.ui => customfeedbackdialog.ui} | 10 +- ui/src/inputselectionwidget.cpp | 4 +- ui/src/src.pro | 6 +- ui/src/virtualconsole/vcwidget.cpp | 4 +- 18 files changed, 251 insertions(+), 251 deletions(-) rename ui/src/{customfeedbacksdialog.cpp => customfeedbackdialog.cpp} (92%) rename ui/src/{customfeedbacksdialog.h => customfeedbackdialog.h} (78%) rename ui/src/{customfeedbacksdialog.ui => customfeedbackdialog.ui} (96%) diff --git a/engine/src/qlcinputchannel.cpp b/engine/src/qlcinputchannel.cpp index b4336ce36e..db99554c19 100644 --- a/engine/src/qlcinputchannel.cpp +++ b/engine/src/qlcinputchannel.cpp @@ -321,7 +321,7 @@ 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; @@ -378,7 +378,7 @@ 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) diff --git a/engine/src/qlcinputchannel.h b/engine/src/qlcinputchannel.h index 741ab46106..88dad0bc73 100644 --- a/engine/src/qlcinputchannel.h +++ b/engine/src/qlcinputchannel.h @@ -48,7 +48,7 @@ class QString; #define KXMLQLCInputChannelRelative QString("Relative") #define KXMLQLCInputChannelSensitivity QString("Sensitivity") #define KXMLQLCInputChannelExtraPress QString("ExtraPress") -#define KXMLQLCInputChannelFeedbacks QString("Feedbacks") +#define KXMLQLCInputChannelFeedback QString("Feedback") #define KXMLQLCInputChannelLowerValue QString("LowerValue") #define KXMLQLCInputChannelUpperValue QString("UpperValue") #define KXMLQLCInputChannelMidiChannel QString("MidiChannel") diff --git a/engine/src/qlcinputprofile.h b/engine/src/qlcinputprofile.h index 23c00867b2..69c6aaa200 100644 --- a/engine/src/qlcinputprofile.h +++ b/engine/src/qlcinputprofile.h @@ -186,7 +186,7 @@ class QLCInputProfile : public QObject /** * Retrieve additional parameters to be passed to plugins - * when sending feedbacks. + * when sending feedback. */ QVariant channelExtraParams(const QLCInputChannel *channel) const; @@ -235,7 +235,7 @@ class QLCInputProfile : public QObject /** Save an input profile into a given file name */ bool saveXML(const QString& fileName); - /** Load an optional color table for RGB LED feedbacks */ + /** Load an optional color table for RGB LED feedback */ bool loadColorTableXML(QXmlStreamReader &tableRoot); /** Load an optional MIDI channel table */ diff --git a/engine/src/universe.cpp b/engine/src/universe.cpp index ee2572d36a..65bd93d29b 100644 --- a/engine/src/universe.cpp +++ b/engine/src/universe.cpp @@ -639,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; } diff --git a/engine/src/universe.h b/engine/src/universe.h index bffc109cea..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 */ @@ -209,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. @@ -258,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. */ diff --git a/qmlui/qml/ExternalControlDelegate.qml b/qmlui/qml/ExternalControlDelegate.qml index ea01b2bc72..e7398a7963 100644 --- a/qmlui/qml/ExternalControlDelegate.qml +++ b/qmlui/qml/ExternalControlDelegate.qml @@ -155,7 +155,7 @@ Column Layout.fillWidth: true height: UISettings.listItemHeight visible: customFeedback - label: qsTr("Custom feedbacks") + label: qsTr("Custom feedback") color: UISettings.bgMedium } diff --git a/qmlui/qml/inputoutput/UniverseIOItem.qml b/qmlui/qml/inputoutput/UniverseIOItem.qml index 8a4816d87c..688ebc820d 100644 --- a/qmlui/qml/inputoutput/UniverseIOItem.qml +++ b/qmlui/qml/inputoutput/UniverseIOItem.qml @@ -151,7 +151,7 @@ Rectangle z: 10 patchesNumber: inputPatchesNumber - showFeedback: universe ? universe.hasFeedbacks : false + showFeedback: universe ? universe.hasFeedback : false } // Input patch drop area @@ -266,8 +266,8 @@ Rectangle checkedColor: "green" imgSource: "" checkable: true - checked: universe ? universe.hasFeedbacks : false - tooltip: qsTr("Enable/Disable feedbacks") + checked: universe ? universe.hasFeedback : false + tooltip: qsTr("Enable/Disable feedback") onToggled: { if (universe) diff --git a/qmlui/virtualconsole/vcwidget.cpp b/qmlui/virtualconsole/vcwidget.cpp index 119008f068..ae49e1c482 100644 --- a/qmlui/virtualconsole/vcwidget.cpp +++ b/qmlui/virtualconsole/vcwidget.cpp @@ -666,7 +666,7 @@ void VCWidget::addInputSource(QSharedPointer const& source) { QLCInputProfile *profile = ip->profile(); - // retrieve plugin specific params for feedbacks + // retrieve plugin specific params for feedback if (source->feedbackExtraParams(QLCInputFeedback::LowerValue).toInt() == -1) source->setFeedbackExtraParams(QLCInputFeedback::LowerValue, profile->channelExtraParams(ich)); if (source->feedbackExtraParams(QLCInputFeedback::UpperValue).toInt() == -1) @@ -697,7 +697,7 @@ void VCWidget::addInputSource(QSharedPointer const& source) this, SLOT(slotInputSourceValueChanged(quint32,quint32,uchar))); } - // user custom feedbacks have precedence over input profile custom feedbacks + // user custom feedback have precedence over input profile custom feedback uchar lower = source->feedbackValue(QLCInputFeedback::LowerValue) != 0 ? source->feedbackValue(QLCInputFeedback::LowerValue) : ich->lowerValue(); diff --git a/resources/inputprofiles/Akai-APC40-mkII.qxi b/resources/inputprofiles/Akai-APC40-mkII.qxi index 557b89dcdc..b5c90a47a2 100644 --- a/resources/inputprofiles/Akai-APC40-mkII.qxi +++ b/resources/inputprofiles/Akai-APC40-mkII.qxi @@ -99,202 +99,202 @@ Button 5-1 Button - + Button 5-2 Button - + Button 5-3 Button - + Button 5-4 Button - + Button 5-5 Button - + Button 5-6 Button - + Button 5-7 Button - + Button 5-8 Button - + Button 4-1 Button - + Button 4-2 Button - + Button 4-3 Button - + Button 4-4 Button - + Button 4-5 Button - + Button 4-6 Button - + Button 4-7 Button - + Button 4-8 Button - + Button 3-1 Button - + Button 3-2 Button - + Button 3-3 Button - + Button 3-4 Button - + Button 3-5 Button - + Button 3-6 Button - + Button 3-7 Button - + Button 3-8 Button - + Button 2-1 Button - + Button 2-2 Button - + Button 2-3 Button - + Button 2-4 Button - + Button 2-5 Button - + Button 2-6 Button - + Button 2-7 Button - + Button 2-8 Button - + Button 1-1 Button - + Button 1-2 Button - + Button 1-3 Button - + Button 1-4 Button - + Button 1-5 Button - + Button 1-6 Button - + Button 1-7 Button - + Button 1-8 Button - + R 1 @@ -355,7 +355,7 @@ Master Button - + Stop all clips diff --git a/resources/inputprofiles/Akai-APCMini-mk2.qxi b/resources/inputprofiles/Akai-APCMini-mk2.qxi index 24c198ff41..44a30c2ecb 100644 --- a/resources/inputprofiles/Akai-APCMini-mk2.qxi +++ b/resources/inputprofiles/Akai-APCMini-mk2.qxi @@ -48,407 +48,407 @@ Button 1-8 Button - + Button 2-8 Button - + Button 3-8 Button - + Button 4-8 Button - + Button 5-8 Button - + Button 6-8 Button - + Button 7-8 Button - + Button 8-8 Button - + Button 1-7 Button - + Button 2-7 Button - + Button 3-7 Button - + Button 4-7 Button - + Button 5-7 Button - + Button 6-7 Button - + Button 7-7 Button - + Button 8-7 Button - + Button 1-6 Button - + Button 2-6 Button - + Button 3-6 Button - + Button 4-6 Button - + Button 5-6 Button - + Button 6-6 Button - + Button 7-6 Button - + Button 8-6 Button - + Button 1-5 Button - + Button 2-5 Button - + Button 3-5 Button - + Button 4-5 Button - + Button 5-5 Button - + Button 6-5 Button - + Button 7-5 Button - + Button 8-5 Button - + Button 1-4 Button - + Button 2-4 Button - + Button 3-4 Button - + Button 4-4 Button - + Button 5-4 Button - + Button 6-4 Button - + Button 7-4 Button - + Button 8-4 Button - + Button 1-3 Button - + Button 2-3 Button - + Button 3-3 Button - + Button 4-3 Button - + Button 5-3 Button - + Button 6-3 Button - + Button 7-3 Button - + Button 8-3 Button - + Button 1-2 Button - + Button 2-2 Button - + Button 3-2 Button - + Button 4-2 Button - + Button 5-2 Button - + Button 6-2 Button - + Button 7-2 Button - + Button 8-2 Button - + Button 1-1 Button - + Button 2-1 Button - + Button 3-1 Button - + Button 4-1 Button - + Button 5-1 Button - + Button 6-1 Button - + Button 7-1 Button - + Button 8-1 Button - + Volume Button Button - + Pan Button Button - + Send Button Button - + Device Button Button - + Up Button Button - + Down Button Button - + Left Button Button - + Right Button Button - + Clip stop Button Button - + Solo Button Button - + Mute Button Button - + Rec arm Button Button - + Select Button Button - + Drum Button Button - + Note Button Button - + Stop all clips Button Button - + Shift Button Button - + diff --git a/resources/inputprofiles/Novation-LaunchPadMiniMK3.qxi b/resources/inputprofiles/Novation-LaunchPadMiniMK3.qxi index d4fef4c79f..b3cbdff10e 100644 --- a/resources/inputprofiles/Novation-LaunchPadMiniMK3.qxi +++ b/resources/inputprofiles/Novation-LaunchPadMiniMK3.qxi @@ -12,406 +12,406 @@ Stop Solo Mute Button - + Row 2 Arrow Button - + Row 3 Arrow Button - + Row 4 Arrow Button - + Row 5 Arrow Button - + Row 6 Arrow Button - + Row 7 Arrow Button - + Row 8 Arrow Button - + Up Button - + Down Button - + Left Button - + Right Button - + Session Button - + Drums Button - + Keys Button - + User Button - + Logo Button - + Pad 1-1 Button - + Pad 1-2 Button - + Pad 1-3 Button - + Pad 1-4 Button - + Pad 1-5 Button - + Pad 1-6 Button - + Pad 1-7 Button - + Pad 1-8 Button - + Pad 2-1 Button - + Pad 2-2 Button - + Pad 2-3 Button - + Pad 2-4 Button - + Pad 2-5 Button - + Pad 2-6 Button - + Pad 2-7 Button - + Pad 2-8 Button - + Pad 3-1 Button - + Pad 3-2 Button - + Pad 3-3 Button - + Pad 3-4 Button - + Pad 3-5 Button - + Pad 3-6 Button - + Pad 3-7 Button - + Pad 3-8 Button - + Pad 4-1 Button - + Pad 4-2 Button - + Pad 4-3 Button - + Pad 4-4 Button - + Pad 4-5 Button - + Pad 4-6 Button - + Pad 4-7 Button - + Pad 4-8 Button - + Pad 5-1 Button - + Pad 5-2 Button - + Pad 5-3 Button - + Pad 5-4 Button - + Pad 5-5 Button - + Pad 5-6 Button - + Pad 5-7 Button - + Pad 5-8 Button - + Pad 6-1 Button - + Pad 6-2 Button - + Pad 6-3 Button - + Pad 6-4 Button - + Pad 6-5 Button - + Pad 6-6 Button - + Pad 6-7 Button - + Pad 6-8 Button - + Pad 7-1 Button - + Pad 7-2 Button - + Pad 7-3 Button - + Pad 7-4 Button - + Pad 7-5 Button - + Pad 7-6 Button - + Pad 7-7 Button - + Pad 7-8 Button - + Pad 8-1 Button - + Pad 8-2 Button - + Pad 8-3 Button - + Pad 8-4 Button - + Pad 8-5 Button - + Pad 8-6 Button - + Pad 8-7 Button - + Pad 8-8 Button - + diff --git a/ui/src/CMakeLists.txt b/ui/src/CMakeLists.txt index 4f412b1ba0..acb69d739b 100644 --- a/ui/src/CMakeLists.txt +++ b/ui/src/CMakeLists.txt @@ -60,7 +60,7 @@ add_library(${module_name} createfixturegroup.cpp createfixturegroup.h createfixturegroup.ui ctkrangeslider.cpp ctkrangeslider.h cuestackmodel.cpp cuestackmodel.h - customfeedbacksdialog.cpp customfeedbacksdialog.h customfeedbacksdialog.ui + customfeedbackdialog.cpp customfeedbackdialog.h customfeedbackdialog.ui dmxdumpfactory.cpp dmxdumpfactory.h dmxdumpfactory.ui efxeditor.cpp efxeditor.h efxeditor.ui efxpreviewarea.cpp efxpreviewarea.h diff --git a/ui/src/customfeedbacksdialog.cpp b/ui/src/customfeedbackdialog.cpp similarity index 92% rename from ui/src/customfeedbacksdialog.cpp rename to ui/src/customfeedbackdialog.cpp index f3e19b18c7..7e3b57fd61 100644 --- a/ui/src/customfeedbacksdialog.cpp +++ b/ui/src/customfeedbackdialog.cpp @@ -1,6 +1,6 @@ /* Q Light Controller Plus - customfeedbacksdialog.cpp + customfeedbackdialog.cpp Copyright (c) Massimo Callegari @@ -17,12 +17,12 @@ limitations under the License. */ -#include "customfeedbacksdialog.h" +#include "customfeedbackdialog.h" #include "qlcinputchannel.h" #include "qlcinputsource.h" #include "doc.h" -CustomFeedbacksDialog::CustomFeedbacksDialog(Doc *doc, const QSharedPointer &source, QWidget *parent) +CustomFeedbackDialog::CustomFeedbackDialog(Doc *doc, const QSharedPointer &source, QWidget *parent) : QDialog(parent) , m_doc(doc) , m_profile(NULL) @@ -126,18 +126,18 @@ CustomFeedbacksDialog::CustomFeedbacksDialog(Doc *doc, const QSharedPointersetVisible(visible); m_monitorSpin->setVisible(visible); m_monitorChannelCombo->setVisible(visible); } -void CustomFeedbacksDialog::accept() +void CustomFeedbackDialog::accept() { if (m_inputSource.isNull()) return; @@ -158,25 +158,25 @@ void CustomFeedbacksDialog::accept() QDialog::accept(); } -void CustomFeedbacksDialog::slotLowerColorButtonClicked() +void CustomFeedbackDialog::slotLowerColorButtonClicked() { m_selectedFeedback = LowerValue; m_profileColorsTree->setVisible(true); } -void CustomFeedbacksDialog::slotUpperColorButtonClicked() +void CustomFeedbackDialog::slotUpperColorButtonClicked() { m_selectedFeedback = UpperValue; m_profileColorsTree->setVisible(true); } -void CustomFeedbacksDialog::slotMonitorColorButtonClicked() +void CustomFeedbackDialog::slotMonitorColorButtonClicked() { m_selectedFeedback = MonitoringValue; m_profileColorsTree->setVisible(true); } -void CustomFeedbacksDialog::slotColorSelected(QTreeWidgetItem *item) +void CustomFeedbackDialog::slotColorSelected(QTreeWidgetItem *item) { QLabel *label = qobject_cast(m_profileColorsTree->itemWidget(item, 2)); diff --git a/ui/src/customfeedbacksdialog.h b/ui/src/customfeedbackdialog.h similarity index 78% rename from ui/src/customfeedbacksdialog.h rename to ui/src/customfeedbackdialog.h index 8ca639c0d3..754adcad90 100644 --- a/ui/src/customfeedbacksdialog.h +++ b/ui/src/customfeedbackdialog.h @@ -1,6 +1,6 @@ /* Q Light Controller Plus - customfeedbacksdialog.h + customfeedbackdialog.h Copyright (c) Massimo Callegari @@ -17,18 +17,18 @@ limitations under the License. */ -#ifndef CUSTOMFEEDBACKSDIALOG_H -#define CUSTOMFEEDBACKSDIALOG_H +#ifndef CUSTOMFEEDBACKDIALOG_H +#define CUSTOMFEEDBACKDIALOG_H #include -#include "ui_customfeedbacksdialog.h" +#include "ui_customfeedbackdialog.h" class Doc; class QLCInputSource; class QLCInputProfile; -class CustomFeedbacksDialog : public QDialog, public Ui_CustomFeedbacksDialog +class CustomFeedbackDialog : public QDialog, public Ui_CustomFeedbackDialog { Q_OBJECT @@ -36,8 +36,8 @@ class CustomFeedbacksDialog : public QDialog, public Ui_CustomFeedbacksDialog * Initialization *********************************************************************/ public: - explicit CustomFeedbacksDialog(Doc *doc, QSharedPointer const& source, QWidget *parent = nullptr); - ~CustomFeedbacksDialog(); + explicit CustomFeedbackDialog(Doc *doc, QSharedPointer const& source, QWidget *parent = nullptr); + ~CustomFeedbackDialog(); enum SelectedFeedback { None, LowerValue, UpperValue, MonitoringValue }; @@ -59,4 +59,4 @@ protected slots: SelectedFeedback m_selectedFeedback; }; -#endif // CUSTOMFEEDBACKSDIALOG_H +#endif // CUSTOMFEEDBACKDIALOG_H diff --git a/ui/src/customfeedbacksdialog.ui b/ui/src/customfeedbackdialog.ui similarity index 96% rename from ui/src/customfeedbacksdialog.ui rename to ui/src/customfeedbackdialog.ui index 9d1490e0d2..b8b0637bf7 100644 --- a/ui/src/customfeedbacksdialog.ui +++ b/ui/src/customfeedbackdialog.ui @@ -1,7 +1,7 @@ - CustomFeedbacksDialog - + CustomFeedbackDialog + 0 @@ -11,7 +11,7 @@ - Custom Feedbacks Configuration + Custom Feedback Configuration @@ -223,7 +223,7 @@ buttonBox accepted() - CustomFeedbacksDialog + CustomFeedbackDialog accept() @@ -239,7 +239,7 @@ buttonBox rejected() - CustomFeedbacksDialog + CustomFeedbackDialog reject() diff --git a/ui/src/inputselectionwidget.cpp b/ui/src/inputselectionwidget.cpp index 633c38baac..d3c2eedc53 100644 --- a/ui/src/inputselectionwidget.cpp +++ b/ui/src/inputselectionwidget.cpp @@ -19,7 +19,7 @@ #include -#include "customfeedbacksdialog.h" +#include "customfeedbackdialog.h" #include "inputselectionwidget.h" #include "selectinputchannel.h" #include "qlcinputchannel.h" @@ -183,7 +183,7 @@ void InputSelectionWidget::slotChooseInputClicked() void InputSelectionWidget::slotCustomFeedbackClicked() { - CustomFeedbacksDialog cfDialog(m_doc, m_inputSource, this); + CustomFeedbackDialog cfDialog(m_doc, m_inputSource, this); cfDialog.setMonitoringVisibility(m_supportMonitoring); cfDialog.exec(); } diff --git a/ui/src/src.pro b/ui/src/src.pro index 2f938b1cb0..8f80738cca 100644 --- a/ui/src/src.pro +++ b/ui/src/src.pro @@ -68,7 +68,7 @@ HEADERS += aboutbox.h \ createfixturegroup.h \ ctkrangeslider.h \ cuestackmodel.h \ - customfeedbacksdialog.h \ + customfeedbackdialog.h \ dmxdumpfactory.h \ efxeditor.h \ efxpreviewarea.h \ @@ -187,7 +187,7 @@ FORMS += aboutbox.ui \ channelsselection.ui \ collectioneditor.ui \ createfixturegroup.ui \ - customfeedbacksdialog.ui \ + customfeedbackdialog.ui \ dmxdumpfactory.ui \ efxeditor.ui \ fixturegroupeditor.ui \ @@ -250,7 +250,7 @@ SOURCES += aboutbox.cpp \ createfixturegroup.cpp \ ctkrangeslider.cpp \ cuestackmodel.cpp \ - customfeedbacksdialog.cpp \ + customfeedbackdialog.cpp \ dmxdumpfactory.cpp \ efxeditor.cpp \ efxpreviewarea.cpp \ diff --git a/ui/src/virtualconsole/vcwidget.cpp b/ui/src/virtualconsole/vcwidget.cpp index d418b79cc7..34c51ccdf9 100644 --- a/ui/src/virtualconsole/vcwidget.cpp +++ b/ui/src/virtualconsole/vcwidget.cpp @@ -620,7 +620,7 @@ void VCWidget::setInputSource(QSharedPointer const& source, quin QLCInputChannel *ich = profile->channel(source->channel() & 0xFFFF); if (ich != NULL) { - // retrieve plugin specific params for feedbacks + // retrieve plugin specific params for feedback if (source->feedbackExtraParams(QLCInputFeedback::LowerValue).toInt() == -1) source->setFeedbackExtraParams(QLCInputFeedback::LowerValue, profile->channelExtraParams(ich)); if (source->feedbackExtraParams(QLCInputFeedback::UpperValue).toInt() == -1) @@ -651,7 +651,7 @@ void VCWidget::setInputSource(QSharedPointer const& source, quin this, SLOT(slotInputValueChanged(quint32,quint32,uchar))); } - // user custom feedbacks have precedence over input profile custom feedbacks + // user custom feedback have precedence over input profile custom feedback uchar lower = source->feedbackValue(QLCInputFeedback::LowerValue) != 0 ? source->feedbackValue(QLCInputFeedback::LowerValue) : ich->lowerValue(); From bf75b007b71543b56487ca60963d5ba7d5eb8d6c Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Sun, 10 Mar 2024 23:03:22 +0100 Subject: [PATCH 094/212] ui: update translations --- ui/src/qlcplus_ca_ES.ts | 1146 +++++++++++++++++++++----------------- ui/src/qlcplus_cz_CZ.ts | 1146 +++++++++++++++++++++----------------- ui/src/qlcplus_de_DE.ts | 1146 +++++++++++++++++++++----------------- ui/src/qlcplus_es_ES.ts | 1146 +++++++++++++++++++++----------------- ui/src/qlcplus_fi_FI.ts | 1146 +++++++++++++++++++++----------------- ui/src/qlcplus_fr_FR.ts | 1146 +++++++++++++++++++++----------------- ui/src/qlcplus_it_IT.ts | 1174 +++++++++++++++++++++------------------ ui/src/qlcplus_ja_JP.ts | 1146 +++++++++++++++++++++----------------- ui/src/qlcplus_nl_NL.ts | 1146 +++++++++++++++++++++----------------- ui/src/qlcplus_pt_BR.ts | 1146 +++++++++++++++++++++----------------- ui/src/rdmmanager.ui | 4 +- 11 files changed, 6336 insertions(+), 5156 deletions(-) diff --git a/ui/src/qlcplus_ca_ES.ts b/ui/src/qlcplus_ca_ES.ts index f9d1978f97..22cbdc99c8 100644 --- a/ui/src/qlcplus_ca_ES.ts +++ b/ui/src/qlcplus_ca_ES.ts @@ -187,12 +187,12 @@ Model del fixture - + Fixtures found: %1 Fixtures trobats: %1 - + Dimmers Dimmers @@ -522,375 +522,375 @@ App - + Fixtures Fixtures - + Functions Funcions - + Shows Shows - + Virtual Console Consola Virtual - + Simple Desk Taula Simple - + Inputs/Outputs Entrades/Sortides - + Cannot exit in Operate mode No es pot sortir en Mode Operació - + You must switch back to Design mode to close the application. Ha de canviar a Mode Disseny per tancar l'aplicació. - + Close Tancar - + Do you wish to save the current workspace before closing the application? Voleu desar l'espai de treball actual abans de tancar l'aplicació? - + Close the application? Tancar l'aplicacció? - + Do you wish to close the application? Voleu tancar l'aplicació? - + Starting Q Light Controller Plus Iniciant Q Light Controller Plus - + - New Workspace - Nou Espai de treball - + Exit Sortir - + Switch to Design Mode Canviar a Mode Disseny - + There are still running functions. Really stop them and switch back to Design mode? Encara hi ha funcions en execució. Voleu aturar-les i tornar a Mode Disseny? - + Design Disseny - + Switch to design mode Canviar a Mode Disseny - + Operate Operació - - + + Switch to operate mode Canviar a Mode Operació - + &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... - + &Operate &Operació - + CTRL+F12 Control|Toggle operate/design mode CTRL+F12 - + &Monitor &Monitor - + CTRL+M Control|Monitor CTRL+M - + Address Tool Eina de direccionament - + Toggle &Blackout Activar/Desactivar &Blackout - + Live edit a function Editar funció en viu - + Toggle Virtual Console Live edit Activa/Desactiva l'edició en viu de la Consola Virtual - + Dump DMX values to a function Bolcar valors DMX a una funció - + CTRL+D Control|Dump DMX CTRL+D - + Stop ALL functions! ¡Aturar TOTES les funcions! - + Fade 1 second and stop Esvaiment podria ser una bona alternativa a Fade Out Fade out de 1 segon i aturar - + Fade 5 seconds and stop Fade out de 5 segons i aturar - + Fade 10 second and stop Fade out de 10 segons i aturar - + Fade 30 second and stop Fade out de 30 segons i aturar - + Toggle Full Screen Canviar a Pantalla Completa - + CTRL+F11 Control|Toggle Full Screen CTRL+F11 - + &Index &Index - + SHIFT+F1 Help|Index SHIFT+F1 - + &About QLC+ Sobre &QLC+ - + Quit QLC+ Abandonar QLC+ - + Workspace Espai de Treball - + Unable to read from file Impossible llegir des de l'arxiu - + Unable to write to file Impossible escriure a l'arxiu - + A fatal error occurred S'ha produït un error fatal - + Unable to access resource Impossible accedir al recurs - + Unable to open file for reading or writing Impossible obrir l'ariux per llegir o escriure - + Operation was aborted L'operació s'ha avortat - + Operation timed out Operació temps d'espera esgotat - + An unspecified error has occurred. Nice. Ha ocorregut un error desconegut.Genial. - + File error Error d'arxiu - - - + + + Do you wish to save the current workspace? Changes will be lost if you don't save them. Voleu desar l'espai de treball actual? Els canvis es perdran si no els guarda. - + New Workspace Nou Espai de Treball - - - + + + Open Workspace Obrir Espai de Treball - - + + Workspaces (*%1) Espais de Treball (*%1) - - + + All Files (*.*) Tots els arxius (*.*) - - + + All Files (*) Tots els arxius (*) - + Save Workspace As Desar l'espai de treball com - + Error Error - + File not found! The selected file has been moved or deleted. ¡Arxiu no trobat! L'arxiu seleccionat s'ha mogut o esborrat. - + Warning Advertència - + Some errors occurred while loading the project: S'han produït alguns errors en carregar el projecte: @@ -913,12 +913,12 @@ L'arxiu seleccionat s'ha mogut o esborrat. Tancar automàticament en pressionar una tecla - + Assign Key Assignar Tecla - + Hit the key combination that you wish to assign. You may hit either a single key or a combination using %1, %2, and %3. Premeu la combinació de tecles que voleu assignar. Podeu prémer una sola tecla o una combinació usant%1,%2 o%3. @@ -1045,23 +1045,23 @@ L'arxiu seleccionat s'ha mogut o esborrat. AudioItem - - + + Preview Left Channel Vista prèvia del Canal Esquerra - + Preview Right Channel Vista Prèvia del Canal Dret - + Preview Stereo Channels Vista Prèvia Canals Estèreo - + Preview Mono Vista Prèvia Mono @@ -1129,52 +1129,52 @@ L'arxiu seleccionat s'ha mogut o esborrat. Entrada - + None Cap - + DMX DMX - + Function Funció - + VC Widget Widget de CV - + %1 channels %1 canals - + No function Cap funció - + No widget Cap widget - + Not assigned No assignat - + Volume Bar Barra de volum - + #%1 (%2Hz - %3Hz) #%1 (%2Hz - %3Hz) @@ -1232,12 +1232,12 @@ L'arxiu seleccionat s'ha mogut o esborrat. Desestablir Modificador - + Error Error - + You are trying to overwrite a system template! Please choose another name and the template will be saved in your channel modifier's user folder. Estau intentant sobreescriure una plantilla del sistema ! Si us plau escolliu un altre nom i la plantilla es desarà al directori de modificadors de canal del usuari. @@ -1251,13 +1251,13 @@ L'arxiu seleccionat s'ha mogut o esborrat. - + Name Nom - + Type Tipus @@ -1277,27 +1277,27 @@ L'arxiu seleccionat s'ha mogut o esborrat. Expandeix-ho tot - + Selected Seleccionats - + Channel properties configuration Configuració de propietats de canals - + Can fade Pot fer Fade - + Behaviour Comportament - + Modifier Modificador @@ -1321,19 +1321,19 @@ L'arxiu seleccionat s'ha mogut o esborrat. - + Fade In Fade in - + Hold Manté - + Fade Out Fade Out @@ -1553,47 +1553,47 @@ L'arxiu seleccionat s'ha mogut o esborrat. Barreja els passos - + Cut Tallar - + Copy Copiar - + Paste Enganxa - + Paste error Error d'enganxat - + Trying to paste on an incompatible Scene. Operation canceled. Intentant enganxar una escena incompatible. Operació cancelada. - + Common Fade In Temps de Fade In comú - + Common Fade Out Temps de Fade Out comú - + Common Hold Temps d'espera comú - + Multiple Steps Passos múltiples @@ -1649,12 +1649,12 @@ L'arxiu seleccionat s'ha mogut o esborrat. ConsoleChannel - + Intensity Intensitat - + Reset this channel Restaurar aquest canal @@ -1715,6 +1715,83 @@ L'arxiu seleccionat s'ha mogut o esborrat. Peu + + CustomFeedbackDialog + + + Custom Feedback Configuration + + + + + Value + Valor + + + + Label + Etiqueta + + + + Color + + + + + Values + Valors + + + + Lower Value + + + + + Monitor Value + + + + + Upper Value + + + + + + + Color Selection + + + + + MIDI Channel + + + + + Upper Channel + + + + + Lower Channel + + + + + Monitor Channel + + + + + + + From plugin settings + + + DmxDumpFactory @@ -1773,49 +1850,16 @@ L'arxiu seleccionat s'ha mogut o esborrat. Bolcar canals seleccionats - + Dump all channels (%1 Universes, %2 Fixtures, %3 Channels) Bolcar tots els canals (%1 Universos, %2Fixtures, %3 Canals) - + New Scene From Live %1 Nova Escena des de Live %1 - - DocBrowser - - - %1 - Document Browser - %1 - Explorador de Documents - - - - Backward - Enrere - - - - Forward - Endavant - - - - Index - Index - - - - About Qt - Sobre Qt - - - - Close this window - Tanca aquesta finestra - - EFXEditor @@ -2097,12 +2141,12 @@ L'arxiu seleccionat s'ha mogut o esborrat. Nom de la funció que s'està editant - + Remove fixtures Treure fixtures - + Do you want to remove the selected fixture(s)? Vol treure els fixtures seleccionats? @@ -2444,88 +2488,88 @@ L'arxiu seleccionat s'ha mogut o esborrat. Reassignar nom de fixtures - - + + (remapped) (reassignat) - + Import Fixtures List Importa la llista de Fixtures - + Fixtures List (*%1) Llista de Fixtures (*%1) - + All Files (*.*) Tots els arxius (*.*) - + All Files (*) Tots els arxius (*) - + Do you want to automatically connect fixtures with the same name? Voleu connectar automàticament els fixtures amb el mateix nom? - + Generic Dimmer Dimmer genèric - + Delete Fixtures Esborrar Fixtures - + Do you want to delete the selected items? Vol esborrar els ítems seleccionats? - + Invalid operation Operació invàlida - + You are trying to clone a fixture on an address already in use. Please fix the target list first. Està tractant de clonar un fixture a una adreça en us. Si us plau arregli la llista de destinació primer. - - - - + + + + Invalid selection Selecció invàlida - - - + + + Please select a source and a target fixture or channel to perform this operation. Si us plau, seleccioni un fixture o canal d'origen i destinació per fer aquesta operació. - + To perform a fixture remap, please select fixtures on both lists. Per fer la reassignació de fixtures, si us plau seleccioni fixtures a ambdues llistes. - + This might take a while... Això pot trigar una estona... - + Cancel Cancelar @@ -2538,12 +2582,12 @@ L'arxiu seleccionat s'ha mogut o esborrat. Seleccioni un fixture - + No fixtures available No hi ha fixtures disponibles - + Go to the Fixture Manager and add some fixtures first. Anar al Gestor de Fixtures i afegir algun fixture primer. @@ -2600,7 +2644,7 @@ L'arxiu seleccionat s'ha mogut o esborrat. FunctionLiveEditDialog - + Function Live Edit Editar la funció en viu @@ -2608,194 +2652,194 @@ L'arxiu seleccionat s'ha mogut o esborrat. FunctionManager - + New &scene Nova e&scena - + New c&haser Nou c&haser - + New se&quence Nova se&qüència - + New c&ollection Nova c&ol·lecció - + New E&FX Nou E&FX - + New &RGB Matrix nova Matriu &RGB - + New scrip&t Nou script&t - + New au&dio Nou au&dio - + New vid&eo Nou &vídeo - + New fo&lder Nova Car&peta - + Select Startup Function Seleccioni Funció d'arranc - + Function &Wizard &Assistent de Funcions - + &Clone &Clonar - + &Delete &Esborrar - + Select &all Seleccionar &tot - + New Scene Nova Escena - + New Chaser Nou Chaser - + New Sequence Nova Seqüència - + New Collection Nova Col·lecció - + New EFX Nou EFX - + New RGB Matrix Nova Matriu RGB - + New Script Nou Script - + Open Audio File Obrir arxiu d'Audio - + Audio Files (%1) Arxius d'Audio (%1) - - + + All Files (*.*) Tots els arxius (*.*) - - + + All Files (*) Tots els arxius (*) - + Unsupported audio file Arxiu d'audio no suportat - + This audio file cannot be played with QLC+. Sorry. Aquest arxiu d'audio no es pot ser reproduir amb QLC+. Ho sento. - + Open Video File Obrir Arxiu de Vídeo - + Video Files (%1) Arxius de Vídeo (%1) - + Unsupported video file Arxiu de vídeo no suportat - + This video file cannot be played with QLC+. Sorry. Aquest vídeo no es pot reproduir amb QLC+. Ho sento. - + Do you want to DELETE folder: Vol ESBORRAR la carpeta: - + Do you want to DELETE functions: Vol ESBORRAR funcions: - + (This will also DELETE: (Això ESBORRARÀ també: - + Delete Functions Esborrar funcions - + Function Funció - + (Copy) (Copiar) @@ -3080,32 +3124,32 @@ p, li { white-space: pre-wrap; } Widgets - + %1 group %1 grup - + Error Error - + %1 has no capability supported by this wizard. %1 no te les capacitats suportades per aquest assistent. - + Presets solo frame Marc de solo presets - + Click & Go RGB RGB Click & Go - + Click & Go Macro Macro Click & Go @@ -3113,27 +3157,27 @@ p, li { white-space: pre-wrap; } GrandMasterSlider - + GM GM - + Grand Master <B>limits</B> the maximum value of Gran Master <B>limita</B> el valor màxim de - + Grand Master <B>reduces</B> the current value of Gran Master <B>redueix</B> el valor actual de - + intensity channels canals d'intensitat - + all channels tor els canals @@ -3234,45 +3278,45 @@ p, li { white-space: pre-wrap; } InputOutputManager - + Add U&niverse Afegir U&nivers - + &Delete Universe &Esborra Univers - + Universe name: Nom del Univers: - + Passthrough pas a través, de moment deix Passthrough Passthrough - - + + Universe %1 Univers %1 - - + + Delete Universe Esborrar Univers - + The universe you are trying to delete is patched. Are you sure you want to delete it? L'univers que està intentant esborrar està assignat. Estau segur de voler esborrar-lo? - + There are some fixtures using the universe you are trying to delete. Are you sure you want to delete it? Alguns fixtures estan emprant l'univers que està intentant esborrar. Està segur de voler-lo esborrar? @@ -3381,20 +3425,20 @@ p, li { white-space: pre-wrap; } Monitor de nivell - - - + + + Error Error - - + + Output line already assigned Línia de Sortida ja assignada - + An error occurred while trying to open the selected device line. This can be caused either by a wrong system configuration or an unsupported input/output mode. Please refer to the plugins documentation to troubleshoot this. @@ -3403,67 +3447,67 @@ Això pot ser degut a una configuració errònea del sistema o a un modus d&apos Si us plau, consulteu la documentació dels plugins per solucionar això. - - + + Existing Input Profile perfil d'Entrada existent - - + + An input profile at %1 already exists. Do you wish to overwrite it? El perfil d'Entrada en %1 ja existeix. El voleu sobreescriure? - - + + Save Input Profile Desar el perfil d'Entrada - - + + Input Profiles (*.qxi) Perfil d'Entrada (*.qxi) - - + + Saving failed Error al Desar - + Unable to save the profile to %1 Impossible desar el perfil a %1 - + Delete profile Esborrar perfil - + Do you wish to permanently delete profile "%1"? Voleu esborar permanentment aquest perfil "%1"? - + File deletion failed Error al eliminar l'arxiu - + Unable to delete file %1 Impossible esborrar l'arxiu %1 - + Unable to save %1 to %2 Impossible desar %1 a %2 - + Default device Dispositu per defecte @@ -3476,188 +3520,272 @@ Si us plau, consulteu la documentació dels plugins per solucionar això.Editor de Perfil d'Entrada - + General General - + The name of the company that made the device Nom de la companyia - + The device's model name Nom del model del dispositiu - + Manufacturer Fabricant - + Model Model - - + + Type Tipus - + MIDI Global Settings Ajustos Globals de MIDI - + When MIDI notes are used, send a Note Off when value is 0 Quan s'utilitzen les notes MIDI, envieu Note Off quan el valor és 0 - - Channels - Canals - - - + + Channel Canal - + + Name Nom - + Custom feedback Feedback personalitzat - + Upper value Valor superior - + Lower value Valor inferior - - + Behaviour Comportament - + Add a new channel description Afegir una nova descripció per el canal - + Remove the selected channels Eliminar els canals seleccionats - + Edit the selected channel Editar el canal selecionat - + Automatically add channels to the list when you wiggle the device's controls Afegir automàticament canals a la llista cuant mogui els controls del dispositiu - + + Input Mapping + + + + Movement Moviment - + Absolute Absolut - + Relative Relatiu - + Generate an extra Press/Release when toggled Generar una ordre addicional de Pressionar / Deixar anar Quan S'activa - + + + MIDI channel + + + + + Colors + + + + + Remove the selected color + + + + + Add a new color + + + + + Value + Valor + + + + Label + Etiqueta + + + + Color + + + + + MIDI Channels + + + + + Add a new MIDI channel + + + + + Remove the selected MIDI channel + + + + Sensitivity Sensibilitat - + File not writable Arxiu només de lectura - + You do not have permission to write to the file %1. You might not be able to save your modifications to the profile. No teniu permissos per escriure al arxiu %1. Pot se que no sigui possible desar les modificacions al perfil. - + + From plugin settings + + + + Missing information Falta informació - + Manufacturer and/or model name is missing. Falta el nom del fabricant o model. - - + + Channel already exists El canal ja existeix - - + + Channel %1 already exists El Canal %1 ja existeix - + Delete channels Esborrar canals - + Delete all %1 selected channels? Esborrar els %1 canals seleccionats? - + 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. Note that the wizard cannot tell the difference between a knob and a slider so you will have to do the change manually. Teniu activat l'assistent de canal d'Entrada. Després de prema OK mogui els controls mapejats del seu perfil d'Entrada. Apareixeran a la llista. Premi de nou el botó de l'assistent per atura l'autodetecció de canals. - + + + Enter value + + + + + Feedback value + + + + + + Enter label + + + + + Color label + + + + + MIDI channel label + + + + Button %1 Botó %1 - + Slider %1 Slider %1 @@ -3690,65 +3818,50 @@ Note that the wizard cannot tell the difference between a knob and a slider so y Entrada Externa - + When toggled, you can click an external button to assign it to this widget. Si està actiu, pot pressionar un botó extern per assignar a aquest widget. - + Auto Detect Detectar Automàticament - + Input Universe Univers d'Entrada - + Input Channel Canal d'Entrada - + The input universe that sends data to this widget L'univers d'entrada que envia dades a aquest widget - + Custom Feedback Feedback personalitzat - + The particular input channel within the input universe that sends data to this widget El canal d'Entrada dins aquest univers d'Entrada que envia dades a aquest widget - + Choose an external input universe & channel that this widget should listen to. Triar l'univers i el canal d'Entrada que aquest grup ha d'escoltar. - + Choose... Triar... - - - Custom feedback - Feedback personalitzat - - - - Lower value - Valor inferior - - - - Upper value - Valor superior - Monitor @@ -3905,14 +4018,14 @@ Note that the wizard cannot tell the difference between a knob and a slider so y Fons - - + + Select background image Seleccioni la imatge de fons - - + + Images Imatges @@ -4200,12 +4313,12 @@ Note that the wizard cannot tell the difference between a knob and a slider so y PlaybackSlider - + Select Seleccionar - + Flash Flash @@ -4221,35 +4334,30 @@ Note that the wizard cannot tell the difference between a knob and a slider so y QObject - + Operate Funcionar tal vegada sería millor en aquest cas. Operació - + Design Disseny - - + + Reversed Invertit - + Page: %1 Pàgina: %1 RDMManager - - - Form - Formulari - Scan for RDM devices... @@ -4670,22 +4778,22 @@ Note that the wizard cannot tell the difference between a knob and a slider so y Cap - + No fixture group to control Cap grup per controlar - + Select image Seleccionar imatge - + Images Imatges - + Sequence Seqüències @@ -4746,106 +4854,106 @@ Note that the wizard cannot tell the difference between a knob and a slider so y Activar tots els canals de totos els fixtures - + Enable all channels in current fixture Activar tots els canals de aquest fixture - + Disable all channels in current fixture Desactivar tots els canals d'aquest fixture - + Copy current values to clipboard Copiar els valors actuals al porta-retalls - + Paste clipboard values to current fixture Enganxar els valors del porta-retalls al fixture actual - + Copy current values to all fixtures Copiar els valors actuals a tots els fixtures - + Color tool for CMY/RGB-capable fixtures Eina de color per fixtures amb capacitats CMY/RGB - + Position tool for moving heads/scanners Eina de posicionament per a capçals mòbils o scanners - + Switch between tab view and all channels view Canviar de la vista de pestanyes a la vista de canals - + Toggle blind mode Blind es Cec deix Blind de moment Activar/Desactivar Mode Blind - + Show/Hide speed dial window Mostra/Amaga la finestra del selector de velocitat - + Clone this scene and append as a new step to the selected chaser Clonar aquesta escena i afegir-la com un nou pas al chaser seleccionat - + Go to next fixture tab Anar a la pestanya següent - + Go to previous fixture tab Anar a la pestanya anterior - + None Cap - + Scene name: Nom de la Escena: + - All fixtures Tots els fixtures + - Generic Genèric - + Remove fixtures Eliminar fixtures - + Do you want to remove the selected fixture(s)? Vol eliminar el(s) fixture(s) seleccionats? + - Channels Groups Grups de Canals @@ -5059,7 +5167,7 @@ Note that the wizard cannot tell the difference between a knob and a slider so y Permet universos sense apedaçar - + <Double click here to enter channel number manually> <Faixi doble click aquí per ingressar manualment el número del canal> @@ -5365,119 +5473,119 @@ Durada: %3 SimpleDesk - + View mode Mode Vista - + Previous page Pàgina Anterior - + Current page Pàgina Actual - + Next page Pàgina Següent - + Reset universe Reiniciar Univers - + Universe Univers - - + + Cue Stack Cue Stack - + Playback Reproduir - + Previous cue Cue Anterior - + Stop cue stack Atura cue stack - + Next cue Següent Cue - + Clone cue stack Clonar cue stack - + Edit cue stack Editar Cue Stack - + Record cue Pot ser Record aquí por voler dir gravar????? Desar Cue - + Channel groups Grups de Canals - + Cue Stack - Playback %1 Cue Stack - Playback %1 - + No selection Cap selecció - + Cue name Nom del Cue - + Multiple Cues Múltiples Cues - + Delete cue Esborrar Cue - + Clone Cue Stack Clonar Cue Stack - + Clone To Playback# Clonar a Playback# - + Cue %1 Cue %1 @@ -5485,32 +5593,32 @@ Durada: %3 SpeedDial - + Hours Hores - + Minutes Minuts - + Seconds Segons - + Milliseconds Milisegons - + Infinite Infinit - + Tap Tap @@ -5518,17 +5626,17 @@ Durada: %3 SpeedDialWidget - + Fade In Fade In - + Fade Out Fade Out - + Hold Espera @@ -5618,17 +5726,17 @@ Durada: %3 VCButton - + Choose... Triar... - + None Cap - + Button %1 Botó %1 @@ -5643,17 +5751,17 @@ Durada: %3 Imatges (%1) - + Toggle Blackout Activar/Desactivar Blackout - + Stop ALL functions! Aturar TOTES les funcions! - + Icon Icona @@ -5681,82 +5789,92 @@ Durada: %3 Mode Interruptor on/off - + Flash the assigned function with this button Activa la funció assignada mentre el botó estigui premut - + Flash function (only for scenes) Mode Flash (només per escenes) - + + Override priority + + + + + Force LTP + + + + Toggle Blackout Blackout On/Off - + Stop All Functions Atura Totes les Funcions - + Fade time: Durada de Fade: - + Adjust function intensity when it is running Ajusta la intensitat de la funció quant està en execució - + Adjust Function Intensity Ajustar la intensitat de la funció - + Function's adjusted intensity percentage when run Percentatge d'intensitat pre-ajustat de la funció quant s'executa - + General General - + Button label Etiqueta del Botó - + Text to display on the button Texte a mostrar al botó - + Function Funció - + The function that this button controls La funció que es controlada per aquest botó - + Attach a function to this button Enllaçar una funció a aquest botó - + Detach the button's function attachment Desenllaçar la funció del botó - + No function Cap funció @@ -5847,64 +5965,64 @@ Durada: %3 VCCueList - + Show/Hide crossfade sliders Motrar/Amagar els sliders de crossfade - - + + Play/Pause Cue list Reprodueix/Pausa Cue List - - + + Stop Cue list Atura Cue list - + Go to previous step in the list Anar al pas anterior de la llista - + Go to next step in the list Anar al pas seqüent de la llista - + Cue list Lliste de Cues - + Play/Stop Cue list Reproduir/Aturar Llista de Cues - + Pause Cue list Pausar Llista de Cues - + Fade In Fade In - + Fade Out Fade Out - + Duration Durada - + Notes Notes @@ -6069,7 +6187,7 @@ Durada: %3 VCFrame - + Add Afegir @@ -6122,17 +6240,17 @@ Durada: %3 Nom de la pàgina - + External Input - Enable Activar entrada externa - + External Input - Previous Page Entrada Externa - Pàgina Anterior - + External Input - Next Page Entrada Externa - Pàgina Següent @@ -6165,17 +6283,17 @@ Durada: %3 VCLabel - + Label Etiqueta - + Rename Label Reanomenar Etiqueta - + Caption: Títol: @@ -6183,42 +6301,42 @@ Durada: %3 VCMatrix - + Animation %1 Animació %1 - + End Color Reset Restablir el Color Final - + Start color Red component Component Vermell del color inicial - + Start color Green component Component Verd del color inicial - + Start color Blue component Component Blau del color inicial - + End color Red component Component Vermell del color final - + End color Green component Component Verd del color final - + End color Blue component Component Blau del color final @@ -6421,48 +6539,48 @@ Durada: %3 Afegir text - + No function Cap funció - + Start Color Color d'inici - + Start Color Knob Perilla color inicial - + End Color Color de final - + End Color Knob Perilla color final - + End Color Reset Restablir color final - + Animation Animació - - + + Text Text - + Enter a text Entri un text @@ -6713,12 +6831,12 @@ Durada: %3 VCSlider - + Slider %1 Slider %1 - + Reset channels override Restaurar l'override de canals @@ -6736,72 +6854,72 @@ Durada: %3 General - + Value display style Estil de Visualització dels Valors - + Show exact DMX values Mostrar el valor DMX exacte - + Actual Actual - + Show value as percentage Mostrar el valor com a percentatge - + Percentage Percentatge - + Widget name Nom del widget - + Name of the slider Nom del slider - + Slider movement Moviment del slider - + Normal Normal - + Inverted Invertit - + Widget appearance Aparença del widget - + Slider Slider - + Knob Perilla - + Catch up with the external controller input value Actualitzar amb el valor de l'entrada del controlador extern @@ -7269,17 +7387,17 @@ Durada: %3 Mostra el camp milisegons - + Multiply by 2 Input Entrada del Multiplicador per 2 - + Divide by 2 Input Entrada del DIvisor per 2 - + Factor Reset Input Entrada de Reinici de Factor @@ -7287,68 +7405,68 @@ Durada: %3 VCWidget - + Button Botó - + Slider Slider - + XYPad XY Pad - + Frame Marc - + Solo frame Marc Solo - + Speed dial Selector de Velocitat - + Cue list Llista de Cues - + Label Etiqueta - + Audio Triggers Disparador d'Àudio - + Animation Animació - + Clock Rellotge + - Unknown Desconegut - + This widget has no properties Aquest widget no te propietats @@ -7546,12 +7664,12 @@ Durada: %3 Nom del preset - + Pan / Horizontal Axis Pan / Eix horitzontal - + Tilt / Vertical Axis Tilt / Eix vertical @@ -7581,45 +7699,45 @@ Durada: %3 Invertit - + Width Ample - + Height Altura - + Remove fixtures Treure fixtures - + Do you want to remove the selected fixtures? Vol eliminar el fixture seleccionat? - - + + Error Error - + The selected Scene does not include any Pan or Tilt channel. Please select one with such channels. La Escena seleccionada no incloeix cap canal de Pan o Tilt. Si us plau seleccionau un d'aquests canals. - + Please select at least one fixture or head to create this type of preset! Si us plau, seleccioneu al menys un fixture o capçal per crear aquest tipus de preset ! - + Fixture Group Grup de Fixtures @@ -7752,12 +7870,12 @@ Si us plau seleccionau un d'aquests canals. VideoItem - + Fullscreen Pantalla completa - + Screen %1 Pantalla %1 diff --git a/ui/src/qlcplus_cz_CZ.ts b/ui/src/qlcplus_cz_CZ.ts index 8b527f636c..4a56a38a7b 100644 --- a/ui/src/qlcplus_cz_CZ.ts +++ b/ui/src/qlcplus_cz_CZ.ts @@ -188,12 +188,12 @@ Model zařízení - + Fixtures found: %1 Počet nalezených šablon zařízení: %1 - + Dimmers Dimmery Stmívače @@ -525,374 +525,374 @@ App - + Fixtures Zařízení - + Functions Funkce - + Shows Představení - + Virtual Console Virtuální pracoviště - + Simple Desk Jednoduchý pult - + Inputs/Outputs Vstupy/výstupy - + Cannot exit in Operate mode Nelze ukončit v režimu Provoz - + You must switch back to Design mode to close the application. Pro uzavření aplikace se nejprve musíte se přepnout zpět do režimu Návrhu. - + Close Zavřít - + Do you wish to save the current workspace before closing the application? Přejete si uložit rozdělanou práci před ukončením aplikace? - + Close the application? Zavřít aplikaci? - + Do you wish to close the application? Opravdu si přejete uzavřít aplikaci? - + Starting Q Light Controller Plus Startuje se Q Light Controller Plus - + - New Workspace - Nové pracoviště - + Exit Zavřít - + Switch to Design Mode Přepnout do režimu Návrhu - + There are still running functions. Really stop them and switch back to Design mode? Některé funkce stále běží. Opravdu si přejete běžící funkce zastavit a přejít zpět do režimu Návrhu? - + Design Návrh - + Switch to design mode Přepnout do režimu Návrhu - + Operate Provoz - - + + Switch to operate mode Přepnout do režimu Provoz - + &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... - + &Operate &Provoz - + CTRL+F12 Control|Toggle operate/design mode CTRL+F12 - + &Monitor &Monitor - + CTRL+M Control|Monitor CTRL+M - + Address Tool Pomocník adresace - + Toggle &Blackout Režim &Blackout - + Live edit a function Live editace funkce - + Toggle Virtual Console Live edit Zapni/vypni úpravy Virtuálního pracoviště - + Dump DMX values to a function Zachytit DMX hodnoty do funkce - + CTRL+D Control|Dump DMX CTRL+D - + Stop ALL functions! Zastavit VŠECHNY funkce! - + Fade 1 second and stop Zeslabení 1 sekundu a poté zastavit - + Fade 5 seconds and stop Zeslabení 5 sekund a poté zastavit - + Fade 10 second and stop Zeslabení 10 sekund a poté zastavit - + Fade 30 second and stop Zeslabení 30 sekund a poté zastavit - + Toggle Full Screen Přepnout na celou obrazovku - + CTRL+F11 Control|Toggle Full Screen CTRL+F11 - + &Index &Index - + SHIFT+F1 Help|Index SHIFT+F1 - + &About QLC+ &O programu QLC+ - + Quit QLC+ Ukončit QLC+ - + Workspace Pracoviště - + Unable to read from file Ze souboru nelze číst - + Unable to write to file Do souboru nelze zapisovat - + A fatal error occurred Nastala kritická chyba - + Unable to access resource K tomuto zdroji se nepodařilo získat přístup - + Unable to open file for reading or writing Nelze otevřít soubor pro čtení nebo zápis - + Operation was aborted Operace byla zrušena - + Operation timed out Časový limit operace vypršel - + An unspecified error has occurred. Nice. No, nastala neočekávaná chyba. Co dodat, mrzí nás to. - + File error Chyba souboru - - - + + + Do you wish to save the current workspace? Changes will be lost if you don't save them. Přejete si uložit rozdělanou práci? Veškeré změny budou zahozeny, pokud je teď neuložíte. - + New Workspace Nové Pracoviště - - - + + + Open Workspace Otevřít Pracoviště - - + + Workspaces (*%1) Pracoviště (*%1) - - + + All Files (*.*) Všechny soubory (*.*) - - + + All Files (*) Všechny soubory (*) - + Save Workspace As Uložit Pracoviště jako - + Error Chyba - + File not found! The selected file has been moved or deleted. Soubor nebyl nalezen! Zvolený soubor byl asi smazán nebo přesunut. - + Warning Výstraha - + Some errors occurred while loading the project: Nastaly nejaké problémy při otevírání projektu: @@ -915,12 +915,12 @@ Zvolený soubor byl asi smazán nebo přesunut. Automaticky zavřít po stisku klávesy - + Assign Key Přiřadit klávesu - + Hit the key combination that you wish to assign. You may hit either a single key or a combination using %1, %2, and %3. Stiskněte kombinaci kláves, kterou chcete přiřadit. Můžete stisknout jen jednu klávesu nebo kombinaci více kláves %1, %2, a %3. @@ -1047,23 +1047,23 @@ Zvolený soubor byl asi smazán nebo přesunut. AudioItem - - + + Preview Left Channel Ukázka levého kanálu - + Preview Right Channel Ukázka pravého kanálu - + Preview Stereo Channels Ukázka v režimu stereo - + Preview Mono Ukázka v režimu mono @@ -1131,52 +1131,52 @@ Zvolený soubor byl asi smazán nebo přesunut. Vstup - + None Nic - + DMX DMX - + Function Funkce - + VC Widget Ovladač Virtuálního pracoviště - + %1 channels %1 kanálů - + No function Bez funkce - + No widget Bez ovladače - + Not assigned Nepřiřazeno - + Volume Bar Regulace hlasitosti - + #%1 (%2Hz - %3Hz) #%1 (%2Hz - %3Hz) @@ -1234,12 +1234,12 @@ Zvolený soubor byl asi smazán nebo přesunut. - + Error Chyba - + You are trying to overwrite a system template! Please choose another name and the template will be saved in your channel modifier's user folder. Pokoušíte se přepsat systémovou šablonu! Zvolte prosím jiný název a šablona bude uložena do Vaší složky modifikací a změn kanálů. @@ -1254,13 +1254,13 @@ Zvolený soubor byl asi smazán nebo přesunut. - + Name Název - + Type Typ @@ -1280,27 +1280,27 @@ Zvolený soubor byl asi smazán nebo přesunut. - + Selected Označen - + Channel properties configuration Konfigurace vlastností kanálu - + Can fade Může být zeslaben - + Behaviour Chování - + Modifier Modifikátor @@ -1324,19 +1324,19 @@ Zvolený soubor byl asi smazán nebo přesunut. - + Fade In Zesílení - + Hold Podržet - + Fade Out Zeslabení @@ -1556,47 +1556,47 @@ Zvolený soubor byl asi smazán nebo přesunut. - + Cut Vyjmout - + Copy Kopírovat - + Paste Vložit - + Paste error Chyba vložení - + Trying to paste on an incompatible Scene. Operation canceled. Pokoušíte se vložit nekompatibilní Scénu. Operace byla zrušena. - + Common Fade In Společné zesílení - + Common Fade Out Společné zeslabení - + Common Hold Společné pozastavení - + Multiple Steps Kroky @@ -1652,12 +1652,12 @@ Zvolený soubor byl asi smazán nebo přesunut. ConsoleChannel - + Intensity Intenzita - + Reset this channel Uvolnit tenhle kanál @@ -1718,6 +1718,83 @@ Zvolený soubor byl asi smazán nebo přesunut. Střih + + CustomFeedbackDialog + + + Custom Feedback Configuration + + + + + Value + Hodnota + + + + Label + Popisek + + + + Color + + + + + Values + Hodnoty + + + + Lower Value + + + + + Monitor Value + + + + + Upper Value + + + + + + + Color Selection + + + + + MIDI Channel + + + + + Upper Channel + + + + + Lower Channel + + + + + Monitor Channel + + + + + + + From plugin settings + + + DmxDumpFactory @@ -1776,49 +1853,16 @@ Zvolený soubor byl asi smazán nebo přesunut. Název Scény: - + Dump all channels (%1 Universes, %2 Fixtures, %3 Channels) Zachytit všechny kanály (%1 Větve, %2 Zařízení, %3 Kanály) - + New Scene From Live %1 Nová scéna z Provozu %1 - - DocBrowser - - - %1 - Document Browser - %1 - Prohlížení dokumentu - - - - Backward - Od konce - - - - Forward - Od začátku - - - - Index - Obsah - - - - About Qt - O aplikaci Qt - - - - Close this window - Zavřít tohle okno - - EFXEditor @@ -2099,12 +2143,12 @@ Zvolený soubor byl asi smazán nebo přesunut. Ukázat co EFX efekt udělá, když poběží - + Remove fixtures Odebrat zařízení - + Do you want to remove the selected fixture(s)? Opravdu si přejete odebrat označená zařízení? @@ -2446,88 +2490,88 @@ Zvolený soubor byl asi smazán nebo přesunut. Přesunout názvy zařízení - - + + (remapped) (přesunuto) - + Import Fixtures List Importovat seznam zařízení - + Fixtures List (*%1) Seznam zařízení (*%1) - + All Files (*.*) Všechny soubory (*.*) - + All Files (*) Všechny soubory (*) - + Do you want to automatically connect fixtures with the same name? - + Generic Dimmer Obecný stmívač - + Delete Fixtures Smazat zařízení - + Do you want to delete the selected items? Opravdu si přejete SMAZAT označená zařízení? - + Invalid operation Neplatná operace - + You are trying to clone a fixture on an address already in use. Please fix the target list first. Snažíte se duplikovat zařízení na adresu, která je už použita. Prosím, nejprve upravte cílový seznam. - - - - + + + + Invalid selection Chybná volba - - - + + + Please select a source and a target fixture or channel to perform this operation. Prosím zvolte zdrojové a cílové zařízení nebo kanál k provedení této operace. - + To perform a fixture remap, please select fixtures on both lists. K provedení přesunu zařízení, prosím zvolte zařízení v obou seznamech. - + This might take a while... Tato operace může nějakou chvilku trvat... - + Cancel Zrušit @@ -2540,12 +2584,12 @@ Zvolený soubor byl asi smazán nebo přesunut. Zvolit zařízení - + No fixtures available Žádná dostupná zařízení - + Go to the Fixture Manager and add some fixtures first. Nejprve přidejte zařízení v panelu Zařízení. @@ -2602,7 +2646,7 @@ Zvolený soubor byl asi smazán nebo přesunut. FunctionLiveEditDialog - + Function Live Edit Live editace funkce @@ -2610,195 +2654,195 @@ Zvolený soubor byl asi smazán nebo přesunut. FunctionManager - + New &scene Nová Scéna (CTRL+&s) - + New c&haser Nové Prolínání (CTRL+&h) - + New se&quence Nová Sekvence (CTRL+&q) - + New c&ollection Nová Kolekce(CTRL+&o) - + New E&FX Nový EFX(CTRL+&f) - + New &RGB Matrix Nová RGB šablona (CTRL+&R) - + New scrip&t Nový skript (CTRL+&t) - + New au&dio Nový zvuk (CTRL+&d) - + New vid&eo Nové vid&eo - + New fo&lder Nová s&ložka - + Select Startup Function Volba funkce při spuštění - + Function &Wizard Tvorba funkcí (CTRL+&W) - + &Clone Duplikovat (CTRL+&C) - + &Delete Smazat (CTRL+&D) - + Select &all Označit vše (CTRL+&a) - + New Scene Nová Scéna - + New Chaser Nové Prolínání - + New Sequence Nová Sekvence - + New Collection Nová Kolekce - + New EFX Nový EFX - + New RGB Matrix Nový RGB šablona - + New Script Nový Skript - + Open Audio File Otevřít soubor zvuku - + Audio Files (%1) Zvukové soubory (%1) - - + + All Files (*.*) Všechny soubory (*.*) - - + + All Files (*) Všechny soubory (*) - + Unsupported audio file Nepodporovaný soubor zvuku - + This audio file cannot be played with QLC+. Sorry. Tento soubor zvuku nemůže být přehrán v QLC+. Je nám to moc líto. - + Open Video File Otevřít soubor videa - + Video Files (%1) Video soubory (%1) - + Unsupported video file Nepodporovaný soubor videa - + This video file cannot be played with QLC+. Sorry. Tento soubor videa nemůže být přehrán v QLC+. Je nám to moc líto. - + Do you want to DELETE folder: Do you want to DELETE foler: Opravdu si přejete si SMAZAT složku: - + Do you want to DELETE functions: Opravdu si přejete SMAZAT funkce: - + (This will also DELETE: Tato operace také VYMAŽE: - + Delete Functions Smazat funkce - + Function Funkce - + (Copy) (kopie) @@ -3085,32 +3129,32 @@ p, li { white-space: pre-wrap; } Odebrat - + %1 group skupina %1 - + Error Chyba - + %1 has no capability supported by this wizard. %1 nemá funkce podporované tímto průvodcem. - + Presets solo frame Přednastavení samostatného rámečku - + Click & Go RGB Rychlá volba RGB - + Click & Go Macro Rychlá volba Makro @@ -3118,27 +3162,27 @@ p, li { white-space: pre-wrap; } GrandMasterSlider - + GM GM - + Grand Master <B>limits</B> the maximum value of GM -Hlavní šavle <B>nastaví</B> omezení maximální úrovně - + Grand Master <B>reduces</B> the current value of GM -Hlavní šavle <B>reguluje</B> aktuální úroveň - + intensity channels intenzity kanálů - + all channels všech kanálů @@ -3239,45 +3283,45 @@ p, li { white-space: pre-wrap; } InputOutputManager - + Add U&niverse Přidat větev - + &Delete Universe Universe Smazat větev - + Universe name: Název větve: - + Passthrough Průhozí - - + + Universe %1 Větev %1 - - + + Delete Universe Zmazat větev - + The universe you are trying to delete is patched. Are you sure you want to delete it? Vetvev, kterou se snažíte smazat je propojená. Opravdu si ji přejete smazat? - + There are some fixtures using the universe you are trying to delete. Are you sure you want to delete it? Některá zařízení používají větev, kterou se pokoušíte smazat. Opravdu ji chcete smazat? @@ -3384,20 +3428,20 @@ p, li { white-space: pre-wrap; } Monitor hlasitosti - - - + + + Error Chyba - - + + Output line already assigned Výstupní linka již byla přiřazena - + An error occurred while trying to open the selected device line. This can be caused either by a wrong system configuration or an unsupported input/output mode. Please refer to the plugins documentation to troubleshoot this. @@ -3406,67 +3450,67 @@ To může být způsobeno nesprávnou konfigurací systému nebo nepodporovaným Použijte nápovědu k pluginu (zásuvnému modulu) pro vyřešení tohoto problému. - - + + Existing Input Profile Existující profil vstupu - - + + An input profile at %1 already exists. Do you wish to overwrite it? Profil vstupu na %1 již existuje. Opravdu si přejete jej přepsat a nahradit? - - + + Save Input Profile Uložit profil vstupu - - + + Input Profiles (*.qxi) Profily vstupu (*.qxi) - - + + Saving failed Uložení se nezdařilo - + Unable to save the profile to %1 Nelze uložit profil do %1 - + Delete profile Smazat profil - + Do you wish to permanently delete profile "%1"? Opravdu si přejete nenávratně SMAZAT profil "%1"? - + File deletion failed Smazání souboru se nezdařilo - + Unable to delete file %1 Nelze smazat soubor %1 - + Unable to save %1 to %2 Nelze uložit %1 do %2 - + Default device Standardní zařízení @@ -3479,188 +3523,272 @@ Použijte nápovědu k pluginu (zásuvnému modulu) pro vyřešení tohoto probl Editor profilu vstupu - + General Obecné - + The name of the company that made the device Název výrobce zařízení - + The device's model name Název modelu zařízení - + Manufacturer Výrobce - + Model Model - - + + Type Typ - + MIDI Global Settings Globální MIDI nastavení - + When MIDI notes are used, send a Note Off when value is 0 Když jsou použity MIDI noty, poslat Note Off když je hodnota 0 - - Channels - Kanály - - - + + Channel Kanál - + + Name Název - + Custom feedback Vlastní odezva - + Upper value Horní hodnota - + Lower value Dolní hodnota - - + Behaviour Chování - + Add a new channel description Přidat nový popis ke kanálu - + Remove the selected channels Odebrat zvolené kanály - + Edit the selected channel Upravit zvolený kanál - + Automatically add channels to the list when you wiggle the device's controls Automaticky přidat kanály do seznamu, když spojíte ovladače zažízení - + + Input Mapping + + + + Movement Pohyb - + Absolute Absolutní - + Relative Relativní - + Generate an extra Press/Release when toggled Generovat extra stlačení/uvolnění - + + + MIDI channel + + + + + Colors + + + + + Remove the selected color + + + + + Add a new color + + + + + Value + Hodnota + + + + Label + Popisek + + + + Color + + + + + MIDI Channels + + + + + Add a new MIDI channel + + + + + Remove the selected MIDI channel + + + + Sensitivity Citlivost - + File not writable Soubor není zapisovatelný - + You do not have permission to write to the file %1. You might not be able to save your modifications to the profile. Nemáte oprávnění zapisovat do souboru %1. Možná nebudete moci uložit Vaše změny v profilu. - + + From plugin settings + + + + Missing information Chybějící informace - + Manufacturer and/or model name is missing. Výrobce a/nebo název modelu nebyl vyplněn. - - + + Channel already exists Kanál již existuje - - + + Channel %1 already exists Kanál %1 již existuje - + Delete channels Smazat kanály - + Delete all %1 selected channels? Smazat všechny %1 zvolené kanály? - + Channel wizard activated Průvodce kanálů je aktivní - + 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. Průvodce vstupních kanálů je aktivní. Po kliknutí na OK, spojíte Vámi přiřazené profily ovládání zařízení, která by se následně měla zobrazit v seznamu. Klikněte opět na tlačítko průvodce pokud chcete proces autodetekce zastavit.\n\n Mějte prosím na paměti, že průvodce nemůže rozlišit rozdíl mezi šavlí a tlačítkem a tak to budete muset později upravit ručně. - + + + Enter value + + + + + Feedback value + + + + + + Enter label + + + + + Color label + + + + + MIDI channel label + + + + Button %1 Tlačítko %1 - + Slider %1 Šavle %1 @@ -3693,65 +3821,50 @@ Note that the wizard cannot tell the difference between a knob and a slider so y Externí vstup - + When toggled, you can click an external button to assign it to this widget. Je-li aktivní, bude dostupná volba externího tlačítka pro přiřazení k řízení tohoto virtuálního tlačítka. - + Auto Detect Automaticky detekovat - + Input Universe Větev vstupu - + Input Channel Vstupní kanál - + The input universe that sends data to this widget Větev vstupů, která bude odesílat data pro tento ovladač - + Custom Feedback Vlastní odezva - + The particular input channel within the input universe that sends data to this widget Kanál větve vstupů, který bude odesílat data pro tento ovladač - + Choose an external input universe & channel that this widget should listen to. Zvolte větev externího vstupu & kanál, který by měl tento ovladač řídit. - + Choose... Zvolit... - - - Custom feedback - Vlastní odezva - - - - Lower value - Dolní hodnota - - - - Upper value - Horní hodnota - Monitor @@ -3908,14 +4021,14 @@ Note that the wizard cannot tell the difference between a knob and a slider so y Obrázek pozadí - - + + Select background image Zvolit obrázek pozadí - - + + Images Obrázky @@ -4205,12 +4318,12 @@ Note that the wizard cannot tell the difference between a knob and a slider so y PlaybackSlider - + Select Zvolit - + Flash Záblesk @@ -4226,34 +4339,29 @@ Note that the wizard cannot tell the difference between a knob and a slider so y QObject - + Operate Provoz - + Design Návrh - - + + Reversed Obrácený - + Page: %1 Strana: %1 RDMManager - - - Form - - Scan for RDM devices... @@ -4673,22 +4781,22 @@ Note that the wizard cannot tell the difference between a knob and a slider so y Nic - + No fixture group to control Žádná skupina zařízení k ovládání - + Select image Volba obrázku - + Images Obrázky - + Sequence @@ -4748,105 +4856,105 @@ Note that the wizard cannot tell the difference between a knob and a slider so y Zapnout všechny kanály zařízení - + Enable all channels in current fixture Zapnout všechny kanály v tomto zařízení - + Disable all channels in current fixture Vypnout všechny kanály v tomto zařízení - + Copy current values to clipboard Kopírovat aktuální hodnoty do schránky - + Paste clipboard values to current fixture Vložit hodnoty ze schránky do tohoto zařízení - + Copy current values to all fixtures Kopírovat aktuální hodnoty do všech zařízení - + Color tool for CMY/RGB-capable fixtures Nástroj volby barev pro typ CMY/RGB zažízení - + Position tool for moving heads/scanners Nástroj pozice pro pohyb hlav/scannerů - + Switch between tab view and all channels view Přepnout mezi zobrazením záložek a zobrazením kanálů - + Toggle blind mode Přepnout řežim ukázky - + Show/Hide speed dial window Zobrazit/Skrýt okno rychlé volby - + Clone this scene and append as a new step to the selected chaser Duplikovat tuto scénu a připojit ji jako nový krok do zvoleného prolínání - + Go to next fixture tab Přejít na další záložku zařízení - + Go to previous fixture tab Přejít na předchozí záložku zařízení - + None Nic - + Scene name: Název scény: + - All fixtures Všechna zařízení + - Generic Obecný - + Remove fixtures Odebrat zařízení - + Do you want to remove the selected fixture(s)? Opravdu si přejete odebrat označená zařízení? + - Channels Groups Skupiny kanálů @@ -5058,7 +5166,7 @@ Note that the wizard cannot tell the difference between a knob and a slider so y Povolit nespojené větve - + <Double click here to enter channel number manually> <klikněte zde pro manuální zadání čísla kanálu> @@ -5364,118 +5472,118 @@ Délka: %3 SimpleDesk - + View mode Režim náhledu - + Previous page Předchozí stana - + Current page Aktuální strana - + Next page Daší strana - + Reset universe Restartovat větev - + Universe Větev - + Playback Přehrávání - - + + Cue Stack Zásobník střihů - + Previous cue Předchozí střih - + Stop cue stack Zastavit zásobník střihů - + Next cue Další střih - + Clone cue stack Duplikovat zásobník sřihů - + Edit cue stack Upravit zásobník střihů - + Record cue Nahrát střih - + Channel groups Skupiny kanálů - + Cue Stack - Playback %1 Zásobník střihů - Přehrávání %1 - + No selection Nevybráno - + Cue name Název střihu - + Multiple Cues Skupina střihů - + Delete cue Smazat sřih - + Clone Cue Stack Duplikovat zásobník střihu - + Clone To Playback# Duplikovat do přehrávání# - + Cue %1 Střih %1 @@ -5483,32 +5591,32 @@ Délka: %3 SpeedDial - + Hours Hodiny - + Minutes Minuty - + Seconds Sekundy - + Milliseconds Milisekundy - + Infinite Nekonečno - + Tap Ťuknout (TAP) @@ -5516,17 +5624,17 @@ Délka: %3 SpeedDialWidget - + Fade In Zesílení - + Fade Out Zeslabení - + Hold Podržet @@ -5616,17 +5724,17 @@ Délka: %3 VCButton - + Choose... Zvolit... - + None Nic - + Button %1 Tlačítko %1 @@ -5641,17 +5749,17 @@ Délka: %3 Obrázky (%1) - + Toggle Blackout Režim Blackout - + Stop ALL functions! Zastavit VŠECHNY funkce! - + Icon Ikona @@ -5664,37 +5772,37 @@ Délka: %3 Vlastnosti tlačítka - + General Obecné - + Button label Popisek tlačítka - + Text to display on the button Text, který se má zobrazit na tlačítku - + Function Funkce - + The function that this button controls Funkce kterou ovládá toto tlačítko - + Attach a function to this button Přiřadit funkci k tomuto tlačítku - + Detach the button's function attachment Odebrat přiřazenou funkci z tlačítka @@ -5714,47 +5822,57 @@ Délka: %3 Zapnout/Vypnout funkci - + Flash the assigned function with this button Bliknout funkcí přiřazenou k tomuto tlačítku - + Flash function (only for scenes) Bliknout funkcí (pouze pro scény) - + + Override priority + + + + + Force LTP + + + + Toggle Blackout Režim Blackout - + Stop All Functions Zastavit VŠECHNY funkce - + Fade time: Čas zesílení/zeslabení: - + Adjust function intensity when it is running Upravit intenzitu funkce pokud běží - + Adjust Function Intensity Upravit intenzitu funkce - + Function's adjusted intensity percentage when run Upravená procentuální intenzita funkce pokud běží - + No function Bez funkce @@ -5845,64 +5963,64 @@ Délka: %3 VCCueList - + Show/Hide crossfade sliders Zobrazit/Skrýt šavle prolínání - - + + Play/Pause Cue list Spustit/pozastavit seznam střihů - - + + Stop Cue list Zastavit seznam střihů - + Go to previous step in the list Přejít na předchotí krok v seznamu - + Go to next step in the list Přejít na další krok v seznamu - + Cue list Seznam střihů - + Play/Stop Cue list - + Pause Cue list - + Fade In Zesílení - + Fade Out Zeslabení - + Duration Délka - + Notes Poznámky @@ -6067,7 +6185,7 @@ Délka: %3 VCFrame - + Add Přidat @@ -6120,17 +6238,17 @@ Délka: %3 - + External Input - Enable Externí vstup - povolit rámeček - + External Input - Previous Page Externí vstup - Předchozí stránka - + External Input - Next Page Externí vstup - Následující stránka @@ -6163,17 +6281,17 @@ Délka: %3 VCLabel - + Label Popisek - + Rename Label Přejmenování popisku - + Caption: Titulek: @@ -6181,42 +6299,42 @@ Délka: %3 VCMatrix - + Animation %1 Animace %1 - + End Color Reset Konec resetu Barvy - + Start color Red component Počátek barvy Červené komponenty - + Start color Green component Počátek barvy Zelené komponenty - + Start color Blue component Počátek barvy Modré komponenty - + End color Red component Konečná barva Červené komponenty - + End color Green component Konečná barva Zelené komponenty - + End color Blue component Cílová barva Modré komponenty @@ -6419,48 +6537,48 @@ Délka: %3 Přidej text - + No function Bez funkce - + Start Color Počáteční barva - + Start Color Knob Knoflík Počáteční barvy - + End Color Konečná barva - + End Color Knob Knoflík konečné barvy - + End Color Reset Konečná barva resetu - + Animation Animace - - + + Text Text - + Enter a text Zadaj text @@ -6711,12 +6829,12 @@ Délka: %3 VCSlider - + Slider %1 Šavle %1 - + Reset channels override @@ -6734,72 +6852,72 @@ Délka: %3 Obecné - + Value display style Styl zobrazení hodnot - + Show exact DMX values Zobrazit přesnou DMX hodnotu - + Actual DMX hodnota - + Show value as percentage Zobrazit hodnotu jako procentuální vyjádření - + Percentage Procenta - + Widget name Název ovladače - + Name of the slider Název šavle - + Slider movement Posun šavle - + Normal Normální - + Inverted Převrácený - + Widget appearance Vzhled ovladače - + Slider Šavle - + Knob Knoflík - + Catch up with the external controller input value @@ -7267,17 +7385,17 @@ Délka: %3 Zobraz milisekundy - + Multiply by 2 Input Násobit vstup dvěma - + Divide by 2 Input Dělit vstup dvěma - + Factor Reset Input Faktor Resetu vstupu @@ -7285,68 +7403,68 @@ Délka: %3 VCWidget - + Button Tlačítko - + Slider Šavle - + XYPad 2D ovladač - + Frame Rámeček - + Solo frame Samostatný rámeček - + Speed dial Rychlá volba - + Cue list Seznam střihů - + Label Popisek - + Audio Triggers Zvukové spoušte - + Animation Animace - + Clock Hodiny + - Unknown Neznámý - + This widget has no properties Tento prvek/ovladač nemá žádné vlastnosti @@ -7544,12 +7662,12 @@ Délka: %3 Název přednastavení - + Pan / Horizontal Axis Otáčení / Vodorovná osa (X) - + Tilt / Vertical Axis Naklápění / Svislá osa (Y) @@ -7579,45 +7697,45 @@ Délka: %3 Převrácený - + Width Šířka - + Height Výška - + Remove fixtures Odebrat zařízení - + Do you want to remove the selected fixtures? Opravdu si přejete odebrat označená zařízení? - - + + Error Chyba - + The selected Scene does not include any Pan or Tilt channel. Please select one with such channels. Zvolená Scéna neobsahuje žádný kanál Natočení/Naklopení (pan/tilt). Vyberte prosím některý z těchto kanálů. - + Please select at least one fixture or head to create this type of preset! Prosím zvolte alespoň jedno zařízení nebo halvu pro vytvoření tohohle presetu! - + Fixture Group Skupina zařízení @@ -7750,12 +7868,12 @@ Vyberte prosím některý z těchto kanálů. VideoItem - + Fullscreen Celá obrazovka - + Screen %1 Obrazovka %1 diff --git a/ui/src/qlcplus_de_DE.ts b/ui/src/qlcplus_de_DE.ts index 89701b02ea..6c40493c84 100644 --- a/ui/src/qlcplus_de_DE.ts +++ b/ui/src/qlcplus_de_DE.ts @@ -189,12 +189,12 @@ Anzahl an leeren Kanälen zwischen den hinzugefügten Geräten - + Fixtures found: %1 Gefundene Geräte: %1 - + Dimmers Dimmer @@ -525,376 +525,376 @@ App - + - New Workspace - Neue Arbeitsfläche - + Cannot exit in Operate mode Im Betriebsmodus kann das Programm nicht beendet werden - + Fixtures Geräte - + Functions Funktionen - + Shows Shows - + Virtual Console Virtuelle Konsole - + Simple Desk Einfache Arbeitsfläche - + Inputs/Outputs Eingänge/Ausgänge - + You must switch back to Design mode to close the application. Du musst in den Designmodus wechseln um das Programm zu schließen. - + Close Schließen - + Do you wish to save the current workspace before closing the application? Willst du die aktuelle Arbeitsfläche vor dem Schließen speichern? - + Switch to Design Mode In den Entwurfsmodus wechseln - + There are still running functions. Really stop them and switch back to Design mode? Es gibt noch laufende Funktionen. Willst du die wirklich stoppen und in den Entwicklungsmodus wechseln? - + Design Entwurf - + Switch to design mode In den Entwurfsmodus wechseln - + Operate Betrieb - - + + Switch to operate mode In den Betriebsmodus wechseln - + &New &Neu - + CTRL+N File|New STRG+N - + &Open Ö&ffnen - + CTRL+O File|Open STRG+O - + &Save &Speichern - + CTRL+S File|Save STRG+S - + Save &As... Speichern &unter ... - + &Operate &Betrieb - + CTRL+F12 Control|Toggle operate/design mode STRG+F12 - + Toggle Virtual Console Live edit Editieren der virtuellen Konsole im Livemodus umschalten - + Fade 1 second and stop Blende 1 Sekunde und stoppe - + Fade 5 seconds and stop Blende 5 Sekunden und stoppe - + Fade 10 second and stop Blende 10 Sekunden und stoppe - + Fade 30 second and stop Blende 30 Sekunden und stoppe - + Quit QLC+ QLC+ beenden - + Error Fehler - + File not found! The selected file has been moved or deleted. Datei nicht gefunden! Die ausgewählte Datei wurde verschoben oder gelöscht. - + Warning Warnung - + Some errors occurred while loading the project: Beim Laden des Projekts sind einige Fehler aufgetreten: - + Starting Q Light Controller Plus Starting Q Light Controller Starte Q Light Controller Plus - + &Monitor &Monitor - + CTRL+M Control|Monitor STRG+M - + Toggle Full Screen Vollbildmodus umschalten - + CTRL+F11 Control|Toggle Full Screen STRG+F11 - + &Index &Handbuch - + SHIFT+F1 Help|Index SHIFT+F1 - + &About QLC+ &About QLC &Über QLC+ - + Close the application? Anwendung beenden? - + Do you wish to close the application? Soll das Programm beendet werden? - + Exit Beenden - + Address Tool Adress-Werkzeug - + Toggle &Blackout &Blackout umschalten - + Live edit a function Livebaearbeitung einer Funktion - + Dump DMX values to a function Abbild (Dump) der DMX-Werte in einer Funktion - + CTRL+D Control|Dump DMX STRG+D - + Stop ALL functions! ALLE Funktionen stoppen! - + Workspace Arbeitsfläche - + Unable to read from file Kann Datei nicht lesen - + Unable to write to file Kann in Datei nicht schreiben - + A fatal error occurred Ein schwerer Fehler ist aufgetreten - + Unable to access resource Ressource nicht erreichbar - + Unable to open file for reading or writing Datei kann nicht zum Lesen oder Schreiben geöffnet werden - + Operation was aborted Vorgang wurde abgebrochen - + Operation timed out Zeitüberschreitung bei Vorgang - + An unspecified error has occurred. Nice. Ein unkategorisierter Fehler ist aufgetreten. Nicht schlecht. - + File error Dateifehler - - - + + + Do you wish to save the current workspace? Changes will be lost if you don't save them. Willst du die aktuelle Arbeitsfläche speichern? Änderungen gehen sonst verloren. - + New Workspace Neue Arbeitsfläche - - - + + + Open Workspace Arbeitsfläche öffnen - - + + Workspaces (*%1) Arbeitsflächen (*%1) - - + + All Files (*.*) Alle Dateien (*.*) - - + + All Files (*) Alle Dateien (*) - + Save Workspace As Arbeitsfläche speichern unter @@ -917,12 +917,12 @@ Changes will be lost if you don't save them. Nach der Tasteneingabe automatisch schließen - + Assign Key Taste zuweisen - + Hit the key combination that you wish to assign. You may hit either a single key or a combination using %1, %2, and %3. Wähle die Tastenkombination aus. Entweder eine einzelne Taste oder eine Kombination %1, %2, und %3. @@ -1049,23 +1049,23 @@ Changes will be lost if you don't save them. AudioItem - - + + Preview Left Channel Vorschau linker Kanal - + Preview Right Channel Vorschau rechter Kanal - + Preview Stereo Channels Vorschau Stereo-Kanäle - + Preview Mono Vorschaue Mono @@ -1133,52 +1133,52 @@ Changes will be lost if you don't save them. Eingang - + None Keine - + DMX DMX - + Function Funktion - + VC Widget Virtuelle Konsole Assistent - + %1 channels %1 Kanäle - + No function Keine Funktion - + No widget Kein Assistent - + Not assigned Nicht zugewiesen - + Volume Bar Lautstärke-Balken - + #%1 (%2Hz - %3Hz) #%1 (%2Hz - %3Hz) @@ -1236,12 +1236,12 @@ Changes will be lost if you don't save them. Modifizierer zurücksetzen - + Error Fehler - + You are trying to overwrite a system template! Please choose another name and the template will be saved in your channel modifier's user folder. Du versuchst, eine Systemvorlage zu überschreiben! Bitte wähle einen anderen Namen. Die Vorlage wird im User-Verzeichnis des Kanaländerungseditors gespeichert. @@ -1256,13 +1256,13 @@ Changes will be lost if you don't save them. - + Name Name - + Type Typ @@ -1282,27 +1282,27 @@ Changes will be lost if you don't save them. Alle ausklappen - + Selected Ausgewählt - + Channel properties configuration Einstellung der Kanaleigenschaften - + Can fade Kann faden - + Behaviour Verhalten - + Modifier Modifizierer @@ -1336,13 +1336,13 @@ Changes will be lost if you don't save them. - + Fade In Einblenden - + Fade Out Ausblenden @@ -1388,7 +1388,7 @@ Changes will be lost if you don't save them. - + Hold Halten @@ -1558,47 +1558,47 @@ Changes will be lost if you don't save them. Rückwärts - + Cut Ausschneiden - + Copy Kopieren - + Paste Einfügen - + Paste error Einfügefehler - + Trying to paste on an incompatible Scene. Operation canceled. Einfügen in eine inkompatible Szene. Ausführung abgebrochen. - + Common Fade In Gemeinsame Einblendung - + Common Fade Out Gemeinsame Ausblendung - + Common Hold Gemeinsamer Halt - + Multiple Steps Mehrere Schritte @@ -1654,12 +1654,12 @@ Changes will be lost if you don't save them. ConsoleChannel - + Intensity Intensität - + Reset this channel Diesen Kanal zurücksetzen @@ -1720,6 +1720,83 @@ Changes will be lost if you don't save them. Cue + + CustomFeedbackDialog + + + Custom Feedback Configuration + + + + + Value + Wert + + + + Label + Beschriftung + + + + Color + + + + + Values + Werte + + + + Lower Value + + + + + Monitor Value + + + + + Upper Value + + + + + + + Color Selection + + + + + MIDI Channel + + + + + Upper Channel + + + + + Lower Channel + + + + + Monitor Channel + + + + + + + From plugin settings + + + DmxDumpFactory @@ -1779,50 +1856,17 @@ Changes will be lost if you don't save them. Szenenname: - + Dump all channels (%1 Universes, %2 Fixtures, %3 Channels) Dump all DMX values (%1 Universes, %2 Fixtures, %3 Channels) Abbild aller Kanäle (%1 Universen, %2 Geräte, %3 Kanäle) - + New Scene From Live %1 Neue Szene aus der Livewiedergabe %1 - - DocBrowser - - - %1 - Document Browser - %1 - Handbuch - - - - Backward - Rückwärts - - - - Forward - Vorwärts - - - - Index - Inhaltsverzeichnis - - - - About Qt - Über Qt - - - - Close this window - Dieses Fenster schließen - - EFXEditor @@ -2103,12 +2147,12 @@ Changes will be lost if you don't save them. Effekt-Vorschau - + Remove fixtures Gerät entfernen - + Do you want to remove the selected fixture(s)? Willst du die ausgewählten Funktionen entfernen? @@ -2453,88 +2497,88 @@ Changes will be lost if you don't save them. Gerätenamen neu zuweisen - - + + (remapped) (neu zugewiesen) - + Import Fixtures List Gerätedefinitionsliste importieren - + Fixtures List (*%1) Gerätedefinitionsliste (*%1) - + All Files (*.*) Alle Dateien (*.*) - + All Files (*) Alle Dateien (*) - + Do you want to automatically connect fixtures with the same name? Geräte mit gleichem Namen automatisch verbinden? - + Generic Dimmer Generischer Dimmer - + Delete Fixtures Geräte löschen - + Do you want to delete the selected items? Ausgewählte Objekte löschen? - + Invalid operation Ungültiger Vorgang - + You are trying to clone a fixture on an address already in use. Please fix the target list first. Geräteadresse für das Klonen bereits belegt. Bitte die Zielliste korrigieren. - - - - + + + + Invalid selection Ungültige Auswahl - - - + + + Please select a source and a target fixture or channel to perform this operation. Bitte Quell- und Zielgerät oder Kanal zur Ausführung auswählen. - + To perform a fixture remap, please select fixtures on both lists. Zur Änderung der Gerätezuweisung bitte Geräte in beiden Listen auswählen. - + This might take a while... Hierzu wird einige Zeit benötigt... - + Cancel Abbrechen @@ -2547,12 +2591,12 @@ Changes will be lost if you don't save them. Gerät auswählen - + No fixtures available Keine Geräte verfügbar - + Go to the Fixture Manager and add some fixtures first. Gehe zum Gerätemanager um Geräte hinzuzufügen. @@ -2609,7 +2653,7 @@ Changes will be lost if you don't save them. FunctionLiveEditDialog - + Function Live Edit Funktionslivebaerbeitung @@ -2617,195 +2661,195 @@ Changes will be lost if you don't save them. FunctionManager - + New &scene Neue &Szene - + New c&haser Neuer C&haser - + New se&quence Neue Se&quenz - + New c&ollection Neue Sa&mmlung - + New E&FX Neuer E&ffekt - + New &RGB Matrix Neue &RGB Matrix - + New scrip&t Neues Scrip&t - + New Scene Neue Szene - + New Chaser Neuer Chaser - + New Sequence Neue Sequenz - + &Clone &Kopieren - + New au&dio Neues &Audio - + New vid&eo Neues vid&eo - + New fo&lder Neuer Ordner - + Select Startup Function Auswahl Start-Funktion - + Function &Wizard Funktionsassistent - + &Delete &Löschen - + Select &all &Alles auswählen - + New Collection Neue Sammlung - + New EFX Neuer Effekt - + New RGB Matrix Neue RGB Matrix - + New Script Neues Skript - + Open Audio File Audio-Datei-öffnen - + Audio Files (%1) Audio Dateien (%1) - - + + All Files (*.*) Alle Dateien (*.*) - - + + All Files (*) Alle Dateien (*) - + Unsupported audio file Nicht unterstützte Audio-Datei - + This audio file cannot be played with QLC+. Sorry. Diese Audiodatei kann mit QLC+ nicht wiedergegeben werden. Sorry. - + Open Video File Videodatei öffnen - + Video Files (%1) Videodateien (%1) - + Unsupported video file Nicht unterstützte Videodatei - + This video file cannot be played with QLC+. Sorry. Dieses Videodatei kann leider nicht mit QLC+ wiedergegeben werden. - + Do you want to DELETE folder: Do you want to DELETE foler: Soll der Ordner GELÖSCHT werden: - + Do you want to DELETE functions: Willst du folgende Funktionen entfernen: - + (This will also DELETE: (Ebenfalls GELÖSCHT wird: - + Delete Functions Funktionen entfernen - + Function Funktion - + (Copy) (Kopie) @@ -3092,32 +3136,32 @@ p, li { white-space: pre-wrap; } Entfernen - + %1 group %1-Gruppe - + Error Fehler - + %1 has no capability supported by this wizard. %1 keine von diesem Assistenten unterstützte Fähigkeit. - + Presets solo frame Voreinstellungen Einzelrahmen - + Click & Go RGB Click & Go RGB - + Click & Go Macro Click & Go Makro @@ -3125,27 +3169,27 @@ p, li { white-space: pre-wrap; } GrandMasterSlider - + GM GM - + Grand Master <B>limits</B> the maximum value of Grand Master <B>Limits</B> Maximalwert von - + Grand Master <B>reduces</B> the current value of Grand Master <B>verringert</B> den aktuellen Wert von - + intensity channels Intensitätskanäle - + all channels alle Kanäle @@ -3246,46 +3290,46 @@ p, li { white-space: pre-wrap; } InputOutputManager - + Add U&niverse U&niversum hinzufüegen - + &Delete Universe Universe Universum entfernen - + Universe name: Name des Universums: - + Passthrough not sure, maybe Durchlauf Passthrough - - + + Universe %1 Universum %1 - - + + Delete Universe Universum entfernen - + The universe you are trying to delete is patched. Are you sure you want to delete it? Das Universum, welchs gelöscht werden soll ist gepatched, soll es wirklich gelöscht werden? - + There are some fixtures using the universe you are trying to delete. Are you sure you want to delete it? Das Universum, welches gelöscht werden soll wird von einigen Geräten verwendet, soll es wirklich gelöscht werden? @@ -3392,20 +3436,20 @@ p, li { white-space: pre-wrap; } Level Monitor - - - + + + Error Fehler - - + + Output line already assigned Ausgang bereits zugewiesen - + An error occurred while trying to open the selected device line. This can be caused either by a wrong system configuration or an unsupported input/output mode. Please refer to the plugins documentation to troubleshoot this. @@ -3414,67 +3458,67 @@ Dies kann entweder an einer fehlerhaften Systemkonfiguration oder einem nicht un Bitte in der Plugindokumentation nachlesen um den Fehler zu beheben. - - + + Existing Input Profile Existierendes Eingangsprofil - - + + An input profile at %1 already exists. Do you wish to overwrite it? Es existiert bereits ein Eingangsprofil in %1. Willst du es überschreiben? - - + + Save Input Profile Eingangsprofil speichern - - + + Input Profiles (*.qxi) Eingangsprofile (*.qxi) - - + + Saving failed Speichern fehlgeschlagen - + Unable to save the profile to %1 Kann das Profil nicht nach %1 speichern - + Delete profile Profil löschen - + Do you wish to permanently delete profile "%1"? Willst du das Profil "%1" permanent löschen? - + File deletion failed Löschen fehlgeschlagen - + Unable to delete file %1 Kann Datei %1 nicht löschen - + Unable to save %1 to %2 Kann %1 nicht nach %2 speichern - + Default device Standardgerät @@ -3487,176 +3531,233 @@ Bitte in der Plugindokumentation nachlesen um den Fehler zu beheben.Eingangsprofil Editor - + General Allgemein - + Manufacturer Hersteller - + The name of the company that made the device Der Name der Firma die dieses Gerät hergestellt hat - + Model Modell - + The device's model name Der Gerätename - - Channels - Kanäle - - - + + Channel Kanal - + + Name Name - - + + Type Typ - + MIDI Global Settings Global MIDI-Einstellungen - + When MIDI notes are used, send a Note Off when value is 0 Wenn MIDI-Noten verwendet werden, sende ein "Note Off", wenn der Wert 0 ist - - + + Input Mapping + + + + Behaviour Verhalten - + Movement Bewegung - + Absolute Absolut - + Relative Relativ - + Generate an extra Press/Release when toggled Ein zusätzliches "Drücken/Loslassen" beim Umschalten generieren - + Custom feedback Benutzerdefinierte Rückmeldung - + Upper value Oberer Wert - + + + MIDI channel + + + + + Colors + + + + + Remove the selected color + + + + + Add a new color + + + + + Value + Wert + + + + Label + Beschriftung + + + + Color + + + + + MIDI Channels + + + + + Add a new MIDI channel + + + + + Remove the selected MIDI channel + + + + Lower value Unterer Wert - + Sensitivity Empfindlichkeit - + Add a new channel description Neue Kanalbeschreibung hinzufügen - + Remove the selected channels Markierten Kanäle löschen - + Edit the selected channel Ausgewählten Kanal bearbeiten - + Automatically add channels to the list when you wiggle the device's controls Kanäle automatisch hinzufügen wenn die externen Bedienelemente betätigt werden - + File not writable Datei nicht beschreibbar - + You do not have permission to write to the file %1. You might not be able to save your modifications to the profile. Du hast keine Schreibrechte für die Datei %1. Du wirst deine Profiländerungen nicht speichern können. - + + From plugin settings + + + + Missing information Fehlende Information - + Manufacturer and/or model name is missing. Hersteller und/oder Modellname fehlen. - - + + Channel already exists Kanal existiert bereits - - + + Channel %1 already exists Kanal %1 existiert bereits - + Delete channels Kanäle löschen - + Delete all %1 selected channels? Alle %1 ausgewählte Kanäle löschen? - + Channel wizard activated Kanalassistent aktiviert - + 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. @@ -3664,12 +3765,39 @@ Note that the wizard cannot tell the difference between a knob and a slider so y Der Assistent kennt keinen Unterschied zwischen einem Schalter und einem Regler, die Änderungen müssen manuell durchgeführt werden. - + + + Enter value + + + + + Feedback value + + + + + + Enter label + + + + + Color label + + + + + MIDI channel label + + + + Button %1 Schalter %1 - + Slider %1 Schieberegler %1 @@ -3702,65 +3830,50 @@ Der Assistent kennt keinen Unterschied zwischen einem Schalter und einem Regler, Externer Eingang - + When toggled, you can click an external button to assign it to this widget. Wenn betätigt kann ein externer Schalter betätigt werden um diesen dem virtuellen Konsolenschalter zuzuweisen. - + Auto Detect Automatische Erkennung - + Input Universe Eingangsuniversum - + Input Channel Eingangskanal - + The input universe that sends data to this widget Das Eingangsuniversum, welches Daten zu diesem Assistenten sendet - + Custom Feedback Benutzerdefinierte Rückmeldung - + The particular input channel within the input universe that sends data to this widget Der bestimmte Eingangskanal im Eingangsuniversum der Daten zu diesem Assistenten sendet - + Choose an external input universe & channel that this widget should listen to. Wähle ein externes Universum & einen Kanal auf den dieser Schalter hören soll. - + Choose... Auswählen ... - - - Custom feedback - Benutzerdefinierte Rückmeldung - - - - Lower value - Unterer Wert - - - - Upper value - Oberer Wert - Monitor @@ -3917,14 +4030,14 @@ Der Assistent kennt keinen Unterschied zwischen einem Schalter und einem Regler, Hintergrund - - + + Select background image Hintergrundbild auswählen - - + + Images Bilder @@ -4214,12 +4327,12 @@ Der Assistent kennt keinen Unterschied zwischen einem Schalter und einem Regler, PlaybackSlider - + Select Auswahl - + Flash Blitz @@ -4235,34 +4348,29 @@ Der Assistent kennt keinen Unterschied zwischen einem Schalter und einem Regler, QObject - + Operate Betrieb - + Design Entwurf - - + + Reversed Umgekehrt - + Page: %1 Seite: %1 RDMManager - - - Form - Formular - Scan for RDM devices... @@ -4682,22 +4790,22 @@ Der Assistent kennt keinen Unterschied zwischen einem Schalter und einem Regler, Nichts - + No fixture group to control Keine Gerätegruppe zu steuern - + Select image Bildauswahl - + Images Bilder - + Sequence Sequenz @@ -4757,105 +4865,105 @@ Der Assistent kennt keinen Unterschied zwischen einem Schalter und einem Regler, Alle Gerätekanäle deaktivieren - + Enable all channels in current fixture Alle Kanäle im aktuellen Gerät aktivieren - + Disable all channels in current fixture Alle Kanäle im aktuellen Gerät deaktivieren - + Copy current values to clipboard Aktuelle Werte in den Zwischenspeicher kopieren - + Paste clipboard values to current fixture Werte aus dem Zwischenspeicher in das aktuelle Gerät kopieren - + Copy current values to all fixtures Aktuelle Werte in alle Geräte kopieren - + Color tool for CMY/RGB-capable fixtures Farbtool für CMY/RGB-fähige Geräte - + Position tool for moving heads/scanners Positionswerkzeug für movingheads/scanner - + Switch between tab view and all channels view Zwischen Tab-Ansischt und allen Kanälen - + Toggle blind mode Blindmodus umschalten - + Show/Hide speed dial window Anzeigen/Ausblenden des Schnellwahlfensters - + Clone this scene and append as a new step to the selected chaser Szene kopieren und als neuen Schritt an den ausgewählten Chaser anfügen - + Go to next fixture tab Zum nächsten Geräte-Tab wechseln - + Go to previous fixture tab Zum vorherigen Geräte-Tab wechseln - + None Nichts - + Scene name: Szenenname: + - All fixtures Alle Geräte + - Channels Groups Kanalgruppen + - Generic Generisch - + Remove fixtures Geräte entfernen - + Do you want to remove the selected fixture(s)? Willst du die ausgewählten Funktionen entfernen? @@ -5067,7 +5175,7 @@ Der Assistent kennt keinen Unterschied zwischen einem Schalter und einem Regler, Erlaube ungepatchte Universen - + <Double click here to enter channel number manually> <Hier klicken um eine Kanalnummer manuell einzugeben> @@ -5374,118 +5482,118 @@ Dauer: %3 SimpleDesk - + Universe Universum - + Next page Nächste Seite - + Current page Aktuelle Seite - + Previous page Vorherige Seite - + View mode Ansischtsmodus - + Reset universe Universum zurücksetzen - + Playback Wiedergabe - - + + Cue Stack Cue-Stapel - + Previous cue Vorheriges Cue - + Stop cue stack Cue-Stapel stoppen - + Next cue Nächste Cue - + Clone cue stack Cue-Stapel kopieren - + Edit cue stack Cue-Stapel bearbeiten - + Record cue Cue aufzeichnen - + Channel groups Kanalgruppen - + Cue Stack - Playback %1 Cue Stapel - Wiedergabe %1 - + No selection Keine Auswahl - + Cue name Cue-Name - + Multiple Cues Mehrere Cues - + Delete cue Cue löschen - + Clone Cue Stack Cue-Stapel kopieren - + Clone To Playback# Zur Wiedergabe kopieren# - + Cue %1 Cue %1 @@ -5493,32 +5601,32 @@ Dauer: %3 SpeedDial - + Hours Stunden - + Minutes Minuten - + Seconds Sekunden - + Milliseconds Millisekunden - + Infinite Unendlich - + Tap Tap @@ -5526,17 +5634,17 @@ Dauer: %3 SpeedDialWidget - + Fade In Einblenden - + Fade Out Ausblenden - + Hold Halten @@ -5626,17 +5734,17 @@ Dauer: %3 VCButton - + Choose... Auswählen ... - + None Kein - + Button %1 Schalter %1 @@ -5651,17 +5759,17 @@ Dauer: %3 Bilder (%1) - + Toggle Blackout Schalte Blackout - + Stop ALL functions! ALLE Funktionen stoppen! - + Icon Symbol @@ -5674,37 +5782,37 @@ Dauer: %3 Schaltereigenschaften - + General Allgemein - + Button label Schalterbeschriftung - + Text to display on the button Text, der auf dem schalter erscheint' - + Function Funktion - + The function that this button controls Die Funktion, die dieser Schalter steuert - + Attach a function to this button Funktion dem Schalter zuweisen - + Detach the button's function attachment Funktion des Schalters zurücknehmen @@ -5724,47 +5832,57 @@ Dauer: %3 Funktion umschalten an/aus - + Flash the assigned function with this button Die Zugewiesene Funktion mit dem Schalter aufleuchten - + Flash function (only for scenes) Flashfunktion (nur für Szenen) - + + Override priority + + + + + Force LTP + + + + Toggle Blackout Schalte Blackout - + Stop All Functions Alle Funktionen stoppen - + Fade time: Überblendzeit: - + Adjust function intensity when it is running Funktionsintensität anpassen wenn es läuft - + Adjust Function Intensity Funktionsintensität anpassen - + Function's adjusted intensity percentage when run Die angepasste Funktionsintensität in Prozent wenn es läuft - + No function Keine Funktion @@ -5855,64 +5973,64 @@ Dauer: %3 VCCueList - + Show/Hide crossfade sliders Anzeigen/Ausblenden Crossfaderegler - - + + Play/Pause Cue list Cue-Liste Abspielen/Pausieren - - + + Stop Cue list Cue-Liste stoppen - + Go to previous step in the list Zum vorherigen Schritt in der Liste wechseln - + Go to next step in the list Zum nächsten Schritt in der Liste wechseln - + Cue list Cue-Liste - + Play/Stop Cue list Cue-Liste Abspielen/Stoppen - + Pause Cue list Cue-Liste pausieren - + Fade In Einblenden - + Fade Out Ausblenden - + Duration Dauer - + Notes Notizen @@ -6078,7 +6196,7 @@ Dauer: %3 VCFrame - + Add Hinzufügen @@ -6131,17 +6249,17 @@ Dauer: %3 Seitenname - + External Input - Enable Externer Eingang - Aktivieren - + External Input - Previous Page Externer Eingang - Vorige Seite - + External Input - Next Page Externer Eingang - Nächste Seite @@ -6174,17 +6292,17 @@ Dauer: %3 VCLabel - + Label Beschriftung - + Rename Label Beschriftung ändern - + Caption: Überschrift: @@ -6192,42 +6310,42 @@ Dauer: %3 VCMatrix - + Animation %1 Animation %1 - + End Color Reset Endfarbenreset - + Start color Red component Anfangsfarbe Rotanteil - + Start color Green component Anfangsfarbe Grünanteil - + Start color Blue component Anfangsfarbe Blauanteil - + End color Red component Endfarbe Rotanteil - + End color Green component Endfarbe Grünanteil - + End color Blue component Endfarbe Blauanteil @@ -6430,48 +6548,48 @@ Dauer: %3 Endfarbenreset hinzufügen - + No function Keine Funktion - + Start Color Anfangsfarbe - + Start Color Knob Schalter für Anfangsfarbe - + End Color Endfarbe - + End Color Knob Schalter für Endfarbe - + End Color Reset Endfarbenreset - + Animation Animation - - + + Text Text - + Enter a text Einen Text eingeben @@ -6723,12 +6841,12 @@ Dauer: %3 VCSlider - + Slider %1 Schieberegler %1 - + Reset channels override Überschreiben zurücksetzen @@ -6746,42 +6864,42 @@ Dauer: %3 Allgemein - + Name of the slider Name des Schiebereglers - + Value display style Werte Anzeigeart - + Show exact DMX values Genauen DMX Wert zeigen - + Show value as percentage Werte als Prozentangabe - + Percentage Prozentual - + Slider movement Regler Bewegung - + Normal Normal - + Inverted Invertiert @@ -6812,32 +6930,32 @@ Dauer: %3 Gobo/Effekt/Makro - + Actual Aktuell - + Widget name Name des Assistenten - + Widget appearance Aussehen des Assistenten - + Slider Schieberegler - + Knob Knopf - + Catch up with the external controller input value Dem Wert des externen Controller-Eingangs folgen @@ -7271,17 +7389,17 @@ Dauer: %3 Millisekundenfeld anzeigen - + Multiply by 2 Input Eingang zum Verdoppeln - + Divide by 2 Input Eingang zum Halbieren - + Factor Reset Input Eingang zum Zurücksetzen des Multiplikationsfaktors @@ -7299,68 +7417,68 @@ Dauer: %3 VCWidget - + Button Schalter - + Slider Schieberegler - + XYPad XY-Pad - + Frame Rahmen - + Solo frame Einzelrahmen - + Speed dial Schnellwahl - + Cue list Cue-Liste - + Label Beschriftung - + Audio Triggers Audiotrigger - + Animation Animation - + Clock Uhr + - Unknown Unbekannt - + This widget has no properties Dieser Assistent hat keine Eigenschaften @@ -7568,12 +7686,12 @@ Dauer: %3 Name der Voreinstellung - + Pan / Horizontal Axis Pan / Horizontale Achsen - + Tilt / Vertical Axis Tilt / Vertikale Achse @@ -7593,45 +7711,45 @@ Dauer: %3 Invertiert - + Width Breite - + Height Höhe - + Remove fixtures Geräte entfernen - + Do you want to remove the selected fixtures? Willst du das ausgewählte Gerät entfernen? - - + + Error Fehler - + The selected Scene does not include any Pan or Tilt channel. Please select one with such channels. I don't understand the context of the second sentence. Which channels? Bitte eine Szene ohne Pan- oder Tilt-Kanäle auswählen. - + Please select at least one fixture or head to create this type of preset! Bitte mindestens ein Gerät oder Kopf auswählen um diese Art Voreinstellung zu erzeugen! - + Fixture Group Gerätegruppe @@ -7764,12 +7882,12 @@ Please select one with such channels. VideoItem - + Fullscreen Vollbild - + Screen %1 Bildschirm %1 diff --git a/ui/src/qlcplus_es_ES.ts b/ui/src/qlcplus_es_ES.ts index 2cee17ca1b..66a5b94504 100644 --- a/ui/src/qlcplus_es_ES.ts +++ b/ui/src/qlcplus_es_ES.ts @@ -189,12 +189,12 @@ Cantidad de canales vacíos a dejar entre fixtures añadidos - + Fixtures found: %1 Fixtures encontrados: %1 - + Dimmers Dimmers @@ -525,376 +525,376 @@ App - + Cannot exit in Operate mode No puede salir en Modo Operación - + You must switch back to Design mode to close the application. Tiene que cambiar a Modo Diseño para cerrar la aplicación. - + Close Cerrar - + Do you wish to save the current workspace before closing the application? ¿Desea guardar el espacio de trabajo antes de cerrar la aplicacion? - + Starting Q Light Controller Plus Starting Q Light Controller Iniciando Q Light Controller Plus - + - New Workspace - Nuevo Espacio de trabajo - + Switch to Design Mode Cambiar a Modo Diseño - + There are still running functions. Really stop them and switch back to Design mode? Todavía hay funciones ejecutándose. ¿Desea detenerlas y volver a Modo Diseño? - + Design Diseño - + Switch to design mode Cambiar a Modo Diseño - + Operate Operación - - + + Switch to operate mode Cambiar a Modo Operación - + &New &Nuevo - + CTRL+N File|New - + &Open &Abrir - + CTRL+O File|Open - + &Save &Guardar - + CTRL+S File|Save CTRL+S - + Save &As... Guardar &como... - + &Operate &Operación - + &Monitor &Monitor - + Toggle &Blackout Activar/Desactivar &Blackout - + CTRL+F12 Control|Toggle operate/design mode - + CTRL+M Control|Monitor - + Live edit a function Edita una función en vivo - + Toggle Full Screen Cambiar a Pantalla Completa - + CTRL+F11 Control|Toggle Full Screen - + &Index &Indice - + SHIFT+F1 Help|Index - + &About QLC+ &About QLC Acerca &QLC+ - + Fixtures Fixtures - + Functions Funciones - + Shows Shows - + Virtual Console Consola Virtual - + Simple Desk Mesa Simple - + Inputs/Outputs Entradas/Salidas - + Close the application? ¿Cerrar la aplicación? - + Do you wish to close the application? ¿Desea cerrar la aplicación? - + Exit Salir - + Address Tool Herramienta de Direccionamiento - + Toggle Virtual Console Live edit Activa/Desactiva la edición en vivo de la Consola Virtual - + Dump DMX values to a function Volcar valores DMX a una función - + CTRL+D Control|Dump DMX - + Stop ALL functions! ¡Detener TODAS las funciones! - + Fade 1 second and stop Fade out de 1 segundo y detener - + Fade 5 seconds and stop Fade out de 5 segundos y detener - + Fade 10 second and stop Fade out de 10 segundos y detener - + Fade 30 second and stop Fade out de 30 segundos y detener - + Quit QLC+ Salir de QLC+ - + Workspace Espacio de Trabajo - + Unable to read from file Imposible leer desde archivo - + Unable to write to file Imposible escribir a archivo - + A fatal error occurred Ocurrió un error fatal - + Unable to access resource Imposible acceder a recurso - + Unable to open file for reading or writing Imposible abrir archivo para leer o escribir - + Operation was aborted La operación ha sido abortada - + Operation timed out Operacion caducada - + An unspecified error has occurred. Nice. Ocurrió un error desconocido. Genial. - + File error Error de archivo - - - + + + Do you wish to save the current workspace? Changes will be lost if you don't save them. ¿Desea guardar el espacio de trabajo actual? Los cambios se perderán si no los guarda. - + New Workspace Nuevo Espacio de Trabajo - - - + + + Open Workspace Abrir Espacio de Trabajo - - + + Workspaces (*%1) Espacios de Trabajo (*%1) - - + + All Files (*.*) Todos los archivos (*.*) - - + + All Files (*) Todos los archivos (*) - + Save Workspace As Guardar el espacio de trabajo como - + Error Error - + File not found! The selected file has been moved or deleted. ¡Archivo no encontrado! El archivo seleccionado ha sido movido o borrado. - + Warning Atención - + Some errors occurred while loading the project: Algunos errores ocurrieron al cargar el proyecto: @@ -917,12 +917,12 @@ El archivo seleccionado ha sido movido o borrado. Cerrar automáticamente al presionar una tecla - + Assign Key Asignar Tecla - + Hit the key combination that you wish to assign. You may hit either a single key or a combination using %1, %2, and %3. Presione la combinación de teclas que quiere asignar. Puede presionar una sola tecla o una combinación usando %1, %2 ó %3. @@ -1049,23 +1049,23 @@ El archivo seleccionado ha sido movido o borrado. AudioItem - - + + Preview Left Channel Visualizar Canal Izquierdo - + Preview Right Channel Visualizar Canal Derecho - + Preview Stereo Channels Visualizar Canales Estéreo - + Preview Mono Visualizar Mono @@ -1133,52 +1133,52 @@ El archivo seleccionado ha sido movido o borrado. Entrada - + None Ninguno - + DMX DMX - + Function Función - + VC Widget Widget de CV - + %1 channels %1 canales - + No function Ninguna función - + No widget Ningún widget - + Not assigned No asignado - + Volume Bar Barra de volumen - + #%1 (%2Hz - %3Hz) #%1 (%2Hz - %3Hz) @@ -1236,12 +1236,12 @@ El archivo seleccionado ha sido movido o borrado. Desestablecer Modificador - + Error Error - + You are trying to overwrite a system template! Please choose another name and the template will be saved in your channel modifier's user folder. ¡Está intentando sobreescribir una plantilla del sistema! Por favor escoja otro nombre y la plantilla se guardará en el directorio de modificadores de canal del usuario. @@ -1256,13 +1256,13 @@ El archivo seleccionado ha sido movido o borrado. - + Name Nombre - + Type Tipo @@ -1282,27 +1282,27 @@ El archivo seleccionado ha sido movido o borrado. Expandir todos - + Selected Seleccionados - + Channel properties configuration Configuración de propiedades de canales - + Can fade Puede hacer fade - + Behaviour Comportamiento - + Modifier @@ -1336,13 +1336,13 @@ El archivo seleccionado ha sido movido o borrado. - + Fade In Fade In - + Fade Out Fade Out @@ -1363,7 +1363,7 @@ El archivo seleccionado ha sido movido o borrado. - + Hold Espera @@ -1559,47 +1559,47 @@ El archivo seleccionado ha sido movido o borrado. Atrás - + Cut Cortar - + Copy Copiar - + Paste Pegar - + Paste error Error de pegado - + Trying to paste on an incompatible Scene. Operation canceled. Intentando pegar en una Escena incompatible. Operación cancelada. - + Common Fade In Tiempo de Fade In común - + Common Fade Out Tiempo de Fade Out común - + Common Hold Espera común - + Multiple Steps Pasos múltiples @@ -1655,12 +1655,12 @@ El archivo seleccionado ha sido movido o borrado. ConsoleChannel - + Intensity Intensidad - + Reset this channel Restaurar este canal @@ -1721,6 +1721,83 @@ El archivo seleccionado ha sido movido o borrado. Pie + + CustomFeedbackDialog + + + Custom Feedback Configuration + + + + + Value + Valor + + + + Label + Etiqueta + + + + Color + + + + + Values + Valores + + + + Lower Value + + + + + Monitor Value + + + + + Upper Value + + + + + + + Color Selection + + + + + MIDI Channel + + + + + Upper Channel + + + + + Lower Channel + + + + + Monitor Channel + + + + + + + From plugin settings + + + DmxDumpFactory @@ -1780,50 +1857,17 @@ El archivo seleccionado ha sido movido o borrado. Nombre de la Escena: - + Dump all channels (%1 Universes, %2 Fixtures, %3 Channels) Dump all DMX values (%1 Universes, %2 Fixtures, %3 Channels) Volcar todos los canales (%1 Universos, %2 Fixtures, %3 Canales) - + New Scene From Live %1 Nueva Escena desde Live %1 - - DocBrowser - - - %1 - Document Browser - %1 - Explorador de Documentos - - - - Backward - Atrás - - - - Forward - Adelante - - - - Index - Índice - - - - About Qt - Acerca de Qt - - - - Close this window - Cerrar esta ventana - - EFXEditor @@ -2104,12 +2148,12 @@ El archivo seleccionado ha sido movido o borrado. Ver qué hace el EFX cuando se ejecuta - + Remove fixtures Quitar fixtures - + Do you want to remove the selected fixture(s)? ¿Quiere quitar los fixtures seleccionados? @@ -2454,88 +2498,88 @@ El archivo seleccionado ha sido movido o borrado. Reasignar nombre de fixtures - - + + (remapped) (reasignado) - + Import Fixtures List Importa la lista de Fixtures - + Fixtures List (*%1) Lista de Fixtures (*%1) - + All Files (*.*) Todos los archivos (*.*) - + All Files (*) Todos los archivos (*) - + Do you want to automatically connect fixtures with the same name? Queréis conectar automáticamente los fixtures con el mismo nombre? - + Generic Dimmer Dimmer genérico - + Delete Fixtures Eliminar fixture - + Do you want to delete the selected items? ¿Desea borrar los ítems seleccionados? - + Invalid operation Operación inválida - + You are trying to clone a fixture on an address already in use. Please fix the target list first. Está tratando de clonar un fixture en una dirección en uso. Por favor arregle la lista de destino primero. - - - - + + + + Invalid selection Selección inválida - - - + + + Please select a source and a target fixture or channel to perform this operation. Por favor, seleccione un fixture o canal de origen y de destino para realizar esta operación. - + To perform a fixture remap, please select fixtures on both lists. Para realizar la reasignación de fixtures, por favor seleccione fixtures en ambas listas. - + This might take a while... Esto puede tardar un rato... - + Cancel Cancelar @@ -2548,12 +2592,12 @@ El archivo seleccionado ha sido movido o borrado. Seleccione un fixture - + No fixtures available No hay fixtures disponibles - + Go to the Fixture Manager and add some fixtures first. Ir al Gestor de Fixtures y añadir algún fixture primero. @@ -2610,7 +2654,7 @@ El archivo seleccionado ha sido movido o borrado. FunctionLiveEditDialog - + Function Live Edit Editar la función en vivo @@ -2618,195 +2662,195 @@ El archivo seleccionado ha sido movido o borrado. FunctionManager - + New &scene Nueva E&scena - + New c&haser Nuevo c&haser - + New se&quence Nueva &secuencia - + New c&ollection Nueva &colección - + New E&FX Nuevo E&FX - + New &RGB Matrix Nueva Matriz &RGB - + New scrip&t Nuevo srip&t - + New Scene Nueva Escena - + New Chaser Nuevo Chaser - + New Sequence Nueva Secuencia - + &Clone C&lonar - + New au&dio Nuevo au&dio - + New vid&eo Nuevo &video - + New fo&lder Nueva Car&peta - + Select Startup Function Función de arranque - + Function &Wizard &Asistente de Funciones - + &Delete &Eliminar - + Select &all Seleccionar &todo - + New Collection Nueva Colección - + New EFX Nuevo EFX - + New RGB Matrix Nueva Matriz RGB - + New Script Nuevo Script - + Open Audio File Abrir Archivo de Audio - + Audio Files (%1) Archivos de Audio (%1) - - + + All Files (*.*) Todos los archivos (*.*) - - + + All Files (*) Todos los archivos (*) - + Unsupported audio file Archivo de audio no soportado - + This audio file cannot be played with QLC+. Sorry. Este archivo de audio no puede ser reproducido con QLC+. Mil disculpas. - + Open Video File Abrir Archivo de video - + Video Files (%1) Archivos de Video (%1) - + Unsupported video file Archivo de video no soportado - + This video file cannot be played with QLC+. Sorry. Este archivo de video no puede ser reproducido con QLC+. Mil disculpas. - + Do you want to DELETE folder: Do you want to DELETE foler: Desea ELIMINAR la carpeta: - + Do you want to DELETE functions: Quiere ELIMINAR funciones: - + (This will also DELETE: (Esto ELIMINARÁ también: - + Delete Functions Borrar Funciones - + Function Función - + (Copy) (Copiar) @@ -3093,32 +3137,32 @@ p, li { white-space: pre-wrap; } Eliminar - + %1 group %1 grupo - + Error Error - + %1 has no capability supported by this wizard. %1 no tiene capacidades soportadas por este asistente. - + Presets solo frame Marco solo de presets - + Click & Go RGB RGB Click & Go - + Click & Go Macro Macro Click & Go @@ -3126,27 +3170,27 @@ p, li { white-space: pre-wrap; } GrandMasterSlider - + GM GM - + Grand Master <B>limits</B> the maximum value of Gran Master <B>limita</B> el valor máximo de - + Grand Master <B>reduces</B> the current value of Gran Master <B>reduce</B> el valor actual de - + intensity channels canales de intensidad - + all channels todos los canales @@ -3248,45 +3292,45 @@ p, li { white-space: pre-wrap; } InputOutputManager - + Add U&niverse Añadir U&niverso - + &Delete Universe Universe &Borrar Universo - + Universe name: Nombre del Universo: - + Passthrough Passthrough - - + + Universe %1 Universo %1 - - + + Delete Universe Borrar Universo - + The universe you are trying to delete is patched. Are you sure you want to delete it? El universo que está tratando de borrar está patcheado. ¿Está seguro de querer borrarlo? - + There are some fixtures using the universe you are trying to delete. Are you sure you want to delete it? Algunos fixtures están usando el universo que está tratando de borrar. ¿Está seguro de querer borrarlo? @@ -3393,20 +3437,20 @@ p, li { white-space: pre-wrap; } Monitor de nivel - - - + + + Error Error - - + + Output line already assigned Línea de Salida ya asignada - + An error occurred while trying to open the selected device line. This can be caused either by a wrong system configuration or an unsupported input/output mode. Please refer to the plugins documentation to troubleshoot this. @@ -3415,67 +3459,67 @@ Esto puede ser causado por una configuración errónea del sistema o un modo de Por favor, revise la documentación de los plugins para solucionar esto. - - + + Existing Input Profile Perfil de Entrada existente - - + + An input profile at %1 already exists. Do you wish to overwrite it? Un perfil de Entrada en %1 ya existe. ¿Desea sobreescribirlo? - - + + Save Input Profile Guardar el perfil de Entrada - - + + Input Profiles (*.qxi) Perfil de Entrada (*.qxi) - - + + Saving failed Error al Guardar - + Unable to save the profile to %1 Imposible guardar el perfil en %1 - + Delete profile Eliminar perfil - + Do you wish to permanently delete profile "%1"? ¿Desea borrar permanentemente este perfil "%1"? - + File deletion failed Error al eliminar archivo - + Unable to delete file %1 Imposible borrar el archivo %1 - + Unable to save %1 to %2 Imposible guardar %1 en %2 - + Default device Dispositivo por defecto @@ -3488,176 +3532,233 @@ Por favor, revise la documentación de los plugins para solucionar esto.Editor de Perfil de Entrada - + General General - + Manufacturer Fabricante - + The name of the company that made the device Nombre de la compañia - + Model Modelo - + The device's model name El nombre del modelo del dispositivo - - Channels - Canales - - - + + Channel Canal - + + Name Nombre - + Custom feedback Feedback personalizado - + Upper value Valor superior - + Lower value Valor inferior - - + + Type Tipo - + MIDI Global Settings Propiedades Globales MIDI - + When MIDI notes are used, send a Note Off when value is 0 Cuando notas MIDI están siendo usadas, enviar una Nota Off cuando el valor es 0 - - + + Input Mapping + + + + Behaviour Comportamiento - + Add a new channel description Añadir una nueva descripcion para el canal - + + + MIDI channel + + + + + Colors + + + + + Remove the selected color + + + + + Add a new color + + + + + Value + Valor + + + + Label + Etiqueta + + + + Color + + + + + MIDI Channels + + + + + Add a new MIDI channel + + + + + Remove the selected MIDI channel + + + + Remove the selected channels Eliminar los canales seleccionados - + Edit the selected channel Editar el canal seleccionado - + Automatically add channels to the list when you wiggle the device's controls Añadir automáticamente canales a la lista cuando mueva los controles del dispositivo - + Movement Movimiento - + Absolute Absoluto - + Relative Relativo - + Generate an extra Press/Release when toggled Generar comando extra de Presionar/Soltar cuando se active - + Sensitivity Sensibilidad - + File not writable Archivo sólo de lectura - + You do not have permission to write to the file %1. You might not be able to save your modifications to the profile. No tiene permiso para escribir en el archivo %1. Puede que no sea posible guardar las modificaciones al perfil. - + + From plugin settings + + + + Missing information Información Faltante - + Manufacturer and/or model name is missing. Falta el nombre del fabricante o modelo. - - + + Channel already exists El canal ya existe - - + + Channel %1 already exists Canal %1 ya existe - + Delete channels Eliminar canales - + Delete all %1 selected channels? ¿Eliminar los %1 canales seleccionados? - + 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. Note that the wizard cannot tell the difference between a knob and a slider so you will have to do the change manually. @@ -3666,12 +3767,39 @@ Note that the wizard cannot tell the difference between a knob and a slider so y Tenga en cuenta que el asistente no puede diferenciar entre una perilla y un slider, así que tendrá que hacer el cambio manualmente. - + + + Enter value + + + + + Feedback value + + + + + + Enter label + + + + + Color label + + + + + MIDI channel label + + + + Button %1 Botón %1 - + Slider %1 Slider %1 @@ -3704,65 +3832,50 @@ Tenga en cuenta que el asistente no puede diferenciar entre una perilla y un sli Entrada Externa - + When toggled, you can click an external button to assign it to this widget. Si está activo, puede presionar un botón externo para asignarlo a este widget. - + Auto Detect Detectar Automáticamente - + Input Universe Universo de Entrada - + Input Channel Canal de Entrada - + The input universe that sends data to this widget El universo de Entrada que manda datos a este widget - + Custom Feedback Feedback personalizado - + The particular input channel within the input universe that sends data to this widget El canal de Entrada dentro del universo de Entrada que manda datos a este widget - + Choose an external input universe & channel that this widget should listen to. Eligir un universo de Entrada y un canal externos que este widget debe escuchar. - + Choose... Elegir... - - - Custom feedback - Feedback personalizado - - - - Lower value - Valor inferior - - - - Upper value - Valor superior - Monitor @@ -3919,14 +4032,14 @@ Tenga en cuenta que el asistente no puede diferenciar entre una perilla y un sli Fondo - - + + Select background image Seleccione la imagen de fondo - - + + Images Imágenes @@ -4216,12 +4329,12 @@ Tenga en cuenta que el asistente no puede diferenciar entre una perilla y un sli PlaybackSlider - + Select Seleccionar - + Flash Flash @@ -4237,34 +4350,29 @@ Tenga en cuenta que el asistente no puede diferenciar entre una perilla y un sli QObject - + Operate Operación - + Design Diseño - - + + Reversed Invertido - + Page: %1 Página: %1 RDMManager - - - Form - Formulario - Scan for RDM devices... @@ -4684,22 +4792,22 @@ Tenga en cuenta que el asistente no puede diferenciar entre una perilla y un sli Ninguno - + No fixture group to control Ningún grupo para controlar - + Select image Seleccionar imagen - + Images Imágenes - + Sequence Secuencia @@ -4759,105 +4867,105 @@ Tenga en cuenta que el asistente no puede diferenciar entre una perilla y un sli Desactivar todos los canales de todos los fixtures - + Enable all channels in current fixture Activar todos los canales de este fixture - + Disable all channels in current fixture Desactivar todos los canales de este fixture - + Copy current values to clipboard Copiar los valores actuales en el portapapeles - + Paste clipboard values to current fixture Pegar los valores del portapapeles al fixture actual - + Copy current values to all fixtures Copiar los valores actuales a todos los fixtures - + Color tool for CMY/RGB-capable fixtures Herramienta de color para fixtures con capacidad CMY/RGB - + Position tool for moving heads/scanners Herramienta de posicionamiento para cabezas móviles o scanners - + Switch between tab view and all channels view Pasar de la vista de pestañas a la vista de canales - + Toggle blind mode Activar/Desactivar Modo Blind - + Show/Hide speed dial window Mostrar/Ocultar ventana de selector de velocidad - + Clone this scene and append as a new step to the selected chaser Clonar esta escena y añadirla como un nuevo paso en el chaser selecionado - + Go to next fixture tab Ir a la pestaña del siguiente fixture - + Go to previous fixture tab Ir a la pestaña del fixture anterior - + None Ninguno - + Scene name: Nombre de la Escena: + - All fixtures Todos los fixtures + - Channels Groups Grupos de Canales + - Generic Genérico - + Remove fixtures Eliminar fixtures - + Do you want to remove the selected fixture(s)? ¿Desea eliminar los fixtures seleccionados? @@ -5069,7 +5177,7 @@ Tenga en cuenta que el asistente no puede diferenciar entre una perilla y un sli Permitir universos sin patchear - + <Double click here to enter channel number manually> <Haga doble click aqui para ingresar manualmente el número del canal> @@ -5375,118 +5483,118 @@ Duración: %3 SimpleDesk - + Universe Universo - + Next page Página Siguiente - + Current page Página Actual - + Previous page Página Anterior - + View mode Modo Vista - + Reset universe Reiniciar universo - + Playback Reproducir - - + + Cue Stack Cue Stack - + Previous cue Cue Anterior - + Stop cue stack Detener cue stack - + Next cue Siguiente Cue - + Clone cue stack Clonar cue stack - + Edit cue stack Editar Cue Stack - + Record cue Guardar Cue - + Channel groups Grupos de canales - + Cue Stack - Playback %1 Cue Stack - Playback %1 - + No selection Ninguna selección - + Cue name Nombre del Cue - + Multiple Cues Múltiples Cues - + Delete cue Borrar Cue - + Clone Cue Stack Clonar Cue stack - + Clone To Playback# Clonar a Playback# - + Cue %1 Cue %1 @@ -5494,32 +5602,32 @@ Duración: %3 SpeedDial - + Hours Horas - + Minutes Minutos - + Seconds Segundos - + Milliseconds Milisegundos - + Infinite Infinito - + Tap Tap @@ -5527,17 +5635,17 @@ Duración: %3 SpeedDialWidget - + Fade In Fade In - + Fade Out Fade Out - + Hold Espera @@ -5627,17 +5735,17 @@ Duración: %3 VCButton - + Choose... Elegir... - + None Ninguno - + Button %1 Botón %1 @@ -5652,17 +5760,17 @@ Duración: %3 Imágenes (%1) - + Toggle Blackout Activar/Desactivar Blackout - + Stop ALL functions! ¡Detener TODAS las funciones! - + Icon Ícono @@ -5675,67 +5783,77 @@ Duración: %3 Propiedades del Botón - + + Override priority + + + + + Force LTP + + + + General General - + Button label Etiqueta del Botón - + Text to display on the button Texto a mostrar en el botón - + Function Función - + The function that this button controls La función que es controlada por este botón - + Attach a function to this button Enlazar un función a este botón - + Detach the button's function attachment Desenlazar la función de este botón - + Toggle Blackout Blackout On/Off - + Stop All Functions Detiene Todas las funciones - + Fade time: Duración de Fade: - + Adjust function intensity when it is running Ajusta la intensidad de la función cuando está en ejecución - + Adjust Function Intensity Ajustar la intensidad de la función - + Function's adjusted intensity percentage when run Porcentaje ajuste de intensidad de la función cuando se ejecuta @@ -5755,17 +5873,17 @@ Duración: %3 Modo Interruptor - + Flash the assigned function with this button Activa la función asignada mientras el botón se encuentra presionado - + Flash function (only for scenes) Modo Flash (sólo para escenas) - + No function Ninguna función @@ -5856,64 +5974,64 @@ Duración: %3 VCCueList - + Show/Hide crossfade sliders Mostrar/ocultar los sliders de crossfade - - + + Play/Pause Cue list Reproducir/Pausar Lista de Cues - - + + Stop Cue list Detener Lista de Cues - + Go to previous step in the list Ir al paso anterior en la lista - + Go to next step in the list Ir al siguiente paso en la lista - + Cue list Lista de Cues - + Play/Stop Cue list Reproducir/Detener Lista de Cues - + Pause Cue list Pausar Lista de Cues - + Fade In Fade In - + Fade Out Fade Out - + Duration Duración - + Notes Notas @@ -6079,7 +6197,7 @@ Duración: %3 VCFrame - + Add Añadir @@ -6132,17 +6250,17 @@ Duración: %3 Nombre de la página - + External Input - Enable Entrada externa - Hablilitar - + External Input - Previous Page Entrada Externa - Página previa - + External Input - Next Page Entrada Externa - Siguiente página @@ -6175,17 +6293,17 @@ Duración: %3 VCLabel - + Label Etiqueta - + Rename Label Renombrar Etiqueta - + Caption: Título: @@ -6193,42 +6311,42 @@ Duración: %3 VCMatrix - + Animation %1 Animación %1 - + End Color Reset Restablecer el color final - + Start color Red component Componente Rojo del color inicial - + Start color Green component Componente Verde del color inicial - + Start color Blue component Componente Azul del color inicial - + End color Red component Componente Rojo del color final - + End color Green component Componente Verde del color final - + End color Blue component Componente Azul del color final @@ -6431,48 +6549,48 @@ Duración: %3 Añadir texto - + No function Ninguna función - + Start Color Color de inicio - + Start Color Knob Perilla de color inicial - + End Color Color de finalización - + End Color Knob Perilla de color final - + End Color Reset Restablecer color final - + Animation Animación - - + + Text Texto - + Enter a text Ingresar texto @@ -6724,12 +6842,12 @@ Duración: %3 VCSlider - + Slider %1 Slider %1 - + Reset channels override Restaurar el override de canales @@ -6747,42 +6865,42 @@ Duración: %3 General - + Name of the slider Nombre del slider - + Value display style Estilo de Visualización de los Valores - + Show exact DMX values Mostrar el valor DMX exacto - + Show value as percentage Mostrar el valor en porcentaje - + Percentage Porcentaje - + Slider movement Movimiento del slider - + Normal Normal - + Inverted Invertido @@ -6858,32 +6976,32 @@ Duración: %3 Cambiar a Modo Reproducción - + Actual Actual - + Widget name Nombre del widget - + Widget appearance Apariencia del widget - + Slider Slider - + Knob Perilla - + Catch up with the external controller input value Actualizar con el valor de entrada del controlador externo @@ -7281,17 +7399,17 @@ Duración: %3 Mostrar el campo de milisegundos - + Multiply by 2 Input Entrada del Multiplicador por 2 - + Divide by 2 Input Entrada del Divisor por 2 - + Factor Reset Input Entrada de Reinicio de Factor @@ -7299,68 +7417,68 @@ Duración: %3 VCWidget - + Button Botón - + Slider Slider - + XYPad XY Pad - + Frame Marco - + Solo frame Marco Solo - + Speed dial Selector de velocidad - + Cue list Lista de Cues - + Label Etiqueta - + Audio Triggers Disparos de Audio - + Animation Animación - + Clock Reloj + - Unknown Desconocido - + This widget has no properties Este widget no tiene propiedades @@ -7558,12 +7676,12 @@ Duración: %3 Nombre del preset - + Pan / Horizontal Axis Pan /Eje horizontal - + Tilt / Vertical Axis Tilt / Eje vertical @@ -7593,45 +7711,45 @@ Duración: %3 Editar los ejes del fixture seleccionado - + Width Ancho - + Height Alto - + Remove fixtures Quitar fixtures - + Do you want to remove the selected fixtures? ¿Desea eleminar el fixture seleccionado? - - + + Error Error - + The selected Scene does not include any Pan or Tilt channel. Please select one with such channels. La Escena elegida no incluye ningún canal de Pan o Tilt. Por favor elija una que tenga esos canales. - + Please select at least one fixture or head to create this type of preset! Por favor, ¡seleccionar al menos un fixture o cabeza para crear este tipo de preset! - + Fixture Group Grupo de Fixtures @@ -7764,12 +7882,12 @@ Por favor elija una que tenga esos canales. VideoItem - + Fullscreen Pantalla completa - + Screen %1 Pantalla %1 diff --git a/ui/src/qlcplus_fi_FI.ts b/ui/src/qlcplus_fi_FI.ts index 6cf8b97956..6a1e0724a7 100644 --- a/ui/src/qlcplus_fi_FI.ts +++ b/ui/src/qlcplus_fi_FI.ts @@ -189,12 +189,12 @@ Jätä valaisinten kanavien väliin näin monta tyhjää kanavaa - + Fixtures found: %1 - + Dimmers Himmentimet @@ -525,375 +525,375 @@ App - + Cannot exit in Operate mode Ei voida sulkea Käyttötilassa - + You must switch back to Design mode to close the application. Sinun täytyy vaihtaa takaisin Suunnittelu-tilaan sulkeaksesi sovelluksen. - + Close Sulje - + Do you wish to save the current workspace before closing the application? Haluatko tallentaa nykyisen työtilan ennen sovelluksen sulkemista? - + Starting Q Light Controller Plus Starting Q Light Controller Käynnistetään Q Light Controller - + - New Workspace - Uusi työtila - + Switch to Design Mode Vaihda Suunnittelutilaan - + There are still running functions. Really stop them and switch back to Design mode? Joitain funktioita on vielä ajossa. Haluatko varmasti pysäyttää ne ja vaihtaa takaisin Suunnittelutilaan? - + Design Suunnittelu - + Switch to design mode Vaihda Suunnittelutilaan - + Operate Käyttötila - - + + Switch to operate mode Vaihda Käyttötilaan - + &New &Uusi - + CTRL+N File|New - + &Open &Avaa - + CTRL+O File|Open - + &Save &Tallenna - + CTRL+S File|Save - + Save &As... Tallenna &nimellä... - + &Operate &Käyttötila - + &Monitor &Monitorointi - + Toggle &Blackout Kytke &Pimennys - + CTRL+F12 Control|Toggle operate/design mode - + CTRL+M Control|Monitor - + Live edit a function - + Toggle Full Screen Kytke koko näyttö - + CTRL+F11 Control|Toggle Full Screen - + &Index &Hakemisto - + SHIFT+F1 Help|Index - + &About QLC+ &About QLC Ti&etoja QLC:stä - + Fixtures Valaisimet - + Functions - + Shows - + Virtual Console Virtuaalikonsoli - + Simple Desk - + Inputs/Outputs - + Close the application? - + Do you wish to close the application? - + Exit - + Address Tool - + Toggle Virtual Console Live edit - + Dump DMX values to a function - + CTRL+D Control|Dump DMX - + Stop ALL functions! Pysäytä KAIKKI funktiot! - + Fade 1 second and stop - + Fade 5 seconds and stop - + Fade 10 second and stop - + Fade 30 second and stop - + Quit QLC+ - + Workspace Työtila - + Unable to read from file Tiedostoa ei voida lukea - + Unable to write to file Tiedostoon ei voida kirjoittaa - + A fatal error occurred Peruuttamaton virhe on tapahtunut - + Unable to access resource Resurssiin ei voida käsitellä - + Unable to open file for reading or writing Tiedostoa ei voida avata lukemista tai kirjoittamista varten - + Operation was aborted Toiminto peruutettiin - + Operation timed out Toiminto aikakatkaistiin - + An unspecified error has occurred. Nice. Määrittelemätön virhe on tapahtunut. Siistiä. - + File error Tiedostovirhe - - - + + + Do you wish to save the current workspace? Changes will be lost if you don't save them. Haluatko tallentaa nykyisen työtilan? Menetät muutokset jos et tallenna niitä. - + New Workspace Uusi työtila - - - + + + Open Workspace Avaa työtila - - + + Workspaces (*%1) Työtilat (*%1) - - + + All Files (*.*) Kaikki tiedostot (*.*) - - + + All Files (*) Kaikki tiedostot (*) - + Save Workspace As Tallenna työtila nimellä - + Error - + File not found! The selected file has been moved or deleted. - + Warning - + Some errors occurred while loading the project: @@ -916,12 +916,12 @@ The selected file has been moved or deleted. Sulje automaattisesti näppäintä painettaessa - + Assign Key Aseta näppäin - + Hit the key combination that you wish to assign. You may hit either a single key or a combination using %1, %2, and %3. @@ -1048,23 +1048,23 @@ The selected file has been moved or deleted. AudioItem - - + + Preview Left Channel - + Preview Right Channel - + Preview Stereo Channels - + Preview Mono @@ -1132,52 +1132,52 @@ The selected file has been moved or deleted. Sisääntulo - + None Ei mitään - + DMX - + Function Funktio - + VC Widget - + %1 channels - + No function Ei funktiota - + No widget - + Not assigned - + Volume Bar - + #%1 (%2Hz - %3Hz) @@ -1235,12 +1235,12 @@ The selected file has been moved or deleted. - + Error - + You are trying to overwrite a system template! Please choose another name and the template will be saved in your channel modifier's user folder. @@ -1255,13 +1255,13 @@ The selected file has been moved or deleted. - + Name Nimi - + Type Tyyppi @@ -1281,27 +1281,27 @@ The selected file has been moved or deleted. - + Selected - + Channel properties configuration - + Can fade - + Behaviour - + Modifier @@ -1335,13 +1335,13 @@ The selected file has been moved or deleted. - + Fade In - + Fade Out @@ -1382,7 +1382,7 @@ The selected file has been moved or deleted. - + Hold @@ -1557,47 +1557,47 @@ The selected file has been moved or deleted. Takaperin - + Cut Leikkaa - + Copy Kopioi - + Paste Liitä - + Paste error - + Trying to paste on an incompatible Scene. Operation canceled. - + Common Fade In - + Common Fade Out - + Common Hold - + Multiple Steps @@ -1653,12 +1653,12 @@ The selected file has been moved or deleted. ConsoleChannel - + Intensity Intensiteetti - + Reset this channel @@ -1719,6 +1719,83 @@ The selected file has been moved or deleted. + + CustomFeedbackDialog + + + Custom Feedback Configuration + + + + + Value + + + + + Label + Etiketti + + + + Color + + + + + Values + Arvot + + + + Lower Value + + + + + Monitor Value + + + + + Upper Value + + + + + + + Color Selection + + + + + MIDI Channel + + + + + Upper Channel + + + + + Lower Channel + + + + + Monitor Channel + + + + + + + From plugin settings + + + DmxDumpFactory @@ -1778,50 +1855,17 @@ The selected file has been moved or deleted. - + Dump all channels (%1 Universes, %2 Fixtures, %3 Channels) Dump all DMX values (%1 Universes, %2 Fixtures, %3 Channels) - + New Scene From Live %1 - - DocBrowser - - - %1 - Document Browser - %1 - ohjeiden selaus - - - - Backward - Takaisin - - - - Forward - Eteen - - - - Index - Hakemisto - - - - About Qt - - - - - Close this window - - - EFXEditor @@ -2102,12 +2146,12 @@ The selected file has been moved or deleted. - + Remove fixtures Poista valaisimia - + Do you want to remove the selected fixture(s)? Haluatko poistaa valitut valaisimet? @@ -2452,88 +2496,88 @@ The selected file has been moved or deleted. - - + + (remapped) - + Import Fixtures List - + Fixtures List (*%1) - + All Files (*.*) Kaikki tiedostot (*.*) - + All Files (*) Kaikki tiedostot (*) - + Do you want to automatically connect fixtures with the same name? - + Generic Dimmer Yleinen himmennin - + Delete Fixtures Poista valaisimia - + Do you want to delete the selected items? - + Invalid operation - + You are trying to clone a fixture on an address already in use. Please fix the target list first. - - - - + + + + Invalid selection - - - + + + Please select a source and a target fixture or channel to perform this operation. - + To perform a fixture remap, please select fixtures on both lists. - + This might take a while... - + Cancel @@ -2546,12 +2590,12 @@ The selected file has been moved or deleted. Valitse valaisin - + No fixtures available Ei valaisimia saatavilla - + Go to the Fixture Manager and add some fixtures first. Mene Laitehallintaan luodaksesi ensin valaisimia. @@ -2608,7 +2652,7 @@ The selected file has been moved or deleted. FunctionLiveEditDialog - + Function Live Edit @@ -2616,195 +2660,195 @@ The selected file has been moved or deleted. FunctionManager - + New &scene Uusi &tilanne - + New c&haser Uusi &juoksutus - + New se&quence - + New c&ollection Uusi &kokoelma - + New E&FX Uusi &EFX - + New &RGB Matrix - + New scrip&t - + New Scene Uusi tilanne - + New Chaser Uusi juoksutus - + New Sequence - + &Clone K&loonaa - + New au&dio - + New vid&eo - + New fo&lder - + Select Startup Function - + Function &Wizard - + &Delete &Poista - + Select &all Valitse k&aikki - + New Collection - + New EFX - + New RGB Matrix - + New Script - + Open Audio File - + Audio Files (%1) - - + + All Files (*.*) Kaikki tiedostot (*.*) - - + + All Files (*) Kaikki tiedostot (*) - + Unsupported audio file - + This audio file cannot be played with QLC+. Sorry. - + Open Video File - + Video Files (%1) - + Unsupported video file - + This video file cannot be played with QLC+. Sorry. - + Do you want to DELETE folder: Do you want to DELETE foler: - + Do you want to DELETE functions: Haluatko POISTAA funktiot: - + (This will also DELETE: - + Delete Functions Poista funktioita - + Function Funktio - + (Copy) @@ -3067,32 +3111,32 @@ p, li { white-space: pre-wrap; } Poista - + %1 group - + Error - + %1 has no capability supported by this wizard. - + Presets solo frame - + Click & Go RGB - + Click & Go Macro @@ -3100,27 +3144,27 @@ p, li { white-space: pre-wrap; } GrandMasterSlider - + GM - + Grand Master <B>limits</B> the maximum value of - + Grand Master <B>reduces</B> the current value of - + intensity channels intensiteettikanaville - + all channels kaikille kanaville @@ -3221,45 +3265,45 @@ p, li { white-space: pre-wrap; } InputOutputManager - + Add U&niverse - + &Delete Universe Universe Universumi - + Universe name: - + Passthrough - - + + Universe %1 - - + + Delete Universe - + The universe you are trying to delete is patched. Are you sure you want to delete it? - + There are some fixtures using the universe you are trying to delete. Are you sure you want to delete it? @@ -3366,87 +3410,87 @@ p, li { white-space: pre-wrap; } - - - + + + Error - - + + Output line already assigned - + An error occurred while trying to open the selected device line. This can be caused either by a wrong system configuration or an unsupported input/output mode. Please refer to the plugins documentation to troubleshoot this. - - + + Existing Input Profile Olemassaoleva sisääntuloprofiili - - + + An input profile at %1 already exists. Do you wish to overwrite it? Sisääntuloprofiili %1 on jo olemassa. Haluatko ylikirjoittaa sen? - - + + Save Input Profile Tallenna sisääntuloprofiili - - + + Input Profiles (*.qxi) Sisääntuloprofiilit (*.qxi) - - + + Saving failed Tallennus epäonnistui - + Unable to save the profile to %1 Profiilia ei voida tallentaa tiedostoon %1 - + Delete profile Poista profiili - + Do you wish to permanently delete profile "%1"? Haluat poistaa profiilin %1 pysyvästi? - + File deletion failed Tiedoston poisto epäonnistui - + Unable to delete file %1 Tiedostoa %1 ei voida poistaa - + Unable to save %1 to %2 Profiilia %1 ei voida tallentaa tiedostoon %2 - + Default device @@ -3459,176 +3503,233 @@ Please refer to the plugins documentation to troubleshoot this. Sisääntulon profiilin muokkaus - + General Yleinen - + Manufacturer Valmistaja - + The name of the company that made the device Laitteen valmistaneen yrityksen nimi - + Model Malli - + The device's model name Laitteen mallin nimi - - Channels - Kanavat - - - + + Channel Kanava - + + Name Nimi - - + + Type Tyyppi - + MIDI Global Settings - + When MIDI notes are used, send a Note Off when value is 0 - - + + Input Mapping + + + + Behaviour - + Movement Liike - + Absolute - + Relative - + Generate an extra Press/Release when toggled - + Custom feedback - + Upper value - + + + MIDI channel + + + + + Colors + + + + + Remove the selected color + + + + + Add a new color + + + + + Value + + + + + Label + Etiketti + + + + Color + + + + + MIDI Channels + + + + + Add a new MIDI channel + + + + + Remove the selected MIDI channel + + + + Lower value - + Sensitivity - + Add a new channel description Lisää uusi kanavakuvaus - + Remove the selected channels Poista valitut kanavat - + Edit the selected channel Muokkaa valittua kanavaa - + Automatically add channels to the list when you wiggle the device's controls Lisää kanavia listaan automaattisesti kun laitteen toimintoja käytetään - + File not writable Tiedoston ei voida kirjoittaa - + You do not have permission to write to the file %1. You might not be able to save your modifications to the profile. Sinulla ei ole oikeuksia kirjoittaa tiedostoon %1. Et ehkä voi tallettaa muutoksiasi valittuun profiiliin. - + + From plugin settings + + + + Missing information Puutteelliset tiedot - + Manufacturer and/or model name is missing. Valmistaja ja/tai mallinimike puuttuu. - - + + Channel already exists Kanava on jo olemassa - - + + Channel %1 already exists Kanava %1 on jo olemassa - + Delete channels Poista kanavia - + Delete all %1 selected channels? Poistetaanko kaikki %1 valittua kanavaa? - + Channel wizard activated Kanavavelho on aktiivisena - + 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. @@ -3637,12 +3738,39 @@ Note that the wizard cannot tell the difference between a knob and a slider so y Huomaa, että velho ei osaa erottaa pyöritettävää nuppia ja liukua toisistaan, joten sinun täytyy tehdä vastaavat muutokset käsin. - + + + Enter value + + + + + Feedback value + + + + + + Enter label + + + + + Color label + + + + + MIDI channel label + + + + Button %1 Nappi %1 - + Slider %1 Liuku %1 @@ -3675,65 +3803,50 @@ Huomaa, että velho ei osaa erottaa pyöritettävää nuppia ja liukua toisistaa Ulkoinen ohjaus - + When toggled, you can click an external button to assign it to this widget. Alaskytkettynä voit painaa ulkoista kontrollinappia kytkeäksesi sen tähän virtuaalinappiin. - + Auto Detect Automaattinen tunnistus - + Input Universe Sisääntulon universumi - + Input Channel Sisääntulokanava - + The input universe that sends data to this widget - + Custom Feedback - + The particular input channel within the input universe that sends data to this widget - + Choose an external input universe & channel that this widget should listen to. Valitse ulkoinen sisääntulouniversumi ja -kanava, jota tämä nappi kuuntelee. - + Choose... Valitse... - - - Custom feedback - - - - - Lower value - - - - - Upper value - - Monitor @@ -3890,14 +4003,14 @@ Huomaa, että velho ei osaa erottaa pyöritettävää nuppia ja liukua toisistaa - - + + Select background image Valitse taustakuva - - + + Images @@ -4187,12 +4300,12 @@ Huomaa, että velho ei osaa erottaa pyöritettävää nuppia ja liukua toisistaa PlaybackSlider - + Select - + Flash @@ -4208,34 +4321,29 @@ Huomaa, että velho ei osaa erottaa pyöritettävää nuppia ja liukua toisistaa QObject - + Operate Käyttötila - + Design Suunnittelu - - + + Reversed Käänteinen - + Page: %1 RDMManager - - - Form - - Scan for RDM devices... @@ -4655,22 +4763,22 @@ Huomaa, että velho ei osaa erottaa pyöritettävää nuppia ja liukua toisistaa Ei mitään - + No fixture group to control - + Select image - + Images - + Sequence @@ -4730,105 +4838,105 @@ Huomaa, että velho ei osaa erottaa pyöritettävää nuppia ja liukua toisistaa Kytke pois kaikkien valaisinten kanavat - + Enable all channels in current fixture Käytä kaikkia valitun valaisimen kanavia - + Disable all channels in current fixture Poista käytöstä valitun valaisimen kaikki kanavat - + Copy current values to clipboard Kopio nykyiset arvot leikepöydälle - + Paste clipboard values to current fixture Liitä leikepöydälle tallennetut arvot nykyiselle valaisimelle - + Copy current values to all fixtures Kopioi nykyisen valaisimen arvot kaikkiin valittuihin valaisimiin - + Color tool for CMY/RGB-capable fixtures Värityökalu CMY/RGB-toiminnolla varustettuja valaisimia varten - + Position tool for moving heads/scanners - + Switch between tab view and all channels view - + Toggle blind mode - + Show/Hide speed dial window - + Clone this scene and append as a new step to the selected chaser - + Go to next fixture tab - + Go to previous fixture tab - + None Ei mitään - + Scene name: + - All fixtures + - Channels Groups + - Generic Yleinen - + Remove fixtures Poista valaisimia - + Do you want to remove the selected fixture(s)? Haluatko poistaa valitut valaisimet? @@ -5037,7 +5145,7 @@ Huomaa, että velho ei osaa erottaa pyöritettävää nuppia ja liukua toisistaa - + <Double click here to enter channel number manually> <Kaksoisnapauta tähän kirjoittaaksesi kanavanumeron käsin> @@ -5340,118 +5448,118 @@ Duration: %3 SimpleDesk - + Universe Universumi - + Next page - + Current page - + Previous page - + View mode - + Reset universe - + Playback Toisto - - + + Cue Stack - + Previous cue - + Stop cue stack - + Next cue - + Clone cue stack - + Edit cue stack - + Record cue - + Channel groups - + Cue Stack - Playback %1 - + No selection - + Cue name - + Multiple Cues - + Delete cue - + Clone Cue Stack - + Clone To Playback# - + Cue %1 @@ -5459,32 +5567,32 @@ Duration: %3 SpeedDial - + Hours - + Minutes - + Seconds - + Milliseconds - + Infinite - + Tap @@ -5492,17 +5600,17 @@ Duration: %3 SpeedDialWidget - + Fade In - + Fade Out - + Hold @@ -5592,17 +5700,17 @@ Duration: %3 VCButton - + Choose... Valitse... - + None Ei mitään - + Button %1 Nappi %1 @@ -5617,17 +5725,17 @@ Duration: %3 Kuvatiedostot (%1) - + Toggle Blackout - + Stop ALL functions! Pysäytä KAIKKI funktiot! - + Icon Ikoni @@ -5640,67 +5748,77 @@ Duration: %3 Napin asetukset - + + Override priority + + + + + Force LTP + + + + General Yleinen - + Button label Napin teksti - + Text to display on the button Teksti, joka näytetään napissa - + Function Funktio - + The function that this button controls Funktio, jota tämä nappi ohjaa - + Attach a function to this button Liitä funktio tähän nappiin - + Detach the button's function attachment Poista funktio tästä napista - + Toggle Blackout - + Stop All Functions - + Fade time: - + Adjust function intensity when it is running Aseta funktion ajonaikainen intensiteetti - + Adjust Function Intensity Aseta funktion intensiteettiä - + Function's adjusted intensity percentage when run Funktion ajonaikainen intensiteetti prosentteina @@ -5720,17 +5838,17 @@ Duration: %3 Kytke funktio vuorottain päälle/pois - + Flash the assigned function with this button Väläytä liitettyä funktiota painamalla nappi alas - + Flash function (only for scenes) Väläytä funktiota (käytössä vain tilanteille) - + No function Ei funktiota @@ -5821,64 +5939,64 @@ Duration: %3 VCCueList - + Show/Hide crossfade sliders - - + + Play/Pause Cue list - - + + Stop Cue list - + Go to previous step in the list - + Go to next step in the list - + Cue list Cue lista - + Play/Stop Cue list - + Pause Cue list - + Fade In - + Fade Out - + Duration - + Notes @@ -6044,7 +6162,7 @@ Duration: %3 VCFrame - + Add Lisää @@ -6097,17 +6215,17 @@ Duration: %3 - + External Input - Enable - + External Input - Previous Page - + External Input - Next Page @@ -6140,17 +6258,17 @@ Duration: %3 VCLabel - + Label Etiketti - + Rename Label - + Caption: Teksti: @@ -6158,42 +6276,42 @@ Duration: %3 VCMatrix - + Animation %1 - + End Color Reset - + Start color Red component - + Start color Green component - + Start color Blue component - + End color Red component - + End color Green component - + End color Blue component @@ -6396,48 +6514,48 @@ Duration: %3 - + No function Ei funktiota - + Start Color - + Start Color Knob - + End Color - + End Color Knob - + End Color Reset - + Animation - - + + Text - + Enter a text @@ -6689,12 +6807,12 @@ Duration: %3 VCSlider - + Slider %1 Liuku %1 - + Reset channels override @@ -6712,42 +6830,42 @@ Duration: %3 Yleinen - + Name of the slider Liukukomponentin nimi - + Value display style Arvon näyttötapa - + Show exact DMX values Näytä tarkat DMX tai aika-arvot - + Show value as percentage Näytä arvo prosentteina - + Percentage Prosentti - + Slider movement Liu'un liike - + Normal Normaali - + Inverted Käänteinen @@ -6823,32 +6941,32 @@ Duration: %3 Vaihda toisto-tilaan - + Actual - + Widget name - + Widget appearance - + Slider - + Knob - + Catch up with the external controller input value @@ -7246,17 +7364,17 @@ Duration: %3 - + Multiply by 2 Input - + Divide by 2 Input - + Factor Reset Input @@ -7264,68 +7382,68 @@ Duration: %3 VCWidget - + Button - + Slider - + XYPad - + Frame Kehys - + Solo frame - + Speed dial - + Cue list Cue lista - + Label Etiketti - + Audio Triggers - + Animation - + Clock + - Unknown Tuntematon - + This widget has no properties Tällä komponentilla ei ole ominaisuuksia @@ -7533,12 +7651,12 @@ Duration: %3 - + Pan / Horizontal Axis - + Tilt / Vertical Axis @@ -7558,44 +7676,44 @@ Duration: %3 Käänteinen - + Width Leveys - + Height Korkeus - + Remove fixtures Poista valaisimia - + Do you want to remove the selected fixtures? Haluatko poistaa valitut valaisimet? - - + + Error - + The selected Scene does not include any Pan or Tilt channel. Please select one with such channels. - + Please select at least one fixture or head to create this type of preset! - + Fixture Group @@ -7728,12 +7846,12 @@ Please select one with such channels. VideoItem - + Fullscreen - + Screen %1 diff --git a/ui/src/qlcplus_fr_FR.ts b/ui/src/qlcplus_fr_FR.ts index e0782a5464..62c7095d9f 100644 --- a/ui/src/qlcplus_fr_FR.ts +++ b/ui/src/qlcplus_fr_FR.ts @@ -189,12 +189,12 @@ Le nombre de canaux vides à laisser entre chaque 'appareil - + Fixtures found: %1 Appareils trouvés : %1 - + Dimmers Gradateurs @@ -525,318 +525,318 @@ App - + Cannot exit in Operate mode Impossible de quitter en mode Production - + You must switch back to Design mode to close the application. Vous devez basculer vers le mode Création pour quitter l'application. - + Close Quitter - + Do you wish to save the current workspace before closing the application? Voulez-vous enregistrer le projet en cours avant de quitter l'application ? - + Starting Q Light Controller Plus Starting Q Light Controller Démarrage de Q Light Controller Plus - + - New Workspace - Nouveau projet - + Switch to Design Mode Basculer vers le mode Création - + There are still running functions. Really stop them and switch back to Design mode? Des fonctions sont encore en cours d'exécution. Voulez-vous vraiment les arrêter et basculer vers le mode Création ? - + Design Création - + Switch to design mode Basculer vers le mode Création - + Operate Production - - + + Switch to operate mode Basculer vers le mode Production - + &New &Nouveau - + CTRL+N File|New - + &Open &Ouvrir - + CTRL+O File|Open - + &Save Enregi&strer - + CTRL+S File|Save - + Save &As... Enregistrer sous (&A) ... - + &Operate &Produire - + &Monitor &Moniteur - + Toggle &Blackout &Blackout - + CTRL+F12 Control|Toggle operate/design mode - + CTRL+M Control|Monitor - + Live edit a function Éditer une fonction en direct - + Toggle Full Screen Plein écran - + CTRL+F11 Control|Toggle Full Screen - + &Index A&ide - + SHIFT+F1 Help|Index - + &About QLC+ &About QLC &À propos de QLC+ - + Fixtures Appareils - + Functions Fonctions - + Shows Shows - + Virtual Console Console virtuelle - + Simple Desk Pupitre traditionnel - + Inputs/Outputs Entrées/Sorties - + Close the application? Quitter QLCPlus ? - + Do you wish to close the application? Voulez-vous quitter l'application ? - + Exit Quitter - + Address Tool Outil d'adressage - + Toggle Virtual Console Live edit Basculer l'édition live de la console virtuelle - + Dump DMX values to a function Capturer les valeurs DMX vers une fonction - + CTRL+D Control|Dump DMX CTRL+D - + Stop ALL functions! Arrêter TOUTES les fonctions ! - + Fade 1 second and stop Fondu d'1 s en sortie - + Fade 5 seconds and stop Fondu de 5 s en sortie - + Fade 10 second and stop Fondu de 10 s en sortie - + Fade 30 second and stop Fondu de 30 s en sortie - + Quit QLC+ Quitter QLC+ - + Workspace Projet - + Unable to read from file Impossible de lire le fichier - + Unable to write to file Impossible d'écrire dans le fichier - + A fatal error occurred ...la vilaine! Une erreur fatale est survenue - + Unable to access resource Impossible d'accéder à la ressource - + Unable to open file for reading or writing Impossible d'ouvrir le fichier en lectue ou écriture - + Operation was aborted L'opération a été abandonnée - + Operation timed out L'opération a pris trop de temps - + An unspecified error has occurred. Nice. Une erreur indeterminée est survenue, sympa. - + File error Erreur de fichier - - - + + + Do you wish to save the current workspace? Changes will be lost if you don't save them. Tiens tiens...perspicace! @@ -844,59 +844,59 @@ Changes will be lost if you don't save them. Les changements seront perdus si vous ne les sauvegardez pas. - + New Workspace Nouveau projet - - - + + + Open Workspace Ouvrir un projet - - + + Workspaces (*%1) Projets (*%1) - - + + All Files (*.*) Tous les fichiers (*.*) - - + + All Files (*) Tous les fichiers (*) - + Save Workspace As Enregistrer le projet sous - + Error Erreur - + File not found! The selected file has been moved or deleted. Fichier introuvable  Celui-ci a dû être déplacé ou effacé. - + Warning Attention - + Some errors occurred while loading the project: Des erreurs sont survenues lors du chargement du projet : @@ -919,12 +919,12 @@ Celui-ci a dû être déplacé ou effacé. Fermer la fenêtre automatiquement sur appui touche - + Assign Key Assigner une touche - + Hit the key combination that you wish to assign. You may hit either a single key or a combination using %1, %2, and %3. Presser le raccourci clavier que vous voulez assigner. Vous pouvez presser une seule touche ou une combinaison utilisant %1, %2 et %3. @@ -1051,23 +1051,23 @@ Celui-ci a dû être déplacé ou effacé. AudioItem - - + + Preview Left Channel Prévisualiser le canal gauche - + Preview Right Channel Prévisualiser le canal droit - + Preview Stereo Channels Prévisualiser les deux canaux (stéréo) - + Preview Mono Prévisualiser le canal (mono) @@ -1135,52 +1135,52 @@ Celui-ci a dû être déplacé ou effacé. Entrée - + None Aucun - + DMX DMX - + Function Fonction - + VC Widget Widget - + %1 channels %1 canaux - + No function Aucune fonction - + No widget Aucun widget - + Not assigned Non assigné - + Volume Bar Barre de volume - + #%1 (%2Hz - %3Hz) @@ -1240,12 +1240,12 @@ Celui-ci a dû être déplacé ou effacé. Désactiver le modificateur - + Error Erreur - + You are trying to overwrite a system template! Please choose another name and the template will be saved in your channel modifier's user folder. Vous tentez d'écraser un modèle système ! Merci de choisir un nom différent et le modèle sera enregistré dans votre dossier personnel. @@ -1260,13 +1260,13 @@ Celui-ci a dû être déplacé ou effacé. - + Name Nom - + Type Type @@ -1286,27 +1286,27 @@ Celui-ci a dû être déplacé ou effacé. Tout déplier - + Selected Sélectionné - + Channel properties configuration Configuration des propriétés du canal - + Can fade Graduable - + Behaviour Comportement - + Modifier Modificateur @@ -1340,13 +1340,13 @@ Celui-ci a dû être déplacé ou effacé. - + Fade In Montée - + Fade Out Descente @@ -1367,7 +1367,7 @@ Celui-ci a dû être déplacé ou effacé. - + Hold Maintien @@ -1562,47 +1562,47 @@ Celui-ci a dû être déplacé ou effacé. Arrière - + Cut Couper - + Copy Copier - + Paste Coller - + Paste error Erreur de collage - + Trying to paste on an incompatible Scene. Operation canceled. La scène vers laquelle coller est incompatible. Opération annulée. - + Common Fade In Montée commune - + Common Fade Out Descente commune - + Common Hold Maintien commun - + Multiple Steps Pas multiples @@ -1658,12 +1658,12 @@ Celui-ci a dû être déplacé ou effacé. ConsoleChannel - + Intensity Intensité - + Reset this channel Réinitialiser ce canal @@ -1724,6 +1724,83 @@ Celui-ci a dû être déplacé ou effacé. Mémoire + + CustomFeedbackDialog + + + Custom Feedback Configuration + + + + + Value + Valeur + + + + Label + Étiquette + + + + Color + + + + + Values + Valeurs + + + + Lower Value + + + + + Monitor Value + + + + + Upper Value + + + + + + + Color Selection + + + + + MIDI Channel + + + + + Upper Channel + + + + + Lower Channel + + + + + Monitor Channel + + + + + + + From plugin settings + + + DmxDumpFactory @@ -1783,50 +1860,17 @@ Celui-ci a dû être déplacé ou effacé. Nom de la scène : - + Dump all channels (%1 Universes, %2 Fixtures, %3 Channels) Dump all DMX values (%1 Universes, %2 Fixtures, %3 Channels) Capturer tous les canaux (%1 univers, %2 appareils, %3 canaux) - + New Scene From Live %1 Nouvelle scène live %1 - - DocBrowser - - - %1 - Document Browser - %1 - Explorateur de document - - - - Backward - Arrière - - - - Forward - Avant - - - - Index - Aide - - - - About Qt - À propos de Qt - - - - Close this window - Fermer cette fenêtre - - EFXEditor @@ -2110,12 +2154,12 @@ Celui-ci a dû être déplacé ou effacé. Prévisualiser l'EFX - + Remove fixtures Enlever les appareils - + Do you want to remove the selected fixture(s)? Voulez-vous enlever le(s) appareil(s) sélectionné(s) ? @@ -2460,88 +2504,88 @@ Celui-ci a dû être déplacé ou effacé. Reprendre le nom des appareils sources - - + + (remapped) (remappé) - + Import Fixtures List Importer une liste d'appareils - + Fixtures List (*%1) Liste d'appareils (*%1) - + All Files (*.*) Tous les fichiers (*.*) - + All Files (*) Tous les fichiers (*) - + Do you want to automatically connect fixtures with the same name? - + Generic Dimmer Gradateur générique - + Delete Fixtures Supprimer un élément - + Do you want to delete the selected items? Voulez-vous supprimer l'élément cible sélectionné ? - + Invalid operation Opération invalide - + You are trying to clone a fixture on an address already in use. Please fix the target list first. L'adresse de l'appareil à dupliquer est déjà utilisée. Veuillez d'abord modifier la liste des appareils cibles. - - - - + + + + Invalid selection Sélection invalide - - - + + + Please select a source and a target fixture or channel to perform this operation. Veuillez d'abord sélectionner un appareil ou un canal dans la liste des sources et des cibles. - + To perform a fixture remap, please select fixtures on both lists. Veuillez sélectionner soit un appareil soit un canal dans les deux listes. - + This might take a while... Ceci peut prendre un moment... - + Cancel Annuler @@ -2554,12 +2598,12 @@ Celui-ci a dû être déplacé ou effacé. Sélectionner un appareil - + No fixtures available Aucun appareil disponible - + Go to the Fixture Manager and add some fixtures first. Veuillez commencez par ajouter des appareils à partir du gestionnaire d'appareils. @@ -2616,7 +2660,7 @@ Celui-ci a dû être déplacé ou effacé. FunctionLiveEditDialog - + Function Live Edit Edition d'une fonction en direct @@ -2624,195 +2668,195 @@ Celui-ci a dû être déplacé ou effacé. FunctionManager - + New &scene Nouvelle &scène - + New c&haser Nouveau c&haser - + New se&quence Nouvelle sé&quence - + New c&ollection Nouvelle c&ollection - + New E&FX Nouvel E&FX - + New &RGB Matrix Nouvelle matrice &RVB - + New scrip&t Nouveau scrip&t - + New Scene Nouvelle scène - + New Chaser Nouveau chaser - + New Sequence Nouvelle séquence - + &Clone &Dupliquer - + New au&dio Nouveau son (&D) - + New vid&eo Nouvelle vid&éo - + New fo&lder Nouveau dossier (&L) - + Select Startup Function Sélectionner la fonction de démarrage - + Function &Wizard Assistant de fonction (&W) - + &Delete Supprimer (&D) - + Select &all Tout sélectionner (&A) - + New Collection Nouvelle collection - + New EFX Nouvel EFX - + New RGB Matrix Nouvelle matrice RVB - + New Script Nouveau script - + Open Audio File Ouvrir un fichier audio - + Audio Files (%1) Fichiers audio (%1) - - + + All Files (*.*) Tous les fichiers (*.*) - - + + All Files (*) Tous les fichiers (*) - + Unsupported audio file Fichier audio non pris en charge - + This audio file cannot be played with QLC+. Sorry. Ce fichier audio ne peut pas être lu par QLC+, désolé. - + Open Video File Ouvrir un fichier vidéo - + Video Files (%1) Fichiers vidéo (%1) - + Unsupported video file Fichier vidéo non pris en charge - + This video file cannot be played with QLC+. Sorry. Ce fichier vidéo ne peut pas être lu par QLC+, désolé. - + Do you want to DELETE folder: Do you want to DELETE foler: Voulez-vous SUPPRIMER le dossier : - + Do you want to DELETE functions: Voulez-vous SUPPRIMER ces fonctions : - + (This will also DELETE: (Cela SUPPRIMERA également : - + Delete Functions Supprimer les fonctions - + Function Fonctions - + (Copy) (copie) @@ -3099,32 +3143,32 @@ p, li { white-space: pre-wrap; } Enlever - + %1 group Groupe %1 - + Error Erreur - + %1 has no capability supported by this wizard. %1 n'a pas de fonction supportée par cet assistant. - + Presets solo frame Cadre de préréglages solos - + Click & Go RGB Accès rapide RVB - + Click & Go Macro Accès rapide Macro @@ -3132,27 +3176,27 @@ p, li { white-space: pre-wrap; } GrandMasterSlider - + GM GM - + Grand Master <B>limits</B> the maximum value of Le Grand Master <B>limite</B> la valeur maximum - + Grand Master <B>reduces</B> the current value of Le Grand Master <B>reduit</B> la valeur - + intensity channels des canaux d'intensité - + all channels de tous les canaux @@ -3253,45 +3297,45 @@ p, li { white-space: pre-wrap; } InputOutputManager - + Add U&niverse Ajouter un u&nivers - + &Delete Universe Universe Supprimer l'univers (&D) - + Universe name: Nom de l'univers : - + Passthrough Traversé - - + + Universe %1 Univers %1 - - + + Delete Universe Supprimer l'univers - + The universe you are trying to delete is patched. Are you sure you want to delete it? Cet univers est actuellement patché. Êtes-vous sûr(e) de vouloir le supprimer ? - + There are some fixtures using the universe you are trying to delete. Are you sure you want to delete it? Des appareils utilisent actuellement cet univers. Êtes-vous sûr(e) de vouloir le supprimer ? @@ -3398,20 +3442,20 @@ p, li { white-space: pre-wrap; } Moniteur de niveau - - - + + + Error Erreur - - + + Output line already assigned La ligne de sortie est déjà assignée - + An error occurred while trying to open the selected device line. This can be caused either by a wrong system configuration or an unsupported input/output mode. Please refer to the plugins documentation to troubleshoot this. @@ -3420,67 +3464,67 @@ Cela peut être dû à une mauvaise configuration système ou un mode d'ent Veuillez vous référer à la documentation du plugin. - - + + Existing Input Profile Profil d'entrée existant - - + + An input profile at %1 already exists. Do you wish to overwrite it? Un profil d'entrée existe déjà en '%1'. Voulez-vous l'écraser ? - - + + Save Input Profile Enregistrer le profil d'entrée - - + + Input Profiles (*.qxi) Profils d'entrée (*.qxi) - - + + Saving failed L'enregistrement a échoué - + Unable to save the profile to %1 Impossible d'enregistrer le profil sous %1 - + Delete profile Supprimer le profil - + Do you wish to permanently delete profile "%1"? Voulez-vous supprimer définitivement le profil "%1" ? - + File deletion failed L'effacement du fichier a échoué - + Unable to delete file %1 Impossible de supprimer le fichier %1 - + Unable to save %1 to %2 Impossible d'enregistrer %1 sous %2 - + Default device Périphérique par défaut @@ -3493,176 +3537,233 @@ Veuillez vous référer à la documentation du plugin. Éditeur de profil d'entrée - + General Général - + Manufacturer Fabricant - + The name of the company that made the device Le nom de la société qui a fabriqué le périphérique - + Model Modèle - + The device's model name Le nom du modèle du périphérique - - Channels - Canaux - - - + + Channel Canal - + + Name Nom - + Custom feedback Retour d'info personnalisé - + Upper value Valeur supérieure - + Lower value Valeur inférieure - - + + Type Type - + MIDI Global Settings Paramètres MIDI globaux - + When MIDI notes are used, send a Note Off when value is 0 Lors de l'utilisation de notes MIDI, envoyer Note Off quand la valeur est de 0 - - + + Input Mapping + + + + Behaviour Comportement - + Add a new channel description Ajouter un nouveau canal - + + + MIDI channel + + + + + Colors + + + + + Remove the selected color + + + + + Add a new color + + + + + Value + Valeur + + + + Label + Étiquette + + + + Color + + + + + MIDI Channels + + + + + Add a new MIDI channel + + + + + Remove the selected MIDI channel + + + + Remove the selected channels Enlever les canaux sélectionnés - + Edit the selected channel Éditer le canal sélectionné - + Automatically add channels to the list when you wiggle the device's controls Ajouter automatiquement le canal à la liste quand un contrôle du périphérique est sollicité - + Movement Mouvement - + Absolute Absolu - + Relative Relatif - + Generate an extra Press/Release when toggled Générer une pression/relâche supplémentaire quand basculé - + Sensitivity Sensibilité - + File not writable Fichier inaccessible en écriture - + You do not have permission to write to the file %1. You might not be able to save your modifications to the profile. Vous n'avez pas les permissions pour écrire le fichier '%1'. Vous risquez de ne pas pouvoir sauvegarder vos modifications du profil. - + + From plugin settings + + + + Missing information Information manquante - + Manufacturer and/or model name is missing. Le nom du fabricant et/ou du modèle est manquant. - - + + Channel already exists Canal existant - - + + Channel %1 already exists Le canal %1 existe déjà - + Delete channels Supprimer les canaux - + Delete all %1 selected channels? Supprimer les %1 canaux sélectionnés ? - + Channel wizard activated Assistant de canal activé - + 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. @@ -3671,12 +3772,39 @@ Note that the wizard cannot tell the difference between a knob and a slider so y Notez que l'assistant ne peut pas différencier un bouton rotatif d'un fader, vous devrez faire ce changement manuellement. - + + + Enter value + + + + + Feedback value + + + + + + Enter label + + + + + Color label + + + + + MIDI channel label + + + + Button %1 Bouton %1 - + Slider %1 Fader %1 @@ -3709,65 +3837,50 @@ Notez que l'assistant ne peut pas différencier un bouton rotatif d'un Entrée externe - + When toggled, you can click an external button to assign it to this widget. Quand activée, pressez un bouton externe afin de l'assigner à ce widget. - + Auto Detect Auto-détection - + Input Universe Univers d'entrée - + Input Channel Canal d'entrée - + The input universe that sends data to this widget L'univers d'entrée qui contrôle ce widget - + Custom Feedback Retour d'info personnalisé - + The particular input channel within the input universe that sends data to this widget Le canal de l'univers d'entrée qui contrôle ce widget - + Choose an external input universe & channel that this widget should listen to. Choisir l'univers d'entrée et son canal qui contrôlera ce widget. - + Choose... Choisir... - - - Custom feedback - Retour d'info personnalisé - - - - Lower value - Valeur inférieure - - - - Upper value - Valeur suppérieure - Monitor @@ -3924,14 +4037,14 @@ Notez que l'assistant ne peut pas différencier un bouton rotatif d'un Arrière-plan - - + + Select background image Choisir une image d'arrière-plan - - + + Images Images @@ -4221,12 +4334,12 @@ Notez que l'assistant ne peut pas différencier un bouton rotatif d'un PlaybackSlider - + Select Sélectionner - + Flash Flash @@ -4242,34 +4355,29 @@ Notez que l'assistant ne peut pas différencier un bouton rotatif d'un QObject - + Operate Production - + Design Création - - + + Reversed Inversé - + Page: %1 Page : %1 RDMManager - - - Form - - Scan for RDM devices... @@ -4689,22 +4797,22 @@ Notez que l'assistant ne peut pas différencier un bouton rotatif d'un Aucun - + No fixture group to control Aucun groupe d'appareils à controller - + Select image Sélectionner une image - + Images Images - + Sequence Séquence @@ -4764,105 +4872,105 @@ Notez que l'assistant ne peut pas différencier un bouton rotatif d'un Désactiver tous les canaux des appareils - + Enable all channels in current fixture Activer tous les canaux des appareils actuel - + Disable all channels in current fixture Désactiver tous les canaux de l'appareil actuel - + Copy current values to clipboard Copier les valeurs actuelles dans le presse-papier - + Paste clipboard values to current fixture Coller les valeurs du presse-papier vers cet appareil - + Copy current values to all fixtures Copier les valeurs actuelles vers tous les appareils - + Color tool for CMY/RGB-capable fixtures Outil de couleur pour les appareils à composantes CMJ/RVB - + Position tool for moving heads/scanners Outil de positionnement pour les lyres/scanners - + Switch between tab view and all channels view Basculer entre la vue par onglets et la vue de tous les canaux - + Toggle blind mode Basculer le mode aveugle - + Show/Hide speed dial window Afficher/Masquer la fenêtre de réglage des vitesses - + Clone this scene and append as a new step to the selected chaser Cloner cette scène et l'ajouter comme nouveau pas dans le chaser sélectionné - + Go to next fixture tab Aller à l'appareil suivant - + Go to previous fixture tab Aller à l'appareil précédent - + None Aucun - + Scene name: Nom de la scène : + - All fixtures Tous les appareils + - Channels Groups Groupes de canaux + - Generic Générique - + Remove fixtures Enlever les appareils - + Do you want to remove the selected fixture(s)? Voulez-vous enlever le(s) appareil(s) sélectionné(s) ? @@ -5074,7 +5182,7 @@ Notez que l'assistant ne peut pas différencier un bouton rotatif d'un Autoriser les univers non patchés - + <Double click here to enter channel number manually> <Double-cliquer ici pour entrer le numéro du canal manuellement> @@ -5380,118 +5488,118 @@ Durée : %3 SimpleDesk - + Universe Univers - + Next page Page suivante - + Current page Page actuelle - + Previous page Page précédente - + View mode Type de vue - + Reset universe Réinitialiser l'univers - + Playback Faders de lancement - - + + Cue Stack Pile de mémoires - + Previous cue Mémoire précédente - + Stop cue stack Arrêter la pile de mémoires - + Next cue Mémoire suivante - + Clone cue stack Cloner la pile de mémoires - + Edit cue stack Éditer la pile de mémoires - + Record cue Enregistrer une mémoire - + Channel groups Groupes de canaux - + Cue Stack - Playback %1 Pile de mémoires - Fader %1 - + No selection Aucune sélection - + Cue name Nom de la mémoire - + Multiple Cues Mémoires multiples - + Delete cue Supprimer la mémoire - + Clone Cue Stack Cloner la pile de mémoires - + Clone To Playback# Cloner vers le fader - + Cue %1 Mémoire %1 @@ -5499,32 +5607,32 @@ Durée : %3 SpeedDial - + Hours Heures - + Minutes Minutes - + Seconds Secondes - + Milliseconds Millisecondes - + Infinite Infini - + Tap Tap @@ -5532,17 +5640,17 @@ Durée : %3 SpeedDialWidget - + Fade In Montée - + Fade Out Descente - + Hold Maintien @@ -5632,17 +5740,17 @@ Durée : %3 VCButton - + Choose... Choisir... - + None Aucun - + Button %1 Bouton %1 @@ -5657,17 +5765,17 @@ Durée : %3 Images (%1) - + Toggle Blackout Blackout - + Stop ALL functions! Arrêter TOUTES les fonctions ! - + Icon Icône @@ -5680,67 +5788,77 @@ Durée : %3 Propriétés du bouton - + + Override priority + + + + + Force LTP + + + + General Général - + Button label Nom du bouton - + Text to display on the button Le texte à afficher sur le bouton - + Function Fonction - + The function that this button controls La fonction que ce bouton contrôle - + Attach a function to this button Attacher une fonction au bouton - + Detach the button's function attachment Détacher la fonction liée au bouton - + Toggle Blackout Blackout - + Stop All Functions Arrêter toutes les fonctions - + Fade time: Temps de fondu : - + Adjust function intensity when it is running Ajuster l'intensité de la fonction durant son exécution - + Adjust Function Intensity Ajuster l'intensité de la fonction - + Function's adjusted intensity percentage when run Ajustement de l'intensité en pourcentage de la fonction @@ -5760,17 +5878,17 @@ Durée : %3 Interrupteur (Toggle) - + Flash the assigned function with this button Lire la fonction attachée au bouton lorsqu'il est pressé - + Flash function (only for scenes) Flash (uniquement pour les scènes) - + No function Aucune fonction @@ -5861,64 +5979,64 @@ Durée : %3 VCCueList - + Show/Hide crossfade sliders Afficher/Masquer les crossfaders - - + + Play/Pause Cue list Lecture/pause de la liste de cue - - + + Stop Cue list Arrêt de la liste de cue - + Go to previous step in the list Aller au pas précédent - + Go to next step in the list Aller au pas suivant - + Cue list Séquenceur - + Play/Stop Cue list Lecture/arrêt de la liste de cue - + Pause Cue list Mettre la liste de cue en pause - + Fade In Montée - + Fade Out Descente - + Duration Durée - + Notes Notes @@ -6084,7 +6202,7 @@ Durée : %3 VCFrame - + Add Ajouter @@ -6137,17 +6255,17 @@ Durée : %3 Nom de la page - + External Input - Enable Entrée externe - Activation - + External Input - Previous Page Entrée Externe - Page Précédente - + External Input - Next Page Entrée Externe - Page Suivante @@ -6180,17 +6298,17 @@ Durée : %3 VCLabel - + Label Étiquette - + Rename Label Renommer l'étiquette - + Caption: Légende : @@ -6198,42 +6316,42 @@ Durée : %3 VCMatrix - + Animation %1 Animation %1 - + End Color Reset Réinitialiser la couleur de fin - + Start color Red component Couleur de départ : composante rouge - + Start color Green component Couleur de départ : composante verte - + Start color Blue component Couleur de départ : composante bleue - + End color Red component Couleur de fin : composante rouge - + End color Green component Couleur de fin : composante verte - + End color Blue component Couleur de fin : composante bleue @@ -6436,48 +6554,48 @@ Durée : %3 Ajouter du texte - + No function Aucune fonction - + Start Color Couleur de départ - + Start Color Knob Potard de couleur de début - + End Color Couleur de fin - + End Color Knob Potard de couleur de fin - + End Color Reset Réinitialisation de la couleur de fin - + Animation Animation - - + + Text Texte - + Enter a text Saisir le texte @@ -6729,12 +6847,12 @@ Durée : %3 VCSlider - + Slider %1 Fader %1 - + Reset channels override Réinitialiser l'écrasement des canaux @@ -6752,42 +6870,42 @@ Durée : %3 Général - + Name of the slider Le nom du fader en cours d'édition - + Value display style Style d'affichage de la valeur - + Show exact DMX values Afficher la valeur DMX - + Show value as percentage Afficher la valeur en pourcentage - + Percentage Pourcentage - + Slider movement Mouvement du fader - + Normal Normal - + Inverted Inversé @@ -6863,32 +6981,32 @@ Durée : %3 Basculer vers le mode Lancement - + Actual DMX - + Widget name Nom du widget - + Widget appearance Apparence du widget - + Slider Fader - + Knob Bouton rotatif - + Catch up with the external controller input value Se mettre à jour avec la valeur d'entrée du contrôleur externe @@ -7286,17 +7404,17 @@ Durée : %3 Afficher le champ des millisecondes - + Multiply by 2 Input Entrée du bouton de multiplication par 2 - + Divide by 2 Input Entrée du bouton de division par 2 - + Factor Reset Input Entrée du bouton de réinitialisation du facteur @@ -7304,68 +7422,68 @@ Durée : %3 VCWidget - + Button Bouton - + Slider Fader - + XYPad Pad XY - + Frame Cadre - + Solo frame Cadre de solos - + Speed dial Contrôleur de vitesse - + Cue list Séquenceur - + Label Étiquette - + Audio Triggers Déclencheur audio - + Animation Animation - + Clock Horloge + - Unknown Inconnu - + This widget has no properties Ce widget n'a pas de propriétés @@ -7563,12 +7681,12 @@ Durée : %3 Nom du préréglage - + Pan / Horizontal Axis Pan / Axe horizontal (X) - + Tilt / Vertical Axis Tilt / Axe vertical (Y) @@ -7598,45 +7716,45 @@ Durée : %3 Éditer les axes de l'appareil sélectionné - + Width Largeur - + Height Hauteur - + Remove fixtures Enlever les appareils - + Do you want to remove the selected fixtures? Voulez-vous enlever le(s) appareils(s) sélectionné(s) ? - - + + Error Erreur - + The selected Scene does not include any Pan or Tilt channel. Please select one with such channels. La scène sélectionnée n'affecte pas de canal Pan ou Tilt. Veuillez en choisir une qui affecte un des deux. - + Please select at least one fixture or head to create this type of preset! Veuillez sélectionner au moins un appareil ou une tête pour créer ce type de préréglage ! - + Fixture Group Groupe d'appareils @@ -7769,12 +7887,12 @@ Veuillez en choisir une qui affecte un des deux. VideoItem - + Fullscreen Plein écran - + Screen %1 Écran %1 diff --git a/ui/src/qlcplus_it_IT.ts b/ui/src/qlcplus_it_IT.ts index 0a41df3ea0..4cc482a994 100644 --- a/ui/src/qlcplus_it_IT.ts +++ b/ui/src/qlcplus_it_IT.ts @@ -189,12 +189,12 @@ Numero di canali vuoti da lasciare tra le fixture aggiunte - + Fixtures found: %1 Fixture trovate: %1 - + Dimmers Dimmer @@ -525,376 +525,376 @@ App - + Cannot exit in Operate mode Non puoi uscire durante la modalità Operativa - + You must switch back to Design mode to close the application. Devi tornare alla modalità di Design per chiudere l'applicazione. - + Close Chiudi - + Do you wish to save the current workspace before closing the application? Vuoi salvare il progetto corrente prima di chiudere l'applicazione? - + Starting Q Light Controller Plus Starting Q Light Controller Avvio Q Light Controller Plus - + - New Workspace - Nuovo Spazio di Lavoro - + Switch to Design Mode Vai in Modalità Design - + There are still running functions. Really stop them and switch back to Design mode? Ci sono Funzioni Attive. Vuoi veramente fermarle e ritornare in modalità Design? - + Design Design - + Switch to design mode Vai in modalità Design - + Operate Operate - - + + Switch to operate mode Vai in modalità Operate - + &New &Nuovo - + CTRL+N File|New - + &Open &Apri - + CTRL+O File|Open - + &Save &Salva - + CTRL+S File|Save - + Save &As... Salva &Con Nome... - + &Operate &Operativo - + &Monitor &Monitor delle fixture - + Toggle &Blackout &Blackout On/Off - + CTRL+F12 Control|Toggle operate/design mode - + CTRL+M Control|Monitor - + Live edit a function Modifica una funzione in modalità live - + Toggle Full Screen Passa in modalità schermo intero - + CTRL+F11 Control|Toggle Full Screen - + &Index &Indice - + SHIFT+F1 Help|Index - + &About QLC+ &About QLC &A proposito di QLC+ - + Fixtures Fixture - + Functions Funzioni - + Shows Show - + Virtual Console Console Virtuale - + Simple Desk Banco Semplice - + Inputs/Outputs Ingressi/Uscite - + Close the application? Chiudere l'applicazione? - + Do you wish to close the application? Vuoi chiudere l'applicazione? - + Exit Uscita - + Address Tool Strumento per indirizzi - + Toggle Virtual Console Live edit Modalità di modifica live della console virtuale - + Dump DMX values to a function Salva i valori DMX su una funzione - + CTRL+D Control|Dump DMX CTRL+D - + Stop ALL functions! Ferma TUTTE le funzioni! - + Fade 1 second and stop Sfuma per 1 secondo e poi ferma - + Fade 5 seconds and stop Sfuma per 5 secondi e poi ferma - + Fade 10 second and stop Sfuma per 10 secondi e poi ferma - + Fade 30 second and stop Sfuma per 30 secondi e poi ferma - + Quit QLC+ Esci da QLC+ - + Workspace Spazio di lavoro - + Unable to read from file Impossibile leggere dal file - + Unable to write to file Impossibile Salvare il File - + A fatal error occurred Errore fatale - + Unable to access resource Impossibile accedere alla risorsa - + Unable to open file for reading or writing Impossibile leggere o scrivere il file - + Operation was aborted L'operazione è stata annullata - + Operation timed out Operazione scaduta - + An unspecified error has occurred. Nice. Si è verificato un errore sconosciuto. - + File error Errore file - - - + + + Do you wish to save the current workspace? Changes will be lost if you don't save them. Vuoi salvare il presente workspace? Tutti i cambiamenti andranno persi senza salvataggio.. - + New Workspace Nuovo Spazio di Lavoro - - - + + + Open Workspace Apri Spazio di Lavoro - - + + Workspaces (*%1) Spazio di Lavoro (*%1) - - + + All Files (*.*) Tutti i tipi di file (*.*) - - + + All Files (*) Tutti i file (*) - + Save Workspace As Salva lo Spazio di Lavoro come - + Error Errore - + File not found! The selected file has been moved or deleted. File non trovato! Il file selezionato è stato spostato o eliminato. - + Warning Attenzione - + Some errors occurred while loading the project: Si sono verificati degli errori durante il caricamento del progetto: @@ -917,12 +917,12 @@ Il file selezionato è stato spostato o eliminato. Chiudi automaticamente quando viene premuto un tasto - + Assign Key Assegna Tasto - + Hit the key combination that you wish to assign. You may hit either a single key or a combination using %1, %2, and %3. Premi la combinazione di tasti che vuoi assegnare. Puoi premere anche un solo tasto o una combinazione usando %1, %2 e %3. @@ -1049,23 +1049,23 @@ Il file selezionato è stato spostato o eliminato. AudioItem - - + + Preview Left Channel Anteprima Canale Sinistro - + Preview Right Channel Anteprima Canale Destro - + Preview Stereo Channels Anteprima Canali Stereo - + Preview Mono Anteprima Mono @@ -1133,52 +1133,52 @@ Il file selezionato è stato spostato o eliminato. Ingresso - + None Nessuno - + DMX DMX - + Function Funzione - + VC Widget Oggetto VC - + %1 channels %1 canali - + No function Nessuna funzione - + No widget Nessun oggetto - + Not assigned Non assegnato - + Volume Bar Barra del volume - + #%1 (%2Hz - %3Hz) #%1 (%2Hz - %3Hz) @@ -1236,12 +1236,12 @@ Il file selezionato è stato spostato o eliminato. Annulla modificatore - + Error Errore - + You are trying to overwrite a system template! Please choose another name and the template will be saved in your channel modifier's user folder. Stai cercando di sovrascrivere un template di sistema! Per favore scegli un altro nome e il template verrà salvato nella cartella utente dei modificatori di canali. @@ -1256,13 +1256,13 @@ Il file selezionato è stato spostato o eliminato. - + Name Nome - + Type Tipo @@ -1282,27 +1282,27 @@ Il file selezionato è stato spostato o eliminato. Espandi tutti - + Selected Selezionato - + Channel properties configuration Configurazione delle proprietà dei canali - + Can fade Consenti fade - + Behaviour Comportamento - + Modifier Modificatore @@ -1336,13 +1336,13 @@ Il file selezionato è stato spostato o eliminato. - + Fade In Fade In - + Fade Out Fade Out @@ -1363,7 +1363,7 @@ Il file selezionato è stato spostato o eliminato. - + Hold Hold @@ -1558,47 +1558,47 @@ Il file selezionato è stato spostato o eliminato. Indietro - + Cut Taglia - + Copy Copia - + Paste Incolla - + Paste error Errore di incolla - + Trying to paste on an incompatible Scene. Operation canceled. Stai cercando di incollare su una traccia non compatibile. Operazione annullata. - + Common Fade In Fade In Comune - + Common Fade Out Fade Out Comune - + Common Hold Hold Comune - + Multiple Steps Step multipli @@ -1654,12 +1654,12 @@ Il file selezionato è stato spostato o eliminato. ConsoleChannel - + Intensity Intensità - + Reset this channel Resetta questo canale @@ -1720,6 +1720,83 @@ Il file selezionato è stato spostato o eliminato. Coda + + CustomFeedbackDialog + + + Custom Feedback Configuration + Configurazione dei feedback personalizzati + + + + Value + Valore + + + + Label + Etichetta + + + + Color + Colore + + + + Values + Valori + + + + Lower Value + Valore inferiore + + + + Monitor Value + Valore di monitoring + + + + Upper Value + Valore superiore + + + + + + Color Selection + Selezione del colore + + + + MIDI Channel + Canale MIDI + + + + Upper Channel + Canale superiore + + + + Lower Channel + Canale inferiore + + + + Monitor Channel + Canale di monitoring + + + + + + From plugin settings + Dalle impostazioni di plugin + + DmxDumpFactory @@ -1779,50 +1856,17 @@ Il file selezionato è stato spostato o eliminato. Nome della scena: - + Dump all channels (%1 Universes, %2 Fixtures, %3 Channels) Dump all DMX values (%1 Universes, %2 Fixtures, %3 Channels) Salva tutti i canali (%1 Universi, %2 Fixture, %3 Canali) - + New Scene From Live %1 Nuova Scena Da Live %1 - - DocBrowser - - - %1 - Document Browser - %1 - Esplora Risorse - - - - Backward - Indietro - - - - Forward - Avanti - - - - Index - Indice - - - - About Qt - A proposito di Qt - - - - Close this window - Chiudi questa finestra - - EFXEditor @@ -2104,12 +2148,12 @@ Il file selezionato è stato spostato o eliminato. Guarda cosa fa l'EFX quando è in esecuzione - + Remove fixtures Rimuovi fixture - + Do you want to remove the selected fixture(s)? Vuoi rimuovere le fixture selezionate? @@ -2454,88 +2498,88 @@ Il file selezionato è stato spostato o eliminato. Riassegna i nomi delle fixture - - + + (remapped) (riassegnato) - + Import Fixtures List Importa Lista Fixture - + Fixtures List (*%1) Lista Fixture (*%1) - + All Files (*.*) Tutti i file (*.*) - + All Files (*) Tutti i file (*) - + Do you want to automatically connect fixtures with the same name? Vuoi connettere automaticamente le fixture con lo stesso nome? - + Generic Dimmer Dimmer generico - + Delete Fixtures Elimina fixture - + Do you want to delete the selected items? Vuoi eliminare gli elementi selezionati? - + Invalid operation Operazione non valida - + You are trying to clone a fixture on an address already in use. Please fix the target list first. Stai cercando di clonare una fixture su un indirizzo già in uso. Devi prima correggere la lista di destinazione. - - - - + + + + Invalid selection Selezione non valida - - - + + + Please select a source and a target fixture or channel to perform this operation. Selezionare una fixture o un canale di sorgente e di destinazione per eseguire questa operazione. - + To perform a fixture remap, please select fixtures on both lists. Per eseguire una riassegnazione di fixture, seleziona una fixture in entrambe le liste. - + This might take a while... Questo potrebbe richiedere del tempo... - + Cancel Annulla @@ -2548,12 +2592,12 @@ Il file selezionato è stato spostato o eliminato. Seleziona una fixture - + No fixtures available Nessuna fixture disponibile - + Go to the Fixture Manager and add some fixtures first. Vai alla Gestione Fixture e aggiungi delle fixture prima. @@ -2610,7 +2654,7 @@ Il file selezionato è stato spostato o eliminato. FunctionLiveEditDialog - + Function Live Edit Modifica Live di Funzione @@ -2618,195 +2662,195 @@ Il file selezionato è stato spostato o eliminato. FunctionManager - + New &scene Nuova &scena - + New c&haser Nuovo c&haser - + New se&quence Nuova se&quenza - + New c&ollection Nuova c&ollezione - + New E&FX Nuovo E&FX - + New &RGB Matrix Nuova Matrice &RGB - + New scrip&t Nuovo Scrip&t - + New Scene Nuova Scena - + New Chaser Nuovo Chaser - + New Sequence Nuova Sequenza - + &Clone &Clona - + New au&dio Nuovo au&dio - + New vid&eo Nuovo vid&eo - + New fo&lder Nuova carte&lla - + Select Startup Function Seleziona una funzione di avvio automatico - + Function &Wizard Assistente per le &funzioni - + &Delete &Elimina - + Select &all Seleziona &Tutto - + New Collection Nuova Collezione - + New EFX Nuovo EFX - + New RGB Matrix Nuova Matrice RGB - + New Script Nuovo Script - + Open Audio File Apri File Audio - + Audio Files (%1) File Audio (%1) - - + + All Files (*.*) Tutti i file (*.*) - - + + All Files (*) Tutti i file (*) - + Unsupported audio file File audio non supportato - + This audio file cannot be played with QLC+. Sorry. Questo file audio non può essere riprodotto da QLC+. Spiacente. - + Open Video File Apri File Video - + Video Files (%1) File video (%1) - + Unsupported video file File video non supportato - + This video file cannot be played with QLC+. Sorry. Questo file video non può essere riprodotto da QLC+. Spiacente. - + Do you want to DELETE folder: Do you want to DELETE foler: Vuoi ELIMINARE la cartella: - + Do you want to DELETE functions: Vuoi ELIMINARE le funzioni: - + (This will also DELETE: (Questo ELIMINERA' anche: - + Delete Functions CAncella Funzioni - + Function Funzione - + (Copy) (Copia) @@ -3093,32 +3137,32 @@ p, li { white-space: pre-wrap; } Elimina - + %1 group Gruppo %1 - + Error Errore - + %1 has no capability supported by this wizard. %1 non ha funzionalità supportate da questo wizard. - + Presets solo frame Solo frame dei preset - + Click & Go RGB Click & Go RGB - + Click & Go Macro Click & Go Macro @@ -3126,27 +3170,27 @@ p, li { white-space: pre-wrap; } GrandMasterSlider - + GM GM - + Grand Master <B>limits</B> the maximum value of Il Grand Master <B>limita</B> il valore massimo di - + Grand Master <B>reduces</B> the current value of Il Grand Master <B>riduce</B> il valore corrente di - + intensity channels canali di intensità - + all channels tutti i canali @@ -3247,45 +3291,45 @@ p, li { white-space: pre-wrap; } InputOutputManager - + Add U&niverse Aggiungi un u&niverso - + &Delete Universe Universe &Elimina un universo - + Universe name: Nome dell'universo: - + Passthrough Passthrough - - + + Universe %1 Universo %1 - - + + Delete Universe Elimina un universo - + The universe you are trying to delete is patched. Are you sure you want to delete it? L'universo che stai per eliminare è connesso. Sei sicuro di volerlo eliminare? - + There are some fixtures using the universe you are trying to delete. Are you sure you want to delete it? Ci sono delle fixture che usano l'universo che vuoi eliminare. Sei sicuro di volerlo eliminare? @@ -3392,20 +3436,20 @@ p, li { white-space: pre-wrap; } Monitor del livello - - - + + + Error Errore - - + + Output line already assigned La linea di uscita è già assegnata - + An error occurred while trying to open the selected device line. This can be caused either by a wrong system configuration or an unsupported input/output mode. Please refer to the plugins documentation to troubleshoot this. @@ -3414,67 +3458,67 @@ Questo può essere causato dalla configurazione errata del sistema oppure da una Consultare la documentazione relativa alle plugin per risolvere il problema. - - + + Existing Input Profile Profilo di ingresso esistente - - + + An input profile at %1 already exists. Do you wish to overwrite it? Un profilo d'Ingresso al %1 già esiste. Lo vuoi sovrascrivere? - - + + Save Input Profile Salva il profilo di ingresso - - + + Input Profiles (*.qxi) Profilo D'ingresso (*.qxi) - - + + Saving failed Salvataggio Fallito - + Unable to save the profile to %1 Impossibile salvare il profilo in %1 - + Delete profile Elimina Profilo - + Do you wish to permanently delete profile "%1"? Vuoi cancellare definitivamente il profilo "%1"? - + File deletion failed Eliminazione fallita - + Unable to delete file %1 Impossibile eliminare il file%1 - + Unable to save %1 to %2 Impossibile salvare %1 in %2 - + Default device Dispositivo predefinito @@ -3487,176 +3531,233 @@ Consultare la documentazione relativa alle plugin per risolvere il problema.
    Editor del profilo di ingresso - + General Generale - + Manufacturer Produttore - + The name of the company that made the device Il nome del produttore che ha costruito il dispositivo - + Model Modello - + The device's model name Nome del modello del dispositivo - - Channels - Canali - - - + + Channel Canale - + + Name Nome - + Custom feedback Feedback personalizzati - + Upper value Valore superiore - + Lower value Valore inferiore - - + + 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 - - + + Input Mapping + Mappatura di input + + + Behaviour Comportamento - + Add a new channel description Aggiungi una descrizione per il canale - + + + MIDI channel + Canale MIDI + + + + Colors + Colori + + + + Remove the selected color + Rimuovi il colore selezionato + + + + Add a new color + Aggiungi un nuovo colore + + + + Value + Valore + + + + Label + Etichetta + + + + Color + Colore + + + + MIDI Channels + Canali MIDI + + + + Add a new MIDI channel + Aggiungi un nuovo canale MIDI + + + + Remove the selected MIDI channel + Rimuovi il canale MIDI selezionato + + + Remove the selected channels Elimina i canali selezionati - + Edit the selected channel Edita il canale selezionato - + Automatically add channels to the list when you wiggle the device's controls Aggiungi automaticamente i canali alla lista quando muovi i comandi del dispositivo - + Movement Movimento - + Absolute Assoluto - + Relative Relativo - + Generate an extra Press/Release when toggled Genera un evento extra di Pressione/Rilascio quando attivato - + Sensitivity Sensibilità - + File not writable File di solo Lettura - + You do not have permission to write to the file %1. You might not be able to save your modifications to the profile. Non hai i permessi di scrittura sul file %1. Non è possibile salvare le tue modifiche fatte al profilo. - + + From plugin settings + Dalle impostazioni di plugin + + + Missing information Informazioni mancanti - + Manufacturer and/or model name is missing. Produttore e/o nome modello mancante. - - + + Channel already exists Canale già esistente - - + + Channel %1 already exists Canale %1 già esistente - + Delete channels Elimina canali - + Delete all %1 selected channels? Elimina tutti %1 canali selezionati? - + Channel wizard activated Assistente creazione 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. Note that the wizard cannot tell the difference between a knob and a slider so you will have to do the change manually. @@ -3665,12 +3766,39 @@ Note that the wizard cannot tell the difference between a knob and a slider so y Si noti che la procedura guidata non può dire la differenza tra una manopola e uno slider per cui dovrai aggiustarli manualmente. - + + + Enter value + Inserisci il valore + + + + Feedback value + Valore di feedback + + + + + Enter label + Inserisci l'etichetta + + + + Color label + Etichetta del colore + + + + MIDI channel label + Etichetta del canale MIDI + + + Button %1 Pulsante %1 - + Slider %1 Slider %1 @@ -3703,65 +3831,50 @@ Si noti che la procedura guidata non può dire la differenza tra una manopola e Ingresso esterno - + When toggled, you can click an external button to assign it to this widget. Se attivato, è possibile fare clic su un pulsante esterno per assegnarlo a questo oggetto. - + Auto Detect Rileva automaticamente - + Input Universe Universo di ingresso - + Input Channel Canale di ingresso - + The input universe that sends data to this widget L'universo di ingresso che invia i dati a questo oggetto - + Custom Feedback Feedback personalizzati - + The particular input channel within the input universe that sends data to this widget Il canale di ingresso all'interno dell'universo di ingresso che invia i dati a questo oggetto - + Choose an external input universe & channel that this widget should listen to. Scegli un universo di ingresso esterno con il relativo canale a cui questo oggetto deve rispondere. - + Choose... Scegli... - - - Custom feedback - Feedback personalizzati - - - - Lower value - Valore inferiore - - - - Upper value - Valore superiore - Monitor @@ -3918,14 +4031,14 @@ Si noti che la procedura guidata non può dire la differenza tra una manopola e Sfondo - - + + Select background image Seleziona l'immagine di sfondo - - + + Images Immagini @@ -4215,12 +4328,12 @@ Si noti che la procedura guidata non può dire la differenza tra una manopola e PlaybackSlider - + Select Seleziona - + Flash Flash @@ -4236,43 +4349,38 @@ Si noti che la procedura guidata non può dire la differenza tra una manopola e QObject - + Operate Operativo - + Design Design - - + + Reversed Invertito - + Page: %1 Pagina %1 RDMManager - - - Form - - Scan for RDM devices... - + Cerca dispositivi RDM... Retrieve the selected fixture information - + Recupera le informazioni della fixture selezionata @@ -4302,53 +4410,53 @@ Si noti che la procedura guidata non può dire la differenza tra una manopola e Manual controls - + Controlli manuali Arguments - + Argomenti A list of comma separated arguments Enter the (optional) arguments to read the PID, separated by commas - + Una lista di argomenti separati da virgola Write - + Scrivi Byte - + Byte Short - + Short (16bit) Long - + Long (32bit) Array (Hex) - + Array (Esadecimale) Read - + Leggi Response - + Risposta @@ -4387,12 +4495,12 @@ Si noti che la procedura guidata non può dire la differenza tra una manopola e Personalities - + Personality Personality - + Personality @@ -4683,22 +4791,22 @@ Si noti che la procedura guidata non può dire la differenza tra una manopola e Nessuno - + No fixture group to control Nessun gruppo di fixture da controllare - + Select image Seleziona un'immagine - + Images Immagini - + Sequence Sequenza @@ -4758,105 +4866,105 @@ Si noti che la procedura guidata non può dire la differenza tra una manopola e Disattiva tutti i canali di tutte le fixture - + Enable all channels in current fixture Abilita tutti i canali di questa fixture - + Disable all channels in current fixture Disabilita tutti i canali di questa fixture - + Copy current values to clipboard Copia i valori correnti in memoria - + Paste clipboard values to current fixture Incolla i valori copiati in memoria su questa fixture - + Copy current values to all fixtures Copia questi valori a tutte le fixture - + Color tool for CMY/RGB-capable fixtures Ruota colori per fixture con miscelazione CMY/RGB - + Position tool for moving heads/scanners Strumento di posizionamento per teste mobili e scanner - + Switch between tab view and all channels view Passa dalla vista a tab alla vista a canali - + Toggle blind mode Modalità blind on/off - + Show/Hide speed dial window Mostra/Nascondi finestra speed dial - + Clone this scene and append as a new step to the selected chaser Clona questa scena e aggiungi un nuovo step al chaser selezionato - + Go to next fixture tab Vai al tab della fixture successiva - + Go to previous fixture tab Vai al tab della fixture precedente - + None Nessuno - + Scene name: Nome della scena: + - All fixtures Tutte le fixture + - Channels Groups Gruppi di Canali + - Generic Generico - + Remove fixtures Elimina Fixture - + Do you want to remove the selected fixture(s)? Vuoi eliminare le fixture selezionate? @@ -5068,7 +5176,7 @@ Si noti che la procedura guidata non può dire la differenza tra una manopola e Permetti universi non connessi - + <Double click here to enter channel number manually> <Doppio click qui per inserire il numero del canale manualmente> @@ -5374,118 +5482,118 @@ Durata: %3 SimpleDesk - + Universe Universo - + Next page Pagina successiva - + Current page Pagina corrente - + Previous page Pagina precedente - + View mode Modalità di visualizzazione - + Reset universe Resetta universo - + Playback Riproduzione - - + + Cue Stack Pila Azioni - + Previous cue Azione precedente - + Stop cue stack Ferma pila azioni - + Next cue Azione successiva - + Clone cue stack Clona pila azioni - + Edit cue stack Modifica pila azioni - + Record cue Registra azione - + Channel groups Gruppi di canali - + Cue Stack - Playback %1 Pila azioni - Riproduzione %1 - + No selection Nessuna selezione - + Cue name Nome azione - + Multiple Cues Azioni multiple - + Delete cue Elimina azione - + Clone Cue Stack Clona pila azioni - + Clone To Playback# Clona su riproduzione# - + Cue %1 Azione %1 @@ -5493,32 +5601,32 @@ Durata: %3 SpeedDial - + Hours Ore - + Minutes Minuti - + Seconds Secondi - + Milliseconds Millisecondi - + Infinite Infinito - + Tap Tap @@ -5526,17 +5634,17 @@ Durata: %3 SpeedDialWidget - + Fade In Fade In - + Fade Out Fade Out - + Hold Hold @@ -5626,17 +5734,17 @@ Durata: %3 VCButton - + Choose... Scegli... - + None Nessuno - + Button %1 Pulsante %1 @@ -5651,17 +5759,17 @@ Durata: %3 Immagini (%1) - + Toggle Blackout Blackout On/Off - + Stop ALL functions! Ferma TUTTE le funzioni! - + Icon Icona @@ -5674,67 +5782,77 @@ Durata: %3 Proprietà del pulsante - + + Override priority + Priorità di override + + + + Force LTP + Forza LTP + + + General Generale - + Button label Etichetta del pulsante - + Text to display on the button Testo da mostrare sul pulsante - + Function Funzione - + The function that this button controls La funzione che questo pulsante controlla - + Attach a function to this button Connetti una funzione a questo pulsante - + Detach the button's function attachment Disconnetti la funzione connessa a questo pulsante - + Toggle Blackout Blackout On/Off - + Stop All Functions Ferma tutte le funzioni - + Fade time: Tempo di fade: - + Adjust function intensity when it is running Aggiusta l'intensità della funzione mentre è in esecuzione - + Adjust Function Intensity Aggiusta l'intensità della funzione - + Function's adjusted intensity percentage when run Percentuale di intensità della funzione aggiustata in esecuzione @@ -5754,17 +5872,17 @@ Durata: %3 Attiva la funzione on/off - + Flash the assigned function with this button Modalità Flash alla funzione assegnata con questo pulsante - + Flash function (only for scenes) Modalità Flash (solo per scene) - + No function Nessuna funzione @@ -5855,64 +5973,64 @@ Durata: %3 VCCueList - + Show/Hide crossfade sliders Mostra/Nascondi gli slider di crossfade - - + + Play/Pause Cue list Riproduci/Sospendi la lista di azioni - - + + Stop Cue list Interrompi la lista di azioni - + Go to previous step in the list Vai allo step precedente nella lista - + Go to next step in the list Vai allo step successivo nella lista - + Cue list Lista di azioni - + Play/Stop Cue list Riproduci/Interrompi la lista di azioni - + Pause Cue list Sospendi la lista di azioni - + Fade In Fade In - + Fade Out Fade Out - + Duration Durata - + Notes Note @@ -6078,7 +6196,7 @@ Durata: %3 VCFrame - + Add Aggiungi @@ -6131,17 +6249,17 @@ Durata: %3 Nome della pagina - + External Input - Enable Ingresso esterno - Abilita - + External Input - Previous Page Ingresso esterno - Pagina precedente - + External Input - Next Page Ingresso esterno - Pagina successiva @@ -6174,17 +6292,17 @@ Durata: %3 VCLabel - + Label Etichetta - + Rename Label Rinomina etichetta - + Caption: Titolo: @@ -6192,42 +6310,42 @@ Durata: %3 VCMatrix - + Animation %1 Animazione %1 - + End Color Reset Reset del colore di fine - + Start color Red component Componente rosso del colore di inizio - + Start color Green component Componente verde del colore di inizio - + Start color Blue component Componente blue del colore di inizio - + End color Red component Componente rosso del colore di fine - + End color Green component Componente verde del colore di fine - + End color Blue component Componente blu del colore di fine @@ -6430,48 +6548,48 @@ Durata: %3 Aggiungi testo - + No function Nessuna funzione - + Start Color Colore di inizio - + Start Color Knob Manopola colore iniziale - + End Color Colore di fine - + End Color Knob Manopola colore finale - + End Color Reset Reset del colore finale - + Animation Animazione - - + + Text Testo - + Enter a text Inserisci un testo @@ -6723,12 +6841,12 @@ Durata: %3 VCSlider - + Slider %1 Slider %1 - + Reset channels override Resetta l'override dei canali @@ -6746,42 +6864,42 @@ Durata: %3 Generale - + Name of the slider Nome dello Slider - + Value display style Stile di visualizzazione - + Show exact DMX values Mostra i valori esatti DMX - + Show value as percentage Mostra valori in percentuale - + Percentage Percentuale - + Slider movement Movimento dello Slider - + Normal Normale - + Inverted Invertito @@ -6857,32 +6975,32 @@ Durata: %3 Passa alla modalità di riproduzione - + Actual Valore - + Widget name Nome dell'oggetto - + Widget appearance Aspetto dell'oggetto - + Slider Slider - + Knob Manopola - + Catch up with the external controller input value Sincronizza con il valore di ingresso esterno @@ -7280,17 +7398,17 @@ Durata: %3 Mostra il campo millisecondi - + Multiply by 2 Input Input per la moltiplicazione per 2 - + Divide by 2 Input Input per la divisione per 2 - + Factor Reset Input Input per reimpostare il fattore @@ -7298,68 +7416,68 @@ Durata: %3 VCWidget - + Button Pulsante - + Slider Slider - + XYPad Pad XY - + Frame Frame - + Solo frame Frame esclusivo - + Speed dial Speed dial - + Cue list Lista di azioni - + Label Etichetta - + Audio Triggers Impulsi audio - + Animation Animazione - + Clock Orologio + - Unknown Sconosciuto - + This widget has no properties Questo oggetto non ha proprietà @@ -7557,12 +7675,12 @@ Durata: %3 Nome del preset - + Pan / Horizontal Axis Pan / Asse orizzontale - + Tilt / Vertical Axis Tilt / Asse verticale @@ -7592,45 +7710,45 @@ Durata: %3 Edita l'Asse della fixture selezionata - + Width Larghezza - + Height Altezza - + Remove fixtures Rimuovi fixture - + Do you want to remove the selected fixtures? Vuoi rimuovere le fixture selezionate? - - + + Error Errore - + The selected Scene does not include any Pan or Tilt channel. Please select one with such channels. La Scena selezionata non include alcun canale di Pan o Tilt. Selezionarne una con i suddetti canali. - + Please select at least one fixture or head to create this type of preset! Selezionare almeno una fixture o una testa per creare questo tipo di preset! - + Fixture Group Gruppo di Fixture @@ -7763,12 +7881,12 @@ Selezionarne una con i suddetti canali. VideoItem - + Fullscreen Schermo intero - + Screen %1 Schermo %1 diff --git a/ui/src/qlcplus_ja_JP.ts b/ui/src/qlcplus_ja_JP.ts index 0a79cb4a36..dc20540a38 100644 --- a/ui/src/qlcplus_ja_JP.ts +++ b/ui/src/qlcplus_ja_JP.ts @@ -189,12 +189,12 @@ 機器間のアドレス間隔 - + Fixtures found: %1 Fixtures found: %1 - + Dimmers ディマー @@ -520,377 +520,377 @@ App - + Cannot exit in Operate mode 本番モード中は終了できません - + You must switch back to Design mode to close the application. 仕込みモードに戻ってから閉じてください。 - + Close 閉じる - + Do you wish to save the current workspace before closing the application? 閉じる前に、現在のプロジェクトを保存しますか? - + Starting Q Light Controller Plus Starting Q Light Controller QLC+ を開始 - + - New Workspace - 新規プロジェクト - + Switch to Design Mode 仕込みモードにする - + There are still running functions. Really stop them and switch back to Design mode? 再生中のシーンがあります。 すべて停止して仕込みモードに戻りますか? - + Design 仕込みモード - + Switch to design mode 仕込みモードにする - + Operate 本番モード - - + + Switch to operate mode 本番モードにする - + &New 新規 - + CTRL+N File|New CTRL+N - + &Open 開く - + CTRL+O File|Open CTRL+O - + &Save 上書き保存 - + CTRL+S File|Save CTRL+S - + Save &As... 名前を付けて保存 - + &Operate 本番モード - + &Monitor モニタ - + Toggle &Blackout 暗転 - + CTRL+F12 Control|Toggle operate/design mode CTRL+F12 - + CTRL+M Control|Monitor CTRL+M - + Live edit a function 再生中修正 - + Toggle Full Screen フルスクリーン - + CTRL+F11 Control|Toggle Full Screen CTRL+F11 - + &Index ヘルプ - + SHIFT+F1 Help|Index SHIFT+F1 - + &About QLC+ &About QLC &QLC+ について - + Fixtures 機器 - + Functions ファンクション - + Shows タイムライン - + Virtual Console バーチャルコンソール - + Simple Desk シンプル卓 - + Inputs/Outputs 入力/出力設定 - + Close the application? QLC+ を終了 - + Do you wish to close the application? QLC+ を終了しますか? - + Exit 閉じる - + Address Tool ディップスイッチ設定用ツール - + Toggle Virtual Console Live edit 本番モード時にバーチャルコンソールを編集 - + Dump DMX values to a function 現在のDMX値をシーンにする - + CTRL+D Control|Dump DMX CTRL+D - + Stop ALL functions! 全ファンクション停止 - + Fade 1 second and stop 1秒フェードで停止 - + Fade 5 seconds and stop 5秒フェードで停止 - + Fade 10 second and stop 10秒フェードで停止 - + Fade 30 second and stop 30秒フェードで停止 - + Quit QLC+ QLC+を終了します - + Workspace プロジェクト - + Unable to read from file ファイル読み取り不可 - + Unable to write to file ファイル書き込み不可 - + A fatal error occurred ...la vilaine! 深刻なエラーが発生しました - + Unable to access resource リソースにアクセスできません - + Unable to open file for reading or writing 読み書きのためのファイルを開けません - + Operation was aborted 操作は中断されました - + Operation timed out 操作はタイムアウトしました - + An unspecified error has occurred. Nice. 予期しないエラーが発生しました。な、何だってー!? - + File error ファイルエラー - - - + + + Do you wish to save the current workspace? Changes will be lost if you don't save them. Tiens tiens...perspicace! 開く前に、現在のプロジェクトを保存しますか? - + New Workspace 新規プロジェクト - - - + + + Open Workspace プロジェクトを開く - - + + Workspaces (*%1) プロジェクト (*%1) - - + + All Files (*.*) すべてのファイル (*.*) - - + + All Files (*) すべてのファイル (*) - + Save Workspace As プロジェクトを名前を付けて保存 - + Error エラー - + File not found! The selected file has been moved or deleted. ファイルが見つかりません。 移動または削除された可能性があります。 - + Warning 警告 - + Some errors occurred while loading the project: 次のプロジェクトを読み込み中に何らかのエラーが発生しました: @@ -913,12 +913,12 @@ The selected file has been moved or deleted. キー設定後、自動的に閉じる - + Assign Key キーを押してください(Shift などとの複合も可) - + Hit the key combination that you wish to assign. You may hit either a single key or a combination using %1, %2, and %3. ショートカットに設定したいキーを押してください。 %1, %2, %3 との複合も可能です。 @@ -1045,23 +1045,23 @@ The selected file has been moved or deleted. AudioItem - - + + Preview Left Channel 左チャンネルを可視化 - + Preview Right Channel 右チャンネルを可視化 - + Preview Stereo Channels ステレオ音声を可視化 - + Preview Mono モノラル音声を可視化 @@ -1129,52 +1129,52 @@ The selected file has been moved or deleted. 入力 - + None 無し - + DMX DMX - + Function ファンクション - + VC Widget 部品 - + %1 channels %1 チャンネル - + No function ファンクション無し - + No widget 部品無し - + Not assigned アサイン無し - + Volume Bar ボリュームバー - + #%1 (%2Hz - %3Hz) #%1 (%2Hz - %3Hz) @@ -1232,12 +1232,12 @@ The selected file has been moved or deleted. リセット - + Error エラー - + You are trying to overwrite a system template! Please choose another name and the template will be saved in your channel modifier's user folder. QLC+のデフォルトから入っているテンプレートは上書きできません、別名で保存してください @@ -1252,13 +1252,13 @@ The selected file has been moved or deleted. - + Name 名前 - + Type 属性 @@ -1278,27 +1278,27 @@ The selected file has been moved or deleted. 全て開く - + Selected 選択 - + Channel properties configuration チャンネルの設定を開く - + Can fade フェード操作を許可する - + Behaviour ブレンドモード - + Modifier ディマーカーブ @@ -1332,13 +1332,13 @@ The selected file has been moved or deleted. - + Fade In フェードイン - + Fade Out フェードアウト @@ -1359,7 +1359,7 @@ The selected file has been moved or deleted. - + Hold ホールド @@ -1554,47 +1554,47 @@ The selected file has been moved or deleted. 逆再生 - + Cut カット - + Copy コピー - + Paste ペースト - + Paste error ペーストエラー - + Trying to paste on an incompatible Scene. Operation canceled. ペーストできないシーンです。 - + Common Fade In 全体F.I.時間 - + Common Fade Out 全体F.O.時間 - + Common Hold 全体ホールド時間 - + Multiple Steps 複数ステップ @@ -1650,12 +1650,12 @@ The selected file has been moved or deleted. ConsoleChannel - + Intensity 明るさ - + Reset this channel リセット @@ -1716,6 +1716,83 @@ The selected file has been moved or deleted. キュー + + CustomFeedbackDialog + + + Custom Feedback Configuration + + + + + Value + + + + + Label + メモ + + + + Color + + + + + Values + + + + + Lower Value + + + + + Monitor Value + + + + + Upper Value + + + + + + + Color Selection + + + + + MIDI Channel + + + + + Upper Channel + + + + + Lower Channel + + + + + Monitor Channel + + + + + + + From plugin settings + + + DmxDumpFactory @@ -1775,50 +1852,17 @@ The selected file has been moved or deleted. シーン名 - + Dump all channels (%1 Universes, %2 Fixtures, %3 Channels) Dump all DMX values (%1 Universes, %2 Fixtures, %3 Channels) すべてのチャンネルを出力 (%1 個のuniverse, %2 個のDMX機器, %3 チャンネル) - + New Scene From Live %1 New Scene From Live %1 - - DocBrowser - - - %1 - Document Browser - %1 - ヘルプ - - - - Backward - 前へ - - - - Forward - 次へ - - - - Index - Aide - - - - About Qt - Qt について - - - - Close this window - ウィンドウを閉じる - - EFXEditor @@ -2101,12 +2145,12 @@ The selected file has been moved or deleted. 作成したEFXのプレビュー - + Remove fixtures 機器の削除 - + Do you want to remove the selected fixture(s)? 選択した機器を削除しますか? @@ -2455,88 +2499,88 @@ The selected file has been moved or deleted. 機器の名前をリマップ元に合わせる - - + + (remapped) (remapped) - + Import Fixtures List 機器リストのインポート - + Fixtures List (*%1) 機器リスト (*%1) - + All Files (*.*) すべてのファイル (*.*) - + All Files (*) すべてのファイル (*) - + Do you want to automatically connect fixtures with the same name? - + Generic Dimmer 一般ディマー - + Delete Fixtures 機器の削除 - + Do you want to delete the selected items? 選択したアイテムを削除しますか? - + Invalid operation 無効な操作 - + You are trying to clone a fixture on an address already in use. Please fix the target list first. そのアドレスはリマップ先で既に使われています。 - - - - + + + + Invalid selection 無効な選択 - - - + + + Please select a source and a target fixture or channel to perform this operation. リマップ元とリマップ先の機器またはチャンネルをそれぞれ選択してください。 - + To perform a fixture remap, please select fixtures on both lists. 機器のリマップを実行するには、左右のリストからそれぞれ機器を選択してください。 - + This might take a while... しばらくお待ちください - + Cancel キャンセル @@ -2549,12 +2593,12 @@ The selected file has been moved or deleted. 機器の選択 - + No fixtures available 機器がありません - + Go to the Fixture Manager and add some fixtures first. まずは「機器」タブをクリックし、機器を追加してください。 @@ -2611,7 +2655,7 @@ The selected file has been moved or deleted. FunctionLiveEditDialog - + Function Live Edit 本番モード時にファンクションを編集 @@ -2619,195 +2663,195 @@ The selected file has been moved or deleted. FunctionManager - + New &scene 新しいシーン - + New c&haser 新しいチェイス - + New se&quence 新しいシーケンス - + New c&ollection 新しいコレクション - + New E&FX 新しいE&FX - + New &RGB Matrix 新しいRGBマトリックス - + New scrip&t 新しいスクリプト - + New Scene 新しいシーン - + New Chaser 新しいチェイス - + New Sequence 新しいシーケンス - + &Clone 複製 - + New au&dio 新しいオーディオ - + New vid&eo 新しいビデオ - + New fo&lder 新しいフォルダ - + Select Startup Function スタートアップファンクションの設定 - + Function &Wizard ファンクションウィザード - + &Delete 削除 - + Select &all すべて選択 - + New Collection 新しいコレクション - + New EFX 新しいEFX - + New RGB Matrix 新しいRGBマトリックス - + New Script 新しいスクリプト - + Open Audio File オーディオファイルを開く - + Audio Files (%1) オーディオファイル (%1) - - + + All Files (*.*) すべてのファイル (*.*) - - + + All Files (*) すべてのファイル (*) - + Unsupported audio file サポートしていないファイル - + This audio file cannot be played with QLC+. Sorry. このオーディオファイルは QLC+ では再生できません。 - + Open Video File ビデオファイルを開く - + Video Files (%1) ビデオファイル (%1) - + Unsupported video file サポートしていないファイル - + This video file cannot be played with QLC+. Sorry. このビデオファイルは QLC+ では再生できません。 - + Do you want to DELETE folder: Do you want to DELETE foler: 以下のフォルダを削除しますか? : - + Do you want to DELETE functions: 以下のファンクションを削除しますか? : - + (This will also DELETE: (以下も同時に削除されます : - + Delete Functions ファンクションの削除 - + Function ファンクション - + (Copy) (コピー) @@ -3094,32 +3138,32 @@ p, li { white-space: pre-wrap; } 削除 - + %1 group 機器グループ「 %1 」 - + Error エラー - + %1 has no capability supported by this wizard. %1 は、ファンクション自動生成には対応していない機器です。 - + Presets solo frame ソロフレームに配置 - + Click & Go RGB Click & Go を利用: RGB - + Click & Go Macro Click & Go を利用: マクロ @@ -3127,27 +3171,27 @@ p, li { white-space: pre-wrap; } GrandMasterSlider - + GM GM - + Grand Master <B>limits</B> the maximum value of <B>limits</B> / - + Grand Master <B>reduces</B> the current value of <B>乗算</B> - + intensity channels 明るさチャンネルのみ - + all channels すべてのチャンネルに有効 @@ -3248,45 +3292,45 @@ p, li { white-space: pre-wrap; } InputOutputManager - + Add U&niverse Universe の追加 - + &Delete Universe Universe Universe の削除 - + Universe name: Universe名: - + Passthrough パススルー - - + + Universe %1 Universe %1 - - + + Delete Universe Universe の削除 - + The universe you are trying to delete is patched. Are you sure you want to delete it? この Universe は接続されています。本当に削除しますか? - + There are some fixtures using the universe you are trying to delete. Are you sure you want to delete it? この Universe には使用中の機器があります。本当に削除しますか? @@ -3393,87 +3437,87 @@ p, li { white-space: pre-wrap; } レベルモニター - - - + + + Error エラー - - + + Output line already assigned 出力先はすでに選択されています - + An error occurred while trying to open the selected device line. This can be caused either by a wrong system configuration or an unsupported input/output mode. Please refer to the plugins documentation to troubleshoot this. デバイスを使用することができませんでした、お使いのPCシステムの設定を確認してください - - + + Existing Input Profile 上書き確認 - - + + An input profile at %1 already exists. Do you wish to overwrite it? プロファイル '%1' は既に存在します。上書き保存しますか? - - + + Save Input Profile プロファイルを保存 - - + + Input Profiles (*.qxi) 外部入力用プロファイル (*.qxi) - - + + Saving failed 保存に失敗しました - + Unable to save the profile to %1 プロファイルを '%1'.に保存できませんでした。 - + Delete profile プロファイルの削除 - + Do you wish to permanently delete profile "%1"? 本当にプロファイル "%1" を削除しますか? - + File deletion failed ファイルの削除に失敗しました - + Unable to delete file %1 '%1' を削除できません。 - + Unable to save %1 to %2 '%1' を '%2' に保存できません。 - + Default device 既定のデバイス @@ -3486,176 +3530,233 @@ Please refer to the plugins documentation to troubleshoot this. 入力機器プロファイル設定 - + General 全般 - + Manufacturer メーカー名 - + The name of the company that made the device メーカー名 - + Model 機種名 - + The device's model name 機種名 - - Channels - チャンネル - - - + + Channel チャンネル - + + Name 名前 - + Custom feedback カスタムフィードバック - + Upper value 上限値 - + Lower value 下限値 - - + + Type タイプ - + MIDI Global Settings MIDI全般の設定 - + When MIDI notes are used, send a Note Off when value is 0 MIDIのnoteがoffの時にvalue=0を送信する - - + + Input Mapping + + + + Behaviour モード - + Add a new channel description チャンネルの説明を追加 - + + + MIDI channel + + + + + Colors + + + + + Remove the selected color + + + + + Add a new color + + + + + Value + + + + + Label + メモ + + + + Color + + + + + MIDI Channels + + + + + Add a new MIDI channel + + + + + Remove the selected MIDI channel + + + + Remove the selected channels 選択したチャンネルを削除 - + Edit the selected channel 選択したチャンネルを編集 - + Automatically add channels to the list when you wiggle the device's controls 自動割り当てのために、設定したいチャンネルを動かしてみてください - + Movement 動き - + Absolute 絶対値 - + Relative 相対値 - + Generate an extra Press/Release when toggled 押下・押上を切り替える - + Sensitivity 感度調整 - + File not writable ファイル書き込み不可 - + You do not have permission to write to the file %1. You might not be able to save your modifications to the profile. ファイル %1 に書き込む権限がありません。 - + + From plugin settings + + + + Missing information 不明な情報 - + Manufacturer and/or model name is missing. メーカー名か機種名が見つかりません。 - - + + Channel already exists チャンネルが既に存在しています - - + + Channel %1 already exists チャンネル %1 は既に存在しています。 - + Delete channels チャンネル削除 - + Delete all %1 selected channels? %1 の選択したチャンネルをすべて削除しますか? - + 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. Note that the wizard cannot tell the difference between a knob and a slider so you will have to do the change manually. @@ -3663,12 +3764,39 @@ Note that the wizard cannot tell the difference between a knob and a slider so y *このウィザードではスライダーとノブの違いを検出することができません、種類は手動で設定する必要があります。 - + + + Enter value + + + + + Feedback value + + + + + + Enter label + + + + + Color label + + + + + MIDI channel label + + + + Button %1 ボタン %1 - + Slider %1 フェーダー %1 @@ -3701,65 +3829,50 @@ Note that the wizard cannot tell the difference between a knob and a slider so y 外部入力 - + When toggled, you can click an external button to assign it to this widget. 自動判別をオンにして、外部入力機器(MIDIコントローラーなど)のフェーダーやボタンを操作してください。 - + Auto Detect 自動判別 - + Input Universe Input Universe - + Input Channel 外部入力 - + The input universe that sends data to this widget 外部入力でウィジェットを操作 - + Custom Feedback カスタムフィードバック - + The particular input channel within the input universe that sends data to this widget 外部入力でウィジェットを操作 - + Choose an external input universe & channel that this widget should listen to. 入力にしようするUniverseとチャンネルを選択してください - + Choose... 選択... - - - Custom feedback - カスタムフィードバック - - - - Lower value - 下限値 - - - - Upper value - 上限値 - Monitor @@ -3916,14 +4029,14 @@ Note that the wizard cannot tell the difference between a knob and a slider so y 背景 - - + + Select background image 背景画像を選択 - - + + Images 画像 @@ -4213,12 +4326,12 @@ Note that the wizard cannot tell the difference between a knob and a slider so y PlaybackSlider - + Select 選択 - + Flash フラッシュ @@ -4234,34 +4347,29 @@ Note that the wizard cannot tell the difference between a knob and a slider so y QObject - + Operate 本番モード - + Design 仕込みモード - - + + Reversed 戻る - + Page: %1 Page : %1 RDMManager - - - Form - - Scan for RDM devices... @@ -4681,22 +4789,22 @@ Note that the wizard cannot tell the difference between a knob and a slider so y 無し - + No fixture group to control 操作する機器グループがありません - + Select image 画像を選択 - + Images 画像 - + Sequence シーケンス @@ -4756,105 +4864,105 @@ Note that the wizard cannot tell the difference between a knob and a slider so y すべての機器とチャンネルを無効 - + Enable all channels in current fixture この機器のすべてのチャンネルを有効 - + Disable all channels in current fixture この機器のすべてのチャンネルを無効 - + Copy current values to clipboard 現在のシーンをコピー - + Paste clipboard values to current fixture クリップボードから貼り付け - + Copy current values to all fixtures すべての機器に現在の値を適用 - + Color tool for CMY/RGB-capable fixtures 色設定ツール(CMY/RGBミックスが可能な機器用) - + Position tool for moving heads/scanners ムービングの位置調整 - + Switch between tab view and all channels view フィクスチャーごとにタブ表示/まとめて表示 - + Toggle blind mode ブラインドモード - + Show/Hide speed dial window スピード設定 - + Clone this scene and append as a new step to the selected chaser このシーンを複製し、選択したチェイスにステップとして登録 - + Go to next fixture tab 次の機器へ - + Go to previous fixture tab 前の機器へ - + None 無し - + Scene name: シーン名: + - All fixtures すべての機器 + - Channels Groups チャンネルグループ + - Generic 全般 - + Remove fixtures 機器を削除 - + Do you want to remove the selected fixture(s)? 選択した機器を削除しますか? @@ -5064,7 +5172,7 @@ Note that the wizard cannot tell the difference between a knob and a slider so y 接続されていないUniversを設定する - + <Double click here to enter channel number manually> チャンネルを手動で入力してください @@ -5370,118 +5478,118 @@ Duration: %3 SimpleDesk - + Universe Universe - + Next page 次のページ - + Current page 現在のページ - + Previous page 前のページ - + View mode ビューモード - + Reset universe Universe のリセット - + Playback プレイバック - - + + Cue Stack キュースタック - + Previous cue 前のキュー - + Stop cue stack キュースタックの停止 - + Next cue 次のキュー - + Clone cue stack キュースタックの複製 - + Edit cue stack キュースタックの編集 - + Record cue キューの記録 - + Channel groups チャンネルグループ - + Cue Stack - Playback %1 キュースタック - フェーダー %1 - + No selection 選択無し - + Cue name キュー名 - + Multiple Cues 複数キュー - + Delete cue キューの削除 - + Clone Cue Stack キュースタックの複製 - + Clone To Playback# 複製先フェーダー: - + Cue %1 キュー %1 @@ -5489,32 +5597,32 @@ Duration: %3 SpeedDial - + Hours - + Minutes - + Seconds - + Milliseconds ミリ秒 - + Infinite - + Tap Tap @@ -5522,17 +5630,17 @@ Duration: %3 SpeedDialWidget - + Fade In フェードイン - + Fade Out フェードアウト - + Hold ホールド @@ -5622,17 +5730,17 @@ Duration: %3 VCButton - + Choose... 選択... - + None 無し - + Button %1 ボタン %1 @@ -5647,17 +5755,17 @@ Duration: %3 画像 (%1) - + Toggle Blackout 暗転 - + Stop ALL functions! 全ファンクション停止 - + Icon アイコン @@ -5670,67 +5778,77 @@ Duration: %3 ボタンの詳細 - + + Override priority + + + + + Force LTP + + + + General 全般 - + Button label 名前 - + Text to display on the button ボタン上に表示するテキスト - + Function ファンクション - + The function that this button controls このボタンで操作するファンクション - + Attach a function to this button このボタンで操作するファンクションを設定 - + Detach the button's function attachment ファンクションを除外 - + Toggle Blackout 暗転 - + Stop All Functions すべてのファンクション停止 - + Fade time: フェードタイム: - + Adjust function intensity when it is running ファンクション再生時の明るさを設定 - + Adjust Function Intensity ファンクション再生時の明るさ - + Function's adjusted intensity percentage when run ファンクション再生時の明るさ(パーセント) @@ -5750,17 +5868,17 @@ Duration: %3 ON/OFF切り替え - + Flash the assigned function with this button ボタンを押している間だけ再生 - + Flash function (only for scenes) フラッシュ(シーンのみ対応) - + No function 無し @@ -5851,64 +5969,64 @@ Duration: %3 VCCueList - + Show/Hide crossfade sliders クロスフェーダー表示 - - + + Play/Pause Cue list キューリストの再生/一時停止 - - + + Stop Cue list キューリストの停止 - + Go to previous step in the list 前のステップへ - + Go to next step in the list 次のステップへ - + Cue list キューリスト - + Play/Stop Cue list キューリストの再生/一時停止 - + Pause Cue list キューリストの停止 - + Fade In フェードイン - + Fade Out フェードアウト - + Duration 再生継続時間 - + Notes メモ @@ -6074,7 +6192,7 @@ Duration: %3 VCFrame - + Add 追加 @@ -6127,17 +6245,17 @@ Duration: %3 ページ名 - + External Input - Enable 外部入力 - + External Input - Previous Page 外部入力 - 前のページへ - + External Input - Next Page @@ -6170,17 +6288,17 @@ Duration: %3 VCLabel - + Label メモ - + Rename Label メモの書き換え - + Caption: 説明: @@ -6188,42 +6306,42 @@ Duration: %3 VCMatrix - + Animation %1 アニメーション %1 - + End Color Reset 第二色のリセット - + Start color Red component 第一色を赤に設定 - + Start color Green component 第一色を緑に設定 - + Start color Blue component 第一色を青に設定 - + End color Red component 第二色を赤に設定 - + End color Green component 第二色を緑に設定 - + End color Blue component 第二色を青に設定 @@ -6426,48 +6544,48 @@ Duration: %3 テキストを追加 - + No function ファンクションなし - + Start Color 第一色 - + Start Color Knob 第一色ノブ - + End Color 第二色 - + End Color Knob 第二色ノブ - + End Color Reset 第二色をリセット - + Animation アニメーション - - + + Text テキスト - + Enter a text テキストを決定 @@ -6719,12 +6837,12 @@ Duration: %3 VCSlider - + Slider %1 フェーダー %1 - + Reset channels override 強制的にリセット @@ -6742,42 +6860,42 @@ Duration: %3 全般 - + Name of the slider 名前 - + Value display style 数値表示 - + Show exact DMX values DMX値を表示 - + Show value as percentage パーセント表示 - + Percentage パーセント - + Slider movement 上下 - + Normal 標準(上が100%) - + Inverted 反転(下が100%) @@ -6853,32 +6971,32 @@ Duration: %3 プレイバックモードにする - + Actual DMX - + Widget name 名前 - + Widget appearance 外見 - + Slider フェーダー - + Knob つまみ - + Catch up with the external controller input value 外部コントローラからの操作を受け付ける @@ -7276,17 +7394,17 @@ Duration: %3 ミリ秒を表示 - + Multiply by 2 Input 2つの入力 - + Divide by 2 Input 2つの入力を分ける - + Factor Reset Input 入力をリセット @@ -7294,68 +7412,68 @@ Duration: %3 VCWidget - + Button ボタン - + Slider フェーダー - + XYPad XYパッド - + Frame フレーム - + Solo frame ソロフレーム - + Speed dial スピードダイヤル - + Cue list キューリスト - + Label メモ - + Audio Triggers オーディオトリガー - + Animation アニメーション - + Clock 現在時刻 + - Unknown 不明 - + This widget has no properties このウィジェットに設定項目はありません @@ -7553,12 +7671,12 @@ Duration: %3 プリセット名 - + Pan / Horizontal Axis パン/X軸 - + Tilt / Vertical Axis チルト/Y軸 @@ -7588,44 +7706,44 @@ Duration: %3 機器の編集 - + Width - + Height 高さ - + Remove fixtures 機器の削除 - + Do you want to remove the selected fixtures? 選択した機器を削除しますか? - - + + Error エラー - + The selected Scene does not include any Pan or Tilt channel. Please select one with such channels. 選択したシーンにはPan,Tiltを操作するチャンネルが含まれていません、ポジションを操作するファンクションを選択してください - + Please select at least one fixture or head to create this type of preset! 選択したフィクスチャーにはPan,Tiltが定義されていません。ポジション操作が定義されているフィクスチャーを選択してください - + Fixture Group 機器グループ @@ -7758,12 +7876,12 @@ Please select one with such channels. VideoItem - + Fullscreen フルスクリーン - + Screen %1 スクリーン %1 diff --git a/ui/src/qlcplus_nl_NL.ts b/ui/src/qlcplus_nl_NL.ts index a1a5601fa3..7f3d2b3039 100644 --- a/ui/src/qlcplus_nl_NL.ts +++ b/ui/src/qlcplus_nl_NL.ts @@ -188,12 +188,12 @@ Fixture model - + Fixtures found: %1 Fixtures gevonden: %1 - + Dimmers Dimmers @@ -524,372 +524,372 @@ App - + Fixtures Fixtures - + Functions Functies - + Shows Shows - + Virtual Console Virtuele Console - + Simple Desk Eenvoudig - + Inputs/Outputs Inputs/Outputs - + Cannot exit in Operate mode Sluiten niet mogelijk in Operate Mode - + You must switch back to Design mode to close the application. Ga eerst terug naar Design Mode om af te sluiten. - + Close Sluiten - + Do you wish to save the current workspace before closing the application? Huidige workspace opslaan voor het afsluiten? - + Close the application? Programma afsluiten? - + Do you wish to close the application? Programma afsluiten? - + Starting Q Light Controller Plus Q Light Controller Plus aan het starten - + - New Workspace - Nieuwe workspace - + Exit Sluiten - + Switch to Design Mode Ga naar Design Mode - + There are still running functions. Really stop them and switch back to Design mode? Eén of meer functies zijn nog actief. Wil je deze stoppen en naar Design Mode gaan? - + Design Design - + Switch to design mode Ga naar Design Mode - + Operate Operate - - + + Switch to operate mode Ga naar Operate Mode - + &New &Nieuw - + CTRL+N File|New CTRL+N - + &Open &Open - + CTRL+O File|Open CTRL+O - + &Save Op&slaan - + CTRL+S File|Save CTRL+S - + Save &As... Opslaan &als... - + &Operate &Operate - + CTRL+F12 Control|Toggle operate/design mode CTRL+F12 - + &Monitor &Monitor - + CTRL+M Control|Monitor CTRL+M - + Address Tool Adres instellen - + Toggle &Blackout &Blackout aan/uit - + Live edit a function Bewerk een functie live - + Toggle Virtual Console Live edit Virtual Console Live Edit aan/uit - + Dump DMX values to a function Dump DMX waarden naar een functie - + CTRL+D Control|Dump DMX CTRL+D - + Stop ALL functions! Stop alle functies! - + Fade 1 second and stop Fade 1 seconde en stop - + Fade 5 seconds and stop Fade 5 seconden en stop - + Fade 10 second and stop Fade 10 seconden en stop - + Fade 30 second and stop Fade 30 seconden en stop - + Toggle Full Screen Volledig scherm aan/uit - + CTRL+F11 Control|Toggle Full Screen CTRL+F11 - + &Index &Index - + SHIFT+F1 Help|Index SHIFT+F1 - + &About QLC+ O&ver QLC+ - + Quit QLC+ - + Workspace Workspace - + Unable to read from file Bestand lezen mislukt - + Unable to write to file Schrijven naar het bestand mislukt - + A fatal error occurred Fatale fout opgetreden - + Unable to access resource Benaderen bron mislukt - + Unable to open file for reading or writing Bestand openen voor lezen of schrijven mislukt - + Operation was aborted Bewerking afgebroken - + Operation timed out Bewerking time-out - + An unspecified error has occurred. Nice. Er is een onbekende fout opgetreden. Fijn. - + File error Bestandsfout - - - + + + Do you wish to save the current workspace? Changes will be lost if you don't save them. Huidige workspace opslaan? De wijzigingen gaan verloren indien ze niet worden opgeslagen. - + New Workspace Nieuwe workspace - - - + + + Open Workspace Open workspace - - + + Workspaces (*%1) Workspaces (*%1) - - + + All Files (*.*) Alle bestanden (*.*) - - + + All Files (*) Alle bestanden (*) - + Save Workspace As Workspace opslaan als - + Error Fout - + File not found! The selected file has been moved or deleted. Bestand niet gevonden! Het geselecteerde bestand is verplaatst of verwijderd. - + Warning Waarschuwing - + Some errors occurred while loading the project: Er is iets fout gegaan tijdens het laden van het project: @@ -912,12 +912,12 @@ The selected file has been moved or deleted. Sluit automatisch bij een toetsaanslag - + Assign Key Wijs toets toe - + Hit the key combination that you wish to assign. You may hit either a single key or a combination using %1, %2, and %3. Sla de toetscombinatie aan die toegewezen moet worden. Toegestaan is een enkele toets of een combinatie met %1, %2, en %3. @@ -1044,23 +1044,23 @@ The selected file has been moved or deleted. AudioItem - - + + Preview Left Channel Voorbeeld linkerkanaal - + Preview Right Channel Voorbeeld rechterkanaal - + Preview Stereo Channels Voorbeeld stereokanalen - + Preview Mono Voorbeeld mono @@ -1128,52 +1128,52 @@ The selected file has been moved or deleted. Input - + None Geen - + DMX DMX - + Function Functie - + VC Widget VC Widget - + %1 channels %1 kanalen - + No function Geen functie - + No widget Geen widget - + Not assigned Niet toegewezen - + Volume Bar Volumemeter - + #%1 (%2Hz - %3Hz) #%1 (%2Hz - %3Hz) @@ -1231,12 +1231,12 @@ The selected file has been moved or deleted. Aanpassingen ongedaan maken - + Error Fout - + You are trying to overwrite a system template! Please choose another name and the template will be saved in your channel modifier's user folder. U probeert een systeem template te overschrijven! Kies een andere naam zodat het template opgeslagen kan worden in de kanaal aanpassingen gebruikers map. @@ -1251,13 +1251,13 @@ The selected file has been moved or deleted. - + Name Naam - + Type Type @@ -1277,27 +1277,27 @@ The selected file has been moved or deleted. - + Selected Geselecteerd - + Channel properties configuration Kanaal eigenschappen configuratie - + Can fade Kan faden - + Behaviour Gedrag - + Modifier Modifier @@ -1321,19 +1321,19 @@ The selected file has been moved or deleted. - + Fade In Fade IN - + Hold Hold - + Fade Out Fade Out @@ -1553,47 +1553,47 @@ The selected file has been moved or deleted. - + Cut Knippen - + Copy Kopiëren - + Paste Plakken - + Paste error Fout bij plakken - + Trying to paste on an incompatible Scene. Operation canceled. Poging om een niet-compatibele scene te plakken. Bewerking geannuleerd. - + Common Fade In Gemeenschappelijke Fade In - + Common Fade Out Gemeenschappelijke Fade Out - + Common Hold Gemeenschappelijke Hold - + Multiple Steps Meerdere stappen @@ -1649,12 +1649,12 @@ The selected file has been moved or deleted. ConsoleChannel - + Intensity Intensiteit - + Reset this channel Reset dit kanaal @@ -1715,6 +1715,83 @@ The selected file has been moved or deleted. Cue + + CustomFeedbackDialog + + + Custom Feedback Configuration + + + + + Value + Waarde + + + + Label + Label + + + + Color + + + + + Values + Waarden + + + + Lower Value + + + + + Monitor Value + + + + + Upper Value + + + + + + + Color Selection + + + + + MIDI Channel + + + + + Upper Channel + + + + + Lower Channel + + + + + Monitor Channel + + + + + + + From plugin settings + + + DmxDumpFactory @@ -1773,49 +1850,16 @@ The selected file has been moved or deleted. Scenenaam: - + Dump all channels (%1 Universes, %2 Fixtures, %3 Channels) Dump alle kanalen (%1 Universes, %2 Fixtures, %3 Kanalen) - + New Scene From Live %1 Nieuwe scene van Live %1 - - DocBrowser - - - %1 - Document Browser - %1 - Documentbrowser - - - - Backward - Terug - - - - Forward - Verder - - - - Index - Index - - - - About Qt - Over Qt - - - - Close this window - - - EFXEditor @@ -2096,12 +2140,12 @@ The selected file has been moved or deleted. Bekijk hoe het eruit ziet als de EFX loopt - + Remove fixtures Verwijder fixtures - + Do you want to remove the selected fixture(s)? Wil je de geselecteerde fixtures verwijderen? @@ -2443,88 +2487,88 @@ The selected file has been moved or deleted. Deel fixturenamen opnieuw in - - + + (remapped) (heringedeeld) - + Import Fixtures List Importeer lijst met fixtures - + Fixtures List (*%1) Fixtures lijst (*%1) - + All Files (*.*) Alle bestanden (*.*) - + All Files (*) Alle bestanden (*) - + Do you want to automatically connect fixtures with the same name? - + Generic Dimmer Algemene dimmer - + Delete Fixtures Verwijder fixtures - + Do you want to delete the selected items? Geselecteerde items verwijderen? - + Invalid operation Ongeldige bewerking - + You are trying to clone a fixture on an address already in use. Please fix the target list first. U probeert een fixture te klonen op een adres dat al in gebruik is. Pas eerst de doellijst aan. - - - - + + + + Invalid selection Ongeldige selectie - - - + + + Please select a source and a target fixture or channel to perform this operation. Selecteer een bron en een doelfixture of kanaal om deze bewerking uit te voeren. - + To perform a fixture remap, please select fixtures on both lists. Om een fixture herindeling uit te voeren dient uit beide lijsten fixtures geselecteerd te worden. - + This might take a while... Even geduld a.u.b... - + Cancel Annuleren @@ -2537,12 +2581,12 @@ The selected file has been moved or deleted. Selecteer fixture - + No fixtures available Geen fixtures beschikbaar - + Go to the Fixture Manager and add some fixtures first. Ga naar de Fixture manager en voeg eerst fixtures toe. @@ -2599,7 +2643,7 @@ The selected file has been moved or deleted. FunctionLiveEditDialog - + Function Live Edit Bewerk een functie live @@ -2607,195 +2651,195 @@ The selected file has been moved or deleted. FunctionManager - + New &scene Nieuwe &scene - + New c&haser Nieuwe c&haser - + New se&quence Nieuwe se&quence - + New c&ollection Nieuwe c&ollectie - + New E&FX Nieuwe E&FX - + New &RGB Matrix Nieuwe &RGB Matrix - + New scrip&t Nieuw scrip&t - + New au&dio Nieuwe au&dio - + New vid&eo Nieuwe vid&eo - + New fo&lder Nieuwe map (fo&lder) - + Select Startup Function Selecteer opstartfunctie - + Function &Wizard Functie&wizard - + &Clone &Klonen - + &Delete Verwij&deren - + Select &all Selecteer &alles - + New Scene Nieuwe scene - + New Chaser Nieuwe chaser - + New Sequence Nieuwe sequence - + New Collection Nieuwe collectie - + New EFX Nieuwe EFX - + New RGB Matrix Nieuwe RGB Matrix - + New Script Nieuw script - + Open Audio File Open audiobestand - + Audio Files (%1) Audiobestanden (%1) - - + + All Files (*.*) Alle bestanden (*.*) - - + + All Files (*) Alle bestanden (*) - + Unsupported audio file Niet ondersteund audiobestand - + This audio file cannot be played with QLC+. Sorry. Dit audiobestand kan niet worden afgespeeld met QLC+. Sorry. - + Open Video File Open videobestand - + Video Files (%1) Videobestanden (%1) - + Unsupported video file Niet ondersteund videobestand - + This video file cannot be played with QLC+. Sorry. Dit videobestand kan niet worden afgespeeld met QLC+. Sorry. - + Do you want to DELETE folder: Do you want to DELETE foler: VERWIJDER map: - + Do you want to DELETE functions: VERWIJDER functies: - + (This will also DELETE: Dit VERWIJDERT tevens: - + Delete Functions Verwijder functies - + Function Functie - + (Copy) (kopie) @@ -3082,32 +3126,32 @@ p, li { white-space: pre-wrap; } Verwijderen - + %1 group %1 groep - + Error Fout - + %1 has no capability supported by this wizard. %1 heeft geen functies die ondersteund worden door deze wizard. - + Presets solo frame Presets solo frame - + Click & Go RGB Klik & Gaan RGB - + Click & Go Macro Click & Go Macro @@ -3115,27 +3159,27 @@ p, li { white-space: pre-wrap; } GrandMasterSlider - + GM GM - + Grand Master <B>limits</B> the maximum value of Grand Master <B>limiteert</B> de maximumwaarde van - + Grand Master <B>reduces</B> the current value of Grand Master <B>verlaagt</B> de huidige waarde van - + intensity channels intensiteit kanalen - + all channels alle kanalen @@ -3237,45 +3281,45 @@ p, li { white-space: pre-wrap; } InputOutputManager - + Add U&niverse U&niverse toevoegen - + &Delete Universe Universe Universe verwij&deren - + Universe name: Universe naam: - + Passthrough Doorgeven - - + + Universe %1 Universe %1 - - + + Delete Universe Universe verwijderen - + The universe you are trying to delete is patched. Are you sure you want to delete it? De universe die u probeert te verwijderen is gepatched. Weet u zeker dat u deze wilt verwijderen? - + There are some fixtures using the universe you are trying to delete. Are you sure you want to delete it? Er zijn fixtures die gebruik maken van de universe die u wilt verwijderen. Weet u zeker dat u deze wilt verwijderen? @@ -3382,20 +3426,20 @@ p, li { white-space: pre-wrap; } Niveau Monitor - - - + + + Error Fout - - + + Output line already assigned Output line is al toegewezen - + An error occurred while trying to open the selected device line. This can be caused either by a wrong system configuration or an unsupported input/output mode. Please refer to the plugins documentation to troubleshoot this. @@ -3404,67 +3448,67 @@ Dit kan komen door een verkeerde systeem configuratie of een niet ondersteunde i Zie de plugin documentatie voor uitleg over dit onderwerp. - - + + Existing Input Profile Bestaand inputprofiel - - + + An input profile at %1 already exists. Do you wish to overwrite it? Inputprofiel %1 bestaat al. Overschijven? - - + + Save Input Profile Sla input profiel op - - + + Input Profiles (*.qxi) Inputprofielen (*.qxi) - - + + Saving failed Opslaan mislukt - + Unable to save the profile to %1 Opslaan als %1 lukt niet - + Delete profile Verwijder profiel - + Do you wish to permanently delete profile "%1"? Profiel "%1" definitief verwijderen? - + File deletion failed Verwijderen bestand mislukt - + Unable to delete file %1 Verwijderen bestand %1 mislukt - + Unable to save %1 to %2 %1 opslaan als %2 mislukt - + Default device Standaard apparaat @@ -3477,176 +3521,233 @@ Zie de plugin documentatie voor uitleg over dit onderwerp. Inputprofiel Editor - + General Algemeen - + The name of the company that made the device De naam van de fabrikant - + The device's model name Modelnaam - + Manufacturer Fabrikant - + Model Model - - + + Type Type - + MIDI Global Settings Globale MIDI Instellingen - + When MIDI notes are used, send a Note Off when value is 0 Indien MIDI notes gebruikt worden, verstuur dan een Note Off als de waarde 0 is - - Channels - Kanalen - - - + + Channel Kanaal - + + Name Naam - + Custom feedback Aangepaste feedback - + Upper value Hoge waarde - + Lower value Lage waarde - - + Behaviour Gedrag - + Add a new channel description Voeg een nieuwe kanaal omschrijving toe - + Remove the selected channels Verwijder de geselecteerde kanalen - + Edit the selected channel Wijzig het geselecteerde kanaal - + Automatically add channels to the list when you wiggle the device's controls Voeg automatisch kanalen toe bij het bedienen van de controls - + + Input Mapping + + + + Movement Beweging - + Absolute Absoluut - + Relative Relatief - + Generate an extra Press/Release when toggled - + + + MIDI channel + + + + + Colors + + + + + Remove the selected color + + + + + Add a new color + + + + + Value + Waarde + + + + Label + Label + + + + Color + + + + + MIDI Channels + + + + + Add a new MIDI channel + + + + + Remove the selected MIDI channel + + + + Sensitivity Gevoeligheid - + File not writable Bestand niet beschrijfbaar - + You do not have permission to write to the file %1. You might not be able to save your modifications to the profile. Onvoldoende permissies om te schrijven naar bestand %1.Mogelijk worden er wijzigingen aan het profiel niet opgeslagen. - + + From plugin settings + + + + Missing information Informatie ontbreekt - + Manufacturer and/or model name is missing. Fabrikant en/of modelnaam ontbreekt. - - + + Channel already exists Kanaal bestaat al - - + + Channel %1 already exists Kanaal %1 bestaat al - + Delete channels Verwijder kanalen - + Delete all %1 selected channels? Alle %1 geselecteerde kanalen verwijderen? - + Channel wizard activated Kanaal wizard geactiveerd - + 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. @@ -3658,12 +3759,39 @@ Let op: de wizard maakt geen onderscheid tussen een knop of een slider, dit zal De wizard kent het verschil tussen een knop en een slider niet. Deze dient handmatig aangepast te worden. - + + + Enter value + + + + + Feedback value + + + + + + Enter label + + + + + Color label + + + + + MIDI channel label + + + + Button %1 Knop %1 - + Slider %1 Slider %1 @@ -3696,65 +3824,50 @@ De wizard kent het verschil tussen een knop en een slider niet. Deze dient handm Externe input - + When toggled, you can click an external button to assign it to this widget. Indien aangezet is het mogelijk om een externe input te koppelen aan deze widget. - + Auto Detect Autodetectie - + Input Universe Input Universe - + Input Channel Input Kanaal - + The input universe that sends data to this widget De input universe die data stuurt naar deze widget - + Custom Feedback Aangepaste Feedback - + The particular input channel within the input universe that sends data to this widget Het input kanaal binnen de input universe dat data verstuurd naar deze widget - + Choose an external input universe & channel that this widget should listen to. Kies een externe input universe en kanaal waarnaar deze widget moet luisteren. - + Choose... Kies... - - - Custom feedback - Aangepaste feedback - - - - Lower value - Lage waarde - - - - Upper value - Hoge waarde - Monitor @@ -3911,14 +4024,14 @@ De wizard kent het verschil tussen een knop en een slider niet. Deze dient handm Achtergrond - - + + Select background image Selecteer achtergrondafbeelding - - + + Images Afbeeldingen @@ -4209,12 +4322,12 @@ De wizard kent het verschil tussen een knop en een slider niet. Deze dient handm PlaybackSlider - + Select Selecteer - + Flash Flits @@ -4230,34 +4343,29 @@ De wizard kent het verschil tussen een knop en een slider niet. Deze dient handm QObject - + Operate Bedienen - + Design Ontwerpen - - + + Reversed Omgekeerd - + Page: %1 Pagina: %1 RDMManager - - - Form - - Scan for RDM devices... @@ -4677,22 +4785,22 @@ De wizard kent het verschil tussen een knop en een slider niet. Deze dient handm Geen - + No fixture group to control Geen fixturegroep om te bedienen - + Select image Selecteer afbeelding - + Images Afbeeldingen - + Sequence @@ -4752,105 +4860,105 @@ De wizard kent het verschil tussen een knop en een slider niet. Deze dient handm Kanalen van alle fixtures aanzetten - + Enable all channels in current fixture Alle kanalen van deze fixture aanzetten - + Disable all channels in current fixture Alle kanalen van deze fixture uitzetten - + Copy current values to clipboard Kopieer huidige waardes naar het klembord - + Paste clipboard values to current fixture Plak klembordwaarden naar deze fixture - + Copy current values to all fixtures Kopieer huidige waarden naar alle fixtures - + Color tool for CMY/RGB-capable fixtures Kleurengereedschap voor CMY/RGB compatibele fixtures - + Position tool for moving heads/scanners Positiegereedschap voor moving heads/scanners - + Switch between tab view and all channels view Wissel tussen tabweergave en alle kanalenweergave - + Toggle blind mode Schakel naar blinde modus - + Show/Hide speed dial window Toon/verberg speed dial venster - + Clone this scene and append as a new step to the selected chaser Kloon deze scene en voeg toe als nieuwe step aan de geselecteerde chaser - + Go to next fixture tab Volgende fixture tab - + Go to previous fixture tab Vorige fixture tab - + None Geen - + Scene name: Scene naam: + - All fixtures Alle fixtures + - Generic Algemeen - + Remove fixtures Verwijder fixtures - + Do you want to remove the selected fixture(s)? Wil je de geselecteerde fixtures verwijderen? + - Channels Groups Kanaalgroepen @@ -5059,7 +5167,7 @@ De wizard kent het verschil tussen een knop en een slider niet. Deze dient handm Ongepatchte universes toestaan - + <Double click here to enter channel number manually> <Dubbelklik hier om een kanaal nummer handmatig in te voeren> @@ -5365,118 +5473,118 @@ Duur: %3 SimpleDesk - + View mode Weergavemodus - + Previous page Vorige pagina - + Current page Huidige pagina - + Next page Volgende pagina - + Reset universe Reset universe - + Universe Universe - + Playback Playback - - + + Cue Stack Cue stack - + Previous cue Vorige cue - + Stop cue stack Stop cue stack - + Next cue Volgende cue - + Clone cue stack Kloon cue stack - + Edit cue stack Wijzig cue stack - + Record cue Neem cue op - + Channel groups Kanaalgroepen - + Cue Stack - Playback %1 Cue Stack - Playback %1 - + No selection Geen selectie - + Cue name Cue naam - + Multiple Cues Meerdere cues - + Delete cue Verwijder cue - + Clone Cue Stack Kloon cue stack - + Clone To Playback# Kloon naar Playback# - + Cue %1 Cue %1 @@ -5484,32 +5592,32 @@ Duur: %3 SpeedDial - + Hours Uren - + Minutes Minuten - + Seconds Seconden - + Milliseconds Milliseconden - + Infinite Oneindig - + Tap Tap @@ -5517,17 +5625,17 @@ Duur: %3 SpeedDialWidget - + Fade In Fade IN - + Fade Out Fade Out - + Hold Hold @@ -5617,17 +5725,17 @@ Duur: %3 VCButton - + Choose... Kies... - + None Geen - + Button %1 Knop %1 @@ -5642,17 +5750,17 @@ Duur: %3 Afbeeldingen (%1) - + Toggle Blackout Blackout aan/uit - + Stop ALL functions! Stop alle functies! - + Icon Icoon @@ -5665,37 +5773,37 @@ Duur: %3 Knop eigenschappen - + General Algemeen - + Button label Knop naam - + Text to display on the button Tekst om te tonen op de knop - + Function Functie - + The function that this button controls De functie die door deze knop bestuurd wordt - + Attach a function to this button Koppel een functie aan deze knop - + Detach the button's function attachment Ontkoppel de functie van de knop @@ -5715,47 +5823,57 @@ Duur: %3 Zet functie aan/uit - + Flash the assigned function with this button Flits de toegewezen functie met deze knop - + Flash function (only for scenes) Flits functie (alleen voor scenes) - + + Override priority + + + + + Force LTP + + + + Toggle Blackout Blackout aan/uit - + Stop All Functions Stop alle functies - + Fade time: Fade tijd: - + Adjust function intensity when it is running Pas de intensiteit van de functie aan als de knop actief is - + Adjust Function Intensity Functie Intensiteit aanpassen - + Function's adjusted intensity percentage when run Aangepaste intensiteit percentage van de functie wanneer het runt - + No function Geen functie @@ -5846,64 +5964,64 @@ Duur: %3 VCCueList - + Show/Hide crossfade sliders Toon/verberg crossfade sliders - - + + Play/Pause Cue list - - + + Stop Cue list - + Go to previous step in the list Ga naar vorige stap in de lijst - + Go to next step in the list Ga naar volgende stap in de lijst - + Cue list Cue list - + Play/Stop Cue list - + Pause Cue list - + Fade In Fade In - + Fade Out Fade Out - + Duration Duur - + Notes Notities @@ -6068,7 +6186,7 @@ Duur: %3 VCFrame - + Add Toevoegen @@ -6121,17 +6239,17 @@ Duur: %3 - + External Input - Enable Externe input - Inschakelen - + External Input - Previous Page - + External Input - Next Page @@ -6164,17 +6282,17 @@ Duur: %3 VCLabel - + Label Label - + Rename Label Hernoem label - + Caption: Opschrift: @@ -6182,42 +6300,42 @@ Duur: %3 VCMatrix - + Animation %1 Animatie %1 - + End Color Reset Eind kleur reset - + Start color Red component Start kleur Rood component - + Start color Green component Start kleur Groen component - + Start color Blue component Start kleur Blauw component - + End color Red component Eind kleur Rood component - + End color Green component Eind kleur Groen component - + End color Blue component Eind kleur Blauw component @@ -6420,48 +6538,48 @@ Duur: %3 Tekst toeveogen - + No function Geen functie - + Start Color Start kleur - + Start Color Knob Start Kleur Knop - + End Color Eind kleur - + End Color Knob Eind Kleur Knop - + End Color Reset Eind Kleur Reset - + Animation Animatie - - + + Text Tekst - + Enter a text Voer een tekst in @@ -6712,12 +6830,12 @@ Duur: %3 VCSlider - + Slider %1 Slider %1 - + Reset channels override @@ -6735,72 +6853,72 @@ Duur: %3 Algemeen - + Value display style Weergavestijl van waarden - + Show exact DMX values Toon exacte DMX waarden - + Actual Huidig - + Show value as percentage Toon waarde als percentage - + Percentage Percentage - + Widget name Widgetnaam - + Name of the slider Naam van de slider - + Slider movement Beweging slider - + Normal Normaal - + Inverted Omgekeerd - + Widget appearance Widget uiterlijk - + Slider Slider - + Knob Knop - + Catch up with the external controller input value @@ -7268,17 +7386,17 @@ Duur: %3 Toon het milliseconden veld - + Multiply by 2 Input Vermenigvuldig met 2 Input - + Divide by 2 Input Deel door 2 Input - + Factor Reset Input Factor Reset Input @@ -7286,68 +7404,68 @@ Duur: %3 VCWidget - + Button Knop - + Slider Slider - + XYPad XYPad - + Frame Frame - + Solo frame Solo frame - + Speed dial Speed dial - + Cue list Cue list - + Label Label - + Audio Triggers Audio Triggers - + Animation Animatie - + Clock Klok + - Unknown Onbekend - + This widget has no properties Deze widget heeft geen eigenschappen @@ -7545,12 +7663,12 @@ Duur: %3 Preset naam - + Pan / Horizontal Axis Pan / Horizontale as - + Tilt / Vertical Axis Tilt / Verticale as @@ -7580,45 +7698,45 @@ Duur: %3 Omgekeerd - + Width Breedte - + Height Hoogte - + Remove fixtures Verwijder fixtures - + Do you want to remove the selected fixtures? Wil je de geselecteerde fixtures verwijderen? - - + + Error Fout - + The selected Scene does not include any Pan or Tilt channel. Please select one with such channels. De geselecteerde Scene bevat geen Pan of Tilt kanaal. Selecteer een Scene met zo'n kanaal. - + Please select at least one fixture or head to create this type of preset! - + Fixture Group Fixturegroep @@ -7751,12 +7869,12 @@ Selecteer een Scene met zo'n kanaal. VideoItem - + Fullscreen Volledig scherm - + Screen %1 Scherm %1 diff --git a/ui/src/qlcplus_pt_BR.ts b/ui/src/qlcplus_pt_BR.ts index a888eb0622..19d13b506a 100644 --- a/ui/src/qlcplus_pt_BR.ts +++ b/ui/src/qlcplus_pt_BR.ts @@ -189,12 +189,12 @@ Quantidade de canais vazios a deixar entre fixtures adicionados - + Fixtures found: %1 Fixtures encontrados: %1 - + Dimmers Dimmers @@ -525,376 +525,376 @@ App - + Cannot exit in Operate mode Não pode sair em Modo de Operação - + You must switch back to Design mode to close the application. Tem que mudar para o Modo de Edição para fechar a aplicação. - + Close Fechar - + Do you wish to save the current workspace before closing the application? Deseja guardar a área de trabalho antes de fechar a aplicação? - + Starting Q Light Controller Plus Starting Q Light Controller Iniciando Q Light Controller Plus - + - New Workspace - Nova área de trabalho - + Switch to Design Mode Mudar para Modo de Edição - + There are still running functions. Really stop them and switch back to Design mode? Ainda há funções a executar. Deseja parar e voltar ao Modo Edição? - + Design Edição - + Switch to design mode Mudar para Modo de Edição - + Operate Operação - - + + Switch to operate mode Mudar para Modo de Operação - + &New &Novo - + CTRL+N File|New - + &Open &Abrir - + CTRL+O File|Open - + &Save &Guardar - + CTRL+S File|Save CTRL+S - + Save &As... Guardar &como... - + &Operate &Operação - + &Monitor &Monitor - + Toggle &Blackout Activar/Desactivar &Blackout - + CTRL+F12 Control|Toggle operate/design mode - + CTRL+M Control|Monitor - + Live edit a function Editar função ao vivo - + Toggle Full Screen Alterar para Ecrã Completo - + CTRL+F11 Control|Toggle Full Screen - + &Index &Indice - + SHIFT+F1 Help|Index - + &About QLC+ &About QLC Acerca &QLC+ - + Fixtures Fixtures - + Functions Funções - + Shows Shows - + Virtual Console Consola Virtual - + Simple Desk Mesa Simples - + Inputs/Outputs Entradas/Saídas - + Close the application? Fechar a aplicação? - + Do you wish to close the application? Deseja fechar a aplicação? - + Exit Sair - + Address Tool Ferramenta de endereçamento - + Toggle Virtual Console Live edit - + Dump DMX values to a function Atribuirvalores DMX a uma função - + CTRL+D Control|Dump DMX - + Stop ALL functions! ¡Detener TODAS las fiunções! - + Fade 1 second and stop Fade 1 segundo e parar - + Fade 5 seconds and stop Fade 5 segundos e parar - + Fade 10 second and stop Fade 10 segundos e parar - + Fade 30 second and stop Fade 30 segundos e parar - + Quit QLC+ - + Workspace Área de trabalho - + Unable to read from file Não é possível ler desde o ficheiro - + Unable to write to file Não é possível escrever para o ficheiro - + A fatal error occurred Ocorreu um erro fatal - + Unable to access resource Não é possível aceder ao recurso - + Unable to open file for reading or writing Não é possível abrir o ficheiro para ler ou escrever - + Operation was aborted Operação cancelada - + Operation timed out Tempo de operação excedido - + An unspecified error has occurred. Nice. Ocorreu um erro desconhecido. - + File error Erro de ficheiro - - - + + + Do you wish to save the current workspace? Changes will be lost if you don't save them. Deseja guardar a àrea de Trabalho actual? As alterações serão perdidas se não as gravar. - + New Workspace Nova área de trabalho - - - + + + Open Workspace Abrir Área de Trabalho - - + + Workspaces (*%1) Áreas de Trabalho (*%1) - - + + All Files (*.*) Todos os ficheiros (*.*) - - + + All Files (*) Todos os ficheiros (*) - + Save Workspace As SalvarÁrea de Trabalho Como - + Error Erro - + File not found! The selected file has been moved or deleted. Ficheiro não encontrado! O ficheiro seleccionado foi movido ou apagado. - + Warning Atenção - + Some errors occurred while loading the project: Ocorreram alguns erros ao carregar o projecto: @@ -917,12 +917,12 @@ O ficheiro seleccionado foi movido ou apagado. Fechar automaticamente ao pressionar uma tecla - + Assign Key Atribuir tecla - + Hit the key combination that you wish to assign. You may hit either a single key or a combination using %1, %2, and %3. Pressione a combinação de teclas que pretende atribuir. Pode presionar uma só tecla ou uma combinação usando %1, %2 e %3. @@ -1049,23 +1049,23 @@ O ficheiro seleccionado foi movido ou apagado. AudioItem - - + + Preview Left Channel Visualizar Canal Esquerdo - + Preview Right Channel Visualizar Canal Direito - + Preview Stereo Channels Visualizar Canais Stereo - + Preview Mono Visualizar Mono @@ -1133,52 +1133,52 @@ O ficheiro seleccionado foi movido ou apagado. Entrada - + None Nenhum - + DMX DMX - + Function Função - + VC Widget Widget CV - + %1 channels %1 canais - + No function Sem função - + No widget Sem widget - + Not assigned Não atribuído - + Volume Bar Barra de volume - + #%1 (%2Hz - %3Hz) #%1 (%2Hz - %3Hz) @@ -1236,12 +1236,12 @@ O ficheiro seleccionado foi movido ou apagado. - + Error Erro - + You are trying to overwrite a system template! Please choose another name and the template will be saved in your channel modifier's user folder. @@ -1256,13 +1256,13 @@ O ficheiro seleccionado foi movido ou apagado. - + Name Nome - + Type Tipo @@ -1282,27 +1282,27 @@ O ficheiro seleccionado foi movido ou apagado. - + Selected - + Channel properties configuration - + Can fade - + Behaviour - + Modifier @@ -1336,13 +1336,13 @@ O ficheiro seleccionado foi movido ou apagado. - + Fade In Fade In - + Fade Out Fade Out @@ -1363,7 +1363,7 @@ O ficheiro seleccionado foi movido ou apagado. - + Hold Pausa @@ -1559,47 +1559,47 @@ O ficheiro seleccionado foi movido ou apagado. Para Trás - + Cut Cortar - + Copy Copiar - + Paste Colar - + Paste error Error ao colar - + Trying to paste on an incompatible Scene. Operation canceled. A tentar colar uma Cena incompatível. Operação cancelada. - + Common Fade In Tempo de Fade In comum - + Common Fade Out Tempo de Fade Out comum - + Common Hold Espera comum - + Multiple Steps Passos múltiplos @@ -1655,12 +1655,12 @@ O ficheiro seleccionado foi movido ou apagado. ConsoleChannel - + Intensity Intensidade - + Reset this channel @@ -1721,6 +1721,83 @@ O ficheiro seleccionado foi movido ou apagado. Cue + + CustomFeedbackDialog + + + Custom Feedback Configuration + + + + + Value + + + + + Label + Etiqueta + + + + Color + + + + + Values + Valores + + + + Lower Value + + + + + Monitor Value + + + + + Upper Value + + + + + + + Color Selection + + + + + MIDI Channel + + + + + Upper Channel + + + + + Lower Channel + + + + + Monitor Channel + + + + + + + From plugin settings + + + DmxDumpFactory @@ -1780,50 +1857,17 @@ O ficheiro seleccionado foi movido ou apagado. Nome da Cena: - + Dump all channels (%1 Universes, %2 Fixtures, %3 Channels) Dump all DMX values (%1 Universes, %2 Fixtures, %3 Channels) Baixar todos os canais (%1 Universos, %2 Fixtures, %3 Canais) - + New Scene From Live %1 Nova Cena a partir de Ao Vivo %1 - - DocBrowser - - - %1 - Document Browser - %1 - Explorador de Documentos - - - - Backward - Para Trás - - - - Forward - Para a Frente - - - - Index - Índice - - - - About Qt - Acerca de Qt - - - - Close this window - - - EFXEditor @@ -2104,12 +2148,12 @@ O ficheiro seleccionado foi movido ou apagado. Ver o qué faz o EFX quando está a ser executado - + Remove fixtures Remover fixtures - + Do you want to remove the selected fixture(s)? Deseja remover os fixtures seleccionados? @@ -2455,89 +2499,89 @@ O ficheiro seleccionado foi movido ou apagado. Reatribuir nome de fixtures - - + + (remapped) (remapeado) - + Import Fixtures List Importar lista de Fixtures - + Fixtures List (*%1) Lista de Fixtures (*%1) - + All Files (*.*) Todos os ficheiros (*.*) - + All Files (*) Todos os ficheiros (*) - + Do you want to automatically connect fixtures with the same name? - + Generic Dimmer Dimmer genérico - + Delete Fixtures Eliminar fixture - + Do you want to delete the selected items? Deseja eliminar os ítems seleccionados? - + Invalid operation Operação inválida - + You are trying to clone a fixture on an address already in use. Please fix the target list first. Está a tentar clonar um fixture para um endereço em uso. Por favor ajuste a lista de destino primeiro. - - - - + + + + Invalid selection Selecção inválida - - - + + + Please select a source and a target fixture or channel to perform this operation. confirmar Por favor, seleccione uma fonte e um destino do fixture ou o canal para realizar esta operação. - + To perform a fixture remap, please select fixtures on both lists. Para realizar la reasignación de fixtures, por favor seleccione fixtures en ambas listas. - + This might take a while... Isto pode demorar um pouco... - + Cancel Cancelar @@ -2550,12 +2594,12 @@ O ficheiro seleccionado foi movido ou apagado. Seleccionar fixture - + No fixtures available Sem fixtures disponíveis - + Go to the Fixture Manager and add some fixtures first. Ir primeiro ao Gestor de Fixtures e adicionar um fixture. @@ -2612,7 +2656,7 @@ O ficheiro seleccionado foi movido ou apagado. FunctionLiveEditDialog - + Function Live Edit Função Edição Ao Vivo @@ -2620,195 +2664,195 @@ O ficheiro seleccionado foi movido ou apagado. FunctionManager - + New &scene Nova &Cena - + New c&haser Novo c&hase - + New se&quence Nova &sequência - + New c&ollection Nova &colecção - + New E&FX Novo E&FX - + New &RGB Matrix Nova Matriz &RGB - + New scrip&t Novo srip&t - + New Scene Nova Cena - + New Chaser Novo Chase - + New Sequence Nova Sequência - + &Clone C&lonar - + New au&dio Novo au&dio - + New vid&eo - + New fo&lder Nova Pas&ta - + Select Startup Function Seleccionar Funçao de arranque - + Function &Wizard &Asistente de Funções - + &Delete &Eliminar - + Select &all Seleccionar &tudo - + New Collection Nova Colecção - + New EFX Novo EFX - + New RGB Matrix Nova Matriz RGB - + New Script Novo Script - + Open Audio File Abrir Ficheiro de Audio - + Audio Files (%1) Ficheiros de Audio (%1) - - + + All Files (*.*) Todos os ficheiros (*.*) - - + + All Files (*) Todos os ficheiros (*) - + Unsupported audio file Ficheiro de audio não suportado - + This audio file cannot be played with QLC+. Sorry. Pedimos desculpa, mas este ficheiro de audio não pode ser reproduzido com o QLC+. - + Open Video File - + Video Files (%1) - + Unsupported video file - + This video file cannot be played with QLC+. Sorry. - + Do you want to DELETE folder: Do you want to DELETE foler: Deseja ELIMINAR a pasta: - + Do you want to DELETE functions: Deseja ELIMINAR as funções: - + (This will also DELETE: (Isto também APAGARÁ: - + Delete Functions Eliminar Funções - + Function Função - + (Copy) (Copiar) @@ -3095,32 +3139,32 @@ p, li { white-space: pre-wrap; } Eliminar - + %1 group %1 grupo - + Error Erro - + %1 has no capability supported by this wizard. %1 não tem capacidades suportadas por este assistente. - + Presets solo frame Moldura solo de presets - + Click & Go RGB RGB Click & Go - + Click & Go Macro Macro Click & Go @@ -3128,27 +3172,27 @@ p, li { white-space: pre-wrap; } GrandMasterSlider - + GM GM - + Grand Master <B>limits</B> the maximum value of Grand Master <B>limita</B> o valor máximo de - + Grand Master <B>reduces</B> the current value of Grand Master <B>reduz</B> o valor actual de - + intensity channels intensidade dos canais - + all channels todos os canais @@ -3249,45 +3293,45 @@ p, li { white-space: pre-wrap; } InputOutputManager - + Add U&niverse Adicionar U&niverso - + &Delete Universe Universe &Apagar universo - + Universe name: Nome do Universo: - + Passthrough Saltar - - + + Universe %1 Universo %1 - - + + Delete Universe Apagar Universo - + The universe you are trying to delete is patched. Are you sure you want to delete it? O universo que está a tentar apagar tem patch efectuado. Tem a certeza que deseja apagá-lo? - + There are some fixtures using the universe you are trying to delete. Are you sure you want to delete it? Existem alguns fixtures a usar o universo que está a tentar apagar. Tem a certeza que deseja apagá-lo? @@ -3394,87 +3438,87 @@ p, li { white-space: pre-wrap; } - - - + + + Error Erro - - + + Output line already assigned Linha de saída já atribuída - + An error occurred while trying to open the selected device line. This can be caused either by a wrong system configuration or an unsupported input/output mode. Please refer to the plugins documentation to troubleshoot this. - - + + Existing Input Profile Perfil de entrada existente - - + + An input profile at %1 already exists. Do you wish to overwrite it? Um perfil de Entrada em %1 já existe. Deseja substitui-lo? - - + + Save Input Profile Guardar o perfil de Entrada - - + + Input Profiles (*.qxi) Perfis de Entrada (*.qxi) - - + + Saving failed Erro ao Guardar - + Unable to save the profile to %1 Não é possível guardar o perfil em %1 - + Delete profile Eliminar perfil - + Do you wish to permanently delete profile "%1"? Deseja eliminar permanentemente este perfil "%1"? - + File deletion failed Erro ao eliminar o ficheiro - + Unable to delete file %1 Não é possível apagar o ficheiro%1 - + Unable to save %1 to %2 Não é possível guardar %1 em %2 - + Default device Dispositivo por defeito @@ -3487,177 +3531,234 @@ Please refer to the plugins documentation to troubleshoot this. Editor de Perfil de Entrada - + General Geral - + Manufacturer Fabricante - + The name of the company that made the device Nome da companhia fabricante do dispositivo - + Model Modelo - + The device's model name Nome do modelo do dispositivo - - Channels - Canais - - - + + Channel Canal - + + Name Nome - + Custom feedback - + Upper value - + Lower value - - + + Type Tipo - + MIDI Global Settings - + When MIDI notes are used, send a Note Off when value is 0 - - + + Input Mapping + + + + Behaviour - + Add a new channel description Adicionar descrição para o canal novo - + + + MIDI channel + + + + + Colors + + + + + Remove the selected color + + + + + Add a new color + + + + + Value + + + + + Label + Etiqueta + + + + Color + + + + + MIDI Channels + + + + + Add a new MIDI channel + + + + + Remove the selected MIDI channel + + + + Remove the selected channels Eliminar os canais seleccionados - + Edit the selected channel Editar canal seleccionado - + Automatically add channels to the list when you wiggle the device's controls procurar melhor tradução Adicionar automaticamente canais à lista quando mover os controlos do dispositivo - + Movement Movimento - + Absolute - + Relative Relativo - + Generate an extra Press/Release when toggled - + Sensitivity - + File not writable Ficheiro só de leitura - + You do not have permission to write to the file %1. You might not be able to save your modifications to the profile. Não tem permissão para escrever no ficheiro %1. Poderá não ser possível guardar as modificações no perfil. - + + From plugin settings + + + + Missing information Informação em falta - + Manufacturer and/or model name is missing. Falta nome do fabricante e/ou do modelo. - - + + Channel already exists Canal já existe - - + + Channel %1 already exists Canal %1 já existe - + Delete channels Eliminar canais - + Delete all %1 selected channels? Eliminar todos os %1 canais seleccionados? - + Channel wizard activated Asistente de Canais 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. Note that the wizard cannot tell the difference between a knob and a slider so you will have to do the change manually. @@ -3667,12 +3768,39 @@ Note that the wizard cannot tell the difference between a knob and a slider so y Note que o assistente não diferencia entre uma roda e um fader pelo que terá de efectuar a alteração manualmente. - + + + Enter value + + + + + Feedback value + + + + + + Enter label + + + + + Color label + + + + + MIDI channel label + + + + Button %1 Botão %1 - + Slider %1 Fader %1 @@ -3705,65 +3833,50 @@ Note que o assistente não diferencia entre uma roda e um fader pelo que terá d Entrada externa - + When toggled, you can click an external button to assign it to this widget. Quando activo, pode pressionar um botão externo para para o atribuir a este widget. - + Auto Detect Detectar automaticamente - + Input Universe Universo de entrada - + Input Channel Canal de entrada - + The input universe that sends data to this widget O universo de entrada que envia dados a este widget - + Custom Feedback - + The particular input channel within the input universe that sends data to this widget O canal de entrada dentro do universo de entrada que envia dados a este widget - + Choose an external input universe & channel that this widget should listen to. Seleccionar um universo de Entrada e um canal externos que este widget deve escutar. - + Choose... Seleccionar... - - - Custom feedback - - - - - Lower value - - - - - Upper value - - Monitor @@ -3920,14 +4033,14 @@ Note que o assistente não diferencia entre uma roda e um fader pelo que terá d - - + + Select background image Seleccionar imagem de fundo - - + + Images @@ -4217,12 +4330,12 @@ Note que o assistente não diferencia entre uma roda e um fader pelo que terá d PlaybackSlider - + Select Seleccionar - + Flash Flash @@ -4238,34 +4351,29 @@ Note que o assistente não diferencia entre uma roda e um fader pelo que terá d QObject - + Operate Operação - + Design Edição - - + + Reversed Invertido - + Page: %1 Página: %1 RDMManager - - - Form - - Scan for RDM devices... @@ -4686,22 +4794,22 @@ Note que o assistente não diferencia entre uma roda e um fader pelo que terá d Nenhum - + No fixture group to control Nenhum grupo de fixtures para controlar - + Select image Seleccionar imagem - + Images - + Sequence @@ -4761,105 +4869,105 @@ Note que o assistente não diferencia entre uma roda e um fader pelo que terá d Desactivar todos os canais de todos os fixtures - + Enable all channels in current fixture Activar todos os canais deste fixture - + Disable all channels in current fixture Desactivar todos os canais deste fixture - + Copy current values to clipboard Copiar os valores actuais para a área de tranferência - + Paste clipboard values to current fixture Colar os valores da área de transferência para o fixture actual - + Copy current values to all fixtures Copiar os valores actuais para todos os fixtures - + Color tool for CMY/RGB-capable fixtures Ferramenta de cor para fixtures com capacidade CMY/RGB - + Position tool for moving heads/scanners - + Switch between tab view and all channels view Pasar de la vista de pestañas a la vista de canales - + Toggle blind mode Activar/Desactivar Modo Blind - + Show/Hide speed dial window Mostrar/Ocultar janela de selector de velocidade - + Clone this scene and append as a new step to the selected chaser Clonar esta cena e adiconá-la como um novo passo nol chase seleccionado - + Go to next fixture tab Ir para a aba do fixture seguinte - + Go to previous fixture tab Ir para a aba do fixture anterior - + None Nenhum - + Scene name: Nome da cena: + - All fixtures Todos os fixtures + - Channels Groups Grupos de Canais + - Generic Genérico - + Remove fixtures Remover fixtures - + Do you want to remove the selected fixture(s)? Deseja eliminar o(s) fixture(s) seleccionado(s)? @@ -5068,7 +5176,7 @@ Note que o assistente não diferencia entre uma roda e um fader pelo que terá d - + <Double click here to enter channel number manually> <Duplo clique aqui para introduzir manualmente o número do canal> @@ -5371,118 +5479,118 @@ Duration: %3 SimpleDesk - + Universe Universo - + Next page Página seguinte - + Current page Página Actual - + Previous page Página Anterior - + View mode Modo Vista - + Reset universe Reiniciar universo - + Playback Reproduzir - - + + Cue Stack Cue Stack - + Previous cue Cue Anterior - + Stop cue stack Parar cue stack - + Next cue Próxima Cue - + Clone cue stack Clonar cue stack - + Edit cue stack Editar Cue Stack - + Record cue Guardar Cue - + Channel groups - + Cue Stack - Playback %1 Cue Stack - Reproduzir %1 - + No selection Nenhuma selecção - + Cue name Nome da Cue - + Multiple Cues Cues múltiplas - + Delete cue Eliminar Cue - + Clone Cue Stack Clonar Cue stack - + Clone To Playback# Clonar para Reproduzção# - + Cue %1 Cue %1 @@ -5490,32 +5598,32 @@ Duration: %3 SpeedDial - + Hours Horas - + Minutes Minutos - + Seconds Segundos - + Milliseconds Milisegundos - + Infinite Infinito - + Tap Tap @@ -5523,17 +5631,17 @@ Duration: %3 SpeedDialWidget - + Fade In Fade In - + Fade Out Fade Out - + Hold Pausa @@ -5623,17 +5731,17 @@ Duration: %3 VCButton - + Choose... Seleccionar... - + None Nenhum - + Button %1 Botão %1 @@ -5648,17 +5756,17 @@ Duration: %3 Imagens (%1) - + Toggle Blackout Activar/Desactivar Blackout - + Stop ALL functions! Parar TODAS as funções! - + Icon Ícone @@ -5671,67 +5779,77 @@ Duration: %3 Propriedades de Botão - + + Override priority + + + + + Force LTP + + + + General Geral - + Button label Etiqueta de Botão - + Text to display on the button Texto a mostrar no botão - + Function Função - + The function that this button controls A função que este botão controla - + Attach a function to this button Anexar uma função a este botão - + Detach the button's function attachment Liberta a função atribuida ao botão - + Toggle Blackout Blackout On/Off - + Stop All Functions Parar TODAS as funções - + Fade time: - + Adjust function intensity when it is running Ajusta a intensidade da funçõ quando está a ser executada - + Adjust Function Intensity Ajustar a intensidade da função - + Function's adjusted intensity percentage when run Percentagem de intensidade de função ajustada quando executa @@ -5751,17 +5869,17 @@ Duration: %3 Modo Interruptor On/Off - + Flash the assigned function with this button Activa a função que foi atribuída enquanto o botão estiver pressionado - + Flash function (only for scenes) Modo Flash (só para cenas) - + No function Sem função @@ -5852,64 +5970,64 @@ Duration: %3 VCCueList - + Show/Hide crossfade sliders Mostrar/ocultar os faders de crossfade - - + + Play/Pause Cue list - - + + Stop Cue list - + Go to previous step in the list Ir para o passo anterior da lista - + Go to next step in the list Ir para o passo seguinte da lista - + Cue list Cue list - + Play/Stop Cue list - + Pause Cue list - + Fade In Fade In - + Fade Out Fade Out - + Duration Duração - + Notes Notas @@ -6075,7 +6193,7 @@ Duration: %3 VCFrame - + Add Adicionar @@ -6128,17 +6246,17 @@ Duration: %3 - + External Input - Enable - + External Input - Previous Page - + External Input - Next Page @@ -6171,17 +6289,17 @@ Duration: %3 VCLabel - + Label Etiqueta - + Rename Label Renomear Etiqueta - + Caption: Legenda: @@ -6189,42 +6307,42 @@ Duration: %3 VCMatrix - + Animation %1 Animação %1 - + End Color Reset - + Start color Red component - + Start color Green component - + Start color Blue component - + End color Red component - + End color Green component - + End color Blue component @@ -6427,48 +6545,48 @@ Duration: %3 - + No function Sem função - + Start Color - + Start Color Knob - + End Color - + End Color Knob - + End Color Reset - + Animation - - + + Text - + Enter a text @@ -6720,12 +6838,12 @@ Duration: %3 VCSlider - + Slider %1 Fader %1 - + Reset channels override @@ -6743,42 +6861,42 @@ Duration: %3 Geral - + Name of the slider Nome do fader - + Value display style Estilo de visualização dos valores - + Show exact DMX values Mostrar o valor DMX exacto - + Show value as percentage Mostrar valor em percentagem - + Percentage Percentagem - + Slider movement Movimento do fader - + Normal Normal - + Inverted Invertido @@ -6854,32 +6972,32 @@ Duration: %3 Alterar para Modo de Reprodução - + Actual Actual - + Widget name Nome do Widget - + Widget appearance Aparência do widget - + Slider Fader - + Knob Roda - + Catch up with the external controller input value @@ -7277,17 +7395,17 @@ Duration: %3 - + Multiply by 2 Input - + Divide by 2 Input - + Factor Reset Input @@ -7295,68 +7413,68 @@ Duration: %3 VCWidget - + Button Botão - + Slider Fader - + XYPad XY Pad - + Frame Moldura - + Solo frame Moldura Solo - + Speed dial Selector de velocidade - + Cue list Lista de Cues - + Label Etiqueta - + Audio Triggers - + Animation - + Clock Relógio + - Unknown Desconhecido - + This widget has no properties Este widget não tem propriedades @@ -7554,12 +7672,12 @@ Duration: %3 - + Pan / Horizontal Axis Pan / Eixo Horizontal - + Tilt / Vertical Axis Tilt / Eixo Vertical @@ -7589,44 +7707,44 @@ Duration: %3 Editar os eixos do fixture seleccionado - + Width Largura - + Height Altura - + Remove fixtures Remover fixtures - + Do you want to remove the selected fixtures? Deseja eliminar os fixtures seleccionados? - - + + Error Erro - + The selected Scene does not include any Pan or Tilt channel. Please select one with such channels. - + Please select at least one fixture or head to create this type of preset! - + Fixture Group Grupo de Fixtures @@ -7759,12 +7877,12 @@ Please select one with such channels. VideoItem - + Fullscreen - + Screen %1 diff --git a/ui/src/rdmmanager.ui b/ui/src/rdmmanager.ui index 1f1bb91e5c..369c9299f0 100644 --- a/ui/src/rdmmanager.ui +++ b/ui/src/rdmmanager.ui @@ -6,12 +6,12 @@ 0 0 - 396 + 402 617 - Form + From dfd965b18b6bb4727cbb210db37b75a93eba902f Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Tue, 12 Mar 2024 22:38:47 +0100 Subject: [PATCH 095/212] webaccess: more flat and modern look --- debian/changelog | 4 +-- webaccess/res/common.css | 33 ++++++++++-------------- webaccess/res/keypad.html | 17 ++---------- webaccess/res/simpledesk.css | 5 ++-- webaccess/src/webaccessconfiguration.cpp | 5 ++-- webaccess/src/webaccessnetwork.cpp | 5 ++-- 6 files changed, 23 insertions(+), 46 deletions(-) diff --git a/debian/changelog b/debian/changelog index ff4f66ca50..35e236b170 100644 --- a/debian/changelog +++ b/debian/changelog @@ -7,13 +7,13 @@ qlcplus (4.13.0) stable; urgency=low * 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 feedbacks + * 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 feedbacks (thanks to ditcheshurt) + * 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 diff --git a/webaccess/res/common.css b/webaccess/res/common.css index 4cdc024be3..2ad56818f7 100644 --- a/webaccess/res/common.css +++ b/webaccess/res/common.css @@ -1,36 +1,32 @@ .controlBar { width: 100%; height: 40px; - background: linear-gradient(to bottom, #B2D360 0%, #4B9002 100%); - background: -ms-linear-gradient(top, #B2D360 0%, #4B9002 100%); - background: -moz-linear-gradient(top, #B2D360 0%, #4B9002 100%); - background: -o-linear-gradient(top, #B2D360 0%, #4B9002 100%); - background: -webkit-gradient(linear, left top, left bottom, color-stop(0, #B2D360), color-stop(1, #4B9002)); - background: -webkit-linear-gradient(top, #B2D360 0%, #4B9002 100%); - font:bold 24px/1.2em sans-serif; + background: #181a1b; + font: 20px/1.2em sans-serif; color: #ffffff; } .button { - height: 35px; + height: 30px; margin-left: 5px; + margin-top: 5px; text-decoration: none; - font: bold 25px/1.2em 'Trebuchet MS',Arial, Helvetica; + font: 22px/1.2em 'Trebuchet MS',Arial, Helvetica; display: inline-block; text-align: center; color: #fff; - border: 1px solid #333; + border: 1px solid #555; } .button, .button span { - -moz-border-radius: .3em; - border-radius: .3em; + -moz-border-radius: .2em; + border-radius: .2em; } .button span { display: block; - padding: 0 10px 0 10px; + padding: 0 20px 0 20px; } .button:hover { @@ -45,15 +41,12 @@ } .button-blue { - background: #4477a1; - background: -webkit-gradient(linear, left top, left bottom, from(#81a8cb), to(#4477a1) ); - background: -moz-linear-gradient(-90deg, #81a8cb, #4477a1); + background: #364e5e; } .button-blue:hover { - background: #81a8cb; - background: -webkit-gradient(linear, left top, left bottom, from(#4477a1), to(#81a8cb) ); - background: -moz-linear-gradient(-90deg, #4477a1, #81a8cb); + background: #ffd42a; + color: #000; } .button-blue:active { background: #4477a1; } @@ -61,7 +54,7 @@ .swInfo { position: absolute; right: 5px; - top: 5px; + top: 7px; font-size: 18px; } diff --git a/webaccess/res/keypad.html b/webaccess/res/keypad.html index 7a1c0258a1..2e625d2703 100644 --- a/webaccess/res/keypad.html +++ b/webaccess/res/keypad.html @@ -5,12 +5,11 @@ diff --git a/webaccess/res/simpledesk.css b/webaccess/res/simpledesk.css index 8bb2a379fe..10a5f0e317 100644 --- a/webaccess/res/simpledesk.css +++ b/webaccess/res/simpledesk.css @@ -1,9 +1,8 @@ -html { height: 100%; background-color: #111; } +html { height: 100%; background-color: #222; } body { margin: 0px; - background-image: linear-gradient(to bottom, #45484d 0%, #111 100%); - background-image: -webkit-linear-gradient(top, #45484d 0%, #111 100%); + background: #222; } .styled-select select { diff --git a/webaccess/src/webaccessconfiguration.cpp b/webaccess/src/webaccessconfiguration.cpp index 7f7d5fb308..ef3b8e91e8 100644 --- a/webaccess/src/webaccessconfiguration.cpp +++ b/webaccess/src/webaccessconfiguration.cpp @@ -303,11 +303,10 @@ QString WebAccessConfiguration::getHTML(Doc *doc, WebAccessAuth *auth) QString m_CSScode = "\n"; From 57048916c279031a3b5987e3c66567d293e4804c Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Wed, 13 Mar 2024 19:12:58 +0100 Subject: [PATCH 096/212] linux: add systemd service --- debian/qlcplus.service | 13 ++++++++++ platforms/linux/CMakeLists.txt | 13 ++++------ platforms/linux/qlcplus-start.sh | 41 ++++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 9 deletions(-) create mode 100644 debian/qlcplus.service create mode 100644 platforms/linux/qlcplus-start.sh diff --git a/debian/qlcplus.service b/debian/qlcplus.service new file mode 100644 index 0000000000..fb150db9bd --- /dev/null +++ b/debian/qlcplus.service @@ -0,0 +1,13 @@ +[Unit] +Description=Q Light Controller Plus +Documentation=man:qlcplus(1) +After=network.target + +[Service] +Type=simple +Restart=always +RestartSec=3 +ExecStart=/usr/sbin/qlcplus-start.sh + +[Install] +WantedBy=multi-user.target diff --git a/platforms/linux/CMakeLists.txt b/platforms/linux/CMakeLists.txt index cc92fae4f0..1d0682c9ef 100644 --- a/platforms/linux/CMakeLists.txt +++ b/platforms/linux/CMakeLists.txt @@ -1,16 +1,12 @@ project(icons) -set(desktop_FILES - qlcplus.desktop -) +set(desktop_FILES qlcplus.desktop) if(NOT qmlui) set(APPEND desktop_FILES qlcplus-fixtureeditor.desktop) endif() install(FILES ${desktop_FILES} DESTINATION ${INSTALLROOT}/share/applications/) -set(icons_SRCS - ../../resources/icons/png/qlcplus.png -) +set(icons_SRCS ../../resources/icons/png/qlcplus.png) if(NOT qmlui) list(APPEND icons_SRCS ../../resources/icons/png/qlcplus-fixtureeditor.png) endif() @@ -18,9 +14,7 @@ install(FILES ${icons_SRCS} DESTINATION ${INSTALLROOT}/share/pixmaps/) install(FILES qlcplus.xml DESTINATION ${INSTALLROOT}/share/mime/packages) -set(appdata_FILES - org.qlcplus.QLCPlus.appdata.xml -) +set(appdata_FILES org.qlcplus.QLCPlus.appdata.xml) if(NOT qmlui) list(APPEND appdata_FILES org.qlcplus.QLCPlusFixtureEditor.appdata.xml) endif() @@ -32,6 +26,7 @@ if(NOT qmlui) endif() # install(FILES ../Sample.qxw DESTINATION ${INSTALLROOT}/${DATADIR}) +# install(FILES qlcplus-start.sh DESTINATION ${INSTALLROOT}/sbin) if(appimage) if (QT_DIR STREQUAL "/usr/lib/x86_64-linux-gnu/cmake/Qt5") diff --git a/platforms/linux/qlcplus-start.sh b/platforms/linux/qlcplus-start.sh new file mode 100644 index 0000000000..a488a5f0c3 --- /dev/null +++ b/platforms/linux/qlcplus-start.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash +# +# Q Light Controller Plus +# qlcplus-start.sh +# +# Copyright (c) 2024 Massimo Callegari +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +QLCPLUS_OPTS="-platform eglfs --nowm --web --web-auth --operate --overscan" + +if [ -ne $HOME/.qlcplus/eglfs.json ]; then + echo '{ "device": "/dev/dri/card1" }' > $HOME/.qlcplus/eglfs.json +fi + +if [ -e /root/.qlcplus/autostart.qxw ]; then + QLCPLUS_OPTS="$QLCPLUS_OPTS --open /root/.qlcplus/autostart.qxw" +fi + +# if NTP hasn't done its job already, set the date to modern age... +CURRDATE=`date +%Y` +if [ "$CURRDATE" -lt "2024" ]; then +date +%Y%m%d -s "20240313" +fi + +export QT_QPA_EGLFS_PHYSICAL_WIDTH=320 +export QT_QPA_EGLFS_PHYSICAL_HEIGHT=200 +export QT_QPA_EGLFS_ALWAYS_SET_MODE=1 +export QT_QPA_EGLFS_KMS_CONFIG=$HOME/.qlcplus/eglfs.json + +/usr/bin/qlcplus $QLCPLUS_OPTS From 55df45619e719684900a887325578a4f9d31d2f6 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Wed, 13 Mar 2024 19:14:45 +0100 Subject: [PATCH 097/212] Archive some changelog --- debian/changelog | 537 +------------------------------------------ debian/changelog-old | 535 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 536 insertions(+), 536 deletions(-) diff --git a/debian/changelog b/debian/changelog index 35e236b170..28d7e05f39 100644 --- a/debian/changelog +++ b/debian/changelog @@ -84,7 +84,7 @@ qlcplus (4.13.0) stable; urgency=low * New fixture: UKing ZQ-02319 (thanks to Mike Ubl) * New fixtures: DTS Jack, Robe LEDBeam 350 (thanks to Tomas Hastings) - -- Massimo Callegari Sun, 10 Mar 2024 12:13:14 +0200 + -- Massimo Callegari Fri, 15 Mar 2024 12:13:14 +0200 qlcplus (4.12.7) stable; urgency=low @@ -519,538 +519,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) From dae4d1f05a1fb082e8a55cfc8747321d93efe2c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans-J=C3=BCrgen=20Tappe?= Date: Thu, 14 Mar 2024 20:28:48 +0100 Subject: [PATCH 098/212] Do not scale Click&Go in a scaled slider. Values are as-is. --- ui/src/virtualconsole/vcslider.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/src/virtualconsole/vcslider.cpp b/ui/src/virtualconsole/vcslider.cpp index 0ff131fad7..5ee67f9610 100644 --- a/ui/src/virtualconsole/vcslider.cpp +++ b/ui/src/virtualconsole/vcslider.cpp @@ -804,7 +804,7 @@ void VCSlider::setClickAndGoWidgetFromLevel(uchar level) void VCSlider::slotClickAndGoLevelChanged(uchar level) { - setSliderValue(level); + setSliderValue(level, false, false); updateFeedback(); QColor col = m_cngWidget->getColorAt(level); @@ -832,7 +832,7 @@ void VCSlider::slotClickAndGoColorChanged(QRgb color) void VCSlider::slotClickAndGoLevelAndPresetChanged(uchar level, QImage img) { - setSliderValue(level); + setSliderValue(level, false, false); updateFeedback(); QPixmap px = QPixmap::fromImage(img); From 9586424a8f4489c4cf1be21f9c3505d2f6332802 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans-J=C3=BCrgen=20Tappe?= Date: Sat, 16 Mar 2024 09:35:43 +0100 Subject: [PATCH 099/212] Display only those CnG options which are within the limits. --- ui/src/clickandgowidget.cpp | 34 +++++++++++++++++++++--------- ui/src/clickandgowidget.h | 14 ++++++++++-- ui/src/virtualconsole/vcslider.cpp | 6 ++++++ 3 files changed, 42 insertions(+), 12 deletions(-) diff --git a/ui/src/clickandgowidget.cpp b/ui/src/clickandgowidget.cpp index c75daedcb5..4cd5806fc7 100644 --- a/ui/src/clickandgowidget.cpp +++ b/ui/src/clickandgowidget.cpp @@ -55,6 +55,8 @@ ClickAndGoWidget::ClickAndGoWidget(QWidget *parent) : m_cellBarXpos = 1; m_cellBarYpos = 1; m_cellBarWidth = 0; + m_levelLowLimit = 0; + m_levelHighLimit = 255; } void ClickAndGoWidget::setupGradient(QColor begin, QColor end) @@ -146,6 +148,16 @@ void ClickAndGoWidget::setType(int type, const QLCChannel *chan) m_type = type; } +void ClickAndGoWidget::setLevelLowLimit(int min) +{ + this->m_levelLowLimit = min; +} + +void ClickAndGoWidget::setLevelHighLimit(int max) +{ + this->m_levelHighLimit = max; +} + int ClickAndGoWidget::getType() { return m_type; @@ -212,7 +224,7 @@ QImage ClickAndGoWidget::getImageFromValue(uchar value) { foreach (PresetResource res, m_resources) { - if (value >= res.m_min && value <= res.m_max) + if (value >= res.m_resLowLimit && value <= res.m_resHighLimit) return res.m_thumbnail; } } @@ -315,6 +327,8 @@ void ClickAndGoWidget::setupPresetPicker() for (int i = 0; i < m_resources.size(); i++) { PresetResource res = m_resources.at(i); + if (res.m_resLowLimit > m_levelHighLimit || res.m_resHighLimit < m_levelLowLimit) + continue; painter.setPen(Qt::black); painter.drawRect(x, y, m_cellWidth, CELL_H); painter.drawImage(x + 1, y + 4, res.m_thumbnail); @@ -355,13 +369,13 @@ void ClickAndGoWidget::mousePressEvent(QMouseEvent *event) if (m_hoverCellIdx >= 0 && m_hoverCellIdx < m_resources.length()) { PresetResource res = m_resources.at(m_hoverCellIdx); - qDebug() << "Mouse press. cellW: " << m_cellBarWidth << "min: " << res.m_min << "max:" << res.m_max; + qDebug() << "Mouse press. cellW: " << m_cellBarWidth << "min: " << res.m_resLowLimit << "max:" << res.m_resHighLimit; float f = SCALE(float(m_cellBarWidth), float(0), float(m_cellWidth), - float(0), float(res.m_max - res.m_min)); - emit levelAndPresetChanged((uchar)f + res.m_min, res.m_thumbnail); + float(0), float(res.m_resHighLimit - res.m_resLowLimit)); + emit levelAndPresetChanged((uchar)f + res.m_resLowLimit, res.m_thumbnail); } } QWidget::mousePressEvent(event); @@ -421,8 +435,8 @@ void ClickAndGoWidget::paintEvent(QPaintEvent *event) ClickAndGoWidget::PresetResource::PresetResource(QString path, QString text, uchar min, uchar max) { m_descr = text; - m_min = min; - m_max = max; + m_resLowLimit = min; + m_resHighLimit = max; QImage px(path); m_thumbnail = QImage(40, 40, QImage::Format_RGB32); m_thumbnail.fill(Qt::white); @@ -436,8 +450,8 @@ ClickAndGoWidget::PresetResource::PresetResource(QColor color1, QColor color2, QString text, uchar min, uchar max) { m_descr = text; - m_min = min; - m_max = max; + m_resLowLimit = min; + m_resHighLimit = max; m_thumbnail = QImage(40, 40, QImage::Format_RGB32); if (color2.isValid() == false) m_thumbnail.fill(color1.rgb()); @@ -453,8 +467,8 @@ ClickAndGoWidget::PresetResource::PresetResource(QColor color1, QColor color2, ClickAndGoWidget::PresetResource::PresetResource(int index, QString text, uchar min, uchar max) { m_descr = text; - m_min = min; - m_max = max; + m_resLowLimit = min; + m_resHighLimit = max; m_thumbnail = QImage(40, 40, QImage::Format_RGB32); m_thumbnail.fill(Qt::white); QFont tfont = QApplication::font(); diff --git a/ui/src/clickandgowidget.h b/ui/src/clickandgowidget.h index 01e5bdddde..b802fb0be0 100644 --- a/ui/src/clickandgowidget.h +++ b/ui/src/clickandgowidget.h @@ -64,6 +64,12 @@ class ClickAndGoWidget : public QWidget */ int getType(); + /** Set the low limits from the fader as a preset filter */ + void setLevelLowLimit(int min); + + /** Set the high limits from the fader as a preset filter */ + void setLevelHighLimit(int max); + /** * Returns the color at pos position. * Used with primary colors linear gradient @@ -123,8 +129,8 @@ class ClickAndGoWidget : public QWidget public: QImage m_thumbnail; QString m_descr; - uchar m_min; - uchar m_max; + int m_resLowLimit; + int m_resHighLimit; }; protected: @@ -145,6 +151,10 @@ class ClickAndGoWidget : public QWidget QString m_title; QList m_resources; + /** Fader limits to also limit the presets */ + int m_levelLowLimit; + int m_levelHighLimit; + /** Used to group all the primary colors */ bool m_linearColor; diff --git a/ui/src/virtualconsole/vcslider.cpp b/ui/src/virtualconsole/vcslider.cpp index 5ee67f9610..a2831802cd 100644 --- a/ui/src/virtualconsole/vcslider.cpp +++ b/ui/src/virtualconsole/vcslider.cpp @@ -572,6 +572,8 @@ QList VCSlider::levelChannels() void VCSlider::setLevelLowLimit(uchar value) { m_levelLowLimit = value; + if (m_cngWidget != NULL) + m_cngWidget->setLevelLowLimit(value); } uchar VCSlider::levelLowLimit() const @@ -582,6 +584,8 @@ uchar VCSlider::levelLowLimit() const void VCSlider::setLevelHighLimit(uchar value) { m_levelHighLimit = value; + if (m_cngWidget != NULL) + m_cngWidget->setLevelHighLimit(value); } uchar VCSlider::levelHighLimit() const @@ -762,6 +766,8 @@ void VCSlider::setupClickAndGoWidget() { const QLCChannel *chan = fxi->channel(lChan.channel); m_cngWidget->setType(m_cngType, chan); + m_cngWidget->setLevelLowLimit(this->levelLowLimit()); + m_cngWidget->setLevelHighLimit(this->levelHighLimit()); } } else From 56b0c13ea3617e1d0585950ac878ac4d55dbb0f3 Mon Sep 17 00:00:00 2001 From: sbenejam Date: Sat, 16 Mar 2024 22:14:11 +0100 Subject: [PATCH 100/212] Updated catalan ans spanish translations --- ui/src/qlcplus_ca_ES.ts | 66 ++++++++++++++++++++--------------------- ui/src/qlcplus_es_ES.ts | 66 ++++++++++++++++++++--------------------- 2 files changed, 66 insertions(+), 66 deletions(-) diff --git a/ui/src/qlcplus_ca_ES.ts b/ui/src/qlcplus_ca_ES.ts index 22cbdc99c8..1482c2b902 100644 --- a/ui/src/qlcplus_ca_ES.ts +++ b/ui/src/qlcplus_ca_ES.ts @@ -1720,76 +1720,76 @@ L'arxiu seleccionat s'ha mogut o esborrat. Custom Feedback Configuration - + Configuració de Feedback Personalitzat Value - Valor + Valor Label - Etiqueta + Etiqueta Color - + Color Values - Valors + Valors Lower Value - + Valor Inferior Monitor Value - + Valor Monitor Upper Value - + Valor Superior Color Selection - + Selecció de Color MIDI Channel - + Canal MIDI Upper Channel - + Canal Superior Lower Channel - + Canal Inferior Monitor Channel - + Canal Monitor From plugin settings - + De la configuració del connector @@ -3615,7 +3615,7 @@ Si us plau, consulteu la documentació dels plugins per solucionar això. Input Mapping - + Mapatge d'Entrada @@ -3641,52 +3641,52 @@ Si us plau, consulteu la documentació dels plugins per solucionar això. MIDI channel - + Canal MIDI Colors - + Colors Remove the selected color - + Elimina el color seleccionat Add a new color - + Afegir un nou color Value - Valor + Valor Label - Etiqueta + Etiqueta Color - + Color MIDI Channels - + Canals MIDI Add a new MIDI channel - + Afegir un nou canal MIDI Remove the selected MIDI channel - + Elimina el canal MIDI seleccionat @@ -3706,7 +3706,7 @@ Si us plau, consulteu la documentació dels plugins per solucionar això. From plugin settings - + Des de la configuració del connector @@ -3756,28 +3756,28 @@ Note that the wizard cannot tell the difference between a knob and a slider so y Enter value - + Valor d'Entrada Feedback value - + Valor de Feedback Enter label - + Etiqueta d'Entrada Color label - + Etiqueta del Color MIDI channel label - + Canal MIDI @@ -5801,12 +5801,12 @@ Durada: %3 Override priority - + Sobreescriu la prioritat Force LTP - + Força LTP diff --git a/ui/src/qlcplus_es_ES.ts b/ui/src/qlcplus_es_ES.ts index 66a5b94504..4ea116f9d1 100644 --- a/ui/src/qlcplus_es_ES.ts +++ b/ui/src/qlcplus_es_ES.ts @@ -1726,76 +1726,76 @@ El archivo seleccionado ha sido movido o borrado. Custom Feedback Configuration - + Configuración de Feedback Personalizado Value - Valor + Valor Label - Etiqueta + Etiqueta Color - + Color Values - Valores + Valores Lower Value - + Valor Inferior Monitor Value - + Valor Monitor Upper Value - + Valor Superior Color Selection - + Selección de Color MIDI Channel - + Canal MIDI Upper Channel - + Canal Superior Lower Channel - + Canal Inferior Monitor Channel - + Canal Monitor From plugin settings - + De la configuración del conector @@ -3602,7 +3602,7 @@ Por favor, revise la documentación de los plugins para solucionar esto. Input Mapping - + Mapeo de Entrada @@ -3618,52 +3618,52 @@ Por favor, revise la documentación de los plugins para solucionar esto. MIDI channel - + Canal MIDI Colors - + Colores Remove the selected color - + Elimina el color seleccionado Add a new color - + Añadir un nuevo color Value - Valor + Valor Label - Etiqueta + Etiqueta Color - + Color MIDI Channels - + Canales MIDI Add a new MIDI channel - + Añadir un nuevo canal MIDI Remove the selected MIDI channel - + Elimina el canal MIDI seleccionado @@ -3718,7 +3718,7 @@ Por favor, revise la documentación de los plugins para solucionar esto. From plugin settings - + Desde la configuración del conector @@ -3770,28 +3770,28 @@ Tenga en cuenta que el asistente no puede diferenciar entre una perilla y un sli Enter value - + Valor de Entrada Feedback value - + Valor de Feedback Enter label - + Etiqueta de Entrada Color label - + Etiqueta del Color MIDI channel label - + Canal MIDI @@ -5785,12 +5785,12 @@ Duración: %3 Override priority - + Sobreescribe la prioridad Force LTP - + Fuerza LTP From fa7ea0d700b941371ba5391f03da93abedc4d988 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Sun, 17 Mar 2024 09:41:13 +0100 Subject: [PATCH 101/212] linux: fix systemd scripts --- debian/qlcplus.service | 1 + platforms/linux/qlcplus-start.sh | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/debian/qlcplus.service b/debian/qlcplus.service index fb150db9bd..6451350d2a 100644 --- a/debian/qlcplus.service +++ b/debian/qlcplus.service @@ -5,6 +5,7 @@ After=network.target [Service] Type=simple +User=pi Restart=always RestartSec=3 ExecStart=/usr/sbin/qlcplus-start.sh diff --git a/platforms/linux/qlcplus-start.sh b/platforms/linux/qlcplus-start.sh index a488a5f0c3..6c32749624 100644 --- a/platforms/linux/qlcplus-start.sh +++ b/platforms/linux/qlcplus-start.sh @@ -19,12 +19,12 @@ QLCPLUS_OPTS="-platform eglfs --nowm --web --web-auth --operate --overscan" -if [ -ne $HOME/.qlcplus/eglfs.json ]; then +if [ ! -e $HOME/.qlcplus/eglfs.json ]; then echo '{ "device": "/dev/dri/card1" }' > $HOME/.qlcplus/eglfs.json fi -if [ -e /root/.qlcplus/autostart.qxw ]; then - QLCPLUS_OPTS="$QLCPLUS_OPTS --open /root/.qlcplus/autostart.qxw" +if [ -e $HOME/.qlcplus/autostart.qxw ]; then + QLCPLUS_OPTS="$QLCPLUS_OPTS --open $HOME/.qlcplus/autostart.qxw" fi # if NTP hasn't done its job already, set the date to modern age... From caeb722147eb33b435108ec8a36e3075d1eca8e2 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Sun, 17 Mar 2024 10:10:13 +0100 Subject: [PATCH 102/212] Enter 4.13.0 release --- debian/changelog | 2 +- variables.cmake | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/debian/changelog b/debian/changelog index 28d7e05f39..a24cde8e06 100644 --- a/debian/changelog +++ b/debian/changelog @@ -84,7 +84,7 @@ qlcplus (4.13.0) stable; urgency=low * New fixture: UKing ZQ-02319 (thanks to Mike Ubl) * New fixtures: DTS Jack, Robe LEDBeam 350 (thanks to Tomas Hastings) - -- Massimo Callegari Fri, 15 Mar 2024 12:13:14 +0200 + -- Massimo Callegari Sun, 17 Mar 2024 12:13:14 +0200 qlcplus (4.12.7) stable; urgency=low diff --git a/variables.cmake b/variables.cmake index 045211b6ab..18b45a0c23 100644 --- a/variables.cmake +++ b/variables.cmake @@ -21,7 +21,7 @@ if(qmlui) add_definitions(-DQMLUI) set(APPVERSION "5.0.0 Beta 3") else() - set(APPVERSION "4.13.0 GIT") + set(APPVERSION "4.13.0") endif() if(UNIX) From 3e509af1a4b6a85429d6f59071208ae5e88565f0 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Sun, 17 Mar 2024 11:33:26 +0100 Subject: [PATCH 103/212] linux: move systemd service in platforms Otherwise it gets included by default in the desktop Debian package --- {debian => platforms/linux}/qlcplus.service | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {debian => platforms/linux}/qlcplus.service (100%) diff --git a/debian/qlcplus.service b/platforms/linux/qlcplus.service similarity index 100% rename from debian/qlcplus.service rename to platforms/linux/qlcplus.service From c025b3ca6cd1578806297fa017e81a501e133019 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Sun, 17 Mar 2024 12:08:31 +0100 Subject: [PATCH 104/212] linux: fix start script again --- platforms/linux/qlcplus-start.sh | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/platforms/linux/qlcplus-start.sh b/platforms/linux/qlcplus-start.sh index 6c32749624..dcef4db730 100644 --- a/platforms/linux/qlcplus-start.sh +++ b/platforms/linux/qlcplus-start.sh @@ -19,18 +19,19 @@ QLCPLUS_OPTS="-platform eglfs --nowm --web --web-auth --operate --overscan" -if [ ! -e $HOME/.qlcplus/eglfs.json ]; then - echo '{ "device": "/dev/dri/card1" }' > $HOME/.qlcplus/eglfs.json +if [ ! -f $HOME/.qlcplus/eglfs.json ]; then + mkdir -p $HOME/.qlcplus + echo '{ "device": "/dev/dri/card1" }' > $HOME/.qlcplus/eglfs.json fi -if [ -e $HOME/.qlcplus/autostart.qxw ]; then - QLCPLUS_OPTS="$QLCPLUS_OPTS --open $HOME/.qlcplus/autostart.qxw" +if [ -f $HOME/.qlcplus/autostart.qxw ]; then + QLCPLUS_OPTS="$QLCPLUS_OPTS --open $HOME/.qlcplus/autostart.qxw" fi # if NTP hasn't done its job already, set the date to modern age... CURRDATE=`date +%Y` if [ "$CURRDATE" -lt "2024" ]; then -date +%Y%m%d -s "20240313" + date +%Y%m%d -s "20240313" fi export QT_QPA_EGLFS_PHYSICAL_WIDTH=320 From 76f2fa3216546ad8429ac42d8d57c878c5e3d4e3 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Sun, 17 Mar 2024 19:25:44 +0100 Subject: [PATCH 105/212] windows: 64bit build --- .github/workflows/build.yml | 80 +++++++++++++++---------------- platforms/windows/CMakeLists.txt | 4 +- platforms/windows/qlcplus4Qt5.nsi | 2 +- platforms/windows/qlcplus4Qt6.nsi | 2 +- platforms/windows/qlcplus5Qt5.nsi | 2 +- plugins/dmxusb/src/CMakeLists.txt | 12 ++--- plugins/dmxusb/src/src.pro | 2 +- 7 files changed, 51 insertions(+), 53 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6192f27dfd..dd843ea9a4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -266,8 +266,8 @@ jobs: QMAKESPEC: win32-g++ QT_MODULES: qtscript - CC: /mingw32/bin/i686-w64-mingw32-gcc.exe - CXX: /mingw32/bin/i686-w64-mingw32-g++.exe + CC: /mingw64/bin/x86_64-w64-mingw32-gcc.exe + CXX: /mingw64/bin/x86_64-w64-mingw32-g++.exe steps: - name: Checkout @@ -321,52 +321,52 @@ jobs: - name: Update and install MSYS2 uses: msys2/setup-msys2@v2 with: - msystem: mingw32 + msystem: mingw64 release: true update: false path-type: inherit install: >- wget unzip - mingw-w64-i686-gcc - mingw-w64-i686-gcc-libs - mingw-w64-i686-cmake - mingw-w64-i686-libmad - mingw-w64-i686-libsndfile - mingw-w64-i686-flac - mingw-w64-i686-fftw - mingw-w64-i686-libusb - mingw-w64-i686-python-lxml - mingw-w64-i686-qt5-base - mingw-w64-i686-qt5-multimedia - mingw-w64-i686-qt5-serialport - mingw-w64-i686-qt5-script - mingw-w64-i686-qt5-tools - mingw-w64-i686-qt5-imageformats - mingw-w64-i686-qt5-svg - mingw-w64-i686-qt5-declarative - mingw-w64-i686-qt5-quickcontrols - mingw-w64-i686-qt5-quickcontrols2 - mingw-w64-i686-qt5-3d - mingw-w64-i686-qt5-quick3d - mingw-w64-i686-nsis + mingw-w64-x86_64-gcc + mingw-w64-x86_64-gcc-libs + mingw-w64-x86_64-cmake + mingw-w64-x86_64-libmad + mingw-w64-x86_64-libsndfile + mingw-w64-x86_64-flac + mingw-w64-x86_64-fftw + mingw-w64-x86_64-libusb + mingw-w64-x86_64-python-lxml + mingw-w64-x86_64-qt5-base + mingw-w64-x86_64-qt5-multimedia + mingw-w64-x86_64-qt5-serialport + mingw-w64-x86_64-qt5-script + mingw-w64-x86_64-qt5-tools + mingw-w64-x86_64-qt5-imageformats + mingw-w64-x86_64-qt5-svg + mingw-w64-x86_64-qt5-declarative + mingw-w64-x86_64-qt5-quickcontrols + mingw-w64-x86_64-qt5-quickcontrols2 + mingw-w64-x86_64-qt5-3d + mingw-w64-x86_64-qt5-quick3d + mingw-w64-x86_64-nsis - name: D2XX SDK shell: msys2 {0} run: | - set MSYSTEM=MINGW32 - mkdir -p /c/Qt/D2XXSDK - wget https://ftdichip.com/wp-content/uploads/2023/09/CDM-v2.12.36.4-WHQL-Certified.zip -O /c/Qt/D2XXSDK/cdm.zip - cd /c/Qt/D2XXSDK + set MSYSTEM=MINGW64 + mkdir -p /c/projects/D2XXSDK + wget https://ftdichip.com/wp-content/uploads/2023/09/CDM-v2.12.36.4-WHQL-Certified.zip -O /c/projects/D2XXSDK/cdm.zip + cd /c/projects/D2XXSDK unzip cdm.zip - cd i386 - gendef.exe - ftd2xx.dll > ftd2xx.def - dlltool -k --input-def ftd2xx.def --dllname ftd2xx.dll --output-lib libftd2xx.a + cd amd64 + gendef.exe - ftd2xx64.dll > ftd2xx.def + dlltool -k --input-def ftd2xx.def --dllname ftd2xx64.dll --output-lib libftd2xx.a - name: Print program versions shell: msys2 {0} run: | - set MSYSTEM=MINGW32 + set MSYSTEM=MINGW64 echo "pwd:" pwd echo "CXX:" @@ -381,19 +381,19 @@ jobs: #if: false shell: msys2 {0} run: | - set MSYSTEM=MINGW32 + set MSYSTEM=MINGW64 # disable Velleman plugin sed -i -e 's/ add_subdirectory(velleman)/# add_subdirectory(velleman)/g' plugins/CMakeLists.txt # fix MSYS2 system path sed -i -e 's/$ENV{SystemDrive}\/msys64/D:\/a\/_temp\/msys64/g' platforms/windows/CMakeLists.txt # fix project path in NSIS script - sed -i -e 's/c\:\\Qt/d:\\a\\qlcplus/g' platforms/windows/${{env.NSIS_SCRIPT}} + sed -i -e 's/c\:\\projects/d:\\a\\qlcplus/g' platforms/windows/${{env.NSIS_SCRIPT}} - name: Configure v4 build for Windows shell: msys2 {0} if: ${{ matrix.task == 'compile-qt5' }} run: | - set MSYSTEM=MINGW32 + set MSYSTEM=MINGW64 mkdir build cd build cmake -G "Unix Makefiles" .. @@ -402,7 +402,7 @@ jobs: shell: msys2 {0} if: ${{ matrix.task == 'compile-qt5qml' }} run: | - set MSYSTEM=MINGW32 + set MSYSTEM=MINGW64 mkdir build cd build cmake -G "Unix Makefiles" -Dqmlui=ON .. @@ -410,14 +410,14 @@ jobs: - name: Build for Windows shell: msys2 {0} run: | - set MSYSTEM=MINGW32 + set MSYSTEM=MINGW64 cd build make -j${NPROC} - name: Install on Windows shell: msys2 {0} run: | - set MSYSTEM=MINGW32 + set MSYSTEM=MINGW64 #echo 'Silently installing QLC+...' cd build make install/fast @@ -427,7 +427,7 @@ jobs: - name: Build installation package shell: msys2 {0} run: | - set MSYSTEM=MINGW32 + set MSYSTEM=MINGW64 cd /c/qlcplus echo 'Creating package...' makensis -X'SetCompressor /FINAL lzma' ${{env.NSIS_SCRIPT}} diff --git a/platforms/windows/CMakeLists.txt b/platforms/windows/CMakeLists.txt index ca890c7256..660754c859 100644 --- a/platforms/windows/CMakeLists.txt +++ b/platforms/windows/CMakeLists.txt @@ -20,7 +20,7 @@ endif() get_filename_component(QT_LIBS_PATH ${QT_DIR}/../../../bin ABSOLUTE) get_filename_component(QT_PLUGINS_PATH ${QT_LIBS_PATH}/../share/${QT_P}/plugins ABSOLUTE) get_filename_component(QT_QML_PATH ${QT_LIBS_PATH}/../share/${QT_P}/qml ABSOLUTE) -set(SYS_LIBS_PATH $ENV{SystemDrive}/msys64/mingw32/bin) +set(SYS_LIBS_PATH $ENV{SystemDrive}/msys64/mingw64/bin) # set(SYS_LIBS_PATH D:/msys64/mingw32/bin) # Qt library dependencies @@ -172,7 +172,7 @@ endif() # MSYS2 libraries set(msys_path "${INSTALLROOT}/${LIBSDIR}") set(msys_files "${SYS_LIBS_PATH}/libstdc++-6.dll" - "${SYS_LIBS_PATH}/libgcc_s_dw2-1.dll" + "${SYS_LIBS_PATH}/libgcc_s_seh-1.dll" "${SYS_LIBS_PATH}/libwinpthread-1.dll" "${SYS_LIBS_PATH}/libicuin74.dll" "${SYS_LIBS_PATH}/libicuuc74.dll" diff --git a/platforms/windows/qlcplus4Qt5.nsi b/platforms/windows/qlcplus4Qt5.nsi index 0176177cc7..afffe2c6c4 100644 --- a/platforms/windows/qlcplus4Qt5.nsi +++ b/platforms/windows/qlcplus4Qt5.nsi @@ -4,7 +4,7 @@ ;-------------------------------- ;Defines -!define QLCPLUS_HOME "c:\Qt\qlcplus" +!define QLCPLUS_HOME "c:\projects\qlcplus" !define MUI_ICON "${QLCPLUS_HOME}\resources\icons\qlcplus.ico" !define MUI_UNICON "${NSISDIR}\Contrib\Graphics\Icons\nsis3-uninstall.ico" !define MUI_HEADERIMAGE diff --git a/platforms/windows/qlcplus4Qt6.nsi b/platforms/windows/qlcplus4Qt6.nsi index 77d056fd19..7508de12df 100644 --- a/platforms/windows/qlcplus4Qt6.nsi +++ b/platforms/windows/qlcplus4Qt6.nsi @@ -4,7 +4,7 @@ ;-------------------------------- ;Defines -!define QLCPLUS_HOME "c:\Qt\qlcplus" +!define QLCPLUS_HOME "c:\projects\qlcplus" !define MUI_ICON "${QLCPLUS_HOME}\resources\icons\qlcplus.ico" !define MUI_UNICON "${NSISDIR}\Contrib\Graphics\Icons\nsis3-uninstall.ico" !define MUI_HEADERIMAGE diff --git a/platforms/windows/qlcplus5Qt5.nsi b/platforms/windows/qlcplus5Qt5.nsi index 569017bfd3..da280ee6c1 100644 --- a/platforms/windows/qlcplus5Qt5.nsi +++ b/platforms/windows/qlcplus5Qt5.nsi @@ -4,7 +4,7 @@ ;-------------------------------- ;Defines -!define QLCPLUS_HOME "c:\Qt\qlcplus" +!define QLCPLUS_HOME "c:\porjects\qlcplus" !define MUI_ICON "${QLCPLUS_HOME}\resources\icons\qlcplus.ico" !define MUI_UNICON "${NSISDIR}\Contrib\Graphics\Icons\nsis3-uninstall.ico" !define MUI_HEADERIMAGE diff --git a/plugins/dmxusb/src/CMakeLists.txt b/plugins/dmxusb/src/CMakeLists.txt index 7c717d5aac..3d902bab46 100644 --- a/plugins/dmxusb/src/CMakeLists.txt +++ b/plugins/dmxusb/src/CMakeLists.txt @@ -27,8 +27,8 @@ add_library(${module_name} ) if(WIN32) - set(FTD2XXDIR "C:/Qt/D2XXSDK") - target_link_libraries(${module_name} PRIVATE ${FTD2XXDIR}/i386/libftd2xx.a) + set(FTD2XXDIR "C:/projects/D2XXSDK") + target_link_libraries(${module_name} PRIVATE ${FTD2XXDIR}/amd64/libftd2xx.a) target_include_directories(${module_name} PRIVATE ${FTD2XXDIR}) message("Building with FTD2xx support.") set(WITH_D2XX TRUE) @@ -151,11 +151,6 @@ if(WITH_D2XX) target_compile_definitions(${module_name} PRIVATE FTD2XX) endif() -if((WITH_D2XX) AND (WIN32)) - target_include_directories(${module_name} PRIVATE ${FTD2XXDIR}) - target_link_libraries(${module_name} PRIVATE ${FTD2XXDIR}/i386/libftd2xx.a) -endif() - if(WITH_LIBFTDI) target_sources(${module_name} PUBLIC libftdi-interface.cpp libftdi-interface.h) endif() @@ -166,6 +161,9 @@ if (UNIX AND NOT APPLE) DESTINATION ${UDEVRULESDIR}) install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/org.qlcplus.QLCPlus.dmxusb.metainfo.xml" DESTINATION ${METAINFODIR}) +elseif (WITH_D2XX) + install(FILES ${FTD2XXDIR}/amd64/ftd2xx64.dll + DESTINATION ${INSTALLROOT}/${LIBSDIR}) endif() install(TARGETS ${module_name} diff --git a/plugins/dmxusb/src/src.pro b/plugins/dmxusb/src/src.pro index 2e775f3d02..aae7b4f956 100644 --- a/plugins/dmxusb/src/src.pro +++ b/plugins/dmxusb/src/src.pro @@ -40,7 +40,7 @@ CONFIG(ftd2xx) { win32 { # Windows target - FTD2XXDIR = C:/Qt/D2XXSDK + FTD2XXDIR = C:/projects/D2XXSDK LIBS += -L$$FTD2XXDIR/i386 -lftd2xx LIBS += $$FTD2XXDIR/i386/libftd2xx.a INCLUDEPATH += $$FTD2XXDIR From 9cec7ac61c982ccf4955ba08a735b3edf5435e9f Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Sun, 17 Mar 2024 19:39:25 +0100 Subject: [PATCH 106/212] windows: fix typo --- platforms/windows/qlcplus5Qt5.nsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platforms/windows/qlcplus5Qt5.nsi b/platforms/windows/qlcplus5Qt5.nsi index da280ee6c1..222ef534e3 100644 --- a/platforms/windows/qlcplus5Qt5.nsi +++ b/platforms/windows/qlcplus5Qt5.nsi @@ -4,7 +4,7 @@ ;-------------------------------- ;Defines -!define QLCPLUS_HOME "c:\porjects\qlcplus" +!define QLCPLUS_HOME "c:\projects\qlcplus" !define MUI_ICON "${QLCPLUS_HOME}\resources\icons\qlcplus.ico" !define MUI_UNICON "${NSISDIR}\Contrib\Graphics\Icons\nsis3-uninstall.ico" !define MUI_HEADERIMAGE From dc979eec6855196f3624873c377f660a4a1a3a71 Mon Sep 17 00:00:00 2001 From: Foul Date: Mon, 18 Mar 2024 12:13:36 +0100 Subject: [PATCH 107/212] Update qlcplus_fr_FR.ts Completed French Translation Changed "Appareil" to "Luminaire" too. --- qmlui/qlcplus_fr_FR.ts | 454 +++++++++++++++++++++-------------------- 1 file changed, 228 insertions(+), 226 deletions(-) diff --git a/qmlui/qlcplus_fr_FR.ts b/qmlui/qlcplus_fr_FR.ts index 86d213814b..5f2cc1a297 100644 --- a/qmlui/qlcplus_fr_FR.ts +++ b/qmlui/qlcplus_fr_FR.ts @@ -42,7 +42,7 @@ Your project has changes - Votre projet comporte des modifications + Votre projet a été modifié @@ -63,7 +63,7 @@ Les modifications seront perdues si vous ne les enregistrez pas. Open file - Ouvrir + Ouvrir fichier @@ -103,12 +103,12 @@ Les modifications seront perdues si vous ne les enregistrez pas. DMX Address tool - Outil d'adressage DMX + Outil d'Adressage DMX UI Settings - + Paramètres UI @@ -178,7 +178,7 @@ Les modifications seront perdues si vous ne les enregistrez pas. About - À propos + À Propos @@ -191,17 +191,17 @@ Les modifications seront perdues si vous ne les enregistrez pas. New Scene - Nouvelle scène + Nouvelle Scène New Chaser - Nouveau chaser + Nouveau Chaser New Sequence - Nouvelle séquence + Nouvelle Séquence @@ -211,22 +211,22 @@ Les modifications seront perdues si vous ne les enregistrez pas. New Collection - Nouvelle collection + Nouvelle Collection New RGB Matrix - Nouvelle matrice RVB + Nouvelle Matrice RVB New Show - Nouveau spectacle + Nouveau Spectacle New Script - Nouveau script + Nouveau Script @@ -236,7 +236,7 @@ Les modifications seront perdues si vous ne les enregistrez pas. New Video - Nouvelle piste vidéo + Nouvelle Vidéo @@ -299,7 +299,7 @@ Les modifications seront perdues si vous ne les enregistrez pas. Volume - + Volume @@ -317,7 +317,7 @@ Les modifications seront perdues si vous ne les enregistrez pas. Global Audio - Audio global + Audio Global @@ -340,7 +340,7 @@ Les modifications seront perdues si vous ne les enregistrez pas. Projected diameter - + Diamètre projeté @@ -348,7 +348,7 @@ Les modifications seront perdues si vous ne les enregistrez pas. Expand/Collapse this panel - Déplier/replier ce panneau + Déplier/Replier ce panneau @@ -357,7 +357,7 @@ Les modifications seront perdues si vous ne les enregistrez pas. Custom - Personnalisée + Personnalisé @@ -365,17 +365,17 @@ Les modifications seront perdues si vous ne les enregistrez pas. Open a picture file - + Ouvrir un fichier image Gobo pictures - + Images Gobo All files - Tous les fichiers + Tous les fichiers @@ -385,7 +385,7 @@ Les modifications seront perdues si vous ne les enregistrez pas. Preset - + Préréglage @@ -416,22 +416,22 @@ Les modifications seront perdues si vous ne les enregistrez pas. Delete the selected capabilities - + Supprimer les fonctionnalités sélectionnées Capability wizard - + Assistant de fonctionnalités Empty description provided - Description vide + Description vide fournie Overlapping with another capability - Superposition avec une autre ... + Chevauchement avec une autre capacité @@ -456,7 +456,7 @@ Les modifications seront perdues si vous ne les enregistrez pas. Primary color - Couleur principale + Couleur primaire @@ -504,17 +504,17 @@ Les modifications seront perdues si vous ne les enregistrez pas. Preview the previous step - + Prévisualisation de l'étape précédente Preview the next step - + Prévisualiser l'étape suivante Duplicate the selected step(s) - + Dupliquer les étapes sélectionnées @@ -604,12 +604,12 @@ Les modifications seront perdues si vous ne les enregistrez pas. Fade In - Fondu en entrée + Fondu en Entrée Fade Out - Fondu en sortie + Fondu en Sortie @@ -627,7 +627,7 @@ Les modifications seront perdues si vous ne les enregistrez pas. Fade In - Fondu en entrée + Fondu en Entrée @@ -637,7 +637,7 @@ Les modifications seront perdues si vous ne les enregistrez pas. Fade Out - Fondu en sortie + Fondu en Sortie @@ -866,27 +866,27 @@ Les modifications seront perdues si vous ne les enregistrez pas. Fixtures - Appareils + Luminaires Add a fixture/head - Ajouter un appareil/tête + Ajouter un Luminaires/tête Remove the selected fixture head(s) - Supprimer la ou les têtes sélectionnées + Supprimer la ou les têtes de luminaire sélectionnées Fixture - Appareil + Luminaire Mode - Mode + Mode @@ -902,22 +902,22 @@ Les modifications seront perdues si vous ne les enregistrez pas. Position - Position + Position Dimmer - + Variateur RGB - + RVB Add a new fixture - Ajouter un nouvel appareil + Ajouter un nouvel luminaire @@ -1105,7 +1105,7 @@ Les modifications seront perdues si vous ne les enregistrez pas. Channel wizard - + Assistant de canal @@ -1205,12 +1205,12 @@ Les modifications seront perdues si vous ne les enregistrez pas. Create a new fixture definition Add a new fixture definition - Créer une nouvelle définition d'appareil + Créer une nouvelle définition de luminaire Edit the selected fixture definition - Modifier la définition d'appareil sélectionnée + Modifier la définition du luminaire sélectionné @@ -1241,13 +1241,13 @@ Les modifications seront perdues si vous ne les enregistrez pas. Open a fixture definition - Ouvrir une définition d'appareil + Ouvrir une définition de luminaire Fixture definition files - Fichiers de définition d'appareil + Fichiers de définition de luminaire @@ -1355,12 +1355,12 @@ Les modifications seront perdues si vous ne les enregistrez pas. Error - Erreur + Erreur Add a new fixture group - Ajouter un nouveau groupe d'appareils + Ajouter un nouveau groupe de luminaires @@ -1370,7 +1370,7 @@ Les modifications seront perdues si vous ne les enregistrez pas. Set a Group/Fixture/Channel search filter - Définir un filtre de recherche de groupe/appareil/canaux + Définir un filtre de recherche Groupe/Luminaire/Canal @@ -1390,12 +1390,12 @@ Les modifications seront perdues si vous ne les enregistrez pas. Toggle fixtures and channels properties - Basculer les propriétés des appareils et des canaux + Basculer les propriétés des luminaires et des canaux Add/Remove a linked fixture - Ajouter/supprimer un appareil lié + Ajouter/supprimer un luminaire lié @@ -1405,7 +1405,7 @@ Les modifications seront perdues si vous ne les enregistrez pas. Mode - Mode + Mode @@ -1458,17 +1458,17 @@ Les modifications seront perdues si vous ne les enregistrez pas. Show/Hide this fixture - + Afficher/Masquer ce luminaire Invert Pan - + Inverse Pan Invert Tilt - + Inverse Tilt @@ -1476,7 +1476,7 @@ Les modifications seront perdues si vous ne les enregistrez pas. Fixture properties - Propriétés de l'appareil + Propriétés du luminaire @@ -1519,7 +1519,7 @@ Les modifications seront perdues si vous ne les enregistrez pas. Print the fixture summary - Afficher la description de l'appareil + Afficher le récapitulatif du luminaire @@ -1622,12 +1622,12 @@ Les modifications seront perdues si vous ne les enregistrez pas. Pan degrees - Degrés de pan + Degrés pan Tilt degrees - Degrés de tilt + Degrés Tilt @@ -1773,7 +1773,7 @@ Les modifications seront perdues si vous ne les enregistrez pas. New RGB Matrix - Nouvelle matrice RGB + Nouvelle Matrice RVB @@ -1909,110 +1909,111 @@ Les modifications seront perdues si vous ne les enregistrez pas. !! Warning !! - !! Attention !! + !! Attention !! Channel wizard activated - + Assistant de canal activé 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. - + Vous avez activé l'assistant de canal d'entrée. Après avoir cliqué sur OK, remuez les commandes de votre profil d'entrée mappé. Ils devraient apparaître dans la liste. Cliquez à nouveau sur le bouton de l'assistant pour arrêter la détection automatique des canaux.<br><br>Notez que l'assistant ne peut pas faire la différence entre un bouton et un curseur, vous devrez donc effectuer la modification manuellement. Unsaved changes - + Modifications non enregistrées Do you wish to save the current profile first? Changes will be lost if you don't save them. - + Souhaitez-vous d'abord enregistrer le profil actuel ? +Les modifications seront perdues si vous ne les enregistrez pas. Manufacturer - Fabricant + Fabricant Model - Modèle + Modèle Type - Type + Type MIDI Global Settings - + Paramètres globaux MIDI When MIDI notes are used, send a Note Off when value is 0 - + Lorsque des notes MIDI sont utilisées, envoyez une Note Off lorsque la valeur est 0 Channel - Canal + Canal Name - Nom + Nom Behaviour - Comportement + Comportement Generate an extra Press/Release when toggled - + Génère un appui/relâchement supplémentaire lorsqu'il est alterné Movement - + Mouvement Sensitivity - + Sensibilité Custom Feedback - + Retour personnalisé Lower value - + Valeur inférieure Upper value - + Valeur supérieure Button %1 - Bouton %1 + Bouton %1 Slider %1 - Fader %1 + Fader %1 @@ -2059,12 +2060,12 @@ Changes will be lost if you don't save them. Add Fixtures - Ajouter des appareils + Ajouter des luminaires Fixture Groups - Groupes d'appareils + Groupes de luminaires @@ -2119,7 +2120,7 @@ Changes will be lost if you don't save them. Select/Deselect all fixtures - Sélectionner/désélectionner tous les appareils + Sélectionner/désélectionner tous les luminaires @@ -2132,7 +2133,7 @@ Changes will be lost if you don't save them. Fixtures & Functions - Appareils & fonctions + Luminaires & fonctions @@ -2162,7 +2163,7 @@ Changes will be lost if you don't save them. Stop all the running functions - + Arrêtez toutes les fonctions en cours d'exécution @@ -2225,7 +2226,7 @@ Changes will be lost if you don't save them. Create a new emitter - + Créer un nouvel émetteur @@ -2235,22 +2236,22 @@ Changes will be lost if you don't save them. Drop channels here - + Déposez les canaux ici Acts on - + Agit sur Emitters - + Émetteurs Remove the selected emitter(s) - + Supprimer le(s) émetteur(s) sélectionné(s) @@ -2351,32 +2352,32 @@ Changes will be lost if you don't save them. X Ascending - + X Ascendant X Descending - + X Descendant Y Ascending - + Y Ascendant Y Descending - + Y Descendant Z Ascending - + Z Ascendant Z Descending - + Z Descendant @@ -2399,7 +2400,7 @@ Changes will be lost if you don't save them. Search a palette - + Rechercher une palette @@ -2464,27 +2465,27 @@ Changes will be lost if you don't save them. Min Degrees - + Degrés Min Max Degrees - + Degrés Max Head(s) - + Tête(s) Pan Max Degrees - + Max Degrés Pan Tilt Max Degrees - + Max Degrés.Tilt @@ -2521,7 +2522,7 @@ Changes will be lost if you don't save them. Electrical - + Électrique @@ -2567,42 +2568,42 @@ Changes will be lost if you don't save them. Channel Modifiers Editor - + Éditeur de modificateurs de canaux Insert a modified value after the selected - + Insérer une valeur modifiée après la valeur sélectionnée Delete the selected modifier value - + Supprimer la valeur du modificateur sélectionné Rename the selected modifier template - + Renommer le modèle de modificateur sélectionné Save the selected modifier template - + Enregistrer le modèle de modificateur sélectionné Templates - + Modèles Original DMX value - + Valeur DMX originale Modified DMX value - + Valeur DMX modifiée @@ -2610,132 +2611,132 @@ Changes will be lost if you don't save them. Fixture Editor Wizard - + Assistant Editeur de Luminaires Properties - + Propriétés Start - + Démarrer Width - Largeur + Largeur Amount - Quantité + Quantité Type - Type + Type Red - Rouge + Rouge Green - Vert + Vert Blue - Bleu + Bleu White - Blanc + Blanc Amber - Ambre + Ambre UV - UV + UV RGB - + RVB RGBW - + RVBW RGBAW - + RVBAW Dimmer - + Variateur Pan - Pan + Pan Tilt - Tilt + Tilt Color Macro - + Macro de couleur Shutter - Obturateur + Obturateur Beam - Rayon + Rayon Effect - Effet + Effet Label - Label + Étiquette Capability # - + Aptitude # Channel # - + Canal # Preview - Prévisualisation + Prévisualisation @@ -2748,7 +2749,7 @@ Changes will be lost if you don't save them. Dimmer - + Variateur @@ -2788,7 +2789,7 @@ Changes will be lost if you don't save them. Also create a Scene - + Créez également une scène @@ -2907,7 +2908,7 @@ Changes will be lost if you don't save them. Fixtures - Appareils + Luminaires @@ -2920,47 +2921,47 @@ Changes will be lost if you don't save them. Input Channel Editor - + Editeur de canal d'entrée Input Channel - + Canal d'entrée Number - Nombre + Nombre Name - Nom + Nom Type - Type + Type Channel - Canal + Canal Message - + Message Parameter - + Paramètre Note - Note + Note @@ -3111,7 +3112,7 @@ Niveau d'accès : Fixture/Group editing - Édition du groupe/appareil + Édition de Luminaires/Groupes @@ -3306,47 +3307,47 @@ Niveau d'accès : !! Warning !! - !! Attention !! + !! Attention !! Save this profile - + Enregistrer ce profil Toggle the automatic detection procedure - + Basculer la procédure de détection automatique Add a new channel - Ajouter un canal + Ajouter un canal Create a new input profile - + Créer un nouveau profil d'entrée Edit the selected channel - + Modifier le canal sélectionné Edit the selected input profile - + Modifier le profil d'entrée sélectionné Delete the selected channel - + Supprimer le canal sélectionné Delete the selected input profile(s) - + Supprimer le(s) profil(s) d'entrée sélectionné(s) @@ -3354,7 +3355,7 @@ Niveau d'accès : Fixture Group - Groupe d'appareils + Groupe de luminaires @@ -3414,7 +3415,7 @@ Niveau d'accès : Dimmer - + Variateur @@ -3761,7 +3762,7 @@ Niveau d'accès : Add a fixture/group - Ajouter un appareil/groupe + Ajouter un luminaire/groupe @@ -3815,7 +3816,7 @@ Niveau d'accès : Show/hide fixture tree - Afficher/masquer l'arbre d'appareils + Afficher/masquer l'arborescence des luminaires @@ -3840,7 +3841,7 @@ Niveau d'accès : Set fixture channel - Définir le canal de l'appareil + Définir le canal du luminaire @@ -3888,7 +3889,7 @@ Niveau d'accès : Remove the selected fixtures - Supprimer les appareils sélectionnés + Supprimer les luminaires sélectionnés @@ -3898,7 +3899,7 @@ Niveau d'accès : Fixtures - Appareils + Luminaires @@ -3966,22 +3967,22 @@ Niveau d'accès : Custom Background - + Arrière-plan personnalisé Select an image - Sélectionner une image + Sélectionner une image Reset background - + Réinitialiser l'arrière-plan Selected fixtures - Appareils sélectionnés + Luminaires sélectionnés @@ -4128,7 +4129,7 @@ Niveau d'accès : Normalize the selected items - + Normaliser les éléments sélectionnés Actions @@ -4322,7 +4323,7 @@ Niveau d'accès : Fixture List - Liste d'appareils + Liste de luminaires @@ -4362,147 +4363,147 @@ Niveau d'accès : Reset to default - + Réinitialisation aux valeurs par défaut Scaling factor - + Facteur d'échelle Background darker - + Arrière-plan plus sombre Background dark - + Arrière-plan sombre Background medium - + Arrière-plan moyen Background light - + Arrière-plan clair Background lighter - + Arrière-plan plus plus clair Controls background - + Arrière-plan des contrôles Foreground main - + Avant-Plan principal Foreground medium - + Avant-Plan moyen Foreground light - + Avant-Plan clair Toolbar gradient start - + Début du dégradé de la barre d'outils Sub-toolbar gradient start - + Début du dégradé de la barre d'outils Toolbar gradient end - + Fin du dégradé de la barre d'outils Toolbar hover gradient start - + Début du dégradé de survol de la barre d'outils Toolbar hover gradient end - + Fin du dégradé du survol de la barre d'outils Toolbar selection - + Sélection barre d'outils Sub-toolbar selection - + Sélection de la barre d'outils secondaire Section header - + En-tête de section Section header divider - + Séparateur d'en-tête de section Item highlight - + Élément mis en surbrillance Item highlight pressed - + Élément mis en surbrillance enfoncé Item hover - + Survol d'un élément Item selection - + Sélection d'un élément VC Frame drop area - + Zone de dépôt de trame VC Item dark border - + Bordure sombre de l'élément Save to file - + Enregistrer dans un fichier Operation completed - + Opération terminée Error - Erreur + Erreur @@ -4510,23 +4511,24 @@ Niveau d'accès : Error - Erreur + Erreur Unable to perform the operation. There is either not enough space or the target universe in invalid - + Impossible d'effectuer l'opération. +Il n'y a pas assez d'espace ou l'univers cible n'est pas valide Cut the selected items into clipboard - + Couper les éléments sélectionnés dans le presse-papiers Paste items in the clipboard at the first available position - + Coller les éléments du presse-papiers à la première position disponible @@ -4538,12 +4540,12 @@ There is either not enough space or the target universe in invalid Enable/Disable passthrough - + Activer/Désactiver l'intermédaire Enable/Disable feedbacks - Activer/désactiver le retour d'informations + Activer/désactiver les retours d'informations @@ -4837,12 +4839,12 @@ There is either not enough space or the target universe in invalid Next Cue - Cue suivante + Cue Suivante Previous Cue - Cue précédente + Cue Précédente @@ -4865,7 +4867,7 @@ There is either not enough space or the target universe in invalid Side Fader - Faders sur le flanc + Fader Latéral @@ -4883,7 +4885,7 @@ There is either not enough space or the target universe in invalid Play/Stop - + Lecture/Pause @@ -4968,7 +4970,7 @@ There is either not enough space or the target universe in invalid Side fader - Faders sur le flanc + Fader latéral @@ -4996,12 +4998,12 @@ There is either not enough space or the target universe in invalid Next Page - Page suivante + Page Suivante Previous Page - Page précédente + Page Précédente @@ -5021,7 +5023,7 @@ There is either not enough space or the target universe in invalid Page %1 - Page %1 + Page %1 @@ -5029,7 +5031,7 @@ There is either not enough space or the target universe in invalid Expand/Collapse this frame - Déplier/replier ce cadre + Déplier/Replier ce cadre @@ -5097,12 +5099,12 @@ There is either not enough space or the target universe in invalid Shortcuts - + Raccourcis Shortcut name - + Nom du raccourci @@ -5110,7 +5112,7 @@ There is either not enough space or the target universe in invalid Label %1 - Label %1 + Étiquette %1 @@ -5214,7 +5216,7 @@ There is either not enough space or the target universe in invalid Delete selected widgets - + Supprimer les widgets sélectionnés Delete functions @@ -5450,7 +5452,7 @@ There is either not enough space or the target universe in invalid Label - Label + Étiquette @@ -5505,7 +5507,7 @@ There is either not enough space or the target universe in invalid Label - Label + Étiquette @@ -5851,7 +5853,7 @@ There is either not enough space or the target universe in invalid Label - Label + Étiquette From 5fc53969988b5e5cb1fc5072a812f4078c3fc542 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Thu, 21 Mar 2024 19:31:42 +0100 Subject: [PATCH 108/212] Back to 4.13.1 debug --- CMakeLists.txt | 2 +- debian/changelog | 4 ++++ platforms/windows/qlcplus4Qt5.nsi | 2 +- platforms/windows/qlcplus4Qt6.nsi | 2 +- platforms/windows/qlcplus5Qt5.nsi | 2 +- variables.cmake | 2 +- variables.pri | 2 +- 7 files changed, 10 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5bb4681a89..7a8402aa21 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ project(qlcplus VERSION 4.13.0 LANGUAGES C CXX) # Set Release build type by default if(NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE) + set(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE) endif() # Prevent CMake make install strips off non-standard build paths diff --git a/debian/changelog b/debian/changelog index a24cde8e06..44c87ce7de 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,7 @@ +qlcplus (4.13.1) stable; urgency=low + + -- Massimo Callegari Sun, 30 Jun 2024 12:13:14 +0200 + qlcplus (4.13.0) stable; urgency=low * engine: fix Chaser random startup (thanks to Dennis Suermann) diff --git a/platforms/windows/qlcplus4Qt5.nsi b/platforms/windows/qlcplus4Qt5.nsi index afffe2c6c4..6a7271ee9e 100644 --- a/platforms/windows/qlcplus4Qt5.nsi +++ b/platforms/windows/qlcplus4Qt5.nsi @@ -15,7 +15,7 @@ ;-------------------------------- ;General Name "Q Light Controller Plus" -OutFile "QLC+_4.13.0.exe" +OutFile "QLC+_4.13.1.exe" InstallDir C:\QLC+ InstallDirRegKey HKCU "Software\qlcplus" "Install_Dir" RequestExecutionLevel user diff --git a/platforms/windows/qlcplus4Qt6.nsi b/platforms/windows/qlcplus4Qt6.nsi index 7508de12df..f211c12393 100644 --- a/platforms/windows/qlcplus4Qt6.nsi +++ b/platforms/windows/qlcplus4Qt6.nsi @@ -15,7 +15,7 @@ ;-------------------------------- ;General Name "Q Light Controller Plus" -OutFile "QLC+_4.13.0.exe" +OutFile "QLC+_4.13.1.exe" InstallDir C:\QLC+ InstallDirRegKey HKCU "Software\qlcplus" "Install_Dir" RequestExecutionLevel user diff --git a/platforms/windows/qlcplus5Qt5.nsi b/platforms/windows/qlcplus5Qt5.nsi index 222ef534e3..3653be3645 100644 --- a/platforms/windows/qlcplus5Qt5.nsi +++ b/platforms/windows/qlcplus5Qt5.nsi @@ -15,7 +15,7 @@ ;-------------------------------- ;General Name "Q Light Controller Plus" -OutFile "QLC+_5.0.0_beta3.exe" +OutFile "QLC+_5.0.0_beta4.exe" InstallDir C:\QLC+5 InstallDirRegKey HKCU "Software\qlcplus" "Install_Dir" RequestExecutionLevel user diff --git a/variables.cmake b/variables.cmake index 18b45a0c23..12269aa261 100644 --- a/variables.cmake +++ b/variables.cmake @@ -21,7 +21,7 @@ if(qmlui) add_definitions(-DQMLUI) set(APPVERSION "5.0.0 Beta 3") else() - set(APPVERSION "4.13.0") + set(APPVERSION "4.13.1 GIT") endif() if(UNIX) diff --git a/variables.pri b/variables.pri index 0afd15dd94..df382cda83 100644 --- a/variables.pri +++ b/variables.pri @@ -4,7 +4,7 @@ APPNAME = Q Light Controller Plus FXEDNAME = Fixture Definition Editor -!qmlui: APPVERSION = 4.13.0 GIT +!qmlui: APPVERSION = 4.13.1 GIT qmlui: APPVERSION = 5.0.0 Beta 3 # Disable these if you don't want to see GIT short hash in the About Box From 37f3f9e8553f9ea65b693c533bed07c6eff4bd1c Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Thu, 21 Mar 2024 19:35:12 +0100 Subject: [PATCH 109/212] webaccess: add include for FreeBSD (fix #1529) --- webaccess/src/webaccessnetwork.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/webaccess/src/webaccessnetwork.cpp b/webaccess/src/webaccessnetwork.cpp index d77a1c77d9..b94290a960 100644 --- a/webaccess/src/webaccessnetwork.cpp +++ b/webaccess/src/webaccessnetwork.cpp @@ -18,6 +18,7 @@ */ #include +#include #include #include #include From ae3e776e1435e8260ce2ae60fde23602ccbca157 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Thu, 21 Mar 2024 19:53:09 +0100 Subject: [PATCH 110/212] actions: build a release version on Windows --- .github/workflows/build.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index dd843ea9a4..283a8a2ffc 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -382,6 +382,8 @@ jobs: shell: msys2 {0} run: | set MSYSTEM=MINGW64 + # force a release build + sed -i -e 's/Debug/Release/g' CMakeLists.txt # disable Velleman plugin sed -i -e 's/ add_subdirectory(velleman)/# add_subdirectory(velleman)/g' plugins/CMakeLists.txt # fix MSYS2 system path From 9b0374329f4de7bf29f5c9bfc79a3a4bd9999c06 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Thu, 21 Mar 2024 23:32:44 +0100 Subject: [PATCH 111/212] engine: fix blackout not working (fix #1533) --- debian/changelog | 2 ++ engine/src/inputoutputmap.cpp | 3 +++ 2 files changed, 5 insertions(+) diff --git a/debian/changelog b/debian/changelog index 44c87ce7de..6d46ada349 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,5 +1,7 @@ qlcplus (4.13.1) stable; urgency=low + * engine: fix blackout not working + -- Massimo Callegari Sun, 30 Jun 2024 12:13:14 +0200 qlcplus (4.13.0) stable; urgency=low diff --git a/engine/src/inputoutputmap.cpp b/engine/src/inputoutputmap.cpp index 677ae9744d..1cf53b67f5 100644 --- a/engine/src/inputoutputmap.cpp +++ b/engine/src/inputoutputmap.cpp @@ -101,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); From 42581d981bfdc8ea002590132ff01a38e8667c74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans-J=C3=BCrgen=20Tappe?= Date: Fri, 22 Mar 2024 14:09:14 +0100 Subject: [PATCH 112/212] Fix superfluous dash to display gobo images. --- qmlui/qml/virtualconsole/VCSliderItem.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qmlui/qml/virtualconsole/VCSliderItem.qml b/qmlui/qml/virtualconsole/VCSliderItem.qml index dcea323ffe..54586bf84f 100644 --- a/qmlui/qml/virtualconsole/VCSliderItem.qml +++ b/qmlui/qml/virtualconsole/VCSliderItem.qml @@ -230,7 +230,7 @@ VCWidgetItem if (Qt.platform.os === "android") presetImageBox.source = cngResource else - presetImageBox.source = "file:/" + cngResource + presetImageBox.source = "file:" + cngResource } onClicked: colorToolLoader.toggleVisibility() From 75319e23bce78b3efb9edd1683533c3b2cf3ed60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans-J=C3=BCrgen=20Tappe?= Date: Fri, 22 Mar 2024 14:26:25 +0100 Subject: [PATCH 113/212] Scale the slider basede on the limits and input values. --- qmlui/virtualconsole/vcslider.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/qmlui/virtualconsole/vcslider.cpp b/qmlui/virtualconsole/vcslider.cpp index 8d22e3902e..c49456fed3 100644 --- a/qmlui/virtualconsole/vcslider.cpp +++ b/qmlui/virtualconsole/vcslider.cpp @@ -387,7 +387,9 @@ void VCSlider::setValue(int value, bool setDMX, bool updateFeedback) Tardis::instance()->enqueueAction(Tardis::VCSliderSetValue, id(), m_value, value); - m_value = value; + m_value = SCALE(float(value), float(0), float(UCHAR_MAX), + float(rangeLowLimit()), + float(rangeHighLimit())); switch(sliderMode()) { From c57b58398708c415b0e3a4230d0854b3e0d8983e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans-J=C3=BCrgen=20Tappe?= Date: Fri, 22 Mar 2024 15:04:26 +0100 Subject: [PATCH 114/212] Fix gobo image display in CnG selection widget. --- qmlui/qml/fixturesfunctions/PresetCapabilityItem.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qmlui/qml/fixturesfunctions/PresetCapabilityItem.qml b/qmlui/qml/fixturesfunctions/PresetCapabilityItem.qml index a1a0a5d3af..947ff790b1 100644 --- a/qmlui/qml/fixturesfunctions/PresetCapabilityItem.qml +++ b/qmlui/qml/fixturesfunctions/PresetCapabilityItem.qml @@ -63,7 +63,7 @@ Rectangle if (Qt.platform.os === "android") pic.source = resArray[0] else - pic.source = "file:/" + resArray[0] + pic.source = "file:" + resArray[0] } } From a46b37d0d23d3c4a1c5ef24502120f244f3245c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans-J=C3=BCrgen=20Tappe?= Date: Sat, 23 Mar 2024 15:57:18 +0100 Subject: [PATCH 115/212] Introduce mouse weel for VCKnob widget --- qmlui/qml/QLCPlusKnob.qml | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/qmlui/qml/QLCPlusKnob.qml b/qmlui/qml/QLCPlusKnob.qml index f9b1de0c46..1496cb0bf6 100644 --- a/qmlui/qml/QLCPlusKnob.qml +++ b/qmlui/qml/QLCPlusKnob.qml @@ -82,6 +82,34 @@ Dial context.stroke() context.closePath() } + + MouseArea + { + anchors.fill: parent + z: 2 + onWheel: { + console.log("Wheel delta: " + wheel.angleDelta.y) + from: sliderObj ? sliderObj.rangeLowLimit : 0 + to: sliderObj ? sliderObj.rangeHighLimit : 255 + + if (wheel.angleDelta.y > 0) + //if (sliderObj && sliderValue < to) + sliderObj.value += 1 + else + //if (sliderObj && sliderValue > from) + sliderObj.value -= 1 + } + onPressed: { + mouse.accepted = false + } + onPositionChanged: { + mouse.accepted = false + kCanvas.requestPaint() + } + onReleased: { + mouse.accepted = false + } + } } handle: Rectangle { From ae378d0ecb79fd807569a95bccd308f00366df39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Sat, 23 Mar 2024 18:13:29 +0100 Subject: [PATCH 116/212] linux: append qlcplus-fixtureeditor.desktop (fix #1534) --- platforms/linux/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platforms/linux/CMakeLists.txt b/platforms/linux/CMakeLists.txt index 1d0682c9ef..ef1679b4ca 100644 --- a/platforms/linux/CMakeLists.txt +++ b/platforms/linux/CMakeLists.txt @@ -2,7 +2,7 @@ project(icons) set(desktop_FILES qlcplus.desktop) if(NOT qmlui) - set(APPEND desktop_FILES qlcplus-fixtureeditor.desktop) + list(APPEND desktop_FILES qlcplus-fixtureeditor.desktop) endif() install(FILES ${desktop_FILES} DESTINATION ${INSTALLROOT}/share/applications/) From 7ce534e001e1c1e628ca84510366fda73d2b7f07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans-J=C3=BCrgen=20Tappe?= Date: Sun, 24 Mar 2024 15:26:42 +0100 Subject: [PATCH 117/212] Choose another place to apply the scaling to make know and slider work consistently. --- qmlui/qml/QLCPlusKnob.qml | 18 +++++++++++------- qmlui/virtualconsole/vcslider.cpp | 9 +++++---- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/qmlui/qml/QLCPlusKnob.qml b/qmlui/qml/QLCPlusKnob.qml index 1496cb0bf6..bec9f03e73 100644 --- a/qmlui/qml/QLCPlusKnob.qml +++ b/qmlui/qml/QLCPlusKnob.qml @@ -88,16 +88,20 @@ Dial anchors.fill: parent z: 2 onWheel: { - console.log("Wheel delta: " + wheel.angleDelta.y) - from: sliderObj ? sliderObj.rangeLowLimit : 0 - to: sliderObj ? sliderObj.rangeHighLimit : 255 + //console.log("Wheel delta: " + wheel.angleDelta.y) + var from = sliderObj ? sliderObj.rangeLowLimit : 0 + var to = sliderObj ? sliderObj.rangeHighLimit : 255 + var sliderValue = sliderObj ? sliderObj.value : 128 - if (wheel.angleDelta.y > 0) - //if (sliderObj && sliderValue < to) + if (wheel.angleDelta.y > 0) { + if (sliderObj && sliderValue < to) { sliderObj.value += 1 - else - //if (sliderObj && sliderValue > from) + } + } else { + if (sliderObj && sliderValue > from) { sliderObj.value -= 1 + } + } } onPressed: { mouse.accepted = false diff --git a/qmlui/virtualconsole/vcslider.cpp b/qmlui/virtualconsole/vcslider.cpp index c49456fed3..9dcc9e5544 100644 --- a/qmlui/virtualconsole/vcslider.cpp +++ b/qmlui/virtualconsole/vcslider.cpp @@ -387,9 +387,7 @@ void VCSlider::setValue(int value, bool setDMX, bool updateFeedback) Tardis::instance()->enqueueAction(Tardis::VCSliderSetValue, id(), m_value, value); - m_value = SCALE(float(value), float(0), float(UCHAR_MAX), - float(rangeLowLimit()), - float(rangeHighLimit())); + m_value = value; switch(sliderMode()) { @@ -1235,10 +1233,13 @@ void VCSlider::updateFeedback() void VCSlider::slotInputValueChanged(quint8 id, uchar value) { + int scaledValue = SCALE(float(value), float(0), float(UCHAR_MAX), + float(rangeLowLimit()), + float(rangeHighLimit())); switch (id) { case INPUT_SLIDER_CONTROL_ID: - setValue(value, true, false); + setValue(scaledValue, true, false); break; case INPUT_SLIDER_RESET_ID: if (value) From 7ddea0ebaa0e6781d4c79e3917813a257fc493c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans-J=C3=BCrgen=20Tappe?= Date: Sun, 24 Mar 2024 20:34:56 +0100 Subject: [PATCH 118/212] Allow multiple inputs to modify an LTP channel not just the last element in the virtual console. --- qmlui/virtualconsole/vcslider.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/qmlui/virtualconsole/vcslider.cpp b/qmlui/virtualconsole/vcslider.cpp index 9dcc9e5544..3021b9e219 100644 --- a/qmlui/virtualconsole/vcslider.cpp +++ b/qmlui/virtualconsole/vcslider.cpp @@ -1029,7 +1029,7 @@ void VCSlider::writeDMXLevel(MasterTimer* timer, QList universes) if (clickAndGoType() == CnGColors) { - float f = SCALE(float(m_value), rangeLowLimit(), rangeHighLimit(), 0.0, 200.0); + float f = SCALE(float(modLevel), rangeLowLimit(), rangeHighLimit(), 0.0, 200.0); if ((uchar)f != 0) { @@ -1136,6 +1136,10 @@ void VCSlider::writeDMXLevel(MasterTimer* timer, QList universes) if (m_isOverriding) fc->addFlag(FadeChannel::Override); + // request to autoremove LTP channels when set + if (! (chType & FadeChannel::Intensity)) + fc->addFlag(FadeChannel::AutoRemove); + if (chType & FadeChannel::Intensity && clickAndGoType() == CnGColors) { const QLCChannel *qlcch = fxi->channel(scv.channel); From fa96bb66420bb461f09ce7475fbb5d7db2d9c907 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans-J=C3=BCrgen=20Tappe?= Date: Mon, 25 Mar 2024 16:18:50 +0100 Subject: [PATCH 119/212] Present only those CnG options which are within slider limits --- qmlui/qml/fixturesfunctions/PresetCapabilityItem.qml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/qmlui/qml/fixturesfunctions/PresetCapabilityItem.qml b/qmlui/qml/fixturesfunctions/PresetCapabilityItem.qml index 947ff790b1..c408d1c5b5 100644 --- a/qmlui/qml/fixturesfunctions/PresetCapabilityItem.qml +++ b/qmlui/qml/fixturesfunctions/PresetCapabilityItem.qml @@ -42,6 +42,9 @@ Rectangle return var resArray = capability.resources + var slMin = (sliderRoot && sliderRoot.sliderObj) ? sliderRoot.sliderObj.rangeLowLimit : 0 + var slMax = (sliderRoot && sliderRoot.sliderObj) ? sliderRoot.sliderObj.rangeHighLimit : 255 + visible = (capability.min <= slMax || capability.max <= slMin) capName.label = capability.name if (resArray.length === 0) @@ -147,7 +150,11 @@ Rectangle onReleased: iRoot.color = "white" onClicked: { - var value = ((capability.max - capability.min) * capBar.width) / iRoot.width + var slMin = (sliderRoot && sliderRoot.sliderObj) ? sliderRoot.sliderObj.rangeLowLimit : 0 + var slMax = (sliderRoot && sliderRoot.sliderObj) ? sliderRoot.sliderObj.rangeHighLimit : 255 + var cMin = Math.max(capability.min, slMin) + var cMax = Math.min(capability.max, slMax) + var value = ((cMax - cMin) * capBar.width) / iRoot.width //console.log("max: " + capability.max + " min: " + capability.min + " value: " + value) valueChanged(value + capability.min) } From 45f332538b5390857a4f030a59cb8f8de5e497c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans-J=C3=BCrgen=20Tappe?= Date: Mon, 25 Mar 2024 16:33:19 +0100 Subject: [PATCH 120/212] Correctly handle rounding to make the last CnG value accessible. --- qmlui/qml/fixturesfunctions/PresetCapabilityItem.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qmlui/qml/fixturesfunctions/PresetCapabilityItem.qml b/qmlui/qml/fixturesfunctions/PresetCapabilityItem.qml index c408d1c5b5..44b459dbf3 100644 --- a/qmlui/qml/fixturesfunctions/PresetCapabilityItem.qml +++ b/qmlui/qml/fixturesfunctions/PresetCapabilityItem.qml @@ -154,8 +154,8 @@ Rectangle var slMax = (sliderRoot && sliderRoot.sliderObj) ? sliderRoot.sliderObj.rangeHighLimit : 255 var cMin = Math.max(capability.min, slMin) var cMax = Math.min(capability.max, slMax) - var value = ((cMax - cMin) * capBar.width) / iRoot.width - //console.log("max: " + capability.max + " min: " + capability.min + " value: " + value) + var value = Math.round(((cMax - cMin) * capBar.width) / iRoot.width) + //console.log("max: " + capability.max + "|" + slMax + "|" + cMax + " min: " + capability.min + "|" + slMin + "|" + cMin + " value: " + value) valueChanged(value + capability.min) } } From 1775f221e70ec0e91b333ecf5476087634160e6f Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Tue, 26 Mar 2024 19:16:14 +0100 Subject: [PATCH 121/212] engine: fix RGBMatrix cloning control mode (fix #1535) --- debian/changelog | 1 + engine/src/rgbmatrix.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/debian/changelog b/debian/changelog index 6d46ada349..5f66ee1f21 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,6 +1,7 @@ qlcplus (4.13.1) stable; urgency=low * engine: fix blackout not working + * engine: fix RGB Matrix clone control mode -- Massimo Callegari Sun, 30 Jun 2024 12:13:14 +0200 diff --git a/engine/src/rgbmatrix.cpp b/engine/src/rgbmatrix.cpp index 7b551b2c11..fb63da39e5 100644 --- a/engine/src/rgbmatrix.cpp +++ b/engine/src/rgbmatrix.cpp @@ -168,6 +168,7 @@ bool RGBMatrix::copyFrom(const Function* function) setAlgorithm(NULL); setStartColor(mtx->startColor()); setEndColor(mtx->endColor()); + setControlMode(mtx->controlMode()); return Function::copyFrom(function); } From 897028cf5768929218f381ccca23feccc38f5e6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans-J=C3=BCrgen=20Tappe?= Date: Tue, 26 Mar 2024 21:13:17 +0100 Subject: [PATCH 122/212] Initial commit of "not implemented" audio trigger widget --- qmlui/CMakeLists.txt | 1 + qmlui/qml/virtualconsole/VCAudioItem.qml | 47 +++++ .../qml/virtualconsole/VCAudioProperties.qml | 66 +++++++ qmlui/qmlui.pro | 2 + qmlui/qmlui.qrc | 2 + qmlui/virtualconsole/vcaudio.cpp | 169 ++++++++++++++++++ qmlui/virtualconsole/vcaudio.h | 97 ++++++++++ qmlui/virtualconsole/vcframe.cpp | 13 ++ qmlui/virtualconsole/virtualconsole.cpp | 2 + 9 files changed, 399 insertions(+) create mode 100644 qmlui/qml/virtualconsole/VCAudioItem.qml create mode 100644 qmlui/qml/virtualconsole/VCAudioProperties.qml create mode 100644 qmlui/virtualconsole/vcaudio.cpp create mode 100644 qmlui/virtualconsole/vcaudio.h diff --git a/qmlui/CMakeLists.txt b/qmlui/CMakeLists.txt index ad1e0bf867..0a1623dc8f 100644 --- a/qmlui/CMakeLists.txt +++ b/qmlui/CMakeLists.txt @@ -63,6 +63,7 @@ add_executable(${module_name} WIN32 MACOSX_BUNDLE uimanager.cpp uimanager.h videoeditor.cpp videoeditor.h videoprovider.cpp videoprovider.h + virtualconsole/vcaudio.cpp virtualconsole/vcaudio.h virtualconsole/vcbutton.cpp virtualconsole/vcbutton.h virtualconsole/vcclock.cpp virtualconsole/vcclock.h virtualconsole/vccuelist.cpp virtualconsole/vccuelist.h diff --git a/qmlui/qml/virtualconsole/VCAudioItem.qml b/qmlui/qml/virtualconsole/VCAudioItem.qml new file mode 100644 index 0000000000..04547cdc3f --- /dev/null +++ b/qmlui/qml/virtualconsole/VCAudioItem.qml @@ -0,0 +1,47 @@ +/* + Q Light Controller Plus + VCAudioItem.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 org.qlcplus.classes 1.0 + +import "." + +VCWidgetItem +{ + id: audioRoot + property VCAudio audioObj: null + + clip: true + + Row + { + anchors.fill: parent + + // value text box + Text + { + Layout.alignment: Qt.AlignHCenter + height: UISettings.listItemHeight + text: "Not implemented" + color: "grey" + } + } +} diff --git a/qmlui/qml/virtualconsole/VCAudioProperties.qml b/qmlui/qml/virtualconsole/VCAudioProperties.qml new file mode 100644 index 0000000000..5310afa140 --- /dev/null +++ b/qmlui/qml/virtualconsole/VCAudioProperties.qml @@ -0,0 +1,66 @@ +/* + Q Light Controller Plus + VCAudioProperties.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: audioPropsColumn.height + + property VCAudio widgetRef: null + + property int gridItemsHeight: UISettings.listItemHeight + + Column + { + id: audioPropsColumn + width: parent.width + spacing: 5 + + SectionBox + { + id: audioProp + sectionLabel: qsTr("Audio Property") + + sectionContents: + GridLayout + { + width: parent.width + columns: 2 + columnSpacing: 5 + rowSpacing: 4 + + // row 1 + RobotoText + { + height: gridItemsHeight + Layout.fillWidth: true + label: qsTr("Not implemented.") + } + } // GridLayout + } // SectionBox + } // Column +} diff --git a/qmlui/qmlui.pro b/qmlui/qmlui.pro index 41473b6f3a..767a7daad4 100644 --- a/qmlui/qmlui.pro +++ b/qmlui/qmlui.pro @@ -126,6 +126,7 @@ HEADERS += \ virtualconsole/vcbutton.h \ virtualconsole/vclabel.h \ virtualconsole/vcslider.h \ + virtualconsole/vcaudio.h \ virtualconsole/vcclock.h \ virtualconsole/vccuelist.h @@ -138,6 +139,7 @@ SOURCES += \ virtualconsole/vcbutton.cpp \ virtualconsole/vclabel.cpp \ virtualconsole/vcslider.cpp \ + virtualconsole/vcaudio.cpp \ virtualconsole/vcclock.cpp \ virtualconsole/vccuelist.cpp diff --git a/qmlui/qmlui.qrc b/qmlui/qmlui.qrc index ec4b6dfdd8..2a67d1fa14 100644 --- a/qmlui/qmlui.qrc +++ b/qmlui/qmlui.qrc @@ -233,6 +233,8 @@ qml/virtualconsole/VCLabelItem.qml qml/virtualconsole/VCSliderItem.qml qml/virtualconsole/VCSliderProperties.qml + qml/virtualconsole/VCAudioItem.qml + qml/virtualconsole/VCAudioProperties.qml qml/virtualconsole/VCClockItem.qml qml/virtualconsole/VCClockProperties.qml qml/virtualconsole/VCCueListItem.qml diff --git a/qmlui/virtualconsole/vcaudio.cpp b/qmlui/virtualconsole/vcaudio.cpp new file mode 100644 index 0000000000..c078c07855 --- /dev/null +++ b/qmlui/virtualconsole/vcaudio.cpp @@ -0,0 +1,169 @@ +/* + Q Light Controller Plus + vcaudio.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 "vcaudio.h" +#include "doc.h" + +VCAudio::VCAudio(Doc *doc, QObject *parent) + : VCWidget(doc, parent) +{ + setType(VCWidget::AudioTriggersWidget); +} + +VCAudio::~VCAudio() +{ + if (m_item) + delete m_item; +} + +QString VCAudio::defaultCaption() +{ + return tr("Audio Trigger %1").arg(id() + 1); +} + +void VCAudio::setupLookAndFeel(qreal pixelDensity, int page) +{ + setPage(page); + QFont wFont = font(); + wFont.setBold(true); + wFont.setPointSize(pixelDensity * 5.0); + setFont(wFont); +} + +void VCAudio::render(QQuickView *view, QQuickItem *parent) +{ + if (view == nullptr || parent == nullptr) + return; + + QQmlComponent *component = new QQmlComponent(view->engine(), QUrl("qrc:/VCAudioItem.qml")); + + if (component->isError()) + { + qDebug() << component->errors(); + return; + } + + m_item = qobject_cast(component->create()); + + m_item->setParentItem(parent); + m_item->setProperty("audioObj", QVariant::fromValue(this)); +} + +QString VCAudio::propertiesResource() const +{ + return QString("qrc:/VCAudioProperties.qml"); +} + +VCWidget *VCAudio::createCopy(VCWidget *parent) +{ + Q_ASSERT(parent != nullptr); + + VCAudio *audio = new VCAudio(m_doc, parent); + if (audio->copyFrom(this) == false) + { + delete audio; + audio = nullptr; + } + + return audio; +} + +bool VCAudio::copyFrom(const VCWidget *widget) +{ + const VCAudio *audio = qobject_cast (widget); + if (audio == nullptr) + return false; + + /* Copy and set properties */ + + /* Copy object lists */ + + /* Common stuff */ + return VCWidget::copyFrom(widget); +} + +FunctionParent VCAudio::functionParent() const +{ + return FunctionParent(FunctionParent::AutoVCWidget, id()); +} + +/********************************************************************* + * Load & Save + *********************************************************************/ + +bool VCAudio::loadXML(QXmlStreamReader &root) +{ + if (root.name() != KXMLQLCVCAudio) + { + qWarning() << Q_FUNC_INFO << "Audio node not found"; + return false; + } + + QXmlStreamAttributes attrs = root.attributes(); + + /* Widget commons */ + loadXMLCommon(root); + + while (root.readNextStartElement()) + { + if (root.name() == KXMLQLCWindowState) + { + bool visible = false; + int x = 0, y = 0, w = 0, h = 0; + loadXMLWindowState(root, &x, &y, &w, &h, &visible); + setGeometry(QRect(x, y, w, h)); + } + else if (root.name() == KXMLQLCVCWidgetAppearance) + { + loadXMLAppearance(root); + } + else + { + qWarning() << Q_FUNC_INFO << "Unknown audio tag:" << root.name().toString(); + root.skipCurrentElement(); + } + } + + return true; +} + +bool VCAudio::saveXML(QXmlStreamWriter *doc) +{ + Q_ASSERT(doc != nullptr); + + /* VC object entry */ + doc->writeStartElement(KXMLQLCVCAudio); + + saveXMLCommon(doc); + + /* Window state */ + saveXMLWindowState(doc); + + /* Appearance */ + saveXMLAppearance(doc); + + /* Write the tag */ + doc->writeEndElement(); + + return true; +} diff --git a/qmlui/virtualconsole/vcaudio.h b/qmlui/virtualconsole/vcaudio.h new file mode 100644 index 0000000000..4498b569f2 --- /dev/null +++ b/qmlui/virtualconsole/vcaudio.h @@ -0,0 +1,97 @@ +/* + Q Light Controller Plus + vcaudio.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 VCAUDIO_H +#define VCAUDIO_H + +#include "vcwidget.h" + +#define KXMLQLCVCAudio QString("Audio Trigger") + +class VCAudio : public VCWidget +{ + Q_OBJECT + + /********************************************************************* + * Initialization + *********************************************************************/ +public: + VCAudio(Doc* doc = nullptr, QObject *parent = nullptr); + virtual ~VCAudio(); + + /** @reimp */ + QString defaultCaption(); + + /** @reimp */ + void setupLookAndFeel(qreal pixelDensity, int page); + + /** @reimp */ + void render(QQuickView *view, QQuickItem *parent); + + /** @reimp */ + QString propertiesResource() const; + + /** @reimp */ + VCWidget *createCopy(VCWidget *parent); + +protected: + /** @reimp */ + bool copyFrom(const VCWidget* widget); + +private: + FunctionParent functionParent() const; + + /********************************************************************* + * Type + *********************************************************************/ +public: + +signals: + +private: + + /********************************************************************* + * Data + *********************************************************************/ +public: + +protected slots: + +signals: + +private: + + /********************************************************************* + * Functions connections + *********************************************************************/ +public: + +signals: + +private: + + /********************************************************************* + * Load & Save + *********************************************************************/ +public: + bool loadXML(QXmlStreamReader &root); + bool saveXML(QXmlStreamWriter *doc); +}; + +#endif diff --git a/qmlui/virtualconsole/vcframe.cpp b/qmlui/virtualconsole/vcframe.cpp index b3b103a259..d087031c9d 100644 --- a/qmlui/virtualconsole/vcframe.cpp +++ b/qmlui/virtualconsole/vcframe.cpp @@ -25,6 +25,7 @@ #include "tardis.h" #include "vcframe.h" #include "vclabel.h" +#include "vcaudio.h" #include "vcclock.h" #include "vcbutton.h" #include "vcslider.h" @@ -289,6 +290,18 @@ void VCFrame::addWidget(QQuickItem *parent, QString wType, QPoint pos) slider->render(m_vc->view(), parent); } break; + case AudioTriggersWidget: + { + VCAudio *audio = new VCAudio(m_doc, this); + QQmlEngine::setObjectOwnership(audio, QQmlEngine::CppOwnership); + m_vc->addWidgetToMap(audio); + Tardis::instance()->enqueueAction(Tardis::VCWidgetCreate, this->id(), QVariant(), + Tardis::instance()->actionToByteArray(Tardis::VCWidgetCreate, audio->id())); + audio->setGeometry(QRect(pos.x(), pos.y(), m_vc->pixelDensity() * 25, m_vc->pixelDensity() * 8)); + setupWidget(audio, currentPage()); + audio->render(m_vc->view(), parent); + } + break; case ClockWidget: { VCClock *clock = new VCClock(m_doc, this); diff --git a/qmlui/virtualconsole/virtualconsole.cpp b/qmlui/virtualconsole/virtualconsole.cpp index bc17556a08..044edb606c 100644 --- a/qmlui/virtualconsole/virtualconsole.cpp +++ b/qmlui/virtualconsole/virtualconsole.cpp @@ -33,6 +33,7 @@ #include "vcslider.h" #include "vcframe.h" #include "vclabel.h" +#include "vcaudio.h" #include "vcclock.h" #include "vcpage.h" #include "tardis.h" @@ -96,6 +97,7 @@ VirtualConsole::VirtualConsole(QQuickView *view, Doc *doc, qmlRegisterType("org.qlcplus.classes", 1, 0, "VCButton"); qmlRegisterType("org.qlcplus.classes", 1, 0, "VCLabel"); qmlRegisterType("org.qlcplus.classes", 1, 0, "VCSlider"); + qmlRegisterType("org.qlcplus.classes", 1, 0, "VCAudio"); qmlRegisterType("org.qlcplus.classes", 1, 0, "VCClock"); qmlRegisterType("org.qlcplus.classes", 1, 0, "VCClockSchedule"); qmlRegisterType("org.qlcplus.classes", 1, 0, "VCCueList"); From 4dc626ffe7a3fbce0d06f52f4db3450f5f6c9c65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans-J=C3=BCrgen=20Tappe?= Date: Tue, 26 Mar 2024 21:27:18 +0100 Subject: [PATCH 123/212] Convert the item to a modifiable item with properties displayed. --- qmlui/qml/virtualconsole/VCAudioItem.qml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/qmlui/qml/virtualconsole/VCAudioItem.qml b/qmlui/qml/virtualconsole/VCAudioItem.qml index 04547cdc3f..93f9b51896 100644 --- a/qmlui/qml/virtualconsole/VCAudioItem.qml +++ b/qmlui/qml/virtualconsole/VCAudioItem.qml @@ -29,10 +29,15 @@ VCWidgetItem id: audioRoot property VCAudio audioObj: null - clip: true + onAudioObjChanged: + { + setCommonProperties(audioObj) + } - Row + Rectangle { + clip: true + color: "grey" anchors.fill: parent // value text box @@ -41,7 +46,7 @@ VCWidgetItem Layout.alignment: Qt.AlignHCenter height: UISettings.listItemHeight text: "Not implemented" - color: "grey" + color: "#111" } } } From 3b1f1c0cb0b6034ce0db290b62fa69b07c4dd203 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans-J=C3=BCrgen=20Tappe?= Date: Wed, 27 Mar 2024 15:28:31 +0100 Subject: [PATCH 124/212] Improve look and feel of the widget template to match other templates and make it manageable. --- qmlui/qml/virtualconsole/VCAudioItem.qml | 28 ++++++++++++++----- .../qml/virtualconsole/VCAudioProperties.qml | 2 +- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/qmlui/qml/virtualconsole/VCAudioItem.qml b/qmlui/qml/virtualconsole/VCAudioItem.qml index 93f9b51896..1fb1fe3e00 100644 --- a/qmlui/qml/virtualconsole/VCAudioItem.qml +++ b/qmlui/qml/virtualconsole/VCAudioItem.qml @@ -19,9 +19,9 @@ import QtQuick 2.0 import QtQuick.Layouts 1.0 +import QtQuick.Controls 2.2 import org.qlcplus.classes 1.0 - import "." VCWidgetItem @@ -29,24 +29,38 @@ VCWidgetItem id: audioRoot property VCAudio audioObj: null + clip: true + onAudioObjChanged: { setCommonProperties(audioObj) } - Rectangle + Row { - clip: true - color: "grey" anchors.fill: parent // value text box Text { - Layout.alignment: Qt.AlignHCenter - height: UISettings.listItemHeight - text: "Not implemented" + width: parent.width + height: parent.height color: "#111" + lineHeight: 0.8 + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + textFormat: Text.RichText + wrapMode: Text.Wrap + text: "Not implemented.
    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/VCAudioProperties.qml b/qmlui/qml/virtualconsole/VCAudioProperties.qml index 5310afa140..74b5893c8e 100644 --- a/qmlui/qml/virtualconsole/VCAudioProperties.qml +++ b/qmlui/qml/virtualconsole/VCAudioProperties.qml @@ -58,7 +58,7 @@ Rectangle { height: gridItemsHeight Layout.fillWidth: true - label: qsTr("Not implemented.") + label: "Not implemented." } } // GridLayout } // SectionBox From 3576203256fdaaf204472704bf36b2f1fe6928f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans-J=C3=BCrgen=20Tappe?= Date: Wed, 27 Mar 2024 15:53:24 +0100 Subject: [PATCH 125/212] vcaudio --> vcaudiotrigger --- qmlui/CMakeLists.txt | 2 +- ...VCAudioItem.qml => VCAudioTriggerItem.qml} | 12 ++--- ...rties.qml => VCAudioTriggerProperties.qml} | 12 ++--- qmlui/qmlui.pro | 4 +- qmlui/qmlui.qrc | 4 +- .../{vcaudio.cpp => vcaudiotrigger.cpp} | 50 +++++++++---------- .../{vcaudio.h => vcaudiotrigger.h} | 14 +++--- qmlui/virtualconsole/vcframe.cpp | 16 +++--- qmlui/virtualconsole/virtualconsole.cpp | 4 +- 9 files changed, 59 insertions(+), 59 deletions(-) rename qmlui/qml/virtualconsole/{VCAudioItem.qml => VCAudioTriggerItem.qml} (88%) rename qmlui/qml/virtualconsole/{VCAudioProperties.qml => VCAudioTriggerProperties.qml} (85%) rename qmlui/virtualconsole/{vcaudio.cpp => vcaudiotrigger.cpp} (68%) rename qmlui/virtualconsole/{vcaudio.h => vcaudiotrigger.h} (89%) diff --git a/qmlui/CMakeLists.txt b/qmlui/CMakeLists.txt index 0a1623dc8f..5426a99736 100644 --- a/qmlui/CMakeLists.txt +++ b/qmlui/CMakeLists.txt @@ -63,7 +63,7 @@ add_executable(${module_name} WIN32 MACOSX_BUNDLE uimanager.cpp uimanager.h videoeditor.cpp videoeditor.h videoprovider.cpp videoprovider.h - virtualconsole/vcaudio.cpp virtualconsole/vcaudio.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 diff --git a/qmlui/qml/virtualconsole/VCAudioItem.qml b/qmlui/qml/virtualconsole/VCAudioTriggerItem.qml similarity index 88% rename from qmlui/qml/virtualconsole/VCAudioItem.qml rename to qmlui/qml/virtualconsole/VCAudioTriggerItem.qml index 1fb1fe3e00..58f84e0058 100644 --- a/qmlui/qml/virtualconsole/VCAudioItem.qml +++ b/qmlui/qml/virtualconsole/VCAudioTriggerItem.qml @@ -1,6 +1,6 @@ /* Q Light Controller Plus - VCAudioItem.qml + VCAudioTriggerItem.qml Copyright (c) Massimo Callegari @@ -26,14 +26,14 @@ import "." VCWidgetItem { - id: audioRoot - property VCAudio audioObj: null + id: audioTriggerRoot + property VCAudioTrigger audioTriggerObj: null clip: true - onAudioObjChanged: + onAudioTriggerObjChanged: { - setCommonProperties(audioObj) + setCommonProperties(audioTriggerObj) } Row @@ -45,7 +45,7 @@ VCWidgetItem { width: parent.width height: parent.height - color: "#111" + color: "#bbb" lineHeight: 0.8 horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter diff --git a/qmlui/qml/virtualconsole/VCAudioProperties.qml b/qmlui/qml/virtualconsole/VCAudioTriggerProperties.qml similarity index 85% rename from qmlui/qml/virtualconsole/VCAudioProperties.qml rename to qmlui/qml/virtualconsole/VCAudioTriggerProperties.qml index 74b5893c8e..3e8dd5d1db 100644 --- a/qmlui/qml/virtualconsole/VCAudioProperties.qml +++ b/qmlui/qml/virtualconsole/VCAudioTriggerProperties.qml @@ -1,6 +1,6 @@ /* Q Light Controller Plus - VCAudioProperties.qml + VCAudioTriggerProperties.qml Copyright (c) Massimo Callegari @@ -28,22 +28,22 @@ Rectangle { id: propsRoot color: "transparent" - height: audioPropsColumn.height + height: audioTriggerPropsColumn.height - property VCAudio widgetRef: null + property VCAudioTrigger widgetRef: null property int gridItemsHeight: UISettings.listItemHeight Column { - id: audioPropsColumn + id: audioTriggerPropsColumn width: parent.width spacing: 5 SectionBox { - id: audioProp - sectionLabel: qsTr("Audio Property") + id: audioTriggerProp + sectionLabel: qsTr("Audio Trigger Properties") sectionContents: GridLayout diff --git a/qmlui/qmlui.pro b/qmlui/qmlui.pro index 767a7daad4..028655adca 100644 --- a/qmlui/qmlui.pro +++ b/qmlui/qmlui.pro @@ -126,7 +126,7 @@ HEADERS += \ virtualconsole/vcbutton.h \ virtualconsole/vclabel.h \ virtualconsole/vcslider.h \ - virtualconsole/vcaudio.h \ + virtualconsole/vcaudiotrigger.h \ virtualconsole/vcclock.h \ virtualconsole/vccuelist.h @@ -139,7 +139,7 @@ SOURCES += \ virtualconsole/vcbutton.cpp \ virtualconsole/vclabel.cpp \ virtualconsole/vcslider.cpp \ - virtualconsole/vcaudio.cpp \ + virtualconsole/vcaudiotrigger.cpp \ virtualconsole/vcclock.cpp \ virtualconsole/vccuelist.cpp diff --git a/qmlui/qmlui.qrc b/qmlui/qmlui.qrc index 2a67d1fa14..0d539432b9 100644 --- a/qmlui/qmlui.qrc +++ b/qmlui/qmlui.qrc @@ -233,8 +233,8 @@ qml/virtualconsole/VCLabelItem.qml qml/virtualconsole/VCSliderItem.qml qml/virtualconsole/VCSliderProperties.qml - qml/virtualconsole/VCAudioItem.qml - qml/virtualconsole/VCAudioProperties.qml + qml/virtualconsole/VCAudioTriggerItem.qml + qml/virtualconsole/VCAudioTriggerProperties.qml qml/virtualconsole/VCClockItem.qml qml/virtualconsole/VCClockProperties.qml qml/virtualconsole/VCCueListItem.qml diff --git a/qmlui/virtualconsole/vcaudio.cpp b/qmlui/virtualconsole/vcaudiotrigger.cpp similarity index 68% rename from qmlui/virtualconsole/vcaudio.cpp rename to qmlui/virtualconsole/vcaudiotrigger.cpp index c078c07855..17699cfd07 100644 --- a/qmlui/virtualconsole/vcaudio.cpp +++ b/qmlui/virtualconsole/vcaudiotrigger.cpp @@ -1,6 +1,6 @@ /* Q Light Controller Plus - vcaudio.cpp + vcaudiotrigger.cpp Copyright (c) Massimo Callegari @@ -21,27 +21,27 @@ #include #include -#include "vcaudio.h" #include "doc.h" +#include "vcaudiotrigger.h" -VCAudio::VCAudio(Doc *doc, QObject *parent) +VCAudioTrigger::VCAudioTrigger(Doc *doc, QObject *parent) : VCWidget(doc, parent) { setType(VCWidget::AudioTriggersWidget); } -VCAudio::~VCAudio() +VCAudioTrigger::~VCAudioTrigger() { if (m_item) delete m_item; } -QString VCAudio::defaultCaption() +QString VCAudioTrigger::defaultCaption() { return tr("Audio Trigger %1").arg(id() + 1); } -void VCAudio::setupLookAndFeel(qreal pixelDensity, int page) +void VCAudioTrigger::setupLookAndFeel(qreal pixelDensity, int page) { setPage(page); QFont wFont = font(); @@ -50,12 +50,12 @@ void VCAudio::setupLookAndFeel(qreal pixelDensity, int page) setFont(wFont); } -void VCAudio::render(QQuickView *view, QQuickItem *parent) +void VCAudioTrigger::render(QQuickView *view, QQuickItem *parent) { if (view == nullptr || parent == nullptr) return; - QQmlComponent *component = new QQmlComponent(view->engine(), QUrl("qrc:/VCAudioItem.qml")); + QQmlComponent *component = new QQmlComponent(view->engine(), QUrl("qrc:/VCAudioTriggerItem.qml")); if (component->isError()) { @@ -66,31 +66,31 @@ void VCAudio::render(QQuickView *view, QQuickItem *parent) m_item = qobject_cast(component->create()); m_item->setParentItem(parent); - m_item->setProperty("audioObj", QVariant::fromValue(this)); + m_item->setProperty("audioTriggerObj", QVariant::fromValue(this)); } -QString VCAudio::propertiesResource() const +QString VCAudioTrigger::propertiesResource() const { - return QString("qrc:/VCAudioProperties.qml"); + return QString("qrc:/VCAudioTriggerProperties.qml"); } -VCWidget *VCAudio::createCopy(VCWidget *parent) +VCWidget *VCAudioTrigger::createCopy(VCWidget *parent) { Q_ASSERT(parent != nullptr); - VCAudio *audio = new VCAudio(m_doc, parent); - if (audio->copyFrom(this) == false) + VCAudioTrigger *audioTrigger = new VCAudioTrigger(m_doc, parent); + if (audioTrigger->copyFrom(this) == false) { - delete audio; - audio = nullptr; + delete audioTrigger; + audioTrigger = nullptr; } - return audio; + return audioTrigger; } -bool VCAudio::copyFrom(const VCWidget *widget) +bool VCAudioTrigger::copyFrom(const VCWidget *widget) { - const VCAudio *audio = qobject_cast (widget); + const VCAudioTrigger *audio = qobject_cast (widget); if (audio == nullptr) return false; @@ -102,7 +102,7 @@ bool VCAudio::copyFrom(const VCWidget *widget) return VCWidget::copyFrom(widget); } -FunctionParent VCAudio::functionParent() const +FunctionParent VCAudioTrigger::functionParent() const { return FunctionParent(FunctionParent::AutoVCWidget, id()); } @@ -111,9 +111,9 @@ FunctionParent VCAudio::functionParent() const * Load & Save *********************************************************************/ -bool VCAudio::loadXML(QXmlStreamReader &root) +bool VCAudioTrigger::loadXML(QXmlStreamReader &root) { - if (root.name() != KXMLQLCVCAudio) + if (root.name() != KXMLQLCVCAudioTrigger) { qWarning() << Q_FUNC_INFO << "Audio node not found"; return false; @@ -139,7 +139,7 @@ bool VCAudio::loadXML(QXmlStreamReader &root) } else { - qWarning() << Q_FUNC_INFO << "Unknown audio tag:" << root.name().toString(); + qWarning() << Q_FUNC_INFO << "Unknown audio trigger tag:" << root.name().toString(); root.skipCurrentElement(); } } @@ -147,12 +147,12 @@ bool VCAudio::loadXML(QXmlStreamReader &root) return true; } -bool VCAudio::saveXML(QXmlStreamWriter *doc) +bool VCAudioTrigger::saveXML(QXmlStreamWriter *doc) { Q_ASSERT(doc != nullptr); /* VC object entry */ - doc->writeStartElement(KXMLQLCVCAudio); + doc->writeStartElement(KXMLQLCVCAudioTrigger); saveXMLCommon(doc); diff --git a/qmlui/virtualconsole/vcaudio.h b/qmlui/virtualconsole/vcaudiotrigger.h similarity index 89% rename from qmlui/virtualconsole/vcaudio.h rename to qmlui/virtualconsole/vcaudiotrigger.h index 4498b569f2..3bdf566f32 100644 --- a/qmlui/virtualconsole/vcaudio.h +++ b/qmlui/virtualconsole/vcaudiotrigger.h @@ -1,6 +1,6 @@ /* Q Light Controller Plus - vcaudio.h + vcaudiotrigger.h Copyright (c) Massimo Callegari @@ -17,14 +17,14 @@ limitations under the License. */ -#ifndef VCAUDIO_H -#define VCAUDIO_H +#ifndef VCAUDIOTRIGGER_H +#define VCAUDIOTRIGGER_H #include "vcwidget.h" -#define KXMLQLCVCAudio QString("Audio Trigger") +#define KXMLQLCVCAudioTrigger QString("Audio Trigger") -class VCAudio : public VCWidget +class VCAudioTrigger : public VCWidget { Q_OBJECT @@ -32,8 +32,8 @@ class VCAudio : public VCWidget * Initialization *********************************************************************/ public: - VCAudio(Doc* doc = nullptr, QObject *parent = nullptr); - virtual ~VCAudio(); + VCAudioTrigger(Doc* doc = nullptr, QObject *parent = nullptr); + virtual ~VCAudioTrigger(); /** @reimp */ QString defaultCaption(); diff --git a/qmlui/virtualconsole/vcframe.cpp b/qmlui/virtualconsole/vcframe.cpp index d087031c9d..20ad73a6eb 100644 --- a/qmlui/virtualconsole/vcframe.cpp +++ b/qmlui/virtualconsole/vcframe.cpp @@ -25,13 +25,13 @@ #include "tardis.h" #include "vcframe.h" #include "vclabel.h" -#include "vcaudio.h" #include "vcclock.h" #include "vcbutton.h" #include "vcslider.h" #include "vccuelist.h" #include "vcsoloframe.h" #include "simplecrypt.h" +#include "vcaudiotrigger.h" #include "virtualconsole.h" #define INPUT_NEXT_PAGE_ID 0 @@ -292,14 +292,14 @@ void VCFrame::addWidget(QQuickItem *parent, QString wType, QPoint pos) break; case AudioTriggersWidget: { - VCAudio *audio = new VCAudio(m_doc, this); - QQmlEngine::setObjectOwnership(audio, QQmlEngine::CppOwnership); - m_vc->addWidgetToMap(audio); + VCAudioTrigger *audioTrigger = new VCAudioTrigger(m_doc, this); + QQmlEngine::setObjectOwnership(audioTrigger, QQmlEngine::CppOwnership); + m_vc->addWidgetToMap(audioTrigger); Tardis::instance()->enqueueAction(Tardis::VCWidgetCreate, this->id(), QVariant(), - Tardis::instance()->actionToByteArray(Tardis::VCWidgetCreate, audio->id())); - audio->setGeometry(QRect(pos.x(), pos.y(), m_vc->pixelDensity() * 25, m_vc->pixelDensity() * 8)); - setupWidget(audio, currentPage()); - audio->render(m_vc->view(), parent); + Tardis::instance()->actionToByteArray(Tardis::VCWidgetCreate, audioTrigger->id())); + audioTrigger->setGeometry(QRect(pos.x(), pos.y(), m_vc->pixelDensity() * 25, m_vc->pixelDensity() * 8)); + setupWidget(audioTrigger, currentPage()); + audioTrigger->render(m_vc->view(), parent); } break; case ClockWidget: diff --git a/qmlui/virtualconsole/virtualconsole.cpp b/qmlui/virtualconsole/virtualconsole.cpp index 044edb606c..449f5951ed 100644 --- a/qmlui/virtualconsole/virtualconsole.cpp +++ b/qmlui/virtualconsole/virtualconsole.cpp @@ -33,12 +33,12 @@ #include "vcslider.h" #include "vcframe.h" #include "vclabel.h" -#include "vcaudio.h" #include "vcclock.h" #include "vcpage.h" #include "tardis.h" #include "doc.h" #include "app.h" +#include "vcaudiotrigger.h" #define KXMLQLCVCProperties QString("Properties") #define KXMLQLCVCPropertiesSize QString("Size") @@ -97,7 +97,7 @@ VirtualConsole::VirtualConsole(QQuickView *view, Doc *doc, qmlRegisterType("org.qlcplus.classes", 1, 0, "VCButton"); qmlRegisterType("org.qlcplus.classes", 1, 0, "VCLabel"); qmlRegisterType("org.qlcplus.classes", 1, 0, "VCSlider"); - qmlRegisterType("org.qlcplus.classes", 1, 0, "VCAudio"); + qmlRegisterType("org.qlcplus.classes", 1, 0, "VCAudioTrigger"); qmlRegisterType("org.qlcplus.classes", 1, 0, "VCClock"); qmlRegisterType("org.qlcplus.classes", 1, 0, "VCClockSchedule"); qmlRegisterType("org.qlcplus.classes", 1, 0, "VCCueList"); From 8e4ccd021a21eeb58c1c393424a0a28c59f333c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans-J=C3=BCrgen=20Tappe?= Date: Wed, 27 Mar 2024 16:23:24 +0100 Subject: [PATCH 126/212] Add VCAnimation stub --- qmlui/CMakeLists.txt | 1 + qmlui/qml/virtualconsole/VCAnimationItem.qml | 66 +++++++ .../virtualconsole/VCAnimationProperties.qml | 66 +++++++ qmlui/qmlui.pro | 2 + qmlui/qmlui.qrc | 2 + qmlui/virtualconsole/vcanimation.cpp | 169 ++++++++++++++++++ qmlui/virtualconsole/vcanimation.h | 97 ++++++++++ qmlui/virtualconsole/vcaudiotrigger.cpp | 6 +- qmlui/virtualconsole/vcframe.cpp | 13 ++ qmlui/virtualconsole/virtualconsole.cpp | 4 +- 10 files changed, 422 insertions(+), 4 deletions(-) create mode 100644 qmlui/qml/virtualconsole/VCAnimationItem.qml create mode 100644 qmlui/qml/virtualconsole/VCAnimationProperties.qml create mode 100644 qmlui/virtualconsole/vcanimation.cpp create mode 100644 qmlui/virtualconsole/vcanimation.h diff --git a/qmlui/CMakeLists.txt b/qmlui/CMakeLists.txt index 5426a99736..613808bc56 100644 --- a/qmlui/CMakeLists.txt +++ b/qmlui/CMakeLists.txt @@ -63,6 +63,7 @@ add_executable(${module_name} WIN32 MACOSX_BUNDLE uimanager.cpp uimanager.h videoeditor.cpp videoeditor.h videoprovider.cpp videoprovider.h + virtualconsole/vcanimation.cpp virtualconsole/vcanimation.h virtualconsole/vcaudiotrigger.cpp virtualconsole/vcaudiotrigger.h virtualconsole/vcbutton.cpp virtualconsole/vcbutton.h virtualconsole/vcclock.cpp virtualconsole/vcclock.h diff --git a/qmlui/qml/virtualconsole/VCAnimationItem.qml b/qmlui/qml/virtualconsole/VCAnimationItem.qml new file mode 100644 index 0000000000..9e9bcfa44a --- /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: "Not implemented.
    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/qmlui.pro b/qmlui/qmlui.pro index 028655adca..5acae1c42f 100644 --- a/qmlui/qmlui.pro +++ b/qmlui/qmlui.pro @@ -126,6 +126,7 @@ HEADERS += \ virtualconsole/vcbutton.h \ virtualconsole/vclabel.h \ virtualconsole/vcslider.h \ + virtualconsole/vcanimation.h \ virtualconsole/vcaudiotrigger.h \ virtualconsole/vcclock.h \ virtualconsole/vccuelist.h @@ -139,6 +140,7 @@ SOURCES += \ virtualconsole/vcbutton.cpp \ virtualconsole/vclabel.cpp \ virtualconsole/vcslider.cpp \ + virtualconsole/vcanimation.cpp \ virtualconsole/vcaudiotrigger.cpp \ virtualconsole/vcclock.cpp \ virtualconsole/vccuelist.cpp diff --git a/qmlui/qmlui.qrc b/qmlui/qmlui.qrc index 0d539432b9..6369d7efd6 100644 --- a/qmlui/qmlui.qrc +++ b/qmlui/qmlui.qrc @@ -233,6 +233,8 @@ 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/VCClockItem.qml diff --git a/qmlui/virtualconsole/vcanimation.cpp b/qmlui/virtualconsole/vcanimation.cpp new file mode 100644 index 0000000000..1ddd37e6d9 --- /dev/null +++ b/qmlui/virtualconsole/vcanimation.cpp @@ -0,0 +1,169 @@ +/* + Q Light Controller Plus + vcanimation.cpp + + Copyright (c) Massimo Callegari + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#include +#include +#include + +#include "doc.h" +#include "vcanimation.h" + +VCAnimation::VCAnimation(Doc *doc, QObject *parent) + : VCWidget(doc, parent) +{ + setType(VCWidget::AnimationWidget); +} + +VCAnimation::~VCAnimation() +{ + if (m_item) + delete m_item; +} + +QString VCAnimation::defaultCaption() +{ + return tr("Animation %1").arg(id() + 1); +} + +void VCAnimation::setupLookAndFeel(qreal pixelDensity, int page) +{ + setPage(page); + QFont wFont = font(); + wFont.setBold(true); + wFont.setPointSize(pixelDensity * 5.0); + setFont(wFont); +} + +void VCAnimation::render(QQuickView *view, QQuickItem *parent) +{ + if (view == nullptr || parent == nullptr) + return; + + QQmlComponent *component = new QQmlComponent(view->engine(), QUrl("qrc:/VCAnimationItem.qml")); + + if (component->isError()) + { + qDebug() << component->errors(); + return; + } + + m_item = qobject_cast(component->create()); + + m_item->setParentItem(parent); + m_item->setProperty("animationObj", QVariant::fromValue(this)); +} + +QString VCAnimation::propertiesResource() const +{ + return QString("qrc:/VCAnimationProperties.qml"); +} + +VCWidget *VCAnimation::createCopy(VCWidget *parent) +{ + Q_ASSERT(parent != nullptr); + + VCAnimation *animation = new VCAnimation(m_doc, parent); + if (animation->copyFrom(this) == false) + { + delete animation; + animation = nullptr; + } + + return animation; +} + +bool VCAnimation::copyFrom(const VCWidget *widget) +{ + const VCAnimation *animation = qobject_cast (widget); + if (animation == nullptr) + return false; + + /* Copy and set properties */ + + /* Copy object lists */ + + /* Common stuff */ + return VCWidget::copyFrom(widget); +} + +FunctionParent VCAnimation::functionParent() const +{ + return FunctionParent(FunctionParent::AutoVCWidget, id()); +} + +/********************************************************************* + * Load & Save + *********************************************************************/ + +bool VCAnimation::loadXML(QXmlStreamReader &root) +{ + if (root.name() != KXMLQLCVCAnimation) + { + qWarning() << Q_FUNC_INFO << "Animation node not found"; + return false; + } + + QXmlStreamAttributes attrs = root.attributes(); + + /* Widget commons */ + loadXMLCommon(root); + + while (root.readNextStartElement()) + { + if (root.name() == KXMLQLCWindowState) + { + bool visible = false; + int x = 0, y = 0, w = 0, h = 0; + loadXMLWindowState(root, &x, &y, &w, &h, &visible); + setGeometry(QRect(x, y, w, h)); + } + else if (root.name() == KXMLQLCVCWidgetAppearance) + { + loadXMLAppearance(root); + } + else + { + qWarning() << Q_FUNC_INFO << "Unknown animation tag:" << root.name().toString(); + root.skipCurrentElement(); + } + } + + return true; +} + +bool VCAnimation::saveXML(QXmlStreamWriter *doc) +{ + Q_ASSERT(doc != nullptr); + + /* VC object entry */ + doc->writeStartElement(KXMLQLCVCAnimation); + + saveXMLCommon(doc); + + /* Window state */ + saveXMLWindowState(doc); + + /* Appearance */ + saveXMLAppearance(doc); + + /* Write the tag */ + doc->writeEndElement(); + + return true; +} diff --git a/qmlui/virtualconsole/vcanimation.h b/qmlui/virtualconsole/vcanimation.h new file mode 100644 index 0000000000..2307affe36 --- /dev/null +++ b/qmlui/virtualconsole/vcanimation.h @@ -0,0 +1,97 @@ +/* + Q Light Controller Plus + vcanimation.h + + Copyright (c) Massimo Callegari + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#ifndef VCANIMATION_H +#define VCANIMATION_H + +#include "vcwidget.h" + +#define KXMLQLCVCAnimation QString("Animation") + +class VCAnimation : public VCWidget +{ + Q_OBJECT + + /********************************************************************* + * Initialization + *********************************************************************/ +public: + VCAnimation(Doc* doc = nullptr, QObject *parent = nullptr); + virtual ~VCAnimation(); + + /** @reimp */ + QString defaultCaption(); + + /** @reimp */ + void setupLookAndFeel(qreal pixelDensity, int page); + + /** @reimp */ + void render(QQuickView *view, QQuickItem *parent); + + /** @reimp */ + QString propertiesResource() const; + + /** @reimp */ + VCWidget *createCopy(VCWidget *parent); + +protected: + /** @reimp */ + bool copyFrom(const VCWidget* widget); + +private: + FunctionParent functionParent() const; + + /********************************************************************* + * Type + *********************************************************************/ +public: + +signals: + +private: + + /********************************************************************* + * Data + *********************************************************************/ +public: + +protected slots: + +signals: + +private: + + /********************************************************************* + * Functions connections + *********************************************************************/ +public: + +signals: + +private: + + /********************************************************************* + * Load & Save + *********************************************************************/ +public: + bool loadXML(QXmlStreamReader &root); + bool saveXML(QXmlStreamWriter *doc); +}; + +#endif diff --git a/qmlui/virtualconsole/vcaudiotrigger.cpp b/qmlui/virtualconsole/vcaudiotrigger.cpp index 17699cfd07..5d7cc4a3e9 100644 --- a/qmlui/virtualconsole/vcaudiotrigger.cpp +++ b/qmlui/virtualconsole/vcaudiotrigger.cpp @@ -90,8 +90,8 @@ VCWidget *VCAudioTrigger::createCopy(VCWidget *parent) bool VCAudioTrigger::copyFrom(const VCWidget *widget) { - const VCAudioTrigger *audio = qobject_cast (widget); - if (audio == nullptr) + const VCAudioTrigger *audioTrigger = qobject_cast (widget); + if (audioTrigger == nullptr) return false; /* Copy and set properties */ @@ -115,7 +115,7 @@ bool VCAudioTrigger::loadXML(QXmlStreamReader &root) { if (root.name() != KXMLQLCVCAudioTrigger) { - qWarning() << Q_FUNC_INFO << "Audio node not found"; + qWarning() << Q_FUNC_INFO << "Audio trigger node not found"; return false; } diff --git a/qmlui/virtualconsole/vcframe.cpp b/qmlui/virtualconsole/vcframe.cpp index 20ad73a6eb..0a2cf0a51b 100644 --- a/qmlui/virtualconsole/vcframe.cpp +++ b/qmlui/virtualconsole/vcframe.cpp @@ -31,6 +31,7 @@ #include "vccuelist.h" #include "vcsoloframe.h" #include "simplecrypt.h" +#include "vcanimation.h" #include "vcaudiotrigger.h" #include "virtualconsole.h" @@ -290,6 +291,18 @@ void VCFrame::addWidget(QQuickItem *parent, QString wType, QPoint pos) slider->render(m_vc->view(), parent); } break; + case AnimationWidget: + { + VCAnimation *animation = new VCAnimation(m_doc, this); + QQmlEngine::setObjectOwnership(animation, QQmlEngine::CppOwnership); + m_vc->addWidgetToMap(animation); + Tardis::instance()->enqueueAction(Tardis::VCWidgetCreate, this->id(), QVariant(), + Tardis::instance()->actionToByteArray(Tardis::VCWidgetCreate, animation->id())); + animation->setGeometry(QRect(pos.x(), pos.y(), m_vc->pixelDensity() * 25, m_vc->pixelDensity() * 8)); + setupWidget(animation, currentPage()); + animation->render(m_vc->view(), parent); + } + break; case AudioTriggersWidget: { VCAudioTrigger *audioTrigger = new VCAudioTrigger(m_doc, this); diff --git a/qmlui/virtualconsole/virtualconsole.cpp b/qmlui/virtualconsole/virtualconsole.cpp index 449f5951ed..ae76582d5b 100644 --- a/qmlui/virtualconsole/virtualconsole.cpp +++ b/qmlui/virtualconsole/virtualconsole.cpp @@ -33,12 +33,13 @@ #include "vcslider.h" #include "vcframe.h" #include "vclabel.h" +#include "vcanimation.h" +#include "vcaudiotrigger.h" #include "vcclock.h" #include "vcpage.h" #include "tardis.h" #include "doc.h" #include "app.h" -#include "vcaudiotrigger.h" #define KXMLQLCVCProperties QString("Properties") #define KXMLQLCVCPropertiesSize QString("Size") @@ -97,6 +98,7 @@ VirtualConsole::VirtualConsole(QQuickView *view, Doc *doc, qmlRegisterType("org.qlcplus.classes", 1, 0, "VCButton"); qmlRegisterType("org.qlcplus.classes", 1, 0, "VCLabel"); qmlRegisterType("org.qlcplus.classes", 1, 0, "VCSlider"); + qmlRegisterType("org.qlcplus.classes", 1, 0, "VCAnimation"); qmlRegisterType("org.qlcplus.classes", 1, 0, "VCAudioTrigger"); qmlRegisterType("org.qlcplus.classes", 1, 0, "VCClock"); qmlRegisterType("org.qlcplus.classes", 1, 0, "VCClockSchedule"); From e4ad963aec3dbc02d3b4f7fdfc7adc75530dc655 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans-J=C3=BCrgen=20Tappe?= Date: Wed, 27 Mar 2024 16:39:58 +0100 Subject: [PATCH 127/212] Add VCXYPad --- qmlui/CMakeLists.txt | 1 + qmlui/qml/virtualconsole/VCXYPadItem.qml | 66 +++++++ .../qml/virtualconsole/VCXYPadProperties.qml | 66 +++++++ qmlui/qmlui.pro | 2 + qmlui/qmlui.qrc | 2 + qmlui/virtualconsole/vcframe.cpp | 13 ++ qmlui/virtualconsole/vcxypad.cpp | 169 ++++++++++++++++++ qmlui/virtualconsole/vcxypad.h | 97 ++++++++++ qmlui/virtualconsole/virtualconsole.cpp | 2 + 9 files changed, 418 insertions(+) create mode 100644 qmlui/qml/virtualconsole/VCXYPadItem.qml create mode 100644 qmlui/qml/virtualconsole/VCXYPadProperties.qml create mode 100644 qmlui/virtualconsole/vcxypad.cpp create mode 100644 qmlui/virtualconsole/vcxypad.h diff --git a/qmlui/CMakeLists.txt b/qmlui/CMakeLists.txt index 613808bc56..1cc358e6dc 100644 --- a/qmlui/CMakeLists.txt +++ b/qmlui/CMakeLists.txt @@ -74,6 +74,7 @@ add_executable(${module_name} WIN32 MACOSX_BUNDLE virtualconsole/vcslider.cpp virtualconsole/vcslider.h virtualconsole/vcsoloframe.cpp virtualconsole/vcsoloframe.h virtualconsole/vcwidget.cpp virtualconsole/vcwidget.h + virtualconsole/vcxypad.cpp virtualconsole/vcxypad.h virtualconsole/virtualconsole.cpp virtualconsole/virtualconsole.h ${QM_FILES} ) diff --git a/qmlui/qml/virtualconsole/VCXYPadItem.qml b/qmlui/qml/virtualconsole/VCXYPadItem.qml new file mode 100644 index 0000000000..6ec1c9fb00 --- /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: "Not implemented.
    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/qmlui.pro b/qmlui/qmlui.pro index 5acae1c42f..0a3ce7305e 100644 --- a/qmlui/qmlui.pro +++ b/qmlui/qmlui.pro @@ -128,6 +128,7 @@ HEADERS += \ virtualconsole/vcslider.h \ virtualconsole/vcanimation.h \ virtualconsole/vcaudiotrigger.h \ + virtualconsole/vcxypad.h \ virtualconsole/vcclock.h \ virtualconsole/vccuelist.h @@ -142,6 +143,7 @@ SOURCES += \ virtualconsole/vcslider.cpp \ virtualconsole/vcanimation.cpp \ virtualconsole/vcaudiotrigger.cpp \ + virtualconsole/vcxypad.cpp \ virtualconsole/vcclock.cpp \ virtualconsole/vccuelist.cpp diff --git a/qmlui/qmlui.qrc b/qmlui/qmlui.qrc index 6369d7efd6..5d733cc0eb 100644 --- a/qmlui/qmlui.qrc +++ b/qmlui/qmlui.qrc @@ -237,6 +237,8 @@ qml/virtualconsole/VCAnimationProperties.qml qml/virtualconsole/VCAudioTriggerItem.qml qml/virtualconsole/VCAudioTriggerProperties.qml + qml/virtualconsole/VCXYPadItem.qml + qml/virtualconsole/VCXYPadProperties.qml qml/virtualconsole/VCClockItem.qml qml/virtualconsole/VCClockProperties.qml qml/virtualconsole/VCCueListItem.qml diff --git a/qmlui/virtualconsole/vcframe.cpp b/qmlui/virtualconsole/vcframe.cpp index 0a2cf0a51b..cf9380407f 100644 --- a/qmlui/virtualconsole/vcframe.cpp +++ b/qmlui/virtualconsole/vcframe.cpp @@ -33,6 +33,7 @@ #include "simplecrypt.h" #include "vcanimation.h" #include "vcaudiotrigger.h" +#include "vcxypad.h" #include "virtualconsole.h" #define INPUT_NEXT_PAGE_ID 0 @@ -315,6 +316,18 @@ void VCFrame::addWidget(QQuickItem *parent, QString wType, QPoint pos) audioTrigger->render(m_vc->view(), parent); } break; + case XYPadWidget: + { + VCXYPad *xyPad = new VCXYPad(m_doc, this); + QQmlEngine::setObjectOwnership(xyPad, QQmlEngine::CppOwnership); + m_vc->addWidgetToMap(xyPad); + Tardis::instance()->enqueueAction(Tardis::VCWidgetCreate, this->id(), QVariant(), + Tardis::instance()->actionToByteArray(Tardis::VCWidgetCreate, xyPad->id())); + xyPad->setGeometry(QRect(pos.x(), pos.y(), m_vc->pixelDensity() * 25, m_vc->pixelDensity() * 8)); + setupWidget(xyPad, currentPage()); + xyPad->render(m_vc->view(), parent); + } + break; case ClockWidget: { VCClock *clock = new VCClock(m_doc, this); diff --git a/qmlui/virtualconsole/vcxypad.cpp b/qmlui/virtualconsole/vcxypad.cpp new file mode 100644 index 0000000000..7ca68b1974 --- /dev/null +++ b/qmlui/virtualconsole/vcxypad.cpp @@ -0,0 +1,169 @@ +/* + Q Light Controller Plus + vcxypad.cpp + + Copyright (c) Massimo Callegari + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#include +#include +#include + +#include "doc.h" +#include "vcxypad.h" + +VCXYPad::VCXYPad(Doc *doc, QObject *parent) + : VCWidget(doc, parent) +{ + setType(VCWidget::XYPadWidget); +} + +VCXYPad::~VCXYPad() +{ + if (m_item) + delete m_item; +} + +QString VCXYPad::defaultCaption() +{ + return tr("XY Pad %1").arg(id() + 1); +} + +void VCXYPad::setupLookAndFeel(qreal pixelDensity, int page) +{ + setPage(page); + QFont wFont = font(); + wFont.setBold(true); + wFont.setPointSize(pixelDensity * 5.0); + setFont(wFont); +} + +void VCXYPad::render(QQuickView *view, QQuickItem *parent) +{ + if (view == nullptr || parent == nullptr) + return; + + QQmlComponent *component = new QQmlComponent(view->engine(), QUrl("qrc:/VCXYPadItem.qml")); + + if (component->isError()) + { + qDebug() << component->errors(); + return; + } + + m_item = qobject_cast(component->create()); + + m_item->setParentItem(parent); + m_item->setProperty("XYPadObj", QVariant::fromValue(this)); +} + +QString VCXYPad::propertiesResource() const +{ + return QString("qrc:/VCXYPadProperties.qml"); +} + +VCWidget *VCXYPad::createCopy(VCWidget *parent) +{ + Q_ASSERT(parent != nullptr); + + VCXYPad *XYPad = new VCXYPad(m_doc, parent); + if (XYPad->copyFrom(this) == false) + { + delete XYPad; + XYPad = nullptr; + } + + return XYPad; +} + +bool VCXYPad::copyFrom(const VCWidget *widget) +{ + const VCXYPad *XYPad = qobject_cast (widget); + if (XYPad == nullptr) + return false; + + /* Copy and set properties */ + + /* Copy object lists */ + + /* Common stuff */ + return VCWidget::copyFrom(widget); +} + +FunctionParent VCXYPad::functionParent() const +{ + return FunctionParent(FunctionParent::AutoVCWidget, id()); +} + +/********************************************************************* + * Load & Save + *********************************************************************/ + +bool VCXYPad::loadXML(QXmlStreamReader &root) +{ + if (root.name() != KXMLQLCVCXYPAD) + { + qWarning() << Q_FUNC_INFO << "XY Pad node not found"; + return false; + } + + QXmlStreamAttributes attrs = root.attributes(); + + /* Widget commons */ + loadXMLCommon(root); + + while (root.readNextStartElement()) + { + if (root.name() == KXMLQLCWindowState) + { + bool visible = false; + int x = 0, y = 0, w = 0, h = 0; + loadXMLWindowState(root, &x, &y, &w, &h, &visible); + setGeometry(QRect(x, y, w, h)); + } + else if (root.name() == KXMLQLCVCWidgetAppearance) + { + loadXMLAppearance(root); + } + else + { + qWarning() << Q_FUNC_INFO << "Unknown XY pad tag:" << root.name().toString(); + root.skipCurrentElement(); + } + } + + return true; +} + +bool VCXYPad::saveXML(QXmlStreamWriter *doc) +{ + Q_ASSERT(doc != nullptr); + + /* VC object entry */ + doc->writeStartElement(KXMLQLCVCXYPAD); + + saveXMLCommon(doc); + + /* Window state */ + saveXMLWindowState(doc); + + /* Appearance */ + saveXMLAppearance(doc); + + /* Write the tag */ + doc->writeEndElement(); + + return true; +} diff --git a/qmlui/virtualconsole/vcxypad.h b/qmlui/virtualconsole/vcxypad.h new file mode 100644 index 0000000000..4d62321e7c --- /dev/null +++ b/qmlui/virtualconsole/vcxypad.h @@ -0,0 +1,97 @@ +/* + Q Light Controller Plus + vcxypad.h + + Copyright (c) Massimo Callegari + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#ifndef VCXYPAD_H +#define VCXYPAD_H + +#include "vcwidget.h" + +#define KXMLQLCVCXYPAD QString("XY Pad") + +class VCXYPad : public VCWidget +{ + Q_OBJECT + + /********************************************************************* + * Initialization + *********************************************************************/ +public: + VCXYPad(Doc* doc = nullptr, QObject *parent = nullptr); + virtual ~VCXYPad(); + + /** @reimp */ + QString defaultCaption(); + + /** @reimp */ + void setupLookAndFeel(qreal pixelDensity, int page); + + /** @reimp */ + void render(QQuickView *view, QQuickItem *parent); + + /** @reimp */ + QString propertiesResource() const; + + /** @reimp */ + VCWidget *createCopy(VCWidget *parent); + +protected: + /** @reimp */ + bool copyFrom(const VCWidget* widget); + +private: + FunctionParent functionParent() const; + + /********************************************************************* + * Type + *********************************************************************/ +public: + +signals: + +private: + + /********************************************************************* + * Data + *********************************************************************/ +public: + +protected slots: + +signals: + +private: + + /********************************************************************* + * Functions connections + *********************************************************************/ +public: + +signals: + +private: + + /********************************************************************* + * Load & Save + *********************************************************************/ +public: + bool loadXML(QXmlStreamReader &root); + bool saveXML(QXmlStreamWriter *doc); +}; + +#endif diff --git a/qmlui/virtualconsole/virtualconsole.cpp b/qmlui/virtualconsole/virtualconsole.cpp index ae76582d5b..42d4118deb 100644 --- a/qmlui/virtualconsole/virtualconsole.cpp +++ b/qmlui/virtualconsole/virtualconsole.cpp @@ -35,6 +35,7 @@ #include "vclabel.h" #include "vcanimation.h" #include "vcaudiotrigger.h" +#include "vcxypad.h" #include "vcclock.h" #include "vcpage.h" #include "tardis.h" @@ -100,6 +101,7 @@ VirtualConsole::VirtualConsole(QQuickView *view, Doc *doc, qmlRegisterType("org.qlcplus.classes", 1, 0, "VCSlider"); qmlRegisterType("org.qlcplus.classes", 1, 0, "VCAnimation"); qmlRegisterType("org.qlcplus.classes", 1, 0, "VCAudioTrigger"); + qmlRegisterType("org.qlcplus.classes", 1, 0, "VCXYPad"); qmlRegisterType("org.qlcplus.classes", 1, 0, "VCClock"); qmlRegisterType("org.qlcplus.classes", 1, 0, "VCClockSchedule"); qmlRegisterType("org.qlcplus.classes", 1, 0, "VCCueList"); From 8859b05b1088c402a38b576ce2388e637ba4dff6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans-J=C3=BCrgen=20Tappe?= Date: Wed, 27 Mar 2024 16:53:58 +0100 Subject: [PATCH 128/212] Add VCSpeedDial --- qmlui/CMakeLists.txt | 1 + qmlui/qml/virtualconsole/VCSpeedDialItem.qml | 66 +++++++ .../virtualconsole/VCSpeedDialProperties.qml | 66 +++++++ qmlui/qmlui.pro | 2 + qmlui/qmlui.qrc | 2 + qmlui/virtualconsole/vcframe.cpp | 13 ++ qmlui/virtualconsole/vcspeeddial.cpp | 169 ++++++++++++++++++ qmlui/virtualconsole/vcspeeddial.h | 97 ++++++++++ qmlui/virtualconsole/virtualconsole.cpp | 2 + 9 files changed, 418 insertions(+) create mode 100644 qmlui/qml/virtualconsole/VCSpeedDialItem.qml create mode 100644 qmlui/qml/virtualconsole/VCSpeedDialProperties.qml create mode 100644 qmlui/virtualconsole/vcspeeddial.cpp create mode 100644 qmlui/virtualconsole/vcspeeddial.h diff --git a/qmlui/CMakeLists.txt b/qmlui/CMakeLists.txt index 1cc358e6dc..9d31e37666 100644 --- a/qmlui/CMakeLists.txt +++ b/qmlui/CMakeLists.txt @@ -75,6 +75,7 @@ add_executable(${module_name} WIN32 MACOSX_BUNDLE virtualconsole/vcsoloframe.cpp virtualconsole/vcsoloframe.h virtualconsole/vcwidget.cpp virtualconsole/vcwidget.h virtualconsole/vcxypad.cpp virtualconsole/vcxypad.h + virtualconsole/vcspeeddial.cpp virtualconsole/vcspeeddial.h virtualconsole/virtualconsole.cpp virtualconsole/virtualconsole.h ${QM_FILES} ) diff --git a/qmlui/qml/virtualconsole/VCSpeedDialItem.qml b/qmlui/qml/virtualconsole/VCSpeedDialItem.qml new file mode 100644 index 0000000000..d0c37ef017 --- /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: "Not implemented.
    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/qmlui.pro b/qmlui/qmlui.pro index 0a3ce7305e..fc62244c62 100644 --- a/qmlui/qmlui.pro +++ b/qmlui/qmlui.pro @@ -129,6 +129,7 @@ HEADERS += \ virtualconsole/vcanimation.h \ virtualconsole/vcaudiotrigger.h \ virtualconsole/vcxypad.h \ + virtualconsole/vcspeeddial.h \ virtualconsole/vcclock.h \ virtualconsole/vccuelist.h @@ -144,6 +145,7 @@ SOURCES += \ 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 5d733cc0eb..e0c168ee2b 100644 --- a/qmlui/qmlui.qrc +++ b/qmlui/qmlui.qrc @@ -239,6 +239,8 @@ 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/virtualconsole/vcframe.cpp b/qmlui/virtualconsole/vcframe.cpp index cf9380407f..2e9522c539 100644 --- a/qmlui/virtualconsole/vcframe.cpp +++ b/qmlui/virtualconsole/vcframe.cpp @@ -34,6 +34,7 @@ #include "vcanimation.h" #include "vcaudiotrigger.h" #include "vcxypad.h" +#include "vcspeeddial.h" #include "virtualconsole.h" #define INPUT_NEXT_PAGE_ID 0 @@ -328,6 +329,18 @@ void VCFrame::addWidget(QQuickItem *parent, QString wType, QPoint pos) xyPad->render(m_vc->view(), parent); } break; + case SpeedDialWidget: + { + VCSpeedDial *speedDial = new VCSpeedDial(m_doc, this); + QQmlEngine::setObjectOwnership(speedDial, QQmlEngine::CppOwnership); + m_vc->addWidgetToMap(speedDial); + Tardis::instance()->enqueueAction(Tardis::VCWidgetCreate, this->id(), QVariant(), + Tardis::instance()->actionToByteArray(Tardis::VCWidgetCreate, speedDial->id())); + speedDial->setGeometry(QRect(pos.x(), pos.y(), m_vc->pixelDensity() * 25, m_vc->pixelDensity() * 8)); + setupWidget(speedDial, currentPage()); + speedDial->render(m_vc->view(), parent); + } + break; case ClockWidget: { VCClock *clock = new VCClock(m_doc, this); diff --git a/qmlui/virtualconsole/vcspeeddial.cpp b/qmlui/virtualconsole/vcspeeddial.cpp new file mode 100644 index 0000000000..337ae16e36 --- /dev/null +++ b/qmlui/virtualconsole/vcspeeddial.cpp @@ -0,0 +1,169 @@ +/* + Q Light Controller Plus + vcspeeddial.cpp + + Copyright (c) Massimo Callegari + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#include +#include +#include + +#include "doc.h" +#include "vcspeeddial.h" + +VCSpeedDial::VCSpeedDial(Doc *doc, QObject *parent) + : VCWidget(doc, parent) +{ + setType(VCWidget::SpeedDialWidget); +} + +VCSpeedDial::~VCSpeedDial() +{ + if (m_item) + delete m_item; +} + +QString VCSpeedDial::defaultCaption() +{ + return tr("Speed Dial %1").arg(id() + 1); +} + +void VCSpeedDial::setupLookAndFeel(qreal pixelDensity, int page) +{ + setPage(page); + QFont wFont = font(); + wFont.setBold(true); + wFont.setPointSize(pixelDensity * 5.0); + setFont(wFont); +} + +void VCSpeedDial::render(QQuickView *view, QQuickItem *parent) +{ + if (view == nullptr || parent == nullptr) + return; + + QQmlComponent *component = new QQmlComponent(view->engine(), QUrl("qrc:/VCSpeedDialItem.qml")); + + if (component->isError()) + { + qDebug() << component->errors(); + return; + } + + m_item = qobject_cast(component->create()); + + m_item->setParentItem(parent); + m_item->setProperty("speedDialObj", QVariant::fromValue(this)); +} + +QString VCSpeedDial::propertiesResource() const +{ + return QString("qrc:/VCSpeedDialProperties.qml"); +} + +VCWidget *VCSpeedDial::createCopy(VCWidget *parent) +{ + Q_ASSERT(parent != nullptr); + + VCSpeedDial *speedDial = new VCSpeedDial(m_doc, parent); + if (speedDial->copyFrom(this) == false) + { + delete speedDial; + speedDial = nullptr; + } + + return speedDial; +} + +bool VCSpeedDial::copyFrom(const VCWidget *widget) +{ + const VCSpeedDial *speedDial = qobject_cast (widget); + if (speedDial == nullptr) + return false; + + /* Copy and set properties */ + + /* Copy object lists */ + + /* Common stuff */ + return VCWidget::copyFrom(widget); +} + +FunctionParent VCSpeedDial::functionParent() const +{ + return FunctionParent(FunctionParent::AutoVCWidget, id()); +} + +/********************************************************************* + * Load & Save + *********************************************************************/ + +bool VCSpeedDial::loadXML(QXmlStreamReader &root) +{ + if (root.name() != KXMLQLCVCSpeedDial) + { + qWarning() << Q_FUNC_INFO << "Speed dial node not found"; + return false; + } + + QXmlStreamAttributes attrs = root.attributes(); + + /* Widget commons */ + loadXMLCommon(root); + + while (root.readNextStartElement()) + { + if (root.name() == KXMLQLCWindowState) + { + bool visible = false; + int x = 0, y = 0, w = 0, h = 0; + loadXMLWindowState(root, &x, &y, &w, &h, &visible); + setGeometry(QRect(x, y, w, h)); + } + else if (root.name() == KXMLQLCVCWidgetAppearance) + { + loadXMLAppearance(root); + } + else + { + qWarning() << Q_FUNC_INFO << "Unknown speed dial tag:" << root.name().toString(); + root.skipCurrentElement(); + } + } + + return true; +} + +bool VCSpeedDial::saveXML(QXmlStreamWriter *doc) +{ + Q_ASSERT(doc != nullptr); + + /* VC object entry */ + doc->writeStartElement(KXMLQLCVCSpeedDial); + + saveXMLCommon(doc); + + /* Window state */ + saveXMLWindowState(doc); + + /* Appearance */ + saveXMLAppearance(doc); + + /* Write the tag */ + doc->writeEndElement(); + + return true; +} diff --git a/qmlui/virtualconsole/vcspeeddial.h b/qmlui/virtualconsole/vcspeeddial.h new file mode 100644 index 0000000000..ef893eadc4 --- /dev/null +++ b/qmlui/virtualconsole/vcspeeddial.h @@ -0,0 +1,97 @@ +/* + Q Light Controller Plus + vcspeeddial.h + + Copyright (c) Massimo Callegari + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#ifndef VCSPEEDDIAL_H +#define VCSPEEDDIAL_H + +#include "vcwidget.h" + +#define KXMLQLCVCSpeedDial QString("Speed Dial") + +class VCSpeedDial : public VCWidget +{ + Q_OBJECT + + /********************************************************************* + * Initialization + *********************************************************************/ +public: + VCSpeedDial(Doc* doc = nullptr, QObject *parent = nullptr); + virtual ~VCSpeedDial(); + + /** @reimp */ + QString defaultCaption(); + + /** @reimp */ + void setupLookAndFeel(qreal pixelDensity, int page); + + /** @reimp */ + void render(QQuickView *view, QQuickItem *parent); + + /** @reimp */ + QString propertiesResource() const; + + /** @reimp */ + VCWidget *createCopy(VCWidget *parent); + +protected: + /** @reimp */ + bool copyFrom(const VCWidget* widget); + +private: + FunctionParent functionParent() const; + + /********************************************************************* + * Type + *********************************************************************/ +public: + +signals: + +private: + + /********************************************************************* + * Data + *********************************************************************/ +public: + +protected slots: + +signals: + +private: + + /********************************************************************* + * Functions connections + *********************************************************************/ +public: + +signals: + +private: + + /********************************************************************* + * Load & Save + *********************************************************************/ +public: + bool loadXML(QXmlStreamReader &root); + bool saveXML(QXmlStreamWriter *doc); +}; + +#endif diff --git a/qmlui/virtualconsole/virtualconsole.cpp b/qmlui/virtualconsole/virtualconsole.cpp index 42d4118deb..92664ee582 100644 --- a/qmlui/virtualconsole/virtualconsole.cpp +++ b/qmlui/virtualconsole/virtualconsole.cpp @@ -36,6 +36,7 @@ #include "vcanimation.h" #include "vcaudiotrigger.h" #include "vcxypad.h" +#include "vcspeeddial.h" #include "vcclock.h" #include "vcpage.h" #include "tardis.h" @@ -102,6 +103,7 @@ VirtualConsole::VirtualConsole(QQuickView *view, Doc *doc, qmlRegisterType("org.qlcplus.classes", 1, 0, "VCAnimation"); qmlRegisterType("org.qlcplus.classes", 1, 0, "VCAudioTrigger"); qmlRegisterType("org.qlcplus.classes", 1, 0, "VCXYPad"); + qmlRegisterType("org.qlcplus.classes", 1, 0, "VCSpeedDial"); qmlRegisterType("org.qlcplus.classes", 1, 0, "VCClock"); qmlRegisterType("org.qlcplus.classes", 1, 0, "VCClockSchedule"); qmlRegisterType("org.qlcplus.classes", 1, 0, "VCCueList"); From aa5b3230d696a63b417ef2426619c8cfd508e11b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans-J=C3=BCrgen=20Tappe?= Date: Wed, 27 Mar 2024 18:52:47 +0100 Subject: [PATCH 129/212] Make the XML tags known already in the header files. --- qmlui/qml/virtualconsole/VCAnimationItem.qml | 2 +- .../qml/virtualconsole/VCAudioTriggerItem.qml | 2 +- qmlui/qml/virtualconsole/VCSpeedDialItem.qml | 2 +- qmlui/qml/virtualconsole/VCXYPadItem.qml | 4 ++-- qmlui/virtualconsole/vcanimation.h | 8 ++++++- qmlui/virtualconsole/vcaudiotrigger.cpp | 4 ++-- qmlui/virtualconsole/vcaudiotrigger.h | 2 +- qmlui/virtualconsole/vcspeeddial.h | 23 ++++++++++++++++++- qmlui/virtualconsole/vcxypad.cpp | 6 ++--- qmlui/virtualconsole/vcxypad.h | 17 +++++++++++++- 10 files changed, 56 insertions(+), 14 deletions(-) diff --git a/qmlui/qml/virtualconsole/VCAnimationItem.qml b/qmlui/qml/virtualconsole/VCAnimationItem.qml index 9e9bcfa44a..c855c09ea1 100644 --- a/qmlui/qml/virtualconsole/VCAnimationItem.qml +++ b/qmlui/qml/virtualconsole/VCAnimationItem.qml @@ -51,7 +51,7 @@ VCWidgetItem verticalAlignment: Text.AlignVCenter textFormat: Text.RichText wrapMode: Text.Wrap - text: "Not implemented.
    See QML Status" + text: "VCAnimation not implemented yet.
    See QML Status" onLinkActivated: Qt.openUrlExternally(link) diff --git a/qmlui/qml/virtualconsole/VCAudioTriggerItem.qml b/qmlui/qml/virtualconsole/VCAudioTriggerItem.qml index 58f84e0058..f93186d298 100644 --- a/qmlui/qml/virtualconsole/VCAudioTriggerItem.qml +++ b/qmlui/qml/virtualconsole/VCAudioTriggerItem.qml @@ -51,7 +51,7 @@ VCWidgetItem verticalAlignment: Text.AlignVCenter textFormat: Text.RichText wrapMode: Text.Wrap - text: "Not implemented.
    See QML Status" + text: "VCAudioTrigger not implemented yet.
    See QML Status" onLinkActivated: Qt.openUrlExternally(link) diff --git a/qmlui/qml/virtualconsole/VCSpeedDialItem.qml b/qmlui/qml/virtualconsole/VCSpeedDialItem.qml index d0c37ef017..e653de4c4c 100644 --- a/qmlui/qml/virtualconsole/VCSpeedDialItem.qml +++ b/qmlui/qml/virtualconsole/VCSpeedDialItem.qml @@ -51,7 +51,7 @@ VCWidgetItem verticalAlignment: Text.AlignVCenter textFormat: Text.RichText wrapMode: Text.Wrap - text: "Not implemented.
    See QML Status" + text: "VCSpeedDial not implemented yet.
    See QML Status" onLinkActivated: Qt.openUrlExternally(link) diff --git a/qmlui/qml/virtualconsole/VCXYPadItem.qml b/qmlui/qml/virtualconsole/VCXYPadItem.qml index 6ec1c9fb00..bf3f813f89 100644 --- a/qmlui/qml/virtualconsole/VCXYPadItem.qml +++ b/qmlui/qml/virtualconsole/VCXYPadItem.qml @@ -31,7 +31,7 @@ VCWidgetItem clip: true - onXYPadObjChanged: + onXyPadObjChanged: { setCommonProperties(xyPadObj) } @@ -51,7 +51,7 @@ VCWidgetItem verticalAlignment: Text.AlignVCenter textFormat: Text.RichText wrapMode: Text.Wrap - text: "Not implemented.
    See QML Status" + text: "VCXYPad not implemented yet.
    See QML Status" onLinkActivated: Qt.openUrlExternally(link) diff --git a/qmlui/virtualconsole/vcanimation.h b/qmlui/virtualconsole/vcanimation.h index 2307affe36..bf7259512a 100644 --- a/qmlui/virtualconsole/vcanimation.h +++ b/qmlui/virtualconsole/vcanimation.h @@ -22,7 +22,13 @@ #include "vcwidget.h" -#define KXMLQLCVCAnimation QString("Animation") +#define KXMLQLCVCAnimation QString("Control") +#define KXMLQLCVCAnimationID QString("ID") +#define KXMLQLCVCAnimationType QString("Type") +#define KXMLQLCVCAnimationColor QString("Color") +#define KXMLQLCVCAnimationResource QString("Resource") +#define KXMLQLCVCAnimationProperty QString("Property") +#define KXMLQLCVCAnimationPropertyName QString("Name") class VCAnimation : public VCWidget { diff --git a/qmlui/virtualconsole/vcaudiotrigger.cpp b/qmlui/virtualconsole/vcaudiotrigger.cpp index 5d7cc4a3e9..3e5773bd60 100644 --- a/qmlui/virtualconsole/vcaudiotrigger.cpp +++ b/qmlui/virtualconsole/vcaudiotrigger.cpp @@ -113,7 +113,7 @@ FunctionParent VCAudioTrigger::functionParent() const bool VCAudioTrigger::loadXML(QXmlStreamReader &root) { - if (root.name() != KXMLQLCVCAudioTrigger) + if (root.name() != KXMLQLCVCAudioTriggers) { qWarning() << Q_FUNC_INFO << "Audio trigger node not found"; return false; @@ -152,7 +152,7 @@ bool VCAudioTrigger::saveXML(QXmlStreamWriter *doc) Q_ASSERT(doc != nullptr); /* VC object entry */ - doc->writeStartElement(KXMLQLCVCAudioTrigger); + doc->writeStartElement(KXMLQLCVCAudioTriggers); saveXMLCommon(doc); diff --git a/qmlui/virtualconsole/vcaudiotrigger.h b/qmlui/virtualconsole/vcaudiotrigger.h index 3bdf566f32..c12b3bd7c8 100644 --- a/qmlui/virtualconsole/vcaudiotrigger.h +++ b/qmlui/virtualconsole/vcaudiotrigger.h @@ -22,7 +22,7 @@ #include "vcwidget.h" -#define KXMLQLCVCAudioTrigger QString("Audio Trigger") +#define KXMLQLCVCAudioTriggers QString("AudioTriggers") class VCAudioTrigger : public VCWidget { diff --git a/qmlui/virtualconsole/vcspeeddial.h b/qmlui/virtualconsole/vcspeeddial.h index ef893eadc4..0684b10227 100644 --- a/qmlui/virtualconsole/vcspeeddial.h +++ b/qmlui/virtualconsole/vcspeeddial.h @@ -22,7 +22,28 @@ #include "vcwidget.h" -#define KXMLQLCVCSpeedDial QString("Speed Dial") +#define KXMLQLCVCSpeedDial QString("SpeedDial") +#define KXMLQLCVCSpeedDialSpeedTypes QString("SpeedTypes") +#define KXMLQLCVCSpeedDialAbsoluteValue QString("AbsoluteValue") +#define KXMLQLCVCSpeedDialAbsoluteValueMin QString("Minimum") +#define KXMLQLCVCSpeedDialAbsoluteValueMax QString("Maximum") +#define KXMLQLCVCSpeedDialTap QString("Tap") +#define KXMLQLCVCSpeedDialMult QString("Mult") +#define KXMLQLCVCSpeedDialDiv QString("Div") +#define KXMLQLCVCSpeedDialMultDivReset QString("MultDivReset") +#define KXMLQLCVCSpeedDialApply QString("Apply") +#define KXMLQLCVCSpeedDialTapKey QString("Key") +#define KXMLQLCVCSpeedDialMultKey QString("MultKey") +#define KXMLQLCVCSpeedDialDivKey QString("DivKey") +#define KXMLQLCVCSpeedDialMultDivResetKey QString("MultDivResetKey") +#define KXMLQLCVCSpeedDialApplyKey QString("ApplyKey") +#define KXMLQLCVCSpeedDialResetFactorOnDialChange QString("ResetFactorOnDialChange") +#define KXMLQLCVCSpeedDialVisibilityMask QString("Visibility") +#define KXMLQLCVCSpeedDialTime QString("Time") + +// Legacy: infinite checkbox +#define KXMLQLCVCSpeedDialInfinite QString("Infinite") +#define KXMLQLCVCSpeedDialInfiniteKey QString("InfiniteKey") class VCSpeedDial : public VCWidget { diff --git a/qmlui/virtualconsole/vcxypad.cpp b/qmlui/virtualconsole/vcxypad.cpp index 7ca68b1974..539176aa14 100644 --- a/qmlui/virtualconsole/vcxypad.cpp +++ b/qmlui/virtualconsole/vcxypad.cpp @@ -66,7 +66,7 @@ void VCXYPad::render(QQuickView *view, QQuickItem *parent) m_item = qobject_cast(component->create()); m_item->setParentItem(parent); - m_item->setProperty("XYPadObj", QVariant::fromValue(this)); + m_item->setProperty("xyPadObj", QVariant::fromValue(this)); } QString VCXYPad::propertiesResource() const @@ -113,7 +113,7 @@ FunctionParent VCXYPad::functionParent() const bool VCXYPad::loadXML(QXmlStreamReader &root) { - if (root.name() != KXMLQLCVCXYPAD) + if (root.name() != KXMLQLCVCXYPad) { qWarning() << Q_FUNC_INFO << "XY Pad node not found"; return false; @@ -152,7 +152,7 @@ bool VCXYPad::saveXML(QXmlStreamWriter *doc) Q_ASSERT(doc != nullptr); /* VC object entry */ - doc->writeStartElement(KXMLQLCVCXYPAD); + doc->writeStartElement(KXMLQLCVCXYPad); saveXMLCommon(doc); diff --git a/qmlui/virtualconsole/vcxypad.h b/qmlui/virtualconsole/vcxypad.h index 4d62321e7c..6cd6434eba 100644 --- a/qmlui/virtualconsole/vcxypad.h +++ b/qmlui/virtualconsole/vcxypad.h @@ -22,7 +22,22 @@ #include "vcwidget.h" -#define KXMLQLCVCXYPAD QString("XY Pad") +#define KXMLQLCVCXYPad QString("XYPad") +#define KXMLQLCVCXYPadPan QString("Pan") +#define KXMLQLCVCXYPadTilt QString("Tilt") +#define KXMLQLCVCXYPadWidth QString("Width") +#define KXMLQLCVCXYPadHeight QString("Height") +#define KXMLQLCVCXYPadPosition QString("Position") +#define KXMLQLCVCXYPadRangeWindow QString("Window") +#define KXMLQLCVCXYPadRangeHorizMin QString("hMin") +#define KXMLQLCVCXYPadRangeHorizMax QString("hMax") +#define KXMLQLCVCXYPadRangeVertMin QString("vMin") +#define KXMLQLCVCXYPadRangeVertMax QString("vMax") + +#define KXMLQLCVCXYPadPositionX "X" // Legacy +#define KXMLQLCVCXYPadPositionY "Y" // Legacy + +#define KXMLQLCVCXYPadInvertedAppearance "InvertedAppearance" class VCXYPad : public VCWidget { From 39ad701a5eed739464414405b625cbacb5c61bd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans-J=C3=BCrgen=20Tappe?= Date: Wed, 27 Mar 2024 20:05:26 +0100 Subject: [PATCH 130/212] Load the new elements. --- qmlui/virtualconsole/vcframe.cpp | 60 ++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/qmlui/virtualconsole/vcframe.cpp b/qmlui/virtualconsole/vcframe.cpp index 2e9522c539..2134b3fb8f 100644 --- a/qmlui/virtualconsole/vcframe.cpp +++ b/qmlui/virtualconsole/vcframe.cpp @@ -1086,6 +1086,66 @@ bool VCFrame::loadWidgetXML(QXmlStreamReader &root, bool render) slider->render(m_vc->view(), m_item); } } + else if (root.name() == KXMLQLCVCAnimation) + { + /* Create a new clock into its parent */ + VCAnimation *animation = new VCAnimation(m_doc, this); + if (animation->loadXML(root) == false) + delete animation; + else + { + QQmlEngine::setObjectOwnership(animation, QQmlEngine::CppOwnership); + setupWidget(animation, animation->page()); + m_vc->addWidgetToMap(animation); + if (render && m_item) + animation->render(m_vc->view(), m_item); + } + } + else if (root.name() == KXMLQLCVCAudioTriggers) + { + /* Create a new clock into its parent */ + VCAnimation *animation = new VCAnimation(m_doc, this); + if (animation->loadXML(root) == false) + delete animation; + else + { + QQmlEngine::setObjectOwnership(animation, QQmlEngine::CppOwnership); + setupWidget(animation, animation->page()); + m_vc->addWidgetToMap(animation); + if (render && m_item) + animation->render(m_vc->view(), m_item); + } + } + else if (root.name() == KXMLQLCVCSpeedDial) + { + /* Create a new speedDial into its parent */ + VCSpeedDial *speedDial = new VCSpeedDial(m_doc, this); + if (speedDial->loadXML(root) == false) + delete speedDial; + else + { + QQmlEngine::setObjectOwnership(speedDial, QQmlEngine::CppOwnership); + setupWidget(speedDial, speedDial->page()); + m_vc->addWidgetToMap(speedDial); + if (render && m_item) + speedDial->render(m_vc->view(), m_item); + } + } + else if (root.name() == KXMLQLCVCXYPad) + { + /* Create a new xyPad into its parent */ + VCXYPad *xyPad = new VCXYPad(m_doc, this); + if (xyPad->loadXML(root) == false) + delete xyPad; + else + { + QQmlEngine::setObjectOwnership(xyPad, QQmlEngine::CppOwnership); + setupWidget(xyPad, xyPad->page()); + m_vc->addWidgetToMap(xyPad); + if (render && m_item) + xyPad->render(m_vc->view(), m_item); + } + } else if (root.name() == KXMLQLCVCClock) { /* Create a new clock into its parent */ From 93b32fb57e3a8e8975da8443c1ecfffcb135d3b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans-J=C3=BCrgen=20Tappe?= Date: Wed, 27 Mar 2024 21:11:42 +0100 Subject: [PATCH 131/212] Copy VCXYPad presets (v4) --- ui/src/virtualconsole/vcxypad.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ui/src/virtualconsole/vcxypad.cpp b/ui/src/virtualconsole/vcxypad.cpp index c7fe797cfe..f37031492e 100644 --- a/ui/src/virtualconsole/vcxypad.cpp +++ b/ui/src/virtualconsole/vcxypad.cpp @@ -228,6 +228,13 @@ VCWidget* VCXYPad::createCopy(VCWidget* parent) xypad = NULL; } + for (QHash::iterator it = m_presets.begin(); + it != m_presets.end(); ++it) + { + VCXYPadPreset *preset = it.value(); + xypad->addPreset(*preset); + } + return xypad; } From 9d3e41feba195ada3589dd28a63cfea5c682c2fa Mon Sep 17 00:00:00 2001 From: Olivier Michel Date: Sun, 31 Mar 2024 17:48:50 +0200 Subject: [PATCH 132/212] Add beamZ BAC500 and BAC506 fixtures (#1530) * Create beamZ-BAC500.qxf * Add files via upload * Update beamZ-BAC506.qxf * Update beamZ-BAC506.qxf * Update beamZ-BAC500.qxf * Update beamZ-BAC500.qxf * Update beamZ-BAC506.qxf --------- Co-authored-by: Massimo Callegari --- resources/fixtures/beamZ/beamZ-BAC500.qxf | 110 ++++++++++++++++++++++ resources/fixtures/beamZ/beamZ-BAC506.qxf | 106 +++++++++++++++++++++ 2 files changed, 216 insertions(+) create mode 100644 resources/fixtures/beamZ/beamZ-BAC500.qxf create mode 100644 resources/fixtures/beamZ/beamZ-BAC506.qxf diff --git a/resources/fixtures/beamZ/beamZ-BAC500.qxf b/resources/fixtures/beamZ/beamZ-BAC500.qxf new file mode 100644 index 0000000000..8c627f40ac --- /dev/null +++ b/resources/fixtures/beamZ/beamZ-BAC500.qxf @@ -0,0 +1,110 @@ + + + + + Q Light Controller Plus + 4.13.1 GIT + Olivier Michel + + beamZ + BAC500 + Color Changer + + + + + + Intensity + White + Warm white intensity (0 - 100%) + + + + + + + Colour + No function + Red + Green + Blue + White + Warm white + Amber + UV + Yellow + Magenta + Cyan + Dark orange + Green yellow + Salmon + Turquoise + Light green + Orange + Straw + Lavender + Light blue + Dark blue + Pink + + + Effect + No function + Colour blending (rate) + Colour change (rate) + Sound colour blending (rate) + Sound color change (rate) + + + Effect + No function + White 2700K + White 3200K + White 4300K + White 5600K + White 6500K + White 8000K + + + Red + Green + Blue + + + Dimmer + Strobe + Preset colours + Mode + Colour temperature + + + Red + Green + Blue + White + Warm white + Amber + UV + + + Dimmer + Strobe + Red + Green + Blue + White + Warm white + Amber + UV + Preset colours + Mode + Colour temperature + + + + + + + + + diff --git a/resources/fixtures/beamZ/beamZ-BAC506.qxf b/resources/fixtures/beamZ/beamZ-BAC506.qxf new file mode 100644 index 0000000000..27b1681470 --- /dev/null +++ b/resources/fixtures/beamZ/beamZ-BAC506.qxf @@ -0,0 +1,106 @@ + + + + + Q Light Controller Plus + 4.13.0 + Olivier Michel + + beamZ + BAC506 + Color Changer + + + + + + + + + + Colour + No function + Red + Green + Blue + White + Amber + UV + Yellow + Magenta + Cyan + Dark orange + Green yellow + Salmon + Turquoise + Light green + Orange + Straw + Lavender + Light blue + Dark blue + Pink + + + Effect + No function + Colour blending (rate) + Colour change (rate) + Sound colour blending (rate) + Sound colour change (rate) + + + Effect + No function + White 2700K + White 3200K + White 4300K + White 5600K + White 6500K + White 8000K + + + Dimmer + Preset colours + + + Red + Green + Blue + + + Dimmer + Strobe + Preset colours + Mode + Colour temperature + + + Red + Green + Blue + White + Amber + UV + + + Dimmer + Strobe + Red + Green + Blue + White + Amber + UV + Preset colours + Mode + Colour temperature + + + + + + + + + From 3b1999059b30b8801aa7f3587d415bce5766094b Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Sun, 31 Mar 2024 17:49:42 +0200 Subject: [PATCH 133/212] Update changelog and fixture map --- debian/changelog | 2 ++ resources/fixtures/FixturesMap.xml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/debian/changelog b/debian/changelog index 5f66ee1f21..b16193bf9e 100644 --- a/debian/changelog +++ b/debian/changelog @@ -2,6 +2,8 @@ qlcplus (4.13.1) stable; urgency=low * engine: fix blackout not working * engine: fix RGB Matrix clone control mode + * Virtual Console/XY Pad: copy presets when cloning (thanks to Hans-Jürgen Tappe) + * New fixtures: beamZ BAC500 and BAC506 (thanks to Olivier Michel) -- Massimo Callegari Sun, 30 Jun 2024 12:13:14 +0200 diff --git a/resources/fixtures/FixturesMap.xml b/resources/fixtures/FixturesMap.xml index 183476fe5c..d852e92e14 100644 --- a/resources/fixtures/FixturesMap.xml +++ b/resources/fixtures/FixturesMap.xml @@ -227,7 +227,9 @@ + + From dda182a8b3788fc190ff90a0623e5cfec2900039 Mon Sep 17 00:00:00 2001 From: Maxime Dumas Date: Sun, 31 Mar 2024 20:30:22 -0400 Subject: [PATCH 134/212] qmlui: fix fixture patch address verification --- qmlui/fixturemanager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qmlui/fixturemanager.cpp b/qmlui/fixturemanager.cpp index 5c8f6201d6..0f8294ac86 100644 --- a/qmlui/fixturemanager.cpp +++ b/qmlui/fixturemanager.cpp @@ -274,7 +274,7 @@ bool FixtureManager::addFixture(QString manuf, QString model, QString mode, QStr { Fixture *fxi = new Fixture(m_doc); //quint32 fxAddress = address + (i * channels) + (i * gap); - if (fxAddress + channels >= UNIVERSE_SIZE) + if (fxAddress + channels > UNIVERSE_SIZE) { uniIdx++; if (m_doc->inputOutputMap()->getUniverseID(uniIdx) == m_doc->inputOutputMap()->invalidUniverse()) From 8337821bf7ddf993c700bd03676e02fa17e6cbfe Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Mon, 1 Apr 2024 21:04:14 +0200 Subject: [PATCH 135/212] linux: improve systemd service and startup script --- platforms/linux/qlcplus-start.sh | 9 ++++++++- platforms/linux/qlcplus.service | 4 ++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/platforms/linux/qlcplus-start.sh b/platforms/linux/qlcplus-start.sh index dcef4db730..3d1a1b2616 100644 --- a/platforms/linux/qlcplus-start.sh +++ b/platforms/linux/qlcplus-start.sh @@ -17,7 +17,14 @@ # See the License for the specific language governing permissions and # limitations under the License. -QLCPLUS_OPTS="-platform eglfs --nowm --web --web-auth --operate --overscan" +# detect HDMI plug state +QTPLATFORM="eglfs" +kmsprint -m | grep connected > /dev/null +if [ $? -eq 1 ]; then + QTPLATFORM="offscreen" +fi + +QLCPLUS_OPTS="-platform $QTPLATFORM --nowm --web --web-auth --operate --overscan" if [ ! -f $HOME/.qlcplus/eglfs.json ]; then mkdir -p $HOME/.qlcplus diff --git a/platforms/linux/qlcplus.service b/platforms/linux/qlcplus.service index 6451350d2a..919be83f19 100644 --- a/platforms/linux/qlcplus.service +++ b/platforms/linux/qlcplus.service @@ -1,12 +1,12 @@ [Unit] Description=Q Light Controller Plus Documentation=man:qlcplus(1) -After=network.target +After=basic.target [Service] Type=simple User=pi -Restart=always +Restart=on-failure RestartSec=3 ExecStart=/usr/sbin/qlcplus-start.sh From 22a5798c86aeecd21eeaee28e82bf0041fd8f265 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans-J=C3=BCrgen=20Tappe?= Date: Thu, 4 Apr 2024 17:29:08 +0200 Subject: [PATCH 136/212] Replace onMoved by onValueChanged and remove MouseArea --- qmlui/qml/QLCPlusKnob.qml | 38 +++-------------------- qmlui/qml/virtualconsole/VCSliderItem.qml | 4 +-- 2 files changed, 6 insertions(+), 36 deletions(-) diff --git a/qmlui/qml/QLCPlusKnob.qml b/qmlui/qml/QLCPlusKnob.qml index bec9f03e73..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() @@ -82,38 +84,6 @@ Dial context.stroke() context.closePath() } - - MouseArea - { - anchors.fill: parent - z: 2 - onWheel: { - //console.log("Wheel delta: " + wheel.angleDelta.y) - var from = sliderObj ? sliderObj.rangeLowLimit : 0 - var to = sliderObj ? sliderObj.rangeHighLimit : 255 - var sliderValue = sliderObj ? sliderObj.value : 128 - - if (wheel.angleDelta.y > 0) { - if (sliderObj && sliderValue < to) { - sliderObj.value += 1 - } - } else { - if (sliderObj && sliderValue > from) { - sliderObj.value -= 1 - } - } - } - onPressed: { - mouse.accepted = false - } - onPositionChanged: { - mouse.accepted = false - kCanvas.requestPaint() - } - onReleased: { - mouse.accepted = false - } - } } handle: Rectangle { diff --git a/qmlui/qml/virtualconsole/VCSliderItem.qml b/qmlui/qml/virtualconsole/VCSliderItem.qml index 54586bf84f..3637f70afb 100644 --- a/qmlui/qml/virtualconsole/VCSliderItem.qml +++ b/qmlui/qml/virtualconsole/VCSliderItem.qml @@ -17,7 +17,7 @@ limitations under the License. */ -import QtQuick 2.0 +import QtQuick 2.14 import QtQuick.Layouts 1.1 import org.qlcplus.classes 1.0 @@ -149,7 +149,7 @@ VCWidgetItem to: sliderObj ? sliderObj.rangeHighLimit : 255 value: sliderValue - onMoved: if (sliderObj) sliderObj.value = value // position * 255 + onValueChanged: if (sliderObj) sliderObj.value = value } // widget name text box From d5627cb069c14ab9ba34e75a0957c34593fa26da Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Tue, 9 Apr 2024 11:13:17 +0200 Subject: [PATCH 137/212] Fix build with Qt 6.7 --- engine/test/efxfixture/efxfixture_test.cpp | 1 + main/main.cpp | 2 +- ui/src/consolechannel.cpp | 10 +++++++ ui/src/virtualconsole/vcmatrix.cpp | 2 +- ui/src/virtualconsole/vcwidget.cpp | 33 ++++++++++++++++------ ui/src/virtualconsole/vcwidget.h | 2 +- 6 files changed, 38 insertions(+), 12 deletions(-) diff --git a/engine/test/efxfixture/efxfixture_test.cpp b/engine/test/efxfixture/efxfixture_test.cpp index 09081da611..2da38bb912 100644 --- a/engine/test/efxfixture/efxfixture_test.cpp +++ b/engine/test/efxfixture/efxfixture_test.cpp @@ -30,6 +30,7 @@ #include "qlcfixturemode.h" #include "qlcfixturedef.h" #include "genericfader.h" +#include "fadechannel.h" #include "efxfixture.h" #include "qlcchannel.h" #include "universe.h" diff --git a/main/main.cpp b/main/main.cpp index b788e21355..7e505fe4dc 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -95,7 +95,7 @@ namespace QLCArgs QRect closeButtonRect = QRect(); /** Debug output level */ - QtMsgType debugLevel = QtSystemMsg; + QtMsgType debugLevel = QtCriticalMsg; /** Log to file flag */ bool logToFile = false; diff --git a/ui/src/consolechannel.cpp b/ui/src/consolechannel.cpp index b075b136bb..4fa56b2c39 100644 --- a/ui/src/consolechannel.cpp +++ b/ui/src/consolechannel.cpp @@ -592,7 +592,11 @@ QIcon ConsoleChannel::colorIcon(const QString& name) index = colorList.indexOf(colname); if (index != -1) { +#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0) color.setNamedColor(colname); +#else + color.fromString(colname); +#endif } else { @@ -609,7 +613,13 @@ QIcon ConsoleChannel::colorIcon(const QString& name) QRegularExpression regex(re, QRegularExpression::CaseInsensitiveOption); index = colorList.indexOf(regex); if (index != -1) + { +#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0) color.setNamedColor(colorList.at(index)); +#else + color.fromString(colorList.at(index)); +#endif + } } if (color.isValid() == true) diff --git a/ui/src/virtualconsole/vcmatrix.cpp b/ui/src/virtualconsole/vcmatrix.cpp index e57f81ce40..849dbba346 100644 --- a/ui/src/virtualconsole/vcmatrix.cpp +++ b/ui/src/virtualconsole/vcmatrix.cpp @@ -536,7 +536,7 @@ void VCMatrix::slotUpdate() QString algorithmText; { - QMutexLocker(&matrix->algorithmMutex()); + QMutexLocker locker(&matrix->algorithmMutex()); RGBAlgorithm *algo = matrix->algorithm(); if (algo != NULL) diff --git a/ui/src/virtualconsole/vcwidget.cpp b/ui/src/virtualconsole/vcwidget.cpp index 34c51ccdf9..93404286b6 100644 --- a/ui/src/virtualconsole/vcwidget.cpp +++ b/ui/src/virtualconsole/vcwidget.cpp @@ -978,6 +978,21 @@ bool VCWidget::loadXMLInput(QXmlStreamReader &root, quint32* uni, quint32* ch) c return true; } +QString VCWidget::extraParamToString(QVariant param) +{ + if (param.isValid() == false) + return QString(); + +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + if (param.type() == QVariant::Int && param.toInt() != -1) + return QString::number(param.toInt()); +#else + if (param.metaType().id() == QMetaType::Int && param.toInt() != -1) + return QString::number(param.toInt()); +#endif + return QString(); +} + bool VCWidget::saveXMLCommon(QXmlStreamWriter *doc) { Q_ASSERT(doc != NULL); @@ -1068,20 +1083,20 @@ bool VCWidget::saveXMLInput(QXmlStreamWriter *doc, doc->writeAttribute(KXMLQLCVCWidgetInputMonitorValue, QString::number(src->feedbackValue(QLCInputFeedback::MonitorValue))); // save feedback extra params - QVariant extraParams = src->feedbackExtraParams(QLCInputFeedback::LowerValue); + QString extraParams = extraParamToString(src->feedbackExtraParams(QLCInputFeedback::LowerValue)); - if (extraParams.isValid() && extraParams.type() == QVariant::Int && extraParams.toInt() != -1) - doc->writeAttribute(KXMLQLCVCWidgetInputLowerParams, QString::number(extraParams.toInt())); + if (!extraParams.isEmpty()) + doc->writeAttribute(KXMLQLCVCWidgetInputLowerParams, extraParams); - extraParams = src->feedbackExtraParams(QLCInputFeedback::UpperValue); + extraParams = extraParamToString(src->feedbackExtraParams(QLCInputFeedback::UpperValue)); - if (extraParams.isValid() && extraParams.type() == QVariant::Int && extraParams.toInt() != -1) - doc->writeAttribute(KXMLQLCVCWidgetInputUpperParams, QString::number(extraParams.toInt())); + if (!extraParams.isEmpty()) + doc->writeAttribute(KXMLQLCVCWidgetInputUpperParams, extraParams); - extraParams = src->feedbackExtraParams(QLCInputFeedback::MonitorValue); + extraParams = extraParamToString(src->feedbackExtraParams(QLCInputFeedback::MonitorValue)); - if (extraParams.isValid() && extraParams.type() == QVariant::Int && extraParams.toInt() != -1) - doc->writeAttribute(KXMLQLCVCWidgetInputMonitorParams, QString::number(extraParams.toInt())); + if (!extraParams.isEmpty()) + doc->writeAttribute(KXMLQLCVCWidgetInputMonitorParams, extraParams); doc->writeEndElement(); } diff --git a/ui/src/virtualconsole/vcwidget.h b/ui/src/virtualconsole/vcwidget.h index 66dd28519c..460ef6e35d 100644 --- a/ui/src/virtualconsole/vcwidget.h +++ b/ui/src/virtualconsole/vcwidget.h @@ -536,6 +536,7 @@ protected slots: /** Load input source from $root to $uni and $ch */ bool loadXMLInput(QXmlStreamReader &root, quint32* uni, quint32* ch) const; + static QString extraParamToString(QVariant param); bool saveXMLCommon(QXmlStreamWriter *doc); bool saveXMLAppearance(QXmlStreamWriter *doc); /** Save the defualt input source to $root */ @@ -565,7 +566,6 @@ protected slots: bool loadXMLWindowState(QXmlStreamReader &tag, int* x, int* y, int* w, int* h, bool* visible); - /********************************************************************* * QLC+ Mode change *********************************************************************/ From 6625c96fabb47ee5265f3107ddbc500ed1cf1d44 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Wed, 10 Apr 2024 19:27:25 +0200 Subject: [PATCH 138/212] ui: enable audio of video playback with Qt6 --- ui/src/videoprovider.cpp | 3 +++ ui/src/videoprovider.h | 3 +++ 2 files changed, 6 insertions(+) diff --git a/ui/src/videoprovider.cpp b/ui/src/videoprovider.cpp index fdeef53c7a..47fa5320e1 100644 --- a/ui/src/videoprovider.cpp +++ b/ui/src/videoprovider.cpp @@ -20,6 +20,7 @@ #include #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) #include + #include #endif #include #include @@ -84,6 +85,8 @@ VideoWidget::VideoWidget(Video *video, QObject *parent) m_videoPlayer = new QMediaPlayer(this, QMediaPlayer::VideoSurface); #else m_videoPlayer = new QMediaPlayer(this); + m_audioOutput = new QAudioOutput(this); + m_videoPlayer->setAudioOutput(m_audioOutput); #endif m_videoPlayer->moveToThread(QCoreApplication::instance()->thread()); diff --git a/ui/src/videoprovider.h b/ui/src/videoprovider.h index 748266d396..be53ede371 100644 --- a/ui/src/videoprovider.h +++ b/ui/src/videoprovider.h @@ -60,6 +60,9 @@ protected slots: QMediaPlayer *m_videoPlayer; /** Qt widget that actually displays the video */ QVideoWidget *m_videoWidget; +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + QAudioOutput *m_audioOutput; +#endif private: FunctionParent functionParent() const; From 99bb8dc3eebabe9fae20bb57fce571ae80a6a8f5 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Wed, 10 Apr 2024 19:35:21 +0200 Subject: [PATCH 139/212] engine: improve Show resuming after pause (fix #1538) --- engine/src/chaserrunner.cpp | 4 +- engine/src/showrunner.cpp | 4 +- ui/src/showmanager/showmanager.cpp | 21 +++++++++-- ui/src/showmanager/showmanager.h | 59 ++++++++++++++++-------------- 4 files changed, 53 insertions(+), 35 deletions(-) diff --git a/engine/src/chaserrunner.cpp b/engine/src/chaserrunner.cpp index 3ea2a8a436..ee2a495668 100644 --- a/engine/src/chaserrunner.cpp +++ b/engine/src/chaserrunner.cpp @@ -52,7 +52,7 @@ ChaserRunner::ChaserRunner(const Doc *doc, const Chaser *chaser, quint32 startTi m_pendingAction.m_fadeMode = Chaser::FromFunction; m_pendingAction.m_stepIndex = -1; - if (m_chaser->type() == Function::SequenceType && startTime > 0) + if (startTime > 0) { qDebug() << "[ChaserRunner] startTime:" << startTime; int idx = 0; @@ -577,7 +577,7 @@ void ChaserRunner::startNewStep(int index, MasterTimer *timer, qreal mIntensity, newStep->m_intensityOverrideId = func->requestAttributeOverride(Function::Intensity, mIntensity * sIntensity); } - // Start the fire up ! + // Start the fire up! func->start(timer, functionParent(), 0, newStep->m_fadeIn, newStep->m_fadeOut, func->defaultSpeed(), m_chaser->tempoType()); m_runnerSteps.append(newStep); diff --git a/engine/src/showrunner.cpp b/engine/src/showrunner.cpp index 37a37eaa59..ffe2eb1368 100644 --- a/engine/src/showrunner.cpp +++ b/engine/src/showrunner.cpp @@ -90,11 +90,11 @@ ShowRunner::ShowRunner(const Doc* doc, quint32 showID, quint32 startTime) #if 1 qDebug() << "Ordered list of ShowFunctions (time):"; foreach (ShowFunction *sfunc, m_timeFunctions) - qDebug() << "ID:" << sfunc->functionID() << "st:" << sfunc->startTime() << "dur:" << sfunc->duration(m_doc); + qDebug() << "[Show] Function ID:" << sfunc->functionID() << "start time:" << sfunc->startTime() << "duration:" << sfunc->duration(m_doc); qDebug() << "Ordered list of ShowFunctions (beats):"; foreach (ShowFunction *sfunc, m_beatFunctions) - qDebug() << "ID:" << sfunc->functionID() << "st:" << sfunc->startTime() << "dur:" << sfunc->duration(m_doc); + qDebug() << "[Show] Function ID:" << sfunc->functionID() << "start time:" << sfunc->startTime() << "duration:" << sfunc->duration(m_doc); #endif m_runningQueue.clear(); diff --git a/ui/src/showmanager/showmanager.cpp b/ui/src/showmanager/showmanager.cpp index ea16be7bc5..be0d9df680 100644 --- a/ui/src/showmanager/showmanager.cpp +++ b/ui/src/showmanager/showmanager.cpp @@ -63,6 +63,7 @@ ShowManager::ShowManager(QWidget* parent, Doc* doc) , m_currentEditor(NULL) , m_editorFunctionID(Function::invalidId()) , m_selectedShowIndex(-1) + , cursorMovedDuringPause(false) , m_splitter(NULL) , m_vsplitter(NULL) , m_showview(NULL) @@ -1201,6 +1202,7 @@ void ShowManager::slotStartPlayback() if (m_show->isRunning() == false) { + cursorMovedDuringPause = false; m_show->start(m_doc->masterTimer(), functionParent(), m_showview->getTimeFromCursor()); m_playAction->setIcon(QIcon(":/player_pause.png")); } @@ -1209,7 +1211,17 @@ void ShowManager::slotStartPlayback() if (m_show->isPaused()) { m_playAction->setIcon(QIcon(":/player_pause.png")); - m_show->setPause(false); + if (cursorMovedDuringPause) + { + m_show->stop(functionParent()); + m_show->stopAndWait(); + cursorMovedDuringPause = false; + m_show->start(m_doc->masterTimer(), functionParent(), m_showview->getTimeFromCursor()); + } + else + { + m_show->setPause(false); + } } else { @@ -1338,7 +1350,7 @@ void ShowManager::slotShowItemMoved(ShowItem *item, quint32 time, bool moved) m_doc->setModified(); } -void ShowManager::slotupdateTimeAndCursor(quint32 msec_time) +void ShowManager::slotUpdateTimeAndCursor(quint32 msec_time) { //qDebug() << Q_FUNC_INFO << "time: " << msec_time; slotUpdateTime(msec_time); @@ -1369,6 +1381,9 @@ void ShowManager::slotUpdateTime(quint32 msec_time) .arg(s, 2, 10, QChar('0')).arg(msec_time / 10, 2, 10, QChar('0')); m_timeLabel->setText(str); + + if (m_show != NULL && m_show->isPaused()) + cursorMovedDuringPause = true; } void ShowManager::slotTrackClicked(Track *track) @@ -1636,7 +1651,7 @@ void ShowManager::updateMultiTrackView() m_timeDivisionCombo->setCurrentIndex(tIdx); connect(m_bpmField, SIGNAL(valueChanged(int)), this, SLOT(slotBPMValueChanged(int))); - connect(m_show, SIGNAL(timeChanged(quint32)), this, SLOT(slotupdateTimeAndCursor(quint32))); + connect(m_show, SIGNAL(timeChanged(quint32)), this, SLOT(slotUpdateTimeAndCursor(quint32))); connect(m_show, SIGNAL(showFinished()), this, SLOT(slotStopPlayback())); connect(m_show, SIGNAL(stopped(quint32)), this, SLOT(slotShowStopped())); diff --git a/ui/src/showmanager/showmanager.h b/ui/src/showmanager/showmanager.h index a19d60e424..822ce39475 100644 --- a/ui/src/showmanager/showmanager.h +++ b/ui/src/showmanager/showmanager.h @@ -74,19 +74,19 @@ class ShowManager : public QWidget void hideEvent(QHideEvent* ev); protected: - static ShowManager* s_instance; + static ShowManager *s_instance; - Doc* m_doc; + Doc *m_doc; /** Currently selected show */ - Show* m_show; + Show *m_show; /** Currently selected track */ Track *m_currentTrack; /** Currently selected scene */ - Scene* m_currentScene; + Scene *m_currentScene; /** Scene editor instance reference */ - QWidget* m_sceneEditor; + QWidget *m_sceneEditor; /** Right editor instance reference (can edit Chaser, Audio, Video) */ - QWidget* m_currentEditor; + QWidget *m_currentEditor; /** ID of the Function currently edited on the right */ quint32 m_editorFunctionID; @@ -94,14 +94,17 @@ class ShowManager : public QWidget * (basically the m_showsCombo index) */ int m_selectedShowIndex; + /** Track if cursor is interactively being moved during pause */ + bool cursorMovedDuringPause; + private: void showSceneEditor(Scene *scene); void hideRightEditor(); void showRightEditor(Function *function); private: - QSplitter* m_splitter; // main view splitter (horizontal) - QSplitter* m_vsplitter; // multitrack view splitter (vertical) + QSplitter *m_splitter; // main view splitter (horizontal) + QSplitter *m_vsplitter; // multitrack view splitter (vertical) MultiTrackView *m_showview; /********************************************************************* @@ -116,25 +119,25 @@ class ShowManager : public QWidget private: bool checkOverlapping(quint32 startTime, quint32 duration); - QToolBar* m_toolbar; - QComboBox* m_showsCombo; - QLabel* m_timeLabel; - QAction* m_addShowAction; - QAction* m_addTrackAction; - QAction* m_addSequenceAction; - QAction* m_addAudioAction; - QAction* m_addVideoAction; - QAction* m_copyAction; - QAction* m_pasteAction; - QAction* m_deleteAction; - QAction* m_colorAction; - QAction* m_lockAction; - QAction* m_timingsAction; - QAction* m_snapGridAction; - QAction* m_stopAction; - QAction* m_playAction; - QComboBox* m_timeDivisionCombo; - QSpinBox* m_bpmField; + QToolBar *m_toolbar; + QComboBox *m_showsCombo; + QLabel *m_timeLabel; + QAction *m_addShowAction; + QAction *m_addTrackAction; + QAction *m_addSequenceAction; + QAction *m_addAudioAction; + QAction *m_addVideoAction; + QAction *m_copyAction; + QAction *m_pasteAction; + QAction *m_deleteAction; + QAction *m_colorAction; + QAction *m_lockAction; + QAction *m_timingsAction; + QAction *m_snapGridAction; + QAction *m_stopAction; + QAction *m_playAction; + QComboBox *m_timeDivisionCombo; + QSpinBox *m_bpmField; protected slots: /** Slot called when the user selects a show from @@ -176,7 +179,7 @@ protected slots: void slotShowItemMoved(ShowItem *item, quint32 time, bool moved); void slotUpdateTime(quint32 msec_time); - void slotupdateTimeAndCursor(quint32 msec_time); + void slotUpdateTimeAndCursor(quint32 msec_time); void slotTrackClicked(Track *track); void slotTrackDoubleClicked(Track *track); void slotTrackMoved(Track *track, int direction); From d3cf81642dab1a993caac0716af3f4890f981bf9 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Wed, 10 Apr 2024 19:54:17 +0200 Subject: [PATCH 140/212] Revert "Merge pull request #1527 from Foul/patch-1" This reverts commit 7a0ae2ddc97498355d6b99239153376e6d032a0e, reversing changes made to 9cec7ac61c982ccf4955ba08a735b3edf5435e9f. --- qmlui/qlcplus_fr_FR.ts | 454 ++++++++++++++++++++--------------------- 1 file changed, 226 insertions(+), 228 deletions(-) diff --git a/qmlui/qlcplus_fr_FR.ts b/qmlui/qlcplus_fr_FR.ts index 5f2cc1a297..86d213814b 100644 --- a/qmlui/qlcplus_fr_FR.ts +++ b/qmlui/qlcplus_fr_FR.ts @@ -42,7 +42,7 @@ Your project has changes - Votre projet a été modifié + Votre projet comporte des modifications @@ -63,7 +63,7 @@ Les modifications seront perdues si vous ne les enregistrez pas. Open file - Ouvrir fichier + Ouvrir @@ -103,12 +103,12 @@ Les modifications seront perdues si vous ne les enregistrez pas. DMX Address tool - Outil d'Adressage DMX + Outil d'adressage DMX UI Settings - Paramètres UI + @@ -178,7 +178,7 @@ Les modifications seront perdues si vous ne les enregistrez pas. About - À Propos + À propos
    @@ -191,17 +191,17 @@ Les modifications seront perdues si vous ne les enregistrez pas. New Scene - Nouvelle Scène + Nouvelle scène New Chaser - Nouveau Chaser + Nouveau chaser New Sequence - Nouvelle Séquence + Nouvelle séquence @@ -211,22 +211,22 @@ Les modifications seront perdues si vous ne les enregistrez pas. New Collection - Nouvelle Collection + Nouvelle collection New RGB Matrix - Nouvelle Matrice RVB + Nouvelle matrice RVB New Show - Nouveau Spectacle + Nouveau spectacle New Script - Nouveau Script + Nouveau script @@ -236,7 +236,7 @@ Les modifications seront perdues si vous ne les enregistrez pas. New Video - Nouvelle Vidéo + Nouvelle piste vidéo @@ -299,7 +299,7 @@ Les modifications seront perdues si vous ne les enregistrez pas. Volume - Volume + @@ -317,7 +317,7 @@ Les modifications seront perdues si vous ne les enregistrez pas. Global Audio - Audio Global + Audio global @@ -340,7 +340,7 @@ Les modifications seront perdues si vous ne les enregistrez pas. Projected diameter - Diamètre projeté + @@ -348,7 +348,7 @@ Les modifications seront perdues si vous ne les enregistrez pas. Expand/Collapse this panel - Déplier/Replier ce panneau + Déplier/replier ce panneau @@ -357,7 +357,7 @@ Les modifications seront perdues si vous ne les enregistrez pas. Custom - Personnalisé + Personnalisée @@ -365,17 +365,17 @@ Les modifications seront perdues si vous ne les enregistrez pas. Open a picture file - Ouvrir un fichier image + Gobo pictures - Images Gobo + All files - Tous les fichiers + Tous les fichiers @@ -385,7 +385,7 @@ Les modifications seront perdues si vous ne les enregistrez pas. Preset - Préréglage + @@ -416,22 +416,22 @@ Les modifications seront perdues si vous ne les enregistrez pas. Delete the selected capabilities - Supprimer les fonctionnalités sélectionnées + Capability wizard - Assistant de fonctionnalités + Empty description provided - Description vide fournie + Description vide Overlapping with another capability - Chevauchement avec une autre capacité + Superposition avec une autre ... @@ -456,7 +456,7 @@ Les modifications seront perdues si vous ne les enregistrez pas. Primary color - Couleur primaire + Couleur principale @@ -504,17 +504,17 @@ Les modifications seront perdues si vous ne les enregistrez pas. Preview the previous step - Prévisualisation de l'étape précédente + Preview the next step - Prévisualiser l'étape suivante + Duplicate the selected step(s) - Dupliquer les étapes sélectionnées + @@ -604,12 +604,12 @@ Les modifications seront perdues si vous ne les enregistrez pas. Fade In - Fondu en Entrée + Fondu en entrée Fade Out - Fondu en Sortie + Fondu en sortie @@ -627,7 +627,7 @@ Les modifications seront perdues si vous ne les enregistrez pas. Fade In - Fondu en Entrée + Fondu en entrée @@ -637,7 +637,7 @@ Les modifications seront perdues si vous ne les enregistrez pas. Fade Out - Fondu en Sortie + Fondu en sortie @@ -866,27 +866,27 @@ Les modifications seront perdues si vous ne les enregistrez pas. Fixtures - Luminaires + Appareils Add a fixture/head - Ajouter un Luminaires/tête + Ajouter un appareil/tête Remove the selected fixture head(s) - Supprimer la ou les têtes de luminaire sélectionnées + Supprimer la ou les têtes sélectionnées Fixture - Luminaire + Appareil Mode - Mode + Mode @@ -902,22 +902,22 @@ Les modifications seront perdues si vous ne les enregistrez pas. Position - Position + Position Dimmer - Variateur + RGB - RVB + Add a new fixture - Ajouter un nouvel luminaire + Ajouter un nouvel appareil @@ -1105,7 +1105,7 @@ Les modifications seront perdues si vous ne les enregistrez pas. Channel wizard - Assistant de canal + @@ -1205,12 +1205,12 @@ Les modifications seront perdues si vous ne les enregistrez pas. Create a new fixture definition Add a new fixture definition - Créer une nouvelle définition de luminaire + Créer une nouvelle définition d'appareil Edit the selected fixture definition - Modifier la définition du luminaire sélectionné + Modifier la définition d'appareil sélectionnée @@ -1241,13 +1241,13 @@ Les modifications seront perdues si vous ne les enregistrez pas. Open a fixture definition - Ouvrir une définition de luminaire + Ouvrir une définition d'appareil Fixture definition files - Fichiers de définition de luminaire + Fichiers de définition d'appareil @@ -1355,12 +1355,12 @@ Les modifications seront perdues si vous ne les enregistrez pas. Error - Erreur + Erreur Add a new fixture group - Ajouter un nouveau groupe de luminaires + Ajouter un nouveau groupe d'appareils @@ -1370,7 +1370,7 @@ Les modifications seront perdues si vous ne les enregistrez pas. Set a Group/Fixture/Channel search filter - Définir un filtre de recherche Groupe/Luminaire/Canal + Définir un filtre de recherche de groupe/appareil/canaux @@ -1390,12 +1390,12 @@ Les modifications seront perdues si vous ne les enregistrez pas. Toggle fixtures and channels properties - Basculer les propriétés des luminaires et des canaux + Basculer les propriétés des appareils et des canaux Add/Remove a linked fixture - Ajouter/supprimer un luminaire lié + Ajouter/supprimer un appareil lié @@ -1405,7 +1405,7 @@ Les modifications seront perdues si vous ne les enregistrez pas. Mode - Mode + Mode @@ -1458,17 +1458,17 @@ Les modifications seront perdues si vous ne les enregistrez pas. Show/Hide this fixture - Afficher/Masquer ce luminaire + Invert Pan - Inverse Pan + Invert Tilt - Inverse Tilt + @@ -1476,7 +1476,7 @@ Les modifications seront perdues si vous ne les enregistrez pas. Fixture properties - Propriétés du luminaire + Propriétés de l'appareil @@ -1519,7 +1519,7 @@ Les modifications seront perdues si vous ne les enregistrez pas. Print the fixture summary - Afficher le récapitulatif du luminaire + Afficher la description de l'appareil @@ -1622,12 +1622,12 @@ Les modifications seront perdues si vous ne les enregistrez pas. Pan degrees - Degrés pan + Degrés de pan Tilt degrees - Degrés Tilt + Degrés de tilt @@ -1773,7 +1773,7 @@ Les modifications seront perdues si vous ne les enregistrez pas. New RGB Matrix - Nouvelle Matrice RVB + Nouvelle matrice RGB @@ -1909,111 +1909,110 @@ Les modifications seront perdues si vous ne les enregistrez pas. !! Warning !! - !! Attention !! + !! Attention !! Channel wizard activated - Assistant de canal activé + 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. - Vous avez activé l'assistant de canal d'entrée. Après avoir cliqué sur OK, remuez les commandes de votre profil d'entrée mappé. Ils devraient apparaître dans la liste. Cliquez à nouveau sur le bouton de l'assistant pour arrêter la détection automatique des canaux.<br><br>Notez que l'assistant ne peut pas faire la différence entre un bouton et un curseur, vous devrez donc effectuer la modification manuellement. + Unsaved changes - Modifications non enregistrées + Do you wish to save the current profile first? Changes will be lost if you don't save them. - Souhaitez-vous d'abord enregistrer le profil actuel ? -Les modifications seront perdues si vous ne les enregistrez pas. + Manufacturer - Fabricant + Fabricant Model - Modèle + Modèle Type - Type + Type MIDI Global Settings - Paramètres globaux MIDI + When MIDI notes are used, send a Note Off when value is 0 - Lorsque des notes MIDI sont utilisées, envoyez une Note Off lorsque la valeur est 0 + Channel - Canal + Canal Name - Nom + Nom Behaviour - Comportement + Comportement Generate an extra Press/Release when toggled - Génère un appui/relâchement supplémentaire lorsqu'il est alterné + Movement - Mouvement + Sensitivity - Sensibilité + Custom Feedback - Retour personnalisé + Lower value - Valeur inférieure + Upper value - Valeur supérieure + Button %1 - Bouton %1 + Bouton %1 Slider %1 - Fader %1 + Fader %1 @@ -2060,12 +2059,12 @@ Les modifications seront perdues si vous ne les enregistrez pas. Add Fixtures - Ajouter des luminaires + Ajouter des appareils Fixture Groups - Groupes de luminaires + Groupes d'appareils @@ -2120,7 +2119,7 @@ Les modifications seront perdues si vous ne les enregistrez pas. Select/Deselect all fixtures - Sélectionner/désélectionner tous les luminaires + Sélectionner/désélectionner tous les appareils @@ -2133,7 +2132,7 @@ Les modifications seront perdues si vous ne les enregistrez pas. Fixtures & Functions - Luminaires & fonctions + Appareils & fonctions @@ -2163,7 +2162,7 @@ Les modifications seront perdues si vous ne les enregistrez pas. Stop all the running functions - Arrêtez toutes les fonctions en cours d'exécution + @@ -2226,7 +2225,7 @@ Les modifications seront perdues si vous ne les enregistrez pas. Create a new emitter - Créer un nouvel émetteur + @@ -2236,22 +2235,22 @@ Les modifications seront perdues si vous ne les enregistrez pas. Drop channels here - Déposez les canaux ici + Acts on - Agit sur + Emitters - Émetteurs + Remove the selected emitter(s) - Supprimer le(s) émetteur(s) sélectionné(s) + @@ -2352,32 +2351,32 @@ Les modifications seront perdues si vous ne les enregistrez pas. X Ascending - X Ascendant + X Descending - X Descendant + Y Ascending - Y Ascendant + Y Descending - Y Descendant + Z Ascending - Z Ascendant + Z Descending - Z Descendant + @@ -2400,7 +2399,7 @@ Les modifications seront perdues si vous ne les enregistrez pas. Search a palette - Rechercher une palette + @@ -2465,27 +2464,27 @@ Les modifications seront perdues si vous ne les enregistrez pas. Min Degrees - Degrés Min + Max Degrees - Degrés Max + Head(s) - Tête(s) + Pan Max Degrees - Max Degrés Pan + Tilt Max Degrees - Max Degrés.Tilt + @@ -2522,7 +2521,7 @@ Les modifications seront perdues si vous ne les enregistrez pas. Electrical - Électrique + @@ -2568,42 +2567,42 @@ Les modifications seront perdues si vous ne les enregistrez pas. Channel Modifiers Editor - Éditeur de modificateurs de canaux + Insert a modified value after the selected - Insérer une valeur modifiée après la valeur sélectionnée + Delete the selected modifier value - Supprimer la valeur du modificateur sélectionné + Rename the selected modifier template - Renommer le modèle de modificateur sélectionné + Save the selected modifier template - Enregistrer le modèle de modificateur sélectionné + Templates - Modèles + Original DMX value - Valeur DMX originale + Modified DMX value - Valeur DMX modifiée + @@ -2611,132 +2610,132 @@ Les modifications seront perdues si vous ne les enregistrez pas. Fixture Editor Wizard - Assistant Editeur de Luminaires + Properties - Propriétés + Start - Démarrer + Width - Largeur + Largeur Amount - Quantité + Quantité Type - Type + Type Red - Rouge + Rouge Green - Vert + Vert Blue - Bleu + Bleu White - Blanc + Blanc Amber - Ambre + Ambre UV - UV + UV RGB - RVB + RGBW - RVBW + RGBAW - RVBAW + Dimmer - Variateur + Pan - Pan + Pan Tilt - Tilt + Tilt Color Macro - Macro de couleur + Shutter - Obturateur + Obturateur Beam - Rayon + Rayon Effect - Effet + Effet Label - Étiquette + Label Capability # - Aptitude # + Channel # - Canal # + Preview - Prévisualisation + Prévisualisation @@ -2749,7 +2748,7 @@ Les modifications seront perdues si vous ne les enregistrez pas. Dimmer - Variateur + @@ -2789,7 +2788,7 @@ Les modifications seront perdues si vous ne les enregistrez pas. Also create a Scene - Créez également une scène + @@ -2908,7 +2907,7 @@ Les modifications seront perdues si vous ne les enregistrez pas. Fixtures - Luminaires + Appareils @@ -2921,47 +2920,47 @@ Les modifications seront perdues si vous ne les enregistrez pas. Input Channel Editor - Editeur de canal d'entrée + Input Channel - Canal d'entrée + Number - Nombre + Nombre Name - Nom + Nom Type - Type + Type Channel - Canal + Canal Message - Message + Parameter - Paramètre + Note - Note + Note @@ -3112,7 +3111,7 @@ Niveau d'accès : Fixture/Group editing - Édition de Luminaires/Groupes + Édition du groupe/appareil @@ -3307,47 +3306,47 @@ Niveau d'accès : !! Warning !! - !! Attention !! + !! Attention !! Save this profile - Enregistrer ce profil + Toggle the automatic detection procedure - Basculer la procédure de détection automatique + Add a new channel - Ajouter un canal + Ajouter un canal Create a new input profile - Créer un nouveau profil d'entrée + Edit the selected channel - Modifier le canal sélectionné + Edit the selected input profile - Modifier le profil d'entrée sélectionné + Delete the selected channel - Supprimer le canal sélectionné + Delete the selected input profile(s) - Supprimer le(s) profil(s) d'entrée sélectionné(s) + @@ -3355,7 +3354,7 @@ Niveau d'accès : Fixture Group - Groupe de luminaires + Groupe d'appareils @@ -3415,7 +3414,7 @@ Niveau d'accès : Dimmer - Variateur + @@ -3762,7 +3761,7 @@ Niveau d'accès : Add a fixture/group - Ajouter un luminaire/groupe + Ajouter un appareil/groupe @@ -3816,7 +3815,7 @@ Niveau d'accès : Show/hide fixture tree - Afficher/masquer l'arborescence des luminaires + Afficher/masquer l'arbre d'appareils @@ -3841,7 +3840,7 @@ Niveau d'accès : Set fixture channel - Définir le canal du luminaire + Définir le canal de l'appareil @@ -3889,7 +3888,7 @@ Niveau d'accès : Remove the selected fixtures - Supprimer les luminaires sélectionnés + Supprimer les appareils sélectionnés @@ -3899,7 +3898,7 @@ Niveau d'accès : Fixtures - Luminaires + Appareils @@ -3967,22 +3966,22 @@ Niveau d'accès : Custom Background - Arrière-plan personnalisé + Select an image - Sélectionner une image + Sélectionner une image Reset background - Réinitialiser l'arrière-plan + Selected fixtures - Luminaires sélectionnés + Appareils sélectionnés @@ -4129,7 +4128,7 @@ Niveau d'accès : Normalize the selected items - Normaliser les éléments sélectionnés + Actions @@ -4323,7 +4322,7 @@ Niveau d'accès : Fixture List - Liste de luminaires + Liste d'appareils @@ -4363,147 +4362,147 @@ Niveau d'accès : Reset to default - Réinitialisation aux valeurs par défaut + Scaling factor - Facteur d'échelle + Background darker - Arrière-plan plus sombre + Background dark - Arrière-plan sombre + Background medium - Arrière-plan moyen + Background light - Arrière-plan clair + Background lighter - Arrière-plan plus plus clair + Controls background - Arrière-plan des contrôles + Foreground main - Avant-Plan principal + Foreground medium - Avant-Plan moyen + Foreground light - Avant-Plan clair + Toolbar gradient start - Début du dégradé de la barre d'outils + Sub-toolbar gradient start - Début du dégradé de la barre d'outils + Toolbar gradient end - Fin du dégradé de la barre d'outils + Toolbar hover gradient start - Début du dégradé de survol de la barre d'outils + Toolbar hover gradient end - Fin du dégradé du survol de la barre d'outils + Toolbar selection - Sélection barre d'outils + Sub-toolbar selection - Sélection de la barre d'outils secondaire + Section header - En-tête de section + Section header divider - Séparateur d'en-tête de section + Item highlight - Élément mis en surbrillance + Item highlight pressed - Élément mis en surbrillance enfoncé + Item hover - Survol d'un élément + Item selection - Sélection d'un élément + VC Frame drop area - Zone de dépôt de trame VC + Item dark border - Bordure sombre de l'élément + Save to file - Enregistrer dans un fichier + Operation completed - Opération terminée + Error - Erreur + Erreur @@ -4511,24 +4510,23 @@ Niveau d'accès : Error - Erreur + Erreur Unable to perform the operation. There is either not enough space or the target universe in invalid - Impossible d'effectuer l'opération. -Il n'y a pas assez d'espace ou l'univers cible n'est pas valide + Cut the selected items into clipboard - Couper les éléments sélectionnés dans le presse-papiers + Paste items in the clipboard at the first available position - Coller les éléments du presse-papiers à la première position disponible + @@ -4540,12 +4538,12 @@ Il n'y a pas assez d'espace ou l'univers cible n'est pas val Enable/Disable passthrough - Activer/Désactiver l'intermédaire + Enable/Disable feedbacks - Activer/désactiver les retours d'informations + Activer/désactiver le retour d'informations @@ -4839,12 +4837,12 @@ Il n'y a pas assez d'espace ou l'univers cible n'est pas val Next Cue - Cue Suivante + Cue suivante Previous Cue - Cue Précédente + Cue précédente @@ -4867,7 +4865,7 @@ Il n'y a pas assez d'espace ou l'univers cible n'est pas val Side Fader - Fader Latéral + Faders sur le flanc @@ -4885,7 +4883,7 @@ Il n'y a pas assez d'espace ou l'univers cible n'est pas val Play/Stop - Lecture/Pause + @@ -4970,7 +4968,7 @@ Il n'y a pas assez d'espace ou l'univers cible n'est pas val Side fader - Fader latéral + Faders sur le flanc @@ -4998,12 +4996,12 @@ Il n'y a pas assez d'espace ou l'univers cible n'est pas val Next Page - Page Suivante + Page suivante Previous Page - Page Précédente + Page précédente @@ -5023,7 +5021,7 @@ Il n'y a pas assez d'espace ou l'univers cible n'est pas val Page %1 - Page %1 + Page %1 @@ -5031,7 +5029,7 @@ Il n'y a pas assez d'espace ou l'univers cible n'est pas val Expand/Collapse this frame - Déplier/Replier ce cadre + Déplier/replier ce cadre @@ -5099,12 +5097,12 @@ Il n'y a pas assez d'espace ou l'univers cible n'est pas val Shortcuts - Raccourcis + Shortcut name - Nom du raccourci + @@ -5112,7 +5110,7 @@ Il n'y a pas assez d'espace ou l'univers cible n'est pas val Label %1 - Étiquette %1 + Label %1 @@ -5216,7 +5214,7 @@ Il n'y a pas assez d'espace ou l'univers cible n'est pas val Delete selected widgets - Supprimer les widgets sélectionnés + Delete functions @@ -5452,7 +5450,7 @@ Il n'y a pas assez d'espace ou l'univers cible n'est pas val Label - Étiquette + Label @@ -5507,7 +5505,7 @@ Il n'y a pas assez d'espace ou l'univers cible n'est pas val Label - Étiquette + Label @@ -5853,7 +5851,7 @@ Il n'y a pas assez d'espace ou l'univers cible n'est pas val Label - Étiquette + Label From b3106c8fde3996a9dc50ad5f2e86e81ed11426ca Mon Sep 17 00:00:00 2001 From: kompot Date: Thu, 11 Apr 2024 14:10:20 +0200 Subject: [PATCH 141/212] Add new fixtures --- .../American_DJ/American-DJ-Par-Z4.qxf | 86 +++++++ .../fixtures/OXO/OXO-ColorBeam-7-FCW-IR.qxf | 71 ++++++ .../Pro-Lights/Pro-Lights-Pixie-Spot.qxf | 220 ++++++++++++++++++ resources/fixtures/beamZ/beamZ-SB400.qxf | 49 ++++ 4 files changed, 426 insertions(+) create mode 100644 resources/fixtures/American_DJ/American-DJ-Par-Z4.qxf create mode 100644 resources/fixtures/OXO/OXO-ColorBeam-7-FCW-IR.qxf create mode 100644 resources/fixtures/Pro-Lights/Pro-Lights-Pixie-Spot.qxf create mode 100644 resources/fixtures/beamZ/beamZ-SB400.qxf diff --git a/resources/fixtures/American_DJ/American-DJ-Par-Z4.qxf b/resources/fixtures/American_DJ/American-DJ-Par-Z4.qxf new file mode 100644 index 0000000000..7843683074 --- /dev/null +++ b/resources/fixtures/American_DJ/American-DJ-Par-Z4.qxf @@ -0,0 +1,86 @@ + + + + + Q Light Controller Plus + 4.12.7 + kompot + + American DJ + Par Z4 + Color Changer + + + + + + Shutter + LED OFF + LED ON + STROBING SLOW - FAST + LED ON + SLOW OPEN - FAST CLOSE + LED ON + SLOW CLOSE - FAST OPEN + LED ON + RANDOM STROBE SLOW - FAST + LED ON + + + + Effect + STANDARD + STAGE + TV + ARCHITECTURAL + THEATRE + DEFAULT DIMMER SETTING + + + + Intensity + 7200K - 3200K + + + Red + Green + Blue + White + + + Red + Green + Blue + White + Shutter/Strobe + Master dimmer + + + Red + Green + Blue + White + Color macro + Shutter/Strobe + Master dimmer + Dimmer Curves + + + Red + Green + Blue + White + Color macro + Shutter/Strobe + Master dimmer + Dimmer Curves + Color Temp + + + + + + + + + diff --git a/resources/fixtures/OXO/OXO-ColorBeam-7-FCW-IR.qxf b/resources/fixtures/OXO/OXO-ColorBeam-7-FCW-IR.qxf new file mode 100644 index 0000000000..4fa39e8135 --- /dev/null +++ b/resources/fixtures/OXO/OXO-ColorBeam-7-FCW-IR.qxf @@ -0,0 +1,71 @@ + + + + + Q Light Controller Plus + 4.12.7 + kompot + + OXO + ColorBeam 7 FCW IR + Color Changer + + Effect + 35 colors setting + black + + + Intensity + luminance + + + + + + + Speed + Static + + + Intensity + No out / RGB/Strobe / 35 colors macro mode + Light mode + Dim mode + Light and Dim mode + Auto mix color mode + 3 colors strobe + 7 colors strobe + 7 colors +audio control / Light changes with sound value + + + + Macro + + + Macro + Luminance + + + Red + Green + Blue + White + + + Red + Green + Blue + White + Macro + Speed/Strobe + Mode + Master dimmer + + + + + + + + + diff --git a/resources/fixtures/Pro-Lights/Pro-Lights-Pixie-Spot.qxf b/resources/fixtures/Pro-Lights/Pro-Lights-Pixie-Spot.qxf new file mode 100644 index 0000000000..e344bb8926 --- /dev/null +++ b/resources/fixtures/Pro-Lights/Pro-Lights-Pixie-Spot.qxf @@ -0,0 +1,220 @@ + + + + + Q Light Controller Plus + 4.12.7 + kompot + + Pro-Lights + Pixie Spot + Moving Head + + + + + + + Intensity + No function + Reset (Hold 3 Seconds) + No function + + + + + + + Colour + No function + Red + Yellow + Green + Cyan + Blue + Magenta + White + Full + Clockwise rotation (Fast to Slow) + Stop Run + Counterclockwise rotation (Slow to Fast) + + + Gobo + Gobo wheel presets + Gobo 1 + Gobo 2 + Gobo 3 + Gobo 4 + Gobo 5 + Gobo 6 + Gobo 7 + Gobo 1 Shaking + Gobo 2 Shaking + Gobo 3 Shaking + Gobo 4 Shaking + Gobo 5 Shaking + Gobo 6 Shaking + Gobo 7 Shaking + Clockwise rotation (Fast to Slow) + Stop Rotation + Counterclockwise rotation (Slow to Fast) + + + Gobo + Stop Rotation + Clockwise rotation (Fast to Slow) + Stop Rotation + Counterclockwise rotation (Slow to Fast) + + + Prism + Prism Off + Prism On + Clockwise rotation (Fast to Slow) + Stop Rotation + Counterclockwise rotation (Slow to Fast) + + + Shutter + Shutter Closed + No Function (Shutter open) + Strobe Effect (Slow to Fast) + No function (Shutter open) + Pulse-effect in sequence + No function (Shutter open) + Random Strobe Effect (Slow to Fast) + No Function (Shutter open) + + + + + Colour + No Function + R:100% / G:0~100% / B:0 / W:0 + R:100%~0 / G:100% / B:0 / W:0 + R:0 / G:100% / B:0~100% / W:0 + R:0 / G:100%~0 / B:100% / W:0 + R:0~100% / G:0 / B:100% / W:0 + R:100% / G:0 / B:100%~0 / W:0 + R:100% / G:0~100% / B:0~100% / W:0 + R:100%~0 / G:100%~0 / B:100% / W:0 + R:100% / G:100% / B:100% / W:100% + Color 1 + Color 2 + Color 3 + Color 4 + Color 5 + Color 6 + Color 7 + Color 8 + Color 9 + Color 10 + Color 11 + + + Effect + No Function + 4 Colours Snap + 4 Colours Fade + 15 Colours Snap + 15 Colours Fade + Sound Control + + + Speed + Speed (Slow to Fast) / Sound Sensitivity OFF + Control the Sound Sensitivity + + + Effect + No Function + Motor Show 1 + Motor Show 2 + Motor Show 3 + Motor Show 4 + Motor Show 5 (Motor Show 1 - 4) + Motor Show 6 + + + Speed + Speed (Slow to Fast) + + + Effect + Preset dimmer speed from display menu + Dimmer speed mode off + Dimmer speed mode 1 + Dimmer speed mode 2 + Dimmer speed mode 3 + + + Pan + Pan fine + Tilt + Tilt fine + Pan speed + Special Function + Red + Green + Blue + White + Gobo wheel + Gobo Rotation + Prism rotation + Strobe + Focus + + + Pan + Pan fine + Tilt + Tilt fine + Pan speed + Special Function + Red + Green + Blue + White + Color wheel + Gobo wheel + Gobo Rotation + Prism rotation + Strobe + Focus + Dimmer + Dimmer Speed Mode + + + Pan + Pan fine + Tilt + Tilt fine + Pan speed + Special Function + Red + Green + Blue + White + Color wheel + Gobo wheel + Gobo Rotation + Prism rotation + Strobe + Focus + Dimmer + Color macro + Auto Program + Auto Program Speed + Motor Show + Motor Show Speed + Dimmer Speed Mode + + + + + + + + + diff --git a/resources/fixtures/beamZ/beamZ-SB400.qxf b/resources/fixtures/beamZ/beamZ-SB400.qxf new file mode 100644 index 0000000000..de5690f502 --- /dev/null +++ b/resources/fixtures/beamZ/beamZ-SB400.qxf @@ -0,0 +1,49 @@ + + + + + Q Light Controller Plus + 4.12.7 + kompot + + beamZ + SB400 + Strobe + + + Shutter + No function + Strobe + + + Effect + No function + Macro function + No function + + + Speed + Macro function speed adjustable + + + + + + + Master dimmer + Strobe + Macro + Macro Speed + Led 1 + Led 2 + Led 3 + Led 4 + + + + + + + + + From 58d4ee220b21660c1fbcded7dd9e8b8f64fdfaa7 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Thu, 11 Apr 2024 21:27:49 +0200 Subject: [PATCH 142/212] engine: include relative EFX in blackout (fix #1523) --- debian/changelog | 2 ++ engine/src/universe.cpp | 2 ++ 2 files changed, 4 insertions(+) diff --git a/debian/changelog b/debian/changelog index b16193bf9e..7031162727 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,7 +1,9 @@ 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 Manage: improve resume after pause * Virtual Console/XY Pad: copy presets when cloning (thanks to Hans-Jürgen Tappe) * New fixtures: beamZ BAC500 and BAC506 (thanks to Olivier Michel) diff --git a/engine/src/universe.cpp b/engine/src/universe.cpp index 65bd93d29b..eceeeee709 100644 --- a/engine/src/universe.cpp +++ b/engine/src/universe.cpp @@ -965,6 +965,7 @@ bool Universe::writeRelative(int address, quint32 value, int channelCount) 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 @@ -978,6 +979,7 @@ bool Universe::writeRelative(int address, quint32 value, int channelCount) 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); } } From 137bd41f06633c04b2affd5cd97c89285029d3a8 Mon Sep 17 00:00:00 2001 From: kompot Date: Fri, 12 Apr 2024 15:29:52 +0200 Subject: [PATCH 143/212] Add dimensions, weight and power consumption --- .../fixtures/American_DJ/American-DJ-Par-Z4.qxf | 10 +++++----- resources/fixtures/OXO/OXO-ColorBeam-7-FCW-IR.qxf | 11 +++++------ .../fixtures/Pro-Lights/Pro-Lights-Pixie-Spot.qxf | 14 +++++++------- resources/fixtures/beamZ/beamZ-SB400.qxf | 6 +++--- 4 files changed, 20 insertions(+), 21 deletions(-) diff --git a/resources/fixtures/American_DJ/American-DJ-Par-Z4.qxf b/resources/fixtures/American_DJ/American-DJ-Par-Z4.qxf index 7843683074..823605cb6e 100644 --- a/resources/fixtures/American_DJ/American-DJ-Par-Z4.qxf +++ b/resources/fixtures/American_DJ/American-DJ-Par-Z4.qxf @@ -3,7 +3,7 @@ Q Light Controller Plus - 4.12.7 + 4.12.3 kompot American DJ @@ -77,10 +77,10 @@ Color Temp - - - + + + - + diff --git a/resources/fixtures/OXO/OXO-ColorBeam-7-FCW-IR.qxf b/resources/fixtures/OXO/OXO-ColorBeam-7-FCW-IR.qxf index 4fa39e8135..cf170d1a48 100644 --- a/resources/fixtures/OXO/OXO-ColorBeam-7-FCW-IR.qxf +++ b/resources/fixtures/OXO/OXO-ColorBeam-7-FCW-IR.qxf @@ -3,7 +3,7 @@ Q Light Controller Plus - 4.12.7 + 4.12.3 kompot OXO @@ -34,8 +34,7 @@ Auto mix color mode 3 colors strobe 7 colors strobe - 7 colors -audio control / Light changes with sound value + 7 colors audio control / Light changes with sound value @@ -62,10 +61,10 @@ audio control / Light changes with sound value Master dimmer - - + + - + diff --git a/resources/fixtures/Pro-Lights/Pro-Lights-Pixie-Spot.qxf b/resources/fixtures/Pro-Lights/Pro-Lights-Pixie-Spot.qxf index e344bb8926..48c51c7d9e 100644 --- a/resources/fixtures/Pro-Lights/Pro-Lights-Pixie-Spot.qxf +++ b/resources/fixtures/Pro-Lights/Pro-Lights-Pixie-Spot.qxf @@ -3,7 +3,7 @@ Q Light Controller Plus - 4.12.7 + 4.12.3 kompot Pro-Lights @@ -141,7 +141,7 @@ Speed (Slow to Fast) - Effect + Speed Preset dimmer speed from display menu Dimmer speed mode off Dimmer speed mode 1 @@ -211,10 +211,10 @@ Dimmer Speed Mode - - - - - + + + + + diff --git a/resources/fixtures/beamZ/beamZ-SB400.qxf b/resources/fixtures/beamZ/beamZ-SB400.qxf index de5690f502..f0b4f975bd 100644 --- a/resources/fixtures/beamZ/beamZ-SB400.qxf +++ b/resources/fixtures/beamZ/beamZ-SB400.qxf @@ -3,7 +3,7 @@ Q Light Controller Plus - 4.12.7 + 4.12.3 kompot beamZ @@ -41,9 +41,9 @@ - + - + From f5271059c0659606898c8eec80bac0680509b090 Mon Sep 17 00:00:00 2001 From: kompot Date: Sun, 14 Apr 2024 21:41:34 +0200 Subject: [PATCH 144/212] Change strobe to preset --- resources/fixtures/beamZ/beamZ-SB400.qxf | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/resources/fixtures/beamZ/beamZ-SB400.qxf b/resources/fixtures/beamZ/beamZ-SB400.qxf index f0b4f975bd..6470d05437 100644 --- a/resources/fixtures/beamZ/beamZ-SB400.qxf +++ b/resources/fixtures/beamZ/beamZ-SB400.qxf @@ -10,11 +10,7 @@ SB400 Strobe - - Shutter - No function - Strobe - + Effect No function From e720c6eb9706071a3e5fbdd7cde61adf1e27fb3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20M=C3=BCllner?= Date: Sat, 20 Apr 2024 21:46:56 +0200 Subject: [PATCH 145/212] Fix build issues with recent GCC releases MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Building qlcplus (upstream/master) with GCC 13.2.1 (Fedora 39) triggers build errors when using the following commands: ``` mkdir build cd build cmake .. make ``` The first error is: ``` [...] [ 10%] Linking CXX shared library libqlcplusengine.so /usr/bin/ld: ../audio/src/libqlcplusaudio.a(audio.cpp.o): relocation R_X86_64_32 against symbol `_ZTV5Audio' can not be used when making a shared object; recompile with -fPIC /usr/bin/ld: failed to set dynamic section sizes: bad value collect2: error: ld returned 1 exit status ``` The second error is: ``` [...] [ 46%] Linking CXX shared library libqlcplusengine.so /usr/bin/ld: ../../hotplugmonitor/src/libhotplugmonitor.a(hotplugmonitor.cpp.o): relocation R_X86_64_32 against symbol `_ZTV14HotPlugMonitor' can not be used when making a shared object; recompile with -fPIC /usr/bin/ld: failed to set dynamic section sizes: bad value collect2: error: ld returned 1 exit status ``` Both errors are caused by linking a static library without position-independent code into a shared object. This patch address the build errors by enabling position-independent code generation for the two static libararies. Signed-off-by: Christoph Müllner --- engine/audio/src/CMakeLists.txt | 1 + hotplugmonitor/src/CMakeLists.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/engine/audio/src/CMakeLists.txt b/engine/audio/src/CMakeLists.txt index 9aa7dd7bc4..cd51c39823 100644 --- a/engine/audio/src/CMakeLists.txt +++ b/engine/audio/src/CMakeLists.txt @@ -9,6 +9,7 @@ add_library(${module_name} audioplugincache.cpp audioplugincache.h audiorenderer.cpp audiorenderer.h ) +set_property(TARGET ${module_name} PROPERTY POSITION_INDEPENDENT_CODE ON) target_include_directories(${module_name} PUBLIC ../../../plugins/interfaces ../../src diff --git a/hotplugmonitor/src/CMakeLists.txt b/hotplugmonitor/src/CMakeLists.txt index 72ed66d7e6..957d313f63 100644 --- a/hotplugmonitor/src/CMakeLists.txt +++ b/hotplugmonitor/src/CMakeLists.txt @@ -1,6 +1,7 @@ add_library(hotplugmonitor hotplugmonitor.cpp hotplugmonitor.h ) +set_property(TARGET hotplugmonitor PROPERTY POSITION_INDEPENDENT_CODE ON) target_link_libraries(hotplugmonitor PUBLIC Qt${QT_MAJOR_VERSION}::Core From 91206c27bb99380c17453a8ce8057e1192e0b6a3 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Sun, 21 Apr 2024 10:24:44 +0200 Subject: [PATCH 146/212] resources: fix and improve #1549 --- debian/changelog | 3 +- .../American_DJ/American-DJ-Par-Z4.qxf | 34 ++++---- resources/fixtures/FixturesMap.xml | 4 + .../fixtures/OXO/OXO-ColorBeam-7-FCW-IR.qxf | 18 ++-- .../Pro-Lights/Pro-Lights-Pixie-Spot.qxf | 84 +++++++++---------- resources/fixtures/beamZ/beamZ-SB400.qxf | 22 ++--- 6 files changed, 85 insertions(+), 80 deletions(-) diff --git a/debian/changelog b/debian/changelog index 7031162727..d10acff9fb 100644 --- a/debian/changelog +++ b/debian/changelog @@ -3,9 +3,10 @@ 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 Manage: improve resume after pause + * Show Manager: improve resume after pause * Virtual Console/XY Pad: copy presets when cloning (thanks to Hans-Jürgen Tappe) * 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) -- Massimo Callegari Sun, 30 Jun 2024 12:13:14 +0200 diff --git a/resources/fixtures/American_DJ/American-DJ-Par-Z4.qxf b/resources/fixtures/American_DJ/American-DJ-Par-Z4.qxf index 823605cb6e..7503d12243 100644 --- a/resources/fixtures/American_DJ/American-DJ-Par-Z4.qxf +++ b/resources/fixtures/American_DJ/American-DJ-Par-Z4.qxf @@ -3,8 +3,8 @@ Q Light Controller Plus - 4.12.3 - kompot + 4.13.1 GIT + Dmitry Kolesnikov American DJ Par Z4 @@ -15,16 +15,16 @@ Shutter - LED OFF - LED ON - STROBING SLOW - FAST - LED ON - SLOW OPEN - FAST CLOSE - LED ON - SLOW CLOSE - FAST OPEN - LED ON - RANDOM STROBE SLOW - FAST - LED ON + LED OFF + LED ON + STROBING SLOW - FAST + LED ON + SLOW OPEN - FAST CLOSE + LED ON + SLOW CLOSE - FAST OPEN + LED ON + RANDOM STROBE SLOW - FAST + LED ON @@ -38,16 +38,16 @@ - Intensity + Effect 7200K - 3200K - + Red Green Blue White - + Red Green Blue @@ -55,7 +55,7 @@ Shutter/Strobe Master dimmer - + Red Green Blue @@ -65,7 +65,7 @@ Master dimmer Dimmer Curves - + Red Green Blue diff --git a/resources/fixtures/FixturesMap.xml b/resources/fixtures/FixturesMap.xml index d852e92e14..db667a69bf 100644 --- a/resources/fixtures/FixturesMap.xml +++ b/resources/fixtures/FixturesMap.xml @@ -128,6 +128,7 @@ + @@ -244,6 +245,7 @@ + @@ -1308,6 +1310,7 @@ + @@ -1353,6 +1356,7 @@ + diff --git a/resources/fixtures/OXO/OXO-ColorBeam-7-FCW-IR.qxf b/resources/fixtures/OXO/OXO-ColorBeam-7-FCW-IR.qxf index cf170d1a48..c83a5320ea 100644 --- a/resources/fixtures/OXO/OXO-ColorBeam-7-FCW-IR.qxf +++ b/resources/fixtures/OXO/OXO-ColorBeam-7-FCW-IR.qxf @@ -3,8 +3,8 @@ Q Light Controller Plus - 4.12.3 - kompot + 4.13.1 GIT + Dmitry Kolesnikov OXO ColorBeam 7 FCW IR @@ -14,8 +14,8 @@ 35 colors setting + black - Intensity - luminance + Effect + Luminance @@ -26,7 +26,7 @@ Static - Intensity + Effect No out / RGB/Strobe / 35 colors macro mode Light mode Dim mode @@ -37,20 +37,20 @@ 7 colors audio control / Light changes with sound value - + Macro - + Macro Luminance - + Red Green Blue White - + Red Green Blue diff --git a/resources/fixtures/Pro-Lights/Pro-Lights-Pixie-Spot.qxf b/resources/fixtures/Pro-Lights/Pro-Lights-Pixie-Spot.qxf index 48c51c7d9e..96018dbd2e 100644 --- a/resources/fixtures/Pro-Lights/Pro-Lights-Pixie-Spot.qxf +++ b/resources/fixtures/Pro-Lights/Pro-Lights-Pixie-Spot.qxf @@ -3,8 +3,8 @@ Q Light Controller Plus - 4.12.3 - kompot + 4.13.1 GIT + Dmitry Kolesnikov Pro-Lights Pixie Spot @@ -13,11 +13,11 @@ - + - Intensity + Maintenance No function - Reset (Hold 3 Seconds) + Reset (Hold 3 Seconds) No function @@ -27,17 +27,17 @@ Colour No function - Red - Yellow - Green - Cyan - Blue - Magenta - White + Red + Yellow + Green + Cyan + Blue + Magenta + White Full - Clockwise rotation (Fast to Slow) - Stop Run - Counterclockwise rotation (Slow to Fast) + Clockwise rotation (Fast to Slow) + Stop Run + Counterclockwise rotation (Slow to Fast) Gobo @@ -62,32 +62,32 @@ Gobo - Stop Rotation - Clockwise rotation (Fast to Slow) - Stop Rotation - Counterclockwise rotation (Slow to Fast) + Stop Rotation + Clockwise rotation (Fast to Slow) + Stop Rotation + Counterclockwise rotation (Slow to Fast) Prism - Prism Off - Prism On - Clockwise rotation (Fast to Slow) - Stop Rotation - Counterclockwise rotation (Slow to Fast) + Prism Off + Prism On + Clockwise rotation (Fast to Slow) + Stop Rotation + Counterclockwise rotation (Slow to Fast) Shutter - Shutter Closed - No Function (Shutter open) - Strobe Effect (Slow to Fast) - No function (Shutter open) - Pulse-effect in sequence - No function (Shutter open) - Random Strobe Effect (Slow to Fast) - No Function (Shutter open) + Shutter Closed + No Function (Shutter open) + Strobe Effect (Slow to Fast) + No function (Shutter open) + Pulse-effect in sequence + No function (Shutter open) + Random Strobe Effect (Slow to Fast) + No Function (Shutter open) - + Colour No Function @@ -148,29 +148,29 @@ Dimmer speed mode 2 Dimmer speed mode 3 - + Pan Pan fine Tilt Tilt fine - Pan speed + Pan/Tilt speed Special Function Red Green Blue White Gobo wheel - Gobo Rotation + Gobo Rotation Prism rotation Strobe Focus - + Pan Pan fine Tilt Tilt fine - Pan speed + Pan/Tilt speed Special Function Red Green @@ -178,19 +178,19 @@ White Color wheel Gobo wheel - Gobo Rotation + Gobo Rotation Prism rotation Strobe Focus Dimmer Dimmer Speed Mode - + Pan Pan fine Tilt Tilt fine - Pan speed + Pan/Tilt speed Special Function Red Green @@ -198,7 +198,7 @@ White Color wheel Gobo wheel - Gobo Rotation + Gobo Rotation Prism rotation Strobe Focus diff --git a/resources/fixtures/beamZ/beamZ-SB400.qxf b/resources/fixtures/beamZ/beamZ-SB400.qxf index 6470d05437..5baaf2dd46 100644 --- a/resources/fixtures/beamZ/beamZ-SB400.qxf +++ b/resources/fixtures/beamZ/beamZ-SB400.qxf @@ -3,8 +3,8 @@ Q Light Controller Plus - 4.12.3 - kompot + 4.13.1 GIT + Dmitry Kolesnikov beamZ SB400 @@ -21,19 +21,19 @@ Speed Macro function speed adjustable - - - - - + + + + + Master dimmer Strobe Macro Macro Speed - Led 1 - Led 2 - Led 3 - Led 4 + LED 1 + LED 2 + LED 3 + LED 4 From 0368413fffb33370c0072f80e2507c8d676e8ce7 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Sun, 21 Apr 2024 10:50:46 +0200 Subject: [PATCH 147/212] resources: fix fixture schema corrupted in #1549 --- resources/fixtures/OXO/OXO-ColorBeam-7-FCW-IR.qxf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/fixtures/OXO/OXO-ColorBeam-7-FCW-IR.qxf b/resources/fixtures/OXO/OXO-ColorBeam-7-FCW-IR.qxf index c83a5320ea..dc0a1f689f 100644 --- a/resources/fixtures/OXO/OXO-ColorBeam-7-FCW-IR.qxf +++ b/resources/fixtures/OXO/OXO-ColorBeam-7-FCW-IR.qxf @@ -65,6 +65,6 @@ - + From 82722bbdaac44029d0a765ff5695d903df3d5dcd Mon Sep 17 00:00:00 2001 From: LilyCalla Date: Sun, 21 Apr 2024 01:55:22 -0700 Subject: [PATCH 148/212] improved UI scaling in ui editor and main ui --- qmlui/qml/ActionsMenu.qml | 3 +++ qmlui/qml/KeyPad.qml | 2 +- qmlui/qml/MainView.qml | 21 ++++++++++++++----- qmlui/qml/UISettingsEditor.qml | 37 ++++++++++++++++++++++++++++++++-- 4 files changed, 55 insertions(+), 8 deletions(-) diff --git a/qmlui/qml/ActionsMenu.qml b/qmlui/qml/ActionsMenu.qml index cc0f53c6bf..3d43d00fb4 100644 --- a/qmlui/qml/ActionsMenu.qml +++ b/qmlui/qml/ActionsMenu.qml @@ -161,6 +161,7 @@ Popup border.width: 1 border.color: UISettings.bgStronger color: UISettings.bgStrong + height: actionsMenuEntries.height } Column @@ -307,6 +308,7 @@ Popup ContextMenuEntry { Layout.fillWidth: true + Layout.fillHeight: true imgSource: "qrc:/undo.svg" entryText: qsTr("Undo") onEntered: submenuItem = null @@ -320,6 +322,7 @@ Popup ContextMenuEntry { Layout.fillWidth: true + Layout.fillHeight: true imgSource: "qrc:/redo.svg" entryText: qsTr("Redo") onEntered: submenuItem = null diff --git a/qmlui/qml/KeyPad.qml b/qmlui/qml/KeyPad.qml index 217e1db541..28005b6b8d 100644 --- a/qmlui/qml/KeyPad.qml +++ b/qmlui/qml/KeyPad.qml @@ -36,7 +36,7 @@ Rectangle property bool showTapButton: false property alias commandString: commandBox.text - property real itemHeight: Math.max(UISettings.iconSizeDefault, keyPadRoot.height / keyPadGrid.rows) - 3 + property real itemHeight: UISettings.iconSizeDefault - 3 //needed for bpm tapping property double tapTimeValue: 0 diff --git a/qmlui/qml/MainView.qml b/qmlui/qml/MainView.qml index 58ef1fc426..5204b48b1a 100644 --- a/qmlui/qml/MainView.qml +++ b/qmlui/qml/MainView.qml @@ -144,6 +144,7 @@ Rectangle MenuBarEntry { id: actEntry + Layout.alignment: Qt.AlignTop imgSource: "qrc:/qlcplus.svg" entryText: qsTr("Actions") onPressed: actionsMenu.open() @@ -165,6 +166,7 @@ Rectangle { id: fnfEntry property string ctxName: "FIXANDFUNC" + Layout.alignment: Qt.AlignTop property string ctxRes: "qrc:/FixturesAndFunctions.qml" imgSource: "qrc:/editor.svg" @@ -180,6 +182,7 @@ Rectangle MenuBarEntry { id: vcEntry + Layout.alignment: Qt.AlignTop property string ctxName: "VC" property string ctxRes: "qrc:/VirtualConsole.qml" @@ -201,6 +204,7 @@ Rectangle MenuBarEntry { id: sdEntry + Layout.alignment: Qt.AlignTop property string ctxName: "SDESK" property string ctxRes: "qrc:/SimpleDesk.qml" @@ -222,6 +226,7 @@ Rectangle MenuBarEntry { id: smEntry + Layout.alignment: Qt.AlignTop property string ctxName: "SHOWMGR" property string ctxRes: "qrc:/ShowManager.qml" @@ -243,6 +248,7 @@ Rectangle MenuBarEntry { id: ioEntry + Layout.alignment: Qt.AlignTop property string ctxName: "IOMGR" property string ctxRes: "qrc:/InputOutputManager.qml" @@ -265,7 +271,7 @@ Rectangle { // acts like an horizontal spacer Layout.fillWidth: true - height: parent.height + implicitHeight: parent.height color: "transparent" } RobotoText @@ -273,6 +279,9 @@ Rectangle label: "BPM: " + (ioManager.bpmNumber > 0 ? ioManager.bpmNumber : qsTr("Off")) color: gsMouseArea.containsMouse ? UISettings.bgLight : "transparent" fontSize: UISettings.textSizeDefault + Layout.alignment: Qt.AlignTop + implicitWidth: width + implicitHeight: parent.height MouseArea { @@ -294,8 +303,9 @@ Rectangle Rectangle { id: beatIndicator - width: height - height: parent.height * 0.5 + implicitWidth: height + implicitHeight: parent.height * 0.5 + Layout.alignment: Qt.AlignVCenter radius: height / 2 border.width: 2 border.color: "#333" @@ -324,8 +334,9 @@ Rectangle IconButton { id: stopAllButton - width: UISettings.iconSizeDefault - height: UISettings.iconSizeDefault + implicitWidth: UISettings.iconSizeDefault + implicitHeight: UISettings.iconSizeDefault + Layout.alignment: Qt.AlignTop enabled: runningCount ? true : false bgColor: "transparent" imgSource: "qrc:/stop.svg" diff --git a/qmlui/qml/UISettingsEditor.qml b/qmlui/qml/UISettingsEditor.qml index 664a6b9595..9b049b0263 100644 --- a/qmlui/qml/UISettingsEditor.qml +++ b/qmlui/qml/UISettingsEditor.qml @@ -30,13 +30,17 @@ Rectangle anchors.fill: parent color: "transparent" - property real origItemHeight: UISettings.listItemHeight - property real origIconMedium: UISettings.iconSizeMedium + property real origItemHeight: { origItemHeight = UISettings.listItemHeight } + property real origIconMedium: { origIconMedium = UISettings.iconSizeMedium } + property real origTextSizeDefault: { origTextSizeDefault = UISettings.textSizeDefault} + property real origIconDefault: {origIconDefault = UISettings.iconSizeDefault} onVisibleChanged: { + console.log("visibility change"); origItemHeight = UISettings.listItemHeight origIconMedium = UISettings.iconSizeMedium + origTextSizeDefault = UISettings.textSizeDefault sfRestore.origScaleFactor = qlcplus.uiScaleFactor } @@ -135,6 +139,7 @@ Rectangle RobotoText { height: origItemHeight + fontSize: origTextSizeDefault label: qsTr("Scaling factor") } RowLayout @@ -181,7 +186,9 @@ Rectangle RobotoText { height: origItemHeight + fontSize: origTextSizeDefault label: qsTr("Background darker") + onHeightChanged: console.log("Row 2 height: " + height); } Loader { @@ -199,7 +206,9 @@ Rectangle RobotoText { height: origItemHeight + fontSize: origTextSizeDefault label: qsTr("Background dark") + onHeightChanged: console.log("text height changed " + height) } Loader { @@ -218,6 +227,7 @@ Rectangle RobotoText { height: origItemHeight + fontSize: origTextSizeDefault label: qsTr("Background medium") } Loader @@ -236,6 +246,7 @@ Rectangle RobotoText { height: origItemHeight + fontSize: origTextSizeDefault label: qsTr("Background light") } Loader @@ -255,6 +266,7 @@ Rectangle RobotoText { height: origItemHeight + fontSize: origTextSizeDefault label: qsTr("Background lighter") } Loader @@ -273,6 +285,7 @@ Rectangle RobotoText { height: origItemHeight + fontSize: origTextSizeDefault label: qsTr("Controls background") } Loader @@ -292,6 +305,7 @@ Rectangle RobotoText { height: origItemHeight + fontSize: origTextSizeDefault label: qsTr("Foreground main") } Loader @@ -310,6 +324,7 @@ Rectangle RobotoText { height: origItemHeight + fontSize: origTextSizeDefault label: qsTr("Foreground medium") } Loader @@ -329,6 +344,7 @@ Rectangle RobotoText { height: origItemHeight + fontSize: origTextSizeDefault label: qsTr("Foreground light") } Loader @@ -347,6 +363,7 @@ Rectangle RobotoText { height: origItemHeight + fontSize: origTextSizeDefault label: qsTr("Toolbar gradient start") } Loader @@ -366,6 +383,7 @@ Rectangle RobotoText { height: origItemHeight + fontSize: origTextSizeDefault label: qsTr("Sub-toolbar gradient start") } Loader @@ -384,6 +402,7 @@ Rectangle RobotoText { height: origItemHeight + fontSize: origTextSizeDefault label: qsTr("Toolbar gradient end") } Loader @@ -403,6 +422,7 @@ Rectangle RobotoText { height: origItemHeight + fontSize: origTextSizeDefault label: qsTr("Toolbar hover gradient start") } Loader @@ -422,6 +442,7 @@ Rectangle RobotoText { height: origItemHeight + fontSize: origTextSizeDefault label: qsTr("Toolbar hover gradient end") } Loader @@ -441,6 +462,7 @@ Rectangle RobotoText { height: origItemHeight + fontSize: origTextSizeDefault label: qsTr("Toolbar selection") } Loader @@ -460,6 +482,7 @@ Rectangle RobotoText { height: origItemHeight + fontSize: origTextSizeDefault label: qsTr("Sub-toolbar selection") } Loader @@ -479,6 +502,7 @@ Rectangle RobotoText { height: origItemHeight + fontSize: origTextSizeDefault label: qsTr("Section header") } Loader @@ -497,6 +521,7 @@ Rectangle RobotoText { height: origItemHeight + fontSize: origTextSizeDefault label: qsTr("Section header divider") } Loader @@ -516,6 +541,7 @@ Rectangle RobotoText { height: origItemHeight + fontSize: origTextSizeDefault label: qsTr("Item highlight") } Loader @@ -534,6 +560,7 @@ Rectangle RobotoText { height: origItemHeight + fontSize: origTextSizeDefault label: qsTr("Item highlight pressed") } Loader @@ -553,6 +580,7 @@ Rectangle RobotoText { height: origItemHeight + fontSize: origTextSizeDefault label: qsTr("Item hover") } Loader @@ -571,6 +599,7 @@ Rectangle RobotoText { height: origItemHeight + fontSize: origTextSizeDefault label: qsTr("Item selection") } Loader @@ -590,6 +619,7 @@ Rectangle RobotoText { height: origItemHeight + fontSize: origTextSizeDefault label: qsTr("VC Frame drop area") } Loader @@ -608,6 +638,7 @@ Rectangle RobotoText { height: origItemHeight + fontSize: origTextSizeDefault label: qsTr("Item dark border") } Loader @@ -628,6 +659,8 @@ Rectangle Layout.columnSpan: 4 Layout.alignment: Qt.AlignHCenter width: origIconMedium * 10 + height: origIconDefault + fontSize: origTextSizeDefault label: qsTr("Save to file") onClicked: { From de4fc77849097012a172d0cc2fd139c1b8f4b6c8 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Sun, 21 Apr 2024 11:56:00 +0200 Subject: [PATCH 149/212] qmlui: bump controls version --- qmlui/qml/fixturesfunctions/FixtureProperties.qml | 2 +- qmlui/qml/fixturesfunctions/RGBPanelProperties.qml | 1 + qmlui/qml/virtualconsole/qmldir | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/qmlui/qml/fixturesfunctions/FixtureProperties.qml b/qmlui/qml/fixturesfunctions/FixtureProperties.qml index 8633e93f17..e6a18e6cf4 100644 --- a/qmlui/qml/fixturesfunctions/FixtureProperties.qml +++ b/qmlui/qml/fixturesfunctions/FixtureProperties.qml @@ -18,7 +18,7 @@ */ import QtQuick 2.3 -import QtQuick.Controls 2.1 +import QtQuick.Controls 2.14 import QtQuick.Layouts 1.1 import "." diff --git a/qmlui/qml/fixturesfunctions/RGBPanelProperties.qml b/qmlui/qml/fixturesfunctions/RGBPanelProperties.qml index c6251b69a9..bfcbee19fd 100644 --- a/qmlui/qml/fixturesfunctions/RGBPanelProperties.qml +++ b/qmlui/qml/fixturesfunctions/RGBPanelProperties.qml @@ -18,6 +18,7 @@ */ import QtQuick 2.3 +import QtQuick.Controls 2.14 import QtQuick.Layouts 1.1 import org.qlcplus.classes 1.0 diff --git a/qmlui/qml/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 From 8e695ef306f1a0d81a78099bff0de0b6a53ac0d0 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Sun, 21 Apr 2024 11:56:56 +0200 Subject: [PATCH 150/212] qmlui: fix crash when adding a RGB panel on unavailable address (fix #1532) --- qmlui/fixturemanager.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/qmlui/fixturemanager.cpp b/qmlui/fixturemanager.cpp index 0f8294ac86..0cb0f0a2bd 100644 --- a/qmlui/fixturemanager.cpp +++ b/qmlui/fixturemanager.cpp @@ -1054,6 +1054,9 @@ void FixtureManager::slotFixtureAdded(quint32 id, QVector3D pos) else { Fixture *fixture = m_doc->fixture(id); + if (fixture == nullptr) + return; + QStringList uniNames = m_doc->inputOutputMap()->universeNames(); QString universeName = uniNames.at(fixture->universe()); int matchMask = 0; From 37b79941ee3be1a5e402ca914babdfd09b04e05e Mon Sep 17 00:00:00 2001 From: LilyCalla Date: Sun, 21 Apr 2024 10:52:17 -0700 Subject: [PATCH 151/212] Prevent multiplier text from overflowing width --- qmlui/qml/UISettingsEditor.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/qmlui/qml/UISettingsEditor.qml b/qmlui/qml/UISettingsEditor.qml index 9b049b0263..52d82dfff3 100644 --- a/qmlui/qml/UISettingsEditor.qml +++ b/qmlui/qml/UISettingsEditor.qml @@ -158,6 +158,7 @@ Rectangle RobotoText { height: origItemHeight + fontSize: origTextSizeDefault label: sfSlider.value.toFixed(2) + "x" } From b945dd24be6970b56f984ae873a82379b23cea74 Mon Sep 17 00:00:00 2001 From: LilyCalla Date: Sun, 21 Apr 2024 11:15:52 -0700 Subject: [PATCH 152/212] prevent About popup text overflow --- qmlui/qml/popup/PopupAbout.qml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/qmlui/qml/popup/PopupAbout.qml b/qmlui/qml/popup/PopupAbout.qml index 41b4fa9deb..5282488cbe 100644 --- a/qmlui/qml/popup/PopupAbout.qml +++ b/qmlui/qml/popup/PopupAbout.qml @@ -57,6 +57,8 @@ CustomPopupDialog " " + qsTr("Apache 2.0 license") + "." onLinkActivated: Qt.openUrlExternally(link) + Layout.fillWidth: true + wrapMode: Text.WordWrap MouseArea { From 502d30914ea35b5cbc9c2c1242ee44b13a59949c Mon Sep 17 00:00:00 2001 From: LilyCalla Date: Sun, 21 Apr 2024 14:00:43 -0700 Subject: [PATCH 153/212] fixed ratcheting of beat generator keypad --- qmlui/qml/BeatGeneratorsPanel.qml | 1 + qmlui/qml/KeyPad.qml | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/qmlui/qml/BeatGeneratorsPanel.qml b/qmlui/qml/BeatGeneratorsPanel.qml index f67496d3af..2e9acfa967 100644 --- a/qmlui/qml/BeatGeneratorsPanel.qml +++ b/qmlui/qml/BeatGeneratorsPanel.qml @@ -154,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/KeyPad.qml b/qmlui/qml/KeyPad.qml index 28005b6b8d..ffa0f51c0e 100644 --- a/qmlui/qml/KeyPad.qml +++ b/qmlui/qml/KeyPad.qml @@ -34,9 +34,9 @@ Rectangle property bool showDMXcontrol: true property bool showTapButton: false - + property alias commandString: commandBox.text - property real itemHeight: UISettings.iconSizeDefault - 3 + property real itemHeight: Math.max(UISettings.iconSizeDefault, keyPadRoot.height / keyPadGrid.rows) - 3 //needed for bpm tapping property double tapTimeValue: 0 @@ -109,15 +109,15 @@ Rectangle else { var currTime = new Date().getTime() - + if (lastTap != 0 && currTime - lastTap < 1500) { var newTime = currTime - lastTap - + tapHistory.push(newTime) tapTimeValue = TimeUtils.calculateBPMByTapIntervals(tapHistory) - + keyPadRoot.tapTimeChanged(tapTimeValue) tapTimer.interval = tapTimeValue tapTimer.restart() From 1718e068a22588197c27c6e354cfb242827a9cd8 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Thu, 25 Apr 2024 15:08:39 +0200 Subject: [PATCH 154/212] vc: fix OSC feedback regression (fix #1546) --- debian/changelog | 1 + ui/src/virtualconsole/vcwidget.cpp | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/debian/changelog b/debian/changelog index d10acff9fb..1d00f60a1f 100644 --- a/debian/changelog +++ b/debian/changelog @@ -4,6 +4,7 @@ qlcplus (4.13.1) stable; urgency=low * engine: include relative EFX in blackout * engine: fix RGB Matrix clone control mode * Show Manager: improve resume after pause + * Virtual Console: fix OSC feedback regression * Virtual Console/XY Pad: copy presets when cloning (thanks to Hans-Jürgen Tappe) * 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) diff --git a/ui/src/virtualconsole/vcwidget.cpp b/ui/src/virtualconsole/vcwidget.cpp index 93404286b6..537b9505e7 100644 --- a/ui/src/virtualconsole/vcwidget.cpp +++ b/ui/src/virtualconsole/vcwidget.cpp @@ -623,7 +623,7 @@ void VCWidget::setInputSource(QSharedPointer const& source, quin // retrieve plugin specific params for feedback if (source->feedbackExtraParams(QLCInputFeedback::LowerValue).toInt() == -1) source->setFeedbackExtraParams(QLCInputFeedback::LowerValue, profile->channelExtraParams(ich)); - if (source->feedbackExtraParams(QLCInputFeedback::UpperValue).toInt() == -1) + if (source->feedbackExtraParams(QLCInputFeedback::UpperValue).toInt() == -1 || !source->feedbackExtraParams(QLCInputFeedback::UpperValue).isValid()) source->setFeedbackExtraParams(QLCInputFeedback::UpperValue, profile->channelExtraParams(ich)); if (source->feedbackExtraParams(QLCInputFeedback::MonitorValue).toInt() == -1) source->setFeedbackExtraParams(QLCInputFeedback::MonitorValue, profile->channelExtraParams(ich)); @@ -713,7 +713,7 @@ void VCWidget::sendFeedback(int value, QSharedPointer src, QVari if (acceptsInput() == false) return; - //qDebug() << "[VCWidget] Send feedback to uni" << src->universe() << "," << src->channel() << ", param" << extraParams; + qDebug() << "[VCWidget] Send feedback to uni" << src->universe() << "," << src->channel() << ", param" << extraParams; m_doc->inputOutputMap()->sendFeedBack( src->universe(), src->channel(), value, From e0dc56766166ea08b3344d919b2153745d0e3b5d Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Thu, 25 Apr 2024 15:09:45 +0200 Subject: [PATCH 155/212] vc: chore --- ui/src/virtualconsole/vcwidget.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ui/src/virtualconsole/vcwidget.cpp b/ui/src/virtualconsole/vcwidget.cpp index 537b9505e7..0c4b0e148f 100644 --- a/ui/src/virtualconsole/vcwidget.cpp +++ b/ui/src/virtualconsole/vcwidget.cpp @@ -623,7 +623,8 @@ void VCWidget::setInputSource(QSharedPointer const& source, quin // retrieve plugin specific params for feedback if (source->feedbackExtraParams(QLCInputFeedback::LowerValue).toInt() == -1) source->setFeedbackExtraParams(QLCInputFeedback::LowerValue, profile->channelExtraParams(ich)); - if (source->feedbackExtraParams(QLCInputFeedback::UpperValue).toInt() == -1 || !source->feedbackExtraParams(QLCInputFeedback::UpperValue).isValid()) + if (source->feedbackExtraParams(QLCInputFeedback::UpperValue).toInt() == -1 || + !source->feedbackExtraParams(QLCInputFeedback::UpperValue).isValid()) source->setFeedbackExtraParams(QLCInputFeedback::UpperValue, profile->channelExtraParams(ich)); if (source->feedbackExtraParams(QLCInputFeedback::MonitorValue).toInt() == -1) source->setFeedbackExtraParams(QLCInputFeedback::MonitorValue, profile->channelExtraParams(ich)); @@ -713,7 +714,7 @@ void VCWidget::sendFeedback(int value, QSharedPointer src, QVari if (acceptsInput() == false) return; - qDebug() << "[VCWidget] Send feedback to uni" << src->universe() << "," << src->channel() << ", param" << extraParams; + //qDebug() << "[VCWidget] Send feedback to uni" << src->universe() << "," << src->channel() << ", param" << extraParams; m_doc->inputOutputMap()->sendFeedBack( src->universe(), src->channel(), value, From e77f7ef85b0a84276c8d8212634790f1ee3944f2 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Thu, 25 Apr 2024 19:30:13 +0200 Subject: [PATCH 156/212] plugins/artnet: conform ArtPollReply to v4 specs (fix #1541) --- plugins/artnet/src/artnetcontroller.cpp | 19 ++++++---- plugins/artnet/src/artnetpacketizer.cpp | 46 ++++++++++++++++--------- plugins/artnet/src/artnetpacketizer.h | 3 +- plugins/artnet/src/artnetplugin.cpp | 2 +- plugins/artnet/src/configureartnet.cpp | 2 +- 5 files changed, 47 insertions(+), 25 deletions(-) diff --git a/plugins/artnet/src/artnetcontroller.cpp b/plugins/artnet/src/artnetcontroller.cpp index 9ebe075bcf..08abf77b6f 100644 --- a/plugins/artnet/src/artnetcontroller.cpp +++ b/plugins/artnet/src/artnetcontroller.cpp @@ -24,7 +24,7 @@ #include #include -#define POLL_INTERVAL_MS 5000 +#define POLL_INTERVAL_MS 3000 #define SEND_INTERVAL_MS 2000 #define TRANSMIT_STANDARD "Standard" @@ -429,9 +429,16 @@ bool ArtNetController::handleArtNetPoll(QByteArray const& datagram, QHostAddress qDebug() << "[ArtNet] ArtPoll received"; #endif QByteArray pollReplyPacket; - m_packetizer->setupArtNetPollReply(pollReplyPacket, m_ipAddr, m_MACAddress); - m_udpSocket->writeDatagram(pollReplyPacket, senderAddress, ARTNET_PORT); - ++m_packetSent; + for (QMap::iterator it = m_universeMap.begin(); it != m_universeMap.end(); ++it) + { + quint32 universe = it.key(); + UniverseInfo &info = it.value(); + bool isInput = (info.type & Input) ? true : false; + + m_packetizer->setupArtNetPollReply(pollReplyPacket, m_ipAddr, m_MACAddress, universe, isInput); + m_udpSocket->writeDatagram(pollReplyPacket, senderAddress, ARTNET_PORT); + ++m_packetSent; + } ++m_packetReceived; return true; } @@ -451,7 +458,7 @@ bool ArtNetController::handleArtNetDmx(QByteArray const& datagram, QHostAddress #if _DEBUG_RECEIVED_PACKETS qDebug() << "[ArtNet] DMX data received. Universe:" << artnetUniverse << ", Data size:" << dmxData.size() << ", data[0]=" << (int)dmxData[0] - << ", from=" << senderAddress.toString(); + << ", from=" << QHostAddress(senderAddress.toIPv4Address()).toString(); #endif for (QMap::iterator it = m_universeMap.begin(); it != m_universeMap.end(); ++it) @@ -524,7 +531,7 @@ bool ArtNetController::handlePacket(QByteArray const& datagram, QHostAddress con // return false; #if _DEBUG_RECEIVED_PACKETS - qDebug() << "Received packet with size: " << datagram.size() << ", host: " << senderAddress.toString(); + qDebug() << "Received packet with size: " << datagram.size() << ", host: " << QHostAddress(senderAddress.toIPv4Address()).toString(); #endif quint16 opCode = -1; diff --git a/plugins/artnet/src/artnetpacketizer.cpp b/plugins/artnet/src/artnetpacketizer.cpp index 98a60f49aa..969605d4ba 100644 --- a/plugins/artnet/src/artnetpacketizer.cpp +++ b/plugins/artnet/src/artnetpacketizer.cpp @@ -63,7 +63,7 @@ void ArtNetPacketizer::setupArtNetPoll(QByteArray& data) data.append('\0'); // Priority } -void ArtNetPacketizer::setupArtNetPollReply(QByteArray &data, QHostAddress ipAddr, QString MACaddr) +void ArtNetPacketizer::setupArtNetPollReply(QByteArray &data, QHostAddress ipAddr, QString MACaddr, quint32 universe, bool isInput) { int i = 0; data.clear(); @@ -87,28 +87,42 @@ void ArtNetPacketizer::setupArtNetPollReply(QByteArray &data, QHostAddress ipAdd data.append((char)0xF0); // Status1 - Ready and booted data.append((char)0xFF); // ESTA Manufacturer MSB data.append((char)0xFF); // ESTA Manufacturer LSB + data.append("QLC+"); // Short Name for (i = 0; i < 14; i++) data.append((char)0x00); // 14 bytes of stuffing data.append("Q Light Controller Plus - ArtNet interface"); // Long Name for (i = 0; i < 22; i++) // 64-42 bytes of stuffing. 42 is the length of the long name data.append((char)0x00); + for (i = 0; i < 64; i++) data.append((char)0x00); // Node report - data.append((char)0x00); // NumPort MSB - // FIXME: this should reflect the actual state of QLC+ output ports ! - data.append((char)0x01); // NumPort LSB - data.append((char)0x80); // Port 1 type: can output DMX512 data - data.append((char)0x80); // Port 2 type: can output DMX512 data - data.append((char)0x80); // Port 3 type: can output DMX512 data - data.append((char)0x80); // Port 4 type: can output DMX512 data - // FIXME: this should reflect the actual state of QLC+ output ports ! - for (i = 0; i < 12; i++) - data.append((char)0x00); // Set GoodInput[4], GoodOutput[4] and SwIn[4] all to unknown state - data.append((char)0x00); // SwOut0 - output 0 - data.append((char)0x01); // SwOut1 - output 1 - data.append((char)0x02); // SwOut2 - output 2 - data.append((char)0x03); // SwOut3 - output 3 + data.append((char)0x00); // NumPortsHi + data.append((char)0x01); // NumPortsLo + data.append(isInput ? (char)0x40 : (char)0x80); // PortTypes[0]: can input or output DMX512 data + data.append((char)0x00); // PortTypes[1]: nothing + data.append((char)0x00); // PortTypes[2]: nothing + data.append((char)0x00); // PortTypes[3]: nothing + + data.append(isInput ? (char)0x80 : (char)0x00); // GoodInput[0] - input status port 1 + data.append((char)0x00); // GoodInput[1] - input status port 2 + data.append((char)0x00); // GoodInput[2] - input status port 3 + data.append((char)0x00); // GoodInput[3] - input status port 4 + + data.append(isInput ? (char)0x00 : (char)0x80); // GoodOutputA[0] - output status port 1 + data.append((char)0x00); // GoodOutputA[0] - output status port 2 + data.append((char)0x00); // GoodOutputA[0] - output status port 3 + data.append((char)0x00); // GoodOutputA[0] - output status port 4 + + data.append(isInput ? (char)universe : (char)0x00); // SwIn[0] - port 1 + data.append((char)0x00); // SwIn[1] - port 2 + data.append((char)0x00); // SwIn[2] - port 3 + data.append((char)0x00); // SwIn[3] - port 4 + + data.append(isInput ? (char)0x00 : (char)universe); // SwOut[0] - port 1 + data.append((char)0x00); // SwOut[1] - port 2 + data.append((char)0x00); // SwOut[2] - port 3 + data.append((char)0x00); // SwOut[3] - port 4 for (i = 0; i < 7; i++) data.append((char)0x00); // SwVideo, SwMacro, SwRemote and 4 spare bytes QStringList MAC = MACaddr.split(":"); @@ -208,7 +222,7 @@ bool ArtNetPacketizer::checkPacketAndCode(QByteArray const& data, quint16 &code) if (data.at(7) != 0x00) return false; - code = ((int)data.at(9) << 8) + data.at(8); + code = (quint16(data.at(9)) << 8) + quint16(data.at(8)); return true; } diff --git a/plugins/artnet/src/artnetpacketizer.h b/plugins/artnet/src/artnetpacketizer.h index 5409bf0f96..a7113636dd 100644 --- a/plugins/artnet/src/artnetpacketizer.h +++ b/plugins/artnet/src/artnetpacketizer.h @@ -86,7 +86,8 @@ class ArtNetPacketizer void setupArtNetPoll(QByteArray& data); /** Prepare an ArtNetPollReply packet */ - void setupArtNetPollReply(QByteArray &data, QHostAddress ipAddr, QString MACaddr); + void setupArtNetPollReply(QByteArray &data, QHostAddress ipAddr, + QString MACaddr, quint32 universe, bool isInput); /** Prepare an ArtNetDmx packet */ void setupArtNetDmx(QByteArray& data, const int& universe, const QByteArray &values); diff --git a/plugins/artnet/src/artnetplugin.cpp b/plugins/artnet/src/artnetplugin.cpp index b866f63404..6f2ae27b72 100644 --- a/plugins/artnet/src/artnetplugin.cpp +++ b/plugins/artnet/src/artnetplugin.cpp @@ -441,7 +441,7 @@ void ArtNetPlugin::slotReadyRead() void ArtNetPlugin::handlePacket(QByteArray const& datagram, QHostAddress const& senderAddress) { - // A firts filter: look for a controller on the same subnet as the sender. + // A first filter: look for a controller on the same subnet as the sender. // This allows having the same ArtNet Universe on 2 different network interfaces. foreach (ArtNetIO io, m_IOmapping) { diff --git a/plugins/artnet/src/configureartnet.cpp b/plugins/artnet/src/configureartnet.cpp index 85b6ef7d0c..55a7584b29 100644 --- a/plugins/artnet/src/configureartnet.cpp +++ b/plugins/artnet/src/configureartnet.cpp @@ -95,7 +95,7 @@ void ConfigureArtNet::fillNodesTree() it.next(); QTreeWidgetItem* nitem = new QTreeWidgetItem(pitem); ArtNetNodeInfo nInfo = it.value(); - nitem->setText(KNodesColumnIP, it.key().toString()); + nitem->setText(KNodesColumnIP, QHostAddress(it.key().toIPv4Address()).toString()); nitem->setText(KNodesColumnShortName, nInfo.shortName); nitem->setText(KNodesColumnLongName, nInfo.longName); } From b5934595f0342db7f8bef599f13cbeadbde6840d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Sun, 28 Apr 2024 18:08:02 +0200 Subject: [PATCH 157/212] plugins/dmxusb: fix and use thread in VinceUSBDMX512 --- plugins/dmxusb/src/vinceusbdmx512.cpp | 225 +++++++++++++++----------- plugins/dmxusb/src/vinceusbdmx512.h | 79 +++------ 2 files changed, 156 insertions(+), 148 deletions(-) diff --git a/plugins/dmxusb/src/vinceusbdmx512.cpp b/plugins/dmxusb/src/vinceusbdmx512.cpp index f033249711..563dee50e9 100644 --- a/plugins/dmxusb/src/vinceusbdmx512.cpp +++ b/plugins/dmxusb/src/vinceusbdmx512.cpp @@ -18,16 +18,20 @@ */ #include + #include "vinceusbdmx512.h" -VinceUSBDMX512::VinceUSBDMX512(DMXInterface *interface, quint32 outputLine) - : DMXUSBWidget(interface, outputLine, DEFAULT_OUTPUT_FREQUENCY) +VinceUSBDMX512::VinceUSBDMX512(DMXInterface *iface, quint32 outputLine) + : QThread(NULL) + , DMXUSBWidget(iface, outputLine, DEFAULT_OUTPUT_FREQUENCY) + , m_running(false) { // TODO: Check if DMX IN is available } VinceUSBDMX512::~VinceUSBDMX512() { + stopOutputThread(); } DMXUSBWidget::Type VinceUSBDMX512::type() const @@ -35,26 +39,6 @@ DMXUSBWidget::Type VinceUSBDMX512::type() const return DMXUSBWidget::VinceTX; } -/**************************************************************************** - * Name & Serial - ****************************************************************************/ - -QString VinceUSBDMX512::additionalInfo() const -{ - QString info; - - info += QString("

    "); - info += QString("%1: %2 (%3)").arg(QObject::tr("Protocol")) - .arg("Vince USB-DMX512") - .arg(QObject::tr("Output")); - info += QString("
    "); - info += QString("%1: %2").arg(QObject::tr("Serial number")) - .arg(serial()); - info += QString("

    "); - - return info; -} - /**************************************************************************** * Open & Close ****************************************************************************/ @@ -74,104 +58,94 @@ bool VinceUSBDMX512::open(quint32 line, bool input) if (iface()->write(QByteArray(2, 0x00)) == false) return false; - // Request start DMX command - return this->writeData(VinceUSBDMX512::StartDMX); + QByteArray startSequence; + + startSequence.append(QByteArray(2, VINCE_START_OF_MSG)); + startSequence.append(VINCE_CMD_START_DMX); + startSequence.append(QByteArray(2, 0x00)); + startSequence.append(VINCE_END_OF_MSG); + + if (iface()->write(startSequence) == false) + qWarning() << Q_FUNC_INFO << name() << "START command failed"; + + start(); + + return true; } bool VinceUSBDMX512::close(quint32 line, bool input) { - Q_UNUSED(line) Q_UNUSED(input) - if (isOpen() == false) - return true; + stopOutputThread(); + + QByteArray stopSequence; - // Reqest stop DMX command - if (this->writeData(VinceUSBDMX512::StopDMX) == true) - return DMXUSBWidget::close(); + stopSequence.append(QByteArray(2, VINCE_START_OF_MSG)); + stopSequence.append(VINCE_CMD_STOP_DMX); + stopSequence.append(QByteArray(2, 0x00)); + stopSequence.append(VINCE_END_OF_MSG); - return false; + if (iface()->write(stopSequence) == false) + qWarning() << Q_FUNC_INFO << name() << "STOP command failed"; + + return DMXUSBWidget::close(line); } /**************************************************************************** - * Write & Read + * Inputs ****************************************************************************/ -bool VinceUSBDMX512::writeData(Command command, const QByteArray &data) +int readData(DMXInterface *iface, QByteArray &payload) { - QByteArray message(1, command); // Command - message.prepend(QByteArray(2, VINCE_START_OF_MSG)); // Start condition - if (data.size() == 0) - message.append(QByteArray(2, 0x00)); // Data length - else - { - message.append(int((data.size() + 2) / 256)); // Data length - message.append(int((data.size() + 2) % 256)); - message.append(QByteArray(2, 0x00)); // Gap with data - message.append(data); // Data - } - message.append(VINCE_END_OF_MSG); // Stop condition - - return iface()->write(message); -} - -QByteArray VinceUSBDMX512::readData(bool* ok) -{ - uchar byte = 0; + bool ok; + char byte; ushort dataLength = 0; - QByteArray data = QByteArray(); // Read headers for (int i = 0; i < 6; i++) { - *ok = false; - // Attempt to read byte - byte = iface()->readByte(ok); - if (*ok == false) - return data; + byte = iface->readByte(&ok); + + if (ok == false) + return 0; // Retrieve response (4th byte) - if (i == 3 && byte != VINCE_RESP_OK) + if (i == 3) { - qWarning() << Q_FUNC_INFO << "Error" << byte << "in readed message"; - *ok = false; + if (byte != VINCE_RESP_OK) + { + qWarning() << Q_FUNC_INFO << "Unable to find start of next message"; + return 0; + } } - // Retrieve length (5th & 6th bytes) + // Retrieve data length (5th & 6th bytes) else if (i == 4) dataLength = ushort(byte) * 256; else if (i == 5) dataLength += ushort(byte); } - // Read data if (dataLength > 0) { qDebug() << Q_FUNC_INFO << "Attempt to read" << dataLength << "bytes"; - ushort i; - for (i = 0; i < dataLength; i++) - { - byte = iface()->readByte(ok); - if (*ok == false) - { - qWarning() << Q_FUNC_INFO << "No available byte to read (" << (dataLength - i) << "missing bytes)"; - return data; - } - data.append(byte); - } + // Read the whole payload + payload.clear(); + payload = iface->read(dataLength); } // Read end of message - byte = iface()->readByte(); - if (byte != VINCE_END_OF_MSG) - { + if ((byte = iface->readByte()) != VINCE_END_OF_MSG) qWarning() << Q_FUNC_INFO << "Incorrect end of message received:" << byte; - *ok = false; - } - return data; + return dataLength; } +/**************************************************************************** + * Outputs + ****************************************************************************/ + bool VinceUSBDMX512::writeUniverse(quint32 universe, quint32 output, const QByteArray& data, bool dataChanged) { Q_UNUSED(universe) @@ -180,28 +154,89 @@ bool VinceUSBDMX512::writeUniverse(quint32 universe, quint32 output, const QByte if (isOpen() == false) return false; - // Write only if universe has changed - if (!dataChanged) - return true; + if (m_outputLines[0].m_universeData.size() == 0) + { + m_outputLines[0].m_universeData.append(data); + m_outputLines[0].m_universeData.append(DMX_CHANNELS - data.size(), 0); + } - if (writeData(VinceUSBDMX512::UpdateDMX, data) == false) + if (dataChanged) + m_outputLines[0].m_universeData.replace(0, data.size(), data); + + return true; +} + +void VinceUSBDMX512::stopOutputThread() +{ + if (isRunning() == true) { - qWarning() << Q_FUNC_INFO << name() << "will not accept DMX data"; - return false; + m_running = false; + wait(); } - else +} + +void VinceUSBDMX512::run() +{ + qDebug() << "OUTPUT thread started"; + + QElapsedTimer timer; + + m_running = true; + + while (m_running == true) { - bool ok = false; - QByteArray resp = this->readData(&ok); + timer.restart(); + + int dataLen = m_outputLines[0].m_universeData.length(); - // Check the interface reponse - if (ok == false || resp.size() > 0) + if (dataLen > 0) { - qWarning() << Q_FUNC_INFO << name() << "doesn't respond properly"; - return false; + QByteArray request; + request.append(QByteArray(2, VINCE_START_OF_MSG)); // Start byte + request.append(VINCE_CMD_UPDATE_DMX); // Command + request.append(int((dataLen + 2) / 256)); // Data length + request.append(int((dataLen + 2) % 256)); + request.append(QByteArray(2, 0x00)); // Gap with data + request.append(m_outputLines[0].m_universeData); + request.append(VINCE_END_OF_MSG); // Stop byte + + if (iface()->write(request) == false) + qWarning() << Q_FUNC_INFO << name() << "Will not accept DMX data"; + else + { + QByteArray reply; + + if (readData(iface(), reply) > 0) + qWarning() << Q_FUNC_INFO << name() << "Invalid response"; + } } - m_universe = data; - return true; + int timetoSleep = m_frameTimeUs - (timer.nsecsElapsed() / 1000); + if (timetoSleep < 0) + qWarning() << "DMX output is running late !"; + else + usleep(timetoSleep); } + + qDebug() << "OUTPUT thread terminated"; +} + +/**************************************************************************** + * Serial & name + ****************************************************************************/ + +QString VinceUSBDMX512::additionalInfo() const +{ + QString info; + + info += QString("

    "); + info += QString("%1: %2 (%3)").arg(QObject::tr("Protocol")) + .arg("Vince USB-DMX512") + .arg(QObject::tr("Output")); + info += QString("
    "); + info += QString("%1: %2").arg(QObject::tr("Serial number")) + .arg(serial()); + info += QString("

    "); + + return info; } diff --git a/plugins/dmxusb/src/vinceusbdmx512.h b/plugins/dmxusb/src/vinceusbdmx512.h index 08c3001f30..ab68d5aa4d 100644 --- a/plugins/dmxusb/src/vinceusbdmx512.h +++ b/plugins/dmxusb/src/vinceusbdmx512.h @@ -21,7 +21,7 @@ #define VINCEUSBDMX512_H #include -#include +#include #include "dmxusbwidget.h" @@ -39,38 +39,18 @@ #define VINCE_RESP_IO_ERR char(0x10) //! CMD_IO_ERR #define VINCE_RESP_PARAM_ERR char(0x11) //! CMD_PARAM_ERR -/** - * This is the base interface class for Vince USB-DMX512 widgets. - */ -class VinceUSBDMX512 : public DMXUSBWidget +class VinceUSBDMX512 : public QThread, public DMXUSBWidget { - /************************************************************************ - * Initialization - ************************************************************************/ + Q_OBJECT + public: - VinceUSBDMX512(DMXInterface *interface, quint32 outputLine); + VinceUSBDMX512(DMXInterface *iface, quint32 outputLine); + virtual ~VinceUSBDMX512(); /** @reimp */ Type type() const; -protected: - /** Requests commands */ - enum Command - { - StartDMX = VINCE_CMD_START_DMX, - StopDMX = VINCE_CMD_STOP_DMX, - ResetDMX = VINCE_CMD_RESET_DMX, - UpdateDMX = VINCE_CMD_UPDATE_DMX - }; - - /**************************************************************************** - * Name & Serial - ****************************************************************************/ -public: - /** @reimp */ - QString additionalInfo() const; - /**************************************************************************** * Open & Close ****************************************************************************/ @@ -79,38 +59,31 @@ class VinceUSBDMX512 : public DMXUSBWidget bool open(quint32 line = 0, bool input = false); /** @reimp */ - virtual bool close(quint32 line = 0, bool input = false); - - /************************************************************************ - * Write & Read - ************************************************************************/ -protected: - /** - * Format and write data to the opened interface. - * - * @param command The request type - * @param data The data to write - * @return true if the values were sent successfully, otherwise false - */ - bool writeData(enum Command command, const QByteArray& data = QByteArray()); - - /** - * Read and extract data from the opened interface. - * - * @param ok A pointer which tells if data was read or not - * @return The available data - */ - QByteArray readData(bool* ok = NULL); - - /************************************************************************ - * Write universe - ************************************************************************/ + bool close(quint32 line = 0, bool input = false); + + /******************************************************************** + * Outputs + ********************************************************************/ public: /** @reimp */ bool writeUniverse(quint32 universe, quint32 output, const QByteArray& data, bool dataChanged); private: - QByteArray m_universe; + /** Stop the output thread */ + void stopOutputThread(); + + /** Output thread worker method */ + void run(); + +private: + bool m_running; + + /**************************************************************************** + * Serial & name + ****************************************************************************/ +public: + /** @reimp */ + QString additionalInfo() const; }; #endif From 01a4afc6886385b92c3297f9d4423d111f0b11a0 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Sun, 28 Apr 2024 21:39:49 +0200 Subject: [PATCH 158/212] android: app icon and 3d configuration --- platforms/android/res/drawable/icon.png | Bin 13943 -> 6442 bytes qmlui/main.cpp | 3 ++- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/platforms/android/res/drawable/icon.png b/platforms/android/res/drawable/icon.png index a6f851d2b34af37c8fa1964a809473d71f0032bb..578101578aeceaeb5fb68f5671649f0988460497 100644 GIT binary patch delta 6440 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 Date: Sun, 28 Apr 2024 23:44:30 +0200 Subject: [PATCH 159/212] qmlui: fix audio trigger widget creation --- qmlui/virtualconsole/vcframe.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/qmlui/virtualconsole/vcframe.cpp b/qmlui/virtualconsole/vcframe.cpp index 2134b3fb8f..5679a65445 100644 --- a/qmlui/virtualconsole/vcframe.cpp +++ b/qmlui/virtualconsole/vcframe.cpp @@ -1104,16 +1104,16 @@ bool VCFrame::loadWidgetXML(QXmlStreamReader &root, bool render) else if (root.name() == KXMLQLCVCAudioTriggers) { /* Create a new clock into its parent */ - VCAnimation *animation = new VCAnimation(m_doc, this); - if (animation->loadXML(root) == false) - delete animation; + VCAudioTrigger *audioTrigger = new VCAudioTrigger(m_doc, this); + if (audioTrigger->loadXML(root) == false) + delete audioTrigger; else { - QQmlEngine::setObjectOwnership(animation, QQmlEngine::CppOwnership); - setupWidget(animation, animation->page()); - m_vc->addWidgetToMap(animation); + QQmlEngine::setObjectOwnership(audioTrigger, QQmlEngine::CppOwnership); + setupWidget(audioTrigger, audioTrigger->page()); + m_vc->addWidgetToMap(audioTrigger); if (render && m_item) - animation->render(m_vc->view(), m_item); + audioTrigger->render(m_vc->view(), m_item); } } else if (root.name() == KXMLQLCVCSpeedDial) From 3c607d02919f11b2bed5518c8c992039d7dc91b9 Mon Sep 17 00:00:00 2001 From: LilyCalla Date: Mon, 29 Apr 2024 18:21:04 -0700 Subject: [PATCH 160/212] removed debug print in settings editor --- qmlui/qml/UISettingsEditor.qml | 1 - 1 file changed, 1 deletion(-) diff --git a/qmlui/qml/UISettingsEditor.qml b/qmlui/qml/UISettingsEditor.qml index 52d82dfff3..6ad89b4208 100644 --- a/qmlui/qml/UISettingsEditor.qml +++ b/qmlui/qml/UISettingsEditor.qml @@ -37,7 +37,6 @@ Rectangle onVisibleChanged: { - console.log("visibility change"); origItemHeight = UISettings.listItemHeight origIconMedium = UISettings.iconSizeMedium origTextSizeDefault = UISettings.textSizeDefault From cbd54876445a7031b5c2e02ec6f51277d3a4791e Mon Sep 17 00:00:00 2001 From: LilyCalla Date: Mon, 29 Apr 2024 18:33:24 -0700 Subject: [PATCH 161/212] removed further debug printouts --- qmlui/qml/UISettingsEditor.qml | 2 -- 1 file changed, 2 deletions(-) diff --git a/qmlui/qml/UISettingsEditor.qml b/qmlui/qml/UISettingsEditor.qml index 6ad89b4208..7afe49446b 100644 --- a/qmlui/qml/UISettingsEditor.qml +++ b/qmlui/qml/UISettingsEditor.qml @@ -188,7 +188,6 @@ Rectangle height: origItemHeight fontSize: origTextSizeDefault label: qsTr("Background darker") - onHeightChanged: console.log("Row 2 height: " + height); } Loader { @@ -208,7 +207,6 @@ Rectangle height: origItemHeight fontSize: origTextSizeDefault label: qsTr("Background dark") - onHeightChanged: console.log("text height changed " + height) } Loader { From 571ff729f6cc774d7c42a20a2add67c39d9b6c55 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Tue, 30 Apr 2024 13:48:11 +0200 Subject: [PATCH 162/212] resources: 4 new fixtures (see changelog) --- debian/changelog | 3 + .../BoomToneDJ-LED-PAR-7X10W-5in1.qxf | 45 ++ .../Eurolite/Eurolite-LED-PIX-16-QCL-Bar.qxf | 566 ++++++++++++++++++ resources/fixtures/FixturesMap.xml | 4 + .../Mac_Mah/Mac-Mah-FLAT-PAR-7x12W-6in1.qxf | 92 +++ .../Showtec/Showtec-ACT-PC-60-RGBW.qxf | 156 +++++ 6 files changed, 866 insertions(+) create mode 100644 resources/fixtures/BoomToneDJ/BoomToneDJ-LED-PAR-7X10W-5in1.qxf create mode 100644 resources/fixtures/Eurolite/Eurolite-LED-PIX-16-QCL-Bar.qxf create mode 100644 resources/fixtures/Mac_Mah/Mac-Mah-FLAT-PAR-7x12W-6in1.qxf create mode 100644 resources/fixtures/Showtec/Showtec-ACT-PC-60-RGBW.qxf diff --git a/debian/changelog b/debian/changelog index 1d00f60a1f..1784b22931 100644 --- a/debian/changelog +++ b/debian/changelog @@ -6,8 +6,11 @@ qlcplus (4.13.1) stable; urgency=low * Show Manager: improve resume after pause * Virtual Console: fix OSC feedback regression * 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) * 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, 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) -- Massimo Callegari Sun, 30 Jun 2024 12:13:14 +0200 diff --git a/resources/fixtures/BoomToneDJ/BoomToneDJ-LED-PAR-7X10W-5in1.qxf b/resources/fixtures/BoomToneDJ/BoomToneDJ-LED-PAR-7X10W-5in1.qxf new file mode 100644 index 0000000000..288e8fac48 --- /dev/null +++ b/resources/fixtures/BoomToneDJ/BoomToneDJ-LED-PAR-7X10W-5in1.qxf @@ -0,0 +1,45 @@ + + + + + Q Light Controller Plus + 5.0.0 Beta 3 + Cédric Monféfoul + + BoomToneDJ + LED PAR 7X10W 5in1 + Color Changer + + + + + + + + + Effect + Auto Show + + + Speed + Speed Auto Show + + + Master Dimmer + Red + Green + Blue + White + Amber + Strobe + Auto Show + Speed Auto Show + + + + + + + + + diff --git a/resources/fixtures/Eurolite/Eurolite-LED-PIX-16-QCL-Bar.qxf b/resources/fixtures/Eurolite/Eurolite-LED-PIX-16-QCL-Bar.qxf new file mode 100644 index 0000000000..99e9772a02 --- /dev/null +++ b/resources/fixtures/Eurolite/Eurolite-LED-PIX-16-QCL-Bar.qxf @@ -0,0 +1,566 @@ + + + + + Q Light Controller Plus + 5.0.0 Beta 3 + Cédric Monféfoul + + Eurolite + LED PIX-16 QCL Bar + LED Bar (Beams) + + + + + + + Shutter + No Function + Strobe (Slow to fast) + + + Effect + No Function + Effect 1 + Effect 2 + Effect 3 + Effect 4 + Effect 5 + Effect 6 + Effect 7 + Effect 8 + Effect 9 + Effect 10 + Effect 11 + Effect 12 + Effect 13 + Effect 14 + Effect 15 + Effect 16 + Effect 17 + Effect 18 + Effect 19 + Effect 20 + + + Speed + Speed of the color effects, increasing + + + Colour + No Function + Red + Green + Blue + White + Red + Green + Red + Blue + Red + White + Green + Blue + Green + White + Blue + White + Red + Green + Blue + Red + Green + White + Red + Blue + White + Green + Blue + white + + + Intensity + Background Effect Color from Bright to Dark + + + Intensity + Run Direction Forwards + Run Direction Backards + + + Intensity + R1/R2/R3/R4 Dimmer (0 - 100%) + + + Intensity + G1/G2/G3/G4 Dimmer (0 - 100%) + + + Intensity + B1/B2/B3/B4 Dimmer (0 - 100%) + + + Intensity + W1/W2/W3/W4 Dimmer (0 - 100%) + + + Intensity + R5/R6/R7/R6 Dimmer (0 - 100%) + + + Intensity + G5/G6/G7/G8 Dimmer (0 - 100%) + + + Intensity + B5/B6/B7/B8 Dimmer (0 - 100%) + + + Intensity + W5/W6/W7/W8 Dimmer (0 - 100%) + + + Intensity + R9/R10/R11/R12 Dimmer (0 - 100%) + + + Intensity + G9/G10/G11/G12 Dimmer (0 - 100%) + + + Intensity + B9/B10/B11/B12 Dimmer (0 - 100%) + + + Intensity + W9/W10/W11/W12 Dimmer (0 - 100%) + + + Intensity + R13/R14/R15/R16 Dimmer (0 - 100%) + + + Intensity + G13/G14/G15/G16 Dimmer (0 - 100%) + + + Intensity + B13/B14/B15/B16 Dimmer (0 - 100%) + + + Intensity + W13/W14/W15/W16 Dimmer (0 - 100%) + + + Effect + Internal programs (See 11CH mode, Channel 7 for overview) + + + Speed + Speed and microphone sensitivity for internal programs + + + Maintenance + Auto Program + Sound Controlled Program + + + Intensity + R1/R5/R9/R13 Dimmer (0 - 100%) + + + Intensity + G1/G5/G9/G13 Dimmer (0 - 100%) + + + Intensity + B1/B5/B9/B13 Dimmer (0 - 100%) + + + Intensity + W1/W5/W9/W13 Dimmer (0 - 100%) + + + Intensity + R2/R6/R10/R14 Dimmer (0 - 100%) + + + Intensity + G2/G6/G10/G14 Dimmer (0 - 100%) + + + Intensity + B2/B6/B10/B14 Dimmer (0 - 100%) + + + Intensity + W2/W6/W10/W14 Dimmer (0 - 100%) + + + Intensity + R3/R7/R11/R15 Dimmer (0 - 100%) + + + Intensity + G3/G7/G11/G15 Dimmer (0 - 100%) + + + Intensity + B3/B7/B11/B15 Dimmer (0 - 100%) + + + Intensity + W3/W7/W11/W15 Dimmer (0 - 100%) + + + Intensity + R4/R8/R12/R16 Dimmer (0 - 100%) + + + Intensity + G4/G8/G12/G16 Dimmer (0 - 100%) + + + Intensity + B4/B8/B12/B16 Dimmer (0 - 100%) + + + Intensity + W4/W8/W12/W16 Dimmer (0 - 100%) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Red 1 + Green 1 + Blue 1 + + + Red 1 + Green 1 + Blue 1 + White 1 + + + Master dimmer + Strobe + Red 1 + Green 1 + Blue 1 + White 1 + Color Effects + Color Effects Speed + Background Colors + Background Effect Color + Direction Forwards + + + R1/R2/R3/R4 Dimmer + G1/G2/G3/G4 Dimmer + B1/B2/B3/B4 Dimmer + W1/W2/W3/W4 Dimmer + R5/R6/R7/R8 Dimmer + G5/G6/G7/G8 Dimmer + B5/B6/B7/B8 Dimmer + W5/W6/W7/W8 Dimmer + R9/R10/R11/R12 Dimmer + G9/G10/G11/G12 Dimmer + B9/B10/B11/B12 Dimmer + W9/W10/W11/W12 Dimmer + R13/R14/R15/R16 Dimmer + G13/G14/G15/G16 Dimmer + B13/B14/B15/B16 Dimmer + W13/W14/W15/W16 Dimmer + Master dimmer + Strobe + Internals Programs + Micro Sensitivity and Speed + Auto / Sound + + 0 + 1 + 3 + 2 + + + 4 + 5 + 6 + 7 + + + 8 + 9 + 10 + 11 + + + 12 + 13 + 14 + 15 + + + + R1/R5/R9/R13 Dimmer + G1/G5/G9/G13 Dimmer + B1/B5/B9/B13 Dimmer + W1/W5/W9/W13 Dimmer + R2/R6/R10/R14 Dimmer + G2/G6/G10/G14 Dimmer + B2/B6/B10/B14 Dimmer + W2/W6/W10/W14 Dimmer + R3/R7/R11/R15 Dimmer + G3/G7/G11/G15 Dimmer + B3/B7/B11/B15 Dimmer + W3/W7/W11/W15 Dimmer + R4/R8/R12/R16 Dimmer + G4/G8/G12/G16 Dimmer + B4/B8/B12/B16 Dimmer + W4/W8/W12/W16 Dimmer + Master dimmer + Strobe + Internals Programs + Micro Sensitivity and Speed + Auto / Sound + + 0 + 1 + 2 + 3 + + + 4 + 5 + 6 + 7 + + + 8 + 9 + 10 + 11 + + + 12 + 13 + 14 + 15 + + + + Red 1 + Green 1 + Blue 1 + White 1 + Red 2 + Green 2 + Blue 2 + White 2 + Red 3 + Green 3 + Blue 3 + White 3 + Red 4 + Green 4 + Blue 4 + White 4 + Red 5 + Green 5 + Blue 5 + White 5 + Red 6 + Green 6 + Blue 6 + White 6 + Red 7 + Green 7 + Blue 7 + White 7 + Red 8 + Green 8 + Blue 8 + White 8 + Red 9 + Green 9 + Blue 9 + White 9 + Red 10 + Green 10 + Blue 10 + White 10 + Red 11 + Green 11 + Blue 11 + White 11 + Red 12 + Green 12 + Blue 12 + White 12 + Red 13 + Green 13 + Blue 13 + White 13 + Red 14 + Green 14 + Blue 14 + White 14 + Red 15 + Green 15 + Blue 15 + White 15 + Red 16 + Green 16 + Blue 16 + White 16 + + 0 + 1 + 2 + 3 + + + 4 + 5 + 6 + 7 + + + 8 + 9 + 10 + 11 + + + 12 + 13 + 14 + 15 + + + 16 + 17 + 18 + 19 + + + 20 + 21 + 22 + 23 + + + 24 + 25 + 26 + 27 + + + 28 + 29 + 30 + 31 + + + 32 + 33 + 34 + 35 + + + 36 + 37 + 38 + 39 + + + 40 + 41 + 42 + 43 + + + 44 + 45 + 46 + 47 + + + 48 + 49 + 50 + 51 + + + 52 + 53 + 54 + 55 + + + 56 + 57 + 58 + 59 + + + 60 + 61 + 62 + 63 + + + + + + + + + + + diff --git a/resources/fixtures/FixturesMap.xml b/resources/fixtures/FixturesMap.xml index db667a69bf..5a879eb745 100644 --- a/resources/fixtures/FixturesMap.xml +++ b/resources/fixtures/FixturesMap.xml @@ -281,6 +281,7 @@ + @@ -788,6 +789,7 @@ + @@ -1189,6 +1191,7 @@ + @@ -1505,6 +1508,7 @@ + diff --git a/resources/fixtures/Mac_Mah/Mac-Mah-FLAT-PAR-7x12W-6in1.qxf b/resources/fixtures/Mac_Mah/Mac-Mah-FLAT-PAR-7x12W-6in1.qxf new file mode 100644 index 0000000000..6cffddbb24 --- /dev/null +++ b/resources/fixtures/Mac_Mah/Mac-Mah-FLAT-PAR-7x12W-6in1.qxf @@ -0,0 +1,92 @@ + + + + + Q Light Controller Plus + 5.0.0 Beta 3 + Cédric Monféfoul + + Mac Mah + FLAT PAR 7x12W 6in1 + Color Changer + + Colour + No Function + Red + Green + Blue + White + Amber + UV + Red + Green + Red + Blue + Red + White + Red + Amber + Red + UV + Green + Blue + Green + White + Green + Amber + Green + UV + Blue + White + Blue + Amber + Blue + UV + White + Amber + White + UV + Amber + UV + Red + Green + Blue + Red + Blue + Amber + Red + Green + Blue + White + Ambre + UV + + + Shutter + No Function + Strobe Speed (Slow to Fast) + + + Maintenance + No Function + Auto DMX + Sound DMX + + + Speed + Auto DMX / Sound Speed (from slow to fast) + + + + + + + + + + Shutter + No Function + LED Strobe (from slow to fast) + + + Colors + Strobe + Auto / Sound + Auto DMX / Sound Speed + + + Red + Green + Blue + White + Amber + UV + Dimming + LED Strobe + Auto / Sound + Auto DMX / Sound Speed + + + + + + + + + diff --git a/resources/fixtures/Showtec/Showtec-ACT-PC-60-RGBW.qxf b/resources/fixtures/Showtec/Showtec-ACT-PC-60-RGBW.qxf new file mode 100644 index 0000000000..433c29f700 --- /dev/null +++ b/resources/fixtures/Showtec/Showtec-ACT-PC-60-RGBW.qxf @@ -0,0 +1,156 @@ + + + + + Q Light Controller Plus + 5.0.0 Beta 3 + Michel Sliepenbeek + + Showtec + ACT PC 60 RGBW + Color Changer + + + + + + Colour + No Function + Color 1 - Red + Color 2 + Color 3 + Color 4 + Color 5 + Color 6 + Color 7 + Color 8 - Orange + Color 9 + Color 10 + Color 11 + Color 12 + Color 13 + Color 14 + Color 15 + Color 16 - Yellow + Color 17 + Color 18 + Color 19 + Color 20 + Color 21 + Color 22 + Color 23 + Color 24 + Color 25 + Color 26 + Color 27 + Color 28 + Color 29 + Color 30 + Color 31 + Color 32 + Color 33 + Color 34 + Color 35 + Color 36 + Color 37 + Color 38 + Color 39 + Color 40 + Color 41 + Color 42 + Color 43 + Color 44 + Color 45 + Color 46 + Color 47 + Color 48 + Color 49 - Blue + Color 50 + Color 51 + Color 52 + Color 53 + Color 54 + Color 55 + Color 56 + Color 57 + Color 58 + Color 59 + Color 60 + Color 61 + Color 62 + Color 63 + Color 64 + + + Shutter + Closed (No Light Output) + Open + Strobe, from Low to High Frequency + Open + Strobe, from Low to High Rate + Open + Random Strobe, from Low to High Rate + Open + + + + Colour + No Function + Color Temperature from Warm (3000 K) to Cold (7200K) + + + + Effect + No Function + Chase 1 + Chase 2 + Chase 3 + Chase 4 + Chase 5 + Chase 6 + Chase 7 + Chase 8 + Chase 9 + Chase 10 + + + Speed + Speed of Chases: Slow to Fast + + + Red + Green + Blue + White + Color Macros + Strobe + Master Dimmer + Color Temperature + Zoom + Chases + Speed + + + Red + Green + Blue + White + Zoom + + + Red + Green + Blue + White + Strobe + Master Dimmer + Zoom + + + + + + + + + From e4167c1b913b9a41afa2a6ecb1858be3ec3b76ca Mon Sep 17 00:00:00 2001 From: Lachlan Hicks <42596763+yestalgia@users.noreply.github.com> Date: Tue, 30 Apr 2024 22:19:19 +1000 Subject: [PATCH 163/212] Chore: Upgrade github actions/upload-artifact and actions/checkout to v4 --- .github/workflows/build.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 283a8a2ffc..a83d7d4ae3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -5,7 +5,7 @@ on: ["push", "pull_request"] jobs: build-linux: #if: false - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest name: QLCplus Linux ${{matrix.task}} strategy: #fail-fast: false @@ -45,7 +45,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: submodules: false @@ -107,7 +107,7 @@ jobs: version: ${{runner.os}}-apt - name: Install Qt through aqt (and python) - uses: jurplel/install-qt-action@v3 + uses: jurplel/install-qt-action@v4 with: version: ${{ env.QT_VERSION }} cache: true @@ -202,7 +202,7 @@ jobs: - name: Store original install artifacts before stripping and AppImage # Activate for debugging if: ${{ false && ! startsWith( matrix.task, 'coverage') }} - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: ${{ matrix.task }}-files path: ${{ env.INSTALL_ROOT }} @@ -246,7 +246,7 @@ jobs: - name: Store AppImage artifacts if: ${{ ! startsWith( matrix.task, 'coverage') }} - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: ${{ matrix.task }}-AppImage path: qlcplus-${{env.TASK}}-${{env.APPVERSION}}-${{env.BUILD_DATE}}-${{env.GIT_REV}}.AppImage @@ -271,7 +271,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: submodules: false @@ -436,7 +436,7 @@ jobs: mv /c/qlcplus/${{env.OUTFILE}} /d/a/qlcplus/qlcplus - name: Store executable artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: QLC+-${{env.TASK}}-${{env.APPVERSION}}-${{env.BUILD_DATE}}-${{env.GIT_REV}}.exe path: ${{env.OUTFILE}} From 3fcccd82623f6cf3ce6bfa14bff7f9882ef1429a Mon Sep 17 00:00:00 2001 From: Lachlan Hicks <42596763+yestalgia@users.noreply.github.com> Date: Tue, 30 Apr 2024 22:21:36 +1000 Subject: [PATCH 164/212] Fix: install-qt-action version --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a83d7d4ae3..1f48a49784 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -107,7 +107,7 @@ jobs: version: ${{runner.os}}-apt - name: Install Qt through aqt (and python) - uses: jurplel/install-qt-action@v4 + uses: jurplel/install-qt-action@v3 with: version: ${{ env.QT_VERSION }} cache: true From 50926866264699995952d5f092df245e06856a98 Mon Sep 17 00:00:00 2001 From: Lachlan Hicks <42596763+yestalgia@users.noreply.github.com> Date: Tue, 30 Apr 2024 22:37:34 +1000 Subject: [PATCH 165/212] Fix: Ubuntu version (required for chrpath) --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1f48a49784..ec469aa612 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -5,7 +5,7 @@ on: ["push", "pull_request"] jobs: build-linux: #if: false - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 name: QLCplus Linux ${{matrix.task}} strategy: #fail-fast: false From a0affb25d4bfbac2c5af383a36d1c9544449b835 Mon Sep 17 00:00:00 2001 From: Lachlan Hicks <42596763+yestalgia@users.noreply.github.com> Date: Tue, 30 Apr 2024 23:11:43 +1000 Subject: [PATCH 166/212] Chore: Update Coveralls version to v2 --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ec469aa612..921f47d458 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -176,7 +176,7 @@ jobs: - name: Coveralls if: ${{ startsWith( matrix.task, 'coverage') }} - uses: coverallsapp/github-action@1.1.3 + uses: coverallsapp/github-action@v2 with: github-token: ${{ secrets.GITHUB_TOKEN }} path-to-lcov: coverage/coverage.info From ef8b66ebd680a10c3c24e7bd4aa0cedbd13f0d59 Mon Sep 17 00:00:00 2001 From: Lachlan Hicks <42596763+yestalgia@users.noreply.github.com> Date: Tue, 30 Apr 2024 23:37:30 +1000 Subject: [PATCH 167/212] Fix: Coveralls V2 file extension regression See issue: https://github.com/coverallsapp/github-action/issues/159 --- .github/workflows/build.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 921f47d458..cc71eb7d26 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -179,7 +179,8 @@ jobs: uses: coverallsapp/github-action@v2 with: github-token: ${{ secrets.GITHUB_TOKEN }} - path-to-lcov: coverage/coverage.info + file: coverage/coverage.info + format: lcov - name: Install if: ${{ ! startsWith( matrix.task, 'coverage') }} From 6b19673c6586156f874835b21d8fecf0cb8303ed Mon Sep 17 00:00:00 2001 From: LilyCalla Date: Tue, 30 Apr 2024 22:45:58 -0700 Subject: [PATCH 168/212] prevent invisible search filter --- qmlui/qml/fixturesfunctions/LeftPanel.qml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/qmlui/qml/fixturesfunctions/LeftPanel.qml b/qmlui/qml/fixturesfunctions/LeftPanel.qml index afab32e9f5..a3ba6b44bf 100644 --- a/qmlui/qml/fixturesfunctions/LeftPanel.qml +++ b/qmlui/qml/fixturesfunctions/LeftPanel.qml @@ -98,8 +98,10 @@ SidePanel autoExclusive: false onToggled: { - if (checked == true) + if (checked == true) { loaderSource = "qrc:/FixtureGroupManager.qml" + fixtureManager.searchFilter = "" + } animatePanel(checked) } } From 689ed36e5ad5af3b80103843912206841942de26 Mon Sep 17 00:00:00 2001 From: LilyCalla Date: Tue, 30 Apr 2024 22:47:58 -0700 Subject: [PATCH 169/212] prevent accidental nullptr entries in map --- qmlui/treemodel.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/qmlui/treemodel.cpp b/qmlui/treemodel.cpp index bbd8881ad3..c28d95797b 100644 --- a/qmlui/treemodel.cpp +++ b/qmlui/treemodel.cpp @@ -202,6 +202,8 @@ TreeModelItem *TreeModel::itemAtPath(QString path) return nullptr; } + if(m_itemsPathMap.contains(pathList.at(0))) + return nullptr; TreeModelItem *item = m_itemsPathMap[pathList.at(0)]; QString subPath = path.mid(path.indexOf(TreeModel::separator()) + 1); return item->children()->itemAtPath(subPath); @@ -270,6 +272,8 @@ void TreeModel::setItemRoleData(QString path, const QVariant &value, int role) } else { + if(!m_itemsPathMap.contains(pathList.at(0))) + return; TreeModelItem *item = m_itemsPathMap[pathList.at(0)]; QString subPath = path.mid(path.indexOf(TreeModel::separator()) + 1); item->children()->setItemRoleData(subPath, value, role); From 74be2d89ee86406c44405fef90a7f82295aebd23 Mon Sep 17 00:00:00 2001 From: LilyCalla Date: Tue, 30 Apr 2024 22:49:23 -0700 Subject: [PATCH 170/212] prevent ESC from clearing fixture text --- qmlui/qml/fixturesfunctions/FixtureNodeDelegate.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/qmlui/qml/fixturesfunctions/FixtureNodeDelegate.qml b/qmlui/qml/fixturesfunctions/FixtureNodeDelegate.qml index 9a44a25cd9..739e084412 100644 --- a/qmlui/qml/fixturesfunctions/FixtureNodeDelegate.qml +++ b/qmlui/qml/fixturesfunctions/FixtureNodeDelegate.qml @@ -132,6 +132,7 @@ Column id: nodeLabel Layout.fillWidth: true text: textLabel + originalText: text onTextConfirmed: { From 3150b445eae811cd84ca76462ba33c98f1f6c074 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Wed, 1 May 2024 12:25:54 +0200 Subject: [PATCH 171/212] qmlui: fix preset tool range limit Broken with #1537 --- qmlui/qml/fixturesfunctions/PresetCapabilityItem.qml | 3 --- qmlui/qml/fixturesfunctions/PresetsTool.qml | 3 +++ qmlui/qml/virtualconsole/VCSliderItem.qml | 5 +++++ 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/qmlui/qml/fixturesfunctions/PresetCapabilityItem.qml b/qmlui/qml/fixturesfunctions/PresetCapabilityItem.qml index 44b459dbf3..09da359ea4 100644 --- a/qmlui/qml/fixturesfunctions/PresetCapabilityItem.qml +++ b/qmlui/qml/fixturesfunctions/PresetCapabilityItem.qml @@ -42,9 +42,6 @@ Rectangle return var resArray = capability.resources - var slMin = (sliderRoot && sliderRoot.sliderObj) ? sliderRoot.sliderObj.rangeLowLimit : 0 - var slMax = (sliderRoot && sliderRoot.sliderObj) ? sliderRoot.sliderObj.rangeHighLimit : 255 - visible = (capability.min <= slMax || capability.max <= slMin) capName.label = capability.name if (resArray.length === 0) diff --git a/qmlui/qml/fixturesfunctions/PresetsTool.qml b/qmlui/qml/fixturesfunctions/PresetsTool.qml index 17d07bd963..5ff1b0a96b 100644 --- a/qmlui/qml/fixturesfunctions/PresetsTool.qml +++ b/qmlui/qml/fixturesfunctions/PresetsTool.qml @@ -38,6 +38,8 @@ Rectangle property int selectedChannel: -1 property bool showPalette: false property int currentValue: 0 // as DMX value + property int rangeLowLimit: 0 + property int rangeHighLimit: 255 signal presetSelected(QLCCapability cap, int fxID, int chIdx, int value) signal valueChanged(int value) @@ -152,6 +154,7 @@ Rectangle { capability: modelData capIndex: index + 1 + visible: (capability.min <= toolRoot.rangeHighLimit || capability.max <= toolRoot.rangeLowLimit) onValueChanged: { toolRoot.currentValue = value diff --git a/qmlui/qml/virtualconsole/VCSliderItem.qml b/qmlui/qml/virtualconsole/VCSliderItem.qml index 3637f70afb..b29add87b3 100644 --- a/qmlui/qml/virtualconsole/VCSliderItem.qml +++ b/qmlui/qml/virtualconsole/VCSliderItem.qml @@ -285,6 +285,11 @@ VCWidgetItem { item.visible = false item.closeOnSelect = true + if (sliderObj && clickAndGoButton.cngType == VCSlider.CnGPreset) + { + item.rangeLowLimit = sliderObj.rangeLowLimit + item.rangeHighLimit = sliderObj.rangeHighLimit + } } Connections From 9afdc73cba65d88b1dfb3b9cdd2ff326cc109aa8 Mon Sep 17 00:00:00 2001 From: LilyCalla Date: Wed, 1 May 2024 08:33:14 -0700 Subject: [PATCH 172/212] correct filename in license header --- qmlui/qml/GenericMultiDragItem.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qmlui/qml/GenericMultiDragItem.qml b/qmlui/qml/GenericMultiDragItem.qml index e3ca4e4759..b354a9d6ee 100644 --- a/qmlui/qml/GenericMultiDragItem.qml +++ b/qmlui/qml/GenericMultiDragItem.qml @@ -1,6 +1,6 @@ /* Q Light Controller Plus - FunctionDragItem.qml + GenericMultiDragItem.qml Copyright (c) Massimo Callegari From 8d9ec56976d6964c772194de5c16e5bce66f248d Mon Sep 17 00:00:00 2001 From: LilyCalla Date: Wed, 1 May 2024 08:43:17 -0700 Subject: [PATCH 173/212] prevent ESC from erasing universe names --- qmlui/qml/TreeNodeDelegate.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/qmlui/qml/TreeNodeDelegate.qml b/qmlui/qml/TreeNodeDelegate.qml index f421b52760..1b9f1bea3c 100644 --- a/qmlui/qml/TreeNodeDelegate.qml +++ b/qmlui/qml/TreeNodeDelegate.qml @@ -103,6 +103,7 @@ Column id: nodeLabel width: nodeBgRect.width - x - 1 text: cRef ? cRef.name : textLabel + originalText: text onTextConfirmed: nodeContainer.pathChanged(nodePath, text) } From 0a1d3322fdb562ecacf76612a3b57f3659f13293 Mon Sep 17 00:00:00 2001 From: LilyCalla Date: Wed, 1 May 2024 10:12:18 -0700 Subject: [PATCH 174/212] fixed color picker full mode --- qmlui/qml/ColorToolFull.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/qmlui/qml/ColorToolFull.qml b/qmlui/qml/ColorToolFull.qml index 1d9af3b3aa..8bd92eb0ba 100644 --- a/qmlui/qml/ColorToolFull.qml +++ b/qmlui/qml/ColorToolFull.qml @@ -125,6 +125,7 @@ Rectangle bSpin.value = b*/ currentRGB = Qt.rgba(r / 255, g / 255, b / 255, 1.0) + colorChanged(currentRGB.r, currentRGB.g, currentRGB.b, currentWAUV.r, currentWAUV.g, currentWAUV.b) } onPressed: setPickedColor(mouse) From 8bde825812c61d42b11222f9ff725c6b4c79a4fc Mon Sep 17 00:00:00 2001 From: Binary-Vanguard-12138 <103393933+Binary-Vanguard-12138@users.noreply.github.com> Date: Wed, 1 May 2024 20:22:05 +0200 Subject: [PATCH 175/212] Added missing resources to assets in Android build --- platforms/android/.gitignore | 1 + resources/CMakeLists.txt | 4 +++ resources/colorfilters/CMakeLists.txt | 13 +++++++- resources/docs/CMakeLists.txt | 27 ++++++++++++++-- resources/fixtures/CMakeLists.txt | 34 +++++++++++++++++---- resources/gobos/CMakeLists.txt | 18 +++++++++++ resources/inputprofiles/CMakeLists.txt | 14 +++++++++ resources/meshes/fixtures/CMakeLists.txt | 14 +++++++++ resources/meshes/generic/CMakeLists.txt | 14 +++++++++ resources/meshes/stage/CMakeLists.txt | 14 +++++++++ resources/miditemplates/CMakeLists.txt | 14 +++++++++ resources/modifierstemplates/CMakeLists.txt | 14 +++++++++ resources/rgbscripts/CMakeLists.txt | 14 +++++++++ resources/samples/CMakeLists.txt | 16 +++++++++- 14 files changed, 200 insertions(+), 11 deletions(-) create mode 100644 platforms/android/.gitignore 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/resources/CMakeLists.txt b/resources/CMakeLists.txt index 42ff4fe251..1cc6a27d4e 100644 --- a/resources/CMakeLists.txt +++ b/resources/CMakeLists.txt @@ -1,5 +1,9 @@ project(resources) +if (NOT qmlui) + add_subdirectory(docs) +endif() + add_subdirectory(fixtures) add_subdirectory(gobos) add_subdirectory(inputprofiles) diff --git a/resources/colorfilters/CMakeLists.txt b/resources/colorfilters/CMakeLists.txt index 73aeb6c5b9..bbfb7df23e 100644 --- a/resources/colorfilters/CMakeLists.txt +++ b/resources/colorfilters/CMakeLists.txt @@ -2,4 +2,15 @@ project(colorfilters) file(GLOB COLOR_FILTER_FILES "${CMAKE_CURRENT_SOURCE_DIR}/*.qxcf") -install(FILES ${COLOR_FILTER_FILES} DESTINATION ${INSTALLROOT}/${COLORFILTERSDIR}) \ No newline at end of file +if(ANDROID) + set(COLOR_FILTERS_ASSETS_DIR "${ANDROID_PACKAGE_SOURCE_DIR}/${COLORFILTERSDIR}") + # Create the assets directory if it doesn't exist + file(MAKE_DIRECTORY ${COLOR_FILTERS_ASSETS_DIR}) + + # Copy the color filter files to the assets directory + foreach(COLOR_FILTER_FILE ${COLOR_FILTER_FILES}) + file(COPY ${COLOR_FILTER_FILE} DESTINATION ${COLOR_FILTERS_ASSETS_DIR}) + endforeach() +endif() + +install(FILES ${COLOR_FILTER_FILES} DESTINATION ${INSTALLROOT}/${COLORFILTERSDIR}) diff --git a/resources/docs/CMakeLists.txt b/resources/docs/CMakeLists.txt index ddc718d5b3..5097d7837f 100644 --- a/resources/docs/CMakeLists.txt +++ b/resources/docs/CMakeLists.txt @@ -1,7 +1,28 @@ project(docs) file(GLOB HTML_FILES "${CMAKE_CURRENT_SOURCE_DIR}/html_en_EN/*.html") -install(FILES ${HTML_FILES} DESTINATION ${INSTALLROOT}/${DOCSDIR}/html) - file(GLOB IMAGE_FILES "${CMAKE_CURRENT_SOURCE_DIR}/images/*.png") -install(FILES ${IMAGE_FILES} DESTINATION ${INSTALLROOT}/${DOCSDIR}/images) \ No newline at end of file + +# Only perform the copy if we're building for Android +if(ANDROID) + # Define the destination directories for the HTML and image files within the Android package + set(HTML_ASSETS_DIR "${ANDROID_PACKAGE_SOURCE_DIR}/${DOCSDIR}/html") + set(IMAGES_ASSETS_DIR "${ANDROID_PACKAGE_SOURCE_DIR}/${DOCSDIR}/images") + + # Create the assets directories if they don't exist + file(MAKE_DIRECTORY ${HTML_ASSETS_DIR}) + file(MAKE_DIRECTORY ${IMAGES_ASSETS_DIR}) + + # Copy the HTML files to the assets directory + foreach(HTML_FILE IN LISTS HTML_FILES) + file(COPY ${HTML_FILE} DESTINATION ${HTML_ASSETS_DIR}) + endforeach() + + # Copy the image files to the assets directory + foreach(IMAGE_FILE IN LISTS IMAGE_FILES) + file(COPY ${IMAGE_FILE} DESTINATION ${IMAGES_ASSETS_DIR}) + endforeach() +endif() + +install(FILES ${HTML_FILES} DESTINATION ${INSTALLROOT}/${DOCSDIR}/html) +install(FILES ${IMAGE_FILES} DESTINATION ${INSTALLROOT}/${DOCSDIR}/images) diff --git a/resources/fixtures/CMakeLists.txt b/resources/fixtures/CMakeLists.txt index aeb9c9bc15..f8441a41f6 100644 --- a/resources/fixtures/CMakeLists.txt +++ b/resources/fixtures/CMakeLists.txt @@ -1,13 +1,35 @@ project(fixtures) # Install FixturesMap.xml and all subdirectories (except ./scripts and .) -file(GLOB FILES_AND_DIRECTORIES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/*) -list(FILTER FILES_AND_DIRECTORIES EXCLUDE REGEX "^scripts$") -list(REMOVE_ITEM FILES_AND_DIRECTORIES ".") +file(GLOB FIXTURES_FILES_AND_DIRECTORIES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/*) +list(FILTER FIXTURES_FILES_AND_DIRECTORIES EXCLUDE REGEX "^scripts$") +list(REMOVE_ITEM FIXTURES_FILES_AND_DIRECTORIES ".") -foreach(FILE_AND_DIRECTORY ${FILES_AND_DIRECTORIES}) - if(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_AND_DIRECTORY}") - install(DIRECTORY ${FILE_AND_DIRECTORY} DESTINATION ${INSTALLROOT}/${FIXTUREDIR}) +# Only perform the copy if we're building for Android +if(ANDROID) + # Loop through each file and directory and copy them to the assets directory, excluding the scripts directory + foreach(FIXTURE_FILE_AND_DIRECTORY ${FIXTURES_FILES_AND_DIRECTORIES}) + if(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${FIXTURE_FILE_AND_DIRECTORY}") + # Calculate the full path to the source directory + set(SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/${FIXTURE_FILE_AND_DIRECTORY}") + + # Calculate the full path to the destination directory within the Android package + set(DESTINATION_DIR "${ANDROID_PACKAGE_SOURCE_DIR}/${FIXTUREDIR}") + + # Copy the directory to the assets directory + file(COPY ${SOURCE_DIR} DESTINATION ${DESTINATION_DIR}) + endif() + endforeach() + + # Copy the FixturesMap.xml file to the assets directory + set(SOURCE_FIXTURES_MAP "${CMAKE_CURRENT_SOURCE_DIR}/FixturesMap.xml") + set(DESTINATION_FIXTURES_MAP "${ANDROID_PACKAGE_SOURCE_DIR}/${FIXTUREDIR}") + file(COPY ${SOURCE_FIXTURES_MAP} DESTINATION ${DESTINATION_FIXTURES_MAP}) +endif() + +foreach(FIXTURE_FILE_AND_DIRECTORY ${FIXTURES_FILES_AND_DIRECTORIES}) + if(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${FIXTURE_FILE_AND_DIRECTORY}") + install(DIRECTORY ${FIXTURE_FILE_AND_DIRECTORY} DESTINATION ${INSTALLROOT}/${FIXTUREDIR}) endif() endforeach() diff --git a/resources/gobos/CMakeLists.txt b/resources/gobos/CMakeLists.txt index d601f570f4..c6c3593155 100644 --- a/resources/gobos/CMakeLists.txt +++ b/resources/gobos/CMakeLists.txt @@ -2,4 +2,22 @@ project(gobo) set(GOBO_FILES Chauvet ClayPaky GLP Others Robe SGM SGM-Color) +# Only perform the copy if we're building for Android +if(ANDROID) + # Define the destination directory for the gobo files within the Android package + set(GOBO_ASSETS_DIR "${ANDROID_PACKAGE_SOURCE_DIR}/${GOBODIR}") + + # Loop through each directory and copy it to the assets directory + foreach(GOBO_DIR IN LISTS GOBO_FILES) + # Calculate the full path to the source gobo directory + set(SOURCE_GOBOS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/${GOBO_DIR}") + + # Calculate the full path to the destination gobo directory + set(DESTINATION_GOBOS_DIR "${GOBO_ASSETS_DIR}") + + # Copy the gobo directory to the assets directory + file(COPY ${SOURCE_GOBOS_DIR} DESTINATION ${DESTINATION_GOBOS_DIR}) + endforeach() +endif() + install(DIRECTORY ${GOBO_FILES} DESTINATION ${INSTALLROOT}/${GOBODIR}) diff --git a/resources/inputprofiles/CMakeLists.txt b/resources/inputprofiles/CMakeLists.txt index 0b3d1d8914..20ca4428a3 100644 --- a/resources/inputprofiles/CMakeLists.txt +++ b/resources/inputprofiles/CMakeLists.txt @@ -2,4 +2,18 @@ project(profiles) file(GLOB PROFILE_FILES "${CMAKE_CURRENT_SOURCE_DIR}/*.qxi") +# Only perform the copy if we're building for Android +if(ANDROID) + # Define the destination directory for the profile files within the Android package + set(PROFILES_ASSETS_DIR "${ANDROID_PACKAGE_SOURCE_DIR}/${INPUTPROFILEDIR}") + + # Create the profiles assets directory if it doesn't exist + file(MAKE_DIRECTORY ${PROFILES_ASSETS_DIR}) + + # Copy the profile .qxi files to the assets directory + foreach(PROFILE_FILE IN LISTS PROFILE_FILES) + file(COPY ${PROFILE_FILE} DESTINATION ${PROFILES_ASSETS_DIR}) + endforeach() +endif() + install(FILES ${PROFILE_FILES} DESTINATION ${INSTALLROOT}/${INPUTPROFILEDIR}) \ No newline at end of file diff --git a/resources/meshes/fixtures/CMakeLists.txt b/resources/meshes/fixtures/CMakeLists.txt index f69efed064..37caf67ec2 100644 --- a/resources/meshes/fixtures/CMakeLists.txt +++ b/resources/meshes/fixtures/CMakeLists.txt @@ -2,4 +2,18 @@ project(fixture_meshes) file(GLOB FIXTURE_MESH_FILES "${CMAKE_CURRENT_SOURCE_DIR}/*.dae") +# Only perform the copy if we're building for Android +if(ANDROID) + # Define the destination directory for the fixture mesh files within the Android package + set(FIXTURE_MESHES_ASSETS_DIR "${ANDROID_PACKAGE_SOURCE_DIR}/${MESHESDIR}/fixtures") + + # Create the fixture meshes assets directory if it doesn't exist + file(MAKE_DIRECTORY ${FIXTURE_MESHES_ASSETS_DIR}) + + # Copy the fixture mesh .dae files to the assets directory + foreach(FIXTURE_MESH_FILE IN LISTS FIXTURE_MESH_FILES) + file(COPY ${FIXTURE_MESH_FILE} DESTINATION ${FIXTURE_MESHES_ASSETS_DIR}) + endforeach() +endif() + install(FILES ${FIXTURE_MESH_FILES} DESTINATION ${INSTALLROOT}/${MESHESDIR}/fixtures) \ No newline at end of file diff --git a/resources/meshes/generic/CMakeLists.txt b/resources/meshes/generic/CMakeLists.txt index c6675cd7d4..6c3b384abb 100644 --- a/resources/meshes/generic/CMakeLists.txt +++ b/resources/meshes/generic/CMakeLists.txt @@ -2,4 +2,18 @@ project(generic_meshes) file(GLOB GENERIC_MESH_FILES "${CMAKE_CURRENT_SOURCE_DIR}/*.obj") +# Only perform the copy if we're building for Android +if(ANDROID) + # Define the destination directory for the generic mesh files within the Android package + set(GENERIC_MESHES_ASSETS_DIR "${ANDROID_PACKAGE_SOURCE_DIR}/${MESHESDIR}/generic") + + # Create the generic meshes assets directory if it doesn't exist + file(MAKE_DIRECTORY ${GENERIC_MESHES_ASSETS_DIR}) + + # Copy the generic mesh .obj files to the assets directory + foreach(GENERIC_MESH_FILE IN LISTS GENERIC_MESH_FILES) + file(COPY ${GENERIC_MESH_FILE} DESTINATION ${GENERIC_MESHES_ASSETS_DIR}) + endforeach() +endif() + install(FILES ${GENERIC_MESH_FILES} DESTINATION ${INSTALLROOT}/${MESHESDIR}/generic) \ No newline at end of file diff --git a/resources/meshes/stage/CMakeLists.txt b/resources/meshes/stage/CMakeLists.txt index f69501c915..ff879832ee 100644 --- a/resources/meshes/stage/CMakeLists.txt +++ b/resources/meshes/stage/CMakeLists.txt @@ -2,4 +2,18 @@ project(stage_meshes) file(GLOB STAGE_MESH_FILES "${CMAKE_CURRENT_SOURCE_DIR}/*.obj") +# Only perform the copy if we're building for Android +if(ANDROID) + # Define the destination directory for the stage mesh files within the Android package + set(STAGE_MESHES_ASSETS_DIR "${ANDROID_PACKAGE_SOURCE_DIR}/${MESHESDIR}/stage") + + # Create the stage meshes assets directory if it doesn't exist + file(MAKE_DIRECTORY ${STAGE_MESHES_ASSETS_DIR}) + + # Copy the stage mesh .obj files to the assets directory + foreach(STAGE_MESH_FILE IN LISTS STAGE_MESH_FILES) + file(COPY ${STAGE_MESH_FILE} DESTINATION ${STAGE_MESHES_ASSETS_DIR}) + endforeach() +endif() + install(FILES ${STAGE_MESH_FILES} DESTINATION ${INSTALLROOT}/${MESHESDIR}/stage) \ No newline at end of file diff --git a/resources/miditemplates/CMakeLists.txt b/resources/miditemplates/CMakeLists.txt index b8944b099c..4cd9a87b68 100644 --- a/resources/miditemplates/CMakeLists.txt +++ b/resources/miditemplates/CMakeLists.txt @@ -2,4 +2,18 @@ project(miditemplates) file(GLOB MIDITEMPLATE_FILES "${CMAKE_CURRENT_SOURCE_DIR}/*.qxm") +# Only perform the copy if we're building for Android +if(ANDROID) + # Define the destination directory for the miditemplate files within the Android package + set(MIDITEMPLATES_ASSETS_DIR "${ANDROID_PACKAGE_SOURCE_DIR}/${MIDITEMPLATEDIR}") + + # Create the miditemplates assets directory if it doesn't exist + file(MAKE_DIRECTORY ${MIDITEMPLATES_ASSETS_DIR}) + + # Copy the miditemplate .qxm files to the assets directory + foreach(MIDITEMPLATE_FILE IN LISTS MIDITEMPLATE_FILES) + file(COPY ${MIDITEMPLATE_FILE} DESTINATION ${MIDITEMPLATES_ASSETS_DIR}) + endforeach() +endif() + install(FILES ${MIDITEMPLATE_FILES} DESTINATION ${INSTALLROOT}/${MIDITEMPLATEDIR}) \ No newline at end of file diff --git a/resources/modifierstemplates/CMakeLists.txt b/resources/modifierstemplates/CMakeLists.txt index 001068d9ee..5ff28a0698 100644 --- a/resources/modifierstemplates/CMakeLists.txt +++ b/resources/modifierstemplates/CMakeLists.txt @@ -2,4 +2,18 @@ project(modtemplates) file(GLOB MODTEMPLATE_FILES "${CMAKE_CURRENT_SOURCE_DIR}/*.qxmt") +# Only perform the copy if we're building for Android +if(ANDROID) + # Define the destination directory for the modtemplate files within the Android package + set(MODTEMPLATES_ASSETS_DIR "${ANDROID_PACKAGE_SOURCE_DIR}/${MODIFIERSTEMPLATEDIR}") + + # Create the modtemplates assets directory if it doesn't exist + file(MAKE_DIRECTORY ${MODTEMPLATES_ASSETS_DIR}) + + # Copy the modtemplate .qxmt files to the assets directory + foreach(MODTEMPLATE_FILE IN LISTS MODTEMPLATE_FILES) + file(COPY ${MODTEMPLATE_FILE} DESTINATION ${MODTEMPLATES_ASSETS_DIR}) + endforeach() +endif() + install(FILES ${MODTEMPLATE_FILES} DESTINATION ${INSTALLROOT}/${MODIFIERSTEMPLATEDIR}) \ No newline at end of file diff --git a/resources/rgbscripts/CMakeLists.txt b/resources/rgbscripts/CMakeLists.txt index acd4510d1b..2089106f74 100644 --- a/resources/rgbscripts/CMakeLists.txt +++ b/resources/rgbscripts/CMakeLists.txt @@ -42,4 +42,18 @@ set(SCRIPT_FILES waves.js ) +# Only perform the copy if we're building for Android +if(ANDROID) + # Define the destination directory for the script files within the Android package + set(SCRIPTS_ASSETS_DIR "${ANDROID_PACKAGE_SOURCE_DIR}/${INSTALLROOT}/${RGBSCRIPTDIR}") + + # Create the scripts assets directory if it doesn't exist + file(MAKE_DIRECTORY ${SCRIPTS_ASSETS_DIR}) + + # Copy the script files to the assets directory + foreach(SCRIPT_FILE IN LISTS SCRIPT_FILES) + file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/${SCRIPT_FILE} DESTINATION ${SCRIPTS_ASSETS_DIR}) + endforeach() +endif() + install(FILES ${SCRIPT_FILES} DESTINATION ${INSTALLROOT}/${RGBSCRIPTDIR}) diff --git a/resources/samples/CMakeLists.txt b/resources/samples/CMakeLists.txt index b3a2ba2a0c..c1caac94ad 100644 --- a/resources/samples/CMakeLists.txt +++ b/resources/samples/CMakeLists.txt @@ -2,4 +2,18 @@ project(samples) file(GLOB SAMPLE_FILES "${CMAKE_CURRENT_SOURCE_DIR}/*.qxw") -install(FILES ${SAMPLE_FILES} DESTINATION ${SAMPLESDIR}) \ No newline at end of file +# Only perform the copy if we're building for Android +if(ANDROID) + # Define the destination directory for the sample files within the Android package + set(SAMPLES_ASSETS_DIR "${ANDROID_PACKAGE_SOURCE_DIR}/${SAMPLESDIR}") + + # Create the samples assets directory if it doesn't exist + file(MAKE_DIRECTORY ${SAMPLES_ASSETS_DIR}) + + # Copy the sample .qxw files to the assets directory + foreach(SAMPLE_FILE ${SAMPLE_FILES}) + file(COPY ${SAMPLE_FILE} DESTINATION ${SAMPLES_ASSETS_DIR}) + endforeach() +endif() + +install(FILES ${SAMPLE_FILES} DESTINATION ${SAMPLESDIR}) From 9466d920c29355b80c499674950f38a6afabc7c9 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Thu, 2 May 2024 11:25:00 +0200 Subject: [PATCH 176/212] Do not install docs --- resources/CMakeLists.txt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/resources/CMakeLists.txt b/resources/CMakeLists.txt index 1cc6a27d4e..42ff4fe251 100644 --- a/resources/CMakeLists.txt +++ b/resources/CMakeLists.txt @@ -1,9 +1,5 @@ project(resources) -if (NOT qmlui) - add_subdirectory(docs) -endif() - add_subdirectory(fixtures) add_subdirectory(gobos) add_subdirectory(inputprofiles) From 6bf200f03c60652367971be5fb7fe9c63a7669af Mon Sep 17 00:00:00 2001 From: LilyCalla Date: Thu, 2 May 2024 12:51:13 -0700 Subject: [PATCH 177/212] fixed code style issues --- qmlui/qml/fixturesfunctions/LeftPanel.qml | 3 ++- qmlui/treemodel.cpp | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/qmlui/qml/fixturesfunctions/LeftPanel.qml b/qmlui/qml/fixturesfunctions/LeftPanel.qml index a3ba6b44bf..679f59f5e3 100644 --- a/qmlui/qml/fixturesfunctions/LeftPanel.qml +++ b/qmlui/qml/fixturesfunctions/LeftPanel.qml @@ -98,7 +98,8 @@ SidePanel autoExclusive: false onToggled: { - if (checked == true) { + if (checked == true) + { loaderSource = "qrc:/FixtureGroupManager.qml" fixtureManager.searchFilter = "" } diff --git a/qmlui/treemodel.cpp b/qmlui/treemodel.cpp index c28d95797b..7149b521bf 100644 --- a/qmlui/treemodel.cpp +++ b/qmlui/treemodel.cpp @@ -202,7 +202,7 @@ TreeModelItem *TreeModel::itemAtPath(QString path) return nullptr; } - if(m_itemsPathMap.contains(pathList.at(0))) + if (m_itemsPathMap.contains(pathList.at(0))) return nullptr; TreeModelItem *item = m_itemsPathMap[pathList.at(0)]; QString subPath = path.mid(path.indexOf(TreeModel::separator()) + 1); @@ -272,7 +272,7 @@ void TreeModel::setItemRoleData(QString path, const QVariant &value, int role) } else { - if(!m_itemsPathMap.contains(pathList.at(0))) + if (!m_itemsPathMap.contains(pathList.at(0))) return; TreeModelItem *item = m_itemsPathMap[pathList.at(0)]; QString subPath = path.mid(path.indexOf(TreeModel::separator()) + 1); From ed21c72316d6cfee593c5f8819aed7f3ea098df0 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Thu, 2 May 2024 23:40:03 +0200 Subject: [PATCH 178/212] qmlui: fix preset tools visibility from channels --- qmlui/qml/ChannelToolLoader.qml | 44 +++++++++++-------- qmlui/qml/ColorTool.qml | 3 +- qmlui/qml/ColorToolPrimary.qml | 18 +++++++- qmlui/qml/SimpleDesk.qml | 7 ++- qmlui/qml/SingleAxisTool.qml | 6 +++ qmlui/qml/fixturesfunctions/IntensityTool.qml | 39 ++++++++-------- qmlui/qml/fixturesfunctions/LeftPanel.qml | 11 ++++- .../PresetCapabilityItem.qml | 12 ++--- qmlui/qml/fixturesfunctions/PresetsTool.qml | 7 +-- 9 files changed, 93 insertions(+), 54 deletions(-) diff --git a/qmlui/qml/ChannelToolLoader.qml b/qmlui/qml/ChannelToolLoader.qml index 011b41d81a..c00368254f 100644 --- a/qmlui/qml/ChannelToolLoader.qml +++ b/qmlui/qml/ChannelToolLoader.qml @@ -23,10 +23,10 @@ import QtQuick.Controls 2.5 import org.qlcplus.classes 1.0 import "." -Popup +Item { - id: popupRoot - padding: 0 + id: itemRoot + visible: false property int fixtureId: -1 property int channelType: -1 @@ -39,13 +39,14 @@ Popup function loadChannelTool(cItem, fxId, chIdx, val) { channelType = fixtureManager.channelType(fxId, chIdx) + if (channelType === QLCChannel.NoGroup || channelType === QLCChannel.Nothing) return var map = cItem.mapToItem(parent, cItem.x, cItem.y) toolLoader.source = "" - x = map.x - yPos = map.y + x = map.x + UISettings.iconSizeMedium + y = map.y fixtureId = fxId channelIndex = chIdx channelValue = val @@ -59,7 +60,6 @@ Popup case QLCChannel.Tilt: toolLoader.source = "qrc:/SingleAxisTool.qml" break - case QLCChannel.Colour: case QLCChannel.Gobo: case QLCChannel.Speed: @@ -82,34 +82,35 @@ Popup onLoaded: { - popupRoot.y = popupRoot.yPos - height - popupRoot.width = width - popupRoot.height = height + itemRoot.width = width + itemRoot.height = height item.showPalette = false - //item.closeOnSelect = true - - popupRoot.open() + if (item.hasOwnProperty('dragTarget')) + item.dragTarget = itemRoot if (channelType >= 0xFF) { - item.currentValue = popupRoot.channelValue - item.targetColor = popupRoot.channelType + item.currentValue = itemRoot.channelValue + item.targetColor = itemRoot.channelType } else if (channelType == QLCChannel.Intensity) { - item.currentValue = popupRoot.channelValue + item.show(itemRoot.channelValue) } else if (channelType == QLCChannel.Pan || channelType == QLCChannel.Tilt) { - item.currentValue = popupRoot.channelValue - item.maxDegrees = fixtureManager.channelDegrees(popupRoot.fixtureId, popupRoot.channelIndex) + item.currentValue = itemRoot.channelValue + item.maxDegrees = fixtureManager.channelDegrees(itemRoot.fixtureId, itemRoot.channelIndex) } else { - item.updatePresets(fixtureManager.presetChannel(popupRoot.fixtureId, popupRoot.channelIndex)) + item.updatePresets(fixtureManager.presetChannel(itemRoot.fixtureId, itemRoot.channelIndex)) } + + item.closeOnSelect = true + itemRoot.visible = true } Connections @@ -118,7 +119,12 @@ Popup target: toolLoader.item function onValueChanged() { - popupRoot.valueChanged(popupRoot.fixtureId, popupRoot.channelIndex, target.currentValue) + itemRoot.valueChanged(itemRoot.fixtureId, itemRoot.channelIndex, target.currentValue) + } + function onClose() + { + itemRoot.visible = false + toolLoader.source = "" } } } diff --git a/qmlui/qml/ColorTool.qml b/qmlui/qml/ColorTool.qml index 7cbd048994..5f92b432a6 100644 --- a/qmlui/qml/ColorTool.qml +++ b/qmlui/qml/ColorTool.qml @@ -33,6 +33,7 @@ Rectangle color: UISettings.bgMedium property bool closeOnSelect: false + property var dragTarget: null property int colorsMask: 0 property color currentRGB property color currentWAUV @@ -164,7 +165,7 @@ Rectangle { Layout.fillWidth: true height: colorToolBar.height - drag.target: paletteBox.isEditing ? null : colorToolBox + drag.target: paletteBox.isEditing ? null : (colorToolBox.dragTarget ? colorToolBox.dragTarget : colorToolBox) } GenericButton { diff --git a/qmlui/qml/ColorToolPrimary.qml b/qmlui/qml/ColorToolPrimary.qml index f3be683beb..5240ebe99e 100644 --- a/qmlui/qml/ColorToolPrimary.qml +++ b/qmlui/qml/ColorToolPrimary.qml @@ -38,6 +38,7 @@ Rectangle property bool showPalette: false signal valueChanged(int value) + signal close() Canvas { @@ -92,7 +93,8 @@ Rectangle { anchors.fill: parent - onClicked: { + function calculateValue(mouse) + { var val = 0 if (mouse.x < width * 0.1) @@ -112,6 +114,20 @@ Rectangle boxRoot.currentValue = val boxRoot.valueChanged(val) } + + onPressed: calculateValue() + onPositionChanged: + { + if (!pressed) + return + + calculateValue(mouse) + } + onReleased: + { + if (closeOnSelect) + boxRoot.close() + } } } } diff --git a/qmlui/qml/SimpleDesk.qml b/qmlui/qml/SimpleDesk.qml index a9570025a8..0f0beb57d5 100644 --- a/qmlui/qml/SimpleDesk.qml +++ b/qmlui/qml/SimpleDesk.qml @@ -46,6 +46,7 @@ Rectangle { anchors.fill: parent orientation: Qt.Vertical + z: 1 // Top view (faders) Rectangle @@ -254,7 +255,8 @@ Rectangle from: 0 to: 255 value: model.chValue - onMoved: { + onMoved: + { model.isOverride = true model.chValue = valueAt(position) simpleDesk.setValue(fixtureObj ? fixtureObj.id : -1, fixtureObj ? model.chIndex : index, model.chValue) @@ -274,7 +276,8 @@ Rectangle padding: 0 horizontalAlignment: Qt.AlignHCenter value: dmxValues ? model.chValue : (model.chValue / 255.0) * 100.0 - onValueModified: { + onValueModified: + { model.isOverride = true model.chValue = value * (dmxValues ? 1.0 : 2.55) simpleDesk.setValue(fixtureObj ? fixtureObj.id : -1, fixtureObj ? model.chIndex : index, model.chValue) diff --git a/qmlui/qml/SingleAxisTool.qml b/qmlui/qml/SingleAxisTool.qml index 965492cd8f..9d773934d0 100644 --- a/qmlui/qml/SingleAxisTool.qml +++ b/qmlui/qml/SingleAxisTool.qml @@ -39,6 +39,7 @@ Rectangle property bool showPalette: false signal valueChanged(int value) + signal close() GridLayout { @@ -60,6 +61,11 @@ Rectangle currentValue = Math.round((valueAt(position) * 255.0) / maxDegrees) boxRoot.valueChanged(currentValue) } + onPressedChanged: + { + if (!pressed && closeOnSelect) + boxRoot.close() + } } RobotoText diff --git a/qmlui/qml/fixturesfunctions/IntensityTool.qml b/qmlui/qml/fixturesfunctions/IntensityTool.qml index 61ebe7ca3f..8192a71950 100644 --- a/qmlui/qml/fixturesfunctions/IntensityTool.qml +++ b/qmlui/qml/fixturesfunctions/IntensityTool.qml @@ -35,6 +35,7 @@ Rectangle property bool dmxValues: true property bool closeOnSelect: false + property var dragTarget: null property alias showPalette: paletteBox.visible property alias currentValue: spinBox.value @@ -55,34 +56,34 @@ Rectangle else { var val = relativeValue ? currentValue - previousValue : currentValue - intRoot.valueChanged(dmxValues ? val : val * 2.55) - if (closeOnSelect) - intRoot.visible = false + if (intRoot.visible) + intRoot.valueChanged(dmxValues ? val : val * 2.55) + //if (closeOnSelect) + // intRoot.close() } previousValue = currentValue } onVisibleChanged: { - if (visible) + if (!visible) + paletteBox.checked = false + } + + function show(value) + { + previousValue = 0 + if (value === -1) { - previousValue = 0 - var val = contextManager.getCurrentValue(QLCChannel.Intensity, false) - if (val === -1) - { - relativeValue = true - currentValue = 0 - } - else - { - relativeValue = false - currentValue = dmxValues ? Math.round(val) : Math.round(val / 2.55) - } + relativeValue = true + currentValue = 0 } else { - paletteBox.checked = false + relativeValue = false + currentValue = dmxValues ? Math.round(value) : Math.round(value / 2.55) } + visible = true } function loadPalette(pId) @@ -137,7 +138,7 @@ Rectangle MouseArea { anchors.fill: parent - drag.target: intRoot + drag.target: intRoot.dragTarget ? intRoot.dragTarget : intRoot } GenericButton @@ -232,7 +233,7 @@ Rectangle suffix: dmxValues ? "" : "%" to: dmxValues ? 255 : 100 - onValueChanged: currentValue = value + onValueModified: currentValue = value } DMXPercentageButton diff --git a/qmlui/qml/fixturesfunctions/LeftPanel.qml b/qmlui/qml/fixturesfunctions/LeftPanel.qml index afab32e9f5..7c941f6d38 100644 --- a/qmlui/qml/fixturesfunctions/LeftPanel.qml +++ b/qmlui/qml/fixturesfunctions/LeftPanel.qml @@ -133,7 +133,16 @@ SidePanel tooltip: qsTr("Intensity") counter: 0 ButtonGroup.group: capabilitiesGroup - onCheckedChanged: intTool.visible = !intTool.visible + onCheckedChanged: + { + if (checked) + { + var val = contextManager.getCurrentValue(QLCChannel.Intensity, false) + intTool.show(val) + } + else + intTool.visible = false + } onCounterChanged: if (counter == 0) intTool.visible = false IntensityTool diff --git a/qmlui/qml/fixturesfunctions/PresetCapabilityItem.qml b/qmlui/qml/fixturesfunctions/PresetCapabilityItem.qml index 09da359ea4..922fe02773 100644 --- a/qmlui/qml/fixturesfunctions/PresetCapabilityItem.qml +++ b/qmlui/qml/fixturesfunctions/PresetCapabilityItem.qml @@ -27,6 +27,7 @@ Rectangle id: iRoot width: UISettings.bigItemHeight * 1.5 height: UISettings.iconSizeDefault * 1.2 + color: capMouseArea.pressed ? UISettings.bgLight : "white" border.width: 1 border.color: UISettings.borderColorDark @@ -137,22 +138,17 @@ 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 slMin = (sliderRoot && sliderRoot.sliderObj) ? sliderRoot.sliderObj.rangeLowLimit : 0 - var slMax = (sliderRoot && sliderRoot.sliderObj) ? sliderRoot.sliderObj.rangeHighLimit : 255 - var cMin = Math.max(capability.min, slMin) - var cMax = Math.min(capability.max, slMax) - var value = Math.round(((cMax - cMin) * capBar.width) / iRoot.width) - //console.log("max: " + capability.max + "|" + slMax + "|" + cMax + " min: " + capability.min + "|" + slMin + "|" + cMin + " value: " + value) + var value = ((capability.max - capability.min) * capBar.width) / iRoot.width + //console.log("max: " + capability.max + " min: " + capability.min + " value: " + value) valueChanged(value + capability.min) } } diff --git a/qmlui/qml/fixturesfunctions/PresetsTool.qml b/qmlui/qml/fixturesfunctions/PresetsTool.qml index 5ff1b0a96b..f239becbda 100644 --- a/qmlui/qml/fixturesfunctions/PresetsTool.qml +++ b/qmlui/qml/fixturesfunctions/PresetsTool.qml @@ -157,9 +157,10 @@ Rectangle visible: (capability.min <= toolRoot.rangeHighLimit || capability.max <= toolRoot.rangeLowLimit) onValueChanged: { - toolRoot.currentValue = value - toolRoot.presetSelected(capability, selectedFixture, selectedChannel, value) - toolRoot.valueChanged(value) + var val = Math.min(Math.max(value, rangeLowLimit), rangeHighLimit) + toolRoot.currentValue = val + toolRoot.presetSelected(capability, selectedFixture, selectedChannel, val) + toolRoot.valueChanged(val) if (closeOnSelect) toolRoot.visible = false } From d8ff203fec5ec578a8b137a1b39a4b3ce71b3266 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Fri, 3 May 2024 12:28:21 +0200 Subject: [PATCH 179/212] qmlui: workaround 3d fixture items deletion reported: https://www.qlcplus.org/forum/viewtopic.php?t=16990 --- qmlui/mainview3d.cpp | 13 +++++++------ qmlui/qml/fixturesfunctions/3DView/3DView.qml | 10 ---------- 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/qmlui/mainview3d.cpp b/qmlui/mainview3d.cpp index 55f8e28c34..cc1d8c0a02 100644 --- a/qmlui/mainview3d.cpp +++ b/qmlui/mainview3d.cpp @@ -166,13 +166,11 @@ void MainView3D::resetItems() { it.next(); SceneItem *e = it.value(); - //if (e->m_headItem) - // delete e->m_headItem; - //if (e->m_armItem) - // delete e->m_armItem; delete e->m_goboTexture; - // delete e->m_rootItem; // TODO: with this -> segfault delete e->m_selectionBox; + // delete e->m_rootItem; // TODO: with this -> segfault + e->m_rootItem->setProperty("enabled", false); // workaround for the above + delete e; } //const auto end = m_entitiesMap.end(); @@ -1502,8 +1500,11 @@ void MainView3D::removeFixtureItem(quint32 itemID) SceneItem *mesh = m_entitiesMap.take(itemID); - delete mesh->m_rootItem; + delete mesh->m_goboTexture; delete mesh->m_selectionBox; + delete mesh->m_rootTransform; +// delete mesh->m_rootItem; // this will cause a segfault + mesh->m_rootItem->setProperty("enabled", false); // workaround for the above delete mesh; } diff --git a/qmlui/qml/fixturesfunctions/3DView/3DView.qml b/qmlui/qml/fixturesfunctions/3DView/3DView.qml index a4d5034f8d..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 { From cd25ed0dd6255cc95b24e8437a96302c9ea5fdab Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Fri, 3 May 2024 12:53:49 +0200 Subject: [PATCH 180/212] qmlui: fix fixture selection reset when no Function is being edited reported: https://www.qlcplus.org/forum/viewtopic.php?t=17130 --- qmlui/contextmanager.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/qmlui/contextmanager.cpp b/qmlui/contextmanager.cpp index 2f429f4c24..a5752e4b49 100644 --- a/qmlui/contextmanager.cpp +++ b/qmlui/contextmanager.cpp @@ -1628,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; } From b1d714c3ff33cba5f5f4aa976653e23dff48c536 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Sat, 4 May 2024 12:45:45 +0200 Subject: [PATCH 181/212] macos: fix launcher cmake build --- launcher/CMakeLists.txt | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 8a84ad7127..2c42d80c52 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -19,7 +19,7 @@ else() qt5_add_translation(QM_FILES ${TS_FILES}) endif() -add_executable(${module_name} WIN32 MACOSX_BUNDLE +add_executable(${module_name} MACOSX_BUNDLE launcher.cpp launcher.h main.cpp ${QM_FILES} @@ -36,7 +36,6 @@ target_link_libraries(${module_name} PRIVATE Qt${QT_MAJOR_VERSION}::Widgets ) - # Resources: set_source_files_properties("../resources/icons/png/qlcplus-fixtureeditor.png" PROPERTIES QT_RESOURCE_ALIAS "qlcplus-fixtureeditor.png" @@ -49,12 +48,20 @@ set(launcher_resource_files "../resources/icons/png/qlcplus.png" ) -qt_add_resources(${module_name} "launcher" +if(QT_VERSION_MAJOR GREATER 5) + qt_add_resources(${module_name} "launcher" PREFIX "/" + BASE + "." FILES ${launcher_resource_files} ) +else() + qt5_add_resources($launcher.qrc) + target_sources(${module_name} PRIVATE + ${launcher_resource_files}) +endif() install(TARGETS ${module_name} DESTINATION ${INSTALLROOT}/${BINDIR} From 01fdda185ed1c008e0605669ec29dac4963511aa Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Mon, 6 May 2024 23:37:22 +0200 Subject: [PATCH 182/212] ui: fix deprecation of combobox activated signal --- ui/src/addfixture.cpp | 12 +++++------ ui/src/addfixture.h | 2 +- ui/src/efxeditor.cpp | 13 ++++++------ ui/src/efxeditor.h | 2 +- ui/src/inputchanneleditor.cpp | 8 +++---- ui/src/inputchanneleditor.h | 2 +- ui/src/rgbmatrixeditor.cpp | 39 +++++++++++++++++++---------------- ui/src/rgbmatrixeditor.h | 6 +++--- 8 files changed, 44 insertions(+), 40 deletions(-) diff --git a/ui/src/addfixture.cpp b/ui/src/addfixture.cpp index 948e64f675..d9fa15add5 100644 --- a/ui/src/addfixture.cpp +++ b/ui/src/addfixture.cpp @@ -72,8 +72,8 @@ AddFixture::AddFixture(QWidget* parent, const Doc* doc, const Fixture* fxi) this, SLOT(slotSelectionChanged())); connect(m_tree, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), this, SLOT(slotTreeDoubleClicked(QTreeWidgetItem*))); - connect(m_modeCombo, SIGNAL(activated(const QString&)), - this, SLOT(slotModeActivated(const QString&))); + connect(m_modeCombo, SIGNAL(activated(int)), + this, SLOT(slotModeActivated(int))); connect(m_universeCombo, SIGNAL(activated(int)), this, SLOT(slotUniverseActivated(int))); connect(m_addressSpin, SIGNAL(valueChanged(int)), @@ -130,7 +130,7 @@ AddFixture::AddFixture(QWidget* parent, const Doc* doc, const Fixture* fxi) { m_channelsSpin->setValue(fxi->channels()); m_modeCombo->setCurrentIndex(index); - slotModeActivated(m_modeCombo->itemText(index)); + slotModeActivated(index); } } else @@ -347,7 +347,7 @@ void AddFixture::fillModeCombo(const QString& text) /* Select the first mode by default */ m_modeCombo->setCurrentIndex(0); - slotModeActivated(m_modeCombo->currentText()); + slotModeActivated(0); } } @@ -445,12 +445,12 @@ bool AddFixture::checkAddressAvailability(int value, int channels) * Slots *****************************************************************************/ -void AddFixture::slotModeActivated(const QString& modeName) +void AddFixture::slotModeActivated(int modeIndex) { if (m_fixtureDef == NULL) return; - m_mode = m_fixtureDef->mode(modeName); + m_mode = m_fixtureDef->modes().at(modeIndex); if (m_mode == NULL) { /* Generic dimmers don't have modes, so bail out */ diff --git a/ui/src/addfixture.h b/ui/src/addfixture.h index e8e3d7d82a..840733355d 100644 --- a/ui/src/addfixture.h +++ b/ui/src/addfixture.h @@ -164,7 +164,7 @@ class AddFixture : public QDialog, public Ui_AddFixture *********************************************************************/ protected slots: /** Callback for mode selection changes */ - void slotModeActivated(const QString& modeName); + void slotModeActivated(int modeIndex); /** Callback for universe combo activations */ void slotUniverseActivated(int universe); diff --git a/ui/src/efxeditor.cpp b/ui/src/efxeditor.cpp index fabad0db76..f471766789 100644 --- a/ui/src/efxeditor.cpp +++ b/ui/src/efxeditor.cpp @@ -203,18 +203,20 @@ void EFXEditor::initMovementPage() m_algorithmCombo->addItems(EFX::algorithmList()); QString algo(EFX::algorithmToString(m_efx->algorithm())); + int algoIndex = 0; /* Select the EFX's algorithm from the algorithm combo */ for (int i = 0; i < m_algorithmCombo->count(); i++) { if (m_algorithmCombo->itemText(i) == algo) { m_algorithmCombo->setCurrentIndex(i); + algoIndex = i; break; } } /* Causes the EFX function to update the preview point array */ - slotAlgorithmSelected(algo); + slotAlgorithmSelected(algoIndex); /* Get the algorithm parameters */ m_widthSpin->setValue(m_efx->width()); @@ -269,8 +271,8 @@ void EFXEditor::initMovementPage() connect(m_backward, SIGNAL(clicked()), this, SLOT(slotBackwardClicked())); - connect(m_algorithmCombo, SIGNAL(activated(const QString&)), - this, SLOT(slotAlgorithmSelected(const QString&))); + connect(m_algorithmCombo, SIGNAL(activated(int)), + this, SLOT(slotAlgorithmSelected(int))); connect(m_widthSpin, SIGNAL(valueChanged(int)), this, SLOT(slotWidthSpinChanged(int))); connect(m_heightSpin, SIGNAL(valueChanged(int)), @@ -884,12 +886,11 @@ void EFXEditor::slotFixtureChanged() * Movement page *****************************************************************************/ -void EFXEditor::slotAlgorithmSelected(const QString &text) +void EFXEditor::slotAlgorithmSelected(int algoIndex) { Q_ASSERT(m_efx != NULL); - EFX::Algorithm algo = EFX::stringToAlgorithm(text); - m_efx->setAlgorithm(algo); + m_efx->setAlgorithm(EFX::Algorithm(algoIndex)); if (m_efx->isFrequencyEnabled()) { diff --git a/ui/src/efxeditor.h b/ui/src/efxeditor.h index 6a5085cf35..b23bec9447 100644 --- a/ui/src/efxeditor.h +++ b/ui/src/efxeditor.h @@ -135,7 +135,7 @@ private slots: * Movement page *********************************************************************/ private slots: - void slotAlgorithmSelected(const QString &text); + void slotAlgorithmSelected(int algoIndex); void slotWidthSpinChanged(int value); void slotHeightSpinChanged(int value); void slotXOffsetSpinChanged(int value); diff --git a/ui/src/inputchanneleditor.cpp b/ui/src/inputchanneleditor.cpp index eb2386a20b..5756f2ba54 100644 --- a/ui/src/inputchanneleditor.cpp +++ b/ui/src/inputchanneleditor.cpp @@ -76,8 +76,8 @@ InputChannelEditor::InputChannelEditor(QWidget* parent, this, SLOT(slotNumberChanged(int))); connect(m_nameEdit, SIGNAL(textEdited(const QString&)), this, SLOT(slotNameEdited(const QString&))); - connect(m_typeCombo, SIGNAL(activated(const QString&)), - this, SLOT(slotTypeActivated(const QString &))); + connect(m_typeCombo, SIGNAL(activated(int)), + this, SLOT(slotTypeActivated(int))); /* Fill type combo with type icons and names */ QStringListIterator it(QLCInputChannel::types()); @@ -182,9 +182,9 @@ void InputChannelEditor::slotNameEdited(const QString& text) m_name = text; } -void InputChannelEditor::slotTypeActivated(const QString& text) +void InputChannelEditor::slotTypeActivated(int index) { - m_type = QLCInputChannel::stringToType(text); + m_type = QLCInputChannel::stringToType(m_typeCombo->itemText(index)); } /**************************************************************************** diff --git a/ui/src/inputchanneleditor.h b/ui/src/inputchanneleditor.h index e2f511c375..1a12602fbf 100644 --- a/ui/src/inputchanneleditor.h +++ b/ui/src/inputchanneleditor.h @@ -58,7 +58,7 @@ class InputChannelEditor : public QDialog, public Ui_InputChannelEditor protected slots: void slotNumberChanged(int number); void slotNameEdited(const QString& text); - void slotTypeActivated(const QString& text); + void slotTypeActivated(int index); protected: quint32 m_channel; diff --git a/ui/src/rgbmatrixeditor.cpp b/ui/src/rgbmatrixeditor.cpp index 25ec5f4c4b..ee0844b2ae 100644 --- a/ui/src/rgbmatrixeditor.cpp +++ b/ui/src/rgbmatrixeditor.cpp @@ -194,8 +194,8 @@ void RGBMatrixEditor::init() this, SLOT(slotSaveToSequenceClicked())); connect(m_shapeButton, SIGNAL(toggled(bool)), this, SLOT(slotShapeToggle(bool))); - connect(m_patternCombo, SIGNAL(activated(const QString&)), - this, SLOT(slotPatternActivated(const QString&))); + connect(m_patternCombo, SIGNAL(activated(int)), + this, SLOT(slotPatternActivated(int))); connect(m_fixtureGroupCombo, SIGNAL(activated(int)), this, SLOT(slotFixtureGroupActivated(int))); connect(m_blendModeCombo, SIGNAL(activated(int)), @@ -212,14 +212,14 @@ void RGBMatrixEditor::init() this, SLOT(slotTextEdited(const QString&))); connect(m_fontButton, SIGNAL(clicked()), this, SLOT(slotFontButtonClicked())); - connect(m_animationCombo, SIGNAL(activated(const QString&)), - this, SLOT(slotAnimationActivated(const QString&))); + connect(m_animationCombo, SIGNAL(activated(int)), + this, SLOT(slotAnimationActivated(int))); connect(m_imageEdit, SIGNAL(editingFinished()), this, SLOT(slotImageEdited())); connect(m_imageButton, SIGNAL(clicked()), this, SLOT(slotImageButtonClicked())); - connect(m_imageAnimationCombo, SIGNAL(activated(const QString&)), - this, SLOT(slotImageAnimationActivated(const QString&))); + connect(m_imageAnimationCombo, SIGNAL(activated(int)), + this, SLOT(slotImageAnimationActivated(int))); connect(m_xOffsetSpin, SIGNAL(valueChanged(int)), this, SLOT(slotOffsetSpinChanged())); connect(m_yOffsetSpin, SIGNAL(valueChanged(int)), @@ -721,9 +721,10 @@ void RGBMatrixEditor::slotDialDestroyed(QObject *) m_speedDialButton->setChecked(false); } -void RGBMatrixEditor::slotPatternActivated(const QString& text) +void RGBMatrixEditor::slotPatternActivated(int patternIndex) { - RGBAlgorithm* algo = RGBAlgorithm::algorithm(m_doc, text); + QString algoName = m_patternCombo->itemText(patternIndex); + RGBAlgorithm *algo = RGBAlgorithm::algorithm(m_doc, algoName); if (algo != NULL) algo->setColors(m_matrix->startColor(), m_matrix->endColor()); m_matrix->setAlgorithm(algo); @@ -808,7 +809,7 @@ void RGBMatrixEditor::slotTextEdited(const QString& text) { if (m_matrix->algorithm() != NULL && m_matrix->algorithm()->type() == RGBAlgorithm::Text) { - RGBText* algo = static_cast (m_matrix->algorithm()); + RGBText *algo = static_cast (m_matrix->algorithm()); Q_ASSERT(algo != NULL); { QMutexLocker algorithmLocker(&m_matrix->algorithmMutex()); @@ -822,7 +823,7 @@ void RGBMatrixEditor::slotFontButtonClicked() { if (m_matrix->algorithm() != NULL && m_matrix->algorithm()->type() == RGBAlgorithm::Text) { - RGBText* algo = static_cast (m_matrix->algorithm()); + RGBText *algo = static_cast (m_matrix->algorithm()); Q_ASSERT(algo != NULL); bool ok = false; @@ -838,14 +839,15 @@ void RGBMatrixEditor::slotFontButtonClicked() } } -void RGBMatrixEditor::slotAnimationActivated(const QString& text) +void RGBMatrixEditor::slotAnimationActivated(int index) { if (m_matrix->algorithm() != NULL && m_matrix->algorithm()->type() == RGBAlgorithm::Text) { - RGBText* algo = static_cast (m_matrix->algorithm()); + RGBText *algo = static_cast (m_matrix->algorithm()); Q_ASSERT(algo != NULL); { QMutexLocker algorithmLocker(&m_matrix->algorithmMutex()); + QString text = m_animationCombo->itemText(index); algo->setAnimationStyle(RGBText::stringToAnimationStyle(text)); } slotRestartTest(); @@ -856,7 +858,7 @@ void RGBMatrixEditor::slotImageEdited() { if (m_matrix->algorithm() != NULL && m_matrix->algorithm()->type() == RGBAlgorithm::Image) { - RGBImage* algo = static_cast (m_matrix->algorithm()); + RGBImage *algo = static_cast (m_matrix->algorithm()); Q_ASSERT(algo != NULL); { QMutexLocker algorithmLocker(&m_matrix->algorithmMutex()); @@ -870,7 +872,7 @@ void RGBMatrixEditor::slotImageButtonClicked() { if (m_matrix->algorithm() != NULL && m_matrix->algorithm()->type() == RGBAlgorithm::Image) { - RGBImage* algo = static_cast (m_matrix->algorithm()); + RGBImage *algo = static_cast (m_matrix->algorithm()); Q_ASSERT(algo != NULL); QString path = algo->filename(); @@ -890,14 +892,15 @@ void RGBMatrixEditor::slotImageButtonClicked() } } -void RGBMatrixEditor::slotImageAnimationActivated(const QString& text) +void RGBMatrixEditor::slotImageAnimationActivated(int index) { if (m_matrix->algorithm() != NULL && m_matrix->algorithm()->type() == RGBAlgorithm::Image) { - RGBImage* algo = static_cast (m_matrix->algorithm()); + RGBImage *algo = static_cast (m_matrix->algorithm()); Q_ASSERT(algo != NULL); { QMutexLocker algorithmLocker(&m_matrix->algorithmMutex()); + QString text = m_imageAnimationCombo->itemText(index); algo->setAnimationStyle(RGBImage::stringToAnimationStyle(text)); } slotRestartTest(); @@ -908,7 +911,7 @@ void RGBMatrixEditor::slotOffsetSpinChanged() { if (m_matrix->algorithm() != NULL && m_matrix->algorithm()->type() == RGBAlgorithm::Text) { - RGBText* algo = static_cast (m_matrix->algorithm()); + RGBText *algo = static_cast (m_matrix->algorithm()); Q_ASSERT(algo != NULL); { QMutexLocker algorithmLocker(&m_matrix->algorithmMutex()); @@ -920,7 +923,7 @@ void RGBMatrixEditor::slotOffsetSpinChanged() if (m_matrix->algorithm() != NULL && m_matrix->algorithm()->type() == RGBAlgorithm::Image) { - RGBImage* algo = static_cast (m_matrix->algorithm()); + RGBImage *algo = static_cast (m_matrix->algorithm()); Q_ASSERT(algo != NULL); { QMutexLocker algorithmLocker(&m_matrix->algorithmMutex()); diff --git a/ui/src/rgbmatrixeditor.h b/ui/src/rgbmatrixeditor.h index 03431b6b8e..4f0abf863c 100644 --- a/ui/src/rgbmatrixeditor.h +++ b/ui/src/rgbmatrixeditor.h @@ -73,7 +73,7 @@ private slots: void slotPreviewTimeout(); void slotNameEdited(const QString& text); void slotSpeedDialToggle(bool state); - void slotPatternActivated(const QString& text); + void slotPatternActivated(int patternIndex); void slotFixtureGroupActivated(int index); void slotBlendModeChanged(int index); void slotControlModeChanged(int index); @@ -83,12 +83,12 @@ private slots: void slotTextEdited(const QString& text); void slotFontButtonClicked(); - void slotAnimationActivated(const QString& text); + void slotAnimationActivated(int index); void slotOffsetSpinChanged(); void slotImageEdited(); void slotImageButtonClicked(); - void slotImageAnimationActivated(const QString& text); + void slotImageAnimationActivated(int index); void slotLoopClicked(); void slotPingPongClicked(); From 9ab04d0a2a7d86a2f9a7c561b9ff18bec58d3e70 Mon Sep 17 00:00:00 2001 From: LilyCalla Date: Fri, 10 May 2024 16:24:30 -0700 Subject: [PATCH 183/212] refresh cue list widget on add or remove --- engine/src/chaser.cpp | 2 ++ engine/src/chaser.h | 1 + qmlui/virtualconsole/vccuelist.cpp | 7 +++++++ qmlui/virtualconsole/vccuelist.h | 1 + 4 files changed, 11 insertions(+) diff --git a/engine/src/chaser.cpp b/engine/src/chaser.cpp index 3a127628e9..26b04b4378 100644 --- a/engine/src/chaser.cpp +++ b/engine/src/chaser.cpp @@ -132,6 +132,7 @@ bool Chaser::addStep(const ChaserStep& step, int index) } emit changed(this->id()); + emit stepListChange(this->id()); return true; } else @@ -150,6 +151,7 @@ bool Chaser::removeStep(int index) } emit changed(this->id()); + emit stepListChange(this->id()); return true; } else diff --git a/engine/src/chaser.h b/engine/src/chaser.h index e7d0ebc511..4ec9113cf8 100644 --- a/engine/src/chaser.h +++ b/engine/src/chaser.h @@ -157,6 +157,7 @@ public slots: signals: void stepChanged(int index); + void stepListChange(quint32 fid); protected: QList m_steps; diff --git a/qmlui/virtualconsole/vccuelist.cpp b/qmlui/virtualconsole/vccuelist.cpp index efc90202db..594586d2c3 100644 --- a/qmlui/virtualconsole/vccuelist.cpp +++ b/qmlui/virtualconsole/vccuelist.cpp @@ -488,6 +488,8 @@ void VCCueList::setChaserID(quint32 fid) this, SLOT(slotCurrentStepChanged(int))); connect(function, SIGNAL(stepChanged(int)), this, SLOT(slotStepChanged(int))); + connect(function, SIGNAL(stepListChange(quint32)), + this, SLOT(slotStepListChange(quint32))); emit chaserIDChanged(fid); } @@ -518,6 +520,11 @@ void VCCueList::slotFunctionRemoved(quint32 fid) } } +void VCCueList::slotStepListChange(quint32 fid) { + if (fid == m_chaserID) + ChaserEditor::updateStepsList(m_doc, chaser(), m_stepsList); +} + void VCCueList::slotStepChanged(int index) { ChaserStep *step = chaser()->stepAt(index); diff --git a/qmlui/virtualconsole/vccuelist.h b/qmlui/virtualconsole/vccuelist.h index 8d7bfe6c63..98867ef86f 100644 --- a/qmlui/virtualconsole/vccuelist.h +++ b/qmlui/virtualconsole/vccuelist.h @@ -188,6 +188,7 @@ private slots: void slotFunctionRemoved(quint32 fid); void slotFunctionNameChanged(quint32 fid); void slotStepChanged(int index); + void slotStepListChange(quint32 fid); private: FunctionParent functionParent() const; From 810efe3384bfcff6d18a885927499ede7ad535b6 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Sat, 11 May 2024 17:20:54 +0200 Subject: [PATCH 184/212] ui/vcbutton: indentation --- ui/src/virtualconsole/vcbutton.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/src/virtualconsole/vcbutton.cpp b/ui/src/virtualconsole/vcbutton.cpp index 616c0e64c6..d8bbc1873a 100644 --- a/ui/src/virtualconsole/vcbutton.cpp +++ b/ui/src/virtualconsole/vcbutton.cpp @@ -1117,7 +1117,7 @@ void VCButton::paintEvent(QPaintEvent* e) if (state() == Active) { - if(m_flashForceLTP || m_flashOverrides) + if (m_flashForceLTP || m_flashOverrides) painter.setBrush(QBrush(QColor(230, 0, 0, 255))); else painter.setBrush(QBrush(QColor(0, 230, 0, 255))); @@ -1149,7 +1149,7 @@ void VCButton::paintEvent(QPaintEvent* e) painter.setPen(QPen(QColor(255, 170, 0, 255), borderWidth)); else { - if(m_flashForceLTP || m_flashOverrides) + if (m_flashForceLTP || m_flashOverrides) painter.setPen(QPen(QColor(230, 0, 0, 255), borderWidth)); else painter.setPen(QPen(QColor(0, 230, 0, 255), borderWidth)); From a1f6cd7d6eedb3ce056200b8fd94a7e583ac79dd Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Sat, 11 May 2024 17:24:42 +0200 Subject: [PATCH 185/212] vc/slider: add an optional button to flash in playback mode (fix #1524) --- debian/changelog | 1 + ui/src/virtualconsole/vcslider.cpp | 125 +++++++++++++++++++ ui/src/virtualconsole/vcslider.h | 45 ++++++- ui/src/virtualconsole/vcsliderproperties.cpp | 50 ++++++-- ui/src/virtualconsole/vcsliderproperties.h | 6 + ui/src/virtualconsole/vcsliderproperties.ui | 10 ++ 6 files changed, 223 insertions(+), 14 deletions(-) diff --git a/debian/changelog b/debian/changelog index 1784b22931..66d4cd1e6f 100644 --- a/debian/changelog +++ b/debian/changelog @@ -5,6 +5,7 @@ qlcplus (4.13.1) stable; urgency=low * engine: fix RGB Matrix clone control mode * Show Manager: improve resume after pause * Virtual Console: fix OSC feedback regression + * Virtual Console/Slider: add an optional button to flash in playback mode * Virtual Console/XY Pad: copy presets when cloning (thanks to Hans-Jürgen Tappe) * Plugins/DMX USB: restore Vince DMX512 output (thanks to Jérôme Lebleu) * New fixtures: beamZ BAC500 and BAC506 (thanks to Olivier Michel) diff --git a/ui/src/virtualconsole/vcslider.cpp b/ui/src/virtualconsole/vcslider.cpp index a2831802cd..fcfc70d0bb 100644 --- a/ui/src/virtualconsole/vcslider.cpp +++ b/ui/src/virtualconsole/vcslider.cpp @@ -59,6 +59,7 @@ const quint8 VCSlider::sliderInputSourceId = 0; const quint8 VCSlider::overrideResetInputSourceId = 1; +const quint8 VCSlider::flashButtonInputSourceId = 2; const QSize VCSlider::defaultSize(QSize(60, 200)); @@ -94,6 +95,8 @@ VCSlider::VCSlider(QWidget *parent, Doc *doc) , m_playbackFunction(Function::invalidId()) , m_playbackValue(0) , m_playbackChangeCounter(0) + , m_playbackFlashEnable(false) + , m_playbackIsFlashing(false) , m_externalMovement(false) , m_widgetMode(WSlider) , m_cngType(ClickAndGoWidget::None) @@ -174,6 +177,7 @@ VCSlider::VCSlider(QWidget *parent, Doc *doc) this, SLOT(slotMonitorDMXValueChanged(int))); m_resetButton = NULL; + m_flashButton = NULL; /* Bottom label */ m_bottomLabel = new QLabel(this); @@ -303,6 +307,8 @@ void VCSlider::enableWidgetUI(bool enable) m_cngButton->setEnabled(enable); if (m_resetButton) m_resetButton->setEnabled(enable); + if (m_flashButton) + m_flashButton->setEnabled(enable); if (enable == false) m_lastInputValue = -1; } @@ -883,6 +889,51 @@ void VCSlider::slotKeyPressed(const QKeySequence &keySequence) if (m_overrideResetKeySequence == keySequence) slotResetButtonClicked(); + else if (m_playbackFlashKeySequence == keySequence) + flashPlayback(true); +} + +void VCSlider::slotKeyReleased(const QKeySequence &keySequence) +{ + if (m_playbackFlashKeySequence == keySequence && m_playbackIsFlashing) + flashPlayback(false); +} + +/********************************************************************* + * Flash button + *********************************************************************/ + +QKeySequence VCSlider::playbackFlashKeySequence() const +{ + return m_playbackFlashKeySequence; +} + +void VCSlider::setPlaybackFlashKeySequence(const QKeySequence &keySequence) +{ + m_playbackFlashKeySequence = QKeySequence(keySequence); +} + +void VCSlider::mousePressEvent(QMouseEvent *e) +{ + VCWidget::mousePressEvent(e); + + if (mode() != Doc::Design && e->button() == Qt::LeftButton && + m_flashButton && m_flashButton->isDown()) + { + flashPlayback(true); + } +} + +void VCSlider::mouseReleaseEvent(QMouseEvent *e) +{ + if (mode() == Doc::Design) + { + VCWidget::mouseReleaseEvent(e); + } + else if (m_playbackIsFlashing) + { + flashPlayback(false); + } } /***************************************************************************** @@ -983,6 +1034,43 @@ void VCSlider::notifyFunctionStarting(quint32 fid, qreal functionIntensity) } } +bool VCSlider::playbackFlashEnable() +{ + return m_playbackFlashEnable; +} + +void VCSlider::setPlaybackFlashEnable(bool enable) +{ + m_playbackFlashEnable = enable; + + if (enable == false && m_flashButton != NULL) + { + delete m_flashButton; + m_flashButton = NULL; + } + else if (enable == true && m_flashButton == NULL) + { + m_flashButton = new FlashButton(this); + m_flashButton->setIconSize(QSize(32, 32)); + m_flashButton->setStyle(AppUtil::saneStyle()); + m_flashButton->setIcon(QIcon(":/flash.png")); + m_flashButton->setToolTip(tr("Flash Function")); + layout()->addWidget(m_flashButton); + layout()->setAlignment(m_flashButton, Qt::AlignHCenter); + + m_flashButton->show(); + } +} + +void VCSlider::flashPlayback(bool on) +{ + if (on) + m_playbackFlashPreviousValue = m_playbackValue; + m_playbackIsFlashing = on; + + setPlaybackValue(on ? UCHAR_MAX : m_playbackFlashPreviousValue); +} + void VCSlider::slotPlaybackFunctionRunning(quint32 fid) { Q_UNUSED(fid); @@ -1497,6 +1585,10 @@ void VCSlider::slotInputValueChanged(quint32 universe, quint32 channel, uchar va if (value > 0) slotResetButtonClicked(); } + else if (checkInputSource(universe, pagedCh, value, sender(), flashButtonInputSourceId)) + { + flashPlayback(value ? true : false); + } } void VCSlider::adjustIntensity(qreal val) @@ -1703,6 +1795,13 @@ bool VCSlider::loadXMLPlayback(QXmlStreamReader &pb_root) /* Function */ setPlaybackFunction(pb_root.readElementText().toUInt()); } + else if (pb_root.name() == KXMLQLCVCSliderPlaybackFlash) + { + setPlaybackFlashEnable(true); + QString str = loadXMLSources(pb_root, flashButtonInputSourceId); + if (str.isEmpty() == false) + m_playbackFlashKeySequence = stripKeySequence(QKeySequence(str)); + } else { qWarning() << Q_FUNC_INFO << "Unknown slider playback tag:" << pb_root.name().toString(); @@ -1804,6 +1903,16 @@ bool VCSlider::saveXML(QXmlStreamWriter *doc) doc->writeStartElement(KXMLQLCVCSliderPlayback); /* Playback function */ doc->writeTextElement(KXMLQLCVCSliderPlaybackFunction, QString::number(playbackFunction())); + + if (sliderMode() == Playback && playbackFlashEnable() == true) + { + doc->writeStartElement(KXMLQLCVCSliderPlaybackFlash); + if (m_playbackFlashKeySequence.toString().isEmpty() == false) + doc->writeTextElement(KXMLQLCVCWidgetKey, m_playbackFlashKeySequence.toString()); + saveXMLInput(doc, inputSource(flashButtonInputSourceId)); + doc->writeEndElement(); + } + /* End the tag */ doc->writeEndElement(); @@ -1866,3 +1975,19 @@ void VCSlider::LevelChannel::saveXML(QXmlStreamWriter *doc) const doc->writeCharacters(QString::number(this->channel)); doc->writeEndElement(); } + +void VCSlider::FlashButton::mousePressEvent(QMouseEvent *e) +{ + QToolButton::mousePressEvent(e); + // ignore event so that it can be + // forwarded to parent widget + e->ignore(); +} + +void VCSlider::FlashButton::mouseReleaseEvent(QMouseEvent *e) +{ + QToolButton::mouseReleaseEvent(e); + // ignore event so that it can be + // forwarded to parent widget + e->ignore(); +} diff --git a/ui/src/virtualconsole/vcslider.h b/ui/src/virtualconsole/vcslider.h index d67950f780..6e9c8c10cb 100644 --- a/ui/src/virtualconsole/vcslider.h +++ b/ui/src/virtualconsole/vcslider.h @@ -21,6 +21,7 @@ #ifndef VCSLIDER_H #define VCSLIDER_H +#include #include #include @@ -32,7 +33,6 @@ class QXmlStreamReader; class QXmlStreamWriter; -class QToolButton; class QHBoxLayout; class QLabel; @@ -70,6 +70,7 @@ class VCSliderProperties; #define KXMLQLCVCSliderPlayback QString("Playback") #define KXMLQLCVCSliderPlaybackFunction QString("Function") +#define KXMLQLCVCSliderPlaybackFlash QString("Flash") class VCSlider : public VCWidget, public DMXSource { @@ -81,6 +82,7 @@ class VCSlider : public VCWidget, public DMXSource public: static const quint8 sliderInputSourceId; static const quint8 overrideResetInputSourceId; + static const quint8 flashButtonInputSourceId; static const QSize defaultSize; @@ -392,6 +394,13 @@ protected slots: /** @reimp */ virtual void notifyFunctionStarting(quint32 fid, qreal intensity); + /** Get/Set the status of the flash button enablement */ + bool playbackFlashEnable(); + void setPlaybackFlashEnable(bool enable); + +protected: + void flashPlayback(bool on); + protected slots: void slotPlaybackFunctionRunning(quint32 fid); void slotPlaybackFunctionStopped(quint32 fid); @@ -404,6 +413,10 @@ protected slots: int m_playbackChangeCounter; QMutex m_playbackValueMutex; + bool m_playbackFlashEnable; + bool m_playbackIsFlashing; + uchar m_playbackFlashPreviousValue; + private: FunctionParent functionParent() const; @@ -568,7 +581,10 @@ private slots: void slotResetButtonClicked(); protected slots: + /** @reimp */ void slotKeyPressed(const QKeySequence& keySequence); + /** @reimp */ + void slotKeyReleased(const QKeySequence& keySequence); protected: QToolButton *m_resetButton; @@ -577,6 +593,33 @@ protected slots: private: QKeySequence m_overrideResetKeySequence; + /********************************************************************* + * Flash button + *********************************************************************/ +public: + /** Get/set the keyboard key combination to flash the playback */ + QKeySequence playbackFlashKeySequence() const; + void setPlaybackFlashKeySequence(const QKeySequence& keySequence); + +protected: + void mousePressEvent(QMouseEvent *e); + void mouseReleaseEvent(QMouseEvent *e); + +protected: + class FlashButton : public QToolButton + { + public: + FlashButton(QWidget *parent) + : QToolButton(parent) {} + protected: + void mousePressEvent(QMouseEvent *e); + void mouseReleaseEvent(QMouseEvent *e); + }; + FlashButton *m_flashButton; + +private: + QKeySequence m_playbackFlashKeySequence; + /********************************************************************* * External input *********************************************************************/ diff --git a/ui/src/virtualconsole/vcsliderproperties.cpp b/ui/src/virtualconsole/vcsliderproperties.cpp index bb6e416f1e..05656f48f9 100644 --- a/ui/src/virtualconsole/vcsliderproperties.cpp +++ b/ui/src/virtualconsole/vcsliderproperties.cpp @@ -108,19 +108,6 @@ VCSliderProperties::VCSliderProperties(VCSlider* slider, Doc* doc) /* Slider mode */ m_sliderMode = m_slider->sliderMode(); - switch (m_sliderMode) - { - default: - case VCSlider::Level: - slotModeLevelClicked(); - break; - case VCSlider::Playback: - slotModePlaybackClicked(); - break; - case VCSlider::Submaster: - slotModeSubmasterClicked(); - break; - } /* Slider movement (Qt understands inverted appearance vice versa) */ if (m_slider->invertedAppearance() == true) @@ -195,6 +182,33 @@ VCSliderProperties::VCSliderProperties(VCSlider* slider, Doc* doc) /* Function */ m_playbackFunctionId = m_slider->playbackFunction(); updatePlaybackFunctionName(); + + m_flashInputWidget = new InputSelectionWidget(m_doc, this); + m_flashInputWidget->setKeySequence(m_slider->playbackFlashKeySequence()); + m_flashInputWidget->setInputSource(m_slider->inputSource(VCSlider::flashButtonInputSourceId)); + m_flashInputWidget->setWidgetPage(m_slider->page()); + m_flashInputWidget->show(); + m_flashInputWidget->setEnabled(m_slider->playbackFlashEnable()); + m_flashButtonLayout->addWidget(m_flashInputWidget); + + connect(m_flashButtonCheck, SIGNAL(clicked(bool)), + this, SLOT(slotFlashCheckClicked(bool))); + m_flashButtonCheck->setChecked(m_slider->playbackFlashEnable()); + + /* At last, sort out visibility */ + switch (m_sliderMode) + { + default: + case VCSlider::Level: + slotModeLevelClicked(); + break; + case VCSlider::Playback: + slotModePlaybackClicked(); + break; + case VCSlider::Submaster: + slotModeSubmasterClicked(); + break; + } } VCSliderProperties::~VCSliderProperties() @@ -298,6 +312,8 @@ void VCSliderProperties::setLevelPageVisibility(bool visible) void VCSliderProperties::setPlaybackPageVisibility(bool visible) { m_playbackFunctionGroup->setVisible(visible); + m_flashButtonCheck->setVisible(visible); + m_flashInputWidget->setVisible(visible); if (visible == true) { @@ -688,6 +704,11 @@ void VCSliderProperties::slotDetachPlaybackFunctionClicked() updatePlaybackFunctionName(); } +void VCSliderProperties::slotFlashCheckClicked(bool checked) +{ + m_flashInputWidget->setEnabled(checked); +} + void VCSliderProperties::updatePlaybackFunctionName() { Function* function = m_doc->function(m_playbackFunctionId); @@ -847,6 +868,9 @@ void VCSliderProperties::accept() /* Playback page */ m_slider->setPlaybackFunction(m_playbackFunctionId); + m_slider->setPlaybackFlashEnable(m_flashButtonCheck->isChecked()); + m_slider->setPlaybackFlashKeySequence(m_flashInputWidget->keySequence()); + m_slider->setInputSource(m_flashInputWidget->inputSource(), VCSlider::flashButtonInputSourceId); /* Slider mode */ if (m_slider->sliderMode() != m_sliderMode) diff --git a/ui/src/virtualconsole/vcsliderproperties.h b/ui/src/virtualconsole/vcsliderproperties.h index 2d7fdb79d9..01aaf58535 100644 --- a/ui/src/virtualconsole/vcsliderproperties.h +++ b/ui/src/virtualconsole/vcsliderproperties.h @@ -130,6 +130,7 @@ protected slots: /** Callback for tree item expanded/collapsed */ void slotItemExpanded(); + /** Callback for monitoring enable */ void slotMonitorCheckClicked(bool checked); protected: @@ -145,6 +146,9 @@ public slots: /** Callback for playback function detach clicks */ void slotDetachPlaybackFunctionClicked(); + /** Callback for flah button enable */ + void slotFlashCheckClicked(bool checked); + protected: /** Update the name of the playback function, based on m_playbackFunctionId */ void updatePlaybackFunctionName(); @@ -153,6 +157,8 @@ public slots: /** The currently selected playback function */ quint32 m_playbackFunctionId; + InputSelectionWidget *m_flashInputWidget; + /************************************************************************* * Submaster page *************************************************************************/ diff --git a/ui/src/virtualconsole/vcsliderproperties.ui b/ui/src/virtualconsole/vcsliderproperties.ui index 8a1ba59bd9..7d447cfbe5 100644 --- a/ui/src/virtualconsole/vcsliderproperties.ui +++ b/ui/src/virtualconsole/vcsliderproperties.ui @@ -521,6 +521,16 @@ + + + + Flash Button + + + + + + From 50707255dcd22467b8453821c128cb64961ba982 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Sun, 12 May 2024 10:38:45 +0200 Subject: [PATCH 186/212] build: attempt to fix build on Arch with GCC 14 (#1565) --- variables.pri | 1 + 1 file changed, 1 insertion(+) diff --git a/variables.pri b/variables.pri index df382cda83..f033ae5e81 100644 --- a/variables.pri +++ b/variables.pri @@ -50,6 +50,7 @@ contains(FORCECONFIG, release) { } else { QMAKE_CXXFLAGS += -Wno-unused-local-typedefs # Fix to build with GCC 4.8 + QMAKE_CXXFLAGS += -Wno-error-template-id-cdtor # Fix to build with GCC 14 } } From 6382dd1c9a6ea1539020494ac4458ba6ab9d0d7e Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Sun, 12 May 2024 11:05:35 +0200 Subject: [PATCH 187/212] build: attempt to fix build on Arch with GCC 14 (#1565) --- variables.pri | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/variables.pri b/variables.pri index f033ae5e81..760d12376d 100644 --- a/variables.pri +++ b/variables.pri @@ -50,7 +50,7 @@ contains(FORCECONFIG, release) { } else { QMAKE_CXXFLAGS += -Wno-unused-local-typedefs # Fix to build with GCC 4.8 - QMAKE_CXXFLAGS += -Wno-error-template-id-cdtor # Fix to build with GCC 14 + QMAKE_CXXFLAGS += -Wno-template-id-cdtor # Fix to build with GCC 14 } } From b60a56d0c5629a69e520d49e8ee962acf2088377 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Sun, 12 May 2024 22:58:51 +0200 Subject: [PATCH 188/212] resources: 5 new fixtures (see changelog) --- debian/changelog | 4 +- .../fixtures/Ayrton/Ayrton-MiniPanel-FX.qxf | 278 +++++++++++++++ .../BoomToneDJ/BoomToneDJ-Maxi-Spot-60.qxf | 103 ++++++ .../Briteq/Briteq-COB-Blinder-2x100W.qxf | 61 ++++ .../fixtures/Elation/Elation-ELED-B48.qxf | 98 ++++++ resources/fixtures/FixturesMap.xml | 5 + resources/fixtures/Martin/Martin-MAC-Aura.qxf | 86 ++--- .../fixtures/Robe/Robe-LEDBeam-350-RGBA.qxf | 273 +++++++++++++++ resources/fixtures/Robe/Robe-LEDBeam-350.qxf | 320 ++++++++++++------ 9 files changed, 1079 insertions(+), 149 deletions(-) create mode 100644 resources/fixtures/Ayrton/Ayrton-MiniPanel-FX.qxf create mode 100644 resources/fixtures/BoomToneDJ/BoomToneDJ-Maxi-Spot-60.qxf create mode 100644 resources/fixtures/Briteq/Briteq-COB-Blinder-2x100W.qxf create mode 100644 resources/fixtures/Elation/Elation-ELED-B48.qxf create mode 100644 resources/fixtures/Robe/Robe-LEDBeam-350-RGBA.qxf diff --git a/debian/changelog b/debian/changelog index 66d4cd1e6f..3ebaa57faa 100644 --- a/debian/changelog +++ b/debian/changelog @@ -10,8 +10,10 @@ qlcplus (4.13.1) stable; urgency=low * Plugins/DMX USB: restore Vince DMX512 output (thanks to Jérôme Lebleu) * 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, Mac Mah FLAT PAR 7x12W 6in1, Eurolite LED PIX-16 QCL Bar (thanks to Cédric Monféfoul) + * 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) -- Massimo Callegari Sun, 30 Jun 2024 12:13:14 +0200 diff --git a/resources/fixtures/Ayrton/Ayrton-MiniPanel-FX.qxf b/resources/fixtures/Ayrton/Ayrton-MiniPanel-FX.qxf new file mode 100644 index 0000000000..b47f935251 --- /dev/null +++ b/resources/fixtures/Ayrton/Ayrton-MiniPanel-FX.qxf @@ -0,0 +1,278 @@ + + + + + Q Light Controller Plus + 4.13.1 GIT + Giacomo Gorini + + Ayrton + MiniPanel FX + Moving Head + + + + + + Speed + max to min speed + blackout by movement + no function + + + Pan + no function + Forwards Pan rotation from fast to slow + No Rotation + Backwards Pan rotation from slow to fast + + + Tilt + no function + Forwards Tilt rotation from fast to slow + No Rotation + Backwards Tilt rotation from slow to fast + + + + + + + Shutter + Led turn off + Led turn on + Strobe effect slow to fast + Led turn on + Pulse-effect in sequences + Led turn on + Random strobe effect slow to fast + Led turn on + + + + Colour + No function + red to yellow + yellow to green + green to cyan + cyan to blue + blue to magenta + magenta to red + red to white + Crossfading colours from slow to fast + + + Colour + No function + White 2700k + White 3200k + White 4200k + White 5600k + White 6500k + White 8000k + Yellow + Magenta + Cyan + Salmon + Turquoise + Light Green + Steel Blue + Orange + Straw + Pale Lavander + Pink + Red + Green + Blue + White + Rainbow1 + Rainbow2 + Rainbow3 + Reserved + + + + Effect + Led Turn off + Chase 1 + Chase 2 + Chase 3 + Chase 4 + Chase 5 + Chase 6 + Chase 7 + Chase 8 + Chase 9 + Chase 10 + Chase 11 + Chase 12 + Chase 13 + Chase 14 + Chase 15 + Chase 16 + Chase 17 + Chase 18 + Chase 19 + Chase 20 + Reserved + + + Speed + Fast to Slow Backward + Stop + Slow to Fast Forward + + + Effect + Fade Chase + + + Maintenance + Normal + All motor reset + Scan motor reset + no function + no function + no function + Others motor reset + Internal Program 1 + Internal Program 2 + Internal Program 3 + Internal Program 4 + Internal Program 5 + Internal Program 6 + Internal Program 7 + Reserved + + + + + + + + + + + + + + + + + + + + + Pan Movement + Pan Fine + Tilt Movement + Tilt Fine + Speed Pan/Tilt + Pan Motor continuous rotation + Tilt Motor continuous rotation + Red LED - all arrays + Green LED - all arrays + Blue LED - all arrays + White LED - all arrays + Shutter, strobe + Dimmer Intensity + Color Macro + Color Presets + Color Preset Dimmer + Chase Patterns + Chase Speed + Chase Fade + Zoom + Zoom fine + Reset, Internal programs + + + Pan Movement + Tilt Movement + Speed Pan/Tilt + Pan Motor continuous rotation + Tilt Motor continuous rotation + Red LED - all arrays + Green LED - all arrays + Blue LED - all arrays + White LED - all arrays + Shutter, strobe + Dimmer Intensity + Color Macro + Color Presets + Color Preset Dimmer + Chase Patterns + Chase Speed + Chase Fade + Zoom + Reset, Internal programs + + + Pan Movement + Pan Fine + Tilt Movement + Tilt Fine + Speed Pan/Tilt + Pan Motor continuous rotation + Tilt Motor continuous rotation + Shutter, strobe + Dimmer Intensity + Color Macro + Color Presets + Color Preset Dimmer + Chase Patterns + Chase Speed + Chase Fade + Zoom + Zoom fine + Reset, Internal programs + Red LED-array 1 + Green LED-array 1 + Blue LED-array 1 + White LED-array 1 + Red LED-array 2 + Green LED-array 2 + Blue LED-array 2 + White LED-array 2 + Red LED-array 3 + Green LED-array 3 + Blue LED-array 3 + White LED-array 3 + Red LED-array 4 + Green LED-array 4 + Blue LED-array 4 + White LED-array 4 + + 18 + 19 + 20 + 21 + + + 22 + 23 + 24 + 25 + + + 26 + 27 + 28 + 29 + + + 30 + 31 + 32 + 33 + + + + + + + + + + + diff --git a/resources/fixtures/BoomToneDJ/BoomToneDJ-Maxi-Spot-60.qxf b/resources/fixtures/BoomToneDJ/BoomToneDJ-Maxi-Spot-60.qxf new file mode 100644 index 0000000000..0b5d0edea2 --- /dev/null +++ b/resources/fixtures/BoomToneDJ/BoomToneDJ-Maxi-Spot-60.qxf @@ -0,0 +1,103 @@ + + + + + Q Light Controller Plus + 4.13.1 GIT + Cédric Monféfoul + + BoomToneDJ + Maxi Spot 60 + Moving Head + + + + + Colour + White + White + Red + Red + Red + Green + Green + Green + Blue + Blue + Blue + Yellow + Yellow + Yellow + Magenta + Magenta + Magenta + Cyan + Cyan + Cyan + Orange + Orange + Rotating rainbow effect From slow to fast + + + Gobo + Open + Gobo 1 + Gobo 2 + Gobo 3 + Gobo 4 + Gobo 5 + Gobo 6 + Gobo 7 + Gobo Wheel auto rotate from slow to fast + Open + White Shake + Gobo 1 Shake + Gobo 2 Shake + Gobo 3 Shake + Gobo 4 Shake + Gobo 5 Shake + Gobo 6 Shake + Gobo 7 Shake + + + + Shutter + No function + Strobe from slow to fast + Random mode and pulse + + + Maintenance + No function + Blackout while Pan/Tilt Move + Blackout while Color Change + Blackout while Gobo change + Blackout while Pan/Tilt, Color, Gobo move + Reset + Sound Mode + + + + + Pan + Tilt + Pan / Tilt Speed + Colors + Gobos + Dimmer + Shutter + Function + + + Pan + Pan Fine + Tilt + Tilt Fine + Pan / Tilt Speed + Colors + Gobos + Dimmer + Shutter + Function + + + + + + + + + diff --git a/resources/fixtures/Briteq/Briteq-COB-Blinder-2x100W.qxf b/resources/fixtures/Briteq/Briteq-COB-Blinder-2x100W.qxf new file mode 100644 index 0000000000..28f0e3b7e2 --- /dev/null +++ b/resources/fixtures/Briteq/Briteq-COB-Blinder-2x100W.qxf @@ -0,0 +1,61 @@ + + + + + Q Light Controller Plus + 4.13.1 GIT + Giacomo Gorini + + Briteq + COB Blinder 2x100W + Strobe + + + + + Maintenance + Fixture Selected Curve + Linear Curve + Square Curve + + + + Dim Left + + 0 + + + 0 + + + + Dimmer + Strobe + + 0 + + + 0 + + + + Dim Left + Dim Right + Strobe + Dimmer Curve + + 0 + + + 1 + + + + + + + + + + + diff --git a/resources/fixtures/Elation/Elation-ELED-B48.qxf b/resources/fixtures/Elation/Elation-ELED-B48.qxf new file mode 100644 index 0000000000..6a9f34e13d --- /dev/null +++ b/resources/fixtures/Elation/Elation-ELED-B48.qxf @@ -0,0 +1,98 @@ + + + + + Q Light Controller Plus + 4.13.1 GIT + Xoneoo + + Elation + ELED B48 + Color Changer + + + + + + Shutter + Static + Strobing slow --> fast + + + Effect + No effect + Auto color mix + Auto mix using channel 1, 2, & 3 + 24 Auto mode programs + Suond active flash + Sound active + + + Effect + Nothing + Color macro + + + + + + + + + + + + + + + Red + Green + Blue + Macro + Strobing/Speed Control + Auto Mode + Dimming + + + RED 1 + GREEN 1 + BLUE 1 + RED 2 + GREEN 2 + BLUE 2 + RED 3 + GREEN 3 + BLUE 3 + RED 4 + GREEN 4 + BLUE 4 + + 0 + 1 + 2 + + + 3 + 4 + 5 + + + 6 + 7 + 8 + + + 9 + 10 + 11 + + + + + + + + + + + diff --git a/resources/fixtures/FixturesMap.xml b/resources/fixtures/FixturesMap.xml index 5a879eb745..e3581ab93b 100644 --- a/resources/fixtures/FixturesMap.xml +++ b/resources/fixtures/FixturesMap.xml @@ -223,6 +223,7 @@ + @@ -282,6 +283,7 @@ + @@ -312,6 +314,7 @@ + @@ -676,6 +679,7 @@ + @@ -1423,6 +1427,7 @@ + diff --git a/resources/fixtures/Martin/Martin-MAC-Aura.qxf b/resources/fixtures/Martin/Martin-MAC-Aura.qxf index 654cd864ec..f491bba266 100644 --- a/resources/fixtures/Martin/Martin-MAC-Aura.qxf +++ b/resources/fixtures/Martin/Martin-MAC-Aura.qxf @@ -3,13 +3,13 @@ Q Light Controller Plus - 4.12.4 - Thierry Rodolfo + 4.13.1 GIT + Thierry Rodolfo, Giacomo Gorini Martin MAC Aura Moving Head - + Shutter Shutter closed Shutter open @@ -100,9 +100,9 @@ LEE 135 - Deep Golden Amber LEE 164 - Flame Red Open - Color rotation fast → slow + Color wheel clockwise fast → slow Stop Rot - Color rotation slow → fast + Color wheel c-clockwise slow → fast Open Random color Fast Random color Medium @@ -114,9 +114,9 @@ - Intensity - Disabled - 10 000K → 2 500K + Colour + Beam CTC Disabled + Beam CTC 10000K → 2500K Effect @@ -218,30 +218,30 @@ Sync random No sync, random - + Shutter - closed - open - Strobe 1 (fast → slow) - Open - Strobe 2: opening pulse (fast → slow) - open - Strobe 3: closing pulse (fast → slow) - open - Strobe 4: random strobe (fast → slow) - open - Strobe 5: random opening pulse (fast → slow) - open - Strobe 6: random closing pulse (fast → slow) - open - Strobe 7: burst pulse (fast → slow) - open - Strobe 8: random burst pulse (fast → slow) - open - Strobe 9: sine wave (fast → slow) - Open - Strobe 10: burst (fast → slow) - open + Shutter aura closed + Shutter aura open + Strobe aura 1 (fast → slow) + Shutter aura open + Strobe aura 2: opening pulse (fast → slow) + Shutter aura open + Strobe aura 3: closing pulse (fast → slow) + Shutter aura open + Strobe aura 4: random strobe (fast → slow) + Shutter aura open + Strobe aura 5: random opening pulse (fast → slow) + Shutter aura open + Strobe aura 6: random closing pulse (fast → slow) + Shutter aura open + Strobe aura 7: burst pulse (fast → slow) + Shutter aura open + Strobe aura 8: random burst pulse (fast → slow) + Shutter aura open + Strobe aura 9: sine wave (fast → slow) + Shutter aura open + Strobe aura 10: burst (fast → slow) + Shutter aura open @@ -281,9 +281,9 @@ LEE 135 - Deep Golden Amber LEE 164 - Flame Red Open - Color rotation fast → slow + Color wheel clockwise fast → slow Stop Rot - Color rotation slow → fast + Color wheel c-clockwise slow → fast Open Random color Fast Random color Medium @@ -293,7 +293,7 @@ - + Beam electronic shutter effect Beam dimmer Zoom @@ -320,14 +320,13 @@ Aura Green Aura Blue + 0 + 1 8 9 10 - 12 11 - 13 - 1 - 0 + 12 19 @@ -335,9 +334,10 @@ 21 22 23 + 24 - + Beam electronic shutter effect Beam dimmer Zoom @@ -354,10 +354,10 @@ Beam CTC - - + + - - + + diff --git a/resources/fixtures/Robe/Robe-LEDBeam-350-RGBA.qxf b/resources/fixtures/Robe/Robe-LEDBeam-350-RGBA.qxf new file mode 100644 index 0000000000..adb9136445 --- /dev/null +++ b/resources/fixtures/Robe/Robe-LEDBeam-350-RGBA.qxf @@ -0,0 +1,273 @@ + + + + + Q Light Controller Plus + 4.13.1 GIT + Giacomo Gorini + + Robe + LEDBeam 350 RGBA + Moving Head + + + + + + Speed + Standard mode + Max Speed mode + P/T Speed mode: Speed from max. to min. - P/T Time mode: Time from 0.2 sec to 25.5 sec + + + Maintenance + Reserved + Display ON + Display OFF + RGBW colour mixing mode + CMY colour mixing mode + Pan/Tilt speed mode + Pan/Tilt time mode + Blackout while pan/tilt moving + Disabled blackout while pan/tilt moving + Dimmer curve - square law + Dimmer curve - linear + Fans mode: Auto + Fans mode: High + White point 8000K ON + White point 8000K OFF + Reserved + Pan 540 + Pan 450 + Quiet mode: Fans On at blackout + Quiet mode: Fans Off at blackout + Reserved + Reserved + Pan/Tilt reset + Zoom reset + Reserved + Tungsten effect simulation (750W) On + Tungsten effect simulation (1000W) On + Tungsten effect simulation (1200W) On + Tungsten effect simulation (2000W) On + Tungsten effect simulation (2500W) On + Tungsten effect simulation Off + Reserved + Total Fixture Reset + Reserved + RoboSpot enabled + RoboSpot disabled - except handle faders and pan/tilt + RoboSpot fully disabled + Reserved + Disabled "Quiet mode" + Quiet mode - fan noise control from min to max + + + Maintenance + PWM frequency from Display menu + 300 Hz + 600 Hz (10=default) + 1200 Hz + 2400 Hz + High + Reserved (fixture utilizes PWM frequency set in the display menu item Frequency Setup) + + + Maintenance + Selected LED Frequency + LED Frequency (step -126 > -1) + Selected LED Frequency (128=default) + LED Frequency (step +1 > +126) + Selected LED Frequency + + + Colour + No function + Filter 4 (Medium Bastard Amber) + Filter 25 (Sunset Red) + Filter 19 (Fire) + Filter 26 (Bright Red) + Filter 58 (Lavender) + Filter 68 (Sky Blue) + Filter 36 (Medium Pink) + Filter 89 (Moss Green) + Filter 88 (Lime Green) + Filter 90 (Dark Yellow Green) + Filter 49 (Medium Purple) + Filter 52 (Light Lavender) + Filter 102 (Light Amber) + Filter 103 (Straw) + Filter 140 (Summer Blue) + Filter 124 (Dark Green) + Filter 106 (Primary Red) + Filter 111 (Dark Pink) + Filter 115 (Peacock Blue) + Filter 126 (Mauve) + Filter 117 (Steel Blue) + Filter 118 (Light Blue) + Filter 122 (Fern Green) + Filter 182 (Light Red) + Filter 121 (Filter Green) + Filter 128 (Bright Pink) + Filter 131 (Marine Blue) + Filter 132 (Medium Blue) + Filter 134 (Golden Amber) + Filter 135 (Deep Golden Amber) + Filter 136 (Pale Lavender) + Filter 137 (Special Lavender) + Filter 138 (Pale Green) + Filter 798 (Chrysalis Pink) + Filter 141 (Bright Blue) + Filter 147 (Apricot) + Filter 148 (Bright Rose) + Filter 152 (Pale Gold) + Filter 154 (Pale Rose) + Filter 157 (Pink) + Filter 143 (Pale Navy Blue) + Filter 162 (Bastard Amber) + Filter 164 (Flame Red) + Filter 165 (Daylight Blue) + Filter 169 (Lilac Tint) + Filter 170 (Deep Lavender) + Filter 172 (Lagoon Blue) + Filter 194 (Surprise Pink) + Filter 180 (Dark Lavender) + Filter 181 (Congo Blue) + Filter 197 (Alice Blue) + Filter 201 (Full C.T. Blue) + Filter 202 (Half C.T. Blue) + Filter 203 (Quarter C.T. Blue) + Filter 204 (Full C.T. Orange) + Filter 219 (Fluorescent Green) + Filter 206 (Quarter C.T. Orange) + Filter 247 (Filter Minus Green) + Filter 248 (Half Minus Green) + Filter 281 (Three Quarter C.T. Blue) + Filter 285 (Three Quarter C.T. Orange) + Filter 352 (Glacier Blue) + Filter 353 (Lighter Blue) + Filter 507 (Madge) + Filter 778 (Millennium Gold) + Filter 793 (Vanity Fair) + Raw DMX + Rainbow effect (with fade time) from slow -> fast + Rainbow effect (without fade time) from slow -> fast + + + + + + + + + + + Colour + If function "White Point 8000K" ON: Col. temperature correction from 8000K to 2700K for whites only. If function "White Point 8000K" OFF: Colour temperature correction from cool white to 2700K + + + Colour + Virtual colors ("Virtual" has priority) + Maximum mode (highest values have priority) + Minimum mode (lowest values have priority) + Multiply mode (multiply Virtual and Colour Mix) + Addition mode (Virtual + Colour mix) + Subtraction mode (Virtual - Colour mix) + Inverted Subtration mode (Virtual - Colour mix) + Reserved + Virtual colors (virtual has priority) + Crossfade (crossfade between Virtual and Colour mix) + Colour channels ("Colour mix" has priority) + + + + + Shutter + Shutter closed + Shutter open + Stobe-effect from slow to fast + Shutter open + Opening pulse in sequences from slow to fast + Closing pulse in sequences from fast to slow + Shutter open + Random strobe-effect from slow to fast + Shutter open + + + + + Pan + Pan Fine + Tilt + Tilt Fine + Pan/Tilt Speed - Pan/Tilt Time + Power/Special Functions + Virtual Colour Wheel + Red/Cyan + Red/Cyan Fine + Green/Magenta + Green/Magenta Fine + Blue/Yellow + Blue/Yellow Fine + Amber + Amber Fine + CTC + Color Mix Control + Zoom + Zoom Fine + Shutter/Strobe + Dimmer Intensity + Dimmer Intensity Fine + + + Pan + Pan Fine + Tilt + Tilt Fine + Pan/Tilt Speed - Pan/Tilt Time + Power/Special Functions + Virtual Colour Wheel + Red/Cyan + Green/Magenta + Blue/Yellow + Amber + CTC + Color Mix Control + Zoom + Shutter/Strobe + Dimmer Intensity + + + Pan + Pan Fine + Tilt + Tilt Fine + Pan/Tilt Speed - Pan/Tilt Time + Power/Special Functions + LED frequency selection + LED frequency fine adjusting + Virtual Colour Wheel + Red/Cyan + Red/Cyan Fine + Green/Magenta + Green/Magenta Fine + Blue/Yellow + Blue/Yellow Fine + Amber + Amber Fine + CTC + Color Mix Control + Zoom + Zoom Fine + Shutter/Strobe + Dimmer Intensity + Dimmer Intensity Fine + + + + + + + + + diff --git a/resources/fixtures/Robe/Robe-LEDBeam-350.qxf b/resources/fixtures/Robe/Robe-LEDBeam-350.qxf index 55fc474562..1867f09c34 100644 --- a/resources/fixtures/Robe/Robe-LEDBeam-350.qxf +++ b/resources/fixtures/Robe/Robe-LEDBeam-350.qxf @@ -3,48 +3,48 @@ Q Light Controller Plus - 4.13.0 GIT - Tomas Hastings + 4.13.1 GIT + Giacomo Gorini Robe LEDBeam 350 Moving Head - + - + - + Speed - Standard Mode - Max Speed Mode - Speed from Max to Min + Standard mode + Max Speed mode + P/T Speed mode: Speed from max. to min. - P/T Time mode: Time from 0.2 sec to 25.5 sec - - Effect - Reserved - To Activate: Shutter/Strobe (Ch 20/15/22)@(0-31) off, stop at value for 3s. - Display On - Display Off - RGBW Colour Mixing Mode - CMY Colour Mixing Mode - Pan/Tilt Speed Mode - Pan/Tilt Time Mode - Blackout While Pan/Tilt Moving - Disabled Blackout While Pan/Tilt Moving - Dimmer Curve - Square Law - Dimmer Curve - Linear - Fan Mode: Auto - Fan Mode: High - White Point 8000K ON - White Point 8000K OFF + + Maintenance + Reserved + Display ON + Display OFF + RGBW colour mixing mode + CMY colour mixing mode + Pan/Tilt speed mode + Pan/Tilt time mode + Blackout while pan/tilt moving + Disabled blackout while pan/tilt moving + Dimmer curve - square law + Dimmer curve - linear + Fans mode: Auto + Fans mode: High + White point 8000K ON + White point 8000K OFF Reserved - Pan 540deg (Full) - Pan 450deg (Reduced) - Quiet Mode: Fans On @ Blackout - Quiet Mode: Fans Off @ Blackout - Reserved - New Function Menu + Pan 540 + Pan 450 + Quiet mode: Fans On at blackout + Quiet mode: Fans Off at blackout + Reserved Reserved - Pan/Tile Reset - Zoom Reset + Pan/Tilt reset + Zoom reset Reserved Tungsten effect simulation (750W) On Tungsten effect simulation (1000W) On @@ -53,110 +53,220 @@ Tungsten effect simulation (2500W) On Tungsten effect simulation Off Reserved - Total Fixture Reset + Total Fixture Reset Reserved - RoboSpot Enabled - RoboSpot Disabled - Except Handle Faders and Pan/Tilt - RoboSpot Fully Disabled + RoboSpot enabled + RoboSpot disabled - except handle faders and pan/tilt + RoboSpot fully disabled Reserved - Disabled Quiet Mode - Quiet Mode - Fan Control From Min to Max + Disabled "Quiet mode" + Quiet mode - fan noise control from min to max - - Effect - PWM Frequency from Display Menu - 300Hz - 600Hz (10=default) - 1200Hz - 2400Hz + + Maintenance + PWM frequency from Display menu + 300 Hz + 600 Hz (10=default) + 1200 Hz + 2400 Hz High - Reserved (fixture utilises PWM frequency set in the display menu item Frequency Setup) + Reserved (fixture utilizes PWM frequency set in the display menu item Frequency Setup) - - Effect - Selected Frequency - LED Frequency Steps (-126 - 126) - Selected Frequency + + Maintenance + Selected LED Frequency + LED Frequency (step -126 > -1) + Selected LED Frequency (128=default) + LED Frequency (step +1 > +126) + Selected LED Frequency Colour - Preset Filters + No function + Filter 4 (Medium Bastard Amber) + Filter 25 (Sunset Red) + Filter 19 (Fire) + Filter 26 (Bright Red) + Filter 58 (Lavender) + Filter 68 (Sky Blue) + Filter 36 (Medium Pink) + Filter 89 (Moss Green) + Filter 88 (Lime Green) + Filter 90 (Dark Yellow Green) + Filter 49 (Medium Purple) + Filter 52 (Light Lavender) + Filter 102 (Light Amber) + Filter 103 (Straw) + Filter 140 (Summer Blue) + Filter 124 (Dark Green) + Filter 106 (Primary Red) + Filter 111 (Dark Pink) + Filter 115 (Peacock Blue) + Filter 126 (Mauve) + Filter 117 (Steel Blue) + Filter 118 (Light Blue) + Filter 122 (Fern Green) + Filter 182 (Light Red) + Filter 121 (Filter Green) + Filter 128 (Bright Pink) + Filter 131 (Marine Blue) + Filter 132 (Medium Blue) + Filter 134 (Golden Amber) + Filter 135 (Deep Golden Amber) + Filter 136 (Pale Lavender) + Filter 137 (Special Lavender) + Filter 138 (Pale Green) + Filter 798 (Chrysalis Pink) + Filter 141 (Bright Blue) + Filter 147 (Apricot) + Filter 148 (Bright Rose) + Filter 152 (Pale Gold) + Filter 154 (Pale Rose) + Filter 157 (Pink) + Filter 143 (Pale Navy Blue) + Filter 162 (Bastard Amber) + Filter 164 (Flame Red) + Filter 165 (Daylight Blue) + Filter 169 (Lilac Tint) + Filter 170 (Deep Lavender) + Filter 172 (Lagoon Blue) + Filter 194 (Surprise Pink) + Filter 180 (Dark Lavender) + Filter 181 (Congo Blue) + Filter 197 (Alice Blue) + Filter 201 (Full C.T. Blue) + Filter 202 (Half C.T. Blue) + Filter 203 (Quarter C.T. Blue) + Filter 204 (Full C.T. Orange) + Filter 219 (Fluorescent Green) + Filter 206 (Quarter C.T. Orange) + Filter 247 (Filter Minus Green) + Filter 248 (Half Minus Green) + Filter 281 (Three Quarter C.T. Blue) + Filter 285 (Three Quarter C.T. Orange) + Filter 352 (Glacier Blue) + Filter 353 (Lighter Blue) + Filter 507 (Madge) + Filter 778 (Millennium Gold) + Filter 793 (Vanity Fair) Raw DMX - Rainbow Effect (with fade time slow -> fast) - Rainbow Effect (without fade time slow -> fast) + Rainbow effect (with fade time) from slow -> fast + Rainbow effect (without fade time) from slow -> fast - - - - - - + + + + + + - + Colour - Col. temperature correction from 8000K to 2700K -for whites only + If function "White Point 8000K" ON: Col. temperature correction from 8000K to 2700K for whites only. If function "White Point 8000K" OFF: Colour temperature correction from cool white to 2700K - + Colour - Virtual Colours - Maximum Mode (Highest values have priority) - Minimum Mode (Lowest values have priority) - Multiply Mode (Multiply Virtual and Colour mix) - Addition Mode (Virtual + Colour Mix, 45 default) - Substration Mode (Virtual - Colour Mix) - Subtraction Mode (Colour - Virtual Mix) + Virtual colors ("Virtual" has priority) + Maximum mode (highest values have priority) + Minimum mode (lowest values have priority) + Multiply mode (multiply Virtual and Colour Mix) + Addition mode (Virtual + Colour mix) + Subtraction mode (Virtual - Colour mix) + Inverted Subtration mode (Virtual - Colour mix) Reserved - Virtual Colours - Crossfade between Virtual and Colour Mix - Colour Channels + Virtual colors (virtual has priority) + Crossfade (crossfade between Virtual and Colour mix) + Colour channels ("Colour mix" has priority) - - + + Shutter - Shutter Closed - Shutter Open - Strobe Effect Slow -> Fast - Shutter Open - Opening Pulse in Sequences from Slow -> Fast - Closing Pulse in Sequences from Fast -> Slow - Shutter Open - Random Strobe Effect from Slow -> Fast - Shutter Open + Shutter closed + Shutter open + Stobe-effect from slow to fast + Shutter open + Opening pulse in sequences from slow to fast + Closing pulse in sequences from fast to slow + Shutter open + Random strobe-effect from slow to fast + Shutter open - - - + + + Pan Pan Fine Tilt Tilt Fine - Pan/Tilt speed - Power / Special Functions - LED Frequency Selection - LED Frequency Fine Adjusting + Pan/Tilt Speed - Pan/Tilt Time + Power/Special Functions + Virtual Colour Wheel + Red/Cyan + Red/Cyan Fine + Green/Magenta + Green/Magenta Fine + Blue/Yellow + Blue/Yellow Fine + White + White Fine + CTC + Color Mix Control + Zoom + Zoom Fine + Shutter/Strobe + Dimmer Intensity + Dimmer Intensity Fine + + + Pan + Pan Fine + Tilt + Tilt Fine + Pan/Tilt Speed - Pan/Tilt Time + Power/Special Functions + Virtual Colour Wheel + Red/Cyan + Green/Magenta + Blue/Yellow + White + CTC + Color Mix Control + Zoom + Shutter/Strobe + Dimmer Intensity + + + Pan + Pan Fine + Tilt + Tilt Fine + Pan/Tilt Speed - Pan/Tilt Time + Power/Special Functions + LED frequency selection + LED frequency fine adjusting Virtual Colour Wheel - Red - Red Fine - Green - Green fine - Blue - Blue Fine + Red/Cyan + Red/Cyan Fine + Green/Magenta + Green/Magenta Fine + Blue/Yellow + Blue/Yellow Fine White White Fine - Colour Temperature Correction - Colour Mix Control + CTC + Color Mix Control Zoom - Zoom fine - Shutter / Strobe - Dimmer - Dimmer Fine + Zoom Fine + Shutter/Strobe + Dimmer Intensity + Dimmer Intensity Fine - + - + From 9805f86cc6914dbdc339aea53e2dbdaa3c286a37 Mon Sep 17 00:00:00 2001 From: LilyCalla Date: Mon, 13 May 2024 00:38:39 -0700 Subject: [PATCH 189/212] fixed formatting --- qmlui/virtualconsole/vccuelist.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/qmlui/virtualconsole/vccuelist.cpp b/qmlui/virtualconsole/vccuelist.cpp index 594586d2c3..f347db9875 100644 --- a/qmlui/virtualconsole/vccuelist.cpp +++ b/qmlui/virtualconsole/vccuelist.cpp @@ -520,7 +520,8 @@ void VCCueList::slotFunctionRemoved(quint32 fid) } } -void VCCueList::slotStepListChange(quint32 fid) { +void VCCueList::slotStepListChange(quint32 fid) +{ if (fid == m_chaserID) ChaserEditor::updateStepsList(m_doc, chaser(), m_stepsList); } From f860c6b01a16fd049def97df12757b39a25eb068 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Mon, 13 May 2024 18:45:27 +0200 Subject: [PATCH 190/212] qmlui: improve #1564 --- engine/src/chaser.cpp | 4 ++-- engine/src/chaser.h | 2 +- qmlui/virtualconsole/vccuelist.cpp | 19 +++++++++++-------- qmlui/virtualconsole/vccuelist.h | 2 +- 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/engine/src/chaser.cpp b/engine/src/chaser.cpp index 26b04b4378..a00b3b3f76 100644 --- a/engine/src/chaser.cpp +++ b/engine/src/chaser.cpp @@ -132,7 +132,7 @@ bool Chaser::addStep(const ChaserStep& step, int index) } emit changed(this->id()); - emit stepListChange(this->id()); + emit stepsListChanged(this->id()); return true; } else @@ -151,7 +151,7 @@ bool Chaser::removeStep(int index) } emit changed(this->id()); - emit stepListChange(this->id()); + emit stepsListChanged(this->id()); return true; } else diff --git a/engine/src/chaser.h b/engine/src/chaser.h index 4ec9113cf8..15bf706173 100644 --- a/engine/src/chaser.h +++ b/engine/src/chaser.h @@ -157,7 +157,7 @@ public slots: signals: void stepChanged(int index); - void stepListChange(quint32 fid); + void stepsListChanged(quint32 fid); protected: QList m_steps; diff --git a/qmlui/virtualconsole/vccuelist.cpp b/qmlui/virtualconsole/vccuelist.cpp index f347db9875..d7edee1be9 100644 --- a/qmlui/virtualconsole/vccuelist.cpp +++ b/qmlui/virtualconsole/vccuelist.cpp @@ -488,8 +488,8 @@ void VCCueList::setChaserID(quint32 fid) this, SLOT(slotCurrentStepChanged(int))); connect(function, SIGNAL(stepChanged(int)), this, SLOT(slotStepChanged(int))); - connect(function, SIGNAL(stepListChange(quint32)), - this, SLOT(slotStepListChange(quint32))); + connect(function, SIGNAL(stepsListChanged(quint32)), + this, SLOT(slotStepsListChanged(quint32))); emit chaserIDChanged(fid); } @@ -520,18 +520,21 @@ void VCCueList::slotFunctionRemoved(quint32 fid) } } -void VCCueList::slotStepListChange(quint32 fid) -{ - if (fid == m_chaserID) - ChaserEditor::updateStepsList(m_doc, chaser(), m_stepsList); -} - void VCCueList::slotStepChanged(int index) { ChaserStep *step = chaser()->stepAt(index); ChaserEditor::updateStepInListModel(m_doc, chaser(), m_stepsList, step, index); } +void VCCueList::slotStepsListChanged(quint32 fid) +{ + if (fid == m_chaserID) + { + ChaserEditor::updateStepsList(m_doc, chaser(), m_stepsList); + emit stepsListChanged(); + } +} + void VCCueList::slotFunctionNameChanged(quint32 fid) { if (fid == m_chaserID) diff --git a/qmlui/virtualconsole/vccuelist.h b/qmlui/virtualconsole/vccuelist.h index 98867ef86f..a6db069d14 100644 --- a/qmlui/virtualconsole/vccuelist.h +++ b/qmlui/virtualconsole/vccuelist.h @@ -188,7 +188,7 @@ private slots: void slotFunctionRemoved(quint32 fid); void slotFunctionNameChanged(quint32 fid); void slotStepChanged(int index); - void slotStepListChange(quint32 fid); + void slotStepsListChanged(quint32 fid); private: FunctionParent functionParent() const; From ee16a5fe6af7383c48a49fc6d43e80adb0c23680 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Tue, 14 May 2024 19:36:31 +0200 Subject: [PATCH 191/212] qmlui: more code hardening --- qmlui/treemodel.cpp | 39 ++++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/qmlui/treemodel.cpp b/qmlui/treemodel.cpp index 7149b521bf..b4ca42f743 100644 --- a/qmlui/treemodel.cpp +++ b/qmlui/treemodel.cpp @@ -136,7 +136,7 @@ TreeModelItem *TreeModel::addItem(QString label, QVariantList data, QString path QStringList pathList = path.split(TreeModel::separator()); if (m_itemsPathMap.contains(pathList.at(0))) { - item = m_itemsPathMap[pathList.at(0)]; + item = m_itemsPathMap.value(pathList.at(0), nullptr); } else { @@ -202,9 +202,10 @@ TreeModelItem *TreeModel::itemAtPath(QString path) return nullptr; } - if (m_itemsPathMap.contains(pathList.at(0))) + TreeModelItem *item = m_itemsPathMap.value(pathList.at(0), nullptr); + if (item == nullptr) return nullptr; - TreeModelItem *item = m_itemsPathMap[pathList.at(0)]; + QString subPath = path.mid(path.indexOf(TreeModel::separator()) + 1); return item->children()->itemAtPath(subPath); } @@ -238,7 +239,10 @@ bool TreeModel::removeItem(QString path) } else { - TreeModelItem *item = m_itemsPathMap[pathList.at(0)]; + TreeModelItem *item = m_itemsPathMap.value(pathList.at(0), nullptr); + if (item == nullptr) + return false; + QString subPath = path.mid(path.indexOf(TreeModel::separator()) + 1); item->children()->removeItem(subPath); } @@ -272,9 +276,10 @@ void TreeModel::setItemRoleData(QString path, const QVariant &value, int role) } else { - if (!m_itemsPathMap.contains(pathList.at(0))) + TreeModelItem *item = m_itemsPathMap.value(pathList.at(0), nullptr); + if (item == nullptr) return; - TreeModelItem *item = m_itemsPathMap[pathList.at(0)]; + QString subPath = path.mid(path.indexOf(TreeModel::separator()) + 1); item->children()->setItemRoleData(subPath, value, role); } @@ -304,18 +309,18 @@ void TreeModel::setPathData(QString path, QVariantList data) return; QStringList pathList = path.split(TreeModel::separator()); - if (m_itemsPathMap.contains(pathList.at(0))) + TreeModelItem *item = m_itemsPathMap.value(pathList.at(0), nullptr); + if (item == nullptr) + return; + + if (pathList.count() == 1) { - TreeModelItem *item = m_itemsPathMap[pathList.at(0)]; - if (pathList.count() == 1) - { - item->setData(data); - } - else if (item->hasChildren()) - { - QString subPath = path.mid(path.indexOf(TreeModel::separator()) + 1); - item->children()->setPathData(subPath, data); - } + item->setData(data); + } + else if (item->hasChildren()) + { + QString subPath = path.mid(path.indexOf(TreeModel::separator()) + 1); + item->children()->setPathData(subPath, data); } } From 6a66af8ec699ec9c31315b0a11c59823eb2b1147 Mon Sep 17 00:00:00 2001 From: Binary-Vanguard-12138 <103393933+Binary-Vanguard-12138@users.noreply.github.com> Date: Wed, 15 May 2024 13:52:44 -0400 Subject: [PATCH 192/212] #1559 Added animation widget preset buttons in Web API test page --- webaccess/res/Test_Web_API.html | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/webaccess/res/Test_Web_API.html b/webaccess/res/Test_Web_API.html index bba18ebb2e..fe2e866628 100644 --- a/webaccess/res/Test_Web_API.html +++ b/webaccess/res/Test_Web_API.html @@ -122,6 +122,22 @@ } } +function vcAnimationWidgetControl(animIDObjName, controlIDObjName) +{ + var animObj = document.getElementById(animIDObjName); + var controlIDObj = document.getElementById(controlIDObjName); + + if (animObj && controlIDObj) + { + if (isConnected === true) + { + websocket.send(animObj.value + "|MATRIX_PUSHBUTTON|" + controlIDObj.value); + } + else + alert("You must connect to QLC+ WebSocket first!"); + } +} + function connectToWebSocket(host) { var url = 'ws://' + host + '/qlcplusWS'; websocket = new WebSocket(url); @@ -496,6 +512,20 @@

    Q Light Controller+ Web API test page

    +
    + + + +
    #" + tr("Name") + "" + tr("Fade In") + "" + tr("Fade Out") + "
    +
    Animation widget control

    + Animation widget ID:
    + Control ID: +
    + This API demonstrates how to control Virtual Console Animation widget. + The parameters to be used are:
    + Animation widget ID: The Animation widget ID as retrieved with the 'getWidgetsList' API
    + Control ID: The Animation control ID of preset buttons. +
    From cc741dbf5713dd119b95e760aedc072a5b9bd19f Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Wed, 15 May 2024 21:02:45 +0200 Subject: [PATCH 193/212] qmlui: add VC Slider flash button --- qmlui/qml/virtualconsole/VCSliderItem.qml | 18 +++++ .../qml/virtualconsole/VCSliderProperties.qml | 15 ++++ qmlui/virtualconsole/vcslider.cpp | 75 ++++++++++++++++--- qmlui/virtualconsole/vcslider.h | 13 ++++ 4 files changed, 111 insertions(+), 10 deletions(-) diff --git a/qmlui/qml/virtualconsole/VCSliderItem.qml b/qmlui/qml/virtualconsole/VCSliderItem.qml index b29add87b3..2209ca1954 100644 --- a/qmlui/qml/virtualconsole/VCSliderItem.qml +++ b/qmlui/qml/virtualconsole/VCSliderItem.qml @@ -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 { diff --git a/qmlui/qml/virtualconsole/VCSliderProperties.qml b/qmlui/qml/virtualconsole/VCSliderProperties.qml index 33e77ab8f7..fd7c087e49 100644 --- a/qmlui/qml/virtualconsole/VCSliderProperties.qml +++ b/qmlui/qml/virtualconsole/VCSliderProperties.qml @@ -257,6 +257,21 @@ Rectangle currentIndex: widgetRef ? widgetRef.controlledAttribute : 0 onCurrentIndexChanged: if (widgetRef) widgetRef.controlledAttribute = currentIndex } + + CustomCheckBox + { + implicitWidth: UISettings.iconSizeMedium + implicitHeight: implicitWidth + checked: widgetRef ? widgetRef.adjustFlashEnabled : false + onClicked: if (widgetRef) widgetRef.adjustFlashEnabled = checked + } + + RobotoText + { + height: gridItemsHeight + Layout.fillWidth: true + label: qsTr("Show flash button") + } } // GridLayout } // SectionBox Function control diff --git a/qmlui/virtualconsole/vcslider.cpp b/qmlui/virtualconsole/vcslider.cpp index 3021b9e219..6747ca542d 100644 --- a/qmlui/virtualconsole/vcslider.cpp +++ b/qmlui/virtualconsole/vcslider.cpp @@ -38,6 +38,7 @@ #define INPUT_SLIDER_CONTROL_ID 0 #define INPUT_SLIDER_RESET_ID 1 +#define INPUT_SLIDER_FLASH_ID 2 VCSlider::VCSlider(Doc *doc, QObject *parent) : VCWidget(doc, parent) @@ -64,12 +65,14 @@ VCSlider::VCSlider(Doc *doc, QObject *parent) , m_controlledAttributeId(Function::invalidAttributeId()) , m_attributeMinValue(0) , m_attributeMaxValue(UCHAR_MAX) + , m_adjustFlashEnabled(false) { setType(VCWidget::SliderWidget); setSliderMode(Adjust); registerExternalControl(INPUT_SLIDER_CONTROL_ID, tr("Slider Control"), false); registerExternalControl(INPUT_SLIDER_RESET_ID, tr("Reset Control"), false); + registerExternalControl(INPUT_SLIDER_FLASH_ID, tr("Flash Control"), true); } VCSlider::~VCSlider() @@ -676,7 +679,7 @@ QVariantList VCSlider::clickAndGoPresetsList() return prList; /* Find the first valid channel and return it to QML */ - for (SceneValue scv : m_levelChannels) + for (SceneValue &scv : m_levelChannels) { Fixture *fixture = m_doc->fixture(scv.fxi); if (fixture == nullptr) @@ -849,7 +852,7 @@ void VCSlider::adjustIntensity(qreal val) if (sliderMode() == Adjust) { - Function* function = m_doc->function(m_controlledFunctionId); + Function *function = m_doc->function(m_controlledFunctionId); if (function == nullptr) return; @@ -882,7 +885,7 @@ void VCSlider::slotControlledFunctionStopped(quint32 fid) if (m_controlledAttributeIndex == Function::Intensity) setValue(0, false, true); - Function* function = m_doc->function(fid); + Function *function = m_doc->function(fid); function->releaseAttributeOverride(m_controlledAttributeId); m_controlledAttributeId = Function::invalidAttributeId(); } @@ -898,7 +901,7 @@ void VCSlider::setControlledAttribute(int attributeIndex) if (m_controlledAttributeIndex == attributeIndex) return; - Function* function = m_doc->function(m_controlledFunctionId); + Function *function = m_doc->function(m_controlledFunctionId); if (function == nullptr || attributeIndex >= function->attributes().count()) return; @@ -944,15 +947,46 @@ void VCSlider::adjustFunctionAttribute(Function *f, qreal value) f->adjustAttribute(value, m_controlledAttributeId); } +bool VCSlider::adjustFlashEnabled() const +{ + return m_adjustFlashEnabled; +} + +void VCSlider::setAdjustFlashEnabled(bool enable) +{ + if (enable == m_adjustFlashEnabled) + return; + + m_adjustFlashEnabled = enable; + emit adjustFlashEnabledChanged(enable); +} + +void VCSlider::flashFunction(bool on) +{ + Function *function = m_doc->function(m_controlledFunctionId); + if (function == nullptr) + return; + + if (on) + { + if (m_controlledAttributeId == Function::invalidAttributeId()) + m_adjustFlashPreviousValue = 0; + else + m_adjustFlashPreviousValue = function->getAttributeValue(m_controlledAttributeIndex); + } + + adjustFunctionAttribute(function, on ? 1.0 : m_adjustFlashPreviousValue); +} + QStringList VCSlider::availableAttributes() const { QStringList list; - Function* function = m_doc->function(m_controlledFunctionId); + Function *function = m_doc->function(m_controlledFunctionId); if (function == nullptr) return list; - for (Attribute attr : function->attributes()) + for (Attribute &attr : function->attributes()) list << attr.m_name; return list; @@ -1053,7 +1087,7 @@ void VCSlider::writeDMXLevel(MasterTimer* timer, QList universes) bool mixedDMXlevels = false; int monitorSliderValue = -1; - for (SceneValue scv : m_levelChannels) + for (SceneValue &scv : m_levelChannels) { Fixture* fxi = m_doc->fixture(scv.fxi); if (fxi != nullptr) @@ -1108,7 +1142,7 @@ void VCSlider::writeDMXLevel(MasterTimer* timer, QList universes) if (m_levelValueChanged) { - for (SceneValue scv : m_levelChannels) + for (SceneValue &scv : m_levelChannels) { Fixture* fxi = m_doc->fixture(scv.fxi); if (fxi == nullptr) @@ -1180,7 +1214,7 @@ void VCSlider::writeDMXAdjust(MasterTimer* timer, QList ua) if (m_adjustChangeCounter == 0) return; - Function* function = m_doc->function(m_controlledFunctionId); + Function *function = m_doc->function(m_controlledFunctionId); if (function == nullptr) return; @@ -1240,6 +1274,7 @@ void VCSlider::slotInputValueChanged(quint8 id, uchar value) int scaledValue = SCALE(float(value), float(0), float(UCHAR_MAX), float(rangeLowLimit()), float(rangeHighLimit())); + switch (id) { case INPUT_SLIDER_CONTROL_ID: @@ -1249,6 +1284,9 @@ void VCSlider::slotInputValueChanged(quint8 id, uchar value) if (value) setIsOverriding(false); break; + case INPUT_SLIDER_FLASH_ID: + flashFunction(value ? true : false); + break; } } @@ -1330,6 +1368,11 @@ bool VCSlider::loadXML(QXmlStreamReader &root) { loadXMLAdjust(root); } + else if (root.name() == KXMLQLCVCSliderFunctionFlash) + { + setAdjustFlashEnabled(true); + loadXMLSources(root, INPUT_SLIDER_FLASH_ID); + } else if (root.name() == KXMLQLCVCSliderPlayback) // LEGACY { loadXMLLegacyPlayback(root); @@ -1458,6 +1501,11 @@ bool VCSlider::loadXMLLegacyPlayback(QXmlStreamReader &pb_root) setControlledFunction(pb_root.readElementText().toUInt()); setControlledAttribute(Function::Intensity); } + else if (pb_root.name() == KXMLQLCVCSliderFunctionFlash) + { + setAdjustFlashEnabled(true); + loadXMLSources(pb_root, INPUT_SLIDER_FLASH_ID); + } else { qWarning() << Q_FUNC_INFO << "Unknown slider playback tag:" << pb_root.name().toString(); @@ -1531,7 +1579,7 @@ bool VCSlider::saveXML(QXmlStreamWriter *doc) doc->writeAttribute(KXMLQLCVCSliderLevelValue, QString::number(value())); /* Level channels */ - for (SceneValue scv : m_levelChannels) + for (SceneValue &scv : m_levelChannels) { doc->writeStartElement(KXMLQLCVCSliderChannel); doc->writeAttribute(KXMLQLCVCSliderChannelFixture, QString::number(scv.fxi)); @@ -1552,6 +1600,13 @@ bool VCSlider::saveXML(QXmlStreamWriter *doc) doc->writeAttribute(KXMLQLCVCSliderControlledFunction, QString::number(controlledFunction())); /* End the tag */ doc->writeEndElement(); + + if (adjustFlashEnabled()) + { + //doc->writeStartElement(KXMLQLCVCSliderFunctionFlash); + saveXMLInputControl(doc, INPUT_SLIDER_FLASH_ID, KXMLQLCVCSliderFunctionFlash); + //doc->writeEndElement(); + } } /* End the tag */ diff --git a/qmlui/virtualconsole/vcslider.h b/qmlui/virtualconsole/vcslider.h index 573ee4ab1f..b624a271fe 100644 --- a/qmlui/virtualconsole/vcslider.h +++ b/qmlui/virtualconsole/vcslider.h @@ -44,6 +44,7 @@ #define KXMLQLCVCSliderLevelValue QString("Value") #define KXMLQLCVCSliderLevelMonitor QString("Monitor") #define KXMLQLCVCSliderOverrideReset QString("Reset") +#define KXMLQLCVCSliderFunctionFlash QString("Flash") #define KXMLQLCVCSliderChannel QString("Channel") #define KXMLQLCVCSliderChannelFixture QString("Fixture") @@ -75,6 +76,8 @@ class VCSlider : public VCWidget, public DMXSource Q_PROPERTY(int monitorValue READ monitorValue NOTIFY monitorValueChanged) Q_PROPERTY(bool isOverriding READ isOverriding WRITE setIsOverriding NOTIFY isOverridingChanged) + Q_PROPERTY(bool adjustFlashEnabled READ adjustFlashEnabled WRITE setAdjustFlashEnabled NOTIFY adjustFlashEnabledChanged) + Q_PROPERTY(quint32 controlledFunction READ controlledFunction WRITE setControlledFunction NOTIFY controlledFunctionChanged) Q_PROPERTY(int controlledAttribute READ controlledAttribute WRITE setControlledAttribute NOTIFY controlledAttributeChanged) Q_PROPERTY(QStringList availableAttributes READ availableAttributes NOTIFY availableAttributesChanged) @@ -372,6 +375,12 @@ protected slots: void adjustFunctionAttribute(Function *f, qreal value); + /** Get/Set the status of the flash button enablement */ + bool adjustFlashEnabled() const; + void setAdjustFlashEnabled(bool enable); + + Q_INVOKABLE void flashFunction(bool on); + /** Get the list of the available attributes for the Function to control */ QStringList availableAttributes() const; @@ -388,6 +397,7 @@ protected slots: signals: void controlledFunctionChanged(quint32 fid); void controlledAttributeChanged(int attr); + void adjustFlashEnabledChanged(bool enable); void availableAttributesChanged(); void attributeMinValueChanged(); void attributeMaxValueChanged(); @@ -404,6 +414,9 @@ protected slots: qreal m_attributeMinValue; qreal m_attributeMaxValue; + bool m_adjustFlashEnabled; + qreal m_adjustFlashPreviousValue; + /********************************************************************* * Submaster *********************************************************************/ From d55aadad4e6c3ad963fcee9ab9b96a6dc0bf198b Mon Sep 17 00:00:00 2001 From: YesterKo Date: Thu, 16 May 2024 11:09:33 +0300 Subject: [PATCH 194/212] Added beamZ PS10W fixture --- resources/fixtures/beamZ/beamZ-PS10W.qxf | 49 ++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 resources/fixtures/beamZ/beamZ-PS10W.qxf diff --git a/resources/fixtures/beamZ/beamZ-PS10W.qxf b/resources/fixtures/beamZ/beamZ-PS10W.qxf new file mode 100644 index 0000000000..3a56036ac6 --- /dev/null +++ b/resources/fixtures/beamZ/beamZ-PS10W.qxf @@ -0,0 +1,49 @@ + + + + + Q Light Controller Plus + 4.13.0 GIT + Jesper Korsen + + beamZ + PS10W + Color Changer + + + + + + + Shutter + Lamp on + Strobe (Slow to fast) + + + Colour + Normal function + Color wheel speed + + + Red + Green + Blue + White + + + Dimmer + Red + Green + Blue + White + Strobe + Color wheel + + + + + + + + + From febc5959a7bf5ddae328f2f037e18581118b4bde Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Thu, 16 May 2024 17:32:59 +0200 Subject: [PATCH 195/212] Update beamZ-PS10W.qxf --- resources/fixtures/beamZ/beamZ-PS10W.qxf | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/resources/fixtures/beamZ/beamZ-PS10W.qxf b/resources/fixtures/beamZ/beamZ-PS10W.qxf index 3a56036ac6..60f4e31b94 100644 --- a/resources/fixtures/beamZ/beamZ-PS10W.qxf +++ b/resources/fixtures/beamZ/beamZ-PS10W.qxf @@ -16,21 +16,21 @@ Shutter - Lamp on - Strobe (Slow to fast) + Lamp on + Strobe (Slow to fast) Colour Normal function Color wheel speed - + Red Green Blue White - + Dimmer Red Green @@ -41,7 +41,7 @@ - + From 8378b8febcc8b2bff47728084662935dc12c712e Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Thu, 16 May 2024 18:54:00 +0200 Subject: [PATCH 196/212] resources: 4 new fixtures (see changelog) --- debian/changelog | 3 + resources/fixtures/Ayra/Ayra-ComPar-10.qxf | 76 +++ resources/fixtures/Ayra/Ayra-ERO-406.qxf | 82 +++ resources/fixtures/FixturesMap.xml | 5 + .../Mac_Mah/Mac-Mah-Moving-FX-Bar.qxf | 478 ++++++++++++++++++ .../Martin/Martin-RUSH-Par-2-RGBW-Zoom.qxf | 38 +- .../Varytec-LED-Pad-Bar-Compact-ST-RGB.qxf | 157 ++++++ resources/fixtures/beamZ/beamZ-PS10W.qxf | 8 +- 8 files changed, 824 insertions(+), 23 deletions(-) create mode 100644 resources/fixtures/Ayra/Ayra-ComPar-10.qxf create mode 100644 resources/fixtures/Ayra/Ayra-ERO-406.qxf create mode 100644 resources/fixtures/Mac_Mah/Mac-Mah-Moving-FX-Bar.qxf create mode 100644 resources/fixtures/Varytec/Varytec-LED-Pad-Bar-Compact-ST-RGB.qxf diff --git a/debian/changelog b/debian/changelog index 3ebaa57faa..03338804ba 100644 --- a/debian/changelog +++ b/debian/changelog @@ -14,6 +14,9 @@ qlcplus (4.13.1) stable; urgency=low * 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 (thanks to Clément Delabroye) -- Massimo Callegari Sun, 30 Jun 2024 12:13:14 +0200 diff --git a/resources/fixtures/Ayra/Ayra-ComPar-10.qxf b/resources/fixtures/Ayra/Ayra-ComPar-10.qxf new file mode 100644 index 0000000000..3de538d5d1 --- /dev/null +++ b/resources/fixtures/Ayra/Ayra-ComPar-10.qxf @@ -0,0 +1,76 @@ + + + + + Q Light Controller Plus + 5.0.0 Beta 3 + René Knuvers + + Ayra + ComPar 10 + Color Changer + + + + + + + + + Effect + Blackout + Color Macro + Color Jumping + Color Fading + Sound Control (mic sens) + + + Shutter + No function + Strobe slow to fast + + + Dimmer + Color macro + + + Red + Green + Blue + + + Dimmer + Strobe + Effect + + + Red + Green + Blue + White + + + Red + Green + Blue + White + Amber + + + Dimmer + Strobe + Red + Green + Blue + White + Amber + Effect + + + + + + + + + diff --git a/resources/fixtures/Ayra/Ayra-ERO-406.qxf b/resources/fixtures/Ayra/Ayra-ERO-406.qxf new file mode 100644 index 0000000000..797ea1ba54 --- /dev/null +++ b/resources/fixtures/Ayra/Ayra-ERO-406.qxf @@ -0,0 +1,82 @@ + + + + + Q Light Controller Plus + 5.0.0 Beta 3 + René Knuvers + + Ayra + ERO 406 + Moving Head + + + + + + + + + + + + + + + Effect + No Function + Reset After 3s + No Function + Sound Controlled + + + Shutter + Blackout + Open + Stroboscope slow - fast + Open + Shutter slow open, close fast, slow-fast + Open + Shutter slow closed, open fast, slow-fast + Open + Random strobe + Open + + + Pan + Tilt + Shutter + Red + Green + Blue + White + Amber + UV + Dimmer + Diverse Functions + + + Pan + Pan fine + Tilt + Tilt fine + Pan/Tilt speed + Shutter + Red + Green + Blue + White + Amber + UV + Dimmer + Color macro + Diverse Functions + + + + + + + + + diff --git a/resources/fixtures/FixturesMap.xml b/resources/fixtures/FixturesMap.xml index e3581ab93b..28cff75782 100644 --- a/resources/fixtures/FixturesMap.xml +++ b/resources/fixtures/FixturesMap.xml @@ -209,9 +209,11 @@ + + @@ -244,6 +246,7 @@ + @@ -1198,6 +1201,7 @@ + @@ -1790,6 +1794,7 @@ + diff --git a/resources/fixtures/Mac_Mah/Mac-Mah-Moving-FX-Bar.qxf b/resources/fixtures/Mac_Mah/Mac-Mah-Moving-FX-Bar.qxf new file mode 100644 index 0000000000..3c1c7dbd33 --- /dev/null +++ b/resources/fixtures/Mac_Mah/Mac-Mah-Moving-FX-Bar.qxf @@ -0,0 +1,478 @@ + + + + + Q Light Controller Plus + 5.0.0 Beta 3 + Clément Delabroye + + Mac Mah + Moving-FX Bar + Other + + + Effect + No function + Auto 1 program + Auto 2 program + Auto 3 program + Auto 4 program + Auto 5 program + Auto 6 program + Auto 7 program + Auto 8 program + Auto 9 program + Auto 10 program + Sound 1 program + Sound 2 program + Sound 3 program + Sound 4 program + Sound 5 program + Sound 6 program + Sound 7 program + Sound 8 program + Sound 9 program + Sound 10 program + + + Speed + Slow to Fast + + + Maintenance + No function + Reset to 0 + No function + + + + + + + Colour + White + Red + Amber + Yellow + Green + Blue + Cyan + Violet + White + Red + Red + Amber + Amber + yellow + Yellow + green + Green + Blue + Blue + Azure + Azure + violet + + + Gobo + Open + GOBO 1 + GOBO 2 + GOBO 3 + GOBO 4 + GOBO 5 + GOBO 6 + GOBO 7 + SPOT SHAKE + GOBO 1 SHAKE + GOBO 2 SHAKE + GOBO 3 SHAKE + GOBO 4 SHAKE + GOBO 5 SHAKE + GOBO 6 SHAKE + GOBO 7 SHAKE + + + + + + Colour + White + Red + Amber + Yellow + Green + Blue + Cyan + Violet + White + Red + Red + Amber + Amber + yellow + Yellow + green + Green + Blue + Blue + Azure + Azure + violet + + + Gobo + Open + GOBO 1 + GOBO 2 + GOBO 3 + GOBO 4 + GOBO 5 + GOBO 6 + GOBO 7 + SPOT SHAKE + GOBO 1 SHAKE + GOBO 2 SHAKE + GOBO 3 SHAKE + GOBO 4 SHAKE + GOBO 5 SHAKE + GOBO 6 SHAKE + GOBO 7 SHAKE + + + + Colour + No function + Red + Green + Blue + UV + Yellow + Magenta + Cyan + Dark orange + Yellow green + Salmon + Turquoise + Light green + Orange + Straw + Lavender + Light blue + Dark blue + Rose + Full + + + Colour + No function + Red + Green + Blue + UV + Yellow + Magenta + Cyan + Dark orange + Yellow green + Salmon + Turquoise + Light green + Orange + Straw + Lavender + Light blue + Dark blue + Rose + Full + + + + Colour + No function + Red + Green + Blue + White + Amber + Rose + Red + Green + Pink + Red + Blue + Pink + Red + White + Pink + Red + Amber + Pink + Green + Blue + Pink + Green + White + Pink + Green + Amber + Pink + Blue + White + Pink + Blue + Amber + Pink + White + Amber + Pink + Red + Green + Blue + Red + Blue + White + Red + White + Amber + Red + Green + Amber + Red + Blue + Amber + Green +Blue + White + Green + White + Amber + Green + Blue + Amber + ALL LEDs ON + + + Colour + No function + Red + Green + Blue + White + Amber + Rose + Red + Green + Pink + Red + Blue + Pink + Red + White + Pink + Red + Amber + Pink + Green + Blue + Pink + Green + White + Pink + Green + Amber + Pink + Blue + White + Pink + Blue + Amber + Pink + White + Amber + Pink + Red + Green + Blue + Red + Blue + White + Red + White + Amber + Red + Green + Amber + Red + Blue + Amber + Green +Blue + White + Green + White + Amber + Green + Blue + Amber + ALL LEDs ON + + + Speed + No function + Derby Motor Speed + + + Colour + No function + Laser red + Laser green + Laser red + green + Laser R+G effect group(speed lowhigh) + + + Speed + No function + Clockwise rotation + Counterclockwise rotation + + + + Effect + No function + Bar-4 white LED ON + Different case + Different case running + + + + + + + + + + + + + + + + + + + + + + + + + + + Master dimmer + Program + Auto and Sound programme speed setting + Reset + + + Master dimmer + Moving - Strobe + Moving L - Dimmer + Moving L - Pan + Moving L - Tilt + Moving L - Color + Moving L - Gobo + Moving R - Dimmer + Moving R - Pan + Moving R - Tilt + Moving R - Color + Moving R - Gobo + PAR - Strobe + PAR L - Color + PAR R - Color + Derby - Strobe + Derby L - Color + Derby R - Color + Derby - Motor Speed + Laser - Color + Laser - Speed + Led Bar - Strobe + Led Bar - Effect + Program + Auto and Sound programme speed setting + Reset + + 0 + 2 + 3 + 4 + 5 + 6 + + + 0 + 7 + 8 + 9 + 10 + 11 + + + 13 + 0 + + + 14 + 0 + + + 16 + 0 + + + 17 + 0 + + + 19 + 20 + 0 + + + 21 + 0 + + + + Master dimmer + Moving - Strobe + Moving L - Dimmer + Moving L - Pan + Moving L - Pan macro speed + Moving L - Tilt + Moving L - Tilt macro speed + Moving L - Color + Moving L - Gobo + Moving R - Dimmer + Moving R - Pan + Moving R - Pan macro speed + Moving R - Tilt + Moving R - Tilt macro speed + Moving R - Color + Moving R - Gobo + PAR - Strobe + PAR L - Red + PAR L - Green + PAR L - Blue + PAR L - UV + PAR R - Red + PAR R - Green + PAR R - Blue + PAR R - UV + Derby - Strobe + Derby L - Red + Derby L - Green + Derby L - Blue + Derby L - White + Derby L - Amber + Derby L - Pink + Derby R - Red + Derby R - Green + Derby R - Blue + Derby R - White + Derby R - Amber + Derby R - Pink + Derby - Motor Speed + Laser - Color + Laser - Speed + Led Bar - Strobe + Led Bar - Effect + Program + Auto and Sound programme speed setting + Reset + + 0 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + + + 0 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + + + 0 + 17 + 18 + 19 + 20 + + + 0 + 21 + 22 + 23 + 24 + + + 0 + 26 + 27 + 28 + 29 + 30 + 31 + + + 0 + 32 + 33 + 34 + 35 + 36 + 37 + + + 0 + 39 + 40 + + + 0 + 41 + 42 + + + + + + + + + + + diff --git a/resources/fixtures/Martin/Martin-RUSH-Par-2-RGBW-Zoom.qxf b/resources/fixtures/Martin/Martin-RUSH-Par-2-RGBW-Zoom.qxf index d6e2defc2a..405c1454ab 100644 --- a/resources/fixtures/Martin/Martin-RUSH-Par-2-RGBW-Zoom.qxf +++ b/resources/fixtures/Martin/Martin-RUSH-Par-2-RGBW-Zoom.qxf @@ -3,17 +3,17 @@ Q Light Controller Plus - 4.12.5 GIT - jeremy-AMMD + 5.0.0 Beta 3 + jeremy-AMMD, Giacomo Gorini Martin RUSH Par 2 RGBW Zoom Color Changer - - - - - + + + + + @@ -63,39 +63,39 @@ Random color, medium Random color, slow - + Shutter Off Open Strobe - slow~fast Open Pulse - fast close & slow open - Open - Pulse - slow close & fast open + Open + Pulse - slow close & fast open Open Random strobe Open - Red 1 - Green 1 - Blue 1 - White 1 + Red + Green + Blue + White Zoom Dimmer Dimmer Fine Strobe - Red 1 - Green 1 - Blue 1 - White 1 + Red + Green + Blue + White Color wheel Zoom - + diff --git a/resources/fixtures/Varytec/Varytec-LED-Pad-Bar-Compact-ST-RGB.qxf b/resources/fixtures/Varytec/Varytec-LED-Pad-Bar-Compact-ST-RGB.qxf new file mode 100644 index 0000000000..d16a202989 --- /dev/null +++ b/resources/fixtures/Varytec/Varytec-LED-Pad-Bar-Compact-ST-RGB.qxf @@ -0,0 +1,157 @@ + + + + + Q Light Controller Plus + 5.0.0 Beta 3 + Clément Delabroye + + Varytec + LED Pad Bar Compact ST RGB + LED Bar (Beams) + + Effect + No function + Automatic programmes + Stroboscope programmes + Shows + Automatic programmes sound mode + Stroboscope programmes sound mode + Shows sound mode + + + Effect + Sub program select + + + Speed + Program Speed + + + + + + Shutter + Dimmer (0 % to 100 %) + Strobe effect sound mode, increasing speed + Strobe effect, increasing speed + All LEDs on + + + + + + + + + + + + + + + + + + + + + + + + Shutter + Strobe + + + Shutter + Strobe + + + Shutter + Strobe + + + Shutter + Strobe + + + Program select + Sub program select + Program Speed (Slow to fast) + + + Red + Green + Blue + Dimmer & Strobe + Strobe + + + Spot 1 - Red + Spot 1 - Green + Spot 1 - Blue + Spot 1 - Dimmer + Spot 1 - Strobe + Spot 2 - Red + Spot 2 -Green fine + Spot 2 -Blue + Spot 2 -Dimmer + Spot 2 - Strobe + Spot 3 - Red + Spot 3 - Green + Spot 3 - Blue + Spot 3 - Dimmer + Spot 3 - Strobe + Spot 4 - Red + Spot 4 - Green + Spot 4 - Blue + Spot 4 - Dimmer + Spot 4 - Strobe + LED 1 - Strobe + LED 2 - Strobe + LED 3 - Strobe + LED 4 - Strobe + + 0 + 4 + 3 + 2 + 1 + + + 5 + 6 + 7 + 8 + 9 + + + 10 + 11 + 12 + 13 + 14 + + + 15 + 16 + 17 + 18 + 19 + + + 20 + 21 + 22 + 23 + + + + + + + + + + + diff --git a/resources/fixtures/beamZ/beamZ-PS10W.qxf b/resources/fixtures/beamZ/beamZ-PS10W.qxf index 60f4e31b94..302fe20168 100644 --- a/resources/fixtures/beamZ/beamZ-PS10W.qxf +++ b/resources/fixtures/beamZ/beamZ-PS10W.qxf @@ -19,10 +19,10 @@ Lamp on Strobe (Slow to fast) - + Colour - Normal function - Color wheel speed + No function + Colour random, from slow to fast Red @@ -37,7 +37,7 @@ Blue White Strobe - Color wheel + Auto From 12bb9fdc4a6e02a3f915cf7449af1949829a2f79 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Sat, 18 May 2024 12:29:08 +0200 Subject: [PATCH 197/212] webaccess: add getWidgetSubIdList API (fix #1559) --- debian/changelog | 1 + ui/src/virtualconsole/vcmatrix.cpp | 10 +++ ui/src/virtualconsole/vcmatrix.h | 1 + ui/src/virtualconsole/vcmatrixcontrol.h | 2 +- ui/src/virtualconsole/vcxypad.cpp | 10 +++ ui/src/virtualconsole/vcxypad.h | 1 + ui/src/virtualconsole/vcxypadpreset.h | 1 - webaccess/res/Test_Web_API.html | 90 ++++++++++++++++++------- webaccess/src/webaccess.cpp | 55 ++++++++++++++- 9 files changed, 141 insertions(+), 30 deletions(-) diff --git a/debian/changelog b/debian/changelog index 03338804ba..18429be4af 100644 --- a/debian/changelog +++ b/debian/changelog @@ -8,6 +8,7 @@ qlcplus (4.13.1) stable; urgency=low * Virtual Console/Slider: add an optional button to flash in playback mode * Virtual Console/XY Pad: copy presets when cloning (thanks to Hans-Jürgen Tappe) * Plugins/DMX USB: restore Vince DMX512 output (thanks to Jérôme Lebleu) + * Web Access: added getWidgetSubIdList API and Animation widget sub-control example (API test page updated) * New fixtures: beamZ BAC500 and BAC506 (thanks to Olivier Michel) * New fixtures: American DJ Par Z4, beamZ SB400, OXO ColorBeam 7 FCW IR, Pro-Lights Pixie Spot (thanks to Dmitry Kolesnikov) * New fixtures: BoomToneDJ LED PAR 7X10W 5in1, BoomToneDJ Maxi Spot 60, Mac Mah FLAT PAR 7x12W 6in1, Eurolite LED PIX-16 QCL Bar (thanks to Cédric Monféfoul) diff --git a/ui/src/virtualconsole/vcmatrix.cpp b/ui/src/virtualconsole/vcmatrix.cpp index 849dbba346..503b538a6d 100644 --- a/ui/src/virtualconsole/vcmatrix.cpp +++ b/ui/src/virtualconsole/vcmatrix.cpp @@ -820,6 +820,16 @@ QList VCMatrix::customControls() const return controls; } +QMap VCMatrix::customControlsMap() const +{ + QMap map; + + foreach (VCMatrixControl *control, m_controls.values()) + map.insert(control->m_id, VCMatrixControl::typeToString(control->m_type)); + + return map; +} + QWidget *VCMatrix::getWidget(VCMatrixControl* control) const { return m_widgets[control]; diff --git a/ui/src/virtualconsole/vcmatrix.h b/ui/src/virtualconsole/vcmatrix.h index f0b5787c4b..1926c1ce3b 100644 --- a/ui/src/virtualconsole/vcmatrix.h +++ b/ui/src/virtualconsole/vcmatrix.h @@ -229,6 +229,7 @@ private slots: void addCustomControl(VCMatrixControl const& control); void resetCustomControls(); QList customControls() const; + QMap customControlsMap() const; QWidget *getWidget(VCMatrixControl* control) const; protected slots: diff --git a/ui/src/virtualconsole/vcmatrixcontrol.h b/ui/src/virtualconsole/vcmatrixcontrol.h index dc718eb499..b71854b730 100644 --- a/ui/src/virtualconsole/vcmatrixcontrol.h +++ b/ui/src/virtualconsole/vcmatrixcontrol.h @@ -83,7 +83,7 @@ class VCMatrixControl * get the rgb value for this value of the knob */ QRgb valueToRgb(quint8 value) const; -protected: + static QString typeToString(ControlType type); static ControlType stringToType(QString str); diff --git a/ui/src/virtualconsole/vcxypad.cpp b/ui/src/virtualconsole/vcxypad.cpp index f37031492e..2f96ddbfaf 100644 --- a/ui/src/virtualconsole/vcxypad.cpp +++ b/ui/src/virtualconsole/vcxypad.cpp @@ -690,6 +690,16 @@ QList VCXYPad::presets() const return presets; } +QMap VCXYPad::presetsMap() const +{ + QMap map; + + foreach (VCXYPadPreset *control, m_presets.values()) + map.insert(control->m_id, VCXYPadPreset::typeToString(control->m_type)); + + return map; +} + void VCXYPad::slotPresetClicked(bool checked) { if (mode() == Doc::Design) diff --git a/ui/src/virtualconsole/vcxypad.h b/ui/src/virtualconsole/vcxypad.h index 4eb3511b57..b193b48c59 100644 --- a/ui/src/virtualconsole/vcxypad.h +++ b/ui/src/virtualconsole/vcxypad.h @@ -210,6 +210,7 @@ public slots: void addPreset(VCXYPadPreset const& preset); void resetPresets(); QList presets() const; + QMap presetsMap() const; protected: void updateSceneChannel(FadeChannel *fc, uchar value); diff --git a/ui/src/virtualconsole/vcxypadpreset.h b/ui/src/virtualconsole/vcxypadpreset.h index eebbe496b4..77179671c0 100644 --- a/ui/src/virtualconsole/vcxypadpreset.h +++ b/ui/src/virtualconsole/vcxypadpreset.h @@ -77,7 +77,6 @@ class VCXYPadPreset bool operator<(VCXYPadPreset const& right) const; static bool compare(VCXYPadPreset const* left, VCXYPadPreset const* right); -protected: static QString typeToString(PresetType type); static PresetType stringToType(QString str); diff --git a/webaccess/res/Test_Web_API.html b/webaccess/res/Test_Web_API.html index fe2e866628..52c6b6fa09 100644 --- a/webaccess/res/Test_Web_API.html +++ b/webaccess/res/Test_Web_API.html @@ -199,10 +199,10 @@ { document.getElementById('getWidgetsNumberBox').innerHTML = msgParams[2]; } - // Arguments is an array formatted as follows: - // Widget ID|Widget name|Widget ID|Widget name|... else if (msgParams[1] === "getWidgetsList") { + // Arguments is an array formatted as follows: + // Widget ID|Widget name|Widget ID|Widget name|... var tableCode = ""; for (i = 2; i < msgParams.length; i+=2) { @@ -222,6 +222,18 @@ status = msgParams[2] + "(Step: " + msgParams[3] + ")"; document.getElementById('getWidgetStatusBox').innerHTML = status; } + else if (msgParams[1] === "getWidgetSubIdList") + { + // Arguments is an array formatted as follows: + // Widget ID|Preset ID|Preset Type|Preset ID|Preset Type|... + var tableCode = "
    IDName
    "; + for (i = 2; i < msgParams.length; i+=2) + { + tableCode = tableCode + ""; + } + tableCode += "
    IDType
    " + msgParams[i] + "" + msgParams[i + 1] + "
    "; + document.getElementById('getWidgetSubIdBox').innerHTML = tableCode; + } else if (msgParams[1] === "getChannelsValues") { var tableCode = ""; @@ -272,22 +284,30 @@ } .apiTable th { - font-size: 18px; + font-size: 20px; color: white; - border: solid 1px white; + background-color: #3da1ff; + border: solid 1px grey; + padding: 10px 0 10px 0 !important; } .apiTable tr { font-size: 14px; - border: solid 1px white; + border: solid 1px grey; } .apiTable td { - border: solid 1px white; - padding: 2px 5px 2px 5px; + border: solid 1px grey; + padding: 5px 5px 5px 5px; margin: 0 5px 0 5px; } +.apiHeader { + font-size: 18px; + background-color: #3bc247; + padding: 10px 0 10px 0 !important; +} + .apiButton { display: table-cell; vertical-align: middle; @@ -351,9 +371,10 @@

    Q Light Controller+ Web API test page

    - + - + + - + + + + + + - + + @@ -421,6 +448,7 @@

    Q Light Controller+ Web API test page

    + + + + + + + + + + + + + - + - - - - -
    IndexValueType
    Channels APIsChannels APIs
    getChannelsValues

    Universe index:
    @@ -368,18 +389,21 @@

    Q Light Controller+ Web API test page

    Function APIsFunction APIs
    getFunctionsNumber
    Retrieve the number of functions loaded
    getFunctionsList
    Retrieve the list of functions with their ID and name
    getFunctionType

    @@ -388,6 +412,7 @@

    Q Light Controller+ Web API test page

    Retrieve the type of a function with the given ID
    getFunctionStatus

    @@ -396,6 +421,7 @@

    Q Light Controller+ Web API test page

    Retrieve the running status of a function with the given ID. Possible values are "Running", "Stopped" and "Undefined"
    setFunctionStatus

    @@ -409,8 +435,9 @@

    Q Light Controller+ Web API test page

    Virtual Console Widget APIsVirtual Console Widget APIs
    getWidgetsNumber
    Retrieve the number of widgets loaded Retrieve the list of Virtual Console Widgets with their ID and name
    getWidgetType

    @@ -429,6 +457,7 @@

    Q Light Controller+ Web API test page

    Retrieve the type of a Virtual Console Widget with the given ID
    getWidgetStatus

    @@ -438,10 +467,33 @@

    Q Light Controller+ Web API test page

    +
    getWidgetSubIdList

    + Widget ID: +
    Retrieve information about sub-widgets (aka presets) of a Virtual Console Widget with the given ID
    +
    Animation widget control

    + Animation widget ID:
    + Control ID: +
    + This API demonstrates how to control Virtual Console Animation widget. + The parameters to be used are:
    + Animation widget ID: The Animation widget ID as retrieved with the 'getWidgetsList' API
    + Control ID: The Animation control ID of preset buttons. +
    High rate APIsHigh rate APIs
    Due to the nature of some type of transmissions (for example a slider changing rapidly), @@ -512,20 +564,6 @@

    Q Light Controller+ Web API test page

    -
    Animation widget control

    - Animation widget ID:
    - Control ID: -
    - This API demonstrates how to control Virtual Console Animation widget. - The parameters to be used are:
    - Animation widget ID: The Animation widget ID as retrieved with the 'getWidgetsList' API
    - Control ID: The Animation control ID of preset buttons. -
    diff --git a/webaccess/src/webaccess.cpp b/webaccess/src/webaccess.cpp index a1343cb50a..7749d95a22 100644 --- a/webaccess/src/webaccess.cpp +++ b/webaccess/src/webaccess.cpp @@ -30,6 +30,7 @@ #include "webaccessnetwork.h" #include "vcaudiotriggers.h" #include "virtualconsole.h" +#include "rgbalgorithm.h" #include "commonjscss.h" #include "vcsoloframe.h" #include "outputpatch.h" @@ -41,11 +42,11 @@ #include "vcbutton.h" #include "vcslider.h" #include "function.h" +#include "vcmatrix.h" #include "vclabel.h" #include "vcframe.h" #include "vcclock.h" -#include "vcmatrix.h" -#include "rgbalgorithm.h" +#include "vcxypad.h" #include "qlcfile.h" #include "chaser.h" #include "doc.h" @@ -650,7 +651,57 @@ void WebAccess::slotHandleWebSocketRequest(QHttpConnection *conn, QString data) wsAPIMessage.append("STOP"); } break; + case VCWidget::AnimationWidget: + { + VCMatrix *animation = qobject_cast(widget); + wsAPIMessage.append(QString::number(animation->sliderValue())); + } + break; + default: + { + wsAPIMessage.append("0"); + } + break; + } + } + } + else if (apiCmd == "getWidgetSubIdList") + { + if (cmdList.count() < 3) + return; + + quint32 wID = cmdList[2].toUInt(); + VCWidget *widget = m_vc->widget(wID); + switch(widget->type()) + { + case VCWidget::AnimationWidget: + { + VCMatrix *animation = qobject_cast(widget); + + QMapIterator it(animation->customControlsMap()); + while (it.hasNext() == true) + { + it.next(); + wsAPIMessage.append(QString("%1|%2|").arg(it.key()).arg(it.value())); + } + // remove trailing separator + wsAPIMessage.truncate(wsAPIMessage.length() - 1); + } + break; + case VCWidget::XYPadWidget: + { + VCXYPad *xypad = qobject_cast(widget); + + QMapIterator it(xypad->presetsMap()); + while (it.hasNext() == true) + { + it.next(); + wsAPIMessage.append(QString("%1|%2|").arg(it.key()).arg(it.value())); + } + // remove trailing separator + wsAPIMessage.truncate(wsAPIMessage.length() - 1); } + break; } } else if (apiCmd == "getChannelsValues") From bfa4d2dd4a0b5e5dfbb195a654b9b73c499a8253 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Sun, 19 May 2024 12:49:15 +0200 Subject: [PATCH 198/212] resources: 6 new fixtures (see changelog) --- debian/changelog | 3 +- .../Electroconcept-Club-Scan-120.qxf | 191 ++++++++++++++++++ .../Electroconcept-Club-Scan-30.qxf | 107 ++++++++++ .../Electroconcept-LED-Blinder.qxf | 25 +++ .../Electroconcept-Micro-Spot-60-LED.qxf | 148 ++++++++++++++ .../Electroconcept-Profile-120-Spot-LED.qxf | 191 ++++++++++++++++++ resources/fixtures/FixturesMap.xml | 6 + .../Laserworld/Laserworld-EL-400RGB-MK2.qxf | 55 +++++ 8 files changed, 725 insertions(+), 1 deletion(-) create mode 100644 resources/fixtures/Electroconcept/Electroconcept-Club-Scan-120.qxf create mode 100644 resources/fixtures/Electroconcept/Electroconcept-Club-Scan-30.qxf create mode 100644 resources/fixtures/Electroconcept/Electroconcept-LED-Blinder.qxf create mode 100644 resources/fixtures/Electroconcept/Electroconcept-Micro-Spot-60-LED.qxf create mode 100644 resources/fixtures/Electroconcept/Electroconcept-Profile-120-Spot-LED.qxf create mode 100644 resources/fixtures/Laserworld/Laserworld-EL-400RGB-MK2.qxf diff --git a/debian/changelog b/debian/changelog index 18429be4af..72bf1c2d08 100644 --- a/debian/changelog +++ b/debian/changelog @@ -17,7 +17,8 @@ qlcplus (4.13.1) stable; urgency=low * 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 (thanks to Clément Delabroye) + * New fixtures: Mac Mah Moving-FX Bar, Varytec LED Pad Bar Compact ST RGB, Laserworld EL-400RGB MK2 (thanks to Clément Delabroye) + * New fixtures: Electroconcept Club Scan 30, Club Scan 120, LED Blinder, Profile 120 Spot LED, Micro Spot 60 LED (thanks to Clément Delabroye) -- Massimo Callegari Sun, 30 Jun 2024 12:13:14 +0200 diff --git a/resources/fixtures/Electroconcept/Electroconcept-Club-Scan-120.qxf b/resources/fixtures/Electroconcept/Electroconcept-Club-Scan-120.qxf new file mode 100644 index 0000000000..acf9767bb2 --- /dev/null +++ b/resources/fixtures/Electroconcept/Electroconcept-Club-Scan-120.qxf @@ -0,0 +1,191 @@ + + + + + Q Light Controller Plus + 4.13.1 GIT + Clément Delabroye + + Electroconcept + Club Scan 120 + Scanner + + + + + + + Shutter + Closed + Strobe ( Slow to Fast) + Open + + + + Colour + White + White + Red + Red + Red + Orange + Orange + Orange + Green + Green + Green + Cyan + Cyan + Cyan + Purple + Purple + Purple + Yellow + Yellow + Yellow + Blue + Blue + Blue + Pink + Pink + Pink + White + Rotation (Slow to Fast) + + + Gobo + Open + Gobo 1 + Gobo 2 + Gobo 3 + Gobo 4 + Gobo 5 + Gobo 6 + Gobo 7 + Clockwise rotation ( Slow to Fast ) + Stop + Counterclockwise rotation( Slow to Fast ) + Gobo 1 Shake + Gobo 2 Shake + Gobo 3 Shake + Gobo 4 Shake + Gobo 5 Shake + Gobo 6 Shake + Gobo 7 Shake + + + Gobo + Angle adjustment + Counterclockwise Gobo Rotation + Clockwise Gobo Rotation + Gobo Shake rotation (Slow to Fast) + + + + Prism + No effect + Prism Effect + + + Prism + Angle adjustment + Counterclockwise Prism Rotation + Clockwise Prism Rotation + Prism Shake (Slow to Fast) + + + Effect + Led gradual extinction + Led instant extinction + No Effect + Program 1 + Program 2 + Program 3 + + + Maintenance + No function + Reset Wheel + Reset Pan / Tilt + Reset All + + + Colour + White + White + Red + Red + Red + Orange + Orange + Orange + Green + Green + Green + Cyan + Cyan + Cyan + Purple + Purple + Purple + Yellow + Yellow + Yellow + Blue + Blue + Blue + Pink + Pink + Pink + White + White + Red + Orange + Green + Cyan + Purple + Yellow + Blue + Pink + White + + + Speed + Color Effect Wheel Speed (Fast to Slow) + + + Speed + Prism Rotation Speed (Fast to Slow) + + + Speed + Gobo Wheel Speed (Fast to Slow) + + + Pan + Pan fine + Tilt + Tilt fine + Pan/Tilt speed + Shutter + Dimmer + Color + Gobo + Gobo Rotation + Focus + Prism + Prism Rotation + Macro + Reset + + + Pan + Pan fine + Tilt + Tilt fine + Pan/Tilt speed + Shutter + Dimmer + Color + Gobo + Gobo Rotation + Focus + Prism + Prism Rotation + Macro + Reset + Color Effect + Color Effect Speed + Prism Rotation Speed + Gobo Wheel Speed + + + + + + + + + diff --git a/resources/fixtures/Electroconcept/Electroconcept-Club-Scan-30.qxf b/resources/fixtures/Electroconcept/Electroconcept-Club-Scan-30.qxf new file mode 100644 index 0000000000..7bb975ddc0 --- /dev/null +++ b/resources/fixtures/Electroconcept/Electroconcept-Club-Scan-30.qxf @@ -0,0 +1,107 @@ + + + + + Q Light Controller Plus + 4.13.1 GIT + Clément Delabroye + + Electroconcept + Club Scan 30 + Scanner + + + + Colour + White + Red + Green + Blue + Yellow + Purple + Cyan + Orange + Orange + Cyan + Cyan + Purple + Purple + Yellow + Yellow + Blue + Blue + Green + Green + Red + Rotation (Slow to fast) + + + Gobo + Open + Gobo 1 + Gobo 2 + Gobo 3 + Gobo 4 + Gobo 5 + Gobo 6 + Gobo 7 + Open + Gobo 1 Shake + Gobo 2 Shake + Gobo 3 Shake + Gobo 4 Shake + Gobo 5 Shake + Gobo 6 Shake + Gobo 7 Shake + Rotation ( Slow to Fast) + + + Shutter + Closed + Strobe ( Slow to Fast) + Open + + + + + Effect + No function + Program + Auto + sound + + + Maintenance + No function + Reset Pan + Reset Tilt + Reset Pan/Tilt + Reset All + + + + + Pan + Tilt + Color + Gobo + Shutter + Dimmer + Pan/Tilt speed + Show modes + Reset + + + Pan + Pan fine + Tilt + Tilt fine + Color + Gobo + Shutter + Dimmer + Pan/Tilt speed + Show modes + Reset + + + + + + + + + diff --git a/resources/fixtures/Electroconcept/Electroconcept-LED-Blinder.qxf b/resources/fixtures/Electroconcept/Electroconcept-LED-Blinder.qxf new file mode 100644 index 0000000000..b78f875671 --- /dev/null +++ b/resources/fixtures/Electroconcept/Electroconcept-LED-Blinder.qxf @@ -0,0 +1,25 @@ + + + + + Q Light Controller Plus + 4.13.1 GIT + Clément Delabroye + + Electroconcept + LED Blinder + Strobe + + + + Master dimmer + Strobe + + + + + + + + + diff --git a/resources/fixtures/Electroconcept/Electroconcept-Micro-Spot-60-LED.qxf b/resources/fixtures/Electroconcept/Electroconcept-Micro-Spot-60-LED.qxf new file mode 100644 index 0000000000..47ae4bbb8b --- /dev/null +++ b/resources/fixtures/Electroconcept/Electroconcept-Micro-Spot-60-LED.qxf @@ -0,0 +1,148 @@ + + + + + Q Light Controller Plus + 4.13.1 GIT + Clément Delabroye + + Electroconcept + Micro Spot 60 LED + Moving Head + + + + + + + Shutter + Closed + Strobe (Slow to Fast) + Open + + + + Colour + White + Red + Green + Blue + Yellow + Magenta + Cyan + Orange + Pink + Pink + White + White + Red + Red + Green + Green + Blue + Blue + Yellow + Yellow + Magenta + Magenta + Cyan + Cyan + Orange + Orange + Pink + Counterclockwise rotation ( Fast to Slow) + Clockwise rotation (Slow to Fast) + + + Gobo + Open + Gobo 1 + Gobo 2 + Gobo 3 + Gobo 4 + Gobo 5 + Gobo 6 + Gobo 7 + Clockwise rotation ( Slow to Fast ) + Stop + Counterclockwise rotation( Slow to Fast ) + Gobo 1 Shake + Gobo 2 Shake + Gobo 3 Shake + Gobo 4 Shake + Gobo 5 Shake + Gobo 6 Shake + Gobo 7 Shake + + + Gobo + Angle adjustment + Clockwise Gobo Rotation (Fast to Slow) + Stop + Counterclockwise Rotation (Slow to Fast) + + + Prism + No effect + Prism Effect + + + Effect + No Effect + Program 1 + Program 2 + Program 3 Sound + + + Maintenance + No function + Reset Wheel + Reset Pan / Tilt + Reset All + + + Colour + White + White + Red + Red + Red + Green + Green + Green + Blue + Blue + Blue + Yellow + Yellow + Yellow + Magenta + Magenta + Magenta + Cyan + Cyan + Cyan + Orange + Orange + Orange + Pink + Pink + Pink + White + White + Red + Green + Blue + Yellow + Magenta + Cyan + Orange + Pink + White + + + Pan + Pan fine + Tilt + Tilt fine + Pan/Tilt speed + Shutter + Dimmer + Color + Gobo + Gobo Rotation + Prism + Color Effect + Macro + Reset + + + + + + + + + diff --git a/resources/fixtures/Electroconcept/Electroconcept-Profile-120-Spot-LED.qxf b/resources/fixtures/Electroconcept/Electroconcept-Profile-120-Spot-LED.qxf new file mode 100644 index 0000000000..5b3458bc6f --- /dev/null +++ b/resources/fixtures/Electroconcept/Electroconcept-Profile-120-Spot-LED.qxf @@ -0,0 +1,191 @@ + + + + + Q Light Controller Plus + 4.13.1 GIT + Clément Delabroye + + Electroconcept + Profile 120 Spot LED + Moving Head + + + + + + + Shutter + Closed + Strobe (Slow to Fast) + Open + + + + Colour + White + White + Red + Red + Red + Orange + Orange + Orange + Green + Green + Green + Cyan + Cyan + Cyan + Purple + Purple + Purple + Yellow + Yellow + Yellow + Blue + Blue + Blue + Pink + Pink + Pink + White + Rotation (Slow to Fast) + + + Gobo + Open + Gobo 1 + Gobo 2 + Gobo 3 + Gobo 4 + Gobo 5 + Gobo 6 + Gobo 7 + Clockwise rotation ( Slow to Fast ) + Stop + Counterclockwise rotation( Slow to Fast ) + Gobo 1 Shake + Gobo 2 Shake + Gobo 3 Shake + Gobo 4 Shake + Gobo 5 Shake + Gobo 6 Shake + Gobo 7 Shake + + + Gobo + Angle adjustment + Counterclockwise Gobo Rotation + Clockwise Gobo Rotation + Gobo Shake rotation (Slow to Fast) + + + + Prism + No effect + Prism Effect + + + Prism + Angle adjustment + Counterclockwise Prism Rotation + Clockwise Prism Rotation + Prism Shake (Slow to Fast) + + + Effect + Led gradual extinction + Led instant extinction + No Effect + Program 1 + Program 2 + Program 3 + + + Maintenance + No function + Reset Wheel + Reset Pan / Tilt + Reset All + + + Colour + White + White + Red + Red + Red + Orange + Orange + Orange + Green + Green + Green + Cyan + Cyan + Cyan + Purple + Purple + Purple + Yellow + Yellow + Yellow + Blue + Blue + Blue + Pink + Pink + Pink + White + White + Red + Orange + Green + Cyan + Purple + Yellow + Blue + Pink + White + + + Speed + Color Effect Wheel Speed (Fast to Slow) + + + Speed + Prism Rotation Speed (Fast to Slow) + + + Speed + Gobo Wheel Speed (Fast to Slow) + + + Pan + Pan fine + Tilt + Tilt fine + Pan/Tilt speed + Shutter + Dimmer + Color + Gobo + Gobo Rotation + Focus + Prism + Prism Rotation + Macro + Reset + + + Pan + Pan fine + Tilt + Tilt fine + Pan/Tilt speed + Shutter + Dimmer + Color + Gobo + Gobo Rotation + Focus + Prism + Prism Rotation + Macro + Reset + Color Effect + Color Effect Speed + Prism Rotation Speed + Gobo Wheel Speed + + + + + + + + + diff --git a/resources/fixtures/FixturesMap.xml b/resources/fixtures/FixturesMap.xml index 28cff75782..aff08b79c7 100644 --- a/resources/fixtures/FixturesMap.xml +++ b/resources/fixtures/FixturesMap.xml @@ -701,7 +701,12 @@
    + + + + + @@ -1110,6 +1115,7 @@ + diff --git a/resources/fixtures/Laserworld/Laserworld-EL-400RGB-MK2.qxf b/resources/fixtures/Laserworld/Laserworld-EL-400RGB-MK2.qxf new file mode 100644 index 0000000000..d8479720fe --- /dev/null +++ b/resources/fixtures/Laserworld/Laserworld-EL-400RGB-MK2.qxf @@ -0,0 +1,55 @@ + + + + + Q Light Controller Plus + 4.13.1 GIT + Clément Delabroye + + Laserworld + EL-400RGB MK2 + Laser + + Effect + Laser off + Sound mode + Automatic mode + Static pattern (DMX mode) + Dynamic pattern (DMX mode) + + + Gobo + Pattern selection + + + + + Speed + Scanning speed + + + Speed + Dynamic pattern speed + + + + + + Style + Pattern selection + X axis positioning + Y axis positioning + Scanning speed + Dynamic pattern speed + Zoom / size + Color + Color segment + + + + + + + + + From 4d993ff3663fd5d65037c8e91fc83891ca2c6d2c Mon Sep 17 00:00:00 2001 From: Nils Tijtgat Date: Thu, 18 Jan 2024 09:52:47 +0100 Subject: [PATCH 199/212] Add Fixture Tester --- ui/src/fixturemanager.cpp | 120 ++++++++++++++++++++++++++++++++++++-- ui/src/fixturemanager.h | 15 +++++ 2 files changed, 130 insertions(+), 5 deletions(-) diff --git a/ui/src/fixturemanager.cpp b/ui/src/fixturemanager.cpp index 45ed7f9158..edb8b19ef8 100644 --- a/ui/src/fixturemanager.cpp +++ b/ui/src/fixturemanager.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include "qlcfixturemode.h" #include "qlcfixturedef.h" @@ -45,6 +46,7 @@ #include "createfixturegroup.h" #include "fixturegroupeditor.h" #include "fixturetreewidget.h" +#include "genericdmxsource.h" #include "channelsselection.h" #include "addchannelsgroup.h" #include "fixturemanager.h" @@ -75,6 +77,7 @@ FixtureManager* FixtureManager::s_instance = NULL; FixtureManager::FixtureManager(QWidget* parent, Doc* doc) : QWidget(parent) , m_doc(doc) + , m_fixtureTestEnabled(true) , m_splitter(NULL) , m_fixtures_tree(NULL) , m_channel_groups_tree(NULL) @@ -151,6 +154,11 @@ FixtureManager::~FixtureManager() settings.setValue(SETTINGS_SPLITTER, m_splitter->saveState()); FixtureManager::s_instance = NULL; + QHash::iterator it; + for (it = m_selectedFixtureHash.begin(); it != m_selectedFixtureHash.end(); ++it) { + delete it.value(); + } + s_instance = NULL; } @@ -212,6 +220,7 @@ void FixtureManager::slotChannelsGroupRemoved(quint32 id) void FixtureManager::slotModeChanged(Doc::Mode mode) { + // TO-DO: hier moeje die check shit toevoegen ojo! if (mode == Doc::Design) { int selected = m_fixtures_tree->selectedItems().size(); @@ -269,6 +278,8 @@ void FixtureManager::slotModeChanged(Doc::Mode mode) m_fadeConfigAction->setEnabled(true); else m_fadeConfigAction->setEnabled(false); + + m_testFixturesAction->setEnabled(true); } else { @@ -279,7 +290,10 @@ void FixtureManager::slotModeChanged(Doc::Mode mode) m_fadeConfigAction->setEnabled(false); m_groupAction->setEnabled(false); m_unGroupAction->setEnabled(false); + m_testFixturesAction->setEnabled(false); } + + updateTestFixtures(); } void FixtureManager::slotFixtureGroupRemoved(quint32 id) @@ -494,6 +508,84 @@ void FixtureManager::updateRDMView() m_remapAction->setEnabled(false); } +void FixtureManager::updateTestFixtures() +{ + + qDebug() << "Run updateTestFixtures()"; + + QList m_selectedFixtures = m_fixtures_tree->selectedItems(); + QSet selectedFixtureIds; + foreach (QTreeWidgetItem* item , m_selectedFixtures) + { + selectedFixtureIds.insert(item->data(KColumnName, PROP_ID).toUInt()); + } + + QSet newlySelectedFixtureIds = selectedFixtureIds.subtract(m_lastSelectedFixtureIds); + QSet deselectedFixtureIds = m_lastSelectedFixtureIds.subtract(selectedFixtureIds); + + // TO-DO: check that no other conditions have to be set! + if(m_doc->mode() == Doc::Design && m_fixtureTestEnabled) + { + // Turn on all selected fixtures + QSet::const_iterator it; + for (it = newlySelectedFixtureIds.constBegin(); it != newlySelectedFixtureIds.constEnd(); ++it) { + testFixture(*it); + } + + // Turn all deselected fixtures off + for (it = deselectedFixtureIds.constBegin(); it != deselectedFixtureIds.constEnd(); ++it) { + untestFixture(*it); + } + } else { + // Turn all fixtures off + QHash::iterator it; + foreach(const quint32 &fixtureId, m_selectedFixtureHash.keys()) { + untestFixture(fixtureId); + } + } + + // TO-DO: terugkeren uit een selectie tijdens de niet edit fase werkt nog nie! + m_lastSelectedFixtureIds = selectedFixtureIds; + +} + +void FixtureManager::testFixture(quint32 id) +{ + Fixture* fxi = m_doc->fixture(id); + if (fxi == NULL) + return; + + GenericDMXSource* source = new GenericDMXSource(m_doc); + m_selectedFixtureHash.insert(id, source); + + // TO-DO: make fixture white! + + // Set the Intensity for every Head to 255 + for (int i = 0; i < fxi->heads(); i++) + { + QLCFixtureHead head= fxi->head(i); + quint32 jo = head.channelNumber(QLCChannel::Intensity, QLCChannel::MSB); + source->set(fxi->id(), jo, 255); + } + + source->setOutputEnabled(true); +} + +void FixtureManager::untestFixture(quint32 id) +{ + Fixture* fxi = m_doc->fixture(id); + if (fxi == NULL) + return; + + if(!m_selectedFixtureHash.contains(id)) + return; + + delete m_selectedFixtureHash.value(id); + m_selectedFixtureHash.remove(id); + + qDebug() << "Untest fixture" << id; +} + void FixtureManager::fixtureSelected(quint32 id) { Fixture* fxi = m_doc->fixture(id); @@ -504,11 +596,8 @@ void FixtureManager::fixtureSelected(quint32 id) createInfo(); m_info->setText(QString("%1%2") - .arg(fixtureInfoStyleSheetHeader()) - .arg(fxi->status())); - - // Enable/disable actions - slotModeChanged(m_doc->mode()); + .arg(fixtureInfoStyleSheetHeader()) + .arg(fxi->status())); } void FixtureManager::fixtureGroupSelected(FixtureGroup* grp) @@ -936,6 +1025,11 @@ void FixtureManager::initActions() tr("Remap fixtures..."), this); connect(m_remapAction, SIGNAL(triggered(bool)), this, SLOT(slotRemap())); + + m_testFixturesAction = new QAction(QIcon(":/fixture.png"), + tr("Enable fixture tester..."), this); + connect(m_testFixturesAction, SIGNAL(triggered(bool)), + this, SLOT(slotTestFixtures())); } void FixtureManager::updateGroupMenu() @@ -985,6 +1079,7 @@ void FixtureManager::initToolBar() toolbar->addAction(m_importAction); toolbar->addAction(m_exportAction); toolbar->addAction(m_remapAction); + toolbar->addAction(m_testFixturesAction); QToolButton* btn = qobject_cast (toolbar->widgetForAction(m_groupAction)); Q_ASSERT(btn != NULL); @@ -1515,6 +1610,21 @@ void FixtureManager::slotRemap() updateView(); } +void FixtureManager::slotTestFixtures() +{ + // Update the icon + if(m_fixtureTestEnabled) + { + m_testFixturesAction->setIcon(QIcon(":/remap.png")); + } else + { + m_testFixturesAction->setIcon(QIcon(":/fixture.png")); + } + + m_fixtureTestEnabled = !m_fixtureTestEnabled; + slotModeChanged(m_doc->mode()); +} + void FixtureManager::slotUnGroup() { if (QMessageBox::question(this, tr("Ungroup fixtures?"), diff --git a/ui/src/fixturemanager.h b/ui/src/fixturemanager.h index d0a5bed521..f908f622ec 100644 --- a/ui/src/fixturemanager.h +++ b/ui/src/fixturemanager.h @@ -25,6 +25,7 @@ #include "function.h" #include "fixture.h" #include "doc.h" +#include "genericdmxsource.h" class QLCFixtureDefCache; class FixtureGroupEditor; @@ -88,6 +89,9 @@ public slots: private: Doc* m_doc; + bool m_fixtureTestEnabled; + QSet m_lastSelectedFixtureIds; + QHash m_selectedFixtureHash; /******************************************************************** * Data view @@ -118,6 +122,15 @@ public slots: /** Construct the list view and data view */ void initDataView(); + /** TO-DO: DESCRIPTION */ + void updateTestFixtures(); + + /** TO-DO: Test fixture when Fixture Tester is enabled */ + void testFixture(quint32 id); + + /** TO-DO: Test fixture when Fixture Tester is enabled */ + void untestFixture(quint32 id); + /** Handle single fixture selection */ void fixtureSelected(quint32 id); @@ -199,6 +212,7 @@ private slots: void slotProperties(); void slotFadeConfig(); void slotRemap(); + void slotTestFixtures(); void slotUnGroup(); void slotGroupSelected(QAction* action); void slotMoveGroupUp(); @@ -216,6 +230,7 @@ private slots: QAction* m_propertiesAction; QAction* m_fadeConfigAction; QAction* m_remapAction; + QAction* m_testFixturesAction; QAction* m_groupAction; QAction* m_unGroupAction; QAction* m_newGroupAction; From f19b4b654053b681eace562c960dd7c637a7f9af Mon Sep 17 00:00:00 2001 From: Nils Tijtgat Date: Thu, 18 Jan 2024 18:09:14 +0100 Subject: [PATCH 200/212] Refine implementation --- resources/icons/png/fixture_off.png | Bin 0 -> 1418 bytes resources/icons/svg/fixture_off.svg | 133 ++++++++++++++++++++++++++++ ui/src/fixturemanager.cpp | 47 +++++----- ui/src/fixturemanager.h | 16 ++-- 4 files changed, 162 insertions(+), 34 deletions(-) create mode 100644 resources/icons/png/fixture_off.png create mode 100644 resources/icons/svg/fixture_off.svg diff --git a/resources/icons/png/fixture_off.png b/resources/icons/png/fixture_off.png new file mode 100644 index 0000000000000000000000000000000000000000..86eb77b1a8fa14ce9550c14aea1fed369323cc6d GIT binary patch literal 1418 zcmV;51$Fv~P)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2iOGx z2ro2$UO|@t00jz3L_t(o!_AjlY!p=($A4!oyR)6`W!i4n-h^(K?8dqkDVCN}9uO#@ zDyE?HO`9Nz661p*rXe&K#YiwlLbV}CVu;}dc_EUN21QUY5HMhBpwtCpY0T z2d8ygOD|H?FF850d(NEiKmYG~;6D~I{$N=a0Fg*U5<(z^hzKE~_sM`{KMyYy00m&f zh7GdE<0@3tYEnWyA_4oH{KoT$pSbzp311hi)xVvC1$g*4swClS5w#(%z2YkR1 z;Bg=mh}CsH&gb(L08PN|yu7>xGlK-20EWF@@2WZM-v!(eLc}f@0>d!20j3b*)!Fvy zy1o<`1o9W^>)E<>>(@Y^Wm%8R_CiffO@ky!KL9(P2kw`>V6h#7@hGC?5Jf2EPlJ>dX z?p1)Qs%k3G3hWpU-+M7^7)JJyBS&TjwQSk4ML?(1>AYTEUj7PD2ebg)b|{+md_Fcd zwpfy+YT(OoIP99?*aV{3WY+)fKeb~=YZ%r0*n9`fa>Dn z;%ByR-yS@0;6U6o$L2=?zXE&=toYlmy1F_M3WZ(<_5x-g5I8Q&a(9&Csl%Kyx`3;| zr@(VSTw!5hqQl`h81*5hJ%6uhTKpsc>FMb{;5XnP(BXEw>r+!xFU|lUGUd={hDL2? zzGE|Gs)}eTT9I^1*Oj|~sjBMk!-o(5xyan zoFxHhnl_Gn5QqWBWBd8i($cQ^DhGz%-xW%ey^e9o74negU*@*|KH%EMS5Q!p_>a2f`<3SAX8!l` Y2SA7N&&CE#w*UYD07*qoM6N<$f~IezDgXcg literal 0 HcmV?d00001 diff --git a/resources/icons/svg/fixture_off.svg b/resources/icons/svg/fixture_off.svg new file mode 100644 index 0000000000..0b2683261e --- /dev/null +++ b/resources/icons/svg/fixture_off.svg @@ -0,0 +1,133 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + diff --git a/ui/src/fixturemanager.cpp b/ui/src/fixturemanager.cpp index edb8b19ef8..5f517f2b7a 100644 --- a/ui/src/fixturemanager.cpp +++ b/ui/src/fixturemanager.cpp @@ -77,7 +77,7 @@ FixtureManager* FixtureManager::s_instance = NULL; FixtureManager::FixtureManager(QWidget* parent, Doc* doc) : QWidget(parent) , m_doc(doc) - , m_fixtureTestEnabled(true) + , m_testFixturesEnabled(true) , m_splitter(NULL) , m_fixtures_tree(NULL) , m_channel_groups_tree(NULL) @@ -155,7 +155,7 @@ FixtureManager::~FixtureManager() FixtureManager::s_instance = NULL; QHash::iterator it; - for (it = m_selectedFixtureHash.begin(); it != m_selectedFixtureHash.end(); ++it) { + for (it = m_fixtureToSourceMap.begin(); it != m_fixtureToSourceMap.end(); ++it) { delete it.value(); } @@ -293,7 +293,7 @@ void FixtureManager::slotModeChanged(Doc::Mode mode) m_testFixturesAction->setEnabled(false); } - updateTestFixtures(); + runTestFixtures(); } void FixtureManager::slotFixtureGroupRemoved(quint32 id) @@ -508,14 +508,11 @@ void FixtureManager::updateRDMView() m_remapAction->setEnabled(false); } -void FixtureManager::updateTestFixtures() +void FixtureManager::runTestFixtures() { - - qDebug() << "Run updateTestFixtures()"; - - QList m_selectedFixtures = m_fixtures_tree->selectedItems(); + QList selectedFixtures = m_fixtures_tree->selectedItems(); QSet selectedFixtureIds; - foreach (QTreeWidgetItem* item , m_selectedFixtures) + foreach (QTreeWidgetItem* item , selectedFixtures) { selectedFixtureIds.insert(item->data(KColumnName, PROP_ID).toUInt()); } @@ -523,42 +520,40 @@ void FixtureManager::updateTestFixtures() QSet newlySelectedFixtureIds = selectedFixtureIds.subtract(m_lastSelectedFixtureIds); QSet deselectedFixtureIds = m_lastSelectedFixtureIds.subtract(selectedFixtureIds); - // TO-DO: check that no other conditions have to be set! - if(m_doc->mode() == Doc::Design && m_fixtureTestEnabled) + if(m_doc->mode() == Doc::Design && m_testFixturesEnabled) { // Turn on all selected fixtures QSet::const_iterator it; for (it = newlySelectedFixtureIds.constBegin(); it != newlySelectedFixtureIds.constEnd(); ++it) { - testFixture(*it); + turnFixtureOn(*it); } // Turn all deselected fixtures off for (it = deselectedFixtureIds.constBegin(); it != deselectedFixtureIds.constEnd(); ++it) { - untestFixture(*it); + turnFixtureOff(*it); } } else { // Turn all fixtures off QHash::iterator it; - foreach(const quint32 &fixtureId, m_selectedFixtureHash.keys()) { - untestFixture(fixtureId); + foreach(const quint32 &fixtureId, m_fixtureToSourceMap.keys()) { + turnFixtureOff(fixtureId); } } - // TO-DO: terugkeren uit een selectie tijdens de niet edit fase werkt nog nie! m_lastSelectedFixtureIds = selectedFixtureIds; - } -void FixtureManager::testFixture(quint32 id) +void FixtureManager::turnFixtureOn(quint32 id) { Fixture* fxi = m_doc->fixture(id); if (fxi == NULL) return; GenericDMXSource* source = new GenericDMXSource(m_doc); - m_selectedFixtureHash.insert(id, source); + m_fixtureToSourceMap.insert(id, source); // TO-DO: make fixture white! + // TO-DO: check if this works for complex lights (Mac Quantum for instance) // Set the Intensity for every Head to 255 for (int i = 0; i < fxi->heads(); i++) @@ -571,17 +566,17 @@ void FixtureManager::testFixture(quint32 id) source->setOutputEnabled(true); } -void FixtureManager::untestFixture(quint32 id) +void FixtureManager::turnFixtureOff(quint32 id) { Fixture* fxi = m_doc->fixture(id); if (fxi == NULL) return; - if(!m_selectedFixtureHash.contains(id)) + if(!m_fixtureToSourceMap.contains(id)) return; - delete m_selectedFixtureHash.value(id); - m_selectedFixtureHash.remove(id); + delete m_fixtureToSourceMap.value(id); + m_fixtureToSourceMap.remove(id); qDebug() << "Untest fixture" << id; } @@ -1613,15 +1608,15 @@ void FixtureManager::slotRemap() void FixtureManager::slotTestFixtures() { // Update the icon - if(m_fixtureTestEnabled) + if(m_testFixturesEnabled) { - m_testFixturesAction->setIcon(QIcon(":/remap.png")); + m_testFixturesAction->setIcon(QIcon(":/fixture_off.png")); } else { m_testFixturesAction->setIcon(QIcon(":/fixture.png")); } - m_fixtureTestEnabled = !m_fixtureTestEnabled; + m_testFixturesEnabled = !m_testFixturesEnabled; slotModeChanged(m_doc->mode()); } diff --git a/ui/src/fixturemanager.h b/ui/src/fixturemanager.h index f908f622ec..1881540b15 100644 --- a/ui/src/fixturemanager.h +++ b/ui/src/fixturemanager.h @@ -89,9 +89,9 @@ public slots: private: Doc* m_doc; - bool m_fixtureTestEnabled; + bool m_testFixturesEnabled; QSet m_lastSelectedFixtureIds; - QHash m_selectedFixtureHash; + QHash m_fixtureToSourceMap; /******************************************************************** * Data view @@ -122,14 +122,14 @@ public slots: /** Construct the list view and data view */ void initDataView(); - /** TO-DO: DESCRIPTION */ - void updateTestFixtures(); + /** Checks if any fixtures have to be turned on/off if Test Fixtures is enabled */ + void runTestFixtures(); - /** TO-DO: Test fixture when Fixture Tester is enabled */ - void testFixture(quint32 id); + /** Creates a GenericDmxSource and turns the fixture on */ + void turnFixtureOn(quint32 id); - /** TO-DO: Test fixture when Fixture Tester is enabled */ - void untestFixture(quint32 id); + /** Turns the fixture off and removes the related GenericDmxSource */ + void turnFixtureOff(quint32 id); /** Handle single fixture selection */ void fixtureSelected(quint32 id); From 8843c90404e6f754eba1c7d47449c03a3d125c03 Mon Sep 17 00:00:00 2001 From: Nils Tijtgat Date: Thu, 18 Jan 2024 19:39:33 +0100 Subject: [PATCH 201/212] Add fixture_off images --- resources/icons/png/fixture_off.png | Bin 1418 -> 3795 bytes resources/icons/svg/fixture_off.svg | 177 +++++++--------------------- 2 files changed, 44 insertions(+), 133 deletions(-) diff --git a/resources/icons/png/fixture_off.png b/resources/icons/png/fixture_off.png index 86eb77b1a8fa14ce9550c14aea1fed369323cc6d..4ce1d8ddb4d576f8ae5876c925b8589579b238f1 100644 GIT binary patch literal 3795 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 delta 1380 zcmV-q1)KWQ9f}K(Bpd;AQb$4nuFf3k00006VoOIv0RI600RN!9r<0K(7k>Z;bV*G` z2iOGx2ro2$UO|@t00jz3L_t(o!_AjlY!p=($A4!oyR)6`W!i4n-h^(K?8dqkDVCN} z9uO#@DyE?HO`9Nz661p*rXe&K#YiwlLbV}CVu;}dc_EUN21QUY5HMhBpwtCpY0T2d8ygOD|H?FF850dw=+Nf++!XSQ$O9z1a1K-@IP=0^d)0(=at_}i|!x;haGg@0ZK_5x-g5I8Q&a(9&C zsl%Kyx`3;|r@(VSTw!5hqQl`h81*5hJ%6uhTKpsc>FMb{;5XnP(BXEw>r+!xFU|lU zGUd={hDL2?zGE|Gs)}eTT9I^1*Oj|~sjBMk!-o(5G`%)%H7G zXHTwnX`1|S^y2E3@fOfynx=Dt0o!IG%Bj?rmX@^9(SOlFMNuw9Q95TSS>wIz_qnsD z{-2WUj&tjZY5SZd0ce^wj(iY^0mftd`O?zTuK6klhTh+sn{D?Hlf>bW9!fMEI^diD zP+3`dNl}zWz-`Cuh^eR=xg^`2Jl*2pklE1GJkV}u(mzd5RaJF6 z7!2;R8GndPN=iBfylWW7g|@b~J5jcsi$vBGr;MnIG;O*5y<;T7vWSyq`OE|WA%uD8 z(4kX)zyB>D-!x4h@VaGLG1snLdvU%DlsxJ6+-vd28$F|dZkpzeXeXqosA#CRw)TUZ zoSc_>dwZRL*45RO2N?5E4uCgByJz?w`>d(Uu759slK=qt{r=(l`ue(xii-8Asi`$U zpB)uVcJ9A52UA#jjM6u){Rhpi&rjb?R#o*xRaI5rjHmSW_QtqeuEln5=(NG=z|+7- zKwDHAo|KkP9$R_E>ey^e9o74negU*@*|KH%EM9Sr?azDevklUyQdfddYAaz zza$!tU+oh8T}eqv!UMnS8HS-bozCM?bv}eKy5;+o=H_Pp_wolohw{(H22HmB0000 - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + From 4fcdffe54fd827235fbe91a5cccbdfbacffaeb80 Mon Sep 17 00:00:00 2001 From: Nils Tijtgat Date: Fri, 19 Jan 2024 00:08:27 +0100 Subject: [PATCH 202/212] More fixes --- fixtureeditor/CMakeLists.txt | 1 + ui/src/CMakeLists.txt | 3 +++ ui/src/fixturemanager.cpp | 46 +++++++++++++++++++++++++----------- ui/src/qlcui.qrc | 1 + 4 files changed, 37 insertions(+), 14 deletions(-) diff --git a/fixtureeditor/CMakeLists.txt b/fixtureeditor/CMakeLists.txt index 94261267a3..1261c6ff27 100644 --- a/fixtureeditor/CMakeLists.txt +++ b/fixtureeditor/CMakeLists.txt @@ -520,6 +520,7 @@ set(qlcui_resource_files "../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/fixture_off.png" "../ui/src/../../resources/icons/png/flash.png" "../ui/src/../../resources/icons/png/flower.png" "../ui/src/../../resources/icons/png/folder.png" diff --git a/ui/src/CMakeLists.txt b/ui/src/CMakeLists.txt index acb69d739b..de6aff9d21 100644 --- a/ui/src/CMakeLists.txt +++ b/ui/src/CMakeLists.txt @@ -353,6 +353,9 @@ set_source_files_properties("../../resources/icons/png/filesaveas.png" set_source_files_properties("../../resources/icons/png/fixture.png" PROPERTIES QT_RESOURCE_ALIAS "fixture.png" ) +set_source_files_properties("../../resources/icons/png/fixture_off.png" + PROPERTIES QT_RESOURCE_ALIAS "fixture_off.png" +) set_source_files_properties("../../resources/icons/png/flash.png" PROPERTIES QT_RESOURCE_ALIAS "flash.png" ) diff --git a/ui/src/fixturemanager.cpp b/ui/src/fixturemanager.cpp index 5f517f2b7a..87a4ea6fa2 100644 --- a/ui/src/fixturemanager.cpp +++ b/ui/src/fixturemanager.cpp @@ -77,7 +77,7 @@ FixtureManager* FixtureManager::s_instance = NULL; FixtureManager::FixtureManager(QWidget* parent, Doc* doc) : QWidget(parent) , m_doc(doc) - , m_testFixturesEnabled(true) + , m_testFixturesEnabled(false) , m_splitter(NULL) , m_fixtures_tree(NULL) , m_channel_groups_tree(NULL) @@ -220,7 +220,6 @@ void FixtureManager::slotChannelsGroupRemoved(quint32 id) void FixtureManager::slotModeChanged(Doc::Mode mode) { - // TO-DO: hier moeje die check shit toevoegen ojo! if (mode == Doc::Design) { int selected = m_fixtures_tree->selectedItems().size(); @@ -524,18 +523,21 @@ void FixtureManager::runTestFixtures() { // Turn on all selected fixtures QSet::const_iterator it; - for (it = newlySelectedFixtureIds.constBegin(); it != newlySelectedFixtureIds.constEnd(); ++it) { + for (it = newlySelectedFixtureIds.constBegin(); it != newlySelectedFixtureIds.constEnd(); ++it) + { turnFixtureOn(*it); } // Turn all deselected fixtures off - for (it = deselectedFixtureIds.constBegin(); it != deselectedFixtureIds.constEnd(); ++it) { + for (it = deselectedFixtureIds.constBegin(); it != deselectedFixtureIds.constEnd(); ++it) + { turnFixtureOff(*it); } } else { // Turn all fixtures off QHash::iterator it; - foreach(const quint32 &fixtureId, m_fixtureToSourceMap.keys()) { + foreach(const quint32 &fixtureId, m_fixtureToSourceMap.keys()) + { turnFixtureOff(fixtureId); } } @@ -552,15 +554,29 @@ void FixtureManager::turnFixtureOn(quint32 id) GenericDMXSource* source = new GenericDMXSource(m_doc); m_fixtureToSourceMap.insert(id, source); - // TO-DO: make fixture white! // TO-DO: check if this works for complex lights (Mac Quantum for instance) - // Set the Intensity for every Head to 255 + // Set the color of every Fixture to white and intensity to 255 for (int i = 0; i < fxi->heads(); i++) { - QLCFixtureHead head= fxi->head(i); - quint32 jo = head.channelNumber(QLCChannel::Intensity, QLCChannel::MSB); - source->set(fxi->id(), jo, 255); + QLCFixtureHead head = fxi->head(i); + + quint32 whiteChannel = head.channelNumber(QLCChannel::White, QLCChannel::MSB); + quint32 redChannel = head.channelNumber(QLCChannel::Red, QLCChannel::MSB); + quint32 greenChannel = head.channelNumber(QLCChannel::Green, QLCChannel::MSB); + quint32 blueChannel = head.channelNumber(QLCChannel::Blue, QLCChannel::MSB); + + if(whiteChannel != QLCChannel::invalid()) + { + source->set(fxi->id(), whiteChannel, 255); + } else if(redChannel != QLCChannel::invalid() && greenChannel != QLCChannel::invalid() && blueChannel != QLCChannel::invalid()) { + source->set(fxi->id(), redChannel, 255); + source->set(fxi->id(), greenChannel, 255); + source->set(fxi->id(), blueChannel, 255); + } + + quint32 intensityChannel = head.channelNumber(QLCChannel::Intensity, QLCChannel::MSB); + source->set(fxi->id(), intensityChannel, 255); } source->setOutputEnabled(true); @@ -577,8 +593,6 @@ void FixtureManager::turnFixtureOff(quint32 id) delete m_fixtureToSourceMap.value(id); m_fixtureToSourceMap.remove(id); - - qDebug() << "Untest fixture" << id; } void FixtureManager::fixtureSelected(quint32 id) @@ -1610,10 +1624,14 @@ void FixtureManager::slotTestFixtures() // Update the icon if(m_testFixturesEnabled) { - m_testFixturesAction->setIcon(QIcon(":/fixture_off.png")); + m_testFixturesAction->setIcon(QIcon(":/fixture.png")); + m_testFixturesAction->setIconText(tr("Enable fixture tester")); + m_testFixturesAction->setToolTip(tr("Enable fixture tester")); } else { - m_testFixturesAction->setIcon(QIcon(":/fixture.png")); + m_testFixturesAction->setIcon(QIcon(":/fixture_off.png")); + m_testFixturesAction->setIconText(tr("Disable fixture tester")); + m_testFixturesAction->setToolTip(tr("Disable fixture tester")); } m_testFixturesEnabled = !m_testFixturesEnabled; diff --git a/ui/src/qlcui.qrc b/ui/src/qlcui.qrc index d6195acb38..d818447939 100644 --- a/ui/src/qlcui.qrc +++ b/ui/src/qlcui.qrc @@ -57,6 +57,7 @@ ../../resources/icons/png/filesave.png ../../resources/icons/png/filesaveas.png ../../resources/icons/png/fixture.png + ../../resources/icons/png/fixture_off.png ../../resources/icons/png/folder.png ../../resources/icons/png/qlcplus-fixtureeditor.png ../../resources/icons/png/flash.png From 6c7fa233915ad505904595ac65cb3e6b6911b08b Mon Sep 17 00:00:00 2001 From: Nils Tijtgat Date: Fri, 19 Jan 2024 00:20:23 +0100 Subject: [PATCH 203/212] Small fix --- ui/src/fixturemanager.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/ui/src/fixturemanager.cpp b/ui/src/fixturemanager.cpp index 87a4ea6fa2..297319246e 100644 --- a/ui/src/fixturemanager.cpp +++ b/ui/src/fixturemanager.cpp @@ -533,7 +533,9 @@ void FixtureManager::runTestFixtures() { turnFixtureOff(*it); } - } else { + } + else + { // Turn all fixtures off QHash::iterator it; foreach(const quint32 &fixtureId, m_fixtureToSourceMap.keys()) @@ -569,7 +571,9 @@ void FixtureManager::turnFixtureOn(quint32 id) if(whiteChannel != QLCChannel::invalid()) { source->set(fxi->id(), whiteChannel, 255); - } else if(redChannel != QLCChannel::invalid() && greenChannel != QLCChannel::invalid() && blueChannel != QLCChannel::invalid()) { + } + else if(redChannel != QLCChannel::invalid() && greenChannel != QLCChannel::invalid() && blueChannel != QLCChannel::invalid()) + { source->set(fxi->id(), redChannel, 255); source->set(fxi->id(), greenChannel, 255); source->set(fxi->id(), blueChannel, 255); @@ -1621,7 +1625,6 @@ void FixtureManager::slotRemap() void FixtureManager::slotTestFixtures() { - // Update the icon if(m_testFixturesEnabled) { m_testFixturesAction->setIcon(QIcon(":/fixture.png")); From 8aeb256b59093499ca014a41eac35fc9afb58a32 Mon Sep 17 00:00:00 2001 From: Nils Tijtgat Date: Fri, 19 Jan 2024 08:43:30 +0100 Subject: [PATCH 204/212] Only enable Highlight when FixtureManager tab is active --- ui/src/fixturemanager.cpp | 15 ++++++++++++--- ui/src/fixturemanager.h | 8 ++++++-- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/ui/src/fixturemanager.cpp b/ui/src/fixturemanager.cpp index 297319246e..f7d0f3fb57 100644 --- a/ui/src/fixturemanager.cpp +++ b/ui/src/fixturemanager.cpp @@ -74,8 +74,9 @@ FixtureManager* FixtureManager::s_instance = NULL; * Initialization *****************************************************************************/ -FixtureManager::FixtureManager(QWidget* parent, Doc* doc) +FixtureManager::FixtureManager(QTabWidget* parent, Doc* doc) : QWidget(parent) + , m_parent(parent) , m_doc(doc) , m_testFixturesEnabled(false) , m_splitter(NULL) @@ -119,6 +120,9 @@ FixtureManager::FixtureManager(QWidget* parent, Doc* doc) if (grpItem != NULL) grpItem->setExpanded(true); + connect(m_parent, SIGNAL(currentChanged(int)), + this, SLOT(slotParentTabChanged())); + /* Connect fixture list change signals from the new document object */ connect(m_doc, SIGNAL(fixtureRemoved(quint32)), this, SLOT(slotFixtureRemoved(quint32))); @@ -168,9 +172,14 @@ FixtureManager* FixtureManager::instance() } /***************************************************************************** - * Doc signal handlers + * Signal handlers *****************************************************************************/ +void FixtureManager::slotParentTabChanged() +{ + runTestFixtures(); +} + void FixtureManager::slotFixtureRemoved(quint32 id) { QList groupsToDelete; @@ -519,7 +528,7 @@ void FixtureManager::runTestFixtures() QSet newlySelectedFixtureIds = selectedFixtureIds.subtract(m_lastSelectedFixtureIds); QSet deselectedFixtureIds = m_lastSelectedFixtureIds.subtract(selectedFixtureIds); - if(m_doc->mode() == Doc::Design && m_testFixturesEnabled) + if(m_doc->mode() == Doc::Design && m_testFixturesEnabled && m_parent->currentIndex() == 0) { // Turn on all selected fixtures QSet::const_iterator it; diff --git a/ui/src/fixturemanager.h b/ui/src/fixturemanager.h index 1881540b15..59eedc8e49 100644 --- a/ui/src/fixturemanager.h +++ b/ui/src/fixturemanager.h @@ -55,7 +55,7 @@ class FixtureManager : public QWidget * Initialization ********************************************************************/ public: - FixtureManager(QWidget* parent, Doc* doc); + FixtureManager(QTabWidget* parent, Doc* doc); ~FixtureManager(); /** Get the singleton instance */ @@ -66,9 +66,12 @@ class FixtureManager : public QWidget static FixtureManager* s_instance; /******************************************************************** - * Doc signal handlers + * Signal handlers ********************************************************************/ public slots: + /** Callback for QTabWidget::currentChanged() signals */ + void slotParentTabChanged(); + /** Callback for Doc::fixtureRemoved() signals */ void slotFixtureRemoved(quint32 id); @@ -88,6 +91,7 @@ public slots: void slotDocLoaded(); private: + QTabWidget* m_parent; Doc* m_doc; bool m_testFixturesEnabled; QSet m_lastSelectedFixtureIds; From 9a19e865a0d1f3ad3e109120160f6a8dfbe0292f Mon Sep 17 00:00:00 2001 From: Nils Tijtgat Date: Fri, 19 Jan 2024 08:46:01 +0100 Subject: [PATCH 205/212] Rename feature to Highlight Fixtures --- ui/src/fixturemanager.cpp | 42 +++++++++++++++++++-------------------- ui/src/fixturemanager.h | 8 ++++---- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/ui/src/fixturemanager.cpp b/ui/src/fixturemanager.cpp index f7d0f3fb57..07698ed341 100644 --- a/ui/src/fixturemanager.cpp +++ b/ui/src/fixturemanager.cpp @@ -78,7 +78,7 @@ FixtureManager::FixtureManager(QTabWidget* parent, Doc* doc) : QWidget(parent) , m_parent(parent) , m_doc(doc) - , m_testFixturesEnabled(false) + , m_highlightFixturesEnabled(false) , m_splitter(NULL) , m_fixtures_tree(NULL) , m_channel_groups_tree(NULL) @@ -177,7 +177,7 @@ FixtureManager* FixtureManager::instance() void FixtureManager::slotParentTabChanged() { - runTestFixtures(); + runHighlightFixtures(); } void FixtureManager::slotFixtureRemoved(quint32 id) @@ -287,7 +287,7 @@ void FixtureManager::slotModeChanged(Doc::Mode mode) else m_fadeConfigAction->setEnabled(false); - m_testFixturesAction->setEnabled(true); + m_highlightFixturesAction->setEnabled(true); } else { @@ -298,10 +298,10 @@ void FixtureManager::slotModeChanged(Doc::Mode mode) m_fadeConfigAction->setEnabled(false); m_groupAction->setEnabled(false); m_unGroupAction->setEnabled(false); - m_testFixturesAction->setEnabled(false); + m_highlightFixturesAction->setEnabled(false); } - runTestFixtures(); + runHighlightFixtures(); } void FixtureManager::slotFixtureGroupRemoved(quint32 id) @@ -516,7 +516,7 @@ void FixtureManager::updateRDMView() m_remapAction->setEnabled(false); } -void FixtureManager::runTestFixtures() +void FixtureManager::runHighlightFixtures() { QList selectedFixtures = m_fixtures_tree->selectedItems(); QSet selectedFixtureIds; @@ -528,7 +528,7 @@ void FixtureManager::runTestFixtures() QSet newlySelectedFixtureIds = selectedFixtureIds.subtract(m_lastSelectedFixtureIds); QSet deselectedFixtureIds = m_lastSelectedFixtureIds.subtract(selectedFixtureIds); - if(m_doc->mode() == Doc::Design && m_testFixturesEnabled && m_parent->currentIndex() == 0) + if(m_doc->mode() == Doc::Design && m_highlightFixturesEnabled && m_parent->currentIndex() == 0) { // Turn on all selected fixtures QSet::const_iterator it; @@ -1048,10 +1048,10 @@ void FixtureManager::initActions() connect(m_remapAction, SIGNAL(triggered(bool)), this, SLOT(slotRemap())); - m_testFixturesAction = new QAction(QIcon(":/fixture.png"), - tr("Enable fixture tester..."), this); - connect(m_testFixturesAction, SIGNAL(triggered(bool)), - this, SLOT(slotTestFixtures())); + m_highlightFixturesAction = new QAction(QIcon(":/fixture.png"), + tr("Enable highlight fixtures..."), this); + connect(m_highlightFixturesAction, SIGNAL(triggered(bool)), + this, SLOT(slotHighlightFixtures())); } void FixtureManager::updateGroupMenu() @@ -1101,7 +1101,7 @@ void FixtureManager::initToolBar() toolbar->addAction(m_importAction); toolbar->addAction(m_exportAction); toolbar->addAction(m_remapAction); - toolbar->addAction(m_testFixturesAction); + toolbar->addAction(m_highlightFixturesAction); QToolButton* btn = qobject_cast (toolbar->widgetForAction(m_groupAction)); Q_ASSERT(btn != NULL); @@ -1632,21 +1632,21 @@ void FixtureManager::slotRemap() updateView(); } -void FixtureManager::slotTestFixtures() +void FixtureManager::slotHighlightFixtures() { - if(m_testFixturesEnabled) + if(m_highlightFixturesEnabled) { - m_testFixturesAction->setIcon(QIcon(":/fixture.png")); - m_testFixturesAction->setIconText(tr("Enable fixture tester")); - m_testFixturesAction->setToolTip(tr("Enable fixture tester")); + m_highlightFixturesAction->setIcon(QIcon(":/fixture.png")); + m_highlightFixturesAction->setIconText(tr("Enable highlight fixtures")); + m_highlightFixturesAction->setToolTip(tr("Enable highlight fixtures")); } else { - m_testFixturesAction->setIcon(QIcon(":/fixture_off.png")); - m_testFixturesAction->setIconText(tr("Disable fixture tester")); - m_testFixturesAction->setToolTip(tr("Disable fixture tester")); + m_highlightFixturesAction->setIcon(QIcon(":/fixture_off.png")); + m_highlightFixturesAction->setIconText(tr("Disable highlight fixtures")); + m_highlightFixturesAction->setToolTip(tr("Disable highlight fixtures")); } - m_testFixturesEnabled = !m_testFixturesEnabled; + m_highlightFixturesEnabled = !m_highlightFixturesEnabled; slotModeChanged(m_doc->mode()); } diff --git a/ui/src/fixturemanager.h b/ui/src/fixturemanager.h index 59eedc8e49..2e1a58b348 100644 --- a/ui/src/fixturemanager.h +++ b/ui/src/fixturemanager.h @@ -93,7 +93,7 @@ public slots: private: QTabWidget* m_parent; Doc* m_doc; - bool m_testFixturesEnabled; + bool m_highlightFixturesEnabled; QSet m_lastSelectedFixtureIds; QHash m_fixtureToSourceMap; @@ -127,7 +127,7 @@ public slots: void initDataView(); /** Checks if any fixtures have to be turned on/off if Test Fixtures is enabled */ - void runTestFixtures(); + void runHighlightFixtures(); /** Creates a GenericDmxSource and turns the fixture on */ void turnFixtureOn(quint32 id); @@ -216,7 +216,7 @@ private slots: void slotProperties(); void slotFadeConfig(); void slotRemap(); - void slotTestFixtures(); + void slotHighlightFixtures(); void slotUnGroup(); void slotGroupSelected(QAction* action); void slotMoveGroupUp(); @@ -234,7 +234,7 @@ private slots: QAction* m_propertiesAction; QAction* m_fadeConfigAction; QAction* m_remapAction; - QAction* m_testFixturesAction; + QAction* m_highlightFixturesAction; QAction* m_groupAction; QAction* m_unGroupAction; QAction* m_newGroupAction; From 251554cd4614b034dd415cf9fd109243b55a4cd9 Mon Sep 17 00:00:00 2001 From: Nils Tijtgat Date: Sat, 20 Jan 2024 10:20:36 +0100 Subject: [PATCH 206/212] Set pan en tilt when highlighting fixtures --- ui/src/fixturemanager.cpp | 22 +++++++++++++++++----- ui/src/fixturemanager.h | 4 ++-- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/ui/src/fixturemanager.cpp b/ui/src/fixturemanager.cpp index 07698ed341..315f55fd1b 100644 --- a/ui/src/fixturemanager.cpp +++ b/ui/src/fixturemanager.cpp @@ -534,13 +534,13 @@ void FixtureManager::runHighlightFixtures() QSet::const_iterator it; for (it = newlySelectedFixtureIds.constBegin(); it != newlySelectedFixtureIds.constEnd(); ++it) { - turnFixtureOn(*it); + highlightFixture(*it); } // Turn all deselected fixtures off for (it = deselectedFixtureIds.constBegin(); it != deselectedFixtureIds.constEnd(); ++it) { - turnFixtureOff(*it); + unHighlightFixture(*it); } } else @@ -549,14 +549,14 @@ void FixtureManager::runHighlightFixtures() QHash::iterator it; foreach(const quint32 &fixtureId, m_fixtureToSourceMap.keys()) { - turnFixtureOff(fixtureId); + unHighlightFixture(fixtureId); } } m_lastSelectedFixtureIds = selectedFixtureIds; } -void FixtureManager::turnFixtureOn(quint32 id) +void FixtureManager::highlightFixture(quint32 id) { Fixture* fxi = m_doc->fixture(id); if (fxi == NULL) @@ -588,6 +588,18 @@ void FixtureManager::turnFixtureOn(quint32 id) source->set(fxi->id(), blueChannel, 255); } + quint32 panChannel = head.channelNumber(QLCChannel::Pan, QLCChannel::MSB); + if(panChannel != QLCChannel::invalid()) + { + source->set(fxi->id(), panChannel, 127); + } + + quint32 tiltChannel = head.channelNumber(QLCChannel::Tilt, QLCChannel::MSB); + if(tiltChannel != QLCChannel::invalid()) + { + source->set(fxi->id(), tiltChannel, 127); + } + quint32 intensityChannel = head.channelNumber(QLCChannel::Intensity, QLCChannel::MSB); source->set(fxi->id(), intensityChannel, 255); } @@ -595,7 +607,7 @@ void FixtureManager::turnFixtureOn(quint32 id) source->setOutputEnabled(true); } -void FixtureManager::turnFixtureOff(quint32 id) +void FixtureManager::unHighlightFixture(quint32 id) { Fixture* fxi = m_doc->fixture(id); if (fxi == NULL) diff --git a/ui/src/fixturemanager.h b/ui/src/fixturemanager.h index 2e1a58b348..623c04e495 100644 --- a/ui/src/fixturemanager.h +++ b/ui/src/fixturemanager.h @@ -130,10 +130,10 @@ public slots: void runHighlightFixtures(); /** Creates a GenericDmxSource and turns the fixture on */ - void turnFixtureOn(quint32 id); + void highlightFixture(quint32 id); /** Turns the fixture off and removes the related GenericDmxSource */ - void turnFixtureOff(quint32 id); + void unHighlightFixture(quint32 id); /** Handle single fixture selection */ void fixtureSelected(quint32 id); From 76f340d56f0de7c0c3150167c1662301258d0a9c Mon Sep 17 00:00:00 2001 From: Nils Tijtgat Date: Sat, 20 Jan 2024 11:21:50 +0100 Subject: [PATCH 207/212] Codestyle fixes and initial shutter open implementation --- ui/src/fixturemanager.cpp | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/ui/src/fixturemanager.cpp b/ui/src/fixturemanager.cpp index 315f55fd1b..02f4dab1cd 100644 --- a/ui/src/fixturemanager.cpp +++ b/ui/src/fixturemanager.cpp @@ -41,6 +41,7 @@ #include "qlcfixturemode.h" #include "qlcfixturedef.h" #include "qlcchannel.h" +#include "qlccapability.h" #include "qlcfile.h" #include "createfixturegroup.h" @@ -528,7 +529,7 @@ void FixtureManager::runHighlightFixtures() QSet newlySelectedFixtureIds = selectedFixtureIds.subtract(m_lastSelectedFixtureIds); QSet deselectedFixtureIds = m_lastSelectedFixtureIds.subtract(selectedFixtureIds); - if(m_doc->mode() == Doc::Design && m_highlightFixturesEnabled && m_parent->currentIndex() == 0) + if (m_doc->mode() == Doc::Design && m_highlightFixturesEnabled && m_parent->currentIndex() == 0) { // Turn on all selected fixtures QSet::const_iterator it; @@ -565,9 +566,24 @@ void FixtureManager::highlightFixture(quint32 id) GenericDMXSource* source = new GenericDMXSource(m_doc); m_fixtureToSourceMap.insert(id, source); - // TO-DO: check if this works for complex lights (Mac Quantum for instance) + for (quint32 i = 0; i < fxi->channels(); i++) + { + const QLCChannel *channel = fxi->channel(i); + for (QLCCapability *cap : channel->capabilities()) + { + if (cap->preset() == QLCCapability::ShutterOpen || + cap->preset() == QLCCapability::LampOn) + { + // TO-DO: I'm not sure if this is correct if a fixture has multiple heads, will this open the shutter for all heads? + source->set(fxi->id(), i, cap->middle()); + + break; + } - // Set the color of every Fixture to white and intensity to 255 + } + } + + // Set the color of every Fixture to white, pan en tilt to 127, shutter to open and intensity to 255 for (int i = 0; i < fxi->heads(); i++) { QLCFixtureHead head = fxi->head(i); @@ -577,11 +593,11 @@ void FixtureManager::highlightFixture(quint32 id) quint32 greenChannel = head.channelNumber(QLCChannel::Green, QLCChannel::MSB); quint32 blueChannel = head.channelNumber(QLCChannel::Blue, QLCChannel::MSB); - if(whiteChannel != QLCChannel::invalid()) + if (whiteChannel != QLCChannel::invalid()) { source->set(fxi->id(), whiteChannel, 255); } - else if(redChannel != QLCChannel::invalid() && greenChannel != QLCChannel::invalid() && blueChannel != QLCChannel::invalid()) + else if (redChannel != QLCChannel::invalid() && greenChannel != QLCChannel::invalid() && blueChannel != QLCChannel::invalid()) { source->set(fxi->id(), redChannel, 255); source->set(fxi->id(), greenChannel, 255); @@ -589,13 +605,13 @@ void FixtureManager::highlightFixture(quint32 id) } quint32 panChannel = head.channelNumber(QLCChannel::Pan, QLCChannel::MSB); - if(panChannel != QLCChannel::invalid()) + if (panChannel != QLCChannel::invalid()) { source->set(fxi->id(), panChannel, 127); } quint32 tiltChannel = head.channelNumber(QLCChannel::Tilt, QLCChannel::MSB); - if(tiltChannel != QLCChannel::invalid()) + if (tiltChannel != QLCChannel::invalid()) { source->set(fxi->id(), tiltChannel, 127); } @@ -613,7 +629,7 @@ void FixtureManager::unHighlightFixture(quint32 id) if (fxi == NULL) return; - if(!m_fixtureToSourceMap.contains(id)) + if (!m_fixtureToSourceMap.contains(id)) return; delete m_fixtureToSourceMap.value(id); @@ -1646,12 +1662,13 @@ void FixtureManager::slotRemap() void FixtureManager::slotHighlightFixtures() { - if(m_highlightFixturesEnabled) + if (m_highlightFixturesEnabled) { m_highlightFixturesAction->setIcon(QIcon(":/fixture.png")); m_highlightFixturesAction->setIconText(tr("Enable highlight fixtures")); m_highlightFixturesAction->setToolTip(tr("Enable highlight fixtures")); - } else + } + else { m_highlightFixturesAction->setIcon(QIcon(":/fixture_off.png")); m_highlightFixturesAction->setIconText(tr("Disable highlight fixtures")); From 1e12842f0e1637c588040da6424faeeb0b75c6fb Mon Sep 17 00:00:00 2001 From: Nils Tijtgat Date: Sat, 20 Jan 2024 17:37:27 +0100 Subject: [PATCH 208/212] Fix shutter open for multi-head fixtures --- ui/src/fixturemanager.cpp | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/ui/src/fixturemanager.cpp b/ui/src/fixturemanager.cpp index 02f4dab1cd..6c6239628a 100644 --- a/ui/src/fixturemanager.cpp +++ b/ui/src/fixturemanager.cpp @@ -566,23 +566,6 @@ void FixtureManager::highlightFixture(quint32 id) GenericDMXSource* source = new GenericDMXSource(m_doc); m_fixtureToSourceMap.insert(id, source); - for (quint32 i = 0; i < fxi->channels(); i++) - { - const QLCChannel *channel = fxi->channel(i); - for (QLCCapability *cap : channel->capabilities()) - { - if (cap->preset() == QLCCapability::ShutterOpen || - cap->preset() == QLCCapability::LampOn) - { - // TO-DO: I'm not sure if this is correct if a fixture has multiple heads, will this open the shutter for all heads? - source->set(fxi->id(), i, cap->middle()); - - break; - } - - } - } - // Set the color of every Fixture to white, pan en tilt to 127, shutter to open and intensity to 255 for (int i = 0; i < fxi->heads(); i++) { @@ -616,6 +599,26 @@ void FixtureManager::highlightFixture(quint32 id) source->set(fxi->id(), tiltChannel, 127); } + QLCFixtureMode* mode = fxi->fixtureMode(); + foreach (quint32 shutter, head.shutterChannels()) + { + QLCChannel *ch = mode->channel(shutter); + if (ch == NULL) + continue; + + foreach (QLCCapability *cap, ch->capabilities()) + { + if (cap->preset() == QLCCapability::ShutterOpen || + cap->preset() == QLCCapability::LampOn) + { + source->set(fxi->id(), i, cap->middle()); + goto setIntensity; + } + + } + } + + setIntensity: quint32 intensityChannel = head.channelNumber(QLCChannel::Intensity, QLCChannel::MSB); source->set(fxi->id(), intensityChannel, 255); } From 9d4fe620390fc0ae95e4f9bc6c3be5d472f39ce1 Mon Sep 17 00:00:00 2001 From: Nils Tijtgat Date: Sun, 21 Jan 2024 18:17:53 +0100 Subject: [PATCH 209/212] Update documentation --- resources/docs/html_en_EN/fixturemanager.html | 7 +++++++ resources/icons/svg/lightning.svg | 3 +++ 2 files changed, 10 insertions(+) create mode 100644 resources/icons/svg/lightning.svg diff --git a/resources/docs/html_en_EN/fixturemanager.html b/resources/docs/html_en_EN/fixturemanager.html index 70a3c1d41c..83921e0a0a 100644 --- a/resources/docs/html_en_EN/fixturemanager.html +++ b/resources/docs/html_en_EN/fixturemanager.html @@ -100,6 +100,13 @@

    Controls

    Opens the Fixtures remapping window. + + + + Enables or disables the Highlight Fixtures features. When enabled, this will set the selected features to max brightness + so you can easily verify these fixture are patched correctly. + + diff --git a/resources/icons/svg/lightning.svg b/resources/icons/svg/lightning.svg new file mode 100644 index 0000000000..ab0a5b219f --- /dev/null +++ b/resources/icons/svg/lightning.svg @@ -0,0 +1,3 @@ + + + From efc5ead9484617561c0c4145e3bc0b48eb809bff Mon Sep 17 00:00:00 2001 From: Nils Tijtgat Date: Sun, 21 Jan 2024 18:28:23 +0100 Subject: [PATCH 210/212] Add configuration to load lightning icon --- fixtureeditor/CMakeLists.txt | 3 ++- resources/icons/png/fixture_off.png | Bin 3795 -> 0 bytes ui/src/CMakeLists.txt | 11 ++++++++--- ui/src/fixturemanager.cpp | 6 +++--- ui/src/qlcui.qrc | 3 ++- 5 files changed, 15 insertions(+), 8 deletions(-) delete mode 100644 resources/icons/png/fixture_off.png diff --git a/fixtureeditor/CMakeLists.txt b/fixtureeditor/CMakeLists.txt index 1261c6ff27..489ee519a9 100644 --- a/fixtureeditor/CMakeLists.txt +++ b/fixtureeditor/CMakeLists.txt @@ -520,7 +520,6 @@ set(qlcui_resource_files "../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/fixture_off.png" "../ui/src/../../resources/icons/png/flash.png" "../ui/src/../../resources/icons/png/flower.png" "../ui/src/../../resources/icons/png/folder.png" @@ -548,6 +547,8 @@ set(qlcui_resource_files "../ui/src/../../resources/icons/png/laser.png" "../ui/src/../../resources/icons/png/ledbar_beams.png" "../ui/src/../../resources/icons/png/ledbar_pixels.png" + "../ui/src/../../resources/icons/png/lightning.png" + "../ui/src/../../resources/icons/png/lightning_off.png" "../ui/src/../../resources/icons/png/liveedit.png" "../ui/src/../../resources/icons/png/liveedit_vc.png" "../ui/src/../../resources/icons/png/lock.png" diff --git a/resources/icons/png/fixture_off.png b/resources/icons/png/fixture_off.png deleted file mode 100644 index 4ce1d8ddb4d576f8ae5876c925b8589579b238f1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3795 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 diff --git a/ui/src/CMakeLists.txt b/ui/src/CMakeLists.txt index de6aff9d21..d7781d0af3 100644 --- a/ui/src/CMakeLists.txt +++ b/ui/src/CMakeLists.txt @@ -353,9 +353,6 @@ set_source_files_properties("../../resources/icons/png/filesaveas.png" set_source_files_properties("../../resources/icons/png/fixture.png" PROPERTIES QT_RESOURCE_ALIAS "fixture.png" ) -set_source_files_properties("../../resources/icons/png/fixture_off.png" - PROPERTIES QT_RESOURCE_ALIAS "fixture_off.png" -) set_source_files_properties("../../resources/icons/png/flash.png" PROPERTIES QT_RESOURCE_ALIAS "flash.png" ) @@ -437,6 +434,12 @@ set_source_files_properties("../../resources/icons/png/ledbar_beams.png" set_source_files_properties("../../resources/icons/png/ledbar_pixels.png" PROPERTIES QT_RESOURCE_ALIAS "ledbar_pixels.png" ) +set_source_files_properties("../../resources/icons/png/lightning.png" + PROPERTIES QT_RESOURCE_ALIAS "lightning.png" +) +set_source_files_properties("../../resources/icons/png/lightning_off.png" + PROPERTIES QT_RESOURCE_ALIAS "lightning_off.png" +) set_source_files_properties("../../resources/icons/png/liveedit.png" PROPERTIES QT_RESOURCE_ALIAS "liveedit.png" ) @@ -681,6 +684,8 @@ set(qlcui_resource_files "../../resources/icons/png/laser.png" "../../resources/icons/png/ledbar_beams.png" "../../resources/icons/png/ledbar_pixels.png" + "../../resources/icons/png/lightning.png" + "../../resources/icons/png/lightning_off.png" "../../resources/icons/png/liveedit.png" "../../resources/icons/png/liveedit_vc.png" "../../resources/icons/png/lock.png" diff --git a/ui/src/fixturemanager.cpp b/ui/src/fixturemanager.cpp index 6c6239628a..9ce6b82c8e 100644 --- a/ui/src/fixturemanager.cpp +++ b/ui/src/fixturemanager.cpp @@ -1079,7 +1079,7 @@ void FixtureManager::initActions() connect(m_remapAction, SIGNAL(triggered(bool)), this, SLOT(slotRemap())); - m_highlightFixturesAction = new QAction(QIcon(":/fixture.png"), + m_highlightFixturesAction = new QAction(QIcon(":/lightning.png"), tr("Enable highlight fixtures..."), this); connect(m_highlightFixturesAction, SIGNAL(triggered(bool)), this, SLOT(slotHighlightFixtures())); @@ -1667,13 +1667,13 @@ void FixtureManager::slotHighlightFixtures() { if (m_highlightFixturesEnabled) { - m_highlightFixturesAction->setIcon(QIcon(":/fixture.png")); + m_highlightFixturesAction->setIcon(QIcon(":/lightning.png")); m_highlightFixturesAction->setIconText(tr("Enable highlight fixtures")); m_highlightFixturesAction->setToolTip(tr("Enable highlight fixtures")); } else { - m_highlightFixturesAction->setIcon(QIcon(":/fixture_off.png")); + m_highlightFixturesAction->setIcon(QIcon(":/lightning_off.png")); m_highlightFixturesAction->setIconText(tr("Disable highlight fixtures")); m_highlightFixturesAction->setToolTip(tr("Disable highlight fixtures")); } diff --git a/ui/src/qlcui.qrc b/ui/src/qlcui.qrc index d818447939..ab3a2c33b0 100644 --- a/ui/src/qlcui.qrc +++ b/ui/src/qlcui.qrc @@ -57,7 +57,6 @@ ../../resources/icons/png/filesave.png ../../resources/icons/png/filesaveas.png ../../resources/icons/png/fixture.png - ../../resources/icons/png/fixture_off.png ../../resources/icons/png/folder.png ../../resources/icons/png/qlcplus-fixtureeditor.png ../../resources/icons/png/flash.png @@ -83,6 +82,8 @@ ../../resources/icons/png/laser.png ../../resources/icons/png/ledbar_beams.png ../../resources/icons/png/ledbar_pixels.png + ../../resources/icons/png/lightning.png + ../../resources/icons/png/lightning_off.png ../../resources/icons/png/liveedit.png ../../resources/icons/png/liveedit_vc.png ../../resources/icons/png/lock.png From 46ed480f2ef60c82a3ca9422d040391d58ec3b59 Mon Sep 17 00:00:00 2001 From: Nils Tijtgat Date: Sun, 21 Jan 2024 23:21:57 +0100 Subject: [PATCH 211/212] Add new lightning icon --- resources/icons/png/lightning.png | Bin 0 -> 381 bytes resources/icons/png/lightning_off.png | Bin 0 -> 1150 bytes resources/icons/svg/lightning_off.svg | 31 ++++++++++++++++++++++++++ 3 files changed, 31 insertions(+) create mode 100644 resources/icons/png/lightning.png create mode 100644 resources/icons/png/lightning_off.png create mode 100644 resources/icons/svg/lightning_off.svg diff --git a/resources/icons/png/lightning.png b/resources/icons/png/lightning.png new file mode 100644 index 0000000000000000000000000000000000000000..98654812257c1d1f9fc610f463322e0dcebe4187 GIT binary patch literal 381 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyegQrquI>dspPn*M3$2o3-wHHY zvLwham?2{?|ApWFvJYM@WfPA3qA}6Z@buZ%*KiBwCCtLgFx1E)*74?@*n)$e~;_riSzaz&d zSn4*MH!RMxo*mx%A>`(r$pw6@S0-1inZEg21i#h3uL~Y8Rqu4BqpPoX| zw{KtuPy{S^NtplhONQsu=6rqw62G{d1tk80;b}(3Np|k1oxO045XO04-WP3cFSE0O z%3e&0dsbR{&72!3byJZ4aYoj04vte?TyHYdj8`TK`<=S4Gvi(rMBL^V{*;#aZk6r({KVHw1mB;Q ze<-bR%ZmMmCeNF-CO7o7UbXta-KO{E7~@T8-upRRuNGTBN)LP#8}eeV>AUc#H*ukF z>q8&baoloYe>zF<{c+X1vD|l~x!;{vCXR zPA&=Z3uZ8V$a-NZpYZFSnYVnzq|Y(EIDb+_r#-Ra)5OE}=QN^AlES`kGB5gRB5EbQ z=U!y<9jUvDyYF8_n0yhH?}xQ=|0W4B)V z@LJNpZ_XLYA0Gb>auC*7l;!hDO!i@oepjKybsj#0vg;?7m|O_Da8jUlfy#P@3%ml# zo}cdS`*Q2@zv;Ji?!3Qhzp1Li=Hq9B!z=kO+GdLXlb)e!G%1*fECy^MtbasKxT)DGwH3xcIE-=2aDz@F4BPf?fOm?J1C3^M3M&*3_>W z8OI9y%D;%kA6az4Fn!Ti9hU8X1?TM#FPd!EZeXvS9oTUHkXHT+P1$2-(i49hefFNV zy-D5X{R=($=U-Ml5dX9@#_o{2)UKXOclJ*f|C8k^<1Z({dg^3B^uxK`pQq>A9k!XJ zsms++oczh>&etD{B)1D(wOT&!;f}M5l9t`;y6Z7px?xw8aozGxsae}Lv%Sw}{GHYy VdUsW$1TZZ!c)I$ztaD0e0sy!RQwsn9 literal 0 HcmV?d00001 diff --git a/resources/icons/svg/lightning_off.svg b/resources/icons/svg/lightning_off.svg new file mode 100644 index 0000000000..97138ea414 --- /dev/null +++ b/resources/icons/svg/lightning_off.svg @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + From e4a28a3290e30483c8f4a05f087cbe2c2c4f9c7c Mon Sep 17 00:00:00 2001 From: Nils Tijtgat Date: Sun, 21 Jan 2024 23:56:32 +0100 Subject: [PATCH 212/212] Final fixes --- resources/docs/html_en_EN/fixturemanager.html | 2 +- resources/icons/svg/fixture_off.svg | 44 ------------------- ui/src/fixturemanager.h | 2 +- 3 files changed, 2 insertions(+), 46 deletions(-) delete mode 100644 resources/icons/svg/fixture_off.svg diff --git a/resources/docs/html_en_EN/fixturemanager.html b/resources/docs/html_en_EN/fixturemanager.html index 83921e0a0a..e92cc2332d 100644 --- a/resources/docs/html_en_EN/fixturemanager.html +++ b/resources/docs/html_en_EN/fixturemanager.html @@ -101,7 +101,7 @@

    Controls

    - + Enables or disables the Highlight Fixtures features. When enabled, this will set the selected features to max brightness so you can easily verify these fixture are patched correctly. diff --git a/resources/icons/svg/fixture_off.svg b/resources/icons/svg/fixture_off.svg deleted file mode 100644 index f28c24d454..0000000000 --- a/resources/icons/svg/fixture_off.svg +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ui/src/fixturemanager.h b/ui/src/fixturemanager.h index 623c04e495..4533f0860b 100644 --- a/ui/src/fixturemanager.h +++ b/ui/src/fixturemanager.h @@ -126,7 +126,7 @@ public slots: /** Construct the list view and data view */ void initDataView(); - /** Checks if any fixtures have to be turned on/off if Test Fixtures is enabled */ + /** Checks if any fixtures have to be highlighted */ void runHighlightFixtures(); /** Creates a GenericDmxSource and turns the fixture on */