Audacity 3.2.0
ProjectManager.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3Audacity: A Digital Audio Editor
4
5ProjectManager.cpp
6
7Paul Licameli split from AudacityProject.cpp
8
9**********************************************************************/
10
11#include "ProjectManager.h"
12
13
14
15#include "ActiveProject.h"
16#include "AdornedRulerPanel.h"
17#include "AudioIO.h"
18#include "Clipboard.h"
19#include "FileNames.h"
20#include "Menus.h"
21#include "ModuleManager.h"
22#include "Project.h"
23#include "ProjectAudioIO.h"
24#include "ProjectAudioManager.h"
25#include "ProjectFileIO.h"
26#include "ProjectFileManager.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 "SelectUtilities.h"
35#include "TrackPanel.h"
36#include "TrackUtilities.h"
37#include "UndoManager.h"
38#include "WaveTrack.h"
39#include "wxFileNameWrapper.h"
40#include "import/Import.h"
41#include "import/ImportMIDI.h"
42#include "QualitySettings.h"
48#include "AudacityMessageBox.h"
49#include "widgets/FileHistory.h"
50#include "WindowAccessible.h"
51
52#include <wx/app.h>
53#include <wx/scrolbar.h>
54#include <wx/sizer.h>
55#include <wx/splitter.h>
56
57#ifdef __WXGTK__
58#include "../images/AudacityLogoAlpha.xpm"
59#endif
60
61namespace {
62 void SaveWindowPreferences(const wxRect& windowRect, const wxRect& normalRect,
63 bool isMaximized, bool isIconized) {
64 ProjectWindowX.Write(windowRect.GetX());
65 ProjectWindowY.Write(windowRect.GetY());
66 ProjectWindowWidth.Write(windowRect.GetWidth());
67 ProjectWindowHeight.Write(windowRect.GetHeight());
68 ProjectWindowMaximized.Write(isMaximized);
69 ProjectWindowNormalX.Write(normalRect.GetX());
70 ProjectWindowNormalY.Write(normalRect.GetY());
71 ProjectWindowNormalWidth.Write(normalRect.GetWidth());
72 ProjectWindowNormalHeight.Write(normalRect.GetHeight());
73 ProjectWindowIconized.Write(isIconized);
74 }
75}
76
77const int AudacityProjectTimerID = 5200;
78
80 []( AudacityProject &project ) {
81 return std::make_shared< ProjectManager >( project );
82 }
83};
84
86{
87 return project.AttachedObjects::Get< ProjectManager >( sProjectManagerKey );
88}
89
91{
92 return Get( const_cast< AudacityProject & >( project ) );
93}
94
96 : mProject{ project }
97 , mTimer{ std::make_unique<wxTimer>(this, AudacityProjectTimerID) }
98{
99 auto &window = ProjectWindow::Get( mProject );
100 window.Bind( wxEVT_CLOSE_WINDOW, &ProjectManager::OnCloseWindow, this );
105}
106
108
109BEGIN_EVENT_TABLE( ProjectManager, wxEvtHandler )
112
113bool ProjectManager::sbWindowRectAlreadySaved = false;
114bool ProjectManager::sbSkipPromptingForSave = false;
115
116void ProjectManager::SaveWindowSize()
117{
118 if (sbWindowRectAlreadySaved)
119 {
120 return;
121 }
122 bool validWindowForSaveWindowSize = FALSE;
123 ProjectWindow * validProject = nullptr;
124 bool foundIconizedProject = FALSE;
125 for ( auto pProject : AllProjects{} )
126 {
127 auto &window = ProjectWindow::Get( *pProject );
128 if (!window.IsIconized()) {
129 validWindowForSaveWindowSize = TRUE;
130 validProject = &window;
131 break;
132 }
133 else
134 foundIconizedProject = TRUE;
135 }
136
137 if (validWindowForSaveWindowSize)
138 {
139 wxRect windowRect = validProject->GetRect();
140 wxRect normalRect = validProject->GetNormalizedWindowState();
141 bool wndMaximized = validProject->IsMaximized();
142
143 SaveWindowPreferences(windowRect, normalRect, wndMaximized, false);
144 }
145 else
146 {
147 if (foundIconizedProject) {
148 validProject = &ProjectWindow::Get( **AllProjects{}.begin() );
149 bool wndMaximized = validProject->IsMaximized();
150 wxRect normalRect = validProject->GetNormalizedWindowState();
151
152 // store only the normal rectangle because the itemized rectangle
153 // makes no sense for an opening project window
154 SaveWindowPreferences(normalRect, normalRect, wndMaximized, true);
155 }
156 else {
157 // this would be a very strange case that might possibly occur on the Mac
158 // Audacity would have to be running with no projects open
159 // in this case we are going to write only the default values
160 wxRect defWndRect;
161 GetDefaultWindowRect(&defWndRect);
162 SaveWindowPreferences(defWndRect, defWndRect, false, false);
163 }
164 }
165 sbWindowRectAlreadySaved = true;
166}
167
169{
170 auto pProject = window.FindProject();
171 if (!pProject)
172 return;
173 auto &project = *pProject;
174
175#ifdef EXPERIMENTAL_DA2
176 SetBackgroundColour(theTheme.Colour( clrMedium ));
177#endif
178 // Note that the first field of the status bar is a dummy, and its width is set
179 // to zero latter in the code. This field is needed for wxWidgets 2.8.12 because
180 // if you move to the menu bar, the first field of the menu bar is cleared, which
181 // is undesirable behaviour.
182 // In addition, the help strings of menu items are by default sent to the first
183 // field. Currently there are no such help strings, but it they were introduced, then
184 // there would need to be an event handler to send them to the appropriate field.
185 auto statusBar = window.CreateStatusBar(4);
186#if wxUSE_ACCESSIBILITY
187 // so that name can be set on a standard control
188 statusBar->SetAccessible(safenew WindowAccessible(statusBar));
189#endif
190 statusBar->SetName(wxT("status_line")); // not localized
191
192 auto &viewInfo = ViewInfo::Get( project );
193
194 // LLL: Read this!!!
195 //
196 // Until the time (and cpu) required to refresh the track panel is
197 // reduced, leave the following window creations in the order specified.
198 // This will place the refresh of the track panel last, allowing all
199 // the others to get done quickly.
200 //
201 // Near as I can tell, this is only a problem under Windows.
202 //
203
204 //
205 // Create the ToolDock
206 //
207 ToolManager::Get( project ).CreateWindows();
208 ToolManager::Get( project ).LayoutToolBars();
209
210 //
211 // Create the TrackPanel and the scrollbars
212 //
213
214 auto topPanel = window.GetTopPanel();
215
216 {
217 auto ubs = std::make_unique<wxBoxSizer>(wxVERTICAL);
218 ubs->Add( ToolManager::Get( project ).GetTopDock(), 0, wxEXPAND | wxALIGN_TOP );
219 topPanel->SetSizer(ubs.release());
220 }
221
222 wxBoxSizer *bs;
223 {
224 auto ubs = std::make_unique<wxBoxSizer>(wxVERTICAL);
225 bs = ubs.get();
226 bs->Add(topPanel, 0, wxEXPAND | wxALIGN_TOP);
227 bs->Add(window.GetContainerWindow(), 1, wxEXPAND);
228 bs->Add( ToolManager::Get( project ).GetBotDock(), 0, wxEXPAND );
229 window.SetAutoLayout(true);
230 window.SetSizer(ubs.release());
231 }
232 bs->Layout();
233
234 auto &trackPanel = TrackPanel::Get( project );
235
236 // LLL: When Audacity starts or becomes active after returning from
237 // another application, the first window that can accept focus
238 // will be given the focus even if we try to SetFocus(). By
239 // making the TrackPanel that first window, we resolve several
240 // keyboard focus problems.
241 window.GetContainerWindow()->MoveAfterInTabOrder(topPanel);
242
243 const auto trackListWindow = window.GetTrackListWindow();
244
245 //
246 // Create the horizontal ruler
247 //
248 auto &ruler = AdornedRulerPanel::Get( project );
249
250 bs = static_cast<wxBoxSizer*>(trackListWindow->GetSizer());
251
252 auto vsBar = &window.GetVerticalScrollBar();
253 auto hsBar = &window.GetHorizontalScrollBar();
254
255 {
256 // Top horizontal grouping
257 auto hs = std::make_unique<wxBoxSizer>(wxHORIZONTAL);
258
259 // Track panel
260 hs->Add(&trackPanel, 1, wxEXPAND | wxALIGN_LEFT | wxALIGN_TOP);
261
262 {
263 // Vertical grouping
264 auto vs = std::make_unique<wxBoxSizer>(wxVERTICAL);
265
266 // Vertical scroll bar
267 vs->Add(vsBar, 1, wxEXPAND | wxALIGN_TOP);
268 hs->Add(vs.release(), 0, wxEXPAND | wxALIGN_TOP);
269 }
270
271 bs->Add(&ruler, 0, wxEXPAND | wxALIGN_TOP);
272 bs->Add(hs.release(), 1, wxEXPAND | wxALIGN_LEFT | wxALIGN_TOP);
273 }
274
275 {
276 // Bottom horizontal grouping
277 auto hs = std::make_unique<wxBoxSizer>(wxHORIZONTAL);
278
279 // Bottom scrollbar
280 hs->Add(viewInfo.GetLeftOffset() - 1, 0);
281 hs->Add(hsBar, 1, wxALIGN_BOTTOM);
282 hs->Add(vsBar->GetSize().GetWidth(), 0);
283 bs->Add(hs.release(), 0, wxEXPAND | wxALIGN_LEFT);
284 }
285
286 // Lay it out
287 trackListWindow->SetAutoLayout(true);
288 trackListWindow->Layout();
289
290 wxASSERT( trackPanel.GetProject() == &project );
291
292 // MM: Give track panel the focus to ensure keyboard commands work
293 trackPanel.SetFocus();
294
295 window.FixScrollbars();
296 ruler.SetLeftOffset(viewInfo.GetLeftOffset()); // bevel on AdornedRuler
297
298 //
299 // Set the Icon
300 //
301
302 // loads either the XPM or the windows resource, depending on the platform
303#if !defined(__WXMAC__) && !defined(__WXX11__)
304 {
305#if defined(__WXMSW__)
306 wxIcon ic{ wxICON(AudacityLogo) };
307#elif defined(__WXGTK__)
308 wxIcon ic{wxICON(AudacityLogoAlpha)};
309#else
310 wxIcon ic{};
311 ic.CopyFromBitmap(theTheme.Bitmap(bmpAudacityLogo48x48));
312#endif
313 window.SetIcon(ic);
314 }
315#endif
316
317 window.UpdateStatusWidths();
318 auto msg = XO("Welcome to Audacity version %s")
319 .Format( AUDACITY_VERSION_STRING );
321
322#ifdef EXPERIMENTAL_DA2
323 ClearBackground();// For wxGTK.
324#endif
325}
326
328{
329 wxRect wndRect;
330 bool bMaximized = false;
331 bool bIconized = false;
332 GetNextWindowPlacement(&wndRect, &bMaximized, &bIconized);
333
334 // Create and show a NEW project
335 // Use a non-default deleter in the smart pointer!
336 auto sp = AudacityProject::Create();
337 AllProjects{}.Add( sp );
338 auto p = sp.get();
339 auto &project = *p;
340 auto &projectHistory = ProjectHistory::Get( project );
341 auto &projectManager = Get( project );
342 auto &window = ProjectWindow::Get( *p );
343
344 // Issue #2569
345 // There is a dependency on the order of initialisation.
346 // The menus must be created, and registered, before
347 // InitProjectWindows can UpdateMenus.
349
350 InitProjectWindow( window );
351
352 // wxGTK3 seems to need to require creating the window using default position
353 // and then manually positioning it.
354 window.SetPosition(wndRect.GetPosition());
355
356 auto &projectFileManager = ProjectFileManager::Get( *p );
357
358 // This may report an error.
359 projectFileManager.OpenNewProject();
360
361 projectHistory.InitialState();
362 projectManager.RestartTimer();
363
364 if(bMaximized) {
365 window.Maximize(true);
366 }
367 else if (bIconized) {
368 // if the user close down and iconized state we could start back up and iconized state
369 // window.Iconize(TRUE);
370 }
371
372 //Initialise the Listeners
373 auto gAudioIO = AudioIO::Get();
374 gAudioIO->SetListener(
375 ProjectAudioManager::Get( project ).shared_from_this() );
376 auto &projectSelectionManager = ProjectSelectionManager::Get( project );
377 SelectionBar::Get( project ).SetListener( &projectSelectionManager );
378#ifdef EXPERIMENTAL_SPECTRAL_EDITING
379 SpectralSelectionBar::Get( project ).SetListener( &projectSelectionManager );
380#endif
381 TimeToolBar::Get( project ).SetListener( &projectSelectionManager );
382
383 //Set the NEW project as active:
385
386 // Okay, GetActiveProject() is ready. Now we can get its CommandManager,
387 // and add the shortcut keys to the tooltips.
389
391
392 window.Show(true);
393
394 return p;
395}
396
398{
400 ProjectWindow::Get(mProject).Close(true);
401}
402
403static bool sbClosingAll = false;
404
406{
407 sbClosingAll = closing;
408}
409
410void ProjectManager::OnCloseWindow(wxCloseEvent & event)
411{
412 auto &project = mProject;
413 auto &projectFileIO = ProjectFileIO::Get( project );
414 auto &projectFileManager = ProjectFileManager::Get( project );
415 const auto &settings = ProjectSettings::Get( project );
416 auto &projectAudioIO = ProjectAudioIO::Get( project );
417 auto &tracks = TrackList::Get( project );
418 auto &window = ProjectWindow::Get( project );
419 auto gAudioIO = AudioIO::Get();
420
421 // We are called for the wxEVT_CLOSE_WINDOW, wxEVT_END_SESSION, and
422 // wxEVT_QUERY_END_SESSION, so we have to protect against multiple
423 // entries. This is a hack until the whole application termination
424 // process can be reviewed and reworked. (See bug #964 for ways
425 // to exercise the bug that instigated this hack.)
426 if (window.IsBeingDeleted())
427 {
428 event.Skip();
429 return;
430 }
431
432 if (event.CanVeto() && (::wxIsBusy() || project.mbBusyImporting))
433 {
434 event.Veto();
435 return;
436 }
437
438 // Check to see if we were playing or recording
439 // audio, and if so, make sure Audio I/O is completely finished.
440 // The main point of this is to properly push the state
441 // and flush the tracks once we've completely finished
442 // recording NEW state.
443 // This code is derived from similar code in
444 // AudacityProject::~AudacityProject() and TrackPanel::OnTimer().
445 if (projectAudioIO.GetAudioIOToken()>0 &&
446 gAudioIO->IsStreamActive(projectAudioIO.GetAudioIOToken())) {
447
448 // We were playing or recording audio, but we've stopped the stream.
449 ProjectAudioManager::Get( project ).Stop();
450
451 projectAudioIO.SetAudioIOToken(0);
452 window.RedrawProject();
453 }
454 else if (gAudioIO->IsMonitoring()) {
455 gAudioIO->StopStream();
456 }
457
458 // MY: Use routine here so other processes can make same check
459 bool bHasTracks = !tracks.empty();
460
461 // We may not bother to prompt the user to save, if the
462 // project is now empty.
464 && event.CanVeto()
465 && (settings.EmptyCanBeDirty() || bHasTracks)) {
466 if ( UndoManager::Get( project ).UnsavedChanges() ) {
467 TitleRestorer Restorer( window, project );// RAII
468 /* i18n-hint: The first %s numbers the project, the second %s is the project name.*/
469 auto Title = XO("%sSave changes to %s?")
470 .Format( Restorer.sProjNumber, Restorer.sProjName );
471 auto Message = XO("Save project before closing?");
472 if( !bHasTracks )
473 {
474 Message += XO("\nIf saved, the project will have no tracks.\n\nTo save any previously open tracks:\nCancel, Edit > Undo until all tracks\nare open, then File > Save Project.");
475 }
476 int result = AudacityMessageBox(
477 Message,
478 Title,
479 wxYES_NO | wxCANCEL | wxICON_QUESTION,
480 &window);
481
482 if (result == wxCANCEL || (result == wxYES &&
483 !GuardedCall<bool>( [&]{ return projectFileManager.Save(); } )
484 )) {
485 event.Veto();
486 return;
487 }
488 }
489 }
490#ifdef __WXMAC__
491 // Fix bug apparently introduced into 2.1.2 because of wxWidgets 3:
492 // closing a project that was made full-screen (as by clicking the green dot
493 // or command+/; not merely "maximized" as by clicking the title bar or
494 // Zoom in the Window menu) leaves the screen black.
495 // Fix it by un-full-screening.
496 // (But is there a different way to do this? What do other applications do?
497 // I don't see full screen windows of Safari shrinking, but I do see
498 // momentary blackness.)
499 window.ShowFullScreen(false);
500#endif
501
502 // This achieves auto save on close of project before other important
503 // project state is destroyed
504 window.Publish(ProjectWindowDestroyedMessage {});
506
507 // Stop the timer since there's no need to update anything anymore
508 mTimer.reset();
509
510 // DMM: Save the size of the last window the user closes
511 //
512 // LL: Save before doing anything else to the window that might make
513 // its size change.
515
516 window.SetIsBeingDeleted();
517
518 // Mac: we never quit as the result of a close.
519 // Other systems: we quit only when the close is the result of an external
520 // command (on Windows, those are taskbar closes, "X" box, Alt+F4, etc.)
521 bool quitOnClose;
522#ifdef __WXMAC__
523 quitOnClose = false;
524#else
525 quitOnClose = !projectFileManager.GetMenuClose();
526#endif
527
528 // DanH: If we're definitely about to quit, clear the clipboard.
529 auto &clipboard = Clipboard::Get();
530 if ((AllProjects{}.size() == 1) &&
531 (quitOnClose || sbClosingAll))
532 clipboard.Clear();
533 else {
534 auto clipboardProject = clipboard.Project().lock();
535 if ( clipboardProject.get() == &mProject ) {
536 // Closing the project from which content was cut or copied.
537 // For 3.0.0, clear the clipboard, because accessing clipboard contents
538 // would depend on a database connection to the closing project, but
539 // that connection should closed now so that the project file can be
540 // freely moved.
541 // Notes:
542 // 1) maybe clipboard contents could be saved by migrating them to
543 // another temporary database, but that extra effort is beyond the
544 // scope of 3.0.0.
545 // 2) strictly speaking this is necessary only when the clipboard
546 // contains WaveTracks.
547 clipboard.Clear();
548 }
549 }
550
551 // JKC: For Win98 and Linux do not detach the menu bar.
552 // We want wxWidgets to clean it up for us.
553 // TODO: Is there a Mac issue here??
554 // SetMenuBar(NULL);
555
556 // Compact the project.
557 projectFileManager.CompactProjectOnClose();
558
559 // Set (or not) the bypass flag to indicate that deletes that would happen during
560 // the UndoManager::ClearStates() below are not necessary.
561 projectFileIO.SetBypass();
562
563 {
564 // This can reduce reference counts of sample blocks in the project's
565 // tracks.
566 UndoManager::Get( project ).ClearStates();
567
568 // Delete all the tracks to free up memory
569 tracks.Clear();
570 }
571
572 // Some of the AdornedRulerPanel functions refer to the TrackPanel, so destroy this
573 // before the TrackPanel is destroyed. This change was needed to stop Audacity
574 // crashing when running with Jaws on Windows 10 1703.
576
577 // Destroy the TrackPanel early so it's not around once we start
578 // deleting things like tracks and such out from underneath it.
579 // Check validity of mTrackPanel per bug 584 Comment 1.
580 // Deeper fix is in the Import code, but this failsafes against crash.
581 TrackPanel::Destroy( project );
582 // Finalize the tool manager before the children since it needs
583 // to save the state of the toolbars.
584 ToolManager::Get( project ).Destroy();
585
586 window.DestroyChildren();
587
588 // Close project only now, because TrackPanel might have been holding
589 // some shared_ptr to WaveTracks keeping SampleBlocks alive.
590 // We're all done with the project file, so close it now
591 projectFileManager.CloseProject();
592
593 WaveTrackFactory::Destroy( project );
594
595 // Remove self from the global array, but defer destruction of self
596 auto pSelf = AllProjects{}.Remove( project );
597 wxASSERT( pSelf );
598
599 if (GetActiveProject().lock().get() == &project) {
600 // Find a NEW active project
601 if ( !AllProjects{}.empty() ) {
603 }
604 else {
605 SetActiveProject(nullptr);
606 }
607 }
608
609 // Since we're going to be destroyed, make sure we're not to
610 // receive audio notifications anymore.
611 // PRL: Maybe all this is unnecessary now that the listener is managed
612 // by a weak pointer.
613 if ( gAudioIO->GetListener().get() == &ProjectAudioManager::Get( project ) ) {
614 auto active = GetActiveProject().lock();
615 gAudioIO->SetListener(
616 active
617 ? ProjectAudioManager::Get( *active ).shared_from_this()
618 : nullptr
619 );
620 }
621
622 if (AllProjects{}.empty() && !sbClosingAll) {
623
624#if !defined(__WXMAC__)
625 if (quitOnClose) {
626 // Simulate the application Exit menu item
627 wxCommandEvent evt{ wxEVT_MENU, wxID_EXIT };
628 wxTheApp->AddPendingEvent( evt );
629 }
630 else {
632 // For non-Mac, always keep at least one project window open
633 (void) New();
634 }
635#endif
636 }
637
638 window.Destroy();
639
640 // Destroys this
641 pSelf.reset();
642}
643
644// static method, can be called outside of a project
646{
647 auto selectedFiles =
648 ProjectFileManager::ShowOpenDialog(FileNames::Operation::Open);
649 if (selectedFiles.size() == 0) {
651 return;
652 }
653
654 //first sort selectedFiles.
655 selectedFiles.Sort(FileNames::CompareNoCase);
656
657 auto cleanup = finally( [] {
659 } );
660
661 for (const auto &fileName : selectedFiles) {
662 // Make sure it isn't already open.
664 continue; // Skip ones that are already open.
665
666 proj = OpenProject( proj, fileName,
667 true /* addtohistory */, false /* reuseNonemptyProject */ );
668 }
669}
670
672{
673 // DMM: If the project is dirty, that means it's been touched at
674 // all, and it's not safe to open a fresh project directly in its
675 // place. Only if the project is brandnew clean and the user
676 // hasn't done any action at all is it safe for Open to take place
677 // inside the current project.
678 //
679 // If you try to Open a fresh project inside the current window when
680 // there are no tracks, but there's an Undo history, etc, then
681 // bad things can happen, including orphan blocks, or tracks
682 // referring to non-existent blocks
683 if (
684 ProjectHistory::Get( proj ).GetDirty() ||
685 !TrackList::Get( proj ).empty()
686 )
687 return false;
688 // This project is clean; it's never been touched. Therefore
689 // all relevant member variables are in their initial state,
690 // and it's okay to open a NEW project inside its window.
691 return true;
692}
693
695{
696 if (mpUsedProject) {
698 // Ensure that it happens here: don't wait for the application level
699 // exception handler, because the exception may be intercepted
700 ProjectHistory::Get(*mpGivenProject).RollbackState();
701 // Any exception now continues propagating
702 }
703 else
704 GetProjectFrame( *mpUsedProject ).Close(true);
705 }
706}
707
710{
711 if (mpGivenProject) {
712 // Always check before opening a project file (for safety);
713 // May check even when opening other files
714 // (to preserve old behavior; as with the File > Open command specifying
715 // multiple files of whatever types, so that each gets its own window)
716 bool checkReuse = (openingProjectFile || !mReuseNonemptyProject);
717 if (!checkReuse || SafeToOpenProjectInto(*mpGivenProject))
718 return *(mpUsedProject = mpGivenProject);
719 }
720 return *(mpUsedProject = New());
721}
722
724{
725 mpUsedProject = nullptr;
726}
727
729 AudacityProject *pGivenProject, const FilePath &fileNameArg,
730 bool addtohistory, bool reuseNonemptyProject)
731{
732 ProjectManager::ProjectChooser chooser{ pGivenProject, reuseNonemptyProject };
733 if (auto pProject = ProjectFileManager::OpenFile(
734 std::ref(chooser), fileNameArg, addtohistory )) {
735 chooser.Commit();
736
737 auto &projectFileIO = ProjectFileIO::Get( *pProject );
738 if( projectFileIO.IsRecovered() ) {
739 auto &window = ProjectWindow::Get( *pProject );
740 window.Zoom( window.GetZoomOfToFit() );
741 // "Project was recovered" replaces "Create new project" in Undo History.
742 auto &undoManager = UndoManager::Get( *pProject );
743 undoManager.RemoveStates(0, 1);
744 }
745 return pProject;
746 }
747 return nullptr;
748}
749
750// This is done to empty out the tracks, but without creating a new project.
752 auto &project = mProject;
753 auto &projectFileIO = ProjectFileIO::Get( project );
754 auto &projectFileManager = ProjectFileManager::Get( project );
755 auto &projectHistory = ProjectHistory::Get( project );
756 auto &viewInfo = ViewInfo::Get( project );
757
760
761 WaveTrackFactory::Reset( project );
762
763 // InitialState will reset UndoManager
764 projectHistory.InitialState();
765 projectHistory.SetDirty(false);
766
767 projectFileManager.CloseProject();
768 projectFileManager.OpenProject();
769}
770
772{
773 if (mTimer) {
774 // mTimer->Stop(); // not really needed
775 mTimer->Start( 3000 ); // Update messages as needed once every 3 s.
776 }
777}
778
779void ProjectManager::OnTimer(wxTimerEvent& WXUNUSED(event))
780{
781 auto &project = mProject;
782 auto &projectAudioIO = ProjectAudioIO::Get( project );
783 auto meterToolBars = MeterToolBar::GetToolBars( project );
784
785 for (auto& meterToolBar : meterToolBars)
786 meterToolBar.get().UpdateControls();
787
788 auto gAudioIO = AudioIO::Get();
789 // gAudioIO->GetNumCaptureChannels() should only be positive
790 // when we are recording.
791 if (projectAudioIO.GetAudioIOToken() > 0 && gAudioIO->GetNumCaptureChannels() > 0) {
792 wxLongLong freeSpace = ProjectFileIO::Get(project).GetFreeDiskSpace();
793 if (freeSpace >= 0) {
794
795 int iRecordingMins = GetEstimatedRecordingMinsLeftOnDisk(gAudioIO->GetNumCaptureChannels());
796 auto sMessage = XO("Disk space remaining for recording: %s")
797 .Format( GetHoursMinsString(iRecordingMins) );
798
799 // Do not change mLastMainStatusMessage
801 }
802 }
803
804 // As also with the TrackPanel timer: wxTimer may be unreliable without
805 // some restarts
806 RestartTimer();
807}
808
810{
811 auto &project = mProject;
812
813 // Be careful to null-check the window. We might get to this function
814 // during shut-down, but a timer hasn't been told to stop sending its
815 // messages yet.
816 auto pWindow = ProjectWindow::Find( &project );
817 if ( !pWindow )
818 return;
819 auto &window = *pWindow;
820
821 window.UpdateStatusWidths();
822
823 const auto &msg = ProjectStatus::Get( project ).Get( field );
824 SetStatusText( msg, field );
825
826 if ( field == mainStatusBarField )
827 // When recording, let the NEW status message stay at least as long as
828 // the timer interval (if it is not replaced again by this function),
829 // before replacing it with the message about remaining disk capacity.
830 RestartTimer();
831}
832
834{
835 auto &project = mProject;
836 auto pWindow = ProjectWindow::Find( &project );
837 if ( !pWindow )
838 return;
839 auto &window = *pWindow;
840 window.GetStatusBar()->SetStatusText(text.Translation(), number);
841}
842
844{
845 if (iMinutes < 1)
846 // Less than a minute...
847 return XO("Less than 1 minute");
848
849 // Calculate
850 int iHours = iMinutes / 60;
851 int iMins = iMinutes % 60;
852
853 auto sHours = XP( "%d hour", "%d hours", 0 )( iHours );
854
855 auto sMins = XP( "%d minute", "%d minutes", 0 )( iMins );
856
857 /* i18n-hint: A time in hours and minutes. Only translate the "and". */
858 return XO("%s and %s.").Format( sHours, sMins );
859}
860
861// This routine will give an estimate of how many
862// minutes of recording time we have available.
863// The calculations made are based on the user's current
864// preferences.
866 auto &project = mProject;
867
868 // Obtain the current settings
869 auto oCaptureFormat = QualitySettings::SampleFormatChoice();
870 if (lCaptureChannels == 0)
871 lCaptureChannels = AudioIORecordChannels.Read();
872
873 // Find out how much free space we have on disk
874 wxLongLong lFreeSpace = ProjectFileIO::Get( project ).GetFreeDiskSpace();
875 if (lFreeSpace < 0) {
876 return 0;
877 }
878
879 // Calculate the remaining time
880 double dRecTime = 0.0;
881 double bytesOnDiskPerSample = SAMPLE_SIZE_DISK(oCaptureFormat);
882 dRecTime = lFreeSpace.GetHi() * 4294967296.0 + lFreeSpace.GetLo();
883 dRecTime /= bytesOnDiskPerSample;
884 dRecTime /= lCaptureChannels;
885 dRecTime /= ProjectRate::Get( project ).GetRate();
886
887 // Convert to minutes before returning
888 int iRecMins = (int)round(dRecTime / 60.0);
889 return iRecMins;
890}
AUDACITY_DLL_API std::weak_ptr< AudacityProject > GetActiveProject()
void SetActiveProject(AudacityProject *project)
Handle changing of active project and keep global project pointer.
wxT("CloseDown"))
int AudacityMessageBox(const TranslatableString &message, const TranslatableString &caption, long style, wxWindow *parent, int x, int y)
IntSetting AudioIORecordChannels
END_EVENT_TABLE()
XO("Cut/Copy/Paste")
#define field(n, t)
Definition: ImportAUP.cpp:169
#define XP(sing, plur, n)
Definition: Internat.h:94
#define safenew
Definition: MemoryX.h:10
@ ProjectClosing
@ ProjectInitialized
wxString FilePath
Definition: Project.h:21
ProjectFileIOMessage
Subscribe to ProjectFileIO to receive messages; always in idle time.
Definition: ProjectFileIO.h:49
void InitProjectWindow(ProjectWindow &window)
static AudacityProject::AttachedObjects::RegisteredFactory sProjectManagerKey
const int AudacityProjectTimerID
static bool sbClosingAll
an object holding per-project preferred sample rate
StatusBarField
Definition: ProjectStatus.h:24
@ mainStatusBarField
Definition: ProjectStatus.h:26
IntSetting ProjectWindowY
IntSetting ProjectWindowX
void GetDefaultWindowRect(wxRect *defRect)
IntSetting ProjectWindowWidth
BoolSetting ProjectWindowIconized
BoolSetting ProjectWindowMaximized
IntSetting ProjectWindowNormalHeight
IntSetting ProjectWindowHeight
IntSetting ProjectWindowNormalX
void GetNextWindowPlacement(wxRect *nextRect, bool *pMaximized, bool *pIconized)
IntSetting ProjectWindowNormalWidth
IntSetting ProjectWindowNormalY
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
#define SAMPLE_SIZE_DISK(SampleFormat)
Return the size on disk of one uncompressed sample (bytes)
Definition: SampleFormat.h:65
THEME_API Theme theTheme
Definition: Theme.cpp:82
static Settings & settings()
Definition: TrackInfo.cpp:83
static AdornedRulerPanel & Get(AudacityProject &project)
static void Destroy(AudacityProject &project)
void Add(const value_type &pProject)
This invalidates iterators.
Definition: Project.cpp:56
size_t size() const
Definition: Project.cpp:17
value_type Remove(AudacityProject &project)
Definition: Project.cpp:42
const_iterator begin() const
Definition: Project.cpp:22
bool empty() const
Definition: Project.h:47
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:90
static std::shared_ptr< AudacityProject > Create()
Use this factory function.
Definition: Project.cpp:78
static AudioIO * Get()
Definition: AudioIO.cpp:126
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
static void SetLastOpenType(const FileNames::FileType &type)
Definition: Import.cpp:224
void CreateMenusAndCommands(AudacityProject &project)
Definition: Menus.cpp:381
static MenuManager & Get(AudacityProject &project)
Definition: Menus.cpp:69
static MeterToolBars GetToolBars(AudacityProject &project)
static ModuleManager & Get()
int Dispatch(ModuleDispatchTypes type)
Subscription Subscribe(Callback callback)
Connect a callback to the Publisher; later-connected are called earlier.
Definition: Observer.h:199
static ProjectAudioIO & Get(AudacityProject &project)
void Stop(bool stopStream=true)
static ProjectAudioManager & Get(AudacityProject &project)
static ProjectFileIO & Get(AudacityProject &project)
wxLongLong GetFreeDiskSpace() const
static bool IsAlreadyOpen(const FilePath &projPathName)
static wxArrayString ShowOpenDialog(FileNames::Operation op, const FileNames::FileType &extraType={})
Show an open dialogue for opening audio files, and possibly other sorts of files.
static AudacityProject * OpenFile(const ProjectChooserFn &chooser, const FilePath &fileName, bool addtohistory=true)
static ProjectFileManager & Get(AudacityProject &project)
static ProjectHistory & Get(AudacityProject &project)
Callable object that supplies the chooser argument of ProjectFileManager::OpenFile.
AudacityProject * mpGivenProject
AudacityProject & operator()(bool openingProjectFile)
May create a fresh project.
AudacityProject * mpUsedProject
~ProjectChooser()
Destroy any fresh project, or rollback the existing project, unless committed.
void Commit()
Commit the creation of any fresh project or changes to the existing project.
Object associated with a project for high-level management of the project's lifetime,...
static bool sbWindowRectAlreadySaved
static ProjectManager & Get(AudacityProject &project)
void OnStatusChange(StatusBarField field)
void OnCloseWindow(wxCloseEvent &event)
static bool sbSkipPromptingForSave
static AudacityProject * New()
TranslatableString GetHoursMinsString(int iMinutes)
std::unique_ptr< wxTimer > mTimer
void OnTimer(wxTimerEvent &event)
static void SaveWindowSize()
static void OpenFiles(AudacityProject *proj)
static AudacityProject * OpenProject(AudacityProject *pGivenProject, const FilePath &fileNameArg, bool addtohistory, bool reuseNonemptyProject)
Open a file into an AudacityProject, returning the project, or nullptr for failure.
static void SetClosingAll(bool closing)
Observer::Subscription mProjectFileIOSubscription
~ProjectManager() override
ProjectManager(AudacityProject &project)
AudacityProject & mProject
static bool SafeToOpenProjectInto(AudacityProject &proj)
False when it is unsafe to overwrite proj with contents of an .aup3 file.
Observer::Subscription mProjectStatusSubscription
void OnReconnectionFailure(ProjectFileIOMessage)
void SetStatusText(const TranslatableString &text, int number)
int GetEstimatedRecordingMinsLeftOnDisk(long lCaptureChannels=0)
void ResetProjectToEmpty()
static ProjectRate & Get(AudacityProject &project)
Definition: ProjectRate.cpp:28
double GetRate() const
Definition: ProjectRate.cpp:53
static ProjectSelectionManager & Get(AudacityProject &project)
static ProjectSettings & Get(AudacityProject &project)
static ProjectStatus & Get(AudacityProject &project)
std::shared_ptr< AudacityProject > FindProject()
A top-level window associated with a project, and handling scrollbars and zooming.
Definition: ProjectWindow.h:41
wxScrollBar & GetHorizontalScrollBar()
static ProjectWindow & Get(AudacityProject &project)
wxScrollBar & GetVerticalScrollBar()
wxPanel * GetTopPanel() noexcept
Top panel contains project-related controls and tools.
static ProjectWindow * Find(AudacityProject *pProject)
wxRect GetNormalizedWindowState() const
wxWindow * GetTrackListWindow() noexcept
Track list window is the parent container for TrackPanel.
wxSplitterWindow * GetContainerWindow() noexcept
Container is a parent window for both effects panel and track list windows.
void UpdateStatusWidths()
void SetListener(SelectionBarListener *l)
static SelectionBar & Get(AudacityProject &project)
bool Write(const T &value)
Write value to config and return true if successful.
Definition: Prefs.h:252
bool Read(T *pVar) const
overload of Read returning a boolean that is true if the value was previously defined *‍/
Definition: Prefs.h:200
static SpectralSelectionBar & Get(AudacityProject &project)
void SetListener(SpectralSelectionBarListener *l)
wxColour & Colour(int iIndex)
wxBitmap & Bitmap(int iIndex)
static TimeToolBar & Get(AudacityProject &project)
Definition: TimeToolBar.cpp:74
void SetListener(TimeToolBarListener *l)
static ToolManager & Get(AudacityProject &project)
void CreateWindows()
void RegenerateTooltips()
void LayoutToolBars()
void Destroy()
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:385
static TrackPanel & Get(AudacityProject &project)
Definition: TrackPanel.cpp:231
static void Destroy(AudacityProject &project)
Definition: TrackPanel.cpp:241
Holds a msgid for the translation catalog; may also bind format arguments.
wxString Translation() const
void ClearStates()
static UndoManager & Get(AudacityProject &project)
Definition: UndoManager.cpp:71
static ViewInfo & Get(AudacityProject &project)
Definition: ViewInfo.cpp:235
static void Destroy(AudacityProject &project)
Definition: WaveTrack.cpp:2623
static WaveTrackFactory & Reset(AudacityProject &project)
Definition: WaveTrack.cpp:2616
An alternative to using wxWindowAccessible, which in wxWidgets 3.1.1 contained GetParent() which was ...
FILES_API int CompareNoCase(const wxString &first, const wxString &second)
PROJECT_RATE_API sampleFormat SampleFormatChoice()
void DoSelectAll(AudacityProject &project)
void DoRemoveTracks(AudacityProject &project)
void SaveWindowPreferences(const wxRect &windowRect, const wxRect &normalRect, bool isMaximized, bool isIconized)
TranslatableString Message(unsigned trackCount)
fastfloat_really_inline void round(adjusted_mantissa &am, callback cb) noexcept
Definition: fast_float.h:2512
STL namespace.
Message sent when the project window is closed.
Definition: ProjectWindow.h:33