From 2c2db0fe09d01d66b535656471749e4903f3d5f6 Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Thu, 28 Dec 2017 19:15:26 -0500 Subject: [PATCH] Implement drag-and-drop of Nyquist, LADSPA, VST plug-ins... ... I'm not sure how to make it work for AudioUnits, LV2, or Vamp, for which the API for identifying a plug-in doesn't map straightforwardly to a file name. --- src/Experimental.h | 4 ++ src/PluginManager.cpp | 105 ++++++++++++++++++++++++++++++++++++++++++ src/PluginManager.h | 2 + src/Project.cpp | 11 +++++ 4 files changed, 122 insertions(+) diff --git a/src/Experimental.h b/src/Experimental.h index ad135bd6b..7c195482b 100644 --- a/src/Experimental.h +++ b/src/Experimental.h @@ -224,6 +224,10 @@ // scrolling past zero is enabled. Perhaps that lessens confusion. #define EXPERIMENTAL_TWO_TONE_TIME_RULER +// Paul Licameli (PRL) 28 Dec 2017 +// Easy drag-and-drop to add Nyquist, LADSPA, and VST plug-ins +#define EXPERIMENTAL_DRAG_DROP_PLUG_INS + #ifndef IN_RC // Define to include crash reporting #include diff --git a/src/PluginManager.cpp b/src/PluginManager.cpp index 930412531..d92c20c91 100644 --- a/src/PluginManager.cpp +++ b/src/PluginManager.cpp @@ -1747,6 +1747,111 @@ void PluginManager::Terminate() } } +bool PluginManager::DropFile(const wxString &fileName) +{ + auto &mm = ModuleManager::Get(); + + for (const PluginDescriptor *plug = GetFirstPlugin(PluginTypeModule); + plug; + plug = GetNextPlugin(PluginTypeModule)) + { + auto module = static_cast + (mm.CreateProviderInstance(plug->GetID(), plug->GetPath())); + const auto &ff = module->InstallPath(); + if (!ff.empty() && module->PathsAreFiles()) { + wxString errMsg; + // Do dry-run test of the file format + unsigned nPlugIns = module->DiscoverPluginsAtPath(fileName, errMsg, + [](ModuleInterface *, EffectIdentInterface *){}); + if (nPlugIns) { + // File contents are good for this module, so check no others. + // All branches of this block return true, even in case of + // failure for other reasons, to signal that other drag-and-drop + // actions should not be tried. + + // Find path to copy it + const wxFileName src{ fileName }; + wxFileName dst; + dst.AssignDir( ff ); + dst.SetFullName( src.GetFullName() ); + if ( dst.Exists() ) { + // Query whether to overwrite + bool overwrite = (wxYES == ::wxMessageBox( + wxString::Format(_("Overwrite the plug-in file %s ?"), + dst.GetFullPath() ), + _("Plug-in already exists"), + wxYES_NO + ) ); + if ( !overwrite ) + return true; + } + + // Move the file or subtree + bool copied = false; + auto dstPath = dst.GetFullPath(); + if ( src.FileExists() ) + // A simple one-file plug-in + copied = FileNames::CopyFile( + src.GetFullPath(), dstPath, true ); + else { + // A sub-folder + // such as for some VST packages + // Recursive copy needed -- to do + return true; + } + + if (!copied) { + ::wxMessageBox( + _("Plug-in file is in use. Failed to overwrite")); + return true; + } + + // Register for real + std::vector ids; + std::vector names; + nPlugIns = module->DiscoverPluginsAtPath(dstPath, errMsg, + [&](ModuleInterface *provider, EffectIdentInterface *ident){ + // Register as by default, but also collecting the PluginIDs + // and names + ids.push_back( + PluginManagerInterface::DefaultRegistrationCallback( + provider, ident) ); + names.push_back( wxGetTranslation( ident->GetName() ) ); + }); + if ( ! nPlugIns ) { + // Unlikely after the dry run succeeded + ::wxMessageBox( wxString::Format( + _("Failed to register:\n%s"), errMsg ) ); + return true; + } + + // Ask whether to enable the plug-ins + if (auto nIds = ids.size()) { + auto format = wxPLURAL( + _("Enable this plug-in?"), + _("Enable these plug-ins?"), + nIds + ); + format += wxT("\n"); + for (const auto &name : names) + format += name + wxT("\n"); + bool enable = (wxYES == ::wxMessageBox( + wxString::Format( format, nIds ), + _("Enable new plug-ins"), + wxYES_NO + ) ); + for (const auto &id : ids) + mPlugins[id].SetEnabled(enable); + } + + return true; + } + } + } + + return false; +} + void PluginManager::Load() { // Create/Open the registry diff --git a/src/PluginManager.h b/src/PluginManager.h index 1e93f0a67..a1c2dd4a3 100644 --- a/src/PluginManager.h +++ b/src/PluginManager.h @@ -228,6 +228,8 @@ public: void Initialize(); void Terminate(); + bool DropFile(const wxString &fileName); + static PluginManager & Get(); static PluginID GetID(ModuleInterface *module); diff --git a/src/Project.cpp b/src/Project.cpp index 829b38caa..6c3dbd1a1 100644 --- a/src/Project.cpp +++ b/src/Project.cpp @@ -461,6 +461,17 @@ public: } ); for (const auto &name : sortednames) { + +#ifdef EXPERIMENTAL_DRAG_DROP_PLUG_INS + // Is it a plug-in? + if (PluginManager::Get().DropFile(name)) { + mProject->RebuildAllMenuBars(); + continue; + } + + // No, so import. +#endif + if (Importer::IsMidi(name)) AudacityProject::DoImportMIDI(mProject, name); else