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)
 
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)
 

Private Member Functions

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

Constructor & Destructor Documentation

◆ ProjectFileManager() [1/2]

ProjectFileManager::ProjectFileManager ( AudacityProject project)
explicit

Definition at line 100 of file ProjectFileManager.cpp.

101: mProject{ project }
102{
103}
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 1113 of file ProjectFileManager.cpp.

1115{
1116 auto &project = mProject;
1117 auto &history = ProjectHistory::Get( project );
1118 auto &projectFileIO = ProjectFileIO::Get( project );
1119 auto &tracks = TrackList::Get( project );
1120
1121 std::vector<Track*> results;
1122
1124
1125 wxFileName fn(fileName);
1126
1127 bool initiallyEmpty = tracks.empty();
1128 double newRate = 0;
1129 wxString trackNameBase = fn.GetName();
1130 int i = -1;
1131
1132 // Fix the bug 2109.
1133 // In case the project had soloed tracks before importing,
1134 // all newly imported tracks are muted.
1135 const bool projectHasSolo =
1136 !(tracks.Any<PlayableTrack>() + &PlayableTrack::GetSolo).empty();
1137 if (projectHasSolo) {
1138 for (auto &group : newTracks)
1139 for (const auto pTrack : group->Any<WaveTrack>())
1140 pTrack->SetMute(true);
1141 }
1142
1143 // Must add all tracks first (before using Track::IsLeader)
1144 for (auto &group : newTracks) {
1145 if (group->empty()) {
1146 assert(false);
1147 continue;
1148 }
1149 for (const auto pTrack : group->Any<WaveTrack>())
1150 results.push_back(pTrack);
1151 tracks.Append(std::move(*group));
1152 }
1153 newTracks.clear();
1154
1155 // Now name them
1156
1157 // Add numbers to track names only if there is more than one (mono or stereo)
1158 // track (not necessarily, more than one channel)
1159 const bool useSuffix = results.size() > 1;
1160
1161 for (const auto &newTrack : results) {
1162 ++i;
1163 newTrack->SetSelected(true);
1164 if (useSuffix)
1165 //i18n-hint Name default name assigned to a clip on track import
1166 newTrack->SetName(XC("%s %d", "clip name template")
1167 .Format(trackNameBase, i + 1).Translation());
1168 else
1169 newTrack->SetName(trackNameBase);
1170
1171 newTrack->TypeSwitch([&](WaveTrack &wt) {
1172 if (newRate == 0)
1173 newRate = wt.GetRate();
1174 const auto trackName = wt.GetName();
1175 for(const auto& interval : wt.Intervals())
1176 interval->SetName(trackName);
1177 });
1178 }
1179
1180 history.PushState(XO("Imported '%s'").Format( fileName ),
1181 XO("Import"));
1182
1183#if defined(__WXGTK__)
1184 // See bug #1224
1185 // The track panel hasn't we been fully created, so the DoZoomFit() will not give
1186 // expected results due to a window width of zero. Should be safe to yield here to
1187 // allow the creation to complete. If this becomes a problem, it "might" be possible
1188 // to queue a dummy event to trigger the DoZoomFit().
1189 wxEventLoopBase::GetActive()->YieldFor(wxEVT_CATEGORY_UI | wxEVT_CATEGORY_USER_INPUT);
1190#endif
1191
1192 // If the project was clean and temporary (not permanently saved), then set
1193 // the filename to the just imported path.
1194 if (initiallyEmpty && projectFileIO.IsTemporary()) {
1195 project.SetProjectName(fn.GetName());
1196 project.SetInitialImportPath(fn.GetPath());
1197 projectFileIO.SetProjectTitle();
1198 }
1199
1200 // Moved this call to higher levels to prevent flicker redrawing everything on each file.
1201 // HandleResize();
1202}
XO("Cut/Copy/Paste")
#define XC(s, c)
Definition: Internat.h:37
const auto tracks
static const auto fn
Abstract base class used in importing a file.
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:56
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:354
A Track that contains audio waveform data.
Definition: WaveTrack.h:220
double GetRate() const override
Definition: WaveTrack.cpp:868
auto Intervals()
Definition: WaveTrack.h:991
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 831 of file ProjectFileManager.cpp.

