From 710ec976ff19b2f1f2f8148aeba8831f48566043 Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Tue, 27 Apr 2021 20:24:49 -0400 Subject: [PATCH] Move class PluginRegistrationDialog to a header file --- src/CMakeLists.txt | 1 + src/PluginManager.cpp | 61 +- src/PluginRegistrationDialog.h | 3110 +------------------------------- 3 files changed, 30 insertions(+), 3142 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 908e1deab..b0b88c6fe 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -202,6 +202,7 @@ list( APPEND SOURCES PlaybackSchedule.h PluginManager.cpp PluginManager.h + PluginRegistrationDialog.h Prefs.cpp Prefs.h Printing.cpp diff --git a/src/PluginManager.cpp b/src/PluginManager.cpp index d4600e322..7a18da43c 100644 --- a/src/PluginManager.cpp +++ b/src/PluginManager.cpp @@ -46,6 +46,7 @@ for shared and private configs - which need to move out. #include "FileNames.h" #include "ModuleManager.h" #include "PlatformCompatibility.h" +#include "PluginRegistrationDialog.h" #include "Prefs.h" #include "ShuttleGui.h" #include "wxFileNameWrapper.h" @@ -379,20 +380,6 @@ enum 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, @@ -415,52 +402,6 @@ enum 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) diff --git a/src/PluginRegistrationDialog.h b/src/PluginRegistrationDialog.h index d4600e322..4cd657a45 100644 --- a/src/PluginRegistrationDialog.h +++ b/src/PluginRegistrationDialog.h @@ -2,418 +2,24 @@ Audacity: A Digital Audio Editor - PluginManager.cpp + PluginRegistrationDialog.h - Leland Lucius + Paul Licameli split from PluginManager.cpp -*******************************************************************//*! +**********************************************************************/ +#ifndef __AUDACITY_PLUGIN_REGISTRATION_DIALOG__ +#define __AUDACITY_PLUGIN_REGISTRATION_DIALOG__ -\file PluginManager.cpp -\brief +#include "widgets/wxPanelWrapper.h" // to inherit +#include +#include // member -************************************************************************//** -\class PluginManager -\brief PluginManager maintains a list of all plug ins. That covers modules, -effects, generators, analysis-effects, commands. It also has functions -for shared and private configs - which need to move out. -*****************************************************************************/ - - -#include "PluginManager.h" - - - -#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 "FileNames.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 CheckListAx; +enum EffectType : int; +class PluginDescriptor; +class ShuttleGui; +class wxListEvent; +class wxListCtrl; class PluginRegistrationDialog final : public wxDialogWrapper { @@ -422,6 +28,20 @@ public: 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); @@ -461,2679 +81,5 @@ private: 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 (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); -} - - - -/////////////////////////////////////////////////////////////////////////////// -// -// Plugindescriptor -// -/////////////////////////////////////////////////////////////////////////////// - -PluginDescriptor::PluginDescriptor() -{ - mPluginType = PluginTypeNone; - mEnabled = false; - mValid = false; - mInstance = nullptr; - - mEffectType = EffectTypeNone; - mEffectInteractive = false; - mEffectDefault = false; - mEffectLegacy = false; - mEffectRealtime = false; - mEffectAutomatable = false; -} - -PluginDescriptor::~PluginDescriptor() -{ -} - -PluginDescriptor &PluginDescriptor::operator =(PluginDescriptor &&) = default; - -bool PluginDescriptor::IsInstantiated() const -{ - return mInstance != nullptr; -} - -ComponentInterface *PluginDescriptor::GetInstance() -{ - if (!mInstance) - { - if (GetPluginType() == PluginTypeModule) - mInstance = ModuleManager::Get().CreateProviderInstance(GetID(), GetPath()); - else - { - muInstance = ModuleManager::Get().CreateInstance(GetProviderID(), GetPath()); - mInstance = muInstance.get(); - } - } - - return mInstance; -} - -void PluginDescriptor::SetInstance(std::unique_ptr instance) -{ - muInstance = std::move(instance); - mInstance = muInstance.get(); -} - -PluginType PluginDescriptor::GetPluginType() const -{ - return mPluginType; -} - -const PluginID & PluginDescriptor::GetID() const -{ - return mID; -} - -const PluginID & PluginDescriptor::GetProviderID() const -{ - return mProviderID; -} - -const PluginPath & PluginDescriptor::GetPath() const -{ - return mPath; -} - -const ComponentInterfaceSymbol & PluginDescriptor::GetSymbol() const -{ - return mSymbol; -} - -wxString PluginDescriptor::GetUntranslatedVersion() const -{ - return mVersion; -} - -wxString PluginDescriptor::GetVendor() const -{ - return mVendor; -} - -bool PluginDescriptor::IsEnabled() const -{ - return mEnabled; -} - -bool PluginDescriptor::IsValid() const -{ - return mValid; -} - -void PluginDescriptor::SetPluginType(PluginType type) -{ - mPluginType = type; -} - -void PluginDescriptor::SetID(const PluginID & ID) -{ - mID = ID; -} - -void PluginDescriptor::SetProviderID(const PluginID & providerID) -{ - mProviderID = providerID; -} - -void PluginDescriptor::SetPath(const PluginPath & path) -{ - mPath = path; -} - -void PluginDescriptor::SetSymbol(const ComponentInterfaceSymbol & symbol) -{ - mSymbol = symbol; -} - -void PluginDescriptor::SetVersion(const wxString & version) -{ - mVersion = version; -} - -void PluginDescriptor::SetVendor(const wxString & vendor) -{ - mVendor = vendor; -} - -void PluginDescriptor::SetEnabled(bool enable) -{ - mEnabled = enable; -} - -void PluginDescriptor::SetValid(bool valid) -{ - mValid = valid; -} - -// Effects - -wxString PluginDescriptor::GetEffectFamily() const -{ - return mEffectFamily; -} - -EffectType PluginDescriptor::GetEffectType() const -{ - return mEffectType; -} - -bool PluginDescriptor::IsEffectInteractive() const -{ - return mEffectInteractive; -} - -bool PluginDescriptor::IsEffectDefault() const -{ - return mEffectDefault; -} - -bool PluginDescriptor::IsEffectLegacy() const -{ - return mEffectLegacy; -} - -bool PluginDescriptor::IsEffectRealtime() const -{ - return mEffectRealtime; -} - -bool PluginDescriptor::IsEffectAutomatable() const -{ - return mEffectAutomatable; -} - -void PluginDescriptor::SetEffectFamily(const wxString & family) -{ - mEffectFamily = family; -} - -void PluginDescriptor::SetEffectType(EffectType type) -{ - mEffectType = type; -} - -void PluginDescriptor::SetEffectInteractive(bool interactive) -{ - mEffectInteractive = interactive; -} - -void PluginDescriptor::SetEffectDefault(bool dflt) -{ - mEffectDefault = dflt; -} - -void PluginDescriptor::SetEffectLegacy(bool legacy) -{ - mEffectLegacy = legacy; -} - -void PluginDescriptor::SetEffectRealtime(bool realtime) -{ - mEffectRealtime = realtime; -} - -void PluginDescriptor::SetEffectAutomatable(bool automatable) -{ - mEffectAutomatable = automatable; -} - -// Importer - -const wxString & PluginDescriptor::GetImporterIdentifier() const -{ - return mImporterIdentifier; -} - -void PluginDescriptor::SetImporterIdentifier(const wxString & identifier) -{ - mImporterIdentifier = identifier; -} - -const FileExtensions & PluginDescriptor::GetImporterExtensions() - const -{ - return mImporterExtensions; -} - -void PluginDescriptor::SetImporterExtensions( FileExtensions extensions ) -{ - mImporterExtensions = std::move( extensions ); -} - -/////////////////////////////////////////////////////////////////////////////// -// -// PluginManager -// -/////////////////////////////////////////////////////////////////////////////// - -// Registry has the list of plug ins -#define REGVERKEY wxString(wxT("/pluginregistryversion")) -#define REGVERCUR wxString(wxT("1.1")) -#define REGROOT wxString(wxT("/pluginregistry/")) - -// Settings has the values of the plug in settings. -#define SETVERKEY wxString(wxT("/pluginsettingsversion")) -#define SETVERCUR wxString(wxT("1.0")) -#define SETROOT wxString(wxT("/pluginsettings/")) - -#define KEY_ID wxT("ID") -#define KEY_PATH wxT("Path") -#define KEY_SYMBOL wxT("Symbol") -#define KEY_NAME wxT("Name") -#define KEY_VENDOR wxT("Vendor") -#define KEY_VERSION wxT("Version") -#define KEY_DESCRIPTION wxT("Description") -#define KEY_LASTUPDATED wxT("LastUpdated") -#define KEY_ENABLED wxT("Enabled") -#define KEY_VALID wxT("Valid") -#define KEY_PROVIDERID wxT("ProviderID") -#define KEY_EFFECTTYPE wxT("EffectType") -#define KEY_EFFECTFAMILY wxT("EffectFamily") -#define KEY_EFFECTDEFAULT wxT("EffectDefault") -#define KEY_EFFECTINTERACTIVE wxT("EffectInteractive") -#define KEY_EFFECTREALTIME wxT("EffectRealtime") -#define KEY_EFFECTAUTOMATABLE wxT("EffectAutomatable") -#define KEY_EFFECTTYPE_NONE wxT("None") -#define KEY_EFFECTTYPE_ANALYZE wxT("Analyze") -#define KEY_EFFECTTYPE_GENERATE wxT("Generate") -#define KEY_EFFECTTYPE_PROCESS wxT("Process") -#define KEY_EFFECTTYPE_TOOL wxT("Tool") -#define KEY_EFFECTTYPE_HIDDEN wxT("Hidden") -#define KEY_IMPORTERIDENT wxT("ImporterIdent") -//#define KEY_IMPORTERFILTER wxT("ImporterFilter") -#define KEY_IMPORTEREXTENSIONS wxT("ImporterExtensions") - -// ============================================================================ -// -// PluginManagerInterface implementation -// -// ============================================================================ - -const PluginID &PluginManagerInterface::DefaultRegistrationCallback( - ModuleInterface *provider, ComponentInterface *pInterface ) -{ - EffectDefinitionInterface * pEInterface = dynamic_cast(pInterface); - if( pEInterface ) - return PluginManager::Get().RegisterPlugin(provider, pEInterface, PluginTypeEffect); - ComponentInterface * pCInterface = dynamic_cast(pInterface); - if( pCInterface ) - return PluginManager::Get().RegisterPlugin(provider, pCInterface); - static wxString empty; - return empty; -} - -const PluginID &PluginManagerInterface::AudacityCommandRegistrationCallback( - ModuleInterface *provider, ComponentInterface *pInterface ) -{ - ComponentInterface * pCInterface = dynamic_cast(pInterface); - if( pCInterface ) - return PluginManager::Get().RegisterPlugin(provider, pCInterface); - static wxString empty; - return empty; -} - -RegistryPath PluginManager::GetPluginEnabledSetting( const PluginID &ID ) const -{ - auto pPlugin = GetPlugin( ID ); - if ( pPlugin ) - return GetPluginEnabledSetting( *pPlugin ); - return {}; -} - -RegistryPath PluginManager::GetPluginEnabledSetting( - const PluginDescriptor &desc ) const -{ - switch ( desc.GetPluginType() ) { - case PluginTypeModule: { - // Retrieve optional family symbol that was recorded in - // RegisterPlugin() for the module - auto family = desc.GetEffectFamily(); - if ( family.empty() ) // as for built-in effect and command modules - return {}; - else - return wxT('/') + family + wxT("/Enable"); - } - case PluginTypeEffect: - // do NOT use GetEffectFamily() for this descriptor, but instead, - // delegate to the plugin descriptor of the provider, which may - // be different (may be empty) - return GetPluginEnabledSetting( desc.GetProviderID() ); - default: - return {}; - } -} - -bool PluginManager::IsPluginRegistered( - const PluginPath &path, const TranslatableString *pName) -{ - for (auto &pair : mPlugins) { - if (auto &descriptor = pair.second; descriptor.GetPath() == path) { - if (pName) - descriptor.SetSymbol( - { descriptor.GetSymbol().Internal(), *pName }); - return true; - } - } - return false; -} - -const PluginID & PluginManager::RegisterPlugin(ModuleInterface *module) -{ - PluginDescriptor & plug = CreatePlugin(GetID(module), module, PluginTypeModule); - plug.SetEffectFamily(module->GetOptionalFamilySymbol().Internal()); - - plug.SetEnabled(true); - plug.SetValid(true); - - return plug.GetID(); -} - -const PluginID & PluginManager::RegisterPlugin(ModuleInterface *provider, ComponentInterface *command) -{ - PluginDescriptor & plug = CreatePlugin(GetID(command), command, (PluginType)PluginTypeAudacityCommand); - - plug.SetProviderID(PluginManager::GetID(provider)); - - plug.SetEnabled(true); - plug.SetValid(true); - - return plug.GetID(); -} - -const PluginID & PluginManager::RegisterPlugin(ModuleInterface *provider, EffectDefinitionInterface *effect, int type) -{ - PluginDescriptor & plug = CreatePlugin(GetID(effect), effect, (PluginType)type); - - plug.SetProviderID(PluginManager::GetID(provider)); - - plug.SetEffectType(effect->GetClassification()); - plug.SetEffectFamily(effect->GetFamily().Internal()); - plug.SetEffectInteractive(effect->IsInteractive()); - plug.SetEffectDefault(effect->IsDefault()); - plug.SetEffectRealtime(effect->SupportsRealtime()); - plug.SetEffectAutomatable(effect->SupportsAutomation()); - - plug.SetEnabled(true); - plug.SetValid(true); - - return plug.GetID(); -} - -const PluginID & PluginManager::RegisterPlugin(ModuleInterface *provider, ImporterInterface *importer) -{ - PluginDescriptor & plug = CreatePlugin(GetID(importer), importer, PluginTypeImporter); - - plug.SetProviderID(PluginManager::GetID(provider)); - - plug.SetImporterIdentifier(importer->GetPluginStringID()); - plug.SetImporterExtensions(importer->GetSupportedExtensions()); - - return plug.GetID(); -} - -void PluginManager::FindFilesInPathList(const wxString & pattern, - const FilePaths & pathList, - FilePaths & files, - bool directories) -{ - - wxLogNull nolog; - - // Why bother... - if (pattern.empty()) - { - return; - } - - // TODO: We REALLY need to figure out the "Audacity" plug-in path(s) - - FilePaths paths; - - // Add the "per-user" plug-ins directory - { - const wxFileName &ff = FileNames::PlugInDir(); - paths.push_back(ff.GetFullPath()); - } - - // Add the "Audacity" plug-ins directory - wxFileName ff = PlatformCompatibility::GetExecutablePath(); -#if defined(__WXMAC__) - // Path ends for example in "Audacity.app/Contents/MacOSX" - //ff.RemoveLastDir(); - //ff.RemoveLastDir(); - // just remove the MacOSX part. - ff.RemoveLastDir(); -#endif - ff.AppendDir(wxT("plug-ins")); - paths.push_back(ff.GetPath()); - - // Weed out duplicates - for (const auto &filePath : pathList) - { - ff = filePath; - const wxString path{ ff.GetFullPath() }; - if (paths.Index(path, wxFileName::IsCaseSensitive()) == wxNOT_FOUND) - { - paths.push_back(path); - } - } - - // Find all matching files in each path - for (size_t i = 0, cnt = paths.size(); i < cnt; i++) - { - ff = paths[i] + wxFILE_SEP_PATH + pattern; - wxDir::GetAllFiles(ff.GetPath(), &files, ff.GetFullName(), directories ? wxDIR_DEFAULT : wxDIR_FILES); - } - - return; -} - -bool PluginManager::HasSharedConfigGroup(const PluginID & ID, const RegistryPath & group) -{ - return HasGroup(SharedGroup(ID, group)); -} - -bool PluginManager::GetSharedConfigSubgroups(const PluginID & ID, const RegistryPath & group, RegistryPaths & subgroups) -{ - return GetSubgroups(SharedGroup(ID, group), subgroups); -} - -bool PluginManager::GetSharedConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key, wxString & value, const wxString & defval) -{ - return GetConfig(SharedKey(ID, group, key), value, defval); -} - -bool PluginManager::GetSharedConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key, int & value, int defval) -{ - return GetConfig(SharedKey(ID, group, key), value, defval); -} - -bool PluginManager::GetSharedConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key, bool & value, bool defval) -{ - return GetConfig(SharedKey(ID, group, key), value, defval); -} - -bool PluginManager::GetSharedConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key, float & value, float defval) -{ - return GetConfig(SharedKey(ID, group, key), value, defval); -} - -bool PluginManager::GetSharedConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key, double & value, double defval) -{ - return GetConfig(SharedKey(ID, group, key), value, defval); -} - -bool PluginManager::SetSharedConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key, const wxString & value) -{ - return SetConfig(SharedKey(ID, group, key), value); -} - -bool PluginManager::SetSharedConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key, const int & value) -{ - return SetConfig(SharedKey(ID, group, key), value); -} - -bool PluginManager::SetSharedConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key, const bool & value) -{ - return SetConfig(SharedKey(ID, group, key), value); -} - -bool PluginManager::SetSharedConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key, const float & value) -{ - return SetConfig(SharedKey(ID, group, key), value); -} - -bool PluginManager::SetSharedConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key, const double & value) -{ - return SetConfig(SharedKey(ID, group, key), value); -} - -bool PluginManager::RemoveSharedConfigSubgroup(const PluginID & ID, const RegistryPath & group) -{ - bool result = GetSettings()->DeleteGroup(SharedGroup(ID, group)); - if (result) - { - GetSettings()->Flush(); - } - - return result; -} - -bool PluginManager::RemoveSharedConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key) -{ - bool result = GetSettings()->DeleteEntry(SharedKey(ID, group, key)); - if (result) - { - GetSettings()->Flush(); - } - - return result; -} - -bool PluginManager::HasPrivateConfigGroup(const PluginID & ID, const RegistryPath & group) -{ - return HasGroup(PrivateGroup(ID, group)); -} - -bool PluginManager::GetPrivateConfigSubgroups(const PluginID & ID, const RegistryPath & group, RegistryPaths & subgroups) -{ - return GetSubgroups(PrivateGroup(ID, group), subgroups); -} - -bool PluginManager::GetPrivateConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key, wxString & value, const wxString & defval) -{ - return GetConfig(PrivateKey(ID, group, key), value, defval); -} - -bool PluginManager::GetPrivateConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key, int & value, int defval) -{ - return GetConfig(PrivateKey(ID, group, key), value, defval); -} - -bool PluginManager::GetPrivateConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key, bool & value, bool defval) -{ - return GetConfig(PrivateKey(ID, group, key), value, defval); -} - -bool PluginManager::GetPrivateConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key, float & value, float defval) -{ - return GetConfig(PrivateKey(ID, group, key), value, defval); -} - -bool PluginManager::GetPrivateConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key, double & value, double defval) -{ - return GetConfig(PrivateKey(ID, group, key), value, defval); -} - -bool PluginManager::SetPrivateConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key, const wxString & value) -{ - return SetConfig(PrivateKey(ID, group, key), value); -} - -bool PluginManager::SetPrivateConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key, const int & value) -{ - return SetConfig(PrivateKey(ID, group, key), value); -} - -bool PluginManager::SetPrivateConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key, const bool & value) -{ - return SetConfig(PrivateKey(ID, group, key), value); -} - -bool PluginManager::SetPrivateConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key, const float & value) -{ - return SetConfig(PrivateKey(ID, group, key), value); -} - -bool PluginManager::SetPrivateConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key, const double & value) -{ - return SetConfig(PrivateKey(ID, group, key), value); -} - -bool PluginManager::RemovePrivateConfigSubgroup(const PluginID & ID, const RegistryPath & group) -{ - bool result = GetSettings()->DeleteGroup(PrivateGroup(ID, group)); - if (result) - { - GetSettings()->Flush(); - } - - return result; -} - -bool PluginManager::RemovePrivateConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key) -{ - bool result = GetSettings()->DeleteEntry(PrivateKey(ID, group, key)); - if (result) - { - GetSettings()->Flush(); - } - - return result; -} - -// ============================================================================ -// -// PluginManager -// -// ============================================================================ - -// The one and only PluginManager -std::unique_ptr PluginManager::mInstance{}; - -// ---------------------------------------------------------------------------- -// Creation/Destruction -// ---------------------------------------------------------------------------- - -PluginManager::PluginManager() -{ - mSettings = NULL; -} - -PluginManager::~PluginManager() -{ - // Ensure termination (harmless if already done) - Terminate(); -} - -// ---------------------------------------------------------------------------- -// PluginManager implementation -// ---------------------------------------------------------------------------- - -// ============================================================================ -// -// Return reference to singleton -// -// (Thread-safe...no active threading during construction or after destruction) -// ============================================================================ - -PluginManager & PluginManager::Get() -{ - if (!mInstance) - { - mInstance.reset(safenew PluginManager); - } - - return *mInstance; -} - -void PluginManager::Initialize() -{ - // Always load the registry first - Load(); - - // And force load of setting to verify it's accessible - GetSettings(); - - // Then look for providers (they may autoregister plugins) - ModuleManager::Get().DiscoverProviders(); - - // And finally check for updates -#ifndef EXPERIMENTAL_EFFECT_MANAGEMENT - CheckForUpdates(); -#else - const bool kFast = true; - CheckForUpdates( kFast ); -#endif -} - -void PluginManager::Terminate() -{ - // Get rid of all non-module plugins first - PluginMap::iterator iter = mPlugins.begin(); - while (iter != mPlugins.end()) - { - PluginDescriptor & plug = iter->second; - if (plug.GetPluginType() == PluginTypeEffect) - { - mPlugins.erase(iter++); - continue; - } - - ++iter; - } - - // Now get rid of the modules - iter = mPlugins.begin(); - while (iter != mPlugins.end()) - { - mPlugins.erase(iter++); - } -} - -bool PluginManager::DropFile(const wxString &fileName) -{ - auto &mm = ModuleManager::Get(); - const wxFileName src{ fileName }; - - for (auto &plug : PluginsOfType(PluginTypeModule)) { - auto module = static_cast - (mm.CreateProviderInstance(plug.GetID(), plug.GetPath())); - if (! module) - continue; - - const auto &ff = module->InstallPath(); - const auto &extensions = module->GetFileExtensions(); - if ( !ff.empty() && - extensions.Index(src.GetExt(), false) != wxNOT_FOUND ) { - TranslatableString errMsg; - // Do dry-run test of the file format - unsigned nPlugIns = - module->DiscoverPluginsAtPath(fileName, errMsg, {}); - if (nPlugIns) { - // File contents are good for this module, so check no others. - // All branches of this block return true, even in case of - // failure for other reasons, to signal that other drag-and-drop - // actions should not be tried. - - // Find path to copy it - wxFileName dst; - dst.AssignDir( ff ); - dst.SetFullName( src.GetFullName() ); - if ( dst.Exists() ) { - // Query whether to overwrite - bool overwrite = (wxYES == ::AudacityMessageBox( - XO("Overwrite the plug-in file %s?") - .Format( dst.GetFullPath() ), - XO("Plug-in already exists"), - wxYES_NO ) ); - if ( !overwrite ) - return true; - } - - // Move the file or subtree - bool copied = false; - auto dstPath = dst.GetFullPath(); - if ( src.FileExists() ) - // A simple one-file plug-in - copied = FileNames::DoCopyFile( - src.GetFullPath(), dstPath, true ); - else { - // A sub-folder - // such as for some VST packages - // Recursive copy needed -- to do - return true; - } - - if (!copied) { - ::AudacityMessageBox( - XO("Plug-in file is in use. Failed to overwrite") ); - return true; - } - - // Register for real - std::vector ids; - std::vector names; - nPlugIns = module->DiscoverPluginsAtPath(dstPath, errMsg, - [&](ModuleInterface *provider, ComponentInterface *ident) - -> const PluginID& { - // Register as by default, but also collecting the PluginIDs - // and names - auto &id = PluginManagerInterface::DefaultRegistrationCallback( - provider, ident); - ids.push_back(id); - names.push_back( ident->GetSymbol().Translation() ); - return id; - }); - if ( ! nPlugIns ) { - // Unlikely after the dry run succeeded - ::AudacityMessageBox( - XO("Failed to register:\n%s").Format( errMsg ) ); - return true; - } - - // Ask whether to enable the plug-ins - if (auto nIds = ids.size()) { - auto message = XPC( - /* i18n-hint A plug-in is an optional added program for a sound - effect, or generator, or analyzer */ - "Enable this plug-in?\n", - "Enable these plug-ins?\n", - 0, - "plug-ins" - )( nIds ); - for (const auto &name : names) - message.Join( Verbatim( name ), wxT("\n") ); - bool enable = (wxYES == ::AudacityMessageBox( - message, - XO("Enable new plug-ins"), - wxYES_NO ) ); - for (const auto &id : ids) - mPlugins[id].SetEnabled(enable); - // Make changes to enabled status persist: - this->Save(); - } - - return true; - } - } - } - - return false; -} - -void PluginManager::Load() -{ - // Create/Open the registry - auto pRegistry = AudacityFileConfig::Create( - {}, {}, FileNames::PluginRegistry()); - auto ®istry = *pRegistry; - - // If this group doesn't exist then we have something that's not a registry. - // We should probably warn the user, but it's pretty unlikely that this will happen. - if (!registry.HasGroup(REGROOT)) - { - // Must start over - registry.DeleteAll(); - registry.Flush(); - return; - } - - // Check for a registry version that we can understand - // TODO: Should also check for a registry file that is newer than - // what we can understand. - wxString regver = registry.Read(REGVERKEY); - if (regver < REGVERCUR ) - { - // Conversion code here, for when registry version changes. - - // We iterate through the effects, possibly updating their info. - wxString groupName; - long groupIndex; - wxString group = GetPluginTypeString(PluginTypeEffect); - wxString cfgPath = REGROOT + group + wxCONFIG_PATH_SEPARATOR; - wxArrayString groupsToDelete; - - registry.SetPath(cfgPath); - for (bool cont = registry.GetFirstGroup(groupName, groupIndex); - cont; - registry.SetPath(cfgPath), - cont = registry.GetNextGroup(groupName, groupIndex)) - { - registry.SetPath(groupName); - wxString effectSymbol = registry.Read(KEY_SYMBOL, ""); - wxString effectVersion = registry.Read(KEY_VERSION, ""); - - - // For 2.3.0 the plugins we distribute have moved around. - // So we upped the registry version number to 1.1. - // These particular config edits were originally written to fix Bug 1914. - if (regver <= "1.0") { - // Nyquist prompt is a built-in that has moved to the tools menu. - if (effectSymbol == NYQUIST_PROMPT_ID) { - registry.Write(KEY_EFFECTTYPE, "Tool"); - // Old version of SDE was in Analyze menu. Now it is in Tools. - // We don't want both the old and the new. - } else if ((effectSymbol == "Sample Data Export") && (effectVersion == "n/a")) { - groupsToDelete.push_back(cfgPath + groupName); - // Old version of SDI was in Generate menu. Now it is in Tools. - } else if ((effectSymbol == "Sample Data Import") && (effectVersion == "n/a")) { - groupsToDelete.push_back(cfgPath + groupName); - } - } - - } - // Doing the deletion within the search loop risked skipping some items, - // hence the delayed delete. - for (unsigned int i = 0; i < groupsToDelete.size(); i++) { - registry.DeleteGroup(groupsToDelete[i]); - } - registry.SetPath(""); - registry.Write(REGVERKEY, REGVERCUR); - // Updates done. Make sure we read the updated data later. - registry.Flush(); - } - - // Load all provider plugins first - LoadGroup(®istry, PluginTypeModule); - - // Now the rest - LoadGroup(®istry, PluginTypeEffect); - LoadGroup(®istry, PluginTypeAudacityCommand ); - LoadGroup(®istry, PluginTypeExporter); - LoadGroup(®istry, PluginTypeImporter); - - LoadGroup(®istry, PluginTypeStub); - return; -} - -void PluginManager::LoadGroup(FileConfig *pRegistry, PluginType type) -{ -#ifdef __WXMAC__ - // Bug 1590: On Mac, we should purge the registry of Nyquist plug-ins - // bundled with other versions of Audacity, assuming both versions - // were properly installed in /Applications (or whatever it is called in - // your locale) - - const auto fullExePath = PlatformCompatibility::GetExecutablePath(); - - // Strip rightmost path components up to *.app - wxFileName exeFn{ fullExePath }; - exeFn.SetEmptyExt(); - exeFn.SetName(wxString{}); - while(exeFn.GetDirCount() && !exeFn.GetDirs().back().EndsWith(".app")) - exeFn.RemoveLastDir(); - - const auto goodPath = exeFn.GetPath(); - - if(exeFn.GetDirCount()) - exeFn.RemoveLastDir(); - const auto possiblyBadPath = exeFn.GetPath(); - - auto AcceptPath = [&](const wxString &path) { - if (!path.StartsWith(possiblyBadPath)) - // Assume it's not under /Applications - return true; - if (path.StartsWith(goodPath)) - // It's bundled with this executable - return true; - return false; - }; -#else - auto AcceptPath = [](const wxString&){ return true; }; -#endif - - wxString strVal; - bool boolVal; - wxString groupName; - long groupIndex; - wxString group = GetPluginTypeString(type); - wxString cfgPath = REGROOT + group + wxCONFIG_PATH_SEPARATOR; - - pRegistry->SetPath(cfgPath); - for (bool cont = pRegistry->GetFirstGroup(groupName, groupIndex); - cont; - pRegistry->SetPath(cfgPath), - cont = pRegistry->GetNextGroup(groupName, groupIndex)) - { - PluginDescriptor plug; - - pRegistry->SetPath(groupName); - - groupName = ConvertID(groupName); - - // Bypass group if the ID is already in use - if (mPlugins.count(groupName)) - continue; - - // Set the ID and type - plug.SetID(groupName); - plug.SetPluginType(type); - - // Get the provider ID and bypass group if not found - if (!pRegistry->Read(KEY_PROVIDERID, &strVal, wxEmptyString)) - { - // Bypass group if the provider isn't valid - if (!strVal.empty() && !mPlugins.count(strVal)) - continue; - } - plug.SetProviderID(PluginID(strVal)); - - // Get the path (optional) - pRegistry->Read(KEY_PATH, &strVal, wxEmptyString); - if (!AcceptPath(strVal)) - // Ignore the obsolete path in the config file, during session, - // but don't remove it from the file. Maybe you really want to - // switch back to the other version of Audacity and lose nothing. - continue; - plug.SetPath(strVal); - - /* - // PRL: Ignore names written in configs before 2.3.0! - // use Internal string only! Let the present version of Audacity map - // that to a user-visible string. - // Get the name and bypass group if not found - if (!pRegistry->Read(KEY_NAME, &strVal)) - { - continue; - } - plug.SetName(strVal); - */ - - // Get the symbol...Audacity 2.3.0 or later requires it - // bypass group if not found - // Note, KEY_SYMBOL started getting written to config files in 2.1.0. - // KEY_NAME (now ignored) was written before that, but only for VST - // effects. - if (!pRegistry->Read(KEY_SYMBOL, &strVal)) - continue; - - // Related to Bug2778: config file only remembered an internal name, - // so this symbol may not contain the correct TranslatableString. - // See calls to IsPluginRegistered which can correct that. - plug.SetSymbol(strVal); - - // Get the version and bypass group if not found - if (!pRegistry->Read(KEY_VERSION, &strVal)) - { - continue; - } - plug.SetVersion(strVal); - - // Get the vendor and bypass group if not found - if (!pRegistry->Read(KEY_VENDOR, &strVal)) - { - continue; - } - plug.SetVendor( strVal ); - -#if 0 - // This was done before version 2.2.2, but the value was not really used - // But absence of a value will cause early versions to skip the group - // Therefore we still write a blank to keep pluginregistry.cfg - // backwards-compatible - - // Get the description and bypass group if not found - if (!pRegistry->Read(KEY_DESCRIPTION, &strVal)) - { - continue; - } -#endif - - // Is it enabled...default to no if not found - pRegistry->Read(KEY_ENABLED, &boolVal, false); - plug.SetEnabled(boolVal); - - // Is it valid...default to no if not found - pRegistry->Read(KEY_VALID, &boolVal, false); - plug.SetValid(boolVal); - - switch (type) - { - case PluginTypeModule: - { - // Nothing to do here yet - } - break; - - case PluginTypeEffect: - { - // Get the effect type and bypass group if not found - if (!pRegistry->Read(KEY_EFFECTTYPE, &strVal)) - continue; - - if (strVal == KEY_EFFECTTYPE_NONE) - plug.SetEffectType(EffectTypeNone); - else if (strVal == KEY_EFFECTTYPE_ANALYZE) - plug.SetEffectType(EffectTypeAnalyze); - else if (strVal == KEY_EFFECTTYPE_GENERATE) - plug.SetEffectType(EffectTypeGenerate); - else if (strVal == KEY_EFFECTTYPE_PROCESS) - plug.SetEffectType(EffectTypeProcess); - else if (strVal == KEY_EFFECTTYPE_TOOL) - plug.SetEffectType(EffectTypeTool); - else if (strVal == KEY_EFFECTTYPE_HIDDEN) - plug.SetEffectType(EffectTypeHidden); - else - continue; - - // Get the effect family and bypass group if not found - if (!pRegistry->Read(KEY_EFFECTFAMILY, &strVal)) - { - continue; - } - plug.SetEffectFamily(strVal); - - // Is it a default (above the line) effect and bypass group if not found - if (!pRegistry->Read(KEY_EFFECTDEFAULT, &boolVal)) - { - continue; - } - plug.SetEffectDefault(boolVal); - - // Is it an interactive effect and bypass group if not found - if (!pRegistry->Read(KEY_EFFECTINTERACTIVE, &boolVal)) - { - continue; - } - plug.SetEffectInteractive(boolVal); - - // Is it a realtime capable effect and bypass group if not found - if (!pRegistry->Read(KEY_EFFECTREALTIME, &boolVal)) - { - continue; - } - plug.SetEffectRealtime(boolVal); - - // Does the effect support automation...bypass group if not found - if (!pRegistry->Read(KEY_EFFECTAUTOMATABLE, &boolVal)) - { - continue; - } - plug.SetEffectAutomatable(boolVal); - } - break; - - case PluginTypeImporter: - { - // Get the importer identifier and bypass group if not found - if (!pRegistry->Read(KEY_IMPORTERIDENT, &strVal)) - { - continue; - } - plug.SetImporterIdentifier(strVal); - - // Get the importer extensions and bypass group if not found - if (!pRegistry->Read(KEY_IMPORTEREXTENSIONS, &strVal)) - { - continue; - } - FileExtensions extensions; - wxStringTokenizer tkr(strVal, wxT(":")); - while (tkr.HasMoreTokens()) - { - extensions.push_back(tkr.GetNextToken()); - } - plug.SetImporterExtensions(extensions); - } - break; - - case PluginTypeStub: - { - // Nothing additional for stubs - } - break; - - // Not used by 2.1.1 or greater and should be removed after a few releases past 2.1.0. - case PluginTypeNone: - { - // Used for stub groups - } - break; - - default: - { - continue; - } - } - - // Everything checked out...accept the plugin - mPlugins[groupName] = std::move(plug); - } - - return; -} - -void PluginManager::Save() -{ - // Create/Open the registry - auto pRegistry = AudacityFileConfig::Create( - {}, {}, FileNames::PluginRegistry()); - auto ®istry = *pRegistry; - - // Clear it out - registry.DeleteAll(); - - // Write the version string - registry.Write(REGVERKEY, REGVERCUR); - - // Save the individual groups - SaveGroup(®istry, PluginTypeEffect); - SaveGroup(®istry, PluginTypeExporter); - SaveGroup(®istry, PluginTypeAudacityCommand); - SaveGroup(®istry, PluginTypeImporter); - SaveGroup(®istry, PluginTypeStub); - - // Not used by 2.1.1 or greater, but must save to allow users to switch between 2.1.0 - // and 2.1.1+. This should be removed after a few releases past 2.1.0. - //SaveGroup(®istry, PluginTypeNone); - - // And now the providers - SaveGroup(®istry, PluginTypeModule); - - // Just to be safe - registry.Flush(); -} - -void PluginManager::SaveGroup(FileConfig *pRegistry, PluginType type) -{ - wxString group = GetPluginTypeString(type); - for (auto &pair : mPlugins) { - auto & plug = pair.second; - - if (plug.GetPluginType() != type) - { - continue; - } - - pRegistry->SetPath(REGROOT + group + wxCONFIG_PATH_SEPARATOR + ConvertID(plug.GetID())); - - pRegistry->Write(KEY_PATH, plug.GetPath()); - - // See comments with the corresponding load-time call to SetSymbol(). - pRegistry->Write(KEY_SYMBOL, plug.GetSymbol().Internal()); - - // PRL: Writing KEY_NAME which is no longer read, but older Audacity - // versions expect to find it. - pRegistry->Write(KEY_NAME, plug.GetSymbol().Msgid().MSGID()); - - pRegistry->Write(KEY_VERSION, plug.GetUntranslatedVersion()); - pRegistry->Write(KEY_VENDOR, plug.GetVendor()); - // Write a blank -- see comments in LoadGroup: - pRegistry->Write(KEY_DESCRIPTION, wxString{}); - pRegistry->Write(KEY_PROVIDERID, plug.GetProviderID()); - pRegistry->Write(KEY_ENABLED, plug.IsEnabled()); - pRegistry->Write(KEY_VALID, plug.IsValid()); - - switch (type) - { - case PluginTypeModule: - break; - - case PluginTypeEffect: - { - EffectType etype = plug.GetEffectType(); - wxString stype; - if (etype == EffectTypeNone) - stype = KEY_EFFECTTYPE_NONE; - else if (etype == EffectTypeAnalyze) - stype = KEY_EFFECTTYPE_ANALYZE; - else if (etype == EffectTypeGenerate) - stype = KEY_EFFECTTYPE_GENERATE; - else if (etype == EffectTypeProcess) - stype = KEY_EFFECTTYPE_PROCESS; - else if (etype == EffectTypeTool) - stype = KEY_EFFECTTYPE_TOOL; - else if (etype == EffectTypeHidden) - stype = KEY_EFFECTTYPE_HIDDEN; - - pRegistry->Write(KEY_EFFECTTYPE, stype); - pRegistry->Write(KEY_EFFECTFAMILY, plug.GetEffectFamily()); - pRegistry->Write(KEY_EFFECTDEFAULT, plug.IsEffectDefault()); - pRegistry->Write(KEY_EFFECTINTERACTIVE, plug.IsEffectInteractive()); - pRegistry->Write(KEY_EFFECTREALTIME, plug.IsEffectRealtime()); - pRegistry->Write(KEY_EFFECTAUTOMATABLE, plug.IsEffectAutomatable()); - } - break; - - case PluginTypeImporter: - { - pRegistry->Write(KEY_IMPORTERIDENT, plug.GetImporterIdentifier()); - const auto & extensions = plug.GetImporterExtensions(); - wxString strExt; - for (size_t i = 0, cnt = extensions.size(); i < cnt; i++) - { - strExt += extensions[i] + wxT(":"); - } - strExt.RemoveLast(1); - pRegistry->Write(KEY_IMPORTEREXTENSIONS, strExt); - } - break; - - default: - break; - } - } - - return; -} - -// If bFast is true, do not do a full check. Just check the ones -// that are quick to check. Currently (Feb 2017) just Nyquist -// and built-ins. -void PluginManager::CheckForUpdates(bool bFast) -{ - ModuleManager & mm = ModuleManager::Get(); - wxArrayString pathIndex; - 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) - pathIndex.push_back(plug.GetPath().BeforeFirst(wxT(';'))); - } - - // Check all known plugins to ensure they are still valid and scan for NEW ones. - // - // All NEW plugins get a stub entry created that will remain in place until the - // user enables or disables the plugin. - // - // Because we use the plugins "path" as returned by the providers, we can actually - // have multiple providers report the same path since, at this point, they only - // know that the path might possibly be one supported by the provider. - // - // When the user enables the plugin, each provider that reported it will be asked - // to register the plugin. - for (auto &pair : mPlugins) { - auto &plug = pair.second; - const PluginID & plugID = plug.GetID(); - const wxString & plugPath = plug.GetPath(); - PluginType plugType = plug.GetPluginType(); - - // Bypass 2.1.0 placeholders...remove this after a few releases past 2.1.0 - if (plugType == PluginTypeNone) - { - continue; - } - - if ( plugType == PluginTypeModule ) - { - if( bFast ) - { - // Skip modules, when doing a fast refresh/check. - } - else if (!mm.IsProviderValid(plugID, plugPath)) - { - plug.SetEnabled(false); - plug.SetValid(false); - } - else - { - // Collect plugin paths - auto paths = mm.FindPluginsForProvider(plugID, plugPath); - for (size_t i = 0, cnt = paths.size(); i < cnt; i++) - { - wxString path = paths[i].BeforeFirst(wxT(';'));; - if ( ! make_iterator_range( pathIndex ).contains( path ) ) - { - PluginID ID = plugID + wxT("_") + path; - PluginDescriptor & plug2 = mPlugins[ID]; // This will create a NEW descriptor - plug2.SetPluginType(PluginTypeStub); - plug2.SetID(ID); - plug2.SetProviderID(plugID); - plug2.SetPath(path); - plug2.SetEnabled(false); - plug2.SetValid(false); - } - } - } - } - else if (plugType != PluginTypeNone && plugType != PluginTypeStub) - { - plug.SetValid(mm.IsPluginValid(plug.GetProviderID(), plugPath, bFast)); - if (!plug.IsValid()) - { - plug.SetEnabled(false); - } - } - } - - Save(); - - 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( - std::unique_ptr effect, PluginType type) -{ - PluginDescriptor & plug = - CreatePlugin(GetID(effect.get()), effect.get(), type); - - plug.SetEffectType(effect->GetType()); - plug.SetEffectFamily(effect->GetFamily().Internal()); - plug.SetEffectInteractive(effect->IsInteractive()); - plug.SetEffectDefault(effect->IsDefault()); - plug.SetEffectRealtime(effect->SupportsRealtime()); - plug.SetEffectAutomatable(effect->SupportsAutomation()); - - plug.SetInstance(std::move(effect)); - plug.SetEffectLegacy(true); - plug.SetEnabled(true); - plug.SetValid(true); - - return plug.GetID(); -} - -void PluginManager::UnregisterPlugin(const PluginID & ID) -{ - mPlugins.erase(ID); -} - -int PluginManager::GetPluginCount(PluginType type) -{ - return count_if(mPlugins.begin(), mPlugins.end(), [type](auto &pair){ - return pair.second.GetPluginType() == type; }); -} - -const PluginDescriptor *PluginManager::GetPlugin(const PluginID & ID) const -{ - if (auto iter = mPlugins.find(ID); iter == mPlugins.end()) - return nullptr; - else - return &iter->second; -} - -void PluginManager::Iterator::Advance(bool incrementing) -{ - 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 = mPm.GetPluginEnabledSetting( plug ); - if (!(setting.empty() || gPrefs->Read( setting, true ))) - continue; - } - // Pause iteration at this match - break; - } - } -} - -PluginManager::Iterator::Iterator(PluginManager &manager) -: mPm{ manager } -, mIterator{ manager.mPlugins.begin() } -{ -} - -PluginManager::Iterator::Iterator(PluginManager &manager, int type) -: mPm{ manager } -, mIterator{ manager.mPlugins.begin() } -, mPluginType{ type } -{ - Advance(false); -} - -PluginManager::Iterator::Iterator(PluginManager &manager, EffectType type) -: mPm{ manager } -, mIterator{ manager.mPlugins.begin() } -, mEffectType{ type } -{ - Advance(false); -} - -auto PluginManager::Iterator::operator ++() -> Iterator & -{ - Advance(true); - return *this; -} - -bool PluginManager::IsPluginEnabled(const PluginID & ID) -{ - if (auto iter = mPlugins.find(ID); iter == mPlugins.end()) - return false; - else - return iter->second.IsEnabled(); -} - -void PluginManager::EnablePlugin(const PluginID & ID, bool enable) -{ - if (auto iter = mPlugins.find(ID); iter == mPlugins.end()) - return; - else - iter->second.SetEnabled(enable); -} - -const ComponentInterfaceSymbol & PluginManager::GetSymbol(const PluginID & ID) -{ - if (auto iter = mPlugins.find(ID); iter == mPlugins.end()) { - static ComponentInterfaceSymbol empty; - return empty; - } - else - return iter->second.GetSymbol(); -} - -ComponentInterface *PluginManager::GetInstance(const PluginID & ID) -{ - if (auto iter = mPlugins.find(ID); iter == mPlugins.end()) - return nullptr; - else { - auto &plug = iter->second; - - // If not dealing with legacy effects, make sure the provider is loaded - if (!plug.IsEffectLegacy()) - { - const PluginID & prov = plug.GetProviderID(); - if (auto iter2 = mPlugins.find(prov); iter2 == mPlugins.end()) - return nullptr; - else - iter2->second.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) -{ - return wxString::Format(wxT("%s_%s_%s_%s_%s"), - GetPluginTypeString(PluginTypeAudacityCommand), - wxEmptyString, - command->GetVendor().Internal(), - command->GetSymbol().Internal(), - command->GetPath()); -} - -PluginID PluginManager::GetID(EffectDefinitionInterface *effect) -{ - return wxString::Format(wxT("%s_%s_%s_%s_%s"), - GetPluginTypeString(PluginTypeEffect), - effect->GetFamily().Internal(), - effect->GetVendor().Internal(), - effect->GetSymbol().Internal(), - effect->GetPath()); -} - -PluginID PluginManager::GetID(ImporterInterface *importer) -{ - return wxString::Format(wxT("%s_%s_%s_%s_%s"), - GetPluginTypeString(PluginTypeImporter), - wxEmptyString, - importer->GetVendor().Internal(), - importer->GetSymbol().Internal(), - importer->GetPath()); -} - -// This string persists in configuration files -// So config compatibility will break if it is changed across Audacity versions -wxString PluginManager::GetPluginTypeString(PluginType type) -{ - wxString str; - - switch (type) - { - default: - case PluginTypeNone: - str = wxT("Placeholder"); - break; - case PluginTypeStub: - str = wxT("Stub"); - break; - case PluginTypeEffect: - str = wxT("Effect"); - break; - case PluginTypeAudacityCommand: - str = wxT("Generic"); - break; - case PluginTypeExporter: - str = wxT("Exporter"); - break; - case PluginTypeImporter: - str = wxT("Importer"); - break; - case PluginTypeModule: - str = wxT("Module"); - break; - } - - return str; -} - -PluginDescriptor & PluginManager::CreatePlugin(const PluginID & id, - ComponentInterface *ident, - PluginType type) -{ - // This will either create a NEW entry or replace an existing entry - PluginDescriptor & plug = mPlugins[id]; - - plug.SetPluginType(type); - - plug.SetID(id); - plug.SetPath(ident->GetPath()); - plug.SetSymbol(ident->GetSymbol()); - plug.SetVendor(ident->GetVendor().Internal()); - plug.SetVersion(ident->GetVersion()); - - return plug; -} - -FileConfig *PluginManager::GetSettings() -{ - if (!mSettings) - { - mSettings = - AudacityFileConfig::Create({}, {}, FileNames::PluginSettings()); - - // Check for a settings version that we can understand - if (mSettings->HasEntry(SETVERKEY)) - { - wxString setver = mSettings->Read(SETVERKEY, SETVERKEY); - if (setver < SETVERCUR ) - { - // This is where we'd put in conversion code when the - // settings version changes. - // - // Should also check for a settings file that is newer than - // what we can understand. - } - } - else - { - // Make sure is has a version string - mSettings->Write(SETVERKEY, SETVERCUR); - mSettings->Flush(); - } - } - - return mSettings.get(); -} - -bool PluginManager::HasGroup(const RegistryPath & group) -{ - auto settings = GetSettings(); - - bool res = settings->HasGroup(group); - if (res) - { - // The group exists, but empty groups aren't considered valid - wxString oldPath = settings->GetPath(); - settings->SetPath(group); - res = settings->GetNumberOfEntries() || settings->GetNumberOfGroups(); - settings->SetPath(oldPath); - } - - return res; -} - -bool PluginManager::GetSubgroups(const RegistryPath & group, RegistryPaths & subgroups) -{ - if (group.empty() || !HasGroup(group)) - { - return false; - } - - wxString path = GetSettings()->GetPath(); - GetSettings()->SetPath(group); - - wxString name; - long index = 0; - if (GetSettings()->GetFirstGroup(name, index)) - { - do - { - subgroups.push_back(name); - } while (GetSettings()->GetNextGroup(name, index)); - } - - GetSettings()->SetPath(path); - - return true; -} - -bool PluginManager::GetConfig(const RegistryPath & key, int & value, int defval) -{ - bool result = false; - - if (!key.empty()) - { - result = GetSettings()->Read(key, &value, defval); - } - - return result; -} - -bool PluginManager::GetConfig(const RegistryPath & key, wxString & value, const wxString & defval) -{ - bool result = false; - - if (!key.empty()) - { - wxString wxval; - - result = GetSettings()->Read(key, &wxval, defval); - - value = wxval; - } - - return result; -} - -bool PluginManager::GetConfig(const RegistryPath & key, bool & value, bool defval) -{ - bool result = false; - - if (!key.empty()) - { - result = GetSettings()->Read(key, &value, defval); - } - - return result; -} - -bool PluginManager::GetConfig(const RegistryPath & key, float & value, float defval) -{ - bool result = false; - - if (!key.empty()) - { - double dval = 0.0; - - result = GetSettings()->Read(key, &dval, (double) defval); - - value = (float) dval; - } - - return result; -} - -bool PluginManager::GetConfig(const RegistryPath & key, double & value, double defval) -{ - bool result = false; - - if (!key.empty()) - { - result = GetSettings()->Read(key, &value, defval); - } - - return result; -} - -bool PluginManager::SetConfig(const RegistryPath & key, const wxString & value) -{ - bool result = false; - - if (!key.empty()) - { - wxString wxval = value; - result = GetSettings()->Write(key, wxval); - if (result) - { - result = GetSettings()->Flush(); - } - } - - return result; -} - -bool PluginManager::SetConfig(const RegistryPath & key, const int & value) -{ - bool result = false; - - if (!key.empty()) - { - result = GetSettings()->Write(key, value); - if (result) - { - result = GetSettings()->Flush(); - } - } - - return result; -} - -bool PluginManager::SetConfig(const RegistryPath & key, const bool & value) -{ - bool result = false; - - if (!key.empty()) - { - result = GetSettings()->Write(key, value); - if (result) - { - result = GetSettings()->Flush(); - } - } - - return result; -} - -bool PluginManager::SetConfig(const RegistryPath & key, const float & value) -{ - bool result = false; - - if (!key.empty()) - { - result = GetSettings()->Write(key, value); - if (result) - { - result = GetSettings()->Flush(); - } - } - - return result; -} - -bool PluginManager::SetConfig(const RegistryPath & key, const double & value) -{ - bool result = false; - - if (!key.empty()) - { - result = GetSettings()->Write(key, value); - if (result) - { - result = GetSettings()->Flush(); - } - } - - return result; -} - -/* Return value is a key for lookup in a config file */ -RegistryPath PluginManager::SettingsPath(const PluginID & ID, bool shared) -{ - // All the strings reported by PluginDescriptor and used in this function - // persist in the plugin settings configuration file, so they should not - // be changed across Audacity versions, or else compatibility of the - // configuration files will break. - - 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; - } -} - -/* Return value is a key for lookup in a config file */ -RegistryPath PluginManager::SharedGroup(const PluginID & ID, const RegistryPath & group) -{ - wxString path = SettingsPath(ID, true); - - wxFileName ff(group); - if (!ff.GetName().empty()) - { - path += ff.GetFullPath(wxPATH_UNIX) + wxCONFIG_PATH_SEPARATOR; - } - - return path; -} - -/* Return value is a key for lookup in a config file */ -RegistryPath PluginManager::SharedKey(const PluginID & ID, const RegistryPath & group, const RegistryPath & key) -{ - auto path = SharedGroup(ID, group); - if (path.empty()) - { - return path; - } - - return path + key; -} - -/* Return value is a key for lookup in a config file */ -RegistryPath PluginManager::PrivateGroup(const PluginID & ID, const RegistryPath & group) -{ - auto path = SettingsPath(ID, false); - - wxFileName ff(group); - if (!ff.GetName().empty()) - { - path += ff.GetFullPath(wxPATH_UNIX) + wxCONFIG_PATH_SEPARATOR; - } - - return path; -} - -/* Return value is a key for lookup in a config file */ -RegistryPath PluginManager::PrivateKey(const PluginID & ID, const RegistryPath & group, const RegistryPath & key) -{ - auto path = PrivateGroup(ID, group); - if (path.empty()) - { - return path; - } - - return path + key; -} - -// Sanitize the ID...not the best solution, but will suffice until this -// is converted to XML. We use base64 encoding to preserve case. -wxString PluginManager::ConvertID(const PluginID & ID) -{ - if (ID.StartsWith(wxT("base64:"))) - { - wxString id = ID.Mid(7); - ArrayOf buf{ id.length() / 4 * 3 }; - id = wxString::FromUTF8(buf.get(), b64decode(id, buf.get())); - return id; - } - - const wxCharBuffer & buf = ID.ToUTF8(); - return wxT("base64:") + b64encode(buf, strlen(buf)); -} - -//////////////////////////////////////////////////////////////////////////////// -// Base64 en/decoding -// -// Original routines marked as public domain and found at: -// -// http://en.wikibooks.org/wiki/Algorithm_implementation/Miscellaneous/Base64 -// -//////////////////////////////////////////////////////////////////////////////// - -// Lookup table for encoding -const static wxChar cset[] = wxT("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"); -const static char padc = wxT('='); - -wxString PluginManager::b64encode(const void *in, int len) -{ - unsigned char *p = (unsigned char *) in; - wxString out; - - unsigned long temp; - for (int i = 0; i < len / 3; i++) - { - temp = (*p++) << 16; //Convert to big endian - temp += (*p++) << 8; - temp += (*p++); - out += cset[(temp & 0x00FC0000) >> 18]; - out += cset[(temp & 0x0003F000) >> 12]; - out += cset[(temp & 0x00000FC0) >> 6]; - out += cset[(temp & 0x0000003F)]; - } - - switch (len % 3) - { - case 1: - temp = (*p++) << 16; //Convert to big endian - out += cset[(temp & 0x00FC0000) >> 18]; - out += cset[(temp & 0x0003F000) >> 12]; - out += padc; - out += padc; - break; - - case 2: - temp = (*p++) << 16; //Convert to big endian - temp += (*p++) << 8; - out += cset[(temp & 0x00FC0000) >> 18]; - out += cset[(temp & 0x0003F000) >> 12]; - out += cset[(temp & 0x00000FC0) >> 6]; - out += padc; - break; - } - - return out; -} - -int PluginManager::b64decode(const wxString &in, void *out) -{ - int len = in.length(); - unsigned char *p = (unsigned char *) out; - - if (len % 4) //Sanity check - { - return 0; - } - - int padding = 0; - if (len) - { - if (in[len - 1] == padc) - { - padding++; - } - - if (in[len - 2] == padc) - { - padding++; - } - } - - //const char *a = in.mb_str(); - //Setup a vector to hold the result - unsigned long temp = 0; //Holds decoded quanta - int i = 0; - while (i < len) - { - for (int quantumPosition = 0; quantumPosition < 4; quantumPosition++) - { - unsigned char c = in[i]; - temp <<= 6; - - if (c >= 0x41 && c <= 0x5A) - { - temp |= c - 0x41; - } - else if (c >= 0x61 && c <= 0x7A) - { - temp |= c - 0x47; - } - else if (c >= 0x30 && c <= 0x39) - { - temp |= c + 0x04; - } - else if (c == 0x2B) - { - temp |= 0x3E; - } - else if (c == 0x2F) - { - temp |= 0x3F; - } - else if (c == padc) - { - switch (len - i) - { - case 1: //One pad character - *p++ = (temp >> 16) & 0x000000FF; - *p++ = (temp >> 8) & 0x000000FF; - return p - (unsigned char *) out; - case 2: //Two pad characters - *p++ = (temp >> 10) & 0x000000FF; - return p - (unsigned char *) out; - } - } - i++; - } - *p++ = (temp >> 16) & 0x000000FF; - *p++ = (temp >> 8) & 0x000000FF; - *p++ = temp & 0x000000FF; - } - - return p - (unsigned char *) out; -} - -// This is defined out-of-line here, to keep ComponentInterface free of other -// #include directives. -TranslatableString ComponentInterface::GetName() -{ - return GetSymbol().Msgid(); -}