From a1a96d535f94338ebd89316fb7623e47c955cf5e Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Sun, 27 Oct 2019 10:31:11 +0100 Subject: [PATCH 01/42] Create HotcueColorPalette class --- build/depends.py | 1 + src/util/color/hotcuecolorpalette.cpp | 20 ++++++++++++++++++++ src/util/color/hotcuecolorpalette.h | 17 +++++++++++++++++ 3 files changed, 38 insertions(+) create mode 100644 src/util/color/hotcuecolorpalette.cpp create mode 100644 src/util/color/hotcuecolorpalette.h diff --git a/build/depends.py b/build/depends.py index 273deb01d25..6bb4e7a03b1 100644 --- a/build/depends.py +++ b/build/depends.py @@ -1225,6 +1225,7 @@ def sources(self, build): "src/util/movinginterquartilemean.cpp", "src/util/console.cpp", "src/util/color/color.cpp", + "src/util/color/hotcuecolorpalette.cpp", "src/util/db/dbconnection.cpp", "src/util/db/dbconnectionpool.cpp", "src/util/db/dbconnectionpooler.cpp", diff --git a/src/util/color/hotcuecolorpalette.cpp b/src/util/color/hotcuecolorpalette.cpp new file mode 100644 index 00000000000..d0b003ef2d5 --- /dev/null +++ b/src/util/color/hotcuecolorpalette.cpp @@ -0,0 +1,20 @@ +// +// Created by Ferran Pujol Camins on 27/10/2019. +// + +#include "hotcuecolorpalette.h" + +HotcueColorPalette::HotcueColorPalette(QList colorList) + : m_colorList(colorList) { +} + +const HotcueColorPalette HotcueColorPalette::mixxxPalette = + HotcueColorPalette(QList{ + QColor("#c50a08"), + QColor("#32be44"), + QColor("#0044ff"), + QColor("#f8d200"), + QColor("#42d4f4"), + QColor("#af00cc"), + QColor("#fca6d7"), + QColor("#f2f2ff")}); \ No newline at end of file diff --git a/src/util/color/hotcuecolorpalette.h b/src/util/color/hotcuecolorpalette.h new file mode 100644 index 00000000000..2f4a4f579f3 --- /dev/null +++ b/src/util/color/hotcuecolorpalette.h @@ -0,0 +1,17 @@ +// +// Created by Ferran Pujol Camins on 27/10/2019. +// + +#pragma once + +#include +#include "QList" + +class HotcueColorPalette { + public: + HotcueColorPalette(QList); + + static const HotcueColorPalette mixxxPalette; + + QList m_colorList; +}; \ No newline at end of file From 9af324ca5de7f056a31abfec60116a2307ea2309 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Sun, 27 Oct 2019 20:04:03 +0100 Subject: [PATCH 02/42] Add methods to store QColor in the config --- src/preferences/configobject.cpp | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/preferences/configobject.cpp b/src/preferences/configobject.cpp index 6eecb2bb991..1819186d9be 100644 --- a/src/preferences/configobject.cpp +++ b/src/preferences/configobject.cpp @@ -270,6 +270,13 @@ void ConfigObject::setValue( set(key, ConfigValue(QString::number(value))); } +template<> +template<> +void ConfigObject::setValue( + const ConfigKey& key, const QColor& value) { + set(key, ConfigValue(value.name(QColor::NameFormat::HexArgb))); +} + template <> template <> bool ConfigObject::getValue( const ConfigKey& key, const bool& default_value) const { @@ -306,6 +313,24 @@ double ConfigObject::getValue( return ok ? result : default_value; } +template<> +template<> +QColor ConfigObject::getValue( + const ConfigKey& key, const QColor& default_value) const { + const ConfigValue value = get(key); + if (value.isNull()) { + return default_value; + } + auto color = QColor(value.value); + return color.isValid() ? color : default_value; +} + +template<> +template<> +QColor ConfigObject::getValue(const ConfigKey& key) const { + return getValue(key, QColor()); +} + // For string literal default template <> QString ConfigObject::getValue( From 39d8624d50cb770b7d231f6786234086108d3900 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Sun, 27 Oct 2019 20:04:40 +0100 Subject: [PATCH 03/42] Add class to store HotcueColorPalette in userconfig --- build/depends.py | 1 + src/preferences/configobject.cpp | 12 +++++++ src/preferences/configobject.h | 2 ++ .../hotcuecolorpalettesettings.cpp | 36 +++++++++++++++++++ src/preferences/hotcuecolorpalettesettings.h | 26 ++++++++++++++ 5 files changed, 77 insertions(+) create mode 100644 src/preferences/hotcuecolorpalettesettings.cpp create mode 100644 src/preferences/hotcuecolorpalettesettings.h diff --git a/build/depends.py b/build/depends.py index 6bb4e7a03b1..9b171f55fb1 100644 --- a/build/depends.py +++ b/build/depends.py @@ -752,6 +752,7 @@ def sources(self, build): "src/preferences/effectsettingsmodel.cpp", "src/preferences/broadcastprofile.cpp", "src/preferences/upgrade.cpp", + "src/preferences/hotcuecolorpalettesettings.cpp", "src/preferences/dlgpreferencepage.cpp", "src/effects/effectmanifest.cpp", diff --git a/src/preferences/configobject.cpp b/src/preferences/configobject.cpp index 1819186d9be..22900ac4e48 100644 --- a/src/preferences/configobject.cpp +++ b/src/preferences/configobject.cpp @@ -215,6 +215,18 @@ template void ConfigObject::save() { } } +template +QList ConfigObject::getKeysWithGroup(QString group) { + QWriteLocker lock(&m_valuesLock); + QList filteredList; + for (const ConfigKey& key : m_values.uniqueKeys()) { + if (key.group == group) { + filteredList.append(key); + } + } + return filteredList; +} + template ConfigObject::ConfigObject(const QDomNode& node) { if (!node.isNull() && node.isElement()) { QDomNode ctrl = node.firstChild(); diff --git a/src/preferences/configobject.h b/src/preferences/configobject.h index 3e4ba6aa20e..3791de6ecd7 100644 --- a/src/preferences/configobject.h +++ b/src/preferences/configobject.h @@ -187,6 +187,8 @@ template class ConfigObject { return m_settingsPath; } + QList getKeysWithGroup(QString group); + protected: // We use QMap because we want a sorted list in mixxx.cfg QMap m_values; diff --git a/src/preferences/hotcuecolorpalettesettings.cpp b/src/preferences/hotcuecolorpalettesettings.cpp new file mode 100644 index 00000000000..b6a745606c0 --- /dev/null +++ b/src/preferences/hotcuecolorpalettesettings.cpp @@ -0,0 +1,36 @@ +#include "preferences/hotcuecolorpalettesettings.h" + +const QString HotcueColorPaletteSettings::sGroup = "[HotcueColorPalette]"; + +HotcueColorPalette HotcueColorPaletteSettings::getHotcueColorPalette() const { + QList colorList; + for (const ConfigKey& key : m_pConfig->getKeysWithGroup(sGroup)) { + QColor color = m_pConfig->getValue(key); + if (color.isValid()) { + colorList.append(color); + } + } + + // If no palette is defined in the settings, we use the default one. + if (colorList.isEmpty()) { + return HotcueColorPalette::mixxxPalette; + } + + return colorList; +} + +void HotcueColorPaletteSettings::setHotcueColorPalette( + const HotcueColorPalette& colorPalette) { + removePalette(); + + for (int index = 0; index < colorPalette.m_colorList.count(); ++index) { + QColor color = colorPalette.m_colorList[index]; + m_pConfig->setValue(keyForIndex(index), color); + } +} + +void HotcueColorPaletteSettings::removePalette() { + for (const ConfigKey& key : m_pConfig->getKeysWithGroup(sGroup)) { + m_pConfig->remove(key); + } +} \ No newline at end of file diff --git a/src/preferences/hotcuecolorpalettesettings.h b/src/preferences/hotcuecolorpalettesettings.h new file mode 100644 index 00000000000..ad28c5ec807 --- /dev/null +++ b/src/preferences/hotcuecolorpalettesettings.h @@ -0,0 +1,26 @@ +#pragma once + +#include "preferences/usersettings.h" +#include "util/color/hotcuecolorpalette.h" + +class HotcueColorPaletteSettings { + public: + HotcueColorPaletteSettings(UserSettingsPointer pConfig) + : m_pConfig(pConfig) { + } + + HotcueColorPalette getHotcueColorPalette() const; + + void setHotcueColorPalette(const HotcueColorPalette& colorPalette); + + private: + static const QString sGroup; + + void removePalette(); + + ConfigKey keyForIndex(int index) { + return ConfigKey(sGroup, QString::number(index)); + } + + UserSettingsPointer m_pConfig; +}; From a25b3a8ed1ac42bce9e048b190a6fdd992803b14 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Fri, 1 Nov 2019 12:04:54 +0100 Subject: [PATCH 04/42] Add tests for color config methods --- src/test/colorconfig_test.cpp | 79 +++++++++++++++++++++++++++++ src/util/color/hotcuecolorpalette.h | 7 ++- 2 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 src/test/colorconfig_test.cpp diff --git a/src/test/colorconfig_test.cpp b/src/test/colorconfig_test.cpp new file mode 100644 index 00000000000..fc95fec6573 --- /dev/null +++ b/src/test/colorconfig_test.cpp @@ -0,0 +1,79 @@ +#include +#include + +#include "test/mixxxtest.h" + +#include +#include + +class ColorConfigTest : public MixxxTest {}; + +TEST_F(ColorConfigTest, TestSavingColorWithoutAlpha) { + ConfigKey key("[Color]", "color"); + QColor originalColor("#FF9900"); + config()->setValue(key, originalColor); + saveAndReloadConfig(); + QColor colorFromConfig = config()->getValue(key); + ASSERT_EQ(originalColor, colorFromConfig); +} + +TEST_F(ColorConfigTest, TestSavingColorWithAlpha) { + ConfigKey key("[Color]", "color"); + QColor originalColor("#66FF9900"); + config()->setValue(key, originalColor); + saveAndReloadConfig(); + QColor colorFromConfig = config()->getValue(key); + ASSERT_EQ(originalColor, colorFromConfig); +} + +TEST_F(ColorConfigTest, TestDefaultColorWhenNoStoredColor) { + ConfigKey key("[Color]", "color"); + QColor defaultColor("#66FF9900"); + QColor colorFromConfig = config()->getValue(key, defaultColor); + ASSERT_EQ(defaultColor, colorFromConfig); +} + +TEST_F(ColorConfigTest, TestSaveColorPalette) { + HotcueColorPaletteSettings colorPaletteSettings(config()); + HotcueColorPalette originalColorPalette(QList{ + QColor("#66FF9900"), + QColor("#FF9900"), + QColor("#00000000"), + QColor("#FFFFFF"), + }); + ConfigKey key("[ColorPalette]", "colorPalette"); + colorPaletteSettings.setHotcueColorPalette(originalColorPalette); + saveAndReloadConfig(); + HotcueColorPalette colorPaletteFromConfig = + colorPaletteSettings.getHotcueColorPalette(); + ASSERT_EQ(originalColorPalette, colorPaletteFromConfig); +} + +TEST_F(ColorConfigTest, TestReplaceColorPalette) { + HotcueColorPaletteSettings colorPaletteSettings(config()); + HotcueColorPalette colorPalette1(QList{ + QColor("#66FF9900"), + QColor("#FF9900"), + QColor("#00000000"), + QColor("#FFFFFF"), + }); + HotcueColorPalette colorPalette2(QList{ + QColor("#0000FF"), + QColor("#FF0000"), + }); + ConfigKey key("[ColorPalette]", "colorPalette"); + colorPaletteSettings.setHotcueColorPalette(colorPalette1); + saveAndReloadConfig(); + colorPaletteSettings.setHotcueColorPalette(colorPalette2); + saveAndReloadConfig(); + HotcueColorPalette colorPaletteFromConfig = + colorPaletteSettings.getHotcueColorPalette(); + ASSERT_EQ(colorPalette2, colorPaletteFromConfig); +} + +TEST_F(ColorConfigTest, TestDefaultColorPalette) { + HotcueColorPaletteSettings colorPaletteSettings(config()); + HotcueColorPalette colorPaletteFromConfig = + colorPaletteSettings.getHotcueColorPalette(); + ASSERT_EQ(HotcueColorPalette::mixxxPalette, colorPaletteFromConfig); +} \ No newline at end of file diff --git a/src/util/color/hotcuecolorpalette.h b/src/util/color/hotcuecolorpalette.h index 2f4a4f579f3..a544599f3e0 100644 --- a/src/util/color/hotcuecolorpalette.h +++ b/src/util/color/hotcuecolorpalette.h @@ -14,4 +14,9 @@ class HotcueColorPalette { static const HotcueColorPalette mixxxPalette; QList m_colorList; -}; \ No newline at end of file +}; + +inline bool operator==( + const HotcueColorPalette& lhs, const HotcueColorPalette& rhs) { + return lhs.m_colorList == rhs.m_colorList; +} From f4c29b2152e2b9a8511d3d6e18f389f00ff30d07 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Sat, 2 Nov 2019 18:57:42 +0100 Subject: [PATCH 05/42] Make Cue related code use QColor .. instead of PredefinedColorPointer. COs and the database now hold the color hex int representation instead of a color id. --- src/engine/controls/cuecontrol.cpp | 30 ++++++----- src/engine/controls/cuecontrol.h | 8 +-- src/library/dao/cuedao.cpp | 19 ++++--- src/library/dlgtrackinfo.cpp | 54 +++++++++++-------- src/library/dlgtrackinfo.h | 3 +- src/track/cue.cpp | 17 ++++-- src/track/cue.h | 18 ++++--- src/waveform/renderers/waveformrendermark.cpp | 2 +- src/widget/colormenu.cpp | 29 +++------- src/widget/colormenu.h | 8 +-- src/widget/cuemenu.cpp | 10 +--- src/widget/cuemenu.h | 6 +-- src/widget/woverview.cpp | 7 ++- src/widget/wtracktableview.cpp | 2 +- 14 files changed, 116 insertions(+), 97 deletions(-) diff --git a/src/engine/controls/cuecontrol.cpp b/src/engine/controls/cuecontrol.cpp index 1e1fd43a5f1..e0d00676bc2 100644 --- a/src/engine/controls/cuecontrol.cpp +++ b/src/engine/controls/cuecontrol.cpp @@ -7,12 +7,13 @@ #include "engine/enginebuffer.h" #include "engine/controls/cuecontrol.h" +#include "control/controlindicator.h" #include "control/controlobject.h" #include "control/controlpushbutton.h" -#include "control/controlindicator.h" -#include "vinylcontrol/defs_vinylcontrol.h" -#include "util/sample.h" +#include "preferences/hotcuecolorpalettesettings.h" #include "util/color/color.h" +#include "util/sample.h" +#include "vinylcontrol/defs_vinylcontrol.h" // TODO: Convert these doubles to a standard enum // and convert elseif logic to switch statements @@ -579,9 +580,12 @@ void CueControl::hotcueSet(HotcueControl* pControl, double v) { // TODO(XXX) deal with spurious signals attachCue(pCue, pControl); - if (getConfig()->getValue(ConfigKey("[Controls]", "auto_hotcue_colors"), false)) { - const QList predefinedColors = Color::kPredefinedColorsSet.allColors; - pCue->setColor(predefinedColors.at((hotcue % (predefinedColors.count() - 1)) + 1)); + ConfigKey autoHotcueColorsKey("[Controls]", "auto_hotcue_colors"); + if (getConfig()->getValue(autoHotcueColorsKey, false)) { + HotcueColorPaletteSettings colorPaletteSettings(getConfig()); + auto hotcueColorPalette = colorPaletteSettings.getHotcueColorPalette(); + auto colors = hotcueColorPalette.m_colorList; + pCue->setColor(colors.at((hotcue % (colors.count() - 1)) + 1)); }; // If quantize is enabled and we are not playing, jump to the cue point @@ -1790,9 +1794,9 @@ void HotcueControl::slotHotcuePositionChanged(double newPosition) { emit(hotcuePositionChanged(this, newPosition)); } -void HotcueControl::slotHotcueColorChanged(double newColorId) { - m_pCue->setColor(Color::kPredefinedColorsSet.predefinedColorFromId(newColorId)); - emit(hotcueColorChanged(this, newColorId)); +void HotcueControl::slotHotcueColorChanged(double newColor) { + m_pCue->setColor(QColor::fromRgba(newColor)); + emit(hotcueColorChanged(this, newColor)); } double HotcueControl::getPosition() const { @@ -1806,12 +1810,12 @@ void HotcueControl::setCue(CuePointer pCue) { // because we have a null check for valid data else where in the code m_pCue = pCue; } -PredefinedColorPointer HotcueControl::getColor() const { - return Color::kPredefinedColorsSet.predefinedColorFromId(m_hotcueColor->get()); +QColor HotcueControl::getColor() const { + return QColor::fromRgba(m_hotcueColor->get()); } -void HotcueControl::setColor(PredefinedColorPointer newColor) { - m_hotcueColor->set(static_cast(newColor->m_iId)); +void HotcueControl::setColor(const QColor& newColor) { + m_hotcueColor->set(newColor.rgba()); } void HotcueControl::resetCue() { // clear pCue first because we have a null check for valid data else where diff --git a/src/engine/controls/cuecontrol.h b/src/engine/controls/cuecontrol.h index 6d9b735cc13..0fcbe048ed5 100644 --- a/src/engine/controls/cuecontrol.h +++ b/src/engine/controls/cuecontrol.h @@ -47,8 +47,8 @@ class HotcueControl : public QObject { void setCue(CuePointer pCue); void resetCue(); void setPosition(double position); - void setColor(PredefinedColorPointer newColor); - PredefinedColorPointer getColor() const; + void setColor(const QColor& newColor); + QColor getColor() const; // Used for caching the preview state of this hotcue control. inline bool isPreviewing() { @@ -73,7 +73,7 @@ class HotcueControl : public QObject { void slotHotcueActivatePreview(double v); void slotHotcueClear(double v); void slotHotcuePositionChanged(double newPosition); - void slotHotcueColorChanged(double newColorId); + void slotHotcueColorChanged(double newColor); signals: void hotcueSet(HotcueControl* pHotcue, double v); @@ -84,7 +84,7 @@ class HotcueControl : public QObject { void hotcueActivatePreview(HotcueControl* pHotcue, double v); void hotcueClear(HotcueControl* pHotcue, double v); void hotcuePositionChanged(HotcueControl* pHotcue, double newPosition); - void hotcueColorChanged(HotcueControl* pHotcue, double newColorId); + void hotcueColorChanged(HotcueControl* pHotcue, double newColor); void hotcuePlay(double v); private: diff --git a/src/library/dao/cuedao.cpp b/src/library/dao/cuedao.cpp index 0b080810a92..bf095943d76 100644 --- a/src/library/dao/cuedao.cpp +++ b/src/library/dao/cuedao.cpp @@ -11,7 +11,6 @@ #include "library/queryutil.h" #include "util/assert.h" #include "util/performancetimer.h" -#include "util/color/color.h" #include "util/color/predefinedcolor.h" int CueDAO::cueCount() { @@ -54,9 +53,17 @@ CuePointer CueDAO::cueFromRow(const QSqlQuery& query) const { int length = record.value(record.indexOf("length")).toInt(); int hotcue = record.value(record.indexOf("hotcue")).toInt(); QString label = record.value(record.indexOf("label")).toString(); - int iColorId = record.value(record.indexOf("color")).toInt(); - PredefinedColorPointer color = Color::kPredefinedColorsSet.predefinedColorFromId(iColorId); - CuePointer pCue(new Cue(id, trackId, (Cue::CueSource)source, (Cue::CueType)type, position, length, hotcue, label, color)); + uint colorValue = record.value(record.indexOf("color")).toUInt(); + QColor color = QColor::fromRgba(colorValue); + CuePointer pCue(new Cue(id, + trackId, + (Cue::CueSource)source, + (Cue::CueType)type, + position, + length, + hotcue, + label, + color)); m_cues[id] = pCue; return pCue; } @@ -149,7 +156,7 @@ bool CueDAO::saveCue(Cue* cue) { query.bindValue(":length", cue->getLength()); query.bindValue(":hotcue", cue->getHotCue()); query.bindValue(":label", cue->getLabel()); - query.bindValue(":color", cue->getColor()->m_iId); + query.bindValue(":color", cue->getColor().rgba()); if (query.exec()) { int id = query.lastInsertId().toInt(); @@ -179,7 +186,7 @@ bool CueDAO::saveCue(Cue* cue) { query.bindValue(":length", cue->getLength()); query.bindValue(":hotcue", cue->getHotCue()); query.bindValue(":label", cue->getLabel()); - query.bindValue(":color", cue->getColor()->m_iId); + query.bindValue(":color", cue->getColor().rgba()); if (query.exec()) { cue->setDirty(false); diff --git a/src/library/dlgtrackinfo.cpp b/src/library/dlgtrackinfo.cpp index 62cd8d5d9cb..9f37dc0399a 100644 --- a/src/library/dlgtrackinfo.cpp +++ b/src/library/dlgtrackinfo.cpp @@ -1,17 +1,18 @@ +#include #include -#include #include -#include +#include #include "library/coverartcache.h" #include "library/coverartutils.h" #include "library/dlgtrackinfo.h" +#include "preferences/hotcuecolorpalettesettings.h" #include "sources/soundsourceproxy.h" #include "track/beatfactory.h" #include "track/cue.h" #include "track/keyfactory.h" #include "track/keyutils.h" -#include "util/color/color.h" +#include "util/color/hotcuecolorpalette.h" #include "util/compatibility.h" #include "util/desktophelper.h" #include "util/duration.h" @@ -22,11 +23,12 @@ const int kMinBpm = 30; // Maximum allowed interval between beats (calculated from kMinBpm). const mixxx::Duration kMaxInterval = mixxx::Duration::fromMillis(1000.0 * (60.0 / kMinBpm)); -DlgTrackInfo::DlgTrackInfo(QWidget* parent) - : QDialog(parent), - m_pTapFilter(new TapFilter(this, kFilterLength, kMaxInterval)), - m_dLastTapedBpm(-1.), - m_pWCoverArtLabel(new WCoverArtLabel(this)) { +DlgTrackInfo::DlgTrackInfo(UserSettingsPointer pConfig, QWidget* parent) + : QDialog(parent), + m_pTapFilter(new TapFilter(this, kFilterLength, kMaxInterval)), + m_dLastTapedBpm(-1.), + m_pWCoverArtLabel(new WCoverArtLabel(this)), + m_pConfig(pConfig) { init(); } @@ -400,21 +402,22 @@ void DlgTrackInfo::populateCues(TrackPointer pTrack) { // Make the type read only typeItem->setFlags(Qt::NoItemFlags); + HotcueColorPaletteSettings colorPaletteSettings(m_pConfig); + auto colorPalette = colorPaletteSettings.getHotcueColorPalette(); + QList hotcueColorList = + colorPaletteSettings.getHotcueColorPalette().m_colorList; QComboBox* colorComboBox = new QComboBox(); - const QList predefinedColors = Color::kPredefinedColorsSet.allColors; - for (int i = 0; i < predefinedColors.count(); i++) { - PredefinedColorPointer color = predefinedColors.at(i); - QColor defaultRgba = color->m_defaultRgba; - colorComboBox->addItem(color->m_sDisplayName, defaultRgba); - if (*color != *Color::kPredefinedColorsSet.noColor) { - QPixmap pixmap(80, 80); - pixmap.fill(defaultRgba); - QIcon icon(pixmap); - colorComboBox->setItemIcon(i, icon); - } + for (int i = 0; i < hotcueColorList.count(); i++) { + QColor color = hotcueColorList.at(i); + colorComboBox->addItem("", color); + QPixmap pixmap(80, 80); + pixmap.fill(color); + QIcon icon(pixmap); + colorComboBox->setItemIcon(i, icon); } - PredefinedColorPointer cueColor = pCue->getColor(); - colorComboBox->setCurrentIndex(Color::kPredefinedColorsSet.predefinedColorIndex(cueColor)); + QColor cueColor = pCue->getColor(); + int colorIndex = hotcueColorList.indexOf(cueColor); + colorComboBox->setCurrentIndex(math_min(colorIndex, 0)); m_cueMap[row] = pCue; cueTable->insertRow(row); @@ -499,7 +502,14 @@ void DlgTrackInfo::saveTrack() { if (pCue->getType() == Cue::CUE) { auto colorComboBox = qobject_cast(colorWidget); if (colorComboBox) { - PredefinedColorPointer color = Color::kPredefinedColorsSet.allColors.at(colorComboBox->currentIndex()); + HotcueColorPaletteSettings colorPaletteSettings(m_pConfig); + auto colorPalette = + colorPaletteSettings.getHotcueColorPalette(); + QList hotcueColorList = + colorPaletteSettings.getHotcueColorPalette() + .m_colorList; + QColor color = + hotcueColorList.at(colorComboBox->currentIndex()); pCue->setColor(color); } } diff --git a/src/library/dlgtrackinfo.h b/src/library/dlgtrackinfo.h index 0ca2a44a292..c343d88a48c 100644 --- a/src/library/dlgtrackinfo.h +++ b/src/library/dlgtrackinfo.h @@ -18,7 +18,7 @@ class DlgTrackInfo : public QDialog, public Ui::DlgTrackInfo { Q_OBJECT public: - DlgTrackInfo(QWidget* parent); + DlgTrackInfo(UserSettingsPointer pConfig, QWidget* parent); virtual ~DlgTrackInfo(); public slots: @@ -85,6 +85,7 @@ class DlgTrackInfo : public QDialog, public Ui::DlgTrackInfo { CoverInfo m_loadedCoverInfo; WCoverArtLabel* m_pWCoverArtLabel; + UserSettingsPointer m_pConfig; }; #endif /* DLGTRACKINFO_H */ diff --git a/src/track/cue.cpp b/src/track/cue.cpp index 1d27df21c6d..828c0958ab1 100644 --- a/src/track/cue.cpp +++ b/src/track/cue.cpp @@ -29,12 +29,19 @@ Cue::Cue(TrackId trackId) m_length(0.0), m_iHotCue(-1), m_label(kDefaultLabel), - m_color(Color::kPredefinedColorsSet.noColor) { + m_color(QColor()) { DEBUG_ASSERT(!m_label.isNull()); } -Cue::Cue(int id, TrackId trackId, Cue::CueSource source, Cue::CueType type, double position, double length, - int hotCue, QString label, PredefinedColorPointer color) +Cue::Cue(int id, + TrackId trackId, + Cue::CueSource source, + Cue::CueType type, + double position, + double length, + int hotCue, + QString label, + QColor color) : m_bDirty(false), m_iId(id), m_trackId(trackId), @@ -155,12 +162,12 @@ void Cue::setLabel(const QString label) { emit(updated()); } -PredefinedColorPointer Cue::getColor() const { +QColor Cue::getColor() const { QMutexLocker lock(&m_mutex); return m_color; } -void Cue::setColor(const PredefinedColorPointer color) { +void Cue::setColor(const QColor& color) { QMutexLocker lock(&m_mutex); m_color = color; m_bDirty = true; diff --git a/src/track/cue.h b/src/track/cue.h index d4b3c343a47..8ea3891de97 100644 --- a/src/track/cue.h +++ b/src/track/cue.h @@ -6,7 +6,6 @@ #include #include "track/trackid.h" -#include "util/color/predefinedcolor.h" #include "util/memory.h" class CuePosition; @@ -60,8 +59,8 @@ class Cue : public QObject { QString getLabel() const; void setLabel(QString label); - PredefinedColorPointer getColor() const; - void setColor(PredefinedColorPointer color); + QColor getColor() const; + void setColor(const QColor& color); double getEndPosition() const; @@ -70,8 +69,15 @@ class Cue : public QObject { private: explicit Cue(TrackId trackId); - Cue(int id, TrackId trackId, CueSource source, CueType type, double position, double length, - int hotCue, QString label, PredefinedColorPointer color); + Cue(int id, + TrackId trackId, + CueSource source, + CueType type, + double position, + double length, + int hotCue, + QString label, + QColor color); void setDirty(bool dirty); void setId(int id); void setTrackId(TrackId trackId); @@ -87,7 +93,7 @@ class Cue : public QObject { double m_length; int m_iHotCue; QString m_label; - PredefinedColorPointer m_color; + QColor m_color; friend class Track; friend class CueDAO; diff --git a/src/waveform/renderers/waveformrendermark.cpp b/src/waveform/renderers/waveformrendermark.cpp index a5d49263185..d7ef8c90bd2 100644 --- a/src/waveform/renderers/waveformrendermark.cpp +++ b/src/waveform/renderers/waveformrendermark.cpp @@ -128,7 +128,7 @@ void WaveformRenderMark::slotCuesUpdated() { } QString newLabel = pCue->getLabel(); - QColor newColor = m_predefinedColorsRepresentation.representationFor(pCue->getColor()); + QColor newColor = pCue->getColor(); if (pMark->m_text.isNull() || newLabel != pMark->m_text || !pMark->fillColor().isValid() || newColor != pMark->fillColor()) { pMark->m_text = newLabel; diff --git a/src/widget/colormenu.cpp b/src/widget/colormenu.cpp index 543b2710842..0a0c6748ca2 100644 --- a/src/widget/colormenu.cpp +++ b/src/widget/colormenu.cpp @@ -6,34 +6,21 @@ ColorMenu::ColorMenu(QWidget *parent) // If another title would be more appropriate in some context, setTitle // can be called again after construction. setTitle(tr("Set color")); - for (const auto& pColor : Color::kPredefinedColorsSet.allColors) { - if (*pColor == *Color::kPredefinedColorsSet.noColor) { - continue; - } + useColorPalette(HotcueColorPalette::mixxxPalette); +} - QAction* pColorAction = new QAction(pColor->m_sDisplayName, this); +void ColorMenu::useColorPalette(const HotcueColorPalette& colorPalette) { + clear(); + for (const auto& pColor : colorPalette.m_colorList) { + QAction* pColorAction = new QAction(this); QPixmap pixmap(80, 80); - pixmap.fill(pColor->m_defaultRgba); + pixmap.fill(pColor); pColorAction->setIcon(QIcon(pixmap)); - m_pColorActions.insert(pColor, pColorAction); + m_pColorActions.append(pColorAction); addAction(pColorAction); connect(pColorAction, &QAction::triggered, this, [pColor, this]() { emit(colorPicked(pColor)); }); } } - -void ColorMenu::useColorSet(PredefinedColorsRepresentation* pColorRepresentation) { - QMapIterator i(m_pColorActions); - while (i.hasNext()) { - i.next(); - QPixmap pixmap(80, 80); - if (pColorRepresentation == nullptr) { - pixmap.fill(i.key()->m_defaultRgba); - } else { - pixmap.fill(pColorRepresentation->representationFor(i.key())); - } - i.value()->setIcon(QIcon(pixmap)); - } -} diff --git a/src/widget/colormenu.h b/src/widget/colormenu.h index 4c82943cbb8..3577cce7fd4 100644 --- a/src/widget/colormenu.h +++ b/src/widget/colormenu.h @@ -2,18 +2,18 @@ #include -#include "util/color/color.h" +#include "util/color/hotcuecolorpalette.h" class ColorMenu : public QMenu { Q_OBJECT public: ColorMenu(QWidget *parent = nullptr); - void useColorSet(PredefinedColorsRepresentation* pColorRepresentation); + void useColorPalette(const HotcueColorPalette& colorPalette); signals: - void colorPicked(PredefinedColorPointer pColor); + void colorPicked(QColor pColor); private: - QMap m_pColorActions; + QList m_pColorActions; }; diff --git a/src/widget/cuemenu.cpp b/src/widget/cuemenu.cpp index b9d62a30911..53d88736e8a 100644 --- a/src/widget/cuemenu.cpp +++ b/src/widget/cuemenu.cpp @@ -37,14 +37,8 @@ void CueMenu::slotEditLabel() { } } -void CueMenu::slotChangeCueColor(PredefinedColorPointer pColor) { - VERIFY_OR_DEBUG_ASSERT(m_pCue != nullptr) { - return; - } - VERIFY_OR_DEBUG_ASSERT(pColor != nullptr) { - return; - } - m_pCue->setColor(pColor); +void CueMenu::slotChangeCueColor(QColor color) { + m_pCue->setColor(color); } void CueMenu::slotRemoveCue() { diff --git a/src/widget/cuemenu.h b/src/widget/cuemenu.h index 3802292e44a..b8f2f2c49a2 100644 --- a/src/widget/cuemenu.h +++ b/src/widget/cuemenu.h @@ -20,16 +20,16 @@ class CueMenu : public QMenu { m_pTrack = pTrack; } - void useColorSet(PredefinedColorsRepresentation* pColorRepresentation) { + void useColorSet(const HotcueColorPalette& colorPalette) { if (m_pColorMenu != nullptr) { - m_pColorMenu->useColorSet(pColorRepresentation); + m_pColorMenu->useColorPalette(colorPalette); } } private slots: void slotEditLabel(); void slotRemoveCue(); - void slotChangeCueColor(PredefinedColorPointer pColor); + void slotChangeCueColor(QColor color); private: CuePointer m_pCue; diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index fdf6030e65b..996fce30699 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -25,6 +25,7 @@ #include "control/controlproxy.h" #include "engine/engine.h" #include "mixer/playermanager.h" +#include "preferences/hotcuecolorpalettesettings.h" #include "track/track.h" #include "util/color/color.h" #include "util/compatibility.h" @@ -137,7 +138,9 @@ void WOverview::setup(const QDomNode& node, const SkinContext& context) { ? defaultMark->fillColor() : m_signalColors.getAxesColor(); m_predefinedColorsRepresentation = context.getCueColorRepresentation(node, defaultColor); - m_pCueMenu->useColorSet(&m_predefinedColorsRepresentation); + HotcueColorPaletteSettings colorPaletteSettings(m_pConfig); + auto colorPalette = colorPaletteSettings.getHotcueColorPalette(); + m_pCueMenu->useColorSet(colorPalette); for (const auto& pMark: m_marks) { if (pMark->isValid()) { @@ -341,7 +344,7 @@ void WOverview::updateCues(const QList &loadedCues) { if (pMark != nullptr && pMark->isValid() && pMark->isVisible() && pMark->getSamplePosition() != Cue::kPositionNotDefined) { - QColor newColor = m_predefinedColorsRepresentation.representationFor(currentCue->getColor()); + QColor newColor = currentCue->getColor(); if (newColor != pMark->fillColor() || newColor != pMark->m_textColor) { pMark->setBaseColor(newColor); } diff --git a/src/widget/wtracktableview.cpp b/src/widget/wtracktableview.cpp index 5a39c4dc025..aa916ce322a 100644 --- a/src/widget/wtracktableview.cpp +++ b/src/widget/wtracktableview.cpp @@ -754,7 +754,7 @@ void WTrackTableView::showTrackInfo(QModelIndex index) { if (m_pTrackInfo.isNull()) { // Give a NULL parent because otherwise it inherits our style which can // make it unreadable. Bug #673411 - m_pTrackInfo.reset(new DlgTrackInfo(nullptr)); + m_pTrackInfo.reset(new DlgTrackInfo(m_pConfig, nullptr)); connect(m_pTrackInfo.data(), SIGNAL(next()), this, SLOT(slotNextTrackInfo())); From ece9385682896c18ecd66802466db80cd7eee125 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Sat, 2 Nov 2019 19:14:52 +0100 Subject: [PATCH 06/42] Migrate hotcue color id to RGBA in DB --- res/schema.xml | 25 +++++++++++++++++++++++++ src/database/mixxxdb.cpp | 2 +- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/res/schema.xml b/res/schema.xml index 8b9040538e7..b091dd9c161 100644 --- a/res/schema.xml +++ b/res/schema.xml @@ -447,4 +447,29 @@ METADATA ALTER TABLE cues ADD COLUMN source INTEGER DEFAULT 2 NOT NULL; + + + Convert the PredefinedColor id to the actual RGBA value. + + + + UPDATE cues SET color=4278190080 WHERE color=0; + + UPDATE cues SET color=4291103240 WHERE color=1; + + UPDATE cues SET color=4281515588 WHERE color=2; + + UPDATE cues SET color=4278207743 WHERE color=3; + + UPDATE cues SET color=4294496768 WHERE color=4; + + UPDATE cues SET color=4282569972 WHERE color=5; + + UPDATE cues SET color=4289659084 WHERE color=6; + + UPDATE cues SET color=4294747863 WHERE color=7; + + UPDATE cues SET color=4294111999 WHERE color=8; + + diff --git a/src/database/mixxxdb.cpp b/src/database/mixxxdb.cpp index 737e64277c0..5a18ff73526 100644 --- a/src/database/mixxxdb.cpp +++ b/src/database/mixxxdb.cpp @@ -11,7 +11,7 @@ const QString MixxxDb::kDefaultSchemaFile(":/schema.xml"); //static -const int MixxxDb::kRequiredSchemaVersion = 29; +const int MixxxDb::kRequiredSchemaVersion = 30; namespace { From ae4907eaae10b9748401e937595d16de3c44b780 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Sat, 2 Nov 2019 21:20:23 +0100 Subject: [PATCH 07/42] Remove PredefinedColorRepresentation --- src/skin/skincontext.h | 15 ------- .../color/predefinedcolorsrepresentation.h | 41 ------------------- src/util/color/predefinedcolorsset.h | 16 -------- src/waveform/renderers/waveformrendermark.cpp | 5 --- src/waveform/renderers/waveformrendermark.h | 2 - src/widget/woverview.cpp | 5 --- src/widget/woverview.h | 1 - 7 files changed, 85 deletions(-) delete mode 100644 src/util/color/predefinedcolorsrepresentation.h diff --git a/src/skin/skincontext.h b/src/skin/skincontext.h index 7a81d296318..9c3f86f1918 100644 --- a/src/skin/skincontext.h +++ b/src/skin/skincontext.h @@ -12,7 +12,6 @@ #include #include -#include "../util/color/predefinedcolorsrepresentation.h" #include "preferences/usersettings.h" #include "skin/pixmapsource.h" #include "util/color/color.h" @@ -247,20 +246,6 @@ class SkinContext { return m_scaleFactor; } - PredefinedColorsRepresentation getCueColorRepresentation(const QDomNode& node, QColor defaultColor) const { - PredefinedColorsRepresentation colorRepresentation = Color::kPredefinedColorsSet.defaultRepresentation(); - for (PredefinedColorPointer color : Color::kPredefinedColorsSet.allColors) { - QString sColorName(color->m_sName); - QColor skinRgba = selectColor(node, "Cue" + sColorName); - if (skinRgba.isValid()) { - PredefinedColorPointer originalColor = Color::kPredefinedColorsSet.predefinedColorFromName(sColorName); - colorRepresentation.setCustomRgba(originalColor, skinRgba); - } - } - colorRepresentation.setCustomRgba(Color::kPredefinedColorsSet.noColor, defaultColor); - return colorRepresentation; - } - private: PixmapSource getPixmapSourceInner(const QString& filename) const; diff --git a/src/util/color/predefinedcolorsrepresentation.h b/src/util/color/predefinedcolorsrepresentation.h deleted file mode 100644 index 83f16405447..00000000000 --- a/src/util/color/predefinedcolorsrepresentation.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef PREDEFINEDCOLORSREPRESENTATION_H -#define PREDEFINEDCOLORSREPRESENTATION_H - -#include -#include - -#include "util/color/predefinedcolor.h" - -// PredefinedColorsRepresentation defines a particular way to render Mixxx PredefinedColors. -// -// PredefinedColorsRepresentation maps a PredefinedColor to a custom Rgba color. -// Initially no color has a custom Rgba set. -// Call setCustomRgba(PredefinedColorPointer, QColor) to add a custom Rgba for a predefined color -// and customize the color map. -// -// This class uses the color's name() property as key, e.g. "#A9A9A9" -// Since QHash has copy-on-write, making a copy of PredefinedColorsRepresentation is fast. -// A deep copy of the QHash will be made when a copy is modified. -class PredefinedColorsRepresentation final { - public: - // Set a custom Rgba for a given color - void setCustomRgba(PredefinedColorPointer color, QColor cutomizedRgba) { - m_colorNameMap[color->m_defaultRgba.name()] = cutomizedRgba.name(); - } - - // Returns the custom Rgba of a color. - // If no custom Rgba is set for color, returns color->m_defaultRgba. - QColor representationFor(PredefinedColorPointer color) const { - QColor defaultRgba = color->m_defaultRgba; - if (m_colorNameMap.contains(defaultRgba.name())) { - return QColor(m_colorNameMap[defaultRgba.name()]); - } - return defaultRgba; - } - - - private: - QHash m_colorNameMap; -}; - -#endif /* PREDEFINEDCOLORSREPRESENTATION_H */ diff --git a/src/util/color/predefinedcolorsset.h b/src/util/color/predefinedcolorsset.h index f2dbff29ee2..b68d6b89c1b 100644 --- a/src/util/color/predefinedcolorsset.h +++ b/src/util/color/predefinedcolorsset.h @@ -3,7 +3,6 @@ #include #include -#include "predefinedcolorsrepresentation.h" #include "util/color/predefinedcolor.h" // This class defines a set of predefined colors and provides some handy functions to work with them. @@ -70,13 +69,6 @@ class PredefinedColorsSet final { white, }; - PredefinedColorsSet() - : m_defaultRepresentation() { - for (PredefinedColorPointer color : allColors) { - m_defaultRepresentation.setCustomRgba(color, color->m_defaultRgba); - } - } - // Returns the position of a PredefinedColor in the allColors list. int predefinedColorIndex(PredefinedColorPointer searchedColor) const { for (int position = 0; position < allColors.count(); ++position) { @@ -109,12 +101,4 @@ class PredefinedColorsSet final { } return noColor; }; - - // The default color representation, i.e. maps each predefined color to its default Rgba. - PredefinedColorsRepresentation defaultRepresentation() const { - return m_defaultRepresentation; - }; - - private: - PredefinedColorsRepresentation m_defaultRepresentation; }; diff --git a/src/waveform/renderers/waveformrendermark.cpp b/src/waveform/renderers/waveformrendermark.cpp index d7ef8c90bd2..034363e2cdf 100644 --- a/src/waveform/renderers/waveformrendermark.cpp +++ b/src/waveform/renderers/waveformrendermark.cpp @@ -27,11 +27,6 @@ WaveformRenderMark::WaveformRenderMark( void WaveformRenderMark::setup(const QDomNode& node, const SkinContext& context) { WaveformSignalColors signalColors = *m_waveformRenderer->getWaveformSignalColors(); m_marks.setup(m_waveformRenderer->getGroup(), node, context, signalColors); - WaveformMarkPointer defaultMark(m_marks.getDefaultMark()); - QColor defaultColor = defaultMark - ? defaultMark->fillColor() - : signalColors.getAxesColor(); - m_predefinedColorsRepresentation = context.getCueColorRepresentation(node, defaultColor); } void WaveformRenderMark::draw(QPainter* painter, QPaintEvent* /*event*/) { diff --git a/src/waveform/renderers/waveformrendermark.h b/src/waveform/renderers/waveformrendermark.h index 01c975fe3c1..b3b0cafbaa5 100644 --- a/src/waveform/renderers/waveformrendermark.h +++ b/src/waveform/renderers/waveformrendermark.h @@ -35,8 +35,6 @@ class WaveformRenderMark : public QObject, public WaveformRendererAbstract { private: void generateMarkImage(WaveformMarkPointer pMark); - PredefinedColorsRepresentation m_predefinedColorsRepresentation; - WaveformMarkSet m_marks; DISALLOW_COPY_AND_ASSIGN(WaveformRenderMark); }; diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index 996fce30699..396dcaff778 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -133,11 +133,6 @@ void WOverview::setup(const QDomNode& node, const SkinContext& context) { // setup hotcues and cue and loop(s) m_marks.setup(m_group, node, context, m_signalColors); - WaveformMarkPointer defaultMark(m_marks.getDefaultMark()); - QColor defaultColor = defaultMark - ? defaultMark->fillColor() - : m_signalColors.getAxesColor(); - m_predefinedColorsRepresentation = context.getCueColorRepresentation(node, defaultColor); HotcueColorPaletteSettings colorPaletteSettings(m_pConfig); auto colorPalette = colorPaletteSettings.getHotcueColorPalette(); m_pCueMenu->useColorSet(colorPalette); diff --git a/src/widget/woverview.h b/src/widget/woverview.h index 9df3e48829b..a2d11dde431 100644 --- a/src/widget/woverview.h +++ b/src/widget/woverview.h @@ -168,7 +168,6 @@ class WOverview : public WWidget, public TrackDropTarget { QColor m_labelBackgroundColor; QColor m_endOfTrackColor; - PredefinedColorsRepresentation m_predefinedColorsRepresentation; // All WaveformMarks WaveformMarkSet m_marks; // List of visible WaveformMarks sorted by the order they appear in the track From 4367fe3181bb44b0b11294d7f0ba91c5c0edc3cc Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Sun, 3 Nov 2019 09:43:25 +0100 Subject: [PATCH 08/42] Make UserSettings available in ControllerEngine --- src/controllers/controller.cpp | 9 +++++---- src/controllers/controller.h | 4 +++- src/controllers/controllerengine.cpp | 4 +++- src/controllers/controllerengine.h | 3 ++- src/controllers/controllermanager.cpp | 4 ++-- src/controllers/hid/hidcontroller.cpp | 5 +++-- src/controllers/hid/hidcontroller.h | 3 ++- src/controllers/hid/hidenumerator.cpp | 5 +++-- src/controllers/hid/hidenumerator.h | 3 ++- src/controllers/midi/midicontroller.cpp | 4 ++-- src/controllers/midi/midicontroller.h | 2 +- src/controllers/midi/portmidicontroller.cpp | 11 +++++------ src/controllers/midi/portmidicontroller.h | 7 ++++--- src/controllers/midi/portmidienumerator.cpp | 12 ++++++++---- src/controllers/midi/portmidienumerator.h | 3 ++- .../controller_preset_validation_test.cpp | 9 ++++----- src/test/midicontrollertest.cpp | 5 +++-- src/test/portmidicontroller_test.cpp | 19 +++++++++++-------- 18 files changed, 65 insertions(+), 47 deletions(-) diff --git a/src/controllers/controller.cpp b/src/controllers/controller.cpp index e30ff7a5f63..15a1e1dd5ec 100644 --- a/src/controllers/controller.cpp +++ b/src/controllers/controller.cpp @@ -13,14 +13,15 @@ #include "controllers/defs_controllers.h" #include "util/screensaver.h" -Controller::Controller() +Controller::Controller(UserSettingsPointer pConfig) : QObject(), m_pEngine(NULL), m_bIsOutputDevice(false), m_bIsInputDevice(false), m_bIsOpen(false), - m_bLearning(false) { - m_userActivityInhibitTimer.start(); + m_bLearning(false), + m_pConfig(pConfig) { + m_userActivityInhibitTimer.start(); } Controller::~Controller() { @@ -35,7 +36,7 @@ void Controller::startEngine() qWarning() << "Controller: Engine already exists! Restarting:"; stopEngine(); } - m_pEngine = new ControllerEngine(this); + m_pEngine = new ControllerEngine(this, m_pConfig); } void Controller::stopEngine() { diff --git a/src/controllers/controller.h b/src/controllers/controller.h index 9cc6be386ce..44a5218e2ae 100644 --- a/src/controllers/controller.h +++ b/src/controllers/controller.h @@ -23,7 +23,7 @@ class Controller : public QObject, ConstControllerPresetVisitor { Q_OBJECT public: - Controller(); + Controller(UserSettingsPointer pConfig); ~Controller() override; // Subclass should call close() at minimum. // Returns the extension for the controller (type) preset files. This is @@ -162,6 +162,8 @@ class Controller : public QObject, ConstControllerPresetVisitor { bool m_bLearning; QTime m_userActivityInhibitTimer; + UserSettingsPointer m_pConfig; + // accesses lots of our stuff, but in the same thread friend class ControllerManager; // For testing diff --git a/src/controllers/controllerengine.cpp b/src/controllers/controllerengine.cpp index 1d09be74657..36d9248223f 100644 --- a/src/controllers/controllerengine.cpp +++ b/src/controllers/controllerengine.cpp @@ -29,9 +29,11 @@ const int kDecks = 16; const int kScratchTimerMs = 1; const double kAlphaBetaDt = kScratchTimerMs / 1000.0; -ControllerEngine::ControllerEngine(Controller* controller) +ControllerEngine::ControllerEngine( + Controller* controller, UserSettingsPointer pConfig) : m_pEngine(nullptr), m_pController(controller), + m_pConfig(pConfig), m_bPopups(false), m_pBaClass(nullptr) { // Handle error dialog buttons diff --git a/src/controllers/controllerengine.h b/src/controllers/controllerengine.h index 2359a0bcbb3..7eafb1763ea 100644 --- a/src/controllers/controllerengine.h +++ b/src/controllers/controllerengine.h @@ -80,7 +80,7 @@ class ScriptConnectionInvokableWrapper : public QObject { class ControllerEngine : public QObject { Q_OBJECT public: - ControllerEngine(Controller* controller); + ControllerEngine(Controller* controller, UserSettingsPointer pConfig); virtual ~ControllerEngine(); bool isReady(); @@ -198,6 +198,7 @@ class ControllerEngine : public QObject { double getDeckRate(const QString& group); Controller* m_pController; + UserSettingsPointer m_pConfig; bool m_bPopups; QList m_scriptFunctionPrefixes; QMap m_scriptErrors; diff --git a/src/controllers/controllermanager.cpp b/src/controllers/controllermanager.cpp index 54029398d48..2de3fc57ce9 100644 --- a/src/controllers/controllermanager.cpp +++ b/src/controllers/controllermanager.cpp @@ -127,7 +127,7 @@ void ControllerManager::slotInitialize() { // Instantiate all enumerators. Enumerators can take a long time to // construct since they interact with host MIDI APIs. - m_enumerators.append(new PortMidiEnumerator()); + m_enumerators.append(new PortMidiEnumerator(m_pConfig)); #ifdef __HSS1394__ m_enumerators.append(new Hss1394Enumerator()); #endif @@ -135,7 +135,7 @@ void ControllerManager::slotInitialize() { m_enumerators.append(new BulkEnumerator()); #endif #ifdef __HID__ - m_enumerators.append(new HidEnumerator()); + m_enumerators.append(new HidEnumerator(m_pConfig)); #endif } diff --git a/src/controllers/hid/hidcontroller.cpp b/src/controllers/hid/hidcontroller.cpp index b07b5167c5e..094e31cae3f 100644 --- a/src/controllers/hid/hidcontroller.cpp +++ b/src/controllers/hid/hidcontroller.cpp @@ -47,8 +47,9 @@ void HidReader::run() { delete [] data; } -HidController::HidController(const hid_device_info deviceInfo) - : m_pHidDevice(NULL) { +HidController::HidController( + const hid_device_info deviceInfo, UserSettingsPointer pConfig) + : Controller(pConfig), m_pHidDevice(NULL) { // Copy required variables from deviceInfo, which will be freed after // this class is initialized by caller. hid_vendor_id = deviceInfo.vendor_id; diff --git a/src/controllers/hid/hidcontroller.h b/src/controllers/hid/hidcontroller.h index ac44aa9998b..0cefb8670d8 100644 --- a/src/controllers/hid/hidcontroller.h +++ b/src/controllers/hid/hidcontroller.h @@ -41,7 +41,8 @@ class HidReader : public QThread { class HidController final : public Controller { Q_OBJECT public: - HidController(const hid_device_info deviceInfo); + HidController( + const hid_device_info deviceInfo, UserSettingsPointer pConfig); ~HidController() override; QString presetExtension() override; diff --git a/src/controllers/hid/hidenumerator.cpp b/src/controllers/hid/hidenumerator.cpp index c1c2dc27834..802459f3c02 100644 --- a/src/controllers/hid/hidenumerator.cpp +++ b/src/controllers/hid/hidenumerator.cpp @@ -11,7 +11,8 @@ #include "controllers/hid/hidenumerator.h" #include "controllers/hid/hidblacklist.h" -HidEnumerator::HidEnumerator() : ControllerEnumerator() { +HidEnumerator::HidEnumerator(UserSettingsPointer pConfig) + : ControllerEnumerator(), m_pConfig(pConfig) { } HidEnumerator::~HidEnumerator() { @@ -98,7 +99,7 @@ QList HidEnumerator::queryDevices() { continue; } - HidController* currentDevice = new HidController(*cur_dev); + HidController* currentDevice = new HidController(*cur_dev, m_pConfig); m_devices.push_back(currentDevice); } hid_free_enumeration(devs); diff --git a/src/controllers/hid/hidenumerator.h b/src/controllers/hid/hidenumerator.h index 351e972beb1..9b39b75c947 100644 --- a/src/controllers/hid/hidenumerator.h +++ b/src/controllers/hid/hidenumerator.h @@ -12,13 +12,14 @@ class HidEnumerator : public ControllerEnumerator { public: - HidEnumerator(); + HidEnumerator(UserSettingsPointer pConfig); virtual ~HidEnumerator(); QList queryDevices(); private: QList m_devices; + UserSettingsPointer m_pConfig; }; #endif diff --git a/src/controllers/midi/midicontroller.cpp b/src/controllers/midi/midicontroller.cpp index 0176aa653ec..43dbaa65bed 100644 --- a/src/controllers/midi/midicontroller.cpp +++ b/src/controllers/midi/midicontroller.cpp @@ -17,8 +17,8 @@ #include "util/math.h" #include "util/screensaver.h" -MidiController::MidiController() - : Controller() { +MidiController::MidiController(UserSettingsPointer pConfig) + : Controller(pConfig) { setDeviceCategory(tr("MIDI Controller")); } diff --git a/src/controllers/midi/midicontroller.h b/src/controllers/midi/midicontroller.h index 93d91fdf443..b8ebb8b94b4 100644 --- a/src/controllers/midi/midicontroller.h +++ b/src/controllers/midi/midicontroller.h @@ -23,7 +23,7 @@ class MidiController : public Controller { Q_OBJECT public: - MidiController(); + MidiController(UserSettingsPointer pConfig); ~MidiController() override; QString presetExtension() override; diff --git a/src/controllers/midi/portmidicontroller.cpp b/src/controllers/midi/portmidicontroller.cpp index c4479a1ae68..4be78583c78 100644 --- a/src/controllers/midi/portmidicontroller.cpp +++ b/src/controllers/midi/portmidicontroller.cpp @@ -12,12 +12,11 @@ #include "controllers/controllerdebug.h" PortMidiController::PortMidiController(const PmDeviceInfo* inputDeviceInfo, - const PmDeviceInfo* outputDeviceInfo, - int inputDeviceIndex, - int outputDeviceIndex) - : MidiController(), - m_cReceiveMsg_index(0), - m_bInSysex(false) { + const PmDeviceInfo* outputDeviceInfo, + int inputDeviceIndex, + int outputDeviceIndex, + UserSettingsPointer pConfig) + : MidiController(pConfig), m_cReceiveMsg_index(0), m_bInSysex(false) { for (unsigned int k = 0; k < MIXXX_PORTMIDI_BUFFER_LEN; ++k) { // Can be shortened to `m_midiBuffer[k] = {}` with C++11. m_midiBuffer[k].message = 0; diff --git a/src/controllers/midi/portmidicontroller.h b/src/controllers/midi/portmidicontroller.h index 96da9afce1a..92d2954e18f 100644 --- a/src/controllers/midi/portmidicontroller.h +++ b/src/controllers/midi/portmidicontroller.h @@ -60,9 +60,10 @@ class PortMidiController : public MidiController { Q_OBJECT public: PortMidiController(const PmDeviceInfo* inputDeviceInfo, - const PmDeviceInfo* outputDeviceInfo, - int inputDeviceIndex, - int outputDeviceIndex); + const PmDeviceInfo* outputDeviceInfo, + int inputDeviceIndex, + int outputDeviceIndex, + UserSettingsPointer pConfig); ~PortMidiController() override; private slots: diff --git a/src/controllers/midi/portmidienumerator.cpp b/src/controllers/midi/portmidienumerator.cpp index 47e6a23a829..ba525ddeb73 100644 --- a/src/controllers/midi/portmidienumerator.cpp +++ b/src/controllers/midi/portmidienumerator.cpp @@ -21,7 +21,8 @@ bool shouldBlacklistDevice(const PmDeviceInfo* device) { deviceName.startsWith("Midi Through Port", Qt::CaseInsensitive); } -PortMidiEnumerator::PortMidiEnumerator() : MidiEnumerator() { +PortMidiEnumerator::PortMidiEnumerator(UserSettingsPointer pConfig) + : MidiEnumerator(), m_pConfig(pConfig) { PmError err = Pm_Initialize(); // Based on reading the source, it's not possible for this to fail. if (err != pmNoError) { @@ -257,9 +258,12 @@ QList PortMidiEnumerator::queryDevices() { // device (outputDeviceInfo != NULL). //.... so create our (aggregate) MIDI device! - PortMidiController *currentDevice = new PortMidiController( - inputDeviceInfo, outputDeviceInfo, - inputDevIndex, outputDevIndex); + PortMidiController* currentDevice = + new PortMidiController(inputDeviceInfo, + outputDeviceInfo, + inputDevIndex, + outputDevIndex, + m_pConfig); m_devices.push_back(currentDevice); } diff --git a/src/controllers/midi/portmidienumerator.h b/src/controllers/midi/portmidienumerator.h index 606c4feb4de..9566b54df1b 100644 --- a/src/controllers/midi/portmidienumerator.h +++ b/src/controllers/midi/portmidienumerator.h @@ -13,13 +13,14 @@ class PortMidiEnumerator : public MidiEnumerator { Q_OBJECT public: - PortMidiEnumerator(); + PortMidiEnumerator(UserSettingsPointer pConfig); virtual ~PortMidiEnumerator(); QList queryDevices(); private: QList m_devices; + UserSettingsPointer m_pConfig; }; // For testing. diff --git a/src/test/controller_preset_validation_test.cpp b/src/test/controller_preset_validation_test.cpp index d49ee3e50ba..970cf0158c9 100644 --- a/src/test/controller_preset_validation_test.cpp +++ b/src/test/controller_preset_validation_test.cpp @@ -15,7 +15,7 @@ class FakeController : public Controller { public: - FakeController(); + FakeController(UserSettingsPointer pConfig); ~FakeController() override; QString presetExtension() override { @@ -112,9 +112,8 @@ class FakeController : public Controller { HidControllerPreset m_hidPreset; }; -FakeController::FakeController() - : m_bMidiPreset(false), - m_bHidPreset(false) { +FakeController::FakeController(UserSettingsPointer pConfig) + : Controller(pConfig), m_bMidiPreset(false), m_bHidPreset(false) { } FakeController::~FakeController() { @@ -135,7 +134,7 @@ class ControllerPresetValidationTest : public MixxxTest { return false; } - FakeController controller; + FakeController controller(config()); controller.setDeviceName("Test Controller"); controller.startEngine(); controller.setPreset(*pPreset); diff --git a/src/test/midicontrollertest.cpp b/src/test/midicontrollertest.cpp index 696483c05fd..0d10c4c54a0 100644 --- a/src/test/midicontrollertest.cpp +++ b/src/test/midicontrollertest.cpp @@ -12,7 +12,8 @@ class MockMidiController : public MidiController { public: - MockMidiController() { } + MockMidiController(UserSettingsPointer pConfig) : MidiController(pConfig) { + } ~MockMidiController() override { } MOCK_METHOD0(open, int()); @@ -27,7 +28,7 @@ class MockMidiController : public MidiController { class MidiControllerTest : public MixxxTest { protected: void SetUp() override { - m_pController.reset(new MockMidiController()); + m_pController.reset(new MockMidiController(config())); } void addMapping(MidiInputMapping mapping) { diff --git a/src/test/portmidicontroller_test.cpp b/src/test/portmidicontroller_test.cpp index b8b9cb63594..8daf1f43d99 100644 --- a/src/test/portmidicontroller_test.cpp +++ b/src/test/portmidicontroller_test.cpp @@ -16,11 +16,15 @@ using ::testing::SetArrayArgument; class MockPortMidiController : public PortMidiController { public: MockPortMidiController(const PmDeviceInfo* inputDeviceInfo, - const PmDeviceInfo* outputDeviceInfo, - int inputDeviceIndex, - int outputDeviceIndex) : PortMidiController( - inputDeviceInfo, outputDeviceInfo, - inputDeviceIndex, outputDeviceIndex) { + const PmDeviceInfo* outputDeviceInfo, + int inputDeviceIndex, + int outputDeviceIndex, + UserSettingsPointer pConfig) + : PortMidiController(inputDeviceInfo, + outputDeviceInfo, + inputDeviceIndex, + outputDeviceIndex, + pConfig) { } ~MockPortMidiController() override { } @@ -71,9 +75,8 @@ class PortMidiControllerTest : public MixxxTest { m_outputDeviceInfo.output = 1; m_outputDeviceInfo.opened = 0; - m_pController.reset(new MockPortMidiController(&m_inputDeviceInfo, - &m_outputDeviceInfo, - 0, 0)); + m_pController.reset(new MockPortMidiController( + &m_inputDeviceInfo, &m_outputDeviceInfo, 0, 0, config())); m_pController->setPortMidiInputDevice(m_mockInput); m_pController->setPortMidiOutputDevice(m_mockOutput); } From bfe25a3bff76940306a3e97bd04c7854473ff716 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Sun, 3 Nov 2019 09:47:13 +0100 Subject: [PATCH 09/42] Remove PredefinedColors from controller scripts ... and use QColor instead. --- src/controllers/colorjsproxy.cpp | 50 +++++++++++++++------------ src/controllers/colorjsproxy.h | 21 ++++++----- src/controllers/controllerengine.cpp | 2 +- src/test/controllerengine_test.cpp | 45 ++++++++++++------------ src/util/color/hotcuecolorpalette.cpp | 5 ++- 5 files changed, 66 insertions(+), 57 deletions(-) diff --git a/src/controllers/colorjsproxy.cpp b/src/controllers/colorjsproxy.cpp index 4147cdcc124..8b2f2fd644e 100644 --- a/src/controllers/colorjsproxy.cpp +++ b/src/controllers/colorjsproxy.cpp @@ -1,36 +1,40 @@ #include "controllers/colorjsproxy.h" +#include "preferences/hotcuecolorpalettesettings.h" -ColorJSProxy::ColorJSProxy(QScriptEngine* pScriptEngine) - : m_pScriptEngine(pScriptEngine), - m_predefinedColorsList(makePredefinedColorsList(pScriptEngine)){}; - -ColorJSProxy::~ColorJSProxy() {}; +ColorJSProxy::ColorJSProxy( + QScriptEngine* pScriptEngine, UserSettingsPointer pConfig) + : m_pScriptEngine(pScriptEngine), + m_hotcueColorPalette(makeHotcueColorPalette(pScriptEngine, pConfig)), + m_pConfig(pConfig) { +} -QScriptValue ColorJSProxy::predefinedColorFromId(int iId) { - PredefinedColorPointer color(Color::kPredefinedColorsSet.predefinedColorFromId(iId)); - return jsColorFrom(color); -}; +ColorJSProxy::~ColorJSProxy() = default; -Q_INVOKABLE QScriptValue ColorJSProxy::predefinedColorsList() { - return m_predefinedColorsList; +Q_INVOKABLE QScriptValue ColorJSProxy::hotcueColorPalette() { + return m_hotcueColorPalette; } -QScriptValue ColorJSProxy::jsColorFrom(PredefinedColorPointer predefinedColor) { +QScriptValue ColorJSProxy::colorFromHexCode(uint colorCode) { + QColor color = QColor::fromRgba(colorCode); QScriptValue jsColor = m_pScriptEngine->newObject(); - jsColor.setProperty("red", predefinedColor->m_defaultRgba.red()); - jsColor.setProperty("green", predefinedColor->m_defaultRgba.green()); - jsColor.setProperty("blue", predefinedColor->m_defaultRgba.blue()); - jsColor.setProperty("alpha", predefinedColor->m_defaultRgba.alpha()); - jsColor.setProperty("id", predefinedColor->m_iId); + jsColor.setProperty("red", color.red()); + jsColor.setProperty("green", color.green()); + jsColor.setProperty("blue", color.blue()); + jsColor.setProperty("alpha", color.alpha()); return jsColor; } -QScriptValue ColorJSProxy::makePredefinedColorsList(QScriptEngine* pScriptEngine) { - int numColors = Color::kPredefinedColorsSet.allColors.length(); - QScriptValue colorList = pScriptEngine->newArray(numColors); +QScriptValue ColorJSProxy::makeHotcueColorPalette( + QScriptEngine* pScriptEngine, UserSettingsPointer pConfig) { + // TODO: make sure we get notified when the palette changes + HotcueColorPaletteSettings colorPaletteSettings(pConfig); + QList colorList = + colorPaletteSettings.getHotcueColorPalette().m_colorList; + int numColors = colorList.length(); + QScriptValue jsColorList = pScriptEngine->newArray(numColors); for (int i = 0; i < numColors; ++i) { - PredefinedColorPointer color = Color::kPredefinedColorsSet.allColors.at(i); - colorList.setProperty(i, jsColorFrom(color)); + QColor color = colorList.at(i); + jsColorList.setProperty(i, colorFromHexCode(color.rgba())); } - return colorList; + return jsColorList; } diff --git a/src/controllers/colorjsproxy.h b/src/controllers/colorjsproxy.h index e6ebeda1d9b..61a801b4617 100644 --- a/src/controllers/colorjsproxy.h +++ b/src/controllers/colorjsproxy.h @@ -5,23 +5,28 @@ #include #include +#include "preferences/usersettings.h" #include "util/color/color.h" -class ColorJSProxy: public QObject { +class ColorJSProxy final : public QObject { Q_OBJECT public: - ColorJSProxy(QScriptEngine* pScriptEngine); + ColorJSProxy(QScriptEngine* pScriptEngine, UserSettingsPointer pConfig); - virtual ~ColorJSProxy(); + ~ColorJSProxy() override; - Q_INVOKABLE QScriptValue predefinedColorFromId(int iId); - Q_INVOKABLE QScriptValue predefinedColorsList(); + Q_INVOKABLE QScriptValue hotcueColorPalette(); + // Return a JS object with the red, green, blue and alpha components + // of a color. The parameter is the hexadecimal representation of the color + // i.e. 0xAARRGGBB + Q_INVOKABLE QScriptValue colorFromHexCode(uint colorCode); private: - QScriptValue jsColorFrom(PredefinedColorPointer predefinedColor); - QScriptValue makePredefinedColorsList(QScriptEngine* pScriptEngine); + QScriptValue makeHotcueColorPalette( + QScriptEngine* pScriptEngine, UserSettingsPointer pConfig); QScriptEngine* m_pScriptEngine; - QScriptValue m_predefinedColorsList; + QScriptValue m_hotcueColorPalette; + UserSettingsPointer m_pConfig; }; #endif /* COLORJSPROXY_H */ diff --git a/src/controllers/controllerengine.cpp b/src/controllers/controllerengine.cpp index 36d9248223f..cfbc5d87b4e 100644 --- a/src/controllers/controllerengine.cpp +++ b/src/controllers/controllerengine.cpp @@ -215,7 +215,7 @@ void ControllerEngine::initializeScriptEngine() { engineGlobalObject.setProperty("midi", m_pEngine->newQObject(m_pController)); } - m_pColorJSProxy = std::make_unique(m_pEngine); + m_pColorJSProxy = std::make_unique(m_pEngine, m_pConfig); engineGlobalObject.setProperty("color", m_pEngine->newQObject(m_pColorJSProxy.get())); m_pBaClass = new ByteArrayClass(m_pEngine); diff --git a/src/test/controllerengine_test.cpp b/src/test/controllerengine_test.cpp index d84f736a6ff..ccf717dbc8c 100644 --- a/src/test/controllerengine_test.cpp +++ b/src/test/controllerengine_test.cpp @@ -1,13 +1,14 @@ -#include #include +#include #include "control/controlobject.h" #include "control/controlpotmeter.h" -#include "preferences/usersettings.h" -#include "controllers/controllerengine.h" #include "controllers/controllerdebug.h" +#include "controllers/controllerengine.h" #include "controllers/softtakeover.h" +#include "preferences/usersettings.h" #include "test/mixxxtest.h" +#include "util/color/hotcuecolorpalette.h" #include "util/memory.h" #include "util/time.h" @@ -17,7 +18,7 @@ class ControllerEngineTest : public MixxxTest { mixxx::Time::setTestMode(true); mixxx::Time::setTestElapsedTime(mixxx::Duration::fromMillis(10)); QThread::currentThread()->setObjectName("Main"); - cEngine = new ControllerEngine(nullptr); + cEngine = new ControllerEngine(nullptr, config()); pScriptEngine = cEngine->m_pEngine; ControllerDebug::enable(); cEngine->setPopups(false); @@ -618,24 +619,24 @@ TEST_F(ControllerEngineTest, connectionExecutesWithCorrectThisObject) { EXPECT_DOUBLE_EQ(1.0, pass->get()); } -TEST_F(ControllerEngineTest, colorProxy) { - QList allColors = Color::kPredefinedColorsSet.allColors; +TEST_F(ControllerEngineTest, colorProxyTestMixxxPalette) { + QList allColors = HotcueColorPalette::mixxxPalette.m_colorList; for (int i = 0; i < allColors.length(); ++i) { - PredefinedColorPointer color = allColors[i]; - qDebug() << "Testing color " << color->m_sName; - QScriptValue jsColor = pScriptEngine->evaluate("color.predefinedColorFromId(" + QString::number(color->m_iId) + ")"); - EXPECT_EQ(jsColor.property("red").toInt32(), color->m_defaultRgba.red()); - EXPECT_EQ(jsColor.property("green").toInt32(), color->m_defaultRgba.green()); - EXPECT_EQ(jsColor.property("blue").toInt32(), color->m_defaultRgba.blue()); - EXPECT_EQ(jsColor.property("alpha").toInt32(), color->m_defaultRgba.alpha()); - EXPECT_EQ(jsColor.property("id").toInt32(), color->m_iId); - - QScriptValue jsColor2 = pScriptEngine->evaluate("color.predefinedColorsList()[" - + QString::number(i) + "]"); - EXPECT_EQ(jsColor2.property("red").toInt32(), color->m_defaultRgba.red()); - EXPECT_EQ(jsColor2.property("green").toInt32(), color->m_defaultRgba.green()); - EXPECT_EQ(jsColor2.property("blue").toInt32(), color->m_defaultRgba.blue()); - EXPECT_EQ(jsColor2.property("alpha").toInt32(), color->m_defaultRgba.alpha()); - EXPECT_EQ(jsColor2.property("id").toInt32(), color->m_iId); + QColor color = allColors[i]; + qDebug() << "Testing color " << color.name(); + QString colorCode = QString::number(color.rgba()); + QScriptValue jsColor = pScriptEngine->evaluate( + "color.colorFromHexCode(" + colorCode + ")"); + EXPECT_EQ(jsColor.property("red").toInt32(), color.red()); + EXPECT_EQ(jsColor.property("green").toInt32(), color.green()); + EXPECT_EQ(jsColor.property("blue").toInt32(), color.blue()); + EXPECT_EQ(jsColor.property("alpha").toInt32(), color.alpha()); + + QScriptValue jsColor2 = pScriptEngine->evaluate( + "color.hotcueColorPalette()[" + QString::number(i) + "]"); + EXPECT_EQ(jsColor2.property("red").toInt32(), color.red()); + EXPECT_EQ(jsColor2.property("green").toInt32(), color.green()); + EXPECT_EQ(jsColor2.property("blue").toInt32(), color.blue()); + EXPECT_EQ(jsColor2.property("alpha").toInt32(), color.alpha()); } } diff --git a/src/util/color/hotcuecolorpalette.cpp b/src/util/color/hotcuecolorpalette.cpp index d0b003ef2d5..6d2358bef3f 100644 --- a/src/util/color/hotcuecolorpalette.cpp +++ b/src/util/color/hotcuecolorpalette.cpp @@ -9,12 +9,11 @@ HotcueColorPalette::HotcueColorPalette(QList colorList) } const HotcueColorPalette HotcueColorPalette::mixxxPalette = - HotcueColorPalette(QList{ - QColor("#c50a08"), + HotcueColorPalette(QList{QColor("#c50a08"), QColor("#32be44"), QColor("#0044ff"), QColor("#f8d200"), QColor("#42d4f4"), QColor("#af00cc"), QColor("#fca6d7"), - QColor("#f2f2ff")}); \ No newline at end of file + QColor("#f2f2ff")}); From e99e1a8ff13cecd9182e2fe2d69adf462027a0ea Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Sun, 3 Nov 2019 10:10:06 +0100 Subject: [PATCH 10/42] Remove PredefinedColor --- build/depends.py | 3 +- src/library/dao/cuedao.cpp | 1 - src/util/color/color.cpp | 2 - src/util/color/color.h | 4 +- src/util/color/predefinedcolor.cpp | 10 --- src/util/color/predefinedcolor.h | 35 --------- src/util/color/predefinedcolorsset.h | 104 --------------------------- 7 files changed, 2 insertions(+), 157 deletions(-) delete mode 100644 src/util/color/predefinedcolor.cpp delete mode 100644 src/util/color/predefinedcolor.h delete mode 100644 src/util/color/predefinedcolorsset.h diff --git a/build/depends.py b/build/depends.py index d7dd0aea47a..d83b4190720 100644 --- a/build/depends.py +++ b/build/depends.py @@ -1254,8 +1254,7 @@ def sources(self, build): "src/util/desktophelper.cpp", "src/util/widgetrendertimer.cpp", "src/util/workerthread.cpp", - "src/util/workerthreadscheduler.cpp", - "src/util/color/predefinedcolor.cpp" + "src/util/workerthreadscheduler.cpp" ] proto_args = { diff --git a/src/library/dao/cuedao.cpp b/src/library/dao/cuedao.cpp index bf095943d76..d1c55d35be5 100644 --- a/src/library/dao/cuedao.cpp +++ b/src/library/dao/cuedao.cpp @@ -11,7 +11,6 @@ #include "library/queryutil.h" #include "util/assert.h" #include "util/performancetimer.h" -#include "util/color/predefinedcolor.h" int CueDAO::cueCount() { qDebug() << "CueDAO::cueCount" << QThread::currentThread() << m_database.connectionName(); diff --git a/src/util/color/color.cpp b/src/util/color/color.cpp index 4627fdf3d9a..92116acb748 100644 --- a/src/util/color/color.cpp +++ b/src/util/color/color.cpp @@ -4,8 +4,6 @@ namespace Color { -const PredefinedColorsSet kPredefinedColorsSet = PredefinedColorsSet(); - // algorithm by http://www.nbdtech.com/Blog/archive/2008/04/27/Calculating-the-Perceived-Brightness-of-a-Color.aspx // NOTE(Swiftb0y): please suggest if I should you use other methods // (like the W3C algorithm) or if this approach is to to performance hungry diff --git a/src/util/color/color.h b/src/util/color/color.h index 13401e05254..e5196a409e7 100644 --- a/src/util/color/color.h +++ b/src/util/color/color.h @@ -1,11 +1,9 @@ #pragma once -#include "util/color/predefinedcolorsset.h" +#include namespace Color { -extern const PredefinedColorsSet kPredefinedColorsSet; - int brightness(int red, int green, int blue); inline int brightness(const QColor& color) { diff --git a/src/util/color/predefinedcolor.cpp b/src/util/color/predefinedcolor.cpp deleted file mode 100644 index 2a5db56e987..00000000000 --- a/src/util/color/predefinedcolor.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#include "util/color/predefinedcolor.h" - -#include "util/color/color.h" - -PredefinedColor::PredefinedColor(QColor defaultRgba, QString sName, QString sDisplayName, int iId) - : m_defaultRgba(defaultRgba), - m_sName(sName), - m_sDisplayName(sDisplayName), - m_iId(iId) { -} diff --git a/src/util/color/predefinedcolor.h b/src/util/color/predefinedcolor.h deleted file mode 100644 index 4e5745c0883..00000000000 --- a/src/util/color/predefinedcolor.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include - -#include "util/memory.h" - -// The PredefinedColor class is used to represent a Mixxx identificable color. -// A PredefinedColor can be uniquely identified with its m_iId property. -// -// PredefinedColors have a default Rgba value. A PredefinedColorsMap can provide with an alternative -// Rgba value for each PredefinedColor. Thus, a PredefinedColorsMap defines a particular way to render -// the PredefinedColors. -class PredefinedColor final { - public: - PredefinedColor(QColor defaultRgba, QString sName, QString sDisplayName, int iId); - - inline bool operator==(const PredefinedColor& other) const { - return m_iId == other.m_iId; - } - - inline bool operator!=(const PredefinedColor& other) const { - return m_iId != other.m_iId; - } - - // The QColor that is used by default to render this PredefinedColor. - const QColor m_defaultRgba; - // The name of the color used programmatically, e.g. on skin files. - const QString m_sName; - // The name of the color used on UI. - const QString m_sDisplayName; - // An Id uniquely identifying this predefined color. - // This value is used to identify a color on the DB and control objects. - const int m_iId; -}; -typedef std::shared_ptr PredefinedColorPointer; diff --git a/src/util/color/predefinedcolorsset.h b/src/util/color/predefinedcolorsset.h deleted file mode 100644 index b68d6b89c1b..00000000000 --- a/src/util/color/predefinedcolorsset.h +++ /dev/null @@ -1,104 +0,0 @@ -#pragma once - -#include -#include - -#include "util/color/predefinedcolor.h" - -// This class defines a set of predefined colors and provides some handy functions to work with them. -// A single global instance is create in the Color namespace. -// This class is thread-safe because all its methods and public properties are const. -class PredefinedColorsSet final { - public: - const PredefinedColorPointer noColor = std::make_shared( - QColor(), - QLatin1String("No Color"), - QObject::tr("No Color"), - 0); - const PredefinedColorPointer red = std::make_shared( - QColor("#c50a08"), - QLatin1String("Red"), - QObject::tr("Red"), - 1); - const PredefinedColorPointer green = std::make_shared( - QColor("#32be44"), - QLatin1String("Green"), - QObject::tr("Green"), - 2); - const PredefinedColorPointer blue = std::make_shared( - QColor("#0044ff"), - QLatin1String("Blue"), - QObject::tr("Blue"), - 3); - const PredefinedColorPointer yellow = std::make_shared( - QColor("#f8d200"), - QLatin1String("Yellow"), - QObject::tr("Yellow"), - 4); - const PredefinedColorPointer cyan = std::make_shared( - QColor("#42d4f4"), - QLatin1String("Celeste"), - QObject::tr("Celeste"), - 5); - const PredefinedColorPointer magenta = std::make_shared( - QColor("#af00cc"), - QLatin1String("Purple"), - QObject::tr("Purple"), - 6); - const PredefinedColorPointer pink = std::make_shared( - QColor("#fca6d7"), - QLatin1String("Pink"), - QObject::tr("Pink"), - 7); - const PredefinedColorPointer white = std::make_shared( - QColor("#f2f2ff"), - QLatin1String("White"), - QObject::tr("White"), - 8); - - // The list of the predefined colors. - const QList allColors{ - noColor, - red, - green, - yellow, - blue, - cyan, - magenta, - pink, - white, - }; - - // Returns the position of a PredefinedColor in the allColors list. - int predefinedColorIndex(PredefinedColorPointer searchedColor) const { - for (int position = 0; position < allColors.count(); ++position) { - PredefinedColorPointer color(allColors.at(position)); - if (*color == *searchedColor) { - return position; - } - } - return 0; - }; - - // Return a predefined color from its name. - // Return noColor if there's no color with such name. - PredefinedColorPointer predefinedColorFromName(QString name) const { - for (PredefinedColorPointer color : allColors) { - if (color->m_sName == name) { - return color; - } - } - return noColor; - }; - - // Return a predefined color from its id. - // Return noColor if there's no color with iId. - PredefinedColorPointer predefinedColorFromId(int iId) const { - for (PredefinedColorPointer color : allColors) { - if (color->m_iId == iId) { - return color; - } - } - return noColor; - }; -}; From 223911e9e3eebb99cc2509f9a2cd2cabf708ea2c Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Sun, 3 Nov 2019 10:16:50 +0100 Subject: [PATCH 11/42] Update midi-components library and change cue color CO name --- res/controllers/Roland_DJ-505-scripts.js | 12 ++++++------ res/controllers/midi-components-0.0.js | 16 ++++++++-------- res/skins/Deere/hotcue_button.xml | 2 +- res/skins/LateNight/button_hotcue.xml | 2 +- res/skins/Shade/deck_hotcue_button.xml | 2 +- res/skins/Tango/button_hotcue_deck.xml | 2 +- src/engine/controls/cuecontrol.cpp | 2 +- 7 files changed, 19 insertions(+), 19 deletions(-) diff --git a/res/controllers/Roland_DJ-505-scripts.js b/res/controllers/Roland_DJ-505-scripts.js index 307df7e645f..1423d21faa6 100644 --- a/res/controllers/Roland_DJ-505-scripts.js +++ b/res/controllers/Roland_DJ-505-scripts.js @@ -1476,7 +1476,7 @@ DJ505.PitchPlayMode = function (deck, offset) { this.number = n + 1; this.on = this.color + DJ505.PadColor.DIM_MODIFIER; this.colors = pitchplayColors; - this.colorIdKey = 'hotcue_' + this.number + '_color_id'; + this.colorKey = 'hotcue_' + this.number + '_color'; components.Button.call(this); }; this.PerformancePad.prototype = new components.Button({ @@ -1538,8 +1538,8 @@ DJ505.PitchPlayMode = function (deck, offset) { this.outKey = "hotcue_" + this.number + "_enabled"; this.output = function (value, group, control) { var outval = this.outValueScale(value); - if (this.colorIdKey !== undefined && outval !== this.off) { - this.outputColor(engine.getValue(this.group, this.colorIdKey)); + if (this.colorKey !== undefined && outval !== this.off) { + this.outputColor(engine.getValue(this.group, this.colorKey)); } else { this.send(DJ505.PadColor.OFF); } @@ -1549,13 +1549,13 @@ DJ505.PitchPlayMode = function (deck, offset) { var previous_cuepoint = this.mode.cuepoint; this.mode.cuepoint = this.number; this.mode.pads[previous_cuepoint - 1].trigger(); - this.outputColor(engine.getValue(this.group, this.colorIdKey)); + this.outputColor(engine.getValue(this.group, this.colorKey)); } }; this.connect = function() { components.Button.prototype.connect.call(this); // call parent connect - if (undefined !== this.group && this.colorIdKey !== undefined) { - this.connections[1] = engine.makeConnection(this.group, this.colorIdKey, function (id) { + if (undefined !== this.group && this.colorKey !== undefined) { + this.connections[1] = engine.makeConnection(this.group, this.colorKey, function (id) { if (engine.getValue(this.group, this.outKey)) { this.outputColor(id); } diff --git a/res/controllers/midi-components-0.0.js b/res/controllers/midi-components-0.0.js index 7bb5c009578..1893768c2a6 100644 --- a/res/controllers/midi-components-0.0.js +++ b/res/controllers/midi-components-0.0.js @@ -295,9 +295,9 @@ return; } if (options.colors !== undefined || options.sendRGB !== undefined) { - this.colorIdKey = 'hotcue_' + options.number + '_color_id'; + this.colorKey = 'hotcue_' + options.number + '_color'; if (options.colors === undefined) { - options.colors = color.predefinedColorsList(); + options.colors = color.hotcueColorPalette(); } } this.number = options.number; @@ -312,8 +312,8 @@ this.inKey = 'hotcue_' + this.number + '_clear'; }, getColor: function() { - if (this.colorIdKey !== undefined) { - return color.predefinedColorFromId(engine.getValue(this.group,this.colorIdKey)); + if (this.colorKey !== undefined) { + return color.colorFromHexCode(engine.getValue(this.group,this.colorKey)); } else { return null; } @@ -324,8 +324,8 @@ // and there is no hotcueColor for turning the LED // off. So the `send()` function is responsible for turning the // actual LED off. - if (this.colorIdKey !== undefined && outval !== this.off) { - this.outputColor(engine.getValue(this.group, this.colorIdKey)); + if (this.colorKey !== undefined && outval !== this.off) { + this.outputColor(engine.getValue(this.group, this.colorKey)); } else { this.send(outval); } @@ -348,8 +348,8 @@ }, connect: function() { Button.prototype.connect.call(this); // call parent connect - if (undefined !== this.group && this.colorIdKey !== undefined) { - this.connections[1] = engine.makeConnection(this.group, this.colorIdKey, function (id) { + if (undefined !== this.group && this.colorKey !== undefined) { + this.connections[1] = engine.makeConnection(this.group, this.colorKey, function (id) { if (engine.getValue(this.group,this.outKey)) { this.outputColor(id); } diff --git a/res/skins/Deere/hotcue_button.xml b/res/skins/Deere/hotcue_button.xml index f5e47776226..118987b10d6 100644 --- a/res/skins/Deere/hotcue_button.xml +++ b/res/skins/Deere/hotcue_button.xml @@ -46,7 +46,7 @@ false - ,hotcue__color_id + ,hotcue__color highlight diff --git a/res/skins/LateNight/button_hotcue.xml b/res/skins/LateNight/button_hotcue.xml index 481b1ab72d7..92110d69b0e 100644 --- a/res/skins/LateNight/button_hotcue.xml +++ b/res/skins/LateNight/button_hotcue.xml @@ -28,7 +28,7 @@ false - ,hotcue__color_id + ,hotcue__color highlight diff --git a/res/skins/Shade/deck_hotcue_button.xml b/res/skins/Shade/deck_hotcue_button.xml index 69c10a0fac1..dbab68078e4 100644 --- a/res/skins/Shade/deck_hotcue_button.xml +++ b/res/skins/Shade/deck_hotcue_button.xml @@ -33,7 +33,7 @@ false - [Channel],hotcue__color_id + [Channel],hotcue__color highlight diff --git a/res/skins/Tango/button_hotcue_deck.xml b/res/skins/Tango/button_hotcue_deck.xml index bb1009dbf42..c858ef7640c 100644 --- a/res/skins/Tango/button_hotcue_deck.xml +++ b/res/skins/Tango/button_hotcue_deck.xml @@ -38,7 +38,7 @@ Variables: false - ,hotcue__color_id + ,hotcue__color highlight diff --git a/src/engine/controls/cuecontrol.cpp b/src/engine/controls/cuecontrol.cpp index e0d00676bc2..44e1207ce02 100644 --- a/src/engine/controls/cuecontrol.cpp +++ b/src/engine/controls/cuecontrol.cpp @@ -1705,7 +1705,7 @@ HotcueControl::HotcueControl(QString group, int i) m_hotcueEnabled->setReadOnly(); // The id of the predefined color assigned to this color. - m_hotcueColor = new ControlObject(keyForControl(i, "color_id")); + m_hotcueColor = new ControlObject(keyForControl(i, "color")); connect(m_hotcueColor, &ControlObject::valueChanged, this, From 150cddfef09232b379a72e24907b6979ac58d615 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Sun, 3 Nov 2019 11:04:05 +0100 Subject: [PATCH 12/42] Avoid constructing HotcueColorPaletteSettings every time its used --- src/controllers/colorjsproxy.cpp | 14 ++++----- src/controllers/colorjsproxy.h | 11 +++---- src/controllers/controllerengine.cpp | 3 +- src/engine/controls/cuecontrol.cpp | 30 ++++++++++---------- src/engine/controls/cuecontrol.h | 4 ++- src/preferences/hotcuecolorpalettesettings.h | 2 +- 6 files changed, 34 insertions(+), 30 deletions(-) diff --git a/src/controllers/colorjsproxy.cpp b/src/controllers/colorjsproxy.cpp index 8b2f2fd644e..bbce727f5c0 100644 --- a/src/controllers/colorjsproxy.cpp +++ b/src/controllers/colorjsproxy.cpp @@ -1,11 +1,12 @@ #include "controllers/colorjsproxy.h" #include "preferences/hotcuecolorpalettesettings.h" -ColorJSProxy::ColorJSProxy( - QScriptEngine* pScriptEngine, UserSettingsPointer pConfig) +ColorJSProxy::ColorJSProxy(QScriptEngine* pScriptEngine, + HotcueColorPaletteSettings colorPaletteSettings) : m_pScriptEngine(pScriptEngine), - m_hotcueColorPalette(makeHotcueColorPalette(pScriptEngine, pConfig)), - m_pConfig(pConfig) { + m_hotcueColorPalette( + makeHotcueColorPalette(pScriptEngine, colorPaletteSettings)), + m_colorPaletteSettings(colorPaletteSettings) { } ColorJSProxy::~ColorJSProxy() = default; @@ -24,10 +25,9 @@ QScriptValue ColorJSProxy::colorFromHexCode(uint colorCode) { return jsColor; } -QScriptValue ColorJSProxy::makeHotcueColorPalette( - QScriptEngine* pScriptEngine, UserSettingsPointer pConfig) { +QScriptValue ColorJSProxy::makeHotcueColorPalette(QScriptEngine* pScriptEngine, + HotcueColorPaletteSettings colorPaletteSettings) { // TODO: make sure we get notified when the palette changes - HotcueColorPaletteSettings colorPaletteSettings(pConfig); QList colorList = colorPaletteSettings.getHotcueColorPalette().m_colorList; int numColors = colorList.length(); diff --git a/src/controllers/colorjsproxy.h b/src/controllers/colorjsproxy.h index 61a801b4617..abeddcf1684 100644 --- a/src/controllers/colorjsproxy.h +++ b/src/controllers/colorjsproxy.h @@ -5,13 +5,14 @@ #include #include -#include "preferences/usersettings.h" +#include "preferences/hotcuecolorpalettesettings.h" #include "util/color/color.h" class ColorJSProxy final : public QObject { Q_OBJECT public: - ColorJSProxy(QScriptEngine* pScriptEngine, UserSettingsPointer pConfig); + ColorJSProxy(QScriptEngine* pScriptEngine, + HotcueColorPaletteSettings colorPaletteSettings); ~ColorJSProxy() override; @@ -22,11 +23,11 @@ class ColorJSProxy final : public QObject { Q_INVOKABLE QScriptValue colorFromHexCode(uint colorCode); private: - QScriptValue makeHotcueColorPalette( - QScriptEngine* pScriptEngine, UserSettingsPointer pConfig); + QScriptValue makeHotcueColorPalette(QScriptEngine* pScriptEngine, + HotcueColorPaletteSettings colorPaletteSettings); QScriptEngine* m_pScriptEngine; QScriptValue m_hotcueColorPalette; - UserSettingsPointer m_pConfig; + HotcueColorPaletteSettings m_colorPaletteSettings; }; #endif /* COLORJSPROXY_H */ diff --git a/src/controllers/controllerengine.cpp b/src/controllers/controllerengine.cpp index cfbc5d87b4e..9bee7c4209e 100644 --- a/src/controllers/controllerengine.cpp +++ b/src/controllers/controllerengine.cpp @@ -215,7 +215,8 @@ void ControllerEngine::initializeScriptEngine() { engineGlobalObject.setProperty("midi", m_pEngine->newQObject(m_pController)); } - m_pColorJSProxy = std::make_unique(m_pEngine, m_pConfig); + m_pColorJSProxy = std::make_unique( + m_pEngine, HotcueColorPaletteSettings(m_pConfig)); engineGlobalObject.setProperty("color", m_pEngine->newQObject(m_pColorJSProxy.get())); m_pBaClass = new ByteArrayClass(m_pEngine); diff --git a/src/engine/controls/cuecontrol.cpp b/src/engine/controls/cuecontrol.cpp index 44e1207ce02..589f4494bf2 100644 --- a/src/engine/controls/cuecontrol.cpp +++ b/src/engine/controls/cuecontrol.cpp @@ -24,19 +24,19 @@ static const double CUE_MODE_NUMARK = 3.0; static const double CUE_MODE_MIXXX_NO_BLINK = 4.0; static const double CUE_MODE_CUP = 5.0; -CueControl::CueControl(QString group, - UserSettingsPointer pConfig) : - EngineControl(group, pConfig), - m_bPreviewing(false), - // m_pPlay->toBoo() -> engine play state - // m_pPlay->set(1.0) -> emulate play button press - m_pPlay(ControlObject::getControl(ConfigKey(group, "play"))), - m_pStopButton(ControlObject::getControl(ConfigKey(group, "stop"))), - m_iCurrentlyPreviewingHotcues(0), - m_bypassCueSetByPlay(false), - m_iNumHotCues(NUM_HOT_CUES), - m_pLoadedTrack(), - m_mutex(QMutex::Recursive) { +CueControl::CueControl(QString group, UserSettingsPointer pConfig) + : EngineControl(group, pConfig), + m_colorPaletteSettings(HotcueColorPaletteSettings(pConfig)), + m_bPreviewing(false), + // m_pPlay->toBoo() -> engine play state + // m_pPlay->set(1.0) -> emulate play button press + m_pPlay(ControlObject::getControl(ConfigKey(group, "play"))), + m_pStopButton(ControlObject::getControl(ConfigKey(group, "stop"))), + m_iCurrentlyPreviewingHotcues(0), + m_bypassCueSetByPlay(false), + m_iNumHotCues(NUM_HOT_CUES), + m_pLoadedTrack(), + m_mutex(QMutex::Recursive) { // To silence a compiler warning about CUE_MODE_PIONEER. Q_UNUSED(CUE_MODE_PIONEER); createControls(); @@ -582,8 +582,8 @@ void CueControl::hotcueSet(HotcueControl* pControl, double v) { ConfigKey autoHotcueColorsKey("[Controls]", "auto_hotcue_colors"); if (getConfig()->getValue(autoHotcueColorsKey, false)) { - HotcueColorPaletteSettings colorPaletteSettings(getConfig()); - auto hotcueColorPalette = colorPaletteSettings.getHotcueColorPalette(); + auto hotcueColorPalette = + m_colorPaletteSettings.getHotcueColorPalette(); auto colors = hotcueColorPalette.m_colorList; pCue->setColor(colors.at((hotcue % (colors.count() - 1)) + 1)); }; diff --git a/src/engine/controls/cuecontrol.h b/src/engine/controls/cuecontrol.h index 0fcbe048ed5..06b53d3f97c 100644 --- a/src/engine/controls/cuecontrol.h +++ b/src/engine/controls/cuecontrol.h @@ -7,9 +7,10 @@ #include #include +#include "control/controlproxy.h" #include "engine/controls/enginecontrol.h" +#include "preferences/hotcuecolorpalettesettings.h" #include "preferences/usersettings.h" -#include "control/controlproxy.h" #include "track/track.h" #define NUM_HOT_CUES 37 @@ -194,6 +195,7 @@ class CueControl : public EngineControl { double quantizeCurrentPosition(QuantizeMode mode); TrackAt getTrackAt() const; + HotcueColorPaletteSettings m_colorPaletteSettings; bool m_bPreviewing; ControlObject* m_pPlay; ControlObject* m_pStopButton; diff --git a/src/preferences/hotcuecolorpalettesettings.h b/src/preferences/hotcuecolorpalettesettings.h index ad28c5ec807..fb1891e32c3 100644 --- a/src/preferences/hotcuecolorpalettesettings.h +++ b/src/preferences/hotcuecolorpalettesettings.h @@ -5,7 +5,7 @@ class HotcueColorPaletteSettings { public: - HotcueColorPaletteSettings(UserSettingsPointer pConfig) + explicit HotcueColorPaletteSettings(UserSettingsPointer pConfig) : m_pConfig(pConfig) { } From 44ffe5dd7beabb7131d6a95081d5bd92336c97bf Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Mon, 4 Nov 2019 09:14:37 +0100 Subject: [PATCH 13/42] Change test names --- src/test/colorconfig_test.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/test/colorconfig_test.cpp b/src/test/colorconfig_test.cpp index fc95fec6573..e3fd79c72c2 100644 --- a/src/test/colorconfig_test.cpp +++ b/src/test/colorconfig_test.cpp @@ -8,7 +8,7 @@ class ColorConfigTest : public MixxxTest {}; -TEST_F(ColorConfigTest, TestSavingColorWithoutAlpha) { +TEST_F(ColorConfigTest, SavingColorWithoutAlpha) { ConfigKey key("[Color]", "color"); QColor originalColor("#FF9900"); config()->setValue(key, originalColor); @@ -17,7 +17,7 @@ TEST_F(ColorConfigTest, TestSavingColorWithoutAlpha) { ASSERT_EQ(originalColor, colorFromConfig); } -TEST_F(ColorConfigTest, TestSavingColorWithAlpha) { +TEST_F(ColorConfigTest, SavingColorWithAlpha) { ConfigKey key("[Color]", "color"); QColor originalColor("#66FF9900"); config()->setValue(key, originalColor); @@ -26,14 +26,14 @@ TEST_F(ColorConfigTest, TestSavingColorWithAlpha) { ASSERT_EQ(originalColor, colorFromConfig); } -TEST_F(ColorConfigTest, TestDefaultColorWhenNoStoredColor) { +TEST_F(ColorConfigTest, GetDefaultColorWhenNoStoredColor) { ConfigKey key("[Color]", "color"); QColor defaultColor("#66FF9900"); QColor colorFromConfig = config()->getValue(key, defaultColor); ASSERT_EQ(defaultColor, colorFromConfig); } -TEST_F(ColorConfigTest, TestSaveColorPalette) { +TEST_F(ColorConfigTest, SaveColorPalette) { HotcueColorPaletteSettings colorPaletteSettings(config()); HotcueColorPalette originalColorPalette(QList{ QColor("#66FF9900"), @@ -49,7 +49,7 @@ TEST_F(ColorConfigTest, TestSaveColorPalette) { ASSERT_EQ(originalColorPalette, colorPaletteFromConfig); } -TEST_F(ColorConfigTest, TestReplaceColorPalette) { +TEST_F(ColorConfigTest, ReplaceColorPalette) { HotcueColorPaletteSettings colorPaletteSettings(config()); HotcueColorPalette colorPalette1(QList{ QColor("#66FF9900"), @@ -71,9 +71,9 @@ TEST_F(ColorConfigTest, TestReplaceColorPalette) { ASSERT_EQ(colorPalette2, colorPaletteFromConfig); } -TEST_F(ColorConfigTest, TestDefaultColorPalette) { +TEST_F(ColorConfigTest, DefaultColorPalette) { HotcueColorPaletteSettings colorPaletteSettings(config()); HotcueColorPalette colorPaletteFromConfig = colorPaletteSettings.getHotcueColorPalette(); ASSERT_EQ(HotcueColorPalette::mixxxPalette, colorPaletteFromConfig); -} \ No newline at end of file +} From 89927fe246e63a651636c195b0b22ce5a782e16c Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Mon, 4 Nov 2019 09:30:01 +0100 Subject: [PATCH 14/42] Reorder constructor parameters --- src/library/dlgtrackinfo.cpp | 2 +- src/library/dlgtrackinfo.h | 2 +- src/widget/wtracktableview.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/library/dlgtrackinfo.cpp b/src/library/dlgtrackinfo.cpp index 9f37dc0399a..ffdf9f4ebd6 100644 --- a/src/library/dlgtrackinfo.cpp +++ b/src/library/dlgtrackinfo.cpp @@ -23,7 +23,7 @@ const int kMinBpm = 30; // Maximum allowed interval between beats (calculated from kMinBpm). const mixxx::Duration kMaxInterval = mixxx::Duration::fromMillis(1000.0 * (60.0 / kMinBpm)); -DlgTrackInfo::DlgTrackInfo(UserSettingsPointer pConfig, QWidget* parent) +DlgTrackInfo::DlgTrackInfo(QWidget* parent, UserSettingsPointer pConfig) : QDialog(parent), m_pTapFilter(new TapFilter(this, kFilterLength, kMaxInterval)), m_dLastTapedBpm(-1.), diff --git a/src/library/dlgtrackinfo.h b/src/library/dlgtrackinfo.h index c343d88a48c..16bfd450e97 100644 --- a/src/library/dlgtrackinfo.h +++ b/src/library/dlgtrackinfo.h @@ -18,7 +18,7 @@ class DlgTrackInfo : public QDialog, public Ui::DlgTrackInfo { Q_OBJECT public: - DlgTrackInfo(UserSettingsPointer pConfig, QWidget* parent); + DlgTrackInfo(QWidget* parent, UserSettingsPointer pConfig); virtual ~DlgTrackInfo(); public slots: diff --git a/src/widget/wtracktableview.cpp b/src/widget/wtracktableview.cpp index aa916ce322a..4084f4b61c6 100644 --- a/src/widget/wtracktableview.cpp +++ b/src/widget/wtracktableview.cpp @@ -754,7 +754,7 @@ void WTrackTableView::showTrackInfo(QModelIndex index) { if (m_pTrackInfo.isNull()) { // Give a NULL parent because otherwise it inherits our style which can // make it unreadable. Bug #673411 - m_pTrackInfo.reset(new DlgTrackInfo(m_pConfig, nullptr)); + m_pTrackInfo.reset(new DlgTrackInfo(nullptr, m_pConfig)); connect(m_pTrackInfo.data(), SIGNAL(next()), this, SLOT(slotNextTrackInfo())); From 91067ebed80ac70324978e527676a3bcbef5cf84 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Mon, 4 Nov 2019 09:34:13 +0100 Subject: [PATCH 15/42] Rename member variable --- src/controllers/colorjsproxy.cpp | 4 ++-- src/controllers/colorjsproxy.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/controllers/colorjsproxy.cpp b/src/controllers/colorjsproxy.cpp index bbce727f5c0..f7ef331f704 100644 --- a/src/controllers/colorjsproxy.cpp +++ b/src/controllers/colorjsproxy.cpp @@ -4,7 +4,7 @@ ColorJSProxy::ColorJSProxy(QScriptEngine* pScriptEngine, HotcueColorPaletteSettings colorPaletteSettings) : m_pScriptEngine(pScriptEngine), - m_hotcueColorPalette( + m_JsHotcueColorPalette( makeHotcueColorPalette(pScriptEngine, colorPaletteSettings)), m_colorPaletteSettings(colorPaletteSettings) { } @@ -12,7 +12,7 @@ ColorJSProxy::ColorJSProxy(QScriptEngine* pScriptEngine, ColorJSProxy::~ColorJSProxy() = default; Q_INVOKABLE QScriptValue ColorJSProxy::hotcueColorPalette() { - return m_hotcueColorPalette; + return m_JsHotcueColorPalette; } QScriptValue ColorJSProxy::colorFromHexCode(uint colorCode) { diff --git a/src/controllers/colorjsproxy.h b/src/controllers/colorjsproxy.h index abeddcf1684..dda4d693798 100644 --- a/src/controllers/colorjsproxy.h +++ b/src/controllers/colorjsproxy.h @@ -26,7 +26,7 @@ class ColorJSProxy final : public QObject { QScriptValue makeHotcueColorPalette(QScriptEngine* pScriptEngine, HotcueColorPaletteSettings colorPaletteSettings); QScriptEngine* m_pScriptEngine; - QScriptValue m_hotcueColorPalette; + QScriptValue m_JsHotcueColorPalette; HotcueColorPaletteSettings m_colorPaletteSettings; }; From 3233dc33213db2d0c2325a3f0103077b1fb46f75 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Mon, 4 Nov 2019 09:50:15 +0100 Subject: [PATCH 16/42] Rename some color related classes --- build/depends.py | 2 +- src/library/dlgtrackinfo.cpp | 2 +- .../hotcuecolorpalettesettings.cpp | 8 +++---- src/preferences/hotcuecolorpalettesettings.h | 6 ++--- src/test/colorconfig_test.cpp | 16 +++++++------- src/test/controllerengine_test.cpp | 4 ++-- ...otcuecolorpalette.cpp => colorpalette.cpp} | 9 ++++---- src/util/color/colorpalette.h | 21 ++++++++++++++++++ src/util/color/hotcuecolorpalette.h | 22 ------------------- src/widget/colormenu.cpp | 4 ++-- src/widget/colormenu.h | 4 ++-- src/widget/cuemenu.h | 2 +- 12 files changed, 49 insertions(+), 51 deletions(-) rename src/util/color/{hotcuecolorpalette.cpp => colorpalette.cpp} (55%) create mode 100644 src/util/color/colorpalette.h delete mode 100644 src/util/color/hotcuecolorpalette.h diff --git a/build/depends.py b/build/depends.py index d83b4190720..d331e73eb46 100644 --- a/build/depends.py +++ b/build/depends.py @@ -1227,7 +1227,7 @@ def sources(self, build): "src/util/movinginterquartilemean.cpp", "src/util/console.cpp", "src/util/color/color.cpp", - "src/util/color/hotcuecolorpalette.cpp", + "src/util/color/colorpalette.cpp", "src/util/db/dbconnection.cpp", "src/util/db/dbconnectionpool.cpp", "src/util/db/dbconnectionpooler.cpp", diff --git a/src/library/dlgtrackinfo.cpp b/src/library/dlgtrackinfo.cpp index ffdf9f4ebd6..3b6cb9346c3 100644 --- a/src/library/dlgtrackinfo.cpp +++ b/src/library/dlgtrackinfo.cpp @@ -12,7 +12,7 @@ #include "track/cue.h" #include "track/keyfactory.h" #include "track/keyutils.h" -#include "util/color/hotcuecolorpalette.h" +#include "util/color/colorpalette.h" #include "util/compatibility.h" #include "util/desktophelper.h" #include "util/duration.h" diff --git a/src/preferences/hotcuecolorpalettesettings.cpp b/src/preferences/hotcuecolorpalettesettings.cpp index b6a745606c0..1da25a868f2 100644 --- a/src/preferences/hotcuecolorpalettesettings.cpp +++ b/src/preferences/hotcuecolorpalettesettings.cpp @@ -2,7 +2,7 @@ const QString HotcueColorPaletteSettings::sGroup = "[HotcueColorPalette]"; -HotcueColorPalette HotcueColorPaletteSettings::getHotcueColorPalette() const { +ColorPalette HotcueColorPaletteSettings::getHotcueColorPalette() const { QList colorList; for (const ConfigKey& key : m_pConfig->getKeysWithGroup(sGroup)) { QColor color = m_pConfig->getValue(key); @@ -13,14 +13,14 @@ HotcueColorPalette HotcueColorPaletteSettings::getHotcueColorPalette() const { // If no palette is defined in the settings, we use the default one. if (colorList.isEmpty()) { - return HotcueColorPalette::mixxxPalette; + return ColorPalette::mixxxHotcuesPalette; } return colorList; } void HotcueColorPaletteSettings::setHotcueColorPalette( - const HotcueColorPalette& colorPalette) { + const ColorPalette& colorPalette) { removePalette(); for (int index = 0; index < colorPalette.m_colorList.count(); ++index) { @@ -33,4 +33,4 @@ void HotcueColorPaletteSettings::removePalette() { for (const ConfigKey& key : m_pConfig->getKeysWithGroup(sGroup)) { m_pConfig->remove(key); } -} \ No newline at end of file +} diff --git a/src/preferences/hotcuecolorpalettesettings.h b/src/preferences/hotcuecolorpalettesettings.h index fb1891e32c3..9ed134146a2 100644 --- a/src/preferences/hotcuecolorpalettesettings.h +++ b/src/preferences/hotcuecolorpalettesettings.h @@ -1,7 +1,7 @@ #pragma once #include "preferences/usersettings.h" -#include "util/color/hotcuecolorpalette.h" +#include "util/color/colorpalette.h" class HotcueColorPaletteSettings { public: @@ -9,9 +9,9 @@ class HotcueColorPaletteSettings { : m_pConfig(pConfig) { } - HotcueColorPalette getHotcueColorPalette() const; + ColorPalette getHotcueColorPalette() const; - void setHotcueColorPalette(const HotcueColorPalette& colorPalette); + void setHotcueColorPalette(const ColorPalette& colorPalette); private: static const QString sGroup; diff --git a/src/test/colorconfig_test.cpp b/src/test/colorconfig_test.cpp index e3fd79c72c2..477bab40a52 100644 --- a/src/test/colorconfig_test.cpp +++ b/src/test/colorconfig_test.cpp @@ -4,7 +4,7 @@ #include "test/mixxxtest.h" #include -#include +#include class ColorConfigTest : public MixxxTest {}; @@ -35,7 +35,7 @@ TEST_F(ColorConfigTest, GetDefaultColorWhenNoStoredColor) { TEST_F(ColorConfigTest, SaveColorPalette) { HotcueColorPaletteSettings colorPaletteSettings(config()); - HotcueColorPalette originalColorPalette(QList{ + ColorPalette originalColorPalette(QList{ QColor("#66FF9900"), QColor("#FF9900"), QColor("#00000000"), @@ -44,20 +44,20 @@ TEST_F(ColorConfigTest, SaveColorPalette) { ConfigKey key("[ColorPalette]", "colorPalette"); colorPaletteSettings.setHotcueColorPalette(originalColorPalette); saveAndReloadConfig(); - HotcueColorPalette colorPaletteFromConfig = + ColorPalette colorPaletteFromConfig = colorPaletteSettings.getHotcueColorPalette(); ASSERT_EQ(originalColorPalette, colorPaletteFromConfig); } TEST_F(ColorConfigTest, ReplaceColorPalette) { HotcueColorPaletteSettings colorPaletteSettings(config()); - HotcueColorPalette colorPalette1(QList{ + ColorPalette colorPalette1(QList{ QColor("#66FF9900"), QColor("#FF9900"), QColor("#00000000"), QColor("#FFFFFF"), }); - HotcueColorPalette colorPalette2(QList{ + ColorPalette colorPalette2(QList{ QColor("#0000FF"), QColor("#FF0000"), }); @@ -66,14 +66,14 @@ TEST_F(ColorConfigTest, ReplaceColorPalette) { saveAndReloadConfig(); colorPaletteSettings.setHotcueColorPalette(colorPalette2); saveAndReloadConfig(); - HotcueColorPalette colorPaletteFromConfig = + ColorPalette colorPaletteFromConfig = colorPaletteSettings.getHotcueColorPalette(); ASSERT_EQ(colorPalette2, colorPaletteFromConfig); } TEST_F(ColorConfigTest, DefaultColorPalette) { HotcueColorPaletteSettings colorPaletteSettings(config()); - HotcueColorPalette colorPaletteFromConfig = + ColorPalette colorPaletteFromConfig = colorPaletteSettings.getHotcueColorPalette(); - ASSERT_EQ(HotcueColorPalette::mixxxPalette, colorPaletteFromConfig); + ASSERT_EQ(ColorPalette::mixxxHotcuesPalette, colorPaletteFromConfig); } diff --git a/src/test/controllerengine_test.cpp b/src/test/controllerengine_test.cpp index ccf717dbc8c..ade6169ca5a 100644 --- a/src/test/controllerengine_test.cpp +++ b/src/test/controllerengine_test.cpp @@ -8,7 +8,7 @@ #include "controllers/softtakeover.h" #include "preferences/usersettings.h" #include "test/mixxxtest.h" -#include "util/color/hotcuecolorpalette.h" +#include "util/color/colorpalette.h" #include "util/memory.h" #include "util/time.h" @@ -620,7 +620,7 @@ TEST_F(ControllerEngineTest, connectionExecutesWithCorrectThisObject) { } TEST_F(ControllerEngineTest, colorProxyTestMixxxPalette) { - QList allColors = HotcueColorPalette::mixxxPalette.m_colorList; + QList allColors = ColorPalette::mixxxHotcuesPalette.m_colorList; for (int i = 0; i < allColors.length(); ++i) { QColor color = allColors[i]; qDebug() << "Testing color " << color.name(); diff --git a/src/util/color/hotcuecolorpalette.cpp b/src/util/color/colorpalette.cpp similarity index 55% rename from src/util/color/hotcuecolorpalette.cpp rename to src/util/color/colorpalette.cpp index 6d2358bef3f..7334763b6f1 100644 --- a/src/util/color/hotcuecolorpalette.cpp +++ b/src/util/color/colorpalette.cpp @@ -2,14 +2,13 @@ // Created by Ferran Pujol Camins on 27/10/2019. // -#include "hotcuecolorpalette.h" +#include "colorpalette.h" -HotcueColorPalette::HotcueColorPalette(QList colorList) - : m_colorList(colorList) { +ColorPalette::ColorPalette(QList colorList) : m_colorList(colorList) { } -const HotcueColorPalette HotcueColorPalette::mixxxPalette = - HotcueColorPalette(QList{QColor("#c50a08"), +const ColorPalette ColorPalette::mixxxHotcuesPalette = + ColorPalette(QList{QColor("#c50a08"), QColor("#32be44"), QColor("#0044ff"), QColor("#f8d200"), diff --git a/src/util/color/colorpalette.h b/src/util/color/colorpalette.h new file mode 100644 index 00000000000..eb276a2fecd --- /dev/null +++ b/src/util/color/colorpalette.h @@ -0,0 +1,21 @@ +// +// Created by Ferran Pujol Camins on 27/10/2019. +// + +#pragma once + +#include +#include "QList" + +class ColorPalette { + public: + ColorPalette(QList); + + static const ColorPalette mixxxHotcuesPalette; + + QList m_colorList; +}; + +inline bool operator==(const ColorPalette& lhs, const ColorPalette& rhs) { + return lhs.m_colorList == rhs.m_colorList; +} diff --git a/src/util/color/hotcuecolorpalette.h b/src/util/color/hotcuecolorpalette.h deleted file mode 100644 index a544599f3e0..00000000000 --- a/src/util/color/hotcuecolorpalette.h +++ /dev/null @@ -1,22 +0,0 @@ -// -// Created by Ferran Pujol Camins on 27/10/2019. -// - -#pragma once - -#include -#include "QList" - -class HotcueColorPalette { - public: - HotcueColorPalette(QList); - - static const HotcueColorPalette mixxxPalette; - - QList m_colorList; -}; - -inline bool operator==( - const HotcueColorPalette& lhs, const HotcueColorPalette& rhs) { - return lhs.m_colorList == rhs.m_colorList; -} diff --git a/src/widget/colormenu.cpp b/src/widget/colormenu.cpp index 0a0c6748ca2..810243da53d 100644 --- a/src/widget/colormenu.cpp +++ b/src/widget/colormenu.cpp @@ -6,10 +6,10 @@ ColorMenu::ColorMenu(QWidget *parent) // If another title would be more appropriate in some context, setTitle // can be called again after construction. setTitle(tr("Set color")); - useColorPalette(HotcueColorPalette::mixxxPalette); + useColorPalette(ColorPalette::mixxxHotcuesPalette); } -void ColorMenu::useColorPalette(const HotcueColorPalette& colorPalette) { +void ColorMenu::useColorPalette(const ColorPalette& colorPalette) { clear(); for (const auto& pColor : colorPalette.m_colorList) { QAction* pColorAction = new QAction(this); diff --git a/src/widget/colormenu.h b/src/widget/colormenu.h index 3577cce7fd4..6d6ef152a25 100644 --- a/src/widget/colormenu.h +++ b/src/widget/colormenu.h @@ -2,14 +2,14 @@ #include -#include "util/color/hotcuecolorpalette.h" +#include "util/color/colorpalette.h" class ColorMenu : public QMenu { Q_OBJECT public: ColorMenu(QWidget *parent = nullptr); - void useColorPalette(const HotcueColorPalette& colorPalette); + void useColorPalette(const ColorPalette& colorPalette); signals: void colorPicked(QColor pColor); diff --git a/src/widget/cuemenu.h b/src/widget/cuemenu.h index b8f2f2c49a2..946255607ba 100644 --- a/src/widget/cuemenu.h +++ b/src/widget/cuemenu.h @@ -20,7 +20,7 @@ class CueMenu : public QMenu { m_pTrack = pTrack; } - void useColorSet(const HotcueColorPalette& colorPalette) { + void useColorSet(const ColorPalette& colorPalette) { if (m_pColorMenu != nullptr) { m_pColorMenu->useColorPalette(colorPalette); } From 768c3066fab585682c48666eafc58e144e8a7695 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Mon, 4 Nov 2019 10:15:40 +0100 Subject: [PATCH 17/42] Remove cues tab on TrackInfo --- src/library/dlgtrackinfo.cpp | 230 ----------------------------------- src/library/dlgtrackinfo.h | 5 - src/library/dlgtrackinfo.ui | 102 ++-------------- 3 files changed, 12 insertions(+), 325 deletions(-) diff --git a/src/library/dlgtrackinfo.cpp b/src/library/dlgtrackinfo.cpp index 3b6cb9346c3..ab665aaffdf 100644 --- a/src/library/dlgtrackinfo.cpp +++ b/src/library/dlgtrackinfo.cpp @@ -39,7 +39,6 @@ DlgTrackInfo::~DlgTrackInfo() { void DlgTrackInfo::init() { setupUi(this); - cueTable->hideColumn(0); coverBox->insertWidget(1, m_pWCoverArtLabel); connect(btnNext, &QPushButton::clicked, this, &DlgTrackInfo::slotNext); @@ -92,14 +91,6 @@ void DlgTrackInfo::init() { this, &DlgTrackInfo::slotKeyTextChanged); - connect(btnCueActivate, - &QPushButton::clicked, - this, - &DlgTrackInfo::cueActivate); - connect(btnCueDelete, - &QPushButton::clicked, - this, - &DlgTrackInfo::cueDelete); connect(bpmTap, &QPushButton::pressed, m_pTapFilter.data(), @@ -165,30 +156,6 @@ void DlgTrackInfo::slotPrev() { emit(previous()); } -void DlgTrackInfo::cueActivate() { - -} - -void DlgTrackInfo::cueDelete() { - QList selected = cueTable->selectedItems(); - QListIterator item_it(selected); - - QSet rowsToDelete; - while(item_it.hasNext()) { - QTableWidgetItem* item = item_it.next(); - rowsToDelete.insert(item->row()); - } - - QList rowsList = QList::fromSet(rowsToDelete); - std::sort(rowsList.begin(), rowsList.end()); - - QListIterator it(rowsList); - it.toBack(); - while (it.hasPrevious()) { - cueTable->removeRow(it.previous()); - } -} - void DlgTrackInfo::populateFields(const Track& track) { setWindowTitle(track.getArtist() % " - " % track.getTitle()); @@ -257,7 +224,6 @@ void DlgTrackInfo::loadTrack(TrackPointer pTrack) { m_pLoadedTrack = pTrack; populateFields(*m_pLoadedTrack); - populateCues(m_pLoadedTrack); m_pWCoverArtLabel->loadTrack(m_pLoadedTrack); // We already listen to changed() so we don't need to listen to individual @@ -309,131 +275,6 @@ void DlgTrackInfo::slotOpenInFileBrowser() { mixxx::DesktopHelper::openInFileBrowser(QStringList(m_pLoadedTrack->getLocation())); } -void DlgTrackInfo::populateCues(TrackPointer pTrack) { - int sampleRate = pTrack->getSampleRate(); - - QList listPoints; - const QList cuePoints = pTrack->getCuePoints(); - QListIterator it(cuePoints); - while (it.hasNext()) { - CuePointer pCue = it.next(); - Cue::CueType type = pCue->getType(); - if (type == Cue::CUE || type == Cue::LOAD || type == Cue::INTRO - || type == Cue::OUTRO) { - listPoints.push_back(pCue); - } - } - it = QListIterator(listPoints); - cueTable->setSortingEnabled(false); - int row = 0; - - while (it.hasNext()) { - CuePointer pCue(it.next()); - - QString rowStr = QString("%1").arg(row); - - // All hotcues are stored in Cue's as 0-indexed, but the GUI presents - // them to the user as 1-indexex. Add 1 here. rryan 9/2010 - int iHotcue = pCue->getHotCue() + 1; - QString hotcue = ""; - hotcue = QString("%1").arg(iHotcue); - double position = pCue->getPosition(); - if (position == -1) { - continue; - } - - double totalSeconds = position / sampleRate / 2.0; - - bool negative = false; - if (totalSeconds < 0) { - totalSeconds *= -1; - negative = true; - } - - int iTotalSeconds = static_cast(totalSeconds); - int fraction = 100 * (totalSeconds - iTotalSeconds); - int seconds = iTotalSeconds % 60; - int mins = iTotalSeconds / 60; - //int hours = mins / 60; //Not going to worry about this for now. :) - - //Construct a nicely formatted duration string now. - QString duration = QString("%1%2:%3.%4").arg( - negative ? QString("-") : QString(), - QString::number(mins), - QString("%1").arg(seconds, 2, 10, QChar('0')), - QString("%1").arg(fraction, 2, 10, QChar('0'))); - - QTableWidgetItem* durationItem = new QTableWidgetItem(duration); - // Make the duration read only - durationItem->setFlags(Qt::NoItemFlags); - - // Decode cue type to display text - QString cueType; - switch (pCue->getType()) { - case Cue::INVALID: - cueType = "?"; - break; - case Cue::CUE: - cueType = "Hotcue"; - break; - case Cue::LOAD: - cueType = "Main Cue"; - break; - case Cue::BEAT: - cueType = "Beat"; - break; - case Cue::LOOP: - cueType = "Loop"; - break; - case Cue::JUMP: - cueType = "Jump"; - break; - case Cue::INTRO: - cueType = "Intro"; - break; - case Cue::OUTRO: - cueType = "Outro"; - break; - default: - break; - } - - QTableWidgetItem* typeItem = new QTableWidgetItem(cueType); - // Make the type read only - typeItem->setFlags(Qt::NoItemFlags); - - HotcueColorPaletteSettings colorPaletteSettings(m_pConfig); - auto colorPalette = colorPaletteSettings.getHotcueColorPalette(); - QList hotcueColorList = - colorPaletteSettings.getHotcueColorPalette().m_colorList; - QComboBox* colorComboBox = new QComboBox(); - for (int i = 0; i < hotcueColorList.count(); i++) { - QColor color = hotcueColorList.at(i); - colorComboBox->addItem("", color); - QPixmap pixmap(80, 80); - pixmap.fill(color); - QIcon icon(pixmap); - colorComboBox->setItemIcon(i, icon); - } - QColor cueColor = pCue->getColor(); - int colorIndex = hotcueColorList.indexOf(cueColor); - colorComboBox->setCurrentIndex(math_min(colorIndex, 0)); - - m_cueMap[row] = pCue; - cueTable->insertRow(row); - cueTable->setItem(row, 0, new QTableWidgetItem(rowStr)); - cueTable->setItem(row, 1, durationItem); - cueTable->setItem(row, 2, typeItem); - cueTable->setItem(row, 3, new QTableWidgetItem(hotcue)); - cueTable->setCellWidget(row, 4, colorComboBox); - cueTable->setItem(row, 5, new QTableWidgetItem(pCue->getLabel())); - row += 1; - } - cueTable->setSortingEnabled(true); - cueTable->horizontalHeader()->setStretchLastSection(true); - cueTable->horizontalHeader()->setSectionResizeMode(3, QHeaderView::ResizeToContents); -} - void DlgTrackInfo::saveTrack() { if (!m_pLoadedTrack) return; @@ -469,73 +310,6 @@ void DlgTrackInfo::saveTrack() { m_pLoadedTrack->setKeys(m_keysClone); - QSet updatedRows; - for (int row = 0; row < cueTable->rowCount(); ++row) { - QTableWidgetItem* rowItem = cueTable->item(row, 0); - QTableWidgetItem* hotcueItem = cueTable->item(row, 3); - QWidget* colorWidget = cueTable->cellWidget(row, 4); - QTableWidgetItem* labelItem = cueTable->item(row, 5); - - VERIFY_OR_DEBUG_ASSERT(rowItem && hotcueItem && colorWidget && labelItem) { - qWarning() << "unable to retrieve cells from cueTable row"; - continue; - } - - int oldRow = rowItem->data(Qt::DisplayRole).toInt(); - CuePointer pCue(m_cueMap.value(oldRow, CuePointer())); - if (!pCue) { - continue; - } - updatedRows.insert(oldRow); - - QVariant vHotcue = hotcueItem->data(Qt::DisplayRole); - bool ok; - int iTableHotcue = vHotcue.toInt(&ok); - if (ok) { - // The GUI shows hotcues as 1-indexed, but they are actually - // 0-indexed, so subtract 1 - pCue->setHotCue(iTableHotcue - 1); - } else { - pCue->setHotCue(-1); - } - - if (pCue->getType() == Cue::CUE) { - auto colorComboBox = qobject_cast(colorWidget); - if (colorComboBox) { - HotcueColorPaletteSettings colorPaletteSettings(m_pConfig); - auto colorPalette = - colorPaletteSettings.getHotcueColorPalette(); - QList hotcueColorList = - colorPaletteSettings.getHotcueColorPalette() - .m_colorList; - QColor color = - hotcueColorList.at(colorComboBox->currentIndex()); - pCue->setColor(color); - } - } - // do nothing for now. - - QString label = labelItem->data(Qt::DisplayRole).toString(); - pCue->setLabel(label); - } - - QMutableHashIterator it(m_cueMap); - // Everything that was not processed above was removed. - while (it.hasNext()) { - it.next(); - int oldRow = it.key(); - - // If cue's old row is not in updatedRows then it must have been - // deleted. - if (updatedRows.contains(oldRow)) { - continue; - } - CuePointer pCue(it.value()); - it.remove(); - qDebug() << "Deleting cue" << pCue->getId() << pCue->getHotCue(); - m_pLoadedTrack->removeCue(pCue); - } - m_pLoadedTrack->setCoverInfo(m_loadedCoverInfo); // Reconnect changed signals now. @@ -585,10 +359,6 @@ void DlgTrackInfo::clear() { txtKey->setText(""); txtReplayGain->setText(""); - m_cueMap.clear(); - cueTable->clearContents(); - cueTable->setRowCount(0); - m_loadedCoverInfo = CoverInfo(); m_pWCoverArtLabel->setCoverArt(m_loadedCoverInfo, QPixmap()); } diff --git a/src/library/dlgtrackinfo.h b/src/library/dlgtrackinfo.h index 16bfd450e97..9856dcbeeb3 100644 --- a/src/library/dlgtrackinfo.h +++ b/src/library/dlgtrackinfo.h @@ -39,9 +39,6 @@ class DlgTrackInfo : public QDialog, public Ui::DlgTrackInfo { void cancel(); void trackUpdated(); - void cueActivate(); - void cueDelete(); - void slotBpmDouble(); void slotBpmHalve(); void slotBpmTwoThirds(); @@ -69,12 +66,10 @@ class DlgTrackInfo : public QDialog, public Ui::DlgTrackInfo { private: void populateFields(const Track& track); void reloadTrackBeats(const Track& track); - void populateCues(TrackPointer pTrack); void saveTrack(); void unloadTrack(bool save); void clear(); void init(); - QHash m_cueMap; TrackPointer m_pLoadedTrack; BeatsPointer m_pBeatsClone; Keys m_keysClone; diff --git a/src/library/dlgtrackinfo.ui b/src/library/dlgtrackinfo.ui index 4435134c7a0..caf42ac2c25 100644 --- a/src/library/dlgtrackinfo.ui +++ b/src/library/dlgtrackinfo.ui @@ -7,7 +7,7 @@ 0 0 700 - 588 + 595 @@ -41,7 +41,7 @@ - 0 + 2 @@ -376,7 +376,16 @@ QLayout::SetDefaultConstraint - + + 0 + + + 0 + + + 0 + + 0 @@ -798,90 +807,6 @@ Often results in higher quality beatgrids, but will not do well on tracks that h - - - Cuepoints - - - - - - - Cue Id - - - - - Position - - - - - Type - - - - - Hotcue - - - - - Color - - - - - Label - - - - - - - - - - - 125 - 0 - - - - Delete Cue - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 125 - 0 - - - - Activate Cue - - - - - - - @@ -1030,9 +955,6 @@ Often results in higher quality beatgrids, but will not do well on tracks that h bpmThreeFourth bpmThreeHalves bpmFourThirds - cueTable - btnCueDelete - btnCueActivate From 03b4f06557d6c25301af69cb96b033b1b825a15d Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Mon, 4 Nov 2019 11:21:43 +0100 Subject: [PATCH 18/42] Add QColorDialog to ColorMenu --- src/widget/colormenu.cpp | 65 +++++++++++++++++++++++++++++++++++----- src/widget/colormenu.h | 14 ++++++++- src/widget/cuemenu.h | 1 + 3 files changed, 71 insertions(+), 9 deletions(-) diff --git a/src/widget/colormenu.cpp b/src/widget/colormenu.cpp index 810243da53d..9b11caa4ccb 100644 --- a/src/widget/colormenu.cpp +++ b/src/widget/colormenu.cpp @@ -1,26 +1,75 @@ #include "widget/colormenu.h" + #include "util/color/color.h" -ColorMenu::ColorMenu(QWidget *parent) - : QMenu(parent) { +ColorMenu::ColorMenu(QWidget* parent) + : QMenu(parent), m_pColorDialog(new QColorDialog(this)) { // If another title would be more appropriate in some context, setTitle // can be called again after construction. setTitle(tr("Set color")); useColorPalette(ColorPalette::mixxxHotcuesPalette); + + connect(m_pColorDialog, + &QColorDialog::colorSelected, + this, + [this](const QColor& newColor) { + if (newColor.isValid()) { + emit(colorPicked(newColor)); + } + }); } void ColorMenu::useColorPalette(const ColorPalette& colorPalette) { clear(); - for (const auto& pColor : colorPalette.m_colorList) { - QAction* pColorAction = new QAction(this); + m_pActionGroup = new QActionGroup(this); + m_pActionGroup->setExclusive(true); + + createPaletteColorsActions(colorPalette); + createOtherColorAction(); +} +void ColorMenu::setCurrentColor(QColor currentColor) { + m_currentColor = currentColor; + selectCurrentColorAction(currentColor); +} +void ColorMenu::openColorDialog() { + m_pColorDialog->setCurrentColor(m_currentColor); + m_pColorDialog->open(); +} +void ColorMenu::createPaletteColorsActions(const ColorPalette& colorPalette) { + for (const auto& color : colorPalette.m_colorList) { + QAction* pColorAction = new QAction(m_pActionGroup); QPixmap pixmap(80, 80); - pixmap.fill(pColor); + pixmap.fill(color); pColorAction->setIcon(QIcon(pixmap)); + pColorAction->setCheckable(true); + pColorAction->setData(color); - m_pColorActions.append(pColorAction); addAction(pColorAction); - connect(pColorAction, &QAction::triggered, this, [pColor, this]() { - emit(colorPicked(pColor)); + connect(pColorAction, &QAction::triggered, this, [color, this]() { + emit(colorPicked(color)); }); } } + +void ColorMenu::createOtherColorAction() { + m_pOtherColorAction = new QAction(m_pActionGroup); + m_pOtherColorAction->setText("..."); + m_pOtherColorAction->setCheckable(true); + addAction(m_pOtherColorAction); + connect(m_pOtherColorAction, + &QAction::triggered, + this, + &ColorMenu::openColorDialog); +} + +void ColorMenu::selectCurrentColorAction(const QColor& currentColor) const { + for (QAction* pAction : actions()) { + QColor color = pAction->data().value(); + // Other color action is the last one, if we find it, we didn't + // matched to any color action. + if (color == currentColor || pAction == m_pOtherColorAction) { + pAction->setChecked(true); + return; + } + } +} diff --git a/src/widget/colormenu.h b/src/widget/colormenu.h index 6d6ef152a25..ad75bd6a099 100644 --- a/src/widget/colormenu.h +++ b/src/widget/colormenu.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include "util/color/colorpalette.h" @@ -10,10 +11,21 @@ class ColorMenu : public QMenu { ColorMenu(QWidget *parent = nullptr); void useColorPalette(const ColorPalette& colorPalette); + void setCurrentColor(QColor currentColor); signals: void colorPicked(QColor pColor); + private slots: + void openColorDialog(); + private: - QList m_pColorActions; + void createPaletteColorsActions(const ColorPalette& colorPalette); + void createOtherColorAction(); + void selectCurrentColorAction(const QColor& currentColor) const; + + QColor m_currentColor; + QAction* m_pOtherColorAction; + QActionGroup* m_pActionGroup; + QColorDialog* m_pColorDialog; }; diff --git a/src/widget/cuemenu.h b/src/widget/cuemenu.h index 946255607ba..5bf1a080649 100644 --- a/src/widget/cuemenu.h +++ b/src/widget/cuemenu.h @@ -14,6 +14,7 @@ class CueMenu : public QMenu { void setCue(CuePointer pCue) { m_pCue = pCue; + m_pColorMenu->setCurrentColor(m_pCue->getColor()); } void setTrack(TrackPointer pTrack) { From 5f22b907e011d6ae8371dd2ed36fb38097fe0ce9 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Mon, 11 Nov 2019 15:41:34 +0100 Subject: [PATCH 19/42] Set hotcue color to the first predefined color --- src/engine/controls/cuecontrol.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/engine/controls/cuecontrol.cpp b/src/engine/controls/cuecontrol.cpp index f607e932fa8..c700da1c8aa 100644 --- a/src/engine/controls/cuecontrol.cpp +++ b/src/engine/controls/cuecontrol.cpp @@ -580,17 +580,19 @@ void CueControl::hotcueSet(HotcueControl* pControl, double v) { pCue->setHotCue(hotcue); pCue->setLabel(""); pCue->setType(Cue::Type::HotCue); - // TODO(XXX) deal with spurious signals - attachCue(pCue, pControl); + auto hotcueColorPalette = m_colorPaletteSettings.getHotcueColorPalette(); ConfigKey autoHotcueColorsKey("[Controls]", "auto_hotcue_colors"); if (getConfig()->getValue(autoHotcueColorsKey, false)) { - auto hotcueColorPalette = - m_colorPaletteSettings.getHotcueColorPalette(); auto colors = hotcueColorPalette.m_colorList; pCue->setColor(colors.at((hotcue % (colors.count() - 1)) + 1)); + } else { + pCue->setColor(hotcueColorPalette.m_colorList.first()); }; + // TODO(XXX) deal with spurious signals + attachCue(pCue, pControl); + // If quantize is enabled and we are not playing, jump to the cue point // since it's not necessarily where we currently are. TODO(XXX) is this // potentially invalid for vinyl control? @@ -1738,7 +1740,7 @@ HotcueControl::HotcueControl(QString group, int i) m_hotcueEnabled = new ControlObject(keyForControl(i, "enabled")); m_hotcueEnabled->setReadOnly(); - // The id of the predefined color assigned to this color. + // The rgba value of the color assigned to this color. m_hotcueColor = new ControlObject(keyForControl(i, "color")); connect(m_hotcueColor, &ControlObject::valueChanged, From 1bc6c777aa21ae98586bf3ab04f4d0a0dcac128a Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Mon, 11 Nov 2019 15:44:43 +0100 Subject: [PATCH 20/42] Set hotcue color CO to -1 when no hotcue is loaded --- src/engine/controls/cuecontrol.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/engine/controls/cuecontrol.cpp b/src/engine/controls/cuecontrol.cpp index c700da1c8aa..86192cc1ab3 100644 --- a/src/engine/controls/cuecontrol.cpp +++ b/src/engine/controls/cuecontrol.cpp @@ -298,6 +298,8 @@ void CueControl::detachCue(HotcueControl* pControl) { } disconnect(pCue.get(), 0, this, 0); pControl->resetCue(); + // Reset the color CO to -1 + pControl->setColor(QColor()); } void CueControl::trackLoaded(TrackPointer pNewTrack) { @@ -1742,6 +1744,7 @@ HotcueControl::HotcueControl(QString group, int i) // The rgba value of the color assigned to this color. m_hotcueColor = new ControlObject(keyForControl(i, "color")); + m_hotcueColor->set(-1); connect(m_hotcueColor, &ControlObject::valueChanged, this, @@ -1851,7 +1854,11 @@ QColor HotcueControl::getColor() const { } void HotcueControl::setColor(const QColor& newColor) { - m_hotcueColor->set(newColor.rgba()); + if (newColor.isValid()) { + m_hotcueColor->set(newColor.rgba()); + } else { + m_hotcueColor->set(-1); + } } void HotcueControl::resetCue() { // clear pCue first because we have a null check for valid data else where From fc22b257ee33398315ef0f8348fff0a08bca852c Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Mon, 11 Nov 2019 19:10:45 +0100 Subject: [PATCH 21/42] Make hotcue skin buttons use the cue color Shade still does not work --- res/skins/Deere/hotcue_button.xml | 2 +- res/skins/Deere/style.qss | 124 ------------------------ res/skins/LateNight/button_hotcue.xml | 2 +- res/skins/LateNight/style.qss | 44 --------- res/skins/Shade/deck_hotcue_button.xml | 2 +- res/skins/Shade/style.qss | 45 --------- res/skins/Shade/style_dark.qss | 5 - res/skins/Shade/style_summer_sunset.qss | 5 - res/skins/Tango/button_hotcue_deck.xml | 2 +- res/skins/Tango/style.qss | 58 ----------- src/widget/wwidget.cpp | 24 +++++ src/widget/wwidget.h | 7 ++ 12 files changed, 35 insertions(+), 285 deletions(-) diff --git a/res/skins/Deere/hotcue_button.xml b/res/skins/Deere/hotcue_button.xml index 118987b10d6..19ba4bb08b4 100644 --- a/res/skins/Deere/hotcue_button.xml +++ b/res/skins/Deere/hotcue_button.xml @@ -47,7 +47,7 @@ ,hotcue__color - highlight + backgroundColorRgba diff --git a/res/skins/Deere/style.qss b/res/skins/Deere/style.qss index 6b5a2c09e68..e4171fc8f0e 100644 --- a/res/skins/Deere/style.qss +++ b/res/skins/Deere/style.qss @@ -1596,130 +1596,6 @@ WPushButton[value="2"]:hover { border: 0px solid #0080BE; } -/* Hotcue Color: No Color */ -#HotcueButton[value="1"][highlight="0"], -#HotcueButton[value="2"][highlight="0"] { - background-color: #006596; -} - -#HotcueButton[value="1"][highlight="0"]:hover, -#HotcueButton[value="2"][highlight="0"]:hover { - background-color: #0080BE; -} - -/* Hotcue Color: Red */ -#HotcueButton[value="1"][highlight="1"], -#HotcueButton[value="2"][highlight="1"] { - background-color: #c50a08; -} - -#HotcueButton[value="1"][highlight="1"]:hover, -#HotcueButton[value="2"][highlight="1"]:hover { - background-color: #e50c08; -} - -/* Hotcue Color: Green */ -#HotcueButton[value="1"][highlight="2"], -#HotcueButton[value="2"][highlight="2"] { - background-color: #32be44; -} - -#HotcueButton[value="1"][highlight="2"]:hover, -#HotcueButton[value="2"][highlight="2"]:hover { - background-color: #52de64; -} - -/* Hotcue Color: Blue */ -#HotcueButton[value="1"][highlight="3"], -#HotcueButton[value="2"][highlight="3"] { - background-color: #0044ff; -} - -#HotcueButton[value="1"][highlight="3"]:hover, -#HotcueButton[value="2"][highlight="3"]:hover { - background-color: #0064ff; -} - -/* Hotcue Color: Yellow */ -#HotcueButton[value="1"][highlight="4"], -#HotcueButton[value="2"][highlight="4"] { - color: #4B4B4B; - background-color: #f8d200; -} - -#HotcueButton[value="1"][highlight="4"]:hover, -#HotcueButton[value="2"][highlight="4"]:hover { - color: #4B4B4B; - background-color: #f8f200; -} - -/* Hotcue Color: Celeste */ -#HotcueButton[value="1"][highlight="5"], -#HotcueButton[value="2"][highlight="5"] { - color: #4B4B4B; - background-color: #42d4f4; -} - -#HotcueButton[value="1"][highlight="5"]:hover, -#HotcueButton[value="2"][highlight="5"]:hover { - color: #4B4B4B; - background-color: #62f4f4; -} - -/* Hotcue Color: Purple */ -#HotcueButton[value="1"][highlight="6"], -#HotcueButton[value="2"][highlight="6"] { - background-color: #af00cc; -} - -#HotcueButton[value="1"][highlight="6"]:hover, -#HotcueButton[value="2"][highlight="6"]:hover { - background-color: #cf00ec; -} - -/* Hotcue Color: Pink */ -#HotcueButton[value="1"][highlight="7"], -#HotcueButton[value="2"][highlight="7"] { - color: #4B4B4B; - background-color: #fca6d7; -} - -#HotcueButton[value="1"][highlight="7"]:hover, -#HotcueButton[value="2"][highlight="7"]:hover { - color: #4B4B4B; - background-color: #fcc6f7; -} - -/* Hotcue Color: White */ -#HotcueButton[value="1"][highlight="8"], -#HotcueButton[value="2"][highlight="8"] { - color: #4B4B4B; - background-color: #f2f2ff; -} - -#HotcueButton[value="1"][highlight="8"]:hover, -#HotcueButton[value="2"][highlight="8"]:hover { - color: #4B4B4B; - background-color: #ffffff; -} - -/*"Enabled" state, e.g. for recording status - 0 -- disconnected / off - 1 -- connecting / enabling - 2 -- connected / enabled -WPushButton[value="2"] { - color: #FDFDFD; - background-color: #4B4B4B; - border: 0px solid #006596; -} - -WPushButton[value="2"]:hover { - color: #FDFDFD; - background-color: #4B4B4B; - border: 0px solid #0080BE; -} -*/ - #PlayToggle[value="0"] { image: url(skin:/icon/ic_play_48px.svg) no-repeat center center; } diff --git a/res/skins/LateNight/button_hotcue.xml b/res/skins/LateNight/button_hotcue.xml index 92110d69b0e..88b031eabb2 100644 --- a/res/skins/LateNight/button_hotcue.xml +++ b/res/skins/LateNight/button_hotcue.xml @@ -29,7 +29,7 @@ ,hotcue__color - highlight + backgroundColorRgba diff --git a/res/skins/LateNight/style.qss b/res/skins/LateNight/style.qss index 7caf3c2110a..441c2ae20a1 100644 --- a/res/skins/LateNight/style.qss +++ b/res/skins/LateNight/style.qss @@ -356,50 +356,6 @@ QHeaderView::down-arrow { #SpecialCueButton[displayValue="1"] { background-color: #0000D4; } -/* Hotcue Color: Red - this is also the default color if Auto-Color - in Preferences > Decks is Off */ -#HotcueButton[displayValue="1"][highlight="0"], -#HotcueButton[displayValue="2"][highlight="0"], -#HotcueButton[displayValue="1"][highlight="1"], -#HotcueButton[displayValue="2"][highlight="1"] { - background-color: #db0000; -} -/* Hotcue Color: Green */ -#HotcueButton[displayValue="1"][highlight="2"], -#HotcueButton[displayValue="2"][highlight="2"] { - background-color: #32be44; -} -/* Hotcue Color: Blue */ -#HotcueButton[displayValue="1"][highlight="3"], -#HotcueButton[displayValue="2"][highlight="3"] { - background-color: #0044ff; -} -/* Hotcue Color: Yellow */ -#HotcueButton[displayValue="1"][highlight="4"], -#HotcueButton[displayValue="2"][highlight="4"] { - background-color: #f8d200; -} -/* Hotcue Color: Celeste */ -#HotcueButton[displayValue="1"][highlight="5"], -#HotcueButton[displayValue="2"][highlight="5"] { - background-color: #42d4f4; -} -/* Hotcue Color: Purple */ -#HotcueButton[displayValue="1"][highlight="6"], -#HotcueButton[displayValue="2"][highlight="6"] { - background-color: #af00cc; -} -/* Hotcue Color: Pink */ -#HotcueButton[displayValue="1"][highlight="7"], -#HotcueButton[displayValue="2"][highlight="7"] { - background-color: #fca6d7; -} -/* Hotcue Color: White */ -#HotcueButton[displayValue="1"][highlight="8"], -#HotcueButton[displayValue="2"][highlight="8"] { - background-color: #f2f2ff; -} #BeatgridButtonsToggle { image: url(skin:/buttons/btn_beatgrid_buttons_toggle.svg) no-repeat center center; diff --git a/res/skins/Shade/deck_hotcue_button.xml b/res/skins/Shade/deck_hotcue_button.xml index dbab68078e4..ef398cbaad7 100644 --- a/res/skins/Shade/deck_hotcue_button.xml +++ b/res/skins/Shade/deck_hotcue_button.xml @@ -34,7 +34,7 @@ [Channel],hotcue__color - highlight + backgroundColorRgba diff --git a/res/skins/Shade/style.qss b/res/skins/Shade/style.qss index 158e25eb970..9e32accdc92 100644 --- a/res/skins/Shade/style.qss +++ b/res/skins/Shade/style.qss @@ -552,48 +552,3 @@ QPushButton#pushButtonRepeatPlaylist { image: url(skin:/btn/btn_autodj_repeat_playlist.svg) no-repeat center center; } /* AutoDJ button icons */ - -/* Hotcue Color: No Color */ -#HotcueButton[highlight="0"] { - background-color: #fd0564; -} - -/* Hotcue Color: Red */ -#HotcueButton[highlight="1"] { - background-color: #c50a08; -} - -/* Hotcue Color: Green */ -#HotcueButton[highlight="2"] { - background-color: #32be44; -} - -/* Hotcue Color: Blue */ -#HotcueButton[highlight="3"] { - background-color: #0044ff; -} - -/* Hotcue Color: Yellow */ -#HotcueButton[highlight="4"] { - background-color: #f8d200; -} - -/* Hotcue Color: Celeste */ -#HotcueButton[highlight="5"] { - background-color: #42d4f4; -} - -/* Hotcue Color: Purple */ -#HotcueButton[highlight="6"] { - background-color: #af00cc; -} - -/* Hotcue Color: Pink */ -#HotcueButton[highlight="7"] { - background-color: #fca6d7; -} - -/* Hotcue Color: White */ -#HotcueButton[highlight="8"] { - background-color: #f2f2ff; -} diff --git a/res/skins/Shade/style_dark.qss b/res/skins/Shade/style_dark.qss index fda2ce766cb..f9f10fc77fe 100644 --- a/res/skins/Shade/style_dark.qss +++ b/res/skins/Shade/style_dark.qss @@ -83,8 +83,3 @@ WLibrary QPushButton { WLibrary QRadioButton::indicator:checked { background: url(skin:/btn/btn_lib_radio_button_on_dark.svg) center center; } - -/* Hotcue Color: No Color */ -#HotcueButton[highlight="0"] { - background-color: #b39a00; -} diff --git a/res/skins/Shade/style_summer_sunset.qss b/res/skins/Shade/style_summer_sunset.qss index 903adc42713..e3d04bed986 100644 --- a/res/skins/Shade/style_summer_sunset.qss +++ b/res/skins/Shade/style_summer_sunset.qss @@ -71,8 +71,3 @@ WLibrary QPushButton { QPushButton#pushButtonAnalyze:hover { border: 1px solid #52F904; } - -/* Hotcue Color: No Color */ -#HotcueButton[highlight="0"] { - background-color: #52f904; -} diff --git a/res/skins/Tango/button_hotcue_deck.xml b/res/skins/Tango/button_hotcue_deck.xml index c858ef7640c..eb0785f234c 100644 --- a/res/skins/Tango/button_hotcue_deck.xml +++ b/res/skins/Tango/button_hotcue_deck.xml @@ -39,7 +39,7 @@ Variables: ,hotcue__color - highlight + backgroundColorRgba diff --git a/res/skins/Tango/style.qss b/res/skins/Tango/style.qss index 9e175df7672..cf084fbd0b7 100644 --- a/res/skins/Tango/style.qss +++ b/res/skins/Tango/style.qss @@ -985,64 +985,6 @@ WLabel#TrackComment { border: 1px solid #eeeeee; } -/* Hotcue Color: No Color */ -#HotcueButton[displayValue="1"][highlight="0"], -#HotcueButton[displayValue="2"][highlight="0"] { - background-color: #666; -} - -/* Hotcue Color: Red */ -#HotcueButton[displayValue="1"][highlight="1"], -#HotcueButton[displayValue="2"][highlight="1"] { - background-color: #c50a08; -} - -/* Hotcue Color: Green */ -#HotcueButton[displayValue="1"][highlight="2"], -#HotcueButton[displayValue="2"][highlight="2"] { - background-color: #32be44; -} - -/* Hotcue Color: Blue */ -#HotcueButton[displayValue="1"][highlight="3"], -#HotcueButton[displayValue="2"][highlight="3"] { - background-color: #0044ff; -} - -/* Hotcue Color: Yellow */ -#HotcueButton[displayValue="1"][highlight="4"], -#HotcueButton[displayValue="2"][highlight="4"] { - color: #333; - background-color: #f8d200; -} - -/* Hotcue Color: Celeste */ -#HotcueButton[displayValue="1"][highlight="5"], -#HotcueButton[displayValue="2"][highlight="5"] { - color: #333; - background-color: #42d4f4; -} - -/* Hotcue Color: Purple */ -#HotcueButton[displayValue="1"][highlight="6"], -#HotcueButton[displayValue="2"][highlight="6"] { - background-color: #af00cc; -} - -/* Hotcue Color: Pink */ -#HotcueButton[displayValue="1"][highlight="7"], -#HotcueButton[displayValue="2"][highlight="7"] { - color: #333; - background-color: #fca6d7; -} - -/* Hotcue Color: White */ -#HotcueButton[displayValue="1"][highlight="8"], -#HotcueButton[displayValue="2"][highlight="8"] { - color: #333; - background-color: #f2f2ff; -} - #CueButton { /* is styled like #HotcueButton, lights up if play position is at main Cue point */ diff --git a/src/widget/wwidget.cpp b/src/widget/wwidget.cpp index 364b46280a1..b5421aec06e 100644 --- a/src/widget/wwidget.cpp +++ b/src/widget/wwidget.cpp @@ -37,6 +37,30 @@ WWidget::~WWidget() { delete m_pTouchShift; } +double WWidget::getBackgroundColorRgba() const { + if (m_backgroundColorRgba >= 0) { + return m_backgroundColorRgba; + } + return -1; +} + +void WWidget::setBackgroundColorRgba(double rgba) { + QColor backgroundColor = QColor::fromRgba(rgba); + QColor textColor = Color::chooseColorByBrightness( + backgroundColor, Qt::white, Qt::black); + + QString style = + "background-color: %1;" + "color: %2;"; + + if (rgba >= 0) { + setStyleSheet(style.arg(backgroundColor.name()).arg(textColor.name())); + } else { + setStyleSheet(""); + } + m_backgroundColorRgba = rgba; +} + bool WWidget::touchIsRightButton() { return (m_pTouchShift->get() != 0.0); } diff --git a/src/widget/wwidget.h b/src/widget/wwidget.h index cbadf8ab2fe..fdd571b036b 100644 --- a/src/widget/wwidget.h +++ b/src/widget/wwidget.h @@ -23,6 +23,7 @@ #include #include "preferences/usersettings.h" +#include "util/color/color.h" #include "widget/wbasewidget.h" class ControlProxy; @@ -44,6 +45,11 @@ class WWidget : public QWidget, public WBaseWidget { ~WWidget() override; Q_PROPERTY(double value READ getControlParameterDisplay); + Q_PROPERTY(double backgroundColorRgba READ getBackgroundColorRgba WRITE + setBackgroundColorRgba); + + double getBackgroundColorRgba() const; + void setBackgroundColorRgba(double rgba); protected: bool touchIsRightButton(); @@ -60,6 +66,7 @@ class WWidget : public QWidget, public WBaseWidget { private: ControlProxy* m_pTouchShift; double m_scaleFactor; + double m_backgroundColorRgba; }; #endif From 85a37620c730d213841127ce01fdd6d85c95e47b Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Mon, 11 Nov 2019 22:15:19 +0100 Subject: [PATCH 22/42] Pass UserSettings to Controller constructor --- src/controllers/bulk/bulkcontroller.cpp | 14 +++++++------- src/controllers/bulk/bulkcontroller.h | 6 ++++-- src/controllers/bulk/bulkenumerator.cpp | 8 ++++---- src/controllers/bulk/bulkenumerator.h | 3 ++- src/controllers/controllermanager.cpp | 2 +- 5 files changed, 18 insertions(+), 15 deletions(-) diff --git a/src/controllers/bulk/bulkcontroller.cpp b/src/controllers/bulk/bulkcontroller.cpp index d178d084877..01853742be4 100644 --- a/src/controllers/bulk/bulkcontroller.cpp +++ b/src/controllers/bulk/bulkcontroller.cpp @@ -68,15 +68,15 @@ static QString get_string(libusb_device_handle *handle, u_int8_t id) { return QString::fromLatin1((char*)buf); } - -BulkController::BulkController(libusb_context* context, - libusb_device_handle *handle, - struct libusb_device_descriptor *desc) - : m_context(context), +BulkController::BulkController(UserSettingsPointer pConfig, + libusb_context* context, + libusb_device_handle* handle, + struct libusb_device_descriptor* desc) + : Controller(pConfig), + m_context(context), m_phandle(handle), in_epaddr(0), - out_epaddr(0) -{ + out_epaddr(0) { vendor_id = desc->idVendor; product_id = desc->idProduct; diff --git a/src/controllers/bulk/bulkcontroller.h b/src/controllers/bulk/bulkcontroller.h index 480aa084a69..9d67c1041ea 100644 --- a/src/controllers/bulk/bulkcontroller.h +++ b/src/controllers/bulk/bulkcontroller.h @@ -42,8 +42,10 @@ class BulkReader : public QThread { class BulkController : public Controller { Q_OBJECT public: - BulkController(libusb_context* context, libusb_device_handle *handle, - struct libusb_device_descriptor *desc); + BulkController(UserSettingsPointer pConfig, + libusb_context* context, + libusb_device_handle* handle, + struct libusb_device_descriptor* desc); ~BulkController() override; QString presetExtension() override; diff --git a/src/controllers/bulk/bulkenumerator.cpp b/src/controllers/bulk/bulkenumerator.cpp index 2942c5b3ac7..27b5eea15d3 100644 --- a/src/controllers/bulk/bulkenumerator.cpp +++ b/src/controllers/bulk/bulkenumerator.cpp @@ -11,9 +11,8 @@ #include "controllers/bulk/bulkenumerator.h" #include "controllers/bulk/bulksupported.h" -BulkEnumerator::BulkEnumerator() - : ControllerEnumerator(), - m_context(NULL) { +BulkEnumerator::BulkEnumerator(UserSettingsPointer pConfig) + : ControllerEnumerator(), m_context(NULL), m_pConfig(pConfig) { libusb_init(&m_context); } @@ -55,7 +54,8 @@ QList BulkEnumerator::queryDevices() { continue; } - BulkController* currentDevice = new BulkController(m_context, handle, &desc); + BulkController* currentDevice = + new BulkController(m_pConfig, m_context, handle, &desc); m_devices.push_back(currentDevice); } } diff --git a/src/controllers/bulk/bulkenumerator.h b/src/controllers/bulk/bulkenumerator.h index 5e16dd67d11..2018a4a1181 100644 --- a/src/controllers/bulk/bulkenumerator.h +++ b/src/controllers/bulk/bulkenumerator.h @@ -14,7 +14,7 @@ struct libusb_context; class BulkEnumerator : public ControllerEnumerator { public: - BulkEnumerator(); + BulkEnumerator(UserSettingsPointer pConfig); virtual ~BulkEnumerator(); QList queryDevices(); @@ -22,6 +22,7 @@ class BulkEnumerator : public ControllerEnumerator { private: QList m_devices; libusb_context* m_context; + UserSettingsPointer m_pConfig; }; #endif diff --git a/src/controllers/controllermanager.cpp b/src/controllers/controllermanager.cpp index 2de3fc57ce9..397a9c7dc84 100644 --- a/src/controllers/controllermanager.cpp +++ b/src/controllers/controllermanager.cpp @@ -132,7 +132,7 @@ void ControllerManager::slotInitialize() { m_enumerators.append(new Hss1394Enumerator()); #endif #ifdef __BULK__ - m_enumerators.append(new BulkEnumerator()); + m_enumerators.append(new BulkEnumerator(m_pConfig)); #endif #ifdef __HID__ m_enumerators.append(new HidEnumerator(m_pConfig)); From 3027abdedecc0ab0b18cb072e4da898376457da4 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Mon, 11 Nov 2019 22:17:22 +0100 Subject: [PATCH 23/42] Format code --- src/engine/controls/cuecontrol.cpp | 2 +- src/widget/colormenu.cpp | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/engine/controls/cuecontrol.cpp b/src/engine/controls/cuecontrol.cpp index 86192cc1ab3..312ba3f3223 100644 --- a/src/engine/controls/cuecontrol.cpp +++ b/src/engine/controls/cuecontrol.cpp @@ -1742,7 +1742,7 @@ HotcueControl::HotcueControl(QString group, int i) m_hotcueEnabled = new ControlObject(keyForControl(i, "enabled")); m_hotcueEnabled->setReadOnly(); - // The rgba value of the color assigned to this color. + // The rgba value of the color assigned to this color. m_hotcueColor = new ControlObject(keyForControl(i, "color")); m_hotcueColor->set(-1); connect(m_hotcueColor, diff --git a/src/widget/colormenu.cpp b/src/widget/colormenu.cpp index 9b11caa4ccb..e4f2612bd58 100644 --- a/src/widget/colormenu.cpp +++ b/src/widget/colormenu.cpp @@ -27,14 +27,17 @@ void ColorMenu::useColorPalette(const ColorPalette& colorPalette) { createPaletteColorsActions(colorPalette); createOtherColorAction(); } + void ColorMenu::setCurrentColor(QColor currentColor) { m_currentColor = currentColor; selectCurrentColorAction(currentColor); } + void ColorMenu::openColorDialog() { m_pColorDialog->setCurrentColor(m_currentColor); m_pColorDialog->open(); } + void ColorMenu::createPaletteColorsActions(const ColorPalette& colorPalette) { for (const auto& color : colorPalette.m_colorList) { QAction* pColorAction = new QAction(m_pActionGroup); From 53a08774cf8bda985c47e82fb4910373c2edbf20 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Mon, 11 Nov 2019 22:23:18 +0100 Subject: [PATCH 24/42] Rename members --- src/widget/colormenu.cpp | 16 ++++++++-------- src/widget/colormenu.h | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/widget/colormenu.cpp b/src/widget/colormenu.cpp index e4f2612bd58..e3c4abc69ab 100644 --- a/src/widget/colormenu.cpp +++ b/src/widget/colormenu.cpp @@ -25,7 +25,7 @@ void ColorMenu::useColorPalette(const ColorPalette& colorPalette) { m_pActionGroup->setExclusive(true); createPaletteColorsActions(colorPalette); - createOtherColorAction(); + createColorPickerAction(); } void ColorMenu::setCurrentColor(QColor currentColor) { @@ -54,12 +54,12 @@ void ColorMenu::createPaletteColorsActions(const ColorPalette& colorPalette) { } } -void ColorMenu::createOtherColorAction() { - m_pOtherColorAction = new QAction(m_pActionGroup); - m_pOtherColorAction->setText("..."); - m_pOtherColorAction->setCheckable(true); - addAction(m_pOtherColorAction); - connect(m_pOtherColorAction, +void ColorMenu::createColorPickerAction() { + m_pColorPickerAction = new QAction(m_pActionGroup); + m_pColorPickerAction->setText("..."); + m_pColorPickerAction->setCheckable(true); + addAction(m_pColorPickerAction); + connect(m_pColorPickerAction, &QAction::triggered, this, &ColorMenu::openColorDialog); @@ -70,7 +70,7 @@ void ColorMenu::selectCurrentColorAction(const QColor& currentColor) const { QColor color = pAction->data().value(); // Other color action is the last one, if we find it, we didn't // matched to any color action. - if (color == currentColor || pAction == m_pOtherColorAction) { + if (color == currentColor || pAction == m_pColorPickerAction) { pAction->setChecked(true); return; } diff --git a/src/widget/colormenu.h b/src/widget/colormenu.h index ad75bd6a099..e661a8996f4 100644 --- a/src/widget/colormenu.h +++ b/src/widget/colormenu.h @@ -21,11 +21,11 @@ class ColorMenu : public QMenu { private: void createPaletteColorsActions(const ColorPalette& colorPalette); - void createOtherColorAction(); + void createColorPickerAction(); void selectCurrentColorAction(const QColor& currentColor) const; QColor m_currentColor; - QAction* m_pOtherColorAction; + QAction* m_pColorPickerAction; QActionGroup* m_pActionGroup; QColorDialog* m_pColorDialog; }; From 83892d206dfcf98a5361eab4d35eb135c3206d9d Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Mon, 11 Nov 2019 22:30:13 +0100 Subject: [PATCH 25/42] Change string literal --- src/widget/colormenu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/widget/colormenu.cpp b/src/widget/colormenu.cpp index e3c4abc69ab..1ceabc888fe 100644 --- a/src/widget/colormenu.cpp +++ b/src/widget/colormenu.cpp @@ -56,7 +56,7 @@ void ColorMenu::createPaletteColorsActions(const ColorPalette& colorPalette) { void ColorMenu::createColorPickerAction() { m_pColorPickerAction = new QAction(m_pActionGroup); - m_pColorPickerAction->setText("..."); + m_pColorPickerAction->setText(tr("Other") + "…"); m_pColorPickerAction->setCheckable(true); addAction(m_pColorPickerAction); connect(m_pColorPickerAction, From b858137949a952e12c0b0223e1e9efd4666ba3e7 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Mon, 11 Nov 2019 23:47:19 +0100 Subject: [PATCH 26/42] Allow skins to configure text color depending on background --- res/skins/Deere/style.qss | 9 ++++++++- res/skins/Tango/style.qss | 8 ++++++++ src/widget/wwidget.cpp | 10 +++------- src/widget/wwidget.h | 9 +++++++++ 4 files changed, 28 insertions(+), 8 deletions(-) diff --git a/res/skins/Deere/style.qss b/res/skins/Deere/style.qss index e4171fc8f0e..bb6d949d82b 100644 --- a/res/skins/Deere/style.qss +++ b/res/skins/Deere/style.qss @@ -1583,7 +1583,6 @@ WPushButton:hover { /*"Pressed" state*/ WPushButton[value="1"], WPushButton[value="2"] { - /*color: #FDFDFD;*/ color: #FDFDFD; background-color: #006596; border: 0px solid #006596; @@ -1596,6 +1595,14 @@ WPushButton[value="2"]:hover { border: 0px solid #0080BE; } +#HotcueButton[backgroundIsDark=true][hasBackgroundColor=true] { + color: #FDFDFD; +} + +#HotcueButton[backgroundIsDark=false][hasBackgroundColor=true] { + color: #1f1e1e; +} + #PlayToggle[value="0"] { image: url(skin:/icon/ic_play_48px.svg) no-repeat center center; } diff --git a/res/skins/Tango/style.qss b/res/skins/Tango/style.qss index cf084fbd0b7..669f0f70ff6 100644 --- a/res/skins/Tango/style.qss +++ b/res/skins/Tango/style.qss @@ -985,6 +985,14 @@ WLabel#TrackComment { border: 1px solid #eeeeee; } +#HotcueButton[backgroundIsDark=true][hasBackgroundColor=true] { + color: #eeeeee; +} + +#HotcueButton[backgroundIsDark=false][hasBackgroundColor=true] { + color: #0f0f0f; +} + #CueButton { /* is styled like #HotcueButton, lights up if play position is at main Cue point */ diff --git a/src/widget/wwidget.cpp b/src/widget/wwidget.cpp index b5421aec06e..09ad3fa530b 100644 --- a/src/widget/wwidget.cpp +++ b/src/widget/wwidget.cpp @@ -46,19 +46,15 @@ double WWidget::getBackgroundColorRgba() const { void WWidget::setBackgroundColorRgba(double rgba) { QColor backgroundColor = QColor::fromRgba(rgba); - QColor textColor = Color::chooseColorByBrightness( - backgroundColor, Qt::white, Qt::black); - - QString style = - "background-color: %1;" - "color: %2;"; + QString style = "background-color: %1;"; if (rgba >= 0) { - setStyleSheet(style.arg(backgroundColor.name()).arg(textColor.name())); + setStyleSheet(style.arg(backgroundColor.name())); } else { setStyleSheet(""); } m_backgroundColorRgba = rgba; + m_bBackgroundIsDark = Color::isDimmColor(backgroundColor); } bool WWidget::touchIsRightButton() { diff --git a/src/widget/wwidget.h b/src/widget/wwidget.h index fdd571b036b..e0956488834 100644 --- a/src/widget/wwidget.h +++ b/src/widget/wwidget.h @@ -47,9 +47,17 @@ class WWidget : public QWidget, public WBaseWidget { Q_PROPERTY(double value READ getControlParameterDisplay); Q_PROPERTY(double backgroundColorRgba READ getBackgroundColorRgba WRITE setBackgroundColorRgba); + Q_PROPERTY(bool hasBackgroundColor READ hasBackgroundColor); + Q_PROPERTY(bool backgroundIsDark READ backgroundIsDark); double getBackgroundColorRgba() const; void setBackgroundColorRgba(double rgba); + bool hasBackgroundColor() const { + return m_backgroundColorRgba >= 0; + } + bool backgroundIsDark() const { + return m_bBackgroundIsDark; + } protected: bool touchIsRightButton(); @@ -67,6 +75,7 @@ class WWidget : public QWidget, public WBaseWidget { ControlProxy* m_pTouchShift; double m_scaleFactor; double m_backgroundColorRgba; + bool m_bBackgroundIsDark; }; #endif From bde2fa89e912dd32a1832d91533d79aaab28e73f Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Mon, 11 Nov 2019 23:58:27 +0100 Subject: [PATCH 27/42] Highlight hotcue buttons when hovered --- src/widget/wwidget.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/widget/wwidget.cpp b/src/widget/wwidget.cpp index 09ad3fa530b..f85c3433651 100644 --- a/src/widget/wwidget.cpp +++ b/src/widget/wwidget.cpp @@ -46,10 +46,14 @@ double WWidget::getBackgroundColorRgba() const { void WWidget::setBackgroundColorRgba(double rgba) { QColor backgroundColor = QColor::fromRgba(rgba); - QString style = "background-color: %1;"; + QColor highlightedBackgroundColor = backgroundColor.lighter(); + QString style = + QString("WWidget { background-color: %1; }" + "WWidget:hover { background-color: %2; }"); if (rgba >= 0) { - setStyleSheet(style.arg(backgroundColor.name())); + setStyleSheet(style.arg(backgroundColor.name()) + .arg(highlightedBackgroundColor.name())); } else { setStyleSheet(""); } From c35de3008d99fe33868497a85bc51775b65fcac5 Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Tue, 12 Nov 2019 12:39:31 +0100 Subject: [PATCH 28/42] Fix Shade hotcue buttons color --- res/skins/Shade/btn/btn_hotcue_1.png | Bin 386 -> 3173 bytes res/skins/Shade/btn/btn_hotcue_1_down.png | Bin 385 -> 0 bytes res/skins/Shade/btn/btn_hotcue_1_over.png | Bin 589 -> 0 bytes res/skins/Shade/btn/btn_hotcue_1_overdown.png | Bin 588 -> 0 bytes res/skins/Shade/btn/btn_hotcue_2.png | Bin 392 -> 3711 bytes res/skins/Shade/btn/btn_hotcue_2_down.png | Bin 392 -> 0 bytes res/skins/Shade/btn/btn_hotcue_2_over.png | Bin 3436 -> 0 bytes res/skins/Shade/btn/btn_hotcue_2_overdown.png | Bin 2265 -> 0 bytes res/skins/Shade/btn/btn_hotcue_3.png | Bin 397 -> 3582 bytes res/skins/Shade/btn/btn_hotcue_3_down.png | Bin 397 -> 0 bytes res/skins/Shade/btn/btn_hotcue_3_over.png | Bin 3242 -> 0 bytes res/skins/Shade/btn/btn_hotcue_3_overdown.png | Bin 2271 -> 0 bytes res/skins/Shade/btn/btn_hotcue_4.png | Bin 389 -> 3504 bytes res/skins/Shade/btn/btn_hotcue_4_down.png | Bin 388 -> 0 bytes res/skins/Shade/btn/btn_hotcue_4_over.png | Bin 3085 -> 0 bytes res/skins/Shade/btn/btn_hotcue_4_overdown.png | Bin 2160 -> 0 bytes res/skins/Shade/btn/btn_hotcue_5.png | Bin 201 -> 3723 bytes res/skins/Shade/btn/btn_hotcue_5_down.png | Bin 201 -> 0 bytes res/skins/Shade/btn/btn_hotcue_5_over.png | Bin 201 -> 0 bytes res/skins/Shade/btn/btn_hotcue_5_overdown.png | Bin 201 -> 0 bytes res/skins/Shade/btn/btn_hotcue_6.png | Bin 202 -> 3593 bytes res/skins/Shade/btn/btn_hotcue_6_down.png | Bin 202 -> 0 bytes res/skins/Shade/btn/btn_hotcue_6_over.png | Bin 202 -> 0 bytes res/skins/Shade/btn/btn_hotcue_6_overdown.png | Bin 202 -> 0 bytes res/skins/Shade/btn/btn_hotcue_7.png | Bin 203 -> 3473 bytes res/skins/Shade/btn/btn_hotcue_7_down.png | Bin 202 -> 0 bytes res/skins/Shade/btn/btn_hotcue_7_over.png | Bin 202 -> 0 bytes res/skins/Shade/btn/btn_hotcue_7_overdown.png | Bin 202 -> 0 bytes res/skins/Shade/btn/btn_hotcue_8.png | Bin 196 -> 3581 bytes res/skins/Shade/btn/btn_hotcue_8_down.png | Bin 195 -> 0 bytes res/skins/Shade/btn/btn_hotcue_8_over.png | Bin 597 -> 0 bytes res/skins/Shade/btn/btn_hotcue_8_overdown.png | Bin 596 -> 0 bytes res/skins/Shade/deck_hotcue_button.xml | 6 +++--- res/skins/Shade/style.qss | 4 ++++ 34 files changed, 7 insertions(+), 3 deletions(-) delete mode 100644 res/skins/Shade/btn/btn_hotcue_1_down.png delete mode 100644 res/skins/Shade/btn/btn_hotcue_1_over.png delete mode 100644 res/skins/Shade/btn/btn_hotcue_1_overdown.png delete mode 100644 res/skins/Shade/btn/btn_hotcue_2_down.png delete mode 100644 res/skins/Shade/btn/btn_hotcue_2_over.png delete mode 100644 res/skins/Shade/btn/btn_hotcue_2_overdown.png delete mode 100644 res/skins/Shade/btn/btn_hotcue_3_down.png delete mode 100644 res/skins/Shade/btn/btn_hotcue_3_over.png delete mode 100644 res/skins/Shade/btn/btn_hotcue_3_overdown.png delete mode 100644 res/skins/Shade/btn/btn_hotcue_4_down.png delete mode 100644 res/skins/Shade/btn/btn_hotcue_4_over.png delete mode 100644 res/skins/Shade/btn/btn_hotcue_4_overdown.png delete mode 100644 res/skins/Shade/btn/btn_hotcue_5_down.png delete mode 100644 res/skins/Shade/btn/btn_hotcue_5_over.png delete mode 100644 res/skins/Shade/btn/btn_hotcue_5_overdown.png delete mode 100644 res/skins/Shade/btn/btn_hotcue_6_down.png delete mode 100644 res/skins/Shade/btn/btn_hotcue_6_over.png delete mode 100644 res/skins/Shade/btn/btn_hotcue_6_overdown.png delete mode 100644 res/skins/Shade/btn/btn_hotcue_7_down.png delete mode 100644 res/skins/Shade/btn/btn_hotcue_7_over.png delete mode 100644 res/skins/Shade/btn/btn_hotcue_7_overdown.png delete mode 100644 res/skins/Shade/btn/btn_hotcue_8_down.png delete mode 100644 res/skins/Shade/btn/btn_hotcue_8_over.png delete mode 100644 res/skins/Shade/btn/btn_hotcue_8_overdown.png diff --git a/res/skins/Shade/btn/btn_hotcue_1.png b/res/skins/Shade/btn/btn_hotcue_1.png index bbcc063c52533d5515480db28be7a660fbc6dbd4..86f572261d970428cd8e69f8e38e1f2ed9ebf307 100644 GIT binary patch delta 3149 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 delta 360 zcmaDV(ZoDKxt@Wg*vT`50|;t3QaTtI7&r?&B8wRqxP?HN@zUM8KR`jT64!_l=c3fa zlFa-(g^eIqk{1IsTA!OwsyDnTmp5_407Q%i~&sufaFi<65o3qb0vlw9)5 z^K$YNQxr-vQWav9{D8_*6oON8Qj_b!27>g&C@B;q=B4H+q!s1oDrA(D6jrda73>L?TgwOLgwDAX#{Ml4xB8|VrdkSl`o(@M${ zi&7aJQ}UBi72IHEC^!e3z1Xv<2dFO8)5S5wqBnU;;*{{8=N()R96E5|fI;D>lvNVb z=FdKTz(sWOgaa!Yn=d$sIdLU9B|UhfnmtXxWWoaOPQw-zW>41U`wmSL4;^rbvf!)J i?{{F9Tp7#Cz_5A+&*}9)^tpgGFnGH9xvX)e0%8#mPmP1t9fSN-p{3c{%xsDGDVSsR}Vlen4d@3c;y4smWmTKw4sy6bcga zQgam2it=+6GD=Dctn~HMGK*5n^NX^J^%9d(6LWI(lJj$wbQCgEtaJ@^6pDe`tg00h zY87fDmaLx*bc77Z5yAOsCFO}lsSJ)O`AMk?ZZI=Mo z_|NkWt_KbsIB;OXta*N-httl_IeEZEbn=7)D;k?GIEXoMSxH$vc+=I!)x;)LqG;GQ zAw^+j!Ut&&gCGeB5ANd!-#-=1P*C2wnt|cqWA4Ldrjk#A<}-M@`njxgN@xNA9OQpF diff --git a/res/skins/Shade/btn/btn_hotcue_1_over.png b/res/skins/Shade/btn/btn_hotcue_1_over.png deleted file mode 100644 index 079f132f1dabfe29a209cb3e4059b765d22a67f4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 589 zcmV-T0EX>4Tx04R}tkv&MmKpe$iQ%glEA{J3`$WR5rf~bh2R-p(LLaorMgUR(vXws0R zxHt-~1qVMCs}3&Cx;nTDg5U>;i>s5Oix`&UicTt|@eeTcEna!CD@QK7TOgAjzb>gW_ zOXs{#9A-s1OMFf|YS0CVAGxl${KmQ9u)s6JMk+Z+93~bEEv&RKD;g^CByl9GYLqXe zUCwge;;fb`tZ`5N!cb0IUgA2C8D2mgcL-J1D{aW^Rx0|GC${V@atcY%7UyM6-rpMfi_=`YuS=}*$D zO)YW+bZr9{*G)~?11@)fz9(HaWJmJT6!LlC{fxdT1N7Vi9jk6{jeVRx04eG!aRVG2 z0>cH$Uh{Z&duwn1o@w;=1A4Y{s&4pKSO5S324YJ`L;(K){{a7>y{D4^000SaNLh0L z01FZT01FZU(%pXi00007bV*G`2jd482^2N#nm;B0002ozL_t(I%VU(3SNYFCMZm(q zz`*e2nFCb}laV^Wz{EghFlyi+G?0am0#3&DaQ65Aft0(D7(+%Qg)kr`97l7?AT*GX b+EoAmf#eNw!9rq000000NkvXXu0mjf3zqp; diff --git a/res/skins/Shade/btn/btn_hotcue_1_overdown.png b/res/skins/Shade/btn/btn_hotcue_1_overdown.png deleted file mode 100644 index 78137be3a2766f5a291c6128014cca6e51c89fd6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 588 zcmV-S0<-;zP)EX>4Tx04R}tkv&MmKpe$iTT4YM4i-^y$WWauh>CR7DionYs1;guFuC*#nlvOS zE{=k0!NHHks)LKOt`4q(Aou~|;_9U6A|?JWDYS_3;J6>}?mh0_0scmXsb<#%plX(p zP9}tGZdC}qB8VXjAdaxaOnpuiQ}7&L_we!cF2=LG&;2=imAuISpFljzbi*RvAfDN@ zbk6(45mu5E;&bA0gDyz?$aUG}H_kbWYY7*5n`d(!Ey()lA#h$6Gs(QqkMnX zWrgz=XSGset$XqphV$CWGS_L2Ac;jRL4*JqHIz|-g&3_GDJIgipYZSxJARQ|GP%lN zZ37qAZB5<-E_Z;zCtWfmM+(sN7Ye}p8GTa@=)VPe*4*A&`#607GSpS-1~@nb z#)_1^?(y#K&ffk#)9UXBleTiS%{j=S00006VoOIv0RI600RN!9r;`8x010qNS#tmY z3labT3lag+-G2N4000McNliru;|CTA6gL8tUJ3vJ07*$iK~y-)W0aIv`OiQ_z{0@5 zz_9JLF;xun5*YTzI=kcE%}^FMpw?2{}ml)I1^Lq;QoFd!uyM{~*`G?0aB^>EX>4U6ba`-PAZ2)IW&i+q+U;6daw9tq z{KqNg2qZ{wIS$u|xxpO&CCE}))@Ir6k2eu*b-PMM3Ivf@5|nBD@4u7&gC7xN$V*Hi zhhXsIv(H>`lg$3f>$f$2{oZfbt+gOhI1%J=w;{%s9Z2kP4(lNs6 z5OfS&1Ug-ranrAr#B<)yFN#ne`zGWRuT`zQukE67`uLyn(nW>sA-t#S2R_0|U9ga2 z9?m+me)~PgK|7aS!Ed{MGwTz^y>p({B_5i)LFCik=5YT$vlY^(?u_ncG6n z>&s4#z_7an+!_y3=p3KA*tKq6>ouFOa<`bu7Qz(jr++VcHTazu&DqpM)}+%`(2LbY zp@t%-zcC9C*m>Y4%fOe{0l&HOAGj(Ilx60G4Hi?kh${BOx7@Z+F}4)@Apj=A z+5%+|0^XWno!DSaWRBLtjtKx2XwJ@a1p-!tJCtPaTwscvSl7m9qFr-YIIuoOZUPYE zf{VZgQGW)oychUS^a(yx#EBP?AW;%YmJ(d>K|%;IXp$?@MIR-G7-Nb#mgJI8l0u3p zrJPFU?2rQ`ha7XtIhVpzuuy@lg82ncDpyy1l^SZSspeW5+^6{_EwtED%dK?ot|LwK z&|^@|%i@~0dkZZDSnozlbVqxJqEY?O= z>k%@Mz51NR7C};1?Wi#xWSh%~bJV5^BY!a?i%qG>S4FeFSg3$%@#;!nO?cyKhtJB` z>Ks0V8q@}5I?*fm)u<`09dV9pN4gh$c!auU+S-*|n8vjtA(O1fs8Q08m}ADnfkGlM zXjIwyUf0R6$aHvuHPCLj9)|9_fW!E;HSH$!=C{`uf@0>_P)ZjR070RM3PnWW*?;>f zu!C*}_k`V)fVZFHt{1r?AkF zg~mg~H{Q2sE7%9ra29|~H)xw{yq8~v!>=A;euWVI5$qobIx*j9e4wsie8zpdG2SiU zX+FaJX^cM)ae9q5NLK<&GQIcVT7Pr}i-M#Cw74G4ksaA5(6erZw%UP1##(MneV3-H z4_4n(E)f7C?;*3PzRn9sS7a;_cX%IDXf9{G%!>1|$Y?-7lO|ZKdrSe%8Y^qANp!pL zhtN+;KhiB;i=+>kw+nx7!}NH^-`p_hB6TUvf#QNPHj}IME!YI!>e}$AA%8&R;$+Ug ziZ)s~_76lG-R0IFMjN4(sA$!fT(tGj6@I>orL&UeW$+lnu5=JBW`>PG+0n$8hO$~{ z!gMK4kvx6v=q{ z8%omRFi26wOvN*|Yu<8MiXN;rvkzD?3z}FNBlT&yHg*FLNPrtGSbr+qzvUUx&LL_t z6_MVnc)%>#qr90ss$gTWL3;yLhc6Xb2~pqIQs^PGOu&YY4v8rzY-DNFl4tC!4DTjd z_y%P37;~l&2iUVlg?F+D zQJu(@h&2noEUMnUBz4w{EZi>~A+M%9HAGCU-cREpJ#?zV-G5ZKg?7;Bk`EndSCbndVTo!!cQ~KI%AAS z!eP;{-Z^Q_pl4GY_hv?L4G>JWM$cAjHgmw2?5M|p9!i*{3SEsR@2go%Ag9DTHJi&7 z$>tOJyyc#>6Mz2e0D%ea$meJ7IhbAsh{j=!+v9mFs~yheir@wcE4V!!(H&<_N8q1h zD1>pcZv)7+~sz z6%6(;It(gDIPlKy^^5^oyP!kh4uX0F!HrCW>_zHD=9Z{<9}?-af;ak^I+p3Pgf*&o zkCZYBSJ9JCUgZSE!x}=;s4eso5pGevEchIdPREgZvZ)yvq;hKdwNR8`;at6j!l-@@ zov>RekbkzLBhkePFWNb9r+m$Uapk~c$al+adwRI@Dsrf3&)Lv&=%+nfvgxEk^o$E# zYR9X-O}{+DwpUG}yXa(%?9Dn5+1Y8(TZAb3|7j9fPf*W4HT&V3GxYk%SsP3|&nS7O#;9~_Wv1yv~^f@mO- zBe#ak;+*4%9E-4PfFrjGt(BS#k#Zq9v0js$0WWZY21mNM2PpD(52JNBve9;(7k^z5mrwstukpig+`w;9!U4ok;YwH3ElYIU zEzJ>u)RtfF#ET9fsguD>u!75GB|RG#8Ahl$hbY+of43~1>)OXFT^8C7>vB$yH%NPj zZ*R~x)V$OG;ZW-T*aXuGfD#Y@9{Dsx6ITEr7IMnksUZMRVN3H<7-nDouY|?Lcz=cf z{G9H%eSaPvuR?(CV&I33GJQUm`gx;FHxUp5{4;>L<rf z(+1L`$wGj%p$=Jx--KKZlTk+XAVRa=m|7`TWfXATn(LDpt0|=|6p^K3m2arr)Pp8# zluXp~{#wCN8^vpiYChn7%p{e)mwAzB(0t_bUMUL+C@$A30(%4IQLJ51BRs?iMP{PsOtOxCu?3qTknzsh{4pxA_}!wB`L? z99^sT8*y~M+nwlPD8;F`f+q3>roc;w@V*RsAjy1bBd3|HAhYpa|GyMbO z*ST9td7pa#00D$)LqkwWLqi~Na&Km7Y-Iodc$|HaJxIe)6opSyMbU~wJBT=Bs7@9{ zMIE&YMW_&Jg;pI*Uit@38j=(jN5Qq=;Ll>!!Nplu2UkH5`~h)sb$?QHkrMAq3N2!M zaCsl+y>qzlK0v6KnPzp20h(@`sYG1NWLL$mSM(x86~?s8EMrcRlJH$$_XzO)F2=L` z@BO*@)SShDfJi*c4AUmwAfDQ^4bJ<-5muB{;&b9rlNuy`C`-N zgjg)JvC_t@Xllfh#D7s$(*{h@IUxHTPr^??j?odKMWw&%l-5@>lA>%qQu!mKHq%`nQ3L>z1bM0hc?#z)q7Po3bkfsf2tUct4|W z$^zkApl8jSTk{;J4?vn`mAnBC4uO#ZWv~0ZySsgE|K4fN?+0~8a;U)B8e{+f00v@9 zM??Ss00000`9r&Zkuey52XskIMF-;x3=1M9f#EQt0001KNkl z7h=(ZxRS+a)}y_FjMT0I0FU|-8|1#wr_ulb002ov JPDHLkV1hfe2Y>(o delta 366 zcmew_)4@DJxt@Wg*vT`50|;t3QaTtI7&r?&B8wRqxP?HN@zUM8KR`jT64!_l=c3fa zlFa-(g^eIqk{1IsTA!OwsyDnTmp5_407Q%i~&sufaFi<65o3qb0vlw9)5 z^K$YNQxr-vQWav9{D8_*6oON8Qj_b!27>g&C@B;q=B4H+q!s1oDrA(D6jrda73>L?TgwOLgwDAX#{Ml4xB8|VrdkSl`o(@M${ zi&7aJQ}UBi72IHEC^!e3z1Xv<2dFOE)5S5wqBnU;`jqgW=N()R96E5|fI;D>lvNVb z=FdKTz(sWOgaa!Yn=d$s)l4!}G>kYpeNCj(yv7$&g^>zopr02FhDNB{r; diff --git a/res/skins/Shade/btn/btn_hotcue_2_down.png b/res/skins/Shade/btn/btn_hotcue_2_down.png deleted file mode 100644 index 93b227bafe4436da27378ab12d3fa5ac45c28d27..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 392 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc1|)ksWqE-VOR)e0%8#mPmP1t9fSN-p{3c{%xsDGDVSsR}Vlen4d@3c;y4smWmTKw4sy6bcga zQgam2it=+6GD=Dctn~HMGK*5n^NX^J^%9d(6LWI(lJj$wbQCgEtaJ@^6pDe`tg00h zY87fDmaLx*bc77Z5yAOsCFO}lsSJ)O`AMk?ZZI=Mo z_|NkWt_KbsIB;OXta*N-httl_IeEZEbn=7)D;k?GIEd9ulI4|+II1nBb;4L6R#}g` z!)$^CvxkuY+lR`DI?NYiy|x;tu<`sWv0&m&SyCp-z;LKbIQrk#oC2UV44$rjF6*2U FngARoe!&0$ diff --git a/res/skins/Shade/btn/btn_hotcue_2_over.png b/res/skins/Shade/btn/btn_hotcue_2_over.png deleted file mode 100644 index 248fb1ea95740ee4658f23fe1541e1a8a108a82b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3436 zcmV-y4U_VTP) zaB^>EX>4U6ba`-PAZ2)IW&i+q+U;3emLn?;{AU%j1dtGqi1e#C1mJl=ON z1B6?QE1#cfUh^C7%j<%C%@6(YzRTm1Q!YW*z)c|A&V(;|DR~KEI`?mixJu_sOLvcR z{gt}+v_)s=&F`3D3>C;@ax^)xKc8n3Lkq6la20$KCrq?^b|>KzqwnqKUQGAIfG^+P z*w2saKEImd2)aF1dSj>Sevs$(+g7^QDA3E!^q|oS3XQ! zqwH^(*kMlI%M#uhe}eCMzboFwH$Q-y;!(Tv9Y1Oy*m{K+G_^uz-9nB#%+4hCG35x?T5_@cGiHI6d){M#lzdZz46ocgP^s{ z%|r0z#a7UZ%-~SNkh4$B0w7^sHkEDP;LgRreHpL}1ZA7K-~pTOM?{zK>RY^VW-ks? zqd)NyvR(pUBJ3?tCNL22DX@eTd?v~vz>Wp@G-ys7sRMx^PDr68MMflKDr9?OO%8i5 z37y-Z7XTp$kql@MKmaR7i3#ym;6lv_OB9hLS&CF?iBn9HQp(AAs%6eGOHMiGl51{- zODs}S$)%K9TBXXMF>y83TuZIBHEh~qgRF-64L3S9%a0hZck9tw&|8PKo*6tDHxKMQK2>}tp_}lz&vpz zf{GSIs*A(qH5N=VR_ zsp2!3Pw|WeQ&vkU9!YLpa-i!UBthpb3m#FMoS|CTONSqO3*8Iw%u+^=qcoDS^57fbpx}rhgrD zsfGm$WLwC-_1O#r)P8HBN{rQ}%fKoQQu#LI{WZ9UtUqwkr`V4WnMTBB0%3-QecL+v zFJ0YNCqB7yt(HXBC=r>~6l*i`LRK5KX?h)7>uw-sI7L+(8q`)RZ+yVrp#HNDY!CPp ze^-79|EGd(_&HK5xR=qtWC3^cj~}tXSiJ(9xO#LQ#VMi8)Jmuy!PueT z!mL1~NcKDrcF)2 z#|m;r0pXC)>OcnSJPT_O)1;(HV-QL3G;TRJn;$%{pn=1 z1X>5pkwP6XNQN(&m{stA7IJCpdyrEfknOuEeN0N_aZ~qhR|HDDin8L5PsQ@7uHs<5InDenNoi62sBGs*)=6jb zWhAT`D7mbwm11WD=Kp9#H$y2(yIDx13kV!MD_Cb%Fi(?cWMn)yL^n7fJPlyFFm80x zVJg24oq#Nsg{a%(ccW1B8ujREd0322bo5YvjYeBEhLp?k0cf?a?)JrpF;*#kyU;c? zoG8GF^zyFPp}PUN8Pzc{KnFh&k3gM-Wokrt?iDwly~wmnmw zRY4A#5a<&$5}6{k)}|QXF^uU000;TVxOtEG%X^GRVCihe2mKeJ(R%2ToFUFFDS82z zUAC2$IW*dPbRkWFCDZ#O)lS$U7_pN;YnY7NZ1Jn>V_mM8+wDl|*rvN# zjlgRI>mk;{x?-)0F@Pbp`X<~HdvM}}UZu^%;AB4qv(a zsNmD7LL6v9OB%2Sn3qQb%^@3v%K^iR2CsXHcV;A5I$1woSwAo4E9(>8vVPu0p$$2V zA$N?HxnUtlN)Q~zwtlY7w(%ljWVvz?v7e__+Jn~+${Hp)B4`1#Q`$bt2~NC_9^zxBYOM>(Gk4 zlr;rVbHh|EMS2dQhlm`D*huilmz3S~K4r5dUIqmcoY2n8L-!pe0~Y`W-NQCDGrYhO zyE!S^Vh1PL`Pqz@>a8t?3m89LwTIce`MIruQosvJ0YVsJ<6Q_Kdbf>kO1)M)d|wSn zx~~TIURN0am8E7K7^#kqidb#D4`P}yyPZZ)D+QAFZdF`NNZ)aRyI?S%(UpUsK}7Jz zs#d8rhc77AD$jD4>-+e2rRzRwc)wHWx{n&(?^L?(yUNUU`i{yh^rVB+ z5>!l7f)0g(Mw1=|5iE^zQwcfDwVo;gLPmCLa5(UzRkUF!NY3~P8WD0*QS-Yp)5dq6 zx-M9b+Ps1e(vCswS>rPQKn@0l*y4Nwr zewu|8uYxT%5S zg>r1xWH2_!GxkR4GA<9D>v911@j=Dc%T)LALB-e0RQJh2)t9gC`z`GAa|-wILB-e4 zDcr{g6<sxSF~``U5k=U4aRgN!%q?Y?$g`8j*LuN_x@ z&fe}9kpD!`H(9{_saus||K(L_q{K8L?vyqa6&32tnM-Rsm4J3z8+O+1kV(=UXn#&4 zj?}#m?Rg)zTgPyCIr2WVM~<gba76RjZ0CH)RQw6nNgNw7S4z7YA_yOYL>ZIr* zCH^ldw21NGxF7HCJ?`EC{ziqVX4eFuYL<~sCWLHmRS3Nzh#?FhjR+1FrbK-G>E=c^yb=l=N&P9j) zJTqcs((}X-Vxicelu?0&7_Ay9CepN@@bC{ievw=D3saMBQaAwwUu^qh z6bS7C&AM%WAKP~G1PD9>S6bU&Z2<94((7$4dIa=s0~gnAP2K}8cYwhsT{0v`3efZy z3c&jreNztTzXf{M+}>LIIDG&z)K%&RI5-5xij=+X@$T-<-u^w)>hA}WwsN#{&LZpp z000JJOGiWi{{a60|De66lK=n!32;bRa{vGf5&!@T5&_cPe*6Fc00(qQO+^Re1sN9x z6LNS5L;wH)R!KxbR5;6Hl$2Nb&p<`M!oa}5@Z^~TBQ*?@kvj07iGj*s)WAV#APYW8 zPR8|E)9CmAfsAAsh^`Tr1r)oGYy*k$B3TBayOe5SIGV7BsDX^st^xph+8HzW*+!=T O0000 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 L`njxgN@xNA`PYF7 diff --git a/res/skins/Shade/btn/btn_hotcue_3_over.png b/res/skins/Shade/btn/btn_hotcue_3_over.png deleted file mode 100644 index bc39f10974a604572a2402332206e51c804a3155..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3242 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 cMrv0907+jNF#^Kw8vp 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 JPDHLkV1oFAkd*)c delta 363 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 mxp*{T4-o@dU?nZJ3* diff --git a/res/skins/Shade/btn/btn_hotcue_5.png b/res/skins/Shade/btn/btn_hotcue_5.png index dab5e3ce7b0928e3ce7f095ceaf92ac21156ac21..002cf0cc546fe6636bf27af4f8af89a482886f0b 100644 GIT binary patch delta 3713 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 diff --git a/res/skins/Shade/btn/btn_hotcue_5_over.png b/res/skins/Shade/btn/btn_hotcue_5_over.png deleted file mode 100644 index 6b9e23bedb83e4b77c74329a680eff003737598d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 201 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc1|)ksWqE-VTavfC3&Vd9T(EcfWS|IVfk$L9 z0|U1Z2s2)~TlWVj$X?><>&pI^U5ryg>F}SgZ-GK#o-U3d7N?V^ObP#a-of?2p#ujF ze0VaGIh1GeM5_Y_jyR+mWIa$YnZP<<>&pI^U5rygLs?ViGf*hZ)5S5w;&k$qDd9iQJGdS=bl|{& z4R5y^hVpzCvORs^h(oGD)&m8T39R#%{G4waanyg)jtS`shHMj36?S=_EK}U&7USqp qC^0ElK{L^ORxsPf#-1zdm>7sESSm_6}fx*+&&t;ucLK6T_GePhG diff --git a/res/skins/Shade/btn/btn_hotcue_6.png b/res/skins/Shade/btn/btn_hotcue_6.png index be5e45c35f8175a3100b63c24d1349b9878fe55b..dffdf0b39c3db7f23c409a28ae6d93f7e225b926 100644 GIT binary patch delta 3572 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 diff --git a/res/skins/Shade/btn/btn_hotcue_6_overdown.png b/res/skins/Shade/btn/btn_hotcue_6_overdown.png deleted file mode 100644 index 3dc5b66bd296ef0136992a7ed5942bad3497c405..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 202 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc1|)ksWqE-VTavfC3&Vd9T(EcfWS|IVfk$L9 z0|U1Z2s2)~TlWVj$X?><>&pI^U5ry&dm^t~7f>kN)5S5w;&k$qDd9iQJGdS=bl|{& z4R5y^hVpzCvORs^h(oGD)&m8T39R#%{G4wqk)$3ObHaQ=gtLd4z}=-kbP|-VwI10u rNk@EAu7YNw`K(~Jjg385)-f?8z7dkKwE2?_w1UCY)z4*}Q$iB}h7CeZ diff --git a/res/skins/Shade/btn/btn_hotcue_7.png b/res/skins/Shade/btn/btn_hotcue_7.png index f0821440d8c2d7f5d17b8d92a44861096ade4ae0..998978cde5e3c3facf533e73f67952e6aed2ca13 100644 GIT binary patch delta 3459 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 l4J6i;qj{Nfz)0;X004X(BpdL$8pBB0);Y jCd+w}m?c-nvNA9ndn$C^%r9>f&_o7LS3j3^P6(MR diff --git a/res/skins/Shade/btn/btn_hotcue_7_down.png b/res/skins/Shade/btn/btn_hotcue_7_down.png deleted file mode 100644 index c6da80d83a13fc0111ae00780334e47ff5565912..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 202 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc1|)ksWqE-VTavfC3&Vd9T(EcfWS|IVfk$L9 z0|U1Z2s2)~TlWVj$X?><>&pI^U5r!K`qqP$89|<9O*C!sA>m8?=7V)YaRhV>W!=i;u r>&@Bqf;>8VF3vIP;7wUlCd$B&uv+lAczdxg&<>&pI^U5rzX{obNB9iULSr;B5V#p&cJQ^J3qcW^y$=)i#k zAD+x)4&|9V(dxi~BMzwsSq~IUCa}&|QGZ;u=F$EC?32<>&pI^U5rzXZ+V?v2T&;7)5S5w;&k$qDd9iQJGdS=bl|{& z4R5y^hVpzCvORs^h(oGD)&m8T39R#%{G4waanyg)jtS`syWC>L1acKL*(Qp#sYk|q rs49M-)H>ya#97Ve8xC$+;fxIL=LvEKPvrXnw1UCY)z4*}Q$iB}$$~<> diff --git a/res/skins/Shade/btn/btn_hotcue_8.png b/res/skins/Shade/btn/btn_hotcue_8.png index d4261196257f454bf46b26cb83a486b0eacb47c6..36542964c2e1e3fd69eb4a871b99e749826b2cf0 100644 GIT binary patch delta 3566 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 diff --git a/res/skins/Shade/btn/btn_hotcue_8_down.png b/res/skins/Shade/btn/btn_hotcue_8_down.png deleted file mode 100644 index 9644ffd1ca1b78f6c59dbd05e57013c15d18894b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 195 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc1|)ksWqE-VTavfC3&Vd9T(EcfWS|IVfk$L9 z0|U1Z2s2)~TlWVj$X?><>&pI^U5rzlWBz@GFF>IHPZ!4!i_^(driA}I@8Eji(18O7 zCd``WCwe&T{G5{qjyR+mWIa$YnZP|<9O*QuC@ivf+-1s276oLK67xmBkn k#%$Jx_QuAZE9;mT_HPn4pFH~~H_&VbPgg&ebxsLQ0C{gh_W%F@ diff --git a/res/skins/Shade/btn/btn_hotcue_8_over.png b/res/skins/Shade/btn/btn_hotcue_8_over.png deleted file mode 100644 index 62eee604366c8cbdff1ff56f148d45be540998f7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 597 zcmV-b0;>IqP)EX>4Tx04R}tkv&MmKpe$iQ%glEA{J3`$WR5rf~bh2R-p(LLaorMgUR(vXws0R zxHt-~1qVMCs}3&Cx;nTDg5U>;i>s5Oix`&UicTt|@eeTcEna!CD@QK7TOgAjzb>gW_ zOXs{#9A-s1OMFf|YS0CVAGxl${KmQ9u)s6JMk+Z+93~bEEv&RKD;g^CByl9GYLqXe zUCwge;;fb`tZ`5N!cb0IUgA2C8D2mgcL-J1D{aW^Rx0|GC${V@atcY%7UyM6-rpMfi_=`YuS=}*$D zO)YW+bZr9{*G)~?11@)fz9(HaWJmJT6!LlC{fxdT1N7Vi9jk6{jeVRx04eG!aRVG2 z0>cH$Uh{Z&duwn1o@w;=1A4Y{s&4pKSO5S324YJ`L;(K){{a7>y{D4^000SaNLh0L z01FZT01FZU(%pXi00007bV*G`2jd482^KV|t8Fp>002=*L_t(I%VU(3SNYFCMZm(q zz`*e2nFCb}laV^Wz{EghFlyi+G>`?KBq!r~tm5DQ2U6z3@Bahw8bY)S3AuD=x{!sC j%SIFSFffpj+EoAmKY10rKu_}%00000NkvXXu0mjfM9BV` diff --git a/res/skins/Shade/btn/btn_hotcue_8_overdown.png b/res/skins/Shade/btn/btn_hotcue_8_overdown.png deleted file mode 100644 index 3f67c1fb925167e364ef7f3ca627b152507b1e8b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 596 zcmV-a0;~OrP)EX>4Tx04R}tkv&MmKpe$iTT4YM4i-^y$WWauh>CR7DionYs1;guFuC*#nlvOS zE{=k0!NHHks)LKOt`4q(Aou~|;_9U6A|?JWDYS_3;J6>}?mh0_0scmXsb<#%plX(p zP9}tGZdC}qB8VXjAdaxaOnpuiQ}7&L_we!cF2=LG&;2=imAuISpFljzbi*RvAfDN@ zbk6(45mu5E;&bA0gDyz?$aUG}H_kbWYY7*5n`d(!Ey()lA#h$6Gs(QqkMnX zWrgz=XSGset$XqphV$CWGS_L2Ac;jRL4*JqHIz|-g&3_GDJIgipYZSxJARQ|GP%lN zZ37qAZB5<-E_Z;zCtWfmM+(sN7Ye}p8GTa@=)VPe*4*A&`#607GSpS-1~@nb z#)_1^?(y#K&ffk#)9UXBleTiS%{j=S00006VoOIv0RI600RN!9r;`8x010qNS#tmY z3labT3lag+-G2N4000McNliru;|CTA7AeZD0qXz&08vRqK~y-)W0aIv`OiQ_z{0@5 zz_9JLF;xun5*YTzI=kOiNl`JX*l#ZR)hQ0BsuEG~ErA=-t6Tskye$U?|v iqX~N$7|2NNDgXev-x3<6IpHe+0000true 0 - skin:/btn/btn_hotcue__down.png + skin:/btn/btn_hotcue_.png skin:/btn/btn_hotcue_.png 1 - skin:/btn/btn_hotcue__overdown.png - skin:/btn/btn_hotcue__over.png + skin:/btn/btn_hotcue_.png + skin:/btn/btn_hotcue_.png [Channel],hotcue__activate diff --git a/res/skins/Shade/style.qss b/res/skins/Shade/style.qss index 9e32accdc92..07da25f2396 100644 --- a/res/skins/Shade/style.qss +++ b/res/skins/Shade/style.qss @@ -552,3 +552,7 @@ QPushButton#pushButtonRepeatPlaylist { image: url(skin:/btn/btn_autodj_repeat_playlist.svg) no-repeat center center; } /* AutoDJ button icons */ + +#HotcueButton { + background-color: #aab2b7; +} From 7930270f5cebbdcec781719baf3d38b755f5f16e Mon Sep 17 00:00:00 2001 From: Ferran Pujol Camins Date: Tue, 12 Nov 2019 22:38:34 +0100 Subject: [PATCH 29/42] Make hotcue button hover highlighting configurable --- res/skins/Deere/style.qss | 4 ++++ src/widget/wwidget.cpp | 10 ++++++---- src/widget/wwidget.h | 3 +++ 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/res/skins/Deere/style.qss b/res/skins/Deere/style.qss index bb6d949d82b..fa6897e4af7 100644 --- a/res/skins/Deere/style.qss +++ b/res/skins/Deere/style.qss @@ -1595,6 +1595,10 @@ WPushButton[value="2"]:hover { border: 0px solid #0080BE; } +#HotcueButton { + qproperty-shouldHighlightBackgroundOnHover: true; +} + #HotcueButton[backgroundIsDark=true][hasBackgroundColor=true] { color: #FDFDFD; } diff --git a/src/widget/wwidget.cpp b/src/widget/wwidget.cpp index f85c3433651..5d523663139 100644 --- a/src/widget/wwidget.cpp +++ b/src/widget/wwidget.cpp @@ -26,7 +26,8 @@ WWidget::WWidget(QWidget* parent, Qt::WindowFlags flags) : QWidget(parent, flags), WBaseWidget(this), m_activeTouchButton(Qt::NoButton), - m_scaleFactor(1.0) { + m_scaleFactor(1.0), + m_bShouldHighlightBackgroundOnHover(false) { m_pTouchShift = new ControlProxy("[Controls]", "touch_shift"); setAttribute(Qt::WA_StaticContents); setAttribute(Qt::WA_AcceptTouchEvents); @@ -47,9 +48,10 @@ double WWidget::getBackgroundColorRgba() const { void WWidget::setBackgroundColorRgba(double rgba) { QColor backgroundColor = QColor::fromRgba(rgba); QColor highlightedBackgroundColor = backgroundColor.lighter(); - QString style = - QString("WWidget { background-color: %1; }" - "WWidget:hover { background-color: %2; }"); + QString style = QString("WWidget { background-color: %1; }"); + if (m_bShouldHighlightBackgroundOnHover) { + style += "WWidget:hover { background-color: %2; }"; + } if (rgba >= 0) { setStyleSheet(style.arg(backgroundColor.name()) diff --git a/src/widget/wwidget.h b/src/widget/wwidget.h index e0956488834..0fbf10631c2 100644 --- a/src/widget/wwidget.h +++ b/src/widget/wwidget.h @@ -47,6 +47,8 @@ class WWidget : public QWidget, public WBaseWidget { Q_PROPERTY(double value READ getControlParameterDisplay); Q_PROPERTY(double backgroundColorRgba READ getBackgroundColorRgba WRITE setBackgroundColorRgba); + Q_PROPERTY(bool shouldHighlightBackgroundOnHover MEMBER + m_bShouldHighlightBackgroundOnHover); Q_PROPERTY(bool hasBackgroundColor READ hasBackgroundColor); Q_PROPERTY(bool backgroundIsDark READ backgroundIsDark); @@ -76,6 +78,7 @@ class WWidget : public QWidget, public WBaseWidget { double m_scaleFactor; double m_backgroundColorRgba; bool m_bBackgroundIsDark; + bool m_bShouldHighlightBackgroundOnHover; }; #endif From 318025e47ec08af2754a78b6a820f55271aeaf14 Mon Sep 17 00:00:00 2001 From: Jan Holthuis Date: Thu, 14 Nov 2019 16:33:19 +0100 Subject: [PATCH 30/42] src/controllers/colorjsproxy: Add nearestColorMidiCode function --- src/controllers/colorjsproxy.cpp | 45 ++++++++++++++++++++++++++++++++ src/controllers/colorjsproxy.h | 1 + 2 files changed, 46 insertions(+) diff --git a/src/controllers/colorjsproxy.cpp b/src/controllers/colorjsproxy.cpp index f7ef331f704..5b8c5163257 100644 --- a/src/controllers/colorjsproxy.cpp +++ b/src/controllers/colorjsproxy.cpp @@ -1,6 +1,24 @@ +#include +#include #include "controllers/colorjsproxy.h" #include "preferences/hotcuecolorpalettesettings.h" +namespace { + double colorDistance(QColor a, QColor b) + { + long mean_red = ((long)a.red() + (long)b.red()) / 2; + long delta_red = (long)a.red() - (long)b.red(); + long delta_green = (long)a.green() - (long)b.green(); + long delta_blue = (long)a.blue() - (long)b.blue(); + return sqrt( + (((512 + mean_red) * delta_red * delta_red) >> 8) + + (4 * delta_green * delta_green) + + (((767 - mean_red) * delta_blue * delta_blue) >> 8) + ); + } +} + + ColorJSProxy::ColorJSProxy(QScriptEngine* pScriptEngine, HotcueColorPaletteSettings colorPaletteSettings) : m_pScriptEngine(pScriptEngine), @@ -25,6 +43,33 @@ QScriptValue ColorJSProxy::colorFromHexCode(uint colorCode) { return jsColor; } +QScriptValue ColorJSProxy::nearestColorMidiCode(uint colorCode, QVariantMap availableColors) { + QColor desiredColor = QColor::fromRgba(colorCode); + uint nearestColorValue = 0; + double nearestColorDistance = qInf(); + QMapIterator it(availableColors); + while (it.hasNext()) { + it.next(); + QColor availableColor(it.key()); + VERIFY_OR_DEBUG_ASSERT(availableColor.isValid()) { + qWarning() << "Received invalid color name from controller script:" << it.key(); + continue; + } + + double distance = colorDistance(desiredColor, availableColor); + if (distance < nearestColorDistance) { + nearestColorDistance = distance; + bool valueOk; + nearestColorValue = it.value().toUInt(&valueOk); + VERIFY_OR_DEBUG_ASSERT(availableColor.isValid()) { + qWarning() << "Failed to convert value to uint:" << it.value(); + } + qDebug() << "Match for " << desiredColor << " -> " << availableColor << "(distance =" << distance << ", value = " << nearestColorValue << ")"; + } + } + return nearestColorValue; +} + QScriptValue ColorJSProxy::makeHotcueColorPalette(QScriptEngine* pScriptEngine, HotcueColorPaletteSettings colorPaletteSettings) { // TODO: make sure we get notified when the palette changes diff --git a/src/controllers/colorjsproxy.h b/src/controllers/colorjsproxy.h index dda4d693798..45bd52569c8 100644 --- a/src/controllers/colorjsproxy.h +++ b/src/controllers/colorjsproxy.h @@ -21,6 +21,7 @@ class ColorJSProxy final : public QObject { // of a color. The parameter is the hexadecimal representation of the color // i.e. 0xAARRGGBB Q_INVOKABLE QScriptValue colorFromHexCode(uint colorCode); + Q_INVOKABLE QScriptValue nearestColorMidiCode(uint colorCode, QVariantMap availableColorCodes); private: QScriptValue makeHotcueColorPalette(QScriptEngine* pScriptEngine, From 77b3f556b497aa2923a7b1c133a8fcfd6590f677 Mon Sep 17 00:00:00 2001 From: Jan Holthuis Date: Thu, 14 Nov 2019 16:34:02 +0100 Subject: [PATCH 31/42] res/controllers/midi-components: Update HotcueButton to use new API --- res/controllers/midi-components-0.0.js | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/res/controllers/midi-components-0.0.js b/res/controllers/midi-components-0.0.js index 1893768c2a6..d07220357c9 100644 --- a/res/controllers/midi-components-0.0.js +++ b/res/controllers/midi-components-0.0.js @@ -330,20 +330,17 @@ this.send(outval); } }, - outputColor: function (id) { - var color = this.colors[id]; - if (color instanceof Array) { - if (color.length !== 3) { - print("ERROR: invalid color array for id: " + id); - return; - } + outputColor: function (colorCode) { + if (this.colors !== undefined) { + var nearestColor = color.nearestColorMidiCode(colorCode, this.colors); + print("COLOR = "+nearestColor); + this.send(nearestColor); + } else { if (this.sendRGB === undefined) { print("ERROR: no function defined for sending RGB colors"); return; } - this.sendRGB(color); - } else if (typeof color === 'number') { - this.send(color); + this.sendRGB(color.colorFromHexCode(colorCode)); } }, connect: function() { From ad38a407a6d05dbddaa691032033362cba045dd8 Mon Sep 17 00:00:00 2001 From: Jan Holthuis Date: Thu, 14 Nov 2019 16:34:44 +0100 Subject: [PATCH 32/42] Roland DJ-505: Update controller script for new color API --- res/controllers/Roland_DJ-505-scripts.js | 41 ++++++++++++++---------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/res/controllers/Roland_DJ-505-scripts.js b/res/controllers/Roland_DJ-505-scripts.js index 2e2777c0e89..428df4bbc81 100644 --- a/res/controllers/Roland_DJ-505-scripts.js +++ b/res/controllers/Roland_DJ-505-scripts.js @@ -941,17 +941,24 @@ DJ505.PadColor = { DIM_MODIFIER: 0x10, }; -DJ505.PadColorMap = [ - DJ505.PadColor.OFF, - DJ505.PadColor.RED, - DJ505.PadColor.GREEN, - DJ505.PadColor.BLUE, - DJ505.PadColor.YELLOW, - DJ505.PadColor.CELESTE, - DJ505.PadColor.PURPLE, - DJ505.PadColor.APRICOT, - DJ505.PadColor.WHITE, -]; +DJ505.PadColorMap = { + '#FFCC0000': DJ505.PadColor.RED, + '#FFCC4400': DJ505.PadColor.CORAL, + '#FFCC8800': DJ505.PadColor.ORANGE, + '#FFCCCC00': DJ505.PadColor.YELLOW, + '#FF88CC00': DJ505.PadColor.GREEN, + '#FF00CC00': DJ505.PadColor.APPLEGREEN, + '#FF00CC88': DJ505.PadColor.AQUAMARINE, + '#FF00CCCC': DJ505.PadColor.TURQUOISE, + '#FF0088CC': DJ505.PadColor.CELESTE, + '#FF0000CC': DJ505.PadColor.BLUE, + '#FF4400CC': DJ505.PadColor.AZURE, + '#FF8800CC': DJ505.PadColor.PURPLE, + '#FFCC00CC': DJ505.PadColor.MAGENTA, + '#FFCC0044': DJ505.PadColor.RED, + '#FFFFCCCC': DJ505.PadColor.APRICOT, + '#FFFFFFFF': DJ505.PadColor.WHITE, +}; DJ505.PadSection = function (deck, offset) { // TODO: Add support for missing modes (flip, slicer, slicerloop) @@ -1190,7 +1197,7 @@ DJ505.HotcueMode = function (deck, offset) { this.ledControl = DJ505.PadMode.HOTCUE; this.color = DJ505.PadColor.WHITE; - var hotcueColors = [this.color].concat(DJ505.PadColorMap.slice(1)); + var hotcueColors = DJ505.PadColorMap; this.pads = new components.ComponentContainer(); for (var i = 0; i <= 7; i++) { this.pads[i] = new components.HotcueButton({ @@ -1226,7 +1233,7 @@ DJ505.CueLoopMode = function (deck, offset) { this.ledControl = DJ505.PadMode.HOTCUE; this.color = DJ505.PadColor.BLUE; - var cueloopColors = [this.color].concat(DJ505.PadColorMap.slice(1)); + var cueloopColors = DJ505.PadColorMap; this.PerformancePad = function(n) { this.midi = [0x94 + offset, 0x14 + n]; this.number = n + 1; @@ -1492,7 +1499,7 @@ DJ505.PitchPlayMode = function (deck, offset) { this.color = DJ505.PadColor.GREEN; this.cuepoint = 1; this.range = PitchPlayRange.MID; - var pitchplayColors = [this.color].concat(DJ505.PadColorMap.slice(1)); + var pitchplayColors = DJ505.PadColorMap; this.PerformancePad = function(n) { this.midi = [0x94 + offset, 0x14 + n]; @@ -1510,10 +1517,10 @@ DJ505.PitchPlayMode = function (deck, offset) { mode: this, outConnect: false, off: DJ505.PadColor.OFF, - outputColor: function(id) { + outputColor: function(colorCode) { // For colored hotcues (shifted only) - var color = this.colors[id]; - this.send((this.mode.cuepoint === this.number) ? color : (color + DJ505.PadColor.DIM_MODIFIER)); + var midiColor = color.nearestColorMidiCode(colorCode, this.colors) + this.send((this.mode.cuepoint === this.number) ? midiColor : (midiColor + DJ505.PadColor.DIM_MODIFIER)); }, unshift: function() { this.outKey = "pitch_adjust"; From b873ca2fe0793f93c83884c5a59b31161957583d Mon Sep 17 00:00:00 2001 From: Jan Holthuis Date: Fri, 15 Nov 2019 23:05:58 +0100 Subject: [PATCH 33/42] src/controllers/colorjsproxy: Fix indentation in colorDistance function --- src/controllers/colorjsproxy.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/controllers/colorjsproxy.cpp b/src/controllers/colorjsproxy.cpp index 5b8c5163257..15620cc23b3 100644 --- a/src/controllers/colorjsproxy.cpp +++ b/src/controllers/colorjsproxy.cpp @@ -6,15 +6,15 @@ namespace { double colorDistance(QColor a, QColor b) { - long mean_red = ((long)a.red() + (long)b.red()) / 2; - long delta_red = (long)a.red() - (long)b.red(); - long delta_green = (long)a.green() - (long)b.green(); - long delta_blue = (long)a.blue() - (long)b.blue(); - return sqrt( - (((512 + mean_red) * delta_red * delta_red) >> 8) + - (4 * delta_green * delta_green) + - (((767 - mean_red) * delta_blue * delta_blue) >> 8) - ); + long mean_red = ((long)a.red() + (long)b.red()) / 2; + long delta_red = (long)a.red() - (long)b.red(); + long delta_green = (long)a.green() - (long)b.green(); + long delta_blue = (long)a.blue() - (long)b.blue(); + return sqrt( + (((512 + mean_red) * delta_red * delta_red) >> 8) + + (4 * delta_green * delta_green) + + (((767 - mean_red) * delta_blue * delta_blue) >> 8) + ); } } From 1d058583b5cab267e651ef095848697a07c23827 Mon Sep 17 00:00:00 2001 From: Jan Holthuis Date: Fri, 15 Nov 2019 23:19:57 +0100 Subject: [PATCH 34/42] src/controllers/colorjsproxy: Add comment to colorDistance algorithm --- src/controllers/colorjsproxy.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/controllers/colorjsproxy.cpp b/src/controllers/colorjsproxy.cpp index 15620cc23b3..c86dd4f4b66 100644 --- a/src/controllers/colorjsproxy.cpp +++ b/src/controllers/colorjsproxy.cpp @@ -6,6 +6,13 @@ namespace { double colorDistance(QColor a, QColor b) { + // This algorithm calculates the distance between two colors. In + // contrast to the L2 norm, this also tries take the human perception + // of colors into account. More accurate algorithms like the CIELAB2000 + // Delta-E rely on sophisticated color space conversions and need a lot + // of costly computations. In contrast, this is a low-cost + // approximation and should be sufficently accurate. + // More details: https://www.compuphase.com/cmetric.htm long mean_red = ((long)a.red() + (long)b.red()) / 2; long delta_red = (long)a.red() - (long)b.red(); long delta_green = (long)a.green() - (long)b.green(); From 2b61025cded6054375928cc3695b80dd115aecc5 Mon Sep 17 00:00:00 2001 From: Jan Holthuis Date: Sat, 16 Nov 2019 04:18:36 +0100 Subject: [PATCH 35/42] src/controllers: Add ColorMapper class for controller scripts --- build/depends.py | 2 + src/controllers/colorjsproxy.cpp | 52 ----------------------- src/controllers/colorjsproxy.h | 1 - src/controllers/colormapper.cpp | 59 ++++++++++++++++++++++++++ src/controllers/colormapper.h | 30 +++++++++++++ src/controllers/colormapperjsproxy.cpp | 37 ++++++++++++++++ src/controllers/colormapperjsproxy.h | 29 +++++++++++++ src/controllers/controllerengine.cpp | 6 ++- 8 files changed, 162 insertions(+), 54 deletions(-) create mode 100644 src/controllers/colormapper.cpp create mode 100644 src/controllers/colormapper.h create mode 100644 src/controllers/colormapperjsproxy.cpp create mode 100644 src/controllers/colormapperjsproxy.h diff --git a/build/depends.py b/build/depends.py index 202419e6199..ac5e778feee 100644 --- a/build/depends.py +++ b/build/depends.py @@ -913,6 +913,8 @@ def sources(self, build): "src/controllers/softtakeover.cpp", "src/controllers/keyboard/keyboardeventfilter.cpp", "src/controllers/colorjsproxy.cpp", + "src/controllers/colormapper.cpp", + "src/controllers/colormapperjsproxy.cpp", "src/main.cpp", "src/mixxx.cpp", diff --git a/src/controllers/colorjsproxy.cpp b/src/controllers/colorjsproxy.cpp index c86dd4f4b66..f7ef331f704 100644 --- a/src/controllers/colorjsproxy.cpp +++ b/src/controllers/colorjsproxy.cpp @@ -1,31 +1,6 @@ -#include -#include #include "controllers/colorjsproxy.h" #include "preferences/hotcuecolorpalettesettings.h" -namespace { - double colorDistance(QColor a, QColor b) - { - // This algorithm calculates the distance between two colors. In - // contrast to the L2 norm, this also tries take the human perception - // of colors into account. More accurate algorithms like the CIELAB2000 - // Delta-E rely on sophisticated color space conversions and need a lot - // of costly computations. In contrast, this is a low-cost - // approximation and should be sufficently accurate. - // More details: https://www.compuphase.com/cmetric.htm - long mean_red = ((long)a.red() + (long)b.red()) / 2; - long delta_red = (long)a.red() - (long)b.red(); - long delta_green = (long)a.green() - (long)b.green(); - long delta_blue = (long)a.blue() - (long)b.blue(); - return sqrt( - (((512 + mean_red) * delta_red * delta_red) >> 8) + - (4 * delta_green * delta_green) + - (((767 - mean_red) * delta_blue * delta_blue) >> 8) - ); - } -} - - ColorJSProxy::ColorJSProxy(QScriptEngine* pScriptEngine, HotcueColorPaletteSettings colorPaletteSettings) : m_pScriptEngine(pScriptEngine), @@ -50,33 +25,6 @@ QScriptValue ColorJSProxy::colorFromHexCode(uint colorCode) { return jsColor; } -QScriptValue ColorJSProxy::nearestColorMidiCode(uint colorCode, QVariantMap availableColors) { - QColor desiredColor = QColor::fromRgba(colorCode); - uint nearestColorValue = 0; - double nearestColorDistance = qInf(); - QMapIterator it(availableColors); - while (it.hasNext()) { - it.next(); - QColor availableColor(it.key()); - VERIFY_OR_DEBUG_ASSERT(availableColor.isValid()) { - qWarning() << "Received invalid color name from controller script:" << it.key(); - continue; - } - - double distance = colorDistance(desiredColor, availableColor); - if (distance < nearestColorDistance) { - nearestColorDistance = distance; - bool valueOk; - nearestColorValue = it.value().toUInt(&valueOk); - VERIFY_OR_DEBUG_ASSERT(availableColor.isValid()) { - qWarning() << "Failed to convert value to uint:" << it.value(); - } - qDebug() << "Match for " << desiredColor << " -> " << availableColor << "(distance =" << distance << ", value = " << nearestColorValue << ")"; - } - } - return nearestColorValue; -} - QScriptValue ColorJSProxy::makeHotcueColorPalette(QScriptEngine* pScriptEngine, HotcueColorPaletteSettings colorPaletteSettings) { // TODO: make sure we get notified when the palette changes diff --git a/src/controllers/colorjsproxy.h b/src/controllers/colorjsproxy.h index 45bd52569c8..dda4d693798 100644 --- a/src/controllers/colorjsproxy.h +++ b/src/controllers/colorjsproxy.h @@ -21,7 +21,6 @@ class ColorJSProxy final : public QObject { // of a color. The parameter is the hexadecimal representation of the color // i.e. 0xAARRGGBB Q_INVOKABLE QScriptValue colorFromHexCode(uint colorCode); - Q_INVOKABLE QScriptValue nearestColorMidiCode(uint colorCode, QVariantMap availableColorCodes); private: QScriptValue makeHotcueColorPalette(QScriptEngine* pScriptEngine, diff --git a/src/controllers/colormapper.cpp b/src/controllers/colormapper.cpp new file mode 100644 index 00000000000..3caa515806e --- /dev/null +++ b/src/controllers/colormapper.cpp @@ -0,0 +1,59 @@ +#include "controllers/colormapper.h" + +#include +#include + +#include "util/debug.h" + +namespace { +double colorDistance(QRgb a, QRgb b) { + // This algorithm calculates the distance between two colors. In + // contrast to the L2 norm, this also tries take the human perception + // of colors into account. More accurate algorithms like the CIELAB2000 + // Delta-E rely on sophisticated color space conversions and need a lot + // of costly computations. In contrast, this is a low-cost + // approximation and should be sufficently accurate. + // More details: https://www.compuphase.com/cmetric.htm + long mean_red = ((long)qRed(a) + (long)qRed(b)) / 2; + long delta_red = (long)qRed(a) - (long)qRed(b); + long delta_green = (long)qGreen(a) - (long)qGreen(b); + long delta_blue = (long)qBlue(a) - (long)qBlue(b); + return sqrt( + (((512 + mean_red) * delta_red * delta_red) >> 8) + + (4 * delta_green * delta_green) + + (((767 - mean_red) * delta_blue * delta_blue) >> 8)); +} +} // namespace + +QPair ColorMapper::getNearestColor(QRgb desiredColor) { + // If desired color is already in cache, use cache entry + QMap::const_iterator i = m_cache.find(desiredColor); + QMap::const_iterator j; + if (i != m_cache.constEnd()) { + j = m_availableColors.find(i.value()); + DEBUG_ASSERT(j != m_availableColors.constEnd()); + qDebug() << "ColorMapper cache hit for" << desiredColor << ":" + << "Color =" << j.key() << "," + << "Value =" << j.value(); + return QPair(j.key(), j.value()); + } + + // Color is not cached + QMap::const_iterator nearestColorIterator; + double nearestColorDistance = qInf(); + for (j = m_availableColors.constBegin(); j != m_availableColors.constEnd(); j++) { + QRgb availableColor = j.key(); + double distance = colorDistance(desiredColor, availableColor); + if (distance < nearestColorDistance) { + nearestColorDistance = distance; + nearestColorIterator = j; + } + } + + DEBUG_ASSERT(nearestColorDistance < qInf()); + qDebug() << "ColorMapper found matching color for" << desiredColor << ":" + << "Color =" << nearestColorIterator.key() << "," + << "Value =" << nearestColorIterator.value(); + m_cache.insert(desiredColor, nearestColorIterator.key()); + return QPair(nearestColorIterator.key(), nearestColorIterator.value()); +} diff --git a/src/controllers/colormapper.h b/src/controllers/colormapper.h new file mode 100644 index 00000000000..c138edbf38d --- /dev/null +++ b/src/controllers/colormapper.h @@ -0,0 +1,30 @@ +#ifndef COLORMAPPER_H +#define COLORMAPPER_H + +#include +#include +#include +#include +#include + +#include "util/assert.h" + +class ColorMapper final : public QObject { + Q_OBJECT + public: + ColorMapper() = delete; + ColorMapper(const QMap availableColors) + : m_availableColors(availableColors) { + DEBUG_ASSERT(!m_availableColors.isEmpty()); + } + + ~ColorMapper() = default; + + QPair getNearestColor(QRgb desiredColor); + + private: + const QMap m_availableColors; + QMap m_cache; +}; + +#endif /* COLORMAPPER_H */ diff --git a/src/controllers/colormapperjsproxy.cpp b/src/controllers/colormapperjsproxy.cpp new file mode 100644 index 00000000000..3e1db371780 --- /dev/null +++ b/src/controllers/colormapperjsproxy.cpp @@ -0,0 +1,37 @@ +#include + +#include "controllers/colormapperjsproxy.h" + +ColorMapperJSProxy::ColorMapperJSProxy(QScriptEngine* pScriptEngine, QMap availableColors) + : m_pScriptEngine(pScriptEngine) { + m_colorMapper = new ColorMapper(availableColors); +} + +QScriptValue ColorMapperJSProxy::getNearestColor(uint colorCode) { + QPair result = m_colorMapper->getNearestColor(static_cast(colorCode)); + return m_pScriptEngine->toScriptValue(result.second); +} + +QScriptValue ColorMapperJSProxyConstructor(QScriptContext* pScriptContext, QScriptEngine* pScriptEngine) { + QMap availableColors; + DEBUG_ASSERT(pScriptContext->argumentCount() == 1); + QScriptValueIterator it(pScriptContext->argument(0)); + while (it.hasNext()) { + it.next(); + DEBUG_ASSERT(!it.value().isObject()); + QColor color(it.name()); + VERIFY_OR_DEBUG_ASSERT(color.isValid()) { + qWarning() << "Received invalid color name from controller script:" << it.name(); + continue; + } + availableColors.insert(color.rgb(), it.value().toVariant()); + } + + if (availableColors.isEmpty()) { + qWarning() << "Failed to create ColorMapper object: available colors mustn't be empty!"; + return pScriptEngine->undefinedValue(); + } + + QObject* colorMapper = new ColorMapperJSProxy(pScriptEngine, availableColors); + return pScriptEngine->newQObject(colorMapper, QScriptEngine::ScriptOwnership); +} diff --git a/src/controllers/colormapperjsproxy.h b/src/controllers/colormapperjsproxy.h new file mode 100644 index 00000000000..eb5643e27bf --- /dev/null +++ b/src/controllers/colormapperjsproxy.h @@ -0,0 +1,29 @@ +#ifndef COLORMAPPERJS_H +#define COLORMAPPERJS_H + +#include +#include + +#include "controllers/colormapper.h" + +class ColorMapperJSProxy final : public QObject { + Q_OBJECT + public: + ColorMapperJSProxy() = delete; + ColorMapperJSProxy(QScriptEngine* pScriptEngine, QMap availableColors); + + ~ColorMapperJSProxy() { + delete m_colorMapper; + }; + + public slots: + QScriptValue getNearestColor(uint ColorCode); + + private: + QScriptEngine* m_pScriptEngine; + ColorMapper* m_colorMapper; +}; + +QScriptValue ColorMapperJSProxyConstructor(QScriptContext* context, QScriptEngine* engine); + +#endif /* COLORMAPPER_H */ diff --git a/src/controllers/controllerengine.cpp b/src/controllers/controllerengine.cpp index 9bee7c4209e..e01e8943a1c 100644 --- a/src/controllers/controllerengine.cpp +++ b/src/controllers/controllerengine.cpp @@ -6,8 +6,8 @@ email : spappalardo@mixxx.org ***************************************************************************/ +#include "controllers/colormapperjsproxy.h" #include "controllers/controllerengine.h" - #include "controllers/controller.h" #include "controllers/controllerdebug.h" #include "control/controlobject.h" @@ -219,6 +219,10 @@ void ControllerEngine::initializeScriptEngine() { m_pEngine, HotcueColorPaletteSettings(m_pConfig)); engineGlobalObject.setProperty("color", m_pEngine->newQObject(m_pColorJSProxy.get())); + QScriptValue constructor = m_pEngine->newFunction(ColorMapperJSProxyConstructor); + QScriptValue metaObject = m_pEngine->newQMetaObject(&ColorMapperJSProxy::staticMetaObject, constructor); + engineGlobalObject.setProperty("ColorMapper", metaObject); + m_pBaClass = new ByteArrayClass(m_pEngine); engineGlobalObject.setProperty("ByteArray", m_pBaClass->constructor()); } From a24b22a91daaebcaa74cedd87154c550562f2667 Mon Sep 17 00:00:00 2001 From: Jan Holthuis Date: Sat, 16 Nov 2019 04:19:45 +0100 Subject: [PATCH 36/42] res/controllers/midi-components: Add support for ColorMapper class --- res/controllers/midi-components-0.0.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/controllers/midi-components-0.0.js b/res/controllers/midi-components-0.0.js index d07220357c9..2d3d0f1943a 100644 --- a/res/controllers/midi-components-0.0.js +++ b/res/controllers/midi-components-0.0.js @@ -332,7 +332,7 @@ }, outputColor: function (colorCode) { if (this.colors !== undefined) { - var nearestColor = color.nearestColorMidiCode(colorCode, this.colors); + var nearestColor = this.colors.getNearestColor(colorCode); print("COLOR = "+nearestColor); this.send(nearestColor); } else { From 3f42532ed8b7148d679f09d419906dc97a8d7ab6 Mon Sep 17 00:00:00 2001 From: Jan Holthuis Date: Sat, 16 Nov 2019 04:20:28 +0100 Subject: [PATCH 37/42] Roland DJ-505: Add support for ColorMapper class --- res/controllers/Roland_DJ-505-scripts.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/res/controllers/Roland_DJ-505-scripts.js b/res/controllers/Roland_DJ-505-scripts.js index 428df4bbc81..6e010d76701 100644 --- a/res/controllers/Roland_DJ-505-scripts.js +++ b/res/controllers/Roland_DJ-505-scripts.js @@ -941,7 +941,7 @@ DJ505.PadColor = { DIM_MODIFIER: 0x10, }; -DJ505.PadColorMap = { +DJ505.PadColorMap = new ColorMapper({ '#FFCC0000': DJ505.PadColor.RED, '#FFCC4400': DJ505.PadColor.CORAL, '#FFCC8800': DJ505.PadColor.ORANGE, @@ -958,7 +958,7 @@ DJ505.PadColorMap = { '#FFCC0044': DJ505.PadColor.RED, '#FFFFCCCC': DJ505.PadColor.APRICOT, '#FFFFFFFF': DJ505.PadColor.WHITE, -}; +}); DJ505.PadSection = function (deck, offset) { // TODO: Add support for missing modes (flip, slicer, slicerloop) @@ -1519,7 +1519,7 @@ DJ505.PitchPlayMode = function (deck, offset) { off: DJ505.PadColor.OFF, outputColor: function(colorCode) { // For colored hotcues (shifted only) - var midiColor = color.nearestColorMidiCode(colorCode, this.colors) + var midiColor = this.colors.getNearestColor(colorCode); this.send((this.mode.cuepoint === this.number) ? midiColor : (midiColor + DJ505.PadColor.DIM_MODIFIER)); }, unshift: function() { From efde9e45ad7906a4e6915cb5053633e1d8fd967f Mon Sep 17 00:00:00 2001 From: Jan Holthuis Date: Tue, 19 Nov 2019 18:48:40 +0100 Subject: [PATCH 38/42] controllers/colormapperjsproxy: Add separate getNearestValue method --- res/controllers/midi-components-0.0.js | 6 +++--- src/controllers/colormapperjsproxy.cpp | 10 ++++++++++ src/controllers/colormapperjsproxy.h | 1 + 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/res/controllers/midi-components-0.0.js b/res/controllers/midi-components-0.0.js index 2d3d0f1943a..1af0bf8454a 100644 --- a/res/controllers/midi-components-0.0.js +++ b/res/controllers/midi-components-0.0.js @@ -332,9 +332,9 @@ }, outputColor: function (colorCode) { if (this.colors !== undefined) { - var nearestColor = this.colors.getNearestColor(colorCode); - print("COLOR = "+nearestColor); - this.send(nearestColor); + var nearestColorValue = this.colors.getNearestValue(colorCode); + print("COLOR = "+nearestColorValue); + this.send(nearestColorValue); } else { if (this.sendRGB === undefined) { print("ERROR: no function defined for sending RGB colors"); diff --git a/src/controllers/colormapperjsproxy.cpp b/src/controllers/colormapperjsproxy.cpp index 3e1db371780..f806cb9752e 100644 --- a/src/controllers/colormapperjsproxy.cpp +++ b/src/controllers/colormapperjsproxy.cpp @@ -8,6 +8,16 @@ ColorMapperJSProxy::ColorMapperJSProxy(QScriptEngine* pScriptEngine, QMap result = m_colorMapper->getNearestColor(static_cast(colorCode)); + QScriptValue jsColor = m_pScriptEngine->newObject(); + jsColor.setProperty("red", qRed(result.first)); + jsColor.setProperty("green", qGreen(result.first)); + jsColor.setProperty("blue", qBlue(result.first)); + jsColor.setProperty("alpha", qAlpha(result.first)); + return jsColor; +} + +QScriptValue ColorMapperJSProxy::getNearestValue(uint colorCode) { QPair result = m_colorMapper->getNearestColor(static_cast(colorCode)); return m_pScriptEngine->toScriptValue(result.second); } diff --git a/src/controllers/colormapperjsproxy.h b/src/controllers/colormapperjsproxy.h index eb5643e27bf..367d13bba3e 100644 --- a/src/controllers/colormapperjsproxy.h +++ b/src/controllers/colormapperjsproxy.h @@ -18,6 +18,7 @@ class ColorMapperJSProxy final : public QObject { public slots: QScriptValue getNearestColor(uint ColorCode); + QScriptValue getNearestValue(uint ColorCode); private: QScriptEngine* m_pScriptEngine; From b94a72f7d52865078e8ac5be42e1158a2c2da89d Mon Sep 17 00:00:00 2001 From: Jan Holthuis Date: Tue, 19 Nov 2019 18:49:12 +0100 Subject: [PATCH 39/42] Roland DJ-505: Switch to ControllerMapper::getNearestValue --- res/controllers/Roland_DJ-505-scripts.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/controllers/Roland_DJ-505-scripts.js b/res/controllers/Roland_DJ-505-scripts.js index 6e010d76701..659b5cb54fc 100644 --- a/res/controllers/Roland_DJ-505-scripts.js +++ b/res/controllers/Roland_DJ-505-scripts.js @@ -1519,7 +1519,7 @@ DJ505.PitchPlayMode = function (deck, offset) { off: DJ505.PadColor.OFF, outputColor: function(colorCode) { // For colored hotcues (shifted only) - var midiColor = this.colors.getNearestColor(colorCode); + var midiColor = this.colors.getNearestValue(colorCode); this.send((this.mode.cuepoint === this.number) ? midiColor : (midiColor + DJ505.PadColor.DIM_MODIFIER)); }, unshift: function() { From 73de6dc30a02e7cc78a85d86a9d34826b5c817ef Mon Sep 17 00:00:00 2001 From: Jan Holthuis Date: Tue, 19 Nov 2019 18:54:41 +0100 Subject: [PATCH 40/42] midi-components: Remove default value for HotcueButton.colors --- res/controllers/midi-components-0.0.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/res/controllers/midi-components-0.0.js b/res/controllers/midi-components-0.0.js index 1af0bf8454a..79a577d88dc 100644 --- a/res/controllers/midi-components-0.0.js +++ b/res/controllers/midi-components-0.0.js @@ -296,9 +296,6 @@ } if (options.colors !== undefined || options.sendRGB !== undefined) { this.colorKey = 'hotcue_' + options.number + '_color'; - if (options.colors === undefined) { - options.colors = color.hotcueColorPalette(); - } } this.number = options.number; this.outKey = 'hotcue_' + this.number + '_enabled'; From d4cda183fd7779242c9c6ed9c6c399f843d3000e Mon Sep 17 00:00:00 2001 From: Jan Holthuis Date: Tue, 19 Nov 2019 18:55:23 +0100 Subject: [PATCH 41/42] controllers/midi-components: Rename HotcueButton.colors to .colorMapper --- res/controllers/midi-components-0.0.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/res/controllers/midi-components-0.0.js b/res/controllers/midi-components-0.0.js index 79a577d88dc..c4685e75ea5 100644 --- a/res/controllers/midi-components-0.0.js +++ b/res/controllers/midi-components-0.0.js @@ -294,7 +294,7 @@ print('ERROR: No hotcue number specified for new HotcueButton.'); return; } - if (options.colors !== undefined || options.sendRGB !== undefined) { + if (options.colorMapper !== undefined || options.sendRGB !== undefined) { this.colorKey = 'hotcue_' + options.number + '_color'; } this.number = options.number; @@ -328,8 +328,8 @@ } }, outputColor: function (colorCode) { - if (this.colors !== undefined) { - var nearestColorValue = this.colors.getNearestValue(colorCode); + if (this.colorMapper !== undefined) { + var nearestColorValue = this.colorMapper.getNearestValue(colorCode); print("COLOR = "+nearestColorValue); this.send(nearestColorValue); } else { From c6c467bc86472273534886fafb406cb29f476dbf Mon Sep 17 00:00:00 2001 From: Jan Holthuis Date: Tue, 19 Nov 2019 18:59:18 +0100 Subject: [PATCH 42/42] Roland DJ-505: Rename Component.colors to .colorMapper --- res/controllers/Roland_DJ-505-scripts.js | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/res/controllers/Roland_DJ-505-scripts.js b/res/controllers/Roland_DJ-505-scripts.js index 659b5cb54fc..25efbbfb523 100644 --- a/res/controllers/Roland_DJ-505-scripts.js +++ b/res/controllers/Roland_DJ-505-scripts.js @@ -1197,7 +1197,6 @@ DJ505.HotcueMode = function (deck, offset) { this.ledControl = DJ505.PadMode.HOTCUE; this.color = DJ505.PadColor.WHITE; - var hotcueColors = DJ505.PadColorMap; this.pads = new components.ComponentContainer(); for (var i = 0; i <= 7; i++) { this.pads[i] = new components.HotcueButton({ @@ -1209,7 +1208,7 @@ DJ505.HotcueMode = function (deck, offset) { group: deck.currentDeck, on: this.color, off: this.color + DJ505.PadColor.DIM_MODIFIER, - colors: hotcueColors, + colorMapper: DJ505.PadColorMap, outConnect: false, }); } @@ -1233,7 +1232,6 @@ DJ505.CueLoopMode = function (deck, offset) { this.ledControl = DJ505.PadMode.HOTCUE; this.color = DJ505.PadColor.BLUE; - var cueloopColors = DJ505.PadColorMap; this.PerformancePad = function(n) { this.midi = [0x94 + offset, 0x14 + n]; this.number = n + 1; @@ -1248,7 +1246,7 @@ DJ505.CueLoopMode = function (deck, offset) { group: deck.currentDeck, on: this.color, off: this.color + DJ505.PadColor.DIM_MODIFIER, - colors: cueloopColors, + colorMapper: DJ505.PadColorMap, outConnect: false, unshift: function() { this.input = function (channel, control, value, status, group) { @@ -1499,13 +1497,12 @@ DJ505.PitchPlayMode = function (deck, offset) { this.color = DJ505.PadColor.GREEN; this.cuepoint = 1; this.range = PitchPlayRange.MID; - var pitchplayColors = DJ505.PadColorMap; this.PerformancePad = function(n) { this.midi = [0x94 + offset, 0x14 + n]; this.number = n + 1; this.on = this.color + DJ505.PadColor.DIM_MODIFIER; - this.colors = pitchplayColors; + this.colorMapper = DJ505.PadColorMap; this.colorKey = 'hotcue_' + this.number + '_color'; components.Button.call(this); }; @@ -1519,7 +1516,7 @@ DJ505.PitchPlayMode = function (deck, offset) { off: DJ505.PadColor.OFF, outputColor: function(colorCode) { // For colored hotcues (shifted only) - var midiColor = this.colors.getNearestValue(colorCode); + var midiColor = this.colorMapper.getNearestValue(colorCode); this.send((this.mode.cuepoint === this.number) ? midiColor : (midiColor + DJ505.PadColor.DIM_MODIFIER)); }, unshift: function() {