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

#include <ProjectFileManager.h>

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

Classes

struct  ReadProjectResults
 

Public Types

using ProjectChooserFn = std::function< AudacityProject &(bool)>
 A function that returns a project to use for opening a file; argument is true if opening a project file. More...
 

Public Member Functions

 ProjectFileManager (AudacityProject &project)
 
 ProjectFileManager (const ProjectFileManager &)=delete
 
ProjectFileManageroperator= (const ProjectFileManager &)=delete
 
 ~ProjectFileManager ()
 
bool OpenProject ()
 
void CloseProject ()
 
bool OpenNewProject ()
 
void CompactProjectOnClose ()
 
bool Save ()
 
bool SaveAs (bool allowOverwrite=false)
 
bool SaveAs (const FilePath &newFileName, bool addToHistory=true)
 
bool SaveFromTimerRecording (wxFileName fnFile)
 
bool SaveCopy (const FilePath &fileName=wxT(""))
 
bool Import (const FilePath &fileName, bool addToHistory=true)
 
bool Import (wxArrayString fileNames, bool addToHistory=true)
 
void Compact ()
 
void AddImportedTracks (const FilePath &fileName, TrackHolders &&newTracks)
 
bool GetMenuClose () const
 
void SetMenuClose (bool value)
 
- Public Member Functions inherited from ClientData::Base
virtual ~Base ()
 

Static Public Member Functions

static ProjectFileManagerGet (AudacityProject &project)
 
static const ProjectFileManagerGet (const AudacityProject &project)
 
static void DiscardAutosave (const FilePath &filename)
 
static wxArrayString ShowOpenDialog (FileNames::Operation op, const FileNames::FileType &extraType={})
 Show an open dialogue for opening audio files, and possibly other sorts of files. More...
 
static bool IsAlreadyOpen (const FilePath &projPathName)
 
static AudacityProjectOpenFile (const ProjectChooserFn &chooser, const FilePath &fileName, bool addtohistory=true)
 
static void FixTracks (TrackList &tracks, const std::function< void(const TranslatableString &)> &onError, const std::function< void(const TranslatableString &)> &onUnlink)
 Attempts to find and fix problems in tracks. More...
 

Private Member Functions

bool ImportAndRunTempoDetection (const std::vector< FilePath > &fileNames, bool addToHistory)
 
bool DoImport (const FilePath &fileName, bool addToHistory, std::shared_ptr< ClipMirAudioReader > &resultingReader)
 
AudacityProjectOpenProjectFile (const FilePath &fileName, bool addtohistory)
 
ReadProjectResults ReadProjectFile (const FilePath &fileName, bool discardAutosave=false)
 
bool DoSave (const FilePath &fileName, bool fromSaveAs)
 

Private Attributes

AudacityProjectmProject
 
std::shared_ptr< TrackListmLastSavedTracks
 
bool mMenuClose { false }
 

Detailed Description

Definition at line 32 of file ProjectFileManager.h.

Member Typedef Documentation

◆ ProjectChooserFn

A function that returns a project to use for opening a file; argument is true if opening a project file.

Definition at line 86 of file ProjectFileManager.h.

Constructor & Destructor Documentation

◆ ProjectFileManager() [1/2]

ProjectFileManager::ProjectFileManager ( AudacityProject project)
explicit

Definition at line 110 of file ProjectFileManager.cpp.

111: mProject{ project }
112{
113}
const auto project
AudacityProject & mProject

◆ ProjectFileManager() [2/2]

ProjectFileManager::ProjectFileManager ( const ProjectFileManager )
delete

◆ ~ProjectFileManager()

ProjectFileManager::~ProjectFileManager ( )
default

Member Function Documentation

◆ AddImportedTracks()

void ProjectFileManager::AddImportedTracks ( const FilePath fileName,
TrackHolders &&  newTracks 
)

Definition at line 1174 of file ProjectFileManager.cpp.

1176{
1177 auto &project = mProject;
1178 auto &history = ProjectHistory::Get( project );
1179 auto &projectFileIO = ProjectFileIO::Get( project );
1180 auto &tracks = TrackList::Get( project );
1181
1182 std::vector<Track*> results;
1183
1185
1186 wxFileName fn(fileName);
1187
1188 bool initiallyEmpty = tracks.empty();
1189 double newRate = 0;
1190 wxString trackNameBase = fn.GetName();
1191 int i = -1;
1192
1193 // Fix the bug 2109.
1194 // In case the project had soloed tracks before importing,
1195 // all newly imported tracks are muted.
1196 const bool projectHasSolo =
1197 !(tracks.Any<PlayableTrack>() + &PlayableTrack::GetSolo).empty();
1198 if (projectHasSolo) {
1199 for (auto &group : newTracks)
1200 if (auto pTrack = dynamic_cast<PlayableTrack*>(group.get()))
1201 pTrack->SetMute(true);
1202 }
1203
1204 for (auto &group : newTracks) {
1205 if (auto pTrack = dynamic_cast<WaveTrack*>(group.get()))
1206 results.push_back(pTrack);
1207 tracks.Add(group);
1208 }
1209 newTracks.clear();
1210
1211 // Now name them
1212
1213 // Add numbers to track names only if there is more than one (mono or stereo)
1214 // track (not necessarily, more than one channel)
1215 const bool useSuffix = results.size() > 1;
1216
1217 for (const auto &newTrack : results) {
1218 ++i;
1219 newTrack->SetSelected(true);
1220 if (useSuffix)
1221 //i18n-hint Name default name assigned to a clip on track import
1222 newTrack->SetName(XC("%s %d", "clip name template")
1223 .Format(trackNameBase, i + 1).Translation());
1224 else
1225 newTrack->SetName(trackNameBase);
1226
1227 newTrack->TypeSwitch([&](WaveTrack &wt) {
1228 if (newRate == 0)
1229 newRate = wt.GetRate();
1230 const auto trackName = wt.GetName();
1231 for (const auto &interval : wt.Intervals())
1232 interval->SetName(trackName);
1233 });
1234 }
1235
1236 history.PushState(XO("Imported '%s'").Format( fileName ),
1237 XO("Import"));
1238
1239#if defined(__WXGTK__)
1240 // See bug #1224
1241 // The track panel hasn't been fully created, so ZoomFitHorizontally() will not give
1242 // expected results due to a window width of zero. Should be safe to yield here to
1243 // allow the creation to complete. If this becomes a problem, it "might" be possible
1244 // to queue a dummy event to trigger ZoomFitHorizontally().
1245 wxEventLoopBase::GetActive()->YieldFor(wxEVT_CATEGORY_UI | wxEVT_CATEGORY_USER_INPUT);
1246#endif
1247
1248 // If the project was clean and temporary (not permanently saved), then set
1249 // the filename to the just imported path.
1250 if (initiallyEmpty && projectFileIO.IsTemporary()) {
1251 project.SetProjectName(fn.GetName());
1252 project.SetInitialImportPath(fn.GetPath());
1253 projectFileIO.SetProjectTitle();
1254 }
1255
1256 // Moved this call to higher levels to prevent flicker redrawing everything on each file.
1257 // HandleResize();
1258}
XO("Cut/Copy/Paste")
#define XC(s, c)
Definition: Internat.h:37
const auto tracks
static const auto fn
AudioTrack subclass that can also be audibly replayed by the program.
Definition: PlayableTrack.h:40
bool GetSolo() const
Definition: PlayableTrack.h:48
static ProjectFileIO & Get(AudacityProject &project)
static ProjectHistory & Get(AudacityProject &project)
const wxString & GetName() const
Name is always the same for all channels of a group.
Definition: Track.cpp:64
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:314
A Track that contains audio waveform data.
Definition: WaveTrack.h:203
double GetRate() const override
Definition: WaveTrack.cpp:798
auto Intervals()
Definition: WaveTrack.h:670
void SelectNone(AudacityProject &project)

References fn, ProjectFileIO::Get(), ProjectHistory::Get(), TrackList::Get(), Track::GetName(), WaveTrack::GetRate(), PlayableTrack::GetSolo(), WaveTrack::Intervals(), mProject, project, SelectUtilities::SelectNone(), tracks, XC, and XO().

Referenced by anonymous_namespace{FileMenus.cpp}::DoImport().

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

◆ CloseProject()

void ProjectFileManager::CloseProject ( )

Definition at line 840 of file ProjectFileManager.cpp.

841{
842 auto &project = mProject;
843 auto &projectFileIO = ProjectFileIO::Get(project);
844
845 projectFileIO.CloseProject();
846
847 // Blocks were locked in CompactProjectOnClose, so DELETE the data structure so that
848 // there's no memory leak.
850 {
851 mLastSavedTracks->Clear();
852 mLastSavedTracks.reset();
853 }
854}
std::shared_ptr< TrackList > mLastSavedTracks

References ProjectFileIO::Get(), mLastSavedTracks, mProject, and project.

Referenced by ApplyMacroDialog::OnApplyToFiles().

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

◆ Compact()

void ProjectFileManager::Compact ( )

Definition at line 1640 of file ProjectFileManager.cpp.

