diff --git a/src/PluginRegistrationDialog.cpp b/src/PluginRegistrationDialog.cpp index 0bc1125abe26b1b40a68bc2707cc1ac2eb540f88..d0fb57017ed731179ac26f81f10c339a9934cad8 100644 --- a/src/PluginRegistrationDialog.cpp +++ b/src/PluginRegistrationDialog.cpp @@ -12,14 +12,15 @@ #include <numeric> #include <unordered_map> +#include "AudacityMessageBox.h" #include "EffectInterface.h" +#include "HelpSystem.h" #include "IncompatiblePluginsDialog.h" #include "ModuleManager.h" #include "PluginManager.h" #include "PluginStartupRegistration.h" -#include "ShuttleGui.h" -#include "AudacityMessageBox.h" #include "ProgressDialog.h" +#include "ShuttleGui.h" #include <set> #include <wx/setup.h> // for wxUSE_* macros @@ -43,13 +44,15 @@ enum ID_FilterType, ID_FilterCategory, ID_List, - ID_Rescan + ID_Rescan, + ID_GetMoreEffects, }; BEGIN_EVENT_TABLE(PluginRegistrationDialog, wxDialogWrapper) EVT_BUTTON(wxID_OK, PluginRegistrationDialog::OnOK) EVT_BUTTON(wxID_CANCEL, PluginRegistrationDialog::OnCancel) EVT_BUTTON(ID_Rescan, PluginRegistrationDialog::OnRescan) + EVT_BUTTON(ID_GetMoreEffects, PluginRegistrationDialog::OnGetMoreEffects) EVT_CHOICE(ID_FilterState, PluginRegistrationDialog::OnStateFilterValueChanged) EVT_CHOICE(ID_FilterType, PluginRegistrationDialog::OnTypeFilterValueChanged) EVT_CHOICE(ID_FilterCategory, PluginRegistrationDialog::OnCategoryFilterValueChanged) @@ -200,6 +203,9 @@ void PluginRegistrationDialog::PopulateOrExchange(ShuttleGui &S) { S.AddSpace(Margin, 1); S.Id(ID_Rescan).AddButton(XXO("&Rescan")); +#if defined(__WXMSW__) || defined(__WXMAC__) + S.Id(ID_GetMoreEffects).AddButton(XXO("&Get more effects...")); +#endif S.AddSpace(1, 1, 1); S.Id(wxID_OK).AddButton(XXO("&OK")); @@ -222,7 +228,7 @@ void PluginRegistrationDialog::PopulateOrExchange(ShuttleGui &S) sz.SetWidth(wxMin(sz.GetWidth(), r.GetWidth())); sz.SetHeight(wxMin(sz.GetHeight(), r.GetHeight())); SetMinSize(sz); - + mPluginList->GetColumn(PluginDataModel::ColumnName)->SetWidth(200); mPluginList->GetColumn(PluginDataModel::ColumnType)->SetWidth(80); mPluginList->GetColumn(PluginDataModel::ColumnPath)->SetWidth(350); @@ -251,7 +257,7 @@ void PluginRegistrationDialog::OnSearchTextChanged(wxCommandEvent& evt) void PluginRegistrationDialog::OnStateFilterValueChanged(wxCommandEvent& evt) { const auto index = evt.GetInt(); - + mPluginsModel->SetFilterState( index == 2 ? 1 : (index == 1 ? 0 : -1) ); @@ -336,6 +342,11 @@ void PluginRegistrationDialog::OnRescan(wxCommandEvent& WXUNUSED(evt)) }); } +void PluginRegistrationDialog::OnGetMoreEffects(wxCommandEvent& WXUNUSED(evt)) +{ + OpenInDefaultBrowser("https://www.musehub.com"); +} + void PluginRegistrationDialog::OnOK(wxCommandEvent & WXUNUSED(evt)) { auto result = ProgressResult::Success; @@ -369,14 +380,14 @@ void PluginRegistrationDialog::OnOK(wxCommandEvent & WXUNUSED(evt)) auto onError = [](const TranslatableString& error) { AudacityMessageBox(error); }; - + mPluginsModel->ApplyChanges(updateProgress, onError); } if(result == ProgressResult::Success) EndModal(wxID_OK); else ReloadModel(); - + } void PluginRegistrationDialog::OnCancel(wxCommandEvent & WXUNUSED(evt)) diff --git a/src/PluginRegistrationDialog.h b/src/PluginRegistrationDialog.h index 1626f6e0922fcff1f86adda42d24204790f30d64..5fa8d3206cf31fb82d175a133d1a0e983836cb87 100644 --- a/src/PluginRegistrationDialog.h +++ b/src/PluginRegistrationDialog.h @@ -37,6 +37,7 @@ private: void OnOK(wxCommandEvent & evt); void OnCancel(wxCommandEvent & evt); void OnRescan(wxCommandEvent & evt); + void OnGetMoreEffects(wxCommandEvent & evt); wxArrayString mPluginProviderIDs; diff --git a/src/RealtimeEffectPanel.cpp b/src/RealtimeEffectPanel.cpp index 11a2141e1c8944793ff12fb523375e6387c159ac..cc083f3bdc73569da97cf6a3f531d2b0710df832 100644 --- a/src/RealtimeEffectPanel.cpp +++ b/src/RealtimeEffectPanel.cpp @@ -150,7 +150,7 @@ namespace wxWindow::SetBackgroundStyle(wxBG_STYLE_PAINT); Bind(wxEVT_PAINT, &DropHintLine::OnPaint, this); } - + bool AcceptsFocus() const override { return false; } private: @@ -179,7 +179,7 @@ namespace { Create(parent, id, label, url, pos, size, style, name); } - + void Create(wxWindow *parent, wxWindowID id, const wxString& label, @@ -192,7 +192,7 @@ namespace ListNavigationEnabled<wxHyperlinkCtrl>::Create(parent, id, label, url, pos, size, style, name); Bind(wxEVT_PAINT, &HyperLinkCtrlWrapper::OnPaint, this); } - + void OnPaint(wxPaintEvent& evt) { wxPaintDC dc(this); @@ -201,7 +201,7 @@ namespace dc.SetTextBackground(GetBackgroundColour()); auto labelRect = GetLabelRect(); - + dc.DrawText(GetLabel(), labelRect.GetTopLeft()); if (HasFocus()) AColor::DrawFocus(dc, labelRect); @@ -218,7 +218,7 @@ namespace { if(childId != wxACC_SELF) return wxACC_NOT_IMPLEMENTED; - + if(auto movable = wxDynamicCast(GetWindow(), MovableControl)) //i18n-hint: argument - position of the effect in the effect stack *name = wxString::Format(_("Effect %d"), movable->FindIndexInParent() + 1); @@ -290,7 +290,7 @@ namespace std::shared_ptr<SampleTrack> mTrack; std::shared_ptr<RealtimeEffectState> mEffectState; std::shared_ptr<EffectSettingsAccess> mSettingsAccess; - + RealtimeEffectPicker* mEffectPicker { nullptr }; ThemedAButtonWrapper<AButton>* mChangeButton{nullptr}; @@ -318,7 +318,7 @@ namespace const wxSize& size = wxDefaultSize) { mEffectPicker = effectPicker; - + //Prevents flickering and paint order issues MovableControl::SetBackgroundStyle(wxBG_STYLE_PAINT); MovableControl::Create(parent, winid, pos, size, wxNO_BORDER | wxWANTS_CHARS); @@ -363,7 +363,7 @@ namespace changeButton->SetBackgroundColorIndex(clrEffectListItemBackground); changeButton->SetTranslatableLabel(XO("Replace effect")); changeButton->Bind(wxEVT_BUTTON, &RealtimeEffectControl::OnChangeButtonClicked, this); - + auto dragArea = safenew wxStaticBitmap(this, wxID_ANY, theTheme.Bitmap(bmpDragArea)); dragArea->Disable(); sizer->Add(dragArea, 0, wxLEFT | wxCENTER, 5); @@ -408,8 +408,8 @@ namespace mEffectState = pState; mSubscription = mEffectState->Subscribe([this](RealtimeEffectStateChange state) { - state == RealtimeEffectStateChange::EffectOn - ? mEnableButton->PushDown() + state == RealtimeEffectStateChange::EffectOn + ? mEnableButton->PushDown() : mEnableButton->PopUp(); if (mProject) @@ -491,13 +491,13 @@ namespace const auto effectID = mEffectPicker->PickEffect(mChangeButton, mEffectState->GetID()); if(!effectID) return;//nothing - + if(effectID->empty()) { RemoveFromList(); return; } - + auto &em = RealtimeEffectManager::Get(*mProject); auto oIndex = em.FindState(&*mTrack, mEffectState); if (!oIndex) @@ -571,7 +571,7 @@ class RealtimeEffectListWindow wxWindow* mEffectListContainer{nullptr}; std::unique_ptr<MenuRegistry::MenuItem> mEffectMenuRoot; - + Observer::Subscription mEffectListItemMovedSubscription; Observer::Subscription mPluginsChangedSubscription; @@ -621,7 +621,7 @@ public: this, wxID_ANY, _("Watch video"), "https://www.audacityteam.org/realtime-video", wxDefaultPosition, wxDefaultSize, wxHL_ALIGN_LEFT | wxHL_CONTEXTMENU); - + //i18n-hint: Hyperlink to the effects stack panel tutorial video addEffectTutorialLink->SetTranslatableLabel(XO("Watch video")); #if wxUSE_ACCESSIBILITY @@ -720,7 +720,7 @@ public: } }); SetScrollRate(0, 20); - + mPluginsChangedSubscription = PluginManager::Get().Subscribe( [this](PluginsChangedMessage) { @@ -733,12 +733,12 @@ public: { UpdateEffectMenuItems(); } - + std::optional<wxString> PickEffect(wxWindow* parent, const wxString& selectedEffectID) override { if (mProject == nullptr) return {}; - + wxMenu menu; if(!selectedEffectID.empty()) { @@ -746,31 +746,33 @@ public: menu.Append(wxID_REMOVE, _("No Effect")); menu.AppendSeparator(); } - + RealtimeEffectsMenuVisitor visitor { menu }; - + Registry::VisitWithFunctions(visitor, mEffectMenuRoot.get(), {}, *mProject); - + int commandId = wxID_NONE; - + menu.AppendSeparator(); +#if defined(__WXMSW__) || defined(__WXMAC__) menu.Append(wxID_MORE, _("Get more effects...")); - +#endif + menu.Bind(wxEVT_MENU, [&](wxCommandEvent evt) { commandId = evt.GetId(); }); - + if(parent->PopupMenu(&menu, parent->GetClientRect().GetLeftBottom()) && commandId != wxID_NONE) { if(commandId == wxID_REMOVE) return wxString {}; else if(commandId == wxID_MORE) - OpenInDefaultBrowser("https://plugins.audacityteam.org/"); + OpenInDefaultBrowser("https://www.musehub.com"); else return visitor.GetPluginID(commandId).GET(); } - + return {}; } - + void UpdateEffectMenuItems() { using namespace MenuRegistry; @@ -792,22 +794,22 @@ public: {}, groupby, nullptr, realtimeEffectPredicate ); - + if(!submenu->empty()) { root->push_back(move(analyzeSection)); } - + MenuHelper::PopulateEffectsMenu( *root, EffectTypeProcess, {}, groupby, nullptr, realtimeEffectPredicate ); - + mEffectMenuRoot.swap(root); } - + void OnSizeChanged(wxSizeEvent& event) { if(auto sizerItem = GetSizer()->GetItem(mAddEffectHint)) @@ -838,18 +840,18 @@ public: // Don't need to auto-save changed settings of effect that is deleted // Undo history push will do it anyway ui.Hide(); - + auto window = sizer->GetItem(msg.srcIndex)->GetWindow(); sizer->Remove(msg.srcIndex); wxTheApp->CallAfter([ref = wxWeakRef { window }] { if(ref) ref->Destroy(); }); - + if(sizer->IsEmpty()) { if(mEffectListContainer->IsDescendant(FindFocus())) mAddEffect->SetFocus(); - + mEffectListContainer->Hide(); mAddEffectHint->Show(); mAddEffectTutorialLink->Show(); @@ -872,7 +874,7 @@ public: window->MoveAfterInTabOrder(sizer->GetItem(msg.dstIndex)->GetWindow()); else window->MoveBeforeInTabOrder(sizer->GetItem(msg.dstIndex)->GetWindow()); - + sizer->Remove(msg.srcIndex); sizer->Insert(msg.dstIndex, window, proportion, flag, border); } @@ -935,16 +937,16 @@ public: void ReloadEffectsList() { wxWindowUpdateLocker freeze(this); - + const auto hadFocus = mEffectListContainer->IsDescendant(FindFocus()); //delete items that were added to the sizer mEffectListContainer->Hide(); mEffectListContainer->GetSizer()->Clear(true); - + if(!mTrack || RealtimeEffectList::Get(*mTrack).GetStatesCount() == 0) mEffectListContainer->Hide(); - + auto isEmpty{true}; if(mTrack) { @@ -959,7 +961,7 @@ public: mEffectListContainer->Show(!isEmpty); mAddEffectHint->Show(isEmpty); mAddEffectTutorialLink->Show(isEmpty); - + SendSizeEventToParent(); } @@ -969,7 +971,7 @@ public: return; const auto effectID = PickEffect(dynamic_cast<wxWindow*>(event.GetEventObject()), {}); - + if(!effectID || effectID->empty()) return; @@ -1092,7 +1094,7 @@ RealtimeEffectPanel::RealtimeEffectPanel( if (mEffectList) { mEffectList->EnableEffects(mToggleEffects->IsDown()); - + ProjectHistory::Get(mProject).ModifyState(false); UndoManager::Get(mProject).MarkUnsaved(); } @@ -1146,7 +1148,7 @@ RealtimeEffectPanel::RealtimeEffectPanel( if (waveTrack == nullptr) return; - + switch (evt.mType) { case TrackListEvent::TRACK_DATA_CHANGE: @@ -1182,7 +1184,7 @@ RealtimeEffectPanel::RealtimeEffectPanel( // Realtime effect UI is only updated on Undo or Redo auto waveTracks = trackList.Any<WaveTrack>(); - + if ( message.type == UndoRedoMessage::Type::UndoOrRedo || message.type == UndoRedoMessage::Type::Reset) @@ -1200,7 +1202,7 @@ RealtimeEffectPanel::RealtimeEffectPanel( // Collect RealtimeEffectUIs that are currently shown // for the potentially removed tracks std::vector<RealtimeEffectStateUI*> shownUIs; - + for (auto track : mPotentiallyRemovedTracks) { // By construction, track cannot be null @@ -1220,9 +1222,9 @@ RealtimeEffectPanel::RealtimeEffectPanel( for (auto effectUI : shownUIs) { bool reachable = false; - + for (auto track : waveTracks) - { + { VisitRealtimeEffectStateUIs( *track, [effectUI, &reachable](auto& ui) diff --git a/src/menus/PluginMenus.cpp b/src/menus/PluginMenus.cpp index ecacf6e57b91380afc86787c1ded9652c339ee36..5375a0409f5ce62199efe765d529c00a1b4b3966 100644 --- a/src/menus/PluginMenus.cpp +++ b/src/menus/PluginMenus.cpp @@ -1,39 +1,39 @@ -#include "AudioIO.h" #include "../Benchmark.h" -#include "../commands/CommandDispatch.h" #include "../CommonCommandFlags.h" -#include "Journal.h" #include "../MenuCreator.h" -#include "PluginManager.h" #include "../PluginRegistrationDialog.h" +#include "../ProjectWindows.h" +#include "../commands/CommandDispatch.h" +#include "../effects/EffectManager.h" +#include "../effects/EffectUI.h" +#include "../prefs/PrefsDialog.h" +#include "../toolbars/SelectionBar.h" +#include "../toolbars/ToolManager.h" +#include "AudacityMessageBox.h" +#include "AudioIO.h" +#include "CommandContext.h" +#include "CommandManager.h" +#include "HelpSystem.h" +#include "Journal.h" +#include "MenuHelper.h" +#include "PluginManager.h" #include "Prefs.h" #include "Project.h" #include "ProjectRate.h" #include "ProjectSnap.h" -#include "../ProjectWindows.h" +#include "RealtimeEffectManager.h" #include "RealtimeEffectPanel.h" #include "SampleTrack.h" #include "SyncLock.h" -#include "../toolbars/ToolManager.h" -#include "../toolbars/SelectionBar.h" -#include "TrackFocus.h" #include "TempDirectory.h" +#include "TrackFocus.h" #include "UndoManager.h" #include "Viewport.h" -#include "CommandContext.h" -#include "CommandManager.h" -#include "../effects/EffectManager.h" -#include "../effects/EffectUI.h" -#include "RealtimeEffectManager.h" -#include "../prefs/PrefsDialog.h" -#include "AudacityMessageBox.h" -#include "MenuHelper.h" #include "prefs/EffectsPrefs.h" - // private helper classes and functions namespace { - + bool ShowManager(wxWindow *parent, int effectsCategory) { PluginRegistrationDialog dlg(parent, effectsCategory); @@ -100,7 +100,7 @@ void OnResetConfig(const CommandContext &context) ToolManager::OnResetToolBars(context); // These are necessary to preserve the newly correctly laid out toolbars. - // In particular the Device Toolbar ends up short on next restart, + // In particular the Device Toolbar ends up short on next restart, // if they are left out. gPrefs->Write(wxT("/PrefsVersion"), wxString(wxT(AUDACITY_PREFS_VERSION_STRING))); @@ -113,7 +113,7 @@ void OnResetConfig(const CommandContext &context) ProjectSnap::Get(project).SetSnapTo(SnapToSetting.Read()); ProjectSnap::Get(project).SetSnapMode(SnapModeSetting.ReadEnum()); - + ProjectRate::Get( project ) .SetRate(gPrefs->ReadDouble("/DefaultProjectSampleRate", 44100.0)); } @@ -346,22 +346,31 @@ auto EffectMenu() { // All of this is a bit hacky until we can get more things connected into // the plugin manager...sorry! :-( - static auto menu = std::shared_ptr{ - Menu( wxT("Effect"), XXO("Effe&ct"), - Section( "Manage", - Command( wxT("ManageEffects"), XXO("Plugin Manager"), - OnManageEffects, AudioIONotBusyFlag() ) - ), - - Section( "RealtimeEffects", - Command ( wxT("AddRealtimeEffects"), XXO("Add Realtime Effects"), - OnAddRealtimeEffects, HasTrackFocusFlag(), wxT("E") ) - ), - - Section( "RepeatLast", + static auto menu = std::shared_ptr { Menu( + wxT("Effect"), XXO("Effe&ct"), + Section( + "Manage", Command( + wxT("ManageEffects"), XXO("Plugin Manager"), + OnManageEffects, AudioIONotBusyFlag())), + + Section( + "RealtimeEffects", + Command( + wxT("AddRealtimeEffects"), XXO("Add Realtime Effects"), + OnAddRealtimeEffects, HasTrackFocusFlag(), wxT("E")) +#if defined(__WXMSW__) || defined(__WXMAC__) + , Command( + wxT("GetMoreEffects"), XXO("Get more effects..."), + [](const CommandContext&) { + OpenInDefaultBrowser("https://www.musehub.com"); + }, + AlwaysEnabledFlag) +#endif + ), + Section( + "RepeatLast", // Delayed evaluation: - [](AudacityProject &project) - { + [](AudacityProject& project) { const auto &lastEffect = CommandManager::Get(project).mLastEffect; TranslatableString buildMenuLabel; if (!lastEffect.empty()) @@ -375,12 +384,12 @@ auto EffectMenu() AudioIONotBusyFlag() | TimeSelectedFlag() | WaveTracksSelectedFlag() | HasLastEffectFlag(), wxT("Ctrl+R") ); - } - ), + }), - Section( "Effects", + Section( + "Effects", // Delayed evaluation: - [](AudacityProject &) { + [](AudacityProject&) { auto result = Items(""); MenuHelper::PopulateEffectsMenu( *result, @@ -389,9 +398,7 @@ auto EffectMenu() EffectsGroupBy.Read(), &OnEffect); return result; - } - ) - ) }; + })) }; return menu; }