832{
833 auto &project = mProject;
834 auto &projectFileIO = ProjectFileIO::Get(project);
835
836 projectFileIO.CloseProject();
837
838 // Blocks were locked in CompactProjectOnClose, so DELETE the data structure so that
839 // there's no memory leak.
841 {
842 mLastSavedTracks->Clear();
843 mLastSavedTracks.reset();
844 }
845}
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 1466 of file ProjectFileManager.cpp.

1467{
1468 auto &project = mProject;
1469 auto &undoManager = UndoManager::Get(project);
1470 auto &clipboard = Clipboard::Get();
1471 auto &projectFileIO = ProjectFileIO::Get(project);
1472 bool isBatch = project.mBatchMode > 0;
1473
1474 // Purpose of this is to remove the -wal file.
1475 projectFileIO.ReopenProject();
1476
1477 auto savedState = undoManager.GetSavedState();
1478 const auto currentState = undoManager.GetCurrentState();
1479 if (savedState < 0) {
1480 undoManager.StateSaved();
1481 savedState = undoManager.GetSavedState();
1482 if (savedState < 0) {
1483 wxASSERT(false);
1484 savedState = 0;
1485 }
1486 }
1487 const auto least = std::min<size_t>(savedState, currentState);
1488 const auto greatest = std::max<size_t>(savedState, currentState);
1489 std::vector<const TrackList*> trackLists;
1490 auto fn = [&](const UndoStackElem& elem) {
1491 if (auto pTracks = TrackList::FindUndoTracks(elem))
1492 trackLists.push_back(pTracks);
1493 };
1494 undoManager.VisitStates(fn, least, 1 + least);
1495 if (least != greatest)
1496 undoManager.VisitStates(fn, greatest, 1 + greatest);
1497
1498 int64_t total = projectFileIO.GetTotalUsage();
1499 int64_t used = projectFileIO.GetCurrentUsage(trackLists);
1500
1501 auto before = wxFileName::GetSize(projectFileIO.GetFileName());
1502
1503 CompactDialog dlg(
1504 XO("Compacting this project will free up disk space by removing unused bytes within the file.\n\n"
1505 "There is %s of free disk space and this project is currently using %s.\n"
1506 "\n"
1507 "If you proceed, the current Undo/Redo History and clipboard contents will be discarded "
1508 "and you will recover approximately %s of disk space.\n"
1509 "\n"
1510 "Do you want to continue?")
1511 .Format(Internat::FormatSize(projectFileIO.GetFreeDiskSpace()),
1512 Internat::FormatSize(before.GetValue()),
1513 Internat::FormatSize(total - used)));
1514 if (isBatch || dlg.ShowModal() == wxYES)
1515 {
1516 // We can remove redo states, if they are after the saved state.
1517 undoManager.RemoveStates(1 + greatest, undoManager.GetNumStates());
1518
1519 // We can remove all states between the current and the last saved.
1520 if (least < greatest)
1521 undoManager.RemoveStates(least + 1, greatest);
1522
1523 // We can remove all states before the current and the last saved.
1524 undoManager.RemoveStates(0, least);
1525
1526 // And clear the clipboard, if needed
1527 if (&mProject == clipboard.Project().lock().get())
1528 clipboard.Clear();
1529
1530 // Refresh the before space usage since it may have changed due to the
1531 // above actions.
1532 auto before = wxFileName::GetSize(projectFileIO.GetFileName());
1533
1534 projectFileIO.Compact(trackLists, true);
1535
1536 auto after = wxFileName::GetSize(projectFileIO.GetFileName());
1537
1538 if (!isBatch)
1539 {
1541 XO("Compacting actually freed %s of disk space.")
1542 .Format(Internat::FormatSize((before - after).GetValue())),
1543 XO("Compact Project"));
1544 }
1545
1546 undoManager.RenameState( undoManager.GetCurrentState(),
1547 XO("Compacted project file"),
1548 XO("Compact") );
1549 }
1550}
int AudacityMessageBox(const TranslatableString &message, const TranslatableString &caption, long style, wxWindow *parent, int x, int y)
static Clipboard & Get()
Definition: Clipboard.cpp:28
static TranslatableString FormatSize(wxLongLong size)
Convert a number to a string while formatting it in bytes, KB, MB, GB.
Definition: Internat.cpp:203
static TrackList * FindUndoTracks(const UndoStackElem &state)
Definition: Track.cpp:1423
static UndoManager & Get(AudacityProject &project)
Definition: UndoManager.cpp:71
Holds one item with description and time range for the UndoManager.
Definition: UndoManager.h:117

