1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-06-16 08:09:32 +02:00

Merge pull request #909 from Paul-Licameli/Bug2764

Bug2764 Open Project... (under Scriptables in Extra menus) can corrupt a project
This commit is contained in:
Paul Licameli 2021-05-24 20:09:00 -04:00 committed by GitHub
commit 6b0eed3a82
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 240 additions and 148 deletions

View File

@ -813,25 +813,8 @@ bool AudacityApp::MRUOpen(const FilePath &fullPathStr) {
if (ProjectFileManager::IsAlreadyOpen(fullPathStr)) if (ProjectFileManager::IsAlreadyOpen(fullPathStr))
return false; return false;
// DMM: If the project is dirty, that means it's been touched at ( void ) ProjectManager::OpenProject( proj, fullPathStr,
// all, and it's not safe to open a NEW project directly in its true /* addtohistory */, false /* reuseNonemptyProject */ );
// place. Only if the project is brand-NEW clean and the user
// hasn't done any action at all is it safe for Open to take place
// inside the current project.
//
// If you try to Open a NEW project inside the current window when
// there are no tracks, but there's an Undo history, etc, then
// bad things can happen, including data files moving to the NEW
// project directory, etc.
if (proj && (
ProjectHistory::Get( *proj ).GetDirty() ||
!TrackList::Get( *proj ).empty()
) )
proj = nullptr;
// This project is clean; it's never been touched. Therefore
// all relevant member variables are in their initial state,
// and it's okay to open a NEW project inside this window.
( void ) ProjectManager::OpenProject( proj, fullPathStr );
} }
else { else {
// File doesn't exist - remove file from history // File doesn't exist - remove file from history
@ -1487,7 +1470,8 @@ bool AudacityApp::InitPart2()
// Auto-recovery // Auto-recovery
// //
bool didRecoverAnything = false; bool didRecoverAnything = false;
if (!ShowAutoRecoveryDialogIfNeeded(&project, &didRecoverAnything)) // This call may reassign project (passed by reference)
if (!ShowAutoRecoveryDialogIfNeeded(project, &didRecoverAnything))
{ {
QuitAudacity(true); QuitAudacity(true);
} }
@ -1495,7 +1479,7 @@ bool AudacityApp::InitPart2()
// //
// Remainder of command line parsing, but only if we didn't recover // Remainder of command line parsing, but only if we didn't recover
// //
if (!didRecoverAnything) if (project && !didRecoverAnything)
{ {
if (parser->Found(wxT("t"))) if (parser->Found(wxT("t")))
{ {

View File

@ -36,7 +36,7 @@ enum {
class AutoRecoveryDialog final : public wxDialogWrapper class AutoRecoveryDialog final : public wxDialogWrapper
{ {
public: public:
AutoRecoveryDialog(AudacityProject *proj); explicit AutoRecoveryDialog(AudacityProject *proj);
bool HasRecoverables() const; bool HasRecoverables() const;
FilePaths GetRecoverables(); FilePaths GetRecoverables();
@ -417,29 +417,27 @@ void AutoRecoveryDialog::OnListKeyDown(wxKeyEvent &evt)
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
static bool RecoverAllProjects(const FilePaths &files, static bool RecoverAllProjects(const FilePaths &files,
AudacityProject **pproj) AudacityProject *&pproj)
{ {
// Open a project window for each auto save file // Open a project window for each auto save file
wxString filename; wxString filename;
bool result = true;
for (auto &file: files) for (auto &file: files)
{ {
AudacityProject *proj = nullptr; AudacityProject *proj = nullptr;
if (*pproj) // Reuse any existing project window, which will be the empty project
{ // created at application startup
// Reuse existing project window std::swap(proj, pproj);
proj = *pproj;
*pproj = NULL;
}
// Open project. // Open project.
if (ProjectManager::OpenProject(proj, file, false) == nullptr) if (ProjectManager::OpenProject(proj, file, false, true) == nullptr)
{ {
return false; result = false;
} }
} }
return true; return result;
} }
static void DiscardAllProjects(const FilePaths &files) static void DiscardAllProjects(const FilePaths &files)
@ -449,7 +447,7 @@ static void DiscardAllProjects(const FilePaths &files)
ProjectFileManager::DiscardAutosave(file); ProjectFileManager::DiscardAutosave(file);
} }
bool ShowAutoRecoveryDialogIfNeeded(AudacityProject **pproj, bool *didRecoverAnything) bool ShowAutoRecoveryDialogIfNeeded(AudacityProject *&pproj, bool *didRecoverAnything)
{ {
if (didRecoverAnything) if (didRecoverAnything)
{ {
@ -472,7 +470,7 @@ bool ShowAutoRecoveryDialogIfNeeded(AudacityProject **pproj, bool *didRecoverAny
// This must be done before "dlg" is declared. // This must be done before "dlg" is declared.
wxEventLoopBase::GetActive()->YieldFor(wxEVT_CATEGORY_UI); wxEventLoopBase::GetActive()->YieldFor(wxEVT_CATEGORY_UI);
AutoRecoveryDialog dialog(*pproj); AutoRecoveryDialog dialog(pproj);
if (dialog.HasRecoverables()) if (dialog.HasRecoverables())
{ {

View File

@ -26,7 +26,7 @@ class AudacityProject;
// The didRecoverAnything param is strictly for a return value. // The didRecoverAnything param is strictly for a return value.
// Any value passed in is ignored. // Any value passed in is ignored.
// //
bool ShowAutoRecoveryDialogIfNeeded(AudacityProject** pproj, bool ShowAutoRecoveryDialogIfNeeded(AudacityProject*& pproj,
bool *didRecoverAnything); bool *didRecoverAnything);
#endif #endif

View File

@ -862,17 +862,9 @@ bool ProjectFileManager::IsAlreadyOpen(const FilePath &projPathName)
return false; return false;
} }
// FIXME:? TRAP_ERR This should return a result that is checked. AudacityProject *ProjectFileManager::OpenFile( const ProjectChooserFn &chooser,
// See comment in AudacityApp::MRUOpen(). const FilePath &fileNameArg, bool addtohistory)
void ProjectFileManager::OpenFile(const FilePath &fileNameArg, bool addtohistory)
{ {
auto &project = mProject;
auto &history = ProjectHistory::Get( project );
auto &projectFileIO = ProjectFileIO::Get( project );
auto &tracks = TrackList::Get( project );
auto &trackPanel = TrackPanel::Get( project );
auto &window = ProjectWindow::Get( project );
// On Win32, we may be given a short (DOS-compatible) file name on rare // On Win32, we may be given a short (DOS-compatible) file name on rare
// occasions (e.g. stuff like "C:\PROGRA~1\AUDACI~1\PROJEC~1.AUP"). We // occasions (e.g. stuff like "C:\PROGRA~1\AUDACI~1\PROJEC~1.AUP"). We
// convert these to long file name first. // convert these to long file name first.
@ -882,7 +874,7 @@ void ProjectFileManager::OpenFile(const FilePath &fileNameArg, bool addtohistory
XO("Project resides on FAT formatted drive.\n" XO("Project resides on FAT formatted drive.\n"
"Copy it to another drive to open it."))) "Copy it to another drive to open it.")))
{ {
return; return nullptr;
} }
// Make sure it isn't already open. // Make sure it isn't already open.
@ -893,7 +885,7 @@ void ProjectFileManager::OpenFile(const FilePath &fileNameArg, bool addtohistory
// This was reported in http://bugzilla.audacityteam.org/show_bug.cgi?id=137#c17, // This was reported in http://bugzilla.audacityteam.org/show_bug.cgi?id=137#c17,
// but is not really part of that bug. Anyway, prevent it! // but is not really part of that bug. Anyway, prevent it!
if (IsAlreadyOpen(fileName)) if (IsAlreadyOpen(fileName))
return; return nullptr;
// Data loss may occur if users mistakenly try to open ".aup3.bak" files // Data loss may occur if users mistakenly try to open ".aup3.bak" files
// left over from an unsuccessful save or by previous versions of Audacity. // left over from an unsuccessful save or by previous versions of Audacity.
@ -905,8 +897,8 @@ void ProjectFileManager::OpenFile(const FilePath &fileNameArg, bool addtohistory
"You are trying to open an automatically created backup file.\nDoing this may result in severe data loss.\n\nPlease open the actual Audacity project file instead."), "You are trying to open an automatically created backup file.\nDoing this may result in severe data loss.\n\nPlease open the actual Audacity project file instead."),
XO("Warning - Backup File Detected"), XO("Warning - Backup File Detected"),
wxOK | wxCENTRE, wxOK | wxCENTRE,
&window); nullptr);
return; return nullptr;
} }
if (!::wxFileExists(fileName)) { if (!::wxFileExists(fileName)) {
@ -914,10 +906,11 @@ void ProjectFileManager::OpenFile(const FilePath &fileNameArg, bool addtohistory
XO("Could not open file: %s").Format( fileName ), XO("Could not open file: %s").Format( fileName ),
XO("Error Opening File"), XO("Error Opening File"),
wxOK | wxCENTRE, wxOK | wxCENTRE,
&window); nullptr);
return; return nullptr;
} }
// Following block covers cases other than a project file:
{ {
wxFFile ff(fileName, wxT("rb")); wxFFile ff(fileName, wxT("rb"));
@ -934,8 +927,8 @@ void ProjectFileManager::OpenFile(const FilePath &fileNameArg, bool addtohistory
XO("Could not open file: %s").Format( fileName ), XO("Could not open file: %s").Format( fileName ),
XO("Error opening file"), XO("Error opening file"),
wxOK | wxCENTRE, wxOK | wxCENTRE,
&window); nullptr);
return; return nullptr;
} }
char buf[7]; char buf[7];
@ -945,40 +938,60 @@ void ProjectFileManager::OpenFile(const FilePath &fileNameArg, bool addtohistory
XO("File may be invalid or corrupted: \n%s").Format( fileName ), XO("File may be invalid or corrupted: \n%s").Format( fileName ),
XO("Error Opening File or Project"), XO("Error Opening File or Project"),
wxOK | wxCENTRE, wxOK | wxCENTRE,
&window); nullptr);
return; return nullptr;
} }
if (wxStrncmp(buf, "SQLite", 6) != 0) if (wxStrncmp(buf, "SQLite", 6) != 0)
{ {
// Not a database
#ifdef EXPERIMENTAL_DRAG_DROP_PLUG_INS #ifdef EXPERIMENTAL_DRAG_DROP_PLUG_INS
// Is it a plug-in? // Is it a plug-in?
if (PluginManager::Get().DropFile(fileName)) if (PluginManager::Get().DropFile(fileName)) {
{
MenuCreator::RebuildAllMenuBars(); MenuCreator::RebuildAllMenuBars();
// Plug-in installation happened, not really opening of a file,
// so return null
return nullptr;
} }
else
#endif #endif
#ifdef USE_MIDI #ifdef USE_MIDI
if (FileNames::IsMidi(fileName)) if (FileNames::IsMidi(fileName)) {
{ auto &project = chooser(false);
DoImportMIDI(project, fileName); // If this succeeds, indo history is incremented, and it also does
// ZoomAfterImport:
if(DoImportMIDI(project, fileName))
return &project;
return nullptr;
} }
else
#endif #endif
{ auto &project = chooser(false);
Import(fileName); // Undo history is incremented inside this:
if (Get(project).Import(fileName)) {
// Undo history is incremented inside this:
// Bug 2743: Don't zoom with lof.
if (!fileName.AfterLast('.').IsSameAs(wxT("lof"), false))
ProjectWindow::Get(project).ZoomAfterImport(nullptr);
return &project;
} }
// Bug 2743: Don't zoom with lof. return nullptr;
if (!fileName.AfterLast('.').IsSameAs(wxT("lof"), false))
window.ZoomAfterImport(nullptr);
return;
} }
} }
auto &project = chooser(true);
return Get(project).OpenProjectFile(fileName, addtohistory);
}
AudacityProject *ProjectFileManager::OpenProjectFile(
const FilePath &fileName, bool addtohistory)
{
auto &project = mProject;
auto &history = ProjectHistory::Get( project );
auto &tracks = TrackList::Get( project );
auto &trackPanel = TrackPanel::Get( project );
auto &projectFileIO = ProjectFileIO::Get( project );
auto &window = ProjectWindow::Get( project );
auto results = ReadProjectFile( fileName ); auto results = ReadProjectFile( fileName );
const bool bParseSuccess = results.parseSuccess; const bool bParseSuccess = results.parseSuccess;
const auto &errorStr = results.errorString; const auto &errorStr = results.errorString;
const bool err = results.trackError; const bool err = results.trackError;
@ -1020,6 +1033,7 @@ void ProjectFileManager::OpenFile(const FilePath &fileNameArg, bool addtohistory
// PushState calls AutoSave(), so no longer need to do so here. // PushState calls AutoSave(), so no longer need to do so here.
history.PushState(XO("Project was recovered"), XO("Recover")); history.PushState(XO("Project was recovered"), XO("Recover"));
} }
return &project;
} }
else { else {
// Vaughan, 2011-10-30: // Vaughan, 2011-10-30:
@ -1044,6 +1058,8 @@ void ProjectFileManager::OpenFile(const FilePath &fileNameArg, bool addtohistory
XO("Error Opening Project"), XO("Error Opening Project"),
errorStr, errorStr,
results.helpUrl); results.helpUrl);
return nullptr;
} }
} }
@ -1292,7 +1308,7 @@ bool ProjectFileManager::Import(
history.PushState(XO("Imported '%s'").Format( fileName ), XO("Import")); history.PushState(XO("Imported '%s'").Format( fileName ), XO("Import"));
return false; return true;
} }
// PRL: Undo history is incremented inside this: // PRL: Undo history is incremented inside this:

