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 std::vector< FilePath > &fileNames, bool addToHistory=true)
 
bool Import (const FilePath &fileName, 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 Import (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 109 of file ProjectFileManager.cpp.

110: mProject{ project }
111{
112}
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 1193 of file ProjectFileManager.cpp.

1195{
1196 auto &project = mProject;
1197 auto &history = ProjectHistory::Get( project );
1198 auto &projectFileIO = ProjectFileIO::Get( project );
1199 auto &tracks = TrackList::Get( project );
1200
1201 std::vector<Track*> results;
1202
1204
1205 wxFileName fn(fileName);
1206
1207 bool initiallyEmpty = tracks.empty();
1208 double newRate = 0;
1209 wxString trackNameBase = fn.GetName();
1210 int i = -1;
1211
1212 // Fix the bug 2109.
1213 // In case the project had soloed tracks before importing,
1214 // all newly imported tracks are muted.
1215 const bool projectHasSolo =
1216 !(tracks.Any<PlayableTrack>() + &PlayableTrack::GetSolo).empty();
1217 if (projectHasSolo) {
1218 for (auto &group : newTracks)
1219 if (auto pTrack = dynamic_cast<PlayableTrack*>(group.get()))
1220 pTrack->SetMute(true);
1221 }
1222
1223 for (auto &group : newTracks) {
1224 if (auto pTrack = dynamic_cast<WaveTrack*>(group.get()))
1225 results.push_back(pTrack);
1226 tracks.Add(group);
1227 }
1228 newTracks.clear();
1229
1230 // Now name them
1231
1232 // Add numbers to track names only if there is more than one (mono or stereo)
1233 // track (not necessarily, more than one channel)
1234 const bool useSuffix = results.size() > 1;
1235
1236 for (const auto &newTrack : results) {
1237 ++i;
1238 newTrack->SetSelected(true);
1239 if (useSuffix)
1240 //i18n-hint Name default name assigned to a clip on track import
1241 newTrack->SetName(XC("%s %d", "clip name template")
1242 .Format(trackNameBase, i + 1).Translation());
1243 else
1244 newTrack->SetName(trackNameBase);
1245
1246 newTrack->TypeSwitch([&](WaveTrack &wt) {
1247 if (newRate == 0)
1248 newRate = wt.GetRate();
1249 const auto trackName = wt.GetName();
1250 for (const auto &interval : wt.Intervals())
1251 interval->SetName(trackName);
1252 });
1253 }
1254
1255 history.PushState(XO("Imported '%s'").Format( fileName ),
1256 XO("Import"));
1257
1258#if defined(__WXGTK__)
1259 // See bug #1224
1260 // The track panel hasn't been fully created, so ZoomFitHorizontally() will not give
1261 // expected results due to a window width of zero. Should be safe to yield here to
1262 // allow the creation to complete. If this becomes a problem, it "might" be possible
1263 // to queue a dummy event to trigger ZoomFitHorizontally().
1264 wxEventLoopBase::GetActive()->YieldFor(wxEVT_CATEGORY_UI | wxEVT_CATEGORY_USER_INPUT);
1265#endif
1266
1267 // If the project was clean and temporary (not permanently saved), then set
1268 // the filename to the just imported path.
1269 if (initiallyEmpty && projectFileIO.IsTemporary()) {
1270 project.SetProjectName(fn.GetName());
1271 project.SetInitialImportPath(fn.GetPath());
1272 projectFileIO.SetProjectTitle();
1273 }
1274
1275 // Moved this call to higher levels to prevent flicker redrawing everything on each file.
1276 // HandleResize();
1277}
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(), and Import().

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

◆ CloseProject()

void ProjectFileManager::CloseProject ( )

Definition at line 859 of file ProjectFileManager.cpp.

860{
861 auto &project = mProject;
862 auto &projectFileIO = ProjectFileIO::Get(project);
863
864 projectFileIO.CloseProject();
865
866 // Blocks were locked in CompactProjectOnClose, so DELETE the data structure so that
867 // there's no memory leak.
869 {
870 mLastSavedTracks->Clear();
871 mLastSavedTracks.reset();
872 }
873}
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 1620 of file ProjectFileManager.cpp.

1621{
1622 auto &project = mProject;
1623 auto &undoManager = UndoManager::Get(project);
1624 auto &clipboard = Clipboard::Get();
1625 auto &projectFileIO = ProjectFileIO::Get(project);
1626 bool isBatch = project.mBatchMode > 0;
1627
1628 // Purpose of this is to remove the -wal file.
1629 projectFileIO.ReopenProject();
1630
1631 auto savedState = undoManager.GetSavedState();
1632 const auto currentState = undoManager.GetCurrentState();
1633 if (savedState < 0) {
1634 undoManager.StateSaved();
1635 savedState = undoManager.GetSavedState();
1636 if (savedState < 0) {
1637 wxASSERT(false);
1638 savedState = 0;
1639 }
1640 }
1641 const auto least = std::min<size_t>(savedState, currentState);
1642 const auto greatest = std::max<size_t>(savedState, currentState);
1643 std::vector<const TrackList*> trackLists;
1644 auto fn = [&](const UndoStackElem& elem) {
1645 if (auto pTracks = UndoTracks::Find(elem))
1646 trackLists.push_back(pTracks);
1647 };
1648 undoManager.VisitStates(fn, least, 1 + least);
1649 if (least != greatest)
1650 undoManager.VisitStates(fn, greatest, 1 + greatest);
1651
1652 int64_t total = projectFileIO.GetTotalUsage();
1653 int64_t used = projectFileIO.GetCurrentUsage(trackLists);
1654
1655 auto before = wxFileName::GetSize(projectFileIO.GetFileName());
1656
1657 CompactDialog dlg(
1658 XO("Compacting this project will free up disk space by removing unused bytes within the file.\n\n"
1659 "There is %s of free disk space and this project is currently using %s.\n"
1660 "\n"
1661 "If you proceed, the current Undo/Redo History and clipboard contents will be discarded "
1662 "and you will recover approximately %s of disk space.\n"
1663 "\n"
1664 "Do you want to continue?")
1665 .Format(Internat::FormatSize(projectFileIO.GetFreeDiskSpace()),
1666 Internat::FormatSize(before.GetValue()),
1667 Internat::FormatSize(total - used)));
1668 if (isBatch || dlg.ShowModal() == wxYES)
1669 {
1670 // We can remove redo states, if they are after the saved state.
1671 undoManager.RemoveStates(1 + greatest, undoManager.GetNumStates());
1672
1673 // We can remove all states between the current and the last saved.
1674 if (least < greatest)
1675 undoManager.RemoveStates(least + 1, greatest);
1676
1677 // We can remove all states before the current and the last saved.
1678 undoManager.RemoveStates(0, least);
1679
1680 // And clear the clipboard, if needed
1681 if (&mProject == clipboard.Project().lock().get())
1682 clipboard.Clear();
1683
1684 // Refresh the before space usage since it may have changed due to the
1685 // above actions.
1686 auto before = wxFileName::GetSize(projectFileIO.GetFileName());
1687
1688 projectFileIO.Compact(trackLists, true);
1689
1690 auto after = wxFileName::GetSize(projectFileIO.GetFileName());
1691
1692 if (!isBatch)
1693 {
1695 XO("Compacting actually freed %s of disk space.")
1696 .Format(Internat::FormatSize((before - after).GetValue())),
1697 XO("Compact Project"));
1698 }
1699
1700 undoManager.RenameState( undoManager.GetCurrentState(),
1701 XO("Compacted project file"),
1702 XO("Compact") );
1703 }
1704}
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 806 of file ProjectFileManager.cpp.

807{
808 auto &project = mProject;
809 auto &projectFileIO = ProjectFileIO::Get(project);
810
811 // Lock all blocks in all tracks of the last saved version, so that
812 // the sample blocks aren't deleted from the database when we destroy the
813 // sample block objects in memory.
815 {
816 for (auto wt : mLastSavedTracks->Any<WaveTrack>())
818
819 // Attempt to compact the project
820 projectFileIO.Compact( { mLastSavedTracks.get() } );
821
822 if ( !projectFileIO.WasCompacted() &&
824 // If compaction failed, we must do some work in case of close
825 // without save. Don't leave the document blob from the last
826 // push of undo history, when that undo state may get purged
827 // with deletion of some new sample blocks.
828 // REVIEW: UpdateSaved() might fail too. Do we need to test
829 // for that and report it?
830 projectFileIO.UpdateSaved( mLastSavedTracks.get() );
831 }
832 }
833}
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 91 of file ProjectFileManager.cpp.

92{
93 InvisibleTemporaryProject tempProject;
94 auto &project = tempProject.Project();
95 auto &projectFileManager = Get(project);
96 // Read the project, discarding autosave
97 projectFileManager.ReadProjectFile(filename, true);
98
99 if (projectFileManager.mLastSavedTracks) {
100 for (auto wt : projectFileManager.mLastSavedTracks->Any<WaveTrack>())
102 projectFileManager.mLastSavedTracks.reset();
103 }
104
105 // Side-effect on database is done, and destructor of tempProject
106 // closes the temporary project properly
107}
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:

◆ DoSave()

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

Definition at line 335 of file ProjectFileManager.cpp.

336{
337 // See explanation above
338 // ProjectDisabler disabler(this);
339 auto &proj = mProject;
340 auto &window = GetProjectFrame( proj );
341 auto &projectFileIO = ProjectFileIO::Get( proj );
342 const auto &settings = ProjectSettings::Get( proj );
343
344 // Some confirmation dialogs
345 {
346 if (TempDirectory::FATFilesystemDenied(fileName, XO("Projects cannot be saved to FAT drives.")))
347 {
348 return false;
349 }
350
351 auto &tracks = TrackList::Get( proj );
352 if (tracks.empty())
353 {
354 if (UndoManager::Get( proj ).UnsavedChanges() &&
355 settings.EmptyCanBeDirty())
356 {
357 int result = AudacityMessageBox(
358 XO(
359 "Your project is now empty.\nIf saved, the project will have no tracks.\n\nTo save any previously open tracks:\nClick 'No', Edit > Undo until all tracks\nare open, then File > Save Project.\n\nSave anyway?"),
360 XO("Warning - Empty Project"),
361 wxYES_NO | wxICON_QUESTION,
362 &window);
363 if (result == wxNO)
364 {
365 return false;
366 }
367 }
368 }
369
370 wxULongLong fileSize = wxFileName::GetSize(projectFileIO.GetFileName());
371
372 wxDiskspaceSize_t freeSpace;
373 if (wxGetDiskSpace(FileNames::AbbreviatePath(fileName), NULL, &freeSpace))
374 {
375 if (freeSpace.GetValue() <= fileSize.GetValue())
376 {
378 XO("Insufficient Disk Space"),
379 XO("The project size exceeds the available free space on the target disk.\n\n"
380 "Please select a different disk with more free space."),
381 "Error:_Disk_full_or_not_writable"
382 );
383
384 return false;
385 }
386 }
387 }
388 // End of confirmations
389
390 // Always save a backup of the original project file
391 std::optional<ProjectFileIO::BackupProject> pBackupProject;
392 if (fromSaveAs && wxFileExists(fileName))
393 {
394 pBackupProject.emplace(projectFileIO, fileName);
395 if (!pBackupProject->IsOk())
396 return false;
397 }
398
399 if (FileNames::IsOnFATFileSystem(fileName))
400 {
401 if (wxFileName::GetSize(projectFileIO.GetFileName()) > UINT32_MAX)
402 {
404 XO("Error Saving Project"),
405 XO("The project exceeds the maximum size of 4GB when writing to a FAT32 formatted filesystem."),
406 "Error:_Unsuitable_drive"
407 );
408 return false;
409 }
410 }
411
412 bool success = projectFileIO.SaveProject(fileName, mLastSavedTracks.get());
413 if (!success)
414 {
415 // Show this error only if we didn't fail reconnection in SaveProject
416 // REVIEW: Could HasConnection() be true but SaveProject() still have failed?
417 if (!projectFileIO.HasConnection()) {
418 using namespace BasicUI;
420 XO("Error Saving Project"),
422 "Error:_Disk_full_or_not_writable",
423 ErrorDialogOptions{ ErrorDialogType::ModalErrorReport } );
424 }
425 return false;
426 }
427
428 proj.SetProjectName(wxFileName(fileName).GetName());
429 projectFileIO.SetProjectTitle();
430
432 ProjectStatus::Get(proj).Set(XO("Saved %s").Format(fileName));
433
435 {
436 mLastSavedTracks->Clear();
437 }
439
440 auto &tracks = TrackList::Get(proj);
441 for (auto t : tracks)
442 mLastSavedTracks->Add(t->Duplicate(Track::DuplicateOptions{}.Backup()));
443
444 // If we get here, saving the project was successful, so we can DELETE
445 // any backup project.
446 if (pBackupProject)
447 pBackupProject->Discard();
448
449 return true;
450}
std::unique_ptr< const BasicUI::WindowPlacement > ProjectFramePlacement(AudacityProject *project)
Make a WindowPlacement object suitable for project (which may be null)
Definition: Project.cpp:129
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:47
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()
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:264
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(), AudacityMessageBox(), 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 1052 of file ProjectFileManager.cpp.

