Audacity 3.2.0
Classes | Public Types | Public Member Functions | Static Public Member Functions | Private Member Functions | Static Private Member Functions | Private Attributes | List of all members
ProjectFileIO Class Referencefinal

Object associated with a project that manages reading and writing of Audacity project file formats, and autosave. More...

#include <ProjectFileIO.h>

Inheritance diagram for ProjectFileIO:
[legend]
Collaboration diagram for ProjectFileIO:
[legend]

Classes

class  BackupProject
 

Public Types

using ExecCB = std::function< int(int cols, char **vals, char **names)>
 
- Public Types inherited from Observer::Publisher< ProjectFileIOMessage >
using message_type = ProjectFileIOMessage
 
using CallbackReturn = std::conditional_t< true, void, bool >
 
using Callback = std::function< CallbackReturn(const ProjectFileIOMessage &) >
 Type of functions that can be connected to the Publisher. More...
 

Public Member Functions

 ProjectFileIO (AudacityProject &project)
 
 ProjectFileIO (const ProjectFileIO &)=delete
 
ProjectFileIOoperator= (const ProjectFileIO &)=delete
 
 ~ProjectFileIO ()
 
const wxString & GetProjectTitle () const
 
void SetProjectTitle (int number=-1)
 
const FilePathGetFileName () const
 
void SetFileName (const FilePath &fileName)
 
bool IsModified () const
 
bool IsTemporary () const
 
bool IsRecovered () const
 
bool AutoSave (bool recording=false)
 
bool AutoSaveDelete (sqlite3 *db=nullptr)
 
bool OpenProject ()
 
bool CloseProject ()
 
bool ReopenProject ()
 
bool LoadProject (const FilePath &fileName, bool ignoreAutosave)
 
bool UpdateSaved (const TrackList *tracks=nullptr)
 
bool SaveProject (const FilePath &fileName, const TrackList *lastSaved)
 
bool SaveCopy (const FilePath &fileName)
 
wxLongLong GetFreeDiskSpace () const
 
int64_t GetBlockUsage (SampleBlockID blockid)
 
int64_t GetCurrentUsage (const std::vector< const TrackList * > &trackLists) const
 
int64_t GetTotalUsage ()
 
void ShowError (const BasicUI::WindowPlacement &placement, const TranslatableString &dlogTitle, const TranslatableString &message, const wxString &helpPage)
 Displays an error dialog with a button that offers help. More...
 
const TranslatableStringGetLastError () const
 
const TranslatableStringGetLibraryError () const
 
int GetLastErrorCode () const
 
const wxString & GetLastLog () const
 
void SetBypass ()
 
void Compact (const std::vector< const TrackList * > &tracks, bool force=false)
 
bool WasCompacted ()
 
bool HadUnused ()
 
bool DeleteBlocks (const BlockIDs &blockids, bool complement)
 
bool HasConnection () const
 Return true if a connection is now open. More...
 
DBConnectionGetConnection ()
 Return a reference to a connection, creating it as needed on demand; throw on failure. More...
 
wxString GenerateDoc ()
 Return a strings representation of the active project XML doc. More...
 
- Public Member Functions inherited from ClientData::Base
virtual ~Base ()
 
- Public Member Functions inherited from XMLTagHandler
 XMLTagHandler ()
 
virtual ~XMLTagHandler ()
 
virtual bool HandleXMLTag (const std::string_view &tag, const AttributesList &attrs)=0
 
virtual void HandleXMLEndTag (const std::string_view &WXUNUSED(tag))
 
virtual void HandleXMLContent (const std::string_view &WXUNUSED(content))
 
virtual XMLTagHandlerHandleXMLChild (const std::string_view &tag)=0
 
void ReadXMLEndTag (const char *tag)
 
void ReadXMLContent (const char *s, int len)
 
XMLTagHandlerReadXMLChild (const char *tag)
 
- Public Member Functions inherited from Observer::Publisher< ProjectFileIOMessage >
 Publisher (ExceptionPolicy *pPolicy=nullptr, Alloc a={})
 Constructor supporting type-erased custom allocation/deletion. More...
 
 Publisher (Publisher &&)=default
 
Publisheroperator= (Publisher &&)=default
 
Subscription Subscribe (Callback callback)
 Connect a callback to the Publisher; later-connected are called earlier. More...
 
Subscription Subscribe (Object &obj, Return(Object::*callback)(Args...))
 Overload of Subscribe takes an object and pointer-to-member-function. More...
 

Static Public Member Functions

static bool InitializeSQL ()
 
static ProjectFileIOGet (AudacityProject &project)
 
static const ProjectFileIOGet (const AudacityProject &project)
 
static int64_t GetDiskUsage (DBConnection &conn, SampleBlockID blockid)
 
static bool RemoveProject (const FilePath &filename)
 Remove any files associated with a project at given path; return true if successful. More...
 

Private Member Functions

bool RenameOrWarn (const FilePath &src, const FilePath &dst)
 Rename a file or put up appropriate warning message. More...
 
bool MoveProject (const FilePath &src, const FilePath &dst)
 
void OnCheckpointFailure ()
 
void WriteXMLHeader (XMLWriter &xmlFile) const
 
void WriteXML (XMLWriter &xmlFile, bool recording=false, const TrackList *tracks=nullptr)
 
bool HandleXMLTag (const std::string_view &tag, const AttributesList &attrs) override
 
XMLTagHandlerHandleXMLChild (const std::string_view &tag) override
 
void UpdatePrefs () override
 
int Exec (const char *query, const ExecCB &callback, bool silent=false)
 
sqlite3 * DB ()
 
bool OpenConnection (FilePath fileName={})
 
bool CloseConnection ()
 
void SaveConnection ()
 
void DiscardConnection ()
 
void RestoreConnection ()
 
void UseConnection (Connection &&conn, const FilePath &filePath)
 
bool Query (const char *sql, const ExecCB &callback, bool silent=false)
 
bool GetValue (const char *sql, wxString &value, bool silent=false)
 
bool GetValue (const char *sql, int64_t &value, bool silent=false)
 
bool CheckVersion ()
 
bool InstallSchema (sqlite3 *db, const char *schema="main")
 
bool WriteDoc (const char *table, const ProjectSerializer &autosave, const char *schema="main")
 
bool CopyTo (const FilePath &destpath, const TranslatableString &msg, bool isTemporary, bool prune=false, const std::vector< const TrackList * > &tracks={})
 
void SetError (const TranslatableString &msg, const TranslatableString &libraryError={}, int errorCode={})
 Just set stored errors. More...
 
void SetDBError (const TranslatableString &msg, const TranslatableString &libraryError={}, int errorCode=-1)
 Set stored errors and write to log; and default libraryError to what database library reports. More...
 
bool ShouldCompact (const std::vector< const TrackList * > &tracks)
 
ConnectionCurrConn ()
 
- Private Member Functions inherited from PrefsListener
 PrefsListener ()
 
virtual ~PrefsListener ()
 
virtual void UpdatePrefs ()=0
 
virtual void UpdateSelectedPrefs (int id)
 

Static Private Member Functions

static const std::vector< wxString > & AuxiliaryFileSuffixes ()
 
static FilePath SafetyFileName (const FilePath &src)
 Generate a name for short-lived backup project files from an existing project. More...
 
static void InSet (sqlite3_context *context, int argc, sqlite3_value **argv)
 
static unsigned int get2 (const unsigned char *ptr)
 
static unsigned int get4 (const unsigned char *ptr)
 
static int get_varint (const unsigned char *ptr, int64_t *out)
 
- Static Private Member Functions inherited from PrefsListener
static void Broadcast (int id=0)
 Call this static function to notify all PrefsListener objects. More...
 

Private Attributes

AudacityProjectmProject
 
wxString mTitle
 
std::shared_ptr< DBConnectionErrorsmpErrors
 
FilePath mFileName
 
bool mRecovered
 
bool mModified
 
bool mTemporary
 
bool mWasCompacted
 
bool mHadUnused
 
Connection mPrevConn
 
FilePath mPrevFileName
 
bool mPrevTemporary
 

Additional Inherited Members

- Static Public Attributes inherited from Observer::Publisher< ProjectFileIOMessage >
static constexpr bool notifies_all
 
- Protected Member Functions inherited from Observer::Publisher< ProjectFileIOMessage >
CallbackReturn Publish (const ProjectFileIOMessage &message)
 Send a message to connected callbacks. More...
 

Detailed Description

Object associated with a project that manages reading and writing of Audacity project file formats, and autosave.

Definition at line 58 of file ProjectFileIO.h.

Member Typedef Documentation

◆ ExecCB

using ProjectFileIO::ExecCB = std::function<int(int cols, char **vals, char **names)>

Definition at line 197 of file ProjectFileIO.h.

Constructor & Destructor Documentation

◆ ProjectFileIO() [1/2]

ProjectFileIO::ProjectFileIO ( AudacityProject project)
explicit

Definition at line 444 of file ProjectFileIO.cpp.

445 : mProject{ project }
446 , mpErrors{ std::make_shared<DBConnectionErrors>() }
447{
448 mPrevConn = nullptr;
449
450 mRecovered = false;
451 mModified = false;
452 mTemporary = true;
453
455
456 // Make sure there is plenty of space for Sqlite files
457 wxLongLong freeSpace = 0;
458
459 auto path = TempDirectory::TempDir();
460 if (wxGetDiskSpace(path, NULL, &freeSpace)) {
461 if (freeSpace < wxLongLong(wxLL(100 * 1048576))) {
462 auto volume = FileNames::AbbreviatePath( path );
463 /* i18n-hint: %s will be replaced by the drive letter (on Windows) */
465 XO("Warning"),
466 XO("There is very little free disk space left on %s\n"
467 "Please select a bigger temporary directory location in\n"
468 "Directories Preferences.").Format( volume ),
469 "Error:_Disk_full_or_not_writable"
470 );
471 }
472 }
473}
XO("Cut/Copy/Paste")
AudacityProject & mProject
void SetProjectTitle(int number=-1)
Connection mPrevConn
std::shared_ptr< DBConnectionErrors > mpErrors
void ShowErrorDialog(const WindowPlacement &placement, const TranslatableString &dlogTitle, const TranslatableString &message, const ManualPageID &helpPage, const ErrorDialogOptions &options={})
Show an error dialog with a link to the manual for further help.
Definition: BasicUI.h:259
FILES_API wxString AbbreviatePath(const wxFileName &fileName)
Give enough of the path to identify the device. (On Windows, drive letter plus ':')
FILES_API wxString TempDir()

References FileNames::AbbreviatePath(), mModified, mPrevConn, mRecovered, mTemporary, SetProjectTitle(), BasicUI::ShowErrorDialog(), TempDirectory::TempDir(), and XO().

Here is the call graph for this function:

◆ ProjectFileIO() [2/2]

ProjectFileIO::ProjectFileIO ( const ProjectFileIO )
delete

◆ ~ProjectFileIO()

ProjectFileIO::~ProjectFileIO ( )

Definition at line 475 of file ProjectFileIO.cpp.

476{
477}

Member Function Documentation

◆ AutoSave()

bool ProjectFileIO::AutoSave ( bool  recording = false)

Definition at line 1801 of file ProjectFileIO.cpp.

1802{
1803 ProjectSerializer autosave;
1804 WriteXMLHeader(autosave);
1805 WriteXML(autosave, recording);
1806
1807 if (WriteDoc("autosave", autosave))
1808 {
1809 mModified = true;
1810 return true;
1811 }
1812
1813 return false;
1814}
void WriteXMLHeader(XMLWriter &xmlFile) const
void WriteXML(XMLWriter &xmlFile, bool recording=false, const TrackList *tracks=nullptr)
bool WriteDoc(const char *table, const ProjectSerializer &autosave, const char *schema="main")
a class used to (de)serialize the project catalog

References mModified, WriteDoc(), WriteXML(), and WriteXMLHeader().

Here is the call graph for this function:

◆ AutoSaveDelete()

bool ProjectFileIO::AutoSaveDelete ( sqlite3 *  db = nullptr)

Definition at line 1816 of file ProjectFileIO.cpp.

1817{
1818 int rc;
1819
1820 if (!db)
1821 {
1822 db = DB();
1823 }
1824
1825 rc = sqlite3_exec(db, "DELETE FROM autosave;", nullptr, nullptr, nullptr);
1826 if (rc != SQLITE_OK)
1827 {
1828 ADD_EXCEPTION_CONTEXT("sqlite3.rc", std::to_string(rc));
1829 ADD_EXCEPTION_CONTEXT("sqlite3.context", "ProjectGileIO::AutoSaveDelete");
1830
1831 SetDBError(
1832 XO("Failed to remove the autosave information from the project file.")
1833 );
1834 return false;
1835 }
1836
1837 mModified = false;
1838
1839 return true;
1840}
#define ADD_EXCEPTION_CONTEXT(name, value)
Definition: SentryHelper.h:21
void SetDBError(const TranslatableString &msg, const TranslatableString &libraryError={}, int errorCode=-1)
Set stored errors and write to log; and default libraryError to what database library reports.
sqlite3 * DB()

References ADD_EXCEPTION_CONTEXT, DB(), mModified, SetDBError(), and XO().

Referenced by Compact(), SaveProject(), and UpdateSaved().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ AuxiliaryFileSuffixes()

const std::vector< wxString > & ProjectFileIO::AuxiliaryFileSuffixes ( )
staticprivate

Strings like -wal that may be appended to main project name to get other files created by the database system

Definition at line 1255 of file ProjectFileIO.cpp.