References AudacityMessageBox(), TrackList::FindUndoTracks(), 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 778 of file ProjectFileManager.cpp.

779{
780 auto &project = mProject;
781 auto &projectFileIO = ProjectFileIO::Get(project);
782
783 // Lock all blocks in all tracks of the last saved version, so that
784 // the sample blocks aren't deleted from the database when we destroy the
785 // sample block objects in memory.
787 {
788 for (auto wt : mLastSavedTracks->Any<WaveTrack>())
789 wt->CloseLock();
790
791 // Attempt to compact the project
792 projectFileIO.Compact( { mLastSavedTracks.get() } );
793
794 if ( !projectFileIO.WasCompacted() &&
796 // If compaction failed, we must do some work in case of close
797 // without save. Don't leave the document blob from the last
798 // push of undo history, when that undo state may get purged
799 // with deletion of some new sample blocks.
800 // REVIEW: UpdateSaved() might fail too. Do we need to test
801 // for that and report it?
802 projectFileIO.UpdateSaved( mLastSavedTracks.get() );
803 }
804 }
805}
bool UnsavedChanges() const

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

83{
84 InvisibleTemporaryProject tempProject;
85 auto &project = tempProject.Project();
86 auto &projectFileManager = Get(project);
87 // Read the project, discarding autosave
88 projectFileManager.ReadProjectFile(filename, true);
89
90 if (projectFileManager.mLastSavedTracks) {
91 for (auto wt : projectFileManager.mLastSavedTracks->Any<WaveTrack>())
92 wt->CloseLock();
93 projectFileManager.mLastSavedTracks.reset();
94 }
95
96 // Side-effect on database is done, and destructor of tempProject
97 // closes the temporary project properly
98}
Makes a temporary project that doesn't display on the screen.
AudacityProject & Project()
static ProjectFileManager & Get(AudacityProject &project)

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

317{
318 // See explanation above
319 // ProjectDisabler disabler(this);
320 auto &proj = mProject;
321 auto &window = GetProjectFrame( proj );
322 auto &projectFileIO = ProjectFileIO::Get( proj );
323 const auto &settings = ProjectSettings::Get( proj );
324
325 // Some confirmation dialogs
326 {
327 if (TempDirectory::FATFilesystemDenied(fileName, XO("Projects cannot be saved to FAT drives.")))
328 {
329 return false;
330 }
331
332 auto &tracks = TrackList::Get( proj );
333 if (tracks.empty())
334 {
335 if (UndoManager::Get( proj ).UnsavedChanges() &&
336 settings.EmptyCanBeDirty())
337 {
338 int result = AudacityMessageBox(
339 XO(
340 "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?"),
341 XO("Warning - Empty Project"),
342 wxYES_NO | wxICON_QUESTION,
343 &window);
344 if (result == wxNO)
345 {
346 return false;
347 }
348 }
349 }
350
351 wxULongLong fileSize = wxFileName::GetSize(projectFileIO.GetFileName());
352
353 wxDiskspaceSize_t freeSpace;
354 if (wxGetDiskSpace(FileNames::AbbreviatePath(fileName), NULL, &freeSpace))
355 {
356 if (freeSpace.GetValue() <= fileSize.GetValue())
357 {
359 XO("Insufficient Disk Space"),
360 XO("The project size exceeds the available free space on the target disk.\n\n"
361 "Please select a different disk with more free space."),
362 "Error:_Disk_full_or_not_writable"
363 );
364
365 return false;
366 }
367 }
368 }
369 // End of confirmations
370
371 // Always save a backup of the original project file
372 std::optional<ProjectFileIO::BackupProject> pBackupProject;
373 if (fromSaveAs && wxFileExists(fileName))
374 {
375 pBackupProject.emplace(projectFileIO, fileName);
376 if (!pBackupProject->IsOk())
377 return false;
378 }
379
380 if (FileNames::IsOnFATFileSystem(fileName))
381 {
382 if (wxFileName::GetSize(projectFileIO.GetFileName()) > UINT32_MAX)
383 {
385 XO("Error Saving Project"),
386 XO("The project exceeds the maximum size of 4GB when writing to a FAT32 formatted filesystem."),
387 "Error:_Unsuitable_drive"
388 );
389 return false;
390 }
391 }
392
393 bool success = projectFileIO.SaveProject(fileName, mLastSavedTracks.get());
394 if (!success)
395 {
396 // Show this error only if we didn't fail reconnection in SaveProject
397 // REVIEW: Could HasConnection() be true but SaveProject() still have failed?
398 if (!projectFileIO.HasConnection()) {
399 using namespace BasicUI;
401 XO("Error Saving Project"),
403 "Error:_Disk_full_or_not_writable",
404 ErrorDialogOptions{ ErrorDialogType::ModalErrorReport } );
405 }
406 return false;
407 }
408
409 proj.SetProjectName(wxFileName(fileName).GetName());
410 projectFileIO.SetProjectTitle();
411
413 ProjectStatus::Get(proj).Set(XO("Saved %s").Format(fileName));
414
416 {
417 mLastSavedTracks->Clear();
418 }
420
421 auto &tracks = TrackList::Get(proj);
422 for (auto t : tracks)
423 mLastSavedTracks->Append(std::move(*t->Duplicate()));
424
425 // If we get here, saving the project was successful, so we can DELETE
426 // any backup project.
427 if (pBackupProject)
428 pBackupProject->Discard();
429
430 return true;
431}
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:83
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:372
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:259
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:51

