diff --git a/include/audacity/ModuleInterface.h b/include/audacity/ModuleInterface.h index fdb3b6fb5..5280c70f1 100644 --- a/include/audacity/ModuleInterface.h +++ b/include/audacity/ModuleInterface.h @@ -43,6 +43,7 @@ #define __AUDACITY_MODULEINTERFACE_H__ #include +#include #include "Identifier.h" #include "audacity/ComponentInterface.h" #include "audacity/PluginInterface.h" @@ -129,10 +130,8 @@ public: virtual bool IsPluginValid(const PluginPath & path, bool bFast) = 0; // When appropriate, CreateInstance() will be called to instantiate the plugin. - virtual ComponentInterface *CreateInstance(const PluginPath & path) = 0; - - // When appropriate, DeleteInstance() will be called to delete the plugin. - virtual void DeleteInstance(ComponentInterface *instance) = 0; + virtual std::unique_ptr + CreateInstance(const PluginPath & path) = 0; }; // ---------------------------------------------------------------------------- diff --git a/modules/mod-nyq-bench/NyqBench.cpp b/modules/mod-nyq-bench/NyqBench.cpp index 6fe612e32..b0909b2c6 100755 --- a/modules/mod-nyq-bench/NyqBench.cpp +++ b/modules/mod-nyq-bench/NyqBench.cpp @@ -1327,9 +1327,11 @@ void NyqBench::OnLargeIcons(wxCommandEvent & e) void NyqBench::OnGo(wxCommandEvent & e) { - // No need to delete...EffectManager will do it - mEffect = new NyquistEffect(wxT("Nyquist Effect Workbench")); - const PluginID & ID = EffectManager::Get().RegisterEffect(mEffect); + auto pEffect = + std::make_unique(L"Nyquist Effect Workbench"); + mEffect = pEffect.get(); + const PluginID & ID = + EffectManager::Get().RegisterEffect(std::move(pEffect)); mEffect->SetCommand(mScript->GetValue()); mEffect->RedirectOutput(); diff --git a/src/BatchCommands.cpp b/src/BatchCommands.cpp index 60dc787e1..141f84c75 100644 --- a/src/BatchCommands.cpp +++ b/src/BatchCommands.cpp @@ -308,17 +308,15 @@ MacroCommandsCatalog::MacroCommandsCatalog( const AudacityProject *project ) PluginManager & pm = PluginManager::Get(); EffectManager & em = EffectManager::Get(); { - const PluginDescriptor *plug = pm.GetFirstPlugin(PluginTypeEffect|PluginTypeAudacityCommand); - while (plug) - { - auto command = em.GetCommandIdentifier(plug->GetID()); + for (auto &plug + : pm.PluginsOfType(PluginTypeEffect|PluginTypeAudacityCommand)) { + auto command = em.GetCommandIdentifier(plug.GetID()); if (!command.empty()) commands.push_back( { - { command, plug->GetSymbol().Msgid() }, - plug->GetPluginType() == PluginTypeEffect ? + { command, plug.GetSymbol().Msgid() }, + plug.GetPluginType() == PluginTypeEffect ? XO("Effect") : XO("Menu Command (With Parameters)") } ); - plug = pm.GetNextPlugin(PluginTypeEffect|PluginTypeAudacityCommand); } } @@ -608,19 +606,12 @@ bool MacroCommands::HandleTextualCommand( CommandManager &commandManager, // Not one of the singleton commands. // We could/should try all the list-style commands. // instead we only try the effects. - PluginManager & pm = PluginManager::Get(); EffectManager & em = EffectManager::Get(); - const PluginDescriptor *plug = pm.GetFirstPlugin(PluginTypeEffect); - while (plug) - { - if (em.GetCommandIdentifier(plug->GetID()) == Str) - { + for (auto &plug : PluginManager::Get().PluginsOfType(PluginTypeEffect)) + if (em.GetCommandIdentifier(plug.GetID()) == Str) return EffectUI::DoEffect( - plug->GetID(), context, + plug.GetID(), context, EffectManager::kConfigured); - } - plug = pm.GetNextPlugin(PluginTypeEffect); - } return false; } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 908e1deab..c35143aef 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -202,6 +202,8 @@ list( APPEND SOURCES PlaybackSchedule.h PluginManager.cpp PluginManager.h + PluginRegistrationDialog.cpp + PluginRegistrationDialog.h Prefs.cpp Prefs.h Printing.cpp diff --git a/src/Menus.h b/src/Menus.h index e2ebc4107..d010db98c 100644 --- a/src/Menus.h +++ b/src/Menus.h @@ -22,7 +22,6 @@ class wxCommandEvent; class AudacityProject; class CommandContext; class CommandManager; -class PluginDescriptor; class Track; class TrackList; class ViewInfo; diff --git a/src/ModuleManager.cpp b/src/ModuleManager.cpp index a5d8b8804..799dfdfe3 100755 --- a/src/ModuleManager.cpp +++ b/src/ModuleManager.cpp @@ -31,7 +31,6 @@ i.e. an alternative to the usual interface, for Audacity. #include "FileNames.h" #include "MemoryX.h" -#include "PluginManager.h" #include "audacity/PluginInterface.h" @@ -403,6 +402,21 @@ ModuleManager & ModuleManager::Get() return *mInstance; } +wxString ModuleManager::GetPluginTypeString() +{ + return L"Module"; +} + +PluginID ModuleManager::GetID(ModuleInterface *module) +{ + return wxString::Format(wxT("%s_%s_%s_%s_%s"), + GetPluginTypeString(), + wxEmptyString, + module->GetVendor().Internal(), + module->GetSymbol().Internal(), + module->GetPath()); +} + bool ModuleManager::DiscoverProviders() { InitializeBuiltins(); @@ -433,20 +447,8 @@ bool ModuleManager::DiscoverProviders() FileNames::FindFilesInPathList(wxT("*.so"), pathList, provList); #endif - PluginManager & pm = PluginManager::Get(); - - for (int i = 0, cnt = provList.size(); i < cnt; i++) - { - ModuleInterface *module = LoadModule(provList[i]); - if (module) - { - // Register the provider - pm.RegisterPlugin(module); - - // Now, allow the module to auto-register children - module->AutoRegisterPlugins(pm); - } - } + for ( const auto &path : provList ) + LoadModule(path); #endif return true; @@ -454,8 +456,6 @@ bool ModuleManager::DiscoverProviders() void ModuleManager::InitializeBuiltins() { - PluginManager & pm = PluginManager::Get(); - for (auto moduleMain : builtinModuleList()) { ModuleInterfaceHandle module { @@ -466,13 +466,10 @@ void ModuleManager::InitializeBuiltins() { // Register the provider ModuleInterface *pInterface = module.get(); - const PluginID & id = pm.RegisterPlugin(pInterface); + auto id = GetID(pInterface); // Need to remember it mDynModules[id] = std::move(module); - - // Allow the module to auto-register children - pInterface->AutoRegisterPlugins(pm); } else { @@ -490,22 +487,6 @@ void ModuleInterfaceDeleter::operator() (ModuleInterface *pInterface) const } } -PluginPaths ModuleManager::FindPluginsForProvider(const PluginID & providerID, - const PluginPath & path) -{ - // Instantiate if it hasn't already been done - if (mDynModules.find(providerID) == mDynModules.end()) - { - // If it couldn't be created, just give up and return an empty list - if (!CreateProviderInstance(providerID, path)) - { - return {}; - } - } - - return mDynModules[providerID]->FindPluginPaths(PluginManager::Get()); -} - bool ModuleManager::RegisterEffectPlugin(const PluginID & providerID, const PluginPath & path, TranslatableString &errMsg) { errMsg = {}; @@ -530,26 +511,14 @@ ModuleInterface *ModuleManager::CreateProviderInstance(const PluginID & provider return nullptr; } -ComponentInterface *ModuleManager::CreateInstance(const PluginID & providerID, - const PluginPath & path) +std::unique_ptr ModuleManager::CreateInstance( + const PluginID & providerID, const PluginPath & path) { - if (mDynModules.find(providerID) == mDynModules.end()) - { - return NULL; - } - - return mDynModules[providerID]->CreateInstance(path); -} - -void ModuleManager::DeleteInstance(const PluginID & providerID, - ComponentInterface *instance) -{ - if (mDynModules.find(providerID) == mDynModules.end()) - { - return; - } - - mDynModules[providerID]->DeleteInstance(instance); + if (auto iter = mDynModules.find(providerID); + iter == mDynModules.end()) + return nullptr; + else + return iter->second->CreateInstance(path); } bool ModuleManager::IsProviderValid(const PluginID & WXUNUSED(providerID), @@ -581,4 +550,3 @@ bool ModuleManager::IsPluginValid(const PluginID & providerID, return mDynModules[providerID]->IsPluginValid(path, bFast); } - diff --git a/src/ModuleManager.h b/src/ModuleManager.h index f749deb71..8e6107f2a 100644 --- a/src/ModuleManager.h +++ b/src/ModuleManager.h @@ -12,7 +12,8 @@ #ifndef __AUDACITY_MODULEMANAGER_H__ #define __AUDACITY_MODULEMANAGER_H__ -#include +#include "MemoryX.h" +#include #include #include @@ -77,6 +78,12 @@ public: static ModuleManager & Get(); + // This string persists in configuration files + // So config compatibility will break if it is changed across Audacity versions + static wxString GetPluginTypeString(); + + static PluginID GetID(ModuleInterface *module); + private: static void FindModules(FilePaths &files); using DelayedErrors = @@ -92,13 +99,17 @@ public: // Can be called before Initialize() bool DiscoverProviders(); - PluginPaths FindPluginsForProvider(const PluginID & provider, const PluginPath & path); + // Supports range-for iteration + auto Providers() const + { return make_iterator_range(mDynModules.cbegin(), mDynModules.cend()); } + bool RegisterEffectPlugin(const PluginID & provider, const PluginPath & path, TranslatableString &errMsg); - ModuleInterface *CreateProviderInstance(const PluginID & provider, const PluginPath & path); - ComponentInterface *CreateInstance(const PluginID & provider, const PluginPath & path); - void DeleteInstance(const PluginID & provider, ComponentInterface *instance); + ModuleInterface *CreateProviderInstance( + const PluginID & provider, const PluginPath & path); + std::unique_ptr + CreateInstance(const PluginID & provider, const PluginPath & path); bool IsProviderValid(const PluginID & provider, const PluginPath & path); bool IsPluginValid(const PluginID & provider, const PluginPath & path, bool bFast); diff --git a/src/PluginManager.cpp b/src/PluginManager.cpp index f9f0c254b..b491ee733 100644 --- a/src/PluginManager.cpp +++ b/src/PluginManager.cpp @@ -25,1057 +25,18 @@ for shared and private configs - which need to move out. #include -#include // for wxUSE_* macros -#include -#include -#include -#include -#include -#include #include -#include -#include #include -#include -#include -#include "audacity/EffectInterface.h" #include "audacity/ModuleInterface.h" #include "AudacityFileConfig.h" +#include "Internat.h" // for macro XO #include "FileNames.h" +#include "MemoryX.h" #include "ModuleManager.h" #include "PlatformCompatibility.h" -#include "Prefs.h" -#include "ShuttleGui.h" -#include "wxFileNameWrapper.h" #include "widgets/AudacityMessageBox.h" -#include "widgets/ProgressDialog.h" - -#include - -// ============================================================================ -// -// -// -// ============================================================================ -#if wxUSE_ACCESSIBILITY -#include "widgets/WindowAccessible.h" - -class CheckListAx final : public WindowAccessible -{ -public: - CheckListAx(wxListCtrl * window); - - virtual ~ CheckListAx(); - - // Retrieves the address of an IDispatch interface for the specified child. - // All objects must support this property. - wxAccStatus GetChild( int childId, wxAccessible **child ) override; - - // Gets the number of children. - wxAccStatus GetChildCount( int *childCount ) override; - - // Gets the default action for this object (0) or > 0 (the action for a child). - // Return wxACC_OK even if there is no action. actionName is the action, or the empty - // string if there is no action. - // The retrieved string describes the action that is performed on an object, - // not what the object does as a result. For example, a toolbar button that prints - // a document has a default action of "Press" rather than "Prints the current document." - wxAccStatus GetDefaultAction( int childId, wxString *actionName ) override; - - // Returns the description for this object or a child. - wxAccStatus GetDescription( int childId, wxString *description ) override; - - // Gets the window with the keyboard focus. - // If childId is 0 and child is NULL, no object in - // this subhierarchy has the focus. - // If this object has the focus, child should be 'this'. - wxAccStatus GetFocus( int *childId, wxAccessible **child ) override; - - // Returns help text for this object or a child, similar to tooltip text. - wxAccStatus GetHelpText( int childId, wxString *helpText ) override; - - // Returns the keyboard shortcut for this object or child. - // Return e.g. ALT+K - wxAccStatus GetKeyboardShortcut( int childId, wxString *shortcut ) override; - - // Returns the rectangle for this object (id = 0) or a child element (id > 0). - // rect is in screen coordinates. - wxAccStatus GetLocation( wxRect& rect, int elementId ) override; - - // Gets the name of the specified object. - wxAccStatus GetName( int childId, wxString *name ) override; - - // Returns a role constant. - wxAccStatus GetRole( int childId, wxAccRole *role ) override; - - // Gets a variant representing the selected children - // of this object. - // Acceptable values: - // - a null variant (IsNull() returns TRUE) - // - a list variant (GetType() == wxT("list")) - // - an integer representing the selected child element, - // or 0 if this object is selected (GetType() == wxT("long")) - // - a "void*" pointer to a wxAccessible child object - wxAccStatus GetSelections( wxVariant *selections ) override; - - // Returns a state constant. - wxAccStatus GetState( int childId, long* state ) override; - - // Returns a localized string representing the value for the object - // or child. - wxAccStatus GetValue( int childId, wxString *strValue ) override; - - void SetSelected( int item, bool focused = true ); - -private: - wxListCtrl *mParent; - int mLastId; -}; - -CheckListAx::CheckListAx( wxListCtrl * window ) -: WindowAccessible( window ) -{ - mParent = window; - mLastId = -1; -} - -CheckListAx::~CheckListAx() -{ -} - -void CheckListAx::SetSelected( int item, bool focused ) -{ - if (mLastId != -1) - { - NotifyEvent( wxACC_EVENT_OBJECT_SELECTIONREMOVE, - mParent, - wxOBJID_CLIENT, - mLastId ); - mLastId = -1; - } - - if (item != -1) - { - if (focused) - { - NotifyEvent( wxACC_EVENT_OBJECT_FOCUS, - mParent, - wxOBJID_CLIENT, - item + 1 ); - } - - NotifyEvent( wxACC_EVENT_OBJECT_SELECTION, - mParent, - wxOBJID_CLIENT, - item + 1 ); - - mLastId = item + 1; - } -} - -// Retrieves the address of an IDispatch interface for the specified child. -// All objects must support this property. -wxAccStatus CheckListAx::GetChild( int childId, wxAccessible** child ) -{ - if( childId == wxACC_SELF ) - { - *child = this; - } - else - { - *child = NULL; - } - - return wxACC_OK; -} - -// Gets the number of children. -wxAccStatus CheckListAx::GetChildCount( int *childCount ) -{ - *childCount = mParent->GetItemCount(); - - return wxACC_OK; -} - -// Gets the default action for this object (0) or > 0 (the action for a child). -// Return wxACC_OK even if there is no action. actionName is the action, or the empty -// string if there is no action. -// The retrieved string describes the action that is performed on an object, -// not what the object does as a result. For example, a toolbar button that prints -// a document has a default action of "Press" rather than "Prints the current document." -wxAccStatus CheckListAx::GetDefaultAction( int WXUNUSED(childId), wxString *actionName ) -{ - actionName->clear(); - - return wxACC_OK; -} - -// Returns the description for this object or a child. -wxAccStatus CheckListAx::GetDescription( int WXUNUSED(childId), wxString *description ) -{ - description->clear(); - - return wxACC_OK; -} - -// Gets the window with the keyboard focus. -// If childId is 0 and child is NULL, no object in -// this subhierarchy has the focus. -// If this object has the focus, child should be 'this'. -wxAccStatus CheckListAx::GetFocus( int *childId, wxAccessible **child ) -{ - *childId = 0; - *child = this; - - return wxACC_OK; -} - -// Returns help text for this object or a child, similar to tooltip text. -wxAccStatus CheckListAx::GetHelpText( int WXUNUSED(childId), wxString *helpText ) -{ - helpText->clear(); - - return wxACC_OK; -} - -// Returns the keyboard shortcut for this object or child. -// Return e.g. ALT+K -wxAccStatus CheckListAx::GetKeyboardShortcut( int WXUNUSED(childId), wxString *shortcut ) -{ - shortcut->clear(); - - return wxACC_OK; -} - -// Returns the rectangle for this object (id = 0) or a child element (id > 0). -// rect is in screen coordinates. -wxAccStatus CheckListAx::GetLocation( wxRect& rect, int elementId ) -{ - if( elementId == wxACC_SELF ) - { - rect = mParent->GetRect(); - rect.SetPosition( mParent->GetParent()->ClientToScreen( rect.GetPosition() ) ); - } - else - { - if( elementId <= mParent->GetItemCount() ) - { - mParent->GetItemRect( elementId - 1, rect, wxLIST_RECT_LABEL ); - rect.SetPosition( mParent->ClientToScreen( rect.GetPosition() ) ); - } - } - - return wxACC_OK; -} - -// Gets the name of the specified object. -wxAccStatus CheckListAx::GetName( int WXUNUSED(childId), wxString *name ) -{ - *name = mParent->GetName(); - - return wxACC_OK; -} - -// Returns a role constant. -wxAccStatus CheckListAx::GetRole( int childId, wxAccRole *role ) -{ - if( childId == wxACC_SELF ) - { - *role = wxROLE_SYSTEM_LIST; - } - else - { - *role = wxROLE_SYSTEM_LISTITEM; - } - - return wxACC_OK; -} - -// Gets a variant representing the selected children -// of this object. -// Acceptable values: -// - a null variant (IsNull() returns TRUE) -// - a list variant (GetType() == wxT("list")) -// - an integer representing the selected child element, -// or 0 if this object is selected (GetType() == wxT("long")) -// - a "void*" pointer to a wxAccessible child object -wxAccStatus CheckListAx::GetSelections( wxVariant * WXUNUSED(selections) ) -{ - return wxACC_NOT_IMPLEMENTED; -} - -// Returns a state constant. -wxAccStatus CheckListAx::GetState( int childId, long *pState ) -{ - int flag = wxACC_STATE_SYSTEM_FOCUSABLE; - - if( childId == wxACC_SELF ) - { - flag |= wxACC_STATE_SYSTEM_FOCUSED; - } - else - { - wxListItem item; - - item.SetId( childId - 1 ); - item.SetState( wxLIST_STATE_FOCUSED | wxLIST_STATE_SELECTED ); - item.SetMask( wxLIST_MASK_STATE ); - - if( mParent->GetItem( item ) ) - { - flag |= wxACC_STATE_SYSTEM_SELECTABLE; - - long state = item.GetState(); - - if( state & wxLIST_STATE_FOCUSED ) - { - flag |= wxACC_STATE_SYSTEM_FOCUSED; - } - - if( state & wxLIST_STATE_SELECTED ) - { - flag |= wxACC_STATE_SYSTEM_SELECTED; - } - } - } - - *pState = flag; - - return wxACC_OK; -} - -// Returns a localized string representing the value for the object -// or child. -wxAccStatus CheckListAx::GetValue( int childId, wxString *strValue ) -{ - if( childId == 0 ) - { - return wxACC_OK; - } - else - { - *strValue = mParent->GetItemText( childId - 1 ); - } - - return wxACC_OK; -} - -#endif - -// ============================================================================ -// -// -// -// ============================================================================ - -enum -{ - STATE_Enabled, - STATE_Disabled, - STATE_New, - - STATE_COUNT -}; - -struct ItemData -{ - std::vector plugs; - wxString name; - PluginPath path; - int state; - bool valid; - int nameWidth; - int pathWidth; - int stateWidth; -}; - -using ItemDataMap = std::unordered_map; - -enum -{ - ID_ShowAll = 10000, - ID_ShowEnabled, - ID_ShowDisabled, - ID_ShowNew, - ID_List, - ID_ClearAll, - ID_SelectAll, - ID_Enable, - ID_Disable, -}; - -enum -{ - COL_Name, - COL_State, - COL_Path, - - COL_COUNT -}; - -class PluginRegistrationDialog final : public wxDialogWrapper -{ -public: - // constructors and destructors - PluginRegistrationDialog(wxWindow *parent, EffectType type); - -private: - void Populate(); - void PopulateOrExchange(ShuttleGui & S); - void RegenerateEffectsList(int iShowWhat); - void SetState(int i, bool toggle, bool state = true); - - static int wxCALLBACK SortCompare(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData); - int SortCompare(ItemData *item1, ItemData *item2); - - void OnChangedVisibility(wxCommandEvent & evt); - void OnSort(wxListEvent & evt); - void DoSort( int col ); - void OnListChar(wxKeyEvent & evt); - void OnOK(wxCommandEvent & evt); - void OnCancel(wxCommandEvent & evt); - void OnSelectAll(wxCommandEvent & evt); - void OnClearAll(wxCommandEvent & evt); - void OnEnable(wxCommandEvent & evt); - void OnDisable(wxCommandEvent & evt); - -private: - EffectType mType; - int mFilter; - - wxArrayString mStates; - ItemDataMap mItems; - - int mSortColumn; - int mSortDirection; - - PluginPath mLongestPath; - - wxListCtrl *mEffects; -#if wxUSE_ACCESSIBILITY - CheckListAx *mAx; -#endif - - DECLARE_EVENT_TABLE() -}; - -BEGIN_EVENT_TABLE(PluginRegistrationDialog, wxDialogWrapper) - EVT_LIST_COL_CLICK(ID_List, PluginRegistrationDialog::OnSort) - EVT_BUTTON(wxID_OK, PluginRegistrationDialog::OnOK) - EVT_BUTTON(wxID_CANCEL, PluginRegistrationDialog::OnCancel) - EVT_BUTTON(ID_ClearAll, PluginRegistrationDialog::OnClearAll) - EVT_BUTTON(ID_SelectAll, PluginRegistrationDialog::OnSelectAll) - EVT_BUTTON(ID_Enable, PluginRegistrationDialog::OnEnable) - EVT_BUTTON(ID_Disable, PluginRegistrationDialog::OnDisable) - EVT_RADIOBUTTON(ID_ShowAll, PluginRegistrationDialog::OnChangedVisibility) - EVT_RADIOBUTTON(ID_ShowEnabled, PluginRegistrationDialog::OnChangedVisibility) - EVT_RADIOBUTTON(ID_ShowDisabled, PluginRegistrationDialog::OnChangedVisibility) - EVT_RADIOBUTTON(ID_ShowNew, PluginRegistrationDialog::OnChangedVisibility) -END_EVENT_TABLE() - -PluginRegistrationDialog::PluginRegistrationDialog(wxWindow *parent, EffectType type) -: wxDialogWrapper(parent, - wxID_ANY, - XO("Manage Plug-ins"), - wxDefaultPosition, wxDefaultSize, - wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) -{ - mType = type; - mEffects = NULL; - SetName(); - - mStates.resize(STATE_COUNT); - mStates[STATE_Enabled] = _("Enabled"); - mStates[STATE_Disabled] = _("Disabled"); - mStates[STATE_New] = _("New"); - - mSortColumn = COL_Name; - mSortDirection = 1; - - Populate(); - - DoSort( mSortColumn ); -} - -void PluginRegistrationDialog::Populate() -{ - //------------------------- Main section -------------------- - ShuttleGui S(this, eIsCreating); - PopulateOrExchange(S); - // ----------------------- End of main section -------------- -} - -/// Defines the dialog and does data exchange with it. -void PluginRegistrationDialog::PopulateOrExchange(ShuttleGui &S) -{ - S.StartVerticalLay(true); - { - /*i18n-hint: The dialog shows a list of plugins with check-boxes - beside each one.*/ -// S.StartStatic(XO("Effects"), true); - S.StartVerticalLay(); - { - S.StartHorizontalLay(wxEXPAND, 0); - { - S.StartHorizontalLay(wxALIGN_LEFT, 0); - { - S.AddPrompt(XXO("Select effects, click the Enable or Disable button, then click OK.")); - } - S.EndHorizontalLay(); - - S.StartHorizontalLay(wxCENTER, 1); - { - S.AddSpace(1); - } - S.EndHorizontalLay(); - - S.StartHorizontalLay(wxALIGN_NOT | wxALIGN_LEFT, 0); - { - wxRadioButton *rb; - - /* i18n-hint: This is before radio buttons selecting which effects to show */ - S.AddPrompt(XXO("Show:")); - rb = S.Id(ID_ShowAll) - /* i18n-hint: Radio button to show all effects */ - .Name(XO("Show all")) - /* i18n-hint: Radio button to show all effects */ - .AddRadioButton(XXO("&All")); -#if wxUSE_ACCESSIBILITY - // so that name can be set on a standard control - rb->SetAccessible(safenew WindowAccessible(rb)); -#endif - - rb = S.Id(ID_ShowDisabled) - /* i18n-hint: Radio button to show just the currently disabled effects */ - .Name(XO("Show disabled")) - /* i18n-hint: Radio button to show just the currently disabled effects */ - .AddRadioButtonToGroup(XXO("D&isabled")); -#if wxUSE_ACCESSIBILITY - // so that name can be set on a standard control - rb->SetAccessible(safenew WindowAccessible(rb)); -#endif - - rb = S.Id(ID_ShowEnabled) - /* i18n-hint: Radio button to show just the currently enabled effects */ - .Name(XO("Show enabled")) - /* i18n-hint: Radio button to show just the currently enabled effects */ - .AddRadioButtonToGroup(XXO("E&nabled")); -#if wxUSE_ACCESSIBILITY - // so that name can be set on a standard control - rb->SetAccessible(safenew WindowAccessible(rb)); -#endif - - rb = S.Id(ID_ShowNew) - /* i18n-hint: Radio button to show just the newly discovered effects */ - .Name(XO("Show new")) - /* i18n-hint: Radio button to show just the newly discovered effects */ - .AddRadioButtonToGroup(XXO("Ne&w")); -#if wxUSE_ACCESSIBILITY - // so that name can be set on a standard control - rb->SetAccessible(safenew WindowAccessible(rb)); -#endif - } - S.EndHorizontalLay(); - } - S.EndHorizontalLay(); - - mEffects = S.Id(ID_List) - .Style(wxSUNKEN_BORDER | wxLC_REPORT | wxLC_HRULES | wxLC_VRULES ) - .ConnectRoot(wxEVT_KEY_DOWN, - &PluginRegistrationDialog::OnListChar) - .AddListControlReportMode({ XO("Name"), XO("State"), XO("Path") }); -#if wxUSE_ACCESSIBILITY - mEffects->SetAccessible(mAx = safenew CheckListAx(mEffects)); -#endif - - S.StartHorizontalLay(wxALIGN_LEFT | wxEXPAND, 0); - { - S.Id(ID_SelectAll).AddButton(XXO("&Select All")); - S.Id(ID_ClearAll).AddButton(XXO("C&lear All")); - - S.StartHorizontalLay(wxALIGN_CENTER); - { - S.AddSpace(1); - } - S.EndHorizontalLay(); - - S.Id(ID_Enable).AddButton(XXO("&Enable")); - S.Id(ID_Disable).AddButton(XXO("&Disable")); - } - S.EndHorizontalLay(); - } -// S.EndStatic(); - S.EndVerticalLay(); - - S.AddStandardButtons(eOkButton | eCancelButton); - } - S.EndVerticalLay(); - - std::vector colWidths; - for (int i = 0, cnt = mEffects->GetColumnCount(); i < cnt; i++) - { - colWidths.push_back(0); - } - - for (int i = 0, cnt = mStates.size(); i < cnt; i++) - { - int x; - mEffects->GetTextExtent(mStates[i], &x, NULL); - colWidths[COL_State] = wxMax(colWidths[COL_State], x + 4); // 2 pixel margin on each side - } - - PluginManager & pm = PluginManager::Get(); - for (PluginMap::iterator iter = pm.mPlugins.begin(); iter != pm.mPlugins.end(); ++iter) - { - PluginDescriptor & plug = iter->second; - - PluginType plugType = plug.GetPluginType(); - if (plugType != PluginTypeEffect && plugType != PluginTypeStub) - { - continue; - } - - const auto &path = plug.GetPath(); - ItemData & item = mItems[path]; // will create NEW entry - item.plugs.push_back(&plug); - item.path = path; - item.state = plug.IsEnabled() ? STATE_Enabled : STATE_Disabled; - item.valid = plug.IsValid(); - - if (plugType == PluginTypeEffect) - { - item.name = plug.GetSymbol().Translation(); - } - // This is not right and will not work when other plugin types are added. - // But it's presumed that the plugin manager dialog will be fully developed - // by then. - else if (plugType == PluginTypeStub) - { - wxFileName fname { path }; - item.name = fname.GetName().Trim(false).Trim(true); - if (!item.valid) - { - item.state = STATE_New; - } - } - - int x; - mEffects->GetTextExtent(item.name, &x, NULL); - colWidths[COL_Name] = wxMax(colWidths[COL_Name], x); - - mEffects->GetTextExtent(item.path, &x, NULL); - if (x > colWidths[COL_Path]) - { - mLongestPath = item.path; - } - colWidths[COL_Path] = wxMax(colWidths[COL_Path], x); - } - - wxRect r = wxGetClientDisplayRect(); - - int maxW = 0; - for (int i = 0, cnt = mEffects->GetColumnCount(); i < cnt; i++) - { - int w = colWidths[i] + /* fudge */ 10; - mEffects->SetColumnWidth(i, w); - maxW += w; - } - - // Keep dialog from getting too wide - int w = r.GetWidth() - (GetClientSize().GetWidth() - mEffects->GetSize().GetWidth()); - mEffects->SetMinSize({ std::min(maxW, w), 200 }); - mEffects->SetMaxSize({ w, -1 }); - - RegenerateEffectsList(ID_ShowAll); - - Layout(); - Fit(); - - wxSize sz = GetSize(); - sz.SetWidth(wxMin(sz.GetWidth(), r.GetWidth())); - sz.SetHeight(wxMin(sz.GetHeight(), r.GetHeight())); - SetMinSize(sz); - - // Parent window is usually not there yet, so centre on screen rather than on parent. - CenterOnScreen(); - - if (mEffects->GetItemCount() > 0) - { - // Make sure first item is selected/focused. - mEffects->SetFocus(); - mEffects->SetItemState(0, wxLIST_STATE_FOCUSED | wxLIST_STATE_SELECTED, wxLIST_STATE_FOCUSED | wxLIST_STATE_SELECTED); -#if wxUSE_ACCESSIBILITY - mAx->SetSelected(0); -#endif - } - -} - -void PluginRegistrationDialog::RegenerateEffectsList(int filter) -{ - mFilter = filter; - - mEffects->DeleteAllItems(); - - int i = 0; - for (ItemDataMap::iterator iter = mItems.begin(); iter != mItems.end(); ++iter) - { - ItemData & item = iter->second; - bool add = false; - - switch (mFilter) - { - case ID_ShowAll: - add = true; - break; - case ID_ShowNew: - if (item.state == STATE_New) - { - add = true; - } - break; - case ID_ShowEnabled: - if (item.state == STATE_Enabled) - { - add = true; - } - break; - case ID_ShowDisabled: - if (item.state == STATE_Disabled) - { - add = true; - } - break; - } - - if (add) - { - mEffects->InsertItem(i, item.name); - mEffects->SetItem(i, COL_State, mStates[item.state]); - mEffects->SetItem(i, COL_Path, item.path); - mEffects->SetItemPtrData(i, (wxUIntPtr) &item); - - ++i; - } - } - - mEffects->SortItems(SortCompare, (wxUIntPtr) this); - - if (mEffects->GetItemCount() > 0) - { - // Make sure first item is selected/focused. -// mEffects->SetFocus(); - mEffects->SetItemState(0, wxLIST_STATE_FOCUSED|wxLIST_STATE_SELECTED, wxLIST_STATE_FOCUSED|wxLIST_STATE_SELECTED); -#if wxUSE_ACCESSIBILITY - mAx->SetSelected(0, false); -#endif - } -} - -void PluginRegistrationDialog::SetState(int i, bool toggle, bool state) -{ - wxListItem li; - - li.m_mask = wxLIST_MASK_DATA; - li.m_itemId = i; - - mEffects->GetItem(li); - - ItemData *item = (ItemData *) li.m_data; - - // If changing the state of a "New" (stub) entry, then we mark it as valid - // since it will either be registered if "Enabled" or ignored if "Disabled". - if (item->state == STATE_New) - { - item->valid = true; - } - - if (toggle) - { - item->state = item->state == STATE_Enabled ? STATE_Disabled : STATE_Enabled; - } - else - { - item->state = state; - } - - if (mFilter == ID_ShowNew && item->state != STATE_New) - { - mEffects->DeleteItem(i); - } - else if (mFilter == ID_ShowDisabled && item->state != STATE_Disabled) - { - mEffects->DeleteItem(i); - } - else if (mFilter == ID_ShowEnabled && item->state != STATE_Enabled) - { - mEffects->DeleteItem(i); - } - else - { - mEffects->SetItem(i, COL_State, mStates[item->state]); -#if wxUSE_ACCESSIBILITY - mAx->SetSelected(i); -#endif - } -} - -int wxCALLBACK PluginRegistrationDialog::SortCompare(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData) -{ - PluginRegistrationDialog *dlg = (PluginRegistrationDialog *) sortData; - ItemData *i1 = (ItemData *) item1; - ItemData *i2 = (ItemData *) item2; - - return dlg->SortCompare(i1, i2); -} - -int PluginRegistrationDialog::SortCompare(ItemData *item1, ItemData *item2) -{ - // This function is a three-valued comparator - - wxString *str1; - wxString *str2; - - switch (mSortColumn) - { - case COL_Name: - str1 = &item1->name; - str2 = &item2->name; - break; - case COL_State: - str1 = &mStates[item1->state]; - str2 = &mStates[item2->state]; - break; - case COL_Path: - str1 = &item1->path; - str2 = &item2->path; - break; - default: - return 0; - } - - return str2->CmpNoCase(*str1) * mSortDirection; -} - -void PluginRegistrationDialog::OnChangedVisibility(wxCommandEvent & evt) -{ - // Go and show the relevant items. - RegenerateEffectsList(evt.GetId()); -} - -void PluginRegistrationDialog::OnSort(wxListEvent & evt) -{ - int col = evt.GetColumn(); - DoSort( col ); -} - -void PluginRegistrationDialog::DoSort( int col ) -{ - if (col != mSortColumn) - { - mSortDirection = 1; - } - else - { - mSortDirection *= -1; - } - - mSortColumn = col; - mEffects->SortItems(SortCompare, (wxUIntPtr) this); - - // Without a refresh, wxMac doesn't redisplay the list properly after a sort - mEffects->Refresh(); -} - -void PluginRegistrationDialog::OnListChar(wxKeyEvent & evt) -{ - switch (evt.GetKeyCode()) - { - case WXK_SPACE: - { - int item = mEffects->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_FOCUSED); - if (item != wxNOT_FOUND) - { - SetState(item, true); - } - } - break; - - case WXK_RETURN: - // Don't know why wxListCtrls prevent default dialog action, - // but they do, so handle it. - EmulateButtonClickIfPresent(GetAffirmativeId()); - break; - - default: - evt.Skip(); - break; - } -} - -void PluginRegistrationDialog::OnSelectAll(wxCommandEvent & WXUNUSED(evt)) -{ - for (int i = 0, cnt = mEffects->GetItemCount(); i < cnt; i++) - { - mEffects->SetItemState(i, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED); - } -} - -void PluginRegistrationDialog::OnClearAll(wxCommandEvent & WXUNUSED(evt)) -{ - for (int i = 0, cnt = mEffects->GetItemCount(); i < cnt; i++) - { - mEffects->SetItemState(i, 0, wxLIST_STATE_SELECTED); - } -} - -void PluginRegistrationDialog::OnEnable(wxCommandEvent & WXUNUSED(evt)) -{ - std::vector items; - - { - long i = mEffects->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); - while (i != wxNOT_FOUND) - { - items.insert(items.begin(), i); - i = mEffects->GetNextItem(i, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); - } - } - - for (size_t i = 0, cnt = items.size(); i < cnt; i++) - { - SetState(items[i], false, STATE_Enabled); - } -} - -void PluginRegistrationDialog::OnDisable(wxCommandEvent & WXUNUSED(evt)) -{ - std::vector items; - - { - long i = mEffects->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); - while (i != wxNOT_FOUND) - { - items.insert(items.begin(), i); - i = mEffects->GetNextItem(i, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); - } - } - - for (size_t i = 0, cnt = items.size(); i < cnt; i++) - { - SetState(items[i], false, STATE_Disabled); - } -} - -void PluginRegistrationDialog::OnOK(wxCommandEvent & WXUNUSED(evt)) -{ - PluginManager & pm = PluginManager::Get(); - ModuleManager & mm = ModuleManager::Get(); - - int enableCount = 0; - for (ItemDataMap::iterator iter = mItems.begin(); iter != mItems.end(); ++iter) - { - ItemData & item = iter->second; - wxString path = item.path; - - if (item.state == STATE_Enabled && item.plugs[0]->GetPluginType() == PluginTypeStub) - { - enableCount++; - } - } - - wxString last3 = mLongestPath + wxT("\n") + - mLongestPath + wxT("\n") + - mLongestPath + wxT("\n"); - - auto msg = XO("Enabling effects or commands:\n\n%s").Format( last3 ); - - // Make sure the progress dialog is deleted before we call EndModal() or - // we will leave the project window in an unusable state on OSX. - // See bug #1192. - { - ProgressDialog progress{ - Verbatim( GetTitle() ), msg, pdlgHideStopButton }; - progress.CenterOnParent(); - - int i = 0; - for (ItemDataMap::iterator iter = mItems.begin(); iter != mItems.end(); ++iter) - { - ItemData & item = iter->second; - wxString path = item.path; - - if (item.state == STATE_Enabled && item.plugs[0]->GetPluginType() == PluginTypeStub) - { - last3 = last3.AfterFirst(wxT('\n')) + item.path + wxT("\n"); - auto status = progress.Update(++i, enableCount, - XO("Enabling effect or command:\n\n%s").Format( last3 )); - if (status == ProgressResult::Cancelled) - { - break; - } - - TranslatableString errMsgs; - - // Try to register the plugin via each provider until one succeeds - for (size_t j = 0, cntj = item.plugs.size(); j < cntj; j++) - { - TranslatableString errMsg; - if (mm.RegisterEffectPlugin(item.plugs[j]->GetProviderID(), path, - errMsg)) - { - for (size_t k = 0, cntk = item.plugs.size(); k < cntk; k++) - { - pm.mPlugins.erase(item.plugs[k]->GetProviderID() + wxT("_") + path); - } - // Bug 1893. We've found a provider that works. - // Error messages from any that failed are no longer useful. - errMsgs = {}; - break; - } - else - { - if (!errMsgs.empty()) - errMsgs.Join( errMsg, '\n' ); - else - errMsgs = errMsg; - } - } - if (!errMsgs.empty()) - AudacityMessageBox( - XO("Effect or Command at %s failed to register:\n%s") - .Format( path, errMsgs ) ); - } - else if (item.state == STATE_New) - { - for (size_t j = 0, cnt = item.plugs.size(); j < cnt; j++) - { - item.plugs[j]->SetValid(false); - } - } - else if (item.state != STATE_New) - { - for (size_t j = 0, cnt = item.plugs.size(); j < cnt; j++) - { - item.plugs[j]->SetEnabled(item.state == STATE_Enabled); - item.plugs[j]->SetValid(item.valid); - } - } - } - - pm.Save(); - } - - EndModal(wxID_OK); -} - -void PluginRegistrationDialog::OnCancel(wxCommandEvent & WXUNUSED(evt)) -{ - EndModal(wxID_CANCEL); -} - - /////////////////////////////////////////////////////////////////////////////// // @@ -1088,7 +49,7 @@ PluginDescriptor::PluginDescriptor() mPluginType = PluginTypeNone; mEnabled = false; mValid = false; - mInstance = NULL; + mInstance = nullptr; mEffectType = EffectTypeNone; mEffectInteractive = false; @@ -1100,21 +61,13 @@ PluginDescriptor::PluginDescriptor() PluginDescriptor::~PluginDescriptor() { - DeleteInstance(); } -void PluginDescriptor::DeleteInstance() -{ - if (mInstance) - { - ModuleManager::Get().DeleteInstance(GetProviderID(), mInstance); - mInstance = nullptr; - } -} +PluginDescriptor &PluginDescriptor::operator =(PluginDescriptor &&) = default; bool PluginDescriptor::IsInstantiated() const { - return mInstance != NULL; + return mInstance != nullptr; } ComponentInterface *PluginDescriptor::GetInstance() @@ -1122,29 +75,21 @@ ComponentInterface *PluginDescriptor::GetInstance() if (!mInstance) { if (GetPluginType() == PluginTypeModule) - { mInstance = ModuleManager::Get().CreateProviderInstance(GetID(), GetPath()); - } else { - mInstance = ModuleManager::Get().CreateInstance(GetProviderID(), GetPath()); + muInstance = ModuleManager::Get().CreateInstance(GetProviderID(), GetPath()); + mInstance = muInstance.get(); } } return mInstance; } -void PluginDescriptor::SetInstance(ComponentInterface *instance) +void PluginDescriptor::SetInstance(std::unique_ptr instance) { - if (mInstance && mInstance != instance) - { - // Be sure not to leak resources!! - DeleteInstance(); - } - - mInstance = instance; - - return; + muInstance = std::move(instance); + mInstance = muInstance.get(); } PluginType PluginDescriptor::GetPluginType() const @@ -1404,7 +349,7 @@ const PluginID &PluginManagerInterface::AudacityCommandRegistrationCallback( return empty; } -RegistryPath PluginManager::GetPluginEnabledSetting( const PluginID &ID ) +RegistryPath PluginManager::GetPluginEnabledSetting( const PluginID &ID ) const { auto pPlugin = GetPlugin( ID ); if ( pPlugin ) @@ -1413,7 +358,7 @@ RegistryPath PluginManager::GetPluginEnabledSetting( const PluginID &ID ) } RegistryPath PluginManager::GetPluginEnabledSetting( - const PluginDescriptor &desc ) + const PluginDescriptor &desc ) const { switch ( desc.GetPluginType() ) { case PluginTypeModule: { @@ -1438,18 +383,14 @@ RegistryPath PluginManager::GetPluginEnabledSetting( bool PluginManager::IsPluginRegistered( const PluginPath &path, const TranslatableString *pName) { - for (PluginMap::iterator iter = mPlugins.begin(); iter != mPlugins.end(); ++iter) - { - auto &descriptor = iter->second; - if (descriptor.GetPath() == path) - { + for (auto &pair : mPlugins) { + if (auto &descriptor = pair.second; descriptor.GetPath() == path) { if (pName) descriptor.SetSymbol( { descriptor.GetSymbol().Internal(), *pName }); return true; } } - return false; } @@ -1781,8 +722,13 @@ void PluginManager::Initialize() // And force load of setting to verify it's accessible GetSettings(); - // Then look for providers (they may autoregister plugins) - ModuleManager::Get().DiscoverProviders(); + auto &mm = ModuleManager::Get(); + mm.DiscoverProviders(); + for (const auto &[id, module] : mm.Providers()) { + RegisterPlugin(module.get()); + // Allow the module to auto-register children + module->AutoRegisterPlugins(*this); + } // And finally check for updates #ifndef EXPERIMENTAL_EFFECT_MANAGEMENT @@ -1822,12 +768,9 @@ bool PluginManager::DropFile(const wxString &fileName) auto &mm = ModuleManager::Get(); const wxFileName src{ fileName }; - for (const PluginDescriptor *plug = GetFirstPlugin(PluginTypeModule); - plug; - plug = GetNextPlugin(PluginTypeModule)) - { + for (auto &plug : PluginsOfType(PluginTypeModule)) { auto module = static_cast - (mm.CreateProviderInstance(plug->GetID(), plug->GetPath())); + (mm.CreateProviderInstance(plug.GetID(), plug.GetPath())); if (! module) continue; @@ -2072,12 +1015,8 @@ void PluginManager::LoadGroup(FileConfig *pRegistry, PluginType type) groupName = ConvertID(groupName); // Bypass group if the ID is already in use - if (mPlugins.find(groupName) != mPlugins.end()) - { - pRegistry->SetPath(wxT("..")); - + if (mPlugins.count(groupName)) continue; - } // Set the ID and type plug.SetID(groupName); @@ -2087,10 +1026,8 @@ void PluginManager::LoadGroup(FileConfig *pRegistry, PluginType type) if (!pRegistry->Read(KEY_PROVIDERID, &strVal, wxEmptyString)) { // Bypass group if the provider isn't valid - if (!strVal.empty() && mPlugins.find(strVal) == mPlugins.end()) - { + if (!strVal.empty() && !mPlugins.count(strVal)) continue; - } } plug.SetProviderID(PluginID(strVal)); @@ -2273,7 +1210,7 @@ void PluginManager::LoadGroup(FileConfig *pRegistry, PluginType type) } // Everything checked out...accept the plugin - mPlugins[groupName] = plug; + mPlugins[groupName] = std::move(plug); } return; @@ -2313,9 +1250,8 @@ void PluginManager::Save() void PluginManager::SaveGroup(FileConfig *pRegistry, PluginType type) { wxString group = GetPluginTypeString(type); - for (PluginMap::iterator iter = mPlugins.begin(); iter != mPlugins.end(); ++iter) - { - PluginDescriptor & plug = iter->second; + for (auto &pair : mPlugins) { + auto & plug = pair.second; if (plug.GetPluginType() != type) { @@ -2399,21 +1335,14 @@ void PluginManager::SaveGroup(FileConfig *pRegistry, PluginType type) // and built-ins. void PluginManager::CheckForUpdates(bool bFast) { - // Get ModuleManager reference ModuleManager & mm = ModuleManager::Get(); - wxArrayString pathIndex; - for (PluginMap::iterator iter = mPlugins.begin(); iter != mPlugins.end(); ++iter) - { - PluginDescriptor & plug = iter->second; + for (auto &pair : mPlugins) { + auto &plug = pair.second; // Bypass 2.1.0 placeholders...remove this after a few releases past 2.1.0 - if (plug.GetPluginType() == PluginTypeNone) - { - continue; - } - - pathIndex.push_back(plug.GetPath().BeforeFirst(wxT(';'))); + if (plug.GetPluginType() != PluginTypeNone) + pathIndex.push_back(plug.GetPath().BeforeFirst(wxT(';'))); } // Check all known plugins to ensure they are still valid and scan for NEW ones. @@ -2427,9 +1356,8 @@ void PluginManager::CheckForUpdates(bool bFast) // // When the user enables the plugin, each provider that reported it will be asked // to register the plugin. - for (PluginMap::iterator iter = mPlugins.begin(); iter != mPlugins.end(); ++iter) - { - PluginDescriptor & plug = iter->second; + for (auto &pair : mPlugins) { + auto &plug = pair.second; const PluginID & plugID = plug.GetID(); const wxString & plugPath = plug.GetPath(); PluginType plugType = plug.GetPluginType(); @@ -2454,7 +1382,9 @@ void PluginManager::CheckForUpdates(bool bFast) else { // Collect plugin paths - auto paths = mm.FindPluginsForProvider(plugID, plugPath); + PluginPaths paths; + if (auto provider = mm.CreateProviderInstance( plugID, plugPath ) ) + paths = provider->FindPluginPaths( *this ); for (size_t i = 0, cnt = paths.size(); i < cnt; i++) { wxString path = paths[i].BeforeFirst(wxT(';'));; @@ -2487,19 +1417,13 @@ void PluginManager::CheckForUpdates(bool bFast) return; } -bool PluginManager::ShowManager(wxWindow *parent, EffectType type) -{ - CheckForUpdates(); - - PluginRegistrationDialog dlg(parent, type); - return dlg.ShowModal() == wxID_OK; -} - // Here solely for the purpose of Nyquist Workbench until // a better solution is devised. -const PluginID & PluginManager::RegisterPlugin(EffectDefinitionInterface *effect, PluginType type) +const PluginID & PluginManager::RegisterPlugin( + std::unique_ptr effect, PluginType type) { - PluginDescriptor & plug = CreatePlugin(GetID(effect), effect, type); + PluginDescriptor & plug = + CreatePlugin(GetID(effect.get()), effect.get(), type); plug.SetEffectType(effect->GetType()); plug.SetEffectFamily(effect->GetFamily().Internal()); @@ -2508,7 +1432,7 @@ const PluginID & PluginManager::RegisterPlugin(EffectDefinitionInterface *effect plug.SetEffectRealtime(effect->SupportsRealtime()); plug.SetEffectAutomatable(effect->SupportsAutomation()); - plug.SetInstance(effect); + plug.SetInstance(std::move(effect)); plug.SetEffectLegacy(true); plug.SetEnabled(true); plug.SetValid(true); @@ -2516,194 +1440,123 @@ const PluginID & PluginManager::RegisterPlugin(EffectDefinitionInterface *effect return plug.GetID(); } -// Here solely for the purpose of Nyquist Workbench until -// a better solution is devised. void PluginManager::UnregisterPlugin(const PluginID & ID) { - if (mPlugins.find(ID) == mPlugins.end()) - { - return; - } - mPlugins.erase(ID); } int PluginManager::GetPluginCount(PluginType type) { - int num = 0; - - for (PluginMap::iterator iter = mPlugins.begin(); iter != mPlugins.end(); ++iter) - { - if (iter->second.GetPluginType() == type) - { - num++; - } - } - - return num; + return count_if(mPlugins.begin(), mPlugins.end(), [type](auto &pair){ + return pair.second.GetPluginType() == type; }); } -const PluginDescriptor *PluginManager::GetPlugin(const PluginID & ID) +const PluginDescriptor *PluginManager::GetPlugin(const PluginID & ID) const { - if (mPlugins.find(ID) == mPlugins.end()) - { - return NULL; - } - - return &mPlugins[ID]; + if (auto iter = mPlugins.find(ID); iter == mPlugins.end()) + return nullptr; + else + return &iter->second; } -const PluginDescriptor *PluginManager::GetFirstPlugin(int type) +void PluginManager::Iterator::Advance(bool incrementing) { - for (mPluginsIter = mPlugins.begin(); mPluginsIter != mPlugins.end(); ++mPluginsIter) - { - PluginDescriptor & plug = mPluginsIter->second; - PluginType plugType = plug.GetPluginType(); - if( plug.IsValid() && plug.IsEnabled() && ((plugType & type) != 0)) - { - bool familyEnabled = true; - if( (plugType & PluginTypeEffect) != 0) { + const auto end = mPm.mPlugins.end(); + if (incrementing && mIterator != end) + ++mIterator; + bool all = mPluginType == PluginTypeNone && mEffectType == EffectTypeNone; + for (; mIterator != end; ++mIterator) { + auto &plug = mIterator->second; + if (!all && !(plug.IsValid() && plug.IsEnabled())) + continue; + auto plugType = plug.GetPluginType(); + if ((mPluginType == PluginTypeNone || (plugType & mPluginType)) && + (mEffectType == EffectTypeNone || plug.GetEffectType() == mEffectType)) { + if (!all && (plugType & PluginTypeEffect)) { // This preference may be written by EffectsPrefs - auto setting = GetPluginEnabledSetting( plug ); - familyEnabled = setting.empty() - ? true - : gPrefs->Read( setting, true ); + auto setting = mPm.GetPluginEnabledSetting( plug ); + if (!(setting.empty() || gPrefs->Read( setting, true ))) + continue; } - if (familyEnabled) - return &mPluginsIter->second; + // Pause iteration at this match + break; } } - - return NULL; } -const PluginDescriptor *PluginManager::GetNextPlugin(int type) -{ - while (++mPluginsIter != mPlugins.end()) - { - PluginDescriptor & plug = mPluginsIter->second; - PluginType plugType = plug.GetPluginType(); - if( plug.IsValid() && plug.IsEnabled() && ((plugType & type) != 0)) - { - bool familyEnabled = true; - if( (plugType & PluginTypeEffect) != 0) { - // This preference may be written by EffectsPrefs - auto setting = GetPluginEnabledSetting( plug ); - familyEnabled = setting.empty() - ? true - : gPrefs->Read( setting, true ); - } - if (familyEnabled) - return &mPluginsIter->second; - } - } - - return NULL; +PluginManager::Iterator::Iterator(PluginManager &manager) +: mPm{ manager } +, mIterator{ manager.mPlugins.begin() } +{ } -const PluginDescriptor *PluginManager::GetFirstPluginForEffectType(EffectType type) +PluginManager::Iterator::Iterator(PluginManager &manager, int type) +: mPm{ manager } +, mIterator{ manager.mPlugins.begin() } +, mPluginType{ type } { - for (mPluginsIter = mPlugins.begin(); mPluginsIter != mPlugins.end(); ++mPluginsIter) - { - PluginDescriptor & plug = mPluginsIter->second; - - bool familyEnabled; - // This preference may be written by EffectsPrefs - auto setting = GetPluginEnabledSetting( plug ); - familyEnabled = setting.empty() - ? true - : gPrefs->Read( setting, true ); - if (plug.IsValid() && plug.IsEnabled() && plug.GetEffectType() == type && familyEnabled) - { - return &plug; - } - } - - return NULL; + Advance(false); } -const PluginDescriptor *PluginManager::GetNextPluginForEffectType(EffectType type) +PluginManager::Iterator::Iterator(PluginManager &manager, EffectType type) +: mPm{ manager } +, mIterator{ manager.mPlugins.begin() } +, mEffectType{ type } { - while (++mPluginsIter != mPlugins.end()) - { - PluginDescriptor & plug = mPluginsIter->second; - bool familyEnabled; - // This preference may be written by EffectsPrefs - auto setting = GetPluginEnabledSetting( plug ); - familyEnabled = setting.empty() - ? true - : gPrefs->Read( setting, true ); - if (plug.IsValid() && plug.IsEnabled() && plug.GetEffectType() == type && familyEnabled) - { - return &plug; - } - } + Advance(false); +} - return NULL; +auto PluginManager::Iterator::operator ++() -> Iterator & +{ + Advance(true); + return *this; } bool PluginManager::IsPluginEnabled(const PluginID & ID) { - if (mPlugins.find(ID) == mPlugins.end()) - { + if (auto iter = mPlugins.find(ID); iter == mPlugins.end()) return false; - } - - return mPlugins[ID].IsEnabled(); + else + return iter->second.IsEnabled(); } void PluginManager::EnablePlugin(const PluginID & ID, bool enable) { - if (mPlugins.find(ID) == mPlugins.end()) - { + if (auto iter = mPlugins.find(ID); iter == mPlugins.end()) return; - } - - return mPlugins[ID].SetEnabled(enable); + else + iter->second.SetEnabled(enable); } const ComponentInterfaceSymbol & PluginManager::GetSymbol(const PluginID & ID) { - if (mPlugins.find(ID) == mPlugins.end()) - { + if (auto iter = mPlugins.find(ID); iter == mPlugins.end()) { static ComponentInterfaceSymbol empty; return empty; } - - return mPlugins[ID].GetSymbol(); + else + return iter->second.GetSymbol(); } ComponentInterface *PluginManager::GetInstance(const PluginID & ID) { - if (mPlugins.find(ID) == mPlugins.end()) - { - return NULL; - } + if (auto iter = mPlugins.find(ID); iter == mPlugins.end()) + return nullptr; + else { + auto &plug = iter->second; - PluginDescriptor & plug = mPlugins[ID]; - - // If not dealing with legacy effects, make sure the provider is loaded - if (!plug.IsEffectLegacy()) - { - const PluginID & prov = plug.GetProviderID(); - if (mPlugins.find(prov) == mPlugins.end()) + // If not dealing with legacy effects, make sure the provider is loaded + if (!plug.IsEffectLegacy()) { - return NULL; + const PluginID & prov = plug.GetProviderID(); + if (auto iter2 = mPlugins.find(prov); iter2 == mPlugins.end()) + return nullptr; + else + iter2->second.GetInstance(); } - mPlugins[prov].GetInstance(); + + return plug.GetInstance(); } - - return plug.GetInstance(); -} - -PluginID PluginManager::GetID(ModuleInterface *module) -{ - return wxString::Format(wxT("%s_%s_%s_%s_%s"), - GetPluginTypeString(PluginTypeModule), - wxEmptyString, - module->GetVendor().Internal(), - module->GetSymbol().Internal(), - module->GetPath()); } PluginID PluginManager::GetID(ComponentInterface *command) @@ -2764,7 +1617,7 @@ wxString PluginManager::GetPluginTypeString(PluginType type) str = wxT("Importer"); break; case PluginTypeModule: - str = wxT("Module"); + str = ModuleManager::GetPluginTypeString(); break; } @@ -3019,26 +1872,25 @@ RegistryPath PluginManager::SettingsPath(const PluginID & ID, bool shared) // be changed across Audacity versions, or else compatibility of the // configuration files will break. - if (mPlugins.find(ID) == mPlugins.end()) - { - return wxEmptyString; + if (auto iter = mPlugins.find(ID); iter == mPlugins.end()) + return {}; + else { + const PluginDescriptor & plug = iter->second; + + wxString id = GetPluginTypeString(plug.GetPluginType()) + + wxT("_") + + plug.GetEffectFamily() + // is empty for non-Effects + wxT("_") + + plug.GetVendor() + + wxT("_") + + (shared ? wxString{} : plug.GetSymbol().Internal()); + + return SETROOT + + ConvertID(id) + + wxCONFIG_PATH_SEPARATOR + + (shared ? wxT("shared") : wxT("private")) + + wxCONFIG_PATH_SEPARATOR; } - - const PluginDescriptor & plug = mPlugins[ID]; - - wxString id = GetPluginTypeString(plug.GetPluginType()) + - wxT("_") + - plug.GetEffectFamily() + // is empty for non-Effects - wxT("_") + - plug.GetVendor() + - wxT("_") + - (shared ? wxString{} : plug.GetSymbol().Internal()); - - return SETROOT + - ConvertID(id) + - wxCONFIG_PATH_SEPARATOR + - (shared ? wxT("shared") : wxT("private")) + - wxCONFIG_PATH_SEPARATOR; } /* Return value is a key for lookup in a config file */ diff --git a/src/PluginManager.h b/src/PluginManager.h index d7ac23417..e68916b3c 100644 --- a/src/PluginManager.h +++ b/src/PluginManager.h @@ -30,8 +30,7 @@ class FileConfig; // /////////////////////////////////////////////////////////////////////////////// -typedef enum -{ +typedef enum : unsigned { PluginTypeNone = 0, // 2.1.0 placeholder entries...not used by 2.1.1 or greater PluginTypeStub =1, // Used for plugins that have not yet been registered PluginTypeEffect =1<<1, @@ -46,14 +45,12 @@ class AUDACITY_DLL_API PluginDescriptor { public: PluginDescriptor(); + PluginDescriptor &operator =(PluginDescriptor &&); virtual ~PluginDescriptor(); bool IsInstantiated() const; - ComponentInterface *GetInstance(); - void SetInstance(ComponentInterface *instance); PluginType GetPluginType() const; - void SetPluginType(PluginType type); // All plugins @@ -71,17 +68,6 @@ public: bool IsEnabled() const; bool IsValid() const; - // These should be passed an untranslated value - void SetID(const PluginID & ID); - void SetProviderID(const PluginID & providerID); - void SetPath(const PluginPath & path); - void SetSymbol(const ComponentInterfaceSymbol & symbol); - - // These should be passed an untranslated value wrapped in XO() so - // the value will still be extracted for translation - void SetVersion(const wxString & version); - void SetVendor(const wxString & vendor); - void SetEnabled(bool enable); void SetValid(bool valid); @@ -100,6 +86,31 @@ public: bool IsEffectRealtime() const; bool IsEffectAutomatable() const; + // Importer plugins only + + const wxString & GetImporterIdentifier() const; + const TranslatableString & GetImporterFilterDescription() const; + const FileExtensions & GetImporterExtensions() const; + +private: + friend class PluginManager; + + ComponentInterface *GetInstance(); + void SetInstance(std::unique_ptr instance); + + void SetPluginType(PluginType type); + + // These should be passed an untranslated value + void SetID(const PluginID & ID); + void SetProviderID(const PluginID & providerID); + void SetPath(const PluginPath & path); + void SetSymbol(const ComponentInterfaceSymbol & symbol); + + // These should be passed an untranslated value wrapped in XO() so + // the value will still be extracted for translation + void SetVersion(const wxString & version); + void SetVendor(const wxString & vendor); + // "family" should be an untranslated string wrapped in wxT() void SetEffectFamily(const wxString & family); void SetEffectType(EffectType type); @@ -109,25 +120,16 @@ public: void SetEffectRealtime(bool realtime); void SetEffectAutomatable(bool automatable); - // Importer plugins only - - const wxString & GetImporterIdentifier() const; - const TranslatableString & GetImporterFilterDescription() const; - const FileExtensions & GetImporterExtensions() const; - void SetImporterIdentifier(const wxString & identifier); void SetImporterFilterDescription(const TranslatableString & filterDesc); void SetImporterExtensions(FileExtensions extensions); -private: - - void DeleteInstance(); - // Common // Among other purposes, PluginDescriptor acts as the resource handle, // or smart pointer, to a resource created in a plugin library, and is responsible // for a cleanup of this pointer. + std::unique_ptr muInstance; // may be null for a module ComponentInterface *mInstance; PluginType mPluginType; @@ -173,8 +175,8 @@ class AUDACITY_DLL_API PluginManager final : public PluginManagerInterface { public: - RegistryPath GetPluginEnabledSetting( const PluginID &ID ); - RegistryPath GetPluginEnabledSetting( const PluginDescriptor &desc ); + RegistryPath GetPluginEnabledSetting( const PluginID &ID ) const; + RegistryPath GetPluginEnabledSetting( const PluginDescriptor &desc ) const; // PluginManagerInterface implementation @@ -236,7 +238,6 @@ public: static PluginManager & Get(); - static PluginID GetID(ModuleInterface *module); static PluginID GetID(ComponentInterface *command); static PluginID GetID(EffectDefinitionInterface *effect); static PluginID GetID(ImporterInterface *importer); @@ -246,13 +247,42 @@ public: static wxString GetPluginTypeString(PluginType type); int GetPluginCount(PluginType type); - const PluginDescriptor *GetPlugin(const PluginID & ID); + const PluginDescriptor *GetPlugin(const PluginID & ID) const; - const PluginDescriptor *GetFirstPlugin(int type); // possible or of several PlugInTypes. - const PluginDescriptor *GetNextPlugin( int type); + //! @name iteration over plugins of certain types, supporting range-for syntax + //! @{ + class Iterator { + public: + //! Iterates all, even disabled + explicit Iterator(PluginManager &manager); + //! Iterates only enabled and matching plugins, with family enabled too if an effect + Iterator(PluginManager &manager, + int pluginType //!< bitwise or of values in PluginType + ); + //! Iterates only enabled and matching effects, with family enabled too + Iterator(PluginManager &manager, EffectType type); + bool operator != (int) const { + return mIterator != mPm.mPlugins.end(); + } + Iterator &operator ++ (); + auto &operator *() const { return mIterator->second; } + private: + void Advance(bool incrementing); + const PluginManager &mPm; + PluginMap::iterator mIterator; + EffectType mEffectType{ EffectTypeNone }; + int mPluginType{ PluginTypeNone }; + }; + struct Range { + Iterator first; + Iterator begin() const { return first; } + int end() const { return 0; } + }; - const PluginDescriptor *GetFirstPluginForEffectType(EffectType type); - const PluginDescriptor *GetNextPluginForEffectType(EffectType type); + Range AllPlugins() { return { Iterator{ *this } }; } + Range PluginsOfType(int type) { return { Iterator{ *this, type } }; } + Range EffectsOfType(EffectType type) { return { Iterator{ *this, type } }; } + //! @} bool IsPluginEnabled(const PluginID & ID); void EnablePlugin(const PluginID & ID, bool enable); @@ -262,19 +292,22 @@ public: void CheckForUpdates(bool bFast = false); - bool ShowManager(wxWindow *parent, EffectType type = EffectTypeNone); - - const PluginID & RegisterPlugin(EffectDefinitionInterface *effect, PluginType type ); + //! Used only by Nyquist Workbench module + const PluginID & RegisterPlugin( + std::unique_ptr effect, PluginType type ); void UnregisterPlugin(const PluginID & ID); + //! Load from preferences + void Load(); + //! Save to preferences + void Save(); + private: // private! Use Get() PluginManager(); ~PluginManager(); - void Load(); void LoadGroup(FileConfig *pRegistry, PluginType type); - void Save(); void SaveGroup(FileConfig *pRegistry, PluginType type); PluginDescriptor & CreatePlugin(const PluginID & id, ComponentInterface *ident, PluginType type); @@ -321,9 +354,6 @@ private: int mCurrentIndex; PluginMap mPlugins; - PluginMap::iterator mPluginsIter; - - friend class PluginRegistrationDialog; }; // Defining these special names in the low-level PluginManager.h diff --git a/src/PluginRegistrationDialog.cpp b/src/PluginRegistrationDialog.cpp new file mode 100644 index 000000000..1d2d4b21e --- /dev/null +++ b/src/PluginRegistrationDialog.cpp @@ -0,0 +1,970 @@ +/*!********************************************************************* + + Audacity: A Digital Audio Editor + + @file PluginRegistrationDialog.cpp + + Paul Licameli split from PluginManager.cpp + +**********************************************************************/ +#include "PluginRegistrationDialog.h" + +#include "audacity/EffectInterface.h" +#include "ModuleManager.h" +#include "PluginManager.h" +#include "ShuttleGui.h" +#include "widgets/AudacityMessageBox.h" +#include "widgets/ProgressDialog.h" + +#include // for wxUSE_* macros +#include +#include +#include +#include +#include +#include +#include +#include + +// ============================================================================ +// +// +// +// ============================================================================ +#if wxUSE_ACCESSIBILITY +#include "widgets/WindowAccessible.h" + +class CheckListAx final : public WindowAccessible +{ +public: + CheckListAx(wxListCtrl * window); + + virtual ~ CheckListAx(); + + // Retrieves the address of an IDispatch interface for the specified child. + // All objects must support this property. + wxAccStatus GetChild( int childId, wxAccessible **child ) override; + + // Gets the number of children. + wxAccStatus GetChildCount( int *childCount ) override; + + // Gets the default action for this object (0) or > 0 (the action for a child). + // Return wxACC_OK even if there is no action. actionName is the action, or the empty + // string if there is no action. + // The retrieved string describes the action that is performed on an object, + // not what the object does as a result. For example, a toolbar button that prints + // a document has a default action of "Press" rather than "Prints the current document." + wxAccStatus GetDefaultAction( int childId, wxString *actionName ) override; + + // Returns the description for this object or a child. + wxAccStatus GetDescription( int childId, wxString *description ) override; + + // Gets the window with the keyboard focus. + // If childId is 0 and child is NULL, no object in + // this subhierarchy has the focus. + // If this object has the focus, child should be 'this'. + wxAccStatus GetFocus( int *childId, wxAccessible **child ) override; + + // Returns help text for this object or a child, similar to tooltip text. + wxAccStatus GetHelpText( int childId, wxString *helpText ) override; + + // Returns the keyboard shortcut for this object or child. + // Return e.g. ALT+K + wxAccStatus GetKeyboardShortcut( int childId, wxString *shortcut ) override; + + // Returns the rectangle for this object (id = 0) or a child element (id > 0). + // rect is in screen coordinates. + wxAccStatus GetLocation( wxRect& rect, int elementId ) override; + + // Gets the name of the specified object. + wxAccStatus GetName( int childId, wxString *name ) override; + + // Returns a role constant. + wxAccStatus GetRole( int childId, wxAccRole *role ) override; + + // Gets a variant representing the selected children + // of this object. + // Acceptable values: + // - a null variant (IsNull() returns TRUE) + // - a list variant (GetType() == wxT("list")) + // - an integer representing the selected child element, + // or 0 if this object is selected (GetType() == wxT("long")) + // - a "void*" pointer to a wxAccessible child object + wxAccStatus GetSelections( wxVariant *selections ) override; + + // Returns a state constant. + wxAccStatus GetState( int childId, long* state ) override; + + // Returns a localized string representing the value for the object + // or child. + wxAccStatus GetValue( int childId, wxString *strValue ) override; + + void SetSelected( int item, bool focused = true ); + +private: + wxListCtrl *mParent; + int mLastId; +}; + +CheckListAx::CheckListAx( wxListCtrl * window ) +: WindowAccessible( window ) +{ + mParent = window; + mLastId = -1; +} + +CheckListAx::~CheckListAx() +{ +} + +void CheckListAx::SetSelected( int item, bool focused ) +{ + if (mLastId != -1) + { + NotifyEvent( wxACC_EVENT_OBJECT_SELECTIONREMOVE, + mParent, + wxOBJID_CLIENT, + mLastId ); + mLastId = -1; + } + + if (item != -1) + { + if (focused) + { + NotifyEvent( wxACC_EVENT_OBJECT_FOCUS, + mParent, + wxOBJID_CLIENT, + item + 1 ); + } + + NotifyEvent( wxACC_EVENT_OBJECT_SELECTION, + mParent, + wxOBJID_CLIENT, + item + 1 ); + + mLastId = item + 1; + } +} + +// Retrieves the address of an IDispatch interface for the specified child. +// All objects must support this property. +wxAccStatus CheckListAx::GetChild( int childId, wxAccessible** child ) +{ + if( childId == wxACC_SELF ) + { + *child = this; + } + else + { + *child = NULL; + } + + return wxACC_OK; +} + +// Gets the number of children. +wxAccStatus CheckListAx::GetChildCount( int *childCount ) +{ + *childCount = mParent->GetItemCount(); + + return wxACC_OK; +} + +// Gets the default action for this object (0) or > 0 (the action for a child). +// Return wxACC_OK even if there is no action. actionName is the action, or the empty +// string if there is no action. +// The retrieved string describes the action that is performed on an object, +// not what the object does as a result. For example, a toolbar button that prints +// a document has a default action of "Press" rather than "Prints the current document." +wxAccStatus CheckListAx::GetDefaultAction( int WXUNUSED(childId), wxString *actionName ) +{ + actionName->clear(); + + return wxACC_OK; +} + +// Returns the description for this object or a child. +wxAccStatus CheckListAx::GetDescription( int WXUNUSED(childId), wxString *description ) +{ + description->clear(); + + return wxACC_OK; +} + +// Gets the window with the keyboard focus. +// If childId is 0 and child is NULL, no object in +// this subhierarchy has the focus. +// If this object has the focus, child should be 'this'. +wxAccStatus CheckListAx::GetFocus( int *childId, wxAccessible **child ) +{ + *childId = 0; + *child = this; + + return wxACC_OK; +} + +// Returns help text for this object or a child, similar to tooltip text. +wxAccStatus CheckListAx::GetHelpText( int WXUNUSED(childId), wxString *helpText ) +{ + helpText->clear(); + + return wxACC_OK; +} + +// Returns the keyboard shortcut for this object or child. +// Return e.g. ALT+K +wxAccStatus CheckListAx::GetKeyboardShortcut( int WXUNUSED(childId), wxString *shortcut ) +{ + shortcut->clear(); + + return wxACC_OK; +} + +// Returns the rectangle for this object (id = 0) or a child element (id > 0). +// rect is in screen coordinates. +wxAccStatus CheckListAx::GetLocation( wxRect& rect, int elementId ) +{ + if( elementId == wxACC_SELF ) + { + rect = mParent->GetRect(); + rect.SetPosition( mParent->GetParent()->ClientToScreen( rect.GetPosition() ) ); + } + else + { + if( elementId <= mParent->GetItemCount() ) + { + mParent->GetItemRect( elementId - 1, rect, wxLIST_RECT_LABEL ); + rect.SetPosition( mParent->ClientToScreen( rect.GetPosition() ) ); + } + } + + return wxACC_OK; +} + +// Gets the name of the specified object. +wxAccStatus CheckListAx::GetName( int WXUNUSED(childId), wxString *name ) +{ + *name = mParent->GetName(); + + return wxACC_OK; +} + +// Returns a role constant. +wxAccStatus CheckListAx::GetRole( int childId, wxAccRole *role ) +{ + if( childId == wxACC_SELF ) + { + *role = wxROLE_SYSTEM_LIST; + } + else + { + *role = wxROLE_SYSTEM_LISTITEM; + } + + return wxACC_OK; +} + +// Gets a variant representing the selected children +// of this object. +// Acceptable values: +// - a null variant (IsNull() returns TRUE) +// - a list variant (GetType() == wxT("list")) +// - an integer representing the selected child element, +// or 0 if this object is selected (GetType() == wxT("long")) +// - a "void*" pointer to a wxAccessible child object +wxAccStatus CheckListAx::GetSelections( wxVariant * WXUNUSED(selections) ) +{ + return wxACC_NOT_IMPLEMENTED; +} + +// Returns a state constant. +wxAccStatus CheckListAx::GetState( int childId, long *pState ) +{ + int flag = wxACC_STATE_SYSTEM_FOCUSABLE; + + if( childId == wxACC_SELF ) + { + flag |= wxACC_STATE_SYSTEM_FOCUSED; + } + else + { + wxListItem item; + + item.SetId( childId - 1 ); + item.SetState( wxLIST_STATE_FOCUSED | wxLIST_STATE_SELECTED ); + item.SetMask( wxLIST_MASK_STATE ); + + if( mParent->GetItem( item ) ) + { + flag |= wxACC_STATE_SYSTEM_SELECTABLE; + + long state = item.GetState(); + + if( state & wxLIST_STATE_FOCUSED ) + { + flag |= wxACC_STATE_SYSTEM_FOCUSED; + } + + if( state & wxLIST_STATE_SELECTED ) + { + flag |= wxACC_STATE_SYSTEM_SELECTED; + } + } + } + + *pState = flag; + + return wxACC_OK; +} + +// Returns a localized string representing the value for the object +// or child. +wxAccStatus CheckListAx::GetValue( int childId, wxString *strValue ) +{ + if( childId == 0 ) + { + return wxACC_OK; + } + else + { + *strValue = mParent->GetItemText( childId - 1 ); + } + + return wxACC_OK; +} + +#endif +enum +{ + STATE_Enabled, + STATE_Disabled, + STATE_New, + + STATE_COUNT +}; + +enum +{ + ID_ShowAll = 10000, + ID_ShowEnabled, + ID_ShowDisabled, + ID_ShowNew, + ID_List, + ID_ClearAll, + ID_SelectAll, + ID_Enable, + ID_Disable, +}; + +enum +{ + COL_Name, + COL_State, + COL_Path, + + COL_COUNT +}; + +BEGIN_EVENT_TABLE(PluginRegistrationDialog, wxDialogWrapper) + EVT_LIST_COL_CLICK(ID_List, PluginRegistrationDialog::OnSort) + EVT_BUTTON(wxID_OK, PluginRegistrationDialog::OnOK) + EVT_BUTTON(wxID_CANCEL, PluginRegistrationDialog::OnCancel) + EVT_BUTTON(ID_ClearAll, PluginRegistrationDialog::OnClearAll) + EVT_BUTTON(ID_SelectAll, PluginRegistrationDialog::OnSelectAll) + EVT_BUTTON(ID_Enable, PluginRegistrationDialog::OnEnable) + EVT_BUTTON(ID_Disable, PluginRegistrationDialog::OnDisable) + EVT_RADIOBUTTON(ID_ShowAll, PluginRegistrationDialog::OnChangedVisibility) + EVT_RADIOBUTTON(ID_ShowEnabled, PluginRegistrationDialog::OnChangedVisibility) + EVT_RADIOBUTTON(ID_ShowDisabled, PluginRegistrationDialog::OnChangedVisibility) + EVT_RADIOBUTTON(ID_ShowNew, PluginRegistrationDialog::OnChangedVisibility) +END_EVENT_TABLE() + +PluginRegistrationDialog::PluginRegistrationDialog(wxWindow *parent, EffectType type) +: wxDialogWrapper(parent, + wxID_ANY, + XO("Manage Plug-ins"), + wxDefaultPosition, wxDefaultSize, + wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) +{ + mType = type; + mEffects = NULL; + SetName(); + + mStates.resize(STATE_COUNT); + mStates[STATE_Enabled] = _("Enabled"); + mStates[STATE_Disabled] = _("Disabled"); + mStates[STATE_New] = _("New"); + + mSortColumn = COL_Name; + mSortDirection = 1; + + Populate(); + + DoSort( mSortColumn ); +} + +void PluginRegistrationDialog::Populate() +{ + //------------------------- Main section -------------------- + ShuttleGui S(this, eIsCreating); + PopulateOrExchange(S); + // ----------------------- End of main section -------------- +} + +/// Defines the dialog and does data exchange with it. +void PluginRegistrationDialog::PopulateOrExchange(ShuttleGui &S) +{ + S.StartVerticalLay(true); + { + /*i18n-hint: The dialog shows a list of plugins with check-boxes + beside each one.*/ +// S.StartStatic(XO("Effects"), true); + S.StartVerticalLay(); + { + S.StartHorizontalLay(wxEXPAND, 0); + { + S.StartHorizontalLay(wxALIGN_LEFT, 0); + { + S.AddPrompt(XXO("Select effects, click the Enable or Disable button, then click OK.")); + } + S.EndHorizontalLay(); + + S.StartHorizontalLay(wxCENTER, 1); + { + S.AddSpace(1); + } + S.EndHorizontalLay(); + + S.StartHorizontalLay(wxALIGN_NOT | wxALIGN_LEFT, 0); + { + wxRadioButton *rb; + + /* i18n-hint: This is before radio buttons selecting which effects to show */ + S.AddPrompt(XXO("Show:")); + rb = S.Id(ID_ShowAll) + /* i18n-hint: Radio button to show all effects */ + .Name(XO("Show all")) + /* i18n-hint: Radio button to show all effects */ + .AddRadioButton(XXO("&All")); +#if wxUSE_ACCESSIBILITY + // so that name can be set on a standard control + rb->SetAccessible(safenew WindowAccessible(rb)); +#endif + + rb = S.Id(ID_ShowDisabled) + /* i18n-hint: Radio button to show just the currently disabled effects */ + .Name(XO("Show disabled")) + /* i18n-hint: Radio button to show just the currently disabled effects */ + .AddRadioButtonToGroup(XXO("D&isabled")); +#if wxUSE_ACCESSIBILITY + // so that name can be set on a standard control + rb->SetAccessible(safenew WindowAccessible(rb)); +#endif + + rb = S.Id(ID_ShowEnabled) + /* i18n-hint: Radio button to show just the currently enabled effects */ + .Name(XO("Show enabled")) + /* i18n-hint: Radio button to show just the currently enabled effects */ + .AddRadioButtonToGroup(XXO("E&nabled")); +#if wxUSE_ACCESSIBILITY + // so that name can be set on a standard control + rb->SetAccessible(safenew WindowAccessible(rb)); +#endif + + rb = S.Id(ID_ShowNew) + /* i18n-hint: Radio button to show just the newly discovered effects */ + .Name(XO("Show new")) + /* i18n-hint: Radio button to show just the newly discovered effects */ + .AddRadioButtonToGroup(XXO("Ne&w")); +#if wxUSE_ACCESSIBILITY + // so that name can be set on a standard control + rb->SetAccessible(safenew WindowAccessible(rb)); +#endif + } + S.EndHorizontalLay(); + } + S.EndHorizontalLay(); + + mEffects = S.Id(ID_List) + .Style(wxSUNKEN_BORDER | wxLC_REPORT | wxLC_HRULES | wxLC_VRULES ) + .ConnectRoot(wxEVT_KEY_DOWN, + &PluginRegistrationDialog::OnListChar) + .AddListControlReportMode({ XO("Name"), XO("State"), XO("Path") }); +#if wxUSE_ACCESSIBILITY + mEffects->SetAccessible(mAx = safenew CheckListAx(mEffects)); +#endif + + S.StartHorizontalLay(wxALIGN_LEFT | wxEXPAND, 0); + { + S.Id(ID_SelectAll).AddButton(XXO("&Select All")); + S.Id(ID_ClearAll).AddButton(XXO("C&lear All")); + + S.StartHorizontalLay(wxALIGN_CENTER); + { + S.AddSpace(1); + } + S.EndHorizontalLay(); + + S.Id(ID_Enable).AddButton(XXO("&Enable")); + S.Id(ID_Disable).AddButton(XXO("&Disable")); + } + S.EndHorizontalLay(); + } +// S.EndStatic(); + S.EndVerticalLay(); + + S.AddStandardButtons(eOkButton | eCancelButton); + } + S.EndVerticalLay(); + + std::vector colWidths; + for (int i = 0, cnt = mEffects->GetColumnCount(); i < cnt; i++) + { + colWidths.push_back(0); + } + + for (int i = 0, cnt = mStates.size(); i < cnt; i++) + { + int x; + mEffects->GetTextExtent(mStates[i], &x, NULL); + colWidths[COL_State] = wxMax(colWidths[COL_State], x + 4); // 2 pixel margin on each side + } + + PluginManager & pm = PluginManager::Get(); + for (auto &plug : pm.AllPlugins()) { + PluginType plugType = plug.GetPluginType(); + if (plugType != PluginTypeEffect && plugType != PluginTypeStub) + continue; + + const auto &path = plug.GetPath(); + ItemData & item = mItems[path]; // will create NEW entry + item.plugs.push_back(&plug); + item.path = path; + item.state = plug.IsEnabled() ? STATE_Enabled : STATE_Disabled; + item.valid = plug.IsValid(); + + if (plugType == PluginTypeEffect) + { + item.name = plug.GetSymbol().Translation(); + } + // This is not right and will not work when other plugin types are added. + // But it's presumed that the plugin manager dialog will be fully developed + // by then. + else if (plugType == PluginTypeStub) + { + wxFileName fname { path }; + item.name = fname.GetName().Trim(false).Trim(true); + if (!item.valid) + { + item.state = STATE_New; + } + } + + int x; + mEffects->GetTextExtent(item.name, &x, NULL); + colWidths[COL_Name] = wxMax(colWidths[COL_Name], x); + + mEffects->GetTextExtent(item.path, &x, NULL); + if (x > colWidths[COL_Path]) + { + mLongestPath = item.path; + } + colWidths[COL_Path] = wxMax(colWidths[COL_Path], x); + } + + wxRect r = wxGetClientDisplayRect(); + + int maxW = 0; + for (int i = 0, cnt = mEffects->GetColumnCount(); i < cnt; i++) + { + int w = colWidths[i] + /* fudge */ 10; + mEffects->SetColumnWidth(i, w); + maxW += w; + } + + // Keep dialog from getting too wide + int w = r.GetWidth() - (GetClientSize().GetWidth() - mEffects->GetSize().GetWidth()); + mEffects->SetMinSize({ std::min(maxW, w), 200 }); + mEffects->SetMaxSize({ w, -1 }); + + RegenerateEffectsList(ID_ShowAll); + + Layout(); + Fit(); + + wxSize sz = GetSize(); + sz.SetWidth(wxMin(sz.GetWidth(), r.GetWidth())); + sz.SetHeight(wxMin(sz.GetHeight(), r.GetHeight())); + SetMinSize(sz); + + // Parent window is usually not there yet, so centre on screen rather than on parent. + CenterOnScreen(); + + if (mEffects->GetItemCount() > 0) + { + // Make sure first item is selected/focused. + mEffects->SetFocus(); + mEffects->SetItemState(0, wxLIST_STATE_FOCUSED | wxLIST_STATE_SELECTED, wxLIST_STATE_FOCUSED | wxLIST_STATE_SELECTED); +#if wxUSE_ACCESSIBILITY + mAx->SetSelected(0); +#endif + } + +} + +void PluginRegistrationDialog::RegenerateEffectsList(int filter) +{ + mFilter = filter; + + mEffects->DeleteAllItems(); + + int i = 0; + for (ItemDataMap::iterator iter = mItems.begin(); iter != mItems.end(); ++iter) + { + ItemData & item = iter->second; + bool add = false; + + switch (mFilter) + { + case ID_ShowAll: + add = true; + break; + case ID_ShowNew: + if (item.state == STATE_New) + { + add = true; + } + break; + case ID_ShowEnabled: + if (item.state == STATE_Enabled) + { + add = true; + } + break; + case ID_ShowDisabled: + if (item.state == STATE_Disabled) + { + add = true; + } + break; + } + + if (add) + { + mEffects->InsertItem(i, item.name); + mEffects->SetItem(i, COL_State, mStates[item.state]); + mEffects->SetItem(i, COL_Path, item.path); + mEffects->SetItemPtrData(i, (wxUIntPtr) &item); + + ++i; + } + } + + mEffects->SortItems(SortCompare, (wxUIntPtr) this); + + if (mEffects->GetItemCount() > 0) + { + // Make sure first item is selected/focused. +// mEffects->SetFocus(); + mEffects->SetItemState(0, wxLIST_STATE_FOCUSED|wxLIST_STATE_SELECTED, wxLIST_STATE_FOCUSED|wxLIST_STATE_SELECTED); +#if wxUSE_ACCESSIBILITY + mAx->SetSelected(0, false); +#endif + } +} + +void PluginRegistrationDialog::SetState(int i, bool toggle, bool state) +{ + wxListItem li; + + li.m_mask = wxLIST_MASK_DATA; + li.m_itemId = i; + + mEffects->GetItem(li); + + ItemData *item = (ItemData *) li.m_data; + + // If changing the state of a "New" (stub) entry, then we mark it as valid + // since it will either be registered if "Enabled" or ignored if "Disabled". + if (item->state == STATE_New) + { + item->valid = true; + } + + if (toggle) + { + item->state = item->state == STATE_Enabled ? STATE_Disabled : STATE_Enabled; + } + else + { + item->state = state; + } + + if (mFilter == ID_ShowNew && item->state != STATE_New) + { + mEffects->DeleteItem(i); + } + else if (mFilter == ID_ShowDisabled && item->state != STATE_Disabled) + { + mEffects->DeleteItem(i); + } + else if (mFilter == ID_ShowEnabled && item->state != STATE_Enabled) + { + mEffects->DeleteItem(i); + } + else + { + mEffects->SetItem(i, COL_State, mStates[item->state]); +#if wxUSE_ACCESSIBILITY + mAx->SetSelected(i); +#endif + } +} + +int wxCALLBACK PluginRegistrationDialog::SortCompare(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData) +{ + PluginRegistrationDialog *dlg = (PluginRegistrationDialog *) sortData; + ItemData *i1 = (ItemData *) item1; + ItemData *i2 = (ItemData *) item2; + + return dlg->SortCompare(i1, i2); +} + +int PluginRegistrationDialog::SortCompare(ItemData *item1, ItemData *item2) +{ + // This function is a three-valued comparator + + wxString *str1; + wxString *str2; + + switch (mSortColumn) + { + case COL_Name: + str1 = &item1->name; + str2 = &item2->name; + break; + case COL_State: + str1 = &mStates[item1->state]; + str2 = &mStates[item2->state]; + break; + case COL_Path: + str1 = &item1->path; + str2 = &item2->path; + break; + default: + return 0; + } + + return str2->CmpNoCase(*str1) * mSortDirection; +} + +void PluginRegistrationDialog::OnChangedVisibility(wxCommandEvent & evt) +{ + // Go and show the relevant items. + RegenerateEffectsList(evt.GetId()); +} + +void PluginRegistrationDialog::OnSort(wxListEvent & evt) +{ + int col = evt.GetColumn(); + DoSort( col ); +} + +void PluginRegistrationDialog::DoSort( int col ) +{ + if (col != mSortColumn) + { + mSortDirection = 1; + } + else + { + mSortDirection *= -1; + } + + mSortColumn = col; + mEffects->SortItems(SortCompare, (wxUIntPtr) this); + + // Without a refresh, wxMac doesn't redisplay the list properly after a sort + mEffects->Refresh(); +} + +void PluginRegistrationDialog::OnListChar(wxKeyEvent & evt) +{ + switch (evt.GetKeyCode()) + { + case WXK_SPACE: + { + int item = mEffects->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_FOCUSED); + if (item != wxNOT_FOUND) + { + SetState(item, true); + } + } + break; + + case WXK_RETURN: + // Don't know why wxListCtrls prevent default dialog action, + // but they do, so handle it. + EmulateButtonClickIfPresent(GetAffirmativeId()); + break; + + default: + evt.Skip(); + break; + } +} + +void PluginRegistrationDialog::OnSelectAll(wxCommandEvent & WXUNUSED(evt)) +{ + for (int i = 0, cnt = mEffects->GetItemCount(); i < cnt; i++) + { + mEffects->SetItemState(i, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED); + } +} + +void PluginRegistrationDialog::OnClearAll(wxCommandEvent & WXUNUSED(evt)) +{ + for (int i = 0, cnt = mEffects->GetItemCount(); i < cnt; i++) + { + mEffects->SetItemState(i, 0, wxLIST_STATE_SELECTED); + } +} + +void PluginRegistrationDialog::OnEnable(wxCommandEvent & WXUNUSED(evt)) +{ + std::vector items; + + { + long i = mEffects->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); + while (i != wxNOT_FOUND) + { + items.insert(items.begin(), i); + i = mEffects->GetNextItem(i, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); + } + } + + for (size_t i = 0, cnt = items.size(); i < cnt; i++) + { + SetState(items[i], false, STATE_Enabled); + } +} + +void PluginRegistrationDialog::OnDisable(wxCommandEvent & WXUNUSED(evt)) +{ + std::vector items; + + { + long i = mEffects->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); + while (i != wxNOT_FOUND) + { + items.insert(items.begin(), i); + i = mEffects->GetNextItem(i, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); + } + } + + for (size_t i = 0, cnt = items.size(); i < cnt; i++) + { + SetState(items[i], false, STATE_Disabled); + } +} + +void PluginRegistrationDialog::OnOK(wxCommandEvent & WXUNUSED(evt)) +{ + PluginManager & pm = PluginManager::Get(); + ModuleManager & mm = ModuleManager::Get(); + + int enableCount = 0; + for (ItemDataMap::iterator iter = mItems.begin(); iter != mItems.end(); ++iter) + { + ItemData & item = iter->second; + wxString path = item.path; + + if (item.state == STATE_Enabled && item.plugs[0]->GetPluginType() == PluginTypeStub) + { + enableCount++; + } + } + + wxString last3 = mLongestPath + wxT("\n") + + mLongestPath + wxT("\n") + + mLongestPath + wxT("\n"); + + auto msg = XO("Enabling effects or commands:\n\n%s").Format( last3 ); + + // Make sure the progress dialog is deleted before we call EndModal() or + // we will leave the project window in an unusable state on OSX. + // See bug #1192. + { + ProgressDialog progress{ + Verbatim( GetTitle() ), msg, pdlgHideStopButton }; + progress.CenterOnParent(); + + int i = 0; + for (ItemDataMap::iterator iter = mItems.begin(); iter != mItems.end(); ++iter) + { + ItemData & item = iter->second; + wxString path = item.path; + + if (item.state == STATE_Enabled && item.plugs[0]->GetPluginType() == PluginTypeStub) + { + last3 = last3.AfterFirst(wxT('\n')) + item.path + wxT("\n"); + auto status = progress.Update(++i, enableCount, + XO("Enabling effect or command:\n\n%s").Format( last3 )); + if (status == ProgressResult::Cancelled) + { + break; + } + + TranslatableString errMsgs; + + // Try to register the plugin via each provider until one succeeds + for (size_t j = 0, cntj = item.plugs.size(); j < cntj; j++) + { + TranslatableString errMsg; + if (mm.RegisterEffectPlugin(item.plugs[j]->GetProviderID(), path, + errMsg)) + { + for (auto plug : item.plugs) + pm.UnregisterPlugin( + plug->GetProviderID() + wxT("_") + path); + // Bug 1893. We've found a provider that works. + // Error messages from any that failed are no longer useful. + errMsgs = {}; + break; + } + else + { + if (!errMsgs.empty()) + errMsgs.Join( errMsg, '\n' ); + else + errMsgs = errMsg; + } + } + if (!errMsgs.empty()) + AudacityMessageBox( + XO("Effect or Command at %s failed to register:\n%s") + .Format( path, errMsgs ) ); + } + else if (item.state == STATE_New) { + for (auto plug : item.plugs) + plug->SetValid(false); + } + else if (item.state != STATE_New) { + for (auto plug : item.plugs) { + plug->SetEnabled(item.state == STATE_Enabled); + plug->SetValid(item.valid); + } + } + } + + pm.Save(); + } + + EndModal(wxID_OK); +} + +void PluginRegistrationDialog::OnCancel(wxCommandEvent & WXUNUSED(evt)) +{ + EndModal(wxID_CANCEL); +} diff --git a/src/PluginRegistrationDialog.h b/src/PluginRegistrationDialog.h new file mode 100644 index 000000000..4cd657a45 --- /dev/null +++ b/src/PluginRegistrationDialog.h @@ -0,0 +1,85 @@ +/********************************************************************** + + Audacity: A Digital Audio Editor + + PluginRegistrationDialog.h + + Paul Licameli split from PluginManager.cpp + +**********************************************************************/ +#ifndef __AUDACITY_PLUGIN_REGISTRATION_DIALOG__ +#define __AUDACITY_PLUGIN_REGISTRATION_DIALOG__ + +#include "widgets/wxPanelWrapper.h" // to inherit +#include +#include // member + +class CheckListAx; +enum EffectType : int; +class PluginDescriptor; +class ShuttleGui; +class wxListEvent; +class wxListCtrl; + +class PluginRegistrationDialog final : public wxDialogWrapper +{ +public: + // constructors and destructors + PluginRegistrationDialog(wxWindow *parent, EffectType type); + +private: + struct ItemData + { + std::vector plugs; + wxString name; + PluginPath path; + int state; + bool valid; + int nameWidth; + int pathWidth; + int stateWidth; + }; + + using ItemDataMap = std::unordered_map; + + void Populate(); + void PopulateOrExchange(ShuttleGui & S); + void RegenerateEffectsList(int iShowWhat); + void SetState(int i, bool toggle, bool state = true); + + static int wxCALLBACK SortCompare(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData); + int SortCompare(ItemData *item1, ItemData *item2); + + void OnChangedVisibility(wxCommandEvent & evt); + void OnSort(wxListEvent & evt); + void DoSort( int col ); + void OnListChar(wxKeyEvent & evt); + void OnOK(wxCommandEvent & evt); + void OnCancel(wxCommandEvent & evt); + void OnSelectAll(wxCommandEvent & evt); + void OnClearAll(wxCommandEvent & evt); + void OnEnable(wxCommandEvent & evt); + void OnDisable(wxCommandEvent & evt); + +private: + EffectType mType; + int mFilter; + + wxArrayString mStates; + ItemDataMap mItems; + + int mSortColumn; + int mSortDirection; + + PluginPath mLongestPath; + + wxListCtrl *mEffects; +#if wxUSE_ACCESSIBILITY + CheckListAx *mAx; +#endif + + DECLARE_EVENT_TABLE() +}; + + +#endif diff --git a/src/commands/GetInfoCommand.cpp b/src/commands/GetInfoCommand.cpp index a99137dd9..289e12042 100644 --- a/src/commands/GetInfoCommand.cpp +++ b/src/commands/GetInfoCommand.cpp @@ -416,14 +416,12 @@ bool GetInfoCommand::SendCommands(const CommandContext &context, int flags ) PluginManager & pm = PluginManager::Get(); EffectManager & em = EffectManager::Get(); { - const PluginDescriptor *plug = pm.GetFirstPlugin(PluginTypeEffect | PluginTypeAudacityCommand); - while (plug) - { - auto command = em.GetCommandIdentifier(plug->GetID()); + for (auto &plug + : pm.PluginsOfType(PluginTypeEffect | PluginTypeAudacityCommand)) { + auto command = em.GetCommandIdentifier(plug.GetID()); if (!command.empty()){ - em.GetCommandDefinition( plug->GetID(), context, flags ); + em.GetCommandDefinition( plug.GetID(), context, flags ); } - plug = pm.GetNextPlugin(PluginTypeEffect | PluginTypeAudacityCommand ); } } context.EndArray(); diff --git a/src/commands/LoadCommands.cpp b/src/commands/LoadCommands.cpp index 1dffca0cf..55fe19078 100644 --- a/src/commands/LoadCommands.cpp +++ b/src/commands/LoadCommands.cpp @@ -194,19 +194,11 @@ bool BuiltinCommandsModule::IsPluginValid(const PluginPath & path, bool bFast) return mCommands.find( path ) != mCommands.end(); } -ComponentInterface *BuiltinCommandsModule::CreateInstance(const PluginPath & path) +std::unique_ptr +BuiltinCommandsModule::CreateInstance(const PluginPath & path) { // Acquires a resource for the application. - // Safety of this depends on complementary calls to DeleteInstance on the module manager side. - return Instantiate(path).release(); -} - -void BuiltinCommandsModule::DeleteInstance(ComponentInterface *instance) -{ - // Releases the resource. - std::unique_ptr < AudacityCommand > { - dynamic_cast(instance) - }; + return Instantiate(path); } // ============================================================================ diff --git a/src/commands/LoadCommands.h b/src/commands/LoadCommands.h index 9aacbdfc9..b7a0153ce 100644 --- a/src/commands/LoadCommands.h +++ b/src/commands/LoadCommands.h @@ -69,8 +69,8 @@ public: bool IsPluginValid(const PluginPath & path, bool bFast) override; - ComponentInterface *CreateInstance(const PluginPath & path) override; - void DeleteInstance(ComponentInterface *instance) override; + std::unique_ptr + CreateInstance(const PluginPath & path) override; private: // BuiltinEffectModule implementation diff --git a/src/effects/EffectManager.cpp b/src/effects/EffectManager.cpp index e983cb6e4..1e2956013 100644 --- a/src/effects/EffectManager.cpp +++ b/src/effects/EffectManager.cpp @@ -56,12 +56,12 @@ EffectManager::~EffectManager() // Here solely for the purpose of Nyquist Workbench until // a better solution is devised. -const PluginID & EffectManager::RegisterEffect(Effect *f) +const PluginID & EffectManager::RegisterEffect(std::unique_ptr uEffect) { - const PluginID & ID = PluginManager::Get().RegisterPlugin(f, PluginTypeEffect); - - mEffects[ID] = f; - + auto pEffect = uEffect.get(); + const PluginID & ID = + PluginManager::Get().RegisterPlugin(std::move(uEffect), PluginTypeEffect); + mEffects[ID] = pEffect; return ID; } @@ -826,15 +826,12 @@ const PluginID & EffectManager::GetEffectByIdentifier(const CommandID & strTarge PluginManager & pm = PluginManager::Get(); // Effects OR Generic commands... - const PluginDescriptor *plug = pm.GetFirstPlugin(PluginTypeEffect | PluginTypeAudacityCommand); - while (plug) - { - if (GetCommandIdentifier(plug->GetID()) == strTarget) - { - return plug->GetID(); - } - plug = pm.GetNextPlugin(PluginTypeEffect | PluginTypeAudacityCommand); + for (auto &plug + : pm.PluginsOfType(PluginTypeEffect | PluginTypeAudacityCommand)) { + auto &ID = plug.GetID(); + if (GetCommandIdentifier(ID) == strTarget) + return ID; } - return empty;; + return empty; } diff --git a/src/effects/EffectManager.h b/src/effects/EffectManager.h index eba137d76..5578207f7 100644 --- a/src/effects/EffectManager.h +++ b/src/effects/EffectManager.h @@ -75,10 +75,10 @@ public: EffectManager(); virtual ~EffectManager(); - /** (Un)Register an effect so it can be executed. */ - // Here solely for the purpose of Nyquist Workbench until - // a better solution is devised. - const PluginID & RegisterEffect(Effect *f); + //! Here solely for the purpose of Nyquist Workbench until a better solution is devised. + /** Register an effect so it can be executed. */ + const PluginID & RegisterEffect(std::unique_ptr uEffect); + //! Used only by Nyquist Workbench module void UnregisterEffect(const PluginID & ID); TranslatableString GetEffectFamilyName(const PluginID & ID); diff --git a/src/effects/LoadEffects.cpp b/src/effects/LoadEffects.cpp index ea943fff8..cfe8208a9 100644 --- a/src/effects/LoadEffects.cpp +++ b/src/effects/LoadEffects.cpp @@ -191,19 +191,11 @@ bool BuiltinEffectsModule::IsPluginValid(const PluginPath & path, bool bFast) return mEffects.find( path ) != mEffects.end(); } -ComponentInterface *BuiltinEffectsModule::CreateInstance(const PluginPath & path) +std::unique_ptr +BuiltinEffectsModule::CreateInstance(const PluginPath & path) { // Acquires a resource for the application. - // Safety of this depends on complementary calls to DeleteInstance on the module manager side. - return Instantiate(path).release(); -} - -void BuiltinEffectsModule::DeleteInstance(ComponentInterface *instance) -{ - // Releases the resource. - std::unique_ptr < Effect > { - dynamic_cast(instance) - }; + return Instantiate(path); } // ============================================================================ diff --git a/src/effects/LoadEffects.h b/src/effects/LoadEffects.h index 29fdc613a..f0323f250 100644 --- a/src/effects/LoadEffects.h +++ b/src/effects/LoadEffects.h @@ -69,8 +69,8 @@ public: bool IsPluginValid(const PluginPath & path, bool bFast) override; - ComponentInterface *CreateInstance(const PluginPath & path) override; - void DeleteInstance(ComponentInterface *instance) override; + std::unique_ptr + CreateInstance(const PluginPath & path) override; private: // BuiltinEffectModule implementation diff --git a/src/effects/VST/VSTEffect.cpp b/src/effects/VST/VSTEffect.cpp index 9743d6a30..20a2f30c4 100644 --- a/src/effects/VST/VSTEffect.cpp +++ b/src/effects/VST/VSTEffect.cpp @@ -684,19 +684,12 @@ bool VSTEffectsModule::IsPluginValid(const PluginPath & path, bool bFast) return wxFileName::FileExists(realPath) || wxFileName::DirExists(realPath); } -ComponentInterface *VSTEffectsModule::CreateInstance(const PluginPath & path) +std::unique_ptr +VSTEffectsModule::CreateInstance(const PluginPath & path) { // Acquires a resource for the application. // For us, the ID is simply the path to the effect - // Safety of this depends on complementary calls to DeleteInstance on the module manager side. - return safenew VSTEffect(path); -} - -void VSTEffectsModule::DeleteInstance(ComponentInterface *instance) -{ - std::unique_ptr < VSTEffect > { - dynamic_cast(instance) - }; + return std::make_unique(path); } // ============================================================================ diff --git a/src/effects/VST/VSTEffect.h b/src/effects/VST/VSTEffect.h index 989d25715..6856773c6 100644 --- a/src/effects/VST/VSTEffect.h +++ b/src/effects/VST/VSTEffect.h @@ -432,8 +432,8 @@ public: bool IsPluginValid(const PluginPath & path, bool bFast) override; - ComponentInterface *CreateInstance(const PluginPath & path) override; - void DeleteInstance(ComponentInterface *instance) override; + std::unique_ptr + CreateInstance(const PluginPath & path) override; // VSTEffectModule implementation diff --git a/src/effects/audiounits/AudioUnitEffect.cpp b/src/effects/audiounits/AudioUnitEffect.cpp index 75889f9a4..e3053239c 100644 --- a/src/effects/audiounits/AudioUnitEffect.cpp +++ b/src/effects/audiounits/AudioUnitEffect.cpp @@ -377,25 +377,13 @@ bool AudioUnitEffectsModule::IsPluginValid(const PluginPath & path, bool bFast) return FindAudioUnit(path, name) != NULL; } -ComponentInterface *AudioUnitEffectsModule::CreateInstance(const PluginPath & path) +std::unique_ptr +AudioUnitEffectsModule::CreateInstance(const PluginPath & path) { // Acquires a resource for the application. - wxString name; - AudioComponent component = FindAudioUnit(path, name); - if (component == NULL) - { - return NULL; - } - - // Safety of this depends on complementary calls to DeleteInstance on the module manager side. - return safenew AudioUnitEffect(path, name, component); -} - -void AudioUnitEffectsModule::DeleteInstance(ComponentInterface *instance) -{ - std::unique_ptr < AudioUnitEffect > { - dynamic_cast(instance) - }; + if (wxString name; auto component = FindAudioUnit(path, name)) + return std::make_unique(path, name, component); + return nullptr; } // ============================================================================ diff --git a/src/effects/audiounits/AudioUnitEffect.h b/src/effects/audiounits/AudioUnitEffect.h index 8263710a4..bc31786dc 100644 --- a/src/effects/audiounits/AudioUnitEffect.h +++ b/src/effects/audiounits/AudioUnitEffect.h @@ -261,8 +261,8 @@ public: bool IsPluginValid(const PluginPath & path, bool bFast) override; - ComponentInterface *CreateInstance(const PluginPath & path) override; - void DeleteInstance(ComponentInterface *instance) override; + std::unique_ptr + CreateInstance(const PluginPath & path) override; // AudioUnitEffectModule implementation diff --git a/src/effects/ladspa/LadspaEffect.cpp b/src/effects/ladspa/LadspaEffect.cpp index 3c47ac8d2..363aa1a39 100644 --- a/src/effects/ladspa/LadspaEffect.cpp +++ b/src/effects/ladspa/LadspaEffect.cpp @@ -330,7 +330,8 @@ bool LadspaEffectsModule::IsPluginValid(const PluginPath & path, bool bFast) return wxFileName::FileExists(realPath); } -ComponentInterface *LadspaEffectsModule::CreateInstance(const PluginPath & path) +std::unique_ptr +LadspaEffectsModule::CreateInstance(const PluginPath & path) { // Acquires a resource for the application. // For us, the path is two words. @@ -339,16 +340,7 @@ ComponentInterface *LadspaEffectsModule::CreateInstance(const PluginPath & path) long index; wxString realPath = path.BeforeFirst(wxT(';')); path.AfterFirst(wxT(';')).ToLong(&index); - - // Safety of this depends on complementary calls to DeleteInstance on the module manager side. - return safenew LadspaEffect(realPath, (int)index); -} - -void LadspaEffectsModule::DeleteInstance(ComponentInterface *instance) -{ - std::unique_ptr < LadspaEffect > { - dynamic_cast(instance) - }; + return std::make_unique(realPath, (int)index); } FilePaths LadspaEffectsModule::GetSearchPaths() diff --git a/src/effects/ladspa/LadspaEffect.h b/src/effects/ladspa/LadspaEffect.h index 93619a121..9971f69eb 100644 --- a/src/effects/ladspa/LadspaEffect.h +++ b/src/effects/ladspa/LadspaEffect.h @@ -237,8 +237,8 @@ public: bool IsPluginValid(const PluginPath & path, bool bFast) override; - ComponentInterface *CreateInstance(const PluginPath & path) override; - void DeleteInstance(ComponentInterface *instance) override; + std::unique_ptr + CreateInstance(const PluginPath & path) override; // LadspaEffectModule implementation diff --git a/src/effects/lv2/LoadLV2.cpp b/src/effects/lv2/LoadLV2.cpp index b101ee734..43c82b5d6 100755 --- a/src/effects/lv2/LoadLV2.cpp +++ b/src/effects/lv2/LoadLV2.cpp @@ -302,24 +302,13 @@ bool LV2EffectsModule::IsPluginValid(const PluginPath & path, bool bFast) return GetPlugin(path) != NULL; } -ComponentInterface *LV2EffectsModule::CreateInstance(const PluginPath & path) +std::unique_ptr +LV2EffectsModule::CreateInstance(const PluginPath & path) { // Acquires a resource for the application. - const LilvPlugin *plug = GetPlugin(path); - if (!plug) - { - return NULL; - } - - // Safety of this depends on complementary calls to DeleteInstance on the module manager side. - return safenew LV2Effect(plug); -} - -void LV2EffectsModule::DeleteInstance(ComponentInterface *instance) -{ - std::unique_ptr < LV2Effect > { - dynamic_cast(instance) - }; + if (auto plug = GetPlugin(path)) + return std::make_unique(plug); + return nullptr; } // ============================================================================ diff --git a/src/effects/lv2/LoadLV2.h b/src/effects/lv2/LoadLV2.h index 35430aa26..a214ea0fd 100755 --- a/src/effects/lv2/LoadLV2.h +++ b/src/effects/lv2/LoadLV2.h @@ -192,8 +192,8 @@ public: bool IsPluginValid(const PluginPath & path, bool bFast) override; - ComponentInterface *CreateInstance(const PluginPath & path) override; - void DeleteInstance(ComponentInterface *instance) override; + std::unique_ptr + CreateInstance(const PluginPath & path) override; // LV2EffectModule implementation diff --git a/src/effects/nyquist/LoadNyquist.cpp b/src/effects/nyquist/LoadNyquist.cpp index e1dcb66e0..0f16de43e 100644 --- a/src/effects/nyquist/LoadNyquist.cpp +++ b/src/effects/nyquist/LoadNyquist.cpp @@ -264,24 +264,14 @@ bool NyquistEffectsModule::IsPluginValid(const PluginPath & path, bool bFast) return wxFileName::FileExists(path); } -ComponentInterface *NyquistEffectsModule::CreateInstance(const PluginPath & path) +std::unique_ptr +NyquistEffectsModule::CreateInstance(const PluginPath & path) { // Acquires a resource for the application. auto effect = std::make_unique(path); if (effect->IsOk()) - { - // Safety of this depends on complementary calls to DeleteInstance on the module manager side. - return effect.release(); - } - - return NULL; -} - -void NyquistEffectsModule::DeleteInstance(ComponentInterface *instance) -{ - std::unique_ptr < NyquistEffect > { - dynamic_cast(instance) - }; + return effect; + return nullptr; } // ============================================================================ diff --git a/src/effects/nyquist/LoadNyquist.h b/src/effects/nyquist/LoadNyquist.h index 77016c78a..9220d063e 100644 --- a/src/effects/nyquist/LoadNyquist.h +++ b/src/effects/nyquist/LoadNyquist.h @@ -51,6 +51,6 @@ public: bool IsPluginValid(const PluginPath & path, bool bFast) override; - ComponentInterface *CreateInstance(const PluginPath & path) override; - void DeleteInstance(ComponentInterface *instance) override; + std::unique_ptr + CreateInstance(const PluginPath & path) override; }; diff --git a/src/effects/vamp/LoadVamp.cpp b/src/effects/vamp/LoadVamp.cpp index 589f922bb..4ce2ee666 100644 --- a/src/effects/vamp/LoadVamp.cpp +++ b/src/effects/vamp/LoadVamp.cpp @@ -236,27 +236,16 @@ bool VampEffectsModule::IsPluginValid(const PluginPath & path, bool bFast) return bool(vp); } -ComponentInterface *VampEffectsModule::CreateInstance(const PluginPath & path) +std::unique_ptr +VampEffectsModule::CreateInstance(const PluginPath & path) { // Acquires a resource for the application. int output; bool hasParameters; - auto vp = FindPlugin(path, output, hasParameters); - if (vp) - { - // Safety of this depends on complementary calls to DeleteInstance on the module manager side. - return safenew VampEffect(std::move(vp), path, output, hasParameters); - } - - return NULL; -} - -void VampEffectsModule::DeleteInstance(ComponentInterface *instance) -{ - std::unique_ptr < VampEffect > { - dynamic_cast(instance) - }; + if (auto vp = FindPlugin(path, output, hasParameters)) + return std::make_unique(std::move(vp), path, output, hasParameters); + return nullptr; } // VampEffectsModule implementation diff --git a/src/effects/vamp/LoadVamp.h b/src/effects/vamp/LoadVamp.h index b79667518..285f0f022 100644 --- a/src/effects/vamp/LoadVamp.h +++ b/src/effects/vamp/LoadVamp.h @@ -58,8 +58,8 @@ public: bool IsPluginValid(const PluginPath & path, bool bFast) override; - ComponentInterface *CreateInstance(const PluginPath & path) override; - void DeleteInstance(ComponentInterface *instance) override; + std::unique_ptr + CreateInstance(const PluginPath & path) override; private: // VampEffectModule implementation diff --git a/src/menus/PluginMenus.cpp b/src/menus/PluginMenus.cpp index 8eaeba3da..d0192a554 100644 --- a/src/menus/PluginMenus.cpp +++ b/src/menus/PluginMenus.cpp @@ -6,6 +6,7 @@ #include "../CommonCommandFlags.h" #include "../Menus.h" #include "../PluginManager.h" +#include "../PluginRegistrationDialog.h" #include "../Prefs.h" #include "../Project.h" #include "../ProjectSettings.h" @@ -36,10 +37,20 @@ AudacityProject::AttachedWindows::RegisteredFactory sMacrosWindowKey{ } }; +bool ShowManager( + PluginManager &pm, wxWindow *parent, EffectType type) +{ + pm.CheckForUpdates(); + + PluginRegistrationDialog dlg(parent, type); + return dlg.ShowModal() == wxID_OK; +} + void DoManagePluginsMenu(AudacityProject &project, EffectType type) { auto &window = GetProjectFrame( project ); - if (PluginManager::Get().ShowManager(&window, type)) + auto &pm = PluginManager::Get(); + if (ShowManager(pm, &window, type)) MenuCreator::RebuildAllMenuBars(); } @@ -302,9 +313,8 @@ MenuTable::BaseItemPtrs PopulateEffectsMenu( std::vector optplugs; EffectManager & em = EffectManager::Get(); - const PluginDescriptor *plug = pm.GetFirstPluginForEffectType(type); - while (plug) - { + for (auto &plugin : pm.EffectsOfType(type)) { + auto plug = &plugin; if( plug->IsInstantiated() && em.IsHidden(plug->GetID()) ) continue; if ( !plug->IsEnabled() ){ @@ -322,7 +332,6 @@ MenuTable::BaseItemPtrs PopulateEffectsMenu( defplugs.push_back(plug); else optplugs.push_back(plug); - plug = pm.GetNextPluginForEffectType(type); } wxString groupby = EffectsGroupBy.Read(); diff --git a/src/prefs/EffectsPrefs.cpp b/src/prefs/EffectsPrefs.cpp index d01dd2df2..2bc0f47c6 100644 --- a/src/prefs/EffectsPrefs.cpp +++ b/src/prefs/EffectsPrefs.cpp @@ -137,10 +137,8 @@ static const std::vector< Entry > &GetModuleData() struct ModuleData : public std::vector< Entry > { ModuleData() { auto &pm = PluginManager::Get(); - for (auto plug = pm.GetFirstPlugin(PluginTypeModule); - plug; - plug = pm.GetNextPlugin(PluginTypeModule)) { - auto internal = plug->GetEffectFamily(); + for (auto &plug : pm.PluginsOfType(PluginTypeModule)) { + auto internal = plug.GetEffectFamily(); if ( internal.empty() ) continue; @@ -153,11 +151,11 @@ static const std::vector< Entry > &GetModuleData() // If there should be new modules, it is not important for them // to follow the " Effects" convention, but instead they can // have shorter msgids. - prompt = plug->GetSymbol().Msgid(); + prompt = plug.GetSymbol().Msgid(); else prompt = iter->second; - auto setting = pm.GetPluginEnabledSetting( *plug ); + auto setting = pm.GetPluginEnabledSetting( plug ); push_back( { prompt, setting } ); }