1055{
1056 // This is successively assigned the left member of each pair that
1057 // becomes unlinked
1058 Track::Holder unlinkedTrack;
1059 // Beware iterator invalidation, because stereo channels get zipped,
1060 // replacing WaveTracks
1061 for (auto iter = tracks.begin(); iter != tracks.end();) {
1062 auto t = (*iter++)->SharedPointer();
1063 const auto linkType = t->GetLinkType();
1064 // Note, the next function may have an important upgrading side effect,
1065 // and return no error; or it may find a real error and repair it, but
1066 // that repaired track won't be used because opening will fail.
1067 if (!t->LinkConsistencyFix()) {
1068 onError(XO("A channel of a stereo track was missing."));
1069 unlinkedTrack = nullptr;
1070 }
1071 if (!unlinkedTrack) {
1072 if (linkType != ChannelGroup::LinkType::None &&
1073 t->NChannels() == 1) {
1074 // The track became unlinked.
1075 // It should NOT have been replaced with a "zip"
1076 assert(t->GetOwner().get() == &tracks);
1077 // Wait until LinkConsistencyFix is called on the second track
1078 unlinkedTrack = t;
1079 // Fix the iterator, which skipped the right channel before the
1080 // unlinking
1081 iter = tracks.Find(t.get());
1082 ++iter;
1083 }
1084 }
1085 else {
1086 //Not an elegant way to deal with stereo wave track linking
1087 //compatibility between versions
1088 if (const auto left = dynamic_cast<WaveTrack*>(unlinkedTrack.get())) {
1089 if (const auto right = dynamic_cast<WaveTrack*>(t.get())) {
1090 // As with the left, it should not have vanished from the list
1091 assert(right->GetOwner().get() == &tracks);
1092 left->SetPan(-1.0f);
1093 right->SetPan(1.0f);
1096
1097 if(left->GetRate() != right->GetRate())
1098 //i18n-hint: explains why opened project was auto-modified
1099 onUnlink(XO("This project contained stereo tracks with different sample rates per channel."));
1100 if(left->GetSampleFormat() != right->GetSampleFormat())
1101 //i18n-hint: explains why opened project was auto-modified
1102 onUnlink(XO("This project contained stereo tracks with different sample formats in channels."));
1103 //i18n-hint: explains why opened project was auto-modified
1104 onUnlink(XO("This project contained stereo tracks with non-aligned content."));
1105 }
1106 }
1107 unlinkedTrack = nullptr;
1108 }
1109
1110 if (const auto message = t->GetErrorOpening()) {
1111 wxLogWarning(
1112 wxT("Track %s had error reading clip values from project file."),
1113 t->GetName());
1114 onError(*message);
1115 }
1116 }
1117}
wxT("CloseDown"))
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 86 of file ProjectFileManager.cpp.

