670 lines
21 KiB
C++
670 lines
21 KiB
C++
/**********************************************************************
|
|
|
|
Audacity: A Digital Audio Editor
|
|
|
|
ProjectFileIO.cpp
|
|
|
|
Paul Licameli split from AudacityProject.cpp
|
|
|
|
**********************************************************************/
|
|
|
|
#include "ProjectFileIO.h"
|
|
|
|
#include <wx/frame.h>
|
|
|
|
#include "AutoRecovery.h"
|
|
#include "DirManager.h"
|
|
#include "FileNames.h"
|
|
#include "Project.h"
|
|
#include "ProjectFileIORegistry.h"
|
|
#include "ProjectSettings.h"
|
|
#include "Tags.h"
|
|
#include "ViewInfo.h"
|
|
#include "WaveTrack.h"
|
|
#include "widgets/AudacityMessageBox.h"
|
|
#include "widgets/NumericTextCtrl.h"
|
|
|
|
static void RefreshAllTitles(bool bShowProjectNumbers )
|
|
{
|
|
for ( auto pProject : AllProjects{} ) {
|
|
if ( !GetProjectFrame( *pProject ).IsIconized() ) {
|
|
ProjectFileIO::Get( *pProject ).SetProjectTitle(
|
|
bShowProjectNumbers ? pProject->GetProjectNumber() : -1 );
|
|
}
|
|
}
|
|
}
|
|
|
|
TitleRestorer::TitleRestorer(
|
|
wxTopLevelWindow &window, AudacityProject &project )
|
|
{
|
|
if( window.IsIconized() )
|
|
window.Restore();
|
|
window.Raise(); // May help identifying the window on Mac
|
|
|
|
// Construct this project's name and number.
|
|
sProjName = project.GetProjectName();
|
|
if ( sProjName.empty() ) {
|
|
sProjName = _("<untitled>");
|
|
UnnamedCount = std::count_if(
|
|
AllProjects{}.begin(), AllProjects{}.end(),
|
|
[]( const AllProjects::value_type &ptr ){
|
|
return ptr->GetProjectName().empty();
|
|
}
|
|
);
|
|
if ( UnnamedCount > 1 ) {
|
|
sProjNumber.Printf(
|
|
"[Project %02i] ", project.GetProjectNumber() + 1 );
|
|
RefreshAllTitles( true );
|
|
}
|
|
}
|
|
else
|
|
UnnamedCount = 0;
|
|
}
|
|
|
|
TitleRestorer::~TitleRestorer() {
|
|
if( UnnamedCount > 1 )
|
|
RefreshAllTitles( false );
|
|
}
|
|
|
|
static const AudacityProject::AttachedObjects::RegisteredFactory sFileIOKey{
|
|
[]( AudacityProject &parent ){
|
|
auto result = std::make_shared< ProjectFileIO >( parent );
|
|
return result;
|
|
}
|
|
};
|
|
|
|
ProjectFileIO &ProjectFileIO::Get( AudacityProject &project )
|
|
{
|
|
return project.AttachedObjects::Get< ProjectFileIO >( sFileIOKey );
|
|
}
|
|
|
|
const ProjectFileIO &ProjectFileIO::Get( const AudacityProject &project )
|
|
{
|
|
return Get( const_cast< AudacityProject & >( project ) );
|
|
}
|
|
|
|
// PRL: I preserve this handler function for an event that was never sent, but
|
|
// I don't know the intention.
|
|
|
|
ProjectFileIO::ProjectFileIO( AudacityProject &project )
|
|
: mProject{ project }
|
|
{
|
|
UpdatePrefs();
|
|
}
|
|
|
|
ProjectFileIO::~ProjectFileIO() = default;
|
|
|
|
void ProjectFileIO::UpdatePrefs()
|
|
{
|
|
SetProjectTitle();
|
|
}
|
|
|
|
// Pass a number in to show project number, or -1 not to.
|
|
void ProjectFileIO::SetProjectTitle( int number)
|
|
{
|
|
auto &project = mProject;
|
|
auto pWindow = project.GetFrame();
|
|
if ( !pWindow )
|
|
return;
|
|
auto &window = *pWindow;
|
|
wxString name = project.GetProjectName();
|
|
|
|
// If we are showing project numbers, then we also explicitly show "<untitled>" if there
|
|
// is none.
|
|
if( number >= 0 ){
|
|
/* i18n-hint: The %02i is the project number, the %s is the project name.*/
|
|
name = wxString::Format( _("[Project %02i] Audacity \"%s\""), number+1 ,
|
|
name.empty() ? "<untitled>" : (const char *)name );
|
|
}
|
|
// If we are not showing numbers, then <untitled> shows as 'Audacity'.
|
|
else if( name.empty() )
|
|
{
|
|
SetLoadedFromAup( false );
|
|
name = _TS("Audacity");
|
|
}
|
|
|
|
if ( IsRecovered() )
|
|
{
|
|
name += wxT(" ");
|
|
/* i18n-hint: E.g this is recovered audio that had been lost.*/
|
|
name += _("(Recovered)");
|
|
}
|
|
|
|
window.SetTitle( name );
|
|
window.SetName(name); // to make the nvda screen reader read the correct title
|
|
}
|
|
|
|
// Most of this string was duplicated 3 places. Made the warning consistent in this global.
|
|
// The %s is to be filled with the version string.
|
|
// PRL: Do not statically allocate a string in _() !
|
|
static TranslatableString gsLegacyFileWarning() { return
|
|
XO("This file was saved by Audacity version %s. The format has changed. \
|
|
\n\nAudacity can try to open and save this file, but saving it in this \
|
|
\nversion will then prevent any 1.2 or earlier version opening it. \
|
|
\n\nAudacity might corrupt the file in opening it, so you should \
|
|
back it up first. \
|
|
\n\nOpen this file now?");
|
|
}
|
|
|
|
bool ProjectFileIO::WarnOfLegacyFile( )
|
|
{
|
|
auto &project = mProject;
|
|
auto &window = GetProjectFrame( project );
|
|
auto msg = gsLegacyFileWarning().Format( XO("1.0 or earlier") );
|
|
|
|
// Stop icon, and choose 'NO' by default.
|
|
int action =
|
|
AudacityMessageBox(
|
|
msg,
|
|
XO("Warning - Opening Old Project File"),
|
|
wxYES_NO | wxICON_STOP | wxNO_DEFAULT | wxCENTRE,
|
|
&window);
|
|
return (action != wxNO);
|
|
}
|
|
|
|
bool ProjectFileIO::HandleXMLTag(const wxChar *tag, const wxChar **attrs)
|
|
{
|
|
auto &project = mProject;
|
|
auto &window = GetProjectFrame( project );
|
|
auto &viewInfo = ViewInfo::Get( project );
|
|
auto &dirManager = DirManager::Get( project );
|
|
auto &settings = ProjectSettings::Get( project );
|
|
bool bFileVersionFound = false;
|
|
wxString fileVersion = _("<unrecognized version -- possibly corrupt project file>");
|
|
wxString audacityVersion = _("<unrecognized version -- possibly corrupt project file>");
|
|
int requiredTags = 0;
|
|
long longVpos = 0;
|
|
|
|
// The auto-save data dir the project has been recovered from
|
|
FilePath recoveryAutoSaveDataDir;
|
|
|
|
// loop through attrs, which is a null-terminated list of
|
|
// attribute-value pairs
|
|
while(*attrs) {
|
|
const wxChar *attr = *attrs++;
|
|
const wxChar *value = *attrs++;
|
|
|
|
if (!value || !XMLValueChecker::IsGoodString(value))
|
|
break;
|
|
|
|
if (viewInfo.ReadXMLAttribute(attr, value)) {
|
|
// We need to save vpos now and restore it below
|
|
longVpos = std::max(longVpos, long(viewInfo.vpos));
|
|
continue;
|
|
}
|
|
|
|
if (!wxStrcmp(attr, wxT("datadir")))
|
|
{
|
|
//
|
|
// This is an auto-saved version whose data is in another directory
|
|
//
|
|
// Note: This attribute must currently be written and parsed before
|
|
// any other attributes
|
|
//
|
|
if ((value[0] != 0) && XMLValueChecker::IsGoodPathString(value))
|
|
{
|
|
// Remember that this is a recovered project
|
|
mIsRecovered = true;
|
|
recoveryAutoSaveDataDir = value;
|
|
}
|
|
}
|
|
|
|
else if (!wxStrcmp(attr, wxT("version")))
|
|
{
|
|
fileVersion = value;
|
|
bFileVersionFound = true;
|
|
requiredTags++;
|
|
}
|
|
|
|
else if (!wxStrcmp(attr, wxT("audacityversion"))) {
|
|
audacityVersion = value;
|
|
requiredTags++;
|
|
}
|
|
|
|
else if (!wxStrcmp(attr, wxT("projname"))) {
|
|
FilePath projName;
|
|
FilePath projPath;
|
|
|
|
if (mIsRecovered) {
|
|
// Fake the filename as if we had opened the original file
|
|
// (which was lost by the crash) rather than the one in the
|
|
// auto save folder
|
|
wxFileName realFileDir;
|
|
realFileDir.AssignDir(recoveryAutoSaveDataDir);
|
|
realFileDir.RemoveLastDir();
|
|
|
|
wxString realFileName = value;
|
|
if (realFileName.length() >= 5 &&
|
|
realFileName.Right(5) == wxT("_data"))
|
|
{
|
|
realFileName = realFileName.Left(realFileName.length() - 5);
|
|
}
|
|
|
|
if (realFileName.empty())
|
|
{
|
|
// A previously unsaved project has been recovered, so fake
|
|
// an unsaved project. The data files just stay in the temp
|
|
// directory
|
|
dirManager.SetLocalTempDir(recoveryAutoSaveDataDir);
|
|
project.SetFileName( wxT("") );
|
|
projName = wxT("");
|
|
projPath = wxT("");
|
|
}
|
|
else
|
|
{
|
|
realFileName += wxT(".aup");
|
|
projPath = realFileDir.GetFullPath();
|
|
project.SetFileName(
|
|
wxFileName{ projPath, realFileName }.GetFullPath() );
|
|
mbLoadedFromAup = true;
|
|
projName = value;
|
|
}
|
|
|
|
SetProjectTitle();
|
|
}
|
|
else {
|
|
projName = value;
|
|
projPath = wxPathOnly( project.GetFileName() );
|
|
}
|
|
|
|
if (!projName.empty())
|
|
{
|
|
// First try to load the data files based on the _data dir given in the .aup file
|
|
// If this fails then try to use the filename of the .aup as the base directory
|
|
// This is because unzipped projects e.g. those that get transfered between mac-pc
|
|
// have encoding issues and end up expanding the wrong filenames for certain
|
|
// international characters (such as capital 'A' with an umlaut.)
|
|
if (!dirManager.SetProject(projPath, projName, false))
|
|
{
|
|
projName = project.GetProjectName() + wxT("_data");
|
|
if (!dirManager.SetProject(projPath, projName, false)) {
|
|
AudacityMessageBox(
|
|
XO("Couldn't find the project data folder: \"%s\"")
|
|
.Format( projName ),
|
|
XO("Error Opening Project"),
|
|
wxOK | wxCENTRE,
|
|
&window);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
requiredTags++;
|
|
}
|
|
|
|
else if (!wxStrcmp(attr, wxT("rate"))) {
|
|
double rate;
|
|
Internat::CompatibleToDouble(value, &rate);
|
|
settings.SetRate( rate );
|
|
}
|
|
|
|
else if (!wxStrcmp(attr, wxT("snapto"))) {
|
|
settings.SetSnapTo(wxString(value) == wxT("on") ? true : false);
|
|
}
|
|
|
|
else if (!wxStrcmp(attr, wxT("selectionformat")))
|
|
settings.SetSelectionFormat(
|
|
NumericConverter::LookupFormat( NumericConverter::TIME, value) );
|
|
|
|
else if (!wxStrcmp(attr, wxT("audiotimeformat")))
|
|
settings.SetAudioTimeFormat(
|
|
NumericConverter::LookupFormat( NumericConverter::TIME, value) );
|
|
|
|
else if (!wxStrcmp(attr, wxT("frequencyformat")))
|
|
settings.SetFrequencySelectionFormatName(
|
|
NumericConverter::LookupFormat( NumericConverter::FREQUENCY, value ) );
|
|
|
|
else if (!wxStrcmp(attr, wxT("bandwidthformat")))
|
|
settings.SetBandwidthSelectionFormatName(
|
|
NumericConverter::LookupFormat( NumericConverter::BANDWIDTH, value ) );
|
|
} // while
|
|
|
|
if (longVpos != 0) {
|
|
// PRL: It seems this must happen after SetSnapTo
|
|
viewInfo.vpos = longVpos;
|
|
}
|
|
|
|
// Specifically detect newer versions of Audacity
|
|
// WARNING: This will need review/revision if we ever have a version string
|
|
// such as 1.5.10, i.e. with 2 digit numbers.
|
|
// We're able to do a shortcut and use string comparison because we know
|
|
// that does not happen.
|
|
// TODO: Um. We actually have released 0.98 and 1.3.14 so the comment
|
|
// above is inaccurate.
|
|
|
|
if (!bFileVersionFound ||
|
|
(fileVersion.length() != 5) || // expecting '1.1.0', for example
|
|
// JKC: I commented out next line. IsGoodInt is not for
|
|
// checking dotted numbers.
|
|
//!XMLValueChecker::IsGoodInt(fileVersion) ||
|
|
(fileVersion > wxT(AUDACITY_FILE_FORMAT_VERSION)))
|
|
{
|
|
/* i18n-hint: %s will be replaced by the version number.*/
|
|
auto msg = XO("This file was saved using Audacity %s.\nYou are using Audacity %s. You may need to upgrade to a newer version to open this file.")
|
|
.Format(audacityVersion,
|
|
AUDACITY_VERSION_STRING);
|
|
AudacityMessageBox(
|
|
msg,
|
|
XO("Can't open project file"),
|
|
wxOK | wxICON_EXCLAMATION | wxCENTRE,
|
|
&window);
|
|
return false;
|
|
}
|
|
|
|
// NOTE: It looks as if there was some confusion about fileversion and audacityversion.
|
|
// fileversion NOT being increased when file formats changed, so unfortunately we're
|
|
// using audacityversion to rescue the situation.
|
|
|
|
// KLUDGE: guess the true 'fileversion' by stripping away any '-beta-Rc' stuff on
|
|
// audacityVersion.
|
|
// It's fairly safe to do this as it has already been established that the
|
|
// supported file version was five chars long.
|
|
fileVersion = audacityVersion.Mid(0,5);
|
|
|
|
bool bIsOld = fileVersion < wxT(AUDACITY_FILE_FORMAT_VERSION);
|
|
bool bIsVeryOld = fileVersion < wxT("1.1.9" );
|
|
// Very old file versions could even have the file version starting
|
|
// with text: 'AudacityProject Version 0.95'
|
|
// Atoi return zero in this case.
|
|
bIsVeryOld |= wxAtoi( fileVersion )==0;
|
|
// Specifically detect older versions of Audacity
|
|
if ( bIsOld | bIsVeryOld ) {
|
|
auto msg = gsLegacyFileWarning().Format( audacityVersion );
|
|
|
|
int icon_choice = wxICON_EXCLAMATION;
|
|
if( bIsVeryOld )
|
|
// Stop icon, and choose 'NO' by default.
|
|
icon_choice = wxICON_STOP | wxNO_DEFAULT;
|
|
int action = AudacityMessageBox(
|
|
msg,
|
|
XO("Warning - Opening Old Project File"),
|
|
wxYES_NO | icon_choice | wxCENTRE,
|
|
&window);
|
|
if (action == wxNO)
|
|
return false;
|
|
}
|
|
|
|
if (wxStrcmp(tag, wxT("audacityproject")) &&
|
|
wxStrcmp(tag, wxT("project"))) {
|
|
// If the tag name is not one of these two (the NEW name is
|
|
// "project" with an Audacity namespace, but we don't detect
|
|
// the namespace yet), then we don't know what the error is
|
|
return false;
|
|
}
|
|
|
|
if (requiredTags < 3)
|
|
return false;
|
|
|
|
// All other tests passed, so we succeed
|
|
return true;
|
|
}
|
|
|
|
XMLTagHandler *ProjectFileIO::HandleXMLChild(const wxChar *tag)
|
|
{
|
|
auto &project = mProject;
|
|
auto fn = ProjectFileIORegistry::Lookup( tag );
|
|
if (fn)
|
|
return fn( project );
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void ProjectFileIO::WriteXMLHeader(XMLWriter &xmlFile) const
|
|
{
|
|
xmlFile.Write(wxT("<?xml "));
|
|
xmlFile.Write(wxT("version=\"1.0\" "));
|
|
xmlFile.Write(wxT("standalone=\"no\" "));
|
|
xmlFile.Write(wxT("?>\n"));
|
|
|
|
xmlFile.Write(wxT("<!DOCTYPE "));
|
|
xmlFile.Write(wxT("project "));
|
|
xmlFile.Write(wxT("PUBLIC "));
|
|
xmlFile.Write(wxT("\"-//audacityproject-1.3.0//DTD//EN\" "));
|
|
xmlFile.Write(wxT("\"http://audacity.sourceforge.net/xml/audacityproject-1.3.0.dtd\" "));
|
|
xmlFile.Write(wxT(">\n"));
|
|
}
|
|
|
|
void ProjectFileIO::WriteXML(
|
|
XMLWriter &xmlFile, FilePaths *strOtherNamesArray)
|
|
// may throw
|
|
{
|
|
auto &proj = mProject;
|
|
auto &tracks = TrackList::Get( proj );
|
|
auto &viewInfo = ViewInfo::Get( proj );
|
|
auto &dirManager = DirManager::Get( proj );
|
|
auto &tags = Tags::Get( proj );
|
|
const auto &settings = ProjectSettings::Get( proj );
|
|
|
|
bool bWantSaveCopy = (strOtherNamesArray != nullptr);
|
|
|
|
//TIMER_START( "AudacityProject::WriteXML", xml_writer_timer );
|
|
// Warning: This block of code is duplicated in Save, for now...
|
|
wxFileName project { proj.GetFileName() };
|
|
if (project.GetExt() == wxT("aup"))
|
|
project.SetExt( {} );
|
|
auto projName = project.GetFullName() + wxT("_data");
|
|
// End Warning -DMM
|
|
|
|
xmlFile.StartTag(wxT("project"));
|
|
xmlFile.WriteAttr(wxT("xmlns"), wxT("http://audacity.sourceforge.net/xml/"));
|
|
|
|
if (mAutoSaving)
|
|
{
|
|
//
|
|
// When auto-saving, remember full path to data files directory
|
|
//
|
|
// Note: This attribute must currently be written and parsed before
|
|
// all other attributes
|
|
//
|
|
xmlFile.WriteAttr(wxT("datadir"), dirManager.GetDataFilesDir());
|
|
|
|
// Note that the code at the start assumes that if mFileName has a value
|
|
// then the file has been saved. This is not necessarily true when
|
|
// autosaving as it gets set by AddImportedTracks (presumably as a proposal).
|
|
// I don't think that mDirManager.projName gets set without a save so check that.
|
|
if( !IsProjectSaved() )
|
|
projName = wxT("_data");
|
|
}
|
|
|
|
xmlFile.WriteAttr(wxT("projname"), projName);
|
|
xmlFile.WriteAttr(wxT("version"), wxT(AUDACITY_FILE_FORMAT_VERSION));
|
|
xmlFile.WriteAttr(wxT("audacityversion"), AUDACITY_VERSION_STRING);
|
|
|
|
viewInfo.WriteXMLAttributes(xmlFile);
|
|
xmlFile.WriteAttr(wxT("rate"), settings.GetRate());
|
|
xmlFile.WriteAttr(wxT("snapto"), settings.GetSnapTo() ? wxT("on") : wxT("off"));
|
|
xmlFile.WriteAttr(wxT("selectionformat"),
|
|
settings.GetSelectionFormat().Internal());
|
|
xmlFile.WriteAttr(wxT("frequencyformat"),
|
|
settings.GetFrequencySelectionFormatName().Internal());
|
|
xmlFile.WriteAttr(wxT("bandwidthformat"),
|
|
settings.GetBandwidthSelectionFormatName().Internal());
|
|
|
|
tags.WriteXML(xmlFile);
|
|
|
|
unsigned int ndx = 0;
|
|
tracks.Any().Visit(
|
|
[&](WaveTrack *pWaveTrack) {
|
|
if (bWantSaveCopy) {
|
|
if (!pWaveTrack->IsLeader())
|
|
return;
|
|
|
|
//vvv This should probably be a method, WaveTrack::WriteCompressedTrackXML().
|
|
xmlFile.StartTag(wxT("import"));
|
|
xmlFile.WriteAttr(wxT("filename"),
|
|
(*strOtherNamesArray)[ndx]); // Assumes mTracks order hasn't changed!
|
|
|
|
// Don't store "channel" and "linked" tags because the importer can figure that out,
|
|
// e.g., from stereo Ogg files.
|
|
// xmlFile.WriteAttr(wxT("channel"), t->GetChannel());
|
|
// xmlFile.WriteAttr(wxT("linked"), t->GetLinked());
|
|
|
|
const auto offset =
|
|
TrackList::Channels( pWaveTrack ).min( &WaveTrack::GetOffset );
|
|
xmlFile.WriteAttr(wxT("offset"), offset, 8);
|
|
xmlFile.WriteAttr(wxT("mute"), pWaveTrack->GetMute());
|
|
xmlFile.WriteAttr(wxT("solo"), pWaveTrack->GetSolo());
|
|
pWaveTrack->Track::WriteCommonXMLAttributes( xmlFile, false );
|
|
|
|
// Don't store "rate" tag because the importer can figure that out.
|
|
// xmlFile.WriteAttr(wxT("rate"), pWaveTrack->GetRate());
|
|
xmlFile.WriteAttr(wxT("gain"), (double)pWaveTrack->GetGain());
|
|
xmlFile.WriteAttr(wxT("pan"), (double)pWaveTrack->GetPan());
|
|
xmlFile.EndTag(wxT("import"));
|
|
|
|
ndx++;
|
|
}
|
|
else {
|
|
pWaveTrack->SetAutoSaveIdent(mAutoSaving ? ++ndx : 0);
|
|
pWaveTrack->WriteXML(xmlFile);
|
|
}
|
|
},
|
|
[&](Track *t) {
|
|
t->WriteXML(xmlFile);
|
|
}
|
|
);
|
|
|
|
if (!mAutoSaving)
|
|
{
|
|
// Only write closing bracket when not auto-saving, since we may add
|
|
// recording log data to the end of the file later
|
|
xmlFile.EndTag(wxT("project"));
|
|
}
|
|
//TIMER_STOP( xml_writer_timer );
|
|
|
|
}
|
|
|
|
static wxString CreateUniqueName()
|
|
{
|
|
static int count = 0;
|
|
return wxDateTime::Now().Format(wxT("%Y-%m-%d %H-%M-%S")) +
|
|
wxString::Format(wxT(" N-%i"), ++count);
|
|
}
|
|
|
|
//
|
|
// This small template class resembles a try-finally block
|
|
//
|
|
// It sets var to val_entry in the constructor and
|
|
// var to val_exit in the destructor.
|
|
//
|
|
template <typename T>
|
|
class VarSetter
|
|
{
|
|
public:
|
|
VarSetter(T* var, T val_entry, T val_exit)
|
|
{
|
|
mVar = var;
|
|
mValExit = val_exit;
|
|
*var = val_entry;
|
|
}
|
|
|
|
~VarSetter()
|
|
{
|
|
*mVar = mValExit;
|
|
}
|
|
private:
|
|
T* mVar;
|
|
T mValExit;
|
|
};
|
|
|
|
void ProjectFileIO::AutoSave()
|
|
{
|
|
auto &project = mProject;
|
|
auto &window = GetProjectFrame( project );
|
|
// SonifyBeginAutoSave(); // part of RBD's r10680 stuff now backed out
|
|
|
|
// To minimize the possibility of race conditions, we first write to a
|
|
// file with the extension ".tmp", then rename the file to .autosave
|
|
wxString projName;
|
|
|
|
auto fileName = project.GetFileName();
|
|
if (fileName.empty())
|
|
projName = wxT("New Project");
|
|
else
|
|
projName = wxFileName{ fileName }.GetName();
|
|
|
|
wxString fn = wxFileName(FileNames::AutoSaveDir(),
|
|
projName + wxString(wxT(" - ")) + CreateUniqueName()).GetFullPath();
|
|
|
|
// PRL: I found a try-catch and rewrote it,
|
|
// but this guard is unnecessary because AutoSaveFile does not throw
|
|
bool success = GuardedCall< bool >( [&]
|
|
{
|
|
VarSetter<bool> setter(&mAutoSaving, true, false);
|
|
|
|
AutoSaveFile buffer;
|
|
WriteXMLHeader( buffer );
|
|
WriteXML( buffer, nullptr );
|
|
|
|
wxFFile saveFile;
|
|
saveFile.Open(fn + wxT(".tmp"), wxT("wb"));
|
|
return buffer.Write(saveFile);
|
|
} );
|
|
|
|
if (!success)
|
|
return;
|
|
|
|
// Now that we have a NEW auto-save file, DELETE the old one
|
|
DeleteCurrentAutoSaveFile();
|
|
|
|
if (!mAutoSaveFileName.empty())
|
|
return; // could not remove auto-save file
|
|
|
|
if (!wxRenameFile(fn + wxT(".tmp"), fn + wxT(".autosave")))
|
|
{
|
|
AudacityMessageBox(
|
|
XO("Could not create autosave file: %s")
|
|
.Format( fn + wxT(".autosave") ),
|
|
XO("Error"),
|
|
wxICON_STOP,
|
|
&window);
|
|
return;
|
|
}
|
|
|
|
mAutoSaveFileName += fn + wxT(".autosave");
|
|
// no-op cruft that's not #ifdefed for NoteTrack
|
|
// See above for further comments.
|
|
// SonifyEndAutoSave();
|
|
}
|
|
|
|
void ProjectFileIO::DeleteCurrentAutoSaveFile()
|
|
{
|
|
auto &project = mProject;
|
|
auto &window = GetProjectFrame( project );
|
|
if (!mAutoSaveFileName.empty())
|
|
{
|
|
if (wxFileExists(mAutoSaveFileName))
|
|
{
|
|
if (!wxRemoveFile(mAutoSaveFileName))
|
|
{
|
|
AudacityMessageBox(
|
|
XO("Could not remove old autosave file: %s")
|
|
.Format( mAutoSaveFileName ),
|
|
XO("Error"),
|
|
wxICON_STOP,
|
|
&window);
|
|
return;
|
|
}
|
|
}
|
|
|
|
mAutoSaveFileName = wxT("");
|
|
}
|
|
}
|
|
|
|
bool ProjectFileIO::IsProjectSaved() const {
|
|
auto &project = mProject;
|
|
auto &dirManager = DirManager::Get( project );
|
|
// This is true if a project was opened from an .aup
|
|
// Otherwise it becomes true only when a project is first saved successfully
|
|
// in DirManager::SetProject
|
|
return (!dirManager.GetProjectName().empty());
|
|
}
|
|
|
|
void ProjectFileIO::Reset()
|
|
{
|
|
mProject.SetFileName( {} );
|
|
mIsRecovered = false;
|
|
mbLoadedFromAup = false;
|
|
SetProjectTitle();
|
|
}
|