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