References FileNames::AbbreviatePath(), AudacityMessageBox(), 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:

◆ Get() [1/2]

ProjectFileManager & ProjectFileManager::Get ( AudacityProject project)
static

◆ Get() [2/2]

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

Definition at line 77 of file ProjectFileManager.cpp.

78{
79 return Get( const_cast< AudacityProject & >( project ) );
80}
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 105 of file ProjectFileManager.h.

105{ return mMenuClose; }

◆ Import()

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

Definition at line 1291 of file ProjectFileManager.cpp.

1294{
1295 auto &project = mProject;
1296 auto &projectFileIO = ProjectFileIO::Get(project);
1297 auto oldTags = Tags::Get( project ).shared_from_this();
1298 bool initiallyEmpty = TrackList::Get(project).empty();
1299 TrackHolders newTracks;
1300 TranslatableString errorMessage;
1301
1302#ifdef EXPERIMENTAL_IMPORT_AUP3
1303 // Handle AUP3 ("project") files directly
1304 if (fileName.AfterLast('.').IsSameAs(wxT("aup3"), false)) {
1305 if (ImportProject(project, fileName)) {
1306 auto &history = ProjectHistory::Get(project);
1307
1308 // If the project was clean and temporary (not permanently saved), then set
1309 // the filename to the just imported path.
1310 if (initiallyEmpty && projectFileIO.IsTemporary()) {
1311 wxFileName fn(fileName);
1312 project.SetProjectName(fn.GetName());
1313 project.SetInitialImportPath(fn.GetPath());
1314 projectFileIO.SetProjectTitle();
1315 }
1316
1317 history.PushState(XO("Imported '%s'").Format(fileName), XO("Import"));
1318
1319 if (addToHistory) {
1320 FileHistory::Global().Append(fileName);
1321 }
1322 }
1323 else {
1324 errorMessage = projectFileIO.GetLastError();
1325 if (errorMessage.empty()) {
1326 errorMessage = XO("Failed to import project");
1327 }
1328
1329 // Additional help via a Help button links to the manual.
1331 XO("Error Importing"),
1332 errorMessage, wxT("Importing_Audio"));
1333 }
1334
1335 return false;
1336 }
1337#endif
1338
1339 {
1340 // Backup Tags, before the import. Be prepared to roll back changes.
1341 bool committed = false;
1342 auto cleanup = finally([&]{
1343 if ( !committed )
1344 Tags::Set( project, oldTags );
1345 });
1346 auto newTags = oldTags->Duplicate();
1347 Tags::Set( project, newTags );
1348
1349#ifndef EXPERIMENTAL_IMPORT_AUP3
1350 // Handle AUP3 ("project") files specially
1351 if (fileName.AfterLast('.').IsSameAs(wxT("aup3"), false)) {
1353 XO("Error Importing"),
1354 XO( "Cannot import AUP3 format. Use File > Open instead"),
1355 wxT("File_Menu"));
1356 return false;
1357 }
1358#endif
1359 ImportProgress importProgress(project);
1360 bool success = Importer::Get().Import(project, fileName,
1361 &importProgress,
1363 newTracks,
1364 newTags.get(),
1365 errorMessage);
1366 if (!errorMessage.empty()) {
1367 // Error message derived from Importer::Import
1368 // Additional help via a Help button links to the manual.
1370 XO("Error Importing"), errorMessage, wxT("Importing_Audio"));
1371 }
1372 if (!success)
1373 return false;
1374
1375 if (addToHistory) {
1376 FileHistory::Global().Append(fileName);
1377 }
1378
1379 // no more errors, commit
1380 committed = true;
1381 }
1382
1383 // for LOF ("list of files") files, do not import the file as if it
1384 // were an audio file itself
1385 if (fileName.AfterLast('.').IsSameAs(wxT("lof"), false)) {
1386 // PRL: don't redundantly do the steps below, because we already
1387 // did it in case of LOF, because of some weird recursion back to this
1388 // same function. I think this should be untangled.
1389
1390 // So Undo history push is not bypassed, despite appearances.
1391 return false;
1392 }
1393
1394 // Handle AUP ("legacy project") files directly
1395 if (fileName.AfterLast('.').IsSameAs(wxT("aup"), false)) {
1396 // If the project was clean and temporary (not permanently saved), then set
1397 // the filename to the just imported path.
1398 if (initiallyEmpty && projectFileIO.IsTemporary()) {
1399 wxFileName fn(fileName);
1400 project.SetProjectName(fn.GetName());
1401 project.SetInitialImportPath(fn.GetPath());
1402 projectFileIO.SetProjectTitle();
1403 }
1404
1405 auto &history = ProjectHistory::Get( project );
1406
1407 history.PushState(XO("Imported '%s'").Format( fileName ), XO("Import"));
1408
1409 return true;
1410 }
1411
1412 // PRL: Undo history is incremented inside this:
1413 AddImportedTracks(fileName, std::move(newTracks));
1414
1415 return true;
1416}
wxT("CloseDown"))
std::vector< std::shared_ptr< TrackList > > TrackHolders
Definition: ImportRaw.h:24
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, TranslatableString &errorMessage)
Definition: Import.cpp:492
void AddImportedTracks(const FilePath &fileName, TrackHolders &&newTracks)
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:972
Holds a msgid for the translation catalog; may also bind format arguments.
static WaveTrackFactory & Get(AudacityProject &project)
Definition: WaveTrack.cpp:4079
bool ImportProject(AudacityProject &dest, const FilePath &fileName)

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