1641{
1642 auto &project = mProject;
1643 auto &undoManager = UndoManager::Get(project);
1644 auto &clipboard = Clipboard::Get();
1645 auto &projectFileIO = ProjectFileIO::Get(project);
1646 bool isBatch = project.mBatchMode > 0;
1647
1648 // Purpose of this is to remove the -wal file.
1649 projectFileIO.ReopenProject();
1650
1651 auto savedState = undoManager.GetSavedState();
1652 const auto currentState = undoManager.GetCurrentState();
1653 if (savedState < 0) {
1654 undoManager.StateSaved();
1655 savedState = undoManager.GetSavedState();
1656 if (savedState < 0) {
1657 wxASSERT(false);
1658 savedState = 0;
1659 }
1660 }
1661 const auto least = std::min<size_t>(savedState, currentState);
1662 const auto greatest = std::max<size_t>(savedState, currentState);
1663 std::vector<const TrackList*> trackLists;
1664 auto fn = [&](const UndoStackElem& elem) {
1665 if (auto pTracks = UndoTracks::Find(elem))
1666 trackLists.push_back(pTracks);
1667 };
1668 undoManager.VisitStates(fn, least, 1 + least);
1669 if (least != greatest)
1670 undoManager.VisitStates(fn, greatest, 1 + greatest);
1671
1672 int64_t total = projectFileIO.GetTotalUsage();
1673 int64_t used = projectFileIO.GetCurrentUsage(trackLists);
1674
1675 auto before = wxFileName::GetSize(projectFileIO.GetFileName());
1676
1677 CompactDialog dlg(
1678 XO("Compacting this project will free up disk space by removing unused bytes within the file.\n\n"
1679 "There is %s of free disk space and this project is currently using %s.\n"
1680 "\n"
1681 "If you proceed, the current Undo/Redo History and clipboard contents will be discarded "
1682 "and you will recover approximately %s of disk space.\n"
1683 "\n"
1684 "Do you want to continue?")
1685 .Format(Internat::FormatSize(projectFileIO.GetFreeDiskSpace()),
1686 Internat::FormatSize(before.GetValue()),
1687 Internat::FormatSize(total - used)));
1688 if (isBatch || dlg.ShowModal() == wxYES)
1689 {
1690 // We can remove redo states, if they are after the saved state.
1691 undoManager.RemoveStates(1 + greatest, undoManager.GetNumStates());
1692
1693 // We can remove all states between the current and the last saved.
1694 if (least < greatest)
1695 undoManager.RemoveStates(least + 1, greatest);
1696
1697 // We can remove all states before the current and the last saved.
1698 undoManager.RemoveStates(0, least);
1699
1700 // And clear the clipboard, if needed
1701 if (&mProject == clipboard.Project().lock().get())
1702 clipboard.Clear();
1703
1704 // Refresh the before space usage since it may have changed due to the
1705 // above actions.
1706 auto before = wxFileName::GetSize(projectFileIO.GetFileName());
1707
1708 projectFileIO.Compact(trackLists, true);
1709
1710 auto after = wxFileName::GetSize(projectFileIO.GetFileName());
1711
1712 if (!isBatch)
1713 {
1715 XO("Compacting actually freed %s of disk space.")
1716 .Format(Internat::FormatSize((before - after).GetValue())),
1717 XO("Compact Project"));
1718 }
1719
1720 undoManager.RenameState( undoManager.GetCurrentState(),
1721 XO("Compacted project file"),
1722 XO("Compact") );
1723 }
1724}
int AudacityMessageBox(const TranslatableString &message, const TranslatableString &caption, long style, wxWindow *parent, int x, int y)
static Clipboard & Get()
Definition: Clipboard.cpp:28
Abstract base class used in importing a file.
static TranslatableString FormatSize(wxLongLong size)
Convert a number to a string while formatting it in bytes, KB, MB, GB.
Definition: Internat.cpp:179
static UndoManager & Get(AudacityProject &project)
Definition: UndoManager.cpp:71
TRACK_API TrackList * Find(const UndoStackElem &state)
Definition: UndoTracks.cpp:47
Holds one item with description and time range for the UndoManager.
Definition: UndoManager.h:117

References AudacityMessageBox(), UndoTracks::Find(), fn, Internat::FormatSize(), Clipboard::Get(), ProjectFileIO::Get(), UndoManager::Get(), mProject, project, and XO().

Referenced by anonymous_namespace{FileMenus.cpp}::OnCompact().

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

◆ CompactProjectOnClose()

void ProjectFileManager::CompactProjectOnClose ( )

Definition at line 787 of file ProjectFileManager.cpp.

788{
789 auto &project = mProject;
790 auto &projectFileIO = ProjectFileIO::Get(project);
791
792 // Lock all blocks in all tracks of the last saved version, so that
793 // the sample blocks aren't deleted from the database when we destroy the
794 // sample block objects in memory.
796 {
797 for (auto wt : mLastSavedTracks->Any<WaveTrack>())
799
800 // Attempt to compact the project
801 projectFileIO.Compact( { mLastSavedTracks.get() } );
802
803 if ( !projectFileIO.WasCompacted() &&
805 // If compaction failed, we must do some work in case of close
806 // without save. Don't leave the document blob from the last
807 // push of undo history, when that undo state may get purged
808 // with deletion of some new sample blocks.
809 // REVIEW: UpdateSaved() might fail too. Do we need to test
810 // for that and report it?
811 projectFileIO.UpdateSaved( mLastSavedTracks.get() );
812 }
813 }
814}
bool UnsavedChanges() const
WAVE_TRACK_API void CloseLock(WaveTrack &track) noexcept
Should be called upon project close. Not balanced by unlocking calls.

References WaveTrackUtilities::CloseLock(), ProjectFileIO::Get(), UndoManager::Get(), mLastSavedTracks, mProject, project, and UndoManager::UnsavedChanges().

Here is the call graph for this function:

◆ DiscardAutosave()

void ProjectFileManager::DiscardAutosave ( const FilePath filename)
static

Definition at line 92 of file ProjectFileManager.cpp.

93{
94 InvisibleTemporaryProject tempProject;
95 auto &project = tempProject.Project();
96 auto &projectFileManager = Get(project);
97 // Read the project, discarding autosave
98 projectFileManager.ReadProjectFile(filename, true);
99
100 if (projectFileManager.mLastSavedTracks) {
101 for (auto wt : projectFileManager.mLastSavedTracks->Any<WaveTrack>())
103 projectFileManager.mLastSavedTracks.reset();
104 }
105
106 // Side-effect on database is done, and destructor of tempProject
107 // closes the temporary project properly
108}
Makes a temporary project that doesn't display on the screen.
AudacityProject & Project()
static ProjectFileManager & Get(AudacityProject &project)

References WaveTrackUtilities::CloseLock(), Get(), InvisibleTemporaryProject::Project(), and project.

Referenced by DiscardAllProjects().

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

◆ DoImport()

bool ProjectFileManager::DoImport ( const FilePath fileName,
bool  addToHistory,
std::shared_ptr< ClipMirAudioReader > &  resultingReader 
)
private

Definition at line 1451 of file ProjectFileManager.cpp.

