From 5613fc553b74a9234b4fb2dcbc5f53ed03b14f5a Mon Sep 17 00:00:00 2001 From: 0xFEEDC0DE64 Date: Wed, 28 Dec 2022 00:09:51 +0100 Subject: [PATCH] Implemented loopstation preset model --- DrumMachine.pro | 2 + drummachinesettings.cpp | 10 ++ drummachinesettings.h | 7 + drumpadpresetsmodel.cpp | 46 +++---- drumpadpresetsmodel.h | 14 +- loopstationpresetsmodel.cpp | 242 ++++++++++++++++++++++++++++++++++ loopstationpresetsmodel.h | 36 +++++ widgets/drumpadwidget.cpp | 6 + widgets/loopstationwidget.cpp | 69 +++++++++- widgets/loopstationwidget.h | 11 ++ widgets/loopstationwidget.ui | 9 +- 11 files changed, 419 insertions(+), 33 deletions(-) create mode 100644 loopstationpresetsmodel.cpp create mode 100644 loopstationpresetsmodel.h diff --git a/DrumMachine.pro b/DrumMachine.pro index ddfed8d..3032b3a 100755 --- a/DrumMachine.pro +++ b/DrumMachine.pro @@ -23,6 +23,7 @@ SOURCES += \ jsonconverters.cpp \ loopstationjsonconverters.cpp \ loopstationpresets.cpp \ + loopstationpresetsmodel.cpp \ main.cpp \ midicontainers.cpp \ midiinwrapper.cpp \ @@ -57,6 +58,7 @@ HEADERS += \ jsonconverters.h \ loopstationjsonconverters.h \ loopstationpresets.h \ + loopstationpresetsmodel.h \ midicontainers.h \ midiinwrapper.h \ midioutwrapper.h \ diff --git a/drummachinesettings.cpp b/drummachinesettings.cpp index 416c85c..41fdbe8 100644 --- a/drummachinesettings.cpp +++ b/drummachinesettings.cpp @@ -222,3 +222,13 @@ void DrumMachineSettings::setDrumpadNote(quint8 pad, quint8 note) { setValue(QString{"drumpad/pad%0_note"}.arg(pad), note); } + +QString DrumMachineSettings::loopstationLastPresetId() const +{ + return value("loopstation/lastPresetId").toString(); +} + +void DrumMachineSettings::setLoopstationLastPresetId(const QString &lastPresetId) +{ + setValue("loopstation/lastPresetId", lastPresetId); +} diff --git a/drummachinesettings.h b/drummachinesettings.h index 235f813..0ff8603 100644 --- a/drummachinesettings.h +++ b/drummachinesettings.h @@ -27,6 +27,8 @@ public: QString lastMidiOutDevice() const; void setLastMidiOutDevice(const QString &lastMidiOutDevice); + + QString drumpadLastPresetId() const; void setDrumpadLastPresetId(const QString &lastPresetId); @@ -64,4 +66,9 @@ public: void setDrumpadChannel(quint8 pad, quint8 channel); quint8 drumpadNote(quint8 pad) const; void setDrumpadNote(quint8 pad, quint8 note); + + + + QString loopstationLastPresetId() const; + void setLoopstationLastPresetId(const QString &lastPresetId); }; diff --git a/drumpadpresetsmodel.cpp b/drumpadpresetsmodel.cpp index feb2cc9..f9c0be3 100755 --- a/drumpadpresetsmodel.cpp +++ b/drumpadpresetsmodel.cpp @@ -37,49 +37,49 @@ DrumPadPresetsModel::DrumPadPresetsModel(QObject *parent) : { } -DrumPadPresetsModel::DrumPadPresetsModel(const std::map &drumpad_presets, QObject *parent) : +DrumPadPresetsModel::DrumPadPresetsModel(const std::map &presets, QObject *parent) : QAbstractTableModel{parent} { - m_drumpad_presets.reserve(std::size(drumpad_presets)); - for (const auto &pair : drumpad_presets) - m_drumpad_presets.emplace_back(pair.second); + m_presets.reserve(std::size(presets)); + for (const auto &pair : presets) + m_presets.emplace_back(pair.second); } -DrumPadPresetsModel::DrumPadPresetsModel(std::vector &&drumpad_presets, QObject *parent) : +DrumPadPresetsModel::DrumPadPresetsModel(std::vector &&presets, QObject *parent) : QAbstractTableModel{parent} { - m_drumpad_presets = std::move(drumpad_presets); + m_presets = std::move(presets); } -DrumPadPresetsModel::DrumPadPresetsModel(const std::vector &drumpad_presets, QObject *parent) : +DrumPadPresetsModel::DrumPadPresetsModel(const std::vector &presets, QObject *parent) : QAbstractTableModel{parent} { - m_drumpad_presets = drumpad_presets; + m_presets = presets; } DrumPadPresetsModel::~DrumPadPresetsModel() = default; -void DrumPadPresetsModel::setPresets(const std::map &drumpad_presets) +void DrumPadPresetsModel::setPresets(const std::map &presets) { beginResetModel(); - m_drumpad_presets.clear(); - m_drumpad_presets.reserve(std::size(drumpad_presets)); - for (const auto &pair : drumpad_presets) - m_drumpad_presets.emplace_back(pair.second); + m_presets.clear(); + m_presets.reserve(std::size(presets)); + for (const auto &pair : presets) + m_presets.emplace_back(pair.second); endResetModel(); } -void DrumPadPresetsModel::setPresets(std::vector &&drumpad_presets) +void DrumPadPresetsModel::setPresets(std::vector &&presets) { beginResetModel(); - m_drumpad_presets = std::move(drumpad_presets); + m_presets = std::move(presets); endResetModel(); } -void DrumPadPresetsModel::setPresets(const std::vector &drumpad_presets) +void DrumPadPresetsModel::setPresets(const std::vector &presets) { beginResetModel(); - m_drumpad_presets = drumpad_presets; + m_presets = presets; endResetModel(); } @@ -90,18 +90,18 @@ const drumpad_presets::Preset &DrumPadPresetsModel::getPreset(const QModelIndex const drumpad_presets::Preset &DrumPadPresetsModel::getPreset(int row) const { - Q_ASSERT(row >= 0 && row < int(std::size(m_drumpad_presets))); - return m_drumpad_presets.at(row); + Q_ASSERT(row >= 0 && row < int(std::size(m_presets))); + return m_presets.at(row); } QModelIndex DrumPadPresetsModel::findPresetById(const QString &id) const { - for (auto iter = std::cbegin(m_drumpad_presets); iter != std::cend(m_drumpad_presets); iter++) + for (auto iter = std::cbegin(m_presets); iter != std::cend(m_presets); iter++) { if (iter->id != id) continue; - return createIndex(std::distance(std::cbegin(m_drumpad_presets), iter), 0); + return createIndex(std::distance(std::cbegin(m_presets), iter), 0); } return {}; @@ -111,7 +111,7 @@ int DrumPadPresetsModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent) - return std::size(m_drumpad_presets); + return std::size(m_presets); } int DrumPadPresetsModel::columnCount(const QModelIndex &parent) const @@ -132,7 +132,7 @@ QVariant DrumPadPresetsModel::data(const QModelIndex &index, int role) const return {}; if (index.row() < 0) return {}; - if (index.row() >= int(std::size(m_drumpad_presets))) + if (index.row() >= int(std::size(m_presets))) return {}; const auto &preset = getPreset(index); diff --git a/drumpadpresetsmodel.h b/drumpadpresetsmodel.h index 02d7e9b..be02e4c 100755 --- a/drumpadpresetsmodel.h +++ b/drumpadpresetsmodel.h @@ -12,14 +12,14 @@ class DrumPadPresetsModel : public QAbstractTableModel public: DrumPadPresetsModel(QObject *parent = nullptr); - DrumPadPresetsModel(const std::map &drumpad_presets, QObject *parent = nullptr); - DrumPadPresetsModel(std::vector &&drumpad_presets, QObject *parent = nullptr); - DrumPadPresetsModel(const std::vector &drumpad_presets, QObject *parent = nullptr); + DrumPadPresetsModel(const std::map &presets, QObject *parent = nullptr); + DrumPadPresetsModel(std::vector &&presets, QObject *parent = nullptr); + DrumPadPresetsModel(const std::vector &presets, QObject *parent = nullptr); ~DrumPadPresetsModel() override; - void setPresets(const std::map &drumpad_presets); - void setPresets(std::vector &&drumpad_presets); - void setPresets(const std::vector &drumpad_presets); + void setPresets(const std::map &presets); + void setPresets(std::vector &&presets); + void setPresets(const std::vector &presets); const drumpad_presets::Preset &getPreset(const QModelIndex &index) const; const drumpad_presets::Preset &getPreset(int row) const; @@ -32,5 +32,5 @@ public: QVariant headerData(int section, Qt::Orientation orientation, int role) const override; private: - std::vector m_drumpad_presets; + std::vector m_presets; }; diff --git a/loopstationpresetsmodel.cpp b/loopstationpresetsmodel.cpp new file mode 100644 index 0000000..73dd9b6 --- /dev/null +++ b/loopstationpresetsmodel.cpp @@ -0,0 +1,242 @@ +#include "loopstationpresetsmodel.h" + +#include + +#include +#include + +#include "loopstationpresets.h" + +enum { + ColumnId, + ColumnTitle, + ColumnAuthor, + ColumnBpm, + ColumnCoverUrl, + ColumnLoopLength, + ColumnOrderBy, + ColumnPremium, + ColumnTags, + ColumnAudioPreviewUrl, + ColumnDELETED, + NumberOfColumns +}; + +LoopStationPresetsModel::LoopStationPresetsModel(QObject *parent) : + QAbstractTableModel{parent} +{ +} + +LoopStationPresetsModel::LoopStationPresetsModel(const std::map &presets, QObject *parent) : + QAbstractTableModel{parent} +{ + m_presets.reserve(std::size(presets)); + for (const auto &pair : presets) + m_presets.emplace_back(pair.second); +} + +LoopStationPresetsModel::LoopStationPresetsModel(std::vector &&presets, QObject *parent) : + QAbstractTableModel{parent} +{ + m_presets = std::move(presets); +} + +LoopStationPresetsModel::LoopStationPresetsModel(const std::vector &presets, QObject *parent) : + QAbstractTableModel{parent} +{ + m_presets = presets; +} + +LoopStationPresetsModel::~LoopStationPresetsModel() = default; + +void LoopStationPresetsModel::setPresets(const std::map &presets) +{ + beginResetModel(); + m_presets.clear(); + m_presets.reserve(std::size(presets)); + for (const auto &pair : presets) + m_presets.emplace_back(pair.second); + endResetModel(); +} + +void LoopStationPresetsModel::setPresets(std::vector &&presets) +{ + beginResetModel(); + m_presets = std::move(presets); + endResetModel(); +} + +void LoopStationPresetsModel::setPresets(const std::vector &presets) +{ + beginResetModel(); + m_presets = presets; + endResetModel(); +} + +const loopstation_presets::Preset &LoopStationPresetsModel::getPreset(const QModelIndex &index) const +{ + return getPreset(index.row()); +} + +const loopstation_presets::Preset &LoopStationPresetsModel::getPreset(int row) const +{ + Q_ASSERT(row >= 0 && row < int(std::size(m_presets))); + return m_presets.at(row); +} + +QModelIndex LoopStationPresetsModel::findPresetById(const QString &id) const +{ + for (auto iter = std::cbegin(m_presets); iter != std::cend(m_presets); iter++) + { + if (iter->id != id) + continue; + + return createIndex(std::distance(std::cbegin(m_presets), iter), 0); + } + + return {}; +} + +int LoopStationPresetsModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent) + + return std::size(m_presets); +} + +int LoopStationPresetsModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent) + + return NumberOfColumns; +} + +QVariant LoopStationPresetsModel::data(const QModelIndex &index, int role) const +{ + if (role != Qt::DisplayRole && role != Qt::EditRole && role != Qt::FontRole && role != Qt::ForegroundRole) + return {}; + + if (index.column() < 0) + return {}; + if (index.column() >= NumberOfColumns) + return {}; + if (index.row() < 0) + return {}; + if (index.row() >= int(std::size(m_presets))) + return {}; + + const auto &preset = getPreset(index); + + const auto handleData = [&](const auto &val) -> QVariant + { + if (!val) + { + if (role == Qt::DisplayRole) + return this->tr("(null)"); + else if (role == Qt::FontRole) + { + QFont font; + font.setItalic(true); + return font; + } + else if (role == Qt::ForegroundRole) + return QColor{Qt::gray}; + return {}; + } + + if (role == Qt::DisplayRole || role == Qt::EditRole) + return *val; + + return {}; + }; + + const auto handleStringVectorData = [&](const auto &val) -> QVariant + { + if (!val) + { + if (role == Qt::DisplayRole) + return this->tr("(null)"); + else if (role == Qt::FontRole) + { + QFont font; + font.setItalic(true); + return font; + } + else if (role == Qt::ForegroundRole) + return QColor{Qt::gray}; + return {}; + } + + if (role == Qt::DisplayRole || role == Qt::EditRole) + { + QString text; + for (auto iter = std::cbegin(*val); iter != std::cend(*val); iter++) + { + if (iter != std::cbegin(*val)) + text += ", "; + text += *iter; + } + return text; + } + + return {}; + }; + + switch (index.column()) + { + case ColumnId: + { + if (preset.id) + { + bool ok; + if (auto id = preset.id->toInt(&ok); ok) + return id; + } + + return handleData(preset.id); + } + case ColumnTitle: return handleData(preset.title); + case ColumnAuthor: return handleData(preset.author); + case ColumnBpm: return handleData(preset.bpm); + case ColumnCoverUrl: return handleData(preset.coverUrl); + case ColumnLoopLength: return handleData(preset.loopLength); + case ColumnOrderBy: return handleData(preset.orderBy); + case ColumnPremium: return handleData(preset.premium); + case ColumnTags: return handleStringVectorData(preset.tags); + case ColumnAudioPreviewUrl: return handleData(preset.audioPreviewUrl); + case ColumnDELETED: return handleData(preset.DELETED); + } + + Q_UNREACHABLE(); +} + +QVariant LoopStationPresetsModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role != Qt::DisplayRole && role != Qt::EditRole) + return {}; + + if (orientation != Qt::Horizontal) + return {}; + + if (section < 0) + return {}; + if (section >= NumberOfColumns) + return {}; + + switch (section) + { + case ColumnId: return tr("id"); + case ColumnTitle: return tr("title"); + case ColumnAuthor: return tr("author"); + case ColumnBpm: return tr("bpm"); + case ColumnCoverUrl: return tr("coverUrl"); + case ColumnLoopLength: return tr("loopLength"); + case ColumnOrderBy: return tr("orderBy"); + case ColumnPremium: return tr("premium"); + case ColumnTags: return tr("tags"); + case ColumnAudioPreviewUrl: return tr("audioPreviewUrl"); + case ColumnDELETED: return tr("DELETED"); + } + + Q_UNREACHABLE(); +} diff --git a/loopstationpresetsmodel.h b/loopstationpresetsmodel.h new file mode 100644 index 0000000..4ddf60d --- /dev/null +++ b/loopstationpresetsmodel.h @@ -0,0 +1,36 @@ +#pragma once + +#include + +#include + +namespace loopstation_presets { class Preset; } + +class LoopStationPresetsModel : public QAbstractTableModel +{ + Q_OBJECT + +public: + LoopStationPresetsModel(QObject *parent = nullptr); + LoopStationPresetsModel(const std::map &presets, QObject *parent = nullptr); + LoopStationPresetsModel(std::vector &&presets, QObject *parent = nullptr); + LoopStationPresetsModel(const std::vector &presets, QObject *parent = nullptr); + ~LoopStationPresetsModel() override; + + void setPresets(const std::map &presets); + void setPresets(std::vector &&presets); + void setPresets(const std::vector &presets); + + const loopstation_presets::Preset &getPreset(const QModelIndex &index) const; + const loopstation_presets::Preset &getPreset(int row) const; + + QModelIndex findPresetById(const QString &id) const; + + int rowCount(const QModelIndex &parent) const override; + int columnCount(const QModelIndex &parent) const override; + QVariant data(const QModelIndex &index, int role) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role) const override; + +private: + std::vector m_presets; +}; diff --git a/widgets/drumpadwidget.cpp b/widgets/drumpadwidget.cpp index 2dbdda7..af64113 100644 --- a/widgets/drumpadwidget.cpp +++ b/widgets/drumpadwidget.cpp @@ -132,7 +132,13 @@ void DrumPadWidget::midiReceived(const midi::MidiMessage &message) void DrumPadWidget::currentRowChanged(const QModelIndex ¤t) { if (!current.isValid()) + { + if (m_settings) + m_settings->setDrumpadLastPresetId(QString{}); + else + qWarning() << "no settings available"; return; + } const auto &preset = m_presetsModel.getPreset(m_presetsProxyModel.mapToSource(current)); diff --git a/widgets/loopstationwidget.cpp b/widgets/loopstationwidget.cpp index f750cd6..67423ad 100644 --- a/widgets/loopstationwidget.cpp +++ b/widgets/loopstationwidget.cpp @@ -21,6 +21,17 @@ LoopStationWidget::LoopStationWidget(QWidget *parent) : connect(m_ui->pushButtonUp, &QAbstractButton::pressed, this, &LoopStationWidget::selectPrevPreset); connect(m_ui->pushButtonDown, &QAbstractButton::pressed, this, &LoopStationWidget::selectNextPreset); connect(m_ui->pushButtonRefresh, &QAbstractButton::pressed, this, &LoopStationWidget::loadPresets); + + m_presetsProxyModel.setFilterCaseSensitivity(Qt::CaseInsensitive); + m_presetsProxyModel.setSortRole(Qt::EditRole); + m_presetsProxyModel.setSourceModel(&m_presetsModel); + m_ui->presetsView->setModel(&m_presetsProxyModel); + + m_presetsProxyModel.setFilterKeyColumn(1); + + connect(m_ui->lineEdit, &QLineEdit::textChanged, &m_presetsProxyModel, &QSortFilterProxyModel::setFilterFixedString); + + connect(m_ui->presetsView->selectionModel(), &QItemSelectionModel::currentRowChanged, this, &LoopStationWidget::currentRowChanged); } LoopStationWidget::~LoopStationWidget() = default; @@ -43,7 +54,7 @@ void LoopStationWidget::injectDecodingThread(QThread &thread) void LoopStationWidget::loadSettings(DrumMachineSettings &settings) { - + m_settings = &settings; } void LoopStationWidget::unsendColors() @@ -61,6 +72,25 @@ void LoopStationWidget::midiReceived(const midi::MidiMessage &message) } +void LoopStationWidget::currentRowChanged(const QModelIndex ¤t) +{ + if (!current.isValid()) + { + if (m_settings) + m_settings->setLoopstationLastPresetId(QString{}); + else + qWarning() << "no settings available"; + return; + } + + const auto &preset = m_presetsModel.getPreset(m_presetsProxyModel.mapToSource(current)); + + if (m_settings) + m_settings->setLoopstationLastPresetId(preset.id ? *preset.id : QString{}); + else + qWarning() << "no settings available"; +} + void LoopStationWidget::loadPresets() { if (!m_networkAccessManager) @@ -101,7 +131,28 @@ void LoopStationWidget::requestFinished() if (!result.presets) throw std::runtime_error("presets missing in response"); - // TODO + m_presetsModel.setPresets(std::move(*std::move(result).presets)); + + if (m_settings) + { + if (const auto &lastPresetId = m_settings->loopstationLastPresetId(); !lastPresetId.isEmpty()) + { + if (const auto &index = m_presetsModel.findPresetById(lastPresetId); index.isValid()) + selectIndex(m_presetsProxyModel.mapFromSource(index)); + else + { + qWarning() << "invalid last preset id" << lastPresetId; + goto noLastId; + } + } + else + goto noLastId; + } + else + { +noLastId: + selectFirstPreset(); + } } catch (const std::exception &e) { @@ -123,3 +174,17 @@ void LoopStationWidget::selectNextPreset() { } + +void LoopStationWidget::selectIndex(const QModelIndex &index) +{ + if (!index.isValid()) + { + qWarning() << "invalid index"; + return; + } + + m_ui->presetsView->scrollTo(index); +// m_ui->presetsView->selectionModel()->select(index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); + m_ui->presetsView->selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); + currentRowChanged(index); +} diff --git a/widgets/loopstationwidget.h b/widgets/loopstationwidget.h index 826cd75..d7712e0 100644 --- a/widgets/loopstationwidget.h +++ b/widgets/loopstationwidget.h @@ -1,9 +1,12 @@ #pragma once #include +#include #include +#include "loopstationpresetsmodel.h" + namespace Ui { class LoopStationWidget; } namespace midi { struct MidiMessage; } class QNetworkAccessManager; @@ -33,6 +36,7 @@ public slots: void midiReceived(const midi::MidiMessage &message); private slots: + void currentRowChanged(const QModelIndex ¤t); void loadPresets(); void requestFinished(); void selectFirstPreset(); @@ -40,8 +44,15 @@ private slots: void selectNextPreset(); private: + void selectIndex(const QModelIndex &index); + const std::unique_ptr m_ui; + DrumMachineSettings *m_settings{}; + + LoopStationPresetsModel m_presetsModel; + QSortFilterProxyModel m_presetsProxyModel; + QNetworkAccessManager *m_networkAccessManager{}; std::unique_ptr m_reply; diff --git a/widgets/loopstationwidget.ui b/widgets/loopstationwidget.ui index 9d81b05..15364b2 100644 --- a/widgets/loopstationwidget.ui +++ b/widgets/loopstationwidget.ui @@ -66,7 +66,14 @@ - + + + false + + + true + +