AUP3: Fixes a couple of reported issues

Add the missing "File -> Save Project -> Backup Project..." menu item

Allows "File -> Save Project -> Save Project" to save unmodified
projects.
This commit is contained in:
Leland Lucius 2020-07-03 14:38:57 -05:00
parent 337e223754
commit c04ed76b6b
5 changed files with 239 additions and 36 deletions

View File

@ -1219,38 +1219,45 @@ bool ProjectFileIO::SaveProject(const FilePath &fileName)
" ON CONFLICT(id) DO UPDATE SET doc = ?1;");
sqlite3_stmt *stmt = nullptr;
rc = sqlite3_prepare_v2(db, sql, -1, &stmt, 0);
if (rc != SQLITE_OK)
{
SetDBError(
XO("Unable to prepare project file command:\n\n%s").Format(sql)
);
return false;
}
sqlite3_stmt* stmt = nullptr;
// BIND SQL project
rc = sqlite3_bind_text(stmt, 1, doc, -1, SQLITE_STATIC);
if (rc != SQLITE_OK)
{
SetDBError(
XO("Unable to bind to project file document.")
);
return false;
}
auto finalize = finally([&]
{
if (stmt)
{
// This will free the statement
sqlite3_finalize(stmt);
}
});
rc = sqlite3_prepare_v2(db, sql, -1, &stmt, 0);
if (rc != SQLITE_OK)
{
SetDBError(
XO("Unable to prepare project file command:\n\n%s").Format(sql)
);
return false;
}
// BIND SQL project
rc = sqlite3_bind_text(stmt, 1, doc, -1, SQLITE_STATIC);
if (rc != SQLITE_OK)
{
SetDBError(
XO("Unable to bind to project file document.")
);
return false;
}
rc = sqlite3_step(stmt);
// This will free the statement
sqlite3_finalize(stmt);
if (rc != SQLITE_DONE)
{
SetDBError(
XO("Failed to save project file information.")
);
return false;
rc = sqlite3_step(stmt);
if (rc != SQLITE_DONE)
{
SetDBError(
XO("Failed to save project file information.")
);
return false;
}
}
// We need to remove the autosave info from the file since it is now
@ -1276,6 +1283,104 @@ bool ProjectFileIO::SaveProject(const FilePath &fileName)
return true;
}
bool ProjectFileIO::SaveCopy(const FilePath& fileName)
{
if (!CopyTo(fileName))
{
return false;
}
sqlite3 *db = nullptr;
int rc;
bool success = false;
auto cleanup = finally([&]
{
if (db)
{
sqlite3_close(db);
}
if (!success)
{
wxRemoveFile(fileName);
}
});
rc = sqlite3_open(fileName, &db);
if (rc != SQLITE_OK)
{
SetDBError(XO("Failed to open backup file"));
return false;
}
XMLStringWriter doc;
WriteXMLHeader(doc);
WriteXML(doc);
// Always use an ID of 1. This will replace any existing row.
char sql[256];
sqlite3_snprintf(sizeof(sql),
sql,
"INSERT INTO project(id, doc) VALUES(1, ?1)"
" ON CONFLICT(id) DO UPDATE SET doc = ?1;");
{
sqlite3_stmt* stmt = nullptr;
auto finalize = finally([&]
{
if (stmt)
{
// This will free the statement
sqlite3_finalize(stmt);
}
});
rc = sqlite3_prepare_v2(db, sql, -1, &stmt, 0);
if (rc != SQLITE_OK)
{
SetDBError(
XO("Unable to prepare project file command:\n\n%s").Format(sql)
);
return false;
}
// BIND SQL project
rc = sqlite3_bind_text(stmt, 1, doc, -1, SQLITE_STATIC);
if (rc != SQLITE_OK)
{
SetDBError(
XO("Unable to bind to project file document.")
);
return false;
}
rc = sqlite3_step(stmt);
if (rc != SQLITE_DONE)
{
SetDBError(
XO("Failed to save project file information.")
);
return false;
}
}
rc = sqlite3_exec(db, "DELETE FROM autosave;", nullptr, nullptr, nullptr);
if (rc != SQLITE_OK)
{
SetDBError(
XO("Failed to remove the autosave information from the project file.")
);
return false;
}
// Tell the finally block to behave
success = true;
return true;
}
bool ProjectFileIO::IsModified() const
{
return mModified;

View File

@ -68,6 +68,7 @@ public:
bool LoadProject(const FilePath &fileName);
bool SaveProject(const FilePath &fileName);
bool SaveCopy(const FilePath& fileName);
XMLTagHandler *HandleXMLChild(const wxChar *tag) override;
void WriteXMLHeader(XMLWriter &xmlFile) const;

View File

@ -335,13 +335,13 @@ bool ProjectFileManager::DoSave(const FilePath & fileName, const bool fromSaveAs
return true;
}
// This version of SaveAs is invoked only from scripting and does not
// prompt for a file name
bool ProjectFileManager::SaveAs(const wxString & newFileName, bool addToHistory /*= true*/)
{
auto &project = mProject;
auto &projectFileIO = ProjectFileIO::Get( project );
// This version of SaveAs is invoked only from scripting and does not
// prompt for a file name
auto oldFileName = projectFileIO.GetFileName();
bool bOwnsNewName = !projectFileIO.IsTemporary() && (oldFileName == newFileName);
@ -382,7 +382,7 @@ bool ProjectFileManager::SaveAs()
filename = projectFileIO.GetFileName();
}
// Bug 1304: Set a default file path if none was given. For Save/SaveAs
// Bug 1304: Set a default file path if none was given. For Save/SaveAs/SaveCopy
if( !FileNames::IsPathAvailable( filename.GetPath( wxPATH_GET_VOLUME| wxPATH_GET_SEPARATOR) ) ){
bHasPath = false;
filename.SetPath(FileNames::DefaultToDocumentsFolder(wxT("/SaveAs/Path")).GetPath());
@ -400,7 +400,7 @@ For an audio file that will open in other apps, use 'Export'.\n");
}
bool bPrompt = (project.mBatchMode == 0) || (projectFileIO.GetFileName().empty());
wxString fName;
FilePath fName;
if (bPrompt) {
// JKC: I removed 'wxFD_OVERWRITE_PROMPT' because we are checking
@ -425,7 +425,7 @@ For an audio file that will open in other apps, use 'Export'.\n");
filename.SetExt(wxT("aup3"));
fName = filename.GetFullPath();
if (!bPrompt && filename.FileExists()) {
if (bPrompt && filename.FileExists()) {
// Saving a copy of the project should never overwrite an existing project.
AudacityMessageDialog m(
nullptr,
@ -481,7 +481,7 @@ will be irreversibly overwritten.").Format( fName, fName );
// Overwrite disallowed. The destination project is open in another window.
AudacityMessageDialog m(
nullptr,
XO("The project will not saved because the selected project is open in another window.\nPlease try again and select an original name."),
XO("The project was not saved because the selected project is open in another window.\nPlease try again and select an original name."),
XO("Error Saving Project"),
wxOK|wxICON_ERROR );
m.ShowModal();
@ -502,6 +502,93 @@ will be irreversibly overwritten.").Format( fName, fName );
return(success);
}
bool ProjectFileManager::SaveCopy()
{
auto &project = mProject;
auto &projectFileIO = ProjectFileIO::Get(project);
auto &window = GetProjectFrame(project);
TitleRestorer Restorer(window, project); // RAII
wxFileName filename;
if (projectFileIO.IsTemporary())
{
filename = FileNames::DefaultToDocumentsFolder(wxT("/SaveAs/Path"));
}
else
{
filename = projectFileIO.GetFileName();
}
// Bug 1304: Set a default file path if none was given. For Save/SaveAs/SaveCopy
if (!FileNames::IsPathAvailable(filename.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR)))
{
filename.SetPath(FileNames::DefaultToDocumentsFolder(wxT("/SaveAs/Path")).GetPath());
}
TranslatableString title =
XO("%sSave Copy of Project \"%s\" As...").Format(Restorer.sProjNumber, Restorer.sProjName);
bool bPrompt = (project.mBatchMode == 0) || (projectFileIO.GetFileName().empty());
FilePath fName;
do
{
if (bPrompt)
{
// JKC: I removed 'wxFD_OVERWRITE_PROMPT' because we are checking
// for overwrite ourselves later, and we disallow it.
// We disallow overwrite because we would have to DELETE the many
// smaller files too, or prompt to move them.
fName = FileNames::SelectFile(FileNames::Operation::Export,
title,
filename.GetPath(),
filename.GetFullName(),
wxT("aup3"),
{ FileNames::AudacityProjects },
wxFD_SAVE | wxRESIZE_BORDER,
&window);
if (fName.empty())
{
return false;
}
filename = fName;
};
filename.SetExt(wxT("aup3"));
fName = filename.GetFullPath();
if (bPrompt && filename.FileExists())
{
// Saving a copy of the project should never overwrite an existing project.
AudacityMessageDialog m(nullptr,
XO("Saving a copy must not overwrite an existing saved project.\nPlease try again and select an original name."),
XO("Error Saving Copy of Project"),
wxOK | wxICON_ERROR);
m.ShowModal();
continue;
}
break;
} while (bPrompt);
if (!projectFileIO.SaveCopy(filename.GetFullPath()))
{
// Overwrite disallowed. The destination project is open in another window.
AudacityMessageDialog m(
nullptr,
XO("The project will not saved because the selected project is open in another window.\nPlease try again and select an original name."),
XO("Error Saving Project"),
wxOK | wxICON_ERROR);
m.ShowModal();
return false;
}
return true;
}
void ProjectFileManager::Reset()
{
// mLastSavedTrack code copied from OnCloseWindow.

View File

@ -59,6 +59,7 @@ public:
bool SaveAs(const wxString & newFileName, bool addToHistory = true);
// strProjectPathName is full path for aup except extension
bool SaveFromTimerRecording( wxFileName fnFile );
bool SaveCopy();
void Reset();

View File

@ -155,6 +155,13 @@ void OnSaveAs(const CommandContext &context )
projectFileManager.SaveAs();
}
void OnSaveCopy(const CommandContext &context )
{
auto &project = context.project;
auto &projectFileManager = ProjectFileManager::Get( project );
projectFileManager.SaveCopy();
}
void OnExportMp3(const CommandContext &context)
{
auto &project = context.project;
@ -595,8 +602,10 @@ BaseItemSharedPtr FileMenu()
Section( "Save",
Menu( wxT("Save"), XXO("&Save Project"),
Command( wxT("Save"), XXO("&Save Project"), FN(OnSave),
AudioIONotBusyFlag() | UnsavedChangesFlag(), wxT("Ctrl+S") ),
AudioIONotBusyFlag(), wxT("Ctrl+S") ),
Command( wxT("SaveAs"), XXO("Save Project &As..."), FN(OnSaveAs),
AudioIONotBusyFlag() ),
Command( wxT("SaveCopy"), XXO("&Backup Project..."), FN(OnSaveCopy),
AudioIONotBusyFlag() )
)
),