Audacity 3.2.0
BatchProcessDialog.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 ApplyMacroDialog.cpp
6
7 Dominic Mazzoni
8 James Crook
9
10*******************************************************************//*******************************************************************/
16
17
18#include "BatchProcessDialog.h"
19
20#include <wx/setup.h> // for wxUSE_* macros
21
22#ifdef __WXMSW__
23 #include <wx/ownerdrw.h>
24#endif
25
26#include <wx/defs.h>
27#include <wx/checkbox.h>
28#include <wx/choice.h>
29#include <wx/intl.h>
30#include <wx/log.h>
31#include <wx/sizer.h>
32#include <wx/statbox.h>
33#include <wx/stattext.h>
34#include <wx/textctrl.h>
35#include <wx/listctrl.h>
36#include <wx/radiobut.h>
37#include <wx/button.h>
38#include <wx/imaglist.h>
39#include <wx/settings.h>
40
41#include "Clipboard.h"
42#include "ShuttleGui.h"
43#include "Menus.h"
44#include "Prefs.h"
45#include "Project.h"
46#include "ProjectFileManager.h"
47#include "ProjectHistory.h"
48#include "ProjectManager.h"
49#include "ProjectWindow.h"
50#include "SelectUtilities.h"
51#include "Track.h"
53#include "effects/Effect.h"
54#include "../images/Arrow.xpm"
55#include "../images/Empty9x16.xpm"
56#include "UndoManager.h"
57
58#include "AllThemeResources.h"
59
61#include "FileNames.h"
62#include "import/Import.h"
65#include "widgets/HelpSystem.h"
66
67#if wxUSE_ACCESSIBILITY
69#endif
70
71#define MacrosPaletteTitle XO("Macros Palette")
72#define ManageMacrosTitle XO("Manage Macros")
73
74
75// Separate numerical range from the additional buttons
76// in the expanded view (which start at 10,000).
77#define MacrosListID 7001
78#define CommandsListID 7002
79#define ApplyToProjectID 7003
80#define ApplyToFilesID 7004
81#define ExpandID 7005
82#define ShrinkID 7006
83
84BEGIN_EVENT_TABLE(ApplyMacroDialog, wxDialogWrapper)
91
93 wxWindow * parent, AudacityProject &project, bool bInherited):
94 wxDialogWrapper(parent, wxID_ANY, MacrosPaletteTitle,
95 wxDefaultPosition, wxDefaultSize,
96 wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
97 , mMacroCommands{ project }
98 , mProject{ project }
99 , mCatalog( &project )
100{
101 mAbort = false;
102 mbExpanded = false;
103 if( bInherited )
104 return;
105 SetLabel(MacrosPaletteTitle); // Provide visual label
106 SetName(MacrosPaletteTitle); // Provide audible label
107 Populate();
108
109}
110
112{
113}
114
116{
117 //------------------------- Main section --------------------
118 ShuttleGui S(this, eIsCreating);
120 // ----------------------- End of main section --------------
121 // Get and validate the currently active macro
122 mActiveMacro = gPrefs->Read(wxT("/Batch/ActiveMacro"), wxT(""));
123 // Go populate the macros list.
125
126 Layout();
127 Fit();
128 wxSize sz = GetSize();
129 SetSizeHints( sz );
130
131 // Size and place window
132 SetSize(std::min(wxSystemSettings::GetMetric(wxSYS_SCREEN_X) * 3 / 4, sz.GetWidth()),
133 std::min(wxSystemSettings::GetMetric(wxSYS_SCREEN_Y) * 4 / 5, 400));
134
135 Center();
136
137 // Set the column size for the macros list.
138 sz = mMacros->GetClientSize();
139 mMacros->SetColumnWidth(0, sz.x);
140}
141
144{
145 /*i18n-hint: A macro is a sequence of commands that can be applied
146 * to one or more audio files.*/
147 S.StartStatic(XO("Select Macro"), 1);
148 {
149 mMacros = S.Id(MacrosListID).Prop(1)
150 .Style(wxSUNKEN_BORDER | wxLC_REPORT | wxLC_HRULES | wxLC_VRULES |
151 wxLC_SINGLE_SEL)
152 // i18n-hint: This is the heading for a column in the edit macros dialog
153 .AddListControlReportMode( { XO("Macro") } );
154 }
155 S.EndStatic();
156
157 S.StartHorizontalLay(wxEXPAND, 0);
158 {
159 S.AddPrompt( XXO("Apply Macro to:") );
160 wxButton* btn = S.Id(ApplyToProjectID)
161 .Name(XO("Apply macro to project"))
162 .AddButton(XXO("&Project"));
163#if wxUSE_ACCESSIBILITY
164 // so that name can be set on a standard control
165 btn->SetAccessible(safenew WindowAccessible(btn));
166#endif
167
168 btn = S.Id(ApplyToFilesID)
169 .Name(XO("Apply macro to files..."))
170 .AddButton(XXO("&Files..."));
171#if wxUSE_ACCESSIBILITY
172 // so that name can be set on a standard control
173 btn->SetAccessible(safenew WindowAccessible(btn));
174#endif
175 }
176 S.EndHorizontalLay();
177
178 S.StartHorizontalLay(wxEXPAND, 0);
179 {
180 /* i18n-hint: The Expand button makes the dialog bigger, with more in it */
181 mResize = S.Id(ExpandID).AddButton(XXO("&Expand"));
182 S.AddSpace( 10,10,1 );
183 S.AddStandardButtons( eCloseButton | eHelpButton);
184 }
185 S.EndHorizontalLay();
186}
187
192{
194 int i;
195
196 int topItem = mMacros->GetTopItem();
197 mMacros->DeleteAllItems();
198 for (i = 0; i < (int)names.size(); i++) {
199 mMacros->InsertItem(i, names[i]);
200 }
201
202 int item = mMacros->FindItem(-1, mActiveMacro);
203 bool bFound = item >=0;
204 if (item == -1) {
205 item = 0;
206 mActiveMacro = mMacros->GetItemText(0);
207 }
208
209 // Select the name in the list...this will fire an event.
210 mMacros->SetItemState(item, wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED,
211 wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED);
212
213 if( 0 <= topItem && topItem < (int)mMacros->GetItemCount())
214 {
215 // Workaround for scrolling being windows only.
216 // Try to scroll back to where we once were...
217 mMacros->EnsureVisible( (int)mMacros->GetItemCount() -1 );
218 mMacros->EnsureVisible( topItem );
219 // And then make sure whatever is selected is still visible...
220 if( bFound )
221 mMacros->EnsureVisible( item );
222 }
223}
224
225void ApplyMacroDialog::OnHelp(wxCommandEvent & WXUNUSED(event))
226{
227 const auto &page = GetHelpPageName();
228 HelpSystem::ShowHelp(this, page, true);
229}
230
231void ApplyMacroDialog::OnApplyToProject(wxCommandEvent & WXUNUSED(event))
232{
233 long item = mMacros->GetNextItem(-1,
234 wxLIST_NEXT_ALL,
235 wxLIST_STATE_SELECTED);
236
237 if (item == -1) {
238 AudacityMessageBox(XO("No macro selected"));
239 return;
240 }
241 ApplyMacroToProject( item );
242}
243
244CommandID ApplyMacroDialog::MacroIdOfName( const wxString & MacroName )
245{
246 wxString Temp = MacroName;
247 Temp.Replace(" ","");
248 Temp = wxString( "Macro_" ) + Temp;
249 return Temp;
250}
251
252// Apply macro, given its ID.
253// Does nothing if not found, rather than returning an error.
255{
256 for( int i=0;i<mMacros->GetItemCount();i++){
257 wxString name = mMacros->GetItemText(i);
258 if( MacroIdOfName( name ) == MacroID ){
259 ApplyMacroToProject( i, bHasGui );
260 return;
261 }
262 }
263}
264
265// Apply macro, given its number in the list.
266void ApplyMacroDialog::ApplyMacroToProject( int iMacro, bool bHasGui )
267{
268 wxString name = mMacros->GetItemText(iMacro);
269 if( name.empty() )
270 return;
271
272#ifdef OPTIONAL_ACTIVITY_WINDOW
273 wxDialogWrapper activityWin( this, wxID_ANY, GetTitle());
274 activityWin.SetName();
275 ShuttleGui S(&activityWin, eIsCreating);
276
277 S.StartHorizontalLay(wxCENTER, false);
278 {
279 S.StartStatic( {}, false); // deliberately not translated (!)
280 {
281 S.SetBorder(20);
282 S.AddFixedText(XO("Applying '%s' to current project")
283 .Format( name ) );
284 }
285 S.EndStatic();
286 }
287 S.EndHorizontalLay();
288
289 activityWin.Layout();
290 activityWin.Fit();
291 activityWin.CenterOnScreen();
292 // Avoid overlap with progress.
293 int x,y;
294 activityWin.GetPosition( &x, &y );
295 activityWin.Move(wxMax(0,x-300), 0);
296 activityWin.Show();
297
298 // Without this the newly created dialog may not show completely.
299 wxYield();
300#endif
301
302 //Since we intend to keep this dialog open, there is no reason to hide it
303 //and then show it again.
304 //if( bHasGui )
305 // Hide();
306
307 gPrefs->Write(wxT("/Batch/ActiveMacro"), name);
308 gPrefs->Flush();
309
311
312 // The disabler must get deleted before the EndModal() call. Otherwise,
313 // the menus on OSX will remain disabled.
314 bool success;
315 {
316#ifdef OPTIONAL_ACTIVITY_WINDOW
317 wxWindowDisabler wd(&activityWin);
318#endif
319 success = GuardedCall< bool >(
320 [this]{ return mMacroCommands.ApplyMacro(mCatalog); } );
321 }
322
323 if( !bHasGui )
324 return;
325
326 Show();
327 Raise();
328}
329
330void ApplyMacroDialog::OnApplyToFiles(wxCommandEvent & WXUNUSED(event))
331{
332 long item = mMacros->GetNextItem(-1,
333 wxLIST_NEXT_ALL,
334 wxLIST_STATE_SELECTED);
335 if (item == -1) {
336 AudacityMessageBox( XO("No macro selected") );
337 return;
338 }
339
340 wxString name = mMacros->GetItemText(item);
341 gPrefs->Write(wxT("/Batch/ActiveMacro"), name);
342 gPrefs->Flush();
343
344 AudacityProject *project = &mProject;
345 if (!TrackList::Get( *project ).empty()) {
347 XO("Please save and close the current project first.") );
348 return;
349 }
350
351 // This insures that we start with an empty and temporary project
354
355 auto prompt = XO("Select file(s) for batch processing...");
356
357 const auto fileTypes = Importer::Get().GetFileTypes();
358
359 auto path = FileNames::FindDefaultPath(FileNames::Operation::Open);
360 FileDialogWrapper dlog(this,
361 prompt,
362 path,
363 wxT(""),
364 fileTypes,
365 wxFD_OPEN | wxFD_MULTIPLE | wxRESIZE_BORDER);
366
368 if (dlog.ShowModal() != wxID_OK) {
369 Raise();
370 return;
371 }
372 Raise();
373
374 wxArrayString files;
375 dlog.GetPaths(files);
376
377 files.Sort();
378
379 wxDialogWrapper activityWin(this, wxID_ANY, Verbatim( GetTitle() ),
380 wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER
381 );
382 activityWin.SetName();
383 ShuttleGui S(&activityWin, eIsCreating);
384
385 wxListCtrl * fileList = NULL;
386
387 S.StartVerticalLay(1);
388 {
389 S.StartStatic(XO("Applying..."), 1);
390 {
391 auto imageList = std::make_unique<wxImageList>(9, 16);
392 imageList->Add(wxIcon(empty9x16_xpm));
393 imageList->Add(wxIcon(arrow_xpm));
394
395 fileList = S.Id(CommandsListID)
396 .Style(wxSUNKEN_BORDER | wxLC_REPORT | wxLC_HRULES | wxLC_VRULES |
397 wxLC_SINGLE_SEL)
398 .AddListControlReportMode( { XO("File") } );
399 // AssignImageList takes ownership
400 fileList->AssignImageList(imageList.release(), wxIMAGE_LIST_SMALL);
401 }
402 S.EndStatic();
403
404 S.StartHorizontalLay(wxCENTER, 0);
405 {
406 S.Id(wxID_CANCEL).AddButton(XXO("&Cancel"));
407 }
408 S.EndHorizontalLay();
409 }
410 S.EndVerticalLay();
411
412 int i;
413 for (i = 0; i < (int)files.size(); i++ ) {
414 fileList->InsertItem(i, files[i], i == 0);
415 }
416
417 // Set the column size for the files list.
418 fileList->SetColumnWidth(0, wxLIST_AUTOSIZE);
419
420 int width = wxMin( fileList->GetColumnWidth(0), 1000);
421 wxSize sz = fileList->GetClientSize();
422 if (sz.GetWidth() < width ) {
423 sz.SetWidth(width);
424 if (sz.GetHeight() < width *0.7)
425 sz.SetHeight(width * 0.7);
426 fileList->SetInitialSize(sz);
427 }
428
429 activityWin.Layout();
430 activityWin.Fit();
431 activityWin.CenterOnScreen();
432 // Avoid overlap with progress.
433 int x,y;
434 activityWin.GetPosition( &x, &y );
435 activityWin.Move(wxMax(0,x-300), 0);
436 activityWin.Show();
437
438 // Without this the newly created dialog may not show completely.
439 wxYield();
440 // We could avoid hiding, but there are many dialogs on screen,
441 // and hiding this one temporarily has some advantages.
442 Hide();
443
445 {
446 // Move global clipboard contents aside temporarily
447 Clipboard tempClipboard;
448 auto &globalClipboard = Clipboard::Get();
449
450 // DV: Macro invocation on file will reset the project to the
451 // initial state. There is a possibility, that clipboard will contain
452 // references to the data removed
453 if (globalClipboard.Project().lock().get() == project)
454 globalClipboard.Clear();
455
456 globalClipboard.Swap(tempClipboard);
457 auto cleanup = finally([&]{
458 globalClipboard.Swap(tempClipboard);
459 });
460
461 wxWindowDisabler wd(&activityWin);
462 for (i = 0; i < (int)files.size(); i++) {
463 if (i > 0) {
464 //Clear the arrow in previous item.
465 fileList->SetItemImage(i - 1, 0, 0);
466 }
467 fileList->SetItemImage(i, 1, 1);
468 fileList->EnsureVisible(i);
469
470 auto success = GuardedCall< bool >([&] {
471 ProjectFileManager::Get(*project).Import(files[i]);
472 ProjectWindow::Get(*project).ZoomAfterImport(nullptr);
475 return false;
476
477 if (!activityWin.IsShown() || mAbort)
478 return false;
479
480 return true;
481 });
482
483 // Ensure project is completely reset
485 // Bug2567:
486 // Must also destroy the clipboard, to be sure sample blocks are
487 // all freed and their ids can be reused safely in the next pass
488 globalClipboard.Clear();
489
490 if (!success)
491 break;
492 }
493 }
494
495 Show();
496 Raise();
497}
498
499void ApplyMacroDialog::OnCancel(wxCommandEvent & WXUNUSED(event))
500{
501 Hide();
502}
503
505#include <wx/textdlg.h>
506#include "BatchCommandDialog.h"
507
508enum {
509 AddButtonID = 10000,
516
518
524
525// MacrosListID 7005
526// CommandsListID, 7002
527// Re-Use IDs from ApplyMacroDialog.
530};
531
532BEGIN_EVENT_TABLE(MacrosWindow, ApplyMacroDialog)
535 EVT_LIST_BEGIN_LABEL_EDIT(MacrosListID, MacrosWindow::OnMacrosBeginEdit)
536 EVT_LIST_END_LABEL_EDIT(MacrosListID, MacrosWindow::OnMacrosEndEdit)
544
547 EVT_SIZE(MacrosWindow::OnSize)
548
555
559
560 EVT_KEY_DOWN(MacrosWindow::OnKeyDown)
562
563enum {
567};
568
571 wxWindow * parent, AudacityProject &project, bool bExpanded):
572 ApplyMacroDialog(parent, project, true)
573 , mProject{ project }
574{
575 mbExpanded = bExpanded;
576 auto Title = WindowTitle();
577 SetLabel( Title ); // Provide visual label
578 SetName( Title ); // Provide audible label
579 SetTitle( Title );
580
581 mChanged = false;
583
584 if( mbExpanded )
585 Populate();
586 else
588}
589
591{
592}
593
596{
597 //------------------------- Main section --------------------
598 ShuttleGui S(this, eIsCreating);
600 // ----------------------- End of main section --------------
601
602 // Get and validate the currently active macro
603 mActiveMacro = gPrefs->Read(wxT("/Batch/ActiveMacro"), wxT(""));
604 // Go populate the macros list.
606
607 // We have a bare list. We need to add columns and content.
608 PopulateList();
609
610 // Layout and set minimum size of window
611 Layout();
612 Fit();
613 SetSizeHints(GetSize());
614
615 // Size and place window
616 SetSize(std::min(wxSystemSettings::GetMetric(wxSYS_SCREEN_X) * 3 / 4, 800),
617 std::min(wxSystemSettings::GetMetric(wxSYS_SCREEN_Y) * 4 / 5, 400));
618 Center();
619
620 // Set the column size for the macros list.
621 wxSize sz = mMacros->GetClientSize();
622 mMacros->SetColumnWidth(0, sz.x);
623
624 // Size columns properly
625 FitColumns();
626}
627
630{
631 S.StartHorizontalLay(wxEXPAND, 1);
632 {
633 S.StartStatic(XO("Select Macro"),0);
634 {
635 S.StartHorizontalLay(wxEXPAND,1);
636 {
637 mMacros = S.Id(MacrosListID).Prop(1)
638 .Style(wxSUNKEN_BORDER | wxLC_REPORT | wxLC_HRULES
639 | wxLC_SINGLE_SEL | wxLC_EDIT_LABELS)
640 // i18n-hint: This is the heading for a column in the edit macros dialog
641 .AddListControlReportMode( { XO("Macro") } );
642 S.StartVerticalLay(wxALIGN_TOP, 0);
643 {
644 S.Id(AddButtonID).AddButton(XXO("&New"), wxALIGN_LEFT);
645 mRemove = S.Id(RemoveButtonID).AddButton(XXO("Remo&ve"), wxALIGN_LEFT);
646 mRename = S.Id(RenameButtonID).AddButton(XXO("&Rename..."), wxALIGN_LEFT);
647 mRestore = S.Id(RestoreButtonID).AddButton(XXO("Re&store"), wxALIGN_LEFT);
648 mImport = S.Id(ImportButtonID).AddButton(XO("I&mport..."), wxALIGN_LEFT);
649 mExport = S.Id(ExportButtonID).AddButton(XO("E&xport..."), wxALIGN_LEFT);
650 }
651 S.EndVerticalLay();
652 }
653 S.EndHorizontalLay();
654 }
655 S.EndStatic();
656
657 S.StartStatic(XO("Edit Steps"), true);
658 {
659 S.StartHorizontalLay(wxEXPAND,1);
660 {
662 .Style(wxSUNKEN_BORDER | wxLC_REPORT | wxLC_HRULES | wxLC_VRULES |
663 wxLC_SINGLE_SEL)
664 .AddListControlReportMode({
665 /* i18n-hint: This is the number of the command in the list */
666 { XO("Num"), wxLIST_FORMAT_RIGHT },
667 { XO("Command "), wxLIST_FORMAT_RIGHT },
668 { XO("Parameters"), wxLIST_FORMAT_LEFT }
669 });
670
671 S.StartVerticalLay(wxALIGN_TOP, 0);
672 {
673 S.Id(InsertButtonID).AddButton(XXO("&Insert"), wxALIGN_LEFT);
674 S.Id(EditButtonID).AddButton(XXO("&Edit..."), wxALIGN_LEFT);
675 S.Id(DeleteButtonID).AddButton(XXO("De&lete"), wxALIGN_LEFT);
676 S.Id(UpButtonID).AddButton(XXO("Move &Up"), wxALIGN_LEFT);
677 S.Id(DownButtonID).AddButton(XXO("Move &Down"), wxALIGN_LEFT);
678 mSave = S.Id(SaveButtonID).AddButton(XO("&Save"), wxALIGN_LEFT);
679 mSave->Enable( mChanged );
680 }
681 S.EndVerticalLay();
682 }
683 S.EndHorizontalLay();
684 }
685 S.EndStatic();
686 }
687 S.EndHorizontalLay();
688
689 S.StartHorizontalLay(wxEXPAND, 0);
690 {
691 /* i18n-hint: The Shrink button makes the dialog smaller, with less in it */
692 mResize = S.Id(ShrinkID).AddButton(XXO("Shrin&k"));
693 // Using variable text just to get the positioning options.
694 S.Prop(0).AddVariableText(
695 XO("Apply Macro to:"), false, wxALL | wxALIGN_CENTRE_VERTICAL );
696 wxButton* btn = S.Id(ApplyToProjectID)
697 .Name(XO("Apply macro to project"))
698 .AddButton(XXO("&Project"));
699#if wxUSE_ACCESSIBILITY
700 // so that name can be set on a standard control
701 btn->SetAccessible(safenew WindowAccessible(btn));
702#endif
703
704 btn = S.Id(ApplyToFilesID)
705 .Name(XO("Apply macro to files..."))
706 .AddButton(XXO("&Files..."));
707#if wxUSE_ACCESSIBILITY
708 // so that name can be set on a standard control
709 btn->SetAccessible(safenew WindowAccessible(btn));
710#endif
711 S.AddSpace( 10,10,1 );
712 // Bug 2524 OK button does much the same as cancel, so remove it.
713 // OnCancel prompts you if there has been a change.
714 // OnOK saves without prompting.
715 // That difference is too slight to merit a button, and with the OK
716 // button, people might expect the dialog to apply the macro too.
717 S.AddStandardButtons( /*eOkButton |*/ eCloseButton | eHelpButton);
718 }
719
720 S.EndHorizontalLay();
721
722
723 return;
724}
725
728{
729 int topItem = mList->GetTopItem();
730 mList->DeleteAllItems();
731
732 for (int i = 0; i < mMacroCommands.GetCount(); i++) {
735 }
736 /*i18n-hint: This is the last item in a list.*/
737 AddItem(_("- END -"), wxT(""));
738
739 // Select the name in the list...this will fire an event.
740 if (mSelectedCommand >= (int)mList->GetItemCount()) {
742 }
743 mList->SetItemState(mSelectedCommand,
744 wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED,
745 wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED);
746 if( 0 <= topItem && topItem < (int)mList->GetItemCount())
747 {
748 // Workaround for scrolling being windows only.
749 // Try to scroll back to where we once were...
750 mList->EnsureVisible( (int)mList->GetItemCount() -1 );
751 mList->EnsureVisible( topItem );
752 // And then make sure whatever is selected is still visible...
753 if (mSelectedCommand >= 0) {
754 mList->EnsureVisible( mSelectedCommand );
755 }
756 }
757}
758
760void MacrosWindow::AddItem(const CommandID &Action, const wxString &Params)
761{
763 auto friendlyName = entry != mCatalog.end()
764 ? entry->name.StrippedTranslation()
765 :
766 // uh oh, using GET to expose an internal name to the user!
767 // in default of any better friendly name
768 Action.GET();
769
770 int i = mList->GetItemCount();
771
772 mList->InsertItem(i, wxString::Format(wxT(" %02i"), i + 1));
773 mList->SetItem(i, ActionColumn, friendlyName );
774 mList->SetItem(i, ParamsColumn, Params );
775}
776
778{
779 // OK even on mac, as dialog is modal.
780 auto p = &mProject;
782}
783
784void MacrosWindow::UpdateDisplay( bool bExpanded )
785{
786 // If we failed to save changes, we abandon the attempt to
787 // change the expand/shrink state of the GUI.
788 if( !SaveChanges() )
789 return;
790
791 mbExpanded = bExpanded;
792
793 mChanged = false;
794 // if we try to access the about to be destroyed mSave button
795 // inappropriately, we need to crash rather than (sometimes) silently
796 // succeed.
797 mSave = nullptr;
798
799 DestroyChildren();
800 SetSizer( nullptr );
801
803 SetMinSize( wxSize( 200,200 ));
804
805 // Get and set position for optical stability.
806 // Expanded and shrunk dialogs 'stay where they were'.
807 // That's OK , and what we want, even if we exapnd off-screen.
808 // We won't shrink to being off-screen, since the shrink button
809 // was clicked, so must have been on screen.
810 wxPoint p = GetPosition( );
811 if( mbExpanded )
812 Populate();
813 else
815 SetPosition( p );
816 mResize->SetFocus();
817
818 auto Title = WindowTitle();
819 SetLabel( Title ); // Provide visual label
820 SetName( Title ); // Provide audible label
821 SetTitle( Title );
822}
823
824void MacrosWindow::OnExpand(wxCommandEvent &WXUNUSED(event))
825{ UpdateDisplay( true );}
826
827void MacrosWindow::OnShrink(wxCommandEvent &WXUNUSED(event))
828{
829 if( ChangeOK() )
830 UpdateDisplay( false );
831}
832
833
835{
836 if (mChanged) {
837 int id;
838
839 auto title = XO("%s changed").Format( mActiveMacro );
840 auto msg = XO("Do you want to save the changes?");
841
843 msg,
844 title,
845 wxYES_NO | wxCANCEL);
846 if (id == wxCANCEL) {
847 return false;
848 }
849
850 if (id == wxYES) {
852 return false;
853 }
854 }
855
856 mChanged = false;
857 mSave->Enable( mChanged );
858 }
859
860 return true;
861}
863void MacrosWindow::OnMacroSelected(wxListEvent & event)
864{
865 if (!ChangeOK()) {
866 event.Veto();
867 return;
868 }
869
870 int item = event.GetIndex();
871
872 mActiveMacro = mMacros->GetItemText(item);
874}
875
877{
879 if( !mbExpanded )
880 return;
881
883 mRemove->Disable();
884 mRename->Disable();
885 mRestore->Enable();
886 }
887 else {
888 mRemove->Enable();
889 mRename->Enable();
890 mRestore->Disable();
891 }
892
893 PopulateList();
894}
895
897void MacrosWindow::OnListSelected(wxListEvent & WXUNUSED(event))
898{
899 FitColumns();
900}
901
903void MacrosWindow::OnSize(wxSizeEvent & WXUNUSED(event))
904{
905 // Refresh the layout and re-fit the columns.
906 Layout();
907 if( !mbExpanded )
908 return;
909 FitColumns();
910}
911
913{
914
915#if defined(__WXMAC__)
916 // wxMac uses a hard coded width of 150 when wxLIST_AUTOSIZE_USEHEADER
917 // is specified, so we calculate the width ourselves. This method may
918 // work equally well on other platforms.
919 for (size_t c = 0; c < mList->GetColumnCount(); c++) {
920 wxListItem info;
921 int width;
922
923 mList->SetColumnWidth(c, wxLIST_AUTOSIZE);
924 info.Clear();
925 info.SetId(c);
926 info.SetMask(wxLIST_MASK_TEXT | wxLIST_MASK_WIDTH);
927 mList->GetColumn(c, info);
928
929 mList->GetTextExtent(info.GetText(), &width, NULL);
930 width += 2 * 4; // 2 * kItemPadding - see listctrl_mac.cpp
931 width += 16; // kIconWidth - see listctrl_mac.cpp
932
933 mList->SetColumnWidth(c, wxMax(width, mList->GetColumnWidth(c)));
934 }
935
936 // Looks strange, but it forces the horizontal scrollbar to get
937 // drawn. If not done, strange column sizing can occur if the
938 // user attempts to resize the columns.
939 mList->SetClientSize(mList->GetClientSize());
940#else
941 mList->SetColumnWidth(0, wxLIST_AUTOSIZE_USEHEADER);
942 mList->SetColumnWidth(1, wxLIST_AUTOSIZE_USEHEADER);
943 mList->SetColumnWidth(2, wxLIST_AUTOSIZE);
944#endif
945
946 int bestfit = mList->GetColumnWidth(2);
947 int clientsize = mList->GetClientSize().GetWidth();
948 int col0 = mList->GetColumnWidth(0);
949 int col1 = mList->GetColumnWidth(1);
950 bestfit = (bestfit > clientsize-col0-col1)? bestfit : clientsize-col0-col1;
951 mList->SetColumnWidth(2, bestfit);
952
953}
954
956void MacrosWindow::OnMacrosBeginEdit(wxListEvent &event)
957{
958 int itemNo = event.GetIndex();
959
960 wxString macro = mMacros->GetItemText(itemNo);
961
962 if (mMacroCommands.IsFixed(macro)) {
963 wxBell();
964 event.Veto();
965 }
966 if( mMacroBeingRenamed.IsEmpty())
967 mMacroBeingRenamed = macro;
968}
969
971void MacrosWindow::OnMacrosEndEdit(wxListEvent &event)
972{
973 if (event.IsEditCancelled()) {
975 return;
976 }
977
978 if( mMacroBeingRenamed.IsEmpty())
979 return;
980
981 wxString newname = event.GetLabel();
982
985 mActiveMacro = newname;
988 UpdateMenus();
989 event.Veto();
990}
991
993void MacrosWindow::OnAdd(wxCommandEvent & WXUNUSED(event))
994{
995 // Similar to Bug 2284 we may need to save a changed macro.
996 if (!ChangeOK()) {
997 return;
998 }
999
1000 while (true) {
1002 XO("Enter name of new macro"),
1003 XO("Name of new macro"));
1004 d.SetName(d.GetTitle());
1005 wxString name;
1006
1007 if (d.ShowModal() == wxID_CANCEL) {
1008 Raise();
1009 return;
1010 }
1011 Raise();
1012
1013 name = d.GetValue().Strip(wxString::both);
1014
1015 if (name.length() == 0) {
1017 XO("Name must not be blank"),
1018 WindowTitle(),
1019 wxOK | wxICON_ERROR,
1020 this);
1021 continue;
1022 }
1023
1024 if (name.Contains(wxFILE_SEP_PATH) ||
1025 name.Contains(wxFILE_SEP_PATH_UNIX)) {
1027 /*i18n-hint: The %c will be replaced with 'forbidden characters', like '/' and '\'.*/
1028 XO("Names may not contain '%c' and '%c'")
1029 .Format(wxFILE_SEP_PATH, wxFILE_SEP_PATH_UNIX),
1030 WindowTitle(),
1031 wxOK | wxICON_ERROR,
1032 this);
1033 continue;
1034 }
1035
1037
1039
1041 UpdateMenus();
1042
1043 break;
1044 }
1045}
1046
1048void MacrosWindow::OnRemove(wxCommandEvent & WXUNUSED(event))
1049{
1050 long item = mMacros->GetNextItem(-1,
1051 wxLIST_NEXT_ALL,
1052 wxLIST_STATE_SELECTED);
1053 if (item == -1) {
1054 return;
1055 }
1056
1057 wxString name = mMacros->GetItemText(item);
1059 this,
1060 /*i18n-hint: %s will be replaced by the name of a file.*/
1061 XO("Are you sure you want to delete %s?").Format( name ),
1062 Verbatim( GetTitle() ),
1063 wxYES_NO | wxICON_QUESTION );
1064 if (m.ShowModal() == wxID_NO) {
1065 Raise();
1066 return;
1067 }
1068 Raise();
1069
1071
1072 item++;
1073 if (item >= (mMacros->GetItemCount() - 1) && item >= 0) {
1074 item--;
1075 }
1076
1077 // Bug 2284. The macro we have just removed might have been
1078 // changed. Since we've just deleted the macro, we should
1079 // forget about that change.
1080 mChanged = false;
1081 mSave->Enable( mChanged );
1082 mActiveMacro = mMacros->GetItemText(item);
1083
1085 UpdateMenus();
1086}
1087
1089void MacrosWindow::OnRename(wxCommandEvent & WXUNUSED(event))
1090{
1091 long item = mMacros->GetNextItem(-1,
1092 wxLIST_NEXT_ALL,
1093 wxLIST_STATE_SELECTED);
1094 if (item == -1) {
1095 return;
1096 }
1097
1098 mMacros->EditLabel(item);
1099 UpdateMenus();
1100}
1101
1103void MacrosWindow::OnRestore(wxCommandEvent & WXUNUSED(event))
1104{
1106
1107 mChanged = true;
1108 mSave->Enable( mChanged );
1109
1110 PopulateList();
1111}
1112
1114void MacrosWindow::OnImport(wxCommandEvent & WXUNUSED(event))
1115{
1116 if (!ChangeOK()) {
1117 return;
1118 }
1119
1120 long item = mMacros->GetNextItem(-1,
1121 wxLIST_NEXT_ALL,
1122 wxLIST_STATE_SELECTED);
1123 if (item == -1) {
1124 return;
1125 }
1126
1127 wxString name = mMacros->GetItemText(item);
1128
1129 name = mMacroCommands.ReadMacro({}, this);
1130 if (name == wxEmptyString) {
1131 return;
1132 }
1133
1135
1137 UpdateMenus();
1138}
1139
1141void MacrosWindow::OnExport(wxCommandEvent & WXUNUSED(event))
1142{
1143 long item = mMacros->GetNextItem(-1,
1144 wxLIST_NEXT_ALL,
1145 wxLIST_STATE_SELECTED);
1146 if (item == -1) {
1147 return;
1148 }
1149
1150 mMacroCommands.WriteMacro(mMacros->GetItemText(item), this);
1151}
1152
1153void MacrosWindow::OnSave(wxCommandEvent & WXUNUSED(event))
1154{
1155 SaveChanges();
1156}
1157
1158
1161void MacrosWindow::OnCommandActivated(wxListEvent & WXUNUSED(event))
1162{
1163 wxCommandEvent dummy;
1164 OnEditCommandParams( dummy );
1165}
1166
1168void MacrosWindow::OnInsert(wxCommandEvent & WXUNUSED(event))
1169{
1170 long item = mList->GetNextItem(-1,
1171 wxLIST_NEXT_ALL,
1172 wxLIST_STATE_SELECTED);
1173 if (item == -1) {
1174 item = mList->GetItemCount()-1;
1175 }
1176 InsertCommandAt( item );
1177}
1178
1180{
1181 if (item == -1) {
1182 return;
1183 }
1184
1185 MacroCommandDialog d(this, wxID_ANY, mProject);
1186
1187 if (!d.ShowModal()) {
1188 Raise();
1189 return;
1190 }
1191 Raise();
1192
1193 if(!d.mSelectedCommand.empty())
1194 {
1197 item);
1198 mChanged = true;
1199 mSave->Enable( mChanged );
1200
1201 mSelectedCommand = item + 1;
1202 PopulateList();
1203 }
1204
1205}
1206
1207void MacrosWindow::OnEditCommandParams(wxCommandEvent & WXUNUSED(event))
1208{
1209 int item = mList->GetNextItem( -1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
1210
1211 // LAST command in list is END.
1212 // If nothing selected, add at END.
1213 // If END selected, add at END.
1214 // When adding at end we use InsertCommandAt, so that a new command
1215 // can be chosen.
1216 int lastItem = mList->GetItemCount()-1;
1217 if( (item<0) || (item+1) == mList->GetItemCount() )
1218 {
1219 InsertCommandAt( lastItem );
1220 return;
1221 }
1222
1223 // Just edit the parameters, and not the command.
1224 auto command = mMacroCommands.GetCommand(item);
1225 wxString params = mMacroCommands.GetParams(item);
1226 wxString oldParams = params;
1227
1228 params = MacroCommands::PromptForParamsFor(command, params, *this).Trim();
1229 Raise();
1230
1231 if (oldParams == params)
1232 return; // They did not actually make any changes..
1233
1235 mMacroCommands.AddToMacro(command,
1236 params,
1237 item);
1238
1239 mChanged = true;
1240 mSave->Enable( mChanged );
1241
1242 mSelectedCommand = item;
1243 PopulateList();
1244}
1245
1247void MacrosWindow::OnDelete(wxCommandEvent & WXUNUSED(event))
1248{
1249 long item = mList->GetNextItem(-1,
1250 wxLIST_NEXT_ALL,
1251 wxLIST_STATE_SELECTED);
1252 if (item == -1 || item + 1 == mList->GetItemCount()) {
1253 return;
1254 }
1255
1257
1258 mChanged = true;
1259 mSave->Enable( mChanged );
1260
1261 if (item >= (mList->GetItemCount() - 2) && item >= 0) {
1262 item--;
1263 }
1264 mSelectedCommand = item;
1265 PopulateList();
1266}
1267
1269void MacrosWindow::OnUp(wxCommandEvent & WXUNUSED(event))
1270{
1271 long item = mList->GetNextItem(-1,
1272 wxLIST_NEXT_ALL,
1273 wxLIST_STATE_SELECTED);
1274 if (item == -1 || item == 0 || item + 1 == mList->GetItemCount()) {
1275 return;
1276 }
1277
1280 item - 1);
1282
1283 mChanged = true;
1284 mSave->Enable( mChanged );
1285
1286 mSelectedCommand = item - 1;
1287 PopulateList();
1288}
1289
1291void MacrosWindow::OnDown(wxCommandEvent & WXUNUSED(event))
1292{
1293 long item = mList->GetNextItem(-1,
1294 wxLIST_NEXT_ALL,
1295 wxLIST_STATE_SELECTED);
1296 if (item == -1 || item + 2 >= mList->GetItemCount()) {
1297 return;
1298 }
1299
1302 item + 2);
1304
1305 mChanged = true;
1306 mSave->Enable( mChanged );
1307
1308 mSelectedCommand = item + 1;
1309 PopulateList();
1310}
1311
1312void MacrosWindow::OnApplyToProject(wxCommandEvent & event)
1313{
1314 if( !SaveChanges() )
1315 return;
1317}
1318
1319void MacrosWindow::OnApplyToFiles(wxCommandEvent & event)
1320{
1321 if( !SaveChanges() )
1322 return;
1324}
1325
1327 gPrefs->Write(wxT("/Batch/ActiveMacro"), mActiveMacro);
1328 gPrefs->Flush();
1329
1330 if (mChanged) {
1332 return false;
1333 }
1334 }
1335
1336 mChanged = false;
1337 if( mSave )
1338 mSave->Enable( mChanged );
1339
1340 return true;
1341}
1342
1344void MacrosWindow::OnOK(wxCommandEvent & WXUNUSED(event))
1345{
1346 if( !SaveChanges() )
1347 return;
1348 Hide();
1349 //EndModal(true);
1350}
1351
1353void MacrosWindow::OnCancel(wxCommandEvent &WXUNUSED(event))
1354{
1355 bool bWasChanged = mChanged;
1356 if (!ChangeOK()) {
1357 return;
1358 }
1359 // If we've rejected a change, we need to restore the display
1360 // of the active macro.
1361 // That's because next time we open this dialog we want to see the
1362 // unedited macro.
1363 if( bWasChanged )
1365 Hide();
1366}
1367
1369void MacrosWindow::OnKeyDown(wxKeyEvent &event)
1370{
1371 if (event.GetKeyCode() == WXK_DELETE) {
1372 wxLogDebug(wxT("wxKeyEvent"));
1373 }
1374
1375 event.Skip();
1376}
1377
1379{
1381}
1382
1383// PrefsListener implementation
1385{
1387}
wxT("CloseDown"))
int AudacityMessageBox(const TranslatableString &message, const TranslatableString &caption, long style, wxWindow *parent, int x, int y)
END_EVENT_TABLE()
#define ApplyToFilesID
#define MacrosListID
@ ActionColumn
@ ParamsColumn
@ ItemNumberColumn
#define CommandsListID
#define ApplyToProjectID
#define MacrosPaletteTitle
#define ShrinkID
#define ExpandID
#define ManageMacrosTitle
@ SaveButtonID
@ UpButtonID
@ ImportButtonID
@ DownButtonID
@ RestoreButtonID
@ RenameButtonID
@ AddButtonID
@ EditButtonID
@ DefaultsButtonID
@ RemoveButtonID
@ ApplyToProjectButtonID
@ DeleteButtonID
@ InsertButtonID
@ ApplyToFilesButtonID
@ ExportButtonID
int min(int a, int b)
EVT_BUTTON(wxID_NO, DependencyDialog::OnNo) EVT_BUTTON(wxID_YES
EffectDistortion::Params params
Definition: Distortion.cpp:83
const TranslatableString name
Definition: Distortion.cpp:82
EVT_LIST_ITEM_SELECTED(CurvesListID, EditCurvesDialog::OnListSelectionChange) EVT_LIST_ITEM_DESELECTED(CurvesListID
Constructor.
EVT_LIST_ITEM_ACTIVATED(wxID_ANY, SuccessDialog::OnItemActivated) ExportMultipleDialog
#define XXO(s)
Definition: Internat.h:44
#define XO(s)
Definition: Internat.h:31
#define _(s)
Definition: Internat.h:75
#define safenew
Definition: MemoryX.h:10
wxString MacroID
Definition: Menus.h:33
static const auto title
FileConfig * gPrefs
Definition: Prefs.cpp:71
static ProjectFileIORegistry::AttributeWriterEntry entry
@ eIsCreating
Definition: ShuttleGui.h:39
@ eCloseButton
Definition: ShuttleGui.h:607
@ eHelpButton
Definition: ShuttleGui.h:601
static TranslatableStrings names
Definition: TagsEditor.cpp:151
#define S(N)
Definition: ToChars.cpp:64
declares abstract base class Track, TrackList, and iterators over TrackList
TranslatableString Verbatim(wxString str)
Require calls to the one-argument constructor to go through this distinct global function name.
int id
Shows progress in executing commands in MacroCommands.
wxButton * mResize
Provides list of available commands.
virtual void OnApplyToFiles(wxCommandEvent &event)
MacroCommands mMacroCommands
AudacityProject & mProject
wxListCtrl * mMacros
void ApplyMacroToProject(int iMacro, bool bHasGui=true)
virtual void OnCancel(wxCommandEvent &event)
virtual void OnApplyToProject(wxCommandEvent &event)
const MacroCommandsCatalog mCatalog
virtual void OnHelp(wxCommandEvent &event)
void PopulateOrExchange(ShuttleGui &S)
Defines the dialog and does data exchange with it.
virtual ManualPageID GetHelpPageName()
static CommandID MacroIdOfName(const wxString &MacroName)
Wrap wxMessageDialog so that caption IS translatable.
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:89
Wrap wxTextEntryDialog so that caption IS translatable.
static Clipboard & Get()
Definition: Clipboard.cpp:29
virtual bool Flush(bool bCurrentOnly=false) wxOVERRIDE
Definition: FileConfig.cpp:143
virtual void GetPaths(wxArrayString &paths) const
virtual void SetFilterIndex(int filterIndex)
virtual int ShowModal()
Abstract base class used in importing a file.
static void ShowHelp(wxWindow *parent, const FilePath &localFileName, const URLString &remoteURL, bool bModal=false, bool alwaysDefaultBrowser=false)
Definition: HelpSystem.cpp:239
bool empty() const
Definition: Identifier.h:61
static Importer & Get()
Definition: Import.cpp:71
FileNames::FileTypes GetFileTypes(const FileNames::FileType &extraType={})
Definition: Import.cpp:179
static size_t SelectDefaultOpenType(const FileNames::FileTypes &fileTypes)
Definition: Import.cpp:244
Provides a list of configurable commands for use with MacroCommands.
Entries::const_iterator ByCommandId(const CommandID &commandId) const
Entries::const_iterator end() const
Definition: BatchCommands.h:48
bool IsFixed(const wxString &name)
void AddToMacro(const CommandID &command, int before=-1)
wxString GetParams(int index)
void DeleteFromMacro(int index)
bool RenameMacro(const wxString &oldmacro, const wxString &newmacro)
CommandID GetCommand(int index)
static wxArrayString GetNames()
bool DeleteMacro(const wxString &name)
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)
wxString ReadMacro(const wxString &macro, wxWindow *parent=nullptr)
void RestoreMacro(const wxString &name)
void PopulateList()
This clears and updates the contents of mList, the commands for the current macro.
TranslatableString WindowTitle() const
void OnDelete(wxCommandEvent &event)
void OnListSelected(wxListEvent &event)
An item in the macros list has been selected.
void OnShrink(wxCommandEvent &event)
void OnExport(wxCommandEvent &event)
wxButton * mRemove
void PopulateOrExchange(ShuttleGui &S)
Defines the dialog and does data exchange with it.
void OnRestore(wxCommandEvent &event)
Reset a built in macro.
void OnCommandActivated(wxListEvent &event)
AudacityProject & mProject
void OnAdd(wxCommandEvent &event)
void OnUp(wxCommandEvent &event)
void OnCancel(wxCommandEvent &event) override
void OnApplyToProject(wxCommandEvent &event) override
void OnMacroSelected(wxListEvent &event)
An item in the macros list has been selected.
void OnExpand(wxCommandEvent &event)
wxButton * mRestore
wxButton * mImport
void OnSave(wxCommandEvent &event)
void OnApplyToFiles(wxCommandEvent &event) override
void UpdateDisplay(bool bExpanded)
void UpdatePrefs() override
void OnMacrosBeginEdit(wxListEvent &event)
void OnSize(wxSizeEvent &event)
The window has been resized.
wxButton * mRename
wxButton * mExport
void OnMacrosEndEdit(wxListEvent &event)
void OnImport(wxCommandEvent &event)
MacrosWindow(wxWindow *parent, AudacityProject &project, bool bExpanded=true)
Constructor.
void OnDown(wxCommandEvent &event)
void OnEditCommandParams(wxCommandEvent &event)
void OnInsert(wxCommandEvent &event)
void OnRemove(wxCommandEvent &event)
void InsertCommandAt(int item)
void Populate()
Creates the dialog and its contents.
void AddItem(const CommandID &command, wxString const &params)
Add one item into mList.
void OnKeyDown(wxKeyEvent &event)
void OnOK(wxCommandEvent &event)
Send changed values back to Prefs, and update Audacity.
void OnRename(wxCommandEvent &event)
void RebuildMenuBar(AudacityProject &project)
Definition: Menus.cpp:486
static MenuManager & Get(AudacityProject &project)
Definition: Menus.cpp:71
bool Import(const FilePath &fileName, bool addToHistory=true)
static ProjectFileManager & Get(AudacityProject &project)
static ProjectManager & Get(AudacityProject &project)
void ResetProjectToEmpty()
void ZoomAfterImport(Track *pTrack)
static ProjectWindow & Get(AudacityProject &project)
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
Definition: ShuttleGui.h:628
bool empty() const
Definition: Track.cpp:1009
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:486
Holds a msgid for the translation catalog; may also bind format arguments.
An alternative to using wxWindowAccessible, which in wxWidgets 3.1.1 contained GetParent() which was ...
void SetTitle(const TranslatableString &title)
void SetLabel(const TranslatableString &title)
void SetName(const TranslatableString &title)
std::function< void()> Action
Definition: BasicUI.h:24
FILES_API FilePath FindDefaultPath(Operation op)
void DoSelectAll(AudacityProject &project)