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