audacia/src/AutoRecovery.cpp

357 lines
9.7 KiB
C++

/**********************************************************************
Audacity: A Digital Audio Editor
Audacity(R) is copyright (c) 1999-2010 Audacity Team.
License: GPL v2. See License.txt.
AutoRecovery.cpp
*******************************************************************//**
\class AutoRecoveryDialog
\brief The AutoRecoveryDialog prompts the user whether to
recover previous Audacity projects that were closed incorrectly.
*//********************************************************************/
#include "AutoRecovery.h"
#include "Audacity.h"
#include "AudacityApp.h"
#include "FileNames.h"
#include "blockfile/SimpleBlockFile.h"
#include <wx/wxprec.h>
#include <wx/filefn.h>
#include <wx/dir.h>
#include <wx/dialog.h>
#include <wx/app.h>
enum {
ID_RECOVER_ALL = 10000,
ID_RECOVER_NONE,
ID_QUIT_AUDACITY,
ID_FILE_LIST
};
class AutoRecoveryDialog : public wxDialog
{
public:
AutoRecoveryDialog(wxWindow *parent);
private:
void PopulateList();
void PopulateOrExchange(ShuttleGui & S);
void OnQuitAudacity(wxCommandEvent &evt);
void OnRecoverNone(wxCommandEvent &evt);
void OnRecoverAll(wxCommandEvent &evt);
wxListCtrl *mFileList;
public:
DECLARE_EVENT_TABLE()
};
AutoRecoveryDialog::AutoRecoveryDialog(wxWindow *parent) :
wxDialog(parent, -1, _("Automatic Crash Recovery"),
wxDefaultPosition, wxDefaultSize,
wxDEFAULT_DIALOG_STYLE & (~wxCLOSE_BOX)) // no close box
{
ShuttleGui S(this, eIsCreating);
PopulateOrExchange(S);
}
BEGIN_EVENT_TABLE(AutoRecoveryDialog, wxDialog)
EVT_BUTTON(ID_RECOVER_ALL, AutoRecoveryDialog::OnRecoverAll)
EVT_BUTTON(ID_RECOVER_NONE, AutoRecoveryDialog::OnRecoverNone)
EVT_BUTTON(ID_QUIT_AUDACITY, AutoRecoveryDialog::OnQuitAudacity)
END_EVENT_TABLE()
void AutoRecoveryDialog::PopulateOrExchange(ShuttleGui& S)
{
S.SetBorder(5);
S.StartVerticalLay();
{
S.AddVariableText(_("Some projects were not saved properly the last time Audacity was run.\nFortunately, the following projects can automatically be recovered:"), false);
S.StartStatic(_("Recoverable projects"));
{
mFileList = S.Id(ID_FILE_LIST).AddListControlReportMode();
mFileList->InsertColumn(0, _("Name"));
mFileList->SetColumnWidth(0, 220);
PopulateList();
}
S.EndStatic();
S.AddVariableText(_("Recovering a project will not change any files on disk before you save it."), false);
S.StartHorizontalLay(true);
{
S.Id(ID_QUIT_AUDACITY).AddButton(_("Quit Audacity"));
S.Id(ID_RECOVER_NONE).AddButton(_("Do Not Recover"));
S.Id(ID_RECOVER_ALL).AddButton(_("Recover Projects"));
}
S.EndHorizontalLay();
}
S.EndVerticalLay();
Layout();
Fit();
SetMinSize(GetSize());
Center();
}
void AutoRecoveryDialog::PopulateList()
{
mFileList->DeleteAllItems();
wxDir dir(FileNames::AutoSaveDir());
if (!dir.IsOpened())
return;
wxString filename;
int i = 0;
for (bool c = dir.GetFirst(&filename, wxT("*.autosave"), wxDIR_FILES);
c; c = dir.GetNext(&filename))
mFileList->InsertItem(i++, wxFileName(filename).GetName());
mFileList->SetColumnWidth(0, wxLIST_AUTOSIZE);
}
void AutoRecoveryDialog::OnQuitAudacity(wxCommandEvent &evt)
{
EndModal(ID_QUIT_AUDACITY);
}
void AutoRecoveryDialog::OnRecoverNone(wxCommandEvent &evt)
{
int ret = wxMessageBox(
_("Are you sure you don't want to recover any projects?\nThey can't be recovered later."),
_("Confirm?"), wxICON_QUESTION | wxYES_NO | wxNO_DEFAULT, this);
if (ret == wxYES)
EndModal(ID_RECOVER_NONE);
}
void AutoRecoveryDialog::OnRecoverAll(wxCommandEvent &evt)
{
EndModal(ID_RECOVER_ALL);
}
////////////////////////////////////////////////////////////////////////////
static bool HaveFilesToRecover()
{
wxDir dir(FileNames::AutoSaveDir());
if (!dir.IsOpened())
{
wxMessageBox(_("Could not enumerate files in auto save directory."),
_("Error"), wxICON_STOP);
return false;
}
wxString filename;
bool c = dir.GetFirst(&filename, wxT("*.autosave"), wxDIR_FILES);
return c;
}
static bool RemoveAllAutoSaveFiles()
{
wxArrayString files;
wxDir::GetAllFiles(FileNames::AutoSaveDir(), &files,
wxT("*.autosave"), wxDIR_FILES);
for (unsigned int i = 0; i < files.GetCount(); i++)
{
if (!wxRemoveFile(files[i]))
{
// I don't think this error message is actually useful.
// -dmazzoni
//wxMessageBox(wxT("Could not remove auto save file: " + files[i]),
// _("Error"), wxICON_STOP);
return false;
}
}
return true;
}
static bool RecoverAllProjects(AudacityProject** pproj)
{
wxDir dir(FileNames::AutoSaveDir());
if (!dir.IsOpened())
{
wxMessageBox(_("Could not enumerate files in auto save directory."),
_("Error"), wxICON_STOP);
return false;
}
// Open a project window for each auto save file
wxString filename;
AudacityProject* proj = NULL;
wxArrayString files;
wxDir::GetAllFiles(FileNames::AutoSaveDir(), &files,
wxT("*.autosave"), wxDIR_FILES);
for (unsigned int i = 0; i < files.GetCount(); i++)
{
if (*pproj)
{
// Reuse existing project window
proj = *pproj;
*pproj = NULL;
} else
{
// Create new project window
proj = CreateNewAudacityProject();
}
// Open project. When an auto-save file has been opened successfully,
// the opened auto-save file is automatically deleted and a new one
// is created.
proj->OpenFile(files[i], false);
}
return true;
}
bool ShowAutoRecoveryDialogIfNeeded(AudacityProject** pproj,
bool *didRecoverAnything)
{
if (didRecoverAnything)
*didRecoverAnything = false;
if (HaveFilesToRecover())
{
AutoRecoveryDialog dlg(*pproj);
int ret = dlg.ShowModal();
switch (ret)
{
case ID_RECOVER_NONE:
return RemoveAllAutoSaveFiles();
case ID_RECOVER_ALL:
if (didRecoverAnything)
*didRecoverAnything = true;
return RecoverAllProjects(pproj);
default:
// This includes ID_QUIT_AUDACITY
return false;
}
} else
{
// Nothing to recover, move along
return true;
}
}
////////////////////////////////////////////////////////////////////////////
/// Recording recovery handler
RecordingRecoveryHandler::RecordingRecoveryHandler(AudacityProject* proj)
{
mProject = proj;
mChannel = -1;
mNumChannels = -1;
}
bool RecordingRecoveryHandler::HandleXMLTag(const wxChar *tag,
const wxChar **attrs)
{
if (wxStrcmp(tag, wxT("simpleblockfile")) == 0)
{
// Check if we have a valid channel and numchannels
if (mChannel < 0 || mNumChannels < 0 || mChannel >= mNumChannels)
{
// This should only happen if there is a bug
wxASSERT(false);
return false;
}
// We need to find the track and sequence where the blockfile belongs
WaveTrackArray tracks = mProject->GetTracks()->GetWaveTrackArray(false);
int index = tracks.GetCount() - mNumChannels + mChannel;
if (index < 0 || index >= (int)tracks.GetCount())
{
// This should only happen if there is a bug
wxASSERT(false);
return false;
}
WaveTrack* track = tracks.Item(index);
WaveClip* clip = track->GetLastOrCreateClip();
Sequence* seq = clip->GetSequence();
// Load the blockfile from the XML
BlockFile* blockFile = NULL;
DirManager* dirManager = mProject->GetDirManager();
dirManager->SetLoadingFormat(seq->GetSampleFormat());
dirManager->SetLoadingTarget(&blockFile);
if (!dirManager->HandleXMLTag(tag, attrs) || !blockFile)
{
// This should only happen if there is a bug
wxASSERT(false);
return false;
}
seq->AppendBlockFile(blockFile);
clip->UpdateEnvelopeTrackLen();
} else if (wxStrcmp(tag, wxT("recordingrecovery")) == 0)
{
// loop through attrs, which is a null-terminated list of
// attribute-value pairs
long nValue;
while(*attrs)
{
const wxChar *attr = *attrs++;
const wxChar *value = *attrs++;
if (!value)
break;
const wxString strValue = value;
//this channels value does not correspond to WaveTrack::Left/Right/Mono, but which channel of the recording device
//it came from, and thus we can't use XMLValueChecker::IsValidChannel on it. Rather we compare to the next attribute value.
if (wxStrcmp(attr, wxT("channel")) == 0)
{
if (!XMLValueChecker::IsGoodInt(strValue) || !strValue.ToLong(&nValue) || nValue < 0)
return false;
mChannel = nValue;
}
else if (wxStrcmp(attr, wxT("numchannels")) == 0)
{
if (!XMLValueChecker::IsGoodInt(strValue) || !strValue.ToLong(&nValue) ||
(nValue < 1))
return false;
if(mChannel >= nValue )
return false;
mNumChannels = nValue;
}
}
}
return true;
}
XMLTagHandler* RecordingRecoveryHandler::HandleXMLChild(const wxChar *tag)
{
if (wxStrcmp(tag, wxT("simpleblockfile")) == 0)
return this; // HandleXMLTag also handles <simpleblockfile>
return NULL;
}
// Indentation settings for Vim and Emacs.
// Please do not modify past this point.
//
// Local Variables:
// c-basic-offset: 3
// indent-tabs-mode: nil
// End:
//
// vim: et sts=3 sw=3