Referenced by ImportCommand::Apply(), anonymous_namespace{FileMenus.cpp}::DoImport(), AUPImportFileHandle::HandleImport(), 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 890 of file ProjectFileManager.cpp.

891{
892 const wxFileName newProjPathName(projPathName);
893 auto start = AllProjects{}.begin(), finish = AllProjects{}.end(),
894 iter = std::find_if( start, finish,
895 [&]( const AllProjects::value_type &ptr ){
896 return newProjPathName.SameAs(wxFileNameWrapper{ ProjectFileIO::Get(*ptr).GetFileName() });
897 } );
898 if (iter != finish) {
899 auto errMsg =
900 XO("%s is already open in another window.")
901 .Format( newProjPathName.GetName() );
902 wxLogError(errMsg.Translation()); //Debug?
904 errMsg,
905 XO("Error Opening Project"),
906 wxOK | wxCENTRE);
907 return true;
908 }
909 return false;
910}
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 912 of file ProjectFileManager.cpp.

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

References AudacityMessageBox(), DoImportMIDI(), TempDirectory::FATFilesystemDenied(), PluginManager::Get(), Get(), ProjectWindow::Get(), PlatformCompatibility::GetLongFileName(), Import(), IsAlreadyOpen(), FileNames::IsMidi(), OpenProjectFile(), project, MenuCreator::RebuildAllMenuBars(), wxT(), XO(), and ProjectWindow::ZoomAfterImport().

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

816{
817 auto &project = mProject;
818 auto &projectFileIO = ProjectFileIO::Get(project);
819
820 bool bOK = OpenProject();
821 if( !bOK )
822 {
823 auto tmpdir = wxFileName(TempDirectory::UnsavedProjectFileName()).GetPath();
824
825 UnwritableLocationErrorDialog dlg(nullptr, tmpdir);
826 dlg.ShowModal();
827 }
828 return bOK;
829}
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 807 of file ProjectFileManager.cpp.

