mirror of
https://github.com/cookiengineer/audacity
synced 2025-06-15 15:49:36 +02:00
Move ProjectFSCK out of DirManager...
... which makes DirManager.cpp not depend on MissingAliasFileDialog.cpp
This commit is contained in:
parent
e5c7e5a21f
commit
2faa24c96b
@ -124,7 +124,7 @@ void FindDependencies(AudacityProject *project,
|
||||
auto aliasBlockFile = static_cast<AliasBlockFile*>( &*f );
|
||||
const wxFileName &fileName = aliasBlockFile->GetAliasedFileName();
|
||||
|
||||
// In DirManager::ProjectFSCK(), if the user has chosen to
|
||||
// In ProjectFSCK(), if the user has chosen to
|
||||
// "Replace missing audio with silence", the code there puts in an empty wxFileName.
|
||||
// Don't count those in dependencies.
|
||||
if (!fileName.IsOk())
|
||||
|
@ -93,12 +93,10 @@
|
||||
#include "blockfile/ODPCMAliasBlockFile.h"
|
||||
#include "blockfile/ODDecodeBlockFile.h"
|
||||
#include "InconsistencyException.h"
|
||||
#include "MissingAliasFileDialog.h"
|
||||
#include "Project.h"
|
||||
#include "Prefs.h"
|
||||
#include "Sequence.h"
|
||||
#include "widgets/Warning.h"
|
||||
#include "widgets/MultiDialog.h"
|
||||
#include "widgets/ErrorDialog.h"
|
||||
#include "widgets/ProgressDialog.h"
|
||||
|
||||
@ -152,14 +150,14 @@ wxMemorySize GetFreeMemory()
|
||||
// JKC: Using flag wxDIR_NO_FOLLOW to NOT follow symbolic links.
|
||||
// Directories and files inside a project should never be symbolic
|
||||
// links, so if we find one, do not follow it.
|
||||
static int RecursivelyEnumerate(const FilePath &dirPath,
|
||||
int DirManager::RecursivelyEnumerate(const FilePath &dirPath,
|
||||
FilePaths& filePathArray, // output: all files in dirPath tree
|
||||
wxString dirspec,
|
||||
wxString filespec,
|
||||
bool bFiles, bool bDirs,
|
||||
int progress_count = 0,
|
||||
int progress_bias = 0,
|
||||
ProgressDialog* progress = NULL)
|
||||
int progress_count,
|
||||
int progress_bias,
|
||||
ProgressDialog* progress)
|
||||
{
|
||||
int count=0;
|
||||
bool cont;
|
||||
@ -206,7 +204,7 @@ static int RecursivelyEnumerate(const FilePath &dirPath,
|
||||
return count;
|
||||
}
|
||||
|
||||
static int RecursivelyEnumerateWithProgress(const FilePath &dirPath,
|
||||
int DirManager::RecursivelyEnumerateWithProgress(const FilePath &dirPath,
|
||||
FilePaths& filePathArray, // output: all files in dirPath tree
|
||||
wxString dirspec,
|
||||
wxString filespec,
|
||||
@ -228,7 +226,7 @@ static int RecursivelyEnumerateWithProgress(const FilePath &dirPath,
|
||||
return count;
|
||||
}
|
||||
|
||||
static int RecursivelyCountSubdirs( const FilePath &dirPath )
|
||||
int DirManager::RecursivelyCountSubdirs( const FilePath &dirPath )
|
||||
{
|
||||
bool bContinue;
|
||||
int nCount = 0;
|
||||
@ -248,9 +246,9 @@ static int RecursivelyCountSubdirs( const FilePath &dirPath )
|
||||
return nCount;
|
||||
}
|
||||
|
||||
static int RecursivelyRemoveEmptyDirs(const FilePath &dirPath,
|
||||
int nDirCount = 0,
|
||||
ProgressDialog* pProgress = NULL)
|
||||
int DirManager::RecursivelyRemoveEmptyDirs(const FilePath &dirPath,
|
||||
int nDirCount,
|
||||
ProgressDialog* pProgress)
|
||||
{
|
||||
bool bContinue;
|
||||
wxDir dir(dirPath);
|
||||
@ -293,8 +291,8 @@ static int RecursivelyRemoveEmptyDirs(const FilePath &dirPath,
|
||||
return nCount;
|
||||
}
|
||||
|
||||
static void RecursivelyRemove(const FilePaths& filePathArray, int count, int bias,
|
||||
int flags, const wxChar* message = NULL)
|
||||
void DirManager::RecursivelyRemove(const FilePaths& filePathArray, int count, int bias,
|
||||
int flags, const wxChar* message)
|
||||
{
|
||||
bool bFiles= (flags & kCleanFiles) != 0;
|
||||
bool bDirs = (flags & kCleanDirs) != 0;
|
||||
@ -1645,370 +1643,6 @@ bool DirManager::EnsureSafeFilename(const wxFileName &fName)
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check the BlockFiles against the disk state.
|
||||
// Missing Blockfile data can be regenerated if possible or replaced with silence.
|
||||
// Orphan blockfiles can be deleted.
|
||||
// Note that even BlockFiles not referenced by the current savefile (but locked
|
||||
// by history) will be reflected in the mBlockFileHash, and that's a
|
||||
// good thing; this is one reason why we use the hash and not the most
|
||||
// recent savefile.
|
||||
int DirManager::ProjectFSCK(const bool bForceError, const bool bAutoRecoverMode)
|
||||
{
|
||||
// In earlier versions of this method, enumerations of errors were
|
||||
// all done in sequence, then the user was prompted for each type of error.
|
||||
// The enumerations are now interleaved with prompting, because, for example,
|
||||
// user choosing to replace missing aliased block files with silence
|
||||
// needs to put in SilentBlockFiles and DELETE the corresponding auf files,
|
||||
// so those would then not be cumulated in missingAUFHash.
|
||||
// We still do the FindX methods outside the conditionals,
|
||||
// so the log always shows all found errors.
|
||||
|
||||
int action; // choice of action for each type of error
|
||||
int nResult = 0;
|
||||
|
||||
if (bForceError && !bAutoRecoverMode)
|
||||
{
|
||||
// TODO: Replace with more user friendly error message?
|
||||
/* i18n-hint: The audacity project file is XML and has 'tags' in it,
|
||||
rather like html tags <something>some stuff</something>.
|
||||
This error message is about the tags that hold the sequence information.
|
||||
The error message is confusing to users in English, and could just say
|
||||
"Found problems with <sequence> when checking project file." */
|
||||
wxString msg = _("Project check read faulty Sequence tags.");
|
||||
const wxChar *buttons[] =
|
||||
{_("Close project immediately with no changes"),
|
||||
_("Continue with repairs noted in log, and check for more errors. This will save the project in its current state, unless you \"Close project immediately\" on further error alerts."),
|
||||
NULL};
|
||||
wxLog::FlushActive(); // MultiDialog has "Show Log..." button, so make sure log is current.
|
||||
action = ShowMultiDialog(msg, _("Warning - Problems Reading Sequence Tags"), buttons);
|
||||
if (action == 0)
|
||||
nResult = FSCKstatus_CLOSE_REQ;
|
||||
else
|
||||
nResult = FSCKstatus_CHANGED | FSCKstatus_SAVE_AUP;
|
||||
}
|
||||
|
||||
FilePaths filePathArray; // *all* files in the project directory/subdirectories
|
||||
auto dirPath = (!projFull.empty() ? projFull : mytemp);
|
||||
RecursivelyEnumerateWithProgress(
|
||||
dirPath,
|
||||
filePathArray, // output: all files in project directory tree
|
||||
wxEmptyString, // All dirs
|
||||
wxEmptyString, // All files
|
||||
true, false,
|
||||
mBlockFileHash.size(), // rough guess of how many BlockFiles will be found/processed, for progress
|
||||
_("Inspecting project file data"));
|
||||
|
||||
//
|
||||
// MISSING ALIASED AUDIO FILES
|
||||
//
|
||||
MissingAliasFilesDialog::SetShouldShow(false);
|
||||
BlockHash missingAliasFilesAUFHash; // (.auf) AliasBlockFiles whose aliased files are missing
|
||||
BlockHash missingAliasFilesPathHash; // full paths of missing aliased files
|
||||
this->FindMissingAliasFiles(missingAliasFilesAUFHash, missingAliasFilesPathHash);
|
||||
|
||||
if ((nResult != FSCKstatus_CLOSE_REQ) && !missingAliasFilesAUFHash.empty())
|
||||
{
|
||||
// In auto-recover mode, we always create silent blocks, and do not ask user.
|
||||
// This makes sure the project is complete next time we open it.
|
||||
if (bAutoRecoverMode)
|
||||
action = 2;
|
||||
else
|
||||
{
|
||||
wxString msgA =
|
||||
_("Project check of \"%s\" folder \
|
||||
\ndetected %lld missing external audio file(s) \
|
||||
\n('aliased files'). There is no way for Audacity \
|
||||
\nto recover these files automatically. \
|
||||
\n\nIf you choose the first or second option below, \
|
||||
\nyou can try to find and restore the missing files \
|
||||
\nto their previous location. \
|
||||
\n\nNote that for the second option, the waveform \
|
||||
\nmay not show silence. \
|
||||
\n\nIf you choose the third option, this will save the \
|
||||
\nproject in its current state, unless you \"Close \
|
||||
\nproject immediately\" on further error alerts.");
|
||||
wxString msg;
|
||||
msg.Printf(msgA, this->projName, (long long) missingAliasFilesPathHash.size());
|
||||
const wxChar *buttons[] =
|
||||
{_("Close project immediately with no changes"),
|
||||
_("Treat missing audio as silence (this session only)"),
|
||||
_("Replace missing audio with silence (permanent immediately)."),
|
||||
NULL};
|
||||
wxLog::FlushActive(); // MultiDialog has "Show Log..." button, so make sure log is current.
|
||||
action = ShowMultiDialog(msg, _("Warning - Missing Aliased File(s)"), buttons);
|
||||
}
|
||||
|
||||
if (action == 0)
|
||||
nResult = FSCKstatus_CLOSE_REQ;
|
||||
else
|
||||
{
|
||||
// LL: A progress dialog should probably be used here
|
||||
BlockHash::iterator iter = missingAliasFilesAUFHash.begin();
|
||||
while (iter != missingAliasFilesAUFHash.end())
|
||||
{
|
||||
// This type cast is safe. We checked that it's an alias block file earlier.
|
||||
BlockFilePtr b = iter->second.lock();
|
||||
wxASSERT(b);
|
||||
if (b) {
|
||||
auto ab = static_cast< AliasBlockFile * > ( &*b );
|
||||
|
||||
if (action == 2)
|
||||
{
|
||||
// silence the blockfiles by yanking the filename
|
||||
// This is done, eventually, in PCMAliasBlockFile::ReadData()
|
||||
// and ODPCMAliasBlockFile::ReadData, in the stack of b->Recover().
|
||||
// There, if the mAliasedFileName is bad, it zeroes the data.
|
||||
wxFileNameWrapper dummy;
|
||||
dummy.Clear();
|
||||
ab->ChangeAliasedFileName(std::move(dummy));
|
||||
|
||||
// If recovery fails for one file, silence it,
|
||||
// and don't try to recover other files but
|
||||
// silence them too. GuardedCall will cause an appropriate
|
||||
// error message for the user.
|
||||
GuardedCall(
|
||||
[&] { ab->Recover(); },
|
||||
[&] (AudacityException*) { action = 1; }
|
||||
);
|
||||
|
||||
nResult = FSCKstatus_CHANGED | FSCKstatus_SAVE_AUP;
|
||||
}
|
||||
|
||||
if (action == 1)
|
||||
// Silence error logging for this block in this session.
|
||||
ab->SilenceAliasLog();
|
||||
}
|
||||
++iter;
|
||||
}
|
||||
if ((action == 2) && bAutoRecoverMode)
|
||||
wxLogWarning(_(" Project check replaced missing aliased file(s) with silence."));
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// MISSING ALIAS (.AUF) AliasBlockFiles
|
||||
//
|
||||
// Alias summary regeneration must happen after checking missing aliased files.
|
||||
//
|
||||
BlockHash missingAUFHash; // missing (.auf) AliasBlockFiles
|
||||
this->FindMissingAUFs(missingAUFHash);
|
||||
if ((nResult != FSCKstatus_CLOSE_REQ) && !missingAUFHash.empty())
|
||||
{
|
||||
// In auto-recover mode, we just recreate the alias files, and do not ask user.
|
||||
// This makes sure the project is complete next time we open it.
|
||||
if (bAutoRecoverMode)
|
||||
action = 0;
|
||||
else
|
||||
{
|
||||
wxString msgA =
|
||||
_("Project check of \"%s\" folder \
|
||||
\ndetected %lld missing alias (.auf) blockfile(s). \
|
||||
\nAudacity can fully regenerate these files \
|
||||
\nfrom the current audio in the project.");
|
||||
wxString msg;
|
||||
msg.Printf(msgA, this->projName, (long long) missingAUFHash.size());
|
||||
const wxChar *buttons[] = {_("Regenerate alias summary files (safe and recommended)"),
|
||||
_("Fill in silence for missing display data (this session only)"),
|
||||
_("Close project immediately with no further changes"),
|
||||
NULL};
|
||||
wxLog::FlushActive(); // MultiDialog has "Show Log..." button, so make sure log is current.
|
||||
action = ShowMultiDialog(msg, _("Warning - Missing Alias Summary File(s)"), buttons);
|
||||
}
|
||||
|
||||
if (action == 2)
|
||||
nResult = FSCKstatus_CLOSE_REQ;
|
||||
else
|
||||
{
|
||||
// LL: A progress dialog should probably be used here
|
||||
BlockHash::iterator iter = missingAUFHash.begin();
|
||||
while (iter != missingAUFHash.end())
|
||||
{
|
||||
BlockFilePtr b = iter->second.lock();
|
||||
wxASSERT(b);
|
||||
if (b) {
|
||||
if(action==0) {
|
||||
//regenerate from data
|
||||
// If recovery fails for one file, silence it,
|
||||
// and don't try to recover other files but
|
||||
// silence them too. GuardedCall will cause an appropriate
|
||||
// error message for the user.
|
||||
GuardedCall(
|
||||
[&] {
|
||||
b->Recover();
|
||||
nResult |= FSCKstatus_CHANGED;
|
||||
},
|
||||
[&] (AudacityException*) { action = 1; }
|
||||
);
|
||||
}
|
||||
|
||||
if (action==1){
|
||||
// Silence error logging for this block in this session.
|
||||
b->SilenceLog();
|
||||
}
|
||||
}
|
||||
++iter;
|
||||
}
|
||||
if ((action == 0) && bAutoRecoverMode)
|
||||
wxLogWarning(_(" Project check regenerated missing alias summary file(s)."));
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// MISSING (.AU) SimpleBlockFiles
|
||||
//
|
||||
BlockHash missingAUHash; // missing data (.au) blockfiles
|
||||
this->FindMissingAUs(missingAUHash);
|
||||
if ((nResult != FSCKstatus_CLOSE_REQ) && !missingAUHash.empty())
|
||||
{
|
||||
// In auto-recover mode, we just always create silent blocks.
|
||||
// This makes sure the project is complete next time we open it.
|
||||
if (bAutoRecoverMode)
|
||||
action = 2;
|
||||
else
|
||||
{
|
||||
wxString msgA =
|
||||
_("Project check of \"%s\" folder \
|
||||
\ndetected %lld missing audio data (.au) blockfile(s), \
|
||||
\nprobably due to a bug, system crash, or accidental \
|
||||
\ndeletion. There is no way for Audacity to recover \
|
||||
\nthese missing files automatically. \
|
||||
\n\nIf you choose the first or second option below, \
|
||||
\nyou can try to find and restore the missing files \
|
||||
\nto their previous location. \
|
||||
\n\nNote that for the second option, the waveform \
|
||||
\nmay not show silence.");
|
||||
wxString msg;
|
||||
msg.Printf(msgA, this->projName, (long long) missingAUHash.size());
|
||||
const wxChar *buttons[] =
|
||||
{_("Close project immediately with no further changes"),
|
||||
_("Treat missing audio as silence (this session only)"),
|
||||
_("Replace missing audio with silence (permanent immediately)"),
|
||||
NULL};
|
||||
wxLog::FlushActive(); // MultiDialog has "Show Log..." button, so make sure log is current.
|
||||
action = ShowMultiDialog(msg, _("Warning - Missing Audio Data Block File(s)"), buttons);
|
||||
}
|
||||
|
||||
if (action == 0)
|
||||
nResult = FSCKstatus_CLOSE_REQ;
|
||||
else
|
||||
{
|
||||
// LL: A progress dialog should probably be used here
|
||||
BlockHash::iterator iter = missingAUHash.begin();
|
||||
while (iter != missingAUHash.end())
|
||||
{
|
||||
BlockFilePtr b = iter->second.lock();
|
||||
wxASSERT(b);
|
||||
if (b) {
|
||||
if (action == 2)
|
||||
{
|
||||
//regenerate from data
|
||||
// If recovery fails for one file, silence it,
|
||||
// and don't try to recover other files but
|
||||
// silence them too. GuardedCall will cause an appropriate
|
||||
// error message for the user.
|
||||
GuardedCall(
|
||||
[&] {
|
||||
//regenerate with zeroes
|
||||
b->Recover();
|
||||
nResult |= FSCKstatus_CHANGED;
|
||||
},
|
||||
[&] (AudacityException*) { action = 1; }
|
||||
);
|
||||
}
|
||||
|
||||
if (action == 1)
|
||||
b->SilenceLog();
|
||||
}
|
||||
++iter;
|
||||
}
|
||||
if ((action == 2) && bAutoRecoverMode)
|
||||
wxLogWarning(_(" Project check replaced missing audio data block file(s) with silence."));
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// ORPHAN BLOCKFILES (.au and .auf files that are not in the project.)
|
||||
//
|
||||
FilePaths orphanFilePathArray; // orphan .au and .auf files
|
||||
this->FindOrphanBlockFiles(filePathArray, orphanFilePathArray);
|
||||
|
||||
if ((nResult != FSCKstatus_CLOSE_REQ) && !orphanFilePathArray.empty())
|
||||
{
|
||||
// In auto-recover mode, leave orphan blockfiles alone.
|
||||
// They will be deleted when project is saved the first time.
|
||||
if (bAutoRecoverMode)
|
||||
{
|
||||
wxLogWarning(_(" Project check ignored orphan block file(s). They will be deleted when project is saved."));
|
||||
action = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
wxString msgA =
|
||||
_("Project check of \"%s\" folder \
|
||||
\nfound %d orphan block file(s). These files are \
|
||||
\nunused by this project, but might belong to \
|
||||
other projects. \
|
||||
\nThey are doing no harm and are small.");
|
||||
wxString msg;
|
||||
msg.Printf(msgA, this->projName, (int)orphanFilePathArray.size());
|
||||
|
||||
const wxChar *buttons[] =
|
||||
{_("Continue without deleting; ignore the extra files this session"),
|
||||
_("Close project immediately with no further changes"),
|
||||
_("Delete orphan files (permanent immediately)"),
|
||||
NULL};
|
||||
wxLog::FlushActive(); // MultiDialog has "Show Log..." button, so make sure log is current.
|
||||
action = ShowMultiDialog(msg, _("Warning - Orphan Block File(s)"), buttons);
|
||||
}
|
||||
|
||||
if (action == 1)
|
||||
nResult = FSCKstatus_CLOSE_REQ;
|
||||
// Nothing is done if (action == 0).
|
||||
else if (action == 2)
|
||||
{
|
||||
// FSCKstatus_CHANGED was bogus here.
|
||||
// The files are deleted, so "Undo Project Repair" could not do anything.
|
||||
// Plus they affect none of the valid tracks, so incorrect to mark them changed,
|
||||
// and no need for refresh.
|
||||
// nResult |= FSCKstatus_CHANGED;
|
||||
for ( const auto &orphan : orphanFilePathArray )
|
||||
wxRemoveFile(orphan);
|
||||
}
|
||||
}
|
||||
|
||||
if ((nResult != FSCKstatus_CLOSE_REQ) && !ODManager::HasLoadedODFlag())
|
||||
{
|
||||
// Remove any empty directories.
|
||||
ProgressDialog pProgress
|
||||
(_("Progress"),
|
||||
_("Cleaning up unused directories in project data"));
|
||||
// nDirCount is for updating pProgress. +1 because we may DELETE dirPath.
|
||||
int nDirCount = RecursivelyCountSubdirs(dirPath) + 1;
|
||||
RecursivelyRemoveEmptyDirs(dirPath, nDirCount, &pProgress);
|
||||
}
|
||||
|
||||
// Summarize and flush the log.
|
||||
if (bForceError ||
|
||||
!missingAliasFilesAUFHash.empty() ||
|
||||
!missingAUFHash.empty() ||
|
||||
!missingAUHash.empty() ||
|
||||
!orphanFilePathArray.empty())
|
||||
{
|
||||
wxLogWarning(_("Project check found file inconsistencies inspecting the loaded project data."));
|
||||
wxLog::FlushActive(); // Flush is modal and will clear the log (both desired).
|
||||
|
||||
// In auto-recover mode, we didn't do any ShowMultiDialog calls above, so put up an alert.
|
||||
if (bAutoRecoverMode)
|
||||
::AudacityMessageBox(
|
||||
_("Project check found file inconsistencies during automatic recovery.\n\nSelect 'Show Log...' in the Help menu to see details."),
|
||||
_("Warning: Problems in Automatic Recovery"),
|
||||
wxOK | wxICON_EXCLAMATION);
|
||||
}
|
||||
|
||||
MissingAliasFilesDialog::SetShouldShow(true);
|
||||
return nResult;
|
||||
}
|
||||
|
||||
void DirManager::FindMissingAliasFiles(
|
||||
BlockHash& missingAliasFilesAUFHash, // output: (.auf) AliasBlockFiles whose aliased files are missing
|
||||
BlockHash& missingAliasFilesPathHash) // output: full paths of missing aliased files
|
||||
|
@ -19,10 +19,7 @@
|
||||
class wxFileNameWrapper;
|
||||
class BlockArray;
|
||||
class BlockFile;
|
||||
|
||||
#define FSCKstatus_CLOSE_REQ 0x1
|
||||
#define FSCKstatus_CHANGED 0x2
|
||||
#define FSCKstatus_SAVE_AUP 0x4 // used in combination with FSCKstatus_CHANGED
|
||||
class ProgressDialog;
|
||||
|
||||
using DirHash = std::unordered_map<int, int>;
|
||||
|
||||
@ -44,11 +41,39 @@ enum {
|
||||
class PROFILE_DLL_API DirManager final : public XMLTagHandler {
|
||||
public:
|
||||
|
||||
static int RecursivelyEnumerate(const FilePath &dirPath,
|
||||
FilePaths& filePathArray, // output: all files in dirPath tree
|
||||
wxString dirspec,
|
||||
wxString filespec,
|
||||
bool bFiles, bool bDirs,
|
||||
int progress_count = 0,
|
||||
int progress_bias = 0,
|
||||
ProgressDialog* progress = nullptr);
|
||||
|
||||
static int RecursivelyEnumerateWithProgress(const FilePath &dirPath,
|
||||
FilePaths& filePathArray, // output: all files in dirPath tree
|
||||
wxString dirspec,
|
||||
wxString filespec,
|
||||
bool bFiles, bool bDirs,
|
||||
int progress_count,
|
||||
const wxChar* message);
|
||||
|
||||
static int RecursivelyCountSubdirs( const FilePath &dirPath );
|
||||
|
||||
static int RecursivelyRemoveEmptyDirs(const FilePath &dirPath,
|
||||
int nDirCount = 0,
|
||||
ProgressDialog* pProgress = nullptr);
|
||||
|
||||
static void RecursivelyRemove(const FilePaths& filePathArray, int count, int bias,
|
||||
int flags, const wxChar* message = nullptr);
|
||||
|
||||
// MM: Construct DirManager
|
||||
DirManager();
|
||||
|
||||
virtual ~DirManager();
|
||||
|
||||
size_t NumBlockFiles() const { return mBlockFileHash.size(); }
|
||||
|
||||
static void SetTempDir(const wxString &_temp) { globaltemp = _temp; }
|
||||
|
||||
class ProjectSetter
|
||||
@ -152,13 +177,6 @@ class PROFILE_DLL_API DirManager final : public XMLTagHandler {
|
||||
const wxString &msg,
|
||||
int flags = 0);
|
||||
|
||||
// Check the project for errors and possibly prompt user
|
||||
// bForceError: Always show log error alert even if no errors are found here.
|
||||
// Important when you know that there are already errors in the log.
|
||||
// bAutoRecoverMode: Do not show any option dialogs for how to deal with errors found here.
|
||||
// Too complicated during auto-recover. Just correct problems the "safest" way.
|
||||
int ProjectFSCK(const bool bForceError, const bool bAutoRecoverMode);
|
||||
|
||||
void FindMissingAliasFiles(
|
||||
BlockHash& missingAliasFilesAUFHash, // output: (.auf) AliasBlockFiles whose aliased files are missing
|
||||
BlockHash& missingAliasFilesPathHash); // output: full paths of missing aliased files
|
||||
|
@ -116,6 +116,7 @@ scroll information. It also has some status flags.
|
||||
#include "Mix.h"
|
||||
#include "NoteTrack.h"
|
||||
#include "Prefs.h"
|
||||
#include "ProjectFSCK.h"
|
||||
#include "Sequence.h"
|
||||
#include "Snap.h"
|
||||
#include "Tags.h"
|
||||
@ -3274,7 +3275,7 @@ void AudacityProject::OpenFile(const FilePath &fileNameArg, bool addtohistory)
|
||||
// at this point mFileName != fileName, because when opening a
|
||||
// recovered file mFileName is faked to point to the original file
|
||||
// which has been recovered, not the one in the auto-save folder.
|
||||
GetDirManager()->ProjectFSCK(err, true); // Correct problems in auto-recover mode.
|
||||
::ProjectFSCK(*GetDirManager(), err, true); // Correct problems in auto-recover mode.
|
||||
|
||||
// PushState calls AutoSave(), so no longer need to do so here.
|
||||
this->PushState(_("Project was recovered"), _("Recover"));
|
||||
@ -3286,7 +3287,7 @@ void AudacityProject::OpenFile(const FilePath &fileNameArg, bool addtohistory)
|
||||
else
|
||||
{
|
||||
// This is a regular project, check it and ask user
|
||||
int status = GetDirManager()->ProjectFSCK(err, false);
|
||||
int status = ::ProjectFSCK(*GetDirManager(), err, false);
|
||||
if (status & FSCKstatus_CLOSE_REQ)
|
||||
{
|
||||
// Vaughan, 2010-08-23: Note this did not do a real close.
|
||||
@ -3318,7 +3319,7 @@ void AudacityProject::OpenFile(const FilePath &fileNameArg, bool addtohistory)
|
||||
|
||||
mTrackPanel->Refresh(true);
|
||||
|
||||
// Vaughan, 2010-08-20: This was bogus, as all the actions in DirManager::ProjectFSCK
|
||||
// Vaughan, 2010-08-20: This was bogus, as all the actions in ProjectFSCK
|
||||
// that return FSCKstatus_CHANGED cannot be undone.
|
||||
// this->PushState(_("Project checker repaired file"), _("Project Repair"));
|
||||
|
||||
|
@ -0,0 +1,391 @@
|
||||
/**********************************************************************
|
||||
|
||||
Audacity: A Digital Audio Editor
|
||||
|
||||
ProjectFSCK.cpp
|
||||
|
||||
A function that performs consistency checks on the tree of block files
|
||||
|
||||
Paul Licameli split this out of DirManager.cpp
|
||||
|
||||
**********************************************************************/
|
||||
|
||||
#include "ProjectFSCK.h"
|
||||
|
||||
#include <wx/log.h>
|
||||
#include <wx/string.h>
|
||||
|
||||
#include "BlockFile.h"
|
||||
#include "DirManager.h"
|
||||
#include "widgets/ErrorDialog.h"
|
||||
#include "Internat.h"
|
||||
#include "MemoryX.h"
|
||||
#include "MissingAliasFileDialog.h"
|
||||
#include "ondemand/ODManager.h"
|
||||
#include "widgets/MultiDialog.h"
|
||||
#include "widgets/ProgressDialog.h"
|
||||
|
||||
// Check the BlockFiles against the disk state.
|
||||
// Missing Blockfile data can be regenerated if possible or replaced with silence.
|
||||
// Orphan blockfiles can be deleted.
|
||||
// Note that even BlockFiles not referenced by the current savefile (but locked
|
||||
// by history) will be reflected in the mBlockFileHash, and that's a
|
||||
// good thing; this is one reason why we use the hash and not the most
|
||||
// recent savefile.
|
||||
int ProjectFSCK(
|
||||
DirManager &dm, const bool bForceError, const bool bAutoRecoverMode)
|
||||
{
|
||||
// In earlier versions of this method, enumerations of errors were
|
||||
// all done in sequence, then the user was prompted for each type of error.
|
||||
// The enumerations are now interleaved with prompting, because, for example,
|
||||
// user choosing to replace missing aliased block files with silence
|
||||
// needs to put in SilentBlockFiles and DELETE the corresponding auf files,
|
||||
// so those would then not be cumulated in missingAUFHash.
|
||||
// We still do the FindX methods outside the conditionals,
|
||||
// so the log always shows all found errors.
|
||||
|
||||
int action; // choice of action for each type of error
|
||||
int nResult = 0;
|
||||
|
||||
if (bForceError && !bAutoRecoverMode)
|
||||
{
|
||||
// TODO: Replace with more user friendly error message?
|
||||
/* i18n-hint: The audacity project file is XML and has 'tags' in it,
|
||||
rather like html tags <something>some stuff</something>.
|
||||
This error message is about the tags that hold the sequence information.
|
||||
The error message is confusing to users in English, and could just say
|
||||
"Found problems with <sequence> when checking project file." */
|
||||
wxString msg = _("Project check read faulty Sequence tags.");
|
||||
const wxChar *buttons[] =
|
||||
{_("Close project immediately with no changes"),
|
||||
_("Continue with repairs noted in log, and check for more errors. This will save the project in its current state, unless you \"Close project immediately\" on further error alerts."),
|
||||
NULL};
|
||||
wxLog::FlushActive(); // MultiDialog has "Show Log..." button, so make sure log is current.
|
||||
action = ShowMultiDialog(msg, _("Warning - Problems Reading Sequence Tags"), buttons);
|
||||
if (action == 0)
|
||||
nResult = FSCKstatus_CLOSE_REQ;
|
||||
else
|
||||
nResult = FSCKstatus_CHANGED | FSCKstatus_SAVE_AUP;
|
||||
}
|
||||
|
||||
FilePaths filePathArray; // *all* files in the project directory/subdirectories
|
||||
auto dirPath = ( dm.GetDataFilesDir() );
|
||||
DirManager::RecursivelyEnumerateWithProgress(
|
||||
dirPath,
|
||||
filePathArray, // output: all files in project directory tree
|
||||
wxEmptyString, // All dirs
|
||||
wxEmptyString, // All files
|
||||
true, false,
|
||||
dm.NumBlockFiles(), // rough guess of how many BlockFiles will be found/processed, for progress
|
||||
_("Inspecting project file data"));
|
||||
|
||||
//
|
||||
// MISSING ALIASED AUDIO FILES
|
||||
//
|
||||
MissingAliasFilesDialog::SetShouldShow(false);
|
||||
BlockHash missingAliasFilesAUFHash; // (.auf) AliasBlockFiles whose aliased files are missing
|
||||
BlockHash missingAliasFilesPathHash; // full paths of missing aliased files
|
||||
dm.FindMissingAliasFiles(missingAliasFilesAUFHash, missingAliasFilesPathHash);
|
||||
|
||||
if ((nResult != FSCKstatus_CLOSE_REQ) && !missingAliasFilesAUFHash.empty())
|
||||
{
|
||||
// In auto-recover mode, we always create silent blocks, and do not ask user.
|
||||
// This makes sure the project is complete next time we open it.
|
||||
if (bAutoRecoverMode)
|
||||
action = 2;
|
||||
else
|
||||
{
|
||||
wxString msgA =
|
||||
_("Project check of \"%s\" folder \
|
||||
\ndetected %lld missing external audio file(s) \
|
||||
\n('aliased files'). There is no way for Audacity \
|
||||
\nto recover these files automatically. \
|
||||
\n\nIf you choose the first or second option below, \
|
||||
\nyou can try to find and restore the missing files \
|
||||
\nto their previous location. \
|
||||
\n\nNote that for the second option, the waveform \
|
||||
\nmay not show silence. \
|
||||
\n\nIf you choose the third option, this will save the \
|
||||
\nproject in its current state, unless you \"Close \
|
||||
\nproject immediately\" on further error alerts.");
|
||||
wxString msg;
|
||||
msg.Printf(msgA, dm.GetProjectName(), (long long) missingAliasFilesPathHash.size());
|
||||
const wxChar *buttons[] =
|
||||
{_("Close project immediately with no changes"),
|
||||
_("Treat missing audio as silence (this session only)"),
|
||||
_("Replace missing audio with silence (permanent immediately)."),
|
||||
NULL};
|
||||
wxLog::FlushActive(); // MultiDialog has "Show Log..." button, so make sure log is current.
|
||||
action = ShowMultiDialog(msg, _("Warning - Missing Aliased File(s)"), buttons);
|
||||
}
|
||||
|
||||
if (action == 0)
|
||||
nResult = FSCKstatus_CLOSE_REQ;
|
||||
else
|
||||
{
|
||||
// LL: A progress dialog should probably be used here
|
||||
BlockHash::iterator iter = missingAliasFilesAUFHash.begin();
|
||||
while (iter != missingAliasFilesAUFHash.end())
|
||||
{
|
||||
// This type cast is safe. We checked that it's an alias block file earlier.
|
||||
BlockFilePtr b = iter->second.lock();
|
||||
wxASSERT(b);
|
||||
if (b) {
|
||||
auto ab = static_cast< AliasBlockFile * > ( &*b );
|
||||
|
||||
if (action == 2)
|
||||
{
|
||||
// silence the blockfiles by yanking the filename
|
||||
// This is done, eventually, in PCMAliasBlockFile::ReadData()
|
||||
// and ODPCMAliasBlockFile::ReadData, in the stack of b->Recover().
|
||||
// There, if the mAliasedFileName is bad, it zeroes the data.
|
||||
wxFileNameWrapper dummy;
|
||||
dummy.Clear();
|
||||
ab->ChangeAliasedFileName(std::move(dummy));
|
||||
|
||||
// If recovery fails for one file, silence it,
|
||||
// and don't try to recover other files but
|
||||
// silence them too. GuardedCall will cause an appropriate
|
||||
// error message for the user.
|
||||
GuardedCall(
|
||||
[&] { ab->Recover(); },
|
||||
[&] (AudacityException*) { action = 1; }
|
||||
);
|
||||
|
||||
nResult = FSCKstatus_CHANGED | FSCKstatus_SAVE_AUP;
|
||||
}
|
||||
|
||||
if (action == 1)
|
||||
// Silence error logging for this block in this session.
|
||||
ab->SilenceAliasLog();
|
||||
}
|
||||
++iter;
|
||||
}
|
||||
if ((action == 2) && bAutoRecoverMode)
|
||||
wxLogWarning(_(" Project check replaced missing aliased file(s) with silence."));
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// MISSING ALIAS (.AUF) AliasBlockFiles
|
||||
//
|
||||
// Alias summary regeneration must happen after checking missing aliased files.
|
||||
//
|
||||
BlockHash missingAUFHash; // missing (.auf) AliasBlockFiles
|
||||
dm.FindMissingAUFs(missingAUFHash);
|
||||
if ((nResult != FSCKstatus_CLOSE_REQ) && !missingAUFHash.empty())
|
||||
{
|
||||
// In auto-recover mode, we just recreate the alias files, and do not ask user.
|
||||
// This makes sure the project is complete next time we open it.
|
||||
if (bAutoRecoverMode)
|
||||
action = 0;
|
||||
else
|
||||
{
|
||||
wxString msgA =
|
||||
_("Project check of \"%s\" folder \
|
||||
\ndetected %lld missing alias (.auf) blockfile(s). \
|
||||
\nAudacity can fully regenerate these files \
|
||||
\nfrom the current audio in the project.");
|
||||
wxString msg;
|
||||
msg.Printf(msgA, dm.GetProjectName(), (long long) missingAUFHash.size());
|
||||
const wxChar *buttons[] = {_("Regenerate alias summary files (safe and recommended)"),
|
||||
_("Fill in silence for missing display data (this session only)"),
|
||||
_("Close project immediately with no further changes"),
|
||||
NULL};
|
||||
wxLog::FlushActive(); // MultiDialog has "Show Log..." button, so make sure log is current.
|
||||
action = ShowMultiDialog(msg, _("Warning - Missing Alias Summary File(s)"), buttons);
|
||||
}
|
||||
|
||||
if (action == 2)
|
||||
nResult = FSCKstatus_CLOSE_REQ;
|
||||
else
|
||||
{
|
||||
// LL: A progress dialog should probably be used here
|
||||
BlockHash::iterator iter = missingAUFHash.begin();
|
||||
while (iter != missingAUFHash.end())
|
||||
{
|
||||
BlockFilePtr b = iter->second.lock();
|
||||
wxASSERT(b);
|
||||
if (b) {
|
||||
if(action==0) {
|
||||
//regenerate from data
|
||||
// If recovery fails for one file, silence it,
|
||||
// and don't try to recover other files but
|
||||
// silence them too. GuardedCall will cause an appropriate
|
||||
// error message for the user.
|
||||
GuardedCall(
|
||||
[&] {
|
||||
b->Recover();
|
||||
nResult |= FSCKstatus_CHANGED;
|
||||
},
|
||||
[&] (AudacityException*) { action = 1; }
|
||||
);
|
||||
}
|
||||
|
||||
if (action==1){
|
||||
// Silence error logging for this block in this session.
|
||||
b->SilenceLog();
|
||||
}
|
||||
}
|
||||
++iter;
|
||||
}
|
||||
if ((action == 0) && bAutoRecoverMode)
|
||||
wxLogWarning(_(" Project check regenerated missing alias summary file(s)."));
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// MISSING (.AU) SimpleBlockFiles
|
||||
//
|
||||
BlockHash missingAUHash; // missing data (.au) blockfiles
|
||||
dm.FindMissingAUs(missingAUHash);
|
||||
if ((nResult != FSCKstatus_CLOSE_REQ) && !missingAUHash.empty())
|
||||
{
|
||||
// In auto-recover mode, we just always create silent blocks.
|
||||
// This makes sure the project is complete next time we open it.
|
||||
if (bAutoRecoverMode)
|
||||
action = 2;
|
||||
else
|
||||
{
|
||||
wxString msgA =
|
||||
_("Project check of \"%s\" folder \
|
||||
\ndetected %lld missing audio data (.au) blockfile(s), \
|
||||
\nprobably due to a bug, system crash, or accidental \
|
||||
\ndeletion. There is no way for Audacity to recover \
|
||||
\nthese missing files automatically. \
|
||||
\n\nIf you choose the first or second option below, \
|
||||
\nyou can try to find and restore the missing files \
|
||||
\nto their previous location. \
|
||||
\n\nNote that for the second option, the waveform \
|
||||
\nmay not show silence.");
|
||||
wxString msg;
|
||||
msg.Printf(msgA, dm.GetProjectName(), (long long) missingAUHash.size());
|
||||
const wxChar *buttons[] =
|
||||
{_("Close project immediately with no further changes"),
|
||||
_("Treat missing audio as silence (this session only)"),
|
||||
_("Replace missing audio with silence (permanent immediately)"),
|
||||
NULL};
|
||||
wxLog::FlushActive(); // MultiDialog has "Show Log..." button, so make sure log is current.
|
||||
action = ShowMultiDialog(msg, _("Warning - Missing Audio Data Block File(s)"), buttons);
|
||||
}
|
||||
|
||||
if (action == 0)
|
||||
nResult = FSCKstatus_CLOSE_REQ;
|
||||
else
|
||||
{
|
||||
// LL: A progress dialog should probably be used here
|
||||
BlockHash::iterator iter = missingAUHash.begin();
|
||||
while (iter != missingAUHash.end())
|
||||
{
|
||||
BlockFilePtr b = iter->second.lock();
|
||||
wxASSERT(b);
|
||||
if (b) {
|
||||
if (action == 2)
|
||||
{
|
||||
//regenerate from data
|
||||
// If recovery fails for one file, silence it,
|
||||
// and don't try to recover other files but
|
||||
// silence them too. GuardedCall will cause an appropriate
|
||||
// error message for the user.
|
||||
GuardedCall(
|
||||
[&] {
|
||||
//regenerate with zeroes
|
||||
b->Recover();
|
||||
nResult |= FSCKstatus_CHANGED;
|
||||
},
|
||||
[&] (AudacityException*) { action = 1; }
|
||||
);
|
||||
}
|
||||
|
||||
if (action == 1)
|
||||
b->SilenceLog();
|
||||
}
|
||||
++iter;
|
||||
}
|
||||
if ((action == 2) && bAutoRecoverMode)
|
||||
wxLogWarning(_(" Project check replaced missing audio data block file(s) with silence."));
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// ORPHAN BLOCKFILES (.au and .auf files that are not in the project.)
|
||||
//
|
||||
FilePaths orphanFilePathArray; // orphan .au and .auf files
|
||||
dm.FindOrphanBlockFiles(filePathArray, orphanFilePathArray);
|
||||
|
||||
if ((nResult != FSCKstatus_CLOSE_REQ) && !orphanFilePathArray.empty())
|
||||
{
|
||||
// In auto-recover mode, leave orphan blockfiles alone.
|
||||
// They will be deleted when project is saved the first time.
|
||||
if (bAutoRecoverMode)
|
||||
{
|
||||
wxLogWarning(_(" Project check ignored orphan block file(s). They will be deleted when project is saved."));
|
||||
action = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
wxString msgA =
|
||||
_("Project check of \"%s\" folder \
|
||||
\nfound %d orphan block file(s). These files are \
|
||||
\nunused by this project, but might belong to \
|
||||
other projects. \
|
||||
\nThey are doing no harm and are small.");
|
||||
wxString msg;
|
||||
msg.Printf(msgA, dm.GetProjectName(), (int)orphanFilePathArray.size());
|
||||
|
||||
const wxChar *buttons[] =
|
||||
{_("Continue without deleting; ignore the extra files this session"),
|
||||
_("Close project immediately with no further changes"),
|
||||
_("Delete orphan files (permanent immediately)"),
|
||||
NULL};
|
||||
wxLog::FlushActive(); // MultiDialog has "Show Log..." button, so make sure log is current.
|
||||
action = ShowMultiDialog(msg, _("Warning - Orphan Block File(s)"), buttons);
|
||||
}
|
||||
|
||||
if (action == 1)
|
||||
nResult = FSCKstatus_CLOSE_REQ;
|
||||
// Nothing is done if (action == 0).
|
||||
else if (action == 2)
|
||||
{
|
||||
// FSCKstatus_CHANGED was bogus here.
|
||||
// The files are deleted, so "Undo Project Repair" could not do anything.
|
||||
// Plus they affect none of the valid tracks, so incorrect to mark them changed,
|
||||
// and no need for refresh.
|
||||
// nResult |= FSCKstatus_CHANGED;
|
||||
for ( const auto &orphan : orphanFilePathArray )
|
||||
wxRemoveFile(orphan);
|
||||
}
|
||||
}
|
||||
|
||||
if ((nResult != FSCKstatus_CLOSE_REQ) && !ODManager::HasLoadedODFlag())
|
||||
{
|
||||
// Remove any empty directories.
|
||||
ProgressDialog pProgress
|
||||
(_("Progress"),
|
||||
_("Cleaning up unused directories in project data"));
|
||||
// nDirCount is for updating pProgress. +1 because we may DELETE dirPath.
|
||||
int nDirCount = DirManager::RecursivelyCountSubdirs(dirPath) + 1;
|
||||
DirManager::RecursivelyRemoveEmptyDirs(dirPath, nDirCount, &pProgress);
|
||||
}
|
||||
|
||||
// Summarize and flush the log.
|
||||
if (bForceError ||
|
||||
!missingAliasFilesAUFHash.empty() ||
|
||||
!missingAUFHash.empty() ||
|
||||
!missingAUHash.empty() ||
|
||||
!orphanFilePathArray.empty())
|
||||
{
|
||||
wxLogWarning(_("Project check found file inconsistencies inspecting the loaded project data."));
|
||||
wxLog::FlushActive(); // Flush is modal and will clear the log (both desired).
|
||||
|
||||
// In auto-recover mode, we didn't do any ShowMultiDialog calls above, so put up an alert.
|
||||
if (bAutoRecoverMode)
|
||||
::AudacityMessageBox(
|
||||
_("Project check found file inconsistencies during automatic recovery.\n\nSelect 'Show Log...' in the Help menu to see details."),
|
||||
_("Warning: Problems in Automatic Recovery"),
|
||||
wxOK | wxICON_EXCLAMATION);
|
||||
}
|
||||
|
||||
MissingAliasFilesDialog::SetShouldShow(true);
|
||||
return nResult;
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
/**********************************************************************
|
||||
|
||||
Audacity: A Digital Audio Editor
|
||||
|
||||
ProjectFSCK.h
|
||||
|
||||
A function that performs consistency checks on the tree of block files
|
||||
|
||||
Paul Licameli split this out of DirManager.h
|
||||
|
||||
**********************************************************************/
|
||||
|
||||
#ifndef __AUDACITY_PROJECT_FSCK__
|
||||
#define __AUDACITY_PROJECT_FSCK__
|
||||
|
||||
class DirManager;
|
||||
|
||||
enum : unsigned {
|
||||
FSCKstatus_CLOSE_REQ = 0x1,
|
||||
FSCKstatus_CHANGED = 0x2,
|
||||
FSCKstatus_SAVE_AUP = 0x4, // used in combination with FSCKstatus_CHANGED
|
||||
};
|
||||
|
||||
// Check the project for errors and possibly prompt user
|
||||
// bForceError: Always show log error alert even if no errors are found here.
|
||||
// Important when you know that there are already errors in the log.
|
||||
// bAutoRecoverMode: Do not show any option dialogs for how to deal with errors found here.
|
||||
// Too complicated during auto-recover. Just correct problems the "safest" way.
|
||||
int ProjectFSCK(
|
||||
DirManager &dm, const bool bForceError, const bool bAutoRecoverMode);
|
||||
|
||||
#endif
|
@ -82,7 +82,7 @@ void LegacyAliasBlockFile::SaveXML(XMLWriter &xmlFile)
|
||||
|
||||
// BuildFromXML methods should always return a BlockFile, not NULL,
|
||||
// even if the result is flawed (e.g., refers to nonexistent file),
|
||||
// as testing will be done in DirManager::ProjectFSCK().
|
||||
// as testing will be done in ProjectFSCK().
|
||||
BlockFilePtr LegacyAliasBlockFile::BuildFromXML(const FilePath &projDir, const wxChar **attrs)
|
||||
{
|
||||
wxFileNameWrapper summaryFileName;
|
||||
|
@ -218,7 +218,7 @@ void LegacyBlockFile::SaveXML(XMLWriter &xmlFile)
|
||||
|
||||
// BuildFromXML methods should always return a BlockFile, not NULL,
|
||||
// even if the result is flawed (e.g., refers to nonexistent file),
|
||||
// as testing will be done in DirManager::ProjectFSCK().
|
||||
// as testing will be done in ProjectFSCK().
|
||||
/// static
|
||||
BlockFilePtr LegacyBlockFile::BuildFromXML(const FilePath &projDir, const wxChar **attrs,
|
||||
size_t len, sampleFormat format)
|
||||
|
@ -237,7 +237,7 @@ void ODDecodeBlockFile::SaveXML(XMLWriter &xmlFile)
|
||||
/// Also schedules the ODDecodeBlockFile for OD loading.
|
||||
// BuildFromXML methods should always return a BlockFile, not NULL,
|
||||
// even if the result is flawed (e.g., refers to nonexistent file),
|
||||
// as testing will be done in DirManager::ProjectFSCK().
|
||||
// as testing will be done in ProjectFSCK().
|
||||
BlockFilePtr ODDecodeBlockFile::BuildFromXML(DirManager &dm, const wxChar **attrs)
|
||||
{
|
||||
wxFileNameWrapper summaryFileName;
|
||||
|
@ -271,7 +271,7 @@ void ODPCMAliasBlockFile::SaveXML(XMLWriter &xmlFile)
|
||||
/// Does not schedule the ODPCMAliasBlockFile for OD loading. Client code must do this.
|
||||
// BuildFromXML methods should always return a BlockFile, not NULL,
|
||||
// even if the result is flawed (e.g., refers to nonexistent file),
|
||||
// as testing will be done in DirManager::ProjectFSCK().
|
||||
// as testing will be done in ProjectFSCK().
|
||||
BlockFilePtr ODPCMAliasBlockFile::BuildFromXML(DirManager &dm, const wxChar **attrs)
|
||||
{
|
||||
wxFileNameWrapper summaryFileName;
|
||||
|
@ -114,7 +114,7 @@ void PCMAliasBlockFile::SaveXML(XMLWriter &xmlFile)
|
||||
|
||||
// BuildFromXML methods should always return a BlockFile, not NULL,
|
||||
// even if the result is flawed (e.g., refers to nonexistent file),
|
||||
// as testing will be done in DirManager::ProjectFSCK().
|
||||
// as testing will be done in ProjectFSCK().
|
||||
BlockFilePtr PCMAliasBlockFile::BuildFromXML(DirManager &dm, const wxChar **attrs)
|
||||
{
|
||||
wxFileNameWrapper summaryFileName;
|
||||
|
@ -53,7 +53,7 @@ void SilentBlockFile::SaveXML(XMLWriter &xmlFile)
|
||||
|
||||
// BuildFromXML methods should always return a BlockFile, not NULL,
|
||||
// even if the result is flawed (e.g., refers to nonexistent file),
|
||||
// as testing will be done in DirManager::ProjectFSCK().
|
||||
// as testing will be done in ProjectFSCK().
|
||||
/// static
|
||||
BlockFilePtr SilentBlockFile::BuildFromXML(DirManager & WXUNUSED(dm), const wxChar **attrs)
|
||||
{
|
||||
|
@ -433,7 +433,7 @@ void SimpleBlockFile::SaveXML(XMLWriter &xmlFile)
|
||||
|
||||
// BuildFromXML methods should always return a BlockFile, not NULL,
|
||||
// even if the result is flawed (e.g., refers to nonexistent file),
|
||||
// as testing will be done in DirManager::ProjectFSCK().
|
||||
// as testing will be done in ProjectFSCK().
|
||||
/// static
|
||||
BlockFilePtr SimpleBlockFile::BuildFromXML(DirManager &dm, const wxChar **attrs)
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user