View File

@ -11,6 +11,7 @@ Paul Licameli split from AudacityProject.h
#ifndef __AUDACITY_PROJECT_FILE_MANAGER__ #ifndef __AUDACITY_PROJECT_FILE_MANAGER__
#define __AUDACITY_PROJECT_FILE_MANAGER__ #define __AUDACITY_PROJECT_FILE_MANAGER__
#include <functional>
#include <memory> #include <memory>
#include <vector> #include <vector>
@ -43,16 +44,6 @@ public:
ProjectFileManager &operator=( const ProjectFileManager & ) PROHIBITED; ProjectFileManager &operator=( const ProjectFileManager & ) PROHIBITED;
~ProjectFileManager(); ~ProjectFileManager();
struct ReadProjectResults
{
bool parseSuccess;
bool trackError;
const TranslatableString errorString;
wxString helpUrl;
};
ReadProjectResults ReadProjectFile(
const FilePath &fileName, bool discardAutosave = false );
bool OpenProject(); bool OpenProject();
void CloseProject(); void CloseProject();
bool OpenNewProject(); bool OpenNewProject();
@ -91,7 +82,18 @@ public:
static bool IsAlreadyOpen(const FilePath &projPathName); static bool IsAlreadyOpen(const FilePath &projPathName);
void OpenFile(const FilePath &fileName, bool addtohistory = true); //! A function that returns a project to use for opening a file; argument is true if opening a project file
using ProjectChooserFn = std::function<AudacityProject&(bool)>;
/*!
Opens files of many kinds. In case of import (sound, MIDI, or .aup), the undo history is pushed.
@param chooser told whether opening a project file; decides which project to open into
@param fileName the name and contents are examined to decide a type and open appropriately
@param addtohistory whether to add .aup3 files to the MRU list (but always done for imports)
@return if something was successfully opened, the project containing it; else null
*/
static AudacityProject *OpenFile( const ProjectChooserFn &chooser,
const FilePath &fileName, bool addtohistory = true);
bool Import(const FilePath &fileName, bool Import(const FilePath &fileName,
bool addToHistory = true); bool addToHistory = true);
@ -105,6 +107,24 @@ public:
void SetMenuClose(bool value) { mMenuClose = value; } void SetMenuClose(bool value) { mMenuClose = value; }
private: private:
/*!
@param fileName a path assumed to exist and contain an .aup3 project
@param addtohistory whether to add the file to the MRU list
@return if something was successfully opened, the project containing it; else null
*/
AudacityProject *OpenProjectFile(
const FilePath &fileName, bool addtohistory);
struct ReadProjectResults
{
bool parseSuccess;
bool trackError;
const TranslatableString errorString;
wxString helpUrl;
};
ReadProjectResults ReadProjectFile(
const FilePath &fileName, bool discardAutosave = false );
bool DoSave(const FilePath & fileName, bool fromSaveAs); bool DoSave(const FilePath & fileName, bool fromSaveAs);
AudacityProject &mProject; AudacityProject &mProject;