87{
88 return Get( const_cast< AudacityProject & >( project ) );
89}
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 107 of file ProjectFileManager.h.

107{ return mMenuClose; }

◆ Import() [1/3]

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

Definition at line 1434 of file ProjectFileManager.cpp.

1437{
1438 auto &project = mProject;
1439 auto &projectFileIO = ProjectFileIO::Get(project);
1440 auto oldTags = Tags::Get( project ).shared_from_this();
1441 bool initiallyEmpty = TrackList::Get(project).empty();
1442 TrackHolders newTracks;
1443 TranslatableString errorMessage;
1444
1445#ifdef EXPERIMENTAL_IMPORT_AUP3
1446 // Handle AUP3 ("project") files directly
1447 if (fileName.AfterLast('.').IsSameAs(wxT("aup3"), false)) {
1448 if (ImportProject(project, fileName)) {
1449 auto &history = ProjectHistory::Get(project);
1450
1451 // If the project was clean and temporary (not permanently saved), then set
1452 // the filename to the just imported path.
1453 if (initiallyEmpty && projectFileIO.IsTemporary()) {
1454 wxFileName fn(fileName);
1455 project.SetProjectName(fn.GetName());
1456 project.SetInitialImportPath(fn.GetPath());
1457 projectFileIO.SetProjectTitle();
1458 }
1459
1460 history.PushState(XO("Imported '%s'").Format(fileName), XO("Import"));
1461
1462 if (addToHistory) {
1463 FileHistory::Global().Append(fileName);
1464 }
1465 }
1466 else {
1467 errorMessage = projectFileIO.GetLastError();
1468 if (errorMessage.empty()) {
1469 errorMessage = XO("Failed to import project");
1470 }
1471
1472 // Additional help via a Help button links to the manual.
1474 XO("Error Importing"),
1475 errorMessage, wxT("Importing_Audio"));
1476 }
1477
1478 return false;
1479 }
1480#endif
1481
1482 {
1483 // Backup Tags, before the import. Be prepared to roll back changes.
1484 bool committed = false;
1485 auto cleanup = finally([&]{
1486 if ( !committed )
1487 Tags::Set( project, oldTags );
1488 });
1489 auto newTags = oldTags->Duplicate();
1490 Tags::Set( project, newTags );
1491
1492#ifndef EXPERIMENTAL_IMPORT_AUP3
1493 // Handle AUP3 ("project") files specially
1494 if (fileName.AfterLast('.').IsSameAs(wxT("aup3"), false)) {
1496 XO("Error Importing"),
1497 XO( "Cannot import AUP3 format. Use File > Open instead"),
1498 wxT("File_Menu"));
1499 return false;
1500 }
1501#endif
1502
1503 ImportProgress importProgress(project);
1504 std::optional<LibFileFormats::AcidizerTags> acidTags;
1505 bool success = Importer::Get().Import(
1506 project, fileName, &importProgress, &WaveTrackFactory::Get(project),
1507 newTracks, newTags.get(), acidTags, errorMessage);
1508 if (!errorMessage.empty()) {
1509 // Error message derived from Importer::Import
1510 // Additional help via a Help button links to the manual.
1512 XO("Error Importing"), errorMessage, wxT("Importing_Audio"));
1513 }
1514 if (!success)
1515 return false;
1516
1517 const auto projectTempo = ProjectTimeSignature::Get(project).GetTempo();
1518 for (auto track : newTracks)
1519 DoProjectTempoChange(*track, projectTempo);
1520
1521 if (newTracks.size() == 1)
1522 {
1523 if (const auto waveTrack = dynamic_cast<WaveTrack*>(newTracks[0].get()))
1524 resultingReader.reset(new ClipMirAudioReader {
1525 std::move(acidTags), fileName.ToStdString(),
1526 *waveTrack });
1527 }
1528
1529 if (addToHistory) {
1530 FileHistory::Global().Append(fileName);
1531 }
1532
1533 // no more errors, commit
1534 committed = true;
1535 }
1536
1537 // for LOF ("list of files") files, do not import the file as if it
1538 // were an audio file itself
1539 if (fileName.AfterLast('.').IsSameAs(wxT("lof"), false)) {
1540 // PRL: don't redundantly do the steps below, because we already
1541 // did it in case of LOF, because of some weird recursion back to this
1542 // same function. I think this should be untangled.
1543
1544 // So Undo history push is not bypassed, despite appearances.
1545 return false;
1546 }
1547
1548 // Handle AUP ("legacy project") files directly
1549 if (fileName.AfterLast('.').IsSameAs(wxT("aup"), false)) {
1550 // If the project was clean and temporary (not permanently saved), then set
1551 // the filename to the just imported path.
1552 if (initiallyEmpty && projectFileIO.IsTemporary()) {
1553 wxFileName fn(fileName);
1554 project.SetProjectName(fn.GetName());
1555 project.SetInitialImportPath(fn.GetPath());
1556 projectFileIO.SetProjectTitle();
1557 }
1558
1559 auto &history = ProjectHistory::Get( project );
1560
1561 history.PushState(XO("Imported '%s'").Format( fileName ), XO("Import"));
1562
1563 return true;
1564 }
1565
1566 // PRL: Undo history is incremented inside this:
1567 AddImportedTracks(fileName, std::move(newTracks));
1568
1569 return true;
1570}
std::vector< std::shared_ptr< Track > > TrackHolders
Definition: ImportRaw.h:24
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:102
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:486
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
bool ImportProject(AudacityProject &dest, const FilePath &fileName)

