Skip to content

Commit

Permalink
Portable Patch Favorites (#5959)
Browse files Browse the repository at this point in the history
A function to export and import favorite lists in a filesystem
independent fashion (namely we replace the factory and user roots
with keys on export and replace them with paths on import)

Closes #5810
  • Loading branch information
baconpaul authored Mar 8, 2022
1 parent 79e20f2 commit 309cb4d
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 5 deletions.
13 changes: 8 additions & 5 deletions src/surge-xt/gui/SurgeGUIEditor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6611,15 +6611,18 @@ std::string SurgeGUIEditor::showShortcutDescription(const std::string &shortcutD
}
}

void SurgeGUIEditor::setPatchAsFavorite(bool b)
void SurgeGUIEditor::setPatchAsFavorite(bool b) { setSpecificPatchAsFavorite(synth->patchid, b); }

void SurgeGUIEditor::setSpecificPatchAsFavorite(int patchid, bool b)
{
if (synth->patchid >= 0 && synth->patchid < synth->storage.patch_list.size())
if (patchid >= 0 && patchid < synth->storage.patch_list.size())
{
synth->storage.patch_list[synth->patchid].isFavorite = b;
synth->storage.patchDB->setUserFavorite(
synth->storage.patch_list[synth->patchid].path.u8string(), b);
synth->storage.patch_list[patchid].isFavorite = b;
synth->storage.patchDB->setUserFavorite(synth->storage.patch_list[patchid].path.u8string(),
b);
}
}

bool SurgeGUIEditor::isPatchFavorite()
{
if (synth->patchid >= 0 && synth->patchid < synth->storage.patch_list.size())
Expand Down
2 changes: 2 additions & 0 deletions src/surge-xt/gui/SurgeGUIEditor.h
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,8 @@ class SurgeGUIEditor : public Surge::GUI::IComponentTagValue::Listener,
bool isPatchFavorite();
bool isPatchUser();

void setSpecificPatchAsFavorite(int patchid, bool b);

/*
* Modulation Client API
*/
Expand Down
128 changes: 128 additions & 0 deletions src/surge-xt/gui/widgets/PatchSelector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -746,6 +746,12 @@ bool PatchSelector::optionallyAddFavorites(juce::PopupMenu &p, bool addColumnBre
[this, f]() { this->loadPatch(f.first); });
}

subMenu.addSeparator();
subMenu.addItem(Surge::GUI::toOSCaseForMenu("Export favorites to..."),
[this]() { exportFavorites(); });
subMenu.addItem(Surge::GUI::toOSCaseForMenu("Load favorites from..."),
[this]() { importFavorites(); });

p.addSubMenu("Favorites", subMenu);
}
else
Expand All @@ -755,11 +761,133 @@ bool PatchSelector::optionallyAddFavorites(juce::PopupMenu &p, bool addColumnBre
p.addItem(juce::CharPointer_UTF8(f.second.name.c_str()),
[this, f]() { this->loadPatch(f.first); });
}
p.addSeparator();
p.addItem(Surge::GUI::toOSCaseForMenu("Export favorites to..."),
[this]() { exportFavorites(); });
p.addItem(Surge::GUI::toOSCaseForMenu("Load favorites from..."),
[this]() { importFavorites(); });
}

return true;
}

void PatchSelector::exportFavorites()
{
auto favoritesCallback = [this](const juce::FileChooser &c) {
auto isSubDir = [](auto p, auto root) {
while (p != fs::path() && p != p.parent_path())
{
if (p == root)
{
return true;
}
p = p.parent_path();
}
return false;
};

auto result = c.getResults();

if (result.isEmpty() || result.size() > 1)
{
return;
}

auto fsp = fs::path{result[0].getFullPathName().toStdString()};
fsp = fsp.replace_extension(".surgefav");

std::ofstream ofs(fsp);

for (auto p : storage->patch_list)
{
if (p.isFavorite)
{
auto q = p.path;
if (isSubDir(q, storage->datapath))
{
q = q.lexically_relative(storage->datapath);
ofs << "FACTORY:" << q.u8string() << std::endl;
}
else if (isSubDir(q, storage->userPatchesPath))
{
q = q.lexically_relative(storage->userPatchesPath);
ofs << "USER:" << q.u8string() << std::endl;
}
}
}
ofs.close();
};
auto sge = firstListenerOfType<SurgeGUIEditor>();
if (!sge)
return;
sge->fileChooser =
std::make_unique<juce::FileChooser>("Export Favorites", juce::File(), "*.surgefav");
sge->fileChooser->launchAsync(juce::FileBrowserComponent::saveMode |
juce::FileBrowserComponent::canSelectFiles |
juce::FileBrowserComponent::warnAboutOverwriting,
favoritesCallback);
}

void PatchSelector::importFavorites()
{
auto importCallback = [this](const juce::FileChooser &c) {
auto result = c.getResults();

if (result.isEmpty() || result.size() > 1)
{
return;
}

auto fsp = fs::path{result[0].getFullPathName().toStdString()};
fsp = fsp.replace_extension(".surgefav");

std::ifstream ifs(fsp);

std::set<fs::path> imports;

for (std::string line; getline(ifs, line);)
{
if (line.find("FACTORY:") == 0)
{
auto q = storage->datapath / fs::path(line.substr(std::string("FACTORY:").size()));
imports.insert(q);
}
else if (line.find("USER:") == 0)
{
auto q =
storage->userPatchesPath / fs::path(line.substr(std::string("USER:").size()));
imports.insert(q);
}
}

int i = 0;
auto sge = firstListenerOfType<SurgeGUIEditor>();
if (!sge)
return;
bool refresh = false;
for (auto p : storage->patch_list)
{
if (!p.isFavorite && imports.find(p.path) != imports.end())
{
sge->setSpecificPatchAsFavorite(i, true);
refresh = true;
}
i++;
}

if (refresh)
sge->queue_refresh = true;

ifs.close();
};
auto sge = firstListenerOfType<SurgeGUIEditor>();
if (!sge)
return;
sge->fileChooser =
std::make_unique<juce::FileChooser>("Import Favorites", juce::File(), "*.surgefav");
sge->fileChooser->launchAsync(juce::FileBrowserComponent::canSelectFiles, importCallback);
}

bool PatchSelector::populatePatchMenuForCategory(int c, juce::PopupMenu &contextMenu,
bool single_category, int &main_e, bool rootCall)
{
Expand Down
2 changes: 2 additions & 0 deletions src/surge-xt/gui/widgets/PatchSelector.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,8 @@ struct PatchSelector : public juce::Component,
void showClassicMenu(bool singleCategory = false);
bool optionallyAddFavorites(juce::PopupMenu &into, bool addColumnBreakAndHeader,
bool addToSubmenu = true);
void exportFavorites();
void importFavorites();
void openPatchBrowser();

void paint(juce::Graphics &g) override;
Expand Down

0 comments on commit 309cb4d

Please sign in to comment.