diff --git a/src/Menus.cpp b/src/Menus.cpp index eab20f4ac..91dbd807a 100644 --- a/src/Menus.cpp +++ b/src/Menus.cpp @@ -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; + } } } diff --git a/src/Project.cpp b/src/Project.cpp index c30b24f91..56bcd1ad4 100644 --- a/src/Project.cpp +++ b/src/Project.cpp @@ -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; +} diff --git a/src/Project.h b/src/Project.h index a27a09cc8..a6941362e 100644 --- a/src/Project.h +++ b/src/Project.h @@ -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; } diff --git a/src/ShuttleGui.cpp b/src/ShuttleGui.cpp index aaf12d59a..8cde5e2d3 100644 --- a/src/ShuttleGui.cpp +++ b/src/ShuttleGui.cpp @@ -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(); diff --git a/src/ShuttleGui.h b/src/ShuttleGui.h index cb6f240a3..f8ef12b67 100644 --- a/src/ShuttleGui.h +++ b/src/ShuttleGui.h @@ -70,7 +70,6 @@ class Shuttle; class WrappedType; - class AUDACITY_DLL_API ShuttleGuiBase /* not final */ { public: diff --git a/src/TimerRecordDialog.cpp b/src/TimerRecordDialog.cpp index 96790744f..2ea6fec9f 100644 --- a/src/TimerRecordDialog.cpp +++ b/src/TimerRecordDialog.cpp @@ -20,6 +20,7 @@ #include "Audacity.h" #include "TimerRecordDialog.h" +#include "FileNames.h" #include #include @@ -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 -// -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; +} diff --git a/src/TimerRecordDialog.h b/src/TimerRecordDialog.h index 71cb8506f..6dbd8f2c3 100644 --- a/src/TimerRecordDialog.h +++ b/src/TimerRecordDialog.h @@ -17,24 +17,46 @@ #define __AUDACITY_TIMERRECORD_DIALOG__ #include +#include #include #include #include +#include +#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(); }; diff --git a/src/export/Export.cpp b/src/export/Export.cpp index 7c4860de9..93858d47d 100644 --- a/src/export/Export.cpp +++ b/src/export/Export.cpp @@ -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 //---------------------------------------------------------------------------- diff --git a/src/export/Export.h b/src/export/Export.h index 9bd50b1be..7964ca8d9 100644 --- a/src/export/Export.h +++ b/src/export/Export.h @@ -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();