References AddImportedTracks(), 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:

◆ Import() [2/3]

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

Definition at line 1363 of file ProjectFileManager.cpp.

1364{
1365 return Import(std::vector<FilePath> { fileName }, addToHistory);
1366}
bool Import(const std::vector< FilePath > &fileNames, bool addToHistory=true)

References Import().

Here is the call graph for this function:

◆ Import() [3/3]

bool ProjectFileManager::Import ( const std::vector< FilePath > &  fileNames,
bool  addToHistory = true 
)

Definition at line 1406 of file ProjectFileManager.cpp.

1408{
1409 const auto projectWasEmpty =
1411 std::vector<std::shared_ptr<ClipMirAudioReader>> resultingReaders;
1412 const auto success = std::all_of(
1413 fileNames.begin(), fileNames.end(), [&](const FilePath& fileName) {
1414 std::shared_ptr<ClipMirAudioReader> resultingReader;
1415 const auto success = Import(fileName, addToHistory, resultingReader);
1416 if (success && resultingReader)
1417 resultingReaders.push_back(std::move(resultingReader));
1418 return success;
1419 });
1420 if (success && !resultingReaders.empty())
1421 {
1422 const auto pProj = mProject.shared_from_this();
1423 BasicUI::CallAfter([=] {
1424 AudacityMirProject mirInterface { *pProj };
1425 const auto analyzedClips =
1426 RunTempoDetection(resultingReaders, mirInterface, projectWasEmpty);
1427 MIR::SynchronizeProject(analyzedClips, mirInterface, projectWasEmpty);
1428 });
1429 }
1430 return success;
1431}
wxString FilePath
Definition: Project.h:21
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:213
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 ImportCommand::Apply(), anonymous_namespace{FileMenus.cpp}::DoImport(), AUPImportFileHandle::HandleImport(), Import(), ApplyMacroDialog::OnApplyToFiles(), and OpenFile().

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 918 of file ProjectFileManager.cpp.

