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
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
218 ShuttleGui S(this, eIsCreating);
219 this->PopulateOrExchange(S);
220
221 // Set initial focus to "1" of "01h" in Duration NumericTextCtrl,
222 // instead of OK button (default).
223 m_pTimeTextCtrl_Duration->SetFocus();
224 m_pTimeTextCtrl_Duration->SetFieldFocus(3);
225
226 m_timer.SetOwner(this, TIMER_ID);
227 m_timer.Start(kSlowTimerInterval);
228}
229
231
232void TimerRecordDialog::OnTimer(wxTimerEvent& WXUNUSED(event))
233{
234 wxDateTime dateTime_UNow = wxDateTime::UNow();
235 if (m_DateTime_Start < dateTime_UNow) {
236 m_DateTime_Start = dateTime_UNow;
239 this->UpdateEnd(); // Keep Duration constant and update End for changed Start.
240 }
241}
242
243void TimerRecordDialog::OnDatePicker_Start(wxDateEvent& WXUNUSED(event))
244{
246 double dTime = m_pTimeTextCtrl_Start->GetValue();
247 long hr = (long)(dTime / 3600.0);
248 long min = (long)((dTime - (hr * 3600.0)) / 60.0);
249 long sec = (long)(dTime - (hr * 3600.0) - (min * 60.0));
250 m_DateTime_Start.SetHour(hr);
251 m_DateTime_Start.SetMinute(min);
252 m_DateTime_Start.SetSecond(sec);
253
254 // User might have had the dialog up for a while, or
255 // had a future day, set hour of day less than now's, then changed day to today.
256 wxTimerEvent dummyTimerEvent;
257 this->OnTimer(dummyTimerEvent);
258
259 // Always update End for changed Start, keeping Duration constant.
260 // Note that OnTimer sometimes calls UpdateEnd, so sometimes this is redundant,
261 // but OnTimer doesn't need to always call UpdateEnd, but we must here.
262 this->UpdateEnd();
263}
264
265void TimerRecordDialog::OnTimeText_Start(wxCommandEvent& WXUNUSED(event))
266{
267 //v NumericTextCtrl doesn't implement upper ranges, i.e.,
268 // if I tell it "024 h 060 m 060 s", then
269 // user increments the hours past 23, it rolls over to 0
270 // (although if you increment below 0, it stays at 0).
271 // So instead, set the max to 99 and just catch hours > 24 and fix the ctrls.
272 double dTime = m_pTimeTextCtrl_Start->GetValue();
273 long days = (long)(dTime / (24.0 * 3600.0));
274 if (days > 0) {
275 dTime -= (double)days * 24.0 * 3600.0;
276 m_DateTime_Start += wxTimeSpan::Days(days);
279 }
280
281 wxDateEvent dummyDateEvent;
282 this->OnDatePicker_Start(dummyDateEvent);
283}
284
285void TimerRecordDialog::OnDatePicker_End(wxDateEvent& WXUNUSED(event))
286{
288 double dTime = m_pTimeTextCtrl_End->GetValue();
289 long hr = (long)(dTime / 3600.0);
290 long min = (long)((dTime - (hr * 3600.0)) / 60.0);
291 long sec = (long)(dTime - (hr * 3600.0) - (min * 60.0));
292 m_DateTime_End.SetHour(hr);
293 m_DateTime_End.SetMinute(min);
294 m_DateTime_End.SetSecond(sec);
295
296 // DatePickerCtrls use SetRange to make sure End is never less than Start, but
297 // need to implement it for the TimeTextCtrls.
302 }
303
304 this->UpdateDuration(); // Keep Start constant and update Duration for changed End.
305}
306
307void TimerRecordDialog::OnTimeText_End(wxCommandEvent& WXUNUSED(event))
308{
309 //v NumericTextCtrl doesn't implement upper ranges, i.e.,
310 // if I tell it "024 h 060 m 060 s", then
311 // user increments the hours past 23, it rolls over to 0
312 // (although if you increment below 0, it stays at 0).
313 // So instead, set the max to 99 and just catch hours > 24 and fix the ctrls.
314 double dTime = m_pTimeTextCtrl_End->GetValue();
315 long days = (long)(dTime / (24.0 * 3600.0));
316 if (days > 0) {
317 dTime -= (double)days * 24.0 * 3600.0;
318 m_DateTime_End += wxTimeSpan::Days(days);
321 }
322
323 wxDateEvent dummyDateEvent;
324 this->OnDatePicker_End(dummyDateEvent);
325}
326
327void TimerRecordDialog::OnTimeText_Duration(wxCommandEvent& WXUNUSED(event))
328{
329 double dTime = m_pTimeTextCtrl_Duration->GetValue();
330 long hr = (long)(dTime / 3600.0);
331 long min = (long)((dTime - (hr * 3600.0)) / 60.0);
332 long sec = (long)(dTime - (hr * 3600.0) - (min * 60.0));
333 m_TimeSpan_Duration = wxTimeSpan(hr, min, sec); //v milliseconds?
334
335 this->UpdateEnd(); // Keep Start constant and update End for changed Duration.
336}
337
338// New events for timer recording automation
339void TimerRecordDialog::OnAutoSavePathButton_Click(wxCommandEvent& WXUNUSED(event))
340{
341 auto &projectFileIO = ProjectFileIO::Get(mProject);
342
343 wxString fName = SelectFile(FileNames::Operation::Export,
344 XO("Save Timer Recording As"),
345 m_fnAutoSaveFile.GetPath(),
346 m_fnAutoSaveFile.GetFullName(),
347 wxT("aup3"),
349 wxFD_SAVE | wxRESIZE_BORDER,
350 this);
351
352 if (fName.empty())
353 return;
354
355 // If project already exists then abort - we do not allow users to overwrite an existing project
356 // unless it is the current project.
357 if (wxFileExists(fName) && (projectFileIO.GetFileName() != fName)) {
359 nullptr,
360 XO("The selected file name could not be used\nfor Timer Recording because it \
361would overwrite another project.\nPlease try again and select an original name."),
362 XO("Error Saving Timer Recording Project"),
363 wxOK|wxICON_ERROR );
364 m.ShowModal();
365 return;
366 }
367
368 // Set this boolean to false so we now do a SaveAs at the end of the recording
369 // unless we're saving the current project.
370 m_bProjectAlreadySaved = projectFileIO.GetFileName() == fName? true : false;
371
372 m_fnAutoSaveFile = fName;
373 m_fnAutoSaveFile.SetExt(wxT("aup3"));
374 this->UpdateTextBoxControls();
375}
376
377void TimerRecordDialog::OnAutoExportPathButton_Click(wxCommandEvent& WXUNUSED(event))
378{
379 // Set the options required
380 TimerRecordExportDialog exportDialog(mProject, this);
381 exportDialog.Bind(
387
388 if(exportDialog.ShowModal() != wxID_OK)
389 return;
390
391 m_pTimerExportPathTextCtrl->SetValue(m_fnAutoExportFile.GetFullPath());
392
393 // Update the text controls
394 this->UpdateTextBoxControls();
395}
396
397void TimerRecordDialog::OnAutoSaveCheckBox_Change(wxCommandEvent& WXUNUSED(event)) {
399}
400
401void TimerRecordDialog::OnAutoExportCheckBox_Change(wxCommandEvent& WXUNUSED(event)) {
403}
404
405void TimerRecordDialog::OnHelpButtonClick(wxCommandEvent& WXUNUSED(event))
406{
407 HelpSystem::ShowHelp(this, L"Timer_Record", true);
408}
409
410void TimerRecordDialog::OnOK(wxCommandEvent& WXUNUSED(event))
411{
413 if (!m_TimeSpan_Duration.IsPositive())
414 {
416 XO("Duration is zero. Nothing will be recorded."),
417 XO("Error in Duration"),
418 wxICON_EXCLAMATION | wxOK);
419 return;
420 }
421
422 // Validate that we have a Save and/or Export path setup if the appropriate check box is ticked
423 wxString sTemp = m_fnAutoSaveFile.GetFullPath();
424 if (m_pTimerAutoSaveCheckBoxCtrl->IsChecked()) {
425 if (!m_fnAutoSaveFile.IsOk() || m_fnAutoSaveFile.IsDir()) {
427 XO("Automatic Save path is invalid."),
428 XO("Error in Automatic Save"),
429 wxICON_EXCLAMATION | wxOK);
430 return;
431 }
432 }
433 if (m_pTimerAutoExportCheckBoxCtrl->IsChecked()) {
434 if (!m_fnAutoExportFile.IsOk() || m_fnAutoExportFile.IsDir()) {
436 XO("Automatic Export path is invalid."),
437 XO("Error in Automatic Export"),
438 wxICON_EXCLAMATION | wxOK);
439 return;
440 }
441 }
442
443 // MY: Estimate here if we have enough disk space to
444 // complete this Timer Recording.
445 // If we don't think there is enough space then ask the user
446 // if they want to continue.
447 // We don't stop the user from starting the recording
448 // as its possible that they plan to free up some
449 // space before the recording begins
450 auto &projectManager = ProjectManager::Get( mProject );
451
452 // How many minutes do we have left on the disc?
453 int iMinsLeft = projectManager.GetEstimatedRecordingMinsLeftOnDisk();
454
455 // How many minutes will this recording require?
456 int iMinsRecording = m_TimeSpan_Duration.GetMinutes();
457
458 // Do we have enough space?
459 if (iMinsRecording >= iMinsLeft) {
460
461 // Format the strings
462 auto sRemainingTime = projectManager.GetHoursMinsString(iMinsLeft);
463 auto sPlannedTime = projectManager.GetHoursMinsString(iMinsRecording);
464
465 // Create the message string
466 auto sMessage = XO(
467"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")
468 .Format( sPlannedTime, sRemainingTime );
469
470 AudacityMessageDialog dlgMessage(
471 nullptr,
472 sMessage,
473 XO("Timer Recording Disk Space Warning"),
474 wxYES_NO | wxNO_DEFAULT | wxICON_WARNING);
475 if (dlgMessage.ShowModal() != wxID_YES ) {
476 // User decided not to continue - bail out!
477 return;
478 }
479 }
480
481 m_timer.Stop(); // Don't need to keep updating m_DateTime_Start to prevent backdating.
482 this->EndModal(wxID_OK);
483 wxLongLong duration = m_TimeSpan_Duration.GetSeconds();
484 // this will assert if the duration won't fit in a long
485 gPrefs->Write(wxT("/TimerRecord/LastDuration"), duration.ToLong());
486 gPrefs->Flush();
487}
488
489void TimerRecordDialog::EnableDisableAutoControls(bool bEnable, int iControlGoup) {
490
491 if (iControlGoup == CONTROL_GROUP_EXPORT) {
492 m_pTimerExportPathTextCtrl->Enable( bEnable );
493 m_pTimerExportPathButtonCtrl->Enable( bEnable);
494 } else if (iControlGoup == CONTROL_GROUP_SAVE) {
495 m_pTimerSavePathTextCtrl->Enable( bEnable);
496 m_pTimerSavePathButtonCtrl->Enable(bEnable );
497 }
498
499 // Enable or disable the Choice box - if there is no Save or Export then this will be disabled
500 if (m_pTimerAutoSaveCheckBoxCtrl->GetValue() || m_pTimerAutoExportCheckBoxCtrl->GetValue()) {
502 } else {
505 }
506}
507
509 // Will update the text box controls
510 m_pTimerSavePathTextCtrl->SetValue(m_fnAutoSaveFile.GetFullPath());
511 m_pTimerExportPathTextCtrl->SetValue(m_fnAutoExportFile.GetFullPath());
512
513 // MY: Ensure we still display "Current Project" if this has already been saved
515 m_pTimerSavePathTextCtrl->SetValue(_("Current Project"));
516 }
517}
518
522{
523 auto updateResult = ProgressResult::Success;
524
525 const auto gAudioIO = AudioIO::Get();
526 gAudioIO->DelayActions(true);
527 {
528 auto cleanup = finally([gAudioIO]{ gAudioIO->DelayActions(false); });
529
530 if (m_DateTime_Start > wxDateTime::UNow())
531 updateResult = this->WaitForStart();
532
533 if (updateResult != ProgressResult::Success) {
534 // Don't proceed, but don't treat it as canceled recording. User just canceled waiting.
536 } else {
537 // Record for specified time.
539 bool bIsRecording = true;
540
541 auto sPostAction = Verbatim(
542 m_pTimerAfterCompleteChoiceCtrl->GetStringSelection() );
543
544 // Two column layout.
546 {
547 XO("Recording start:") ,
548 XO("Duration:") ,
549 XO("Recording end:") ,
550 {} ,
551 XO("Automatic Save enabled:") ,
552 XO("Automatic Export enabled:") ,
553 XO("Action after Timer Recording:") ,
554 },
555 {
557 Verbatim( m_TimeSpan_Duration.Format() ),
559 {} ,
560 (m_bAutoSaveEnabled ? XO("Yes") : XO("No")) ,
561 (m_bAutoExportEnabled ? XO("Yes") : XO("No")) ,
562 sPostAction ,
563 }
564 };
565
567 progress(m_TimeSpan_Duration.GetMilliseconds().GetValue(),
568 XO("Audacity Timer Record Progress"),
569 columns,
571
572 // Make sure that start and end time are updated, so we always get the full
573 // duration, even if there's some delay getting here.
574 wxTimerEvent dummyTimerEvent;
575 this->OnTimer(dummyTimerEvent);
576
577 // Loop for progress display during recording.
578 while (bIsRecording && (updateResult == ProgressResult::Success)) {
579 updateResult = progress.UpdateProgress();
580 using namespace std::chrono;
581 std::this_thread::sleep_for(kTimerInterval);
582 bIsRecording = (wxDateTime::UNow() <= m_DateTime_End); // Call UNow() again for extra accuracy...
583 }
584 }
585 }
586
587 // Must do this AFTER the timer project dialog has been deleted to ensure the application
588 // responds to the AUDIOIO events...see not about bug #334 in the ProgressDialog constructor.
590
591 // Let the caller handle cancellation or failure from recording progress.
592 if (updateResult == ProgressResult::Cancelled || updateResult == ProgressResult::Failed)
594
595 return ExecutePostRecordActions((updateResult == ProgressResult::Stopped));
596}
597
599 // MY: We no longer automatically (and silently) call ->Save() when the
600 // timer recording is completed. We can now Save and/or Export depending
601 // on the options selected by the user.
602 // Once completed, we can also close Audacity, restart the system or
603 // shutdown the system.
604 // If there was any error with the auto save or export then we will not do
605 // the actions requested and instead present an error mesasge to the user.
606 // Finally, if there is no post-record action selected then we output
607 // a dialog detailing what has been carried out instead.
608
609 bool bSaveOK = false;
610 bool bExportOK = false;
611 int iPostRecordAction = m_pTimerAfterCompleteChoiceCtrl->GetSelection();
612 int iOverriddenAction = iPostRecordAction;
613 bool bErrorOverride = false;
614
615 // Do Automatic Save?
616 if (m_bAutoSaveEnabled) {
617
618 auto &projectFileManager = ProjectFileManager::Get( mProject );
619 // MY: If this project has already been saved then simply execute a Save here
621 bSaveOK = projectFileManager.Save();
622 } else {
623 bSaveOK = projectFileManager.SaveFromTimerRecording(m_fnAutoSaveFile);
624 }
625 }
626
627 // Do Automatic Export?
629 const auto& tracks = TrackList::Get(mProject);
630 if(ExportUtils::FindExportWaveTracks(tracks, false).empty())
631 {
632 ShowExportErrorDialog(XO("All audio is muted."), XO("Warning"), false);
633 bExportOK = true;
634 }
635 else
636 {
637 const auto t0 = std::max(.0, tracks.GetStartTime());
638
639 auto [exportPlugin, formatIndex] =
641
642 if(exportPlugin != nullptr)
643 {
644 auto builder = ExportTaskBuilder {}
647 .SetRange(t0, tracks.GetEndTime(), false)
650 .SetPlugin(exportPlugin, formatIndex);
651
653 {
654 const auto result = ExportProgressUI::Show(builder.Build(mProject));
655 bExportOK = result == ExportResult::Success ||
656 result == ExportResult::Stopped;
657 });
658
659 if(bExportOK)
660 {
663 }
664 }
665 }
666 }
667
668 // Check if we need to override the post recording action
669 bErrorOverride = ((m_bAutoSaveEnabled && !bSaveOK) || (m_bAutoExportEnabled && !bExportOK));
670 if (bErrorOverride || bWasStopped) {
671 iPostRecordAction = POST_TIMER_RECORD_NOTHING;
672 }
673
674 if (iPostRecordAction == POST_TIMER_RECORD_NOTHING) {
675 // If there is no post-record action then we can show a message indicating what has been done
676
677 auto sMessage = (bWasStopped ? XO("Timer Recording stopped.") :
678 XO("Timer Recording completed."));
679
680 if (m_bAutoSaveEnabled) {
681 if (bSaveOK) {
682 sMessage = XO("%s\n\nRecording saved: %s").Format(
683 sMessage, m_fnAutoSaveFile.GetFullPath());
684 } else {
685 sMessage = XO("%s\n\nError saving recording.").Format( sMessage );
686 }
687 }
689 if (bExportOK) {
690 sMessage = XO("%s\n\nRecording exported: %s").Format(
691 sMessage, m_fnAutoExportFile.GetFullPath());
692 } else {
693 sMessage = XO("%s\n\nError exporting recording.").Format( sMessage );
694 }
695 }
696
697 if (bErrorOverride) {
698
699 if ((iOverriddenAction != iPostRecordAction) &&
700 (iOverriddenAction != POST_TIMER_RECORD_NOTHING)) {
701 // Inform the user that we have overridden the selected action
702 sMessage = XO("%s\n\n'%s' has been canceled due to the error(s) noted above.").Format(
703 sMessage,
704 m_pTimerAfterCompleteChoiceCtrl->GetString(iOverriddenAction));
705 }
706
707 // Show Error Message Box
709 sMessage,
710 XO("Error"),
711 wxICON_EXCLAMATION | wxOK);
712 } else {
713
714 if (bWasStopped && (iOverriddenAction != POST_TIMER_RECORD_NOTHING)) {
715 sMessage = XO("%s\n\n'%s' has been canceled as the recording was stopped.").Format(
716 sMessage,
717 m_pTimerAfterCompleteChoiceCtrl->GetString(iOverriddenAction));
718 }
719
721 sMessage,
722 XO("Timer Recording"),
723 wxICON_INFORMATION | wxOK);
724 }
725 }
726
727 // MY: Lets do some actions that only apply to Exit/Restart/Shutdown
728 if (iPostRecordAction >= POST_TIMER_RECORD_CLOSE) {
729 do {
730
731 // Set the flags as appropriate based on what we have done
732 wxUint32 eActionFlags = TR_ACTION_NOTHING;
733 if (m_bAutoSaveEnabled && bSaveOK) {
734 eActionFlags |= TR_ACTION_SAVED;
735 }
736 if (m_bAutoExportEnabled && bExportOK) {
737 eActionFlags |= TR_ACTION_EXPORTED;
738 }
739
740 // Lets show a warning dialog telling the user what is about to happen.
741 // If the user no longer wants to carry out this action then they can click
742 // Cancel and we will do POST_TIMER_RECORD_NOTHING instead.
743 auto iDelayOutcome = PreActionDelay(iPostRecordAction, (TimerRecordCompletedActions)eActionFlags);
744 if (iDelayOutcome != ProgressResult::Success) {
745 // Cancel the action!
746 iPostRecordAction = POST_TIMER_RECORD_NOTHING;
747 break;
748 }
749 } while (false);
750 }
751
752 // Return the action as required
753 return iPostRecordAction;
754}
755
757{
758#if defined(__WXMSW__)
759 // On Windows, wxWidgets uses the system date control and it displays the
760 // date based on the Windows locale selected by the user. But, wxDateTime
761 // using the strftime function to return the formatted date. Since the
762 // default locale for the Windows CRT environment is "C", the dates come
763 // back in a different format.
764 //
765 // So, we make direct Windows calls to format the date like it the date
766 // control.
767 //
768 // (Most of this taken from src/msw/datectrl.cpp)
769
770 const wxDateTime::Tm tm(dt.GetTm());
771 SYSTEMTIME st;
772 wxString s;
773 int len;
774
775 st.wYear = (WXWORD)tm.year;
776 st.wMonth = (WXWORD)(tm.mon - wxDateTime::Jan + 1);
777 st.wDay = tm.mday;
778 st.wDayOfWeek = st.wMinute = st.wSecond = st.wMilliseconds = 0;
779
780 len = ::GetDateFormat(LOCALE_USER_DEFAULT,
781 DATE_SHORTDATE,
782 &st,
783 NULL,
784 NULL,
785 0);
786 if (len > 0) {
787 len = ::GetDateFormat(LOCALE_USER_DEFAULT,
788 DATE_SHORTDATE,
789 &st,
790 NULL,
791 wxStringBuffer(s, len),
792 len);
793 if (len > 0) {
794 s += wxT(" ") + dt.FormatTime();
795 return Verbatim( s );
796 }
797 }
798#endif
799
800 // Use default formatting
801wxPrintf(wxT("%s\n"), dt.Format());
802 return Verbatim( dt.FormatDate() + wxT(" ") + dt.FormatTime() );
803}
804
806 wxWindow *wParent, const int iID,
807 const TranslatableString &sCaption, const TranslatableString &sValue)
808{
809 wxTextCtrlWrapper * pTextCtrl;
810 wxASSERT(wParent); // to justify safenew
811 pTextCtrl = safenew wxTextCtrlWrapper(wParent, iID, sValue.Translation());
812 pTextCtrl->SetName(sCaption.Translation());
813 return pTextCtrl;
814}
815
817{
818 bool bAutoSave = gPrefs->ReadBool("/TimerRecord/AutoSave", false);
819 bool bAutoExport = gPrefs->ReadBool("/TimerRecord/AutoExport", false);
820 int iPostTimerRecordAction = gPrefs->ReadLong("/TimerRecord/PostAction", 0);
821
822 S.SetBorder(5);
823 using Options = NumericTextCtrl::Options;
824 /* i18n-hint a format string for hours, minutes, and seconds */
825 auto strFormat = XO("099 h 060 m 060 s");
826 /* i18n-hint a format string for days, hours, minutes, and seconds */
827 auto strFormat1 = XO("099 days 024 h 060 m 060 s");
828
829 S.StartMultiColumn(2, wxCENTER);
830 {
831 S.StartVerticalLay(true);
832 {
833 /* i18n-hint: This string is used to configure the controls for times when the recording is
834 * started and stopped. As such it is important that only the alphabetic parts of the string
835 * are translated, with the numbers left exactly as they are.
836 * The 'h' indicates the first number displayed is hours, the 'm' indicates the second number
837 * displayed is minutes, and the 's' indicates that the third number displayed is seconds.
838 */
839 S.StartStatic(XO("Start Date and Time"), true);
840 {
842 safenew wxDatePickerCtrl(S.GetParent(), // wxWindow *parent,
843 ID_DATEPICKER_START, // wxWindowID id,
844 m_DateTime_Start); // const wxDateTime& dt = wxDefaultDateTime,
845 // const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDP_DEFAULT | wxDP_SHOWCENTURY, const wxValidator& validator = wxDefaultValidator, const wxString& name = "datectrl")
846 m_pDatePickerCtrl_Start->SetRange(wxDateTime::Today(), wxInvalidDateTime); // No backdating.
847#if wxUSE_ACCESSIBILITY
848 m_pDatePickerCtrl_Start->SetAccessible( safenew DatePickerCtrlAx(m_pDatePickerCtrl_Start));
849#endif
850 S.Name(XO("Start Date"))
851 .AddWindow(m_pDatePickerCtrl_Start);
852
855 {}, 0,
856 Options{}
857 .MenuEnabled(false)
858 .CustomFormat(strFormat)
860 S.Name(XO("Start Time"))
861 .AddWindow(m_pTimeTextCtrl_Start);
862 }
863 S.EndStatic();
864
865 S.StartStatic(XO("End Date and Time"), true);
866 {
868 safenew wxDatePickerCtrl(S.GetParent(), // wxWindow *parent,
869 ID_DATEPICKER_END, // wxWindowID id,
870 m_DateTime_End); // const wxDateTime& dt = wxDefaultDateTime,
871 // const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize,
872 // long style = wxDP_DEFAULT | wxDP_SHOWCENTURY,
873 // const wxValidator& validator = wxDefaultValidator,
874 // const wxString& name = "datectrl")
875 m_pDatePickerCtrl_End->SetRange(m_DateTime_Start, wxInvalidDateTime); // No backdating.
876#if wxUSE_ACCESSIBILITY
877 m_pDatePickerCtrl_End->SetAccessible( safenew DatePickerCtrlAx(m_pDatePickerCtrl_End));
878#endif
879 S.Name(XO("End Date"))
880 .AddWindow(m_pDatePickerCtrl_End);
881
884 {}, 0,
885 Options{}
886 .MenuEnabled(false)
887 .CustomFormat(strFormat)
889 S.Name(XO("End Time"))
890 .AddWindow(m_pTimeTextCtrl_End);
891 }
892 S.EndStatic();
893
894 S.StartStatic(XO("Duration"), true);
895 {
898 {}, 0,
899 Options{}
900 .MenuEnabled(false)
901 .CustomFormat(strFormat1)
902 .Value(true, m_TimeSpan_Duration.GetSeconds().ToDouble()));
903 /* i18n-hint: This string is used to configure the controls which shows the recording
904 * duration. As such it is important that only the alphabetic parts of the string
905 * are translated, with the numbers left exactly as they are.
906 * The string 'days' indicates that the first number in the control will be the number of days,
907 * then the 'h' indicates the second number displayed is hours, the 'm' indicates the third
908 * number displayed is minutes, and the 's' indicates that the fourth number displayed is
909 * seconds.
910 */
911 S.Name(XO("Duration"))
912 .AddWindow(m_pTimeTextCtrl_Duration);
913 }
914 S.EndStatic();
915 }
916 S.EndVerticalLay();
917
918 S.StartVerticalLay(true);
919 {
920 S.StartStatic(XO("Automatic Save"), true);
921 {
922 // If checked, the project will be saved when the recording is completed
923 m_pTimerAutoSaveCheckBoxCtrl = S.Id(ID_AUTOSAVE_CHECKBOX).AddCheckBox(XXO("Enable &Automatic Save?"),
924 bAutoSave);
925 S.StartMultiColumn(3, wxEXPAND);
926 {
927 TranslatableString sInitialValue;
928 auto sSaveValue = ProjectFileIO::Get(mProject).GetFileName();
929 if (!sSaveValue.empty()) {
930 m_fnAutoSaveFile.Assign(sSaveValue);
931 sInitialValue = XO("Current Project");
932 }
933 S.AddPrompt(XXO("Save Project As:"));
935 S.GetParent(), ID_AUTOSAVEPATH_TEXT,
936 XO("Save Project As:"), sInitialValue);
938 S.AddWindow(m_pTimerSavePathTextCtrl);
939 m_pTimerSavePathButtonCtrl = S.Id(ID_AUTOSAVEPATH_BUTTON).AddButton(XXO("Select..."));
940 }
941 S.EndMultiColumn();
942 }
943 S.EndStatic();
944
945 S.StartStatic(XO("Automatic Export"), true);
946 {
947 m_pTimerAutoExportCheckBoxCtrl = S.Id(ID_AUTOEXPORT_CHECKBOX).AddCheckBox(XXO("Enable Automatic &Export?"), bAutoExport);
948 S.StartMultiColumn(3, wxEXPAND);
949 {
950 S.AddPrompt(XXO("Export Project As:"));
952 S.GetParent(), ID_AUTOEXPORTPATH_TEXT,
953 XO("Export Project As:"), {});
955 m_pTimerExportPathTextCtrl->SetValue(m_fnAutoExportFile.GetFullPath());
956 S.AddWindow(m_pTimerExportPathTextCtrl);
957 m_pTimerExportPathButtonCtrl = S.Id(ID_AUTOEXPORTPATH_BUTTON).AddButton(XXO("Select..."));
958 }
959 S.EndMultiColumn();
960 }
961 S.EndStatic();
962
963 S.StartStatic(XO("Options"), true);
964 {
965
966 S.StartMultiColumn(1, wxEXPAND);
967 {
968 S.SetStretchyCol( 0 );
969 m_pTimerAfterCompleteChoiceCtrl = S.AddChoice(XXO("After Recording completes:"),
970 {
971 XO("Do nothing") ,
972 XO("Exit Audacity") ,
973 #ifdef __WINDOWS__
974 XO("Restart system") ,
975 XO("Shutdown system") ,
976 #endif
977 },
978 iPostTimerRecordAction
979 );
980 }
981 S.EndMultiColumn();
982 }
983 S.EndStatic();
984
985 }
986 S.EndVerticalLay();
987 }
988 S.EndMultiColumn();
989
990 // MY: Added the help button here
991 S.AddStandardButtons(eOkButton | eCancelButton | eHelpButton);
992
993 Layout();
994 Fit();
995 SetMinSize(GetSize());
996 Center();
997
1000}
1001
1003{
1004 double dTime;
1005 long hr;
1006 long min;
1007 long sec;
1008
1011 hr = (long)(dTime / 3600.0);
1012 min = (long)((dTime - (hr * 3600.0)) / 60.0);
1013 sec = (long)(dTime - (hr * 3600.0) - (min * 60.0));
1014 m_DateTime_Start.SetHour(hr);
1015 m_DateTime_Start.SetMinute(min);
1016 m_DateTime_Start.SetSecond(sec);
1017
1019 dTime = m_pTimeTextCtrl_End->GetValue();
1020 hr = (long)(dTime / 3600.0);
1021 min = (long)((dTime - (hr * 3600.0)) / 60.0);
1022 sec = (long)(dTime - (hr * 3600.0) - (min * 60.0));
1023 m_DateTime_End.SetHour(hr);
1024 m_DateTime_End.SetMinute(min);
1025 m_DateTime_End.SetSecond(sec);
1026
1028
1029 // Pull the settings from the auto save/export controls and write to the pref file
1032
1033 // MY: Obtain the index from the choice control so we can save to the prefs file
1034 int iPostRecordAction = m_pTimerAfterCompleteChoiceCtrl->GetSelection();
1035
1036 // Save the options back to the prefs file
1037 gPrefs->Write("/TimerRecord/AutoSave", m_bAutoSaveEnabled);
1038 gPrefs->Write("/TimerRecord/AutoExport", m_bAutoExportEnabled);
1039 gPrefs->Write("/TimerRecord/PostAction", iPostRecordAction);
1040
1041 return true;
1042}
1043
1044// Update m_TimeSpan_Duration and ctrl based on m_DateTime_Start and m_DateTime_End.
1046{
1048 m_pTimeTextCtrl_Duration->SetValue(m_TimeSpan_Duration.GetSeconds().ToDouble());
1049}
1050
1051// Update m_DateTime_End and ctrls based on m_DateTime_Start and m_TimeSpan_Duration.
1053{
1054 //v Use remaining disk -> record time calcs from AudacityProject::OnTimer to set range?
1056 //wxLogDebug( "Time start %s end %s",
1057 // m_DateTime_Start.FormatISOCombined(' '),
1058 // m_DateTime_End.FormatISOCombined(' ') );
1059
1060 // Disable the range limitation (to fix Bug 1749 and 1978)
1061 // Otherwise SetVallue asserts when going back in time.
1062 m_pDatePickerCtrl_End->SetRange(wxInvalidDateTime, wxInvalidDateTime);
1064 // Re-enable range limitation to constrain user input.
1065 m_pDatePickerCtrl_End->SetRange(m_DateTime_Start, wxInvalidDateTime); // No backdating.
1066 m_pDatePickerCtrl_End->Refresh();
1068}
1069
1071{
1072 // MY: The Waiting For Start dialog now shows what actions will occur after recording has completed
1073 auto sPostAction = Verbatim(
1074 m_pTimerAfterCompleteChoiceCtrl->GetStringSelection() );
1075
1076 // Two column layout.
1078 {
1079 XO("Waiting to start recording at:") ,
1080 XO("Recording duration:") ,
1081 XO("Scheduled to stop at:") ,
1082 {} ,
1083 XO("Automatic Save enabled:") ,
1084 XO("Automatic Export enabled:") ,
1085 XO("Action after Timer Recording:") ,
1086 },
1087 {
1089 Verbatim( m_TimeSpan_Duration.Format() ),
1091 {} ,
1092 (m_bAutoSaveEnabled ? XO("Yes") : XO("No")) ,
1093 (m_bAutoExportEnabled ? XO("Yes") : XO("No")) ,
1094 sPostAction ,
1095 },
1096 };
1097
1098 wxDateTime startWait_DateTime = wxDateTime::UNow();
1099 wxTimeSpan waitDuration = m_DateTime_Start - startWait_DateTime;
1100 TimerProgressDialog progress(waitDuration.GetMilliseconds().GetValue(),
1101 XO("Audacity Timer Record - Waiting for Start"),
1102 columns,
1104 /* i18n-hint: "in" means after a duration of time,
1105 which is shown below this string */
1106 XO("Recording will commence in:"));
1107
1108 auto updateResult = ProgressResult::Success;
1109 bool bIsRecording = false;
1110 while (updateResult == ProgressResult::Success && !bIsRecording)
1111 {
1112 updateResult = progress.UpdateProgress();
1113 using namespace std::chrono;
1114 std::this_thread::sleep_for(kTimerInterval);
1115 bIsRecording = (m_DateTime_Start <= wxDateTime::UNow());
1116 }
1117 return updateResult;
1118}
1119
1121{
1123 ->GetString(iActionIndex) );
1124
1125 /* i18n-hint: %s is one of "Do nothing", "Exit Audacity", "Restart system",
1126 or "Shutdown system", and
1127 "in" means after a duration of time, shown below this string */
1128 auto sCountdownLabel = XO("%s in:").Format( sAction );
1129
1130 // Two column layout.
1132 {
1133 XO("Timer Recording completed.") ,
1134 {} ,
1135 XO("Recording Saved:") ,
1136 XO("Recording Exported:") ,
1137 XO("Action after Timer Recording:") ,
1138 },
1139 {
1140 {} ,
1141 {} ,
1142 ((eCompletedActions & TR_ACTION_SAVED) ? XO("Yes") : XO("No")) ,
1143 ((eCompletedActions & TR_ACTION_EXPORTED) ? XO("Yes") : XO("No")) ,
1144 sAction ,
1145 },
1146 };
1147
1148
1149 wxDateTime dtNow = wxDateTime::UNow();
1150 wxTimeSpan tsWait = wxTimeSpan(0, 1, 0, 0);
1151 wxDateTime dtActionTime = dtNow.Add(tsWait);
1152
1153 TimerProgressDialog dlgAction(tsWait.GetMilliseconds().GetValue(),
1154 XO("Audacity Timer Record - Waiting"),
1155 columns,
1157 sCountdownLabel);
1158
1159 auto iUpdateResult = ProgressResult::Success;
1160 bool bIsTime = false;
1161 while (iUpdateResult == ProgressResult::Success && !bIsTime)
1162 {
1163 iUpdateResult = dlgAction.UpdateProgress();
1164 using namespace std::chrono;
1165 std::this_thread::sleep_for(kTimerInterval);
1166 bIsTime = (dtActionTime <= wxDateTime::UNow());
1167 }
1168 return iUpdateResult;
1169}
1170
1171// Register a menu item
1172
1173#include "CommandContext.h"
1174#include "MenuRegistry.h"
1175#include "CommonCommandFlags.h"
1176#include "Project.h"
1177#include "ProjectHistory.h"
1178#include "ProjectSettings.h"
1179#include "UndoManager.h"
1180
1181namespace {
1182void OnTimerRecord(const CommandContext &context)
1183{
1184 auto &project = context.project;
1185 const auto &settings = ProjectSettings::Get( project );
1186 auto &undoManager = UndoManager::Get( project );
1187 auto &window = GetProjectFrame(project);
1188
1189 // MY: Due to improvements in how Timer Recording saves and/or exports
1190 // it is now safer to disable Timer Recording when there is more than
1191 // one open project.
1192 if (AllProjects{}.size() > 1) {
1194 XO(
1195"Timer Recording cannot be used with more than one open project.\n\nPlease close any additional projects and try again."),
1196 XO("Timer Recording"),
1197 wxICON_INFORMATION | wxOK);
1198 return;
1199 }
1200
1201 // MY: If the project has unsaved changes then we no longer allow access
1202 // to Timer Recording. This decision has been taken as the safest approach
1203 // preventing issues surrounding "dirty" projects when Automatic Save/Export
1204 // is used in Timer Recording.
1205 if ((undoManager.UnsavedChanges()) &&
1206 (!TrackList::Get(project).empty() || settings.EmptyCanBeDirty())) {
1208 XO(
1209"Timer Recording cannot be used while you have unsaved changes.\n\nPlease save or close this project and try again."),
1210 XO("Timer Recording"),
1211 wxICON_INFORMATION | wxOK);
1212 return;
1213 }
1214
1215 // We check the selected tracks to see if there is enough of them to accommodate
1216 // all input channels and all of them have the same sampling rate.
1217 // Those checks will be later performed by recording function anyway,
1218 // but we want to warn the user about potential problems from the very start.
1219 const auto selectedTracks{ GetPropertiesOfSelected(project) };
1220 const int rateOfSelected{ selectedTracks.rateOfSelected };
1221 const bool anySelected{ selectedTracks.anySelected };
1222 const bool allSameRate{ selectedTracks.allSameRate };
1223
1224 if (!allSameRate) {
1225 AudacityMessageBox(XO("The tracks selected "
1226 "for recording must all have the same sampling rate"),
1227 XO("Mismatched Sampling Rates"),
1228 wxICON_ERROR | wxCENTRE);
1229
1230 return;
1231 }
1232
1233 // Only need the size
1234 const auto existingTracks =
1236 rateOfSelected);
1237 if (existingTracks.empty()) {
1238 if (anySelected && rateOfSelected !=
1241 "Too few tracks are selected for recording at this sample rate.\n"
1242 "(Audacity requires two channels at the same sample rate for\n"
1243 "each stereo track)"),
1244 XO("Too Few Compatible Tracks Selected"),
1245 wxICON_ERROR | wxCENTRE);
1246
1247 return;
1248 }
1249 }
1250
1251 // We use this variable to display "Current Project" in the Timer Recording
1252 // save project field
1253 bool bProjectSaved = !ProjectFileIO::Get( project ).IsModified();
1254
1255 //we break the prompting and waiting dialogs into two sections
1256 //because they both give the user a chance to click cancel
1257 //and therefore remove the newly inserted track.
1258
1259 TimerRecordDialog dialog(
1260 &window, project, bProjectSaved); /* parent, project, project saved? */
1261 int modalResult = dialog.ShowModal();
1262 if (modalResult == wxID_CANCEL)
1263 {
1264 // Cancelled before recording - don't need to do anything.
1265 }
1266 else
1267 {
1268 // Bug #2382
1269 // Allow recording to start at current cursor position.
1270 #if 0
1271 // Timer Record should not record into a selection.
1272 bool bPreferNewTrack;
1273 gPrefs->Read("/GUI/PreferNewTrackRecord",&bPreferNewTrack, false);
1274 if (bPreferNewTrack) {
1275 window.Rewind(false);
1276 } else {
1277 window.SkipEnd(false);
1278 }
1279 #endif
1280
1281 int iTimerRecordingOutcome = dialog.RunWaitDialog();
1282 switch (iTimerRecordingOutcome) {
1284 // Canceled on the wait dialog
1286 break;
1288 // RunWaitDialog() shows the "wait for start" as well as "recording"
1289 // dialog if it returned POST_TIMER_RECORD_CANCEL it means the user
1290 // cancelled while the recording, so throw out the fresh track.
1291 // However, we can't undo it here because the PushState() is called in TrackPanel::OnTimer(),
1292 // which is blocked by this function.
1293 // so instead we mark a flag to undo it there.
1295 break;
1297 // No action required
1298 break;
1300 wxTheApp->CallAfter( []{
1301 // Simulate the application Exit menu item
1302 wxCommandEvent evt{ wxEVT_MENU, wxID_EXIT };
1303 wxTheApp->AddPendingEvent( evt );
1304 } );
1306 break;
1307
1308#ifdef __WINDOWS__
1309 case POST_TIMER_RECORD_RESTART:
1310 // Restart System
1312 system("shutdown /r /f /t 30");
1313 break;
1314 case POST_TIMER_RECORD_SHUTDOWN:
1315 // Shutdown System
1317 system("shutdown /s /f /t 30");
1318 break;
1319#endif
1320 }
1321 }
1322}
1323
1325
1326using namespace MenuRegistry;
1328 Command( wxT("TimerRecord"), XXO("&Timer Record..."),
1329 OnTimerRecord, CanStopFlags, wxT("Shift+T") ),
1330 { wxT("Transport/Basic/Record"),
1331 { OrderingHint::After, wxT("Record2ndChoice") } }
1332};
1333
1334}
wxT("CloseDown"))
int AudacityMessageBox(const TranslatableString &message, const TranslatableString &caption, long style, wxWindow *parent, int x, int y)
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:599
@ eCancelButton
Definition: ShuttleGui.h:600
@ eHelpButton
Definition: ShuttleGui.h:603
const auto tracks
const auto project
const int kSlowTimerInterval
@ 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
constexpr auto kTimerInterval
@ CONTROL_GROUP_SAVE
@ CONTROL_GROUP_EXPORT
#define TIMER_ID
static double wxDateTime_to_AudacityTime(wxDateTime &dateTime)
@ POST_TIMER_RECORD_CLOSE
@ POST_TIMER_RECORD_CANCEL
@ POST_TIMER_RECORD_CANCEL_WAIT
@ POST_TIMER_RECORD_NOTHING
TimerRecordCompletedActions
@ TR_ACTION_NOTHING
@ TR_ACTION_EXPORTED
@ TR_ACTION_SAVED
#define S(N)
Definition: ToChars.cpp:64
declares abstract base class Track, TrackList, and iterators over TrackList
static Settings & settings()
Definition: TrackInfo.cpp:69
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:18
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:233
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)
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:257
bool Read(T *pVar) const
overload of Read returning a boolean that is true if the value was previously defined *‍/
Definition: Prefs.h:205
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
Definition: ShuttleGui.h:630
Specialization of Setting for strings.
Definition: Prefs.h:368
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:955
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:347
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:373
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:196
void OnTimerRecord(const CommandContext &context)