mirror of
https://github.com/cookiengineer/audacity
synced 2025-09-18 17:10:55 +02:00
Preliminaries for fixing Bug2550 residuals
This commit is contained in:
commit
98d7218e31
@ -44,6 +44,7 @@ Paul Licameli split from AudacityProject.cpp
|
||||
|
||||
wxDEFINE_EVENT(EVT_PROJECT_TITLE_CHANGE, wxCommandEvent);
|
||||
wxDEFINE_EVENT( EVT_CHECKPOINT_FAILURE, wxCommandEvent);
|
||||
wxDEFINE_EVENT( EVT_RECONNECTION_FAILURE, wxCommandEvent);
|
||||
|
||||
static const int ProjectFileID = ('A' << 24 | 'U' << 16 | 'D' << 8 | 'Y');
|
||||
static const int ProjectFileVersion = 1;
|
||||
@ -263,6 +264,12 @@ ProjectFileIO::~ProjectFileIO()
|
||||
{
|
||||
}
|
||||
|
||||
bool ProjectFileIO::HasConnection() const
|
||||
{
|
||||
auto &connectionPtr = ConnectionPtr::Get( mProject );
|
||||
return connectionPtr.mpConnection != nullptr;
|
||||
}
|
||||
|
||||
DBConnection &ProjectFileIO::GetConnection()
|
||||
{
|
||||
auto &curConn = CurrConn();
|
||||
@ -331,6 +338,7 @@ bool ProjectFileIO::OpenConnection(FilePath fileName /* = {} */)
|
||||
if (!CheckVersion())
|
||||
{
|
||||
CloseConnection();
|
||||
curConn.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -344,7 +352,8 @@ bool ProjectFileIO::OpenConnection(FilePath fileName /* = {} */)
|
||||
bool ProjectFileIO::CloseConnection()
|
||||
{
|
||||
auto &curConn = CurrConn();
|
||||
wxASSERT(curConn);
|
||||
if (!curConn)
|
||||
return false;
|
||||
|
||||
if (!curConn->Close())
|
||||
{
|
||||
@ -689,6 +698,10 @@ bool ProjectFileIO::CopyTo(const FilePath &destpath,
|
||||
bool prune /* = false */,
|
||||
const std::vector<const TrackList *> &tracks /* = {} */)
|
||||
{
|
||||
auto pConn = CurrConn().get();
|
||||
if (!pConn)
|
||||
return false;
|
||||
|
||||
// Get access to the active tracklist
|
||||
auto pProject = &mProject;
|
||||
|
||||
@ -763,7 +776,7 @@ bool ProjectFileIO::CopyTo(const FilePath &destpath,
|
||||
//
|
||||
// NOTE: Between the above attach and setting the mode here, a normal DELETE
|
||||
// mode journal will be used and will briefly appear in the filesystem.
|
||||
CurrConn()->FastMode("outbound");
|
||||
pConn->FastMode("outbound");
|
||||
|
||||
// Install our schema into the new database
|
||||
if (!InstallSchema(db, "outbound"))
|
||||
@ -2039,20 +2052,38 @@ bool ProjectFileIO::SaveProject(
|
||||
FilePath savedName = mFileName;
|
||||
if (CloseConnection())
|
||||
{
|
||||
if (MoveProject(savedName, fileName))
|
||||
bool reopened = false;
|
||||
bool moved = false;
|
||||
if (true == (moved = MoveProject(savedName, fileName)))
|
||||
{
|
||||
if (!OpenConnection(fileName))
|
||||
{
|
||||
if (OpenConnection(fileName))
|
||||
reopened = true;
|
||||
else {
|
||||
MoveProject(fileName, savedName);
|
||||
OpenConnection(savedName);
|
||||
reopened = OpenConnection(savedName);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Rename can fail -- if it's to a different device, requiring
|
||||
// real copy of contents, which might exhaust space
|
||||
OpenConnection(savedName);
|
||||
return false;
|
||||
reopened = OpenConnection(savedName);
|
||||
}
|
||||
|
||||
if (!reopened)
|
||||
wxTheApp->CallAfter([this]{
|
||||
ShowErrorDialog(nullptr,
|
||||
XO("Warning"),
|
||||
XO(
|
||||
"The project's database failed to reopen, "
|
||||
"possibly because of limited space on the storage device."),
|
||||
"Error:_Disk_full_or_not_writable"
|
||||
);
|
||||
wxCommandEvent evt{ EVT_RECONNECTION_FAILURE };
|
||||
mProject.ProcessEvent(evt);
|
||||
});
|
||||
|
||||
if (!moved)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2179,11 +2210,9 @@ bool ProjectFileIO::OpenProject()
|
||||
bool ProjectFileIO::CloseProject()
|
||||
{
|
||||
auto &currConn = CurrConn();
|
||||
wxASSERT(currConn);
|
||||
|
||||
// Protect...
|
||||
if (!currConn)
|
||||
{
|
||||
wxLogDebug("Closing project with no database connection");
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -2270,14 +2299,16 @@ void ProjectFileIO::SetError(
|
||||
const TranslatableString &msg, const TranslatableString &libraryError )
|
||||
{
|
||||
auto &currConn = CurrConn();
|
||||
currConn->SetError(msg, libraryError);
|
||||
if (currConn)
|
||||
currConn->SetError(msg, libraryError);
|
||||
}
|
||||
|
||||
void ProjectFileIO::SetDBError(
|
||||
const TranslatableString &msg, const TranslatableString &libraryError)
|
||||
{
|
||||
auto &currConn = CurrConn();
|
||||
currConn->SetDBError(msg, libraryError);
|
||||
if (currConn)
|
||||
currConn->SetDBError(msg, libraryError);
|
||||
}
|
||||
|
||||
void ProjectFileIO::SetBypass()
|
||||
@ -2316,7 +2347,10 @@ void ProjectFileIO::SetBypass()
|
||||
|
||||
int64_t ProjectFileIO::GetBlockUsage(SampleBlockID blockid)
|
||||
{
|
||||
return GetDiskUsage(CurrConn().get(), blockid);
|
||||
auto pConn = CurrConn().get();
|
||||
if (!pConn)
|
||||
return 0;
|
||||
return GetDiskUsage(*pConn, blockid);
|
||||
}
|
||||
|
||||
int64_t ProjectFileIO::GetCurrentUsage(
|
||||
@ -2337,7 +2371,10 @@ int64_t ProjectFileIO::GetCurrentUsage(
|
||||
|
||||
int64_t ProjectFileIO::GetTotalUsage()
|
||||
{
|
||||
return GetDiskUsage(CurrConn().get(), 0);
|
||||
auto pConn = CurrConn().get();
|
||||
if (!pConn)
|
||||
return 0;
|
||||
return GetDiskUsage(*pConn, 0);
|
||||
}
|
||||
|
||||
//
|
||||
@ -2346,7 +2383,7 @@ int64_t ProjectFileIO::GetTotalUsage()
|
||||
// pages available from the "sqlite_dbpage" virtual table to traverse the SQLite
|
||||
// table b-tree described here: https://www.sqlite.org/fileformat.html
|
||||
//
|
||||
int64_t ProjectFileIO::GetDiskUsage(DBConnection *conn, SampleBlockID blockid /* = 0 */)
|
||||
int64_t ProjectFileIO::GetDiskUsage(DBConnection &conn, SampleBlockID blockid /* = 0 */)
|
||||
{
|
||||
// Information we need to track our travels through the b-tree
|
||||
typedef struct
|
||||
@ -2365,7 +2402,7 @@ int64_t ProjectFileIO::GetDiskUsage(DBConnection *conn, SampleBlockID blockid /*
|
||||
|
||||
// Get the rootpage for the sampleblocks table.
|
||||
sqlite3_stmt *stmt =
|
||||
conn->Prepare(DBConnection::GetRootPage,
|
||||
conn.Prepare(DBConnection::GetRootPage,
|
||||
"SELECT rootpage FROM sqlite_master WHERE tbl_name = 'sampleblocks';");
|
||||
if (stmt == nullptr || sqlite3_step(stmt) != SQLITE_ROW)
|
||||
{
|
||||
@ -2380,7 +2417,7 @@ int64_t ProjectFileIO::GetDiskUsage(DBConnection *conn, SampleBlockID blockid /*
|
||||
sqlite3_reset(stmt);
|
||||
|
||||
// Prepare/retrieve statement to read raw database page
|
||||
stmt = conn->Prepare(DBConnection::GetDBPage,
|
||||
stmt = conn.Prepare(DBConnection::GetDBPage,
|
||||
"SELECT data FROM sqlite_dbpage WHERE pgno = ?1;");
|
||||
if (stmt == nullptr)
|
||||
{
|
||||
|
@ -45,6 +45,11 @@ using BlockIDs = std::unordered_set<SampleBlockID>;
|
||||
wxDECLARE_EXPORTED_EVENT( AUDACITY_DLL_API,
|
||||
EVT_CHECKPOINT_FAILURE, wxCommandEvent );
|
||||
|
||||
// An event processed by the project in the main thread after failure to
|
||||
// reconnect to the database, after temporary close and attempted file movement
|
||||
wxDECLARE_EXPORTED_EVENT( AUDACITY_DLL_API,
|
||||
EVT_RECONNECTION_FAILURE, wxCommandEvent );
|
||||
|
||||
///\brief Object associated with a project that manages reading and writing
|
||||
/// of Audacity project file formats, and autosave
|
||||
class ProjectFileIO final
|
||||
@ -108,7 +113,7 @@ public:
|
||||
|
||||
// Return the bytes used for the given block using the connection to a
|
||||
// specific database. This is the workhorse for the above 3 methods.
|
||||
static int64_t GetDiskUsage(DBConnection *conn, SampleBlockID blockid);
|
||||
static int64_t GetDiskUsage(DBConnection &conn, SampleBlockID blockid);
|
||||
|
||||
const TranslatableString &GetLastError();
|
||||
const TranslatableString &GetLibraryError();
|
||||
@ -179,6 +184,10 @@ public:
|
||||
// 0 for success or non-zero to stop the query
|
||||
using ExecCB = std::function<int(int cols, char **vals, char **names)>;
|
||||
|
||||
//! Return true if a connetion is now open
|
||||
bool HasConnection() const;
|
||||
|
||||
//! Return a reference to a connection, creating it as needed on demand; throw on failure
|
||||
DBConnection &GetConnection();
|
||||
|
||||
private:
|
||||
|
@ -302,15 +302,17 @@ bool ProjectFileManager::DoSave(const FilePath & fileName, const bool fromSaveAs
|
||||
bool success = projectFileIO.SaveProject(fileName, mLastSavedTracks.get());
|
||||
if (!success)
|
||||
{
|
||||
ShowErrorDialog(
|
||||
&window,
|
||||
XO("Error Saving Project"),
|
||||
XO("Could not save project. Perhaps %s \n"
|
||||
"is not writable or the disk is full.\n"
|
||||
"For tips on freeing up space, click the help button.")
|
||||
.Format(fileName),
|
||||
"Error:_Disk_full_or_not_writable"
|
||||
);
|
||||
// Show this error only if we didn't fail reconnection in SaveProject
|
||||
if (projectFileIO.HasConnection())
|
||||
ShowErrorDialog(
|
||||
&window,
|
||||
XO("Error Saving Project"),
|
||||
XO("Could not save project. Perhaps %s \n"
|
||||
"is not writable or the disk is full.\n"
|
||||
"For tips on freeing up space, click the help button.")
|
||||
.Format(fileName),
|
||||
"Error:_Disk_full_or_not_writable"
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -82,6 +82,8 @@ ProjectManager::ProjectManager( AudacityProject &project )
|
||||
window.Bind( wxEVT_CLOSE_WINDOW, &ProjectManager::OnCloseWindow, this );
|
||||
mProject.Bind(EVT_PROJECT_STATUS_UPDATE,
|
||||
&ProjectManager::OnStatusChange, this);
|
||||
project.Bind( EVT_RECONNECTION_FAILURE,
|
||||
&ProjectManager::OnReconnectionFailure, this );
|
||||
}
|
||||
|
||||
ProjectManager::~ProjectManager() = default;
|
||||
@ -589,6 +591,14 @@ AudacityProject *ProjectManager::New()
|
||||
return p;
|
||||
}
|
||||
|
||||
void ProjectManager::OnReconnectionFailure(wxCommandEvent & event)
|
||||
{
|
||||
event.Skip();
|
||||
wxTheApp->CallAfter([this]{
|
||||
ProjectWindow::Get(mProject).Close(true);
|
||||
});
|
||||
}
|
||||
|
||||
void ProjectManager::OnCloseWindow(wxCloseEvent & event)
|
||||
{
|
||||
auto &project = mProject;
|
||||
|
@ -65,6 +65,7 @@ public:
|
||||
void SetSkipSavePrompt(bool bSkip) { sbSkipPromptingForSave = bSkip; };
|
||||
|
||||
private:
|
||||
void OnReconnectionFailure(wxCommandEvent & event);
|
||||
void OnCloseWindow(wxCloseEvent & event);
|
||||
void OnTimer(wxTimerEvent & event);
|
||||
void OnOpenAudioFile(wxCommandEvent & event);
|
||||
|
@ -88,6 +88,7 @@ private:
|
||||
|
||||
private:
|
||||
//! This must never be called for silent blocks
|
||||
/*! @post return value is not null */
|
||||
DBConnection *Conn() const;
|
||||
sqlite3 *DB() const
|
||||
{
|
||||
@ -527,7 +528,7 @@ size_t SqliteSampleBlock::GetSpaceUsage() const
|
||||
if (IsSilent())
|
||||
return 0;
|
||||
else
|
||||
return ProjectFileIO::GetDiskUsage(Conn(), mBlockID);
|
||||
return ProjectFileIO::GetDiskUsage(*Conn(), mBlockID);
|
||||
}
|
||||
|
||||
size_t SqliteSampleBlock::GetBlob(void *dest,
|
||||
|
Loading…
x
Reference in New Issue
Block a user