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