1454{
1455 auto &project = mProject;
1456 auto &projectFileIO = ProjectFileIO::Get(project);
1457 auto oldTags = Tags::Get( project ).shared_from_this();
1458 bool initiallyEmpty = TrackList::Get(project).empty();
1459 TrackHolders newTracks;
1460 TranslatableString errorMessage;
1461
1462#ifdef EXPERIMENTAL_IMPORT_AUP3
1463 // Handle AUP3 ("project") files directly
1464 if (fileName.AfterLast('.').IsSameAs(wxT("aup3"), false)) {
1465 if (ImportProject(project, fileName)) {
1466 auto &history = ProjectHistory::Get(project);
1467
1468 // If the project was clean and temporary (not permanently saved), then set
1469 // the filename to the just imported path.
1470 if (initiallyEmpty && projectFileIO.IsTemporary()) {
1471 wxFileName fn(fileName);
1472 project.SetProjectName(fn.GetName());
1473 project.SetInitialImportPath(fn.GetPath());
1474 projectFileIO.SetProjectTitle();
1475 }
1476
1477 history.PushState(XO("Imported '%s'").Format(fileName), XO("Import"));
1478
1479 if (addToHistory) {
1480 FileHistory::Global().Append(fileName);
1481 }
1482 }
1483 else {
1484 errorMessage = projectFileIO.GetLastError();
1485 if (errorMessage.empty()) {
1486 errorMessage = XO("Failed to import project");
1487 }
1488
1489 // Additional help via a Help button links to the manual.
1491 XO("Error Importing"),
1492 errorMessage, wxT("Importing_Audio"));
1493 }
1494
1495 return false;
1496 }
1497#endif
1498
1499 {
1500 // Backup Tags, before the import. Be prepared to roll back changes.
1501 bool committed = false;
1502 auto cleanup = finally([&]{
1503 if ( !committed )
1504 Tags::Set( project, oldTags );
1505 });
1506 auto newTags = oldTags->Duplicate();
1507 Tags::Set( project, newTags );
1508
1509#ifndef EXPERIMENTAL_IMPORT_AUP3
1510 // Handle AUP3 ("project") files specially
1511 if (fileName.AfterLast('.').IsSameAs(wxT("aup3"), false)) {
1513 XO("Error Importing"),
1514 XO( "Cannot import AUP3 format. Use File > Open instead"),
1515 wxT("File_Menu"));
1516 return false;
1517 }
1518#endif
1519
1520 ImportProgress importProgress(project);
1521 std::optional<LibFileFormats::AcidizerTags> acidTags;
1522 bool success = Importer::Get().Import(
1523 project, fileName, &importProgress, &WaveTrackFactory::Get(project),
1524 newTracks, newTags.get(), acidTags, errorMessage);
1525 if (!errorMessage.empty()) {
1526 // Error message derived from Importer::Import
1527 // Additional help via a Help button links to the manual.
1529 XO("Error Importing"), errorMessage, wxT("Importing_Audio"));
1530 }
1531 if (!success)
1532 return false;
1533
1534 const auto projectTempo = ProjectTimeSignature::Get(project).GetTempo();
1535 for (auto track : newTracks)
1536 DoProjectTempoChange(*track, projectTempo);
1537
1538 if (newTracks.size() == 1)
1539 {
1540 const auto waveTrack = dynamic_cast<WaveTrack*>(newTracks[0].get());
1541 // Also check that the track has a clip, as protection against empty
1542 // file import.
1543 if (waveTrack && !waveTrack->GetClipInterfaces().empty())
1544 resultingReader.reset(new ClipMirAudioReader {
1545 std::move(acidTags), fileName.ToStdString(),
1546 *waveTrack });
1547 }
1548
1549 if (addToHistory) {
1550 FileHistory::Global().Append(fileName);
1551 }
1552
1553 // no more errors, commit
1554 committed = true;
1555 }
1556
1557 // for LOF ("list of files") files, do not import the file as if it
1558 // were an audio file itself
1559 if (fileName.AfterLast('.').IsSameAs(wxT("lof"), false)) {
1560 // PRL: don't redundantly do the steps below, because we already
1561 // did it in case of LOF, because of some weird recursion back to this
1562 // same function. I think this should be untangled.
1563
1564 // So Undo history push is not bypassed, despite appearances.
1565 return false;
1566 }
1567
1568 // Handle AUP ("legacy project") files directly
1569 if (fileName.AfterLast('.').IsSameAs(wxT("aup"), false)) {
1570 // If the project was clean and temporary (not permanently saved), then set
1571 // the filename to the just imported path.
1572 if (initiallyEmpty && projectFileIO.IsTemporary()) {
1573 wxFileName fn(fileName);
1574 project.SetProjectName(fn.GetName());
1575 project.SetInitialImportPath(fn.GetPath());
1576 projectFileIO.SetProjectTitle();
1577 }
1578
1579 auto &history = ProjectHistory::Get( project );
1580
1581 history.PushState(XO("Imported '%s'").Format( fileName ), XO("Import"));
1582
1583 return true;
1584 }
1585
1586 // PRL: Undo history is incremented inside this:
1587 AddImportedTracks(fileName, std::move(newTracks));
1588
1589 return true;
1590}
wxT("CloseDown"))
std::vector< std::shared_ptr< Track > > TrackHolders
Definition: ImportRaw.h:24
std::unique_ptr< const BasicUI::WindowPlacement > ProjectFramePlacement(AudacityProject *project)
Make a WindowPlacement object suitable for project (which may be null)
Definition: Project.cpp:129
void DoProjectTempoChange(ChannelGroup &group, double newTempo)
Definition: TempoChange.cpp:41
void Append(const FilePath &file)
Definition: FileHistory.h:46
static FileHistory & Global()
Definition: FileHistory.cpp:39
static Importer & Get()
Definition: Import.cpp:103
bool Import(AudacityProject &project, const FilePath &fName, ImportProgressListener *importProgressListener, WaveTrackFactory *trackFactory, TrackHolders &tracks, Tags *tags, std::optional< LibFileFormats::AcidizerTags > &outAcidTags, TranslatableString &errorMessage)
Definition: Import.cpp:487
void AddImportedTracks(const FilePath &fileName, TrackHolders &&newTracks)
static ProjectTimeSignature & Get(AudacityProject &project)
static Tags & Get(AudacityProject &project)
Definition: Tags.cpp:214
static Tags & Set(AudacityProject &project, const std::shared_ptr< Tags > &tags)
Definition: Tags.cpp:224
bool empty() const
Definition: Track.cpp:758
Holds a msgid for the translation catalog; may also bind format arguments.
static WaveTrackFactory & Get(AudacityProject &project)
Definition: WaveTrack.cpp:3349
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:272
bool ImportProject(AudacityProject &dest, const FilePath &fileName)

References FileHistory::Append(), DoProjectTempoChange(), TranslatableString::empty(), TrackList::empty(), fn, Importer::Get(), ProjectTimeSignature::Get(), ProjectFileIO::Get(), ProjectHistory::Get(), Tags::Get(), TrackList::Get(), WaveTrackFactory::Get(), ProjectTimeSignature::GetTempo(), FileHistory::Global(), Importer::Import(), anonymous_namespace{ProjectFileManager.cpp}::ImportProject(), mProject, project, ProjectFramePlacement(), Tags::Set(), BasicUI::ShowErrorDialog(), wxT(), and XO().

Here is the call graph for this function:

◆ DoSave()

bool ProjectFileManager::DoSave ( const FilePath fileName,
bool  fromSaveAs 
)
private

Definition at line 342 of file ProjectFileManager.cpp.

343{
344 // See explanation above
345 // ProjectDisabler disabler(this);
346 auto &proj = mProject;
347 auto &window = GetProjectFrame( proj );
348 auto &projectFileIO = ProjectFileIO::Get( proj );
349 const auto &settings = ProjectSettings::Get( proj );
350
351 // Some confirmation dialogs
352 {
353 if (TempDirectory::FATFilesystemDenied(fileName, XO("Projects cannot be saved to FAT drives.")))
354 {
355 return false;
356 }
357
358 wxULongLong fileSize = wxFileName::GetSize(projectFileIO.GetFileName());
359
360 wxDiskspaceSize_t freeSpace;
361 if (wxGetDiskSpace(FileNames::AbbreviatePath(fileName), NULL, &freeSpace))
362 {
363 if (freeSpace.GetValue() <= fileSize.GetValue())
364 {
366 XO("Insufficient Disk Space"),
367 XO("The project size exceeds the available free space on the target disk.\n\n"
368 "Please select a different disk with more free space."),
369 "Error:_Disk_full_or_not_writable"
370 );
371
372 return false;
373 }
374 }
375 }
376 // End of confirmations
377
378 // Always save a backup of the original project file
379 std::optional<ProjectFileIO::BackupProject> pBackupProject;
380 if (fromSaveAs && wxFileExists(fileName))
381 {
382 pBackupProject.emplace(projectFileIO, fileName);
383 if (!pBackupProject->IsOk())
384 return false;
385 }
386
387 if (FileNames::IsOnFATFileSystem(fileName))
388 {
389 if (wxFileName::GetSize(projectFileIO.GetFileName()) > UINT32_MAX)
390 {
392 XO("Error Saving Project"),
393 XO("The project exceeds the maximum size of 4GB when writing to a FAT32 formatted filesystem."),
394 "Error:_Unsuitable_drive"
395 );
396 return false;
397 }
398 }
399
400 bool success = projectFileIO.SaveProject(fileName, mLastSavedTracks.get());
401 if (!success)
402 {
403 // Show this error only if we didn't fail reconnection in SaveProject
404 // REVIEW: Could HasConnection() be true but SaveProject() still have failed?
405 if (!projectFileIO.HasConnection()) {
406 using namespace BasicUI;
408 XO("Error Saving Project"),
410 "Error:_Disk_full_or_not_writable",
411 ErrorDialogOptions{ ErrorDialogType::ModalErrorReport } );
412 }
413 return false;
414 }
415
416 proj.SetProjectName(wxFileName(fileName).GetName());
417 projectFileIO.SetProjectTitle();
418
420 ProjectStatus::Get(proj).Set(XO("Saved %s").Format(fileName));
421
423 {
424 mLastSavedTracks->Clear();
425 }
427
428 auto &tracks = TrackList::Get(proj);
429 for (auto t : tracks)
430 mLastSavedTracks->Add(t->Duplicate(Track::DuplicateOptions{}.Backup()));
431
432 // If we get here, saving the project was successful, so we can DELETE
433 // any backup project.
434 if (pBackupProject)
435 pBackupProject->Discard();
436
437 return true;
438}
AUDACITY_DLL_API wxFrame & GetProjectFrame(AudacityProject &project)
Get the top-level window associated with the project (as a wxFrame only, when you do not need to use ...
static Settings & settings()
Definition: TrackInfo.cpp:51
static TranslatableString WriteFailureMessage(const wxFileName &fileName)
static ProjectSettings & Get(AudacityProject &project)
static ProjectStatus & Get(AudacityProject &project)
void Set(const TranslatableString &msg, StatusBarField field=MainStatusBarField())
static TrackListHolder Create(AudacityProject *pOwner)
Definition: Track.cpp:330
void StateSaved()
FILES_API bool IsOnFATFileSystem(const FilePath &path)
FILES_API wxString AbbreviatePath(const wxFileName &fileName)
Give enough of the path to identify the device. (On Windows, drive letter plus ':')
FILES_API bool FATFilesystemDenied(const FilePath &path, const TranslatableString &msg, const BasicUI::WindowPlacement &placement={})
Options for variations of error dialogs; the default is for modal dialogs.
Definition: BasicUI.h:52
Choices when duplicating a track.
Definition: Track.h:271
DuplicateOptions Backup() &&
Definition: Track.h:288

References FileNames::AbbreviatePath(), Track::DuplicateOptions::Backup(), TrackList::Create(), TempDirectory::FATFilesystemDenied(), ProjectFileIO::Get(), UndoManager::Get(), ProjectStatus::Get(), TrackList::Get(), ProjectSettings::Get(), GetProjectFrame(), FileNames::IsOnFATFileSystem(), mLastSavedTracks, mProject, ProjectFramePlacement(), ProjectStatus::Set(), settings(), BasicUI::ShowErrorDialog(), UndoManager::StateSaved(), tracks, FileException::WriteFailureMessage(), and XO().

Referenced by Save(), SaveAs(), and SaveFromTimerRecording().

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

◆ FixTracks()

void ProjectFileManager::FixTracks ( TrackList tracks,
const std::function< void(const TranslatableString &)> &  onError,
const std::function< void(const TranslatableString &)> &  onUnlink 
)
static

Attempts to find and fix problems in tracks.

Parameters
tracksA list of tracks to be fixed
onErrorCalled each time unrepairable error has been found.
onUnlinkCalled when tracks unlinked due to link inconsistency.

Definition at line 1033 of file ProjectFileManager.cpp.

1036{
1037 // This is successively assigned the left member of each pair that
1038 // becomes unlinked
1039 Track::Holder unlinkedTrack;
1040 // Beware iterator invalidation, because stereo channels get zipped,
1041 // replacing WaveTracks
1042 for (auto iter = tracks.begin(); iter != tracks.end();) {
1043 auto t = (*iter++)->SharedPointer();
1044 const auto linkType = t->GetLinkType();
1045 // Note, the next function may have an important upgrading side effect,
1046 // and return no error; or it may find a real error and repair it, but
1047 // that repaired track won't be used because opening will fail.
1048 if (!t->LinkConsistencyFix()) {
1049 onError(XO("A channel of a stereo track was missing."));
1050 unlinkedTrack = nullptr;
1051 }
1052 if (!unlinkedTrack) {
1053 if (linkType != ChannelGroup::LinkType::None &&
1054 t->NChannels() == 1) {
1055 // The track became unlinked.
1056 // It should NOT have been replaced with a "zip"
1057 assert(t->GetOwner().get() == &tracks);
1058 // Wait until LinkConsistencyFix is called on the second track
1059 unlinkedTrack = t;
1060 // Fix the iterator, which skipped the right channel before the
1061 // unlinking
1062 iter = tracks.Find(t.get());
1063 ++iter;
1064 }
1065 }
1066 else {
1067 //Not an elegant way to deal with stereo wave track linking
1068 //compatibility between versions
1069 if (const auto left = dynamic_cast<WaveTrack*>(unlinkedTrack.get())) {
1070 if (const auto right = dynamic_cast<WaveTrack*>(t.get())) {
1071 // As with the left, it should not have vanished from the list
1072 assert(right->GetOwner().get() == &tracks);
1073 left->SetPan(-1.0f);
1074 right->SetPan(1.0f);
1077
1078 if(left->GetRate() != right->GetRate())
1079 //i18n-hint: explains why opened project was auto-modified
1080 onUnlink(XO("This project contained stereo tracks with different sample rates per channel."));
1081 if(left->GetSampleFormat() != right->GetSampleFormat())
1082 //i18n-hint: explains why opened project was auto-modified
1083 onUnlink(XO("This project contained stereo tracks with different sample formats in channels."));
1084 //i18n-hint: explains why opened project was auto-modified
1085 onUnlink(XO("This project contained stereo tracks with non-aligned content."));
1086 }
1087 }
1088 unlinkedTrack = nullptr;
1089 }
1090
1091 if (const auto message = t->GetErrorOpening()) {
1092 wxLogWarning(
1093 wxT("Track %s had error reading clip values from project file."),
1094 t->GetName());
1095 onError(*message);
1096 }
1097 }
1098}
static RealtimeEffectList & Get(AudacityProject &project)
void Clear()
Use only in the main thread. Sends Remove messages.
std::shared_ptr< Track > Holder
Definition: Track.h:202

References RealtimeEffectList::Clear(), RealtimeEffectList::Get(), ChannelGroup::None, tracks, wxT(), and XO().

Referenced by AUPImportFileHandle::Import().

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

◆ Get() [1/2]

ProjectFileManager & ProjectFileManager::Get ( AudacityProject project)
static

◆ Get() [2/2]

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

Definition at line 87 of file ProjectFileManager.cpp.

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

References Get(), and project.

Here is the call graph for this function:

◆ GetMenuClose()

bool ProjectFileManager::GetMenuClose ( ) const
inline

Definition at line 106 of file ProjectFileManager.h.

106{ return mMenuClose; }

◆ Import() [1/2]

bool ProjectFileManager::Import ( const FilePath fileName,
bool  addToHistory = true 
)

Definition at line 1344 of file ProjectFileManager.cpp.

1345{
1346 wxArrayString fileNames;
1347 fileNames.Add(fileName);
1348 return Import(std::move(fileNames), addToHistory);
1349}
bool Import(const FilePath &fileName, bool addToHistory=true)

References Import().

Referenced by ImportCommand::Apply(), anonymous_namespace{FileMenus.cpp}::DoImport(), AUPImportFileHandle::HandleImport(), Import(), ApplyMacroDialog::OnApplyToFiles(), anonymous_namespace{EditMenus.cpp}::OnPaste(), and OpenFile().

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

◆ Import() [2/2]

bool ProjectFileManager::Import ( wxArrayString  fileNames,
bool  addToHistory = true 
)

Definition at line 1351 of file ProjectFileManager.cpp.

1352{
1353 fileNames.Sort(FileNames::CompareNoCase);
1355 std::vector<wxString> { fileNames.begin(), fileNames.end() },
1356 addToHistory))
1357 return false;
1358 // Last track in the project is the one that was just added. Use it for
1359 // focus, selection, etc.
1360 Track* lastTrack = nullptr;
1361 const auto range = TrackList::Get(mProject).Any<Track>();
1362 assert(!range.empty());
1363 if(range.empty())
1364 return false;
1365 lastTrack = *(range.rbegin());
1366 BasicUI::CallAfter([wTrack = lastTrack->weak_from_this(), wProject = mProject.weak_from_this()] {
1367 const auto project = wProject.lock();
1368 const auto track = wTrack.lock();
1369 if (!project || !track)
1370 return;
1371 auto& viewPort = Viewport::Get(*project);
1372 TrackFocus::Get(*project).Set(track.get(), true);
1373 viewPort.ZoomFitHorizontally();
1374 viewPort.ShowTrack(*track);
1375 viewPort.HandleResize(); // Adjust scrollers for NEW track sizes.
1376 ViewInfo::Get(*project).selectedRegion.setTimes(
1377 track->GetStartTime(), track->GetEndTime());
1378 });
1379 return true;
1380}
bool ImportAndRunTempoDetection(const std::vector< FilePath > &fileNames, bool addToHistory)
Abstract base class for an object holding data associated with points on a time axis.
Definition: Track.h:110
auto Any() -> TrackIterRange< TrackType >
Definition: Track.h:950
void CallAfter(Action action)
Schedule an action to be done later, and in the main thread.
Definition: BasicUI.cpp:214
FILES_API int CompareNoCase(const wxString &first, const wxString &second)

References TrackList::Any(), BasicUI::CallAfter(), FileNames::CompareNoCase(), TrackList::Get(), Get(), ImportAndRunTempoDetection(), and mProject.

Here is the call graph for this function:

◆ ImportAndRunTempoDetection()

bool ProjectFileManager::ImportAndRunTempoDetection ( const std::vector< FilePath > &  fileNames,
bool  addToHistory 
)
private

Definition at line 1420 of file ProjectFileManager.cpp.

1422{
1423 const auto projectWasEmpty =
1425 std::vector<std::shared_ptr<ClipMirAudioReader>> resultingReaders;
1426 const auto success = std::all_of(
1427 fileNames.begin(), fileNames.end(), [&](const FilePath& fileName) {
1428 std::shared_ptr<ClipMirAudioReader> resultingReader;
1429 const auto success = DoImport(fileName, addToHistory, resultingReader);
1430 if (success && resultingReader)
1431 resultingReaders.push_back(std::move(resultingReader));
1432 return success;
1433 });
1434 // At the moment, one failing import doesn't revert the project state, hence
1435 // we still run the analysis on what was successfully imported.
1436 // TODO implement reverting of the project state on failure.
1437 if (!resultingReaders.empty())
1438 {
1439 const auto pProj = mProject.shared_from_this();
1440 BasicUI::CallAfter([=] {
1441 AudacityMirProject mirInterface { *pProj };
1442 const auto analyzedClips =
1443 RunTempoDetection(resultingReaders, mirInterface, projectWasEmpty);
1444 MIR::SynchronizeProject(analyzedClips, mirInterface, projectWasEmpty);
1445 });
1446 }
1447 return success;
1448}
wxString FilePath
Definition: Project.h:21
void SynchronizeProject(const std::vector< std::shared_ptr< AnalyzedAudioClip > > &clips, ProjectInterface &project, bool projectWasEmpty)
std::vector< std::shared_ptr< MIR::AnalyzedAudioClip > > RunTempoDetection(const std::vector< std::shared_ptr< ClipMirAudioReader > > &readers, const MIR::ProjectInterface &project, bool projectWasEmpty)

References TrackList::Any(), BasicUI::CallAfter(), TrackList::Get(), mProject, anonymous_namespace{ProjectFileManager.cpp}::RunTempoDetection(), and MIR::SynchronizeProject().

Referenced by Import().

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

◆ IsAlreadyOpen()

bool ProjectFileManager::IsAlreadyOpen ( const FilePath projPathName)
static

Definition at line 899 of file ProjectFileManager.cpp.

900{
901 const wxFileName newProjPathName(projPathName);
902 auto start = AllProjects{}.begin(), finish = AllProjects{}.end(),
903 iter = std::find_if( start, finish,
904 [&]( const AllProjects::value_type &ptr ){
905 return newProjPathName.SameAs(wxFileNameWrapper{ ProjectFileIO::Get(*ptr).GetFileName() });
906 } );
907 if (iter != finish) {
908 auto errMsg =
909 XO("%s is already open in another window.")
910 .Format( newProjPathName.GetName() );
911 wxLogError(errMsg.Translation()); //Debug?
913 errMsg,
914 XO("Error Opening Project"),
915 wxOK | wxCENTRE);
916 return true;
917 }
918 return false;
919}
const_iterator end() const
Definition: Project.cpp:27
Container::value_type value_type
Definition: Project.h:57
const_iterator begin() const
Definition: Project.cpp:22
const FilePath & GetFileName() const

References AudacityMessageBox(), AllProjects::begin(), AllProjects::end(), ProjectFileIO::Get(), ProjectFileIO::GetFileName(), and XO().

Referenced by EVT_MENU_RANGE(), AudacityApp::OnMRUFile(), OpenFile(), and ProjectManager::OpenFiles().

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

◆ OpenFile()

AudacityProject * ProjectFileManager::OpenFile ( const ProjectChooserFn chooser,
const FilePath fileName,
bool  addtohistory = true 
)
static

Opens files of many kinds. In case of import (sound, MIDI, or .aup), the undo history is pushed.

Parameters
choosertold whether opening a project file; decides which project to open into
fileNamethe name and contents are examined to decide a type and open appropriately
addtohistorywhether to add .aup3 files to the MRU list (but always done for imports)
Returns
if something was successfully opened, the project containing it; else null

Definition at line 921 of file ProjectFileManager.cpp.

923{
924 // On Win32, we may be given a short (DOS-compatible) file name on rare
925 // occasions (e.g. stuff like "C:\PROGRA~1\AUDACI~1\PROJEC~1.AUP"). We
926 // convert these to long file name first.
927 auto fileName = PlatformCompatibility::GetLongFileName(fileNameArg);
928
929 // Make sure it isn't already open.
930 // Vaughan, 2011-03-25: This was done previously in AudacityProject::OpenFiles()
931 // and AudacityApp::MRUOpen(), but if you open an aup file by double-clicking it
932 // from, e.g., Win Explorer, it would bypass those, get to here with no check,
933 // then open a NEW project from the same data with no warning.
934 // This was reported in http://bugzilla.audacityteam.org/show_bug.cgi?id=137#c17,
935 // but is not really part of that bug. Anyway, prevent it!
936 if (IsAlreadyOpen(fileName))
937 return nullptr;
938
939 // Data loss may occur if users mistakenly try to open ".aup3.bak" files
940 // left over from an unsuccessful save or by previous versions of Audacity.
941 // So we always refuse to open such files.
942 if (fileName.Lower().EndsWith(wxT(".aup3.bak")))
943 {
945 XO(
946"You are trying to open an automatically created backup file.\nDoing this may result in severe data loss.\n\nPlease open the actual Audacity project file instead."),
947 XO("Warning - Backup File Detected"),
948 wxOK | wxCENTRE,
949 nullptr);
950 return nullptr;
951 }
952
953 if (!::wxFileExists(fileName)) {
955 XO("Could not open file: %s").Format( fileName ),
956 XO("Error Opening File"),
957 wxOK | wxCENTRE,
958 nullptr);
959 return nullptr;
960 }
961
962 // Following block covers cases other than a project file:
963 {
964 wxFFile ff(fileName, wxT("rb"));
965
966 auto cleanup = finally([&]
967 {
968 if (ff.IsOpened())
969 {
970 ff.Close();
971 }
972 });
973
974 if (!ff.IsOpened()) {
976 XO("Could not open file: %s").Format( fileName ),
977 XO("Error opening file"),
978 wxOK | wxCENTRE,
979 nullptr);
980 return nullptr;
981 }
982
983 char buf[7];
984 auto numRead = ff.Read(buf, 6);
985 if (numRead != 6) {
987 XO("File may be invalid or corrupted: \n%s").Format( fileName ),
988 XO("Error Opening File or Project"),
989 wxOK | wxCENTRE,
990 nullptr);
991 return nullptr;
992 }
993
994 if (wxStrncmp(buf, "SQLite", 6) != 0)
995 {
996 // Not a database
997#ifdef EXPERIMENTAL_DRAG_DROP_PLUG_INS
998 // Is it a plug-in?
999 if (PluginManager::Get().DropFile(fileName)) {
1001 // Plug-in installation happened, not really opening of a file,
1002 // so return null
1003 return nullptr;
1004 }
1005#endif
1006 auto &project = chooser(false);
1007 // Undo history is incremented inside this:
1008 if (Get(project).Import(fileName))
1009 {
1010 // Undo history is incremented inside this:
1011 // Bug 2743: Don't zoom with lof.
1012 if (!fileName.AfterLast('.').IsSameAs(wxT("lof"), false))
1014 return &project;
1015 }
1016 return nullptr;
1017 }
1018 }
1019
1020 // Disallow opening of .aup3 project files from FAT drives, but only such
1021 // files, not importable types. (Bug 2800)
1023 XO("Project resides on FAT formatted drive.\n"
1024 "Copy it to another drive to open it.")))
1025 {
1026 return nullptr;
1027 }
1028
1029 auto &project = chooser(true);
1030 return Get(project).OpenProjectFile(fileName, addtohistory);
1031}
static void RebuildAllMenuBars()
static FilePath GetLongFileName(const FilePath &shortFileName)
static PluginManager & Get()
static bool IsAlreadyOpen(const FilePath &projPathName)
AudacityProject * OpenProjectFile(const FilePath &fileName, bool addtohistory)
void ZoomFitHorizontallyAndShowTrack(Track *pTrack)
Definition: Viewport.cpp:443
static Viewport & Get(AudacityProject &project)
Definition: Viewport.cpp:33

References AudacityMessageBox(), TempDirectory::FATFilesystemDenied(), PluginManager::Get(), Viewport::Get(), Get(), PlatformCompatibility::GetLongFileName(), Import(), IsAlreadyOpen(), OpenProjectFile(), project, MenuCreator::RebuildAllMenuBars(), wxT(), XO(), and Viewport::ZoomFitHorizontallyAndShowTrack().

Referenced by OpenProjectCommand::Apply(), and ProjectManager::OpenProject().

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

◆ OpenNewProject()

bool ProjectFileManager::OpenNewProject ( )

Definition at line 824 of file ProjectFileManager.cpp.

825{
826 auto &project = mProject;
827 auto &projectFileIO = ProjectFileIO::Get(project);
828
829 bool bOK = OpenProject();
830 if( !bOK )
831 {
832 auto tmpdir = wxFileName(TempDirectory::UnsavedProjectFileName()).GetPath();
833
834 UnwritableLocationErrorDialog dlg(nullptr, tmpdir);
835 dlg.ShowModal();
836 }
837 return bOK;
838}
An error dialog about unwritable location, that allows to navigate to settings quickly.
FILES_API wxString UnsavedProjectFileName()

References ProjectFileIO::Get(), mProject, OpenProject(), project, and TempDirectory::UnsavedProjectFileName().

Here is the call graph for this function:

◆ OpenProject()

bool ProjectFileManager::OpenProject ( )

Definition at line 816 of file ProjectFileManager.cpp.

817{
818 auto &project = mProject;
819 auto &projectFileIO = ProjectFileIO::Get(project);
820
821 return projectFileIO.OpenProject();
822}

References ProjectFileIO::Get(), mProject, and project.

Referenced by ApplyMacroDialog::OnApplyToFiles(), and OpenNewProject().

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

◆ OpenProjectFile()

AudacityProject * ProjectFileManager::OpenProjectFile ( const FilePath fileName,
bool  addtohistory 
)
private
Parameters
fileNamea path assumed to exist and contain an .aup3 project
addtohistorywhether to add the file to the MRU list
Returns
if something was successfully opened, the project containing it; else null

Definition at line 1100 of file ProjectFileManager.cpp.

1102{
1103 // Allow extensions to update the project before opening it.
1106 return nullptr;
1107
1108 auto &project = mProject;
1109 auto &history = ProjectHistory::Get( project );
1110 auto &tracks = TrackList::Get( project );
1111 auto &trackPanel = TrackPanel::Get( project );
1112 auto &projectFileIO = ProjectFileIO::Get( project );
1113 auto &viewport = Viewport::Get( project );
1114
1115 auto results = ReadProjectFile( fileName );
1116 const bool bParseSuccess = results.parseSuccess;
1117 const auto &errorStr = results.errorString;
1118 const bool err = results.trackError;
1119
1120 if (bParseSuccess && !err) {
1122
1124 TrackFocus::Get(project).Set(*tracks.begin());
1125 viewport.HandleResize();
1126 trackPanel.Refresh(false);
1127
1128 // ? Old rationale in this comment no longer applies in 3.0.0, with no
1129 // more on-demand loading:
1130 trackPanel.Update(); // force any repaint to happen now,
1131 // else any asynch calls into the blockfile code will not have
1132 // finished logging errors (if any) before the call to ProjectFSCK()
1133
1134 if (addtohistory)
1135 FileHistory::Global().Append(fileName);
1136 }
1137
1138 if (bParseSuccess && !err) {
1139 if (projectFileIO.IsRecovered())
1140 {
1141 // PushState calls AutoSave(), so no longer need to do so here.
1142 history.PushState(XO("Project was recovered"), XO("Recover"));
1143 }
1144 return &project;
1145 }
1146 else {
1147 // Vaughan, 2011-10-30:
1148 // See first topic at http://bugzilla.audacityteam.org/show_bug.cgi?id=451#c16.
1149 // Calling mTracks->Clear() with deleteTracks true results in data loss.
1150
1151 // PRL 2014-12-19:
1152 // I made many changes for wave track memory management, but only now
1153 // read the above comment. I may have invalidated the fix above (which
1154 // may have spared the files at the expense of leaked memory). But
1155 // here is a better way to accomplish the intent, doing like what happens
1156 // when the project closes:
1157 for (auto pTrack : tracks.Any<WaveTrack>())
1159
1160 tracks.Clear(); //tracks.Clear(true);
1161
1162 wxLogError(wxT("Could not parse file \"%s\". \nError: %s"), fileName, errorStr.Debug());
1163
1164 projectFileIO.ShowError( *ProjectFramePlacement(&project),
1165 XO("Error Opening Project"),
1166 errorStr,
1167 results.helpUrl);
1168
1169 return nullptr;
1170 }
1171}
@ Cancel
Open was cancelled by the extension.
ReadProjectResults ReadProjectFile(const FilePath &fileName, bool discardAutosave=false)
Track * Get()
Definition: TrackFocus.cpp:156
static TrackPanel & Get(AudacityProject &project)
Definition: TrackPanel.cpp:234
void ReinitScrollbars()
Definition: Viewport.h:172
std::string ToUTF8(const std::wstring &wstr)
static OnOpenAction OnOpen(AudacityProject &project, const std::string &path)

References FileHistory::Append(), Cancel, WaveTrackUtilities::CloseLock(), TrackFocus::Get(), ProjectFileIO::Get(), ProjectHistory::Get(), TrackList::Get(), Viewport::Get(), TrackPanel::Get(), FileHistory::Global(), ProjectHistory::InitialState(), mProject, ProjectFileIOExtensionRegistry::OnOpen(), project, ProjectFramePlacement(), ReadProjectFile(), Viewport::ReinitScrollbars(), audacity::ToUTF8(), tracks, wxT(), and XO().

Referenced by OpenFile().

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

◆ operator=()

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

◆ ReadProjectFile()

auto ProjectFileManager::ReadProjectFile ( const FilePath fileName,
bool  discardAutosave = false 
)
private

Parse project file

Definition at line 165 of file ProjectFileManager.cpp.

168{
169 auto &project = mProject;
170 auto &projectFileIO = ProjectFileIO::Get( project );
171 auto &window = GetProjectFrame( project );
172
176 auto parseResult = projectFileIO.LoadProject(fileName, discardAutosave);
177 const bool bParseSuccess = parseResult.has_value();
178
179 bool err = false;
180 std::optional<TranslatableString> linkTypeChangeReason;
181
182 TranslatableString otherError;
183
184 if (bParseSuccess)
185 {
187 // By making a duplicate set of pointers to the existing blocks
188 // on disk, we add one to their reference count, guaranteeing
189 // that their reference counts will never reach zero and thus
190 // the version saved on disk will be preserved until the
191 // user selects Save().
192 // Do this before FixTracks might delete zero-length clips!
194 WaveTrack *leader{};
195 tracks.Any().Visit(
196 [&](WaveTrack& track) {
197 // A rare place where TrackList::Channels remains necessary, to
198 // visit the right channels of stereo tracks not yet "zipped",
199 // otherwise later, CloseLock() will be missed for some sample
200 // blocks and corrupt the project
201 for (const auto pChannel : TrackList::Channels(&track))
202 {
203 auto left = leader;
204 auto newTrack =
205 pChannel->Duplicate(Track::DuplicateOptions {}.Backup());
206 leader = left ? nullptr // now visiting the right channel
207 :
208 (pChannel->GetLinkType() == Track::LinkType::None) ?
209 nullptr // now visiting a mono channel
210 :
211 static_cast<WaveTrack*>(newTrack.get())
212 // now visiting a left channel
213 ;
214 mLastSavedTracks->Add(newTrack);
215 if (left)
216 // Zip clips allowing misalignment -- this may be a legacy
217 // project. This duplicate track will NOT be used for normal
218 // editing, but only later to visit all the sample blocks that
219 // existed at last save time.
220 left->ZipClips(false);
221 }
222 },
223 [this](Track& track) {
224 mLastSavedTracks->Add(
226 });
227
228 FixTracks(
229 tracks,
230 // Keep at most one of the error messages
231 [&](const auto& errorMessage) { otherError = errorMessage; err = true; },
232 [&](const auto& unlinkReason) { linkTypeChangeReason = unlinkReason; });
233
234 if (!err) {
235 if(linkTypeChangeReason && !discardAutosave)
236 {
238//i18n-hint: Text of the message dialog that may appear on attempt
239//to open a project created by Audacity version prior to 3.4.
240//%s will be replaced with an explanation of the actual reason of
241//project modification.
242"%s\n"
243"This feature is not supported in Audacity versions past 3.3.3.\n"
244"These stereo tracks have been split into mono tracks.\n"
245"As a result, some realtime effects may be missing.\n"
246"Please verify that everything works as intended before saving."
247 ).Format(linkTypeChangeReason->Translation()));
248 }
249
250 parseResult->Commit();
251 if (discardAutosave)
252 // REVIEW: Failure OK?
253 projectFileIO.AutoSaveDelete();
254 else if (projectFileIO.IsRecovered()) {
255 bool resaved = false;
256
257 if (!projectFileIO.IsTemporary() &&
258 !linkTypeChangeReason)
259 {
260 // Re-save non-temporary project to its own path. This
261 // might fail to update the document blob in the database.
262 resaved = projectFileIO.SaveProject(fileName, nullptr);
263 }
264
266 resaved
267 ? XO(
268"This project was not saved properly the last time Audacity ran.\n\n"
269"It has been recovered to the last snapshot.")
270 : XO(
271"This project was not saved properly the last time Audacity ran.\n\n"
272"It has been recovered to the last snapshot, but you must save it\n"
273"to preserve its contents."),
274 XO("Project Recovered"),
275 wxICON_WARNING,
276 &window);
277 }
278 }
279
281 }
282
283 return {
284 bParseSuccess,
285 err,
286 (bParseSuccess ? otherError : projectFileIO.GetLastError()),
287 FindHelpUrl(projectFileIO.GetLibraryError())
288 };
289}
static void FixTracks(TrackList &tracks, const std::function< void(const TranslatableString &)> &onError, const std::function< void(const TranslatableString &)> &onUnlink)
Attempts to find and fix problems in tracks.
virtual Holder Duplicate(DuplicateOptions={}) const
public nonvirtual duplication function that invokes Clone()
Definition: Track.cpp:109
static auto Channels(TrackType *pTrack) -> TrackIterRange< TrackType >
Definition: Track.h:1016
MessageBoxResult ShowMessageBox(const TranslatableString &message, MessageBoxOptions options={})
Show a modal message box with either Ok or Yes and No, and optionally Cancel.
Definition: BasicUI.h:287
wxString FindHelpUrl(const TranslatableString &libraryError)
static void OnLoad(AudacityProject &project)

References AudacityMessageBox(), Track::DuplicateOptions::Backup(), TrackList::Channels(), TrackList::Create(), Track::Duplicate(), ProjectFileIO::Get(), TrackList::Get(), GetProjectFrame(), ChannelGroup::None, ProjectFileIOExtensionRegistry::OnLoad(), project, BasicUI::ShowMessageBox(), tracks, XO(), and WaveTrack::ZipClips().

Referenced by OpenProjectFile().

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

◆ Save()

bool ProjectFileManager::Save ( )

Definition at line 291 of file ProjectFileManager.cpp.

