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