diff --git a/src/Menus.cpp b/src/Menus.cpp index aa9e5bb98..fa413cd43 100644 --- a/src/Menus.cpp +++ b/src/Menus.cpp @@ -68,13 +68,7 @@ menu items. #include "Dependencies.h" #include "float_cast.h" #include "LabelTrack.h" -#include "commands/CommandManager.h" -#ifdef USE_MIDI -#include "import/ImportMIDI.h" -#endif // USE_MIDI #include "import/ImportRaw.h" -#include "export/Export.h" -#include "export/ExportMultiple.h" #include "prefs/PrefsDialog.h" #include "prefs/PlaybackPrefs.h" #include "ShuttleGui.h" @@ -87,7 +81,6 @@ menu items. #include "ModuleManager.h" #include "PluginManager.h" #include "Prefs.h" -#include "Printing.h" #ifdef USE_MIDI #include "NoteTrack.h" #endif // USE_MIDI @@ -100,7 +93,6 @@ menu items. #include "ondemand/ODManager.h" #include "BatchProcessDialog.h" -#include "BatchCommands.h" #include "prefs/BatchPrefs.h" #include "toolbars/ToolManager.h" @@ -128,7 +120,6 @@ menu items. #include "DeviceManager.h" #include "UndoManager.h" -#include "WaveTrack.h" #if defined(EXPERIMENTAL_CRASH_REPORT) #include @@ -147,7 +138,6 @@ menu items. #include "widgets/Meter.h" #include "widgets/ErrorDialog.h" #include "./commands/AudacityCommand.h" -#include "commands/CommandContext.h" static MenuTable::BaseItemPtrs PopulateMacrosMenu( CommandFlag flags ); @@ -232,11 +222,8 @@ enum { POST_TIMER_RECORD_SHUTDOWN }; -#include "commands/CommandContext.h" #include "commands/ScreenshotCommand.h" -#include "BatchCommands.h" - // // Effects menu arrays @@ -541,8 +528,9 @@ static CommandHandlerObject &findMenuCommandHandler(AudacityProject &project) static_cast(& MenuCommandHandler :: X) #define XXO(X) _(X), wxString{X}.Contains("...") -namespace { MenuTable::BaseItemPtr FileMenu( AudacityProject& ); + +namespace { MenuTable::BaseItemPtr EditMenu( AudacityProject& ); MenuTable::BaseItemPtr SelectMenu( AudacityProject& ); MenuTable::BaseItemPtr ToolbarsMenu( AudacityProject& ); @@ -617,151 +605,6 @@ static const auto menuTree = MenuTable::Items( namespace { -MenuTable::BaseItemPtr FileMenu( AudacityProject& ) -{ - using namespace MenuTable; - - return Menu( _("&File"), - /*i18n-hint: "New" is an action (verb) to create a NEW project*/ - Command( wxT("New"), XXO("&New"), FN(OnNew), - AudioIONotBusyFlag, wxT("Ctrl+N") ), - - /*i18n-hint: (verb)*/ - Command( wxT("Open"), XXO("&Open..."), FN(OnOpen), - AudioIONotBusyFlag, wxT("Ctrl+O") ), - -#ifdef EXPERIMENTAL_RESET - // Empty the current project and forget its name and path. DANGEROUS - // It's just for developers. - // Do not translate this menu item (no XXO). - // It MUST not be shown to regular users. - Command( wxT("Reset"), XXO("&Dangerous Reset..."), FN(OnProjectReset), - AudioIONotBusyFlag ), -#endif - - ///////////////////////////////////////////////////////////////////////////// - - Menu( -#ifdef __WXMAC__ - /* i18n-hint: This is the name of the menu item on Mac OS X only */ - _("Open Recent") -#else - /* i18n-hint: This is the name of the menu item on Windows and Linux */ - _("Recent &Files") -#endif - , - Special( [](AudacityProject &, wxMenu &theMenu){ - // Recent Files and Recent Projects menus - wxGetApp().GetRecentFiles()->UseMenu( &theMenu ); - wxGetApp().GetRecentFiles()->AddFilesToMenu( &theMenu ); - - wxWeakRef recentFilesMenu{ &theMenu }; - wxTheApp->CallAfter( [=] { - // Bug 143 workaround. - // The bug is in wxWidgets. For a menu that has scrollers, - // the scrollers have an ID of 0 (not wxID_NONE which is -3). - // Therefore wxWidgets attempts to find a help string. See - // wxFrameBase::ShowMenuHelp(int menuId) - // It finds a bogus automatic help string of "Recent &Files" - // from that submenu. - // So we set the help string for command with Id 0 to empty. - if ( recentFilesMenu ) - recentFilesMenu->GetParent()->SetHelpString( 0, "" ); - } ); - } ) - ), - - ///////////////////////////////////////////////////////////////////////////// - - Command( wxT("Close"), XXO("&Close"), FN(OnClose), - AudioIONotBusyFlag, wxT("Ctrl+W") ), - - Separator(), - - Menu( _("&Save Project"), - Command( wxT("Save"), XXO("&Save Project"), FN(OnSave), - AudioIONotBusyFlag | UnsavedChangesFlag, wxT("Ctrl+S") ), - Command( wxT("SaveAs"), XXO("Save Project &As..."), FN(OnSaveAs), - AudioIONotBusyFlag ), - // TODO: The next two items should be disabled if project is empty - Command( wxT("SaveCopy"), XXO("Save Lossless Copy of Project..."), - FN(OnSaveCopy), AudioIONotBusyFlag ) -#ifdef USE_LIBVORBIS - , - Command( wxT("SaveCompressed"), - XXO("&Save Compressed Copy of Project..."), - FN(OnSaveCompressed), AudioIONotBusyFlag ) -#endif - ), - - Separator(), - - Menu( _("&Export"), - // Enable Export audio commands only when there are audio tracks. - Command( wxT("ExportMp3"), XXO("Export as MP&3"), FN(OnExportMp3), - AudioIONotBusyFlag | WaveTracksExistFlag ), - - Command( wxT("ExportWav"), XXO("Export as &WAV"), FN(OnExportWav), - AudioIONotBusyFlag | WaveTracksExistFlag ), - - Command( wxT("ExportOgg"), XXO("Export as &OGG"), FN(OnExportOgg), - AudioIONotBusyFlag | WaveTracksExistFlag ), - - Command( wxT("Export"), XXO("&Export Audio..."), FN(OnExportAudio), - AudioIONotBusyFlag | WaveTracksExistFlag, wxT("Ctrl+Shift+E") ), - - // Enable Export Selection commands only when there's a selection. - Command( wxT("ExportSel"), XXO("Expo&rt Selected Audio..."), - FN(OnExportSelection), - AudioIONotBusyFlag | TimeSelectedFlag | WaveTracksSelectedFlag ), - - Command( wxT("ExportLabels"), XXO("Export &Labels..."), - FN(OnExportLabels), - AudioIONotBusyFlag | LabelTracksExistFlag ), - // Enable Export audio commands only when there are audio tracks. - Command( wxT("ExportMultiple"), XXO("Export &Multiple..."), - FN(OnExportMultiple), - AudioIONotBusyFlag | WaveTracksExistFlag, wxT("Ctrl+Shift+L") ) -#if defined(USE_MIDI) - , - Command( wxT("ExportMIDI"), XXO("Export MI&DI..."), FN(OnExportMIDI), - AudioIONotBusyFlag | NoteTracksExistFlag ) -#endif - ), - - Menu( _("&Import"), - Command( wxT("ImportAudio"), XXO("&Audio..."), FN(OnImport), - AudioIONotBusyFlag, wxT("Ctrl+Shift+I") ), - Command( wxT("ImportLabels"), XXO("&Labels..."), FN(OnImportLabels), - AudioIONotBusyFlag ), - #ifdef USE_MIDI - Command( wxT("ImportMIDI"), XXO("&MIDI..."), FN(OnImportMIDI), - AudioIONotBusyFlag ), - #endif // USE_MIDI - Command( wxT("ImportRaw"), XXO("&Raw Data..."), FN(OnImportRaw), - AudioIONotBusyFlag ) - ), - - Separator(), - - ///////////////////////////////////////////////////////////////////////////// - - Command( wxT("PageSetup"), XXO("Pa&ge Setup..."), FN(OnPageSetup), - AudioIONotBusyFlag | TracksExistFlag ), - /* i18n-hint: (verb) It's item on a menu. */ - Command( wxT("Print"), XXO("&Print..."), FN(OnPrint), - AudioIONotBusyFlag | TracksExistFlag ), - - Separator(), - - // On the Mac, the Exit item doesn't actually go here...wxMac will pull it out - // and put it in the Audacity menu for us based on its ID. - /* i18n-hint: (verb) It's item on a menu. */ - Command( wxT("Exit"), XXO("E&xit"), FN(OnExit), - AlwaysEnabledFlag, wxT("Ctrl+Q") ) - ); -} - MenuTable::BaseItemPtr LabelEditMenus( AudacityProject &project ) { using namespace MenuTable; @@ -5567,333 +5410,12 @@ void MenuCommandHandler::OnAudacityCommand(const CommandContext & ctx) // File Menu // -void MenuCommandHandler::OnNew(const CommandContext &WXUNUSED(context) ) -{ - CreateNewAudacityProject(); -} - -void MenuCommandHandler::OnOpen(const CommandContext &context) -{ - auto &project = context.project; - AudacityProject::OpenFiles(&project); -} - -// JKC: This is like OnClose, except it emptys the project in place, -// rather than createing a new empty project (with new toolbars etc). -// It does not test for unsaved changes. -// It is not in the menus by default. Its main purpose is/was for -// developers checking functionality of ResetProjectToEmpty(). -void MenuCommandHandler::OnProjectReset(const CommandContext &context) -{ - auto &project = context.project; - project.ResetProjectToEmpty(); -} - -void MenuCommandHandler::OnClose(const CommandContext &context) -{ - auto &project = context.project; - project.SetMenuClose(true); - project.Close(); -} - -void MenuCommandHandler::OnSave(const CommandContext &context) -{ - auto &project = context.project; - project.Save(); -} - -void MenuCommandHandler::OnSaveAs(const CommandContext &context) -{ - auto &project = context.project; - project.SaveAs(); -} - -void MenuCommandHandler::OnSaveCopy(const CommandContext &context) -{ - auto &project = context.project; - project.SaveAs(true, true); -} - -#ifdef USE_LIBVORBIS -void MenuCommandHandler::OnSaveCompressed(const CommandContext &context) -{ - auto &project = context.project; - project.SaveAs(true); -} -#endif - void MenuCommandHandler::OnCheckDependencies(const CommandContext &context) { auto &project = context.project; ::ShowDependencyDialogIfNeeded(&project, false); } -void MenuCommandHandler::OnExit(const CommandContext &WXUNUSED(context) ) -{ - QuitAudacity(); -} - -void MenuCommandHandler::OnExportLabels(const CommandContext &context) -{ - auto &project = context.project; - auto tracks = project.GetTracks(); - - /* i18n-hint: filename containing exported text from label tracks */ - wxString fName = _("labels.txt"); - auto trackRange = tracks->Any(); - auto numLabelTracks = trackRange.size(); - - if (numLabelTracks == 0) { - AudacityMessageBox(_("There are no label tracks to export.")); - return; - } - else - fName = (*trackRange.rbegin())->GetName(); - - fName = FileNames::SelectFile(FileNames::Operation::Export, - _("Export Labels As:"), - wxEmptyString, - fName, - wxT("txt"), - wxT("*.txt"), - wxFD_SAVE | wxFD_OVERWRITE_PROMPT | wxRESIZE_BORDER, - &project); - - if (fName == wxT("")) - return; - - // Move existing files out of the way. Otherwise wxTextFile will - // append to (rather than replace) the current file. - - if (wxFileExists(fName)) { -#ifdef __WXGTK__ - wxString safetyFileName = fName + wxT("~"); -#else - wxString safetyFileName = fName + wxT(".bak"); -#endif - - if (wxFileExists(safetyFileName)) - wxRemoveFile(safetyFileName); - - wxRename(fName, safetyFileName); - } - - wxTextFile f(fName); - f.Create(); - f.Open(); - if (!f.IsOpened()) { - AudacityMessageBox( wxString::Format( - _("Couldn't write to file: %s"), fName ) ); - return; - } - - for (auto lt : trackRange) - lt->Export(f); - - f.Write(); - f.Close(); -} - - -#ifdef USE_MIDI -void MenuCommandHandler::OnExportMIDI(const CommandContext &context) -{ - auto &project = context.project; - auto tracks = project.GetTracks(); - - // Make sure that there is - // exactly one NoteTrack selected. - const auto range = tracks->Selected< const NoteTrack >(); - const auto numNoteTracksSelected = range.size(); - - if(numNoteTracksSelected > 1) { - AudacityMessageBox(_( - "Please select only one Note Track at a time.")); - return; - } - else if(numNoteTracksSelected < 1) { - AudacityMessageBox(_( - "Please select a Note Track.")); - return; - } - - wxASSERT(numNoteTracksSelected); - if (!numNoteTracksSelected) - return; - - const auto nt = *range.begin(); - - while(true) { - - wxString fName = wxT(""); - - fName = FileNames::SelectFile(FileNames::Operation::Export, - _("Export MIDI As:"), - wxEmptyString, - fName, - wxT(".mid|.gro"), - _("MIDI file (*.mid)|*.mid|Allegro file (*.gro)|*.gro"), - wxFD_SAVE | wxFD_OVERWRITE_PROMPT | wxRESIZE_BORDER, - &project); - - if (fName == wxT("")) - return; - - if(!fName.Contains(wxT("."))) { - fName = fName + wxT(".mid"); - } - - // Move existing files out of the way. Otherwise wxTextFile will - // append to (rather than replace) the current file. - - if (wxFileExists(fName)) { -#ifdef __WXGTK__ - wxString safetyFileName = fName + wxT("~"); -#else - wxString safetyFileName = fName + wxT(".bak"); -#endif - - if (wxFileExists(safetyFileName)) - wxRemoveFile(safetyFileName); - - wxRename(fName, safetyFileName); - } - - if(fName.EndsWith(wxT(".mid")) || fName.EndsWith(wxT(".midi"))) { - nt->ExportMIDI(fName); - } else if(fName.EndsWith(wxT(".gro"))) { - nt->ExportAllegro(fName); - } else { - wxString msg = _("You have selected a filename with an unrecognized file extension.\nDo you want to continue?"); - wxString title = _("Export MIDI"); - int id = AudacityMessageBox(msg, title, wxYES_NO); - if (id == wxNO) { - continue; - } else if (id == wxYES) { - nt->ExportMIDI(fName); - } - } - break; - } -} -#endif // USE_MIDI - - -void MenuCommandHandler::DoExport -(AudacityProject &project, const wxString & Format ) -{ - auto tracks = project.GetTracks(); - - Exporter e; - - wxGetApp().SetMissingAliasedFileWarningShouldShow(true); - double t0 = 0.0; - double t1 = tracks->GetEndTime(); - - // Prompt for file name and/or extension? - bool bPromptingRequired = - (project.mBatchMode == 0) || project.GetFileName().IsEmpty() || - Format.IsEmpty(); - wxString filename; - - if (!bPromptingRequired) { - - // We're in batch mode, and we have an mFileName and Format. - wxString extension = "."; - extension += Format; - extension.MakeLower(); - - filename = MacroCommands::BuildCleanFileName(project.GetFileName(), extension); - - // Bug 1854, No warning of file overwrite (when export is called from Macros). - int counter = 0; - bPromptingRequired = wxFileExists(filename); - - // We'll try alternative names to avoid overwriting. - while ( bPromptingRequired && counter < 100 ) { - counter++; - wxString number; - number.Printf("%03i", counter); - // So now the name has a number in it too. - filename = - MacroCommands::BuildCleanFileName(project.GetFileName(), number + extension); - bPromptingRequired = wxFileExists(filename); - } - // If we've run out of alternative names, we will fall back to prompting - even if in a macro. - } - - - if (bPromptingRequired) - { - // Do export with prompting. - e.SetDefaultFormat(Format); - e.Process(&project, false, t0, t1); - } - else - { - wxGetApp().AddFileToHistory(filename); - // We're in batch mode, the file does not exist already. - // We really can proceed without prompting. - int nChannels = MacroCommands::IsMono() ? 1 : 2; - e.Process( - &project, // AudacityProject - nChannels, // numChannels, - Format, // type, - filename, // filename, - false, // selectedOnly, - t0, // t0 - t1 // t1 - ); - } - -} - -void MenuCommandHandler::OnExportAudio(const CommandContext &context) -{ - auto &project = context.project; - DoExport(project, ""); -} - -void MenuCommandHandler::OnExportMp3(const CommandContext &context) -{ - auto &project = context.project; - DoExport(project, "MP3"); -} - -void MenuCommandHandler::OnExportWav(const CommandContext &context) -{ - auto &project = context.project; - DoExport(project, "WAV"); -} - -void MenuCommandHandler::OnExportOgg(const CommandContext &context) -{ - auto &project = context.project; - DoExport(project, "OGG"); -} - - -void MenuCommandHandler::OnExportSelection(const CommandContext &context) -{ - auto &project = context.project; - auto &selectedRegion = project.GetViewInfo().selectedRegion; - Exporter e; - - wxGetApp().SetMissingAliasedFileWarningShouldShow(true); - e.SetFileDialogTitle( _("Export Selected Audio") ); - e.Process(&project, true, selectedRegion.t0(), - selectedRegion.t1()); -} - -void MenuCommandHandler::OnExportMultiple(const CommandContext &context) -{ - auto &project = context.project; - ExportMultiple em(&project); - - wxGetApp().SetMissingAliasedFileWarningShouldShow(true); - em.ShowModal(); -} - void MenuCommandHandler::OnPreferences(const CommandContext &context) { auto &project = context.project; @@ -5968,20 +5490,6 @@ void MenuCommandHandler::OnReloadPreferences(const CommandContext &context ) } } -void MenuCommandHandler::OnPageSetup(const CommandContext &context) -{ - auto &project = context.project; - HandlePageSetup(&project); -} - -void MenuCommandHandler::OnPrint(const CommandContext &context) -{ - auto &project = context.project; - auto name = project.GetName(); - auto tracks = project.GetTracks(); - HandlePrint(&project, name, tracks); -} - // // Edit Menu // @@ -8199,175 +7707,6 @@ void MenuCommandHandler::OnShowEffectsRack(const &WXUNUSED(context) ) // Project Menu // -void MenuCommandHandler::OnImport(const CommandContext &context) -{ - auto &project = context.project; - - // An import trigger for the alias missing dialog might not be intuitive, but - // this serves to track the file if the users zooms in and such. - wxGetApp().SetMissingAliasedFileWarningShouldShow(true); - - wxArrayString selectedFiles = project.ShowOpenDialog(wxT("")); - if (selectedFiles.GetCount() == 0) { - gPrefs->Write(wxT("/LastOpenType"),wxT("")); - gPrefs->Flush(); - return; - } - - // PRL: This affects FFmpegImportPlugin::Open which resets the preference - // to false. Should it also be set to true on other paths that reach - // AudacityProject::Import ? - gPrefs->Write(wxT("/NewImportingSession"), true); - - //sort selected files by OD status. Load non OD first so user can edit asap. - //first sort selectedFiles. - selectedFiles.Sort(CompareNoCaseFileName); - ODManager::Pauser pauser; - - auto cleanup = finally( [&] { - gPrefs->Write(wxT("/LastOpenType"),wxT("")); - - gPrefs->Flush(); - - project.HandleResize(); // Adjust scrollers for NEW track sizes. - } ); - - for (size_t ff = 0; ff < selectedFiles.GetCount(); ff++) { - wxString fileName = selectedFiles[ff]; - - FileNames::UpdateDefaultPath(FileNames::Operation::Open, fileName); - - project.Import(fileName); - } - - project.ZoomAfterImport(nullptr); -} - -void MenuCommandHandler::OnImportLabels(const CommandContext &context) -{ - auto &project = context.project; - auto trackFactory = project.GetTrackFactory(); - auto tracks = project.GetTracks(); - - wxString fileName = - FileNames::SelectFile(FileNames::Operation::Open, - _("Select a text file containing labels"), - wxEmptyString, // Path - wxT(""), // Name - wxT(".txt"), // Extension - _("Text files (*.txt)|*.txt|All files|*"), - wxRESIZE_BORDER, // Flags - &project); // Parent - - if (fileName != wxT("")) { - wxTextFile f; - - f.Open(fileName); - if (!f.IsOpened()) { - AudacityMessageBox( - wxString::Format( _("Could not open file: %s"), fileName ) ); - return; - } - - auto newTrack = trackFactory->NewLabelTrack(); - wxString sTrackName; - wxFileName::SplitPath(fileName, NULL, NULL, &sTrackName, NULL); - newTrack->SetName(sTrackName); - - newTrack->Import(f); - - project.SelectNone(); - newTrack->SetSelected(true); - tracks->Add(std::move(newTrack)); - - project.PushState(wxString:: - Format(_("Imported labels from '%s'"), fileName), - _("Import Labels")); - - project.ZoomAfterImport(nullptr); - } -} - -#ifdef USE_MIDI -void MenuCommandHandler::OnImportMIDI(const CommandContext &context) -{ - auto &project = context.project; - - wxString fileName = FileNames::SelectFile(FileNames::Operation::Open, - _("Select a MIDI file"), - wxEmptyString, // Path - wxT(""), // Name - wxT(""), // Extension - _("MIDI and Allegro files (*.mid;*.midi;*.gro)|*.mid;*.midi;*.gro|MIDI files (*.mid;*.midi)|*.mid;*.midi|Allegro files (*.gro)|*.gro|All files|*"), - wxRESIZE_BORDER, // Flags - &project); // Parent - - if (fileName != wxT("")) - DoImportMIDI(&project, fileName); -} - -AudacityProject *MenuCommandHandler::DoImportMIDI( - AudacityProject *pProject, const wxString &fileName) -{ - auto tracks = pProject->GetTracks(); - - AudacityProject *pNewProject {}; - if ( !pProject ) - pProject = pNewProject = CreateNewAudacityProject(); - auto cleanup = finally( [&] { if ( pNewProject ) pNewProject->Close(true); } ); - - auto newTrack = pProject->GetTrackFactory()->NewNoteTrack(); - - if (::ImportMIDI(fileName, newTrack.get())) { - - pProject->SelectNone(); - auto pTrack = tracks->Add(std::move(newTrack)); - pTrack->SetSelected(true); - - pProject->PushState(wxString::Format(_("Imported MIDI from '%s'"), - fileName), _("Import MIDI")); - - pProject->ZoomAfterImport(pTrack); - pNewProject = nullptr; - - wxGetApp().AddFileToHistory(fileName); - - return pProject; - } - else - return nullptr; -} -#endif // USE_MIDI - -void MenuCommandHandler::OnImportRaw(const CommandContext &context) -{ - auto &project = context.project; - auto trackFactory = project.GetTrackFactory(); - - wxString fileName = - FileNames::SelectFile(FileNames::Operation::Open, - _("Select any uncompressed audio file"), - wxEmptyString, // Path - wxT(""), // Name - wxT(""), // Extension - _("All files|*"), - wxRESIZE_BORDER, // Flags - &project); // Parent - - if (fileName == wxT("")) - return; - - TrackHolders newTracks; - - ::ImportRaw(&project, fileName, trackFactory, newTracks); - - if (newTracks.size() <= 0) - return; - - project.AddImportedTracks(fileName, std::move(newTracks)); - project.HandleResize(); // Adjust scrollers for NEW track sizes. -} - void MenuCommandHandler::OnEditMetadata(const CommandContext &context) { auto &project = context.project; diff --git a/src/Menus.h b/src/Menus.h index 801966e0d..f36340899 100644 --- a/src/Menus.h +++ b/src/Menus.h @@ -218,37 +218,11 @@ void OnMacBringAllToFront(const CommandContext &context ); // File Menu -void OnNew(const CommandContext &context ); -void OnOpen(const CommandContext &context ); -void OnProjectReset(const CommandContext &context); -void OnClose(const CommandContext &context ); -void OnSave(const CommandContext &context ); -void OnSaveAs(const CommandContext &context ); -void OnSaveCopy(const CommandContext &context ); -#ifdef USE_LIBVORBIS - void OnSaveCompressed(const CommandContext &context ); -#endif - void OnCheckDependencies(const CommandContext &context ); -void DoExport(AudacityProject &project, const wxString & Format); -void OnExportAudio(const CommandContext &context ); -void OnExportMp3(const CommandContext &context ); -void OnExportWav(const CommandContext &context ); -void OnExportOgg(const CommandContext &context ); -void OnExportSelection(const CommandContext &context ); -void OnExportMultiple(const CommandContext &context ); -void OnExportLabels(const CommandContext &context ); -void OnExportMIDI(const CommandContext &context ); - void OnPreferences(const CommandContext &context ); void OnReloadPreferences(const CommandContext &context ); -void OnPageSetup(const CommandContext &context ); -void OnPrint(const CommandContext &context ); - -void OnExit(const CommandContext &context ); - // Edit Menu void OnUndo(const CommandContext &context ); @@ -416,16 +390,6 @@ void OnPunchAndRoll(const CommandContext &context); #endif // Import Submenu -void OnImport(const CommandContext &context ); -void OnImportLabels(const CommandContext &context ); -void OnImportMIDI(const CommandContext &context ); - -// return null on failure; if success, return the given project, or a NEW -// one, if the given was null; create no NEW project if failure -static AudacityProject *DoImportMIDI( - AudacityProject *pProject, const wxString &fileName); - -void OnImportRaw(const CommandContext &context ); void OnEditMetadata(const CommandContext &context ); bool DoEditMetadata(AudacityProject &project, @@ -656,6 +620,12 @@ private: MenuCommandHandler &GetMenuCommandHandler(AudacityProject &project); MenuManager &GetMenuManager(AudacityProject &project); +// Exported helper functions from various menu handling source files +namespace FileActions { +AudacityProject *DoImportMIDI( + AudacityProject *pProject, const wxString &fileName ); +} + #endif diff --git a/src/Project.cpp b/src/Project.cpp index 7d4bab702..20ab7c85d 100644 --- a/src/Project.cpp +++ b/src/Project.cpp @@ -473,7 +473,7 @@ public: for (const auto &name : sortednames) { #ifdef USE_MIDI if (Importer::IsMidi(name)) - MenuCommandHandler::DoImportMIDI(mProject, name); + FileActions::DoImportMIDI(mProject, name); else #endif mProject->Import(name); @@ -3133,7 +3133,7 @@ void AudacityProject::OpenFile(const wxString &fileNameArg, bool addtohistory) { #ifdef USE_MIDI if (Importer::IsMidi(fileName)) - MenuCommandHandler::DoImportMIDI(this, fileName); + FileActions::DoImportMIDI(this, fileName); else #endif Import(fileName); diff --git a/src/import/ImportLOF.cpp b/src/import/ImportLOF.cpp index d0b8780b2..81ddbc9d3 100644 --- a/src/import/ImportLOF.cpp +++ b/src/import/ImportLOF.cpp @@ -375,7 +375,7 @@ void LOFImportFileHandle::lofOpenFiles(wxString* ln) // If file is a midi if (Importer::IsMidi(targetfile)) { - mProject = MenuCommandHandler::DoImportMIDI(mProject, targetfile); + mProject = FileActions::DoImportMIDI(mProject, targetfile); } // If not a midi, open audio file diff --git a/src/menus/FileMenus.cpp b/src/menus/FileMenus.cpp index e69de29bb..cab10a079 100644 --- a/src/menus/FileMenus.cpp +++ b/src/menus/FileMenus.cpp @@ -0,0 +1,707 @@ +#include "../AudacityApp.h" +#include "../BatchCommands.h" +#include "../Experimental.h" +#include "../FileNames.h" +#include "../LabelTrack.h" +#include "../NoteTrack.h" +#include "../Prefs.h" +#include "../Printing.h" +#include "../Project.h" +#include "../WaveTrack.h" +#include "../commands/CommandContext.h" +#include "../commands/CommandManager.h" +#include "../export/Export.h" +#include "../export/ExportMultiple.h" + +#ifdef USE_MIDI +#include "../import/ImportMIDI.h" +#endif // USE_MIDI + +#include "../ondemand/ODManager.h" + +// private helper classes and functions +namespace { +void DoExport +(AudacityProject &project, const wxString & Format ) +{ + auto tracks = project.GetTracks(); + + Exporter e; + + wxGetApp().SetMissingAliasedFileWarningShouldShow(true); + double t0 = 0.0; + double t1 = tracks->GetEndTime(); + + // Prompt for file name and/or extension? + bool bPromptingRequired = + (project.mBatchMode == 0) || project.GetFileName().IsEmpty() || + Format.IsEmpty(); + wxString filename; + + if (!bPromptingRequired) { + + // We're in batch mode, and we have an mFileName and Format. + wxString extension = "."; + extension += Format; + extension.MakeLower(); + + filename = + MacroCommands::BuildCleanFileName(project.GetFileName(), extension); + + // Bug 1854, No warning of file overwrite + // (when export is called from Macros). + int counter = 0; + bPromptingRequired = wxFileExists(filename); + + // We'll try alternative names to avoid overwriting. + while ( bPromptingRequired && counter < 100 ) { + counter++; + wxString number; + number.Printf("%03i", counter); + // So now the name has a number in it too. + filename = MacroCommands::BuildCleanFileName( + project.GetFileName(), number + extension); + bPromptingRequired = wxFileExists(filename); + } + // If we've run out of alternative names, we will fall back to prompting + // - even if in a macro. + } + + + if (bPromptingRequired) + { + // Do export with prompting. + e.SetDefaultFormat(Format); + e.Process(&project, false, t0, t1); + } + else + { + wxGetApp().AddFileToHistory(filename); + // We're in batch mode, the file does not exist already. + // We really can proceed without prompting. + int nChannels = MacroCommands::IsMono() ? 1 : 2; + e.Process( + &project, // AudacityProject + nChannels, // numChannels, + Format, // type, + filename, // filename, + false, // selectedOnly, + t0, // t0 + t1 // t1 + ); + } + +} +} + +namespace FileActions { + +// exported helper functions + +#ifdef USE_MIDI +// return null on failure; if success, return the given project, or a NEW +// one, if the given was null; create no NEW project if failure +AudacityProject *DoImportMIDI( + AudacityProject *pProject, const wxString &fileName) +{ + auto tracks = pProject->GetTracks(); + + AudacityProject *pNewProject {}; + if ( !pProject ) + pProject = pNewProject = CreateNewAudacityProject(); + auto cleanup = finally( [&] + { if ( pNewProject ) pNewProject->Close(true); } ); + + auto newTrack = pProject->GetTrackFactory()->NewNoteTrack(); + + if (::ImportMIDI(fileName, newTrack.get())) { + + pProject->SelectNone(); + auto pTrack = tracks->Add(std::move(newTrack)); + pTrack->SetSelected(true); + + pProject->PushState(wxString::Format(_("Imported MIDI from '%s'"), + fileName), _("Import MIDI")); + + pProject->ZoomAfterImport(pTrack); + pNewProject = nullptr; + + wxGetApp().AddFileToHistory(fileName); + + return pProject; + } + else + return nullptr; +} +#endif + +#ifdef USE_MIDI + +// Menu handler functions + +struct Handler : CommandHandlerObject { + +void OnNew(const CommandContext &context ) +{ + CreateNewAudacityProject(); +} + +void OnOpen(const CommandContext &context ) +{ + auto &project = context.project; + AudacityProject::OpenFiles(&project); +} + +// JKC: This is like OnClose, except it emptys the project in place, +// rather than createing a new empty project (with new toolbars etc). +// It does not test for unsaved changes. +// It is not in the menus by default. Its main purpose is/was for +// developers checking functionality of ResetProjectToEmpty(). +void OnProjectReset(const CommandContext &context) +{ + auto &project = context.project; + project.ResetProjectToEmpty(); +} + +void OnClose(const CommandContext &context ) +{ + auto &project = context.project; + project.SetMenuClose(true); + project.Close(); +} + +void OnSave(const CommandContext &context ) +{ + auto &project = context.project; + project.Save(); +} + +void OnSaveAs(const CommandContext &context ) +{ + auto &project = context.project; + project.SaveAs(); +} + +void OnSaveCopy(const CommandContext &context ) +{ + auto &project = context.project; + project.SaveAs(true, true); +} + +#ifdef USE_LIBVORBIS +void OnSaveCompressed(const CommandContext &context) +{ + auto &project = context.project; + project.SaveAs(true); +} +#endif + +void OnExportMp3(const CommandContext &context) +{ + auto &project = context.project; + DoExport(project, "MP3"); +} + +void OnExportWav(const CommandContext &context) +{ + auto &project = context.project; + DoExport(project, "WAV"); +} + +void OnExportOgg(const CommandContext &context) +{ + auto &project = context.project; + DoExport(project, "OGG"); +} + +void OnExportAudio(const CommandContext &context) +{ + auto &project = context.project; + DoExport(project, ""); +} + +void OnExportSelection(const CommandContext &context) +{ + auto &project = context.project; + auto &selectedRegion = project.GetViewInfo().selectedRegion; + Exporter e; + + wxGetApp().SetMissingAliasedFileWarningShouldShow(true); + e.SetFileDialogTitle( _("Export Selected Audio") ); + e.Process(&project, true, selectedRegion.t0(), + selectedRegion.t1()); +} + +void OnExportLabels(const CommandContext &context) +{ + auto &project = context.project; + auto tracks = project.GetTracks(); + + /* i18n-hint: filename containing exported text from label tracks */ + wxString fName = _("labels.txt"); + auto trackRange = tracks->Any(); + auto numLabelTracks = trackRange.size(); + + if (numLabelTracks == 0) { + AudacityMessageBox(_("There are no label tracks to export.")); + return; + } + else + fName = (*trackRange.rbegin())->GetName(); + + fName = FileNames::SelectFile(FileNames::Operation::Export, + _("Export Labels As:"), + wxEmptyString, + fName, + wxT("txt"), + wxT("*.txt"), + wxFD_SAVE | wxFD_OVERWRITE_PROMPT | wxRESIZE_BORDER, + &project); + + if (fName == wxT("")) + return; + + // Move existing files out of the way. Otherwise wxTextFile will + // append to (rather than replace) the current file. + + if (wxFileExists(fName)) { +#ifdef __WXGTK__ + wxString safetyFileName = fName + wxT("~"); +#else + wxString safetyFileName = fName + wxT(".bak"); +#endif + + if (wxFileExists(safetyFileName)) + wxRemoveFile(safetyFileName); + + wxRename(fName, safetyFileName); + } + + wxTextFile f(fName); + f.Create(); + f.Open(); + if (!f.IsOpened()) { + AudacityMessageBox( wxString::Format( + _("Couldn't write to file: %s"), fName ) ); + return; + } + + for (auto lt : trackRange) + lt->Export(f); + + f.Write(); + f.Close(); +} + +void OnExportMultiple(const CommandContext &context) +{ + auto &project = context.project; + ExportMultiple em(&project); + + wxGetApp().SetMissingAliasedFileWarningShouldShow(true); + em.ShowModal(); +} + +#ifdef USE_MIDI +void OnExportMIDI(const CommandContext &context) +{ + auto &project = context.project; + auto tracks = project.GetTracks(); + + // Make sure that there is + // exactly one NoteTrack selected. + const auto range = tracks->Selected< const NoteTrack >(); + const auto numNoteTracksSelected = range.size(); + + if(numNoteTracksSelected > 1) { + AudacityMessageBox(_( + "Please select only one Note Track at a time.")); + return; + } + else if(numNoteTracksSelected < 1) { + AudacityMessageBox(_( + "Please select a Note Track.")); + return; + } + + wxASSERT(numNoteTracksSelected); + if (!numNoteTracksSelected) + return; + + const auto nt = *range.begin(); + + while(true) { + + wxString fName = wxT(""); + + fName = FileNames::SelectFile(FileNames::Operation::Export, + _("Export MIDI As:"), + wxEmptyString, + fName, + wxT(".mid|.gro"), + _("MIDI file (*.mid)|*.mid|Allegro file (*.gro)|*.gro"), + wxFD_SAVE | wxFD_OVERWRITE_PROMPT | wxRESIZE_BORDER, + &project); + + if (fName == wxT("")) + return; + + if(!fName.Contains(wxT("."))) { + fName = fName + wxT(".mid"); + } + + // Move existing files out of the way. Otherwise wxTextFile will + // append to (rather than replace) the current file. + + if (wxFileExists(fName)) { +#ifdef __WXGTK__ + wxString safetyFileName = fName + wxT("~"); +#else + wxString safetyFileName = fName + wxT(".bak"); +#endif + + if (wxFileExists(safetyFileName)) + wxRemoveFile(safetyFileName); + + wxRename(fName, safetyFileName); + } + + if(fName.EndsWith(wxT(".mid")) || fName.EndsWith(wxT(".midi"))) { + nt->ExportMIDI(fName); + } else if(fName.EndsWith(wxT(".gro"))) { + nt->ExportAllegro(fName); + } else { + wxString msg = _("You have selected a filename with an unrecognized file extension.\nDo you want to continue?"); + wxString title = _("Export MIDI"); + int id = AudacityMessageBox(msg, title, wxYES_NO); + if (id == wxNO) { + continue; + } else if (id == wxYES) { + nt->ExportMIDI(fName); + } + } + break; + } +} +#endif // USE_MIDI + +void OnImport(const CommandContext &context) +{ + auto &project = context.project; + + // An import trigger for the alias missing dialog might not be intuitive, but + // this serves to track the file if the users zooms in and such. + wxGetApp().SetMissingAliasedFileWarningShouldShow(true); + + wxArrayString selectedFiles = project.ShowOpenDialog(wxT("")); + if (selectedFiles.GetCount() == 0) { + gPrefs->Write(wxT("/LastOpenType"),wxT("")); + gPrefs->Flush(); + return; + } + + // PRL: This affects FFmpegImportPlugin::Open which resets the preference + // to false. Should it also be set to true on other paths that reach + // AudacityProject::Import ? + gPrefs->Write(wxT("/NewImportingSession"), true); + + //sort selected files by OD status. Load non OD first so user can edit asap. + //first sort selectedFiles. + selectedFiles.Sort(CompareNoCaseFileName); + ODManager::Pauser pauser; + + auto cleanup = finally( [&] { + gPrefs->Write(wxT("/LastOpenType"),wxT("")); + + gPrefs->Flush(); + + project.HandleResize(); // Adjust scrollers for NEW track sizes. + } ); + + for (size_t ff = 0; ff < selectedFiles.GetCount(); ff++) { + wxString fileName = selectedFiles[ff]; + + FileNames::UpdateDefaultPath(FileNames::Operation::Open, fileName); + + project.Import(fileName); + } + + project.ZoomAfterImport(nullptr); +} + +void OnImportLabels(const CommandContext &context) +{ + auto &project = context.project; + auto trackFactory = project.GetTrackFactory(); + auto tracks = project.GetTracks(); + + wxString fileName = + FileNames::SelectFile(FileNames::Operation::Open, + _("Select a text file containing labels"), + wxEmptyString, // Path + wxT(""), // Name + wxT(".txt"), // Extension + _("Text files (*.txt)|*.txt|All files|*"), + wxRESIZE_BORDER, // Flags + &project); // Parent + + if (fileName != wxT("")) { + wxTextFile f; + + f.Open(fileName); + if (!f.IsOpened()) { + AudacityMessageBox( + wxString::Format( _("Could not open file: %s"), fileName ) ); + return; + } + + auto newTrack = trackFactory->NewLabelTrack(); + wxString sTrackName; + wxFileName::SplitPath(fileName, NULL, NULL, &sTrackName, NULL); + newTrack->SetName(sTrackName); + + newTrack->Import(f); + + project.SelectNone(); + newTrack->SetSelected(true); + tracks->Add(std::move(newTrack)); + + project.PushState(wxString:: + Format(_("Imported labels from '%s'"), fileName), + _("Import Labels")); + + project.ZoomAfterImport(nullptr); + } +} + +void OnImportMIDI(const CommandContext &context) +{ + auto &project = context.project; + + wxString fileName = FileNames::SelectFile(FileNames::Operation::Open, + _("Select a MIDI file"), + wxEmptyString, // Path + wxT(""), // Name + wxT(""), // Extension + _("MIDI and Allegro files (*.mid;*.midi;*.gro)|*.mid;*.midi;*.gro|MIDI files (*.mid;*.midi)|*.mid;*.midi|Allegro files (*.gro)|*.gro|All files|*"), + wxRESIZE_BORDER, // Flags + &project); // Parent + + if (fileName != wxT("")) + DoImportMIDI(&project, fileName); +} +#endif + +void OnImportRaw(const CommandContext &context) +{ + auto &project = context.project; + auto trackFactory = project.GetTrackFactory(); + + wxString fileName = + FileNames::SelectFile(FileNames::Operation::Open, + _("Select any uncompressed audio file"), + wxEmptyString, // Path + wxT(""), // Name + wxT(""), // Extension + _("All files|*"), + wxRESIZE_BORDER, // Flags + &project); // Parent + + if (fileName == wxT("")) + return; + + TrackHolders newTracks; + + ::ImportRaw(&project, fileName, trackFactory, newTracks); + + if (newTracks.size() <= 0) + return; + + project.AddImportedTracks(fileName, std::move(newTracks)); + project.HandleResize(); // Adjust scrollers for NEW track sizes. +} + +void OnPageSetup(const CommandContext &context) +{ + auto &project = context.project; + HandlePageSetup(&project); +} + +void OnPrint(const CommandContext &context) +{ + auto &project = context.project; + auto name = project.GetName(); + auto tracks = project.GetTracks(); + HandlePrint(&project, name, tracks); +} + +void OnExit(const CommandContext &WXUNUSED(context) ) +{ + QuitAudacity(); +} + +}; // struct Handler + +} // namespace + +static CommandHandlerObject &findCommandHandler(AudacityProject &) { + // Handler is not stateful. Doesn't need a factory registered with + // AudacityProject. + static FileActions::Handler instance; + return instance; +}; + +// Menu definitions + +#define FN(X) findCommandHandler, \ + static_cast(& FileActions::Handler :: X) +#define XXO(X) _(X), wxString{X}.Contains("...") + +MenuTable::BaseItemPtr FileMenu( AudacityProject& ) +{ + using namespace MenuTable; + + return Menu( _("&File"), + /*i18n-hint: "New" is an action (verb) to create a NEW project*/ + Command( wxT("New"), XXO("&New"), FN(OnNew), + AudioIONotBusyFlag, wxT("Ctrl+N") ), + + /*i18n-hint: (verb)*/ + Command( wxT("Open"), XXO("&Open..."), FN(OnOpen), + AudioIONotBusyFlag, wxT("Ctrl+O") ), + +#ifdef EXPERIMENTAL_RESET + // Empty the current project and forget its name and path. DANGEROUS + // It's just for developers. + // Do not translate this menu item (no XXO). + // It MUST not be shown to regular users. + Command( wxT("Reset"), XXO("&Dangerous Reset..."), FN(OnProjectReset), + AudioIONotBusyFlag ), +#endif + +///////////////////////////////////////////////////////////////////////////// + + Menu( +#ifdef __WXMAC__ + /* i18n-hint: This is the name of the menu item on Mac OS X only */ + _("Open Recent") +#else + /* i18n-hint: This is the name of the menu item on Windows and Linux */ + _("Recent &Files") +#endif + , + Special( [](AudacityProject &, wxMenu &theMenu){ + // Recent Files and Recent Projects menus + wxGetApp().GetRecentFiles()->UseMenu( &theMenu ); + wxGetApp().GetRecentFiles()->AddFilesToMenu( &theMenu ); + + wxWeakRef recentFilesMenu{ &theMenu }; + wxTheApp->CallAfter( [=] { + // Bug 143 workaround. + // The bug is in wxWidgets. For a menu that has scrollers, + // the scrollers have an ID of 0 (not wxID_NONE which is -3). + // Therefore wxWidgets attempts to find a help string. See + // wxFrameBase::ShowMenuHelp(int menuId) + // It finds a bogus automatic help string of "Recent &Files" + // from that submenu. + // So we set the help string for command with Id 0 to empty. + if ( recentFilesMenu ) + recentFilesMenu->GetParent()->SetHelpString( 0, "" ); + } ); + } ) + ), + +///////////////////////////////////////////////////////////////////////////// + + Command( wxT("Close"), XXO("&Close"), FN(OnClose), + AudioIONotBusyFlag, wxT("Ctrl+W") ), + + Separator(), + + Menu( _("&Save Project"), + Command( wxT("Save"), XXO("&Save Project"), FN(OnSave), + AudioIONotBusyFlag | UnsavedChangesFlag, wxT("Ctrl+S") ), + Command( wxT("SaveAs"), XXO("Save Project &As..."), FN(OnSaveAs), + AudioIONotBusyFlag ), + // TODO: The next two items should be disabled if project is empty + Command( wxT("SaveCopy"), XXO("Save Lossless Copy of Project..."), + FN(OnSaveCopy), AudioIONotBusyFlag ) +#ifdef USE_LIBVORBIS + , + Command( wxT("SaveCompressed"), + XXO("&Save Compressed Copy of Project..."), + FN(OnSaveCompressed), AudioIONotBusyFlag ) +#endif + ), + + Separator(), + + Menu( _("&Export"), + // Enable Export audio commands only when there are audio tracks. + Command( wxT("ExportMp3"), XXO("Export as MP&3"), FN(OnExportMp3), + AudioIONotBusyFlag | WaveTracksExistFlag ), + + Command( wxT("ExportWav"), XXO("Export as &WAV"), FN(OnExportWav), + AudioIONotBusyFlag | WaveTracksExistFlag ), + + Command( wxT("ExportOgg"), XXO("Export as &OGG"), FN(OnExportOgg), + AudioIONotBusyFlag | WaveTracksExistFlag ), + + Command( wxT("Export"), XXO("&Export Audio..."), FN(OnExportAudio), + AudioIONotBusyFlag | WaveTracksExistFlag, wxT("Ctrl+Shift+E") ), + + // Enable Export Selection commands only when there's a selection. + Command( wxT("ExportSel"), XXO("Expo&rt Selected Audio..."), + FN(OnExportSelection), + AudioIONotBusyFlag | TimeSelectedFlag | WaveTracksSelectedFlag ), + + Command( wxT("ExportLabels"), XXO("Export &Labels..."), + FN(OnExportLabels), + AudioIONotBusyFlag | LabelTracksExistFlag ), + // Enable Export audio commands only when there are audio tracks. + Command( wxT("ExportMultiple"), XXO("Export &Multiple..."), + FN(OnExportMultiple), + AudioIONotBusyFlag | WaveTracksExistFlag, wxT("Ctrl+Shift+L") ) +#if defined(USE_MIDI) + , + Command( wxT("ExportMIDI"), XXO("Export MI&DI..."), FN(OnExportMIDI), + AudioIONotBusyFlag | NoteTracksExistFlag ) +#endif + ), + + Menu( _("&Import"), + Command( wxT("ImportAudio"), XXO("&Audio..."), FN(OnImport), + AudioIONotBusyFlag, wxT("Ctrl+Shift+I") ), + Command( wxT("ImportLabels"), XXO("&Labels..."), FN(OnImportLabels), + AudioIONotBusyFlag ), + #ifdef USE_MIDI + Command( wxT("ImportMIDI"), XXO("&MIDI..."), FN(OnImportMIDI), + AudioIONotBusyFlag ), + #endif // USE_MIDI + Command( wxT("ImportRaw"), XXO("&Raw Data..."), FN(OnImportRaw), + AudioIONotBusyFlag ) + ), + + Separator(), + +///////////////////////////////////////////////////////////////////////////// + + Command( wxT("PageSetup"), XXO("Pa&ge Setup..."), FN(OnPageSetup), + AudioIONotBusyFlag | TracksExistFlag ), + /* i18n-hint: (verb) It's item on a menu. */ + Command( wxT("Print"), XXO("&Print..."), FN(OnPrint), + AudioIONotBusyFlag | TracksExistFlag ), + + Separator(), + + // On the Mac, the Exit item doesn't actually go here...wxMac will + // pull it out + // and put it in the Audacity menu for us based on its ID. + /* i18n-hint: (verb) It's item on a menu. */ + Command( wxT("Exit"), XXO("E&xit"), FN(OnExit), + AlwaysEnabledFlag, wxT("Ctrl+Q") ) + ); +} + +#undef XXO +#undef FN