919{
920 const wxFileName newProjPathName(projPathName);
921 auto start = AllProjects{}.begin(), finish = AllProjects{}.end(),
922 iter = std::find_if( start, finish,
923 [&]( const AllProjects::value_type &ptr ){
924 return newProjPathName.SameAs(wxFileNameWrapper{ ProjectFileIO::Get(*ptr).GetFileName() });
925 } );
926 if (iter != finish) {
927 auto errMsg =
928 XO("%s is already open in another window.")
929 .Format( newProjPathName.GetName() );
930 wxLogError(errMsg.Translation()); //Debug?
932 errMsg,
933 XO("Error Opening Project"),
934 wxOK | wxCENTRE);
935 return true;
936 }
937 return false;
938}
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 940 of file ProjectFileManager.cpp.

942{
943 // On Win32, we may be given a short (DOS-compatible) file name on rare
944 // occasions (e.g. stuff like "C:\PROGRA~1\AUDACI~1\PROJEC~1.AUP"). We
945 // convert these to long file name first.
946 auto fileName = PlatformCompatibility::GetLongFileName(fileNameArg);
947
948 // Make sure it isn't already open.
949 // Vaughan, 2011-03-25: This was done previously in AudacityProject::OpenFiles()
950 // and AudacityApp::MRUOpen(), but if you open an aup file by double-clicking it
951 // from, e.g., Win Explorer, it would bypass those, get to here with no check,
952 // then open a NEW project from the same data with no warning.
953 // This was reported in http://bugzilla.audacityteam.org/show_bug.cgi?id=137#c17,
954 // but is not really part of that bug. Anyway, prevent it!
955 if (IsAlreadyOpen(fileName))
956 return nullptr;
957
958 // Data loss may occur if users mistakenly try to open ".aup3.bak" files
959 // left over from an unsuccessful save or by previous versions of Audacity.
960 // So we always refuse to open such files.
961 if (fileName.Lower().EndsWith(wxT(".aup3.bak")))
962 {
964 XO(
965"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."),
966 XO("Warning - Backup File Detected"),
967 wxOK | wxCENTRE,
968 nullptr);
969 return nullptr;
970 }
971
972 if (!::wxFileExists(fileName)) {
974 XO("Could not open file: %s").Format( fileName ),
975 XO("Error Opening File"),
976 wxOK | wxCENTRE,
977 nullptr);
978 return nullptr;
979 }
980
981 // Following block covers cases other than a project file:
982 {
983 wxFFile ff(fileName, wxT("rb"));
984
985 auto cleanup = finally([&]
986 {
987 if (ff.IsOpened())
988 {
989 ff.Close();
990 }
991 });
992
993 if (!ff.IsOpened()) {
995 XO("Could not open file: %s").Format( fileName ),
996 XO("Error opening file"),
997 wxOK | wxCENTRE,
998 nullptr);
999 return nullptr;
1000 }
1001
1002 char buf[7];
1003 auto numRead = ff.Read(buf, 6);
1004 if (numRead != 6) {
1006 XO("File may be invalid or corrupted: \n%s").Format( fileName ),
1007 XO("Error Opening File or Project"),
1008 wxOK | wxCENTRE,
1009 nullptr);
1010 return nullptr;
1011 }
1012
1013 if (wxStrncmp(buf, "SQLite", 6) != 0)
1014 {
1015 // Not a database
1016#ifdef EXPERIMENTAL_DRAG_DROP_PLUG_INS
1017 // Is it a plug-in?
1018 if (PluginManager::Get().DropFile(fileName)) {
1020 // Plug-in installation happened, not really opening of a file,
1021 // so return null
1022 return nullptr;
1023 }
1024#endif
1025 auto &project = chooser(false);
1026 // Undo history is incremented inside this:
1027 if (Get(project).Import(fileName))
1028 {
1029 // Undo history is incremented inside this:
1030 // Bug 2743: Don't zoom with lof.
1031 if (!fileName.AfterLast('.').IsSameAs(wxT("lof"), false))
1033 return &project;
1034 }
1035 return nullptr;
1036 }
1037 }
1038
1039 // Disallow opening of .aup3 project files from FAT drives, but only such
1040 // files, not importable types. (Bug 2800)
1042 XO("Project resides on FAT formatted drive.\n"
1043 "Copy it to another drive to open it.")))
1044 {
1045 return nullptr;
1046 }
1047
1048 auto &project = chooser(true);
1049 return Get(project).OpenProjectFile(fileName, addtohistory);
1050}
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:439
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 843 of file ProjectFileManager.cpp.

