Audacity  2.2.2
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 *state )
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  *state = 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  long i = mEffects->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
928  while (i != wxNOT_FOUND)
929  {
930  items.insert(items.begin(), i);
931  i = mEffects->GetNextItem(i, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
932  }
933 
934  for (size_t i = 0, cnt = items.size(); i < cnt; i++)
935  {
936  SetState(items[i], false, STATE_Enabled);
937  }
938 }
939 
940 void PluginRegistrationDialog::OnDisable(wxCommandEvent & WXUNUSED(evt))
941 {
942  std::vector<long> items;
943 
944  long i = mEffects->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
945  while (i != wxNOT_FOUND)
946  {
947  items.insert(items.begin(), i);
948  i = mEffects->GetNextItem(i, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
949  }
950 
951  for (size_t i = 0, cnt = items.size(); i < cnt; i++)
952  {
953  SetState(items[i], false, STATE_Disabled);
954  }
955 }
956 
957 void PluginRegistrationDialog::OnOK(wxCommandEvent & WXUNUSED(evt))
958 {
961 
962  int enableCount = 0;
963  for (ItemDataMap::iterator iter = mItems.begin(); iter != mItems.end(); ++iter)
964  {
965  ItemData & item = iter->second;
966  wxString path = item.path;
967 
968  if (item.state == STATE_Enabled && item.plugs[0]->GetPluginType() == PluginTypeStub)
969  {
970  enableCount++;
971  }
972  }
973 
974  wxString last3 = mLongestPath + wxT("\n") +
975  mLongestPath + wxT("\n") +
976  mLongestPath + wxT("\n");
977 
978  wxString msg;
979 
980  msg.Printf(_("Enabling effects or commands:\n\n%s"), last3);
981 
982  // Make sure the progress dialog is deleted before we call EndModal() or
983  // we will leave the project window in an unusable state on OSX.
984  // See bug #1192.
985  {
986  ProgressDialog progress(GetTitle(), msg, pdlgHideStopButton);
987  progress.CenterOnParent();
988 
989  int i = 0;
990  for (ItemDataMap::iterator iter = mItems.begin(); iter != mItems.end(); ++iter)
991  {
992  ItemData & item = iter->second;
993  wxString path = item.path;
994 
995  if (item.state == STATE_Enabled && item.plugs[0]->GetPluginType() == PluginTypeStub)
996  {
997  last3 = last3.AfterFirst(wxT('\n')) + item.path + wxT("\n");
998  auto status = progress.Update(++i, enableCount, wxString::Format(_("Enabling effect or command:\n\n%s"), last3));
999  if (status == ProgressResult::Cancelled)
1000  {
1001  break;
1002  }
1003 
1004  wxString errMsgs;
1005 
1006  // Try to register the plugin via each provider until one succeeds
1007  for (size_t j = 0, cnt = item.plugs.size(); j < cnt; j++)
1008  {
1009  wxString errMsg;
1010  if (mm.RegisterEffectPlugin(item.plugs[j]->GetProviderID(), path,
1011  errMsg))
1012  {
1013  for (size_t j = 0, cnt = item.plugs.size(); j < cnt; j++)
1014  {
1015  pm.mPlugins.erase(item.plugs[j]->GetProviderID() + wxT("_") + path);
1016  }
1017  break;
1018  }
1019  else
1020  {
1021  if (errMsgs.empty())
1022  errMsgs += '\n';
1023  errMsgs += errMsg;
1024  }
1025  }
1026  if (!errMsgs.empty())
1027  AudacityMessageBox( wxString::Format(
1028  _("Effect or Command at %s failed to register:\n%s"),
1029  path, errMsgs
1030  ) );
1031  }
1032  else if (item.state == STATE_New)
1033  {
1034  for (size_t j = 0, cnt = item.plugs.size(); j < cnt; j++)
1035  {
1036  item.plugs[j]->SetValid(false);
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]->SetEnabled(item.state == STATE_Enabled);
1044  item.plugs[j]->SetValid(item.valid);
1045  }
1046  }
1047  }
1048 
1049  pm.Save();
1050  }
1051 
1052  EndModal(wxID_OK);
1053 }
1054 
1055 void PluginRegistrationDialog::OnCancel(wxCommandEvent & WXUNUSED(evt))
1056 {
1057  EndModal(wxID_CANCEL);
1058 }
1059 
1060 
1061 
1063 //
1064 // Plugindescriptor
1065 //
1067 
1069 {
1071  mEnabled = false;
1072  mValid = false;
1073  mInstance = NULL;
1074 
1076  mEffectInteractive = false;
1077  mEffectDefault = false;
1078  mEffectLegacy = false;
1079  mEffectRealtime = false;
1080  mEffectAutomatable = false;
1081 }
1082 
1084 {
1085  DeleteInstance();
1086 }
1087 
1089 {
1090  if (mInstance)
1091  {
1093  mInstance = nullptr;
1094  }
1095 }
1096 
1098 {
1099  return mInstance != NULL;
1100 }
1101 
1103 {
1104  if (!mInstance)
1105  {
1107  {
1109  }
1110  else
1111  {
1113  }
1114  }
1115 
1116  return mInstance;
1117 }
1118 
1120 {
1121  if (mInstance && mInstance != instance)
1122  {
1123  // Be sure not to leak resources!!
1124  DeleteInstance();
1125  }
1126 
1127  mInstance = instance;
1128 
1129  return;
1130 }
1131 
1133 {
1134  return mPluginType;
1135 }
1136 
1138 {
1139  return mID;
1140 }
1141 
1143 {
1144  return mProviderID;
1145 }
1146 
1147 const wxString & PluginDescriptor::GetPath() const
1148 {
1149  return mPath;
1150 }
1151 
1153 {
1154  return mSymbol;
1155 }
1156 
1158 {
1159  return mVersion;
1160 }
1161 
1163 {
1164  return mVendor;
1165 }
1166 
1168 {
1169  return mEnabled;
1170 }
1171 
1173 {
1174  return mValid;
1175 }
1176 
1178 {
1179  mPluginType = type;
1180 }
1181 
1183 {
1184  mID = ID;
1185 }
1186 
1188 {
1189  mProviderID = providerID;
1190 }
1191 
1192 void PluginDescriptor::SetPath(const wxString & path)
1193 {
1194  mPath = path;
1195 }
1196 
1198 {
1199  mSymbol = symbol;
1200 }
1201 
1202 void PluginDescriptor::SetVersion(const wxString & version)
1203 {
1204  mVersion = version;
1205 }
1206 
1207 void PluginDescriptor::SetVendor(const wxString & vendor)
1208 {
1209  mVendor = vendor;
1210 }
1211 
1213 {
1214  mEnabled = enable;
1215 }
1216 
1218 {
1219  mValid = valid;
1220 }
1221 
1222 // Effects
1223 
1225 {
1226  return mEffectFamily;
1227 }
1228 
1230 {
1231  return mEffectType;
1232 }
1233 
1235 {
1236  return mEffectInteractive;
1237 }
1238 
1240 {
1241  return mEffectDefault;
1242 }
1243 
1245 {
1246  return mEffectLegacy;
1247 }
1248 
1250 {
1251  return mEffectRealtime;
1252 }
1253 
1255 {
1256  return mEffectAutomatable;
1257 }
1258 
1259 void PluginDescriptor::SetEffectFamilyId(const wxString & family)
1260 {
1261  mEffectFamily = family;
1262 }
1263 
1265 {
1266  mEffectType = type;
1267 }
1268 
1270 {
1271  mEffectInteractive = interactive;
1272 }
1273 
1275 {
1276  mEffectDefault = dflt;
1277 }
1278 
1280 {
1281  mEffectLegacy = legacy;
1282 }
1283 
1285 {
1286  mEffectRealtime = realtime;
1287 }
1288 
1290 {
1291  mEffectAutomatable = automatable;
1292 }
1293 
1294 // Importer
1295 
1297 {
1298  return mImporterIdentifier;
1299 }
1300 
1301 void PluginDescriptor::SetImporterIdentifier(const wxString & identifier)
1302 {
1303  mImporterIdentifier = identifier;
1304 }
1305 
1307 {
1308  return mImporterFilterDesc;
1309 }
1310 
1311 void PluginDescriptor::SetImporterFilterDescription(const wxString & filterDesc)
1312 {
1313  mImporterFilterDesc = filterDesc;
1314 }
1315 
1316 const wxArrayString & PluginDescriptor::GetImporterExtensions() const
1317 {
1318  return mImporterExtensions;
1319 }
1320 
1321 void PluginDescriptor::SetImporterExtensions(const wxArrayString & extensions)
1322 {
1323  mImporterExtensions = extensions;
1324 }
1325 
1327 //
1328 // PluginManager
1329 //
1331 
1332 #define REGVERKEY wxString(wxT("/pluginregistryversion"))
1333 #define REGVERCUR wxString(wxT("1.0"))
1334 #define REGROOT wxString(wxT("/pluginregistry/"))
1335 
1336 #define SETVERKEY wxString(wxT("/pluginsettingsversion"))
1337 #define SETVERCUR wxString(wxT("1.0"))
1338 #define SETROOT wxString(wxT("/pluginsettings/"))
1339 
1340 #define KEY_ID wxT("ID")
1341 #define KEY_PATH wxT("Path")
1342 #define KEY_SYMBOL wxT("Symbol")
1343 #define KEY_NAME wxT("Name")
1344 #define KEY_VENDOR wxT("Vendor")
1345 #define KEY_VERSION wxT("Version")
1346 #define KEY_DESCRIPTION wxT("Description")
1347 #define KEY_LASTUPDATED wxT("LastUpdated")
1348 #define KEY_ENABLED wxT("Enabled")
1349 #define KEY_VALID wxT("Valid")
1350 #define KEY_PROVIDERID wxT("ProviderID")
1351 #define KEY_EFFECTTYPE wxT("EffectType")
1352 #define KEY_EFFECTFAMILY wxT("EffectFamily")
1353 #define KEY_EFFECTDEFAULT wxT("EffectDefault")
1354 #define KEY_EFFECTINTERACTIVE wxT("EffectInteractive")
1355 #define KEY_EFFECTREALTIME wxT("EffectRealtime")
1356 #define KEY_EFFECTAUTOMATABLE wxT("EffectAutomatable")
1357 #define KEY_EFFECTTYPE_NONE wxT("None")
1358 #define KEY_EFFECTTYPE_ANALYZE wxT("Analyze")
1359 #define KEY_EFFECTTYPE_GENERATE wxT("Generate")
1360 #define KEY_EFFECTTYPE_PROCESS wxT("Process")
1361 #define KEY_EFFECTTYPE_TOOL wxT("Tool")
1362 #define KEY_EFFECTTYPE_HIDDEN wxT("Hidden")
1363 #define KEY_IMPORTERIDENT wxT("ImporterIdent")
1364 #define KEY_IMPORTERFILTER wxT("ImporterFilter")
1365 #define KEY_IMPORTEREXTENSIONS wxT("ImporterExtensions")
1366 
1367 // ============================================================================
1368 //
1369 // PluginManagerInterface implementation
1370 //
1371 // ============================================================================
1372 
1374  ModuleInterface *provider, IdentInterface *pInterface )
1375 {
1376  EffectDefinitionInterface * pEInterface = dynamic_cast<EffectDefinitionInterface*>(pInterface);
1377  if( pEInterface )
1378  return PluginManager::Get().RegisterPlugin(provider, pEInterface, PluginTypeEffect);
1379  CommandDefinitionInterface * pCInterface = dynamic_cast<CommandDefinitionInterface*>(pInterface);
1380  if( pCInterface )
1381  return PluginManager::Get().RegisterPlugin(provider, pCInterface);
1382  static wxString empty;
1383  return empty;
1384 }
1385 
1387  ModuleInterface *provider, IdentInterface *pInterface )
1388 {
1389  CommandDefinitionInterface * pCInterface = dynamic_cast<CommandDefinitionInterface*>(pInterface);
1390  if( pCInterface )
1391  return PluginManager::Get().RegisterPlugin(provider, pCInterface);
1392  static wxString empty;
1393  return empty;
1394 }
1395 
1396 
1397 bool PluginManager::IsPluginRegistered(const wxString & path)
1398 {
1399  for (PluginMap::iterator iter = mPlugins.begin(); iter != mPlugins.end(); ++iter)
1400  {
1401  if (iter->second.GetPath().IsSameAs(path))
1402  {
1403  return true;
1404  }
1405  }
1406 
1407  return false;
1408 }
1409 
1411 {
1412  PluginDescriptor & plug = CreatePlugin(GetID(module), module, PluginTypeModule);
1413 
1414  plug.SetEnabled(true);
1415  plug.SetValid(true);
1416 
1417  return plug.GetID();
1418 }
1419 
1421 {
1423 
1424  plug.SetProviderID(PluginManager::GetID(provider));
1425 
1426  plug.SetEnabled(true);
1427  plug.SetValid(true);
1428 
1429  return plug.GetID();
1430 }
1431 
1433 {
1434  PluginDescriptor & plug = CreatePlugin(GetID(effect), effect, (PluginType)type);
1435 
1436  plug.SetProviderID(PluginManager::GetID(provider));
1437 
1438  plug.SetEffectType(effect->GetType());
1439  plug.SetEffectFamilyId(effect->GetFamilyId().Internal());
1440  plug.SetEffectInteractive(effect->IsInteractive());
1441  plug.SetEffectDefault(effect->IsDefault());
1442  plug.SetEffectRealtime(effect->SupportsRealtime());
1443  plug.SetEffectAutomatable(effect->SupportsAutomation());
1444 
1445  plug.SetEnabled(true);
1446  plug.SetValid(true);
1447 
1448  return plug.GetID();
1449 }
1450 
1452 {
1453  PluginDescriptor & plug = CreatePlugin(GetID(importer), importer, PluginTypeImporter);
1454 
1455  plug.SetProviderID(PluginManager::GetID(provider));
1456 
1457  plug.SetImporterIdentifier(importer->GetPluginStringID());
1460 
1461  return plug.GetID();
1462 }
1463 
1464 void PluginManager::FindFilesInPathList(const wxString & pattern,
1465  const wxArrayString & pathList,
1466  wxArrayString & files,
1467  bool directories)
1468 {
1469 
1470  wxLogNull nolog;
1471 
1472  // Why bother...
1473  if (pattern.IsEmpty())
1474  {
1475  return;
1476  }
1477 
1478  // TODO: We REALLY need to figure out the "Audacity" plug-in path(s)
1479 
1480  wxArrayString paths;
1481 
1482  // Add the "per-user" plug-ins directory
1483  {
1484  const wxFileName &ff = FileNames::PlugInDir();
1485  paths.Add(ff.GetFullPath());
1486  }
1487 
1488  // Add the "Audacity" plug-ins directory
1489  wxFileName ff = PlatformCompatibility::GetExecutablePath();
1490 #if defined(__WXMAC__)
1491  // Path ends for example in "Audacity.app/Contents/MacOSX"
1492  //ff.RemoveLastDir();
1493  //ff.RemoveLastDir();
1494  // just remove the MacOSX part.
1495  ff.RemoveLastDir();
1496 #endif
1497  ff.AppendDir(wxT("plug-ins"));
1498  paths.Add(ff.GetPath());
1499 
1500  // Weed out duplicates
1501  for (size_t i = 0, cnt = pathList.size(); i < cnt; i++)
1502  {
1503  ff = pathList[i];
1504  const wxString path{ ff.GetFullPath() };
1505  if (paths.Index(path, wxFileName::IsCaseSensitive()) == wxNOT_FOUND)
1506  {
1507  paths.Add(path);
1508  }
1509  }
1510 
1511  // Find all matching files in each path
1512  for (size_t i = 0, cnt = paths.GetCount(); i < cnt; i++)
1513  {
1514  ff = paths[i] + wxFILE_SEP_PATH + pattern;
1515  wxDir::GetAllFiles(ff.GetPath(), &files, ff.GetFullName(), directories ? wxDIR_DEFAULT : wxDIR_FILES);
1516  }
1517 
1518  return;
1519 }
1520 
1521 bool PluginManager::HasSharedConfigGroup(const PluginID & ID, const wxString & group)
1522 {
1523  return HasGroup(SharedGroup(ID, group));
1524 }
1525 
1526 bool PluginManager::GetSharedConfigSubgroups(const PluginID & ID, const wxString & group, wxArrayString & subgroups)
1527 {
1528  return GetSubgroups(SharedGroup(ID, group), subgroups);
1529 }
1530 
1531 bool PluginManager::GetSharedConfig(const PluginID & ID, const wxString & group, const wxString & key, wxString & value, const wxString & defval)
1532 {
1533  return GetConfig(SharedKey(ID, group, key), value, defval);
1534 }
1535 
1536 bool PluginManager::GetSharedConfig(const PluginID & ID, const wxString & group, const wxString & key, int & value, int defval)
1537 {
1538  return GetConfig(SharedKey(ID, group, key), value, defval);
1539 }
1540 
1541 bool PluginManager::GetSharedConfig(const PluginID & ID, const wxString & group, const wxString & key, bool & value, bool defval)
1542 {
1543  return GetConfig(SharedKey(ID, group, key), value, defval);
1544 }
1545 
1546 bool PluginManager::GetSharedConfig(const PluginID & ID, const wxString & group, const wxString & key, float & value, float defval)
1547 {
1548  return GetConfig(SharedKey(ID, group, key), value, defval);
1549 }
1550 
1551 bool PluginManager::GetSharedConfig(const PluginID & ID, const wxString & group, const wxString & key, double & value, double defval)
1552 {
1553  return GetConfig(SharedKey(ID, group, key), value, defval);
1554 }
1555 
1556 bool PluginManager::SetSharedConfig(const PluginID & ID, const wxString & group, const wxString & key, const wxString & value)
1557 {
1558  return SetConfig(SharedKey(ID, group, key), value);
1559 }
1560 
1561 bool PluginManager::SetSharedConfig(const PluginID & ID, const wxString & group, const wxString & key, const int & value)
1562 {
1563  return SetConfig(SharedKey(ID, group, key), value);
1564 }
1565 
1566 bool PluginManager::SetSharedConfig(const PluginID & ID, const wxString & group, const wxString & key, const bool & value)
1567 {
1568  return SetConfig(SharedKey(ID, group, key), value);
1569 }
1570 
1571 bool PluginManager::SetSharedConfig(const PluginID & ID, const wxString & group, const wxString & key, const float & value)
1572 {
1573  return SetConfig(SharedKey(ID, group, key), value);
1574 }
1575 
1576 bool PluginManager::SetSharedConfig(const PluginID & ID, const wxString & group, const wxString & key, const double & value)
1577 {
1578  return SetConfig(SharedKey(ID, group, key), value);
1579 }
1580 
1581 bool PluginManager::RemoveSharedConfigSubgroup(const PluginID & ID, const wxString & group)
1582 {
1583  bool result = GetSettings()->DeleteGroup(SharedGroup(ID, group));
1584  if (result)
1585  {
1586  GetSettings()->Flush();
1587  }
1588 
1589  return result;
1590 }
1591 
1592 bool PluginManager::RemoveSharedConfig(const PluginID & ID, const wxString & group, const wxString & key)
1593 {
1594  bool result = GetSettings()->DeleteEntry(SharedKey(ID, group, key));
1595  if (result)
1596  {
1597  GetSettings()->Flush();
1598  }
1599 
1600  return result;
1601 }
1602 
1603 bool PluginManager::HasPrivateConfigGroup(const PluginID & ID, const wxString & group)
1604 {
1605  return HasGroup(PrivateGroup(ID, group));
1606 }
1607 
1608 bool PluginManager::GetPrivateConfigSubgroups(const PluginID & ID, const wxString & group, wxArrayString & subgroups)
1609 {
1610  return GetSubgroups(PrivateGroup(ID, group), subgroups);
1611 }
1612 
1613 bool PluginManager::GetPrivateConfig(const PluginID & ID, const wxString & group, const wxString & key, wxString & value, const wxString & defval)
1614 {
1615  return GetConfig(PrivateKey(ID, group, key), value, defval);
1616 }
1617 
1618 bool PluginManager::GetPrivateConfig(const PluginID & ID, const wxString & group, const wxString & key, int & value, int defval)
1619 {
1620  return GetConfig(PrivateKey(ID, group, key), value, defval);
1621 }
1622 
1623 bool PluginManager::GetPrivateConfig(const PluginID & ID, const wxString & group, const wxString & key, bool & value, bool defval)
1624 {
1625  return GetConfig(PrivateKey(ID, group, key), value, defval);
1626 }
1627 
1628 bool PluginManager::GetPrivateConfig(const PluginID & ID, const wxString & group, const wxString & key, float & value, float defval)
1629 {
1630  return GetConfig(PrivateKey(ID, group, key), value, defval);
1631 }
1632 
1633 bool PluginManager::GetPrivateConfig(const PluginID & ID, const wxString & group, const wxString & key, double & value, double defval)
1634 {
1635  return GetConfig(PrivateKey(ID, group, key), value, defval);
1636 }
1637 
1638 bool PluginManager::SetPrivateConfig(const PluginID & ID, const wxString & group, const wxString & key, const wxString & value)
1639 {
1640  return SetConfig(PrivateKey(ID, group, key), value);
1641 }
1642 
1643 bool PluginManager::SetPrivateConfig(const PluginID & ID, const wxString & group, const wxString & key, const int & value)
1644 {
1645  return SetConfig(PrivateKey(ID, group, key), value);
1646 }
1647 
1648 bool PluginManager::SetPrivateConfig(const PluginID & ID, const wxString & group, const wxString & key, const bool & value)
1649 {
1650  return SetConfig(PrivateKey(ID, group, key), value);
1651 }
1652 
1653 bool PluginManager::SetPrivateConfig(const PluginID & ID, const wxString & group, const wxString & key, const float & value)
1654 {
1655  return SetConfig(PrivateKey(ID, group, key), value);
1656 }
1657 
1658 bool PluginManager::SetPrivateConfig(const PluginID & ID, const wxString & group, const wxString & key, const double & value)
1659 {
1660  return SetConfig(PrivateKey(ID, group, key), value);
1661 }
1662 
1663 bool PluginManager::RemovePrivateConfigSubgroup(const PluginID & ID, const wxString & group)
1664 {
1665  bool result = GetSettings()->DeleteGroup(PrivateGroup(ID, group));
1666  if (result)
1667  {
1668  GetSettings()->Flush();
1669  }
1670 
1671  return result;
1672 }
1673 
1674 bool PluginManager::RemovePrivateConfig(const PluginID & ID, const wxString & group, const wxString & key)
1675 {
1676  bool result = GetSettings()->DeleteEntry(PrivateKey(ID, group, key));
1677  if (result)
1678  {
1679  GetSettings()->Flush();
1680  }
1681 
1682  return result;
1683 }
1684 
1685 // ============================================================================
1686 //
1687 // PluginManager
1688 //
1689 // ============================================================================
1690 
1691 // The one and only PluginManager
1692 std::unique_ptr<PluginManager> PluginManager::mInstance{};
1693 
1694 // ----------------------------------------------------------------------------
1695 // Creation/Destruction
1696 // ----------------------------------------------------------------------------
1697 
1699 {
1700  mSettings = NULL;
1701 }
1702 
1704 {
1705  // Ensure termination (harmless if already done)
1706  Terminate();
1707 }
1708 
1709 // ----------------------------------------------------------------------------
1710 // PluginManager implementation
1711 // ----------------------------------------------------------------------------
1712 
1713 // ============================================================================
1714 //
1715 // Return reference to singleton
1716 //
1717 // (Thread-safe...no active threading during construction or after destruction)
1718 // ============================================================================
1719 
1721 {
1722  if (!mInstance)
1723  {
1725  }
1726 
1727  return *mInstance;
1728 }
1729 
1731 {
1732  // Always load the registry first
1733  Load();
1734 
1735  // Then look for providers (they may autoregister plugins)
1737 
1738  // And finally check for updates
1739 #ifndef EXPERIMENTAL_EFFECT_MANAGEMENT
1740  CheckForUpdates();
1741 #else
1742  const bool kFast = true;
1743  CheckForUpdates( kFast );
1744 #endif
1745 }
1746 
1748 {
1749  // Get rid of all non-module plugins first
1750  PluginMap::iterator iter = mPlugins.begin();
1751  while (iter != mPlugins.end())
1752  {
1753  PluginDescriptor & plug = iter->second;
1754  if (plug.GetPluginType() == PluginTypeEffect)
1755  {
1756  mPlugins.erase(iter++);
1757  continue;
1758  }
1759 
1760  ++iter;
1761  }
1762 
1763  // Now get rid of the modules
1764  iter = mPlugins.begin();
1765  while (iter != mPlugins.end())
1766  {
1767  mPlugins.erase(iter++);
1768  }
1769 }
1770 
1771 bool PluginManager::DropFile(const wxString &fileName)
1772 {
1773  auto &mm = ModuleManager::Get();
1774  const wxFileName src{ fileName };
1775 
1776  for (const PluginDescriptor *plug = GetFirstPlugin(PluginTypeModule);
1777  plug;
1779  {
1780  auto module = static_cast<ModuleInterface *>
1781  (mm.CreateProviderInstance(plug->GetID(), plug->GetPath()));
1782  if (! module)
1783  continue;
1784 
1785  const auto &ff = module->InstallPath();
1786  auto extensions = module->FileExtensions();
1787  if (!ff.empty() &&
1788  make_iterator_range(extensions).contains(src.GetExt())) {
1789  wxString errMsg;
1790  // Do dry-run test of the file format
1791  unsigned nPlugIns =
1792  module->DiscoverPluginsAtPath(fileName, errMsg, {});
1793  if (nPlugIns) {
1794  // File contents are good for this module, so check no others.
1795  // All branches of this block return true, even in case of
1796  // failure for other reasons, to signal that other drag-and-drop
1797  // actions should not be tried.
1798 
1799  // Find path to copy it
1800  wxFileName dst;
1801  dst.AssignDir( ff );
1802  dst.SetFullName( src.GetFullName() );
1803  if ( dst.Exists() ) {
1804  // Query whether to overwrite
1805  bool overwrite = (wxYES == ::AudacityMessageBox(
1806  wxString::Format(_("Overwrite the plug-in file %s?"),
1807  dst.GetFullPath() ),
1808  _("Plug-in already exists"),
1809  wxYES_NO
1810  ) );
1811  if ( !overwrite )
1812  return true;
1813  }
1814 
1815  // Move the file or subtree
1816  bool copied = false;
1817  auto dstPath = dst.GetFullPath();
1818  if ( src.FileExists() )
1819  // A simple one-file plug-in
1820  copied = FileNames::CopyFile(
1821  src.GetFullPath(), dstPath, true );
1822  else {
1823  // A sub-folder
1824  // such as for some VST packages
1825  // Recursive copy needed -- to do
1826  return true;
1827  }
1828 
1829  if (!copied) {
1831  _("Plug-in file is in use. Failed to overwrite"));
1832  return true;
1833  }
1834 
1835  // Register for real
1836  std::vector<PluginID> ids;
1837  std::vector<wxString> names;
1838  nPlugIns = module->DiscoverPluginsAtPath(dstPath, errMsg,
1839  [&](ModuleInterface *provider, IdentInterface *ident)
1840  -> const PluginID& {
1841  // Register as by default, but also collecting the PluginIDs
1842  // and names
1844  provider, ident);
1845  ids.push_back(id);
1846  names.push_back( ident->GetSymbol().Translation() );
1847  return id;
1848  });
1849  if ( ! nPlugIns ) {
1850  // Unlikely after the dry run succeeded
1851  ::AudacityMessageBox( wxString::Format(
1852  _("Failed to register:\n%s"), errMsg ) );
1853  return true;
1854  }
1855 
1856  // Ask whether to enable the plug-ins
1857  if (auto nIds = ids.size()) {
1858  auto message = wxPLURAL( "Enable this plug-in?", "Enable these plug-ins?", nIds );
1859  message += wxT("\n");
1860  for (const auto &name : names)
1861  message += name + wxT("\n");
1862  bool enable = (wxYES == ::AudacityMessageBox(
1863  message,
1864  _("Enable new plug-ins"),
1865  wxYES_NO
1866  ) );
1867  for (const auto &id : ids)
1868  mPlugins[id].SetEnabled(enable);
1869  // Make changes to enabled status persist:
1870  this->Save();
1871  }
1872 
1873  return true;
1874  }
1875  }
1876  }
1877 
1878  return false;
1879 }
1880 
1882 {
1883  // Create/Open the registry
1884  wxFileConfig registry(wxEmptyString, wxEmptyString, FileNames::PluginRegistry());
1885 
1886  // If this group doesn't exist then we have something that's not a registry.
1887  // We should probably warn the user, but it's pretty unlikely that this will happen.
1888  if (!registry.HasGroup(REGROOT))
1889  {
1890  // Must start over
1891  registry.DeleteAll();
1892  return;
1893  }
1894 
1895  // Check for a registry version that we can understand
1896  wxString regver = registry.Read(REGVERKEY);
1897  if (regver < REGVERCUR )
1898  {
1899  // This is where we'd put in conversion code when the
1900  // registry version changes.
1901  //
1902  // Should also check for a registry file that is newer than
1903  // what we can understand.
1904  }
1905 
1906  // Load all provider plugins first
1907  LoadGroup(&registry, PluginTypeModule);
1908 
1909  // Now the rest
1910  LoadGroup(&registry, PluginTypeEffect);
1911  LoadGroup(&registry, PluginTypeAudacityCommand );
1912  LoadGroup(&registry, PluginTypeExporter);
1913  LoadGroup(&registry, PluginTypeImporter);
1914 
1915  LoadGroup(&registry, PluginTypeStub);
1916 
1917  // Not used by 2.1.1 or greater, but must load to allow users to switch between 2.1.0
1918  // and 2.1.1+. This should be removed after a few releases past 2.1.0.
1919  LoadGroup(&registry, PluginTypeNone);
1920 
1921  return;
1922 }
1923 
1924 void PluginManager::LoadGroup(wxFileConfig *pRegistry, PluginType type)
1925 {
1926 #ifdef __WXMAC__
1927  // Bug 1590: On Mac, we should purge the registry of Nyquist plug-ins
1928  // bundled with other versions of Audacity, assuming both versions
1929  // were properly installed in /Applications (or whatever it is called in
1930  // your locale)
1931 
1932  const auto fullExePath = PlatformCompatibility::GetExecutablePath();
1933 
1934  // Strip rightmost path components up to *.app
1935  wxFileName exeFn{ fullExePath };
1936  exeFn.SetEmptyExt();
1937  exeFn.SetName(wxString{});
1938  while(exeFn.GetDirCount() && !exeFn.GetDirs().Last().EndsWith(".app"))
1939  exeFn.RemoveLastDir();
1940 
1941  const auto goodPath = exeFn.GetPath();
1942 
1943  if(exeFn.GetDirCount())
1944  exeFn.RemoveLastDir();
1945  const auto possiblyBadPath = exeFn.GetPath();
1946 
1947  auto AcceptPath = [&](const wxString &path) {
1948  if (!path.StartsWith(possiblyBadPath))
1949  // Assume it's not under /Applications
1950  return true;
1951  if (path.StartsWith(goodPath))
1952  // It's bundled with this executable
1953  return true;
1954  return false;
1955  };
1956 #else
1957  auto AcceptPath = [](const wxString&){ return true; };
1958 #endif
1959 
1960  wxString strVal;
1961  bool boolVal;
1962  wxString groupName;
1963  long groupIndex;
1964  wxString group = GetPluginTypeString(type);
1965  wxString cfgPath = REGROOT + group + wxCONFIG_PATH_SEPARATOR;
1966 
1967  pRegistry->SetPath(cfgPath);
1968  for (bool cont = pRegistry->GetFirstGroup(groupName, groupIndex);
1969  cont;
1970  pRegistry->SetPath(cfgPath),
1971  cont = pRegistry->GetNextGroup(groupName, groupIndex))
1972  {
1973  PluginDescriptor plug;
1974 
1975  pRegistry->SetPath(groupName);
1976 
1977  groupName = ConvertID(groupName);
1978 
1979  // Bypass group if the ID is already in use
1980  if (mPlugins.find(groupName) != mPlugins.end())
1981  {
1982  pRegistry->SetPath(wxT(".."));
1983 
1984  continue;
1985  }
1986 
1987  // Set the ID and type
1988  plug.SetID(groupName);
1989  plug.SetPluginType(type);
1990 
1991  // Get the provider ID and bypass group if not found
1992  if (!pRegistry->Read(KEY_PROVIDERID, &strVal, wxEmptyString))
1993  {
1994  // Bypass group if the provider isn't valid
1995  if (!strVal.IsEmpty() && mPlugins.find(strVal) == mPlugins.end())
1996  {
1997  continue;
1998  }
1999  }
2000  plug.SetProviderID(PluginID(strVal));
2001 
2002  // Get the path (optional)
2003  pRegistry->Read(KEY_PATH, &strVal, wxEmptyString);
2004  if (!AcceptPath(strVal))
2005  // Ignore the obsolete path in the config file, during session,
2006  // but don't remove it from the file. Maybe you really want to
2007  // switch back to the other version of Audacity and lose nothing.
2008  continue;
2009  plug.SetPath(strVal);
2010 
2011  /*
2012  // PRL: Ignore names written in configs before 2.3.0!
2013  // use Internal string only! Let the present version of Audacity map
2014  // that to a user-visible string.
2015  // Get the name and bypass group if not found
2016  if (!pRegistry->Read(KEY_NAME, &strVal))
2017  {
2018  continue;
2019  }
2020  plug.SetName(strVal);
2021  */
2022 
2023  // Get the symbol...Audacity 2.3.0 or later requires it
2024  // bypass group if not found
2025  // Note, KEY_SYMBOL started getting written to config files in 2.1.0.
2026  // KEY_NAME (now ignored) was written before that, but only for VST
2027  // effects.
2028  if (!pRegistry->Read(KEY_SYMBOL, &strVal))
2029  continue;
2030  plug.SetSymbol(strVal);
2031 
2032  // Get the version and bypass group if not found
2033  if (!pRegistry->Read(KEY_VERSION, &strVal))
2034  {
2035  continue;
2036  }
2037  plug.SetVersion(strVal);
2038 
2039  // Get the vendor and bypass group if not found
2040  if (!pRegistry->Read(KEY_VENDOR, &strVal))
2041  {
2042  continue;
2043  }
2044  plug.SetVendor( strVal );
2045 
2046 #if 0
2047  // This was done before version 2.2.2, but the value was not really used
2048  // But absence of a value will cause early versions to skip the group
2049  // Therefore we still write a blank to keep pluginregistry.cfg
2050  // backwards-compatible
2051 
2052  // Get the description and bypass group if not found
2053  if (!pRegistry->Read(KEY_DESCRIPTION, &strVal))
2054  {
2055  continue;
2056  }
2057 #endif
2058 
2059  // Is it enabled...default to no if not found
2060  pRegistry->Read(KEY_ENABLED, &boolVal, false);
2061  plug.SetEnabled(boolVal);
2062 
2063  // Is it valid...default to no if not found
2064  pRegistry->Read(KEY_VALID, &boolVal, false);
2065  plug.SetValid(boolVal);
2066 
2067  switch (type)
2068  {
2069  case PluginTypeModule:
2070  {
2071  // Nothing to do here yet
2072  }
2073  break;
2074 
2075  case PluginTypeEffect:
2076  {
2077  // Get the effect type and bypass group if not found
2078  if (!pRegistry->Read(KEY_EFFECTTYPE, &strVal))
2079  continue;
2080 
2081  if (strVal.IsSameAs(KEY_EFFECTTYPE_NONE))
2083  else if (strVal.IsSameAs(KEY_EFFECTTYPE_ANALYZE))
2085  else if (strVal.IsSameAs(KEY_EFFECTTYPE_GENERATE))
2087  else if (strVal.IsSameAs(KEY_EFFECTTYPE_PROCESS))
2089  else if (strVal.IsSameAs(KEY_EFFECTTYPE_TOOL))
2091  else if (strVal.IsSameAs(KEY_EFFECTTYPE_HIDDEN))
2093  else
2094  continue;
2095 
2096  // Get the effect family and bypass group if not found
2097  if (!pRegistry->Read(KEY_EFFECTFAMILY, &strVal))
2098  {
2099  continue;
2100  }
2101  plug.SetEffectFamilyId(strVal);
2102 
2103  // Is it a default (above the line) effect and bypass group if not found
2104  if (!pRegistry->Read(KEY_EFFECTDEFAULT, &boolVal))
2105  {
2106  continue;
2107  }
2108  plug.SetEffectDefault(boolVal);
2109 
2110  // Is it an interactive effect and bypass group if not found
2111  if (!pRegistry->Read(KEY_EFFECTINTERACTIVE, &boolVal))
2112  {
2113  continue;
2114  }
2115  plug.SetEffectInteractive(boolVal);
2116 
2117  // Is it a realtime capable effect and bypass group if not found
2118  if (!pRegistry->Read(KEY_EFFECTREALTIME, &boolVal))
2119  {
2120  continue;
2121  }
2122  plug.SetEffectRealtime(boolVal);
2123 
2124  // Does the effect support automation...bypass group if not found
2125  if (!pRegistry->Read(KEY_EFFECTAUTOMATABLE, &boolVal))
2126  {
2127  continue;
2128  }
2129  plug.SetEffectAutomatable(boolVal);
2130  }
2131  break;
2132 
2133  case PluginTypeImporter:
2134  {
2135  // Get the importer identifier and bypass group if not found
2136  if (!pRegistry->Read(KEY_IMPORTERIDENT, &strVal))
2137  {
2138  continue;
2139  }
2140  plug.SetImporterIdentifier(strVal);
2141 
2142  // Get the importer filter description and bypass group if not found
2143  if (!pRegistry->Read(KEY_IMPORTERFILTER, &strVal))
2144  {
2145  continue;
2146  }
2147  plug.SetImporterFilterDescription(strVal);
2148 
2149  // Get the importer extensions and bypass group if not found
2150  if (!pRegistry->Read(KEY_IMPORTEREXTENSIONS, &strVal))
2151  {
2152  continue;
2153  }
2154  wxArrayString extensions;
2155  wxStringTokenizer tkr(strVal, wxT(":"));
2156  while (tkr.HasMoreTokens())
2157  {
2158  extensions.Add(tkr.GetNextToken());
2159  }
2160  plug.SetImporterExtensions(extensions);
2161  }
2162  break;
2163 
2164  case PluginTypeStub:
2165  {
2166  // Nothing additional for stubs
2167  }
2168  break;
2169 
2170  // Not used by 2.1.1 or greater and should be removed after a few releases past 2.1.0.
2171  case PluginTypeNone:
2172  {
2173  // Used for stub groups
2174  }
2175  break;
2176 
2177  default:
2178  {
2179  continue;
2180  }
2181  }
2182 
2183  // Everything checked out...accept the plugin
2184  mPlugins[groupName] = plug;
2185  }
2186 
2187  return;
2188 }
2189 
2191 {
2192  // Create/Open the registry
2193  wxFileConfig registry(wxEmptyString, wxEmptyString, FileNames::PluginRegistry());
2194 
2195  // Clear it out
2196  registry.DeleteAll();
2197 
2198  // Write the version string
2199  registry.Write(REGVERKEY, REGVERCUR);
2200 
2201  // Save the individual groups
2202  SaveGroup(&registry, PluginTypeEffect);
2203  SaveGroup(&registry, PluginTypeExporter);
2205  SaveGroup(&registry, PluginTypeImporter);
2206  SaveGroup(&registry, PluginTypeStub);
2207 
2208  // Not used by 2.1.1 or greater, but must save to allow users to switch between 2.1.0
2209  // and 2.1.1+. This should be removed after a few releases past 2.1.0.
2210  //SaveGroup(&registry, PluginTypeNone);
2211 
2212  // And now the providers
2213  SaveGroup(&registry, PluginTypeModule);
2214 
2215  // Just to be safe
2216  registry.Flush();
2217 }
2218 
2219 void PluginManager::SaveGroup(wxFileConfig *pRegistry, PluginType type)
2220 {
2221  wxString group = GetPluginTypeString(type);
2222  for (PluginMap::iterator iter = mPlugins.begin(); iter != mPlugins.end(); ++iter)
2223  {
2224  PluginDescriptor & plug = iter->second;
2225 
2226  if (plug.GetPluginType() != type)
2227  {
2228  continue;
2229  }
2230 
2231  pRegistry->SetPath(REGROOT + group + wxCONFIG_PATH_SEPARATOR + ConvertID(plug.GetID()));
2232 
2233  pRegistry->Write(KEY_PATH, plug.GetPath());
2234  pRegistry->Write(KEY_SYMBOL, plug.GetSymbol().Internal());
2235 
2236  // PRL: Writing KEY_NAME which is no longer read, but older Audacity
2237  // versions expect to find it.
2238  pRegistry->Write(KEY_NAME, plug.GetSymbol().Msgid());
2239 
2240  pRegistry->Write(KEY_VERSION, plug.GetUntranslatedVersion());
2241  pRegistry->Write(KEY_VENDOR, plug.GetVendor());
2242  // Write a blank -- see comments in LoadGroup:
2243  pRegistry->Write(KEY_DESCRIPTION, wxString{});
2244  pRegistry->Write(KEY_PROVIDERID, plug.GetProviderID());
2245  pRegistry->Write(KEY_ENABLED, plug.IsEnabled());
2246  pRegistry->Write(KEY_VALID, plug.IsValid());
2247 
2248  switch (type)
2249  {
2250  case PluginTypeModule:
2251  break;
2252 
2253  case PluginTypeEffect:
2254  {
2255  EffectType etype = plug.GetEffectType();
2256  wxString stype;
2257  if (etype == EffectTypeNone)
2258  stype = KEY_EFFECTTYPE_NONE;
2259  else if (etype == EffectTypeAnalyze)
2260  stype = KEY_EFFECTTYPE_ANALYZE;
2261  else if (etype == EffectTypeGenerate)
2262  stype = KEY_EFFECTTYPE_GENERATE;
2263  else if (etype == EffectTypeProcess)
2264  stype = KEY_EFFECTTYPE_PROCESS;
2265  else if (etype == EffectTypeTool)
2266  stype = KEY_EFFECTTYPE_TOOL;
2267  else if (etype == EffectTypeHidden)
2268  stype = KEY_EFFECTTYPE_HIDDEN;
2269 
2270  pRegistry->Write(KEY_EFFECTTYPE, stype);
2271  pRegistry->Write(KEY_EFFECTFAMILY, plug.GetEffectFamilyId());
2272  pRegistry->Write(KEY_EFFECTDEFAULT, plug.IsEffectDefault());
2273  pRegistry->Write(KEY_EFFECTINTERACTIVE, plug.IsEffectInteractive());
2274  pRegistry->Write(KEY_EFFECTREALTIME, plug.IsEffectRealtime());
2275  pRegistry->Write(KEY_EFFECTAUTOMATABLE, plug.IsEffectAutomatable());
2276  }
2277  break;
2278 
2279  case PluginTypeImporter:
2280  {
2281  pRegistry->Write(KEY_IMPORTERIDENT, plug.GetImporterIdentifier());
2282  pRegistry->Write(KEY_IMPORTERFILTER, plug.GetImporterFilterDescription());
2283  const wxArrayString & extensions = plug.GetImporterExtensions();
2284  wxString strExt;
2285  for (size_t i = 0, cnt = extensions.size(); i < cnt; i++)
2286  {
2287  strExt += extensions[i] + wxT(":");
2288  }
2289  strExt.RemoveLast(1);
2290  pRegistry->Write(KEY_IMPORTEREXTENSIONS, strExt);
2291  }
2292  break;
2293 
2294  default:
2295  break;
2296  }
2297  }
2298 
2299  return;
2300 }
2301 
2302 // If bFast is true, do not do a full check. Just check the ones
2303 // that are quick to check. Currently (Feb 2017) just Nyquist
2304 // and built-ins.
2306 {
2307  // Get ModuleManager reference
2309 
2310  wxArrayString pathIndex;
2311  for (PluginMap::iterator iter = mPlugins.begin(); iter != mPlugins.end(); ++iter)
2312  {
2313  PluginDescriptor & plug = iter->second;
2314 
2315  // Bypass 2.1.0 placeholders...remove this after a few releases past 2.1.0
2316  if (plug.GetPluginType() == PluginTypeNone)
2317  {
2318  continue;
2319  }
2320 
2321  pathIndex.Add(plug.GetPath().BeforeFirst(wxT(';')));
2322  }
2323 
2324  // Check all known plugins to ensure they are still valid and scan for NEW ones.
2325  //
2326  // All NEW plugins get a stub entry created that will remain in place until the
2327  // user enables or disables the plugin.
2328  //
2329  // Becuase we use the plugins "path" as returned by the providers, we can actually
2330  // have multiple providers report the same path since, at this point, they only
2331  // know that the path might possibly be one supported by the provider.
2332  //
2333  // When the user enables the plugin, each provider that reported it will be asked
2334  // to register the plugin.
2335  for (PluginMap::iterator iter = mPlugins.begin(); iter != mPlugins.end(); ++iter)
2336  {
2337  PluginDescriptor & plug = iter->second;
2338  const PluginID & plugID = plug.GetID();
2339  const wxString & plugPath = plug.GetPath();
2340  PluginType plugType = plug.GetPluginType();
2341 
2342  // Bypass 2.1.0 placeholders...remove this after a few releases past 2.1.0
2343  if (plugType == PluginTypeNone)
2344  {
2345  continue;
2346  }
2347 
2348  if ( plugType == PluginTypeModule )
2349  {
2350  if( bFast )
2351  {
2352  // Skip modules, when doing a fast refresh/check.
2353  }
2354  else if (!mm.IsProviderValid(plugID, plugPath))
2355  {
2356  plug.SetEnabled(false);
2357  plug.SetValid(false);
2358  }
2359  else
2360  {
2361  // Collect plugin paths
2362  wxArrayString paths = mm.FindPluginsForProvider(plugID, plugPath);
2363  for (size_t i = 0, cnt = paths.GetCount(); i < cnt; i++)
2364  {
2365  wxString path = paths[i].BeforeFirst(wxT(';'));;
2366  if (pathIndex.Index(path) == wxNOT_FOUND)
2367  {
2368  PluginID ID = plugID + wxT("_") + path;
2369  PluginDescriptor & plug = mPlugins[ID]; // This will create a NEW descriptor
2371  plug.SetID(ID);
2372  plug.SetProviderID(plugID);
2373  plug.SetPath(path);
2374  plug.SetEnabled(false);
2375  plug.SetValid(false);
2376  }
2377  }
2378  }
2379  }
2380  else if (plugType != PluginTypeNone && plugType != PluginTypeStub)
2381  {
2382  plug.SetValid(mm.IsPluginValid(plug.GetProviderID(), plugPath, bFast));
2383  if (!plug.IsValid())
2384  {
2385  plug.SetEnabled(false);
2386  }
2387  }
2388  }
2389 
2390  Save();
2391 
2392  return;
2393 }
2394 
2395 bool PluginManager::ShowManager(wxWindow *parent, EffectType type)
2396 {
2397  CheckForUpdates();
2398 
2399  PluginRegistrationDialog dlg(parent, type);
2400  return dlg.ShowModal() == wxID_OK;
2401 }
2402 
2403 // Here solely for the purpose of Nyquist Workbench until
2404 // a better solution is devised.
2406 {
2407  PluginDescriptor & plug = CreatePlugin(GetID(effect), effect, type);
2408 
2409  plug.SetEffectType(effect->GetType());
2410  plug.SetEffectFamilyId(effect->GetFamilyId().Internal());
2411  plug.SetEffectInteractive(effect->IsInteractive());
2412  plug.SetEffectDefault(effect->IsDefault());
2413  plug.SetEffectRealtime(effect->SupportsRealtime());
2414  plug.SetEffectAutomatable(effect->SupportsAutomation());
2415 
2416  plug.SetInstance(effect);
2417  plug.SetEffectLegacy(true);
2418  plug.SetEnabled(true);
2419  plug.SetValid(true);
2420 
2421  return plug.GetID();
2422 }
2423 
2424 // Here solely for the purpose of Nyquist Workbench until
2425 // a better solution is devised.
2427 {
2428  if (mPlugins.find(ID) == mPlugins.end())
2429  {
2430  return;
2431  }
2432 
2433  mPlugins.erase(ID);
2434 }
2435 
2437 {
2438  int num = 0;
2439 
2440  for (PluginMap::iterator iter = mPlugins.begin(); iter != mPlugins.end(); ++iter)
2441  {
2442  if (iter->second.GetPluginType() == type)
2443  {
2444  num++;
2445  }
2446  }
2447 
2448  return num;
2449 }
2450 
2452 {
2453  if (mPlugins.find(ID) == mPlugins.end())
2454  {
2455  return NULL;
2456  }
2457 
2458  return &mPlugins[ID];
2459 }
2460 
2462 {
2463  for (mPluginsIter = mPlugins.begin(); mPluginsIter != mPlugins.end(); ++mPluginsIter)
2464  {
2465  PluginDescriptor & plug = mPluginsIter->second;
2466  PluginType plugType = plug.GetPluginType();
2467  if( plug.IsValid() && plug.IsEnabled() && ((plugType & type) != 0))
2468  {
2469  bool familyEnabled = true;
2470  if( (plugType & PluginTypeEffect) != 0)
2471  // This preference may be written by EffectsPrefs
2472  gPrefs->Read(plug.GetEffectFamilyId() + wxT("/Enable"), &familyEnabled, true);
2473  if (familyEnabled)
2474  return &mPluginsIter->second;
2475  }
2476  }
2477 
2478  return NULL;
2479 }
2480 
2482 {
2483  while (++mPluginsIter != mPlugins.end())
2484  {
2485  PluginDescriptor & plug = mPluginsIter->second;
2486  PluginType plugType = plug.GetPluginType();
2487  if( plug.IsValid() && plug.IsEnabled() && ((plugType & type) != 0))
2488  {
2489  bool familyEnabled = true;
2490  if( (plugType & PluginTypeEffect) != 0)
2491  // This preference may be written by EffectsPrefs
2492  gPrefs->Read(plug.GetEffectFamilyId() + wxT("/Enable"), &familyEnabled, true);
2493  if (familyEnabled)
2494  return &mPluginsIter->second;
2495  }
2496  }
2497 
2498  return NULL;
2499 }
2500 
2502 {
2504 
2505  for (mPluginsIter = mPlugins.begin(); mPluginsIter != mPlugins.end(); ++mPluginsIter)
2506  {
2507  PluginDescriptor & plug = mPluginsIter->second;
2508 
2509  bool familyEnabled;
2510  // This preference may be written by EffectsPrefs
2511  gPrefs->Read(plug.GetEffectFamilyId() + wxT("/Enable"), &familyEnabled, true);
2512  if (plug.IsValid() && plug.IsEnabled() && plug.GetEffectType() == type && familyEnabled)
2513  {
2514  if (plug.IsInstantiated() && em.IsHidden(plug.GetID()))
2515  {
2516  continue;
2517  }
2518 
2519  return &plug;
2520  }
2521  }
2522 
2523  return NULL;
2524 }
2525 
2527 {
2529 
2530  while (++mPluginsIter != mPlugins.end())
2531  {
2532  PluginDescriptor & plug = mPluginsIter->second;
2533  bool familyEnabled;
2534  // This preference may be written by EffectsPrefs
2535  gPrefs->Read(plug.GetEffectFamilyId() + wxT("/Enable"), &familyEnabled, true);
2536  if (plug.IsValid() && plug.IsEnabled() && plug.GetEffectType() == type && familyEnabled)
2537  {
2538  if (plug.IsInstantiated() && em.IsHidden(plug.GetID()))
2539  {
2540  continue;
2541  }
2542 
2543  return &plug;
2544  }
2545  }
2546 
2547  return NULL;
2548 }
2549 
2551 {
2552  if (mPlugins.find(ID) == mPlugins.end())
2553  {
2554  return false;
2555  }
2556 
2557  return mPlugins[ID].IsEnabled();
2558 }
2559 
2560 void PluginManager::EnablePlugin(const PluginID & ID, bool enable)
2561 {
2562  if (mPlugins.find(ID) == mPlugins.end())
2563  {
2564  return;
2565  }
2566 
2567  return mPlugins[ID].SetEnabled(enable);
2568 }
2569 
2571 {
2572  if (mPlugins.find(ID) == mPlugins.end())
2573  {
2574  static IdentInterfaceSymbol empty;
2575  return empty;
2576  }
2577 
2578  return mPlugins[ID].GetSymbol();
2579 }
2580 
2582 {
2583  if (mPlugins.find(ID) == mPlugins.end())
2584  {
2585  return NULL;
2586  }
2587 
2588  PluginDescriptor & plug = mPlugins[ID];
2589 
2590  // If not dealing with legacy effects, make sure the provider is loaded
2591  if (!plug.IsEffectLegacy())
2592  {
2593  const PluginID & prov = plug.GetProviderID();
2594  if (mPlugins.find(prov) == mPlugins.end())
2595  {
2596  return NULL;
2597  }
2598  mPlugins[prov].GetInstance();
2599  }
2600 
2601  return plug.GetInstance();
2602 }
2603 
2605 {
2606  return wxString::Format(wxT("%s_%s_%s_%s_%s"),
2608  wxEmptyString,
2609  module->GetVendor().Internal(),
2610  module->GetSymbol().Internal(),
2611  module->GetPath());
2612 }
2613 
2615 {
2616  return wxString::Format(wxT("%s_%s_%s_%s_%s"),
2618  wxEmptyString,
2619  command->GetVendor().Internal(),
2620  command->GetSymbol().Internal(),
2621  command->GetPath());
2622 }
2623 
2625 {
2626  return wxString::Format(wxT("%s_%s_%s_%s_%s"),
2628  effect->GetFamilyId().Internal(),
2629  effect->GetVendor().Internal(),
2630  effect->GetSymbol().Internal(),
2631  effect->GetPath());
2632 }
2633 
2635 {
2636  return wxString::Format(wxT("%s_%s_%s_%s_%s"),
2638  wxEmptyString,
2639  importer->GetVendor().Internal(),
2640  importer->GetSymbol().Internal(),
2641  importer->GetPath());
2642 }
2643 
2644 // This string persists in configuration files
2645 // So config compatibility will break if it is changed across Audacity versions
2647 {
2648  wxString str;
2649 
2650  switch (type)
2651  {
2652  default:
2653  case PluginTypeNone:
2654  str = wxT("Placeholder");
2655  break;
2656  case PluginTypeStub:
2657  str = wxT("Stub");
2658  break;
2659  case PluginTypeEffect:
2660  str = wxT("Effect");
2661  break;
2663  str = wxT("Generic");
2664  break;
2665  case PluginTypeExporter:
2666  str = wxT("Exporter");
2667  break;
2668  case PluginTypeImporter:
2669  str = wxT("Importer");
2670  break;
2671  case PluginTypeModule:
2672  str = wxT("Module");
2673  break;
2674  }
2675 
2676  return str;
2677 }
2678 
2681  PluginType type)
2682 {
2683  // This will either create a NEW entry or replace an existing entry
2684  PluginDescriptor & plug = mPlugins[id];
2685 
2686  plug.SetPluginType(type);
2687 
2688  plug.SetID(id);
2689  plug.SetPath(ident->GetPath());
2690  plug.SetSymbol(ident->GetSymbol());
2691  plug.SetVendor(ident->GetVendor().Internal());
2692  plug.SetVersion(ident->GetVersion());
2693 
2694  return plug;
2695 }
2696 
2698 {
2699  if (!mSettings)
2700  {
2701  mSettings = std::make_unique<wxFileConfig>(wxEmptyString, wxEmptyString, FileNames::PluginSettings());
2702 
2703  // Check for a settings version that we can understand
2704  if (mSettings->HasEntry(SETVERKEY))
2705  {
2706  wxString setver = mSettings->Read(SETVERKEY, SETVERKEY);
2707  if (setver < SETVERCUR )
2708  {
2709  // This is where we'd put in conversion code when the
2710  // settings version changes.
2711  //
2712  // Should also check for a settings file that is newer than
2713  // what we can understand.
2714  }
2715  }
2716  else
2717  {
2718  // Make sure is has a version string
2719  mSettings->Write(SETVERKEY, SETVERCUR);
2720  mSettings->Flush();
2721  }
2722  }
2723 
2724  return mSettings.get();
2725 }
2726 
2727 bool PluginManager::HasGroup(const wxString & group)
2728 {
2729  wxFileConfig *settings = GetSettings();
2730 
2731  bool res = settings->HasGroup(group);
2732  if (res)
2733  {
2734  // The group exists, but empty groups aren't considered valid
2735  wxString oldPath = settings->GetPath();
2736  settings->SetPath(group);
2737  res = settings->GetNumberOfEntries() || settings->GetNumberOfGroups();
2738  settings->SetPath(oldPath);
2739  }
2740 
2741  return res;
2742 }
2743 
2744 bool PluginManager::GetSubgroups(const wxString & group, wxArrayString & subgroups)
2745 {
2746  if (group.IsEmpty() || !HasGroup(group))
2747  {
2748  return false;
2749  }
2750 
2751  wxString path = GetSettings()->GetPath();
2752  GetSettings()->SetPath(group);
2753 
2754  wxString name = wxEmptyString;
2755  long index = 0;
2756  if (GetSettings()->GetFirstGroup(name, index))
2757  {
2758  do
2759  {
2760  subgroups.Add(name);
2761  } while (GetSettings()->GetNextGroup(name, index));
2762  }
2763 
2764  GetSettings()->SetPath(path);
2765 
2766  return true;
2767 }
2768 
2769 bool PluginManager::GetConfig(const wxString & key, int & value, int defval)
2770 {
2771  bool result = false;
2772 
2773  if (!key.IsEmpty())
2774  {
2775  result = GetSettings()->Read(key, &value, defval);
2776  }
2777 
2778  return result;
2779 }
2780 
2781 bool PluginManager::GetConfig(const wxString & key, wxString & value, const wxString & defval)
2782 {
2783  bool result = false;
2784 
2785  if (!key.IsEmpty())
2786  {
2787  wxString wxval = wxEmptyString;
2788 
2789  result = GetSettings()->Read(key, &wxval, defval);
2790 
2791  value = wxval;
2792  }
2793 
2794  return result;
2795 }
2796 
2797 bool PluginManager::GetConfig(const wxString & key, bool & value, bool defval)
2798 {
2799  bool result = false;
2800 
2801  if (!key.IsEmpty())
2802  {
2803  result = GetSettings()->Read(key, &value, defval);
2804  }
2805 
2806  return result;
2807 }
2808 
2809 bool PluginManager::GetConfig(const wxString & key, float & value, float defval)
2810 {
2811  bool result = false;
2812 
2813  if (!key.IsEmpty())
2814  {
2815  double dval = 0.0;
2816 
2817  result = GetSettings()->Read(key, &dval, (double) defval);
2818 
2819  value = (float) dval;
2820  }
2821 
2822  return result;
2823 }
2824 
2825 bool PluginManager::GetConfig(const wxString & key, double & value, double defval)
2826 {
2827  bool result = false;
2828 
2829  if (!key.IsEmpty())
2830  {
2831  result = GetSettings()->Read(key, &value, defval);
2832  }
2833 
2834  return result;
2835 }
2836 
2837 bool PluginManager::SetConfig(const wxString & key, const wxString & value)
2838 {
2839  bool result = false;
2840 
2841  if (!key.IsEmpty())
2842  {
2843  wxString wxval = value;
2844  result = GetSettings()->Write(key, wxval);
2845  if (result)
2846  {
2847  result = GetSettings()->Flush();
2848  }
2849  }
2850 
2851  return result;
2852 }
2853 
2854 bool PluginManager::SetConfig(const wxString & key, const int & value)
2855 {
2856  bool result = false;
2857 
2858  if (!key.IsEmpty())
2859  {
2860  result = GetSettings()->Write(key, value);
2861  if (result)
2862  {
2863  result = GetSettings()->Flush();
2864  }
2865  }
2866 
2867  return result;
2868 }
2869 
2870 bool PluginManager::SetConfig(const wxString & key, const bool & value)
2871 {
2872  bool result = false;
2873 
2874  if (!key.IsEmpty())
2875  {
2876  result = GetSettings()->Write(key, value);
2877  if (result)
2878  {
2879  result = GetSettings()->Flush();
2880  }
2881  }
2882 
2883  return result;
2884 }
2885 
2886 bool PluginManager::SetConfig(const wxString & key, const float & value)
2887 {
2888  bool result = false;
2889 
2890  if (!key.IsEmpty())
2891  {
2892  result = GetSettings()->Write(key, value);
2893  if (result)
2894  {
2895  result = GetSettings()->Flush();
2896  }
2897  }
2898 
2899  return result;
2900 }
2901 
2902 bool PluginManager::SetConfig(const wxString & key, const double & 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 /* Return value is a key for lookup in a config file */
2919 wxString PluginManager::SettingsPath(const PluginID & ID, bool shared)
2920 {
2921  // All the strings reported by PluginDescriptor and used in this function
2922  // persist in the plugin settings configuration file, so they should not
2923  // be changed across Audacity versions, or else compatibility of the
2924  // configuration files will break.
2925 
2926  if (mPlugins.find(ID) == mPlugins.end())
2927  {
2928  return wxEmptyString;
2929  }
2930 
2931  const PluginDescriptor & plug = mPlugins[ID];
2932 
2933  wxString id = GetPluginTypeString(plug.GetPluginType()) +
2934  wxT("_") +
2935  plug.GetEffectFamilyId() + // is empty for non-Effects
2936  wxT("_") +
2937  plug.GetVendor() +
2938  wxT("_") +
2939  (shared ? wxT("") : plug.GetSymbol().Internal());
2940 
2941  return SETROOT +
2942  ConvertID(id) +
2943  wxCONFIG_PATH_SEPARATOR +
2944  (shared ? wxT("shared") : wxT("private")) +
2945  wxCONFIG_PATH_SEPARATOR;
2946 }
2947 
2948 /* Return value is a key for lookup in a config file */
2949 wxString PluginManager::SharedGroup(const PluginID & ID, const wxString & group)
2950 {
2951  wxString path = SettingsPath(ID, true);
2952 
2953  wxFileName ff(group);
2954  if (!ff.GetName().IsEmpty())
2955  {
2956  path += ff.GetFullPath(wxPATH_UNIX) + wxCONFIG_PATH_SEPARATOR;
2957  }
2958 
2959  return path;
2960 }
2961 
2962 /* Return value is a key for lookup in a config file */
2963 wxString PluginManager::SharedKey(const PluginID & ID, const wxString & group, const wxString & key)
2964 {
2965  wxString path = SharedGroup(ID, group);
2966  if (path.IsEmpty())
2967  {
2968  return path;
2969  }
2970 
2971  return path + key;
2972 }
2973 
2974 /* Return value is a key for lookup in a config file */
2975 wxString PluginManager::PrivateGroup(const PluginID & ID, const wxString & group)
2976 {
2977  wxString path = SettingsPath(ID, false);
2978 
2979  wxFileName ff(group);
2980  if (!ff.GetName().IsEmpty())
2981  {
2982  path += ff.GetFullPath(wxPATH_UNIX) + wxCONFIG_PATH_SEPARATOR;
2983  }
2984 
2985  return path;
2986 }
2987 
2988 /* Return value is a key for lookup in a config file */
2989 wxString PluginManager::PrivateKey(const PluginID & ID, const wxString & group, const wxString & key)
2990 {
2991  wxString path = PrivateGroup(ID, group);
2992  if (path.IsEmpty())
2993  {
2994  return path;
2995  }
2996 
2997  return path + key;
2998 }
2999 
3000 // Sanitize the ID...not the best solution, but will suffice until this
3001 // is converted to XML. We use base64 encoding to preserve case.
3003 {
3004  if (ID.StartsWith(wxT("base64:")))
3005  {
3006  wxString id = ID.Mid(7);
3007  ArrayOf<char> buf{ id.Length() / 4 * 3 };
3008  id = wxString::FromUTF8(buf.get(), b64decode(id, buf.get()));
3009  return id;
3010  }
3011 
3012  const wxCharBuffer & buf = ID.ToUTF8();
3013  return wxT("base64:") + b64encode(buf, strlen(buf));
3014 }
3015 
3017 // Base64 en/decoding
3018 //
3019 // Original routines marked as public domain and found at:
3020 //
3021 // http://en.wikibooks.org/wiki/Algorithm_implementation/Miscellaneous/Base64
3022 //
3024 
3025 // Lookup table for encoding
3026 const static wxChar cset[] = wxT("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");
3027 const static char padc = wxT('=');
3028 
3029 wxString PluginManager::b64encode(const void *in, int len)
3030 {
3031  unsigned char *p = (unsigned char *) in;
3032  wxString out;
3033 
3034  unsigned long temp;
3035  for (int i = 0; i < len / 3; i++)
3036  {
3037  temp = (*p++) << 16; //Convert to big endian
3038  temp += (*p++) << 8;
3039  temp += (*p++);
3040  out += cset[(temp & 0x00FC0000) >> 18];
3041  out += cset[(temp & 0x0003F000) >> 12];
3042  out += cset[(temp & 0x00000FC0) >> 6];
3043  out += cset[(temp & 0x0000003F)];
3044  }
3045 
3046  switch (len % 3)
3047  {
3048  case 1:
3049  temp = (*p++) << 16; //Convert to big endian
3050  out += cset[(temp & 0x00FC0000) >> 18];
3051  out += cset[(temp & 0x0003F000) >> 12];
3052  out += padc;
3053  out += padc;
3054  break;
3055 
3056  case 2:
3057  temp = (*p++) << 16; //Convert to big endian
3058  temp += (*p++) << 8;
3059  out += cset[(temp & 0x00FC0000) >> 18];
3060  out += cset[(temp & 0x0003F000) >> 12];
3061  out += cset[(temp & 0x00000FC0) >> 6];
3062  out += padc;
3063  break;
3064  }
3065 
3066  return out;
3067 }
3068 
3069 int PluginManager::b64decode(const wxString &in, void *out)
3070 {
3071  int len = in.length();
3072  unsigned char *p = (unsigned char *) out;
3073 
3074  if (len % 4) //Sanity check
3075  {
3076  return 0;
3077  }
3078 
3079  int padding = 0;
3080  if (len)
3081  {
3082  if (in[len - 1] == padc)
3083  {
3084  padding++;
3085  }
3086 
3087  if (in[len - 2] == padc)
3088  {
3089  padding++;
3090  }
3091  }
3092 
3093  //const char *a = in.mb_str();
3094  //Setup a vector to hold the result
3095  unsigned long temp = 0; //Holds decoded quanta
3096  int i = 0;
3097  while (i < len)
3098  {
3099  for (int quantumPosition = 0; quantumPosition < 4; quantumPosition++)
3100  {
3101  unsigned char c = in[i];
3102  temp <<= 6;
3103 
3104  if (c >= 0x41 && c <= 0x5A)
3105  {
3106  temp |= c - 0x41;
3107  }
3108  else if (c >= 0x61 && c <= 0x7A)
3109  {
3110  temp |= c - 0x47;
3111  }
3112  else if (c >= 0x30 && c <= 0x39)
3113  {
3114  temp |= c + 0x04;
3115  }
3116  else if (c == 0x2B)
3117  {
3118  temp |= 0x3E;
3119  }
3120  else if (c == 0x2F)
3121  {
3122  temp |= 0x3F;
3123  }
3124  else if (c == padc)
3125  {
3126  switch (len - i)
3127  {
3128  case 1: //One pad character
3129  *p++ = (temp >> 16) & 0x000000FF;
3130  *p++ = (temp >> 8) & 0x000000FF;
3131  return p - (unsigned char *) out;
3132  case 2: //Two pad characters
3133  *p++ = (temp >> 10) & 0x000000FF;
3134  return p - (unsigned char *) out;
3135  }
3136  }
3137  i++;
3138  }
3139  *p++ = (temp >> 16) & 0x000000FF;
3140  *p++ = (temp >> 8) & 0x000000FF;
3141  *p++ = temp & 0x000000FF;
3142  }
3143 
3144  return p - (unsigned char *) out;
3145 }
3146 
3147 // These are defined out-of-line here, to keep IdentInterface free of other
3148 // #include directives.
3150 {
3151  return GetSymbol().Translation();
3152 }
const wxString & GetImporterIdentifier() const
static wxArrayString names()
Definition: Tags.cpp:697
#define KEY_VALID
AudacityPrefs * gPrefs
Definition: Prefs.cpp:73
const PluginDescriptor * GetFirstPlugin(int type)
#define wxPLURAL(sing, plur, n)
Definition: Internat.h:82
void OnClearAll(wxCommandEvent &evt)
void SetImporterIdentifier(const wxString &identifier)
#define KEY_DESCRIPTION
static CommandHandlerObject & ident(AudacityProject &project)
Definition: Menus.cpp:298
wxString mProviderID
wxArrayString FindPluginsForProvider(const PluginID &provider, const wxString &path)
void OnSelectAll(const CommandContext &context)
wxString GetVendor() const
PluginDescriptor & CreatePlugin(const PluginID &id, IdentInterface *ident, PluginType type)
IdentInterface * mInstance
static PluginID GetID(ModuleInterface *module)
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI...
Definition: ShuttleGui.h:409
virtual IdentInterfaceSymbol GetFamilyId()=0
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)
bool GetSubgroups(const wxString &group, wxArrayString &subgroups)
#define KEY_PATH
bool DiscoverProviders()
#define KEY_EFFECTTYPE_ANALYZE
static const PluginID & DefaultRegistrationCallback(ModuleInterface *provider, IdentInterface *ident)
#define REGROOT
bool HasPrivateConfigGroup(const PluginID &ID, const wxString &group)
IdentInterface * CreateInstance(const PluginID &provider, const wxString &path)
PluginRegistrationDialog(wxWindow *parent, EffectType type)
wxString PluginID
Definition: Types.h:209
static const char padc
virtual bool SupportsRealtime()=0
IdentInterface * CreateProviderInstance(const PluginID &provider, const wxString &path)
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
#define SETROOT
void SetEffectAutomatable(bool automatable)
void OnChangedVisibility(wxCommandEvent &evt)
bool IsProviderValid(const PluginID &provider, const wxString &path)
PluginType
Definition: PluginManager.h:32
static wxString PluginSettings()
Definition: FileNames.cpp:224
bool IsEffectAutomatable() const
bool IsEffectInteractive() const
wxString SharedKey(const PluginID &ID, const wxString &group, const wxString &key)
virtual IdentInterfaceSymbol GetSymbol()=0
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
const wxString & Translation() const
#define KEY_SYMBOL
#define KEY_EFFECTTYPE_HIDDEN
#define KEY_EFFECTAUTOMATABLE
void EndHorizontalLay()
const wxString & GetPath() const
bool IsInstantiated() const
const IdentInterfaceSymbol & GetSymbol(const PluginID &ID)
void AddPrompt(const wxString &Prompt)
Right aligned text string.
Definition: ShuttleGui.cpp:239
void DeleteInstance(const PluginID &provider, IdentInterface *instance)
An alternative to using wxWindowAccessible, which in wxWidgets 3.1.1 contained GetParent() which was ...
wxString ConvertID(const PluginID &ID)
void EndVerticalLay()
static const PluginID & AudacityCommandRegistrationCallback(ModuleInterface *provider, IdentInterface *ident)
virtual wxString GetVersion()=0
bool IsHidden(const PluginID &ID)
bool HasSharedConfigGroup(const PluginID &ID, const wxString &group)
void SetEffectInteractive(bool interactive)
#define KEY_EFFECTTYPE
EffectDefinitionInterface is a CommandDefinitionInterface that additionally tracks flag-functions for...
#define REGVERKEY
wxString mImporterIdentifier
IdentInterface * GetInstance()
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
void StartHorizontalLay(int PositionFlags=wxALIGN_CENTRE, int iProp=1)
const PluginDescriptor * GetNextPluginForEffectType(EffectType type)
bool IsEffectRealtime() const
#define KEY_EFFECTTYPE_PROCESS
wxListCtrl * AddListControlReportMode()
Definition: ShuttleGui.cpp:689
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:677
void OnSort(wxListEvent &evt)
bool IsPluginRegistered(const wxString &path) override
PluginMap mPlugins
IdentInterfaceSymbol mSymbol
ShuttleGui & Id(int id)
#define KEY_PROVIDERID
void SetStyle(int Style)
Definition: ShuttleGui.h:287
ProgressResult Update(int value, const wxString &message=wxEmptyString)
#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:46
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()
CommandDefinitionInterface is an IdentInterface (to name the command) along with a DefineParameters v...
void UnregisterPlugin(const PluginID &ID)
void OnSelectAll(wxCommandEvent &evt)
virtual bool IsDefault()=0
void EnablePlugin(const PluginID &ID, bool enable)
#define KEY_VENDOR
IdentInterfaceSymbol pairs a persistent string identifier used internally with an optional...
const IdentInterfaceSymbol & GetSymbol() const
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)
std::unique_ptr< wxFileConfig > mSettings
const PluginDescriptor * GetFirstPluginForEffectType(EffectType type)
wxString SettingsPath(const PluginID &ID, bool shared)
const wxString & GetTranslatedName()
void SetVersion(const wxString &version)
#define KEY_IMPORTEREXTENSIONS
EffectType GetEffectType() const
void SetSymbol(const IdentInterfaceSymbol &symbol)
_("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
EffectType mEffectType
#define KEY_EFFECTTYPE_TOOL
void SetInstance(IdentInterface *instance)
EffectType
const wxChar * name
Definition: Distortion.cpp:94
virtual IdentInterfaceSymbol GetVendor()=0
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:219
const PluginID & RegisterPlugin(ModuleInterface *module) override
const wxString & Internal() const
bool RemovePrivateConfigSubgroup(const PluginID &ID, const wxString &group) override
bool IsPluginEnabled(const PluginID &ID)
const wxString & GetProviderID() const
wxString name
void SetEffectDefault(bool dflt)
bool GetPrivateConfigSubgroups(const PluginID &ID, const wxString &group, wxArrayString &subgroups) override
const wxString & Msgid() const
std::unordered_map< wxString, wxArrayString > ProviderMap
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:484
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
IdentInterface * GetInstance(const PluginID &ID)
void SetProviderID(const PluginID &providerID)
virtual wxArrayString GetSupportedExtensions()=0
#define KEY_EFFECTREALTIME
static PluginManager & Get()
void OnCancel(wxCommandEvent &evt)
bool IsEnabled() const
virtual wxString GetPath()=0
IdentInterface provides name / vendor / version functions to identify plugins. It is what makes a cla...
const PluginDescriptor * GetPlugin(const PluginID &ID)
void SetImporterExtensions(const wxArrayString &extensions)
void SetEffectRealtime(bool realtime)
void LoadGroup(wxFileConfig *pRegistry, PluginType type)
void SetValid(bool valid)
static wxString PlugInDir()
The user plug-in directory (not a system one)
Definition: FileNames.cpp:214
bool HasGroup(const wxString &group)
wxButton * AddButton(const wxString &Text, int PositionFlags=wxALIGN_CENTRE)
Definition: ShuttleGui.cpp:341
wxRadioButton * AddRadioButton(const wxString &Prompt)
Definition: ShuttleGui.cpp:468
wxString mImporterFilterDesc
#define KEY_NAME
void SetEffectType(EffectType type)
static const wxChar cset[]
wxArrayString mImporterExtensions
void StartVerticalLay(int iProp=1)