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