844{
845 auto &project = mProject;
846 auto &projectFileIO = ProjectFileIO::Get(project);
847
848 bool bOK = OpenProject();
849 if( !bOK )
850 {
851 auto tmpdir = wxFileName(TempDirectory::UnsavedProjectFileName()).GetPath();
852
853 UnwritableLocationErrorDialog dlg(nullptr, tmpdir);
854 dlg.ShowModal();
855 }
856 return bOK;
857}
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 835 of file ProjectFileManager.cpp.

836{
837 auto &project = mProject;
838 auto &projectFileIO = ProjectFileIO::Get(project);
839
840 return projectFileIO.OpenProject();
841}

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 1119 of file ProjectFileManager.cpp.

1121{
1122 // Allow extensions to update the project before opening it.
1125 return nullptr;
1126
1127 auto &project = mProject;
1128 auto &history = ProjectHistory::Get( project );
1129 auto &tracks = TrackList::Get( project );
1130 auto &trackPanel = TrackPanel::Get( project );
1131 auto &projectFileIO = ProjectFileIO::Get( project );
1132 auto &viewport = Viewport::Get( project );
1133
1134 auto results = ReadProjectFile( fileName );
1135 const bool bParseSuccess = results.parseSuccess;
1136 const auto &errorStr = results.errorString;
1137 const bool err = results.trackError;
1138
1139 if (bParseSuccess && !err) {
1141
1143 TrackFocus::Get(project).Set(*tracks.begin());
1144 viewport.HandleResize();
1145 trackPanel.Refresh(false);
1146
1147 // ? Old rationale in this comment no longer applies in 3.0.0, with no
1148 // more on-demand loading:
1149 trackPanel.Update(); // force any repaint to happen now,
1150 // else any asynch calls into the blockfile code will not have
1151 // finished logging errors (if any) before the call to ProjectFSCK()
1152
1153 if (addtohistory)
1154 FileHistory::Global().Append(fileName);
1155 }
1156
1157 if (bParseSuccess && !err) {
1158 if (projectFileIO.IsRecovered())
1159 {
1160 // PushState calls AutoSave(), so no longer need to do so here.
1161 history.PushState(XO("Project was recovered"), XO("Recover"));
1162 }
1163 return &project;
1164 }
1165 else {
1166 // Vaughan, 2011-10-30:
1167 // See first topic at http://bugzilla.audacityteam.org/show_bug.cgi?id=451#c16.
1168 // Calling mTracks->Clear() with deleteTracks true results in data loss.
1169
1170 // PRL 2014-12-19:
1171 // I made many changes for wave track memory management, but only now
1172 // read the above comment. I may have invalidated the fix above (which
1173 // may have spared the files at the expense of leaked memory). But
1174 // here is a better way to accomplish the intent, doing like what happens
1175 // when the project closes:
1176 for (auto pTrack : tracks.Any<WaveTrack>())
1178
1179 tracks.Clear(); //tracks.Clear(true);
1180
1181 wxLogError(wxT("Could not parse file \"%s\". \nError: %s"), fileName, errorStr.Debug());
1182
1183 projectFileIO.ShowError( *ProjectFramePlacement(&project),
1184 XO("Error Opening Project"),
1185 errorStr,
1186 results.helpUrl);
1187
1188 return nullptr;
1189 }
1190}
@ 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 164 of file ProjectFileManager.cpp.

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

