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