audacia/src/BatchProcessDialog.cpp
Paul Licameli 02e620d35f Move functions into new file SelectUtilities.cpp ...
... so that other files do not have link dependency on SelectMenus.cpp

The new file enlarges the big s.c.c. to 24
2019-06-24 23:06:44 -04:00

1243 lines
34 KiB
C++

/**********************************************************************
Audacity: A Digital Audio Editor
ApplyMacroDialog.cpp
Dominic Mazzoni
James Crook
*******************************************************************//*!
\class ApplyMacroDialog
\brief Shows progress in executing commands in MacroCommands.
*//*******************************************************************/
#include "Audacity.h"
#include "BatchProcessDialog.h"
#include <wx/setup.h> // for wxUSE_* macros
#ifdef __WXMSW__
#include <wx/ownerdrw.h>
#endif
#include <wx/defs.h>
#include <wx/checkbox.h>
#include <wx/choice.h>
#include <wx/filedlg.h>
#include <wx/intl.h>
#include <wx/sizer.h>
#include <wx/statbox.h>
#include <wx/stattext.h>
#include <wx/textctrl.h>
#include <wx/listctrl.h>
#include <wx/radiobut.h>
#include <wx/button.h>
#include <wx/imaglist.h>
#include <wx/settings.h>
#include "ShuttleGui.h"
#include "Menus.h"
#include "Prefs.h"
#include "Project.h"
#include "ProjectFileManager.h"
#include "ProjectHistory.h"
#include "ProjectManager.h"
#include "ProjectWindow.h"
#include "SelectUtilities.h"
#include "commands/CommandManager.h"
#include "effects/Effect.h"
#include "../images/Arrow.xpm"
#include "../images/Empty9x16.xpm"
#include "UndoManager.h"
#include "AllThemeResources.h"
#include "FileDialog.h"
#include "FileNames.h"
#include "import/Import.h"
#include "widgets/ErrorDialog.h"
#include "widgets/AudacityMessageBox.h"
#include "widgets/HelpSystem.h"
#if wxUSE_ACCESSIBILITY
#include "widgets/WindowAccessible.h"
#endif
#define MacrosListID 7001
#define CommandsListID 7002
#define ApplyToProjectID 7003
#define ApplyToFilesID 7004
#define ExpandID 7005
#define ShrinkID 7006
BEGIN_EVENT_TABLE(ApplyMacroDialog, wxDialogWrapper)
EVT_BUTTON(ApplyToProjectID, ApplyMacroDialog::OnApplyToProject)
EVT_BUTTON(ApplyToFilesID, ApplyMacroDialog::OnApplyToFiles)
EVT_BUTTON(wxID_CANCEL, ApplyMacroDialog::OnCancel)
EVT_BUTTON(wxID_HELP, ApplyMacroDialog::OnHelp)
END_EVENT_TABLE()
ApplyMacroDialog::ApplyMacroDialog(wxWindow * parent, bool bInherited):
wxDialogWrapper(parent, wxID_ANY, _("Macros Palette"),
wxDefaultPosition, wxDefaultSize,
wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
, mCatalog( GetActiveProject() )
{
//AudacityProject * p = GetActiveProject();
mAbort = false;
mbExpanded = false;
if( bInherited )
return;
SetLabel(_("Macros Palette")); // Provide visual label
SetName(_("Macros Palette")); // Provide audible label
Populate();
}
ApplyMacroDialog::~ApplyMacroDialog()
{
}
void ApplyMacroDialog::Populate()
{
//------------------------- Main section --------------------
ShuttleGui S(this, eIsCreating);
PopulateOrExchange(S);
// ----------------------- End of main section --------------
// Get and validate the currently active macro
mActiveMacro = gPrefs->Read(wxT("/Batch/ActiveMacro"), wxT(""));
// Go populate the macros list.
PopulateMacros();
Layout();
Fit();
wxSize sz = GetSize();
SetSizeHints( sz );
// Size and place window
SetSize(std::min(wxSystemSettings::GetMetric(wxSYS_SCREEN_X) * 3 / 4, sz.GetWidth()),
std::min(wxSystemSettings::GetMetric(wxSYS_SCREEN_Y) * 4 / 5, 400));
Center();
// Set the column size for the macros list.
sz = mMacros->GetClientSize();
mMacros->SetColumnWidth(0, sz.x);
}
/// Defines the dialog and does data exchange with it.
void ApplyMacroDialog::PopulateOrExchange(ShuttleGui &S)
{
/*i18n-hint: A macro is a sequence of commands that can be applied
* to one or more audio files.*/
S.StartStatic(_("Select Macro"), 1);
{
S.SetStyle(wxSUNKEN_BORDER | wxLC_REPORT | wxLC_HRULES | wxLC_VRULES |
wxLC_SINGLE_SEL);
mMacros = S.Id(MacrosListID).Prop(1).AddListControlReportMode();
mMacros->InsertColumn(0, _("Macro"), wxLIST_FORMAT_LEFT);
}
S.EndStatic();
S.StartHorizontalLay(wxEXPAND, 0);
{
S.AddPrompt( _("Apply Macro to:") );
wxButton* btn = S.Id(ApplyToProjectID).AddButton(_("&Project"));
#if wxUSE_ACCESSIBILITY
// so that name can be set on a standard control
btn->SetAccessible(safenew WindowAccessible(btn));
#endif
btn->SetName(_("Apply macro to project"));
btn = S.Id(ApplyToFilesID).AddButton(_("&Files..."));
#if wxUSE_ACCESSIBILITY
// so that name can be set on a standard control
btn->SetAccessible(safenew WindowAccessible(btn));
#endif
btn->SetName(_("Apply macro to files..."));
}
S.EndHorizontalLay();
S.StartHorizontalLay(wxEXPAND, 0);
{
/* i18n-hint: The Expand button makes the dialog bigger, with more in it */
mResize = S.Id(ExpandID).AddButton(_("&Expand"));
S.Prop(1).AddSpace( 10 );
S.AddStandardButtons( eCancelButton | eHelpButton);
}
S.EndHorizontalLay();
}
/// This clears and updates the contents of mMacros, the list of macros.
/// It has cut-and-paste code from PopulateList, and both should call
/// a shared function.
void ApplyMacroDialog::PopulateMacros()
{
auto names = mMacroCommands.GetNames();
int i;
int topItem = mMacros->GetTopItem();
mMacros->DeleteAllItems();
for (i = 0; i < (int)names.size(); i++) {
mMacros->InsertItem(i, names[i]);
}
int item = mMacros->FindItem(-1, mActiveMacro);
bool bFound = item >=0;
if (item == -1) {
item = 0;
mActiveMacro = mMacros->GetItemText(0);
}
// Select the name in the list...this will fire an event.
mMacros->SetItemState(item, wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED,
wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED);
if( 0 <= topItem && topItem < (int)mMacros->GetItemCount())
{
// Workaround for scrolling being windows only.
// Try to scroll back to where we once were...
mMacros->EnsureVisible( (int)mMacros->GetItemCount() -1 );
mMacros->EnsureVisible( topItem );
// And then make sure whatever is selected is still visible...
if( bFound )
mMacros->EnsureVisible( item );
}
}
void ApplyMacroDialog::OnHelp(wxCommandEvent & WXUNUSED(event))
{
wxString page = GetHelpPageName();
HelpSystem::ShowHelp(this, page, true);
}
void ApplyMacroDialog::OnApplyToProject(wxCommandEvent & WXUNUSED(event))
{
long item = mMacros->GetNextItem(-1,
wxLIST_NEXT_ALL,
wxLIST_STATE_SELECTED);
if (item == -1) {
AudacityMessageBox(_("No macro selected"));
return;
}
ApplyMacroToProject( item );
}
CommandID ApplyMacroDialog::MacroIdOfName( const wxString & MacroName )
{
wxString Temp = MacroName;
Temp.Replace(" ","");
Temp = wxString( "Macro_" ) + Temp;
return Temp;
}
// Apply macro, given its ID.
// Does nothing if not found, rather than returning an error.
void ApplyMacroDialog::ApplyMacroToProject( const CommandID & MacroID, bool bHasGui )
{
for( int i=0;i<mMacros->GetItemCount();i++){
wxString name = mMacros->GetItemText(i);
if( MacroIdOfName( name ) == MacroID ){
ApplyMacroToProject( i, bHasGui );
return;
}
}
}
// Apply macro, given its number in the list.
void ApplyMacroDialog::ApplyMacroToProject( int iMacro, bool bHasGui )
{
wxString name = mMacros->GetItemText(iMacro);
if( name.empty() )
return;
#ifdef OPTIONAL_ACTIVITY_WINDOW
wxDialogWrapper activityWin( this, wxID_ANY, GetTitle());
activityWin.SetName(activityWin.GetTitle());
ShuttleGui S(&activityWin, eIsCreating);
S.StartHorizontalLay(wxCENTER, false);
{
S.StartStatic( {}, false); // deliberately not translated (!)
{
S.SetBorder(20);
S.AddFixedText(wxString::Format(_("Applying '%s' to current project"),
name));
}
S.EndStatic();
}
S.EndHorizontalLay();
activityWin.Layout();
activityWin.Fit();
activityWin.CenterOnScreen();
// Avoid overlap with progress.
int x,y;
activityWin.GetPosition( &x, &y );
activityWin.Move(wxMax(0,x-300), 0);
activityWin.Show();
// Without this the newly created dialog may not show completely.
wxYield();
#endif
//Since we intend to keep this dialog open, there is no reason to hide it
//and then show it again.
//if( bHasGui )
// Hide();
gPrefs->Write(wxT("/Batch/ActiveMacro"), name);
gPrefs->Flush();
mMacroCommands.ReadMacro(name);
// The disabler must get deleted before the EndModal() call. Otherwise,
// the menus on OSX will remain disabled.
bool success;
{
#ifdef OPTIONAL_ACTIVITY_WINDOW
wxWindowDisabler wd(&activityWin);
#endif
success = GuardedCall< bool >(
[this]{ return mMacroCommands.ApplyMacro(mCatalog); } );
}
if( !bHasGui )
return;
Show();
Raise();
}
void ApplyMacroDialog::OnApplyToFiles(wxCommandEvent & WXUNUSED(event))
{
long item = mMacros->GetNextItem(-1,
wxLIST_NEXT_ALL,
wxLIST_STATE_SELECTED);
if (item == -1) {
AudacityMessageBox(_("No macro selected"));
return;
}
wxString name = mMacros->GetItemText(item);
gPrefs->Write(wxT("/Batch/ActiveMacro"), name);
gPrefs->Flush();
AudacityProject *project = GetActiveProject();
if (!TrackList::Get( *project ).empty()) {
AudacityMessageBox(_("Please save and close the current project first."));
return;
}
wxString prompt = _("Select file(s) for batch processing...");
FormatList l;
wxString filter;
wxString all;
Importer::Get().GetSupportedImportFormats(&l);
for (const auto &format : l) {
const Format *f = &format;
wxString newfilter = f->formatName + wxT("|");
for (size_t i = 0; i < f->formatExtensions.size(); i++) {
if (!newfilter.Contains(wxT("*.") + f->formatExtensions[i] + wxT(";")))
newfilter += wxT("*.") + f->formatExtensions[i] + wxT(";");
if (!all.Contains(wxT("*.") + f->formatExtensions[i] + wxT(";")))
all += wxT("*.") + f->formatExtensions[i] + wxT(";");
}
newfilter.RemoveLast(1);
filter += newfilter;
filter += wxT("|");
}
all.RemoveLast(1);
filter.RemoveLast(1);
wxString mask = _("All files|*|All supported files|") +
all + wxT("|") +
filter;
wxString type = gPrefs->Read(wxT("/DefaultOpenType"),mask.BeforeFirst(wxT('|')));
// Convert the type to the filter index
int index = mask.First(type + wxT("|"));
if (index == wxNOT_FOUND) {
index = 0;
}
else {
index = mask.Left(index).Freq(wxT('|')) / 2;
if (index < 0) {
index = 0;
}
}
auto path = FileNames::FindDefaultPath(FileNames::Operation::Open);
FileDialogWrapper dlog(this,
prompt,
path,
wxT(""),
mask,
wxFD_OPEN | wxFD_MULTIPLE | wxRESIZE_BORDER);
dlog.SetFilterIndex(index);
if (dlog.ShowModal() != wxID_OK) {
Raise();
return;
}
Raise();
wxArrayString files;
dlog.GetPaths(files);
files.Sort();
wxDialogWrapper activityWin(this, wxID_ANY, GetTitle());
activityWin.SetName(activityWin.GetTitle());
ShuttleGui S(&activityWin, eIsCreating);
wxListCtrl * fileList = NULL;
S.StartVerticalLay(false);
{
S.StartStatic(_("Applying..."), 1);
{
auto imageList = std::make_unique<wxImageList>(9, 16);
imageList->Add(wxIcon(empty9x16_xpm));
imageList->Add(wxIcon(arrow_xpm));
S.SetStyle(wxSUNKEN_BORDER | wxLC_REPORT | wxLC_HRULES | wxLC_VRULES |
wxLC_SINGLE_SEL);
fileList = S.Id(CommandsListID).AddListControlReportMode();
// AssignImageList takes ownership
fileList->AssignImageList(imageList.release(), wxIMAGE_LIST_SMALL);
fileList->InsertColumn(0, _("File"), wxLIST_FORMAT_LEFT);
}
S.EndStatic();
S.StartHorizontalLay(wxCENTER, false);
{
S.Id(wxID_CANCEL).AddButton(_("&Cancel"));
}
S.EndHorizontalLay();
}
S.EndVerticalLay();
int i;
for (i = 0; i < (int)files.size(); i++ ) {
fileList->InsertItem(i, files[i], i == 0);
}
// Set the column size for the files list.
fileList->SetColumnWidth(0, wxLIST_AUTOSIZE);
int width = fileList->GetColumnWidth(0);
wxSize sz = fileList->GetClientSize();
if (width > sz.GetWidth() && width < 500) {
sz.SetWidth(width);
fileList->SetInitialSize(sz);
}
activityWin.Layout();
activityWin.Fit();
activityWin.CenterOnScreen();
// Avoid overlap with progress.
int x,y;
activityWin.GetPosition( &x, &y );
activityWin.Move(wxMax(0,x-300), 0);
activityWin.Show();
// Without this the newly created dialog may not show completely.
wxYield();
// We could avoid hiding, but there are many dialogs on screen,
// and hiding this one temporarily has some advantages.
Hide();
mMacroCommands.ReadMacro(name);
for (i = 0; i < (int)files.size(); i++) {
wxWindowDisabler wd(&activityWin);
if (i > 0) {
//Clear the arrow in previous item.
fileList->SetItemImage(i - 1, 0, 0);
}
fileList->SetItemImage(i, 1, 1);
fileList->EnsureVisible(i);
auto success = GuardedCall< bool >( [&] {
ProjectFileManager::Get( *project ).Import(files[i]);
ProjectWindow::Get( *project ).ZoomAfterImport(nullptr);
SelectUtilities::DoSelectAll(*project);
if (!mMacroCommands.ApplyMacro(mCatalog))
return false;
if (!activityWin.IsShown() || mAbort)
return false;
return true;
} );
if (!success)
break;
ProjectManager::Get( *project ).ResetProjectToEmpty();
}
Show();
Raise();
}
void ApplyMacroDialog::OnCancel(wxCommandEvent & WXUNUSED(event))
{
Hide();
}
/////////////////////////////////////////////////////////////////////
#include <wx/textdlg.h>
#include "BatchCommandDialog.h"
enum {
AddButtonID = 10000,
RemoveButtonID,
ImportButtonID,
ExportButtonID,
DefaultsButtonID,
InsertButtonID,
EditButtonID,
DeleteButtonID,
UpButtonID,
DownButtonID,
RenameButtonID,
// MacrosListID 7005
// CommandsListID, 7002
// Re-Use IDs from ApplyMacroDialog.
ApplyToProjectButtonID = ApplyToProjectID,
ApplyToFilesButtonID = ApplyToFilesID,
};
BEGIN_EVENT_TABLE(MacrosWindow, ApplyMacroDialog)
EVT_LIST_ITEM_SELECTED(MacrosListID, MacrosWindow::OnMacroSelected)
EVT_LIST_ITEM_SELECTED(CommandsListID, MacrosWindow::OnListSelected)
EVT_LIST_BEGIN_LABEL_EDIT(MacrosListID, MacrosWindow::OnMacrosBeginEdit)
EVT_LIST_END_LABEL_EDIT(MacrosListID, MacrosWindow::OnMacrosEndEdit)
EVT_BUTTON(AddButtonID, MacrosWindow::OnAdd)
EVT_BUTTON(RemoveButtonID, MacrosWindow::OnRemove)
EVT_BUTTON(RenameButtonID, MacrosWindow::OnRename)
EVT_BUTTON(ExpandID, MacrosWindow::OnExpand)
EVT_BUTTON(ShrinkID, MacrosWindow::OnShrink)
EVT_SIZE(MacrosWindow::OnSize)
EVT_LIST_ITEM_ACTIVATED(CommandsListID, MacrosWindow::OnCommandActivated)
EVT_BUTTON(InsertButtonID, MacrosWindow::OnInsert)
EVT_BUTTON(EditButtonID, MacrosWindow::OnEditCommandParams)
EVT_BUTTON(DeleteButtonID, MacrosWindow::OnDelete)
EVT_BUTTON(UpButtonID, MacrosWindow::OnUp)
EVT_BUTTON(DownButtonID, MacrosWindow::OnDown)
EVT_BUTTON(DefaultsButtonID, MacrosWindow::OnDefaults)
EVT_BUTTON(wxID_OK, MacrosWindow::OnOK)
EVT_BUTTON(wxID_CANCEL, MacrosWindow::OnCancel)
EVT_KEY_DOWN(MacrosWindow::OnKeyDown)
END_EVENT_TABLE()
enum {
ItemNumberColumn,
ActionColumn,
ParamsColumn,
};
/// Constructor
MacrosWindow::MacrosWindow(wxWindow * parent, bool bExpanded):
ApplyMacroDialog(parent, true)
{
mbExpanded = bExpanded;
wxString Title = mbExpanded ? _("Manage Macros") : _("Macros Palette");
SetLabel( Title ); // Provide visual label
SetName( Title ); // Provide audible label
SetTitle( Title );
mChanged = false;
mSelectedCommand = 0;
if( mbExpanded )
Populate();
else
ApplyMacroDialog::Populate();
}
MacrosWindow::~MacrosWindow()
{
}
/// Creates the dialog and its contents.
void MacrosWindow::Populate()
{
//------------------------- Main section --------------------
ShuttleGui S(this, eIsCreating);
PopulateOrExchange(S);
// ----------------------- End of main section --------------
// Get and validate the currently active macro
mActiveMacro = gPrefs->Read(wxT("/Batch/ActiveMacro"), wxT(""));
// Go populate the macros list.
PopulateMacros();
// We have a bare list. We need to add columns and content.
PopulateList();
// Layout and set minimum size of window
Layout();
Fit();
SetSizeHints(GetSize());
// Size and place window
SetSize(std::min(wxSystemSettings::GetMetric(wxSYS_SCREEN_X) * 3 / 4, 800),
std::min(wxSystemSettings::GetMetric(wxSYS_SCREEN_Y) * 4 / 5, 400));
Center();
// Set the column size for the macros list.
wxSize sz = mMacros->GetClientSize();
mMacros->SetColumnWidth(0, sz.x);
// Size columns properly
FitColumns();
}
/// Defines the dialog and does data exchange with it.
void MacrosWindow::PopulateOrExchange(ShuttleGui & S)
{
S.StartHorizontalLay(wxEXPAND, 1);
{
S.StartStatic(_("Select Macro"),0);
{
S.StartHorizontalLay(wxEXPAND,1);
{
S.SetStyle(wxSUNKEN_BORDER | wxLC_REPORT | wxLC_HRULES | wxLC_SINGLE_SEL |
wxLC_EDIT_LABELS);
mMacros = S.Id(MacrosListID).Prop(1).AddListControlReportMode();
// i18n-hint: This is the heading for a column in the edit macros dialog
mMacros->InsertColumn(0, _("Macro"), wxLIST_FORMAT_LEFT);
S.StartVerticalLay(wxALIGN_TOP, 0);
{
S.Id(AddButtonID).AddButton(_("&New"));
mRemove = S.Id(RemoveButtonID).AddButton(_("Remo&ve"));
mRename = S.Id(RenameButtonID).AddButton(_("&Rename..."));
// Not yet ready for prime time.
#if 0
S.Id(ImportButtonID).AddButton(_("I&mport..."))->Enable( false);
S.Id(ExportButtonID).AddButton(_("E&xport..."))->Enable( false);
#endif
}
S.EndVerticalLay();
}
S.EndHorizontalLay();
}
S.EndStatic();
S.StartStatic(_("Edit Steps"), true);
{
S.StartHorizontalLay(wxEXPAND,1);
{
S.SetStyle(wxSUNKEN_BORDER | wxLC_REPORT | wxLC_HRULES | wxLC_VRULES |
wxLC_SINGLE_SEL);
mList = S.Id(CommandsListID).AddListControlReportMode();
//A dummy first column, which is then deleted, is a workaround - under Windows the first column
//can't be right aligned.
mList->InsertColumn(0, wxT(""), wxLIST_FORMAT_LEFT);
/* i18n-hint: This is the number of the command in the list */
mList->InsertColumn(ItemNumberColumn + 1, _("Num"), wxLIST_FORMAT_RIGHT);
mList->InsertColumn(ActionColumn + 1, _("Command "), wxLIST_FORMAT_RIGHT);
mList->InsertColumn(ParamsColumn + 1, _("Parameters"), wxLIST_FORMAT_LEFT);
mList->DeleteColumn(0);
S.StartVerticalLay(wxALIGN_TOP, 0);
{
S.Id(InsertButtonID).AddButton(_("&Insert"), wxALIGN_LEFT);
S.Id(EditButtonID).AddButton(_("&Edit..."), wxALIGN_LEFT);
S.Id(DeleteButtonID).AddButton(_("De&lete"), wxALIGN_LEFT);
S.Id(UpButtonID).AddButton(_("Move &Up"), wxALIGN_LEFT);
S.Id(DownButtonID).AddButton(_("Move &Down"), wxALIGN_LEFT);
mDefaults = S.Id(DefaultsButtonID).AddButton(_("De&faults"));
}
S.EndVerticalLay();
}
S.EndHorizontalLay();
}
S.EndStatic();
}
S.EndHorizontalLay();
S.StartHorizontalLay(wxEXPAND, 0);
{
/* i18n-hint: The Shrink button makes the dialog smaller, with less in it */
mResize = S.Id(ShrinkID).AddButton(_("Shrin&k"));
// Using variable text just to get the positioning options.
S.Prop(0).AddVariableText( _("Apply Macro to:"), false, wxALL | wxALIGN_CENTRE_VERTICAL );
wxButton* btn = S.Id(ApplyToProjectID).AddButton(_("&Project"));
#if wxUSE_ACCESSIBILITY
// so that name can be set on a standard control
btn->SetAccessible(safenew WindowAccessible(btn));
#endif
btn->SetName(_("Apply macro to project"));
btn = S.Id(ApplyToFilesID).AddButton(_("&Files..."));
#if wxUSE_ACCESSIBILITY
// so that name can be set on a standard control
btn->SetAccessible(safenew WindowAccessible(btn));
#endif
btn->SetName(_("Apply macro to files..."));
S.Prop(1).AddSpace( 10 );
S.AddStandardButtons( eOkButton | eCancelButton | eHelpButton);
}
S.EndHorizontalLay();
return;
}
/// This clears and updates the contents of mList, the commands for the current macro.
void MacrosWindow::PopulateList()
{
int topItem = mList->GetTopItem();
mList->DeleteAllItems();
for (int i = 0; i < mMacroCommands.GetCount(); i++) {
AddItem(mMacroCommands.GetCommand(i),
mMacroCommands.GetParams(i));
}
/*i18n-hint: This is the last item in a list.*/
AddItem(_("- END -"), wxT(""));
// Select the name in the list...this will fire an event.
if (mSelectedCommand >= (int)mList->GetItemCount()) {
mSelectedCommand = 0;
}
mList->SetItemState(mSelectedCommand,
wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED,
wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED);
if( 0 <= topItem && topItem < (int)mList->GetItemCount())
{
// Workaround for scrolling being windows only.
// Try to scroll back to where we once were...
mList->EnsureVisible( (int)mList->GetItemCount() -1 );
mList->EnsureVisible( topItem );
// And then make sure whatever is selected is still visible...
if (mSelectedCommand >= 0) {
mList->EnsureVisible( mSelectedCommand );
}
}
}
/// Add one item into mList
void MacrosWindow::AddItem(const CommandID &Action, const wxString &Params)
{
auto entry = mCatalog.ByCommandId(Action);
auto friendlyName = entry != mCatalog.end()
? entry->name.Translated()
:
// uh oh, using GET to expose an internal name to the user!
// in default of any better friendly name
Action.GET();
int i = mList->GetItemCount();
mList->InsertItem(i, wxString::Format(wxT(" %02i"), i + 1));
mList->SetItem(i, ActionColumn, friendlyName );
mList->SetItem(i, ParamsColumn, Params );
}
void MacrosWindow::UpdateMenus()
{
// OK even on mac, as dialog is modal.
auto p = GetActiveProject();
MenuManager::Get(*p).RebuildMenuBar(*p);
}
void MacrosWindow::UpdateDisplay( bool bExpanded )
{
if( bExpanded == mbExpanded )
return;
if( !SaveChanges() )
return;
mbExpanded = bExpanded;
DestroyChildren();
SetSizer( nullptr );
mChanged = false;
mSelectedCommand = 0;
SetMinSize( wxSize( 200,200 ));
// Get and set position for optical stability.
// Expanded and shrunk dialogs 'stay where they were'.
// That's OK , and what we want, even if we exapnd off-screen.
// We won't shrink to being off-screen, since the shrink button
// was clicked, so must have been on screen.
wxPoint p = GetPosition( );
if( mbExpanded )
Populate();
else
ApplyMacroDialog::Populate();
SetPosition( p );
mResize->SetFocus();
wxString Title = mbExpanded ? _("Manage Macros") : _("Macros Palette");
SetLabel( Title ); // Provide visual label
SetName( Title ); // Provide audible label
SetTitle( Title );
}
void MacrosWindow::OnExpand(wxCommandEvent &WXUNUSED(event))
{ UpdateDisplay( true );}
void MacrosWindow::OnShrink(wxCommandEvent &WXUNUSED(event))
{ UpdateDisplay( false );}
bool MacrosWindow::ChangeOK()
{
if (mChanged) {
wxString title;
wxString msg;
int id;
title.Printf(_("%s changed"), mActiveMacro);
msg = _("Do you want to save the changes?");
id = AudacityMessageBox(msg, title, wxYES_NO | wxCANCEL);
if (id == wxCANCEL) {
return false;
}
if (id == wxYES) {
if (!mMacroCommands.WriteMacro(mActiveMacro)) {
return false;
}
}
mChanged = false;
}
return true;
}
/// An item in the macros list has been selected.
void MacrosWindow::OnMacroSelected(wxListEvent & event)
{
if (!ChangeOK()) {
event.Veto();
return;
}
int item = event.GetIndex();
mActiveMacro = mMacros->GetItemText(item);
mMacroCommands.ReadMacro(mActiveMacro);
if( !mbExpanded )
return;
if (mMacroCommands.IsFixed(mActiveMacro)) {
mRemove->Disable();
mRename->Disable();
mDefaults->Enable();
}
else {
mRemove->Enable();
mRename->Enable();
mDefaults->Disable();
}
PopulateList();
}
/// An item in the macros list has been selected.
void MacrosWindow::OnListSelected(wxListEvent & WXUNUSED(event))
{
FitColumns();
}
/// The window has been resized.
void MacrosWindow::OnSize(wxSizeEvent & WXUNUSED(event))
{
// Refrsh the layout and re-fit the columns.
Layout();
if( !mbExpanded )
return;
FitColumns();
}
void MacrosWindow::FitColumns()
{
#if defined(__WXMAC__)
// wxMac uses a hard coded width of 150 when wxLIST_AUTOSIZE_USEHEADER
// is specified, so we calculate the width ourselves. This method may
// work equally well on other platforms.
for (size_t c = 0; c < mList->GetColumnCount(); c++) {
wxListItem info;
int width;
mList->SetColumnWidth(c, wxLIST_AUTOSIZE);
info.Clear();
info.SetId(c);
info.SetMask(wxLIST_MASK_TEXT | wxLIST_MASK_WIDTH);
mList->GetColumn(c, info);
mList->GetTextExtent(info.GetText(), &width, NULL);
width += 2 * 4; // 2 * kItemPadding - see listctrl_mac.cpp
width += 16; // kIconWidth - see listctrl_mac.cpp
mList->SetColumnWidth(c, wxMax(width, mList->GetColumnWidth(c)));
}
// Looks strange, but it forces the horizontal scrollbar to get
// drawn. If not done, strange column sizing can occur if the
// user attempts to resize the columns.
mList->SetClientSize(mList->GetClientSize());
#else
mList->SetColumnWidth(0, wxLIST_AUTOSIZE_USEHEADER);
mList->SetColumnWidth(1, wxLIST_AUTOSIZE_USEHEADER);
mList->SetColumnWidth(2, wxLIST_AUTOSIZE);
#endif
int bestfit = mList->GetColumnWidth(2);
int clientsize = mList->GetClientSize().GetWidth();
int col0 = mList->GetColumnWidth(0);
int col1 = mList->GetColumnWidth(1);
bestfit = (bestfit > clientsize-col0-col1)? bestfit : clientsize-col0-col1;
mList->SetColumnWidth(2, bestfit);
}
///
void MacrosWindow::OnMacrosBeginEdit(wxListEvent &event)
{
int itemNo = event.GetIndex();
wxString macro = mMacros->GetItemText(itemNo);
if (mMacroCommands.IsFixed(mActiveMacro)) {
wxBell();
event.Veto();
}
}
///
void MacrosWindow::OnMacrosEndEdit(wxListEvent &event)
{
if (event.IsEditCancelled()) {
return;
}
wxString newname = event.GetLabel();
mMacroCommands.RenameMacro(mActiveMacro, newname);
mActiveMacro = newname;
PopulateMacros();
}
///
void MacrosWindow::OnAdd(wxCommandEvent & WXUNUSED(event))
{
while (true) {
AudacityTextEntryDialog d(this,
_("Enter name of new macro"),
_("Name of new macro"));
d.SetName(d.GetTitle());
wxString name;
if (d.ShowModal() == wxID_CANCEL) {
Raise();
return;
}
Raise();
name = d.GetValue().Strip(wxString::both);
if (name.length() == 0) {
AudacityMessageBox(_("Name must not be blank"),
GetTitle(),
wxOK | wxICON_ERROR,
this);
continue;
}
if (name.Contains(wxFILE_SEP_PATH) ||
name.Contains(wxFILE_SEP_PATH_UNIX)) {
/*i18n-hint: The %c will be replaced with 'forbidden characters', like '/' and '\'.*/
AudacityMessageBox(wxString::Format(_("Names may not contain '%c' and '%c'"),
wxFILE_SEP_PATH, wxFILE_SEP_PATH_UNIX),
GetTitle(),
wxOK | wxICON_ERROR,
this);
continue;
}
mMacroCommands.AddMacro(name);
mActiveMacro = name;
PopulateMacros();
UpdateMenus();
break;
}
}
///
void MacrosWindow::OnRemove(wxCommandEvent & WXUNUSED(event))
{
long item = mMacros->GetNextItem(-1,
wxLIST_NEXT_ALL,
wxLIST_STATE_SELECTED);
if (item == -1) {
return;
}
wxString name = mMacros->GetItemText(item);
AudacityMessageDialog m(this,
/*i18n-hint: %s will be replaced by the name of a file.*/
wxString::Format(_("Are you sure you want to delete %s?"), name),
GetTitle(),
wxYES_NO | wxICON_QUESTION);
if (m.ShowModal() == wxID_NO) {
Raise();
return;
}
Raise();
mMacroCommands.DeleteMacro(name);
item++;
if (item >= (mMacros->GetItemCount() - 1) && item >= 0) {
item--;
}
mActiveMacro = mMacros->GetItemText(item);
PopulateMacros();
UpdateMenus();
}
///
void MacrosWindow::OnRename(wxCommandEvent & WXUNUSED(event))
{
long item = mMacros->GetNextItem(-1,
wxLIST_NEXT_ALL,
wxLIST_STATE_SELECTED);
if (item == -1) {
return;
}
mMacros->EditLabel(item);
UpdateMenus();
}
/// An item in the list has been selected.
/// Bring up a dialog to allow its parameters to be edited.
void MacrosWindow::OnCommandActivated(wxListEvent & WXUNUSED(event))
{
wxCommandEvent dummy;
OnEditCommandParams( dummy );
}
///
void MacrosWindow::OnInsert(wxCommandEvent & WXUNUSED(event))
{
long item = mList->GetNextItem(-1,
wxLIST_NEXT_ALL,
wxLIST_STATE_SELECTED);
if (item == -1) {
item = mList->GetItemCount()-1;
}
InsertCommandAt( item );
}
void MacrosWindow::InsertCommandAt(int item)
{
if (item == -1) {
return;
}
MacroCommandDialog d(this, wxID_ANY);
if (!d.ShowModal()) {
Raise();
return;
}
Raise();
if(!d.mSelectedCommand.empty())
{
mMacroCommands.AddToMacro(d.mSelectedCommand,
d.mSelectedParameters,
item);
mChanged = true;
mSelectedCommand = item + 1;
PopulateList();
}
}
void MacrosWindow::OnEditCommandParams(wxCommandEvent & WXUNUSED(event))
{
int item = mList->GetNextItem( -1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
// LAST command in list is END.
// If nothing selected, add at END.
// If END selected, add at END.
// When adding at end we use InsertCommandAt, so that a new command
// can be chosen.
int lastItem = mList->GetItemCount()-1;
if( (item<0) || (item+1) == mList->GetItemCount() )
{
InsertCommandAt( lastItem );
return;
}
// Just edit the parameters, and not the command.
auto command = mMacroCommands.GetCommand(item);
wxString params = mMacroCommands.GetParams(item);
params = MacroCommands::PromptForParamsFor(command, params, this).Trim();
Raise();
mMacroCommands.DeleteFromMacro(item);
mMacroCommands.AddToMacro(command,
params,
item);
mChanged = true;
mSelectedCommand = item;
PopulateList();
}
///
void MacrosWindow::OnDelete(wxCommandEvent & WXUNUSED(event))
{
long item = mList->GetNextItem(-1,
wxLIST_NEXT_ALL,
wxLIST_STATE_SELECTED);
if (item == -1 || item + 1 == mList->GetItemCount()) {
return;
}
mMacroCommands.DeleteFromMacro(item);
mChanged = true;
if (item >= (mList->GetItemCount() - 2) && item >= 0) {
item--;
}
mSelectedCommand = item;
PopulateList();
}
///
void MacrosWindow::OnUp(wxCommandEvent & WXUNUSED(event))
{
long item = mList->GetNextItem(-1,
wxLIST_NEXT_ALL,
wxLIST_STATE_SELECTED);
if (item == -1 || item == 0 || item + 1 == mList->GetItemCount()) {
return;
}
mMacroCommands.AddToMacro(mMacroCommands.GetCommand(item),
mMacroCommands.GetParams(item),
item - 1);
mMacroCommands.DeleteFromMacro(item + 1);
mChanged = true;
mSelectedCommand = item - 1;
PopulateList();
}
///
void MacrosWindow::OnDown(wxCommandEvent & WXUNUSED(event))
{
long item = mList->GetNextItem(-1,
wxLIST_NEXT_ALL,
wxLIST_STATE_SELECTED);
if (item == -1 || item + 2 >= mList->GetItemCount()) {
return;
}
mMacroCommands.AddToMacro(mMacroCommands.GetCommand(item),
mMacroCommands.GetParams(item),
item + 2);
mMacroCommands.DeleteFromMacro(item);
mChanged = true;
mSelectedCommand = item + 1;
PopulateList();
}
void MacrosWindow::OnApplyToProject(wxCommandEvent & event)
{
if( !SaveChanges() )
return;
ApplyMacroDialog::OnApplyToProject( event );
}
void MacrosWindow::OnApplyToFiles(wxCommandEvent & event)
{
if( !SaveChanges() )
return;
ApplyMacroDialog::OnApplyToFiles( event );
}
/// Select the empty Command macro.
void MacrosWindow::OnDefaults(wxCommandEvent & WXUNUSED(event))
{
mMacroCommands.RestoreMacro(mActiveMacro);
mChanged = true;
PopulateList();
}
bool MacrosWindow::SaveChanges(){
gPrefs->Write(wxT("/Batch/ActiveMacro"), mActiveMacro);
gPrefs->Flush();
if (mChanged) {
if (!mMacroCommands.WriteMacro(mActiveMacro)) {
return false;
}
}
mChanged = false;
return true;
}
/// Send changed values back to Prefs, and update Audacity.
void MacrosWindow::OnOK(wxCommandEvent & WXUNUSED(event))
{
if( !SaveChanges() )
return;
Hide();
//EndModal(true);
}
///
void MacrosWindow::OnCancel(wxCommandEvent &WXUNUSED(event))
{
if (!ChangeOK()) {
return;
}
Hide();
}
///
void MacrosWindow::OnKeyDown(wxKeyEvent &event)
{
if (event.GetKeyCode() == WXK_DELETE) {
wxLogDebug(wxT("wxKeyEvent"));
}
event.Skip();
}