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
41#include "AudioIO.h"
42#include "SelectFile.h"
43#include "ShuttleGui.h"
44#include "ProjectAudioManager.h"
45#include "ProjectFileIO.h"
46#include "ProjectFileManager.h"
47#include "ProjectManager.h"
48#include "ProjectRate.h"
49#include "Prefs.h"
50#include "Track.h"
52#include "widgets/HelpSystem.h"
56
57#if wxUSE_ACCESSIBILITY
59#endif
60
61#define TIMER_ID 7000
62
63enum { // control IDs
75};
76
77enum {
80};
81
82// The slow timer interval is used to update the start and end times, which only show
83// time to the nearest second. So we only need an update once a second.
84const int kSlowTimerInterval = 1000; // ms
85
86// This timer interval is used in some busy-wait loops and is much shorter.
87constexpr auto kTimerInterval = std::chrono::milliseconds{50};
88
89static double wxDateTime_to_AudacityTime(wxDateTime& dateTime)
90{
91 return (dateTime.GetHour() * 3600.0) + (dateTime.GetMinute() * 60.0) + dateTime.GetSecond();
92};
93
94
95// The purpose of the DatePickerCtrlAx class is to make to wxDatePickerCtrl more accessible for
96// the NVDA screen reader.
97// By default the msaa state of wxDatePickerCtrl is always normal (0x0), and this causes nvda not
98// to read the control when the user tabs to it. This class
99// modifies the state to be focusable + focused (when it's the focus).
100// Note that even with this class NVDA still doesn't read the NEW selected part of the control when left/right
101// arrow keys are used.
102
103#if wxUSE_ACCESSIBILITY
104
105class DatePickerCtrlAx final : public WindowAccessible
106{
107public:
108 DatePickerCtrlAx(wxDatePickerCtrl * ctrl) : WindowAccessible(ctrl), mCtrl(ctrl) {};
109
110 virtual ~ DatePickerCtrlAx() {};
111
112 // Returns a state constant.
113 wxAccStatus GetState(int childId, long *state) override;
114
115private:
116 wxDatePickerCtrl *mCtrl;
117};
118
119// Returns a state constant.
120wxAccStatus DatePickerCtrlAx::GetState(int WXUNUSED(childId), long *state)
121{
122 *state = wxACC_STATE_SYSTEM_FOCUSABLE;
123 *state |= (mCtrl == wxWindow::FindFocus() ? wxACC_STATE_SYSTEM_FOCUSED : 0);
124
125 return wxACC_OK;
126}
127
128#endif // wxUSE_ACCESSIBILITY
129
130
131BEGIN_EVENT_TABLE(TimerRecordDialog, wxDialogWrapper)
134
137
139
142
144
147
150
152
154 wxWindow* parent, AudacityProject &project, bool bAlreadySaved)
155: wxDialogWrapper(parent, -1, XO("Audacity Timer Record"), wxDefaultPosition,
156 wxDefaultSize, wxCAPTION)
157, mProject{ project }
158{
159 SetName();
160
161 m_DateTime_Start = wxDateTime::UNow();
162 long seconds; // default duration is 1 hour = 3600 seconds
163 gPrefs->Read(wxT("/TimerRecord/LastDuration"), &seconds, 3600);
164 m_TimeSpan_Duration = wxTimeSpan::Seconds(seconds);
165 m_DateTime_End = m_DateTime_Start + m_TimeSpan_Duration;
166
167 m_pDatePickerCtrl_Start = NULL;
168 m_pTimeTextCtrl_Start = NULL;
169
170 m_pDatePickerCtrl_End = NULL;
171 m_pTimeTextCtrl_End = NULL;
172
173 m_pTimeTextCtrl_Duration = NULL;
174
175 // Do we allow the user to change the Automatic Save file?
176 m_bProjectAlreadySaved = bAlreadySaved;
177
178 ShuttleGui S(this, eIsCreating);
179 this->PopulateOrExchange(S);
180
181 // Set initial focus to "1" of "01h" in Duration NumericTextCtrl,
182 // instead of OK button (default).
183 m_pTimeTextCtrl_Duration->SetFocus();
184 m_pTimeTextCtrl_Duration->SetFieldFocus(3);
185
186 m_timer.SetOwner(this, TIMER_ID);
187 m_timer.Start(kSlowTimerInterval);
188}
189
191{
192}
193
194void TimerRecordDialog::OnTimer(wxTimerEvent& WXUNUSED(event))
195{
196 wxDateTime dateTime_UNow = wxDateTime::UNow();
197 if (m_DateTime_Start < dateTime_UNow) {
198 m_DateTime_Start = dateTime_UNow;
201 this->UpdateEnd(); // Keep Duration constant and update End for changed Start.
202 }
203}
204
205void TimerRecordDialog::OnDatePicker_Start(wxDateEvent& WXUNUSED(event))
206{
208 double dTime = m_pTimeTextCtrl_Start->GetValue();
209 long hr = (long)(dTime / 3600.0);
210 long min = (long)((dTime - (hr * 3600.0)) / 60.0);
211 long sec = (long)(dTime - (hr * 3600.0) - (min * 60.0));
212 m_DateTime_Start.SetHour(hr);
213 m_DateTime_Start.SetMinute(min);
214 m_DateTime_Start.SetSecond(sec);
215
216 // User might have had the dialog up for a while, or
217 // had a future day, set hour of day less than now's, then changed day to today.
218 wxTimerEvent dummyTimerEvent;
219 this->OnTimer(dummyTimerEvent);
220
221 // Always update End for changed Start, keeping Duration constant.
222 // Note that OnTimer sometimes calls UpdateEnd, so sometimes this is redundant,
223 // but OnTimer doesn't need to always call UpdateEnd, but we must here.
224 this->UpdateEnd();
225}
226
227void TimerRecordDialog::OnTimeText_Start(wxCommandEvent& WXUNUSED(event))
228{
229 //v NumericTextCtrl doesn't implement upper ranges, i.e.,
230 // if I tell it "024 h 060 m 060 s", then
231 // user increments the hours past 23, it rolls over to 0
232 // (although if you increment below 0, it stays at 0).
233 // So instead, set the max to 99 and just catch hours > 24 and fix the ctrls.
234 double dTime = m_pTimeTextCtrl_Start->GetValue();
235 long days = (long)(dTime / (24.0 * 3600.0));
236 if (days > 0) {
237 dTime -= (double)days * 24.0 * 3600.0;
238 m_DateTime_Start += wxTimeSpan::Days(days);
241 }
242
243 wxDateEvent dummyDateEvent;
244 this->OnDatePicker_Start(dummyDateEvent);
245}
246
247void TimerRecordDialog::OnDatePicker_End(wxDateEvent& WXUNUSED(event))
248{
250 double dTime = m_pTimeTextCtrl_End->GetValue();
251 long hr = (long)(dTime / 3600.0);
252 long min = (long)((dTime - (hr * 3600.0)) / 60.0);
253 long sec = (long)(dTime - (hr * 3600.0) - (min * 60.0));
254 m_DateTime_End.SetHour(hr);
255 m_DateTime_End.SetMinute(min);
256 m_DateTime_End.SetSecond(sec);
257
258 // DatePickerCtrls use SetRange to make sure End is never less than Start, but
259 // need to implement it for the TimeTextCtrls.
264 }
265
266 this->UpdateDuration(); // Keep Start constant and update Duration for changed End.
267}
268
269void TimerRecordDialog::OnTimeText_End(wxCommandEvent& WXUNUSED(event))
270{
271 //v NumericTextCtrl doesn't implement upper ranges, i.e.,
272 // if I tell it "024 h 060 m 060 s", then
273 // user increments the hours past 23, it rolls over to 0
274 // (although if you increment below 0, it stays at 0).
275 // So instead, set the max to 99 and just catch hours > 24 and fix the ctrls.
276 double dTime = m_pTimeTextCtrl_End->GetValue();
277 long days = (long)(dTime / (24.0 * 3600.0));
278 if (days > 0) {
279 dTime -= (double)days * 24.0 * 3600.0;
280 m_DateTime_End += wxTimeSpan::Days(days);
283 }
284
285 wxDateEvent dummyDateEvent;
286 this->OnDatePicker_End(dummyDateEvent);
287}
288
289void TimerRecordDialog::OnTimeText_Duration(wxCommandEvent& WXUNUSED(event))
290{
291 double dTime = m_pTimeTextCtrl_Duration->GetValue();
292 long hr = (long)(dTime / 3600.0);
293 long min = (long)((dTime - (hr * 3600.0)) / 60.0);
294 long sec = (long)(dTime - (hr * 3600.0) - (min * 60.0));
295 m_TimeSpan_Duration = wxTimeSpan(hr, min, sec); //v milliseconds?
296
297 this->UpdateEnd(); // Keep Start constant and update End for changed Duration.
298}
299
300// New events for timer recording automation
301void TimerRecordDialog::OnAutoSavePathButton_Click(wxCommandEvent& WXUNUSED(event))
302{
303 auto &projectFileIO = ProjectFileIO::Get(mProject);
304
305 wxString fName = SelectFile(FileNames::Operation::Export,
306 XO("Save Timer Recording As"),
307 m_fnAutoSaveFile.GetPath(),
308 m_fnAutoSaveFile.GetFullName(),
309 wxT("aup3"),
311 wxFD_SAVE | wxRESIZE_BORDER,
312 this);
313
314 if (fName.empty())
315 return;
316
317 // If project already exists then abort - we do not allow users to overwrite an existing project
318 // unless it is the current project.
319 if (wxFileExists(fName) && (projectFileIO.GetFileName() != fName)) {
321 nullptr,
322 XO("The selected file name could not be used\nfor Timer Recording because it \
323would overwrite another project.\nPlease try again and select an original name."),
324 XO("Error Saving Timer Recording Project"),
325 wxOK|wxICON_ERROR );
326 m.ShowModal();
327 return;
328 }
329
330 // Set this boolean to false so we now do a SaveAs at the end of the recording
331 // unless we're saving the current project.
332 m_bProjectAlreadySaved = projectFileIO.GetFileName() == fName? true : false;
333
334 m_fnAutoSaveFile = fName;
335 m_fnAutoSaveFile.SetExt(wxT("aup3"));
336 this->UpdateTextBoxControls();
337}
338
339void TimerRecordDialog::OnAutoExportPathButton_Click(wxCommandEvent& WXUNUSED(event))
340{
341 Exporter eExporter{ mProject };
342
343 // Call the Exporter to set the options required
344 if (eExporter.SetAutoExportOptions()) {
345 // Populate the options so that we can destroy this instance of the Exporter
346 m_fnAutoExportFile = eExporter.GetAutoExportFileName();
347 m_iAutoExportFormat = eExporter.GetAutoExportFormat();
348 m_iAutoExportSubFormat = eExporter.GetAutoExportSubFormat();
349 m_iAutoExportFilterIndex = eExporter.GetAutoExportFilterIndex();
350
351 // Update the text controls
352 this->UpdateTextBoxControls();
353 }
354}
355
356void TimerRecordDialog::OnAutoSaveCheckBox_Change(wxCommandEvent& WXUNUSED(event)) {
358}
359
360void TimerRecordDialog::OnAutoExportCheckBox_Change(wxCommandEvent& WXUNUSED(event)) {
362}
363
364void TimerRecordDialog::OnHelpButtonClick(wxCommandEvent& WXUNUSED(event))
365{
366 HelpSystem::ShowHelp(this, L"Timer_Record", true);
367}
368
369void TimerRecordDialog::OnOK(wxCommandEvent& WXUNUSED(event))
370{
372 if (!m_TimeSpan_Duration.IsPositive())
373 {
375 XO("Duration is zero. Nothing will be recorded."),
376 XO("Error in Duration"),
377 wxICON_EXCLAMATION | wxOK);
378 return;
379 }
380
381 // Validate that we have a Save and/or Export path setup if the appropriate check box is ticked
382 wxString sTemp = m_fnAutoSaveFile.GetFullPath();
383 if (m_pTimerAutoSaveCheckBoxCtrl->IsChecked()) {
384 if (!m_fnAutoSaveFile.IsOk() || m_fnAutoSaveFile.IsDir()) {
386 XO("Automatic Save path is invalid."),
387 XO("Error in Automatic Save"),
388 wxICON_EXCLAMATION | wxOK);
389 return;
390 }
391 }
392 if (m_pTimerAutoExportCheckBoxCtrl->IsChecked()) {
393 if (!m_fnAutoExportFile.IsOk() || m_fnAutoExportFile.IsDir()) {
395 XO("Automatic Export path is invalid."),
396 XO("Error in Automatic Export"),
397 wxICON_EXCLAMATION | wxOK);
398 return;
399 }
400 }
401
402 // MY: Estimate here if we have enough disk space to
403 // complete this Timer Recording.
404 // If we don't think there is enough space then ask the user
405 // if they want to continue.
406 // We don't stop the user from starting the recording
407 // as its possible that they plan to free up some
408 // space before the recording begins
409 auto &projectManager = ProjectManager::Get( mProject );
410
411 // How many minutes do we have left on the disc?
412 int iMinsLeft = projectManager.GetEstimatedRecordingMinsLeftOnDisk();
413
414 // How many minutes will this recording require?
415 int iMinsRecording = m_TimeSpan_Duration.GetMinutes();
416
417 // Do we have enough space?
418 if (iMinsRecording >= iMinsLeft) {
419
420 // Format the strings
421 auto sRemainingTime = projectManager.GetHoursMinsString(iMinsLeft);
422 auto sPlannedTime = projectManager.GetHoursMinsString(iMinsRecording);
423
424 // Create the message string
425 auto sMessage = XO(
426"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")
427 .Format( sPlannedTime, sRemainingTime );
428
429 AudacityMessageDialog dlgMessage(
430 nullptr,
431 sMessage,
432 XO("Timer Recording Disk Space Warning"),
433 wxYES_NO | wxNO_DEFAULT | wxICON_WARNING);
434 if (dlgMessage.ShowModal() != wxID_YES ) {
435 // User decided not to continue - bail out!
436 return;
437 }
438 }
439
440 m_timer.Stop(); // Don't need to keep updating m_DateTime_Start to prevent backdating.
441 this->EndModal(wxID_OK);
442 wxLongLong duration = m_TimeSpan_Duration.GetSeconds();
443 // this will assert if the duration won't fit in a long
444 gPrefs->Write(wxT("/TimerRecord/LastDuration"), duration.ToLong());
445 gPrefs->Flush();
446}
447
448void TimerRecordDialog::EnableDisableAutoControls(bool bEnable, int iControlGoup) {
449
450 if (iControlGoup == CONTROL_GROUP_EXPORT) {
451 m_pTimerExportPathTextCtrl->Enable( bEnable );
452 m_pTimerExportPathButtonCtrl->Enable( bEnable);
453 } else if (iControlGoup == CONTROL_GROUP_SAVE) {
454 m_pTimerSavePathTextCtrl->Enable( bEnable);
455 m_pTimerSavePathButtonCtrl->Enable(bEnable );
456 }
457
458 // Enable or disable the Choice box - if there is no Save or Export then this will be disabled
459 if (m_pTimerAutoSaveCheckBoxCtrl->GetValue() || m_pTimerAutoExportCheckBoxCtrl->GetValue()) {
461 } else {
464 }
465}
466
468 // Will update the text box controls
469 m_pTimerSavePathTextCtrl->SetValue(m_fnAutoSaveFile.GetFullPath());
470 m_pTimerExportPathTextCtrl->SetValue(m_fnAutoExportFile.GetFullPath());
471
472 // MY: Ensure we still display "Current Project" if this has already been saved
474 m_pTimerSavePathTextCtrl->SetValue(_("Current Project"));
475 }
476}
477
481{
482 auto updateResult = ProgressResult::Success;
483
484 const auto gAudioIO = AudioIO::Get();
485 gAudioIO->DelayActions(true);
486 {
487 auto cleanup = finally([gAudioIO]{ gAudioIO->DelayActions(false); });
488
489 if (m_DateTime_Start > wxDateTime::UNow())
490 updateResult = this->WaitForStart();
491
492 if (updateResult != ProgressResult::Success) {
493 // Don't proceed, but don't treat it as canceled recording. User just canceled waiting.
495 } else {
496 // Record for specified time.
498 bool bIsRecording = true;
499
500 auto sPostAction = Verbatim(
501 m_pTimerAfterCompleteChoiceCtrl->GetStringSelection() );
502
503 // Two column layout.
505 {
506 XO("Recording start:") ,
507 XO("Duration:") ,
508 XO("Recording end:") ,
509 {} ,
510 XO("Automatic Save enabled:") ,
511 XO("Automatic Export enabled:") ,
512 XO("Action after Timer Recording:") ,
513 },
514 {
516 Verbatim( m_TimeSpan_Duration.Format() ),
518 {} ,
519 (m_bAutoSaveEnabled ? XO("Yes") : XO("No")) ,
520 (m_bAutoExportEnabled ? XO("Yes") : XO("No")) ,
521 sPostAction ,
522 }
523 };
524
526 progress(m_TimeSpan_Duration.GetMilliseconds().GetValue(),
527 XO("Audacity Timer Record Progress"),
528 columns,
530
531 // Make sure that start and end time are updated, so we always get the full
532 // duration, even if there's some delay getting here.
533 wxTimerEvent dummyTimerEvent;
534 this->OnTimer(dummyTimerEvent);
535
536 // Loop for progress display during recording.
537 while (bIsRecording && (updateResult == ProgressResult::Success)) {
538 updateResult = progress.UpdateProgress();
539 using namespace std::chrono;
540 std::this_thread::sleep_for(kTimerInterval);
541 bIsRecording = (wxDateTime::UNow() <= m_DateTime_End); // Call UNow() again for extra accuracy...
542 }
543 }
544 }
545
546 // Must do this AFTER the timer project dialog has been deleted to ensure the application
547 // responds to the AUDIOIO events...see not about bug #334 in the ProgressDialog constructor.
549
550 // Let the caller handle cancellation or failure from recording progress.
551 if (updateResult == ProgressResult::Cancelled || updateResult == ProgressResult::Failed)
553
554 return ExecutePostRecordActions((updateResult == ProgressResult::Stopped));
555}
556
558 // MY: We no longer automatically (and silently) call ->Save() when the
559 // timer recording is completed. We can now Save and/or Export depending
560 // on the options selected by the user.
561 // Once completed, we can also close Audacity, restart the system or
562 // shutdown the system.
563 // If there was any error with the auto save or export then we will not do
564 // the actions requested and instead present an error mesasge to the user.
565 // Finally, if there is no post-record action selected then we output
566 // a dialog detailing what has been carried out instead.
567
568 bool bSaveOK = false;
569 bool bExportOK = false;
570 int iPostRecordAction = m_pTimerAfterCompleteChoiceCtrl->GetSelection();
571 int iOverriddenAction = iPostRecordAction;
572 bool bErrorOverride = false;
573
574 // Do Automatic Save?
575 if (m_bAutoSaveEnabled) {
576
577 auto &projectFileManager = ProjectFileManager::Get( mProject );
578 // MY: If this project has already been saved then simply execute a Save here
580 bSaveOK = projectFileManager.Save();
581 } else {
582 bSaveOK = projectFileManager.SaveFromTimerRecording(m_fnAutoSaveFile);
583 }
584 }
585
586 // Do Automatic Export?
588 Exporter e{ mProject };
589 bExportOK = e.ProcessFromTimerRecording(
590 false, 0.0, TrackList::Get( mProject ).GetEndTime(),
593 }
594
595 // Check if we need to override the post recording action
596 bErrorOverride = ((m_bAutoSaveEnabled && !bSaveOK) || (m_bAutoExportEnabled && !bExportOK));
597 if (bErrorOverride || bWasStopped) {
598 iPostRecordAction = POST_TIMER_RECORD_NOTHING;
599 }
600
601 if (iPostRecordAction == POST_TIMER_RECORD_NOTHING) {
602 // If there is no post-record action then we can show a message indicating what has been done
603
604 auto sMessage = (bWasStopped ? XO("Timer Recording stopped.") :
605 XO("Timer Recording completed."));
606
607 if (m_bAutoSaveEnabled) {
608 if (bSaveOK) {
609 sMessage = XO("%s\n\nRecording saved: %s").Format(
610 sMessage, m_fnAutoSaveFile.GetFullPath());
611 } else {
612 sMessage = XO("%s\n\nError saving recording.").Format( sMessage );
613 }
614 }
616 if (bExportOK) {
617 sMessage = XO("%s\n\nRecording exported: %s").Format(
618 sMessage, m_fnAutoExportFile.GetFullPath());
619 } else {
620 sMessage = XO("%s\n\nError exporting recording.").Format( sMessage );
621 }
622 }
623
624 if (bErrorOverride) {
625
626 if ((iOverriddenAction != iPostRecordAction) &&
627 (iOverriddenAction != POST_TIMER_RECORD_NOTHING)) {
628 // Inform the user that we have overridden the selected action
629 sMessage = XO("%s\n\n'%s' has been canceled due to the error(s) noted above.").Format(
630 sMessage,
631 m_pTimerAfterCompleteChoiceCtrl->GetString(iOverriddenAction));
632 }
633
634 // Show Error Message Box
636 sMessage,
637 XO("Error"),
638 wxICON_EXCLAMATION | wxOK);
639 } else {
640
641 if (bWasStopped && (iOverriddenAction != POST_TIMER_RECORD_NOTHING)) {
642 sMessage = XO("%s\n\n'%s' has been canceled as the recording was stopped.").Format(
643 sMessage,
644 m_pTimerAfterCompleteChoiceCtrl->GetString(iOverriddenAction));
645 }
646
648 sMessage,
649 XO("Timer Recording"),
650 wxICON_INFORMATION | wxOK);
651 }
652 }
653
654 // MY: Lets do some actions that only apply to Exit/Restart/Shutdown
655 if (iPostRecordAction >= POST_TIMER_RECORD_CLOSE) {
656 do {
657
658 // Set the flags as appropriate based on what we have done
659 wxUint32 eActionFlags = TR_ACTION_NOTHING;
660 if (m_bAutoSaveEnabled && bSaveOK) {
661 eActionFlags |= TR_ACTION_SAVED;
662 }
663 if (m_bAutoExportEnabled && bExportOK) {
664 eActionFlags |= TR_ACTION_EXPORTED;
665 }
666
667 // Lets show a warning dialog telling the user what is about to happen.
668 // If the user no longer wants to carry out this action then they can click
669 // Cancel and we will do POST_TIMER_RECORD_NOTHING instead.
670 auto iDelayOutcome = PreActionDelay(iPostRecordAction, (TimerRecordCompletedActions)eActionFlags);
671 if (iDelayOutcome != ProgressResult::Success) {
672 // Cancel the action!
673 iPostRecordAction = POST_TIMER_RECORD_NOTHING;
674 break;
675 }
676 } while (false);
677 }
678
679 // Return the action as required
680 return iPostRecordAction;
681}
682
684{
685#if defined(__WXMSW__)
686 // On Windows, wxWidgets uses the system date control and it displays the
687 // date based on the Windows locale selected by the user. But, wxDateTime
688 // using the strftime function to return the formatted date. Since the
689 // default locale for the Windows CRT environment is "C", the dates come
690 // back in a different format.
691 //
692 // So, we make direct Windows calls to format the date like it the date
693 // control.
694 //
695 // (Most of this taken from src/msw/datectrl.cpp)
696
697 const wxDateTime::Tm tm(dt.GetTm());
698 SYSTEMTIME st;
699 wxString s;
700 int len;
701
702 st.wYear = (WXWORD)tm.year;
703 st.wMonth = (WXWORD)(tm.mon - wxDateTime::Jan + 1);
704 st.wDay = tm.mday;
705 st.wDayOfWeek = st.wMinute = st.wSecond = st.wMilliseconds = 0;
706
707 len = ::GetDateFormat(LOCALE_USER_DEFAULT,
708 DATE_SHORTDATE,
709 &st,
710 NULL,
711 NULL,
712 0);
713 if (len > 0) {
714 len = ::GetDateFormat(LOCALE_USER_DEFAULT,
715 DATE_SHORTDATE,
716 &st,
717 NULL,
718 wxStringBuffer(s, len),
719 len);
720 if (len > 0) {
721 s += wxT(" ") + dt.FormatTime();
722 return Verbatim( s );
723 }
724 }
725#endif
726
727 // Use default formatting
728wxPrintf(wxT("%s\n"), dt.Format());
729 return Verbatim( dt.FormatDate() + wxT(" ") + dt.FormatTime() );
730}
731
733 wxWindow *wParent, const int iID,
734 const TranslatableString &sCaption, const TranslatableString &sValue)
735{
736 wxTextCtrlWrapper * pTextCtrl;
737 wxASSERT(wParent); // to justify safenew
738 pTextCtrl = safenew wxTextCtrlWrapper(wParent, iID, sValue.Translation());
739 pTextCtrl->SetName(sCaption.Translation());
740 return pTextCtrl;
741}
742
744{
745 bool bAutoSave = gPrefs->ReadBool("/TimerRecord/AutoSave", false);
746 bool bAutoExport = gPrefs->ReadBool("/TimerRecord/AutoExport", false);
747 int iPostTimerRecordAction = gPrefs->ReadLong("/TimerRecord/PostAction", 0);
748
749 S.SetBorder(5);
751 /* i18n-hint a format string for hours, minutes, and seconds */
752 auto strFormat = XO("099 h 060 m 060 s");
753 /* i18n-hint a format string for days, hours, minutes, and seconds */
754 auto strFormat1 = XO("099 days 024 h 060 m 060 s");
755
756 S.StartMultiColumn(2, wxCENTER);
757 {
758 S.StartVerticalLay(true);
759 {
760 /* i18n-hint: This string is used to configure the controls for times when the recording is
761 * started and stopped. As such it is important that only the alphabetic parts of the string
762 * are translated, with the numbers left exactly as they are.
763 * The 'h' indicates the first number displayed is hours, the 'm' indicates the second number
764 * displayed is minutes, and the 's' indicates that the third number displayed is seconds.
765 */
766 S.StartStatic(XO("Start Date and Time"), true);
767 {
769 safenew wxDatePickerCtrl(S.GetParent(), // wxWindow *parent,
770 ID_DATEPICKER_START, // wxWindowID id,
771 m_DateTime_Start); // const wxDateTime& dt = wxDefaultDateTime,
772 // const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDP_DEFAULT | wxDP_SHOWCENTURY, const wxValidator& validator = wxDefaultValidator, const wxString& name = "datectrl")
773 m_pDatePickerCtrl_Start->SetRange(wxDateTime::Today(), wxInvalidDateTime); // No backdating.
774#if wxUSE_ACCESSIBILITY
775 m_pDatePickerCtrl_Start->SetAccessible( safenew DatePickerCtrlAx(m_pDatePickerCtrl_Start));
776#endif
777 S.Name(XO("Start Date"))
778 .AddWindow(m_pDatePickerCtrl_Start);
779
782 {}, 0, 44100,
783 Options{}
784 .MenuEnabled(false)
785 .Format(strFormat)
787 S.Name(XO("Start Time"))
788 .AddWindow(m_pTimeTextCtrl_Start);
789 }
790 S.EndStatic();
791
792 S.StartStatic(XO("End Date and Time"), true);
793 {
795 safenew wxDatePickerCtrl(S.GetParent(), // wxWindow *parent,
796 ID_DATEPICKER_END, // wxWindowID id,
797 m_DateTime_End); // const wxDateTime& dt = wxDefaultDateTime,
798 // const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize,
799 // long style = wxDP_DEFAULT | wxDP_SHOWCENTURY,
800 // const wxValidator& validator = wxDefaultValidator,
801 // const wxString& name = "datectrl")
802 m_pDatePickerCtrl_End->SetRange(m_DateTime_Start, wxInvalidDateTime); // No backdating.
803#if wxUSE_ACCESSIBILITY
804 m_pDatePickerCtrl_End->SetAccessible( safenew DatePickerCtrlAx(m_pDatePickerCtrl_End));
805#endif
806 S.Name(XO("End Date"))
807 .AddWindow(m_pDatePickerCtrl_End);
808
811 {}, 0, 44100,
812 Options{}
813 .MenuEnabled(false)
814 .Format(strFormat)
816 S.Name(XO("End Time"))
817 .AddWindow(m_pTimeTextCtrl_End);
818 }
819 S.EndStatic();
820
821 S.StartStatic(XO("Duration"), true);
822 {
825 {}, 0, 44100,
826 Options{}
827 .MenuEnabled(false)
828 .Format(strFormat1)
829 .Value(true, m_TimeSpan_Duration.GetSeconds().ToDouble()));
830 /* i18n-hint: This string is used to configure the controls which shows the recording
831 * duration. As such it is important that only the alphabetic parts of the string
832 * are translated, with the numbers left exactly as they are.
833 * The string 'days' indicates that the first number in the control will be the number of days,
834 * then the 'h' indicates the second number displayed is hours, the 'm' indicates the third
835 * number displayed is minutes, and the 's' indicates that the fourth number displayed is
836 * seconds.
837 */
838 S.Name(XO("Duration"))
839 .AddWindow(m_pTimeTextCtrl_Duration);
840 }
841 S.EndStatic();
842 }
843 S.EndVerticalLay();
844
845 S.StartVerticalLay(true);
846 {
847 S.StartStatic(XO("Automatic Save"), true);
848 {
849 // If checked, the project will be saved when the recording is completed
850 m_pTimerAutoSaveCheckBoxCtrl = S.Id(ID_AUTOSAVE_CHECKBOX).AddCheckBox(XXO("Enable &Automatic Save?"),
851 bAutoSave);
852 S.StartMultiColumn(3, wxEXPAND);
853 {
854 TranslatableString sInitialValue;
855 auto sSaveValue = ProjectFileIO::Get(mProject).GetFileName();
856 if (!sSaveValue.empty()) {
857 m_fnAutoSaveFile.Assign(sSaveValue);
858 sInitialValue = XO("Current Project");
859 }
860 S.AddPrompt(XXO("Save Project As:"));
862 S.GetParent(), ID_AUTOSAVEPATH_TEXT,
863 XO("Save Project As:"), sInitialValue);
865 S.AddWindow(m_pTimerSavePathTextCtrl);
866 m_pTimerSavePathButtonCtrl = S.Id(ID_AUTOSAVEPATH_BUTTON).AddButton(XXO("Select..."));
867 }
868 S.EndMultiColumn();
869 }
870 S.EndStatic();
871
872 S.StartStatic(XO("Automatic Export"), true);
873 {
874 m_pTimerAutoExportCheckBoxCtrl = S.Id(ID_AUTOEXPORT_CHECKBOX).AddCheckBox(XXO("Enable Automatic &Export?"), bAutoExport);
875 S.StartMultiColumn(3, wxEXPAND);
876 {
877 S.AddPrompt(XXO("Export Project As:"));
879 S.GetParent(), ID_AUTOEXPORTPATH_TEXT,
880 XO("Export Project As:"), {});
882 S.AddWindow(m_pTimerExportPathTextCtrl);
883 m_pTimerExportPathButtonCtrl = S.Id(ID_AUTOEXPORTPATH_BUTTON).AddButton(XXO("Select..."));
884 }
885 S.EndMultiColumn();
886 }
887 S.EndStatic();
888
889 S.StartStatic(XO("Options"), true);
890 {
891
892 S.StartMultiColumn(1, wxEXPAND);
893 {
894 S.SetStretchyCol( 0 );
895 m_pTimerAfterCompleteChoiceCtrl = S.AddChoice(XXO("After Recording completes:"),
896 {
897 XO("Do nothing") ,
898 XO("Exit Audacity") ,
899 #ifdef __WINDOWS__
900 XO("Restart system") ,
901 XO("Shutdown system") ,
902 #endif
903 },
904 iPostTimerRecordAction
905 );
906 }
907 S.EndMultiColumn();
908 }
909 S.EndStatic();
910
911 }
912 S.EndVerticalLay();
913 }
914 S.EndMultiColumn();
915
916 // MY: Added the help button here
917 S.AddStandardButtons(eOkButton | eCancelButton | eHelpButton);
918
919 Layout();
920 Fit();
921 SetMinSize(GetSize());
922 Center();
923
926}
927
929{
930 double dTime;
931 long hr;
932 long min;
933 long sec;
934
937 hr = (long)(dTime / 3600.0);
938 min = (long)((dTime - (hr * 3600.0)) / 60.0);
939 sec = (long)(dTime - (hr * 3600.0) - (min * 60.0));
940 m_DateTime_Start.SetHour(hr);
941 m_DateTime_Start.SetMinute(min);
942 m_DateTime_Start.SetSecond(sec);
943
945 dTime = m_pTimeTextCtrl_End->GetValue();
946 hr = (long)(dTime / 3600.0);
947 min = (long)((dTime - (hr * 3600.0)) / 60.0);
948 sec = (long)(dTime - (hr * 3600.0) - (min * 60.0));
949 m_DateTime_End.SetHour(hr);
950 m_DateTime_End.SetMinute(min);
951 m_DateTime_End.SetSecond(sec);
952
954
955 // Pull the settings from the auto save/export controls and write to the pref file
958
959 // MY: Obtain the index from the choice control so we can save to the prefs file
960 int iPostRecordAction = m_pTimerAfterCompleteChoiceCtrl->GetSelection();
961
962 // Save the options back to the prefs file
963 gPrefs->Write("/TimerRecord/AutoSave", m_bAutoSaveEnabled);
964 gPrefs->Write("/TimerRecord/AutoExport", m_bAutoExportEnabled);
965 gPrefs->Write("/TimerRecord/PostAction", iPostRecordAction);
966
967 return true;
968}
969
970// Update m_TimeSpan_Duration and ctrl based on m_DateTime_Start and m_DateTime_End.
972{
974 m_pTimeTextCtrl_Duration->SetValue(m_TimeSpan_Duration.GetSeconds().ToDouble());
975}
976
977// Update m_DateTime_End and ctrls based on m_DateTime_Start and m_TimeSpan_Duration.
979{
980 //v Use remaining disk -> record time calcs from AudacityProject::OnTimer to set range?
982 //wxLogDebug( "Time start %s end %s",
983 // m_DateTime_Start.FormatISOCombined(' '),
984 // m_DateTime_End.FormatISOCombined(' ') );
985
986 // Disable the range limitation (to fix Bug 1749 and 1978)
987 // Otherwise SetVallue asserts when going back in time.
988 m_pDatePickerCtrl_End->SetRange(wxInvalidDateTime, wxInvalidDateTime);
990 // Re-enable range limitation to constrain user input.
991 m_pDatePickerCtrl_End->SetRange(m_DateTime_Start, wxInvalidDateTime); // No backdating.
992 m_pDatePickerCtrl_End->Refresh();
994}
995
997{
998 // MY: The Waiting For Start dialog now shows what actions will occur after recording has completed
999 auto sPostAction = Verbatim(
1000 m_pTimerAfterCompleteChoiceCtrl->GetStringSelection() );
1001
1002 // Two column layout.
1004 {
1005 XO("Waiting to start recording at:") ,
1006 XO("Recording duration:") ,
1007 XO("Scheduled to stop at:") ,
1008 {} ,
1009 XO("Automatic Save enabled:") ,
1010 XO("Automatic Export enabled:") ,
1011 XO("Action after Timer Recording:") ,
1012 },
1013 {
1015 Verbatim( m_TimeSpan_Duration.Format() ),
1017 {} ,
1018 (m_bAutoSaveEnabled ? XO("Yes") : XO("No")) ,
1019 (m_bAutoExportEnabled ? XO("Yes") : XO("No")) ,
1020 sPostAction ,
1021 },
1022 };
1023
1024 wxDateTime startWait_DateTime = wxDateTime::UNow();
1025 wxTimeSpan waitDuration = m_DateTime_Start - startWait_DateTime;
1026 TimerProgressDialog progress(waitDuration.GetMilliseconds().GetValue(),
1027 XO("Audacity Timer Record - Waiting for Start"),
1028 columns,
1030 /* i18n-hint: "in" means after a duration of time,
1031 which is shown below this string */
1032 XO("Recording will commence in:"));
1033
1034 auto updateResult = ProgressResult::Success;
1035 bool bIsRecording = false;
1036 while (updateResult == ProgressResult::Success && !bIsRecording)
1037 {
1038 updateResult = progress.UpdateProgress();
1039 using namespace std::chrono;
1040 std::this_thread::sleep_for(kTimerInterval);
1041 bIsRecording = (m_DateTime_Start <= wxDateTime::UNow());
1042 }
1043 return updateResult;
1044}
1045
1047{
1049 ->GetString(iActionIndex) );
1050
1051 /* i18n-hint: %s is one of "Do nothing", "Exit Audacity", "Restart system",
1052 or "Shutdown system", and
1053 "in" means after a duration of time, shown below this string */
1054 auto sCountdownLabel = XO("%s in:").Format( sAction );
1055
1056 // Two column layout.
1058 {
1059 XO("Timer Recording completed.") ,
1060 {} ,
1061 XO("Recording Saved:") ,
1062 XO("Recording Exported:") ,
1063 XO("Action after Timer Recording:") ,
1064 },
1065 {
1066 {} ,
1067 {} ,
1068 ((eCompletedActions & TR_ACTION_SAVED) ? XO("Yes") : XO("No")) ,
1069 ((eCompletedActions & TR_ACTION_EXPORTED) ? XO("Yes") : XO("No")) ,
1070 sAction ,
1071 },
1072 };
1073
1074
1075 wxDateTime dtNow = wxDateTime::UNow();
1076 wxTimeSpan tsWait = wxTimeSpan(0, 1, 0, 0);
1077 wxDateTime dtActionTime = dtNow.Add(tsWait);
1078
1079 TimerProgressDialog dlgAction(tsWait.GetMilliseconds().GetValue(),
1080 XO("Audacity Timer Record - Waiting"),
1081 columns,
1083 sCountdownLabel);
1084
1085 auto iUpdateResult = ProgressResult::Success;
1086 bool bIsTime = false;
1087 while (iUpdateResult == ProgressResult::Success && !bIsTime)
1088 {
1089 iUpdateResult = dlgAction.UpdateProgress();
1090 using namespace std::chrono;
1091 std::this_thread::sleep_for(kTimerInterval);
1092 bIsTime = (dtActionTime <= wxDateTime::UNow());
1093 }
1094 return iUpdateResult;
1095}
1096
1097// Register a menu item
1098
1101#include "CommonCommandFlags.h"
1102#include "Project.h"
1103#include "ProjectHistory.h"
1104#include "ProjectSettings.h"
1105#include "ProjectWindow.h"
1106#include "UndoManager.h"
1107
1108namespace {
1109void OnTimerRecord(const CommandContext &context)
1110{
1111 auto &project = context.project;
1112 const auto &settings = ProjectSettings::Get( project );
1113 auto &undoManager = UndoManager::Get( project );
1114 auto &window = ProjectWindow::Get( project );
1115
1116 // MY: Due to improvements in how Timer Recording saves and/or exports
1117 // it is now safer to disable Timer Recording when there is more than
1118 // one open project.
1119 if (AllProjects{}.size() > 1) {
1121 XO(
1122"Timer Recording cannot be used with more than one open project.\n\nPlease close any additional projects and try again."),
1123 XO("Timer Recording"),
1124 wxICON_INFORMATION | wxOK);
1125 return;
1126 }
1127
1128 // MY: If the project has unsaved changes then we no longer allow access
1129 // to Timer Recording. This decision has been taken as the safest approach
1130 // preventing issues surrounding "dirty" projects when Automatic Save/Export
1131 // is used in Timer Recording.
1132 if ((undoManager.UnsavedChanges()) &&
1133 (TrackList::Get( project ).Any() || settings.EmptyCanBeDirty())) {
1135 XO(
1136"Timer Recording cannot be used while you have unsaved changes.\n\nPlease save or close this project and try again."),
1137 XO("Timer Recording"),
1138 wxICON_INFORMATION | wxOK);
1139 return;
1140 }
1141
1142 // We check the selected tracks to see if there is enough of them to accommodate
1143 // all input channels and all of them have the same sampling rate.
1144 // Those checks will be later performed by recording function anyway,
1145 // but we want to warn the user about potential problems from the very start.
1146 const auto selectedTracks{ GetPropertiesOfSelected(project) };
1147 const int rateOfSelected{ selectedTracks.rateOfSelected };
1148 const int numberOfSelected{ selectedTracks.numberOfSelected };
1149 const bool allSameRate{ selectedTracks.allSameRate };
1150
1151 if (!allSameRate) {
1152 AudacityMessageBox(XO("The tracks selected "
1153 "for recording must all have the same sampling rate"),
1154 XO("Mismatched Sampling Rates"),
1155 wxICON_ERROR | wxCENTRE);
1156
1157 return;
1158 }
1159
1160 const auto existingTracks{ ProjectAudioManager::ChooseExistingRecordingTracks(project, true, rateOfSelected) };
1161 if (existingTracks.empty()) {
1162 if (numberOfSelected > 0 && rateOfSelected !=
1163 ProjectRate::Get(project).GetRate()) {
1165 "Too few tracks are selected for recording at this sample rate.\n"
1166 "(Audacity requires two channels at the same sample rate for\n"
1167 "each stereo track)"),
1168 XO("Too Few Compatible Tracks Selected"),
1169 wxICON_ERROR | wxCENTRE);
1170
1171 return;
1172 }
1173 }
1174
1175 // We use this variable to display "Current Project" in the Timer Recording
1176 // save project field
1177 bool bProjectSaved = !ProjectFileIO::Get( project ).IsModified();
1178
1179 //we break the prompting and waiting dialogs into two sections
1180 //because they both give the user a chance to click cancel
1181 //and therefore remove the newly inserted track.
1182
1183 TimerRecordDialog dialog(
1184 &window, project, bProjectSaved); /* parent, project, project saved? */
1185 int modalResult = dialog.ShowModal();
1186 if (modalResult == wxID_CANCEL)
1187 {
1188 // Cancelled before recording - don't need to do anything.
1189 }
1190 else
1191 {
1192 // Bug #2382
1193 // Allow recording to start at current cursor position.
1194 #if 0
1195 // Timer Record should not record into a selection.
1196 bool bPreferNewTrack;
1197 gPrefs->Read("/GUI/PreferNewTrackRecord",&bPreferNewTrack, false);
1198 if (bPreferNewTrack) {
1199 window.Rewind(false);
1200 } else {
1201 window.SkipEnd(false);
1202 }
1203 #endif
1204
1205 int iTimerRecordingOutcome = dialog.RunWaitDialog();
1206 switch (iTimerRecordingOutcome) {
1208 // Canceled on the wait dialog
1210 break;
1212 // RunWaitDialog() shows the "wait for start" as well as "recording"
1213 // dialog if it returned POST_TIMER_RECORD_CANCEL it means the user
1214 // cancelled while the recording, so throw out the fresh track.
1215 // However, we can't undo it here because the PushState() is called in TrackPanel::OnTimer(),
1216 // which is blocked by this function.
1217 // so instead we mark a flag to undo it there.
1219 break;
1221 // No action required
1222 break;
1224 wxTheApp->CallAfter( []{
1225 // Simulate the application Exit menu item
1226 wxCommandEvent evt{ wxEVT_MENU, wxID_EXIT };
1227 wxTheApp->AddPendingEvent( evt );
1228 } );
1230 break;
1231
1232#ifdef __WINDOWS__
1233 case POST_TIMER_RECORD_RESTART:
1234 // Restart System
1236 system("shutdown /r /f /t 30");
1237 break;
1238 case POST_TIMER_RECORD_SHUTDOWN:
1239 // Shutdown System
1241 system("shutdown /s /f /t 30");
1242 break;
1243#endif
1244 }
1245 }
1246}
1247
1249
1250using namespace MenuTable;
1252 { wxT("Transport/Basic/Record"),
1253 { OrderingHint::After, wxT("Record2ndChoice") } },
1254 Command( wxT("TimerRecord"), XXO("&Timer Record..."),
1255 OnTimerRecord, CanStopFlags, wxT("Shift+T") )
1256};
1257
1258}
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
XO("Cut/Copy/Paste")
XXO("&Cut/Copy/Paste Toolbar")
#define _(s)
Definition: Internat.h:75
#define safenew
Definition: MemoryX.h:10
FileConfig * gPrefs
Definition: Prefs.cpp:70
@ pdlgConfirmStopCancel
@ pdlgHideStopButton
@ pdlgHideElapsedTime
@ pdlgHideCancelButton
const ReservedCommandFlag & CanStopAudioStreamFlag()
PropertiesOfSelected GetPropertiesOfSelected(const AudacityProject &proj)
an object holding per-project preferred sample rate
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:39
@ eOkButton
Definition: ShuttleGui.h:597
@ eCancelButton
Definition: ShuttleGui.h:598
@ eHelpButton
Definition: ShuttleGui.h:601
const int kSlowTimerInterval
@ CONTROL_GROUP_SAVE
@ CONTROL_GROUP_EXPORT
constexpr auto kTimerInterval
@ 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)
@ 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:87
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:147
CommandContext provides additional information to an 'Apply()' command. It provides the project,...
AudacityProject & project
virtual bool Flush(bool bCurrentOnly=false) wxOVERRIDE
Definition: FileConfig.cpp:143
FILES_API const FileType AudacityProjects
Definition: FileNames.h:71
static void ShowHelp(wxWindow *parent, const FilePath &localFileName, const URLString &remoteURL, bool bModal=false, bool alwaysDefaultBrowser=false)
Definition: HelpSystem.cpp:234
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
static ProjectSettings & Get(AudacityProject &project)
static ProjectWindow & Get(AudacityProject &project)
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
Definition: ShuttleGui.h:628
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
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)
auto Any() -> TrackIterRange< TrackType >
Definition: Track.h:1437
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:486
Holds a msgid for the translation catalog; may also bind format arguments.
wxString Translation() const
static UndoManager & Get(AudacityProject &project)
Definition: UndoManager.cpp:67
An alternative to using wxWindowAccessible, which in wxWidgets 3.1.1 contained GetParent() which was ...
void SetReadOnly(bool readonly=true)
ProgressResult
Definition: BasicUI.h:145
std::unique_ptr< CommandItem > Command(const CommandID &name, const TranslatableString &label_in, void(Handler::*pmf)(const CommandContext &), CommandFlag flags, const CommandManager::Options &options={}, CommandHandlerFinder finder=FinderScope::DefaultFinder())
void OnTimerRecord(const CommandContext &context)