Audacity  2.3.1
PluginManager.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3  Audacity: A Digital Audio Editor
4 
5  PluginManager.cpp
6 
7  Leland Lucius
8 
9 *******************************************************************/
21 #include <algorithm>
22 
23 #include "Audacity.h"
24 
25 #include <wx/defs.h>
26 #include <wx/dialog.h>
27 #include <wx/dir.h>
28 #include <wx/dynlib.h>
29 #include <wx/hashmap.h>
30 #include <wx/filename.h>
31 #include <wx/list.h>
32 #include <wx/listctrl.h>
33 #include <wx/log.h>
34 #include <wx/radiobut.h>
35 #include <wx/string.h>
36 #include <wx/tokenzr.h>
37 #include <wx/wfstream.h>
38 #include <wx/utils.h>
39 
41 
42 #include "FileNames.h"
43 #include "ModuleManager.h"
44 #include "PlatformCompatibility.h"
45 #include "Prefs.h"
46 #include "ShuttleGui.h"
47 #include "effects/EffectManager.h"
48 #include "widgets/ErrorDialog.h"
49 #include "widgets/ProgressDialog.h"
50 
51 #if wxUSE_ACCESSIBILITY
53 #endif
54 
55 #include "PluginManager.h"
56 
57 #include "Experimental.h"
58 
59 #include <unordered_map>
60 
61 using ProviderMap = std::unordered_map<wxString, wxArrayString>;
62 
63 // ============================================================================
64 //
65 //
66 //
67 // ============================================================================
68 #if wxUSE_ACCESSIBILITY
70 
71 class CheckListAx final : public WindowAccessible
72 {
73 public:
74  CheckListAx(wxListCtrl * window);
75 
76  virtual ~ CheckListAx();
77 
78  // Retrieves the address of an IDispatch interface for the specified child.
79  // All objects must support this property.
80  wxAccStatus GetChild( int childId, wxAccessible **child ) override;
81 
82  // Gets the number of children.
83  wxAccStatus GetChildCount( int *childCount ) override;
84 
85  // Gets the default action for this object (0) or > 0 (the action for a child).
86  // Return wxACC_OK even if there is no action. actionName is the action, or the empty
87  // string if there is no action.
88  // The retrieved string describes the action that is performed on an object,
89  // not what the object does as a result. For example, a toolbar button that prints
90  // a document has a default action of "Press" rather than "Prints the current document."
91  wxAccStatus GetDefaultAction( int childId, wxString *actionName ) override;
92 
93  // Returns the description for this object or a child.
94  wxAccStatus GetDescription( int childId, wxString *description ) override;
95 
96  // Gets the window with the keyboard focus.
97  // If childId is 0 and child is NULL, no object in
98  // this subhierarchy has the focus.
99  // If this object has the focus, child should be 'this'.
100  wxAccStatus GetFocus( int *childId, wxAccessible **child ) override;
101 
102  // Returns help text for this object or a child, similar to tooltip text.
103  wxAccStatus GetHelpText( int childId, wxString *helpText ) override;
104 
105  // Returns the keyboard shortcut for this object or child.
106  // Return e.g. ALT+K
107  wxAccStatus GetKeyboardShortcut( int childId, wxString *shortcut ) override;
108 
109  // Returns the rectangle for this object (id = 0) or a child element (id > 0).
110  // rect is in screen coordinates.
111  wxAccStatus GetLocation( wxRect& rect, int elementId ) override;
112 
113  // Gets the name of the specified object.
114  wxAccStatus GetName( int childId, wxString *name ) override;
115 
116  // Returns a role constant.
117  wxAccStatus GetRole( int childId, wxAccRole *role ) override;
118 
119  // Gets a variant representing the selected children
120  // of this object.
121  // Acceptable values:
122  // - a null variant (IsNull() returns TRUE)
123  // - a list variant (GetType() == wxT("list"))
124  // - an integer representing the selected child element,
125  // or 0 if this object is selected (GetType() == wxT("long"))
126  // - a "void*" pointer to a wxAccessible child object
127  wxAccStatus GetSelections( wxVariant *selections ) override;
128 
129  // Returns a state constant.
130  wxAccStatus GetState( int childId, long* state ) override;
131 
132  // Returns a localized string representing the value for the object
133  // or child.
134  wxAccStatus GetValue( int childId, wxString *strValue ) override;
135 
136  void SetSelected( int item, bool focused = true );
137 
138 private:
139  wxListCtrl *mParent;
140  int mLastId;
141 };
142 
143 CheckListAx::CheckListAx( wxListCtrl * window )
144 : WindowAccessible( window )
145 {
146  mParent = window;
147  mLastId = -1;
148 }
149 
150 CheckListAx::~CheckListAx()
151 {
152 }
153 
154 void CheckListAx::SetSelected( int item, bool focused )
155 {
156  if (mLastId != -1)
157  {
158  NotifyEvent( wxACC_EVENT_OBJECT_SELECTIONREMOVE,
159  mParent,
160  wxOBJID_CLIENT,
161  mLastId );
162  mLastId = -1;
163  }
164 
165  if (item != -1)
166  {
167  if (focused)
168  {
169  NotifyEvent( wxACC_EVENT_OBJECT_FOCUS,
170  mParent,
171  wxOBJID_CLIENT,
172  item + 1 );
173  }
174 
175  NotifyEvent( wxACC_EVENT_OBJECT_SELECTION,
176  mParent,
177  wxOBJID_CLIENT,
178  item + 1 );
179 
180  mLastId = item + 1;
181  }
182 }
183 
184 // Retrieves the address of an IDispatch interface for the specified child.
185 // All objects must support this property.
186 wxAccStatus CheckListAx::GetChild( int childId, wxAccessible** child )
187 {
188  if( childId == wxACC_SELF )
189  {
190  *child = this;
191  }
192  else
193  {
194  *child = NULL;
195  }
196 
197  return wxACC_OK;
198 }
199 
200 // Gets the number of children.
201 wxAccStatus CheckListAx::GetChildCount( int *childCount )
202 {
203  *childCount = mParent->GetItemCount();
204 
205  return wxACC_OK;
206 }
207 
208 // Gets the default action for this object (0) or > 0 (the action for a child).
209 // Return wxACC_OK even if there is no action. actionName is the action, or the empty
210 // string if there is no action.
211 // The retrieved string describes the action that is performed on an object,
212 // not what the object does as a result. For example, a toolbar button that prints
213 // a document has a default action of "Press" rather than "Prints the current document."
214 wxAccStatus CheckListAx::GetDefaultAction( int WXUNUSED(childId), wxString *actionName )
215 {
216  actionName->Clear();
217 
218  return wxACC_OK;
219 }
220 
221 // Returns the description for this object or a child.
222 wxAccStatus CheckListAx::GetDescription( int WXUNUSED(childId), wxString *description )
223 {
224  description->Clear();
225 
226  return wxACC_OK;
227 }
228 
229 // Gets the window with the keyboard focus.
230 // If childId is 0 and child is NULL, no object in
231 // this subhierarchy has the focus.
232 // If this object has the focus, child should be 'this'.
233 wxAccStatus CheckListAx::GetFocus( int *childId, wxAccessible **child )
234 {
235  *childId = 0;
236  *child = this;
237 
238  return wxACC_OK;
239 }
240 
241 // Returns help text for this object or a child, similar to tooltip text.
242 wxAccStatus CheckListAx::GetHelpText( int WXUNUSED(childId), wxString *helpText )
243 {
244  helpText->Clear();
245 
246  return wxACC_OK;
247 }
248 
249 // Returns the keyboard shortcut for this object or child.
250 // Return e.g. ALT+K
251 wxAccStatus CheckListAx::GetKeyboardShortcut( int WXUNUSED(childId), wxString *shortcut )
252 {
253  shortcut->Clear();
254 
255  return wxACC_OK;
256 }
257 
258 // Returns the rectangle for this object (id = 0) or a child element (id > 0).
259 // rect is in screen coordinates.
260 wxAccStatus CheckListAx::GetLocation( wxRect& rect, int elementId )
261 {
262  if( elementId == wxACC_SELF )
263  {
264  rect = mParent->GetRect();
265  rect.SetPosition( mParent->GetParent()->ClientToScreen( rect.GetPosition() ) );
266  }
267  else
268  {
269  if( elementId <= mParent->GetItemCount() )
270  {
271  mParent->GetItemRect( elementId - 1, rect, wxLIST_RECT_LABEL );
272  rect.SetPosition( mParent->ClientToScreen( rect.GetPosition() ) );
273  }
274  }
275 
276  return wxACC_OK;
277 }
278 
279 // Gets the name of the specified object.
280 wxAccStatus CheckListAx::GetName( int WXUNUSED(childId), wxString *name )
281 {
282  *name = mParent->GetName();
283 
284  return wxACC_OK;
285 }
286 
287 // Returns a role constant.
288 wxAccStatus CheckListAx::GetRole( int childId, wxAccRole *role )
289 {
290  if( childId == wxACC_SELF )
291  {
292  *role = wxROLE_SYSTEM_LIST;
293  }
294  else
295  {
296  *role = wxROLE_SYSTEM_LISTITEM;
297  }
298 
299  return wxACC_OK;
300 }
301 
302 // Gets a variant representing the selected children
303 // of this object.
304 // Acceptable values:
305 // - a null variant (IsNull() returns TRUE)
306 // - a list variant (GetType() == wxT("list"))
307 // - an integer representing the selected child element,
308 // or 0 if this object is selected (GetType() == wxT("long"))
309 // - a "void*" pointer to a wxAccessible child object
310 wxAccStatus CheckListAx::GetSelections( wxVariant * WXUNUSED(selections) )
311 {
312  return wxACC_NOT_IMPLEMENTED;
313 }
314 
315 // Returns a state constant.
316 wxAccStatus CheckListAx::GetState( int childId, long *pState )
317 {
318  int flag = wxACC_STATE_SYSTEM_FOCUSABLE;
319 
320  if( childId == wxACC_SELF )
321  {
322  flag |= wxACC_STATE_SYSTEM_FOCUSED;
323  }
324  else
325  {
326  wxListItem item;
327 
328  item.SetId( childId - 1 );
329  item.SetState( wxLIST_STATE_FOCUSED | wxLIST_STATE_SELECTED );
330  item.SetMask( wxLIST_MASK_STATE );
331 
332  if( mParent->GetItem( item ) )
333  {
334  flag |= wxACC_STATE_SYSTEM_SELECTABLE;
335 
336  long state = item.GetState();
337 
338  if( state & wxLIST_STATE_FOCUSED )
339  {
340  flag |= wxACC_STATE_SYSTEM_FOCUSED;
341  }
342 
343  if( state & wxLIST_STATE_SELECTED )
344  {
345  flag |= wxACC_STATE_SYSTEM_SELECTED;
346  }
347  }
348  }
349 
350  *pState = flag;
351 
352  return wxACC_OK;
353 }
354 
355 // Returns a localized string representing the value for the object
356 // or child.
357 wxAccStatus CheckListAx::GetValue( int childId, wxString *strValue )
358 {
359  if( childId == 0 )
360  {
361  return wxACC_OK;
362  }
363  else
364  {
365  *strValue = mParent->GetItemText( childId - 1 );
366  }
367 
368  return wxACC_OK;
369 }
370 
371 #endif
372 
373 // ============================================================================
374 //
375 //
376 //
377 // ============================================================================
378 
379 enum
380 {
384 
386 };
387 
388 struct ItemData
389 {
390  std::vector<PluginDescriptor*> plugs;
391  wxString name;
392  wxString path;
393  int state;
394  bool valid;
398 };
399 
400 using ItemDataMap = std::unordered_map<wxString, ItemData>;
401 
402 enum
403 {
404  ID_ShowAll = 10000,
413 };
414 
415 enum
416 {
420 
422 };
423 
425 {
426 public:
427  // constructors and destructors
428  PluginRegistrationDialog(wxWindow *parent, EffectType type);
429 
430 private:
431  void Populate();
432  void PopulateOrExchange(ShuttleGui & S);
433  void RegenerateEffectsList(int iShowWhat);
434  void SetState(int i, bool toggle, bool state = true);
435 
436  static int wxCALLBACK SortCompare(long item1, long item2, long sortData);
437  int SortCompare(ItemData *item1, ItemData *item2);
438 
439  void OnChangedVisibility(wxCommandEvent & evt);
440  void OnSort(wxListEvent & evt);
441  void OnListChar(wxKeyEvent & evt);
442  void OnOK(wxCommandEvent & evt);
443  void OnCancel(wxCommandEvent & evt);
444  void OnSelectAll(wxCommandEvent & evt);
445  void OnClearAll(wxCommandEvent & evt);
446  void OnEnable(wxCommandEvent & evt);
447  void OnDisable(wxCommandEvent & evt);
448 
449 private:
452  int mFilter;
453 
454  wxArrayString mStates;
456 
459 
460  wxString mLongestPath;
461 
462  wxListCtrl *mEffects;
463 #if wxUSE_ACCESSIBILITY
464  CheckListAx *mAx;
465 #endif
466 
467  DECLARE_EVENT_TABLE()
468 };
469 
470 BEGIN_EVENT_TABLE(PluginRegistrationDialog, wxDialogWrapper)
471  EVT_LIST_COL_CLICK(ID_List, PluginRegistrationDialog::OnSort)
472  EVT_BUTTON(wxID_OK, PluginRegistrationDialog::OnOK)
473  EVT_BUTTON(wxID_CANCEL, PluginRegistrationDialog::OnCancel)
478  EVT_RADIOBUTTON(ID_ShowAll, PluginRegistrationDialog::OnChangedVisibility)
479  EVT_RADIOBUTTON(ID_ShowEnabled, PluginRegistrationDialog::OnChangedVisibility)
480  EVT_RADIOBUTTON(ID_ShowDisabled, PluginRegistrationDialog::OnChangedVisibility)
481  EVT_RADIOBUTTON(ID_ShowNew, PluginRegistrationDialog::OnChangedVisibility)
483 
485 : wxDialogWrapper(parent,
486  wxID_ANY,
487  _("Manage Plug-ins"),
488  wxDefaultPosition, wxDefaultSize,
489  wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
490 {
491  mType = type;
492  mEffects = NULL;
493  SetName(GetTitle());
494 
495  mStates.SetCount(STATE_COUNT);
496  mStates[STATE_Enabled] = _("Enabled");
497  mStates[STATE_Disabled] = _("Disabled");
498  mStates[STATE_New] = _("New");
499 
500  mSortColumn = COL_Name;
501  mSortDirection = 1;
502 
503  Populate();
504 }
505 
507 {
508  //------------------------- Main section --------------------
509  ShuttleGui S(this, eIsCreating);
511  // ----------------------- End of main section --------------
512 }
513 
516 {
517  S.StartVerticalLay(true);
518  {
519  /*i18n-hint: The dialog shows a list of plugins with check-boxes
520  beside each one.*/
521 // S.StartStatic(_("Effects"), true);
522  S.StartVerticalLay();
523  {
524  S.StartHorizontalLay(wxEXPAND, 0);
525  {
526  S.StartHorizontalLay(wxALIGN_LEFT, 0);
527  {
528  S.AddPrompt(_("Select effects, click the Enable or Disable button, then click OK."));
529  }
530  S.EndHorizontalLay();
531 
532  S.StartHorizontalLay(wxCENTER, 1);
533  {
534  S.AddSpace(1);
535  }
536  S.EndHorizontalLay();
537 
538  S.StartHorizontalLay(wxALIGN_NOT | wxALIGN_LEFT, 0);
539  {
540  wxRadioButton* rb;
541  /* i18n-hint: This is before radio buttons selecting which effects to show */
542  S.AddPrompt(_("Show:"));
543  /* i18n-hint: Radio button to show all effects */
544  rb = S.Id(ID_ShowAll).AddRadioButton(_("&All"));
545 #if wxUSE_ACCESSIBILITY
546  // so that name can be set on a standard control
547  rb->SetAccessible(safenew WindowAccessible(rb));
548 #endif
549  rb->SetName(_("Show all"));
550  /* i18n-hint: Radio button to show just the currently disabled effects */
551  rb = S.Id(ID_ShowDisabled).AddRadioButtonToGroup(_("D&isabled"));
552 #if wxUSE_ACCESSIBILITY
553  // so that name can be set on a standard control
554  rb->SetAccessible(safenew WindowAccessible(rb));
555 #endif
556  rb->SetName(_("Show disabled"));
557  /* i18n-hint: Radio button to show just the currently enabled effects */
558  rb = S.Id(ID_ShowEnabled).AddRadioButtonToGroup(_("E&nabled"));
559 #if wxUSE_ACCESSIBILITY
560  // so that name can be set on a standard control
561  rb->SetAccessible(safenew WindowAccessible(rb));
562 #endif
563  rb->SetName(_("Show enabled"));
564  /* i18n-hint: Radio button to show just the newly discovered effects */
565  rb = S.Id(ID_ShowNew).AddRadioButtonToGroup(_("Ne&w"));
566 #if wxUSE_ACCESSIBILITY
567  // so that name can be set on a standard control
568  rb->SetAccessible(safenew WindowAccessible(rb));
569 #endif
570  rb->SetName(_("Show new"));
571  }
572  S.EndHorizontalLay();
573  }
574  S.EndHorizontalLay();
575 
576  S.SetStyle(wxSUNKEN_BORDER | wxLC_REPORT | wxLC_HRULES | wxLC_VRULES );
578  mEffects->Bind(wxEVT_KEY_DOWN,
580  this);
581 #if wxUSE_ACCESSIBILITY
582  mEffects->SetAccessible(mAx = safenew CheckListAx(mEffects));
583 #endif
584  mEffects->InsertColumn(COL_Name, _("Name"));
585  mEffects->InsertColumn(COL_State, _("State"));
586  mEffects->InsertColumn(COL_Path, _("Path"));
587 
588  S.StartHorizontalLay(wxALIGN_LEFT | wxEXPAND, 0);
589  {
590  S.Id(ID_SelectAll).AddButton(_("&Select All"));
591  S.Id(ID_ClearAll).AddButton(_("C&lear All"));
592 
593  S.StartHorizontalLay(wxALIGN_CENTER);
594  {
595  S.AddSpace(1);
596  }
597  S.EndHorizontalLay();
598 
599  S.Id(ID_Enable).AddButton(_("&Enable"));
600  S.Id(ID_Disable).AddButton(_("&Disable"));
601  }
602  S.EndHorizontalLay();
603  }
604 // S.EndStatic();
605  S.EndVerticalLay();
606 
608  }
609  S.EndVerticalLay();
610 
611  std::vector<int> colWidths;
612  for (int i = 0, cnt = mEffects->GetColumnCount(); i < cnt; i++)
613  {
614  colWidths.push_back(0);
615  }
616 
617  for (int i = 0, cnt = mStates.GetCount(); i < cnt; i++)
618  {
619  int x;
620  mEffects->GetTextExtent(mStates[i], &x, NULL);
621  colWidths[COL_State] = wxMax(colWidths[COL_State], x + 4); // 2 pixel margin on each side
622  }
623 
625  for (PluginMap::iterator iter = pm.mPlugins.begin(); iter != pm.mPlugins.end(); ++iter)
626  {
627  PluginDescriptor & plug = iter->second;
628 
629  PluginType plugType = plug.GetPluginType();
630  if (plugType != PluginTypeEffect && plugType != PluginTypeStub)
631  {
632  continue;
633  }
634 
635  const wxString &path = plug.GetPath();
636  ItemData & item = mItems[path]; // will create NEW entry
637  item.plugs.push_back(&plug);
638  item.path = path;
639  item.state = plug.IsEnabled() ? STATE_Enabled : STATE_Disabled;
640  item.valid = plug.IsValid();
641 
642  if (plugType == PluginTypeEffect)
643  {
644  item.name = plug.GetSymbol().Translation();
645  }
646  // This is not right and will not work when other plugin types are added.
647  // But it's presumed that the plugin manager dialog will be fully developed
648  // by then.
649  else if (plugType == PluginTypeStub)
650  {
651  wxFileName fname = path;
652  item.name = fname.GetName().Trim(false).Trim(true);
653  if (!item.valid)
654  {
655  item.state = STATE_New;
656  }
657  }
658 
659  int x;
660  mEffects->GetTextExtent(item.name, &x, NULL);
661  colWidths[COL_Name] = wxMax(colWidths[COL_Name], x);
662 
663  mEffects->GetTextExtent(item.path, &x, NULL);
664  if (x > colWidths[COL_Path])
665  {
666  mLongestPath = item.path;
667  }
668  colWidths[COL_Path] = wxMax(colWidths[COL_Path], x);
669  }
670 
671  wxRect r = wxGetClientDisplayRect();
672 
673  int maxW = 0;
674  for (int i = 0, cnt = mEffects->GetColumnCount(); i < cnt; i++)
675  {
676  int w = colWidths[i] + /* fudge */ 10;
677  mEffects->SetColumnWidth(i, w);
678  maxW += w;
679  }
680 
681  // Keep dialog from getting too wide
682  int w = r.GetWidth() - (GetClientSize().GetWidth() - mEffects->GetSize().GetWidth());
683  mEffects->SetSizeHints(wxSize(wxMin(maxW, w), 200), wxSize(w, -1));
684 
686 
687  Layout();
688  Fit();
689 
690  wxSize sz = GetSize();
691  sz.SetWidth(wxMin(sz.GetWidth(), r.GetWidth()));
692  sz.SetHeight(wxMin(sz.GetHeight(), r.GetHeight()));
693  SetMinSize(sz);
694 
695  // Parent window is usually not there yet, so centre on screen rather than on parent.
696  CenterOnScreen();
697 
698  if (mEffects->GetItemCount() > 0)
699  {
700  // Make sure first item is selected/focused.
701  mEffects->SetFocus();
702  mEffects->SetItemState(0, wxLIST_STATE_FOCUSED | wxLIST_STATE_SELECTED, wxLIST_STATE_FOCUSED | wxLIST_STATE_SELECTED);
703 #if wxUSE_ACCESSIBILITY
704  mAx->SetSelected(0);
705 #endif
706  }
707 
708 }
709 
711 {
712  mFilter = filter;
713 
714  mEffects->DeleteAllItems();
715 
716  int i = 0;
717  for (ItemDataMap::iterator iter = mItems.begin(); iter != mItems.end(); ++iter)
718  {
719  ItemData & item = iter->second;
720  bool add = false;
721 
722  switch (mFilter)
723  {
724  case ID_ShowAll:
725  add = true;
726  break;
727  case ID_ShowNew:
728  if (item.state == STATE_New)
729  {
730  add = true;
731  }
732  break;
733  case ID_ShowEnabled:
734  if (item.state == STATE_Enabled)
735  {
736  add = true;
737  }
738  break;
739  case ID_ShowDisabled:
740  if (item.state == STATE_Disabled)
741  {
742  add = true;
743  }
744  break;
745  }
746 
747  if (add)
748  {
749  mEffects->InsertItem(i, item.name);
750  mEffects->SetItem(i, COL_State, mStates[item.state]);
751  mEffects->SetItem(i, COL_Path, item.path);
752  mEffects->SetItemPtrData(i, (wxUIntPtr) &item);
753 
754  ++i;
755  }
756  }
757 
758  mEffects->SortItems(SortCompare, (wxUIntPtr) this);
759 
760  if (mEffects->GetItemCount() > 0)
761  {
762  // Make sure first item is selected/focused.
763 // mEffects->SetFocus();
764  mEffects->SetItemState(0, wxLIST_STATE_FOCUSED|wxLIST_STATE_SELECTED, wxLIST_STATE_FOCUSED|wxLIST_STATE_SELECTED);
765 #if wxUSE_ACCESSIBILITY
766  mAx->SetSelected(0, false);
767 #endif
768  }
769 }
770 
771 void PluginRegistrationDialog::SetState(int i, bool toggle, bool state)
772 {
773  wxListItem li;
774 
775  li.m_mask = wxLIST_MASK_DATA;
776  li.m_itemId = i;
777 
778  mEffects->GetItem(li);
779 
780  ItemData *item = (ItemData *) li.m_data;
781 
782  // If changing the state of a "New" (stub) entry, then we mark it as valid
783  // since it will either be registered if "Enabled" or ignored if "Disabled".
784  if (item->state == STATE_New)
785  {
786  item->valid = true;
787  }
788 
789  if (toggle)
790  {
792  }
793  else
794  {
795  item->state = state;
796  }
797 
798  if (mFilter == ID_ShowNew && item->state != STATE_New)
799  {
800  mEffects->DeleteItem(i);
801  }
802  else if (mFilter == ID_ShowDisabled && item->state != STATE_Disabled)
803  {
804  mEffects->DeleteItem(i);
805  }
806  else if (mFilter == ID_ShowEnabled && item->state != STATE_Enabled)
807  {
808  mEffects->DeleteItem(i);
809  }
810  else
811  {
812  mEffects->SetItem(i, COL_State, mStates[item->state]);
813 #if wxUSE_ACCESSIBILITY
814  mAx->SetSelected(i);
815 #endif
816  }
817 }
818 
819 int wxCALLBACK PluginRegistrationDialog::SortCompare(long item1, long item2, long sortData)
820 {
822  ItemData *i1 = (ItemData *) item1;
823  ItemData *i2 = (ItemData *) item2;
824 
825  return dlg->SortCompare(i1, i2);
826 }
827 
829 {
830  wxString *str1;
831  wxString *str2;
832 
833  switch (mSortColumn)
834  {
835  case COL_Name:
836  str1 = &item1->name;
837  str2 = &item2->name;
838  break;
839  case COL_State:
840  str1 = &mStates[item1->state];
841  str2 = &mStates[item2->state];
842  break;
843  case COL_Path:
844  str1 = &item1->path;
845  str2 = &item2->path;
846  break;
847  default:
848  return 0;
849  }
850 
851 #if defined(__WXMAC__)
852  return str2->Cmp(*str1) * mSortDirection;
853 #else
854  return str1->Cmp(*str2) * mSortDirection;
855 #endif
856 }
857 
859 {
860  // Go and show the relevant items.
861  RegenerateEffectsList(evt.GetId());
862 }
863 
864 void PluginRegistrationDialog::OnSort(wxListEvent & evt)
865 {
866  int col = evt.GetColumn();
867 
868  if (col != mSortColumn)
869  {
870  mSortDirection = 1;
871  }
872  else
873  {
874  mSortDirection *= -1;
875  }
876 
877  mSortColumn = col;
878  mEffects->SortItems(SortCompare, (wxUIntPtr) this);
879 }
880 
882 {
883  switch (evt.GetKeyCode())
884  {
885  case WXK_SPACE:
886  {
887  int item = mEffects->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_FOCUSED);
888  if (item != wxNOT_FOUND)
889  {
890  SetState(item, true);
891  }
892  }
893  break;
894 
895  case WXK_RETURN:
896  // Don't know why wxListCtrls prevent default dialog action,
897  // but they do, so handle it.
898  EmulateButtonClickIfPresent(GetAffirmativeId());
899  break;
900 
901  default:
902  evt.Skip();
903  break;
904  }
905 }
906 
907 void PluginRegistrationDialog::OnSelectAll(wxCommandEvent & WXUNUSED(evt))
908 {
909  for (int i = 0, cnt = mEffects->GetItemCount(); i < cnt; i++)
910  {
911  mEffects->SetItemState(i, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
912  }
913 }
914 
915 void PluginRegistrationDialog::OnClearAll(wxCommandEvent & WXUNUSED(evt))
916 {
917  for (int i = 0, cnt = mEffects->GetItemCount(); i < cnt; i++)
918  {
919  mEffects->SetItemState(i, 0, wxLIST_STATE_SELECTED);
920  }
921 }
922 
923 void PluginRegistrationDialog::OnEnable(wxCommandEvent & WXUNUSED(evt))
924 {
925  std::vector<long> items;
926 
927  {
928  long i = mEffects->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
929  while (i != wxNOT_FOUND)
930  {
931  items.insert(items.begin(), i);
932  i = mEffects->GetNextItem(i, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
933  }
934  }
935 
936  for (size_t i = 0, cnt = items.size(); i < cnt; i++)
937  {
938  SetState(items[i], false, STATE_Enabled);
939  }
940 }
941 
942 void PluginRegistrationDialog::OnDisable(wxCommandEvent & WXUNUSED(evt))
943 {
944  std::vector<long> items;
945 
946  {
947  long i = mEffects->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
948  while (i != wxNOT_FOUND)
949  {
950  items.insert(items.begin(), i);
951  i = mEffects->GetNextItem(i, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
952  }
953  }
954 
955  for (size_t i = 0, cnt = items.size(); i < cnt; i++)
956  {
957  SetState(items[i], false, STATE_Disabled);
958  }
959 }
960 
961 void PluginRegistrationDialog::OnOK(wxCommandEvent & WXUNUSED(evt))
962 {
965 
966  int enableCount = 0;
967  for (ItemDataMap::iterator iter = mItems.begin(); iter != mItems.end(); ++iter)
968  {
969  ItemData & item = iter->second;
970  wxString path = item.path;
971 
972  if (item.state == STATE_Enabled && item.plugs[0]->GetPluginType() == PluginTypeStub)
973  {
974  enableCount++;
975  }
976  }
977 
978  wxString last3 = mLongestPath + wxT("\n") +
979  mLongestPath + wxT("\n") +
980  mLongestPath + wxT("\n");
981 
982  wxString msg;
983 
984  msg.Printf(_("Enabling effects or commands:\n\n%s"), last3);
985 
986  // Make sure the progress dialog is deleted before we call EndModal() or
987  // we will leave the project window in an unusable state on OSX.
988  // See bug #1192.
989  {
990  ProgressDialog progress(GetTitle(), msg, pdlgHideStopButton);
991  progress.CenterOnParent();
992 
993  int i = 0;
994  for (ItemDataMap::iterator iter = mItems.begin(); iter != mItems.end(); ++iter)
995  {
996  ItemData & item = iter->second;
997  wxString path = item.path;
998 
999  if (item.state == STATE_Enabled && item.plugs[0]->GetPluginType() == PluginTypeStub)
1000  {
1001  last3 = last3.AfterFirst(wxT('\n')) + item.path + wxT("\n");
1002  auto status = progress.Update(++i, enableCount, wxString::Format(_("Enabling effect or command:\n\n%s"), last3));
1003  if (status == ProgressResult::Cancelled)
1004  {
1005  break;
1006  }
1007 
1008  wxString errMsgs;
1009 
1010  // Try to register the plugin via each provider until one succeeds
1011  for (size_t j = 0, cntj = item.plugs.size(); j < cntj; j++)
1012  {
1013  wxString errMsg;
1014  if (mm.RegisterEffectPlugin(item.plugs[j]->GetProviderID(), path,
1015  errMsg))
1016  {
1017  for (size_t k = 0, cntk = item.plugs.size(); k < cntk; k++)
1018  {
1019  pm.mPlugins.erase(item.plugs[k]->GetProviderID() + wxT("_") + path);
1020  }
1021  // Bug 1893. We've found a provider that works.
1022  // Error messages from any that failed are no longer useful.
1023  errMsgs.clear();
1024  break;
1025  }
1026  else
1027  {
1028  if (errMsgs.empty())
1029  errMsgs += '\n';
1030  errMsgs += errMsg;
1031  }
1032  }
1033  if (!errMsgs.empty())
1034  AudacityMessageBox( wxString::Format(
1035  _("Effect or Command at %s failed to register:\n%s"),
1036  path, errMsgs
1037  ) );
1038  }
1039  else if (item.state == STATE_New)
1040  {
1041  for (size_t j = 0, cnt = item.plugs.size(); j < cnt; j++)
1042  {
1043  item.plugs[j]->SetValid(false);
1044  }
1045  }
1046  else if (item.state != STATE_New)
1047  {
1048  for (size_t j = 0, cnt = item.plugs.size(); j < cnt; j++)
1049  {
1050  item.plugs[j]->SetEnabled(item.state == STATE_Enabled);
1051  item.plugs[j]->SetValid(item.valid);
1052  }
1053  }
1054  }
1055 
1056  pm.Save();
1057  }
1058 
1059  EndModal(wxID_OK);
1060 }
1061 
1062 void PluginRegistrationDialog::OnCancel(wxCommandEvent & WXUNUSED(evt))
1063 {
1064  EndModal(wxID_CANCEL);
1065 }
1066 
1067 
1068 
1070 //
1071 // Plugindescriptor
1072 //
1074 
1076 {
1078  mEnabled = false;
1079  mValid = false;
1080  mInstance = NULL;
1081 
1083  mEffectInteractive = false;
1084  mEffectDefault = false;
1085  mEffectLegacy = false;
1086  mEffectRealtime = false;
1087  mEffectAutomatable = false;
1088 }
1089 
1091 {
1092  DeleteInstance();
1093 }
1094 
1096 {
1097  if (mInstance)
1098  {
1100  mInstance = nullptr;
1101  }
1102 }
1103 
1105 {
1106  return mInstance != NULL;
1107 }
1108 
1110 {
1111  if (!mInstance)
1112  {
1114  {
1116  }
1117  else
1118  {
1120  }
1121  }
1122 
1123  return mInstance;
1124 }
1125 
1127 {
1128  if (mInstance && mInstance != instance)
1129  {
1130  // Be sure not to leak resources!!
1131  DeleteInstance();
1132  }
1133 
1134  mInstance = instance;
1135 
1136  return;
1137 }
1138 
1140 {
1141  return mPluginType;
1142 }
1143 
1145 {
1146  return mID;
1147 }
1148 
1150 {
1151  return mProviderID;
1152 }
1153 
1154 const wxString & PluginDescriptor::GetPath() const
1155 {
1156  return mPath;
1157 }
1158 
1160 {
1161  return mSymbol;
1162 }
1163 
1165 {
1166  return mVersion;
1167 }
1168 
1170 {
1171  return mVendor;
1172 }
1173 
1175 {
1176  return mEnabled;
1177 }
1178 
1180 {
1181  return mValid;
1182 }
1183 
1185 {
1186  mPluginType = type;
1187 }
1188 
1190 {
1191  mID = ID;
1192 }
1193 
1195 {
1196  mProviderID = providerID;
1197 }
1198 
1199 void PluginDescriptor::SetPath(const wxString & path)
1200 {
1201  mPath = path;
1202 }
1203 
1205 {
1206  mSymbol = symbol;
1207 }
1208 
1209 void PluginDescriptor::SetVersion(const wxString & version)
1210 {
1211  mVersion = version;
1212 }
1213 
1214 void PluginDescriptor::SetVendor(const wxString & vendor)
1215 {
1216  mVendor = vendor;
1217 }
1218 
1220 {
1221  mEnabled = enable;
1222 }
1223 
1225 {
1226  mValid = valid;
1227 }
1228 
1229 // Effects
1230 
1232 {
1233  return mEffectFamily;
1234 }
1235 
1237 {
1238  return mEffectType;
1239 }
1240 
1242 {
1243  return mEffectInteractive;
1244 }
1245 
1247 {
1248  return mEffectDefault;
1249 }
1250 
1252 {
1253  return mEffectLegacy;
1254 }
1255 
1257 {
1258  return mEffectRealtime;
1259 }
1260 
1262 {
1263  return mEffectAutomatable;
1264 }
1265 
1266 void PluginDescriptor::SetEffectFamilyId(const wxString & family)
1267 {
1268  mEffectFamily = family;
1269 }
1270 
1272 {
1273  mEffectType = type;
1274 }
1275 
1277 {
1278  mEffectInteractive = interactive;
1279 }
1280 
1282 {
1283  mEffectDefault = dflt;
1284 }
1285 
1287 {
1288  mEffectLegacy = legacy;
1289 }
1290 
1292 {
1293  mEffectRealtime = realtime;
1294 }
1295 
1297 {
1298  mEffectAutomatable = automatable;
1299 }
1300 
1301 // Importer
1302 
1304 {
1305  return mImporterIdentifier;
1306 }
1307 
1308 void PluginDescriptor::SetImporterIdentifier(const wxString & identifier)
1309 {
1310  mImporterIdentifier = identifier;
1311 }
1312 
1314 {
1315  return mImporterFilterDesc;
1316 }
1317 
1318 void PluginDescriptor::SetImporterFilterDescription(const wxString & filterDesc)
1319 {
1320  mImporterFilterDesc = filterDesc;
1321 }
1322 
1323 const wxArrayString & PluginDescriptor::GetImporterExtensions() const
1324 {
1325  return mImporterExtensions;
1326 }
1327 
1328 void PluginDescriptor::SetImporterExtensions(const wxArrayString & extensions)
1329 {
1330  mImporterExtensions = extensions;
1331 }
1332 
1334 //
1335 // PluginManager
1336 //
1338 
1339 // Registry has the list of plug ins
1340 #define REGVERKEY wxString(wxT("/pluginregistryversion"))
1341 #define REGVERCUR wxString(wxT("1.1"))
1342 #define REGROOT wxString(wxT("/pluginregistry/"))
1343 
1344 // Settings has the values of the plug in settings.
1345 #define SETVERKEY wxString(wxT("/pluginsettingsversion"))
1346 #define SETVERCUR wxString(wxT("1.0"))
1347 #define SETROOT wxString(wxT("/pluginsettings/"))
1348 
1349 #define KEY_ID wxT("ID")
1350 #define KEY_PATH wxT("Path")
1351 #define KEY_SYMBOL wxT("Symbol")
1352 #define KEY_NAME wxT("Name")
1353 #define KEY_VENDOR wxT("Vendor")
1354 #define KEY_VERSION wxT("Version")
1355 #define KEY_DESCRIPTION wxT("Description")
1356 #define KEY_LASTUPDATED wxT("LastUpdated")
1357 #define KEY_ENABLED wxT("Enabled")
1358 #define KEY_VALID wxT("Valid")
1359 #define KEY_PROVIDERID wxT("ProviderID")
1360 #define KEY_EFFECTTYPE wxT("EffectType")
1361 #define KEY_EFFECTFAMILY wxT("EffectFamily")
1362 #define KEY_EFFECTDEFAULT wxT("EffectDefault")
1363 #define KEY_EFFECTINTERACTIVE wxT("EffectInteractive")
1364 #define KEY_EFFECTREALTIME wxT("EffectRealtime")
1365 #define KEY_EFFECTAUTOMATABLE wxT("EffectAutomatable")
1366 #define KEY_EFFECTTYPE_NONE wxT("None")
1367 #define KEY_EFFECTTYPE_ANALYZE wxT("Analyze")
1368 #define KEY_EFFECTTYPE_GENERATE wxT("Generate")
1369 #define KEY_EFFECTTYPE_PROCESS wxT("Process")
1370 #define KEY_EFFECTTYPE_TOOL wxT("Tool")
1371 #define KEY_EFFECTTYPE_HIDDEN wxT("Hidden")
1372 #define KEY_IMPORTERIDENT wxT("ImporterIdent")
1373 #define KEY_IMPORTERFILTER wxT("ImporterFilter")
1374 #define KEY_IMPORTEREXTENSIONS wxT("ImporterExtensions")
1375 
1376 // ============================================================================
1377 //
1378 // PluginManagerInterface implementation
1379 //
1380 // ============================================================================
1381 
1383  ModuleInterface *provider, ComponentInterface *pInterface )
1384 {
1385  EffectDefinitionInterface * pEInterface = dynamic_cast<EffectDefinitionInterface*>(pInterface);
1386  if( pEInterface )
1387  return PluginManager::Get().RegisterPlugin(provider, pEInterface, PluginTypeEffect);
1388  ComponentInterface * pCInterface = dynamic_cast<ComponentInterface*>(pInterface);
1389  if( pCInterface )
1390  return PluginManager::Get().RegisterPlugin(provider, pCInterface);
1391  static wxString empty;
1392  return empty;
1393 }
1394 
1396  ModuleInterface *provider, ComponentInterface *pInterface )
1397 {
1398  ComponentInterface * pCInterface = dynamic_cast<ComponentInterface*>(pInterface);
1399  if( pCInterface )
1400  return PluginManager::Get().RegisterPlugin(provider, pCInterface);
1401  static wxString empty;
1402  return empty;
1403 }
1404 
1405 
1406 bool PluginManager::IsPluginRegistered(const wxString & path)
1407 {
1408  for (PluginMap::iterator iter = mPlugins.begin(); iter != mPlugins.end(); ++iter)
1409  {
1410  if (iter->second.GetPath().IsSameAs(path))
1411  {
1412  return true;
1413  }
1414  }
1415 
1416  return false;
1417 }
1418 
1420 {
1421  PluginDescriptor & plug = CreatePlugin(GetID(module), module, PluginTypeModule);
1422 
1423  plug.SetEnabled(true);
1424  plug.SetValid(true);
1425 
1426  return plug.GetID();
1427 }
1428 
1430 {
1432 
1433  plug.SetProviderID(PluginManager::GetID(provider));
1434 
1435  plug.SetEnabled(true);
1436  plug.SetValid(true);
1437 
1438  return plug.GetID();
1439 }
1440 
1442 {
1443  PluginDescriptor & plug = CreatePlugin(GetID(effect), effect, (PluginType)type);
1444 
1445  plug.SetProviderID(PluginManager::GetID(provider));
1446 
1447  plug.SetEffectType(effect->GetClassification());
1448  plug.SetEffectFamilyId(effect->GetFamilyId().Internal());
1449  plug.SetEffectInteractive(effect->IsInteractive());
1450  plug.SetEffectDefault(effect->IsDefault());
1451  plug.SetEffectRealtime(effect->SupportsRealtime());
1452  plug.SetEffectAutomatable(effect->SupportsAutomation());
1453 
1454  plug.SetEnabled(true);
1455  plug.SetValid(true);
1456 
1457  return plug.GetID();
1458 }
1459 
1461 {
1462  PluginDescriptor & plug = CreatePlugin(GetID(importer), importer, PluginTypeImporter);
1463 
1464  plug.SetProviderID(PluginManager::GetID(provider));
1465 
1466  plug.SetImporterIdentifier(importer->GetPluginStringID());
1469 
1470  return plug.GetID();
1471 }
1472 
1473 void PluginManager::FindFilesInPathList(const wxString & pattern,
1474  const wxArrayString & pathList,
1475  wxArrayString & files,
1476  bool directories)
1477 {
1478 
1479  wxLogNull nolog;
1480 
1481  // Why bother...
1482  if (pattern.IsEmpty())
1483  {
1484  return;
1485  }
1486 
1487  // TODO: We REALLY need to figure out the "Audacity" plug-in path(s)
1488 
1489  wxArrayString paths;
1490 
1491  // Add the "per-user" plug-ins directory
1492  {
1493  const wxFileName &ff = FileNames::PlugInDir();
1494  paths.Add(ff.GetFullPath());
1495  }
1496 
1497  // Add the "Audacity" plug-ins directory
1498  wxFileName ff = PlatformCompatibility::GetExecutablePath();
1499 #if defined(__WXMAC__)
1500  // Path ends for example in "Audacity.app/Contents/MacOSX"
1501  //ff.RemoveLastDir();
1502  //ff.RemoveLastDir();
1503  // just remove the MacOSX part.
1504  ff.RemoveLastDir();
1505 #endif
1506  ff.AppendDir(wxT("plug-ins"));
1507  paths.Add(ff.GetPath());
1508 
1509  // Weed out duplicates
1510  for (size_t i = 0, cnt = pathList.size(); i < cnt; i++)
1511  {
1512  ff = pathList[i];
1513  const wxString path{ ff.GetFullPath() };
1514  if (paths.Index(path, wxFileName::IsCaseSensitive()) == wxNOT_FOUND)
1515  {
1516  paths.Add(path);
1517  }
1518  }
1519 
1520  // Find all matching files in each path
1521  for (size_t i = 0, cnt = paths.GetCount(); i < cnt; i++)
1522  {
1523  ff = paths[i] + wxFILE_SEP_PATH + pattern;
1524  wxDir::GetAllFiles(ff.GetPath(), &files, ff.GetFullName(), directories ? wxDIR_DEFAULT : wxDIR_FILES);
1525  }
1526 
1527  return;
1528 }
1529 
1530 bool PluginManager::HasSharedConfigGroup(const PluginID & ID, const wxString & group)
1531 {
1532  return HasGroup(SharedGroup(ID, group));
1533 }
1534 
1535 bool PluginManager::GetSharedConfigSubgroups(const PluginID & ID, const wxString & group, wxArrayString & subgroups)
1536 {
1537  return GetSubgroups(SharedGroup(ID, group), subgroups);
1538 }
1539 
1540 bool PluginManager::GetSharedConfig(const PluginID & ID, const wxString & group, const wxString & key, wxString & value, const wxString & defval)
1541 {
1542  return GetConfig(SharedKey(ID, group, key), value, defval);
1543 }
1544 
1545 bool PluginManager::GetSharedConfig(const PluginID & ID, const wxString & group, const wxString & key, int & value, int defval)
1546 {
1547  return GetConfig(SharedKey(ID, group, key), value, defval);
1548 }
1549 
1550 bool PluginManager::GetSharedConfig(const PluginID & ID, const wxString & group, const wxString & key, bool & value, bool defval)
1551 {
1552  return GetConfig(SharedKey(ID, group, key), value, defval);
1553 }
1554 
1555 bool PluginManager::GetSharedConfig(const PluginID & ID, const wxString & group, const wxString & key, float & value, float defval)
1556 {
1557  return GetConfig(SharedKey(ID, group, key), value, defval);
1558 }
1559 
1560 bool PluginManager::GetSharedConfig(const PluginID & ID, const wxString & group, const wxString & key, double & value, double defval)
1561 {
1562  return GetConfig(SharedKey(ID, group, key), value, defval);
1563 }
1564 
1565 bool PluginManager::SetSharedConfig(const PluginID & ID, const wxString & group, const wxString & key, const wxString & value)
1566 {
1567  return SetConfig(SharedKey(ID, group, key), value);
1568 }
1569 
1570 bool PluginManager::SetSharedConfig(const PluginID & ID, const wxString & group, const wxString & key, const int & value)
1571 {
1572  return SetConfig(SharedKey(ID, group, key), value);
1573 }
1574 
1575 bool PluginManager::SetSharedConfig(const PluginID & ID, const wxString & group, const wxString & key, const bool & value)
1576 {
1577  return SetConfig(SharedKey(ID, group, key), value);
1578 }
1579 
1580 bool PluginManager::SetSharedConfig(const PluginID & ID, const wxString & group, const wxString & key, const float & value)
1581 {
1582  return SetConfig(SharedKey(ID, group, key), value);
1583 }
1584 
1585 bool PluginManager::SetSharedConfig(const PluginID & ID, const wxString & group, const wxString & key, const double & value)
1586 {
1587  return SetConfig(SharedKey(ID, group, key), value);
1588 }
1589 
1590 bool PluginManager::RemoveSharedConfigSubgroup(const PluginID & ID, const wxString & group)
1591 {
1592  bool result = GetSettings()->DeleteGroup(SharedGroup(ID, group));
1593  if (result)
1594  {
1595  GetSettings()->Flush();
1596  }
1597 
1598  return result;
1599 }
1600 
1601 bool PluginManager::RemoveSharedConfig(const PluginID & ID, const wxString & group, const wxString & key)
1602 {
1603  bool result = GetSettings()->DeleteEntry(SharedKey(ID, group, key));
1604  if (result)
1605  {
1606  GetSettings()->Flush();
1607  }
1608 
1609  return result;
1610 }
1611 
1612 bool PluginManager::HasPrivateConfigGroup(const PluginID & ID, const wxString & group)
1613 {
1614  return HasGroup(PrivateGroup(ID, group));
1615 }
1616 
1617 bool PluginManager::GetPrivateConfigSubgroups(const PluginID & ID, const wxString & group, wxArrayString & subgroups)
1618 {
1619  return GetSubgroups(PrivateGroup(ID, group), subgroups);
1620 }
1621 
1622 bool PluginManager::GetPrivateConfig(const PluginID & ID, const wxString & group, const wxString & key, wxString & value, const wxString & defval)
1623 {
1624  return GetConfig(PrivateKey(ID, group, key), value, defval);
1625 }
1626 
1627 bool PluginManager::GetPrivateConfig(const PluginID & ID, const wxString & group, const wxString & key, int & value, int defval)
1628 {
1629  return GetConfig(PrivateKey(ID, group, key), value, defval);
1630 }
1631 
1632 bool PluginManager::GetPrivateConfig(const PluginID & ID, const wxString & group, const wxString & key, bool & value, bool defval)
1633 {
1634  return GetConfig(PrivateKey(ID, group, key), value, defval);
1635 }
1636 
1637 bool PluginManager::GetPrivateConfig(const PluginID & ID, const wxString & group, const wxString & key, float & value, float defval)
1638 {
1639  return GetConfig(PrivateKey(ID, group, key), value, defval);
1640 }
1641 
1642 bool PluginManager::GetPrivateConfig(const PluginID & ID, const wxString & group, const wxString & key, double & value, double defval)
1643 {
1644  return GetConfig(PrivateKey(ID, group, key), value, defval);
1645 }
1646 
1647 bool PluginManager::SetPrivateConfig(const PluginID & ID, const wxString & group, const wxString & key, const wxString & value)
1648 {
1649  return SetConfig(PrivateKey(ID, group, key), value);
1650 }
1651 
1652 bool PluginManager::SetPrivateConfig(const PluginID & ID, const wxString & group, const wxString & key, const int & value)
1653 {
1654  return SetConfig(PrivateKey(ID, group, key), value);
1655 }
1656 
1657 bool PluginManager::SetPrivateConfig(const PluginID & ID, const wxString & group, const wxString & key, const bool & value)
1658 {
1659  return SetConfig(PrivateKey(ID, group, key), value);
1660 }
1661 
1662 bool PluginManager::SetPrivateConfig(const PluginID & ID, const wxString & group, const wxString & key, const float & value)
1663 {
1664  return SetConfig(PrivateKey(ID, group, key), value);
1665 }
1666 
1667 bool PluginManager::SetPrivateConfig(const PluginID & ID, const wxString & group, const wxString & key, const double & value)
1668 {
1669  return SetConfig(PrivateKey(ID, group, key), value);
1670 }
1671 
1672 bool PluginManager::RemovePrivateConfigSubgroup(const PluginID & ID, const wxString & group)
1673 {
1674  bool result = GetSettings()->DeleteGroup(PrivateGroup(ID, group));
1675  if (result)
1676  {
1677  GetSettings()->Flush();
1678  }
1679 
1680  return result;
1681 }
1682 
1683 bool PluginManager::RemovePrivateConfig(const PluginID & ID, const wxString & group, const wxString & key)
1684 {
1685  bool result = GetSettings()->DeleteEntry(PrivateKey(ID, group, key));
1686  if (result)
1687  {
1688  GetSettings()->Flush();
1689  }
1690 
1691  return result;
1692 }
1693 
1694 // ============================================================================
1695 //
1696 // PluginManager
1697 //
1698 // ============================================================================
1699 
1700 // The one and only PluginManager
1701 std::unique_ptr<PluginManager> PluginManager::mInstance{};
1702 
1703 // ----------------------------------------------------------------------------
1704 // Creation/Destruction
1705 // ----------------------------------------------------------------------------
1706 
1708 {
1709  mSettings = NULL;
1710 }
1711 
1713 {
1714  // Ensure termination (harmless if already done)
1715  Terminate();
1716 }
1717 
1718 // ----------------------------------------------------------------------------
1719 // PluginManager implementation
1720 // ----------------------------------------------------------------------------
1721 
1722 // ============================================================================
1723 //
1724 // Return reference to singleton
1725 //
1726 // (Thread-safe...no active threading during construction or after destruction)
1727 // ============================================================================
1728 
1730 {
1731  if (!mInstance)
1732  {
1734  }
1735 
1736  return *mInstance;
1737 }
1738 
1740 {
1741  // Always load the registry first
1742  Load();
1743 
1744  // Then look for providers (they may autoregister plugins)
1746 
1747  // And finally check for updates
1748 #ifndef EXPERIMENTAL_EFFECT_MANAGEMENT
1749  CheckForUpdates();
1750 #else
1751  const bool kFast = true;
1752  CheckForUpdates( kFast );
1753 #endif
1754 }
1755 
1757 {
1758  // Get rid of all non-module plugins first
1759  PluginMap::iterator iter = mPlugins.begin();
1760  while (iter != mPlugins.end())
1761  {
1762  PluginDescriptor & plug = iter->second;
1763  if (plug.GetPluginType() == PluginTypeEffect)
1764  {
1765  mPlugins.erase(iter++);
1766  continue;
1767  }
1768 
1769  ++iter;
1770  }
1771 
1772  // Now get rid of the modules
1773  iter = mPlugins.begin();
1774  while (iter != mPlugins.end())
1775  {
1776  mPlugins.erase(iter++);
1777  }
1778 }
1779 
1780 bool PluginManager::DropFile(const wxString &fileName)
1781 {
1782  auto &mm = ModuleManager::Get();
1783  const wxFileName src{ fileName };
1784 
1785  for (const PluginDescriptor *plug = GetFirstPlugin(PluginTypeModule);
1786  plug;
1788  {
1789  auto module = static_cast<ModuleInterface *>
1790  (mm.CreateProviderInstance(plug->GetID(), plug->GetPath()));
1791  if (! module)
1792  continue;
1793 
1794  const auto &ff = module->InstallPath();
1795  auto extensions = module->FileExtensions();
1796  if (!ff.empty() &&
1797  make_iterator_range(extensions).contains(src.GetExt())) {
1798  wxString errMsg;
1799  // Do dry-run test of the file format
1800  unsigned nPlugIns =
1801  module->DiscoverPluginsAtPath(fileName, errMsg, {});
1802  if (nPlugIns) {
1803  // File contents are good for this module, so check no others.
1804  // All branches of this block return true, even in case of
1805  // failure for other reasons, to signal that other drag-and-drop
1806  // actions should not be tried.
1807 
1808  // Find path to copy it
1809  wxFileName dst;
1810  dst.AssignDir( ff );
1811  dst.SetFullName( src.GetFullName() );
1812  if ( dst.Exists() ) {
1813  // Query whether to overwrite
1814  bool overwrite = (wxYES == ::AudacityMessageBox(
1815  wxString::Format(_("Overwrite the plug-in file %s?"),
1816  dst.GetFullPath() ),
1817  _("Plug-in already exists"),
1818  wxYES_NO
1819  ) );
1820  if ( !overwrite )
1821  return true;
1822  }
1823 
1824  // Move the file or subtree
1825  bool copied = false;
1826  auto dstPath = dst.GetFullPath();
1827  if ( src.FileExists() )
1828  // A simple one-file plug-in
1829  copied = FileNames::CopyFile(
1830  src.GetFullPath(), dstPath, true );
1831  else {
1832  // A sub-folder
1833  // such as for some VST packages
1834  // Recursive copy needed -- to do
1835  return true;
1836  }
1837 
1838  if (!copied) {
1840  _("Plug-in file is in use. Failed to overwrite"));
1841  return true;
1842  }
1843 
1844  // Register for real
1845  std::vector<PluginID> ids;
1846  std::vector<wxString> names;
1847  nPlugIns = module->DiscoverPluginsAtPath(dstPath, errMsg,
1848  [&](ModuleInterface *provider, ComponentInterface *ident)
1849  -> const PluginID& {
1850  // Register as by default, but also collecting the PluginIDs
1851  // and names
1853  provider, ident);
1854  ids.push_back(id);
1855  names.push_back( ident->GetSymbol().Translation() );
1856  return id;
1857  });
1858  if ( ! nPlugIns ) {
1859  // Unlikely after the dry run succeeded
1860  ::AudacityMessageBox( wxString::Format(
1861  _("Failed to register:\n%s"), errMsg ) );
1862  return true;
1863  }
1864 
1865  // Ask whether to enable the plug-ins
1866  if (auto nIds = ids.size()) {
1867  auto message = wxPLURAL( "Enable this plug-in?", "Enable these plug-ins?", nIds );
1868  message += wxT("\n");
1869  for (const auto &name : names)
1870  message += name + wxT("\n");
1871  bool enable = (wxYES == ::AudacityMessageBox(
1872  message,
1873  _("Enable new plug-ins"),
1874  wxYES_NO
1875  ) );
1876  for (const auto &id : ids)
1877  mPlugins[id].SetEnabled(enable);
1878  // Make changes to enabled status persist:
1879  this->Save();
1880  }
1881 
1882  return true;
1883  }
1884  }
1885  }
1886 
1887  return false;
1888 }
1889 
1891 {
1892  // Create/Open the registry
1893  wxFileConfig registry(wxEmptyString, wxEmptyString, FileNames::PluginRegistry());
1894 
1895  // If this group doesn't exist then we have something that's not a registry.
1896  // We should probably warn the user, but it's pretty unlikely that this will happen.
1897  if (!registry.HasGroup(REGROOT))
1898  {
1899  // Must start over
1900  registry.DeleteAll();
1901  return;
1902  }
1903 
1904  // Check for a registry version that we can understand
1905  // TODO: Should also check for a registry file that is newer than
1906  // what we can understand.
1907  wxString regver = registry.Read(REGVERKEY);
1908  if (regver < REGVERCUR )
1909  {
1910  // Conversion code here, for when registry version changes.
1911 
1912  // We iterate through the effects, possibly updating their info.
1913  wxString groupName;
1914  long groupIndex;
1915  wxString group = GetPluginTypeString(PluginTypeEffect);
1916  wxString cfgPath = REGROOT + group + wxCONFIG_PATH_SEPARATOR;
1917  wxArrayString groupsToDelete;
1918 
1919  registry.SetPath(cfgPath);
1920  for (bool cont = registry.GetFirstGroup(groupName, groupIndex);
1921  cont;
1922  registry.SetPath(cfgPath),
1923  cont = registry.GetNextGroup(groupName, groupIndex))
1924  {
1925  registry.SetPath(groupName);
1926  wxString effectSymbol = registry.Read(KEY_SYMBOL, "");
1927  wxString effectVersion = registry.Read(KEY_VERSION, "");
1928 
1929 
1930  // For 2.3.0 the plugins we distribute have moved around.
1931  // So we upped the registry version number to 1.1.
1932  // These particular config edits were originally written to fix Bug 1914.
1933  if (regver <= "1.0") {
1934  // Nyquist prompt is a built-in that has moved to the tools menu.
1935  if (effectSymbol == "Nyquist Prompt") {
1936  registry.Write(KEY_EFFECTTYPE, "Tool");
1937  // Old version of SDE was in Analyze menu. Now it is in Tools.
1938  // We don't want both the old and the new.
1939  } else if ((effectSymbol == "Sample Data Export") && (effectVersion == "n/a")) {
1940  groupsToDelete.push_back(cfgPath + groupName);
1941  // Old version of SDI was in Generate menu. Now it is in Tools.
1942  } else if ((effectSymbol == "Sample Data Import") && (effectVersion == "n/a")) {
1943  groupsToDelete.push_back(cfgPath + groupName);
1944  }
1945  }
1946 
1947  }
1948  // Doing the deletion within the search loop risked skipping some items,
1949  // hence the delayed delete.
1950  for (unsigned int i = 0; i < groupsToDelete.Count(); i++) {
1951  registry.DeleteGroup(groupsToDelete[i]);
1952  }
1953  registry.SetPath("");
1954  registry.Write(REGVERKEY, REGVERCUR);
1955  // Updates done. Make sure we read the updated data later.
1956  registry.Flush();
1957  }
1958 
1959  // Load all provider plugins first
1960  LoadGroup(&registry, PluginTypeModule);
1961 
1962  // Now the rest
1963  LoadGroup(&registry, PluginTypeEffect);
1964  LoadGroup(&registry, PluginTypeAudacityCommand );
1965  LoadGroup(&registry, PluginTypeExporter);
1966  LoadGroup(&registry, PluginTypeImporter);
1967 
1968  LoadGroup(&registry, PluginTypeStub);
1969  return;
1970 }
1971 
1972 void PluginManager::LoadGroup(wxFileConfig *pRegistry, PluginType type)
1973 {
1974 #ifdef __WXMAC__
1975  // Bug 1590: On Mac, we should purge the registry of Nyquist plug-ins
1976  // bundled with other versions of Audacity, assuming both versions
1977  // were properly installed in /Applications (or whatever it is called in
1978  // your locale)
1979 
1980  const auto fullExePath = PlatformCompatibility::GetExecutablePath();
1981 
1982  // Strip rightmost path components up to *.app
1983  wxFileName exeFn{ fullExePath };
1984  exeFn.SetEmptyExt();
1985  exeFn.SetName(wxString{});
1986  while(exeFn.GetDirCount() && !exeFn.GetDirs().Last().EndsWith(".app"))
1987  exeFn.RemoveLastDir();
1988 
1989  const auto goodPath = exeFn.GetPath();
1990 
1991  if(exeFn.GetDirCount())
1992  exeFn.RemoveLastDir();
1993  const auto possiblyBadPath = exeFn.GetPath();
1994 
1995  auto AcceptPath = [&](const wxString &path) {
1996  if (!path.StartsWith(possiblyBadPath))
1997  // Assume it's not under /Applications
1998  return true;
1999  if (path.StartsWith(goodPath))
2000  // It's bundled with this executable
2001  return true;
2002  return false;
2003  };
2004 #else
2005  auto AcceptPath = [](const wxString&){ return true; };
2006 #endif
2007 
2008  wxString strVal;
2009  bool boolVal;
2010  wxString groupName;
2011  long groupIndex;
2012  wxString group = GetPluginTypeString(type);
2013  wxString cfgPath = REGROOT + group + wxCONFIG_PATH_SEPARATOR;
2014 
2015  pRegistry->SetPath(cfgPath);
2016  for (bool cont = pRegistry->GetFirstGroup(groupName, groupIndex);
2017  cont;
2018  pRegistry->SetPath(cfgPath),
2019  cont = pRegistry->GetNextGroup(groupName, groupIndex))
2020  {
2021  PluginDescriptor plug;
2022 
2023  pRegistry->SetPath(groupName);
2024 
2025  groupName = ConvertID(groupName);
2026 
2027  // Bypass group if the ID is already in use
2028  if (mPlugins.find(groupName) != mPlugins.end())
2029  {
2030  pRegistry->SetPath(wxT(".."));
2031 
2032  continue;
2033  }
2034 
2035  // Set the ID and type
2036  plug.SetID(groupName);
2037  plug.SetPluginType(type);
2038 
2039  // Get the provider ID and bypass group if not found
2040  if (!pRegistry->Read(KEY_PROVIDERID, &strVal, wxEmptyString))
2041  {
2042  // Bypass group if the provider isn't valid
2043  if (!strVal.IsEmpty() && mPlugins.find(strVal) == mPlugins.end())
2044  {
2045  continue;
2046  }
2047  }
2048  plug.SetProviderID(PluginID(strVal));
2049 
2050  // Get the path (optional)
2051  pRegistry->Read(KEY_PATH, &strVal, wxEmptyString);
2052  if (!AcceptPath(strVal))
2053  // Ignore the obsolete path in the config file, during session,
2054  // but don't remove it from the file. Maybe you really want to
2055  // switch back to the other version of Audacity and lose nothing.
2056  continue;
2057  plug.SetPath(strVal);
2058 
2059  /*
2060  // PRL: Ignore names written in configs before 2.3.0!
2061  // use Internal string only! Let the present version of Audacity map
2062  // that to a user-visible string.
2063  // Get the name and bypass group if not found
2064  if (!pRegistry->Read(KEY_NAME, &strVal))
2065  {
2066  continue;
2067  }
2068  plug.SetName(strVal);
2069  */
2070 
2071  // Get the symbol...Audacity 2.3.0 or later requires it
2072  // bypass group if not found
2073  // Note, KEY_SYMBOL started getting written to config files in 2.1.0.
2074  // KEY_NAME (now ignored) was written before that, but only for VST
2075  // effects.
2076  if (!pRegistry->Read(KEY_SYMBOL, &strVal))
2077  continue;
2078  plug.SetSymbol(strVal);
2079 
2080  // Get the version and bypass group if not found
2081  if (!pRegistry->Read(KEY_VERSION, &strVal))
2082  {
2083  continue;
2084  }
2085  plug.SetVersion(strVal);
2086 
2087  // Get the vendor and bypass group if not found
2088  if (!pRegistry->Read(KEY_VENDOR, &strVal))
2089  {
2090  continue;
2091  }
2092  plug.SetVendor( strVal );
2093 
2094 #if 0
2095  // This was done before version 2.2.2, but the value was not really used
2096  // But absence of a value will cause early versions to skip the group
2097  // Therefore we still write a blank to keep pluginregistry.cfg
2098  // backwards-compatible
2099 
2100  // Get the description and bypass group if not found
2101  if (!pRegistry->Read(KEY_DESCRIPTION, &strVal))
2102  {
2103  continue;
2104  }
2105 #endif
2106 
2107  // Is it enabled...default to no if not found
2108  pRegistry->Read(KEY_ENABLED, &boolVal, false);
2109  plug.SetEnabled(boolVal);
2110 
2111  // Is it valid...default to no if not found
2112  pRegistry->Read(KEY_VALID, &boolVal, false);
2113  plug.SetValid(boolVal);
2114 
2115  switch (type)
2116  {
2117  case PluginTypeModule:
2118  {
2119  // Nothing to do here yet
2120  }
2121  break;
2122 
2123  case PluginTypeEffect:
2124  {
2125  // Get the effect type and bypass group if not found
2126  if (!pRegistry->Read(KEY_EFFECTTYPE, &strVal))
2127  continue;
2128 
2129  if (strVal.IsSameAs(KEY_EFFECTTYPE_NONE))
2131  else if (strVal.IsSameAs(KEY_EFFECTTYPE_ANALYZE))
2133  else if (strVal.IsSameAs(KEY_EFFECTTYPE_GENERATE))
2135  else if (strVal.IsSameAs(KEY_EFFECTTYPE_PROCESS))
2137  else if (strVal.IsSameAs(KEY_EFFECTTYPE_TOOL))
2139  else if (strVal.IsSameAs(KEY_EFFECTTYPE_HIDDEN))
2141  else
2142  continue;
2143 
2144  // Get the effect family and bypass group if not found
2145  if (!pRegistry->Read(KEY_EFFECTFAMILY, &strVal))
2146  {
2147  continue;
2148  }
2149  plug.SetEffectFamilyId(strVal);
2150 
2151  // Is it a default (above the line) effect and bypass group if not found
2152  if (!pRegistry->Read(KEY_EFFECTDEFAULT, &boolVal))
2153  {
2154  continue;
2155  }
2156  plug.SetEffectDefault(boolVal);
2157 
2158  // Is it an interactive effect and bypass group if not found
2159  if (!pRegistry->Read(KEY_EFFECTINTERACTIVE, &boolVal))
2160  {
2161  continue;
2162  }
2163  plug.SetEffectInteractive(boolVal);
2164 
2165  // Is it a realtime capable effect and bypass group if not found
2166  if (!pRegistry->Read(KEY_EFFECTREALTIME, &boolVal))
2167  {
2168  continue;
2169  }
2170  plug.SetEffectRealtime(boolVal);
2171 
2172  // Does the effect support automation...bypass group if not found
2173  if (!pRegistry->Read(KEY_EFFECTAUTOMATABLE, &boolVal))
2174  {
2175  continue;
2176  }
2177  plug.SetEffectAutomatable(boolVal);
2178  }
2179  break;
2180 
2181  case PluginTypeImporter:
2182  {
2183  // Get the importer identifier and bypass group if not found
2184  if (!pRegistry->Read(KEY_IMPORTERIDENT, &strVal))
2185  {
2186  continue;
2187  }
2188  plug.SetImporterIdentifier(strVal);
2189 
2190  // Get the importer filter description and bypass group if not found
2191  if (!pRegistry->Read(KEY_IMPORTERFILTER, &strVal))
2192  {
2193  continue;
2194  }
2195  plug.SetImporterFilterDescription(strVal);
2196 
2197  // Get the importer extensions and bypass group if not found
2198  if (!pRegistry->Read(KEY_IMPORTEREXTENSIONS, &strVal))
2199  {
2200  continue;
2201  }
2202  wxArrayString extensions;
2203  wxStringTokenizer tkr(strVal, wxT(":"));
2204  while (tkr.HasMoreTokens())
2205  {
2206  extensions.Add(tkr.GetNextToken());
2207  }
2208  plug.SetImporterExtensions(extensions);
2209  }
2210  break;
2211 
2212  case PluginTypeStub:
2213  {
2214  // Nothing additional for stubs
2215  }
2216  break;
2217 
2218  // Not used by 2.1.1 or greater and should be removed after a few releases past 2.1.0.
2219  case PluginTypeNone:
2220  {
2221  // Used for stub groups
2222  }
2223  break;
2224 
2225  default:
2226  {
2227  continue;
2228  }
2229  }
2230 
2231  // Everything checked out...accept the plugin
2232  mPlugins[groupName] = plug;
2233  }
2234 
2235  return;
2236 }
2237 
2239 {
2240  // Create/Open the registry
2241  wxFileConfig registry(wxEmptyString, wxEmptyString, FileNames::PluginRegistry());
2242 
2243  // Clear it out
2244  registry.DeleteAll();
2245 
2246  // Write the version string
2247  registry.Write(REGVERKEY, REGVERCUR);
2248 
2249  // Save the individual groups
2250  SaveGroup(&registry, PluginTypeEffect);
2251  SaveGroup(&registry, PluginTypeExporter);
2253  SaveGroup(&registry, PluginTypeImporter);
2254  SaveGroup(&registry, PluginTypeStub);
2255 
2256  // Not used by 2.1.1 or greater, but must save to allow users to switch between 2.1.0
2257  // and 2.1.1+. This should be removed after a few releases past 2.1.0.
2258  //SaveGroup(&registry, PluginTypeNone);
2259 
2260  // And now the providers
2261  SaveGroup(&registry, PluginTypeModule);
2262 
2263  // Just to be safe
2264  registry.Flush();
2265 }
2266 
2267 void PluginManager::SaveGroup(wxFileConfig *pRegistry, PluginType type)
2268 {
2269  wxString group = GetPluginTypeString(type);
2270  for (PluginMap::iterator iter = mPlugins.begin(); iter != mPlugins.end(); ++iter)
2271  {
2272  PluginDescriptor & plug = iter->second;
2273 
2274  if (plug.GetPluginType() != type)
2275  {
2276  continue;
2277  }
2278 
2279  pRegistry->SetPath(REGROOT + group + wxCONFIG_PATH_SEPARATOR + ConvertID(plug.GetID()));
2280 
2281  pRegistry->Write(KEY_PATH, plug.GetPath());
2282  pRegistry->Write(KEY_SYMBOL, plug.GetSymbol().Internal());
2283 
2284  // PRL: Writing KEY_NAME which is no longer read, but older Audacity
2285  // versions expect to find it.
2286  pRegistry->Write(KEY_NAME, plug.GetSymbol().Msgid());
2287 
2288  pRegistry->Write(KEY_VERSION, plug.GetUntranslatedVersion());
2289  pRegistry->Write(KEY_VENDOR, plug.GetVendor());
2290  // Write a blank -- see comments in LoadGroup:
2291  pRegistry->Write(KEY_DESCRIPTION, wxString{});
2292  pRegistry->Write(KEY_PROVIDERID, plug.GetProviderID());
2293  pRegistry->Write(KEY_ENABLED, plug.IsEnabled());
2294  pRegistry->Write(KEY_VALID, plug.IsValid());
2295 
2296  switch (type)
2297  {
2298  case PluginTypeModule:
2299  break;
2300 
2301  case PluginTypeEffect:
2302  {
2303  EffectType etype = plug.GetEffectType();
2304  wxString stype;
2305  if (etype == EffectTypeNone)
2306  stype = KEY_EFFECTTYPE_NONE;
2307  else if (etype == EffectTypeAnalyze)
2308  stype = KEY_EFFECTTYPE_ANALYZE;
2309  else if (etype == EffectTypeGenerate)
2310  stype = KEY_EFFECTTYPE_GENERATE;
2311  else if (etype == EffectTypeProcess)
2312  stype = KEY_EFFECTTYPE_PROCESS;
2313  else if (etype == EffectTypeTool)
2314  stype = KEY_EFFECTTYPE_TOOL;
2315  else if (etype == EffectTypeHidden)
2316  stype = KEY_EFFECTTYPE_HIDDEN;
2317 
2318  pRegistry->Write(KEY_EFFECTTYPE, stype);
2319  pRegistry->Write(KEY_EFFECTFAMILY, plug.GetEffectFamilyId());
2320  pRegistry->Write(KEY_EFFECTDEFAULT, plug.IsEffectDefault());
2321  pRegistry->Write(KEY_EFFECTINTERACTIVE, plug.IsEffectInteractive());
2322  pRegistry->Write(KEY_EFFECTREALTIME, plug.IsEffectRealtime());
2323  pRegistry->Write(KEY_EFFECTAUTOMATABLE, plug.IsEffectAutomatable());
2324  }
2325  break;
2326 
2327  case PluginTypeImporter:
2328  {
2329  pRegistry->Write(KEY_IMPORTERIDENT, plug.GetImporterIdentifier());
2330  pRegistry->Write(KEY_IMPORTERFILTER, plug.GetImporterFilterDescription());
2331  const wxArrayString & extensions = plug.GetImporterExtensions();
2332  wxString strExt;
2333  for (size_t i = 0, cnt = extensions.size(); i < cnt; i++)
2334  {
2335  strExt += extensions[i] + wxT(":");
2336  }
2337  strExt.RemoveLast(1);
2338  pRegistry->Write(KEY_IMPORTEREXTENSIONS, strExt);
2339  }
2340  break;
2341 
2342  default:
2343  break;
2344  }
2345  }
2346 
2347  return;
2348 }
2349 
2350 // If bFast is true, do not do a full check. Just check the ones
2351 // that are quick to check. Currently (Feb 2017) just Nyquist
2352 // and built-ins.
2354 {
2355  // Get ModuleManager reference
2357 
2358  wxArrayString pathIndex;
2359  for (PluginMap::iterator iter = mPlugins.begin(); iter != mPlugins.end(); ++iter)
2360  {
2361  PluginDescriptor & plug = iter->second;
2362 
2363  // Bypass 2.1.0 placeholders...remove this after a few releases past 2.1.0
2364  if (plug.GetPluginType() == PluginTypeNone)
2365  {
2366  continue;
2367  }
2368 
2369  pathIndex.Add(plug.GetPath().BeforeFirst(wxT(';')));
2370  }
2371 
2372  // Check all known plugins to ensure they are still valid and scan for NEW ones.
2373  //
2374  // All NEW plugins get a stub entry created that will remain in place until the
2375  // user enables or disables the plugin.
2376  //
2377  // Becuase we use the plugins "path" as returned by the providers, we can actually
2378  // have multiple providers report the same path since, at this point, they only
2379  // know that the path might possibly be one supported by the provider.
2380  //
2381  // When the user enables the plugin, each provider that reported it will be asked
2382  // to register the plugin.
2383  for (PluginMap::iterator iter = mPlugins.begin(); iter != mPlugins.end(); ++iter)
2384  {
2385  PluginDescriptor & plug = iter->second;
2386  const PluginID & plugID = plug.GetID();
2387  const wxString & plugPath = plug.GetPath();
2388  PluginType plugType = plug.GetPluginType();
2389 
2390  // Bypass 2.1.0 placeholders...remove this after a few releases past 2.1.0
2391  if (plugType == PluginTypeNone)
2392  {
2393  continue;
2394  }
2395 
2396  if ( plugType == PluginTypeModule )
2397  {
2398  if( bFast )
2399  {
2400  // Skip modules, when doing a fast refresh/check.
2401  }
2402  else if (!mm.IsProviderValid(plugID, plugPath))
2403  {
2404  plug.SetEnabled(false);
2405  plug.SetValid(false);
2406  }
2407  else
2408  {
2409  // Collect plugin paths
2410  wxArrayString paths = mm.FindPluginsForProvider(plugID, plugPath);
2411  for (size_t i = 0, cnt = paths.GetCount(); i < cnt; i++)
2412  {
2413  wxString path = paths[i].BeforeFirst(wxT(';'));;
2414  if (pathIndex.Index(path) == wxNOT_FOUND)
2415  {
2416  PluginID ID = plugID + wxT("_") + path;
2417  PluginDescriptor & plug2 = mPlugins[ID]; // This will create a NEW descriptor
2419  plug2.SetID(ID);
2420  plug2.SetProviderID(plugID);
2421  plug2.SetPath(path);
2422  plug2.SetEnabled(false);
2423  plug2.SetValid(false);
2424  }
2425  }
2426  }
2427  }
2428  else if (plugType != PluginTypeNone && plugType != PluginTypeStub)
2429  {
2430  plug.SetValid(mm.IsPluginValid(plug.GetProviderID(), plugPath, bFast));
2431  if (!plug.IsValid())
2432  {
2433  plug.SetEnabled(false);
2434  }
2435  }
2436  }
2437 
2438  Save();
2439 
2440  return;
2441 }
2442 
2443 bool PluginManager::ShowManager(wxWindow *parent, EffectType type)
2444 {
2445  CheckForUpdates();
2446 
2447  PluginRegistrationDialog dlg(parent, type);
2448  return dlg.ShowModal() == wxID_OK;
2449 }
2450 
2451 // Here solely for the purpose of Nyquist Workbench until
2452 // a better solution is devised.
2454 {
2455  PluginDescriptor & plug = CreatePlugin(GetID(effect), effect, type);
2456 
2457  plug.SetEffectType(effect->GetType());
2458  plug.SetEffectFamilyId(effect->GetFamilyId().Internal());
2459  plug.SetEffectInteractive(effect->IsInteractive());
2460  plug.SetEffectDefault(effect->IsDefault());
2461  plug.SetEffectRealtime(effect->SupportsRealtime());
2462  plug.SetEffectAutomatable(effect->SupportsAutomation());
2463 
2464  plug.SetInstance(effect);
2465  plug.SetEffectLegacy(true);
2466  plug.SetEnabled(true);
2467  plug.SetValid(true);
2468 
2469  return plug.GetID();
2470 }
2471 
2472 // Here solely for the purpose of Nyquist Workbench until
2473 // a better solution is devised.
2475 {
2476  if (mPlugins.find(ID) == mPlugins.end())
2477  {
2478  return;
2479  }
2480 
2481  mPlugins.erase(ID);
2482 }
2483 
2485 {
2486  int num = 0;
2487 
2488  for (PluginMap::iterator iter = mPlugins.begin(); iter != mPlugins.end(); ++iter)
2489  {
2490  if (iter->second.GetPluginType() == type)
2491  {
2492  num++;
2493  }
2494  }
2495 
2496  return num;
2497 }
2498 
2500 {
2501  if (mPlugins.find(ID) == mPlugins.end())
2502  {
2503  return NULL;
2504  }
2505 
2506  return &mPlugins[ID];
2507 }
2508 
2510 {
2511  for (mPluginsIter = mPlugins.begin(); mPluginsIter != mPlugins.end(); ++mPluginsIter)
2512  {
2513  PluginDescriptor & plug = mPluginsIter->second;
2514  PluginType plugType = plug.GetPluginType();
2515  if( plug.IsValid() && plug.IsEnabled() && ((plugType & type) != 0))
2516  {
2517  bool familyEnabled = true;
2518  if( (plugType & PluginTypeEffect) != 0)
2519  // This preference may be written by EffectsPrefs
2520  gPrefs->Read(plug.GetEffectFamilyId() + wxT("/Enable"), &familyEnabled, true);
2521  if (familyEnabled)
2522  return &mPluginsIter->second;
2523  }
2524  }
2525 
2526  return NULL;
2527 }
2528 
2530 {
2531  while (++mPluginsIter != mPlugins.end())
2532  {
2533  PluginDescriptor & plug = mPluginsIter->second;
2534  PluginType plugType = plug.GetPluginType();
2535  if( plug.IsValid() && plug.IsEnabled() && ((plugType & type) != 0))
2536  {
2537  bool familyEnabled = true;
2538  if( (plugType & PluginTypeEffect) != 0)
2539  // This preference may be written by EffectsPrefs
2540  gPrefs->Read(plug.GetEffectFamilyId() + wxT("/Enable"), &familyEnabled, true);
2541  if (familyEnabled)
2542  return &mPluginsIter->second;
2543  }
2544  }
2545 
2546  return NULL;
2547 }
2548 
2550 {
2552 
2553  for (mPluginsIter = mPlugins.begin(); mPluginsIter != mPlugins.end(); ++mPluginsIter)
2554  {
2555  PluginDescriptor & plug = mPluginsIter->second;
2556 
2557  bool familyEnabled;
2558  // This preference may be written by EffectsPrefs
2559  gPrefs->Read(plug.GetEffectFamilyId() + wxT("/Enable"), &familyEnabled, true);
2560  if (plug.IsValid() && plug.IsEnabled() && plug.GetEffectType() == type && familyEnabled)
2561  {
2562  if (plug.IsInstantiated() && em.IsHidden(plug.GetID()))
2563  {
2564  continue;
2565  }
2566 
2567  return &plug;
2568  }
2569  }
2570 
2571  return NULL;
2572 }
2573 
2575 {
2577 
2578  while (++mPluginsIter != mPlugins.end())
2579  {
2580  PluginDescriptor & plug = mPluginsIter->second;
2581  bool familyEnabled;
2582  // This preference may be written by EffectsPrefs
2583  gPrefs->Read(plug.GetEffectFamilyId() + wxT("/Enable"), &familyEnabled, true);
2584  if (plug.IsValid() && plug.IsEnabled() && plug.GetEffectType() == type && familyEnabled)
2585  {
2586  if (plug.IsInstantiated() && em.IsHidden(plug.GetID()))
2587  {
2588  continue;
2589  }
2590 
2591  return &plug;
2592  }
2593  }
2594 
2595  return NULL;
2596 }
2597 
2599 {
2600  if (mPlugins.find(ID) == mPlugins.end())
2601  {
2602  return false;
2603  }
2604 
2605  return mPlugins[ID].IsEnabled();
2606 }
2607 
2608 void PluginManager::EnablePlugin(const PluginID & ID, bool enable)
2609 {
2610  if (mPlugins.find(ID) == mPlugins.end())
2611  {
2612  return;
2613  }
2614 
2615  return mPlugins[ID].SetEnabled(enable);
2616 }
2617 
2619 {
2620  if (mPlugins.find(ID) == mPlugins.end())
2621  {
2622  static ComponentInterfaceSymbol empty;
2623  return empty;
2624  }
2625 
2626  return mPlugins[ID].GetSymbol();
2627 }
2628 
2630 {
2631  if (mPlugins.find(ID) == mPlugins.end())
2632  {
2633  return NULL;
2634  }
2635 
2636  PluginDescriptor & plug = mPlugins[ID];
2637 
2638  // If not dealing with legacy effects, make sure the provider is loaded
2639  if (!plug.IsEffectLegacy())
2640  {
2641  const PluginID & prov = plug.GetProviderID();
2642  if (mPlugins.find(prov) == mPlugins.end())
2643  {
2644  return NULL;
2645  }
2646  mPlugins[prov].GetInstance();
2647  }
2648 
2649  return plug.GetInstance();
2650 }
2651 
2653 {
2654  return wxString::Format(wxT("%s_%s_%s_%s_%s"),
2656  wxEmptyString,
2657  module->GetVendor().Internal(),
2658  module->GetSymbol().Internal(),
2659  module->GetPath());
2660 }
2661 
2663 {
2664  return wxString::Format(wxT("%s_%s_%s_%s_%s"),
2666  wxEmptyString,
2667  command->GetVendor().Internal(),
2668  command->GetSymbol().Internal(),
2669  command->GetPath());
2670 }
2671 
2673 {
2674  return wxString::Format(wxT("%s_%s_%s_%s_%s"),
2676  effect->GetFamilyId().Internal(),
2677  effect->GetVendor().Internal(),
2678  effect->GetSymbol().Internal(),
2679  effect->GetPath());
2680 }
2681 
2683 {
2684  return wxString::Format(wxT("%s_%s_%s_%s_%s"),
2686  wxEmptyString,
2687  importer->GetVendor().Internal(),
2688  importer->GetSymbol().Internal(),
2689  importer->GetPath());
2690 }
2691 
2692 // This string persists in configuration files
2693 // So config compatibility will break if it is changed across Audacity versions
2695 {
2696  wxString str;
2697 
2698  switch (type)
2699  {
2700  default:
2701  case PluginTypeNone:
2702  str = wxT("Placeholder");
2703  break;
2704  case PluginTypeStub:
2705  str = wxT("Stub");
2706  break;
2707  case PluginTypeEffect:
2708  str = wxT("Effect");
2709  break;
2711  str = wxT("Generic");
2712  break;
2713  case PluginTypeExporter:
2714  str = wxT("Exporter");
2715  break;
2716  case PluginTypeImporter:
2717  str = wxT("Importer");
2718  break;
2719  case PluginTypeModule:
2720  str = wxT("Module");
2721  break;
2722  }
2723 
2724  return str;
2725 }
2726 
2728  ComponentInterface *ident,
2729  PluginType type)
2730 {
2731  // This will either create a NEW entry or replace an existing entry
2732  PluginDescriptor & plug = mPlugins[id];
2733 
2734  plug.SetPluginType(type);
2735 
2736  plug.SetID(id);
2737  plug.SetPath(ident->GetPath());
2738  plug.SetSymbol(ident->GetSymbol());
2739  plug.SetVendor(ident->GetVendor().Internal());
2740  plug.SetVersion(ident->GetVersion());
2741 
2742  return plug;
2743 }
2744 
2746 {
2747  if (!mSettings)
2748  {
2749  mSettings = std::make_unique<wxFileConfig>(wxEmptyString, wxEmptyString, FileNames::PluginSettings());
2750 
2751  // Check for a settings version that we can understand
2752  if (mSettings->HasEntry(SETVERKEY))
2753  {
2754  wxString setver = mSettings->Read(SETVERKEY, SETVERKEY);
2755  if (setver < SETVERCUR )
2756  {
2757  // This is where we'd put in conversion code when the
2758  // settings version changes.
2759  //
2760  // Should also check for a settings file that is newer than
2761  // what we can understand.
2762  }
2763  }
2764  else
2765  {
2766  // Make sure is has a version string
2767  mSettings->Write(SETVERKEY, SETVERCUR);
2768  mSettings->Flush();
2769  }
2770  }
2771 
2772  return mSettings.get();
2773 }
2774 
2775 bool PluginManager::HasGroup(const wxString & group)
2776 {
2777  wxFileConfig *settings = GetSettings();
2778 
2779  bool res = settings->HasGroup(group);
2780  if (res)
2781  {
2782  // The group exists, but empty groups aren't considered valid
2783  wxString oldPath = settings->GetPath();
2784  settings->SetPath(group);
2785  res = settings->GetNumberOfEntries() || settings->GetNumberOfGroups();
2786  settings->SetPath(oldPath);
2787  }
2788 
2789  return res;
2790 }
2791 
2792 bool PluginManager::GetSubgroups(const wxString & group, wxArrayString & subgroups)
2793 {
2794  if (group.IsEmpty() || !HasGroup(group))
2795  {
2796  return false;
2797  }
2798 
2799  wxString path = GetSettings()->GetPath();
2800  GetSettings()->SetPath(group);
2801 
2802  wxString name = wxEmptyString;
2803  long index = 0;
2804  if (GetSettings()->GetFirstGroup(name, index))
2805  {
2806  do
2807  {
2808  subgroups.Add(name);
2809  } while (GetSettings()->GetNextGroup(name, index));
2810  }
2811 
2812  GetSettings()->SetPath(path);
2813 
2814  return true;
2815 }
2816 
2817 bool PluginManager::GetConfig(const wxString & key, int & value, int defval)
2818 {
2819  bool result = false;
2820 
2821  if (!key.IsEmpty())
2822  {
2823  result = GetSettings()->Read(key, &value, defval);
2824  }
2825 
2826  return result;
2827 }
2828 
2829 bool PluginManager::GetConfig(const wxString & key, wxString & value, const wxString & defval)
2830 {
2831  bool result = false;
2832 
2833  if (!key.IsEmpty())
2834  {
2835  wxString wxval = wxEmptyString;
2836 
2837  result = GetSettings()->Read(key, &wxval, defval);
2838 
2839  value = wxval;
2840  }
2841 
2842  return result;
2843 }
2844 
2845 bool PluginManager::GetConfig(const wxString & key, bool & value, bool defval)
2846 {
2847  bool result = false;
2848 
2849  if (!key.IsEmpty())
2850  {
2851  result = GetSettings()->Read(key, &value, defval);
2852  }
2853 
2854  return result;
2855 }
2856 
2857 bool PluginManager::GetConfig(const wxString & key, float & value, float defval)
2858 {
2859  bool result = false;
2860 
2861  if (!key.IsEmpty())
2862  {
2863  double dval = 0.0;
2864 
2865  result = GetSettings()->Read(key, &dval, (double) defval);
2866 
2867  value = (float) dval;
2868  }
2869 
2870  return result;
2871 }
2872 
2873 bool PluginManager::GetConfig(const wxString & key, double & value, double defval)
2874 {
2875  bool result = false;
2876 
2877  if (!key.IsEmpty())
2878  {
2879  result = GetSettings()->Read(key, &value, defval);
2880  }
2881 
2882  return result;
2883 }
2884 
2885 bool PluginManager::SetConfig(const wxString & key, const wxString & value)
2886 {
2887  bool result = false;
2888 
2889  if (!key.IsEmpty())
2890  {
2891  wxString wxval = value;
2892  result = GetSettings()->Write(key, wxval);
2893  if (result)
2894  {
2895  result = GetSettings()->Flush();
2896  }
2897  }
2898 
2899  return result;
2900 }
2901 
2902 bool PluginManager::SetConfig(const wxString & key, const int & value)
2903 {
2904  bool result = false;
2905 
2906  if (!key.IsEmpty())
2907  {
2908  result = GetSettings()->Write(key, value);
2909  if (result)
2910  {
2911  result = GetSettings()->Flush();
2912  }
2913  }
2914 
2915  return result;
2916 }
2917 
2918 bool PluginManager::SetConfig(const wxString & key, const bool & value)
2919 {
2920  bool result = false;
2921 
2922  if (!key.IsEmpty())
2923  {
2924  result = GetSettings()->Write(key, value);
2925  if (result)
2926  {
2927  result = GetSettings()->Flush();
2928  }
2929  }
2930 
2931  return result;
2932 }
2933 
2934 bool PluginManager::SetConfig(const wxString & key, const float & value)
2935 {
2936  bool result = false;
2937 
2938  if (!key.IsEmpty())
2939  {
2940  result = GetSettings()->Write(key, value);
2941  if (result)
2942  {
2943  result = GetSettings()->Flush();
2944  }
2945  }
2946 
2947  return result;
2948 }
2949 
2950 bool PluginManager::SetConfig(const wxString & key, const double & value)
2951 {
2952  bool result = false;
2953 
2954  if (!key.IsEmpty())
2955  {
2956  result = GetSettings()->Write(key, value);
2957  if (result)
2958  {
2959  result = GetSettings()->Flush();
2960  }
2961  }
2962 
2963  return result;
2964 }
2965 
2966 /* Return value is a key for lookup in a config file */
2967 wxString PluginManager::SettingsPath(const PluginID & ID, bool shared)
2968 {
2969  // All the strings reported by PluginDescriptor and used in this function
2970  // persist in the plugin settings configuration file, so they should not
2971  // be changed across Audacity versions, or else compatibility of the
2972  // configuration files will break.
2973 
2974  if (mPlugins.find(ID) == mPlugins.end())
2975  {
2976  return wxEmptyString;
2977  }
2978 
2979  const PluginDescriptor & plug = mPlugins[ID];
2980 
2981  wxString id = GetPluginTypeString(plug.GetPluginType()) +
2982  wxT("_") +
2983  plug.GetEffectFamilyId() + // is empty for non-Effects
2984  wxT("_") +
2985  plug.GetVendor() +
2986  wxT("_") +
2987  (shared ? wxT("") : plug.GetSymbol().Internal());
2988 
2989  return SETROOT +
2990  ConvertID(id) +
2991  wxCONFIG_PATH_SEPARATOR +
2992  (shared ? wxT("shared") : wxT("private")) +
2993  wxCONFIG_PATH_SEPARATOR;
2994 }
2995 
2996 /* Return value is a key for lookup in a config file */
2997 wxString PluginManager::SharedGroup(const PluginID & ID, const wxString & group)
2998 {
2999  wxString path = SettingsPath(ID, true);
3000 
3001  wxFileName ff(group);
3002  if (!ff.GetName().IsEmpty())
3003  {
3004  path += ff.GetFullPath(wxPATH_UNIX) + wxCONFIG_PATH_SEPARATOR;
3005  }
3006 
3007  return path;
3008 }
3009 
3010 /* Return value is a key for lookup in a config file */
3011 wxString PluginManager::SharedKey(const PluginID & ID, const wxString & group, const wxString & key)
3012 {
3013  wxString path = SharedGroup(ID, group);
3014  if (path.IsEmpty())
3015  {
3016  return path;
3017  }
3018 
3019  return path + key;
3020 }
3021 
3022 /* Return value is a key for lookup in a config file */
3023 wxString PluginManager::PrivateGroup(const PluginID & ID, const wxString & group)
3024 {
3025  wxString path = SettingsPath(ID, false);
3026 
3027  wxFileName ff(group);
3028  if (!ff.GetName().IsEmpty())
3029  {
3030  path += ff.GetFullPath(wxPATH_UNIX) + wxCONFIG_PATH_SEPARATOR;
3031  }
3032 
3033  return path;
3034 }
3035 
3036 /* Return value is a key for lookup in a config file */
3037 wxString PluginManager::PrivateKey(const PluginID & ID, const wxString & group, const wxString & key)
3038 {
3039  wxString path = PrivateGroup(ID, group);
3040  if (path.IsEmpty())
3041  {
3042  return path;
3043  }
3044 
3045  return path + key;
3046 }
3047 
3048 // Sanitize the ID...not the best solution, but will suffice until this
3049 // is converted to XML. We use base64 encoding to preserve case.
3051 {
3052  if (ID.StartsWith(wxT("base64:")))
3053  {
3054  wxString id = ID.Mid(7);
3055  ArrayOf<char> buf{ id.Length() / 4 * 3 };
3056  id = wxString::FromUTF8(buf.get(), b64decode(id, buf.get()));
3057  return id;
3058  }
3059 
3060  const wxCharBuffer & buf = ID.ToUTF8();
3061  return wxT("base64:") + b64encode(buf, strlen(buf));
3062 }
3063 
3065 // Base64 en/decoding
3066 //
3067 // Original routines marked as public domain and found at:
3068 //
3069 // http://en.wikibooks.org/wiki/Algorithm_implementation/Miscellaneous/Base64
3070 //
3072 
3073 // Lookup table for encoding
3074 const static wxChar cset[] = wxT("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");
3075 const static char padc = wxT('=');
3076 
3077 wxString PluginManager::b64encode(const void *in, int len)
3078 {
3079  unsigned char *p = (unsigned char *) in;
3080  wxString out;
3081 
3082  unsigned long temp;
3083  for (int i = 0; i < len / 3; i++)
3084  {
3085  temp = (*p++) << 16; //Convert to big endian
3086  temp += (*p++) << 8;
3087  temp += (*p++);
3088  out += cset[(temp & 0x00FC0000) >> 18];
3089  out += cset[(temp & 0x0003F000) >> 12];
3090  out += cset[(temp & 0x00000FC0) >> 6];
3091  out += cset[(temp & 0x0000003F)];
3092  }
3093 
3094  switch (len % 3)
3095  {
3096  case 1:
3097  temp = (*p++) << 16; //Convert to big endian
3098  out += cset[(temp & 0x00FC0000) >> 18];
3099  out += cset[(temp & 0x0003F000) >> 12];
3100  out += padc;
3101  out += padc;
3102  break;
3103 
3104  case 2:
3105  temp = (*p++) << 16; //Convert to big endian
3106  temp += (*p++) << 8;
3107  out += cset[(temp & 0x00FC0000) >> 18];
3108  out += cset[(temp & 0x0003F000) >> 12];
3109  out += cset[(temp & 0x00000FC0) >> 6];
3110  out += padc;
3111  break;
3112  }
3113 
3114  return out;
3115 }
3116 
3117 int PluginManager::b64decode(const wxString &in, void *out)
3118 {
3119  int len = in.length();
3120  unsigned char *p = (unsigned char *) out;
3121 
3122  if (len % 4) //Sanity check
3123  {
3124  return 0;
3125  }
3126 
3127  int padding = 0;
3128  if (len)
3129  {
3130  if (in[len - 1] == padc)
3131  {
3132  padding++;
3133  }
3134 
3135  if (in[len - 2] == padc)
3136  {
3137  padding++;
3138  }
3139  }
3140 
3141  //const char *a = in.mb_str();
3142  //Setup a vector to hold the result
3143  unsigned long temp = 0; //Holds decoded quanta
3144  int i = 0;
3145  while (i < len)
3146  {
3147  for (int quantumPosition = 0; quantumPosition < 4; quantumPosition++)
3148  {
3149  unsigned char c = in[i];
3150  temp <<= 6;
3151 
3152  if (c >= 0x41 && c <= 0x5A)
3153  {
3154  temp |= c - 0x41;
3155  }
3156  else if (c >= 0x61 && c <= 0x7A)
3157  {
3158  temp |= c - 0x47;
3159  }
3160  else if (c >= 0x30 && c <= 0x39)
3161  {
3162  temp |= c + 0x04;
3163  }
3164  else if (c == 0x2B)
3165  {
3166  temp |= 0x3E;
3167  }
3168  else if (c == 0x2F)
3169  {
3170  temp |= 0x3F;
3171  }
3172  else if (c == padc)
3173  {
3174  switch (len - i)
3175  {
3176  case 1: //One pad character
3177  *p++ = (temp >> 16) & 0x000000FF;
3178  *p++ = (temp >> 8) & 0x000000FF;
3179  return p - (unsigned char *) out;
3180  case 2: //Two pad characters
3181  *p++ = (temp >> 10) & 0x000000FF;
3182  return p - (unsigned char *) out;
3183  }
3184  }
3185  i++;
3186  }
3187  *p++ = (temp >> 16) & 0x000000FF;
3188  *p++ = (temp >> 8) & 0x000000FF;
3189  *p++ = temp & 0x000000FF;
3190  }
3191 
3192  return p - (unsigned char *) out;
3193 }
3194 
3195 // These are defined out-of-line here, to keep ComponentInterface free of other
3196 // #include directives.
3198 {
3199  return GetSymbol().Translation();
3200 }
const wxString & GetImporterIdentifier() const
static wxArrayString names()
Definition: Tags.cpp:703
#define KEY_VALID
AudacityPrefs * gPrefs
Definition: Prefs.cpp:73
const PluginDescriptor * GetFirstPlugin(int type)
#define wxPLURAL(sing, plur, n)
Definition: Internat.h:82
const wxString & Translation() const
void OnClearAll(wxCommandEvent &evt)
void SetImporterIdentifier(const wxString &identifier)
#define KEY_DESCRIPTION
wxString mProviderID
wxArrayString FindPluginsForProvider(const PluginID &provider, const wxString &path)
wxString GetVendor() const
static PluginID GetID(ModuleInterface *module)
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI...
Definition: ShuttleGui.h:409
bool RemoveSharedConfig(const PluginID &ID, const wxString &group, const wxString &key) override
ProgressDialog Class.
bool GetSharedConfigSubgroups(const PluginID &ID, const wxString &group, wxArrayString &subgroups) override
static ModuleManager & Get()
static const wxString & GetExecutablePath()
int GetPluginCount(PluginType type)
#define KEY_IMPORTERFILTER
ModuleInterface * mMod
wxString PrivateKey(const PluginID &ID, const wxString &group, const wxString &key)
void SetPluginType(PluginType type)
void SetEffectFamilyId(const wxString &family)
EffectType
bool GetSubgroups(const wxString &group, wxArrayString &subgroups)
#define KEY_PATH
bool DiscoverProviders()
virtual wxString GetPath()=0
virtual ComponentInterfaceSymbol GetSymbol()=0
ComponentInterface * CreateInstance(const PluginID &provider, const wxString &path)
#define KEY_EFFECTTYPE_ANALYZE
ComponentInterfaceSymbol mSymbol
#define REGROOT
bool HasPrivateConfigGroup(const PluginID &ID, const wxString &group)
PluginRegistrationDialog(wxWindow *parent, EffectType type)
wxString PluginID
Definition: Types.h:209
static const char padc
virtual bool SupportsRealtime()=0
wxString SharedGroup(const PluginID &ID, const wxString &group)
void SetImporterFilterDescription(const wxString &filterDesc)
bool GetConfig(const wxString &key, wxString &value, const wxString &defval=L"")
const PluginDescriptor * GetNextPlugin(int type)
int AudacityMessageBox(const wxString &message, const wxString &caption=AudacityMessageBoxCaptionStr(), long style=wxOK|wxCENTRE, wxWindow *parent=NULL, int x=wxDefaultCoord, int y=wxDefaultCoord)
Definition: ErrorDialog.h:92
ComponentInterface * GetInstance(const PluginID &ID)
#define SETROOT
void SetEffectAutomatable(bool automatable)
const wxString & Internal() const
void OnChangedVisibility(wxCommandEvent &evt)
bool IsProviderValid(const PluginID &provider, const wxString &path)
PluginType
Definition: PluginManager.h:32
static wxString PluginSettings()
Definition: FileNames.cpp:240
bool IsEffectAutomatable() const
bool IsEffectInteractive() const
wxString SharedKey(const PluginID &ID, const wxString &group, const wxString &key)
PluginDescriptor & CreatePlugin(const PluginID &id, ComponentInterface *ident, PluginType type)
bool SetConfig(const wxString &key, const wxString &value)
void SetEnabled(bool enable)
const wxString & GetImporterFilterDescription() const
virtual wxString InstallPath()=0
bool IsPluginValid(const PluginID &provider, const wxString &path, bool bFast)
#define SETVERCUR
void RegenerateEffectsList(int iShowWhat)
std::vector< PluginDescriptor * > plugs
wxString PrivateGroup(const PluginID &ID, const wxString &group)
bool SetSharedConfig(const PluginID &ID, const wxString &group, const wxString &key, const wxString &value) override
#define safenew
Definition: Audacity.h:230
#define KEY_SYMBOL
#define KEY_EFFECTTYPE_HIDDEN
#define KEY_EFFECTAUTOMATABLE
void EndHorizontalLay()
const wxString & GetPath() const
ComponentInterfaceSymbol pairs a persistent string identifier used internally with an optional...
bool IsInstantiated() const
void AddPrompt(const wxString &Prompt)
Right aligned text string.
Definition: ShuttleGui.cpp:239
An alternative to using wxWindowAccessible, which in wxWidgets 3.1.1 contained GetParent() which was ...
wxString ConvertID(const PluginID &ID)
void EndVerticalLay()
bool IsHidden(const PluginID &ID)
bool HasSharedConfigGroup(const PluginID &ID, const wxString &group)
void SetEffectInteractive(bool interactive)
#define KEY_EFFECTTYPE
EffectDefinitionInterface is a ComponentInterface that additionally tracks flag-functions for interac...
#define REGVERKEY
wxString mImporterIdentifier
void OnOK(wxCommandEvent &evt)
PluginMap::iterator mPluginsIter
#define SETVERKEY
wxString path
static std::unique_ptr< PluginManager > mInstance
void SetID(const PluginID &ID)
bool SetPrivateConfig(const PluginID &ID, const wxString &group, const wxString &key, const wxString &value) override
bool ShowManager(wxWindow *parent, EffectType type=EffectTypeNone)
wxString mEffectFamily
#define KEY_EFFECTINTERACTIVE
ComponentInterface * mInstance
void StartHorizontalLay(int PositionFlags=wxALIGN_CENTRE, int iProp=1)
const PluginDescriptor * GetNextPluginForEffectType(EffectType type)
virtual ComponentInterfaceSymbol GetFamilyId()=0
bool IsEffectRealtime() const
#define KEY_EFFECTTYPE_PROCESS
wxListCtrl * AddListControlReportMode()
Definition: ShuttleGui.cpp:697
const wxString & GetTranslatedName()
wxString b64encode(const void *in, int len)
#define KEY_IMPORTERIDENT
static wxString GetPluginTypeString(PluginType type)
wxString GetUntranslatedVersion() const
IteratorRange< Iterator > make_iterator_range(const Iterator &i1, const Iterator &i2)
Definition: MemoryX.h:608
void OnSort(wxListEvent &evt)
bool IsPluginRegistered(const wxString &path) override
PluginMap mPlugins
ShuttleGui & Id(int id)
#define KEY_PROVIDERID
void SetStyle(int Style)
Definition: ShuttleGui.h:287
virtual ComponentInterfaceSymbol GetVendor()=0
ProgressResult Update(int value, const wxString &message=wxEmptyString)
virtual EffectType GetClassification()
#define REGVERCUR
#define KEY_VERSION
void SetState(int i, bool toggle, bool state=true)
static bool CopyFile(const wxString &file1, const wxString &file2, bool overwrite=true)
Definition: FileNames.cpp:48
ComponentInterface * CreateProviderInstance(const PluginID &provider, const wxString &path)
const ComponentInterfaceSymbol & GetSymbol() const
void FindFilesInPathList(const wxString &pattern, const wxArrayString &pathList, wxArrayString &files, bool directories=false) override
bool DropFile(const wxString &fileName)
void OnListChar(wxKeyEvent &evt)
if(pTrack &&pTrack->GetDisplay()!=WaveTrack::Spectrum)
bool RemoveSharedConfigSubgroup(const PluginID &ID, const wxString &group) override
EffectManager is the class that handles effects and effect categories.
Definition: EffectManager.h:45
virtual ~PluginDescriptor()
void UnregisterPlugin(const PluginID &ID)
void OnSelectAll(wxCommandEvent &evt)
virtual bool IsDefault()=0
void EnablePlugin(const PluginID &ID, bool enable)
#define KEY_VENDOR
void CheckForUpdates(bool bFast=false)
const wxArrayString & GetImporterExtensions() const
static EffectManager & Get()
void SetEffectLegacy(bool legacy)
int b64decode(const wxString &in, void *out)
virtual bool SupportsAutomation()=0
virtual wxString GetPluginStringID()=0
EVT_BUTTON(wxID_NO, DependencyDialog::OnNo) EVT_BUTTON(wxID_YES
PluginManager maintains a list of all plug ins. That covers modules, effects, generators, analysis-effects, commands. It also has functions for shared and private configs - which need to move out.
void OnDisable(wxCommandEvent &evt)
static int wxCALLBACK SortCompare(long item1, long item2, long sortData)
virtual wxString GetPluginFormatDescription()=0
void SetVendor(const wxString &vendor)
void OnEnable(wxCommandEvent &evt)
AdornedRulerPanel * mParent
std::unique_ptr< wxFileConfig > mSettings
const PluginDescriptor * GetFirstPluginForEffectType(EffectType type)
wxString SettingsPath(const PluginID &ID, bool shared)
virtual wxString GetVersion()=0
void SetVersion(const wxString &version)
#define KEY_IMPORTEREXTENSIONS
EffectType GetEffectType() const
_("Move Track &Down")+wxT("\t")+(GetActiveProject() -> GetCommandManager() ->GetKeyFromName(wxT("TrackMoveDown")).Raw()), OnMoveTrack) POPUP_MENU_ITEM(OnMoveTopID, _("Move Track to &Top")+wxT("\t")+(GetActiveProject() ->GetCommandManager() ->GetKeyFromName(wxT("TrackMoveTop")).Raw()), OnMoveTrack) POPUP_MENU_ITEM(OnMoveBottomID, _("Move Track to &Bottom")+wxT("\t")+(GetActiveProject() ->GetCommandManager() ->GetKeyFromName(wxT("TrackMoveBottom")).Raw()), OnMoveTrack)#define SET_TRACK_NAME_PLUGIN_SYMBOLclass SetTrackNameCommand:public AudacityCommand
virtual EffectType GetType()=0
bool IsEffectDefault() const
static const PluginID & DefaultRegistrationCallback(ModuleInterface *provider, ComponentInterface *ident)
EffectType mEffectType
#define KEY_EFFECTTYPE_TOOL
const wxChar * name
Definition: Distortion.cpp:94
wxString GetEffectFamilyId() const
void SaveGroup(wxFileConfig *pRegistry, PluginType type)
const wxString & GetID() const
std::unordered_map< wxString, ItemData > ItemDataMap
#define KEY_ENABLED
static wxString PluginRegistry()
Definition: FileNames.cpp:235
const PluginID & RegisterPlugin(ModuleInterface *module) override
bool RemovePrivateConfigSubgroup(const PluginID &ID, const wxString &group) override
bool IsPluginEnabled(const PluginID &ID)
void SetInstance(ComponentInterface *instance)
const wxString & GetProviderID() const
wxString name
void SetEffectDefault(bool dflt)
bool GetPrivateConfigSubgroups(const PluginID &ID, const wxString &group, wxArrayString &subgroups) override
std::unordered_map< wxString, wxArrayString > ProviderMap
void SetSymbol(const ComponentInterfaceSymbol &symbol)
void PopulateOrExchange(ShuttleGui &S)
Defines the dialog and does data exchange with it.
PluginType mPluginType
#define KEY_EFFECTTYPE_NONE
bool RemovePrivateConfig(const PluginID &ID, const wxString &group, const wxString &key) override
wxRadioButton * AddRadioButtonToGroup(const wxString &Prompt)
Definition: ShuttleGui.cpp:492
void DeleteInstance(const PluginID &provider, ComponentInterface *instance)
bool GetSharedConfig(const PluginID &ID, const wxString &group, const wxString &key, wxString &value, const wxString &defval=_T("")) override
wxFileConfig * GetSettings()
#define KEY_EFFECTTYPE_GENERATE
void SetPath(const wxString &path)
bool RegisterEffectPlugin(const PluginID &provider, const wxString &path, wxString &errMsg)
bool IsEffectLegacy() const
void AddStandardButtons(long buttons=eOkButton|eCancelButton, wxButton *extra=NULL)
PluginType GetPluginType() const
virtual bool IsInteractive()=0
bool IsValid() const
END_EVENT_TABLE()
wxSizerItem * AddSpace(int width, int height)
#define KEY_EFFECTDEFAULT
bool GetPrivateConfig(const PluginID &ID, const wxString &group, const wxString &key, wxString &value, const wxString &defval=_T("")) override
#define KEY_EFFECTFAMILY
void SetProviderID(const PluginID &providerID)
virtual wxArrayString GetSupportedExtensions()=0
#define KEY_EFFECTREALTIME
static PluginManager & Get()
void OnCancel(wxCommandEvent &evt)
bool IsEnabled() const
const PluginDescriptor * GetPlugin(const PluginID &ID)
void SetImporterExtensions(const wxArrayString &extensions)
void SetEffectRealtime(bool realtime)
void LoadGroup(wxFileConfig *pRegistry, PluginType type)
const wxString & Msgid() const
void SetValid(bool valid)
ComponentInterface * GetInstance()
static const PluginID & AudacityCommandRegistrationCallback(ModuleInterface *provider, ComponentInterface *ident)
static wxString PlugInDir()
The user plug-in directory (not a system one)
Definition: FileNames.cpp:230
bool HasGroup(const wxString &group)
wxButton * AddButton(const wxString &Text, int PositionFlags=wxALIGN_CENTRE)
Definition: ShuttleGui.cpp:349
wxRadioButton * AddRadioButton(const wxString &Prompt)
Definition: ShuttleGui.cpp:476
wxString mImporterFilterDesc
#define KEY_NAME
void SetEffectType(EffectType type)
const ComponentInterfaceSymbol & GetSymbol(const PluginID &ID)
static const wxChar cset[]
ComponentInterface provides name / vendor / version functions to identify plugins. It is what makes a class a plug-in. Additionally it provides an optional parameter definitions function, for those components such as commands, effects and (soon) preference pagess that define parameters.
wxArrayString mImporterExtensions
void StartVerticalLay(int iProp=1)