Audacity 3.2.0
TimerRecordDialog.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 TimerRecordDialog.cpp
6
7 Copyright 2006-2009 by Vaughan Johnson
8
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
13
14*******************************************************************//*******************************************************************/
20
21
22#include "TimerRecordDialog.h"
23
24#include "FileNames.h"
25
26#include <thread>
27#include <wx/setup.h> // for wxUSE_* macros
28
29#include <wx/app.h>
30#include <wx/wxcrtvararg.h>
31#include <wx/button.h>
32#include <wx/calctrl.h>
33#include <wx/checkbox.h>
34#include <wx/choice.h>
35#include <wx/defs.h>
36#include <wx/dir.h>
37#include <wx/datectrl.h>
38#include <wx/datetime.h>
39#include <wx/dynlib.h> //<! For windows.h
40#include <wx/frame.h>
41
42#include "AudioIO.h"
43#include "SelectFile.h"
44#include "ShuttleGui.h"
45#include "ProjectAudioManager.h"
46#include "ProjectFileIO.h"
47#include "ProjectFileManager.h"
48#include "ProjectManager.h"
49#include "ProjectRate.h"
50#include "ProjectWindows.h"
51#include "Project.h"
52#include "Prefs.h"
53#include "Track.h"
54#include "TagsEditor.h"
56#include "HelpSystem.h"
57#include "AudacityMessageBox.h"
59#include "ExportUtils.h"
60#include "ProgressDialog.h"
61#include "wxTextCtrlWrapper.h"
62
64
66#include "ExportProgressUI.h"
67
68#if wxUSE_ACCESSIBILITY
69#include "WindowAccessible.h"
70#endif
71
72#define TIMER_ID 7000
73
74enum { // control IDs
86};
87
88enum {
91};
92
93// The slow timer interval is used to update the start and end times, which only show
94// time to the nearest second. So we only need an update once a second.
95const int kSlowTimerInterval = 1000; // ms
96
97// This timer interval is used in some busy-wait loops and is much shorter.
98constexpr auto kTimerInterval = std::chrono::milliseconds{50};
99
100static double wxDateTime_to_AudacityTime(wxDateTime& dateTime)
101{
102 return (dateTime.GetHour() * 3600.0) + (dateTime.GetMinute() * 60.0) + dateTime.GetSecond();
103};
104
105namespace {
106 StringSetting DefaultExportAudioFormat{ L"/TimerRecordDialog/ExportFormat", L"WAV" };
107 StringSetting DefaultExportAudioPath{ L"/TimerRecordDialog/ExportPath", L"" };
108}
109
110// The purpose of the DatePickerCtrlAx class is to make to wxDatePickerCtrl more accessible for
111// the NVDA screen reader.
112// By default the msaa state of wxDatePickerCtrl is always normal (0x0), and this causes nvda not
113// to read the control when the user tabs to it. This class
114// modifies the state to be focusable + focused (when it's the focus).
115// Note that even with this class NVDA still doesn't read the NEW selected part of the control when left/right
116// arrow keys are used.
117
118#if wxUSE_ACCESSIBILITY
119
120class DatePickerCtrlAx final : public WindowAccessible
121{
122public:
123 DatePickerCtrlAx(wxDatePickerCtrl * ctrl) : WindowAccessible(ctrl), mCtrl(ctrl) {};
124
125 virtual ~ DatePickerCtrlAx() {};
126
127 // Returns a state constant.
128 wxAccStatus GetState(int childId, long *state) override;
129
130private:
131 wxDatePickerCtrl *mCtrl;
132};
133
134// Returns a state constant.
135wxAccStatus DatePickerCtrlAx::GetState(int WXUNUSED(childId), long *state)
136{
137 *state = wxACC_STATE_SYSTEM_FOCUSABLE;
138 *state |= (mCtrl == wxWindow::FindFocus() ? wxACC_STATE_SYSTEM_FOCUSED : 0);
139
140 return wxACC_OK;
141}
142
143#endif // wxUSE_ACCESSIBILITY
144
145
146BEGIN_EVENT_TABLE(TimerRecordDialog, wxDialogWrapper)
149
152
154
157
159
162
165
167
169 wxWindow* parent, AudacityProject &project, bool bAlreadySaved)
170: wxDialogWrapper(parent, -1, XO("Audacity Timer Record"), wxDefaultPosition,
171 wxDefaultSize, wxCAPTION)
172, mProject{ project }
173{
174 SetName();
175
176 m_DateTime_Start = wxDateTime::UNow();
177 long seconds; // default duration is 1 hour = 3600 seconds
178 gPrefs->Read(wxT("/TimerRecord/LastDuration"), &seconds, 3600L);
179 m_TimeSpan_Duration = wxTimeSpan::Seconds(seconds);
180 m_DateTime_End = m_DateTime_Start + m_TimeSpan_Duration;
181
182 m_pDatePickerCtrl_Start = NULL;
183 m_pTimeTextCtrl_Start = NULL;
184
185 m_pDatePickerCtrl_End = NULL;
186 m_pTimeTextCtrl_End = NULL;
187
188 m_pTimeTextCtrl_Duration = NULL;
189
190 // Do we allow the user to change the Automatic Save file?
191 m_bProjectAlreadySaved = bAlreadySaved;
192
193 wxString exportPath;
194 DefaultExportAudioPath.Read(&exportPath);
195 if(exportPath.empty())
196 exportPath = FileNames::FindDefaultPath(FileNames::Operation::Export);
197 m_fnAutoExportFile.SetPath(exportPath);
198
199 m_fnAutoExportFile.SetName(mProject.GetProjectName());
200 if(m_fnAutoExportFile.GetName().IsEmpty())
201 m_fnAutoExportFile.SetName(_("untitled"));
202
203 DefaultExportAudioFormat.Read(&m_sAutoExportFormat);
204 if(!m_sAutoExportFormat.empty())
205 {
206 auto [plugin, formatIndex]
207 = ExportPluginRegistry::Get().FindFormat(m_sAutoExportFormat);
208
209 if(plugin != nullptr)
210 {
211 const auto formatInfo = plugin->GetFormatInfo(formatIndex);
212 m_fnAutoExportFile.SetExt(formatInfo.extensions[0]);
213 }
214 }
215
216 m_iAutoExportSampleRate = ProjectRate::Get(mProject).GetRate();
217 m_iAutoExportChannels = AudioIORecordChannels.Read();
218
219 ShuttleGui S(this, eIsCreating);
220 this->PopulateOrExchange(S);
221
222 // Set initial focus to "1" of "01h" in Duration NumericTextCtrl,
223 // instead of OK button (default).
224 m_pTimeTextCtrl_Duration->SetFocus();
225 m_pTimeTextCtrl_Duration->SetFieldFocus(3);
226
227 m_timer.SetOwner(this, TIMER_ID);
228 m_timer.Start(kSlowTimerInterval);
229}
230
232
233void TimerRecordDialog::OnTimer(wxTimerEvent& WXUNUSED(event))
234{
235 wxDateTime dateTime_UNow = wxDateTime::UNow();
236 if (m_DateTime_Start < dateTime_UNow) {
237 m_DateTime_Start = dateTime_UNow;
240 this->UpdateEnd(); // Keep Duration constant and update End for changed Start.
241 }
242}
243
244void TimerRecordDialog::OnDatePicker_Start(wxDateEvent& WXUNUSED(event))
245{
247 double dTime = m_pTimeTextCtrl_Start->GetValue();
248 long hr = (long)(dTime / 3600.0);
249 long min = (long)((dTime - (hr * 3600.0)) / 60.0);
250 long sec = (long)(dTime - (hr * 3600.0) - (min * 60.0));
251 m_DateTime_Start.SetHour(hr);
252 m_DateTime_Start.SetMinute(min);
253 m_DateTime_Start.SetSecond(sec);
254
255 // User might have had the dialog up for a while, or
256 // had a future day, set hour of day less than now's, then changed day to today.
257 wxTimerEvent dummyTimerEvent;
258 this->OnTimer(dummyTimerEvent);
259
260 // Always update End for changed Start, keeping Duration constant.
261 // Note that OnTimer sometimes calls UpdateEnd, so sometimes this is redundant,
262 // but OnTimer doesn't need to always call UpdateEnd, but we must here.
263 this->UpdateEnd();
264}
265
266void TimerRecordDialog::OnTimeText_Start(wxCommandEvent& WXUNUSED(event))
267{
268 //v NumericTextCtrl doesn't implement upper ranges, i.e.,
269 // if I tell it "024 h 060 m 060 s", then
270 // user increments the hours past 23, it rolls over to 0
271 // (although if you increment below 0, it stays at 0).
272 // So instead, set the max to 99 and just catch hours > 24 and fix the ctrls.
273 double dTime = m_pTimeTextCtrl_Start->GetValue();
274 long days = (long)(dTime / (24.0 * 3600.0));
275 if (days > 0) {
276 dTime -= (double)days * 24.0 * 3600.0;
277 m_DateTime_Start += wxTimeSpan::Days(days);
280 }
281
282 wxDateEvent dummyDateEvent;
283 this->OnDatePicker_Start(dummyDateEvent);
284}
285
286void TimerRecordDialog::OnDatePicker_End(wxDateEvent& WXUNUSED(event))
287{
289 double dTime = m_pTimeTextCtrl_End->GetValue();
290 long hr = (long)(dTime / 3600.0);
291 long min = (long)((dTime - (hr * 3600.0)) / 60.0);
292 long sec = (long)(dTime - (hr * 3600.0) - (min * 60.0));
293 m_DateTime_End.SetHour(hr);
294 m_DateTime_End.SetMinute(min);
295 m_DateTime_End.SetSecond(sec);
296
297 // DatePickerCtrls use SetRange to make sure End is never less than Start, but
298 // need to implement it for the TimeTextCtrls.
303 }
304
305 this->UpdateDuration(); // Keep Start constant and update Duration for changed End.
306}
307
308void TimerRecordDialog::OnTimeText_End(wxCommandEvent& WXUNUSED(event))
309{
310 //v NumericTextCtrl doesn't implement upper ranges, i.e.,
311 // if I tell it "024 h 060 m 060 s", then
312 // user increments the hours past 23, it rolls over to 0
313 // (although if you increment below 0, it stays at 0).
314 // So instead, set the max to 99 and just catch hours > 24 and fix the ctrls.
315 double dTime = m_pTimeTextCtrl_End->GetValue();
316 long days = (long)(dTime / (24.0 * 3600.0));
317 if (days > 0) {
318 dTime -= (double)days * 24.0 * 3600.0;
319 m_DateTime_End += wxTimeSpan::Days(days);
322 }
323
324 wxDateEvent dummyDateEvent;
325 this->OnDatePicker_End(dummyDateEvent);
326}
327
328void TimerRecordDialog::OnTimeText_Duration(wxCommandEvent& WXUNUSED(event))
329{
330 double dTime = m_pTimeTextCtrl_Duration->GetValue();
331 long hr = (long)(dTime / 3600.0);
332 long min = (long)((dTime - (hr * 3600.0)) / 60.0);
333 long sec = (long)(dTime - (hr * 3600.0) - (min * 60.0));
334 m_TimeSpan_Duration = wxTimeSpan(hr, min, sec); //v milliseconds?
335
336 this->UpdateEnd(); // Keep Start constant and update End for changed Duration.
337}
338
339// New events for timer recording automation
340void TimerRecordDialog::OnAutoSavePathButton_Click(wxCommandEvent& WXUNUSED(event))
341{
342 auto &projectFileIO = ProjectFileIO::Get(mProject);
343
344 wxString fName = SelectFile(FileNames::Operation::Export,
345 XO("Save Timer Recording As"),
346 m_fnAutoSaveFile.GetPath(),
347 m_fnAutoSaveFile.GetFullName(),
348 wxT("aup3"),
350 wxFD_SAVE | wxRESIZE_BORDER,
351 this);
352
353 if (fName.empty())
354 return;
355
356 // If project already exists then abort - we do not allow users to overwrite an existing project
357 // unless it is the current project.
358 if (wxFileExists(fName) && (projectFileIO.GetFileName() != fName)) {
360 nullptr,
361 XO("The selected file name could not be used\nfor Timer Recording because it \
362would overwrite another project.\nPlease try again and select an original name."),
363 XO("Error Saving Timer Recording Project"),
364 wxOK|wxICON_ERROR );
365 m.ShowModal();
366 return;
367 }
368
369 // Set this boolean to false so we now do a SaveAs at the end of the recording
370 // unless we're saving the current project.
371 m_bProjectAlreadySaved = projectFileIO.GetFileName() == fName? true : false;
372
373 m_fnAutoSaveFile = fName;
374 m_fnAutoSaveFile.SetExt(wxT("aup3"));
375 this->UpdateTextBoxControls();
376}
377
378void TimerRecordDialog::OnAutoExportPathButton_Click(wxCommandEvent& WXUNUSED(event))
379{
380 // Set the options required
381 TimerRecordExportDialog exportDialog(mProject, this);
382 exportDialog.Bind(
388
389 if(exportDialog.ShowModal() != wxID_OK)
390 return;
391
392 m_pTimerExportPathTextCtrl->SetValue(m_fnAutoExportFile.GetFullPath());
393
394 // Update the text controls
395 this->UpdateTextBoxControls();
396}
397
398void TimerRecordDialog::OnAutoSaveCheckBox_Change(wxCommandEvent& WXUNUSED(event)) {
400}
401
402void TimerRecordDialog::OnAutoExportCheckBox_Change(wxCommandEvent& WXUNUSED(event)) {
404}
405
406void TimerRecordDialog::OnHelpButtonClick(wxCommandEvent& WXUNUSED(event))
407{
408 HelpSystem::ShowHelp(this, L"Timer_Record", true);
409}
410
411void TimerRecordDialog::OnOK(wxCommandEvent& WXUNUSED(event))
412{
414 if (!m_TimeSpan_Duration.IsPositive())
415 {
417 XO("Duration is zero. Nothing will be recorded."),
418 XO("Error in Duration"),
419 wxICON_EXCLAMATION | wxOK);
420 return;
421 }
422
423 // Validate that we have a Save and/or Export path setup if the appropriate check box is ticked
424 wxString sTemp = m_fnAutoSaveFile.GetFullPath();
425 if (m_pTimerAutoSaveCheckBoxCtrl->IsChecked()) {
426 if (!m_fnAutoSaveFile.IsOk() || m_fnAutoSaveFile.IsDir()) {
428 XO("Automatic Save path is invalid."),
429 XO("Error in Automatic Save"),
430 wxICON_EXCLAMATION | wxOK);
431 return;
432 }
433 }
434 if (m_pTimerAutoExportCheckBoxCtrl->IsChecked()) {
435 if (!m_fnAutoExportFile.IsOk() || m_fnAutoExportFile.IsDir()) {
437 XO("Automatic Export path is invalid."),
438 XO("Error in Automatic Export"),
439 wxICON_EXCLAMATION | wxOK);
440 return;
441 }
442 }
443
444 // MY: Estimate here if we have enough disk space to
445 // complete this Timer Recording.
446 // If we don't think there is enough space then ask the user
447 // if they want to continue.
448 // We don't stop the user from starting the recording
449 // as its possible that they plan to free up some
450 // space before the recording begins
451 auto &projectManager = ProjectManager::Get( mProject );
452
453 // How many minutes do we have left on the disc?
454 int iMinsLeft = projectManager.GetEstimatedRecordingMinsLeftOnDisk();
455
456 // How many minutes will this recording require?
457 int iMinsRecording = m_TimeSpan_Duration.GetMinutes();
458
459 // Do we have enough space?
460 if (iMinsRecording >= iMinsLeft) {
461
462 // Format the strings
463 auto sRemainingTime = projectManager.GetHoursMinsString(iMinsLeft);
464 auto sPlannedTime = projectManager.GetHoursMinsString(iMinsRecording);
465
466 // Create the message string
467 auto sMessage = XO(
468"You may not have enough free disk space to complete this Timer Recording, based on your current settings.\n\nDo you wish to continue?\n\nPlanned recording duration: %s\nRecording time remaining on disk: %s")
469 .Format( sPlannedTime, sRemainingTime );
470
471 AudacityMessageDialog dlgMessage(
472 nullptr,
473 sMessage,
474 XO("Timer Recording Disk Space Warning"),
475 wxYES_NO | wxNO_DEFAULT | wxICON_WARNING);
476 if (dlgMessage.ShowModal() != wxID_YES ) {
477 // User decided not to continue - bail out!
478 return;
479 }
480 }
481
482 m_timer.Stop(); // Don't need to keep updating m_DateTime_Start to prevent backdating.
483 this->EndModal(wxID_OK);
484 wxLongLong duration = m_TimeSpan_Duration.GetSeconds();
485 // this will assert if the duration won't fit in a long
486 gPrefs->Write(wxT("/TimerRecord/LastDuration"), duration.ToLong());
487 gPrefs->Flush();
488}
489
490void TimerRecordDialog::EnableDisableAutoControls(bool bEnable, int iControlGoup) {
491
492 if (iControlGoup == CONTROL_GROUP_EXPORT) {
493 m_pTimerExportPathTextCtrl->Enable( bEnable );
494 m_pTimerExportPathButtonCtrl->Enable( bEnable);
495 } else if (iControlGoup == CONTROL_GROUP_SAVE) {
496 m_pTimerSavePathTextCtrl->Enable( bEnable);
497 m_pTimerSavePathButtonCtrl->Enable(bEnable );
498 }
499
500 // Enable or disable the Choice box - if there is no Save or Export then this will be disabled
501 if (m_pTimerAutoSaveCheckBoxCtrl->GetValue() || m_pTimerAutoExportCheckBoxCtrl->GetValue()) {
503 } else {
506 }
507}
508
510 // Will update the text box controls
511 m_pTimerSavePathTextCtrl->SetValue(m_fnAutoSaveFile.GetFullPath());
512 m_pTimerExportPathTextCtrl->SetValue(m_fnAutoExportFile.GetFullPath());
513
514 // MY: Ensure we still display "Current Project" if this has already been saved
516 m_pTimerSavePathTextCtrl->SetValue(_("Current Project"));
517 }
518}
519
523{
524 auto updateResult = ProgressResult::Success;
525
526 const auto gAudioIO = AudioIO::Get();
527 gAudioIO->DelayActions(true);
528 {
529 auto cleanup = finally([gAudioIO]{ gAudioIO->DelayActions(false); });
530
531 if (m_DateTime_Start > wxDateTime::UNow())
532 updateResult = this->WaitForStart();
533
534 if (updateResult != ProgressResult::Success) {
535 // Don't proceed, but don't treat it as canceled recording. User just canceled waiting.
537 } else {
538 // Record for specified time.
540 bool bIsRecording = true;
541
542 auto sPostAction = Verbatim(
543 m_pTimerAfterCompleteChoiceCtrl->GetStringSelection() );
544
545 // Two column layout.
547 {
548 XO("Recording start:") ,
549 XO("Duration:") ,
550 XO("Recording end:") ,
551 {} ,
552 XO("Automatic Save enabled:") ,
553 XO("Automatic Export enabled:") ,
554 XO("Action after Timer Recording:") ,
555 },
556 {
558 Verbatim( m_TimeSpan_Duration.Format() ),
560 {} ,
561 (m_bAutoSaveEnabled ? XO("Yes") : XO("No")) ,
562 (m_bAutoExportEnabled ? XO("Yes") : XO("No")) ,
563 sPostAction ,
564 }
565 };
566
568 progress(m_TimeSpan_Duration.GetMilliseconds().GetValue(),
569 XO("Audacity Timer Record Progress"),
570 columns,
572
573 // Make sure that start and end time are updated, so we always get the full
574 // duration, even if there's some delay getting here.
575 wxTimerEvent dummyTimerEvent;
576 this->OnTimer(dummyTimerEvent);
577
578 // Loop for progress display during recording.
579 while (bIsRecording && (updateResult == ProgressResult::Success)) {
580 updateResult = progress.UpdateProgress();
581 using namespace std::chrono;
582 std::this_thread::sleep_for(kTimerInterval);
583 bIsRecording = (wxDateTime::UNow() <= m_DateTime_End); // Call UNow() again for extra accuracy...
584 }
585 }
586 }
587
588 // Must do this AFTER the timer project dialog has been deleted to ensure the application
589 // responds to the AUDIOIO events...see not about bug #334 in the ProgressDialog constructor.
591
592 // Let the caller handle cancellation or failure from recording progress.
593 if (updateResult == ProgressResult::Cancelled || updateResult == ProgressResult::Failed)
595
596 return ExecutePostRecordActions((updateResult == ProgressResult::Stopped));
597}
598
600 // MY: We no longer automatically (and silently) call ->Save() when the
601 // timer recording is completed. We can now Save and/or Export depending
602 // on the options selected by the user.
603 // Once completed, we can also close Audacity, restart the system or
604 // shutdown the system.
605 // If there was any error with the auto save or export then we will not do
606 // the actions requested and instead present an error mesasge to the user.
607 // Finally, if there is no post-record action selected then we output
608 // a dialog detailing what has been carried out instead.
609
610 bool bSaveOK = false;
611 bool bExportOK = false;
612 int iPostRecordAction = m_pTimerAfterCompleteChoiceCtrl->GetSelection();
613 int iOverriddenAction = iPostRecordAction;
614 bool bErrorOverride = false;
615
616 // Do Automatic Save?
617 if (m_bAutoSaveEnabled) {
618
619 auto &projectFileManager = ProjectFileManager::Get( mProject );
620 // MY: If this project has already been saved then simply execute a Save here
622 bSaveOK = projectFileManager.Save();
623 } else {
624 bSaveOK = projectFileManager.SaveFromTimerRecording(m_fnAutoSaveFile);
625 }
626 }
627
628 // Do Automatic Export?
630 const auto& tracks = TrackList::Get(mProject);
631 if(ExportUtils::FindExportWaveTracks(tracks, false).empty())
632 {
633 ShowExportErrorDialog(XO("All audio is muted."), XO("Warning"), false);
634 bExportOK = true;
635 }
636 else
637 {
638 const auto t0 = std::max(.0, tracks.GetStartTime());
639
640 auto [exportPlugin, formatIndex] =
642
643 if(exportPlugin != nullptr)
644 {
645 auto builder = ExportTaskBuilder {}
648 .SetRange(t0, tracks.GetEndTime(), false)
651 .SetPlugin(exportPlugin, formatIndex);
652
654 {
655 const auto result = ExportProgressUI::Show(builder.Build(mProject));
656 bExportOK = result == ExportResult::Success ||
657 result == ExportResult::Stopped;
658 });
659
660 if(bExportOK)
661 {
664 }
665 }
666 }
667 }
668
669 // Check if we need to override the post recording action
670 bErrorOverride = ((m_bAutoSaveEnabled && !bSaveOK) || (m_bAutoExportEnabled && !bExportOK));
671 if (bErrorOverride || bWasStopped) {
672 iPostRecordAction = POST_TIMER_RECORD_NOTHING;
673 }
674
675 if (iPostRecordAction == POST_TIMER_RECORD_NOTHING) {
676 // If there is no post-record action then we can show a message indicating what has been done
677
678 auto sMessage = (bWasStopped ? XO("Timer Recording stopped.") :
679 XO("Timer Recording completed."));
680
681 if (m_bAutoSaveEnabled) {
682 if (bSaveOK) {
683 sMessage = XO("%s\n\nRecording saved: %s").Format(
684 sMessage, m_fnAutoSaveFile.GetFullPath());
685 } else {
686 sMessage = XO("%s\n\nError saving recording.").Format( sMessage );
687 }
688 }
690 if (bExportOK) {
691 sMessage = XO("%s\n\nRecording exported: %s").Format(
692 sMessage, m_fnAutoExportFile.GetFullPath());
693 } else {
694 sMessage = XO("%s\n\nError exporting recording.").Format( sMessage );
695 }
696 }
697
698 if (bErrorOverride) {
699
700 if ((iOverriddenAction != iPostRecordAction) &&
701 (iOverriddenAction != POST_TIMER_RECORD_NOTHING)) {
702 // Inform the user that we have overridden the selected action
703 sMessage = XO("%s\n\n'%s' has been canceled due to the error(s) noted above.").Format(
704 sMessage,
705 m_pTimerAfterCompleteChoiceCtrl->GetString(iOverriddenAction));
706 }
707
708 // Show Error Message Box
710 sMessage,
711 XO("Error"),
712 wxICON_EXCLAMATION | wxOK);
713 } else {
714
715 if (bWasStopped && (iOverriddenAction != POST_TIMER_RECORD_NOTHING)) {
716 sMessage = XO("%s\n\n'%s' has been canceled as the recording was stopped.").Format(
717 sMessage,
718 m_pTimerAfterCompleteChoiceCtrl->GetString(iOverriddenAction));
719 }
720
722 sMessage,
723 XO("Timer Recording"),
724 wxICON_INFORMATION | wxOK);
725 }
726 }
727
728 // MY: Lets do some actions that only apply to Exit/Restart/Shutdown
729 if (iPostRecordAction >= POST_TIMER_RECORD_CLOSE) {
730 do {
731
732 // Set the flags as appropriate based on what we have done
733 wxUint32 eActionFlags = TR_ACTION_NOTHING;
734 if (m_bAutoSaveEnabled && bSaveOK) {
735 eActionFlags |= TR_ACTION_SAVED;
736 }
737 if (m_bAutoExportEnabled && bExportOK) {
738 eActionFlags |= TR_ACTION_EXPORTED;
739 }
740
741 // Lets show a warning dialog telling the user what is about to happen.
742 // If the user no longer wants to carry out this action then they can click
743 // Cancel and we will do POST_TIMER_RECORD_NOTHING instead.
744 auto iDelayOutcome = PreActionDelay(iPostRecordAction, (TimerRecordCompletedActions)eActionFlags);
745 if (iDelayOutcome != ProgressResult::Success) {
746 // Cancel the action!
747 iPostRecordAction = POST_TIMER_RECORD_NOTHING;
748 break;
749 }
750 } while (false);
751 }
752
753 // Return the action as required
754 return iPostRecordAction;
755}
756
758{
759#if defined(__WXMSW__)
760 // On Windows, wxWidgets uses the system date control and it displays the
761 // date based on the Windows locale selected by the user. But, wxDateTime
762 // using the strftime function to return the formatted date. Since the
763 // default locale for the Windows CRT environment is "C", the dates come
764 // back in a different format.
765 //
766 // So, we make direct Windows calls to format the date like it the date
767 // control.
768 //
769 // (Most of this taken from src/msw/datectrl.cpp)
770
771 const wxDateTime::Tm tm(dt.GetTm());
772 SYSTEMTIME st;
773 wxString s;
774 int len;
775
776 st.wYear = (WXWORD)tm.year;
777 st.wMonth = (WXWORD)(tm.mon - wxDateTime::Jan + 1);
778 st.wDay = tm.mday;
779 st.wDayOfWeek = st.wMinute = st.wSecond = st.wMilliseconds = 0;
780
781 len = ::GetDateFormat(LOCALE_USER_DEFAULT,
782 DATE_SHORTDATE,
783 &st,
784 NULL,
785 NULL,
786 0);
787 if (len > 0) {
788 len = ::GetDateFormat(LOCALE_USER_DEFAULT,
789 DATE_SHORTDATE,
790 &st,
791 NULL,
792 wxStringBuffer(s, len),
793 len);
794 if (len > 0) {
795 s += wxT(" ") + dt.FormatTime();
796 return Verbatim( s );
797 }
798 }
799#endif
800
801 // Use default formatting
802wxPrintf(wxT("%s\n"), dt.Format());
803 return Verbatim( dt.FormatDate() + wxT(" ") + dt.FormatTime() );
804}
805
807 wxWindow *wParent, const int iID,
808 const TranslatableString &sCaption, const TranslatableString &sValue)
809{
810 wxTextCtrlWrapper * pTextCtrl;
811 wxASSERT(wParent); // to justify safenew
812 pTextCtrl = safenew wxTextCtrlWrapper(wParent, iID, sValue.Translation());
813 pTextCtrl->SetName(sCaption.Translation());
814 return pTextCtrl;
815}
816
818{
819 bool bAutoSave = gPrefs->ReadBool("/TimerRecord/AutoSave", false);
820 bool bAutoExport = gPrefs->ReadBool("/TimerRecord/AutoExport", false);
821 int iPostTimerRecordAction = gPrefs->ReadLong("/TimerRecord/PostAction", 0);
822
823 S.SetBorder(5);
824 using Options = NumericTextCtrl::Options;
825 /* i18n-hint a format string for hours, minutes, and seconds */
826 auto strFormat = XO("099 h 060 m 060 s");
827 /* i18n-hint a format string for days, hours, minutes, and seconds */
828 auto strFormat1 = XO("099 days 024 h 060 m 060 s");
829
830 S.StartMultiColumn(2, wxCENTER);
831 {
832 S.StartVerticalLay(true);
833 {
834 /* i18n-hint: This string is used to configure the controls for times when the recording is
835 * started and stopped. As such it is important that only the alphabetic parts of the string
836 * are translated, with the numbers left exactly as they are.
837 * The 'h' indicates the first number displayed is hours, the 'm' indicates the second number
838 * displayed is minutes, and the 's' indicates that the third number displayed is seconds.
839 */
840 S.StartStatic(XO("Start Date and Time"), true);
841 {
843 safenew wxDatePickerCtrl(S.GetParent(), // wxWindow *parent,
844 ID_DATEPICKER_START, // wxWindowID id,
845 m_DateTime_Start); // const wxDateTime& dt = wxDefaultDateTime,
846 // const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDP_DEFAULT | wxDP_SHOWCENTURY, const wxValidator& validator = wxDefaultValidator, const wxString& name = "datectrl")
847 m_pDatePickerCtrl_Start->SetRange(wxDateTime::Today(), wxInvalidDateTime); // No backdating.
848#if wxUSE_ACCESSIBILITY
849 m_pDatePickerCtrl_Start->SetAccessible( safenew DatePickerCtrlAx(m_pDatePickerCtrl_Start));
850#endif
851 S.Name(XO("Start Date"))
852 .AddWindow(m_pDatePickerCtrl_Start);
853
856 {}, 0,
857 Options{}
858 .MenuEnabled(false)
859 .CustomFormat(strFormat)
861 S.Name(XO("Start Time"))
862 .AddWindow(m_pTimeTextCtrl_Start);
863 }
864 S.EndStatic();
865
866 S.StartStatic(XO("End Date and Time"), true);
867 {
869 safenew wxDatePickerCtrl(S.GetParent(), // wxWindow *parent,
870 ID_DATEPICKER_END, // wxWindowID id,
871 m_DateTime_End); // const wxDateTime& dt = wxDefaultDateTime,
872 // const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize,
873 // long style = wxDP_DEFAULT | wxDP_SHOWCENTURY,
874 // const wxValidator& validator = wxDefaultValidator,
875 // const wxString& name = "datectrl")
876 m_pDatePickerCtrl_End->SetRange(m_DateTime_Start, wxInvalidDateTime); // No backdating.
877#if wxUSE_ACCESSIBILITY
878 m_pDatePickerCtrl_End->SetAccessible( safenew DatePickerCtrlAx(m_pDatePickerCtrl_End));
879#endif
880 S.Name(XO("End Date"))
881 .AddWindow(m_pDatePickerCtrl_End);
882
885 {}, 0,
886 Options{}
887 .MenuEnabled(false)
888 .CustomFormat(strFormat)
890 S.Name(XO("End Time"))
891 .AddWindow(m_pTimeTextCtrl_End);
892 }
893 S.EndStatic();
894
895 S.StartStatic(XO("Duration"), true);
896 {
899 {}, 0,
900 Options{}
901 .MenuEnabled(false)
902 .CustomFormat(strFormat1)
903 .Value(true, m_TimeSpan_Duration.GetSeconds().ToDouble()));
904 /* i18n-hint: This string is used to configure the controls which shows the recording
905 * duration. As such it is important that only the alphabetic parts of the string
906 * are translated, with the numbers left exactly as they are.
907 * The string 'days' indicates that the first number in the control will be the number of days,
908 * then the 'h' indicates the second number displayed is hours, the 'm' indicates the third
909 * number displayed is minutes, and the 's' indicates that the fourth number displayed is
910 * seconds.
911 */
912 S.Name(XO("Duration"))
913 .AddWindow(m_pTimeTextCtrl_Duration);
914 }
915 S.EndStatic();
916 }
917 S.EndVerticalLay();
918
919 S.StartVerticalLay(true);
920 {
921 S.StartStatic(XO("Automatic Save"), true);
922 {
923 // If checked, the project will be saved when the recording is completed
924 m_pTimerAutoSaveCheckBoxCtrl = S.Id(ID_AUTOSAVE_CHECKBOX).AddCheckBox(XXO("Enable &Automatic Save?"),
925 bAutoSave);
926 S.StartMultiColumn(3, wxEXPAND);
927 {
928 TranslatableString sInitialValue;
929 auto sSaveValue = ProjectFileIO::Get(mProject).GetFileName();
930 if (!sSaveValue.empty()) {
931 m_fnAutoSaveFile.Assign(sSaveValue);
932 sInitialValue = XO("Current Project");
933 }
934 S.AddPrompt(XXO("Save Project As:"));
936 S.GetParent(), ID_AUTOSAVEPATH_TEXT,
937 XO("Save Project As:"), sInitialValue);
939 S.AddWindow(m_pTimerSavePathTextCtrl);
940 m_pTimerSavePathButtonCtrl = S.Id(ID_AUTOSAVEPATH_BUTTON).AddButton(XXO("Select..."));
941 }
942 S.EndMultiColumn();
943 }
944 S.EndStatic();
945
946 S.StartStatic(XO("Automatic Export"), true);
947 {
948 m_pTimerAutoExportCheckBoxCtrl = S.Id(ID_AUTOEXPORT_CHECKBOX).AddCheckBox(XXO("Enable Automatic &Export?"), bAutoExport);
949 S.StartMultiColumn(3, wxEXPAND);
950 {
951 S.AddPrompt(XXO("Export Project As:"));
953 S.GetParent(), ID_AUTOEXPORTPATH_TEXT,
954 XO("Export Project As:"), {});
956 m_pTimerExportPathTextCtrl->SetValue(m_fnAutoExportFile.GetFullPath());
957 S.AddWindow(m_pTimerExportPathTextCtrl);
958 m_pTimerExportPathButtonCtrl = S.Id(ID_AUTOEXPORTPATH_BUTTON).AddButton(XXO("Select..."));
959 }
960 S.EndMultiColumn();
961 }
962 S.EndStatic();
963
964 S.StartStatic(XO("Options"), true);
965 {
966
967 S.StartMultiColumn(1, wxEXPAND);
968 {
969 S.SetStretchyCol( 0 );
970 m_pTimerAfterCompleteChoiceCtrl = S.AddChoice(XXO("After Recording completes:"),
971 {
972 XO("Do nothing") ,
973 XO("Exit Audacity") ,
974 #ifdef __WINDOWS__
975 XO("Restart system") ,
976 XO("Shutdown system") ,
977 #endif
978 },
979 iPostTimerRecordAction
980 );
981 }
982 S.EndMultiColumn();
983 }
984 S.EndStatic();
985
986 }
987 S.EndVerticalLay();
988 }
989 S.EndMultiColumn();
990
991 // MY: Added the help button here
992 S.AddStandardButtons(eOkButton | eCancelButton | eHelpButton);
993
994 Layout();
995 Fit();
996 SetMinSize(GetSize());
997 Center();
998
1001}
1002
1004{
1005 double dTime;
1006 long hr;
1007 long min;
1008 long sec;
1009
1012 hr = (long)(dTime / 3600.0);
1013 min = (long)((dTime - (hr * 3600.0)) / 60.0);
1014 sec = (long)(dTime - (hr * 3600.0) - (min * 60.0));
1015 m_DateTime_Start.SetHour(hr);
1016 m_DateTime_Start.SetMinute(min);
1017 m_DateTime_Start.SetSecond(sec);
1018
1020 dTime = m_pTimeTextCtrl_End->GetValue();
1021 hr = (long)(dTime / 3600.0);
1022 min = (long)((dTime - (hr * 3600.0)) / 60.0);
1023 sec = (long)(dTime - (hr * 3600.0) - (min * 60.0));
1024 m_DateTime_End.SetHour(hr);
1025 m_DateTime_End.SetMinute(min);
1026 m_DateTime_End.SetSecond(sec);
1027
1029
1030 // Pull the settings from the auto save/export controls and write to the pref file
1033
1034 // MY: Obtain the index from the choice control so we can save to the prefs file
1035 int iPostRecordAction = m_pTimerAfterCompleteChoiceCtrl->GetSelection();
1036
1037 // Save the options back to the prefs file
1038 gPrefs->Write("/TimerRecord/AutoSave", m_bAutoSaveEnabled);
1039 gPrefs->Write("/TimerRecord/AutoExport", m_bAutoExportEnabled);
1040 gPrefs->Write("/TimerRecord/PostAction", iPostRecordAction);
1041
1042 return true;
1043}
1044
1045// Update m_TimeSpan_Duration and ctrl based on m_DateTime_Start and m_DateTime_End.
1047{
1049 m_pTimeTextCtrl_Duration->SetValue(m_TimeSpan_Duration.GetSeconds().ToDouble());
1050}
1051
1052// Update m_DateTime_End and ctrls based on m_DateTime_Start and m_TimeSpan_Duration.
1054{
1055 //v Use remaining disk -> record time calcs from AudacityProject::OnTimer to set range?
1057 //wxLogDebug( "Time start %s end %s",
1058 // m_DateTime_Start.FormatISOCombined(' '),
1059 // m_DateTime_End.FormatISOCombined(' ') );
1060
1061 // Disable the range limitation (to fix Bug 1749 and 1978)
1062 // Otherwise SetVallue asserts when going back in time.
1063 m_pDatePickerCtrl_End->SetRange(wxInvalidDateTime, wxInvalidDateTime);
1065 // Re-enable range limitation to constrain user input.
1066 m_pDatePickerCtrl_End->SetRange(m_DateTime_Start, wxInvalidDateTime); // No backdating.
1067 m_pDatePickerCtrl_End->Refresh();
1069}
1070
1072{
1073 // MY: The Waiting For Start dialog now shows what actions will occur after recording has completed
1074 auto sPostAction = Verbatim(
1075 m_pTimerAfterCompleteChoiceCtrl->GetStringSelection() );
1076
1077 // Two column layout.
1079 {
1080 XO("Waiting to start recording at:") ,
1081 XO("Recording duration:") ,
1082 XO("Scheduled to stop at:") ,
1083 {} ,
1084 XO("Automatic Save enabled:") ,
1085 XO("Automatic Export enabled:") ,
1086 XO("Action after Timer Recording:") ,
1087 },
1088 {
1090 Verbatim( m_TimeSpan_Duration.Format() ),
1092 {} ,
1093 (m_bAutoSaveEnabled ? XO("Yes") : XO("No")) ,
1094 (m_bAutoExportEnabled ? XO("Yes") : XO("No")) ,
1095 sPostAction ,
1096 },
1097 };
1098
1099 wxDateTime startWait_DateTime = wxDateTime::UNow();
1100 wxTimeSpan waitDuration = m_DateTime_Start - startWait_DateTime;
1101 TimerProgressDialog progress(waitDuration.GetMilliseconds().GetValue(),
1102 XO("Audacity Timer Record - Waiting for Start"),
1103 columns,
1105 /* i18n-hint: "in" means after a duration of time,
1106 which is shown below this string */
1107 XO("Recording will commence in:"));
1108
1109 auto updateResult = ProgressResult::Success;
1110 bool bIsRecording = false;
1111 while (updateResult == ProgressResult::Success && !bIsRecording)
1112 {
1113 updateResult = progress.UpdateProgress();
1114 using namespace std::chrono;
1115 std::this_thread::sleep_for(kTimerInterval);
1116 bIsRecording = (m_DateTime_Start <= wxDateTime::UNow());
1117 }
1118 return updateResult;
1119}
1120
1122{
1124 ->GetString(iActionIndex) );
1125
1126 /* i18n-hint: %s is one of "Do nothing", "Exit Audacity", "Restart system",
1127 or "Shutdown system", and
1128 "in" means after a duration of time, shown below this string */
1129 auto sCountdownLabel = XO("%s in:").Format( sAction );
1130
1131 // Two column layout.
1133 {
1134 XO("Timer Recording completed.") ,
1135 {} ,
1136 XO("Recording Saved:") ,
1137 XO("Recording Exported:") ,
1138 XO("Action after Timer Recording:") ,
1139 },
1140 {
1141 {} ,
1142 {} ,
1143 ((eCompletedActions & TR_ACTION_SAVED) ? XO("Yes") : XO("No")) ,
1144 ((eCompletedActions & TR_ACTION_EXPORTED) ? XO("Yes") : XO("No")) ,
1145 sAction ,
1146 },
1147 };
1148
1149
1150 wxDateTime dtNow = wxDateTime::UNow();
1151 wxTimeSpan tsWait = wxTimeSpan(0, 1, 0, 0);
1152 wxDateTime dtActionTime = dtNow.Add(tsWait);
1153
1154 TimerProgressDialog dlgAction(tsWait.GetMilliseconds().GetValue(),
1155 XO("Audacity Timer Record - Waiting"),
1156 columns,
1158 sCountdownLabel);
1159
1160 auto iUpdateResult = ProgressResult::Success;
1161 bool bIsTime = false;
1162 while (iUpdateResult == ProgressResult::Success && !bIsTime)
1163 {
1164 iUpdateResult = dlgAction.UpdateProgress();
1165 using namespace std::chrono;
1166 std::this_thread::sleep_for(kTimerInterval);
1167 bIsTime = (dtActionTime <= wxDateTime::UNow());
1168 }
1169 return iUpdateResult;
1170}
1171
1172// Register a menu item
1173
1174#include "CommandContext.h"
1175#include "MenuRegistry.h"
1176#include "CommonCommandFlags.h"
1177#include "Project.h"
1178#include "ProjectHistory.h"
1179#include "ProjectSettings.h"
1180#include "UndoManager.h"
1181
1182namespace {
1183void OnTimerRecord(const CommandContext &context)
1184{
1185 auto &project = context.project;
1186 const auto &settings = ProjectSettings::Get( project );
1187 auto &undoManager = UndoManager::Get( project );
1188 auto &window = GetProjectFrame(project);
1189
1190 // MY: Due to improvements in how Timer Recording saves and/or exports
1191 // it is now safer to disable Timer Recording when there is more than
1192 // one open project.
1193 if (AllProjects{}.size() > 1) {
1195 XO(
1196"Timer Recording cannot be used with more than one open project.\n\nPlease close any additional projects and try again."),
1197 XO("Timer Recording"),
1198 wxICON_INFORMATION | wxOK);
1199 return;
1200 }
1201
1202 // MY: If the project has unsaved changes then we no longer allow access
1203 // to Timer Recording. This decision has been taken as the safest approach
1204 // preventing issues surrounding "dirty" projects when Automatic Save/Export
1205 // is used in Timer Recording.
1206 if ((undoManager.UnsavedChanges()) &&
1207 (!TrackList::Get(project).empty() || settings.EmptyCanBeDirty())) {
1209 XO(
1210"Timer Recording cannot be used while you have unsaved changes.\n\nPlease save or close this project and try again."),
1211 XO("Timer Recording"),
1212 wxICON_INFORMATION | wxOK);
1213 return;
1214 }
1215
1216 // We check the selected tracks to see if there is enough of them to accommodate
1217 // all input channels and all of them have the same sampling rate.
1218 // Those checks will be later performed by recording function anyway,
1219 // but we want to warn the user about potential problems from the very start.
1220 const auto selectedTracks{ GetPropertiesOfSelected(project) };
1221 const int rateOfSelected{ selectedTracks.rateOfSelected };
1222 const bool anySelected{ selectedTracks.anySelected };
1223 const bool allSameRate{ selectedTracks.allSameRate };
1224
1225 if (!allSameRate) {
1226 AudacityMessageBox(XO("The tracks selected "
1227 "for recording must all have the same sampling rate"),
1228 XO("Mismatched Sampling Rates"),
1229 wxICON_ERROR | wxCENTRE);
1230
1231 return;
1232 }
1233
1234 // Only need the size
1235 const auto existingTracks =
1237 rateOfSelected);
1238 if (existingTracks.empty()) {
1239 if (anySelected && rateOfSelected !=
1242 "Too few tracks are selected for recording at this sample rate.\n"
1243 "(Audacity requires two channels at the same sample rate for\n"
1244 "each stereo track)"),
1245 XO("Too Few Compatible Tracks Selected"),
1246 wxICON_ERROR | wxCENTRE);
1247
1248 return;
1249 }
1250 }
1251
1252 // We use this variable to display "Current Project" in the Timer Recording
1253 // save project field
1254 bool bProjectSaved = !ProjectFileIO::Get( project ).IsModified();
1255
1256 //we break the prompting and waiting dialogs into two sections
1257 //because they both give the user a chance to click cancel
1258 //and therefore remove the newly inserted track.
1259
1260 TimerRecordDialog dialog(
1261 &window, project, bProjectSaved); /* parent, project, project saved? */
1262 int modalResult = dialog.ShowModal();
1263 if (modalResult == wxID_CANCEL)
1264 {
1265 // Cancelled before recording - don't need to do anything.
1266 }
1267 else
1268 {
1269 // Bug #2382
1270 // Allow recording to start at current cursor position.
1271 #if 0
1272 // Timer Record should not record into a selection.
1273 bool bPreferNewTrack;
1274 gPrefs->Read("/GUI/PreferNewTrackRecord",&bPreferNewTrack, false);
1275 if (bPreferNewTrack) {
1276 window.Rewind(false);
1277 } else {
1278 window.SkipEnd(false);
1279 }
1280 #endif
1281
1282 int iTimerRecordingOutcome = dialog.RunWaitDialog();
1283 switch (iTimerRecordingOutcome) {
1285 // Canceled on the wait dialog
1287 break;
1289 // RunWaitDialog() shows the "wait for start" as well as "recording"
1290 // dialog if it returned POST_TIMER_RECORD_CANCEL it means the user
1291 // cancelled while the recording, so throw out the fresh track.
1292 // However, we can't undo it here because the PushState() is called in TrackPanel::OnTimer(),
1293 // which is blocked by this function.
1294 // so instead we mark a flag to undo it there.
1296 break;
1298 // No action required
1299 break;
1301 wxTheApp->CallAfter( []{
1302 // Simulate the application Exit menu item
1303 wxCommandEvent evt{ wxEVT_MENU, wxID_EXIT };
1304 wxTheApp->AddPendingEvent( evt );
1305 } );
1307 break;
1308
1309#ifdef __WINDOWS__
1310 case POST_TIMER_RECORD_RESTART:
1311 // Restart System
1313 system("shutdown /r /f /t 30");
1314 break;
1315 case POST_TIMER_RECORD_SHUTDOWN:
1316 // Shutdown System
1318 system("shutdown /s /f /t 30");
1319 break;
1320#endif
1321 }
1322 }
1323}
1324
1326
1327using namespace MenuRegistry;
1329 Command( wxT("TimerRecord"), XXO("&Timer Record..."),
1330 OnTimerRecord, CanStopFlags, wxT("Shift+T") ),
1331 { wxT("Transport/Basic/Record"),
1332 { OrderingHint::After, wxT("Record2ndChoice") } }
1333};
1334
1335}
wxT("CloseDown"))
int AudacityMessageBox(const TranslatableString &message, const TranslatableString &caption, long style, wxWindow *parent, int x, int y)
IntSetting AudioIORecordChannels
END_EVENT_TABLE()
const ReservedCommandFlag & AudioIONotBusyFlag()
int min(int a, int b)
EVT_BUTTON(wxID_NO, DependencyDialog::OnNo) EVT_BUTTON(wxID_YES
void ShowExportErrorDialog(const TranslatableString &message, const TranslatableString &caption, bool allowReporting)
Definition: Export.cpp:144
XO("Cut/Copy/Paste")
XXO("&Cut/Copy/Paste Toolbar")
#define _(s)
Definition: Internat.h:73
#define safenew
Definition: MemoryX.h:10
const NumericConverterType & NumericConverterType_TIME()
audacity::BasicSettings * gPrefs
Definition: Prefs.cpp:68
@ pdlgConfirmStopCancel
@ pdlgHideStopButton
@ pdlgHideElapsedTime
@ pdlgHideCancelButton
const ReservedCommandFlag & CanStopAudioStreamFlag()
PropertiesOfSelected GetPropertiesOfSelected(const AudacityProject &proj)
an object holding per-project preferred sample rate
AUDACITY_DLL_API wxFrame & GetProjectFrame(AudacityProject &project)
Get the top-level window associated with the project (as a wxFrame only, when you do not need to use ...
accessors for certain important windows associated with each project
FilePath SelectFile(FileNames::Operation op, const TranslatableString &message, const FilePath &default_path, const FilePath &default_filename, const FileExtension &default_extension, const FileTypes &fileTypes, int flags, wxWindow *parent)
Definition: SelectFile.cpp:17
@ eIsCreating
Definition: ShuttleGui.h:37
@ eOkButton
Definition: ShuttleGui.h:609
@ eCancelButton
Definition: ShuttleGui.h:610
@ eHelpButton
Definition: ShuttleGui.h:613
const auto tracks
const auto project
const int kSlowTimerInterval
constexpr auto kTimerInterval
@ CONTROL_GROUP_SAVE
@ CONTROL_GROUP_EXPORT
@ ID_AUTOEXPORTPATH_BUTTON
@ ID_AUTOSAVEPATH_TEXT
@ ID_DATEPICKER_START
@ ID_AUTOSAVEPATH_BUTTON
@ ID_AUTOSAVE_CHECKBOX
@ ID_AUTOEXPORTPATH_TEXT
@ ID_TIMETEXT_DURATION
@ ID_TIMETEXT_END
@ ID_DATEPICKER_END
@ ID_TIMETEXT_START
@ ID_AUTOEXPORT_CHECKBOX
#define TIMER_ID
static double wxDateTime_to_AudacityTime(wxDateTime &dateTime)
TimerRecordCompletedActions
@ TR_ACTION_NOTHING
@ TR_ACTION_EXPORTED
@ TR_ACTION_SAVED
@ POST_TIMER_RECORD_CLOSE
@ POST_TIMER_RECORD_CANCEL
@ POST_TIMER_RECORD_CANCEL_WAIT
@ POST_TIMER_RECORD_NOTHING
#define S(N)
Definition: ToChars.cpp:64
declares abstract base class Track, TrackList, and iterators over TrackList
static Settings & settings()
Definition: TrackInfo.cpp:47
TranslatableString Verbatim(wxString str)
Require calls to the one-argument constructor to go through this distinct global function name.
size_t size() const
Definition: Project.cpp:17
Wrap wxMessageDialog so that caption IS translatable.
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:90
static AudioIO * Get()
Definition: AudioIO.cpp:126
CommandContext provides additional information to an 'Apply()' command. It provides the project,...
AudacityProject & project
std::tuple< ExportPlugin *, int > FindFormat(const wxString &format, bool compareWithCase=false) const
Returns first pair of [exportPlugin, formatIndex], such that: exportPlugin->GetFormatInfo(formatIndex...
static ExportPluginRegistry & Get()
ExportTaskBuilder & SetPlugin(const ExportPlugin *plugin, int format=0) noexcept
Definition: Export.cpp:59
ExportTaskBuilder & SetParameters(ExportProcessor::Parameters parameters) noexcept
Definition: Export.cpp:47
ExportTaskBuilder & SetNumChannels(unsigned numChannels) noexcept
Definition: Export.cpp:53
ExportTaskBuilder & SetSampleRate(double sampleRate) noexcept
Definition: Export.cpp:72
ExportTaskBuilder & SetFileName(const wxFileName &filename)
Definition: Export.cpp:33
ExportTaskBuilder & SetRange(double t0, double t1, bool selectedOnly=false) noexcept
Definition: Export.cpp:39
static TrackIterRange< const WaveTrack > FindExportWaveTracks(const TrackList &tracks, bool selectedOnly)
Definition: ExportUtils.cpp:23
FILES_API const FileType AudacityProjects
Definition: FileNames.h:71
static FormatterContext EmptyContext()
static void ShowHelp(wxWindow *parent, const FilePath &localFileName, const URLString &remoteURL, bool bModal=false, bool alwaysDefaultBrowser=false)
Definition: HelpSystem.cpp:231
void SetValue(double newValue)
std::vector< MessageColumn > MessageTable
void Stop(bool stopStream=true)
static WritableSampleTrackArray ChooseExistingRecordingTracks(AudacityProject &proj, bool selectedOnly, double targetRate=RATE_NOT_SELECTED)
Find suitable tracks to record into, or return an empty array.
static ProjectAudioManager & Get(AudacityProject &project)
void OnRecord(bool altAppearance)
static ProjectFileIO & Get(AudacityProject &project)
const FilePath & GetFileName() const
bool IsModified() const
static ProjectFileManager & Get(AudacityProject &project)
static ProjectHistory & Get(AudacityProject &project)
static ProjectManager & Get(AudacityProject &project)
void SetSkipSavePrompt(bool bSkip)
static ProjectRate & Get(AudacityProject &project)
Definition: ProjectRate.cpp:28
double GetRate() const
Definition: ProjectRate.cpp:53
static ProjectSettings & Get(AudacityProject &project)
Generates classes whose instances register items at construction.
Definition: Registry.h:388
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
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
Definition: ShuttleGui.h:640
Specialization of Setting for strings.
Definition: Prefs.h:370
ProgressResult UpdateProgress()
Dialog for Timer Record, i.e., timed or long recording.
void OnAutoSaveCheckBox_Change(wxCommandEvent &event)
wxDatePickerCtrl * m_pDatePickerCtrl_Start
NumericTextCtrl * m_pTimeTextCtrl_Duration
void OnHelpButtonClick(wxCommandEvent &event)
void OnTimeText_Duration(wxCommandEvent &event)
NumericTextCtrl * m_pTimeTextCtrl_End
AudacityProject & mProject
TranslatableString GetDisplayDate(wxDateTime &dt)
wxButton * m_pTimerSavePathButtonCtrl
void EnableDisableAutoControls(bool bEnable, int iControlGoup)
wxCheckBox * m_pTimerAutoSaveCheckBoxCtrl
int ExecutePostRecordActions(bool bWasStopped)
void OnOK(wxCommandEvent &event)
void PopulateOrExchange(ShuttleGui &S)
NumericTextCtrl * m_pTimeTextCtrl_Start
wxTextCtrlWrapper * m_pTimerExportPathTextCtrl
wxDateTime m_DateTime_Start
void OnAutoExportPathButton_Click(wxCommandEvent &event)
void OnAutoExportCheckBox_Change(wxCommandEvent &event)
bool TransferDataFromWindow() override
wxChoice * m_pTimerAfterCompleteChoiceCtrl
wxFileName m_fnAutoSaveFile
void OnDatePicker_End(wxDateEvent &event)
ProgressResult WaitForStart()
wxTextCtrlWrapper * NewPathControl(wxWindow *wParent, const int iID, const TranslatableString &sCaption, const TranslatableString &sValue)
wxCheckBox * m_pTimerAutoExportCheckBoxCtrl
wxFileName m_fnAutoExportFile
void OnTimeText_Start(wxCommandEvent &event)
wxTimeSpan m_TimeSpan_Duration
wxDatePickerCtrl * m_pDatePickerCtrl_End
void OnAutoSavePathButton_Click(wxCommandEvent &event)
wxTextCtrlWrapper * m_pTimerSavePathTextCtrl
ExportProcessor::Parameters m_AutoExportParameters
void OnTimeText_End(wxCommandEvent &event)
wxButton * m_pTimerExportPathButtonCtrl
void OnTimer(wxTimerEvent &event)
int RunWaitDialog()
Runs the wait for start dialog. Returns false if the user clicks stop.
void OnDatePicker_Start(wxDateEvent &event)
ProgressResult PreActionDelay(int iActionIndex, TimerRecordCompletedActions eCompletedActions)
void Bind(wxFileName &filename, wxString &format, int &sampleRate, int &channels, ExportProcessor::Parameters &paramters)
bool empty() const
Definition: Track.cpp:758
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:314
Holds a msgid for the translation catalog; may also bind format arguments.
wxString Translation() const
static UndoManager & Get(AudacityProject &project)
Definition: UndoManager.cpp:71
An alternative to using wxWindowAccessible, which in wxWidgets 3.1.1 contained GetParent() which was ...
virtual bool Flush() noexcept=0
virtual bool Write(const wxString &key, bool value)=0
long ReadLong(const wxString &key, long defaultValue) const
bool ReadBool(const wxString &key, bool defaultValue) const
virtual bool Read(const wxString &key, bool *value) const =0
void SetReadOnly(bool readonly=true)
ProgressResult
Definition: BasicUI.h:148
std::unique_ptr< WindowPlacement > FindFocus()
Find the window that is accepting keyboard input, if any.
Definition: BasicUI.h:375
IMPORT_EXPORT_API ExportResult Show(ExportTask exportTask)
void ExceptionWrappedCall(Callable callable)
FILES_API FilePath FindDefaultPath(Operation op)
constexpr auto Command
Definition: MenuRegistry.h:456
double GetRate(const Track &track)
Definition: TimeTrack.cpp:182
void OnTimerRecord(const CommandContext &context)