808{
809 auto &project = mProject;
810 auto &projectFileIO = ProjectFileIO::Get(project);
811
812 return projectFileIO.OpenProject();
813}

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

1035{
1036 auto &project = mProject;
1037 auto &history = ProjectHistory::Get( project );
1038 auto &tracks = TrackList::Get( project );
1039 auto &trackPanel = TrackPanel::Get( project );
1040 auto &projectFileIO = ProjectFileIO::Get( project );
1041 auto &window = ProjectWindow::Get( project );
1042
1043 auto results = ReadProjectFile( fileName );
1044 const bool bParseSuccess = results.parseSuccess;
1045 const auto &errorStr = results.errorString;
1046 const bool err = results.trackError;
1047
1048 if (bParseSuccess && !err) {
1049 auto &formats = ProjectNumericFormats::Get( project );
1051 window.mbInitializingScrollbar = true;
1052
1053 auto &selectionManager = ProjectSelectionManager::Get( project );
1054
1055 selectionManager.AS_SetSelectionFormat(formats.GetSelectionFormat());
1056 selectionManager.TT_SetAudioTimeFormat(formats.GetAudioTimeFormat());
1057 selectionManager.SSBL_SetFrequencySelectionFormatName(
1058 formats.GetFrequencySelectionFormatName());
1059 selectionManager.SSBL_SetBandwidthSelectionFormatName(
1060 formats.GetBandwidthSelectionFormatName());
1061
1063 TrackFocus::Get(project).Set(*tracks.begin());
1064 window.HandleResize();
1065 trackPanel.Refresh(false);
1066
1067 // ? Old rationale in this comment no longer applies in 3.0.0, with no
1068 // more on-demand loading:
1069 trackPanel.Update(); // force any repaint to happen now,
1070 // else any asynch calls into the blockfile code will not have
1071 // finished logging errors (if any) before the call to ProjectFSCK()
1072
1073 if (addtohistory)
1074 FileHistory::Global().Append(fileName);
1075 }
1076
1077 if (bParseSuccess && !err) {
1078 if (projectFileIO.IsRecovered())
1079 {
1080 // PushState calls AutoSave(), so no longer need to do so here.
1081 history.PushState(XO("Project was recovered"), XO("Recover"));
1082 }
1083 return &project;
1084 }
1085 else {
1086 // Vaughan, 2011-10-30:
1087 // See first topic at http://bugzilla.audacityteam.org/show_bug.cgi?id=451#c16.
1088 // Calling mTracks->Clear() with deleteTracks true results in data loss.
1089
1090 // PRL 2014-12-19:
1091 // I made many changes for wave track memory management, but only now
1092 // read the above comment. I may have invalidated the fix above (which
1093 // may have spared the files at the expense of leaked memory). But
1094 // here is a better way to accomplish the intent, doing like what happens
1095 // when the project closes:
1096 for (auto pTrack : tracks.Any<WaveTrack>())
1097 pTrack->CloseLock();
1098
1099 tracks.Clear(); //tracks.Clear(true);
1100
1101 wxLogError(wxT("Could not parse file \"%s\". \nError: %s"), fileName, errorStr.Debug());
1102
1103 projectFileIO.ShowError( *ProjectFramePlacement(&project),
1104 XO("Error Opening Project"),
1105 errorStr,
1106 results.helpUrl);
1107
1108 return nullptr;
1109 }
1110}
ReadProjectResults ReadProjectFile(const FilePath &fileName, bool discardAutosave=false)
static ProjectNumericFormats & Get(AudacityProject &project)
static ProjectSelectionManager & Get(AudacityProject &project)
Track * Get()
static TrackPanel & Get(AudacityProject &project)
Definition: TrackPanel.cpp:232

References FileHistory::Append(), TrackFocus::Get(), ProjectNumericFormats::Get(), ProjectFileIO::Get(), ProjectHistory::Get(), TrackList::Get(), ProjectSelectionManager::Get(), ProjectSettings::Get(), ProjectWindow::Get(), TrackPanel::Get(), FileHistory::Global(), ProjectHistory::InitialState(), mProject, project, ProjectFramePlacement(), ReadProjectFile(), settings(), 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 155 of file ProjectFileManager.cpp.