1256{
1257 static const std::vector<wxString> strings {
1258 "-wal",
1259#ifndef NO_SHM
1260 "-shm",
1261#endif
1262 };
1263 return strings;
1264}

Referenced by MoveProject(), RemoveProject(), SafetyFileName(), and ProjectFileIO::BackupProject::~BackupProject().

Here is the caller graph for this function:

◆ CheckVersion()

bool ProjectFileIO::CheckVersion ( )
private

Definition at line 764 of file ProjectFileIO.cpp.

765{
766 auto db = DB();
767 int rc;
768
769 // Install our schema if this is an empty DB
770 wxString result;
771 if (!GetValue("SELECT Count(*) FROM sqlite_master WHERE type='table';", result))
772 {
773 // Bug 2718 workaround for a better error message:
774 // If at this point we get SQLITE_CANTOPEN, then the directory is read-only
775 if (GetLastErrorCode() == SQLITE_CANTOPEN)
776 {
777 SetError(
778 /* i18n-hint: An error message. */
779 XO("Project is in a read only directory\n(Unable to create the required temporary files)"),
781 );
782 }
783
784 return false;
785 }
786
787 // If the return count is zero, then there are no tables defined, so this
788 // must be a new project file.
789 if (wxStrtol<char **>(result, nullptr, 10) == 0)
790 {
791 return InstallSchema(db);
792 }
793
794 // Check for our application ID
795 if (!GetValue("PRAGMA application_ID;", result))
796 {
797 return false;
798 }
799
800 // It's a database that SQLite recognizes, but it's not one of ours
801 if (wxStrtoul<char **>(result, nullptr, 10) != ProjectFileID)
802 {
803 SetError(XO("This is not an Audacity project file"));
804 return false;
805 }
806
807 // Get the project file version
808 if (!GetValue("PRAGMA user_version;", result))
809 {
810 return false;
811 }
812
813 const ProjectFormatVersion version =
814 ProjectFormatVersion::FromPacked(wxStrtoul<char**>(result, nullptr, 10));
815
816 // Project file version is higher than ours. We will refuse to
817 // process it since we can't trust anything about it.
818 if (SupportedProjectFormatVersion < version)
819 {
820 SetError(
821 XO("This project was created with a newer version of Audacity.\n\nYou will need to upgrade to open it.")
822 );
823 return false;
824 }
825
826 return true;
827}
static const int ProjectFileID
const ProjectFormatVersion SupportedProjectFormatVersion
This constant represents the current version of Audacity.
const TranslatableString & GetLibraryError() const
bool GetValue(const char *sql, wxString &value, bool silent=false)
bool InstallSchema(sqlite3 *db, const char *schema="main")
int GetLastErrorCode() const
void SetError(const TranslatableString &msg, const TranslatableString &libraryError={}, int errorCode={})
Just set stored errors.
A structure that holds the project version.
static ProjectFormatVersion FromPacked(uint32_t) noexcept

References DB(), ProjectFormatVersion::FromPacked(), GetLastErrorCode(), GetLibraryError(), GetValue(), InstallSchema(), ProjectFileID, SetError(), SupportedProjectFormatVersion, and XO().

Referenced by OpenConnection().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ CloseConnection()

bool ProjectFileIO::CloseConnection ( )
private

Definition at line 582 of file ProjectFileIO.cpp.

583{
584 auto &curConn = CurrConn();
585 if (!curConn)
586 return false;
587
588 if (!curConn->Close())
589 {
590 return false;
591 }
592 curConn.reset();
593
594 SetFileName({});
595
596 return true;
597}
void SetFileName(const FilePath &fileName)
Connection & CurrConn()

References CurrConn(), and SetFileName().

Referenced by CloseProject(), Compact(), OpenConnection(), ReopenProject(), and SaveProject().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ CloseProject()

bool ProjectFileIO::CloseProject ( )

Definition at line 2366 of file ProjectFileIO.cpp.

2367{
2368 auto &currConn = CurrConn();
2369 if (!currConn)
2370 {
2371 wxLogDebug("Closing project with no database connection");
2372 return true;
2373 }
2374
2375 // Save the filename since CloseConnection() will clear it
2376 wxString filename = mFileName;
2377
2378 // Not much we can do if this fails. The user will simply get
2379 // the recovery dialog upon next restart.
2380 if (CloseConnection())
2381 {
2382 // If this is a temporary project, we no longer want to keep the
2383 // project file.
2384 if (IsTemporary())
2385 {
2386 // This is just a safety check.
2387 wxFileName temp(TempDirectory::TempDir(), wxT(""));
2388 wxFileName file(filename);
2389 file.SetFullName(wxT(""));
2390 if (file == temp)
2391 RemoveProject(filename);
2392 }
2393 }
2394
2395 return true;
2396}
wxT("CloseDown"))
static bool RemoveProject(const FilePath &filename)
Remove any files associated with a project at given path; return true if successful.
FilePath mFileName
bool CloseConnection()
bool IsTemporary() const

References CloseConnection(), CurrConn(), IsTemporary(), mFileName, RemoveProject(), TempDirectory::TempDir(), and wxT().

Referenced by SaveProject().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ Compact()

void ProjectFileIO::Compact ( const std::vector< const TrackList * > &  tracks,
bool  force = false 
)

Definition at line 1431 of file ProjectFileIO.cpp.

1433{
1434 // Haven't compacted yet
1435 mWasCompacted = false;
1436
1437 // Assume we have unused blocks until we find out otherwise. That way cleanup
1438 // at project close time will still occur.
1439 mHadUnused = true;
1440
1441 // If forcing compaction, bypass inspection.
1442 if (!force)
1443 {
1444 // Don't compact if this is a temporary project or if it's determined there are not
1445 // enough unused blocks to make it worthwhile.
1446 if (IsTemporary() || !ShouldCompact(tracks))
1447 {
1448 // Delete the AutoSave doc it if exists
1449 if (IsModified())
1450 {
1451 // PRL: not clear what to do if the following fails, but the worst should
1452 // be, the project may reopen in its present state as a recovery file, not
1453 // at the last saved state.
1454 // REVIEW: Could the autosave file be corrupt though at that point, and so
1455 // prevent recovery?
1456 // LLL: I believe Paul is correct since it's deleted with a single SQLite
1457 // transaction. The next time the file opens will just invoke recovery.
1458 (void) AutoSaveDelete();
1459 }
1460
1461 return;
1462 }
1463 }
1464
1465 wxString origName = mFileName;
1466 wxString backName = origName + "_compact_back";
1467 wxString tempName = origName + "_compact_temp";
1468
1469 // Copy the original database to a new database. Only prune sample blocks if
1470 // we have a tracklist.
1471 // REVIEW: Compact can fail on the CopyTo with no error messages. That's OK?
1472 // LLL: We could display an error message or just ignore the failure and allow
1473 // the file to be compacted the next time it's saved.
1474 if (CopyTo(tempName, XO("Compacting project"), IsTemporary(), !tracks.empty(), tracks))
1475 {
1476 // Must close the database to rename it
1477 if (CloseConnection())
1478 {
1479 // Only use the new file if it is actually smaller than the original.
1480 //
1481 // If the original file doesn't have anything to compact (original and new
1482 // are basically identical), the file could grow by a few pages because of
1483 // differences in how SQLite constructs the b-tree.
1484 //
1485 // In this case, just toss the new file and continue to use the original.
1486 //
1487 // Also, do this after closing the connection so that the -wal file
1488 // gets cleaned up.
1489 if (wxFileName::GetSize(tempName) < wxFileName::GetSize(origName))
1490 {
1491 // Rename the original to backup
1492 if (wxRenameFile(origName, backName))
1493 {
1494 // Rename the temporary to original
1495 if (wxRenameFile(tempName, origName))
1496 {
1497 // Open the newly compacted original file
1498 if (OpenConnection(origName))
1499 {
1500 // Remove the old original file
1501 if (!wxRemoveFile(backName))
1502 {
1503 // Just log the error, nothing can be done to correct it
1504 // and WX should have logged another message showing the
1505 // system error code.
1506 wxLogWarning(wxT("Compaction failed to delete backup %s"), backName);
1507 }
1508
1509 // Remember that we compacted
1510 mWasCompacted = true;
1511
1512 return;
1513 }
1514 else
1515 {
1516 wxLogWarning(wxT("Compaction failed to open new project %s"), origName);
1517 }
1518
1519 if (!wxRenameFile(origName, tempName))
1520 {
1521 wxLogWarning(wxT("Compaction failed to rename original %s to temp %s"),
1522 origName, tempName);
1523 }
1524 }
1525 else
1526 {
1527 wxLogWarning(wxT("Compaction failed to rename temp %s to orig %s"),
1528 origName, tempName);
1529 }
1530
1531 if (!wxRenameFile(backName, origName))
1532 {
1533 wxLogWarning(wxT("Compaction failed to rename back %s to orig %s"),
1534 backName, origName);
1535 }
1536 }
1537 else
1538 {
1539 wxLogWarning(wxT("Compaction failed to rename orig %s to back %s"),
1540 backName, origName);
1541 }
1542 }
1543
1544 if (!OpenConnection(origName))
1545 {
1546 wxLogWarning(wxT("Compaction failed to reopen %s"), origName);
1547 }
1548 }
1549
1550 // Did not achieve any real compaction
1551 // RemoveProject not needed for what was an attached database
1552 if (!wxRemoveFile(tempName))
1553 {
1554 // Just log the error, nothing can be done to correct it
1555 // and WX should have logged another message showing the
1556 // system error code.
1557 wxLogWarning(wxT("Failed to delete temporary file...ignoring"));
1558 }
1559 }
1560
1561 return;
1562}
bool CopyTo(const FilePath &destpath, const TranslatableString &msg, bool isTemporary, bool prune=false, const std::vector< const TrackList * > &tracks={})
bool OpenConnection(FilePath fileName={})
bool ShouldCompact(const std::vector< const TrackList * > &tracks)
bool AutoSaveDelete(sqlite3 *db=nullptr)
bool IsModified() const

References AutoSaveDelete(), CloseConnection(), CopyTo(), IsModified(), IsTemporary(), mFileName, mHadUnused, mWasCompacted, OpenConnection(), ShouldCompact(), wxT(), and XO().

Referenced by SaveProject().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ CopyTo()

