Audacity  3.0.3
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 Member Functions

 ProjectFileIO (AudacityProject &project)
 
 ProjectFileIO (const ProjectFileIO &) PROHIBITED
 
ProjectFileIOoperator= (const ProjectFileIO &) PROHIBITED
 
 ~ProjectFileIO ()
 
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 void HandleXMLEndTag (const wxChar *WXUNUSED(tag))
 
virtual void HandleXMLContent (const wxString &WXUNUSED(content))
 
bool ReadXMLTag (const char *tag, const char **attrs)
 
void ReadXMLEndTag (const char *tag)
 
void ReadXMLContent (const char *s, int len)
 
XMLTagHandlerReadXMLChild (const char *tag)
 

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 wxChar *tag, const wxChar **attrs) override
 
XMLTagHandlerHandleXMLChild (const wxChar *tag) override
 
void UpdatePrefs () override
 
int Exec (const char *query, const ExecCB &callback)
 
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 GetValue (const char *sql, wxString &value)
 
bool GetBlob (const char *sql, wxMemoryBuffer &buffer)
 
bool CheckVersion ()
 
bool InstallSchema (sqlite3 *db, const char *schema="main")
 
bool UpgradeSchema ()
 
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 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
 
std::shared_ptr< DBConnectionErrorsmpErrors
 
FilePath mFileName
 
bool mRecovered
 
bool mModified
 
bool mTemporary
 
bool mWasCompacted
 
bool mHadUnused
 
Connection mPrevConn
 
FilePath mPrevFileName
 
bool mPrevTemporary
 

Detailed Description

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

Definition at line 57 of file ProjectFileIO.h.

Member Typedef Documentation

◆ ExecCB

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

Definition at line 193 of file ProjectFileIO.h.

Constructor & Destructor Documentation

◆ ProjectFileIO() [1/2]

ProjectFileIO::ProjectFileIO ( AudacityProject project)
explicit

Definition at line 280 of file ProjectFileIO.cpp.

281  : mProject{ project }
282  , mpErrors{ std::make_shared<DBConnectionErrors>() }
283 {
284  mPrevConn = nullptr;
285 
286  mRecovered = false;
287  mModified = false;
288  mTemporary = true;
289 
290  UpdatePrefs();
291 }

◆ ProjectFileIO() [2/2]

ProjectFileIO::ProjectFileIO ( const ProjectFileIO )

◆ ~ProjectFileIO()

ProjectFileIO::~ProjectFileIO ( )

Definition at line 293 of file ProjectFileIO.cpp.

294 {
295 }

Member Function Documentation

◆ AutoSave()

bool ProjectFileIO::AutoSave ( bool  recording = false)

Definition at line 1742 of file ProjectFileIO.cpp.

1743 {
1744  ProjectSerializer autosave;
1745  WriteXMLHeader(autosave);
1746  WriteXML(autosave, recording);
1747 
1748  if (WriteDoc("autosave", autosave))
1749  {
1750  mModified = true;
1751  return true;
1752  }
1753 
1754  return false;
1755 }

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

Referenced by anonymous_namespace{ProjectHistory.cpp}::AutoSaveOrThrow().

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

◆ AutoSaveDelete()

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

Definition at line 1757 of file ProjectFileIO.cpp.

1758 {
1759  int rc;
1760 
1761  if (!db)
1762  {
1763  db = DB();
1764  }
1765 
1766  rc = sqlite3_exec(db, "DELETE FROM autosave;", nullptr, nullptr, nullptr);
1767  if (rc != SQLITE_OK)
1768  {
1769  ADD_EXCEPTION_CONTEXT("sqlite3.rc", std::to_string(rc));
1770  ADD_EXCEPTION_CONTEXT("sqlite3.context", "ProjectGileIO::AutoSaveDelete");
1771 
1772  SetDBError(
1773  XO("Failed to remove the autosave information from the project file.")
1774  );
1775  return false;
1776  }
1777 
1778  mModified = false;
1779 
1780  return true;
1781 }

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 1118 of file ProjectFileIO.cpp.

1119 {
1120  static const std::vector<wxString> strings {
1121  "-wal",
1122 #ifndef NO_SHM
1123  "-shm",
1124 #endif
1125  };
1126  return strings;
1127 }

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 618 of file ProjectFileIO.cpp.

619 {
620  auto db = DB();
621  int rc;
622 
623  // Install our schema if this is an empty DB
624  wxString result;
625  if (!GetValue("SELECT Count(*) FROM sqlite_master WHERE type='table';", result))
626  {
627  // Bug 2718 workaround for a better error message:
628  // If at this point we get SQLITE_CANTOPEN, then the directory is read-only
629  if (GetLastErrorCode() == SQLITE_CANTOPEN)
630  {
631  SetError(
632  /* i18n-hint: An error message. */
633  XO("Project is in a read only directory\n(Unable to create the required temporary files)"),
635  );
636  }
637 
638  return false;
639  }
640 
641  // If the return count is zero, then there are no tables defined, so this
642  // must be a new project file.
643  if (wxStrtol<char **>(result, nullptr, 10) == 0)
644  {
645  return InstallSchema(db);
646  }
647 
648  // Check for our application ID
649  if (!GetValue("PRAGMA application_ID;", result))
650  {
651  return false;
652  }
653 
654  // It's a database that SQLite recognizes, but it's not one of ours
655  if (wxStrtoul<char **>(result, nullptr, 10) != ProjectFileID)
656  {
657  SetError(XO("This is not an Audacity project file"));
658  return false;
659  }
660 
661  // Get the project file version
662  if (!GetValue("PRAGMA user_version;", result))
663  {
664  return false;
665  }
666 
667  long version = wxStrtol<char **>(result, nullptr, 10);
668 
669  // Project file version is higher than ours. We will refuse to
670  // process it since we can't trust anything about it.
671  if (version > ProjectFileVersion)
672  {
673  SetError(
674  XO("This project was created with a newer version of Audacity.\n\nYou will need to upgrade to open it.")
675  );
676  return false;
677  }
678 
679  // Project file is older than ours, ask the user if it's okay to
680  // upgrade.
681  if (version < ProjectFileVersion)
682  {
683  return UpgradeSchema();
684  }
685 
686  return true;
687 }

References DB(), GetLastErrorCode(), GetLibraryError(), GetValue(), InstallSchema(), ProjectFileID, ProjectFileVersion, SetError(), UpgradeSchema(), 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 400 of file ProjectFileIO.cpp.

401 {
402  auto &curConn = CurrConn();
403  if (!curConn)
404  return false;
405 
406  if (!curConn->Close())
407  {
408  return false;
409  }
410  curConn.reset();
411 
412  SetFileName({});
413 
414  return true;
415 }

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 2221 of file ProjectFileIO.cpp.

2222 {
2223  auto &currConn = CurrConn();
2224  if (!currConn)
2225  {
2226  wxLogDebug("Closing project with no database connection");
2227  return true;
2228  }
2229 
2230  // Save the filename since CloseConnection() will clear it
2231  wxString filename = mFileName;
2232 
2233  // Not much we can do if this fails. The user will simply get
2234  // the recovery dialog upon next restart.
2235  if (CloseConnection())
2236  {
2237  // If this is a temporary project, we no longer want to keep the
2238  // project file.
2239  if (IsTemporary())
2240  {
2241  // This is just a safety check.
2242  wxFileName temp(TempDirectory::TempDir(), wxT(""));
2243  wxFileName file(filename);
2244  file.SetFullName(wxT(""));
2245  if (file == temp)
2246  RemoveProject(filename);
2247  }
2248  }
2249 
2250  return true;
2251 }

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

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 1293 of file ProjectFileIO.cpp.

1295 {
1296  // Haven't compacted yet
1297  mWasCompacted = false;
1298 
1299  // Assume we have unused blocks until we find out otherwise. That way cleanup
1300  // at project close time will still occur.
1301  mHadUnused = true;
1302 
1303  // If forcing compaction, bypass inspection.
1304  if (!force)
1305  {
1306  // Don't compact if this is a temporary project or if it's determined there are not
1307  // enough unused blocks to make it worthwhile.
1308  if (IsTemporary() || !ShouldCompact(tracks))
1309  {
1310  // Delete the AutoSave doc it if exists
1311  if (IsModified())
1312  {
1313  // PRL: not clear what to do if the following fails, but the worst should
1314  // be, the project may reopen in its present state as a recovery file, not
1315  // at the last saved state.
1316  // REVIEW: Could the autosave file be corrupt though at that point, and so
1317  // prevent recovery?
1318  // LLL: I believe Paul is correct since it's deleted with a single SQLite
1319  // transaction. The next time the file opens will just invoke recovery.
1320  (void) AutoSaveDelete();
1321  }
1322 
1323  return;
1324  }
1325  }
1326 
1327  wxString origName = mFileName;
1328  wxString backName = origName + "_compact_back";
1329  wxString tempName = origName + "_compact_temp";
1330 
1331  // Copy the original database to a new database. Only prune sample blocks if
1332  // we have a tracklist.
1333  // REVIEW: Compact can fail on the CopyTo with no error messages. That's OK?
1334  // LLL: We could display an error message or just ignore the failure and allow
1335  // the file to be compacted the next time it's saved.
1336  if (CopyTo(tempName, XO("Compacting project"), IsTemporary(), !tracks.empty(), tracks))
1337  {
1338  // Must close the database to rename it
1339  if (CloseConnection())
1340  {
1341  // Only use the new file if it is actually smaller than the original.
1342  //
1343  // If the original file doesn't have anything to compact (original and new
1344  // are basically identical), the file could grow by a few pages because of
1345  // differences in how SQLite constructs the b-tree.
1346  //
1347  // In this case, just toss the new file and continue to use the original.
1348  //
1349  // Also, do this after closing the connection so that the -wal file
1350  // gets cleaned up.
1351  if (wxFileName::GetSize(tempName) < wxFileName::GetSize(origName))
1352  {
1353  // Rename the original to backup
1354  if (wxRenameFile(origName, backName))
1355  {
1356  // Rename the temporary to original
1357  if (wxRenameFile(tempName, origName))
1358  {
1359  // Open the newly compacted original file
1360  if (OpenConnection(origName))
1361  {
1362  // Remove the old original file
1363  if (!wxRemoveFile(backName))
1364  {
1365  // Just log the error, nothing can be done to correct it
1366  // and WX should have logged another message showing the
1367  // system error code.
1368  wxLogWarning(wxT("Compaction failed to delete backup %s"), backName);
1369  }
1370 
1371  // Remember that we compacted
1372  mWasCompacted = true;
1373 
1374  return;
1375  }
1376  else
1377  {
1378  wxLogWarning(wxT("Compaction failed to open new project %s"), origName);
1379  }
1380 
1381  if (!wxRenameFile(origName, tempName))
1382  {
1383  wxLogWarning(wxT("Compaction failed to rename orignal %s to temp %s"),
1384  origName, tempName);
1385  }
1386  }
1387  else
1388  {
1389  wxLogWarning(wxT("Compaction failed to rename temp %s to orig %s"),
1390  origName, tempName);
1391  }
1392 
1393  if (!wxRenameFile(backName, origName))
1394  {
1395  wxLogWarning(wxT("Compaction failed to rename back %s to orig %s"),
1396  backName, origName);
1397  }
1398  }
1399  else
1400  {
1401  wxLogWarning(wxT("Compaction failed to rename orig %s to back %s"),
1402  backName, origName);
1403  }
1404  }
1405 
1406  if (!OpenConnection(origName))
1407  {
1408  wxLogWarning(wxT("Compaction failed to reopen %s"), origName);
1409  }
1410  }
1411 
1412  // Did not achieve any real compaction
1413  // RemoveProject not needed for what was an attached database
1414  if (!wxRemoveFile(tempName))
1415  {
1416  // Just log the error, nothing can be done to correct it
1417  // and WX should have logged another message showing the
1418  // system error code.
1419  wxLogWarning(wxT("Failed to delete temporary file...ignoring"));
1420  }
1421  }
1422 
1423  return;
1424 }

References AutoSaveDelete(), CloseConnection(), CopyTo(), IsModified(), IsTemporary(), mFileName, mHadUnused, mWasCompacted, OpenConnection(), ShouldCompact(), 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 805 of file ProjectFileIO.cpp.