View File

@ -838,17 +838,18 @@ void ProjectManager::OnCloseWindow(wxCloseEvent & event)
// PRL: I preserve this handler function for an event that was never sent, but // PRL: I preserve this handler function for an event that was never sent, but
// I don't know the intention. // I don't know the intention.
void ProjectManager::OnOpenAudioFile(wxCommandEvent & event) void ProjectManager::OnOpenAudioFile(wxCommandEvent & event)
{ {
auto &project = mProject;
auto &window = GetProjectFrame( project );
const wxString &cmd = event.GetString(); const wxString &cmd = event.GetString();
if (!cmd.empty()) {
if (!cmd.empty()) ProjectChooser chooser{ &mProject, true };
ProjectFileManager::Get( mProject ).OpenFile(cmd); if (auto project = ProjectFileManager::OpenFile(
std::ref(chooser), cmd)) {
window.RequestUserAttention(); auto &window = GetProjectFrame( *project );
window.RequestUserAttention();
chooser.Commit();
}
}
} }
// static method, can be called outside of a project // static method, can be called outside of a project
@ -868,68 +869,93 @@ void ProjectManager::OpenFiles(AudacityProject *proj)
Importer::SetLastOpenType({}); Importer::SetLastOpenType({});
} ); } );
for (size_t ff = 0; ff < selectedFiles.size(); ff++) { for (const auto &fileName : selectedFiles) {
const wxString &fileName = selectedFiles[ff];
// Make sure it isn't already open. // Make sure it isn't already open.
if (ProjectFileManager::IsAlreadyOpen(fileName)) if (ProjectFileManager::IsAlreadyOpen(fileName))
continue; // Skip ones that are already open. continue; // Skip ones that are already open.
// DMM: If the project is dirty, that means it's been touched at proj = OpenProject( proj, fileName,
// all, and it's not safe to open a NEW project directly in its true /* addtohistory */, false /* reuseNonemptyProject */ );
// place. Only if the project is brand-NEW clean and the user
// hasn't done any action at all is it safe for Open to take place
// inside the current project.
//
// If you try to Open a NEW project inside the current window when
// there are no tracks, but there's an Undo history, etc, then
// bad things can happen, including data files moving to the NEW
// project directory, etc.
if ( proj && (
ProjectHistory::Get( *proj ).GetDirty() ||
!TrackList::Get( *proj ).empty()
) )
proj = nullptr;
// This project is clean; it's never been touched. Therefore
// all relevant member variables are in their initial state,
// and it's okay to open a NEW project inside this window.
proj = OpenProject( proj, fileName );
} }
} }
AudacityProject *ProjectManager::OpenProject( bool ProjectManager::SafeToOpenProjectInto(AudacityProject &proj)
AudacityProject *pProject, const FilePath &fileNameArg, bool addtohistory)
{ {
bool success = false; // DMM: If the project is dirty, that means it's been touched at
AudacityProject *pNewProject = nullptr; // all, and it's not safe to open a fresh project directly in its
if ( ! pProject ) // place. Only if the project is brandnew clean and the user
pProject = pNewProject = New(); // hasn't done any action at all is it safe for Open to take place
auto cleanup = finally( [&] { // inside the current project.
if ( pNewProject ) //
GetProjectFrame( *pNewProject ).Close(true); // If you try to Open a fresh project inside the current window when
else if ( !success ) // there are no tracks, but there's an Undo history, etc, then
// bad things can happen, including orphan blocks, or tracks
// referring to non-existent blocks
if (
ProjectHistory::Get( proj ).GetDirty() ||
!TrackList::Get( proj ).empty()
)
return false;
// This project is clean; it's never been touched. Therefore
// all relevant member variables are in their initial state,
// and it's okay to open a NEW project inside its window.
return true;
}
ProjectManager::ProjectChooser::~ProjectChooser()
{
if (mpUsedProject) {
if (mpUsedProject == mpGivenProject) {
// Ensure that it happens here: don't wait for the application level // Ensure that it happens here: don't wait for the application level
// exception handler, because the exception may be intercepted // exception handler, because the exception may be intercepted
ProjectHistory::Get(*pProject).RollbackState(); ProjectHistory::Get(*mpGivenProject).RollbackState();
// Any exception now continues propagating // Any exception now continues propagating
} ); }
ProjectFileManager::Get( *pProject ).OpenFile( fileNameArg, addtohistory ); else
GetProjectFrame( *mpUsedProject ).Close(true);
// The above didn't throw, so change what finally will do
success = true;
pNewProject = nullptr;
auto &projectFileIO = ProjectFileIO::Get( *pProject );
if( projectFileIO.IsRecovered() ) {
auto &window = ProjectWindow::Get( *pProject );
window.Zoom( window.GetZoomOfToFit() );
// "Project was recovered" replaces "Create new project" in Undo History.
auto &undoManager = UndoManager::Get( *pProject );
undoManager.RemoveStates(0, 1);
} }
}
return pProject; AudacityProject &
ProjectManager::ProjectChooser::operator() ( bool openingProjectFile )
{
if (mpGivenProject) {
// Always check before opening a project file (for safety);
// May check even when opening other files
// (to preserve old behavior; as with the File > Open command specifying
// multiple files of whatever types, so that each gets its own window)
bool checkReuse = (openingProjectFile || !mReuseNonemptyProject);
if (!checkReuse || SafeToOpenProjectInto(*mpGivenProject))
return *(mpUsedProject = mpGivenProject);
}
return *(mpUsedProject = New());
}
void ProjectManager::ProjectChooser::Commit()
{
mpUsedProject = nullptr;
}
AudacityProject *ProjectManager::OpenProject(
AudacityProject *pGivenProject, const FilePath &fileNameArg,
bool addtohistory, bool reuseNonemptyProject)
{
ProjectManager::ProjectChooser chooser{ pGivenProject, reuseNonemptyProject };
if (auto pProject = ProjectFileManager::OpenFile(
std::ref(chooser), fileNameArg, addtohistory )) {
chooser.Commit();
auto &projectFileIO = ProjectFileIO::Get( *pProject );
if( projectFileIO.IsRecovered() ) {
auto &window = ProjectWindow::Get( *pProject );
window.Zoom( window.GetZoomOfToFit() );
// "Project was recovered" replaces "Create new project" in Undo History.
auto &undoManager = UndoManager::Get( *pProject );
undoManager.RemoveStates(0, 1);
}
return pProject;
}
return nullptr;
} }
// This is done to empty out the tracks, but without creating a new project. // This is done to empty out the tracks, but without creating a new project.