bool ProjectFileIO::CopyTo ( const FilePath destpath,
const TranslatableString msg,
bool  isTemporary,
bool  prune = false,
const std::vector< const TrackList * > &  tracks = {} 
)
private
Parameters
tracksFirst track list (or if none, then the project's track list) are tracks to write into document blob; That list, plus any others, contain tracks whose sample blocks must be kept

Definition at line 939 of file ProjectFileIO.cpp.

944{
945 using namespace BasicUI;
946
947 auto pConn = CurrConn().get();
948 if (!pConn)
949 return false;
950
951 // Get access to the active tracklist
952 auto pProject = &mProject;
953
954 SampleBlockIDSet blockids;
955
956 // Collect all active blockids
957 if (prune)
958 {
959 for (auto trackList : tracks)
960 if (trackList)
961 InspectBlocks( *trackList, {}, &blockids );
962 }
963 // Collect ALL blockids
964 else
965 {
966 auto cb = [&blockids](int cols, char **vals, char **){
967 SampleBlockID blockid;
968 wxString{ vals[0] }.ToLongLong(&blockid);
969 blockids.insert(blockid);
970 return 0;
971 };
972
973 if (!Query("SELECT blockid FROM sampleblocks;", cb))
974 {
975 // Error message already captured.
976 return false;
977 }
978 }
979
980 // Create the project doc
982 WriteXMLHeader(doc);
983 WriteXML(doc, false, tracks.empty() ? nullptr : tracks[0]);
984
985 auto db = DB();
986 Connection destConn = nullptr;
987 bool success = false;
988 int rc = SQLITE_OK;
990
991 // Cleanup in case things go awry
992 auto cleanup = finally([&]
993 {
994 if (!success)
995 {
996 if (destConn)
997 {
998 destConn->Close();
999 destConn = nullptr;
1000 }
1001
1002 // Rollback transaction in case one was active.
1003 // If this fails (probably due to memory or disk space), the transaction will
1004 // (presumably) still be active, so further updates to the project file will
1005 // fail as well. Not really much we can do about it except tell the user.
1006 auto result = sqlite3_exec(db, "ROLLBACK;", nullptr, nullptr, nullptr);
1007
1008 // Only capture the error if there wasn't a previous error
1009 if (result != SQLITE_OK && (rc == SQLITE_DONE || rc == SQLITE_OK))
1010 {
1011 ADD_EXCEPTION_CONTEXT("sqlite3.rc", std::to_string(rc));
1013 "sqlite3.context", "ProjectGileIO::CopyTo.cleanup");
1014
1015 SetDBError(
1016 XO("Failed to rollback transaction during import")
1017 );
1018 }
1019
1020 // And detach the outbound DB in case (if it's attached). Don't check for
1021 // errors since it may not be attached. But, if it is and the DETACH fails,
1022 // subsequent CopyTo() actions will fail until Audacity is relaunched.
1023 sqlite3_exec(db, "DETACH DATABASE outbound;", nullptr, nullptr, nullptr);
1024
1025 // RemoveProject not necessary to clean up attached database
1026 wxRemoveFile(destpath);
1027 }
1028 });
1029
1030 // Attach the destination database
1031 wxString sql;
1032 wxString dbName = destpath;
1033 // Bug 2793: Quotes in name need escaping for sqlite3.
1034 dbName.Replace( "'", "''");
1035 sql.Printf("ATTACH DATABASE '%s' AS outbound;", dbName.ToUTF8());
1036
1037 rc = sqlite3_exec(db, sql, nullptr, nullptr, nullptr);
1038 if (rc != SQLITE_OK)
1039 {
1040 SetDBError(
1041 XO("Unable to attach destination database")
1042 );
1043 return false;
1044 }
1045
1046 // Ensure attached DB connection gets configured
1047 //
1048 // NOTE: Between the above attach and setting the mode here, a normal DELETE
1049 // mode journal will be used and will briefly appear in the filesystem.
1050 if ( pConn->FastMode("outbound") != SQLITE_OK)
1051 {
1052 SetDBError(
1053 XO("Unable to switch to fast journaling mode")
1054 );
1055
1056 return false;
1057 }
1058
1059 // Install our schema into the new database
1060 if (!InstallSchema(db, "outbound"))
1061 {
1062 // Message already set
1063 return false;
1064 }
1065
1066 {
1067 // Ensure statement gets cleaned up
1068 sqlite3_stmt *stmt = nullptr;
1069 auto cleanup = finally([&]
1070 {
1071 if (stmt)
1072 {
1073 // No need to check return code
1074 sqlite3_finalize(stmt);
1075 }
1076 });
1077
1078 // Prepare the statement only once
1079 rc = sqlite3_prepare_v2(db,
1080 "INSERT INTO outbound.sampleblocks"
1081 " SELECT * FROM main.sampleblocks"
1082 " WHERE blockid = ?;",
1083 -1,
1084 &stmt,
1085 nullptr);
1086 if (rc != SQLITE_OK)
1087 {
1088 ADD_EXCEPTION_CONTEXT("sqlite3.rc", std::to_string(rc));
1090 "sqlite3.context", "ProjectGileIO::CopyTo.prepare");
1091
1092 SetDBError(
1093 XO("Unable to prepare project file command:\n\n%s").Format(sql)
1094 );
1095 return false;
1096 }
1097
1098 /* i18n-hint: This title appears on a dialog that indicates the progress
1099 in doing something.*/
1100 auto progress =
1101 BasicUI::MakeProgress(XO("Progress"), msg, ProgressShowCancel);
1103
1104 wxLongLong_t count = 0;
1105 wxLongLong_t total = blockids.size();
1106
1107 // Start a transaction. Since we're running without a journal,
1108 // this really doesn't provide rollback. It just prevents SQLite
1109 // from auto committing after each step through the loop.
1110 //
1111 // Also note that we will have an open transaction if we fail
1112 // while copying the blocks. This is fine since we're just going
1113 // to delete the database anyway.
1114 sqlite3_exec(db, "BEGIN;", nullptr, nullptr, nullptr);
1115
1116 // Copy sample blocks from the main DB to the outbound DB
1117 for (auto blockid : blockids)
1118 {
1119 // Bind statement parameters
1120 rc = sqlite3_bind_int64(stmt, 1, blockid);
1121 if (rc != SQLITE_OK)
1122 {
1123 ADD_EXCEPTION_CONTEXT("sqlite3.rc", std::to_string(rc));
1125 "sqlite3.context", "ProjectGileIO::CopyTo.bind");
1126
1127 SetDBError(
1128 XO("Failed to bind SQL parameter")
1129 );
1130
1131 return false;
1132 }
1133
1134 // Process it
1135 rc = sqlite3_step(stmt);
1136 if (rc != SQLITE_DONE)
1137 {
1138 ADD_EXCEPTION_CONTEXT("sqlite3.rc", std::to_string(rc));
1140 "sqlite3.context", "ProjectGileIO::CopyTo.step");
1141
1142 SetDBError(
1143 XO("Failed to update the project file.\nThe following command failed:\n\n%s").Format(sql)
1144 );
1145 return false;
1146 }
1147
1148 // Reset statement to beginning
1149 if (sqlite3_reset(stmt) != SQLITE_OK)
1150 {
1151 ADD_EXCEPTION_CONTEXT("sqlite3.rc", std::to_string(rc));
1153 "sqlite3.context", "ProjectGileIO::CopyTo.reset");
1154
1156 }
1157
1158 result = progress->Poll(++count, total);
1159 if (result != ProgressResult::Success)
1160 {
1161 // Note that we're not setting success, so the finally
1162 // block above will take care of cleaning up
1163 return false;
1164 }
1165 }
1166
1167 // Write the doc.
1168 //
1169 // If we're compacting a temporary project (user initiated from the File
1170 // menu), then write the doc to the "autosave" table since temporary
1171 // projects do not have a "project" doc.
1172 if (!WriteDoc(isTemporary ? "autosave" : "project", doc, "outbound"))
1173 {
1174 return false;
1175 }
1176
1177 // See BEGIN above...
1178 sqlite3_exec(db, "COMMIT;", nullptr, nullptr, nullptr);
1179 }
1180
1181 // Detach the destination database
1182 rc = sqlite3_exec(db, "DETACH DATABASE outbound;", nullptr, nullptr, nullptr);
1183 if (rc != SQLITE_OK)
1184 {
1185 ADD_EXCEPTION_CONTEXT("sqlite3.rc", std::to_string(rc));
1186 ADD_EXCEPTION_CONTEXT("sqlite3.context", "ProjectGileIO::CopyTo::detach");
1187
1188 SetDBError(
1189 XO("Destination project could not be detached")
1190 );
1191
1192 return false;
1193 }
1194
1195 // Tell cleanup everything is good to go
1196 success = true;
1197
1198 return true;
1199}
std::unique_ptr< DBConnection > Connection
Definition: DBConnection.h:132
#define THROW_INCONSISTENCY_EXCEPTION
Throw InconsistencyException, using C++ preprocessor to identify the source code location.
long long SampleBlockID
Definition: ProjectFileIO.h:42
void InspectBlocks(const TrackList &tracks, BlockInspector inspector, SampleBlockIDSet *pIDs)
Definition: WaveTrack.cpp:2517
std::unordered_set< SampleBlockID > SampleBlockIDSet
Definition: WaveTrack.h:545
Abstract base class used in importing a file.
bool Query(const char *sql, const ExecCB &callback, bool silent=false)
ProgressResult
Definition: BasicUI.h:147
@ ProgressShowCancel
Definition: BasicUI.h:141
std::unique_ptr< ProgressDialog > MakeProgress(const TranslatableString &title, const TranslatableString &message, unsigned flags=(ProgressShowStop|ProgressShowCancel), const TranslatableString &remainingLabelText={})
Create and display a progress dialog.
Definition: BasicUI.h:289

References ADD_EXCEPTION_CONTEXT, CurrConn(), DB(), InspectBlocks(), InstallSchema(), BasicUI::MakeProgress(), mProject, BasicUI::ProgressShowCancel, Query(), SetDBError(), BasicUI::Success, THROW_INCONSISTENCY_EXCEPTION, WriteDoc(), WriteXML(), WriteXMLHeader(), and XO().

Referenced by Compact(), SaveCopy(), and SaveProject().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ CurrConn()

Connection & ProjectFileIO::CurrConn ( )
private

Definition at line 1249 of file ProjectFileIO.cpp.

1250{
1251 auto &connectionPtr = ConnectionPtr::Get( mProject );
1252 return connectionPtr.mpConnection;
1253}
static ConnectionPtr & Get(AudacityProject &project)

References ConnectionPtr::Get(), and mProject.

Referenced by CloseConnection(), CloseProject(), CopyTo(), GetBlockUsage(), GetConnection(), GetTotalUsage(), OpenConnection(), RestoreConnection(), SaveConnection(), SetBypass(), SetDBError(), SetError(), and UseConnection().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ DB()

sqlite3 * ProjectFileIO::DB ( )
private

Definition at line 515 of file ProjectFileIO.cpp.

516{
517 return GetConnection().DB();
518}
sqlite3 * DB()
DBConnection & GetConnection()
Return a reference to a connection, creating it as needed on demand; throw on failure.

References DBConnection::DB(), and GetConnection().

Referenced by AutoSaveDelete(), CheckVersion(), CopyTo(), DeleteBlocks(), Exec(), LoadProject(), and WriteDoc().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ DeleteBlocks()

bool ProjectFileIO::DeleteBlocks ( const BlockIDs blockids,
bool  complement 
)

Definition at line 863 of file ProjectFileIO.cpp.

864{
865 auto db = DB();
866 int rc;
867
868 auto cleanup = finally([&]
869 {
870 // Remove our function, whether it was successfully defined or not.
871 sqlite3_create_function(db, "inset", 1, SQLITE_UTF8 | SQLITE_DETERMINISTIC, nullptr, nullptr, nullptr, nullptr);
872 });
873
874 // Add the function used to verify each row's blockid against the set of active blockids
875 const void *p = &blockids;
876 rc = sqlite3_create_function(db, "inset", 1, SQLITE_UTF8 | SQLITE_DETERMINISTIC, const_cast<void*>(p), InSet, nullptr, nullptr);
877 if (rc != SQLITE_OK)
878 {
879 ADD_EXCEPTION_CONTEXT("sqlite3.rc", std::to_string(rc));
880 ADD_EXCEPTION_CONTEXT("sqlite3.context", "ProjectGileIO::DeleteBlocks::create_function");
881
882 /* i18n-hint: An error message. Don't translate inset or blockids.*/
883 SetDBError(XO("Unable to add 'inset' function (can't verify blockids)"));
884 return false;
885 }
886
887 // Delete all rows in the set, or not in it
888 // This is the first command that writes to the database, and so we
889 // do more informative error reporting than usual, if it fails.
890 auto sql = wxString::Format(
891 "DELETE FROM sampleblocks WHERE %sinset(blockid);",
892 complement ? "NOT " : "" );
893 rc = sqlite3_exec(db, sql, nullptr, nullptr, nullptr);
894 if (rc != SQLITE_OK)
895 {
896 ADD_EXCEPTION_CONTEXT("sqlite3.query", sql.ToStdString());
897 ADD_EXCEPTION_CONTEXT("sqlite3.rc", std::to_string(rc));
898 ADD_EXCEPTION_CONTEXT("sqlite3.context", "ProjectGileIO::GetBlob");
899
900 if( rc==SQLITE_READONLY)
901 /* i18n-hint: An error message. Don't translate blockfiles.*/
902 SetDBError(XO("Project is read only\n(Unable to work with the blockfiles)"));
903 else if( rc==SQLITE_LOCKED)
904 /* i18n-hint: An error message. Don't translate blockfiles.*/
905 SetDBError(XO("Project is locked\n(Unable to work with the blockfiles)"));
906 else if( rc==SQLITE_BUSY)
907 /* i18n-hint: An error message. Don't translate blockfiles.*/
908 SetDBError(XO("Project is busy\n(Unable to work with the blockfiles)"));
909 else if( rc==SQLITE_CORRUPT)
910 /* i18n-hint: An error message. Don't translate blockfiles.*/
911 SetDBError(XO("Project is corrupt\n(Unable to work with the blockfiles)"));
912 else if( rc==SQLITE_PERM)
913 /* i18n-hint: An error message. Don't translate blockfiles.*/
914 SetDBError(XO("Some permissions issue\n(Unable to work with the blockfiles)"));
915 else if( rc==SQLITE_IOERR)
916 /* i18n-hint: An error message. Don't translate blockfiles.*/
917 SetDBError(XO("A disk I/O error\n(Unable to work with the blockfiles)"));
918 else if( rc==SQLITE_AUTH)
919 /* i18n-hint: An error message. Don't translate blockfiles.*/
920 SetDBError(XO("Not authorized\n(Unable to work with the blockfiles)"));
921 else
922 /* i18n-hint: An error message. Don't translate blockfiles.*/
923 SetDBError(XO("Unable to work with the blockfiles"));
924
925 return false;
926 }
927
928 // Mark the project recovered if we deleted any rows
929 int changes = sqlite3_changes(db);
930 if (changes > 0)
931 {
932 wxLogInfo(XO("Total orphan blocks deleted %d").Translation(), changes);
933 mRecovered = true;
934 }
935
936 return true;
937}
static void InSet(sqlite3_context *context, int argc, sqlite3_value **argv)

References ADD_EXCEPTION_CONTEXT, DB(), InSet(), mRecovered, SetDBError(), and XO().

Referenced by LoadProject(), and SaveProject().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ DiscardConnection()

void ProjectFileIO::DiscardConnection ( )
private

Definition at line 614 of file ProjectFileIO.cpp.

615{
616 if (mPrevConn)
617 {
618 if (!mPrevConn->Close())
619 {
620 // Store an error message
622 XO("Failed to discard connection")
623 );
624 }
625
626 // If this is a temporary project, we no longer want to keep the
627 // project file.
628 if (mPrevTemporary)
629 {
630 // This is just a safety check.
631 wxFileName temp(TempDirectory::TempDir(), wxT(""));
632 wxFileName file(mPrevFileName);
633 file.SetFullName(wxT(""));
634 if (file == temp)
635 {
637 {
638 wxLogMessage("Failed to remove temporary project %s", mPrevFileName);
639 }
640 }
641 }
642 mPrevConn = nullptr;
643 mPrevFileName.clear();
644 }
645}
FilePath mPrevFileName

References mPrevConn, mPrevFileName, mPrevTemporary, RemoveProject(), SetDBError(), TempDirectory::TempDir(), wxT(), and XO().

Referenced by LoadProject(), and SaveConnection().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ Exec()

int ProjectFileIO::Exec ( const char *  query,
const ExecCB callback,
bool  silent = false 
)
private

Definition at line 688 of file ProjectFileIO.cpp.

689{
690 char *errmsg = nullptr;
691
692 const void *ptr = &callback;
693 int rc = sqlite3_exec(DB(), query, ExecCallback,
694 const_cast<void*>(ptr), &errmsg);
695
696 if (rc != SQLITE_ABORT && errmsg && !silent)
697 {
698 ADD_EXCEPTION_CONTEXT("sqlite3.query", query);
699 ADD_EXCEPTION_CONTEXT("sqlite3.rc", std::to_string(rc));
700
702 XO("Failed to execute a project file command:\n\n%s").Format(query),
703 Verbatim(errmsg),
704 rc
705 );
706 }
707 if (errmsg)
708 {
709 sqlite3_free(errmsg);
710 }
711
712 return rc;
713}
static int ExecCallback(void *data, int cols, char **vals, char **names)
TranslatableString Verbatim(wxString str)
Require calls to the one-argument constructor to go through this distinct global function name.

References ADD_EXCEPTION_CONTEXT, DB(), ExecCallback(), SetDBError(), Verbatim(), and XO().

Referenced by Query().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ GenerateDoc()

wxString ProjectFileIO::GenerateDoc ( )

Return a strings representation of the active project XML doc.

Definition at line 505 of file ProjectFileIO.cpp.

506{
507 auto &trackList = TrackList::Get( mProject );
508
509 XMLStringWriter doc;
510 WriteXMLHeader(doc);
511 WriteXML(doc, false, trackList.empty() ? nullptr : &trackList);
512 return doc;
513}
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:487
Wrapper to output XML data to strings.
Definition: XMLWriter.h:139

References TrackList::Get(), mProject, WriteXML(), and WriteXMLHeader().

Here is the call graph for this function:

◆ Get() [1/2]

ProjectFileIO & ProjectFileIO::Get ( AudacityProject project)
static

Definition at line 433 of file ProjectFileIO.cpp.

434{
435 auto &result = project.AttachedObjects::Get< ProjectFileIO >( sFileIOKey );
436 return result;
437}
static const AudacityProject::AttachedObjects::RegisteredFactory sFileIOKey
Object associated with a project that manages reading and writing of Audacity project file formats,...
Definition: ProjectFileIO.h:64

References sFileIOKey.

Referenced by ProjectFileManager::AddImportedTracks(), OpenProjectCommand::Apply(), ProjectFileManager::CloseProject(), ProjectFileManager::Compact(), ProjectFileManager::CompactProjectOnClose(), DoImportMIDI(), ProjectFileManager::DoSave(), Get(), ProjectManager::GetEstimatedRecordingMinsLeftOnDisk(), ProjectFileManager::Import(), anonymous_namespace{ProjectFileManager.cpp}::ImportProject(), ProjectFileManager::IsAlreadyOpen(), ProjectAudioManager::OnAudioIONewBlocks(), ProjectAudioManager::OnAudioIOStopRecording(), TimerRecordDialog::OnAutoSavePathButton_Click(), ProjectManager::OnCloseWindow(), HistoryDialog::OnCompact(), ContrastDialog::OnExport(), ProjectWindow::OnProjectTitleChange(), ProjectManager::OnTimer(), anonymous_namespace{TimerRecordDialog.cpp}::OnTimerRecord(), ProjectFileManager::OpenNewProject(), ProjectFileManager::OpenProject(), ProjectManager::OpenProject(), ProjectFileManager::OpenProjectFile(), AutoRecoveryDialog::PopulateList(), TimerRecordDialog::PopulateOrExchange(), ProjectAudioManager::ProjectAudioManager(), ProjectManager::ProjectManager(), ProjectWindow::ProjectWindow(), ProjectFileManager::ReadProjectFile(), RefreshAllTitles(), ProjectManager::ResetProjectToEmpty(), ProjectFileManager::Save(), ProjectFileManager::SaveAs(), ProjectFileManager::SaveCopy(), and ProjectFileManager::SaveFromTimerRecording().

Here is the caller graph for this function:

◆ Get() [2/2]

const ProjectFileIO & ProjectFileIO::Get ( const AudacityProject project)
static

Definition at line 439 of file ProjectFileIO.cpp.

440{
441 return Get( const_cast< AudacityProject & >( project ) );
442}
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:90
static ProjectFileIO & Get(AudacityProject &project)

References Get().

Here is the call graph for this function:

◆ get2()

static unsigned int ProjectFileIO::get2 ( const unsigned char *  ptr)
staticprivate

◆ get4()

static unsigned int ProjectFileIO::get4 ( const unsigned char *  ptr)
staticprivate

◆ get_varint()

static int ProjectFileIO::get_varint ( const unsigned char *  ptr,
int64_t *  out 
)
staticprivate

◆ GetBlockUsage()

int64_t ProjectFileIO::GetBlockUsage ( SampleBlockID  blockid)

Definition at line 2533 of file ProjectFileIO.cpp.

2534{
2535 auto pConn = CurrConn().get();
2536 if (!pConn)
2537 return 0;
2538 return GetDiskUsage(*pConn, blockid);
2539}
static int64_t GetDiskUsage(DBConnection &conn, SampleBlockID blockid)

References CurrConn(), and GetDiskUsage().

Here is the call graph for this function:

◆ GetConnection()

DBConnection & ProjectFileIO::GetConnection ( )

Return a reference to a connection, creating it as needed on demand; throw on failure.

Definition at line 485 of file ProjectFileIO.cpp.

486{
487 auto &curConn = CurrConn();
488 if (!curConn)
489 {
490 if (!OpenConnection())
491 {
493 {
495 XO("Failed to open the project's database"),
496 XO("Warning"),
497 "Error:_Disk_full_or_not_writable"
498 };
499 }
500 }
501
502 return *curConn;
503}
@ Internal
Indicates internal failure from Audacity.
A MessageBoxException that shows a given, unvarying string.

References CurrConn(), Internal, OpenConnection(), and XO().

Referenced by DB().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ GetCurrentUsage()

int64_t ProjectFileIO::GetCurrentUsage ( const std::vector< const TrackList * > &  trackLists) const

Definition at line 2541 of file ProjectFileIO.cpp.

2543{
2544 unsigned long long current = 0;
2545 const auto fn = BlockSpaceUsageAccumulator(current);
2546
2547 // Must pass address of this set, even if not otherwise used, to avoid
2548 // possible multiple count of shared blocks
2549 SampleBlockIDSet seen;
2550 for (auto pTracks: trackLists)
2551 if (pTracks)
2552 InspectBlocks(*pTracks, fn, &seen);
2553
2554 return current;
2555}
std::function< void(const SampleBlock &) > BlockSpaceUsageAccumulator(unsigned long long &total)
Definition: SampleBlock.h:102
static const auto fn

References BlockSpaceUsageAccumulator(), fn, and InspectBlocks().

Here is the call graph for this function:

◆ GetDiskUsage()

int64_t ProjectFileIO::GetDiskUsage ( DBConnection conn,
SampleBlockID  blockid 
)
static

Definition at line 2570 of file ProjectFileIO.cpp.

2571{
2572 sqlite3_stmt* stmt = nullptr;
2573
2574 if (blockid == 0)
2575 {
2576 static const char* statement =
2577R"(SELECT
2578 sum(length(blockid) + length(sampleformat) +
2579 length(summin) + length(summax) + length(sumrms) +
2580 length(summary256) + length(summary64k) +
2581 length(samples))
2582FROM sampleblocks;)";
2583
2584 stmt = conn.Prepare(DBConnection::GetAllSampleBlocksSize, statement);
2585 }
2586 else
2587 {
2588 static const char* statement =
2589R"(SELECT
2590 length(blockid) + length(sampleformat) +
2591 length(summin) + length(summax) + length(sumrms) +
2592 length(summary256) + length(summary64k) +
2593 length(samples)
2594FROM sampleblocks WHERE blockid = ?1;)";
2595
2596 stmt = conn.Prepare(DBConnection::GetSampleBlockSize, statement);
2597 }
2598
2599 auto cleanup = finally(
2600 [stmt]() {
2601 // Clear statement bindings and rewind statement
2602 if (stmt != nullptr)
2603 {
2604 sqlite3_clear_bindings(stmt);
2605 sqlite3_reset(stmt);
2606 }
2607 });
2608
2609 if (blockid != 0)
2610 {
2611 int rc = sqlite3_bind_int64(stmt, 1, blockid);
2612
2613 if (rc != SQLITE_OK)
2614 {
2616 "sqlite3.rc", std::to_string(rc));
2617
2619 "sqlite3.context", "ProjectFileIO::GetDiskUsage::bind");
2620
2621 conn.ThrowException(false);
2622 }
2623 }
2624
2625 int rc = sqlite3_step(stmt);
2626
2627 if (rc != SQLITE_ROW)
2628 {
2629 ADD_EXCEPTION_CONTEXT("sqlite3.rc", std::to_string(rc));
2630
void ThrowException(bool write) const
throw and show appropriate message box
@ GetAllSampleBlocksSize
Definition: DBConnection.h:83
sqlite3_stmt * Prepare(enum StatementID id, const char *sql)

References ADD_EXCEPTION_CONTEXT, DBConnection::GetAllSampleBlocksSize, DBConnection::GetSampleBlockSize, DBConnection::Prepare(), and DBConnection::ThrowException().

Referenced by GetBlockUsage(), SqliteSampleBlock::GetSpaceUsage(), and GetTotalUsage().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ GetFileName()

const FilePath & ProjectFileIO::GetFileName ( ) const

Definition at line 1618 of file ProjectFileIO.cpp.

1619{
1620 return mFileName;
1621}

References mFileName.

Referenced by ProjectFileManager::IsAlreadyOpen(), ContrastDialog::OnExport(), OpenConnection(), TimerRecordDialog::PopulateOrExchange(), and ProjectFileManager::SaveAs().

Here is the caller graph for this function:

◆ GetFreeDiskSpace()

wxLongLong ProjectFileIO::GetFreeDiskSpace ( ) const

Definition at line 2424 of file ProjectFileIO.cpp.

2425{
2426 wxLongLong freeSpace;
2427 if (wxGetDiskSpace(wxPathOnly(mFileName), NULL, &freeSpace))
2428 {
2430 // 4 GiB per-file maximum
2431 constexpr auto limit = 1ll << 32;
2432
2433 // Opening a file only to find its length looks wasteful but
2434 // seems to be necessary at least on Windows with FAT filesystems.
2435 // I don't know if that is only a wxWidgets bug.
2436 auto length = wxFile{mFileName}.Length();
2437 // auto length = wxFileName::GetSize(mFileName);
2438
2439 if (length == wxInvalidSize)
2440 length = 0;
2441 auto free = std::max<wxLongLong>(0, limit - length);
2442 freeSpace = std::min(freeSpace, free);
2443 }
2444 return freeSpace;
2445 }
2446
2447 return -1;
2448}
int min(int a, int b)
FILES_API bool IsOnFATFileSystem(const FilePath &path)

References FileNames::IsOnFATFileSystem(), mFileName, and min().

Referenced by ProjectManager::GetEstimatedRecordingMinsLeftOnDisk(), and ProjectManager::OnTimer().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ GetLastError()

const TranslatableString & ProjectFileIO::GetLastError ( ) const

Definition at line 2463 of file ProjectFileIO.cpp.

2464{
2465 return mpErrors->mLastError;
2466}

References mpErrors.

Referenced by SaveProject().

Here is the caller graph for this function:

◆ GetLastErrorCode()

int ProjectFileIO::GetLastErrorCode ( ) const

Definition at line 2473 of file ProjectFileIO.cpp.

2474{
2475 return mpErrors->mErrorCode;
2476}

References mpErrors.

Referenced by CheckVersion().

Here is the caller graph for this function:

◆ GetLastLog()

const wxString & ProjectFileIO::GetLastLog ( ) const

Definition at line 2478 of file ProjectFileIO.cpp.

2479{
2480 return mpErrors->mLog;
2481}

References mpErrors.

Referenced by ShowError().

Here is the caller graph for this function:

◆ GetLibraryError()

const TranslatableString & ProjectFileIO::GetLibraryError ( ) const

Definition at line 2468 of file ProjectFileIO.cpp.

2469{
2470 return mpErrors->mLibraryError;
2471}

References mpErrors.

Referenced by CheckVersion().

Here is the caller graph for this function:

◆ GetProjectTitle()

const wxString & ProjectFileIO::GetProjectTitle ( ) const
inline

Definition at line 79 of file ProjectFileIO.h.

79{ return mTitle; }
wxString mTitle

Referenced by ProjectWindow::OnProjectTitleChange().

Here is the caller graph for this function:

◆ GetTotalUsage()

int64_t ProjectFileIO::GetTotalUsage ( )

Definition at line 2557 of file ProjectFileIO.cpp.

2558{
2559 auto pConn = CurrConn().get();
2560 if (!pConn)
2561 return 0;
2562 return GetDiskUsage(*pConn, 0);
2563}

References CurrConn(), and GetDiskUsage().

Referenced by ShouldCompact().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ GetValue() [1/2]

bool ProjectFileIO::GetValue ( const char *  sql,
int64_t &  value,
bool  silent = false 
)
private

Definition at line 742 of file ProjectFileIO.cpp.

743{
744 bool success = false;
745 auto cb = [&value, &success](int cols, char** vals, char**)
746 {
747 if (cols > 0)
748 {
749 const std::string_view valueString = vals[0];
750
751 success = std::errc() ==
752 FromChars(
753 valueString.data(), valueString.data() + valueString.length(),
754 value)
755 .ec;
756 }
757 // Stop after one row
758 return 1;
759 };
760
761 return Query(sql, cb, silent) && success;
762}
FromCharsResult FromChars(const char *buffer, const char *last, float &value) noexcept
Parse a string into a single precision floating point value, always uses the dot as decimal.
Definition: FromChars.cpp:153
std::errc ec
A pointer to the first character not matching the pattern.
Definition: FromChars.h:23

References FromCharsResult::ec, FromChars(), and Query().

Here is the call graph for this function:

◆ GetValue() [2/2]

bool ProjectFileIO::GetValue ( const char *  sql,
wxString &  value,
bool  silent = false 
)
private

Definition at line 728 of file ProjectFileIO.cpp.

729{
730 // Retrieve the first column in the first row, if any
731 result.clear();
732 auto cb = [&result](int cols, char **vals, char **){
733 if (cols > 0)
734 result = vals[0];
735 // Stop after one row
736 return 1;
737 };
738
739 return Query(sql, cb, silent);
740}

References Query().

Referenced by CheckVersion(), LoadProject(), and WriteDoc().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ HadUnused()

bool ProjectFileIO::HadUnused ( )

Definition at line 1569 of file ProjectFileIO.cpp.

1570{
1571 return mHadUnused;
1572}

References mHadUnused.

Referenced by SetBypass().

Here is the caller graph for this function:

◆ HandleXMLChild()

XMLTagHandler * ProjectFileIO::HandleXMLChild ( const std::string_view &  tag)
overrideprivatevirtual

Implements XMLTagHandler.

Definition at line 1730 of file ProjectFileIO.cpp.

1731{
1732 auto &project = mProject;
1733 return ProjectFileIORegistry::Get().CallObjectAccessor(tag, project);
1734}
XMLTagHandler * CallObjectAccessor(const std::string_view &tag, Host &host)
static XMLMethodRegistry & Get()
Get the unique instance.

References XMLMethodRegistry< Host >::CallObjectAccessor(), XMLMethodRegistry< Host >::Get(), and mProject.

Here is the call graph for this function:

◆ HandleXMLTag()

bool ProjectFileIO::HandleXMLTag ( const std::string_view &  tag,
const AttributesList attrs 
)
overrideprivatevirtual

Implements XMLTagHandler.

Definition at line 1651 of file ProjectFileIO.cpp.

1652{
1653 auto &project = mProject;
1654
1655 wxString fileVersion;
1656 wxString audacityVersion;
1657 int requiredTags = 0;
1658
1659 // loop through attrs, which is a null-terminated list of
1660 // attribute-value pairs
1661 for (auto pair : attrs)
1662 {
1663 auto attr = pair.first;
1664 auto value = pair.second;
1665
1667 .CallAttributeHandler( attr, project, value ) )
1668 continue;
1669
1670 else if (attr == "version")
1671 {
1672 fileVersion = value.ToWString();
1673 requiredTags++;
1674 }
1675
1676 else if (attr == "audacityversion")
1677 {
1678 audacityVersion = value.ToWString();
1679 requiredTags++;
1680 }
1681 } // while
1682
1683 if (requiredTags < 2)
1684 {
1685 return false;
1686 }
1687
1688 // Parse the file version from the project
1689 int fver;
1690 int frel;
1691 int frev;
1692 if (!wxSscanf(fileVersion, wxT("%i.%i.%i"), &fver, &frel, &frev))
1693 {
1694 return false;
1695 }
1696
1697 // Parse the file version Audacity was build with
1698 int cver;
1699 int crel;
1700 int crev;
1701 wxSscanf(wxT(AUDACITY_FILE_FORMAT_VERSION), wxT("%i.%i.%i"), &cver, &crel, &crev);
1702
1703 int fileVer = ((fver *100)+frel)*100+frev;
1704 int codeVer = ((cver *100)+crel)*100+crev;
1705
1706 if (codeVer<fileVer)
1707 {
1708 /* i18n-hint: %s will be replaced by the version number.*/
1709 auto msg = XO("This file was saved using Audacity %s.\nYou are using Audacity %s. You may need to upgrade to a newer version to open this file.")
1710 .Format(audacityVersion, AUDACITY_VERSION_STRING);
1711
1712 ShowError( *ProjectFramePlacement(&project),
1713 XO("Can't open project file"),
1714 msg,
1715 "FAQ:Errors_opening_an_Audacity_project"
1716 );
1717
1718 return false;
1719 }
1720
1721 if (tag != "project")
1722 {
1723 return false;
1724 }
1725
1726 // All other tests passed, so we succeed
1727 return true;
1728}
std::unique_ptr< const BasicUI::WindowPlacement > ProjectFramePlacement(AudacityProject *project)
Make a WindowPlacement object suitable for project (which may be null)
Definition: Project.cpp:129
#define AUDACITY_FILE_FORMAT_VERSION
void ShowError(const BasicUI::WindowPlacement &placement, const TranslatableString &dlogTitle, const TranslatableString &message, const wxString &helpPage)
Displays an error dialog with a button that offers help.

References AUDACITY_FILE_FORMAT_VERSION, XMLMethodRegistry< Host >::Get(), mProject, ProjectFramePlacement(), ShowError(), wxT(), and XO().

Here is the call graph for this function:

◆ HasConnection()

bool ProjectFileIO::HasConnection ( ) const

Return true if a connection is now open.

Definition at line 479 of file ProjectFileIO.cpp.

480{
481 auto &connectionPtr = ConnectionPtr::Get( mProject );
482 return connectionPtr.mpConnection != nullptr;
483}

References ConnectionPtr::Get(), and mProject.

Here is the call graph for this function:

◆ InitializeSQL()

bool ProjectFileIO::InitializeSQL ( )
static

Definition at line 420 of file ProjectFileIO.cpp.

421{
422 static SQLiteIniter sqliteIniter;
423 return sqliteIniter.mRc == SQLITE_OK;
424}

References SQLiteIniter::mRc.

Referenced by AudacityApp::OnInit0().

Here is the caller graph for this function:

◆ InSet()

void ProjectFileIO::InSet ( sqlite3_context *  context,
int  argc,
sqlite3_value **  argv 
)
staticprivate

Definition at line 855 of file ProjectFileIO.cpp.

856{
857 BlockIDs *blockids = (BlockIDs *) sqlite3_user_data(context);
858 SampleBlockID blockid = sqlite3_value_int64(argv[0]);
859
860 sqlite3_result_int(context, blockids->find(blockid) != blockids->end());
861}
std::unordered_set< SampleBlockID > BlockIDs
Definition: ProjectFileIO.h:46
UTILITY_API const char *const * argv
A copy of argv; responsibility of application startup to assign it.

References CommandLineArgs::argv.

Referenced by DeleteBlocks().

Here is the caller graph for this function:

◆ InstallSchema()

bool ProjectFileIO::InstallSchema ( sqlite3 *  db,
const char *  schema = "main" 
)
private

Definition at line 829 of file ProjectFileIO.cpp.

830{
831 int rc;
832
833 wxString sql;
835 sql.Replace("<schema>", schema);
836
837 rc = sqlite3_exec(db, sql, nullptr, nullptr, nullptr);
838 if (rc != SQLITE_OK)
839 {
841 XO("Unable to initialize the project file")
842 );
843 return false;
844 }
845
846 return true;
847}
static const char * ProjectFileSchema
const ProjectFormatVersion BaseProjectFormatVersion
This is a helper constant for the "most compatible" project version with the value (3,...
uint32_t GetPacked() const noexcept
Returns a version packed to 32-bit integer.

References BaseProjectFormatVersion, ProjectFormatVersion::GetPacked(), ProjectFileID, ProjectFileSchema, SetDBError(), and XO().

Referenced by CheckVersion(), and CopyTo().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ IsModified()

bool ProjectFileIO::IsModified ( ) const

Definition at line 2409 of file ProjectFileIO.cpp.

2410{
2411 return mModified;
2412}

References mModified.

Referenced by Compact(), and anonymous_namespace{TimerRecordDialog.cpp}::OnTimerRecord().

Here is the caller graph for this function:

◆ IsRecovered()

bool ProjectFileIO::IsRecovered ( ) const

Definition at line 2419 of file ProjectFileIO.cpp.

2420{
2421 return mRecovered;
2422}

References mRecovered.

◆ IsTemporary()

bool ProjectFileIO::IsTemporary ( ) const

Definition at line 2414 of file ProjectFileIO.cpp.

2415{
2416 return mTemporary;
2417}

References mTemporary.

Referenced by CloseProject(), Compact(), SaveProject(), SetBypass(), and SetFileName().

Here is the caller graph for this function:

◆ LoadProject()

bool ProjectFileIO::LoadProject ( const FilePath fileName,
bool  ignoreAutosave 
)

Definition at line 2007 of file ProjectFileIO.cpp.

2008{
2009 auto now = std::chrono::high_resolution_clock::now();
2010
2011 bool success = false;
2012
2013 auto cleanup = finally([&]
2014 {
2015 if (!success)
2016 {
2018 }
2019 });
2020
2022
2023 // Open the project file
2024 if (!OpenConnection(fileName))
2025 {
2026 return false;
2027 }
2028
2029 int64_t rowId = -1;
2030
2031 bool useAutosave =
2032 !ignoreAutosave &&
2033 GetValue("SELECT ROWID FROM main.autosave WHERE id = 1;", rowId, true);
2034
2035 int64_t rowsCount = 0;
2036 // If we didn't have an autosave doc, load the project doc instead
2037 if (
2038 !useAutosave &&
2039 (!GetValue("SELECT COUNT(1) FROM main.project;", rowsCount, true) || rowsCount == 0))
2040 {
2041 // Missing both the autosave and project docs. This can happen if the
2042 // system were to crash before the first autosave into a temporary file.
2043 // This should be a recoverable scenario.
2044 mRecovered = true;
2045 mModified = true;
2046
2047 return true;
2048 }
2049
2050 if (!useAutosave && !GetValue("SELECT ROWID FROM main.project WHERE id = 1;", rowId, false))
2051 {
2052 return false;
2053 }
2054 else
2055 {
2056 // Load 'er up
2058 DB(), "main", useAutosave ? "autosave" : "project", rowId);
2059
2060 success = ProjectSerializer::Decode(stream, this);
2061
2062 if (!success)
2063 {
2064 SetError(
2065 XO("Unable to parse project information.")
2066 );
2067 return false;
2068 }
2069
2070 // Check for orphans blocks...sets mRecovered if any were deleted
2071
2072 auto blockids = WaveTrackFactory::Get( mProject )
2074 ->GetActiveBlockIDs();
2075 if (blockids.size() > 0)
2076 {
2077 success = DeleteBlocks(blockids, true);
2078 if (!success)
2079 {
2080 return false;
2081 }
2082 }
2083
2084 // Remember if we used autosave or not
2085 if (useAutosave)
2086 {
2087 mRecovered = true;
2088 }
2089 }
2090
2091 // Mark the project modified if we recovered it
2092 if (mRecovered)
2093 {
2094 mModified = true;
2095 }
2096
2097 // A previously saved project will have a document in the project table, so
2098 // we use that knowledge to determine if this file is an unsaved/temporary
2099 // file or a permanent project file
2100 wxString result;
2101 success = GetValue("SELECT Count(*) FROM project;", result);
2102 if (!success)
2103 {
2104 return false;
2105 }
2106
2107 mTemporary = !result.IsSameAs(wxT("1"));
2108
2109 SetFileName(fileName);
2110
2112
2113 success = true;
2114
2115 auto duration = std::chrono::high_resolution_clock::now() - now;
2116
2117 wxLogInfo(
2118 "Project loaded in %lld ms",
2119 std::chrono::duration_cast<std::chrono::milliseconds>(duration).count());
2120
2121 return true;
2122}
void RestoreConnection()
void DiscardConnection()
bool DeleteBlocks(const BlockIDs &blockids, bool complement)
static bool Decode(BufferedStreamReader &in, XMLTagHandler *handler)
static WaveTrackFactory & Get(AudacityProject &project)
Definition: WaveTrack.cpp:2536
const SampleBlockFactoryPtr & GetSampleBlockFactory() const
Definition: WaveTrack.h:582

References DB(), ProjectSerializer::Decode(), DeleteBlocks(), DiscardConnection(), WaveTrackFactory::Get(), WaveTrackFactory::GetSampleBlockFactory(), GetValue(), mModified, mProject, mRecovered, mTemporary, OpenConnection(), RestoreConnection(), SaveConnection(), SetError(), SetFileName(), wxT(), and XO().

Here is the call graph for this function:

◆ MoveProject()

bool ProjectFileIO::MoveProject ( const FilePath src,
const FilePath dst 
)
private

Definition at line 1342 of file ProjectFileIO.cpp.

1343{
1344 // Assume the src database file is not busy.
1345 if (!RenameOrWarn(src, dst))
1346 return false;
1347
1348 // So far so good, but the separate -wal and -shm files might yet exist,
1349 // as when checkpointing failed for limited space on the drive.
1350 // If so move them too or else lose data.
1351
1352 std::vector< std::pair<FilePath, FilePath> > pairs{ { src, dst } };
1353 bool success = false;
1354 auto cleanup = finally([&]{
1355 if (!success) {
1356 // If any one of the renames failed, back out the previous ones.
1357 // This should be a no-fail recovery! Not clear what to do if any
1358 // of these renames fails.
1359 for (auto &pair : pairs) {
1360 if (!(pair.first.empty() && pair.second.empty()))
1361 wxRenameFile(pair.second, pair.first);
1362 }
1363 }
1364 });
1365
1366 for (const auto &suffix : AuxiliaryFileSuffixes()) {
1367 auto srcName = src + suffix;
1368 if (wxFileExists(srcName)) {
1369 auto dstName = dst + suffix;
1370 if (!RenameOrWarn(srcName, dstName))
1371 return false;
1372 pairs.push_back({ srcName, dstName });
1373 }
1374 }
1375
1376 return (success = true);
1377}
bool RenameOrWarn(const FilePath &src, const FilePath &dst)
Rename a file or put up appropriate warning message.
static const std::vector< wxString > & AuxiliaryFileSuffixes()

