diff --git a/src/ProjectWindows.cpp b/src/ProjectWindows.cpp new file mode 100644 index 000000000..67010c617 --- /dev/null +++ b/src/ProjectWindows.cpp @@ -0,0 +1,172 @@ +/********************************************************************** + + Audacity: A Digital Audio Editor + + Project.cpp + + Dominic Mazzoni + Vaughan Johnson + +*//*******************************************************************/ + + +#include "Project.h" + +#include "widgets/wxWidgetsBasicUI.h" + +#include +#include +#include + +wxDEFINE_EVENT(EVT_TRACK_PANEL_TIMER, wxCommandEvent); + +size_t AllProjects::size() const +{ + return gAudacityProjects.size(); +} + +auto AllProjects::begin() const -> const_iterator +{ + return gAudacityProjects.begin(); +} + +auto AllProjects::end() const -> const_iterator +{ + return gAudacityProjects.end(); +} + +auto AllProjects::rbegin() const -> const_reverse_iterator +{ + return gAudacityProjects.rbegin(); +} + +auto AllProjects::rend() const -> const_reverse_iterator +{ + return gAudacityProjects.rend(); +} + +auto AllProjects::Remove( AudacityProject &project ) -> value_type +{ + std::lock_guard guard{ Mutex() }; + auto start = begin(), finish = end(), iter = std::find_if( + start, finish, + [&]( const value_type &ptr ){ return ptr.get() == &project; } + ); + if (iter == finish) + return nullptr; + auto result = *iter; + gAudacityProjects.erase( iter ); + return result; +} + +void AllProjects::Add( const value_type &pProject ) +{ + if (!pProject) { + wxASSERT(false); + return; + } + std::lock_guard guard{ Mutex() }; + gAudacityProjects.push_back( pProject ); +} + +std::mutex &AllProjects::Mutex() +{ + static std::mutex theMutex; + return theMutex; +} + +int AudacityProject::mProjectCounter=0;// global counter. + +/* Define Global Variables */ +//This array holds onto all of the projects currently open +AllProjects::Container AllProjects::gAudacityProjects; + +AudacityProject::AudacityProject() +{ + mProjectNo = mProjectCounter++; // Bug 322 + AttachedObjects::BuildAll(); + // But not for the attached windows. They get built only on demand, such as + // from menu items. +} + +AudacityProject::~AudacityProject() +{ +} + +void AudacityProject::SetFrame( wxFrame *pFrame ) +{ + mFrame = pFrame; +} + +void AudacityProject::SetPanel( wxWindow *pPanel ) +{ + mPanel = pPanel; +} + +const wxString &AudacityProject::GetProjectName() const +{ + return mName; +} + +void AudacityProject::SetProjectName(const wxString &name) +{ + mName = name; +} + +FilePath AudacityProject::GetInitialImportPath() const +{ + return mInitialImportPath; +} + +void AudacityProject::SetInitialImportPath(const FilePath &path) +{ + if (mInitialImportPath.empty()) + { + mInitialImportPath = path; + } +} + +TENACITY_DLL_API wxFrame &GetProjectFrame( AudacityProject &project ) +{ + auto ptr = project.GetFrame(); + if ( !ptr ) + THROW_INCONSISTENCY_EXCEPTION; + return *ptr; +} + +TENACITY_DLL_API const wxFrame &GetProjectFrame( const AudacityProject &project ) +{ + auto ptr = project.GetFrame(); + if ( !ptr ) + THROW_INCONSISTENCY_EXCEPTION; + return *ptr; +} + +std::unique_ptr +ProjectFramePlacement( AudacityProject *project ) +{ + if (!project) + return std::make_unique(); + return std::make_unique( + &GetProjectFrame(*project)); +} + +TENACITY_DLL_API wxWindow &GetProjectPanel( AudacityProject &project ) +{ + auto ptr = project.GetPanel(); + if ( !ptr ) + THROW_INCONSISTENCY_EXCEPTION; + return *ptr; +} + +TENACITY_DLL_API const wxWindow &GetProjectPanel( + const AudacityProject &project ) +{ + auto ptr = project.GetPanel(); + if ( !ptr ) + THROW_INCONSISTENCY_EXCEPTION; + return *ptr; +} + +// Generate the needed, linkable registry functions +DEFINE_XML_METHOD_REGISTRY( ProjectFileIORegistry ); diff --git a/src/ProjectWindows.h b/src/ProjectWindows.h new file mode 100644 index 000000000..394edc7b2 --- /dev/null +++ b/src/ProjectWindows.h @@ -0,0 +1,187 @@ +/********************************************************************** + + Audacity: A Digital Audio Editor + + Project.h + + Dominic Mazzoni + +**********************************************************************/ + +#ifndef __AUDACITY_PROJECT__ +#define __AUDACITY_PROJECT__ + +#include "Identifier.h" + +#include "ClientData.h" // to inherit + +#include +#include +#include // member variable +#include // MSVC wants this + +class wxFrame; +class wxWindow; +namespace BasicUI { class WindowPlacement; } + +class AudacityProject; + +//! Like a standard library container of all open projects. +//! @invariant pointers accessible through the iterators are not null +/*! +So you can iterate easily over shared pointers to them with range-for : +for (auto pProject : AllProjects{}) { ... } +The pointers are never null. + +However iterators will be invalid if addition or deletion of projects occur +during traversal. +*/ +class TENACITY_DLL_API AllProjects +{ + + // Use shared_ptr to projects, because elsewhere we need weak_ptr + using AProjectHolder = std::shared_ptr< AudacityProject >; + using Container = std::vector< AProjectHolder >; + static Container gAudacityProjects; + +public: + AllProjects() = default; + + size_t size() const; + bool empty() const { return size() == 0; } + + using const_iterator = Container::const_iterator; + const_iterator begin() const; + const_iterator end() const; + + using const_reverse_iterator = Container::const_reverse_iterator; + const_reverse_iterator rbegin() const; + const_reverse_iterator rend() const; + + using value_type = Container::value_type; + + // If the project is present, remove it from the global array and return + // a shared pointer, else return null. This invalidates any iterators. + value_type Remove( AudacityProject &project ); + + //! This invalidates iterators + /*! + @pre pProject is not null + */ + void Add( const value_type &pProject ); + + /// In case you must iterate in a non-main thread, use this to prevent + /// changes in the set of open projects + static std::mutex &Mutex(); +}; + +// Container of various objects associated with the project, which is +// responsible for destroying them +using AttachedProjectObjects = ClientData::Site< + AudacityProject, ClientData::Base, ClientData::SkipCopying, std::shared_ptr +>; +// Container of pointers to various windows associated with the project, which +// is not responsible for destroying them -- wxWidgets handles that instead +using AttachedProjectWindows = ClientData::Site< + AudacityProject, wxWindow, ClientData::SkipCopying, ClientData::BarePtr +>; + +wxDECLARE_EXPORTED_EVENT(TENACITY_DLL_API, + EVT_TRACK_PANEL_TIMER, wxCommandEvent); + +///\brief The top-level handle to an Audacity project. It serves as a source +/// of events that other objects can bind to, and a container of associated +/// sub-objects that it treats opaquely. It stores a filename and a status +/// message and a few other things. +/// There is very little in this class, most of the intelligence residing in +/// the cooperating attached objects. +class TENACITY_DLL_API AudacityProject final + : public wxEvtHandler + , public AttachedProjectObjects + , public AttachedProjectWindows + , public std::enable_shared_from_this +{ + public: + using AttachedObjects = ::AttachedProjectObjects; + using AttachedWindows = ::AttachedProjectWindows; + + AudacityProject(); + virtual ~AudacityProject(); + + wxFrame *GetFrame() { return mFrame; } + const wxFrame *GetFrame() const { return mFrame; } + void SetFrame( wxFrame *pFrame ); + + wxWindow *GetPanel() { return mPanel; } + const wxWindow *GetPanel() const { return mPanel; } + void SetPanel( wxWindow *pPanel ); + + int GetProjectNumber(){ return mProjectNo;} + + // Project name can be either empty or have the name of the project. + // + // If empty, it signifies that the project is empty/unmodified or + // that the project hasn't yet been saved to a permanent project + // file. + // + // If a name has been assigned, it is merely used to identify + // the project and should not be used for any other purposes. + const wxString &GetProjectName() const; + void SetProjectName(const wxString &name); + + // Used exclusively in batch mode, this allows commands to remember + // and use the initial import path + FilePath GetInitialImportPath() const; + void SetInitialImportPath(const FilePath &path); + +private: + + // The project's name + wxString mName; + + static int mProjectCounter;// global counter. + int mProjectNo; // count when this project was created. + + FilePath mInitialImportPath; + + public: + bool mbBusyImporting{ false }; // used to fix bug 584 + int mBatchMode{ 0 };// 0 means not, >0 means in batch mode. + + private: + wxWeakRef< wxFrame > mFrame{}; + wxWeakRef< wxWindow > mPanel{}; +}; + +///\brief Get the top-level window associated with the project (as a wxFrame +/// only, when you do not need to use the subclass ProjectWindow) +TENACITY_DLL_API wxFrame &GetProjectFrame( AudacityProject &project ); +TENACITY_DLL_API const wxFrame &GetProjectFrame( const AudacityProject &project ); + +///\brief Get a pointer to the window associated with a project, or null if +/// the given pointer is null. +inline wxFrame *FindProjectFrame( AudacityProject *project ) { + return project ? &GetProjectFrame( *project ) : nullptr; +} +inline const wxFrame *FindProjectFrame( const AudacityProject *project ) { + return project ? &GetProjectFrame( *project ) : nullptr; +} + +//! Make a WindowPlacement object suitable for `project` (which may be null) +/*! @post return value is not null */ +TENACITY_DLL_API std::unique_ptr +ProjectFramePlacement( AudacityProject *project ); + +///\brief Get the main sub-window of the project frame that displays track data +// (as a wxWindow only, when you do not need to use the subclass TrackPanel) +TENACITY_DLL_API wxWindow &GetProjectPanel( AudacityProject &project ); +TENACITY_DLL_API const wxWindow &GetProjectPanel( + const AudacityProject &project ); + +// Generate a registry for serialized data attached to the project +#include "XMLMethodRegistry.h" +class AudacityProject; +using ProjectFileIORegistry = XMLMethodRegistry; +DECLARE_XML_METHOD_REGISTRY( TENACITY_DLL_API, ProjectFileIORegistry ); + +#endif