810 {
811  auto pConn = CurrConn().get();
812  if (!pConn)
813  return false;
814 
815  // Get access to the active tracklist
816  auto pProject = &mProject;
817 
818  SampleBlockIDSet blockids;
819 
820  // Collect all active blockids
821  if (prune)
822  {
823  for (auto trackList : tracks)
824  if (trackList)
825  InspectBlocks( *trackList, {}, &blockids );
826  }
827  // Collect ALL blockids
828  else
829  {
830  auto cb = [&blockids](int cols, char **vals, char **){
831  SampleBlockID blockid;
832  wxString{ vals[0] }.ToLongLong(&blockid);
833  blockids.insert(blockid);
834  return 0;
835  };
836 
837  if (!Query("SELECT blockid FROM sampleblocks;", cb))
838  {
839  // Error message already captured.
840  return false;
841  }
842  }
843 
844  // Create the project doc
845  ProjectSerializer doc;
846  WriteXMLHeader(doc);
847  WriteXML(doc, false, tracks.empty() ? nullptr : tracks[0]);
848 
849  auto db = DB();
850  Connection destConn = nullptr;
851  bool success = false;
852  int rc = SQLITE_OK;
854 
855  // Cleanup in case things go awry
856  auto cleanup = finally([&]
857  {
858  if (!success)
859  {
860  if (destConn)
861  {
862  destConn->Close();
863  destConn = nullptr;
864  }
865 
866  // Rollback transaction in case one was active.
867  // If this fails (probably due to memory or disk space), the transaction will
868  // (presumably) stil be active, so further updates to the project file will
869  // fail as well. Not really much we can do about it except tell the user.
870  auto result = sqlite3_exec(db, "ROLLBACK;", nullptr, nullptr, nullptr);
871 
872  // Only capture the error if there wasn't a previous error
873  if (result != SQLITE_OK && (rc == SQLITE_DONE || rc == SQLITE_OK))
874  {
875  ADD_EXCEPTION_CONTEXT("sqlite3.rc", std::to_string(rc));
877  "sqlite3.context", "ProjectGileIO::CopyTo.cleanup");
878 
879  SetDBError(
880  XO("Failed to rollback transaction during import")
881  );
882  }
883 
884  // And detach the outbound DB in case (if it's attached). Don't check for
885  // errors since it may not be attached. But, if it is and the DETACH fails,
886  // subsequent CopyTo() actions will fail until Audacity is relaunched.
887  sqlite3_exec(db, "DETACH DATABASE outbound;", nullptr, nullptr, nullptr);
888 
889  // RemoveProject not necessary to clean up attached database
890  wxRemoveFile(destpath);
891  }
892  });
893 
894  // Attach the destination database
895  wxString sql;
896  wxString dbName = destpath;
897  // Bug 2793: Quotes in name need escaping for sqlite3.
898  dbName.Replace( "'", "''");
899  sql.Printf("ATTACH DATABASE '%s' AS outbound;", dbName.ToUTF8());
900 
901  rc = sqlite3_exec(db, sql, nullptr, nullptr, nullptr);
902  if (rc != SQLITE_OK)
903  {
904  SetDBError(
905  XO("Unable to attach destination database")
906  );
907  return false;
908  }
909 
910  // Ensure attached DB connection gets configured
911  //
912  // NOTE: Between the above attach and setting the mode here, a normal DELETE
913  // mode journal will be used and will briefly appear in the filesystem.
914  if ( pConn->FastMode("outbound") != SQLITE_OK)
915  {
916  SetDBError(
917  XO("Unable to switch to fast journaling mode")
918  );
919 
920  return false;
921  }
922 
923  // Install our schema into the new database
924  if (!InstallSchema(db, "outbound"))
925  {
926  // Message already set
927  return false;
928  }
929 
930  {
931  // Ensure statement gets cleaned up
932  sqlite3_stmt *stmt = nullptr;
933  auto cleanup = finally([&]
934  {
935  if (stmt)
936  {
937  // No need to check return code
938  sqlite3_finalize(stmt);
939  }
940  });
941 
942  // Prepare the statement only once
943  rc = sqlite3_prepare_v2(db,
944  "INSERT INTO outbound.sampleblocks"
945  " SELECT * FROM main.sampleblocks"
946  " WHERE blockid = ?;",
947  -1,
948  &stmt,
949  nullptr);
950  if (rc != SQLITE_OK)
951  {
952  ADD_EXCEPTION_CONTEXT("sqlite3.rc", std::to_string(rc));
954  "sqlite3.context", "ProjectGileIO::CopyTo.prepare");
955 
956  SetDBError(
957  XO("Unable to prepare project file command:\n\n%s").Format(sql)
958  );
959  return false;
960  }
961 
962  /* i18n-hint: This title appears on a dialog that indicates the progress
963  in doing something.*/
964  ProgressDialog progress(XO("Progress"), msg, pdlgHideStopButton);
966 
967  wxLongLong_t count = 0;
968  wxLongLong_t total = blockids.size();
969 
970  // Start a transaction. Since we're running without a journal,
971  // this really doesn't provide rollback. It just prevents SQLite
972  // from auto committing after each step through the loop.
973  //
974  // Also note that we will have an open transaction if we fail
975  // while copying the blocks. This is fine since we're just going
976  // to delete the database anyway.
977  sqlite3_exec(db, "BEGIN;", nullptr, nullptr, nullptr);
978 
979  // Copy sample blocks from the main DB to the outbound DB
980  for (auto blockid : blockids)
981  {
982  // Bind statement parameters
983  rc = sqlite3_bind_int64(stmt, 1, blockid);
984  if (rc != SQLITE_OK)
985  {
986  ADD_EXCEPTION_CONTEXT("sqlite3.rc", std::to_string(rc));
988  "sqlite3.context", "ProjectGileIO::CopyTo.bind");
989 
990  SetDBError(
991  XO("Failed to bind SQL parameter")
992  );
993 
994  return false;
995  }
996 
997  // Process it
998  rc = sqlite3_step(stmt);
999  if (rc != SQLITE_DONE)
1000  {
1001  ADD_EXCEPTION_CONTEXT("sqlite3.rc", std::to_string(rc));
1003  "sqlite3.context", "ProjectGileIO::CopyTo.step");
1004 
1005  SetDBError(
1006  XO("Failed to update the project file.\nThe following command failed:\n\n%s").Format(sql)
1007  );
1008  return false;
1009  }
1010 
1011  // Reset statement to beginning
1012  if (sqlite3_reset(stmt) != SQLITE_OK)
1013  {
1014  ADD_EXCEPTION_CONTEXT("sqlite3.rc", std::to_string(rc));
1016  "sqlite3.context", "ProjectGileIO::CopyTo.reset");
1017 
1019  }
1020 
1021  result = progress.Update(++count, total);
1022  if (result != ProgressResult::Success)
1023  {
1024  // Note that we're not setting success, so the finally
1025  // block above will take care of cleaning up
1026  return false;
1027  }
1028  }
1029 
1030  // Write the doc.
1031  //
1032  // If we're compacting a temporary project (user initiated from the File
1033  // menu), then write the doc to the "autosave" table since temporary
1034  // projects do not have a "project" doc.
1035  if (!WriteDoc(isTemporary ? "autosave" : "project", doc, "outbound"))
1036  {
1037  return false;
1038  }
1039 
1040  // See BEGIN above...
1041  sqlite3_exec(db, "COMMIT;", nullptr, nullptr, nullptr);
1042  }
1043 
1044  // Detach the destination database
1045  rc = sqlite3_exec(db, "DETACH DATABASE outbound;", nullptr, nullptr, nullptr);
1046  if (rc != SQLITE_OK)
1047  {
1048  ADD_EXCEPTION_CONTEXT("sqlite3.rc", std::to_string(rc));
1049  ADD_EXCEPTION_CONTEXT("sqlite3.context", "ProjectGileIO::CopyTo::detach");
1050 
1051  SetDBError(
1052  XO("Destination project could not be detached")
1053  );
1054 
1055  return false;
1056  }
1057 
1058  // Tell cleanup everything is good to go
1059  success = true;
1060 
1061  return true;
1062 }

References ADD_EXCEPTION_CONTEXT, CurrConn(), DB(), InspectBlocks(), InstallSchema(), mProject, pdlgHideStopButton, Query(), SetDBError(), BasicUI::Success, THROW_INCONSISTENCY_EXCEPTION, ProgressDialog::Update(), 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 1112 of file ProjectFileIO.cpp.

1113 {
1114  auto &connectionPtr = ConnectionPtr::Get( mProject );
1115  return connectionPtr.mpConnection;
1116 }

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 333 of file ProjectFileIO.cpp.

334 {
335  return GetConnection().DB();
336 }

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

Referenced by AutoSaveDelete(), CheckVersion(), CopyTo(), DeleteBlocks(), Exec(), GetBlob(), 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 729 of file ProjectFileIO.cpp.

730 {
731  auto db = DB();
732  int rc;
733 
734  auto cleanup = finally([&]
735  {
736  // Remove our function, whether it was successfully defined or not.
737  sqlite3_create_function(db, "inset", 1, SQLITE_UTF8 | SQLITE_DETERMINISTIC, nullptr, nullptr, nullptr, nullptr);
738  });
739 
740  // Add the function used to verify each row's blockid against the set of active blockids
741  const void *p = &blockids;
742  rc = sqlite3_create_function(db, "inset", 1, SQLITE_UTF8 | SQLITE_DETERMINISTIC, const_cast<void*>(p), InSet, nullptr, nullptr);
743  if (rc != SQLITE_OK)
744  {
745  ADD_EXCEPTION_CONTEXT("sqlite3.rc", std::to_string(rc));
746  ADD_EXCEPTION_CONTEXT("sqlite3.context", "ProjectGileIO::DeleteBlocks::create_function");
747 
748  /* i18n-hint: An error message. Don't translate inset or blockids.*/
749  SetDBError(XO("Unable to add 'inset' function (can't verify blockids)"));
750  return false;
751  }
752 
753  // Delete all rows in the set, or not in it
754  // This is the first command that writes to the database, and so we
755  // do more informative error reporting than usual, if it fails.
756  auto sql = wxString::Format(
757  "DELETE FROM sampleblocks WHERE %sinset(blockid);",
758  complement ? "NOT " : "" );
759  rc = sqlite3_exec(db, sql, nullptr, nullptr, nullptr);
760  if (rc != SQLITE_OK)
761  {
762  ADD_EXCEPTION_CONTEXT("sqlite3.query", sql.ToStdString());
763  ADD_EXCEPTION_CONTEXT("sqlite3.rc", std::to_string(rc));
764  ADD_EXCEPTION_CONTEXT("sqlite3.context", "ProjectGileIO::GetBlob");
765 
766  if( rc==SQLITE_READONLY)
767  /* i18n-hint: An error message. Don't translate blockfiles.*/
768  SetDBError(XO("Project is read only\n(Unable to work with the blockfiles)"));
769  else if( rc==SQLITE_LOCKED)
770  /* i18n-hint: An error message. Don't translate blockfiles.*/
771  SetDBError(XO("Project is locked\n(Unable to work with the blockfiles)"));
772  else if( rc==SQLITE_BUSY)
773  /* i18n-hint: An error message. Don't translate blockfiles.*/
774  SetDBError(XO("Project is busy\n(Unable to work with the blockfiles)"));
775  else if( rc==SQLITE_CORRUPT)
776  /* i18n-hint: An error message. Don't translate blockfiles.*/
777  SetDBError(XO("Project is corrupt\n(Unable to work with the blockfiles)"));
778  else if( rc==SQLITE_PERM)
779  /* i18n-hint: An error message. Don't translate blockfiles.*/
780  SetDBError(XO("Some permissions issue\n(Unable to work with the blockfiles)"));
781  else if( rc==SQLITE_IOERR)
782  /* i18n-hint: An error message. Don't translate blockfiles.*/
783  SetDBError(XO("A disk I/O error\n(Unable to work with the blockfiles)"));
784  else if( rc==SQLITE_AUTH)
785  /* i18n-hint: An error message. Don't translate blockfiles.*/
786  SetDBError(XO("Not authorized\n(Unable to work with the blockfiles)"));
787  else
788  /* i18n-hint: An error message. Don't translate blockfiles.*/
789  SetDBError(XO("Unable to work with the blockfiles"));
790 
791  return false;
792  }
793 
794  // Mark the project recovered if we deleted any rows
795  int changes = sqlite3_changes(db);
796  if (changes > 0)
797  {
798  wxLogInfo(XO("Total orphan blocks deleted %d").Translation(), changes);
799  mRecovered = true;
800  }
801 
802  return true;
803 }

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 432 of file ProjectFileIO.cpp.

433 {
434  if (mPrevConn)
435  {
436  if (!mPrevConn->Close())
437  {
438  // Store an error message
439  SetDBError(
440  XO("Failed to discard connection")
441  );
442  }
443 
444  // If this is a temporary project, we no longer want to keep the
445  // project file.
446  if (mPrevTemporary)
447  {
448  // This is just a safety check.
449  wxFileName temp(TempDirectory::TempDir(), wxT(""));
450  wxFileName file(mPrevFileName);
451  file.SetFullName(wxT(""));
452  if (file == temp)
453  {
455  {
456  wxLogMessage("Failed to remove temporary project %s", mPrevFileName);
457  }
458  }
459  }
460  mPrevConn = nullptr;
461  mPrevFileName.clear();
462  }
463 }