View File

@ -45,12 +45,56 @@ public:
// reason remains in this class, not in ProjectFileManager // reason remains in this class, not in ProjectFileManager
static void OpenFiles(AudacityProject *proj); static void OpenFiles(AudacityProject *proj);
// Return the given project if that is not NULL, else create a project. //! False when it is unsafe to overwrite proj with contents of an .aup3 file
// Then open the given project path. static bool SafeToOpenProjectInto(AudacityProject &proj);
// But if an exception escapes this function, create no NEW project.
//! Callable object that supplies the `chooser` argument of ProjectFileManager::OpenFile
/*!
Its operator(), called lower down in ProjectFileManager, decides which project to put new file data into,
using file type information deduced there. It may have the side effect of creating a project.
At the higher level where it is constructed, it provides conditional RAII.
One indicates there that the file opening succeeded by calling Commit(). But if that is never
called, creation of projects, or changes to a preexisting project, are undone.
*/
class ProjectChooser {
public:
/*!
@param pProject if not null, an existing project to reuse if possible
@param reuseNonemptyProject if true, may reuse the given project when nonempty,
but only if importing (not for a project file)
*/
ProjectChooser( AudacityProject *pProject, bool reuseNonemptyProject )
: mpGivenProject{ pProject }
, mReuseNonemptyProject{ reuseNonemptyProject }
{}
//! Don't copy. Use std::ref to pass it to ProjectFileManager
ProjectChooser( const ProjectChooser& ) PROHIBITED;
//! Destroy any fresh project, or rollback the existing project, unless committed
~ProjectChooser();
//! May create a fresh project
AudacityProject &operator() ( bool openingProjectFile );
//! Commit the creation of any fresh project or changes to the existing project
void Commit();
private:
AudacityProject *mpGivenProject;
AudacityProject *mpUsedProject = nullptr;
bool mReuseNonemptyProject;
};
//! Open a file into an AudacityProject, returning the project, or nullptr for failure
/*!
If an exception escapes this function, no projects are created.
@param pGivenProject if not null, a project that may be reused
@param fileNameArg path to the file to open; not always an Audacity project file, may be an import
@param addtohistory whether to add .aup3 files to the MRU list (but always done for imports)
@param reuseNonemptyProject if true, may reuse the given project when nonempty,
but only if importing (not for a project file)
*/
static AudacityProject *OpenProject( static AudacityProject *OpenProject(
AudacityProject *pProject, AudacityProject *pGivenProject,
const FilePath &fileNameArg, bool addtohistory = true); const FilePath &fileNameArg, bool addtohistory, bool reuseNonemptyProject);
void ResetProjectToEmpty(); void ResetProjectToEmpty();

