mirror of
https://github.com/cookiengineer/audacity
synced 2025-08-02 08:59:28 +02:00
AUP3: AutoRecoveryDialog experiment
Looking for feedback...
This commit is contained in:
parent
a3fcd611b5
commit
a1e83c141a
98
src/ActiveProjects.cpp
Normal file
98
src/ActiveProjects.cpp
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
/**********************************************************************
|
||||||
|
|
||||||
|
Audacity: A Digital Audio Editor
|
||||||
|
|
||||||
|
ActiveProjects.cpp
|
||||||
|
|
||||||
|
********************************************************************//**
|
||||||
|
|
||||||
|
\class ActiveProjects
|
||||||
|
\brief Manages a list of active projects
|
||||||
|
|
||||||
|
*//********************************************************************/
|
||||||
|
|
||||||
|
#include "Audacity.h"
|
||||||
|
#include "ActiveProjects.h"
|
||||||
|
#include "prefs.h"
|
||||||
|
|
||||||
|
#include <wx/filename.h>
|
||||||
|
|
||||||
|
FilePaths ActiveProjects::GetAll()
|
||||||
|
{
|
||||||
|
FilePaths files;
|
||||||
|
|
||||||
|
wxString key;
|
||||||
|
long ndx;
|
||||||
|
|
||||||
|
wxString configPath = gPrefs->GetPath();
|
||||||
|
gPrefs->SetPath(wxT("/ActiveProjects"));
|
||||||
|
|
||||||
|
bool more = gPrefs->GetFirstEntry(key, ndx);
|
||||||
|
while (more)
|
||||||
|
{
|
||||||
|
wxFileName path = gPrefs->Read(key, wxT(""));
|
||||||
|
|
||||||
|
files.Add(path.GetFullPath());
|
||||||
|
|
||||||
|
more = gPrefs->GetNextEntry(key, ndx);
|
||||||
|
}
|
||||||
|
gPrefs->SetPath(configPath);
|
||||||
|
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActiveProjects::Add(const FilePath &path)
|
||||||
|
{
|
||||||
|
wxString key = Find(path);
|
||||||
|
|
||||||
|
if (key.empty())
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
key.Printf(wxT("/ActiveProjects/%d"), ++i);
|
||||||
|
} while (gPrefs->HasEntry(key));
|
||||||
|
|
||||||
|
gPrefs->Write(key, path);
|
||||||
|
gPrefs->Flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActiveProjects::Remove(const FilePath &path)
|
||||||
|
{
|
||||||
|
wxString key = Find(path);
|
||||||
|
|
||||||
|
if (!key.empty())
|
||||||
|
{
|
||||||
|
gPrefs->DeleteEntry(wxT("/ActiveProjects/" + key));
|
||||||
|
gPrefs->Flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wxString ActiveProjects::Find(const FilePath &path)
|
||||||
|
{
|
||||||
|
bool found = false;
|
||||||
|
|
||||||
|
wxString key;
|
||||||
|
long ndx;
|
||||||
|
|
||||||
|
wxString configPath = gPrefs->GetPath();
|
||||||
|
gPrefs->SetPath(wxT("/ActiveProjects"));
|
||||||
|
|
||||||
|
bool more = gPrefs->GetFirstEntry(key, ndx);
|
||||||
|
while (more)
|
||||||
|
{
|
||||||
|
if (gPrefs->Read(key, wxT("")).IsSameAs(path))
|
||||||
|
{
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
more = gPrefs->GetNextEntry(key, ndx);
|
||||||
|
}
|
||||||
|
|
||||||
|
gPrefs->SetPath(configPath);
|
||||||
|
|
||||||
|
return found ? key : wxT("");
|
||||||
|
}
|
||||||
|
|
25
src/ActiveProjects.h
Normal file
25
src/ActiveProjects.h
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/**********************************************************************
|
||||||
|
|
||||||
|
Audacity: A Digital Audio Editor
|
||||||
|
|
||||||
|
ActiveProjects.h
|
||||||
|
|
||||||
|
**********************************************************************/
|
||||||
|
|
||||||
|
#ifndef __AUDACITY_ACTIVE_PROJECTS__
|
||||||
|
#define __AUDACITY_ACTIVE_PROJECTS__
|
||||||
|
|
||||||
|
#include "Audacity.h"
|
||||||
|
#include "audacity/Types.h"
|
||||||
|
|
||||||
|
#include <wx/string.h>
|
||||||
|
|
||||||
|
namespace ActiveProjects
|
||||||
|
{
|
||||||
|
FilePaths GetAll();
|
||||||
|
void Add(const FilePath &path);
|
||||||
|
void Remove(const FilePath &path);
|
||||||
|
wxString Find(const FilePath &path);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
@ -10,60 +10,76 @@ Paul Licameli split from AutoRecovery.cpp
|
|||||||
|
|
||||||
#include "AutoRecoveryDialog.h"
|
#include "AutoRecoveryDialog.h"
|
||||||
|
|
||||||
|
#include "ActiveProjects.h"
|
||||||
#include "FileNames.h"
|
#include "FileNames.h"
|
||||||
#include "ProjectManager.h"
|
#include "ProjectManager.h"
|
||||||
#include "ShuttleGui.h"
|
#include "ShuttleGui.h"
|
||||||
#include "widgets/AudacityMessageBox.h"
|
#include "widgets/AudacityMessageBox.h"
|
||||||
#include "widgets/wxPanelWrapper.h"
|
#include "widgets/wxPanelWrapper.h"
|
||||||
|
|
||||||
|
#include <wx/dir.h>
|
||||||
#include <wx/evtloop.h>
|
#include <wx/evtloop.h>
|
||||||
#include <wx/filefn.h>
|
#include <wx/filefn.h>
|
||||||
#include <wx/filename.h>
|
#include <wx/filename.h>
|
||||||
#include <wx/listctrl.h>
|
#include <wx/listctrl.h>
|
||||||
|
|
||||||
|
#define USE_CHECKBOXES
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
ID_RECOVER_ALL = 10000,
|
ID_QUIT_AUDACITY = 10000,
|
||||||
ID_RECOVER_NONE,
|
ID_DISCARD_SELECTED,
|
||||||
ID_QUIT_AUDACITY,
|
ID_RECOVER_SELECTED,
|
||||||
ID_FILE_LIST
|
ID_FILE_LIST
|
||||||
};
|
};
|
||||||
|
|
||||||
class AutoRecoveryDialog final : public wxDialogWrapper
|
class AutoRecoveryDialog final : public wxDialogWrapper
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
AutoRecoveryDialog(const FilePaths &files);
|
AutoRecoveryDialog();
|
||||||
|
|
||||||
|
bool HasRecoverables() const;
|
||||||
|
FilePaths GetRecoverables();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void PopulateOrExchange(ShuttleGui &S);
|
void PopulateOrExchange(ShuttleGui &S);
|
||||||
void PopulateList();
|
void PopulateList();
|
||||||
|
|
||||||
void OnQuitAudacity(wxCommandEvent &evt);
|
void OnQuitAudacity(wxCommandEvent &evt);
|
||||||
void OnRecoverNone(wxCommandEvent &evt);
|
void OnDiscardSelected(wxCommandEvent &evt);
|
||||||
void OnRecoverAll(wxCommandEvent &evt);
|
void OnRecoverSelected(wxCommandEvent &evt);
|
||||||
|
|
||||||
const FilePaths & mFiles;
|
FilePaths mFiles;
|
||||||
wxListCtrl *mFileList;
|
wxListCtrl *mFileList;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DECLARE_EVENT_TABLE()
|
DECLARE_EVENT_TABLE()
|
||||||
};
|
};
|
||||||
|
|
||||||
AutoRecoveryDialog::AutoRecoveryDialog(const FilePaths &files)
|
BEGIN_EVENT_TABLE(AutoRecoveryDialog, wxDialogWrapper)
|
||||||
: wxDialogWrapper(nullptr, -1, XO("Automatic Crash Recovery"),
|
EVT_BUTTON(ID_QUIT_AUDACITY, AutoRecoveryDialog::OnQuitAudacity)
|
||||||
|
EVT_BUTTON(ID_DISCARD_SELECTED, AutoRecoveryDialog::OnDiscardSelected)
|
||||||
|
EVT_BUTTON(ID_RECOVER_SELECTED, AutoRecoveryDialog::OnRecoverSelected)
|
||||||
|
END_EVENT_TABLE()
|
||||||
|
|
||||||
|
AutoRecoveryDialog::AutoRecoveryDialog()
|
||||||
|
: wxDialogWrapper(nullptr, wxID_ANY, XO("Automatic Crash Recovery"),
|
||||||
wxDefaultPosition, wxDefaultSize,
|
wxDefaultPosition, wxDefaultSize,
|
||||||
wxDEFAULT_DIALOG_STYLE & (~wxCLOSE_BOX)), // no close box
|
wxDEFAULT_DIALOG_STYLE & (~wxCLOSE_BOX)) // no close box
|
||||||
mFiles(files)
|
|
||||||
{
|
{
|
||||||
SetName();
|
SetName();
|
||||||
ShuttleGui S(this, eIsCreating);
|
ShuttleGui S(this, eIsCreating);
|
||||||
PopulateOrExchange(S);
|
PopulateOrExchange(S);
|
||||||
}
|
}
|
||||||
|
|
||||||
BEGIN_EVENT_TABLE(AutoRecoveryDialog, wxDialogWrapper)
|
bool AutoRecoveryDialog::HasRecoverables() const
|
||||||
EVT_BUTTON(ID_RECOVER_ALL, AutoRecoveryDialog::OnRecoverAll)
|
{
|
||||||
EVT_BUTTON(ID_RECOVER_NONE, AutoRecoveryDialog::OnRecoverNone)
|
return mFiles.size() > 0;
|
||||||
EVT_BUTTON(ID_QUIT_AUDACITY, AutoRecoveryDialog::OnQuitAudacity)
|
}
|
||||||
END_EVENT_TABLE()
|
|
||||||
|
FilePaths AutoRecoveryDialog::GetRecoverables()
|
||||||
|
{
|
||||||
|
return mFiles;
|
||||||
|
}
|
||||||
|
|
||||||
void AutoRecoveryDialog::PopulateOrExchange(ShuttleGui &S)
|
void AutoRecoveryDialog::PopulateOrExchange(ShuttleGui &S)
|
||||||
{
|
{
|
||||||
@ -76,9 +92,18 @@ void AutoRecoveryDialog::PopulateOrExchange(ShuttleGui &S)
|
|||||||
|
|
||||||
S.StartStatic(XO("Recoverable projects"));
|
S.StartStatic(XO("Recoverable projects"));
|
||||||
{
|
{
|
||||||
mFileList = S.Id(ID_FILE_LIST)
|
mFileList = S.Id(ID_FILE_LIST).AddListControlReportMode(
|
||||||
|
{
|
||||||
|
#if defined(USE_CHECKBOXES)
|
||||||
|
/*i18n-hint: (verb). It instruct the user to select items.*/
|
||||||
|
XO("Select"),
|
||||||
|
#endif
|
||||||
/*i18n-hint: (noun). It's the name of the project to recover.*/
|
/*i18n-hint: (noun). It's the name of the project to recover.*/
|
||||||
.AddListControlReportMode( { XO("Name") } );
|
XO("Name")
|
||||||
|
});
|
||||||
|
#if defined(USE_CHECKBOXES)
|
||||||
|
mFileList->EnableCheckBoxes();
|
||||||
|
#endif
|
||||||
PopulateList();
|
PopulateList();
|
||||||
}
|
}
|
||||||
S.EndStatic();
|
S.EndStatic();
|
||||||
@ -90,8 +115,8 @@ void AutoRecoveryDialog::PopulateOrExchange(ShuttleGui &S)
|
|||||||
S.StartHorizontalLay();
|
S.StartHorizontalLay();
|
||||||
{
|
{
|
||||||
S.Id(ID_QUIT_AUDACITY).AddButton(XXO("Quit Audacity"));
|
S.Id(ID_QUIT_AUDACITY).AddButton(XXO("Quit Audacity"));
|
||||||
S.Id(ID_RECOVER_NONE).AddButton(XXO("Discard Projects"));
|
S.Id(ID_DISCARD_SELECTED).AddButton(XXO("Discard Selected"));
|
||||||
S.Id(ID_RECOVER_ALL).AddButton(XXO("Recover Projects"));
|
S.Id(ID_RECOVER_SELECTED).AddButton(XXO("Recover Selected"));
|
||||||
}
|
}
|
||||||
S.EndHorizontalLay();
|
S.EndHorizontalLay();
|
||||||
}
|
}
|
||||||
@ -109,14 +134,46 @@ void AutoRecoveryDialog::PopulateOrExchange(ShuttleGui &S)
|
|||||||
|
|
||||||
void AutoRecoveryDialog::PopulateList()
|
void AutoRecoveryDialog::PopulateList()
|
||||||
{
|
{
|
||||||
|
wxString tempdir = FileNames::TempDir();
|
||||||
|
wxString pattern = wxT("*.") + FileNames::UnsavedProjectExtension();
|
||||||
|
FilePaths files;
|
||||||
|
|
||||||
|
wxDir::GetAllFiles(tempdir, &files, pattern, wxDIR_FILES);
|
||||||
|
|
||||||
|
FilePaths active = ActiveProjects::GetAll();
|
||||||
|
|
||||||
mFileList->DeleteAllItems();
|
mFileList->DeleteAllItems();
|
||||||
|
|
||||||
for (int i = 0, cnt = mFiles.size(); i < cnt; ++i)
|
long item = 0;
|
||||||
|
for (auto file : active)
|
||||||
{
|
{
|
||||||
mFileList->InsertItem(i, wxFileName{ mFiles[i] }.GetName());
|
wxFileName fn = file;
|
||||||
|
if (fn.FileExists())
|
||||||
|
{
|
||||||
|
FilePath fullPath = fn.GetFullPath();
|
||||||
|
if (files.Index(fullPath) == wxNOT_FOUND)
|
||||||
|
{
|
||||||
|
files.push_back(fullPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (auto file : files)
|
||||||
|
{
|
||||||
|
wxFileName fn = file;
|
||||||
|
|
||||||
|
mFiles.push_back(fn.GetFullPath());
|
||||||
|
mFileList->InsertItem(item, wxT(""));
|
||||||
|
mFileList->SetItem(item, 1, fn.GetName());
|
||||||
|
item++;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(USE_CHECKBOXES)
|
||||||
|
mFileList->SetColumnWidth(0, wxLIST_AUTOSIZE_USEHEADER);
|
||||||
|
mFileList->SetColumnWidth(1, wxLIST_AUTOSIZE);
|
||||||
|
#else
|
||||||
mFileList->SetColumnWidth(0, wxLIST_AUTOSIZE);
|
mFileList->SetColumnWidth(0, wxLIST_AUTOSIZE);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void AutoRecoveryDialog::OnQuitAudacity(wxCommandEvent & WXUNUSED(event))
|
void AutoRecoveryDialog::OnQuitAudacity(wxCommandEvent & WXUNUSED(event))
|
||||||
@ -124,40 +181,41 @@ void AutoRecoveryDialog::OnQuitAudacity(wxCommandEvent & WXUNUSED(event))
|
|||||||
EndModal(ID_QUIT_AUDACITY);
|
EndModal(ID_QUIT_AUDACITY);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AutoRecoveryDialog::OnRecoverNone(wxCommandEvent & WXUNUSED(event))
|
void AutoRecoveryDialog::OnDiscardSelected(wxCommandEvent & WXUNUSED(event))
|
||||||
{
|
{
|
||||||
int ret = AudacityMessageBox(
|
int ret = AudacityMessageBox(
|
||||||
XO("Are you sure you want to discard all recoverable projects?\n\nChoosing \"Yes\" discards all recoverable projects immediately."),
|
XO("Are you sure you want to discard the selected projects?\n\n"
|
||||||
|
"Choosing \"Yes\" permanently deletes the selected projects immediately."),
|
||||||
XO("Confirm Discard Projects"),
|
XO("Confirm Discard Projects"),
|
||||||
wxICON_QUESTION | wxYES_NO | wxNO_DEFAULT, this);
|
wxICON_QUESTION | wxYES_NO | wxNO_DEFAULT, this);
|
||||||
|
|
||||||
if (ret == wxYES)
|
if (ret == wxNO)
|
||||||
EndModal(ID_RECOVER_NONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AutoRecoveryDialog::OnRecoverAll(wxCommandEvent & WXUNUSED(event))
|
|
||||||
{
|
|
||||||
EndModal(ID_RECOVER_ALL);
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
static FilePaths HaveFilesToRecover()
|
|
||||||
{
|
|
||||||
wxString tempdir = FileNames::TempDir();
|
|
||||||
wxString pattern = wxT("*.") + FileNames::UnsavedProjectExtension();
|
|
||||||
FilePaths files;
|
|
||||||
|
|
||||||
wxDir::GetAllFiles(tempdir, &files, pattern, wxDIR_FILES);
|
|
||||||
|
|
||||||
return files;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool RemoveAllAutoSaveFiles(const FilePaths &files)
|
|
||||||
{
|
|
||||||
for (int i = 0, cnt = files.size(); i < cnt; ++i)
|
|
||||||
{
|
{
|
||||||
FilePath file = files[i];
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define USE_CHECKBOXES
|
||||||
|
#if defined(USE_CHECKBOXES)
|
||||||
|
#define state wxLIST_STATE_DONTCARE
|
||||||
|
#else
|
||||||
|
#define state wxLIST_STATE_SELECTED
|
||||||
|
#endif
|
||||||
|
|
||||||
|
long item = -1;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
item = mFileList->GetNextItem(item, wxLIST_NEXT_ALL, state);
|
||||||
|
if (item == wxNOT_FOUND)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#if defined(USE_CHECKBOXES)
|
||||||
|
if (!mFileList->IsItemChecked(item))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
FilePath file = mFiles[item];
|
||||||
|
|
||||||
if (wxRemoveFile(file))
|
if (wxRemoveFile(file))
|
||||||
{
|
{
|
||||||
@ -175,12 +233,55 @@ static bool RemoveAllAutoSaveFiles(const FilePaths &files)
|
|||||||
{
|
{
|
||||||
wxRemoveFile(file + wxT("-journal"));
|
wxRemoveFile(file + wxT("-journal"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ActiveProjects::Remove(file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
PopulateList();
|
||||||
|
|
||||||
|
if (mFileList->GetItemCount() == 0)
|
||||||
|
{
|
||||||
|
EndModal(ID_DISCARD_SELECTED);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AutoRecoveryDialog::OnRecoverSelected(wxCommandEvent & WXUNUSED(event))
|
||||||
|
{
|
||||||
|
#define USE_CHECKBOXES
|
||||||
|
#if defined(USE_CHECKBOXES)
|
||||||
|
#define state wxLIST_STATE_DONTCARE
|
||||||
|
#else
|
||||||
|
#define state wxLIST_STATE_SELECTED
|
||||||
|
#endif
|
||||||
|
|
||||||
|
FilePaths files;
|
||||||
|
|
||||||
|
long item = -1;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
item = mFileList->GetNextItem(item, wxLIST_NEXT_ALL, state);
|
||||||
|
if (item == wxNOT_FOUND)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#if defined(USE_CHECKBOXES)
|
||||||
|
if (!mFileList->IsItemChecked(item))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
files.push_back(mFiles[item]);
|
||||||
|
}
|
||||||
|
|
||||||
|
mFiles = files;
|
||||||
|
|
||||||
|
EndModal(ID_RECOVER_SELECTED);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
static bool RecoverAllProjects(const FilePaths &files,
|
static bool RecoverAllProjects(const FilePaths &files,
|
||||||
AudacityProject **pproj)
|
AudacityProject **pproj)
|
||||||
{
|
{
|
||||||
@ -197,24 +298,27 @@ static bool RecoverAllProjects(const FilePaths &files,
|
|||||||
*pproj = NULL;
|
*pproj = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open project. When an auto-save file has been opened successfully,
|
// Open project.
|
||||||
// the opened auto-save file is automatically deleted and a NEW one
|
if (ProjectManager::OpenProject(proj, files[i], false) == nullptr)
|
||||||
// is created.
|
{
|
||||||
(void) ProjectManager::OpenProject(proj, files[i], false);
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ActiveProjects::Remove(files[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ShowAutoRecoveryDialogIfNeeded(AudacityProject **pproj,
|
bool ShowAutoRecoveryDialogIfNeeded(AudacityProject **pproj, bool *didRecoverAnything)
|
||||||
bool *didRecoverAnything)
|
|
||||||
{
|
{
|
||||||
if (didRecoverAnything)
|
if (didRecoverAnything)
|
||||||
*didRecoverAnything = false;
|
|
||||||
|
|
||||||
FilePaths files = HaveFilesToRecover();
|
|
||||||
if (files.size())
|
|
||||||
{
|
{
|
||||||
|
*didRecoverAnything = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool success = true;
|
||||||
|
|
||||||
// Under wxGTK3, the auto recovery dialog will not get
|
// Under wxGTK3, the auto recovery dialog will not get
|
||||||
// the focus since the project window hasn't been allowed
|
// the focus since the project window hasn't been allowed
|
||||||
// to completely initialize.
|
// to completely initialize.
|
||||||
@ -229,25 +333,34 @@ bool ShowAutoRecoveryDialogIfNeeded(AudacityProject **pproj,
|
|||||||
// 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);
|
||||||
|
|
||||||
int ret = AutoRecoveryDialog(files).ShowModal();
|
AutoRecoveryDialog dialog;
|
||||||
|
|
||||||
|
if (dialog.HasRecoverables())
|
||||||
|
{
|
||||||
|
int ret = dialog.ShowModal();
|
||||||
|
|
||||||
switch (ret)
|
switch (ret)
|
||||||
{
|
{
|
||||||
case ID_RECOVER_NONE:
|
case ID_DISCARD_SELECTED:
|
||||||
return RemoveAllAutoSaveFiles(files);
|
success = true;
|
||||||
|
break;
|
||||||
|
|
||||||
case ID_RECOVER_ALL:
|
case ID_RECOVER_SELECTED:
|
||||||
|
success = RecoverAllProjects(dialog.GetRecoverables(), pproj);
|
||||||
|
if (success)
|
||||||
|
{
|
||||||
if (didRecoverAnything)
|
if (didRecoverAnything)
|
||||||
|
{
|
||||||
*didRecoverAnything = true;
|
*didRecoverAnything = true;
|
||||||
return RecoverAllProjects(files, pproj);
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// This includes ID_QUIT_AUDACITY
|
// This includes ID_QUIT_AUDACITY
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else
|
|
||||||
{
|
|
||||||
// Nothing to recover, move along
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
}
|
}
|
||||||
|
@ -63,6 +63,8 @@ list( APPEND SOURCES
|
|||||||
PRIVATE
|
PRIVATE
|
||||||
AColor.cpp
|
AColor.cpp
|
||||||
AColor.h
|
AColor.h
|
||||||
|
ActiveProjects.cpp
|
||||||
|
ActiveProjects.h
|
||||||
AboutDialog.cpp
|
AboutDialog.cpp
|
||||||
AboutDialog.h
|
AboutDialog.h
|
||||||
AdornedRulerPanel.cpp
|
AdornedRulerPanel.cpp
|
||||||
|
@ -18,6 +18,7 @@ Paul Licameli split from AudacityProject.cpp
|
|||||||
#include <wx/sstream.h>
|
#include <wx/sstream.h>
|
||||||
#include <wx/xml/xml.h>
|
#include <wx/xml/xml.h>
|
||||||
|
|
||||||
|
#include "ActiveProjects.h"
|
||||||
#include "DBConnection.h"
|
#include "DBConnection.h"
|
||||||
#include "FileNames.h"
|
#include "FileNames.h"
|
||||||
#include "Project.h"
|
#include "Project.h"
|
||||||
@ -1178,8 +1179,18 @@ void ProjectFileIO::SetFileName(const FilePath &fileName)
|
|||||||
{
|
{
|
||||||
auto &project = mProject;
|
auto &project = mProject;
|
||||||
|
|
||||||
|
if (!mFileName.empty())
|
||||||
|
{
|
||||||
|
ActiveProjects::Remove(mFileName);
|
||||||
|
}
|
||||||
|
|
||||||
mFileName = fileName;
|
mFileName = fileName;
|
||||||
|
|
||||||
|
if (!mFileName.empty())
|
||||||
|
{
|
||||||
|
ActiveProjects::Add(mFileName);
|
||||||
|
}
|
||||||
|
|
||||||
if (mTemporary)
|
if (mTemporary)
|
||||||
{
|
{
|
||||||
project.SetProjectName({});
|
project.SetProjectName({});
|
||||||
|
2513
src/ProjectFileIO.cpp.bkp.cpp
Normal file
2513
src/ProjectFileIO.cpp.bkp.cpp
Normal file
File diff suppressed because it is too large
Load Diff
278
src/ProjectFileIO.h.bkp.h
Normal file
278
src/ProjectFileIO.h.bkp.h
Normal file
@ -0,0 +1,278 @@
|
|||||||
|
/**********************************************************************
|
||||||
|
|
||||||
|
Audacity: A Digital Audio Editor
|
||||||
|
|
||||||
|
ProjectFileIO.h
|
||||||
|
|
||||||
|
Paul Licameli split from AudacityProject.h
|
||||||
|
|
||||||
|
**********************************************************************/
|
||||||
|
|
||||||
|
#ifndef __AUDACITY_PROJECT_FILE_IO__
|
||||||
|
#define __AUDACITY_PROJECT_FILE_IO__
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
#include <thread>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
#include "ClientData.h" // to inherit
|
||||||
|
#include "Prefs.h" // to inherit
|
||||||
|
#include "xml/XMLTagHandler.h" // to inherit
|
||||||
|
|
||||||
|
struct sqlite3;
|
||||||
|
struct sqlite3_context;
|
||||||
|
struct sqlite3_value;
|
||||||
|
|
||||||
|
class AudacityProject;
|
||||||
|
class AutoCommitTransaction;
|
||||||
|
class ProjectSerializer;
|
||||||
|
class SqliteSampleBlock;
|
||||||
|
class TrackList;
|
||||||
|
class WaveTrack;
|
||||||
|
|
||||||
|
using WaveTrackArray = std::vector < std::shared_ptr < WaveTrack > >;
|
||||||
|
|
||||||
|
// From SampleBlock.h
|
||||||
|
using SampleBlockID = long long;
|
||||||
|
|
||||||
|
///\brief Object associated with a project that manages reading and writing
|
||||||
|
/// of Audacity project file formats, and autosave
|
||||||
|
class ProjectFileIO final
|
||||||
|
: public ClientData::Base
|
||||||
|
, public XMLTagHandler
|
||||||
|
, private PrefsListener
|
||||||
|
, public std::enable_shared_from_this<ProjectFileIO>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// Call this static function once before constructing any instances of this
|
||||||
|
// class. Reinvocations have no effect. Return value is true for success.
|
||||||
|
static bool InitializeSQL();
|
||||||
|
|
||||||
|
static ProjectFileIO &Get( AudacityProject &project );
|
||||||
|
static const ProjectFileIO &Get( const AudacityProject &project );
|
||||||
|
|
||||||
|
explicit ProjectFileIO( AudacityProject &project );
|
||||||
|
// unfortunate two-step construction needed because of
|
||||||
|
// enable_shared_from_this
|
||||||
|
void Init( AudacityProject &project );
|
||||||
|
|
||||||
|
ProjectFileIO( const ProjectFileIO & ) PROHIBITED;
|
||||||
|
ProjectFileIO &operator=( const ProjectFileIO & ) PROHIBITED;
|
||||||
|
~ProjectFileIO();
|
||||||
|
|
||||||
|
// It seems odd to put this method in this class, but the results do depend
|
||||||
|
// on what is discovered while opening the file, such as whether it is a
|
||||||
|
// recovery file
|
||||||
|
void SetProjectTitle(int number = -1);
|
||||||
|
// Should be empty or a fully qualified file name
|
||||||
|
|
||||||
|
const FilePath &GetFileName() const;
|
||||||
|
void SetFileName( const FilePath &fileName );
|
||||||
|
|
||||||
|
bool IsModified() const;
|
||||||
|
bool IsTemporary() const;
|
||||||
|
bool IsRecovered() const;
|
||||||
|
|
||||||
|
void Reset();
|
||||||
|
|
||||||
|
bool AutoSave(bool recording = false);
|
||||||
|
bool AutoSaveDelete(sqlite3 *db = nullptr);
|
||||||
|
|
||||||
|
bool ImportProject(const FilePath &fileName);
|
||||||
|
bool LoadProject(const FilePath &fileName);
|
||||||
|
bool SaveProject(const FilePath &fileName);
|
||||||
|
bool SaveCopy(const FilePath& fileName);
|
||||||
|
bool CloseProject();
|
||||||
|
|
||||||
|
wxLongLong GetFreeDiskSpace();
|
||||||
|
|
||||||
|
const TranslatableString &GetLastError() const;
|
||||||
|
const TranslatableString &GetLibraryError() const;
|
||||||
|
|
||||||
|
// Provides a means to bypass "DELETE"s at shutdown if the database
|
||||||
|
// is just going to be deleted anyway. This prevents a noticable
|
||||||
|
// delay caused by SampleBlocks being deleted when the Sequences that
|
||||||
|
// own them are deleted.
|
||||||
|
//
|
||||||
|
// This is definitely hackage territory. While this ability would
|
||||||
|
// still be needed, I think handling it in a DB abstraction might be
|
||||||
|
// a tad bit cleaner.
|
||||||
|
//
|
||||||
|
// For it's usage, see:
|
||||||
|
// SqliteSampleBlock::~SqliteSampleBlock()
|
||||||
|
// ProjectManager::OnCloseWindow()
|
||||||
|
void SetBypass();
|
||||||
|
bool ShouldBypass();
|
||||||
|
|
||||||
|
// Remove all unused space within a project file
|
||||||
|
void Vacuum(const std::shared_ptr<TrackList> &tracks);
|
||||||
|
|
||||||
|
// The last vacuum check did actually vacuum the project file if true
|
||||||
|
bool WasVacuumed();
|
||||||
|
|
||||||
|
// The last vacuum check found unused blocks in the project file
|
||||||
|
bool HadUnused();
|
||||||
|
|
||||||
|
bool TransactionStart(const wxString &name);
|
||||||
|
bool TransactionCommit(const wxString &name);
|
||||||
|
bool TransactionRollback(const wxString &name);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void WriteXMLHeader(XMLWriter &xmlFile) const;
|
||||||
|
void WriteXML(XMLWriter &xmlFile, bool recording = false, const std::shared_ptr<TrackList> &tracks = nullptr) /* not override */;
|
||||||
|
|
||||||
|
// XMLTagHandler callback methods
|
||||||
|
bool HandleXMLTag(const wxChar *tag, const wxChar **attrs) override;
|
||||||
|
XMLTagHandler *HandleXMLChild(const wxChar *tag) override;
|
||||||
|
|
||||||
|
void UpdatePrefs() override;
|
||||||
|
|
||||||
|
using ExecResult = std::vector<std::vector<wxString>>;
|
||||||
|
using ExecCB = std::function<int(ExecResult &result, int cols, char **vals, char **names)>;
|
||||||
|
struct ExecParm
|
||||||
|
{
|
||||||
|
ExecCB func;
|
||||||
|
ExecResult &result;
|
||||||
|
};
|
||||||
|
static int ExecCallback(void *data, int cols, char **vals, char **names);
|
||||||
|
int Exec(const char *query, ExecCB callback, ExecResult &result);
|
||||||
|
|
||||||
|
// The opening of the database may be delayed until demanded.
|
||||||
|
// Returns a non-null pointer to an open database, or throws an exception
|
||||||
|
// if opening fails.
|
||||||
|
sqlite3 *DB();
|
||||||
|
|
||||||
|
// Put the current database connection aside, keeping it open, so that
|
||||||
|
// another may be opened with OpenDB()
|
||||||
|
void SaveConnection();
|
||||||
|
|
||||||
|
// Close any set-aside connection
|
||||||
|
void DiscardConnection();
|
||||||
|
|
||||||
|
// Close any current connection and switch back to using the saved
|
||||||
|
void RestoreConnection();
|
||||||
|
|
||||||
|
// Use a connection that is already open rather than invoke OpenDB
|
||||||
|
void UseConnection(sqlite3 *db, const FilePath &filePath);
|
||||||
|
|
||||||
|
// Make sure the connection/schema combo is configured the way we want
|
||||||
|
void Config(sqlite3 *db, const char *config, const wxString &schema = wxT("main"));
|
||||||
|
|
||||||
|
sqlite3 *OpenDB(FilePath fileName = {});
|
||||||
|
bool CloseDB();
|
||||||
|
bool DeleteDB();
|
||||||
|
|
||||||
|
bool Query(const char *sql, ExecResult &result);
|
||||||
|
|
||||||
|
bool GetValue(const char *sql, wxString &value);
|
||||||
|
bool GetBlob(const char *sql, wxMemoryBuffer &buffer);
|
||||||
|
|
||||||
|
bool CheckVersion();
|
||||||
|
bool InstallSchema(sqlite3 *db, const char *schema = "main");
|
||||||
|
bool UpgradeSchema();
|
||||||
|
|
||||||
|
// Write project or autosave XML (binary) documents
|
||||||
|
bool WriteDoc(const char *table, const ProjectSerializer &autosave, sqlite3 *db = nullptr);
|
||||||
|
|
||||||
|
// Application defined function to verify blockid exists is in set of blockids
|
||||||
|
using BlockIDs = std::set<SampleBlockID>;
|
||||||
|
static void InSet(sqlite3_context *context, int argc, sqlite3_value **argv);
|
||||||
|
|
||||||
|
// Checks for orphan blocks. This will go away at a future date
|
||||||
|
bool CheckForOrphans(BlockIDs &blockids);
|
||||||
|
|
||||||
|
// Return a database connection if successful, which caller must close
|
||||||
|
sqlite3 *CopyTo(const FilePath &destpath,
|
||||||
|
const TranslatableString &msg,
|
||||||
|
bool prune = false,
|
||||||
|
const std::shared_ptr<TrackList> &tracks = nullptr);
|
||||||
|
|
||||||
|
void SetError(const TranslatableString & msg);
|
||||||
|
void SetDBError(const TranslatableString & msg);
|
||||||
|
|
||||||
|
bool ShouldVacuum(const std::shared_ptr<TrackList> &tracks);
|
||||||
|
|
||||||
|
void CheckpointThread();
|
||||||
|
static int CheckpointHook(void *that, sqlite3 *db, const char *schema, int pages);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// non-static data members
|
||||||
|
std::weak_ptr<AudacityProject> mpProject;
|
||||||
|
|
||||||
|
// The project's file path
|
||||||
|
FilePath mFileName;
|
||||||
|
|
||||||
|
// Has this project been recovered from an auto-saved version
|
||||||
|
bool mRecovered;
|
||||||
|
|
||||||
|
// Has this project been modified
|
||||||
|
bool mModified;
|
||||||
|
|
||||||
|
// Is this project still a temporary/unsaved project
|
||||||
|
bool mTemporary;
|
||||||
|
|
||||||
|
// Bypass transactions if database will be deleted after close
|
||||||
|
bool mBypass;
|
||||||
|
|
||||||
|
// Project was vacuumed last time Vacuum() ran
|
||||||
|
bool mWasVacuumed;
|
||||||
|
|
||||||
|
// Project had unused blocks during last Vacuum()
|
||||||
|
bool mHadUnused;
|
||||||
|
|
||||||
|
sqlite3 *mPrevDB;
|
||||||
|
FilePath mPrevFileName;
|
||||||
|
|
||||||
|
sqlite3 *mDB;
|
||||||
|
TranslatableString mLastError;
|
||||||
|
TranslatableString mLibraryError;
|
||||||
|
|
||||||
|
std::thread mCheckpointThread;
|
||||||
|
std::condition_variable mCheckpointCondition;
|
||||||
|
std::mutex mCheckpointMutex;
|
||||||
|
std::mutex mCheckpointActive;
|
||||||
|
std::mutex mCheckpointClose;
|
||||||
|
std::atomic_bool mCheckpointStop;
|
||||||
|
uint64_t mCheckpointWaitingPages;
|
||||||
|
uint64_t mCheckpointCurrentPages;
|
||||||
|
|
||||||
|
friend SqliteSampleBlock;
|
||||||
|
friend AutoCommitTransaction;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AutoCommitTransaction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AutoCommitTransaction(ProjectFileIO &projectFileIO, const char *name);
|
||||||
|
~AutoCommitTransaction();
|
||||||
|
|
||||||
|
bool Commit();
|
||||||
|
bool Rollback();
|
||||||
|
|
||||||
|
private:
|
||||||
|
ProjectFileIO &mIO;
|
||||||
|
bool mInTrans;
|
||||||
|
wxString mName;
|
||||||
|
};
|
||||||
|
|
||||||
|
class wxTopLevelWindow;
|
||||||
|
|
||||||
|
// TitleRestorer restores project window titles to what they were, in its destructor.
|
||||||
|
class TitleRestorer{
|
||||||
|
public:
|
||||||
|
TitleRestorer( wxTopLevelWindow &window, AudacityProject &project );
|
||||||
|
~TitleRestorer();
|
||||||
|
wxString sProjNumber;
|
||||||
|
wxString sProjName;
|
||||||
|
size_t UnnamedCount;
|
||||||
|
};
|
||||||
|
|
||||||
|
// This event is emitted by the project when there is a change
|
||||||
|
// in its title
|
||||||
|
wxDECLARE_EXPORTED_EVENT(AUDACITY_DLL_API,
|
||||||
|
EVT_PROJECT_TITLE_CHANGE, wxCommandEvent);
|
||||||
|
|
||||||
|
#endif
|
Loading…
x
Reference in New Issue
Block a user