23#include <wx/checkbox.h>
28#include <wx/listbase.h>
30#include <wx/filename.h>
33#include <wx/radiobut.h>
34#include <wx/simplebook.h>
36#include <wx/statbox.h>
37#include <wx/stattext.h>
38#include <wx/textctrl.h>
39#include <wx/textdlg.h>
48#include "../SelectionState.h"
49#include "../ShuttleGui.h"
50#include "../TagsEditor.h"
51#include "../WaveTrack.h"
52#include "../widgets/HelpSystem.h"
53#include "../widgets/AudacityMessageBox.h"
54#include "../widgets/AudacityTextEntryDialog.h"
55#include "../widgets/ProgressDialog.h"
133, mExporter{ *project }
141 for (
const auto &plugin : mExporter.GetPlugins())
142 mPlugins.push_back(plugin.get());
144 this->CountTracksAndLabels();
153 mInitialized =
false;
154 PopulateOrExchange(
S);
159 SetMinSize(GetSize());
188 XO(
"All audio is muted."),
189 XO(
"Cannot Export Multiple"),
199"You have no unmuted Audio Tracks and no applicable \
200\nlabels, so you cannot export to separate audio files."),
201 XO(
"Cannot Export Multiple"),
210 mLabel->Enable(bHasLabels && bHasTracks);
211 mTrack->Enable(bHasTracks);
217 mLabel->SetValue(bPreferByLabels);
218 mTrack->SetValue(!bPreferByLabels);
222 return wxDialogWrapper::ShowModal();
228 wxString defaultFormat =
gPrefs->Read(wxT(
"/Export/Format"), wxT(
"WAV"));
237 for (
const auto &pPlugin :
mPlugins)
240 for (
int j = 0; j < pPlugin->GetFormatCount(); j++)
243 visibleFormats.push_back(
format );
247 formats.push_back(
format.MSGID().GET() );
248 if (
mPlugins[i]->GetFormat(j) == defaultFormat) {
269 S.StartHorizontalLay(wxEXPAND,
true);
272 S.StartStatic(
XO(
"Export files to:"),
true);
274 S.StartMultiColumn(4,
true);
277 .AddTextBox(
XXO(
"Folder:"),
284 .TieChoice(
XXO(
"Format:"),
286 wxT(
"/Export/MultipleFormat"),
295 S.AddVariableText( {},
false);
296 S.AddVariableText( {},
false);
298 S.AddPrompt(
XXO(
"Options:"));
301 .Style(wxBORDER_STATIC)
305 for (
const auto &pPlugin :
mPlugins)
307 for (
int j = 0; j < pPlugin->GetFormatCount(); j++)
310 S.StartNotebookPage( {} );
311 pPlugin->OptionsCreate(
S, j);
318 S.AddVariableText( {},
false);
319 S.AddVariableText( {},
false);
325 S.EndHorizontalLay();
327 S.StartHorizontalLay(wxEXPAND,
false);
330 S.StartStatic(
XO(
"Split files based on:"), 1);
340 .AddRadioButton(
XXO(
"Tracks"));
345 .AddRadioButtonToGroup(
XXO(
"Labels"));
350 S.StartMultiColumn(2, wxEXPAND);
356 .AddCheckBox(
XXO(
"Include audio before first label"),
false);
359 S.AddVariableText( {},
false);
360 S.StartMultiColumn(2, wxEXPAND);
364 S.AddVariableText(
XO(
"First file name:"),
false);
367 .Name(
XO(
"First file name"))
381 S.StartStatic(
XO(
"Name files:"), 1);
389 S.StartRadioButtonGroup({
390 wxT(
"/Export/TrackNameWithOrWithoutNumbers"),
392 { wxT(
"labelTrack"),
XXO(
"Using Label/Track Name") },
393 { wxT(
"numberBefore"),
XXO(
"Numbering before Label/Track Name") },
394 { wxT(
"numberAfter"),
XXO(
"Numbering after File name prefix") },
405 S.EndRadioButtonGroup();
409 S.StartMultiColumn(3, wxEXPAND);
416 .Name(
XO(
"File name prefix"))
425 S.EndHorizontalLay();
428 S.StartHorizontalLay(wxEXPAND,
false);
431 {wxT(
"/Export/OverwriteExisting"),
434 S.EndHorizontalLay();
437 mExport = (wxButton *)wxWindow::FindWindowById(wxID_OK,
this);
452 enable =
mLabel->GetValue() &&
485 const int sel =
mFormat->GetSelection();
486 if (sel != wxNOT_FOUND)
490 for (
const auto &pPlugin :
mPlugins)
493 for (
int j = 0; j < pPlugin->GetFormatCount(); j++)
495 if ((
size_t)sel == c)
511 fn.AssignDir(
mDir->GetValue());
513 bool ok =
fn.Mkdir(0777, wxPATH_MKDIR_FULL);
521 XO(
"\"%s\" successfully created.").
Format(
fn.GetPath() ),
522 XO(
"Export Multiple"),
530 XO(
"Choose a location to save the exported files"),
533 if (!dlog.GetPath().empty())
534 mDir->SetValue(dlog.GetPath());
601 for (
const auto &pPlugin :
mPlugins)
604 for (
int j = 0; j < pPlugin->GetFormatCount(); j++, c++)
622 auto cleanup =
finally( [&]
625 ?
XO(
"Successfully exported the following %lld file(s).")
626 : ok == ProgressResult::Failed
627 ?
XO(
"Something went wrong after exporting the following %lld file(s).")
629 ?
XO(
"Export canceled after exporting the following %lld file(s).")
630 : ok == ProgressResult::Stopped
631 ?
XO(
"Export stopped after exporting the following %lld file(s).")
632 :
XO(
"Something went really wrong after exporting the following %lld file(s).")
636 for (
size_t i = 0; i <
mExported.size(); i++) {
647 XO(
"Export Multiple"),
674 fn.AssignDir(
mDir->GetValue());
676 if (
fn.DirExists()) {
680 auto prompt =
XO(
"\"%s\" doesn't exist.\n\nWould you like to create it?")
681 .Format(
fn.GetFullPath() );
686 wxYES_NO | wxICON_EXCLAMATION);
687 if (action != wxYES) {
691 return fn.Mkdir(0777, wxPATH_MKDIR_FULL);
721 float pan = tr->GetPan();
727 else if (pan == 1.0) {
741 if (numRight > 0 || numLeft > 0) {
751 const wxString &prefix,
bool addNumber)
756 std::vector<ExportKit> exportSettings;
757 exportSettings.reserve(numFiles);
760 if(
mFirst->GetValue() ) {
771 setting.destfile.SetPath(
mDir->GetValue());
774 wxLogDebug(wxT(
"File extension is %s"), setting.destfile.GetExt());
806 name =
_(
"untitled");
813 name.Printf(wxT(
"%s-%02d"), prefix, l+1);
814 }
else if( addNumber ) {
817 name.Prepend(wxString::Format(wxT(
"%02d-"), l+1));
822 if( setting.destfile.GetName().empty() )
831 wxASSERT(setting.destfile.IsOk());
839 setting.filetags.LoadDefaults();
840 if (exportSettings.size()) {
841 setting.filetags = exportSettings.back().filetags;
848 bool bShowTagsDialog =
settings.GetShowId3Dialog();
852 if( bShowTagsDialog ){
855 XO(
"Edit Metadata Tags"), bShowTagsDialog);
856 gPrefs->Read(wxT(
"/AudioFiles/ShowId3Dialog"), &bShowTagsDialog,
true);
857 settings.SetShowId3Dialog( bShowTagsDialog );
864 exportSettings.push_back(setting);
871 ExportKit activeSetting;
874 std::unique_ptr<ProgressDialog> pDialog;
875 for (count = 0; count < numFiles; count++) {
877 activeSetting = exportSettings[count];
879 if( activeSetting.destfile.GetName().empty() )
883 ok =
DoExport(pDialog, channels, activeSetting.destfile,
false,
884 activeSetting.t0, activeSetting.t1, activeSetting.filetags);
885 if (ok == ProgressResult::Stopped) {
888 XO(
"Continue to export remaining files?"),
890 wxYES_NO | wxNO_DEFAULT | wxICON_WARNING);
891 if (dlgMessage.ShowModal() != wxID_YES ) {
905 const wxString &prefix,
bool addNumber)
911 std::vector<ExportKit> exportSettings;
915 setting.destfile.SetPath(
mDir->GetValue());
924 tr->SetSelected(
false);
928 bool skipSilenceAtBeginning;
929 gPrefs->Read(wxT(
"/AudioFiles/SkipSilenceAtBeginning"), &skipSilenceAtBeginning,
false);
941 setting.channels = channels.size();
942 if (setting.channels == 1 &&
944 tr->GetPan() == 0.0))
945 setting.channels = 2;
948 title = tr->GetName();
956 wxString::Format(wxT(
"%02d-"), l+1));
960 name = (wxString::Format(wxT(
"%s-%02d"), prefix, l+1));
966 if (setting.destfile.GetName().empty())
975 wxASSERT(setting.destfile.IsOk());
983 setting.filetags.LoadDefaults();
984 if (exportSettings.size()) {
985 setting.filetags = exportSettings.back().filetags;
992 bool bShowTagsDialog =
settings.GetShowId3Dialog();
996 if( bShowTagsDialog ){
999 XO(
"Edit Metadata Tags"), bShowTagsDialog);
1000 gPrefs->Read(wxT(
"/AudioFiles/ShowId3Dialog"), &bShowTagsDialog,
true);
1001 settings.SetShowId3Dialog( bShowTagsDialog );
1007 exportSettings.push_back(setting);
1014 ExportKit activeSetting;
1015 std::unique_ptr<ProgressDialog> pDialog;
1020 wxLogDebug(
"Get setting %i", count );
1022 activeSetting = exportSettings[count];
1023 if( activeSetting.destfile.GetName().empty() ){
1031 for (
auto channel : range)
1032 channel->SetSelected(
true);
1036 activeSetting.channels, activeSetting.destfile,
true,
1037 activeSetting.t0, activeSetting.t1, activeSetting.filetags);
1038 if (ok == ProgressResult::Stopped) {
1041 XO(
"Continue to export remaining files?"),
1043 wxYES_NO | wxNO_DEFAULT | wxICON_WARNING);
1044 if (dlgMessage.ShowModal() != wxID_YES ) {
1062 const wxFileName &inName,
1070 wxLogDebug(wxT(
"Doing multiple Export: File name \"%s\""), (inName.GetFullName()));
1071 wxLogDebug(wxT(
"Channels: %i, Start: %lf, End: %lf "), channels, t0, t1);
1073 wxLogDebug(wxT(
"Selected Region Only"));
1075 wxLogDebug(wxT(
"Whole Project"));
1080 backup.Assign(
name);
1084 backup.SetName(
name.GetName() +
1085 wxString::Format(wxT(
"%d"), suffix));
1088 while (backup.FileExists());
1089 ::wxRenameFile(inName.GetFullPath(), backup.GetFullPath());
1094 wxString base(
name.GetName());
1095 while (
name.FileExists()) {
1096 name.SetName(wxString::Format(wxT(
"%s-%d"), base, i++));
1101 const wxString fullPath{
name.GetFullPath()};
1103 auto cleanup =
finally( [&] {
1105 success == ProgressResult::Stopped ||
1107 if (backup.IsOk()) {
1110 ::wxRemoveFile(backup.GetFullPath());
1113 ::wxRemoveFile(fullPath);
1114 ::wxRenameFile(backup.GetFullPath(), fullPath);
1120 ::wxRemoveFile(fullPath);
1125 success = mPlugins[mPluginIndex]->Export(mProject,
1137 mExported.push_back(fullPath);
1148 wxString newname = input;
1159 if( excluded.length() > 1 ){
1162"Label or track \"%s\" is not a legal file name.\nYou cannot use any of these characters:\n\n%s\n\nSuggested replacement:")
1163 .Format( input, excluded );
1167"Label or track \"%s\" is not a legal file name. You cannot use \"%s\".\n\nSuggested replacement:")
1168 .Format( input, excluded );
1175 dlg.SetTextValidator( wxFILTER_EXCLUDE_CHAR_LIST );
1176 wxTextValidator *tv = dlg.GetTextValidator();
1180 if( dlg.ShowModal() == wxID_CANCEL )
1182 return wxEmptyString;
1185 newname = dlg.GetValue();
1192 if (event.GetKeyCode() == WXK_RETURN)
R GuardedCall(const F1 &body, const F2 &handler=F2::Default(), F3 delayedHandler=DefaultDelayedHandlerAction) noexcept(noexcept(handler(std::declval< AudacityException * >())) &&noexcept(handler(nullptr)) &&noexcept(std::function< void(AudacityException *)>{std::move(delayedHandler)}))
Execute some code on any thread; catch any AudacityException; enqueue error report on the main thread...
int AudacityMessageBox(const TranslatableString &message, const TranslatableString &caption, long style, wxWindow *parent, int x, int y)
EVT_BUTTON(wxID_NO, DependencyDialog::OnNo) EVT_BUTTON(wxID_YES
const TranslatableString name
static unsigned GetNumExportChannels(const TrackList &tracks)
EVT_LIST_ITEM_ACTIVATED(wxID_ANY, SuccessDialog::OnItemActivated) ExportMultipleDialog
AUDACITY_DLL_API wxFrame & GetProjectFrame(AudacityProject &project)
Get the top-level window associated with the project (as a wxFrame only, when you do not need to use ...
accessors for certain important windows associated with each project
static Settings & settings()
TranslatableString Verbatim(wxString str)
Require calls to the one-argument constructor to go through this distinct global function name.
std::vector< TranslatableString > TranslatableStrings
Wrap wxMessageDialog so that caption IS translatable.
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
const wxString & GetProjectName() const
Wrap wxTextEntryDialog so that caption IS translatable.
Main class to control the export function.
Presents a dialog box allowing the user to export multiple files either by exporting each track as a ...
ProgressResult ExportMultipleByTrack(bool byName, const wxString &prefix, bool addNumber)
Export each track in the project to a separate file.
void OnCreate(wxCommandEvent &event)
wxString MakeFileName(const wxString &input)
Takes an arbitrary text string and converts it to a form that can be used as a file name,...
void OnFormat(wxCommandEvent &event)
void OnChoose(wxCommandEvent &event)
void OnByName(wxCommandEvent &event)
void OnByNumber(wxCommandEvent &event)
void CountTracksAndLabels()
std::vector< ExportPlugin * > mPlugins
void OnHelp(wxCommandEvent &event)
wxTextCtrl * mFirstFileName
void OnCancel(wxCommandEvent &event)
ProgressResult ExportMultipleByLabel(bool byName, const wxString &prefix, bool addNumber)
Export multiple labeled regions of the project to separate files.
const LabelTrack * mLabels
void OnTrack(wxCommandEvent &event)
wxStaticText * mFirstFileLabel
virtual ~ExportMultipleDialog()
ProgressResult DoExport(std::unique_ptr< ProgressDialog > &pDialog, unsigned channels, const wxFileName &name, bool selectedOnly, double t0, double t1, const Tags &tags)
SelectionState & mSelectionState
void OnFirstFileName(wxCommandEvent &event)
void OnLabel(wxCommandEvent &event)
wxRadioButton * mByNumberAndName
wxStaticText * mPrefixLabel
void OnFirst(wxCommandEvent &event)
void OnExport(wxCommandEvent &event)
wxRadioButton * mByNumber
void PopulateOrExchange(ShuttleGui &S)
AudacityProject * mProject
void OnPrefix(wxCommandEvent &event)
void OnOptions(wxCommandEvent &event)
virtual bool Flush(bool bCurrentOnly=false) wxOVERRIDE
static void ShowHelp(wxWindow *parent, const FilePath &localFileName, const URLString &remoteURL, bool bModal=false, bool alwaysDefaultBrowser=false)
static void ShowInfoDialog(wxWindow *parent, const TranslatableString &dlogTitle, const TranslatableString &shortMsg, const wxString &message, const int xSize, const int ySize)
Displays cuttable information in a text ctrl, with an OK button.
static bool SanitiseFilename(wxString &name, const wxString &sub)
Check a proposed file name string for illegal characters and remove them return true iff name is "vis...
static const wxArrayString & GetExcludedCharacters()
A LabelStruct holds information for ONE label in a LabelTrack.
SelectedRegion selectedRegion
A LabelTrack is a Track that holds labels (LabelStruct).
const LabelStruct * GetLabel(int index) const
void OnMouse(wxMouseEvent &event)
static ProjectSettings & Get(AudacityProject &project)
static ProjectWindow * Find(AudacityProject *pProject)
static SelectionState & Get(AudacityProject &project)
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
void OnKeyDown(wxListEvent &event)
void OnItemActivated(wxListEvent &event)
virtual double GetStartTime() const =0
virtual double GetEndTime() const =0
A flat linked list of tracks supporting Add, Remove, Clear, and Contains, serialization of the list o...
double GetEndTime() const
auto Leaders() -> TrackIterRange< TrackType >
auto Any() -> TrackIterRange< TrackType >
static TrackList & Get(AudacityProject &project)
auto Selected() -> TrackIterRange< TrackType >
static auto Channels(TrackType *pTrack) -> TrackIterRange< TrackType >
Holds a msgid for the translation catalog; may also bind format arguments.
A Track that contains audio waveform data.
A private class used to store the information needed to do an export.
wxFileNameWrapper destfile
Extend wxArrayString with move operations and construction and insertion fromstd::initializer_list.
FILES_API void UpdateDefaultPath(Operation op, const FilePath &path)
FILES_API void MakeNameUnique(FilePaths &otherNames, wxFileName &newName)
FILES_API FilePath FindDefaultPath(Operation op)
auto begin(const Ptr< Type, BaseDeleter > &p)
Enables range-for.