mirror of
https://github.com/cookiengineer/audacity
synced 2025-06-20 14:20:06 +02:00
Timer Recording Enhancements to allow Automatic Save and Export after
recording. Also allows for additional options to be carried out after a successful timer recording such as Exit, Retsrat and Shutdown.
This commit is contained in:
parent
484f23dcc7
commit
aa5ffe99a7
@ -144,6 +144,18 @@ enum {
|
||||
kAlignTogether
|
||||
};
|
||||
|
||||
// Post Timer Recording Actions
|
||||
// Ensure this matches the enum in TimerRecordDialog.cpp
|
||||
enum {
|
||||
POST_TIMER_RECORD_STOPPED = -3,
|
||||
POST_TIMER_RECORD_CANCEL_WAIT,
|
||||
POST_TIMER_RECORD_CANCEL,
|
||||
POST_TIMER_RECORD_NOTHING,
|
||||
POST_TIMER_RECORD_CLOSE,
|
||||
POST_TIMER_RECORD_RESTART,
|
||||
POST_TIMER_RECORD_SHUTDOWN
|
||||
};
|
||||
|
||||
// Define functor subclasses that dispatch to the correct call sequence on
|
||||
// member functions of AudacityProject
|
||||
|
||||
@ -6309,24 +6321,80 @@ void AudacityProject::OnNewTimeTrack()
|
||||
|
||||
void AudacityProject::OnTimerRecord()
|
||||
{
|
||||
// MY: Due to improvements in how Timer Recording saves and/or exports
|
||||
// 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."),
|
||||
_("Timer Recording"),
|
||||
wxICON_INFORMATION | wxOK);
|
||||
return;
|
||||
}
|
||||
|
||||
// MY: If the project has unsaved changes then we no longer allow access
|
||||
// to Timer Recording. This decision has been taken as the safest approach
|
||||
// preventing issues surrounding "dirty" projects when Automatic Save/Export
|
||||
// is used in Timer Recording.
|
||||
if (GetUndoManager()->UnsavedChanges()) {
|
||||
wxMessageBox(_("Timer Recording cannot be used while you have unsaved changes.\n\n\
|
||||
Please save or close this project and try again."),
|
||||
_("Timer Recording"),
|
||||
wxICON_INFORMATION | wxOK);
|
||||
return;
|
||||
}
|
||||
// We use this variable to display "Current Project" in the Timer Recording save project field
|
||||
bool bProjectSaved = IsProjectSaved();
|
||||
|
||||
//we break the prompting and waiting dialogs into two sections
|
||||
//because they both give the user a chance to click cancel
|
||||
//and therefore remove the newly inserted track.
|
||||
|
||||
TimerRecordDialog dialog(this /* parent */ );
|
||||
TimerRecordDialog dialog(this, bProjectSaved); /* parent, project saved? */
|
||||
int modalResult = dialog.ShowModal();
|
||||
if (modalResult == wxID_CANCEL)
|
||||
{
|
||||
// Cancelled before recording - don't need to do anyting.
|
||||
}
|
||||
else if(!dialog.RunWaitDialog())
|
||||
else
|
||||
{
|
||||
//RunWaitDialog() shows the "wait for start" as well as "recording" dialog
|
||||
//if it returned false it means the user cancelled while the recording, so throw out the fresh track.
|
||||
//However, we can't undo it here because the PushState() is called in TrackPanel::OnTimer(),
|
||||
//which is blocked by this function.
|
||||
//so instead we mark a flag to undo it there.
|
||||
mTimerRecordCanceled = true;
|
||||
int iTimerRecordingOutcome = dialog.RunWaitDialog();
|
||||
switch (iTimerRecordingOutcome) {
|
||||
case POST_TIMER_RECORD_CANCEL_WAIT:
|
||||
// Canceled on the wait dialog
|
||||
if (GetUndoManager()->UndoAvailable()) {
|
||||
// MY: We need to roll back what changes we have made here
|
||||
OnUndo();
|
||||
}
|
||||
break;
|
||||
case POST_TIMER_RECORD_CANCEL:
|
||||
// RunWaitDialog() shows the "wait for start" as well as "recording" dialog
|
||||
// if it returned POST_TIMER_RECORD_CANCEL it means the user cancelled while the recording, so throw out the fresh track.
|
||||
// However, we can't undo it here because the PushState() is called in TrackPanel::OnTimer(),
|
||||
// which is blocked by this function.
|
||||
// so instead we mark a flag to undo it there.
|
||||
mTimerRecordCanceled = true;
|
||||
break;
|
||||
case POST_TIMER_RECORD_NOTHING:
|
||||
// No action required
|
||||
break;
|
||||
case POST_TIMER_RECORD_CLOSE:
|
||||
// Quit Audacity
|
||||
exit(0);
|
||||
break;
|
||||
case POST_TIMER_RECORD_RESTART:
|
||||
// Restart System
|
||||
#ifdef __WINDOWS__
|
||||
system("shutdown /r /f /t 30");
|
||||
#endif
|
||||
break;
|
||||
case POST_TIMER_RECORD_SHUTDOWN:
|
||||
// Shutdown System
|
||||
#ifdef __WINDOWS__
|
||||
system("shutdown /s /f /t 30");
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5193,3 +5193,57 @@ void AudacityProject::ReleaseKeyboard(wxWindow * /* handler */)
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
bool AudacityProject::ExportFromTimerRecording(wxFileName fnFile, int iFormat, int iSubFormat, int iFilterIndex)
|
||||
{
|
||||
Exporter e;
|
||||
|
||||
wxGetApp().SetMissingAliasedFileWarningShouldShow(true);
|
||||
return e.ProcessFromTimerRecording(this, false, 0.0, mTracks->GetEndTime(), fnFile, iFormat, iSubFormat, iFilterIndex);
|
||||
}
|
||||
|
||||
int AudacityProject::GetOpenProjectCount() {
|
||||
return gAudacityProjects.Count();
|
||||
}
|
||||
|
||||
bool AudacityProject::IsProjectSaved() {
|
||||
wxString sProjectName = mDirManager->GetProjectName();
|
||||
return (sProjectName != wxT(""));
|
||||
}
|
||||
|
||||
bool AudacityProject::SaveFromTimerRecording(wxFileName fnFile) {
|
||||
// MY: Will save the project to a new location a-la Save As
|
||||
// and then tidy up after itself.
|
||||
|
||||
wxString sNewFileName = fnFile.GetFullPath();
|
||||
|
||||
// MY: To allow SaveAs from Timer Recording we need to check what
|
||||
// the value of mFileName is befoer we change it.
|
||||
wxString sOldFilename = "";
|
||||
if (IsProjectSaved()) {
|
||||
sOldFilename = mFileName;
|
||||
}
|
||||
|
||||
// MY: If the project file already exists then bail out
|
||||
// and send populate the message string (pointer) so
|
||||
// we can tell the user what went wrong.
|
||||
if (wxFileExists(sNewFileName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mFileName = sNewFileName;
|
||||
SetProjectTitle();
|
||||
|
||||
bool bSuccess = Save(false, true, false);
|
||||
|
||||
if (bSuccess) {
|
||||
wxGetApp().AddFileToHistory(mFileName);
|
||||
} else
|
||||
{
|
||||
// Reset file name on error
|
||||
mFileName = sOldFilename;
|
||||
SetProjectTitle();
|
||||
}
|
||||
|
||||
return bSuccess;
|
||||
}
|
||||
|
@ -275,6 +275,12 @@ class AUDACITY_DLL_API AudacityProject final : public wxFrame,
|
||||
*/
|
||||
wxDialog *GetMissingAliasFileDialog();
|
||||
|
||||
// Timer Record Auto Save/Export Routines
|
||||
bool SaveFromTimerRecording(wxFileName fnFile);
|
||||
bool ExportFromTimerRecording(wxFileName fnFile, int iFormat, int iSubFormat, int iFilterIndex);
|
||||
int GetOpenProjectCount();
|
||||
bool IsProjectSaved();
|
||||
|
||||
#include "Menus.h"
|
||||
|
||||
CommandManager *GetCommandManager() { return &mCommandManager; }
|
||||
|
@ -487,7 +487,6 @@ wxSpinCtrl * ShuttleGuiBase::AddSpinCtrl(const wxString &Prompt, int Value, int
|
||||
return pSpinCtrl;
|
||||
}
|
||||
|
||||
|
||||
wxTextCtrl * ShuttleGuiBase::AddTextBox(const wxString &Caption, const wxString &Value, const int nChars)
|
||||
{
|
||||
UseUpId();
|
||||
|
@ -70,7 +70,6 @@ class Shuttle;
|
||||
|
||||
class WrappedType;
|
||||
|
||||
|
||||
class AUDACITY_DLL_API ShuttleGuiBase /* not final */
|
||||
{
|
||||
public:
|
||||
|
@ -20,6 +20,7 @@
|
||||
|
||||
#include "Audacity.h"
|
||||
#include "TimerRecordDialog.h"
|
||||
#include "FileNames.h"
|
||||
|
||||
#include <wx/defs.h>
|
||||
#include <wx/datetime.h>
|
||||
@ -44,7 +45,30 @@ enum { // control IDs
|
||||
ID_TIMETEXT_START,
|
||||
ID_DATEPICKER_END,
|
||||
ID_TIMETEXT_END,
|
||||
ID_TIMETEXT_DURATION
|
||||
ID_TIMETEXT_DURATION,
|
||||
ID_AUTOSAVEPATH_BUTTON,
|
||||
ID_AUTOSAVEPATH_TEXT,
|
||||
ID_AUTOEXPORTPATH_BUTTON,
|
||||
ID_AUTOEXPORTPATH_TEXT,
|
||||
ID_AUTOSAVE_CHECKBOX,
|
||||
ID_AUTOEXPORT_CHECKBOX
|
||||
};
|
||||
|
||||
enum {
|
||||
CONTROL_GROUP_SAVE,
|
||||
CONTROL_GROUP_EXPORT
|
||||
};
|
||||
|
||||
// Post Timer Recording Actions
|
||||
// Ensure this matches the enum in Menus.cpp
|
||||
enum {
|
||||
POST_TIMER_RECORD_STOPPED = -3,
|
||||
POST_TIMER_RECORD_CANCEL_WAIT,
|
||||
POST_TIMER_RECORD_CANCEL,
|
||||
POST_TIMER_RECORD_NOTHING,
|
||||
POST_TIMER_RECORD_CLOSE,
|
||||
POST_TIMER_RECORD_RESTART,
|
||||
POST_TIMER_RECORD_SHUTDOWN
|
||||
};
|
||||
|
||||
const int kTimerInterval = 50; // ms
|
||||
@ -66,9 +90,16 @@ BEGIN_EVENT_TABLE(TimerRecordDialog, wxDialog)
|
||||
EVT_BUTTON(wxID_OK, TimerRecordDialog::OnOK)
|
||||
|
||||
EVT_TIMER(TIMER_ID, TimerRecordDialog::OnTimer)
|
||||
|
||||
EVT_BUTTON(ID_AUTOSAVEPATH_BUTTON, TimerRecordDialog::OnAutoSavePathButton_Click)
|
||||
EVT_BUTTON(ID_AUTOEXPORTPATH_BUTTON, TimerRecordDialog::OnAutoExportPathButton_Click)
|
||||
|
||||
EVT_CHECKBOX(ID_AUTOSAVE_CHECKBOX, TimerRecordDialog::OnAutoSaveCheckBox_Change)
|
||||
EVT_CHECKBOX(ID_AUTOEXPORT_CHECKBOX, TimerRecordDialog::OnAutoExportCheckBox_Change)
|
||||
|
||||
END_EVENT_TABLE()
|
||||
|
||||
TimerRecordDialog::TimerRecordDialog(wxWindow* parent)
|
||||
TimerRecordDialog::TimerRecordDialog(wxWindow* parent, bool bAlreadySaved)
|
||||
: wxDialog(parent, -1, _("Audacity Timer Record"), wxDefaultPosition,
|
||||
wxDefaultSize, wxCAPTION)
|
||||
{
|
||||
@ -88,6 +119,9 @@ TimerRecordDialog::TimerRecordDialog(wxWindow* parent)
|
||||
|
||||
m_pTimeTextCtrl_Duration = NULL;
|
||||
|
||||
// Do we allow the user to change the Automatic Save file?
|
||||
m_bProjectAlreadySaved = bAlreadySaved;
|
||||
|
||||
ShuttleGui S(this, eIsCreating);
|
||||
this->PopulateOrExchange(S);
|
||||
|
||||
@ -98,6 +132,10 @@ TimerRecordDialog::TimerRecordDialog(wxWindow* parent)
|
||||
|
||||
m_timer.SetOwner(this, TIMER_ID);
|
||||
m_timer.Start(kTimerInterval);
|
||||
|
||||
// Do we need to tidy up when the timer recording has been completed?
|
||||
m_bProjectCleanupRequired = !(this->HaveFilesToRecover());
|
||||
|
||||
}
|
||||
|
||||
TimerRecordDialog::~TimerRecordDialog()
|
||||
@ -210,6 +248,66 @@ void TimerRecordDialog::OnTimeText_Duration(wxCommandEvent& WXUNUSED(event))
|
||||
this->UpdateEnd(); // Keep Start constant and update End for changed Duration.
|
||||
}
|
||||
|
||||
// New events for timer recording automation
|
||||
void TimerRecordDialog::OnAutoSavePathButton_Click(wxCommandEvent& WXUNUSED(event))
|
||||
{
|
||||
wxString fName = FileSelector(_T("Save Timer Recording As"),
|
||||
m_fnAutoSaveFile.GetPath(),
|
||||
m_fnAutoSaveFile.GetFullName(),
|
||||
wxT("aup"),
|
||||
_("Audacity projects") + wxT(" (*.aup)|*.aup"),
|
||||
wxFD_SAVE | wxRESIZE_BORDER,
|
||||
this);
|
||||
|
||||
if (fName == wxT(""))
|
||||
return;
|
||||
|
||||
// MY: If project already exits then abort - we do not allow users to overwrite an existing project
|
||||
if (wxFileExists(fName)) {
|
||||
wxMessageDialog m(
|
||||
NULL,
|
||||
_("The selected file name could not be used\nfor Timer Recording because it would overwrite another project.\n\
|
||||
Please try again and select an original name."),
|
||||
_("Error Saving Timer Recording Project"),
|
||||
wxOK|wxICON_ERROR);
|
||||
m.ShowModal();
|
||||
return;
|
||||
}
|
||||
|
||||
// MY: Set this boolean to false so we now do a SaveAs at the end of the recording
|
||||
m_bProjectAlreadySaved = false;
|
||||
|
||||
m_fnAutoSaveFile = fName;
|
||||
m_fnAutoSaveFile.SetExt(wxT("aup"));
|
||||
this->UpdateTextBoxControls();
|
||||
}
|
||||
|
||||
void TimerRecordDialog::OnAutoExportPathButton_Click(wxCommandEvent& WXUNUSED(event))
|
||||
{
|
||||
AudacityProject* pProject = GetActiveProject();
|
||||
Exporter eExporter;
|
||||
|
||||
// Call the Exporter to set the options required
|
||||
if (eExporter.SetAutoExportOptions(pProject)) {
|
||||
// Populate the options so that we can destroy this instance of the Exporter
|
||||
m_fnAutoExportFile = eExporter.GetAutoExportFileName();
|
||||
m_iAutoExportFormat = eExporter.GetAutoExportFormat();
|
||||
m_iAutoExportSubFormat = eExporter.GetAutoExportSubFormat();
|
||||
m_iAutoExportFilterIndex = eExporter.GetAutoExportFilterIndex();
|
||||
|
||||
// Update the text controls
|
||||
this->UpdateTextBoxControls();
|
||||
}
|
||||
}
|
||||
|
||||
void TimerRecordDialog::OnAutoSaveCheckBox_Change(wxCommandEvent& WXUNUSED(event)) {
|
||||
EnableDisableAutoControls(m_pTimerAutoSaveCheckBoxCtrl->GetValue(), CONTROL_GROUP_SAVE);
|
||||
}
|
||||
|
||||
void TimerRecordDialog::OnAutoExportCheckBox_Change(wxCommandEvent& WXUNUSED(event)) {
|
||||
EnableDisableAutoControls(m_pTimerAutoExportCheckBoxCtrl->GetValue(), CONTROL_GROUP_EXPORT);
|
||||
}
|
||||
|
||||
void TimerRecordDialog::OnOK(wxCommandEvent& WXUNUSED(event))
|
||||
{
|
||||
this->TransferDataFromWindow();
|
||||
@ -220,6 +318,24 @@ void TimerRecordDialog::OnOK(wxCommandEvent& WXUNUSED(event))
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate that we have a Save and/or Export path setup if the appropriate check box is ticked
|
||||
wxString sTemp = m_fnAutoSaveFile.GetFullPath();
|
||||
if (m_pTimerAutoSaveCheckBoxCtrl->IsChecked()) {
|
||||
if (!m_fnAutoSaveFile.IsOk() || m_fnAutoSaveFile.IsDir()) {
|
||||
wxMessageBox(_("Automatic Save path is invalid."),
|
||||
_("Error in Automatic Save"), wxICON_EXCLAMATION | wxOK);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (m_pTimerAutoExportCheckBoxCtrl->IsChecked()) {
|
||||
|
||||
if (!m_fnAutoExportFile.IsOk() || m_fnAutoExportFile.IsDir()) {
|
||||
wxMessageBox(_("Automatic Export path is invalid."),
|
||||
_("Error in Automatic Export"), wxICON_EXCLAMATION | wxOK);
|
||||
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();
|
||||
@ -228,12 +344,95 @@ void TimerRecordDialog::OnOK(wxCommandEvent& WXUNUSED(event))
|
||||
gPrefs->Flush();
|
||||
}
|
||||
|
||||
///Runs the wait for start dialog. Returns false if the user clicks stop while we are recording
|
||||
///so that the high
|
||||
// <ANSWER-ME: so that the "high" what does what?>
|
||||
bool TimerRecordDialog::RunWaitDialog()
|
||||
void TimerRecordDialog::EnableDisableAutoControls(bool bEnable, int iControlGoup) {
|
||||
if (iControlGoup == CONTROL_GROUP_EXPORT) {
|
||||
// Enable or Disable the Export controls
|
||||
if (bEnable) {
|
||||
m_pTimerExportPathTextCtrl->Enable();
|
||||
m_pTimerExportPathButtonCtrl->Enable();
|
||||
}
|
||||
else {
|
||||
m_pTimerExportPathTextCtrl->Disable();
|
||||
m_pTimerExportPathButtonCtrl->Disable();
|
||||
}
|
||||
}
|
||||
else if (iControlGoup == CONTROL_GROUP_SAVE) {
|
||||
// Enable or Disable the Save controls
|
||||
if (bEnable) {
|
||||
m_pTimerSavePathTextCtrl->Enable();
|
||||
m_pTimerSavePathButtonCtrl->Enable();
|
||||
}
|
||||
else {
|
||||
m_pTimerSavePathTextCtrl->Disable();
|
||||
m_pTimerSavePathButtonCtrl->Disable();
|
||||
}
|
||||
}
|
||||
|
||||
// Enable or disable the Choice box - if there is no Save or Export then this will be disabled
|
||||
if (m_pTimerAutoSaveCheckBoxCtrl->GetValue() || m_pTimerAutoExportCheckBoxCtrl->GetValue()) {
|
||||
m_pTimerAfterCompleteChoiceCtrl->Enable();
|
||||
}
|
||||
else {
|
||||
m_pTimerAfterCompleteChoiceCtrl->SetSelection(POST_TIMER_RECORD_NOTHING);
|
||||
m_pTimerAfterCompleteChoiceCtrl->Disable();
|
||||
}
|
||||
}
|
||||
|
||||
void TimerRecordDialog::UpdateTextBoxControls() {
|
||||
// Will update the text box controls
|
||||
m_pTimerSavePathTextCtrl->SetValue(m_fnAutoSaveFile.GetFullPath());
|
||||
m_pTimerExportPathTextCtrl->SetValue(m_fnAutoExportFile.GetFullPath());
|
||||
|
||||
// MY: Ensure we still display "Current Project" if this has already been saved
|
||||
if (m_bProjectAlreadySaved) {
|
||||
m_pTimerSavePathTextCtrl->SetValue(_("Current Project"));
|
||||
}
|
||||
}
|
||||
|
||||
// Copied from AutoRecovery.cpp - for use with Timer Recording Improvements
|
||||
bool TimerRecordDialog::HaveFilesToRecover()
|
||||
{
|
||||
wxDir dir(FileNames::AutoSaveDir());
|
||||
if (!dir.IsOpened())
|
||||
{
|
||||
wxMessageBox(_("Could not enumerate files in auto save directory."),
|
||||
_("Error"), wxICON_STOP);
|
||||
return false;
|
||||
}
|
||||
|
||||
wxString filename;
|
||||
bool c = dir.GetFirst(&filename, wxT("*.autosave"), wxDIR_FILES);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
bool TimerRecordDialog::RemoveAllAutoSaveFiles()
|
||||
{
|
||||
wxArrayString files;
|
||||
wxDir::GetAllFiles(FileNames::AutoSaveDir(), &files,
|
||||
wxT("*.autosave"), wxDIR_FILES);
|
||||
|
||||
for (unsigned int i = 0; i < files.GetCount(); i++)
|
||||
{
|
||||
if (!wxRemoveFile(files[i]))
|
||||
{
|
||||
// I don't think this error message is actually useful.
|
||||
// -dmazzoni
|
||||
//wxMessageBox(wxT("Could not remove auto save file: " + files[i]),
|
||||
// _("Error"), wxICON_STOP);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Runs the wait for start dialog. Returns -1 if the user clicks stop while we are recording
|
||||
/// or if the post recording actions fail.
|
||||
int TimerRecordDialog::RunWaitDialog()
|
||||
{
|
||||
AudacityProject* pProject = GetActiveProject();
|
||||
|
||||
int updateResult = eProgressSuccess;
|
||||
|
||||
if (m_DateTime_Start > wxDateTime::UNow())
|
||||
@ -242,7 +441,7 @@ bool TimerRecordDialog::RunWaitDialog()
|
||||
if (updateResult != eProgressSuccess)
|
||||
{
|
||||
// Don't proceed, but don't treat it as canceled recording. User just canceled waiting.
|
||||
return true;
|
||||
return POST_TIMER_RECORD_CANCEL_WAIT;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -282,14 +481,136 @@ bool TimerRecordDialog::RunWaitDialog()
|
||||
|
||||
// Let the caller handle cancellation or failure from recording progress.
|
||||
if (updateResult == eProgressCancelled || updateResult == eProgressFailed)
|
||||
return false;
|
||||
return POST_TIMER_RECORD_CANCEL;
|
||||
|
||||
// Success, so let's automatically save it, for safety's sake.
|
||||
// If user hadn't saved it before, they'll see the Save As dialog.
|
||||
// If user had saved it before, it will safely be saved, automatically.
|
||||
pProject->Save();
|
||||
return ExecutePostRecordActions((updateResult == eProgressStopped));
|
||||
}
|
||||
|
||||
return true;
|
||||
int TimerRecordDialog::ExecutePostRecordActions(bool bWasStopped) {
|
||||
// We no longer automatically (and silently) call ->Save() when the
|
||||
// timer recording is completed!
|
||||
// We can now Save and/or Export depending on the options selected by
|
||||
// the user.
|
||||
// Once completed, we can also close Audacity, restart the system or
|
||||
// shutdown the system.
|
||||
// If there was any error with the auto save or export then we will not do
|
||||
// the actions requested and instead present an error mesasge to the user.
|
||||
// Finally, if there is no post-record action selected then we will output
|
||||
// a dialog detailing what has been carried out instead.
|
||||
|
||||
AudacityProject* pProject = GetActiveProject();
|
||||
|
||||
bool bSaveOK = false;
|
||||
bool bExportOK = false;
|
||||
int iPostRecordAction = m_pTimerAfterCompleteChoiceCtrl->GetSelection();
|
||||
int iOverriddenAction = iPostRecordAction;
|
||||
bool bErrorOverride = false;
|
||||
|
||||
// Do Automatic Save?
|
||||
if (m_bAutoSaveEnabled) {
|
||||
|
||||
// MY: If this project has already been saved then simply execute a Save here
|
||||
if (m_bProjectAlreadySaved) {
|
||||
bSaveOK = pProject->Save();
|
||||
}
|
||||
else {
|
||||
bSaveOK = pProject->SaveFromTimerRecording(m_fnAutoSaveFile);
|
||||
}
|
||||
}
|
||||
|
||||
// Do Automatic Export?
|
||||
if (m_bAutoExportEnabled) {
|
||||
bExportOK = pProject->ExportFromTimerRecording(m_fnAutoExportFile, m_iAutoExportFormat,
|
||||
m_iAutoExportSubFormat, m_iAutoExportFilterIndex);
|
||||
}
|
||||
|
||||
// Check if we need to override the post recording action
|
||||
bErrorOverride = ((m_bAutoSaveEnabled && !bSaveOK) || (m_bAutoExportEnabled && !bExportOK));
|
||||
if (bErrorOverride || bWasStopped) {
|
||||
iPostRecordAction = POST_TIMER_RECORD_NOTHING;
|
||||
}
|
||||
|
||||
if (iPostRecordAction == POST_TIMER_RECORD_NOTHING) {
|
||||
// If there is no post-record action then we can show a message indicating what has been done
|
||||
|
||||
wxString sMessage = (bWasStopped ? _("Timer Recording Stopped.") :
|
||||
_("Timer Recording Completed."));
|
||||
|
||||
if (m_bAutoSaveEnabled) {
|
||||
if (bSaveOK) {
|
||||
sMessage.Printf("%s\n\nRecording saved: %s",
|
||||
sMessage, m_fnAutoSaveFile.GetFullPath());
|
||||
}
|
||||
else {
|
||||
sMessage.Printf("%s\n\nError saving recording.", sMessage);
|
||||
}
|
||||
}
|
||||
if (m_bAutoExportEnabled) {
|
||||
if (bExportOK) {
|
||||
sMessage.Printf("%s\n\nRecording exported: %s",
|
||||
sMessage, m_fnAutoExportFile.GetFullPath());
|
||||
}
|
||||
else {
|
||||
sMessage.Printf("%s\n\nError exporting recording.", sMessage);
|
||||
}
|
||||
}
|
||||
|
||||
if (bErrorOverride) {
|
||||
|
||||
if ((iOverriddenAction != iPostRecordAction) &&
|
||||
(iOverriddenAction != POST_TIMER_RECORD_NOTHING)) {
|
||||
// Inform the user that we have overridden the selected action
|
||||
sMessage.Printf("%s\n\n'%s' has been canceled due to the error(s) noted above.",
|
||||
sMessage,
|
||||
m_pTimerAfterCompleteChoiceCtrl->GetString(iOverriddenAction));
|
||||
}
|
||||
|
||||
// Show Error Message Box
|
||||
wxMessageBox(sMessage, _("Error"), wxICON_EXCLAMATION | wxOK);
|
||||
}
|
||||
else {
|
||||
|
||||
if (bWasStopped && (iOverriddenAction != POST_TIMER_RECORD_NOTHING)) {
|
||||
sMessage.Printf("%s\n\n'%s' has been cancelled as the recording was stopped.",
|
||||
sMessage,
|
||||
m_pTimerAfterCompleteChoiceCtrl->GetString(iOverriddenAction));
|
||||
}
|
||||
|
||||
wxMessageBox(sMessage, _("Timer Recording"), wxICON_INFORMATION | wxOK);
|
||||
}
|
||||
}
|
||||
|
||||
// MY: Lets do some actions that only apply to Exit/Restart/Shutdown
|
||||
if (iPostRecordAction >= POST_TIMER_RECORD_CLOSE) {
|
||||
do {
|
||||
// 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));
|
||||
if (iDelayOutcome != eProgressSuccess) {
|
||||
// Cancel the action!
|
||||
iPostRecordAction = POST_TIMER_RECORD_NOTHING;
|
||||
// Set this to true to avoid any chance of the temp files being deleted
|
||||
bErrorOverride = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// If we have simply recorded, exported and then plan to Exit/Restart/Shutdown
|
||||
// then we will have a temporary project setup. Let's get rid of that!
|
||||
if (m_bAutoExportEnabled && !m_bAutoSaveEnabled) {
|
||||
DirManager::CleanTempDir();
|
||||
}
|
||||
} while (false);
|
||||
}
|
||||
|
||||
// Do we need to cleanup the orphaned temporary project?
|
||||
if (m_bProjectCleanupRequired && !bErrorOverride) {
|
||||
RemoveAllAutoSaveFiles();
|
||||
}
|
||||
|
||||
// Return the action as required
|
||||
return iPostRecordAction;
|
||||
}
|
||||
|
||||
wxString TimerRecordDialog::GetDisplayDate( wxDateTime & dt )
|
||||
@ -341,84 +662,166 @@ wxPrintf(wxT("%s\n"), dt.Format().c_str());
|
||||
return dt.FormatDate() + wxT(" ") + dt.FormatTime();
|
||||
}
|
||||
|
||||
TimerRecordPathCtrl * TimerRecordDialog::NewPathControl(wxWindow *wParent, const int iID,
|
||||
const wxString &sCaption, const wxString &sValue)
|
||||
{
|
||||
TimerRecordPathCtrl * pTextCtrl;
|
||||
pTextCtrl = safenew TimerRecordPathCtrl(wParent, iID, sValue);
|
||||
pTextCtrl->SetName(sCaption);
|
||||
return pTextCtrl;
|
||||
}
|
||||
|
||||
void TimerRecordDialog::PopulateOrExchange(ShuttleGui& S)
|
||||
{
|
||||
bool bAutoSave = gPrefs->ReadBool("/TimerRecord/AutoSave", false);
|
||||
bool bAutoExport = gPrefs->ReadBool("/TimerRecord/AutoExport", false);
|
||||
int iPostTimerRecordAction = gPrefs->ReadLong("/TimerRecord/PostAction", 0);
|
||||
|
||||
S.SetBorder(5);
|
||||
S.StartVerticalLay(true);
|
||||
S.StartMultiColumn(2, wxCENTER);
|
||||
{
|
||||
/* i18n-hint: This string is used to configure the controls for times when the recording is
|
||||
* started and stopped. As such it is important that only the alphabetic parts of the string
|
||||
* are translated, with the numbers left exactly as they are.
|
||||
* The 'h' indicates the first number displayed is hours, the 'm' indicates the second number
|
||||
* displayed is minutes, and the 's' indicates that the third number displayed is seconds.
|
||||
*/
|
||||
wxString strFormat = _("099 h 060 m 060 s");
|
||||
S.StartStatic(_("Start Date and Time"), true);
|
||||
S.StartVerticalLay(true);
|
||||
{
|
||||
m_pDatePickerCtrl_Start =
|
||||
safenew wxDatePickerCtrl(this, // wxWindow *parent,
|
||||
ID_DATEPICKER_START, // wxWindowID id,
|
||||
m_DateTime_Start); // const wxDateTime& dt = wxDefaultDateTime,
|
||||
// const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDP_DEFAULT | wxDP_SHOWCENTURY, const wxValidator& validator = wxDefaultValidator, const wxString& name = "datectrl")
|
||||
m_pDatePickerCtrl_Start->SetName(_("Start Date"));
|
||||
m_pDatePickerCtrl_Start->SetRange(wxDateTime::Today(), wxInvalidDateTime); // No backdating.
|
||||
S.AddWindow(m_pDatePickerCtrl_Start);
|
||||
/* i18n-hint: This string is used to configure the controls for times when the recording is
|
||||
* started and stopped. As such it is important that only the alphabetic parts of the string
|
||||
* are translated, with the numbers left exactly as they are.
|
||||
* The 'h' indicates the first number displayed is hours, the 'm' indicates the second number
|
||||
* displayed is minutes, and the 's' indicates that the third number displayed is seconds.
|
||||
*/
|
||||
wxString strFormat = _("099 h 060 m 060 s");
|
||||
S.StartStatic(_("Start Date and Time"), true);
|
||||
{
|
||||
m_pDatePickerCtrl_Start =
|
||||
new wxDatePickerCtrl(this, // wxWindow *parent,
|
||||
ID_DATEPICKER_START, // wxWindowID id,
|
||||
m_DateTime_Start); // const wxDateTime& dt = wxDefaultDateTime,
|
||||
// const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDP_DEFAULT | wxDP_SHOWCENTURY, const wxValidator& validator = wxDefaultValidator, const wxString& name = "datectrl")
|
||||
m_pDatePickerCtrl_Start->SetName(_("Start Date"));
|
||||
m_pDatePickerCtrl_Start->SetRange(wxDateTime::Today(), wxInvalidDateTime); // No backdating.
|
||||
S.AddWindow(m_pDatePickerCtrl_Start);
|
||||
|
||||
m_pTimeTextCtrl_Start = safenew NumericTextCtrl(
|
||||
NumericConverter::TIME, this, ID_TIMETEXT_START);
|
||||
m_pTimeTextCtrl_Start->SetName(_("Start Time"));
|
||||
m_pTimeTextCtrl_Start->SetFormatString(strFormat);
|
||||
m_pTimeTextCtrl_Start->
|
||||
SetValue(wxDateTime_to_AudacityTime(m_DateTime_Start));
|
||||
S.AddWindow(m_pTimeTextCtrl_Start);
|
||||
m_pTimeTextCtrl_Start->EnableMenu(false);
|
||||
m_pTimeTextCtrl_Start = new NumericTextCtrl(
|
||||
NumericConverter::TIME, this, ID_TIMETEXT_START);
|
||||
m_pTimeTextCtrl_Start->SetName(_("Start Time"));
|
||||
m_pTimeTextCtrl_Start->SetFormatString(strFormat);
|
||||
m_pTimeTextCtrl_Start->
|
||||
SetValue(wxDateTime_to_AudacityTime(m_DateTime_Start));
|
||||
S.AddWindow(m_pTimeTextCtrl_Start);
|
||||
m_pTimeTextCtrl_Start->EnableMenu(false);
|
||||
}
|
||||
S.EndStatic();
|
||||
|
||||
S.StartStatic(_("End Date and Time"), true);
|
||||
{
|
||||
m_pDatePickerCtrl_End =
|
||||
new wxDatePickerCtrl(this, // wxWindow *parent,
|
||||
ID_DATEPICKER_END, // wxWindowID id,
|
||||
m_DateTime_End); // const wxDateTime& dt = wxDefaultDateTime,
|
||||
// const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDP_DEFAULT | wxDP_SHOWCENTURY, const wxValidator& validator = wxDefaultValidator, const wxString& name = "datectrl")
|
||||
m_pDatePickerCtrl_End->SetRange(m_DateTime_Start, wxInvalidDateTime); // No backdating.
|
||||
m_pDatePickerCtrl_End->SetName(_("End Date"));
|
||||
S.AddWindow(m_pDatePickerCtrl_End);
|
||||
|
||||
m_pTimeTextCtrl_End = new NumericTextCtrl(
|
||||
NumericConverter::TIME, this, ID_TIMETEXT_END);
|
||||
m_pTimeTextCtrl_End->SetName(_("End Time"));
|
||||
m_pTimeTextCtrl_End->SetFormatString(strFormat);
|
||||
m_pTimeTextCtrl_End->SetValue(wxDateTime_to_AudacityTime(m_DateTime_End));
|
||||
S.AddWindow(m_pTimeTextCtrl_End);
|
||||
m_pTimeTextCtrl_End->EnableMenu(false);
|
||||
}
|
||||
S.EndStatic();
|
||||
|
||||
S.StartStatic(_("Duration"), true);
|
||||
{
|
||||
/* i18n-hint: This string is used to configure the controls which shows the recording
|
||||
* duration. As such it is important that only the alphabetic parts of the string
|
||||
* are translated, with the numbers left exactly as they are.
|
||||
* The string 'days' indicates that the first number in the control will be the number of days,
|
||||
* then the 'h' indicates the second number displayed is hours, the 'm' indicates the third
|
||||
* number displayed is minutes, and the 's' indicates that the fourth number displayed is
|
||||
* seconds.
|
||||
*/
|
||||
wxString strFormat1 = _("099 days 024 h 060 m 060 s");
|
||||
m_pTimeTextCtrl_Duration = new NumericTextCtrl(NumericConverter::TIME, this, ID_TIMETEXT_DURATION);
|
||||
m_pTimeTextCtrl_Duration->SetName(_("Duration"));
|
||||
m_pTimeTextCtrl_Duration->SetFormatString(strFormat1);
|
||||
m_pTimeTextCtrl_Duration->SetValue(m_TimeSpan_Duration.GetSeconds().ToDouble());
|
||||
S.AddWindow(m_pTimeTextCtrl_Duration);
|
||||
m_pTimeTextCtrl_Duration->EnableMenu(false);
|
||||
}
|
||||
S.EndStatic();
|
||||
}
|
||||
S.EndStatic();
|
||||
S.EndVerticalLay();
|
||||
|
||||
S.StartStatic(_("End Date and Time"), true);
|
||||
S.StartVerticalLay(true);
|
||||
{
|
||||
m_pDatePickerCtrl_End =
|
||||
safenew wxDatePickerCtrl(this, // wxWindow *parent,
|
||||
ID_DATEPICKER_END, // wxWindowID id,
|
||||
m_DateTime_End); // const wxDateTime& dt = wxDefaultDateTime,
|
||||
// const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDP_DEFAULT | wxDP_SHOWCENTURY, const wxValidator& validator = wxDefaultValidator, const wxString& name = "datectrl")
|
||||
m_pDatePickerCtrl_End->SetRange(m_DateTime_Start, wxInvalidDateTime); // No backdating.
|
||||
m_pDatePickerCtrl_End->SetName(_("End Date"));
|
||||
S.AddWindow(m_pDatePickerCtrl_End);
|
||||
S.StartStatic(_("Automatic Save"), true);
|
||||
{
|
||||
// If checked, the project will be saved when the recording is completed
|
||||
m_pTimerAutoSaveCheckBoxCtrl = S.Id(ID_AUTOSAVE_CHECKBOX).AddCheckBox(_("Enable &Automatic Save?"),
|
||||
(bAutoSave ? "true" : "false"));
|
||||
S.StartMultiColumn(3, wxEXPAND);
|
||||
{
|
||||
wxString sInitialValue = wxT("");
|
||||
AudacityProject* pProject = GetActiveProject();
|
||||
wxString sSaveValue = pProject->GetFileName();
|
||||
if (sSaveValue != wxEmptyString) {
|
||||
m_fnAutoSaveFile.Assign(sSaveValue);
|
||||
sInitialValue = _("Current Project");
|
||||
}
|
||||
S.AddPrompt(_("Save Project As:"));
|
||||
m_pTimerSavePathTextCtrl = NewPathControl(this, ID_AUTOSAVEPATH_TEXT, _("Save Project As:"), sInitialValue);
|
||||
m_pTimerSavePathTextCtrl->SetEditable(false);
|
||||
S.AddWindow(m_pTimerSavePathTextCtrl);
|
||||
m_pTimerSavePathButtonCtrl = S.Id(ID_AUTOSAVEPATH_BUTTON).AddButton(_("Select..."));
|
||||
}
|
||||
S.EndMultiColumn();
|
||||
}
|
||||
S.EndStatic();
|
||||
|
||||
m_pTimeTextCtrl_End = safenew NumericTextCtrl(
|
||||
NumericConverter::TIME, this, ID_TIMETEXT_END);
|
||||
m_pTimeTextCtrl_End->SetName(_("End Time"));
|
||||
m_pTimeTextCtrl_End->SetFormatString(strFormat);
|
||||
m_pTimeTextCtrl_End->SetValue(wxDateTime_to_AudacityTime(m_DateTime_End));
|
||||
S.AddWindow(m_pTimeTextCtrl_End);
|
||||
m_pTimeTextCtrl_End->EnableMenu(false);
|
||||
}
|
||||
S.EndStatic();
|
||||
S.StartStatic(_("Automatic Export"), true);
|
||||
{
|
||||
m_pTimerAutoExportCheckBoxCtrl = S.Id(ID_AUTOEXPORT_CHECKBOX).AddCheckBox(_("Enable Automatic &Export?"), (bAutoExport ? "true" : "false"));
|
||||
S.StartMultiColumn(3, wxEXPAND);
|
||||
{
|
||||
S.AddPrompt(_("Export Project As:"));
|
||||
m_pTimerExportPathTextCtrl = NewPathControl(this, ID_AUTOEXPORTPATH_TEXT, _("Export Project As:"), _(""));
|
||||
m_pTimerExportPathTextCtrl->SetEditable(false);
|
||||
S.AddWindow(m_pTimerExportPathTextCtrl);
|
||||
m_pTimerExportPathButtonCtrl = S.Id(ID_AUTOEXPORTPATH_BUTTON).AddButton(_("Select..."));
|
||||
}
|
||||
S.EndMultiColumn();
|
||||
}
|
||||
S.EndStatic();
|
||||
|
||||
S.StartStatic(_("Options"), true);
|
||||
{
|
||||
|
||||
wxArrayString arrayOptions;
|
||||
arrayOptions.Add(_("Do Nothing"));
|
||||
arrayOptions.Add(_("Exit Audacity"));
|
||||
arrayOptions.Add(_("Restart System"));
|
||||
arrayOptions.Add(_("Shutdown System"));
|
||||
|
||||
m_sTimerAfterCompleteOptionsArray.Add(arrayOptions.Item(0));
|
||||
m_sTimerAfterCompleteOptionsArray.Add(arrayOptions.Item(1));
|
||||
#ifdef __WINDOWS__
|
||||
m_sTimerAfterCompleteOptionsArray.Add(arrayOptions.Item(2));
|
||||
m_sTimerAfterCompleteOptionsArray.Add(arrayOptions.Item(3));
|
||||
#endif
|
||||
m_sTimerAfterCompleteOption = arrayOptions.Item(iPostTimerRecordAction);
|
||||
|
||||
m_pTimerAfterCompleteChoiceCtrl = S.AddChoice(_("After Recording Completes:"),
|
||||
m_sTimerAfterCompleteOption,
|
||||
&m_sTimerAfterCompleteOptionsArray);
|
||||
}
|
||||
S.EndStatic();
|
||||
|
||||
S.StartStatic(_("Duration"), true);
|
||||
{
|
||||
/* i18n-hint: This string is used to configure the controls which shows the recording
|
||||
* duration. As such it is important that only the alphabetic parts of the string
|
||||
* are translated, with the numbers left exactly as they are.
|
||||
* The string 'days' indicates that the first number in the control will be the number of days,
|
||||
* then the 'h' indicates the second number displayed is hours, the 'm' indicates the third
|
||||
* number displayed is minutes, and the 's' indicates that the fourth number displayed is
|
||||
* seconds.
|
||||
*/
|
||||
wxString strFormat1 = _("099 days 024 h 060 m 060 s");
|
||||
m_pTimeTextCtrl_Duration = safenew NumericTextCtrl(
|
||||
NumericConverter::TIME, this, ID_TIMETEXT_DURATION);
|
||||
m_pTimeTextCtrl_Duration->SetName(_("Duration"));
|
||||
m_pTimeTextCtrl_Duration->SetFormatString(strFormat1);
|
||||
m_pTimeTextCtrl_Duration->
|
||||
SetValue(m_TimeSpan_Duration.GetSeconds().ToDouble());
|
||||
S.AddWindow(m_pTimeTextCtrl_Duration);
|
||||
m_pTimeTextCtrl_Duration->EnableMenu(false);
|
||||
}
|
||||
S.EndStatic();
|
||||
S.EndVerticalLay();
|
||||
}
|
||||
S.EndVerticalLay();
|
||||
S.EndMultiColumn();
|
||||
|
||||
S.AddStandardButtons();
|
||||
|
||||
@ -426,6 +829,9 @@ void TimerRecordDialog::PopulateOrExchange(ShuttleGui& S)
|
||||
Fit();
|
||||
SetMinSize(GetSize());
|
||||
Center();
|
||||
|
||||
EnableDisableAutoControls(bAutoSave, CONTROL_GROUP_SAVE);
|
||||
EnableDisableAutoControls(bAutoExport, CONTROL_GROUP_EXPORT);
|
||||
}
|
||||
|
||||
bool TimerRecordDialog::TransferDataFromWindow()
|
||||
@ -455,6 +861,18 @@ bool TimerRecordDialog::TransferDataFromWindow()
|
||||
|
||||
m_TimeSpan_Duration = m_DateTime_End - m_DateTime_Start;
|
||||
|
||||
// Pull the settings from the auto save/export controls and write to the pref file
|
||||
m_bAutoSaveEnabled = m_pTimerAutoSaveCheckBoxCtrl->GetValue();
|
||||
m_bAutoExportEnabled = m_pTimerAutoExportCheckBoxCtrl->GetValue();
|
||||
|
||||
// MY: Obtain the index from the choice control so we can save to the prefs file
|
||||
int iPostRecordAction = m_pTimerAfterCompleteChoiceCtrl->GetSelection();
|
||||
|
||||
// Save the options back to the prefs file
|
||||
gPrefs->Write("/TimerRecord/AutoSave", m_bAutoSaveEnabled);
|
||||
gPrefs->Write("/TimerRecord/AutoExport", m_bAutoExportEnabled);
|
||||
gPrefs->Write("/TimerRecord/PostAction", iPostRecordAction);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -501,3 +919,40 @@ int TimerRecordDialog::WaitForStart()
|
||||
}
|
||||
return updateResult;
|
||||
}
|
||||
|
||||
int TimerRecordDialog::PreActionDelay(int iActionIndex, bool bSaved, bool bExported)
|
||||
{
|
||||
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";
|
||||
}
|
||||
sMessage.Printf(_("Timer Recording completed: Recording has been %s as instructed.\n\n'%s' will occur shortly...\n"),
|
||||
sDone, sAction);
|
||||
|
||||
wxDateTime dtNow = wxDateTime::UNow();
|
||||
wxTimeSpan tsWait = wxTimeSpan(0, 1, 0, 0);
|
||||
wxDateTime dtActionTime = dtNow.Add(tsWait);
|
||||
|
||||
TimerProgressDialog dlgAction(tsWait.GetMilliseconds().GetValue(),
|
||||
_("Audacity Timer Record - Waiting"),
|
||||
sMessage,
|
||||
pdlgHideStopButton);
|
||||
|
||||
int iUpdateResult = eProgressSuccess;
|
||||
bool bIsTime = false;
|
||||
while (iUpdateResult == eProgressSuccess && !bIsTime)
|
||||
{
|
||||
wxMilliSleep(10);
|
||||
iUpdateResult = dlgAction.Update();
|
||||
bIsTime = (dtActionTime <= wxDateTime::UNow());
|
||||
}
|
||||
return iUpdateResult;
|
||||
}
|
||||
|
@ -17,24 +17,46 @@
|
||||
#define __AUDACITY_TIMERRECORD_DIALOG__
|
||||
|
||||
#include <wx/dialog.h>
|
||||
#include <wx/textctrl.h>
|
||||
#include <wx/datectrl.h>
|
||||
#include <wx/calctrl.h>
|
||||
#include <wx/timer.h>
|
||||
#include <wx/checkbox.h>
|
||||
#include "export/Export.h"
|
||||
|
||||
class wxTimerEvent;
|
||||
|
||||
class NumericTextCtrl;
|
||||
class ShuttleGui;
|
||||
class TimerRecordPathCtrl;
|
||||
|
||||
class TimerRecordPathCtrl final : public wxTextCtrl
|
||||
{
|
||||
// MY: Class that inherits from the wxTextCtrl class.
|
||||
// We override AcceptsFocusFromKeyboard in order to add
|
||||
// the text controls to the Tab Order.
|
||||
public:
|
||||
TimerRecordPathCtrl(wxWindow * parent, wxWindowID id, const wxString &value
|
||||
= wxEmptyString, const wxPoint &pos = wxDefaultPosition, const wxSize &
|
||||
size = wxDefaultSize, long style = 0, const wxValidator & validator =
|
||||
wxDefaultValidator, const wxString & name = wxTextCtrlNameStr)
|
||||
:wxTextCtrl(parent, id, value, pos, size, style, validator, name) {};
|
||||
~TimerRecordPathCtrl() {};
|
||||
|
||||
virtual bool AcceptsFocusFromKeyboard() const override {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class TimerRecordDialog final : public wxDialog
|
||||
{
|
||||
public:
|
||||
TimerRecordDialog(wxWindow* parent);
|
||||
TimerRecordDialog(wxWindow* parent, bool bAlreadySaved);
|
||||
~TimerRecordDialog();
|
||||
|
||||
void OnTimer(wxTimerEvent& event);
|
||||
///Runs the wait for start dialog. Returns false if the user clicks stop.
|
||||
bool RunWaitDialog();
|
||||
int RunWaitDialog();
|
||||
|
||||
private:
|
||||
void OnDatePicker_Start(wxDateEvent& event);
|
||||
@ -54,6 +76,25 @@ private:
|
||||
void UpdateEnd(); // Update m_DateTime_End and ctrls based on m_DateTime_Start and m_TimeSpan_Duration.
|
||||
int WaitForStart();
|
||||
|
||||
// Timer Recording Automation Control Events
|
||||
void OnAutoSavePathButton_Click(wxCommandEvent& event);
|
||||
void OnAutoExportPathButton_Click(wxCommandEvent& event);
|
||||
void OnAutoSaveCheckBox_Change(wxCommandEvent& event);
|
||||
void OnAutoExportCheckBox_Change(wxCommandEvent& event);
|
||||
// Timer Recording Automation Routines
|
||||
void EnableDisableAutoControls(bool bEnable, int iControlGoup);
|
||||
void UpdateTextBoxControls();
|
||||
// Tidy up after Timer Recording
|
||||
bool HaveFilesToRecover();
|
||||
bool RemoveAllAutoSaveFiles();
|
||||
|
||||
// Add Path Controls to Form
|
||||
TimerRecordPathCtrl *NewPathControl(wxWindow *wParent, const int iID, const wxString &sCaption, const wxString &sValue);
|
||||
|
||||
int ExecutePostRecordActions(bool bWasStopped);
|
||||
|
||||
int PreActionDelay(int iActionIndex, bool bSaved, bool bExported);
|
||||
|
||||
private:
|
||||
wxDateTime m_DateTime_Start;
|
||||
wxDateTime m_DateTime_End;
|
||||
@ -70,6 +111,34 @@ private:
|
||||
|
||||
wxTimer m_timer;
|
||||
|
||||
// Controls for Auto Save/Export
|
||||
wxCheckBox *m_pTimerAutoSaveCheckBoxCtrl;
|
||||
TimerRecordPathCtrl *m_pTimerSavePathTextCtrl;
|
||||
wxButton *m_pTimerSavePathButtonCtrl;
|
||||
wxCheckBox *m_pTimerAutoExportCheckBoxCtrl;
|
||||
TimerRecordPathCtrl *m_pTimerExportPathTextCtrl;
|
||||
wxButton *m_pTimerExportPathButtonCtrl;
|
||||
|
||||
// After Timer Record Options Choice
|
||||
wxChoice *m_pTimerAfterCompleteChoiceCtrl;
|
||||
|
||||
// After Timer Record do we need to clean up?
|
||||
bool m_bProjectCleanupRequired;
|
||||
|
||||
// Variables for the Auto Save/Export
|
||||
bool m_bAutoSaveEnabled;
|
||||
wxFileName m_fnAutoSaveFile;
|
||||
bool m_bAutoExportEnabled;
|
||||
wxFileName m_fnAutoExportFile;
|
||||
int m_iAutoExportFormat;
|
||||
int m_iAutoExportSubFormat;
|
||||
int m_iAutoExportFilterIndex;
|
||||
bool m_bProjectAlreadySaved;
|
||||
|
||||
// Variables for After Timer Recording Option
|
||||
wxString m_sTimerAfterCompleteOption;
|
||||
wxArrayString m_sTimerAfterCompleteOptionsArray;
|
||||
|
||||
DECLARE_EVENT_TABLE();
|
||||
};
|
||||
|
||||
|
@ -640,9 +640,9 @@ bool Exporter::GetFilename()
|
||||
!mFilename.FileExists()) {
|
||||
// Warn and return to the dialog
|
||||
wxMessageBox(_("You are attempting to overwrite an aliased file that is missing.\n\
|
||||
The file cannot be written because the path is needed to restore the original audio to the project.\n\
|
||||
Choose File > Check Dependencies to view the locations of all missing files.\n\
|
||||
If you still wish to export, please choose a different filename or folder."));
|
||||
The file cannot be written because the path is needed to restore the original audio to the project.\n\
|
||||
Choose File > Check Dependencies to view the locations of all missing files.\n\
|
||||
If you still wish to export, please choose a different filename or folder."));
|
||||
overwritingMissingAlias = true;
|
||||
}
|
||||
}
|
||||
@ -886,6 +886,253 @@ void Exporter::OnFilterChanged(wxFileCtrlEvent & evt)
|
||||
mBook->ChangeSelection(index);
|
||||
}
|
||||
|
||||
bool Exporter::ProcessFromTimerRecording(AudacityProject *project,
|
||||
bool selectedOnly,
|
||||
double t0,
|
||||
double t1,
|
||||
wxFileName fnFile,
|
||||
int iFormat,
|
||||
int iSubFormat,
|
||||
int iFilterIndex)
|
||||
{
|
||||
// Save parms
|
||||
mProject = project;
|
||||
mSelectedOnly = selectedOnly;
|
||||
mT0 = t0;
|
||||
mT1 = t1;
|
||||
|
||||
// Auto Export Parameters
|
||||
mFilename = fnFile;
|
||||
mFormat = iFormat;
|
||||
mSubFormat = iSubFormat;
|
||||
mFilterIndex = iFilterIndex;
|
||||
|
||||
// Gather track information
|
||||
if (!ExamineTracks()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for down mixing
|
||||
if (!CheckMix()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ensure filename doesn't interfere with project files.
|
||||
if (!CheckFilename()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Export the tracks
|
||||
bool success = ExportTracks();
|
||||
|
||||
// Get rid of mixerspec
|
||||
if (mMixerSpec) {
|
||||
delete mMixerSpec;
|
||||
mMixerSpec = NULL;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
int Exporter::GetAutoExportFormat() {
|
||||
return mFormat;
|
||||
}
|
||||
|
||||
int Exporter::GetAutoExportSubFormat() {
|
||||
return mSubFormat;
|
||||
}
|
||||
|
||||
int Exporter::GetAutoExportFilterIndex() {
|
||||
return mFormat;
|
||||
}
|
||||
|
||||
wxFileName Exporter::GetAutoExportFileName() {
|
||||
return mFilename;
|
||||
}
|
||||
|
||||
bool Exporter::SetAutoExportOptions(AudacityProject *project) {
|
||||
mFormat = -1;
|
||||
mProject = project;
|
||||
|
||||
wxString maskString;
|
||||
wxString defaultFormat = gPrefs->Read(wxT("/Export/Format"),
|
||||
wxT("WAV"));
|
||||
|
||||
mFilterIndex = 0;
|
||||
|
||||
for (size_t i = 0; i < mPlugins.GetCount(); i++) {
|
||||
for (int j = 0; j < mPlugins[i]->GetFormatCount(); j++)
|
||||
{
|
||||
maskString += mPlugins[i]->GetMask(j) + wxT("|");
|
||||
if (mPlugins[i]->GetFormat(j) == defaultFormat) {
|
||||
mFormat = i;
|
||||
mSubFormat = j;
|
||||
}
|
||||
if (mFormat == -1) mFilterIndex++;
|
||||
}
|
||||
}
|
||||
if (mFormat == -1)
|
||||
{
|
||||
mFormat = 0;
|
||||
mFilterIndex = 0;
|
||||
}
|
||||
maskString.RemoveLast();
|
||||
|
||||
mFilename.SetPath(gPrefs->Read(wxT("/Export/Path"), ::wxGetCwd()));
|
||||
mFilename.SetName(mProject->GetName());
|
||||
while (true) {
|
||||
// Must reset each iteration
|
||||
mBook = NULL;
|
||||
|
||||
FileDialog fd(mProject,
|
||||
mFileDialogTitle,
|
||||
mFilename.GetPath(),
|
||||
mFilename.GetFullName(),
|
||||
maskString,
|
||||
wxFD_SAVE | wxRESIZE_BORDER);
|
||||
mDialog = &fd;
|
||||
mDialog->PushEventHandler(this);
|
||||
|
||||
fd.SetUserPaneCreator(CreateUserPaneCallback, (wxUIntPtr) this);
|
||||
fd.SetFilterIndex(mFilterIndex);
|
||||
|
||||
int result = fd.ShowModal();
|
||||
|
||||
mDialog->PopEventHandler();
|
||||
|
||||
if (result == wxID_CANCEL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mFilename = fd.GetPath();
|
||||
if (mFilename == wxT("")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mFormat = fd.GetFilterIndex();
|
||||
mFilterIndex = fd.GetFilterIndex();
|
||||
|
||||
int c = 0;
|
||||
for (size_t i = 0; i < mPlugins.GetCount(); i++)
|
||||
{
|
||||
for (int j = 0; j < mPlugins[i]->GetFormatCount(); j++)
|
||||
{
|
||||
if (mFilterIndex == c)
|
||||
{
|
||||
mFormat = i;
|
||||
mSubFormat = j;
|
||||
}
|
||||
c++;
|
||||
}
|
||||
}
|
||||
|
||||
wxString ext = mFilename.GetExt();
|
||||
wxString defext = mPlugins[mFormat]->GetExtension(mSubFormat).Lower();
|
||||
|
||||
//
|
||||
// Check the extension - add the default if it's not there,
|
||||
// and warn user if it's abnormal.
|
||||
//
|
||||
if (ext.IsEmpty()) {
|
||||
//
|
||||
// Make sure the user doesn't accidentally save the file
|
||||
// as an extension with no name, like just plain ".wav".
|
||||
//
|
||||
if (mFilename.GetName().Left(1) == wxT(".")) {
|
||||
wxString prompt = _("Are you sure you want to export the file as \"") +
|
||||
mFilename.GetFullName() +
|
||||
wxT("\"?\n");
|
||||
|
||||
int action = wxMessageBox(prompt,
|
||||
_("Warning"),
|
||||
wxYES_NO | wxICON_EXCLAMATION);
|
||||
if (action != wxYES) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
mFilename.SetExt(defext);
|
||||
}
|
||||
else if (!mPlugins[mFormat]->CheckFileName(mFilename, mSubFormat))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if (!ext.IsEmpty() && !mPlugins[mFormat]->IsExtension(ext, mSubFormat) && ext.CmpNoCase(defext)) {
|
||||
wxString prompt;
|
||||
prompt.Printf(_("You are about to export a %s file with the name \"%s\".\n\n\
|
||||
Normally these files end in \".%s\", and some programs\n\
|
||||
will not open files with nonstandard extensions.\n\n\
|
||||
Are you sure you want to export the file under this name?"),
|
||||
mPlugins[mFormat]->GetFormat(mSubFormat).c_str(),
|
||||
mFilename.GetFullName().c_str(),
|
||||
defext.c_str());
|
||||
|
||||
int action = wxMessageBox(prompt,
|
||||
_("Warning"),
|
||||
wxYES_NO | wxICON_EXCLAMATION);
|
||||
if (action != wxYES) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (mFilename.GetFullPath().Length() >= 256) {
|
||||
wxMessageBox(_("Sorry, pathnames longer than 256 characters not supported."));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check to see if we are writing to a path that a missing aliased file existed at.
|
||||
// This causes problems for the exporter, so we don't allow it.
|
||||
// Overwritting non-missing aliased files is okay.
|
||||
// Also, this can only happen for uncompressed audio.
|
||||
bool overwritingMissingAlias;
|
||||
overwritingMissingAlias = false;
|
||||
for (size_t i = 0; i < gAudacityProjects.GetCount(); i++) {
|
||||
AliasedFileArray aliasedFiles;
|
||||
FindDependencies(gAudacityProjects[i], &aliasedFiles);
|
||||
size_t j;
|
||||
for (j = 0; j< aliasedFiles.GetCount(); j++) {
|
||||
if (mFilename.GetFullPath() == aliasedFiles[j].mFileName.GetFullPath() &&
|
||||
!mFilename.FileExists()) {
|
||||
// Warn and return to the dialog
|
||||
wxMessageBox(_("You are attempting to overwrite an aliased file that is missing.\n\
|
||||
The file cannot be written because the path is needed to restore the original audio to the project.\n\
|
||||
Choose File > Check Dependencies to view the locations of all missing files.\n\
|
||||
If you still wish to export, please choose a different filename or folder."));
|
||||
overwritingMissingAlias = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (overwritingMissingAlias)
|
||||
continue;
|
||||
|
||||
if (mFilename.FileExists()) {
|
||||
wxString prompt;
|
||||
|
||||
prompt.Printf(_("A file named \"%s\" already exists. Replace?"),
|
||||
mFilename.GetFullPath().c_str());
|
||||
|
||||
int action = wxMessageBox(prompt,
|
||||
_("Warning"),
|
||||
wxYES_NO | wxICON_EXCLAMATION);
|
||||
if (action != wxYES) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Let user edit MetaData
|
||||
if (mPlugins[mFormat]->GetCanMetaData(mSubFormat)) {
|
||||
if (!(project->DoEditMetadata(_("Edit Metadata Tags for Export"),
|
||||
_("Exported Tags"), mProject->GetShowId3Dialog()))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// ExportMixerPanel
|
||||
//----------------------------------------------------------------------------
|
||||
|
@ -153,6 +153,21 @@ public:
|
||||
|
||||
const ExportPluginArray GetPlugins();
|
||||
|
||||
// Auto Export from Timer Recording
|
||||
bool ProcessFromTimerRecording(AudacityProject *project,
|
||||
bool selectedOnly,
|
||||
double t0,
|
||||
double t1,
|
||||
wxFileName fnFile,
|
||||
int iFormat,
|
||||
int iSubFormat,
|
||||
int iFilterIndex);
|
||||
bool SetAutoExportOptions(AudacityProject *project);
|
||||
int GetAutoExportFormat();
|
||||
int GetAutoExportSubFormat();
|
||||
int GetAutoExportFilterIndex();
|
||||
wxFileName GetAutoExportFileName();
|
||||
|
||||
private:
|
||||
|
||||
bool ExamineTracks();
|
||||
|
Loading…
x
Reference in New Issue
Block a user