References AuxiliaryFileSuffixes(), and RenameOrWarn().

Referenced by ProjectFileIO::BackupProject::BackupProject(), and SaveProject().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ OnCheckpointFailure()

void ProjectFileIO::OnCheckpointFailure ( )
private

Definition at line 1736 of file ProjectFileIO.cpp.

1737{
1738 // DBConnection promises to invoke this in main thread idle time
1739 // So we don't need a redundant CallAfter to satisfy our own promise
1741}
@ CheckpointFailure
Failure happened in a worker thread.
CallbackReturn Publish(const ProjectFileIOMessage &message)
Send a message to connected callbacks.
Definition: Observer.h:207

References CheckpointFailure, and Observer::Publisher< ProjectFileIOMessage >::Publish().

Here is the call graph for this function:

◆ OpenConnection()

bool ProjectFileIO::OpenConnection ( FilePath  fileName = {})
private
Precondition
*CurConn() does not exist
Postcondition
*CurConn() exists or return value is false

Definition at line 524 of file ProjectFileIO.cpp.

525{
526 auto &curConn = CurrConn();
527 wxASSERT(!curConn);
528 bool isTemp = false;
529
530 if (fileName.empty())
531 {
532 fileName = GetFileName();
533 if (fileName.empty())
534 {
536 isTemp = true;
537 }
538 }
539 else
540 {
541 // If this project resides in the temporary directory, then we'll mark it
542 // as temporary.
543 wxFileName temp(TempDirectory::TempDir(), wxT(""));
544 wxFileName file(fileName);
545 file.SetFullName(wxT(""));
546 if (file == temp)
547 {
548 isTemp = true;
549 }
550 }
551
552 // Pass weak_ptr to project into DBConnection constructor
553 curConn = std::make_unique<DBConnection>(
554 mProject.shared_from_this(), mpErrors, [this]{ OnCheckpointFailure(); } );
555 auto rc = curConn->Open(fileName);
556 if (rc != SQLITE_OK)
557 {
558 // Must use SetError() here since we do not have an active DB
559 SetError(
560 XO("Failed to open database file:\n\n%s").Format(fileName),
561 {},
562 rc
563 );
564 curConn.reset();
565 return false;
566 }
567
568 if (!CheckVersion())
569 {
571 curConn.reset();
572 return false;
573 }
574
575 mTemporary = isTemp;
576
577 SetFileName(fileName);
578
579 return true;
580}
const FilePath & GetFileName() const
FILES_API wxString UnsavedProjectFileName()

References CheckVersion(), CloseConnection(), CurrConn(), GetFileName(), mpErrors, mProject, mTemporary, SetError(), SetFileName(), TempDirectory::TempDir(), TempDirectory::UnsavedProjectFileName(), wxT(), and XO().

Referenced by Compact(), GetConnection(), LoadProject(), OpenProject(), ReopenProject(), and SaveProject().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ OpenProject()

bool ProjectFileIO::OpenProject ( )

Definition at line 2361 of file ProjectFileIO.cpp.

2362{
2363 return OpenConnection();
2364}

References OpenConnection().

Here is the call graph for this function:

◆ operator=()

ProjectFileIO & ProjectFileIO::operator= ( const ProjectFileIO )
delete

◆ Query()

bool ProjectFileIO::Query ( const char *  sql,
const ExecCB callback,
bool  silent = false 
)
private

Definition at line 715 of file ProjectFileIO.cpp.

716{
717 int rc = Exec(sql, callback, silent);
718 // SQLITE_ABORT is a non-error return only meaning the callback
719 // stopped the iteration of rows early
720 if ( !(rc == SQLITE_OK || rc == SQLITE_ABORT) )
721 {
722 return false;
723 }
724
725 return true;
726}
int Exec(const char *query, const ExecCB &callback, bool silent=false)

References Exec().

Referenced by CopyTo(), GetValue(), ShouldCompact(), and WriteDoc().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ RemoveProject()

bool ProjectFileIO::RemoveProject ( const FilePath filename)
static

Remove any files associated with a project at given path; return true if successful.

Definition at line 1379 of file ProjectFileIO.cpp.

1380{
1381 if (!wxFileExists(filename))
1382 return false;
1383
1384 bool success = wxRemoveFile(filename);
1385 auto &suffixes = AuxiliaryFileSuffixes();
1386 for (const auto &suffix : suffixes) {
1387 auto file = filename + suffix;
1388 if (wxFileExists(file))
1389 success = wxRemoveFile(file) && success;
1390 }
1391 return success;
1392}

References AuxiliaryFileSuffixes().

Referenced by CloseProject(), ProjectFileIO::BackupProject::Discard(), DiscardConnection(), and AutoRecoveryDialog::OnDiscardSelected().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ RenameOrWarn()

bool ProjectFileIO::RenameOrWarn ( const FilePath src,
const FilePath dst 
)
private

Rename a file or put up appropriate warning message.

Failure might happen when renaming onto another device, doing copy of contents

Definition at line 1301 of file ProjectFileIO.cpp.

1302{
1303 std::atomic_bool done = {false};
1304 bool success = false;
1305 auto thread = std::thread([&]
1306 {
1307 success = wxRenameFile(src, dst);
1308 done = true;
1309 });
1310
1311 // Provides a progress dialog with indeterminate mode
1312 using namespace BasicUI;
1314 XO("Copying Project"), XO("This may take several seconds"));
1315 wxASSERT(pd);
1316
1317 // Wait for the checkpoints to end
1318 while (!done)
1319 {
1320 using namespace std::chrono;
1321 std::this_thread::sleep_for(50ms);
1322 pd->Pulse();
1323 }
1324 thread.join();
1325
1326 if (!success)
1327 {
1329 XO("Error Writing to File"),
1330 XO("Audacity failed to write file %s.\n"
1331 "Perhaps disk is full or not writable.\n"
1332 "For tips on freeing up space, click the help button.")
1333 .Format(dst),
1334 "Error:_Disk_full_or_not_writable"
1335 );
1336 return false;
1337 }
1338
1339 return true;
1340}
std::unique_ptr< GenericProgressDialog > MakeGenericProgress(const WindowPlacement &placement, const TranslatableString &title, const TranslatableString &message)
Create and display a progress dialog (return nullptr if Services not installed)
Definition: BasicUI.h:307

References BasicUI::MakeGenericProgress(), mProject, ProjectFramePlacement(), ShowError(), and XO().

Referenced by MoveProject().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ ReopenProject()

bool ProjectFileIO::ReopenProject ( )

Definition at line 2398 of file ProjectFileIO.cpp.

2399{
2400 FilePath fileName = mFileName;
2401 if (!CloseConnection())
2402 {
2403 return false;
2404 }
2405
2406 return OpenConnection(fileName);
2407}
wxString FilePath
Definition: Project.h:21

References CloseConnection(), mFileName, and OpenConnection().

Here is the call graph for this function:

◆ RestoreConnection()

void ProjectFileIO::RestoreConnection ( )
private

Definition at line 648 of file ProjectFileIO.cpp.

649{
650 auto &curConn = CurrConn();
651 if (curConn)
652 {
653 if (!curConn->Close())
654 {
655 // Store an error message
657 XO("Failed to restore connection")
658 );
659 }
660 }
661
662 curConn = std::move(mPrevConn);
665
666 mPrevFileName.clear();
667}

References CurrConn(), mPrevConn, mPrevFileName, mPrevTemporary, mTemporary, SetDBError(), SetFileName(), and XO().

Referenced by LoadProject().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ SafetyFileName()

FilePath ProjectFileIO::SafetyFileName ( const FilePath src)
staticprivate

Generate a name for short-lived backup project files from an existing project.

Definition at line 1266 of file ProjectFileIO.cpp.

1267{
1268 wxFileNameWrapper fn{ src };
1269
1270 // Extra characters inserted into filename before extension
1271 wxString extra =
1272#ifdef __WXGTK__
1273 wxT("~")
1274#else
1275 wxT(".bak")
1276#endif
1277 ;
1278
1279 int nn = 1;
1280 auto numberString = [](int num) -> wxString {
1281 return num == 1 ? wxString{} : wxString::Format(".%d", num);
1282 };
1283
1284 auto suffixes = AuxiliaryFileSuffixes();
1285 suffixes.push_back({});
1286
1287 // Find backup paths not already occupied; check all auxiliary suffixes
1288 const auto name = fn.GetName();
1289 FilePath result;
1290 do {
1291 fn.SetName( name + numberString(nn++) + extra );
1292 result = fn.GetFullPath();
1293 }
1294 while( std::any_of(suffixes.begin(), suffixes.end(), [&](auto &suffix){
1295 return wxFileExists(result + suffix);
1296 }) );
1297
1298 return result;
1299}
const TranslatableString name
Definition: Distortion.cpp:76

References AuxiliaryFileSuffixes(), fn, name, and wxT().

Referenced by ProjectFileIO::BackupProject::BackupProject().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ SaveConnection()

void ProjectFileIO::SaveConnection ( )
private

Definition at line 601 of file ProjectFileIO.cpp.

602{
603 // Should do nothing in proper usage, but be sure not to leak a connection:
605
606 mPrevConn = std::move(CurrConn());
609
610 SetFileName({});
611}

References CurrConn(), DiscardConnection(), mFileName, mPrevConn, mPrevFileName, mPrevTemporary, mTemporary, and SetFileName().

Referenced by LoadProject().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ SaveCopy()

bool ProjectFileIO::SaveCopy ( const FilePath fileName)

Definition at line 2355 of file ProjectFileIO.cpp.