References AudacityMessageBox(), Track::DuplicateOptions::Backup(), TrackList::Channels(), TrackList::Create(), Track::Duplicate(), anonymous_namespace{ProjectFileManager.cpp}::FindHelpUrl(), 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 283 of file ProjectFileManager.cpp.

284{
285 auto &projectFileIO = ProjectFileIO::Get(mProject);
286
287
289 mProject, [this](auto& path, bool rename)
290 { return DoSave(audacity::ToWXString(path), rename); });
291 action != OnSaveAction::Continue)
292 return action == OnSaveAction::Handled;
293
294 // Prompt for file name?
295 if (projectFileIO.IsTemporary())
296 {
297 return SaveAs(true);
298 }
299
300 return DoSave(projectFileIO.GetFileName(), false);
301}
@ 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 483 of file ProjectFileManager.cpp.

484{
485 auto &project = mProject;
486 auto &projectFileIO = ProjectFileIO::Get( project );
487 auto &window = GetProjectFrame( project );
488 TitleRestorer Restorer( window, project ); // RAII
489 wxFileName filename;
490 FilePath defaultSavePath = FileNames::FindDefaultPath(FileNames::Operation::Save);
491
492 if (projectFileIO.IsTemporary()) {
493 filename.SetPath(defaultSavePath);
494 filename.SetName(project.GetProjectName());
495 }
496 else {
497 filename = projectFileIO.GetFileName();
498 }
499
500 // Bug 1304: Set a default file path if none was given. For Save/SaveAs/SaveCopy
501 if( !FileNames::IsPathAvailable( filename.GetPath( wxPATH_GET_VOLUME| wxPATH_GET_SEPARATOR) ) ){
502 filename.SetPath(defaultSavePath);
503 }
504
505 TranslatableString title = XO("%sSave Project \"%s\" As...")
506 .Format( Restorer.sProjNumber, Restorer.sProjName );
507 TranslatableString message = XO("\
508'Save Project' is for an Audacity project, not an audio file.\n\
509For an audio file that will open in other apps, use 'Export'.\n");
510
511 if (ShowWarningDialog(&window, wxT("FirstProjectSave"), message, true) != wxID_OK) {
512 return false;
513 }
514
515 bool bPrompt = (project.mBatchMode == 0) || (projectFileIO.GetFileName().empty());
516 FilePath fName;
517 bool bOwnsNewName;
518
519 do {
520 if (bPrompt) {
521 // JKC: I removed 'wxFD_OVERWRITE_PROMPT' because we are checking
522 // for overwrite ourselves later, and we disallow it.
523 fName = SelectFile(FileNames::Operation::Save,
524 title,
525 filename.GetPath(),
526 filename.GetFullName(),
527 wxT("aup3"),
529 wxFD_SAVE | wxRESIZE_BORDER,
530 &window);
531
532 if (fName.empty())
533 return false;
534
535 filename = fName;
536 };
537
538 filename.SetExt(wxT("aup3"));
539
540 if ((!bPrompt || !allowOverwrite) && filename.FileExists()) {
541 // Saving a copy of the project should never overwrite an existing project.
543 nullptr,
544 XO("The project was not saved because the file name provided would overwrite another project.\nPlease try again and select an original name."),
545 XO("Error Saving Project"),
546 wxOK|wxICON_ERROR );
547 m.ShowModal();
548 return false;
549 }
550
551 fName = filename.GetFullPath();
552
553 bOwnsNewName = !projectFileIO.IsTemporary() && ( projectFileIO.GetFileName() == fName );
554 // Check to see if the project file already exists, and if it does
555 // check that the project file 'belongs' to this project.
556 // otherwise, prompt the user before overwriting.
557 if (!bOwnsNewName && filename.FileExists()) {
558 // Ensure that project of same name is not open in another window.
559 // fName is the destination file.
560 // mFileName is this project.
561 // It is possible for mFileName == fName even when this project is not
562 // saved to disk, and we then need to check the destination file is not
563 // open in another window.
564 int mayOverwrite = ( projectFileIO.GetFileName() == fName ) ? 2 : 1;
565 for ( auto p : AllProjects{} ) {
566 const wxFileName openProjectName{ ProjectFileIO::Get(*p).GetFileName() };
567 if (openProjectName.SameAs(fName)) {
568 mayOverwrite -= 1;
569 if (mayOverwrite == 0)
570 break;
571 }
572 }
573
574 if (mayOverwrite > 0) {
575 /* i18n-hint: In each case, %s is the name
576 of the file being overwritten.*/
577 auto Message = XO("\
578 Do you want to overwrite the project:\n\"%s\"?\n\n\
579 If you select \"Yes\" the project\n\"%s\"\n\
580 will be irreversibly overwritten.").Format( fName, fName );
581
582 // For safety, there should NOT be an option to hide this warning.
583 int result = AudacityMessageBox(
584 Message,
585 /* i18n-hint: Heading: A warning that a project is about to be overwritten.*/
586 XO("Overwrite Project Warning"),
587 wxYES_NO | wxNO_DEFAULT | wxICON_WARNING,
588 &window);
589 if (result == wxNO) {
590 continue;
591 }
592 if (result == wxCANCEL) {
593 return false;
594 }
595 }
596 else {
597 // Overwrite disallowed. The destination project is open in another window.
599 nullptr,
600 XO("The project was not saved because the selected project is open in another window.\nPlease try again and select an original name."),
601 XO("Error Saving Project"),
602 wxOK|wxICON_ERROR );
603 m.ShowModal();
604 continue;
605 }
606 }
607
608 break;
609 } while (bPrompt);
610
611
612 // Pretend that we are closing the project
613 if (!bOwnsNewName)
614 {
615 if (
618 return false;
619 }
620
621 auto success = DoSave(fName, !bOwnsNewName);
622 if (success) {
623 FileHistory::Global().Append( projectFileIO.GetFileName() );
624 }
625
626 return(success);
627}
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
int ShowWarningDialog(wxWindow *parent, const wxString &internalDialogName, const TranslatableString &message, bool showCancelButton, const TranslatableString &footer)
Definition: Warning.cpp:90
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(), ShowWarningDialog(), 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 454 of file ProjectFileManager.cpp.

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

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 629 of file ProjectFileManager.cpp.

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

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 773 of file ProjectFileManager.cpp.