View File

@ -59,13 +59,16 @@ bool OpenProjectCommand::Apply(const CommandContext & context){
auto oldFileName = projectFileIO.GetFileName(); auto oldFileName = projectFileIO.GetFileName();
if(mFileName.empty()) if(mFileName.empty())
{ {
// This path queries the user for files to open
auto project = &context.project; auto project = &context.project;
ProjectManager::OpenFiles(project); ProjectManager::OpenFiles(project);
} }
else else
{ {
ProjectFileManager::Get( context.project ) ProjectManager::ProjectChooser chooser{ &context.project, true };
.OpenFile(mFileName, mbAddToHistory); if(ProjectFileManager::OpenFile(
std::ref(chooser), mFileName, mbAddToHistory))
chooser.Commit();
} }
const auto &newFileName = projectFileIO.GetFileName(); const auto &newFileName = projectFileIO.GetFileName();

View File

@ -410,7 +410,8 @@ void LOFImportFileHandle::lofOpenFiles(wxString* ln)
* audio file. TODO: Some sort of message here? */ * audio file. TODO: Some sort of message here? */
#endif // USE_MIDI #endif // USE_MIDI
mProject = ProjectManager::OpenProject( mProject, targetfile ); mProject = ProjectManager::OpenProject( mProject, targetfile,
true /* addtohistory */, true /* reuseNonemptyProject */ );
// Set tok to right after filename // Set tok to right after filename
temptok2.SetString(targettoken); temptok2.SetString(targettoken);