References mPrevConn, mPrevFileName, mPrevTemporary, RemoveProject(), SetDBError(), TempDirectory::TempDir(), 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 
)
private

Definition at line 506 of file ProjectFileIO.cpp.

507 {
508  char *errmsg = nullptr;
509 
510  const void *ptr = &callback;
511  int rc = sqlite3_exec(DB(), query, ExecCallback,
512  const_cast<void*>(ptr), &errmsg);
513 
514  if (rc != SQLITE_ABORT && errmsg)
515  {
516  ADD_EXCEPTION_CONTEXT("sqlite3.query", query);
517  ADD_EXCEPTION_CONTEXT("sqlite3.rc", std::to_string(rc));
518 
519  SetDBError(
520  XO("Failed to execute a project file command:\n\n%s").Format(query),
521  Verbatim(errmsg),
522  rc
523  );
524  }
525  if (errmsg)
526  {
527  sqlite3_free(errmsg);
528  }
529 
530  return rc;
531 }

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 323 of file ProjectFileIO.cpp.

324 {
325  auto &trackList = TrackList::Get( mProject );
326 
327  XMLStringWriter doc;
328  WriteXMLHeader(doc);
329  WriteXML(doc, false, trackList.empty() ? nullptr : &trackList);
330  return doc;
331 }

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 269 of file ProjectFileIO.cpp.

270 {
271  auto &result = project.AttachedObjects::Get< ProjectFileIO >( sFileIOKey );
272  return result;
273 }

References sFileIOKey.

Referenced by ProjectFileManager::AddImportedTracks(), OpenProjectCommand::Apply(), ProjectFileManager::CloseProject(), ProjectFileManager::Compact(), ProjectFileManager::CompactProjectOnClose(), Effect::DoEffect(), anonymous_namespace{FileMenus.cpp}::DoExport(), DoImportMIDI(), ProjectFileManager::DoSave(), AudioIO::FillBuffers(), Get(), ProjectManager::GetEstimatedRecordingMinsLeftOnDisk(), ProjectFileManager::Import(), anonymous_namespace{ProjectFileManager.cpp}::ImportProject(), ProjectFileManager::IsAlreadyOpen(), ProjectHistory::ModifyState(), ProjectAudioManager::OnAudioIONewBlocks(), ProjectAudioManager::OnAudioIOStopRecording(), TimerRecordDialog::OnAutoSavePathButton_Click(), ProjectManager::OnCloseWindow(), HistoryDialog::OnCompact(), ContrastDialog::OnExport(), ProjectManager::OnTimer(), TransportActions::Handler::OnTimerRecord(), ProjectFileManager::OpenNewProject(), ProjectFileManager::OpenProject(), ProjectManager::OpenProject(), ProjectFileManager::OpenProjectFile(), ProjectHistory::PopState(), AutoRecoveryDialog::PopulateList(), TimerRecordDialog::PopulateOrExchange(), ProjectHistory::PushState(), ProjectFileManager::ReadProjectFile(), RefreshAllTitles(), ProjectManager::ResetProjectToEmpty(), ProjectFileManager::Save(), ProjectFileManager::SaveAs(), ProjectFileManager::SaveCopy(), ProjectFileManager::SaveFromTimerRecording(), AudioIO::StopStream(), UnsavedChangesFlag(), and InvisibleTemporaryProject::~InvisibleTemporaryProject().

Here is the caller graph for this function:

◆ Get() [2/2]

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

Definition at line 275 of file ProjectFileIO.cpp.

276 {
277  return Get( const_cast< AudacityProject & >( project ) );
278 }

References Get().

Here is the call graph for this function:

◆ get2()

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

Definition at line 2637 of file ProjectFileIO.cpp.

2638 {
2639  return (ptr[0] << 8) | ptr[1];
2640 }

Referenced by GetDiskUsage().

Here is the caller graph for this function:

◆ get4()

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

Definition at line 2643 of file ProjectFileIO.cpp.

2644 {
2645  return ((unsigned int) ptr[0] << 24) |
2646  ((unsigned int) ptr[1] << 16) |
2647  ((unsigned int) ptr[2] << 8) |
2648  ((unsigned int) ptr[3]);
2649 }

Referenced by GetDiskUsage().

Here is the caller graph for this function:

◆ get_varint()

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

Definition at line 2654 of file ProjectFileIO.cpp.

2655 {
2656  int64_t val = 0;
2657  int i;
2658 
2659  for (i = 0; i < 8; ++i)
2660  {
2661  val = (val << 7) + (ptr[i] & 0x7f);
2662  if ((ptr[i] & 0x80) == 0)
2663  {
2664  *out = val;
2665  return i + 1;
2666  }
2667  }
2668 
2669  val = (val << 8) + (ptr[i] & 0xff);
2670  *out = val;
2671 
2672  return 9;
2673 }

Referenced by GetDiskUsage().

Here is the caller graph for this function:

◆ GetBlob()

bool ProjectFileIO::GetBlob ( const char *  sql,
wxMemoryBuffer &  buffer 
)
private

Definition at line 560 of file ProjectFileIO.cpp.

561 {
562  auto db = DB();
563  int rc;
564 
565  buffer.Clear();
566 
567  sqlite3_stmt *stmt = nullptr;
568  auto cleanup = finally([&]
569  {
570  if (stmt)
571  {
572  sqlite3_finalize(stmt);
573  }
574  });
575 
576  rc = sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr);
577  if (rc != SQLITE_OK)
578  {
579  ADD_EXCEPTION_CONTEXT("sqlite3.query", sql);
580  ADD_EXCEPTION_CONTEXT("sqlite3.rc", std::to_string(rc));
581  ADD_EXCEPTION_CONTEXT("sqlite3.context", "ProjectGileIO::GetBlob::prepare");
582 
583  SetDBError(
584  XO("Unable to prepare project file command:\n\n%s").Format(sql)
585  );
586  return false;
587  }
588 
589  rc = sqlite3_step(stmt);
590 
591  // A row wasn't found...not an error
592  if (rc == SQLITE_DONE)
593  {
594  return true;
595  }
596 
597  if (rc != SQLITE_ROW)
598  {
599  ADD_EXCEPTION_CONTEXT("sqlite3.query", sql);
600  ADD_EXCEPTION_CONTEXT("sqlite3.rc", std::to_string(rc));
601  ADD_EXCEPTION_CONTEXT("sqlite3.context", "ProjectGileIO::GetBlob::step");
602 
603  SetDBError(
604  XO("Failed to retrieve data from the project file.\nThe following command failed:\n\n%s").Format(sql)
605  );
606  // AUD TODO handle error
607  return false;
608  }
609 
610  const void *blob = sqlite3_column_blob(stmt, 0);
611  int size = sqlite3_column_bytes(stmt, 0);
612 
613  buffer.AppendData(blob, size);
614 
615  return true;
616 }

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

Referenced by LoadProject().

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

◆ GetBlockUsage()

int64_t ProjectFileIO::GetBlockUsage ( SampleBlockID  blockid)

Definition at line 2388 of file ProjectFileIO.cpp.

2389 {
2390  auto pConn = CurrConn().get();
2391  if (!pConn)
2392  return 0;
2393  return GetDiskUsage(*pConn, blockid);
2394 }

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 303 of file ProjectFileIO.cpp.

304 {
305  auto &curConn = CurrConn();
306  if (!curConn)
307  {
308  if (!OpenConnection())
309  {
311  {
313  XO("Failed to open the project's database"),
314  XO("Warning"),
315  "Error:_Disk_full_or_not_writable"
316  };
317  }
318  }
319 
320  return *curConn;
321 }

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 2396 of file ProjectFileIO.cpp.

2398 {
2399  unsigned long long current = 0;
2400  const auto fn = BlockSpaceUsageAccumulator(current);
2401 
2402  // Must pass address of this set, even if not otherwise used, to avoid
2403  // possible multiple count of shared blocks
2404  SampleBlockIDSet seen;
2405  for (auto pTracks: trackLists)
2406  if (pTracks)
2407  InspectBlocks(*pTracks, fn, &seen);
2408 
2409  return current;
2410 }

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 2426 of file ProjectFileIO.cpp.

2427 {
2428  // Information we need to track our travels through the b-tree
2429  typedef struct
2430  {
2431  int64_t pgno;
2432  int currentCell;
2433  int numCells;
2434  unsigned char data[65536];
2435  } page;
2436  std::vector<page> stack;
2437 
2438  int64_t total = 0;
2439  int64_t found = 0;
2440  int64_t right = 0;
2441  int rc;
2442 
2443  // Get the rootpage for the sampleblocks table.
2444  sqlite3_stmt *stmt =
2446  "SELECT rootpage FROM sqlite_master WHERE tbl_name = 'sampleblocks';");
2447  if (stmt == nullptr || sqlite3_step(stmt) != SQLITE_ROW)
2448  {
2449  ADD_EXCEPTION_CONTEXT("sqlite3.rc", std::to_string(sqlite3_errcode(conn.DB())));
2450  ADD_EXCEPTION_CONTEXT("sqlite3.context", "ProjectGileIO::GetDiskUsage");
2451 
2452  return 0;
2453  }
2454 
2455  // And store it in our first stack frame
2456  stack.push_back({sqlite3_column_int64(stmt, 0)});
2457 
2458  // All done with the statement
2459  sqlite3_clear_bindings(stmt);
2460  sqlite3_reset(stmt);
2461 
2462  // Prepare/retrieve statement to read raw database page
2463  stmt = conn.Prepare(DBConnection::GetDBPage,
2464  "SELECT data FROM sqlite_dbpage WHERE pgno = ?1;");
2465  if (stmt == nullptr)
2466  {
2467  return 0;
2468  }
2469 
2470  // Traverse the b-tree until we've visited all of the leaf pages or until
2471  // we find the one corresponding to the passed in sample blockid. Because we
2472  // use an integer primary key for the sampleblocks table, the traversal will
2473  // be in ascending blockid sequence.
2474  do
2475  {
2476  // Acces the top stack frame
2477  page &pg = stack.back();
2478 
2479  // Read the page from the sqlite_dbpage table if it hasn't yet been loaded
2480  if (pg.numCells == 0)
2481  {
2482  // Bind the page number
2483  sqlite3_bind_int64(stmt, 1, pg.pgno);
2484 
2485  // And retrieve the page
2486  if (sqlite3_step(stmt) != SQLITE_ROW)
2487  {
2488  // REVIEW: Likely harmless failure - says size is zero on
2489  // this error.
2490  // LLL: Yea, but not much else we can do.
2491  return 0;
2492  }
2493 
2494  // Copy the page content to the stack frame
2495  memcpy(&pg.data,
2496  sqlite3_column_blob(stmt, 0),
2497  sqlite3_column_bytes(stmt, 0));
2498 
2499  // And retrieve the total number of cells within it
2500  pg.numCells = get2(&pg.data[3]);
2501 
2502  // Reset statement for next usage
2503  sqlite3_clear_bindings(stmt);
2504  sqlite3_reset(stmt);
2505  }
2506 
2507  //wxLogDebug("%*.*spgno %lld currentCell %d numCells %d", (stack.size() - 1) * 2, (stack.size() - 1) * 2, "", pg.pgno, pg.currentCell, pg.numCells);
2508 
2509  // Process an interior table b-tree page
2510  if (pg.data[0] == 0x05)
2511  {
2512  // Process the next cell if we haven't examined all of them yet
2513  if (pg.currentCell < pg.numCells)
2514  {
2515  // Remember the right-most leaf page number.
2516  right = get4(&pg.data[8]);
2517 
2518  // Iterate over the cells.
2519  //
2520  // If we're not looking for a specific blockid, then we always push the
2521  // target page onto the stack and leave the loop after a single iteration.
2522  //
2523  // Otherwise, we match the blockid against the highest integer key contained
2524  // within the cell and if the blockid falls within the cell, we stack the
2525  // page and stop the iteration.
2526  //
2527  // In theory, we could do a binary search for a specific blockid here, but
2528  // because our sample blocks are always large, we will get very few cells
2529  // per page...usually 6 or less.
2530  //
2531  // In both cases, the stacked page can be either an internal or leaf page.
2532  bool stacked = false;
2533  while (pg.currentCell < pg.numCells)
2534  {
2535  // Get the offset to this cell using the offset in the cell pointer
2536  // array.
2537  //
2538  // The cell pointer array starts immediately after the page header
2539  // at offset 12 and the retrieved offset is from the beginning of
2540  // the page.
2541  int celloff = get2(&pg.data[12 + (pg.currentCell * 2)]);
2542 
2543  // Bump to the next cell for the next iteration.
2544  pg.currentCell++;
2545 
2546  // Get the page number this cell describes
2547  int pagenum = get4(&pg.data[celloff]);
2548 
2549  // And the highest integer key, which starts at offset 4 within the cell.
2550  int64_t intkey = 0;
2551  get_varint(&pg.data[celloff + 4], &intkey);
2552 
2553  //wxLogDebug("%*.*sinternal - right %lld celloff %d pagenum %d intkey %lld", (stack.size() - 1) * 2, (stack.size() - 1) * 2, " ", right, celloff, pagenum, intkey);
2554 
2555  // Stack the described page if we're not looking for a specific blockid
2556  // or if this page contains the given blockid.
2557  if (!blockid || blockid <= intkey)
2558  {
2559  stack.push_back({pagenum, 0, 0});
2560  stacked = true;
2561  break;
2562  }
2563  }
2564 
2565  // If we pushed a new page onto the stack, we need to jump back up
2566  // to read the page
2567  if (stacked)
2568  {
2569  continue;
2570  }
2571  }
2572 
2573  // We've exhausted all the cells with this page, so we stack the right-most
2574  // leaf page. Ensure we only process it once.
2575  if (right)
2576  {
2577  stack.push_back({right, 0, 0});
2578  right = 0;
2579  continue;
2580  }
2581  }
2582  // Process a leaf table b-tree page
2583  else if (pg.data[0] == 0x0d)
2584  {
2585  // Iterate over the cells
2586  //
2587  // If we're not looking for a specific blockid, then just accumulate the
2588  // payload sizes. We will be reading every leaf page in the sampleblocks
2589  // table.
2590  //
2591  // Otherwise we break out when we find the matching blockid. In this case,
2592  // we only ever look at 1 leaf page.
2593  bool stop = false;
2594  for (int i = 0; i < pg.numCells; i++)
2595  {
2596  // Get the offset to this cell using the offset in the cell pointer
2597  // array.
2598  //
2599  // The cell pointer array starts immediately after the page header
2600  // at offset 8 and the retrieved offset is from the beginning of
2601  // the page.
2602  int celloff = get2(&pg.data[8 + (i * 2)]);
2603 
2604  // Get the total payload size in bytes of the described row.
2605  int64_t payload = 0;
2606  int digits = get_varint(&pg.data[celloff], &payload);
2607 
2608  // Get the integer key for this row.
2609  int64_t intkey = 0;
2610  get_varint(&pg.data[celloff + digits], &intkey);
2611 
2612  //wxLogDebug("%*.*sleaf - celloff %4d intkey %lld payload %lld", (stack.size() - 1) * 2, (stack.size() - 1) * 2, " ", celloff, intkey, payload);
2613 
2614  // Add this payload size to the total if we're not looking for a specific
2615  // blockid
2616  if (!blockid)
2617  {
2618  total += payload;
2619  }
2620  // Otherwise, return the payload size for a matching row
2621  else if (blockid == intkey)
2622  {
2623  return payload;
2624  }
2625  }
2626  }
2627 
2628  // Done with the current branch, so pop back up to the previous one (if any)
2629  stack.pop_back();
2630  } while (!stack.empty());
2631 
2632  // Return the total used for all sample blocks
2633  return total;
2634 }