292{
293 auto &projectFileIO = ProjectFileIO::Get(mProject);
294
296 mProject, [this](auto& path, bool rename)
297 { return DoSave(audacity::ToWXString(path), rename); });
298 action != OnSaveAction::Continue)
299 return action == OnSaveAction::Handled;
300
301 // Prompt for file name?
302 if (projectFileIO.IsTemporary())
303 {
304 return SaveAs(true);
305 }
306
307 return DoSave(projectFileIO.GetFileName(), false);
308}
@ Handled
Save was handled by the extension.
@ Continue
Save was not handled by the extension.
bool SaveAs(bool allowOverwrite=false)
bool DoSave(const FilePath &fileName, bool fromSaveAs)
wxString ToWXString(const std::string &str)
static OnSaveAction OnSave(AudacityProject &project, const ProjectSaveCallback &projectSaveCallback)

References Continue, DoSave(), ProjectFileIO::Get(), Handled, mProject, ProjectFileIOExtensionRegistry::OnSave(), SaveAs(), and audacity::ToWXString().

Referenced by audacity::cloud::audiocom::sync::SaveToCloud().

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

◆ SaveAs() [1/2]

bool ProjectFileManager::SaveAs ( bool  allowOverwrite = false)

Definition at line 471 of file ProjectFileManager.cpp.

472{
473 auto &project = mProject;
474 auto &projectFileIO = ProjectFileIO::Get( project );
475 auto &window = GetProjectFrame( project );
476 TitleRestorer Restorer( window, project ); // RAII
477 wxFileName filename;
478 FilePath defaultSavePath = FileNames::FindDefaultPath(FileNames::Operation::Save);
479
480 if (projectFileIO.IsTemporary()) {
481 filename.SetPath(defaultSavePath);
482 filename.SetName(project.GetProjectName());
483 }
484 else {
485 filename = projectFileIO.GetFileName();
486 }
487
488 // Bug 1304: Set a default file path if none was given. For Save/SaveAs/SaveCopy
489 if( !FileNames::IsPathAvailable( filename.GetPath( wxPATH_GET_VOLUME| wxPATH_GET_SEPARATOR) ) ){
490 filename.SetPath(defaultSavePath);
491 }
492
493 TranslatableString title = XO("%sSave Project \"%s\" As...")
494 .Format( Restorer.sProjNumber, Restorer.sProjName );
495
496 bool bPrompt = (project.mBatchMode == 0) || (projectFileIO.GetFileName().empty());
497 FilePath fName;
498 bool bOwnsNewName;
499
500 do {
501 if (bPrompt) {
502 // JKC: I removed 'wxFD_OVERWRITE_PROMPT' because we are checking
503 // for overwrite ourselves later, and we disallow it.
504 fName = SelectFile(FileNames::Operation::Save,
505 title,
506 filename.GetPath(),
507 filename.GetFullName(),
508 wxT("aup3"),
510 wxFD_SAVE | wxRESIZE_BORDER,
511 &window);
512
513 if (fName.empty())
514 return false;
515
516 filename = fName;
517 };
518
519 filename.SetExt(wxT("aup3"));
520
521 if ((!bPrompt || !allowOverwrite) && filename.FileExists()) {
522 // Saving a copy of the project should never overwrite an existing project.
524 nullptr,
525 XO("The project was not saved because the file name provided would overwrite another project.\nPlease try again and select an original name."),
526 XO("Error Saving Project"),
527 wxOK|wxICON_ERROR );
528 m.ShowModal();
529 return false;
530 }
531
532 fName = filename.GetFullPath();
533
534 bOwnsNewName = !projectFileIO.IsTemporary() && ( projectFileIO.GetFileName() == fName );
535 // Check to see if the project file already exists, and if it does
536 // check that the project file 'belongs' to this project.
537 // otherwise, prompt the user before overwriting.
538 if (!bOwnsNewName && filename.FileExists()) {
539 // Ensure that project of same name is not open in another window.
540 // fName is the destination file.
541 // mFileName is this project.
542 // It is possible for mFileName == fName even when this project is not
543 // saved to disk, and we then need to check the destination file is not
544 // open in another window.
545 int mayOverwrite = ( projectFileIO.GetFileName() == fName ) ? 2 : 1;
546 for ( auto p : AllProjects{} ) {
547 const wxFileName openProjectName{ ProjectFileIO::Get(*p).GetFileName() };
548 if (openProjectName.SameAs(fName)) {
549 mayOverwrite -= 1;
550 if (mayOverwrite == 0)
551 break;
552 }
553 }
554
555 if (mayOverwrite > 0) {
556 /* i18n-hint: In each case, %s is the name
557 of the file being overwritten.*/
558 auto Message = XO("\
559 Do you want to overwrite the project:\n\"%s\"?\n\n\
560 If you select \"Yes\" the project\n\"%s\"\n\
561 will be irreversibly overwritten.").Format( fName, fName );
562
563 // For safety, there should NOT be an option to hide this warning.
564 int result = AudacityMessageBox(
565 Message,
566 /* i18n-hint: Heading: A warning that a project is about to be overwritten.*/
567 XO("Overwrite Project Warning"),
568 wxYES_NO | wxNO_DEFAULT | wxICON_WARNING,
569 &window);
570 if (result == wxNO) {
571 continue;
572 }
573 if (result == wxCANCEL) {
574 return false;
575 }
576 }
577 else {
578 // Overwrite disallowed. The destination project is open in another window.
580 nullptr,
581 XO("The project was not saved because the selected project is open in another window.\nPlease try again and select an original name."),
582 XO("Error Saving Project"),
583 wxOK|wxICON_ERROR );
584 m.ShowModal();
585 continue;
586 }
587 }
588
589 break;
590 } while (bPrompt);
591
592
593 // Pretend that we are closing the project
594 if (!bOwnsNewName)
595 {
596 if (
599 return false;
600 }
601
602 auto success = DoSave(fName, !bOwnsNewName);
603 if (success) {
604 FileHistory::Global().Append( projectFileIO.GetFileName() );
605 }
606
607 return(success);
608}
static const auto title
@ Veto
Extension vetoed the close.
FilePath SelectFile(FileNames::Operation op, const TranslatableString &message, const FilePath &default_path, const FilePath &default_filename, const FileExtension &default_extension, const FileTypes &fileTypes, int flags, wxWindow *parent)
Definition: SelectFile.cpp:17
Wrap wxMessageDialog so that caption IS translatable.
FILES_API const FileType AudacityProjects
Definition: FileNames.h:71
FILES_API bool IsPathAvailable(const FilePath &Path)
FILES_API FilePath FindDefaultPath(Operation op)
TranslatableString Message(unsigned trackCount)
static OnCloseAction OnClose(AudacityProject &project)

References FileHistory::Append(), AudacityMessageBox(), FileNames::AudacityProjects, DoSave(), FileNames::FindDefaultPath(), ProjectFileIO::Get(), ProjectFileIO::GetFileName(), GetProjectFrame(), FileHistory::Global(), FileNames::IsPathAvailable(), anonymous_namespace{TrackSelectHandle.cpp}::Message(), mProject, ProjectFileIOExtensionRegistry::OnClose(), project, SelectFile(), TitleRestorer::sProjName, TitleRestorer::sProjNumber, title, Veto, wxT(), and XO().

Referenced by audacity::cloud::audiocom::sync::ResaveLocally(), and Save().

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

◆ SaveAs() [2/2]

bool ProjectFileManager::SaveAs ( const FilePath newFileName,
bool  addToHistory = true 
)

Definition at line 442 of file ProjectFileManager.cpp.

443{
444 auto &project = mProject;
445 auto &projectFileIO = ProjectFileIO::Get( project );
446
447 auto oldFileName = projectFileIO.GetFileName();
448
449 bool bOwnsNewName = !projectFileIO.IsTemporary() && (oldFileName == newFileName);
450 //check to see if the NEW project file already exists.
451 //We should only overwrite it if this project already has the same name, where the user
452 //simply chose to use the save as command although the save command would have the effect.
453 if( !bOwnsNewName && wxFileExists(newFileName)) {
455 nullptr,
456 XO("The project was not saved because the file name provided would overwrite another project.\nPlease try again and select an original name."),
457 XO("Error Saving Project"),
458 wxOK|wxICON_ERROR );
459 m.ShowModal();
460 return false;
461 }
462
463 auto success = DoSave(newFileName, !bOwnsNewName);
464 if (success && addToHistory) {
465 FileHistory::Global().Append( projectFileIO.GetFileName() );
466 }
467
468 return(success);
469}

References FileHistory::Append(), DoSave(), ProjectFileIO::Get(), FileHistory::Global(), mProject, project, and XO().

Here is the call graph for this function:

◆ SaveCopy()

bool ProjectFileManager::SaveCopy ( const FilePath fileName = wxT(""))

Definition at line 610 of file ProjectFileManager.cpp.