158{
159 auto &project = mProject;
160 auto &projectFileIO = ProjectFileIO::Get( project );
161 auto &window = GetProjectFrame( project );
162
166 auto parseResult = projectFileIO.LoadProject(fileName, discardAutosave);
167 const bool bParseSuccess = parseResult.has_value();
168
169 bool err = false;
170 bool waveTrackLinkTypeChanged = false;
171
172 TranslatableString otherError;
173
174 if (bParseSuccess)
175 {
177 for (auto t : tracks) {
178 const auto linkType = t->GetLinkType();
179 // Note, the next function may have an important upgrading side effect,
180 // and return no error; or it may find a real error and repair it, but
181 // that repaired track won't be used because opening will fail.
182 if (!t->LinkConsistencyFix()) {
183 otherError = XO("A channel of a stereo track was missing.");
184 err = true;
185 }
186 if(!err &&
187 linkType != ChannelGroup::LinkType::None &&
188 t->GetLinkType() == ChannelGroup::LinkType::None)
189 {
190 //Not an elegant way to deal with wave track linking
191 //compatibility between versions
192 if(const auto left = dynamic_cast<WaveTrack*>(t))
193 {
195 if(const auto right = dynamic_cast<WaveTrack*>(*tracks.Find(left).advance(1)))
197 waveTrackLinkTypeChanged = true;
198 }
199 }
200
201 if (const auto message = t->GetErrorOpening()) {
202 wxLogWarning(
203 wxT("Track %s had error reading clip values from project file."),
204 t->GetName());
205 err = true;
206 // Keep at most one of the error messages
207 otherError = *message;
208 }
209 }
210
211 if (!err) {
212 if(waveTrackLinkTypeChanged && !discardAutosave)
213 {
215"This project contained stereo tracks with non-aligned content.\n"
216"This feature is not supported in Audacity versions past 3.3.3.\n"
217"These stereo tracks have been split into mono tracks.\n"
218"As a result, some realtime effects may be missing.\n"
219"Please verify that everything works as intended before saving."
220 ));
221 }
222
223 parseResult->Commit();
224 if (discardAutosave)
225 // REVIEW: Failure OK?
226 projectFileIO.AutoSaveDelete();
227 else if (projectFileIO.IsRecovered()) {
228 bool resaved = false;
229
230 if (!projectFileIO.IsTemporary() &&
231 !waveTrackLinkTypeChanged)
232 {
233 // Re-save non-temporary project to its own path. This
234 // might fail to update the document blob in the database.
235 resaved = projectFileIO.SaveProject(fileName, nullptr);
236 }
237
239 resaved
240 ? XO(
241"This project was not saved properly the last time Audacity ran.\n\n"
242"It has been recovered to the last snapshot.")
243 : XO(
244"This project was not saved properly the last time Audacity ran.\n\n"
245"It has been recovered to the last snapshot, but you must save it\n"
246"to preserve its contents."),
247 XO("Project Recovered"),
248 wxICON_WARNING,
249 &window);
250 }
251
252 // By making a duplicate set of pointers to the existing blocks
253 // on disk, we add one to their reference count, guaranteeing
254 // that their reference counts will never reach zero and thus
255 // the version saved on disk will be preserved until the
256 // user selects Save().
258 for (auto t : tracks)
259 mLastSavedTracks->Append(std::move(*t->Duplicate()));
260 }
261 }
262
263 return {
264 bParseSuccess,
265 err,
266 (bParseSuccess ? otherError : projectFileIO.GetLastError()),
267 FindHelpUrl(projectFileIO.GetLibraryError())
268 };
269}
static RealtimeEffectList & Get(AudacityProject &project)
void Clear()
Use only in the main thread. Sends Remove messages.
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:274
wxString FindHelpUrl(const TranslatableString &libraryError)

References AudacityMessageBox(), RealtimeEffectList::Clear(), TrackList::Create(), anonymous_namespace{ProjectFileManager.cpp}::FindHelpUrl(), ProjectFileIO::Get(), RealtimeEffectList::Get(), TrackList::Get(), GetProjectFrame(), ChannelGroup::None, project, BasicUI::ShowMessageBox(), tracks, wxT(), and XO().

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

