diff --git a/CMakeLists.txt b/CMakeLists.txt
index e6e8f9303f9a..9ec3eb023f58 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -188,7 +188,8 @@ add_library(mixxx-lib STATIC EXCLUDE_FROM_ALL
src/control/controlproxy.cpp
src/control/controlpushbutton.cpp
src/control/controlttrotary.cpp
- src/controllers/colorjsproxy.cpp
+ src/controllers/colormapper.cpp
+ src/controllers/colormapperjsproxy.cpp
src/controllers/controller.cpp
src/controllers/controllerdebug.cpp
src/controllers/controllerengine.cpp
@@ -334,7 +335,6 @@ add_library(mixxx-lib STATIC EXCLUDE_FROM_ALL
src/errordialoghandler.cpp
src/library/analysisfeature.cpp
src/library/analysislibrarytablemodel.cpp
- src/library/trackloader.cpp
src/library/autodj/autodjfeature.cpp
src/library/autodj/autodjprocessor.cpp
src/library/autodj/dlgautodj.cpp
@@ -431,6 +431,7 @@ add_library(mixxx-lib STATIC EXCLUDE_FROM_ALL
src/library/tableitemdelegate.cpp
src/library/trackcollection.cpp
src/library/trackcollectionmanager.cpp
+ src/library/trackloader.cpp
src/library/traktor/traktorfeature.cpp
src/library/treeitem.cpp
src/library/treeitemmodel.cpp
@@ -497,6 +498,7 @@ add_library(mixxx-lib STATIC EXCLUDE_FROM_ALL
src/preferences/dialog/dlgprefwaveformdlg.ui
src/preferences/dlgpreferencepage.cpp
src/preferences/effectsettingsmodel.cpp
+ src/preferences/colorpalettesettings.cpp
src/preferences/replaygainsettings.cpp
src/preferences/settingsmanager.cpp
src/preferences/upgrade.cpp
@@ -559,7 +561,7 @@ add_library(mixxx-lib STATIC EXCLUDE_FROM_ALL
src/util/cache.cpp
src/util/cmdlineargs.cpp
src/util/color/color.cpp
- src/util/color/predefinedcolor.cpp
+ src/util/color/colorpalette.cpp
src/util/console.cpp
src/util/db/dbconnection.cpp
src/util/db/dbconnectionpool.cpp
@@ -946,6 +948,9 @@ add_executable(mixxx-test
src/test/broadcastsettings_test.cpp
src/test/cache_test.cpp
src/test/channelhandle_test.cpp
+ src/test/colorconfig_test.cpp
+ src/test/colormapperjsproxy_test.cpp
+ src/test/colorpalette_test.cpp
src/test/compatibility_test.cpp
src/test/configobject_test.cpp
src/test/controller_preset_validation_test.cpp
diff --git a/build/depends.py b/build/depends.py
index 4740b2c5c087..e13d2df5b3b2 100644
--- a/build/depends.py
+++ b/build/depends.py
@@ -790,6 +790,7 @@ def sources(self, build):
"src/preferences/effectsettingsmodel.cpp",
"src/preferences/broadcastprofile.cpp",
"src/preferences/upgrade.cpp",
+ "src/preferences/colorpalettesettings.cpp",
"src/preferences/dlgpreferencepage.cpp",
"src/effects/effectmanifest.cpp",
@@ -925,7 +926,8 @@ def sources(self, build):
"src/controllers/midi/midioutputhandler.cpp",
"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",
@@ -1283,6 +1285,7 @@ def sources(self, build):
"src/util/cache.cpp",
"src/util/console.cpp",
"src/util/color/color.cpp",
+ "src/util/color/colorpalette.cpp",
"src/util/db/dbconnection.cpp",
"src/util/db/dbconnectionpool.cpp",
"src/util/db/dbconnectionpooler.cpp",
@@ -1309,8 +1312,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/res/controllers/.eslintrc.json b/res/controllers/.eslintrc.json
index 5c726c8bc03b..2a6722aac48c 100644
--- a/res/controllers/.eslintrc.json
+++ b/res/controllers/.eslintrc.json
@@ -1,7 +1,7 @@
{
"globals": {
+ "ColorMapper": "readonly",
"_": "readonly",
- "color": "readonly",
"components": "readonly",
"engine": "readonly",
"midi": "readonly",
@@ -10,6 +10,8 @@
"arrayContains": "readonly",
"secondstominutes": "readonly",
"msecondstominutes": "readonly",
+ "colorCodeToObject": "readonly",
+ "colorCodeFromObject": "readonly",
"script": "readonly",
"bpm": "readonly",
"ButtonState": "readonly",
diff --git a/res/controllers/Roland_DJ-505-scripts.js b/res/controllers/Roland_DJ-505-scripts.js
index be2ddb4f7728..ceee4d06b3be 100644
--- a/res/controllers/Roland_DJ-505-scripts.js
+++ b/res/controllers/Roland_DJ-505-scripts.js
@@ -970,17 +970,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 = new ColorMapper({
+ "#CC0000": DJ505.PadColor.RED,
+ "#CC4400": DJ505.PadColor.CORAL,
+ "#CC8800": DJ505.PadColor.ORANGE,
+ "#CCCC00": DJ505.PadColor.YELLOW,
+ "#88CC00": DJ505.PadColor.GREEN,
+ "#00CC00": DJ505.PadColor.APPLEGREEN,
+ "#00CC88": DJ505.PadColor.AQUAMARINE,
+ "#00CCCC": DJ505.PadColor.TURQUOISE,
+ "#0088CC": DJ505.PadColor.CELESTE,
+ "#0000CC": DJ505.PadColor.BLUE,
+ "#4400CC": DJ505.PadColor.AZURE,
+ "#8800CC": DJ505.PadColor.PURPLE,
+ "#CC00CC": DJ505.PadColor.MAGENTA,
+ "#CC0044": DJ505.PadColor.RED,
+ "#FFCCCC": DJ505.PadColor.APRICOT,
+ "#FFFFFF": DJ505.PadColor.WHITE,
+});
DJ505.PadSection = function(deck, offset) {
// TODO: Add support for missing modes (flip, slicer, slicerloop)
@@ -1239,7 +1246,6 @@ DJ505.HotcueMode = function(deck, offset) {
this.ledControl = DJ505.PadMode.HOTCUE;
this.color = DJ505.PadColor.WHITE;
- var hotcueColors = [this.color].concat(DJ505.PadColorMap.slice(1));
this.pads = new components.ComponentContainer();
for (var i = 0; i <= 7; i++) {
this.pads[i] = new components.HotcueButton({
@@ -1251,7 +1257,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,
});
}
@@ -1287,7 +1293,6 @@ DJ505.CueLoopMode = function(deck, offset) {
this.ledControl = DJ505.PadMode.HOTCUE;
this.color = DJ505.PadColor.BLUE;
- var cueloopColors = [this.color].concat(DJ505.PadColorMap.slice(1));
this.PerformancePad = function(n) {
this.midi = [0x94 + offset, 0x14 + n];
this.number = n + 1;
@@ -1303,7 +1308,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) {
@@ -1596,14 +1601,13 @@ 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));
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.colorIdKey = "hotcue_" + this.number + "_color_id";
+ this.colorMapper = DJ505.PadColorMap;
+ this.colorKey = "hotcue_" + this.number + "_color";
components.Button.call(this);
};
this.PerformancePad.prototype = new components.Button({
@@ -1614,10 +1618,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 = this.colorMapper.getNearestValue(colorCode);
+ this.send((this.mode.cuepoint === this.number) ? midiColor : (midiColor + DJ505.PadColor.DIM_MODIFIER));
},
unshift: function() {
this.outKey = "pitch_adjust";
@@ -1665,8 +1669,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);
}
@@ -1676,13 +1680,13 @@ DJ505.PitchPlayMode = function(deck, offset) {
var previousCuepoint = this.mode.cuepoint;
this.mode.cuepoint = this.number;
this.mode.pads[previousCuepoint - 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/common-controller-scripts.js b/res/controllers/common-controller-scripts.js
index 5f8346f32c99..a7421d613f68 100644
--- a/res/controllers/common-controller-scripts.js
+++ b/res/controllers/common-controller-scripts.js
@@ -5,6 +5,8 @@
arrayContains:off
secondstominutes:off
msecondstominutes:off
+ colorCodeToObject:off
+ colorCodeFromObject:off
script:off
bpm:off
ButtonState:off
@@ -102,6 +104,24 @@ var msecondstominutes = function(msecs) {
+ (msecs < 10 ? "0" + msecs : msecs);
};
+// Converts an object with "red", "green" and "blue" properties (value range
+// 0-255) into an RGB color code (e.g. 0xFF0000).
+// eslint-disable-next-line no-unused-vars
+var colorCodeFromObject = function(color) {
+ return ((color.red & 0xFF) << 16 | (color.green & 0xFF) << 8 | (color.blue & 0xFF));
+};
+
+// Converts an RGB color code (e.g. 0xFF0000) into an object with "red",
+// "green" and "blue" properties (value range 0-255).
+// eslint-disable-next-line no-unused-vars
+var colorCodeToObject = function(colorCode) {
+ return {
+ "red": (colorCode >> 16) & 0xFF,
+ "green": (colorCode >> 8) & 0xFF,
+ "blue": colorCode & 0xFF,
+ };
+};
+
var script = function() {
};
diff --git a/res/controllers/midi-components-0.0.js b/res/controllers/midi-components-0.0.js
index 2657bb2176e0..3a87fec128d3 100644
--- a/res/controllers/midi-components-0.0.js
+++ b/res/controllers/midi-components-0.0.js
@@ -294,11 +294,8 @@
print("ERROR: No hotcue number specified for new HotcueButton.");
return;
}
- if (options.colors !== undefined || options.sendRGB !== undefined) {
- this.colorIdKey = "hotcue_" + options.number + "_color_id";
- if (options.colors === undefined) {
- options.colors = color.predefinedColorsList();
- }
+ if (options.colorMapper !== undefined || options.sendRGB !== undefined) {
+ this.colorKey = "hotcue_" + options.number + "_color";
}
this.number = options.number;
this.outKey = "hotcue_" + this.number + "_enabled";
@@ -311,45 +308,48 @@
shift: function() {
this.inKey = "hotcue_" + this.number + "_clear";
},
- getColor: function() {
- if (this.colorIdKey !== undefined) {
- return color.predefinedColorFromId(engine.getValue(this.group, this.colorIdKey));
- } else {
- return null;
- }
- },
output: function(value) {
var outval = this.outValueScale(value);
- // WARNING: outputColor only handles hotcueColors
+ // NOTE: outputColor only handles hotcueColors
// 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);
}
},
- 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;
- }
- 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);
+ outputColor: function(colorCode) {
+ // Sends the color from the colorCode to the controller. This
+ // method will not be called if no colorKey has been specified.
+ if (colorCode === undefined || colorCode < 0 || colorCode > 0xFFFFFF) {
+ print("Ignoring invalid color code '" + colorCode + "' in outputColor()");
+ return;
+ }
+
+ if (this.colorMapper !== undefined) {
+ // This HotcueButton holds a reference to a ColorMapper. This means
+ // that the controller only supports a fixed set of colors, so we
+ // get the MIDI value for the nearest supported color and send it.
+ var nearestColorValue = this.colorMapper.getValueForNearestColor(colorCode);
+ this.send(nearestColorValue);
+ } else {
+ // Since outputColor has been called but no ColorMapper is
+ // available, we can assume that controller supports arbitrary
+ // RGB color output.
+ this.sendRGB(colorCodeToObject(colorCode));
}
},
+ sendRGB: function(_colorObject) {
+ // This method needs to be overridden in controller mappings,
+ // because the procedure is controller-dependent.
+ throw Error("sendRGB(colorObject) not implemented - unable to send RGB colors!");
+ },
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);
}
@@ -732,14 +732,14 @@
if (engine.getValue(eu.group, "show_focus") > 0) {
engine.setValue(eu.group, "show_focus", 0);
eu.previouslyFocusedEffect = engine.getValue(eu.group,
- "focused_effect");
+ "focused_effect");
engine.setValue(eu.group, "focused_effect", 0);
}
} else {
engine.setValue(eu.group, "show_focus", 1);
if (eu.previouslyFocusedEffect !== undefined) {
engine.setValue(eu.group, "focused_effect",
- eu.previouslyFocusedEffect);
+ eu.previouslyFocusedEffect);
}
}
if (eu.enableButtons !== undefined) {
@@ -770,8 +770,8 @@
// show_focus is always in the correct state, even if the user
// presses the skin button for show_parameters.
this.showParametersConnection = engine.makeConnection(this.group,
- "show_parameters",
- this.onShowParametersChange);
+ "show_parameters",
+ this.onShowParametersChange);
this.showParametersConnection.trigger();
}
@@ -916,7 +916,7 @@
outKey: "focused_effect",
connect: function() {
this.connections[0] = engine.makeConnection(eu.group, "focused_effect",
- this.onFocusChange);
+ this.onFocusChange);
},
disconnect: function() {
engine.softTakeoverIgnoreNextValue(this.group, this.inKey);
@@ -979,7 +979,7 @@
this.connect = function() {
this.connections[0] = engine.makeConnection(eu.group, "focused_effect",
- this.onFocusChange);
+ this.onFocusChange);
// this.onFocusChange sets this.group and this.outKey, so trigger it
// before making the connection for LED output
this.connections[0].trigger();
@@ -1026,8 +1026,8 @@
// of assigning to this.connections[0] to avoid
// Component.prototype.trigger() triggering the disconnected connection.
this.connections = [engine.makeConnection(eu.group,
- "focused_effect",
- this.output)];
+ "focused_effect",
+ this.output)];
};
},
});
@@ -1069,8 +1069,8 @@
var showParameters = engine.getValue(this.group, "show_parameters");
if (this.isPress(channel, control, value, status)) {
this.longPressTimer = engine.beginTimer(this.longPressTimeout,
- this.startEffectFocusChooseMode,
- true);
+ this.startEffectFocusChooseMode,
+ true);
if (!showParameters) {
if (!allowFocusWhenParametersHidden) {
engine.setValue(this.group, "show_parameters", 1);
@@ -1093,11 +1093,11 @@
eu.focusChooseModeActive = false;
} else {
if (!showParameters && allowFocusWhenParametersHidden) {
- engine.setValue(this.group, "show_parameters", 1);
+ engine.setValue(this.group, "show_parameters", 1);
} else if (showParameters && !this.pressedWhenParametersHidden) {
- engine.setValue(this.group, "show_parameters", 0);
- // eu.onShowParametersChange will save the focused effect,
- // unfocus, and hide focus buttons in skin
+ engine.setValue(this.group, "show_parameters", 0);
+ // eu.onShowParametersChange will save the focused effect,
+ // unfocus, and hide focus buttons in skin
}
}
this.pressedWhenParametersHidden = false;
diff --git a/res/schema.xml b/res/schema.xml
index 29c6af59fe8d..5f332d04844d 100644
--- a/res/schema.xml
+++ b/res/schema.xml
@@ -465,4 +465,34 @@ METADATA
ALTER TABLE library ADD COLUMN color INTEGER;
+
+
+ Convert the PredefinedColor ID to the actual RGB value.
+
+
+
+ UPDATE cues SET color=0xFF8C00 WHERE color=0;
+
+
+ UPDATE cues SET color=0xC50A08 WHERE color=1;
+
+ UPDATE cues SET color=0x32BE44 WHERE color=2;
+
+ UPDATE cues SET color=0x0044FF WHERE color=3;
+
+ UPDATE cues SET color=0xF8D200 WHERE color=4;
+
+ UPDATE cues SET color=0x42D4F4 WHERE color=5;
+
+ UPDATE cues SET color=0xAF00CC WHERE color=6;
+
+ UPDATE cues SET color=0xFCA6D7 WHERE color=7;
+
+ UPDATE cues SET color=0xF2F2FF WHERE color=8;
+
+ UPDATE cues SET color = (color & 0xFFFFFF) WHERE color > 0xFFFFFF;
+
+
diff --git a/res/skins/Deere/hotcue_button.xml b/res/skins/Deere/hotcue_button.xml
index b301c6994eda..2712d6eb0f0e 100644
--- a/res/skins/Deere/hotcue_button.xml
+++ b/res/skins/Deere/hotcue_button.xml
@@ -46,8 +46,8 @@
false
- ,hotcue__color_id
- highlight
+ ,hotcue__color
+ backgroundColorRgba
diff --git a/res/skins/Deere/style.qss b/res/skins/Deere/style.qss
index c9dd33053b64..5480087178c2 100644
--- a/res/skins/Deere/style.qss
+++ b/res/skins/Deere/style.qss
@@ -1546,7 +1546,6 @@ WPushButton:hover {
/*"Pressed" state*/
WPushButton[value="1"],
WPushButton[value="2"] {
- /*color: #FDFDFD;*/
color: #FDFDFD;
background-color: #006596;
border: 1px solid #006596;
@@ -1559,139 +1558,17 @@ WPushButton[value="2"]:hover {
border: 1px solid #0080BE;
}
-/* Hotcue Color: No Color */
-#HotcueButton[value="1"][highlight="0"],
-#HotcueButton[value="2"][highlight="0"] {
- background-color: #006596;
- border-color: #006596;
+#HotcueButton {
+ qproperty-shouldHighlightBackgroundOnHover: true;
}
-#HotcueButton[value="1"][highlight="0"]:hover,
-#HotcueButton[value="2"][highlight="0"]:hover {
- background-color: #0080BE;
- border-color: #0080BE;
-}
-
-/* Hotcue Color: Red */
-#HotcueButton[value="1"][highlight="1"],
-#HotcueButton[value="2"][highlight="1"] {
- background-color: #c50a08;
- border-color: #c50a08;
-}
-#HotcueButton[value="1"][highlight="1"]:hover,
-#HotcueButton[value="2"][highlight="1"]:hover {
- background-color: #e50c08;
- border-color: #e50c08;
-}
-
-/* Hotcue Color: Green */
-#HotcueButton[value="1"][highlight="2"],
-#HotcueButton[value="2"][highlight="2"] {
- color: #3B3B3B;
- background-color: #32be44;
- border-color: #32be44;
-}
-#HotcueButton[value="1"][highlight="2"]:hover,
-#HotcueButton[value="2"][highlight="2"]:hover {
- background-color: #52de64;
- border-color: #52de64;
-}
-
-/* Hotcue Color: Blue */
-#HotcueButton[value="1"][highlight="3"],
-#HotcueButton[value="2"][highlight="3"] {
- background-color: #0044ff;
- border-color: #0044ff;
-}
-#HotcueButton[value="1"][highlight="3"]:hover,
-#HotcueButton[value="2"][highlight="3"]:hover {
- background-color: #0064ff;
- border-color: #0064ff;
-}
-
-/* Hotcue Color: Yellow */
-#HotcueButton[value="1"][highlight="4"],
-#HotcueButton[value="2"][highlight="4"] {
- color: #3B3B3B;
- background-color: #f8d200;
- border-color: #f8d200;
-}
-#HotcueButton[value="1"][highlight="4"]:hover,
-#HotcueButton[value="2"][highlight="4"]:hover {
- color: #3B3B3B;
- background-color: #f8f200;
- border-color: #f8f200;
-}
-
-/* Hotcue Color: Celeste */
-#HotcueButton[value="1"][highlight="5"],
-#HotcueButton[value="2"][highlight="5"] {
- color: #3B3B3B;
- background-color: #42d4f4;
- border-color: #42d4f4;
-}
-#HotcueButton[value="1"][highlight="5"]:hover,
-#HotcueButton[value="2"][highlight="5"]:hover {
- color: #3B3B3B;
- background-color: #62f4f4;
- border-color: #62f4f4;
-}
-
-/* Hotcue Color: Purple */
-#HotcueButton[value="1"][highlight="6"],
-#HotcueButton[value="2"][highlight="6"] {
- background-color: #af00cc;
- border-color: #af00cc;
-}
-#HotcueButton[value="1"][highlight="6"]:hover,
-#HotcueButton[value="2"][highlight="6"]:hover {
- background-color: #cf00ec;
- border-color: #cf00ec;
-}
-
-/* Hotcue Color: Pink */
-#HotcueButton[value="1"][highlight="7"],
-#HotcueButton[value="2"][highlight="7"] {
- color: #4B4B4B;
- background-color: #fca6d7;
- border-color: #fca6d7;
-}
-#HotcueButton[value="1"][highlight="7"]:hover,
-#HotcueButton[value="2"][highlight="7"]:hover {
- color: #4B4B4B;
- background-color: #fcc6f7;
- border-color: #fcc6f7;
-}
-
-/* Hotcue Color: White */
-#HotcueButton[value="1"][highlight="8"],
-#HotcueButton[value="2"][highlight="8"] {
- color: #4B4B4B;
- background-color: #f2f2ff;
- border-color: #f2f2ff;
-}
-#HotcueButton[value="1"][highlight="8"]:hover,
-#HotcueButton[value="2"][highlight="8"]:hover {
- color: #4B4B4B;
- background-color: #ffffff;
- border-color: #ffffff;
-}
-
-/*"Enabled" state, e.g. for recording status
- 0 -- disconnected / off
- 1 -- connecting / enabling
- 2 -- connected / enabled
-WPushButton[value="2"] {
+
+#HotcueButton[backgroundIsDark=true][hasBackgroundColor=true] {
color: #FDFDFD;
- background-color: #4B4B4B;
- border: 0px solid #006596;
}
-WPushButton[value="2"]:hover {
- color: #FDFDFD;
- background-color: #4B4B4B;
- border: 0px solid #0080BE;
+#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/LateNight/button_hotcue.xml b/res/skins/LateNight/button_hotcue.xml
index 647656855040..920ffc05d324 100644
--- a/res/skins/LateNight/button_hotcue.xml
+++ b/res/skins/LateNight/button_hotcue.xml
@@ -37,8 +37,8 @@
false
- ,hotcue__color_id
- highlight
+ ,hotcue__color
+ backgroundColorRgba
diff --git a/res/skins/LateNight/style.qss b/res/skins/LateNight/style.qss
index ec9943b3ae4e..c51102c3e5b2 100644
--- a/res/skins/LateNight/style.qss
+++ b/res/skins/LateNight/style.qss
@@ -716,44 +716,6 @@ QPushButton#pushButtonAutoDJ:checked,
}
/* ToDo
* orange Play button when playing from Cue / Hotcue */
-/* 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"],
-#SpecialCueButton[value="1"] {
- 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;
-}
-/************** Button borders & backgrounds **********************************/
-
/************** Button icons **************************************************/
@@ -1112,7 +1074,7 @@ QPushButton#pushButtonRepeatPlaylist:!checked {
padding: 3px 6px; */
qproperty-icon: url(skin:/buttons_classic/btn__delete.svg);
/* color buttons are 42x24 px.
- To get the final size for the Delete button consider border width.
+ To get the final size for the Delete button consider border width.
wide button:
width: 38px;
height: 20px;
@@ -2216,7 +2178,7 @@ WCueMenuPopup QPushButton:focus {
padding-right: 3px;
border-right: 1px solid #000;
/* gradient colors should match those of QHeaderView gradient,
- with a litte transparency added to not cut off the header label */
+ with a litte transparency added to not cut off the header label */
background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1,
stop:0 rgba(34,34,34,190),
stop:1 rgba(17,17,17,190));
@@ -2561,7 +2523,7 @@ WEffectSelector,
#fadeModeCombobox {
height: 19px;
padding: 0px 0px 1px 5px;
- margin: 0px 1px 2px 1px;
+ margin: 0px 1px 2px 1px;
}
WEffectSelector::down-arrow,
#fadeModeCombobox::down-arrow {
@@ -2584,7 +2546,7 @@ WEffectSelector,
WEffectSelector::indicator:checked,
#fadeModeCombobox::indicator:checked {
/* checkbox container is 28 x 22px;
- use margin + border to create a square checkbox sized like kill buttons */
+ use margin + border to create a square checkbox sized like kill buttons */
margin: 2px;
image: url(skin:/buttons_classic/btn__lib_checkmark_orange.svg);
}
@@ -2633,7 +2595,7 @@ WEffectSelector,
padding: 5px 12px 5px 26px;
}
/* Icons in QLineEdit menus:
- beatsize spinbox, searchbox, editable track properties */
+ beatsize spinbox, searchbox, editable track properties */
WBeatSpinBox QMenu::icon,
#LibraryContainer QMenu::icon,
WCueMenuPopup QMenu::icon,
@@ -2774,7 +2736,3 @@ WEffectSelector,
#Border58 {
border: 1px solid #585858;
}
-
-
-
-
diff --git a/res/skins/Shade/btn/btn_hotcue_1.png b/res/skins/Shade/btn/btn_hotcue_1.png
index bbcc063c5253..86f572261d97 100644
Binary files a/res/skins/Shade/btn/btn_hotcue_1.png and b/res/skins/Shade/btn/btn_hotcue_1.png differ
diff --git a/res/skins/Shade/btn/btn_hotcue_1_down.png b/res/skins/Shade/btn/btn_hotcue_1_down.png
deleted file mode 100644
index b2b78e0b2cdd..000000000000
Binary files a/res/skins/Shade/btn/btn_hotcue_1_down.png and /dev/null differ
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 b02f6cbbfc77..000000000000
Binary files a/res/skins/Shade/btn/btn_hotcue_1_over.png and /dev/null differ
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 d63e01ebedb4..000000000000
Binary files a/res/skins/Shade/btn/btn_hotcue_1_overdown.png and /dev/null differ
diff --git a/res/skins/Shade/btn/btn_hotcue_2.png b/res/skins/Shade/btn/btn_hotcue_2.png
index f085b9f63a35..6c93b48fe2ed 100644
Binary files a/res/skins/Shade/btn/btn_hotcue_2.png and b/res/skins/Shade/btn/btn_hotcue_2.png differ
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 93b227bafe44..000000000000
Binary files a/res/skins/Shade/btn/btn_hotcue_2_down.png and /dev/null differ
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 248fb1ea9574..000000000000
Binary files a/res/skins/Shade/btn/btn_hotcue_2_over.png and /dev/null differ
diff --git a/res/skins/Shade/btn/btn_hotcue_2_overdown.png b/res/skins/Shade/btn/btn_hotcue_2_overdown.png
deleted file mode 100644
index a89346b0e30b..000000000000
Binary files a/res/skins/Shade/btn/btn_hotcue_2_overdown.png and /dev/null differ
diff --git a/res/skins/Shade/btn/btn_hotcue_3.png b/res/skins/Shade/btn/btn_hotcue_3.png
index 2ccd932a1647..b6494126f05a 100644
Binary files a/res/skins/Shade/btn/btn_hotcue_3.png and b/res/skins/Shade/btn/btn_hotcue_3.png differ
diff --git a/res/skins/Shade/btn/btn_hotcue_3_down.png b/res/skins/Shade/btn/btn_hotcue_3_down.png
deleted file mode 100644
index 96b06c765968..000000000000
Binary files a/res/skins/Shade/btn/btn_hotcue_3_down.png and /dev/null differ
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 bc39f10974a6..000000000000
Binary files a/res/skins/Shade/btn/btn_hotcue_3_over.png and /dev/null differ
diff --git a/res/skins/Shade/btn/btn_hotcue_3_overdown.png b/res/skins/Shade/btn/btn_hotcue_3_overdown.png
deleted file mode 100644
index 87587d95efa3..000000000000
Binary files a/res/skins/Shade/btn/btn_hotcue_3_overdown.png and /dev/null differ
diff --git a/res/skins/Shade/btn/btn_hotcue_4.png b/res/skins/Shade/btn/btn_hotcue_4.png
index d973f1eb8504..f3c47cc7ef81 100644
Binary files a/res/skins/Shade/btn/btn_hotcue_4.png and b/res/skins/Shade/btn/btn_hotcue_4.png differ
diff --git a/res/skins/Shade/btn/btn_hotcue_4_down.png b/res/skins/Shade/btn/btn_hotcue_4_down.png
deleted file mode 100644
index da33bba1f704..000000000000
Binary files a/res/skins/Shade/btn/btn_hotcue_4_down.png and /dev/null differ
diff --git a/res/skins/Shade/btn/btn_hotcue_4_over.png b/res/skins/Shade/btn/btn_hotcue_4_over.png
deleted file mode 100644
index 944eef1c6327..000000000000
Binary files a/res/skins/Shade/btn/btn_hotcue_4_over.png and /dev/null differ
diff --git a/res/skins/Shade/btn/btn_hotcue_4_overdown.png b/res/skins/Shade/btn/btn_hotcue_4_overdown.png
deleted file mode 100644
index 6dd65f063ccf..000000000000
Binary files a/res/skins/Shade/btn/btn_hotcue_4_overdown.png and /dev/null differ
diff --git a/res/skins/Shade/btn/btn_hotcue_5.png b/res/skins/Shade/btn/btn_hotcue_5.png
index dab5e3ce7b09..002cf0cc546f 100644
Binary files a/res/skins/Shade/btn/btn_hotcue_5.png and b/res/skins/Shade/btn/btn_hotcue_5.png differ
diff --git a/res/skins/Shade/btn/btn_hotcue_5_down.png b/res/skins/Shade/btn/btn_hotcue_5_down.png
deleted file mode 100644
index b893778f3148..000000000000
Binary files a/res/skins/Shade/btn/btn_hotcue_5_down.png and /dev/null differ
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 65f7deda2ed1..000000000000
Binary files a/res/skins/Shade/btn/btn_hotcue_5_over.png and /dev/null differ
diff --git a/res/skins/Shade/btn/btn_hotcue_5_overdown.png b/res/skins/Shade/btn/btn_hotcue_5_overdown.png
deleted file mode 100644
index 9c4a9e443d8a..000000000000
Binary files a/res/skins/Shade/btn/btn_hotcue_5_overdown.png and /dev/null differ
diff --git a/res/skins/Shade/btn/btn_hotcue_6.png b/res/skins/Shade/btn/btn_hotcue_6.png
index be5e45c35f81..dffdf0b39c3d 100644
Binary files a/res/skins/Shade/btn/btn_hotcue_6.png and b/res/skins/Shade/btn/btn_hotcue_6.png differ
diff --git a/res/skins/Shade/btn/btn_hotcue_6_down.png b/res/skins/Shade/btn/btn_hotcue_6_down.png
deleted file mode 100644
index 22a37ba05d8c..000000000000
Binary files a/res/skins/Shade/btn/btn_hotcue_6_down.png and /dev/null differ
diff --git a/res/skins/Shade/btn/btn_hotcue_6_over.png b/res/skins/Shade/btn/btn_hotcue_6_over.png
deleted file mode 100644
index 7d792e2992cd..000000000000
Binary files a/res/skins/Shade/btn/btn_hotcue_6_over.png and /dev/null differ
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 2f3f39c0582a..000000000000
Binary files a/res/skins/Shade/btn/btn_hotcue_6_overdown.png and /dev/null differ
diff --git a/res/skins/Shade/btn/btn_hotcue_7.png b/res/skins/Shade/btn/btn_hotcue_7.png
index f0821440d8c2..998978cde5e3 100644
Binary files a/res/skins/Shade/btn/btn_hotcue_7.png and b/res/skins/Shade/btn/btn_hotcue_7.png differ
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 c6da80d83a13..000000000000
Binary files a/res/skins/Shade/btn/btn_hotcue_7_down.png and /dev/null differ
diff --git a/res/skins/Shade/btn/btn_hotcue_7_over.png b/res/skins/Shade/btn/btn_hotcue_7_over.png
deleted file mode 100644
index 782c28391727..000000000000
Binary files a/res/skins/Shade/btn/btn_hotcue_7_over.png and /dev/null differ
diff --git a/res/skins/Shade/btn/btn_hotcue_7_overdown.png b/res/skins/Shade/btn/btn_hotcue_7_overdown.png
deleted file mode 100644
index 81af392fc4ab..000000000000
Binary files a/res/skins/Shade/btn/btn_hotcue_7_overdown.png and /dev/null differ
diff --git a/res/skins/Shade/btn/btn_hotcue_8.png b/res/skins/Shade/btn/btn_hotcue_8.png
index d4261196257f..36542964c2e1 100644
Binary files a/res/skins/Shade/btn/btn_hotcue_8.png and b/res/skins/Shade/btn/btn_hotcue_8.png differ
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 9644ffd1ca1b..000000000000
Binary files a/res/skins/Shade/btn/btn_hotcue_8_down.png and /dev/null differ
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 331d76d12bf2..000000000000
Binary files a/res/skins/Shade/btn/btn_hotcue_8_over.png and /dev/null differ
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 70c287b664a0..000000000000
Binary files a/res/skins/Shade/btn/btn_hotcue_8_overdown.png and /dev/null differ
diff --git a/res/skins/Shade/hotcue_button.xml b/res/skins/Shade/hotcue_button.xml
index cf889c255f90..40149e41b8d2 100644
--- a/res/skins/Shade/hotcue_button.xml
+++ b/res/skins/Shade/hotcue_button.xml
@@ -6,13 +6,13 @@
2
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
,hotcue__activate
@@ -27,8 +27,8 @@
false
- ,hotcue__color_id
- highlight
+ ,hotcue__color
+ backgroundColorRgba
diff --git a/res/skins/Shade/style.qss b/res/skins/Shade/style.qss
index 5051566ea5e7..b0bc06487d96 100644
--- a/res/skins/Shade/style.qss
+++ b/res/skins/Shade/style.qss
@@ -220,7 +220,7 @@ WCoverArtMenu {
/* unchecked menu checkbox */
#LibraryContainer QMenu QCheckBox::indicator:enabled:!checked,
#LibraryContainer QMenu::indicator:!checked {
- border-color: #1a2025;/*
+ border-color: #1a2025;/*
background-color: #7e868b;
remove OS focus indicator */
outline: none;
@@ -784,47 +784,6 @@ QPushButton#pushButtonRepeatPlaylist {
}
/* 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;
+#HotcueButton {
+ background-color: #aab2b7;
}
diff --git a/res/skins/Shade/style_dark.qss b/res/skins/Shade/style_dark.qss
index fbe409391e08..365e5d283d5f 100644
--- a/res/skins/Shade/style_dark.qss
+++ b/res/skins/Shade/style_dark.qss
@@ -187,8 +187,3 @@ WLibrary QPushButton:enabled {
WLibrary QRadioButton::indicator:checked {
image: url(skin:/btn/btn_lib_radio_button_on_mustard.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 86da85fbe159..6d569a637603 100644
--- a/res/skins/Shade/style_summer_sunset.qss
+++ b/res/skins/Shade/style_summer_sunset.qss
@@ -109,7 +109,9 @@ WLibrary QRadioButton::indicator:checked {
image: url(skin:/btn/btn_lib_radio_button_on_neongreen.svg) center center;
}
-/* Hotcue Color: No Color */
-#HotcueButton[highlight="0"] {
- background-color: #52f904;
-}
+/* 'Enable AutoDJ' button */
+QPushButton#pushButtonAutoDJ:hover,
+QPushButton#pushButtonRecording:hover,
+QPushButton#pushButtonAnalyze:hover {
+ border: 1px solid #52F904;
+ }
diff --git a/res/skins/Tango/button_hotcue_deck.xml b/res/skins/Tango/button_hotcue_deck.xml
index bb1009dbf424..eb0785f234c6 100644
--- a/res/skins/Tango/button_hotcue_deck.xml
+++ b/res/skins/Tango/button_hotcue_deck.xml
@@ -38,8 +38,8 @@ Variables:
false
- ,hotcue__color_id
- highlight
+ ,hotcue__color
+ backgroundColorRgba
diff --git a/res/skins/Tango/style.qss b/res/skins/Tango/style.qss
index 4e77414dbf82..0938c92be550 100644
--- a/res/skins/Tango/style.qss
+++ b/res/skins/Tango/style.qss
@@ -1006,63 +1006,12 @@ 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;
- color: #111;
-}
-
-/* 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: #222;
- background-color: #f8d200;
-}
-
-/* Hotcue Color: Celeste */
-#HotcueButton[displayValue="1"][highlight="5"],
-#HotcueButton[displayValue="2"][highlight="5"] {
- color: #111;
- 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;
+#HotcueButton[backgroundIsDark=true][hasBackgroundColor=true] {
+ color: #eeeeee;
}
-/* Hotcue Color: White */
-#HotcueButton[displayValue="1"][highlight="8"],
-#HotcueButton[displayValue="2"][highlight="8"] {
- color: #222;
- background-color: #f2f2ff;
+#HotcueButton[backgroundIsDark=false][hasBackgroundColor=true] {
+ color: #0f0f0f;
}
#CueButton {
diff --git a/src/controllers/bulk/bulkcontroller.cpp b/src/controllers/bulk/bulkcontroller.cpp
index 17459ae8d973..49468b1ceabf 100644
--- a/src/controllers/bulk/bulkcontroller.cpp
+++ b/src/controllers/bulk/bulkcontroller.cpp
@@ -69,15 +69,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 480aa084a69f..9d67c1041ea5 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 2942c5b3ac7b..8df74892935c 100644
--- a/src/controllers/bulk/bulkenumerator.cpp
+++ b/src/controllers/bulk/bulkenumerator.cpp
@@ -11,9 +11,10 @@
#include "controllers/bulk/bulkenumerator.h"
#include "controllers/bulk/bulksupported.h"
-BulkEnumerator::BulkEnumerator()
+BulkEnumerator::BulkEnumerator(UserSettingsPointer pConfig)
: ControllerEnumerator(),
- m_context(NULL) {
+ m_context(nullptr),
+ m_pConfig(pConfig) {
libusb_init(&m_context);
}
@@ -55,7 +56,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 5e16dd67d11d..effd0044d68a 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();
+ explicit 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/colorjsproxy.cpp b/src/controllers/colorjsproxy.cpp
deleted file mode 100644
index 4147cdcc124a..000000000000
--- a/src/controllers/colorjsproxy.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-#include "controllers/colorjsproxy.h"
-
-ColorJSProxy::ColorJSProxy(QScriptEngine* pScriptEngine)
- : m_pScriptEngine(pScriptEngine),
- m_predefinedColorsList(makePredefinedColorsList(pScriptEngine)){};
-
-ColorJSProxy::~ColorJSProxy() {};
-
-QScriptValue ColorJSProxy::predefinedColorFromId(int iId) {
- PredefinedColorPointer color(Color::kPredefinedColorsSet.predefinedColorFromId(iId));
- return jsColorFrom(color);
-};
-
-Q_INVOKABLE QScriptValue ColorJSProxy::predefinedColorsList() {
- return m_predefinedColorsList;
-}
-
-QScriptValue ColorJSProxy::jsColorFrom(PredefinedColorPointer predefinedColor) {
- 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);
- return jsColor;
-}
-
-QScriptValue ColorJSProxy::makePredefinedColorsList(QScriptEngine* pScriptEngine) {
- int numColors = Color::kPredefinedColorsSet.allColors.length();
- QScriptValue colorList = pScriptEngine->newArray(numColors);
- for (int i = 0; i < numColors; ++i) {
- PredefinedColorPointer color = Color::kPredefinedColorsSet.allColors.at(i);
- colorList.setProperty(i, jsColorFrom(color));
- }
- return colorList;
-}
diff --git a/src/controllers/colorjsproxy.h b/src/controllers/colorjsproxy.h
deleted file mode 100644
index e6ebeda1d9b0..000000000000
--- a/src/controllers/colorjsproxy.h
+++ /dev/null
@@ -1,27 +0,0 @@
-#ifndef COLORJSPROXY_H
-#define COLORJSPROXY_H
-
-#include
-#include
-#include
-
-#include "util/color/color.h"
-
-class ColorJSProxy: public QObject {
- Q_OBJECT
- public:
- ColorJSProxy(QScriptEngine* pScriptEngine);
-
- virtual ~ColorJSProxy();
-
- Q_INVOKABLE QScriptValue predefinedColorFromId(int iId);
- Q_INVOKABLE QScriptValue predefinedColorsList();
-
- private:
- QScriptValue jsColorFrom(PredefinedColorPointer predefinedColor);
- QScriptValue makePredefinedColorsList(QScriptEngine* pScriptEngine);
- QScriptEngine* m_pScriptEngine;
- QScriptValue m_predefinedColorsList;
-};
-
-#endif /* COLORJSPROXY_H */
diff --git a/src/controllers/colormapper.cpp b/src/controllers/colormapper.cpp
new file mode 100644
index 000000000000..e308c7b1d839
--- /dev/null
+++ b/src/controllers/colormapper.cpp
@@ -0,0 +1,64 @@
+#include "controllers/colormapper.h"
+
+#include
+#include
+
+#include "util/logger.h"
+
+namespace {
+
+const mixxx::Logger kLogger("ColorMapper");
+
+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 = (static_cast(qRed(a)) + static_cast(qRed(b))) / 2;
+ long delta_red = static_cast(qRed(a)) - static_cast(qRed(b));
+ long delta_green = static_cast(qGreen(a)) - static_cast(qGreen(b));
+ long delta_blue = static_cast(qBlue(a)) - static_cast(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
+
+QRgb ColorMapper::getNearestColor(QRgb desiredColor) {
+ // If desired color is already in cache, use cache entry
+ QMap::const_iterator i = m_cache.constFind(desiredColor);
+ if (i != m_cache.constEnd()) {
+ DEBUG_ASSERT(m_availableColors.contains(i.value()));
+ kLogger.trace()
+ << "ColorMapper cache hit for" << desiredColor << ":"
+ << "Color =" << i.value();
+ return i.value();
+ }
+
+ // Color is not cached
+ QRgb nearestColor;
+ double nearestColorDistance = qInf();
+ for (auto j = m_availableColors.constBegin(); j != m_availableColors.constEnd(); j++) {
+ QRgb availableColor = j.key();
+ double distance = colorDistance(desiredColor, availableColor);
+ if (distance < nearestColorDistance) {
+ nearestColorDistance = distance;
+ nearestColor = j.key();
+ }
+ }
+
+ kLogger.trace()
+ << "ColorMapper found matching color for" << desiredColor << ":"
+ << "Color =" << nearestColor;
+ m_cache.insert(desiredColor, nearestColor);
+ return nearestColor;
+}
+
+QVariant ColorMapper::getValueForNearestColor(QRgb desiredColor) {
+ return m_availableColors.value(getNearestColor(desiredColor));
+}
diff --git a/src/controllers/colormapper.h b/src/controllers/colormapper.h
new file mode 100644
index 000000000000..05e2cb905518
--- /dev/null
+++ b/src/controllers/colormapper.h
@@ -0,0 +1,27 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+
+#include "util/assert.h"
+
+// This class allows to find the nearest color representation of a given color
+// in a set of fixed colors. Additional user data (e.g. MIDI byte values) can
+// be linked to colors in the color set as QVariant.
+class ColorMapper final {
+ public:
+ ColorMapper() = delete;
+ explicit ColorMapper(QMap availableColors)
+ : m_availableColors(availableColors) {
+ DEBUG_ASSERT(!m_availableColors.isEmpty());
+ }
+
+ QRgb getNearestColor(QRgb desiredColor);
+ QVariant getValueForNearestColor(QRgb desiredColor);
+
+ private:
+ const QMap m_availableColors;
+ QMap m_cache;
+};
diff --git a/src/controllers/colormapperjsproxy.cpp b/src/controllers/colormapperjsproxy.cpp
new file mode 100644
index 000000000000..12ed2d77df16
--- /dev/null
+++ b/src/controllers/colormapperjsproxy.cpp
@@ -0,0 +1,59 @@
+#include
+
+#include "controllers/colormapperjsproxy.h"
+
+ColorMapperJSProxy::ColorMapperJSProxy(QScriptEngine* pScriptEngine, QMap availableColors)
+ : m_pScriptEngine(pScriptEngine),
+ m_colorMapper(new ColorMapper(availableColors)) {
+}
+
+QScriptValue ColorMapperJSProxy::getNearestColor(uint colorCode) {
+ QRgb result = m_colorMapper->getNearestColor(static_cast(colorCode));
+ QScriptValue jsColor = m_pScriptEngine->newObject();
+ jsColor.setProperty("red", qRed(result));
+ jsColor.setProperty("green", qGreen(result));
+ jsColor.setProperty("blue", qBlue(result));
+ return jsColor;
+}
+
+QScriptValue ColorMapperJSProxy::getValueForNearestColor(uint colorCode) {
+ return m_pScriptEngine->toScriptValue(
+ m_colorMapper->getValueForNearestColor(static_cast(colorCode)));
+}
+
+QScriptValue ColorMapperJSProxyConstructor(QScriptContext* pScriptContext, QScriptEngine* pScriptEngine) {
+ QMap availableColors;
+ if (pScriptContext->argumentCount() != 1) {
+ pScriptContext->throwError(
+ QStringLiteral("Failed to create ColorMapper object: constructor takes exactly one argument!"));
+ return pScriptEngine->undefinedValue();
+ }
+ QScriptValue argument = pScriptContext->argument(0);
+ if (!argument.isValid() || !argument.isObject()) {
+ pScriptContext->throwError(
+ QStringLiteral("Failed to create ColorMapper object: argument needs to be an object!"));
+ return pScriptEngine->undefinedValue();
+ }
+
+ QScriptValueIterator it(argument);
+ while (it.hasNext()) {
+ it.next();
+ QColor color(it.name());
+ if (color.isValid()) {
+ availableColors.insert(color.rgb(), it.value().toVariant());
+ } else {
+ pScriptContext->throwError(
+ QStringLiteral("Invalid color name passed to ColorMapper: ") + it.name());
+ continue;
+ }
+ }
+
+ if (availableColors.isEmpty()) {
+ pScriptContext->throwError(
+ QStringLiteral("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 000000000000..3a08f062d556
--- /dev/null
+++ b/src/controllers/colormapperjsproxy.h
@@ -0,0 +1,39 @@
+#pragma once
+
+#include
+#include
+
+#include "controllers/colormapper.h"
+
+// This is a wrapper class that exposes ColorMapper via the QScriptEngine and
+// makes it possible to create and use ColorMapper object from JavaScript
+// controller mappings.
+class ColorMapperJSProxy final : public QObject {
+ Q_OBJECT
+ public:
+ ColorMapperJSProxy() = delete;
+ ColorMapperJSProxy(QScriptEngine* pScriptEngine, QMap availableColors);
+
+ ~ColorMapperJSProxy() override {
+ delete m_colorMapper;
+ };
+
+ // Q_INVOKABLE is need here because these methods callable from controller
+ // scripts
+
+ // For a given RGB color code (e.g. 0xFF0000), this finds the nearest
+ // available color and returns a JS object with properties "red", "green",
+ // "blue" (each with value range 0-255).
+ Q_INVOKABLE QScriptValue getNearestColor(uint ColorCode);
+
+ // For a given RGB color code (e.g. 0xFF0000), this finds the nearest
+ // available color, then returns the value associated with that color
+ // (which could be a MIDI byte value for example).
+ Q_INVOKABLE QScriptValue getValueForNearestColor(uint ColorCode);
+
+ private:
+ QScriptEngine* m_pScriptEngine;
+ ColorMapper* m_colorMapper;
+};
+
+QScriptValue ColorMapperJSProxyConstructor(QScriptContext* context, QScriptEngine* engine);
diff --git a/src/controllers/controller.cpp b/src/controllers/controller.cpp
index e30ff7a5f63a..15a1e1dd5ec0 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 ec2ad82ceac3..09a20d8181bc 100644
--- a/src/controllers/controller.h
+++ b/src/controllers/controller.h
@@ -23,7 +23,7 @@
class Controller : public QObject, ConstControllerPresetVisitor {
Q_OBJECT
public:
- Controller();
+ explicit Controller(UserSettingsPointer pConfig);
~Controller() override; // Subclass should call close() at minimum.
// Returns the extension for the controller (type) preset files. This is
@@ -101,7 +101,7 @@ class Controller : public QObject, ConstControllerPresetVisitor {
// To be called in sub-class' close() functions after stopping any input
// polling/processing but before closing the device.
void stopEngine();
-
+
// To be called when receiving events
void triggerActivity();
@@ -162,6 +162,8 @@ class Controller : public QObject, ConstControllerPresetVisitor {
bool m_bLearning;
QElapsedTimer 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 8b272c4e5286..1488353b5d48 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"
@@ -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
@@ -185,7 +187,6 @@ void ControllerEngine::gracefulShutdown() {
++it;
}
- m_pColorJSProxy.reset();
delete m_pBaClass;
m_pBaClass = nullptr;
}
@@ -213,8 +214,9 @@ void ControllerEngine::initializeScriptEngine() {
engineGlobalObject.setProperty("midi", m_pEngine->newQObject(m_pController));
}
- m_pColorJSProxy = std::make_unique(m_pEngine);
- 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());
diff --git a/src/controllers/controllerengine.h b/src/controllers/controllerengine.h
index 2359a0bcbb3c..87a75abd730a 100644
--- a/src/controllers/controllerengine.h
+++ b/src/controllers/controllerengine.h
@@ -16,7 +16,6 @@
#include "bytearrayclass.h"
#include "preferences/usersettings.h"
#include "controllers/controllerpreset.h"
-#include "controllers/colorjsproxy.h"
#include "controllers/softtakeover.h"
#include "util/alphabetafilter.h"
#include "util/duration.h"
@@ -80,7 +79,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 +197,7 @@ class ControllerEngine : public QObject {
double getDeckRate(const QString& group);
Controller* m_pController;
+ const UserSettingsPointer m_pConfig;
bool m_bPopups;
QList m_scriptFunctionPrefixes;
QMap m_scriptErrors;
@@ -210,7 +210,6 @@ class ControllerEngine : public QObject {
QHash m_timers;
SoftTakeoverCtrl m_st;
ByteArrayClass* m_pBaClass;
- std::unique_ptr m_pColorJSProxy;
// 256 (default) available virtual decks is enough I would think.
// If more are needed at run-time, these will move to the heap automatically
QVarLengthArray m_intervalAccumulator;
diff --git a/src/controllers/controllermanager.cpp b/src/controllers/controllermanager.cpp
index d6d4592bd273..ec74976f7f5d 100644
--- a/src/controllers/controllermanager.cpp
+++ b/src/controllers/controllermanager.cpp
@@ -127,15 +127,15 @@ 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());
+ m_enumerators.append(new Hss1394Enumerator(m_pConfig));
#endif
#ifdef __BULK__
- m_enumerators.append(new BulkEnumerator());
+ m_enumerators.append(new BulkEnumerator(m_pConfig));
#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 c317799155e5..ec118012088c 100644
--- a/src/controllers/hid/hidcontroller.cpp
+++ b/src/controllers/hid/hidcontroller.cpp
@@ -16,8 +16,9 @@
#include "controllers/controllerdebug.h"
#include "util/time.h"
-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 4f446c9ae9bc..f60843067a8e 100644
--- a/src/controllers/hid/hidcontroller.h
+++ b/src/controllers/hid/hidcontroller.h
@@ -20,7 +20,7 @@
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 c1c2dc278342..802459f3c028 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 351e972beb13..a61490f1ab49 100644
--- a/src/controllers/hid/hidenumerator.h
+++ b/src/controllers/hid/hidenumerator.h
@@ -12,13 +12,14 @@
class HidEnumerator : public ControllerEnumerator {
public:
- HidEnumerator();
+ explicit HidEnumerator(UserSettingsPointer pConfig);
virtual ~HidEnumerator();
QList queryDevices();
private:
QList m_devices;
+ UserSettingsPointer m_pConfig;
};
#endif
diff --git a/src/controllers/midi/hss1394controller.cpp b/src/controllers/midi/hss1394controller.cpp
index 7c78a5b4a6a7..886530d29290 100644
--- a/src/controllers/midi/hss1394controller.cpp
+++ b/src/controllers/midi/hss1394controller.cpp
@@ -64,9 +64,11 @@ void DeviceChannelListener::Reconnected() {
qDebug() << "HSS1394 device" << m_sName << "re-connected";
}
-Hss1394Controller::Hss1394Controller(const hss1394::TNodeInfo deviceInfo,
- int deviceIndex)
- : MidiController(),
+Hss1394Controller::Hss1394Controller(
+ const hss1394::TNodeInfo& deviceInfo,
+ int deviceIndex,
+ UserSettingsPointer pConfig)
+ : MidiController(pConfig),
m_deviceInfo(deviceInfo),
m_iDeviceIndex(deviceIndex) {
// Note: We prepend the input stream's index to the device's name to prevent
diff --git a/src/controllers/midi/hss1394controller.h b/src/controllers/midi/hss1394controller.h
index ec6e55030931..c4a0045c59f3 100644
--- a/src/controllers/midi/hss1394controller.h
+++ b/src/controllers/midi/hss1394controller.h
@@ -43,7 +43,10 @@ class DeviceChannelListener : public QObject, public hss1394::ChannelListener {
class Hss1394Controller : public MidiController {
Q_OBJECT
public:
- Hss1394Controller(const hss1394::TNodeInfo deviceInfo, int deviceIndex);
+ Hss1394Controller(
+ const hss1394::TNodeInfo& deviceInfo,
+ int deviceIndex,
+ UserSettingsPointer pConfig);
~Hss1394Controller() override;
private slots:
diff --git a/src/controllers/midi/hss1394enumerator.cpp b/src/controllers/midi/hss1394enumerator.cpp
index 8152abd0e367..9c29ac98ee30 100644
--- a/src/controllers/midi/hss1394enumerator.cpp
+++ b/src/controllers/midi/hss1394enumerator.cpp
@@ -9,7 +9,9 @@
#include "controllers/midi/hss1394controller.h"
#include "controllers/midi/hss1394enumerator.h"
-Hss1394Enumerator::Hss1394Enumerator() : MidiEnumerator() {
+Hss1394Enumerator::Hss1394Enumerator(UserSettingsPointer pConfig)
+ : MidiEnumerator(),
+ m_pConfig(pConfig) {
}
Hss1394Enumerator::~Hss1394Enumerator() {
@@ -42,8 +44,8 @@ QList Hss1394Enumerator::queryDevices() {
QString("%1").arg(tNodeInfo.uGUID.mu32Low, 0, 16),
QString("%1").arg(tNodeInfo.uProtocolVersion, 0, 16));
qDebug() << " " << message;
- Hss1394Controller *currentDevice = new Hss1394Controller(
- tNodeInfo, i);
+ Hss1394Controller* currentDevice = new Hss1394Controller(
+ tNodeInfo, i, m_pConfig);
m_devices.push_back(currentDevice);
}
}
diff --git a/src/controllers/midi/hss1394enumerator.h b/src/controllers/midi/hss1394enumerator.h
index baa208701fc7..184fcfb233c9 100644
--- a/src/controllers/midi/hss1394enumerator.h
+++ b/src/controllers/midi/hss1394enumerator.h
@@ -13,12 +13,13 @@
class Hss1394Enumerator : public MidiEnumerator {
Q_OBJECT
public:
- Hss1394Enumerator();
+ explicit Hss1394Enumerator(UserSettingsPointer pConfig);
virtual ~Hss1394Enumerator();
QList queryDevices();
private:
+ UserSettingsPointer m_pConfig;
QList m_devices;
};
diff --git a/src/controllers/midi/midicontroller.cpp b/src/controllers/midi/midicontroller.cpp
index 479631b83885..877897f00f19 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 93d91fdf4436..a81ff7d680ce 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();
+ explicit MidiController(UserSettingsPointer pConfig);
~MidiController() override;
QString presetExtension() override;
diff --git a/src/controllers/midi/portmidicontroller.cpp b/src/controllers/midi/portmidicontroller.cpp
index c4479a1ae68e..4be78583c781 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 96da9afce1a4..92d2954e18f2 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 47e6a23a829b..ba525ddeb737 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 606c4feb4de3..5169efbfa066 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();
+ explicit PortMidiEnumerator(UserSettingsPointer pConfig);
virtual ~PortMidiEnumerator();
QList queryDevices();
private:
QList m_devices;
+ UserSettingsPointer m_pConfig;
};
// For testing.
diff --git a/src/database/mixxxdb.cpp b/src/database/mixxxdb.cpp
index e3a2e70cc7e4..ab01999a95ac 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 = 31;
+const int MixxxDb::kRequiredSchemaVersion = 32;
namespace {
diff --git a/src/engine/controls/cuecontrol.cpp b/src/engine/controls/cuecontrol.cpp
index 7c0b35a32b37..d087506dbc4e 100644
--- a/src/engine/controls/cuecontrol.cpp
+++ b/src/engine/controls/cuecontrol.cpp
@@ -1,17 +1,18 @@
// cuecontrol.cpp
// Created 11/5/2009 by RJ Ryan (rryan@mit.edu)
-#include
-
-#include "engine/enginebuffer.h"
#include "engine/controls/cuecontrol.h"
+#include
+
+#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 "engine/enginebuffer.h"
+#include "preferences/colorpalettesettings.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
@@ -22,19 +23,39 @@ 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;
+constexpr double kNoColorControlValue = -1;
+
+namespace {
+
+// Helper function to convert control values (i.e. doubles) into RgbColor
+// instances (or nullopt if value < 0). This happens by using the integer
+// component as RGB color codes (e.g. 0xFF0000).
+inline mixxx::RgbColor::optional_t doubleToRgbColor(double value) {
+ if (value < 0) {
+ return std::nullopt;
+ }
+ auto colorCode = static_cast(value);
+ if (value != mixxx::RgbColor::validateCode(colorCode)) {
+ return std::nullopt;
+ }
+ return mixxx::RgbColor::optional(colorCode);
+}
+
+} // namespace
+
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) {
+ UserSettingsPointer pConfig)
+ : EngineControl(group, pConfig),
+ m_pConfig(pConfig),
+ m_colorPaletteSettings(ColorPaletteSettings(pConfig)),
+ m_bPreviewing(false),
+ 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();
@@ -314,6 +335,7 @@ void CueControl::detachCue(HotcueControl* pControl) {
}
disconnect(pCue.get(), 0, this, 0);
pControl->resetCue();
+ pControl->setColor(std::nullopt);
}
void CueControl::trackLoaded(TrackPointer pNewTrack) {
@@ -594,14 +616,18 @@ void CueControl::hotcueSet(HotcueControl* pControl, double v) {
pCue->setHotCue(hotcue);
pCue->setLabel();
pCue->setType(mixxx::CueType::HotCue);
+
+ ConfigKey autoHotcueColorsKey("[Controls]", "auto_hotcue_colors");
+ if (getConfig()->getValue(autoHotcueColorsKey, false)) {
+ auto hotcueColorPalette = m_colorPaletteSettings.getHotcueColorPalette();
+ pCue->setColor(hotcueColorPalette.colorForHotcueIndex(hotcue));
+ } else {
+ pCue->setColor(ColorPalette::kDefaultCueColor);
+ }
+
// 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));
- };
-
// 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?
@@ -1684,20 +1710,13 @@ void CueControl::hotcueFocusColorPrev(double v) {
return;
}
- PredefinedColorPointer pColor = pControl->getColor();
- if (!pColor) {
+ mixxx::RgbColor::optional_t controlColor = pControl->getColor();
+ if (!controlColor) {
return;
}
- // Get previous color in color set
- int iColorIndex = Color::kPredefinedColorsSet.predefinedColorIndex(pColor) - 1;
- if (iColorIndex <= 0) {
- iColorIndex = Color::kPredefinedColorsSet.allColors.size() - 1;
- }
- pColor = Color::kPredefinedColorsSet.allColors.at(iColorIndex);
- DEBUG_ASSERT(pColor != nullptr);
-
- pControl->setColor(pColor);
+ ColorPalette colorPalette = m_colorPaletteSettings.getHotcueColorPalette();
+ pControl->setColor(colorPalette.previousColor(*controlColor));
}
void CueControl::hotcueFocusColorNext(double v) {
@@ -1715,20 +1734,13 @@ void CueControl::hotcueFocusColorNext(double v) {
return;
}
- PredefinedColorPointer pColor = pControl->getColor();
- if (!pColor) {
+ mixxx::RgbColor::optional_t controlColor = pControl->getColor();
+ if (!controlColor) {
return;
}
- // Get next color in color set
- int iColorIndex = Color::kPredefinedColorsSet.predefinedColorIndex(pColor) + 1;
- if (iColorIndex >= Color::kPredefinedColorsSet.allColors.size()) {
- iColorIndex = 0;
- }
- pColor = Color::kPredefinedColorsSet.allColors.at(iColorIndex);
- DEBUG_ASSERT(pColor != nullptr);
-
- pControl->setColor(pColor);
+ ColorPalette colorPalette = m_colorPaletteSettings.getHotcueColorPalette();
+ pControl->setColor(colorPalette.nextColor(*controlColor));
}
@@ -1755,8 +1767,9 @@ 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.
- m_hotcueColor = new ControlObject(keyForControl(i, "color_id"));
+ // The rgba value of the color assigned to this color.
+ m_hotcueColor = new ControlObject(keyForControl(i, "color"));
+ m_hotcueColor->set(kNoColorControlValue);
connect(m_hotcueColor,
&ControlObject::valueChanged,
this,
@@ -1845,9 +1858,19 @@ 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) {
+ if (!m_pCue) {
+ return;
+ }
+
+ mixxx::RgbColor::optional_t color = doubleToRgbColor(newColor);
+ if (!color) {
+ qWarning() << "slotHotcueColorChanged got invalid value:" << newColor;
+ return;
+ }
+
+ m_pCue->setColor(*color);
+ emit hotcueColorChanged(this, newColor);
}
double HotcueControl::getPosition() const {
@@ -1861,12 +1884,16 @@ 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());
+mixxx::RgbColor::optional_t HotcueControl::getColor() const {
+ return doubleToRgbColor(m_hotcueColor->get());
}
-void HotcueControl::setColor(PredefinedColorPointer newColor) {
- m_hotcueColor->set(static_cast(newColor->m_iId));
+void HotcueControl::setColor(mixxx::RgbColor::optional_t newColor) {
+ if (newColor) {
+ m_hotcueColor->set(*newColor);
+ } else {
+ m_hotcueColor->set(kNoColorControlValue);
+ }
}
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 be65fd1a3337..4c9f0e32a784 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/colorpalettesettings.h"
#include "preferences/usersettings.h"
-#include "control/controlproxy.h"
#include "track/track.h"
#define NUM_HOT_CUES 37
@@ -50,8 +51,8 @@ class HotcueControl : public QObject {
void setCue(CuePointer pCue);
void resetCue();
void setPosition(double position);
- void setColor(PredefinedColorPointer newColor);
- PredefinedColorPointer getColor() const;
+ void setColor(mixxx::RgbColor::optional_t newColor);
+ mixxx::RgbColor::optional_t getColor() const;
// Used for caching the preview state of this hotcue control.
inline bool isPreviewing() {
@@ -76,7 +77,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);
@@ -87,7 +88,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:
@@ -193,6 +194,8 @@ class CueControl : public EngineControl {
double getQuantizedCurrentPosition();
TrackAt getTrackAt() const;
+ UserSettingsPointer m_pConfig;
+ ColorPaletteSettings m_colorPaletteSettings;
bool m_bPreviewing;
ControlObject* m_pPlay;
ControlObject* m_pStopButton;
diff --git a/src/library/dao/cuedao.cpp b/src/library/dao/cuedao.cpp
index 5a7d50d851ae..c8576109d7df 100644
--- a/src/library/dao/cuedao.cpp
+++ b/src/library/dao/cuedao.cpp
@@ -1,18 +1,18 @@
// cuedao.cpp
// Created 10/26/2009 by RJ Ryan (rryan@mit.edu)
+#include "library/dao/cuedao.h"
+
+#include
#include
#include
-#include
-#include "library/dao/cuedao.h"
+#include "library/queryutil.h"
#include "track/cue.h"
#include "track/track.h"
-#include "library/queryutil.h"
#include "util/assert.h"
+#include "util/color/rgbcolor.h"
#include "util/performancetimer.h"
-#include "util/color/color.h"
-#include "util/color/predefinedcolor.h"
namespace {
@@ -37,7 +37,7 @@ inline QString labelFromQVariant(const QVariant& value) {
}
}
-}
+} // namespace
int CueDAO::cueCount() {
qDebug() << "CueDAO::cueCount" << QThread::currentThread() << m_database.connectionName();
@@ -78,9 +78,18 @@ 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 = labelFromQVariant(record.value(record.indexOf("label")));
- int iColorId = record.value(record.indexOf("color")).toInt();
- PredefinedColorPointer color = Color::kPredefinedColorsSet.predefinedColorFromId(iColorId);
- CuePointer pCue(new Cue(id, trackId, (mixxx::CueType)type, position, length, hotcue, label, color));
+ mixxx::RgbColor::optional_t color = mixxx::RgbColor::fromQVariant(record.value(record.indexOf("color")));
+ VERIFY_OR_DEBUG_ASSERT(color) {
+ return CuePointer();
+ }
+ CuePointer pCue(new Cue(id,
+ trackId,
+ static_cast(type),
+ position,
+ length,
+ hotcue,
+ label,
+ *color));
m_cues[id] = pCue;
return pCue;
}
@@ -171,9 +180,8 @@ bool CueDAO::saveCue(Cue* cue) {
query.bindValue(":position", cue->getPosition());
query.bindValue(":length", cue->getLength());
query.bindValue(":hotcue", cue->getHotCue());
-
query.bindValue(":label", labelToQVariant(cue->getLabel()));
- query.bindValue(":color", cue->getColor()->m_iId);
+ query.bindValue(":color", mixxx::RgbColor::toQVariant(cue->getColor()));
if (query.exec()) {
int id = query.lastInsertId().toInt();
@@ -201,7 +209,7 @@ bool CueDAO::saveCue(Cue* cue) {
query.bindValue(":length", cue->getLength());
query.bindValue(":hotcue", cue->getHotCue());
query.bindValue(":label", labelToQVariant(cue->getLabel()));
- query.bindValue(":color", cue->getColor()->m_iId);
+ query.bindValue(":color", mixxx::RgbColor::toQVariant(cue->getColor()));
if (query.exec()) {
cue->setDirty(false);
diff --git a/src/library/dlgtrackinfo.cpp b/src/library/dlgtrackinfo.cpp
index fdffce1de51c..35848013acd8 100644
--- a/src/library/dlgtrackinfo.cpp
+++ b/src/library/dlgtrackinfo.cpp
@@ -1,16 +1,19 @@
+#include "library/dlgtrackinfo.h"
+
+#include
#include
+#include
#include
-#include
#include "library/coverartcache.h"
#include "library/coverartutils.h"
-#include "library/dlgtrackinfo.h"
+#include "preferences/colorpalettesettings.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/colorpalette.h"
#include "util/compatibility.h"
#include "util/desktophelper.h"
#include "util/duration.h"
@@ -21,11 +24,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();
}
@@ -36,7 +40,6 @@ DlgTrackInfo::~DlgTrackInfo() {
void DlgTrackInfo::init() {
setupUi(this);
- cueTable->hideColumn(0);
coverBox->insertWidget(1, m_pWCoverArtLabel);
connect(btnNext, &QPushButton::clicked, this, &DlgTrackInfo::slotNext);
@@ -89,14 +92,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(),
@@ -162,39 +157,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());
- }
-
- // TODO: QList::fromSet(const QSet&) is deprecated and should be
- // replaced with QList(set.begin(), set.end()).
- // However, the proposed alternative has just been introduced in Qt
- // 5.14. Until the minimum required Qt version of Mixx is increased,
- // we need a version check here
-#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
- QList rowsList = QList(rowsToDelete.begin(), rowsToDelete.end());
-#else
- QList rowsList = QList::fromSet(rowsToDelete);
-#endif
- 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());
@@ -260,7 +222,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
@@ -313,129 +274,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();
- mixxx::CueType type = pCue->getType();
- if (type == mixxx::CueType::HotCue || type == mixxx::CueType::MainCue || type == mixxx::CueType::Intro || type == mixxx::CueType::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 mixxx::CueType::Invalid:
- cueType = "?";
- break;
- case mixxx::CueType::HotCue:
- cueType = "Hotcue";
- break;
- case mixxx::CueType::MainCue:
- cueType = "Main Cue";
- break;
- case mixxx::CueType::Beat:
- cueType = "Beat";
- break;
- case mixxx::CueType::Loop:
- cueType = "Loop";
- break;
- case mixxx::CueType::Jump:
- cueType = "Jump";
- break;
- case mixxx::CueType::Intro:
- cueType = "Intro";
- break;
- case mixxx::CueType::Outro:
- cueType = "Outro";
- break;
- default:
- break;
- }
-
- QTableWidgetItem* typeItem = new QTableWidgetItem(cueType);
- // Make the type read only
- typeItem->setFlags(Qt::NoItemFlags);
-
- 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);
- }
- }
- PredefinedColorPointer cueColor = pCue->getColor();
- colorComboBox->setCurrentIndex(Color::kPredefinedColorsSet.predefinedColorIndex(cueColor));
-
- 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;
@@ -470,67 +308,6 @@ void DlgTrackInfo::saveTrack() {
slotKeyTextChanged();
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() == mixxx::CueType::HotCue) {
- auto colorComboBox = qobject_cast(colorWidget);
- if (colorComboBox) {
- PredefinedColorPointer color = Color::kPredefinedColorsSet.allColors.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.
@@ -582,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 eb721c96c79f..98dc17c5279d 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:
@@ -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();
@@ -73,12 +70,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;
@@ -89,6 +84,7 @@ class DlgTrackInfo : public QDialog, public Ui::DlgTrackInfo {
CoverInfo m_loadedCoverInfo;
WCoverArtLabel* m_pWCoverArtLabel;
+ UserSettingsPointer m_pConfig;
};
#endif /* DLGTRACKINFO_H */
diff --git a/src/library/dlgtrackinfo.ui b/src/library/dlgtrackinfo.ui
index 4435134c7a04..caf42ac2c25a 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
diff --git a/src/preferences/colorpalettesettings.cpp b/src/preferences/colorpalettesettings.cpp
new file mode 100644
index 000000000000..63976e4cbdb1
--- /dev/null
+++ b/src/preferences/colorpalettesettings.cpp
@@ -0,0 +1,112 @@
+#include "preferences/colorpalettesettings.h"
+
+namespace {
+const mixxx::RgbColor kColorBlack(0x000000);
+const QString kColorPaletteConfigGroup = QStringLiteral("[Config]");
+const QString kColorPaletteGroupStart = QStringLiteral("[ColorPalette ");
+const QString kColorPaletteGroupEnd = QStringLiteral("]");
+const QRegExp kColorPaletteGroupNameRegex(QStringLiteral("^\\[ColorPalette (.+)\\]$"));
+const ConfigKey kHotcueColorPaletteConfigKey(kColorPaletteConfigGroup, QStringLiteral("HotcueColorPalette"));
+const ConfigKey kTrackColorPaletteConfigKey(kColorPaletteConfigGroup, QStringLiteral("TrackColorPalette"));
+
+int numberOfDecimalDigits(int number) {
+ int numDigits = 1;
+ while (number /= 10) {
+ numDigits++;
+ }
+ return numDigits;
+}
+
+ConfigKey keyForIndex(const QString& group, int index, int numDigits) {
+ return ConfigKey(group, QString::number(index).rightJustified(numDigits, '0'));
+}
+
+} // anonymous namespace
+
+ColorPalette ColorPaletteSettings::getColorPalette(
+ const QString& name, const ColorPalette& defaultPalette) const {
+ QList colorList;
+
+ const QString group = kColorPaletteGroupStart + name + kColorPaletteGroupEnd;
+ for (const ConfigKey& key : m_pConfig->getKeysWithGroup(group)) {
+ mixxx::RgbColor color = mixxx::RgbColor(m_pConfig->getValue(key, kColorBlack));
+ colorList.append(color);
+ }
+
+ // If no palette is defined in the settings, we use the default one.
+ if (colorList.isEmpty()) {
+ return defaultPalette;
+ }
+
+ return ColorPalette(name, colorList);
+}
+
+void ColorPaletteSettings::setColorPalette(const QString& name, const ColorPalette& colorPalette) {
+ VERIFY_OR_DEBUG_ASSERT(!name.isEmpty()) {
+ qWarning() << "Palette name must not be empty!";
+ return;
+ }
+
+ removePalette(name);
+ const QString group = kColorPaletteGroupStart + name + kColorPaletteGroupEnd;
+
+ int numDigits = numberOfDecimalDigits(colorPalette.size() - 1);
+ for (int index = 0; index < colorPalette.size(); ++index) {
+ mixxx::RgbColor color = colorPalette.at(index);
+ m_pConfig->setValue(keyForIndex(group, index, numDigits), color);
+ }
+}
+
+void ColorPaletteSettings::removePalette(const QString& name) {
+ const QString group = kColorPaletteGroupStart + name + kColorPaletteGroupEnd;
+ for (const ConfigKey& key : m_pConfig->getKeysWithGroup(group)) {
+ m_pConfig->remove(key);
+ }
+}
+
+ColorPalette ColorPaletteSettings::getHotcueColorPalette() const {
+ QString name = m_pConfig->getValueString(kHotcueColorPaletteConfigKey);
+ if (name.isEmpty()) {
+ return ColorPalette::mixxxHotcuePalette;
+ }
+ return getColorPalette(name, ColorPalette::mixxxHotcuePalette);
+}
+
+void ColorPaletteSettings::setHotcueColorPalette(const ColorPalette& colorPalette) {
+ QString name = colorPalette.getName();
+ VERIFY_OR_DEBUG_ASSERT(!name.isEmpty()) {
+ qWarning() << "Palette name must not be empty!";
+ return;
+ }
+ m_pConfig->setValue(kHotcueColorPaletteConfigKey, name);
+ setColorPalette(name, colorPalette);
+}
+
+ColorPalette ColorPaletteSettings::getTrackColorPalette() const {
+ QString name = m_pConfig->getValueString(kTrackColorPaletteConfigKey);
+ if (name.isEmpty()) {
+ return ColorPalette::mixxxHotcuePalette;
+ }
+ return getColorPalette(name, ColorPalette::mixxxHotcuePalette);
+}
+
+void ColorPaletteSettings::setTrackColorPalette(const ColorPalette& colorPalette) {
+ QString name = colorPalette.getName();
+ VERIFY_OR_DEBUG_ASSERT(!name.isEmpty()) {
+ qWarning() << "Palette name must not be empty!";
+ return;
+ }
+ m_pConfig->setValue(kTrackColorPaletteConfigKey, name);
+ setColorPalette(name, colorPalette);
+}
+
+QSet ColorPaletteSettings::getColorPaletteNames() const {
+ QSet names;
+ for (const QString& group : m_pConfig->getGroups()) {
+ int pos = kColorPaletteGroupNameRegex.indexIn(group);
+ if (pos > -1) {
+ names.insert(kColorPaletteGroupNameRegex.cap(1));
+ }
+ }
+ return names;
+}
diff --git a/src/preferences/colorpalettesettings.h b/src/preferences/colorpalettesettings.h
new file mode 100644
index 000000000000..39ce03e49e18
--- /dev/null
+++ b/src/preferences/colorpalettesettings.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include "preferences/usersettings.h"
+#include "util/color/colorpalette.h"
+
+class ColorPaletteSettings {
+ public:
+ explicit ColorPaletteSettings(UserSettingsPointer pConfig)
+ : m_pConfig(pConfig) {
+ }
+
+ ColorPalette getHotcueColorPalette() const;
+ void setHotcueColorPalette(const ColorPalette& colorPalette);
+
+ ColorPalette getTrackColorPalette() const;
+ void setTrackColorPalette(const ColorPalette& colorPalette);
+
+ ColorPalette getColorPalette(const QString& name, const ColorPalette& defaultPalette) const;
+ void setColorPalette(const QString& name, const ColorPalette& colorPalette);
+ void removePalette(const QString& name);
+ QSet getColorPaletteNames() const;
+
+ private:
+ UserSettingsPointer m_pConfig;
+};
diff --git a/src/preferences/configobject.cpp b/src/preferences/configobject.cpp
index 6eecb2bb991f..d49617887720 100644
--- a/src/preferences/configobject.cpp
+++ b/src/preferences/configobject.cpp
@@ -1,14 +1,15 @@
#include "preferences/configobject.h"
-#include
-#include
#include
#include
+#include
+#include
#include
-#include "widget/wwidget.h"
#include "util/cmdlineargs.h"
+#include "util/color/rgbcolor.h"
#include "util/xml.h"
+#include "widget/wwidget.h"
// TODO(rryan): Move to a utility file.
namespace {
@@ -215,6 +216,28 @@ template void ConfigObject::save() {
}
}
+template
+QSet ConfigObject::getGroups() {
+ QWriteLocker lock(&m_valuesLock);
+ QSet groups;
+ for (const ConfigKey& key : m_values.uniqueKeys()) {
+ groups.insert(key.group);
+ }
+ return groups;
+}
+
+template
+QList ConfigObject::getKeysWithGroup(QString group) const {
+ 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();
@@ -270,6 +293,24 @@ void ConfigObject::setValue(
set(key, ConfigValue(QString::number(value)));
}
+template<>
+template<>
+void ConfigObject::setValue(
+ const ConfigKey& key, const mixxx::RgbColor::optional_t& value) {
+ if (!value) {
+ remove(key);
+ return;
+ }
+ set(key, ConfigValue(mixxx::RgbColor::toQString(value)));
+}
+
+template<>
+template<>
+void ConfigObject::setValue(
+ const ConfigKey& key, const mixxx::RgbColor& value) {
+ set(key, ConfigValue(mixxx::RgbColor::toQString(value)));
+}
+
template <> template <>
bool ConfigObject::getValue(
const ConfigKey& key, const bool& default_value) const {
@@ -306,6 +347,40 @@ double ConfigObject::getValue(
return ok ? result : default_value;
}
+template<>
+template<>
+mixxx::RgbColor::optional_t ConfigObject::getValue(
+ const ConfigKey& key, const mixxx::RgbColor::optional_t& default_value) const {
+ const ConfigValue value = get(key);
+ if (value.isNull()) {
+ return default_value;
+ }
+ return mixxx::RgbColor::fromQString(value.value, default_value);
+}
+
+template<>
+template<>
+mixxx::RgbColor::optional_t ConfigObject::getValue(const ConfigKey& key) const {
+ return getValue(key, mixxx::RgbColor::optional_t(std::nullopt));
+}
+
+template<>
+template<>
+mixxx::RgbColor ConfigObject::getValue(
+ const ConfigKey& key, const mixxx::RgbColor& default_value) const {
+ const mixxx::RgbColor::optional_t value = getValue(key, mixxx::RgbColor::optional_t(std::nullopt));
+ if (!value) {
+ return default_value;
+ }
+ return *value;
+}
+
+template<>
+template<>
+mixxx::RgbColor ConfigObject::getValue(const ConfigKey& key) const {
+ return getValue(key, mixxx::RgbColor(0));
+}
+
// For string literal default
template <>
QString ConfigObject::getValue(
diff --git a/src/preferences/configobject.h b/src/preferences/configobject.h
index 3e4ba6aa20e4..93d1b2425231 100644
--- a/src/preferences/configobject.h
+++ b/src/preferences/configobject.h
@@ -187,6 +187,9 @@ template class ConfigObject {
return m_settingsPath;
}
+ QSet getGroups();
+ QList getKeysWithGroup(QString group) const;
+
protected:
// We use QMap because we want a sorted list in mixxx.cfg
QMap m_values;
diff --git a/src/skin/skincontext.h b/src/skin/skincontext.h
index 10bba69feffc..8898b337f35b 100644
--- a/src/skin/skincontext.h
+++ b/src/skin/skincontext.h
@@ -15,7 +15,6 @@
#include "preferences/usersettings.h"
#include "skin/pixmapsource.h"
#include "util/color/color.h"
-#include "util/color/predefinedcolorsrepresentation.h"
#include "widget/wsingletoncontainer.h"
#include "widget/wpixmapstore.h"
@@ -258,20 +257,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/test/colorconfig_test.cpp b/src/test/colorconfig_test.cpp
new file mode 100644
index 000000000000..58a17f706a29
--- /dev/null
+++ b/src/test/colorconfig_test.cpp
@@ -0,0 +1,105 @@
+#include
+
+#include "preferences/colorpalettesettings.h"
+#include "test/mixxxtest.h"
+#include "util/color/colorpalette.h"
+#include "util/color/rgbcolor.h"
+
+class ColorConfigTest : public MixxxTest {};
+
+TEST_F(ColorConfigTest, SavingColor) {
+ ConfigKey key("[Color]", "color");
+ mixxx::RgbColor originalColor(0xFF9900);
+ config()->setValue(key, originalColor);
+ saveAndReloadConfig();
+ mixxx::RgbColor colorFromConfig = config()->getValue(key, mixxx::RgbColor(0));
+ ASSERT_EQ(originalColor, colorFromConfig);
+}
+
+TEST_F(ColorConfigTest, GetDefaultColorWhenNoStoredColor) {
+ ConfigKey key("[Color]", "color");
+ mixxx::RgbColor defaultColor(0x66FF99);
+ mixxx::RgbColor colorFromConfig = config()->getValue(key, defaultColor);
+ ASSERT_EQ(defaultColor, colorFromConfig);
+}
+
+TEST_F(ColorConfigTest, SaveColorPalette) {
+ ColorPaletteSettings colorPaletteSettings(config());
+ ColorPalette originalColorPalette(
+ "SaveColorPaletteTest", QList{
+ mixxx::RgbColor(0x66FF99),
+ mixxx::RgbColor(0xFF9900),
+ mixxx::RgbColor(0x000000),
+ mixxx::RgbColor(0xFFFFFF),
+ });
+ colorPaletteSettings.setHotcueColorPalette(originalColorPalette);
+ saveAndReloadConfig();
+ ColorPalette colorPaletteFromConfig =
+ colorPaletteSettings.getHotcueColorPalette();
+ ASSERT_EQ(originalColorPalette, colorPaletteFromConfig);
+}
+
+TEST_F(ColorConfigTest, ReplaceColorPalette) {
+ ColorPaletteSettings colorPaletteSettings(config());
+ ColorPalette colorPalette1(
+ "ReplaceColorPaletteTest", QList{
+ mixxx::RgbColor(0x66FF99),
+ mixxx::RgbColor(0xFF9900),
+ mixxx::RgbColor(0x000000),
+ mixxx::RgbColor(0xFFFFFF),
+ });
+ ColorPalette colorPalette2(
+ "ReplaceColorPaletteTest", QList{
+ mixxx::RgbColor(0x0000FF),
+ mixxx::RgbColor(0xFF0000),
+ });
+ colorPaletteSettings.setHotcueColorPalette(colorPalette1);
+ saveAndReloadConfig();
+ colorPaletteSettings.setHotcueColorPalette(colorPalette2);
+ saveAndReloadConfig();
+ ColorPalette colorPaletteFromConfig =
+ colorPaletteSettings.getHotcueColorPalette();
+ ASSERT_EQ(colorPalette2, colorPaletteFromConfig);
+}
+
+TEST_F(ColorConfigTest, LoadSavePalettes) {
+ const QString kName1 = QStringLiteral("Custom Palette No. 1");
+ const QString kName2 = QStringLiteral("My Custom Palette 2");
+ const QString kName3 = QStringLiteral("I'm blue, da ba dee");
+ ColorPaletteSettings colorPaletteSettings(config());
+ ColorPalette colorPalette1(
+ kName1, QList{
+ mixxx::RgbColor(0x66FF99),
+ mixxx::RgbColor(0xFF9900),
+ mixxx::RgbColor(0x000000),
+ mixxx::RgbColor(0xFFFFFF),
+ });
+ colorPaletteSettings.setColorPalette(colorPalette1.getName(), colorPalette1);
+ ColorPalette colorPalette2(
+ kName2, QList{
+ mixxx::RgbColor(0x0000FF),
+ mixxx::RgbColor(0xFF0000),
+ });
+ colorPaletteSettings.setColorPalette(colorPalette2.getName(), colorPalette2);
+ ColorPalette colorPalette3(
+ kName3, QList{
+ mixxx::RgbColor(0x0000FF),
+ mixxx::RgbColor(0x123456),
+ mixxx::RgbColor(0x000080),
+ });
+ colorPaletteSettings.setColorPalette(colorPalette3.getName(), colorPalette3);
+ saveAndReloadConfig();
+ QSet expectedNames{
+ kName1,
+ kName2,
+ kName3,
+ };
+ ASSERT_EQ(expectedNames, colorPaletteSettings.getColorPaletteNames());
+}
+
+TEST_F(ColorConfigTest, DefaultColorPalette) {
+ ColorPaletteSettings colorPaletteSettings(config());
+ ColorPalette colorPaletteFromConfig =
+ colorPaletteSettings.getHotcueColorPalette();
+ ASSERT_EQ(ColorPalette::mixxxHotcuePalette, colorPaletteFromConfig);
+}
diff --git a/src/test/colormapperjsproxy_test.cpp b/src/test/colormapperjsproxy_test.cpp
new file mode 100644
index 000000000000..4f1a291aadf9
--- /dev/null
+++ b/src/test/colormapperjsproxy_test.cpp
@@ -0,0 +1,128 @@
+#include "controllers/colormapperjsproxy.h"
+
+#include
+
+#include "test/mixxxtest.h"
+
+namespace {
+
+QScriptEngine* createScriptEngine() {
+ QScriptEngine* pEngine = new QScriptEngine();
+ QScriptValue constructor = pEngine->newFunction(ColorMapperJSProxyConstructor);
+ QScriptValue metaObject = pEngine->newQMetaObject(&ColorMapperJSProxy::staticMetaObject, constructor);
+ pEngine->globalObject().setProperty("ColorMapper", metaObject);
+ return pEngine;
+}
+
+} // namespace
+
+class ColorMapperJSProxyTest : public MixxxTest {};
+
+TEST_F(ColorMapperJSProxyTest, Instantiation) {
+ QScriptEngine* pEngine = createScriptEngine();
+
+ // Valid instantiation
+ pEngine->evaluate(
+ R"JavaScript(
+ var mapper = new ColorMapper({
+ '#FF0000': 1,
+ '#00FF00': 2,
+ '#0000FF': 3,
+ });
+ )JavaScript");
+ EXPECT_FALSE(pEngine->hasUncaughtException());
+ pEngine->clearExceptions();
+
+ // Invalid instantiation: no arguments
+ pEngine->evaluate("var mapper = new ColorMapper();");
+ EXPECT_TRUE(pEngine->hasUncaughtException());
+ pEngine->clearExceptions();
+
+ // Invalid instantiation: invalid argument
+ pEngine->evaluate("var mapper = new ColorMapper('hello');");
+ EXPECT_TRUE(pEngine->hasUncaughtException());
+ pEngine->clearExceptions();
+
+ // Invalid instantiation: argument is an empty object
+ pEngine->evaluate("var mapper = new ColorMapper({});");
+ EXPECT_TRUE(pEngine->hasUncaughtException());
+ pEngine->clearExceptions();
+
+ // Invalid instantiation: argument is an empty object
+ pEngine->evaluate(
+ R"JavaScript(
+ var mapper = new ColorMapper({
+ 'not a color': 1
+ });
+ )JavaScript");
+ EXPECT_TRUE(pEngine->hasUncaughtException());
+ pEngine->clearExceptions();
+}
+
+TEST_F(ColorMapperJSProxyTest, GetNearestColor) {
+ QScriptEngine* pEngine = createScriptEngine();
+ pEngine->evaluate(
+ R"JavaScript(
+ var mapper = new ColorMapper({
+ '#C50A08': 1,
+ '#32BE44': 2,
+ '#42D4F4': 3,
+ '#F8D200': 4,
+ '#0044FF': 5,
+ '#AF00CC': 6,
+ '#FCA6D7': 7,
+ '#F2F2FF': 8,
+ });
+ /* white */
+ var color1 = mapper.getNearestColor(0xFFFFFF);
+ if (color1.red != 0xF2 || color1.green != 0xF2 || color1.blue != 0xFF) {
+ throw Error();
+ };
+ /* white */
+ var color2 = mapper.getNearestColor(0xDCDCDC);
+ if (color2.red != 0xF2 || color2.green != 0xF2 || color2.blue != 0xFF) {
+ throw Error();
+ };
+ /* red */
+ var color3 = mapper.getNearestColor(0xFF0000);
+ if (color3.red != 0xC5 || color3.green != 0x0A || color3.blue != 0x08) {
+ throw Error();
+ };
+ /* yellow */
+ var color4 = mapper.getNearestColor(0x22CC22);
+ if (color4.red != 0x32 || color4.green != 0xBE || color4.blue != 0x44) {
+ throw Error();
+ }
+ )JavaScript");
+ EXPECT_FALSE(pEngine->hasUncaughtException());
+}
+
+TEST_F(ColorMapperJSProxyTest, GetNearestValue) {
+ QScriptEngine* pEngine = createScriptEngine();
+ pEngine->evaluate(
+ R"JavaScript(
+ var mapper = new ColorMapper({
+ '#C50A08': 1,
+ '#32BE44': 2,
+ '#42D4F4': 3,
+ '#F8D200': 4,
+ '#0044FF': 5,
+ '#AF00CC': 6,
+ '#FCA6D7': 7,
+ '#F2F2FF': 8,
+ });
+ /* red */
+ if (mapper.getValueForNearestColor(0xFF0000) != 1) {
+ throw Error();
+ };
+ /* blue */
+ if (mapper.getValueForNearestColor(0x0000AA) != 5) {
+ throw Error();
+ };
+ /* white */
+ if (mapper.getValueForNearestColor(0xFFFFFF) != 8) {
+ throw Error();
+ };
+ )JavaScript");
+ EXPECT_FALSE(pEngine->hasUncaughtException());
+}
diff --git a/src/test/colorpalette_test.cpp b/src/test/colorpalette_test.cpp
new file mode 100644
index 000000000000..77b13d731de3
--- /dev/null
+++ b/src/test/colorpalette_test.cpp
@@ -0,0 +1,30 @@
+#include "util/color/colorpalette.h"
+
+#include
+
+#include "test/mixxxtest.h"
+
+class ColorPaletteTest : public MixxxTest {};
+
+TEST_F(ColorPaletteTest, NextColor) {
+ const ColorPalette palette = ColorPalette::mixxxHotcuePalette;
+ ASSERT_TRUE(palette.size() >= 1);
+ ASSERT_EQ(palette.nextColor(palette.at(0)), palette.at(1));
+ ASSERT_EQ(palette.nextColor(palette.at(palette.size() - 1)), palette.at(0));
+}
+
+TEST_F(ColorPaletteTest, PreviousColor) {
+ const ColorPalette palette = ColorPalette::mixxxHotcuePalette;
+ ASSERT_TRUE(palette.size() >= 1);
+ ASSERT_EQ(palette.previousColor(palette.at(1)), palette.at(0));
+ ASSERT_EQ(palette.previousColor(palette.at(0)), palette.at(palette.size() - 1));
+}
+
+TEST_F(ColorPaletteTest, NextAndPreviousColorRoundtrip) {
+ const ColorPalette palette = ColorPalette::mixxxHotcuePalette;
+ ASSERT_TRUE(palette.size() >= 1);
+ ASSERT_EQ(palette.nextColor(palette.previousColor(palette.at(0))), palette.at(0));
+ ASSERT_EQ(palette.nextColor(palette.previousColor(palette.at(palette.size() - 1))), palette.at(palette.size() - 1));
+ ASSERT_EQ(palette.previousColor(palette.nextColor(palette.at(0))), palette.at(0));
+ ASSERT_EQ(palette.previousColor(palette.nextColor(palette.at(palette.size() - 1))), palette.at(palette.size() - 1));
+}
diff --git a/src/test/controller_preset_validation_test.cpp b/src/test/controller_preset_validation_test.cpp
index d49ee3e50bac..a1a65f4015ff 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();
+ explicit 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/controllerengine_test.cpp b/src/test/controllerengine_test.cpp
index d84f736a6ff8..4b16d53b8e7a 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/colorpalette.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);
@@ -617,25 +618,3 @@ TEST_F(ControllerEngineTest, connectionExecutesWithCorrectThisObject) {
// The counter should have been incremented exactly once.
EXPECT_DOUBLE_EQ(1.0, pass->get());
}
-
-TEST_F(ControllerEngineTest, colorProxy) {
- QList allColors = Color::kPredefinedColorsSet.allColors;
- 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);
- }
-}
diff --git a/src/test/cue_test.cpp b/src/test/cue_test.cpp
index 0518c15eaa3b..8154d6104d9d 100644
--- a/src/test/cue_test.cpp
+++ b/src/test/cue_test.cpp
@@ -2,17 +2,17 @@
#include
-#include "test/mixxxtest.h"
-
#include "engine/engine.h"
+#include "test/mixxxtest.h"
#include "util/color/color.h"
namespace mixxx {
TEST(CueTest, DefaultCueToCueInfoTest) {
const Cue cueObject;
- const auto cueInfo = cueObject.getCueInfo(
+ auto cueInfo = cueObject.getCueInfo(
AudioSignal::SampleRate(44100));
+ cueInfo.setColor(std::nullopt);
EXPECT_EQ(CueInfo(), cueInfo);
}
@@ -21,8 +21,9 @@ TEST(CueTest, DefaultCueInfoToCueRoundtrip) {
const Cue cueObject(
cueInfo1,
AudioSignal::SampleRate(44100));
- const auto cueInfo2 = cueObject.getCueInfo(
+ auto cueInfo2 = cueObject.getCueInfo(
AudioSignal::SampleRate(44100));
+ cueInfo2.setColor(std::nullopt);
EXPECT_EQ(cueInfo1, cueInfo2);
}
@@ -30,16 +31,13 @@ TEST(CueTest, ConvertCueInfoToCueRoundtrip) {
// Due to rounding errors this test may fail if the
// cue position/sample conversions don't always result
// in integer numbers.
- const auto predefinedColor =
- Color::kPredefinedColorsSet.predefinedColorFromRgbColor(
- RgbColor::optional(0xabcdef))->m_defaultRgba;
const auto cueInfo1 = CueInfo(
CueType::HotCue,
std::make_optional(1.0 * 44100 * mixxx::kEngineChannelCount),
std::make_optional(2.0 * 44100 * mixxx::kEngineChannelCount),
std::make_optional(3),
QStringLiteral("label"),
- RgbColor::fromQColor(predefinedColor));
+ RgbColor::optional(0xABCDEF));
const Cue cueObject(
cueInfo1,
AudioSignal::SampleRate(44100));
diff --git a/src/test/midicontrollertest.cpp b/src/test/midicontrollertest.cpp
index 696483c05fd1..ac520908638e 100644
--- a/src/test/midicontrollertest.cpp
+++ b/src/test/midicontrollertest.cpp
@@ -12,7 +12,9 @@
class MockMidiController : public MidiController {
public:
- MockMidiController() { }
+ explicit MockMidiController(UserSettingsPointer pConfig)
+ : MidiController(pConfig) {
+ }
~MockMidiController() override { }
MOCK_METHOD0(open, int());
@@ -27,7 +29,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 ad007b790958..9d8fccdaa697 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);
}
diff --git a/src/track/cue.cpp b/src/track/cue.cpp
index c5dc66f8622a..a923ab7a9416 100644
--- a/src/track/cue.cpp
+++ b/src/track/cue.cpp
@@ -9,6 +9,7 @@
#include "engine/engine.h"
#include "util/assert.h"
#include "util/color/color.h"
+#include "util/color/colorpalette.h"
namespace {
@@ -37,7 +38,6 @@ inline double positionMillisToSamples(
// Try to avoid rounding errors
return (*positionMillis * sampleRate * mixxx::kEngineChannelCount) / 1000;
}
-
}
//static
@@ -54,7 +54,7 @@ Cue::Cue()
m_sampleStartPosition(Cue::kNoPosition),
m_sampleEndPosition(Cue::kNoPosition),
m_iHotCue(Cue::kNoHotCue),
- m_color(Color::kPredefinedColorsSet.noColor) {
+ m_color(ColorPalette::kDefaultCueColor) {
}
Cue::Cue(
@@ -65,7 +65,7 @@ Cue::Cue(
double length,
int hotCue,
QString label,
- PredefinedColorPointer color)
+ mixxx::RgbColor color)
: m_bDirty(false),
m_iId(id),
m_trackId(trackId),
@@ -101,7 +101,7 @@ Cue::Cue(
sampleRate)),
m_iHotCue(cueInfo.getHotCueNumber() ? *cueInfo.getHotCueNumber() : kNoHotCue),
m_label(cueInfo.getLabel()),
- m_color(Color::kPredefinedColorsSet.predefinedColorFromRgbColor(cueInfo.getColor())) {
+ m_color(cueInfo.getColor().value_or(ColorPalette::kDefaultCueColor)) {
}
mixxx::CueInfo Cue::getCueInfo(
@@ -113,7 +113,7 @@ mixxx::CueInfo Cue::getCueInfo(
positionSamplesToMillis(m_sampleEndPosition, sampleRate),
m_iHotCue == kNoHotCue ? std::nullopt : std::make_optional(m_iHotCue),
m_label,
- m_color ? mixxx::RgbColor::fromQColor(m_color->m_defaultRgba) : std::nullopt);
+ m_color);
}
int Cue::getId() const {
@@ -214,12 +214,12 @@ void Cue::setLabel(const QString label) {
emit updated();
}
-PredefinedColorPointer Cue::getColor() const {
+mixxx::RgbColor Cue::getColor() const {
QMutexLocker lock(&m_mutex);
return m_color;
}
-void Cue::setColor(const PredefinedColorPointer color) {
+void Cue::setColor(mixxx::RgbColor color) {
QMutexLocker lock(&m_mutex);
m_color = color;
m_bDirty = true;
diff --git a/src/track/cue.h b/src/track/cue.h
index 0ce931eebf06..14f605c28f73 100644
--- a/src/track/cue.h
+++ b/src/track/cue.h
@@ -7,7 +7,7 @@
#include "track/cueinfo.h"
#include "track/trackid.h"
#include "util/audiosignal.h"
-#include "util/color/predefinedcolor.h"
+#include "util/color/rgbcolor.h"
#include "util/memory.h"
class CuePosition;
@@ -50,8 +50,8 @@ class Cue : public QObject {
void setLabel(
QString label = QString());
- PredefinedColorPointer getColor() const;
- void setColor(PredefinedColorPointer color);
+ mixxx::RgbColor getColor() const;
+ void setColor(mixxx::RgbColor color);
double getEndPosition() const;
@@ -70,7 +70,7 @@ class Cue : public QObject {
double length,
int hotCue,
QString label,
- PredefinedColorPointer color);
+ mixxx::RgbColor color);
void setDirty(bool dirty);
@@ -87,7 +87,7 @@ class Cue : public QObject {
double m_sampleEndPosition;
int m_iHotCue;
QString m_label;
- PredefinedColorPointer m_color;
+ mixxx::RgbColor m_color;
friend class Track;
friend class CueDAO;
diff --git a/src/util/color/color.cpp b/src/util/color/color.cpp
index 4627fdf3d9a5..92116acb7482 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 13401e05254e..e5196a409e79 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/colorpalette.cpp b/src/util/color/colorpalette.cpp
new file mode 100644
index 000000000000..a606f8e73b4e
--- /dev/null
+++ b/src/util/color/colorpalette.cpp
@@ -0,0 +1,55 @@
+#include "colorpalette.h"
+
+namespace {
+
+constexpr mixxx::RgbColor kColorMixxxRed(0xC50A08);
+constexpr mixxx::RgbColor kColorMixxxYellow(0x32BE44);
+constexpr mixxx::RgbColor kColorMixxxGreen(0x42D4F4);
+constexpr mixxx::RgbColor kColorMixxxCeleste(0xF8D200);
+constexpr mixxx::RgbColor kColorMixxxBlue(0x0044FF);
+constexpr mixxx::RgbColor kColorMixxxPurple(0xAF00CC);
+constexpr mixxx::RgbColor kColorMixxxPink(0xFCA6D7);
+constexpr mixxx::RgbColor kColorMixxxWhite(0xF2F2FF);
+
+// Replaces "no color" values and is used for new cues if auto_hotcue_colors is
+// disabled
+constexpr mixxx::RgbColor kSchemaMigrationReplacementColor(0xFF8C00);
+
+} // anonymous namespace
+
+const ColorPalette ColorPalette::mixxxHotcuePalette =
+ ColorPalette(
+ QStringLiteral("Mixxx Hotcue Colors"),
+ QList{
+ kColorMixxxRed,
+ kColorMixxxYellow,
+ kColorMixxxGreen,
+ kColorMixxxCeleste,
+ kColorMixxxBlue,
+ kColorMixxxPurple,
+ kColorMixxxPink,
+ kColorMixxxWhite,
+ });
+
+const mixxx::RgbColor ColorPalette::kDefaultCueColor = kSchemaMigrationReplacementColor;
+
+mixxx::RgbColor ColorPalette::nextColor(mixxx::RgbColor color) const {
+ // Return first color if color not in palette ((-1 + 1) % size)
+ return at((indexOf(color) + 1) % size());
+}
+
+mixxx::RgbColor ColorPalette::previousColor(mixxx::RgbColor color) const {
+ int iIndex = indexOf(color);
+ if (iIndex < 0) {
+ // Return last color if color not in palette
+ iIndex = size() - 1;
+ } else {
+ iIndex = (iIndex + size() - 1) % size();
+ }
+ return at(iIndex);
+}
+
+mixxx::RgbColor ColorPalette::colorForHotcueIndex(unsigned int index) const {
+ // For hotcue n, get nth color from palette
+ return at(index % size());
+}
diff --git a/src/util/color/colorpalette.h b/src/util/color/colorpalette.h
new file mode 100644
index 000000000000..1f0165a8b396
--- /dev/null
+++ b/src/util/color/colorpalette.h
@@ -0,0 +1,63 @@
+#pragma once
+
+#include
+
+#include "util/color/rgbcolor.h"
+
+class ColorPalette final {
+ public:
+ explicit ColorPalette(QString name, QList colorList)
+ : m_name(name),
+ m_colorList(colorList) {
+ DEBUG_ASSERT(m_colorList.size() != 0);
+ }
+
+ mixxx::RgbColor at(int i) const {
+ return m_colorList.at(i);
+ }
+
+ int size() const {
+ return m_colorList.size();
+ }
+
+ int indexOf(mixxx::RgbColor color) const {
+ return m_colorList.indexOf(color);
+ }
+
+ mixxx::RgbColor nextColor(mixxx::RgbColor color) const;
+ mixxx::RgbColor previousColor(mixxx::RgbColor color) const;
+ mixxx::RgbColor colorForHotcueIndex(unsigned int index) const;
+
+ QList::const_iterator begin() const {
+ return m_colorList.begin();
+ }
+
+ QList::const_iterator end() const {
+ return m_colorList.end();
+ }
+
+ QString getName() const {
+ return m_name;
+ }
+
+ void setName(const QString name) {
+ m_name = name;
+ }
+
+ static const ColorPalette mixxxHotcuePalette;
+ static const mixxx::RgbColor kDefaultCueColor;
+
+ const QList& getColorList() const {
+ return m_colorList;
+ }
+
+ private:
+ QString m_name;
+ QList m_colorList;
+};
+
+inline bool operator==(
+ const ColorPalette& lhs, const ColorPalette& rhs) {
+ return lhs.getName() == rhs.getName() &&
+ lhs.getColorList() == rhs.getColorList();
+}
diff --git a/src/util/color/predefinedcolor.cpp b/src/util/color/predefinedcolor.cpp
deleted file mode 100644
index 2a5db56e9873..000000000000
--- 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 4e5745c0883b..000000000000
--- 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/predefinedcolorsrepresentation.h b/src/util/color/predefinedcolorsrepresentation.h
deleted file mode 100644
index 83f16405447d..000000000000
--- 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
deleted file mode 100644
index 8e758b9c7f48..000000000000
--- a/src/util/color/predefinedcolorsset.h
+++ /dev/null
@@ -1,140 +0,0 @@
-#pragma once
-
-#include
-#include
-
-#include "predefinedcolorsrepresentation.h"
-#include "util/color/predefinedcolor.h"
-#include "util/color/rgbcolor.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(0xC50A08),
- QLatin1String("Red"),
- QObject::tr("Red"),
- 1);
- const PredefinedColorPointer green = std::make_shared(
- QColor(0x32BE44),
- QLatin1String("Green"),
- QObject::tr("Green"),
- 2);
- const PredefinedColorPointer blue = std::make_shared(
- QColor(0x0044FF),
- QLatin1String("Blue"),
- QObject::tr("Blue"),
- 3);
- const PredefinedColorPointer yellow = std::make_shared(
- QColor(0xF8D200),
- QLatin1String("Yellow"),
- QObject::tr("Yellow"),
- 4);
- const PredefinedColorPointer cyan = std::make_shared(
- QColor(0x42D4F4),
- QLatin1String("Celeste"),
- QObject::tr("Celeste"),
- 5);
- const PredefinedColorPointer magenta = std::make_shared(
- QColor(0xAF00CC),
- QLatin1String("Purple"),
- QObject::tr("Purple"),
- 6);
- const PredefinedColorPointer pink = std::make_shared(
- QColor(0xFCA6D7),
- QLatin1String("Pink"),
- QObject::tr("Pink"),
- 7);
- const PredefinedColorPointer white = std::make_shared(
- QColor(0xF2F2FF),
- QLatin1String("White"),
- QObject::tr("White"),
- 8);
-
- // The list of the predefined colors.
- const QList allColors{
- noColor,
- red,
- green,
- yellow,
- blue,
- cyan,
- magenta,
- pink,
- 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) {
- 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;
- }
- }
- qWarning() << "No color matches name" << name;
- 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;
- }
- }
- qWarning() << "No color matches id" << iId;
- return noColor;
- };
-
- PredefinedColorPointer predefinedColorFromRgbColor(mixxx::RgbColor color) const {
- for (PredefinedColorPointer pColor : allColors) {
- if (mixxx::RgbColor(pColor->m_defaultRgba.rgb()) == color) {
- return pColor;
- }
- }
- qWarning() << "No color matches RgbColor" << color;
- return noColor;
- };
-
- PredefinedColorPointer predefinedColorFromRgbColor(mixxx::RgbColor::optional_t color) const {
- if (color) {
- return predefinedColorFromRgbColor(*color);
- }
- 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 1d338688e08c..61aa4f1b1f5c 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*/) {
@@ -128,7 +123,7 @@ void WaveformRenderMark::slotCuesUpdated() {
}
QString newLabel = pCue->getLabel();
- QColor newColor = m_predefinedColorsRepresentation.representationFor(pCue->getColor());
+ QColor newColor = mixxx::RgbColor::toQColor(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/waveform/renderers/waveformrendermark.h b/src/waveform/renderers/waveformrendermark.h
index 693332c81624..dc0fad17c90f 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/wcolorpicker.cpp b/src/widget/wcolorpicker.cpp
index c6c942786989..fb1547206503 100644
--- a/src/widget/wcolorpicker.cpp
+++ b/src/widget/wcolorpicker.cpp
@@ -1,6 +1,5 @@
#include "widget/wcolorpicker.h"
-#include
#include
#include
#include
@@ -9,11 +8,37 @@
#include "util/parented_ptr.h"
namespace {
- const int kNumColumns = 4;
+constexpr int kNumColumnsCandidates[] = {5, 4, 3};
}
-WColorPicker::WColorPicker(ColorOption colorOption, QWidget* parent)
- : QWidget(parent) {
+// Determine the best number of columns for items in a QGridView.
+//
+// Ideally, numItems % numColumn == 0 holds true. Rows that are almost
+// empty do not look good, so if the we can't find the ideal column count,
+// we fall back to a column count with the biggest number of elements in
+// the last row.
+inline int idealColumnCount(int numItems) {
+ int numColumns = 4; // Default in case kNumColumnsCandidates is empty
+ int numColumnsRemainder = -1;
+ for (const int numColumnsCandidate : kNumColumnsCandidates) {
+ const int remainder = numItems % numColumnsCandidate;
+ if (remainder == 0) {
+ numColumns = numColumnsCandidate;
+ break;
+ }
+ if (remainder > numColumnsRemainder) {
+ numColumnsRemainder = numColumnsCandidate;
+ numColumns = numColumnsCandidate;
+ }
+ }
+
+ return numColumns;
+}
+
+WColorPicker::WColorPicker(ColorOption colorOption, const ColorPalette& palette, QWidget* parent)
+ : QWidget(parent),
+ m_colorOption(colorOption),
+ m_palette(palette) {
QGridLayout* pLayout = new QGridLayout();
pLayout->setMargin(0);
pLayout->setContentsMargins(0, 0, 0, 0);
@@ -30,96 +55,155 @@ WColorPicker::WColorPicker(ColorOption colorOption, QWidget* parent)
// better than having buttons without any colors (which would make the
// color picker unusable).
m_pStyle = QStyleFactory::create(QString("fusion"));
+ setLayout(pLayout);
+ addColorButtons();
+}
+
+void WColorPicker::removeColorButtons() {
+ QGridLayout* pLayout = static_cast(layout());
+ VERIFY_OR_DEBUG_ASSERT(pLayout) {
+ qWarning() << "Color Picker has no layout!";
+ return;
+ }
+
+ while (!m_colorButtons.isEmpty()) {
+ QPushButton* pColorButton = m_colorButtons.takeLast();
+ pLayout->removeWidget(pColorButton);
+ delete pColorButton;
+ }
+}
+
+void WColorPicker::addColorButtons() {
+ QGridLayout* pLayout = static_cast(layout());
+ VERIFY_OR_DEBUG_ASSERT(pLayout) {
+ qWarning() << "Color Picker has no layout!";
+ return;
+ }
int row = 0;
int column = 0;
- for (const auto& pColor : Color::kPredefinedColorsSet.allColors) {
- if (colorOption != ColorOption::AllowNoColor &&
- pColor == Color::kPredefinedColorsSet.noColor) {
- continue;
- }
- parented_ptr pColorButton = make_parented("", this);
- if (m_pStyle) {
- pColorButton->setStyle(m_pStyle);
- }
-
- if (pColor->m_defaultRgba.isValid()) {
- // Set the background color of the button. This can't be overridden in skin stylesheets.
- pColorButton->setStyleSheet(
- QString("QPushButton { background-color: #%1; }").arg(pColor->m_defaultRgba.rgb(), 6, 16, QChar('0'))
- );
- } else {
- pColorButton->setProperty("noColor", true);
- }
+ int numColors = m_palette.size();
+ if (m_colorOption == ColorOption::AllowNoColor) {
+ numColors++;
+ }
- pColorButton->setToolTip(pColor->m_sDisplayName);
- pColorButton->setCheckable(true);
- m_pColorButtons.insert(pColor, pColorButton);
+ int numColumns = idealColumnCount(numColors);
+ if (m_colorOption == ColorOption::AllowNoColor) {
+ addColorButton(std::nullopt, pLayout, row, column);
+ column++;
+ }
- pLayout->addWidget(pColorButton, row, column);
+ for (const auto& color : m_palette) {
+ addColorButton(color, pLayout, row, column);
column++;
- if (column == kNumColumns) {
+ if (column == numColumns) {
column = 0;
row++;
}
-
- connect(this,
- &WColorPicker::colorPicked,
- this,
- &WColorPicker::slotColorPicked);
- connect(pColorButton,
- &QPushButton::clicked,
- this,
- [pColor, this]() {
- emit colorPicked(pColor);
- });
}
- setLayout(pLayout);
}
-void WColorPicker::setSelectedColor(PredefinedColorPointer pColor) {
- if (m_pSelectedColor) {
- auto it = m_pColorButtons.constFind(m_pSelectedColor);
- if (it != m_pColorButtons.constEnd()) {
- it.value()->setChecked(false);
- // This is needed to re-apply skin styles (e.g. to show/hide a checkmark icon)
- it.value()->style()->unpolish(it.value());
- it.value()->style()->polish(it.value());
- }
+void WColorPicker::addColorButton(mixxx::RgbColor::optional_t color, QGridLayout* pLayout, int row, int column) {
+ parented_ptr pColorButton = make_parented("", this);
+ if (m_pStyle) {
+ pColorButton->setStyle(m_pStyle);
}
- if (pColor) {
- auto it = m_pColorButtons.constFind(pColor);
- if (it != m_pColorButtons.constEnd()) {
- it.value()->setChecked(true);
- // This is needed to re-apply skin styles (e.g. to show/hide a checkmark icon)
- it.value()->style()->unpolish(it.value());
- it.value()->style()->polish(it.value());
+ if (color) {
+ // Set the background color of the button. This can't be overridden in skin stylesheets.
+ pColorButton->setStyleSheet(
+ QString("QPushButton { background-color: %1; }").arg(mixxx::RgbColor::toQString(color)));
+ } else {
+ pColorButton->setProperty("noColor", true);
+ }
+ pColorButton->setToolTip(mixxx::RgbColor::toQString(color, tr("No Color")));
+
+ pColorButton->setCheckable(true);
+ m_colorButtons.append(pColorButton);
+
+ pLayout->addWidget(pColorButton, row, column);
+
+ connect(this,
+ &WColorPicker::colorPicked,
+ this,
+ &WColorPicker::slotColorPicked);
+ connect(pColorButton,
+ &QPushButton::clicked,
+ this,
+ [this, color]() {
+ emit colorPicked(color);
+ });
+}
+
+void WColorPicker::resetSelectedColor() {
+ // Unset currently selected color
+ int i = 0;
+ if (m_selectedColor) {
+ i = m_palette.indexOf(*m_selectedColor);
+ if (i == -1) {
+ return;
+ }
+ if (m_colorOption == ColorOption::AllowNoColor) {
+ i++;
}
+ } else if (m_colorOption != ColorOption::AllowNoColor) {
+ return;
}
- m_pSelectedColor = pColor;
+ DEBUG_ASSERT(i < m_colorButtons.size());
+
+ QPushButton* pButton = m_colorButtons.at(i);
+ VERIFY_OR_DEBUG_ASSERT(pButton != nullptr) {
+ return;
+ }
+ pButton->setChecked(false);
+ // This is needed to re-apply skin styles (e.g. to show/hide a checkmark icon)
+ pButton->style()->unpolish(pButton);
+ pButton->style()->polish(pButton);
}
-void WColorPicker::useColorSet(PredefinedColorsRepresentation* pColorRepresentation) {
- QMapIterator i(m_pColorButtons);
- while (i.hasNext()) {
- i.next();
- PredefinedColorPointer pColor = i.key();
- QPushButton* pColorButton = i.value();
- QColor color = (pColorRepresentation == nullptr) ? pColor->m_defaultRgba : pColorRepresentation->representationFor(pColor);
+void WColorPicker::setSelectedColor(mixxx::RgbColor::optional_t color) {
+ resetSelectedColor();
- // Set the background color of the button. This can't be overridden in skin stylesheets.
- pColorButton->setStyleSheet(
- QString("QPushButton { background-color: #%1; }").arg(color.rgb(), 6, 16, QChar('0'))
- );
+ m_selectedColor = color;
+
+ int i = 0;
+ if (color) {
+ i = m_palette.indexOf(*color);
+ if (i == -1) {
+ return;
+ }
+ if (m_colorOption == ColorOption::AllowNoColor) {
+ i++;
+ }
+ } else if (m_colorOption != ColorOption::AllowNoColor) {
+ return;
+ }
+
+ DEBUG_ASSERT(i < m_colorButtons.size());
- pColorButton->setToolTip(pColor->m_sDisplayName);
+ QPushButton* pButton = m_colorButtons.at(i);
+ VERIFY_OR_DEBUG_ASSERT(pButton != nullptr) {
+ return;
}
+ pButton->setChecked(true);
+ // This is needed to re-apply skin styles (e.g. to show/hide a checkmark icon)
+ pButton->style()->unpolish(pButton);
+ pButton->style()->polish(pButton);
}
+void WColorPicker::setColorPalette(const ColorPalette& palette) {
+ if (m_palette == palette) {
+ return;
+ }
+
+ resetSelectedColor();
+ removeColorButtons();
+ m_palette = palette;
+ addColorButtons();
+}
-void WColorPicker::slotColorPicked(PredefinedColorPointer pColor) {
- setSelectedColor(pColor);
+void WColorPicker::slotColorPicked(mixxx::RgbColor::optional_t color) {
+ setSelectedColor(color);
}
diff --git a/src/widget/wcolorpicker.h b/src/widget/wcolorpicker.h
index e3e10e570962..d905e6124623 100644
--- a/src/widget/wcolorpicker.h
+++ b/src/widget/wcolorpicker.h
@@ -1,11 +1,13 @@
#pragma once
+#include
#include
#include
#include
#include
#include "util/color/color.h"
+#include "util/color/colorpalette.h"
class WColorPicker : public QWidget {
Q_OBJECT
@@ -15,19 +17,25 @@ class WColorPicker : public QWidget {
AllowNoColor,
};
- explicit WColorPicker(ColorOption colorOption, QWidget* parent = nullptr);
+ explicit WColorPicker(ColorOption colorOption, const ColorPalette& palette, QWidget* parent = nullptr);
- void setSelectedColor(PredefinedColorPointer pColor = nullptr);
- void useColorSet(PredefinedColorsRepresentation* pColorRepresentation);
+ void resetSelectedColor();
+ void setSelectedColor(mixxx::RgbColor::optional_t color);
+ void setColorPalette(const ColorPalette& palette);
signals:
- void colorPicked(PredefinedColorPointer pColor);
+ void colorPicked(mixxx::RgbColor::optional_t color);
private slots:
- void slotColorPicked(PredefinedColorPointer pColor);
+ void slotColorPicked(mixxx::RgbColor::optional_t color);
private:
- QMap m_pColorButtons;
- PredefinedColorPointer m_pSelectedColor;
+ void addColorButtons();
+ void removeColorButtons();
+ void addColorButton(mixxx::RgbColor::optional_t color, QGridLayout* pLayout, int row, int column);
+ ColorOption m_colorOption;
+ mixxx::RgbColor::optional_t m_selectedColor;
+ ColorPalette m_palette;
+ QList m_colorButtons;
QStyle* m_pStyle;
};
diff --git a/src/widget/wcolorpickeraction.cpp b/src/widget/wcolorpickeraction.cpp
index db72bc97e951..8837695ac616 100644
--- a/src/widget/wcolorpickeraction.cpp
+++ b/src/widget/wcolorpickeraction.cpp
@@ -1,8 +1,8 @@
#include "widget/wcolorpickeraction.h"
-WColorPickerAction::WColorPickerAction(WColorPicker::ColorOption colorOption, QWidget* parent)
+WColorPickerAction::WColorPickerAction(WColorPicker::ColorOption colorOption, const ColorPalette& palette, QWidget* parent)
: QWidgetAction(parent),
- m_pColorPicker(make_parented(colorOption)) {
+ m_pColorPicker(make_parented(colorOption, palette)) {
connect(m_pColorPicker.get(), &WColorPicker::colorPicked, this, &WColorPickerAction::colorPicked);
QHBoxLayout* pLayout = new QHBoxLayout();
@@ -13,6 +13,14 @@ WColorPickerAction::WColorPickerAction(WColorPicker::ColorOption colorOption, QW
setDefaultWidget(pWidget);
}
-void WColorPickerAction::setSelectedColor(PredefinedColorPointer pColor) {
- m_pColorPicker->setSelectedColor(pColor);
+void WColorPickerAction::resetSelectedColor() {
+ m_pColorPicker->resetSelectedColor();
+}
+
+void WColorPickerAction::setSelectedColor(mixxx::RgbColor::optional_t color) {
+ m_pColorPicker->setSelectedColor(color);
+}
+
+void WColorPickerAction::setColorPalette(const ColorPalette& palette) {
+ m_pColorPicker->setColorPalette(palette);
}
diff --git a/src/widget/wcolorpickeraction.h b/src/widget/wcolorpickeraction.h
index 48e1012772b2..ce222ff49cc8 100644
--- a/src/widget/wcolorpickeraction.h
+++ b/src/widget/wcolorpickeraction.h
@@ -12,12 +12,15 @@ class WColorPickerAction : public QWidgetAction {
public:
explicit WColorPickerAction(
WColorPicker::ColorOption colorOption,
+ const ColorPalette& palette,
QWidget* parent = nullptr);
- void setSelectedColor(PredefinedColorPointer pColor = nullptr);
+ void resetSelectedColor();
+ void setSelectedColor(mixxx::RgbColor::optional_t color);
+ void setColorPalette(const ColorPalette& palette);
signals:
- void colorPicked(PredefinedColorPointer pColor);
+ void colorPicked(mixxx::RgbColor::optional_t color);
private:
parented_ptr m_pColorPicker;
diff --git a/src/widget/wcuemenupopup.cpp b/src/widget/wcuemenupopup.cpp
index 6cefca6aef22..3c2817c4ab3a 100644
--- a/src/widget/wcuemenupopup.cpp
+++ b/src/widget/wcuemenupopup.cpp
@@ -7,8 +7,9 @@
#include "engine/engine.h"
#include "util/color/color.h"
-WCueMenuPopup::WCueMenuPopup(QWidget* parent)
- : QWidget(parent) {
+WCueMenuPopup::WCueMenuPopup(UserSettingsPointer pConfig, QWidget* parent)
+ : QWidget(parent),
+ m_colorPaletteSettings(ColorPaletteSettings(pConfig)) {
QWidget::hide();
setWindowFlags(Qt::Popup);
setAttribute(Qt::WA_StyledBackground);
@@ -30,7 +31,7 @@ WCueMenuPopup::WCueMenuPopup(QWidget* parent)
connect(m_pEditLabel, &QLineEdit::textEdited, this, &WCueMenuPopup::slotEditLabel);
connect(m_pEditLabel, &QLineEdit::returnPressed, this, &WCueMenuPopup::hide);
- m_pColorPicker = new WColorPicker(WColorPicker::ColorOption::DenyNoColor, this);
+ m_pColorPicker = new WColorPicker(WColorPicker::ColorOption::DenyNoColor, m_colorPaletteSettings.getHotcueColorPalette(), this);
m_pColorPicker->setObjectName("CueColorPicker");
connect(m_pColorPicker, &WColorPicker::colorPicked, this, &WCueMenuPopup::slotChangeCueColor);
@@ -97,7 +98,7 @@ void WCueMenuPopup::setTrackAndCue(TrackPointer pTrack, CuePointer pCue) {
m_pCueNumber->setText(QString(""));
m_pCuePosition->setText(QString(""));
m_pEditLabel->setText(QString(""));
- m_pColorPicker->setSelectedColor();
+ m_pColorPicker->setSelectedColor(std::nullopt);
}
}
@@ -108,14 +109,15 @@ void WCueMenuPopup::slotEditLabel() {
m_pCue->setLabel(m_pEditLabel->text());
}
-void WCueMenuPopup::slotChangeCueColor(PredefinedColorPointer pColor) {
+void WCueMenuPopup::slotChangeCueColor(mixxx::RgbColor::optional_t color) {
VERIFY_OR_DEBUG_ASSERT(m_pCue != nullptr) {
return;
}
- VERIFY_OR_DEBUG_ASSERT(pColor != nullptr) {
+ VERIFY_OR_DEBUG_ASSERT(color) {
return;
}
- m_pCue->setColor(pColor);
+ m_pCue->setColor(*color);
+ m_pColorPicker->setSelectedColor(color);
hide();
}
diff --git a/src/widget/wcuemenupopup.h b/src/widget/wcuemenupopup.h
index cac6a9825c15..7e1743590d0d 100644
--- a/src/widget/wcuemenupopup.h
+++ b/src/widget/wcuemenupopup.h
@@ -4,6 +4,7 @@
#include
#include
+#include "preferences/colorpalettesettings.h"
#include "track/cue.h"
#include "track/track.h"
#include "widget/wcolorpicker.h"
@@ -11,7 +12,7 @@
class WCueMenuPopup : public QWidget {
Q_OBJECT
public:
- WCueMenuPopup(QWidget* parent = nullptr);
+ WCueMenuPopup(UserSettingsPointer pConfig, QWidget* parent = nullptr);
~WCueMenuPopup() {
delete m_pCueNumber;
@@ -23,9 +24,9 @@ class WCueMenuPopup : public QWidget {
void setTrackAndCue(TrackPointer pTrack, CuePointer pCue);
- void useColorSet(PredefinedColorsRepresentation* pColorRepresentation) {
+ void setColorPalette(const ColorPalette& palette) {
if (m_pColorPicker != nullptr) {
- m_pColorPicker->useColorSet(pColorRepresentation);
+ m_pColorPicker->setColorPalette(palette);
}
}
@@ -42,6 +43,7 @@ class WCueMenuPopup : public QWidget {
}
void show() {
+ setColorPalette(m_colorPaletteSettings.getHotcueColorPalette());
m_pEditLabel->setFocus();
emit aboutToShow();
QWidget::show();
@@ -54,9 +56,10 @@ class WCueMenuPopup : public QWidget {
private slots:
void slotEditLabel();
void slotDeleteCue();
- void slotChangeCueColor(PredefinedColorPointer pColor);
+ void slotChangeCueColor(mixxx::RgbColor::optional_t color);
private:
+ ColorPaletteSettings m_colorPaletteSettings;
CuePointer m_pCue;
TrackPointer m_pTrack;
diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp
index b2a5379cfd04..4fb65c6fbfec 100644
--- a/src/widget/woverview.cpp
+++ b/src/widget/woverview.cpp
@@ -10,21 +10,23 @@
//
//
+#include "woverview.h"
+
#include
-#include
+#include
#include
#include
#include
-#include
#include
#include
-#include
+#include
#include "analyzer/analyzerprogress.h"
#include "control/controlobject.h"
#include "control/controlproxy.h"
#include "engine/engine.h"
#include "mixer/playermanager.h"
+#include "preferences/colorpalettesettings.h"
#include "track/track.h"
#include "util/color/color.h"
#include "util/compatibility.h"
@@ -33,12 +35,10 @@
#include "util/math.h"
#include "util/painterscope.h"
#include "util/timer.h"
-#include "widget/controlwidgetconnection.h"
-#include "woverview.h"
-#include "wskincolor.h"
-
#include "waveform/waveform.h"
#include "waveform/waveformwidgetfactory.h"
+#include "widget/controlwidgetconnection.h"
+#include "wskincolor.h"
WOverview::WOverview(
const char* group,
@@ -54,7 +54,7 @@ WOverview::WOverview(
m_group(group),
m_pConfig(pConfig),
m_endOfTrack(false),
- m_pCueMenuPopup(std::make_unique(this)),
+ m_pCueMenuPopup(std::make_unique(pConfig, this)),
m_bShowCueTimes(true),
m_iPosSeconds(0),
m_bLeftClickDragging(false),
@@ -131,12 +131,10 @@ 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);
- m_pCueMenuPopup->useColorSet(&m_predefinedColorsRepresentation);
+
+ ColorPaletteSettings colorPaletteSettings(m_pConfig);
+ auto colorPalette = colorPaletteSettings.getHotcueColorPalette();
+ m_pCueMenuPopup->setColorPalette(colorPalette);
for (const auto& pMark: m_marks) {
if (pMark->isValid()) {
@@ -349,8 +347,9 @@ void WOverview::updateCues(const QList &loadedCues) {
for (CuePointer currentCue: loadedCues) {
const WaveformMarkPointer pMark = m_marks.getHotCueMark(currentCue->getHotCue());
- if (pMark != nullptr && pMark->isValid() && pMark->isVisible() && pMark->getSamplePosition() != Cue::kNoPosition) {
- QColor newColor = m_predefinedColorsRepresentation.representationFor(currentCue->getColor());
+ if (pMark != nullptr && pMark->isValid() && pMark->isVisible()
+ && pMark->getSamplePosition() != Cue::kNoPosition) {
+ QColor newColor = mixxx::RgbColor::toQColor(currentCue->getColor());
if (newColor != pMark->fillColor() || newColor != pMark->m_textColor) {
pMark->setBaseColor(newColor);
}
diff --git a/src/widget/woverview.h b/src/widget/woverview.h
index 4837b89f002b..25ce680bd181 100644
--- a/src/widget/woverview.h
+++ b/src/widget/woverview.h
@@ -169,7 +169,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
diff --git a/src/widget/wtracktableview.cpp b/src/widget/wtracktableview.cpp
index 35b426e76284..3fc47d469c2a 100644
--- a/src/widget/wtracktableview.cpp
+++ b/src/widget/wtracktableview.cpp
@@ -1,45 +1,45 @@
-#include
-#include
+#include "widget/wtracktableview.h"
+
+#include
#include
-#include
#include
-#include
-#include
-#include
+#include
#include
+#include
#include
+#include
+#include
+#include
-#include "widget/wtracktableview.h"
-
-#include "widget/wcolorpickeraction.h"
-#include "widget/wcoverartmenu.h"
-#include "widget/wskincolor.h"
-#include "widget/wtracktableviewheader.h"
-#include "widget/wwidget.h"
+#include "control/controlobject.h"
+#include "control/controlproxy.h"
#include "library/coverartutils.h"
-#include "library/dlgtagfetcher.h"
-#include "library/dlgtrackinfo.h"
-#include "library/librarytablemodel.h"
#include "library/crate/cratefeaturehelper.h"
#include "library/dao/trackschema.h"
+#include "library/dlgtagfetcher.h"
+#include "library/dlgtrackinfo.h"
#include "library/dlgtrackmetadataexport.h"
#include "library/externaltrackcollection.h"
+#include "library/librarytablemodel.h"
#include "library/trackcollection.h"
#include "library/trackcollectionmanager.h"
-#include "control/controlobject.h"
-#include "control/controlproxy.h"
-#include "track/track.h"
-#include "track/trackref.h"
-#include "sources/soundsourceproxy.h"
#include "mixer/playermanager.h"
+#include "preferences/colorpalettesettings.h"
#include "preferences/dialog/dlgpreflibrary.h"
-#include "waveform/guitick.h"
-#include "util/dnd.h"
-#include "util/time.h"
+#include "sources/soundsourceproxy.h"
+#include "track/track.h"
+#include "track/trackref.h"
#include "util/assert.h"
-#include "util/parented_ptr.h"
#include "util/desktophelper.h"
-#include "util/color/predefinedcolorsset.h"
+#include "util/dnd.h"
+#include "util/parented_ptr.h"
+#include "util/time.h"
+#include "waveform/guitick.h"
+#include "widget/wcolorpickeraction.h"
+#include "widget/wcoverartmenu.h"
+#include "widget/wskincolor.h"
+#include "widget/wtracktableviewheader.h"
+#include "widget/wwidget.h"
WTrackTableView::WTrackTableView(QWidget * parent,
UserSettingsPointer pConfig,
@@ -574,7 +574,8 @@ void WTrackTableView::createActions() {
connect(m_pBpmThreeHalvesAction, &QAction::triggered,
this, [this] { slotScaleBpm(Beats::THREEHALVES); });
- m_pColorPickerAction = new WColorPickerAction(WColorPicker::ColorOption::AllowNoColor, this);
+ ColorPaletteSettings colorPaletteSettings(m_pConfig);
+ m_pColorPickerAction = new WColorPickerAction(WColorPicker::ColorOption::AllowNoColor, colorPaletteSettings.getTrackColorPalette(), this);
m_pColorPickerAction->setObjectName("TrackColorPickerAction");
connect(m_pColorPickerAction,
&WColorPickerAction::colorPicked,
@@ -751,7 +752,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()));
@@ -1088,6 +1089,9 @@ void WTrackTableView::contextMenuEvent(QContextMenuEvent* event) {
// Track color menu only appears if at least one track is selected
if (indices.size()) {
+ m_pColorPickerAction->setColorPalette(
+ ColorPaletteSettings(m_pConfig).getTrackColorPalette());
+
// Get color of first selected track
int column = trackModel->fieldIndex(LIBRARYTABLE_COLOR);
QModelIndex index = indices.at(0).sibling(indices.at(0).row(), column);
@@ -1106,19 +1110,11 @@ void WTrackTableView::contextMenuEvent(QContextMenuEvent* event) {
}
}
- // Get the predefined color of the selected tracks. If they have
- // different colors, do not preselect a color (by using nullptr
- // instead).
- PredefinedColorPointer predefinedTrackColor = nullptr;
- if (trackColor) {
- // All tracks have the same color
- predefinedTrackColor = Color::kPredefinedColorsSet.predefinedColorFromRgbColor(trackColor);
- } else if (!multipleTrackColors) {
- // All tracks have no color
- predefinedTrackColor = Color::kPredefinedColorsSet.noColor;
+ if (multipleTrackColors) {
+ m_pColorPickerAction->resetSelectedColor();
+ } else {
+ m_pColorPickerAction->setSelectedColor(trackColor);
}
-
- m_pColorPickerAction->setSelectedColor(predefinedTrackColor);
m_pColorMenu->addAction(m_pColorPickerAction);
m_pMenu->addMenu(m_pColorMenu);
}
@@ -1977,7 +1973,7 @@ void WTrackTableView::lockBpm(bool lock) {
}
}
-void WTrackTableView::slotColorPicked(PredefinedColorPointer pColor) {
+void WTrackTableView::slotColorPicked(mixxx::RgbColor::optional_t color) {
TrackModel* trackModel = getTrackModel();
if (trackModel == nullptr) {
return;
@@ -1988,7 +1984,7 @@ void WTrackTableView::slotColorPicked(PredefinedColorPointer pColor) {
for (const auto& index : selectedTrackIndices) {
TrackPointer pTrack = trackModel->getTrack(index);
if (pTrack) {
- pTrack->setColor(mixxx::RgbColor::fromQColor(pColor->m_defaultRgba));
+ pTrack->setColor(color);
}
}
diff --git a/src/widget/wtracktableview.h b/src/widget/wtracktableview.h
index 2c1b90d7c651..fd6beae9d6ae 100644
--- a/src/widget/wtracktableview.h
+++ b/src/widget/wtracktableview.h
@@ -81,7 +81,7 @@ class WTrackTableView : public WLibraryTableView {
void slotLockBpm();
void slotUnlockBpm();
void slotScaleBpm(int);
- void slotColorPicked(PredefinedColorPointer pColor);
+ void slotColorPicked(mixxx::RgbColor::optional_t color);
void slotClearBeats();
void slotClearPlayCount();
diff --git a/src/widget/wwidget.cpp b/src/widget/wwidget.cpp
index 364b46280a13..801678a107a3 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);
@@ -37,6 +38,31 @@ WWidget::~WWidget() {
delete m_pTouchShift;
}
+double WWidget::getBackgroundColorRgba() const {
+ if (m_backgroundColorRgba < 0) {
+ return -1;
+ }
+ return m_backgroundColorRgba;
+}
+
+void WWidget::setBackgroundColorRgba(double rgba) {
+ QColor backgroundColor = QColor::fromRgba(rgba);
+ QColor highlightedBackgroundColor = backgroundColor.lighter();
+ QString style = QString("WWidget { background-color: %1; }");
+ if (m_bShouldHighlightBackgroundOnHover) {
+ style += "WWidget:hover { background-color: %2; }";
+ }
+
+ if (rgba >= 0) {
+ setStyleSheet(style.arg(backgroundColor.name())
+ .arg(highlightedBackgroundColor.name()));
+ } else {
+ setStyleSheet("");
+ }
+ m_backgroundColorRgba = rgba;
+ m_bBackgroundIsDark = Color::isDimmColor(backgroundColor);
+}
+
bool WWidget::touchIsRightButton() {
return (m_pTouchShift->get() != 0.0);
}
diff --git a/src/widget/wwidget.h b/src/widget/wwidget.h
index cbadf8ab2fe6..0fbf10631c25 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,21 @@ class WWidget : public QWidget, public WBaseWidget {
~WWidget() override;
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);
+
+ double getBackgroundColorRgba() const;
+ void setBackgroundColorRgba(double rgba);
+ bool hasBackgroundColor() const {
+ return m_backgroundColorRgba >= 0;
+ }
+ bool backgroundIsDark() const {
+ return m_bBackgroundIsDark;
+ }
protected:
bool touchIsRightButton();
@@ -60,6 +76,9 @@ class WWidget : public QWidget, public WBaseWidget {
private:
ControlProxy* m_pTouchShift;
double m_scaleFactor;
+ double m_backgroundColorRgba;
+ bool m_bBackgroundIsDark;
+ bool m_bShouldHighlightBackgroundOnHover;
};
#endif