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