272{
273 auto &projectFileIO = ProjectFileIO::Get(mProject);
274
275 // Prompt for file name?
276 if (projectFileIO.IsTemporary())
277 {
278 return SaveAs(true);
279 }
280
281 return DoSave(projectFileIO.GetFileName(), false);
282}
bool SaveAs(bool allowOverwrite=false)
bool DoSave(const FilePath &fileName, bool fromSaveAs)

References DoSave(), ProjectFileIO::Get(), mProject, and SaveAs().

Here is the call graph for this function:

◆ SaveAs() [1/2]

bool ProjectFileManager::SaveAs ( bool  allowOverwrite = false)

Definition at line 464 of file ProjectFileManager.cpp.

465{
466 auto &project = mProject;
467 auto &projectFileIO = ProjectFileIO::Get( project );
468 auto &window = GetProjectFrame( project );
469 TitleRestorer Restorer( window, project ); // RAII
470 wxFileName filename;
471 FilePath defaultSavePath = FileNames::FindDefaultPath(FileNames::Operation::Save);
472
473 if (projectFileIO.IsTemporary()) {
474 filename.SetPath(defaultSavePath);
475 filename.SetName(project.GetProjectName());
476 }
477 else {
478 filename = projectFileIO.GetFileName();
479 }
480
481 // Bug 1304: Set a default file path if none was given. For Save/SaveAs/SaveCopy
482 if( !FileNames::IsPathAvailable( filename.GetPath( wxPATH_GET_VOLUME| wxPATH_GET_SEPARATOR) ) ){
483 filename.SetPath(defaultSavePath);
484 }
485
486 TranslatableString title = XO("%sSave Project \"%s\" As...")
487 .Format( Restorer.sProjNumber, Restorer.sProjName );
488 TranslatableString message = XO("\
489'Save Project' is for an Audacity project, not an audio file.\n\
490For an audio file that will open in other apps, use 'Export'.\n");
491
492 if (ShowWarningDialog(&window, wxT("FirstProjectSave"), message, true) != wxID_OK) {
493 return false;
494 }
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 auto success = DoSave(fName, !bOwnsNewName);
594 if (success) {
595 FileHistory::Global().Append( projectFileIO.GetFileName() );
596 }
597
598 return(success);
599}
static const auto title
wxString FilePath
Definition: Project.h:21
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)

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

Referenced by 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 435 of file ProjectFileManager.cpp.

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

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

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

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

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

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

106{ 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 848 of file ProjectFileManager.cpp.

850{
851 // Construct the filter
852 const auto fileTypes = Importer::Get().GetFileTypes( extraType );
853
854 // Retrieve saved path
855 auto path = FileNames::FindDefaultPath(op);
856
857 // Construct and display the file dialog
858 wxArrayString selected;
859
860 FileDialogWrapper dlog(nullptr,
861 XO("Select one or more files"),
862 path,
863 wxT(""),
864 fileTypes,
865 wxFD_OPEN | wxFD_MULTIPLE | wxRESIZE_BORDER);
866
867 dlog.SetFilterIndex( Importer::SelectDefaultOpenType( fileTypes ) );
868
869 int dialogResult = dlog.ShowModal();
870
871 // Convert the filter index to type and save
872 auto index = dlog.GetFilterIndex();
873 const auto &saveType = fileTypes[ index ];
874
876 Importer::SetLastOpenType( saveType );
877
878 if (dialogResult == wxID_OK) {
879 // Return the selected files
880 dlog.GetPaths(selected);
881
882 // Remember the directory
883 FileNames::UpdateDefaultPath(op, ::wxPathOnly(dlog.GetPath()));
884 }
885
886 return selected;
887}
static void SetLastOpenType(const FileNames::FileType &type)
Definition: Import.cpp:259
FileNames::FileTypes GetFileTypes(const FileNames::FileType &extraType={})
Definition: Import.cpp:214
static size_t SelectDefaultOpenType(const FileNames::FileTypes &fileTypes)
Definition: Import.cpp:279
static void SetDefaultOpenType(const FileNames::FileType &type)
Definition: Import.cpp:269
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 131 of file ProjectFileManager.h.

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

◆ mMenuClose

bool ProjectFileManager::mMenuClose { false }
private

Definition at line 134 of file ProjectFileManager.h.

◆ mProject

AudacityProject& ProjectFileManager::mProject
private

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