Audacity 3.2.0
SelectMenus.cpp
Go to the documentation of this file.
1#include "../AdornedRulerPanel.h"
2#include "AudioIO.h"
3#include "../CommonCommandFlags.h"
4#include "Prefs.h"
5#include "Project.h"
6#include "ProjectAudioIO.h"
7#include "../ProjectAudioManager.h"
8#include "ProjectHistory.h"
9#include "ProjectRate.h"
10#include "../ProjectSelectionManager.h"
11#include "../ProjectSettings.h"
12#include "../ProjectWindow.h"
13#include "../ProjectWindows.h"
14#include "../SelectUtilities.h"
15#include "../SyncLock.h"
16#include "../TrackPanel.h"
17#include "../WaveTrack.h"
18#include "../LabelTrack.h"
19#include "../commands/CommandContext.h"
20#include "../commands/CommandManager.h"
21#include "../toolbars/ControlToolBar.h"
22#include "../tracks/ui/SelectHandle.h"
23#include "../tracks/labeltrack/ui/LabelTrackView.h"
24#include "../tracks/playabletrack/wavetrack/ui/WaveTrackView.h"
25
26// private helper classes and functions
27namespace {
28
30(AudacityProject &project, double t0)
31{
32 auto rate = ProjectRate::Get(project).GetRate();
33 auto &tracks = TrackList::Get( project );
34
35 // Window is 1/100th of a second.
36 auto windowSize = size_t(std::max(1.0, rate / 100));
37 Floats dist{ windowSize, true };
38
39 int nTracks = 0;
40 for (auto one : tracks.Selected< const WaveTrack >()) {
41 auto oneWindowSize = size_t(std::max(1.0, one->GetRate() / 100));
42 Floats oneDist{ oneWindowSize };
43 auto s = one->TimeToLongSamples(t0);
44 // fillTwo to ensure that missing values are treated as 2, and hence do
45 // not get used as zero crossings.
46 one->GetFloats(oneDist.get(),
47 s - (int)oneWindowSize/2, oneWindowSize, fillTwo);
48
49
50 // Looking for actual crossings.
51 double prev = 2.0;
52 for(size_t i=0; i<oneWindowSize; i++){
53 float fDist = fabs( oneDist[i]); // score is absolute value
54 if( prev * oneDist[i] > 0 ) // both same sign? No good.
55 fDist = fDist + 0.4; // No good if same sign.
56 else if( prev > 0.0 )
57 fDist = fDist + 0.1; // medium penalty for downward crossing.
58 prev = oneDist[i];
59 oneDist[i] = fDist;
60 }
61
62 // TODO: The mixed rate zero crossing code is broken,
63 // if oneWindowSize > windowSize we'll miss out some
64 // samples - so they will still be zero, so we'll use them.
65 for(size_t i = 0; i < windowSize; i++) {
66 size_t j;
67 if (windowSize != oneWindowSize)
68 j = i * (oneWindowSize-1) / (windowSize-1);
69 else
70 j = i;
71
72 dist[i] += oneDist[j];
73 // Apply a small penalty for distance from the original endpoint
74 // We'll always prefer an upward
75 dist[i] +=
76 0.1 * (abs(int(i) - int(windowSize/2))) / float(windowSize/2);
77 }
78 nTracks++;
79 }
80
81 // Find minimum
82 int argmin = 0;
83 float min = 3.0;
84 for(size_t i=0; i<windowSize; i++) {
85 if (dist[i] < min) {
86 argmin = i;
87 min = dist[i];
88 }
89 }
90
91 // If we're worse than 0.2 on average, on one track, then no good.
92 if(( nTracks == 1 ) && ( min > (0.2*nTracks) ))
93 return t0;
94 // If we're worse than 0.6 on average, on multi-track, then no good.
95 if(( nTracks > 1 ) && ( min > (0.6*nTracks) ))
96 return t0;
97
98 return t0 + (argmin - (int)windowSize/2) / rate;
99}
100
101// If this returns true, then there was a key up, and nothing more to do,
102// after this function has completed.
103// (at most this function just does a ModifyState for the keyup)
104bool OnlyHandleKeyUp( const CommandContext &context )
105{
106 auto &project = context.project;
107 auto evt = context.pEvt;
108 bool bKeyUp = (evt) && evt->GetEventType() == wxEVT_KEY_UP;
109
110 if( ProjectAudioIO::Get( project ).IsAudioActive() )
111 return bKeyUp;
112 if( !bKeyUp )
113 return false;
114
115 ProjectHistory::Get( project ).ModifyState(false);
116 return true;
117}
118
121 DIRECTION_RIGHT = +1
123
129
134
136{
137 wxLongLong mLastSelectionAdjustment { ::wxGetUTCTimeMillis() };
138 double mSeekShort{ 0.0 };
139 double mSeekLong{ 0.0 };
140};
141
142void SeekWhenAudioActive(double seekStep, wxLongLong &lastSelectionAdjustment)
143{
144 auto gAudioIO = AudioIO::Get();
145#ifdef EXPERIMENTAL_IMPROVED_SEEKING
146 if (gAudioIO->GetLastPlaybackTime() < lastSelectionAdjustment) {
147 // Allow time for the last seek to output a buffer before
148 // discarding samples again
149 // Do not advance mLastSelectionAdjustment
150 return;
151 }
152#endif
153 lastSelectionAdjustment = ::wxGetUTCTimeMillis();
154
155 gAudioIO->SeekStream(seekStep);
156}
157
158// Handles moving a selection edge with the keyboard in snap-to-time mode;
159// returns the moved value.
160// Will move at least minPix pixels -- set minPix positive to move forward,
161// negative to move backward.
162// Helper for moving by keyboard with snap-to-grid enabled
164(AudacityProject &project, double t, int minPix)
165{
166 auto &settings = ProjectSettings::Get(project);
167 auto rate = ProjectRate::Get(project).GetRate();
168 auto &viewInfo = ViewInfo::Get( project );
169 auto format = settings.GetSelectionFormat();
170
172
173 // Try incrementing/decrementing the value; if we've moved far enough we're
174 // done
175 double result;
176 minPix >= 0 ? nc.Increment() : nc.Decrement();
177 result = nc.GetValue();
178 if (std::abs(viewInfo.TimeToPosition(result) - viewInfo.TimeToPosition(t))
179 >= abs(minPix))
180 return result;
181
182 // Otherwise, move minPix pixels, then snap to the time.
183 result = viewInfo.OffsetTimeByPixels(t, minPix);
184 nc.SetValue(result);
185 result = nc.GetValue();
186 return result;
187}
188
190(AudacityProject &project,
191 double t, double offset, TimeUnit timeUnit, int snapToTime)
192{
193 auto &viewInfo = ViewInfo::Get( project );
194
195 if (timeUnit == TIME_UNIT_SECONDS)
196 return t + offset; // snapping is currently ignored for non-pixel moves
197
198 if (snapToTime == SNAP_OFF)
199 return viewInfo.OffsetTimeByPixels(t, (int)offset);
200
201 return GridMove(project, t, (int)offset);
202}
203
204// Moving a cursor, and collapsed selection.
206(AudacityProject &project, double seekStep, TimeUnit timeUnit)
207{
208 auto &viewInfo = ViewInfo::Get( project );
209 auto &trackPanel = TrackPanel::Get( project );
210 auto &tracks = TrackList::Get( project );
211 auto &ruler = AdornedRulerPanel::Get( project );
212 const auto &settings = ProjectSettings::Get( project );
213 auto &window = ProjectWindow::Get( project );
214
215 // If TIME_UNIT_SECONDS, snap-to will be off.
216 int snapToTime = settings.GetSnapTo();
217 const double t0 = viewInfo.selectedRegion.t0();
218 const double end = std::max(
219 tracks.GetEndTime(),
220 viewInfo.GetScreenEndTime());
221
222 // Move the cursor
223 // Already in cursor mode?
224 if( viewInfo.selectedRegion.isPoint() )
225 {
226 double newT = OffsetTime(project,
227 t0, seekStep, timeUnit, snapToTime);
228 // constrain.
229 newT = std::max(0.0, newT);
230 newT = std::min(newT, end);
231 // Move
232 viewInfo.selectedRegion.setT0(
233 newT,
234 false); // do not swap selection boundaries
235 viewInfo.selectedRegion.collapseToT0();
236
237 // Move the visual cursor, avoiding an unnecessary complete redraw
238 trackPanel.DrawOverlays(false);
239 ruler.DrawOverlays(false);
240 }
241 else
242 {
243 // Transition to cursor mode.
244 if( seekStep < 0 )
245 viewInfo.selectedRegion.collapseToT0();
246 else
247 viewInfo.selectedRegion.collapseToT1();
248 trackPanel.Refresh(false);
249 }
250
251 // Make sure NEW position is in view
252 window.ScrollIntoView(viewInfo.selectedRegion.t1());
253 return;
254}
255
257(AudacityProject &project, double seekStep, TimeUnit timeUnit,
258SelectionOperation operation)
259{
260 auto &viewInfo = ViewInfo::Get( project );
261 auto &tracks = TrackList::Get( project );
262 const auto &settings = ProjectSettings::Get( project );
263 auto &window = ProjectWindow::Get( project );
264
265 if( operation == CURSOR_MOVE )
266 {
267 MoveWhenAudioInactive( project, seekStep, timeUnit);
268 return;
269 }
270
271 int snapToTime = settings.GetSnapTo();
272 const double t0 = viewInfo.selectedRegion.t0();
273 const double t1 = viewInfo.selectedRegion.t1();
274 const double end = std::max(
275 tracks.GetEndTime(),
276 viewInfo.GetScreenEndTime());
277
278 // Is it t0 or t1 moving?
279 bool bMoveT0 = (operation == SELECTION_CONTRACT && seekStep > 0) ||
280 (operation == SELECTION_EXTEND && seekStep < 0);
281 // newT is where we want to move to
282 double newT = OffsetTime( project,
283 bMoveT0 ? t0 : t1, seekStep, timeUnit, snapToTime);
284 // constrain to be in the track/screen limits.
285 newT = std::max( 0.0, newT );
286 newT = std::min( newT, end);
287 // optionally constrain to be a contraction, i.e. so t0/t1 do not cross over
288 if( operation == SELECTION_CONTRACT )
289 newT = bMoveT0 ? std::min( t1, newT ) : std::max( t0, newT );
290
291 // Actually move
292 if( bMoveT0 )
293 viewInfo.selectedRegion.setT0( newT );
294 else
295 viewInfo.selectedRegion.setT1( newT );
296
297 // Ensure it is visible
298 window.ScrollIntoView(newT);
299}
300
301// Handle small cursor and play head movements
303(AudacityProject &project, double direction, SelectionOperation operation,
304 SeekInfo &info)
305{
306 // PRL: What I found and preserved, strange though it be:
307 // During playback: jump depends on preferences and is independent of the
308 // zoom and does not vary if the key is held
309 // Else: jump depends on the zoom and gets bigger if the key is held
310
311 if( ProjectAudioIO::Get( project ).IsAudioActive() )
312 {
313 if( operation == CURSOR_MOVE )
314 SeekWhenAudioActive( info.mSeekShort * direction,
316 else if( operation == SELECTION_EXTEND )
317 SeekWhenAudioActive( info.mSeekLong * direction,
319 // Note: no action for CURSOR_CONTRACT
320 return;
321 }
322
323 // If the last adjustment was very recent, we are
324 // holding the key down and should move faster.
325 const wxLongLong curtime = ::wxGetUTCTimeMillis();
326 enum { MIN_INTERVAL = 50 };
327 const bool fast =
328 (curtime - info.mLastSelectionAdjustment < MIN_INTERVAL);
329
330 info.mLastSelectionAdjustment = curtime;
331
332 // How much faster should the cursor move if shift is down?
333 enum { LARGER_MULTIPLIER = 4 };
334 const double seekStep = (fast ? LARGER_MULTIPLIER : 1.0) * direction;
335
336 SeekWhenAudioInactive( project, seekStep, TIME_UNIT_PIXELS, operation);
337}
338
339// Move the cursor forward or backward, while paused or while playing.
341 AudacityProject &project, double seekStep,
342 wxLongLong &lastSelectionAdjustment)
343{
344 if (ProjectAudioIO::Get( project ).IsAudioActive()) {
345 SeekWhenAudioActive(seekStep, lastSelectionAdjustment);
346 }
347 else
348 {
349 lastSelectionAdjustment = ::wxGetUTCTimeMillis();
350 MoveWhenAudioInactive(project, seekStep, TIME_UNIT_SECONDS);
351 }
352
353 ProjectHistory::Get( project ).ModifyState(false);
354}
355
356void DoBoundaryMove(AudacityProject &project, int step, SeekInfo &info)
357{
358 auto &viewInfo = ViewInfo::Get( project );
359 auto &tracks = TrackList::Get( project );
360 auto &window = ProjectWindow::Get( project );
361
362 // step is negative, then is moving left. step positive, moving right.
363 // Move the left/right selection boundary, to expand the selection
364
365 // If the last adjustment was very recent, we are
366 // holding the key down and should move faster.
367 wxLongLong curtime = ::wxGetUTCTimeMillis();
368 int pixels = step;
369 if( curtime - info.mLastSelectionAdjustment < 50 )
370 {
371 pixels *= 4;
372 }
373 info.mLastSelectionAdjustment = curtime;
374
375 // we used to have a parameter boundaryContract to say if expanding or
376 // contracting. it is no longer needed.
377 bool bMoveT0 = (step < 0 );// ^ boundaryContract ;
378
379 if( ProjectAudioIO::Get( project ).IsAudioActive() )
380 {
381 auto gAudioIO = AudioIO::Get();
382 double indicator = gAudioIO->GetStreamTime();
383 if( bMoveT0 )
384 viewInfo.selectedRegion.setT0(indicator, false);
385 else
386 viewInfo.selectedRegion.setT1(indicator);
387
388 ProjectHistory::Get( project ).ModifyState(false);
389 return;
390 }
391
392 const double t0 = viewInfo.selectedRegion.t0();
393 const double t1 = viewInfo.selectedRegion.t1();
394 const double end = std::max(
395 tracks.GetEndTime(),
396 viewInfo.GetScreenEndTime());
397
398 double newT = viewInfo.OffsetTimeByPixels( bMoveT0 ? t0 : t1, pixels);
399 // constrain to be in the track/screen limits.
400 newT = std::max( 0.0, newT );
401 newT = std::min( newT, end);
402 // optionally constrain to be a contraction, i.e. so t0/t1 do not cross over
403 //if( boundaryContract )
404 // newT = bMoveT0 ? std::min( t1, newT ) : std::max( t0, newT );
405
406 // Actually move
407 if( bMoveT0 )
408 viewInfo.selectedRegion.setT0( newT );
409 else
410 viewInfo.selectedRegion.setT1( newT );
411
412 // Ensure it is visible
413 window.ScrollIntoView(newT);
414
415 ProjectHistory::Get( project ).ModifyState(false);
416}
417
418}
419
420namespace SelectActions {
421
422
423// Menu handler functions
424
426 : CommandHandlerObject // MUST be the first base class!
429{
430
431void OnSelectAll(const CommandContext &context)
432{
433 auto& trackPanel = TrackPanel::Get(context.project);
434 auto& tracks = TrackList::Get(context.project);
435
436 for (auto lt : tracks.Selected< LabelTrack >()) {
437 auto& view = LabelTrackView::Get(*lt);
438 if (view.SelectAllText(context.project)) {
439 trackPanel.Refresh(false);
440 return;
441 }
442 }
443
444 //Presumably, there might be not more than one track
445 //that expects text input
446 for (auto wt : tracks.Any<WaveTrack>()) {
447 auto& view = WaveTrackView::Get(*wt);
448 if (view.SelectAllText(context.project)) {
449 trackPanel.Refresh(false);
450 return;
451 }
452 }
453
455}
456
457void OnSelectNone(const CommandContext &context)
458{
459 auto &project = context.project;
460 auto &selectedRegion = ViewInfo::Get( project ).selectedRegion;
461
462 selectedRegion.collapseToT0();
464 ProjectHistory::Get( project ).ModifyState(false);
465}
466
468{
469 auto &project = context.project;
470 SelectUtilities::DoSelectTimeAndTracks( project, false, true );
471}
472
474{
475 auto &project = context.project;
476 auto &tracks = TrackList::Get( project );
477
478 bool selected = false;
479 for (auto t : tracks.Any() + &Track::SupportsBasicEditing
481 t->SetSelected(true);
482 selected = true;
483 }
484
485 if (selected)
486 ProjectHistory::Get( project ).ModifyState(false);
487}
488
490{
492 true, true, XO("Set Left Selection Boundary"));
493}
494
496{
498 false, true, XO("Set Right Selection Boundary"));
499}
500
502{
503 auto &project = context.project;
504 auto &tracks = TrackList::Get( project );
505 auto &selectedRegion = ViewInfo::Get( project ).selectedRegion;
506
507 double kWayOverToRight = std::numeric_limits<double>::max();
508
509 auto range = tracks.Selected();
510 if ( ! range )
511 return;
512
513 double minOffset = range.min( &Track::GetStartTime );
514
515 if( minOffset >=
516 (kWayOverToRight * (1 - std::numeric_limits<double>::epsilon()) ))
517 return;
518
519 selectedRegion.setT0(minOffset);
520
521 ProjectHistory::Get( project ).ModifyState(false);
522}
523
525{
526 auto &project = context.project;
527 auto &tracks = TrackList::Get( project );
528 auto &selectedRegion = ViewInfo::Get( project ).selectedRegion;
529
530 double kWayOverToLeft = std::numeric_limits<double>::lowest();
531
532 auto range = tracks.Selected();
533 if ( ! range )
534 return;
535
536 double maxEndOffset = range.max( &Track::GetEndTime );
537
538 if( maxEndOffset <=
539 (kWayOverToLeft * (1 - std::numeric_limits<double>::epsilon()) ))
540 return;
541
542 selectedRegion.setT1(maxEndOffset);
543
544 ProjectHistory::Get( project ).ModifyState(false);
545}
546
548{
549 auto &project = context.project;
550 auto &viewInfo = ViewInfo::Get( project );
551 auto &tracks = TrackList::Get( project );
552
553 auto range = tracks.Selected();
554 double maxEndOffset = range.max( &Track::GetEndTime );
555 double minOffset = range.min( &Track::GetStartTime );
556
557 if( maxEndOffset < minOffset)
558 return;
559
560 viewInfo.selectedRegion.setTimes( minOffset, maxEndOffset );
561 ProjectHistory::Get( project ).ModifyState(false);
562}
563
564// Handler state:
566
567void OnSelectionSave(const CommandContext &context)
568{
569 auto &project = context.project;
570 auto &selectedRegion = ViewInfo::Get( project ).selectedRegion;
571
572 mRegionSave = selectedRegion;
573}
574
576{
577 auto &project = context.project;
578 auto &selectedRegion = ViewInfo::Get( project ).selectedRegion;
579 auto &window = ProjectWindow::Get(project);
580
581 if ((mRegionSave.t0() == 0.0) &&
582 (mRegionSave.t1() == 0.0))
583 return;
584
585 selectedRegion = mRegionSave;
586 window.ScrollIntoView(selectedRegion.t0());
587
588 ProjectHistory::Get( project ).ModifyState(false);
589}
590
591// Handler state:
594
596{
597 auto &project = context.project;
598 auto &selectedRegion = ViewInfo::Get( project ).selectedRegion;
599 auto isAudioActive = ProjectAudioIO::Get( project ).IsAudioActive();
600
602 auto gAudioIO = AudioIO::Get();
603 double cursorPositionCurrent = isAudioActive
604 ? gAudioIO->GetStreamTime()
605 : selectedRegion.t0();
606 selectedRegion.setTimes(
607 std::min(cursorPositionCurrent, mCursorPositionStored),
608 std::max(cursorPositionCurrent, mCursorPositionStored));
609
610 ProjectHistory::Get( project ).ModifyState(false);
611 }
612}
613
615{
616 auto &project = context.project;
617 auto &selectedRegion = ViewInfo::Get( project ).selectedRegion;
618 auto isAudioActive = ProjectAudioIO::Get( project ).IsAudioActive();
619
620 auto gAudioIO = AudioIO::Get();
622 isAudioActive ? gAudioIO->GetStreamTime() : selectedRegion.t0();
624}
625
626void OnZeroCrossing(const CommandContext &context)
627{
628 auto &project = context.project;
629 auto &selectedRegion = ViewInfo::Get( project ).selectedRegion;
630
631 const double t0 = NearestZeroCrossing(project, selectedRegion.t0());
632 if (selectedRegion.isPoint())
633 selectedRegion.setTimes(t0, t0);
634 else {
635 const double t1 = NearestZeroCrossing(project, selectedRegion.t1());
636 // Empty selection is generally not much use, so do not make it if empty.
637 if( fabs( t1 - t0 ) * ProjectRate::Get(project).GetRate() > 1.5 )
638 selectedRegion.setTimes(t0, t1);
639 }
640
641 ProjectHistory::Get( project ).ModifyState(false);
642}
643
644void OnSnapToOff(const CommandContext &context)
645{
646 auto &project = context.project;
648}
649
650void OnSnapToNearest(const CommandContext &context)
651{
652 auto &project = context.project;
654}
655
656void OnSnapToPrior(const CommandContext &context)
657{
658 auto &project = context.project;
660}
661
662void OnSelToStart(const CommandContext &context)
663{
664 auto &project = context.project;
665 auto &window = ProjectWindow::Get( project );
666 window.Rewind(true);
667 ProjectHistory::Get( project ).ModifyState(false);
668}
669
670void OnSelToEnd(const CommandContext &context)
671{
672 auto &project = context.project;
673 auto &window = ProjectWindow::Get( project );
674 window.SkipEnd(true);
675 ProjectHistory::Get( project ).ModifyState(false);
676}
677
678// Handler state:
679SeekInfo mSeekInfo;
680
681void OnSelExtendLeft(const CommandContext &context)
682{
683 if( !OnlyHandleKeyUp( context ) )
685 mSeekInfo );
686}
687
689{
690 if( !OnlyHandleKeyUp( context ) )
692 mSeekInfo );
693}
694
696{
698}
699
701{
703}
704
706{
707 if( !OnlyHandleKeyUp( context ) )
709 mSeekInfo );
710}
711
713{
714 if( !OnlyHandleKeyUp( context ) )
716 mSeekInfo );
717}
718
720{
721 auto &project = context.project;
722 auto &selectedRegion = ViewInfo::Get( project ).selectedRegion;
723 auto &window = ProjectWindow::Get( project );
724
725 selectedRegion.collapseToT0();
726 ProjectHistory::Get( project ).ModifyState(false);
727 window.ScrollIntoView(selectedRegion.t0());
728}
729
730void OnCursorSelEnd(const CommandContext &context)
731{
732 auto &project = context.project;
733 auto &selectedRegion = ViewInfo::Get( project ).selectedRegion;
734 auto &window = ProjectWindow::Get( project );
735
736 selectedRegion.collapseToT1();
737 ProjectHistory::Get( project ).ModifyState(false);
738 window.ScrollIntoView(selectedRegion.t1());
739}
740
742{
743 auto &project = context.project;
744 auto &tracks = TrackList::Get( project );
745 auto &selectedRegion = ViewInfo::Get( project ).selectedRegion;
746 auto &window = ProjectWindow::Get( project );
747
748 double kWayOverToRight = std::numeric_limits<double>::max();
749
750 auto trackRange = tracks.Selected() + &Track::SupportsBasicEditing;
751 if (trackRange.empty())
752 // This should have been prevented by command manager
753 return;
754
755 // Range is surely nonempty now
756 auto minOffset = std::max( 0.0, trackRange.min( &Track::GetOffset ) );
757
758 if( minOffset >=
759 (kWayOverToRight * (1 - std::numeric_limits<double>::epsilon()) ))
760 return;
761
762 selectedRegion.setTimes(minOffset, minOffset);
763 ProjectHistory::Get( project ).ModifyState(false);
764 window.ScrollIntoView(selectedRegion.t0());
765}
766
768{
769 auto &project = context.project;
770 auto &tracks = TrackList::Get( project );
771 auto &selectedRegion = ViewInfo::Get( project ).selectedRegion;
772 auto &window = ProjectWindow::Get( project );
773
774 double kWayOverToLeft = std::numeric_limits<double>::lowest();
775
776 auto trackRange = tracks.Selected() + &Track::SupportsBasicEditing;
777 if (trackRange.empty())
778 // This should have been prevented by command manager
779 return;
780
781 // Range is surely nonempty now
782 auto maxEndOffset = trackRange.max( &Track::GetEndTime );
783
784 if( maxEndOffset <
785 (kWayOverToLeft * (1 - std::numeric_limits<double>::epsilon()) ))
786 return;
787
788 selectedRegion.setTimes(maxEndOffset, maxEndOffset);
789 ProjectHistory::Get( project ).ModifyState(false);
790 window.ScrollIntoView(selectedRegion.t1());
791}
792
793void OnSkipStart(const CommandContext &context)
794{
795 auto &project = context.project;
796 wxCommandEvent evt;
797
798 auto &controlToolBar = ControlToolBar::Get( project );
799 controlToolBar.OnRewind(evt);
800 ProjectHistory::Get( project ).ModifyState(false);
801}
802
803void OnSkipEnd(const CommandContext &context)
804{
805 auto &project = context.project;
806 wxCommandEvent evt;
807
808 auto &controlToolBar = ControlToolBar::Get( project );
809 controlToolBar.OnFF(evt);
810 ProjectHistory::Get( project ).ModifyState(false);
811}
812
813void OnCursorLeft(const CommandContext &context)
814{
815 if( !OnlyHandleKeyUp( context ) )
817 mSeekInfo );
818}
819
820void OnCursorRight(const CommandContext &context)
821{
822 if( !OnlyHandleKeyUp( context ) )
824 mSeekInfo );
825}
826
828{
829 DoCursorMove( context.project,
830 -mSeekInfo.mSeekShort, mSeekInfo.mLastSelectionAdjustment );
831}
832
834{
835 DoCursorMove( context.project,
836 mSeekInfo.mSeekShort, mSeekInfo.mLastSelectionAdjustment );
837}
838
840{
841 DoCursorMove( context.project,
842 -mSeekInfo.mSeekLong, mSeekInfo.mLastSelectionAdjustment );
843}
844
846{
847 DoCursorMove( context.project,
848 mSeekInfo.mSeekLong, mSeekInfo.mLastSelectionAdjustment );
849}
850
851void OnSeekLeftShort(const CommandContext &context)
852{
853 auto &project = context.project;
855}
856
858{
859 auto &project = context.project;
861}
862
863void OnSeekLeftLong(const CommandContext &context)
864{
865 auto &project = context.project;
867}
868
869void OnSeekRightLong(const CommandContext &context)
870{
871 auto &project = context.project;
873}
874
875#if 1
876// Legacy functions, not used as of version 2.3.0
877void OnSelectAllTime(const CommandContext &context)
878{
879 auto &project = context.project;
880 SelectUtilities::DoSelectTimeAndTracks( project, true, false );
881}
882#endif
883
884void UpdatePrefs() override
885{
886 gPrefs->Read(wxT("/AudioIO/SeekShortPeriod"), &mSeekInfo.mSeekShort, 1.0);
887 gPrefs->Read(wxT("/AudioIO/SeekLongPeriod"), &mSeekInfo.mSeekLong, 15.0);
888}
890{
891 UpdatePrefs();
892}
893Handler( const Handler & ) PROHIBITED;
894Handler &operator=( const Handler & ) PROHIBITED;
895
896}; // struct Handler
897
898} // namespace
899
900// Handler is stateful. Needs a factory registered with
901// AudacityProject.
903 [](AudacityProject&) {
904 return std::make_unique< SelectActions::Handler >(); } };
905
907 return project.AttachedObjects::Get< SelectActions::Handler >( key );
908};
909
910// Menu definitions
911
912#define FN(X) (& SelectActions::Handler :: X)
913
914namespace {
915using namespace MenuTable;
917{
919 static BaseItemSharedPtr menu{
921 /* i18n-hint: (verb) It's an item on a menu. */
922 Menu( wxT("Select"), XXO("&Select"),
923 Section( "Basic",
924 Command( wxT("SelectAll"), XXO("&All"), FN(OnSelectAll),
926 Options{ wxT("Ctrl+A"), XO("Select All") } ),
927 Command( wxT("SelectNone"), XXO("&None"), FN(OnSelectNone),
929 Options{ wxT("Ctrl+Shift+A"), XO("Select None") } ),
930
932
933 Menu( wxT("Tracks"), XXO("&Tracks"),
934 Command( wxT("SelAllTracks"), XXO("In All &Tracks"),
935 FN(OnSelectAllTracks),
937 wxT("Ctrl+Shift+K") )
938
939 #ifdef EXPERIMENTAL_SYNC_LOCK
940 ,
941 Command( wxT("SelSyncLockTracks"), XXO("In All &Sync-Locked Tracks"),
942 FN(OnSelectSyncLockSel),
944 Options{ wxT("Ctrl+Shift+Y"), XO("Select Sync-Locked") } )
945 #endif
946 ),
947
949
950 Menu( wxT("Region"), XXO("R&egion"),
951 Section( "",
952 Command( wxT("SetLeftSelection"), XXO("&Left at Playback Position"),
953 FN(OnSetLeftSelection), TracksExistFlag(),
954 Options{ wxT("["), XO("Set Selection Left at Play Position") } ),
955 Command( wxT("SetRightSelection"), XXO("&Right at Playback Position"),
956 FN(OnSetRightSelection), TracksExistFlag(),
957 Options{ wxT("]"), XO("Set Selection Right at Play Position") } ),
958 Command( wxT("SelTrackStartToCursor"), XXO("Track &Start to Cursor"),
959 FN(OnSelectStartCursor), AlwaysEnabledFlag,
960 Options{ wxT("Shift+J"), XO("Select Track Start to Cursor") } ),
961 Command( wxT("SelCursorToTrackEnd"), XXO("Cursor to Track &End"),
962 FN(OnSelectCursorEnd), AlwaysEnabledFlag,
963 Options{ wxT("Shift+K"), XO("Select Cursor to Track End") } ),
964 Command( wxT("SelTrackStartToEnd"), XXO("Track Start to En&d"),
965 FN(OnSelectTrackStartToEnd), AlwaysEnabledFlag,
966 Options{}.LongName( XO("Select Track Start to End") ) )
967 ),
968
969 Section( "",
970 // GA: Audacity had 'Store Re&gion' here previously. There is no
971 // one-step way to restore the 'Saved Cursor Position' in Select Menu,
972 // so arguably using the word 'Selection' to do duty for both saving
973 // the region or the cursor is better. But it does not belong in a
974 // 'Region' submenu.
975 Command( wxT("SelSave"), XXO("S&tore Selection"), FN(OnSelectionSave),
977 // Audacity had 'Retrieve Regio&n' here previously.
978 Command( wxT("SelRestore"), XXO("Retrieve Selectio&n"),
979 FN(OnSelectionRestore), TracksExistFlag() )
980 )
981 )
982
984
985 ),
986
987 Section( "",
988 Command( wxT("SelCursorStoredCursor"),
989 XXO("Cursor to Stored &Cursor Position"),
990 FN(OnSelectCursorStoredCursor), TracksExistFlag(),
991 Options{}.LongName( XO("Select Cursor to Stored") ) ),
992
993 Command( wxT("StoreCursorPosition"), XXO("Store Cursor Pos&ition"),
994 FN(OnCursorPositionStore),
996 // Save cursor position is used in some selections.
997 // Maybe there should be a restore for it?
998 ),
999
1000 Section( "",
1001 Command( wxT("ZeroCross"), XXO("At &Zero Crossings"),
1002 FN(OnZeroCrossing), EditableTracksSelectedFlag(),
1003 Options{ wxT("Z"), XO("Select Zero Crossing") } )
1004 )
1005 ) ) };
1006 return menu;
1007}
1008
1010 wxT(""),
1011 Shared( SelectMenu() )
1012};
1013
1015{
1017 static BaseItemSharedPtr menu{
1019 Menu( wxT("Select"), XXO("&Selection"),
1020 Command( wxT("SnapToOff"), XXO("Snap-To &Off"), FN(OnSnapToOff),
1022 Command( wxT("SnapToNearest"), XXO("Snap-To &Nearest"),
1023 FN(OnSnapToNearest), AlwaysEnabledFlag ),
1024 Command( wxT("SnapToPrior"), XXO("Snap-To &Prior"), FN(OnSnapToPrior),
1026 Command( wxT("SelStart"), XXO("Selection to &Start"), FN(OnSelToStart),
1027 AlwaysEnabledFlag, wxT("Shift+Home") ),
1028 Command( wxT("SelEnd"), XXO("Selection to En&d"), FN(OnSelToEnd),
1029 AlwaysEnabledFlag, wxT("Shift+End") ),
1030 Command( wxT("SelExtLeft"), XXO("Selection Extend &Left"),
1031 FN(OnSelExtendLeft),
1033 Options{ wxT("Shift+Left") }.WantKeyUp().AllowDup() ),
1034 Command( wxT("SelExtRight"), XXO("Selection Extend &Right"),
1035 FN(OnSelExtendRight),
1037 Options{ wxT("Shift+Right") }.WantKeyUp().AllowDup() ),
1038 Command( wxT("SelSetExtLeft"), XXO("Set (or Extend) Le&ft Selection"),
1039 FN(OnSelSetExtendLeft),
1041 Command( wxT("SelSetExtRight"), XXO("Set (or Extend) Rig&ht Selection"),
1042 FN(OnSelSetExtendRight),
1044 Command( wxT("SelCntrLeft"), XXO("Selection Contract L&eft"),
1045 FN(OnSelContractLeft),
1047 Options{ wxT("Ctrl+Shift+Right") }.WantKeyUp() ),
1048 Command( wxT("SelCntrRight"), XXO("Selection Contract R&ight"),
1049 FN(OnSelContractRight),
1051 Options{ wxT("Ctrl+Shift+Left") }.WantKeyUp() )
1052 ) ) };
1053 return menu;
1054}
1055
1057 wxT("Optional/Extra/Part1"),
1059};
1060}
1061
1062namespace {
1064{
1066 static const auto CanStopFlags = AudioIONotBusyFlag() | CanStopAudioStreamFlag();
1067
1068 // JKC: ANSWER-ME: How is 'cursor to' different to 'Skip To' and how is it
1069 // useful?
1070 // GA: 'Skip to' moves the viewpoint to center of the track and preserves the
1071 // selection. 'Cursor to' does neither. 'Center at' might describe it better
1072 // than 'Skip'.
1073 static BaseItemSharedPtr menu{
1075 Menu( wxT("Cursor"), XXO("&Cursor to"),
1076 Command( wxT("CursSelStart"), XXO("Selection Star&t"),
1077 FN(OnCursorSelStart),
1079 Options{}.LongName( XO("Cursor to Selection Start") ) ),
1080 Command( wxT("CursSelEnd"), XXO("Selection En&d"),
1081 FN(OnCursorSelEnd),
1083 Options{}.LongName( XO("Cursor to Selection End") ) ),
1084
1085 Command( wxT("CursTrackStart"), XXO("Track &Start"),
1086 FN(OnCursorTrackStart),
1088 Options{ wxT("J"), XO("Cursor to Track Start") } ),
1089 Command( wxT("CursTrackEnd"), XXO("Track &End"),
1090 FN(OnCursorTrackEnd),
1092 Options{ wxT("K"), XO("Cursor to Track End") } ),
1093
1094 Command( wxT("CursProjectStart"), XXO("&Project Start"),
1095 FN(OnSkipStart),
1097 Options{ wxT("Home"), XO("Cursor to Project Start") } ),
1098 Command( wxT("CursProjectEnd"), XXO("Project E&nd"), FN(OnSkipEnd),
1100 Options{ wxT("End"), XO("Cursor to Project End") } )
1101 ) ) };
1102 return menu;
1103}
1104
1106 wxT("Transport/Basic"),
1107 Shared( CursorMenu() )
1108};
1109
1111{
1113 static BaseItemSharedPtr menu{
1115 Menu( wxT("Cursor"), XXO("&Cursor"),
1116 Command( wxT("CursorLeft"), XXO("Cursor &Left"), FN(OnCursorLeft),
1118 Options{ wxT("Left") }.WantKeyUp().AllowDup() ),
1119 Command( wxT("CursorRight"), XXO("Cursor &Right"), FN(OnCursorRight),
1121 Options{ wxT("Right") }.WantKeyUp().AllowDup() ),
1122 Command( wxT("CursorShortJumpLeft"), XXO("Cursor Sh&ort Jump Left"),
1123 FN(OnCursorShortJumpLeft),
1125 Command( wxT("CursorShortJumpRight"), XXO("Cursor Shor&t Jump Right"),
1126 FN(OnCursorShortJumpRight),
1128 Command( wxT("CursorLongJumpLeft"), XXO("Cursor Long J&ump Left"),
1129 FN(OnCursorLongJumpLeft),
1130 TracksExistFlag() | TrackPanelHasFocus(), wxT("Shift+,") ),
1131 Command( wxT("CursorLongJumpRight"), XXO("Cursor Long Ju&mp Right"),
1132 FN(OnCursorLongJumpRight),
1133 TracksExistFlag() | TrackPanelHasFocus(), wxT("Shift+.") )
1134 ) ) };
1135 return menu;
1136}
1137
1139 wxT("Optional/Extra/Part2"),
1141};
1142
1144{
1146 static BaseItemSharedPtr menu{
1148 Menu( wxT("Seek"), XXO("See&k"),
1149 Command( wxT("SeekLeftShort"), XXO("Short Seek &Left During Playback"),
1150 FN(OnSeekLeftShort), AudioIOBusyFlag(),
1151 Options{ wxT("Left") }.AllowDup() ),
1152 Command( wxT("SeekRightShort"),
1153 XXO("Short Seek &Right During Playback"), FN(OnSeekRightShort),
1155 Options{ wxT("Right") }.AllowDup() ),
1156 Command( wxT("SeekLeftLong"), XXO("Long Seek Le&ft During Playback"),
1157 FN(OnSeekLeftLong), AudioIOBusyFlag(),
1158 Options{ wxT("Shift+Left") }.AllowDup() ),
1159 Command( wxT("SeekRightLong"), XXO("Long Seek Rig&ht During Playback"),
1160 FN(OnSeekRightLong), AudioIOBusyFlag(),
1161 Options{ wxT("Shift+Right") }.AllowDup() )
1162 ) ) };
1163 return menu;
1164}
1165
1167 wxT("Optional/Extra/Part1"),
1169};
1170
1171}
1172
1173#undef FN
wxT("CloseDown"))
AttachedItem sAttachment1
AttachedItem sAttachment2
constexpr CommandFlag AlwaysEnabledFlag
Definition: CommandFlag.h:34
wxEvtHandler CommandHandlerObject
const ReservedCommandFlag & AudioIOBusyFlag()
const ReservedCommandFlag & AudioIONotBusyFlag()
const ReservedCommandFlag & IsSyncLockedFlag()
const ReservedCommandFlag & TimeSelectedFlag()
const ReservedCommandFlag & EditableTracksSelectedFlag()
const ReservedCommandFlag & TracksExistFlag()
const ReservedCommandFlag & WaveTracksExistFlag()
const ReservedCommandFlag & WaveTracksSelectedFlag()
const ReservedCommandFlag & TrackPanelHasFocus()
int min(int a, int b)
int format
Definition: ExportPCM.cpp:53
XO("Cut/Copy/Paste")
XXO("&Cut/Copy/Paste Toolbar")
FileConfig * gPrefs
Definition: Prefs.cpp:70
const ReservedCommandFlag & CanStopAudioStreamFlag()
an object holding per-project preferred sample rate
@ SNAP_NEAREST
@ SNAP_PRIOR
@ SNAP_OFF
@ fillTwo
Definition: SampleFormat.h:61
static const AudacityProject::AttachedObjects::RegisteredFactory key
#define FN(X)
static CommandHandlerObject & findCommandHandler(AudacityProject &project)
static Settings & settings()
Definition: TrackInfo.cpp:87
static AdornedRulerPanel & Get(AudacityProject &project)
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:90
static AudioIO * Get()
Definition: AudioIO.cpp:147
Client code makes static instance from a factory of attachments; passes it to Get or Find as a retrie...
Definition: ClientData.h:266
CommandContext provides additional information to an 'Apply()' command. It provides the project,...
const wxEvent * pEvt
AudacityProject & project
static ControlToolBar & Get(AudacityProject &project)
A LabelTrack is a Track that holds labels (LabelStruct).
Definition: LabelTrack.h:87
static LabelTrackView & Get(LabelTrack &)
NumericConverter provides the advanced formatting control used in the selection bar of Audacity.
void SetValue(double newValue)
A listener notified of changes in preferences.
Definition: Prefs.h:556
bool IsAudioActive() const
static ProjectAudioIO & Get(AudacityProject &project)
void ModifyState(bool bWantsAutoSave)
static ProjectHistory & Get(AudacityProject &project)
static ProjectRate & Get(AudacityProject &project)
Definition: ProjectRate.cpp:28
double GetRate() const
Definition: ProjectRate.cpp:53
void AS_SetSnapTo(int snap) override
static ProjectSelectionManager & Get(AudacityProject &project)
static ProjectSettings & Get(AudacityProject &project)
static ProjectWindow & Get(AudacityProject &project)
Defines a selected portion of a project.
double t1() const
double t0() const
static bool IsSyncLockSelected(const Track *pTrack)
Definition: SyncLock.cpp:43
bool IsSelected() const
Definition: Track.cpp:402
virtual bool SupportsBasicEditing() const
Whether this track type implements cut-copy-paste; by default, true.
Definition: Track.cpp:1258
virtual double GetStartTime() const =0
virtual double GetOffset() const =0
virtual double GetEndTime() const =0
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:486
static TrackPanel & Get(AudacityProject &project)
Definition: TrackPanel.cpp:230
NotifyingSelectedRegion selectedRegion
Definition: ViewInfo.h:217
static ViewInfo & Get(AudacityProject &project)
Definition: ViewInfo.cpp:234
A Track that contains audio waveform data.
Definition: WaveTrack.h:57
static WaveTrackView & Get(WaveTrack &track)
std::unique_ptr< MenuItem > Menu(const Identifier &internalName, const TranslatableString &title, Args &&... args)
std::unique_ptr< MenuPart > Section(const Identifier &internalName, Args &&... args)
std::unique_ptr< CommandItem > Command(const CommandID &name, const TranslatableString &label_in, void(Handler::*pmf)(const CommandContext &), CommandFlag flags, const CommandManager::Options &options={}, CommandHandlerFinder finder=FinderScope::DefaultFinder())
auto end(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
Definition: PackedArray.h:159
std::shared_ptr< BaseItem > BaseItemSharedPtr
Definition: Registry.h:72
void SelectNone(AudacityProject &project)
void DoSelectAll(AudacityProject &project)
void OnSetRegion(AudacityProject &project, bool left, bool selection, const TranslatableString &dialogTitle)
Adjust left or right of selection or play region.
void DoSelectTimeAndTracks(AudacityProject &project, bool bAllTime, bool bAllTracks)
void DoBoundaryMove(AudacityProject &project, int step, SeekInfo &info)
double NearestZeroCrossing(AudacityProject &project, double t0)
Definition: SelectMenus.cpp:30
bool OnlyHandleKeyUp(const CommandContext &context)
double OffsetTime(AudacityProject &project, double t, double offset, TimeUnit timeUnit, int snapToTime)
void SeekWhenAudioActive(double seekStep, wxLongLong &lastSelectionAdjustment)
void MoveWhenAudioInactive(AudacityProject &project, double seekStep, TimeUnit timeUnit)
double GridMove(AudacityProject &project, double t, int minPix)
void SeekWhenAudioInactive(AudacityProject &project, double seekStep, TimeUnit timeUnit, SelectionOperation operation)
void SeekLeftOrRight(AudacityProject &project, double direction, SelectionOperation operation, SeekInfo &info)
void DoCursorMove(AudacityProject &project, double seekStep, wxLongLong &lastSelectionAdjustment)
A convenient default parameter for class template Site.
Definition: ClientData.h:28
Options && LongName(const TranslatableString &value) &&
void OnSetLeftSelection(const CommandContext &context)
void OnCursorLeft(const CommandContext &context)
void OnSnapToOff(const CommandContext &context)
void OnCursorRight(const CommandContext &context)
Handler & operator=(const Handler &) PROHIBITED
void OnSelExtendRight(const CommandContext &context)
void OnSelectionSave(const CommandContext &context)
void OnSkipEnd(const CommandContext &context)
void OnSelContractRight(const CommandContext &context)
void OnSelContractLeft(const CommandContext &context)
void OnSelectAll(const CommandContext &context)
SelectedRegion mRegionSave
void OnCursorLongJumpRight(const CommandContext &context)
void UpdatePrefs() override
void OnSeekRightShort(const CommandContext &context)
void OnSnapToNearest(const CommandContext &context)
void OnSelToStart(const CommandContext &context)
void OnCursorLongJumpLeft(const CommandContext &context)
void OnSeekLeftShort(const CommandContext &context)
void OnSelectAllTime(const CommandContext &context)
void OnSnapToPrior(const CommandContext &context)
void OnSelectTrackStartToEnd(const CommandContext &context)
void OnCursorTrackEnd(const CommandContext &context)
void OnZeroCrossing(const CommandContext &context)
void OnCursorShortJumpRight(const CommandContext &context)
void OnCursorShortJumpLeft(const CommandContext &context)
void OnCursorTrackStart(const CommandContext &context)
void OnSelectNone(const CommandContext &context)
void OnSelSetExtendLeft(const CommandContext &context)
void OnSelectCursorEnd(const CommandContext &context)
void OnSelectSyncLockSel(const CommandContext &context)
Handler(const Handler &) PROHIBITED
void OnSelectCursorStoredCursor(const CommandContext &context)
void OnSelToEnd(const CommandContext &context)
void OnSelSetExtendRight(const CommandContext &context)
void OnCursorSelStart(const CommandContext &context)
void OnSkipStart(const CommandContext &context)
void OnSelectStartCursor(const CommandContext &context)
void OnSelectionRestore(const CommandContext &context)
void OnSeekRightLong(const CommandContext &context)
void OnSelExtendLeft(const CommandContext &context)
void OnSelectAllTracks(const CommandContext &context)
void OnSetRightSelection(const CommandContext &context)
void OnSeekLeftLong(const CommandContext &context)
void OnCursorPositionStore(const CommandContext &context)
void OnCursorSelEnd(const CommandContext &context)