611{
612 auto &project = mProject;
613 auto &projectFileIO = ProjectFileIO::Get(project);
614 auto &window = GetProjectFrame(project);
615 TitleRestorer Restorer(window, project); // RAII
616 wxFileName filename = fileName;
617 FilePath defaultSavePath = FileNames::FindDefaultPath(FileNames::Operation::Save);
618
619 if (fileName.empty())
620 {
621 if (projectFileIO.IsTemporary())
622 {
623 filename.SetPath(defaultSavePath);
624 }
625 else
626 {
627 filename = projectFileIO.GetFileName();
628 }
629 }
630
631 // Bug 1304: Set a default file path if none was given. For Save/SaveAs/SaveCopy
632 if (!FileNames::IsPathAvailable(filename.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR)))
633 {
634 filename.SetPath(defaultSavePath);
635 }
636
638 XO("%sSave Copy of Project \"%s\" As...")
639 .Format(Restorer.sProjNumber, Restorer.sProjName);
640
641 bool bPrompt = (project.mBatchMode == 0) || (projectFileIO.GetFileName().empty());
642 FilePath fName;
643
644 do
645 {
646 if (bPrompt)
647 {
648 // JKC: I removed 'wxFD_OVERWRITE_PROMPT' because we are checking
649 // for overwrite ourselves later, and we disallow it.
650 // Previously we disallowed overwrite because we would have had
651 // to DELETE the many smaller files too, or prompt to move them.
652 // Maybe we could allow it now that we have aup3 format?
653 fName = SelectFile(FileNames::Operation::Export,
654 title,
655 filename.GetPath(),
656 filename.GetFullName(),
657 wxT("aup3"),
659 wxFD_SAVE | wxRESIZE_BORDER,
660 &window);
661
662 if (fName.empty())
663 {
664 return false;
665 }
666
667 filename = fName;
668 };
669
670 filename.SetExt(wxT("aup3"));
671
672 if (TempDirectory::FATFilesystemDenied(filename.GetFullPath(), XO("Projects cannot be saved to FAT drives.")))
673 {
674 if (project.mBatchMode)
675 {
676 return false;
677 }
678
679 continue;
680 }
681
682 if (filename.FileExists())
683 {
684 // Saving a copy of the project should never overwrite an existing project.
685 AudacityMessageDialog m(nullptr,
686 XO("Saving a copy must not overwrite an existing saved project.\nPlease try again and select an original name."),
687 XO("Error Saving Copy of Project"),
688 wxOK | wxICON_ERROR);
689 m.ShowModal();
690
691 if (project.mBatchMode)
692 {
693 return false;
694 }
695
696 continue;
697 }
698
699 wxULongLong fileSize = wxFileName::GetSize(projectFileIO.GetFileName());
700
701 wxDiskspaceSize_t freeSpace;
702 if (wxGetDiskSpace(FileNames::AbbreviatePath(filename.GetFullPath()), NULL, &freeSpace))
703 {
704 if (freeSpace.GetValue() <= fileSize.GetValue())
705 {
707 XO("Insufficient Disk Space"),
708 XO("The project size exceeds the available free space on the target disk.\n\n"
709 "Please select a different disk with more free space."),
710 "Error:_Unsuitable_drive"
711 );
712
713 continue;
714 }
715 }
716
717 if (FileNames::IsOnFATFileSystem(filename.GetFullPath()))
718 {
719 if (fileSize > UINT32_MAX)
720 {
722 XO("Error Saving Project"),
723 XO("The project exceeds the maximum size of 4GB when writing to a FAT32 formatted filesystem."),
724 "Error:_Unsuitable_drive"
725 );
726
727 if (project.mBatchMode)
728 {
729 return false;
730 }
731
732 continue;
733 }
734 }
735
736 fName = filename.GetFullPath();
737 break;
738 } while (bPrompt);
739
740 if (!projectFileIO.SaveCopy(fName))
741 {
742 auto msg = FileException::WriteFailureMessage(fName);
744 nullptr, msg, XO("Error Saving Project"), wxOK | wxICON_ERROR);
745
746 m.ShowModal();
747
748 return false;
749 }
750
751 return true;
752}

References FileNames::AbbreviatePath(), FileNames::AudacityProjects, TempDirectory::FATFilesystemDenied(), FileNames::FindDefaultPath(), ProjectFileIO::Get(), GetProjectFrame(), FileNames::IsOnFATFileSystem(), FileNames::IsPathAvailable(), mProject, project, ProjectFramePlacement(), SelectFile(), BasicUI::ShowErrorDialog(), TitleRestorer::sProjName, TitleRestorer::sProjNumber, title, FileException::WriteFailureMessage(), wxT(), and XO().

Here is the call graph for this function:

◆ SaveFromTimerRecording()

bool ProjectFileManager::SaveFromTimerRecording ( wxFileName  fnFile)

Definition at line 754 of file ProjectFileManager.cpp.

755{
756 auto &project = mProject;
757 auto &projectFileIO = ProjectFileIO::Get( project );
758
759 // MY: Will save the project to a NEW location a-la Save As
760 // and then tidy up after itself.
761
762 wxString sNewFileName = fnFile.GetFullPath();
763
764 // MY: To allow SaveAs from Timer Recording we need to check what
765 // the value of mFileName is before we change it.
766 FilePath sOldFilename;
767 if (!projectFileIO.IsModified()) {
768 sOldFilename = projectFileIO.GetFileName();
769 }
770
771 // MY: If the project file already exists then bail out
772 // and send populate the message string (pointer) so
773 // we can tell the user what went wrong.
774 if (wxFileExists(sNewFileName)) {
775 return false;
776 }
777
778 auto success = DoSave(sNewFileName, true);
779
780 if (success) {
781 FileHistory::Global().Append( projectFileIO.GetFileName() );
782 }
783
784 return success;
785}

References FileHistory::Append(), DoSave(), ProjectFileIO::Get(), FileHistory::Global(), mProject, and project.

Here is the call graph for this function:

◆ SetMenuClose()

void ProjectFileManager::SetMenuClose ( bool  value)
inline

Definition at line 107 of file ProjectFileManager.h.

107{ mMenuClose = value; }

Referenced by anonymous_namespace{FileMenus.cpp}::OnClose().

Here is the caller graph for this function:

◆ ShowOpenDialog()

wxArrayString ProjectFileManager::ShowOpenDialog ( FileNames::Operation  op,
const FileNames::FileType extraType = {} 
)
static

Show an open dialogue for opening audio files, and possibly other sorts of files.

The file type filter will automatically contain:

  • "All files" with any extension or none,
  • "All supported files" based on the file formats supported in this build of Audacity,
  • All of the individual formats specified by the importer plug-ins which are built into this build of Audacity, each with the relevant file extensions for that format. The dialogue will start in the DefaultOpenPath directory read from the preferences, failing that the working directory. The file format filter will be set to the DefaultOpenType from the preferences, failing that the first format specified in the dialogue. These two parameters will be saved to the preferences once the user has chosen a file to open.
    Parameters
    extraTypeSpecify an additional format to allow opening in this dialogue.
    Returns
    Array of file paths which the user selected to open (multiple selections allowed).

Definition at line 857 of file ProjectFileManager.cpp.

859{
860 // Construct the filter
861 const auto fileTypes = Importer::Get().GetFileTypes( extraType );
862
863 // Retrieve saved path
864 auto path = FileNames::FindDefaultPath(op);
865
866 // Construct and display the file dialog
867 wxArrayString selected;
868
869 FileDialogWrapper dlog(nullptr,
870 XO("Select one or more files"),
871 path,
872 wxT(""),
873 fileTypes,
874 wxFD_OPEN | wxFD_MULTIPLE | wxRESIZE_BORDER);
875
876 dlog.SetFilterIndex( Importer::SelectDefaultOpenType( fileTypes ) );
877
878 int dialogResult = dlog.ShowModal();
879
880 // Convert the filter index to type and save
881 auto index = dlog.GetFilterIndex();
882 const auto &saveType = fileTypes[ index ];
883
885 Importer::SetLastOpenType( saveType );
886
887 if (dialogResult == wxID_OK) {
888 // Return the selected files
889 dlog.GetPaths(selected);
890
891 // Remember the directory
892 FileNames::UpdateDefaultPath(op, ::wxPathOnly(dlog.GetPath()));
893 }
894
895 return selected;
896}
static void SetLastOpenType(const FileNames::FileType &type)
Definition: Import.cpp:254
FileNames::FileTypes GetFileTypes(const FileNames::FileType &extraType={})
Definition: Import.cpp:209
static size_t SelectDefaultOpenType(const FileNames::FileTypes &fileTypes)
Definition: Import.cpp:274
static void SetDefaultOpenType(const FileNames::FileType &type)
Definition: Import.cpp:264
FILES_API void UpdateDefaultPath(Operation op, const FilePath &path)

References FileNames::FindDefaultPath(), Importer::Get(), Importer::GetFileTypes(), FileDialog::GetFilterIndex(), FileDialog::GetPath(), FileDialog::GetPaths(), Importer::SelectDefaultOpenType(), Importer::SetDefaultOpenType(), FileDialog::SetFilterIndex(), Importer::SetLastOpenType(), FileDialog::ShowModal(), FileNames::UpdateDefaultPath(), wxT(), and XO().

Referenced by anonymous_namespace{FileMenus.cpp}::DoImport(), and ProjectManager::OpenFiles().

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

Member Data Documentation

◆ mLastSavedTracks

std::shared_ptr<TrackList> ProjectFileManager::mLastSavedTracks
private

Definition at line 149 of file ProjectFileManager.h.

Referenced by CloseProject(), CompactProjectOnClose(), and DoSave().

◆ mMenuClose

bool ProjectFileManager::mMenuClose { false }
private

Definition at line 152 of file ProjectFileManager.h.

◆ mProject

AudacityProject& ProjectFileManager::mProject
private

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