2356{
2357 return CopyTo(fileName, XO("Backing up project"), false, true,
2359}

References CopyTo(), TrackList::Get(), mProject, and XO().

Here is the call graph for this function:

◆ SaveProject()

bool ProjectFileIO::SaveProject ( const FilePath fileName,
const TrackList lastSaved 
)

Definition at line 2147 of file ProjectFileIO.cpp.

2149{
2150 // In the case where we're saving a temporary project to a permanent project,
2151 // we'll try to simply rename the project to save a bit of time. We then fall
2152 // through to the normal Save (not SaveAs) processing.
2153 if (IsTemporary() && mFileName != fileName)
2154 {
2155 FilePath savedName = mFileName;
2156 if (CloseConnection())
2157 {
2158 bool reopened = false;
2159 bool moved = false;
2160 if (true == (moved = MoveProject(savedName, fileName)))
2161 {
2162 if (OpenConnection(fileName))
2163 reopened = true;
2164 else {
2165 MoveProject(fileName, savedName);
2166 moved = false; // No longer moved
2167
2168 reopened = OpenConnection(savedName);
2169 }
2170 }
2171 else {
2172 // Rename can fail -- if it's to a different device, requiring
2173 // real copy of contents, which might exhaust space
2174 reopened = OpenConnection(savedName);
2175 }
2176
2177 // Warning issued in MoveProject()
2178 if (reopened && !moved) {
2179 return false;
2180 }
2181
2182 if (!reopened) {
2183 BasicUI::CallAfter([this]{
2184 ShowError( {},
2185 XO("Warning"),
2186 XO(
2187"The project's database failed to reopen, "
2188"possibly because of limited space on the storage device."),
2189 "Error:_Disk_full_or_not_writable"
2190 );
2192 });
2193
2194 return false;
2195 }
2196 }
2197 }
2198
2199 // If we're saving to a different file than the current one, then copy the
2200 // current to the new file and make it the active file.
2201 if (mFileName != fileName)
2202 {
2203 // Do NOT prune here since we need to retain the Undo history
2204 // after we switch to the new file.
2205 if (!CopyTo(fileName, XO("Saving project"), false))
2206 {
2207 ShowError( {},
2208 XO("Error Saving Project"),
2210 "Error:_Disk_full_or_not_writable"
2211 );
2212 return false;
2213 }
2214
2215 // Open the newly created database
2216 Connection newConn = std::make_unique<DBConnection>(
2217 mProject.shared_from_this(), mpErrors,
2218 [this]{ OnCheckpointFailure(); });
2219
2220 // NOTE: There is a noticeable delay here when dealing with large multi-hour
2221 // projects that we just created. The delay occurs in Open() when it
2222 // calls SafeMode() and is due to the switch from the NONE journal mode
2223 // to the WAL journal mode.
2224 //
2225 // So, we do the Open() in a thread and display a progress dialog. Since
2226 // this is currently the only known instance where this occurs, we do the
2227 // threading here. If more instances are identified, then the threading
2228 // should be moved to DBConnection::Open(), wrapping the SafeMode() call
2229 // there.
2230 {
2231 std::atomic_bool done = {false};
2232 bool success = true;
2233 auto thread = std::thread([&]
2234 {
2235 auto rc = newConn->Open(fileName);
2236 if (rc != SQLITE_OK)
2237 {
2238 // Capture the error string
2239 SetError(Verbatim(sqlite3_errstr(rc)));
2240 success = false;
2241 }
2242 done = true;
2243 });
2244
2245 // Provides a progress dialog with indeterminate mode
2246 using namespace BasicUI;
2247 auto pd = MakeGenericProgress({},
2248 XO("Syncing"), XO("This may take several seconds"));
2249 wxASSERT(pd);
2250
2251 // Wait for the checkpoints to end
2252 while (!done)
2253 {
2254 using namespace std::chrono;
2255 std::this_thread::sleep_for(50ms);
2256 pd->Pulse();
2257 }
2258 thread.join();
2259
2260 if (!success)
2261 {
2262 // Additional help via a Help button links to the manual.
2263 ShowError( {},
2264 XO("Error Saving Project"),
2265 XO("The project failed to open, possibly due to limited space\n"
2266 "on the storage device.\n\n%s").Format(GetLastError()),
2267 "Error:_Disk_full_or_not_writable");
2268
2269 newConn = nullptr;
2270
2271 // Clean up the destination project
2272 if (!wxRemoveFile(fileName))
2273 {
2274 wxLogMessage("Failed to remove destination project after open failure: %s", fileName);
2275 }
2276
2277 return false;
2278 }
2279 }
2280
2281 // Autosave no longer needed in original project file.
2282 if (!AutoSaveDelete())
2283 {
2284 // Additional help via a Help button links to the manual.
2285 ShowError( {},
2286 XO("Error Saving Project"),
2287 XO("Unable to remove autosave information, possibly due to limited space\n"
2288 "on the storage device.\n\n%s").Format(GetLastError()),
2289 "Error:_Disk_full_or_not_writable");
2290
2291 newConn = nullptr;
2292
2293 // Clean up the destination project
2294 if (!wxRemoveFile(fileName))
2295 {
2296 wxLogMessage("Failed to remove destination project after AutoSaveDelete failure: %s", fileName);
2297 }
2298
2299 return false;
2300 }
2301
2302 if (lastSaved) {
2303 // Bug2605: Be sure not to save orphan blocks
2304 bool recovered = mRecovered;
2305 SampleBlockIDSet blockids;
2306 InspectBlocks( *lastSaved, {}, &blockids );
2307 // TODO: Not sure what to do if the deletion fails
2308 DeleteBlocks(blockids, true);
2309 // Don't set mRecovered if any were deleted
2310 mRecovered = recovered;
2311 }
2312
2313 // Try to compact the original project file.
2314 auto empty = TrackList::Create(&mProject);
2315 Compact( { lastSaved ? lastSaved : empty.get() }, true );
2316
2317 // Safe to close the original project file now. Not much we can do if this fails,
2318 // but we should still be in good shape since we'll be switching to the newly
2319 // saved database below.
2320 CloseProject();
2321
2322 // And make it the active project file
2323 UseConnection(std::move(newConn), fileName);
2324 }
2325 else
2326 {
2327 if ( !UpdateSaved( nullptr ) ) {
2328 ShowError( {},
2329 XO("Error Saving Project"),
2331 "Error:_Disk_full_or_not_writable"
2332 );
2333 return false;
2334 }
2335 }
2336
2337 // Reaching this point defines success and all the rest are no-fail
2338 // operations:
2339
2340 // No longer modified
2341 mModified = false;
2342
2343 // No longer recovered
2344 mRecovered = false;
2345
2346 // No longer a temporary project
2347 mTemporary = false;
2348
2349 // Adjust the title
2351
2352 return true;
2353}
static TranslatableString WriteFailureMessage(const wxFileName &fileName)
bool MoveProject(const FilePath &src, const FilePath &dst)
bool UpdateSaved(const TrackList *tracks=nullptr)
void UseConnection(Connection &&conn, const FilePath &filePath)
void Compact(const std::vector< const TrackList * > &tracks, bool force=false)
const TranslatableString & GetLastError() const
static std::shared_ptr< TrackList > Create(AudacityProject *pOwner)
Definition: Track.cpp:503
void CallAfter(Action action)
Schedule an action to be done later, and in the main thread.
Definition: BasicUI.cpp:208

References AutoSaveDelete(), BasicUI::CallAfter(), CloseConnection(), CloseProject(), Compact(), CopyTo(), TrackList::Create(), DeleteBlocks(), GetLastError(), InspectBlocks(), IsTemporary(), BasicUI::MakeGenericProgress(), mFileName, mModified, MoveProject(), mpErrors, mProject, mRecovered, mTemporary, OpenConnection(), Observer::Publisher< ProjectFileIOMessage >::Publish(), ReconnectionFailure, SetError(), SetProjectTitle(), ShowError(), UpdateSaved(), UseConnection(), Verbatim(), FileException::WriteFailureMessage(), and XO().

Here is the call graph for this function:

◆ SetBypass()

void ProjectFileIO::SetBypass ( )

Definition at line 2499 of file ProjectFileIO.cpp.

2500{
2501 auto &currConn = CurrConn();
2502 if (!currConn)
2503 return;
2504
2505 // Determine if we can bypass sample block deletes during shutdown.
2506 //
2507 // IMPORTANT:
2508 // If the project was compacted, then we MUST bypass further
2509 // deletions since the new file doesn't have the blocks that the
2510 // Sequences expect to be there.
2511
2512 currConn->SetBypass( true );
2513
2514 // Only permanent project files need cleaning at shutdown
2515 if (!IsTemporary() && !WasCompacted())
2516 {
2517 // If we still have unused blocks, then we must not bypass deletions
2518 // during shutdown. Otherwise, we would have orphaned blocks the next time
2519 // the project is opened.
2520 //
2521 // An example of when dead blocks will exist is when a user opens a permanent
2522 // project, adds a track (with samples) to it, and chooses not to save the
2523 // changes.
2524 if (HadUnused())
2525 {
2526 currConn->SetBypass( false );
2527 }
2528 }
2529
2530 return;
2531}

References CurrConn(), HadUnused(), IsTemporary(), and WasCompacted().

Here is the call graph for this function:

◆ SetDBError()

void ProjectFileIO::SetDBError ( const TranslatableString msg,
const TranslatableString libraryError = {},
int  errorCode = -1 
)
private

Set stored errors and write to log; and default libraryError to what database library reports.

Definition at line 2491 of file ProjectFileIO.cpp.

2493{
2494 auto &currConn = CurrConn();
2495 if (currConn)
2496 currConn->SetDBError(msg, libraryError, errorCode);
2497}

References CurrConn().

Referenced by AutoSaveDelete(), CopyTo(), DeleteBlocks(), DiscardConnection(), Exec(), InstallSchema(), RestoreConnection(), and WriteDoc().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ SetError()

void ProjectFileIO::SetError ( const TranslatableString msg,
const TranslatableString libraryError = {},
int  errorCode = {} 
)
private

Just set stored errors.

Definition at line 2483 of file ProjectFileIO.cpp.

2485{
2486 auto &currConn = CurrConn();
2487 if (currConn)
2488 currConn->SetError(msg, libraryError, errorCode);
2489}

References CurrConn().

Referenced by CheckVersion(), LoadProject(), OpenConnection(), and SaveProject().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ SetFileName()

void ProjectFileIO::SetFileName ( const FilePath fileName)

Definition at line 1623 of file ProjectFileIO.cpp.

1624{
1625 auto &project = mProject;
1626
1627 if (!mFileName.empty())
1628 {
1630 }
1631
1632 mFileName = fileName;
1633
1634 if (!mFileName.empty())
1635 {
1637 }
1638
1639 if (IsTemporary())
1640 {
1641 project.SetProjectName({});
1642 }
1643 else
1644 {
1645 project.SetProjectName(wxFileName(mFileName).GetName());
1646 }
1647
1649}
PROJECT_FILE_IO_API void Remove(const FilePath &path)
PROJECT_FILE_IO_API void Add(const FilePath &path)

References ActiveProjects::Add(), IsTemporary(), mFileName, mProject, ActiveProjects::Remove(), and SetProjectTitle().

Referenced by CloseConnection(), LoadProject(), OpenConnection(), RestoreConnection(), SaveConnection(), and UseConnection().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ SetProjectTitle()

void ProjectFileIO::SetProjectTitle ( int  number = -1)

Definition at line 1580 of file ProjectFileIO.cpp.

1581{
1582 auto &project = mProject;
1583 wxString name = project.GetProjectName();
1584
1585 // If we are showing project numbers, then we also explicitly show "<untitled>" if there
1586 // is none.
1587 if (number >= 0)
1588 {
1589 name =
1590 /* i18n-hint: The %02i is the project number, the %s is the project name.*/
1591 XO("[Project %02i] Audacity \"%s\"")
1592 .Format( number + 1,
1593 name.empty() ? XO("<untitled>") : Verbatim((const char *)name))
1594 .Translation();
1595 }
1596 // If we are not showing numbers, then <untitled> shows as 'Audacity'.
1597 else if (name.empty())
1598 {
1599 name = _TS("Audacity");
1600 }
1601
1602 if (mRecovered)
1603 {
1604 name += wxT(" ");
1605 /* i18n-hint: E.g this is recovered audio that had been lost.*/
1606 name += _("(Recovered)");
1607 }
1608
1609 if (name != mTitle) {
1610 mTitle = name;
1611 BasicUI::CallAfter( [wThis = weak_from_this()]{
1612 if (auto pThis = wThis.lock())
1614 } );
1615 }
1616}
#define _TS(s)
Definition: Internat.h:27
#define _(s)
Definition: Internat.h:73
@ ProjectTitleChange
A normal occurrence.

References _, _TS, BasicUI::CallAfter(), TranslatableString::empty(), mProject, mRecovered, mTitle, name, ProjectTitleChange, Verbatim(), wxT(), and XO().

Referenced by ProjectFileIO(), RefreshAllTitles(), SaveProject(), SetFileName(), and UpdatePrefs().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ ShouldCompact()

bool ProjectFileIO::ShouldCompact ( const std::vector< const TrackList * > &  tracks)
private

Definition at line 1201 of file ProjectFileIO.cpp.

1202{
1203 SampleBlockIDSet active;
1204 unsigned long long current = 0;
1205
1206 {
1207 auto fn = BlockSpaceUsageAccumulator( current );
1208 for (auto pTracks : tracks)
1209 if (pTracks)
1210 InspectBlocks( *pTracks, fn,
1211 &active // Visit unique blocks only
1212 );
1213 }
1214
1215 // Get the number of blocks and total length from the project file.
1216 unsigned long long total = GetTotalUsage();
1217 unsigned long long blockcount = 0;
1218
1219 auto cb = [&blockcount](int cols, char **vals, char **)
1220 {
1221 // Convert
1222 wxString(vals[0]).ToULongLong(&blockcount);
1223 return 0;
1224 };
1225
1226 if (!Query("SELECT Count(*) FROM sampleblocks;", cb) || blockcount == 0)
1227 {
1228 // Shouldn't compact since we don't have the full picture
1229 return false;
1230 }
1231
1232 // Remember if we had unused blocks in the project file
1233 mHadUnused = (blockcount > active.size());
1234
1235 // Let's make a percentage...should be plenty of head room
1236 current *= 100;
1237
1238 wxLogDebug(wxT("used = %lld total = %lld %lld"), current, total, total ? current / total : 0);
1239 if (!total || current / total > 80)
1240 {
1241 wxLogDebug(wxT("not compacting"));
1242 return false;
1243 }
1244 wxLogDebug(wxT("compacting"));
1245
1246 return true;
1247}
int64_t GetTotalUsage()

References BlockSpaceUsageAccumulator(), fn, GetTotalUsage(), InspectBlocks(), mHadUnused, Query(), and wxT().

Referenced by Compact().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ ShowError()

void ProjectFileIO::ShowError ( const BasicUI::WindowPlacement placement,
const TranslatableString dlogTitle,
const TranslatableString message,
const wxString &  helpPage 
)

Displays an error dialog with a button that offers help.

Definition at line 2451 of file ProjectFileIO.cpp.

2455{
2456 using namespace audacity;
2457 using namespace BasicUI;
2458 ShowErrorDialog( placement, dlogTitle, message, helpPage,
2459 ErrorDialogOptions{ ErrorDialogType::ModalErrorReport }
2460 .Log(ToWString(GetLastLog())));
2461}
const wxString & GetLastLog() const
std::wstring ToWString(const std::string &str)
Options for variations of error dialogs; the default is for modal dialogs.
Definition: BasicUI.h:51
ErrorDialogOptions && Log(std::wstring log_) &&
Definition: BasicUI.h:63

References GetLastLog(), BasicUI::ErrorDialogOptions::Log(), BasicUI::ShowErrorDialog(), and audacity::ToWString().

Referenced by HandleXMLTag(), RenameOrWarn(), and SaveProject().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ UpdatePrefs()

void ProjectFileIO::UpdatePrefs ( )
overrideprivatevirtual

Implements PrefsListener.

Definition at line 1574 of file ProjectFileIO.cpp.

1575{
1577}

References SetProjectTitle().

Here is the call graph for this function:

◆ UpdateSaved()

bool ProjectFileIO::UpdateSaved ( const TrackList tracks = nullptr)

Definition at line 2124 of file ProjectFileIO.cpp.

2125{
2127 WriteXMLHeader(doc);
2128 WriteXML(doc, false, tracks);
2129
2130 if (!WriteDoc("project", doc))
2131 {
2132 return false;
2133 }
2134
2135 // Autosave no longer needed
2136 if (!AutoSaveDelete())
2137 {
2138 return false;
2139 }
2140
2141 return true;
2142}

References AutoSaveDelete(), WriteDoc(), WriteXML(), and WriteXMLHeader().

Referenced by SaveProject().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ UseConnection()

void ProjectFileIO::UseConnection ( Connection &&  conn,
const FilePath filePath 
)
private

Definition at line 669 of file ProjectFileIO.cpp.

670{
671 auto &curConn = CurrConn();
672 wxASSERT(!curConn);
673
674 curConn = std::move(conn);
675 SetFileName(filePath);
676}

References CurrConn(), and SetFileName().

Referenced by SaveProject().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ WasCompacted()

bool ProjectFileIO::WasCompacted ( )

Definition at line 1564 of file ProjectFileIO.cpp.

1565{
1566 return mWasCompacted;
1567}

References mWasCompacted.

Referenced by SetBypass().

Here is the caller graph for this function:

◆ WriteDoc()

bool ProjectFileIO::WriteDoc ( const char *  table,
const ProjectSerializer autosave,
const char *  schema = "main" 
)
private

Definition at line 1842 of file ProjectFileIO.cpp.

1845{
1846 auto db = DB();
1847
1848 TransactionScope transaction(mProject, "UpdateProject");
1849
1850 int rc;
1851
1852 // For now, we always use an ID of 1. This will replace the previously
1853 // written row every time.
1854 char sql[256];
1855 sqlite3_snprintf(
1856 sizeof(sql), sql,
1857 "INSERT INTO %s.%s(id, dict, doc) VALUES(1, ?1, ?2)"
1858 " ON CONFLICT(id) DO UPDATE SET dict = ?1, doc = ?2;",
1859 schema, table);
1860
1861 sqlite3_stmt *stmt = nullptr;
1862 auto cleanup = finally([&]
1863 {
1864 if (stmt)
1865 {
1866 sqlite3_finalize(stmt);
1867 }
1868 });
1869
1870 rc = sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr);
1871 if (rc != SQLITE_OK)
1872 {
1873 ADD_EXCEPTION_CONTEXT("sqlite3.query", sql);
1874 ADD_EXCEPTION_CONTEXT("sqlite3.rc", std::to_string(rc));
1875 ADD_EXCEPTION_CONTEXT("sqlite3.context", "ProjectGileIO::WriteDoc::prepare");
1876
1877 SetDBError(
1878 XO("Unable to prepare project file command:\n\n%s").Format(sql)
1879 );
1880 return false;
1881 }
1882
1883 const MemoryStream& dict = autosave.GetDict();
1884 const MemoryStream& data = autosave.GetData();
1885
1886 // Bind statement parameters
1887 // Might return SQL_MISUSE which means it's our mistake that we violated
1888 // preconditions; should return SQL_OK which is 0
1889 if (
1890 sqlite3_bind_zeroblob(stmt, 1, dict.GetSize()) ||
1891 sqlite3_bind_zeroblob(stmt, 2, data.GetSize()))
1892 {
1893 ADD_EXCEPTION_CONTEXT("sqlite3.query", sql);
1894 ADD_EXCEPTION_CONTEXT("sqlite3.rc", std::to_string(rc));
1895 ADD_EXCEPTION_CONTEXT("sqlite3.context", "ProjectGileIO::WriteDoc::bind");
1896
1897 SetDBError(XO("Unable to bind to blob"));
1898 return false;
1899 }
1900
1901 const auto reportError = [this](auto sql) {
1902 SetDBError(
1903 XO("Failed to update the project file.\nThe following command failed:\n\n%s")
1904 .Format(sql));
1905 };
1906
1907 rc = sqlite3_step(stmt);
1908
1909 if (rc != SQLITE_DONE)
1910 {
1911 ADD_EXCEPTION_CONTEXT("sqlite3.query", sql);
1912 ADD_EXCEPTION_CONTEXT("sqlite3.rc", std::to_string(rc));
1913 ADD_EXCEPTION_CONTEXT("sqlite3.context", "ProjectGileIO::WriteDoc::step");
1914
1915 reportError(sql);
1916 return false;
1917 }
1918
1919 // Finalize the statement before committing the transaction
1920 sqlite3_finalize(stmt);
1921 stmt = nullptr;
1922
1923 // Get rowid
1924
1925 int64_t rowID = 0;
1926
1927 const wxString rowIDSql =
1928 wxString::Format("SELECT ROWID FROM %s.%s WHERE id = 1;", schema, table);
1929
1930 if (!GetValue(rowIDSql, rowID, true))
1931 {
1932 ADD_EXCEPTION_CONTEXT("sqlite3.rc", std::to_string(sqlite3_errcode(db)));
1933 ADD_EXCEPTION_CONTEXT("sqlite3.context", "ProjectGileIO::WriteDoc::rowid");
1934
1935 reportError(rowIDSql);
1936 return false;
1937 }
1938
1939 const auto writeStream = [db, schema, table, rowID, this](const char* column, const MemoryStream& stream) {
1940
1941 auto blobStream =
1942 SQLiteBlobStream::Open(db, schema, table, column, rowID, false);
1943
1944 if (!blobStream)
1945 {
1946 ADD_EXCEPTION_CONTEXT("sqlite3.rc", std::to_string(sqlite3_errcode(db)));
1947 ADD_EXCEPTION_CONTEXT("sqlite3.col", column);
1948 ADD_EXCEPTION_CONTEXT("sqlite3.context", "ProjectGileIO::WriteDoc::openBlobStream");
1949
1950 SetDBError(XO("Unable to bind to blob"));
1951 return false;
1952 }
1953
1954 for (auto chunk : stream)
1955 {
1956 if (SQLITE_OK != blobStream->Write(chunk.first, chunk.second))
1957 {
1958 ADD_EXCEPTION_CONTEXT("sqlite3.rc", std::to_string(sqlite3_errcode(db)));
1959 ADD_EXCEPTION_CONTEXT("sqlite3.col", column);
1960 ADD_EXCEPTION_CONTEXT("sqlite3.context", "ProjectGileIO::WriteDoc::writeBlobStream");
1961 // The user visible message is not changed, so there is no need for new strings
1962 SetDBError(XO("Unable to bind to blob"));
1963 return false;
1964 }
1965 }
1966
1967 if (blobStream->Close() != SQLITE_OK)
1968 {
1970 "sqlite3.rc", std::to_string(sqlite3_errcode(db)));
1971 ADD_EXCEPTION_CONTEXT("sqlite3.col", column);
1973 "sqlite3.context", "ProjectGileIO::WriteDoc::writeBlobStream");
1974 // The user visible message is not changed, so there is no need for new
1975 // strings
1976 SetDBError(XO("Unable to bind to blob"));
1977 return false;
1978 }
1979
1980 return true;
1981 };
1982
1983 if (!writeStream("dict", dict))
1984 return false;
1985
1986 if (!writeStream("doc", data))
1987 return false;
1988
1989 const auto requiredVersion =
1991
1992 const wxString setVersionSql =
1993 wxString::Format("PRAGMA user_version = %u", requiredVersion.GetPacked());
1994
1995 if (!Query(setVersionSql.c_str(), [](auto...) { return 0; }))
1996 {
1997 // DV: Very unlikely case.
1998 // Since we need to improve the error messages in the future, let's use
1999 // the generic message for now, so no new strings are needed
2000 reportError(setVersionSql);
2001 return false;
2002 }
2003
2004 return transaction.Commit();
2005}
A low overhead memory stream with O(1) append, low heap fragmentation and a linear memory view.
const size_t GetSize() const noexcept
ProjectFormatVersion GetRequiredVersion(const AudacityProject &project) const
Returns the minimum possible version that can be used to save the project.
static const ProjectFormatExtensionsRegistry & Get()
const MemoryStream & GetData() const
const MemoryStream & GetDict() const
static std::optional< SQLiteBlobStream > Open(sqlite3 *db, const char *schema, const char *table, const char *column, int64_t rowID, bool readOnly) noexcept
RAII for a database transaction, possibly nested.

