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

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

843{
844 auto &project = mProject;
845 auto &projectFileIO = ProjectFileIO::Get(project);
846
847 projectFileIO.CloseProject();
848
849 // Blocks were locked in CompactProjectOnClose, so DELETE the data structure so that
850 // there's no memory leak.
852 {
853 mLastSavedTracks->Clear();
854 mLastSavedTracks.reset();
855 }
856}
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 (
804 !projectFileIO.WasCompacted() &&
806 {
807 // If compaction failed, we must do some work in case of close
808 // without save. Don't leave the document blob from the last
809 // push of undo history, when that undo state may get purged
810 // with deletion of some new sample blocks.
811 // REVIEW: UpdateSaved() might fail too. Do we need to test
812 // for that and report it?
813 projectFileIO.UpdateSaved(mLastSavedTracks.get());
814 }
815 }
816}
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:3376
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 1035 of file ProjectFileManager.cpp.

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

1347{
1348 wxArrayString fileNames;
1349 fileNames.Add(fileName);
1350 return Import(std::move(fileNames), addToHistory);
1351}
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 1353 of file ProjectFileManager.cpp.

1354{
1355 fileNames.Sort(FileNames::CompareNoCase);
1357 std::vector<wxString> { fileNames.begin(), fileNames.end() },
1358 addToHistory))
1359 return false;
1360 // Last track in the project is the one that was just added. Use it for
1361 // focus, etc.
1362 Track* lastTrack = nullptr;
1363 const auto range = TrackList::Get(mProject).Any<Track>();
1364 assert(!range.empty());
1365 if(range.empty())
1366 return false;
1367 lastTrack = *(range.rbegin());
1368 BasicUI::CallAfter([wTrack = lastTrack->weak_from_this(), wProject = mProject.weak_from_this()] {
1369 const auto project = wProject.lock();
1370 const auto track = wTrack.lock();
1371 if (!project || !track)
1372 return;
1373 auto& viewPort = Viewport::Get(*project);
1374 TrackFocus::Get(*project).Set(track.get(), true);
1375 viewPort.ZoomFitHorizontally();
1376 viewPort.ShowTrack(*track);
1377 viewPort.HandleResize(); // Adjust scrollers for NEW track sizes.
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 901 of file ProjectFileManager.cpp.

902{
903 const wxFileName newProjPathName(projPathName);
904 auto start = AllProjects{}.begin(), finish = AllProjects{}.end(),
905 iter = std::find_if( start, finish,
906 [&]( const AllProjects::value_type &ptr ){
907 return newProjPathName.SameAs(wxFileNameWrapper{ ProjectFileIO::Get(*ptr).GetFileName() });
908 } );
909 if (iter != finish) {
910 auto errMsg =
911 XO("%s is already open in another window.")
912 .Format( newProjPathName.GetName() );
913 wxLogError(errMsg.Translation()); //Debug?
915 errMsg,
916 XO("Error Opening Project"),
917 wxOK | wxCENTRE);
918 return true;
919 }
920 return false;
921}
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 923 of file ProjectFileManager.cpp.

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

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

827{
828 auto &project = mProject;
829 auto &projectFileIO = ProjectFileIO::Get(project);
830
831 bool bOK = OpenProject();
832 if( !bOK )
833 {
834 auto tmpdir = wxFileName(TempDirectory::UnsavedProjectFileName()).GetPath();
835
836 UnwritableLocationErrorDialog dlg(nullptr, tmpdir);
837 dlg.ShowModal();
838 }
839 return bOK;
840}
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 818 of file ProjectFileManager.cpp.

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

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

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

861{
862 // Construct the filter
863 const auto fileTypes = Importer::Get().GetFileTypes( extraType );
864
865 // Retrieve saved path
866 auto path = FileNames::FindDefaultPath(op);
867
868 // Construct and display the file dialog
869 wxArrayString selected;
870
871 FileDialogWrapper dlog(nullptr,
872 XO("Select one or more files"),
873 path,
874 wxT(""),
875 fileTypes,
876 wxFD_OPEN | wxFD_MULTIPLE | wxRESIZE_BORDER);
877
878 dlog.SetFilterIndex( Importer::SelectDefaultOpenType( fileTypes ) );
879
880 int dialogResult = dlog.ShowModal();
881
882 // Convert the filter index to type and save
883 auto index = dlog.GetFilterIndex();
884 const auto &saveType = fileTypes[ index ];
885
887 Importer::SetLastOpenType( saveType );
888
889 if (dialogResult == wxID_OK) {
890 // Return the selected files
891 dlog.GetPaths(selected);
892
893 // Remember the directory
894 FileNames::UpdateDefaultPath(op, ::wxPathOnly(dlog.GetPath()));
895 }
896
897 return selected;
898}
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: