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 && (settings.EmptyCanBeDirty() || 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 if( !bHasTracks )
444 {
445 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.");
446 }
447 int result = AudacityMessageBox(
448 Message,
449 Title,
450 wxYES_NO | wxCANCEL | wxICON_QUESTION,
451 &window);
452
453 if (result == wxCANCEL || (result == wxYES &&
454 !GuardedCall<bool>( [&]{ return projectFileManager.Save(); } )
455 )) {
456 event.Veto();
457 return;
458 }
459 }
460 }
461
462 // Ask extensions if they allow the project to be closed
464 && event.CanVeto())
465 {
466 event.Veto();
467 return;
468 }
469
470#ifdef __WXMAC__
471 // Fix bug apparently introduced into 2.1.2 because of wxWidgets 3:
472 // closing a project that was made full-screen (as by clicking the green dot
473 // or command+/; not merely "maximized" as by clicking the title bar or
474 // Zoom in the Window menu) leaves the screen black.
475 // Fix it by un-full-screening.
476 // (But is there a different way to do this? What do other applications do?
477 // I don't see full screen windows of Safari shrinking, but I do see
478 // momentary blackness.)
479 window.ShowFullScreen(false);
480#endif
481
482 // This achieves auto save on close of project before other important
483 // project state is destroyed
484 window.Publish(ProjectWindowDestroyedMessage {});
486
487 // Stop the timer since there's no need to update anything anymore
488 mTimer.reset();
489
490 // DMM: Save the size of the last window the user closes
491 //
492 // LL: Save before doing anything else to the window that might make
493 // its size change.
495
496 window.SetIsBeingDeleted();
497
498 // Mac: we never quit as the result of a close.
499 // Other systems: we quit only when the close is the result of an external
500 // command (on Windows, those are taskbar closes, "X" box, Alt+F4, etc.)
501 bool quitOnClose;
502#ifdef __WXMAC__
503 quitOnClose = false;
504#else
505 quitOnClose = !projectFileManager.GetMenuClose();
506#endif
507
508 // DanH: If we're definitely about to quit, clear the clipboard.
509 auto &clipboard = Clipboard::Get();
510 if ((AllProjects{}.size() == 1) &&
511 (quitOnClose || sbClosingAll))
512 clipboard.Clear();
513 else {
514 auto clipboardProject = clipboard.Project().lock();
515 if ( clipboardProject.get() == &mProject ) {
516 // Closing the project from which content was cut or copied.
517 // For 3.0.0, clear the clipboard, because accessing clipboard contents
518 // would depend on a database connection to the closing project, but
519 // that connection should closed now so that the project file can be
520 // freely moved.
521 // Notes:
522 // 1) maybe clipboard contents could be saved by migrating them to
523 // another temporary database, but that extra effort is beyond the
524 // scope of 3.0.0.
525 // 2) strictly speaking this is necessary only when the clipboard
526 // contains WaveTracks.
527 clipboard.Clear();
528 }
529 }
530
531 // JKC: For Win98 and Linux do not detach the menu bar.
532 // We want wxWidgets to clean it up for us.
533 // TODO: Is there a Mac issue here??
534 // SetMenuBar(NULL);
535
536 // Compact the project.
537 projectFileManager.CompactProjectOnClose();
538
539 // Set (or not) the bypass flag to indicate that deletes that would happen during
540 // the UndoManager::ClearStates() below are not necessary.
541 projectFileIO.SetBypass();
542
543 {
544 // This can reduce reference counts of sample blocks in the project's
545 // tracks.
547
548 // Delete all the tracks to free up memory
549 tracks.Clear();
550 }
551
552 // Some of the AdornedRulerPanel functions refer to the TrackPanel, so destroy this
553 // before the TrackPanel is destroyed. This change was needed to stop Audacity
554 // crashing when running with Jaws on Windows 10 1703.
556
557 // Destroy the TrackPanel early so it's not around once we start
558 // deleting things like tracks and such out from underneath it.
559 // Check validity of mTrackPanel per bug 584 Comment 1.
560 // Deeper fix is in the Import code, but this failsafes against crash.
562 // Finalize the tool manager before the children since it needs
563 // to save the state of the toolbars.
565
566 window.DestroyChildren();
567
568 // Close project only now, because TrackPanel might have been holding
569 // some shared_ptr to WaveTracks keeping SampleBlocks alive.
570 // We're all done with the project file, so close it now
571 projectFileManager.CloseProject();
572
574
575 // Remove self from the global array, but defer destruction of self
576 auto pSelf = AllProjects{}.Remove( project );
577 wxASSERT( pSelf );
578
579 if (GetActiveProject().lock().get() == &project) {
580 // Find a NEW active project
581 if ( !AllProjects{}.empty() ) {
583 }
584 else {
585 SetActiveProject(nullptr);
586 }
587 }
588
589 // Since we're going to be destroyed, make sure we're not to
590 // receive audio notifications anymore.
591 // PRL: Maybe all this is unnecessary now that the listener is managed
592 // by a weak pointer.
593 if ( gAudioIO->GetListener().get() == &ProjectAudioManager::Get( project ) ) {
594 auto active = GetActiveProject().lock();
595 gAudioIO->SetListener(
596 active
597 ? ProjectAudioManager::Get( *active ).shared_from_this()
598 : nullptr
599 );
600 }
601
602 if (AllProjects{}.empty() && !sbClosingAll) {
603
604#if !defined(__WXMAC__)
605 if (quitOnClose) {
606 // Simulate the application Exit menu item
607 wxCommandEvent evt{ wxEVT_MENU, wxID_EXIT };
608 wxTheApp->AddPendingEvent( evt );
609 }
610 else {
612 // For non-Mac, always keep at least one project window open
613 (void) New();
614 }
615#endif
616 }
617
618 window.Destroy();
619
620 // Destroys this
621 pSelf.reset();
622}
623
624// static method, can be called outside of a project
626{
627 auto selectedFiles =
628 ProjectFileManager::ShowOpenDialog(FileNames::Operation::Open);
629 if (selectedFiles.size() == 0) {
631 return;
632 }
633
634 //first sort selectedFiles.
635 selectedFiles.Sort(FileNames::CompareNoCase);
636
637 auto cleanup = finally( [] {
639 } );
640
641 for (const auto &fileName : selectedFiles) {
642 // Make sure it isn't already open.
644 continue; // Skip ones that are already open.
645
646 proj = OpenProject( proj, fileName,
647 true /* addtohistory */, false /* reuseNonemptyProject */ );
648 }
649}
650
652{
653 // DMM: If the project is dirty, that means it's been touched at
654 // all, and it's not safe to open a fresh project directly in its
655 // place. Only if the project is brandnew clean and the user
656 // hasn't done any action at all is it safe for Open to take place
657 // inside the current project.
658 //
659 // If you try to Open a fresh project inside the current window when
660 // there are no tracks, but there's an Undo history, etc, then
661 // bad things can happen, including orphan blocks, or tracks
662 // referring to non-existent blocks
663 if (
664 ProjectHistory::Get( proj ).GetDirty() ||
665 !TrackList::Get( proj ).empty()
666 )
667 return false;
668 // This project is clean; it's never been touched. Therefore
669 // all relevant member variables are in their initial state,
670 // and it's okay to open a NEW project inside its window.
671 return true;
672}
673
675{
676 if (mpUsedProject) {
678 // Ensure that it happens here: don't wait for the application level
679 // exception handler, because the exception may be intercepted
680 ProjectHistory::Get(*mpGivenProject).RollbackState();
681 // Any exception now continues propagating
682 }
683 else
684 GetProjectFrame( *mpUsedProject ).Close(true);
685 }
686}
687
690{
691 if (mpGivenProject) {
692 // Always check before opening a project file (for safety);
693 // May check even when opening other files
694 // (to preserve old behavior; as with the File > Open command specifying
695 // multiple files of whatever types, so that each gets its own window)
696 bool checkReuse = (openingProjectFile || !mReuseNonemptyProject);
697 if (!checkReuse || SafeToOpenProjectInto(*mpGivenProject))
698 return *(mpUsedProject = mpGivenProject);
699 }
700 return *(mpUsedProject = New());
701}
702
704{
705 mpUsedProject = nullptr;
706}
707
709 AudacityProject *pGivenProject, const FilePath &fileNameArg,
710 bool addtohistory, bool reuseNonemptyProject)
711{
712 ProjectManager::ProjectChooser chooser{ pGivenProject, reuseNonemptyProject };
713 if (auto pProject = ProjectFileManager::OpenFile(
714 std::ref(chooser), fileNameArg, addtohistory )) {
715 chooser.Commit();
716
717 auto &projectFileIO = ProjectFileIO::Get( *pProject );
718 if( projectFileIO.IsRecovered() ) {
719 auto &viewport = Viewport::Get(*pProject);
720 viewport.Zoom(viewport.GetZoomOfToFit());
721 // "Project was recovered" replaces "Create new project" in Undo History.
722 auto &undoManager = UndoManager::Get( *pProject );
723 undoManager.RemoveStates(0, 1);
724 }
725 return pProject;
726 }
727 return nullptr;
728}
729
730// This is done to empty out the tracks, but without creating a new project.
732 auto &project = mProject;
733 auto &projectFileIO = ProjectFileIO::Get( project );
734 auto &projectFileManager = ProjectFileManager::Get( project );
735 auto &projectHistory = ProjectHistory::Get( project );
736 auto &viewInfo = ViewInfo::Get( project );
737
740
742
743 // InitialState will reset UndoManager
744 projectHistory.InitialState();
745 projectHistory.SetDirty(false);
746
747 projectFileManager.CloseProject();
748 projectFileManager.OpenProject();
749}
750
752{
753 if (mTimer) {
754 // mTimer->Stop(); // not really needed
755 mTimer->Start( 3000 ); // Update messages as needed once every 3 s.
756 }
757}
758
759void ProjectManager::OnTimer(wxTimerEvent& WXUNUSED(event))
760{
761 auto &project = mProject;
762 auto &projectAudioIO = ProjectAudioIO::Get( project );
763 auto meterToolBars = MeterToolBar::GetToolBars( project );
764
765 for (auto& meterToolBar : meterToolBars)
766 meterToolBar.get().UpdateControls();
767
768 auto gAudioIO = AudioIO::Get();
769 // gAudioIO->GetNumCaptureChannels() should only be positive
770 // when we are recording.
771 if (projectAudioIO.GetAudioIOToken() > 0 && gAudioIO->GetNumCaptureChannels() > 0) {
772 wxLongLong freeSpace = ProjectFileIO::Get(project).GetFreeDiskSpace();
773 if (freeSpace >= 0) {
774
775 int iRecordingMins = GetEstimatedRecordingMinsLeftOnDisk(gAudioIO->GetNumCaptureChannels());
776 auto sMessage = XO("Disk space remaining for recording: %s")
777 .Format( GetHoursMinsString(iRecordingMins) );
778
779 // Do not change mLastMainStatusMessage
781 }
782 }
783
784 // As also with the TrackPanel timer: wxTimer may be unreliable without
785 // some restarts
786 RestartTimer();
787}
788
790{
791 auto &project = mProject;
792
793 // Be careful to null-check the window. We might get to this function
794 // during shut-down, but a timer hasn't been told to stop sending its
795 // messages yet.
796 auto pWindow = ProjectWindow::Find( &project );
797 if ( !pWindow )
798 return;
799 auto &window = *pWindow;
800
801 window.UpdateStatusWidths();
802
803 const auto &msg = ProjectStatus::Get( project ).Get( field );
804 SetStatusText( msg, field );
805
806 if ( field == MainStatusBarField() )
807 // When recording, let the NEW status message stay at least as long as
808 // the timer interval (if it is not replaced again by this function),
809 // before replacing it with the message about remaining disk capacity.
810 RestartTimer();
811}
812
814 const TranslatableString& text, const StatusBarField& field)
815{
817
818 if (index >= 0)
819 SetStatusText(text, index);
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.
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: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: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: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)
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
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:33
static void Destroy(AudacityProject &project)
Definition: WaveTrack.cpp:3366
static WaveTrackFactory & Reset(AudacityProject &project)
Definition: WaveTrack.cpp:3359
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