Audacity 3.2.0
ProjectFileManager.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3Audacity: A Digital Audio Editor
4
5ProjectFileManager.cpp
6
7Paul Licameli split from AudacityProject.cpp
8
9**********************************************************************/
10
11#include "ProjectFileManager.h"
12
13#include <wx/crt.h> // for wxPrintf
14
15#if defined(__WXGTK__)
16#include <wx/evtloop.h>
17#endif
18
19#include <wx/frame.h>
20#include <wx/log.h>
21#include "BasicUI.h"
22#include "CodeConversions.h"
23#include "Legacy.h"
25#include "Project.h"
26#include "ProjectFileIO.h"
27#include "ProjectFSCK.h"
28#include "ProjectHistory.h"
30#include "ProjectWindows.h"
31#include "ProjectRate.h"
32#include "ProjectSettings.h"
33#include "ProjectStatus.h"
34#include "ProjectWindow.h"
35#include "SelectFile.h"
36#include "SelectUtilities.h"
37#include "SelectionState.h"
38#include "Tags.h"
39#include "TempDirectory.h"
40#include "TrackPanelAx.h"
41#include "TrackPanel.h"
42#include "UndoManager.h"
43#include "WaveTrack.h"
44#include "WaveClip.h"
45#include "wxFileNameWrapper.h"
46#include "export/Export.h"
47#include "import/Import.h"
48#include "import/ImportMIDI.h"
51#include "widgets/FileHistory.h"
53#include "widgets/Warning.h"
55#include "XMLFileReader.h"
56
57#include "HelpText.h"
58
59#include <optional>
60
62 []( AudacityProject &parent ){
63 auto result = std::make_shared< ProjectFileManager >( parent );
64 return result;
65 }
66};
67
69{
70 return project.AttachedObjects::Get< ProjectFileManager >( sFileManagerKey );
71}
72
74{
75 return Get( const_cast< AudacityProject & >( project ) );
76}
77
79{
80 InvisibleTemporaryProject tempProject;
81 auto &project = tempProject.Project();
82 auto &projectFileManager = Get(project);
83 // Read the project, discarding autosave
84 projectFileManager.ReadProjectFile(filename, true);
85
86 if (projectFileManager.mLastSavedTracks) {
87 for (auto wt : projectFileManager.mLastSavedTracks->Any<WaveTrack>())
88 wt->CloseLock();
89 projectFileManager.mLastSavedTracks.reset();
90 }
91
92 // Side-effect on database is done, and destructor of tempProject
93 // closes the temporary project properly
94}
95
97: mProject{ project }
98{
99}
100
102
103namespace {
104
105const char *const defaultHelpUrl =
106 "FAQ:Errors_on_opening_or_recovering_an_Audacity_project";
107
108using Pair = std::pair< const char *, const char * >;
110 {
111 "not well-formed (invalid token)",
112 "Error:_not_well-formed_(invalid_token)_at_line_x"
113 },
114 {
115 "reference to invalid character number",
116 "Error_Opening_Project:_Reference_to_invalid_character_number_at_line_x"
117 },
118 {
119 "mismatched tag",
120 "#mismatched"
121 },
122// This error with FAQ entry is reported elsewhere, not here....
123//#[[#corrupt|Error Opening File or Project: File may be invalid or corrupted]]
124};
125
126wxString FindHelpUrl( const TranslatableString &libraryError )
127{
128 wxString helpUrl;
129 if ( !libraryError.empty() ) {
130 helpUrl = defaultHelpUrl;
131
132 auto msgid = libraryError.MSGID().GET();
133 auto found = std::find_if( begin(helpURLTable), end(helpURLTable),
134 [&]( const Pair &pair ) {
135 return msgid.Contains( pair.first ); }
136 );
137 if (found != end(helpURLTable)) {
138 auto url = found->second;
139 if (url[0] == '#')
140 helpUrl += url;
141 else
142 helpUrl = url;
143 }
144 }
145
146 return helpUrl;
147}
148
149}
150
152 const FilePath &fileName, bool discardAutosave )
154{
155 auto &project = mProject;
156 auto &projectFileIO = ProjectFileIO::Get( project );
157 auto &window = GetProjectFrame( project );
158
162 bool bParseSuccess = projectFileIO.LoadProject(fileName, discardAutosave);
163
164 bool err = false;
165
166 if (bParseSuccess)
167 {
168 if (discardAutosave)
169 // REVIEW: Failure OK?
170 projectFileIO.AutoSaveDelete();
171 else if (projectFileIO.IsRecovered()) {
172 bool resaved = false;
173
174 if (!projectFileIO.IsTemporary())
175 {
176 // Re-save non-temporary project to its own path. This
177 // might fail to update the document blob in the database.
178 resaved = projectFileIO.SaveProject(fileName, nullptr);
179 }
180
182 resaved
183 ? XO("This project was not saved properly the last time Audacity ran.\n\n"
184 "It has been recovered to the last snapshot.")
185 : XO("This project was not saved properly the last time Audacity ran.\n\n"
186 "It has been recovered to the last snapshot, but you must save it\n"
187 "to preserve its contents."),
188 XO("Project Recovered"),
189 wxICON_WARNING,
190 &window);
191 }
192
193 // By making a duplicate set of pointers to the existing blocks
194 // on disk, we add one to their reference count, guaranteeing
195 // that their reference counts will never reach zero and thus
196 // the version saved on disk will be preserved until the
197 // user selects Save().
198 mLastSavedTracks = TrackList::Create( nullptr );
199
200 auto &tracks = TrackList::Get( project );
201 for (auto t : tracks.Any())
202 {
203 if (t->GetErrorOpening())
204 {
205 wxLogWarning(
206 wxT("Track %s had error reading clip values from project file."),
207 t->GetName());
208 err = true;
209 }
210
211 err = ( !t->LinkConsistencyFix() ) || err;
212
213 mLastSavedTracks->Add(t->Duplicate());
214 }
215 }
216
217 return
218 {
219 bParseSuccess,
220 err,
221 projectFileIO.GetLastError(),
222 FindHelpUrl(projectFileIO.GetLibraryError())
223 };
224}
225
227{
228 auto &projectFileIO = ProjectFileIO::Get(mProject);
229
230 // Prompt for file name?
231 if (projectFileIO.IsTemporary())
232 {
233 return SaveAs(true);
234 }
235
236 return DoSave(projectFileIO.GetFileName(), false);
237}
238
239#if 0
240// I added this to "fix" bug #334. At that time, we were on wxWidgets 2.8.12 and
241// there was a window between the closing of the "Save" progress dialog and the
242// end of the actual save where the user was able to close the project window and
243// recursively enter the Save code (where they could inadvertently cause the issue
244// described in #334).
245//
246// When we converted to wx3, this "disabler" caused focus problems when returning
247// to the project after the save (bug #1172) because the focus and activate events
248// weren't being dispatched and the focus would get lost.
249//
250// After some testing, it looks like the window described above no longer exists,
251// so I've disabled the disabler. However, I'm leaving it here in case we run
252// into the problem in the future. (even though it can't be used as-is)
253class ProjectDisabler
254{
255public:
256 ProjectDisabler(wxWindow *w)
257 : mWindow(w)
258 {
259 mWindow->GetEventHandler()->SetEvtHandlerEnabled(false);
260 }
261 ~ProjectDisabler()
262 {
263 mWindow->GetEventHandler()->SetEvtHandlerEnabled(true);
264 }
265private:
266 wxWindow *mWindow;
267};
268#endif
269
270// Assumes ProjectFileIO::mFileName has been set to the desired path.
271bool ProjectFileManager::DoSave(const FilePath & fileName, const bool fromSaveAs)
272{
273 // See explanation above
274 // ProjectDisabler disabler(this);
275 auto &proj = mProject;
276 auto &window = GetProjectFrame( proj );
277 auto &projectFileIO = ProjectFileIO::Get( proj );
278 const auto &settings = ProjectSettings::Get( proj );
279
280 // Some confirmation dialogs
281 {
282 if (TempDirectory::FATFilesystemDenied(fileName, XO("Projects cannot be saved to FAT drives.")))
283 {
284 return false;
285 }
286
287 auto &tracks = TrackList::Get( proj );
288 if (!tracks.Any())
289 {
290 if (UndoManager::Get( proj ).UnsavedChanges() &&
291 settings.EmptyCanBeDirty())
292 {
293 int result = AudacityMessageBox(
294 XO(
295 "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?"),
296 XO("Warning - Empty Project"),
297 wxYES_NO | wxICON_QUESTION,
298 &window);
299 if (result == wxNO)
300 {
301 return false;
302 }
303 }
304 }
305
306 wxULongLong fileSize = wxFileName::GetSize(projectFileIO.GetFileName());
307
308 wxDiskspaceSize_t freeSpace;
309 if (wxGetDiskSpace(FileNames::AbbreviatePath(fileName), NULL, &freeSpace))
310 {
311 if (freeSpace.GetValue() <= fileSize.GetValue())
312 {
314 XO("Insufficient Disk Space"),
315 XO("The project size exceeds the available free space on the target disk.\n\n"
316 "Please select a different disk with more free space."),
317 "Error:_Disk_full_or_not_writable"
318 );
319
320 return false;
321 }
322 }
323 }
324 // End of confirmations
325
326 // Always save a backup of the original project file
327 std::optional<ProjectFileIO::BackupProject> pBackupProject;
328 if (fromSaveAs && wxFileExists(fileName))
329 {
330 pBackupProject.emplace(projectFileIO, fileName);
331 if (!pBackupProject->IsOk())
332 return false;
333 }
334
335 if (FileNames::IsOnFATFileSystem(fileName))
336 {
337 if (wxFileName::GetSize(projectFileIO.GetFileName()) > UINT32_MAX)
338 {
340 XO("Error Saving Project"),
341 XO("The project exceeds the maximum size of 4GB when writing to a FAT32 formatted filesystem."),
342 "Error:_Unsuitable_drive"
343 );
344 return false;
345 }
346 }
347
348 bool success = projectFileIO.SaveProject(fileName, mLastSavedTracks.get());
349 if (!success)
350 {
351 // Show this error only if we didn't fail reconnection in SaveProject
352 // REVIEW: Could HasConnection() be true but SaveProject() still have failed?
353 if (!projectFileIO.HasConnection()) {
354 using namespace BasicUI;
356 XO("Error Saving Project"),
358 "Error:_Disk_full_or_not_writable",
359 ErrorDialogOptions{ ErrorDialogType::ModalErrorReport } );
360 }
361 return false;
362 }
363
364 proj.SetProjectName(wxFileName(fileName).GetName());
365 projectFileIO.SetProjectTitle();
366
368 ProjectStatus::Get(proj).Set(XO("Saved %s").Format(fileName));
369
371 {
372 mLastSavedTracks->Clear();
373 }
375
376 auto &tracks = TrackList::Get(proj);
377 for (auto t : tracks.Any())
378 {
379 mLastSavedTracks->Add(t->Duplicate());
380 }
381
382 // If we get here, saving the project was successful, so we can DELETE
383 // any backup project.
384 if (pBackupProject)
385 pBackupProject->Discard();
386
387 return true;
388}
389
390// This version of SaveAs is invoked only from scripting and does not
391// prompt for a file name
392bool ProjectFileManager::SaveAs(const FilePath &newFileName, bool addToHistory /*= true*/)
393{
394 auto &project = mProject;
395 auto &projectFileIO = ProjectFileIO::Get( project );
396
397 auto oldFileName = projectFileIO.GetFileName();
398
399 bool bOwnsNewName = !projectFileIO.IsTemporary() && (oldFileName == newFileName);
400 //check to see if the NEW project file already exists.
401 //We should only overwrite it if this project already has the same name, where the user
402 //simply chose to use the save as command although the save command would have the effect.
403 if( !bOwnsNewName && wxFileExists(newFileName)) {
405 nullptr,
406 XO("The project was not saved because the file name provided would overwrite another project.\nPlease try again and select an original name."),
407 XO("Error Saving Project"),
408 wxOK|wxICON_ERROR );
409 m.ShowModal();
410 return false;
411 }
412
413 auto success = DoSave(newFileName, !bOwnsNewName);
414 if (success && addToHistory) {
415 FileHistory::Global().Append( projectFileIO.GetFileName() );
416 }
417
418 return(success);
419}
420
421bool ProjectFileManager::SaveAs(bool allowOverwrite /* = false */)
422{
423 auto &project = mProject;
424 auto &projectFileIO = ProjectFileIO::Get( project );
425 auto &window = GetProjectFrame( project );
426 TitleRestorer Restorer( window, project ); // RAII
427 wxFileName filename;
428 FilePath defaultSavePath = FileNames::FindDefaultPath(FileNames::Operation::Save);
429
430 if (projectFileIO.IsTemporary()) {
431 filename.SetPath(defaultSavePath);
432 filename.SetName(project.GetProjectName());
433 }
434 else {
435 filename = projectFileIO.GetFileName();
436 }
437
438 // Bug 1304: Set a default file path if none was given. For Save/SaveAs/SaveCopy
439 if( !FileNames::IsPathAvailable( filename.GetPath( wxPATH_GET_VOLUME| wxPATH_GET_SEPARATOR) ) ){
440 filename.SetPath(defaultSavePath);
441 }
442
443 TranslatableString title = XO("%sSave Project \"%s\" As...")
444 .Format( Restorer.sProjNumber, Restorer.sProjName );
445 TranslatableString message = XO("\
446'Save Project' is for an Audacity project, not an audio file.\n\
447For an audio file that will open in other apps, use 'Export'.\n");
448
449 if (ShowWarningDialog(&window, wxT("FirstProjectSave"), message, true) != wxID_OK) {
450 return false;
451 }
452
453 bool bPrompt = (project.mBatchMode == 0) || (projectFileIO.GetFileName().empty());
454 FilePath fName;
455 bool bOwnsNewName;
456
457 do {
458 if (bPrompt) {
459 // JKC: I removed 'wxFD_OVERWRITE_PROMPT' because we are checking
460 // for overwrite ourselves later, and we disallow it.
461 fName = SelectFile(FileNames::Operation::Save,
462 title,
463 filename.GetPath(),
464 filename.GetFullName(),
465 wxT("aup3"),
467 wxFD_SAVE | wxRESIZE_BORDER,
468 &window);
469
470 if (fName.empty())
471 return false;
472
473 filename = fName;
474 };
475
476 filename.SetExt(wxT("aup3"));
477
478 if ((!bPrompt || !allowOverwrite) && filename.FileExists()) {
479 // Saving a copy of the project should never overwrite an existing project.
481 nullptr,
482 XO("The project was not saved because the file name provided would overwrite another project.\nPlease try again and select an original name."),
483 XO("Error Saving Project"),
484 wxOK|wxICON_ERROR );
485 m.ShowModal();
486 return false;
487 }
488
489 fName = filename.GetFullPath();
490
491 bOwnsNewName = !projectFileIO.IsTemporary() && ( projectFileIO.GetFileName() == fName );
492 // Check to see if the project file already exists, and if it does
493 // check that the project file 'belongs' to this project.
494 // otherwise, prompt the user before overwriting.
495 if (!bOwnsNewName && filename.FileExists()) {
496 // Ensure that project of same name is not open in another window.
497 // fName is the destination file.
498 // mFileName is this project.
499 // It is possible for mFileName == fName even when this project is not
500 // saved to disk, and we then need to check the destination file is not
501 // open in another window.
502 int mayOverwrite = ( projectFileIO.GetFileName() == fName ) ? 2 : 1;
503 for ( auto p : AllProjects{} ) {
504 const wxFileName openProjectName{ ProjectFileIO::Get(*p).GetFileName() };
505 if (openProjectName.SameAs(fName)) {
506 mayOverwrite -= 1;
507 if (mayOverwrite == 0)
508 break;
509 }
510 }
511
512 if (mayOverwrite > 0) {
513 /* i18n-hint: In each case, %s is the name
514 of the file being overwritten.*/
515 auto Message = XO("\
516 Do you want to overwrite the project:\n\"%s\"?\n\n\
517 If you select \"Yes\" the project\n\"%s\"\n\
518 will be irreversibly overwritten.").Format( fName, fName );
519
520 // For safety, there should NOT be an option to hide this warning.
521 int result = AudacityMessageBox(
522 Message,
523 /* i18n-hint: Heading: A warning that a project is about to be overwritten.*/
524 XO("Overwrite Project Warning"),
525 wxYES_NO | wxNO_DEFAULT | wxICON_WARNING,
526 &window);
527 if (result == wxNO) {
528 continue;
529 }
530 if (result == wxCANCEL) {
531 return false;
532 }
533 }
534 else {
535 // Overwrite disallowed. The destination project is open in another window.
537 nullptr,
538 XO("The project was not saved because the selected project is open in another window.\nPlease try again and select an original name."),
539 XO("Error Saving Project"),
540 wxOK|wxICON_ERROR );
541 m.ShowModal();
542 continue;
543 }
544 }
545
546 break;
547 } while (bPrompt);
548
549
550 auto success = DoSave(fName, !bOwnsNewName);
551 if (success) {
552 FileHistory::Global().Append( projectFileIO.GetFileName() );
553 }
554
555 return(success);
556}
557
558bool ProjectFileManager::SaveCopy(const FilePath &fileName /* = wxT("") */)
559{
560 auto &project = mProject;
561 auto &projectFileIO = ProjectFileIO::Get(project);
562 auto &window = GetProjectFrame(project);
563 TitleRestorer Restorer(window, project); // RAII
564 wxFileName filename = fileName;
565 FilePath defaultSavePath = FileNames::FindDefaultPath(FileNames::Operation::Save);
566
567 if (fileName.empty())
568 {
569 if (projectFileIO.IsTemporary())
570 {
571 filename.SetPath(defaultSavePath);
572 }
573 else
574 {
575 filename = projectFileIO.GetFileName();
576 }
577 }
578
579 // Bug 1304: Set a default file path if none was given. For Save/SaveAs/SaveCopy
580 if (!FileNames::IsPathAvailable(filename.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR)))
581 {
582 filename.SetPath(defaultSavePath);
583 }
584
586 XO("%sSave Copy of Project \"%s\" As...")
587 .Format(Restorer.sProjNumber, Restorer.sProjName);
588
589 bool bPrompt = (project.mBatchMode == 0) || (projectFileIO.GetFileName().empty());
590 FilePath fName;
591
592 do
593 {
594 if (bPrompt)
595 {
596 // JKC: I removed 'wxFD_OVERWRITE_PROMPT' because we are checking
597 // for overwrite ourselves later, and we disallow it.
598 // Previously we disallowed overwrite because we would have had
599 // to DELETE the many smaller files too, or prompt to move them.
600 // Maybe we could allow it now that we have aup3 format?
601 fName = SelectFile(FileNames::Operation::Export,
602 title,
603 filename.GetPath(),
604 filename.GetFullName(),
605 wxT("aup3"),
607 wxFD_SAVE | wxRESIZE_BORDER,
608 &window);
609
610 if (fName.empty())
611 {
612 return false;
613 }
614
615 filename = fName;
616 };
617
618 filename.SetExt(wxT("aup3"));
619
620 if (TempDirectory::FATFilesystemDenied(filename.GetFullPath(), XO("Projects cannot be saved to FAT drives.")))
621 {
622 if (project.mBatchMode)
623 {
624 return false;
625 }
626
627 continue;
628 }
629
630 if (filename.FileExists())
631 {
632 // Saving a copy of the project should never overwrite an existing project.
633 AudacityMessageDialog m(nullptr,
634 XO("Saving a copy must not overwrite an existing saved project.\nPlease try again and select an original name."),
635 XO("Error Saving Copy of Project"),
636 wxOK | wxICON_ERROR);
637 m.ShowModal();
638
639 if (project.mBatchMode)
640 {
641 return false;
642 }
643
644 continue;
645 }
646
647 wxULongLong fileSize = wxFileName::GetSize(projectFileIO.GetFileName());
648
649 wxDiskspaceSize_t freeSpace;
650 if (wxGetDiskSpace(FileNames::AbbreviatePath(filename.GetFullPath()), NULL, &freeSpace))
651 {
652 if (freeSpace.GetValue() <= fileSize.GetValue())
653 {
655 XO("Insufficient Disk Space"),
656 XO("The project size exceeds the available free space on the target disk.\n\n"
657 "Please select a different disk with more free space."),
658 "Error:_Unsuitable_drive"
659 );
660
661 continue;
662 }
663 }
664
665 if (FileNames::IsOnFATFileSystem(filename.GetFullPath()))
666 {
667 if (fileSize > UINT32_MAX)
668 {
670 XO("Error Saving Project"),
671 XO("The project exceeds the maximum size of 4GB when writing to a FAT32 formatted filesystem."),
672 "Error:_Unsuitable_drive"
673 );
674
675 if (project.mBatchMode)
676 {
677 return false;
678 }
679
680 continue;
681 }
682 }
683
684 fName = filename.GetFullPath();
685 break;
686 } while (bPrompt);
687
688 if (!projectFileIO.SaveCopy(fName))
689 {
690 auto msg = FileException::WriteFailureMessage(fName);
692 nullptr, msg, XO("Error Saving Project"), wxOK | wxICON_ERROR);
693
694 m.ShowModal();
695
696 return false;
697 }
698
699 return true;
700}
701
703{
704 auto &project = mProject;
705 auto &projectFileIO = ProjectFileIO::Get( project );
706
707 // MY: Will save the project to a NEW location a-la Save As
708 // and then tidy up after itself.
709
710 wxString sNewFileName = fnFile.GetFullPath();
711
712 // MY: To allow SaveAs from Timer Recording we need to check what
713 // the value of mFileName is before we change it.
714 FilePath sOldFilename;
715 if (!projectFileIO.IsModified()) {
716 sOldFilename = projectFileIO.GetFileName();
717 }
718
719 // MY: If the project file already exists then bail out
720 // and send populate the message string (pointer) so
721 // we can tell the user what went wrong.
722 if (wxFileExists(sNewFileName)) {
723 return false;
724 }
725
726 auto success = DoSave(sNewFileName, true);
727
728 if (success) {
729 FileHistory::Global().Append( projectFileIO.GetFileName() );
730 }
731
732 return success;
733}
734
736{
737 auto &project = mProject;
738 auto &projectFileIO = ProjectFileIO::Get(project);
739
740 // Lock all blocks in all tracks of the last saved version, so that
741 // the sample blocks aren't deleted from the database when we destroy the
742 // sample block objects in memory.
744 {
745 for (auto wt : mLastSavedTracks->Any<WaveTrack>())
746 {
747 wt->CloseLock();
748 }
749
750 // Attempt to compact the project
751 projectFileIO.Compact( { mLastSavedTracks.get() } );
752
753 if ( !projectFileIO.WasCompacted() &&
754 UndoManager::Get( project ).UnsavedChanges() ) {
755 // If compaction failed, we must do some work in case of close
756 // without save. Don't leave the document blob from the last
757 // push of undo history, when that undo state may get purged
758 // with deletion of some new sample blocks.
759 // REVIEW: UpdateSaved() might fail too. Do we need to test
760 // for that and report it?
761 projectFileIO.UpdateSaved( mLastSavedTracks.get() );
762 }
763 }
764}
765
767{
768 auto &project = mProject;
769 auto &projectFileIO = ProjectFileIO::Get(project);
770
771 return projectFileIO.OpenProject();
772}
773
775{
776 auto &project = mProject;
777 auto &projectFileIO = ProjectFileIO::Get(project);
778
779 bool bOK = OpenProject();
780 if( !bOK )
781 {
782 auto tmpdir = wxFileName(TempDirectory::UnsavedProjectFileName()).GetPath();
783
784 UnwritableLocationErrorDialog dlg(nullptr, tmpdir);
785 dlg.ShowModal();
786 }
787 return bOK;
788}
789
791{
792 auto &project = mProject;
793 auto &projectFileIO = ProjectFileIO::Get(project);
794
795 projectFileIO.CloseProject();
796
797 // Blocks were locked in CompactProjectOnClose, so DELETE the data structure so that
798 // there's no memory leak.
800 {
801 mLastSavedTracks->Clear();
802 mLastSavedTracks.reset();
803 }
804}
805
806// static method, can be called outside of a project
808 const FileNames::FileType &extraType )
809{
810 // Construct the filter
811 const auto fileTypes = Importer::Get().GetFileTypes( extraType );
812
813 // Retrieve saved path
814 auto path = FileNames::FindDefaultPath(op);
815
816 // Construct and display the file dialog
817 wxArrayString selected;
818
819 FileDialogWrapper dlog(nullptr,
820 XO("Select one or more files"),
821 path,
822 wxT(""),
823 fileTypes,
824 wxFD_OPEN | wxFD_MULTIPLE | wxRESIZE_BORDER);
825
827
828 int dialogResult = dlog.ShowModal();
829
830 // Convert the filter index to type and save
831 auto index = dlog.GetFilterIndex();
832 const auto &saveType = fileTypes[ index ];
833
835 Importer::SetLastOpenType( saveType );
836
837 if (dialogResult == wxID_OK) {
838 // Return the selected files
839 dlog.GetPaths(selected);
840
841 // Remember the directory
842 FileNames::UpdateDefaultPath(op, ::wxPathOnly(dlog.GetPath()));
843 }
844
845 return selected;
846}
847
848// static method, can be called outside of a project
850{
851 const wxFileName newProjPathName(projPathName);
852 auto start = AllProjects{}.begin(), finish = AllProjects{}.end(),
853 iter = std::find_if( start, finish,
854 [&]( const AllProjects::value_type &ptr ){
855 return newProjPathName.SameAs(wxFileNameWrapper{ ProjectFileIO::Get(*ptr).GetFileName() });
856 } );
857 if (iter != finish) {
858 auto errMsg =
859 XO("%s is already open in another window.")
860 .Format( newProjPathName.GetName() );
861 wxLogError(errMsg.Translation()); //Debug?
863 errMsg,
864 XO("Error Opening Project"),
865 wxOK | wxCENTRE);
866 return true;
867 }
868 return false;
869}
870
872 const FilePath &fileNameArg, bool addtohistory)
873{
874 // On Win32, we may be given a short (DOS-compatible) file name on rare
875 // occasions (e.g. stuff like "C:\PROGRA~1\AUDACI~1\PROJEC~1.AUP"). We
876 // convert these to long file name first.
877 auto fileName = PlatformCompatibility::GetLongFileName(fileNameArg);
878
879 // Make sure it isn't already open.
880 // Vaughan, 2011-03-25: This was done previously in AudacityProject::OpenFiles()
881 // and AudacityApp::MRUOpen(), but if you open an aup file by double-clicking it
882 // from, e.g., Win Explorer, it would bypass those, get to here with no check,
883 // then open a NEW project from the same data with no warning.
884 // This was reported in http://bugzilla.audacityteam.org/show_bug.cgi?id=137#c17,
885 // but is not really part of that bug. Anyway, prevent it!
886 if (IsAlreadyOpen(fileName))
887 return nullptr;
888
889 // Data loss may occur if users mistakenly try to open ".aup3.bak" files
890 // left over from an unsuccessful save or by previous versions of Audacity.
891 // So we always refuse to open such files.
892 if (fileName.Lower().EndsWith(wxT(".aup3.bak")))
893 {
895 XO(
896"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."),
897 XO("Warning - Backup File Detected"),
898 wxOK | wxCENTRE,
899 nullptr);
900 return nullptr;
901 }
902
903 if (!::wxFileExists(fileName)) {
905 XO("Could not open file: %s").Format( fileName ),
906 XO("Error Opening File"),
907 wxOK | wxCENTRE,
908 nullptr);
909 return nullptr;
910 }
911
912 // Following block covers cases other than a project file:
913 {
914 wxFFile ff(fileName, wxT("rb"));
915
916 auto cleanup = finally([&]
917 {
918 if (ff.IsOpened())
919 {
920 ff.Close();
921 }
922 });
923
924 if (!ff.IsOpened()) {
926 XO("Could not open file: %s").Format( fileName ),
927 XO("Error opening file"),
928 wxOK | wxCENTRE,
929 nullptr);
930 return nullptr;
931 }
932
933 char buf[7];
934 auto numRead = ff.Read(buf, 6);
935 if (numRead != 6) {
937 XO("File may be invalid or corrupted: \n%s").Format( fileName ),
938 XO("Error Opening File or Project"),
939 wxOK | wxCENTRE,
940 nullptr);
941 return nullptr;
942 }
943
944 if (wxStrncmp(buf, "SQLite", 6) != 0)
945 {
946 // Not a database
947#ifdef EXPERIMENTAL_DRAG_DROP_PLUG_INS
948 // Is it a plug-in?
949 if (PluginManager::Get().DropFile(fileName)) {
951 // Plug-in installation happened, not really opening of a file,
952 // so return null
953 return nullptr;
954 }
955#endif
956#ifdef USE_MIDI
957 if (FileNames::IsMidi(fileName)) {
958 auto &project = chooser(false);
959 // If this succeeds, indo history is incremented, and it also does
960 // ZoomAfterImport:
961 if(DoImportMIDI(project, fileName))
962 return &project;
963 return nullptr;
964 }
965#endif
966 auto &project = chooser(false);
967 // Undo history is incremented inside this:
968 if (Get(project).Import(fileName)) {
969 // Undo history is incremented inside this:
970 // Bug 2743: Don't zoom with lof.
971 if (!fileName.AfterLast('.').IsSameAs(wxT("lof"), false))
972 ProjectWindow::Get(project).ZoomAfterImport(nullptr);
973 return &project;
974 }
975 return nullptr;
976 }
977 }
978
979 // Disallow opening of .aup3 project files from FAT drives, but only such
980 // files, not importable types. (Bug 2800)
982 XO("Project resides on FAT formatted drive.\n"
983 "Copy it to another drive to open it.")))
984 {
985 return nullptr;
986 }
987
988 auto &project = chooser(true);
989 return Get(project).OpenProjectFile(fileName, addtohistory);
990}
991
993 const FilePath &fileName, bool addtohistory)
994{
995 auto &project = mProject;
996 auto &history = ProjectHistory::Get( project );
997 auto &tracks = TrackList::Get( project );
998 auto &trackPanel = TrackPanel::Get( project );
999 auto &projectFileIO = ProjectFileIO::Get( project );
1000 auto &window = ProjectWindow::Get( project );
1001
1002 auto results = ReadProjectFile( fileName );
1003 const bool bParseSuccess = results.parseSuccess;
1004 const auto &errorStr = results.errorString;
1005 const bool err = results.trackError;
1006
1007 if (bParseSuccess) {
1008 auto &settings = ProjectSettings::Get( project );
1009 window.mbInitializingScrollbar = true; // this must precede AS_SetSnapTo
1010 // to make persistence of the vertical scrollbar position work
1011
1012 auto &selectionManager = ProjectSelectionManager::Get( project );
1013 selectionManager.AS_SetSnapTo(settings.GetSnapTo());
1014 selectionManager.AS_SetSelectionFormat(settings.GetSelectionFormat());
1015 selectionManager.TT_SetAudioTimeFormat(settings.GetAudioTimeFormat());
1016 selectionManager.SSBL_SetFrequencySelectionFormatName(
1017 settings.GetFrequencySelectionFormatName());
1018 selectionManager.SSBL_SetBandwidthSelectionFormatName(
1019 settings.GetBandwidthSelectionFormatName());
1020
1021 SelectionBar::Get( project )
1022 .SetRate( ProjectRate::Get(project).GetRate() );
1023
1024 ProjectHistory::Get( project ).InitialState();
1025 TrackFocus::Get( project ).Set( *tracks.Any().begin() );
1026 window.HandleResize();
1027 trackPanel.Refresh(false);
1028
1029 // ? Old rationale in this comment no longer applies in 3.0.0, with no
1030 // more on-demand loading:
1031 trackPanel.Update(); // force any repaint to happen now,
1032 // else any asynch calls into the blockfile code will not have
1033 // finished logging errors (if any) before the call to ProjectFSCK()
1034
1035 if (addtohistory)
1036 FileHistory::Global().Append(fileName);
1037 }
1038
1039 if (bParseSuccess) {
1040 if (projectFileIO.IsRecovered())
1041 {
1042 // PushState calls AutoSave(), so no longer need to do so here.
1043 history.PushState(XO("Project was recovered"), XO("Recover"));
1044 }
1045 return &project;
1046 }
1047 else {
1048 // Vaughan, 2011-10-30:
1049 // See first topic at http://bugzilla.audacityteam.org/show_bug.cgi?id=451#c16.
1050 // Calling mTracks->Clear() with deleteTracks true results in data loss.
1051
1052 // PRL 2014-12-19:
1053 // I made many changes for wave track memory management, but only now
1054 // read the above comment. I may have invalidated the fix above (which
1055 // may have spared the files at the expense of leaked memory). But
1056 // here is a better way to accomplish the intent, doing like what happens
1057 // when the project closes:
1058 for ( auto pTrack : tracks.Any< WaveTrack >() )
1059 pTrack->CloseLock();
1060
1061 tracks.Clear(); //tracks.Clear(true);
1062
1063 wxLogError(wxT("Could not parse file \"%s\". \nError: %s"), fileName, errorStr.Debug());
1064
1065 projectFileIO.ShowError( *ProjectFramePlacement(&project),
1066 XO("Error Opening Project"),
1067 errorStr,
1068 results.helpUrl);
1069
1070 return nullptr;
1071 }
1072}
1073
1074void
1076 TrackHolders &&newTracks)
1077{
1078 auto &project = mProject;
1079 auto &history = ProjectHistory::Get( project );
1080 auto &projectFileIO = ProjectFileIO::Get( project );
1081 auto &tracks = TrackList::Get( project );
1082
1083 std::vector< std::shared_ptr< Track > > results;
1084
1085 SelectUtilities::SelectNone( project );
1086
1087 wxFileName fn(fileName);
1088
1089 bool initiallyEmpty = tracks.empty();
1090 double newRate = 0;
1091 wxString trackNameBase = fn.GetName();
1092 int i = -1;
1093
1094 // Fix the bug 2109.
1095 // In case the project had soloed tracks before importing,
1096 // all newly imported tracks are muted.
1097 const bool projectHasSolo =
1098 !(tracks.Any<PlayableTrack>() + &PlayableTrack::GetSolo).empty();
1099 if (projectHasSolo)
1100 {
1101 for (auto& track : newTracks)
1102 for (auto& channel : track)
1103 channel->SetMute(true);
1104 }
1105
1106 // Must add all tracks first (before using Track::IsLeader)
1107 for (auto &group : newTracks) {
1108 if (group.empty()) {
1109 wxASSERT(false);
1110 continue;
1111 }
1112 auto first = group.begin()->get();
1113 auto nChannels = group.size();
1114 for (auto &uNewTrack : group) {
1115 auto newTrack = tracks.Add( uNewTrack );
1116 results.push_back(newTrack->SharedPointer());
1117 }
1118 tracks.MakeMultiChannelTrack(*first, nChannels, true);
1119 }
1120 newTracks.clear();
1121
1122 // Now name them
1123
1124 // Add numbers to track names only if there is more than one (mono or stereo)
1125 // track (not necessarily, more than one channel)
1126 const bool useSuffix =
1127 make_iterator_range( results.begin() + 1, results.end() )
1128 .any_of( []( decltype(*results.begin()) &pTrack )
1129 { return pTrack->IsLeader(); } );
1130
1131 for (const auto &newTrack : results) {
1132 if ( newTrack->IsLeader() )
1133 // Count groups only
1134 ++i;
1135
1136 newTrack->SetSelected(true);
1137
1138
1139 if (useSuffix)
1140 //i18n-hint Name default name assigned to a clip on track import
1141 newTrack->SetName(XC("%s %d", "clip name template").Format(trackNameBase, i + 1).Translation());
1142 else
1143 newTrack->SetName(trackNameBase);
1144
1145 newTrack->TypeSwitch([&](WaveTrack *wt) {
1146 if (newRate == 0)
1147 newRate = wt->GetRate();
1148 auto trackName = wt->GetName();
1149 for (auto& clip : wt->GetClips())
1150 clip->SetName(trackName);
1151 });
1152 }
1153
1154 // Automatically assign rate of imported file to whole project,
1155 // if this is the first file that is imported
1156 if (initiallyEmpty && newRate > 0) {
1157 ProjectRate::Get(project).SetRate( newRate );
1158 SelectionBar::Get( project ).SetRate( newRate );
1159 }
1160
1161 history.PushState(XO("Imported '%s'").Format( fileName ),
1162 XO("Import"));
1163
1164#if defined(__WXGTK__)
1165 // See bug #1224
1166 // The track panel hasn't we been fully created, so the DoZoomFit() will not give
1167 // expected results due to a window width of zero. Should be safe to yield here to
1168 // allow the creation to complete. If this becomes a problem, it "might" be possible
1169 // to queue a dummy event to trigger the DoZoomFit().
1170 wxEventLoopBase::GetActive()->YieldFor(wxEVT_CATEGORY_UI | wxEVT_CATEGORY_USER_INPUT);
1171#endif
1172
1173 // If the project was clean and temporary (not permanently saved), then set
1174 // the filename to the just imported path.
1175 if (initiallyEmpty && projectFileIO.IsTemporary()) {
1176 project.SetProjectName(fn.GetName());
1177 project.SetInitialImportPath(fn.GetPath());
1178 projectFileIO.SetProjectTitle();
1179 }
1180
1181 // Moved this call to higher levels to prevent flicker redrawing everything on each file.
1182 // HandleResize();
1183}
1184
1185namespace {
1186bool ImportProject(AudacityProject &dest, const FilePath &fileName)
1187{
1189 auto &project = temp.Project();
1190
1191 auto &projectFileIO = ProjectFileIO::Get(project);
1192 if (!projectFileIO.LoadProject(fileName, false))
1193 return false;
1194 auto &srcTracks = TrackList::Get(project);
1195 auto &destTracks = TrackList::Get(dest);
1196 for (const Track *pTrack : srcTracks.Any()) {
1197 auto destTrack = pTrack->PasteInto(dest);
1198 Track::FinishCopy(pTrack, destTrack.get());
1199 if (destTrack.use_count() == 1)
1200 destTracks.Add(destTrack);
1201 }
1202 Tags::Get(dest).Merge(Tags::Get(project));
1203
1204 return true;
1205}
1206}
1207
1208// If pNewTrackList is passed in non-NULL, it gets filled with the pointers to NEW tracks.
1210 const FilePath &fileName,
1211 bool addToHistory /* = true */)
1212{
1213 auto &project = mProject;
1214 auto &projectFileIO = ProjectFileIO::Get(project);
1215 auto oldTags = Tags::Get( project ).shared_from_this();
1216 bool initiallyEmpty = TrackList::Get(project).empty();
1217 TrackHolders newTracks;
1218 TranslatableString errorMessage;
1219
1220#ifdef EXPERIMENTAL_IMPORT_AUP3
1221 // Handle AUP3 ("project") files directly
1222 if (fileName.AfterLast('.').IsSameAs(wxT("aup3"), false)) {
1223 if (ImportProject(project, fileName)) {
1224 auto &history = ProjectHistory::Get(project);
1225
1226 // If the project was clean and temporary (not permanently saved), then set
1227 // the filename to the just imported path.
1228 if (initiallyEmpty && projectFileIO.IsTemporary()) {
1229 wxFileName fn(fileName);
1230 project.SetProjectName(fn.GetName());
1231 project.SetInitialImportPath(fn.GetPath());
1232 projectFileIO.SetProjectTitle();
1233 }
1234
1235 history.PushState(XO("Imported '%s'").Format(fileName), XO("Import"));
1236
1237 if (addToHistory) {
1238 FileHistory::Global().Append(fileName);
1239 }
1240 }
1241 else {
1242 errorMessage = projectFileIO.GetLastError();
1243 if (errorMessage.empty()) {
1244 errorMessage = XO("Failed to import project");
1245 }
1246
1247 // Additional help via a Help button links to the manual.
1249 XO("Error Importing"),
1250 errorMessage, wxT("Importing_Audio"));
1251 }
1252
1253 return false;
1254 }
1255#endif
1256
1257 {
1258 // Backup Tags, before the import. Be prepared to roll back changes.
1259 bool committed = false;
1260 auto cleanup = finally([&]{
1261 if ( !committed )
1262 Tags::Set( project, oldTags );
1263 });
1264 auto newTags = oldTags->Duplicate();
1265 Tags::Set( project, newTags );
1266
1267#ifndef EXPERIMENTAL_IMPORT_AUP3
1268 // Handle AUP3 ("project") files specially
1269 if (fileName.AfterLast('.').IsSameAs(wxT("aup3"), false)) {
1271 XO("Error Importing"),
1272 XO( "Cannot import AUP3 format. Use File > Open instead"),
1273 wxT("File_Menu"));
1274 return false;
1275 }
1276#endif
1277 bool success = Importer::Get().Import(project, fileName,
1278 &WaveTrackFactory::Get( project ),
1279 newTracks,
1280 newTags.get(),
1281 errorMessage);
1282 if (!errorMessage.empty()) {
1283 // Error message derived from Importer::Import
1284 // Additional help via a Help button links to the manual.
1286 XO("Error Importing"), errorMessage, wxT("Importing_Audio"));
1287 }
1288 if (!success)
1289 return false;
1290
1291 if (addToHistory) {
1292 FileHistory::Global().Append(fileName);
1293 }
1294
1295 // no more errors, commit
1296 committed = true;
1297 }
1298
1299 // for LOF ("list of files") files, do not import the file as if it
1300 // were an audio file itself
1301 if (fileName.AfterLast('.').IsSameAs(wxT("lof"), false)) {
1302 // PRL: don't redundantly do the steps below, because we already
1303 // did it in case of LOF, because of some weird recursion back to this
1304 // same function. I think this should be untangled.
1305
1306 // So Undo history push is not bypassed, despite appearances.
1307 return false;
1308 }
1309
1310 // Handle AUP ("legacy project") files directly
1311 if (fileName.AfterLast('.').IsSameAs(wxT("aup"), false)) {
1312 // If the project was clean and temporary (not permanently saved), then set
1313 // the filename to the just imported path.
1314 if (initiallyEmpty && projectFileIO.IsTemporary()) {
1315 wxFileName fn(fileName);
1316 project.SetProjectName(fn.GetName());
1317 project.SetInitialImportPath(fn.GetPath());
1318 projectFileIO.SetProjectTitle();
1319 }
1320
1321 auto &history = ProjectHistory::Get( project );
1322
1323 history.PushState(XO("Imported '%s'").Format( fileName ), XO("Import"));
1324
1325 return true;
1326 }
1327
1328 // PRL: Undo history is incremented inside this:
1329 AddImportedTracks(fileName, std::move(newTracks));
1330
1331 return true;
1332}
1333
1334#include "Clipboard.h"
1335#include "ShuttleGui.h"
1336#include "widgets/HelpSystem.h"
1337
1338// Compact dialog
1339namespace {
1341{
1342public:
1344 : wxDialogWrapper(nullptr, wxID_ANY, XO("Compact Project"))
1345 {
1346 ShuttleGui S(this, eIsCreating);
1347
1348 S.StartVerticalLay(true);
1349 {
1350 S.AddFixedText(text, false, 500);
1351
1352 S.AddStandardButtons(eYesButton | eNoButton | eHelpButton);
1353 }
1354 S.EndVerticalLay();
1355
1356 FindWindowById(wxID_YES, this)->Bind(wxEVT_BUTTON, &CompactDialog::OnYes, this);
1357 FindWindowById(wxID_NO, this)->Bind(wxEVT_BUTTON, &CompactDialog::OnNo, this);
1358 FindWindowById(wxID_HELP, this)->Bind(wxEVT_BUTTON, &CompactDialog::OnGetURL, this);
1359
1360 Layout();
1361 Fit();
1362 Center();
1363 }
1364
1365 void OnYes(wxCommandEvent &WXUNUSED(evt))
1366 {
1367 EndModal(wxYES);
1368 }
1369
1370 void OnNo(wxCommandEvent &WXUNUSED(evt))
1371 {
1372 EndModal(wxNO);
1373 }
1374
1375 void OnGetURL(wxCommandEvent &WXUNUSED(evt))
1376 {
1377 HelpSystem::ShowHelp(this, L"File_Menu:_Compact_Project", true);
1378 }
1379};
1380}
1381
1383{
1384 auto &project = mProject;
1385 auto &undoManager = UndoManager::Get(project);
1386 auto &clipboard = Clipboard::Get();
1387 auto &projectFileIO = ProjectFileIO::Get(project);
1388 bool isBatch = project.mBatchMode > 0;
1389
1390 // Purpose of this is to remove the -wal file.
1391 projectFileIO.ReopenProject();
1392
1393 auto savedState = undoManager.GetSavedState();
1394 const auto currentState = undoManager.GetCurrentState();
1395 if (savedState < 0) {
1396 undoManager.StateSaved();
1397 savedState = undoManager.GetSavedState();
1398 if (savedState < 0) {
1399 wxASSERT(false);
1400 savedState = 0;
1401 }
1402 }
1403 const auto least = std::min<size_t>(savedState, currentState);
1404 const auto greatest = std::max<size_t>(savedState, currentState);
1405 std::vector<const TrackList*> trackLists;
1406 auto fn = [&](auto& elem){
1407 trackLists.push_back(elem.state.tracks.get()); };
1408 undoManager.VisitStates(fn, least, 1 + least);
1409 if (least != greatest)
1410 undoManager.VisitStates(fn, greatest, 1 + greatest);
1411
1412 int64_t total = projectFileIO.GetTotalUsage();
1413 int64_t used = projectFileIO.GetCurrentUsage(trackLists);
1414
1415 auto before = wxFileName::GetSize(projectFileIO.GetFileName());
1416
1417 CompactDialog dlg(
1418 XO("Compacting this project will free up disk space by removing unused bytes within the file.\n\n"
1419 "There is %s of free disk space and this project is currently using %s.\n"
1420 "\n"
1421 "If you proceed, the current Undo/Redo History and clipboard contents will be discarded "
1422 "and you will recover approximately %s of disk space.\n"
1423 "\n"
1424 "Do you want to continue?")
1425 .Format(Internat::FormatSize(projectFileIO.GetFreeDiskSpace()),
1426 Internat::FormatSize(before.GetValue()),
1427 Internat::FormatSize(total - used)));
1428 if (isBatch || dlg.ShowModal() == wxYES)
1429 {
1430 // We can remove redo states, if they are after the saved state.
1431 undoManager.RemoveStates(1 + greatest, undoManager.GetNumStates());
1432
1433 // We can remove all states between the current and the last saved.
1434 if (least < greatest)
1435 undoManager.RemoveStates(least + 1, greatest);
1436
1437 // We can remove all states before the current and the last saved.
1438 undoManager.RemoveStates(0, least);
1439
1440 // And clear the clipboard, if needed
1441 if (&mProject == clipboard.Project().lock().get())
1442 clipboard.Clear();
1443
1444 // Refresh the before space usage since it may have changed due to the
1445 // above actions.
1446 auto before = wxFileName::GetSize(projectFileIO.GetFileName());
1447
1448 projectFileIO.Compact(trackLists, true);
1449
1450 auto after = wxFileName::GetSize(projectFileIO.GetFileName());
1451
1452 if (!isBatch)
1453 {
1455 XO("Compacting actually freed %s of disk space.")
1456 .Format(Internat::FormatSize((before - after).GetValue())),
1457 XO("Compact Project"));
1458 }
1459
1460 undoManager.RenameState( undoManager.GetCurrentState(),
1461 XO("Compacted project file"),
1462 XO("Compact") );
1463 }
1464}
wxT("CloseDown"))
int AudacityMessageBox(const TranslatableString &message, const TranslatableString &caption, long style, wxWindow *parent, int x, int y)
Toolkit-neutral facade for basic user interface services.
Declare functions to perform UTF-8 to std::wstring conversions.
std::vector< std::vector< std::shared_ptr< WaveTrack > > > TrackHolders
Definition: Import.h:39
bool DoImportMIDI(AudacityProject &project, const FilePath &fileName)
Definition: ImportMIDI.cpp:37
#define XO(s)
Definition: Internat.h:31
#define XC(s, c)
Definition: Internat.h:37
IteratorRange< Iterator > make_iterator_range(const Iterator &i1, const Iterator &i2)
Definition: MemoryX.h:431
static const auto title
wxString FilePath
Definition: Project.h:20
static const AudacityProject::AttachedObjects::RegisteredFactory sFileManagerKey
an object holding per-project preferred sample rate
std::unique_ptr< const BasicUI::WindowPlacement > ProjectFramePlacement(AudacityProject *project)
Make a WindowPlacement object suitable for project (which may be null)
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 ...
accessors for certain important windows associated with each project
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
@ eIsCreating
Definition: ShuttleGui.h:39
@ eYesButton
Definition: ShuttleGui.h:599
@ eHelpButton
Definition: ShuttleGui.h:601
@ eNoButton
Definition: ShuttleGui.h:600
for(int ii=0, nn=names.size();ii< nn;++ii)
#define S(N)
Definition: ToChars.cpp:64
static Settings & settings()
Definition: TrackInfo.cpp:87
int ShowWarningDialog(wxWindow *parent, const wxString &internalDialogName, const TranslatableString &message, bool showCancelButton, const TranslatableString &footer)
Definition: Warning.cpp:92
static const auto fn
const_iterator end() const
Definition: Project.cpp:27
Container::value_type value_type
Definition: Project.h:56
const_iterator begin() const
Definition: Project.cpp:22
Wrap wxMessageDialog so that caption IS translatable.
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:89
Client code makes static instance from a factory of attachments; passes it to Get or Find as a retrie...
Definition: ClientData.h:266
static Clipboard & Get()
Definition: Clipboard.cpp:29
virtual int GetFilterIndex() const
virtual wxString GetPath() const
virtual void GetPaths(wxArrayString &paths) const
virtual void SetFilterIndex(int filterIndex)
virtual int ShowModal()
static TranslatableString WriteFailureMessage(const wxFileName &fileName)
void Append(const FilePath &file)
Definition: FileHistory.h:42
static FileHistory & Global()
Definition: FileHistory.cpp:37
FILES_API const FileType AudacityProjects
Definition: FileNames.h:72
Abstract base class used in importing a file.
static void ShowHelp(wxWindow *parent, const FilePath &localFileName, const URLString &remoteURL, bool bModal=false, bool alwaysDefaultBrowser=false)
Definition: HelpSystem.cpp:239
const wxString & GET() const
Explicit conversion to wxString, meant to be ugly-looking and demanding of a comment why it's correct...
Definition: Identifier.h:66
static Importer & Get()
Definition: Import.cpp:71
static void SetLastOpenType(const FileNames::FileType &type)
Definition: Import.cpp:224
FileNames::FileTypes GetFileTypes(const FileNames::FileType &extraType={})
Definition: Import.cpp:179
bool Import(AudacityProject &project, const FilePath &fName, WaveTrackFactory *trackFactory, TrackHolders &tracks, Tags *tags, TranslatableString &errorMessage)
Definition: Import.cpp:457
static size_t SelectDefaultOpenType(const FileNames::FileTypes &fileTypes)
Definition: Import.cpp:244
static void SetDefaultOpenType(const FileNames::FileType &type)
Definition: Import.cpp:234
static TranslatableString FormatSize(wxLongLong size)
Convert a number to a string while formatting it in bytes, KB, MB, GB.
Definition: Internat.cpp:204
Makes a temporary project that doesn't display on the screen.
AudacityProject & Project()
static void RebuildAllMenuBars()
Definition: Menus.cpp:687
static FilePath GetLongFileName(const FilePath &shortFileName)
AudioTrack subclass that can also be audibly replayed by the program.
Definition: Track.h:916
bool GetSolo() const
Definition: Track.h:924
static PluginManager & Get()
static ProjectFileIO & Get(AudacityProject &project)
const FilePath & GetFileName() const
std::shared_ptr< TrackList > mLastSavedTracks
static bool IsAlreadyOpen(const FilePath &projPathName)
AudacityProject & mProject
bool Import(const FilePath &fileName, bool addToHistory=true)
bool SaveCopy(const FilePath &fileName=wxT(""))
bool SaveAs(bool allowOverwrite=false)
static void DiscardAutosave(const FilePath &filename)
void AddImportedTracks(const FilePath &fileName, TrackHolders &&newTracks)
static wxArrayString ShowOpenDialog(FileNames::Operation op, const FileNames::FileType &extraType={})
Show an open dialogue for opening audio files, and possibly other sorts of files.
bool DoSave(const FilePath &fileName, bool fromSaveAs)
bool SaveFromTimerRecording(wxFileName fnFile)
static AudacityProject * OpenFile(const ProjectChooserFn &chooser, const FilePath &fileName, bool addtohistory=true)
ReadProjectResults ReadProjectFile(const FilePath &fileName, bool discardAutosave=false)
std::function< AudacityProject &(bool)> ProjectChooserFn
A function that returns a project to use for opening a file; argument is true if opening a project fi...
static ProjectFileManager & Get(AudacityProject &project)
ProjectFileManager(AudacityProject &project)
AudacityProject * OpenProjectFile(const FilePath &fileName, bool addtohistory)
static ProjectHistory & Get(AudacityProject &project)
static ProjectRate & Get(AudacityProject &project)
Definition: ProjectRate.cpp:28
void SetRate(double rate)
Definition: ProjectRate.cpp:58
static ProjectSelectionManager & Get(AudacityProject &project)
static ProjectSettings & Get(AudacityProject &project)
static ProjectStatus & Get(AudacityProject &project)
void Set(const TranslatableString &msg, StatusBarField field=mainStatusBarField)
void ZoomAfterImport(Track *pTrack)
static ProjectWindow & Get(AudacityProject &project)
void SetRate(double rate)
static SelectionBar & Get(AudacityProject &project)
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
Definition: ShuttleGui.h:628
void Merge(const Tags &other)
Definition: Tags.cpp:246
static Tags & Get(AudacityProject &project)
Definition: Tags.cpp:214
static Tags & Set(AudacityProject &project, const std::shared_ptr< Tags > &tags)
Definition: Tags.cpp:224
wxString sProjName
wxString sProjNumber
Track * Get()
Abstract base class for an object holding data associated with points on a time axis.
Definition: Track.h:225
static void FinishCopy(const Track *n, Track *dest)
Definition: Track.cpp:414
wxString GetName() const
Definition: Track.h:466
bool Any() const
Definition: Track.cpp:399
bool empty() const
Definition: Track.cpp:1009
static std::shared_ptr< TrackList > Create(AudacityProject *pOwner)
Definition: Track.cpp:502
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:486
static TrackPanel & Get(AudacityProject &project)
Definition: TrackPanel.cpp:230
Holds a msgid for the translation catalog; may also bind format arguments.
Identifier MSGID() const
MSGID is the English lookup key in the catalog, not necessarily for user's eyes if locale is some oth...
static UndoManager & Get(AudacityProject &project)
Definition: UndoManager.cpp:67
void StateSaved()
bool UnsavedChanges() const
An error dialog about unwritable location, that allows to navigate to settings quickly.
static WaveTrackFactory & Get(AudacityProject &project)
Definition: WaveTrack.cpp:2810
A Track that contains audio waveform data.
Definition: WaveTrack.h:57
double GetRate() const override
Definition: WaveTrack.cpp:479
WaveClipHolders & GetClips()
Definition: WaveTrack.h:329
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:254
FILES_API bool IsMidi(const FilePath &fName)
FILES_API bool IsOnFATFileSystem(const FilePath &path)
FILES_API bool IsPathAvailable(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 void UpdateDefaultPath(Operation op, const FilePath &path)
FILES_API FilePath FindDefaultPath(Operation op)
auto end(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
Definition: PackedArray.h:159
auto begin(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
Definition: PackedArray.h:150
void SelectNone(AudacityProject &project)
FILES_API bool FATFilesystemDenied(const FilePath &path, const TranslatableString &msg, const BasicUI::WindowPlacement &placement={})
FILES_API wxString UnsavedProjectFileName()
bool ImportProject(AudacityProject &dest, const FilePath &fileName)
std::pair< const char *, const char * > Pair
wxString FindHelpUrl(const TranslatableString &libraryError)
TranslatableString Message(unsigned trackCount)
Options for variations of error dialogs; the default is for modal dialogs.
Definition: BasicUI.h:49