1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-07-25 17:08:07 +02:00

Define DirManager::ProjectSetter so that commit steps can be deferred

This commit is contained in:
Paul Licameli 2018-10-07 16:50:11 -04:00
parent 0db0c6e0d5
commit ec506ab52b
2 changed files with 114 additions and 29 deletions

View File

@ -521,50 +521,100 @@ namespace {
}; };
} }
bool DirManager::SetProject( struct DirManager::ProjectSetter::Impl
wxString& newProjPath, const wxString& newProjName, const bool bCreate)
{ {
bool committed = false; Impl(
DirManager &dm,
wxString& newProjPath, const wxString& newProjName, const bool bCreate );
void Commit();
DirManager &dirManager;
bool committed{ false };
// RAII object
// Save old state of paths in case of failure // Save old state of paths in case of failure
PathRestorer pathRestorer{ committed, projPath, projName, projFull }; PathRestorer pathRestorer{
committed,
dirManager.projPath, dirManager.projName, dirManager.projFull };
// Another RAII object
// Be prepared to un-create directory on failure
Maybe<DirCleaner> dirCleaner;
// State variables to carry over into Commit()
// Remember old path to be cleaned up in case of successful move // Remember old path to be cleaned up in case of successful move
const auto oldFull = projFull; wxString oldFull{ dirManager.projFull };
wxArrayString newPaths;
size_t trueTotal{ 0 };
bool moving{ true };
// Make this true only after successful construction
bool ok{ false };
};
DirManager::ProjectSetter::ProjectSetter(
DirManager &dirManager,
wxString& newProjPath, const wxString& newProjName, const bool bCreate )
: mpImpl{
std::make_unique<Impl>( dirManager, newProjPath, newProjName, bCreate )
}
{
}
DirManager::ProjectSetter::~ProjectSetter()
{
}
bool DirManager::ProjectSetter::Ok()
{
return mpImpl->ok;
}
void DirManager::ProjectSetter::Commit()
{
mpImpl->Commit();
}
DirManager::ProjectSetter::Impl::Impl(
DirManager &dm,
wxString& newProjPath, const wxString& newProjName, const bool bCreate )
: dirManager{ dm }
{
// Choose new paths // Choose new paths
if (newProjPath == wxT("")) if (newProjPath == wxT(""))
newProjPath = ::wxGetCwd(); newProjPath = ::wxGetCwd();
this->projPath = newProjPath; dirManager.projPath = newProjPath;
this->projName = newProjName; dirManager.projName = newProjName;
if (newProjPath.Last() == wxFILE_SEP_PATH) if (newProjPath.Last() == wxFILE_SEP_PATH)
this->projFull = newProjPath + newProjName; dirManager.projFull = newProjPath + newProjName;
else else
this->projFull = newProjPath + wxFILE_SEP_PATH + newProjName; dirManager.projFull = newProjPath + wxFILE_SEP_PATH + newProjName;
// Verify new paths, maybe creating a directory // Verify new paths, maybe creating a directory
if (bCreate) { if (bCreate) {
if (!wxDirExists(projFull) && if (!wxDirExists(dirManager.projFull) &&
!wxMkdir(projFull)) !wxMkdir(dirManager.projFull))
return false; return;
#ifdef __UNIX__ #ifdef __UNIX__
chmod(OSFILENAME(projFull), 0775); chmod(OSFILENAME(dirManager.projFull), 0775);
#endif #endif
#ifdef __WXMAC__ #ifdef __WXMAC__
chmod(OSFILENAME(projFull), 0775); chmod(OSFILENAME(dirManager.projFull), 0775);
#endif #endif
} }
else if (!wxDirExists(projFull)) else if (!wxDirExists(dirManager.projFull))
return false; return;
// Be prepared to un-create directory on failure // Be prepared to un-create directory on failure
Maybe<DirCleaner> dirCleaner;
if (bCreate) if (bCreate)
dirCleaner.create( committed, projFull ); dirCleaner.create( committed, dirManager.projFull );
/* Move all files into this NEW directory. Files which are /* Move all files into this NEW directory. Files which are
"locked" get copied instead of moved. (This happens when "locked" get copied instead of moved. (This happens when
@ -572,9 +622,8 @@ bool DirManager::SetProject(
saved version of the old project must not be moved, saved version of the old project must not be moved,
otherwise the old project would not be safe.) */ otherwise the old project would not be safe.) */
bool moving = true; moving = true;
size_t trueTotal = 0; trueTotal = 0;
wxArrayString newPaths;
{ {
/* i18n-hint: This title appears on a dialog that indicates the progress /* i18n-hint: This title appears on a dialog that indicates the progress
@ -582,18 +631,18 @@ bool DirManager::SetProject(
ProgressDialog progress(_("Progress"), ProgressDialog progress(_("Progress"),
_("Saving project data files")); _("Saving project data files"));
int total = mBlockFileHash.size(); int total = dirManager.mBlockFileHash.size();
for (const auto &pair : mBlockFileHash) { for (const auto &pair : dirManager.mBlockFileHash) {
if( progress.Update(newPaths.size(), total) != ProgressResult::Success ) if( progress.Update(newPaths.size(), total) != ProgressResult::Success )
return false; return;
wxString newPath; wxString newPath;
if (auto b = pair.second.lock()) { if (auto b = pair.second.lock()) {
moving = moving && !b->IsLocked(); moving = moving && !b->IsLocked();
auto result = CopyToNewProjectDirectory( &*b ); auto result = dirManager.CopyToNewProjectDirectory( &*b );
if (!result.first) if (!result.first)
return false; return;
newPath = result.second; newPath = result.second;
++trueTotal; ++trueTotal;
} }
@ -601,11 +650,19 @@ bool DirManager::SetProject(
} }
} }
ok = true;
}
void DirManager::ProjectSetter::Impl::Commit()
{
wxASSERT( ok );
// We have built all of the new file tree. // We have built all of the new file tree.
// So cancel the destructor actions of the RAII objects.
committed = true; committed = true;
auto size = newPaths.size(); auto size = newPaths.size();
wxASSERT( size == mBlockFileHash.size() ); wxASSERT( size == dirManager.mBlockFileHash.size() );
// Commit changes to filenames in the BlockFile objects, and removal // Commit changes to filenames in the BlockFile objects, and removal
// of files at old paths, ONLY NOW! This must be nothrow. // of files at old paths, ONLY NOW! This must be nothrow.
@ -627,7 +684,7 @@ bool DirManager::SetProject(
// same procedure in all cases. // same procedure in all cases.
size_t ii = 0; size_t ii = 0;
for (const auto &pair : mBlockFileHash) for (const auto &pair : dirManager.mBlockFileHash)
{ {
BlockFilePtr b = pair.second.lock(); BlockFilePtr b = pair.second.lock();
@ -667,7 +724,7 @@ bool DirManager::SetProject(
// that we didn't put there, but that Finder may insert into the folders, // that we didn't put there, but that Finder may insert into the folders,
// and mercilessly remove them, in addition to removing the directories. // and mercilessly remove them, in addition to removing the directories.
auto cleanupLoc1 = oldFull.empty() ? mytemp : oldFull; auto cleanupLoc1 = oldFull.empty() ? dirManager.mytemp : oldFull;
CleanDir( CleanDir(
cleanupLoc1, cleanupLoc1,
wxEmptyString, // EmptyString => ALL directories. wxEmptyString, // EmptyString => ALL directories.
@ -680,6 +737,15 @@ bool DirManager::SetProject(
//Dont know if this will make the project dirty, but I doubt it. (mchinen) //Dont know if this will make the project dirty, but I doubt it. (mchinen)
// count += RecursivelyEnumerate(cleanupLoc2, dirlist, wxEmptyString, false, true); // count += RecursivelyEnumerate(cleanupLoc2, dirlist, wxEmptyString, false, true);
} }
}
bool DirManager::SetProject(
wxString& newProjPath, const wxString& newProjName, const bool bCreate)
{
ProjectSetter setter{ *this, newProjPath, newProjName, bCreate };
if (!setter.Ok())
return false;
setter.Commit();
return true; return true;
} }

View File

@ -59,9 +59,28 @@ class PROFILE_DLL_API DirManager final : public XMLTagHandler {
static void SetTempDir(const wxString &_temp) { globaltemp = _temp; } static void SetTempDir(const wxString &_temp) { globaltemp = _temp; }
class ProjectSetter
{
public:
ProjectSetter(
DirManager &dirManager,
wxString& newProjPath, // assigns it if empty
const wxString& newProjName, const bool bCreate);
~ProjectSetter();
bool Ok();
void Commit();
private:
struct Impl;
std::unique_ptr<Impl> mpImpl;
};
// Returns true on success. // Returns true on success.
// If SetProject is told NOT to create the directory // If SetProject is told NOT to create the directory
// but it doesn't already exist, SetProject fails and returns false. // but it doesn't already exist, SetProject fails and returns false.
// This function simply creates a ProjectSetter and commits it if successful.
// Using ProjectSetter directly allows separation of those steps.
bool SetProject( bool SetProject(
wxString& newProjPath, // assigns it if empty wxString& newProjPath, // assigns it if empty
const wxString& newProjName, const bool bCreate); const wxString& newProjName, const bool bCreate);