Audacity 3.2.0
EffectsPrefs.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 EffectsPrefs.cpp
6
7 Brian Gunlogson
8 Joshua Haberman
9 Dominic Mazzoni
10 James Crook
11
12
13*******************************************************************//*******************************************************************/
19
20
21#include "EffectsPrefs.h"
22
23#include <wx/choice.h>
24#include <wx/defs.h>
25#include <wx/button.h>
26#include <wx/sizer.h>
27#include <wx/statbox.h>
28#include <wx/scrolwin.h>
29
30#include "PluginManager.h"
32#include "MenuCreator.h"
33#include "ModuleManager.h"
34#include "Prefs.h"
35#include "ShuttleGui.h"
36
37#if wxUSE_ACCESSIBILITY
38#include "WindowAccessible.h"
39#endif
40
41wxDEFINE_EVENT(EVT_PLUGIN_LOCATIONS_CHANGED, wxCommandEvent);
42
44{
45 //Helper container that stores paths during editing.
46 //Workaround: wxTextCtrl::GetValue may return previous value if dialog
47 //gets closed while text control is still has focus.
48 std::vector<wxString> mPaths;
49 //Container never shrinks, when row gets removed from the list
50 //corresponding pointer is nulled.
51 std::vector<wxTextCtrl*> mPathCtrls;
52 //Sizer that holds rows (which are sizers too). Each row
53 //consists of three controls: path text, browse button, remove button.
54 //When created this controls are assigned with unique ids which could be
55 //mapped to the indexes in mPathCtrls.
56 wxSizer* mRows{};
57 wxButton* mAddNewLocation{};
58
59 //Returns the ID for the first element in the row that should be assigned to the
60 //element stored at index in mPathCtrls.
61 static int BaseRowIdForIndex(int index)
62 {
63 return wxID_HIGHEST + index * 3;
64 }
65 //Maps ID of any control in the row to the index in mPathCtrls.
66 static int IndexForId(int id)
67 {
68 return (id - wxID_HIGHEST) / 3;
69 }
70
71public:
72
73 template<typename... Args>
74 EffectsLocationPanel(Args&&... args)
75 : wxPanelWrapper(std::forward<Args>(args)...)
76 {
77 InitUI();
78 }
79
80 void InitUI()
81 {
82 auto mainSizer = std::make_unique<wxBoxSizer>(wxVERTICAL);
83 mainSizer->Add(mRows = safenew wxBoxSizer(wxVERTICAL), 0, wxEXPAND);
84 mainSizer->Add(
85 mAddNewLocation = safenew wxButton(this, wxID_ANY, _("Add new location")),
86 0, wxALL, 3
87 );
88 SetSizer(mainSizer.release());
89
91 }
92
93 void AddLocation(const PluginPath& path, bool setFocus = false)
94 {
95 const auto baseId = BaseRowIdForIndex(mPathCtrls.size());
96
97 auto rowSizer = std::make_unique<wxBoxSizer>(wxHORIZONTAL);
98 wxTextCtrl* text;
99 wxButton* remove;
100 wxButton* browse;
101 rowSizer->Add(
102 text = safenew wxTextCtrl(this, baseId, path),
103 1, wxEXPAND | wxALL, 3
104 );
105 rowSizer->Add(
106 browse = safenew wxButton(this, baseId + 1, _("Browse...")),
107 0, wxALL, 3
108 );
109 rowSizer->Add(
110 remove = safenew wxButton(this, baseId + 2, _("Remove")),
111 0, wxALL, 3
112 );
113 text->Bind(wxEVT_TEXT, [this](wxCommandEvent& evt)
114 {
115 const auto index = IndexForId(evt.GetId());
116 mPaths[index] = evt.GetString();
117 evt.Skip();
118 });
119
120 browse->Bind(wxEVT_BUTTON, &EffectsLocationPanel::OnBrowseClicked, this);
121 remove->Bind(wxEVT_BUTTON, &EffectsLocationPanel::OnRemoveClicked, this);
122 mPathCtrls.push_back(text);
123 mPaths.push_back(path);
124
125 mRows->Add(rowSizer.release(), 0, wxEXPAND);
126
127 mAddNewLocation->MoveAfterInTabOrder(remove);
128
129 if(setFocus)
130 text->SetFocus();
131
132 wxPostEvent(this, wxCommandEvent(EVT_PLUGIN_LOCATIONS_CHANGED));
133 }
134
135 void AddLocations(const PluginPaths& paths)
136 {
137 for(const auto& location : paths)
138 AddLocation(location);
139 }
140
142 {
143 PluginPaths paths;
144 for(const auto& text : mPaths)
145 {
146 if(!text.IsEmpty())
147 paths.push_back(text);
148 }
149 return paths;
150 }
151
152 void OnAddNewLocationClicked(wxCommandEvent&)
153 {
154 AddLocation("", true);
155 }
156
157 void OnRemoveClicked(wxCommandEvent& evt)
158 {
159 const auto index = IndexForId(evt.GetId());
160 const auto baseId = BaseRowIdForIndex(index);
161 //When item is removed from the sizer the tail gets shifted,
162 //but items in mPathCtrls are not. We need to map index from
163 //mPathCtrls to index in mRows
164 mRows->Remove(
165 std::count_if(
166 mPathCtrls.begin(),
167 mPathCtrls.begin() + index,
168 [](const auto ptr) { return ptr != nullptr; }
169 )
170 );
171
172 FindWindowById(baseId, this)->Destroy();
173 FindWindowById(baseId + 1, this)->Destroy();
174 FindWindowById(baseId + 2, this)->Destroy();
175 mPathCtrls[index] = nullptr;
176 mPaths[index] = wxString{};//Don't save path on commit
177
178 wxPostEvent(this, wxCommandEvent(EVT_PLUGIN_LOCATIONS_CHANGED));
179 }
180
181 void OnBrowseClicked(wxCommandEvent& evt)
182 {
183 const auto index = IndexForId(evt.GetId());
184
185 wxDirDialogWrapper dirDialog(
186 wxGetTopLevelParent(this),
188 mPathCtrls[index]->GetValue()
189 );
190 if(dirDialog.ShowModal() == wxID_OK)
191 mPathCtrls[index]->SetValue(dirDialog.GetPath());//also invoke wxEVT_TEXT
192 }
193};
194
195
196EffectsPrefs::EffectsPrefs(wxWindow * parent, wxWindowID winid)
197: PrefsPanel(parent, winid, XO("Effects"))
198{
199 Populate();
200}
201
203{
204}
205
207{
209}
210
212{
213 return XO("Preferences for Effects");
214}
215
217{
218 return "Effects_Preferences";
219}
220
222{
223 //------------------------- Main section --------------------
224 // Now construct the GUI itself.
225 // Use 'eIsCreatingFromPrefs' so that the GUI is
226 // initialised with values from gPrefs.
229 // ----------------------- End of main section --------------
230}
231
233 ByColumns,
234 {
235 XO("Sort by effect name") ,
236 XO("Sort by publisher and effect name") ,
237 XO("Sort by type and effect name") ,
238 XO("Group by publisher") ,
239 XO("Group by type") ,
240 XO("Group by category"),
241 XO("Group by type and publisher")
242 },
243 {
244 wxT("sortby:name") ,
245 wxT("sortby:publisher:name") ,
246 wxT("sortby:type:name") ,
247 wxT("groupby:publisher") ,
248 wxT("groupby:type") ,
249 wxT("default"),
250 wxT("groupby:type:publisher")
251 }
252};
253
255 wxT("/Effects/SkipEffectsScanAtStartup"),
256 false
257};
258
260 wxT("/Effects/GroupBy"),
262 5 // "default"
263};
264
266 wxT("/Effects/RealtimeGroupBy"),
268 6 // "groupby:type:publisher"
269};
270
272{
273 const auto scroller = S.StartScroller();
274
275 S.StartStatic(XO("Effect Options"));
276 {
277 S.StartMultiColumn(2);
278 {
279 S.SetBorder(3);
280 S.MinSize()
281 .TieChoice( XXO("Effect menu &organization:"), EffectsGroupBy);
282 S.MinSize()
283 .TieChoice( XXO("Realtime effect o&rganization:"), RealtimeEffectsGroupBy);
284 }
285 S.EndMultiColumn();
286 }
287 S.EndStatic();
288
289#ifdef EXPERIMENTAL_EQ_SSE_THREADED
290 S.StartStatic(XO("Instruction Set"));
291 {
292 S.TieCheckBox(XXO("&Use SSE/SSE2/.../AVX"),
293 {wxT("/SSE/GUI"),
294 true});
295 }
296 S.EndStatic();
297#endif
298
299 auto& pluginManager = PluginManager::Get();
300 for(auto& [id, provider] : ModuleManager::Get().Providers())
301 {
302 if(!provider->SupportsCustomModulePaths())
303 continue;
304
305 /*i18n-hint: Title of the panel containing user-defined paths where plugins could be found
306 * First argument is replaced with plugin type (e.g. "LV2 plugin locations")
307 */
308 const auto panelTitle = XO("%s plugin locations")
309 .Format(provider->GetOptionalFamilySymbol().Translation());
310 S.StartStatic(panelTitle);
311 {
312 if(S.GetMode() == eIsCreating)
313 {
314 const auto panel = safenew EffectsLocationPanel(S.GetParent());
315#if wxUSE_ACCESSIBILITY
316 panel->SetName(panelTitle);
318#endif
319
320 panel->AddLocations(pluginManager.ReadCustomPaths(*provider.get()));
321 S.Prop(1).AddWindow(panel, wxEXPAND);
322 mLocations.emplace_back(provider.get(), panel);
323
324 panel->Bind(EVT_PLUGIN_LOCATIONS_CHANGED, [wnd = wxWeakRef(scroller)](const auto&)
325 {
326 if(!wnd)
327 return;
328 wnd->Layout();
329 wnd->FitInside();
330 });
331 }
332 }
333 S.EndStatic();
334 }
335
336 S.TieCheckBox(XXO("&Skip effects scanning at startup"), SkipEffectsScanAtStartup);
337
338 if (auto pButton = S.AddButton(XXO("Open Plugin &Manager"), wxALIGN_LEFT))
339 pButton->Bind(wxEVT_BUTTON, [this](auto) {
340 //Adding dependency on PluginRegistrationDialog, not good. Alternatively
341 //that could be done with events, though event should be visible here too...
342 PluginRegistrationDialog dialog(wxGetTopLevelParent(this));
343 if(dialog.ShowModal() == wxID_OK)
345 });
346
347 S.EndScroller();
348}
349
351{
352 auto& pluginManager = PluginManager::Get();
353
354 for(auto [provider, panel] : mLocations)
355 pluginManager.StoreCustomPaths(*provider, panel->GetLocations());
356
359
360 return true;
361}
362
363namespace{
365 [](wxWindow *parent, wxWindowID winid, AudacityProject *)
366 {
367 wxASSERT(parent); // to justify safenew
368 return safenew EffectsPrefs(parent, winid);
369 }
370};
371}
wxT("CloseDown"))
EnumValueSymbols EffectsGroupSymbols
wxDEFINE_EVENT(EVT_PLUGIN_LOCATIONS_CHANGED, wxCommandEvent)
ChoiceSetting EffectsGroupBy
BoolSetting SkipEffectsScanAtStartup
ChoiceSetting RealtimeEffectsGroupBy
#define EFFECTS_PREFS_PLUGIN_SYMBOL
Definition: EffectsPrefs.h:25
XO("Cut/Copy/Paste")
XXO("&Cut/Copy/Paste Toolbar")
std::vector< PluginPath > PluginPaths
Definition: Identifier.h:215
wxString PluginPath
type alias for identifying a Plugin supplied by a module, each module defining its own interpretation...
Definition: Identifier.h:214
#define _(s)
Definition: Internat.h:73
#define safenew
Definition: MemoryX.h:10
ByColumns_t ByColumns
Definition: Prefs.cpp:515
@ eIsCreating
Definition: ShuttleGui.h:37
@ eIsCreatingFromPrefs
Definition: ShuttleGui.h:46
@ eIsSavingToPrefs
Definition: ShuttleGui.h:47
#define S(N)
Definition: ToChars.cpp:64
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:90
This specialization of Setting for bool adds a Toggle method to negate the saved value.
Definition: Prefs.h:346
ComponentInterfaceSymbol pairs a persistent string identifier used internally with an optional,...
std::vector< wxString > mPaths
void OnRemoveClicked(wxCommandEvent &evt)
EffectsLocationPanel(Args &&... args)
void OnAddNewLocationClicked(wxCommandEvent &)
std::vector< wxTextCtrl * > mPathCtrls
void AddLocations(const PluginPaths &paths)
PluginPaths GetLocations() const
static int IndexForId(int id)
void AddLocation(const PluginPath &path, bool setFocus=false)
wxButton * mAddNewLocation
static int BaseRowIdForIndex(int index)
void OnBrowseClicked(wxCommandEvent &evt)
A PrefsPanel for general GUI preferences.
Definition: EffectsPrefs.h:28
void PopulateOrExchange(ShuttleGui &S) override
EffectsPrefs(wxWindow *parent, wxWindowID winid)
bool Commit() override
ComponentInterfaceSymbol GetSymbol() const override
TranslatableString GetDescription() const override
ManualPageID HelpPageName() override
If not empty string, the Help button is added below the panel.
std::vector< std::pair< PluginProvider *, EffectsLocationPanel * > > mLocations
Definition: EffectsPrefs.h:42
static void RebuildAllMenuBars()
static ModuleManager & Get()
static PluginManager & Get()
Base class for a panel in the PrefsDialog. Classes derived from this class include BatchPrefs,...
Definition: PrefsPanel.h:51
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
Definition: ShuttleGui.h:640
Holds a msgid for the translation catalog; may also bind format arguments.
An alternative to using wxWindowAccessible, which in wxWidgets 3.1.1 contained GetParent() which was ...
static const TranslatableString DefaultDialogPrompt
PrefsPanel::Registration sAttachment
STL namespace.