diff --git a/plugin/components/rpl_view.cpp b/plugin/components/rpl_view.cpp index 3af1ebb..e66cf0d 100644 --- a/plugin/components/rpl_view.cpp +++ b/plugin/components/rpl_view.cpp @@ -22,6 +22,7 @@ #include "utility/functional_timer.h" #include #include +#include "components/modal_textinputbox.h" class BankItemsListBoxModel final : public juce::ListBox, public juce::ListBoxModel, public juce::DragAndDropTarget { @@ -48,8 +49,15 @@ class BankItemsListBoxModel final : public juce::ListBox, public juce::ListBoxMo m_dblClickCallback = dblClickCallback; } + void setRenameCallback(std::function renameCallback) { + m_renameCallback = renameCallback; + } + private: + std::unique_ptr m_editDialog; + std::unique_ptr m_itemMenu; std::vector m_items; + std::function m_renameCallback; std::function m_dblClickCallback; std::function, juce::WeakReference)> m_dropCallback; std::function)> m_deleteCallback; @@ -129,6 +137,24 @@ class BankItemsListBoxModel final : public juce::ListBox, public juce::ListBoxMo { m_dblClickCallback(row); } + + void listBoxItemClicked(int row, const juce::MouseEvent& evnt) override + { + if (evnt.mods.isRightButtonDown() && m_renameCallback) { + m_itemMenu.reset(new juce::PopupMenu); + + juce::PopupMenu::Options presetOptions = juce::PopupMenu::Options{} + .withTargetComponent(getComponentForRowNumber(row)); + + m_itemMenu->addItem(1, "Rename"); + + m_itemMenu->showMenuAsync(presetOptions, [this, row](int index) { + if (index == 1) { + m_renameCallback(row); + } + }); + } + } }; class LoadedBank : public juce::Component, public juce::DragAndDropContainer { @@ -137,6 +163,7 @@ class LoadedBank : public juce::Component, public juce::DragAndDropContainer { juce::File m_file; ysfx_bank_shared m_bank; + std::unique_ptr m_editDialog; std::unique_ptr m_listBox; std::unique_ptr m_label; std::unique_ptr m_btnLoadFile; @@ -255,6 +282,35 @@ class LoadedBank : public juce::Component, public juce::DragAndDropContainer { ); } + void renamePreset(int row) { + if (!m_bank) return; + if (row > static_cast(m_bank->preset_count)) return; + + std::string currentPreset{m_bank->presets[row].name}; + + m_editDialog.reset( + show_async_text_input( + "Enter new name", + "", + [this, currentPreset](juce::String presetName, bool wantRename) { + if (wantRename) { + m_bank.reset(ysfx_rename_preset_from_bank(m_bank.get(), currentPreset.c_str(), presetName.toStdString().c_str())); + this->m_listBox->deselectAllRows(); + save_bank(m_file.getFullPathName().toStdString().c_str(), m_bank.get()); + if (m_bankUpdatedCallback) m_bankUpdatedCallback(); + } + }, + [this](juce::String presetName) { + if (ysfx_preset_exists(m_bank.get(), presetName.toStdString().c_str())) { + return juce::String("Preset with that name already exists.\nChoose a different name or click cancel."); + } else { + return juce::String(""); + } + } + ) + ); + } + void createUI(bool withLoad) { m_listBox.reset(new BankItemsListBoxModel()); @@ -268,6 +324,7 @@ class LoadedBank : public juce::Component, public juce::DragAndDropContainer { m_listBox->setOutlineThickness(1); m_listBox->setDropCallback([this](std::vector indices, juce::WeakReference ref) { this->transferPresets(indices, ref); }); m_listBox->setDeleteCallback([this](std::vector indices) { this->deletePresets(indices); }); + m_listBox->setRenameCallback([this](int row) { this->renamePreset(row); }); m_listBox->setDoubleClickCallback([this](int idx) { if (m_loadPresetCallback) m_loadPresetCallback(std::string{m_bank->presets[idx].name}); }); addAndMakeVisible(*m_listBox); addAndMakeVisible(*m_label); @@ -283,6 +340,7 @@ class LoadedBank : public juce::Component, public juce::DragAndDropContainer { if (m_file == juce::File{}) { m_listBox->setItems({}); m_listBox->updateContent(); + repaint(); return; } @@ -303,9 +361,9 @@ class LoadedBank : public juce::Component, public juce::DragAndDropContainer { names.push_back(juce::String::fromUTF8(m_bank->presets[i].name)); } m_listBox->setItems(names); - m_listBox->updateContent(); - + m_listBox->updateContent(); m_label->setText(m_file.getFileName() + juce::String(" (") + juce::String(bank->name) + juce::String(")"), juce::dontSendNotification); + repaint(); } void setFile(juce::File file) diff --git a/plugin/editor.cpp b/plugin/editor.cpp index f4cabe7..1b895a9 100644 --- a/plugin/editor.cpp +++ b/plugin/editor.cpp @@ -410,7 +410,7 @@ void YsfxEditor::Impl::updateInfo() auto index = ysfx_preset_exists(bank.get(), preset.c_str()); if (index > 0) { - m_proc->loadJsfxPreset(info, bank, (uint32_t)(index - 1), true, true); + m_proc->loadJsfxPreset(info, bank, (uint32_t)(index - 1), PresetLoadMode::load, true); } } ); @@ -759,7 +759,7 @@ void YsfxEditor::Impl::popupPresets() showPopupMenuWithQuickSearch(*m_presetsPopup, quickSearchOptions, [this, info, bank](int index) { if ((index > 0) && (index < 32767)) { - m_proc->loadJsfxPreset(info, bank, (uint32_t)(index - 1), true, true); + m_proc->loadJsfxPreset(info, bank, (uint32_t)(index - 1), PresetLoadMode::load, true); } } ); diff --git a/plugin/processor.cpp b/plugin/processor.cpp index 68946eb..1e30d6d 100644 --- a/plugin/processor.cpp +++ b/plugin/processor.cpp @@ -73,7 +73,7 @@ struct YsfxProcessor::Impl : public juce::AudioProcessorListener { YsfxInfo::Ptr info; ysfx_bank_shared bank; uint32_t index = 0; - bool load = false; + PresetLoadMode load = noLoad; volatile bool completion = false; std::mutex completionMutex; std::condition_variable completionVariable; @@ -231,7 +231,7 @@ void YsfxProcessor::loadJsfxFile(const juce::String &filePath, ysfx_state_t *ini } } -void YsfxProcessor::loadJsfxPreset(YsfxInfo::Ptr info, ysfx_bank_shared bank, uint32_t index, bool load, bool async) +void YsfxProcessor::loadJsfxPreset(YsfxInfo::Ptr info, ysfx_bank_shared bank, uint32_t index, PresetLoadMode load, bool async) { Impl::PresetRequest::Ptr presetRequest{new Impl::PresetRequest}; presetRequest->info = info; @@ -264,10 +264,10 @@ void YsfxProcessor::reloadBank() return; ysfx_bank_shared bank = m_impl->loadDefaultBank(m_impl->m_info); - loadJsfxPreset(m_impl->m_info, bank, false, false, true); + loadJsfxPreset(m_impl->m_info, bank, false, PresetLoadMode::noLoad, true); } -void YsfxProcessor::savePreset(const char* preset_name, ysfx_state_t* preset, bool load) +void YsfxProcessor::savePreset(const char* preset_name, ysfx_state_t* preset) { ysfx_t *fx = m_impl->m_fx.get(); if (!fx) return; @@ -287,7 +287,7 @@ void YsfxProcessor::savePreset(const char* preset_name, ysfx_state_t* preset, bo } save_bank(bankLocation.getFullPathName().toStdString().c_str(), newBank.get()); - loadJsfxPreset(m_impl->m_info, newBank, ysfx_preset_exists(newBank.get(), preset_name) - 1, load, true); + loadJsfxPreset(m_impl->m_info, newBank, ysfx_preset_exists(newBank.get(), preset_name) - 1, PresetLoadMode::load, true); } void YsfxProcessor::saveCurrentPreset(const char* preset_name) @@ -295,7 +295,7 @@ void YsfxProcessor::saveCurrentPreset(const char* preset_name) ysfx_t *fx = m_impl->m_fx.get(); if (!fx) return; - savePreset(preset_name, ysfx_save_state(fx), true); + savePreset(preset_name, ysfx_save_state(fx)); } void YsfxProcessor::renameCurrentPreset(const char* new_preset_name) @@ -303,19 +303,25 @@ void YsfxProcessor::renameCurrentPreset(const char* new_preset_name) ysfx_t *fx = m_impl->m_fx.get(); if (!fx) return; - // Make a backup copy before we write in case there's trouble - juce::File bankLocation = getCustomBankLocation(fx); - backupPresetFile(bankLocation); - ysfx_bank_shared bank = m_impl->m_bank; // Make sure we keep it alive while we are operating on it if (!bank) return; auto currentPreset = m_impl->m_currentPresetInfo->m_lastChosenPreset; if (currentPreset.isEmpty()) return; + // It doesn't exist => Save instead! + if (ysfx_preset_exists(bank.get(), currentPreset.toStdString().c_str()) == 0) { + saveCurrentPreset(new_preset_name); + return; + } + + // Make a backup copy before we write in case there's trouble + juce::File bankLocation = getCustomBankLocation(fx); + backupPresetFile(bankLocation); + ysfx_bank_shared newBank = make_ysfx_bank_shared(ysfx_rename_preset_from_bank(bank.get(), currentPreset.toStdString().c_str(), new_preset_name)); save_bank(bankLocation.getFullPathName().toStdString().c_str(), newBank.get()); - loadJsfxPreset(m_impl->m_info, newBank, ysfx_preset_exists(newBank.get(), new_preset_name) - 1, true, true); + loadJsfxPreset(m_impl->m_info, newBank, ysfx_preset_exists(newBank.get(), new_preset_name) - 1, PresetLoadMode::load, true); } void YsfxProcessor::deleteCurrentPreset() @@ -335,7 +341,7 @@ void YsfxProcessor::deleteCurrentPreset() ysfx_bank_shared newBank = make_ysfx_bank_shared(ysfx_delete_preset_from_bank(bank.get(), currentPreset.toStdString().c_str())); save_bank(bankLocation.getFullPathName().toStdString().c_str(), newBank.get()); - loadJsfxPreset(m_impl->m_info, newBank, 0, false, true); + loadJsfxPreset(m_impl->m_info, newBank, 0, PresetLoadMode::deleteName, true); } void YsfxProcessor::cyclePreset(int direction) @@ -366,7 +372,7 @@ void YsfxProcessor::cyclePreset(int direction) next_preset = 0; } - loadJsfxPreset(m_impl->m_info, m_impl->m_bank, next_preset, true, true); + loadJsfxPreset(m_impl->m_info, m_impl->m_bank, next_preset, PresetLoadMode::load, true); } YsfxInfo::Ptr YsfxProcessor::getCurrentInfo() @@ -1025,13 +1031,13 @@ void YsfxProcessor::Impl::Background::processPresetRequest(PresetRequest &req) ysfx_bank_t *bank = req.bank.get(); - if (req.load) { + if (req.load == PresetLoadMode::load) { if (!bank || req.index >= bank->preset_count) return; const ysfx_preset_t &preset = bank->presets[req.index]; m_impl->loadNewPreset(preset); - } else { + } else if (req.load == PresetLoadMode::deleteName) { m_impl->resetPresetInfo(); } diff --git a/plugin/processor.h b/plugin/processor.h index 459480e..bf8a5bc 100644 --- a/plugin/processor.h +++ b/plugin/processor.h @@ -24,6 +24,7 @@ using ysfx_t = struct ysfx_s; using ysfx_state_t = struct ysfx_state_s; enum RetryState {ok, mustRetry, retrying}; +enum PresetLoadMode {load, noLoad, deleteName}; class YsfxProcessor : public juce::AudioProcessor { public: @@ -32,10 +33,10 @@ class YsfxProcessor : public juce::AudioProcessor { YsfxParameter *getYsfxParameter(int sliderIndex); void loadJsfxFile(const juce::String &filePath, ysfx_state_t *initialState, bool async); - void loadJsfxPreset(YsfxInfo::Ptr info, ysfx_bank_shared bank, uint32_t index, bool load, bool async); + void loadJsfxPreset(YsfxInfo::Ptr info, ysfx_bank_shared bank, uint32_t index, PresetLoadMode load, bool async); bool presetExists(const char *preset_name); void reloadBank(); - void savePreset(const char* preset_name, ysfx_state_t *preset, bool load); + void savePreset(const char* preset_name, ysfx_state_t *preset); void cyclePreset(int direction); void saveCurrentPreset(const char* preset_name); void renameCurrentPreset(const char *new_preset_name);