diff --git a/locale/POTFILES.in b/locale/POTFILES.in index cd38332bd..89a29e878 100644 --- a/locale/POTFILES.in +++ b/locale/POTFILES.in @@ -156,6 +156,8 @@ src/Profiler.cpp src/Profiler.h src/Project.cpp src/Project.h +src/ProjectFileIORegistry.cpp +src/ProjectFileIORegistry.h src/ProjectFSCK.cpp src/ProjectFSCK.h src/RealFFTf.cpp diff --git a/mac/Audacity.xcodeproj/project.pbxproj b/mac/Audacity.xcodeproj/project.pbxproj index 70c85c39b..cc24b65ed 100644 --- a/mac/Audacity.xcodeproj/project.pbxproj +++ b/mac/Audacity.xcodeproj/project.pbxproj @@ -1224,6 +1224,7 @@ 5E1512701DB0010C00702E29 /* TrackVRulerControls.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E15126B1DB0010C00702E29 /* TrackVRulerControls.cpp */; }; 5E16FF4D1FF9CE0B0085E1B8 /* LanguageNames.txt in Resources */ = {isa = PBXBuildFile; fileRef = 5E16FF4C1FF9CE0B0085E1B8 /* LanguageNames.txt */; }; 5E18CFF322931D3D00E75250 /* AudacityMessageBox.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E18CFF222931D3D00E75250 /* AudacityMessageBox.cpp */; }; + 5E18CFF02291C31000E75250 /* ProjectFileIORegistry.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E18CFEE2291C31000E75250 /* ProjectFileIORegistry.cpp */; }; 5E19D655217D51190024D0B1 /* PluginMenus.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E19D64C217D51190024D0B1 /* PluginMenus.cpp */; }; 5E2A19941EED688500217B58 /* SelectionState.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E2A19921EED688500217B58 /* SelectionState.cpp */; }; 5E36A0A8217FA2430068E082 /* EditMenus.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E36A09F217FA2430068E082 /* EditMenus.cpp */; }; @@ -3198,6 +3199,8 @@ 5E16FF4C1FF9CE0B0085E1B8 /* LanguageNames.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = LanguageNames.txt; path = ../locale/LanguageNames.txt; sourceTree = ""; }; 5E18CFF122931CA900E75250 /* AudacityMessageBox.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AudacityMessageBox.h; sourceTree = ""; }; 5E18CFF222931D3D00E75250 /* AudacityMessageBox.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AudacityMessageBox.cpp; sourceTree = ""; }; + 5E18CFEE2291C31000E75250 /* ProjectFileIORegistry.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ProjectFileIORegistry.cpp; sourceTree = ""; }; + 5E18CFEF2291C31000E75250 /* ProjectFileIORegistry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ProjectFileIORegistry.h; sourceTree = ""; }; 5E19D64C217D51190024D0B1 /* PluginMenus.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PluginMenus.cpp; path = menus/PluginMenus.cpp; sourceTree = ""; }; 5E2A19921EED688500217B58 /* SelectionState.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SelectionState.cpp; sourceTree = ""; }; 5E2A19931EED688500217B58 /* SelectionState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SelectionState.h; sourceTree = ""; }; @@ -4353,6 +4356,7 @@ 1790B0CE09883BFD008A330A /* Printing.cpp */, 186CCEA30E523C8E00659159 /* Profiler.cpp */, 1790B0D009883BFD008A330A /* Project.cpp */, + 5E18CFEE2291C31000E75250 /* ProjectFileIORegistry.cpp */, 5ECF728C228B307E007F2A35 /* ProjectFSCK.cpp */, 28DABFBC0FF19DB100AC7848 /* RealFFTf.cpp */, EDFCEBA218894B2A00C98E51 /* RealFFTf48x.cpp */, @@ -4465,6 +4469,7 @@ 1790B0CF09883BFD008A330A /* Printing.h */, 186CCEA20E523C8D00659159 /* Profiler.h */, 1790B0D109883BFD008A330A /* Project.h */, + 5E18CFEF2291C31000E75250 /* ProjectFileIORegistry.h */, 5ECF728B228B307E007F2A35 /* ProjectFSCK.h */, 28DABFBD0FF19DB100AC7848 /* RealFFTf.h */, EDFCEBA318894B2A00C98E51 /* RealFFTf48x.h */, @@ -8422,6 +8427,7 @@ 2897F6F00AB3DB5A003C20C5 /* ControlToolBar.cpp in Sources */, 2897F6F10AB3DB5A003C20C5 /* EditToolBar.cpp in Sources */, 2897F6F20AB3DB5A003C20C5 /* MeterToolBar.cpp in Sources */, + 5E18CFF02291C31000E75250 /* ProjectFileIORegistry.cpp in Sources */, 2897F6F30AB3DB5A003C20C5 /* MixerToolBar.cpp in Sources */, 2897F6F40AB3DB5A003C20C5 /* SelectionBar.cpp in Sources */, 2897F6F50AB3DB5A003C20C5 /* ToolBar.cpp in Sources */, diff --git a/src/AdornedRulerPanel.cpp b/src/AdornedRulerPanel.cpp index 43987e3e1..57e21d285 100644 --- a/src/AdornedRulerPanel.cpp +++ b/src/AdornedRulerPanel.cpp @@ -130,6 +130,8 @@ public: private: AdornedRulerPanel *GetRuler() const; + unsigned SequenceNumber() const override; + std::pair DoGetRectangle(wxSize size) override; void Draw(OverlayPanel &panel, wxDC &dc) override; @@ -159,6 +161,7 @@ public: QuickPlayIndicatorOverlay(AudacityProject *project); private: + unsigned SequenceNumber() const override; std::pair DoGetRectangle(wxSize size) override; void Draw(OverlayPanel &panel, wxDC &dc) override; @@ -229,6 +232,12 @@ void AdornedRulerPanel::QuickPlayRulerOverlay::Update() } } +unsigned +AdornedRulerPanel::QuickPlayRulerOverlay::SequenceNumber() const +{ + return 30; +} + std::pair AdornedRulerPanel::QuickPlayRulerOverlay::DoGetRectangle(wxSize /*size*/) { @@ -284,6 +293,12 @@ AdornedRulerPanel::QuickPlayIndicatorOverlay::QuickPlayIndicatorOverlay( { } +unsigned +AdornedRulerPanel::QuickPlayIndicatorOverlay::SequenceNumber() const +{ + return 30; +} + std::pair AdornedRulerPanel::QuickPlayIndicatorOverlay::DoGetRectangle(wxSize size) { @@ -878,6 +893,9 @@ AdornedRulerPanel::AdornedRulerPanel(AudacityProject* project, wxTheApp->Bind(EVT_AUDIOIO_CAPTURE, &AdornedRulerPanel::OnRecordStartStop, this); + + // Delay until after CommandManager has been populated: + this->CallAfter( &AdornedRulerPanel::UpdatePrefs ); } AdornedRulerPanel::~AdornedRulerPanel() @@ -905,6 +923,14 @@ namespace { void AdornedRulerPanel::UpdatePrefs() { + if (mNeedButtonUpdate) { + // Visit this block once only in the lifetime of this panel + mNeedButtonUpdate = false; + // Do this first time setting of button status texts + // when we are sure the CommandManager is initialized. + ReCreateButtons(); + } + // Update button texts for language change UpdateButtonStates(); @@ -1087,16 +1113,6 @@ void AdornedRulerPanel::OnRecordStartStop(wxCommandEvent & evt) void AdornedRulerPanel::OnPaint(wxPaintEvent & WXUNUSED(evt)) { - if (mNeedButtonUpdate) { - // Visit this block once only in the lifetime of this panel - mNeedButtonUpdate = false; - // Do this first time setting of button status texts - // when we are sure the CommandManager is initialized. - ReCreateButtons(); - // Sends a resize event, which will cause a second paint. - UpdatePrefs(); - } - wxPaintDC dc(this); auto &backDC = GetBackingDCForRepaint(); @@ -1574,18 +1590,17 @@ void AdornedRulerPanel::StartQPPlay(bool looped, bool cutPreview) else options.timeTrack = NULL; - ControlToolBar::PlayAppearance appearance = - cutPreview ? ControlToolBar::PlayAppearance::CutPreview - : options.playLooped ? ControlToolBar::PlayAppearance::Looped - : ControlToolBar::PlayAppearance::Straight; + auto mode = + cutPreview ? PlayMode::cutPreviewPlay + : options.playLooped ? PlayMode::loopedPlay + : PlayMode::normalPlay; mPlayRegionStart = start; mPlayRegionEnd = end; Refresh(); ctb->PlayPlayRegion((SelectedRegion(start, end)), - options, PlayMode::normalPlay, - appearance, + options, mode, false, true); @@ -1785,8 +1800,11 @@ void AdornedRulerPanel::OnAutoScroll(wxCommandEvent&) gPrefs->Write(wxT("/GUI/AutoScroll"), false); else gPrefs->Write(wxT("/GUI/AutoScroll"), true); - mProject->UpdatePrefs(); + gPrefs->Flush(); + + wxTheApp->AddPendingEvent(wxCommandEvent{ + EVT_PREFS_UPDATE, ViewInfo::UpdateScrollPrefsID() }); } diff --git a/src/AdornedRulerPanel.h b/src/AdornedRulerPanel.h index 7d2f95c35..572a47f18 100644 --- a/src/AdornedRulerPanel.h +++ b/src/AdornedRulerPanel.h @@ -13,6 +13,7 @@ #include "CellularPanel.h" #include "widgets/Ruler.h" // member variable +#include "Prefs.h" class ViewInfo; class AudacityProject; @@ -20,7 +21,9 @@ class SnapManager; class TrackList; // This is an Audacity Specific ruler panel. -class AUDACITY_DLL_API AdornedRulerPanel final : public CellularPanel +class AUDACITY_DLL_API AdornedRulerPanel final +: public CellularPanel +, private PrefsListener { public: AdornedRulerPanel(AudacityProject *project, @@ -53,7 +56,7 @@ public: void InvalidateRuler(); - void UpdatePrefs(); + void UpdatePrefs() override; void ReCreateButtons(); void RegenerateTooltips(); diff --git a/src/AudacityApp.cpp b/src/AudacityApp.cpp index eab26b7be..2facb0bf0 100644 --- a/src/AudacityApp.cpp +++ b/src/AudacityApp.cpp @@ -1005,9 +1005,9 @@ Choose Help > Diagnostics > Check Dependencies to view a list of \ locations of the missing files."), missingFileName); // if an old dialog exists, raise it if it is - if (offendingProject->GetMissingAliasFileDialog()) { - offendingProject->GetMissingAliasFileDialog()->Raise(); - } else { + if ( auto dialog = MissingAliasFilesDialog::Find( *offendingProject ) ) + dialog->Raise(); + else { MissingAliasFilesDialog::Show(offendingProject.get(), _("Files Missing"), errorMessage, wxT(""), true); } diff --git a/src/AudioIO.cpp b/src/AudioIO.cpp index aa3e73916..53de3a852 100644 --- a/src/AudioIO.cpp +++ b/src/AudioIO.cpp @@ -5371,7 +5371,7 @@ void AudioIoCallback::SendVuOutputMeterData( //MixerBoard* pMixerBoard = mOwningProject->GetMixerBoard(); //if (pMixerBoard) // pMixerBoard->UpdateMeters(GetStreamTime(), - // (pProj->mLastPlayMode == loopedPlay)); + // (pProj->GetControlToolBar()->GetLastPlayMode() == loopedPlay)); } mUpdatingMeters = false; } diff --git a/src/BatchCommands.cpp b/src/BatchCommands.cpp index c86ff3b1d..ddd3215b7 100644 --- a/src/BatchCommands.cpp +++ b/src/BatchCommands.cpp @@ -729,7 +729,7 @@ bool MacroCommands::ApplyEffectCommand( // IF nothing selected, THEN select everything // (most effects require that you have something selected). if( plug->GetPluginType() != PluginTypeAudacityCommand ) - project->SelectAllIfNone(); + SelectActions::SelectAllIfNone( *project ); bool res = false; diff --git a/src/LabelTrack.cpp b/src/LabelTrack.cpp index dfcf3e2d0..659112b36 100644 --- a/src/LabelTrack.cpp +++ b/src/LabelTrack.cpp @@ -63,6 +63,7 @@ for drawing different aspects of the label and its text box. #include "AllThemeResources.h" #include "AColor.h" #include "Project.h" +#include "ProjectFileIORegistry.h" #include "TrackArtist.h" #include "TrackPanel.h" #include "UndoManager.h" @@ -98,6 +99,15 @@ int LabelTrack::mTextHeight; int LabelTrack::mFontHeight=-1; +static ProjectFileIORegistry::Entry registerFactory{ + wxT( "labeltrack" ), + []( AudacityProject &project ){ + auto &trackFactory = *project.GetTrackFactory(); + auto &tracks = *project.GetTracks(); + return tracks.Add(trackFactory.NewLabelTrack()); + } +}; + LabelTrack::Holder TrackFactory::NewLabelTrack() { return std::make_shared(mDirManager); diff --git a/src/Makefile.am b/src/Makefile.am index 231186b1a..a90377f67 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -207,6 +207,8 @@ audacity_SOURCES = \ Profiler.h \ Project.cpp \ Project.h \ + ProjectFileIORegistry.cpp \ + ProjectFileIORegistry.h \ ProjectFSCK.cpp \ ProjectFSCK.h \ RealFFTf.cpp \ diff --git a/src/Makefile.in b/src/Makefile.in index 9b732bb31..287a8dd22 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -319,10 +319,11 @@ am__audacity_SOURCES_DIST = BlockFile.cpp BlockFile.h DirManager.cpp \ PitchName.cpp PitchName.h PlatformCompatibility.cpp \ PlatformCompatibility.h PluginManager.cpp PluginManager.h \ Printing.cpp Printing.h Profiler.cpp Profiler.h Project.cpp \ - Project.h ProjectFSCK.cpp ProjectFSCK.h RealFFTf.cpp \ - RealFFTf.h RealFFTf48x.cpp RealFFTf48x.h RefreshCode.h \ - Resample.cpp Resample.h RevisionIdent.h RingBuffer.cpp \ - RingBuffer.h Screenshot.cpp Screenshot.h SelectedRegion.cpp \ + Project.h ProjectFileIORegistry.cpp ProjectFileIORegistry.h \ + ProjectFSCK.cpp ProjectFSCK.h RealFFTf.cpp RealFFTf.h \ + RealFFTf48x.cpp RealFFTf48x.h RefreshCode.h Resample.cpp \ + Resample.h RevisionIdent.h RingBuffer.cpp RingBuffer.h \ + Screenshot.cpp Screenshot.h SelectedRegion.cpp \ SelectedRegion.h SelectionState.cpp SelectionState.h \ Shuttle.cpp Shuttle.h ShuttleGetDefinition.cpp \ ShuttleGetDefinition.h ShuttleGui.cpp ShuttleGui.h \ @@ -662,6 +663,7 @@ am_audacity_OBJECTS = $(am__objects_1) audacity-AboutDialog.$(OBJEXT) \ audacity-PlatformCompatibility.$(OBJEXT) \ audacity-PluginManager.$(OBJEXT) audacity-Printing.$(OBJEXT) \ audacity-Profiler.$(OBJEXT) audacity-Project.$(OBJEXT) \ + audacity-ProjectFileIORegistry.$(OBJEXT) \ audacity-ProjectFSCK.$(OBJEXT) audacity-RealFFTf.$(OBJEXT) \ audacity-RealFFTf48x.$(OBJEXT) audacity-Resample.$(OBJEXT) \ audacity-RingBuffer.$(OBJEXT) audacity-Screenshot.$(OBJEXT) \ @@ -1378,10 +1380,11 @@ audacity_SOURCES = $(libaudacity_la_SOURCES) AboutDialog.cpp \ PitchName.cpp PitchName.h PlatformCompatibility.cpp \ PlatformCompatibility.h PluginManager.cpp PluginManager.h \ Printing.cpp Printing.h Profiler.cpp Profiler.h Project.cpp \ - Project.h ProjectFSCK.cpp ProjectFSCK.h RealFFTf.cpp \ - RealFFTf.h RealFFTf48x.cpp RealFFTf48x.h RefreshCode.h \ - Resample.cpp Resample.h RevisionIdent.h RingBuffer.cpp \ - RingBuffer.h Screenshot.cpp Screenshot.h SelectedRegion.cpp \ + Project.h ProjectFileIORegistry.cpp ProjectFileIORegistry.h \ + ProjectFSCK.cpp ProjectFSCK.h RealFFTf.cpp RealFFTf.h \ + RealFFTf48x.cpp RealFFTf48x.h RefreshCode.h Resample.cpp \ + Resample.h RevisionIdent.h RingBuffer.cpp RingBuffer.h \ + Screenshot.cpp Screenshot.h SelectedRegion.cpp \ SelectedRegion.h SelectionState.cpp SelectionState.h \ Shuttle.cpp Shuttle.h ShuttleGetDefinition.cpp \ ShuttleGetDefinition.h ShuttleGui.cpp ShuttleGui.h \ @@ -2553,6 +2556,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/audacity-Profiler.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/audacity-Project.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/audacity-ProjectFSCK.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/audacity-ProjectFileIORegistry.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/audacity-RealFFTf.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/audacity-RealFFTf48x.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/audacity-Resample.Po@am__quote@ @@ -3954,6 +3958,20 @@ audacity-Project.obj: Project.cpp @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(audacity_CPPFLAGS) $(CPPFLAGS) $(audacity_CXXFLAGS) $(CXXFLAGS) -c -o audacity-Project.obj `if test -f 'Project.cpp'; then $(CYGPATH_W) 'Project.cpp'; else $(CYGPATH_W) '$(srcdir)/Project.cpp'; fi` +audacity-ProjectFileIORegistry.o: ProjectFileIORegistry.cpp +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(audacity_CPPFLAGS) $(CPPFLAGS) $(audacity_CXXFLAGS) $(CXXFLAGS) -MT audacity-ProjectFileIORegistry.o -MD -MP -MF $(DEPDIR)/audacity-ProjectFileIORegistry.Tpo -c -o audacity-ProjectFileIORegistry.o `test -f 'ProjectFileIORegistry.cpp' || echo '$(srcdir)/'`ProjectFileIORegistry.cpp +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/audacity-ProjectFileIORegistry.Tpo $(DEPDIR)/audacity-ProjectFileIORegistry.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='ProjectFileIORegistry.cpp' object='audacity-ProjectFileIORegistry.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(audacity_CPPFLAGS) $(CPPFLAGS) $(audacity_CXXFLAGS) $(CXXFLAGS) -c -o audacity-ProjectFileIORegistry.o `test -f 'ProjectFileIORegistry.cpp' || echo '$(srcdir)/'`ProjectFileIORegistry.cpp + +audacity-ProjectFileIORegistry.obj: ProjectFileIORegistry.cpp +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(audacity_CPPFLAGS) $(CPPFLAGS) $(audacity_CXXFLAGS) $(CXXFLAGS) -MT audacity-ProjectFileIORegistry.obj -MD -MP -MF $(DEPDIR)/audacity-ProjectFileIORegistry.Tpo -c -o audacity-ProjectFileIORegistry.obj `if test -f 'ProjectFileIORegistry.cpp'; then $(CYGPATH_W) 'ProjectFileIORegistry.cpp'; else $(CYGPATH_W) '$(srcdir)/ProjectFileIORegistry.cpp'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/audacity-ProjectFileIORegistry.Tpo $(DEPDIR)/audacity-ProjectFileIORegistry.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='ProjectFileIORegistry.cpp' object='audacity-ProjectFileIORegistry.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(audacity_CPPFLAGS) $(CPPFLAGS) $(audacity_CXXFLAGS) $(CXXFLAGS) -c -o audacity-ProjectFileIORegistry.obj `if test -f 'ProjectFileIORegistry.cpp'; then $(CYGPATH_W) 'ProjectFileIORegistry.cpp'; else $(CYGPATH_W) '$(srcdir)/ProjectFileIORegistry.cpp'; fi` + audacity-ProjectFSCK.o: ProjectFSCK.cpp @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(audacity_CPPFLAGS) $(CPPFLAGS) $(audacity_CXXFLAGS) $(CXXFLAGS) -MT audacity-ProjectFSCK.o -MD -MP -MF $(DEPDIR)/audacity-ProjectFSCK.Tpo -c -o audacity-ProjectFSCK.o `test -f 'ProjectFSCK.cpp' || echo '$(srcdir)/'`ProjectFSCK.cpp @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/audacity-ProjectFSCK.Tpo $(DEPDIR)/audacity-ProjectFSCK.Po diff --git a/src/Menus.cpp b/src/Menus.cpp index bc2aa45ad..4076cc717 100644 --- a/src/Menus.cpp +++ b/src/Menus.cpp @@ -51,14 +51,6 @@ #include -PrefsListener::~PrefsListener() -{ -} - -void PrefsListener::UpdatePrefs() -{ -} - MenuManager &GetMenuManager(AudacityProject &project) { return *project.mMenuManager; } @@ -70,6 +62,11 @@ MenuCreator::~MenuCreator() { } +MenuManager::MenuManager() +{ + UpdatePrefs(); +} + void MenuManager::UpdatePrefs() { bool bSelectAllIfNone; @@ -388,6 +385,7 @@ CommandFlag MenuManager::GetFocusedFrame(AudacityProject &project) return AlwaysEnabledFlag; } + CommandFlag MenuManager::GetUpdateFlags (AudacityProject &project, bool checkActive) { @@ -548,7 +546,7 @@ CommandFlag MenuManager::GetUpdateFlags if (!EffectManager::Get().RealtimeIsActive()) flags |= IsRealtimeNotActiveFlag; - if (!project.IsCapturing()) + if ( !( gAudioIO->IsBusy() && gAudioIO->GetNumCaptureChannels() > 0 ) ) flags |= CaptureNotBusyFlag; ControlToolBar *bar = project.GetControlToolBar(); @@ -781,7 +779,7 @@ bool MenuManager::TryToMakeActionAllowed auto MissingFlags = (~flags & flagsRqd) & mask; if( mStopIfWasPaused && (MissingFlags & AudioIONotBusyFlag ) ){ - project.StopIfPaused(); + TransportActions::StopIfPaused( project ); // Hope this will now reflect stopped audio. flags = GetMenuManager(project).GetUpdateFlags(project); bAllowed = ((flags & mask) == (flagsRqd & mask)); diff --git a/src/Menus.h b/src/Menus.h index f67ab5930..1d39a250c 100644 --- a/src/Menus.h +++ b/src/Menus.h @@ -13,6 +13,7 @@ #include "audacity/Types.h" #include // member variable +#include "Prefs.h" class wxArrayString; class AudacityProject; @@ -32,13 +33,6 @@ enum EffectType : int; typedef wxString PluginID; typedef wxArrayString PluginIDs; -class PrefsListener -{ -public: - virtual ~PrefsListener(); - virtual void UpdatePrefs(); // default is no-op -}; - class MenuCreator { public: @@ -56,9 +50,11 @@ public: PluginID mLastEffect{}; }; -class MenuManager : public MenuCreator +class MenuManager final : public MenuCreator, private PrefsListener { public: + MenuManager(); + static void ModifyUndoMenuItems(AudacityProject &project); static void ModifyToolbarMenus(AudacityProject &project); // Calls ModifyToolbarMenus() on all projects @@ -72,7 +68,7 @@ public: // inactive project as it is needlessly expensive. CommandFlag GetUpdateFlags( AudacityProject &project, bool checkActive = false); - void UpdatePrefs(); + void UpdatePrefs() override; // Command Handling bool ReportIfActionNotAllowed( @@ -115,6 +111,8 @@ void DoUndo( AudacityProject &project ); /// Namespace for functions for Select menu namespace SelectActions { +void SelectAllIfNone( AudacityProject &project ); +void SelectNone( AudacityProject &project ); void DoListSelection( AudacityProject &project, Track *t, bool shift, bool ctrl, bool modifyState ); @@ -132,6 +130,7 @@ void DoZoomFitV( AudacityProject &project ); /// Namespace for functions for Transport menu namespace TransportActions { +void StopIfPaused( AudacityProject &project ); bool DoPlayStopSelect( AudacityProject &project, bool click, bool shift ); void DoPlayStopSelect( AudacityProject &project ); void DoStop( AudacityProject & ); diff --git a/src/MissingAliasFileDialog.cpp b/src/MissingAliasFileDialog.cpp index d0f00ddb9..ea9924f60 100644 --- a/src/MissingAliasFileDialog.cpp +++ b/src/MissingAliasFileDialog.cpp @@ -14,11 +14,16 @@ #include "Project.h" #include "widgets/ErrorDialog.h" +namespace { + using wxDialogRef = wxWeakRef< wxDialog >; + std::vector< wxDialogRef > sDialogs; +} + // special case for alias missing dialog because we keep track of if it exists. class MissingAliasFileDialog final : public ErrorDialog { public: - MissingAliasFileDialog(AudacityProject *parent, + MissingAliasFileDialog(wxWindow *parent, const wxString & dlogTitle, const wxString & message, const wxString & helpURL, @@ -27,20 +32,23 @@ class MissingAliasFileDialog final : public ErrorDialog }; -MissingAliasFileDialog::MissingAliasFileDialog(AudacityProject *parent, +MissingAliasFileDialog::MissingAliasFileDialog(wxWindow *parent, const wxString & dlogTitle, const wxString & message, const wxString & helpURL, const bool Close, const bool modal): ErrorDialog(parent, dlogTitle, message, helpURL, Close, modal) { - parent->SetMissingAliasFileDialog(this); + sDialogs.push_back( this ); } MissingAliasFileDialog::~MissingAliasFileDialog() { - static_cast(GetParent()) - ->SetMissingAliasFileDialog( nullptr ); + auto begin = sDialogs.begin(), end = sDialogs.end(), + newEnd = std::remove_if( begin, end, + [&]( const wxDialogRef &ref ){ + return ref == this; } ); + sDialogs.erase( newEnd, end ); } namespace MissingAliasFilesDialog { @@ -82,6 +90,17 @@ namespace MissingAliasFilesDialog { // but in practice Destroy() in OnOK does that } + wxDialog *Find( const AudacityProject &project ) + { + auto begin = sDialogs.begin(), end = sDialogs.end(), + iter = std::find_if( begin, end, + [&]( const wxDialogRef &ref ){ + return ref && ref->GetParent() == &project; } ); + if (iter != end) + return *iter; + return nullptr; + } + void Mark(const AliasBlockFile *b) { Lock lock{ m_LastMissingBlockFileLock }; diff --git a/src/MissingAliasFileDialog.h b/src/MissingAliasFileDialog.h index 8c2d15e8b..d02a14180 100644 --- a/src/MissingAliasFileDialog.h +++ b/src/MissingAliasFileDialog.h @@ -37,6 +37,8 @@ void Show(AudacityProject *parent, const wxString &helpPage, const bool Close = true); +wxDialog *Find( const AudacityProject &project ); + } #endif diff --git a/src/MixerBoard.cpp b/src/MixerBoard.cpp index 383c32fd2..e16b0382b 100644 --- a/src/MixerBoard.cpp +++ b/src/MixerBoard.cpp @@ -54,6 +54,7 @@ #endif #include "commands/CommandManager.h" +#include "toolbars/ControlToolBar.h" // class MixerTrackSlider @@ -355,8 +356,6 @@ void MixerTrackCluster::UpdatePrefs() { this->SetBackgroundColour( theTheme.Colour( clrMedium ) ); mStaticText_TrackName->SetForegroundColour(theTheme.Colour(clrTrackPanelText)); - if (mMeter) - mMeter->UpdatePrefs(); // in case meter range has changed HandleResize(); // in case prefs "/GUI/Solo" changed } #endif @@ -828,7 +827,7 @@ void MixerBoardScrolledWindow::OnMouseEvent(wxMouseEvent& event) //v Even when I implement MixerBoard::OnMouseEvent and call event.Skip() // here, MixerBoard::OnMouseEvent never gets called. // So, added mProject to MixerBoardScrolledWindow and just directly do what's needed here. - mProject->SelectNone(); + SelectActions::SelectNone( *mProject ); } else event.Skip(); @@ -1344,8 +1343,11 @@ void MixerBoard::OnTimer(wxCommandEvent &event) // audacityAudioCallback where it calls gAudioIO->mOutputMeter->UpdateDisplay(). if (mProject->IsAudioActive()) { - UpdateMeters(gAudioIO->GetStreamTime(), - (mProject->mLastPlayMode == PlayMode::loopedPlay)); + UpdateMeters( + gAudioIO->GetStreamTime(), + (mProject->GetControlToolBar()->GetLastPlayMode() + == PlayMode::loopedPlay) + ); } // Let other listeners get the notification diff --git a/src/MixerBoard.h b/src/MixerBoard.h index 3f10396b3..570dd5d62 100644 --- a/src/MixerBoard.h +++ b/src/MixerBoard.h @@ -20,6 +20,8 @@ #include "widgets/ASlider.h" // to inherit #include "commands/CommandManagerWindowClasses.h" +#include "Prefs.h" + class wxArrayString; class wxBitmapButton; class wxImage; @@ -188,7 +190,7 @@ public: class MixerBoardFrame; class TrackList; -class MixerBoard final : public wxWindow +class MixerBoard final : public wxWindow, private PrefsListener { friend class MixerBoardFrame; @@ -198,7 +200,7 @@ public: const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize); - void UpdatePrefs(); + void UpdatePrefs() override; // Add clusters for any tracks we're not yet showing. // Update pointers for tracks we're aleady showing. diff --git a/src/NoteTrack.cpp b/src/NoteTrack.cpp index 440c1606c..df7789f5f 100644 --- a/src/NoteTrack.cpp +++ b/src/NoteTrack.cpp @@ -33,6 +33,8 @@ #include "AColor.h" #include "DirManager.h" #include "Prefs.h" +#include "Project.h" +#include "ProjectFileIORegistry.h" #include "InconsistencyException.h" @@ -102,6 +104,15 @@ SONFNS(AutoSave) +static ProjectFileIORegistry::Entry registerFactory{ + wxT( "notetrack" ), + []( AudacityProject &project ){ + auto &trackFactory = *project.GetTrackFactory(); + auto &tracks = *project.GetTracks(); + return tracks.Add(trackFactory.NewNoteTrack()); + } +}; + NoteTrack::Holder TrackFactory::NewNoteTrack() { return std::make_shared(mDirManager); diff --git a/src/Prefs.cpp b/src/Prefs.cpp index 97268379a..3da5c2d27 100755 --- a/src/Prefs.cpp +++ b/src/Prefs.cpp @@ -69,6 +69,33 @@ std::unique_ptr ugPrefs {}; AudacityPrefs *gPrefs = NULL; int gMenusDirty = 0; +wxDEFINE_EVENT(EVT_PREFS_UPDATE, wxCommandEvent); + +PrefsListener::PrefsListener() +{ + wxTheApp->Bind(EVT_PREFS_UPDATE, &PrefsListener::OnEvent, this); +} + +PrefsListener::~PrefsListener() +{ + // Explicit unbinding is needed because this is not a wxEvtHandler + wxTheApp->Unbind(EVT_PREFS_UPDATE, &PrefsListener::OnEvent, this); +} + +void PrefsListener::UpdateSelectedPrefs( int ) +{ +} + +void PrefsListener::OnEvent( wxCommandEvent &evt ) +{ + evt.Skip(); + auto id = evt.GetId(); + if (id <= 0) + UpdatePrefs(); + else + UpdateSelectedPrefs( id ); +} + #if 0 // Copy one entry from one wxConfig object to another static void CopyEntry(wxString path, wxConfigBase *src, wxConfigBase *dst, wxString entry) diff --git a/src/Prefs.h b/src/Prefs.h index 7afa5ef27..0f1d40160 100644 --- a/src/Prefs.h +++ b/src/Prefs.h @@ -34,6 +34,7 @@ #include "../include/audacity/ComponentInterface.h" #include // to inherit wxFileConfig +#include // to declare custom event types void InitPreferences(); void FinishPreferences(); @@ -159,4 +160,29 @@ private: const wxString mOldKey; }; +// An event emitted by the application when the Preference dialog commits +// changes +wxDECLARE_EVENT(EVT_PREFS_UPDATE, wxCommandEvent); + +// Invoke UpdatePrefs() when Preference dialog commits changes. +class PrefsListener +{ +public: + PrefsListener(); + virtual ~PrefsListener(); + + // Called when all preferences should be updated. + virtual void UpdatePrefs() = 0; + +protected: + // Called when only selected preferences are to be updated. + // id is some value generated by wxNewId() that identifies the portion + // of preferences. + // Default function does nothing. + virtual void UpdateSelectedPrefs( int id ); + +private: + void OnEvent(wxCommandEvent&); +}; + #endif diff --git a/src/Project.cpp b/src/Project.cpp index 48cfddab4..280eeb0a2 100644 --- a/src/Project.cpp +++ b/src/Project.cpp @@ -51,6 +51,8 @@ scroll information. It also has some status flags. #include "Audacity.h" // for USE_* macros #include "Project.h" +#include "ProjectFileIORegistry.h" + #include "Experimental.h" #include @@ -583,6 +585,20 @@ private: #endif +XMLTagHandler * +AudacityProject::ImportHandlerFactory( AudacityProject &project ) { + auto &ptr = project.mImportXMLTagHandler; + if (!ptr) + ptr = + std::make_unique( &project ); + return ptr.get(); +} + +ProjectFileIORegistry::Entry +AudacityProject::sImportHandlerFactory{ + wxT("import"), ImportHandlerFactory +}; + bool ImportXMLTagHandler::HandleXMLTag(const wxChar *tag, const wxChar **attrs) { if (wxStrcmp(tag, wxT("import")) || attrs==NULL || (*attrs)==NULL || wxStrcmp(*attrs++, wxT("filename"))) @@ -1045,9 +1061,15 @@ enum { HSBarID, VSBarID, - TrackPanelID + + NextID, }; +int AudacityProject::NextWindowID() +{ + return mNextWindowID++; +} + // PRL: This event type definition used to be in AudacityApp.h, which created // a bad compilation dependency. The event was never emitted anywhere. I @@ -1076,8 +1098,6 @@ BEGIN_EVENT_TABLE(AudacityProject, wxFrame) EVT_COMMAND(wxID_ANY, EVT_OPEN_AUDIO_FILE, AudacityProject::OnOpenAudioFile) EVT_COMMAND(wxID_ANY, EVT_TOOLBAR_UPDATED, AudacityProject::OnToolBarUpdate) //mchinen:multithreaded calls - may not be threadsafe with CommandEvent: may have to change. - EVT_COMMAND(wxID_ANY, EVT_ODTASK_UPDATE, AudacityProject::OnODTaskUpdate) - EVT_COMMAND(wxID_ANY, EVT_ODTASK_COMPLETE, AudacityProject::OnODTaskComplete) END_EVENT_TABLE() AudacityProject::AudacityProject(wxWindow * parent, wxWindowID id, @@ -1100,6 +1120,10 @@ AudacityProject::AudacityProject(wxWindow * parent, wxWindowID id, mUndoManager(std::make_unique()) , mCommandManager( std::make_unique() ) { + auto &window = *this; + + mNextWindowID = NextID; + if (!gPrefs->Read(wxT("/SamplingRate/DefaultProjectSampleRate"), &mRate, AudioIO::GetOptimalSupportedSampleRate())) { // The default given above can vary with host/devices. So unless there is an entry for // the default sample rate in audacity.cfg, Audacity can open with a rate which is different @@ -1150,8 +1174,6 @@ AudacityProject::AudacityProject(wxWindow * parent, wxWindowID id, mMenuManager = std::make_unique(); - UpdatePrefs(); - mLockPlayRegion = false; // Make sure valgrind sees mIsSyncLocked is initialized, even @@ -1199,7 +1221,7 @@ AudacityProject::AudacityProject(wxWindow * parent, wxWindowID id, // // Create the horizontal ruler // - mRuler = safenew AdornedRulerPanel( this, mTopPanel, + mRuler = safenew AdornedRulerPanel( this, window.GetTopPanel(), wxID_ANY, wxDefaultPosition, wxSize( -1, AdornedRulerPanel::GetRulerHeight(false) ), @@ -1246,6 +1268,8 @@ AudacityProject::AudacityProject(wxWindow * parent, wxWindowID id, pPage->SetBackgroundColour(theTheme.Colour( clrMedium )); #endif + mMainPage = pPage; + { auto ubs = std::make_unique(wxVERTICAL); ubs->Add(mToolManager->GetTopDock(), 0, wxEXPAND | wxALIGN_TOP); @@ -1265,18 +1289,19 @@ AudacityProject::AudacityProject(wxWindow * parent, wxWindowID id, } bs->Layout(); - // The right hand side translates to NEW TrackPanel(...) in normal - // Audacity without additional DLLs. - mTrackPanel = TrackPanel::FactoryFunction(pPage, - TrackPanelID, + { + auto mainPage = window.GetMainPage(); + wxASSERT( mainPage ); // to justify safenew + mTrackPanel = safenew TrackPanel(mainPage, + window.NextWindowID(), wxDefaultPosition, wxDefaultSize, mTracks, &mViewInfo, this, mRuler); - mTrackPanel->UpdatePrefs(); - + } + mCursorOverlay = std::make_shared(this); mBackgroundCell = std::make_shared(this); @@ -1294,7 +1319,7 @@ AudacityProject::AudacityProject(wxWindow * parent, wxWindowID id, &ViewInfo::OnTimer, &mViewInfo); - // Add the overlays, in the sequence in which they will be painted + // Add the overlays mTrackPanel->AddOverlay( mIndicatorOverlay ); mTrackPanel->AddOverlay( mCursorOverlay ); #ifdef EXPERIMENTAL_SCRUBBING_BASIC @@ -1413,6 +1438,8 @@ AudacityProject::AudacityProject(wxWindow * parent, wxWindowID id, mTimer = std::make_unique(this, AudacityProjectTimerID); RestartTimer(); + UpdatePrefs(); + #if wxUSE_DRAG_AND_DROP // We can import now, so become a drag target // SetDropTarget(safenew AudacityDropTarget(this)); @@ -1422,10 +1449,6 @@ AudacityProject::AudacityProject(wxWindow * parent, wxWindowID id, mTrackPanel->SetDropTarget(safenew DropTarget(this)); #endif - wxTheApp->Bind(EVT_AUDIOIO_CAPTURE, - &AudacityProject::OnCapture, - this); - wxTheApp->Bind(EVT_THEME_CHANGE, &AudacityProject::OnThemeChange, this); #ifdef EXPERIMENTAL_DA2 @@ -1474,7 +1497,6 @@ void AudacityProject::UpdatePrefsVariables() { gPrefs->Read(wxT("/AudioFiles/ShowId3Dialog"), &mShowId3Dialog, true); gPrefs->Read(wxT("/AudioFiles/NormalizeOnLoad"),&mNormalizeOnLoad, false); - gPrefs->Read(wxT("/GUI/AutoScroll"), &mViewInfo.bUpdateTrackIndicator, true); gPrefs->Read(wxT("/GUI/EmptyCanBeDirty"), &mEmptyCanBeDirty, true ); gPrefs->Read(wxT("/GUI/ShowSplashScreen"), &mShowSplashScreen, true); gPrefs->Read(wxT("/GUI/Solo"), &mSoloPref, wxT("Simple")); @@ -1508,38 +1530,6 @@ void AudacityProject::UpdatePrefs() UpdatePrefsVariables(); SetProjectTitle(); - - { - ObjectFactorySetLocker locker; - for( const auto &pObject : mAttachedObjects ) - pObject->UpdatePrefs(); - } - - GetMenuManager(*this).UpdatePrefs(); - - if (mTrackPanel) { - mTrackPanel->UpdatePrefs(); - } - if (mMixerBoard) - mMixerBoard->UpdatePrefs(); - - if (mToolManager) { - mToolManager->UpdatePrefs(); - } - - if (mRuler) { - mRuler->UpdatePrefs(); - } -} - -void AudacityProject::SetMissingAliasFileDialog(wxDialog *dialog) -{ - mMissingAliasFilesWarningDialog = dialog; -} - -wxDialog *AudacityProject::GetMissingAliasFileDialog() -{ - return mMissingAliasFilesWarningDialog; } void AudacityProject::RedrawProject(const bool bForceWaveTracks /*= false*/) @@ -1559,16 +1549,6 @@ void AudacityProject::RefreshCursor() mTrackPanel->HandleCursorForPresentMouseState(); } -void AudacityProject::OnCapture(wxCommandEvent& evt) -{ - evt.Skip(); - - if (evt.GetInt() != 0) - mIsCapturing = true; - else - mIsCapturing = false; -} - void AudacityProject::OnThemeChange(wxCommandEvent& evt) { evt.Skip(); @@ -1582,7 +1562,6 @@ void AudacityProject::OnThemeChange(wxCommandEvent& evt) GetRulerPanel()->ReCreateButtons(); } - const std::shared_ptr &AudacityProject::GetDirManager() { return mDirManager; @@ -1614,7 +1593,12 @@ bool AudacityProject::IsAudioActive() const gAudioIO->IsStreamActive(GetAudioIOToken()); } -const Tags *AudacityProject::GetTags() +Tags *AudacityProject::GetTags() +{ + return mTags.get(); +} + +const Tags *AudacityProject::GetTags() const { return mTags.get(); } @@ -2371,22 +2355,6 @@ void AudacityProject::OnToolBarUpdate(wxCommandEvent & event) event.Skip(false); /* No need to propagate any further */ } -///Handles the redrawing necessary for tasks as they partially update in the background. -void AudacityProject::OnODTaskUpdate(wxCommandEvent & WXUNUSED(event)) -{ - //todo: add track data to the event - check to see if the project contains it before redrawing. - if(mTrackPanel) - mTrackPanel->Refresh(false); - -} - -//redraws the task and does other book keeping after the task is complete. -void AudacityProject::OnODTaskComplete(wxCommandEvent & WXUNUSED(event)) -{ - if(mTrackPanel) - mTrackPanel->Refresh(false); -} - void AudacityProject::OnScroll(wxScrollEvent & WXUNUSED(event)) { const wxInt64 offset = PixelWidthBeforeTime(0.0); @@ -2484,7 +2452,7 @@ void AudacityProject::OnActivate(wxActivateEvent & event) { // Activate events can fire during window teardown, so just // ignore them. - if (mIsDeleting) { + if (IsBeingDeleted()) { return; } @@ -2570,12 +2538,16 @@ public: // and/or attempts to DELETE objects twice. void AudacityProject::OnCloseWindow(wxCloseEvent & event) { + auto &project = *this; + auto &projectFileIO = project; + auto &window = project; + // We are called for the wxEVT_CLOSE_WINDOW, wxEVT_END_SESSION, and // wxEVT_QUERY_END_SESSION, so we have to protect against multiple // entries. This is a hack until the whole application termination // process can be reviewed and reworked. (See bug #964 for ways // to exercise the bug that instigated this hack.) - if (mIsBeingDeleted) + if (window.IsBeingDeleted()) { event.Skip(); return; @@ -2673,7 +2645,7 @@ void AudacityProject::OnCloseWindow(wxCloseEvent & event) // its size change. AllProjects::SaveWindowSize(); - mIsDeleting = true; + window.SetIsBeingDeleted(); // Mac: we never quit as the result of a close. // Other systems: we quit only when the close is the result of an external @@ -2696,17 +2668,7 @@ void AudacityProject::OnCloseWindow(wxCloseEvent & event) // TODO: Is there a Mac issue here?? // SetMenuBar(NULL); - // Lock all blocks in all tracks of the last saved version, so that - // the blockfiles aren't deleted on disk when we DELETE the blockfiles - // in memory. After it's locked, DELETE the data structure so that - // there's no memory leak. - if (mLastSavedTracks) { - for (auto wt : mLastSavedTracks->Any()) - wt->CloseLock(); - - mLastSavedTracks->Clear(); // sends an event - mLastSavedTracks.reset(); - } + projectFileIO.CloseLock(); // Get rid of the history window // LL: Destroy it before the TrackPanel and ToolBars since they @@ -2744,8 +2706,6 @@ void AudacityProject::OnCloseWindow(wxCloseEvent & event) mTags.reset(); - mImportXMLTagHandler.reset(); - // Delete all the tracks to free up memory and DirManager references. mTracks->Clear(); mTracks.reset(); @@ -2809,9 +2769,6 @@ void AudacityProject::OnCloseWindow(wxCloseEvent & event) // Destroys this pSelf.reset(); mRuler = nullptr; - - mIsBeingDeleted = true; - } void AudacityProject::OnOpenAudioFile(wxCommandEvent & event) @@ -3046,6 +3003,106 @@ AudacityProject *AudacityProject::OpenProject( return pProject; } +auto AudacityProject::ReadProjectFile( const FilePath &fileName ) + -> ReadProjectResults +{ + mFileName = fileName; + mbLoadedFromAup = true; + + mRecoveryAutoSaveDataDir = wxT(""); + mIsRecovered = false; + + SetProjectTitle(); + + const wxString autoSaveExt = wxT("autosave"); + if ( wxFileNameWrapper{ mFileName }.GetExt() == autoSaveExt ) + { + AutoSaveFile asf; + if (!asf.Decode(fileName)) + { + auto message = AutoSaveFile::FailureMessage( fileName ); + AudacityMessageBox( + message, + _("Error decoding file"), + wxOK | wxCENTRE, this); + // Important: Prevent deleting any temporary files! + DirManager::SetDontDeleteTempFiles(); + return { true }; + } + } + + /// + /// Parse project file + /// + + XMLFileReader xmlFile; + + // 'Lossless copy' projects have dependencies. We need to always copy-in + // these dependencies when converting to a normal project. + wxString oldAction = + gPrefs->Read(wxT("/FileFormats/CopyOrEditUncompressedData"), wxT("copy")); + bool oldAsk = + gPrefs->ReadBool(wxT("/Warnings/CopyOrEditUncompressedDataAsk"), true); + if (oldAction != wxT("copy")) + gPrefs->Write(wxT("/FileFormats/CopyOrEditUncompressedData"), wxT("copy")); + if (oldAsk) + gPrefs->Write(wxT("/Warnings/CopyOrEditUncompressedDataAsk"), (long) false); + gPrefs->Flush(); + + auto cleanup = finally( [&] { + // and restore old settings if necessary. + if (oldAction != wxT("copy")) + gPrefs->Write(wxT("/FileFormats/CopyOrEditUncompressedData"), oldAction); + if (oldAsk) + gPrefs->Write(wxT("/Warnings/CopyOrEditUncompressedDataAsk"), (long) true); + gPrefs->Flush(); + } ); + + bool bParseSuccess = xmlFile.Parse(this, fileName); + + bool err = false; + + if (bParseSuccess) { + // By making a duplicate set of pointers to the existing blocks + // on disk, we add one to their reference count, guaranteeing + // that their reference counts will never reach zero and thus + // the version saved on disk will be preserved until the + // user selects Save(). + + mLastSavedTracks = TrackList::Create(); + + for (auto t : GetTracks()->Any()) { + if (t->GetErrorOpening()) + { + wxLogWarning( + wxT("Track %s had error reading clip values from project file."), + t->GetName()); + err = true; + } + + err = ( !t->LinkConsistencyCheck() ) || err; + + mLastSavedTracks->Add(t->Duplicate()); + } + } + + return { false, bParseSuccess, err, xmlFile.GetErrorStr() }; +} + +XMLTagHandler * +AudacityProject::RecordingRecoveryFactory( AudacityProject &project ) { + auto &ptr = project.mRecordingRecoveryHandler; + if (!ptr) + ptr = + std::make_unique( &project ); + return ptr.get(); +} + +ProjectFileIORegistry::Entry +AudacityProject::sRecoveryFactory{ + wxT("recordingrecovery"), RecordingRecoveryFactory +}; + // FIXME:? TRAP_ERR This should return a result that is checked. // See comment in AudacityApp::MRUOpen(). void AudacityProject::OpenFile(const FilePath &fileNameArg, bool addtohistory) @@ -3160,85 +3217,23 @@ void AudacityProject::OpenFile(const FilePath &fileNameArg, bool addtohistory) return; } - /// - /// Parse project file - /// + // The handlers may be created during ReadProjectFile and are not needed + // after this function exits. + auto cleanupHandlers = finally( [this]{ + mImportXMLTagHandler.reset(); + mRecordingRecoveryHandler.reset(); + } ); - mFileName = fileName; - mbLoadedFromAup = true; + auto results = ReadProjectFile( fileName ); - mRecoveryAutoSaveDataDir = wxT(""); - mIsRecovered = false; + if ( results.decodeError ) + return; - SetProjectTitle(); - - const wxString autoSaveExt = wxT(".autosave"); - if (mFileName.length() >= autoSaveExt.length() && - mFileName.Right(autoSaveExt.length()) == autoSaveExt) - { - AutoSaveFile asf; - if (!asf.Decode(fileName)) - { - auto message = AutoSaveFile::FailureMessage( fileName ); - AudacityMessageBox( - message, - _("Error decoding file"), - wxOK | wxCENTRE, this); - // Important: Prevent deleting any temporary files! - DirManager::SetDontDeleteTempFiles(); - return; - } - } - - XMLFileReader xmlFile; - - // 'Lossless copy' projects have dependencies. We need to always copy-in - // these dependencies when converting to a normal project. - wxString oldAction = gPrefs->Read(wxT("/FileFormats/CopyOrEditUncompressedData"), wxT("copy")); - bool oldAsk = gPrefs->Read(wxT("/Warnings/CopyOrEditUncompressedDataAsk"), true)?true:false; - if (oldAction != wxT("copy")) - gPrefs->Write(wxT("/FileFormats/CopyOrEditUncompressedData"), wxT("copy")); - if (oldAsk) - gPrefs->Write(wxT("/Warnings/CopyOrEditUncompressedDataAsk"), (long) false); - gPrefs->Flush(); - - bool bParseSuccess = xmlFile.Parse(this, fileName); - - // and restore old settings if necessary. - if (oldAction != wxT("copy")) - gPrefs->Write(wxT("/FileFormats/CopyOrEditUncompressedData"), oldAction); - if (oldAsk) - gPrefs->Write(wxT("/Warnings/CopyOrEditUncompressedDataAsk"), (long) true); - gPrefs->Flush(); - - // Clean up now unused recording recovery handler if any - mRecordingRecoveryHandler.reset(); - - bool err = false; + const bool bParseSuccess = results.parseSuccess; + const wxString &errorStr = results.errorString; + const bool err = results.trackError; if (bParseSuccess) { - // By making a duplicate set of pointers to the existing blocks - // on disk, we add one to their reference count, guaranteeing - // that their reference counts will never reach zero and thus - // the version saved on disk will be preserved until the - // user selects Save(). - - mLastSavedTracks = TrackList::Create(); - - for (auto t : GetTracks()->Any()) { - if (t->GetErrorOpening()) - { - wxLogWarning( - wxT("Track %s had error reading clip values from project file."), - t->GetName()); - err = true; - } - - err = ( !t->LinkConsistencyCheck() ) || err; - - mLastSavedTracks->Add(t->Duplicate()); - } - InitialState(); mTrackPanel->SetFocusedTrack(*GetTracks()->Any().begin()); HandleResize(); @@ -3259,9 +3254,6 @@ void AudacityProject::OpenFile(const FilePath &fileNameArg, bool addtohistory) ODManager::UnmarkLoadedODFlag(); if (! closed ) { - // Shouldn't need it any more. - mImportXMLTagHandler.reset(); - if ( bParseSuccess ) { // This is a no-fail: GetDirManager()->FillBlockfilesCache(); @@ -3363,9 +3355,8 @@ void AudacityProject::OpenFile(const FilePath &fileNameArg, bool addtohistory) mFileName = wxT(""); SetProjectTitle(); - wxLogError(wxT("Could not parse file \"%s\". \nError: %s"), fileName, xmlFile.GetErrorStr()); + wxLogError(wxT("Could not parse file \"%s\". \nError: %s"), fileName, errorStr); - wxString errorStr = xmlFile.GetErrorStr(); wxString url = wxT("FAQ:Errors_on_opening_or_recovering_an_Audacity_project"); // Certain errors have dedicated help. @@ -3627,8 +3618,6 @@ bool AudacityProject::HandleXMLTag(const wxChar *tag, const wxChar **attrs) NumericConverter::LookupFormat( NumericConverter::BANDWIDTH, value ) ); } // while - mViewInfo.UpdatePrefs(); - if (longVpos != 0) { // PRL: It seems this must happen after SetSnapTo mViewInfo.vpos = longVpos; @@ -3712,45 +3701,11 @@ bool AudacityProject::HandleXMLTag(const wxChar *tag, const wxChar **attrs) XMLTagHandler *AudacityProject::HandleXMLChild(const wxChar *tag) { - if (!wxStrcmp(tag, wxT("tags"))) { - return mTags.get(); - } + auto fn = ProjectFileIORegistry::Lookup( tag ); + if (fn) + return fn( *this ); - // Note that TrackList::Add includes assignment of unique in-session TrackId - // to a reloaded track, though no promise that it equals the id it originally - // had - - if (!wxStrcmp(tag, wxT("wavetrack"))) { - return mTracks->Add(mTrackFactory->NewWaveTrack()); - } - - #ifdef USE_MIDI - if (!wxStrcmp(tag, wxT("notetrack"))) { - return mTracks->Add(mTrackFactory->NewNoteTrack()); - } - #endif // USE_MIDI - - if (!wxStrcmp(tag, wxT("labeltrack"))) { - return mTracks->Add(mTrackFactory->NewLabelTrack()); - } - - if (!wxStrcmp(tag, wxT("timetrack"))) { - return mTracks->Add(mTrackFactory->NewTimeTrack()); - } - - if (!wxStrcmp(tag, wxT("recordingrecovery"))) { - if (!mRecordingRecoveryHandler) - mRecordingRecoveryHandler = std::make_unique(this); - return mRecordingRecoveryHandler.get(); - } - - if (!wxStrcmp(tag, wxT("import"))) { - if (!mImportXMLTagHandler) - mImportXMLTagHandler = std::make_unique(this); - return mImportXMLTagHandler.get(); - } - - return NULL; + return nullptr; } void AudacityProject::WriteXMLHeader(XMLWriter &xmlFile) const @@ -4275,9 +4230,10 @@ std::vector< std::shared_ptr< Track > > AudacityProject::AddImportedTracks(const FilePath &fileName, TrackHolders &&newTracks) { + auto &project = *this; std::vector< std::shared_ptr< Track > > results; - SelectNone(); + SelectActions::SelectNone( project ); bool initiallyEmpty = mTracks->empty(); double newRate = 0; @@ -4387,6 +4343,7 @@ void AudacityProject::ZoomAfterImport(Track *pTrack) // If pNewTrackList is passed in non-NULL, it gets filled with the pointers to NEW tracks. bool AudacityProject::Import(const FilePath &fileName, WaveTrackArray* pTrackArray /*= NULL*/) { + auto &project = *this; TrackHolders newTracks; wxString errorMessage; @@ -4441,9 +4398,9 @@ bool AudacityProject::Import(const FilePath &fileName, WaveTrackArray* pTrackArr int mode = gPrefs->Read(wxT("/AudioFiles/NormalizeOnLoad"), 0L); if (mode == 1) { //TODO: All we want is a SelectAll() - SelectNone(); - SelectAllIfNone(); - const CommandContext context( *this); + SelectActions::SelectNone( project ); + SelectActions::SelectAllIfNone( project ); + const CommandContext context( project ); PluginActions::DoEffect( EffectManager::Get().GetEffectByIdentifier(wxT("Normalize")), context, @@ -5347,14 +5304,6 @@ void AudacityProject::SetSyncLock(bool flag) } } -bool AudacityProject::ExportFromTimerRecording(wxFileName fnFile, int iFormat, int iSubFormat, int iFilterIndex) -{ - Exporter e; - - MissingAliasFilesDialog::SetShouldShow(true); - return e.ProcessFromTimerRecording(this, false, 0.0, mTracks->GetEndTime(), fnFile, iFormat, iSubFormat, iFilterIndex); -} - int AudacityProject::GetOpenProjectCount() { return gAudacityProjects.size(); } @@ -5368,32 +5317,35 @@ bool AudacityProject::IsProjectSaved() { // This is done to empty out the tracks, but without creating a new project. void AudacityProject::ResetProjectToEmpty() { - SelectActions::DoSelectAll(*this); - TrackActions::DoRemoveTracks(*this); + auto &project = *this; + auto &projectFileIO = project; + + SelectActions::DoSelectAll( project ); + TrackActions::DoRemoveTracks( project ); // A new DirManager. mDirManager = DirManager::Create(); mTrackFactory.reset(safenew TrackFactory{ mDirManager, &mViewInfo }); + projectFileIO.ResetProjectFileIO(); + + mDirty = false; + GetUndoManager()->ClearStates(); +} + +void AudacityProject::ResetProjectFileIO() +{ // mLastSavedTrack code copied from OnCloseWindow. // Lock all blocks in all tracks of the last saved version, so that // the blockfiles aren't deleted on disk when we DELETE the blockfiles // in memory. After it's locked, DELETE the data structure so that // there's no memory leak. - if (mLastSavedTracks) { - for (auto t : mLastSavedTracks->Any()) - t->CloseLock(); - - mLastSavedTracks->Clear(); // sends an event - mLastSavedTracks.reset(); - } + CloseLock(); //mLastSavedTracks = TrackList::Create(); mFileName = ""; mIsRecovered = false; mbLoadedFromAup = false; SetProjectTitle(); - mDirty = false; - GetUndoManager()->ClearStates(); } bool AudacityProject::SaveFromTimerRecording(wxFileName fnFile) { @@ -5561,10 +5513,8 @@ LyricsWindow* AudacityProject::GetLyricsWindow(bool create) MixerBoardFrame* AudacityProject::GetMixerBoardFrame(bool create) { - if (create && !mMixerBoardFrame) { + if (create && !mMixerBoardFrame) mMixerBoardFrame = safenew MixerBoardFrame{ this }; - mMixerBoard = mMixerBoardFrame->mMixerBoard; - } return mMixerBoardFrame; } @@ -5610,14 +5560,6 @@ ContrastDialog *AudacityProject::GetContrastDialog(bool create) return mContrastDialog.get(); } -void AudacityProject::SelectNone() -{ - for (auto t : GetTracks()->Any()) - t->SetSelected(false); - - mTrackPanel->Refresh(false); -} - void AudacityProject::ZoomInByFactor( double ZoomFactor ) { // LLL: Handling positioning differently when audio is @@ -5708,20 +5650,17 @@ void AudacityProject::ZoomOutByFactor( double ZoomFactor ) TP_ScrollWindow(newh); } -// Select the full time range, if no -// time range is selected. -void AudacityProject::SelectAllIfNone() +void AudacityProject::CloseLock() { - auto flags = GetMenuManager(*this).GetUpdateFlags(*this); - if(!(flags & TracksSelectedFlag) || - (mViewInfo.selectedRegion.isPoint())) - SelectActions::DoSelectAllAudio(*this); -} + // Lock all blocks in all tracks of the last saved version, so that + // the blockfiles aren't deleted on disk when we DELETE the blockfiles + // in memory. After it's locked, DELETE the data structure so that + // there's no memory leak. + if (mLastSavedTracks) { + for (auto wt : mLastSavedTracks->Any()) + wt->CloseLock(); -// Stop playing or recording, if paused. -void AudacityProject::StopIfPaused() -{ - auto flags = GetMenuManager(*this).GetUpdateFlags(*this); - if( flags & PausedFlag ) - TransportActions::DoStop(*this); + mLastSavedTracks->Clear(); + mLastSavedTracks.reset(); + } } diff --git a/src/Project.h b/src/Project.h index b7dad6ae2..eac8cd477 100644 --- a/src/Project.h +++ b/src/Project.h @@ -23,6 +23,7 @@ #include "Experimental.h" #include "Track.h" +#include "Prefs.h" #include "SelectionState.h" #include "ViewInfo.h" #include "commands/CommandManagerWindowClasses.h" @@ -56,6 +57,7 @@ class Importer; class ODLock; class Overlay; class RecordingRecoveryHandler; +namespace ProjectFileIORegistry{ struct Entry; } class TrackList; class Tags; @@ -115,12 +117,6 @@ using WaveTrackArray = std::vector < std::shared_ptr < WaveTrack > >; extern AProjectArray gAudacityProjects; -enum class PlayMode : int { - normalPlay, - oneSecondPlay, // Disables auto-scrolling - loopedPlay // Disables auto-scrolling -}; - enum StatusBarField { stateStatusBarField = 1, mainStatusBarField = 2, @@ -176,21 +172,23 @@ class WaveTrack; class MenuManager; -class PrefsListener; - class AUDACITY_DLL_API AudacityProject final : public wxFrame, public TrackPanelListener, public SelectionBarListener, public SpectralSelectionBarListener, public XMLTagHandler, - public AudioIOListener + public AudioIOListener, + private PrefsListener { public: AudacityProject(wxWindow * parent, wxWindowID id, const wxPoint & pos, const wxSize & size); virtual ~AudacityProject(); - using AttachedObject = PrefsListener; + // Next available ID for sub-windows + int NextWindowID(); + + using AttachedObject = wxObject; using AttachedObjectFactory = std::function< std::unique_ptr() >; @@ -233,7 +231,8 @@ class AUDACITY_DLL_API AudacityProject final : public wxFrame, const std::shared_ptr &GetDirManager(); TrackFactory *GetTrackFactory(); AdornedRulerPanel *GetRulerPanel(); - const Tags *GetTags(); + Tags *GetTags(); + const Tags *GetTags() const; void SetTags( const std::shared_ptr &tags ); int GetAudioIOToken() const; bool IsAudioActive() const; @@ -282,6 +281,15 @@ class AUDACITY_DLL_API AudacityProject final : public wxFrame, AudacityProject *pProject, const FilePath &fileNameArg, bool addtohistory = true); + struct ReadProjectResults + { + bool decodeError; + bool parseSuccess; + bool trackError; + wxString errorString; + }; + ReadProjectResults ReadProjectFile( const FilePath &fileName ); + void OpenFile(const FilePath &fileName, bool addtohistory = true); private: @@ -302,6 +310,8 @@ public: AddImportedTracks(const FilePath &fileName, TrackHolders &&newTracks); + void CloseLock(); + bool Save(); bool SaveAs(bool bWantSaveCopy = false, bool bLossless = false); bool SaveAs(const wxString & newFileName, bool bWantSaveCopy = false, bool addToHistory = true); @@ -316,6 +326,7 @@ public: bool GetDirty() { return mDirty; } void SetProjectTitle( int number =-1); + wxWindow *GetMainPage() { return mMainPage; } wxPanel *GetTopPanel() { return mTopPanel; } TrackPanel * GetTrackPanel() {return mTrackPanel;} const TrackPanel * GetTrackPanel() const {return mTrackPanel;} @@ -330,21 +341,12 @@ public: bool GetNormalizeOnLoad() { return mNormalizeOnLoad; } //lda void SetNormalizeOnLoad(bool flag) { mNormalizeOnLoad = flag; } //lda - /** \brief Sets the wxDialog that is being displayed - * Used by the custom dialog warning constructor and destructor - */ - void SetMissingAliasFileDialog(wxDialog *dialog); - - /** \brief returns a pointer to the wxDialog if it is displayed, NULL otherwise. - */ - wxDialog *GetMissingAliasFileDialog(); - // Timer Record Auto Save/Export Routines bool SaveFromTimerRecording(wxFileName fnFile); - bool ExportFromTimerRecording(wxFileName fnFile, int iFormat, int iSubFormat, int iFilterIndex); static int GetOpenProjectCount(); bool IsProjectSaved(); void ResetProjectToEmpty(); + void ResetProjectFileIO(); // Routine to estimate how many minutes of recording time are left on disk int GetEstimatedRecordingMinsLeftOnDisk(long lCaptureChannels = 0); @@ -378,8 +380,6 @@ public: void OnTimer(wxTimerEvent & event); void OnToolBarUpdate(wxCommandEvent & event); void OnOpenAudioFile(wxCommandEvent & event); - void OnODTaskUpdate(wxCommandEvent & event); - void OnODTaskComplete(wxCommandEvent & event); void HandleResize(); void UpdateLayout(); @@ -391,13 +391,10 @@ public: int GetProjectNumber(){ return mProjectNo;}; static int CountUnnamed(); static void RefreshAllTitles(bool bShowProjectNumbers ); - void UpdatePrefs(); + void UpdatePrefs() override; void UpdatePrefsVariables(); void RedrawProject(const bool bForceWaveTracks = false); void RefreshCursor(); - void SelectNone(); - void SelectAllIfNone(); - void StopIfPaused(); void Zoom(double level); void ZoomBy(double multiplier); void Rewind(bool shift); @@ -521,7 +518,6 @@ public: void WriteXMLHeader(XMLWriter &xmlFile) const; - PlayMode mLastPlayMode{ PlayMode::normalPlay }; ViewInfo mViewInfo; // Audio IO callback methods @@ -540,7 +536,6 @@ public: private: - void OnCapture(wxCommandEvent & evt); void OnThemeChange(wxCommandEvent & evt); void InitialState(); @@ -613,6 +608,7 @@ private: TrackPanel *mTrackPanel{}; SelectionState mSelectionState{}; std::unique_ptr mTrackFactory{}; + wxWindow * mMainPage; wxPanel * mMainPanel; wxScrollBar *mHsbar; wxScrollBar *mVsbar; @@ -621,6 +617,8 @@ public: wxScrollBar &GetVerticalScrollBar() { return *mVsbar; } private: + int mNextWindowID; + bool mAutoScrolling{ false }; bool mActive{ true }; bool mIconized; @@ -629,14 +627,10 @@ private: HistoryWindow *mHistoryWindow{}; LyricsWindow* mLyricsWindow{}; MixerBoardFrame* mMixerBoardFrame{}; - MixerBoard* mMixerBoard{}; Destroy_ptr mFreqWindow; Destroy_ptr mContrastDialog; - // dialog for missing alias warnings - wxDialog *mMissingAliasFilesWarningDialog{}; - bool mShownOnce{ false }; // Project owned meters @@ -659,13 +653,6 @@ private: void SetTimerRecordCancelled(){mTimerRecordCanceled=true;} void ResetTimerRecordCancelled(){mTimerRecordCanceled=false;} - //sort method used by OnSortName and OnSortTime - //currently only supported flags are kAudacitySortByName and kAudacitySortByName - //in the future we might have 0x01 as sort ascending and we can bit or it -#define kAudacitySortByTime (1 << 1) -#define kAudacitySortByName (1 << 2) - void SortTracks(int flags); - private: int mAudioIOToken{ -1 }; @@ -677,6 +664,8 @@ private: public: bool EmptyCanBeDirty() const { return mEmptyCanBeDirty; } + bool IsBeingDeleted() const { return mIsDeleting; } + void SetIsBeingDeleted() { mIsDeleting = true; } private: bool mIsSyncLocked; @@ -719,20 +708,6 @@ public: private: bool mbInitializingScrollbar{ false }; - // Flag that we're recoding. - bool mIsCapturing{ false }; - -public: - bool IsCapturing() const { return mIsCapturing; } - -private: - - // See explanation in OnCloseWindow - bool mIsBeingDeleted{ false }; - - // CommandManager needs to use private methods - friend class CommandManager; - // TrackPanelOverlay objects std::shared_ptr mIndicatorOverlay, mCursorOverlay; @@ -783,6 +758,12 @@ public: private: std::unique_ptr mPlaybackScroller; + // Declared in this class so that they can have access to private members + static XMLTagHandler *RecordingRecoveryFactory( AudacityProject &project ); + static ProjectFileIORegistry::Entry sRecoveryFactory; + static XMLTagHandler *ImportHandlerFactory( AudacityProject &project ); + static ProjectFileIORegistry::Entry sImportHandlerFactory; + public: PlaybackScroller &GetPlaybackScroller() { return *mPlaybackScroller; } std::shared_ptr GetBackgroundCell() const diff --git a/src/ProjectFileIORegistry.cpp b/src/ProjectFileIORegistry.cpp new file mode 100644 index 000000000..3a5ea517f --- /dev/null +++ b/src/ProjectFileIORegistry.cpp @@ -0,0 +1,44 @@ +/********************************************************************** + + Audacity: A Digital Audio Editor + + ProjectFileIORegistry.cpp + + Paul Licameli + +**********************************************************************/ + +#include "ProjectFileIORegistry.h" + +#include "audacity/Types.h" +#include +#include + + +namespace ProjectFileIORegistry { + +namespace { + using TagTable = std::unordered_map< wxString, TagHandlerFactory >; + static TagTable &sTagTable() + { + static TagTable theTable; + return theTable; + } +} + +Entry::Entry( + const wxString &tag, const TagHandlerFactory &factory ) +{ + sTagTable()[ tag ] = factory; +} + +TagHandlerFactory Lookup( const wxString &tag ) +{ + const auto &table = sTagTable(); + auto iter = table.find( tag ); + if ( iter == table.end() ) + return {}; + return iter->second; +} + +} diff --git a/src/ProjectFileIORegistry.h b/src/ProjectFileIORegistry.h new file mode 100644 index 000000000..f092ca704 --- /dev/null +++ b/src/ProjectFileIORegistry.h @@ -0,0 +1,35 @@ +/********************************************************************** + + Audacity: A Digital Audio Editor + + ProjectFileIORegistry.h + + Paul Licameli + +**********************************************************************/ + +#ifndef __AUDACITY_PROJECT_FILE_IO_REGISTRY__ +#define __AUDACITY_PROJECT_FILE_IO_REGISTRY__ + +#include + +class AudacityProject; +class XMLTagHandler; +class wxString; + +namespace ProjectFileIORegistry { + +// Type of functions returning objects that intepret a part of the saved XML +using TagHandlerFactory = + std::function< XMLTagHandler *( AudacityProject & ) >; + +// Typically statically constructed +struct Entry{ + Entry( const wxString &tag, const TagHandlerFactory &factory ); +}; + +TagHandlerFactory Lookup( const wxString &tag ); + +} + +#endif diff --git a/src/Tags.cpp b/src/Tags.cpp index 4fb7ad359..23a1edf38 100644 --- a/src/Tags.cpp +++ b/src/Tags.cpp @@ -45,6 +45,8 @@ #include "FileNames.h" #include "Prefs.h" +#include "Project.h" +#include "ProjectFileIORegistry.h" #include "ShuttleGui.h" #include "TranslatableStringArray.h" #include "widgets/Grid.h" @@ -224,6 +226,11 @@ static const wxChar *DefaultGenres[] = wxT("Synthpop") }; +static ProjectFileIORegistry::Entry registerFactory{ + wxT( "tags" ), + []( AudacityProject &project ){ return project.GetTags(); } +}; + Tags::Tags() { mEditTitle = true; diff --git a/src/TimeTrack.cpp b/src/TimeTrack.cpp index b7c8e59c3..523ead76a 100644 --- a/src/TimeTrack.cpp +++ b/src/TimeTrack.cpp @@ -27,6 +27,7 @@ #include "Envelope.h" #include "Prefs.h" #include "Project.h" +#include "ProjectFileIORegistry.h" #include "TrackArtist.h" #include "AllThemeResources.h" @@ -39,6 +40,15 @@ std::shared_ptr TrackFactory::NewTimeTrack() return std::make_shared(mDirManager, mZoomInfo); } +static ProjectFileIORegistry::Entry registerFactory{ + wxT( "timetrack" ), + []( AudacityProject &project ){ + auto &trackFactory = *project.GetTrackFactory(); + auto &tracks = *project.GetTracks(); + return tracks.Add(trackFactory.NewTimeTrack()); + } +}; + TimeTrack::TimeTrack(const std::shared_ptr &projDirManager, const ZoomInfo *zoomInfo): Track(projDirManager) , mZoomInfo(zoomInfo) diff --git a/src/TimerRecordDialog.cpp b/src/TimerRecordDialog.cpp index 0d1e3b4bc..ac5cfde61 100644 --- a/src/TimerRecordDialog.cpp +++ b/src/TimerRecordDialog.cpp @@ -45,6 +45,7 @@ #include "DirManager.h" #include "ShuttleGui.h" #include "Menus.h" +#include "MissingAliasFileDialog.h" #include "Project.h" #include "Prefs.h" #include "widgets/NumericTextCtrl.h" @@ -627,8 +628,12 @@ int TimerRecordDialog::ExecutePostRecordActions(bool bWasStopped) { // Do Automatic Export? if (m_bAutoExportEnabled) { - bExportOK = pProject->ExportFromTimerRecording(m_fnAutoExportFile, m_iAutoExportFormat, - m_iAutoExportSubFormat, m_iAutoExportFilterIndex); + Exporter e; + MissingAliasFilesDialog::SetShouldShow(true); + bExportOK = e.ProcessFromTimerRecording( + pProject, false, 0.0, pProject->GetTracks()->GetEndTime(), + m_fnAutoExportFile, m_iAutoExportFormat, + m_iAutoExportSubFormat, m_iAutoExportFilterIndex); } // Check if we need to override the post recording action diff --git a/src/TrackArtist.cpp b/src/TrackArtist.cpp index dc83a769b..057d0d849 100644 --- a/src/TrackArtist.cpp +++ b/src/TrackArtist.cpp @@ -77,6 +77,7 @@ audio tracks. #include "LabelTrack.h" #include "TimeTrack.h" #include "Prefs.h" +#include "prefs/GUIPrefs.h" #include "prefs/GUISettings.h" #include "prefs/SpectrogramSettings.h" #include "prefs/TracksPrefs.h" @@ -142,7 +143,6 @@ TrackArtist::TrackArtist( TrackPanel *parent_ ) mdBrange = ENV_DB_RANGE; mShowClipping = false; mSampleDisplay = 1;// Stem plots by default. - UpdatePrefs(); SetColours(0); vruler = std::make_unique(); @@ -3267,15 +3267,22 @@ void TrackArt::DrawTimeTrack(TrackPanelDrawingContext &context, track->GetDisplayLog(), dbRange, lower, upper, false ); } +void TrackArtist::UpdateSelectedPrefs( int id ) +{ + if( id == ShowClippingPrefsID()) + mShowClipping = gPrefs->Read(wxT("/GUI/ShowClipping"), mShowClipping); +} + void TrackArtist::UpdatePrefs() { mdBrange = gPrefs->Read(ENV_DB_KEY, mdBrange); - mShowClipping = gPrefs->Read(wxT("/GUI/ShowClipping"), mShowClipping); mSampleDisplay = TracksPrefs::SampleViewChoice(); mbShowTrackNameInTrack = gPrefs->ReadBool(wxT("/GUI/ShowTrackNameInWaveform"), false); + UpdateSelectedPrefs( ShowClippingPrefsID() ); + SetColours(0); } diff --git a/src/TrackArtist.h b/src/TrackArtist.h index 4cb5e3ef2..dd28f67c7 100644 --- a/src/TrackArtist.h +++ b/src/TrackArtist.h @@ -25,6 +25,7 @@ #include // member variable #include // member variables #include "audacity/Types.h" +#include "Prefs.h" class wxRect; @@ -157,7 +158,7 @@ namespace TrackArt { const wxRect & rect, int x0, int y0, int cy, bool top); } -class AUDACITY_DLL_API TrackArtist { +class AUDACITY_DLL_API TrackArtist final : private PrefsListener { public: TrackArtist( TrackPanel *parent_ ); @@ -174,7 +175,8 @@ public: void SetColours(int iColorIndex); - void UpdatePrefs(); + void UpdatePrefs() override; + void UpdateSelectedPrefs( int id ) override; void UpdateVRuler(const Track *t, const wxRect & rect); diff --git a/src/TrackPanel.cpp b/src/TrackPanel.cpp index 2e5c563fc..220c6fa39 100644 --- a/src/TrackPanel.cpp +++ b/src/TrackPanel.cpp @@ -88,6 +88,9 @@ is time to refresh some aspect of the screen. #include "NoteTrack.h" #endif +#include "ondemand/ODManager.h" +#include "ondemand/ODTask.h" + #include "toolbars/ControlToolBar.h" #include "toolbars/ToolsToolBar.h" @@ -180,6 +183,9 @@ BEGIN_EVENT_TABLE(TrackPanel, CellularPanel) EVT_PAINT(TrackPanel::OnPaint) EVT_TIMER(wxID_ANY, TrackPanel::OnTimer) + + EVT_COMMAND(wxID_ANY, EVT_ODTASK_UPDATE, TrackPanel::OnODTask) + EVT_COMMAND(wxID_ANY, EVT_ODTASK_COMPLETE, TrackPanel::OnODTask) END_EVENT_TABLE() /// Makes a cursor from an XPM, uses CursorId as a fallback. @@ -267,6 +273,8 @@ TrackPanel::TrackPanel(wxWindow * parent, wxWindowID id, wxTheApp->Bind(EVT_AUDIOIO_PLAYBACK, &TrackPanel::OnPlayback, this); + + UpdatePrefs(); } @@ -313,16 +321,8 @@ wxString TrackPanel::gSoloPref; void TrackPanel::UpdatePrefs() { - gPrefs->Read(wxT("/GUI/AutoScroll"), &mViewInfo->bUpdateTrackIndicator, - true); gPrefs->Read(wxT("/GUI/Solo"), &gSoloPref, wxT("Simple")); - mViewInfo->UpdatePrefs(); - - if (mTrackArtist) { - mTrackArtist->UpdatePrefs(); - } - // All vertical rulers must be recalculated since the minimum and maximum // frequences may have been changed. UpdateVRulers(); @@ -473,6 +473,14 @@ void TrackPanel::OnTimer(wxTimerEvent& ) mTimeCount = 0; } +///Handles the redrawing necessary for tasks as they partially update in the +///background, or finish. +void TrackPanel::OnODTask(wxCommandEvent & WXUNUSED(event)) +{ + //todo: add track data to the event - check to see if the project contains it before redrawing. + Refresh(false); +} + double TrackPanel::GetScreenEndTime() const { int width; @@ -2772,40 +2780,6 @@ void TrackInfo::UpdatePrefs( wxWindow *pParent ) } while (textWidth >= allowableWidth); } -static TrackPanel * TrackPanelFactory(wxWindow * parent, - wxWindowID id, - const wxPoint & pos, - const wxSize & size, - const std::shared_ptr &tracks, - ViewInfo * viewInfo, - TrackPanelListener * listener, - AdornedRulerPanel * ruler) -{ - wxASSERT(parent); // to justify safenew - return safenew TrackPanel( - parent, - id, - pos, - size, - tracks, - viewInfo, - listener, - ruler); -} - - -// Declare the static factory function. -// We defined it in the class. -TrackPanel *(*TrackPanel::FactoryFunction)( - wxWindow * parent, - wxWindowID id, - const wxPoint & pos, - const wxSize & size, - const std::shared_ptr &tracks, - ViewInfo * viewInfo, - TrackPanelListener * listener, - AdornedRulerPanel * ruler) = TrackPanelFactory; - TrackPanelNode::TrackPanelNode() { } diff --git a/src/TrackPanel.h b/src/TrackPanel.h index df927807d..4aad1cc11 100644 --- a/src/TrackPanel.h +++ b/src/TrackPanel.h @@ -20,6 +20,7 @@ #include // to inherit #include "HitTestResult.h" +#include "Prefs.h" #include "SelectedRegion.h" @@ -210,6 +211,8 @@ namespace TrackInfo wxWindow *pParent); #endif + // Non-member, namespace function relying on TrackPanel to invoke it + // when it handles preference update events void UpdatePrefs( wxWindow *pParent ); }; @@ -251,6 +254,7 @@ enum : int { class AUDACITY_DLL_API TrackPanel final : public CellularPanel , public NonKeystrokeInterceptingWindow + , private PrefsListener { public: TrackPanel(wxWindow * parent, @@ -264,7 +268,7 @@ class AUDACITY_DLL_API TrackPanel final virtual ~ TrackPanel(); - void UpdatePrefs(); + void UpdatePrefs() override; void ApplyUpdatedTheme(); void OnPaint(wxPaintEvent & event); @@ -280,6 +284,7 @@ class AUDACITY_DLL_API TrackPanel final void OnIdle(wxIdleEvent & event); void OnTimer(wxTimerEvent& event); + void OnODTask(wxCommandEvent &event); int GetLeftOffset() const { return GetLabelWidth() + 1;} @@ -370,16 +375,6 @@ public: ViewInfo * GetViewInfo(){ return mViewInfo;} TrackPanelListener * GetListener(){ return mListener;} AdornedRulerPanel * GetRuler(){ return mRuler;} -// JKC and here is a factory function which just does 'NEW' in standard Audacity. - // Precondition: parent != NULL - static TrackPanel *(*FactoryFunction)(wxWindow * parent, - wxWindowID id, - const wxPoint & pos, - const wxSize & size, - const std::shared_ptr &tracks, - ViewInfo * viewInfo, - TrackPanelListener * listener, - AdornedRulerPanel * ruler); protected: void DrawTracks(wxDC * dc); diff --git a/src/ViewInfo.cpp b/src/ViewInfo.cpp index 2af0132e2..51aca87c5 100644 --- a/src/ViewInfo.cpp +++ b/src/ViewInfo.cpp @@ -146,6 +146,14 @@ ViewInfo::ViewInfo(double start, double screenDuration, double pixelsPerSecond) UpdatePrefs(); } +void ViewInfo::UpdateSelectedPrefs( int id ) +{ + if (id == UpdateScrollPrefsID()) + gPrefs->Read(wxT("/GUI/AutoScroll"), &bUpdateTrackIndicator, + true); + ZoomInfo::UpdateSelectedPrefs( id ); +} + void ViewInfo::UpdatePrefs() { ZoomInfo::UpdatePrefs(); @@ -155,6 +163,8 @@ void ViewInfo::UpdatePrefs() #endif gPrefs->Read(wxT("/GUI/AdjustSelectionEdges"), &bAdjustSelectionEdges, true); + + UpdateSelectedPrefs( UpdateScrollPrefsID() ); } void ViewInfo::SetBeforeScreenWidth(wxInt64 beforeWidth, wxInt64 screenWidth, double lowerBoundTime) @@ -206,3 +216,9 @@ void ViewInfo::OnTimer(wxCommandEvent &event) // Propagate the message to other listeners bound to this this->ProcessEvent( event ); } + +int ViewInfo::UpdateScrollPrefsID() +{ + static int value = wxNewId(); + return value; +} diff --git a/src/ViewInfo.h b/src/ViewInfo.h index 0143cddca..1b208aa52 100644 --- a/src/ViewInfo.h +++ b/src/ViewInfo.h @@ -15,6 +15,7 @@ #include // inherit wxEvtHandler #include "SelectedRegion.h" #include "MemoryX.h" +#include "Prefs.h" class Track; @@ -31,6 +32,7 @@ class Track; class AUDACITY_DLL_API ZoomInfo /* not final */ // Note that ViewInfo inherits from ZoomInfo but there are no virtual functions. // That's okay if we pass always by reference and never copy, suffering "slicing." +: protected PrefsListener { public: ZoomInfo(double start, double pixelsPerSecond); @@ -40,7 +42,7 @@ public: ZoomInfo(const ZoomInfo&) PROHIBITED; ZoomInfo& operator= (const ZoomInfo&) PROHIBITED; - void UpdatePrefs(); + void UpdatePrefs() override; int vpos; // vertical scroll pos @@ -146,7 +148,9 @@ class AUDACITY_DLL_API ViewInfo final public: ViewInfo(double start, double screenDuration, double pixelsPerSecond); - void UpdatePrefs(); + static int UpdateScrollPrefsID(); + void UpdatePrefs() override; + void UpdateSelectedPrefs( int id ) override; double GetBeforeScreenWidth() const { diff --git a/src/WaveTrack.cpp b/src/WaveTrack.cpp index b4b5a8ed0..ac4cf1c6b 100644 --- a/src/WaveTrack.cpp +++ b/src/WaveTrack.cpp @@ -48,6 +48,7 @@ Track classes. #include "Spectrum.h" #include "Project.h" +#include "ProjectFileIORegistry.h" #include "AudioIO.h" #include "Prefs.h" @@ -69,6 +70,15 @@ Track classes. using std::max; +static ProjectFileIORegistry::Entry registerFactory{ + wxT( "wavetrack" ), + []( AudacityProject &project ){ + auto &trackFactory = *project.GetTrackFactory(); + auto &tracks = *project.GetTracks(); + return tracks.Add(trackFactory.NewWaveTrack()); + } +}; + WaveTrack::Holder TrackFactory::DuplicateWaveTrack(const WaveTrack &orig) { return std::static_pointer_cast( orig.Duplicate() ); diff --git a/src/effects/Effect.cpp b/src/effects/Effect.cpp index f13866a6d..7376c4af1 100644 --- a/src/effects/Effect.cpp +++ b/src/effects/Effect.cpp @@ -3529,9 +3529,10 @@ void EffectUIHost::OnPlay(wxCommandEvent & WXUNUSED(evt)) mPlayPos = mRegion.t1(); } - mProject->GetControlToolBar()->PlayPlayRegion - (SelectedRegion(mPlayPos, mRegion.t1()), - mProject->GetDefaultPlayOptions(), PlayMode::normalPlay); + mProject->GetControlToolBar()->PlayPlayRegion( + SelectedRegion(mPlayPos, mRegion.t1()), + mProject->GetDefaultPlayOptions(), + PlayMode::normalPlay ); } } diff --git a/src/menus/FileMenus.cpp b/src/menus/FileMenus.cpp index 5ae0ffcd5..57fcf7c0e 100644 --- a/src/menus/FileMenus.cpp +++ b/src/menus/FileMenus.cpp @@ -5,6 +5,7 @@ #include "../FileNames.h" #include "../LabelTrack.h" #include "../MissingAliasFileDialog.h" +#include "../Menus.h" #include "../NoteTrack.h" #include "../Prefs.h" #include "../Printing.h" @@ -121,7 +122,7 @@ AudacityProject *DoImportMIDI( if (::ImportMIDI(fileName, newTrack.get())) { - pProject->SelectNone(); + SelectActions::SelectNone( *pProject ); auto pTrack = tracks->Add( newTrack ); pTrack->SetSelected(true); @@ -467,7 +468,7 @@ void OnImportLabels(const CommandContext &context) newTrack->Import(f); - project.SelectNone(); + SelectActions::SelectNone( project ); newTrack->SetSelected(true); tracks->Add( newTrack ); diff --git a/src/menus/PluginMenus.cpp b/src/menus/PluginMenus.cpp index 29df0f45f..b2df9d829 100644 --- a/src/menus/PluginMenus.cpp +++ b/src/menus/PluginMenus.cpp @@ -412,7 +412,7 @@ bool DoEffect( if (flags & kConfigured) { TransportActions::DoStop(project); - project.SelectAllIfNone(); + SelectActions::SelectAllIfNone( project ); } MissingAliasFilesDialog::SetShouldShow(true); diff --git a/src/menus/SelectMenus.cpp b/src/menus/SelectMenus.cpp index b61806eb8..54e489a1b 100644 --- a/src/menus/SelectMenus.cpp +++ b/src/menus/SelectMenus.cpp @@ -484,6 +484,24 @@ namespace SelectActions { // exported helper functions +void SelectNone( AudacityProject &project ) +{ + for (auto t : project.GetTracks()->Any()) + t->SetSelected(false); + + project.GetTrackPanel()->Refresh(false); +} + +// Select the full time range, if no +// time range is selected. +void SelectAllIfNone( AudacityProject &project ) +{ + auto flags = GetMenuManager( project ).GetUpdateFlags( project ); + if(!(flags & TracksSelectedFlag) || + (project.GetViewInfo().selectedRegion.isPoint())) + DoSelectAllAudio( project ); +} + void DoListSelection (AudacityProject &project, Track *t, bool shift, bool ctrl, bool modifyState) { @@ -549,7 +567,7 @@ void OnSelectNone(const CommandContext &context) auto &selectedRegion = project.GetViewInfo().selectedRegion; selectedRegion.collapseToT0(); - project.SelectNone(); + SelectNone( project ); project.ModifyState(false); } diff --git a/src/menus/TrackMenus.cpp b/src/menus/TrackMenus.cpp index 18445ee44..dc1479ffc 100644 --- a/src/menus/TrackMenus.cpp +++ b/src/menus/TrackMenus.cpp @@ -440,6 +440,11 @@ long mixer_process(void *mixer, float **buffer, long n) #endif // EXPERIMENTAL_SCOREALIGN +enum{ + kAudacitySortByTime = (1 << 1), + kAudacitySortByName = (1 << 2), +}; + //sort based on flags. see Project.h for sort flags void DoSortTracks( AudacityProject &project, int flags ) { @@ -797,7 +802,7 @@ void OnNewWaveTrack(const CommandContext &context) auto rate = project.GetRate(); auto t = tracks->Add(trackFactory->NewWaveTrack(defaultFormat, rate)); - project.SelectNone(); + SelectActions::SelectNone( project ); t->SetSelected(true); @@ -816,7 +821,7 @@ void OnNewStereoTrack(const CommandContext &context) auto defaultFormat = project.GetDefaultFormat(); auto rate = project.GetRate(); - project.SelectNone(); + SelectActions::SelectNone( project ); auto left = tracks->Add(trackFactory->NewWaveTrack(defaultFormat, rate)); left->SetSelected(true); @@ -841,7 +846,7 @@ void OnNewLabelTrack(const CommandContext &context) auto t = tracks->Add(trackFactory->NewLabelTrack()); - project.SelectNone(); + SelectActions::SelectNone( project ); t->SetSelected(true); @@ -865,7 +870,7 @@ void OnNewTimeTrack(const CommandContext &context) auto t = tracks->AddToHead(trackFactory->NewTimeTrack()); - project.SelectNone(); + SelectActions::SelectNone( project ); t->SetSelected(true); diff --git a/src/menus/TransportMenus.cpp b/src/menus/TransportMenus.cpp index 5d5d7e3c4..9b5abe1d4 100644 --- a/src/menus/TransportMenus.cpp +++ b/src/menus/TransportMenus.cpp @@ -193,6 +193,14 @@ namespace TransportActions { // exported helper functions +// Stop playing or recording, if paused. +void StopIfPaused( AudacityProject &project ) +{ + auto flags = GetMenuManager( project ).GetUpdateFlags( project ); + if( flags & PausedFlag ) + DoStop( project ); +} + bool DoPlayStopSelect (AudacityProject &project, bool click, bool shift) { @@ -699,9 +707,9 @@ void OnPlayOneSecond(const CommandContext &context) auto options = project.GetDefaultPlayOptions(); double pos = trackPanel->GetMostRecentXPos(); - controlToolBar->PlayPlayRegion - (SelectedRegion(pos - 0.5, pos + 0.5), options, - PlayMode::oneSecondPlay); + controlToolBar->PlayPlayRegion( + SelectedRegion(pos - 0.5, pos + 0.5), options, + PlayMode::oneSecondPlay); } /// The idea for this function (and first implementation) @@ -751,8 +759,8 @@ void OnPlayToSelection(const CommandContext &context) auto controlToolBar = project.GetControlToolBar(); auto playOptions = project.GetDefaultPlayOptions(); - controlToolBar->PlayPlayRegion - (SelectedRegion(t0, t1), playOptions, PlayMode::oneSecondPlay); + controlToolBar->PlayPlayRegion( + SelectedRegion(t0, t1), playOptions, PlayMode::oneSecondPlay); } // The next 4 functions provide a limited version of the diff --git a/src/menus/ViewMenus.cpp b/src/menus/ViewMenus.cpp index 7545e63a0..f3eb8521d 100644 --- a/src/menus/ViewMenus.cpp +++ b/src/menus/ViewMenus.cpp @@ -10,6 +10,7 @@ #include "../TrackPanel.h" #include "../commands/CommandContext.h" #include "../commands/CommandManager.h" +#include "../prefs/GUIPrefs.h" #include "../prefs/TracksPrefs.h" #ifdef EXPERIMENTAL_EFFECTS_RACK @@ -367,7 +368,10 @@ void OnShowClipping(const CommandContext &context) gPrefs->Write(wxT("/GUI/ShowClipping"), checked); gPrefs->Flush(); commandManager->Check(wxT("ShowClipping"), checked); - trackPanel->UpdatePrefs(); + + wxTheApp->AddPendingEvent(wxCommandEvent{ + EVT_PREFS_UPDATE, ShowClippingPrefsID() }); + trackPanel->Refresh(false); } diff --git a/src/prefs/GUIPrefs.cpp b/src/prefs/GUIPrefs.cpp index 78bdd3436..b329a6fc8 100644 --- a/src/prefs/GUIPrefs.cpp +++ b/src/prefs/GUIPrefs.cpp @@ -269,6 +269,10 @@ bool GUIPrefs::Commit() gPrefs->Flush(); } + // Reads preference /GUI/Theme + theTheme.LoadPreferredTheme(); + ThemePrefs::ApplyUpdatedImages(); + return true; } @@ -366,6 +370,12 @@ wxString GUIPrefs::GetLang() return {}; } +int ShowClippingPrefsID() +{ + static int value = wxNewId(); + return value; +} + PrefsPanel::Factory GUIPrefsFactory = [](wxWindow *parent, wxWindowID winid) { diff --git a/src/prefs/GUIPrefs.h b/src/prefs/GUIPrefs.h index d6f913cb4..c97f25ada 100644 --- a/src/prefs/GUIPrefs.h +++ b/src/prefs/GUIPrefs.h @@ -67,4 +67,7 @@ class GUIPrefs final : public PrefsPanel /// A PrefsPanel::Factory that creates one GUIPrefs panel. extern PrefsPanel::Factory GUIPrefsFactory; + +int ShowClippingPrefsID(); + #endif diff --git a/src/prefs/PrefsDialog.cpp b/src/prefs/PrefsDialog.cpp index cb62197c4..4538fd5f9 100644 --- a/src/prefs/PrefsDialog.cpp +++ b/src/prefs/PrefsDialog.cpp @@ -68,7 +68,6 @@ #include "MidiIOPrefs.h" #endif -#include "../Theme.h" #include "../widgets/HelpSystem.h" #if wxUSE_ACCESSIBILITY @@ -813,10 +812,6 @@ void PrefsDialog::OnOK(wxCommandEvent & WXUNUSED(event)) } gPrefs->Flush(); - // Reads preference /GUI/Theme - theTheme.LoadPreferredTheme(); - ThemePrefs::ApplyUpdatedImages(); - SavePreferredPage(); #if USE_PORTMIXER @@ -844,12 +839,14 @@ void PrefsDialog::OnOK(wxCommandEvent & WXUNUSED(event)) } #endif + // PRL: Is the following concern still valid, now that prefs update is + // handled instead by delayed event processing? + // LL: wxMac can't handle recreating the menus when this dialog is still active, // so AudacityProject::UpdatePrefs() or any of the routines it calls must // not cause MenuCreator::RebuildMenuBar() to be executed. - for (size_t i = 0; i < gAudacityProjects.size(); i++) { - gAudacityProjects[i]->UpdatePrefs(); - } + + wxTheApp->AddPendingEvent(wxCommandEvent{ EVT_PREFS_UPDATE }); WaveformSettings::defaults().LoadPrefs(); SpectrogramSettings::defaults().LoadPrefs(); diff --git a/src/toolbars/ControlToolBar.cpp b/src/toolbars/ControlToolBar.cpp index 05d8a1666..c65239d29 100644 --- a/src/toolbars/ControlToolBar.cpp +++ b/src/toolbars/ControlToolBar.cpp @@ -127,6 +127,7 @@ ControlToolBar::~ControlToolBar() void ControlToolBar::Create(wxWindow * parent) { ToolBar::Create(parent); + UpdatePrefs(); } // This is a convenience function that allows for button creation in @@ -537,7 +538,6 @@ bool ControlToolBar::IsRecordDown() const int ControlToolBar::PlayPlayRegion(const SelectedRegion &selectedRegion, const AudioIOStartStreamOptions &options, PlayMode mode, - PlayAppearance appearance, /* = PlayOption::Straight */ bool backwards, /* = false */ bool playWhiteSpace /* = false */) // STRONG-GUARANTEE (for state of mCutPreviewTracks) @@ -563,7 +563,18 @@ int ControlToolBar::PlayPlayRegion(const SelectedRegion &selectedRegion, if (backwards) std::swap(t0, t1); - SetPlay(true, appearance); + { + PlayAppearance appearance; + switch( mode ) { + case PlayMode::cutPreviewPlay: + appearance = PlayAppearance::CutPreview; break; + case PlayMode::loopedPlay: + appearance = PlayAppearance::Looped; break; + default: + appearance = PlayAppearance::Straight; break; + } + SetPlay(true, appearance); + } bool success = false; auto cleanup = finally( [&] { @@ -577,7 +588,7 @@ int ControlToolBar::PlayPlayRegion(const SelectedRegion &selectedRegion, if (gAudioIO->IsBusy()) return -1; - const bool cutpreview = appearance == PlayAppearance::CutPreview; + const bool cutpreview = mode == PlayMode::cutPreviewPlay; if (cutpreview && t0==t1) return -1; /* msmeyer: makes no sense */ @@ -589,7 +600,7 @@ int ControlToolBar::PlayPlayRegion(const SelectedRegion &selectedRegion, if (!t) return -1; // Should never happen, but... - p->mLastPlayMode = mode; + mLastPlayMode = mode; bool hasaudio; if (useMidi) @@ -744,14 +755,13 @@ void ControlToolBar::PlayCurrentRegion(bool looped /* = false */, options.playLooped = looped; if (cutpreview) options.timeTrack = NULL; - ControlToolBar::PlayAppearance appearance = - cutpreview ? ControlToolBar::PlayAppearance::CutPreview - : looped ? ControlToolBar::PlayAppearance::Looped - : ControlToolBar::PlayAppearance::Straight; + auto mode = + cutpreview ? PlayMode::cutPreviewPlay + : options.playLooped ? PlayMode::loopedPlay + : PlayMode::normalPlay; PlayPlayRegion(SelectedRegion(playRegionStart, playRegionEnd), options, - (looped ? PlayMode::loopedPlay : PlayMode::normalPlay), - appearance); + mode); } } @@ -1305,7 +1315,7 @@ void ControlToolBar::OnRewind(wxCommandEvent & WXUNUSED(evt)) AudacityProject *p = GetActiveProject(); if (p) { - p->StopIfPaused(); + TransportActions::StopIfPaused( *p ); p->Rewind(mRewind->WasShiftDown()); } } @@ -1318,7 +1328,7 @@ void ControlToolBar::OnFF(wxCommandEvent & WXUNUSED(evt)) AudacityProject *p = GetActiveProject(); if (p) { - p->StopIfPaused(); + TransportActions::StopIfPaused( *p ); p->SkipEnd(mFF->WasShiftDown()); } } diff --git a/src/toolbars/ControlToolBar.h b/src/toolbars/ControlToolBar.h index a3f45da7d..77d06b878 100644 --- a/src/toolbars/ControlToolBar.h +++ b/src/toolbars/ControlToolBar.h @@ -33,8 +33,12 @@ class TimeTrack; struct AudioIOStartStreamOptions; class SelectedRegion; -// Defined in Project.h -enum class PlayMode : int; +enum class PlayMode : int { + normalPlay, + oneSecondPlay, // Disables auto-scrolling + loopedPlay, // Disables auto-scrolling + cutPreviewPlay +}; class WaveTrack; using WaveTrackArray = std::vector < std::shared_ptr < WaveTrack > >; @@ -100,7 +104,6 @@ class ControlToolBar final : public ToolBar { int PlayPlayRegion(const SelectedRegion &selectedRegion, const AudioIOStartStreamOptions &options, PlayMode playMode, - PlayAppearance appearance = PlayAppearance::Straight, bool backwards = false, // Allow t0 and t1 to be beyond end of tracks bool playWhiteSpace = false); @@ -133,6 +136,8 @@ class ControlToolBar final : public ToolBar { // Cancel the addition of temporary recording tracks into the project void CancelRecording(); + PlayMode GetLastPlayMode() const { return mLastPlayMode; } + private: static AButton *MakeButton( @@ -192,6 +197,8 @@ class ControlToolBar final : public ToolBar { wxString mStateRecord; wxString mStatePause; + PlayMode mLastPlayMode{ PlayMode::normalPlay }; + public: DECLARE_CLASS(ControlToolBar) diff --git a/src/toolbars/DeviceToolBar.cpp b/src/toolbars/DeviceToolBar.cpp index 4eaada867..1a41352b9 100644 --- a/src/toolbars/DeviceToolBar.cpp +++ b/src/toolbars/DeviceToolBar.cpp @@ -64,6 +64,12 @@ BEGIN_EVENT_TABLE(DeviceToolBar, ToolBar) EVT_COMMAND(wxID_ANY, EVT_CAPTURE_KEY, DeviceToolBar::OnCaptureKey) END_EVENT_TABLE() +static int DeviceToolbarPrefsID() +{ + static int value = wxNewId(); + return value; +} + //Standard contructor DeviceToolBar::DeviceToolBar() : ToolBar(DeviceBarID, _("Device"), wxT("Device"), true) @@ -313,6 +319,13 @@ void DeviceToolBar::UpdatePrefs() Refresh(); } +void DeviceToolBar::UpdateSelectedPrefs( int id ) +{ + if (id == DeviceToolbarPrefsID()) + UpdatePrefs(); + ToolBar::UpdateSelectedPrefs( id ); +} + void DeviceToolBar::EnableDisableButtons() { @@ -773,10 +786,8 @@ void DeviceToolBar::OnChoice(wxCommandEvent &event) gAudioIO->HandleDeviceChange(); } - // Update all projects' DeviceToolBar. - for (size_t i = 0; i < gAudacityProjects.size(); i++) { - gAudacityProjects[i]->GetDeviceToolBar()->UpdatePrefs(); - } + wxTheApp->AddPendingEvent(wxCommandEvent{ + EVT_PREFS_UPDATE, DeviceToolbarPrefsID() }); } void DeviceToolBar::ShowInputDialog() diff --git a/src/toolbars/DeviceToolBar.h b/src/toolbars/DeviceToolBar.h index 5c996a014..66b7f670b 100644 --- a/src/toolbars/DeviceToolBar.h +++ b/src/toolbars/DeviceToolBar.h @@ -29,6 +29,7 @@ class DeviceToolBar final : public ToolBar { void Create(wxWindow * parent) override; void UpdatePrefs() override; + void UpdateSelectedPrefs( int ) override; void DeinitChildren(); void Populate() override; diff --git a/src/toolbars/EditToolBar.cpp b/src/toolbars/EditToolBar.cpp index d167e7d75..206622d2c 100644 --- a/src/toolbars/EditToolBar.cpp +++ b/src/toolbars/EditToolBar.cpp @@ -89,6 +89,7 @@ EditToolBar::~EditToolBar() void EditToolBar::Create(wxWindow * parent) { ToolBar::Create(parent); + UpdatePrefs(); } void EditToolBar::AddSeparator() diff --git a/src/toolbars/MeterToolBar.cpp b/src/toolbars/MeterToolBar.cpp index b8c67c3cb..a1e656462 100644 --- a/src/toolbars/MeterToolBar.cpp +++ b/src/toolbars/MeterToolBar.cpp @@ -78,6 +78,8 @@ void MeterToolBar::Create(wxWindow * parent) { ToolBar::Create(parent); + UpdatePrefs(); + // Simulate a size event to set initial meter placement/size wxSizeEvent dummy; OnSize(dummy); @@ -156,18 +158,6 @@ void MeterToolBar::Populate() void MeterToolBar::UpdatePrefs() { - if( mPlayMeter ) - { - mPlayMeter->UpdatePrefs(); - mPlayMeter->Refresh(); - } - - if( mRecordMeter ) - { - mRecordMeter->UpdatePrefs(); - mRecordMeter->Refresh(); - } - RegenerateTooltips(); // Set label to pull in language change @@ -175,8 +165,6 @@ void MeterToolBar::UpdatePrefs() // Give base class a chance ToolBar::UpdatePrefs(); - - } void MeterToolBar::RegenerateTooltips() diff --git a/src/toolbars/MixerToolBar.cpp b/src/toolbars/MixerToolBar.cpp index 96d15ec90..48837f670 100644 --- a/src/toolbars/MixerToolBar.cpp +++ b/src/toolbars/MixerToolBar.cpp @@ -67,6 +67,7 @@ MixerToolBar::~MixerToolBar() void MixerToolBar::Create(wxWindow *parent) { ToolBar::Create(parent); + UpdatePrefs(); } void MixerToolBar::Populate() diff --git a/src/toolbars/ScrubbingToolBar.cpp b/src/toolbars/ScrubbingToolBar.cpp index a00a63591..f0e36d88f 100644 --- a/src/toolbars/ScrubbingToolBar.cpp +++ b/src/toolbars/ScrubbingToolBar.cpp @@ -68,6 +68,7 @@ ScrubbingToolBar::~ScrubbingToolBar() void ScrubbingToolBar::Create(wxWindow * parent) { ToolBar::Create(parent); + UpdatePrefs(); } /// This is a convenience function that allows for button creation in diff --git a/src/toolbars/SelectionBar.cpp b/src/toolbars/SelectionBar.cpp index 90a5b6888..2e694620b 100644 --- a/src/toolbars/SelectionBar.cpp +++ b/src/toolbars/SelectionBar.cpp @@ -132,6 +132,7 @@ SelectionBar::~SelectionBar() void SelectionBar::Create(wxWindow * parent) { ToolBar::Create(parent); + UpdatePrefs(); } @@ -504,7 +505,8 @@ void SelectionBar::OnUpdate(wxCommandEvent &evt) // Save format name before recreating the controls so they resize properly { auto format = mStartTime->GetBuiltinName(index); - mListener->AS_SetSelectionFormat(format); + if (mListener) + mListener->AS_SetSelectionFormat(format); } RegenerateTooltips(); diff --git a/src/toolbars/SpectralSelectionBar.cpp b/src/toolbars/SpectralSelectionBar.cpp index 7059c6a4a..1201b178c 100644 --- a/src/toolbars/SpectralSelectionBar.cpp +++ b/src/toolbars/SpectralSelectionBar.cpp @@ -107,6 +107,7 @@ SpectralSelectionBar::~SpectralSelectionBar() void SpectralSelectionBar::Create(wxWindow * parent) { ToolBar::Create(parent); + UpdatePrefs(); mHeight = wxWindowBase::GetSizer()->GetSize().GetHeight(); } @@ -370,12 +371,14 @@ void SpectralSelectionBar::OnUpdate(wxCommandEvent &evt) if (type == EVT_FREQUENCYTEXTCTRL_UPDATED) { NumericTextCtrl *frequencyCtrl = (mbCenterAndWidth ? mCenterCtrl : mLowCtrl); auto frequencyFormatName = frequencyCtrl->GetBuiltinName(index); - mListener->SSBL_SetFrequencySelectionFormatName(frequencyFormatName); + if (mListener) + mListener->SSBL_SetFrequencySelectionFormatName(frequencyFormatName); } else if (mbCenterAndWidth && type == EVT_BANDWIDTHTEXTCTRL_UPDATED) { auto bandwidthFormatName = mWidthCtrl->GetBuiltinName(index); - mListener->SSBL_SetBandwidthSelectionFormatName(bandwidthFormatName); + if (mListener) + mListener->SSBL_SetBandwidthSelectionFormatName(bandwidthFormatName); } // ToolBar::ReCreateButtons() will get rid of our sizers and controls diff --git a/src/toolbars/ToolBar.h b/src/toolbars/ToolBar.h index f8b7999e5..beb6f2ae9 100644 --- a/src/toolbars/ToolBar.h +++ b/src/toolbars/ToolBar.h @@ -18,6 +18,7 @@ #include #include +#include "../Prefs.h" #include "../Theme.h" #include "../widgets/wxPanelWrapper.h" // to inherit @@ -84,7 +85,9 @@ enum // How may pixels padding each side of a floating toolbar enum { ToolBarFloatMargin = 1 }; -class ToolBar /* not final */ : public wxPanelWrapper +class ToolBar /* not final */ +: public wxPanelWrapper +, protected PrefsListener { public: @@ -101,7 +104,7 @@ class ToolBar /* not final */ : public wxPanelWrapper virtual void Create(wxWindow *parent); virtual void EnableDisableButtons() = 0; virtual void ReCreateButtons(); - virtual void UpdatePrefs(); + void UpdatePrefs() override; virtual void RegenerateTooltips() = 0; int GetType(); diff --git a/src/toolbars/ToolManager.cpp b/src/toolbars/ToolManager.cpp index a51137642..ba086ccaf 100644 --- a/src/toolbars/ToolManager.cpp +++ b/src/toolbars/ToolManager.cpp @@ -1091,21 +1091,6 @@ void ToolManager::LayoutToolBars() mBotDock->LayoutToolBars(); } -// -// Tell the toolbars that preferences have been updated -// -void ToolManager::UpdatePrefs() -{ - for( int ndx = 0; ndx < ToolBarCount; ndx++ ) - { - ToolBar *bar = mBars[ ndx ].get(); - if( bar ) - { - bar->UpdatePrefs(); - } - } -} - // // Handle toolbar dragging // diff --git a/src/toolbars/ToolManager.h b/src/toolbars/ToolManager.h index 84d70a52a..58bbfc0dd 100644 --- a/src/toolbars/ToolManager.h +++ b/src/toolbars/ToolManager.h @@ -48,7 +48,6 @@ class ToolManager final : public wxEvtHandler, public wxEventFilter ~ToolManager(); void LayoutToolBars(); - void UpdatePrefs(); bool IsDocked( int type ); diff --git a/src/toolbars/ToolsToolBar.cpp b/src/toolbars/ToolsToolBar.cpp index 0d53a5824..aaf27326c 100644 --- a/src/toolbars/ToolsToolBar.cpp +++ b/src/toolbars/ToolsToolBar.cpp @@ -258,3 +258,9 @@ void ToolsToolBar::OnTool(wxCommandEvent & evt) IsDown(multiTool)); gPrefs->Flush(); } + +void ToolsToolBar::Create(wxWindow * parent) +{ + ToolBar::Create(parent); + UpdatePrefs(); +} diff --git a/src/toolbars/ToolsToolBar.h b/src/toolbars/ToolsToolBar.h index a881d0d45..c6a91dac2 100644 --- a/src/toolbars/ToolsToolBar.h +++ b/src/toolbars/ToolsToolBar.h @@ -68,6 +68,7 @@ class ToolsToolBar final : public ToolBar { private: + void Create(wxWindow * parent) override; void RegenerateTooltips() override; wxImage *MakeToolImage(wxImage *tool, wxImage *mask, int style); static AButton *MakeTool( diff --git a/src/toolbars/TranscriptionToolBar.cpp b/src/toolbars/TranscriptionToolBar.cpp index 4eef25ee2..35741b583 100644 --- a/src/toolbars/TranscriptionToolBar.cpp +++ b/src/toolbars/TranscriptionToolBar.cpp @@ -483,18 +483,15 @@ void TranscriptionToolBar::PlayAtSpeed(bool looped, bool cutPreview) AudioIOStartStreamOptions options(p->GetDefaultPlayOptions()); options.playLooped = looped; // No need to set cutPreview options. - // Due to a rather hacky approach, the appearance is used - // to signal use of cutpreview to code below. options.timeTrack = mTimeTrack.get(); - ControlToolBar::PlayAppearance appearance = - cutPreview ? ControlToolBar::PlayAppearance::CutPreview - : looped ? ControlToolBar::PlayAppearance::Looped - : ControlToolBar::PlayAppearance::Straight; + auto mode = + cutPreview ? PlayMode::cutPreviewPlay + : options.playLooped ? PlayMode::loopedPlay + : PlayMode::normalPlay; p->GetControlToolBar()->PlayPlayRegion (SelectedRegion(playRegionStart, playRegionEnd), options, - PlayMode::normalPlay, - appearance); + mode); } else { diff --git a/src/toolbars/TranscriptionToolBar.h b/src/toolbars/TranscriptionToolBar.h index 0ccb97acd..b9f093b6c 100644 --- a/src/toolbars/TranscriptionToolBar.h +++ b/src/toolbars/TranscriptionToolBar.h @@ -98,7 +98,6 @@ class TranscriptionToolBar final : public ToolBar { //void Populate() override; //void Repaint(wxDC * WXUNUSED(dc)) override {} //void EnableDisableButtons() override; - //void UpdatePrefs() override; //void OnFocus(wxFocusEvent &event); //void OnCaptureKey(wxCommandEvent &event); diff --git a/src/tracks/ui/EditCursorOverlay.cpp b/src/tracks/ui/EditCursorOverlay.cpp index d6fc4b714..b40de12ac 100644 --- a/src/tracks/ui/EditCursorOverlay.cpp +++ b/src/tracks/ui/EditCursorOverlay.cpp @@ -36,6 +36,11 @@ EditCursorOverlay::EditCursorOverlay(AudacityProject *project, bool isMaster) { } +unsigned EditCursorOverlay::SequenceNumber() const +{ + return 20; +} + std::pair EditCursorOverlay::DoGetRectangle(wxSize size) { const auto &selection = mProject->GetViewInfo().selectedRegion; diff --git a/src/tracks/ui/EditCursorOverlay.h b/src/tracks/ui/EditCursorOverlay.h index 2a7115562..afa46c51d 100644 --- a/src/tracks/ui/EditCursorOverlay.h +++ b/src/tracks/ui/EditCursorOverlay.h @@ -22,6 +22,7 @@ public: EditCursorOverlay(AudacityProject *project, bool isMaster = true); private: + unsigned SequenceNumber() const override; std::pair DoGetRectangle(wxSize size) override; void Draw(OverlayPanel &panel, wxDC &dc) override; diff --git a/src/tracks/ui/PlayIndicatorOverlay.cpp b/src/tracks/ui/PlayIndicatorOverlay.cpp index 1b99d0c9f..f32589ac9 100644 --- a/src/tracks/ui/PlayIndicatorOverlay.cpp +++ b/src/tracks/ui/PlayIndicatorOverlay.cpp @@ -17,6 +17,7 @@ Paul Licameli split from TrackPanel.cpp #include "../../Project.h" #include "../../TrackPanel.h" #include "Scrubbing.h" +#include "../../toolbars/ControlToolBar.h" #include @@ -42,6 +43,11 @@ PlayIndicatorOverlayBase::~PlayIndicatorOverlayBase() { } +unsigned PlayIndicatorOverlayBase::SequenceNumber() const +{ + return 10; +} + std::pair PlayIndicatorOverlayBase::DoGetRectangle(wxSize size) { auto width = mIsMaster ? 1 : IndicatorMediumWidth; @@ -171,9 +177,10 @@ void PlayIndicatorOverlay::OnTimer(wxCommandEvent &event) playPos >= 0 && !onScreen ) { // msmeyer: But only if not playing looped or in one-second mode // PRL: and not scrolling with play/record head fixed + auto mode = mProject->GetControlToolBar()->GetLastPlayMode(); if (!pinned && - mProject->mLastPlayMode != PlayMode::loopedPlay && - mProject->mLastPlayMode != PlayMode::oneSecondPlay && + mode != PlayMode::loopedPlay && + mode != PlayMode::oneSecondPlay && !gAudioIO->IsPaused()) { auto newPos = playPos; diff --git a/src/tracks/ui/PlayIndicatorOverlay.h b/src/tracks/ui/PlayIndicatorOverlay.h index 729b9e662..8129f1b63 100644 --- a/src/tracks/ui/PlayIndicatorOverlay.h +++ b/src/tracks/ui/PlayIndicatorOverlay.h @@ -28,6 +28,7 @@ public: void Update(int newIndicatorX) { mNewIndicatorX = newIndicatorX; } private: + unsigned SequenceNumber() const override; std::pair DoGetRectangle(wxSize size) override; void Draw(OverlayPanel &panel, wxDC &dc) override; diff --git a/src/tracks/ui/Scrubbing.cpp b/src/tracks/ui/Scrubbing.cpp index 2a2e1c059..c5862b0e3 100644 --- a/src/tracks/ui/Scrubbing.cpp +++ b/src/tracks/ui/Scrubbing.cpp @@ -394,13 +394,6 @@ bool Scrubber::MaybeStartScrubbing(wxCoord xx) #endif std::max(0.0, MinStutter); - ControlToolBar::PlayAppearance appearance = - // commented out to fix Bug 1241 - // mSeeking - // ? ControlToolBar::PlayAppearance::Seek - // : ControlToolBar::PlayAppearance::Scrub; - ControlToolBar::PlayAppearance::Straight; -// const bool cutPreview = false; const bool backwards = time1 < time0; #ifdef EXPERIMENTAL_SCRUBBING_SCROLL_WHEEL static const double maxScrubSpeedBase = @@ -421,7 +414,7 @@ bool Scrubber::MaybeStartScrubbing(wxCoord xx) mScrubToken = ctb->PlayPlayRegion(SelectedRegion(time0, time1), options, - PlayMode::normalPlay, appearance, backwards); + PlayMode::normalPlay, backwards); if (mScrubToken <= 0) { // Bug1627 (part of it): // infinite error spew when trying to start scrub: @@ -485,8 +478,6 @@ bool Scrubber::StartSpeedPlay(double speed, double time0, double time1) mOptions.adjustStart = false; mOptions.isPlayingAtSpeed = true; - ControlToolBar::PlayAppearance appearance = ControlToolBar::PlayAppearance::Straight; - const bool backwards = time1 < time0; #ifdef EXPERIMENTAL_SCRUBBING_SCROLL_WHEEL static const double maxScrubSpeedBase = @@ -510,7 +501,7 @@ bool Scrubber::StartSpeedPlay(double speed, double time0, double time1) mScrubToken = // Reduce time by 'stopTolerance' fudge factor, so that the Play will stop. ctb->PlayPlayRegion(SelectedRegion(time0, time1-stopTolerance), options, - PlayMode::normalPlay, appearance, backwards); + PlayMode::normalPlay, backwards); if (mScrubToken >= 0) { mLastScrubPosition = 0; @@ -886,6 +877,11 @@ ScrubbingOverlay::ScrubbingOverlay(AudacityProject *project) this); } +unsigned ScrubbingOverlay::SequenceNumber() const +{ + return 40; +} + std::pair ScrubbingOverlay::DoGetRectangle(wxSize) { wxRect rect(mLastScrubRect); diff --git a/src/tracks/ui/Scrubbing.h b/src/tracks/ui/Scrubbing.h index 6f95b6564..4845c76f9 100644 --- a/src/tracks/ui/Scrubbing.h +++ b/src/tracks/ui/Scrubbing.h @@ -216,6 +216,7 @@ public: ScrubbingOverlay(AudacityProject *project); private: + unsigned SequenceNumber() const override; std::pair DoGetRectangle(wxSize size) override; void Draw(OverlayPanel &panel, wxDC &dc) override; diff --git a/src/tracks/ui/TrackButtonHandles.cpp b/src/tracks/ui/TrackButtonHandles.cpp index ca3ab3c5c..0eb3bc994 100644 --- a/src/tracks/ui/TrackButtonHandles.cpp +++ b/src/tracks/ui/TrackButtonHandles.cpp @@ -145,7 +145,7 @@ UIHandle::Result CloseButtonHandle::CommitChanges auto pTrack = mpTrack.lock(); if (pTrack) { - pProject->StopIfPaused(); + TransportActions::StopIfPaused( *pProject ); if (!pProject->IsAudioActive()) { // This pushes an undo item: TrackActions::DoRemoveTrack(*pProject, pTrack.get()); diff --git a/src/widgets/Meter.cpp b/src/widgets/Meter.cpp index eaff53515..9e89d9dee 100644 --- a/src/widgets/Meter.cpp +++ b/src/widgets/Meter.cpp @@ -253,9 +253,6 @@ bool MeterUpdateQueue::Get(MeterUpdateMsg &msg) // How many pixels between items? const static int gap = 2; -// Event used to notify all meters of preference changes -wxDEFINE_EVENT(EVT_METER_PREFERENCES_CHANGED, wxCommandEvent); - const static wxChar *PrefStyles[] = { wxT("AutomaticStereo"), @@ -348,11 +345,6 @@ MeterPanel::MeterPanel(AudacityProject *project, mPeakPeakPen = wxPen(theTheme.Colour( clrMeterPeak), 1, wxPENSTYLE_SOLID); mDisabledPen = wxPen(theTheme.Colour( clrMeterDisabledPen), 1, wxPENSTYLE_SOLID); - // Register for our preference update event - wxTheApp->Bind(EVT_METER_PREFERENCES_CHANGED, - &MeterPanel::OnMeterPrefsUpdated, - this); - if (mIsInput) { wxTheApp->Bind(EVT_AUDIOIO_MONITOR, &MeterPanel::OnAudioIOStatus, @@ -457,6 +449,20 @@ void MeterPanel::UpdatePrefs() Reset(mRate, false); mLayoutValid = false; + + Refresh(false); +} + +static int MeterPrefsID() +{ + static int value = wxNewId(); + return value; +} + +void MeterPanel::UpdateSelectedPrefs(int id) +{ + if (id == MeterPrefsID()) + UpdatePrefs(); } void MeterPanel::OnErase(wxEraseEvent & WXUNUSED(event)) @@ -1964,15 +1970,6 @@ void MeterPanel::OnMonitor(wxCommandEvent & WXUNUSED(event)) StartMonitoring(); } -void MeterPanel::OnMeterPrefsUpdated(wxCommandEvent & evt) -{ - evt.Skip(); - - UpdatePrefs(); - - Refresh(false); -} - void MeterPanel::OnPreferences(wxCommandEvent & WXUNUSED(event)) { wxTextCtrl *rate; @@ -2099,9 +2096,8 @@ void MeterPanel::OnPreferences(wxCommandEvent & WXUNUSED(event)) // Currently, there are 2 playback meters and 2 record meters and any number of // mixerboard meters, so we have to send out an preferences updated message to // ensure they all update themselves. - wxCommandEvent e(EVT_METER_PREFERENCES_CHANGED); - e.SetEventObject(this); - GetParent()->GetEventHandler()->ProcessEvent(e); + wxTheApp->AddPendingEvent(wxCommandEvent{ + EVT_PREFS_UPDATE, MeterPrefsID() }); } } diff --git a/src/widgets/Meter.h b/src/widgets/Meter.h index 8a2b8d5e9..f28bcc2d1 100644 --- a/src/widgets/Meter.h +++ b/src/widgets/Meter.h @@ -22,14 +22,11 @@ #include // member variable #include "../SampleFormat.h" +#include "../Prefs.h" #include "Ruler.h" // member variable class AudacityProject; -// Event used to notify all meters of preference changes -wxDECLARE_EXPORTED_EVENT(AUDACITY_DLL_API, - EVT_METER_PREFERENCES_CHANGED, wxCommandEvent); - // Increase this when we add support for multichannel meters // (most of the code is already there) const int kMaxMeterBars = 2; @@ -94,7 +91,7 @@ class MeterAx; \brief MeterPanel is a panel that paints the meter used for monitoring or playback. ************************************************************************/ -class MeterPanel final : public wxPanelWrapper +class MeterPanel final : public wxPanelWrapper, private PrefsListener { DECLARE_DYNAMIC_CLASS(MeterPanel) @@ -124,7 +121,6 @@ class MeterPanel final : public wxPanelWrapper void SetFocusFromKbd() override; - void UpdatePrefs(); void Clear(); Style GetStyle() const { return mStyle; } @@ -192,6 +188,9 @@ class MeterPanel final : public wxPanelWrapper int GetDBRange() const { return mDB ? mDBRange : -1; } private: + void UpdatePrefs() override; + void UpdateSelectedPrefs( int ) override; + static bool s_AcceptsFocus; struct Resetter { void operator () (bool *p) const { if(p) *p = false; } }; using TempAllowFocus = std::unique_ptr; @@ -232,7 +231,6 @@ class MeterPanel final : public wxPanelWrapper void ShowMenu(const wxPoint & pos); void OnMonitor(wxCommandEvent &evt); void OnPreferences(wxCommandEvent &evt); - void OnMeterPrefsUpdated(wxCommandEvent &evt); wxString Key(const wxString & key) const; diff --git a/src/widgets/Overlay.h b/src/widgets/Overlay.h index 914c28027..ba4f5f16d 100644 --- a/src/widgets/Overlay.h +++ b/src/widgets/Overlay.h @@ -93,6 +93,10 @@ class Overlay public: virtual ~Overlay() = 0; + ///\brief This number determines an ordering of overlays, so that those + /// with higher numbers overpaint those with lower numbers that intersect + virtual unsigned SequenceNumber() const = 0; + // nonvirtual wrapper std::pair GetRectangle(wxSize size); diff --git a/src/widgets/OverlayPanel.cpp b/src/widgets/OverlayPanel.cpp index 6235ea44b..c367ed48b 100644 --- a/src/widgets/OverlayPanel.cpp +++ b/src/widgets/OverlayPanel.cpp @@ -23,7 +23,16 @@ OverlayPanel::OverlayPanel(wxWindow * parent, wxWindowID id, void OverlayPanel::AddOverlay( const std::weak_ptr &pOverlay) { - mOverlays.push_back(pOverlay); + if (pOverlay.expired()) + return; + Compress(); + auto iter = std::lower_bound( mOverlays.begin(), mOverlays.end(), + pOverlay.lock()->SequenceNumber(), + []( const OverlayPtr &p, unsigned value ) { + return p.expired() || p.lock()->SequenceNumber() < value; + } + ); + mOverlays.insert(iter, pOverlay); } void OverlayPanel::ClearOverlays() diff --git a/src/widgets/OverlayPanel.h b/src/widgets/OverlayPanel.h index 93d4cae9d..e2ee3bdfa 100644 --- a/src/widgets/OverlayPanel.h +++ b/src/widgets/OverlayPanel.h @@ -40,8 +40,10 @@ public: void DrawOverlays(bool repaint_all, wxDC *pDC = nullptr); private: + using OverlayPtr = std::weak_ptr; + void Compress(); - std::vector< std::weak_ptr > mOverlays; + std::vector< OverlayPtr > mOverlays; DECLARE_EVENT_TABLE() diff --git a/win/Projects/Audacity/Audacity.vcxproj b/win/Projects/Audacity/Audacity.vcxproj index 0b48fa51e..08af97e03 100755 --- a/win/Projects/Audacity/Audacity.vcxproj +++ b/win/Projects/Audacity/Audacity.vcxproj @@ -221,6 +221,7 @@ + @@ -645,6 +646,7 @@ + diff --git a/win/Projects/Audacity/Audacity.vcxproj.filters b/win/Projects/Audacity/Audacity.vcxproj.filters index a52f9a118..d814b88bf 100755 --- a/win/Projects/Audacity/Audacity.vcxproj.filters +++ b/win/Projects/Audacity/Audacity.vcxproj.filters @@ -260,6 +260,9 @@ src + + src + src @@ -1315,6 +1318,9 @@ src + + src + src