diff --git a/CMakeLists.txt b/CMakeLists.txt
index a3f849873b1..0ecc247eb3d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -217,6 +217,7 @@ add_library(mixxx-lib STATIC EXCLUDE_FROM_ALL
src/controllers/keyboard/keyboardeventfilter.cpp
src/controllers/learningutils.cpp
src/controllers/midi/midicontroller.cpp
+ src/controllers/midi/midicontrollerpreset.cpp
src/controllers/midi/midicontrollerpresetfilehandler.cpp
src/controllers/midi/midienumerator.cpp
src/controllers/midi/midimessage.cpp
@@ -1979,6 +1980,7 @@ if(HID)
target_sources(mixxx-lib PRIVATE
src/controllers/hid/hidcontroller.cpp
src/controllers/hid/hidenumerator.cpp
+ src/controllers/hid/hidcontrollerpreset.cpp
src/controllers/hid/hidcontrollerpresetfilehandler.cpp
)
target_compile_definitions(mixxx-lib PUBLIC __HID__)
diff --git a/build/depends.py b/build/depends.py
index b68c7f85cdf..f8794c9a680 100644
--- a/build/depends.py
+++ b/build/depends.py
@@ -924,6 +924,7 @@ def sources(self, build):
"src/controllers/midi/midimessage.cpp",
"src/controllers/midi/midiutils.cpp",
"src/controllers/midi/midicontroller.cpp",
+ "src/controllers/midi/midicontrollerpreset.cpp",
"src/controllers/midi/midicontrollerpresetfilehandler.cpp",
"src/controllers/midi/midienumerator.cpp",
"src/controllers/midi/midioutputhandler.cpp",
diff --git a/build/features.py b/build/features.py
index b701b9c4fcf..0209740979e 100644
--- a/build/features.py
+++ b/build/features.py
@@ -100,6 +100,7 @@ def configure(self, build, conf):
def sources(self, build):
sources = ['src/controllers/hid/hidcontroller.cpp',
+ 'src/controllers/hid/hidcontrollerpreset.cpp',
'src/controllers/hid/hidenumerator.cpp',
'src/controllers/hid/hidcontrollerpresetfilehandler.cpp']
diff --git a/res/images/ic_custom.svg b/res/images/ic_custom.svg
new file mode 100644
index 00000000000..44a0fe3f680
--- /dev/null
+++ b/res/images/ic_custom.svg
@@ -0,0 +1,67 @@
+
+
+
+
diff --git a/res/images/ic_mixxx_symbolic.svg b/res/images/ic_mixxx_symbolic.svg
new file mode 100644
index 00000000000..c51004a4556
--- /dev/null
+++ b/res/images/ic_mixxx_symbolic.svg
@@ -0,0 +1,1308 @@
+
+
+
+
diff --git a/res/images/ic_none.svg b/res/images/ic_none.svg
new file mode 100644
index 00000000000..1d4ae648d3a
--- /dev/null
+++ b/res/images/ic_none.svg
@@ -0,0 +1,77 @@
+
+
+
+
diff --git a/res/mixxx.qrc b/res/mixxx.qrc
index c863791eee5..02f2f053c66 100644
--- a/res/mixxx.qrc
+++ b/res/mixxx.qrc
@@ -33,7 +33,10 @@
images/mixxx-icon-logo-symbolic.svg
images/skin_preview_placeholder.png
images/ic_checkmark.svg
+ images/ic_custom.svg
images/ic_delete.svg
+ images/ic_mixxx_symbolic.svg
+ images/ic_none.svg
images/preferences/ic_preferences_autodj.svg
images/preferences/ic_preferences_bpmdetect.svg
images/preferences/ic_preferences_broadcast.svg
diff --git a/src/controllers/bulk/bulkcontroller.cpp b/src/controllers/bulk/bulkcontroller.cpp
index 49468b1ceab..4280cc75bee 100644
--- a/src/controllers/bulk/bulkcontroller.cpp
+++ b/src/controllers/bulk/bulkcontroller.cpp
@@ -116,11 +116,6 @@ void BulkController::visit(const HidControllerPreset* preset) {
emit presetLoaded(getPreset());
}
-bool BulkController::savePreset(const QString fileName) const {
- HidControllerPresetFileHandler handler;
- return handler.save(m_preset, getName(), fileName);
-}
-
bool BulkController::matchPreset(const PresetInfo& preset) {
const QList& products = preset.getProducts();
for (const auto& product : products) {
diff --git a/src/controllers/bulk/bulkcontroller.h b/src/controllers/bulk/bulkcontroller.h
index 9d67c1041ea..94e32869ba1 100644
--- a/src/controllers/bulk/bulkcontroller.h
+++ b/src/controllers/bulk/bulkcontroller.h
@@ -56,8 +56,6 @@ class BulkController : public Controller {
return ControllerPresetPointer(pClone);
}
- bool savePreset(const QString fileName) const override;
-
void visit(const MidiControllerPreset* preset) override;
void visit(const HidControllerPreset* preset) override;
diff --git a/src/controllers/controller.cpp b/src/controllers/controller.cpp
index 15a1e1dd5ec..fb16eea5a70 100644
--- a/src/controllers/controller.cpp
+++ b/src/controllers/controller.cpp
@@ -50,7 +50,7 @@ void Controller::stopEngine() {
m_pEngine = NULL;
}
-bool Controller::applyPreset(QList scriptPaths, bool initializeScripts) {
+bool Controller::applyPreset(bool initializeScripts) {
qDebug() << "Applying controller preset...";
const ControllerPreset* pPreset = preset();
@@ -61,14 +61,15 @@ bool Controller::applyPreset(QList scriptPaths, bool initializeScripts)
return false;
}
- if (pPreset->scripts.isEmpty()) {
+ QList scriptFiles = pPreset->getScriptFiles();
+ if (scriptFiles.isEmpty()) {
qWarning() << "No script functions available! Did the XML file(s) load successfully? See above for any errors.";
return true;
}
- bool success = m_pEngine->loadScriptFiles(scriptPaths, pPreset->scripts);
+ bool success = m_pEngine->loadScriptFiles(scriptFiles);
if (initializeScripts) {
- m_pEngine->initializeScripts(pPreset->scripts);
+ m_pEngine->initializeScripts(scriptFiles);
}
return success;
}
diff --git a/src/controllers/controller.h b/src/controllers/controller.h
index 09a20d8181b..06fed55a482 100644
--- a/src/controllers/controller.h
+++ b/src/controllers/controller.h
@@ -26,9 +26,9 @@ class Controller : public QObject, ConstControllerPresetVisitor {
explicit Controller(UserSettingsPointer pConfig);
~Controller() override; // Subclass should call close() at minimum.
- // Returns the extension for the controller (type) preset files. This is
- // used by the ControllerManager to display only relevant preset files for
- // the controller (type.)
+ /// Returns the extension for the controller (type) preset files. This is
+ /// used by the ControllerManager to display only relevant preset files for
+ /// the controller (type.)
virtual QString presetExtension() = 0;
void setPreset(const ControllerPreset& preset) {
@@ -39,8 +39,6 @@ class Controller : public QObject, ConstControllerPresetVisitor {
virtual void accept(ControllerVisitor* visitor) = 0;
- virtual bool savePreset(const QString filename) const = 0;
-
// Returns a clone of the Controller's loaded preset.
virtual ControllerPresetPointer getPreset() const = 0;
@@ -71,8 +69,11 @@ class Controller : public QObject, ConstControllerPresetVisitor {
// preset, not a pointer to the preset itself.
void presetLoaded(ControllerPresetPointer pPreset);
- // Making these slots protected/private ensures that other parts of Mixxx can
- // only signal them which allows us to use no locks.
+ /// Emitted when the controller is opened or closed.
+ void openChanged(bool bOpen);
+
+ // Making these slots protected/private ensures that other parts of Mixxx can
+ // only signal them which allows us to use no locks.
protected slots:
// TODO(XXX) move this into the inherited classes since is not called here
// (via Controller) and re-implemented anyway in most cases.
@@ -82,8 +83,13 @@ class Controller : public QObject, ConstControllerPresetVisitor {
// this if they have an alternate way of handling such data.)
virtual void receive(const QByteArray data, mixxx::Duration timestamp);
- // Initializes the controller engine and returns whether it was successful.
- virtual bool applyPreset(QList scriptPaths, bool initializeScripts);
+ /// Apply the preset to the controller.
+ /// @brief Initializes both controller engine and static output mappings.
+ ///
+ /// @param initializeScripts Can be set to false to skip script
+ /// initialization for unit tests.
+ /// @return Returns whether it was successful.
+ virtual bool applyPreset(bool initializeScripts = true);
// Puts the controller in and out of learning mode.
void startLearning();
@@ -122,6 +128,7 @@ class Controller : public QObject, ConstControllerPresetVisitor {
}
inline void setOpen(bool open) {
m_bIsOpen = open;
+ emit openChanged(m_bIsOpen);
}
private: // but used by ControllerManager
diff --git a/src/controllers/controllerengine.cpp b/src/controllers/controllerengine.cpp
index 1488353b5d4..7739b742d4a 100644
--- a/src/controllers/controllerengine.cpp
+++ b/src/controllers/controllerengine.cpp
@@ -227,14 +227,10 @@ void ControllerEngine::initializeScriptEngine() {
Input: List of script paths and file names to load
Output: Returns true if no errors occurred.
-------- ------------------------------------------------------ */
-bool ControllerEngine::loadScriptFiles(const QList& scriptPaths,
- const QList& scripts) {
- m_lastScriptPaths = scriptPaths;
-
- // scriptPaths holds the paths to search in when we're looking for scripts
+bool ControllerEngine::loadScriptFiles(const QList& scripts) {
bool result = true;
- for (const ControllerPreset::ScriptFileInfo& script : scripts) {
- if (!evaluate(script.name, scriptPaths)) {
+ for (const auto& script : scripts) {
+ if (!evaluate(script.file)) {
result = false;
}
@@ -243,6 +239,8 @@ bool ControllerEngine::loadScriptFiles(const QList& scriptPaths,
}
}
+ m_lastScriptFiles = scripts;
+
connect(&m_scriptWatcher, SIGNAL(fileChanged(QString)),
this, SLOT(scriptHasChanged(QString)));
@@ -271,10 +269,10 @@ void ControllerEngine::scriptHasChanged(const QString& scriptFilename) {
}
initializeScriptEngine();
- loadScriptFiles(m_lastScriptPaths, pPreset->scripts);
+ loadScriptFiles(m_lastScriptFiles);
qDebug() << "Re-initializing scripts";
- initializeScripts(pPreset->scripts);
+ initializeScripts(m_lastScriptFiles);
}
/* -------- ------------------------------------------------------
@@ -310,10 +308,7 @@ void ControllerEngine::initializeScripts(const QList dummy;
- bool ret = evaluate(filepath, dummy);
-
- return ret;
+ return evaluate(QFileInfo(filepath));
}
bool ControllerEngine::syntaxIsValid(const QString& scriptCode) {
@@ -933,35 +928,22 @@ void ControllerEngine::trigger(QString group, QString name) {
Input: Script filename
Output: false if the script file has errors or doesn't exist
-------- ------------------------------------------------------ */
-bool ControllerEngine::evaluate(const QString& scriptName, QList scriptPaths) {
+bool ControllerEngine::evaluate(const QFileInfo& scriptFile) {
if (m_pEngine == nullptr) {
return false;
}
- QString filename = "";
- QFile input;
-
- if (scriptPaths.length() == 0) {
- // If we aren't given any paths to search, assume that scriptName
- // contains the full file name
- filename = scriptName;
- input.setFileName(filename);
- } else {
- for (const QString& scriptPath : scriptPaths) {
- QDir scriptPathDir(scriptPath);
- filename = scriptPathDir.absoluteFilePath(scriptName);
- input.setFileName(filename);
- if (input.exists()) {
- qDebug() << "ControllerEngine: Watching JS File:" << filename;
- m_scriptWatcher.addPath(filename);
- break;
- }
- }
+ if (!scriptFile.exists()) {
+ qWarning() << "ControllerEngine: File does not exist:" << scriptFile.absoluteFilePath();
+ return false;
}
+ m_scriptWatcher.addPath(scriptFile.absoluteFilePath());
- qDebug() << "ControllerEngine: Loading" << filename;
+ qDebug() << "ControllerEngine: Loading" << scriptFile.absoluteFilePath();
// Read in the script file
+ QString filename = scriptFile.absoluteFilePath();
+ QFile input(filename);
if (!input.open(QIODevice::ReadOnly)) {
qWarning() << QString("ControllerEngine: Problem opening the script file: %1, error # %2, %3")
.arg(filename, QString::number(input.error()), input.errorString());
diff --git a/src/controllers/controllerengine.h b/src/controllers/controllerengine.h
index 87a75abd730..ecc1c4a4761 100644
--- a/src/controllers/controllerengine.h
+++ b/src/controllers/controllerengine.h
@@ -158,8 +158,7 @@ class ControllerEngine : public QObject {
// Evaluates all provided script files and returns true if no script errors
// occurred while evaluating them.
- bool loadScriptFiles(const QList& scriptPaths,
- const QList& scripts);
+ bool loadScriptFiles(const QList& scripts);
void initializeScripts(const QList& scripts);
void gracefulShutdown();
void scriptHasChanged(const QString&);
@@ -173,7 +172,7 @@ class ControllerEngine : public QObject {
private:
bool syntaxIsValid(const QString& scriptCode);
- bool evaluate(const QString& scriptName, QList scriptPaths);
+ bool evaluate(const QFileInfo& scriptFile);
bool internalExecute(QScriptValue thisObject, const QString& scriptCode);
bool internalExecute(QScriptValue thisObject, QScriptValue functionObject,
QScriptValueList arguments);
@@ -221,7 +220,7 @@ class ControllerEngine : public QObject {
QHash m_scriptWrappedFunctionCache;
// Filesystem watcher for script auto-reload
QFileSystemWatcher m_scriptWatcher;
- QList m_lastScriptPaths;
+ QList m_lastScriptFiles;
friend class ControllerEngineTest;
};
diff --git a/src/controllers/controllerinputmappingtablemodel.cpp b/src/controllers/controllerinputmappingtablemodel.cpp
index 456e0556307..237cda87745 100644
--- a/src/controllers/controllerinputmappingtablemodel.cpp
+++ b/src/controllers/controllerinputmappingtablemodel.cpp
@@ -18,12 +18,13 @@ void ControllerInputMappingTableModel::apply() {
if (m_pMidiPreset != NULL) {
// Clear existing input mappings and insert all the input mappings in
// the table into the preset.
- m_pMidiPreset->inputMappings.clear();
+ QHash mappings;
foreach (const MidiInputMapping& mapping, m_midiInputMappings) {
// Use insertMulti because we support multiple inputs mappings for
// the same input MidiKey.
- m_pMidiPreset->inputMappings.insertMulti(mapping.key.key, mapping);
+ mappings.insertMulti(mapping.key.key, mapping);
}
+ m_pMidiPreset->setInputMappings(mappings);
}
}
@@ -39,9 +40,9 @@ void ControllerInputMappingTableModel::onPresetLoaded() {
setHeaderData(MIDI_COLUMN_ACTION, Qt::Horizontal, tr("Action"));
setHeaderData(MIDI_COLUMN_COMMENT, Qt::Horizontal, tr("Comment"));
- if (!m_pMidiPreset->inputMappings.isEmpty()) {
- beginInsertRows(QModelIndex(), 0, m_pMidiPreset->inputMappings.size() - 1);
- m_midiInputMappings = m_pMidiPreset->inputMappings.values();
+ if (!m_pMidiPreset->getInputMappings().isEmpty()) {
+ beginInsertRows(QModelIndex(), 0, m_pMidiPreset->getInputMappings().size() - 1);
+ m_midiInputMappings = m_pMidiPreset->getInputMappings().values();
endInsertRows();
}
}
diff --git a/src/controllers/controllerinputmappingtablemodel.h b/src/controllers/controllerinputmappingtablemodel.h
index 96b6134f2a7..40d51d08c63 100644
--- a/src/controllers/controllerinputmappingtablemodel.h
+++ b/src/controllers/controllerinputmappingtablemodel.h
@@ -1,5 +1,4 @@
-#ifndef CONTROLLERINPUTMAPPINGTABLEMODEL_H
-#define CONTROLLERINPUTMAPPINGTABLEMODEL_H
+#pragma once
#include
#include
@@ -9,6 +8,9 @@
#include "controllers/controllermappingtablemodel.h"
#include "controllers/midi/midimessage.h"
+/// Table Model for the "Inputs" table view in the preferences dialog.
+///
+/// This allows editing the input mappings for a MIDI preset.
class ControllerInputMappingTableModel : public ControllerMappingTableModel {
Q_OBJECT
public:
@@ -64,5 +66,3 @@ class ControllerInputMappingTableModel : public ControllerMappingTableModel {
QList m_midiInputMappings;
};
-
-#endif /* CONTROLLERINPUTMAPPINGTABLEMODEL_H */
diff --git a/src/controllers/controllermanager.cpp b/src/controllers/controllermanager.cpp
index ec74976f7f5..a232fbfa294 100644
--- a/src/controllers/controllermanager.cpp
+++ b/src/controllers/controllermanager.cpp
@@ -39,6 +39,28 @@ const int kPollIntervalMillis = 5;
const int kPollIntervalMillis = 1;
#endif
+/// Strip slashes and spaces from device name, so that it can be used as config
+/// key or a filename.
+QString sanitizeDeviceName(QString name) {
+ return name.replace(" ", "_").replace("/", "_").replace("\\", "_");
+}
+
+QFileInfo findPresetFile(const QString& pathOrFilename, const QStringList& paths) {
+ QFileInfo fileInfo(pathOrFilename);
+ if (fileInfo.isAbsolute()) {
+ return fileInfo;
+ }
+
+ for (const QString& path : paths) {
+ fileInfo = QFileInfo(QDir(path).absoluteFilePath(pathOrFilename));
+ if (fileInfo.exists()) {
+ return fileInfo;
+ }
+ }
+
+ return QFileInfo();
+}
+
} // anonymous namespace
QString firstAvailableFilename(QSet& filenames,
@@ -95,8 +117,6 @@ ControllerManager::ControllerManager(UserSettingsPointer pConfig)
this, SLOT(slotSetUpDevices()));
connect(this, SIGNAL(requestShutdown()),
this, SLOT(slotShutdown()));
- connect(this, SIGNAL(requestSave(bool)),
- this, SLOT(slotSavePresets(bool)));
// Signal that we should run slotInitialize once our event loop has started
// up.
@@ -119,11 +139,10 @@ void ControllerManager::slotInitialize() {
// Initialize preset info parsers. This object is only for use in the main
// thread. Do not touch it from within ControllerManager.
- QStringList presetSearchPaths;
- presetSearchPaths << userPresetsPath(m_pConfig)
- << resourcePresetsPath(m_pConfig);
- m_pMainThreadPresetEnumerator = QSharedPointer(
- new PresetInfoEnumerator(presetSearchPaths));
+ m_pMainThreadUserPresetEnumerator = QSharedPointer(
+ new PresetInfoEnumerator(userPresetsPath(m_pConfig)));
+ m_pMainThreadSystemPresetEnumerator = QSharedPointer(
+ new PresetInfoEnumerator(resourcePresetsPath(m_pConfig)));
// Instantiate all enumerators. Enumerators can take a long time to
// construct since they interact with host MIDI APIs.
@@ -150,7 +169,7 @@ void ControllerManager::slotShutdown() {
locker.unlock();
// Delete enumerators and they'll delete their Devices
- foreach (ControllerEnumerator* pEnumerator, enumerators) {
+ for (ControllerEnumerator* pEnumerator : enumerators) {
delete pEnumerator;
}
@@ -168,7 +187,7 @@ void ControllerManager::updateControllerList() {
locker.unlock();
QList newDeviceList;
- foreach (ControllerEnumerator* pEnumerator, enumerators) {
+ for (ControllerEnumerator* pEnumerator : enumerators) {
newDeviceList.append(pEnumerator->queryDevices());
}
@@ -196,7 +215,7 @@ QList ControllerManager::getControllerList(bool bOutputDevices, boo
// options.
QList filteredDeviceList;
- foreach (Controller* device, controllers) {
+ for (Controller* device : controllers) {
if ((bOutputDevices == device->isOutputDevice()) ||
(bInputDevices == device->isInputDevice())) {
filteredDeviceList.push_back(device);
@@ -205,15 +224,18 @@ QList ControllerManager::getControllerList(bool bOutputDevices, boo
return filteredDeviceList;
}
+QString ControllerManager::getConfiguredPresetFileForDevice(QString name) {
+ return m_pConfig->getValueString(ConfigKey("[ControllerPreset]", sanitizeDeviceName(name)));
+}
+
void ControllerManager::slotSetUpDevices() {
qDebug() << "ControllerManager: Setting up devices";
updateControllerList();
QList deviceList = getControllerList(false, true);
+ QStringList presetPaths(getPresetPaths(m_pConfig));
- QSet filenames;
-
- foreach (Controller* pController, deviceList) {
+ for (Controller* pController : deviceList) {
QString name = pController->getName();
if (pController->isOpen()) {
@@ -221,27 +243,36 @@ void ControllerManager::slotSetUpDevices() {
}
// The filename for this device name.
- QString presetBaseName = presetFilenameFromName(name);
+ QString deviceName = sanitizeDeviceName(name);
- // The first unique filename for this device (appends numbers at the end
- // if we have already seen a controller by this name on this run of
- // Mixxx.
- presetBaseName = firstAvailableFilename(filenames, presetBaseName);
+ // Check if device is enabled
+ if (!m_pConfig->getValue(ConfigKey("[Controller]", deviceName), 0)) {
+ continue;
+ }
- ControllerPresetPointer pPreset =
- ControllerPresetFileHandler::loadPreset(
- presetBaseName + pController->presetExtension(),
- getPresetPaths(m_pConfig));
+ // Check if device has a configured preset
+ QString presetFilePath = getConfiguredPresetFileForDevice(deviceName);
+ if (presetFilePath.isEmpty()) {
+ continue;
+ }
- if (!loadPreset(pController, pPreset)) {
- // TODO(XXX) : auto load midi preset here.
+ qDebug() << "Searching for controller preset" << presetFilePath
+ << "in paths:" << presetPaths.join(",");
+ QFileInfo presetFile = findPresetFile(presetFilePath, presetPaths);
+ if (!presetFile.exists()) {
+ qDebug() << "Could not find" << presetFilePath << "in any preset path.";
continue;
}
- if (m_pConfig->getValueString(ConfigKey("[Controller]", presetBaseName)) != "1") {
+ ControllerPresetPointer pPreset = ControllerPresetFileHandler::loadPreset(
+ presetFile, resourcePresetsPath(m_pConfig));
+
+ if (!pPreset) {
continue;
}
+ pController->setPreset(*pPreset);
+
// If we are in safe mode, skip opening controllers.
if (CmdlineArgs::Instance().getSafeMode()) {
qDebug() << "We are in safe mode -- skipping opening controller.";
@@ -255,7 +286,7 @@ void ControllerManager::slotSetUpDevices() {
qWarning() << "There was a problem opening" << name;
continue;
}
- pController->applyPreset(getPresetPaths(m_pConfig), true);
+ pController->applyPreset();
}
maybeStartOrStopPolling();
@@ -267,7 +298,7 @@ void ControllerManager::maybeStartOrStopPolling() {
locker.unlock();
bool shouldPoll = false;
- foreach (Controller* pController, controllers) {
+ for (Controller* pController : controllers) {
if (pController->isOpen() && pController->isPolling()) {
shouldPoll = true;
}
@@ -321,7 +352,7 @@ void ControllerManager::pollDevices() {
}
mixxx::Duration start = mixxx::Time::elapsed();
- foreach (Controller* pDevice, m_controllers) {
+ for (Controller* pDevice : m_controllers) {
if (pDevice->isOpen() && pDevice->isPolling()) {
pDevice->poll();
}
@@ -347,11 +378,11 @@ void ControllerManager::openController(Controller* pController) {
// If successfully opened the device, apply the preset and save the
// preference setting.
if (result == 0) {
- pController->applyPreset(getPresetPaths(m_pConfig), true);
+ pController->applyPreset();
// Update configuration to reflect controller is enabled.
- m_pConfig->setValue(ConfigKey(
- "[Controller]", presetFilenameFromName(pController->getName())), 1);
+ m_pConfig->setValue(
+ ConfigKey("[Controller]", sanitizeDeviceName(pController->getName())), 1);
}
}
@@ -362,45 +393,40 @@ void ControllerManager::closeController(Controller* pController) {
pController->close();
maybeStartOrStopPolling();
// Update configuration to reflect controller is disabled.
- m_pConfig->setValue(ConfigKey(
- "[Controller]", presetFilenameFromName(pController->getName())), 0);
+ m_pConfig->setValue(
+ ConfigKey("[Controller]", sanitizeDeviceName(pController->getName())), 0);
}
-bool ControllerManager::loadPreset(Controller* pController,
- ControllerPresetPointer preset) {
- if (!preset) {
- return false;
+void ControllerManager::slotApplyPreset(Controller* pController,
+ ControllerPresetPointer pPreset,
+ bool bEnabled) {
+ VERIFY_OR_DEBUG_ASSERT(pController) {
+ qWarning() << "slotApplyPreset got invalid controller!";
+ return;
}
- pController->setPreset(*preset.data());
+
+ ConfigKey key("[ControllerPreset]", sanitizeDeviceName(pController->getName()));
+ if (!pPreset) {
+ closeController(pController);
+ // Unset the controller preset for this controller
+ m_pConfig->remove(key);
+ return;
+ }
+
+ VERIFY_OR_DEBUG_ASSERT(!pPreset->isDirty()) {
+ qWarning() << "Preset is dirty, changes might be lost on restart!";
+ }
+
+ pController->setPreset(*pPreset);
+
// Save the file path/name in the config so it can be auto-loaded at
// startup next time
- m_pConfig->set(
- ConfigKey("[ControllerPreset]",
- presetFilenameFromName(pController->getName())),
- preset->filePath());
- return true;
-}
-
-void ControllerManager::slotSavePresets(bool onlyActive) {
- QList deviceList = getControllerList(false, true);
- QSet filenames;
+ m_pConfig->set(key, pPreset->filePath());
- // TODO(rryan): This should be split up somehow but the filename selection
- // is dependent on all of the controllers to prevent over-writing each
- // other. We need a better solution.
- foreach (Controller* pController, deviceList) {
- if (onlyActive && !pController->isOpen()) {
- continue;
- }
- QString name = pController->getName();
- QString filename = firstAvailableFilename(
- filenames, presetFilenameFromName(name));
- QString presetPath = userPresetsPath(m_pConfig) + filename
- + pController->presetExtension();
- if (!pController->savePreset(presetPath)) {
- qWarning() << "Failed to write preset for device"
- << name << "to" << presetPath;
- }
+ if (bEnabled) {
+ openController(pController);
+ } else {
+ closeController(pController);
}
}
@@ -411,111 +437,3 @@ QList ControllerManager::getPresetPaths(UserSettingsPointer pConfig) {
scriptPaths.append(resourcePresetsPath(pConfig));
return scriptPaths;
}
-
-// static
-bool ControllerManager::checksumFile(const QString& filename,
- quint16* pChecksum) {
- QFile file(filename);
- if (!file.open(QIODevice::ReadOnly)) {
- return false;
- }
-
- qint64 fileSize = file.size();
- const char* pFile = reinterpret_cast(file.map(0, fileSize));
-
- if (pFile == NULL) {
- file.close();
- return false;
- }
-
- *pChecksum = qChecksum(pFile, fileSize);
- file.close();
- return true;
-}
-
-// static
-QString ControllerManager::getAbsolutePath(const QString& pathOrFilename,
- const QStringList& paths) {
- QFileInfo fileInfo(pathOrFilename);
- if (fileInfo.isAbsolute()) {
- return pathOrFilename;
- }
-
- foreach (const QString& path, paths) {
- QDir pathDir(path);
-
- if (pathDir.exists(pathOrFilename)) {
- return pathDir.absoluteFilePath(pathOrFilename);
- }
- }
-
- return QString();
-}
-
-bool ControllerManager::importScript(const QString& scriptPath,
- QString* newScriptFileName) {
- QDir userPresets(userPresetsPath(m_pConfig));
-
- qDebug() << "ControllerManager::importScript importing script" << scriptPath
- << "to" << userPresets.absolutePath();
-
- QFile scriptFile(scriptPath);
- QFileInfo script(scriptFile);
-
- if (!script.exists() || !script.isReadable()) {
- qWarning() << "ControllerManager::importScript script does not exist"
- << "or is unreadable:" << scriptPath;
- return false;
- }
-
- // Not fatal if we can't checksum but still warn about it.
- quint16 scriptChecksum = 0;
- bool scriptChecksumGood = checksumFile(scriptPath, &scriptChecksum);
- if (!scriptChecksumGood) {
- qWarning() << "ControllerManager::importScript could not checksum file:"
- << scriptPath;
- }
-
- // The name we will save this file as in our local script mixxxdb. The
- // conflict resolution logic below will mutate this variable if the name is
- // already taken.
- QString scriptFileName = script.fileName();
-
- // For a file like "myfile.foo.bar.js", scriptBaseName is "myfile.foo.bar"
- // and scriptSuffix is "js".
- QString scriptBaseName = script.completeBaseName();
- QString scriptSuffix = script.suffix();
- int conflictNumber = 1;
-
- // This script exists.
- while (userPresets.exists(scriptFileName)) {
- // If the two files are identical. We're done.
- quint16 localScriptChecksum = 0;
- if (checksumFile(userPresets.filePath(scriptFileName), &localScriptChecksum) &&
- scriptChecksumGood && scriptChecksum == localScriptChecksum) {
- *newScriptFileName = scriptFileName;
- qDebug() << "ControllerManager::importScript" << scriptFileName
- << "had identical checksum to a file of the same name."
- << "Skipping import.";
- return true;
- }
-
- // Otherwise, we need to rename the file to a non-conflicting
- // name. Insert a .X where X is a counter that we count up until we find
- // a filename that does not exist.
- scriptFileName = QString("%1.%2.%3").arg(
- scriptBaseName,
- QString::number(conflictNumber++),
- scriptSuffix);
- }
-
- QString destinationPath = userPresets.filePath(scriptFileName);
- if (!scriptFile.copy(destinationPath)) {
- qDebug() << "ControllerManager::importScript could not copy script to"
- << "local preset path:" << destinationPath;
- return false;
- }
-
- *newScriptFileName = scriptFileName;
- return true;
-}
diff --git a/src/controllers/controllermanager.h b/src/controllers/controllermanager.h
index 1fa873ba4bc..8837d903580 100644
--- a/src/controllers/controllermanager.h
+++ b/src/controllers/controllermanager.h
@@ -33,41 +33,32 @@ class ControllerManager : public QObject {
QList getControllers() const;
QList getControllerList(bool outputDevices=true, bool inputDevices=true);
ControllerLearningEventFilter* getControllerLearningEventFilter() const;
- QSharedPointer getMainThreadPresetEnumerator() {
- return m_pMainThreadPresetEnumerator;
+ QSharedPointer getMainThreadUserPresetEnumerator() {
+ return m_pMainThreadUserPresetEnumerator;
}
+ QSharedPointer getMainThreadSystemPresetEnumerator() {
+ return m_pMainThreadSystemPresetEnumerator;
+ }
+ QString getConfiguredPresetFileForDevice(QString name);
// Prevent other parts of Mixxx from having to manually connect to our slots
void setUpDevices() { emit requestSetUpDevices(); };
- void savePresets(bool onlyActive=false) { emit requestSave(onlyActive); };
static QList getPresetPaths(UserSettingsPointer pConfig);
- // If pathOrFilename is an absolute path, returns it. If it is a relative
- // path and it is contained within any of the directories in presetPaths,
- // returns the path to the first file in the path that exists.
- static QString getAbsolutePath(const QString& pathOrFilename,
- const QStringList& presetPaths);
-
- bool importScript(const QString& scriptPath, QString* newScriptFileName);
- static bool checksumFile(const QString& filename, quint16* pChecksum);
-
signals:
void devicesChanged();
void requestSetUpDevices();
void requestShutdown();
- void requestSave(bool onlyActive);
void requestInitialize();
public slots:
void updateControllerList();
+ void slotApplyPreset(Controller* pController, ControllerPresetPointer pPreset, bool bEnabled);
void openController(Controller* pController);
void closeController(Controller* pController);
- // Writes out presets for currently connected input devices
- void slotSavePresets(bool onlyActive=false);
-
private slots:
// Perform initialization that should be delayed until the ControllerManager
// thread is started.
@@ -77,18 +68,12 @@ class ControllerManager : public QObject {
// preferences dialog on apply, and only open/close changed devices
void slotSetUpDevices();
void slotShutdown();
- bool loadPreset(Controller* pController,
- ControllerPresetPointer preset);
// Calls poll() on all devices that have isPolling() true.
void pollDevices();
void startPolling();
void stopPolling();
void maybeStartOrStopPolling();
- static QString presetFilenameFromName(QString name) {
- return name.replace(" ", "_").replace("/", "_").replace("\\", "_");
- }
-
private:
UserSettingsPointer m_pConfig;
ControllerLearningEventFilter* m_pControllerLearningEventFilter;
@@ -97,7 +82,8 @@ class ControllerManager : public QObject {
QList m_enumerators;
QList m_controllers;
QThread* m_pThread;
- QSharedPointer m_pMainThreadPresetEnumerator;
+ QSharedPointer m_pMainThreadUserPresetEnumerator;
+ QSharedPointer m_pMainThreadSystemPresetEnumerator;
bool m_skipPoll;
};
diff --git a/src/controllers/controlleroutputmappingtablemodel.cpp b/src/controllers/controlleroutputmappingtablemodel.cpp
index 7dc3756e414..253aa412d6d 100644
--- a/src/controllers/controlleroutputmappingtablemodel.cpp
+++ b/src/controllers/controlleroutputmappingtablemodel.cpp
@@ -18,12 +18,13 @@ void ControllerOutputMappingTableModel::apply() {
if (m_pMidiPreset != NULL) {
// Clear existing output mappings and insert all the output mappings in
// the table into the preset.
- m_pMidiPreset->outputMappings.clear();
+ QHash mappings;
foreach (const MidiOutputMapping& mapping, m_midiOutputMappings) {
// Use insertMulti because we support multiple outputs from the same
// control.
- m_pMidiPreset->outputMappings.insertMulti(mapping.controlKey, mapping);
+ mappings.insertMulti(mapping.controlKey, mapping);
}
+ m_pMidiPreset->setOutputMappings(mappings);
}
}
@@ -42,9 +43,9 @@ void ControllerOutputMappingTableModel::onPresetLoaded() {
setHeaderData(MIDI_COLUMN_MAX, Qt::Horizontal, tr("On Range Max"));
setHeaderData(MIDI_COLUMN_COMMENT, Qt::Horizontal, tr("Comment"));
- if (!m_pMidiPreset->outputMappings.isEmpty()) {
- beginInsertRows(QModelIndex(), 0, m_pMidiPreset->outputMappings.size() - 1);
- m_midiOutputMappings = m_pMidiPreset->outputMappings.values();
+ if (!m_pMidiPreset->getOutputMappings().isEmpty()) {
+ beginInsertRows(QModelIndex(), 0, m_pMidiPreset->getOutputMappings().size() - 1);
+ m_midiOutputMappings = m_pMidiPreset->getOutputMappings().values();
endInsertRows();
}
}
diff --git a/src/controllers/controlleroutputmappingtablemodel.h b/src/controllers/controlleroutputmappingtablemodel.h
index 781338c0e7a..36be40f69a6 100644
--- a/src/controllers/controlleroutputmappingtablemodel.h
+++ b/src/controllers/controlleroutputmappingtablemodel.h
@@ -1,5 +1,4 @@
-#ifndef CONTROLLEROUTPUTMAPPINGTABLEMODEL_H
-#define CONTROLLEROUTPUTMAPPINGTABLEMODEL_H
+#pragma once
#include
#include
@@ -9,6 +8,9 @@
#include "controllers/controllermappingtablemodel.h"
#include "controllers/midi/midimessage.h"
+/// Table Model for the "Outputs" table view in the preferences dialog.
+///
+/// This allows editing the output mappings for a MIDI preset.
class ControllerOutputMappingTableModel : public ControllerMappingTableModel {
Q_OBJECT
public:
@@ -58,5 +60,3 @@ class ControllerOutputMappingTableModel : public ControllerMappingTableModel {
QList m_midiOutputMappings;
};
-
-#endif /* CONTROLLEROUTPUTMAPPINGTABLEMODEL_H */
diff --git a/src/controllers/controllerpreset.h b/src/controllers/controllerpreset.h
index ad4019632e1..e51092961de 100644
--- a/src/controllers/controllerpreset.h
+++ b/src/controllers/controllerpreset.h
@@ -1,27 +1,26 @@
-/**
-* @file controllerpreset.h
-* @author Sean Pappalardo spappalardo@mixxx.org
-* @date Mon 9 Apr 2012
-* @brief Controller preset
-*
-* This class represents a controller preset, containing the data elements that
-* make it up.
-*/
-
-#ifndef CONTROLLERPRESET_H
-#define CONTROLLERPRESET_H
-
+#pragma once
+/// @file controllerpreset.h
+/// @author Sean Pappalardo spappalardo@mixxx.org
+/// @date Mon 9 Apr 2012
+/// @brief Controller Preset
+
+#include
+#include
#include
+#include
#include
#include
-#include
class ControllerPresetVisitor;
class ConstControllerPresetVisitor;
+/// This class represents a controller preset, containing the data elements that
+/// make it up.
class ControllerPreset {
public:
- ControllerPreset() {}
+ ControllerPreset()
+ : m_bDirty(false) {
+ }
virtual ~ControllerPreset() {}
struct ScriptFileInfo {
@@ -32,25 +31,43 @@ class ControllerPreset {
QString name;
QString functionPrefix;
+ QFileInfo file;
bool builtin;
};
- /** addScriptFile(QString,QString)
- * Adds an entry to the list of script file names & associated list of function prefixes
- * @param filename Name of the XML file to add
- * @param functionprefix Function prefix to add
- */
- void addScriptFile(QString filename, QString functionprefix,
- bool builtin=false) {
+ /// Adds a script file to the list of controller scripts for this preset.
+ /// @param filename Name of the script file to add
+ /// @param functionprefix The script's function prefix (or empty string)
+ /// @param file A FileInfo object pointing to the script file
+ /// @param builtin If this is true, the script won't be written to the XML
+ void addScriptFile(const QString& name,
+ const QString& functionprefix,
+ const QFileInfo& file,
+ bool builtin = false) {
ScriptFileInfo info;
- info.name = filename;
+ info.name = name;
info.functionPrefix = functionprefix;
+ info.file = file;
info.builtin = builtin;
- scripts.append(info);
+ m_scripts.append(info);
+ setDirty(true);
+ }
+
+ const QList& getScriptFiles() const {
+ return m_scripts;
+ }
+
+ inline void setDirty(bool bDirty) {
+ m_bDirty = bDirty;
+ }
+
+ inline bool isDirty() const {
+ return m_bDirty;
}
inline void setDeviceId(const QString id) {
m_deviceId = id;
+ setDirty(true);
}
inline QString deviceId() const {
@@ -59,14 +76,20 @@ class ControllerPreset {
inline void setFilePath(const QString filePath) {
m_filePath = filePath;
+ setDirty(true);
}
inline QString filePath() const {
return m_filePath;
}
+ inline QDir dirPath() const {
+ return QFileInfo(filePath()).absoluteDir();
+ }
+
inline void setName(const QString name) {
m_name = name;
+ setDirty(true);
}
inline QString name() const {
@@ -75,6 +98,7 @@ class ControllerPreset {
inline void setAuthor(const QString author) {
m_author = author;
+ setDirty(true);
}
inline QString author() const {
@@ -83,6 +107,7 @@ class ControllerPreset {
inline void setDescription(const QString description) {
m_description = description;
+ setDirty(true);
}
inline QString description() const {
@@ -91,6 +116,7 @@ class ControllerPreset {
inline void setForumLink(const QString forumlink) {
m_forumlink = forumlink;
+ setDirty(true);
}
inline QString forumlink() const {
@@ -99,6 +125,7 @@ class ControllerPreset {
inline void setWikiLink(const QString wikilink) {
m_wikilink = wikilink;
+ setDirty(true);
}
inline QString wikilink() const {
@@ -107,6 +134,7 @@ class ControllerPreset {
inline void setSchemaVersion(const QString schemaVersion) {
m_schemaVersion = schemaVersion;
+ setDirty(true);
}
inline QString schemaVersion() const {
@@ -115,6 +143,7 @@ class ControllerPreset {
inline void setMixxxVersion(const QString mixxxVersion) {
m_mixxxVersion = mixxxVersion;
+ setDirty(true);
}
inline QString mixxxVersion() const {
@@ -123,8 +152,11 @@ class ControllerPreset {
inline void addProductMatch(QHash match) {
m_productMatches.append(match);
+ setDirty(true);
}
+ virtual bool savePreset(const QString& filename) const = 0;
+
virtual void accept(ControllerPresetVisitor* visitor) = 0;
virtual void accept(ConstControllerPresetVisitor* visitor) const = 0;
virtual bool isMappable() const = 0;
@@ -134,6 +166,8 @@ class ControllerPreset {
QList< QHash > m_productMatches;
private:
+ bool m_bDirty;
+
QString m_deviceId;
QString m_filePath;
QString m_name;
@@ -143,8 +177,8 @@ class ControllerPreset {
QString m_wikilink;
QString m_schemaVersion;
QString m_mixxxVersion;
+
+ QList m_scripts;
};
typedef QSharedPointer ControllerPresetPointer;
-
-#endif
diff --git a/src/controllers/controllerpresetfilehandler.cpp b/src/controllers/controllerpresetfilehandler.cpp
index f50b8bd1b23..6d147a6e885 100644
--- a/src/controllers/controllerpresetfilehandler.cpp
+++ b/src/controllers/controllerpresetfilehandler.cpp
@@ -1,10 +1,7 @@
-/**
-* @file controllerpresetfilehandler.cpp
-* @author Sean Pappalardo spappalardo@mixxx.org
-* @date Mon 9 Apr 2012
-* @brief Handles loading and saving of Controller presets.
-*
-*/
+/// @file controllerpresetfilehandler.cpp
+/// @author Sean Pappalardo spappalardo@mixxx.org
+/// @date Mon 9 Apr 2012
+/// @brief Handles loading and saving of Controller presets.
#include "controllers/controllerpresetfilehandler.h"
#include "controllers/controllermanager.h"
@@ -12,57 +9,75 @@
#include "controllers/midi/midicontrollerpresetfilehandler.h"
#include "controllers/hid/hidcontrollerpresetfilehandler.h"
-// static
-ControllerPresetPointer ControllerPresetFileHandler::loadPreset(const QString& pathOrFilename,
- const QStringList& presetPaths) {
- qDebug() << "Searching for controller preset" << pathOrFilename
- << "in paths:" << presetPaths.join(",");
- QString scriptPath = ControllerManager::getAbsolutePath(pathOrFilename,
- presetPaths);
-
- if (scriptPath.isEmpty()) {
- qDebug() << "Could not find" << pathOrFilename
- << "in any preset path.";
- return ControllerPresetPointer();
- }
+namespace {
+
+/// Find script file in the preset or system path.
+///
+/// @param preset The controller preset the script belongs to.
+/// @param filename The script filename.
+/// @param systemPresetsPath The system presets path to use as fallback.
+/// @return Returns a QFileInfo object. If the script was not found in either
+/// of the search directories, the QFileInfo object might point to a
+/// non-existing file.
+QFileInfo findScriptFile(ControllerPreset* preset,
+ const QString& filename,
+ const QDir& systemPresetsPath) {
+ // Always try to load script from the mapping's directory first
+ QFileInfo file = QFileInfo(preset->dirPath().absoluteFilePath(filename));
+
+ // If the script does not exist, try to find it in the fallback dir
+ if (!file.exists()) {
+ file = QFileInfo(systemPresetsPath.absoluteFilePath(filename));
+ }
+ return file;
+}
+
+} // namespace
- QFileInfo scriptPathInfo(scriptPath);
- if (!scriptPathInfo.exists() || !scriptPathInfo.isReadable()) {
- qDebug() << "Preset" << scriptPath << "does not exist or is unreadable.";
+// static
+ControllerPresetPointer ControllerPresetFileHandler::loadPreset(
+ const QFileInfo& presetFile, const QDir& systemPresetsPath) {
+ if (!presetFile.exists() || !presetFile.isReadable()) {
+ qDebug() << "Preset" << presetFile.absoluteFilePath()
+ << "does not exist or is unreadable.";
return ControllerPresetPointer();
}
- ControllerPresetFileHandler* pHandler = NULL;
- if (scriptPath.endsWith(MIDI_PRESET_EXTENSION, Qt::CaseInsensitive)) {
+ ControllerPresetFileHandler* pHandler = nullptr;
+ if (presetFile.fileName().endsWith(
+ MIDI_PRESET_EXTENSION, Qt::CaseInsensitive)) {
pHandler = new MidiControllerPresetFileHandler();
- } else if (scriptPath.endsWith(HID_PRESET_EXTENSION, Qt::CaseInsensitive) ||
- scriptPath.endsWith(BULK_PRESET_EXTENSION, Qt::CaseInsensitive)) {
+ } else if (presetFile.fileName().endsWith(
+ HID_PRESET_EXTENSION, Qt::CaseInsensitive) ||
+ presetFile.fileName().endsWith(
+ BULK_PRESET_EXTENSION, Qt::CaseInsensitive)) {
pHandler = new HidControllerPresetFileHandler();
}
- if (pHandler == NULL) {
- qDebug() << "Preset" << scriptPath << "has an unrecognized extension.";
+ if (pHandler == nullptr) {
+ qDebug() << "Preset" << presetFile.absoluteFilePath()
+ << "has an unrecognized extension.";
return ControllerPresetPointer();
}
- // NOTE(rryan): We don't provide a device name. It's unused currently.
- // TODO(rryan): Delete pHandler.
- return pHandler->load(scriptPath, QString());
+ ControllerPresetPointer pPreset = pHandler->load(
+ presetFile.absoluteFilePath(), systemPresetsPath);
+ if (pPreset) {
+ pPreset->setDirty(false);
+ }
+ return pPreset;
}
-ControllerPresetPointer ControllerPresetFileHandler::load(const QString path,
- const QString deviceName) {
+ControllerPresetPointer ControllerPresetFileHandler::load(
+ const QString& path, const QDir& systemPresetsPath) {
qDebug() << "Loading controller preset from" << path;
- ControllerPresetPointer pPreset = load(XmlParse::openXMLFile(path, "controller"),
- deviceName);
- if (pPreset) {
- pPreset->setFilePath(path);
- }
+ ControllerPresetPointer pPreset = load(
+ XmlParse::openXMLFile(path, "controller"), path, systemPresetsPath);
return pPreset;
}
-void ControllerPresetFileHandler::parsePresetInfo(const QDomElement& root,
- ControllerPreset* preset) const {
+void ControllerPresetFileHandler::parsePresetInfo(
+ const QDomElement& root, ControllerPreset* preset) const {
if (root.isNull() || !preset) {
return;
}
@@ -88,9 +103,8 @@ void ControllerPresetFileHandler::parsePresetInfo(const QDomElement& root,
preset->setWikiLink(wiki.isNull() ? "" : wiki.text());
}
-QDomElement ControllerPresetFileHandler::getControllerNode(const QDomElement& root,
- const QString deviceName) {
- Q_UNUSED(deviceName);
+QDomElement ControllerPresetFileHandler::getControllerNode(
+ const QDomElement& root) {
if (root.isNull()) {
return QDomElement();
}
@@ -102,7 +116,9 @@ QDomElement ControllerPresetFileHandler::getControllerNode(const QDomElement& ro
}
void ControllerPresetFileHandler::addScriptFilesToPreset(
- const QDomElement& controller, ControllerPreset* preset) const {
+ const QDomElement& controller,
+ ControllerPreset* preset,
+ const QDir& systemPresetsPath) const {
if (controller.isNull())
return;
@@ -111,22 +127,27 @@ void ControllerPresetFileHandler::addScriptFilesToPreset(
// Build a list of script files to load
QDomElement scriptFile = controller.firstChildElement("scriptfiles")
- .firstChildElement("file");
+ .firstChildElement("file");
// Default currently required file
- preset->addScriptFile(REQUIRED_SCRIPT_FILE, "", true);
+ preset->addScriptFile(REQUIRED_SCRIPT_FILE,
+ "",
+ findScriptFile(preset, REQUIRED_SCRIPT_FILE, systemPresetsPath),
+ true);
// Look for additional ones
while (!scriptFile.isNull()) {
- QString functionPrefix = scriptFile.attribute("functionprefix","");
- QString filename = scriptFile.attribute("filename","");
- preset->addScriptFile(filename, functionPrefix);
+ QString functionPrefix = scriptFile.attribute("functionprefix", "");
+ QString filename = scriptFile.attribute("filename", "");
+ QFileInfo file = findScriptFile(preset, filename, systemPresetsPath);
+
+ preset->addScriptFile(filename, functionPrefix, file);
scriptFile = scriptFile.nextSiblingElement("file");
}
}
-bool ControllerPresetFileHandler::writeDocument(QDomDocument root,
- const QString fileName) const {
+bool ControllerPresetFileHandler::writeDocument(
+ QDomDocument root, const QString fileName) const {
// Need to do this on Windows
QDir directory;
if (!directory.mkpath(fileName.left(fileName.lastIndexOf("/")))) {
@@ -150,16 +171,18 @@ bool ControllerPresetFileHandler::writeDocument(QDomDocument root,
return true;
}
-void addTextTag(QDomDocument& doc, QDomElement& holder,
- QString tagName, QString tagText) {
+void addTextTag(QDomDocument& doc,
+ QDomElement& holder,
+ QString tagName,
+ QString tagText) {
QDomElement tag = doc.createElement(tagName);
QDomText textNode = doc.createTextNode(tagText);
tag.appendChild(textNode);
holder.appendChild(tag);
}
-QDomDocument ControllerPresetFileHandler::buildRootWithScripts(const ControllerPreset& preset,
- const QString deviceName) const {
+QDomDocument ControllerPresetFileHandler::buildRootWithScripts(
+ const ControllerPreset& preset) const {
QDomDocument doc("Preset");
QString blank = "\n"
"\n"
@@ -190,13 +213,13 @@ QDomDocument ControllerPresetFileHandler::buildRootWithScripts(const ControllerP
QDomElement controller = doc.createElement("controller");
// Strip off the serial number
- controller.setAttribute("id", rootDeviceName(deviceName));
+ controller.setAttribute("id", rootDeviceName(preset.deviceId()));
rootNode.appendChild(controller);
QDomElement scriptFiles = doc.createElement("scriptfiles");
controller.appendChild(scriptFiles);
- foreach (const ControllerPreset::ScriptFileInfo& script, preset.scripts) {
+ for (const ControllerPreset::ScriptFileInfo& script : preset.getScriptFiles()) {
QString filename = script.name;
// Don't need to write anything for built-in files.
if (script.builtin) {
diff --git a/src/controllers/controllerpresetfilehandler.h b/src/controllers/controllerpresetfilehandler.h
index 904a8721960..2ee8c34e37d 100644
--- a/src/controllers/controllerpresetfilehandler.h
+++ b/src/controllers/controllerpresetfilehandler.h
@@ -1,30 +1,31 @@
-/**
-* @file controllerpresetfilehandler.h
-* @author Sean Pappalardo spappalardo@mixxx.org
-* @date Mon 9 Apr 2012
-* @brief Handles loading and saving of Controller presets.
-*
-*/
-#ifndef CONTROLLERPRESETFILEHANDLER_H
-#define CONTROLLERPRESETFILEHANDLER_H
+#pragma once
+/// @file controllerpresetfilehandler.h
+/// @author Sean Pappalardo spappalardo@mixxx.org
+/// @date Mon 9 Apr 2012
+/// @brief Handles loading and saving of Controller presets.
#include "util/xml.h"
#include "controllers/controllerpreset.h"
+/// The ControllerPresetFileHandler is used for serializing/deserializing the
+/// ControllerPreset objects to/from XML files and is also responsible
+/// finding the script files that belong to a preset in the file system.
+///
+/// Subclasses can implement the private load function to add support for XML
+/// elements that are only useful for certain mapping types.
class ControllerPresetFileHandler {
public:
ControllerPresetFileHandler() {};
virtual ~ControllerPresetFileHandler() {};
- static ControllerPresetPointer loadPreset(const QString& path,
- const QStringList& presetPaths);
+ static ControllerPresetPointer loadPreset(const QFileInfo& presetFile,
+ const QDir& systemPresetsPath);
- /** load(QString,QString,bool)
- * Overloaded function for convenience
- * @param path The path to a controller preset XML file.
- * @param deviceName The name/id of the controller
- */
- ControllerPresetPointer load(const QString path, const QString deviceName);
+ /// Overloaded function for convenience
+ ///
+ /// @param path The path to a controller preset XML file.
+ /// @param systemPresetsPath Fallback directory for searching script files.
+ ControllerPresetPointer load(const QString& path, const QDir& systemPresetsPath);
// Returns just the name of a given device (everything before the first
// space)
@@ -33,32 +34,33 @@ class ControllerPresetFileHandler {
}
protected:
- QDomElement getControllerNode(const QDomElement& root,
- const QString deviceName);
+ QDomElement getControllerNode(const QDomElement& root);
void parsePresetInfo(const QDomElement& root,
ControllerPreset* preset) const;
- /** addScriptFilesToPreset(QDomElement,QString,bool)
- * Loads script files specified in a QDomElement structure into the supplied
- * ControllerPreset.
- * @param root The root node of the XML document for the preset.
- * @param deviceName The name/id of the controller
- * @param preset The ControllerPreset into which the scripts should be placed.
- */
+ /// Adds script files from XML to the ControllerPreset.
+ ///
+ /// This function parses the supplied QDomElement structure, finds the
+ /// matching script files inside the search paths and adds them to
+ /// ControllerPreset.
+ ///
+ /// @param root The root node of the XML document for the preset.
+ /// @param preset The ControllerPreset these scripts belong to.
+ /// @param systemPresetsPath Fallback directory for searching script files.
void addScriptFilesToPreset(const QDomElement& root,
- ControllerPreset* preset) const;
+ ControllerPreset* preset,
+ const QDir& systemPresetsPath) const;
- // Creates the XML document and includes what script files are currently
- // loaded. Sub-classes need to call this before adding any other items.
- QDomDocument buildRootWithScripts(const ControllerPreset& preset,
- const QString deviceName) const;
+ /// Creates the XML document and includes what script files are currently
+ /// loaded. Sub-classes need to call this before adding any other items.
+ QDomDocument buildRootWithScripts(const ControllerPreset& preset) const;
bool writeDocument(QDomDocument root, const QString fileName) const;
private:
// Sub-classes implement this.
- virtual ControllerPresetPointer load(const QDomElement root, const QString deviceName) = 0;
+ virtual ControllerPresetPointer load(const QDomElement& root,
+ const QString& filePath,
+ const QDir& systemPresetPath) = 0;
};
-
-#endif
diff --git a/src/controllers/controllerpresetinfo.cpp b/src/controllers/controllerpresetinfo.cpp
index 3e8c1d581f4..bf0051209af 100644
--- a/src/controllers/controllerpresetinfo.cpp
+++ b/src/controllers/controllerpresetinfo.cpp
@@ -10,7 +10,6 @@
*/
#include "controllers/controllerpresetinfo.h"
-#include "controllers/controllerpresetinfoenumerator.h"
#include "controllers/defs_controllers.h"
#include "util/xml.h"
diff --git a/src/controllers/controllerpresetinfoenumerator.cpp b/src/controllers/controllerpresetinfoenumerator.cpp
index 60e5d6e0408..becbad20a4d 100644
--- a/src/controllers/controllerpresetinfoenumerator.cpp
+++ b/src/controllers/controllerpresetinfoenumerator.cpp
@@ -29,6 +29,10 @@ bool presetInfoNameComparator(const PresetInfo &a, const PresetInfo &b) {
}
}
+PresetInfoEnumerator::PresetInfoEnumerator(const QString& searchPath)
+ : PresetInfoEnumerator(QList{searchPath}) {
+}
+
PresetInfoEnumerator::PresetInfoEnumerator(const QStringList& searchPaths)
: m_controllerDirPaths(searchPaths) {
loadSupportedPresets();
@@ -48,6 +52,10 @@ QList PresetInfoEnumerator::getPresetsByExtension(const QString& ext
}
void PresetInfoEnumerator::loadSupportedPresets() {
+ m_midiPresets.clear();
+ m_hidPresets.clear();
+ m_bulkPresets.clear();
+
for (const QString& dirPath : m_controllerDirPaths) {
QDirIterator it(dirPath);
while (it.hasNext()) {
diff --git a/src/controllers/controllerpresetinfoenumerator.h b/src/controllers/controllerpresetinfoenumerator.h
index d948aadfb61..07a71dfd10d 100644
--- a/src/controllers/controllerpresetinfoenumerator.h
+++ b/src/controllers/controllerpresetinfoenumerator.h
@@ -15,12 +15,11 @@
class PresetInfoEnumerator {
public:
+ PresetInfoEnumerator(const QString& searchPath);
PresetInfoEnumerator(const QStringList& searchPaths);
// Return cached list of presets for this extension
QList getPresetsByExtension(const QString& extension);
-
- protected:
void loadSupportedPresets();
private:
diff --git a/src/controllers/dlgprefcontroller.cpp b/src/controllers/dlgprefcontroller.cpp
index b470fad540b..e281bbb5011 100644
--- a/src/controllers/dlgprefcontroller.cpp
+++ b/src/controllers/dlgprefcontroller.cpp
@@ -39,15 +39,14 @@ DlgPrefController::DlgPrefController(QWidget* parent, Controller* controller,
initTableView(m_ui.m_pInputMappingTableView);
initTableView(m_ui.m_pOutputMappingTableView);
- connect(m_pController, SIGNAL(presetLoaded(ControllerPresetPointer)),
- this, SLOT(slotPresetLoaded(ControllerPresetPointer)));
+ connect(m_pController, &Controller::presetLoaded, this, &DlgPrefController::slotShowPreset);
// TODO(rryan): Eh, this really isn't thread safe but it's the way it's been
// since 1.11.0. We shouldn't be calling Controller methods because it lives
// in a different thread. Booleans (like isOpen()) are fine but a complex
// object like a preset involves QHash's and other data structures that
// really don't like concurrent access.
ControllerPresetPointer pPreset = m_pController->getPreset();
- slotPresetLoaded(pPreset);
+ slotShowPreset(pPreset);
m_ui.labelDeviceName->setText(m_pController->getName());
QString category = m_pController->getCategory();
@@ -58,44 +57,55 @@ DlgPrefController::DlgPrefController(QWidget* parent, Controller* controller,
}
// When the user picks a preset, load it.
- connect(m_ui.comboBoxPreset, SIGNAL(activated(int)),
- this, SLOT(slotLoadPreset(int)));
+ connect(m_ui.comboBoxPreset, SIGNAL(activated(int)), this, SLOT(slotPresetSelected(int)));
- // When the user toggles the Enabled checkbox, toggle.
- connect(m_ui.chkEnabledDevice, SIGNAL(clicked(bool)),
- this, SLOT(slotEnableDevice(bool)));
+ // When the user toggles the Enabled checkbox, mark as dirty
+ connect(m_ui.chkEnabledDevice, &QCheckBox::clicked, [this] { setDirty(true); });
// Connect our signals to controller manager.
- connect(this, SIGNAL(openController(Controller*)),
- m_pControllerManager, SLOT(openController(Controller*)));
- connect(this, SIGNAL(closeController(Controller*)),
- m_pControllerManager, SLOT(closeController(Controller*)));
- connect(this, SIGNAL(loadPreset(Controller*, ControllerPresetPointer)),
- m_pControllerManager, SLOT(loadPreset(Controller*, ControllerPresetPointer)));
+ connect(this,
+ &DlgPrefController::applyPreset,
+ m_pControllerManager,
+ &ControllerManager::slotApplyPreset);
// Open script file links
connect(m_ui.labelLoadedPresetScriptFileLinks,
&QLabel::linkActivated,
- [](const QString & path) {
- QDesktopServices::openUrl(QUrl::fromLocalFile(path)); });
+ [](const QString& path) {
+ QDesktopServices::openUrl(QUrl::fromLocalFile(path));
+ });
// Input mappings
- connect(m_ui.btnAddInputMapping, SIGNAL(clicked()),
- this, SLOT(addInputMapping()));
- connect(m_ui.btnRemoveInputMappings, SIGNAL(clicked()),
- this, SLOT(removeInputMappings()));
- connect(m_ui.btnLearningWizard, SIGNAL(clicked()),
- this, SLOT(showLearningWizard()));
- connect(m_ui.btnClearAllInputMappings, SIGNAL(clicked()),
- this, SLOT(clearAllInputMappings()));
+ connect(m_ui.btnAddInputMapping,
+ SIGNAL(clicked()),
+ this,
+ SLOT(addInputMapping()));
+ connect(m_ui.btnRemoveInputMappings,
+ SIGNAL(clicked()),
+ this,
+ SLOT(removeInputMappings()));
+ connect(m_ui.btnLearningWizard,
+ SIGNAL(clicked()),
+ this,
+ SLOT(showLearningWizard()));
+ connect(m_ui.btnClearAllInputMappings,
+ SIGNAL(clicked()),
+ this,
+ SLOT(clearAllInputMappings()));
// Output mappings
- connect(m_ui.btnAddOutputMapping, SIGNAL(clicked()),
- this, SLOT(addOutputMapping()));
- connect(m_ui.btnRemoveOutputMappings, SIGNAL(clicked()),
- this, SLOT(removeOutputMappings()));
- connect(m_ui.btnClearAllOutputMappings, SIGNAL(clicked()),
- this, SLOT(clearAllOutputMappings()));
+ connect(m_ui.btnAddOutputMapping,
+ SIGNAL(clicked()),
+ this,
+ SLOT(addOutputMapping()));
+ connect(m_ui.btnRemoveOutputMappings,
+ SIGNAL(clicked()),
+ this,
+ SLOT(removeOutputMappings()));
+ connect(m_ui.btnClearAllOutputMappings,
+ SIGNAL(clicked()),
+ this,
+ SLOT(clearAllOutputMappings()));
}
DlgPrefController::~DlgPrefController() {
@@ -107,13 +117,14 @@ void DlgPrefController::showLearningWizard() {
// learning dialog. If we don't apply the settings first and open the
// device, the dialog won't react to controller messages.
if (m_ui.chkEnabledDevice->isChecked() && !m_pController->isOpen()) {
- QMessageBox::StandardButton result = QMessageBox::question(
- this,
- tr("Apply device settings?"),
- tr("Your settings must be applied before starting the learning wizard.\n"
- "Apply settings and continue?"),
- QMessageBox::Ok | QMessageBox::Cancel, // Buttons to be displayed
- QMessageBox::Ok); // Default button
+ QMessageBox::StandardButton result = QMessageBox::question(this,
+ tr("Apply device settings?"),
+ tr("Your settings must be applied before starting the learning "
+ "wizard.\n"
+ "Apply settings and continue?"),
+ QMessageBox::Ok |
+ QMessageBox::Cancel, // Buttons to be displayed
+ QMessageBox::Ok); // Default button
// Stop if the user has not pressed the Ok button,
// which could be the Cancel or the Close Button.
if (result != QMessageBox::Ok) {
@@ -122,9 +133,6 @@ void DlgPrefController::showLearningWizard() {
}
slotApply();
- // After this point we consider the mapping wizard as dirtying the preset.
- slotDirty();
-
// Note that DlgControllerLearning is set to delete itself on close using
// the Qt::WA_DeleteOnClose attribute (so this "new" doesn't leak memory)
m_pDlgControllerLearning = new DlgControllerLearning(this, m_pController);
@@ -132,23 +140,36 @@ void DlgPrefController::showLearningWizard() {
ControllerLearningEventFilter* pControllerLearning =
m_pControllerManager->getControllerLearningEventFilter();
pControllerLearning->startListening();
- connect(pControllerLearning, SIGNAL(controlClicked(ControlObject*)),
- m_pDlgControllerLearning, SLOT(controlClicked(ControlObject*)));
- connect(m_pDlgControllerLearning, SIGNAL(listenForClicks()),
- pControllerLearning, SLOT(startListening()));
- connect(m_pDlgControllerLearning, SIGNAL(stopListeningForClicks()),
- pControllerLearning, SLOT(stopListening()));
- connect(m_pDlgControllerLearning, SIGNAL(stopLearning()),
- this, SLOT(show()));
- connect(m_pDlgControllerLearning, SIGNAL(inputMappingsLearned(MidiInputMappings)),
- this, SLOT(midiInputMappingsLearned(MidiInputMappings)));
+ connect(pControllerLearning,
+ SIGNAL(controlClicked(ControlObject*)),
+ m_pDlgControllerLearning,
+ SLOT(controlClicked(ControlObject*)));
+ connect(m_pDlgControllerLearning,
+ SIGNAL(listenForClicks()),
+ pControllerLearning,
+ SLOT(startListening()));
+ connect(m_pDlgControllerLearning,
+ SIGNAL(stopListeningForClicks()),
+ pControllerLearning,
+ SLOT(stopListening()));
+ connect(m_pDlgControllerLearning,
+ SIGNAL(stopLearning()),
+ this,
+ SLOT(show()));
+ connect(m_pDlgControllerLearning,
+ SIGNAL(inputMappingsLearned(MidiInputMappings)),
+ this,
+ SLOT(midiInputMappingsLearned(MidiInputMappings)));
emit mappingStarted();
- connect(m_pDlgControllerLearning, SIGNAL(stopLearning()),
- this, SIGNAL(mappingEnded()));
+ connect(m_pDlgControllerLearning,
+ SIGNAL(stopLearning()),
+ this,
+ SIGNAL(mappingEnded()));
}
-void DlgPrefController::midiInputMappingsLearned(const MidiInputMappings& mappings) {
+void DlgPrefController::midiInputMappingsLearned(
+ const MidiInputMappings& mappings) {
// This is just a shortcut since doing a round-trip from Learning ->
// Controller -> slotPresetLoaded -> setPreset is too heavyweight.
if (m_pInputTableModel != NULL) {
@@ -156,7 +177,8 @@ void DlgPrefController::midiInputMappingsLearned(const MidiInputMappings& mappin
}
}
-QString DlgPrefController::presetShortName(const ControllerPresetPointer pPreset) const {
+QString DlgPrefController::presetShortName(
+ const ControllerPresetPointer pPreset) const {
QString presetName = tr("None");
if (pPreset) {
QString name = pPreset->name();
@@ -173,7 +195,8 @@ QString DlgPrefController::presetShortName(const ControllerPresetPointer pPreset
return presetName;
}
-QString DlgPrefController::presetName(const ControllerPresetPointer pPreset) const {
+QString DlgPrefController::presetName(
+ const ControllerPresetPointer pPreset) const {
if (pPreset) {
QString name = pPreset->name();
if (name.length() > 0)
@@ -182,7 +205,8 @@ QString DlgPrefController::presetName(const ControllerPresetPointer pPreset) con
return tr("No Name");
}
-QString DlgPrefController::presetDescription(const ControllerPresetPointer pPreset) const {
+QString DlgPrefController::presetDescription(
+ const ControllerPresetPointer pPreset) const {
if (pPreset) {
QString description = pPreset->description();
if (description.length() > 0)
@@ -191,7 +215,8 @@ QString DlgPrefController::presetDescription(const ControllerPresetPointer pPres
return tr("No Description");
}
-QString DlgPrefController::presetAuthor(const ControllerPresetPointer pPreset) const {
+QString DlgPrefController::presetAuthor(
+ const ControllerPresetPointer pPreset) const {
if (pPreset) {
QString author = pPreset->author();
if (author.length() > 0)
@@ -200,7 +225,8 @@ QString DlgPrefController::presetAuthor(const ControllerPresetPointer pPreset) c
return tr("No Author");
}
-QString DlgPrefController::presetForumLink(const ControllerPresetPointer pPreset) const {
+QString DlgPrefController::presetForumLink(
+ const ControllerPresetPointer pPreset) const {
QString url;
if (pPreset) {
QString link = pPreset->forumlink();
@@ -210,7 +236,8 @@ QString DlgPrefController::presetForumLink(const ControllerPresetPointer pPreset
return url;
}
-QString DlgPrefController::presetWikiLink(const ControllerPresetPointer pPreset) const {
+QString DlgPrefController::presetWikiLink(
+ const ControllerPresetPointer pPreset) const {
QString url;
if (pPreset) {
QString link = pPreset->wikilink();
@@ -220,74 +247,112 @@ QString DlgPrefController::presetWikiLink(const ControllerPresetPointer pPreset)
return url;
}
-QString DlgPrefController::presetScriptFileLinks(const ControllerPresetPointer pPreset) const {
- QString scriptFileLinks;
+QString DlgPrefController::presetScriptFileLinks(
+ const ControllerPresetPointer pPreset) const {
+ if (!pPreset || pPreset->getScriptFiles().empty()) {
+ return tr("No Scripts");
+ }
- if (pPreset) {
- QList presetDirs;
- presetDirs.append(userPresetsPath(m_pConfig));
- presetDirs.append(resourcePresetsPath(m_pConfig));
- QStringList linkList;
- for (QList::iterator it =
- pPreset->scripts.begin(); it != pPreset->scripts.end(); ++it) {
- QString name = it->name;
- QString path = ControllerManager::getAbsolutePath(
- name, presetDirs);
- QString scriptFileLink = "" + name + "";
- linkList << scriptFileLink;
+ QString systemPresetPath = resourcePresetsPath(m_pConfig);
+ QStringList linkList;
+ for (const auto& script : pPreset->getScriptFiles()) {
+ QString scriptFileLink = QStringLiteral("") +
+ script.name + QStringLiteral("");
+
+ if (!script.file.exists()) {
+ scriptFileLink +=
+ QStringLiteral(" (") + tr("missing") + QStringLiteral(")");
+ } else if (script.file.absoluteFilePath().startsWith(
+ systemPresetPath)) {
+ scriptFileLink +=
+ QStringLiteral(" (") + tr("built-in") + QStringLiteral(")");
}
- scriptFileLinks = linkList.join("
");
- }
- return scriptFileLinks;
-}
-void DlgPrefController::slotDirty() {
- m_bDirty = true;
+ linkList << scriptFileLink;
+ }
+ return linkList.join("
");
}
-void DlgPrefController::enumeratePresets() {
+void DlgPrefController::enumeratePresets(const QString& selectedPresetPath) {
m_ui.comboBoxPreset->clear();
// qDebug() << "Enumerating presets for controller" << m_pController->getName();
- // Insert a dummy "..." item at the top to try to make it less confusing.
+ // Insert a dummy item at the top to try to make it less confusing.
// (We don't want the first found file showing up as the default item when a
// user has their controller plugged in)
- m_ui.comboBoxPreset->addItem("...");
+ QIcon noPresetIcon(":/images/ic_none.svg");
+ m_ui.comboBoxPreset->addItem(noPresetIcon, "No Preset");
- // Ask the controller manager for a list of applicable presets
- QSharedPointer pie =
- m_pControllerManager->getMainThreadPresetEnumerator();
+ PresetInfo match;
+ // Enumerate user presets
+ QIcon userPresetIcon(":/images/ic_custom.svg");
- // Not ready yet. Should be rare. We will re-enumerate on the next open of
- // the preferences.
- if (pie.isNull()) {
- return;
+ // Reload user presets to detect added, changed or removed mappings
+ m_pControllerManager->getMainThreadUserPresetEnumerator()->loadSupportedPresets();
+
+ PresetInfo userPresetsMatch = enumeratePresetsFromEnumerator(
+ m_pControllerManager->getMainThreadUserPresetEnumerator(),
+ userPresetIcon);
+ if (userPresetsMatch.isValid()) {
+ match = userPresetsMatch;
}
- // Making the list of presets in the alphabetical order
- QList presets = pie->getPresetsByExtension(
- m_pController->presetExtension());
+ // Insert a separator between user presets (+ dummy item) and system presets
+ m_ui.comboBoxPreset->insertSeparator(m_ui.comboBoxPreset->count());
- PresetInfo match;
- for (const PresetInfo& preset : presets) {
- m_ui.comboBoxPreset->addItem(preset.getName(), preset.getPath());
- if (m_pController->matchPreset(preset)) {
- match = preset;
- }
+ // Enumerate system presets
+ QIcon systemPresetIcon(":/images/ic_mixxx_symbolic.svg");
+ PresetInfo systemPresetsMatch = enumeratePresetsFromEnumerator(
+ m_pControllerManager->getMainThreadSystemPresetEnumerator(),
+ systemPresetIcon);
+ if (systemPresetsMatch.isValid()) {
+ match = systemPresetsMatch;
+ }
+
+ // Preselect configured or matching preset
+ int index = -1;
+ if (!selectedPresetPath.isEmpty()) {
+ index = m_ui.comboBoxPreset->findData(selectedPresetPath);
+ } else if (match.isValid()) {
+ index = m_ui.comboBoxPreset->findText(match.getName());
}
+ if (index == -1) {
+ m_ui.chkEnabledDevice->setEnabled(false);
+ } else {
+ m_ui.comboBoxPreset->setCurrentIndex(index);
+ m_ui.chkEnabledDevice->setEnabled(true);
+ }
+}
+
+PresetInfo DlgPrefController::enumeratePresetsFromEnumerator(
+ QSharedPointer pPresetEnumerator, QIcon icon) {
+ PresetInfo match;
- // Jump to matching device in list if it was found.
- if (match.isValid()) {
- int index = m_ui.comboBoxPreset->findText(match.getName());
- if (index != -1) {
- m_ui.comboBoxPreset->setCurrentIndex(index);
+ // Check if enumerator is ready. Should be rare that it isn't. We will
+ // re-enumerate on the next open of the preferences.
+ if (!pPresetEnumerator.isNull()) {
+ // Get a list of presets in alphabetical order
+ QList systemPresets =
+ pPresetEnumerator->getPresetsByExtension(
+ m_pController->presetExtension());
+
+ for (const PresetInfo& preset : systemPresets) {
+ m_ui.comboBoxPreset->addItem(
+ icon, preset.getName(), preset.getPath());
+ if (m_pController->matchPreset(preset)) {
+ match = preset;
+ }
}
}
+
+ return match;
}
void DlgPrefController::slotUpdate() {
- enumeratePresets();
+ enumeratePresets(m_pControllerManager->getConfiguredPresetFileForDevice(
+ m_pController->getName()));
// Check if the controller is open.
bool deviceOpen = m_pController->isOpen();
@@ -303,94 +368,136 @@ void DlgPrefController::slotUpdate() {
}
void DlgPrefController::slotCancel() {
- if (m_pInputTableModel != NULL) {
- m_pInputTableModel->cancel();
+ slotShowPreset(m_pController->getPreset());
+}
+
+void DlgPrefController::applyPresetChanges() {
+ if (m_pInputTableModel) {
+ m_pInputTableModel->apply();
}
- if (m_pOutputTableModel != NULL) {
- m_pOutputTableModel->cancel();
+ if (m_pOutputTableModel) {
+ m_pOutputTableModel->apply();
}
}
void DlgPrefController::slotApply() {
- if (m_bDirty) {
- // Apply the presets and load the resulting preset.
- if (m_pInputTableModel != NULL) {
- m_pInputTableModel->apply();
- }
+ applyPresetChanges();
- if (m_pOutputTableModel != NULL) {
- m_pOutputTableModel->apply();
- }
-
- // Load the resulting preset (which has been mutated by the input/output
- // table models). The controller clones the preset so we aren't touching
- // the same preset.
- emit loadPreset(m_pController, m_pPreset);
+ // If no changes were made, do nothing
+ if (!(isDirty() || (m_pPreset && m_pPreset->isDirty()))) {
+ return;
+ }
- //Select the "..." item again in the combobox.
- m_ui.comboBoxPreset->setCurrentIndex(0);
+ bool bEnabled = false;
+ if (m_pPreset) {
+ bEnabled = m_ui.chkEnabledDevice->isChecked();
- bool wantEnabled = m_ui.chkEnabledDevice->isChecked();
- bool enabled = m_pController->isOpen();
- if (wantEnabled && !enabled) {
- enableDevice();
- } else if (!wantEnabled && enabled) {
- disableDevice();
+ if (m_pPreset->isDirty()) {
+ savePreset();
}
+ }
+ m_ui.chkEnabledDevice->setChecked(bEnabled);
- m_bDirty = false;
+ // The shouldn't be dirty at this pint because we already tried to save
+ // it. If that failed, don't apply the preset.
+ if (m_pPreset && m_pPreset->isDirty()) {
+ return;
}
+
+ // Load the resulting preset (which has been mutated by the input/output
+ // table models). The controller clones the preset so we aren't touching
+ // the same preset.
+ emit applyPreset(m_pController, m_pPreset, bEnabled);
+
+ // Mark the dialog as not dirty
+ setDirty(false);
}
-void DlgPrefController::slotLoadPreset(int chosenIndex) {
+void DlgPrefController::slotPresetSelected(int chosenIndex) {
+ QString presetPath;
if (chosenIndex == 0) {
- // User picked ...
- return;
+ // User picked "No Preset" item
+ m_ui.chkEnabledDevice->setEnabled(false);
+
+ if (m_ui.chkEnabledDevice->isChecked()) {
+ m_ui.chkEnabledDevice->setChecked(false);
+ setDirty(true);
+ }
+ } else {
+ // User picked a preset
+ m_ui.chkEnabledDevice->setEnabled(true);
+
+ if (!m_ui.chkEnabledDevice->isChecked()) {
+ m_ui.chkEnabledDevice->setChecked(true);
+ setDirty(true);
+ }
+
+ presetPath = m_ui.comboBoxPreset->itemData(chosenIndex).toString();
}
- const QString presetPath = m_ui.comboBoxPreset->itemData(chosenIndex).toString();
- // When loading the preset, we only want to load from the same dir as the
- // preset itself, otherwise when loading from the system-wide dir we'll
- // start the search in the user's dir find the existing script,
- // and do nothing.
- const QFileInfo presetFileInfo(presetPath);
- QList presetDirs;
- presetDirs.append(presetFileInfo.canonicalPath());
+ // Check if the preset is different from the configured preset
+ if (m_pControllerManager->getConfiguredPresetFileForDevice(
+ m_pController->getName()) != presetPath) {
+ setDirty(true);
+ }
+
+ applyPresetChanges();
+ if (m_pPreset && m_pPreset->isDirty()) {
+ if (QMessageBox::question(this,
+ tr("Preset has been edited"),
+ tr("Do you want to save the changes?")) ==
+ QMessageBox::Yes) {
+ savePreset();
+ }
+ }
ControllerPresetPointer pPreset = ControllerPresetFileHandler::loadPreset(
- presetPath, ControllerManager::getPresetPaths(m_pConfig));
+ presetPath, QDir(resourcePresetsPath(m_pConfig)));
- if (!pPreset) {
+ if (pPreset) {
+ DEBUG_ASSERT(!pPreset->isDirty());
+ }
+
+ slotShowPreset(pPreset);
+}
+
+void DlgPrefController::savePreset() {
+ VERIFY_OR_DEBUG_ASSERT(m_pPreset) {
return;
}
- // Import the preset scripts to the user scripts folder.
- for (QList::iterator it =
- pPreset->scripts.begin(); it != pPreset->scripts.end(); ++it) {
- // No need to import builtin scripts.
- if (it->builtin) {
- continue;
- }
+ if (!m_pPreset->isDirty()) {
+ qDebug() << "Preset is not dirty, no need to save it.";
+ return;
+ }
- QString scriptPath = ControllerManager::getAbsolutePath(
- it->name, presetDirs);
+ QFileInfo fileInfo(m_pPreset->filePath());
+ QString fileName = fileInfo.fileName();
+ // Add " (edited)" to preset name (if it's not already present)
+ QString editedSuffix = QStringLiteral(" (") + tr("edited") + QStringLiteral(")");
+ if (!m_pPreset->name().endsWith(editedSuffix)) {
+ m_pPreset->setName(m_pPreset->name() + editedSuffix);
+ qDebug() << "Renamed preset to " << m_pPreset->name();
- QString importedScriptFileName;
- // If a conflict exists then importScript will provide a new filename to
- // use. If importing fails then load the preset anyway without the
- // import.
- if (m_pControllerManager->importScript(scriptPath, &importedScriptFileName)) {
- it->name = importedScriptFileName;
+ // Add " (edited)" to file name (if it's not already present)
+ QString baseName = fileInfo.baseName();
+ if (baseName.endsWith(editedSuffix)) {
+ baseName.chop(editedSuffix.size());
}
+ fileName = baseName + editedSuffix + QStringLiteral(".") + fileInfo.completeSuffix();
+ }
+ QString filePath = QDir(userPresetsPath(m_pConfig)).absoluteFilePath(fileName);
+
+ if (!m_pPreset->savePreset(filePath)) {
+ qDebug() << "Failed to save preset!";
}
- // TODO(rryan): We really should not load the preset here. We should load it
- // into the preferences GUI and then load it to the actual controller once
- // the user hits apply.
- emit loadPreset(m_pController, pPreset);
- slotDirty();
+ m_pPreset->setFilePath(filePath);
+ m_pPreset->setDirty(false);
+
+ enumeratePresets(m_pPreset->filePath());
}
void DlgPrefController::initTableView(QTableView* pTable) {
@@ -413,7 +520,7 @@ void DlgPrefController::initTableView(QTableView* pTable) {
pTable->setAlternatingRowColors(true);
}
-void DlgPrefController::slotPresetLoaded(ControllerPresetPointer preset) {
+void DlgPrefController::slotShowPreset(ControllerPresetPointer preset) {
m_ui.labelLoadedPreset->setText(presetName(preset));
m_ui.labelLoadedPresetDescription->setText(presetDescription(preset));
m_ui.labelLoadedPresetAuthor->setText(presetAuthor(preset));
@@ -449,13 +556,6 @@ void DlgPrefController::slotPresetLoaded(ControllerPresetPointer preset) {
ControllerInputMappingTableModel* pInputModel =
new ControllerInputMappingTableModel(this);
- // If the model reports changes, mark ourselves as dirty.
- connect(pInputModel, SIGNAL(dataChanged(QModelIndex, QModelIndex)),
- this, SLOT(slotDirty()));
- connect(pInputModel, SIGNAL(rowsInserted(QModelIndex, int, int)),
- this, SLOT(slotDirty()));
- connect(pInputModel, SIGNAL(rowsRemoved(QModelIndex, int, int)),
- this, SLOT(slotDirty()));
pInputModel->setPreset(preset);
QSortFilterProxyModel* pInputProxyModel = new QSortFilterProxyModel(this);
@@ -503,23 +603,6 @@ void DlgPrefController::slotPresetLoaded(ControllerPresetPointer preset) {
m_pOutputTableModel = pOutputModel;
}
-void DlgPrefController::slotEnableDevice(bool enable) {
- slotDirty();
-
- // Set tree item text to normal/bold.
- emit controllerEnabled(this, enable);
-}
-
-void DlgPrefController::enableDevice() {
- emit openController(m_pController);
- //TODO: Should probably check if open() actually succeeded.
-}
-
-void DlgPrefController::disableDevice() {
- emit closeController(m_pController);
- //TODO: Should probably check if close() actually succeeded.
-}
-
void DlgPrefController::addInputMapping() {
if (m_pInputTableModel) {
m_pInputTableModel->addEmptyMapping();
@@ -532,7 +615,6 @@ void DlgPrefController::addInputMapping() {
m_ui.m_pInputMappingTableView->selectionModel()->select(
QItemSelection(left, right), QItemSelectionModel::Clear | QItemSelectionModel::Select);
m_ui.m_pInputMappingTableView->scrollTo(left);
- slotDirty();
}
}
@@ -543,7 +625,6 @@ void DlgPrefController::removeInputMappings() {
QModelIndexList selectedIndices = selection.indexes();
if (selectedIndices.size() > 0 && m_pInputTableModel) {
m_pInputTableModel->removeMappings(selectedIndices);
- slotDirty();
}
}
}
@@ -557,7 +638,6 @@ void DlgPrefController::clearAllInputMappings() {
}
if (m_pInputTableModel) {
m_pInputTableModel->clear();
- slotDirty();
}
}
@@ -573,7 +653,6 @@ void DlgPrefController::addOutputMapping() {
m_ui.m_pOutputMappingTableView->selectionModel()->select(
QItemSelection(left, right), QItemSelectionModel::Clear | QItemSelectionModel::Select);
m_ui.m_pOutputMappingTableView->scrollTo(left);
- slotDirty();
}
}
@@ -584,7 +663,6 @@ void DlgPrefController::removeOutputMappings() {
QModelIndexList selectedIndices = selection.indexes();
if (selectedIndices.size() > 0 && m_pOutputTableModel) {
m_pOutputTableModel->removeMappings(selectedIndices);
- slotDirty();
}
}
}
@@ -598,6 +676,5 @@ void DlgPrefController::clearAllOutputMappings() {
}
if (m_pOutputTableModel) {
m_pOutputTableModel->clear();
- slotDirty();
}
}
diff --git a/src/controllers/dlgprefcontroller.h b/src/controllers/dlgprefcontroller.h
index 58a0c81849d..45774383310 100644
--- a/src/controllers/dlgprefcontroller.h
+++ b/src/controllers/dlgprefcontroller.h
@@ -1,9 +1,7 @@
-/**
-* @file dlgprefcontroller.h
-* @author Sean M. Pappalardo spappalardo@mixxx.org
-* @date Mon May 2 2011
-* @brief Configuration dialog for a DJ controller
-*/
+/// @file dlgprefcontroller.h
+/// @author Sean M. Pappalardo spappalardo@mixxx.org
+/// @date Mon May 2 2011
+/// @brief Configuration dialog for a single DJ controller
#ifndef DLGPREFCONTROLLER_H
#define DLGPREFCONTROLLER_H
@@ -23,6 +21,7 @@
// Forward declarations
class Controller;
class ControllerManager;
+class PresetInfoEnumerator;
class DlgPrefController : public DlgPreferencePage {
Q_OBJECT
@@ -39,24 +38,18 @@ class DlgPrefController : public DlgPreferencePage {
void slotCancel();
// Called when preference dialog (not this dialog) is displayed.
void slotUpdate();
- // Called when the user toggles the enabled checkbox.
- void slotEnableDevice(bool enable);
- // Called when the user selects a preset from the combobox.
- void slotLoadPreset(int index);
- // Mark that we need to apply the settings.
- void slotDirty();
signals:
- void controllerEnabled(DlgPrefController*, bool);
- void openController(Controller* pController);
- void closeController(Controller* pController);
- void loadPreset(Controller* pController, QString controllerName);
- void loadPreset(Controller* pController, ControllerPresetPointer pPreset);
+ void applyPreset(Controller* pController, ControllerPresetPointer pPreset, bool bEnabled);
void mappingStarted();
void mappingEnded();
private slots:
- void slotPresetLoaded(ControllerPresetPointer preset);
+ /// Called when the user selects another preset in the combobox
+ void slotPresetSelected(int index);
+ /// Used to selected the current preset in the combobox and display the
+ /// preset information.
+ void slotShowPreset(ControllerPresetPointer preset);
// Input mappings
void addInputMapping();
@@ -79,11 +72,35 @@ class DlgPrefController : public DlgPreferencePage {
QString presetForumLink(const ControllerPresetPointer pPreset) const;
QString presetWikiLink(const ControllerPresetPointer pPreset) const;
QString presetScriptFileLinks(const ControllerPresetPointer pPreset) const;
- void savePreset(QString path);
+ void applyPresetChanges();
+ void savePreset();
void initTableView(QTableView* pTable);
- // Reload the mappings in the dropdown dialog
- void enumeratePresets();
+ /// Set dirty state (i.e. changes have been made).
+ ///
+ /// When this preferences page is marked as "dirty", changes have occured
+ /// that can be applied or discarded.
+ ///
+ /// @param bDirty The new dialog's dirty state.
+ void setDirty(bool bDirty) {
+ m_bDirty = bDirty;
+ }
+
+ /// Set dirty state (i.e. changes have been made).
+ ///
+ /// When this preferences page is marked as "dirty", changes have occured
+ /// that can be applied or discarded.
+ ///
+ /// @param bDirty The new dialog's dirty state.
+ bool isDirty() {
+ return m_bDirty;
+ }
+
+ /// Reload the mappings in the dropdown dialog
+ void enumeratePresets(const QString& selectedPresetPath);
+ PresetInfo enumeratePresetsFromEnumerator(
+ QSharedPointer pPresetEnumerator,
+ QIcon icon = QIcon());
void enableDevice();
void disableDevice();
diff --git a/src/controllers/dlgprefcontrollers.cpp b/src/controllers/dlgprefcontrollers.cpp
index babf12c0887..3af59017f85 100644
--- a/src/controllers/dlgprefcontrollers.cpp
+++ b/src/controllers/dlgprefcontrollers.cpp
@@ -55,11 +55,6 @@ void DlgPrefControllers::slotApply() {
foreach (DlgPrefController* pControllerWindows, m_controllerWindows) {
pControllerWindows->slotApply();
}
-
- // Save all controller presets.
- // TODO(rryan): Get rid of this and make DlgPrefController do this for each
- // preset.
- m_pControllerManager->savePresets();
}
bool DlgPrefControllers::handleTreeItemClick(QTreeWidgetItem* clickedItem) {
@@ -116,8 +111,11 @@ void DlgPrefControllers::setupControllerWidgets() {
m_controllerWindows.append(controllerDlg);
m_pDlgPreferences->addPageWidget(controllerDlg);
- connect(controllerDlg, SIGNAL(controllerEnabled(DlgPrefController*, bool)),
- this, SLOT(slotHighlightDevice(DlgPrefController*, bool)));
+ connect(pController,
+ &Controller::openChanged,
+ [this, controllerDlg](bool bOpen) {
+ slotHighlightDevice(controllerDlg, bOpen);
+ });
QTreeWidgetItem * controllerWindowLink = new QTreeWidgetItem(QTreeWidgetItem::Type);
controllerWindowLink->setIcon(0, QIcon(":/images/preferences/ic_preferences_controllers.png"));
diff --git a/src/controllers/dlgprefcontrollers.h b/src/controllers/dlgprefcontrollers.h
index 9fbef1bc47e..5e704f211ca 100644
--- a/src/controllers/dlgprefcontrollers.h
+++ b/src/controllers/dlgprefcontrollers.h
@@ -1,5 +1,4 @@
-#ifndef DLGPREFCONTROLLERS_H
-#define DLGPREFCONTROLLERS_H
+#pragma once
#include
@@ -11,6 +10,10 @@ class DlgPreferences;
class DlgPrefController;
class ControllerManager;
+/// Controllers Overview in the preferences
+///
+/// This dialog allows selecting controllers for configuration.
+
class DlgPrefControllers : public DlgPreferencePage, public Ui::DlgPrefControllersDlg {
Q_OBJECT
public:
@@ -43,5 +46,3 @@ class DlgPrefControllers : public DlgPreferencePage, public Ui::DlgPrefControlle
QList m_controllerWindows;
QList m_controllerTreeItems;
};
-
-#endif /* DLGPREFCONTROLLERS_H */
diff --git a/src/controllers/hid/hidcontroller.cpp b/src/controllers/hid/hidcontroller.cpp
index ec118012088..08bff7ef126 100644
--- a/src/controllers/hid/hidcontroller.cpp
+++ b/src/controllers/hid/hidcontroller.cpp
@@ -101,11 +101,6 @@ void HidController::visit(const HidControllerPreset* preset) {
emit presetLoaded(getPreset());
}
-bool HidController::savePreset(const QString fileName) const {
- HidControllerPresetFileHandler handler;
- return handler.save(m_preset, getName(), fileName);
-}
-
bool HidController::matchPreset(const PresetInfo& preset) {
const QList& products = preset.getProducts();
for (const auto& product : products) {
diff --git a/src/controllers/hid/hidcontroller.h b/src/controllers/hid/hidcontroller.h
index f60843067a8..543684ee766 100644
--- a/src/controllers/hid/hidcontroller.h
+++ b/src/controllers/hid/hidcontroller.h
@@ -31,8 +31,6 @@ class HidController final : public Controller {
return ControllerPresetPointer(pClone);
}
- bool savePreset(const QString fileName) const override;
-
void visit(const MidiControllerPreset* preset) override;
void visit(const HidControllerPreset* preset) override;
diff --git a/src/controllers/hid/hidcontrollerpreset.cpp b/src/controllers/hid/hidcontrollerpreset.cpp
new file mode 100644
index 00000000000..5bcbd327255
--- /dev/null
+++ b/src/controllers/hid/hidcontrollerpreset.cpp
@@ -0,0 +1,34 @@
+/// @file hidcontrollerpreset.cpp
+/// @author Jan Holthuis holzhaus@mixxx.org
+/// @date Mon 8 Apr 2020
+/// @brief HID/Bulk Controller Preset
+///
+/// This class represents a HID or Bulk controller preset, containing the data
+/// elements that make it up.
+
+#include "controllers/hid/hidcontrollerpreset.h"
+
+#include "controllers/controllerpresetvisitor.h"
+#include "controllers/defs_controllers.h"
+#include "controllers/hid/hidcontrollerpresetfilehandler.h"
+
+bool HidControllerPreset::savePreset(const QString& fileName) const {
+ HidControllerPresetFileHandler handler;
+ return handler.save(*this, fileName);
+}
+
+void HidControllerPreset::accept(ControllerPresetVisitor* visitor) {
+ if (visitor) {
+ visitor->visit(this);
+ }
+}
+
+void HidControllerPreset::accept(ConstControllerPresetVisitor* visitor) const {
+ if (visitor) {
+ visitor->visit(this);
+ }
+}
+
+bool HidControllerPreset::isMappable() const {
+ return false;
+}
diff --git a/src/controllers/hid/hidcontrollerpreset.h b/src/controllers/hid/hidcontrollerpreset.h
index 4a66ea0554b..77cfc7f34b3 100644
--- a/src/controllers/hid/hidcontrollerpreset.h
+++ b/src/controllers/hid/hidcontrollerpreset.h
@@ -1,29 +1,21 @@
-#ifndef HIDCONTROLLERPRESET_H
-#define HIDCONTROLLERPRESET_H
+#pragma once
+/// @file hidcontrollerpreset.h
+/// @brief HID/Bulk Controller Preset
#include "controllers/controllerpreset.h"
#include "controllers/controllerpresetvisitor.h"
+#include "controllers/hid/hidcontrollerpresetfilehandler.h"
+/// This class represents a HID or Bulk controller preset, containing the data
+/// elements that make it up.
class HidControllerPreset : public ControllerPreset {
public:
HidControllerPreset() {}
virtual ~HidControllerPreset() {}
- virtual void accept(ControllerPresetVisitor* visitor) {
- if (visitor) {
- visitor->visit(this);
- }
- }
+ bool savePreset(const QString& fileName) const override;
- virtual void accept(ConstControllerPresetVisitor* visitor) const {
- if (visitor) {
- visitor->visit(this);
- }
- }
-
- virtual bool isMappable() const {
- return false;
- }
+ virtual void accept(ControllerPresetVisitor* visitor);
+ virtual void accept(ConstControllerPresetVisitor* visitor) const;
+ virtual bool isMappable() const;
};
-
-#endif /* HIDCONTROLLERPRESET_H */
diff --git a/src/controllers/hid/hidcontrollerpresetfilehandler.cpp b/src/controllers/hid/hidcontrollerpresetfilehandler.cpp
index c300103a6fd..253090ce9fe 100644
--- a/src/controllers/hid/hidcontrollerpresetfilehandler.cpp
+++ b/src/controllers/hid/hidcontrollerpresetfilehandler.cpp
@@ -1,25 +1,26 @@
#include "controllers/hid/hidcontrollerpresetfilehandler.h"
bool HidControllerPresetFileHandler::save(const HidControllerPreset& preset,
- const QString deviceName,
- const QString fileName) const {
- QDomDocument doc = buildRootWithScripts(preset, deviceName);
+ const QString& fileName) const {
+ QDomDocument doc = buildRootWithScripts(preset);
return writeDocument(doc, fileName);
}
-ControllerPresetPointer HidControllerPresetFileHandler::load(const QDomElement root,
- const QString deviceName) {
+ControllerPresetPointer HidControllerPresetFileHandler::load(const QDomElement& root,
+ const QString& filePath,
+ const QDir& systemPresetsPath) {
if (root.isNull()) {
return ControllerPresetPointer();
}
- QDomElement controller = getControllerNode(root, deviceName);
+ QDomElement controller = getControllerNode(root);
if (controller.isNull()) {
return ControllerPresetPointer();
}
HidControllerPreset* preset = new HidControllerPreset();
+ preset->setFilePath(filePath);
parsePresetInfo(root, preset);
- addScriptFilesToPreset(controller, preset);
+ addScriptFilesToPreset(controller, preset, systemPresetsPath);
return ControllerPresetPointer(preset);
}
diff --git a/src/controllers/hid/hidcontrollerpresetfilehandler.h b/src/controllers/hid/hidcontrollerpresetfilehandler.h
index 7e27fc0eafe..7f99f56481d 100644
--- a/src/controllers/hid/hidcontrollerpresetfilehandler.h
+++ b/src/controllers/hid/hidcontrollerpresetfilehandler.h
@@ -9,12 +9,12 @@ class HidControllerPresetFileHandler : public ControllerPresetFileHandler {
HidControllerPresetFileHandler() {};
virtual ~HidControllerPresetFileHandler() {};
- bool save(const HidControllerPreset& preset,
- const QString deviceName, const QString fileName) const;
+ bool save(const HidControllerPreset& preset, const QString& fileName) const;
private:
- virtual ControllerPresetPointer load(const QDomElement root,
- const QString deviceName);
+ virtual ControllerPresetPointer load(const QDomElement& root,
+ const QString& filePath,
+ const QDir& systemPresetsPath);
};
#endif /* HIDCONTROLLERPRESETFILEHANDLER_H */
diff --git a/src/controllers/midi/midicontroller.cpp b/src/controllers/midi/midicontroller.cpp
index 877897f00f1..7f6c8ea5d6b 100644
--- a/src/controllers/midi/midicontroller.cpp
+++ b/src/controllers/midi/midicontroller.cpp
@@ -54,14 +54,9 @@ bool MidiController::matchPreset(const PresetInfo& preset) {
return false;
}
-bool MidiController::savePreset(const QString fileName) const {
- MidiControllerPresetFileHandler handler;
- return handler.save(m_preset, getName(), fileName);
-}
-
-bool MidiController::applyPreset(QList scriptPaths, bool initializeScripts) {
+bool MidiController::applyPreset(bool initializeScripts) {
// Handles the engine
- bool result = Controller::applyPreset(scriptPaths, initializeScripts);
+ bool result = Controller::applyPreset(initializeScripts);
// Only execute this code if this is an output device
if (isOutputDevice()) {
@@ -75,11 +70,11 @@ bool MidiController::applyPreset(QList scriptPaths, bool initializeScri
}
void MidiController::createOutputHandlers() {
- if (m_preset.outputMappings.isEmpty()) {
+ if (m_preset.getOutputMappings().isEmpty()) {
return;
}
- QHashIterator outIt(m_preset.outputMappings);
+ QHashIterator outIt(m_preset.getOutputMappings());
QStringList failures;
while (outIt.hasNext()) {
outIt.next();
@@ -185,15 +180,19 @@ void MidiController::clearTemporaryInputMappings() {
void MidiController::commitTemporaryInputMappings() {
// We want to replace duplicates that exist in m_preset but allow duplicates
// in m_temporaryInputMappings. To do this, we first remove every key in
- // m_temporaryInputMappings from m_preset.inputMappings.
+ // m_temporaryInputMappings from m_preset's input mappings.
for (auto it = m_temporaryInputMappings.constBegin();
it != m_temporaryInputMappings.constEnd(); ++it) {
- m_preset.inputMappings.remove(it.key());
+ m_preset.removeInputMapping(it.key());
}
- // Now, we can just use unite since we manually removed the duplicates in
- // the original set.
- m_preset.inputMappings.unite(m_temporaryInputMappings);
+ // Now, we can just use add all mappings from m_temporaryInputMappings
+ // since we removed the duplicates in the original set.
+ for (auto it = m_temporaryInputMappings.constBegin();
+ it != m_temporaryInputMappings.constEnd();
+ ++it) {
+ m_preset.addInputMapping(it.key(), it.value());
+ }
m_temporaryInputMappings.clear();
}
@@ -219,8 +218,8 @@ void MidiController::receive(unsigned char status, unsigned char control,
}
}
- auto it = m_preset.inputMappings.constFind(mappingKey.key);
- for (; it != m_preset.inputMappings.constEnd() && it.key() == mappingKey.key; ++it) {
+ auto it = m_preset.getInputMappings().constFind(mappingKey.key);
+ for (; it != m_preset.getInputMappings().constEnd() && it.key() == mappingKey.key; ++it) {
processInputMapping(it.value(), status, control, value, timestamp);
}
}
@@ -470,8 +469,8 @@ void MidiController::receive(QByteArray data, mixxx::Duration timestamp) {
}
}
- auto it = m_preset.inputMappings.constFind(mappingKey.key);
- for (; it != m_preset.inputMappings.constEnd() && it.key() == mappingKey.key; ++it) {
+ auto it = m_preset.getInputMappings().constFind(mappingKey.key);
+ for (; it != m_preset.getInputMappings().constEnd() && it.key() == mappingKey.key; ++it) {
processInputMapping(it.value(), data, timestamp);
}
}
diff --git a/src/controllers/midi/midicontroller.h b/src/controllers/midi/midicontroller.h
index a81ff7d680c..0fe825f5661 100644
--- a/src/controllers/midi/midicontroller.h
+++ b/src/controllers/midi/midicontroller.h
@@ -34,8 +34,6 @@ class MidiController : public Controller {
return ControllerPresetPointer(pClone);
}
- bool savePreset(const QString fileName) const override;
-
void visit(const MidiControllerPreset* preset) override;
void visit(const HidControllerPreset* preset) override;
@@ -59,9 +57,9 @@ class MidiController : public Controller {
Q_INVOKABLE virtual void sendShortMsg(unsigned char status,
unsigned char byte1, unsigned char byte2) = 0;
- // Alias for send()
- // The length parameter is here for backwards compatibility for when scripts
- // were required to specify it.
+ /// Alias for send()
+ /// The length parameter is here for backwards compatibility for when scripts
+ /// were required to specify it.
Q_INVOKABLE inline void sendSysexMsg(QList data, unsigned int length = 0) {
Q_UNUSED(length);
send(data);
@@ -75,8 +73,13 @@ class MidiController : public Controller {
int close() override;
private slots:
- // Initializes the engine and static output mappings.
- bool applyPreset(QList scriptPaths, bool initializeScripts) override;
+ /// Apply the preset to the controller.
+ /// @brief Initializes both controller engine and static output mappings.
+ ///
+ /// @param initializeScripts Can be set to false to skip script
+ /// initialization for unit tests.
+ /// @return Returns whether it was successful.
+ bool applyPreset(bool initializeScripts = false) override;
void learnTemporaryInputMappings(const MidiInputMappings& mappings);
void clearTemporaryInputMappings();
@@ -97,8 +100,8 @@ class MidiController : public Controller {
void updateAllOutputs();
void destroyOutputHandlers();
- // Returns a pointer to the currently loaded controller preset. For internal
- // use only.
+ /// Returns a pointer to the currently loaded controller preset. For internal
+ /// use only.
ControllerPreset* preset() override {
return &m_preset;
}
diff --git a/src/controllers/midi/midicontrollerpreset.cpp b/src/controllers/midi/midicontrollerpreset.cpp
new file mode 100644
index 00000000000..31d363349ac
--- /dev/null
+++ b/src/controllers/midi/midicontrollerpreset.cpp
@@ -0,0 +1,77 @@
+/// @file midicontrollerpreset.cpp
+/// @author Jan Holthuis holzhaus@mixxx.org
+/// @date Wed 8 Apr 2020
+/// @brief MIDI Controller Preset
+///
+/// This class represents a MIDI controller preset, containing the data elements
+/// that make it up.
+
+#include "controllers/midi/midicontrollerpreset.h"
+
+#include "controllers/defs_controllers.h"
+#include "controllers/midi/midicontrollerpresetfilehandler.h"
+
+bool MidiControllerPreset::savePreset(const QString& fileName) const {
+ MidiControllerPresetFileHandler handler;
+ return handler.save(*this, fileName);
+}
+
+void MidiControllerPreset::accept(ControllerPresetVisitor* visitor) {
+ if (visitor) {
+ visitor->visit(this);
+ }
+}
+
+void MidiControllerPreset::accept(ConstControllerPresetVisitor* visitor) const {
+ if (visitor) {
+ visitor->visit(this);
+ }
+}
+
+bool MidiControllerPreset::isMappable() const {
+ return true;
+}
+
+void MidiControllerPreset::addInputMapping(uint16_t key, MidiInputMapping mapping) {
+ m_inputMappings.insertMulti(key, mapping);
+ setDirty(true);
+}
+
+void MidiControllerPreset::removeInputMapping(uint16_t key) {
+ m_inputMappings.remove(key);
+ setDirty(true);
+}
+
+const QHash& MidiControllerPreset::getInputMappings() const {
+ return m_inputMappings;
+}
+
+void MidiControllerPreset::setInputMappings(const QHash& mappings) {
+ if (m_inputMappings != mappings) {
+ m_inputMappings.clear();
+ m_inputMappings.unite(mappings);
+ setDirty(true);
+ }
+}
+
+void MidiControllerPreset::addOutputMapping(ConfigKey key, MidiOutputMapping mapping) {
+ m_outputMappings.insertMulti(key, mapping);
+ setDirty(true);
+}
+
+void MidiControllerPreset::removeOutputMapping(ConfigKey key) {
+ m_outputMappings.remove(key);
+ setDirty(true);
+}
+
+const QHash& MidiControllerPreset::getOutputMappings() const {
+ return m_outputMappings;
+}
+
+void MidiControllerPreset::setOutputMappings(const QHash& mappings) {
+ if (m_outputMappings != mappings) {
+ m_outputMappings.clear();
+ m_outputMappings.unite(mappings);
+ setDirty(true);
+ }
+}
diff --git a/src/controllers/midi/midicontrollerpreset.h b/src/controllers/midi/midicontrollerpreset.h
index 12ef6f0a13d..530e1e34950 100644
--- a/src/controllers/midi/midicontrollerpreset.h
+++ b/src/controllers/midi/midicontrollerpreset.h
@@ -1,16 +1,8 @@
-/**
- * @file midicontrollerpreset.h
- * @author Sean Pappalardo spappalardo@mixxx.org
- * @date Mon 9 Apr 2012
- * @brief MIDI Controller preset
- *
- * This class represents a MIDI controller preset, containing the data elements
- * that make it up.
- *
- */
-
-#ifndef MIDICONTROLLERPRESET_H
-#define MIDICONTROLLERPRESET_H
+#pragma once
+/// @file midicontrollerpreset.h
+/// @author Sean Pappalardo spappalardo@mixxx.org
+/// @date Mon 9 Apr 2012
+/// @brief MIDI Controller preset
#include
@@ -18,30 +10,33 @@
#include "controllers/controllerpresetvisitor.h"
#include "controllers/midi/midimessage.h"
+/// This class represents a MIDI controller preset, containing the data elements
+/// that make it up.
class MidiControllerPreset : public ControllerPreset {
public:
- MidiControllerPreset() {}
- virtual ~MidiControllerPreset() {}
+ MidiControllerPreset(){};
+ virtual ~MidiControllerPreset(){};
- virtual void accept(ControllerPresetVisitor* visitor) {
- if (visitor) {
- visitor->visit(this);
- }
- }
+ bool savePreset(const QString& fileName) const override;
- virtual void accept(ConstControllerPresetVisitor* visitor) const {
- if (visitor) {
- visitor->visit(this);
- }
- }
+ virtual void accept(ControllerPresetVisitor* visitor);
+ virtual void accept(ConstControllerPresetVisitor* visitor) const;
+ virtual bool isMappable() const;
- virtual bool isMappable() const {
- return true;
- }
+ // Input mappings
+ void addInputMapping(uint16_t key, MidiInputMapping mapping);
+ void removeInputMapping(uint16_t key);
+ const QHash& getInputMappings() const;
+ void setInputMappings(const QHash& mappings);
+ // Output mappings
+ void addOutputMapping(ConfigKey key, MidiOutputMapping mapping);
+ void removeOutputMapping(ConfigKey key);
+ const QHash& getOutputMappings() const;
+ void setOutputMappings(const QHash& mappings);
+
+ private:
// MIDI input and output mappings.
- QHash inputMappings;
- QHash outputMappings;
+ QHash m_inputMappings;
+ QHash m_outputMappings;
};
-
-#endif
diff --git a/src/controllers/midi/midicontrollerpresetfilehandler.cpp b/src/controllers/midi/midicontrollerpresetfilehandler.cpp
index 32e01244768..fddafd929a7 100644
--- a/src/controllers/midi/midicontrollerpresetfilehandler.cpp
+++ b/src/controllers/midi/midicontrollerpresetfilehandler.cpp
@@ -14,22 +14,24 @@
#define DEFAULT_OUTPUT_ON 0x7F
#define DEFAULT_OUTPUT_OFF 0x00
-ControllerPresetPointer MidiControllerPresetFileHandler::load(const QDomElement root,
- const QString deviceName) {
+ControllerPresetPointer MidiControllerPresetFileHandler::load(const QDomElement& root,
+ const QString& filePath,
+ const QDir& systemPresetsPath) {
if (root.isNull()) {
return ControllerPresetPointer();
}
- QDomElement controller = getControllerNode(root, deviceName);
+ QDomElement controller = getControllerNode(root);
if (controller.isNull()) {
return ControllerPresetPointer();
}
MidiControllerPreset* preset = new MidiControllerPreset();
+ preset->setFilePath(filePath);
// Superclass handles parsing tag and script files
parsePresetInfo(root, preset);
- addScriptFilesToPreset(controller, preset);
+ addScriptFilesToPreset(controller, preset, systemPresetsPath);
QDomElement control = controller.firstChildElement("controls").firstChildElement("control");
@@ -101,7 +103,7 @@ ControllerPresetPointer MidiControllerPresetFileHandler::load(const QDomElement
// Use insertMulti because we support multiple inputs mappings for the
// same input MidiKey.
- preset->inputMappings.insertMulti(mapping.key.key, mapping);
+ preset->addInputMapping(mapping.key.key, mapping);
control = control.nextSiblingElement("control");
}
@@ -181,7 +183,7 @@ ControllerPresetPointer MidiControllerPresetFileHandler::load(const QDomElement
// Use insertMulti because we support multiple outputs from the same
// control.
- preset->outputMappings.insertMulti(mapping.controlKey, mapping);
+ preset->addOutputMapping(mapping.controlKey, mapping);
output = output.nextSiblingElement("output");
}
@@ -192,10 +194,9 @@ ControllerPresetPointer MidiControllerPresetFileHandler::load(const QDomElement
}
bool MidiControllerPresetFileHandler::save(const MidiControllerPreset& preset,
- const QString deviceName,
- const QString fileName) const {
- qDebug() << "Saving preset for" << deviceName << "to" << fileName;
- QDomDocument doc = buildRootWithScripts(preset, deviceName);
+ const QString& fileName) const {
+ qDebug() << "Saving preset" << preset.name() << "to" << fileName;
+ QDomDocument doc = buildRootWithScripts(preset);
addControlsToDocument(preset, &doc);
return writeDocument(doc, fileName);
}
@@ -209,11 +210,12 @@ void MidiControllerPresetFileHandler::addControlsToDocument(const MidiController
QDomElement controls = doc->createElement("controls");
// We will iterate over all of the values that have the same keys, so we need
// to remove duplicate keys or else we'll duplicate those values.
- auto sortedInputKeys = preset.inputMappings.uniqueKeys();
+ auto sortedInputKeys = preset.getInputMappings().uniqueKeys();
std::sort(sortedInputKeys.begin(), sortedInputKeys.end());
for (const auto& key : sortedInputKeys) {
- for (auto it = preset.inputMappings.constFind(key);
- it != preset.inputMappings.constEnd() && it.key() == key; ++it) {
+ for (auto it = preset.getInputMappings().constFind(key);
+ it != preset.getInputMappings().constEnd() && it.key() == key;
+ ++it) {
QDomElement controlNode = inputMappingToXML(doc, it.value());
controls.appendChild(controlNode);
}
@@ -223,11 +225,12 @@ void MidiControllerPresetFileHandler::addControlsToDocument(const MidiController
// Repeat the process for the output mappings.
QDomElement outputs = doc->createElement("outputs");
- auto sortedOutputKeys = preset.outputMappings.uniqueKeys();
+ auto sortedOutputKeys = preset.getOutputMappings().uniqueKeys();
std::sort(sortedOutputKeys.begin(), sortedOutputKeys.end());
for (const auto& key : sortedOutputKeys) {
- for (auto it = preset.outputMappings.constFind(key);
- it != preset.outputMappings.constEnd() && it.key() == key; ++it) {
+ for (auto it = preset.getOutputMappings().constFind(key);
+ it != preset.getOutputMappings().constEnd() && it.key() == key;
+ ++it) {
QDomElement outputNode = outputMappingToXML(doc, it.value());
outputs.appendChild(outputNode);
}
diff --git a/src/controllers/midi/midicontrollerpresetfilehandler.h b/src/controllers/midi/midicontrollerpresetfilehandler.h
index e43e850580f..ad7c43419e0 100644
--- a/src/controllers/midi/midicontrollerpresetfilehandler.h
+++ b/src/controllers/midi/midicontrollerpresetfilehandler.h
@@ -16,11 +16,12 @@ class MidiControllerPresetFileHandler : public ControllerPresetFileHandler {
MidiControllerPresetFileHandler() {};
virtual ~MidiControllerPresetFileHandler() {};
- bool save(const MidiControllerPreset& preset,
- const QString deviceName, const QString fileName) const;
+ bool save(const MidiControllerPreset& preset, const QString& fileName) const;
private:
- virtual ControllerPresetPointer load(const QDomElement root, const QString deviceName);
+ virtual ControllerPresetPointer load(const QDomElement& root,
+ const QString& filePath,
+ const QDir& systemPresetPath);
void addControlsToDocument(const MidiControllerPreset& preset,
QDomDocument* doc) const;
diff --git a/src/controllers/midi/midimessage.h b/src/controllers/midi/midimessage.h
index 5e04d07ef3e..8223dea32e8 100644
--- a/src/controllers/midi/midimessage.h
+++ b/src/controllers/midi/midimessage.h
@@ -160,11 +160,19 @@ struct MidiInputMapping {
control(control) {
}
- // Don't use descriptions in operator== since we only use equality testing
- // for unit tests.
+ MidiInputMapping(MidiKey key,
+ MidiOptions options,
+ const ConfigKey& control,
+ QString description)
+ : key(key),
+ options(options),
+ control(control),
+ description(description) {
+ }
+
bool operator==(const MidiInputMapping& other) const {
return key == other.key && options == other.options &&
- control == other.control;
+ control == other.control && description == other.description;
}
MidiKey key;
diff --git a/src/test/controller_preset_validation_test.cpp b/src/test/controller_preset_validation_test.cpp
index a1a65f4015f..415ca1920d8 100644
--- a/src/test/controller_preset_validation_test.cpp
+++ b/src/test/controller_preset_validation_test.cpp
@@ -35,11 +35,6 @@ class FakeController : public Controller {
}
}
- bool savePreset(const QString fileName) const override {
- Q_UNUSED(fileName);
- return true;
- }
-
void visit(const MidiControllerPreset* preset) override {
m_bMidiPreset = true;
m_bHidPreset = false;
@@ -122,14 +117,16 @@ FakeController::~FakeController() {
class ControllerPresetValidationTest : public MixxxTest {
protected:
void SetUp() override {
- m_presetPaths << QDir::currentPath() + "/res/controllers";
- m_pEnumerator.reset(new PresetInfoEnumerator(m_presetPaths));
+ m_presetPath = QDir::current();
+ m_presetPath.cd("res/controllers");
+ m_pEnumerator.reset(new PresetInfoEnumerator(QList{m_presetPath.absolutePath()}));
}
bool testLoadPreset(const PresetInfo& preset) {
ControllerPresetPointer pPreset =
- ControllerPresetFileHandler::loadPreset(preset.getPath(),
- m_presetPaths);
+ ControllerPresetFileHandler::loadPreset(
+ preset.getPath(),
+ m_presetPath);
if (pPreset.isNull()) {
return false;
}
@@ -139,13 +136,12 @@ class ControllerPresetValidationTest : public MixxxTest {
controller.startEngine();
controller.setPreset(*pPreset);
// Do not initialize the scripts.
- bool result = controller.applyPreset(m_presetPaths, false);
+ bool result = controller.applyPreset(false);
controller.stopEngine();
return result;
}
-
- QStringList m_presetPaths;
+ QDir m_presetPath;
QScopedPointer m_pEnumerator;
};
diff --git a/src/test/learningutilstest.cpp b/src/test/learningutilstest.cpp
index 664c88a8275..c6e2057503b 100644
--- a/src/test/learningutilstest.cpp
+++ b/src/test/learningutilstest.cpp
@@ -15,6 +15,20 @@ class LearningUtilsTest : public MixxxTest {
m_messages.append(qMakePair(MidiKey(status, control), value));
}
+ /// Check if mapping in present in mapping list.
+ /// Similar to MidiInputMappings::contains(const MidiInputMapping&), but
+ /// does not compare the description.
+ bool containsMapping(const MidiInputMappings& haystack, const MidiInputMapping& needle) {
+ for (const MidiInputMapping& mapping : haystack) {
+ if (mapping.key == needle.key &&
+ mapping.options == needle.options &&
+ mapping.control == needle.control) {
+ return true;
+ }
+ }
+ return false;
+ }
+
QList > m_messages;
};
@@ -29,8 +43,10 @@ TEST_F(LearningUtilsTest, NoteOnButton) {
ASSERT_EQ(1, mappings.size());
EXPECT_EQ(MidiInputMapping(MidiKey(MIDI_NOTE_ON | 0x01, 0x10),
- MidiOptions(), control),
- mappings.first());
+ MidiOptions(),
+ control,
+ mappings.first().description),
+ mappings.first());
}
TEST_F(LearningUtilsTest, NoteOnNoteOffButton) {
@@ -44,11 +60,15 @@ TEST_F(LearningUtilsTest, NoteOnNoteOffButton) {
ASSERT_EQ(2, mappings.size());
EXPECT_EQ(MidiInputMapping(MidiKey(MIDI_NOTE_ON | 0x01, 0x10),
- MidiOptions(), control),
- mappings.at(0));
+ MidiOptions(),
+ control,
+ mappings.at(0).description),
+ mappings.at(0));
EXPECT_EQ(MidiInputMapping(MidiKey(MIDI_NOTE_OFF | 0x01, 0x10),
- MidiOptions(), control),
- mappings.at(1));
+ MidiOptions(),
+ control,
+ mappings.at(1).description),
+ mappings.at(1));
}
TEST_F(LearningUtilsTest, CC7BitKnob) {
@@ -69,8 +89,10 @@ TEST_F(LearningUtilsTest, CC7BitKnob) {
ASSERT_EQ(1, mappings.size());
EXPECT_EQ(MidiInputMapping(MidiKey(MIDI_CC | 0x01, 0x10),
- MidiOptions(), control),
- mappings.at(0));
+ MidiOptions(),
+ control,
+ mappings.first().description),
+ mappings.first());
}
TEST_F(LearningUtilsTest, CC7BitKnob_CenterPointButton_NoteOn) {
@@ -103,10 +125,13 @@ TEST_F(LearningUtilsTest, CC7BitKnob_CenterPointButton_NoteOn) {
LearningUtils::guessMidiInputMappings(control, m_messages);
ASSERT_EQ(2, mappings.size());
- EXPECT_TRUE(mappings.contains(MidiInputMapping(MidiKey(MIDI_CC | 0x01, 0x10),
- MidiOptions(), control)));
- EXPECT_TRUE(mappings.contains(MidiInputMapping(MidiKey(MIDI_NOTE_ON | 0x01, 0xE0),
- MidiOptions(), resetControl)));
+ EXPECT_TRUE(containsMapping(mappings,
+ MidiInputMapping(
+ MidiKey(MIDI_CC | 0x01, 0x10), MidiOptions(), control)));
+ EXPECT_TRUE(containsMapping(mappings,
+ MidiInputMapping(MidiKey(MIDI_NOTE_ON | 0x01, 0xE0),
+ MidiOptions(),
+ resetControl)));
m_messages.clear();
@@ -124,10 +149,13 @@ TEST_F(LearningUtilsTest, CC7BitKnob_CenterPointButton_NoteOn) {
addMessage(MIDI_CC | 0x01, 0x10, 0x00);
ASSERT_EQ(2, mappings.size());
- EXPECT_TRUE(mappings.contains(MidiInputMapping(MidiKey(MIDI_CC | 0x01, 0x10),
- MidiOptions(), control)));
- EXPECT_TRUE(mappings.contains(MidiInputMapping(MidiKey(MIDI_NOTE_ON | 0x01, 0xE0),
- MidiOptions(), resetControl)));
+ EXPECT_TRUE(containsMapping(mappings,
+ MidiInputMapping(
+ MidiKey(MIDI_CC | 0x01, 0x10), MidiOptions(), control)));
+ EXPECT_TRUE(containsMapping(mappings,
+ MidiInputMapping(MidiKey(MIDI_NOTE_ON | 0x01, 0xE0),
+ MidiOptions(),
+ resetControl)));
}
TEST_F(LearningUtilsTest, CC14BitKnob_MSBFirst) {
@@ -167,12 +195,14 @@ TEST_F(LearningUtilsTest, CC14BitKnob_MSBFirst) {
ASSERT_EQ(2, mappings.size());
MidiOptions lsb_option;
lsb_option.fourteen_bit_lsb = true;
- EXPECT_TRUE(mappings.contains(MidiInputMapping(MidiKey(MIDI_CC | 0x01, 0x10),
- lsb_option, control)));
+ EXPECT_TRUE(containsMapping(mappings,
+ MidiInputMapping(
+ MidiKey(MIDI_CC | 0x01, 0x10), lsb_option, control)));
MidiOptions msb_option;
msb_option.fourteen_bit_msb = true;
- EXPECT_TRUE(mappings.contains(MidiInputMapping(MidiKey(MIDI_CC | 0x01, 0x11),
- msb_option, control)));
+ EXPECT_TRUE(containsMapping(mappings,
+ MidiInputMapping(
+ MidiKey(MIDI_CC | 0x01, 0x11), msb_option, control)));
}
TEST_F(LearningUtilsTest, CC14BitKnob_LSBFirst) {
@@ -212,12 +242,14 @@ TEST_F(LearningUtilsTest, CC14BitKnob_LSBFirst) {
ASSERT_EQ(2, mappings.size());
MidiOptions lsb_option;
lsb_option.fourteen_bit_lsb = true;
- EXPECT_TRUE(mappings.contains(MidiInputMapping(MidiKey(MIDI_CC | 0x01, 0x10),
- lsb_option, control)));
+ EXPECT_TRUE(containsMapping(mappings,
+ MidiInputMapping(
+ MidiKey(MIDI_CC | 0x01, 0x10), lsb_option, control)));
MidiOptions msb_option;
msb_option.fourteen_bit_msb = true;
- EXPECT_TRUE(mappings.contains(MidiInputMapping(MidiKey(MIDI_CC | 0x01, 0x11),
- msb_option, control)));
+ EXPECT_TRUE(containsMapping(mappings,
+ MidiInputMapping(
+ MidiKey(MIDI_CC | 0x01, 0x11), msb_option, control)));
}
TEST_F(LearningUtilsTest, CC7BitKnob_ConfusableForCC7BitTicker_Zeroes) {
@@ -236,8 +268,11 @@ TEST_F(LearningUtilsTest, CC7BitKnob_ConfusableForCC7BitTicker_Zeroes) {
MidiOptions options;
options.selectknob = true;
ASSERT_EQ(1, mappings.size());
- EXPECT_EQ(MidiInputMapping(MidiKey(MIDI_CC | 0x01, 0x10), options, control),
- mappings.at(0));
+ EXPECT_EQ(MidiInputMapping(MidiKey(MIDI_CC | 0x01, 0x10),
+ options,
+ control,
+ mappings.first().description),
+ mappings.first());
m_messages.clear();
@@ -252,8 +287,10 @@ TEST_F(LearningUtilsTest, CC7BitKnob_ConfusableForCC7BitTicker_Zeroes) {
ASSERT_EQ(1, mappings.size());
EXPECT_EQ(MidiInputMapping(MidiKey(MIDI_CC | 0x01, 0x10),
- MidiOptions(), control),
- mappings.at(0));
+ MidiOptions(),
+ control,
+ mappings.first().description),
+ mappings.first());
}
TEST_F(LearningUtilsTest, CC7BitKnob_ConfusableForCC7BitTicker_ZeroIncluded) {
@@ -285,8 +322,10 @@ TEST_F(LearningUtilsTest, CC7BitKnob_ConfusableForCC7BitTicker_ZeroIncluded) {
ASSERT_EQ(1, mappings.size());
EXPECT_EQ(MidiInputMapping(MidiKey(MIDI_CC | 0x01, 0x10),
- MidiOptions(), control),
- mappings.at(0));
+ MidiOptions(),
+ control,
+ mappings.first().description),
+ mappings.first());
}
TEST_F(LearningUtilsTest, CC7BitKnob_ConfusableForCC7BitTicker) {
@@ -312,11 +351,14 @@ TEST_F(LearningUtilsTest, CC7BitKnob_ConfusableForCC7BitTicker) {
ASSERT_EQ(1, mappings.size());
EXPECT_EQ(MidiInputMapping(MidiKey(MIDI_CC | 0x01, 0x10),
- MidiOptions(), control),
- mappings.at(0));
+ MidiOptions(),
+ control,
+ mappings.first().description),
+ mappings.first());
}
-TEST_F(LearningUtilsTest, CC7BitKnob_ConfusableForSpread64Ticker_StartAndStopOn41) {
+TEST_F(LearningUtilsTest,
+ CC7BitKnob_ConfusableForSpread64Ticker_StartAndStopOn41) {
// Moving a CC knob through its range multiple times is confusable for
// Spread64 select knobs when a 0x41 or 0x3F is repeated. If we start and
// stop on 0x41 (and don't pass through 0x40) then this can set off Spread64
@@ -342,8 +384,10 @@ TEST_F(LearningUtilsTest, CC7BitKnob_ConfusableForSpread64Ticker_StartAndStopOn4
ASSERT_EQ(1, mappings.size());
EXPECT_EQ(MidiInputMapping(MidiKey(MIDI_CC | 0x01, 0x10),
- MidiOptions(), control),
- mappings.at(0));
+ MidiOptions(),
+ control,
+ mappings.first().description),
+ mappings.first());
}
TEST_F(LearningUtilsTest, CC7BitKnob_ConfusableForSpread64Ticker_0x40Included) {
@@ -375,8 +419,10 @@ TEST_F(LearningUtilsTest, CC7BitKnob_ConfusableForSpread64Ticker_0x40Included) {
ASSERT_EQ(1, mappings.size());
EXPECT_EQ(MidiInputMapping(MidiKey(MIDI_CC | 0x01, 0x10),
- MidiOptions(), control),
- mappings.at(0));
+ MidiOptions(),
+ control,
+ mappings.first().description),
+ mappings.first());
}
TEST_F(LearningUtilsTest, CC7BitTicker) {
@@ -401,8 +447,10 @@ TEST_F(LearningUtilsTest, CC7BitTicker) {
MidiOptions options;
options.selectknob = true;
EXPECT_EQ(MidiInputMapping(MidiKey(MIDI_CC | 0x01, 0x10),
- options, control),
- mappings.at(0));
+ options,
+ control,
+ mappings.first().description),
+ mappings.first());
}
TEST_F(LearningUtilsTest, Spread64Ticker) {
@@ -425,8 +473,10 @@ TEST_F(LearningUtilsTest, Spread64Ticker) {
MidiOptions options;
options.spread64 = true;
EXPECT_EQ(MidiInputMapping(MidiKey(MIDI_CC | 0x01, 0x10),
- options, control),
- mappings.at(0));
+ options,
+ control,
+ mappings.first().description),
+ mappings.first());
}
TEST_F(LearningUtilsTest, CC7BitTicker_SingleDirection) {
@@ -452,8 +502,10 @@ TEST_F(LearningUtilsTest, CC7BitTicker_SingleDirection) {
LearningUtils::guessMidiInputMappings(control, m_messages);
ASSERT_EQ(1, mappings.size());
EXPECT_EQ(MidiInputMapping(MidiKey(MIDI_CC | 0x01, 0x10),
- options, control),
- mappings.at(0));
+ options,
+ control,
+ mappings.first().description),
+ mappings.first());
m_messages.clear();
@@ -466,8 +518,10 @@ TEST_F(LearningUtilsTest, CC7BitTicker_SingleDirection) {
mappings = LearningUtils::guessMidiInputMappings(control, m_messages);
ASSERT_EQ(1, mappings.size());
EXPECT_EQ(MidiInputMapping(MidiKey(MIDI_CC | 0x01, 0x10),
- options, control),
- mappings.at(0));
+ options,
+ control,
+ mappings.first().description),
+ mappings.first());
}
TEST_F(LearningUtilsTest, SingleMessageSwitchMode_NoteOn) {
@@ -485,8 +539,10 @@ TEST_F(LearningUtilsTest, SingleMessageSwitchMode_NoteOn) {
MidiOptions options;
options.sw = true;
EXPECT_EQ(MidiInputMapping(MidiKey(MIDI_NOTE_ON | 0x01, 0x10),
- options, control),
- mappings.at(0));
+ options,
+ control,
+ mappings.first().description),
+ mappings.first());
m_messages.clear();
@@ -497,8 +553,10 @@ TEST_F(LearningUtilsTest, SingleMessageSwitchMode_NoteOn) {
ASSERT_EQ(1, mappings.size());
EXPECT_EQ(MidiInputMapping(MidiKey(MIDI_NOTE_ON | 0x01, 0x10),
- options, control),
- mappings.at(0));
+ options,
+ control,
+ mappings.first().description),
+ mappings.first());
}
TEST_F(LearningUtilsTest, SingleMessageSwitchMode_CC) {
@@ -516,8 +574,10 @@ TEST_F(LearningUtilsTest, SingleMessageSwitchMode_CC) {
MidiOptions options;
options.sw = true;
EXPECT_EQ(MidiInputMapping(MidiKey(MIDI_CC | 0x01, 0x10),
- options, control),
- mappings.at(0));
+ options,
+ control,
+ mappings.first().description),
+ mappings.first());
m_messages.clear();
@@ -528,11 +588,12 @@ TEST_F(LearningUtilsTest, SingleMessageSwitchMode_CC) {
ASSERT_EQ(1, mappings.size());
EXPECT_EQ(MidiInputMapping(MidiKey(MIDI_CC | 0x01, 0x10),
- options, control),
- mappings.at(0));
+ options,
+ control,
+ mappings.first().description),
+ mappings.first());
}
-
TEST_F(LearningUtilsTest, MultipleControlsUnrecognized_BindsFirst) {
// Status 0x91, Control 0x10
addMessage(MIDI_NOTE_ON | 0x01, 0x10, 0x7F);
@@ -547,8 +608,10 @@ TEST_F(LearningUtilsTest, MultipleControlsUnrecognized_BindsFirst) {
LearningUtils::guessMidiInputMappings(control, m_messages);
ASSERT_EQ(1, mappings.size());
EXPECT_EQ(MidiInputMapping(MidiKey(MIDI_NOTE_ON | 0x01, 0x10),
- MidiOptions(), control),
- mappings.first());
+ MidiOptions(),
+ control,
+ mappings.first().description),
+ mappings.first());
}
TEST_F(LearningUtilsTest, MultipleChannelsUnrecognized_BindsFirst) {
@@ -565,6 +628,8 @@ TEST_F(LearningUtilsTest, MultipleChannelsUnrecognized_BindsFirst) {
LearningUtils::guessMidiInputMappings(control, m_messages);
ASSERT_EQ(1, mappings.size());
EXPECT_EQ(MidiInputMapping(MidiKey(MIDI_NOTE_ON | 0x01, 0x10),
- MidiOptions(), control),
- mappings.first());
+ MidiOptions(),
+ control,
+ mappings.first().description),
+ mappings.first());
}
diff --git a/src/test/midicontrollertest.cpp b/src/test/midicontrollertest.cpp
index ac520908638..1e44a89e850 100644
--- a/src/test/midicontrollertest.cpp
+++ b/src/test/midicontrollertest.cpp
@@ -33,7 +33,7 @@ class MidiControllerTest : public MixxxTest {
}
void addMapping(MidiInputMapping mapping) {
- m_preset.inputMappings.insertMulti(mapping.key.key, mapping);
+ m_preset.addInputMapping(mapping.key.key, mapping);
}
void loadPreset(const MidiControllerPreset& preset) {