References ADD_EXCEPTION_CONTEXT, DBConnection::DB(), get2(), get4(), get_varint(), DBConnection::GetDBPage, DBConnection::GetRootPage, and DBConnection::Prepare().

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 1487 of file ProjectFileIO.cpp.

1488 {
1489  return mFileName;
1490 }

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 2279 of file ProjectFileIO.cpp.

2280 {
2281  wxLongLong freeSpace;
2282  if (wxGetDiskSpace(wxPathOnly(mFileName), NULL, &freeSpace))
2283  {
2285  // 4 GiB per-file maximum
2286  constexpr auto limit = 1ll << 32;
2287 
2288  // Opening a file only to find its length looks wasteful but
2289  // seems to be necessary at least on Windows with FAT filesystems.
2290  // I don't know if that is only a wxWidgets bug.
2291  auto length = wxFile{mFileName}.Length();
2292  // auto length = wxFileName::GetSize(mFileName);
2293 
2294  if (length == wxInvalidSize)
2295  length = 0;
2296  auto free = std::max<wxLongLong>(0, limit - length);
2297  freeSpace = std::min(freeSpace, free);
2298  }
2299  return freeSpace;
2300  }
2301 
2302  return -1;
2303 }

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 2318 of file ProjectFileIO.cpp.

2319 {
2320  return mpErrors->mLastError;
2321 }

References mpErrors.

Referenced by SaveProject().

Here is the caller graph for this function:

◆ GetLastErrorCode()

int ProjectFileIO::GetLastErrorCode ( ) const

Definition at line 2328 of file ProjectFileIO.cpp.

2329 {
2330  return mpErrors->mErrorCode;
2331 }

References mpErrors.

Referenced by CheckVersion().

Here is the caller graph for this function:

◆ GetLastLog()

const wxString & ProjectFileIO::GetLastLog ( ) const

Definition at line 2333 of file ProjectFileIO.cpp.

2334 {
2335  return mpErrors->mLog;
2336 }

References mpErrors.

Referenced by ShowError().

Here is the caller graph for this function:

◆ GetLibraryError()

const TranslatableString & ProjectFileIO::GetLibraryError ( ) const

Definition at line 2323 of file ProjectFileIO.cpp.

2324 {
2325  return mpErrors->mLibraryError;
2326 }

References mpErrors.

Referenced by CheckVersion().

Here is the caller graph for this function:

◆ GetTotalUsage()

int64_t ProjectFileIO::GetTotalUsage ( )

Definition at line 2412 of file ProjectFileIO.cpp.

2413 {
2414  auto pConn = CurrConn().get();
2415  if (!pConn)
2416  return 0;
2417  return GetDiskUsage(*pConn, 0);
2418 }

References CurrConn(), and GetDiskUsage().

Referenced by ShouldCompact().

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

◆ GetValue()

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

Definition at line 546 of file ProjectFileIO.cpp.

547 {
548  // Retrieve the first column in the first row, if any
549  result.clear();
550  auto cb = [&result](int cols, char **vals, char **){
551  if (cols > 0)
552  result = vals[0];
553  // Stop after one row
554  return 1;
555  };
556 
557  return Query(sql, cb);
558 }

References Query().

Referenced by CheckVersion(), and LoadProject().

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

◆ HadUnused()

bool ProjectFileIO::HadUnused ( )

Definition at line 1431 of file ProjectFileIO.cpp.

1432 {
1433  return mHadUnused;
1434 }

References mHadUnused.

Referenced by SetBypass().

Here is the caller graph for this function:

◆ HandleXMLChild()

XMLTagHandler * ProjectFileIO::HandleXMLChild ( const wxChar *  tag)
overrideprivatevirtual

Implements XMLTagHandler.

Definition at line 1652 of file ProjectFileIO.cpp.

1653 {
1654  auto &project = mProject;
1655  auto fn = ProjectFileIORegistry::Lookup(tag);
1656  if (fn)
1657  {
1658  return fn(project);
1659  }
1660 
1661  return nullptr;
1662 }

References fn, ProjectFileIORegistry::Lookup(), and mProject.

Here is the call graph for this function:

◆ HandleXMLTag()

bool ProjectFileIO::HandleXMLTag ( const wxChar *  tag,
const wxChar **  attrs 
)
overrideprivatevirtual

Implements XMLTagHandler.

Definition at line 1520 of file ProjectFileIO.cpp.