774{
775 auto &project = mProject;
776 auto &projectFileIO = ProjectFileIO::Get( project );
777
778 // MY: Will save the project to a NEW location a-la Save As
779 // and then tidy up after itself.
780
781 wxString sNewFileName = fnFile.GetFullPath();
782
783 // MY: To allow SaveAs from Timer Recording we need to check what
784 // the value of mFileName is before we change it.
785 FilePath sOldFilename;
786 if (!projectFileIO.IsModified()) {
787 sOldFilename = projectFileIO.GetFileName();
788 }
789
790 // MY: If the project file already exists then bail out
791 // and send populate the message string (pointer) so
792 // we can tell the user what went wrong.
793 if (wxFileExists(sNewFileName)) {
794 return false;
795 }
796
797 auto success = DoSave(sNewFileName, true);
798
799 if (success) {
800 FileHistory::Global().Append( projectFileIO.GetFileName() );
801 }
802
803 return success;
804}

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 108 of file ProjectFileManager.h.

108{ 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 876 of file ProjectFileManager.cpp.

878{
879 // Construct the filter
880 const auto fileTypes = Importer::Get().GetFileTypes( extraType );
881
882 // Retrieve saved path
883 auto path = FileNames::FindDefaultPath(op);
884
885 // Construct and display the file dialog
886 wxArrayString selected;
887
888 FileDialogWrapper dlog(nullptr,
889 XO("Select one or more files"),
890 path,
891 wxT(""),
892 fileTypes,
893 wxFD_OPEN | wxFD_MULTIPLE | wxRESIZE_BORDER);
894
895 dlog.SetFilterIndex( Importer::SelectDefaultOpenType( fileTypes ) );
896
897 int dialogResult = dlog.ShowModal();
898
899 // Convert the filter index to type and save
900 auto index = dlog.GetFilterIndex();
901 const auto &saveType = fileTypes[ index ];
902
904 Importer::SetLastOpenType( saveType );
905
906 if (dialogResult == wxID_OK) {
907 // Return the selected files
908 dlog.GetPaths(selected);
909
910 // Remember the directory
911 FileNames::UpdateDefaultPath(op, ::wxPathOnly(dlog.GetPath()));
912 }
913
914 return selected;
915}
static void SetLastOpenType(const FileNames::FileType &type)
Definition: Import.cpp:253
FileNames::FileTypes GetFileTypes(const FileNames::FileType &extraType={})
Definition: Import.cpp:208
static size_t SelectDefaultOpenType(const FileNames::FileTypes &fileTypes)
Definition: Import.cpp:273
static void SetDefaultOpenType(const FileNames::FileType &type)
Definition: Import.cpp:263
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 147 of file ProjectFileManager.h.

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

◆ mMenuClose

bool ProjectFileManager::mMenuClose { false }
private

Definition at line 150 of file ProjectFileManager.h.

◆ mProject

AudacityProject& ProjectFileManager::mProject
private

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