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"
142 
143 enum {
149  // The next two are only in one subMenu, so more easily handled at the end.
152 };
153 
154 // Post Timer Recording Actions
155 // Ensure this matches the enum in TimerRecordDialog.cpp
156 enum {
164 };
165 
168 //
169 // Effects menu arrays
170 //
171 WX_DEFINE_ARRAY_PTR(const PluginDescriptor *, EffectPlugs);
172 static int SortEffectsByName(const PluginDescriptor **a, const PluginDescriptor **b)
173 {
174  wxString akey = (*a)->GetTranslatedName();
175  wxString bkey = (*b)->GetTranslatedName();
176 
177  akey += (*a)->GetPath();
178  bkey += (*b)->GetPath();
179 
180  return akey.CmpNoCase(bkey);
181 }
182 
184 {
185  wxString akey = (*a)->GetTranslatedVendor();
186  wxString bkey = (*b)->GetTranslatedVendor();
187 
188  if (akey.IsEmpty())
189  {
190  akey = _("Uncategorized");
191  }
192  if (bkey.IsEmpty())
193  {
194  bkey = _("Uncategorized");
195  }
196 
197  akey += (*a)->GetTranslatedName();
198  bkey += (*b)->GetTranslatedName();
199 
200  akey += (*a)->GetPath();
201  bkey += (*b)->GetPath();
202 
203  return akey.CmpNoCase(bkey);
204 }
205 
207 {
208  wxString akey = (*a)->GetTranslatedVendor();
209  wxString bkey = (*b)->GetTranslatedVendor();
210 
211  if ((*a)->IsEffectDefault())
212  {
213  akey = wxEmptyString;
214  }
215  if ((*b)->IsEffectDefault())
216  {
217  bkey = wxEmptyString;
218  }
219 
220  akey += (*a)->GetTranslatedName();
221  bkey += (*b)->GetTranslatedName();
222 
223  akey += (*a)->GetPath();
224  bkey += (*b)->GetPath();
225 
226  return akey.CmpNoCase(bkey);
227 }
228 
230 {
231  wxString akey = (*a)->GetTranslatedEffectFamily();
232  wxString bkey = (*b)->GetTranslatedEffectFamily();
233 
234  if (akey.IsEmpty())
235  {
236  akey = _("Uncategorized");
237  }
238  if (bkey.IsEmpty())
239  {
240  bkey = _("Uncategorized");
241  }
242 
243  if ((*a)->IsEffectDefault())
244  {
245  akey = wxEmptyString;
246  }
247  if ((*b)->IsEffectDefault())
248  {
249  bkey = wxEmptyString;
250  }
251 
252  akey += (*a)->GetTranslatedName();
253  bkey += (*b)->GetTranslatedName();
254 
255  akey += (*a)->GetPath();
256  bkey += (*b)->GetPath();
257 
258  return akey.CmpNoCase(bkey);
259 }
260 
261 static int SortEffectsByType(const PluginDescriptor **a, const PluginDescriptor **b)
262 {
263  wxString akey = (*a)->GetTranslatedEffectFamily();
264  wxString bkey = (*b)->GetTranslatedEffectFamily();
265 
266  if (akey.IsEmpty())
267  {
268  akey = _("Uncategorized");
269  }
270  if (bkey.IsEmpty())
271  {
272  bkey = _("Uncategorized");
273  }
274 
275  akey += (*a)->GetTranslatedName();
276  bkey += (*b)->GetTranslatedName();
277 
278  akey += (*a)->GetPath();
279  bkey += (*b)->GetPath();
280 
281  return akey.CmpNoCase(bkey);
282 }
283 
287 
288 // To supply the "finder" argument in AddItem calls
289 static CommandHandlerObject &ident(AudacityProject &project) { return project; }
290 
291 #define FN(X) ident, static_cast<CommandFunctorPointer>(& AudacityProject :: X)
292 
294 {
296  wxArrayString names;
297  wxArrayInt indices;
298 
299  // The list of defaults to exclude depends on
300  // preference wxT("/GUI/Shortcuts/FullDefaults"), which may have changed.
301  c->SetMaxList();
302 
303  {
304  auto menubar = c->AddMenuBar(wxT("appmenu"));
305  wxASSERT(menubar);
306  c->SetOccultCommands( false );
307 
309  // File menu
311 
312  c->BeginMenu(_("&File"));
314 
315  /*i18n-hint: "New" is an action (verb) to create a NEW project*/
316  c->AddItem(wxT("New"), _("&New"), FN(OnNew), wxT("Ctrl+N"),
319 
320  /*i18n-hint: (verb)*/
321  c->AddItem(wxT("Open"), _("&Open..."), FN(OnOpen), wxT("Ctrl+O"),
324 
326 
328 
330 
331  c->AddSeparator();
332 
333  c->AddItem(wxT("Close"), _("&Close"), FN(OnClose), wxT("Ctrl+W"));
334 
335  c->AddItem(wxT("Save"), _("&Save Project"), FN(OnSave), wxT("Ctrl+S"),
338  c->AddItem(wxT("SaveAs"), _("Save Project &As..."), FN(OnSaveAs));
339 
340  c->BeginSubMenu( _("&Export") );
341 
342  // Enable Export audio commands only when there are audio tracks.
343  c->AddItem(wxT("ExportMp3"), _("Export as MP&3"), FN(OnExportMp3), wxT(""),
346 
347  c->AddItem(wxT("ExportWav"), _("Export as &WAV"), FN(OnExportWav), wxT(""),
350 
351  c->AddItem(wxT("ExportOgg"), _("Export as &OGG"), FN(OnExportOgg), wxT(""),
354 
355  c->AddItem(wxT("Export"), _("&Export Audio..."), FN(OnExportAudio), wxT("Ctrl+Shift+E"),
358 
359  // Enable Export Selection commands only when there's a selection.
360  c->AddItem(wxT("ExportSel"), _("Expo&rt Selected Audio..."), FN(OnExportSelection),
363 
364  c->AddItem(wxT("ExportLabels"), _("Export &Labels..."), FN(OnExportLabels),
367  // Enable Export audio commands only when there are audio tracks.
368  c->AddItem(wxT("ExportMultiple"), _("Export &Multiple..."), FN(OnExportMultiple), wxT("Ctrl+Shift+L"),
371 #if defined(USE_MIDI)
372  c->AddItem(wxT("ExportMIDI"), _("Export MI&DI..."), FN(OnExportMIDI),
375 #endif
376 #ifdef USE_LIBVORBIS
377  c->AddSeparator();
378  c->AddItem(wxT("SaveCompressed"), _("&Save Compressed Copy of Project..."), FN(OnSaveCompressed));
379 #endif
380  c->EndSubMenu();
381  c->AddSeparator();
382  c->BeginSubMenu(_("&Import"));
383 
384  c->AddItem(wxT("ImportAudio"), _("&Audio..."), FN(OnImport), wxT("Ctrl+Shift+I"));
385  c->AddItem(wxT("ImportLabels"), _("&Labels..."), FN(OnImportLabels));
386 #ifdef USE_MIDI
387  c->AddItem(wxT("ImportMIDI"), _("&MIDI..."), FN(OnImportMIDI));
388 #endif // USE_MIDI
389  c->AddItem(wxT("ImportRaw"), _("&Raw Data..."), FN(OnImportRaw));
390 
391  c->EndSubMenu();
392  c->AddSeparator();
393 
395 
396  c->BeginSubMenu(_("C&hains"));
397  c->AddItem(wxT("ApplyChain"), _("Appl&y Chain..."), FN(OnApplyChain),
400  c->AddItem(wxT("EditChains"), _("Edit C&hains..."), FN(OnEditChains));
401  c->EndSubMenu();
402 
403  c->AddSeparator();
404 
405  c->AddItem(wxT("PageSetup"), _("Pa&ge Setup..."), FN(OnPageSetup),
408  /* i18n-hint: (verb) It's item on a menu. */
409  c->AddItem(wxT("Print"), _("&Print..."), FN(OnPrint),
412 
413  c->AddSeparator();
414 
415  // On the Mac, the Exit item doesn't actually go here...wxMac will pull it out
416  // and put it in the Audacity menu for us based on its ID.
417  /* i18n-hint: (verb) It's item on a menu. */
418  c->AddItem(wxT("Exit"), _("E&xit"), FN(OnExit), wxT("Ctrl+Q"),
421 
422  c->EndMenu();
423 
425  // Edit Menu
427 
428  c->BeginMenu(_("&Edit"));
429 
432 
433  c->AddItem(wxT("Undo"), _("&Undo"), FN(OnUndo), wxT("Ctrl+Z"),
436 
437  // The default shortcut key for Redo is different on different platforms.
438  wxString key =
439 #ifdef __WXMSW__
440  wxT("Ctrl+Y");
441 #else
442  wxT("Ctrl+Shift+Z");
443 #endif
444 
445  c->AddItem(wxT("Redo"), _("&Redo"), FN(OnRedo), key,
448 
450 
451  c->AddSeparator();
452 
453  // Basic Edit coomands
454  /* i18n-hint: (verb)*/
455  c->AddItem(wxT("Cut"), _("Cu&t"), FN(OnCut), wxT("Ctrl+X"),
458  c->AddItem(wxT("Delete"), _("&Delete"), FN(OnDelete), wxT("Ctrl+K"),
461  /* i18n-hint: (verb)*/
462  c->AddItem(wxT("Copy"), _("&Copy"), FN(OnCopy), wxT("Ctrl+C"),
465  /* i18n-hint: (verb)*/
466  c->AddItem(wxT("Paste"), _("&Paste"), FN(OnPaste), wxT("Ctrl+V"),
469  /* i18n-hint: (verb)*/
470  c->AddItem(wxT("Duplicate"), _("Duplic&ate"), FN(OnDuplicate), wxT("Ctrl+D"));
471 
472  c->AddSeparator();
473 
474  c->BeginSubMenu(_("R&emove Special"));
475  /* i18n-hint: (verb) Do a special kind of cut*/
476  c->AddItem(wxT("SplitCut"), _("Spl&it Cut"), FN(OnSplitCut), wxT("Ctrl+Alt+X"));
477  /* i18n-hint: (verb) Do a special kind of DELETE*/
478  c->AddItem(wxT("SplitDelete"), _("Split D&elete"), FN(OnSplitDelete), wxT("Ctrl+Alt+K"));
479 
480  c->AddSeparator();
481 
482  /* i18n-hint: (verb)*/
483  c->AddItem(wxT("Silence"), _("Silence Audi&o"), FN(OnSilence), wxT("Ctrl+L"),
486  /* i18n-hint: (verb)*/
487  c->AddItem(wxT("Trim"), _("Tri&m Audio"), FN(OnTrim), wxT("Ctrl+T"),
490  c->EndSubMenu();
491 
492  c->AddSeparator();
493 
495 
496  c->BeginSubMenu(_("Clip B&oundaries"));
497  /* i18n-hint: (verb) It's an item on a menu. */
498  c->AddItem(wxT("Split"), _("Sp&lit"), FN(OnSplit), wxT("Ctrl+I"),
501  c->AddItem(wxT("SplitNew"), _("Split Ne&w"), FN(OnSplitNew), wxT("Ctrl+Alt+I"),
504  c->AddSeparator();
505  /* i18n-hint: (verb)*/
506  c->AddItem(wxT("Join"), _("&Join"), FN(OnJoin), wxT("Ctrl+J"));
507  c->AddItem(wxT("Disjoin"), _("Detac&h at Silences"), FN(OnDisjoin), wxT("Ctrl+Alt+J"));
508  c->EndSubMenu();
509 
511 
512  c->BeginSubMenu(_("&Labels"));
513 
514  c->AddItem(wxT("EditLabels"), _("&Edit Labels..."), FN(OnEditLabels),
516 
517  c->AddSeparator();
518 
519  c->AddItem(wxT("AddLabel"), _("Add Label At &Selection"), FN(OnAddLabel), wxT("Ctrl+B"),
521  c->AddItem(wxT("AddLabelPlaying"), _("Add Label At &Playback Position"),
523 #ifdef __WXMAC__
524  wxT("Ctrl+."),
525 #else
526  wxT("Ctrl+M"),
527 #endif
529  AudioIOBusyFlag);
531  c->AddItem(wxT("PasteNewLabel"), _("Paste Te&xt to New Label"), FN(OnPasteNewLabel), wxT("Ctrl+Alt+V"),
533 
534  c->AddSeparator();
535 
536  c->AddCheck(wxT("TypeToCreateLabel"), _("&Type to Create a Label (on/off)"),
538 
539  c->EndSubMenu();
540 
542 
543  c->BeginSubMenu(_("La&beled Audio"));
544 
547 
548  /* i18n-hint: (verb)*/
549  c->AddItem(wxT("CutLabels"), _("&Cut"), FN(OnCutLabels), wxT("Alt+X"),
552  c->AddItem(wxT("DeleteLabels"), _("&Delete"), FN(OnDeleteLabels), wxT("Alt+K"),
555 
556  c->AddSeparator();
557 
558  /* i18n-hint: (verb) A special way to cut out a piece of audio*/
559  c->AddItem(wxT("SplitCutLabels"), _("&Split Cut"), FN(OnSplitCutLabels), wxT("Alt+Shift+X"));
560  c->AddItem(wxT("SplitDeleteLabels"), _("Sp&lit Delete"), FN(OnSplitDeleteLabels), wxT("Alt+Shift+K"));
561 
562  c->AddSeparator();
563 
564 
565  c->AddItem(wxT("SilenceLabels"), _("Silence &Audio"), FN(OnSilenceLabels), wxT("Alt+L"));
566  /* i18n-hint: (verb)*/
567  c->AddItem(wxT("CopyLabels"), _("Co&py"), FN(OnCopyLabels), wxT("Alt+Shift+C"));
568 
569  c->AddSeparator();
570 
571  /* i18n-hint: (verb)*/
572  c->AddItem(wxT("SplitLabels"), _("Spli&t"), FN(OnSplitLabels), wxT("Alt+I"),
575  /* i18n-hint: (verb)*/
576  c->AddItem(wxT("JoinLabels"), _("&Join"), FN(OnJoinLabels), wxT("Alt+J"));
577  c->AddItem(wxT("DisjoinLabels"), _("Detac&h at Silences"), FN(OnDisjoinLabels), wxT("Alt+Shift+J"));
578 
579  c->EndSubMenu();
580 
581  c->AddItem(wxT("EditMetaData"), _("Me&tadata..."), FN(OnEditMetadata),
583 
585 
586 #ifndef __WXMAC__
587  c->AddSeparator();
588 #endif
589 
590  // The default shortcut key for Preferences is different on different platforms.
591  key =
592 #ifdef __WXMAC__
593  wxT("Ctrl+,");
594 #else
595  wxT("Ctrl+P");
596 #endif
597 
598  c->AddItem(wxT("Preferences"), _("Pre&ferences..."), FN(OnPreferences), key,
601 
602  c->EndMenu();
603 
605  // Select Menu
607 
608  /* i18n-hint: (verb) It's an item on a menu. */
609  c->BeginMenu(_("&Select"));
611 
612  c->AddItem(wxT("SelectAll"), _("&All"), FN(OnSelectAll), wxT("Ctrl+A"));
613  c->AddItem(wxT("SelectNone"), _("&None"), FN(OnSelectNone), wxT("Ctrl+Shift+A"));
614 
616 
618 
619  c->BeginSubMenu(_("&Tracks"));
620  c->AddItem(wxT("SelAllTracks"), _("In All &Tracks"), FN(OnSelectAllTracks),
621  wxT("Ctrl+Shift+K"),
623 
624 #ifdef EXPERIMENTAL_SYNC_LOCK
625  c->AddItem(wxT("SelSyncLockTracks"), _("In All &Sync-Locked Tracks"),
626  FN(OnSelectSyncLockSel), wxT("Ctrl+Shift+Y"),
629 #endif
630 
631  c->EndSubMenu();
632 
634 
636 
637  c->BeginSubMenu(_("R&egion"));
638 
639  c->AddItem(wxT("SetLeftSelection"), _("&Left at Playback Position"), FN(OnSetLeftSelection), wxT("["));
640  c->AddItem(wxT("SetRightSelection"), _("&Right at Playback Position"), FN(OnSetRightSelection), wxT("]"));
642  c->AddItem(wxT("SelStartCursor"), _("Track &Start to Cursor"), FN(OnSelectStartCursor), wxT("Shift+J"),AlwaysEnabledFlag,AlwaysEnabledFlag);
643  c->AddItem(wxT("SelCursorEnd"), _("Cursor to Track &End"), FN(OnSelectCursorEnd), wxT("Shift+K"),AlwaysEnabledFlag,AlwaysEnabledFlag);
644  c->AddSeparator();
645  // GA: Audacity had 'Store Re&gion' here previously. There is no one-step
646  // way to restore the 'Saved Cursor Position' in Select Menu, so arguably
647  // using the word 'Selection' to do duty for both saving the region or the
648  // cursor is better. But it does not belong in a 'Region' submenu.
649  c->AddItem(wxT("SelSave"), _("S&tore Selection"), FN(OnSelectionSave),
652  // Audacity had 'Retrieve Regio&n' here previously.
653  c->AddItem(wxT("SelRestore"), _("Retrieve Selectio&n"), FN(OnSelectionRestore),
656 
657  c->EndSubMenu();
658 
660 
662 
663 #ifdef EXPERIMENTAL_SPECTRAL_EDITING
664  c->BeginSubMenu(_("S&pectral"));
665  c->AddItem(wxT("ToggleSpectralSelection"), _("To&ggle spectral selection"), FN(OnToggleSpectralSelection), wxT("Q"));
666  c->AddItem(wxT("NextHigherPeakFrequency"), _("Next &Higher Peak Frequency"), FN(OnNextHigherPeakFrequency));
667  c->AddItem(wxT("NextLowerPeakFrequency"), _("Next &Lower Peak Frequency"), FN(OnNextLowerPeakFrequency));
668  c->EndSubMenu();
669 #endif
670 
672 
674 
675  c->BeginSubMenu(_("Clip B&oundaries"));
676  c->AddItem(wxT("SelPrevClipBoundaryToCursor"), _("Pre&vious Clip Boundary to Cursor"),
679  c->AddItem(wxT("SelCursorToNextClipBoundary"), _("Cursor to Ne&xt Clip Boundary"),
682  c->AddItem(wxT("SelPrevClip"), _("Previo&us Clip"), FN(OnSelectPrevClip), wxT("Alt+P"),
684  c->AddItem(wxT("SelNextClip"), _("N&ext Clip"), FN(OnSelectNextClip), wxT("Alt+N"),
686 
687  c->EndSubMenu();
689 
690  c->AddSeparator();
691 
692  c->AddItem(wxT("SelCursorStoredCursor"), _("Cursor to Stored &Cursor Position"), FN(OnSelectCursorStoredCursor),
693  wxT(""), TracksExistFlag, TracksExistFlag);
694 
695  c->AddItem(wxT("StoreCursorPosition"), _("Store Cursor Pos&ition"), FN(OnCursorPositionStore),
698  // Save cursor position is used in some selections.
699  // Maybe there should be a restore for it?
700 
701  c->AddSeparator();
702 
703  c->AddItem(wxT("ZeroCross"), _("At &Zero Crossings"), FN(OnZeroCrossing), wxT("Z"));
704 
705  c->EndMenu();
706 
708  // View Menu
710 
711  c->BeginMenu(_("&View"));
713  c->BeginSubMenu(_("&Zoom"));
714 
715  c->AddItem(wxT("ZoomIn"), _("Zoom &In"), FN(OnZoomIn), wxT("Ctrl+1"),
718  c->AddItem(wxT("ZoomNormal"), _("Zoom &Normal"), FN(OnZoomNormal), wxT("Ctrl+2"));
719  c->AddItem(wxT("ZoomOut"), _("Zoom &Out"), FN(OnZoomOut), wxT("Ctrl+3"),
722  c->AddItem(wxT("ZoomSel"), _("&Zoom to Selection"), FN(OnZoomSel), wxT("Ctrl+E"),
725  c->AddItem(wxT("ZoomToggle"), _("Zoom &Toggle"), FN(OnZoomToggle), wxT("Shift+Z"),
728  c->EndSubMenu();
729 
730  c->BeginSubMenu(_("T&rack Size"));
731  c->AddItem(wxT("FitInWindow"), _("&Fit to Width"), FN(OnZoomFit), wxT("Ctrl+F"));
732  c->AddItem(wxT("FitV"), _("Fit to &Height"), FN(OnZoomFitV), wxT("Ctrl+Shift+F"));
733  c->AddItem(wxT("CollapseAllTracks"), _("&Collapse All Tracks"), FN(OnCollapseAllTracks), wxT("Ctrl+Shift+C"));
734  c->AddItem(wxT("ExpandAllTracks"), _("E&xpand Collapsed Tracks"), FN(OnExpandAllTracks), wxT("Ctrl+Shift+X"));
735  c->EndSubMenu();
736 
737  c->BeginSubMenu(_("Sk&ip to"));
738  c->AddItem(wxT("SkipSelStart"), _("Selection Sta&rt"), FN(OnGoSelStart), wxT("Ctrl+["),
740  c->AddItem(wxT("SkipSelEnd"), _("Selection En&d"), FN(OnGoSelEnd), wxT("Ctrl+]"),
742  c->EndSubMenu();
743 
744  c->AddSeparator();
745 
746  // History window should be available either for UndoAvailableFlag or RedoAvailableFlag,
747  // but we can't make the AddItem flags and mask have both, because they'd both have to be true for the
748  // command to be enabled.
749  // If user has Undone the entire stack, RedoAvailableFlag is on but UndoAvailableFlag is off.
750  // If user has done things but not Undone anything, RedoAvailableFlag is off but UndoAvailableFlag is on.
751  // So in either of those cases, (AudioIONotBusyFlag | UndoAvailableFlag | RedoAvailableFlag) mask
752  // would fail.
753  // The only way to fix this in the current architecture is to hack in special cases for RedoAvailableFlag
754  // in AudacityProject::UpdateMenus() (ugly) and CommandManager::HandleCommandEntry() (*really* ugly --
755  // shouldn't know about particular command names and flags).
756  // Here's the hack that would be necessary in AudacityProject::UpdateMenus(), if somebody decides to do it:
757  // // Because EnableUsingFlags requires all the flag bits match the corresponding mask bits,
758  // // "UndoHistory" specifies only AudioIONotBusyFlag | UndoAvailableFlag, because that
759  // // covers the majority of cases where it should be enabled.
760  // // If history is not empty but we've Undone the whole stack, we also want to enable,
761  // // to show the Redo's on stack.
762  // // "UndoHistory" might already be enabled, but add this check for RedoAvailableFlag.
763  // if (flags & RedoAvailableFlag)
764  // mCommandManager.Enable(wxT("UndoHistory"), true);
765  // So for now, enable the command regardless of stack. It will just show empty sometimes.
766  // FOR REDESIGN, clearly there are some limitations with the flags/mask bitmaps.
767 
768  /* i18n-hint: Clicking this menu item shows the various editing steps that have been taken.*/
769  c->AddItem(wxT("UndoHistory"), _("&History..."), FN(OnHistory),
772 
773  c->AddItem(wxT("Karaoke"), _("&Karaoke..."), FN(OnKaraoke), LabelTracksExistFlag, LabelTracksExistFlag);
774  c->AddItem(wxT("MixerBoard"), _("&Mixer Board..."), FN(OnMixerBoard), PlayableTracksExistFlag, PlayableTracksExistFlag);
775 
776  c->AddSeparator();
777 
779 
780  c->BeginSubMenu(_("&Toolbars"));
781 
782  /* i18n-hint: (verb)*/
783  c->AddItem(wxT("ResetToolbars"), _("Reset Toolb&ars"), FN(OnResetToolBars), 0, AlwaysEnabledFlag, AlwaysEnabledFlag);
784  c->AddSeparator();
785 
786  /* i18n-hint: Clicking this menu item shows the toolbar with the big buttons on it (play record etc)*/
787  c->AddCheck(wxT("ShowTransportTB"), _("&Transport Toolbar"), FN(OnShowTransportToolBar), 0, AlwaysEnabledFlag, AlwaysEnabledFlag);
788  /* i18n-hint: Clicking this menu item shows a toolbar that has some tools in it*/
789  c->AddCheck(wxT("ShowToolsTB"), _("T&ools Toolbar"), FN(OnShowToolsToolBar), 0, AlwaysEnabledFlag, AlwaysEnabledFlag);
790  /* i18n-hint: Clicking this menu item shows the toolbar with the recording level meters*/
791  c->AddCheck(wxT("ShowRecordMeterTB"), _("&Recording Meter Toolbar"), FN(OnShowRecordMeterToolBar), 0, AlwaysEnabledFlag, AlwaysEnabledFlag);
792  /* i18n-hint: Clicking this menu item shows the toolbar with the playback level meter*/
793  c->AddCheck(wxT("ShowPlayMeterTB"), _("&Playback Meter Toolbar"), FN(OnShowPlayMeterToolBar), 0, AlwaysEnabledFlag, AlwaysEnabledFlag);
794  /* --i18n-hint: Clicking this menu item shows the toolbar which has sound level meters*/
795  //c->AddCheck(wxT("ShowMeterTB"), _("Co&mbined Meter Toolbar"), FN(OnShowMeterToolBar), 0, AlwaysEnabledFlag, AlwaysEnabledFlag);
796  /* i18n-hint: Clicking this menu item shows the toolbar with the mixer*/
797  c->AddCheck(wxT("ShowMixerTB"), _("Mi&xer Toolbar"), FN(OnShowMixerToolBar), 0, AlwaysEnabledFlag, AlwaysEnabledFlag);
798  /* i18n-hint: Clicking this menu item shows the toolbar for editing*/
799  c->AddCheck(wxT("ShowEditTB"), _("&Edit Toolbar"), FN(OnShowEditToolBar), 0, AlwaysEnabledFlag, AlwaysEnabledFlag);
800  /* i18n-hint: Clicking this menu item shows the toolbar for transcription (currently just vary play speed)*/
801  c->AddCheck(wxT("ShowTranscriptionTB"), _("Tra&nscription Toolbar"), FN(OnShowTranscriptionToolBar), 0, AlwaysEnabledFlag, AlwaysEnabledFlag);
802  /* i18n-hint: Clicking this menu item shows the toolbar that enables Scrub or Seek playback and Scrub Ruler*/
803  c->AddCheck(wxT("ShowScrubbingTB"), _("Scru&b Toolbar"), FN(OnShowScrubbingToolBar), 0, AlwaysEnabledFlag, AlwaysEnabledFlag);
804  /* i18n-hint: Clicking this menu item shows the toolbar that manages devices*/
805  c->AddCheck(wxT("ShowDeviceTB"), _("&Device Toolbar"), FN(OnShowDeviceToolBar), 0, AlwaysEnabledFlag, AlwaysEnabledFlag);
806  /* i18n-hint: Clicking this menu item shows the toolbar for selecting a time range of audio*/
807  c->AddCheck(wxT("ShowSelectionTB"), _("&Selection Toolbar"), FN(OnShowSelectionToolBar), 0, AlwaysEnabledFlag, AlwaysEnabledFlag);
808 #ifdef EXPERIMENTAL_SPECTRAL_EDITING
809  /* i18n-hint: Clicking this menu item shows the toolbar for selecting a frequency range of audio*/
810  c->AddCheck(wxT("ShowSpectralSelectionTB"), _("Spe&ctral Selection Toolbar"), FN(OnShowSpectralSelectionToolBar), 0, AlwaysEnabledFlag, AlwaysEnabledFlag);
811 #endif
812 
813  c->EndSubMenu();
814 
815  c->AddSeparator();
816 
817  c->AddCheck(wxT("ShowExtraMenus"), _("&Extra Menus (on/off)"), FN(OnShowExtraMenus),
818  gPrefs->Read(wxT("/GUI/ShowExtraMenus"), 0L), AlwaysEnabledFlag, AlwaysEnabledFlag);
819  c->AddCheck(wxT("ShowClipping"), _("&Show Clipping (on/off)"), FN(OnShowClipping),
820  gPrefs->Read(wxT("/GUI/ShowClipping"), 0L), AlwaysEnabledFlag, AlwaysEnabledFlag);
821 
822 
823  c->EndMenu();
824 
826  // Transport Menu
828 
829  /*i18n-hint: 'Transport' is the name given to the set of controls that
830  play, record, pause etc. */
831  c->BeginMenu(_("T&ransport"));
833  c->BeginSubMenu(_("Pl&ay"));
834  /* i18n-hint: (verb) Start or Stop audio playback*/
835  c->AddItem(wxT("PlayStop"), _("Pl&ay/Stop"), FN(OnPlayStop), wxT("Space"));
836  c->AddItem(wxT("PlayStopSelect"), _("Play/Stop and &Set Cursor"), FN(OnPlayStopSelect), wxT("X"));
837  c->AddItem(wxT("PlayLooped"), _("&Loop Play"), FN(OnPlayLooped), wxT("Shift+Space"),
840  c->AddItem(wxT("Pause"), _("&Pause"), FN(OnPause), wxT("P"));
841  c->EndSubMenu();
842 
843  c->BeginSubMenu( _("&Record"));
846  /* i18n-hint: (verb)*/
847  c->AddItem(wxT("Record1stChoice"), _("&Record"), FN(OnRecord), wxT("R"));
848  // The OnRecord2ndChoice function is: if normal record records beside,
849  // it records below, if normal record records below, it records beside.
850  // TODO: Do 'the right thing' with other options like TimerRecord.
851  bool bPreferNewTrack;
852  gPrefs->Read("/GUI/PreferNewTrackRecord",&bPreferNewTrack, false);
853  c->AddItem( wxT("Record2ndChoice"),
854  // Our first choice is bound to R (by default) and gets the prime position.
855  // We supply the name for the 'other one' here. It should be bound to Shift+R
856  bPreferNewTrack ? _("&Append Record") : _("Record &New Track"),
858  wxT("Shift+R")
859  );
860 
861  c->AddItem(wxT("TimerRecord"), _("&Timer Record..."), FN(OnTimerRecord), wxT("Shift+T"));
862  // JKC: I decided to duplicate this between play and record, rather than put it
863  // at the top level. AddItem can now cope with simple duplicated items.
864  // PRL: This second registration of wxT("Pause"), with unspecified flags,
865  // in fact will use the same flags as in the previous registration.
866  c->AddItem(wxT("Pause"), _("&Pause"), FN(OnPause), wxT("P"));
867  c->EndSubMenu();
868 
869  // Scrubbing sub-menu
871 
872  // JKC: ANSWER-ME: How is 'cursor to' different to 'Skip To' and how is it useful?
873  // GA: 'Skip to' moves the viewpoint to center of the track and preserves the
874  // selection. 'Cursor to' does neither. 'Center at' might describe it better than 'Skip'.
875  c->BeginSubMenu(_("&Cursor to"));
876 
877  c->AddItem(wxT("CursSelStart"), _("Selection Star&t"), FN(OnCursorSelStart),
879  c->AddItem(wxT("CursSelEnd"), _("Selection En&d"), FN(OnCursorSelEnd),
881 
882  // PRL: I thought these two should require TracksSelectedFlag but there
883  // was complaint about an unhelpful error message
884  c->AddItem(wxT("CursTrackStart"), _("Track &Start"), FN(OnCursorTrackStart), wxT("J"),
886  c->AddItem(wxT("CursTrackEnd"), _("Track &End"), FN(OnCursorTrackEnd), wxT("K"),
888 
889  c->AddItem(wxT("CursPrevClipBoundary"), _("Pre&vious Clip Boundary"), FN(OnCursorPrevClipBoundary), wxT(""),
891  c->AddItem(wxT("CursNextClipBoundary"), _("Ne&xt Clip Boundary"), FN(OnCursorNextClipBoundary), wxT(""),
893 
894  c->AddItem(wxT("CursProjectStart"), _("&Project Start"), FN(OnSkipStart), wxT("Home"));
895  c->AddItem(wxT("CursProjectEnd"), _("Project E&nd"), FN(OnSkipEnd), wxT("End"));
896 
897  c->EndSubMenu();
898 
899  c->AddSeparator();
900 
902 
903  c->BeginSubMenu(_("Pla&y Region"));
904 
905  c->AddItem(wxT("LockPlayRegion"), _("&Lock"), FN(OnLockPlayRegion),
908  c->AddItem(wxT("UnlockPlayRegion"), _("&Unlock"), FN(OnUnlockPlayRegion),
911 
912  c->EndSubMenu();
913 
914  c->AddSeparator();
915 
916  c->AddItem(wxT("RescanDevices"), _("R&escan Audio Devices"), FN(OnRescanDevices),
919 
920  c->BeginSubMenu(_("Transport &Options"));
921  // Sound Activated recording options
922  c->AddItem(wxT("SoundActivationLevel"), _("Sound Activation Le&vel..."), FN(OnSoundActivated),
925  c->AddCheck(wxT("SoundActivation"), _("Sound A&ctivated Recording (on/off)"), FN(OnToggleSoundActivated), 0,
928  c->AddSeparator();
929 
930  c->AddCheck(wxT("PinnedHead"), _("Pinned Play/Record &Head (on/off)"),
932  // Switching of scrolling on and off is permitted even during transport
934 
935  c->AddCheck(wxT("Duplex"), _("&Overdub (on/off)"), FN(OnTogglePlayRecording), 0,
938  c->AddCheck(wxT("SWPlaythrough"), _("So&ftware Playthrough (on/off)"), FN(OnToggleSWPlaythrough), 0,
941 
942 
943 #ifdef EXPERIMENTAL_AUTOMATED_INPUT_LEVEL_ADJUSTMENT
944  c->AddCheck(wxT("AutomatedInputLevelAdjustmentOnOff"), _("A&utomated Recording Level Adjustment (on/off)"), FN(OnToggleAutomatedInputLevelAdjustment), 0,
947 #endif
948  c->EndSubMenu();
949 
950  c->EndMenu();
951 
953  // Tracks Menu (formerly Project Menu)
955 
956  c->BeginMenu(_("&Tracks"));
958 
960 
961  c->BeginSubMenu(_("Add &New"));
962 
963  c->AddItem(wxT("NewMonoTrack"), _("&Mono Track"), FN(OnNewWaveTrack), wxT("Ctrl+Shift+N"));
964  c->AddItem(wxT("NewStereoTrack"), _("&Stereo Track"), FN(OnNewStereoTrack));
965  c->AddItem(wxT("NewLabelTrack"), _("&Label Track"), FN(OnNewLabelTrack));
966  c->AddItem(wxT("NewTimeTrack"), _("&Time Track"), FN(OnNewTimeTrack));
967 
968  c->EndSubMenu();
969 
971 
972  c->AddSeparator();
973 
974  c->BeginSubMenu(_("Mi&x") );
975  {
976  // Stereo to Mono is an oddball command that is also subject to control by the
977  // plug-in manager, as if an effect. Decide whether to show or hide it.
978  const PluginID ID = EffectManager::Get().GetEffectByIdentifier(wxT("StereoToMono"));
979  const PluginDescriptor *plug = PluginManager::Get().GetPlugin(ID);
980  if (plug && plug->IsEnabled())
981  c->AddItem(wxT("Stereo to Mono"), _("Mix Stereo down to &Mono"), FN(OnStereoToMono),
984  }
985  c->AddItem(wxT("MixAndRender"), _("Mi&x and Render"), FN(OnMixAndRender),
988  c->AddItem(wxT("MixAndRenderToNewTrack"), _("Mix and Render to Ne&w Track"), FN(OnMixAndRenderToNewTrack), wxT("Ctrl+Shift+M"),
991  c->EndSubMenu();
992 
993  c->AddItem(wxT("Resample"), _("&Resample..."), FN(OnResample),
996 
997  c->AddSeparator();
998 
999  c->AddItem(wxT("RemoveTracks"), _("Remo&ve Tracks"), FN(OnRemoveTracks),
1002 
1003  c->AddSeparator();
1004 
1005  c->BeginSubMenu(_("M&ute/Unmute"));
1006  c->AddItem(wxT("MuteAllTracks"), _("&Mute All Tracks"), FN(OnMuteAllTracks), wxT("Ctrl+U"));
1007  c->AddItem(wxT("UnMuteAllTracks"), _("&Unmute All Tracks"), FN(OnUnMuteAllTracks), wxT("Ctrl+Shift+U"));
1008  c->EndSubMenu();
1009 
1010  c->BeginSubMenu(_("&Pan"));
1011  c->AddItem(wxT("PanLeft"), _("&Left"), FN(OnPanLeft));
1012  c->AddItem(wxT("PanRight"), _("&Right"), FN(OnPanRight));
1013  c->AddItem(wxT("PanCenter"), _("&Center"), FN(OnPanCenter));
1014  c->EndSubMenu();
1015 
1016 
1017  c->AddSeparator();
1018 
1020 
1021  wxArrayString alignLabelsNoSync;
1022  alignLabelsNoSync.Add(_("&Align End to End"));
1023  alignLabelsNoSync.Add(_("Align &Together"));
1024 
1025  wxArrayString alignLabels;
1026  alignLabels.Add(_("Start to &Zero"));
1027  alignLabels.Add(_("Start to &Cursor/Selection Start"));
1028  alignLabels.Add(_("Start to Selection &End"));
1029  alignLabels.Add(_("End to Cu&rsor/Selection Start"));
1030  alignLabels.Add(_("End to Selection En&d"));
1031  mAlignLabelsCount = alignLabels.GetCount();
1032 
1033  // Calling c->SetCommandFlags() after AddItemList for "Align" and "AlignMove"
1034  // does not correctly set flags for submenus, so do it this way.
1037 
1038  c->BeginSubMenu(_("&Align Tracks"));
1039 
1040  //c->BeginSubMenu(_("Just Move Tracks"));
1041  c->AddItemList(wxT("Align"), alignLabelsNoSync, FN(OnAlignNoSync));
1042  c->AddSeparator();
1043  c->AddItemList(wxT("Align"), alignLabels, FN(OnAlign));
1044  c->AddSeparator();
1045  c->AddCheck(wxT("MoveSelectionWithTracks"), _("&Move Selection with Tracks (on/off)"),
1047  gPrefs->Read(wxT("/GUI/MoveSelectionWithTracks"), 0L),
1049  c->EndSubMenu();
1050 
1051 #if 0
1052  // TODO: Can these labels be made clearer? Do we need this sub-menu at all?
1053  c->BeginSubMenu(_("Move Sele&ction and Tracks"));
1054 
1055  c->AddItemList(wxT("AlignMove"), alignLabels, FN(OnAlignMoveSel));
1056  c->SetCommandFlags(wxT("AlignMove"),
1059 
1060  c->EndSubMenu();
1061 #endif
1062 
1064 
1065 
1067 
1068 #ifdef EXPERIMENTAL_SCOREALIGN
1069  c->AddItem(wxT("ScoreAlign"), _("Synchronize MIDI with Audio"), FN(OnScoreAlign),
1072 #endif // EXPERIMENTAL_SCOREALIGN
1073 
1075 
1076  c->BeginSubMenu(_("S&ort Tracks"));
1077 
1078  c->AddItem(wxT("SortByTime"), _("by &Start time"), FN(OnSortTime),
1080  TracksExistFlag);
1081  c->AddItem(wxT("SortByName"), _("by &Name"), FN(OnSortName),
1083  TracksExistFlag);
1084 
1085  c->EndSubMenu();
1086 
1088 
1089 #ifdef EXPERIMENTAL_SYNC_LOCK
1090  c->AddSeparator();
1091  c->AddCheck(wxT("SyncLock"), _("Sync-&Lock Tracks (on/off)"), FN(OnSyncLock),
1092  gPrefs->Read(wxT("/GUI/SyncLockTracks"), 0L),
1094 
1095 #endif
1096 
1097  c->EndMenu();
1098 
1099  // All of this is a bit hacky until we can get more things connected into
1100  // the plugin manager...sorry! :-(
1101 
1102  wxArrayString defaults;
1103 
1105  // Generate Menu
1107 
1108  c->BeginMenu(_("&Generate"));
1110 
1111 #ifdef EXPERIMENTAL_EFFECT_MANAGEMENT
1112  c->AddItem(wxT("ManageGenerators"), _("Add / Remove Plug-ins..."), FN(OnManageGenerators));
1113  c->AddSeparator();
1114 #endif
1115 
1116 
1118  EffectTypeGenerate,
1121 
1122  c->EndMenu();
1123 
1125  // Effect Menu
1127 
1128  c->BeginMenu(_("Effe&ct"));
1129 
1130  wxString buildMenuLabel;
1131  if (!mLastEffect.IsEmpty()) {
1132  buildMenuLabel.Printf(_("Repeat %s"),
1133  EffectManager::Get().GetEffectName(mLastEffect));
1134  }
1135  else
1136  buildMenuLabel = _("Repeat Last Effect");
1137 
1138 #ifdef EXPERIMENTAL_EFFECT_MANAGEMENT
1139  c->AddItem(wxT("ManageEffects"), _("Add / Remove Plug-ins..."), FN(OnManageEffects));
1140  c->AddSeparator();
1141 #endif
1142 
1143  c->AddItem(wxT("RepeatLastEffect"), buildMenuLabel, FN(OnRepeatLastEffect), wxT("Ctrl+R"),
1146 
1147  c->AddSeparator();
1148 
1150  EffectTypeProcess,
1153 
1154  c->EndMenu();
1155 
1157  // Analyze Menu
1159 
1160  c->BeginMenu(_("&Analyze"));
1161 
1162 #ifdef EXPERIMENTAL_EFFECT_MANAGEMENT
1163  c->AddItem(wxT("ManageAnalyzers"), _("Add / Remove Plug-ins..."), FN(OnManageAnalyzers));
1164  c->AddSeparator();
1165 #endif
1166 
1167 
1168  c->AddItem(wxT("ContrastAnalyser"), _("Contrast..."), FN(OnContrast), wxT("Ctrl+Shift+T"),
1171  c->AddItem(wxT("PlotSpectrum"), _("Plot Spectrum..."), FN(OnPlotSpectrum),
1174 
1176  EffectTypeAnalyze,
1179 
1180  c->EndMenu();
1181 
1182 #ifdef __WXMAC__
1183  // poor imitation of the Mac Windows Menu
1186 
1187  {
1188  c->BeginMenu(_("&Window"));
1189  /* i18n-hint: Standard Macintosh Window menu item: Make (the current
1190  * window) shrink to an icon on the dock */
1191  c->AddItem(wxT("MacMinimize"), _("&Minimize"), FN(OnMacMinimize),
1192  wxT("Ctrl+M"), NotMinimizedFlag, NotMinimizedFlag);
1193  /* i18n-hint: Standard Macintosh Window menu item: Make (the current
1194  * window) full sized */
1195  c->AddItem(wxT("MacZoom"), _("&Zoom"), FN(OnMacZoom),
1197  c->AddSeparator();
1198  /* i18n-hint: Standard Macintosh Window menu item: Make all project
1199  * windows un-hidden */
1200  c->AddItem(wxT("MacBringAllToFront"),
1201  _("&Bring All to Front"), FN(OnMacBringAllToFront),
1203  c->EndMenu();
1204  }
1205 #endif
1206 
1208  // Help Menu
1210 
1211 #ifdef __WXMAC__
1212  wxGetApp().s_macHelpMenuTitleName = _("&Help");
1213 #endif
1214 
1215  c->BeginMenu(_("&Help"));
1217 
1218 // DA: Emphasise it is the Audacity Manual (No separate DA manual).
1219 #ifdef EXPERIMENTAL_DA
1220  // 'Getting Started' rather than 'Quick Help' for DarkAudacity.
1221  // At the moment the video tutorials are aspirational (aka do not exist yet).
1222  // Emphasise that manual is for Audacity, not DarkAudacity.
1223  c->AddItem(wxT("QuickHelp"), _("&Getting Started"), FN(OnQuickHelp));
1224  c->AddItem(wxT("Manual"), wxT("Audacity &Manual"), FN(OnManual));
1225 #else
1226  c->AddItem(wxT("QuickHelp"), _("&Quick Help"), FN(OnQuickHelp));
1227  c->AddItem(wxT("Manual"), _("&Manual"), FN(OnManual));
1228 #endif
1229 
1230 
1231  c->AddSeparator();
1232 
1233  c->BeginSubMenu( _("&Tools") );
1234 
1235 #ifdef IS_ALPHA
1236  c->AddCheck(wxT("SimulateRecordingErrors"),
1237  _("Simulate Recording Errors"),
1240  c->AddCheck(wxT("DetectUpstreamDropouts"),
1241  _("Detect Upstream Dropouts"),
1244 #endif
1245 
1246  c->AddItem(wxT("Screenshot"), _("&Screenshot Tools..."), FN(OnScreenshot));
1247 
1248 // PRL: team consensus for 2.2.0 was, we let end users have this diagnostic,
1249 // as they used to in 1.3.x
1250 //#ifdef IS_ALPHA
1251  // TODO: What should we do here? Make benchmark a plug-in?
1252  // Easy enough to do. We'd call it mod-self-test.
1253 
1254  c->AddItem(wxT("Benchmark"), _("&Run Benchmark..."), FN(OnBenchmark));
1255 //#endif
1256 
1257  c->EndSubMenu();
1258  c->AddSeparator();
1259 
1260  c->BeginSubMenu(_("&Diagnostics"));
1261  c->AddItem(wxT("DeviceInfo"), _("Au&dio Device Info..."), FN(OnAudioDeviceInfo),
1264 #ifdef EXPERIMENTAL_MIDI_OUT
1265  c->AddItem(wxT("MidiDeviceInfo"), _("&MIDI Device Info..."), FN(OnMidiDeviceInfo),
1268 #endif
1269 
1270  c->AddItem(wxT("Log"), _("Show &Log..."), FN(OnShowLog));
1271 
1272 #if defined(EXPERIMENTAL_CRASH_REPORT)
1273  c->AddItem(wxT("CrashReport"), _("&Generate Support Data..."), FN(OnCrashReport));
1274 #endif
1275  c->AddItem(wxT("CheckDeps"), _("Chec&k Dependencies..."), FN(OnCheckDependencies),
1277  c->EndSubMenu();
1278 
1279 #ifndef __WXMAC__
1280  c->AddSeparator();
1281 #endif
1282 
1283 // DA: Does not fully support update checking.
1284 #ifndef EXPERIMENTAL_DA
1285  c->AddItem(wxT("Updates"), _("&Check for Updates..."), FN(OnCheckForUpdates));
1286 #endif
1287  c->AddItem(wxT("About"), _("&About Audacity..."), FN(OnAbout));
1288 
1289  c->EndMenu();
1290 
1292 
1293 
1295 
1296  bool bShowExtraMenus;
1297  gPrefs->Read(wxT("/GUI/ShowExtraMenus"), &bShowExtraMenus, false);
1298  std::unique_ptr<wxMenuBar> menubar2;
1299  if( !bShowExtraMenus )
1300  {
1301  menubar2 = c->AddMenuBar(wxT("ext-menu"));
1302  c->SetOccultCommands(true);
1303  }
1304 
1306  // Ext-Menu
1308 
1309  // i18n-hint: Extra is a menu with extra commands
1310  c->BeginMenu(_("E&xtra"));
1311 
1313 
1315  c->BeginSubMenu(_("T&ransport"));
1316 
1317  // PlayStop is already in the menus.
1318  /* i18n-hint: (verb) Start playing audio*/
1319  c->AddItem(wxT("Play"), _("Pl&ay"), FN(OnPlayStop),
1322  /* i18n-hint: (verb) Stop playing audio*/
1323  c->AddItem(wxT("Stop"), _("Sto&p"), FN(OnStop),
1324  AudioIOBusyFlag | CanStopAudioStreamFlag,
1325  AudioIOBusyFlag | CanStopAudioStreamFlag);
1326 
1328 
1329  c->AddItem(wxT("PlayOneSec"), _("Play &One Second"), FN(OnPlayOneSecond), wxT("1"),
1332  c->AddItem(wxT("PlayToSelection"), _("Play To &Selection"), FN(OnPlayToSelection), wxT("B"),
1335  c->AddItem(wxT("PlayBeforeSelectionStart"), _("Play &Before Selection Start"), FN(OnPlayBeforeSelectionStart), wxT("Shift+F5"));
1336  c->AddItem(wxT("PlayAfterSelectionStart"), _("Play Af&ter Selection Start"), FN(OnPlayAfterSelectionStart), wxT("Shift+F6"));
1337  c->AddItem(wxT("PlayBeforeSelectionEnd"), _("Play Be&fore Selection End"), FN(OnPlayBeforeSelectionEnd), wxT("Shift+F7"));
1338  c->AddItem(wxT("PlayAfterSelectionEnd"), _("Play Aft&er Selection End"), FN(OnPlayAfterSelectionEnd), wxT("Shift+F8"));
1339  c->AddItem(wxT("PlayBeforeAndAfterSelectionStart"), _("Play Before a&nd After Selection Start"), FN(OnPlayBeforeAndAfterSelectionStart), wxT("Ctrl+Shift+F5"));
1340  c->AddItem(wxT("PlayBeforeAndAfterSelectionEnd"), _("Play Before an&d After Selection End"), FN(OnPlayBeforeAndAfterSelectionEnd), wxT("Ctrl+Shift+F7"));
1341  c->AddItem(wxT("PlayCutPreview"), _("Play C&ut Preview"), FN(OnPlayCutPreview), wxT("C"),
1344  c->EndSubMenu();
1345 
1347 
1349  c->BeginSubMenu(_("T&ools"));
1350 
1351  c->AddItem(wxT("SelectTool"), _("&Selection Tool"), FN(OnSelectTool), wxT("F1"));
1352  c->AddItem(wxT("EnvelopeTool"), _("&Envelope Tool"), FN(OnEnvelopeTool), wxT("F2"));
1353  c->AddItem(wxT("DrawTool"), _("&Draw Tool"), FN(OnDrawTool), wxT("F3"));
1354  c->AddItem(wxT("ZoomTool"), _("&Zoom Tool"), FN(OnZoomTool), wxT("F4"));
1355  c->AddItem(wxT("TimeShiftTool"), _("&Time Shift Tool"), FN(OnTimeShiftTool), wxT("F5"));
1356  c->AddItem(wxT("MultiTool"), _("&Multi Tool"), FN(OnMultiTool), wxT("F6"));
1357 
1358  c->AddItem(wxT("PrevTool"), _("&Previous Tool"), FN(OnPrevTool), wxT("A"));
1359  c->AddItem(wxT("NextTool"), _("&Next Tool"), FN(OnNextTool), wxT("D"));
1360  c->EndSubMenu();
1361 
1363 
1365  c->BeginSubMenu(_("Mi&xer"));
1366 
1367  c->AddItem(wxT("OutputGain"), _("Ad&just playback volume"), FN(OnOutputGain));
1368  c->AddItem(wxT("OutputGainInc"), _("&Increase playback volume"), FN(OnOutputGainInc));
1369  c->AddItem(wxT("OutputGainDec"), _("&Decrease playback volume"), FN(OnOutputGainDec));
1370  c->AddItem(wxT("InputGain"), _("Adj&ust recording volume"), FN(OnInputGain));
1371  c->AddItem(wxT("InputGainInc"), _("I&ncrease recording volume"), FN(OnInputGainInc));
1372  c->AddItem(wxT("InputGainDec"), _("D&ecrease recording volume"), FN(OnInputGainDec));
1373  c->EndSubMenu();
1374 
1376 
1378  c->BeginSubMenu(_("&Edit"));
1379 
1380  c->AddItem(wxT("DeleteKey"), _("&DeleteKey"), FN(OnDelete), wxT("Backspace"),
1383 
1384  c->AddItem(wxT("DeleteKey2"), _("DeleteKey&2"), FN(OnDelete), wxT("Delete"),
1387  c->EndSubMenu();
1388 
1390 
1392  c->BeginSubMenu(_("Transcri&ption"));
1393 
1394  c->AddItem(wxT("PlayAtSpeed"), _("Pl&ay-at-Speed"), FN(OnPlayAtSpeed));
1395  c->AddItem(wxT("PlayAtSpeedLooped"), _("&Loop Play-at-Speed"), FN(OnPlayAtSpeedLooped));
1396  c->AddItem(wxT("PlayAtSpeedCutPreview"), _("Play C&ut Preview-at-Speed"), FN(OnPlayAtSpeedCutPreview));
1397  c->AddItem(wxT("SetPlaySpeed"), _("Ad&just playback speed"), FN(OnSetPlaySpeed));
1398  c->AddItem(wxT("PlaySpeedInc"), _("&Increase playback speed"), FN(OnPlaySpeedInc));
1399  c->AddItem(wxT("PlaySpeedDec"), _("&Decrease playback speed"), FN(OnPlaySpeedDec));
1400 
1401  // These were on the original transcription toolbar. But they are not on the
1402  // shortened one.
1403  c->AddItem(wxT("MoveToPrevLabel"), _("Move to &Previous Label"), FN(OnMoveToPrevLabel), wxT("Alt+Left"),
1405  c->AddItem(wxT("MoveToNextLabel"), _("Move to &Next Label"), FN(OnMoveToNextLabel), wxT("Alt+Right"),
1407  c->EndSubMenu();
1408 
1410 
1411  c->SetDefaultFlags(AudioIOBusyFlag, AudioIOBusyFlag);
1412  c->BeginSubMenu(_("See&k"));
1413 
1414  c->AddItem(wxT("SeekLeftShort"), _("Short seek &left during playback"), FN(OnSeekLeftShort), wxT("Left\tallowDup"));
1415  c->AddItem(wxT("SeekRightShort"), _("Short seek &right during playback"), FN(OnSeekRightShort), wxT("Right\tallowDup"));
1416  c->AddItem(wxT("SeekLeftLong"), _("Long seek le&ft during playback"), FN(OnSeekLeftLong), wxT("Shift+Left\tallowDup"));
1417  c->AddItem(wxT("SeekRightLong"), _("Long Seek rig&ht during playback"), FN(OnSeekRightLong), wxT("Shift+Right\tallowDup"));
1418  c->EndSubMenu();
1419 
1421 
1423  c->BeginSubMenu(_("De&vice"));
1424 
1425  c->AddItem(wxT("InputDevice"), _("Change &recording device"), FN(OnInputDevice), wxT("Shift+I"),
1428  c->AddItem(wxT("OutputDevice"), _("Change &playback device"), FN(OnOutputDevice), wxT("Shift+O"),
1431  c->AddItem(wxT("AudioHost"), _("Change audio &host"), FN(OnAudioHost), wxT("Shift+H"),
1434  c->AddItem(wxT("InputChannels"), _("Change recording cha&nnels"), FN(OnInputChannels), wxT("Shift+N"),
1437  c->EndSubMenu();
1438 
1440 
1442  c->BeginSubMenu(_("&Selection"));
1443 
1444  c->AddItem(wxT("SnapToOff"), _("Snap To &Off"), FN(OnSnapToOff));
1445  c->AddItem(wxT("SnapToNearest"), _("Snap To &Nearest"), FN(OnSnapToNearest));
1446  c->AddItem(wxT("SnapToPrior"), _("Snap To &Prior"), FN(OnSnapToPrior));
1447 
1448  c->AddItem(wxT("SelStart"), _("Selection to &Start"), FN(OnSelToStart), wxT("Shift+Home"));
1449  c->AddItem(wxT("SelEnd"), _("Selection to En&d"), FN(OnSelToEnd), wxT("Shift+End"));
1450  c->AddItem(wxT("SelExtLeft"), _("Selection Extend &Left"), FN(OnSelExtendLeft), wxT("Shift+Left\twantKeyup\tallowDup"),
1453  c->AddItem(wxT("SelExtRight"), _("Selection Extend &Right"), FN(OnSelExtendRight), wxT("Shift+Right\twantKeyup\tallowDup"),
1456 
1457  c->AddItem(wxT("SelSetExtLeft"), _("Set (or Extend) Le&ft Selection"), FN(OnSelSetExtendLeft),
1460  c->AddItem(wxT("SelSetExtRight"), _("Set (or Extend) Rig&ht Selection"), FN(OnSelSetExtendRight),
1463 
1464  c->AddItem(wxT("SelCntrLeft"), _("Selection Contract L&eft"), FN(OnSelContractLeft), wxT("Ctrl+Shift+Right\twantKeyup"),
1467  c->AddItem(wxT("SelCntrRight"), _("Selection Contract R&ight"), FN(OnSelContractRight), wxT("Ctrl+Shift+Left\twantKeyup"),
1470 
1471  c->EndSubMenu();
1472 
1473 
1475  c->AddSeparator();
1476 
1477  c->AddGlobalCommand(wxT("PrevWindow"), _("Move backward thru active windows"), FN(PrevWindow), wxT("Alt+Shift+F6"));
1478  c->AddGlobalCommand(wxT("NextWindow"), _("Move forward thru active windows"), FN(NextWindow), wxT("Alt+F6"));
1479 
1481 
1483  c->BeginSubMenu(_("F&ocus"));
1484 
1485  c->AddItem(wxT("PrevFrame"), _("Move &backward from toolbars to tracks"), FN(PrevFrame), wxT("Ctrl+Shift+F6"));
1486  c->AddItem(wxT("NextFrame"), _("Move f&orward from toolbars to tracks"), FN(NextFrame), wxT("Ctrl+F6"));
1487 
1490  c->AddItem(wxT("PrevTrack"), _("Move Focus to &Previous Track"), FN(OnCursorUp), wxT("Up"));
1491  c->AddItem(wxT("NextTrack"), _("Move Focus to &Next Track"), FN(OnCursorDown), wxT("Down"));
1492  c->AddItem(wxT("FirstTrack"), _("Move Focus to &First Track"), FN(OnFirstTrack), wxT("Ctrl+Home"));
1493  c->AddItem(wxT("LastTrack"), _("Move Focus to &Last Track"), FN(OnLastTrack), wxT("Ctrl+End"));
1494 
1495  c->AddItem(wxT("ShiftUp"), _("Move Focus to P&revious and Select"), FN(OnShiftUp), wxT("Shift+Up"));
1496  c->AddItem(wxT("ShiftDown"), _("Move Focus to N&ext and Select"), FN(OnShiftDown), wxT("Shift+Down"));
1497 
1498  c->AddItem(wxT("Toggle"), _("&Toggle Focused Track"), FN(OnToggle), wxT("Return"));
1499  c->AddItem(wxT("ToggleAlt"), _("Toggle Focuse&d Track"), FN(OnToggle), wxT("NUMPAD_ENTER"));
1500  c->EndSubMenu();
1501 
1503 
1505  c->BeginSubMenu(_("&Cursor"));
1506 
1507  c->AddItem(wxT("CursorLeft"), _("Cursor &Left"), FN(OnCursorLeft), wxT("Left\twantKeyup\tallowDup"),
1510  c->AddItem(wxT("CursorRight"), _("Cursor &Right"), FN(OnCursorRight), wxT("Right\twantKeyup\tallowDup"),
1513  c->AddItem(wxT("CursorShortJumpLeft"), _("Cursor Sh&ort Jump Left"), FN(OnCursorShortJumpLeft), wxT(","),
1516  c->AddItem(wxT("CursorShortJumpRight"), _("Cursor Shor&t Jump Right"), FN(OnCursorShortJumpRight), wxT("."),
1519  c->AddItem(wxT("CursorLongJumpLeft"), _("Cursor Long J&ump Left"), FN(OnCursorLongJumpLeft), wxT("Shift+,"),
1522  c->AddItem(wxT("CursorLongJumpRight"), _("Cursor Long Ju&mp Right"), FN(OnCursorLongJumpRight), wxT("Shift+."),
1525 
1526  c->AddItem(wxT("ClipLeft"), _("Clip L&eft"), FN(OnClipLeft), wxT("\twantKeyup"),
1529  c->AddItem(wxT("ClipRight"), _("Clip Rig&ht"), FN(OnClipRight), wxT("\twantKeyup"),
1532  c->EndSubMenu();
1533 
1535 
1537  c->BeginSubMenu(_("&Track"));
1538 
1539  c->AddItem(wxT("TrackPan"), _("Change p&an on focused track"), FN(OnTrackPan), wxT("Shift+P"),
1542  c->AddItem(wxT("TrackPanLeft"), _("Pan &left on focused track"), FN(OnTrackPanLeft), wxT("Alt+Shift+Left"),
1545  c->AddItem(wxT("TrackPanRight"), _("Pan &right on focused track"), FN(OnTrackPanRight), wxT("Alt+Shift+Right"),
1548  c->AddItem(wxT("TrackGain"), _("Change gai&n on focused track"), FN(OnTrackGain), wxT("Shift+G"),
1551  c->AddItem(wxT("TrackGainInc"), _("&Increase gain on focused track"), FN(OnTrackGainInc), wxT("Alt+Shift+Up"),
1554  c->AddItem(wxT("TrackGainDec"), _("&Decrease gain on focused track"), FN(OnTrackGainDec), wxT("Alt+Shift+Down"),
1557  c->AddItem(wxT("TrackMenu"), _("Op&en menu on focused track"), FN(OnTrackMenu), wxT("Shift+M\tskipKeydown"),
1560  c->AddItem(wxT("TrackMute"), _("M&ute/Unmute focused track"), FN(OnTrackMute), wxT("Shift+U"),
1563  c->AddItem(wxT("TrackSolo"), _("&Solo/Unsolo focused track"), FN(OnTrackSolo), wxT("Shift+S"),
1566  c->AddItem(wxT("TrackClose"), _("&Close focused track"), FN(OnTrackClose), wxT("Shift+C"),
1569  c->AddItem(wxT("TrackMoveUp"), _("Move focused track u&p"), FN(OnTrackMoveUp),
1572  c->AddItem(wxT("TrackMoveDown"), _("Move focused track do&wn"), FN(OnTrackMoveDown),
1575  c->AddItem(wxT("TrackMoveTop"), _("Move focused track to t&op"), FN(OnTrackMoveTop),
1578  c->AddItem(wxT("TrackMoveBottom"), _("Move focused track to &bottom"), FN(OnTrackMoveBottom),
1581  c->EndSubMenu();
1582 
1583  // Accel key is not bindable.
1584  c->AddItem(wxT("FullScreenOnOff"), _("&Full screen (on/off)"), FN(OnFullScreen),
1585 #ifdef __WXMAC__
1586  wxT("Ctrl+/"),
1587 #else
1588  wxT("F11"),
1589 #endif
1590  AlwaysEnabledFlag, AlwaysEnabledFlag,
1591  wxTopLevelWindow::IsFullScreen() ? 1:0); // Check Mark.
1592 
1593 #ifdef __WXMAC__
1594  /* i18n-hint: Shrink all project windows to icons on the Macintosh tooldock */
1595  c->AddItem(wxT("MacMinimizeAll"), _("Minimize all projects"),
1596  FN(OnMacMinimizeAll), wxT("Ctrl+Alt+M"),
1597  AlwaysEnabledFlag, AlwaysEnabledFlag);
1598 #endif
1599  c->EndMenu();
1600 
1601 
1602  SetMenuBar(menubar.release());
1603  // Bug 143 workaround.
1604  // The bug is in wxWidgets. For a menu that has scrollers, the
1605  // scrollers have an ID of 0 (not wxID_NONE which is -3).
1606  // Therefore wxWidgets attempts to find a help string. See
1607  // wxFrameBase::ShowMenuHelp(int menuId)
1608  // It finds a bogus automatic help string of "Recent &Files"
1609  // from that submenu.
1610  // So we set the help string for command with Id 0 to empty.
1611  mRecentFilesMenu->GetParent()->SetHelpString( 0, "" );
1612  }
1613 
1614 
1615 
1617 
1618 #if defined(__WXDEBUG__)
1619 // c->CheckDups();
1620 #endif
1621 }
1622 
1627  EffectType type,
1628  CommandFlag batchflags,
1629  CommandFlag realflags)
1630 {
1632 
1633  EffectPlugs defplugs;
1634  EffectPlugs optplugs;
1635 
1636  const PluginDescriptor *plug = pm.GetFirstPluginForEffectType(type);
1637  while (plug)
1638  {
1639  if ( !plug->IsEnabled() ){
1640  ;// don't add to menus!
1641  }
1642  else if (plug->IsEffectDefault()
1643 #ifdef EXPERIMENTAL_DA
1644  // Move Nyquist prompt into nyquist group.
1645  && (plug->GetName() != _("Nyquist Prompt"))
1646 #endif
1647  )
1648  {
1649  defplugs.Add(plug);
1650  }
1651  else
1652  {
1653  optplugs.Add(plug);
1654  }
1655  plug = pm.GetNextPluginForEffectType(type);
1656  }
1657 
1658  wxString groupby = gPrefs->Read(wxT("/Effects/GroupBy"), wxT("name"));
1659 
1660  if (groupby == wxT("sortby:name"))
1661  {
1662  defplugs.Sort(SortEffectsByName);
1663  optplugs.Sort(SortEffectsByName);
1664  }
1665  else if (groupby == wxT("sortby:publisher:name"))
1666  {
1667  defplugs.Sort(SortEffectsByName);
1668  optplugs.Sort(SortEffectsByPublisherAndName);
1669  }
1670  else if (groupby == wxT("sortby:type:name"))
1671  {
1672  defplugs.Sort(SortEffectsByName);
1673  optplugs.Sort(SortEffectsByTypeAndName);
1674  }
1675  else if (groupby == wxT("groupby:publisher"))
1676  {
1677  defplugs.Sort(SortEffectsByPublisher);
1678  optplugs.Sort(SortEffectsByPublisher);
1679  }
1680  else if (groupby == wxT("groupby:type"))
1681  {
1682  defplugs.Sort(SortEffectsByType);
1683  optplugs.Sort(SortEffectsByType);
1684  }
1685  else // name
1686  {
1687  defplugs.Sort(SortEffectsByName);
1688  optplugs.Sort(SortEffectsByName);
1689  }
1690 
1691  AddEffectMenuItems(c, defplugs, batchflags, realflags, true);
1692 
1693  if (defplugs.GetCount() && optplugs.GetCount())
1694  {
1695  c->AddSeparator();
1696  }
1697 
1698  AddEffectMenuItems(c, optplugs, batchflags, realflags, false);
1699 
1700  return;
1701 }
1702 
1704  EffectPlugs & plugs,
1705  CommandFlag batchflags,
1706  CommandFlag realflags,
1707  bool isDefault)
1708 {
1709  size_t pluginCnt = plugs.GetCount();
1710 
1711  wxString groupBy = gPrefs->Read(wxT("/Effects/GroupBy"), wxT("name"));
1712 
1713  bool grouped = false;
1714  if (groupBy.StartsWith(wxT("groupby")))
1715  {
1716  grouped = true;
1717  }
1718 
1719  wxArrayString groupNames;
1720  PluginIDList groupPlugs;
1721  std::vector<CommandFlag> groupFlags;
1722  if (grouped)
1723  {
1724  wxString last;
1725  wxString current;
1726 
1727  for (size_t i = 0; i < pluginCnt; i++)
1728  {
1729  const PluginDescriptor *plug = plugs[i];
1730 
1731  wxString name = plug->GetTranslatedName();
1732 
1733  if (plug->IsEffectInteractive())
1734  {
1735  name += wxT("...");
1736  }
1737 
1738  if (groupBy == wxT("groupby:publisher"))
1739  {
1740  current = plug->GetTranslatedVendor();
1741  if (current.IsEmpty())
1742  {
1743  current = _("Unknown");
1744  }
1745  }
1746  else if (groupBy == wxT("groupby:type"))
1747  {
1748  current = plug->GetTranslatedEffectFamily();
1749  if (current.IsEmpty())
1750  {
1751  current = _("Unknown");
1752  }
1753  }
1754 
1755  if (current != last)
1756  {
1757  if (!last.IsEmpty())
1758  {
1759  c->BeginSubMenu(last);
1760  }
1761 
1762  AddEffectMenuItemGroup(c, groupNames, groupPlugs, groupFlags, isDefault);
1763 
1764  if (!last.IsEmpty())
1765  {
1766  c->EndSubMenu();
1767  }
1768 
1769  groupNames.Clear();
1770  groupPlugs.Clear();
1771  groupFlags.clear();
1772  last = current;
1773  }
1774 
1775  groupNames.Add(name);
1776  groupPlugs.Add(plug->GetID());
1777  groupFlags.push_back(plug->IsEffectRealtime() ? realflags : batchflags);
1778  }
1779 
1780  if (groupNames.GetCount() > 0)
1781  {
1782  c->BeginSubMenu(current);
1783 
1784  AddEffectMenuItemGroup(c, groupNames, groupPlugs, groupFlags, isDefault);
1785 
1786  c->EndSubMenu();
1787  }
1788  }
1789  else
1790  {
1791  for (size_t i = 0; i < pluginCnt; i++)
1792  {
1793  const PluginDescriptor *plug = plugs[i];
1794 
1795  wxString name = plug->GetTranslatedName();
1796 
1797  if (plug->IsEffectInteractive())
1798  {
1799  name += wxT("...");
1800  }
1801 
1802  wxString group = wxEmptyString;
1803  if (groupBy == wxT("sortby:publisher:name"))
1804  {
1805  group = plug->GetTranslatedVendor();
1806  }
1807  else if (groupBy == wxT("sortby:type:name"))
1808  {
1809  group = plug->GetTranslatedEffectFamily();
1810  }
1811 
1812  if (plug->IsEffectDefault())
1813  {
1814  group = wxEmptyString;
1815  }
1816 
1817  if (!group.IsEmpty())
1818  {
1819  group += wxT(": ");
1820  }
1821 
1822  groupNames.Add(group + name);
1823  groupPlugs.Add(plug->GetID());
1824  groupFlags.push_back(plug->IsEffectRealtime() ? realflags : batchflags);
1825  }
1826 
1827  if (groupNames.GetCount() > 0)
1828  {
1829  AddEffectMenuItemGroup(c, groupNames, groupPlugs, groupFlags, isDefault);
1830  }
1831 
1832  }
1833 
1834  return;
1835 }
1836 
1838  const wxArrayString & names,
1839  const PluginIDList & plugs,
1840  const std::vector<CommandFlag> & flags,
1841  bool isDefault)
1842 {
1843  int namesCnt = (int) names.GetCount();
1844  int perGroup;
1845 
1846 #if defined(__WXGTK__)
1847  gPrefs->Read(wxT("/Effects/MaxPerGroup"), &perGroup, 15);
1848 #else
1849  gPrefs->Read(wxT("/Effects/MaxPerGroup"), &perGroup, 0);
1850 #endif
1851 
1852  int groupCnt = namesCnt;
1853  for (int i = 0; i < namesCnt; i++)
1854  {
1855  while (i + 1 < namesCnt && names[i].IsSameAs(names[i + 1]))
1856  {
1857  i++;
1858  groupCnt--;
1859  }
1860  }
1861 
1862  // The "default" effects shouldn't be broken into subgroups
1863  if (namesCnt > 0 && isDefault)
1864  {
1865  perGroup = 0;
1866  }
1867 
1868  int max = perGroup;
1869  int items = perGroup;
1870 
1871  if (max > groupCnt)
1872  {
1873  max = 0;
1874  }
1875 
1876  int groupNdx = 0;
1877  for (int i = 0; i < namesCnt; i++)
1878  {
1879  if (max > 0 && items == max)
1880  {
1881  int end = groupNdx + max;
1882  if (end + 1 > groupCnt)
1883  {
1884  end = groupCnt;
1885  }
1886  c->BeginSubMenu(wxString::Format(_("Plug-in %d to %d"),
1887  groupNdx + 1,
1888  end));
1889  }
1890 
1891  if (i + 1 < namesCnt && names[i].IsSameAs(names[i + 1]))
1892  {
1893  wxString name = names[i];
1894  c->BeginSubMenu(name);
1895  while (i < namesCnt && names[i].IsSameAs(name))
1896  {
1897  wxString item = PluginManager::Get().GetPlugin(plugs[i])->GetPath();
1898  c->AddItem(item,
1899  item,
1900  FN(OnEffect),
1901  flags[i],
1902  flags[i], plugs[i]);
1903 
1904  i++;
1905  }
1906  c->EndSubMenu();
1907  i--;
1908  }
1909  else
1910  {
1911  c->AddItem(names[i],
1912  names[i],
1913  FN(OnEffect),
1914  flags[i],
1915  flags[i], plugs[i]);
1916  }
1917 
1918  if (max > 0)
1919  {
1920  groupNdx++;
1921  items--;
1922  if (items == 0 || i + 1 == namesCnt)
1923  {
1924  c->EndSubMenu();
1925  items = max;
1926  }
1927  }
1928  }
1929 
1930  return;
1931 }
1932 
1934 {
1935  // Recent Files and Recent Projects menus
1936 
1937 #ifdef __WXMAC__
1938  /* i18n-hint: This is the name of the menu item on Mac OS X only */
1939  mRecentFilesMenu = c->BeginSubMenu(_("Open Recent"));
1940 #else
1941  /* i18n-hint: This is the name of the menu item on Windows and Linux */
1942  mRecentFilesMenu = c->BeginSubMenu(_("Recent &Files"));
1943 #endif
1944 
1947 
1948  c->EndSubMenu();
1949 
1950 }
1951 
1953 {
1954  wxString desc;
1955  int cur = GetUndoManager()->GetCurrentState();
1956 
1957  if (GetUndoManager()->UndoAvailable()) {
1958  GetUndoManager()->GetShortDescription(cur, &desc);
1959  mCommandManager.Modify(wxT("Undo"),
1960  wxString::Format(_("&Undo %s"),
1961  desc));
1962  }
1963  else {
1964  mCommandManager.Modify(wxT("Undo"),
1965  _("&Undo"));
1966  }
1967 
1968  if (GetUndoManager()->RedoAvailable()) {
1969  GetUndoManager()->GetShortDescription(cur+1, &desc);
1970  mCommandManager.Modify(wxT("Redo"),
1971  wxString::Format(_("&Redo %s"),
1972  desc));
1973  mCommandManager.Enable(wxT("Redo"), true);
1974  }
1975  else {
1976  mCommandManager.Modify(wxT("Redo"),
1977  _("&Redo"));
1978  mCommandManager.Enable(wxT("Redo"), false);
1979  }
1980 }
1981 
1983 {
1984  // On OSX, we can't rebuild the menus while a modal dialog is being shown
1985  // since the enabled state for menus like Quit and Preference gets out of
1986  // sync with wxWidgets idea of what it should be.
1987 #if defined(__WXMAC__) && defined(__WXDEBUG__)
1988  {
1989  wxDialog *dlg = wxDynamicCast(wxGetTopLevelParent(FindFocus()), wxDialog);
1990  wxASSERT((!dlg || !dlg->IsModal()));
1991  }
1992 #endif
1993 
1994  // Allow FileHistory to remove its own menu
1996 
1997  // Delete the menus, since we will soon recreate them.
1998  // Rather oddly, the menus don't vanish as a result of doing this.
1999  {
2000  std::unique_ptr<wxMenuBar> menuBar{ GetMenuBar() };
2001  DetachMenuBar();
2002  // menuBar gets deleted here
2003  }
2004 
2006 
2008 
2010 }
2011 
2013 {
2014 }
2015 
2017 {
2018  wxWindow *w = FindFocus();
2019 
2020  while (w && mToolManager && mTrackPanel) {
2021  if (w == mToolManager->GetTopDock()) {
2022  return TopDockHasFocus;
2023  }
2024 
2025  if (w == mRuler)
2026  return RulerHasFocus;
2027 
2028  if (w == mTrackPanel) {
2029  return TrackPanelHasFocus;
2030  }
2031  // LIE if Lyrics window has focus.
2032  // we want to act as if TrackPanel has focus.
2033  if (w== mLyricsWindow) {
2034  return TrackPanelHasFocus;
2035  }
2036  if (w == mToolManager->GetBotDock()) {
2037  return BotDockHasFocus;
2038  }
2039 
2040  w = w->GetParent();
2041  }
2042 
2043  return AlwaysEnabledFlag;
2044 }
2045 
2047 {
2048  // This method determines all of the flags that determine whether
2049  // certain menu items and commands should be enabled or disabled,
2050  // and returns them in a bitfield. Note that if none of the flags
2051  // have changed, it's not necessary to even check for updates.
2052  auto flags = AlwaysEnabledFlag;
2053  // static variable, used to remember flags for next time.
2054  static auto lastFlags = flags;
2055 
2056  if (auto focus = wxWindow::FindFocus()) {
2057  while (focus && focus->GetParent())
2058  focus = focus->GetParent();
2059  if (focus && !static_cast<wxTopLevelWindow*>(focus)->IsIconized())
2060  flags |= NotMinimizedFlag;
2061  }
2062 
2063  // quick 'short-circuit' return.
2064  if ( checkActive && !IsActive() ){
2065  // short cirucit return should preserve flags that have not been calculated.
2066  flags = (lastFlags & ~NotMinimizedFlag) | flags;
2067  lastFlags = flags;
2068  return flags;
2069  }
2070 
2072  flags |= AudioIONotBusyFlag;
2073  else
2074  flags |= AudioIOBusyFlag;
2075 
2076  if( gAudioIO->IsPaused() )
2077  flags |= PausedFlag;
2078  else
2079  flags |= NotPausedFlag;
2080 
2082  flags |= TimeSelectedFlag;
2083 
2084  TrackListIterator iter(GetTracks());
2085  Track *t = iter.First();
2086  while (t) {
2087  flags |= TracksExistFlag;
2088  if (t->GetKind() == Track::Label) {
2089  LabelTrack *lt = (LabelTrack *) t;
2090 
2091  flags |= LabelTracksExistFlag;
2092 
2093  if (lt->GetSelected()) {
2094  flags |= TracksSelectedFlag;
2095  for (int i = 0; i < lt->GetNumLabels(); i++) {
2096  const LabelStruct *ls = lt->GetLabel(i);
2097  if (ls->getT0() >= mViewInfo.selectedRegion.t0() &&
2098  ls->getT1() <= mViewInfo.selectedRegion.t1()) {
2099  flags |= LabelsSelectedFlag;
2100  break;
2101  }
2102  }
2103  }
2104 
2105  if (lt->IsTextSelected()) {
2106  flags |= CutCopyAvailableFlag;
2107  }
2108  }
2109  else if (t->GetKind() == Track::Wave) {
2110  flags |= WaveTracksExistFlag;
2111  flags |= PlayableTracksExistFlag;
2112  if (t->GetSelected()) {
2113  flags |= TracksSelectedFlag;
2114  if (t->GetLinked()) {
2115  flags |= StereoRequiredFlag;
2116  }
2117  else {
2118  flags |= WaveTracksSelectedFlag;
2119  flags |= AudioTracksSelectedFlag;
2120  }
2121  }
2122  if( t->GetEndTime() > t->GetStartTime() )
2123  flags |= HasWaveDataFlag;
2124  }
2125 #if defined(USE_MIDI)
2126  else if (t->GetKind() == Track::Note) {
2127  NoteTrack *nt = (NoteTrack *) t;
2128 
2129  flags |= NoteTracksExistFlag;
2130 #ifdef EXPERIMENTAL_MIDI_OUT
2131  flags |= PlayableTracksExistFlag;
2132 #endif
2133 
2134  if (nt->GetSelected()) {
2135  flags |= TracksSelectedFlag;
2136  flags |= NoteTracksSelectedFlag;
2137  flags |= AudioTracksSelectedFlag; // even if not EXPERIMENTAL_MIDI_OUT
2138  }
2139  }
2140 #endif
2141  t = iter.Next();
2142  }
2143 
2144  if((msClipT1 - msClipT0) > 0.0)
2145  flags |= ClipboardFlag;
2146 
2147  if (GetUndoManager()->UnsavedChanges() || !IsProjectSaved())
2148  flags |= UnsavedChangesFlag;
2149 
2150  if (!mLastEffect.IsEmpty())
2151  flags |= HasLastEffectFlag;
2152 
2153  if (GetUndoManager()->UndoAvailable())
2154  flags |= UndoAvailableFlag;
2155 
2156  if (GetUndoManager()->RedoAvailable())
2157  flags |= RedoAvailableFlag;
2158 
2159  if (ZoomInAvailable() && (flags & TracksExistFlag))
2160  flags |= ZoomInAvailableFlag;
2161 
2162  if (ZoomOutAvailable() && (flags & TracksExistFlag))
2163  flags |= ZoomOutAvailableFlag;
2164 
2165  // TextClipFlag is currently unused (Jan 2017, 2.1.3 alpha)
2166  // and LabelTrack::IsTextClipSupported() is quite slow on Linux,
2167  // so disable for now (See bug 1575).
2168  // if ((flags & LabelTracksExistFlag) && LabelTrack::IsTextClipSupported())
2169  // flags |= TextClipFlag;
2170 
2171  flags |= GetFocusedFrame();
2172 
2173  double start, end;
2174  GetPlayRegion(&start, &end);
2175  if (IsPlayRegionLocked())
2176  flags |= PlayRegionLockedFlag;
2177  else if (start != end)
2178  flags |= PlayRegionNotLockedFlag;
2179 
2180  if (flags & AudioIONotBusyFlag) {
2181  if (flags & TimeSelectedFlag) {
2182  if (flags & TracksSelectedFlag) {
2183  flags |= CutCopyAvailableFlag;
2184  }
2185  }
2186  }
2187 
2188  if (wxGetApp().GetRecentFiles()->GetCount() > 0)
2189  flags |= HaveRecentFiles;
2190 
2191  if (IsSyncLocked())
2192  flags |= IsSyncLockedFlag;
2193  else
2194  flags |= IsNotSyncLockedFlag;
2195 
2196  if (!EffectManager::Get().RealtimeIsActive())
2197  flags |= IsRealtimeNotActiveFlag;
2198 
2199  if (!mIsCapturing)
2200  flags |= CaptureNotBusyFlag;
2201 
2203  if (bar->ControlToolBar::CanStopAudioStream())
2204  flags |= CanStopAudioStreamFlag;
2205 
2206  lastFlags = flags;
2207  return flags;
2208 }
2209 
2210 // Select the full time range, if no
2211 // time range is selected.
2213 {
2214  auto flags = GetUpdateFlags();
2215  if(!(flags & TracksSelectedFlag) ||
2217  OnSelectSomething(*this);
2218 }
2219 
2220 // Stop playing or recording, if paused.
2222 {
2223  auto flags = GetUpdateFlags();
2224  if( flags & PausedFlag )
2225  OnStop(*this);
2226 }
2227 
2229 {
2230  AProjectArray::iterator i;
2231  for (i = gAudacityProjects.begin(); i != gAudacityProjects.end(); ++i) {
2232  (*i)->ModifyToolbarMenus();
2233  }
2234 }
2235 
2237 {
2238  // Refreshes can occur during shutdown and the toolmanager may already
2239  // be deleted, so protect against it.
2240  if (!mToolManager) {
2241  return;
2242  }
2243 
2244  mCommandManager.Check(wxT("ShowScrubbingTB"),
2245  mToolManager->IsVisible(ScrubbingBarID));
2246  mCommandManager.Check(wxT("ShowDeviceTB"),
2247  mToolManager->IsVisible(DeviceBarID));
2248  mCommandManager.Check(wxT("ShowEditTB"),
2249  mToolManager->IsVisible(EditBarID));
2250  mCommandManager.Check(wxT("ShowMeterTB"),
2251  mToolManager->IsVisible(MeterBarID));
2252  mCommandManager.Check(wxT("ShowRecordMeterTB"),
2253  mToolManager->IsVisible(RecordMeterBarID));
2254  mCommandManager.Check(wxT("ShowPlayMeterTB"),
2255  mToolManager->IsVisible(PlayMeterBarID));
2256  mCommandManager.Check(wxT("ShowMixerTB"),
2257  mToolManager->IsVisible(MixerBarID));
2258  mCommandManager.Check(wxT("ShowSelectionTB"),
2259  mToolManager->IsVisible(SelectionBarID));
2260 #ifdef EXPERIMENTAL_SPECTRAL_EDITING
2261  mCommandManager.Check(wxT("ShowSpectralSelectionTB"),
2262  mToolManager->IsVisible(SpectralSelectionBarID));
2263 #endif
2264  mCommandManager.Check(wxT("ShowToolsTB"),
2265  mToolManager->IsVisible(ToolsBarID));
2266  mCommandManager.Check(wxT("ShowTranscriptionTB"),
2267  mToolManager->IsVisible(TranscriptionBarID));
2268  mCommandManager.Check(wxT("ShowTransportTB"),
2269  mToolManager->IsVisible(TransportBarID));
2270 
2271  // Now, go through each toolbar, and call EnableDisableButtons()
2272  for (int i = 0; i < ToolBarCount; i++) {
2273  mToolManager->GetToolBar(i)->EnableDisableButtons();
2274  }
2275 
2276  // These don't really belong here, but it's easier and especially so for
2277  // the Edit toolbar and the sync-lock menu item.
2278  bool active;
2279  gPrefs->Read(wxT("/AudioIO/SoundActivatedRecord"),&active, false);
2280  mCommandManager.Check(wxT("SoundActivation"), active);
2281 #ifdef EXPERIMENTAL_AUTOMATED_INPUT_LEVEL_ADJUSTMENT
2282  gPrefs->Read(wxT("/AudioIO/AutomatedInputLevelAdjustment"),&active, false);
2283  mCommandManager.Check(wxT("AutomatedInputLevelAdjustmentOnOff"), active);
2284 #endif
2285 
2287  mCommandManager.Check(wxT("PinnedHead"), active);
2288 
2289 #ifdef EXPERIMENTAL_DA
2290  gPrefs->Read(wxT("/AudioIO/Duplex"),&active, false);
2291 #else
2292  gPrefs->Read(wxT("/AudioIO/Duplex"),&active, true);
2293 #endif
2294  mCommandManager.Check(wxT("Duplex"), active);
2295  gPrefs->Read(wxT("/AudioIO/SWPlaythrough"),&active, false);
2296  mCommandManager.Check(wxT("SWPlaythrough"), active);
2297  gPrefs->Read(wxT("/GUI/SyncLockTracks"), &active, false);
2298  SetSyncLock(active);
2299  mCommandManager.Check(wxT("SyncLock"), active);
2300  gPrefs->Read(wxT("/GUI/TypeToCreateLabel"),&active, true);
2301  mCommandManager.Check(wxT("TypeToCreateLabel"), active);
2302 }
2303 
2304 // checkActive is a temporary hack that should be removed as soon as we
2305 // get multiple effect preview working
2306 void AudacityProject::UpdateMenus(bool checkActive)
2307 {
2308  //ANSWER-ME: Why UpdateMenus only does active project?
2309  //JKC: Is this test fixing a bug when multiple projects are open?
2310  //so that menu states work even when different in different projects?
2311  if (this != GetActiveProject())
2312  return;
2313 
2314  auto flags = GetUpdateFlags(checkActive);
2315  auto flags2 = flags;
2316 
2317  // We can enable some extra items if we have select-all-on-none.
2318  //EXPLAIN-ME: Why is this here rather than in GetUpdateFlags()?
2319  //ANSWER: Because flags2 is used in the menu enable/disable.
2320  //The effect still needs flags to determine whether it will need
2321  //to actually do the 'select all' to make the command valid.
2322  if (mWhatIfNoSelection != 0)
2323  {
2324  if ((flags & TracksExistFlag))
2325  {
2326  flags2 |= TracksSelectedFlag;
2327  if ((flags & WaveTracksExistFlag))
2328  {
2329  flags2 |= TimeSelectedFlag
2332  }
2333  }
2334  }
2335 
2336  if( mStopIfWasPaused )
2337  {
2338  if( flags & PausedFlag ){
2339  flags2 |= AudioIONotBusyFlag;
2340  }
2341  }
2342 
2343  // Return from this function if nothing's changed since
2344  // the last time we were here.
2345  if (flags == mLastFlags)
2346  return;
2347  mLastFlags = flags;
2348 
2350 
2351  // With select-all-on-none, some items that we don't want enabled may have
2352  // been enabled, since we changed the flags. Here we manually disable them.
2353  // 0 is grey out, 1 is Autoselect, 2 is Give warnings.
2354  if (mWhatIfNoSelection != 0)
2355  {
2356  if (!(flags & TimeSelectedFlag) | !(flags & TracksSelectedFlag))
2357  {
2358  mCommandManager.Enable(wxT("SplitCut"), false);
2359  mCommandManager.Enable(wxT("SplitDelete"), false);
2360  }
2361  if (!(flags & WaveTracksSelectedFlag))
2362  {
2363  mCommandManager.Enable(wxT("Split"), false);
2364  }
2365  if (!(flags & TimeSelectedFlag) | !(flags & WaveTracksSelectedFlag))
2366  {
2367  mCommandManager.Enable(wxT("ExportSel"), false);
2368  mCommandManager.Enable(wxT("SplitNew"), false);
2369  }
2370  if (!(flags & TimeSelectedFlag) | !(flags & AudioTracksSelectedFlag))
2371  {
2372  mCommandManager.Enable(wxT("Trim"), false);
2373  }
2374  }
2375 
2376 #if 0
2377  if (flags & CutCopyAvailableFlag) {
2378  mCommandManager.Enable(wxT("Copy"), true);
2379  mCommandManager.Enable(wxT("Cut"), true);
2380  }
2381 #endif
2382 
2384 }
2385 
2386 //
2387 // Tool selection commands
2388 //
2389 
2392 {
2393  ToolsToolBar *toolbar = GetToolsToolBar();
2394  if (toolbar) {
2395  toolbar->SetCurrentTool(tool);
2396  mTrackPanel->Refresh(false);
2397  }
2398 }
2399 
2402 {
2404 }
2405 
2408 {
2409  SetTool(zoomTool);
2410 }
2411 
2414 {
2416 }
2417 
2420 {
2421  SetTool(slideTool);
2422 }
2423 
2425 {
2426  SetTool(drawTool);
2427 }
2428 
2430 {
2431  SetTool(multiTool);
2432 }
2433 
2434 
2436 {
2437  ToolsToolBar *toolbar = GetToolsToolBar();
2438  if (toolbar) {
2439  // Use GetDownTool() here since GetCurrentTool() can return a value that
2440  // doesn't represent the real tool if the Multi-tool is being used.
2441  toolbar->SetCurrentTool((toolbar->GetDownTool()+1)%numTools);
2442  mTrackPanel->Refresh(false);
2443  }
2444 }
2445 
2447 {
2448  ToolsToolBar *toolbar = GetToolsToolBar();
2449  if (toolbar) {
2450  // Use GetDownTool() here since GetCurrentTool() can return a value that
2451  // doesn't represent the real tool if the Multi-tool is being used.
2452  toolbar->SetCurrentTool((toolbar->GetDownTool()+(numTools-1))%numTools);
2453  mTrackPanel->Refresh(false);
2454  }
2455 }
2456 
2457 
2458 //
2459 // Audio I/O Commands
2460 //
2461 
2462 // TODO: Should all these functions which involve
2463 // the toolbar actually move into ControlToolBar?
2464 
2469 bool AudacityProject::MakeReadyToPlay(bool loop, bool cutpreview)
2470 {
2471  ControlToolBar *toolbar = GetControlToolBar();
2472  wxCommandEvent evt;
2473 
2474  // If this project is playing, stop playing
2476  toolbar->SetPlay(false); //Pops
2477  toolbar->SetStop(true); //Pushes stop down
2478  toolbar->OnStop(evt);
2479 
2480  ::wxMilliSleep(100);
2481  }
2482 
2483  // If it didn't stop playing quickly, or if some other
2484  // project is playing, return
2485  if (gAudioIO->IsBusy())
2486  return false;
2487 
2488  ControlToolBar::PlayAppearance appearance =
2492  toolbar->SetPlay(true, appearance);
2493  toolbar->SetStop(false);
2494 
2495  return true;
2496 }
2497 
2499 {
2500  if( !MakeReadyToPlay() )
2501  return;
2502 
2503  double pos = mTrackPanel->GetMostRecentXPos();
2505  (SelectedRegion(pos - 0.5, pos + 0.5), GetDefaultPlayOptions(),
2507 }
2508 
2509 
2518 {
2519  if( !MakeReadyToPlay() )
2520  return;
2521 
2522  double pos = mTrackPanel->GetMostRecentXPos();
2523 
2524  double t0,t1;
2525  // check region between pointer and the nearest selection edge
2526  if (fabs(pos - mViewInfo.selectedRegion.t0()) <
2527  fabs(pos - mViewInfo.selectedRegion.t1())) {
2528  t0 = t1 = mViewInfo.selectedRegion.t0();
2529  } else {
2530  t0 = t1 = mViewInfo.selectedRegion.t1();
2531  }
2532  if( pos < t1)
2533  t0=pos;
2534  else
2535  t1=pos;
2536 
2537  // JKC: oneSecondPlay mode disables auto scrolling
2538  // On balance I think we should always do this in this function
2539  // since you are typically interested in the sound EXACTLY
2540  // where the cursor is.
2541  // TODO: have 'playing attributes' such as 'with_autoscroll'
2542  // rather than modes, since that's how we're now using the modes.
2543 
2544  // An alternative, commented out below, is to disable autoscroll
2545  // only when playing a short region, less than or equal to a second.
2546 // mLastPlayMode = ((t1-t0) > 1.0) ? normalPlay : oneSecondPlay;
2547 
2550 }
2551 
2552 // The next 4 functions provide a limited version of the
2553 // functionality of OnPlayToSelection() for keyboard users
2554 
2556 {
2557  if( !MakeReadyToPlay() )
2558  return;
2559 
2560  double t0 = mViewInfo.selectedRegion.t0();
2561  double beforeLen;
2562  gPrefs->Read(wxT("/AudioIO/CutPreviewBeforeLen"), &beforeLen, 2.0);
2563 
2565 }
2566 
2568 {
2569  if( !MakeReadyToPlay() )
2570  return;
2571 
2572  double t0 = mViewInfo.selectedRegion.t0();
2573  double t1 = mViewInfo.selectedRegion.t1();
2574  double afterLen;
2575  gPrefs->Read(wxT("/AudioIO/CutPreviewAfterLen"), &afterLen, 1.0);
2576 
2577  if ( t1 - t0 > 0.0 && t1 - t0 < afterLen )
2580  else
2582 }
2583 
2585 {
2586  if( !MakeReadyToPlay() )
2587  return;
2588 
2589  double t0 = mViewInfo.selectedRegion.t0();
2590  double t1 = mViewInfo.selectedRegion.t1();
2591  double beforeLen;
2592  gPrefs->Read(wxT("/AudioIO/CutPreviewBeforeLen"), &beforeLen, 2.0);
2593 
2594  if ( t1 - t0 > 0.0 && t1 - t0 < beforeLen )
2597  else
2599 }
2600 
2601 
2603 {
2604  if( !MakeReadyToPlay() )
2605  return;
2606 
2607  double t1 = mViewInfo.selectedRegion.t1();
2608  double afterLen;
2609  gPrefs->Read(wxT("/AudioIO/CutPreviewAfterLen"), &afterLen, 1.0);
2610 
2612 }
2613 
2615 {
2616  if (!MakeReadyToPlay())
2617  return;
2618 
2619  double t0 = mViewInfo.selectedRegion.t0();
2620  double t1 = mViewInfo.selectedRegion.t1();
2621  double beforeLen;
2622  gPrefs->Read(wxT("/AudioIO/CutPreviewBeforeLen"), &beforeLen, 2.0);
2623  double afterLen;
2624  gPrefs->Read(wxT("/AudioIO/CutPreviewAfterLen"), &afterLen, 1.0);
2625 
2626  if ( t1 - t0 > 0.0 && t1 - t0 < afterLen )
2628  else
2630 }
2631 
2633 {
2634  if (!MakeReadyToPlay())
2635  return;
2636 
2637  double t0 = mViewInfo.selectedRegion.t0();
2638  double t1 = mViewInfo.selectedRegion.t1();
2639  double beforeLen;
2640  gPrefs->Read(wxT("/AudioIO/CutPreviewBeforeLen"), &beforeLen, 2.0);
2641  double afterLen;
2642  gPrefs->Read(wxT("/AudioIO/CutPreviewAfterLen"), &afterLen, 1.0);
2643 
2644  if ( t1 - t0 > 0.0 && t1 - t0 < beforeLen )
2646  else
2648 }
2649 
2650 
2652 {
2653  if( !MakeReadyToPlay(true) )
2654  return;
2655 
2656  // Now play in a loop
2657  // Will automatically set mLastPlayMode
2659 }
2660 
2662 {
2663  if ( !MakeReadyToPlay(false, true) )
2664  return;
2665 
2666  // Play with cut preview
2667  GetControlToolBar()->PlayCurrentRegion(false, true);
2668 }
2669 
2671 {
2672  ControlToolBar *toolbar = GetControlToolBar();
2673 
2674  //If this project is playing, stop playing, make sure everything is unpaused.
2676  toolbar->SetPlay(false); //Pops
2677  toolbar->SetStop(true); //Pushes stop down
2678  toolbar->StopPlaying();
2679  }
2680  else if (gAudioIO->IsStreamActive()) {
2681  //If this project isn't playing, but another one is, stop playing the old and start the NEW.
2682 
2683  //find out which project we need;
2684  AudacityProject* otherProject = NULL;
2685  for(unsigned i=0; i<gAudacityProjects.size(); i++) {
2686  if(gAudioIO->IsStreamActive(gAudacityProjects[i]->GetAudioIOToken())) {
2687  otherProject=gAudacityProjects[i].get();
2688  break;
2689  }
2690  }
2691 
2692  //stop playing the other project
2693  if(otherProject) {
2694  ControlToolBar *otherToolbar = otherProject->GetControlToolBar();
2695  otherToolbar->SetPlay(false); //Pops
2696  otherToolbar->SetStop(true); //Pushes stop down
2697  otherToolbar->StopPlaying();
2698  }
2699 
2700  //play the front project
2701  if (!gAudioIO->IsBusy()) {
2702  //update the playing area
2704  //Otherwise, start playing (assuming audio I/O isn't busy)
2705  //toolbar->SetPlay(true); // Not needed as done in PlayPlayRegion.
2706  toolbar->SetStop(false);
2707 
2708  // Will automatically set mLastPlayMode
2709  toolbar->PlayCurrentRegion(false);
2710  }
2711  }
2712  else if (!gAudioIO->IsBusy()) {
2713  //Otherwise, start playing (assuming audio I/O isn't busy)
2714  //toolbar->SetPlay(true); // Not needed as done in PlayPlayRegion.
2715  toolbar->SetStop(false);
2716 
2717  // Will automatically set mLastPlayMode
2718  toolbar->PlayCurrentRegion(false);
2719  }
2720 }
2721 
2723 {
2724  wxCommandEvent evt;
2725 
2726  GetControlToolBar()->OnStop(evt);
2727 }
2728 
2730 {
2731  wxCommandEvent evt;
2732 
2733  GetControlToolBar()->OnPause(evt);
2734 }
2735 
2737 {
2738  wxCommandEvent evt;
2739  evt.SetInt(2); // 0 is default, use 1 to set shift on, 2 to clear it
2740 
2741  GetControlToolBar()->OnRecord(evt);
2742 }
2743 
2744 // If first choice is record same track 2nd choice is record NEW track
2745 // and vice versa.
2747 {
2748  wxCommandEvent evt;
2749  evt.SetInt(1); // 0 is default, use 1 to set shift on, 2 to clear it
2750 
2751  GetControlToolBar()->OnRecord(evt);
2752 }
2753 
2754 // The code for "OnPlayStopSelect" is simply the code of "OnPlayStop" and "OnStopSelect" merged.
2756 {
2757  ControlToolBar *toolbar = GetControlToolBar();
2758  wxCommandEvent evt;
2759  if (DoPlayStopSelect(false, false))
2760  toolbar->OnStop(evt);
2761  else if (!gAudioIO->IsBusy()) {
2762  //Otherwise, start playing (assuming audio I/O isn't busy)
2763  //toolbar->SetPlay(true); // Not needed as set in PlayPlayRegion()
2764  toolbar->SetStop(false);
2765 
2766  // Will automatically set mLastPlayMode
2767  toolbar->PlayCurrentRegion(false);
2768  }
2769 }
2770 
2771 bool AudacityProject::DoPlayStopSelect(bool click, bool shift)
2772 {
2773  ControlToolBar *toolbar = GetControlToolBar();
2774 
2775  //If busy, stop playing, make sure everything is unpaused.
2776  if (GetScrubber().HasStartedScrubbing() ||
2778  toolbar->SetPlay(false); //Pops
2779  toolbar->SetStop(true); //Pushes stop down
2780 
2781  // change the selection
2782  auto time = gAudioIO->GetStreamTime();
2783  auto &selection = mViewInfo.selectedRegion;
2784  if (shift && click) {
2785  // Change the region selection, as if by shift-click at the play head
2786  auto t0 = selection.t0(), t1 = selection.t1();
2787  if (time < t0)
2788  // Grow selection
2789  t0 = time;
2790  else if (time > t1)
2791  // Grow selection
2792  t1 = time;
2793  else {
2794  // Shrink selection, changing the nearer boundary
2795  if (fabs(t0 - time) < fabs(t1 - time))
2796  t0 = time;
2797  else
2798  t1 = time;
2799  }
2800  selection.setTimes(t0, t1);
2801  }
2802  else if (click){
2803  // avoid a point at negative time.
2804  time = wxMax( time, 0 );
2805  // Set a point selection, as if by a click at the play head
2806  selection.setTimes(time, time);
2807  } else
2808  // How stop and set cursor always worked
2809  // -- change t0, collapsing to point only if t1 was greater
2810  selection.setT0(time, false);
2811 
2812  ModifyState(false); // without bWantsAutoSave
2813  return true;
2814  }
2815  return false;
2816 }
2817 
2819 {
2820  wxCommandEvent evt;
2821 
2822  if (gAudioIO->IsStreamActive()) {
2824  GetControlToolBar()->OnStop(evt);
2825  ModifyState(false); // without bWantsAutoSave
2826  }
2827 }
2828 
2830 {
2831  bool pause;
2832  gPrefs->Read(wxT("/AudioIO/SoundActivatedRecord"), &pause, false);
2833  gPrefs->Write(wxT("/AudioIO/SoundActivatedRecord"), !pause);
2834  gPrefs->Flush();
2836 }
2837 
2839 {
2840  bool value = !TracksPrefs::GetPinnedHeadPreference();
2843 
2844  // Change what happens in case transport is in progress right now
2845  auto ctb = GetActiveProject()->GetControlToolBar();
2846  if (ctb)
2848 
2849  auto ruler = GetRulerPanel();
2850  if (ruler)
2851  // Update button image
2852  ruler->UpdateButtonStates();
2853 
2854  auto &scrubber = GetScrubber();
2855  if (scrubber.HasStartedScrubbing())
2856  scrubber.SetScrollScrubbing(value);
2857 }
2858 
2860 {
2861  bool Duplex;
2862 #ifdef EXPERIMENTAL_DA
2863  gPrefs->Read(wxT("/AudioIO/Duplex"), &Duplex, false);
2864 #else
2865  gPrefs->Read(wxT("/AudioIO/Duplex"), &Duplex, true);
2866 #endif
2867  gPrefs->Write(wxT("/AudioIO/Duplex"), !Duplex);
2868  gPrefs->Flush();
2870 }
2871 
2873 {
2874  bool SWPlaythrough;
2875  gPrefs->Read(wxT("/AudioIO/SWPlaythrough"), &SWPlaythrough, false);
2876  gPrefs->Write(wxT("/AudioIO/SWPlaythrough"), !SWPlaythrough);
2877  gPrefs->Flush();
2879 }
2880 
2881 #ifdef EXPERIMENTAL_AUTOMATED_INPUT_LEVEL_ADJUSTMENT
2882 void AudacityProject::OnToggleAutomatedInputLevelAdjustment()
2883 {
2884  bool AVEnabled;
2885  gPrefs->Read(wxT("/AudioIO/AutomatedInputLevelAdjustment"), &AVEnabled, false);
2886  gPrefs->Write(wxT("/AudioIO/AutomatedInputLevelAdjustment"), !AVEnabled);
2887  gPrefs->Flush();
2889 }
2890 #endif
2891 
2893 {
2894  double stime = 0.0;
2895 
2896  if (t->GetKind() == Track::Wave) {
2897  WaveTrack *w = (WaveTrack *)t;
2898  stime = w->GetEndTime();
2899 
2900  WaveClip *c;
2901  int ndx;
2902  for (ndx = 0; ndx < w->GetNumClips(); ndx++) {
2903  c = w->GetClipByIndex(ndx);
2904  if (c->GetNumSamples() == 0)
2905  continue;
2906  if (c->GetStartTime() < stime) {
2907  stime = c->GetStartTime();
2908  }
2909  }
2910  }
2911  else if (t->GetKind() == Track::Label) {
2912  LabelTrack *l = (LabelTrack *)t;
2913  stime = l->GetStartTime();
2914  }
2915 
2916  return stime;
2917 }
2918 
2919 //sort based on flags. see Project.h for sort flags
2921 {
2922  size_t ndx = 0;
2923  int cmpValue;
2924  // This one place outside of TrackList where we must use undisguised
2925  // std::list iterators! Avoid this elsewhere!
2926  std::vector<ListOfTracks::iterator> arr;
2927  arr.reserve(mTracks->size());
2928  bool lastTrackLinked = false;
2929  //sort by linked tracks. Assumes linked track follows owner in list.
2930 
2931  // First find the permutation.
2932  for (auto iter = mTracks->ListOfTracks::begin(),
2933  end = mTracks->ListOfTracks::end(); iter != end; ++iter) {
2934  const auto &track = *iter;
2935  if(lastTrackLinked) {
2936  //insert after the last track since this track should be linked to it.
2937  ndx++;
2938  }
2939  else {
2940  bool bArrayTrackLinked = false;
2941  for (ndx = 0; ndx < arr.size(); ++ndx) {
2942  Track &arrTrack = **arr[ndx];
2943  // Don't insert between channels of a stereo track!
2944  if( bArrayTrackLinked ){
2945  bArrayTrackLinked = false;
2946  }
2947  else if(flags & kAudacitySortByName) {
2948  //do case insensitive sort - cmpNoCase returns less than zero if the string is 'less than' its argument
2949  //also if we have case insensitive equality, then we need to sort by case as well
2950  //We sort 'b' before 'B' accordingly. We uncharacteristically use greater than for the case sensitive
2951  //compare because 'b' is greater than 'B' in ascii.
2952  cmpValue = track->GetName().CmpNoCase(arrTrack.GetName());
2953  if (cmpValue < 0 ||
2954  (0 == cmpValue && track->GetName().CompareTo(arrTrack.GetName()) > 0) )
2955  break;
2956  }
2957  //sort by time otherwise
2958  else if(flags & kAudacitySortByTime) {
2959  //we have to search each track and all its linked ones to fine the minimum start time.
2960  double time1, time2, tempTime;
2961  const Track* tempTrack;
2962  size_t candidatesLookedAt;
2963 
2964  candidatesLookedAt = 0;
2965  tempTrack = &*track;
2966  time1 = time2 = std::numeric_limits<double>::max(); //TODO: find max time value. (I don't think we have one yet)
2967  while(tempTrack) {
2968  tempTime = GetTime(tempTrack);
2969  time1 = std::min(time1, tempTime);
2970  if(tempTrack->GetLinked())
2971  tempTrack = tempTrack->GetLink();
2972  else
2973  tempTrack = NULL;
2974  }
2975 
2976  //get candidate's (from sorted array) time
2977  tempTrack = &arrTrack;
2978  while(tempTrack) {
2979  tempTime = GetTime(tempTrack);
2980  time2 = std::min(time2, tempTime);
2981  if(tempTrack->GetLinked() && (ndx+candidatesLookedAt < arr.size()-1) ) {
2982  candidatesLookedAt++;
2983  tempTrack = &**arr[ndx+candidatesLookedAt];
2984  }
2985  else
2986  tempTrack = NULL;
2987  }
2988 
2989  if (time1 < time2)
2990  break;
2991 
2992  ndx+=candidatesLookedAt;
2993  }
2994  bArrayTrackLinked = arrTrack.GetLinked();
2995  }
2996  }
2997  arr.insert(arr.begin() + ndx, iter);
2998 
2999  lastTrackLinked = track->GetLinked();
3000  }
3001 
3002  // Now apply the permutation
3003  mTracks->Permute(arr);
3004 }
3005 
3007 {
3009 
3010  PushState(_("Tracks sorted by time"), _("Sort by Time"));
3011 
3012  mTrackPanel->Refresh(false);
3013 }
3014 
3016 {
3018 
3019  PushState(_("Tracks sorted by name"), _("Sort by Name"));
3020 
3021  mTrackPanel->Refresh(false);
3022 }
3023 
3025 {
3026  wxCommandEvent evt;
3027 
3028  GetControlToolBar()->OnRewind(evt);
3029  ModifyState(false);
3030 }
3031 
3033 {
3034  wxCommandEvent evt;
3035 
3036  GetControlToolBar()->OnFF(evt);
3037  ModifyState(false);
3038 }
3039 
3041 {
3042  OnCursorLeft( false, false );
3043 }
3044 
3046 {
3047  OnCursorRight( false, false );
3048 }
3049 
3051 {
3052  OnCursorLeft( true, false );
3053 }
3054 
3056 {
3057  OnCursorRight( true, false );
3058 }
3059 
3061 {
3062  Rewind(true);
3063  ModifyState(false);
3064 }
3065 
3067 {
3068  SkipEnd(true);
3069  ModifyState(false);
3070 }
3071 
3073 {
3074  OnMoveToLabel(true);
3075 }
3076 
3078 {
3079  OnMoveToLabel(false);
3080 }
3081 
3083 {
3084  // Find the number of label tracks, and ptr to last track found
3085  Track* track = nullptr;
3086  int nLabelTrack = 0;
3088  for (Track* t = iter.First(); t; t = iter.Next()) {
3089  nLabelTrack++;
3090  track = t;
3091  }
3092 
3093  if (nLabelTrack == 0 ) {
3094  mTrackPanel->MessageForScreenReader(_("no label track"));
3095  }
3096  else if (nLabelTrack > 1) { // find first label track, if any, starting at the focused track
3097  track = mTrackPanel->GetFocusedTrack();
3098  while (track && track->GetKind() != Track::Label) {
3099  track = mTracks->GetNext(track, true);
3100  if (!track) {
3101  mTrackPanel->MessageForScreenReader(_("no label track at or below focused track"));
3102  }
3103  }
3104  }
3105 
3106  // If there is a single label track, or there is a label track at or below the focused track
3107  if (track) {
3108  LabelTrack* lt = static_cast<LabelTrack*>(track);
3109  int i;
3110  if (next)
3111  i = lt->FindNextLabel(GetSelection());
3112  else
3113  i = lt->FindPrevLabel(GetSelection());
3114 
3115  if (i >= 0) {
3116  const LabelStruct* label = lt->GetLabel(i);
3117  if (IsAudioActive()) {
3118  OnPlayStop(*this); // stop
3120  RedrawProject();
3121  OnPlayStop(*this); // play
3122  }
3123  else {
3125  mTrackPanel->ScrollIntoView(GetViewInfo().selectedRegion.t0());
3126  RedrawProject();
3127  }
3128 
3129  wxString message;
3130  message.Printf(wxT("%s %d of %d"), label->title, i + 1, lt->GetNumLabels() );
3132  }
3133  else {
3134  mTrackPanel->MessageForScreenReader(_("no labels in label track"));
3135  }
3136  }
3137 }
3138 
3142 
3145 {
3146  TrackListIterator iter( GetTracks() );
3148  if( t == NULL ) // if there isn't one, focus on last
3149  {
3150  t = iter.Last();
3152  mTrackPanel->EnsureVisible( t );
3153  ModifyState(false);
3154  return;
3155  }
3156 
3157  Track* p = NULL;
3158  bool tSelected = false;
3159  bool pSelected = false;
3160  if( shift )
3161  {
3162  p = mTracks->GetPrev( t, true ); // Get previous track
3163  if( p == NULL ) // On first track
3164  {
3165  // JKC: wxBell() is probably for accessibility, so a blind
3166  // user knows they were at the top track.
3167  wxBell();
3169  {
3170  TrackListIterator iter( GetTracks() );
3171  p = iter.Last();
3172  }
3173  else
3174  {
3175  mTrackPanel->EnsureVisible( t );
3176  return;
3177  }
3178  }
3179  tSelected = t->GetSelected();
3180  if (p)
3181  pSelected = p->GetSelected();
3182  if( tSelected && pSelected )
3183  {
3185  ( *mTracks, *t, false, false, GetMixerBoard() );
3186  mTrackPanel->SetFocusedTrack( p ); // move focus to next track down
3187  mTrackPanel->EnsureVisible( p );
3188  ModifyState(false);
3189  return;
3190  }
3191  if( tSelected && !pSelected )
3192  {
3194  ( *mTracks, *p, true, false, GetMixerBoard() );
3195  mTrackPanel->SetFocusedTrack( p ); // move focus to next track down
3196  mTrackPanel->EnsureVisible( p );
3197  ModifyState(false);
3198  return;
3199  }
3200  if( !tSelected && pSelected )
3201  {
3203  ( *mTracks, *p, false, false, GetMixerBoard() );
3204  mTrackPanel->SetFocusedTrack( p ); // move focus to next track down
3205  mTrackPanel->EnsureVisible( p );
3206  ModifyState(false);
3207  return;
3208  }
3209  if( !tSelected && !pSelected )
3210  {
3212  ( *mTracks, *t, true, false, GetMixerBoard() );
3213  mTrackPanel->SetFocusedTrack( p ); // move focus to next track down
3214  mTrackPanel->EnsureVisible( p );
3215  ModifyState(false);
3216  return;
3217  }
3218  }
3219  else
3220  {
3221  p = mTracks->GetPrev( t, true ); // Get next track
3222  if( p == NULL ) // On last track so stay there?
3223  {
3224  wxBell();
3226  {
3227  TrackListIterator iter( GetTracks() );
3228  for( Track *d = iter.First(); d; d = iter.Next( true ) )
3229  {
3230  p = d;
3231  }
3232  mTrackPanel->SetFocusedTrack( p ); // Wrap to the first track
3233  mTrackPanel->EnsureVisible( p );
3234  ModifyState(false);
3235  return;
3236  }
3237  else
3238  {
3239  mTrackPanel->EnsureVisible( t );
3240  return;
3241  }
3242  }
3243  else
3244  {
3245  mTrackPanel->SetFocusedTrack( p ); // move focus to next track down
3246  mTrackPanel->EnsureVisible( p );
3247  ModifyState(false);
3248  return;
3249  }
3250  }
3251 }
3252 
3257 {
3258  Track *t;
3259  Track *n;
3260  TrackListIterator iter( GetTracks() );
3261  bool tSelected,nSelected;
3262 
3263  t = mTrackPanel->GetFocusedTrack(); // Get currently focused track
3264  if( t == NULL ) // if there isn't one, focus on first
3265  {
3266  t = iter.First();
3268  mTrackPanel->EnsureVisible( t );
3269  ModifyState(false);
3270  return;
3271  }
3272 
3273  if( shift )
3274  {
3275  n = mTracks->GetNext( t, true ); // Get next track
3276  if( n == NULL ) // On last track so stay there
3277  {
3278  wxBell();
3280  {
3281  TrackListIterator iter( GetTracks() );
3282  n = iter.First();
3283  }
3284  else
3285  {
3286  mTrackPanel->EnsureVisible( t );
3287  return;
3288  }
3289  }
3290  tSelected = t->GetSelected();
3291  nSelected = n->GetSelected();
3292  if( tSelected && nSelected )
3293  {
3295  ( *mTracks, *t, false, false, GetMixerBoard() );
3296  mTrackPanel->SetFocusedTrack( n ); // move focus to next track down
3297  mTrackPanel->EnsureVisible( n );
3298  ModifyState(false);
3299  return;
3300  }
3301  if( tSelected && !nSelected )
3302  {
3304  ( *mTracks, *n, true, false, GetMixerBoard() );
3305  mTrackPanel->SetFocusedTrack( n ); // move focus to next track down
3306  mTrackPanel->EnsureVisible( n );
3307  ModifyState(false);
3308  return;
3309  }
3310  if( !tSelected && nSelected )
3311  {
3313  ( *mTracks, *n, false, false, GetMixerBoard() );
3314  mTrackPanel->SetFocusedTrack( n ); // move focus to next track down
3315  mTrackPanel->EnsureVisible( n );
3316  ModifyState(false);
3317  return;
3318  }
3319  if( !tSelected && !nSelected )
3320  {
3322  ( *mTracks, *t, true, false, GetMixerBoard() );
3323  mTrackPanel->SetFocusedTrack( n ); // move focus to next track down
3324  mTrackPanel->EnsureVisible( n );
3325  ModifyState(false);
3326  return;
3327  }
3328  }
3329  else
3330  {
3331  n = mTracks->GetNext( t, true ); // Get next track
3332  if( n == NULL ) // On last track so stay there
3333  {
3334  wxBell();
3336  {
3337  TrackListIterator iter( GetTracks() );
3338  n = iter.First();
3339  mTrackPanel->SetFocusedTrack( n ); // Wrap to the first track
3340  mTrackPanel->EnsureVisible( n );
3341  ModifyState(false);
3342  return;
3343  }
3344  else
3345  {
3346  mTrackPanel->EnsureVisible( t );
3347  return;
3348  }
3349  }
3350  else
3351  {
3352  mTrackPanel->SetFocusedTrack( n ); // move focus to next track down
3353  mTrackPanel->EnsureVisible( n );
3354  ModifyState(false);
3355  return;
3356  }
3357  }
3358 }
3359 
3361 {
3362  OnPrevTrack( false );
3363 }
3364 
3366 {
3367  OnNextTrack( false );
3368 }
3369 
3371 {
3373  if (!t)
3374  return;
3375 
3376  TrackListIterator iter(GetTracks());
3377  Track *f = iter.First();
3378  if (t != f)
3379  {
3381  ModifyState(false);
3382  }
3384 }
3385 
3387 {
3389  if (!t)
3390  return;
3391 
3392  TrackListIterator iter(GetTracks());
3393  Track *l = iter.Last();
3394  if (t != l)
3395  {
3397  ModifyState(false);
3398  }
3400 }
3401 
3403 {
3404  OnPrevTrack( true );
3405 }
3406 
3408 {
3409  OnNextTrack( true );
3410 }
3411 
3412 #include "TrackPanelAx.h"
3414 {
3415  Track *t;
3416 
3417  t = mTrackPanel->GetFocusedTrack(); // Get currently focused track
3418  if (!t)
3419  return;
3420 
3422  ( *mTracks, *t, !t->GetSelected(), true, GetMixerBoard() );
3423  mTrackPanel->EnsureVisible( t );
3424  ModifyState(false);
3425 
3426  mTrackPanel->GetAx().Updated();
3427 
3428  return;
3429 }
3430 
3431 void AudacityProject::HandleListSelection(Track *t, bool shift, bool ctrl,
3432  bool modifyState)
3433 {
3435  ( *GetTracks(), mViewInfo, *t,
3436  shift, ctrl, IsSyncLocked(), GetMixerBoard() );
3437 
3438  if (! ctrl )
3440  Refresh(false);
3441  if (modifyState)
3442  ModifyState(true);
3443 }
3444 
3445 
3447 {
3448  auto evt = context.pEvt;
3449  bool bKeyUp = (evt) && evt->GetEventType() == wxEVT_KEY_UP;
3450  OnCursorLeft( false, false, bKeyUp );
3451 }
3452 
3454 {
3455  auto evt = context.pEvt;
3456  bool bKeyUp = (evt) && evt->GetEventType() == wxEVT_KEY_UP;
3457  OnCursorRight( false, false, bKeyUp );
3458 }
3459 
3461 {
3462  OnCursorMove( false, true, false );
3463 }
3464 
3466 {
3467  OnCursorMove( true, true, false );
3468 }
3469 
3471 {
3472  OnCursorMove( false, true, true );
3473 }
3474 
3476 {
3477  OnCursorMove( true, true, true );
3478 }
3479 
3481 {
3482  OnBoundaryMove( true, false);
3483 }
3484 
3486 {
3487  OnBoundaryMove( false, false);
3488 }
3489 
3491 {
3492  auto evt = context.pEvt;
3493  bool bKeyUp = (evt) && evt->GetEventType() == wxEVT_KEY_UP;
3494  OnCursorLeft( true, false, bKeyUp );
3495 }
3496 
3498 {
3499  auto evt = context.pEvt;
3500  bool bKeyUp = (evt) && evt->GetEventType() == wxEVT_KEY_UP;
3501  OnCursorRight( true, false, bKeyUp );
3502 }
3503 
3505 {
3506  auto evt = context.pEvt;
3507  bool bKeyUp = (evt) && evt->GetEventType() == wxEVT_KEY_UP;
3508  OnCursorRight( true, true, bKeyUp );
3509 }
3510 
3512 {
3513  auto evt = context.pEvt;
3514  bool bKeyUp = (evt) && evt->GetEventType() == wxEVT_KEY_UP;
3515  OnCursorLeft( true, true, bKeyUp );
3516 }
3517 
3518 #include "tracks/ui/TimeShiftHandle.h"
3519 
3520 // This function returns the amount moved. Possibly 0.0.
3522  ( ViewInfo &viewInfo, Track *track,
3523  TrackList &trackList, bool syncLocked, bool right )
3524 {
3525  // just dealing with clips in wave tracks for the moment. Note tracks??
3526  if (track && track->GetKind() == Track::Wave) {
3527  ClipMoveState state;
3528 
3529  auto wt = static_cast<WaveTrack*>(track);
3530  auto t0 = viewInfo.selectedRegion.t0();
3531 
3532  state.capturedClip = wt->GetClipAtTime( t0 );
3533  if (state.capturedClip == nullptr && track->GetLinked() && track->GetLink()) {
3534  // the clips in the right channel may be different from the left
3535  track = track->GetLink();
3536  wt = static_cast<WaveTrack*>(track);
3537  state.capturedClip = wt->GetClipAtTime(t0);
3538  }
3539  if (state.capturedClip == nullptr)
3540  return 0.0;
3541 
3542  state.capturedClipIsSelection =
3543  track->GetSelected() && !viewInfo.selectedRegion.isPoint();
3544  state.trackExclusions.clear();
3545 
3547  ( state, viewInfo, *track, trackList, syncLocked, t0 );
3548 
3549  auto desiredT0 = viewInfo.OffsetTimeByPixels( t0, ( right ? 1 : -1 ) );
3550  auto desiredSlideAmount = desiredT0 - t0;
3551 
3552  // set it to a sample point, and minimum of 1 sample point
3553  if (!right)
3554  desiredSlideAmount *= -1;
3555  double nSamples = rint(wt->GetRate() * desiredSlideAmount);
3556  nSamples = std::max(nSamples, 1.0);
3557  desiredSlideAmount = nSamples / wt->GetRate();
3558  if (!right)
3559  desiredSlideAmount *= -1;
3560 
3561  state.hSlideAmount = desiredSlideAmount;
3562  TimeShiftHandle::DoSlideHorizontal( state, trackList, *track );
3563 
3564  // update t0 and t1. There is the possibility that the updated
3565  // t0 may no longer be within the clip due to rounding errors,
3566  // so t0 is adjusted so that it is.
3567  double newT0 = t0 + state.hSlideAmount;
3568  if (newT0 < state.capturedClip->GetStartTime())
3569  newT0 = state.capturedClip->GetStartTime();
3570  if (newT0 > state.capturedClip->GetEndTime())
3571  newT0 = state.capturedClip->GetEndTime();
3572  double diff = viewInfo.selectedRegion.duration();
3573  viewInfo.selectedRegion.setTimes(newT0, newT0 + diff);
3574 
3575  return state.hSlideAmount;
3576  }
3577  return 0.0;
3578 }
3579 
3580 void AudacityProject::DoClipLeftOrRight(bool right, bool keyUp )
3581 {
3582  if (keyUp) {
3584  return;
3585  }
3586 
3587  auto &panel = *GetTrackPanel();
3588 
3589  auto amount = OnClipMove
3590  ( mViewInfo, panel.GetFocusedTrack(),
3591  *GetTracks(), IsSyncLocked(), right );
3592 
3593  panel.ScrollIntoView(mViewInfo.selectedRegion.t0());
3594  panel.Refresh(false);
3595 
3596  if (amount != 0.0) {
3597  wxString message = right? _("Time shifted clips to the right") :
3598  _("Time shifted clips to the left");
3599 
3600  // The following use of the UndoPush flags is so that both a single
3601  // keypress (keydown, then keyup), and holding down a key
3602  // (multiple keydowns followed by a keyup) result in a single
3603  // entry in Audacity's history dialog.
3604  PushState(message, _("Time-Shift"), UndoPush::CONSOLIDATE);
3605  }
3606 
3607  if ( amount == 0.0 )
3608  panel.MessageForScreenReader( _("clip not moved"));
3609 }
3610 
3612 {
3613  auto evt = context.pEvt;
3614  if (evt)
3615  DoClipLeftOrRight( false, evt->GetEventType() == wxEVT_KEY_UP );
3616  else { // called from menu, so simulate keydown and keyup
3617  DoClipLeftOrRight( false, false );
3618  DoClipLeftOrRight( false, true );
3619  }
3620 }
3621 
3623 {
3624  auto evt = context.pEvt;
3625  if (evt)
3626  DoClipLeftOrRight( true, evt->GetEventType() == wxEVT_KEY_UP );
3627  else { // called from menu, so simulate keydown and keyup
3628  DoClipLeftOrRight( true, false );
3629  DoClipLeftOrRight( true, true );
3630  }
3631 }
3632 
3633 //this pops up a dialog which allows the left selection to be set.
3634 //If playing/recording is happening, it sets the left selection at
3635 //the current play position.
3637 {
3638  bool bSelChanged = false;
3640  {
3641  double indicator = gAudioIO->GetStreamTime();
3642  mViewInfo.selectedRegion.setT0(indicator, false);
3643  bSelChanged = true;
3644  }
3645  else
3646  {
3647  wxString fmt = GetSelectionFormat();
3648  TimeDialog dlg(this, _("Set Left Selection Boundary"),
3649  fmt, mRate, mViewInfo.selectedRegion.t0(), _("Position"));
3650 
3651  if (wxID_OK == dlg.ShowModal())
3652  {
3653  //Get the value from the dialog
3655  std::max(0.0, dlg.GetTimeValue()), false);
3656  bSelChanged = true;
3657  }
3658  }
3659 
3660  if (bSelChanged)
3661  {
3662  ModifyState(false);
3663  mTrackPanel->Refresh(false);
3664  }
3665 }
3666 
3667 
3669 {
3670  bool bSelChanged = false;
3672  {
3673  double indicator = gAudioIO->GetStreamTime();
3674  mViewInfo.selectedRegion.setT1(indicator, false);
3675  bSelChanged = true;
3676  }
3677  else
3678  {
3679  wxString fmt = GetSelectionFormat();
3680  TimeDialog dlg(this, _("Set Right Selection Boundary"),
3681  fmt, mRate, mViewInfo.selectedRegion.t1(), _("Position"));
3682 
3683  if (wxID_OK == dlg.ShowModal())
3684  {
3685  //Get the value from the dialog
3687  std::max(0.0, dlg.GetTimeValue()), false);
3688  bSelChanged = true;
3689  }
3690  }
3691 
3692  if (bSelChanged)
3693  {
3694  ModifyState(false);
3695  mTrackPanel->Refresh(false);
3696  }
3697 }
3698 
3700 {
3701  // Focus won't take in a dock unless at least one descendant window
3702  // accepts focus. Tell controls to take focus for the duration of this
3703  // function, only. Outside of this, they won't steal the focus when
3704  // clicked.
3705  auto temp1 = AButton::TemporarilyAllowFocus();
3706  auto temp2 = ASlider::TemporarilyAllowFocus();
3707  auto temp3 = Meter::TemporarilyAllowFocus();
3708 
3709 
3710  // Define the set of windows we rotate among.
3711  static const unsigned rotationSize = 3u;
3712 
3713  wxWindow *const begin [rotationSize] = {
3714  GetTopPanel(),
3715  GetTrackPanel(),
3716  mToolManager->GetBotDock(),
3717  };
3718 
3719  const auto end = begin + rotationSize;
3720 
3721  // helper functions
3722  auto IndexOf = [&](wxWindow *pWindow) {
3723  return std::find(begin, end, pWindow) - begin;
3724  };
3725 
3726  auto FindAncestor = [&]() {
3727  wxWindow *pWindow = wxWindow::FindFocus();
3728  unsigned index = rotationSize;
3729  while ( pWindow &&
3730  (rotationSize == (index = IndexOf(pWindow) ) ) )
3731  pWindow = pWindow->GetParent();
3732  return index;
3733  };
3734 
3735  const auto idx = FindAncestor();
3736  if (idx == rotationSize)
3737  return;
3738 
3739  auto idx2 = idx;
3740  auto increment = (forward ? 1 : rotationSize - 1);
3741 
3742  while( idx != (idx2 = (idx2 + increment) % rotationSize) ) {
3743  wxWindow *toFocus = begin[idx2];
3744  bool bIsAnEmptyDock=false;
3745  if( idx2 != 1 )
3746  bIsAnEmptyDock = ((idx2==0)?mToolManager->GetTopDock() : mToolManager->GetBotDock())->
3747  GetChildren().GetCount() < 1;
3748 
3749  // Skip docks that are empty (Bug 1564).
3750  if( !bIsAnEmptyDock ){
3751  toFocus->SetFocus();
3752  if ( FindAncestor() == idx2 )
3753  // The focus took!
3754  break;
3755  }
3756  }
3757 }
3758 
3760 {
3761  NextOrPrevFrame(true);
3762 }
3763 
3765 {
3766  NextOrPrevFrame(false);
3767 }
3768 
3770 {
3771  wxWindow *w = wxGetTopLevelParent(wxWindow::FindFocus());
3772  const auto & list = GetChildren();
3773  auto iter = list.begin(), end = list.end();
3774 
3775  // If the project window has the current focus, start the search with the first child
3776  if (w == this)
3777  {
3778  }
3779  // Otherwise start the search with the current window's next sibling
3780  else
3781  {
3782  // Find the window in this projects children. If the window with the
3783  // focus isn't a child of this project (like when a dialog is created
3784  // without specifying a parent), then we'll get back NULL here.
3785  while (iter != end && *iter != w)
3786  ++iter;
3787  if (iter != end)
3788  ++iter;
3789  }
3790 
3791  // Search for the next toplevel window
3792  for (; iter != end; ++iter)
3793  {
3794  // If it's a toplevel, visible (we have hidden windows) and is enabled,
3795  // then we're done. The IsEnabled() prevents us from moving away from
3796  // a modal dialog because all other toplevel windows will be disabled.
3797  w = *iter;
3798  if (w->IsTopLevel() && w->IsShown() && w->IsEnabled())
3799  {
3800  break;
3801  }
3802  }
3803 
3804  // Ran out of siblings, so make the current project active
3805  if ((iter == end) && IsEnabled())
3806  {
3807  w = this;
3808  }
3809 
3810  // And make sure it's on top (only for floating windows...project window will not raise)
3811  // (Really only works on Windows)
3812  w->Raise();
3813 
3814 
3815 #if defined(__WXMAC__) || defined(__WXGTK__)
3816  // bug 868
3817  // Simulate a TAB key press before continuing, else the cycle of
3818  // navigation among top level windows stops because the keystrokes don't
3819  // go to the CommandManager.
3820  if (dynamic_cast<wxDialog*>(w)) {
3821  w->SetFocus();
3822  }
3823 #endif
3824 }
3825 
3827 {
3828  wxWindow *w = wxGetTopLevelParent(wxWindow::FindFocus());
3829  const auto & list = GetChildren();
3830  auto iter = list.rbegin(), end = list.rend();
3831 
3832  // If the project window has the current focus, start the search with the last child
3833  if (w == this)
3834  {
3835  }
3836  // Otherwise start the search with the current window's previous sibling
3837  else
3838  {
3839  while (iter != end && *iter != w)
3840  ++iter;
3841  if (iter != end)
3842  ++iter;
3843  }
3844 
3845  // Search for the previous toplevel window
3846  for (; iter != end; ++iter)
3847  {
3848  // If it's a toplevel and is visible (we have come hidden windows), then we're done
3849  w = *iter;
3850  if (w->IsTopLevel() && w->IsShown() && IsEnabled())
3851  {
3852  break;
3853  }
3854  }
3855 
3856  // Ran out of siblings, so make the current project active
3857  if ((iter == end) && IsEnabled())
3858  {
3859  w = this;
3860  }
3861 
3862  // And make sure it's on top (only for floating windows...project window will not raise)
3863  // (Really only works on Windows)
3864  w->Raise();
3865 
3866 
3867 #if defined(__WXMAC__) || defined(__WXGTK__)
3868  // bug 868
3869  // Simulate a TAB key press before continuing, else the cycle of
3870  // navigation among top level windows stops because the keystrokes don't
3871  // go to the CommandManager.
3872  if (dynamic_cast<wxDialog*>(w)) {
3873  w->SetFocus();
3874  }
3875 #endif
3876 }
3877 
3881 {
3882  Track *const track = mTrackPanel->GetFocusedTrack();
3883  if (!track || (track->GetKind() != Track::Wave)) {
3884  return;
3885  }
3886  const auto wt = static_cast<WaveTrack*>(track);
3887 
3888  LWSlider *slider = mTrackPanel->PanSlider(wt);
3889  if (slider->ShowDialog()) {
3890  SetTrackPan(wt, slider);
3891  }
3892 }
3893 
3895 {
3896  Track *const track = mTrackPanel->GetFocusedTrack();
3897  if (!track || (track->GetKind() != Track::Wave)) {
3898  return;
3899  }
3900  const auto wt = static_cast<WaveTrack*>(track);
3901 
3902  LWSlider *slider = mTrackPanel->PanSlider(wt);
3903  slider->Decrease(1);
3904  SetTrackPan(wt, slider);
3905 }
3906 
3908 {
3909  Track *const track = mTrackPanel->GetFocusedTrack();
3910  if (!track || (track->GetKind() != Track::Wave)) {
3911  return;
3912  }
3913  const auto wt = static_cast<WaveTrack*>(track);
3914 
3915  LWSlider *slider = mTrackPanel->PanSlider(wt);
3916  slider->Increase(1);
3917  SetTrackPan(wt, slider);
3918 }
3919 
3921 {
3923  Track *const track = mTrackPanel->GetFocusedTrack();
3924  if (!track || (track->GetKind() != Track::Wave)) {
3925  return;
3926  }
3927  const auto wt = static_cast<WaveTrack*>(track);
3928 
3929  LWSlider *slider = mTrackPanel->GainSlider(wt);
3930  if (slider->ShowDialog()) {
3931  SetTrackGain(wt, slider);
3932  }
3933 }
3934 
3936 {
3937  Track *const track = mTrackPanel->GetFocusedTrack();
3938  if (!track || (track->GetKind() != Track::Wave)) {
3939  return;
3940  }
3941  const auto wt = static_cast<WaveTrack*>(track);
3942 
3943  LWSlider *slider = mTrackPanel->GainSlider(wt);
3944  slider->Increase(1);
3945  SetTrackGain(wt, slider);
3946 }
3947 
3949 {
3950  Track *const track = mTrackPanel->GetFocusedTrack();
3951  if (!track || (track->GetKind() != Track::Wave)) {
3952  return;
3953  }
3954  const auto wt = static_cast<WaveTrack*>(track);
3955 
3956  LWSlider *slider = mTrackPanel->GainSlider(wt);
3957  slider->Decrease(1);
3958  SetTrackGain(wt, slider);
3959 }
3960 
3962 {
3964 }
3965 
3967 {
3968  Track *t = NULL;
3969  if (!t) {
3971  if (!dynamic_cast<PlayableTrack*>(t))
3972  return;
3973  }
3974  DoTrackMute(t, false);
3975 }
3976 
3978 {
3979  Track *t = NULL;
3980  if (!t)
3981  {
3983  if (!dynamic_cast<PlayableTrack*>(t))
3984  return;
3985  }
3986  DoTrackSolo(t, false);
3987 }
3988 
3990 {
3992  if (!t)
3993  return;
3994 
3995  if (IsAudioActive())
3996  {
3997  this->TP_DisplayStatusMessage(_("Can't delete track with active audio"));
3998  wxBell();
3999  return;
4000  }
4001 
4002  RemoveTrack(t);
4003 
4005  GetTrackPanel()->Refresh(false);
4006 }
4007 
4009 {
4010  Track *const focusedTrack = mTrackPanel->GetFocusedTrack();
4011  if (mTracks->CanMoveUp(focusedTrack)) {
4012  MoveTrack(focusedTrack, OnMoveUpID);
4013  mTrackPanel->Refresh(false);
4014  }
4015 }
4016 
4018 {
4019  Track *const focusedTrack = mTrackPanel->GetFocusedTrack();
4020  if (mTracks->CanMoveDown(focusedTrack)) {
4021  MoveTrack(focusedTrack, OnMoveDownID);
4022  mTrackPanel->Refresh(false);
4023  }
4024 }
4025 
4027 {
4028  Track *const focusedTrack = mTrackPanel->GetFocusedTrack();
4029  if (mTracks->CanMoveUp(focusedTrack)) {
4030  MoveTrack(focusedTrack, OnMoveTopID);
4031  mTrackPanel->Refresh(false);
4032  }
4033 }
4034 
4036 {
4037  Track *const focusedTrack = mTrackPanel->GetFocusedTrack();
4038  if (mTracks->CanMoveDown(focusedTrack)) {
4039  MoveTrack(focusedTrack, OnMoveBottomID);
4040  mTrackPanel->Refresh(false);
4041  }
4042 }
4043 
4045 
4047 {
4048  wxString longDesc, shortDesc;
4049 
4050  auto pt = dynamic_cast<PlayableTrack*>(target);
4051  switch (choice)
4052  {
4053  case OnMoveTopID:
4054  /* i18n-hint: Past tense of 'to move', as in 'moved audio track up'.*/
4055  longDesc = _("Moved '%s' to Top");
4056  shortDesc = _("Move Track to Top");
4057 
4058  while (mTracks->CanMoveUp(target)) {
4059  if (mTracks->Move(target, true)) {
4060  MixerBoard* pMixerBoard = this->GetMixerBoard(); // Update mixer board.
4061  if (pMixerBoard && pt)
4062  pMixerBoard->MoveTrackCluster(pt, true);
4063  }
4064  }
4065  break;
4066  case OnMoveBottomID:
4067  /* i18n-hint: Past tense of 'to move', as in 'moved audio track up'.*/
4068  longDesc = _("Moved '%s' to Bottom");
4069  shortDesc = _("Move Track to Bottom");
4070 
4071  while (mTracks->CanMoveDown(target)) {
4072  if (mTracks->Move(target, false)) {
4073  MixerBoard* pMixerBoard = this->GetMixerBoard(); // Update mixer board.
4074  if (pMixerBoard && pt)
4075  pMixerBoard->MoveTrackCluster(pt, false);
4076  }
4077  }
4078  break;
4079  default:
4080  bool bUp = (OnMoveUpID == choice);
4081 
4082  if (mTracks->Move(target, bUp)) {
4083  MixerBoard* pMixerBoard = this->GetMixerBoard();
4084  if (pMixerBoard && pt)
4085  pMixerBoard->MoveTrackCluster(pt, bUp);
4086  }
4087  longDesc =
4088  /* i18n-hint: Past tense of 'to move', as in 'moved audio track up'.*/
4089  bUp? _("Moved '%s' Up")
4090  : _("Moved '%s' Down");
4091  shortDesc =
4092  /* i18n-hint: Past tense of 'to move', as in 'moved audio track up'.*/
4093  bUp? _("Move Track Up")
4094  : _("Move Track Down");
4095 
4096  }
4097 
4098  longDesc = longDesc.Format(target->GetName());
4099 
4100  PushState(longDesc, shortDesc);
4101  GetTrackPanel()->Refresh(false);
4102 }
4103 
4105 {
4107  if (tb) {
4108  tb->ShowInputDialog();
4109  }
4110 }
4111 
4113 {
4115  if (tb) {
4116  tb->ShowOutputDialog();
4117  }
4118 }
4119 
4121 {
4123  if (tb) {
4124  tb->ShowHostDialog();
4125  }
4126 }
4127 
4129 {
4131  if (tb) {
4132  tb->ShowChannelsDialog();
4133  }
4134 }
4135 
4137 {
4138  MixerToolBar *tb = GetMixerToolBar();
4139  if (tb) {
4140  tb->ShowOutputGainDialog();
4141  }
4142 }
4143 
4145 {
4146  MixerToolBar *tb = GetMixerToolBar();
4147  if (tb) {
4148  tb->ShowInputGainDialog();
4149  }
4150 }
4151 
4153 {
4154  MixerToolBar *tb = GetMixerToolBar();
4155  if (tb) {
4156  tb->AdjustOutputGain(1);
4157  }
4158 }
4159 
4161 {
4162  MixerToolBar *tb = GetMixerToolBar();
4163  if (tb) {
4164  tb->AdjustOutputGain(-1);
4165  }
4166 }
4167 
4169 {
4170  MixerToolBar *tb = GetMixerToolBar();
4171  if (tb) {
4172  tb->AdjustInputGain(1);
4173  }
4174 }
4175 
4177 {
4178  MixerToolBar *tb = GetMixerToolBar();
4179  if (tb) {
4180  tb->AdjustInputGain(-1);
4181  }
4182 }
4183 
4185 {
4187  if (tb) {
4188  tb->PlayAtSpeed(false, false);
4189  }
4190 }
4191 
4193 {
4195  if (tb) {
4196  tb->PlayAtSpeed(true, false);
4197  }
4198 }
4199 
4201 {
4203  if (tb) {
4204  tb->PlayAtSpeed(false, true);
4205  }
4206 }
4207 
4209 {
4211  if (tb) {
4212  tb->ShowPlaySpeedDialog();
4213  }
4214 }
4215 
4217 {
4219  if (tb) {
4220  tb->AdjustPlaySpeed(0.1f);
4221  }
4222 }
4223 
4225 {
4227  if (tb) {
4228  tb->AdjustPlaySpeed(-0.1f);
4229  }
4230 }
4231 
4233 {
4234  // Window is 1/100th of a second.
4235  auto windowSize = size_t(std::max(1.0, GetRate() / 100));
4236  Floats dist{ windowSize, true };
4237 
4238  TrackListIterator iter(GetTracks());
4239  Track *track = iter.First();
4240  while (track) {
4241  if (!track->GetSelected() || track->GetKind() != (Track::Wave)) {
4242  track = iter.Next();
4243  continue;
4244  }
4245  WaveTrack *one = (WaveTrack *)track;
4246  auto oneWindowSize = size_t(std::max(1.0, one->GetRate() / 100));
4247  Floats oneDist{ oneWindowSize };
4248  auto s = one->TimeToLongSamples(t0);
4249  // fillTwo to ensure that missing values are treated as 2, and hence do not
4250  // get used as zero crossings.
4251  one->Get((samplePtr)oneDist.get(), floatSample,
4252  s - (int)oneWindowSize/2, oneWindowSize, fillTwo);
4253 
4254  // Start by penalizing downward motion. We prefer upward
4255  // zero crossings.
4256  if (oneDist[1] - oneDist[0] < 0)
4257  oneDist[0] = oneDist[0]*6 + (oneDist[0] > 0 ? 0.3 : -0.3);
4258  for(size_t i=1; i<oneWindowSize; i++)
4259  if (oneDist[i] - oneDist[i-1] < 0)
4260  oneDist[i] = oneDist[i]*6 + (oneDist[i] > 0 ? 0.3 : -0.3);
4261 
4262  // Taking the absolute value -- apply a tiny LPF so square waves work.
4263  float newVal, oldVal = oneDist[0];
4264  oneDist[0] = fabs(.75 * oneDist[0] + .25 * oneDist[1]);
4265  for(size_t i=1; i + 1 < oneWindowSize; i++)
4266  {
4267  newVal = fabs(.25 * oldVal + .5 * oneDist[i] + .25 * oneDist[i+1]);
4268  oldVal = oneDist[i];
4269  oneDist[i] = newVal;
4270  }
4271  oneDist[oneWindowSize-1] = fabs(.25 * oldVal +
4272  .75 * oneDist[oneWindowSize-1]);
4273 
4274  // TODO: The mixed rate zero crossing code is broken,
4275  // if oneWindowSize > windowSize we'll miss out some
4276  // samples - so they will still be zero, so we'll use them.
4277  for(size_t i = 0; i < windowSize; i++) {
4278  size_t j;
4279  if (windowSize != oneWindowSize)
4280  j = i * (oneWindowSize-1) / (windowSize-1);
4281  else
4282  j = i;
4283 
4284  dist[i] += oneDist[j];
4285  // Apply a small penalty for distance from the original endpoint
4286  dist[i] += 0.1 * (abs(int(i) - int(windowSize/2))) / float(windowSize/2);
4287  }
4288 
4289  track = iter.Next();
4290  }
4291 
4292  // Find minimum
4293  int argmin = 0;
4294  float min = 3.0;
4295  for(size_t i=0; i<windowSize; i++) {
4296  if (dist[i] < min) {
4297  argmin = i;
4298  min = dist[i];
4299  }
4300  }
4301 
4302  return t0 + (argmin - (int)windowSize/2)/GetRate();
4303 }
4304 
4306 {
4307  const double t0 = NearestZeroCrossing(mViewInfo.selectedRegion.t0());
4310  else {
4311  const double t1 = NearestZeroCrossing(mViewInfo.selectedRegion.t1());
4313  }
4314 
4315  ModifyState(false);
4316 
4317  mTrackPanel->Refresh(false);
4318 }
4319 
4320 //
4321 // Effect Menus
4322 //
4323 
4328 bool AudacityProject::DoEffect(const PluginID & ID, int flags)
4329 {
4330  const PluginDescriptor *plug = PluginManager::Get().GetPlugin(ID);
4331  if (!plug)
4332  return false;
4333 
4334  EffectType type = plug->GetEffectType();
4335 
4336  // Make sure there's no activity since the effect is about to be applied
4337  // to the project's tracks. Mainly for Apply during RTP, but also used
4338  // for batch commands
4339  if (flags & OnEffectFlags::kConfigured)
4340  {
4341  OnStop(*this);
4342  SelectAllIfNone();
4343  }
4344 
4346 
4347  auto nTracksOriginally = GetTrackCount();
4348  TrackListIterator iter(GetTracks());
4349  Track *t = iter.First();
4350  WaveTrack *newTrack{};
4351  wxWindow *focus = wxWindow::FindFocus();
4352 
4353  bool success = false;
4354  auto cleanup = finally( [&] {
4355 
4356  if (!success) {
4357  if (newTrack) {
4358  mTracks->Remove(newTrack);
4359  mTrackPanel->Refresh(false);
4360  }
4361 
4362  // For now, we're limiting realtime preview to a single effect, so
4363  // make sure the menus reflect that fact that one may have just been
4364  // opened.
4365  UpdateMenus(false);
4366  }
4367 
4368  } );
4369 
4370  //double prevEndTime = mTracks->GetEndTime();
4371  int count = 0;
4372  bool clean = true;
4373  while (t) {
4374  if (t->GetSelected() && t->GetKind() == (Track::Wave)) {
4375  if (t->GetEndTime() != 0.0) clean = false;
4376  count++;
4377  }
4378  t = iter.Next();
4379  }
4380 
4381  if (count == 0) {
4382  // No tracks were selected...
4383  if (type == EffectTypeGenerate) {
4384  // Create a NEW track for the generated audio...
4385  newTrack = static_cast<WaveTrack*>(mTracks->Add(mTrackFactory->NewWaveTrack()));
4386  newTrack->SetSelected(true);
4387  }
4388  }
4389 
4391 
4392  success = em.DoEffect(ID, this, mRate,
4395  (flags & OnEffectFlags::kConfigured) == 0);
4396 
4397  if (!success)
4398  return false;
4399 
4400  if (em.GetSkipStateFlag())
4401  flags = flags | OnEffectFlags::kSkipState;
4402 
4403  if (!(flags & OnEffectFlags::kSkipState))
4404  {
4405  wxString shortDesc = em.GetEffectName(ID);
4406  wxString longDesc = em.GetEffectDescription(ID);
4407  PushState(longDesc, shortDesc);
4408  }
4409 
4410  if (!(flags & OnEffectFlags::kDontRepeatLast))
4411  {
4412  // Only remember a successful effect, don't remember insert,
4413  // or analyze effects.
4414  if (type == EffectTypeProcess) {
4415  wxString shortDesc = em.GetEffectName(ID);
4416  mLastEffect = ID;
4417  wxString lastEffectDesc;
4418  /* i18n-hint: %s will be the name of the effect which will be
4419  * repeated if this menu item is chosen */
4420  lastEffectDesc.Printf(_("Repeat %s"), shortDesc);
4421  mCommandManager.Modify(wxT("RepeatLastEffect"), lastEffectDesc);
4422  }
4423  }
4424 
4425  //STM:
4426  //The following automatically re-zooms after sound was generated.
4427  // IMO, it was disorienting, removing to try out without re-fitting
4428  //mchinen:12/14/08 reapplying for generate effects
4429  if (type == EffectTypeGenerate)
4430  {
4431  if (count == 0 || (clean && mViewInfo.selectedRegion.t0() == 0.0))
4432  OnZoomFit(*this);
4433  // mTrackPanel->Refresh(false);
4434  }
4435  RedrawProject();
4436  if (focus != nullptr) {
4437  focus->SetFocus();
4438  }
4439 
4440  // A fix for Bug 63
4441  // New tracks added? Scroll them into view so that user sees them.
4442  // Don't care what track type. An analyser might just have added a
4443  // Label track and we want to see it.
4444  if( GetTrackCount() > nTracksOriginally ){
4445  // 0.0 is min scroll position, 1.0 is max scroll position.
4446  GetTrackPanel()->VerticalScroll( 1.0 );
4447  } else {
4449  mTrackPanel->Refresh(false);
4450  }
4451 
4452  return true;
4453 }
4454 
4456 {
4457  DoEffect(context.parameter, 0);
4458 }
4459 
4461 {
4462  if (!mLastEffect.IsEmpty())
4463  {
4465  }
4466 }
4467 
4468 
4470  for( size_t i = 0; i < gAudacityProjects.size(); i++ ) {
4471  AudacityProject *p = gAudacityProjects[i].get();
4472 
4473  p->RebuildMenuBar();
4474 #if defined(__WXGTK__)
4475  // Workaround for:
4476  //
4477  // http://bugzilla.audacityteam.org/show_bug.cgi?id=458
4478  //
4479  // This workaround should be removed when Audacity updates to wxWidgets 3.x which has a fix.
4480  wxRect r = p->GetRect();
4481  p->SetSize(wxSize(1,1));
4482  p->SetSize(r.GetSize());
4483 #endif
4484  }
4485 }
4486 
4488 {
4489  if (PluginManager::Get().ShowManager(this, type))
4491 }
4492 
4494 {
4495  OnManagePluginsMenu(EffectTypeGenerate);
4496 }
4497 
4499 {
4500  OnManagePluginsMenu(EffectTypeProcess);
4501 }
4502 
4504 {
4505  OnManagePluginsMenu(EffectTypeAnalyze);
4506 }
4507 
4508 
4509 
4511 {
4512  DoEffect(EffectManager::Get().GetEffectByIdentifier(wxT("StereoToMono")),
4514 }
4515 
4516 //
4517 // File Menu
4518 //
4519 
4521 {
4523 }
4524 
4526 {
4527  OpenFiles(this);
4528 }
4529 
4531 {
4532  mMenuClose = true;
4533  Close();
4534 }
4535 
4537 {
4538  Save();
4539 }
4540 
4542 {
4543  SaveAs();
4544 }
4545 
4546 #ifdef USE_LIBVORBIS
4547  void AudacityProject::OnSaveCompressed(const CommandContext &)
4548  {
4549  SaveAs(true);
4550  }
4551 #endif
4552 
4554 {
4555  ShowDependencyDialogIfNeeded(this, false);
4556 }
4557 
4559 {
4560  QuitAudacity();
4561 }
4562 
4564 {
4565  Track *t;
4566  int numLabelTracks = 0;
4567 
4568  TrackListIterator iter(GetTracks());
4569 
4570  /* i18n-hint: filename containing exported text from label tracks */
4571  wxString fName = _("labels.txt");
4572  t = iter.First();
4573  while (t) {
4574  if (t->GetKind() == Track::Label)
4575  {
4576  numLabelTracks++;
4577  fName = t->GetName();
4578  }
4579  t = iter.Next();
4580  }
4581 
4582  if (numLabelTracks == 0) {
4583  AudacityMessageBox(_("There are no label tracks to export."));
4584  return;
4585  }
4586 
4588  _("Export Labels As:"),
4589  wxEmptyString,
4590  fName,
4591  wxT("txt"),
4592  wxT("*.txt"),
4593  wxFD_SAVE | wxFD_OVERWRITE_PROMPT | wxRESIZE_BORDER,
4594  this);
4595 
4596  if (fName == wxT(""))
4597  return;
4598 
4599  // Move existing files out of the way. Otherwise wxTextFile will
4600  // append to (rather than replace) the current file.
4601 
4602  if (wxFileExists(fName)) {
4603 #ifdef __WXGTK__
4604  wxString safetyFileName = fName + wxT("~");
4605 #else
4606  wxString safetyFileName = fName + wxT(".bak");
4607 #endif
4608 
4609  if (wxFileExists(safetyFileName))
4610  wxRemoveFile(safetyFileName);
4611 
4612  wxRename(fName, safetyFileName);
4613  }
4614 
4615  wxTextFile f(fName);
4616  f.Create();
4617  f.Open();
4618  if (!f.IsOpened()) {
4619  AudacityMessageBox( wxString::Format(
4620  _("Couldn't write to file: %s"), fName ) );
4621  return;
4622  }
4623 
4624  t = iter.First();
4625  while (t) {
4626  if (t->GetKind() == Track::Label)
4627  ((LabelTrack *) t)->Export(f);
4628 
4629  t = iter.Next();
4630  }
4631 
4632  f.Write();
4633  f.Close();
4634 }
4635 
4636 
4637 #ifdef USE_MIDI
4639  TrackListIterator iter(GetTracks());
4640  Track *t = iter.First();
4641  int numNoteTracksSelected = 0;
4642  NoteTrack *nt = NULL;
4643 
4644  // Iterate through once to make sure that there is
4645  // exactly one NoteTrack selected.
4646  while (t) {
4647  if (t->GetSelected()) {
4648  if(t->GetKind() == Track::Note) {
4649  numNoteTracksSelected++;
4650  nt = (NoteTrack *) t;
4651  }
4652  }
4653  t = iter.Next();
4654  }
4655 
4656  if(numNoteTracksSelected > 1) {
4658  "Please select only one Note Track at a time."));
4659  return;
4660  }
4661  else if(numNoteTracksSelected < 1) {
4663  "Please select a Note Track."));
4664  return;
4665  }
4666 
4667  wxASSERT(nt);
4668  if (!nt)
4669  return;
4670 
4671  while(true){
4672 
4673  wxString fName = wxT("");
4674 
4676  _("Export MIDI As:"),
4677  wxEmptyString,
4678  fName,
4679  wxT(".mid|.gro"),
4680  _("MIDI file (*.mid)|*.mid|Allegro file (*.gro)|*.gro"),
4681  wxFD_SAVE | wxFD_OVERWRITE_PROMPT | wxRESIZE_BORDER,
4682  this);
4683 
4684  if (fName == wxT(""))
4685  return;
4686 
4687  if(!fName.Contains(wxT("."))) {
4688  fName = fName + wxT(".mid");
4689  }
4690 
4691  // Move existing files out of the way. Otherwise wxTextFile will
4692  // append to (rather than replace) the current file.
4693 
4694  if (wxFileExists(fName)) {
4695 #ifdef __WXGTK__
4696  wxString safetyFileName = fName + wxT("~");
4697 #else
4698  wxString safetyFileName = fName + wxT(".bak");
4699 #endif
4700 
4701  if (wxFileExists(safetyFileName))
4702  wxRemoveFile(safetyFileName);
4703 
4704  wxRename(fName, safetyFileName);
4705  }
4706 
4707  if(fName.EndsWith(wxT(".mid")) || fName.EndsWith(wxT(".midi"))) {
4708  nt->ExportMIDI(fName);
4709  } else if(fName.EndsWith(wxT(".gro"))) {
4710  nt->ExportAllegro(fName);
4711  } else {
4712  wxString msg = _("You have selected a filename with an unrecognized file extension.\nDo you want to continue?");
4713  wxString title = _("Export MIDI");
4714  int id = AudacityMessageBox(msg, title, wxYES_NO);
4715  if (id == wxNO) {
4716  continue;
4717  } else if (id == wxYES) {
4718  nt->ExportMIDI(fName);
4719  }
4720  }
4721  break;
4722  }
4723 }
4724 #endif // USE_MIDI
4725 
4726 
4727 void AudacityProject::OnExport(const wxString & Format )
4728 {
4729  Exporter e;
4730  e.SetDefaultFormat( Format );
4731 
4733  e.Process(this, false, 0.0, mTracks->GetEndTime());
4734 }
4735 
4740 
4742 {
4743  Exporter e;
4744 
4746  e.SetFileDialogTitle( _("Export Selected Audio") );
4747  e.Process(this, true, mViewInfo.selectedRegion.t0(),
4749 }
4750 
4752 {
4753  ExportMultiple em(this);
4754 
4756  em.ShowModal();
4757 }
4758 
4760 {
4761  GlobalPrefsDialog dialog(this /* parent */ );
4762 
4763  if( ScreenshotCommand::MayCapture( &dialog ) )
4764  return;
4765 
4766  if (!dialog.ShowModal()) {
4767  // Canceled
4768  return;
4769  }
4770 
4771  // LL: Moved from PrefsDialog since wxWidgets on OSX can't deal with
4772  // rebuilding the menus while the PrefsDialog is still in the modal
4773  // state.
4774  for (size_t i = 0; i < gAudacityProjects.size(); i++) {
4775  AudacityProject *p = gAudacityProjects[i].get();
4776 
4777  p->RebuildMenuBar();
4778  p->RebuildOtherMenus();
4779 // TODO: The comment below suggests this workaround is obsolete.
4780 #if defined(__WXGTK__)
4781  // Workaround for:
4782  //
4783  // http://bugzilla.audacityteam.org/show_bug.cgi?id=458
4784  //
4785  // This workaround should be removed when Audacity updates to wxWidgets 3.x which has a fix.
4786  wxRect r = p->GetRect();
4787  p->SetSize(wxSize(1,1));
4788  p->SetSize(r.GetSize());
4789 #endif
4790  }
4791 }
4792 
4794 {
4795  HandlePageSetup(this);
4796 }
4797 
4799 {
4800  HandlePrint(this, GetName(), GetTracks());
4801 }
4802 
4803 //
4804 // Edit Menu
4805 //
4806 
4808 {
4809  if (!GetUndoManager()->UndoAvailable()) {
4810  AudacityMessageBox(_("Nothing to undo"));
4811  return;
4812  }
4813 
4814  // can't undo while dragging
4815  if (mTrackPanel->IsMouseCaptured()) {
4816  return;
4817  }
4818 
4820  PopState(state);
4821 
4824 
4825  RedrawProject();
4826 
4827  if (mHistoryWindow)
4829 
4830  if (mMixerBoard)
4831  // Mixer board may need to change for selection state and pan/gain
4832  mMixerBoard->Refresh();
4833 
4835 }
4836 
4838 {
4839  if (!GetUndoManager()->RedoAvailable()) {
4840  AudacityMessageBox(_("Nothing to redo"));
4841  return;
4842  }
4843  // Can't redo whilst dragging
4844  if (mTrackPanel->IsMouseCaptured()) {
4845  return;
4846  }
4847 
4849  PopState(state);
4850 
4853 
4854  RedrawProject();
4855 
4856  if (mHistoryWindow)
4858 
4859  if (mMixerBoard)
4860  // Mixer board may need to change for selection state and pan/gain
4861  mMixerBoard->Refresh();
4862 
4864 }
4865 
4867  (const Track *n, Track *dest)
4868 {
4869  if (dest) {
4870  dest->SetChannel(n->GetChannel());
4871  dest->SetLinked(n->GetLinked());
4872  dest->SetName(n->GetName());
4873  }
4874 }
4875 
4877  (const Track *n, Track::Holder &&dest, TrackList &list)
4878 {
4879  FinishCopy( n, dest.get() );
4880  if (dest)
4881  list.Add(std::move(dest));
4882 }
4883 
4885 {
4886  TrackListIterator iter(GetTracks());
4887  Track *n = iter.First();
4888 
4889  // This doesn't handle cutting labels, it handles
4890  // cutting the _text_ inside of labels, i.e. if you're
4891  // in the middle of editing the label text and select "Cut".
4892 
4893  while (n) {
4894  if (n->GetSelected()) {
4895  if (n->GetKind() == Track::Label) {
4896  if (((LabelTrack *)n)->CutSelectedText()) {
4897  mTrackPanel->Refresh(false);
4898  return;
4899  }
4900  }
4901  }
4902  n = iter.Next();
4903  }
4904 
4905  ClearClipboard();
4906 
4907  auto pNewClipboard = TrackList::Create();
4908  auto &newClipboard = *pNewClipboard;
4909 
4910  n = iter.First();
4911  while (n) {
4912  if (n->GetSelected()) {
4913  Track::Holder dest;
4914 #if defined(USE_MIDI)
4915  if (n->GetKind() == Track::Note)
4916  // Since portsmf has a built-in cut operator, we use that instead
4917  dest = n->Cut(mViewInfo.selectedRegion.t0(),
4919  else
4920 #endif
4921  dest = n->Copy(mViewInfo.selectedRegion.t0(),
4923 
4924  FinishCopy(n, std::move(dest), newClipboard);
4925  }
4926  n = iter.Next();
4927  }
4928 
4929  // Survived possibility of exceptions. Commit changes to the clipboard now.
4930  newClipboard.Swap(*msClipboard);
4931 
4932  // Proceed to change the project. If this throws, the project will be
4933  // rolled back by the top level handler.
4934 
4935  n = iter.First();
4936  while (n) {
4937  // We clear from selected and sync-lock selected tracks.
4938  if (n->GetSelected() || n->IsSyncLockSelected()) {
4939  switch (n->GetKind())
4940  {
4941 #if defined(USE_MIDI)
4942  case Track::Note:
4943  //if NoteTrack, it was cut, so do not clear anything
4944  break;
4945 #endif
4946  case Track::Wave:
4947  if (gPrefs->Read(wxT("/GUI/EnableCutLines"), (long)0)) {
4948  ((WaveTrack*)n)->ClearAndAddCutLine(
4951  break;
4952  }
4953 
4954  // Fall through
4955 
4956  default:
4959  bre