1521 {
1522  auto &project = mProject;
1523  auto &viewInfo = ViewInfo::Get(project);
1524  auto &settings = ProjectSettings::Get(project);
1525 
1526  wxString fileVersion;
1527  wxString audacityVersion;
1528  int requiredTags = 0;
1529  long longVpos = 0;
1530 
1531  // loop through attrs, which is a null-terminated list of
1532  // attribute-value pairs
1533  while (*attrs)
1534  {
1535  const wxChar *attr = *attrs++;
1536  const wxChar *value = *attrs++;
1537 
1538  if (!value || !XMLValueChecker::IsGoodString(value))
1539  {
1540  break;
1541  }
1542 
1543  if (viewInfo.ReadXMLAttribute(attr, value))
1544  {
1545  // We need to save vpos now and restore it below
1546  longVpos = std::max(longVpos, long(viewInfo.vpos));
1547  continue;
1548  }
1549 
1550  else if (!wxStrcmp(attr, wxT("version")))
1551  {
1552  fileVersion = value;
1553  requiredTags++;
1554  }
1555 
1556  else if (!wxStrcmp(attr, wxT("audacityversion")))
1557  {
1558  audacityVersion = value;
1559  requiredTags++;
1560  }
1561 
1562  else if (!wxStrcmp(attr, wxT("rate")))
1563  {
1564  double rate;
1565  Internat::CompatibleToDouble(value, &rate);
1566  settings.SetRate( rate );
1567  }
1568 
1569  else if (!wxStrcmp(attr, wxT("snapto")))
1570  {
1571  settings.SetSnapTo(wxString(value) == wxT("on") ? true : false);
1572  }
1573 
1574  else if (!wxStrcmp(attr, wxT("selectionformat")))
1575  {
1576  settings.SetSelectionFormat(
1578  }
1579 
1580  else if (!wxStrcmp(attr, wxT("audiotimeformat")))
1581  {
1582  settings.SetAudioTimeFormat(
1584  }
1585 
1586  else if (!wxStrcmp(attr, wxT("frequencyformat")))
1587  {
1588  settings.SetFrequencySelectionFormatName(
1590  }
1591 
1592  else if (!wxStrcmp(attr, wxT("bandwidthformat")))
1593  {
1594  settings.SetBandwidthSelectionFormatName(
1596  }
1597  } // while
1598 
1599  if (longVpos != 0)
1600  {
1601  // PRL: It seems this must happen after SetSnapTo
1602  viewInfo.vpos = longVpos;
1603  }
1604 
1605  if (requiredTags < 2)
1606  {
1607  return false;
1608  }
1609 
1610  // Parse the file version from the project
1611  int fver;
1612  int frel;
1613  int frev;
1614  if (!wxSscanf(fileVersion, wxT("%i.%i.%i"), &fver, &frel, &frev))
1615  {
1616  return false;
1617  }
1618 
1619  // Parse the file version Audacity was build with
1620  int cver;
1621  int crel;
1622  int crev;
1623  wxSscanf(wxT(AUDACITY_FILE_FORMAT_VERSION), wxT("%i.%i.%i"), &cver, &crel, &crev);
1624 
1625  int fileVer = ((fver *100)+frel)*100+frev;
1626  int codeVer = ((cver *100)+crel)*100+crev;
1627 
1628  if (codeVer<fileVer)
1629  {
1630  /* i18n-hint: %s will be replaced by the version number.*/
1631  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.")
1632  .Format(audacityVersion, AUDACITY_VERSION_STRING);
1633 
1634  ShowError( *ProjectFramePlacement(&project),
1635  XO("Can't open project file"),
1636  msg,
1637  "FAQ:Errors_opening_an_Audacity_project"
1638  );
1639 
1640  return false;
1641  }
1642 
1643  if (wxStrcmp(tag, wxT("project")))
1644  {
1645  return false;
1646  }
1647 
1648  // All other tests passed, so we succeed
1649  return true;
1650 }

References AUDACITY_FILE_FORMAT_VERSION, NumericConverter::BANDWIDTH, Internat::CompatibleToDouble(), NumericConverter::FREQUENCY, ProjectSettings::Get(), ViewInfo::Get(), XMLValueChecker::IsGoodString(), NumericConverter::LookupFormat(), mProject, ProjectFramePlacement(), settings(), ShowError(), NumericConverter::TIME, 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 297 of file ProjectFileIO.cpp.

298 {
299  auto &connectionPtr = ConnectionPtr::Get( mProject );
300  return connectionPtr.mpConnection != nullptr;
301 }

References ConnectionPtr::Get(), and mProject.

Here is the call graph for this function:

◆ InitializeSQL()

bool ProjectFileIO::InitializeSQL ( )
static

Definition at line 214 of file ProjectFileIO.cpp.

215 {
216  static SQLiteIniter sqliteIniter;
217  return sqliteIniter.mRc == SQLITE_OK;
218 }

References SQLiteIniter::mRc.

Referenced by AudacityApp::OnInit().

Here is the caller graph for this function:

◆ InSet()

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

Definition at line 721 of file ProjectFileIO.cpp.

722 {
723  BlockIDs *blockids = (BlockIDs *) sqlite3_user_data(context);
724  SampleBlockID blockid = sqlite3_value_int64(argv[0]);
725 
726  sqlite3_result_int(context, blockids->find(blockid) != blockids->end());
727 }

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 689 of file ProjectFileIO.cpp.

690 {
691  int rc;
692 
693  wxString sql;
695  sql.Replace("<schema>", schema);
696 
697  rc = sqlite3_exec(db, sql, nullptr, nullptr, nullptr);
698  if (rc != SQLITE_OK)
699  {
700  SetDBError(
701  XO("Unable to initialize the project file")
702  );
703  return false;
704  }
705 
706  return true;
707 }

References ProjectFileID, ProjectFileSchema, ProjectFileVersion, 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 2264 of file ProjectFileIO.cpp.

2265 {
2266  return mModified;
2267 }

References mModified.

Referenced by Compact(), TransportActions::Handler::OnTimerRecord(), and UnsavedChangesFlag().

Here is the caller graph for this function:

◆ IsRecovered()

bool ProjectFileIO::IsRecovered ( ) const

Definition at line 2274 of file ProjectFileIO.cpp.

2275 {
2276  return mRecovered;
2277 }

References mRecovered.

◆ IsTemporary()

bool ProjectFileIO::IsTemporary ( ) const

Definition at line 2269 of file ProjectFileIO.cpp.

2270 {
2271  return mTemporary;
2272 }

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 1857 of file ProjectFileIO.cpp.

1858 {
1859  bool success = false;
1860 
1861  auto cleanup = finally([&]
1862  {
1863  if (!success)
1864  {
1866  }
1867  });
1868 
1869  SaveConnection();
1870 
1871  // Open the project file
1872  if (!OpenConnection(fileName))
1873  {
1874  return false;
1875  }
1876 
1877  wxString project;
1878  wxMemoryBuffer buffer;
1879  bool usedAutosave = true;
1880 
1881  // Get the autosave doc, if any
1882  if (!ignoreAutosave &&
1883  !GetBlob("SELECT dict || doc FROM autosave WHERE id = 1;", buffer))
1884  {
1885  // Error already set
1886  return false;
1887  }
1888 
1889  // If we didn't have an autosave doc, load the project doc instead
1890  if (buffer.GetDataLen() == 0)
1891  {
1892  usedAutosave = false;
1893 
1894  if (!GetBlob("SELECT dict || doc FROM project WHERE id = 1;", buffer))
1895  {
1896  // Error already set
1897  return false;
1898  }
1899  }
1900 
1901  // Missing both the autosave and project docs. This can happen if the
1902  // system were to crash before the first autosave into a temporary file.
1903  // This should be a recoverable scenario.
1904  if (buffer.GetDataLen() == 0)
1905  {
1906  mRecovered = true;
1907  }
1908  else
1909  {
1910  project = ProjectSerializer::Decode(buffer);
1911  if (project.empty())
1912  {
1913  SetError(XO("Unable to decode project document"));
1914 
1915  return false;
1916  }
1917 
1918  XMLFileReader xmlFile;
1919 
1920  // Load 'er up
1921  success = xmlFile.ParseString(this, project);
1922  if (!success)
1923  {
1924  SetError(
1925  XO("Unable to parse project information."),
1926  xmlFile.GetErrorStr()
1927  );
1928  return false;
1929  }
1930 
1931  // Check for orphans blocks...sets mRecovered if any were deleted
1932 
1933  auto blockids = WaveTrackFactory::Get( mProject )
1935  ->GetActiveBlockIDs();
1936  if (blockids.size() > 0)
1937  {
1938  success = DeleteBlocks(blockids, true);
1939  if (!success)
1940  {
1941  return false;
1942  }
1943  }
1944 
1945  // Remember if we used autosave or not
1946  if (usedAutosave)
1947  {
1948  mRecovered = true;
1949  }
1950  }
1951 
1952  // Mark the project modified if we recovered it
1953  if (mRecovered)
1954  {
1955  mModified = true;
1956  }
1957 
1958  // A previously saved project will have a document in the project table, so
1959  // we use that knowledge to determine if this file is an unsaved/temporary
1960  // file or a permanent project file
1961  wxString result;
1962  success = GetValue("SELECT Count(*) FROM project;", result);
1963  if (!success)
1964  {
1965  return false;
1966  }
1967 
1968  mTemporary = !result.IsSameAs(wxT("1"));
1969 
1970  SetFileName(fileName);
1971 
1973 
1974  success = true;
1975 
1976  return true;
1977 }

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

Here is the call graph for this function:

◆ MoveProject()

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

Definition at line 1204 of file ProjectFileIO.cpp.

1205 {
1206  // Assume the src database file is not busy.
1207  if (!RenameOrWarn(src, dst))
1208  return false;
1209 
1210  // So far so good, but the separate -wal and -shm files might yet exist,
1211  // as when checkpointing failed for limited space on the drive.
1212  // If so move them too or else lose data.
1213 
1214  std::vector< std::pair<FilePath, FilePath> > pairs{ { src, dst } };
1215  bool success = false;
1216  auto cleanup = finally([&]{
1217  if (!success) {
1218  // If any one of the renames failed, back out the previous ones.
1219  // This should be a no-fail recovery! Not clear what to do if any
1220  // of these renames fails.
1221  for (auto &pair : pairs) {
1222  if (!(pair.first.empty() && pair.second.empty()))
1223  wxRenameFile(pair.second, pair.first);
1224  }
1225  }
1226  });
1227 
1228  for (const auto &suffix : AuxiliaryFileSuffixes()) {
1229  auto srcName = src + suffix;
1230  if (wxFileExists(srcName)) {
1231  auto dstName = dst + suffix;
1232  if (!RenameOrWarn(srcName, dstName))
1233  return false;
1234  pairs.push_back({ srcName, dstName });
1235  }
1236  }
1237 
1238  return (success = true);
1239 }

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 1664 of file ProjectFileIO.cpp.

1665 {
1666  wxCommandEvent evt{ EVT_CHECKPOINT_FAILURE };
1667  mProject.ProcessEvent(evt);
1668 }

References mProject.

◆ OpenConnection()

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

Definition at line 342 of file ProjectFileIO.cpp.

343 {
344  auto &curConn = CurrConn();
345  wxASSERT(!curConn);
346  bool isTemp = false;
347 
348  if (fileName.empty())
349  {
350  fileName = GetFileName();
351  if (fileName.empty())
352  {
354  isTemp = true;
355  }
356  }
357  else
358  {
359  // If this project resides in the temporary directory, then we'll mark it
360  // as temporary.
361  wxFileName temp(TempDirectory::TempDir(), wxT(""));
362  wxFileName file(fileName);
363  file.SetFullName(wxT(""));
364  if (file == temp)
365  {
366  isTemp = true;
367  }
368  }
369 
370  // Pass weak_ptr to project into DBConnection constructor
371  curConn = std::make_unique<DBConnection>(
372  mProject.shared_from_this(), mpErrors, [this]{ OnCheckpointFailure(); } );
373  auto rc = curConn->Open(fileName);
374  if (rc != SQLITE_OK)
375  {
376  // Must use SetError() here since we do not have an active DB
377  SetError(
378  XO("Failed to open database file:\n\n%s").Format(fileName),
379  {},
380  rc
381  );
382  curConn.reset();
383  return false;
384  }
385 
386  if (!CheckVersion())
387  {
388  CloseConnection();
389  curConn.reset();
390  return false;
391  }
392 
393  mTemporary = isTemp;
394 
395  SetFileName(fileName);
396 
397  return true;
398 }

References CheckVersion(), CloseConnection(), CurrConn(), GetFileName(), mpErrors, mProject, mTemporary, SetError(), SetFileName(), TempDirectory::TempDir(), TempDirectory::UnsavedProjectFileName(), 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 2216 of file ProjectFileIO.cpp.

2217 {
2218  return OpenConnection();
2219 }

References OpenConnection().

Here is the call graph for this function:

◆ operator=()

ProjectFileIO& ProjectFileIO::operator= ( const ProjectFileIO )

◆ Query()

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

Definition at line 533 of file ProjectFileIO.cpp.

534 {
535  int rc = Exec(sql, callback);
536  // SQLITE_ABORT is a non-error return only meaning the callback
537  // stopped the iteration of rows early
538  if ( !(rc == SQLITE_OK || rc == SQLITE_ABORT) )
539  {
540  return false;
541  }
542 
543  return true;
544 }

References Exec().

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

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 1241 of file ProjectFileIO.cpp.

1242 {
1243  if (!wxFileExists(filename))
1244  return false;
1245 
1246  bool success = wxRemoveFile(filename);
1247  auto &suffixes = AuxiliaryFileSuffixes();
1248  for (const auto &suffix : suffixes) {
1249  auto file = filename + suffix;
1250  if (wxFileExists(file))
1251  success = wxRemoveFile(file) && success;
1252  }
1253  return success;
1254 }

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 1164 of file ProjectFileIO.cpp.

1165 {
1166  std::atomic_bool done = {false};
1167  bool success = false;
1168  auto thread = std::thread([&]
1169  {
1170  success = wxRenameFile(src, dst);
1171  done = true;
1172  });
1173 
1174  // Provides a progress dialog with indeterminate mode
1175  using namespace BasicUI;
1177  XO("Copying Project"), XO("This may take several seconds"));
1178  wxASSERT(pd);
1179 
1180  // Wait for the checkpoints to end
1181  while (!done)
1182  {
1183  wxMilliSleep(50);
1184  pd->Pulse();
1185  }
1186  thread.join();
1187 
1188  if (!success)
1189  {
1191  XO("Error Writing to File"),
1192  XO("Audacity failed to write file %s.\n"
1193  "Perhaps disk is full or not writable.\n"
1194  "For tips on freeing up space, click the help button.")
1195  .Format(dst),
1196  "Error:_Disk_full_or_not_writable"
1197  );
1198  return false;
1199  }
1200 
1201  return true;
1202 }

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 2253 of file ProjectFileIO.cpp.

2254 {
2255  FilePath fileName = mFileName;
2256  if (!CloseConnection())
2257  {
2258  return false;
2259  }
2260 
2261  return OpenConnection(fileName);
2262 }

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

Here is the call graph for this function:

◆ RestoreConnection()

void ProjectFileIO::RestoreConnection ( )
private

Definition at line 466 of file ProjectFileIO.cpp.

467 {
468  auto &curConn = CurrConn();
469  if (curConn)
470  {
471  if (!curConn->Close())
472  {
473  // Store an error message
474  SetDBError(
475  XO("Failed to restore connection")
476  );
477  }
478  }
479 
480  curConn = std::move(mPrevConn);
483 
484  mPrevFileName.clear();
485 }

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 1129 of file ProjectFileIO.cpp.

1130 {
1131  wxFileNameWrapper fn{ src };
1132 
1133  // Extra characters inserted into filename before extension
1134  wxString extra =
1135 #ifdef __WXGTK__
1136  wxT("~")
1137 #else
1138  wxT(".bak")
1139 #endif
1140  ;
1141 
1142  int nn = 1;
1143  auto numberString = [](int num) -> wxString {
1144  return num == 1 ? wxString{} : wxString::Format(".%d", num);
1145  };
1146 
1147  auto suffixes = AuxiliaryFileSuffixes();
1148  suffixes.push_back({});
1149 
1150  // Find backup paths not already occupied; check all auxiliary suffixes
1151  const auto name = fn.GetName();
1152  FilePath result;
1153  do {
1154  fn.SetName( name + numberString(nn++) + extra );
1155  result = fn.GetFullPath();
1156  }
1157  while( std::any_of(suffixes.begin(), suffixes.end(), [&](auto &suffix){
1158  return wxFileExists(result + suffix);
1159  }) );
1160 
1161  return result;
1162 }

References AuxiliaryFileSuffixes(), fn, and name.

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 419 of file ProjectFileIO.cpp.

420 {
421  // Should do nothing in proper usage, but be sure not to leak a connection:
423 
424  mPrevConn = std::move(CurrConn());
427 
428  SetFileName({});
429 }

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 2210 of file ProjectFileIO.cpp.

2211 {
2212  return CopyTo(fileName, XO("Backing up project"), false, true,
2213  {&TrackList::Get(mProject)});
2214 }

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 2002 of file ProjectFileIO.cpp.

2004 {
2005  // In the case where we're saving a temporary project to a permanent project,
2006  // we'll try to simply rename the project to save a bit of time. We then fall
2007  // through to the normal Save (not SaveAs) processing.
2008  if (IsTemporary() && mFileName != fileName)
2009  {
2010  FilePath savedName = mFileName;
2011  if (CloseConnection())
2012  {
2013  bool reopened = false;
2014  bool moved = false;
2015  if (true == (moved = MoveProject(savedName, fileName)))
2016  {
2017  if (OpenConnection(fileName))
2018  reopened = true;
2019  else {
2020  MoveProject(fileName, savedName);
2021  moved = false; // No longer moved
2022 
2023  reopened = OpenConnection(savedName);
2024  }
2025  }
2026  else {
2027  // Rename can fail -- if it's to a different device, requiring
2028  // real copy of contents, which might exhaust space
2029  reopened = OpenConnection(savedName);
2030  }
2031 
2032  // Warning issued in MoveProject()
2033  if (reopened && !moved) {
2034  return false;
2035  }
2036 
2037  if (!reopened) {
2038  wxTheApp->CallAfter([this]{
2039  ShowError( {},
2040  XO("Warning"),
2041  XO(
2042 "The project's database failed to reopen, "
2043 "possibly because of limited space on the storage device."),
2044  "Error:_Disk_full_or_not_writable"
2045  );
2046  wxCommandEvent evt{ EVT_RECONNECTION_FAILURE };
2047  mProject.ProcessEvent(evt);
2048  });
2049 
2050  return false;
2051  }
2052  }
2053  }
2054 
2055  // If we're saving to a different file than the current one, then copy the
2056  // current to the new file and make it the active file.
2057  if (mFileName != fileName)
2058  {
2059  // Do NOT prune here since we need to retain the Undo history
2060  // after we switch to the new file.
2061  if (!CopyTo(fileName, XO("Saving project"), false))
2062  {
2063  ShowError( {},
2064  XO("Error Saving Project"),
2066  "Error:_Disk_full_or_not_writable"
2067  );
2068  return false;
2069  }
2070 
2071  // Open the newly created database
2072  Connection newConn = std::make_unique<DBConnection>(
2073  mProject.shared_from_this(), mpErrors,
2074  [this]{ OnCheckpointFailure(); });
2075 
2076  // NOTE: There is a noticeable delay here when dealing with large multi-hour
2077  // projects that we just created. The delay occurs in Open() when it
2078  // calls SafeMode() and is due to the switch from the NONE journal mode
2079  // to the WAL journal mode.
2080  //
2081  // So, we do the Open() in a thread and display a progress dialog. Since
2082  // this is currently the only known instance where this occurs, we do the
2083  // threading here. If more instances are identified, then the threading
2084  // should be moved to DBConnection::Open(), wrapping the SafeMode() call
2085  // there.
2086  {
2087  std::atomic_bool done = {false};
2088  bool success = true;
2089  auto thread = std::thread([&]
2090  {
2091  auto rc = newConn->Open(fileName);
2092  if (rc != SQLITE_OK)
2093  {
2094  // Capture the error string
2095  SetError(Verbatim(sqlite3_errstr(rc)));
2096  success = false;
2097  }
2098  done = true;
2099  });
2100 
2101  // Provides a progress dialog with indeterminate mode
2102  using namespace BasicUI;
2103  auto pd = MakeGenericProgress({},
2104  XO("Syncing"), XO("This may take several seconds"));
2105  wxASSERT(pd);
2106 
2107  // Wait for the checkpoints to end
2108  while (!done)
2109  {
2110  wxMilliSleep(50);
2111  pd->Pulse();
2112  }
2113  thread.join();
2114 
2115  if (!success)
2116  {
2117  // Additional help via a Help button links to the manual.
2118  ShowError( {},
2119  XO("Error Saving Project"),
2120  XO("The project failed to open, possibly due to limited space\n"
2121  "on the storage device.\n\n%s").Format(GetLastError()),
2122  "Error:_Disk_full_or_not_writable");
2123 
2124  newConn = nullptr;
2125 
2126  // Clean up the destination project
2127  if (!wxRemoveFile(fileName))
2128  {
2129  wxLogMessage("Failed to remove destination project after open failure: %s", fileName);
2130  }
2131 
2132  return false;
2133  }
2134  }
2135 
2136  // Autosave no longer needed in original project file.
2137  if (!AutoSaveDelete())
2138  {
2139  // Additional help via a Help button links to the manual.
2140  ShowError( {},
2141  XO("Error Saving Project"),
2142  XO("Unable to remove autosave information, possibly due to limited space\n"
2143  "on the storage device.\n\n%s").Format(GetLastError()),
2144  "Error:_Disk_full_or_not_writable");
2145 
2146  newConn = nullptr;
2147 
2148  // Clean up the destination project
2149  if (!wxRemoveFile(fileName))
2150  {
2151  wxLogMessage("Failed to remove destination project after AutoSaveDelete failure: %s", fileName);
2152  }
2153 
2154  return false;
2155  }
2156 
2157  if (lastSaved) {
2158  // Bug2605: Be sure not to save orphan blocks
2159  bool recovered = mRecovered;
2160  SampleBlockIDSet blockids;
2161  InspectBlocks( *lastSaved, {}, &blockids );
2162  // TODO: Not sure what to do if the deletion fails
2163  DeleteBlocks(blockids, true);
2164  // Don't set mRecovered if any were deleted
2165  mRecovered = recovered;
2166  }
2167 
2168  // Try to compact the original project file.
2169  auto empty = TrackList::Create(&mProject);
2170  Compact( { lastSaved ? lastSaved : empty.get() }, true );
2171 
2172  // Safe to close the original project file now. Not much we can do if this fails,
2173  // but we should still be in good shape since we'll be switching to the newly
2174  // saved database below.
2175  CloseProject();
2176 
2177  // And make it the active project file
2178  UseConnection(std::move(newConn), fileName);
2179  }
2180  else
2181  {
2182  if ( !UpdateSaved( nullptr ) ) {
2183  ShowError( {},
2184  XO("Error Saving Project"),
2186  "Error:_Disk_full_or_not_writable"
2187  );
2188  return false;
2189  }
2190  }
2191 
2192  // Reaching this point defines success and all the rest are no-fail
2193  // operations:
2194 
2195  // No longer modified
2196  mModified = false;
2197 
2198  // No longer recovered
2199  mRecovered = false;
2200 
2201  // No longer a temporary project
2202  mTemporary = false;
2203 
2204  // Adjust the title
2205  SetProjectTitle();
2206 
2207  return true;
2208 }

References AutoSaveDelete(), CloseConnection(), CloseProject(), Compact(), CopyTo(), TrackList::Create(), DeleteBlocks(), GetLastError(), InspectBlocks(), IsTemporary(), BasicUI::MakeGenericProgress(), mFileName, mModified, MoveProject(), mpErrors, mProject, mRecovered, mTemporary, OpenConnection(), 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 2354 of file ProjectFileIO.cpp.

2355 {
2356  auto &currConn = CurrConn();
2357  if (!currConn)
2358  return;
2359 
2360  // Determine if we can bypass sample block deletes during shutdown.
2361  //
2362  // IMPORTANT:
2363  // If the project was compacted, then we MUST bypass further
2364  // deletions since the new file doesn't have the blocks that the
2365  // Sequences expect to be there.
2366 
2367  currConn->SetBypass( true );
2368 
2369  // Only permanent project files need cleaning at shutdown
2370  if (!IsTemporary() && !WasCompacted())
2371  {
2372  // If we still have unused blocks, then we must not bypass deletions
2373  // during shutdown. Otherwise, we would have orphaned blocks the next time
2374  // the project is opened.
2375  //
2376  // An example of when dead blocks will exist is when a user opens a permanent
2377  // project, adds a track (with samples) to it, and chooses not to save the
2378  // changes.
2379  if (HadUnused())
2380  {
2381  currConn->SetBypass( false );
2382  }
2383  }
2384 
2385  return;
2386 }

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 2346 of file ProjectFileIO.cpp.

2348 {
2349  auto &currConn = CurrConn();
2350  if (currConn)
2351  currConn->SetDBError(msg, libraryError, errorCode);
2352 }

References CurrConn().

Referenced by AutoSaveDelete(), CopyTo(), DeleteBlocks(), DiscardConnection(), Exec(), GetBlob(), 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 2338 of file ProjectFileIO.cpp.

2340 {
2341  auto &currConn = CurrConn();
2342  if (currConn)
2343  currConn->SetError(msg, libraryError, errorCode);
2344 }

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 1492 of file ProjectFileIO.cpp.

1493 {
1494  auto &project = mProject;
1495 
1496  if (!mFileName.empty())
1497  {
1499  }
1500 
1501  mFileName = fileName;
1502 
1503  if (!mFileName.empty())
1504  {
1506  }
1507 
1508  if (IsTemporary())
1509  {
1510  project.SetProjectName({});
1511  }
1512  else
1513  {
1514  project.SetProjectName(wxFileName(mFileName).GetName());
1515  }
1516 
1517  SetProjectTitle();
1518 }

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 1442 of file ProjectFileIO.cpp.

1443 {
1444  auto &project = mProject;
1445  auto pWindow = project.GetFrame();
1446  if (!pWindow)
1447  {
1448  return;
1449  }
1450  auto &window = *pWindow;
1451  wxString name = project.GetProjectName();
1452 
1453  // If we are showing project numbers, then we also explicitly show "<untitled>" if there
1454  // is none.
1455  if (number >= 0)
1456  {
1457  name =
1458  /* i18n-hint: The %02i is the project number, the %s is the project name.*/
1459  XO("[Project %02i] Audacity \"%s\"")
1460  .Format( number + 1,
1461  name.empty() ? XO("<untitled>") : Verbatim((const char *)name))
1462  .Translation();
1463  }
1464  // If we are not showing numbers, then <untitled> shows as 'Audacity'.
1465  else if (name.empty())
1466  {
1467  name = _TS("Audacity");
1468  }
1469 
1470  if (mRecovered)
1471  {
1472  name += wxT(" ");
1473  /* i18n-hint: E.g this is recovered audio that had been lost.*/
1474  name += _("(Recovered)");
1475  }
1476 
1477  if (name != window.GetTitle())
1478  {
1479  window.SetTitle( name );
1480  window.SetName(name); // to make the nvda screen reader read the correct title
1481 
1482  project.QueueEvent(
1483  safenew wxCommandEvent{ EVT_PROJECT_TITLE_CHANGE } );
1484  }
1485 }

References _, _TS, TranslatableString::empty(), AudacityProject::GetFrame(), mProject, mRecovered, name, safenew, Verbatim(), and XO.

Referenced by 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 1064 of file ProjectFileIO.cpp.

1065 {
1066  SampleBlockIDSet active;
1067  unsigned long long current = 0;
1068 
1069  {
1070  auto fn = BlockSpaceUsageAccumulator( current );
1071  for (auto pTracks : tracks)
1072  if (pTracks)
1073  InspectBlocks( *pTracks, fn,
1074  &active // Visit unique blocks only
1075  );
1076  }
1077 
1078  // Get the number of blocks and total length from the project file.
1079  unsigned long long total = GetTotalUsage();
1080  unsigned long long blockcount = 0;
1081 
1082  auto cb = [&blockcount](int cols, char **vals, char **)
1083  {
1084  // Convert
1085  wxString(vals[0]).ToULongLong(&blockcount);
1086  return 0;
1087  };
1088 
1089  if (!Query("SELECT Count(*) FROM sampleblocks;", cb) || blockcount == 0)
1090  {
1091  // Shouldn't compact since we don't have the full picture
1092  return false;
1093  }
1094 
1095  // Remember if we had unused blocks in the project file
1096  mHadUnused = (blockcount > active.size());
1097 
1098  // Let's make a percentage...should be plenty of head room
1099  current *= 100;
1100 
1101  wxLogDebug(wxT("used = %lld total = %lld %lld"), current, total, total ? current / total : 0);
1102  if (!total || current / total > 80)
1103  {
1104  wxLogDebug(wxT("not compacting"));
1105  return false;
1106  }
1107  wxLogDebug(wxT("compacting"));
1108 
1109  return true;
1110 }

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

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 2306 of file ProjectFileIO.cpp.

2310 {
2311  using namespace audacity;
2312  using namespace BasicUI;
2313  ShowErrorDialog( placement, dlogTitle, message, helpPage,
2314  ErrorDialogOptions{ ErrorDialogType::ModalErrorReport }
2315  .Log(ToWString(GetLastLog())));
2316 }

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 1436 of file ProjectFileIO.cpp.

1437 {
1438  SetProjectTitle();
1439 }

References SetProjectTitle().

Here is the call graph for this function:

◆ UpdateSaved()

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

Definition at line 1979 of file ProjectFileIO.cpp.

1980 {
1981  ProjectSerializer doc;
1982  WriteXMLHeader(doc);
1983  WriteXML(doc, false, tracks);
1984 
1985  if (!WriteDoc("project", doc))
1986  {
1987  return false;
1988  }
1989 
1990  // Autosave no longer needed
1991  if (!AutoSaveDelete())
1992  {
1993  return false;
1994  }
1995 
1996  return true;
1997 }

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:

◆ UpgradeSchema()

bool ProjectFileIO::UpgradeSchema ( )
private

Definition at line 709 of file ProjectFileIO.cpp.

710 {
711  // To do
712  return true;
713 }

Referenced by CheckVersion().

Here is the caller graph for this function:

◆ UseConnection()

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

Definition at line 487 of file ProjectFileIO.cpp.

488 {
489  auto &curConn = CurrConn();
490  wxASSERT(!curConn);
491 
492  curConn = std::move(conn);
493  SetFileName(filePath);
494 }

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 1426 of file ProjectFileIO.cpp.

1427 {
1428  return mWasCompacted;
1429 }

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 1783 of file ProjectFileIO.cpp.

1786 {
1787  auto db = DB();
1788  int rc;
1789 
1790  // For now, we always use an ID of 1. This will replace the previously
1791  // written row every time.
1792  char sql[256];
1793  sqlite3_snprintf(sizeof(sql),
1794  sql,
1795  "INSERT INTO %s.%s(id, dict, doc) VALUES(1, ?1, ?2)"
1796  " ON CONFLICT(id) DO UPDATE SET dict = ?1, doc = ?2;",
1797  schema,
1798  table);
1799 
1800  sqlite3_stmt *stmt = nullptr;
1801  auto cleanup = finally([&]
1802  {
1803  if (stmt)
1804  {
1805  sqlite3_finalize(stmt);
1806  }
1807  });
1808 
1809  rc = sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr);
1810  if (rc != SQLITE_OK)
1811  {
1812  ADD_EXCEPTION_CONTEXT("sqlite3.query", sql);
1813  ADD_EXCEPTION_CONTEXT("sqlite3.rc", std::to_string(rc));
1814  ADD_EXCEPTION_CONTEXT("sqlite3.context", "ProjectGileIO::WriteDoc::prepare");
1815 
1816  SetDBError(
1817  XO("Unable to prepare project file command:\n\n%s").Format(sql)
1818  );
1819  return false;
1820  }
1821 
1822  const wxMemoryBuffer &dict = autosave.GetDict();
1823  const wxMemoryBuffer &data = autosave.GetData();
1824 
1825  // Bind statement parameters
1826  // Might return SQL_MISUSE which means it's our mistake that we violated
1827  // preconditions; should return SQL_OK which is 0
1828  if (sqlite3_bind_blob(stmt, 1, dict.GetData(), dict.GetDataLen(), SQLITE_STATIC) ||
1829  sqlite3_bind_blob(stmt, 2, data.GetData(), data.GetDataLen(), SQLITE_STATIC))
1830  {
1831  ADD_EXCEPTION_CONTEXT("sqlite3.query", sql);
1832  ADD_EXCEPTION_CONTEXT("sqlite3.rc", std::to_string(rc));
1833  ADD_EXCEPTION_CONTEXT("sqlite3.context", "ProjectGileIO::WriteDoc::bind");
1834 
1835  SetDBError(
1836  XO("Unable to bind to blob")
1837  );
1838  return false;
1839  }
1840 
1841  rc = sqlite3_step(stmt);
1842  if (rc != SQLITE_DONE)
1843  {
1844  ADD_EXCEPTION_CONTEXT("sqlite3.query", sql);
1845  ADD_EXCEPTION_CONTEXT("sqlite3.rc", std::to_string(rc));
1846  ADD_EXCEPTION_CONTEXT("sqlite3.context", "ProjectGileIO::WriteDoc::step");
1847 
1848  SetDBError(
1849  XO("Failed to update the project file.\nThe following command failed:\n\n%s").Format(sql)
1850  );
1851  return false;
1852  }
1853 
1854  return true;
1855 }

References ADD_EXCEPTION_CONTEXT, DB(), ProjectSerializer::GetData(), ProjectSerializer::GetDict(), 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 1685 of file ProjectFileIO.cpp.

1689 {
1690  auto &proj = mProject;
1691  auto &tracklist = tracks ? *tracks : TrackList::Get(proj);
1692  auto &viewInfo = ViewInfo::Get(proj);
1693  auto &tags = Tags::Get(proj);
1694  const auto &settings = ProjectSettings::Get(proj);
1695 
1696  //TIMER_START( "AudacityProject::WriteXML", xml_writer_timer );
1697 
1698  xmlFile.StartTag(wxT("project"));
1699  xmlFile.WriteAttr(wxT("xmlns"), wxT("http://audacity.sourceforge.net/xml/"));
1700 
1701  xmlFile.WriteAttr(wxT("version"), wxT(AUDACITY_FILE_FORMAT_VERSION));
1702  xmlFile.WriteAttr(wxT("audacityversion"), AUDACITY_VERSION_STRING);
1703 
1704  viewInfo.WriteXMLAttributes(xmlFile);
1705  xmlFile.WriteAttr(wxT("rate"), settings.GetRate());
1706  xmlFile.WriteAttr(wxT("snapto"), settings.GetSnapTo() ? wxT("on") : wxT("off"));
1707  xmlFile.WriteAttr(wxT("selectionformat"),
1708  settings.GetSelectionFormat().Internal());
1709  xmlFile.WriteAttr(wxT("frequencyformat"),
1710  settings.GetFrequencySelectionFormatName().Internal());
1711  xmlFile.WriteAttr(wxT("bandwidthformat"),
1712  settings.GetBandwidthSelectionFormatName().Internal());
1713 
1714  tags.WriteXML(xmlFile);
1715 
1716  unsigned int ndx = 0;
1717  tracklist.Any().Visit([&](const Track *t)
1718  {
1719  auto useTrack = t;
1720  if ( recording ) {
1721  // When append-recording, there is a temporary "shadow" track accumulating
1722  // changes and displayed on the screen but it is not yet part of the
1723  // regular track list. That is the one that we want to back up.
1724  // SubstitutePendingChangedTrack() fetches the shadow, if the track has
1725  // one, else it gives the same track back.
1726  useTrack = t->SubstitutePendingChangedTrack().get();
1727  }
1728  else if ( useTrack->GetId() == TrackId{} ) {
1729  // This is a track added during a non-appending recording that is
1730  // not yet in the undo history. The UndoManager skips backing it up
1731  // when pushing. Don't auto-save it.
1732  return;
1733  }
1734  useTrack->WriteXML(xmlFile);
1735  });
1736 
1737  xmlFile.EndTag(wxT("project"));
1738 
1739  //TIMER_STOP( xml_writer_timer );
1740 }

References AUDACITY_FILE_FORMAT_VERSION, XMLWriter::EndTag(), ProjectSettings::Get(), Tags::Get(), TrackList::Get(), ViewInfo::Get(), mProject, settings(), XMLWriter::StartTag(), Track::SubstitutePendingChangedTrack(), and XMLWriter::WriteAttr().

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 1670 of file ProjectFileIO.cpp.

1671 {
1672  xmlFile.Write(wxT("<?xml "));
1673  xmlFile.Write(wxT("version=\"1.0\" "));
1674  xmlFile.Write(wxT("standalone=\"no\" "));
1675  xmlFile.Write(wxT("?>\n"));
1676 
1677  xmlFile.Write(wxT("<!DOCTYPE "));
1678  xmlFile.Write(wxT("project "));
1679  xmlFile.Write(wxT("PUBLIC "));
1680  xmlFile.Write(wxT("\"-//audacityproject-1.3.0//DTD//EN\" "));
1681  xmlFile.Write(wxT("\"http://audacity.sourceforge.net/xml/audacityproject-1.3.0.dtd\" "));
1682  xmlFile.Write(wxT(">\n"));
1683 }

References XMLWriter::Write().

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 307 of file ProjectFileIO.h.

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

◆ mModified

bool ProjectFileIO::mModified
private

Definition at line 298 of file ProjectFileIO.h.

Referenced by AutoSave(), AutoSaveDelete(), IsModified(), LoadProject(), and SaveProject().

◆ mpErrors

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

◆ mPrevConn

Connection ProjectFileIO::mPrevConn
private

Definition at line 309 of file ProjectFileIO.h.

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

◆ mPrevFileName

FilePath ProjectFileIO::mPrevFileName
private

Definition at line 310 of file ProjectFileIO.h.

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

◆ mPrevTemporary

bool ProjectFileIO::mPrevTemporary
private

Definition at line 311 of file ProjectFileIO.h.

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

◆ mProject

AudacityProject& ProjectFileIO::mProject
private

◆ mRecovered

bool ProjectFileIO::mRecovered
private

Definition at line 295 of file ProjectFileIO.h.

Referenced by DeleteBlocks(), IsRecovered(), LoadProject(), SaveProject(), and SetProjectTitle().

◆ mTemporary

bool ProjectFileIO::mTemporary
private

◆ mWasCompacted

bool ProjectFileIO::mWasCompacted
private

Definition at line 304 of file ProjectFileIO.h.

Referenced by Compact(), and WasCompacted().


The documentation for this class was generated from the following files:
ProjectFileIO::CopyTo
bool CopyTo(const FilePath &destpath, const TranslatableString &msg, bool isTemporary, bool prune=false, const std::vector< const TrackList * > &tracks={})
Definition: ProjectFileIO.cpp:805
XMLWriter::EndTag
virtual void EndTag(const wxString &name)
Definition: XMLWriter.cpp:100
ProjectFileIO::HadUnused
bool HadUnused()
Definition: ProjectFileIO.cpp:1431
FilePath
wxString FilePath
Definition: Identifier.h:227
ViewInfo::Get
static ViewInfo & Get(AudacityProject &project)
Definition: ViewInfo.cpp:156
TranslatableString::empty
bool empty() const
Definition: TranslatableString.h:72
BasicUI::ErrorDialogOptions::Log
ErrorDialogOptions && Log(std::wstring log_) &&
Definition: BasicUI.h:61
WaveTrackFactory::Get
static WaveTrackFactory & Get(AudacityProject &project)
Definition: WaveTrack.cpp:2799
ProjectFileIO::GetFileName
const FilePath & GetFileName() const
Definition: ProjectFileIO.cpp:1487
ProjectSerializer::Decode
static wxString Decode(const wxMemoryBuffer &buffer)
Definition: ProjectSerializer.cpp:386
ProjectFileIO::GetConnection
DBConnection & GetConnection()
Return a reference to a connection, creating it as needed on demand; throw on failure.
Definition: ProjectFileIO.cpp:303
BasicUI::ProgressResult::Success
@ Success
ProjectFileIO::GetLibraryError
const TranslatableString & GetLibraryError() const
Definition: ProjectFileIO.cpp:2323
ProjectFileIO::SetFileName
void SetFileName(const FilePath &fileName)
Definition: ProjectFileIO.cpp:1492
SampleBlockIDSet
std::unordered_set< SampleBlockID > SampleBlockIDSet
Definition: WaveTrack.h:683
fn
static const auto fn
Definition: WaveformView.cpp:1114
wxFileNameWrapper
Definition: wxFileNameWrapper.h:21
ProjectFileIO::UpgradeSchema
bool UpgradeSchema()
Definition: ProjectFileIO.cpp:709
AudacityProject::GetFrame
wxFrame * GetFrame()
Definition: Project.h:121
BasicUI::ShowErrorDialog
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:233
ProjectFileIO::WriteDoc
bool WriteDoc(const char *table, const ProjectSerializer &autosave, const char *schema="main")
Definition: ProjectFileIO.cpp:1783
ProjectFileIO::CurrConn
Connection & CurrConn()
Definition: ProjectFileIO.cpp:1112
pdlgHideStopButton
@ pdlgHideStopButton
Definition: ProgressDialog.h:38
ProjectFileVersion
static const int ProjectFileVersion
Definition: ProjectFileIO.cpp:79
Format
Abstract base class used in importing a file.
ProjectFileIO::AuxiliaryFileSuffixes
static const std::vector< wxString > & AuxiliaryFileSuffixes()
Definition: ProjectFileIO.cpp:1118
BasicUI::ProgressResult
ProgressResult
Definition: BasicUI.h:145
ProjectFileIO::RemoveProject
static bool RemoveProject(const FilePath &filename)
Remove any files associated with a project at given path; return true if successful.
Definition: ProjectFileIO.cpp:1241
ProjectFileIO::GetLastErrorCode
int GetLastErrorCode() const
Definition: ProjectFileIO.cpp:2328
Connection
std::unique_ptr< DBConnection > Connection
Definition: DBConnection.h:153
XO
#define XO(s)
Definition: Internat.h:31
XMLStringWriter
Wrapper to output XML data to strings.
Definition: XMLWriter.h:136
ProjectFileIO::IsTemporary
bool IsTemporary() const
Definition: ProjectFileIO.cpp:2269
ProjectFileIO::Get
static ProjectFileIO & Get(AudacityProject &project)
Definition: ProjectFileIO.cpp:269
ProjectFileIO::CloseConnection
bool CloseConnection()
Definition: ProjectFileIO.cpp:400
ProjectSettings::Get
static ProjectSettings & Get(AudacityProject &project)
Definition: ProjectSettings.cpp:40
audacity
Definition: ErrorReportDialog.h:22
ProjectFileIO::DB
sqlite3 * DB()
Definition: ProjectFileIO.cpp:333
ProjectFileIO::InstallSchema
bool InstallSchema(sqlite3 *db, const char *schema="main")
Definition: ProjectFileIO.cpp:689
SQLiteIniter::mRc
int mRc
Definition: ProjectFileIO.cpp:211
ExecCallback
static int ExecCallback(void *data, int cols, char **vals, char **names)
Definition: ProjectFileIO.cpp:496
ProjectFileSchema
static const char * ProjectFileSchema
Definition: ProjectFileIO.cpp:89
ProjectFileIO::SaveConnection
void SaveConnection()
Definition: ProjectFileIO.cpp:419
InspectBlocks
void InspectBlocks(const TrackList &tracks, BlockInspector inspector, SampleBlockIDSet *pIDs)
Definition: WaveTrack.cpp:2780
ProjectFileIO::mFileName
FilePath mFileName
Definition: ProjectFileIO.h:292
ProjectFileID
static const int ProjectFileID
Definition: ProjectFileIO.cpp:64
XMLValueChecker::IsGoodString
static bool IsGoodString(const wxString &str)
Definition: XMLTagHandler.cpp:41
Tags::Get
static Tags & Get(AudacityProject &project)
Definition: Tags.cpp:237
SampleBlockID
long long SampleBlockID
Definition: ProjectFileIO.h:39
NumericConverter::BANDWIDTH
@ BANDWIDTH
Definition: NumericTextCtrl.h:55
ProjectFileIO::ShouldCompact
bool ShouldCompact(const std::vector< const TrackList * > &tracks)
Definition: ProjectFileIO.cpp:1064
ProjectFileIO::WriteXML
void WriteXML(XMLWriter &xmlFile, bool recording=false, const TrackList *tracks=nullptr)
Definition: ProjectFileIO.cpp:1685
_TS
#define _TS(s)
Definition: Internat.h:27
DBConnection::DB
sqlite3 * DB()
Definition: DBConnection.cpp:375
ProjectFileIO::Compact
void Compact(const std::vector< const TrackList * > &tracks, bool force=false)
Definition: ProjectFileIO.cpp:1293
ProjectFileIO::mModified
bool mModified
Definition: ProjectFileIO.h:298
ProjectFileIO::mPrevTemporary
bool mPrevTemporary
Definition: ProjectFileIO.h:311
ProjectFileIO::mRecovered
bool mRecovered
Definition: ProjectFileIO.h:295
DBConnection::GetDBPage
@ GetDBPage
Definition: DBConnection.h:80
NumericConverter::LookupFormat
static NumericFormatSymbol LookupFormat(Type type, const wxString &id)
Definition: NumericTextCtrl.cpp:702
ProjectFileIORegistry::Lookup
TagHandlerFactory Lookup(const wxString &tag)
Definition: ProjectFileIORegistry.cpp:35
Track::SubstitutePendingChangedTrack
std::shared_ptr< Track > SubstitutePendingChangedTrack()
Definition: Track.cpp:1186
ProjectFileIO::SetError
void SetError(const TranslatableString &msg, const TranslatableString &libraryError={}, int errorCode={})
Just set stored errors.
Definition: ProjectFileIO.cpp:2338
XMLFileReader::GetErrorStr
const TranslatableString & GetErrorStr() const
Definition: XMLFileReader.cpp:178
ProjectFramePlacement
std::unique_ptr< const BasicUI::WindowPlacement > ProjectFramePlacement(AudacityProject *project)
Make a WindowPlacement object suitable for project (which may be null)
Definition: Project.cpp:205
ProjectSerializer::GetDict
const wxMemoryBuffer & GetDict() const
Definition: ProjectSerializer.cpp:365
ProjectFileIO::UpdatePrefs
void UpdatePrefs() override
Definition: ProjectFileIO.cpp:1436
ProjectFileIO::CheckVersion
bool CheckVersion()
Definition: ProjectFileIO.cpp:618
ProjectFileIO::InSet
static void InSet(sqlite3_context *context, int argc, sqlite3_value **argv)
Definition: ProjectFileIO.cpp:721
ProjectFileIO::UseConnection
void UseConnection(Connection &&conn, const FilePath &filePath)
Definition: ProjectFileIO.cpp:487
ProjectFileIO::DeleteBlocks
bool DeleteBlocks(const BlockIDs &blockids, bool complement)
Definition: ProjectFileIO.cpp:729
ProjectFileIO::AutoSaveDelete
bool AutoSaveDelete(sqlite3 *db=nullptr)
Definition: ProjectFileIO.cpp:1757
AUDACITY_FILE_FORMAT_VERSION
#define AUDACITY_FILE_FORMAT_VERSION
Definition: ProjectFileIO.cpp:43
ProjectFileIO::DiscardConnection
void DiscardConnection()
Definition: ProjectFileIO.cpp:432
XMLFileReader::ParseString
bool ParseString(XMLTagHandler *baseHandler, const wxString &xmldata)
Definition: XMLFileReader.cpp:136
name
const TranslatableString name
Definition: Distortion.cpp:98
ProjectFileIO::WriteXMLHeader
void WriteXMLHeader(XMLWriter &xmlFile) const
Definition: ProjectFileIO.cpp:1670
NumericConverter::TIME
@ TIME
Definition: NumericTextCtrl.h:52
ProjectFileIO::WasCompacted
bool WasCompacted()
Definition: ProjectFileIO.cpp:1426
DBConnection::GetRootPage
@ GetRootPage
Definition: DBConnection.h:79
ProjectFileIO::OpenConnection
bool OpenConnection(FilePath fileName={})
Definition: ProjectFileIO.cpp:342
THROW_INCONSISTENCY_EXCEPTION
#define THROW_INCONSISTENCY_EXCEPTION
Throw InconsistencyException, using C++ preprocessor to identify the source code location.
Definition: InconsistencyException.h:79
sFileIOKey
static const AudacityProject::AttachedObjects::RegisteredFactory sFileIOKey
Definition: ProjectFileIO.cpp:262
ProjectFileIO::GetTotalUsage
int64_t GetTotalUsage()
Definition: ProjectFileIO.cpp:2412
ProjectFileIO::UpdateSaved
bool UpdateSaved(const TrackList *tracks=nullptr)
Definition: ProjectFileIO.cpp:1979
ProjectFileIO::mPrevFileName
FilePath mPrevFileName
Definition: ProjectFileIO.h:310
ProjectFileIO::GetLastError
const TranslatableString & GetLastError() const
Definition: ProjectFileIO.cpp:2318
BasicUI::ErrorDialogOptions
Options for variations of error dialogs; the default is for modal dialogs.
Definition: BasicUI.h:49
FileNames::IsOnFATFileSystem
AUDACITY_DLL_API bool IsOnFATFileSystem(const FilePath &path)
NumericConverter::FREQUENCY
@ FREQUENCY
Definition: NumericTextCtrl.h:54
ProjectFileIO::GetValue
bool GetValue(const char *sql, wxString &value)
Definition: ProjectFileIO.cpp:546
BlockIDs
std::unordered_set< SampleBlockID > BlockIDs
Definition: ProjectFileIO.h:43
ProjectFileIO::get2
static unsigned int get2(const unsigned char *ptr)
Definition: ProjectFileIO.cpp:2637
min
int min(int a, int b)
Definition: CompareAudioCommand.cpp:106
ProjectSerializer::GetData
const wxMemoryBuffer & GetData() const
Definition: ProjectSerializer.cpp:370
TrackId
An in-session identifier of track objects across undo states. It does not persist between sessions.
Definition: Track.h:165
BasicUI
Definition: Export.h:39
TrackList::Get
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:495
XMLFileReader
Reads a file and passes the results through an XMLTagHandler.
Definition: XMLFileReader.h:20
BasicUI::MakeGenericProgress
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:281
Track
Abstract base class for an object holding data associated with points on a time axis.
Definition: Track.h:239
XMLWriter::Write
virtual void Write(const wxString &data)=0
_
#define _(s)
Definition: Internat.h:75
ConnectionPtr::Get
static ConnectionPtr & Get(AudacityProject &project)
Definition: DBConnection.cpp:693
BasicUI::ProgressDialog
Abstraction of a progress dialog with well defined time-to-completion estimate.
Definition: BasicUI.h:154
WaveTrackFactory::GetSampleBlockFactory
const SampleBlockFactoryPtr & GetSampleBlockFactory() const
Definition: WaveTrack.h:717
AudacityProject
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:113
TempDirectory::UnsavedProjectFileName
AUDACITY_DLL_API wxString UnsavedProjectFileName()
Definition: TempDirectory.cpp:107
ProjectFileIO::ShowError
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.
Definition: ProjectFileIO.cpp:2306
ProjectFileIO::mWasCompacted
bool mWasCompacted
Definition: ProjectFileIO.h:304
ProjectFileIO::Query
bool Query(const char *sql, const ExecCB &callback)
Definition: ProjectFileIO.cpp:533
ProjectFileIO::RenameOrWarn
bool RenameOrWarn(const FilePath &src, const FilePath &dst)
Rename a file or put up appropriate warning message.
Definition: ProjectFileIO.cpp:1164
XMLWriter::WriteAttr
void WriteAttr(const wxString &name, const Identifier &value)
Definition: XMLWriter.h:34
Verbatim
TranslatableString Verbatim(wxString str)
Require calls to the one-argument constructor to go through this distinct global function name.
Definition: TranslatableString.h:321
ExceptionType::Internal
@ Internal
Indicates internal failure from Audacity.
TrackList::Create
static std::shared_ptr< TrackList > Create(AudacityProject *pOwner)
Definition: Track.cpp:512
ProjectFileIO::GetLastLog
const wxString & GetLastLog() const
Definition: ProjectFileIO.cpp:2333
ProjectFileIO::MoveProject
bool MoveProject(const FilePath &src, const FilePath &dst)
Definition: ProjectFileIO.cpp:1204
FileException::WriteFailureMessage
static TranslatableString WriteFailureMessage(const wxFileName &fileName)
Definition: FileException.cpp:61
ProjectFileIO::GetBlob
bool GetBlob(const char *sql, wxMemoryBuffer &buffer)
Definition: ProjectFileIO.cpp:560
ProjectFileIO::get4
static unsigned int get4(const unsigned char *ptr)
Definition: ProjectFileIO.cpp:2643
audacity::ToWString
std::wstring ToWString(const std::string &str)
Definition: CodeConversions.cpp:34
SQLiteIniter
Definition: ProjectFileIO.cpp:169
ProjectFileIO::IsModified
bool IsModified() const
Definition: ProjectFileIO.cpp:2264
ProjectFileIO::GetDiskUsage
static int64_t GetDiskUsage(DBConnection &conn, SampleBlockID blockid)
Definition: ProjectFileIO.cpp:2426
ActiveProjects::Remove
void Remove(const FilePath &path)
ProjectFileIO::get_varint
static int get_varint(const unsigned char *ptr, int64_t *out)
Definition: ProjectFileIO.cpp:2654
ProjectFileIO::mPrevConn
Connection mPrevConn
Definition: ProjectFileIO.h:309
safenew
#define safenew
Definition: MemoryX.h:10
settings
static Settings & settings()
Definition: TrackInfo.cpp:86
ADD_EXCEPTION_CONTEXT
#define ADD_EXCEPTION_CONTEXT(name, value)
Definition: SentryHelper.h:21
ActiveProjects::Add
void Add(const FilePath &path)
BlockSpaceUsageAccumulator
std::function< void(const SampleBlock &) > BlockSpaceUsageAccumulator(unsigned long long &total)
Definition: SampleBlock.h:97
ProjectSerializer
a class used to (de)serialize the project catalog
Definition: ProjectSerializer.h:34
SimpleMessageBoxException
A MessageBoxException that shows a given, unvarying string.
Definition: AudacityException.h:95
ProjectFileIO::SetDBError
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.
Definition: ProjectFileIO.cpp:2346
ProjectFileIO::Exec
int Exec(const char *query, const ExecCB &callback)
Definition: ProjectFileIO.cpp:506
ProjectFileIO::mpErrors
std::shared_ptr< DBConnectionErrors > mpErrors
Definition: ProjectFileIO.h:289
ProjectFileIO::SetProjectTitle
void SetProjectTitle(int number=-1)
Definition: ProjectFileIO.cpp:1442
DBConnection::Prepare
sqlite3_stmt * Prepare(enum StatementID id, const char *sql)
Definition: DBConnection.cpp:392
ProjectFileIO::mProject
AudacityProject & mProject
Definition: ProjectFileIO.h:287
XMLWriter::StartTag
virtual void StartTag(const wxString &name)
Definition: XMLWriter.cpp:77
ProjectFileIO::mHadUnused
bool mHadUnused
Definition: ProjectFileIO.h:307
ProjectFileIO::mTemporary
bool mTemporary
Definition: ProjectFileIO.h:301
Internat::CompatibleToDouble
static bool CompatibleToDouble(const wxString &stringToConvert, double *result)
Convert a string to a number.
Definition: Internat.cpp:134
ProjectFileIO::CloseProject
bool CloseProject()
Definition: ProjectFileIO.cpp:2221
ProjectFileIO
Object associated with a project that manages reading and writing of Audacity project file formats,...
Definition: ProjectFileIO.h:62
TempDirectory::TempDir
AUDACITY_DLL_API wxString TempDir()
Definition: TempDirectory.cpp:26
ProjectFileIO::RestoreConnection
void RestoreConnection()
Definition: ProjectFileIO.cpp:466