/********************************************************************** Audacity: A Digital Audio Editor PluginManager.cpp Leland Lucius *******************************************************************//*! \file PluginManager.cpp \brief *//*******************************************************************/ #include <algorithm> #include "Audacity.h" #include <wx/defs.h> #include <wx/dialog.h> #include <wx/dir.h> #include <wx/dynarray.h> #include <wx/dynlib.h> #include <wx/hashmap.h> #include <wx/filename.h> #include <wx/icon.h> #include <wx/imaglist.h> #include <wx/list.h> #include <wx/listctrl.h> #include <wx/log.h> #include <wx/string.h> #include <wx/tokenzr.h> #include <wx/wfstream.h> #include "audacity/EffectInterface.h" #include "AudacityApp.h" #include "effects/EffectManager.h" #include "FileNames.h" #include "ModuleManager.h" #include "PlatformCompatibility.h" #include "Prefs.h" #include "ShuttleGui.h" #include "xml/XMLFileReader.h" #include "xml/XMLWriter.h" #include "PluginManager.h" #include <wx/arrimpl.cpp> WX_DECLARE_STRING_HASH_MAP(wxArrayString, ProviderMap); // ============================================================================ // // // // ============================================================================ #if wxUSE_ACCESSIBILITY class CheckListAx: public wxWindowAccessible { public: CheckListAx(wxListCtrl * window); virtual ~ CheckListAx(); // Retrieves the address of an IDispatch interface for the specified child. // All objects must support this property. virtual wxAccStatus GetChild( int childId, wxAccessible **child ); // Gets the number of children. virtual wxAccStatus GetChildCount( int *childCount ); // 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." virtual wxAccStatus GetDefaultAction( int childId, wxString *actionName ); // Returns the description for this object or a child. virtual wxAccStatus GetDescription( int childId, wxString *description ); // 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'. virtual wxAccStatus GetFocus( int *childId, wxAccessible **child ); // Returns help text for this object or a child, similar to tooltip text. virtual wxAccStatus GetHelpText( int childId, wxString *helpText ); // Returns the keyboard shortcut for this object or child. // Return e.g. ALT+K virtual wxAccStatus GetKeyboardShortcut( int childId, wxString *shortcut ); // Returns the rectangle for this object (id = 0) or a child element (id > 0). // rect is in screen coordinates. virtual wxAccStatus GetLocation( wxRect& rect, int elementId ); // Gets the name of the specified object. virtual wxAccStatus GetName( int childId, wxString *name ); // Returns a role constant. virtual wxAccStatus GetRole( int childId, wxAccRole *role ); // 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 virtual wxAccStatus GetSelections( wxVariant *selections ); // Returns a state constant. virtual wxAccStatus GetState( int childId, long* state ); // Returns a localized string representing the value for the object // or child. virtual wxAccStatus GetValue( int childId, wxString *strValue ); void SetSelected( int item ); private: wxListCtrl *mParent; int mLastId; }; CheckListAx::CheckListAx( wxListCtrl * window ): wxWindowAccessible( window ) { mParent = window; mLastId = -1; } CheckListAx::~CheckListAx() { } void CheckListAx::SetSelected( int item ) { if (mLastId != -1) { NotifyEvent( wxACC_EVENT_OBJECT_SELECTIONREMOVE, mParent, wxOBJID_CLIENT, mLastId ); mLastId = -1; } if (item != -1) { 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 *state ) { 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_IMAGE | 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; } if( item.GetImage() != 0 ) { flag |= wxACC_STATE_SYSTEM_CHECKED; } } } *state = 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 // ============================================================================ // // // // ============================================================================ #include "../images/Unchecked.xpm" #include "../images/Checked.xpm" #include "../images/Arrow15x15.xpm" #define EffectListID 7001 #define EffectClearAllID 7002 #define EffectSelectAllID 7003 int wxCALLBACK SortCompare(long item1, long item2, long WXUNUSED(sortData)) { wxString *str1 = (wxString *) item1; wxString *str2 = (wxString *) item2; #if defined(__WXMAC__) return str2->Cmp(*str1); #else return str1->Cmp(*str2); #endif } class PluginRegistrationDialog : public wxDialog { public: // constructors and destructors PluginRegistrationDialog(ProviderMap & map); virtual ~PluginRegistrationDialog(); private: void Populate(); void PopulateOrExchange(ShuttleGui & S); void OnOK(wxCommandEvent & evt); void OnCancel(wxCommandEvent & evt); void OnListChar(wxKeyEvent & evt); void OnListMouseDown(wxMouseEvent & evt); void OnSelectAll(wxCommandEvent & evt); void OnClearAll(wxCommandEvent & evt); void SetBoldOrRegular(int i); void SetState(int i, int state); void ToggleItem(int i); private: ModuleInterface *mMod; #if wxUSE_ACCESSIBILITY CheckListAx *mAx; #endif wxListCtrl *mEffects; PluginIDList mProvs; wxArrayString mPaths; wxArrayInt miState; bool mCancelClicked; ProviderMap & mMap; DECLARE_EVENT_TABLE() }; BEGIN_EVENT_TABLE(PluginRegistrationDialog, wxDialog) EVT_BUTTON(wxID_OK, PluginRegistrationDialog::OnOK) EVT_BUTTON(wxID_CANCEL, PluginRegistrationDialog::OnCancel) EVT_BUTTON(EffectClearAllID, PluginRegistrationDialog::OnClearAll) EVT_BUTTON(EffectSelectAllID, PluginRegistrationDialog::OnSelectAll) END_EVENT_TABLE() PluginRegistrationDialog::PluginRegistrationDialog(ProviderMap & map) : wxDialog(wxGetApp().GetTopWindow(), wxID_ANY, _("Register Effects"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER), mMap(map) { mEffects = NULL; SetLabel(_("Register Effects")); // Provide visual label SetName(_("Register Effects")); // Provide audible label Populate(); SetReturnCode(wxID_OK); } PluginRegistrationDialog::~PluginRegistrationDialog() { mEffects->Disconnect(wxEVT_LEFT_DOWN, wxMouseEventHandler(PluginRegistrationDialog::OnListMouseDown), NULL, this); mEffects->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(PluginRegistrationDialog::OnListChar), NULL, this); for (int i = 0, cnt = mEffects->GetItemCount(); i < cnt; i++) { wxString *str = (wxString *) mEffects->GetItemData(i); if (str) { delete str; } } } 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) { wxImageList * pImageList = new wxImageList( 15, 15 ); #define SHOW_UNCHECKED (0) #define SHOW_CHECKED (1) #define SHOW_ARROW (2) #define COL_NAME (0) #define COL_PATH (1) pImageList->Add(wxBitmap(unchecked_xpm)); pImageList->Add(wxBitmap(checked_xpm)); pImageList->Add(wxBitmap(arrow15x15_xpm)); S.StartVerticalLay(true); { /*i18n-hint: The dialog shows a list of plugins with check-boxes beside each one.*/ S.StartStatic(_("&Select Plug-ins to Install or press ENTER to Install All"), true); { S.SetStyle(wxSUNKEN_BORDER | wxLC_REPORT | wxLC_SINGLE_SEL | wxLC_HRULES | wxLC_VRULES ); mEffects = S.Id(EffectListID).AddListControlReportMode(); mEffects->Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(PluginRegistrationDialog::OnListMouseDown), NULL, this); mEffects->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(PluginRegistrationDialog::OnListChar), NULL, this); #if wxUSE_ACCESSIBILITY mAx = new CheckListAx(mEffects); mEffects->SetAccessible(mAx); #endif mEffects->AssignImageList( pImageList, wxIMAGE_LIST_SMALL ); mEffects->InsertColumn(COL_NAME, _("Plug-in Name")); mEffects->InsertColumn(COL_PATH, _("Path")); } S.EndStatic(); S.StartHorizontalLay(wxALIGN_LEFT | wxEXPAND, false); { S.SetBorder(10); S.StartHorizontalLay(wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL); { S.AddSpace(12); S.SetBorder(6); S.Id(EffectSelectAllID).AddButton(_("Select &All")); S.Id(EffectClearAllID).AddButton(_("Clea&r All")); } S.EndHorizontalLay(); S.StartHorizontalLay(wxALIGN_CENTER | wxEXPAND); { S.AddSpace(1); } S.EndHorizontalLay(); S.AddStandardButtons(eOkButton | eCancelButton); } S.EndHorizontalLay(); } S.EndVerticalLay(); // The dc is used to compute the text width in pixels. // FIXME: That works fine for PC, but apparently comes out too small for wxMAC. // iLen is minimum width in pixels shown for the file names. 200 is reasonable. int iNameLen = 0; int iPathLen = 0; int x, y; wxRect iconrect; int i = 0; for (ProviderMap::iterator iter = mMap.begin(); iter != mMap.end(); iter++, i++) { miState.Add( SHOW_CHECKED ); wxFileName fname = iter->first; wxString name = fname.GetName(); wxString path = iter->first; mEffects->InsertItem(i, name, SHOW_CHECKED); mEffects->SetItemPtrData(i, (wxUIntPtr) new wxString(name)); mEffects->SetItem(i, COL_PATH, path); // Only need to get the icon width once if (i == 0) { #if defined(__WXMAC__) // wxMac doesn't return the ICON rectangle. It returns the // rectangle for the first column and that even comes back // with negative numbers sometimes. // // So, just guess. wxIcon i1(unchecked_xpm); wxIcon i2(checked_xpm); wxIcon i3(arrow15x15_xpm); iconrect.x = 4; iconrect.width = wxMax(wxMax(i1.GetWidth(), i2.GetWidth()), i3.GetWidth()); #else mEffects->GetItemRect( i, iconrect, wxLIST_RECT_ICON ); #endif } mEffects->GetTextExtent(name, &x, &y); iNameLen = wxMax(iNameLen, x + iconrect.width + (iconrect.x * 2)); mEffects->GetTextExtent(path, &x, &y ); iPathLen = wxMax(iPathLen, x + iconrect.width + (iconrect.x * 2)); } mEffects->SortItems(SortCompare, 0); mEffects->SetColumnWidth(COL_NAME, iNameLen + /* fudge */ 5); mEffects->SetColumnWidth(COL_PATH, iPathLen + /* fudge */ 5); mEffects->SetSizeHints(iNameLen + iPathLen + /* fudge */ 15, 200); if (mPaths.size() > 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 } Layout(); Fit(); SetSizeHints(GetSize()); // Parent window is usually not there yet, so centre on screen rather than on parent. CenterOnScreen(); } void PluginRegistrationDialog::OnListMouseDown(wxMouseEvent & evt) { wxPoint p = evt.GetPosition(); int flags = wxLIST_HITTEST_ONITEM; int item = mEffects->HitTest(p, flags); if (item != wxNOT_FOUND) { ToggleItem(item); } evt.Skip(); } void PluginRegistrationDialog::OnListChar(wxKeyEvent & evt) { switch (evt.GetKeyCode()) { case WXK_SPACE: { int iItem = mEffects->GetNextItem( -1, wxLIST_NEXT_ALL, wxLIST_STATE_FOCUSED); if (iItem != wxNOT_FOUND) { ToggleItem(iItem); } } 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::SetBoldOrRegular(int i) { wxFont Font = mEffects->GetItemFont(i); Font.SetWeight( (miState[i] == SHOW_CHECKED) ? wxFONTWEIGHT_BOLD : wxFONTWEIGHT_NORMAL); mEffects->SetItemFont(i, Font); } // We can't capture mouse clicks, only selected and deselected. // Clicking on a selected item does not generate any event. // Therefore our workaround solution is to NEVER actually select. // So whenever the code tries to , we cancel the selection. // That way we continue to get events. void PluginRegistrationDialog::SetState(int i, int state) { miState[i] = state; mEffects->SetItemImage(i, miState[i]); #if wxUSE_ACCESSIBILITY mAx->SetSelected(i); #endif } void PluginRegistrationDialog::ToggleItem(int i) { SetState(i, miState[i] == SHOW_CHECKED ? SHOW_UNCHECKED : SHOW_CHECKED); } void PluginRegistrationDialog::OnSelectAll(wxCommandEvent & WXUNUSED(evt)) { for (size_t i = 0, cnt = miState.size(); i < cnt; i++) { SetState(i, SHOW_CHECKED); } } void PluginRegistrationDialog::OnClearAll(wxCommandEvent & WXUNUSED(evt)) { for (size_t i = 0, cnt = miState.size(); i < cnt; i++) { SetState(i, SHOW_UNCHECKED); } } void PluginRegistrationDialog::OnOK(wxCommandEvent & WXUNUSED(evt)) { mCancelClicked = false; FindWindowById(EffectListID)->Disable(); FindWindowById(wxID_OK)->Disable(); FindWindowById(EffectListID)->Disable(); FindWindowById(EffectClearAllID)->Disable(); FindWindowById(EffectSelectAllID)->Disable(); PluginManager & pm = PluginManager::Get(); ModuleManager & mm = ModuleManager::Get(); wxListItem li; li.Clear(); for (int i = 0, cnt = mEffects->GetItemCount(); i < cnt && !mCancelClicked; i++) { mEffects->EnsureVisible(i); li.SetId(i); li.SetColumn(COL_PATH); li.SetMask(wxLIST_MASK_TEXT); mEffects->GetItem(li); wxString path = li.GetText(); // Create a placeholder descriptor to show we've seen this plugin before and not // to show it as new the next time Audacity starts. // // Placeholder descriptors have a plugin type of PluginTypeNone and the ID is the // path. PluginDescriptor & plug = pm.mPlugins[path]; plug.SetID(path); plug.SetPath(path); plug.SetEnabled(false); plug.SetValid(false); if (miState[i] == SHOW_CHECKED) { mEffects->SetItemImage(i, SHOW_ARROW); wxArrayString providers = mMap[path]; for (size_t j = 0, cnt = providers.GetCount(); j < cnt; j++) { if (mm.RegisterPlugin(providers[j], path)) { break; } } mEffects->SetItemImage(i, SHOW_CHECKED); } wxYield(); } EndModal(mCancelClicked ? wxID_CANCEL : wxID_OK); } void PluginRegistrationDialog::OnCancel(wxCommandEvent & WXUNUSED(evt)) { mCancelClicked = true; EndModal(mCancelClicked ? wxID_CANCEL : wxID_OK); } /////////////////////////////////////////////////////////////////////////////// // // Plugindescriptor // /////////////////////////////////////////////////////////////////////////////// PluginDescriptor::PluginDescriptor() { mPluginType = PluginTypeNone; mEnabled = false; mInstance = NULL; mEffectType = EffectTypeNone; mEffectInteractive = false; mEffectDefault = false; mEffectLegacy = false; mEffectRealtime = false; mEffectAutomatable = false; } PluginDescriptor::~PluginDescriptor() { if (mInstance) { ModuleManager::Get().DeleteInstance(GetProviderID(), mInstance); } return; } bool PluginDescriptor::IsInstantiated() { return mInstance != NULL; } IdentInterface *PluginDescriptor::GetInstance() { if (!mInstance) { if (GetPluginType() == PluginTypeModule) { mInstance = ModuleManager::Get().CreateProviderInstance(GetID(), GetPath()); } else { mInstance = ModuleManager::Get().CreateInstance(GetProviderID(), GetPath()); } } return mInstance; } void PluginDescriptor::SetInstance(IdentInterface *instance) { mInstance = instance; return; } PluginType PluginDescriptor::GetPluginType() const { return mPluginType; } const PluginID & PluginDescriptor::GetID() const { return mID; } const PluginID & PluginDescriptor::GetProviderID() const { return mProviderID; } const wxString & PluginDescriptor::GetPath() const { return mPath; } const wxString & PluginDescriptor::GetSymbol() const { if (mSymbol.IsEmpty()) { return mName; } return mSymbol; } wxString PluginDescriptor::GetName() const { return wxGetTranslation(mName); } wxString PluginDescriptor::GetVersion() const { return wxGetTranslation(mVersion); } wxString PluginDescriptor::GetVendor() const { return wxGetTranslation(mVendor); } wxString PluginDescriptor::GetDescription() const { return wxGetTranslation(mDescription); } 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 wxString & path) { mPath = path; } void PluginDescriptor::SetSymbol(const wxString & symbol) { mSymbol = symbol; } void PluginDescriptor::SetName(const wxString & name) { mName = name; } void PluginDescriptor::SetVersion(const wxString & version) { mVersion = version; } void PluginDescriptor::SetVendor(const wxString & vendor) { mVendor = vendor; } void PluginDescriptor::SetDescription(const wxString & description) { mDescription = description; } void PluginDescriptor::SetEnabled(bool enable) { mEnabled = enable; } void PluginDescriptor::SetValid(bool valid) { mValid = valid; } // Effects const 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 wxString & PluginDescriptor::GetImporterFilterDescription() const { return mImporterFilterDesc; } void PluginDescriptor::SetImporterFilterDescription(const wxString & filterDesc) { mImporterFilterDesc = filterDesc; } const wxArrayString & PluginDescriptor::GetImporterExtensions() const { return mImporterExtensions; } void PluginDescriptor::SetImporterExtensions(const wxArrayString & extensions) { mImporterExtensions = extensions; } /////////////////////////////////////////////////////////////////////////////// // // PluginManager // /////////////////////////////////////////////////////////////////////////////// #define REGVERKEY wxString(wxT("/pluginregistryversion")) #define REGVERCUR wxString(wxT("1.0")) #define REGROOT wxString(wxT("/pluginregistry/")) #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_IMPORTERIDENT wxT("ImporterIdent") #define KEY_IMPORTERFILTER wxT("ImporterFilter") #define KEY_IMPORTEREXTENSIONS wxT("ImporterExtensions") // ============================================================================ // // PluginManagerInterface implementation // // ============================================================================ const PluginID & PluginManager::RegisterModulePlugin(ModuleInterface *module) { PluginDescriptor & plug = CreatePlugin(GetID(module), module, PluginTypeModule); plug.SetEnabled(true); plug.SetValid(true); return plug.GetID(); } const PluginID & PluginManager::RegisterEffectPlugin(ModuleInterface *provider, EffectIdentInterface *effect) { PluginDescriptor & plug = CreatePlugin(GetID(effect), effect, PluginTypeEffect); plug.SetProviderID(PluginManager::GetID(provider)); plug.SetEffectType(effect->GetType()); plug.SetEffectFamily(effect->GetFamily()); 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::RegisterImporterPlugin(ModuleInterface *provider, ImporterInterface *importer) { PluginDescriptor & plug = CreatePlugin(GetID(importer), importer, PluginTypeImporter); plug.SetProviderID(PluginManager::GetID(provider)); plug.SetImporterIdentifier(importer->GetPluginStringID()); plug.SetImporterFilterDescription(importer->GetPluginFormatDescription()); plug.SetImporterExtensions(importer->GetSupportedExtensions()); return plug.GetID(); } void PluginManager::FindFilesInPathList(const wxString & pattern, const wxArrayString & pathList, wxArrayString & files, bool directories) { wxLogNull nolog; // Why bother... if (pattern.IsEmpty()) { return; } // TODO: We REALLY need to figure out the "Audacity" plug-in path(s) wxFileName f; wxArrayString paths; // Add the "per-user" plug-ins directory f = FileNames::PlugInDir(); paths.Add(f.GetFullPath()); // Add the "Audacity" plug-ins directory f = PlatformCompatibility::GetExecutablePath(); #if defined(__WXMAC__) f.RemoveLastDir(); f.RemoveLastDir(); f.RemoveLastDir(); #endif f.AppendDir(wxT("plug-ins")); paths.Add(f.GetPath()); // Weed out duplicates for (size_t i = 0, cnt = pathList.size(); i < cnt; i++) { f = pathList[i]; wxString path = f.GetFullPath(); if (paths.Index(path, wxFileName::IsCaseSensitive()) == wxNOT_FOUND) { paths.Add(path); } } // Find all matching files in each path for (size_t i = 0, cnt = paths.GetCount(); i < cnt; i++) { f = paths[i] + wxFILE_SEP_PATH + pattern; wxDir::GetAllFiles(f.GetPath(), &files, f.GetFullName(), directories ? wxDIR_DEFAULT : wxDIR_FILES); } return; } bool PluginManager::GetSharedConfigSubgroups(const PluginID & ID, const wxString & group, wxArrayString & subgroups) { return GetSubgroups(SharedGroup(ID, group), subgroups); } bool PluginManager::GetSharedConfig(const PluginID & ID, const wxString & group, const wxString & key, wxString & value, const wxString & defval) { return GetConfig(SharedKey(ID, group, key), value, defval); } bool PluginManager::GetSharedConfig(const PluginID & ID, const wxString & group, const wxString & key, int & value, int defval) { return GetConfig(SharedKey(ID, group, key), value, defval); } bool PluginManager::GetSharedConfig(const PluginID & ID, const wxString & group, const wxString & key, bool & value, bool defval) { return GetConfig(SharedKey(ID, group, key), value, defval); } bool PluginManager::GetSharedConfig(const PluginID & ID, const wxString & group, const wxString & key, float & value, float defval) { return GetConfig(SharedKey(ID, group, key), value, defval); } bool PluginManager::GetSharedConfig(const PluginID & ID, const wxString & group, const wxString & key, double & value, double defval) { return GetConfig(SharedKey(ID, group, key), value, defval); } bool PluginManager::GetSharedConfig(const PluginID & ID, const wxString & group, const wxString & key, sampleCount & value, sampleCount defval) { return GetConfig(SharedKey(ID, group, key), value, defval); } bool PluginManager::SetSharedConfig(const PluginID & ID, const wxString & group, const wxString & key, const wxString & value) { return SetConfig(SharedKey(ID, group, key), value); } bool PluginManager::SetSharedConfig(const PluginID & ID, const wxString & group, const wxString & key, const int & value) { return SetConfig(SharedKey(ID, group, key), value); } bool PluginManager::SetSharedConfig(const PluginID & ID, const wxString & group, const wxString & key, const bool & value) { return SetConfig(SharedKey(ID, group, key), value); } bool PluginManager::SetSharedConfig(const PluginID & ID, const wxString & group, const wxString & key, const float & value) { return SetConfig(SharedKey(ID, group, key), value); } bool PluginManager::SetSharedConfig(const PluginID & ID, const wxString & group, const wxString & key, const double & value) { return SetConfig(SharedKey(ID, group, key), value); } bool PluginManager::SetSharedConfig(const PluginID & ID, const wxString & group, const wxString & key, const sampleCount & value) { return SetConfig(SharedKey(ID, group, key), value); } bool PluginManager::RemoveSharedConfigSubgroup(const PluginID & ID, const wxString & group) { bool result = GetSettings()->DeleteGroup(SharedGroup(ID, group)); if (result) { GetSettings()->Flush(); } return result; } bool PluginManager::RemoveSharedConfig(const PluginID & ID, const wxString & group, const wxString & key) { bool result = GetSettings()->DeleteEntry(SharedKey(ID, group, key)); if (result) { GetSettings()->Flush(); } return result; } bool PluginManager::GetPrivateConfigSubgroups(const PluginID & ID, const wxString & group, wxArrayString & subgroups) { return GetSubgroups(PrivateGroup(ID, group), subgroups); } bool PluginManager::GetPrivateConfig(const PluginID & ID, const wxString & group, const wxString & key, wxString & value, const wxString & defval) { return GetConfig(PrivateKey(ID, group, key), value, defval); } bool PluginManager::GetPrivateConfig(const PluginID & ID, const wxString & group, const wxString & key, int & value, int defval) { return GetConfig(PrivateKey(ID, group, key), value, defval); } bool PluginManager::GetPrivateConfig(const PluginID & ID, const wxString & group, const wxString & key, bool & value, bool defval) { return GetConfig(PrivateKey(ID, group, key), value, defval); } bool PluginManager::GetPrivateConfig(const PluginID & ID, const wxString & group, const wxString & key, float & value, float defval) { return GetConfig(PrivateKey(ID, group, key), value, defval); } bool PluginManager::GetPrivateConfig(const PluginID & ID, const wxString & group, const wxString & key, double & value, double defval) { return GetConfig(PrivateKey(ID, group, key), value, defval); } bool PluginManager::GetPrivateConfig(const PluginID & ID, const wxString & group, const wxString & key, sampleCount & value, sampleCount defval) { return GetConfig(PrivateKey(ID, group, key), value, defval); } bool PluginManager::SetPrivateConfig(const PluginID & ID, const wxString & group, const wxString & key, const wxString & value) { return SetConfig(PrivateKey(ID, group, key), value); } bool PluginManager::SetPrivateConfig(const PluginID & ID, const wxString & group, const wxString & key, const int & value) { return SetConfig(PrivateKey(ID, group, key), value); } bool PluginManager::SetPrivateConfig(const PluginID & ID, const wxString & group, const wxString & key, const bool & value) { return SetConfig(PrivateKey(ID, group, key), value); } bool PluginManager::SetPrivateConfig(const PluginID & ID, const wxString & group, const wxString & key, const float & value) { return SetConfig(PrivateKey(ID, group, key), value); } bool PluginManager::SetPrivateConfig(const PluginID & ID, const wxString & group, const wxString & key, const double & value) { return SetConfig(PrivateKey(ID, group, key), value); } bool PluginManager::SetPrivateConfig(const PluginID & ID, const wxString & group, const wxString & key, const sampleCount & value) { return SetConfig(PrivateKey(ID, group, key), value); } bool PluginManager::RemovePrivateConfigSubgroup(const PluginID & ID, const wxString & group) { bool result = GetSettings()->DeleteGroup(PrivateGroup(ID, group)); if (result) { GetSettings()->Flush(); } return result; } bool PluginManager::RemovePrivateConfig(const PluginID & ID, const wxString & group, const wxString & key) { bool result = GetSettings()->DeleteEntry(PrivateKey(ID, group, key)); if (result) { GetSettings()->Flush(); } return result; } // ============================================================================ // // PluginManager // // ============================================================================ // The one and only PluginManager PluginManager *PluginManager::mInstance = NULL; // ---------------------------------------------------------------------------- // Creation/Destruction // ---------------------------------------------------------------------------- PluginManager::PluginManager() { mSettings = NULL; } PluginManager::~PluginManager() { if (mSettings) { delete mSettings; } } // ---------------------------------------------------------------------------- // PluginManager implementation // ---------------------------------------------------------------------------- // ============================================================================ // // Return reference to singleton // // (Thread-safe...no active threading during construction or after destruction) // ============================================================================ PluginManager & PluginManager::Get() { if (!mInstance) { mInstance = new PluginManager(); } return *mInstance; } void PluginManager::Destroy() { if (mInstance) { delete mInstance; } } void PluginManager::Initialize() { // Always load the registry first Load(); // Then look for providers (they may autoregister plugins) ModuleManager::Get().DiscoverProviders(); // And finally check for updates CheckForUpdates(); } 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++); } } void PluginManager::Load() { // Get the full scan setting bool doRescan; gPrefs->Read(wxT("/Plugins/Rescan"), &doRescan, true); // Create/Open the registry mRegistry = new wxFileConfig(wxEmptyString, wxEmptyString, FileNames::PluginRegistry()); // 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 (!mRegistry->HasGroup(REGROOT) || doRescan) { // Must start over mRegistry->DeleteAll(); delete mRegistry; return; } // Check for a registry version that we can understand wxString regver = mRegistry->Read(REGVERKEY); if (regver < REGVERCUR ) { // This is where we'd put in conversion code when the // registry version changes. // // Should also check for a registry file that is newer than // what we can understand. } // Load all provider plugins first LoadGroup(PluginTypeModule); // Now the rest LoadGroup(PluginTypeEffect); LoadGroup(PluginTypeExporter); LoadGroup(PluginTypeImporter); LoadGroup(PluginTypeNone); delete mRegistry; return; } void PluginManager::LoadGroup(PluginType type) { wxString strVal; bool boolVal; wxString groupName; long groupIndex; wxString group = GetPluginTypeString(type); wxString cfgPath = REGROOT + group + wxCONFIG_PATH_SEPARATOR; mRegistry->SetPath(cfgPath); for (bool cont = mRegistry->GetFirstGroup(groupName, groupIndex); cont; mRegistry->SetPath(cfgPath), cont = mRegistry->GetNextGroup(groupName, groupIndex)) { PluginDescriptor plug; mRegistry->SetPath(groupName); groupName = ConvertID(groupName); // Bypass group if the ID is already in use if (mPlugins.find(groupName) != mPlugins.end()) { mRegistry->SetPath(wxT("..")); continue; } // Set the ID and type plug.SetID(groupName); plug.SetPluginType(type); // Get the provider ID and bypass group if not found if (!mRegistry->Read(KEY_PROVIDERID, &strVal, wxEmptyString)) { // Bypass group if the provider isn't valid if (!strVal.IsEmpty() && mPlugins.find(strVal) == mPlugins.end()) { continue; } } plug.SetProviderID(PluginID(strVal)); // Get the path (optional) mRegistry->Read(KEY_PATH, &strVal, wxEmptyString); plug.SetPath(strVal); // Get the name and bypass group if not found if (!mRegistry->Read(KEY_NAME, &strVal)) { continue; } plug.SetName(strVal); // Get the symbol...use name if not found if (!mRegistry->Read(KEY_SYMBOL, &strVal)) { strVal = plug.GetName(); } plug.SetSymbol(strVal); // Get the version and bypass group if not found if (!mRegistry->Read(KEY_VERSION, &strVal)) { continue; } plug.SetVersion(strVal); // Get the vendor and bypass group if not found if (!mRegistry->Read(KEY_VENDOR, &strVal)) { continue; } plug.SetVendor(strVal); // Get the description and bypass group if not found if (!mRegistry->Read(KEY_DESCRIPTION, &strVal)) { continue; } plug.SetDescription(strVal); // Is it enabled...default to no if not found mRegistry->Read(KEY_ENABLED, &boolVal, false); plug.SetEnabled(boolVal); // Is it valid...default to no if not found mRegistry->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 (!mRegistry->Read(KEY_EFFECTTYPE, &strVal)) { continue; } if (strVal.IsSameAs(KEY_EFFECTTYPE_NONE)) { plug.SetEffectType(EffectTypeNone); } else if (strVal.IsSameAs(KEY_EFFECTTYPE_ANALYZE)) { plug.SetEffectType(EffectTypeAnalyze); } else if (strVal.IsSameAs(KEY_EFFECTTYPE_GENERATE)) { plug.SetEffectType(EffectTypeGenerate); } else if (strVal.IsSameAs(KEY_EFFECTTYPE_PROCESS)) { plug.SetEffectType(EffectTypeProcess); } else { continue; } // Get the effect family and bypass group if not found if (!mRegistry->Read(KEY_EFFECTFAMILY, &strVal)) { continue; } plug.SetEffectFamily(strVal); // Is it a default (above the line) effect and bypass group if not found if (!mRegistry->Read(KEY_EFFECTDEFAULT, &boolVal)) { continue; } plug.SetEffectDefault(boolVal); // Is it an interactive effect and bypass group if not found if (!mRegistry->Read(KEY_EFFECTINTERACTIVE, &boolVal)) { continue; } plug.SetEffectInteractive(boolVal); // Is it a realtime capable effect and bypass group if not found if (!mRegistry->Read(KEY_EFFECTREALTIME, &boolVal)) { continue; } plug.SetEffectRealtime(boolVal); // Does the effect support automation...bypass group if not found if (!mRegistry->Read(KEY_EFFECTAUTOMATABLE, &boolVal)) { continue; } plug.SetEffectAutomatable(boolVal); } break; case PluginTypeImporter: { // Get the importer identifier and bypass group if not found if (!mRegistry->Read(KEY_IMPORTERIDENT, &strVal)) { continue; } plug.SetImporterIdentifier(strVal); // Get the importer filter description and bypass group if not found if (!mRegistry->Read(KEY_IMPORTERFILTER, &strVal)) { continue; } plug.SetImporterFilterDescription(strVal); // Get the importer extensions and bypass group if not found if (!mRegistry->Read(KEY_IMPORTEREXTENSIONS, &strVal)) { continue; } wxArrayString extensions; wxStringTokenizer tkr(strVal, wxT(":")); while (tkr.HasMoreTokens()) { extensions.Add(tkr.GetNextToken()); } plug.SetImporterExtensions(extensions); } break; case PluginTypeNone: { // Used for placeholder groups } break; default: { continue; } } // Everything checked out...accept the plugin mPlugins[groupName] = plug; } return; } void PluginManager::Save() { // Create/Open the registry mRegistry = new wxFileConfig(wxEmptyString, wxEmptyString, FileNames::PluginRegistry()); // Write the version string mRegistry->Write(REGVERKEY, REGVERCUR); // Save the individual groups SaveGroup(PluginTypeEffect); SaveGroup(PluginTypeExporter); SaveGroup(PluginTypeImporter); SaveGroup(PluginTypeNone); // And now the providers SaveGroup(PluginTypeModule); // Just to be safe mRegistry->Flush(); delete mRegistry; } void PluginManager::SaveGroup(PluginType type) { wxString group = GetPluginTypeString(type); for (PluginMap::iterator iter = mPlugins.begin(); iter != mPlugins.end(); iter++) { PluginDescriptor & plug = iter->second; if (plug.GetPluginType() != type) { continue; } mRegistry->SetPath(REGROOT + group + wxCONFIG_PATH_SEPARATOR + ConvertID(plug.GetID())); mRegistry->Write(KEY_PATH, plug.GetPath()); mRegistry->Write(KEY_SYMBOL, plug.GetSymbol()); mRegistry->Write(KEY_NAME, plug.GetName()); mRegistry->Write(KEY_VERSION, plug.GetVersion()); mRegistry->Write(KEY_VENDOR, plug.GetVendor()); mRegistry->Write(KEY_DESCRIPTION, plug.GetDescription()); mRegistry->Write(KEY_PROVIDERID, plug.GetProviderID()); mRegistry->Write(KEY_ENABLED, plug.IsEnabled()); mRegistry->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; } mRegistry->Write(KEY_EFFECTTYPE, stype); mRegistry->Write(KEY_EFFECTFAMILY, plug.GetEffectFamily()); mRegistry->Write(KEY_EFFECTDEFAULT, plug.IsEffectDefault()); mRegistry->Write(KEY_EFFECTINTERACTIVE, plug.IsEffectInteractive()); mRegistry->Write(KEY_EFFECTREALTIME, plug.IsEffectRealtime()); mRegistry->Write(KEY_EFFECTAUTOMATABLE, plug.IsEffectAutomatable()); } break; case PluginTypeImporter: { mRegistry->Write(KEY_IMPORTERIDENT, plug.GetImporterIdentifier()); mRegistry->Write(KEY_IMPORTERFILTER, plug.GetImporterFilterDescription()); const wxArrayString & extensions = plug.GetImporterExtensions(); wxString strExt; for (size_t i = 0, cnt = extensions.size(); i < cnt; i++) { strExt += extensions[i] + wxT(":"); } strExt.RemoveLast(1); mRegistry->Write(KEY_IMPORTEREXTENSIONS, strExt); } break; default: break; } } return; } void PluginManager::CheckForUpdates() { // Get ModuleManager reference ModuleManager & mm = ModuleManager::Get(); // Get the full scan and check for update settings bool doRescan; bool doCheck; gPrefs->Read(wxT("/Plugins/Rescan"), &doRescan, true); gPrefs->Read(wxT("/Plugins/CheckForUpdates"), &doCheck, true); ProviderMap map; // Always check for and disable missing plugins // // Since the user's saved presets are in the registery, never delete them. That is // a job for the plugin manager UI (once it is written) // Check for plugins that are no longer valid PluginMap::iterator iter = mPlugins.begin(); while (iter != mPlugins.end()) { PluginDescriptor & plug = iter->second; const PluginID & plugID = plug.GetID(); const wxString & plugPath = plug.GetPath(); if (plug.GetPluginType() == PluginTypeModule) { if (!mm.IsProviderValid(plugID, plugPath)) { plug.SetValid(false); } else { // Only collect plugin paths if we're doing a full scan or checking for updates if (doRescan || doCheck) { wxArrayString paths = mm.FindPluginsForProvider(plugID, plugPath); for (size_t i = 0, cnt = paths.GetCount(); i < cnt; i++) { map[paths[i]].Add(plugID); } } } } else { plug.SetValid(mm.IsPluginValid(plug.GetProviderID(), plugPath)); } iter++; } // If we're only checking for new plugins, then remove all of the known ones if (doCheck && !doRescan) { for (PluginMap::iterator iter = mPlugins.begin(); iter != mPlugins.end(); iter++) { PluginDescriptor & plug = iter->second; const wxString & plugPath = plug.GetPath(); ProviderMap::iterator mapiter = map.find(plugPath); if (mapiter != map.end()) { map.erase(mapiter); } } } // Allow the user to choose which ones to enable if (map.size() != 0) { PluginRegistrationDialog dlg(map); if (dlg.ShowModal() == wxID_OK) { gPrefs->Write(wxT("/Plugins/Rescan"), false); } } Save(); return; } 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; } const PluginDescriptor *PluginManager::GetPlugin(const PluginID & ID) { if (mPlugins.find(ID) == mPlugins.end()) { return NULL; } return &mPlugins[ID]; } const PluginDescriptor *PluginManager::GetFirstPlugin(PluginType type) { for (mPluginsIter = mPlugins.begin(); mPluginsIter != mPlugins.end(); mPluginsIter++) { PluginDescriptor & plug = mPluginsIter->second; bool familyEnabled = true; if (type == PluginTypeEffect) { gPrefs->Read(plug.GetEffectFamily() + wxT("/Enable"), &familyEnabled, true); } if (plug.IsValid() && plug.IsEnabled() && plug.GetPluginType() == type && familyEnabled) { return &mPluginsIter->second; } } return NULL; } const PluginDescriptor *PluginManager::GetNextPlugin(PluginType type) { while (++mPluginsIter != mPlugins.end()) { PluginDescriptor & plug = mPluginsIter->second; bool familyEnabled = true; if (type == PluginTypeEffect) { gPrefs->Read(plug.GetEffectFamily() + wxT("/Enable"), &familyEnabled, true); } if (plug.IsValid() && plug.IsEnabled() && plug.GetPluginType() == type && familyEnabled) { return &mPluginsIter->second; } } return NULL; } const PluginDescriptor *PluginManager::GetFirstPluginForEffectType(EffectType type) { for (mPluginsIter = mPlugins.begin(); mPluginsIter != mPlugins.end(); mPluginsIter++) { PluginDescriptor & plug = mPluginsIter->second; bool familyEnabled; gPrefs->Read(plug.GetEffectFamily() + wxT("/Enable"), &familyEnabled, true); if (plug.IsValid() && plug.IsEnabled() && plug.GetEffectType() == type && familyEnabled) { return &plug; } } return NULL; } const PluginDescriptor *PluginManager::GetNextPluginForEffectType(EffectType type) { while (++mPluginsIter != mPlugins.end()) { PluginDescriptor & plug = mPluginsIter->second; bool familyEnabled; gPrefs->Read(plug.GetEffectFamily() + wxT("/Enable"), &familyEnabled, true); if (plug.IsValid() && plug.IsEnabled() && plug.GetEffectType() == type && familyEnabled) { return &plug; } } return NULL; } bool PluginManager::IsRegistered(const PluginID & ID) { if (mPlugins.find(ID) == mPlugins.end()) { return false; } return true; } const PluginID & PluginManager::RegisterLegacyEffectPlugin(EffectIdentInterface *effect) { PluginDescriptor & plug = CreatePlugin(GetID(effect), effect, PluginTypeEffect); plug.SetEffectType(effect->GetType()); plug.SetEffectFamily(effect->GetFamily()); plug.SetEffectInteractive(effect->IsInteractive()); plug.SetEffectDefault(effect->IsDefault()); plug.SetEffectRealtime(effect->SupportsRealtime()); plug.SetEffectAutomatable(effect->SupportsAutomation()); plug.SetInstance(effect); plug.SetEffectLegacy(true); plug.SetEnabled(true); plug.SetValid(true); return plug.GetID(); } bool PluginManager::IsPluginEnabled(const PluginID & ID) { if (mPlugins.find(ID) == mPlugins.end()) { return false; } return mPlugins[ID].IsEnabled(); } void PluginManager::EnablePlugin(const PluginID & ID, bool enable) { if (mPlugins.find(ID) == mPlugins.end()) { return; } return mPlugins[ID].SetEnabled(enable); } const wxString & PluginManager::GetSymbol(const PluginID & ID) { if (mPlugins.find(ID) == mPlugins.end()) { static wxString empty; return empty; } return mPlugins[ID].GetSymbol(); } wxString PluginManager::GetName(const PluginID & ID) { if (mPlugins.find(ID) == mPlugins.end()) { return wxEmptyString; } return mPlugins[ID].GetName(); } IdentInterface *PluginManager::GetInstance(const PluginID & ID) { if (mPlugins.find(ID) == mPlugins.end()) { return NULL; } 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()) { return NULL; } mPlugins[prov].GetInstance(); } return plug.GetInstance(); } // TODO: This goes away when all effects have been converted void PluginManager::SetInstance(const PluginID & ID, IdentInterface *instance) { if (mPlugins.find(ID) == mPlugins.end()) { return; } return mPlugins[ID].SetInstance(instance); } PluginID PluginManager::GetID(ModuleInterface *module) { return wxString::Format(wxT("%s_%s_%s_%s_%s"), GetPluginTypeString(PluginTypeModule).c_str(), wxEmptyString, module->GetVendor().c_str(), module->GetName().c_str(), module->GetPath().c_str()); } PluginID PluginManager::GetID(EffectIdentInterface *effect) { return wxString::Format(wxT("%s_%s_%s_%s_%s"), GetPluginTypeString(PluginTypeEffect).c_str(), effect->GetFamily().c_str(), effect->GetVendor().c_str(), effect->GetName().c_str(), effect->GetPath().c_str()); } PluginID PluginManager::GetID(ImporterInterface *importer) { return wxString::Format(wxT("%s_%s_%s_%s_%s"), GetPluginTypeString(PluginTypeImporter).c_str(), wxEmptyString, importer->GetVendor().c_str(), importer->GetName().c_str(), importer->GetPath().c_str()); } wxString PluginManager::GetPluginTypeString(PluginType type) { wxString str; switch (type) { case PluginTypeNone: str = wxT("Placeholder"); break; case PluginTypeEffect: str = wxT("Effect"); 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, IdentInterface *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.SetName(ident->GetName()); plug.SetVendor(ident->GetVendor()); plug.SetVersion(ident->GetVersion()); plug.SetDescription(ident->GetDescription()); return plug; } wxFileConfig *PluginManager::GetSettings() { if (!mSettings) { mSettings = new wxFileConfig(wxEmptyString, wxEmptyString, 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; } bool PluginManager::GetSubgroups(const wxString & group, wxArrayString & subgroups) { bool result = false; if (!group.IsEmpty()) { wxString name = wxEmptyString; long index = 0; wxString path = GetSettings()->GetPath(); GetSettings()->SetPath(group); if (GetSettings()->GetFirstGroup(name, index)) { do { subgroups.Add(name); } while (GetSettings()->GetNextGroup(name, index)); } GetSettings()->SetPath(path); } return result; } bool PluginManager::GetConfig(const wxString & key, int & value, int defval) { bool result = false; if (!key.IsEmpty()) { result = GetSettings()->Read(key, &value, defval); } return result; } bool PluginManager::GetConfig(const wxString & key, wxString & value, const wxString & defval) { bool result = false; if (!key.IsEmpty()) { wxString wxval = wxEmptyString; result = GetSettings()->Read(key, &wxval, defval); value = wxval; } return result; } bool PluginManager::GetConfig(const wxString & key, bool & value, bool defval) { bool result = false; if (!key.IsEmpty()) { result = GetSettings()->Read(key, &value, defval); } return result; } bool PluginManager::GetConfig(const wxString & key, float & value, float defval) { bool result = false; if (!key.IsEmpty()) { double dval = 0.0; result = GetSettings()->Read(key, &dval, (double) defval); value = (float) dval; } return result; } bool PluginManager::GetConfig(const wxString & key, double & value, double defval) { bool result = false; if (!key.IsEmpty()) { result = GetSettings()->Read(key, &value, defval); } return result; } bool PluginManager::GetConfig(const wxString & key, sampleCount & value, sampleCount defval) { bool result = false; if (!key.IsEmpty()) { wxString wxval = wxEmptyString; wxString wxdef; wchar_t *endptr; wxdef.Printf(wxT("%Ld"), defval); result = GetSettings()->Read(key, &wxval, wxdef); value = wxStrtoll(wxval.c_str(), &endptr, 10); } return result; } bool PluginManager::SetConfig(const wxString & key, const wxString & value) { bool result = false; if (!key.IsEmpty()) { wxString wxval = value.c_str(); result = GetSettings()->Write(key, wxval); if (result) { result = GetSettings()->Flush(); } } return result; } bool PluginManager::SetConfig(const wxString & key, const int & value) { bool result = false; if (!key.IsEmpty()) { result = GetSettings()->Write(key, value); if (result) { result = GetSettings()->Flush(); } } return result; } bool PluginManager::SetConfig(const wxString & key, const bool & value) { bool result = false; if (!key.IsEmpty()) { result = GetSettings()->Write(key, value); if (result) { result = GetSettings()->Flush(); } } return result; } bool PluginManager::SetConfig(const wxString & key, const float & value) { bool result = false; if (!key.IsEmpty()) { result = GetSettings()->Write(key, value); if (result) { result = GetSettings()->Flush(); } } return result; } bool PluginManager::SetConfig(const wxString & key, const double & value) { bool result = false; if (!key.IsEmpty()) { result = GetSettings()->Write(key, value); if (result) { result = GetSettings()->Flush(); } } return result; } bool PluginManager::SetConfig(const wxString & key, const sampleCount & value) { bool result = false; if (!key.IsEmpty()) { result = GetSettings()->Write(key, wxString::Format(wxT("%d"), (int) value)); if (result) { result = GetSettings()->Flush(); } } return result; } wxString PluginManager::SettingsID(const PluginID & ID) { if (mPlugins.find(ID) == mPlugins.end()) { return wxEmptyString; } const PluginDescriptor & plug = mPlugins[ID]; return wxString::Format(wxT("%s_%s_%s_%s"), GetPluginTypeString(plug.GetPluginType()).c_str(), plug.GetEffectFamily().c_str(), // is empty for non-Effects plug.GetVendor().c_str(), plug.GetName().c_str()); } wxString PluginManager::SharedGroup(const PluginID & ID, const wxString & group) { wxString settingsID = SettingsID(ID); wxString path = SETROOT + ConvertID(settingsID) + wxCONFIG_PATH_SEPARATOR + wxT("shared") + wxCONFIG_PATH_SEPARATOR; wxFileName f(group); if (!f.GetName().IsEmpty()) { path += f.GetFullPath(wxPATH_UNIX) + wxCONFIG_PATH_SEPARATOR; } return path; } wxString PluginManager::SharedKey(const PluginID & ID, const wxString & group, const wxString & key) { wxString path = SharedGroup(ID, group); if (path.IsEmpty()) { return path; } return path + key; } wxString PluginManager::PrivateGroup(const PluginID & ID, const wxString & group) { wxString settingsID = SettingsID(ID); wxString path = SETROOT + ConvertID(settingsID) + wxCONFIG_PATH_SEPARATOR + wxT("private") + wxCONFIG_PATH_SEPARATOR; wxFileName f(group); if (!f.GetName().IsEmpty()) { path += f.GetFullPath(wxPATH_UNIX) + wxCONFIG_PATH_SEPARATOR; } return path; } wxString PluginManager::PrivateKey(const PluginID & ID, const wxString & group, const wxString & key) { wxString path = PrivateGroup(ID, group); if (path.IsEmpty()) { 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); char *buf = new char[id.Length() / 4 * 3]; id = wxString::FromUTF8(buf, b64decode(id, buf)); delete [] buf; 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(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; }