References ADD_EXCEPTION_CONTEXT, TransactionScope::Commit(), DB(), ProjectFormatExtensionsRegistry::Get(), ProjectSerializer::GetData(), ProjectSerializer::GetDict(), ProjectFormatExtensionsRegistry::GetRequiredVersion(), MemoryStream::GetSize(), GetValue(), mProject, SQLiteBlobStream::Open(), Query(), SetDBError(), and XO().

Referenced by AutoSave(), CopyTo(), and UpdateSaved().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ WriteXML()

void ProjectFileIO::WriteXML ( XMLWriter xmlFile,
bool  recording = false,
const TrackList tracks = nullptr 
)
private

Definition at line 1758 of file ProjectFileIO.cpp.

1762{
1763 auto &proj = mProject;
1764 auto &tracklist = tracks ? *tracks : TrackList::Get(proj);
1765
1766 //TIMER_START( "AudacityProject::WriteXML", xml_writer_timer );
1767
1768 xmlFile.StartTag(wxT("project"));
1769 xmlFile.WriteAttr(wxT("xmlns"), wxT("http://audacity.sourceforge.net/xml/"));
1770
1771 xmlFile.WriteAttr(wxT("version"), wxT(AUDACITY_FILE_FORMAT_VERSION));
1772 xmlFile.WriteAttr(wxT("audacityversion"), AUDACITY_VERSION_STRING);
1773
1774 ProjectFileIORegistry::Get().CallWriters(proj, xmlFile);
1775
1776 tracklist.Any().Visit([&](const Track *t)
1777 {
1778 auto useTrack = t;
1779 if ( recording ) {
1780 // When append-recording, there is a temporary "shadow" track accumulating
1781 // changes and displayed on the screen but it is not yet part of the
1782 // regular track list. That is the one that we want to back up.
1783 // SubstitutePendingChangedTrack() fetches the shadow, if the track has
1784 // one, else it gives the same track back.
1785 useTrack = t->SubstitutePendingChangedTrack().get();
1786 }
1787 else if ( useTrack->GetId() == TrackId{} ) {
1788 // This is a track added during a non-appending recording that is
1789 // not yet in the undo history. The UndoManager skips backing it up
1790 // when pushing. Don't auto-save it.
1791 return;
1792 }
1793 useTrack->WriteXML(xmlFile);
1794 });
1795
1796 xmlFile.EndTag(wxT("project"));
1797
1798 //TIMER_STOP( xml_writer_timer );
1799}
Abstract base class for an object holding data associated with points on a time axis.
Definition: Track.h:226
std::shared_ptr< Track > SubstitutePendingChangedTrack()
Definition: Track.cpp:1211
An in-session identifier of track objects across undo states. It does not persist between sessions.
Definition: Track.h:152
void CallWriters(const Host &host, XMLWriter &writer)
virtual void StartTag(const wxString &name)
Definition: XMLWriter.cpp:79
void WriteAttr(const wxString &name, const Identifier &value)
Definition: XMLWriter.h:36
virtual void EndTag(const wxString &name)
Definition: XMLWriter.cpp:102

References AUDACITY_FILE_FORMAT_VERSION, XMLMethodRegistry< Host >::CallWriters(), XMLWriter::EndTag(), XMLMethodRegistry< Host >::Get(), TrackList::Get(), mProject, XMLWriter::StartTag(), Track::SubstitutePendingChangedTrack(), XMLWriter::WriteAttr(), and wxT().

Referenced by AutoSave(), CopyTo(), GenerateDoc(), and UpdateSaved().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ WriteXMLHeader()

void ProjectFileIO::WriteXMLHeader ( XMLWriter xmlFile) const
private

Definition at line 1743 of file ProjectFileIO.cpp.

1744{
1745 xmlFile.Write(wxT("<?xml "));
1746 xmlFile.Write(wxT("version=\"1.0\" "));
1747 xmlFile.Write(wxT("standalone=\"no\" "));
1748 xmlFile.Write(wxT("?>\n"));
1749
1750 xmlFile.Write(wxT("<!DOCTYPE "));
1751 xmlFile.Write(wxT("project "));
1752 xmlFile.Write(wxT("PUBLIC "));
1753 xmlFile.Write(wxT("\"-//audacityproject-1.3.0//DTD//EN\" "));
1754 xmlFile.Write(wxT("\"http://audacity.sourceforge.net/xml/audacityproject-1.3.0.dtd\" "));
1755 xmlFile.Write(wxT(">\n"));
1756}
virtual void Write(const wxString &data)=0

References XMLWriter::Write(), and wxT().

Referenced by AutoSave(), CopyTo(), GenerateDoc(), and UpdateSaved().

Here is the call graph for this function:
Here is the caller graph for this function:

Member Data Documentation

◆ mFileName

FilePath ProjectFileIO::mFileName
private

◆ mHadUnused

bool ProjectFileIO::mHadUnused
private

Definition at line 312 of file ProjectFileIO.h.

Referenced by Compact(), HadUnused(), and ShouldCompact().

◆ mModified

bool ProjectFileIO::mModified
private

◆ mpErrors

std::shared_ptr<DBConnectionErrors> ProjectFileIO::mpErrors
private

◆ mPrevConn

Connection ProjectFileIO::mPrevConn
private

◆ mPrevFileName

FilePath ProjectFileIO::mPrevFileName
private

Definition at line 315 of file ProjectFileIO.h.

Referenced by DiscardConnection(), RestoreConnection(), and SaveConnection().

◆ mPrevTemporary

bool ProjectFileIO::mPrevTemporary
private

Definition at line 316 of file ProjectFileIO.h.

Referenced by DiscardConnection(), RestoreConnection(), and SaveConnection().

◆ mProject

AudacityProject& ProjectFileIO::mProject
private

◆ mRecovered

bool ProjectFileIO::mRecovered
private

◆ mTemporary

bool ProjectFileIO::mTemporary
private

◆ mTitle

wxString ProjectFileIO::mTitle
private

Definition at line 292 of file ProjectFileIO.h.

Referenced by SetProjectTitle().

◆ mWasCompacted

bool ProjectFileIO::mWasCompacted
private

Definition at line 309 of file ProjectFileIO.h.

Referenced by Compact(), and WasCompacted().


The documentation for this class was generated from the following files: