From 280e8d9bac8c66d5e8669470e1ac3f689110b3bc Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Sun, 21 Oct 2018 19:37:11 -0400 Subject: [PATCH] Some machinery to add more menu handler object classes... ...without adding linkage dependencies to AudacityProject constructor --- src/Menus.cpp | 17 ++++++++++- src/Menus.h | 10 ++++++- src/Project.cpp | 76 +++++++++++++++++++++++++++++++++++++++++++++++-- src/Project.h | 22 +++++++++++++- 4 files changed, 120 insertions(+), 5 deletions(-) diff --git a/src/Menus.cpp b/src/Menus.cpp index e71e9b907..aa9e5bb98 100644 --- a/src/Menus.cpp +++ b/src/Menus.cpp @@ -171,8 +171,23 @@ static void AddEffectMenuItemGroup( constexpr size_t kAlignLabelsCount = 5; +static const AudacityProject::RegisteredAttachedObjectFactory factory{ []{ + return std::make_unique< MenuCommandHandler >(); +} }; + +PrefsListener::~PrefsListener() +{ +} + +void PrefsListener::UpdatePrefs() +{ +} + MenuCommandHandler &GetMenuCommandHandler(AudacityProject &project) -{ return *project.mMenuCommandHandler; } +{ + return static_cast( + project.GetAttachedObject( factory ) ); +} MenuManager &GetMenuManager(AudacityProject &project) { return *project.mMenuManager; } diff --git a/src/Menus.h b/src/Menus.h index da52f3e03..801966e0d 100644 --- a/src/Menus.h +++ b/src/Menus.h @@ -35,8 +35,16 @@ enum EffectType : int; typedef wxString PluginID; typedef wxArrayString PluginIDList; +class PrefsListener +{ +public: + virtual ~PrefsListener(); + virtual void UpdatePrefs(); // default is no-op +}; + struct MenuCommandHandler final : public CommandHandlerObject // MUST be the first base class! + , public PrefsListener { MenuCommandHandler(); ~MenuCommandHandler(); @@ -592,7 +600,7 @@ public: bool mCircularTrackNavigation{}; wxLongLong mLastSelectionAdjustment; - void UpdatePrefs(); + void UpdatePrefs() override; }; class MenuCreator diff --git a/src/Project.cpp b/src/Project.cpp index 1614b8b1e..7d4bab702 100644 --- a/src/Project.cpp +++ b/src/Project.cpp @@ -98,6 +98,7 @@ scroll information. It also has some status flags. #include "Dependencies.h" #include "Diags.h" #include "HistoryWindow.h" +#include "InconsistencyException.h" #include "Lyrics.h" #include "LyricsWindow.h" #include "MixerBoard.h" @@ -888,6 +889,64 @@ static wxString CreateUniqueName() wxString::Format(wxT(" N-%i"), ++count); } +namespace { + +#if 0 +std::mutex sObjectFactoriesMutex; +struct ObjectFactorySetLocker : private std::unique_lock< std::mutex > +{ + ObjectFactorySetLocker() + : std::unique_lock< std::mutex > { sObjectFactoriesMutex } + {} +}; +#else +struct ObjectFactorySetLocker {}; +#endif + +std::vector &sObjectFactories() +{ + // Put this declaration inside a function to avoid problems of undefined + // sequence of initialization of file-scope statics in different + // compilation units. + // Note that mutex locking is not needed for constructing a static object + // in C++11: + //https://en.cppreference.com/w/cpp/language/storage_duration#Static_local_variables + static std::vector factories; + return factories; +} +} + +AudacityProject:: +RegisteredAttachedObjectFactory::RegisteredAttachedObjectFactory( + const AttachedObjectFactory &factory ) +{ + ObjectFactorySetLocker locker; + mIndex = sObjectFactories().size(); + sObjectFactories().push_back( factory ); + + // In case registration happens while projects exist: + for (const auto &pProject : gAudacityProjects) { + if (pProject->mAttachedObjects.size() == mIndex) { + auto pObject = factory(); + wxASSERT( pObject ); + pProject->mAttachedObjects.push_back( std::move( pObject ) ); + } + } +} + +AudacityProject:: +AttachedObject &AudacityProject::GetAttachedObject( + const RegisteredAttachedObjectFactory &factory ) +{ + ObjectFactorySetLocker locker; + if ( factory.mIndex >= mAttachedObjects.size() ) + THROW_INCONSISTENCY_EXCEPTION; + auto &pObject = mAttachedObjects[ factory.mIndex ]; + if ( !pObject ) + THROW_INCONSISTENCY_EXCEPTION; + return *pObject; +} + enum { FirstID = 1000, @@ -982,7 +1041,15 @@ AudacityProject::AudacityProject(wxWindow * parent, wxWindowID id, // Initialize view info (shared with TrackPanel) // - mMenuCommandHandler = std::make_unique(); + { + ObjectFactorySetLocker locker; + for (const auto &factory : sObjectFactories()) { + auto pObject = factory(); + wxASSERT( pObject ); + mAttachedObjects.push_back( std::move( pObject ) ); + } + } + mMenuManager = std::make_unique(); UpdatePrefs(); @@ -1364,7 +1431,12 @@ void AudacityProject::UpdatePrefs() SetProjectTitle(); - GetMenuCommandHandler(*this).UpdatePrefs(); + { + ObjectFactorySetLocker locker; + for( const auto &pObject : mAttachedObjects ) + pObject->UpdatePrefs(); + } + GetMenuManager(*this).UpdatePrefs(); if (mTrackPanel) { diff --git a/src/Project.h b/src/Project.h index 8b033b82f..32120eae4 100644 --- a/src/Project.h +++ b/src/Project.h @@ -172,6 +172,8 @@ class WaveTrack; struct MenuCommandHandler; class MenuManager; +class PrefsListener; + class AUDACITY_DLL_API AudacityProject final : public wxFrame, public TrackPanelListener, public SelectionBarListener, @@ -184,6 +186,24 @@ class AUDACITY_DLL_API AudacityProject final : public wxFrame, const wxPoint & pos, const wxSize & size); virtual ~AudacityProject(); + using AttachedObject = PrefsListener; + using AttachedObjectFactory = + std::function< std::unique_ptr() >; + + // Typically a static object. Allows various application code to + // attach per-project state, without Project.cpp needing to include a header + // file or know the details. + class RegisteredAttachedObjectFactory { + public: + RegisteredAttachedObjectFactory( const AttachedObjectFactory &factory ); + + private: + friend AudacityProject; + size_t mIndex {}; + }; + AttachedObject & + GetAttachedObject( const RegisteredAttachedObjectFactory& factory ); + virtual void ApplyUpdatedTheme(); AudioIOStartStreamOptions GetDefaultPlayOptions(); @@ -807,7 +827,7 @@ private: #endif private: - std::unique_ptr mMenuCommandHandler; + std::vector< std::unique_ptr > mAttachedObjects; std::unique_ptr mMenuManager; public: