1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-06-24 08:10:05 +02:00

Introduce further enhancement of the Timer Recording process:

* Disk space warning if the recording potentially will not fit in disk space available.
* ProgressDialog enhancements that allow the Stop/Cancel button to be confirmed and the elapsed time to be hidden.
* Messages enhanced to clearly show the actions being taken.
This commit is contained in:
tip2tail 2016-03-30 23:46:15 +01:00 committed by James Crook
parent 726efcf5c8
commit 6686d0e314
7 changed files with 257 additions and 80 deletions

View File

@ -6316,8 +6316,7 @@ void AudacityProject::OnTimerRecord()
// it is now safer to disable Timer Recording when there is more than
// one open project.
if (GetOpenProjectCount() > 1) {
wxMessageBox(_("Timer Recording cannot be used with more than one open project.\n\n\
Please close any additional projects and try again."),
wxMessageBox(_("Timer Recording cannot be used with more than one open project.\n\nPlease close any additional projects and try again."),
_("Timer Recording"),
wxICON_INFORMATION | wxOK);
return;

View File

@ -5254,3 +5254,36 @@ bool AudacityProject::ProjectHasTracks() {
bool bHasTracks = (iter2.First() != NULL);
return bHasTracks;
}
// MY: This routine will give an estimate of how many
// minutes of recording time we have available.
// This is called from TimerRecordDialog::OnOK() to allow
// the user to resolve a potential disk space issue before
// Timer Recording starts.
// The calculations made are based on the user's current
// preferences.
int AudacityProject::GetEstimatedRecordingMinsLeftOnDisk() {
// Obtain the current settings
sampleFormat oCaptureFormat = (sampleFormat)
gPrefs->Read(wxT("/SamplingRate/DefaultProjectSampleFormat"), floatSample);
long lCaptureChannels;
gPrefs->Read(wxT("/AudioIO/RecordChannels"), &lCaptureChannels, 2L);
// Find out how much free space we have on disk
wxLongLong lFreeSpace = mDirManager->GetFreeDiskSpace();
if (lFreeSpace < 0) {
return 0;
}
// Calculate the remaining time
double dRecTime = 0.0;
dRecTime = lFreeSpace.GetHi() * 4294967296.0 + lFreeSpace.GetLo();
dRecTime /= SAMPLE_SIZE_DISK(oCaptureFormat);
dRecTime /= lCaptureChannels;
dRecTime /= GetRate();
// Convert to minutes before returning
int iRecMins = (int)(dRecTime / 60.0);
return iRecMins;
}

View File

@ -283,6 +283,9 @@ class AUDACITY_DLL_API AudacityProject final : public wxFrame,
bool ProjectHasTracks();
// Routine to estimate how many minutes of recording time are left on disk
int GetEstimatedRecordingMinsLeftOnDisk();
#include "Menus.h"
CommandManager *GetCommandManager() { return &mCommandManager; }

View File

@ -371,6 +371,33 @@ void TimerRecordDialog::OnOK(wxCommandEvent& WXUNUSED(event))
}
}
// MY: Estimate here if we have enough disk space to
// complete this Timer Recording.
// If we dont think there is enough space then ask the user
// if they want to continue.
// We don't stop the user from starting the recording
// as its possible that they plan to free up some
// space before the recording begins
AudacityProject* pProject = GetActiveProject();
// How many minutes do we have left on the recording?
int iMinsLeft = pProject->GetEstimatedRecordingMinsLeftOnDisk();
// How many minutes will this recording require?
int iMinsRecording = m_TimeSpan_Duration.GetMinutes();
// Do we have enough space?
if (iMinsRecording >= iMinsLeft) {
wxMessageDialog dlgMessage(NULL,
_("You may not have enough free disk space to complete this timer recording, based on your current settings.\n\nDo you wish to continue?"),
_("Timer Recording Disk Space Warning"),
wxYES_NO | wxNO_DEFAULT | wxICON_WARNING);
if (dlgMessage.ShowModal() != wxID_YES) {
// User decided not to continue - bail out!
return;
}
}
m_timer.Stop(); // Don't need to keep updating m_DateTime_Start to prevent backdating.
this->EndModal(wxID_OK);
wxLongLong duration = m_TimeSpan_Duration.GetSeconds();
@ -465,17 +492,26 @@ int TimerRecordDialog::RunWaitDialog()
pProject->OnRecord();
bool bIsRecording = true;
wxString strMsg =
_("Recording start") + (wxString)wxT(":\t\t")
+ GetDisplayDate(m_DateTime_Start) + wxT("\n") + _("Recording end")
+ wxT(":\t\t") + GetDisplayDate(m_DateTime_End) + wxT("\n")
+ _("Duration") + wxT(":\t\t") + m_TimeSpan_Duration.Format();
wxString sPostAction = m_pTimerAfterCompleteChoiceCtrl->GetString(m_pTimerAfterCompleteChoiceCtrl->GetSelection());
wxString strMsg;
strMsg.Printf(_("Recording start:\t\t\t%s\n") +
_("Recording end:\t\t\t%s\n") +
_("Duration:\t\t\t%s\n\n") +
_("Automatic Save Enabled:\t\t%s\n") +
_("Automatic Export Enabled:\t\t%s\n") +
_("Post Timer Recording Action:\t%s"),
GetDisplayDate(m_DateTime_Start).c_str(),
m_TimeSpan_Duration.Format(),
GetDisplayDate(m_DateTime_End).c_str(),
(m_bAutoSaveEnabled ? _("Yes") : _("No")),
(m_bAutoExportEnabled ? _("Yes") : _("No")),
sPostAction);
TimerProgressDialog
progress(m_TimeSpan_Duration.GetMilliseconds().GetValue(),
_("Audacity Timer Record Progress"),
strMsg,
pdlgHideCancelButton);
pdlgHideCancelButton | pdlgConfirmStopCancel);
// Make sure that start and end time are updated, so we always get the full
// duration, even if there's some delay getting here.
@ -593,11 +629,20 @@ int TimerRecordDialog::ExecutePostRecordActions(bool bWasStopped) {
// MY: Lets do some actions that only apply to Exit/Restart/Shutdown
if (iPostRecordAction >= POST_TIMER_RECORD_CLOSE) {
do {
// Set the flags as appropriate based on what we have done
wxUint32 eActionFlags = TR_ACTION_NOTHING;
if (m_bAutoSaveEnabled && bSaveOK) {
eActionFlags |= TR_ACTION_SAVED;
}
if (m_bAutoExportEnabled && bExportOK) {
eActionFlags |= TR_ACTION_EXPORTED;
}
// Lets show a warning dialog telling the user what is about to happen.
// If the user no longer wants to carry out this action then they can click
// Cancel and we will do POST_TIMER_RECORD_NOTHING instead.
int iDelayOutcome = PreActionDelay(iPostRecordAction, (m_bAutoSaveEnabled && bSaveOK),
(m_bAutoExportEnabled && bExportOK));
int iDelayOutcome = PreActionDelay(iPostRecordAction, (TimerRecordCompletedActions)eActionFlags);
if (iDelayOutcome != eProgressSuccess) {
// Cancel the action!
iPostRecordAction = POST_TIMER_RECORD_NOTHING;
@ -912,18 +957,32 @@ void TimerRecordDialog::UpdateEnd()
int TimerRecordDialog::WaitForStart()
{
// MY: The Waiting For Start dialog now shows what actions will occur after recording has completed
wxString sPostAction = m_pTimerAfterCompleteChoiceCtrl->GetString(m_pTimerAfterCompleteChoiceCtrl->GetSelection());
/* i18n-hint: Time specifications like "Sunday 28th October 2007 15:16:17 GMT"
* but hopefully translated by wxwidgets will be inserted into this */
wxString strMsg;
/* i18n-hint: A time specification like "Sunday 28th October 2007 15:16:17 GMT"
* but hopefully translated by wxwidgets will be inserted into this */
strMsg.Printf(_("Waiting to start recording at %s.\n"),
GetDisplayDate(m_DateTime_Start).c_str());
strMsg.Printf(_("Waiting to start recording at:\t%s\n") +
_("Recording duration:\t\t%s\n") +
_("Scheduled to stop at:\t\t%s\n\n") +
_("Automatic Save Enabled:\t\t%s\n") +
_("Automatic Export Enabled:\t\t%s\n") +
_("Post Timer Recording Action:\t%s"),
GetDisplayDate(m_DateTime_Start).c_str(),
m_TimeSpan_Duration.Format(),
GetDisplayDate(m_DateTime_End).c_str(),
(m_bAutoSaveEnabled ? _("Yes") : _("No")),
(m_bAutoExportEnabled ? _("Yes") : _("No")),
sPostAction);
wxDateTime startWait_DateTime = wxDateTime::UNow();
wxTimeSpan waitDuration = m_DateTime_Start - startWait_DateTime;
TimerProgressDialog
progress(waitDuration.GetMilliseconds().GetValue(),
_("Audacity Timer Record - Waiting for Start"),
strMsg,
pdlgHideStopButton);
TimerProgressDialog progress(waitDuration.GetMilliseconds().GetValue(),
_("Audacity Timer Record - Waiting for Start"),
strMsg,
pdlgHideStopButton | pdlgConfirmStopCancel | pdlgHideElapsedTime,
_("Recording will commence in:"));
int updateResult = eProgressSuccess;
bool bIsRecording = false;
@ -936,28 +995,21 @@ int TimerRecordDialog::WaitForStart()
return updateResult;
}
// TODO: Rather than two flags, an enum with the possibilities would be better.
int TimerRecordDialog::PreActionDelay(int iActionIndex, bool bSaved, bool bExported)
int TimerRecordDialog::PreActionDelay(int iActionIndex, TimerRecordCompletedActions eCompletedActions)
{
wxString sMessage;
wxString sAction = m_pTimerAfterCompleteChoiceCtrl->GetString(iActionIndex);
wxString sDone = "";
if (bSaved && bExported) {
sDone = _("Saved and Exported");
}
else if (bSaved) {
sDone = _("Saved");
}
else if (bExported) {
sDone = _("Exported");
}
// TODO: The wording will sound better if there are complete messages for
// the will-occur-shortly messages.
/* i18n-hint: The first %s will be a translation of 'Saved', 'Exported' or
* 'Saved and Exported'. The second %s will be 'Exit Audacity'
* 'Restart System' or 'Shutdown System' */
sMessage.Printf(_("Timer Recording completed: Recording has been %s.\n\n'%s' will occur shortly...\n"),
sDone, sAction);
wxString sCountdownLabel;
sCountdownLabel.Printf("%s in:", sAction);
// Build a clearer message...
wxString sMessage;
sMessage.Printf(_("Timer Recording Completed.\n\n") +
_("Recording Saved:\t\t\t%s\n") +
_("Recording Exported:\t\t%s\n") +
_("Post Timer Recording Action:\t%s"),
((eCompletedActions & TR_ACTION_SAVED) ? _("Yes") : _("No")),
((eCompletedActions & TR_ACTION_EXPORTED) ? _("Yes") : _("No")),
sAction);
wxDateTime dtNow = wxDateTime::UNow();
wxTimeSpan tsWait = wxTimeSpan(0, 1, 0, 0);
@ -966,7 +1018,8 @@ int TimerRecordDialog::PreActionDelay(int iActionIndex, bool bSaved, bool bExpor
TimerProgressDialog dlgAction(tsWait.GetMilliseconds().GetValue(),
_("Audacity Timer Record - Waiting"),
sMessage,
pdlgHideStopButton);
pdlgHideStopButton | pdlgHideElapsedTime,
sCountdownLabel);
int iUpdateResult = eProgressSuccess;
bool bIsTime = false;

View File

@ -30,6 +30,12 @@ class NumericTextCtrl;
class ShuttleGui;
class TimerRecordPathCtrl;
enum TimerRecordCompletedActions {
TR_ACTION_NOTHING = 0x00000000,
TR_ACTION_SAVED = 0x00000001,
TR_ACTION_EXPORTED = 0x00000002
};
class TimerRecordPathCtrl final : public wxTextCtrl
{
// MY: Class that inherits from the wxTextCtrl class.
@ -93,7 +99,7 @@ private:
int ExecutePostRecordActions(bool bWasStopped);
int PreActionDelay(int iActionIndex, bool bSaved, bool bExported);
int PreActionDelay(int iActionIndex, TimerRecordCompletedActions eCompletedActions);
private:
wxDateTime m_DateTime_Start;

View File

@ -996,13 +996,14 @@ ProgressDialog::ProgressDialog()
}
ProgressDialog::ProgressDialog(const wxString & title,
const wxString & message,
int flags)
const wxString & message /* = wxEmptyString*/,
int flags /* = pdlgDefaultFlags */,
const wxString & sRemainingLabelText /* = wxEmptyString */)
: wxDialog()
{
Init();
Create(title, message, flags);
Create(title, message, flags, sRemainingLabelText);
}
//
@ -1070,11 +1071,17 @@ void ProgressDialog::Init()
}
bool ProgressDialog::Create(const wxString & title,
const wxString & message,
int flags)
const wxString & message /* = wxEmptyString */,
int flags /* = pdlgDefaultFlags */,
const wxString & sRemainingLabelText /* = wxEmptyString */)
{
wxWindow *parent = GetParentForModalDialog(NULL, 0);
// Set this boolean to indicate if we are using the "Elapsed" labels
m_bShowElapsedTime = !(flags & pdlgHideElapsedTime);
// Set this boolean to indicate if we confirm the Cancel/Stop actions
m_bConfirmAction = (flags & pdlgConfirmStopCancel);
bool success = wxDialog::Create(parent,
wxID_ANY,
title,
@ -1125,33 +1132,45 @@ bool ProgressDialog::Create(const wxString & title,
//
{
auto ug = std::make_unique<wxFlexGridSizer>(2, 2, 10, 10);
// MY: Only one row if we are not going to show the elapsed time
if (m_bShowElapsedTime == false) {
ug = std::make_unique<wxFlexGridSizer>(1, 2, 10, 10);
}
g = ug.get();
w = safenew wxStaticText(this,
wxID_ANY,
_("Elapsed Time:"),
wxDefaultPosition,
wxDefaultSize,
wxALIGN_RIGHT);
w->SetName(w->GetLabel()); // fix for bug 577 (NVDA/Narrator screen readers do not read static text in dialogs)
g->Add(w, 0, wxALIGN_RIGHT);
if (m_bShowElapsedTime) {
w = safenew wxStaticText(this,
wxID_ANY,
_("Elapsed Time:"),
wxDefaultPosition,
wxDefaultSize,
wxALIGN_RIGHT);
w->SetName(w->GetLabel()); // fix for bug 577 (NVDA/Narrator screen readers do not read static text in dialogs)
g->Add(w, 0, wxALIGN_RIGHT);
mElapsed = safenew wxStaticText(this,
wxID_ANY,
wxT("00:00:00"),
wxDefaultPosition,
wxDefaultSize,
wxALIGN_LEFT);
mElapsed->SetName(mElapsed->GetLabel()); // fix for bug 577 (NVDA/Narrator screen readers do not read static text in dialogs)
g->Add(mElapsed, 0, wxALIGN_LEFT);
ds.y += mElapsed->GetSize().y + 10;
mElapsed = safenew wxStaticText(this,
wxID_ANY,
wxT("00:00:00"),
wxDefaultPosition,
wxDefaultSize,
wxALIGN_LEFT);
mElapsed->SetName(mElapsed->GetLabel()); // fix for bug 577 (NVDA/Narrator screen readers do not read static text in dialogs)
g->Add(mElapsed, 0, wxALIGN_LEFT);
ds.y += mElapsed->GetSize().y + 10;
}
// Customised "Remaining" label text
wxString sRemainingText = sRemainingLabelText;
if (sRemainingText == wxEmptyString) {
sRemainingText = _("Remaining Time:");
}
//
//
//
w = safenew wxStaticText(this,
wxID_ANY,
_("Remaining Time:"),
sRemainingText,
wxDefaultPosition,
wxDefaultSize,
wxALIGN_RIGHT);
@ -1180,12 +1199,14 @@ bool ProgressDialog::Create(const wxString & title,
{
w = safenew wxButton(this, wxID_OK, _("Stop"));
h->Add(w, 0, wxRIGHT, 10);
m_btnStop = w;
}
if (!(flags & pdlgHideCancelButton))
{
w = safenew wxButton(this, wxID_CANCEL, _("Cancel"));
h->Add(w, 0, wxRIGHT, 10);
m_btnCancel = w;
}
v->Add(uh.release(), 0, wxALIGN_RIGHT | wxRIGHT | wxBOTTOM, 10);
@ -1201,6 +1222,10 @@ bool ProgressDialog::Create(const wxString & title,
wxClientDC dc(this);
dc.GetMultiLineTextExtent(message, &mLastW, &mLastH);
// MY: Add a little bit more width when we have TABs to stop words wrapping
int iTabFreq = wxMax((message.Freq('\t') - 1), 0);
mLastW = mLastW + (iTabFreq * 8);
#if defined(__WXMAC__)
mMessage->SetMinSize(wxSize(mLastW, mLastH));
#endif
@ -1297,12 +1322,14 @@ int ProgressDialog::Update(int value, const wxString & message)
// Only update if a full second has passed or track progress is complete
if ((now - mLastUpdate > 1000) || (value == 1000))
{
wxTimeSpan tsElapsed(0, 0, 0, elapsed);
wxTimeSpan tsRemains(0, 0, 0, remains);
if (m_bShowElapsedTime) {
wxTimeSpan tsElapsed(0, 0, 0, elapsed);
mElapsed->SetLabel(tsElapsed.Format(wxT("%H:%M:%S")));
mElapsed->SetName(mElapsed->GetLabel()); // fix for bug 577 (NVDA/Narrator screen readers do not read static text in dialogs)
mElapsed->Update();
}
mElapsed->SetLabel(tsElapsed.Format(wxT("%H:%M:%S")));
mElapsed->SetName(mElapsed->GetLabel()); // fix for bug 577 (NVDA/Narrator screen readers do not read static text in dialogs)
mElapsed->Update();
wxTimeSpan tsRemains(0, 0, 0, remains);
mRemaining->SetLabel(tsRemains.Format(wxT("%H:%M:%S")));
mRemaining->SetName(mRemaining->GetLabel()); // fix for bug 577 (NVDA/Narrator screen readers do not read static text in dialogs)
mRemaining->Update();
@ -1477,12 +1504,36 @@ bool ProgressDialog::SearchForWindow(const wxWindowList & list, const wxWindow *
void ProgressDialog::OnCancel(wxCommandEvent & WXUNUSED(event))
{
if (m_bConfirmAction) {
wxString sPrompt = _("Are you sure you wish to cancel?");
wxMessageDialog dlgMessage(this,
sPrompt,
_("Confirm Cancel"),
wxYES_NO | wxICON_QUESTION | wxNO_DEFAULT | wxSTAY_ON_TOP);
int iAction = dlgMessage.ShowModal();
if (iAction != wxID_YES) {
m_btnCancel->SetFocus();
return;
}
}
FindWindowById(wxID_CANCEL, this)->Disable();
mCancel = true;
}
void ProgressDialog::OnStop(wxCommandEvent & WXUNUSED(event))
{
if (m_bConfirmAction) {
wxString sPrompt = _("Are you sure you wish to stop?");
wxMessageDialog dlgMessage(this,
sPrompt,
_("Confirm Stop"),
wxYES_NO | wxICON_QUESTION | wxNO_DEFAULT | wxSTAY_ON_TOP);
int iAction = dlgMessage.ShowModal();
if (iAction != wxID_YES) {
m_btnStop->SetFocus();
return;
}
}
FindWindowById(wxID_OK, this)->Disable();
mCancel = false;
mStop = true;
@ -1490,6 +1541,17 @@ void ProgressDialog::OnStop(wxCommandEvent & WXUNUSED(event))
void ProgressDialog::OnCloseWindow(wxCloseEvent & WXUNUSED(event))
{
if (m_bConfirmAction) {
wxString sPrompt = _("Are you sure you wish to close?");
wxMessageDialog dlgMessage(this,
sPrompt,
_("Confirm Close"),
wxYES_NO | wxICON_QUESTION | wxNO_DEFAULT | wxSTAY_ON_TOP);
int iAction = dlgMessage.ShowModal();
if (iAction != wxID_YES) {
return;
}
}
mCancel = true;
}
@ -1525,10 +1587,11 @@ void ProgressDialog::Beep() const
}
TimerProgressDialog::TimerProgressDialog(const wxLongLong_t duration,
const wxString & title,
const wxString & message /*= wxEmptyString*/,
int flags /*= pdlgEmptyFlags*/)
: ProgressDialog(title, message, flags)
const wxString & title,
const wxString & message /* = wxEmptyString */,
int flags /* = pdlgDefaultFlags */,
const wxString & sRemainingLabelText /* = wxEmptyString */)
: ProgressDialog(title, message, flags, sRemainingLabelText)
{
mDuration = duration;
}
@ -1587,11 +1650,13 @@ int TimerProgressDialog::Update(const wxString & message /*= wxEmptyString*/)
// Only update if a full second has passed.
if (now - mLastUpdate > 1000)
{
wxTimeSpan tsElapsed(0, 0, 0, elapsed);
wxTimeSpan tsRemains(0, 0, 0, remains);
if (m_bShowElapsedTime) {
wxTimeSpan tsElapsed(0, 0, 0, elapsed);
mElapsed->SetLabel(tsElapsed.Format(wxT("%H:%M:%S")));
mElapsed->Update();
}
mElapsed->SetLabel(tsElapsed.Format(wxT("%H:%M:%S")));
mElapsed->Update();
wxTimeSpan tsRemains(0, 0, 0, remains);
mRemaining->SetLabel(tsRemains.Format(wxT("%H:%M:%S")));
mRemaining->Update();

View File

@ -26,6 +26,7 @@
#include <wx/gauge.h>
#include <wx/stattext.h>
#include <wx/utils.h>
#include <wx/msgdlg.h>
enum
{
@ -40,6 +41,8 @@ enum ProgressDialogFlags
pdlgEmptyFlags = 0x00000000,
pdlgHideStopButton = 0x00000001,
pdlgHideCancelButton = 0x00000002,
pdlgHideElapsedTime = 0x00000004,
pdlgConfirmStopCancel = 0x00000008,
pdlgDefaultFlags = pdlgEmptyFlags
};
@ -52,11 +55,17 @@ class AUDACITY_DLL_API ProgressDialog /* not final */ : public wxDialog
{
public:
ProgressDialog();
ProgressDialog(const wxString & title, const wxString & message = wxEmptyString, int flags = pdlgDefaultFlags);
ProgressDialog(const wxString & title,
const wxString & message = wxEmptyString,
int flags = pdlgDefaultFlags,
const wxString & sRemainingLabelText = wxEmptyString);
virtual ~ProgressDialog();
// NEW virtual? It doesn't override wxDialog
virtual bool Create(const wxString & title, const wxString & message = wxEmptyString, int flags = pdlgDefaultFlags);
virtual bool Create(const wxString & title,
const wxString & message = wxEmptyString,
int flags = pdlgDefaultFlags,
const wxString & sRemainingLabelText = wxEmptyString);
int Update(int value, const wxString & message = wxEmptyString);
int Update(double current, const wxString & message = wxEmptyString);
@ -83,6 +92,14 @@ protected:
bool mIsTransparent;
// MY: Booleans to hold the flag values
bool m_bShowElapsedTime = true;
bool m_bConfirmAction = false;
// MY: Declare the buttons so we can se the focus on them later
wxWindow *m_btnStop;
wxWindow *m_btnCancel;
private:
void Init();
bool SearchForWindow(const wxWindowList & list, const wxWindow *searchfor) const;
@ -110,7 +127,8 @@ public:
TimerProgressDialog(const wxLongLong_t duration,
const wxString & title,
const wxString & message = wxEmptyString,
int flags = pdlgDefaultFlags);
int flags = pdlgDefaultFlags,
const wxString & sRemainingLabelText = wxEmptyString);
int Update(const wxString & message = wxEmptyString);
protected: