Audacity 3.2.0
RealtimeEffectPanel.cpp
Go to the documentation of this file.
1/*!********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 @file RealtimeEffectPanel.cpp
6
7 @author Vitaly Sverchinsky
8
9**********************************************************************/
10
11#include "RealtimeEffectPanel.h"
12
13#include <wx/app.h>
14#include <wx/sizer.h>
15#include <wx/splitter.h>
16#include <wx/statbmp.h>
17#include <wx/stattext.h>
18#include <wx/menu.h>
19#include <wx/wupdlock.h>
20#include <wx/hyperlink.h>
21
22#include <wx/dcbuffer.h>
23
24#include "HelpSystem.h"
25#include "Theme.h"
26#include "AllThemeResources.h"
27#include "AudioIO.h"
28#include "BasicUI.h"
29#include "Observer.h"
30#include "PluginManager.h"
31#include "Project.h"
32#include "ProjectHistory.h"
33#include "ProjectWindow.h"
34#include "ProjectWindows.h"
35#include "Track.h"
36#include "TrackPanelAx.h"
37#include "AColor.h"
38#include "WaveTrack.h"
39#include "effects/EffectUI.h"
41#include "RealtimeEffectList.h"
42#include "RealtimeEffectState.h"
44#include "UndoManager.h"
45#include "Prefs.h"
46#include "BasicUI.h"
48#include "ListNavigationPanel.h"
49#include "MovableControl.h"
50#include "menus/MenuHelper.h"
51#include "Menus.h"
52#include "prefs/EffectsPrefs.h"
53
54#if wxUSE_ACCESSIBILITY
55#include "WindowAccessible.h"
56#endif
57
58namespace
59{
60
62 {
63 wxMenu& mMenu;
64 wxMenu* mMenuPtr { nullptr };
65 int mMenuItemIdCounter { wxID_HIGHEST };
66 std::vector<Identifier> mIndexedPluginList;
67 int mMenuLevelCounter { 0 };
68 public:
69
71 : mMenu(menu), mMenuPtr(&mMenu) { }
72
73 void DoBeginGroup( MenuTable::GroupItem &item, const Path& ) override
74 {
75 if(auto menuItem = dynamic_cast<MenuTable::MenuItem*>(&item))
76 {
77 //Don't create a group item for root
78 if(mMenuLevelCounter != 0 && !menuItem->Transparent())
79 {
80 auto submenu = std::make_unique<wxMenu>();
81 mMenuPtr->AppendSubMenu(submenu.get(), menuItem->title.Translation());
82 mMenuPtr = submenu.release();
83 }
84 ++mMenuLevelCounter;
85 }
86 }
87
88 void DoEndGroup( MenuTable::GroupItem &item, const Path& ) override
89 {
90 if(auto menuItem = dynamic_cast<MenuTable::MenuItem*>(&item))
91 {
92 --mMenuLevelCounter;
93 if(mMenuLevelCounter != 0 && !menuItem->Transparent())
94 {
95 assert(mMenuPtr->GetParent() != nullptr);
96 mMenuPtr = mMenuPtr->GetParent();
97 }
98 }
99 }
100
101 void DoVisit( MenuTable::SingleItem &item, const Path& ) override
102 {
103 if(auto commandItem = dynamic_cast<MenuTable::CommandItem*>(&item))
104 {
105 mMenuPtr->Append(mMenuItemIdCounter, commandItem->label_in.Translation());
106 mIndexedPluginList.push_back(commandItem->name);
107 ++mMenuItemIdCounter;
108 }
109 }
110
111 void DoSeparator() override
112 {
113 mMenuPtr->AppendSeparator();
114 }
115
116 Identifier GetPluginID(int menuIndex) const
117 {
118 assert(menuIndex >= wxID_HIGHEST && menuIndex < (wxID_HIGHEST + mIndexedPluginList.size()));
119 return mIndexedPluginList[menuIndex - wxID_HIGHEST];
120 }
121
122 };
123
124 template <typename Visitor>
125 void VisitRealtimeEffectStateUIs(Track& track, Visitor&& visitor)
126 {
127 auto& effects = RealtimeEffectList::Get(track);
128 effects.Visit(
129 [visitor](auto& effectState, bool)
130 {
131 auto& ui = RealtimeEffectStateUI::Get(effectState);
132 visitor(ui);
133 });
134 }
135
137 {
139 track, [&](auto& ui) { ui.UpdateTrackData(track); });
140 }
141
143 {
145 track,
146 [&](auto& ui)
147 {
148 if (ui.IsShown())
149 {
150 ui.Hide(&project);
151 ui.Show(project);
152 }
153 });
154 }
155 //fwd
156 class RealtimeEffectControl;
157
158 class DropHintLine : public wxWindow
159 {
160 public:
161 DropHintLine(wxWindow *parent,
162 wxWindowID id,
163 const wxPoint& pos = wxDefaultPosition,
164 const wxSize& size = wxDefaultSize)
165 : wxWindow(parent, id, pos, size, wxNO_BORDER, wxEmptyString)
166 {
167 wxWindow::SetBackgroundStyle(wxBG_STYLE_PAINT);
168 Bind(wxEVT_PAINT, &DropHintLine::OnPaint, this);
169 }
170
171 bool AcceptsFocus() const override { return false; }
172
173 private:
174 void OnPaint(wxPaintEvent&)
175 {
176 wxBufferedPaintDC dc(this);
177 const auto rect = wxRect(GetSize());
178
179 dc.SetPen(*wxTRANSPARENT_PEN);
180 dc.SetBrush(GetBackgroundColour());
181 dc.DrawRectangle(rect);
182 }
183 };
184
185 class HyperLinkCtrlWrapper : public ListNavigationEnabled<wxHyperlinkCtrl>
186 {
187 public:
188 HyperLinkCtrlWrapper(wxWindow *parent,
189 wxWindowID id,
190 const wxString& label,
191 const wxString& url,
192 const wxPoint& pos = wxDefaultPosition,
193 const wxSize& size = wxDefaultSize,
194 long style = wxHL_DEFAULT_STYLE,
195 const wxString& name = wxHyperlinkCtrlNameStr)
196 {
197 Create(parent, id, label, url, pos, size, style, name);
198 }
199
200 void Create(wxWindow *parent,
201 wxWindowID id,
202 const wxString& label,
203 const wxString& url,
204 const wxPoint& pos = wxDefaultPosition,
205 const wxSize& size = wxDefaultSize,
206 long style = wxHL_DEFAULT_STYLE,
207 const wxString& name = wxHyperlinkCtrlNameStr)
208 {
209 ListNavigationEnabled<wxHyperlinkCtrl>::Create(parent, id, label, url, pos, size, style, name);
210 Bind(wxEVT_PAINT, &HyperLinkCtrlWrapper::OnPaint, this);
211 }
212
213 void OnPaint(wxPaintEvent& evt)
214 {
215 wxPaintDC dc(this);
216 dc.SetFont(GetFont());
217 dc.SetTextForeground(GetForegroundColour());
218 dc.SetTextBackground(GetBackgroundColour());
219
220 auto labelRect = GetLabelRect();
221
222 dc.DrawText(GetLabel(), labelRect.GetTopLeft());
223 if (HasFocus())
224 AColor::DrawFocus(dc, labelRect);
225 }
226 };
227
228#if wxUSE_ACCESSIBILITY
229 class RealtimeEffectControlAx : public wxAccessible
230 {
231 public:
232 RealtimeEffectControlAx(wxWindow* win = nullptr) : wxAccessible(win) { }
233
234 wxAccStatus GetName(int childId, wxString* name) override
235 {
236 if(childId != wxACC_SELF)
237 return wxACC_NOT_IMPLEMENTED;
238
239 if(auto movable = wxDynamicCast(GetWindow(), MovableControl))
240 //i18n-hint: argument - position of the effect in the effect stack
241 *name = wxString::Format(_("Effect %d"), movable->FindIndexInParent() + 1);
242 return wxACC_OK;
243 }
244
245 wxAccStatus GetChildCount(int* childCount) override
246 {
247 const auto window = GetWindow();
248 *childCount = window->GetChildren().size();
249 return wxACC_OK;
250 }
251
252 wxAccStatus GetChild(int childId, wxAccessible** child) override
253 {
254 if(childId == wxACC_SELF)
255 *child = this;
256 else
257 {
258 const auto window = GetWindow();
259 const auto& children = window->GetChildren();
260 const auto childIndex = childId - 1;
261 if(childIndex < children.size())
262 *child = children[childIndex]->GetAccessible();
263 else
264 *child = nullptr;
265 }
266 return wxACC_OK;
267 }
268
269 wxAccStatus GetRole(int childId, wxAccRole* role) override
270 {
271 if(childId != wxACC_SELF)
272 return wxACC_NOT_IMPLEMENTED;
273
274 *role = wxROLE_SYSTEM_PANE;
275 return wxACC_OK;
276 }
277
278 wxAccStatus GetState(int childId, long* state) override
279 {
280 if(childId != wxACC_SELF)
281 return wxACC_NOT_IMPLEMENTED;
282
283 const auto window = GetWindow();
284 if(!window->IsEnabled())
285 *state = wxACC_STATE_SYSTEM_UNAVAILABLE;
286 else
287 {
288 *state = wxACC_STATE_SYSTEM_FOCUSABLE;
289 if(window->HasFocus())
290 *state |= wxACC_STATE_SYSTEM_FOCUSED;
291 }
292 return wxACC_OK;
293 }
294 };
295#endif
296
298 {
299 public:
300 virtual std::optional<wxString> PickEffect(wxWindow* parent, const wxString& selectedEffectID) = 0;
301 };
302
303 //UI control that represents individual effect from the effect list
304 class RealtimeEffectControl : public ListNavigationEnabled<MovableControl>
305 {
306 wxWeakRef<AudacityProject> mProject;
307 std::shared_ptr<Track> mTrack;
308 std::shared_ptr<RealtimeEffectState> mEffectState;
309 std::shared_ptr<EffectSettingsAccess> mSettingsAccess;
310
311 RealtimeEffectPicker* mEffectPicker { nullptr };
312
313 ThemedAButtonWrapper<AButton>* mChangeButton{nullptr};
314 AButton* mEnableButton{nullptr};
316
318
319 public:
321
322 RealtimeEffectControl(wxWindow* parent,
323 RealtimeEffectPicker* effectPicker,
324 wxWindowID winid,
325 const wxPoint& pos = wxDefaultPosition,
326 const wxSize& size = wxDefaultSize)
327 {
328 Create(parent, effectPicker, winid, pos, size);
329 }
330
331 void Create(wxWindow* parent,
332 RealtimeEffectPicker* effectPicker,
333 wxWindowID winid,
334 const wxPoint& pos = wxDefaultPosition,
335 const wxSize& size = wxDefaultSize)
336 {
337 mEffectPicker = effectPicker;
338
339 //Prevents flickering and paint order issues
340 MovableControl::SetBackgroundStyle(wxBG_STYLE_PAINT);
341 MovableControl::Create(parent, winid, pos, size, wxNO_BORDER | wxWANTS_CHARS);
342
343 Bind(wxEVT_PAINT, &RealtimeEffectControl::OnPaint, this);
344 Bind(wxEVT_SET_FOCUS, &RealtimeEffectControl::OnFocusChange, this);
345 Bind(wxEVT_KILL_FOCUS, &RealtimeEffectControl::OnFocusChange, this);
346
347 auto sizer = std::make_unique<wxBoxSizer>(wxHORIZONTAL);
348
349 //On/off button
350 auto enableButton = safenew ThemedAButtonWrapper<AButton>(this);
351 enableButton->SetTranslatableLabel(XO("Power"));
352 enableButton->SetImageIndices(0, bmpEffectOff, bmpEffectOff, bmpEffectOn, bmpEffectOn, bmpEffectOff);
353 enableButton->SetButtonToggles(true);
354 enableButton->SetBackgroundColorIndex(clrEffectListItemBackground);
355 mEnableButton = enableButton;
356
357 enableButton->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) {
358
359 mEffectState->SetActive(mEnableButton->IsDown());
360 });
361
362 //Central button with effect name, show settings
363 const auto optionsButton = safenew ThemedAButtonWrapper<AButton>(this, wxID_ANY);
364 optionsButton->SetImageIndices(0,
365 bmpHButtonNormal,
366 bmpHButtonHover,
367 bmpHButtonDown,
368 bmpHButtonHover,
369 bmpHButtonDisabled);
370 optionsButton->SetBackgroundColorIndex(clrEffectListItemBackground);
371 optionsButton->SetForegroundColorIndex(clrTrackPanelText);
372 optionsButton->SetButtonType(AButton::TextButton);
373 optionsButton->Bind(wxEVT_BUTTON, &RealtimeEffectControl::OnOptionsClicked, this);
374
375 //Remove/replace effect
376 auto changeButton = safenew ThemedAButtonWrapper<AButton>(this);
377 changeButton->SetImageIndices(0, bmpMoreNormal, bmpMoreHover, bmpMoreDown, bmpMoreHover, bmpMoreDisabled);
378 changeButton->SetBackgroundColorIndex(clrEffectListItemBackground);
379 changeButton->SetTranslatableLabel(XO("Replace effect"));
380 changeButton->Bind(wxEVT_BUTTON, &RealtimeEffectControl::OnChangeButtonClicked, this);
381
382 auto dragArea = safenew wxStaticBitmap(this, wxID_ANY, theTheme.Bitmap(bmpDragArea));
383 dragArea->Disable();
384 sizer->Add(dragArea, 0, wxLEFT | wxCENTER, 5);
385 sizer->Add(enableButton, 0, wxLEFT | wxCENTER, 5);
386 sizer->Add(optionsButton, 1, wxLEFT | wxCENTER, 5);
387 sizer->Add(changeButton, 0, wxLEFT | wxRIGHT | wxCENTER, 5);
388 mChangeButton = changeButton;
389 mOptionsButton = optionsButton;
390
391 auto vSizer = std::make_unique<wxBoxSizer>(wxVERTICAL);
392 vSizer->Add(sizer.release(), 0, wxUP | wxDOWN | wxEXPAND, 10);
393
394 SetSizer(vSizer.release());
395
396#if wxUSE_ACCESSIBILITY
397 SetAccessible(safenew RealtimeEffectControlAx(this));
398#endif
399 }
400
401 static const PluginDescriptor *GetPlugin(const PluginID &ID) {
402 auto desc = PluginManager::Get().GetPlugin(ID);
403 return desc;
404 }
405
408 {
409 const auto &ID = mEffectState->GetID();
410 const auto desc = GetPlugin(ID);
411 return desc
412 ? desc->GetSymbol().Msgid()
413 : XO("%s (missing)")
414 .Format(PluginManager::GetEffectNameFromID(ID).GET());
415 }
416
418 const std::shared_ptr<Track>& track,
419 const std::shared_ptr<RealtimeEffectState> &pState)
420 {
421 mProject = &project;
422 mTrack = track;
423 mEffectState = pState;
424
425 mSubscription = mEffectState->Subscribe([this](RealtimeEffectStateChange state) {
427 ? mEnableButton->PushDown()
428 : mEnableButton->PopUp();
429
430 if (mProject)
431 ProjectHistory::Get(*mProject).ModifyState(false);
432 });
433
435 if (pState) {
436 label = GetEffectName();
437 mSettingsAccess = pState->GetAccess();
438 }
439 else
440 mSettingsAccess.reset();
441 if (mEnableButton)
442 mSettingsAccess && mSettingsAccess->Get().extra.GetActive()
443 ? mEnableButton->PushDown()
444 : mEnableButton->PopUp();
445 if (mOptionsButton)
446 {
447 mOptionsButton->SetTranslatableLabel(label);
448 mOptionsButton->SetEnabled(pState && GetPlugin(pState->GetID()));
449 }
450 }
451
453 {
454 if(mProject == nullptr || mEffectState == nullptr)
455 return;
456
457 auto& ui = RealtimeEffectStateUI::Get(*mEffectState);
458 // Don't need autosave for the effect that is being removed
459 ui.Hide();
460
461 auto effectName = GetEffectName();
462 //After AudioIO::RemoveState call this will be destroyed
463 auto project = mProject.get();
464 auto trackName = mTrack->GetName();
465
466 AudioIO::Get()->RemoveState(*project, &*mTrack, mEffectState);
472 XO("Removed %s from %s").Format(effectName, trackName),
475 XO("Remove %s").Format(effectName)
476 );
477 }
478
479 void OnOptionsClicked(wxCommandEvent& event)
480 {
481 if(mProject == nullptr || mEffectState == nullptr)
482 return;//not initialized
483
484 const auto ID = mEffectState->GetID();
485 const auto effectPlugin = EffectManager::Get().GetEffect(ID);
486
487 if(effectPlugin == nullptr)
488 {
490 return;
491 }
492
493 auto& effectStateUI = RealtimeEffectStateUI::Get(*mEffectState);
494
495 effectStateUI.UpdateTrackData(*mTrack);
496 effectStateUI.Toggle( *mProject );
497 }
498
499 void OnChangeButtonClicked(wxCommandEvent& event)
500 {
501 if(!mTrack || mProject == nullptr)
502 return;
503 if(mEffectState == nullptr)
504 return;//not initialized
505
506 const auto effectID = mEffectPicker->PickEffect(mChangeButton, mEffectState->GetID());
507 if(!effectID)
508 return;//nothing
509
510 if(effectID->empty())
511 {
512 RemoveFromList();
513 return;
514 }
515
516 auto &em = RealtimeEffectManager::Get(*mProject);
517 auto oIndex = em.FindState(&*mTrack, mEffectState);
518 if (!oIndex)
519 return;
520
521 auto oldName = GetEffectName();
522 auto &project = *mProject;
523 auto trackName = mTrack->GetName();
524 if (auto state = AudioIO::Get()
525 ->ReplaceState(project, &*mTrack, *oIndex, *effectID)
526 ){
527 // Message subscription took care of updating the button text
528 // and destroyed `this`!
529 auto effect = state->GetEffect();
530 assert(effect); // postcondition of ReplaceState
532 /*i18n-hint: undo history,
533 first and second parameters - realtime effect names
534 */
535 XO("Replaced %s with %s")
536 .Format(oldName, effect->GetName()),
539 XO("Replace %s").Format(oldName));
540 }
541 }
542
543 void OnPaint(wxPaintEvent&)
544 {
545 wxBufferedPaintDC dc(this);
546 const auto rect = wxRect(GetSize());
547
548 dc.SetPen(*wxTRANSPARENT_PEN);
549 dc.SetBrush(GetBackgroundColour());
550 dc.DrawRectangle(rect);
551
552 dc.SetPen(theTheme.Colour(clrEffectListItemBorder));
553 dc.SetBrush(theTheme.Colour(clrEffectListItemBorder));
554 dc.DrawLine(rect.GetBottomLeft(), rect.GetBottomRight());
555
556 if(HasFocus())
557 AColor::DrawFocus(dc, GetClientRect().Deflate(3, 3));
558 }
559
560 void OnFocusChange(wxFocusEvent& evt)
561 {
562 Refresh(false);
563 evt.Skip();
564 }
565 };
566
567 static wxString GetSafeVendor(const PluginDescriptor& descriptor)
568 {
569 if (descriptor.GetVendor().empty())
570 return XO("Unknown").Translation();
571
572 return descriptor.GetVendor();
573 }
574}
575
577 : public wxScrolledWindow
578 , public RealtimeEffectPicker
579 , public PrefsListener
580{
581 wxWeakRef<AudacityProject> mProject;
582 std::shared_ptr<Track> mTrack;
584 wxStaticText* mAddEffectHint{nullptr};
585 wxWindow* mAddEffectTutorialLink{nullptr};
586 wxWindow* mEffectListContainer{nullptr};
587
588 std::unique_ptr<MenuTable::MenuItem> mEffectMenuRoot;
589
592
593public:
594 RealtimeEffectListWindow(wxWindow *parent,
595 wxWindowID winid = wxID_ANY,
596 const wxPoint& pos = wxDefaultPosition,
597 const wxSize& size = wxDefaultSize,
598 long style = wxScrolledWindowStyle,
599 const wxString& name = wxPanelNameStr)
600 : wxScrolledWindow(parent, winid, pos, size, style, name)
601 {
602 Bind(wxEVT_SIZE, &RealtimeEffectListWindow::OnSizeChanged, this);
603#ifdef __WXMSW__
604 //Fixes flickering on redraw
605 wxScrolledWindow::SetDoubleBuffered(true);
606#endif
607 auto rootSizer = std::make_unique<wxBoxSizer>(wxVERTICAL);
608
609 auto effectListContainer = safenew ThemedWindowWrapper<wxPanel>(this, wxID_ANY);
610 effectListContainer->SetBackgroundColorIndex(clrMedium);
611 effectListContainer->SetSizer(safenew wxBoxSizer(wxVERTICAL));
612 effectListContainer->SetDoubleBuffered(true);
613 effectListContainer->Hide();
614 mEffectListContainer = effectListContainer;
615
616 auto addEffect = safenew ThemedAButtonWrapper<AButton>(this, wxID_ANY);
617 addEffect->SetImageIndices(0,
618 bmpHButtonNormal,
619 bmpHButtonHover,
620 bmpHButtonDown,
621 bmpHButtonHover,
622 bmpHButtonDisabled);
623 addEffect->SetTranslatableLabel(XO("Add effect"));
624 addEffect->SetButtonType(AButton::TextButton);
625 addEffect->SetBackgroundColorIndex(clrMedium);
626 addEffect->SetForegroundColorIndex(clrTrackPanelText);
627 addEffect->Bind(wxEVT_BUTTON, &RealtimeEffectListWindow::OnAddEffectClicked, this);
628 mAddEffect = addEffect;
629
630 auto addEffectHint = safenew ThemedWindowWrapper<wxStaticText>(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxST_NO_AUTORESIZE);
631 //Workaround: text is set in the OnSizeChange
632 addEffectHint->SetForegroundColorIndex(clrTrackPanelText);
633 mAddEffectHint = addEffectHint;
634
635 auto addEffectTutorialLink = safenew ThemedWindowWrapper<wxHyperlinkCtrl>(
636 this, wxID_ANY, _("Watch video"),
637 "https://www.audacityteam.org/realtime-video", wxDefaultPosition,
638 wxDefaultSize, wxHL_ALIGN_LEFT | wxHL_CONTEXTMENU);
639
640 //i18n-hint: Hyperlink to the effects stack panel tutorial video
641 addEffectTutorialLink->SetTranslatableLabel(XO("Watch video"));
642#if wxUSE_ACCESSIBILITY
643 safenew WindowAccessible(addEffectTutorialLink);
644#endif
645
646 addEffectTutorialLink->Bind(
647 wxEVT_HYPERLINK, [](wxHyperlinkEvent& event)
648 { BasicUI::OpenInDefaultBrowser(event.GetURL()); });
649
650 mAddEffectTutorialLink = addEffectTutorialLink;
651
652 //indicates the insertion position of the item
653 auto dropHintLine = safenew ThemedWindowWrapper<DropHintLine>(effectListContainer, wxID_ANY);
654 dropHintLine->SetBackgroundColorIndex(clrDropHintHighlight);
655 dropHintLine->Hide();
656
657 rootSizer->Add(mEffectListContainer, 0, wxEXPAND | wxBOTTOM, 10);
658 rootSizer->Add(addEffect, 0, wxLEFT | wxRIGHT | wxBOTTOM | wxEXPAND, 20);
659 rootSizer->Add(addEffectHint, 0, wxLEFT | wxRIGHT | wxBOTTOM | wxEXPAND, 20);
660 rootSizer->Add(addEffectTutorialLink, 0, wxLEFT | wxRIGHT | wxEXPAND, 20);
661
662 SetSizer(rootSizer.release());
663 SetMinSize({});
664
665 Bind(EVT_MOVABLE_CONTROL_DRAG_STARTED, [dropHintLine](const MovableControlEvent& event)
666 {
667 if(auto window = dynamic_cast<wxWindow*>(event.GetEventObject()))
668 window->Raise();
669 });
670 Bind(EVT_MOVABLE_CONTROL_DRAG_POSITION, [this, dropHintLine](const MovableControlEvent& event)
671 {
672 constexpr auto DropHintLineHeight { 3 };//px
673
674 auto sizer = mEffectListContainer->GetSizer();
675 assert(sizer != nullptr);
676
677 if(event.GetSourceIndex() == event.GetTargetIndex())
678 {
679 //do not display hint line if position didn't change
680 dropHintLine->Hide();
681 return;
682 }
683
684 if(!dropHintLine->IsShown())
685 {
686 dropHintLine->Show();
687 dropHintLine->Raise();
688 if(auto window = dynamic_cast<wxWindow*>(event.GetEventObject()))
689 window->Raise();
690 }
691
692 auto item = sizer->GetItem(event.GetTargetIndex());
693 dropHintLine->SetSize(item->GetSize().x, DropHintLineHeight);
694
695 if(event.GetTargetIndex() > event.GetSourceIndex())
696 dropHintLine->SetPosition(item->GetRect().GetBottomLeft() - wxPoint(0, DropHintLineHeight));
697 else
698 dropHintLine->SetPosition(item->GetRect().GetTopLeft());
699 });
700 Bind(EVT_MOVABLE_CONTROL_DRAG_FINISHED, [this, dropHintLine](const MovableControlEvent& event)
701 {
702 dropHintLine->Hide();
703
704 if(mProject == nullptr)
705 return;
706
707 auto& effectList = RealtimeEffectList::Get(*mTrack);
708 const auto from = event.GetSourceIndex();
709 const auto to = event.GetTargetIndex();
710 if(from != to)
711 {
712 auto effectName =
713 effectList.GetStateAt(from)->GetEffect()->GetName();
714 bool up = (to < from);
715 effectList.MoveEffect(from, to);
717 (up
722 ? XO("Moved %s up in %s")
727 : XO("Moved %s down in %s"))
728 .Format(effectName, mTrack->GetName()),
729 XO("Change effect order"), UndoPush::CONSOLIDATE);
730 }
731 else
732 {
733 wxWindowUpdateLocker freeze(this);
734 Layout();
735 }
736 });
737 SetScrollRate(0, 20);
738
741 {
743 });
745 }
746
747 void UpdatePrefs() override
748 {
750 }
751
752 std::optional<wxString> PickEffect(wxWindow* parent, const wxString& selectedEffectID) override
753 {
754 wxMenu menu;
755 if(!selectedEffectID.empty())
756 {
757 //no need to handle language change since menu creates its own event loop
758 menu.Append(wxID_REMOVE, _("No Effect"));
759 menu.AppendSeparator();
760 }
761
762 RealtimeEffectsMenuVisitor visitor { menu };
763
764 Registry::Visit(visitor, mEffectMenuRoot.get());
765
766 int commandId = wxID_NONE;
767
768 menu.AppendSeparator();
769 menu.Append(wxID_MORE, _("Get more effects..."));
770
771 menu.Bind(wxEVT_MENU, [&](wxCommandEvent evt) { commandId = evt.GetId(); });
772
773 if(parent->PopupMenu(&menu, parent->GetClientRect().GetLeftBottom()) && commandId != wxID_NONE)
774 {
775 if(commandId == wxID_REMOVE)
776 return wxString {};
777 else if(commandId == wxID_MORE)
778 OpenInDefaultBrowser("https://plugins.audacityteam.org/");
779 else
780 return visitor.GetPluginID(commandId).GET();
781 }
782
783 return {};
784 }
785
787 {
788 auto root = std::make_unique<MenuTable::MenuItem>(Identifier {}, TranslatableString {});
789
790 static auto realtimeEffectPredicate = [](const PluginDescriptor& desc)
791 {
792 return desc.IsEffectRealtime();
793 };
794
795 const auto groupby = RealtimeEffectsGroupBy.Read();
796
797 auto analyzeItems = MenuHelper::PopulateEffectsMenu(
799 {}, groupby, nullptr,
800 realtimeEffectPredicate
801 );
802
803 if(!analyzeItems.empty())
804 {
805 auto analyzeSection = MenuTable::Section(
806 wxEmptyString,
807 std::make_unique<MenuTable::MenuItem>(
808 wxEmptyString,
809 XO("Analyze"),
810 std::move(analyzeItems)
811 )
812 );
813 root->items.push_back(std::move(analyzeSection));
814 }
815
816 auto processItems = MenuHelper::PopulateEffectsMenu(
818 {}, groupby, nullptr,
819 realtimeEffectPredicate
820 );
821
822 std::move(processItems.begin(), processItems.end(), std::back_inserter(root->items));
823
824 mEffectMenuRoot.swap(root);
825 }
826
827 void OnSizeChanged(wxSizeEvent& event)
828 {
829 if(auto sizerItem = GetSizer()->GetItem(mAddEffectHint))
830 {
831 //We need to wrap the text whenever panel width changes and adjust widget height
832 //so that text is fully visible, but there is no height-for-width layout algorithm
833 //in wxWidgets yet, so for now we just do it manually
834
835 //Restore original text, because 'Wrap' will replace it with wrapped one
836 mAddEffectHint->SetLabel(_("Realtime effects are non-destructive and can be changed at any time."));
837 mAddEffectHint->Wrap(GetClientSize().x - sizerItem->GetBorder() * 2);
838 mAddEffectHint->InvalidateBestSize();
839 }
840 event.Skip();
841 }
842
844 {
845 auto sizer = mEffectListContainer->GetSizer();
846 const auto insertItem = [this, &msg](){
847 auto& effects = RealtimeEffectList::Get(*mTrack);
848 InsertEffectRow(msg.srcIndex, effects.GetStateAt(msg.srcIndex));
849 mAddEffectHint->Hide();
851 };
852 const auto removeItem = [&](){
854 // Don't need to auto-save changed settings of effect that is deleted
855 // Undo history push will do it anyway
856 ui.Hide();
857
858 auto window = sizer->GetItem(msg.srcIndex)->GetWindow();
859 sizer->Remove(msg.srcIndex);
860 wxTheApp->CallAfter([ref = wxWeakRef { window }] {
861 if(ref) ref->Destroy();
862 });
863
864 if(sizer->IsEmpty())
865 {
866 if(mEffectListContainer->IsDescendant(FindFocus()))
867 mAddEffect->SetFocus();
868
869 mEffectListContainer->Hide();
870 mAddEffectHint->Show();
872 }
873 };
874
875 wxWindowUpdateLocker freeze(this);
877 {
878 const auto sizer = mEffectListContainer->GetSizer();
879
880 const auto movedItem = sizer->GetItem(msg.srcIndex);
881
882 const auto proportion = movedItem->GetProportion();
883 const auto flag = movedItem->GetFlag();
884 const auto border = movedItem->GetBorder();
885 const auto window = movedItem->GetWindow();
886
887 if(msg.srcIndex < msg.dstIndex)
888 window->MoveAfterInTabOrder(sizer->GetItem(msg.dstIndex)->GetWindow());
889 else
890 window->MoveBeforeInTabOrder(sizer->GetItem(msg.dstIndex)->GetWindow());
891
892 sizer->Remove(msg.srcIndex);
893 sizer->Insert(msg.dstIndex, window, proportion, flag, border);
894 }
896 {
897 insertItem();
898 }
900 {
901 removeItem();
902 }
904 {
905 insertItem();
906 }
908 {
909 removeItem();
910 }
911 SendSizeEventToParent();
912 }
913
915 {
917
918 mTrack.reset();
919 mProject = nullptr;
921 }
922
923 void SetTrack(AudacityProject& project, const std::shared_ptr<Track>& track)
924 {
925 if (mTrack == track)
926 return;
927
929
930 mTrack = track;
931 mProject = &project;
933
934 if (track)
935 {
936 auto& effects = RealtimeEffectList::Get(*mTrack);
937 mEffectListItemMovedSubscription = effects.Subscribe(
939
941 }
942 }
943
944 void EnableEffects(bool enable)
945 {
946 if (mTrack)
947 RealtimeEffectList::Get(*mTrack).SetActive(enable);
948 }
949
951 {
952 wxWindowUpdateLocker freeze(this);
953
954 const auto hadFocus = mEffectListContainer->IsDescendant(FindFocus());
955 //delete items that were added to the sizer
956 mEffectListContainer->Hide();
957 mEffectListContainer->GetSizer()->Clear(true);
958
959
960 if(!mTrack || RealtimeEffectList::Get(*mTrack).GetStatesCount() == 0)
961 mEffectListContainer->Hide();
962
963 auto isEmpty{true};
964 if(mTrack)
965 {
966 auto& effects = RealtimeEffectList::Get(*mTrack);
967 isEmpty = effects.GetStatesCount() == 0;
968 for(size_t i = 0, count = effects.GetStatesCount(); i < count; ++i)
969 InsertEffectRow(i, effects.GetStateAt(i));
970 }
972 //Workaround for GTK: Underlying GTK widget does not update
973 //its size when wxWindow size is set to zero
974 mEffectListContainer->Show(!isEmpty);
975 mAddEffectHint->Show(isEmpty);
976 mAddEffectTutorialLink->Show(isEmpty);
977
978 SendSizeEventToParent();
979 }
980
981 void OnAddEffectClicked(const wxCommandEvent& event)
982 {
983 if(!mTrack || mProject == nullptr)
984 return;
985
986 const auto effectID = PickEffect(dynamic_cast<wxWindow*>(event.GetEventObject()), {});
987
988 if(!effectID || effectID->empty())
989 return;
990
991 auto plug = PluginManager::Get().GetPlugin(*effectID);
992 if(!plug)
993 return;
994
997 XO("This plugin could not be loaded.\nIt may have been deleted."),
999 .Caption(XO("Plugin Error")));
1000
1001 return;
1002 }
1003
1004 if(auto state = AudioIO::Get()->AddState(*mProject, &*mTrack, *effectID))
1005 {
1006 auto effect = state->GetEffect();
1007 assert(effect); // postcondition of AddState
1008 const auto effectName = effect->GetName();
1009 ProjectHistory::Get(*mProject).PushState(
1014 XO("Added %s to %s").Format(effectName, mTrack->GetName()),
1015 //i18n-hint: undo history record
1016 XO("Add %s").Format(effectName));
1017 }
1018 }
1019
1020 void InsertEffectRow(size_t index,
1021 const std::shared_ptr<RealtimeEffectState> &pState)
1022 {
1023 if(mProject == nullptr)
1024 return;
1025
1026 // See comment in ReloadEffectsList
1027 if(!mEffectListContainer->IsShown())
1028 mEffectListContainer->Show();
1029
1031 row->SetBackgroundColorIndex(clrEffectListItemBackground);
1032 row->SetEffect(*mProject, mTrack, pState);
1033 mEffectListContainer->GetSizer()->Insert(index, row, 0, wxEXPAND);
1034 }
1035};
1036
1037
1039{
1041
1043 : mProject { project }
1044 {}
1045
1046 void UpdatePrefs() override
1047 {
1048 auto& trackList = TrackList::Get(mProject);
1049 for (auto waveTrack : trackList.Any<WaveTrack>())
1051 }
1052};
1053
1054namespace {
1055AttachedWindows::RegisteredFactory sKey{
1056[](AudacityProject &project) -> wxWeakRef<wxWindow> {
1057 constexpr auto EffectsPanelMinWidth { 255 };
1058
1059 const auto pProjectWindow = &ProjectWindow::Get(project);
1061 project, pProjectWindow->GetContainerWindow(), wxID_ANY);
1062 effectsPanel->SetMinSize({EffectsPanelMinWidth, -1});
1063 effectsPanel->SetName(_("Realtime effects"));
1064 effectsPanel->SetBackgroundColorIndex(clrMedium);
1065 effectsPanel->Hide();//initially hidden
1066 return effectsPanel;
1067}
1068};
1069}
1070
1072{
1074}
1075
1076const RealtimeEffectPanel &
1078{
1079 return Get(const_cast<AudacityProject &>(project));
1080}
1081
1083 AudacityProject& project, wxWindow* parent, wxWindowID id, const wxPoint& pos,
1084 const wxSize& size,
1085 long style, const wxString& name)
1086 : wxPanel(parent, id, pos, size, style, name)
1087 , mProject(project)
1088 , mPrefsListenerHelper(std::make_unique<PrefsListenerHelper>(project))
1089{
1090 auto vSizer = std::make_unique<wxBoxSizer>(wxVERTICAL);
1091
1092 auto header = safenew ThemedWindowWrapper<ListNavigationPanel>(this, wxID_ANY);
1093#if wxUSE_ACCESSIBILITY
1094 safenew WindowAccessible(header);
1095#endif
1096 header->SetBackgroundColorIndex(clrMedium);
1097 {
1098 auto hSizer = std::make_unique<wxBoxSizer>(wxHORIZONTAL);
1099 auto toggleEffects = safenew ThemedAButtonWrapper<AButton>(header);
1100 toggleEffects->SetImageIndices(0, bmpEffectOff, bmpEffectOff, bmpEffectOn, bmpEffectOn, bmpEffectOff);
1101 toggleEffects->SetButtonToggles(true);
1102 toggleEffects->SetTranslatableLabel(XO("Power"));
1103 toggleEffects->SetBackgroundColorIndex(clrMedium);
1104 mToggleEffects = toggleEffects;
1105
1106 toggleEffects->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) {
1107 if (mEffectList)
1108 {
1110
1112 }
1113 });
1114
1115 hSizer->Add(toggleEffects, 0, wxSTRETCH_NOT | wxALIGN_CENTER | wxLEFT, 5);
1116 {
1117 auto vSizer = std::make_unique<wxBoxSizer>(wxVERTICAL);
1118
1119 auto headerText = safenew ThemedWindowWrapper<wxStaticText>(header, wxID_ANY, wxEmptyString);
1120 headerText->SetFont(wxFont(wxFontInfo().Bold()));
1121 headerText->SetTranslatableLabel(XO("Realtime Effects"));
1122 headerText->SetForegroundColorIndex(clrTrackPanelText);
1123
1124 auto trackTitle = safenew ThemedWindowWrapper<wxStaticText>(header, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxST_ELLIPSIZE_END);
1125 trackTitle->SetForegroundColorIndex(clrTrackPanelText);
1126 mTrackTitle = trackTitle;
1127
1128 vSizer->Add(headerText);
1129 vSizer->Add(trackTitle);
1130
1131 hSizer->Add(vSizer.release(), 1, wxEXPAND | wxALL, 10);
1132 }
1133 auto close = safenew ThemedAButtonWrapper<AButton>(header);
1134 close->SetTranslatableLabel(XO("Close"));
1135 close->SetImageIndices(0, bmpCloseNormal, bmpCloseHover, bmpCloseDown, bmpCloseHover, bmpCloseDisabled);
1136 close->SetBackgroundColorIndex(clrMedium);
1137
1138 close->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { Close(); });
1139
1140 hSizer->Add(close, 0, wxSTRETCH_NOT | wxALIGN_CENTER | wxRIGHT, 5);
1141
1142 header->SetSizer(hSizer.release());
1143 }
1144 vSizer->Add(header, 0, wxEXPAND);
1145
1146 auto effectList = safenew ThemedWindowWrapper<RealtimeEffectListWindow>(this, wxID_ANY);
1147 effectList->SetBackgroundColorIndex(clrMedium);
1148 vSizer->Add(effectList, 1, wxEXPAND);
1149
1150 mHeader = header;
1151 mEffectList = effectList;
1152
1153 SetSizerAndFit(vSizer.release());
1154
1155 Bind(wxEVT_CHAR_HOOK, &RealtimeEffectPanel::OnCharHook, this);
1157 auto track = evt.mpTrack.lock();
1158 auto waveTrack = dynamic_cast<WaveTrack*>(track.get());
1159
1160 if (waveTrack == nullptr)
1161 return;
1162
1163 switch (evt.mType)
1164 {
1166 if (mCurrentTrack.lock() == track)
1167 mTrackTitle->SetLabel(track->GetName());
1168 UpdateRealtimeEffectUIData(*waveTrack);
1169 break;
1171 if (evt.mExtra == 0)
1172 mPotentiallyRemovedTracks.push_back(track);
1173 break;
1175 // Addition can be fired as a part of "replace" event.
1176 // Calling UpdateRealtimeEffectUIData is mostly no-op,
1177 // it will just create a new State and Access for it.
1178 UpdateRealtimeEffectUIData(*waveTrack);
1179 break;
1180 default:
1181 break;
1182 }
1183 });
1184
1186 [this](UndoRedoMessage message)
1187 {
1188 if (
1189 message.type == UndoRedoMessage::Type::Purge ||
1190 message.type == UndoRedoMessage::Type::BeginPurge ||
1191 message.type == UndoRedoMessage::Type::EndPurge)
1192 return;
1193
1194 auto& trackList = TrackList::Get(mProject);
1195
1196 // Realtime effect UI is only updated on Undo or Redo
1197 auto waveTracks = trackList.Any<WaveTrack>();
1198
1199 if (
1200 message.type == UndoRedoMessage::Type::UndoOrRedo ||
1201 message.type == UndoRedoMessage::Type::Reset)
1202 {
1203 for (auto waveTrack : waveTracks)
1204 UpdateRealtimeEffectUIData(*waveTrack);
1205 }
1206
1207 // But mPotentiallyRemovedTracks processing happens as fast as possible.
1208 // This event is fired right after the track is deleted, so we do not
1209 // hold the strong reference to the track much longer than need.
1210 if (mPotentiallyRemovedTracks.empty())
1211 return;
1212
1213 // Collect RealtimeEffectUIs that are currently shown
1214 // for the potentially removed tracks
1215 std::vector<RealtimeEffectStateUI*> shownUIs;
1216
1217 for (auto track : mPotentiallyRemovedTracks)
1218 {
1219 // By construction, track cannot be null
1220 assert(track != nullptr);
1221
1223 *track,
1224 [&shownUIs](auto& ui)
1225 {
1226 if (ui.IsShown())
1227 shownUIs.push_back(&ui);
1228 });
1229 }
1230
1231 // For every UI shown - check if the corresponding state
1232 // is reachable from the current track list.
1233 for (auto effectUI : shownUIs)
1234 {
1235 bool reachable = false;
1236
1237 for (auto track : waveTracks)
1238 {
1240 *track,
1241 [effectUI, &reachable](auto& ui)
1242 {
1243 if (effectUI == &ui)
1244 reachable = true;
1245 });
1246
1247 if (reachable)
1248 break;
1249 }
1250
1251 if (!reachable)
1252 // Don't need to autosave for an unreachable state
1253 effectUI->Hide();
1254 }
1255
1257 });
1258
1260 .Subscribe([this](const TrackFocusChangeMessage& msg) {
1261 if (IsShown())
1262 {
1263 auto& trackFocus = TrackFocus::Get(mProject);
1264 ShowPanel(trackFocus.Get(), false);
1265 }
1266 });
1267
1268 Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent&) {
1269 HidePanel(); });
1270}
1271
1273{
1274}
1275
1277{
1278 if(track == nullptr)
1279 {
1280 ResetTrack();
1281 return;
1282 }
1283
1284 wxWindowUpdateLocker freeze(this);
1285
1286 SetTrack(track->shared_from_this());
1287
1288 auto &projectWindow = ProjectWindow::Get(mProject);
1289 const auto pContainerWindow = projectWindow.GetContainerWindow();
1290 if (pContainerWindow->GetWindow1() != this)
1291 {
1292 //Restore previous effects window size
1293 pContainerWindow->SplitVertically(
1294 this,
1295 projectWindow.GetTrackListWindow(),
1296 this->GetSize().GetWidth());
1297 }
1298 if(focus)
1299 SetFocus();
1300 projectWindow.Layout();
1301}
1302
1304{
1305 wxWindowUpdateLocker freeze(this);
1306
1307 auto &projectWindow = ProjectWindow::Get(mProject);
1308 const auto pContainerWindow = projectWindow.GetContainerWindow();
1309 const auto pTrackListWindow = projectWindow.GetTrackListWindow();
1310 if (pContainerWindow->GetWindow2() == nullptr)
1311 //only effects panel is present, restore split positions before removing effects panel
1312 //Workaround: ::Replace and ::Initialize do not work here...
1313 pContainerWindow->SplitVertically(this, pTrackListWindow);
1314
1315 pContainerWindow->Unsplit(this);
1316 pTrackListWindow->SetFocus();
1317 projectWindow.Layout();
1318}
1319
1320void RealtimeEffectPanel::SetTrack(const std::shared_ptr<Track>& track)
1321{
1322 //Avoid creation-on-demand of a useless, empty list in case the track is of non-wave type.
1323 if(track && dynamic_cast<WaveTrack*>(&*track) != nullptr)
1324 {
1325 mTrackTitle->SetLabel(track->GetName());
1327 track && RealtimeEffectList::Get(*track).IsActive()
1329 : mToggleEffects->PopUp();
1330 mEffectList->SetTrack(mProject, track);
1331
1332 mCurrentTrack = track;
1333 //i18n-hint: argument - track name
1334 mHeader->SetName(wxString::Format(_("Realtime effects for %s"), track->GetName()));
1335 }
1336 else
1337 ResetTrack();
1338}
1339
1341{
1342 mTrackTitle->SetLabel(wxEmptyString);
1345 mCurrentTrack.reset();
1346 mHeader->SetName(wxEmptyString);
1347}
1348
1350{
1351 mHeader->SetFocus();
1352}
1353
1355{
1356 if(evt.GetKeyCode() == WXK_ESCAPE && IsShown() && IsDescendant(FindFocus()))
1357 Close();
1358 else
1359 evt.Skip();
1360}
Toolkit-neutral facade for basic user interface services.
const TranslatableString name
Definition: Distortion.cpp:76
@ EffectTypeAnalyze
@ EffectTypeProcess
wxString PluginID
Definition: EffectManager.h:30
ChoiceSetting RealtimeEffectsGroupBy
const TranslatableString desc
Definition: ExportPCM.cpp:55
XO("Cut/Copy/Paste")
#define _(s)
Definition: Internat.h:73
#define safenew
Definition: MemoryX.h:10
AUDACITY_DLL_API AttachedWindows & GetAttachedWindows(AudacityProject &project)
accessors for certain important windows associated with each project
RealtimeEffectStateChange
for(int ii=0, nn=names.size();ii< nn;++ii)
TranslatableString label
Definition: TagsEditor.cpp:164
THEME_API Theme theTheme
Definition: Theme.cpp:82
declares abstract base class Track, TrackList, and iterators over TrackList
int id
static std::once_flag flag
A wxButton with mouse-over behaviour.
Definition: AButton.h:104
void PushDown()
Definition: AButton.cpp:577
bool IsDown()
Definition: AButton.h:208
void Disable()
Definition: AButton.cpp:560
void Enable()
Definition: AButton.cpp:551
void PopUp()
Definition: AButton.cpp:585
@ TextButton
Definition: AButton.h:112
void SetEnabled(bool state)
Definition: AButton.h:182
static void DrawFocus(wxDC &dc, wxRect &r)
Definition: AColor.cpp:235
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:90
static AudioIO * Get()
Definition: AudioIO.cpp:147
void RemoveState(AudacityProject &project, Track *pTrack, std::shared_ptr< RealtimeEffectState > pState)
Forwards to RealtimeEffectManager::RemoveState with proper init scope.
Definition: AudioIO.cpp:390
wxString Read() const
Definition: Prefs.cpp:354
Subclass & Get(const RegisteredFactory &key)
Get reference to an attachment, creating on demand if not present, down-cast it to Subclass.
Definition: ClientData.h:309
EffectPlugin * GetEffect(const PluginID &ID)
static EffectManager & Get()
Abstract base class used in importing a file.
An explicitly nonlocalized string, not meant for the user to see.
Definition: Identifier.h:22
Changes default arrow navigation to behave more list- or table-like. Instead of searching focusable i...
void Create(wxWindow *parent, wxWindowID id, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxDefaultSize, long style=0, const wxString &name=wxPanelNameStr)
Subscription Subscribe(Callback callback)
Connect a callback to the Publisher; later-connected are called earlier.
Definition: Observer.h:199
A move-only handle representing a connection to a Publisher.
Definition: Observer.h:70
void Reset() noexcept
Breaks the connection (constant time)
Definition: Observer.cpp:101
const wxString & GetVendor() const
static Identifier GetEffectNameFromID(const PluginID &ID)
static bool IsPluginAvailable(const PluginDescriptor &plug)
const PluginDescriptor * GetPlugin(const PluginID &ID) const
static PluginManager & Get()
A listener notified of changes in preferences.
Definition: Prefs.h:556
void PushState(const TranslatableString &desc, const TranslatableString &shortDesc)
void ModifyState(bool bWantsAutoSave)
static ProjectHistory & Get(AudacityProject &project)
static ProjectWindow & Get(AudacityProject &project)
static RealtimeEffectList & Get(AudacityProject &project)
size_t GetStatesCount() const noexcept
bool IsActive() const
Non-blocking atomic boolean load.
void SetActive(bool value)
Done by main thread only, under a lock guard.
void OnEffectListItemChange(const RealtimeEffectListMessage &msg)
std::shared_ptr< Track > mTrack
RealtimeEffectListWindow(wxWindow *parent, wxWindowID winid=wxID_ANY, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxDefaultSize, long style=wxScrolledWindowStyle, const wxString &name=wxPanelNameStr)
void OnAddEffectClicked(const wxCommandEvent &event)
void OnSizeChanged(wxSizeEvent &event)
std::unique_ptr< MenuTable::MenuItem > mEffectMenuRoot
void SetTrack(AudacityProject &project, const std::shared_ptr< Track > &track)
std::optional< wxString > PickEffect(wxWindow *parent, const wxString &selectedEffectID) override
Observer::Subscription mEffectListItemMovedSubscription
void InsertEffectRow(size_t index, const std::shared_ptr< RealtimeEffectState > &pState)
Observer::Subscription mPluginsChangedSubscription
wxWeakRef< AudacityProject > mProject
static RealtimeEffectManager & Get(AudacityProject &project)
UI Panel that displays realtime effects from the effect stack of an individual track,...
std::weak_ptr< Track > mCurrentTrack
Observer::Subscription mUndoSubscription
void ShowPanel(Track *track, bool focus)
std::vector< std::shared_ptr< Track > > mPotentiallyRemovedTracks
wxStaticText * mTrackTitle
void SetTrack(const std::shared_ptr< Track > &track)
Shows effects from the effect stack of the track.
RealtimeEffectListWindow * mEffectList
void OnCharHook(wxKeyEvent &evt)
static RealtimeEffectPanel & Get(AudacityProject &project)
RealtimeEffectPanel(AudacityProject &project, wxWindow *parent, wxWindowID id, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxDefaultSize, long style=0, const wxString &name=wxPanelNameStr)
Observer::Subscription mFocusChangeSubscription
AudacityProject & mProject
Observer::Subscription mTrackListChanged
static RealtimeEffectStateUI & Get(RealtimeEffectState &state)
std::vector< Identifier > Path
Definition: Registry.h:262
wxColour & Colour(int iIndex)
wxBitmap & Bitmap(int iIndex)
Track * Get()
Abstract base class for an object holding data associated with points on a time axis.
Definition: Track.h:226
auto Any() -> TrackIterRange< TrackType >
Definition: Track.h:1440
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:487
Holds a msgid for the translation catalog; may also bind format arguments.
static UndoManager & Get(AudacityProject &project)
Definition: UndoManager.cpp:71
A Track that contains audio waveform data.
Definition: WaveTrack.h:51
An alternative to using wxWindowAccessible, which in wxWidgets 3.1.1 contained GetParent() which was ...
DropHintLine(wxWindow *parent, wxWindowID id, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxDefaultSize)
void SetEffect(AudacityProject &project, const std::shared_ptr< Track > &track, const std::shared_ptr< RealtimeEffectState > &pState)
static const PluginDescriptor * GetPlugin(const PluginID &ID)
void Create(wxWindow *parent, RealtimeEffectPicker *effectPicker, wxWindowID winid, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxDefaultSize)
RealtimeEffectControl(wxWindow *parent, RealtimeEffectPicker *effectPicker, wxWindowID winid, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxDefaultSize)
virtual std::optional< wxString > PickEffect(wxWindow *parent, const wxString &selectedEffectID)=0
void DoVisit(MenuTable::SingleItem &item, const Path &) override
void DoBeginGroup(MenuTable::GroupItem &item, const Path &) override
void DoEndGroup(MenuTable::GroupItem &item, const Path &) override
bool OpenInDefaultBrowser(const wxString &url)
Open an URL in default browser.
Definition: BasicUI.cpp:240
MessageBoxResult ShowMessageBox(const TranslatableString &message, MessageBoxOptions options={})
Show a modal message box with either Ok or Yes and No, and optionally Cancel.
Definition: BasicUI.h:274
std::unique_ptr< WindowPlacement > FindFocus()
Find the window that is accepting keyboard input, if any.
Definition: BasicUI.h:343
MenuTable::BaseItemPtrs PopulateEffectsMenu(EffectType type, CommandFlag batchflags, const wxString &groupby, void(*onMenuCommand)(const CommandContext &), std::function< bool(const PluginDescriptor &)> pred={})
Definition: MenuHelper.cpp:558
std::unique_ptr< MenuPart > Section(const Identifier &internalName, Args &&... args)
void Visit(Visitor &visitor, BaseItem *pTopItem, const GroupItem *pRegistry)
Definition: Registry.cpp:713
static wxString GetSafeVendor(const PluginDescriptor &descriptor)
void ReopenRealtimeEffectUIData(AudacityProject &project, Track &track)
void VisitRealtimeEffectStateUIs(Track &track, Visitor &&visitor)
STL namespace.
@ Remove
Effect item was removed from the list at srcIndex position. affectedState is removed state.
@ DidReplace
Effect item was replaced with a new item at srcIndex position. affectedState is an old state.
@ Move
Item position has changed, from srcIndex to dstIndex. affectedState is the moved state.
@ Insert
New effect item was added to the list at srcIndex position. affectedState is a new state.
@ WillReplace
Effect item will be replaced with a new item at srcIndex position. affectedState is the state to be r...
std::shared_ptr< RealtimeEffectState > affectedState
Notification of changes in individual tracks of TrackList, or of TrackList's composition.
Definition: Track.h:1289
const int mExtra
Definition: Track.h:1328
const std::weak_ptr< Track > mpTrack
Definition: Track.h:1327
const Type mType
Definition: Track.h:1326
@ DELETION
Posted when a track has been deleted from a tracklist. Also posted when one track replaces another.
Definition: Track.h:1314
@ ADDITION
Posted when a track has been added to a tracklist. Also posted when one track replaces another.
Definition: Track.h:1308
@ TRACK_DATA_CHANGE
Posted when certain fields of a track change.
Definition: Track.h:1295
Type of message published by UndoManager.
Definition: UndoManager.h:55
enum UndoRedoMessage::Type type