1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-08-01 08:29:27 +02:00

AUP3: Rework Compact Project menu item and AutoRecoverDialog

Plus a couple of fixes that prevent leaving temporary files
after a project is loaded.
This commit is contained in:
Leland Lucius 2020-07-26 02:01:37 -05:00
parent 674cfe68c9
commit 6fef14dd08
7 changed files with 160 additions and 114 deletions

View File

@ -20,10 +20,9 @@ list( APPEND INCLUDES
list( APPEND DEFINES list( APPEND DEFINES
PRIVATE PRIVATE
# #
# Connection based settings. Persistent settings are done in the # We need the dbstats table for space calculations.
# schema.
# #
# SQLITE_DEFAULT_LOCKING_MODE=0 SQLITE_ENABLE_DBSTAT_VTAB=1
# Can't be set after a WAL mode database is initialized, so change # Can't be set after a WAL mode database is initialized, so change
# the default here to ensure all project files get the same page # the default here to ensure all project files get the same page

View File

@ -24,12 +24,11 @@ Paul Licameli split from AutoRecovery.cpp
#include <wx/filename.h> #include <wx/filename.h>
#include <wx/listctrl.h> #include <wx/listctrl.h>
#define USE_CHECKBOXES
enum { enum {
ID_QUIT_AUDACITY = 10000, ID_QUIT_AUDACITY = 10000,
ID_DISCARD_SELECTED, ID_DISCARD_SELECTED,
ID_RECOVER_SELECTED, ID_RECOVER_SELECTED,
ID_SKIP,
ID_FILE_LIST ID_FILE_LIST
}; };
@ -44,10 +43,13 @@ public:
private: private:
void PopulateOrExchange(ShuttleGui &S); void PopulateOrExchange(ShuttleGui &S);
void PopulateList(); void PopulateList();
bool HaveChecked();
void OnQuitAudacity(wxCommandEvent &evt); void OnQuitAudacity(wxCommandEvent &evt);
void OnDiscardSelected(wxCommandEvent &evt); void OnDiscardSelected(wxCommandEvent &evt);
void OnRecoverSelected(wxCommandEvent &evt); void OnRecoverSelected(wxCommandEvent &evt);
void OnSkip(wxCommandEvent &evt);
void OnItemActivated(wxListEvent &evt);
FilePaths mFiles; FilePaths mFiles;
wxListCtrl *mFileList; wxListCtrl *mFileList;
@ -61,6 +63,8 @@ BEGIN_EVENT_TABLE(AutoRecoveryDialog, wxDialogWrapper)
EVT_BUTTON(ID_QUIT_AUDACITY, AutoRecoveryDialog::OnQuitAudacity) EVT_BUTTON(ID_QUIT_AUDACITY, AutoRecoveryDialog::OnQuitAudacity)
EVT_BUTTON(ID_DISCARD_SELECTED, AutoRecoveryDialog::OnDiscardSelected) EVT_BUTTON(ID_DISCARD_SELECTED, AutoRecoveryDialog::OnDiscardSelected)
EVT_BUTTON(ID_RECOVER_SELECTED, AutoRecoveryDialog::OnRecoverSelected) EVT_BUTTON(ID_RECOVER_SELECTED, AutoRecoveryDialog::OnRecoverSelected)
EVT_BUTTON(ID_SKIP, AutoRecoveryDialog::OnSkip)
EVT_LIST_ITEM_ACTIVATED(ID_FILE_LIST, AutoRecoveryDialog::OnItemActivated)
END_EVENT_TABLE() END_EVENT_TABLE()
AutoRecoveryDialog::AutoRecoveryDialog(AudacityProject *project) AutoRecoveryDialog::AutoRecoveryDialog(AudacityProject *project)
@ -97,16 +101,12 @@ void AutoRecoveryDialog::PopulateOrExchange(ShuttleGui &S)
{ {
mFileList = S.Id(ID_FILE_LIST).AddListControlReportMode( mFileList = S.Id(ID_FILE_LIST).AddListControlReportMode(
{ {
#if defined(USE_CHECKBOXES)
/*i18n-hint: (verb). It instruct the user to select items.*/ /*i18n-hint: (verb). It instruct the user to select items.*/
XO("Select"), 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.*/
XO("Name") XO("Name")
}); });
#if defined(USE_CHECKBOXES)
mFileList->EnableCheckBoxes(); mFileList->EnableCheckBoxes();
#endif
PopulateList(); PopulateList();
} }
S.EndStatic(); S.EndStatic();
@ -120,6 +120,7 @@ void AutoRecoveryDialog::PopulateOrExchange(ShuttleGui &S)
S.Id(ID_QUIT_AUDACITY).AddButton(XXO("Quit Audacity")); S.Id(ID_QUIT_AUDACITY).AddButton(XXO("Quit Audacity"));
S.Id(ID_DISCARD_SELECTED).AddButton(XXO("Discard Selected")); S.Id(ID_DISCARD_SELECTED).AddButton(XXO("Discard Selected"));
S.Id(ID_RECOVER_SELECTED).AddButton(XXO("Recover Selected")); S.Id(ID_RECOVER_SELECTED).AddButton(XXO("Recover Selected"));
S.Id(ID_SKIP).AddButton(XXO("Skip"));
} }
S.EndHorizontalLay(); S.EndHorizontalLay();
} }
@ -141,25 +142,13 @@ void AutoRecoveryDialog::PopulateList()
wxString pattern = wxT("*.") + FileNames::UnsavedProjectExtension(); wxString pattern = wxT("*.") + FileNames::UnsavedProjectExtension();
FilePaths files; FilePaths files;
FilePath activeFile; wxDir::GetAllFiles(tempdir, &files, pattern, wxDIR_FILES);
if (mProject)
{
auto &projectFileIO = ProjectFileIO::Get(*mProject);
activeFile = projectFileIO.GetFileName();
}
// wxDir::GetAllFiles(tempdir, &files, pattern, wxDIR_FILES);
FilePaths active = ActiveProjects::GetAll(); FilePaths active = ActiveProjects::GetAll();
mFileList->DeleteAllItems();
long item = 0;
for (auto file : active) for (auto file : active)
{ {
wxFileName fn = file; wxFileName fn = file;
if (fn != activeFile)
{
if (fn.FileExists()) if (fn.FileExists())
{ {
FilePath fullPath = fn.GetFullPath(); FilePath fullPath = fn.GetFullPath();
@ -169,37 +158,71 @@ void AutoRecoveryDialog::PopulateList()
} }
} }
} }
FilePath activeFile;
if (mProject)
{
auto &projectFileIO = ProjectFileIO::Get(*mProject);
activeFile = projectFileIO.GetFileName();
} }
mFiles.clear();
mFileList->DeleteAllItems();
long item = 0;
for (auto file : files) for (auto file : files)
{ {
wxFileName fn = file; wxFileName fn = file;
if (fn != activeFile)
{
mFiles.push_back(fn.GetFullPath()); mFiles.push_back(fn.GetFullPath());
mFileList->InsertItem(item, wxT("")); mFileList->InsertItem(item, wxT(""));
mFileList->SetItem(item, 1, fn.GetName()); mFileList->SetItem(item, 1, fn.GetName());
item++; item++;
} }
}
#if defined(USE_CHECKBOXES)
mFileList->SetColumnWidth(0, wxLIST_AUTOSIZE_USEHEADER); mFileList->SetColumnWidth(0, wxLIST_AUTOSIZE_USEHEADER);
mFileList->SetColumnWidth(1, wxLIST_AUTOSIZE); mFileList->SetColumnWidth(1, 500);
#else
mFileList->SetColumnWidth(0, wxLIST_AUTOSIZE);
#endif
} }
void AutoRecoveryDialog::OnQuitAudacity(wxCommandEvent & WXUNUSED(event)) bool AutoRecoveryDialog::HaveChecked()
{
long item = -1;
while (true)
{
item = mFileList->GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_DONTCARE);
if (item == wxNOT_FOUND)
{
break;
}
if (mFileList->IsItemChecked(item))
{
return true;
}
}
AudacityMessageBox(XO("No projects selected"), XO("Automatic Crash Recovery"));
return false;
}
void AutoRecoveryDialog::OnQuitAudacity(wxCommandEvent &WXUNUSED(evt))
{ {
EndModal(ID_QUIT_AUDACITY); EndModal(ID_QUIT_AUDACITY);
} }
void AutoRecoveryDialog::OnDiscardSelected(wxCommandEvent & WXUNUSED(event)) void AutoRecoveryDialog::OnDiscardSelected(wxCommandEvent &WXUNUSED(evt))
{ {
if (!HaveChecked())
{
return;
}
int ret = AudacityMessageBox( int ret = AudacityMessageBox(
XO("Are you sure you want to discard the selected projects?\n\n" XO("Are you sure you want to discard the selected projects?\n\n"
"Choosing \"Yes\" permanently deletes the selected projects immediately."), "Choosing \"Yes\" permanently deletes the selected projects immediately."),
XO("Confirm Discard Projects"), XO("Automatic Crash Recovery"),
wxICON_QUESTION | wxYES_NO | wxNO_DEFAULT, this); wxICON_QUESTION | wxYES_NO | wxNO_DEFAULT, this);
if (ret == wxNO) if (ret == wxNO)
@ -207,27 +230,18 @@ void AutoRecoveryDialog::OnDiscardSelected(wxCommandEvent & WXUNUSED(event))
return; return;
} }
#define USE_CHECKBOXES
#if defined(USE_CHECKBOXES)
#define state wxLIST_STATE_DONTCARE
#else
#define state wxLIST_STATE_SELECTED
#endif
long item = -1; long item = -1;
while (true) while (true)
{ {
item = mFileList->GetNextItem(item, wxLIST_NEXT_ALL, state); item = mFileList->GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_DONTCARE);
if (item == wxNOT_FOUND) if (item == wxNOT_FOUND)
{ {
break; break;
} }
#if defined(USE_CHECKBOXES)
if (!mFileList->IsItemChecked(item)) if (!mFileList->IsItemChecked(item))
{ {
continue; continue;
} }
#endif
FilePath file = mFiles[item]; FilePath file = mFiles[item];
if (wxRemoveFile(file)) if (wxRemoveFile(file))
@ -259,31 +273,34 @@ void AutoRecoveryDialog::OnDiscardSelected(wxCommandEvent & WXUNUSED(event))
} }
} }
void AutoRecoveryDialog::OnRecoverSelected(wxCommandEvent & WXUNUSED(event)) void AutoRecoveryDialog::OnRecoverSelected(wxCommandEvent &WXUNUSED(evt))
{ {
#define USE_CHECKBOXES if (!HaveChecked())
#if defined(USE_CHECKBOXES) {
#define state wxLIST_STATE_DONTCARE return;
#else }
#define state wxLIST_STATE_SELECTED
#endif
FilePaths files; FilePaths files;
bool selected = false;
long item = -1; long item = -1;
while (true) while (true)
{ {
item = mFileList->GetNextItem(item, wxLIST_NEXT_ALL, state); item = mFileList->GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_DONTCARE);
if (item == wxNOT_FOUND) if (item == wxNOT_FOUND)
{ {
if (!selected)
{
AudacityMessageBox(XO("No projects selected"), XO("Automatic Crash Recovery"));
}
break; break;
} }
#if defined(USE_CHECKBOXES) selected = true;
if (!mFileList->IsItemChecked(item)) if (!mFileList->IsItemChecked(item))
{ {
continue; continue;
} }
#endif
files.push_back(mFiles[item]); files.push_back(mFiles[item]);
} }
@ -293,6 +310,17 @@ void AutoRecoveryDialog::OnRecoverSelected(wxCommandEvent & WXUNUSED(event))
EndModal(ID_RECOVER_SELECTED); EndModal(ID_RECOVER_SELECTED);
} }
void AutoRecoveryDialog::OnSkip(wxCommandEvent &WXUNUSED(evt))
{
EndModal(ID_SKIP);
}
void AutoRecoveryDialog::OnItemActivated(wxListEvent &evt)
{
long item = evt.GetIndex();
mFileList->CheckItem(item, !mFileList->IsItemChecked(item));
}
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
static bool RecoverAllProjects(const FilePaths &files, static bool RecoverAllProjects(const FilePaths &files,
@ -352,6 +380,7 @@ bool ShowAutoRecoveryDialogIfNeeded(AudacityProject **pproj, bool *didRecoverAny
switch (ret) switch (ret)
{ {
case ID_SKIP:
case ID_DISCARD_SELECTED: case ID_DISCARD_SELECTED:
success = true; success = true;
break; break;

View File

@ -354,9 +354,21 @@ void ProjectFileIO::DiscardConnection()
{ {
// Store an error message // Store an error message
SetDBError( SetDBError(
XO("Failed to successfully close the source project file") XO("Failed to discard connection")
); );
} }
// If this is a temporary project, we no longer want to keep the
// project file.
if (mPrevTemporary)
{
// This is just a safety check.
wxFileName temp(FileNames::TempDir());
if (temp == wxPathOnly(mPrevFileName))
{
wxRemoveFile(mPrevFileName);
}
}
mPrevConn = nullptr; mPrevConn = nullptr;
mPrevFileName.clear(); mPrevFileName.clear();
} }
@ -372,7 +384,7 @@ void ProjectFileIO::RestoreConnection()
{ {
// Store an error message // Store an error message
SetDBError( SetDBError(
XO("Failed to successfully close the destination project file") XO("Failed to restore connection")
); );
} }
} }
@ -938,21 +950,14 @@ bool ProjectFileIO::ShouldVacuum(const std::shared_ptr<TrackList> &tracks)
unsigned long long blockcount = 0; unsigned long long blockcount = 0;
unsigned long long total = 0; unsigned long long total = 0;
auto cb = auto cb = [&blockcount, &total](int cols, char **vals, char **)
[&blockcount, &total](int cols, char **vals, char **){ {
if ( cols != 2 )
// Should have two exactly!
return 1;
if ( total > 0 ) {
// Should not have multiple rows!
total = 0;
return 1;
}
// Convert // Convert
wxString{ vals[0] }.ToULongLong( &blockcount ); wxString(vals[0]).ToULongLong(&blockcount);
wxString{ vals[1] }.ToULongLong( &total ); wxString(vals[1]).ToULongLong(&total);
return 0; return 0;
}; };
if (!Query("SELECT Count(*), " if (!Query("SELECT Count(*), "
"Sum(Length(summary256)) + Sum(Length(summary64k)) + Sum(Length(samples)) " "Sum(Length(summary256)) + Sum(Length(summary64k)) + Sum(Length(samples)) "
"FROM sampleblocks;", cb) "FROM sampleblocks;", cb)
@ -985,7 +990,7 @@ Connection &ProjectFileIO::CurrConn()
return connectionPtr.mpConnection; return connectionPtr.mpConnection;
} }
void ProjectFileIO::Vacuum(const std::shared_ptr<TrackList> &tracks) void ProjectFileIO::Vacuum(const std::shared_ptr<TrackList> &tracks, bool force /* = false */)
{ {
// Haven't vacuumed yet // Haven't vacuumed yet
mWasVacuumed = false; mWasVacuumed = false;
@ -996,6 +1001,8 @@ void ProjectFileIO::Vacuum(const std::shared_ptr<TrackList> &tracks)
// Don't vacuum if this is a temporary project or if it's determined there are not // Don't vacuum if this is a temporary project or if it's determined there are not
// enough unused blocks to make it worthwhile // enough unused blocks to make it worthwhile
if (!force)
{
if (IsTemporary() || !ShouldVacuum(tracks)) if (IsTemporary() || !ShouldVacuum(tracks))
{ {
// Delete the AutoSave doc it if exists // Delete the AutoSave doc it if exists
@ -1009,6 +1016,7 @@ void ProjectFileIO::Vacuum(const std::shared_ptr<TrackList> &tracks)
return; return;
} }
}
// Create the project doc // Create the project doc
ProjectSerializer doc; ProjectSerializer doc;
@ -1932,12 +1940,6 @@ bool ProjectFileIO::SaveProject(const FilePath &fileName)
// The Save was successful, so now it is safe to abandon the // The Save was successful, so now it is safe to abandon the
// original connection // original connection
DiscardConnection(); DiscardConnection();
// And also remove the original file if it was a temporary file
if (wasTemp)
{
wxRemoveFile(origName);
}
} }
else else
{ {

View File

@ -64,8 +64,8 @@ public:
// on what is discovered while opening the file, such as whether it is a // on what is discovered while opening the file, such as whether it is a
// recovery file // recovery file
void SetProjectTitle(int number = -1); void SetProjectTitle(int number = -1);
// Should be empty or a fully qualified file name
// Should be empty or a fully qualified file name
const FilePath &GetFileName() const; const FilePath &GetFileName() const;
void SetFileName( const FilePath &fileName ); void SetFileName( const FilePath &fileName );
@ -106,7 +106,7 @@ public:
void SetBypass(); void SetBypass();
// Remove all unused space within a project file // Remove all unused space within a project file
void Vacuum(const std::shared_ptr<TrackList> &tracks); void Vacuum(const std::shared_ptr<TrackList> &tracks, bool force = false);
// The last vacuum check did actually vacuum the project file if true // The last vacuum check did actually vacuum the project file if true
bool WasVacuumed(); bool WasVacuumed();

View File

@ -91,7 +91,6 @@ ProjectSettings::ProjectSettings(AudacityProject &project)
void ProjectSettings::UpdatePrefs() void ProjectSettings::UpdatePrefs()
{ {
gPrefs->Read(wxT("/GUI/CompactAtClose"), &mCompactAtClose, true);
gPrefs->Read(wxT("/AudioFiles/ShowId3Dialog"), &mShowId3Dialog, true); gPrefs->Read(wxT("/AudioFiles/ShowId3Dialog"), &mShowId3Dialog, true);
gPrefs->Read(wxT("/GUI/EmptyCanBeDirty"), &mEmptyCanBeDirty, true); gPrefs->Read(wxT("/GUI/EmptyCanBeDirty"), &mEmptyCanBeDirty, true);
gPrefs->Read(wxT("/GUI/ShowSplashScreen"), &mShowSplashScreen, true); gPrefs->Read(wxT("/GUI/ShowSplashScreen"), &mShowSplashScreen, true);

View File

@ -118,10 +118,6 @@ public:
bool GetShowSplashScreen() const { return mShowSplashScreen; } bool GetShowSplashScreen() const { return mShowSplashScreen; }
// Compact at close
void SetCompactAtClose(bool compact) { mCompactAtClose = compact; };
bool GetCompactAtClose() const { return mCompactAtClose; }
private: private:
void UpdatePrefs() override; void UpdatePrefs() override;
@ -149,7 +145,6 @@ private:
bool mIsSyncLocked{ false }; bool mIsSyncLocked{ false };
bool mEmptyCanBeDirty; bool mEmptyCanBeDirty;
bool mShowSplashScreen; bool mShowSplashScreen;
bool mCompactAtClose;
}; };
#endif #endif

View File

@ -2,6 +2,7 @@
#include "../Experimental.h" #include "../Experimental.h"
#include "../BatchCommands.h" #include "../BatchCommands.h"
#include "../Clipboard.h"
#include "../CommonCommandFlags.h" #include "../CommonCommandFlags.h"
#include "../FileNames.h" #include "../FileNames.h"
#include "../LabelTrack.h" #include "../LabelTrack.h"
@ -142,19 +143,41 @@ void OnClose(const CommandContext &context )
window.Close(); window.Close();
} }
void OnCompact(const CommandContext &context ) void OnCompact(const CommandContext &context)
{ {
int id = AudacityMessageBox(
XO("Compacting this project will free up disk space by removing unused bytes within the file.\n\n"
"NOTE: If you proceed, the current Undo History and clipboard contents will be discarded.\n\n"
"Do you want to continue?"),
XO("Compact Project"),
wxYES_NO);
if (id == wxNO)
{
return;
}
auto &project = context.project; auto &project = context.project;
auto &settings = ProjectSettings::Get( project ); auto &undoManager = UndoManager::Get(project);
bool compact; ProjectHistory::Get(project)
gPrefs->Read(wxT("/GUI/CompactAtClose"), &compact, true); .PushState(XO("Compacted project file"), XO("Compact"), UndoPush::CONSOLIDATE);
compact = !compact; auto numStates = undoManager.GetNumStates();
gPrefs->Write(wxT("/GUI/CompactAtClose"), compact); undoManager.RemoveStates(numStates - 1);
gPrefs->Flush();
settings.SetCompactAtClose(compact); auto &clipboard = Clipboard::Get();
clipboard.Clear();
auto currentTracks = TrackList::Create( nullptr );
auto &tracks = TrackList::Get( project );
for (auto t : tracks.Any())
{
currentTracks->Add(t->Duplicate());
}
auto &projectFileIO = ProjectFileIO::Get(project);
projectFileIO.Vacuum(currentTracks, true);
} }
void OnSave(const CommandContext &context ) void OnSave(const CommandContext &context )
@ -612,11 +635,7 @@ BaseItemSharedPtr FileMenu()
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
Command( wxT("Close"), XXO("&Close"), FN(OnClose), Command( wxT("Close"), XXO("&Close"), FN(OnClose),
AudioIONotBusyFlag(), wxT("Ctrl+W") ), AudioIONotBusyFlag(), wxT("Ctrl+W") )
Command( wxT("Compact"), XXO("Com&pact at close (on/off)"),
FN(OnCompact), AlwaysEnabledFlag,
Options{}.CheckTest( wxT("/GUI/CompactAtClose"), true ) )
), ),
Section( "Save", Section( "Save",
@ -627,7 +646,10 @@ BaseItemSharedPtr FileMenu()
AudioIONotBusyFlag() ), AudioIONotBusyFlag() ),
Command( wxT("SaveCopy"), XXO("&Backup Project..."), FN(OnSaveCopy), Command( wxT("SaveCopy"), XXO("&Backup Project..."), FN(OnSaveCopy),
AudioIONotBusyFlag() ) AudioIONotBusyFlag() )
) ),
Command( wxT("Compact"), XXO("Com&pact project"), FN(OnCompact),
AudioIONotBusyFlag() )
), ),
Section( "Import-Export", Section( "Import-Export",