Audacity  2.2.2
Menus.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3  Audacity: A Digital Audio Editor
4 
5  Menus.cpp
6 
7  Dominic Mazzoni
8  Brian Gunlogson
9  et al.
10 
11 *******************************************************************//****************************************************************//*******************************************************************/
32 
33 #include "Audacity.h"
34 #include "Project.h"
35 
36 #include <cfloat>
37 #include <iterator>
38 #include <algorithm>
39 #include <limits>
40 #include <math.h>
41 
42 
43 #include <wx/defs.h>
44 #include <wx/docview.h>
45 #include <wx/filedlg.h>
46 #include <wx/textfile.h>
47 #include <wx/textdlg.h>
48 #include <wx/progdlg.h>
49 #include <wx/scrolbar.h>
50 #include <wx/ffile.h>
51 #include <wx/statusbr.h>
52 #include <wx/utils.h>
53 
54 #include "FreqWindow.h"
55 #include "effects/Contrast.h"
56 #include "TrackPanel.h"
57 
58 #include "effects/EffectManager.h"
59 
60 #include "AudacityApp.h"
61 #include "AudacityLogger.h"
62 #include "AudioIO.h"
63 #include "Dependencies.h"
64 #include "float_cast.h"
65 #include "LabelTrack.h"
66 #ifdef USE_MIDI
67 #include "import/ImportMIDI.h"
68 #endif // USE_MIDI
69 #include "import/ImportRaw.h"
70 #include "export/Export.h"
71 #include "export/ExportMultiple.h"
72 #include "prefs/PrefsDialog.h"
73 #include "prefs/PlaybackPrefs.h"
74 #include "ShuttleGui.h"
75 #include "HistoryWindow.h"
76 #include "LyricsWindow.h"
77 #include "MixerBoard.h"
78 #include "Internat.h"
79 #include "FileFormats.h"
80 #include "ModuleManager.h"
81 #include "PluginManager.h"
82 #include "Prefs.h"
83 #include "Printing.h"
84 #ifdef USE_MIDI
85 #include "NoteTrack.h"
86 #endif // USE_MIDI
87 #include "Tags.h"
88 #include "TimeTrack.h"
89 #include "Mix.h"
90 #include "AboutDialog.h"
91 #include "Benchmark.h"
92 #include "Screenshot.h"
93 #include "ondemand/ODManager.h"
94 
95 #include "BatchProcessDialog.h"
96 #include "BatchCommands.h"
97 #include "prefs/BatchPrefs.h"
98 
99 #include "toolbars/ToolManager.h"
100 #include "toolbars/ControlToolBar.h"
101 #include "toolbars/ToolsToolBar.h"
102 #include "toolbars/EditToolBar.h"
103 #include "toolbars/DeviceToolBar.h"
104 #include "toolbars/MixerToolBar.h"
106 
107 #include "tracks/ui/SelectHandle.h"
108 
110 
111 #include "Experimental.h"
112 #include "PlatformCompatibility.h"
113 #include "FileNames.h"
114 #include "TimeDialog.h"
115 #include "TimerRecordDialog.h"
116 #include "SoundActivatedRecord.h"
117 #include "LabelDialog.h"
118 
119 #include "SplashDialog.h"
120 #include "widgets/HelpSystem.h"
121 #include "DeviceManager.h"
122 
123 #include "UndoManager.h"
124 #include "WaveTrack.h"
125 
126 #if defined(EXPERIMENTAL_CRASH_REPORT)
127 #include <wx/debugrpt.h>
128 #endif
129 
130 #ifdef EXPERIMENTAL_SCOREALIGN
132 #include "audioreader.h"
133 #include "scorealign.h"
134 #include "scorealign-glue.h"
135 #endif /* EXPERIMENTAL_SCOREALIGN */
136 
137 #include "tracks/ui/Scrubbing.h"
138 #include "prefs/TracksPrefs.h"
139 
140 #include "widgets/Meter.h"
141 #include "widgets/ErrorDialog.h"
143 #include "commands/CommandContext.h"
144 
145 enum {
151  // The next two are only in one subMenu, so more easily handled at the end.
154 };
155 
156 // Post Timer Recording Actions
157 // Ensure this matches the enum in TimerRecordDialog.cpp
158 enum {
166 };
167 
168 #include "commands/CommandContext.h"
170 
171 #include "BatchCommands.h"
172 
173 
174 //
175 // Effects menu arrays
176 //
177 static bool SortEffectsByName(const PluginDescriptor *a, const PluginDescriptor *b)
178 {
179  auto akey = a->GetSymbol().Translation();
180  auto bkey = b->GetSymbol().Translation();
181 
182  akey += a->GetPath();
183  bkey += b->GetPath();
184 
185  return akey.CmpNoCase(bkey) < 0;
186 }
187 
189 {
190  auto &em = EffectManager::Get();
191  auto akey = em.GetVendorName(a->GetID());
192  auto bkey = em.GetVendorName(b->GetID());
193 
194  if (akey.IsEmpty())
195  {
196  akey = _("Uncategorized");
197  }
198  if (bkey.IsEmpty())
199  {
200  bkey = _("Uncategorized");
201  }
202 
203  akey += a->GetSymbol().Translation();
204  bkey += b->GetSymbol().Translation();
205 
206  akey += a->GetPath();
207  bkey += b->GetPath();
208 
209  return akey.CmpNoCase(bkey) < 0;
210 }
211 
213 {
214  auto &em = EffectManager::Get();
215  auto akey = em.GetVendorName(a->GetID());
216  auto bkey = em.GetVendorName(b->GetID());
217 
218  if (a->IsEffectDefault())
219  {
220  akey = wxEmptyString;
221  }
222  if (b->IsEffectDefault())
223  {
224  bkey = wxEmptyString;
225  }
226 
227  akey += a->GetSymbol().Translation();
228  bkey += b->GetSymbol().Translation();
229 
230  akey += a->GetPath();
231  bkey += b->GetPath();
232 
233  return akey.CmpNoCase(bkey) < 0;
234 }
235 
237 {
238  auto &em = EffectManager::Get();
239  auto akey = em.GetEffectFamilyName(a->GetID());
240  auto bkey = em.GetEffectFamilyName(b->GetID());
241 
242  if (akey.IsEmpty())
243  {
244  akey = _("Uncategorized");
245  }
246  if (bkey.IsEmpty())
247  {
248  bkey = _("Uncategorized");
249  }
250 
251  if (a->IsEffectDefault())
252  {
253  akey = wxEmptyString;
254  }
255  if (b->IsEffectDefault())
256  {
257  bkey = wxEmptyString;
258  }
259 
260  akey += a->GetSymbol().Translation();
261  bkey += b->GetSymbol().Translation();
262 
263  akey += a->GetPath();
264  bkey += b->GetPath();
265 
266  return akey.CmpNoCase(bkey) < 0;
267 }
268 
269 static bool SortEffectsByType(const PluginDescriptor *a, const PluginDescriptor *b)
270 {
271  auto &em = EffectManager::Get();
272  auto akey = em.GetEffectFamilyName(a->GetID());
273  auto bkey = em.GetEffectFamilyName(b->GetID());
274 
275  if (akey.IsEmpty())
276  {
277  akey = _("Uncategorized");
278  }
279  if (bkey.IsEmpty())
280  {
281  bkey = _("Uncategorized");
282  }
283 
284  akey += a->GetSymbol().Translation();
285  bkey += b->GetSymbol().Translation();
286 
287  akey += a->GetPath();
288  bkey += b->GetPath();
289 
290  return akey.CmpNoCase(bkey) < 0;
291 }
292 
296 
297 // To supply the "finder" argument in AddItem calls
298 static CommandHandlerObject &ident(AudacityProject &project) { return project; }
299 
300 #define FN(X) ident, static_cast<CommandFunctorPointer>(& AudacityProject :: X)
301 #define XXO(X) _(X), wxString{X}.Contains("...")
302 
304 {
306  wxArrayString names;
307  std::vector<int> indices;
308 
309  // The list of defaults to exclude depends on
310  // preference wxT("/GUI/Shortcuts/FullDefaults"), which may have changed.
311  c->SetMaxList();
312 
313  {
314  auto menubar = c->AddMenuBar(wxT("appmenu"));
315  wxASSERT(menubar);
316  c->SetOccultCommands( false );
317 
319  // File menu
321 
322  c->BeginMenu(_("&File"));
324 
325  /*i18n-hint: "New" is an action (verb) to create a NEW project*/
326  c->AddItem(wxT("New"), XXO("&New"), FN(OnNew), wxT("Ctrl+N"),
329 
330  /*i18n-hint: (verb)*/
331  c->AddItem(wxT("Open"), XXO("&Open..."), FN(OnOpen), wxT("Ctrl+O"),
334 
336 
338 
340 
341  c->AddItem(wxT("Close"), XXO("&Close"), FN(OnClose), wxT("Ctrl+W"));
342 
343  c->AddSeparator();
344 
345  c->BeginSubMenu( _("&Save Project") );
346  c->AddItem(wxT("Save"), XXO("&Save Project"), FN(OnSave), wxT("Ctrl+S"),
349  c->AddItem(wxT("SaveAs"), XXO("Save Project &As..."), FN(OnSaveAs));
350  // TODO: The next two items should be disabled if project is empty
351  c->AddItem(wxT("SaveCopy"), XXO("Save Lossless Copy of Project..."), FN(OnSaveCopy));
352 #ifdef USE_LIBVORBIS
353  c->AddItem(wxT("SaveCompressed"), XXO("&Save Compressed Copy of Project..."), FN(OnSaveCompressed));
354 #endif
355  c->EndSubMenu();
356  c->AddSeparator();
357 
358  c->BeginSubMenu( _("&Export") );
359 
360  // Enable Export audio commands only when there are audio tracks.
361  c->AddItem(wxT("ExportMp3"), XXO("Export as MP&3"), FN(OnExportMp3), wxT(""),
364 
365  c->AddItem(wxT("ExportWav"), XXO("Export as &WAV"), FN(OnExportWav), wxT(""),
368 
369  c->AddItem(wxT("ExportOgg"), XXO("Export as &OGG"), FN(OnExportOgg), wxT(""),
372 
373  c->AddItem(wxT("Export"), XXO("&Export Audio..."), FN(OnExportAudio), wxT("Ctrl+Shift+E"),
376 
377  // Enable Export Selection commands only when there's a selection.
378  c->AddItem(wxT("ExportSel"), XXO("Expo&rt Selected Audio..."), FN(OnExportSelection),
381 
382  c->AddItem(wxT("ExportLabels"), XXO("Export &Labels..."), FN(OnExportLabels),
385  // Enable Export audio commands only when there are audio tracks.
386  c->AddItem(wxT("ExportMultiple"), XXO("Export &Multiple..."), FN(OnExportMultiple), wxT("Ctrl+Shift+L"),
389 #if defined(USE_MIDI)
390  c->AddItem(wxT("ExportMIDI"), XXO("Export MI&DI..."), FN(OnExportMIDI),
393 #endif
394  c->EndSubMenu();
395 
396  c->BeginSubMenu(_("&Import"));
397 
398  c->AddItem(wxT("ImportAudio"), XXO("&Audio..."), FN(OnImport), wxT("Ctrl+Shift+I"));
399  c->AddItem(wxT("ImportLabels"), XXO("&Labels..."), FN(OnImportLabels));
400 #ifdef USE_MIDI
401  c->AddItem(wxT("ImportMIDI"), XXO("&MIDI..."), FN(OnImportMIDI));
402 #endif // USE_MIDI
403  c->AddItem(wxT("ImportRaw"), XXO("&Raw Data..."), FN(OnImportRaw));
404 
405  c->EndSubMenu();
406  c->AddSeparator();
407 
409 
410  c->AddItem(wxT("PageSetup"), XXO("Pa&ge Setup..."), FN(OnPageSetup),
413  /* i18n-hint: (verb) It's item on a menu. */
414  c->AddItem(wxT("Print"), XXO("&Print..."), FN(OnPrint),
417 
418  c->AddSeparator();
419 
420  // On the Mac, the Exit item doesn't actually go here...wxMac will pull it out
421  // and put it in the Audacity menu for us based on its ID.
422  /* i18n-hint: (verb) It's item on a menu. */
423  c->AddItem(wxT("Exit"), XXO("E&xit"), FN(OnExit), wxT("Ctrl+Q"),
426 
427  c->EndMenu();
428 
430  // Edit Menu
432 
433  c->BeginMenu(_("&Edit"));
434 
437 
438  c->AddItem(wxT("Undo"), XXO("&Undo"), FN(OnUndo), wxT("Ctrl+Z"),
441 
442  // The default shortcut key for Redo is different on different platforms.
443  wxString key =
444 #ifdef __WXMSW__
445  wxT("Ctrl+Y");
446 #else
447  wxT("Ctrl+Shift+Z");
448 #endif
449 
450  c->AddItem(wxT("Redo"), XXO("&Redo"), FN(OnRedo), key,
453 
455 
456  c->AddSeparator();
457 
458  // Basic Edit coomands
459  /* i18n-hint: (verb)*/
460  c->AddItem(wxT("Cut"), XXO("Cu&t"), FN(OnCut), wxT("Ctrl+X"),
463  c->AddItem(wxT("Delete"), XXO("&Delete"), FN(OnDelete), wxT("Ctrl+K"),
466  /* i18n-hint: (verb)*/
467  c->AddItem(wxT("Copy"), XXO("&Copy"), FN(OnCopy), wxT("Ctrl+C"),
470  /* i18n-hint: (verb)*/
471  c->AddItem(wxT("Paste"), XXO("&Paste"), FN(OnPaste), wxT("Ctrl+V"),
474  /* i18n-hint: (verb)*/
475  c->AddItem(wxT("Duplicate"), XXO("Duplic&ate"), FN(OnDuplicate), wxT("Ctrl+D"));
476 
477  c->AddSeparator();
478 
479  c->BeginSubMenu(_("R&emove Special"));
480  /* i18n-hint: (verb) Do a special kind of cut*/
481  c->AddItem(wxT("SplitCut"), XXO("Spl&it Cut"), FN(OnSplitCut), wxT("Ctrl+Alt+X"));
482  /* i18n-hint: (verb) Do a special kind of DELETE*/
483  c->AddItem(wxT("SplitDelete"), XXO("Split D&elete"), FN(OnSplitDelete), wxT("Ctrl+Alt+K"));
484 
485  c->AddSeparator();
486 
487  /* i18n-hint: (verb)*/
488  c->AddItem(wxT("Silence"), XXO("Silence Audi&o"), FN(OnSilence), wxT("Ctrl+L"),
491  /* i18n-hint: (verb)*/
492  c->AddItem(wxT("Trim"), XXO("Tri&m Audio"), FN(OnTrim), wxT("Ctrl+T"),
495  c->EndSubMenu();
496 
497  c->AddSeparator();
498 
500 
501  c->BeginSubMenu(_("Clip B&oundaries"));
502  /* i18n-hint: (verb) It's an item on a menu. */
503  c->AddItem(wxT("Split"), XXO("Sp&lit"), FN(OnSplit), wxT("Ctrl+I"),
506  c->AddItem(wxT("SplitNew"), XXO("Split Ne&w"), FN(OnSplitNew), wxT("Ctrl+Alt+I"),
509  c->AddSeparator();
510  /* i18n-hint: (verb)*/
511  c->AddItem(wxT("Join"), XXO("&Join"), FN(OnJoin), wxT("Ctrl+J"));
512  c->AddItem(wxT("Disjoin"), XXO("Detac&h at Silences"), FN(OnDisjoin), wxT("Ctrl+Alt+J"));
513  c->EndSubMenu();
514 
516 
517  c->BeginSubMenu(_("&Labels"));
518 
519  c->AddItem(wxT("EditLabels"), XXO("&Edit Labels..."), FN(OnEditLabels),
521 
522  c->AddSeparator();
523 
524  c->AddItem(wxT("AddLabel"), XXO("Add Label at &Selection"), FN(OnAddLabel), wxT("Ctrl+B"),
526  c->AddItem(wxT("AddLabelPlaying"), XXO("Add Label at &Playback Position"),
528 #ifdef __WXMAC__
529  wxT("Ctrl+."),
530 #else
531  wxT("Ctrl+M"),
532 #endif
534  AudioIOBusyFlag);
536  c->AddItem(wxT("PasteNewLabel"), XXO("Paste Te&xt to New Label"), FN(OnPasteNewLabel), wxT("Ctrl+Alt+V"),
538 
539  c->AddSeparator();
540 
541  c->AddCheck(wxT("TypeToCreateLabel"), XXO("&Type to Create a Label (on/off)"),
543 
544  c->EndSubMenu();
545 
547 
548  c->BeginSubMenu(_("La&beled Audio"));
549 
552 
553  /* i18n-hint: (verb)*/
554  c->SetLongName( _("Label Cut"))->AddItem(wxT("CutLabels"), XXO("&Cut"), FN(OnCutLabels), wxT("Alt+X"),
557  c->SetLongName( _("Label Delete"))->AddItem(wxT("DeleteLabels"), XXO("&Delete"), FN(OnDeleteLabels), wxT("Alt+K"),
560 
561  c->AddSeparator();
562 
563  /* i18n-hint: (verb) A special way to cut out a piece of audio*/
564  c->SetLongName( _("Label Split Cut"))->AddItem(wxT("SplitCutLabels"), XXO("&Split Cut"), FN(OnSplitCutLabels), wxT("Alt+Shift+X"));
565  c->SetLongName( _("Label Split Delete"))->AddItem(wxT("SplitDeleteLabels"), XXO("Sp&lit Delete"), FN(OnSplitDeleteLabels), wxT("Alt+Shift+K"));
566 
567  c->AddSeparator();
568 
569 
570  c->SetLongName( _("Label Silence"))->AddItem(wxT("SilenceLabels"), XXO("Silence &Audio"), FN(OnSilenceLabels), wxT("Alt+L"));
571  /* i18n-hint: (verb)*/
572  c->SetLongName( _("Label Copy"))->AddItem(wxT("CopyLabels"), XXO("Co&py"), FN(OnCopyLabels), wxT("Alt+Shift+C"));
573 
574  c->AddSeparator();
575 
576  /* i18n-hint: (verb)*/
577  c->SetLongName( _("Label Split"))->AddItem(wxT("SplitLabels"), XXO("Spli&t"), FN(OnSplitLabels), wxT("Alt+I"),
580  /* i18n-hint: (verb)*/
581  c->SetLongName( _("Label Join"))->AddItem(wxT("JoinLabels"), XXO("&Join"), FN(OnJoinLabels), wxT("Alt+J"));
582  c->AddItem(wxT("DisjoinLabels"), XXO("Detac&h at Silences"), FN(OnDisjoinLabels), wxT("Alt+Shift+J"));
583 
584  c->EndSubMenu();
585 
586  c->AddItem(wxT("EditMetaData"), XXO("Me&tadata..."), FN(OnEditMetadata),
588 
590 
591 #ifndef __WXMAC__
592  c->AddSeparator();
593 #endif
594 
595  // The default shortcut key for Preferences is different on different platforms.
596  key =
597 #ifdef __WXMAC__
598  wxT("Ctrl+,");
599 #else
600  wxT("Ctrl+P");
601 #endif
602 
603  c->AddItem(wxT("Preferences"), XXO("Pre&ferences..."), FN(OnPreferences), key,
606 
607  c->EndMenu();
608 
610  // Select Menu
612 
613  /* i18n-hint: (verb) It's an item on a menu. */
614  c->BeginMenu(_("&Select"));
616 
617  c->SetLongName( _("Select All"))->AddItem(wxT("SelectAll"), XXO("&All"), FN(OnSelectAll), wxT("Ctrl+A"));
618  c->SetLongName( _("Select None"))->AddItem(wxT("SelectNone"), XXO("&None"), FN(OnSelectNone), wxT("Ctrl+Shift+A"));
619 
621 
623 
624  c->BeginSubMenu(_("&Tracks"));
625  c->AddItem(wxT("SelAllTracks"), XXO("In All &Tracks"), FN(OnSelectAllTracks),
626  wxT("Ctrl+Shift+K"),
628 
629 #ifdef EXPERIMENTAL_SYNC_LOCK
630  c->SetLongName( _("Select Sync-Locked"))->AddItem(wxT("SelSyncLockTracks"), XXO("In All &Sync-Locked Tracks"),
631  FN(OnSelectSyncLockSel), wxT("Ctrl+Shift+Y"),
634 #endif
635 
636  c->EndSubMenu();
637 
639 
641 
642  c->BeginSubMenu(_("R&egion"));
643 
644  c->SetLongName( _("Set Selection Left at Play Position"))->AddItem(wxT("SetLeftSelection"), XXO("&Left at Playback Position"), FN(OnSetLeftSelection), wxT("["));
645  c->SetLongName( _("Set Selection Right at Play Position"))->AddItem(wxT("SetRightSelection"), XXO("&Right at Playback Position"), FN(OnSetRightSelection), wxT("]"));
647  c->SetLongName( _("Select Track Start to Cursor"))->AddItem(wxT("SelTrackStartToCursor"), XXO("Track &Start to Cursor"), FN(OnSelectStartCursor), wxT("Shift+J"),AlwaysEnabledFlag,AlwaysEnabledFlag);
648  c->SetLongName( _("Select Cursor to Track End"))->AddItem(wxT("SelCursorToTrackEnd"), XXO("Cursor to Track &End"), FN(OnSelectCursorEnd), wxT("Shift+K"),AlwaysEnabledFlag,AlwaysEnabledFlag);
649  c->SetLongName( _("Select Track Start to End"))->AddItem(wxT("SelTrackStartToEnd"), XXO("Track Start to En&d"), FN(OnSelectTrackStartToEnd), wxT(""),AlwaysEnabledFlag,AlwaysEnabledFlag);
650  c->AddSeparator();
651  // GA: Audacity had 'Store Re&gion' here previously. There is no one-step
652  // way to restore the 'Saved Cursor Position' in Select Menu, so arguably
653  // using the word 'Selection' to do duty for both saving the region or the
654  // cursor is better. But it does not belong in a 'Region' submenu.
655  c->AddItem(wxT("SelSave"), XXO("S&tore Selection"), FN(OnSelectionSave),
658  // Audacity had 'Retrieve Regio&n' here previously.
659  c->AddItem(wxT("SelRestore"), XXO("Retrieve Selectio&n"), FN(OnSelectionRestore),
662 
663  c->EndSubMenu();
664 
666 
668 
669 #ifdef EXPERIMENTAL_SPECTRAL_EDITING
670  c->BeginSubMenu(_("S&pectral"));
671  c->AddItem(wxT("ToggleSpectralSelection"), XXO("To&ggle Spectral Selection"), FN(OnToggleSpectralSelection), wxT("Q"));
672  c->AddItem(wxT("NextHigherPeakFrequency"), XXO("Next &Higher Peak Frequency"), FN(OnNextHigherPeakFrequency));
673  c->AddItem(wxT("NextLowerPeakFrequency"), XXO("Next &Lower Peak Frequency"), FN(OnNextLowerPeakFrequency));
674  c->EndSubMenu();
675 #endif
676 
678 
680 
681  c->BeginSubMenu(_("Clip B&oundaries"));
682  c->AddItem(wxT("SelPrevClipBoundaryToCursor"), XXO("Pre&vious Clip Boundary to Cursor"),
685  c->AddItem(wxT("SelCursorToNextClipBoundary"), XXO("Cursor to Ne&xt Clip Boundary"),
688  c->SetLongName( _("Select Previous Clip"))->AddItem(wxT("SelPrevClip"), XXO("Previo&us Clip"), FN(OnSelectPrevClip), wxT("Ctrl+Alt+P"),
690  c->SetLongName( _("Select Next Clip"))->AddItem(wxT("SelNextClip"), XXO("N&ext Clip"), FN(OnSelectNextClip), wxT("Ctrl+Alt+N"),
692 
693  c->EndSubMenu();
695 
696  c->AddSeparator();
697 
698  c->SetLongName( _("Select Cursor to Stored"))->AddItem(wxT("SelCursorStoredCursor"), XXO("Cursor to Stored &Cursor Position"), FN(OnSelectCursorStoredCursor),
699  wxT(""), TracksExistFlag, TracksExistFlag);
700 
701  c->AddItem(wxT("StoreCursorPosition"), XXO("Store Cursor Pos&ition"), FN(OnCursorPositionStore),
704  // Save cursor position is used in some selections.
705  // Maybe there should be a restore for it?
706 
707  c->AddSeparator();
708 
709  c->SetLongName( _("Select Zero Crossing"))->AddItem(wxT("ZeroCross"), XXO("At &Zero Crossings"), FN(OnZeroCrossing), wxT("Z"));
710 
711  c->EndMenu();
712 
714  // View Menu
716 
717  c->BeginMenu(_("&View"));
719  c->BeginSubMenu(_("&Zoom"));
720 
721  c->AddItem(wxT("ZoomIn"), XXO("Zoom &In"), FN(OnZoomIn), wxT("Ctrl+1"),
724  c->AddItem(wxT("ZoomNormal"), XXO("Zoom &Normal"), FN(OnZoomNormal), wxT("Ctrl+2"));
725  c->AddItem(wxT("ZoomOut"), XXO("Zoom &Out"), FN(OnZoomOut), wxT("Ctrl+3"),
728  c->AddItem(wxT("ZoomSel"), XXO("&Zoom to Selection"), FN(OnZoomSel), wxT("Ctrl+E"),
731  c->AddItem(wxT("ZoomToggle"), XXO("Zoom &Toggle"), FN(OnZoomToggle), wxT("Shift+Z"),
734  c->EndSubMenu();
735 
736  c->BeginSubMenu(_("T&rack Size"));
737  c->AddItem(wxT("FitInWindow"), XXO("&Fit to Width"), FN(OnZoomFit), wxT("Ctrl+F"));
738  c->AddItem(wxT("FitV"), XXO("Fit to &Height"), FN(OnZoomFitV), wxT("Ctrl+Shift+F"));
739  c->AddItem(wxT("CollapseAllTracks"), XXO("&Collapse All Tracks"), FN(OnCollapseAllTracks), wxT("Ctrl+Shift+C"));
740  c->AddItem(wxT("ExpandAllTracks"), XXO("E&xpand Collapsed Tracks"), FN(OnExpandAllTracks), wxT("Ctrl+Shift+X"));
741  c->EndSubMenu();
742 
743  c->BeginSubMenu(_("Sk&ip to"));
744  c->SetLongName( _("Skip to Selection Start"))->AddItem(wxT("SkipSelStart"), XXO("Selection Sta&rt"), FN(OnGoSelStart), wxT("Ctrl+["),
746  c->SetLongName( _("Skip to Selection End"))->AddItem(wxT("SkipSelEnd"), XXO("Selection En&d"), FN(OnGoSelEnd), wxT("Ctrl+]"),
748  c->EndSubMenu();
749 
750  c->AddSeparator();
751 
752  // History window should be available either for UndoAvailableFlag or RedoAvailableFlag,
753  // but we can't make the AddItem flags and mask have both, because they'd both have to be true for the
754  // command to be enabled.
755  // If user has Undone the entire stack, RedoAvailableFlag is on but UndoAvailableFlag is off.
756  // If user has done things but not Undone anything, RedoAvailableFlag is off but UndoAvailableFlag is on.
757  // So in either of those cases, (AudioIONotBusyFlag | UndoAvailableFlag | RedoAvailableFlag) mask
758  // would fail.
759  // The only way to fix this in the current architecture is to hack in special cases for RedoAvailableFlag
760  // in AudacityProject::UpdateMenus() (ugly) and CommandManager::HandleCommandEntry() (*really* ugly --
761  // shouldn't know about particular command names and flags).
762  // Here's the hack that would be necessary in AudacityProject::UpdateMenus(), if somebody decides to do it:
763  // // Because EnableUsingFlags requires all the flag bits match the corresponding mask bits,
764  // // "UndoHistory" specifies only AudioIONotBusyFlag | UndoAvailableFlag, because that
765  // // covers the majority of cases where it should be enabled.
766  // // If history is not empty but we've Undone the whole stack, we also want to enable,
767  // // to show the Redo's on stack.
768  // // "UndoHistory" might already be enabled, but add this check for RedoAvailableFlag.
769  // if (flags & RedoAvailableFlag)
770  // mCommandManager.Enable(wxT("UndoHistory"), true);
771  // So for now, enable the command regardless of stack. It will just show empty sometimes.
772  // FOR REDESIGN, clearly there are some limitations with the flags/mask bitmaps.
773 
774  /* i18n-hint: Clicking this menu item shows the various editing steps that have been taken.*/
775  c->AddItem(wxT("UndoHistory"), XXO("&History..."), FN(OnHistory),
778 
779  c->AddItem(wxT("Karaoke"), XXO("&Karaoke..."), FN(OnKaraoke), LabelTracksExistFlag, LabelTracksExistFlag);
780  c->AddItem(wxT("MixerBoard"), XXO("&Mixer Board..."), FN(OnMixerBoard), PlayableTracksExistFlag, PlayableTracksExistFlag);
781 
782  c->AddSeparator();
783 
785 
786  c->BeginSubMenu(_("&Toolbars"));
787 
788  /* i18n-hint: (verb)*/
789  c->AddItem(wxT("ResetToolbars"), XXO("Reset Toolb&ars"), FN(OnResetToolBars), 0, AlwaysEnabledFlag, AlwaysEnabledFlag);
790  c->AddSeparator();
791 
792  /* i18n-hint: Clicking this menu item shows the toolbar with the big buttons on it (play record etc)*/
793  c->AddCheck(wxT("ShowTransportTB"), XXO("&Transport Toolbar"), FN(OnShowTransportToolBar), 0, AlwaysEnabledFlag, AlwaysEnabledFlag);
794  /* i18n-hint: Clicking this menu item shows a toolbar that has some tools in it*/
795  c->AddCheck(wxT("ShowToolsTB"), XXO("T&ools Toolbar"), FN(OnShowToolsToolBar), 0, AlwaysEnabledFlag, AlwaysEnabledFlag);
796  /* i18n-hint: Clicking this menu item shows the toolbar with the recording level meters*/
797  c->AddCheck(wxT("ShowRecordMeterTB"), XXO("&Recording Meter Toolbar"), FN(OnShowRecordMeterToolBar), 0, AlwaysEnabledFlag, AlwaysEnabledFlag);
798  /* i18n-hint: Clicking this menu item shows the toolbar with the playback level meter*/
799  c->AddCheck(wxT("ShowPlayMeterTB"), XXO("&Playback Meter Toolbar"), FN(OnShowPlayMeterToolBar), 0, AlwaysEnabledFlag, AlwaysEnabledFlag);
800  /* --i18n-hint: Clicking this menu item shows the toolbar which has sound level meters*/
801  //c->AddCheck(wxT("ShowMeterTB"), XXO("Co&mbined Meter Toolbar"), FN(OnShowMeterToolBar), 0, AlwaysEnabledFlag, AlwaysEnabledFlag);
802  /* i18n-hint: Clicking this menu item shows the toolbar with the mixer*/
803  c->AddCheck(wxT("ShowMixerTB"), XXO("Mi&xer Toolbar"), FN(OnShowMixerToolBar), 0, AlwaysEnabledFlag, AlwaysEnabledFlag);
804  /* i18n-hint: Clicking this menu item shows the toolbar for editing*/
805  c->AddCheck(wxT("ShowEditTB"), XXO("&Edit Toolbar"), FN(OnShowEditToolBar), 0, AlwaysEnabledFlag, AlwaysEnabledFlag);
806  /* i18n-hint: Clicking this menu item shows the toolbar for transcription (currently just vary play speed)*/
807  c->AddCheck(wxT("ShowTranscriptionTB"), XXO("Tra&nscription Toolbar"), FN(OnShowTranscriptionToolBar), 0, AlwaysEnabledFlag, AlwaysEnabledFlag);
808  /* i18n-hint: Clicking this menu item shows the toolbar that enables Scrub or Seek playback and Scrub Ruler*/
809  c->AddCheck(wxT("ShowScrubbingTB"), XXO("Scru&b Toolbar"), FN(OnShowScrubbingToolBar), 0, AlwaysEnabledFlag, AlwaysEnabledFlag);
810  /* i18n-hint: Clicking this menu item shows the toolbar that manages devices*/
811  c->AddCheck(wxT("ShowDeviceTB"), XXO("&Device Toolbar"), FN(OnShowDeviceToolBar), 0, AlwaysEnabledFlag, AlwaysEnabledFlag);
812  /* i18n-hint: Clicking this menu item shows the toolbar for selecting a time range of audio*/
813  c->AddCheck(wxT("ShowSelectionTB"), XXO("&Selection Toolbar"), FN(OnShowSelectionToolBar), 0, AlwaysEnabledFlag, AlwaysEnabledFlag);
814 #ifdef EXPERIMENTAL_SPECTRAL_EDITING
815  /* i18n-hint: Clicking this menu item shows the toolbar for selecting a frequency range of audio*/
816  c->AddCheck(wxT("ShowSpectralSelectionTB"), XXO("Spe&ctral Selection Toolbar"), FN(OnShowSpectralSelectionToolBar), 0, AlwaysEnabledFlag, AlwaysEnabledFlag);
817 #endif
818 
819  c->EndSubMenu();
820 
821  c->AddSeparator();
822 
823  c->AddCheck(wxT("ShowExtraMenus"), XXO("&Extra Menus (on/off)"), FN(OnShowExtraMenus),
824  gPrefs->Read(wxT("/GUI/ShowExtraMenus"), 0L), AlwaysEnabledFlag, AlwaysEnabledFlag);
825  c->AddCheck(wxT("ShowClipping"), XXO("&Show Clipping (on/off)"), FN(OnShowClipping),
826  gPrefs->Read(wxT("/GUI/ShowClipping"), 0L), AlwaysEnabledFlag, AlwaysEnabledFlag);
827 #if defined(EXPERIMENTAL_EFFECTS_RACK)
828  c->AddCheck(wxT("ShowEffectsRack"), XXO("Show Effects Rack"), FN(OnShowEffectsRack), 0, AlwaysEnabledFlag, AlwaysEnabledFlag);
829 #endif
830 
831 
832  c->EndMenu();
833 
835  // Transport Menu
837 
838  /*i18n-hint: 'Transport' is the name given to the set of controls that
839  play, record, pause etc. */
840  c->BeginMenu(_("Tra&nsport"));
842  c->BeginSubMenu(_("Pl&ay"));
843  /* i18n-hint: (verb) Start or Stop audio playback*/
844  c->AddItem(wxT("PlayStop"), XXO("Pl&ay/Stop"), FN(OnPlayStop), wxT("Space"));
845  c->AddItem(wxT("PlayStopSelect"), XXO("Play/Stop and &Set Cursor"), FN(OnPlayStopSelect), wxT("X"));
846  c->AddItem(wxT("PlayLooped"), XXO("&Loop Play"), FN(OnPlayLooped), wxT("Shift+Space"),
849  c->AddItem(wxT("Pause"), XXO("&Pause"), FN(OnPause), wxT("P"));
850  c->EndSubMenu();
851 
852  c->BeginSubMenu( _("&Record"));
855  /* i18n-hint: (verb)*/
856  c->AddItem(wxT("Record1stChoice"), XXO("&Record"), FN(OnRecord), wxT("R"));
857  // The OnRecord2ndChoice function is: if normal record records beside,
858  // it records below, if normal record records below, it records beside.
859  // TODO: Do 'the right thing' with other options like TimerRecord.
860  bool bPreferNewTrack;
861  gPrefs->Read("/GUI/PreferNewTrackRecord",&bPreferNewTrack, false);
862  c->AddItem( wxT("Record2ndChoice"),
863  // Our first choice is bound to R (by default) and gets the prime position.
864  // We supply the name for the 'other one' here. It should be bound to Shift+R
865  (bPreferNewTrack ? _("&Append Record") : _("Record &New Track")), false,
867  wxT("Shift+R")
868  );
869 
870  c->AddItem(wxT("TimerRecord"), XXO("&Timer Record..."), FN(OnTimerRecord), wxT("Shift+T"));
871  // JKC: I decided to duplicate this between play and record, rather than put it
872  // at the top level. AddItem can now cope with simple duplicated items.
873  // PRL: This second registration of wxT("Pause"), with unspecified flags,
874  // in fact will use the same flags as in the previous registration.
875  c->AddItem(wxT("Pause"), XXO("&Pause"), FN(OnPause), wxT("P"));
876  c->EndSubMenu();
877 
878  // Scrubbing sub-menu
880 
881  // JKC: ANSWER-ME: How is 'cursor to' different to 'Skip To' and how is it useful?
882  // GA: 'Skip to' moves the viewpoint to center of the track and preserves the
883  // selection. 'Cursor to' does neither. 'Center at' might describe it better than 'Skip'.
884  c->BeginSubMenu(_("&Cursor to"));
885 
886  c->SetLongName( _("Cursor to Selection Start"))->AddItem(wxT("CursSelStart"), XXO("Selection Star&t"), FN(OnCursorSelStart),
888  c->SetLongName( _("Cursor to Selection End"))->AddItem(wxT("CursSelEnd"), XXO("Selection En&d"), FN(OnCursorSelEnd),
890 
891  c->SetLongName( _("Cursor to Track Start"))->AddItem(wxT("CursTrackStart"), XXO("Track &Start"), FN(OnCursorTrackStart), wxT("J"),
893  c->SetLongName( _("Cursor to Track End"))->AddItem(wxT("CursTrackEnd"), XXO("Track &End"), FN(OnCursorTrackEnd), wxT("K"),
895 
896  c->SetLongName( _("Cursor to Prev Clip Boundary"))->AddItem(wxT("CursPrevClipBoundary"), XXO("Pre&vious Clip Boundary"), FN(OnCursorPrevClipBoundary), wxT(""),
898  c->SetLongName( _("Cursor to Next Clip Boundary"))->AddItem(wxT("CursNextClipBoundary"), XXO("Ne&xt Clip Boundary"), FN(OnCursorNextClipBoundary), wxT(""),
900 
901  c->SetLongName( _("Cursor to Project Start"))->AddItem(wxT("CursProjectStart"), XXO("&Project Start"), FN(OnSkipStart), wxT("Home"));
902  c->SetLongName( _("Cursor to Project End"))->AddItem(wxT("CursProjectEnd"), XXO("Project E&nd"), FN(OnSkipEnd), wxT("End"));
903 
904  c->EndSubMenu();
905 
906  c->AddSeparator();
907 
909 
910  c->BeginSubMenu(_("Pla&y Region"));
911 
912  c->AddItem(wxT("LockPlayRegion"), XXO("&Lock"), FN(OnLockPlayRegion),
915  c->AddItem(wxT("UnlockPlayRegion"), XXO("&Unlock"), FN(OnUnlockPlayRegion),
918 
919  c->EndSubMenu();
920 
921  c->AddSeparator();
922 
923  c->AddItem(wxT("RescanDevices"), XXO("R&escan Audio Devices"), FN(OnRescanDevices),
926 
927  c->BeginSubMenu(_("Transport &Options"));
928  // Sound Activated recording options
929  c->AddItem(wxT("SoundActivationLevel"), XXO("Sound Activation Le&vel..."), FN(OnSoundActivated),
932  c->AddCheck(wxT("SoundActivation"), XXO("Sound A&ctivated Recording (on/off)"), FN(OnToggleSoundActivated), 0,
935  c->AddSeparator();
936 
937  c->AddCheck(wxT("PinnedHead"), XXO("Pinned Play/Record &Head (on/off)"),
939  // Switching of scrolling on and off is permitted even during transport
941 
942  c->AddCheck(wxT("Duplex"), XXO("&Overdub (on/off)"), FN(OnTogglePlayRecording), 0,
945  c->AddCheck(wxT("SWPlaythrough"), XXO("So&ftware Playthrough (on/off)"), FN(OnToggleSWPlaythrough), 0,
948 
949 
950 #ifdef EXPERIMENTAL_AUTOMATED_INPUT_LEVEL_ADJUSTMENT
951  c->AddCheck(wxT("AutomatedInputLevelAdjustmentOnOff"), XXO("A&utomated Recording Level Adjustment (on/off)"), FN(OnToggleAutomatedInputLevelAdjustment), 0,
954 #endif
955  c->EndSubMenu();
956 
957  c->EndMenu();
958 
960  // Tracks Menu (formerly Project Menu)
962 
963  c->BeginMenu(_("&Tracks"));
965 
967 
968  c->BeginSubMenu(_("Add &New"));
969 
970  c->AddItem(wxT("NewMonoTrack"), XXO("&Mono Track"), FN(OnNewWaveTrack), wxT("Ctrl+Shift+N"));
971  c->AddItem(wxT("NewStereoTrack"), XXO("&Stereo Track"), FN(OnNewStereoTrack));
972  c->AddItem(wxT("NewLabelTrack"), XXO("&Label Track"), FN(OnNewLabelTrack));
973  c->AddItem(wxT("NewTimeTrack"), XXO("&Time Track"), FN(OnNewTimeTrack));
974 
975  c->EndSubMenu();
976 
978 
979  c->AddSeparator();
980 
981  c->BeginSubMenu(_("Mi&x") );
982  {
983  // Stereo to Mono is an oddball command that is also subject to control by the
984  // plug-in manager, as if an effect. Decide whether to show or hide it.
985  const PluginID ID = EffectManager::Get().GetEffectByIdentifier(wxT("StereoToMono"));
986  const PluginDescriptor *plug = PluginManager::Get().GetPlugin(ID);
987  if (plug && plug->IsEnabled())
988  c->AddItem(wxT("Stereo to Mono"), XXO("Mix Stereo Down to &Mono"), FN(OnStereoToMono),
991  }
992  c->AddItem(wxT("MixAndRender"), XXO("Mi&x and Render"), FN(OnMixAndRender),
995  c->AddItem(wxT("MixAndRenderToNewTrack"), XXO("Mix and Render to Ne&w Track"), FN(OnMixAndRenderToNewTrack), wxT("Ctrl+Shift+M"),
998  c->EndSubMenu();
999 
1000  c->AddItem(wxT("Resample"), XXO("&Resample..."), FN(OnResample),
1003 
1004  c->AddSeparator();
1005 
1006  c->AddItem(wxT("RemoveTracks"), XXO("Remo&ve Tracks"), FN(OnRemoveTracks),
1009 
1010  c->AddSeparator();
1011 
1012  c->BeginSubMenu(_("M&ute/Unmute"));
1013  c->AddItem(wxT("MuteAllTracks"), XXO("&Mute All Tracks"), FN(OnMuteAllTracks), wxT("Ctrl+U"));
1014  c->AddItem(wxT("UnmuteAllTracks"), XXO("&Unmute All Tracks"), FN(OnUnmuteAllTracks), wxT("Ctrl+Shift+U"));
1015  c->EndSubMenu();
1016 
1017  c->BeginSubMenu(_("&Pan"));
1018  // As Pan changes are not saved on Undo stack, pan settings for all tracks
1019  // in the project could very easily be lost unless we require the tracks to be selcted.
1021  c->SetLongName( _("Pan Left"))->AddItem(wxT("PanLeft"), XXO("&Left"), FN(OnPanLeft));
1022  c->SetLongName( _("Pan Right"))->AddItem(wxT("PanRight"), XXO("&Right"), FN(OnPanRight));
1023  c->SetLongName( _("Pan Center"))->AddItem(wxT("PanCenter"), XXO("&Center"), FN(OnPanCenter));
1024  c->EndSubMenu();
1025 
1026 
1027  c->AddSeparator();
1028 
1030 
1031  const TranslatedInternalString alignLabelsNoSync[] = {
1032  { wxT("EndToEnd"), _("&Align End to End") },
1033  { wxT("Together"), _("Align &Together") },
1034  };
1035 
1036  const TranslatedInternalString alignLabels[] = {
1037  { wxT("StartToZero"), _("Start to &Zero") },
1038  { wxT("StartToSelStart"), _("Start to &Cursor/Selection Start") },
1039  { wxT("StartToSelEnd"), _("Start to Selection &End") },
1040  { wxT("EndToSelStart"), _("End to Cu&rsor/Selection Start") },
1041  { wxT("EndToSelEnd"), _("End to Selection En&d") },
1042  };
1043  mAlignLabelsCount = sizeof(alignLabels) / sizeof(alignLabels[0]);
1044 
1045  // Calling c->SetCommandFlags() after AddItemList for "Align" and "AlignMove"
1046  // does not correctly set flags for submenus, so do it this way.
1049 
1050  c->BeginSubMenu(_("&Align Tracks"));
1051 
1052  //c->BeginSubMenu(_("Just Move Tracks"));
1053  c->AddItemList(wxT("Align"), alignLabelsNoSync, 2u, FN(OnAlignNoSync));
1054  c->AddSeparator();
1055  c->AddItemList(wxT("Align"), alignLabels, mAlignLabelsCount, FN(OnAlign));
1056  c->AddSeparator();
1057  c->AddCheck(wxT("MoveSelectionWithTracks"), XXO("&Move Selection with Tracks (on/off)"),
1059  gPrefs->Read(wxT("/GUI/MoveSelectionWithTracks"), 0L),
1061  c->EndSubMenu();
1062 
1063 #if 0
1064  // TODO: Can these labels be made clearer? Do we need this sub-menu at all?
1065  c->BeginSubMenu(_("Move Sele&ction and Tracks"));
1066 
1067  c->AddItemList(wxT("AlignMove"), alignLabels, mAlignLabelsCount, FN(OnAlignMoveSel));
1068  c->SetCommandFlags(wxT("AlignMove"),
1071 
1072  c->EndSubMenu();
1073 #endif
1074 
1076 
1077 
1079 
1080 #ifdef EXPERIMENTAL_SCOREALIGN
1081  c->AddItem(wxT("ScoreAlign"), XXO("Synchronize MIDI with Audio"), FN(OnScoreAlign),
1084 #endif // EXPERIMENTAL_SCOREALIGN
1085 
1087 
1088  c->BeginSubMenu(_("S&ort Tracks"));
1089 
1090  c->SetLongName( _("Sort by Time"))->AddItem(wxT("SortByTime"), XXO("By &Start Time"), FN(OnSortTime),
1092  TracksExistFlag);
1093  c->SetLongName( _("Sort by Name"))->AddItem(wxT("SortByName"), XXO("By &Name"), FN(OnSortName),
1095  TracksExistFlag);
1096 
1097  c->EndSubMenu();
1098 
1100 
1101 #ifdef EXPERIMENTAL_SYNC_LOCK
1102  c->AddSeparator();
1103  c->AddCheck(wxT("SyncLock"), XXO("Sync-&Lock Tracks (on/off)"), FN(OnSyncLock),
1104  gPrefs->Read(wxT("/GUI/SyncLockTracks"), 0L),
1106 
1107 #endif
1108 
1109  c->EndMenu();
1110 
1111  // All of this is a bit hacky until we can get more things connected into
1112  // the plugin manager...sorry! :-(
1113 
1114  wxArrayString defaults;
1115 
1117  // Generate Menu
1119 
1120  c->BeginMenu(_("&Generate"));
1122 
1123 #ifdef EXPERIMENTAL_EFFECT_MANAGEMENT
1124  c->AddItem(wxT("ManageGenerators"), XXO("Add / Remove Plug-ins..."), FN(OnManageGenerators));
1125  c->AddSeparator();
1126 #endif
1127 
1128 
1133 
1134  c->EndMenu();
1135 
1137  // Effect Menu
1139 
1140  c->BeginMenu(_("Effe&ct"));
1141 
1142  wxString buildMenuLabel;
1143  if (!mLastEffect.IsEmpty()) {
1144  buildMenuLabel.Printf(_("Repeat %s"),
1145  EffectManager::Get().GetCommandName(mLastEffect));
1146  }
1147  else
1148  buildMenuLabel = _("Repeat Last Effect");
1149 
1150 #ifdef EXPERIMENTAL_EFFECT_MANAGEMENT
1151  c->AddItem(wxT("ManageEffects"), XXO("Add / Remove Plug-ins..."), FN(OnManageEffects));
1152  c->AddSeparator();
1153 #endif
1154 
1155  c->AddItem(wxT("RepeatLastEffect"), buildMenuLabel, false, FN(OnRepeatLastEffect), wxT("Ctrl+R"),
1158 
1159  c->AddSeparator();
1160 
1165 
1166  c->EndMenu();
1167 
1169  // Analyze Menu
1171 
1172  c->BeginMenu(_("&Analyze"));
1173 
1174 #ifdef EXPERIMENTAL_EFFECT_MANAGEMENT
1175  c->AddItem(wxT("ManageAnalyzers"), XXO("Add / Remove Plug-ins..."), FN(OnManageAnalyzers));
1176  c->AddSeparator();
1177 #endif
1178 
1179 
1180  c->AddItem(wxT("ContrastAnalyser"), XXO("Contrast..."), FN(OnContrast), wxT("Ctrl+Shift+T"),
1183  c->AddItem(wxT("PlotSpectrum"), XXO("Plot Spectrum..."), FN(OnPlotSpectrum),
1186 
1191 
1192  c->EndMenu();
1193 
1195  // Tools Menu
1197 
1198  c->BeginMenu(_("T&ools"));
1199 
1200 #ifdef EXPERIMENTAL_EFFECT_MANAGEMENT
1201  c->AddItem(wxT("ManageTools"), XXO("Add / Remove Plug-ins..."), FN(OnManageTools));
1202  //c->AddSeparator();
1203 #endif
1204 
1205  c->AddItem(wxT("ManageMacros"), XXO("&Macros..."), FN(OnManageMacros));
1206 
1207  c->BeginSubMenu(_("&Apply Macro"));
1208  c->AddItem(wxT("ApplyMacrosPalette"), XXO("&Palette..."), FN(OnApplyMacrosPalette));
1209  c->AddSeparator();
1211  c->EndSubMenu();
1212  c->AddSeparator();
1213 
1214  c->AddItem(wxT("FancyScreenshot"), XXO("&Screenshot..."), FN(OnScreenshot));
1215 
1216 // PRL: team consensus for 2.2.0 was, we let end users have this diagnostic,
1217 // as they used to in 1.3.x
1218 //#ifdef IS_ALPHA
1219  // TODO: What should we do here? Make benchmark a plug-in?
1220  // Easy enough to do. We'd call it mod-self-test.
1221  c->AddItem(wxT("Benchmark"), XXO("&Run Benchmark..."), FN(OnBenchmark));
1222 //#endif
1223 
1224 #ifdef IS_ALPHA
1225  c->AddCheck(wxT("SimulateRecordingErrors"),
1226  XXO("Simulate Recording Errors"),
1229  c->AddCheck(wxT("DetectUpstreamDropouts"),
1230  XXO("Detect Upstream Dropouts"),
1233 #endif
1234  c->AddSeparator();
1235 
1240 
1241  c->EndMenu();
1242 
1243 
1244 #ifdef __WXMAC__
1245  // poor imitation of the Mac Windows Menu
1248 
1249  {
1250  c->BeginMenu(_("&Window"));
1251  /* i18n-hint: Standard Macintosh Window menu item: Make (the current
1252  * window) shrink to an icon on the dock */
1253  c->AddItem(wxT("MacMinimize"), XXO("&Minimize"), FN(OnMacMinimize),
1254  wxT("Ctrl+M"), NotMinimizedFlag, NotMinimizedFlag);
1255  /* i18n-hint: Standard Macintosh Window menu item: Make (the current
1256  * window) full sized */
1257  c->AddItem(wxT("MacZoom"), XXO("&Zoom"), FN(OnMacZoom),
1259  c->AddSeparator();
1260  /* i18n-hint: Standard Macintosh Window menu item: Make all project
1261  * windows un-hidden */
1262  c->AddItem(wxT("MacBringAllToFront"),
1263  XXO("&Bring All to Front"), FN(OnMacBringAllToFront),
1265  c->EndMenu();
1266  }
1267 #endif
1268 
1270  // Help Menu
1272 
1273 #ifdef __WXMAC__
1274  wxGetApp().s_macHelpMenuTitleName = _("&Help");
1275 #endif
1276 
1277  c->BeginMenu(_("&Help"));
1279 
1280 // DA: Emphasise it is the Audacity Manual (No separate DA manual).
1281 #ifdef EXPERIMENTAL_DA
1282  // 'Getting Started' rather than 'Quick Help' for DarkAudacity.
1283  // At the moment the video tutorials are aspirational (aka do not exist yet).
1284  // Emphasise that manual is for Audacity, not DarkAudacity.
1285  c->AddItem(wxT("QuickHelp"), XXO("&Getting Started"), FN(OnQuickHelp));
1286  c->AddItem(wxT("Manual"), XXO("Audacity &Manual"), FN(OnManual));
1287 #else
1288  c->AddItem(wxT("QuickHelp"), XXO("&Quick Help..."), FN(OnQuickHelp));
1289  c->AddItem(wxT("Manual"), XXO("&Manual..."), FN(OnManual));
1290 #endif
1291  c->AddSeparator();
1292 
1293  c->BeginSubMenu(_("&Diagnostics"));
1294  c->AddItem(wxT("DeviceInfo"), XXO("Au&dio Device Info..."), FN(OnAudioDeviceInfo),
1297 #ifdef EXPERIMENTAL_MIDI_OUT
1298  c->AddItem(wxT("MidiDeviceInfo"), XXO("&MIDI Device Info..."), FN(OnMidiDeviceInfo),
1301 #endif
1302 
1303  c->AddItem(wxT("Log"), XXO("Show &Log..."), FN(OnShowLog));
1304 
1305 #if defined(EXPERIMENTAL_CRASH_REPORT)
1306  c->AddItem(wxT("CrashReport"), XXO("&Generate Support Data..."), FN(OnCrashReport));
1307 #endif
1308  c->AddItem(wxT("CheckDeps"), XXO("Chec&k Dependencies..."), FN(OnCheckDependencies),
1310  c->EndSubMenu();
1311 
1312 #ifndef __WXMAC__
1313  c->AddSeparator();
1314 #endif
1315 
1316 // DA: Does not fully support update checking.
1317 #ifndef EXPERIMENTAL_DA
1318  c->AddItem(wxT("Updates"), XXO("&Check for Updates..."), FN(OnCheckForUpdates));
1319 #endif
1320  c->AddItem(wxT("About"), XXO("&About Audacity..."), FN(OnAbout));
1321 
1322  c->EndMenu();
1323 
1325 
1326 
1328 
1329  bool bShowExtraMenus;
1330  gPrefs->Read(wxT("/GUI/ShowExtraMenus"), &bShowExtraMenus, false);
1331  std::unique_ptr<wxMenuBar> menubar2;
1332  if( !bShowExtraMenus )
1333  {
1334  menubar2 = c->AddMenuBar(wxT("ext-menu"));
1335  c->SetOccultCommands(true);
1336  }
1337 
1339  // Ext-Menu
1341 
1342  // i18n-hint: Extra is a menu with extra commands
1343  c->BeginMenu(_("Ext&ra"));
1344 
1346 
1348  c->BeginSubMenu(_("T&ransport"));
1349 
1350  // PlayStop is already in the menus.
1351  /* i18n-hint: (verb) Start playing audio*/
1352  c->AddItem(wxT("Play"), XXO("Pl&ay"), FN(OnPlayStop),
1355  /* i18n-hint: (verb) Stop playing audio*/
1356  c->AddItem(wxT("Stop"), XXO("Sto&p"), FN(OnStop),
1357  AudioIOBusyFlag | CanStopAudioStreamFlag,
1358  AudioIOBusyFlag | CanStopAudioStreamFlag);
1359 
1361 
1362  c->AddItem(wxT("PlayOneSec"), XXO("Play &One Second"), FN(OnPlayOneSecond), wxT("1"),
1365  c->AddItem(wxT("PlayToSelection"), XXO("Play to &Selection"), FN(OnPlayToSelection), wxT("B"),
1368  c->AddItem(wxT("PlayBeforeSelectionStart"), XXO("Play &Before Selection Start"), FN(OnPlayBeforeSelectionStart), wxT("Shift+F5"));
1369  c->AddItem(wxT("PlayAfterSelectionStart"), XXO("Play Af&ter Selection Start"), FN(OnPlayAfterSelectionStart), wxT("Shift+F6"));
1370  c->AddItem(wxT("PlayBeforeSelectionEnd"), XXO("Play Be&fore Selection End"), FN(OnPlayBeforeSelectionEnd), wxT("Shift+F7"));
1371  c->AddItem(wxT("PlayAfterSelectionEnd"), XXO("Play Aft&er Selection End"), FN(OnPlayAfterSelectionEnd), wxT("Shift+F8"));
1372  c->AddItem(wxT("PlayBeforeAndAfterSelectionStart"), XXO("Play Before a&nd After Selection Start"), FN(OnPlayBeforeAndAfterSelectionStart), wxT("Ctrl+Shift+F5"));
1373  c->AddItem(wxT("PlayBeforeAndAfterSelectionEnd"), XXO("Play Before an&d After Selection End"), FN(OnPlayBeforeAndAfterSelectionEnd), wxT("Ctrl+Shift+F7"));
1374  c->AddItem(wxT("PlayCutPreview"), XXO("Play C&ut Preview"), FN(OnPlayCutPreview), wxT("C"),
1377  c->EndSubMenu();
1378 
1380 
1382  c->BeginSubMenu(_("T&ools"));
1383 
1384  c->AddItem(wxT("SelectTool"), XXO("&Selection Tool"), FN(OnSelectTool), wxT("F1"));
1385  c->AddItem(wxT("EnvelopeTool"), XXO("&Envelope Tool"), FN(OnEnvelopeTool), wxT("F2"));
1386  c->AddItem(wxT("DrawTool"), XXO("&Draw Tool"), FN(OnDrawTool), wxT("F3"));
1387  c->AddItem(wxT("ZoomTool"), XXO("&Zoom Tool"), FN(OnZoomTool), wxT("F4"));
1388  c->AddItem(wxT("TimeShiftTool"), XXO("&Time Shift Tool"), FN(OnTimeShiftTool), wxT("F5"));
1389  c->AddItem(wxT("MultiTool"), XXO("&Multi Tool"), FN(OnMultiTool), wxT("F6"));
1390 
1391  c->AddItem(wxT("PrevTool"), XXO("&Previous Tool"), FN(OnPrevTool), wxT("A"));
1392  c->AddItem(wxT("NextTool"), XXO("&Next Tool"), FN(OnNextTool), wxT("D"));
1393  c->EndSubMenu();
1394 
1396 
1398  c->BeginSubMenu(_("Mi&xer"));
1399 
1400  c->AddItem(wxT("OutputGain"), XXO("Ad&just Playback Volume..."), FN(OnOutputGain));
1401  c->AddItem(wxT("OutputGainInc"), XXO("&Increase Playback Volume"), FN(OnOutputGainInc));
1402  c->AddItem(wxT("OutputGainDec"), XXO("&Decrease Playback Volume"), FN(OnOutputGainDec));
1403  c->AddItem(wxT("InputGain"), XXO("Adj&ust Recording Volume..."), FN(OnInputGain));
1404  c->AddItem(wxT("InputGainInc"), XXO("I&ncrease Recording Volume"), FN(OnInputGainInc));
1405  c->AddItem(wxT("InputGainDec"), XXO("D&ecrease Recording Volume"), FN(OnInputGainDec));
1406  c->EndSubMenu();
1407 
1409 
1411  c->BeginSubMenu(_("&Edit"));
1412 
1413  c->AddItem(wxT("DeleteKey"), XXO("&Delete Key"), FN(OnDelete), wxT("Backspace"),
1416 
1417  c->AddItem(wxT("DeleteKey2"), XXO("Delete Key&2"), FN(OnDelete), wxT("Delete"),
1420  c->EndSubMenu();
1421 
1423 
1425  c->BeginSubMenu(_("Transcri&ption"));
1426 
1427  c->AddItem(wxT("PlayAtSpeed"), XXO("Pl&ay-at-Speed"), FN(OnPlayAtSpeed));
1428  c->AddItem(wxT("PlayAtSpeedLooped"), XXO("&Loop Play-at-Speed"), FN(OnPlayAtSpeedLooped));
1429  c->AddItem(wxT("PlayAtSpeedCutPreview"), XXO("Play C&ut Preview-at-Speed"), FN(OnPlayAtSpeedCutPreview));
1430  c->AddItem(wxT("SetPlaySpeed"), XXO("Ad&just Playback Speed..."), FN(OnSetPlaySpeed));
1431  c->AddItem(wxT("PlaySpeedInc"), XXO("&Increase Playback Speed"), FN(OnPlaySpeedInc));
1432  c->AddItem(wxT("PlaySpeedDec"), XXO("&Decrease Playback Speed"), FN(OnPlaySpeedDec));
1433 
1434  // These were on the original transcription toolbar. But they are not on the
1435  // shortened one.
1436  c->AddItem(wxT("MoveToPrevLabel"), XXO("Move to &Previous Label"), FN(OnMoveToPrevLabel), wxT("Alt+Left"),
1438  c->AddItem(wxT("MoveToNextLabel"), XXO("Move to &Next Label"), FN(OnMoveToNextLabel), wxT("Alt+Right"),
1440  c->EndSubMenu();
1441 
1443 
1444  c->SetDefaultFlags(AudioIOBusyFlag, AudioIOBusyFlag);
1445  c->BeginSubMenu(_("See&k"));
1446 
1447  c->AddItem(wxT("SeekLeftShort"), XXO("Short Seek &Left During Playback"), FN(OnSeekLeftShort), wxT("Left\tallowDup"));
1448  c->AddItem(wxT("SeekRightShort"), XXO("Short Seek &Right During Playback"), FN(OnSeekRightShort), wxT("Right\tallowDup"));
1449  c->AddItem(wxT("SeekLeftLong"), XXO("Long Seek Le&ft During Playback"), FN(OnSeekLeftLong), wxT("Shift+Left\tallowDup"));
1450  c->AddItem(wxT("SeekRightLong"), XXO("Long Seek Rig&ht During Playback"), FN(OnSeekRightLong), wxT("Shift+Right\tallowDup"));
1451  c->EndSubMenu();
1452 
1454 
1456  c->BeginSubMenu(_("De&vice"));
1457 
1458  c->AddItem(wxT("InputDevice"), XXO("Change &Recording Device..."), FN(OnInputDevice), wxT("Shift+I"),
1461  c->AddItem(wxT("OutputDevice"), XXO("Change &Playback Device..."), FN(OnOutputDevice), wxT("Shift+O"),
1464  c->AddItem(wxT("AudioHost"), XXO("Change Audio &Host..."), FN(OnAudioHost), wxT("Shift+H"),
1467  c->AddItem(wxT("InputChannels"), XXO("Change Recording Cha&nnels..."), FN(OnInputChannels), wxT("Shift+N"),
1470  c->EndSubMenu();
1471 
1473 
1475  c->BeginSubMenu(_("&Selection"));
1476 
1477  c->AddItem(wxT("SnapToOff"), XXO("Snap-To &Off"), FN(OnSnapToOff));
1478  c->AddItem(wxT("SnapToNearest"), XXO("Snap-To &Nearest"), FN(OnSnapToNearest));
1479  c->AddItem(wxT("SnapToPrior"), XXO("Snap-To &Prior"), FN(OnSnapToPrior));
1480 
1481  c->AddItem(wxT("SelStart"), XXO("Selection to &Start"), FN(OnSelToStart), wxT("Shift+Home"));
1482  c->AddItem(wxT("SelEnd"), XXO("Selection to En&d"), FN(OnSelToEnd), wxT("Shift+End"));
1483  c->AddItem(wxT("SelExtLeft"), XXO("Selection Extend &Left"), FN(OnSelExtendLeft), wxT("Shift+Left\twantKeyup\tallowDup"),
1486  c->AddItem(wxT("SelExtRight"), XXO("Selection Extend &Right"), FN(OnSelExtendRight), wxT("Shift+Right\twantKeyup\tallowDup"),
1489 
1490  c->AddItem(wxT("SelSetExtLeft"), XXO("Set (or Extend) Le&ft Selection"), FN(OnSelSetExtendLeft),
1493  c->AddItem(wxT("SelSetExtRight"), XXO("Set (or Extend) Rig&ht Selection"), FN(OnSelSetExtendRight),
1496 
1497  c->AddItem(wxT("SelCntrLeft"), XXO("Selection Contract L&eft"), FN(OnSelContractLeft), wxT("Ctrl+Shift+Right\twantKeyup"),
1500  c->AddItem(wxT("SelCntrRight"), XXO("Selection Contract R&ight"), FN(OnSelContractRight), wxT("Ctrl+Shift+Left\twantKeyup"),
1503 
1504  c->EndSubMenu();
1505 
1506 
1508  c->AddSeparator();
1509 
1510  c->AddGlobalCommand(wxT("PrevWindow"), XXO("Move Backward Through Active Windows"), FN(PrevWindow), wxT("Alt+Shift+F6"));
1511  c->AddGlobalCommand(wxT("NextWindow"), XXO("Move Forward Through Active Windows"), FN(NextWindow), wxT("Alt+F6"));
1512 
1514 
1516  c->BeginSubMenu(_("F&ocus"));
1517 
1518  c->AddItem(wxT("PrevFrame"), XXO("Move &Backward from Toolbars to Tracks"), FN(PrevFrame), wxT("Ctrl+Shift+F6"));
1519  c->AddItem(wxT("NextFrame"), XXO("Move F&orward from Toolbars to Tracks"), FN(NextFrame), wxT("Ctrl+F6"));
1520 
1523  c->AddItem(wxT("PrevTrack"), XXO("Move Focus to &Previous Track"), FN(OnCursorUp), wxT("Up"));
1524  c->AddItem(wxT("NextTrack"), XXO("Move Focus to &Next Track"), FN(OnCursorDown), wxT("Down"));
1525  c->AddItem(wxT("FirstTrack"), XXO("Move Focus to &First Track"), FN(OnFirstTrack), wxT("Ctrl+Home"));
1526  c->AddItem(wxT("LastTrack"), XXO("Move Focus to &Last Track"), FN(OnLastTrack), wxT("Ctrl+End"));
1527 
1528  c->AddItem(wxT("ShiftUp"), XXO("Move Focus to P&revious and Select"), FN(OnShiftUp), wxT("Shift+Up"));
1529  c->AddItem(wxT("ShiftDown"), XXO("Move Focus to N&ext and Select"), FN(OnShiftDown), wxT("Shift+Down"));
1530 
1531  c->AddItem(wxT("Toggle"), XXO("&Toggle Focused Track"), FN(OnToggle), wxT("Return"));
1532  c->AddItem(wxT("ToggleAlt"), XXO("Toggle Focuse&d Track"), FN(OnToggle), wxT("NUMPAD_ENTER"));
1533  c->EndSubMenu();
1534 
1536 
1538  c->BeginSubMenu(_("&Cursor"));
1539 
1540  c->AddItem(wxT("CursorLeft"), XXO("Cursor &Left"), FN(OnCursorLeft), wxT("Left\twantKeyup\tallowDup"),
1543  c->AddItem(wxT("CursorRight"), XXO("Cursor &Right"), FN(OnCursorRight), wxT("Right\twantKeyup\tallowDup"),
1546  c->AddItem(wxT("CursorShortJumpLeft"), XXO("Cursor Sh&ort Jump Left"), FN(OnCursorShortJumpLeft), wxT(","),
1549  c->AddItem(wxT("CursorShortJumpRight"), XXO("Cursor Shor&t Jump Right"), FN(OnCursorShortJumpRight), wxT("."),
1552  c->AddItem(wxT("CursorLongJumpLeft"), XXO("Cursor Long J&ump Left"), FN(OnCursorLongJumpLeft), wxT("Shift+,"),
1555  c->AddItem(wxT("CursorLongJumpRight"), XXO("Cursor Long Ju&mp Right"), FN(OnCursorLongJumpRight), wxT("Shift+."),
1558 
1559  c->AddItem(wxT("ClipLeft"), XXO("Clip L&eft"), FN(OnClipLeft), wxT("\twantKeyup"),
1562  c->AddItem(wxT("ClipRight"), XXO("Clip Rig&ht"), FN(OnClipRight), wxT("\twantKeyup"),
1565  c->EndSubMenu();
1566 
1568 
1570  c->BeginSubMenu(_("&Track"));
1571 
1572  c->AddItem(wxT("TrackPan"), XXO("Change P&an on Focused Track..."), FN(OnTrackPan), wxT("Shift+P"),
1575  c->AddItem(wxT("TrackPanLeft"), XXO("Pan &Left on Focused Track"), FN(OnTrackPanLeft), wxT("Alt+Shift+Left"),
1578  c->AddItem(wxT("TrackPanRight"), XXO("Pan &Right on Focused Track"), FN(OnTrackPanRight), wxT("Alt+Shift+Right"),
1581  c->AddItem(wxT("TrackGain"), XXO("Change Gai&n on Focused Track..."), FN(OnTrackGain), wxT("Shift+G"),
1584  c->AddItem(wxT("TrackGainInc"), XXO("&Increase Gain on Focused Track"), FN(OnTrackGainInc), wxT("Alt+Shift+Up"),
1587  c->AddItem(wxT("TrackGainDec"), XXO("&Decrease Gain on Focused Track"), FN(OnTrackGainDec), wxT("Alt+Shift+Down"),
1590  c->AddItem(wxT("TrackMenu"), XXO("Op&en Menu on Focused Track..."), FN(OnTrackMenu), wxT("Shift+M\tskipKeydown"),
1593  c->AddItem(wxT("TrackMute"), XXO("M&ute/Unmute Focused Track"), FN(OnTrackMute), wxT("Shift+U"),
1596  c->AddItem(wxT("TrackSolo"), XXO("&Solo/Unsolo Focused Track"), FN(OnTrackSolo), wxT("Shift+S"),
1599  c->AddItem(wxT("TrackClose"), XXO("&Close Focused Track"), FN(OnTrackClose), wxT("Shift+C"),
1602  c->AddItem(wxT("TrackMoveUp"), XXO("Move Focused Track U&p"), FN(OnTrackMoveUp),
1605  c->AddItem(wxT("TrackMoveDown"), XXO("Move Focused Track Do&wn"), FN(OnTrackMoveDown),
1608  c->AddItem(wxT("TrackMoveTop"), XXO("Move Focused Track to T&op"), FN(OnTrackMoveTop),
1611  c->AddItem(wxT("TrackMoveBottom"), XXO("Move Focused Track to &Bottom"), FN(OnTrackMoveBottom),
1614  c->EndSubMenu();
1615 
1616  // These are the more useful to VI user Scriptables.
1617  // i18n-hint: Scriptables are commands normally used from Python, Perl etc.
1618  c->BeginSubMenu(_("&Scriptables I"));
1619 
1620  // Note that the PLUGIN_SYMBOL must have a space between words,
1621  // whereas the short-form used here must not.
1622  // (So if you did write "CompareAudio" for the PLUGIN_SYMBOL name, then
1623  // you would have to use "Compareaudio" here.)
1624 
1625  c->AddItem(wxT("SelectTime"), XXO("Select Time..."), FN(OnAudacityCommand),
1627  c->AddItem(wxT("SelectFrequencies"), XXO("Select Frequencies..."), FN(OnAudacityCommand),
1629  c->AddItem(wxT("SelectTracks"), XXO("Select Tracks..."), FN(OnAudacityCommand),
1631 
1632  c->AddItem(wxT("SetTrackStatus"), XXO("Set Track Status..."), FN(OnAudacityCommand),
1634  c->AddItem(wxT("SetTrackAudio"), XXO("Set Track Audio..."), FN(OnAudacityCommand),
1636  c->AddItem(wxT("SetTrackVisuals"), XXO("Set Track Visuals..."), FN(OnAudacityCommand),
1638 
1639 
1640  c->AddItem(wxT("GetPreference"), XXO("Get Preference..."), FN(OnAudacityCommand),
1642  c->AddItem(wxT("SetPreference"), XXO("Set Preference..."), FN(OnAudacityCommand),
1644  c->AddItem(wxT("SetClip"), XXO("Set Clip..."), FN(OnAudacityCommand),
1646  c->AddItem(wxT("SetEnvelope"), XXO("Set Envelope..."), FN(OnAudacityCommand),
1648  c->AddItem(wxT("SetLabel"), XXO("Set Label..."), FN(OnAudacityCommand),
1650  c->AddItem(wxT("SetProject"), XXO("Set Project..."), FN(OnAudacityCommand),
1652 
1653  c->EndSubMenu();
1654  // Less useful to VI users.
1655  c->BeginSubMenu(_("Scripta&bles II"));
1656 
1657  c->AddItem(wxT("Select"), XXO("Select..."), FN(OnAudacityCommand),
1659  c->AddItem(wxT("SetTrack"), XXO("Set Track..."), FN(OnAudacityCommand),
1661  c->AddItem(wxT("GetInfo"), XXO("Get Info..."), FN(OnAudacityCommand),
1663  c->AddItem(wxT("Message"), XXO("Message..."), FN(OnAudacityCommand),
1665  c->AddItem(wxT("Help"), XXO("Help..."), FN(OnAudacityCommand),
1667 
1668  c->AddItem(wxT("Import2"), XXO("Import..."), FN(OnAudacityCommand),
1670  c->AddItem(wxT("Export2"), XXO("Export..."), FN(OnAudacityCommand),
1672  c->AddItem(wxT("OpenProject2"), XXO("Open Project..."), FN(OnAudacityCommand),
1674  c->AddItem(wxT("SaveProject2"), XXO("Save Project..."), FN(OnAudacityCommand),
1676 
1677  c->AddItem(wxT("Drag"), XXO("Move Mouse..."), FN(OnAudacityCommand),
1679  c->AddItem(wxT("CompareAudio"), XXO("Compare Audio..."), FN(OnAudacityCommand),
1681  // i18n-hint: Screenshot in the help menu has a much bigger dialog.
1682  c->AddItem(wxT("Screenshot"), XXO("Screenshot (short format)..."), FN(OnAudacityCommand),
1684 
1685 
1686  c->EndSubMenu();
1687 
1688 
1689  // Accel key is not bindable.
1690  c->AddItem(wxT("FullScreenOnOff"), XXO("&Full Screen (on/off)"), FN(OnFullScreen),
1691 #ifdef __WXMAC__
1692  wxT("Ctrl+/"),
1693 #else
1694  wxT("F11"),
1695 #endif
1696  AlwaysEnabledFlag, AlwaysEnabledFlag,
1697  wxTopLevelWindow::IsFullScreen() ? 1:0); // Check Mark.
1698 
1699 #ifdef __WXMAC__
1700  /* i18n-hint: Shrink all project windows to icons on the Macintosh tooldock */
1701  c->AddItem(wxT("MacMinimizeAll"), XXO("Minimize All Projects"),
1702  FN(OnMacMinimizeAll), wxT("Ctrl+Alt+M"),
1703  AlwaysEnabledFlag, AlwaysEnabledFlag);
1704 #endif
1705 
1706 
1707 
1708  c->EndMenu();
1709 
1710 
1711  SetMenuBar(menubar.release());
1712  // Bug 143 workaround.
1713  // The bug is in wxWidgets. For a menu that has scrollers, the
1714  // scrollers have an ID of 0 (not wxID_NONE which is -3).
1715  // Therefore wxWidgets attempts to find a help string. See
1716  // wxFrameBase::ShowMenuHelp(int menuId)
1717  // It finds a bogus automatic help string of "Recent &Files"
1718  // from that submenu.
1719  // So we set the help string for command with Id 0 to empty.
1720  mRecentFilesMenu->GetParent()->SetHelpString( 0, "" );
1721  }
1722 
1723 
1724 
1726 
1727 #if defined(__WXDEBUG__)
1728 // c->CheckDups();
1729 #endif
1730 }
1731 
1732 #undef XXO
1733 
1734 
1735 
1737 {
1738  wxArrayString names = MacroCommands::GetNames();
1739  int i;
1740 
1741  for (i = 0; i < (int)names.GetCount(); i++) {
1742  wxString MacroID = ApplyMacroDialog::MacroIdOfName( names[i] );
1743  c->AddItem(MacroID, names[i], false, FN(OnApplyMacroDirectly),
1744  flags,
1745  flags);
1746  }
1747 
1748 }
1749 
1750 
1755  EffectType type,
1756  CommandFlag batchflags,
1757  CommandFlag realflags)
1758 {
1760 
1761  std::vector<const PluginDescriptor*> defplugs;
1762  std::vector<const PluginDescriptor*> optplugs;
1763 
1764  const PluginDescriptor *plug = pm.GetFirstPluginForEffectType(type);
1765  while (plug)
1766  {
1767  if ( !plug->IsEnabled() ){
1768  ;// don't add to menus!
1769  }
1770  else if (plug->IsEffectDefault()
1771 #ifdef EXPERIMENTAL_DA
1772  // Move Nyquist prompt into nyquist group.
1773  && (plug->GetName() != _("Nyquist Prompt"))
1774 #endif
1775  )
1776  defplugs.push_back(plug);
1777  else
1778  optplugs.push_back(plug);
1779  plug = pm.GetNextPluginForEffectType(type);
1780  }
1781 
1782  wxString groupby = gPrefs->Read(wxT("/Effects/GroupBy"), wxT("name"));
1783 
1784  using Comparator = bool(*)(const PluginDescriptor*, const PluginDescriptor*);
1785  Comparator comp1, comp2;
1786  if (groupby == wxT("sortby:name"))
1787  comp1 = comp2 = SortEffectsByName;
1788  else if (groupby == wxT("sortby:publisher:name"))
1790  else if (groupby == wxT("sortby:type:name"))
1792  else if (groupby == wxT("groupby:publisher"))
1793  comp1 = comp2 = SortEffectsByPublisher;
1794  else if (groupby == wxT("groupby:type"))
1795  comp1 = comp2 = SortEffectsByType;
1796  else // name
1797  comp1 = comp2 = SortEffectsByName;
1798 
1799  std::sort( defplugs.begin(), defplugs.end(), comp1 );
1800  std::sort( optplugs.begin(), optplugs.end(), comp2 );
1801 
1802  AddEffectMenuItems(c, defplugs, batchflags, realflags, true);
1803 
1804  if (defplugs.size() && optplugs.size())
1805  {
1806  c->AddSeparator();
1807  }
1808 
1809  AddEffectMenuItems(c, optplugs, batchflags, realflags, false);
1810 
1811  return;
1812 }
1813 
1815  std::vector<const PluginDescriptor*> & plugs,
1816  CommandFlag batchflags,
1817  CommandFlag realflags,
1818  bool isDefault)
1819 {
1820  size_t pluginCnt = plugs.size();
1821 
1822  wxString groupBy = gPrefs->Read(wxT("/Effects/GroupBy"), wxT("name"));
1823 
1824  bool grouped = false;
1825  if (groupBy.StartsWith(wxT("groupby")))
1826  {
1827  grouped = true;
1828  }
1829 
1830  std::vector<bool> vHasDialog;
1831  wxArrayString groupNames;
1832  PluginIDList groupPlugs;
1833  std::vector<CommandFlag> groupFlags;
1834  if (grouped)
1835  {
1836  wxString last;
1837  wxString current;
1838 
1839  for (size_t i = 0; i < pluginCnt; i++)
1840  {
1841  const PluginDescriptor *plug = plugs[i];
1842 
1843  bool hasDialog = plug->GetSymbol().Msgid().Contains("...");
1844  auto name = plug->GetSymbol().Translation();
1845 
1846  if (plug->IsEffectInteractive())
1847  {
1848  name += wxT("...");
1849  }
1850 
1851  if (groupBy == wxT("groupby:publisher"))
1852  {
1853  current = EffectManager::Get().GetVendorName(plug->GetID());
1854  if (current.IsEmpty())
1855  {
1856  current = _("Unknown");
1857  }
1858  }
1859  else if (groupBy == wxT("groupby:type"))
1860  {
1861  current = EffectManager::Get().GetEffectFamilyName(plug->GetID());
1862  if (current.IsEmpty())
1863  {
1864  current = _("Unknown");
1865  }
1866  }
1867 
1868  if (current != last)
1869  {
1870  if (!last.IsEmpty())
1871  {
1872  c->BeginSubMenu(last);
1873  }
1874 
1875  AddEffectMenuItemGroup(c, groupNames, vHasDialog,
1876  groupPlugs, groupFlags, isDefault);
1877 
1878  if (!last.IsEmpty())
1879  {
1880  c->EndSubMenu();
1881  }
1882 
1883  groupNames.Clear();
1884  vHasDialog.clear();
1885  groupPlugs.Clear();
1886  groupFlags.clear();
1887  last = current;
1888  }
1889 
1890  groupNames.Add(name);
1891  vHasDialog.push_back(hasDialog);
1892  groupPlugs.Add(plug->GetID());
1893  groupFlags.push_back(plug->IsEffectRealtime() ? realflags : batchflags);
1894  }
1895 
1896  if (groupNames.GetCount() > 0)
1897  {
1898  c->BeginSubMenu(current);
1899 
1900  AddEffectMenuItemGroup(c, groupNames, vHasDialog, groupPlugs, groupFlags, isDefault);
1901 
1902  c->EndSubMenu();
1903  }
1904  }
1905  else
1906  {
1907  for (size_t i = 0; i < pluginCnt; i++)
1908  {
1909  const PluginDescriptor *plug = plugs[i];
1910 
1911  bool hasDialog = plug->GetSymbol().Msgid().Contains("...");
1912  auto name = plug->GetSymbol().Translation();
1913 
1914  if (plug->IsEffectInteractive())
1915  {
1916  name += wxT("...");
1917  }
1918 
1919  wxString group = wxEmptyString;
1920  if (groupBy == wxT("sortby:publisher:name"))
1921  {
1922  group = EffectManager::Get().GetVendorName(plug->GetID());
1923  }
1924  else if (groupBy == wxT("sortby:type:name"))
1925  {
1926  group = EffectManager::Get().GetEffectFamilyName(plug->GetID());
1927  }
1928 
1929  if (plug->IsEffectDefault())
1930  {
1931  group = wxEmptyString;
1932  }
1933 
1934  if (!group.IsEmpty())
1935  {
1936  group += wxT(": ");
1937  }
1938 
1939  groupNames.Add(group + name);
1940  vHasDialog.push_back(hasDialog);
1941  groupPlugs.Add(plug->GetID());
1942  groupFlags.push_back(plug->IsEffectRealtime() ? realflags : batchflags);
1943  }
1944 
1945  if (groupNames.GetCount() > 0)
1946  {
1947  AddEffectMenuItemGroup(c, groupNames, vHasDialog, groupPlugs, groupFlags, isDefault);
1948  }
1949 
1950  }
1951 
1952  return;
1953 }
1954 
1956  const wxArrayString & names,
1957  const std::vector<bool> &vHasDialog,
1958  const PluginIDList & plugs,
1959  const std::vector<CommandFlag> & flags,
1960  bool isDefault)
1961 {
1962  int namesCnt = (int) names.GetCount();
1963  int perGroup;
1964 
1965 #if defined(__WXGTK__)
1966  gPrefs->Read(wxT("/Effects/MaxPerGroup"), &perGroup, 15);
1967 #else
1968  gPrefs->Read(wxT("/Effects/MaxPerGroup"), &perGroup, 0);
1969 #endif
1970 
1971  int groupCnt = namesCnt;
1972  for (int i = 0; i < namesCnt; i++)
1973  {
1974  while (i + 1 < namesCnt && names[i].IsSameAs(names[i + 1]))
1975  {
1976  i++;
1977  groupCnt--;
1978  }
1979  }
1980 
1981  // The "default" effects shouldn't be broken into subgroups
1982  if (namesCnt > 0 && isDefault)
1983  {
1984  perGroup = 0;
1985  }
1986 
1987  int max = perGroup;
1988  int items = perGroup;
1989 
1990  if (max > groupCnt)
1991  {
1992  max = 0;
1993  }
1994 
1995  int groupNdx = 0;
1996  for (int i = 0; i < namesCnt; i++)
1997  {
1998  if (max > 0 && items == max)
1999  {
2000  int end = groupNdx + max;
2001  if (end + 1 > groupCnt)
2002  {
2003  end = groupCnt;
2004  }
2005  c->BeginSubMenu(wxString::Format(_("Plug-in %d to %d"),
2006  groupNdx + 1,
2007  end));
2008  }
2009 
2010  if (i + 1 < namesCnt && names[i].IsSameAs(names[i + 1]))
2011  {
2012  wxString name = names[i];
2013  c->BeginSubMenu(name);
2014  while (i < namesCnt && names[i].IsSameAs(name))
2015  {
2016  const PluginDescriptor *plug = PluginManager::Get().GetPlugin(plugs[i]);
2017  wxString item = plug->GetPath();
2018  if( plug->GetPluginType() == PluginTypeEffect )
2019  c->AddItem(item,
2020  item,
2021  item.Contains("..."),
2022  FN(OnEffect),
2023  flags[i],
2024  flags[i], true, plugs[i]);
2025 
2026  i++;
2027  }
2028  c->EndSubMenu();
2029  i--;
2030  }
2031  else
2032  {
2033  const PluginDescriptor *plug = PluginManager::Get().GetPlugin(plugs[i]);
2034  if( plug->GetPluginType() == PluginTypeEffect )
2035  c->AddItem(names[i],
2036  names[i],
2037  vHasDialog[i],
2038  FN(OnEffect),
2039  flags[i],
2040  flags[i], true, plugs[i]);
2041  }
2042 
2043  if (max > 0)
2044  {
2045  groupNdx++;
2046  items--;
2047  if (items == 0 || i + 1 == namesCnt)
2048  {
2049  c->EndSubMenu();
2050  items = max;
2051  }
2052  }
2053  }
2054 
2055  return;
2056 }
2057 
2058 #undef FN
2059 
2061 {
2062  // Recent Files and Recent Projects menus
2063 
2064 #ifdef __WXMAC__
2065  /* i18n-hint: This is the name of the menu item on Mac OS X only */
2066  mRecentFilesMenu = c->BeginSubMenu(_("Open Recent"));
2067 #else
2068  /* i18n-hint: This is the name of the menu item on Windows and Linux */
2069  mRecentFilesMenu = c->BeginSubMenu(_("Recent &Files"));
2070 #endif
2071 
2074 
2075  c->EndSubMenu();
2076 
2077 }
2078 
2080 {
2081  wxString desc;
2082  int cur = GetUndoManager()->GetCurrentState();
2083 
2084  if (GetUndoManager()->UndoAvailable()) {
2085  GetUndoManager()->GetShortDescription(cur, &desc);
2086  mCommandManager.Modify(wxT("Undo"),
2087  wxString::Format(_("&Undo %s"),
2088  desc));
2089  mCommandManager.Enable(wxT("Undo"), this->UndoAvailable());
2090  }
2091  else {
2092  mCommandManager.Modify(wxT("Undo"),
2093  _("&Undo"));
2094  }
2095 
2096  if (GetUndoManager()->RedoAvailable()) {
2097  GetUndoManager()->GetShortDescription(cur+1, &desc);
2098  mCommandManager.Modify(wxT("Redo"),
2099  wxString::Format(_("&Redo %s"),
2100  desc));
2101  mCommandManager.Enable(wxT("Redo"), this->RedoAvailable());
2102  }
2103  else {
2104  mCommandManager.Modify(wxT("Redo"),
2105  _("&Redo"));
2106  mCommandManager.Enable(wxT("Redo"), false);
2107  }
2108 }
2109 
2111 {
2112  // On OSX, we can't rebuild the menus while a modal dialog is being shown
2113  // since the enabled state for menus like Quit and Preference gets out of
2114  // sync with wxWidgets idea of what it should be.
2115 #if defined(__WXMAC__) && defined(__WXDEBUG__)
2116  {
2117  wxDialog *dlg = wxDynamicCast(wxGetTopLevelParent(FindFocus()), wxDialog);
2118  wxASSERT((!dlg || !dlg->IsModal()));
2119  }
2120 #endif
2121 
2122  // Allow FileHistory to remove its own menu
2124 
2125  // Delete the menus, since we will soon recreate them.
2126  // Rather oddly, the menus don't vanish as a result of doing this.
2127  {
2128  std::unique_ptr<wxMenuBar> menuBar{ GetMenuBar() };
2129  DetachMenuBar();
2130  // menuBar gets deleted here
2131  }
2132 
2134 
2136 
2138 }
2139 
2141 {
2142 }
2143 
2145 {
2146  wxWindow *w = FindFocus();
2147 
2148  while (w && mToolManager && mTrackPanel) {
2149  if (w == mToolManager->GetTopDock()) {
2150  return TopDockHasFocus;
2151  }
2152 
2153  if (w == mRuler)
2154  return RulerHasFocus;
2155 
2156  if (w == mTrackPanel) {
2157  return TrackPanelHasFocus;
2158  }
2159  // LIE if LyricsPanel window has focus.
2160  // we want to act as if TrackPanel has focus.
2161  if (w== mLyricsWindow) {
2162  return TrackPanelHasFocus;
2163  }
2164  if (w == mToolManager->GetBotDock()) {
2165  return BotDockHasFocus;
2166  }
2167 
2168  w = w->GetParent();
2169  }
2170 
2171  return AlwaysEnabledFlag;
2172 }
2173 
2175 {
2176  // This method determines all of the flags that determine whether
2177  // certain menu items and commands should be enabled or disabled,
2178  // and returns them in a bitfield. Note that if none of the flags
2179  // have changed, it's not necessary to even check for updates.
2180  auto flags = AlwaysEnabledFlag;
2181  // static variable, used to remember flags for next time.
2182  static auto lastFlags = flags;
2183 
2184  if (auto focus = wxWindow::FindFocus()) {
2185  while (focus && focus->GetParent())
2186  focus = focus->GetParent();
2187  if (focus && !static_cast<wxTopLevelWindow*>(focus)->IsIconized())
2188  flags |= NotMinimizedFlag;
2189  }
2190 
2191  // quick 'short-circuit' return.
2192  if ( checkActive && !IsActive() ){
2193  // short cirucit return should preserve flags that have not been calculated.
2194  flags = (lastFlags & ~NotMinimizedFlag) | flags;
2195  lastFlags = flags;
2196  return flags;
2197  }
2198 
2200  flags |= AudioIONotBusyFlag;
2201  else
2202  flags |= AudioIOBusyFlag;
2203 
2204  if( gAudioIO->IsPaused() )
2205  flags |= PausedFlag;
2206  else
2207  flags |= NotPausedFlag;
2208 
2210  flags |= TimeSelectedFlag;
2211 
2212  TrackListIterator iter(GetTracks());
2213  Track *t = iter.First();
2214  while (t) {
2215  flags |= TracksExistFlag;
2216  if (t->GetKind() == Track::Label) {
2217  LabelTrack *lt = (LabelTrack *) t;
2218 
2219  flags |= LabelTracksExistFlag;
2220 
2221  if (lt->GetSelected()) {
2222  flags |= TracksSelectedFlag;
2223  for (int i = 0; i < lt->GetNumLabels(); i++) {
2224  const LabelStruct *ls = lt->GetLabel(i);
2225  if (ls->getT0() >= mViewInfo.selectedRegion.t0() &&
2226  ls->getT1() <= mViewInfo.selectedRegion.t1()) {
2227  flags |= LabelsSelectedFlag;
2228  break;
2229  }
2230  }
2231  }
2232 
2233  if (lt->IsTextSelected()) {
2234  flags |= CutCopyAvailableFlag;
2235  }
2236  }
2237  else if (t->GetKind() == Track::Wave) {
2238  flags |= WaveTracksExistFlag;
2239  flags |= PlayableTracksExistFlag;
2240  if (t->GetSelected()) {
2241  flags |= TracksSelectedFlag;
2242  if (t->GetLinked()) {
2243  flags |= StereoRequiredFlag;
2244  }
2245  else {
2246  flags |= WaveTracksSelectedFlag;
2247  flags |= AudioTracksSelectedFlag;
2248  }
2249  }
2250  if( t->GetEndTime() > t->GetStartTime() )
2251  flags |= HasWaveDataFlag;
2252  }
2253 #if defined(USE_MIDI)
2254  else if (t->GetKind() == Track::Note) {
2255  NoteTrack *nt = (NoteTrack *) t;
2256 
2257  flags |= NoteTracksExistFlag;
2258 #ifdef EXPERIMENTAL_MIDI_OUT
2259  flags |= PlayableTracksExistFlag;
2260 #endif
2261 
2262  if (nt->GetSelected()) {
2263  flags |= TracksSelectedFlag;
2264  flags |= NoteTracksSelectedFlag;
2265  flags |= AudioTracksSelectedFlag; // even if not EXPERIMENTAL_MIDI_OUT
2266  }
2267  }
2268 #endif
2269  t = iter.Next();
2270  }
2271 
2272  if((msClipT1 - msClipT0) > 0.0)
2273  flags |= ClipboardFlag;
2274 
2275  if (GetUndoManager()->UnsavedChanges() || !IsProjectSaved())
2276  flags |= UnsavedChangesFlag;
2277 
2278  if (!mLastEffect.IsEmpty())
2279  flags |= HasLastEffectFlag;
2280 
2281  if (UndoAvailable())
2282  flags |= UndoAvailableFlag;
2283 
2284  if (RedoAvailable())
2285  flags |= RedoAvailableFlag;
2286 
2287  if (ZoomInAvailable() && (flags & TracksExistFlag))
2288  flags |= ZoomInAvailableFlag;
2289 
2290  if (ZoomOutAvailable() && (flags & TracksExistFlag))
2291  flags |= ZoomOutAvailableFlag;
2292 
2293  // TextClipFlag is currently unused (Jan 2017, 2.1.3 alpha)
2294  // and LabelTrack::IsTextClipSupported() is quite slow on Linux,
2295  // so disable for now (See bug 1575).
2296  // if ((flags & LabelTracksExistFlag) && LabelTrack::IsTextClipSupported())
2297  // flags |= TextClipFlag;
2298 
2299  flags |= GetFocusedFrame();
2300 
2301  double start, end;
2302  GetPlayRegion(&start, &end);
2303  if (IsPlayRegionLocked())
2304  flags |= PlayRegionLockedFlag;
2305  else if (start != end)
2306  flags |= PlayRegionNotLockedFlag;
2307 
2308  if (flags & AudioIONotBusyFlag) {
2309  if (flags & TimeSelectedFlag) {
2310  if (flags & TracksSelectedFlag) {
2311  flags |= CutCopyAvailableFlag;
2312  }
2313  }
2314  }
2315 
2316  if (wxGetApp().GetRecentFiles()->GetCount() > 0)
2317  flags |= HaveRecentFiles;
2318 
2319  if (IsSyncLocked())
2320  flags |= IsSyncLockedFlag;
2321  else
2322  flags |= IsNotSyncLockedFlag;
2323 
2324  if (!EffectManager::Get().RealtimeIsActive())
2325  flags |= IsRealtimeNotActiveFlag;
2326 
2327  if (!mIsCapturing)
2328  flags |= CaptureNotBusyFlag;
2329 
2331  if (bar->ControlToolBar::CanStopAudioStream())
2332  flags |= CanStopAudioStreamFlag;
2333 
2334  lastFlags = flags;
2335  return flags;
2336 }
2337 
2338 // Select the full time range, if no
2339 // time range is selected.
2341 {
2342  auto flags = GetUpdateFlags();
2343  if(!(flags & TracksSelectedFlag) ||
2345  OnSelectSomething(*this);
2346 }
2347 
2348 // Stop playing or recording, if paused.
2350 {
2351  auto flags = GetUpdateFlags();
2352  if( flags & PausedFlag )
2353  OnStop(*this);
2354 }
2355 
2357 {
2358  AProjectArray::iterator i;
2359  for (i = gAudacityProjects.begin(); i != gAudacityProjects.end(); ++i) {
2360  (*i)->ModifyToolbarMenus();
2361  }
2362 }
2363 
2365 {
2366  // Refreshes can occur during shutdown and the toolmanager may already
2367  // be deleted, so protect against it.
2368  if (!mToolManager) {
2369  return;
2370  }
2371 
2372  mCommandManager.Check(wxT("ShowScrubbingTB"),
2373  mToolManager->IsVisible(ScrubbingBarID));
2374  mCommandManager.Check(wxT("ShowDeviceTB"),
2375  mToolManager->IsVisible(DeviceBarID));
2376  mCommandManager.Check(wxT("ShowEditTB"),
2377  mToolManager->IsVisible(EditBarID));
2378  mCommandManager.Check(wxT("ShowMeterTB"),
2379  mToolManager->IsVisible(MeterBarID));
2380  mCommandManager.Check(wxT("ShowRecordMeterTB"),
2381  mToolManager->IsVisible(RecordMeterBarID));
2382  mCommandManager.Check(wxT("ShowPlayMeterTB"),
2383  mToolManager->IsVisible(PlayMeterBarID));
2384  mCommandManager.Check(wxT("ShowMixerTB"),
2385  mToolManager->IsVisible(MixerBarID));
2386  mCommandManager.Check(wxT("ShowSelectionTB"),
2387  mToolManager->IsVisible(SelectionBarID));
2388 #ifdef EXPERIMENTAL_SPECTRAL_EDITING
2389  mCommandManager.Check(wxT("ShowSpectralSelectionTB"),
2390  mToolManager->IsVisible(SpectralSelectionBarID));
2391 #endif
2392  mCommandManager.Check(wxT("ShowToolsTB"),
2393  mToolManager->IsVisible(ToolsBarID));
2394  mCommandManager.Check(wxT("ShowTranscriptionTB"),
2395  mToolManager->IsVisible(TranscriptionBarID));
2396  mCommandManager.Check(wxT("ShowTransportTB"),
2397  mToolManager->IsVisible(TransportBarID));
2398 
2399  // Now, go through each toolbar, and call EnableDisableButtons()
2400  for (int i = 0; i < ToolBarCount; i++) {
2401  mToolManager->GetToolBar(i)->EnableDisableButtons();
2402  }
2403 
2404  // These don't really belong here, but it's easier and especially so for
2405  // the Edit toolbar and the sync-lock menu item.
2406  bool active;
2407  gPrefs->Read(wxT("/AudioIO/SoundActivatedRecord"),&active, false);
2408  mCommandManager.Check(wxT("SoundActivation"), active);
2409 #ifdef EXPERIMENTAL_AUTOMATED_INPUT_LEVEL_ADJUSTMENT
2410  gPrefs->Read(wxT("/AudioIO/AutomatedInputLevelAdjustment"),&active, false);
2411  mCommandManager.Check(wxT("AutomatedInputLevelAdjustmentOnOff"), active);
2412 #endif
2413 
2415  mCommandManager.Check(wxT("PinnedHead"), active);
2416 
2417 #ifdef EXPERIMENTAL_DA
2418  gPrefs->Read(wxT("/AudioIO/Duplex"),&active, false);
2419 #else
2420  gPrefs->Read(wxT("/AudioIO/Duplex"),&active, true);
2421 #endif
2422  mCommandManager.Check(wxT("Duplex"), active);
2423  gPrefs->Read(wxT("/AudioIO/SWPlaythrough"),&active, false);
2424  mCommandManager.Check(wxT("SWPlaythrough"), active);
2425  gPrefs->Read(wxT("/GUI/SyncLockTracks"), &active, false);
2426  SetSyncLock(active);
2427  mCommandManager.Check(wxT("SyncLock"), active);
2428  gPrefs->Read(wxT("/GUI/TypeToCreateLabel"),&active, true);
2429  mCommandManager.Check(wxT("TypeToCreateLabel"), active);
2430 }
2431 
2432 // checkActive is a temporary hack that should be removed as soon as we
2433 // get multiple effect preview working
2434 void AudacityProject::UpdateMenus(bool checkActive)
2435 {
2436  //ANSWER-ME: Why UpdateMenus only does active project?
2437  //JKC: Is this test fixing a bug when multiple projects are open?
2438  //so that menu states work even when different in different projects?
2439  if (this != GetActiveProject())
2440  return;
2441 
2442  auto flags = GetUpdateFlags(checkActive);
2443  auto flags2 = flags;
2444 
2445  // We can enable some extra items if we have select-all-on-none.
2446  //EXPLAIN-ME: Why is this here rather than in GetUpdateFlags()?
2447  //ANSWER: Because flags2 is used in the menu enable/disable.
2448  //The effect still needs flags to determine whether it will need
2449  //to actually do the 'select all' to make the command valid.
2450  if (mWhatIfNoSelection != 0)
2451  {
2452  if ((flags & TracksExistFlag))
2453  {
2454  flags2 |= TracksSelectedFlag;
2455  if ((flags & WaveTracksExistFlag))
2456  {
2457  flags2 |= TimeSelectedFlag
2460  }
2461  }
2462  }
2463 
2464  if( mStopIfWasPaused )
2465  {
2466  if( flags & PausedFlag ){
2467  flags2 |= AudioIONotBusyFlag;
2468  }
2469  }
2470 
2471  // Return from this function if nothing's changed since
2472  // the last time we were here.
2473  if (flags == mLastFlags)
2474  return;
2475  mLastFlags = flags;
2476 
2478 
2479  // With select-all-on-none, some items that we don't want enabled may have
2480  // been enabled, since we changed the flags. Here we manually disable them.
2481  // 0 is grey out, 1 is Autoselect, 2 is Give warnings.
2482  if (mWhatIfNoSelection != 0)
2483  {
2484  if (!(flags & TimeSelectedFlag) | !(flags & TracksSelectedFlag))
2485  {
2486  mCommandManager.Enable(wxT("SplitCut"), false);
2487  mCommandManager.Enable(wxT("SplitDelete"), false);
2488  }
2489  if (!(flags & WaveTracksSelectedFlag))
2490  {
2491  mCommandManager.Enable(wxT("Split"), false);
2492  }
2493  if (!(flags & TimeSelectedFlag) | !(flags & WaveTracksSelectedFlag))
2494  {
2495  mCommandManager.Enable(wxT("ExportSel"), false);
2496  mCommandManager.Enable(wxT("SplitNew"), false);
2497  }
2498  if (!(flags & TimeSelectedFlag) | !(flags & AudioTracksSelectedFlag))
2499  {
2500  mCommandManager.Enable(wxT("Trim"), false);
2501  }
2502  }
2503 
2504 #if 0
2505  if (flags & CutCopyAvailableFlag) {
2506  mCommandManager.Enable(wxT("Copy"), true);
2507  mCommandManager.Enable(wxT("Cut"), true);
2508  }
2509 #endif
2510 
2512 }
2513 
2514 //
2515 // Tool selection commands
2516 //
2517 
2520 {
2521  ToolsToolBar *toolbar = GetToolsToolBar();
2522  if (toolbar) {
2523  toolbar->SetCurrentTool(tool);
2524  mTrackPanel->Refresh(false);
2525  }
2526 }
2527 
2529 void AudacityProject::OnSelectTool(const CommandContext &WXUNUSED(context) )
2530 {
2532 }
2533 
2535 void AudacityProject::OnZoomTool(const CommandContext &WXUNUSED(context) )
2536 {
2537  SetTool(zoomTool);
2538 }
2539 
2541 void AudacityProject::OnEnvelopeTool(const CommandContext &WXUNUSED(context) )
2542 {
2544 }
2545 
2547 void AudacityProject::OnTimeShiftTool(const CommandContext &WXUNUSED(context) )
2548 {
2549  SetTool(slideTool);
2550 }
2551 
2552 void AudacityProject::OnDrawTool(const CommandContext &WXUNUSED(context) )
2553 {
2554  SetTool(drawTool);
2555 }
2556 
2557 void AudacityProject::OnMultiTool(const CommandContext &WXUNUSED(context) )
2558 {
2559  SetTool(multiTool);
2560 }
2561 
2562 
2563 void AudacityProject::OnNextTool(const CommandContext &WXUNUSED(context) )
2564 {
2565  ToolsToolBar *toolbar = GetToolsToolBar();
2566  if (toolbar) {
2567  // Use GetDownTool() here since GetCurrentTool() can return a value that
2568  // doesn't represent the real tool if the Multi-tool is being used.
2569  toolbar->SetCurrentTool((toolbar->GetDownTool()+1)%numTools);
2570  mTrackPanel->Refresh(false);
2571  }
2572 }
2573 
2574 void AudacityProject::OnPrevTool(const CommandContext &WXUNUSED(context) )
2575 {
2576  ToolsToolBar *toolbar = GetToolsToolBar();
2577  if (toolbar) {
2578  // Use GetDownTool() here since GetCurrentTool() can return a value that
2579  // doesn't represent the real tool if the Multi-tool is being used.
2580  toolbar->SetCurrentTool((toolbar->GetDownTool()+(numTools-1))%numTools);
2581  mTrackPanel->Refresh(false);
2582  }
2583 }
2584 
2585 
2586 //
2587 // Audio I/O Commands
2588 //
2589 
2590 // TODO: Should all these functions which involve
2591 // the toolbar actually move into ControlToolBar?
2592 
2597 bool AudacityProject::MakeReadyToPlay(bool loop, bool cutpreview)
2598 {
2599  ControlToolBar *toolbar = GetControlToolBar();
2600  wxCommandEvent evt;
2601 
2602  // If this project is playing, stop playing
2604  toolbar->SetPlay(false); //Pops
2605  toolbar->SetStop(true); //Pushes stop down
2606  toolbar->OnStop(evt);
2607 
2608  ::wxMilliSleep(100);
2609  }
2610 
2611  // If it didn't stop playing quickly, or if some other
2612  // project is playing, return
2613  if (gAudioIO->IsBusy())
2614  return false;
2615 
2616  ControlToolBar::PlayAppearance appearance =
2620  toolbar->SetPlay(true, appearance);
2621  toolbar->SetStop(false);
2622 
2623  return true;
2624 }
2625 
2626 void AudacityProject::OnPlayOneSecond(const CommandContext &WXUNUSED(context) )
2627 {
2628  if( !MakeReadyToPlay() )
2629  return;
2630 
2631  double pos = mTrackPanel->GetMostRecentXPos();
2633  (SelectedRegion(pos - 0.5, pos + 0.5), GetDefaultPlayOptions(),
2635 }
2636 
2637 
2646 {
2647  if( !MakeReadyToPlay() )
2648  return;
2649 
2650  double pos = mTrackPanel->GetMostRecentXPos();
2651 
2652  double t0,t1;
2653  // check region between pointer and the nearest selection edge
2654  if (fabs(pos - mViewInfo.selectedRegion.t0()) <
2655  fabs(pos - mViewInfo.selectedRegion.t1())) {
2656  t0 = t1 = mViewInfo.selectedRegion.t0();
2657  } else {
2658  t0 = t1 = mViewInfo.selectedRegion.t1();
2659  }
2660  if( pos < t1)
2661  t0=pos;
2662  else
2663  t1=pos;
2664 
2665  // JKC: oneSecondPlay mode disables auto scrolling
2666  // On balance I think we should always do this in this function
2667  // since you are typically interested in the sound EXACTLY
2668  // where the cursor is.
2669  // TODO: have 'playing attributes' such as 'with_autoscroll'
2670  // rather than modes, since that's how we're now using the modes.
2671 
2672  // An alternative, commented out below, is to disable autoscroll
2673  // only when playing a short region, less than or equal to a second.
2674 // mLastPlayMode = ((t1-t0) > 1.0) ? normalPlay : oneSecondPlay;
2675 
2678 }
2679 
2680 // The next 4 functions provide a limited version of the
2681 // functionality of OnPlayToSelection() for keyboard users
2682 
2684 {
2685  if( !MakeReadyToPlay() )
2686  return;
2687 
2688  double t0 = mViewInfo.selectedRegion.t0();
2689  double beforeLen;
2690  gPrefs->Read(wxT("/AudioIO/CutPreviewBeforeLen"), &beforeLen, 2.0);
2691 
2693 }
2694 
2696 {
2697  if( !MakeReadyToPlay() )
2698  return;
2699 
2700  double t0 = mViewInfo.selectedRegion.t0();
2701  double t1 = mViewInfo.selectedRegion.t1();
2702  double afterLen;
2703  gPrefs->Read(wxT("/AudioIO/CutPreviewAfterLen"), &afterLen, 1.0);
2704 
2705  if ( t1 - t0 > 0.0 && t1 - t0 < afterLen )
2708  else
2710 }
2711 
2713 {
2714  if( !MakeReadyToPlay() )
2715  return;
2716 
2717  double t0 = mViewInfo.selectedRegion.t0();
2718  double t1 = mViewInfo.selectedRegion.t1();
2719  double beforeLen;
2720  gPrefs->Read(wxT("/AudioIO/CutPreviewBeforeLen"), &beforeLen, 2.0);
2721 
2722  if ( t1 - t0 > 0.0 && t1 - t0 < beforeLen )
2725  else
2727 }
2728 
2729 
2731 {
2732  if( !MakeReadyToPlay() )
2733  return;
2734 
2735  double t1 = mViewInfo.selectedRegion.t1();
2736  double afterLen;
2737  gPrefs->Read(wxT("/AudioIO/CutPreviewAfterLen"), &afterLen, 1.0);
2738 
2740 }
2741 
2743 {
2744  if (!MakeReadyToPlay())
2745  return;
2746 
2747  double t0 = mViewInfo.selectedRegion.t0();
2748  double t1 = mViewInfo.selectedRegion.t1();
2749  double beforeLen;
2750  gPrefs->Read(wxT("/AudioIO/CutPreviewBeforeLen"), &beforeLen, 2.0);
2751  double afterLen;
2752  gPrefs->Read(wxT("/AudioIO/CutPreviewAfterLen"), &afterLen, 1.0);
2753 
2754  if ( t1 - t0 > 0.0 && t1 - t0 < afterLen )
2756  else
2758 }
2759 
2761 {
2762  if (!MakeReadyToPlay())
2763  return;
2764 
2765  double t0 = mViewInfo.selectedRegion.t0();
2766  double t1 = mViewInfo.selectedRegion.t1();
2767  double beforeLen;
2768  gPrefs->Read(wxT("/AudioIO/CutPreviewBeforeLen"), &beforeLen, 2.0);
2769  double afterLen;
2770  gPrefs->Read(wxT("/AudioIO/CutPreviewAfterLen"), &afterLen, 1.0);
2771 
2772  if ( t1 - t0 > 0.0 && t1 - t0 < beforeLen )
2774  else
2776 }
2777 
2778 
2779 void AudacityProject::OnPlayLooped(const CommandContext &WXUNUSED(context) )
2780 {
2781  if( !MakeReadyToPlay(true) )
2782  return;
2783 
2784  // Now play in a loop
2785  // Will automatically set mLastPlayMode
2787 }
2788 
2789 void AudacityProject::OnPlayCutPreview(const CommandContext &WXUNUSED(context) )
2790 {
2791  if ( !MakeReadyToPlay(false, true) )
2792  return;
2793 
2794  // Play with cut preview
2795  GetControlToolBar()->PlayCurrentRegion(false, true);
2796 }
2797 
2798 void AudacityProject::OnPlayStop(const CommandContext &WXUNUSED(context) )
2799 {
2800  ControlToolBar *toolbar = GetControlToolBar();
2801 
2802  //If this project is playing, stop playing, make sure everything is unpaused.
2804  toolbar->SetPlay(false); //Pops
2805  toolbar->SetStop(true); //Pushes stop down
2806  toolbar->StopPlaying();
2807  }
2808  else if (gAudioIO->IsStreamActive()) {
2809  //If this project isn't playing, but another one is, stop playing the old and start the NEW.
2810 
2811  //find out which project we need;
2812  AudacityProject* otherProject = NULL;
2813  for(unsigned i=0; i<gAudacityProjects.size(); i++) {
2814  if(gAudioIO->IsStreamActive(gAudacityProjects[i]->GetAudioIOToken())) {
2815  otherProject=gAudacityProjects[i].get();
2816  break;
2817  }
2818  }
2819 
2820  //stop playing the other project
2821  if(otherProject) {
2822  ControlToolBar *otherToolbar = otherProject->GetControlToolBar();
2823  otherToolbar->SetPlay(false); //Pops
2824  otherToolbar->SetStop(true); //Pushes stop down
2825  otherToolbar->StopPlaying();
2826  }
2827 
2828  //play the front project
2829  if (!gAudioIO->IsBusy()) {
2830  //update the playing area
2832  //Otherwise, start playing (assuming audio I/O isn't busy)
2833  //toolbar->SetPlay(true); // Not needed as done in PlayPlayRegion.
2834  toolbar->SetStop(false);
2835 
2836  // Will automatically set mLastPlayMode
2837  toolbar->PlayCurrentRegion(false);
2838  }
2839  }
2840  else if (!gAudioIO->IsBusy()) {
2841  //Otherwise, start playing (assuming audio I/O isn't busy)
2842  //toolbar->SetPlay(true); // Not needed as done in PlayPlayRegion.
2843  toolbar->SetStop(false);
2844 
2845  // Will automatically set mLastPlayMode
2846  toolbar->PlayCurrentRegion(false);
2847  }
2848 }
2849 
2850 void AudacityProject::OnStop(const CommandContext &WXUNUSED(context) )
2851 {
2852  wxCommandEvent evt;
2853 
2854  GetControlToolBar()->OnStop(evt);
2855 }
2856 
2857 void AudacityProject::OnPause(const CommandContext &WXUNUSED(context) )
2858 {
2859  wxCommandEvent evt;
2860 
2861  GetControlToolBar()->OnPause(evt);
2862 }
2863 
2864 void AudacityProject::OnRecord(const CommandContext &WXUNUSED(context) )
2865 {
2866  wxCommandEvent evt;
2867  evt.SetInt(2); // 0 is default, use 1 to set shift on, 2 to clear it
2868 
2869  GetControlToolBar()->OnRecord(evt);
2870 }
2871 
2872 // If first choice is record same track 2nd choice is record NEW track
2873 // and vice versa.
2875 {
2876  wxCommandEvent evt;
2877  evt.SetInt(1); // 0 is default, use 1 to set shift on, 2 to clear it
2878 
2879  GetControlToolBar()->OnRecord(evt);
2880 }
2881 
2882 // The code for "OnPlayStopSelect" is simply the code of "OnPlayStop" and "OnStopSelect" merged.
2883 void AudacityProject::OnPlayStopSelect(const CommandContext &WXUNUSED(context) )
2884 {
2885  ControlToolBar *toolbar = GetControlToolBar();
2886  wxCommandEvent evt;
2887  if (DoPlayStopSelect(false, false))
2888  toolbar->OnStop(evt);
2889  else if (!gAudioIO->IsBusy()) {
2890  //Otherwise, start playing (assuming audio I/O isn't busy)
2891  //toolbar->SetPlay(true); // Not needed as set in PlayPlayRegion()
2892  toolbar->SetStop(false);
2893 
2894  // Will automatically set mLastPlayMode
2895  toolbar->PlayCurrentRegion(false);
2896  }
2897 }
2898 
2899 bool AudacityProject::DoPlayStopSelect(bool click, bool shift)
2900 {
2901  ControlToolBar *toolbar = GetControlToolBar();
2902 
2903  //If busy, stop playing, make sure everything is unpaused.
2904  if (GetScrubber().HasStartedScrubbing() ||
2906  toolbar->SetPlay(false); //Pops
2907  toolbar->SetStop(true); //Pushes stop down
2908 
2909  // change the selection
2910  auto time = gAudioIO->GetStreamTime();
2911  auto &selection = mViewInfo.selectedRegion;
2912  if (shift && click) {
2913  // Change the region selection, as if by shift-click at the play head
2914  auto t0 = selection.t0(), t1 = selection.t1();
2915  if (time < t0)
2916  // Grow selection
2917  t0 = time;
2918  else if (time > t1)
2919  // Grow selection
2920  t1 = time;
2921  else {
2922  // Shrink selection, changing the nearer boundary
2923  if (fabs(t0 - time) < fabs(t1 - time))
2924  t0 = time;
2925  else
2926  t1 = time;
2927  }
2928  selection.setTimes(t0, t1);
2929  }
2930  else if (click){
2931  // avoid a point at negative time.
2932  time = wxMax( time, 0 );
2933  // Set a point selection, as if by a click at the play head
2934  selection.setTimes(time, time);
2935  } else
2936  // How stop and set cursor always worked
2937  // -- change t0, collapsing to point only if t1 was greater
2938  selection.setT0(time, false);
2939 
2940  ModifyState(false); // without bWantsAutoSave
2941  return true;
2942  }
2943  return false;
2944 }
2945 
2946 void AudacityProject::OnStopSelect(const CommandContext &WXUNUSED(context) )
2947 {
2948  wxCommandEvent evt;
2949 
2950  if (gAudioIO->IsStreamActive()) {
2952  GetControlToolBar()->OnStop(evt);
2953  ModifyState(false); // without bWantsAutoSave
2954  }
2955 }
2956 
2958 {
2959  bool pause;
2960  gPrefs->Read(wxT("/AudioIO/SoundActivatedRecord"), &pause, false);
2961  gPrefs->Write(wxT("/AudioIO/SoundActivatedRecord"), !pause);
2962  gPrefs->Flush();
2964 }
2965 
2967 {
2968  bool value = !TracksPrefs::GetPinnedHeadPreference();
2971 
2972  // Change what happens in case transport is in progress right now
2973  auto ctb = GetActiveProject()->GetControlToolBar();
2974  if (ctb)
2976 
2977  auto ruler = GetRulerPanel();
2978  if (ruler)
2979  // Update button image
2980  ruler->UpdateButtonStates();
2981 
2982  auto &scrubber = GetScrubber();
2983  if (scrubber.HasStartedScrubbing())
2984  scrubber.SetScrollScrubbing(value);
2985 }
2986 
2988 {
2989  bool Duplex;
2990 #ifdef EXPERIMENTAL_DA
2991  gPrefs->Read(wxT("/AudioIO/Duplex"), &Duplex, false);
2992 #else
2993  gPrefs->Read(wxT("/AudioIO/Duplex"), &Duplex, true);
2994 #endif
2995  gPrefs->Write(wxT("/AudioIO/Duplex"), !Duplex);
2996  gPrefs->Flush();
2998 }
2999 
3001 {
3002  bool SWPlaythrough;
3003  gPrefs->Read(wxT("/AudioIO/SWPlaythrough"), &SWPlaythrough, false);
3004  gPrefs->Write(wxT("/AudioIO/SWPlaythrough"), !SWPlaythrough);
3005  gPrefs->Flush();
3007 }
3008 
3009 #ifdef EXPERIMENTAL_AUTOMATED_INPUT_LEVEL_ADJUSTMENT
3010 void AudacityProject::OnToggleAutomatedInputLevelAdjustment()
3011 {
3012  bool AVEnabled;
3013  gPrefs->Read(wxT("/AudioIO/AutomatedInputLevelAdjustment"), &AVEnabled, false);
3014  gPrefs->Write(wxT("/AudioIO/AutomatedInputLevelAdjustment"), !AVEnabled);
3015  gPrefs->Flush();
3017 }
3018 #endif
3019 
3021 {
3022  double stime = 0.0;
3023 
3024  if (t->GetKind() == Track::Wave) {
3025  WaveTrack *w = (WaveTrack *)t;
3026  stime = w->GetEndTime();
3027 
3028  WaveClip *c;
3029  int ndx;
3030  for (ndx = 0; ndx < w->GetNumClips(); ndx++) {
3031  c = w->GetClipByIndex(ndx);
3032  if (c->GetNumSamples() == 0)
3033  continue;
3034  if (c->GetStartTime() < stime) {
3035  stime = c->GetStartTime();
3036  }
3037  }
3038  }
3039  else if (t->GetKind() == Track::Label) {
3040  LabelTrack *l = (LabelTrack *)t;
3041  stime = l->GetStartTime();
3042  }
3043 
3044  return stime;
3045 }
3046 
3047 //sort based on flags. see Project.h for sort flags
3049 {
3050  size_t ndx = 0;
3051  int cmpValue;
3052  // This one place outside of TrackList where we must use undisguised
3053  // std::list iterators! Avoid this elsewhere!
3054  std::vector<TrackNodePointer> arr;
3055  arr.reserve(mTracks->size());
3056  bool lastTrackLinked = false;
3057  //sort by linked tracks. Assumes linked track follows owner in list.
3058 
3059  // First find the permutation.
3060  for (auto iter = mTracks->ListOfTracks::begin(),
3061  end = mTracks->ListOfTracks::end(); iter != end; ++iter) {
3062  const auto &track = *iter;
3063  if(lastTrackLinked) {
3064  //insert after the last track since this track should be linked to it.
3065  ndx++;
3066  }
3067  else {
3068  bool bArrayTrackLinked = false;
3069  for (ndx = 0; ndx < arr.size(); ++ndx) {
3070  Track &arrTrack = **arr[ndx].first;
3071  // Don't insert between channels of a stereo track!
3072  if( bArrayTrackLinked ){
3073  bArrayTrackLinked = false;
3074  }
3075  else if(flags & kAudacitySortByName) {
3076  //do case insensitive sort - cmpNoCase returns less than zero if the string is 'less than' its argument
3077  //also if we have case insensitive equality, then we need to sort by case as well
3078  //We sort 'b' before 'B' accordingly. We uncharacteristically use greater than for the case sensitive
3079  //compare because 'b' is greater than 'B' in ascii.
3080  cmpValue = track->GetName().CmpNoCase(arrTrack.GetName());
3081  if (cmpValue < 0 ||
3082  (0 == cmpValue && track->GetName().CompareTo(arrTrack.GetName()) > 0) )
3083  break;
3084  }
3085  //sort by time otherwise
3086  else if(flags & kAudacitySortByTime) {
3087  //we have to search each track and all its linked ones to fine the minimum start time.
3088  double time1, time2, tempTime;
3089  const Track* tempTrack;
3090  size_t candidatesLookedAt;
3091 
3092  candidatesLookedAt = 0;
3093  tempTrack = &*track;
3094  time1 = time2 = std::numeric_limits<double>::max(); //TODO: find max time value. (I don't think we have one yet)
3095  while(tempTrack) {
3096  tempTime = GetTime(tempTrack);
3097  time1 = std::min(time1, tempTime);
3098  if(tempTrack->GetLinked())
3099  tempTrack = tempTrack->GetLink();
3100  else
3101  tempTrack = NULL;
3102  }
3103 
3104  //get candidate's (from sorted array) time
3105  tempTrack = &arrTrack;
3106  while(tempTrack) {
3107  tempTime = GetTime(tempTrack);
3108  time2 = std::min(time2, tempTime);
3109  if(tempTrack->GetLinked() && (ndx+candidatesLookedAt < arr.size()-1) ) {
3110  candidatesLookedAt++;
3111  tempTrack = &**arr[ndx+candidatesLookedAt].first;
3112  }
3113  else
3114  tempTrack = NULL;
3115  }
3116 
3117  if (time1 < time2)
3118  break;
3119 
3120  ndx+=candidatesLookedAt;
3121  }
3122  bArrayTrackLinked = arrTrack.GetLinked();
3123  }
3124  }
3125  arr.insert(arr.begin() + ndx, TrackNodePointer{iter, mTracks.get()});
3126 
3127  lastTrackLinked = track->GetLinked();
3128  }
3129 
3130  // Now apply the permutation
3131  mTracks->Permute(arr);
3132 }
3133 
3134 void AudacityProject::OnSortTime(const CommandContext &WXUNUSED(context) )
3135 {
3137 
3138  PushState(_("Tracks sorted by time"), _("Sort by Time"));
3139 
3140  mTrackPanel->Refresh(false);
3141 }
3142 
3143 void AudacityProject::OnSortName(const CommandContext &WXUNUSED(context) )
3144 {
3146 
3147  PushState(_("Tracks sorted by name"), _("Sort by Name"));
3148 
3149  mTrackPanel->Refresh(false);
3150 }
3151 
3152 void AudacityProject::OnSkipStart(const CommandContext &WXUNUSED(context) )
3153 {
3154  wxCommandEvent evt;
3155 
3156  GetControlToolBar()->OnRewind(evt);
3157  ModifyState(false);
3158 }
3159 
3160 void AudacityProject::OnSkipEnd(const CommandContext &WXUNUSED(context) )
3161 {
3162  wxCommandEvent evt;
3163 
3164  GetControlToolBar()->OnFF(evt);
3165  ModifyState(false);
3166 }
3167 
3168 void AudacityProject::OnSeekLeftShort(const CommandContext &WXUNUSED(context) )
3169 {
3171 }
3172 
3173 void AudacityProject::OnSeekRightShort(const CommandContext &WXUNUSED(context) )
3174 {
3176 }
3177 
3178 void AudacityProject::OnSeekLeftLong(const CommandContext &WXUNUSED(context) )
3179 {
3181 }
3182 
3183 void AudacityProject::OnSeekRightLong(const CommandContext &WXUNUSED(context) )
3184 {
3186 }
3187 
3188 void AudacityProject::OnSelToStart(const CommandContext &WXUNUSED(context) )
3189 {
3190  Rewind(true);
3191  ModifyState(false);
3192 }
3193 
3194 void AudacityProject::OnSelToEnd(const CommandContext &WXUNUSED(context) )
3195 {
3196  SkipEnd(true);
3197  ModifyState(false);
3198 }
3199 
3201 {
3202  OnMoveToLabel(true);
3203 }
3204 
3206 {
3207  OnMoveToLabel(false);
3208 }
3209 
3211 {
3212  // Find the number of label tracks, and ptr to last track found
3213  Track* track = nullptr;
3214  int nLabelTrack = 0;
3216  for (Track* t = iter.First(); t; t = iter.Next()) {
3217  nLabelTrack++;
3218  track = t;
3219  }
3220 
3221  if (nLabelTrack == 0 ) {
3222  mTrackPanel->MessageForScreenReader(_("no label track"));
3223  }
3224  else if (nLabelTrack > 1) { // find first label track, if any, starting at the focused track
3225  track = mTrackPanel->GetFocusedTrack();
3226  while (track && track->GetKind() != Track::Label) {
3227  track = mTracks->GetNext(track, true);
3228  if (!track) {
3229  mTrackPanel->MessageForScreenReader(_("no label track at or below focused track"));
3230  }
3231  }
3232  }
3233 
3234  // If there is a single label track, or there is a label track at or below the focused track
3235  if (track) {
3236  LabelTrack* lt = static_cast<LabelTrack*>(track);
3237  int i;
3238  if (next)
3239  i = lt->FindNextLabel(GetSelection());
3240  else
3241  i = lt->FindPrevLabel(GetSelection());
3242 
3243  if (i >= 0) {
3244  const LabelStruct* label = lt->GetLabel(i);
3245  if (IsAudioActive()) {
3246  OnPlayStop(*this); // stop
3248  RedrawProject();
3249  OnPlayStop(*this); // play
3250  }
3251  else {
3253  mTrackPanel->ScrollIntoView(GetViewInfo().selectedRegion.t0());
3254  RedrawProject();
3255  }
3256 
3257  wxString message;
3258  message.Printf(wxT("%s %d of %d"), label->title, i + 1, lt->GetNumLabels() );
3260  }
3261  else {
3262  mTrackPanel->MessageForScreenReader(_("no labels in label track"));
3263  }
3264  }
3265 }
3266 
3270 
3273 {
3274  TrackListIterator iter( GetTracks() );
3276  if( t == NULL ) // if there isn't one, focus on last
3277  {
3278  t = iter.Last();
3280  mTrackPanel->EnsureVisible( t );
3281  ModifyState(false);
3282  return;
3283  }
3284 
3285  Track* p = NULL;
3286  bool tSelected = false;
3287  bool pSelected = false;
3288  if( shift )
3289  {
3290  p = mTracks->GetPrev( t, true ); // Get previous track
3291  if( p == NULL ) // On first track
3292  {
3293  // JKC: wxBell() is probably for accessibility, so a blind
3294  // user knows they were at the top track.
3295  wxBell();
3297  {
3298  TrackListIterator iter( GetTracks() );
3299  p = iter.Last();
3300  }
3301  else
3302  {
3303  mTrackPanel->EnsureVisible( t );
3304  return;
3305  }
3306  }
3307  tSelected = t->GetSelected();
3308  if (p)
3309  pSelected = p->GetSelected();
3310  if( tSelected && pSelected )
3311  {
3313  ( *mTracks, *t, false, false, GetMixerBoard() );
3314  mTrackPanel->SetFocusedTrack( p ); // move focus to next track down
3315  mTrackPanel->EnsureVisible( p );
3316  ModifyState(false);
3317  return;
3318  }
3319  if( tSelected && !pSelected )
3320  {
3322  ( *mTracks, *p, true, false, GetMixerBoard() );
3323  mTrackPanel->SetFocusedTrack( p ); // move focus to next track down
3324  mTrackPanel->EnsureVisible( p );
3325  ModifyState(false);
3326  return;
3327  }
3328  if( !tSelected && pSelected )
3329  {
3331  ( *mTracks, *p, false, false, GetMixerBoard() );
3332  mTrackPanel->SetFocusedTrack( p ); // move focus to next track down
3333  mTrackPanel->EnsureVisible( p );
3334  ModifyState(false);
3335  return;
3336  }
3337  if( !tSelected && !pSelected )
3338  {
3340  ( *mTracks, *t, true, false, GetMixerBoard() );
3341  mTrackPanel->SetFocusedTrack( p ); // move focus to next track down
3342  mTrackPanel->EnsureVisible( p );
3343  ModifyState(false);
3344  return;
3345  }
3346  }
3347  else
3348  {
3349  p = mTracks->GetPrev( t, true ); // Get next track
3350  if( p == NULL ) // On last track so stay there?
3351  {
3352  wxBell();
3354  {
3355  TrackListIterator iter( GetTracks() );
3356  for( Track *d = iter.First(); d; d = iter.Next( true ) )
3357  {
3358  p = d;
3359  }
3360  mTrackPanel->SetFocusedTrack( p ); // Wrap to the first track
3361  mTrackPanel->EnsureVisible( p );
3362  ModifyState(false);
3363  return;
3364  }
3365  else
3366  {
3367  mTrackPanel->EnsureVisible( t );
3368  return;
3369  }
3370  }
3371  else
3372  {
3373  mTrackPanel->SetFocusedTrack( p ); // move focus to next track down
3374  mTrackPanel->EnsureVisible( p );
3375  ModifyState(false);
3376  return;
3377  }
3378  }
3379 }
3380 
3385 {
3386  Track *t;
3387  Track *n;
3388  TrackListIterator iter( GetTracks() );
3389  bool tSelected,nSelected;
3390 
3391  t = mTrackPanel->GetFocusedTrack(); // Get currently focused track
3392  if( t == NULL ) // if there isn't one, focus on first
3393  {
3394  t = iter.First();
3396  mTrackPanel->EnsureVisible( t );
3397  ModifyState(false);
3398  return;
3399  }
3400 
3401  if( shift )
3402  {
3403  n = mTracks->GetNext( t, true ); // Get next track
3404  if( n == NULL ) // On last track so stay there
3405  {
3406  wxBell();
3408  {
3409  TrackListIterator iter( GetTracks() );
3410  n = iter.First();
3411  }
3412  else
3413  {
3414  mTrackPanel->EnsureVisible( t );
3415  return;
3416  }
3417  }
3418  tSelected = t->GetSelected();
3419  nSelected = n->GetSelected();
3420  if( tSelected && nSelected )
3421  {
3423  ( *mTracks, *t, false, false, GetMixerBoard() );
3424  mTrackPanel->SetFocusedTrack( n ); // move focus to next track down
3425  mTrackPanel->EnsureVisible( n );
3426  ModifyState(false);
3427  return;
3428  }
3429  if( tSelected && !nSelected )
3430  {
3432  ( *mTracks, *n, true, false, GetMixerBoard() );
3433  mTrackPanel->SetFocusedTrack( n ); // move focus to next track down
3434  mTrackPanel->EnsureVisible( n );
3435  ModifyState(false);
3436  return;
3437  }
3438  if( !tSelected && nSelected )
3439  {
3441  ( *mTracks, *n, false, false, GetMixerBoard() );
3442  mTrackPanel->SetFocusedTrack( n ); // move focus to next track down
3443  mTrackPanel->EnsureVisible( n );
3444  ModifyState(false);
3445  return;
3446  }
3447  if( !tSelected && !nSelected )
3448  {
3450  ( *mTracks, *t, true, false, GetMixerBoard() );
3451  mTrackPanel->SetFocusedTrack( n ); // move focus to next track down
3452  mTrackPanel->EnsureVisible( n );
3453  ModifyState(false);
3454  return;
3455  }
3456  }
3457  else
3458  {
3459  n = mTracks->GetNext( t, true ); // Get next track
3460  if( n == NULL ) // On last track so stay there
3461  {
3462  wxBell();
3464  {
3465  TrackListIterator iter( GetTracks() );
3466  n = iter.First();
3467  mTrackPanel->SetFocusedTrack( n ); // Wrap to the first track
3468  mTrackPanel->EnsureVisible( n );
3469  ModifyState(false);
3470  return;
3471  }
3472  else
3473  {
3474  mTrackPanel->EnsureVisible( t );
3475  return;
3476  }
3477  }
3478  else
3479  {
3480  mTrackPanel->SetFocusedTrack( n ); // move focus to next track down
3481  mTrackPanel->EnsureVisible( n );
3482  ModifyState(false);
3483  return;
3484  }
3485  }
3486 }
3487 
3488 void AudacityProject::OnCursorUp(const CommandContext &WXUNUSED(context) )
3489 {
3490  OnPrevTrack( false );
3491 }
3492 
3493 void AudacityProject::OnCursorDown(const CommandContext &WXUNUSED(context) )
3494 {
3495  OnNextTrack( false );
3496 }
3497 
3498 void AudacityProject::OnFirstTrack(const CommandContext &WXUNUSED(context) )
3499 {
3501  if (!t)
3502  return;
3503 
3504  TrackListIterator iter(GetTracks());
3505  Track *f = iter.First();
3506  if (t != f)
3507  {
3509  ModifyState(false);
3510  }
3512 }
3513 
3514 void AudacityProject::OnLastTrack(const CommandContext &WXUNUSED(context) )
3515 {
3517  if (!t)
3518  return;
3519 
3520  TrackListIterator iter(GetTracks());
3521  Track *l = iter.Last();
3522  if (t != l)
3523  {
3525  ModifyState(false);
3526  }
3528 }
3529 
3530 void AudacityProject::OnShiftUp(const CommandContext &WXUNUSED(context) )
3531 {
3532  OnPrevTrack( true );
3533 }
3534 
3535 void AudacityProject::OnShiftDown(const CommandContext &WXUNUSED(context) )
3536 {
3537  OnNextTrack( true );
3538 }
3539 
3540 #include "TrackPanelAx.h"
3541 void AudacityProject::OnToggle(const CommandContext &WXUNUSED(context) )
3542 {
3543  Track *t;
3544 
3545  t = mTrackPanel->GetFocusedTrack(); // Get currently focused track
3546  if (!t)
3547  return;
3548 
3550  ( *mTracks, *t, !t->GetSelected(), true, GetMixerBoard() );
3551  mTrackPanel->EnsureVisible( t );
3552  ModifyState(false);
3553 
3554  mTrackPanel->GetAx().Updated();
3555 
3556  return;
3557 }
3558 
3559 void AudacityProject::HandleListSelection(Track *t, bool shift, bool ctrl,
3560  bool modifyState)
3561 {
3563  ( *GetTracks(), mViewInfo, *t,
3564  shift, ctrl, IsSyncLocked(), GetMixerBoard() );
3565 
3566  if (! ctrl )
3568  Refresh(false);
3569  if (modifyState)
3570  ModifyState(true);
3571 }
3572 
3573 // If this returns true, then there was a key up, and nothing more to do,
3574 // after this function has completed.
3575 // (at most this function just does a ModifyState for the keyup)
3577 {
3578  auto evt = context.pEvt;
3579  bool bKeyUp = (evt) && evt->GetEventType() == wxEVT_KEY_UP;
3580 
3581  if( IsAudioActive() )
3582  return bKeyUp;
3583  if( !bKeyUp )
3584  return false;
3585 
3586  ModifyState(false);
3587  return true;
3588 }
3589 
3591 {
3592  if( !OnlyHandleKeyUp( context ) )
3594 }
3595 
3597 {
3598  if( !OnlyHandleKeyUp( context ) )
3600 }
3601 
3603 {
3605 }
3606 
3608 {
3610 }
3611 
3613 {
3614  OnCursorMove( -mSeekLong );
3615 }
3616 
3618 {
3620 }
3621 
3623 {
3625 }
3626 
3628 {
3630 }
3631 
3633 {
3634  if( !OnlyHandleKeyUp( context ) )
3636 }
3637 
3639 {
3640  if( !OnlyHandleKeyUp( context ) )
3642 }
3643 
3645 {
3646  if( !OnlyHandleKeyUp( context ) )
3648 }
3649 
3651 {
3652  if( !OnlyHandleKeyUp( context ) )
3654 }
3655 
3656 #include "tracks/ui/TimeShiftHandle.h"
3657 
3658 // This function returns the amount moved. Possibly 0.0.
3660  ( ViewInfo &viewInfo, Track *track,
3661  TrackList &trackList, bool syncLocked, bool right )
3662 {
3663  // just dealing with clips in wave tracks for the moment. Note tracks??
3664  if (track && track->GetKind() == Track::Wave) {
3665  ClipMoveState state;
3666 
3667  auto wt = static_cast<WaveTrack*>(track);
3668  auto t0 = viewInfo.selectedRegion.t0();
3669 
3670  state.capturedClip = wt->GetClipAtTime( t0 );
3671  if (state.capturedClip == nullptr && track->GetLinked() && track->GetLink()) {
3672  // the clips in the right channel may be different from the left
3673  track = track->GetLink();
3674  wt = static_cast<WaveTrack*>(track);
3675  state.capturedClip = wt->GetClipAtTime(t0);
3676  }
3677  if (state.capturedClip == nullptr)
3678  return 0.0;
3679 
3680  state.capturedClipIsSelection =
3681  track->GetSelected() && !viewInfo.selectedRegion.isPoint();
3682  state.trackExclusions.clear();
3683 
3685  ( state, viewInfo, *track, trackList, syncLocked, t0 );
3686 
3687  auto desiredT0 = viewInfo.OffsetTimeByPixels( t0, ( right ? 1 : -1 ) );
3688  auto desiredSlideAmount = desiredT0 - t0;
3689 
3690  // set it to a sample point, and minimum of 1 sample point
3691  if (!right)
3692  desiredSlideAmount *= -1;
3693  double nSamples = rint(wt->GetRate() * desiredSlideAmount);
3694  nSamples = std::max(nSamples, 1.0);
3695  desiredSlideAmount = nSamples / wt->GetRate();
3696  if (!right)
3697  desiredSlideAmount *= -1;
3698 
3699  state.hSlideAmount = desiredSlideAmount;
3700  TimeShiftHandle::DoSlideHorizontal( state, trackList, *track );
3701 
3702  // update t0 and t1. There is the possibility that the updated
3703  // t0 may no longer be within the clip due to rounding errors,
3704  // so t0 is adjusted so that it is.
3705  double newT0 = t0 + state.hSlideAmount;
3706  if (newT0 < state.capturedClip->GetStartTime())
3707  newT0 = state.capturedClip->GetStartTime();
3708  if (newT0 > state.capturedClip->GetEndTime())
3709  newT0 = state.capturedClip->GetEndTime();
3710  double diff = viewInfo.selectedRegion.duration();
3711  viewInfo.selectedRegion.setTimes(newT0, newT0 + diff);
3712 
3713  return state.hSlideAmount;
3714  }
3715  return 0.0;
3716 }
3717 
3718 void AudacityProject::DoClipLeftOrRight(bool right, bool keyUp )
3719 {
3720  if (keyUp) {
3722  return;
3723  }
3724 
3725  auto &panel = *GetTrackPanel();
3726 
3727  auto amount = OnClipMove
3728  ( mViewInfo, panel.GetFocusedTrack(),
3729  *GetTracks(), IsSyncLocked(), right );
3730 
3731  panel.ScrollIntoView(mViewInfo.selectedRegion.t0());
3732  panel.Refresh(false);
3733 
3734  if (amount != 0.0) {
3735  wxString message = right? _("Time shifted clips to the right") :
3736  _("Time shifted clips to the left");
3737 
3738  // The following use of the UndoPush flags is so that both a single
3739  // keypress (keydown, then keyup), and holding down a key
3740  // (multiple keydowns followed by a keyup) result in a single
3741  // entry in Audacity's history dialog.
3742  PushState(message, _("Time-Shift"), UndoPush::CONSOLIDATE);
3743  }
3744 
3745  if ( amount == 0.0 )
3746  panel.MessageForScreenReader( _("clip not moved"));
3747 }
3748 
3750 {
3751  auto evt = context.pEvt;
3752  if (evt)
3753  DoClipLeftOrRight( false, evt->GetEventType() == wxEVT_KEY_UP );
3754  else { // called from menu, so simulate keydown and keyup
3755  DoClipLeftOrRight( false, false );
3756  DoClipLeftOrRight( false, true );
3757  }
3758 }
3759 
3761 {
3762  auto evt = context.pEvt;
3763  if (evt)
3764  DoClipLeftOrRight( true, evt->GetEventType() == wxEVT_KEY_UP );
3765  else { // called from menu, so simulate keydown and keyup
3766  DoClipLeftOrRight( true, false );
3767  DoClipLeftOrRight( true, true );
3768  }
3769 }
3770 
3771 //this pops up a dialog which allows the left selection to be set.
3772 //If playing/recording is happening, it sets the left selection at
3773 //the current play position.
3775 {
3776  bool bSelChanged = false;
3778  {
3779  double indicator = gAudioIO->GetStreamTime();
3780  mViewInfo.selectedRegion.setT0(indicator, false);
3781  bSelChanged = true;
3782  }
3783  else
3784  {
3785  auto fmt = GetSelectionFormat();
3786  TimeDialog dlg(this, _("Set Left Selection Boundary"),
3787  fmt, mRate, mViewInfo.selectedRegion.t0(), _("Position"));
3788 
3789  if (wxID_OK == dlg.ShowModal())
3790  {
3791  //Get the value from the dialog
3793  std::max(0.0, dlg.GetTimeValue()), false);
3794  bSelChanged = true;
3795  }
3796  }
3797 
3798  if (bSelChanged)
3799  {
3800  ModifyState(false);
3801  mTrackPanel->Refresh(false);
3802  }
3803 }
3804 
3805 
3807 {
3808  bool bSelChanged = false;
3810  {
3811  double indicator = gAudioIO->GetStreamTime();
3812  mViewInfo.selectedRegion.setT1(indicator, false);
3813  bSelChanged = true;
3814  }
3815  else
3816  {
3817  auto fmt = GetSelectionFormat();
3818  TimeDialog dlg(this, _("Set Right Selection Boundary"),
3819  fmt, mRate, mViewInfo.selectedRegion.t1(), _("Position"));
3820 
3821  if (wxID_OK == dlg.ShowModal())
3822  {
3823  //Get the value from the dialog
3825  std::max(0.0, dlg.GetTimeValue()), false);
3826  bSelChanged = true;
3827  }
3828  }
3829 
3830  if (bSelChanged)
3831  {
3832  ModifyState(false);
3833  mTrackPanel->Refresh(false);
3834  }
3835 }
3836 
3838 {
3839  // Focus won't take in a dock unless at least one descendant window
3840  // accepts focus. Tell controls to take focus for the duration of this
3841  // function, only. Outside of this, they won't steal the focus when
3842  // clicked.
3843  auto temp1 = AButton::TemporarilyAllowFocus();
3844  auto temp2 = ASlider::TemporarilyAllowFocus();
3845  auto temp3 = MeterPanel::TemporarilyAllowFocus();
3846 
3847 
3848  // Define the set of windows we rotate among.
3849  static const unsigned rotationSize = 3u;
3850 
3851  wxWindow *const begin [rotationSize] = {
3852  GetTopPanel(),
3853  GetTrackPanel(),
3854  mToolManager->GetBotDock(),
3855  };
3856 
3857  const auto end = begin + rotationSize;
3858 
3859  // helper functions
3860  auto IndexOf = [&](wxWindow *pWindow) {
3861  return std::find(begin, end, pWindow) - begin;
3862  };
3863 
3864  auto FindAncestor = [&]() {
3865  wxWindow *pWindow = wxWindow::FindFocus();
3866  unsigned index = rotationSize;
3867  while ( pWindow &&
3868  (rotationSize == (index = IndexOf(pWindow) ) ) )
3869  pWindow = pWindow->GetParent();
3870  return index;
3871  };
3872 
3873  const auto idx = FindAncestor();
3874  if (idx == rotationSize)
3875  return;
3876 
3877  auto idx2 = idx;
3878  auto increment = (forward ? 1 : rotationSize - 1);
3879 
3880  while( idx != (idx2 = (idx2 + increment) % rotationSize) ) {
3881  wxWindow *toFocus = begin[idx2];
3882  bool bIsAnEmptyDock=false;
3883  if( idx2 != 1 )
3884  bIsAnEmptyDock = ((idx2==0)?mToolManager->GetTopDock() : mToolManager->GetBotDock())->
3885  GetChildren().GetCount() < 1;
3886 
3887  // Skip docks that are empty (Bug 1564).
3888  if( !bIsAnEmptyDock ){
3889  toFocus->SetFocus();
3890  if ( FindAncestor() == idx2 )
3891  // The focus took!
3892  break;
3893  }
3894  }
3895 }
3896 
3897 void AudacityProject::NextFrame(const CommandContext &WXUNUSED(context) )
3898 {
3899  NextOrPrevFrame(true);
3900 }
3901 
3902 void AudacityProject::PrevFrame(const CommandContext &WXUNUSED(context) )
3903 {
3904  NextOrPrevFrame(false);
3905 }
3906 
3907 void AudacityProject::NextWindow(const CommandContext &WXUNUSED(context) )
3908 {
3909  wxWindow *w = wxGetTopLevelParent(wxWindow::FindFocus());
3910  const auto & list = GetChildren();
3911  auto iter = list.begin(), end = list.end();
3912 
3913  // If the project window has the current focus, start the search with the first child
3914  if (w == this)
3915  {
3916  }
3917  // Otherwise start the search with the current window's next sibling
3918  else
3919  {
3920  // Find the window in this projects children. If the window with the
3921  // focus isn't a child of this project (like when a dialog is created
3922  // without specifying a parent), then we'll get back NULL here.
3923  while (iter != end && *iter != w)
3924  ++iter;
3925  if (iter != end)
3926  ++iter;
3927  }
3928 
3929  // Search for the next toplevel window
3930  for (; iter != end; ++iter)
3931  {
3932  // If it's a toplevel, visible (we have hidden windows) and is enabled,
3933  // then we're done. The IsEnabled() prevents us from moving away from
3934  // a modal dialog because all other toplevel windows will be disabled.
3935  w = *iter;
3936  if (w->IsTopLevel() && w->IsShown() && w->IsEnabled())
3937  {
3938  break;
3939  }
3940  }
3941 
3942  // Ran out of siblings, so make the current project active
3943  if ((iter == end) && IsEnabled())
3944  {
3945  w = this;
3946  }
3947 
3948  // And make sure it's on top (only for floating windows...project window will not raise)
3949  // (Really only works on Windows)
3950  w->Raise();
3951 
3952 
3953 #if defined(__WXMAC__) || defined(__WXGTK__)
3954  // bug 868
3955  // Simulate a TAB key press before continuing, else the cycle of
3956  // navigation among top level windows stops because the keystrokes don't
3957  // go to the CommandManager.
3958  if (dynamic_cast<wxDialog*>(w)) {
3959  w->SetFocus();
3960  }
3961 #endif
3962 }
3963 
3964 void AudacityProject::PrevWindow(const CommandContext &WXUNUSED(context) )
3965 {
3966  wxWindow *w = wxGetTopLevelParent(wxWindow::FindFocus());
3967  const auto & list = GetChildren();
3968  auto iter = list.rbegin(), end = list.rend();
3969 
3970  // If the project window has the current focus, start the search with the last child
3971  if (w == this)
3972  {
3973  }
3974  // Otherwise start the search with the current window's previous sibling
3975  else
3976  {
3977  while (iter != end && *iter != w)
3978  ++iter;
3979  if (iter != end)
3980  ++iter;
3981  }
3982 
3983  // Search for the previous toplevel window
3984  for (; iter != end; ++iter)
3985  {
3986  // If it's a toplevel and is visible (we have come hidden windows), then we're done
3987  w = *iter;
3988  if (w->IsTopLevel() && w->IsShown() && IsEnabled())
3989  {
3990  break;
3991  }
3992  }
3993 
3994  // Ran out of siblings, so make the current project active
3995  if ((iter == end) && IsEnabled())
3996  {
3997  w = this;
3998  }
3999 
4000  // And make sure it's on top (only for floating windows...project window will not raise)
4001  // (Really only works on Windows)
4002  w->Raise();
4003 
4004 
4005 #if defined(__WXMAC__) || defined(__WXGTK__)
4006  // bug 868
4007  // Simulate a TAB key press before continuing, else the cycle of
4008  // navigation among top level windows stops because the keystrokes don't
4009  // go to the CommandManager.
4010  if (dynamic_cast<wxDialog*>(w)) {
4011  w->SetFocus();
4012  }
4013 #endif
4014 }
4015 
4018 void AudacityProject::OnTrackPan(const CommandContext &WXUNUSED(context) )
4019 {
4020  Track *const track = mTrackPanel->GetFocusedTrack();
4021  if (!track || (track->GetKind() != Track::Wave)) {
4022  return;
4023  }
4024  const auto wt = static_cast<WaveTrack*>(track);
4025 
4026  LWSlider *slider = mTrackPanel->PanSlider(wt);
4027  if (slider->ShowDialog()) {
4028  SetTrackPan(wt, slider);
4029  }
4030 }
4031 
4032 void AudacityProject::OnTrackPanLeft(const CommandContext &WXUNUSED(context) )
4033 {
4034  Track *const track = mTrackPanel->GetFocusedTrack();
4035  if (!track || (track->GetKind() != Track::Wave)) {
4036  return;
4037  }
4038  const auto wt = static_cast<WaveTrack*>(track);
4039 
4040  LWSlider *slider = mTrackPanel->PanSlider(wt);
4041  slider->Decrease(1);
4042  SetTrackPan(wt, slider);
4043 }
4044 
4045 void AudacityProject::OnTrackPanRight(const CommandContext &WXUNUSED(context) )
4046 {
4047  Track *const track = mTrackPanel->GetFocusedTrack();
4048  if (!track || (track->GetKind() != Track::Wave)) {
4049  return;
4050  }
4051  const auto wt = static_cast<WaveTrack*>(track);
4052 
4053  LWSlider *slider = mTrackPanel->PanSlider(wt);
4054  slider->Increase(1);
4055  SetTrackPan(wt, slider);
4056 }
4057 
4058 void AudacityProject::OnTrackGain(const CommandContext &WXUNUSED(context) )
4059 {
4061  Track *const track = mTrackPanel->GetFocusedTrack();
4062  if (!track || (track->GetKind() != Track::Wave)) {
4063  return;
4064  }
4065  const auto wt = static_cast<WaveTrack*>(track);
4066 
4067  LWSlider *slider = mTrackPanel->GainSlider(wt);
4068  if (slider->ShowDialog()) {
4069  SetTrackGain(wt, slider);
4070  }
4071 }
4072 
4073 void AudacityProject::OnTrackGainInc(const CommandContext &WXUNUSED(context) )
4074 {
4075  Track *const track = mTrackPanel->GetFocusedTrack();
4076  if (!track || (track->GetKind() != Track::Wave)) {
4077  return;
4078  }
4079  const auto wt = static_cast<WaveTrack*>(track);
4080 
4081  LWSlider *slider = mTrackPanel->GainSlider(wt);
4082  slider->Increase(1);
4083  SetTrackGain(wt, slider);
4084 }
4085 
4086 void AudacityProject::OnTrackGainDec(const CommandContext &WXUNUSED(context) )
4087 {
4088  Track *const track = mTrackPanel->GetFocusedTrack();
4089  if (!track || (track->GetKind() != Track::Wave)) {
4090  return;
4091  }
4092  const auto wt = static_cast<WaveTrack*>(track);
4093 
4094  LWSlider *slider = mTrackPanel->GainSlider(wt);
4095  slider->Decrease(1);
4096  SetTrackGain(wt, slider);
4097 }
4098 
4099 void AudacityProject::OnTrackMenu(const CommandContext &WXUNUSED(context) )
4100 {
4102 }
4103 
4104 void AudacityProject::OnTrackMute(const CommandContext &WXUNUSED(context) )
4105 {
4106  Track *t = NULL;
4107  if (!t) {
4109  if (!dynamic_cast<PlayableTrack*>(t))
4110  return;
4111  }
4112  DoTrackMute(t, false);
4113 }
4114 
4115 void AudacityProject::OnTrackSolo(const CommandContext &WXUNUSED(context) )
4116 {
4117  Track *t = NULL;
4118  if (!t)
4119  {
4121  if (!dynamic_cast<PlayableTrack*>(t))
4122  return;
4123  }
4124  DoTrackSolo(t, false);
4125 }
4126 
4127 void AudacityProject::OnTrackClose(const CommandContext &WXUNUSED(context) )
4128 {
4130  if (!t)
4131  return;
4132 
4133  if (IsAudioActive())
4134  {
4135  this->TP_DisplayStatusMessage(_("Can't delete track with active audio"));
4136  wxBell();
4137  return;
4138  }
4139 
4140  RemoveTrack(t);
4141 
4143  GetTrackPanel()->Refresh(false);
4144 }
4145 
4146 void AudacityProject::OnTrackMoveUp(const CommandContext &WXUNUSED(context) )
4147 {
4148  Track *const focusedTrack = mTrackPanel->GetFocusedTrack();
4149  if (mTracks->CanMoveUp(focusedTrack)) {
4150  MoveTrack(focusedTrack, OnMoveUpID);
4151  mTrackPanel->Refresh(false);
4152  }
4153 }
4154 
4155 void AudacityProject::OnTrackMoveDown(const CommandContext &WXUNUSED(context) )
4156 {
4157  Track *const focusedTrack = mTrackPanel->GetFocusedTrack();
4158  if (mTracks->CanMoveDown(focusedTrack)) {
4159  MoveTrack(focusedTrack, OnMoveDownID);
4160  mTrackPanel->Refresh(false);
4161  }
4162 }
4163 
4164 void AudacityProject::OnTrackMoveTop(const CommandContext &WXUNUSED(context) )
4165 {
4166  Track *const focusedTrack = mTrackPanel->GetFocusedTrack();
4167  if (mTracks->CanMoveUp(focusedTrack)) {
4168  MoveTrack(focusedTrack, OnMoveTopID);
4169  mTrackPanel->Refresh(false);
4170  }
4171 }
4172 
4174 {
4175  Track *const focusedTrack = mTrackPanel->GetFocusedTrack();
4176  if (mTracks->CanMoveDown(focusedTrack)) {
4177  MoveTrack(focusedTrack, OnMoveBottomID);
4178  mTrackPanel->Refresh(false);
4179  }
4180 }
4181 
4183 
4185 {
4186  wxString longDesc, shortDesc;
4187 
4188  auto pt = dynamic_cast<PlayableTrack*>(target);
4189  switch (choice)
4190  {
4191  case OnMoveTopID:
4192  /* i18n-hint: Past tense of 'to move', as in 'moved audio track up'.*/
4193  longDesc = _("Moved '%s' to Top");
4194  shortDesc = _("Move Track to Top");
4195 
4196  while (mTracks->CanMoveUp(target)) {
4197  if (mTracks->Move(target, true)) {
4198  MixerBoard* pMixerBoard = this->GetMixerBoard(); // Update mixer board.
4199  if (pMixerBoard && pt)
4200  pMixerBoard->MoveTrackCluster(pt, true);
4201  }
4202  }
4203  break;
4204  case OnMoveBottomID:
4205  /* i18n-hint: Past tense of 'to move', as in 'moved audio track up'.*/
4206  longDesc = _("Moved '%s' to Bottom");
4207  shortDesc = _("Move Track to Bottom");
4208 
4209  while (mTracks->CanMoveDown(target)) {
4210  if (mTracks->Move(target, false)) {
4211  MixerBoard* pMixerBoard = this->GetMixerBoard(); // Update mixer board.
4212  if (pMixerBoard && pt)
4213  pMixerBoard->MoveTrackCluster(pt, false);
4214  }
4215  }
4216  break;
4217  default:
4218  bool bUp = (OnMoveUpID == choice);
4219 
4220  if (mTracks->Move(target, bUp)) {
4221  MixerBoard* pMixerBoard = this->GetMixerBoard();
4222  if (pMixerBoard && pt)
4223  pMixerBoard->MoveTrackCluster(pt, bUp);
4224  }
4225  longDesc =
4226  /* i18n-hint: Past tense of 'to move', as in 'moved audio track up'.*/
4227  bUp? _("Moved '%s' Up")
4228  : _("Moved '%s' Down");
4229  shortDesc =
4230  /* i18n-hint: Past tense of 'to move', as in 'moved audio track up'.*/
4231  bUp? _("Move Track Up")
4232  : _("Move Track Down");
4233 
4234  }
4235 
4236  longDesc = longDesc.Format(target->GetName());
4237 
4238  PushState(longDesc, shortDesc);
4239  GetTrackPanel()->Refresh(false);
4240 }
4241 
4242 void AudacityProject::OnInputDevice(const CommandContext &WXUNUSED(context) )
4243 {
4245  if (tb) {
4246  tb->ShowInputDialog();
4247  }
4248 }
4249 
4250 void AudacityProject::OnOutputDevice(const CommandContext &WXUNUSED(context) )
4251 {
4253  if (tb) {
4254  tb->ShowOutputDialog();
4255  }
4256 }
4257 
4258 void AudacityProject::OnAudioHost(const CommandContext &WXUNUSED(context) )
4259 {
4261  if (tb) {
4262  tb->ShowHostDialog();
4263  }
4264 }
4265 
4266 void AudacityProject::OnInputChannels(const CommandContext &WXUNUSED(context) )
4267 {
4269  if (tb) {
4270  tb->ShowChannelsDialog();
4271  }
4272 }
4273 
4274 void AudacityProject::OnOutputGain(const CommandContext &WXUNUSED(context) )
4275 {
4276  MixerToolBar *tb = GetMixerToolBar();
4277  if (tb) {
4278  tb->ShowOutputGainDialog();
4279  }
4280 }
4281 
4282 void AudacityProject::OnInputGain(const CommandContext &WXUNUSED(context) )
4283 {
4284  MixerToolBar *tb = GetMixerToolBar();
4285  if (tb) {
4286  tb->ShowInputGainDialog();
4287  }
4288 }
4289 
4290 void AudacityProject::OnOutputGainInc(const CommandContext &WXUNUSED(context) )
4291 {
4292  MixerToolBar *tb = GetMixerToolBar();
4293  if (tb) {
4294  tb->AdjustOutputGain(1);
4295  }
4296 }
4297 
4298 void AudacityProject::OnOutputGainDec(const CommandContext &WXUNUSED(context) )
4299 {
4300  MixerToolBar *tb = GetMixerToolBar();
4301  if (tb) {
4302  tb->AdjustOutputGain(-1);
4303  }
4304 }
4305 
4306 void AudacityProject::OnInputGainInc(const CommandContext &WXUNUSED(context) )
4307 {
4308  MixerToolBar *tb = GetMixerToolBar();
4309  if (tb) {
4310  tb->AdjustInputGain(1);
4311  }
4312 }
4313 
4314 void AudacityProject::OnInputGainDec(const CommandContext &WXUNUSED(context) )
4315 {
4316  MixerToolBar *tb = GetMixerToolBar();
4317  if (tb) {
4318  tb->AdjustInputGain(-1);
4319  }
4320 }
4321 
4322 void AudacityProject::OnPlayAtSpeed(const CommandContext &WXUNUSED(context) )
4323 {
4325  if (tb) {
4326  tb->PlayAtSpeed(false, false);
4327  }
4328 }
4329 
4331 {
4333  if (tb) {
4334  tb->PlayAtSpeed(true, false);
4335  }
4336 }
4337 
4339 {
4341  if (tb) {
4342  tb->PlayAtSpeed(false, true);
4343  }
4344 }
4345 
4346 void AudacityProject::OnSetPlaySpeed(const CommandContext &WXUNUSED(context) )
4347 {
4349  if (tb) {
4350  tb->ShowPlaySpeedDialog();
4351  }
4352 }
4353 
4354 void AudacityProject::OnPlaySpeedInc(const CommandContext &WXUNUSED(context) )
4355 {
4357  if (tb) {
4358  tb->AdjustPlaySpeed(0.1f);
4359  }
4360 }
4361 
4362 void AudacityProject::OnPlaySpeedDec(const CommandContext &WXUNUSED(context) )
4363 {
4365  if (tb) {
4366  tb->AdjustPlaySpeed(-0.1f);
4367  }
4368 }
4369 
4371 {
4372  // Window is 1/100th of a second.
4373  auto windowSize = size_t(std::max(1.0, GetRate() / 100));
4374  Floats dist{ windowSize, true };
4375 
4376  TrackListIterator iter(GetTracks());
4377  Track *track = iter.First();
4378  int nTracks = 0;
4379  while (track) {
4380  if (!track->GetSelected() || track->GetKind() != (Track::Wave)) {
4381  track = iter.Next();
4382  continue;
4383  }
4384  WaveTrack *one = (WaveTrack *)track;
4385  auto oneWindowSize = size_t(std::max(1.0, one->GetRate() / 100));
4386  Floats oneDist{ oneWindowSize };
4387  auto s = one->TimeToLongSamples(t0);
4388  // fillTwo to ensure that missing values are treated as 2, and hence do not
4389  // get used as zero crossings.
4390  one->Get((samplePtr)oneDist.get(), floatSample,
4391  s - (int)oneWindowSize/2, oneWindowSize, fillTwo);
4392 
4393 
4394  // Looking for actual crossings.
4395  double prev = 2.0;
4396  for(size_t i=0; i<oneWindowSize; i++){
4397  float fDist = fabs( oneDist[i]); // score is absolute value
4398  if( prev * oneDist[i] > 0 ) // both same sign? No good.
4399  fDist = fDist + 0.4; // No good if same sign.
4400  else if( prev > 0.0 )
4401  fDist = fDist + 0.1; // medium penalty for downward crossing.
4402  prev = oneDist[i];
4403  oneDist[i] = fDist;
4404  }
4405 
4406  // TODO: The mixed rate zero crossing code is broken,
4407  // if oneWindowSize > windowSize we'll miss out some
4408  // samples - so they will still be zero, so we'll use them.
4409  for(size_t i = 0; i < windowSize; i++) {
4410  size_t j;
4411  if (windowSize != oneWindowSize)
4412  j = i * (oneWindowSize-1) / (windowSize-1);
4413  else
4414  j = i;
4415 
4416  dist[i] += oneDist[j];
4417  // Apply a small penalty for distance from the original endpoint
4418  // We'll always prefer an upward
4419  dist[i] += 0.1 * (abs(int(i) - int(windowSize/2))) / float(windowSize/2);
4420  }
4421  nTracks++;
4422  track = iter.Next();
4423  }
4424 
4425  // Find minimum
4426  int argmin = 0;
4427  float min = 3.0;
4428  for(size_t i=0; i<windowSize; i++) {
4429  if (dist[i] < min) {
4430  argmin = i;
4431  min = dist[i];
4432  }
4433  }
4434 
4435  // If we're worse than 0.2 on average, on one track, then no good.
4436  if(( nTracks == 1 ) && ( min > (0.2*nTracks) ))
4437  return t0;
4438  // If we're worse than 0.6 on average, on multi-track, then no good.
4439  if(( nTracks > 1 ) && ( min > (0.6*nTracks) ))
4440  return t0;
4441 
4442  return t0 + (argmin - (int)windowSize/2)/GetRate();
4443 }
4444 
4445 void AudacityProject::OnZeroCrossing(const CommandContext &WXUNUSED(context) )
4446 {
4447  const double t0 = NearestZeroCrossing(mViewInfo.selectedRegion.t0());
4450  else {
4451  const double t1 = NearestZeroCrossing(mViewInfo.selectedRegion.t1());
4452  // Empty selection is generally not much use, so do not make it if empty.
4453  if( fabs( t1 - t0 ) * GetRate() > 1.5 )
4455  }
4456 
4457  ModifyState(false);
4458 
4459  mTrackPanel->Refresh(false);
4460 }
4461 
4462 
4466 bool AudacityProject::DoAudacityCommand(const PluginID & ID, const CommandContext & context, int flags)
4467 {
4468  const PluginDescriptor *plug = PluginManager::Get().GetPlugin(ID);
4469  if (!plug)
4470  return false;
4471 
4472  if (flags & OnEffectFlags::kConfigured)
4473  {
4474  OnStop(*this);
4475 // SelectAllIfNone();
4476  }
4477 
4479  bool success = em.DoAudacityCommand(ID,
4480  context,
4481  this,
4482  (flags & OnEffectFlags::kConfigured) == 0);
4483 
4484  if (!success)
4485  return false;
4486 
4487 /*
4488  if (em.GetSkipStateFlag())
4489  flags = flags | OnEffectFlags::kSkipState;
4490 
4491  if (!(flags & OnEffectFlags::kSkipState))
4492  {
4493  wxString shortDesc = em.GetCommandName(ID);
4494  wxString longDesc = em.GetCommandDescription(ID);
4495  PushState(longDesc, shortDesc);
4496  }
4497 */
4498  RedrawProject();
4499  return true;
4500 }
4501 
4502 
4503 
4504 //
4505 // Effect Menus
4506 //
4507 
4512 bool AudacityProject::DoEffect(const PluginID & ID, const CommandContext &WXUNUSED(context), int flags)
4513 {
4514  const PluginDescriptor *plug = PluginManager::Get().GetPlugin(ID);
4515  if (!plug)
4516  return false;
4517 
4518  EffectType type = plug->GetEffectType();
4519 
4520  // Make sure there's no activity since the effect is about to be applied
4521  // to the project's tracks. Mainly for Apply during RTP, but also used
4522  // for batch commands
4523  if (flags & OnEffectFlags::kConfigured)
4524  {
4525  OnStop(*this);
4526  SelectAllIfNone();
4527  }
4528 
4530 
4531  auto nTracksOriginally = GetTrackCount();
4532  TrackListIterator iter(GetTracks());
4533  Track *t = iter.First();
4534  WaveTrack *newTrack{};
4535  wxWindow *focus = wxWindow::FindFocus();
4536 
4537  bool success = false;
4538  auto cleanup = finally( [&] {
4539 
4540  if (!success) {
4541  if (newTrack) {
4542  mTracks->Remove(newTrack);
4543  mTrackPanel->Refresh(false);
4544  }
4545 
4546  // For now, we're limiting realtime preview to a single effect, so
4547  // make sure the menus reflect that fact that one may have just been
4548  // opened.
4549  UpdateMenus(false);
4550  }
4551 
4552  } );
4553 
4554  //double prevEndTime = mTracks->GetEndTime();
4555  int count = 0;
4556  bool clean = true;
4557  while (t) {
4558  if (t->GetSelected() && t->GetKind() == (Track::Wave)) {
4559  if (t->GetEndTime() != 0.0) clean = false;
4560  count++;
4561  }
4562  t = iter.Next();
4563  }
4564 
4565  if (count == 0) {
4566  // No tracks were selected...
4567  if (type == EffectTypeGenerate) {
4568  // Create a NEW track for the generated audio...
4569  newTrack = static_cast<WaveTrack*>(mTracks->Add(mTrackFactory->NewWaveTrack()));
4570  newTrack->SetSelected(true);
4571  }
4572  }
4573 
4575 
4576  success = em.DoEffect(ID, this, mRate,
4579  (flags & OnEffectFlags::kConfigured) == 0);
4580 
4581  if (!success)
4582  return false;
4583 
4584  if (em.GetSkipStateFlag())
4585  flags = flags | OnEffectFlags::kSkipState;
4586 
4587  if (!(flags & OnEffectFlags::kSkipState))
4588  {
4589  wxString shortDesc = em.GetCommandName(ID);
4590  wxString longDesc = em.GetCommandDescription(ID);
4591  PushState(longDesc, shortDesc);
4592  }
4593 
4594  if (!(flags & OnEffectFlags::kDontRepeatLast))
4595  {
4596  // Only remember a successful effect, don't remember insert,
4597  // or analyze effects.
4598  if (type == EffectTypeProcess) {
4599  wxString shortDesc = em.GetCommandName(ID);
4600  mLastEffect = ID;
4601  wxString lastEffectDesc;
4602  /* i18n-hint: %s will be the name of the effect which will be
4603  * repeated if this menu item is chosen */
4604  lastEffectDesc.Printf(_("Repeat %s"), shortDesc);
4605  mCommandManager.Modify(wxT("RepeatLastEffect"), lastEffectDesc);
4606  }
4607  }
4608 
4609  //STM:
4610  //The following automatically re-zooms after sound was generated.
4611  // IMO, it was disorienting, removing to try out without re-fitting
4612  //mchinen:12/14/08 reapplying for generate effects
4613  if (type == EffectTypeGenerate)
4614  {
4615  if (count == 0 || (clean && mViewInfo.selectedRegion.t0() == 0.0))
4616  OnZoomFit(*this);
4617  // mTrackPanel->Refresh(false);
4618  }
4619  RedrawProject();
4620  if (focus != nullptr) {
4621  focus->SetFocus();
4622  }
4623 
4624  // A fix for Bug 63
4625  // New tracks added? Scroll them into view so that user sees them.
4626  // Don't care what track type. An analyser might just have added a
4627  // Label track and we want to see it.
4628  if( GetTrackCount() > nTracksOriginally ){
4629  // 0.0 is min scroll position, 1.0 is max scroll position.
4630  GetTrackPanel()->VerticalScroll( 1.0 );
4631  } else {
4633  mTrackPanel->Refresh(false);
4634  }
4635 
4636  return true;
4637 }
4638 
4640 {
4641  DoEffect(context.parameter, context, 0);
4642 }
4643 
4645 {
4646  if (!mLastEffect.IsEmpty())
4647  {
4649  }
4650 }
4651 
4652 
4654  for( size_t i = 0; i < gAudacityProjects.size(); i++ ) {
4655  AudacityProject *p = gAudacityProjects[i].get();
4656 
4657  p->RebuildMenuBar();
4658 #if defined(__WXGTK__)
4659  // Workaround for:
4660  //
4661  // http://bugzilla.audacityteam.org/show_bug.cgi?id=458
4662  //
4663  // This workaround should be removed when Audacity updates to wxWidgets 3.x which has a fix.
4664  wxRect r = p->GetRect();
4665  p->SetSize(wxSize(1,1));
4666  p->SetSize(r.GetSize());
4667 #endif
4668  }
4669 }
4670 
4672 {
4673  if (PluginManager::Get().ShowManager(this, type))
4675 }
4676 
4678 {
4680 }
4681 
4682 void AudacityProject::OnManageEffects(const CommandContext &WXUNUSED(context) )
4683 {
4685 }
4686 
4688 {
4690 }
4691 
4692 void AudacityProject::OnManageTools(const CommandContext &WXUNUSED(context) )
4693 {
4695 }
4696 
4697 
4699 {
4700  DoEffect(EffectManager::Get().GetEffectByIdentifier(wxT("StereoToMono")),
4701  context,
4703 }
4704 
4706 {
4707  wxLogDebug( "Command was: %s", ctx.parameter);
4708  DoAudacityCommand(EffectManager::Get().GetEffectByIdentifier(ctx.parameter),
4709  ctx,
4710  OnEffectFlags::kNone); // Not configured, so prompt user.
4711 }
4712 
4713 //
4714 // File Menu
4715 //
4716 
4717 void AudacityProject::OnNew(const CommandContext &WXUNUSED(context) )
4718 {
4720 }
4721 
4722 void AudacityProject::OnOpen(const CommandContext &WXUNUSED(context) )
4723 {
4724  OpenFiles(this);
4725 }
4726 
4727 void AudacityProject::OnClose(const CommandContext &WXUNUSED(context) )
4728 {
4729  mMenuClose = true;
4730  Close();
4731 }
4732 
4733 void AudacityProject::OnSave(const CommandContext &WXUNUSED(context) )
4734 {
4735  Save();
4736 }
4737 
4738 void AudacityProject::OnSaveAs(const CommandContext &WXUNUSED(context) )
4739 {
4740  SaveAs();
4741 }
4742 
4743 void AudacityProject::OnSaveCopy(const CommandContext &WXUNUSED(context) )
4744 {
4745  SaveAs(true, true);
4746 }
4747 
4748 #ifdef USE_LIBVORBIS
4749  void AudacityProject::OnSaveCompressed(const CommandContext &WXUNUSED(context) )
4750  {
4751  SaveAs(true);
4752  }
4753 #endif
4754 
4756 {
4757  ShowDependencyDialogIfNeeded(this, false);
4758 }
4759 
4760 void AudacityProject::OnExit(const CommandContext &WXUNUSED(context) )
4761 {
4762