Audacity  3.0.3
BatchCommands.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3  Audacity: A Digital Audio Editor
4 
5  MacroCommands.cpp
6 
7  Dominic Mazzoni
8  James Crook
9 
10 ********************************************************************//*******************************************************************/
17 
18 #define wxLOG_COMPONENT "MacroCommands"
19 
20 
21 #include "BatchCommands.h"
22 
23 #include <wx/defs.h>
24 #include <wx/datetime.h>
25 #include <wx/dir.h>
26 #include <wx/log.h>
27 #include <wx/textfile.h>
28 #include <wx/time.h>
29 
30 #include "Project.h"
31 #include "ProjectAudioManager.h"
32 #include "ProjectHistory.h"
33 #include "ProjectSettings.h"
34 #include "ProjectWindow.h"
36 #include "effects/EffectManager.h"
37 #include "effects/EffectUI.h"
38 #include "FileNames.h"
39 #include "Menus.h"
40 #include "PluginManager.h"
41 #include "Prefs.h"
42 #include "SelectFile.h"
43 #include "SelectUtilities.h"
44 #include "Shuttle.h"
45 #include "Track.h"
46 #include "UndoManager.h"
47 
48 #include "AllThemeResources.h"
49 
51 
53 
55 : mProject{ project }
56 , mExporter{ project }
57 {
58  ResetMacro();
59 
60  auto names = GetNames();
61  auto defaults = GetNamesOfDefaultMacros();
62 
63  for( size_t i = 0;i<defaults.size();i++){
64  wxString name = defaults[i];
65  if ( ! make_iterator_range( names ).contains(name) ) {
66  AddMacro(name);
67  RestoreMacro(name);
68  WriteMacro(name);
69  }
70  }
71 }
72 
73 static const auto MP3Conversion = XO("MP3 Conversion");
74 static const auto FadeEnds = XO("Fade Ends");
75 
77 {
78  return {
79  MP3Conversion.Translation() ,
80  FadeEnds.Translation() ,
81  };
82 }
83 
84 void MacroCommands::RestoreMacro(const wxString & name)
85 {
86 // TIDY-ME: Effects change their name with localisation.
87 // Commands (at least currently) don't. Messy.
88  ResetMacro();
89  if (name == MP3Conversion.Translation() ){
90  AddToMacro( wxT("Normalize") );
91  AddToMacro( wxT("ExportMP3") );
92  } else if (name == FadeEnds.Translation() ){
93  AddToMacro( wxT("Select"), wxT("Start=\"0\" End=\"1\"") );
94  AddToMacro( wxT("FadeIn") );
95  AddToMacro( wxT("Select"), wxT("Start=\"0\" End=\"1\" RelativeTo=\"ProjectEnd\"") );
96  AddToMacro( wxT("FadeOut") );
97  AddToMacro( wxT("Select"), wxT("Start=\"0\" End=\"0\"") );
98  }
99 }
100 
102 {
103  if (index < 0 || index >= (int)mCommandMacro.size()) {
104  return wxT("");
105  }
106 
107  return mCommandMacro[index];
108 }
109 
110 wxString MacroCommands::GetParams(int index)
111 {
112  if (index < 0 || index >= (int)mParamsMacro.size()) {
113  return wxT("");
114  }
115 
116  return mParamsMacro[index];
117 }
118 
120 {
121  return (int)mCommandMacro.size();
122 }
123 
124 wxString MacroCommands::ReadMacro(const wxString & macro, wxWindow *parent)
125 {
126  // Clear any previous macro
127  ResetMacro();
128 
129  // Build the filename
130  wxFileName name(FileNames::MacroDir(), macro, wxT("txt"));
131 
132  // But, ask the user for the real name if we're importing
133  if (parent) {
134  FilePath fn = SelectFile(FileNames::Operation::_None,
135  XO("Import Macro"),
136  wxEmptyString,
137  name.GetName(),
138  wxT("txt"),
140  wxFD_OPEN | wxRESIZE_BORDER,
141  parent);
142 
143  // User canceled...
144  if (fn.empty()) {
145  return wxEmptyString;
146  }
147 
148  wxFileName check(fn);
149  check.SetPath(name.GetPath());
150  if (check.FileExists())
151  {
152  int id = AudacityMessageBox(
153  XO("Macro %s already exists. Would you like to replace it?").Format(check.GetName()),
154  XO("Import Macro"),
155  wxYES_NO);
156  if (id == wxNO) {
157  return wxEmptyString;
158  }
159  }
160 
161  name.Assign(fn);
162  }
163 
164  // Set the file name
165  wxTextFile tf(name.GetFullPath());
166 
167  // Open and check
168  tf.Open();
169  if (!tf.IsOpened()) {
170  // wxTextFile will display any errors
171  return wxEmptyString;
172  }
173 
174  // Load commands from the file
175  int lines = tf.GetLineCount();
176  if (lines > 0) {
177  for (int i = 0; i < lines; i++) {
178 
179  // Find the command name terminator...ignore line if not found
180  int splitAt = tf[i].Find(wxT(':'));
181  if (splitAt < 0) {
182  continue;
183  }
184 
185  // Parse and clean
186  wxString cmd = tf[i].Left(splitAt).Strip(wxString::both);
187  wxString parm = tf[i].Mid(splitAt + 1).Strip(wxString::trailing);
188 
189  // Add to lists
190  mCommandMacro.push_back(cmd);
191  mParamsMacro.push_back(parm);
192  }
193  }
194 
195  // Done with the file
196  tf.Close();
197 
198  // Write to macro directory if importing
199  if (parent) {
200  return WriteMacro(name.GetName());
201  }
202 
203  return name.GetName();
204 }
205 
206 wxString MacroCommands::WriteMacro(const wxString & macro, wxWindow *parent)
207 {
208  // Build the default filename
209  wxFileName name(FileNames::MacroDir(), macro, wxT("txt"));
210 
211  // But, ask the user for the real name if we're exporting
212  if (parent) {
213  FilePath fn = SelectFile(FileNames::Operation::_None,
214  XO("Export Macro"),
215  wxEmptyString,
216  name.GetName(),
217  wxT("txt"),
219  wxFD_SAVE | wxFD_OVERWRITE_PROMPT | wxRESIZE_BORDER,
220  parent);
221 
222  // User canceled...
223  if (fn.empty()) {
224  return wxEmptyString;
225  }
226 
227  name.Assign(fn);
228  }
229 
230  // Set the file name
231  wxTextFile tf(name.GetFullPath());
232 
233  // Create the file (Create() doesn't leave the file open)
234  if (!tf.Exists()) {
235  tf.Create();
236  }
237 
238  // Open it
239  tf.Open();
240 
241  if (!tf.IsOpened()) {
242  // wxTextFile will display any errors
243  return wxEmptyString;
244  }
245 
246  // Start with a clean slate
247  tf.Clear();
248 
249  // Copy over the commands
250  int lines = mCommandMacro.size();
251  for (int i = 0; i < lines; i++) {
252  // using GET to serialize macro definition to a text file
253  tf.AddLine(mCommandMacro[i].GET() + wxT(":") + mParamsMacro[ i ]);
254  }
255 
256  // Write the macro
257  tf.Write();
258 
259  // Done with the file
260  tf.Close();
261 
262  return name.GetName();
263 }
264 
265 bool MacroCommands::AddMacro(const wxString & macro)
266 {
267  // Build the filename
268  wxFileName name(FileNames::MacroDir(), macro, wxT("txt"));
269 
270  // Set the file name
271  wxTextFile tf(name.GetFullPath());
272 
273  // Create it..Create will display errors
274  return tf.Create();
275 }
276 
277 bool MacroCommands::DeleteMacro(const wxString & macro)
278 {
279  // Build the filename
280  wxFileName name(FileNames::MacroDir(), macro, wxT("txt"));
281 
282  // Delete it...wxRemoveFile will display errors
283  auto result = wxRemoveFile(name.GetFullPath());
284 
285  // Delete any legacy chain that it shadowed
286  auto oldPath = wxFileName{ FileNames::LegacyChainDir(), macro, wxT("txt") };
287  wxRemoveFile(oldPath.GetFullPath()); // Don't care about this return value
288 
289  return result;
290 }
291 
292 bool MacroCommands::RenameMacro(const wxString & oldmacro, const wxString & newmacro)
293 {
294  // Build the filenames
295  wxFileName oname(FileNames::MacroDir(), oldmacro, wxT("txt"));
296  wxFileName nname(FileNames::MacroDir(), newmacro, wxT("txt"));
297 
298  // Rename it...wxRenameFile will display errors
299  return wxRenameFile(oname.GetFullPath(), nname.GetFullPath());
300 }
301 
302 // Gets all commands that are valid for this mode.
304 {
305  if (!project)
306  return;
307 
308  Entries commands;
309 
312  {
313  for (auto &plug
315  auto command = em.GetCommandIdentifier(plug.GetID());
316  if (!command.empty())
317  commands.push_back( {
318  { command, plug.GetSymbol().Msgid() },
319  plug.GetPluginType() == PluginTypeEffect ?
320  XO("Effect") : XO("Menu Command (With Parameters)")
321  } );
322  }
323  }
324 
325  auto &manager = CommandManager::Get( *project );
326  TranslatableStrings mLabels;
327  CommandIDs mNames;
328  std::vector<bool> vExcludeFromMacros;
329  mLabels.clear();
330  mNames.clear();
331  manager.GetAllCommandLabels(mLabels, vExcludeFromMacros, true);
332  manager.GetAllCommandNames(mNames, true);
333 
334  const bool english = wxGetLocale()->GetCanonicalName().StartsWith(wxT("en"));
335 
336  for(size_t i=0; i<mNames.size(); i++) {
337  if( !vExcludeFromMacros[i] ){
338  auto label = mLabels[i];
339  label.Strip();
340  bool suffix;
341  if (!english)
342  suffix = false;
343  else {
344  // We'll disambiguate if the squashed name is short and shorter than the internal name.
345  // Otherwise not.
346  // This means we won't have repetitive items like "Cut (Cut)"
347  // But we will show important disambiguation like "All (SelectAll)" and "By Date (SortByDate)"
348  // Disambiguation is no longer essential as the details box will show it.
349  // PRL: I think this reasoning applies only when locale is English.
350  // For other locales, show the (CamelCaseCodeName) always. Or, never?
351  wxString squashed = label.Translation();
352  squashed.Replace( " ", "" );
353 
354  // uh oh, using GET for dubious comparison of (lengths of)
355  // user-visible name and internal CommandID!
356  // and doing this only for English locale!
357  suffix = squashed.length() < wxMin( 18, mNames[i].GET().length());
358  }
359 
360  if( suffix )
361  // uh oh, using GET to expose CommandID to the user, as a
362  // disambiguating suffix on a name, but this is only ever done if
363  // the locale is English!
364  // PRL: In case this logic does get fixed for other locales,
365  // localize even this punctuation format. I'm told Chinese actually
366  // prefers slightly different parenthesis characters
367  label.Join( XO("(%s)").Format( mNames[i].GET() ), wxT(" ") );
368 
369  // Bug 2294. The Close command pulls the rug out from under
370  // batch processing, because it destroys the project.
371  // So it is UNSAFE for scripting, and therefore excluded from
372  // the catalog.
373  if (mNames[i] == "Close")
374  continue;
375 
376  commands.push_back(
377  {
378  {
379  mNames[i], // Internal name.
380  label // User readable name
381  },
382  XO("Menu Command (No Parameters)")
383  }
384  );
385  }
386  }
387 
388  // Sort commands by their user-visible names.
389  // PRL: What exactly should happen if first members of pairs are not unique?
390  // I'm not sure, but at least I can sort stably for a better defined result.
391  auto less =
392  [](const Entry &a, const Entry &b)
393  { return a.name.StrippedTranslation() <
394  b.name.StrippedTranslation(); };
395  std::stable_sort(commands.begin(), commands.end(), less);
396 
397  // Now uniquify by friendly name
398  auto equal =
399  [](const Entry &a, const Entry &b)
400  { return a.name.StrippedTranslation() ==
401  b.name.StrippedTranslation(); };
402  std::unique_copy(
403  commands.begin(), commands.end(), std::back_inserter(mCommands), equal);
404 }
405 
406 // binary search
408  -> Entries::const_iterator
409 {
410  const auto less = [](const Entry &entryA, const Entry &entryB)
411  { return entryA.name.StrippedTranslation() <
412  entryB.name.StrippedTranslation(); };
413  auto range = std::equal_range(
414  begin(), end(), Entry{ { {}, friendlyName }, {} }, less
415  );
416  if (range.first != range.second) {
417  wxASSERT_MSG( range.first + 1 == range.second,
418  "Non-unique user-visible command name" );
419  return range.first;
420  }
421  else
422  return end();
423 }
424 
425 // linear search
426 auto MacroCommandsCatalog::ByCommandId( const CommandID &commandId ) const
427  -> Entries::const_iterator
428 {
429  // Maybe this too should have a uniqueness check?
430  return std::find_if( begin(), end(),
431  [&](const Entry &entry)
432  { return entry.name.Internal() == commandId; });
433 }
434 
436 {
437  const PluginID & ID =
439  if (ID.empty())
440  {
441  return wxEmptyString; // effect not found.
442  }
443 
445 }
446 
448  const CommandID & command, const wxString & params, wxWindow &parent)
449 {
450  const PluginID & ID =
452  if (ID.empty())
453  {
454  return wxEmptyString; // effect not found
455  }
456 
457  wxString res = params;
458 
459  auto cleanup = EffectManager::Get().SetBatchProcessing(ID);
460 
461  if (EffectManager::Get().SetEffectParameters(ID, params))
462  {
463  if (EffectManager::Get().PromptUser(ID, EffectUI::DialogFactory, parent))
464  {
466  }
467  }
468 
469  return res;
470 }
471 
472 wxString MacroCommands::PromptForPresetFor(const CommandID & command, const wxString & params, wxWindow *parent)
473 {
474  const PluginID & ID =
476  if (ID.empty())
477  {
478  return wxEmptyString; // effect not found.
479  }
480 
481  wxString preset = EffectManager::Get().GetPreset(ID, params, parent);
482 
483  // Preset will be empty if the user cancelled the dialog, so return the original
484  // parameter value.
485  if (preset.empty())
486  {
487  return params;
488  }
489 
490  return preset;
491 }
492 
498  const PluginID & ID, const CommandContext & context, unsigned flags )
499 {
500  auto &project = context.project;
501  auto &window = ProjectWindow::Get( project );
502  const PluginDescriptor *plug = PluginManager::Get().GetPlugin(ID);
503  if (!plug)
504  return false;
505 
506  if (flags & EffectManager::kConfigured)
507  {
508  ProjectAudioManager::Get( project ).Stop();
509 // SelectAllIfNone();
510  }
511 
513  bool success = em.DoAudacityCommand(ID,
514  context,
515  &window,
516  (flags & EffectManager::kConfigured) == 0);
517 
518  if (!success)
519  return false;
520 
521 /*
522  if (em.GetSkipStateFlag())
523  flags = flags | OnEffectFlags::kSkipState;
524 
525  if (!(flags & OnEffectFlags::kSkipState))
526  {
527  wxString shortDesc = em.GetCommandName(ID);
528  wxString longDesc = em.GetCommandDescription(ID);
529  PushState(longDesc, shortDesc);
530  }
531 */
532  window.RedrawProject();
533  return true;
534 }
535 
537  const PluginID & ID, const TranslatableString &friendlyCommand,
538  const CommandID & command, const wxString & params,
539  const CommandContext & Context)
540 {
541  static_cast<void>(command);//compiler food.
542 
543  //Possibly end processing here, if in batch-debug
544  if( ReportAndSkip(friendlyCommand, params))
545  return true;
546 
547  const PluginDescriptor *plug = PluginManager::Get().GetPlugin(ID);
548  if (!plug)
549  return false;
550 
551  AudacityProject *project = &mProject;
552 
553  // IF nothing selected, THEN select everything depending
554  // on preferences setting.
555  // (most effects require that you have something selected).
557  {
559  {
561  // i18n-hint: %s will be replaced by the name of an action, such as "Remove Tracks".
562  XO("\"%s\" requires one or more tracks to be selected.").Format(friendlyCommand));
563  return false;
564  }
565  }
566 
567  bool res = false;
568 
569  auto cleanup = EffectManager::Get().SetBatchProcessing(ID);
570 
571  // transfer the parameters to the effect...
572  if (EffectManager::Get().SetEffectParameters(ID, params))
573  {
575  // and apply the effect...
576  res = DoAudacityCommand(ID,
577  Context,
581  else
582  // and apply the effect...
583  res = EffectUI::DoEffect(ID,
584  Context,
588  }
589 
590  return res;
591 }
592 
594  const CommandID & Str,
595  const CommandContext & context, CommandFlag flags, bool alwaysEnabled)
596 {
597  switch ( commandManager.HandleTextualCommand(
598  Str, context, flags, alwaysEnabled) ) {
600  return true;
602  return false;
604  default:
605  break;
606  }
607 
608  // Not one of the singleton commands.
609  // We could/should try all the list-style commands.
610  // instead we only try the effects.
612  for (auto &plug : PluginManager::Get().PluginsOfType(PluginTypeEffect))
613  if (em.GetCommandIdentifier(plug.GetID()) == Str)
614  return EffectUI::DoEffect(
615  plug.GetID(), context,
617 
618  return false;
619 }
620 
621 bool MacroCommands::ApplyCommand( const TranslatableString &friendlyCommand,
622  const CommandID & command, const wxString & params,
623  CommandContext const * pContext)
624 {
625  // Test for an effect.
626  const PluginID & ID =
628  if (!ID.empty())
629  {
630  if( pContext )
631  return ApplyEffectCommand(
632  ID, friendlyCommand, command, params, *pContext);
633  const CommandContext context( mProject );
634  return ApplyEffectCommand(
635  ID, friendlyCommand, command, params, context);
636  }
637 
638  AudacityProject *project = &mProject;
639  auto &manager = CommandManager::Get( *project );
640  if( pContext ){
642  manager, command, *pContext, AlwaysEnabledFlag, true ) )
643  return true;
644  pContext->Status( wxString::Format(
645  _("Your batch command of %s was not recognized."), friendlyCommand.Translation() ));
646  return false;
647  }
648  else
649  {
650  const CommandContext context( mProject );
652  manager, command, context, AlwaysEnabledFlag, true ) )
653  return true;
654  }
655 
657  XO("Your batch command of %s was not recognized.")
658  .Format( friendlyCommand ) );
659 
660  return false;
661 }
662 
664  const TranslatableString &friendlyCommand,
665  const CommandID & command, const wxString &params,
666  CommandContext const * pContext)
667 {
668  AudacityProject *project = &mProject;
669  auto &settings = ProjectSettings::Get( *project );
670  // Recalc flags and enable items that may have become enabled.
671  MenuManager::Get(*project).UpdateMenus(false);
672  // enter batch mode...
673  bool prevShowMode = settings.GetShowId3Dialog();
674  project->mBatchMode++;
675  auto cleanup = finally( [&] {
676  // exit batch mode...
677  settings.SetShowId3Dialog(prevShowMode);
678  project->mBatchMode--;
679  } );
680 
681  return ApplyCommand( friendlyCommand, command, params, pContext );
682 }
683 
684 static int MacroReentryCount = 0;
685 // ApplyMacro returns true on success, false otherwise.
686 // Any error reporting to the user in setting up the macro
687 // has already been done.
689  const MacroCommandsCatalog &catalog, const wxString & filename)
690 {
691  // Check for reentrant ApplyMacro commands.
692  // We'll allow 1 level of reentry, but not more.
693  // And we treat ignoring deeper levels as a success.
694  if (MacroReentryCount > 1) {
695  return true;
696  }
697 
698  // Restore the reentry counter (to zero) when we exit.
699  auto cleanup1 = valueRestorer(MacroReentryCount);
701 
702  AudacityProject *proj = &mProject;
703  bool res = false;
704 
705  // Only perform this group on initial entry. They should not be done
706  // while recursing.
707  if (MacroReentryCount == 1) {
708  mFileName = filename;
709 
710  TranslatableString longDesc, shortDesc;
711  wxString name = gPrefs->Read(wxT("/Batch/ActiveMacro"), wxEmptyString);
712  if (name.empty()) {
713  /* i18n-hint: active verb in past tense */
714  longDesc = XO("Applied Macro");
715  shortDesc = XO("Apply Macro");
716  }
717  else {
718  /* i18n-hint: active verb in past tense */
719  longDesc = XO("Applied Macro '%s'").Format(name);
720  shortDesc = XO("Apply '%s'").Format(name);
721  }
722 
723  // Save the project state before making any changes. It will be rolled
724  // back if an error occurs.
725  // It also causes any calls to ModifyState (such as by simple
726  // view-changing commands) to append changes to this state, not to the
727  // previous state in history. See Bug 2076
728  if (proj) {
729  ProjectHistory::Get(*proj).PushState(longDesc, shortDesc);
730  }
731  }
732 
733  // Upon exit of the top level apply, roll back the state if an error occurs.
734  auto cleanup2 = finally([&, macroReentryCount = MacroReentryCount] {
735  if (macroReentryCount == 1 && !res && proj) {
736  // Be sure that exceptions do not escape this destructor
737  GuardedCall([&]{
738  // Macro failed or was cancelled; revert to the previous state
739  auto &history = ProjectHistory::Get(*proj);
740  history.RollbackState();
741  // The added undo state is now vacuous. Remove it (Bug 2759)
742  auto &undoManager = UndoManager::Get(*proj);
743  undoManager.Undo(
744  [&]( const UndoStackElem &elem ){
745  history.PopState( elem.state ); } );
746  undoManager.AbandonRedo();
747  });
748  }
749  });
750 
751  mAbort = false;
752 
753  // Is tracing enabled?
754  bool trace;
755  gPrefs->Read(wxT("/EnableMacroTracing"), &trace, false);
756 
757  // If so, then block most other messages while running the macro
758  wxLogLevel prevLevel = wxLog::GetComponentLevel("");
759  if (trace) {
760  wxLog::SetComponentLevel("", wxLOG_FatalError);
761  wxLog::SetComponentLevel(wxLOG_COMPONENT, wxLOG_Info);
762  }
763 
764  size_t i = 0;
765  for (; i < mCommandMacro.size(); i++) {
766  const auto &command = mCommandMacro[i];
767  auto iter = catalog.ByCommandId(command);
768  const auto friendly = (iter == catalog.end())
769  ?
770  // uh oh, using GET to expose an internal name to the user!
771  // in default of any better friendly name
772  Verbatim( command.GET() )
773  : iter->name.Msgid().Stripped();
774 
775  wxTimeSpan before;
776  if (trace) {
777  before = wxTimeSpan(0, 0, 0, wxGetUTCTimeMillis());
778  }
779 
780  bool success = ApplyCommandInBatchMode(friendly, command, mParamsMacro[i]);
781 
782  if (trace) {
783  auto after = wxTimeSpan(0, 0, 0, wxGetUTCTimeMillis());
784  wxLogMessage(wxT("Macro line #%ld took %s : %s:%s"),
785  i + 1,
786  (after - before).Format(wxT("%H:%M:%S.%l")),
787  command.GET(),
788  mParamsMacro[i]);
789  }
790 
791  if (!success || mAbort)
792  break;
793  }
794 
795  // Restore message level
796  if (trace) {
797  wxLog::SetComponentLevel("", prevLevel);
798  }
799 
800  res = (i == mCommandMacro.size());
801  if (!res)
802  return false;
803 
804  if (MacroReentryCount == 1) {
805  mFileName.Empty();
806 
807  if (proj)
808  ProjectHistory::Get(*proj).ModifyState(true);
809  }
810 
811  return true;
812 }
813 
814 // AbortBatch() allows a premature terminatation of a batch.
816 {
817  mAbort = true;
818 }
819 
820 void MacroCommands::AddToMacro(const CommandID &command, int before)
821 {
822  AddToMacro(command, GetCurrentParamsFor(command), before);
823 }
824 
825 void MacroCommands::AddToMacro(const CommandID &command, const wxString &params, int before)
826 {
827  if (before == -1) {
828  before = (int)mCommandMacro.size();
829  }
830 
831  mCommandMacro.insert(mCommandMacro.begin() + before, command);
832  mParamsMacro.insert(mParamsMacro.begin() + before, params);
833 }
834 
836 {
837  if (index < 0 || index >= (int)mCommandMacro.size()) {
838  return;
839  }
840 
841  mCommandMacro.erase( mCommandMacro.begin() + index );
842  mParamsMacro.erase( mParamsMacro.begin() + index );
843 }
844 
846 {
847  mCommandMacro.clear();
848  mParamsMacro.clear();
849 }
850 
851 // ReportAndSkip() is a diagnostic function that avoids actually
852 // applying the requested effect if in batch-debug mode.
854  const TranslatableString & friendlyCommand, const wxString & params)
855 {
856  int bDebug;
857  gPrefs->Read(wxT("/Batch/Debug"), &bDebug, false);
858  if( bDebug == 0 )
859  return false;
860 
861  //TODO: Add a cancel button to these, and add the logic so that we can abort.
862  if( !params.empty() )
863  {
865  XO("Apply %s with parameter(s)\n\n%s")
866  .Format( friendlyCommand, params ),
867  XO("Test Mode"));
868  }
869  else
870  {
872  XO("Apply %s").Format( friendlyCommand ),
873  XO("Test Mode"));
874  }
875  return true;
876 }
877 
879 {
880  static bool done = false;
881  if (!done) {
882  // Check once per session at most
883 
884  // Copy chain files from the old Chains into the new Macros directory,
885  // but only if like-named files are not already present in Macros.
886 
887  // Leave the old copies in place, in case a user wants to go back to
888  // an old Audacity version. They will have their old chains intact, but
889  // won't have any edits they made to the copy that now lives in Macros
890  // which old Audacity will not read.
891 
892  const auto oldDir = FileNames::LegacyChainDir();
893  FilePaths files;
894  wxDir::GetAllFiles(oldDir, &files, wxT("*.txt"), wxDIR_FILES);
895 
896  // add a dummy path component to be overwritten by SetFullName
897  wxFileName newDir{ FileNames::MacroDir(), wxT("x") };
898 
899  for (const auto &file : files) {
900  auto name = wxFileName{file}.GetFullName();
901  newDir.SetFullName(name);
902  const auto newPath = newDir.GetFullPath();
903  if (!wxFileExists(newPath))
904  FileNames::DoCopyFile(file, newPath);
905  }
906  done = true;
907  }
908  // To do: use std::once
909 }
910 
911 wxArrayString MacroCommands::GetNames()
912 {
914 
915  wxArrayString names;
916  FilePaths files;
917  wxDir::GetAllFiles(FileNames::MacroDir(), &files, wxT("*.txt"), wxDIR_FILES);
918  size_t i;
919 
920  wxFileName ff;
921  for (i = 0; i < files.size(); i++) {
922  ff = (files[i]);
923  names.push_back(ff.GetName());
924  }
925 
926  std::sort( names.begin(), names.end() );
927 
928  return names;
929 }
930 
931 bool MacroCommands::IsFixed(const wxString & name)
932 {
933  auto defaults = GetNamesOfDefaultMacros();
934  if( make_iterator_range( defaults ).contains( name ) )
935  return true;
936  return false;
937 }
938 
939 void MacroCommands::Split(const wxString & str, wxString & command, wxString & param)
940 {
941  int splitAt;
942 
943  command.Empty();
944  param.Empty();
945 
946  if (str.empty()) {
947  return;
948  }
949 
950  splitAt = str.Find(wxT(':'));
951  if (splitAt < 0) {
952  return;
953  }
954 
955  command = str.Mid(0, splitAt);
956  param = str.Mid(splitAt + 1);
957 
958  return;
959 }
960 
961 wxString MacroCommands::Join(const wxString & command, const wxString & param)
962 {
963  return command + wxT(": ") + param;
964 }
MacroCommands::Split
void Split(const wxString &str, wxString &command, wxString &param)
Definition: BatchCommands.cpp:939
ProjectHistory::ModifyState
void ModifyState(bool bWantsAutoSave)
Definition: ProjectHistory.cpp:124
TranslatableString
Holds a msgid for the translation catalog; may also bind format arguments.
Definition: TranslatableString.h:32
CommandManager::CommandFailure
@ CommandFailure
Definition: CommandManager.h:222
MacroCommandsCatalog::Entry
Definition: BatchCommands.h:31
MacroCommands::RestoreMacro
void RestoreMacro(const wxString &name)
Definition: BatchCommands.cpp:84
TranslatableString::empty
bool empty() const
Definition: TranslatableString.h:72
valueRestorer
ValueRestorer< T > valueRestorer(T &var)
inline functions provide convenient parameter type deduction
Definition: MemoryX.h:354
PluginTypeEffect
@ PluginTypeEffect
Definition: PluginManager.h:35
GuardedCall
R GuardedCall(const F1 &body, const F2 &handler=F2::Default(), std::function< void(AudacityException *)> delayedHandler=DefaultDelayedHandlerAction{})
Execute some code on any thread; catch any AudacityException; enqueue error report on the main thread...
Definition: AudacityException.h:202
EffectManager::GetEffectParameters
wxString GetEffectParameters(const PluginID &ID)
Definition: EffectManager.cpp:235
make_iterator_range
IteratorRange< Iterator > make_iterator_range(const Iterator &i1, const Iterator &i2)
Definition: MemoryX.h:549
ProjectAudioManager::Get
static ProjectAudioManager & Get(AudacityProject &project)
Definition: ProjectAudioManager.cpp:53
AudacityMessageBox
int AudacityMessageBox(const TranslatableString &message, const TranslatableString &caption, long style, wxWindow *parent, int x, int y)
Definition: AudacityMessageBox.cpp:17
fn
static const auto fn
Definition: WaveformView.cpp:1113
ComponentInterfaceSymbol::StrippedTranslation
const wxString StrippedTranslation() const
Definition: ComponentInterfaceSymbol.h:59
gPrefs
FileConfig * gPrefs
Definition: Prefs.cpp:70
AllThemeResources.h
TranslatableStrings
std::vector< TranslatableString > TranslatableStrings
Definition: TranslatableString.h:295
EffectManager::SetBatchProcessing
void SetBatchProcessing(const PluginID &ID, bool start)
Definition: EffectManager.cpp:690
Project.h
CommandManager::CommandNotFound
@ CommandNotFound
Definition: CommandManager.h:224
SelectFile
FilePath SelectFile(FileNames::Operation op, const TranslatableString &message, const FilePath &default_path, const FilePath &default_filename, const FileExtension &default_extension, const FileTypes &fileTypes, int flags, wxWindow *parent)
Definition: SelectFile.cpp:17
PluginDescriptor::GetPluginType
PluginType GetPluginType() const
Definition: PluginManager.cpp:95
MacroCommands::GetCurrentParamsFor
static wxString GetCurrentParamsFor(const CommandID &command)
Definition: BatchCommands.cpp:435
MacroCommands::PromptForPresetFor
static wxString PromptForPresetFor(const CommandID &command, const wxString &params, wxWindow *parent)
Definition: BatchCommands.cpp:472
MacroCommandsCatalog::end
Entries::const_iterator end() const
Definition: BatchCommands.h:48
MacroCommandsCatalog::mCommands
Entries mCommands
Definition: BatchCommands.h:52
Format
Abstract base class used in importing a file.
MacroCommands::ReportAndSkip
bool ReportAndSkip(const TranslatableString &friendlyCommand, const wxString &params)
Definition: BatchCommands.cpp:853
entry
static ProjectFileIORegistry::WriterEntry entry
Definition: ProjectSettings.cpp:193
MacroCommands::ReadMacro
wxString ReadMacro(const wxString &macro, wxWindow *parent=nullptr)
Definition: BatchCommands.cpp:124
EffectManager::kConfigured
@ kConfigured
Definition: EffectManager.h:53
EffectManager::kDontRepeatLast
@ kDontRepeatLast
Definition: EffectManager.h:57
CommandManager.h
MacroCommands::ApplyEffectCommand
bool ApplyEffectCommand(const PluginID &ID, const TranslatableString &friendlyCommand, const CommandID &command, const wxString &params, const CommandContext &Context)
Definition: BatchCommands.cpp:536
MacroCommands::ApplyMacro
bool ApplyMacro(const MacroCommandsCatalog &catalog, const wxString &filename={})
Definition: BatchCommands.cpp:688
MacroCommands::mCommandMacro
CommandIDs mCommandMacro
Definition: BatchCommands.h:120
XO
#define XO(s)
Definition: Internat.h:31
CommandManager::CommandSuccess
@ CommandSuccess
Definition: CommandManager.h:223
MacroCommandsCatalog::ByCommandId
Entries::const_iterator ByCommandId(const CommandID &commandId) const
Definition: BatchCommands.cpp:426
ProjectSettings::Get
static ProjectSettings & Get(AudacityProject &project)
Definition: ProjectSettings.cpp:41
CommandManager::HandleTextualCommand
TextualCommandResult HandleTextualCommand(const CommandID &Str, const CommandContext &context, CommandFlag flags, bool alwaysEnabled)
Definition: CommandManager.cpp:1343
MacroCommands::RenameMacro
bool RenameMacro(const wxString &oldmacro, const wxString &newmacro)
Definition: BatchCommands.cpp:292
MacroCommands::Join
wxString Join(const wxString &command, const wxString &param)
Definition: BatchCommands.cpp:961
BatchCommands.h
ProjectSettings.h
ProjectWindow::Get
static ProjectWindow & Get(AudacityProject &project)
Definition: ProjectWindow.cpp:535
AlwaysEnabledFlag
constexpr CommandFlag AlwaysEnabledFlag
Definition: CommandFlag.h:35
MacroCommands::mFileName
wxString mFileName
Definition: BatchCommands.h:126
MacroCommands::MacroCommands
MacroCommands(AudacityProject &project)
Definition: BatchCommands.cpp:54
CommandFlag
std::bitset< NCommandFlags > CommandFlag
Definition: CommandFlag.h:31
EffectManager::Get
static EffectManager & Get()
Definition: EffectManager.cpp:42
ProjectAudioManager.h
MacroCommands::AddToMacro
void AddToMacro(const CommandID &command, int before=-1)
Definition: BatchCommands.cpp:820
wxArrayStringEx
Extend wxArrayString with move operations and construction and insertion fromstd::initializer_list.
Definition: wxArrayStringEx.h:18
MacroCommands::AbortBatch
void AbortBatch()
Definition: BatchCommands.cpp:815
SelectUtilities::SelectAllIfNoneAndAllowed
bool SelectAllIfNoneAndAllowed(AudacityProject &project)
Definition: SelectUtilities.cpp:96
EffectUI::DialogFactory
AUDACITY_DLL_API wxDialog * DialogFactory(wxWindow &parent, EffectHostInterface *pHost, EffectUIClientInterface *client)
Definition: EffectUI.cpp:1820
FileNames::LegacyChainDir
FILES_API FilePath LegacyChainDir()
MacroCommands::MigrateLegacyChains
static void MigrateLegacyChains()
Definition: BatchCommands.cpp:878
MacroCommandsCatalog::Entries
std::vector< Entry > Entries
Definition: BatchCommands.h:35
MacroCommandsCatalog
Definition: BatchCommands.h:28
TranslatableString::Strip
TranslatableString & Strip(unsigned options=MenuCodes) &
Definition: TranslatableString.cpp:41
EffectUI.h
CommandIDs
std::vector< CommandID > CommandIDs
Definition: Identifier.h:233
MacroCommands::GetCount
int GetCount()
Definition: BatchCommands.cpp:119
EffectManager::GetCommandIdentifier
CommandID GetCommandIdentifier(const PluginID &ID)
Definition: EffectManager.cpp:122
CommandContext::Status
virtual void Status(const wxString &message, bool bFlush=false) const
Definition: CommandContext.cpp:63
CommandContext.h
MacroCommands::DoAudacityCommand
static bool DoAudacityCommand(const PluginID &ID, const CommandContext &context, unsigned flags)
Definition: BatchCommands.cpp:497
FilePath
wxString FilePath
Definition: Project.h:20
EffectManager
EffectManager is the class that handles effects and effect categories.
Definition: EffectManager.h:46
MenuManager::UpdateMenus
void UpdateMenus(bool checkActive=true)
Definition: Menus.cpp:635
CommandContext
CommandContext provides additional information to an 'Apply()' command. It provides the project,...
Definition: CommandContext.h:34
label
TranslatableString label
Definition: Tags.cpp:756
FileNames::TextFiles
FILES_API const FileType TextFiles
Definition: FileNames.h:74
PluginManager.h
MacroCommands::GetCommand
CommandID GetCommand(int index)
Definition: BatchCommands.cpp:101
AudacityProject::mBatchMode
int mBatchMode
Definition: Project.h:129
PluginID
wxString PluginID
Definition: EffectManager.h:30
name
const TranslatableString name
Definition: Distortion.cpp:98
SelectFile.h
UndoStackElem::state
UndoState state
Definition: UndoManager.h:110
MacroCommands::DeleteFromMacro
void DeleteFromMacro(int index)
Definition: BatchCommands.cpp:835
UndoManager.h
PluginManager::GetPlugin
const PluginDescriptor * GetPlugin(const PluginID &ID) const
Definition: PluginManager.cpp:1445
PluginTypeAudacityCommand
@ PluginTypeAudacityCommand
Definition: PluginManager.h:36
PluginDescriptor
Definition: PluginManager.h:44
MacroCommands::GetParams
wxString GetParams(int index)
Definition: BatchCommands.cpp:110
MacroCommands::mProject
AudacityProject & mProject
Definition: BatchCommands.h:118
ProjectAudioManager::Stop
void Stop(bool stopStream=true)
Definition: ProjectAudioManager.cpp:314
MacroCommands::GetNames
static wxArrayString GetNames()
Definition: BatchCommands.cpp:911
MacroCommands::DeleteMacro
bool DeleteMacro(const wxString &name)
Definition: BatchCommands.cpp:277
Menus.h
MacroCommands::WriteMacro
wxString WriteMacro(const wxString &macro, wxWindow *parent=nullptr)
Definition: BatchCommands.cpp:206
MacroCommands::AddMacro
bool AddMacro(const wxString &macro)
Definition: BatchCommands.cpp:265
ProjectHistory::PushState
void PushState(const TranslatableString &desc, const TranslatableString &shortDesc)
Definition: ProjectHistory.cpp:90
PluginManager::Get
static PluginManager & Get()
Definition: PluginManager.cpp:695
MacroCommands::HandleTextualCommand
static bool HandleTextualCommand(CommandManager &commandManager, const CommandID &Str, const CommandContext &context, CommandFlag flags, bool alwaysEnabled)
Definition: BatchCommands.cpp:593
PluginManager
PluginManager maintains a list of all plug ins. That covers modules, effects, generators,...
Definition: PluginManager.h:174
TaggedIdentifier< CommandIdTag, false >
names
static TranslatableStrings names
Definition: Tags.cpp:744
MacroCommands::PromptForParamsFor
static wxString PromptForParamsFor(const CommandID &command, const wxString &params, wxWindow &parent)
Definition: BatchCommands.cpp:447
_
#define _(s)
Definition: Internat.h:75
UndoManager::Get
static UndoManager & Get(AudacityProject &project)
Definition: UndoManager.cpp:57
AudacityProject
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:92
MacroCommandsCatalog::ByFriendlyName
Entries::const_iterator ByFriendlyName(const TranslatableString &friendlyName) const
Definition: BatchCommands.cpp:407
MacroCommandsCatalog::MacroCommandsCatalog
MacroCommandsCatalog(const AudacityProject *project)
Definition: BatchCommands.cpp:303
FileNames.h
MacroCommandsCatalog::Entry::name
ComponentInterfaceSymbol name
Definition: BatchCommands.h:32
AudacityMessageBox.h
MenuManager::Get
static MenuManager & Get(AudacityProject &project)
Definition: Menus.cpp:70
PluginManager::PluginsOfType
Range PluginsOfType(int type)
Definition: PluginManager.h:281
CommandManager
CommandManager implements a system for organizing all user-callable commands.
Definition: CommandManager.h:59
ProjectHistory.h
MacroCommands::ResetMacro
void ResetMacro()
Definition: BatchCommands.cpp:845
FileNames::MacroDir
FILES_API FilePath MacroDir()
Verbatim
TranslatableString Verbatim(wxString str)
Require calls to the one-argument constructor to go through this distinct global function name.
Definition: TranslatableString.h:321
EffectManager::kSkipState
@ kSkipState
Definition: EffectManager.h:55
EffectManager::GetEffectByIdentifier
const PluginID & GetEffectByIdentifier(const CommandID &strTarget)
Definition: EffectManager.cpp:821
EffectUI::DoEffect
AUDACITY_DLL_API bool DoEffect(const PluginID &ID, const CommandContext &context, unsigned flags)
'Repeat Last Effect'.
Definition: EffectUI.cpp:1860
MP3Conversion
static const auto MP3Conversion
Definition: BatchCommands.cpp:73
Track.h
declares abstract base class Track, TrackList, and iterators over TrackList
MacroCommands::GetNamesOfDefaultMacros
static wxArrayStringEx GetNamesOfDefaultMacros()
Definition: BatchCommands.cpp:76
Shuttle.h
FileNames::DoCopyFile
FILES_API bool DoCopyFile(const FilePath &file1, const FilePath &file2, bool overwrite=true)
Prefs.h
params
EffectDistortion::Params params
Definition: Distortion.cpp:99
ProjectWindow.h
TranslatableString::Translation
wxString Translation() const
Definition: TranslatableString.h:79
CommandManager::Get
static CommandManager & Get(AudacityProject &project)
Definition: CommandManager.cpp:207
TranslatableString::Join
TranslatableString & Join(TranslatableString arg, const wxString &separator={}) &
Append another translatable string.
Definition: TranslatableString.cpp:124
wxLOG_COMPONENT
#define wxLOG_COMPONENT
Definition: BatchCommands.cpp:18
TranslatableString::Stripped
TranslatableString Stripped(unsigned options=MenuCodes) const
non-mutating, constructs another TranslatableString object
Definition: TranslatableString.h:198
EffectManager.h
settings
static Settings & settings()
Definition: TrackInfo.cpp:86
MacroCommands::mParamsMacro
wxArrayString mParamsMacro
Definition: BatchCommands.h:121
MacroCommands::ApplyCommandInBatchMode
bool ApplyCommandInBatchMode(const TranslatableString &friendlyCommand, const CommandID &command, const wxString &params, CommandContext const *pContext=NULL)
Definition: BatchCommands.cpp:663
EffectManager::GetPreset
wxString GetPreset(const PluginID &ID, const wxString &params, wxWindow *parent)
Definition: EffectManager.cpp:615
MacroReentryCount
static int MacroReentryCount
Definition: BatchCommands.cpp:684
CommandContext::project
AudacityProject & project
Definition: CommandContext.h:64
SelectUtilities.h
EffectManager::DoAudacityCommand
bool DoAudacityCommand(const PluginID &ID, const CommandContext &, wxWindow *parent, bool shouldPrompt=true)
Definition: EffectManager.cpp:77
MacroCommands::ApplyCommand
bool ApplyCommand(const TranslatableString &friendlyCommand, const CommandID &command, const wxString &params, CommandContext const *pContext=NULL)
Definition: BatchCommands.cpp:621
FadeEnds
static const auto FadeEnds
Definition: BatchCommands.cpp:74
ProjectHistory::Get
static ProjectHistory & Get(AudacityProject &project)
Definition: ProjectHistory.cpp:26
MacroCommands::mAbort
bool mAbort
Definition: BatchCommands.h:122
UndoStackElem
Holds one item with description and time range for the UndoManager.
Definition: UndoManager.h:97
MacroCommands::IsFixed
bool IsFixed(const wxString &name)
Definition: BatchCommands.cpp:931