1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-06-15 15:49:36 +02:00
audacity/src/PluginManager.cpp
2014-12-08 18:12:56 +00:00

2460 lines
63 KiB
C++

/**********************************************************************
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(), GetID(), GetPath());
}
}
return mInstance;
}
void PluginDescriptor::SetInstance(IdentInterface *instance)
{
mInstance = instance;
return;
}
PluginType PluginDescriptor::GetPluginType() const
{
return mPluginType;
}
const wxString & PluginDescriptor::GetPath() const
{
return mPath;
}
const wxString & PluginDescriptor::GetName() const
{
return mName;
}
const wxString & PluginDescriptor::GetVersion() const
{
return mVersion;
}
const wxString & PluginDescriptor::GetVendor() const
{
return mVendor;
}
const wxString & PluginDescriptor::GetDescription() const
{
return mDescription;
}
const PluginID & PluginDescriptor::GetID() const
{
return mID;
}
const PluginID & PluginDescriptor::GetProviderID() const
{
return mProviderID;
}
bool PluginDescriptor::IsEnabled() const
{
return mEnabled;
}
bool PluginDescriptor::IsValid() const
{
return mValid;
}
wxString PluginDescriptor::GetMenuName() const
{
// This probably shouldn't be here...but it was easy
wxString name;
mName.EndsWith(wxT("..."), &name);
return (mVendor.IsEmpty() ? wxT("") : mVendor + wxT(": ")) +
(name.IsEmpty() ? mName : name) +
(mEffectInteractive ? wxT("...") : wxT(""));
}
void PluginDescriptor::SetPluginType(PluginType type)
{
mPluginType = type;
}
void PluginDescriptor::SetPath(const wxString & path)
{
mPath = path;
}
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::SetID(const PluginID & ID)
{
mID = ID;
}
void PluginDescriptor::SetProviderID(const PluginID & providerID)
{
mProviderID = providerID;
}
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 CACHEROOT wxString(wxT("/plugincache/"))
#define KEY_ID wxT("ID")
#define KEY_NAME wxT("Name")
#define KEY_PATH wxT("Path")
#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
//
// ============================================================================
void PluginManager::RegisterModulePlugin(IdentInterface *module)
{
PluginDescriptor & plug = CreatePlugin(module, PluginTypeModule);
plug.SetEnabled(true);
plug.SetValid(true);
}
void PluginManager::RegisterEffectPlugin(IdentInterface *provider, EffectIdentInterface *effect)
{
PluginDescriptor & plug = CreatePlugin(effect, PluginTypeEffect);
plug.SetProviderID(provider->GetID());
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);
}
void PluginManager::RegisterImporterPlugin(IdentInterface *provider, ImporterInterface *importer)
{
PluginDescriptor & plug = CreatePlugin(importer, PluginTypeImporter);
plug.SetProviderID(provider->GetID());
plug.SetImporterIdentifier(importer->GetPluginStringID());
plug.SetImporterFilterDescription(importer->GetPluginFormatDescription());
plug.SetImporterExtensions(importer->GetSupportedExtensions());
}
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)
{
return mConfig->DeleteGroup(SharedGroup(ID, group));
}
bool PluginManager::RemoveSharedConfig(const PluginID & ID, const wxString & group, const wxString & key)
{
return mConfig->DeleteEntry(SharedKey(ID, group, key));
}
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)
{
return mConfig->DeleteGroup(PrivateGroup(ID, group));
}
bool PluginManager::RemovePrivateConfig(const PluginID & ID, const wxString & group, const wxString & key)
{
return mConfig->DeleteEntry(PrivateKey(ID, group, key));
}
// ============================================================================
//
// PluginManager
//
// ============================================================================
// The one and only PluginManager
PluginManager PluginManager::mInstance;
// ----------------------------------------------------------------------------
// Creation/Destruction
// ----------------------------------------------------------------------------
PluginManager::PluginManager()
{
mConfig = NULL;
}
PluginManager::~PluginManager()
{
if (mConfig)
{
delete mConfig;
}
}
// ----------------------------------------------------------------------------
// PluginManager implementation
// ----------------------------------------------------------------------------
// ============================================================================
//
// Return reference to singleton
//
// (Thread-safe...no active threading during construction or after destruction)
// ============================================================================
PluginManager & PluginManager::Get()
{
return mInstance;
}
void PluginManager::Initialize()
{
// Always load the config first
bool loaded = Load();
// Then look for providers (they may autoregister plugins)
ModuleManager::Get().DiscoverProviders();
// And finally check for updates
CheckForUpdates(!loaded);
}
void PluginManager::Terminate()
{
if (mConfig)
{
Save();
delete mConfig;
mConfig = NULL;
}
// 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::Load()
{
// IF already open THEN nothing to do.
if (mConfig != NULL)
{
return true;
}
// Create the config
//
// TODO: I'd like to switch to XML using the TinyXML2 lib
mConfig = new wxFileConfig(wxEmptyString, wxEmptyString, FileNames::PluginsCache());
// If this group doesn't exist then we have the original format and we
// can't use it or even attempt a conversion, so just start with a fresh
// config.
if (!mConfig->HasGroup(CACHEROOT))
{
// Must start over
mConfig->DeleteAll();
return false;
}
// Load all provider plugins first
LoadGroup(wxT("modules"), PluginTypeModule);
// Now the rest
LoadGroup(wxT("effects"), PluginTypeEffect);
LoadGroup(wxT("exporters"), PluginTypeExporter);
LoadGroup(wxT("importers"), PluginTypeImporter);
LoadGroup(wxT("placeholders"), PluginTypeNone);
return true;
}
void PluginManager::LoadGroup(const wxChar * group, PluginType type)
{
wxString strVal;
bool boolVal;
wxString groupName;
long groupIndex;
wxString cfgPath = CACHEROOT + group + wxCONFIG_PATH_SEPARATOR;
mConfig->SetPath(cfgPath);
for (bool cont = mConfig->GetFirstGroup(groupName, groupIndex);
cont;
mConfig->SetPath(cfgPath),
cont = mConfig->GetNextGroup(groupName, groupIndex))
{
PluginDescriptor plug;
mConfig->SetPath(groupName);
groupName = ConvertID(groupName);
// Bypass group if the ID is already in use
if (mPlugins.find(groupName) != mPlugins.end())
{
mConfig->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 (!mConfig->Read(KEY_PROVIDERID, &strVal, wxEmptyString))
{
// Bypass group if the provider isn't valid
if (!strVal.IsEmpty() && mPlugins.find(wxString(strVal)) == mPlugins.end())
{
continue;
}
}
plug.SetProviderID(PluginID(strVal));
// Get the path (optional)
mConfig->Read(KEY_PATH, &strVal, wxEmptyString);
plug.SetPath(wxString(strVal));
// Get the name and bypass group if not found
if (!mConfig->Read(KEY_NAME, &strVal))
{
continue;
}
plug.SetName(wxString(strVal));
// Get the version and bypass group if not found
if (!mConfig->Read(KEY_VERSION, &strVal))
{
continue;
}
plug.SetVersion(wxString(strVal));
// Get the vendor and bypass group if not found
if (!mConfig->Read(KEY_VENDOR, &strVal))
{
continue;
}
plug.SetVendor(wxString(strVal));
// Get the description and bypass group if not found
if (!mConfig->Read(KEY_DESCRIPTION, &strVal))
{
continue;
}
plug.SetDescription(wxString(strVal));
// Is it enabled...default to no if not found
mConfig->Read(KEY_ENABLED, &boolVal, false);
plug.SetEnabled(boolVal);
// Is it valid...default to no if not found
mConfig->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 (!mConfig->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 (!mConfig->Read(KEY_EFFECTFAMILY, &strVal))
{
continue;
}
plug.SetEffectFamily(wxString(strVal));
// Is it a default (above the line) effect and bypass group if not found
if (!mConfig->Read(KEY_EFFECTDEFAULT, &boolVal))
{
continue;
}
plug.SetEffectDefault(boolVal);
// Is it an interactive effect and bypass group if not found
if (!mConfig->Read(KEY_EFFECTINTERACTIVE, &boolVal))
{
continue;
}
plug.SetEffectInteractive(boolVal);
// Is it a realtime capable effect and bypass group if not found
if (!mConfig->Read(KEY_EFFECTREALTIME, &boolVal))
{
continue;
}
plug.SetEffectRealtime(boolVal);
// Does the effect support automation...bypass group if not found
if (!mConfig->Read(KEY_EFFECTAUTOMATABLE, &boolVal))
{
continue;
}
plug.SetEffectAutomatable(boolVal);
}
break;
case PluginTypeImporter:
{
// Get the importer identifier and bypass group if not found
if (!mConfig->Read(KEY_IMPORTERIDENT, &strVal))
{
continue;
}
plug.SetImporterIdentifier(wxString(strVal));
// Get the importer filter description and bypass group if not found
if (!mConfig->Read(KEY_IMPORTERFILTER, &strVal))
{
continue;
}
plug.SetImporterFilterDescription(wxString(strVal));
// Get the importer extensions and bypass group if not found
if (!mConfig->Read(KEY_IMPORTEREXTENSIONS, &strVal))
{
continue;
}
wxArrayString extensions;
wxStringTokenizer tkr(strVal, wxT(":"));
while (tkr.HasMoreTokens())
{
extensions.push_back(wxString(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()
{
// IF already closed THEN nothing to do.
if (mConfig == NULL)
{
return;
}
// TODO: This is a bit drastic...only save groups when new plugins are registered
// Save the individual groups
SaveGroup(wxT("effects"), PluginTypeEffect);
SaveGroup(wxT("exporters"), PluginTypeExporter);
SaveGroup(wxT("importers"), PluginTypeImporter);
SaveGroup(wxT("placeholders"), PluginTypeNone);
// And now the providers
SaveGroup(wxT("modules"), PluginTypeModule);
mConfig->Flush();
}
void PluginManager::SaveGroup(const wxChar *group, PluginType type)
{
for (PluginMap::iterator iter = mPlugins.begin(); iter != mPlugins.end(); iter++)
{
PluginDescriptor & plug = iter->second;
if (plug.GetPluginType() != type)
{
continue;
}
mConfig->SetPath(CACHEROOT + group + wxCONFIG_PATH_SEPARATOR + ConvertID(plug.GetID()));
mConfig->Write(KEY_PATH, plug.GetPath());
mConfig->Write(KEY_NAME, plug.GetName());
mConfig->Write(KEY_VERSION, plug.GetVersion());
mConfig->Write(KEY_VENDOR, plug.GetVendor());
mConfig->Write(KEY_DESCRIPTION, plug.GetDescription());
mConfig->Write(KEY_PROVIDERID, plug.GetProviderID());
mConfig->Write(KEY_ENABLED, plug.IsEnabled());
mConfig->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;
}
mConfig->Write(KEY_EFFECTTYPE, stype);
mConfig->Write(KEY_EFFECTFAMILY, plug.GetEffectFamily());
mConfig->Write(KEY_EFFECTDEFAULT, plug.IsEffectDefault());
mConfig->Write(KEY_EFFECTINTERACTIVE, plug.IsEffectInteractive());
mConfig->Write(KEY_EFFECTREALTIME, plug.IsEffectRealtime());
mConfig->Write(KEY_EFFECTAUTOMATABLE, plug.IsEffectAutomatable());
}
break;
case PluginTypeImporter:
{
mConfig->Write(KEY_IMPORTERIDENT, plug.GetImporterIdentifier());
mConfig->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);
mConfig->Write(KEY_IMPORTEREXTENSIONS, strExt);
}
break;
default:
break;
}
}
return;
}
void PluginManager::CheckForUpdates(bool forceRescan)
{
// Get ModuleManager reference
ModuleManager & mm = ModuleManager::Get();
bool doRescan;
gPrefs->Read(wxT("/Plugins/Rescan"), &doRescan, true);
bool doCheck;
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(), plugID, 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);
}
}
if (mConfig)
{
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;
if (type == PluginTypeEffect)
{
gPrefs->Read(plug.GetEffectFamily() + wxT("/Enable"), &familyEnabled, true);
}
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(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::GetName(const PluginID & ID)
{
if (mPlugins.find(ID) == mPlugins.end())
{
static wxString empty;
return empty;
}
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);
}
PluginDescriptor & PluginManager::CreatePlugin(IdentInterface *ident, PluginType type)
{
// This will either create a new entry or replace an existing entry
PluginDescriptor & plug = mPlugins[ident->GetID()];
plug.SetPluginType(type);
plug.SetID(ident->GetID());
plug.SetPath(ident->GetPath());
plug.SetName(ident->GetName());
plug.SetVendor(ident->GetVendor());
plug.SetVersion(ident->GetVersion());
plug.SetDescription(ident->GetDescription());
return plug;
}
bool PluginManager::GetSubgroups(const wxString & group, wxArrayString & subgroups)
{
bool result = false;
if (mConfig && !group.IsEmpty())
{
wxString name = wxEmptyString;
long index = 0;
wxString path = mConfig->GetPath();
mConfig->SetPath(group);
if (mConfig->GetFirstGroup(name, index))
{
do
{
subgroups.Add(name);
} while (mConfig->GetNextGroup(name, index));
}
mConfig->SetPath(path);
}
return result;
}
bool PluginManager::GetConfig(const wxString & key, int & value, int defval)
{
bool result = false;
if (mConfig && !key.IsEmpty())
{
result = mConfig->Read(key, &value, defval);
}
return result;
}
bool PluginManager::GetConfig(const wxString & key, wxString & value, const wxString & defval)
{
bool result = false;
if (mConfig && !key.IsEmpty())
{
wxString wxval = wxEmptyString;
result = mConfig->Read(key, &wxval, defval);
value = wxval;
}
return result;
}
bool PluginManager::GetConfig(const wxString & key, bool & value, bool defval)
{
bool result = false;
if (mConfig && !key.IsEmpty())
{
result = mConfig->Read(key, &value, defval);
}
return result;
}
bool PluginManager::GetConfig(const wxString & key, float & value, float defval)
{
bool result = false;
if (mConfig && !key.IsEmpty())
{
double dval = 0.0;
result = mConfig->Read(key, &dval, (double) defval);
value = (float) dval;
}
return result;
}
bool PluginManager::GetConfig(const wxString & key, double & value, double defval)
{
bool result = false;
if (mConfig && !key.IsEmpty())
{
result = mConfig->Read(key, &value, defval);
}
return result;
}
bool PluginManager::GetConfig(const wxString & key, sampleCount & value, sampleCount defval)
{
bool result = false;
if (mConfig && !key.IsEmpty())
{
wxString wxval = wxEmptyString;
wxString wxdef;
wchar_t *endptr;
wxdef.Printf(wxT("%Ld"), defval);
result = mConfig->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 (mConfig && !key.IsEmpty())
{
wxString wxval = value.c_str();
result = mConfig->Write(key, wxval);
if (result)
{
result = mConfig->Flush();
}
}
return result;
}
bool PluginManager::SetConfig(const wxString & key, const int & value)
{
bool result = false;
if (mConfig && !key.IsEmpty())
{
result = mConfig->Write(key, value);
if (result)
{
result = mConfig->Flush();
}
}
return result;
}
bool PluginManager::SetConfig(const wxString & key, const bool & value)
{
bool result = false;
if (mConfig && !key.IsEmpty())
{
result = mConfig->Write(key, value);
if (result)
{
result = mConfig->Flush();
}
}
return result;
}
bool PluginManager::SetConfig(const wxString & key, const float & value)
{
bool result = false;
if (mConfig && !key.IsEmpty())
{
result = mConfig->Write(key, value);
if (result)
{
result = mConfig->Flush();
}
}
return result;
}
bool PluginManager::SetConfig(const wxString & key, const double & value)
{
bool result = false;
if (mConfig && !key.IsEmpty())
{
result = mConfig->Write(key, value);
if (result)
{
result = mConfig->Flush();
}
}
return result;
}
bool PluginManager::SetConfig(const wxString & key, const sampleCount & value)
{
bool result = false;
if (mConfig && !key.IsEmpty())
{
result = mConfig->Write(key, wxString::Format(wxT("%d"), (int) value));
if (result)
{
result = mConfig->Flush();
}
}
return result;
}
wxString PluginManager::SharedGroup(const PluginID & ID, const wxString & group)
{
if (mPlugins.find(ID) == mPlugins.end())
{
return wxEmptyString;
}
wxString path = CACHEROOT +
ConvertID(mPlugins[ID].GetProviderID()) +
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)
{
if (mPlugins.find(ID) == mPlugins.end())
{
return wxEmptyString;
}
wxString path = CACHEROOT +
ConvertID(ID) +
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;
}