1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-08-01 08:29:27 +02:00

Split class ProjectFileManager from ProjectFileIO...

... The former will handle File menu items, and the choosing of file paths to
write; the latter will handle the XML contents and do auto-save.  Auto-save is
a lower-level thing that must be done in many places whenever undo history
is pushed or modified.
This commit is contained in:
Paul Licameli 2019-06-07 13:24:38 -04:00
parent e84dae4093
commit 4bf3365af4
6 changed files with 145 additions and 89 deletions

View File

@ -83,10 +83,34 @@ TitleRestorer::~TitleRestorer() {
RefreshAllTitles( false );
}
static const AudacityProject::AttachedObjects::RegisteredFactory sFileManagerKey{
[]( AudacityProject &parent ){
auto result = std::make_shared< ProjectFileManager >( parent );
return result;
}
};
ProjectFileManager &ProjectFileManager::Get( AudacityProject &project )
{
return project.AttachedObjects::Get< ProjectFileManager >( sFileManagerKey );
}
const ProjectFileManager &ProjectFileManager::Get( const AudacityProject &project )
{
return Get( const_cast< AudacityProject & >( project ) );
}
ProjectFileManager::ProjectFileManager( AudacityProject &project )
: mProject{ project }
{
}
ProjectFileManager::~ProjectFileManager() = default;
static const AudacityProject::AttachedObjects::RegisteredFactory sFileIOKey{
[]( AudacityProject &parent ){
auto result = std::make_shared< ProjectFileIO >( parent );
return result;
[]( AudacityProject &parent ){
auto result = std::make_shared< ProjectFileIO >( parent );
return result;
}
};
@ -176,18 +200,17 @@ bool ProjectFileIO::WarnOfLegacyFile( )
return (action != wxNO);
}
auto ProjectFileIO::ReadProjectFile( const FilePath &fileName )
auto ProjectFileManager::ReadProjectFile( const FilePath &fileName )
-> ReadProjectResults
{
auto &project = mProject;
auto &projectFileIO = ProjectFileIO::Get( project );
auto &window = GetProjectFrame( project );
project.SetFileName( fileName );
mbLoadedFromAup = true;
mIsRecovered = false;
SetProjectTitle();
projectFileIO.SetLoadedFromAup( true );
projectFileIO.SetIsRecovered( false );
projectFileIO.SetProjectTitle();
const wxString autoSaveExt = wxT("autosave");
if ( wxFileNameWrapper{ fileName }.GetExt() == autoSaveExt )
@ -233,7 +256,7 @@ auto ProjectFileIO::ReadProjectFile( const FilePath &fileName )
gPrefs->Flush();
} );
bool bParseSuccess = xmlFile.Parse(this, fileName);
bool bParseSuccess = xmlFile.Parse(&projectFileIO, fileName);
bool err = false;
@ -265,7 +288,7 @@ auto ProjectFileIO::ReadProjectFile( const FilePath &fileName )
return { false, bParseSuccess, err, xmlFile.GetErrorStr() };
}
void ProjectFileIO::EnqueueODTasks()
void ProjectFileManager::EnqueueODTasks()
{
//check the ODManager to see if we should add the tracks to the ODManager.
//this flag would have been set in the HandleXML calls from above, if there were
@ -722,10 +745,10 @@ private:
};
#endif
bool ProjectFileIO::Save()
bool ProjectFileManager::Save()
{
// Prompt for file name?
bool bPromptingRequired = !IsProjectSaved();
bool bPromptingRequired = !ProjectFileIO::Get( mProject ).IsProjectSaved();
if (bPromptingRequired)
return SaveAs();
@ -734,7 +757,7 @@ bool ProjectFileIO::Save()
}
// Assumes AudacityProject::mFileName has been set to the desired path.
bool ProjectFileIO::DoSave (const bool fromSaveAs,
bool ProjectFileManager::DoSave (const bool fromSaveAs,
const bool bWantSaveCopy,
const bool bLossless /*= false*/)
{
@ -744,6 +767,7 @@ bool ProjectFileIO::DoSave (const bool fromSaveAs,
const auto &fileName = proj.GetFileName();
auto &window = GetProjectFrame( proj );
auto &dirManager = DirManager::Get( proj );
auto &projectFileIO = ProjectFileIO::Get( proj );
const auto &settings = ProjectSettings::Get( proj );
wxASSERT_MSG(!bWantSaveCopy || fromSaveAs, "Copy Project SHOULD only be availabele from SaveAs");
@ -871,8 +895,8 @@ bool ProjectFileIO::DoSave (const bool fromSaveAs,
// (SetProject, when it fails, cleans itself up.)
XMLFileWriter saveFile{ fileName, _("Error Saving Project") };
success = GuardedCall< bool >( [&] {
WriteXMLHeader(saveFile);
WriteXML(saveFile, bWantSaveCopy ? &strOtherNamesArray : nullptr);
projectFileIO.WriteXMLHeader(saveFile);
projectFileIO.WriteXML(saveFile, bWantSaveCopy ? &strOtherNamesArray : nullptr);
// Flushes files, forcing space exhaustion errors before trying
// SetProject():
saveFile.PreCommit();
@ -940,9 +964,9 @@ bool ProjectFileIO::DoSave (const bool fromSaveAs,
if ( !bWantSaveCopy )
{
// Now that we have saved the file, we can DELETE the auto-saved version
DeleteCurrentAutoSaveFile();
projectFileIO.DeleteCurrentAutoSaveFile();
if (mIsRecovered)
if ( projectFileIO.IsRecovered() )
{
// This was a recovered file, that is, we have just overwritten the
// old, crashed .aup file. There may still be orphaned blockfiles in
@ -951,8 +975,8 @@ bool ProjectFileIO::DoSave (const bool fromSaveAs,
// Before we saved this, this was a recovered project, but now it is
// a regular project, so remember this.
mIsRecovered = false;
SetProjectTitle();
projectFileIO.SetIsRecovered( false );
projectFileIO.SetProjectTitle();
}
else if (fromSaveAs)
{
@ -992,7 +1016,7 @@ bool ProjectFileIO::DoSave (const bool fromSaveAs,
return true;
}
bool ProjectFileIO::SaveCopyWaveTracks(const FilePath & strProjectPathName,
bool ProjectFileManager::SaveCopyWaveTracks(const FilePath & strProjectPathName,
const bool bLossless, FilePaths &strOtherNamesArray)
{
auto &project = mProject;
@ -1099,15 +1123,17 @@ bool ProjectFileIO::SaveCopyWaveTracks(const FilePath & strProjectPathName,
return bSuccess;
}
bool ProjectFileIO::SaveAs(const wxString & newFileName, bool bWantSaveCopy /*= false*/, bool addToHistory /*= true*/)
bool ProjectFileManager::SaveAs(const wxString & newFileName, bool bWantSaveCopy /*= false*/, bool addToHistory /*= true*/)
{
auto &project = mProject;
auto &projectFileIO = ProjectFileIO::Get( project );
bool bLoadedFromAup = projectFileIO.IsLoadedFromAup();
// This version of SaveAs is invoked only from scripting and does not
// prompt for a file name
auto oldFileName = project.GetFileName();
bool bOwnsNewAupName = mbLoadedFromAup && (oldFileName == newFileName);
bool bOwnsNewAupName = bLoadedFromAup && (oldFileName == newFileName);
//check to see if the NEW project file already exists.
//We should only overwrite it if this project already has the same name, where the user
//simply chose to use the save as command although the save command would have the effect.
@ -1141,17 +1167,18 @@ bool ProjectFileIO::SaveAs(const wxString & newFileName, bool bWantSaveCopy /*=
{
}
else {
mbLoadedFromAup = true;
SetProjectTitle();
projectFileIO.SetLoadedFromAup( true );
projectFileIO.SetProjectTitle();
}
return(success);
}
bool ProjectFileIO::SaveAs(bool bWantSaveCopy /*= false*/, bool bLossless /*= false*/)
bool ProjectFileManager::SaveAs(bool bWantSaveCopy /*= false*/, bool bLossless /*= false*/)
{
auto &project = mProject;
auto &projectFileIO = ProjectFileIO::Get( project );
auto &window = ProjectWindow::Get( project );
TitleRestorer Restorer( window, project ); // RAII
bool bHasPath = true;
@ -1160,6 +1187,8 @@ bool ProjectFileIO::SaveAs(bool bWantSaveCopy /*= false*/, bool bLossless /*= fa
if (bLossless)
bWantSaveCopy = true;
bool bLoadedFromAup = projectFileIO.IsLoadedFromAup();
// Bug 1304: Set a default file path if none was given. For Save/SaveAs
if( !FileNames::IsPathAvailable( filename.GetPath( wxPATH_GET_VOLUME| wxPATH_GET_SEPARATOR) ) ){
bHasPath = false;
@ -1243,7 +1272,7 @@ For an audio file that will open in other apps, use 'Export'.\n");
return false;
}
bool bOwnsNewAupName = mbLoadedFromAup && ( project.GetFileName() == fName );
bool bOwnsNewAupName = bLoadedFromAup && ( project.GetFileName() == fName );
// Check to see if the project file already exists, and if it does
// check that the project file 'belongs' to this project.
// otherwise, prompt the user before overwriting.
@ -1318,8 +1347,8 @@ will be irreversibly overwritten."), fName, fName);
{
}
else {
mbLoadedFromAup = true;
SetProjectTitle();
projectFileIO.SetLoadedFromAup( true );
projectFileIO.SetProjectTitle();
}
@ -1448,7 +1477,7 @@ bool ProjectFileIO::IsProjectSaved() {
return (!dirManager.GetProjectName().empty());
}
void ProjectFileIO::ResetProjectFileIO()
void ProjectFileManager::Reset()
{
// mLastSavedTrack code copied from OnCloseWindow.
// Lock all blocks in all tracks of the last saved version, so that
@ -1456,17 +1485,22 @@ void ProjectFileIO::ResetProjectFileIO()
// in memory. After it's locked, DELETE the data structure so that
// there's no memory leak.
CloseLock();
ProjectFileIO::Get( mProject ).Reset();
}
//mLastSavedTracks = TrackList::Create();
void ProjectFileIO::Reset()
{
mProject.SetFileName( {} );
mIsRecovered = false;
mbLoadedFromAup = false;
SetProjectTitle();
}
bool ProjectFileIO::SaveFromTimerRecording(wxFileName fnFile)
bool ProjectFileManager::SaveFromTimerRecording(wxFileName fnFile)
{
auto &project = mProject;
auto &projectFileIO = ProjectFileIO::Get( project );
// MY: Will save the project to a NEW location a-la Save As
// and then tidy up after itself.
@ -1476,7 +1510,7 @@ bool ProjectFileIO::SaveFromTimerRecording(wxFileName fnFile)
// MY: To allow SaveAs from Timer Recording we need to check what
// the value of mFileName is before we change it.
FilePath sOldFilename;
if (IsProjectSaved()) {
if (projectFileIO.IsProjectSaved()) {
sOldFilename = project.GetFileName();
}
@ -1499,14 +1533,14 @@ bool ProjectFileIO::SaveFromTimerRecording(wxFileName fnFile)
if (bSuccess) {
FileHistory::Global().AddFileToHistory( project.GetFileName() );
mbLoadedFromAup = true;
SetProjectTitle();
projectFileIO.SetLoadedFromAup( true );
projectFileIO.SetProjectTitle();
}
return bSuccess;
}
void ProjectFileIO::CloseLock()
void ProjectFileManager::CloseLock()
{
// 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

View File

@ -18,19 +18,15 @@ Paul Licameli split from AudacityProject.h
class AudacityProject;
class TrackList;
///\brief Object associated with a project that manages reading and writing
/// of Audacity project file formats, and autosave
class ProjectFileIO final
class ProjectFileManager final
: public ClientData::Base
, private XMLTagHandler
, private PrefsListener
{
public:
static ProjectFileIO &Get( AudacityProject &project );
static const ProjectFileIO &Get( const AudacityProject &project );
static ProjectFileManager &Get( AudacityProject &project );
static const ProjectFileManager &Get( const AudacityProject &project );
explicit ProjectFileIO( AudacityProject &project );
~ProjectFileIO();
explicit ProjectFileManager( AudacityProject &project );
~ProjectFileManager();
struct ReadProjectResults
{
@ -43,8 +39,6 @@ public:
void EnqueueODTasks();
bool WarnOfLegacyFile( );
// To be called when closing a project that has been saved, so that
// block files are not erased
void CloseLock();
@ -56,6 +50,40 @@ public:
// strProjectPathName is full path for aup except extension
bool SaveFromTimerRecording( wxFileName fnFile );
void Reset();
void SetImportedDependencies( bool value ) { mImportedDependencies = value; }
private:
// Push names of NEW export files onto the path list
bool SaveCopyWaveTracks(const FilePath & strProjectPathName,
bool bLossless, FilePaths &strOtherNamesArray);
bool DoSave(bool fromSaveAs, bool bWantSaveCopy, bool bLossless = false);
AudacityProject &mProject;
std::shared_ptr<TrackList> mLastSavedTracks;
// Dependencies have been imported and a warning should be shown on save
bool mImportedDependencies{ false };
};
///\brief Object associated with a project that manages reading and writing
/// of Audacity project file formats, and autosave
class ProjectFileIO final
: public ClientData::Base
, public XMLTagHandler
, private PrefsListener
{
public:
static ProjectFileIO &Get( AudacityProject &project );
static const ProjectFileIO &Get( const AudacityProject &project );
explicit ProjectFileIO( AudacityProject &project );
~ProjectFileIO();
bool WarnOfLegacyFile( );
const FilePath &GetAutoSaveFileName() { return mAutoSaveFileName; }
// It seems odd to put this method in this class, but the results do depend
@ -65,25 +93,16 @@ public:
bool IsProjectSaved();
void ResetProjectFileIO();
void Reset();
void AutoSave();
void DeleteCurrentAutoSaveFile();
bool IsRecovered() const { return mIsRecovered; }
void SetImportedDependencies( bool value ) { mImportedDependencies = value; }
void SetIsRecovered( bool value ) { mIsRecovered = value; }
bool IsLoadedFromAup() const { return mbLoadedFromAup; }
void SetLoadedFromAup( bool value ) { mbLoadedFromAup = value; }
private:
// Push names of NEW export files onto the path list
bool SaveCopyWaveTracks(const FilePath & strProjectPathName,
bool bLossless, FilePaths &strOtherNamesArray);
bool DoSave(bool fromSaveAs, bool bWantSaveCopy, bool bLossless = false);
void UpdatePrefs() override;
// XMLTagHandler callback methods
bool HandleXMLTag(const wxChar *tag, const wxChar **attrs) override;
XMLTagHandler *HandleXMLChild(const wxChar *tag) override;
void WriteXMLHeader(XMLWriter &xmlFile) const;
@ -93,10 +112,14 @@ private:
void WriteXML(
XMLWriter &xmlFile, FilePaths *strOtherNamesArray) /* not override */;
// non-staic data members
AudacityProject &mProject;
private:
// XMLTagHandler callback methods
bool HandleXMLTag(const wxChar *tag, const wxChar **attrs) override;
std::shared_ptr<TrackList> mLastSavedTracks;
void UpdatePrefs() override;
// non-static data members
AudacityProject &mProject;
// Last auto-save file name and path (empty if none)
FilePath mAutoSaveFileName;
@ -108,9 +131,6 @@ private:
bool mIsRecovered{ false };
bool mbLoadedFromAup{ false };
// Dependencies have been imported and a warning should be shown on save
bool mImportedDependencies{ false };
};
class wxTopLevelWindow;

View File

@ -428,6 +428,7 @@ void ProjectManager::OnCloseWindow(wxCloseEvent & event)
{
auto &project = mProject;
auto &projectFileIO = ProjectFileIO::Get( project );
auto &projectFileManager = ProjectFileManager::Get( project );
const auto &settings = ProjectSettings::Get( project );
auto &projectAudioIO = ProjectAudioIO::Get( project );
auto &tracks = TrackList::Get( project );
@ -493,7 +494,7 @@ void ProjectManager::OnCloseWindow(wxCloseEvent & event)
&window);
if (result == wxCANCEL || (result == wxYES &&
!GuardedCall<bool>( [&]{ return projectFileIO.Save(); } )
!GuardedCall<bool>( [&]{ return projectFileManager.Save(); } )
)) {
event.Veto();
return;
@ -551,7 +552,7 @@ void ProjectManager::OnCloseWindow(wxCloseEvent & event)
// TODO: Is there a Mac issue here??
// SetMenuBar(NULL);
projectFileIO.CloseLock();
projectFileManager.CloseLock();
// Some of the AdornedRulerPanel functions refer to the TrackPanel, so destroy this
// before the TrackPanel is destroyed. This change was needed to stop Audacity
@ -967,6 +968,7 @@ void ProjectManager::OpenFile(const FilePath &fileNameArg, bool addtohistory)
auto &project = mProject;
auto &history = ProjectHistory::Get( project );
auto &projectFileIO = ProjectFileIO::Get( project );
auto &projectFileManager = ProjectFileManager::Get( project );
auto &tracks = TrackList::Get( project );
auto &trackPanel = TrackPanel::Get( project );
auto &dirManager = DirManager::Get( project );
@ -1089,7 +1091,7 @@ void ProjectManager::OpenFile(const FilePath &fileNameArg, bool addtohistory)
mRecordingRecoveryHandler.reset();
} );
auto results = projectFileIO.ReadProjectFile( fileName );
auto results = projectFileManager.ReadProjectFile( fileName );
if ( results.decodeError )
return;
@ -1131,7 +1133,7 @@ void ProjectManager::OpenFile(const FilePath &fileNameArg, bool addtohistory)
if ( bParseSuccess ) {
// This is a no-fail:
dirManager.FillBlockfilesCache();
projectFileIO.EnqueueODTasks();
projectFileManager.EnqueueODTasks();
}
// For an unknown reason, OSX requires that the project window be
@ -1199,7 +1201,7 @@ void ProjectManager::OpenFile(const FilePath &fileNameArg, bool addtohistory)
// this->PushState(_("Project checker repaired file"), _("Project Repair"));
if (status & FSCKstatus_SAVE_AUP)
projectFileIO.Save(), saved = true;
projectFileManager.Save(), saved = true;
}
}
@ -1207,7 +1209,7 @@ void ProjectManager::OpenFile(const FilePath &fileNameArg, bool addtohistory)
if (!saved)
// We processed an <import> tag, so save it as a normal project,
// with no <import> tags.
projectFileIO.Save();
projectFileManager.Save();
}
}
else {
@ -1306,6 +1308,7 @@ ProjectManager::AddImportedTracks(const FilePath &fileName,
auto &project = mProject;
auto &history = ProjectHistory::Get( project );
auto &projectFileIO = ProjectFileIO::Get( project );
auto &projectFileManager = ProjectFileManager::Get( project );
auto &tracks = TrackList::Get( project );
std::vector< std::shared_ptr< Track > > results;
@ -1366,9 +1369,7 @@ ProjectManager::AddImportedTracks(const FilePath &fileName,
{
SeqBlock& block = blocks[0];
if (block.f->IsAlias())
{
projectFileIO.SetImportedDependencies( true );
}
projectFileManager.SetImportedDependencies( true );
}
}
});
@ -1492,6 +1493,7 @@ bool ProjectManager::Import(
void ProjectManager::ResetProjectToEmpty() {
auto &project = mProject;
auto &projectFileIO = ProjectFileIO::Get( project );
auto &projectFileManager = ProjectFileManager::Get( project );
auto &projectHistory = ProjectHistory::Get( project );
auto &viewInfo = ViewInfo::Get( project );
@ -1502,7 +1504,7 @@ void ProjectManager::ResetProjectToEmpty() {
DirManager::Reset( project );
TrackFactory::Reset( project );
projectFileIO.ResetProjectFileIO();
projectFileManager.Reset();
projectHistory.SetDirty( false );
auto &undoManager = UndoManager::Get( project );

View File

@ -622,12 +622,12 @@ int TimerRecordDialog::ExecutePostRecordActions(bool bWasStopped) {
// Do Automatic Save?
if (m_bAutoSaveEnabled) {
auto &projectFileIO = ProjectFileIO::Get( *pProject );
auto &projectFileManager = ProjectFileManager::Get( *pProject );
// MY: If this project has already been saved then simply execute a Save here
if (m_bProjectAlreadySaved) {
bSaveOK = projectFileIO.Save();
bSaveOK = projectFileManager.Save();
} else {
bSaveOK = projectFileIO.SaveFromTimerRecording(m_fnAutoSaveFile);
bSaveOK = projectFileManager.SaveFromTimerRecording(m_fnAutoSaveFile);
}
}

View File

@ -87,10 +87,10 @@ void SaveProjectCommand::PopulateOrExchange(ShuttleGui & S)
bool SaveProjectCommand::Apply(const CommandContext &context)
{
auto &projectFileIO = ProjectFileIO::Get( context.project );
auto &projectFileManager = ProjectFileManager::Get( context.project );
if ( mFileName.empty() )
return projectFileIO.SaveAs(mbCompress);
return projectFileManager.SaveAs(mbCompress);
else
return projectFileIO.SaveAs(
return projectFileManager.SaveAs(
mFileName, mbCompress, mbAddToHistory);
}

View File

@ -187,30 +187,30 @@ void OnClose(const CommandContext &context )
void OnSave(const CommandContext &context )
{
auto &project = context.project;
auto &projectFileIO = ProjectFileIO::Get( project );
projectFileIO.Save();
auto &projectFileManager = ProjectFileManager::Get( project );
projectFileManager.Save();
}
void OnSaveAs(const CommandContext &context )
{
auto &project = context.project;
auto &projectFileIO = ProjectFileIO::Get( project );
projectFileIO.SaveAs();
auto &projectFileManager = ProjectFileManager::Get( project );
projectFileManager.SaveAs();
}
void OnSaveCopy(const CommandContext &context )
{
auto &project = context.project;
auto &projectFileIO = ProjectFileIO::Get( project );
projectFileIO.SaveAs(true, true);
auto &projectFileManager = ProjectFileManager::Get( project );
projectFileManager.SaveAs(true, true);
}
#ifdef USE_LIBVORBIS
void OnSaveCompressed(const CommandContext &context)
{
auto &project = context.project;
auto &projectFileIO = ProjectFileIO::Get( project );
projectFileIO.SaveAs(true);
auto &projectFileManager = ProjectFileManager::Get( project );
projectFileManager.SaveAs(true);
}
#endif