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