Audacity 3.2.0
PluginRegistrationDialog.cpp
Go to the documentation of this file.
1/*!*********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 @file PluginRegistrationDialog.cpp
6
7 Paul Licameli split from PluginManager.cpp
8
9**********************************************************************/
11
12#include "EffectInterface.h"
14#include "ModuleManager.h"
15#include "PluginManager.h"
17#include "ShuttleGui.h"
18#include "AudacityMessageBox.h"
19#include "ProgressDialog.h"
20
21#include <set>
22#include <wx/setup.h> // for wxUSE_* macros
23#include <wx/app.h>
24#include <wx/defs.h>
25#include <wx/dir.h>
26#include <wx/dynlib.h>
27#include <wx/filename.h>
28#include <wx/listctrl.h>
29#include <wx/radiobut.h>
30#include <wx/wfstream.h>
31#include <wx/utils.h>
32
33#define DISABLE_STATE_NEW
34
35// ============================================================================
36//
37//
38//
39// ============================================================================
40#if wxUSE_ACCESSIBILITY
41#include "WindowAccessible.h"
42
43class CheckListAx final : public WindowAccessible
44{
45public:
46 CheckListAx(wxListCtrl * window);
47
48 virtual ~ CheckListAx();
49
50 // Retrieves the address of an IDispatch interface for the specified child.
51 // All objects must support this property.
52 wxAccStatus GetChild( int childId, wxAccessible **child ) override;
53
54 // Gets the number of children.
55 wxAccStatus GetChildCount( int *childCount ) override;
56
57 // Gets the default action for this object (0) or > 0 (the action for a child).
58 // Return wxACC_OK even if there is no action. actionName is the action, or the empty
59 // string if there is no action.
60 // The retrieved string describes the action that is performed on an object,
61 // not what the object does as a result. For example, a toolbar button that prints
62 // a document has a default action of "Press" rather than "Prints the current document."
63 wxAccStatus GetDefaultAction( int childId, wxString *actionName ) override;
64
65 // Returns the description for this object or a child.
66 wxAccStatus GetDescription( int childId, wxString *description ) override;
67
68 // Gets the window with the keyboard focus.
69 // If childId is 0 and child is NULL, no object in
70 // this subhierarchy has the focus.
71 // If this object has the focus, child should be 'this'.
72 wxAccStatus GetFocus( int *childId, wxAccessible **child ) override;
73
74 // Returns help text for this object or a child, similar to tooltip text.
75 wxAccStatus GetHelpText( int childId, wxString *helpText ) override;
76
77 // Returns the keyboard shortcut for this object or child.
78 // Return e.g. ALT+K
79 wxAccStatus GetKeyboardShortcut( int childId, wxString *shortcut ) override;
80
81 // Returns the rectangle for this object (id = 0) or a child element (id > 0).
82 // rect is in screen coordinates.
83 wxAccStatus GetLocation( wxRect& rect, int elementId ) override;
84
85 // Gets the name of the specified object.
86 wxAccStatus GetName( int childId, wxString *name ) override;
87
88 // Returns a role constant.
89 wxAccStatus GetRole( int childId, wxAccRole *role ) override;
90
91 // Gets a variant representing the selected children
92 // of this object.
93 // Acceptable values:
94 // - a null variant (IsNull() returns TRUE)
95 // - a list variant (GetType() == wxT("list"))
96 // - an integer representing the selected child element,
97 // or 0 if this object is selected (GetType() == wxT("long"))
98 // - a "void*" pointer to a wxAccessible child object
99 wxAccStatus GetSelections( wxVariant *selections ) override;
100
101 // Returns a state constant.
102 wxAccStatus GetState( int childId, long* state ) override;
103
104 // Returns a localized string representing the value for the object
105 // or child.
106 wxAccStatus GetValue( int childId, wxString *strValue ) override;
107
108 void SetSelected( int item, bool focused = true );
109
110private:
111 wxListCtrl *mParent;
112 int mLastId;
113};
114
115CheckListAx::CheckListAx( wxListCtrl * window )
116: WindowAccessible( window )
117{
118 mParent = window;
119 mLastId = -1;
120}
121
122CheckListAx::~CheckListAx()
123{
124}
125
126void CheckListAx::SetSelected( int item, bool focused )
127{
128 if (mLastId != -1)
129 {
130 NotifyEvent( wxACC_EVENT_OBJECT_SELECTIONREMOVE,
131 mParent,
132 wxOBJID_CLIENT,
133 mLastId );
134 mLastId = -1;
135 }
136
137 if (item != -1)
138 {
139 if (focused)
140 {
141 NotifyEvent( wxACC_EVENT_OBJECT_FOCUS,
142 mParent,
143 wxOBJID_CLIENT,
144 item + 1 );
145 }
146
147 NotifyEvent( wxACC_EVENT_OBJECT_SELECTION,
148 mParent,
149 wxOBJID_CLIENT,
150 item + 1 );
151
152 mLastId = item + 1;
153 }
154}
155
156// Retrieves the address of an IDispatch interface for the specified child.
157// All objects must support this property.
158wxAccStatus CheckListAx::GetChild( int childId, wxAccessible** child )
159{
160 if( childId == wxACC_SELF )
161 {
162 *child = this;
163 }
164 else
165 {
166 *child = NULL;
167 }
168
169 return wxACC_OK;
170}
171
172// Gets the number of children.
173wxAccStatus CheckListAx::GetChildCount( int *childCount )
174{
175 *childCount = mParent->GetItemCount();
176
177 return wxACC_OK;
178}
179
180// Gets the default action for this object (0) or > 0 (the action for a child).
181// Return wxACC_OK even if there is no action. actionName is the action, or the empty
182// string if there is no action.
183// The retrieved string describes the action that is performed on an object,
184// not what the object does as a result. For example, a toolbar button that prints
185// a document has a default action of "Press" rather than "Prints the current document."
186wxAccStatus CheckListAx::GetDefaultAction( int WXUNUSED(childId), wxString *actionName )
187{
188 actionName->clear();
189
190 return wxACC_OK;
191}
192
193// Returns the description for this object or a child.
194wxAccStatus CheckListAx::GetDescription( int WXUNUSED(childId), wxString *description )
195{
196 description->clear();
197
198 return wxACC_OK;
199}
200
201// Gets the window with the keyboard focus.
202// If childId is 0 and child is NULL, no object in
203// this subhierarchy has the focus.
204// If this object has the focus, child should be 'this'.
205wxAccStatus CheckListAx::GetFocus( int *childId, wxAccessible **child )
206{
207 *childId = 0;
208 *child = this;
209
210 return wxACC_OK;
211}
212
213// Returns help text for this object or a child, similar to tooltip text.
214wxAccStatus CheckListAx::GetHelpText( int WXUNUSED(childId), wxString *helpText )
215{
216 helpText->clear();
217
218 return wxACC_OK;
219}
220
221// Returns the keyboard shortcut for this object or child.
222// Return e.g. ALT+K
223wxAccStatus CheckListAx::GetKeyboardShortcut( int WXUNUSED(childId), wxString *shortcut )
224{
225 shortcut->clear();
226
227 return wxACC_OK;
228}
229
230// Returns the rectangle for this object (id = 0) or a child element (id > 0).
231// rect is in screen coordinates.
232wxAccStatus CheckListAx::GetLocation( wxRect& rect, int elementId )
233{
234 if( elementId == wxACC_SELF )
235 {
236 rect = mParent->GetRect();
237 rect.SetPosition( mParent->GetParent()->ClientToScreen( rect.GetPosition() ) );
238 }
239 else
240 {
241 if( elementId <= mParent->GetItemCount() )
242 {
243 mParent->GetItemRect( elementId - 1, rect, wxLIST_RECT_LABEL );
244 rect.SetPosition( mParent->ClientToScreen( rect.GetPosition() ) );
245 }
246 }
247
248 return wxACC_OK;
249}
250
251// Gets the name of the specified object.
252wxAccStatus CheckListAx::GetName( int WXUNUSED(childId), wxString *name )
253{
254 *name = mParent->GetName();
255
256 return wxACC_OK;
257}
258
259// Returns a role constant.
260wxAccStatus CheckListAx::GetRole( int childId, wxAccRole *role )
261{
262 if( childId == wxACC_SELF )
263 {
264 *role = wxROLE_SYSTEM_LIST;
265 }
266 else
267 {
268 *role = wxROLE_SYSTEM_LISTITEM;
269 }
270
271 return wxACC_OK;
272}
273
274// Gets a variant representing the selected children
275// of this object.
276// Acceptable values:
277// - a null variant (IsNull() returns TRUE)
278// - a list variant (GetType() == wxT("list"))
279// - an integer representing the selected child element,
280// or 0 if this object is selected (GetType() == wxT("long"))
281// - a "void*" pointer to a wxAccessible child object
282wxAccStatus CheckListAx::GetSelections( wxVariant * WXUNUSED(selections) )
283{
284 return wxACC_NOT_IMPLEMENTED;
285}
286
287// Returns a state constant.
288wxAccStatus CheckListAx::GetState( int childId, long *pState )
289{
290 int flag = wxACC_STATE_SYSTEM_FOCUSABLE;
291
292 if( childId == wxACC_SELF )
293 {
294 flag |= wxACC_STATE_SYSTEM_FOCUSED;
295 }
296 else
297 {
298 wxListItem item;
299
300 item.SetId( childId - 1 );
301 item.SetState( wxLIST_STATE_FOCUSED | wxLIST_STATE_SELECTED );
302 item.SetMask( wxLIST_MASK_STATE );
303
304 if( mParent->GetItem( item ) )
305 {
306 flag |= wxACC_STATE_SYSTEM_SELECTABLE;
307
308 long state = item.GetState();
309
310 if( state & wxLIST_STATE_FOCUSED )
311 {
312 flag |= wxACC_STATE_SYSTEM_FOCUSED;
313 }
314
315 if( state & wxLIST_STATE_SELECTED )
316 {
317 flag |= wxACC_STATE_SYSTEM_SELECTED;
318 }
319 }
320 }
321
322 *pState = flag;
323
324 return wxACC_OK;
325}
326
327// Returns a localized string representing the value for the object
328// or child.
329wxAccStatus CheckListAx::GetValue( int childId, wxString *strValue )
330{
331 if( childId == 0 )
332 {
333 return wxACC_OK;
334 }
335 else
336 {
337 *strValue = mParent->GetItemText( childId - 1 );
338 }
339
340 return wxACC_OK;
341}
342
343#endif
344enum
345{
348#ifndef DISABLE_STATE_NEW
349 STATE_New,
350#endif
351
354
355enum
356{
357 ID_ShowAll = 10000,
360#ifndef DISABLE_STATE_NEW
361 ID_ShowNew,
362#endif
369};
370
371enum
372{
376
379
381 EVT_LIST_COL_CLICK(ID_List, PluginRegistrationDialog::OnSort)
392#ifndef DISABLE_STATE_NEW
393 EVT_RADIOBUTTON(ID_ShowNew, PluginRegistrationDialog::OnChangedVisibility)
394#endif
396
398: wxDialogWrapper(parent,
399 wxID_ANY,
400 XO("Manage Plugins"),
401 wxDefaultPosition, wxDefaultSize,
402 wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
403{
404 mEffects = NULL;
405 SetName();
406
407 mStates.resize(STATE_COUNT);
408 mStates[STATE_Enabled] = _("Enabled");
409 mStates[STATE_Disabled] = _("Disabled");
410#ifndef DISABLE_STATE_NEW
411 mStates[STATE_New] = _("New");
412#endif
413
414 mSortColumn = COL_Name;
415 mSortDirection = 1;
416
417 Populate();
418
419 DoSort( mSortColumn );
420}
421
423{
424 //------------------------- Main section --------------------
425 ShuttleGui S(this, eIsCreating);
427 // ----------------------- End of main section --------------
428}
429
432{
433 S.StartVerticalLay(true);
434 {
435 /*i18n-hint: The dialog shows a list of plugins with check-boxes
436 beside each one.*/
437// S.StartStatic(XO("Effects"), true);
438 S.StartVerticalLay();
439 {
440 S.StartHorizontalLay(wxEXPAND, 0);
441 {
442 S.StartHorizontalLay(wxALIGN_LEFT, 0);
443 {
444 S.AddPrompt(XXO("Select effects, click the Enable or Disable button, then click OK."));
445 }
446 S.EndHorizontalLay();
447
448 S.StartHorizontalLay(wxCENTER, 1);
449 {
450 S.AddSpace(1);
451 }
452 S.EndHorizontalLay();
453
454 S.StartHorizontalLay(wxALIGN_NOT | wxALIGN_LEFT, 0);
455 {
456 wxRadioButton *rb;
457
458 /* i18n-hint: This is before radio buttons selecting which effects to show */
459 S.AddPrompt(XXO("Show:"));
460 rb = S.Id(ID_ShowAll)
461 /* i18n-hint: Radio button to show all effects */
462 .Name(XO("Show all"))
463 /* i18n-hint: Radio button to show all effects */
464 .AddRadioButton(XXO("&All"));
465#if wxUSE_ACCESSIBILITY
466 // so that name can be set on a standard control
467 rb->SetAccessible(safenew WindowAccessible(rb));
468#endif
469
470 rb = S.Id(ID_ShowDisabled)
471 /* i18n-hint: Radio button to show just the currently disabled effects */
472 .Name(XO("Show disabled"))
473 /* i18n-hint: Radio button to show just the currently disabled effects */
474 .AddRadioButtonToGroup(XXO("D&isabled"));
475#if wxUSE_ACCESSIBILITY
476 // so that name can be set on a standard control
477 rb->SetAccessible(safenew WindowAccessible(rb));
478#endif
479
480 rb = S.Id(ID_ShowEnabled)
481 /* i18n-hint: Radio button to show just the currently enabled effects */
482 .Name(XO("Show enabled"))
483 /* i18n-hint: Radio button to show just the currently enabled effects */
484 .AddRadioButtonToGroup(XXO("E&nabled"));
485#if wxUSE_ACCESSIBILITY
486 // so that name can be set on a standard control
487 rb->SetAccessible(safenew WindowAccessible(rb));
488#endif
489
490#ifndef DISABLE_STATE_NEW
491 rb = S.Id(ID_ShowNew)
492 /* i18n-hint: Radio button to show just the newly discovered effects */
493 .Name(XO("Show new"))
494 /* i18n-hint: Radio button to show just the newly discovered effects */
495 .AddRadioButtonToGroup(XXO("Ne&w"));
496#if wxUSE_ACCESSIBILITY
497 // so that name can be set on a standard control
498 rb->SetAccessible(safenew WindowAccessible(rb));
499#endif
500#endif
501 }
502 S.EndHorizontalLay();
503 }
504 S.EndHorizontalLay();
505
506 mEffects = S.Id(ID_List)
507 .Style(wxSUNKEN_BORDER | wxLC_REPORT | wxLC_HRULES | wxLC_VRULES )
508 .ConnectRoot(wxEVT_KEY_DOWN,
510 .AddListControlReportMode({ XO("Name"), XO("State"), XO("Path") });
511#if wxUSE_ACCESSIBILITY
512 mEffects->SetAccessible(mAx = safenew CheckListAx(mEffects));
513#endif
514
515 S.StartHorizontalLay(wxALIGN_LEFT | wxEXPAND, 0);
516 {
517 S.Id(ID_SelectAll).AddButton(XXO("&Select All"));
518 S.Id(ID_ClearAll).AddButton(XXO("C&lear All"));
519 S.Id(ID_Rescan).AddButton(XXO("Rescan"));
520
521 S.StartHorizontalLay(wxALIGN_CENTER);
522 {
523 S.AddSpace(1);
524 }
525 S.EndHorizontalLay();
526
527 S.Id(ID_Enable).AddButton(XXO("&Enable"));
528 S.Id(ID_Disable).AddButton(XXO("&Disable"));
529 }
530 S.EndHorizontalLay();
531 }
532// S.EndStatic();
533 S.EndVerticalLay();
534
535 S.AddStandardButtons(eOkButton | eCancelButton);
536 }
537 S.EndVerticalLay();
538
539 std::vector<int> colWidths;
540 for (int i = 0, cnt = mEffects->GetColumnCount(); i < cnt; i++)
541 {
542 colWidths.push_back(0);
543 }
544
545 for (int i = 0, cnt = mStates.size(); i < cnt; i++)
546 {
547 int x;
548 mEffects->GetTextExtent(mStates[i], &x, NULL);
549 colWidths[COL_State] = wxMax(colWidths[COL_State], x + 4); // 2 pixel margin on each side
550 }
551
554
555 for (const auto& item : mItems)
556 {
557 const auto& itemData = item.second;
558
559 int x;
560 mEffects->GetTextExtent(itemData.name, &x, NULL);
561 colWidths[COL_Name] = wxMax(colWidths[COL_Name], x);
562
563 mEffects->GetTextExtent(itemData.path, &x, NULL);
564 if (x > colWidths[COL_Path])
565 {
566 mLongestPath = itemData.path;
567 }
568 colWidths[COL_Path] = wxMax(colWidths[COL_Path], x);
569 }
570
571 wxRect r = wxGetClientDisplayRect();
572
573 int maxW = 0;
574 for (int i = 0, cnt = mEffects->GetColumnCount(); i < cnt; i++)
575 {
576 int w = colWidths[i] + /* fudge */ 10;
577 mEffects->SetColumnWidth(i, w);
578 maxW += w;
579 }
580
581 // Keep dialog from getting too wide
582 int w = r.GetWidth() - (GetClientSize().GetWidth() - mEffects->GetSize().GetWidth());
583 mEffects->SetMinSize({ std::min(maxW, w), 200 });
584 mEffects->SetMaxSize({ w, -1 });
585
587
588 Layout();
589 Fit();
590
591 wxSize sz = GetSize();
592 sz.SetWidth(wxMin(sz.GetWidth(), r.GetWidth()));
593 sz.SetHeight(wxMin(sz.GetHeight(), r.GetHeight()));
594 SetMinSize(sz);
595
596 // Parent window is usually not there yet, so centre on screen rather than on parent.
597 CenterOnScreen();
598
599 if (mEffects->GetItemCount() > 0)
600 {
601 // Make sure first item is selected/focused.
602 mEffects->SetFocus();
603 mEffects->SetItemState(0, wxLIST_STATE_FOCUSED | wxLIST_STATE_SELECTED, wxLIST_STATE_FOCUSED | wxLIST_STATE_SELECTED);
604#if wxUSE_ACCESSIBILITY
605 mAx->SetSelected(0);
606#endif
607 }
608
609}
610
612{
613 for (auto& plug : pm.AllPlugins())
614 {
615 PluginType plugType = plug.GetPluginType();
616 if (plugType != PluginTypeEffect && plugType != PluginTypeStub)
617 continue;
618
619 const auto& path = plug.GetPath();
620 ItemData& item = mItems[path]; // will create NEW entry
621 item.plugs.push_back(&plug);
622 item.path = path;
623 item.state = plug.IsEnabled() ? STATE_Enabled : STATE_Disabled;
624 item.valid = plug.IsValid();
625
626 if (plugType == PluginTypeEffect)
627 {
628 item.name = plug.GetSymbol().Translation();
629 }
630 // This is not right and will not work when other plugin types are added.
631 // But it's presumed that the plugin manager dialog will be fully developed
632 // by then.
633 else if (plugType == PluginTypeStub)
634 {
635 wxFileName fname{ path };
636 item.name = fname.GetName().Trim(false).Trim(true);
637#ifndef DISABLE_STATE_NEW
638 if (!item.valid)
639 {
640 item.state = STATE_New;
641 }
642#endif
643 }
644 }
645}
646
648{
649 mFilter = filter;
650
651 mEffects->DeleteAllItems();
652
653 int i = 0;
654 for (ItemDataMap::iterator iter = mItems.begin(); iter != mItems.end(); ++iter)
655 {
656 ItemData & item = iter->second;
657 bool add = false;
658
659 switch (mFilter)
660 {
661 case ID_ShowAll:
662 add = true;
663 break;
664#ifndef DISABLE_STATE_NEW
665 case ID_ShowNew:
666 if (item.state == STATE_New)
667 {
668 add = true;
669 }
670 break;
671#endif
672 case ID_ShowEnabled:
673 if (item.state == STATE_Enabled)
674 {
675 add = true;
676 }
677 break;
678 case ID_ShowDisabled:
679 if (item.state == STATE_Disabled)
680 {
681 add = true;
682 }
683 break;
684 }
685
686 if (add)
687 {
688 mEffects->InsertItem(i, item.name);
689 mEffects->SetItem(i, COL_State, mStates[item.state]);
690 mEffects->SetItem(i, COL_Path, item.path);
691 mEffects->SetItemPtrData(i, (wxUIntPtr) &item);
692
693 ++i;
694 }
695 }
696
697 mEffects->SortItems(SortCompare, (wxUIntPtr) this);
698
699 if (mEffects->GetItemCount() > 0)
700 {
701 // Make sure first item is selected/focused.
702// mEffects->SetFocus();
703 mEffects->SetItemState(0, wxLIST_STATE_FOCUSED|wxLIST_STATE_SELECTED, wxLIST_STATE_FOCUSED|wxLIST_STATE_SELECTED);
704#if wxUSE_ACCESSIBILITY
705 mAx->SetSelected(0, false);
706#endif
707 }
708}
709
710void PluginRegistrationDialog::SetState(int i, bool toggle, bool state)
711{
712 wxListItem li;
713
714 li.m_mask = wxLIST_MASK_DATA;
715 li.m_itemId = i;
716
717 mEffects->GetItem(li);
718
719 ItemData *item = (ItemData *) li.m_data;
720
721#ifndef DISABLE_STATE_NEW
722 // If changing the state of a "New" (stub) entry, then we mark it as valid
723 // since it will either be registered if "Enabled" or ignored if "Disabled".
724 if (item->state == STATE_New)
725 {
726 item->valid = true;
727 }
728#endif
729
730 if (toggle)
731 {
733 }
734 else
735 {
736 item->state = state;
737 }
738
739#ifndef DISABLE_STATE_NEW
740 if (mFilter == ID_ShowNew && item->state != STATE_New)
741 {
742 mEffects->DeleteItem(i);
743 }
744 else//if
745#endif
746 if (mFilter == ID_ShowDisabled && item->state != STATE_Disabled)
747 {
748 mEffects->DeleteItem(i);
749 }
750 else if (mFilter == ID_ShowEnabled && item->state != STATE_Enabled)
751 {
752 mEffects->DeleteItem(i);
753 }
754 else
755 {
756 mEffects->SetItem(i, COL_State, mStates[item->state]);
757#if wxUSE_ACCESSIBILITY
758 mAx->SetSelected(i);
759#endif
760 }
761}
762
763int wxCALLBACK PluginRegistrationDialog::SortCompare(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData)
764{
766 ItemData *i1 = (ItemData *) item1;
767 ItemData *i2 = (ItemData *) item2;
768
769 return dlg->SortCompare(i1, i2);
770}
771
773{
774 // This function is a three-valued comparator
775
776 wxString *str1;
777 wxString *str2;
778
779 switch (mSortColumn)
780 {
781 case COL_Name:
782 str1 = &item1->name;
783 str2 = &item2->name;
784 break;
785 case COL_State:
786 str1 = &mStates[item1->state];
787 str2 = &mStates[item2->state];
788 break;
789 case COL_Path:
790 str1 = &item1->path;
791 str2 = &item2->path;
792 break;
793 default:
794 return 0;
795 }
796
797 return str2->CmpNoCase(*str1) * mSortDirection;
798}
799
801{
802 // Go and show the relevant items.
803 RegenerateEffectsList(evt.GetId());
804}
805
806void PluginRegistrationDialog::OnSort(wxListEvent & evt)
807{
808 int col = evt.GetColumn();
809 DoSort( col );
810}
811
813{
814 if (col != mSortColumn)
815 {
816 mSortDirection = 1;
817 }
818 else
819 {
820 mSortDirection *= -1;
821 }
822
823 mSortColumn = col;
824 mEffects->SortItems(SortCompare, (wxUIntPtr) this);
825
826 // Without a refresh, wxMac doesn't redisplay the list properly after a sort
827 mEffects->Refresh();
828}
829
831{
832 switch (evt.GetKeyCode())
833 {
834 case WXK_SPACE:
835 {
836 int item = mEffects->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_FOCUSED);
837 if (item != wxNOT_FOUND)
838 {
839 SetState(item, true);
840 }
841 }
842 break;
843
844 case WXK_RETURN:
845 // Don't know why wxListCtrls prevent default dialog action,
846 // but they do, so handle it.
847 EmulateButtonClickIfPresent(GetAffirmativeId());
848 break;
849
850 default:
851 evt.Skip();
852 break;
853 }
854}
855
856void PluginRegistrationDialog::OnSelectAll(wxCommandEvent & WXUNUSED(evt))
857{
858 for (int i = 0, cnt = mEffects->GetItemCount(); i < cnt; i++)
859 {
860 mEffects->SetItemState(i, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
861 }
862}
863
864void PluginRegistrationDialog::OnClearAll(wxCommandEvent & WXUNUSED(evt))
865{
866 for (int i = 0, cnt = mEffects->GetItemCount(); i < cnt; i++)
867 {
868 mEffects->SetItemState(i, 0, wxLIST_STATE_SELECTED);
869 }
870}
871
872void PluginRegistrationDialog::OnRescan(wxCommandEvent& WXUNUSED(evt))
873{
874 mItems.clear();
875 mEffects->DeleteAllItems();
876 mEffects->Update();
877
878 wxTheApp->CallAfter([this] {
879 std::set<PluginPath> disabledPlugins;
880 std::vector<wxString> failedPlugins;
881
882 auto& pm = PluginManager::Get();
883
884 // Record list of plugins that are currently disabled
885 for (auto& plug : pm.AllPlugins())
886 {
887 PluginType plugType = plug.GetPluginType();
888 if (plugType != PluginTypeEffect && plugType != PluginTypeStub)
889 continue;
890
891 if (!plug.IsEnabled())
892 disabledPlugins.insert(plug.GetPath());
893 }
894
895 pm.ClearEffectPlugins();
896
897 auto newPlugins = PluginManager::Get().CheckPluginUpdates();
898 if (!newPlugins.empty())
899 {
900 PluginStartupRegistration reg(newPlugins);
901 reg.Run();
902
903 failedPlugins = reg.GetFailedPluginsPaths();
904 }
905
906 // Disable all plugins which were previously disabled
907 for (auto& plug : pm.AllPlugins())
908 {
909 PluginType plugType = plug.GetPluginType();
910 if (plugType != PluginTypeEffect && plugType != PluginTypeStub)
911 continue;
912
913 const auto& path = plug.GetPath();
914 if (disabledPlugins.find(path) != disabledPlugins.end())
915 plug.SetEnabled(false);
916 }
917
918 pm.Save();
919 pm.NotifyPluginsChanged();
920
921 if (!failedPlugins.empty())
922 {
923 auto dialog = safenew IncompatiblePluginsDialog(this, wxID_ANY, ScanType::Manual, failedPlugins);
924 dialog->ShowModal();
925 }
926
929 });
930}
931
932void PluginRegistrationDialog::OnEnable(wxCommandEvent & WXUNUSED(evt))
933{
934 std::vector<long> items;
935
936 {
937 long i = mEffects->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
938 while (i != wxNOT_FOUND)
939 {
940 items.insert(items.begin(), i);
941 i = mEffects->GetNextItem(i, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
942 }
943 }
944
945 for (size_t i = 0, cnt = items.size(); i < cnt; i++)
946 {
947 SetState(items[i], false, STATE_Enabled);
948 }
949}
950
951void PluginRegistrationDialog::OnDisable(wxCommandEvent & WXUNUSED(evt))
952{
953 std::vector<long> items;
954
955 {
956 long i = mEffects->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
957 while (i != wxNOT_FOUND)
958 {
959 items.insert(items.begin(), i);
960 i = mEffects->GetNextItem(i, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
961 }
962 }
963
964 for (size_t i = 0, cnt = items.size(); i < cnt; i++)
965 {
966 SetState(items[i], false, STATE_Disabled);
967 }
968}
969
970void PluginRegistrationDialog::OnOK(wxCommandEvent & WXUNUSED(evt))
971{
974
975 int enableCount = 0;
976 for (ItemDataMap::iterator iter = mItems.begin(); iter != mItems.end(); ++iter)
977 {
978 ItemData & item = iter->second;
979 wxString path = item.path;
980
981 if (item.state == STATE_Enabled && item.plugs[0]->GetPluginType() == PluginTypeStub)
982 {
983 enableCount++;
984 }
985 }
986
987 wxString last3 = mLongestPath + wxT("\n") +
988 mLongestPath + wxT("\n") +
989 mLongestPath + wxT("\n");
990
991 auto msg = XO("Enabling effects or commands:\n\n%s").Format( last3 );
992
993 // Make sure the progress dialog is deleted before we call EndModal() or
994 // we will leave the project window in an unusable state on OSX.
995 // See bug #1192.
996 {
997 ProgressDialog progress{
998 Verbatim( GetTitle() ), msg, pdlgHideStopButton };
999 progress.CenterOnParent();
1000
1001 int i = 0;
1002 for (ItemDataMap::iterator iter = mItems.begin(); iter != mItems.end(); ++iter)
1003 {
1004 ItemData & item = iter->second;
1005 wxString path = item.path;
1006
1007 if (item.state == STATE_Enabled && item.plugs[0]->GetPluginType() == PluginTypeStub)
1008 {
1009 last3 = last3.AfterFirst(wxT('\n')) + item.path + wxT("\n");
1010 auto status = progress.Update(++i, enableCount,
1011 XO("Enabling effect or command:\n\n%s").Format( last3 ));
1012 if (status == ProgressResult::Cancelled)
1013 {
1014 break;
1015 }
1016
1017 TranslatableString errMsgs;
1018
1019 // Try to register the plugin via each provider until one succeeds
1020 for (size_t j = 0, cntj = item.plugs.size(); j < cntj; j++)
1021 {
1022 TranslatableString errMsg;
1023 if (mm.RegisterEffectPlugin(item.plugs[j]->GetProviderID(), path,
1024 errMsg))
1025 {
1026 for (auto plug : item.plugs)
1028 plug->GetProviderID() + wxT("_") + path);
1029 // Bug 1893. We've found a provider that works.
1030 // Error messages from any that failed are no longer useful.
1031 errMsgs = {};
1032 break;
1033 }
1034 else
1035 {
1036 if (!errMsgs.empty())
1037 errMsgs.Join( errMsg, '\n' );
1038 else
1039 errMsgs = errMsg;
1040 }
1041 }
1042 if (!errMsgs.empty())
1044 XO("Effect or Command at %s failed to register:\n%s")
1045 .Format( path, errMsgs ) );
1046 }
1047#ifndef DISABLE_STATE_NEW
1048 else if (item.state == STATE_New) {
1049 for (auto plug : item.plugs)
1050 plug->SetValid(false);
1051 }
1052#endif
1053 else {
1054 for (auto plug : item.plugs) {
1055 plug->SetEnabled(item.state == STATE_Enabled);
1056 plug->SetValid(item.valid);
1057 }
1058 }
1059 }
1060
1061 pm.Save();
1063 }
1064
1065 EndModal(wxID_OK);
1066}
1067
1068void PluginRegistrationDialog::OnCancel(wxCommandEvent & WXUNUSED(evt))
1069{
1070 EndModal(wxID_CANCEL);
1071}
wxT("CloseDown"))
int AudacityMessageBox(const TranslatableString &message, const TranslatableString &caption, long style, wxWindow *parent, int x, int y)
END_EVENT_TABLE()
int min(int a, int b)
EVT_BUTTON(wxID_NO, DependencyDialog::OnNo) EVT_BUTTON(wxID_YES
const TranslatableString name
Definition: Distortion.cpp:76
XO("Cut/Copy/Paste")
XXO("&Cut/Copy/Paste Toolbar")
#define _(s)
Definition: Internat.h:73
#define safenew
Definition: MemoryX.h:10
PluginType
@ PluginTypeStub
@ PluginTypeEffect
#define DISABLE_STATE_NEW
@ pdlgHideStopButton
@ eIsCreating
Definition: ShuttleGui.h:37
@ eOkButton
Definition: ShuttleGui.h:594
@ eCancelButton
Definition: ShuttleGui.h:595
#define S(N)
Definition: ToChars.cpp:64
TranslatableString Verbatim(wxString str)
Require calls to the one-argument constructor to go through this distinct global function name.
static std::once_flag flag
Abstract base class used in importing a file.
static ModuleManager & Get()
bool RegisterEffectPlugin(const PluginID &provider, const PluginPath &path, TranslatableString &errMsg)
PluginManager maintains a list of all plug ins. That covers modules, effects, generators,...
Definition: PluginManager.h:51
std::map< wxString, std::vector< wxString > > CheckPluginUpdates()
Ensures that all currently registered plugins still exist and scans for new ones.
void Save()
Save to preferences.
void UnregisterPlugin(const PluginID &ID)
void NotifyPluginsChanged()
Range AllPlugins()
static PluginManager & Get()
void PopulateOrExchange(ShuttleGui &S)
Defines the dialog and does data exchange with it.
void OnClearAll(wxCommandEvent &evt)
void OnDisable(wxCommandEvent &evt)
void SetState(int i, bool toggle, bool state=true)
void PopulateItemsList(PluginManager &pm)
void OnCancel(wxCommandEvent &evt)
static int wxCALLBACK SortCompare(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData)
void OnRescan(wxCommandEvent &evt)
void RegenerateEffectsList(int iShowWhat)
void OnChangedVisibility(wxCommandEvent &evt)
void OnOK(wxCommandEvent &evt)
void OnEnable(wxCommandEvent &evt)
void OnSelectAll(wxCommandEvent &evt)
ProgressDialog Class.
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
Definition: ShuttleGui.h:625
Holds a msgid for the translation catalog; may also bind format arguments.
TranslatableString & Join(TranslatableString arg, const wxString &separator={}) &
Append another translatable string.
An alternative to using wxWindowAccessible, which in wxWidgets 3.1.1 contained GetParent() which was ...
BuiltinCommandsModule::Registration< CompareAudioCommand > reg
void add(const T *src1, const T *src2, T *dst, int32_t n)
Definition: VectorOps.h:38
std::vector< PluginDescriptor * > plugs