Audacity 3.2.0
ClipMenus.cpp
Go to the documentation of this file.
1#include "../CommonCommandFlags.h"
2#include "ProjectHistory.h"
3#include "SyncLock.h"
4#include "TrackFocus.h"
5#include "Viewport.h"
6#include "UndoManager.h"
7#include "WaveClip.h"
8#include "WaveClipUtilities.h"
9#include "ViewInfo.h"
10#include "WaveTrack.h"
11#include "CommandContext.h"
12#include "MenuRegistry.h"
13#include "../tracks/ui/TimeShiftHandle.h"
14
15#include <cassert>
16
17// private helper classes and functions
18namespace {
19
20struct FoundTrack {
21 const WaveTrack* waveTrack{};
22 int trackNum{};
23
24 wxString ComposeTrackName() const
25 {
26 /* i18n-hint: The %d is replaced by the number of the track.*/
27 auto shortName = wxString::Format(_("Track %d"), trackNum)
28 .Append(" " + waveTrack->GetName());
29 return shortName;
30 }
31};
32
34 bool found{};
35 double startTime{};
36 double endTime{};
37 wxString name{};
38 int index{};
39};
40
42 int nFound{}; // 0, 1, or 2
43 double time{};
44 int index1{};
45 wxString name1{};
46 bool clipStart1{};
47 int index2{};
48 wxString name2{};
49 bool clipStart2{};
50};
51
52// When two clips are immediately next to each other, the GetPlayEndTime() of the
53// first clip and the GetPlayStartTime() of the second clip may not be exactly equal
54// due to rounding errors. When searching for the next/prev start time from a
55// given time, the following function adjusts that given time if necessary to
56// take this into account. If the given time is the end time of the first of two
57// clips which are next to each other, then the given time is changed to the
58// start time of the second clip. This ensures that the correct next/prev start
59// time is found.
61 const WaveTrack::IntervalConstHolders &clips, double time)
62{
63 auto q = std::find_if(clips.begin(), clips.end(),
64 [&] (const auto& clip) {
65 return clip->GetPlayEndTime() == time; });
66 if (q != clips.end() && q + 1 != clips.end() &&
68 time = (*(q+1))->GetPlayStartTime();
69 }
70
71 return time;
72}
73
74// When two clips are immediately next to each other, the GetPlayEndTime() of the
75// first clip and the GetPlayStartTime() of the second clip may not be exactly equal
76// due to rounding errors. When searching for the next/prev end time from a
77// given time, the following function adjusts that given time if necessary to
78// take this into account. If the given time is the start time of the second of
79// two clips which are next to each other, then the given time is changed to the
80// end time of the first clip. This ensures that the correct next/prev end time
81// is found.
83 const WaveTrack::IntervalConstHolders& clips, double time)
84{
85 auto q = std::find_if(clips.begin(), clips.end(),
86 [&] (auto& clip) {
87 return clip->GetPlayStartTime() == time; });
88 if (q != clips.end() && q != clips.begin() &&
90 time = (*(q-1))->GetPlayEndTime();
91 }
92
93 return time;
94}
95
97{
98 FoundClipBoundary result{};
99 result.waveTrack = wt;
100 const auto clips = wt->SortedIntervalArray();
101 double timeStart = AdjustForFindingStartTimes(clips, time);
102 double timeEnd = AdjustForFindingEndTimes(clips, time);
103
104 auto pStart = std::find_if(clips.begin(), clips.end(),
105 [&] (const auto& clip) {
106 return clip->GetPlayStartTime() > timeStart; });
107 auto pEnd = std::find_if(clips.begin(), clips.end(),
108 [&] (const auto& clip) {
109 return clip->GetPlayEndTime() > timeEnd; });
110
111 if (pStart != clips.end() && pEnd != clips.end()) {
112 if (WaveClipUtilities::SharesBoundaryWithNextClip(**pEnd, **pStart)) {
113 // boundary between two clips which are immediately next to each other.
114 result.nFound = 2;
115 result.time = (*pEnd)->GetPlayEndTime();
116 result.index1 = std::distance(clips.begin(), pEnd);
117 result.name1 = (*pEnd)->GetName();
118 result.clipStart1 = false;
119 result.index2 = std::distance(clips.begin(), pStart);
120 result.name2 = (*pStart)->GetName();
121 result.clipStart2 = true;
122 }
123 else if ((*pStart)->GetPlayStartTime() < (*pEnd)->GetPlayEndTime()) {
124 result.nFound = 1;
125 result.time = (*pStart)->GetPlayStartTime();
126 result.index1 = std::distance(clips.begin(), pStart);
127 result.name1 = (*pStart)->GetName();
128 result.clipStart1 = true;
129 }
130 else {
131 result.nFound = 1;
132 result.time = (*pEnd)->GetPlayEndTime();
133 result.index1 = std::distance(clips.begin(), pEnd);
134 result.name1 = (*pEnd)->GetName();
135 result.clipStart1 = false;
136 }
137 }
138 else if (pEnd != clips.end()) {
139 result.nFound = 1;
140 result.time = (*pEnd)->GetPlayEndTime();
141 result.index1 = std::distance(clips.begin(), pEnd);
142 result.name1 = (*pEnd)->GetName();
143 result.clipStart1 = false;
144 }
145
146 return result;
147}
148
150{
151 FoundClipBoundary result{};
152 result.waveTrack = wt;
153 const auto clips = wt->SortedIntervalArray();
154 double timeStart = AdjustForFindingStartTimes(clips, time);
155 double timeEnd = AdjustForFindingEndTimes(clips, time);
156
157 auto pStart = std::find_if(clips.rbegin(), clips.rend(),
158 [&] (const auto& clip) {
159 return clip->GetPlayStartTime() < timeStart; });
160 auto pEnd = std::find_if(clips.rbegin(), clips.rend(),
161 [&] (const auto& clip) {
162 return clip->GetPlayEndTime() < timeEnd; });
163
164 if (pStart != clips.rend() && pEnd != clips.rend()) {
165 if (WaveClipUtilities::SharesBoundaryWithNextClip(**pEnd, **pStart)) {
166 // boundary between two clips which are immediately next to each other.
167 result.nFound = 2;
168 result.time = (*pStart)->GetPlayStartTime();
169 result.index1 =
170 static_cast<int>(clips.size()) - 1 -
171 std::distance(clips.rbegin(), pStart);
172 result.name1 = (*pStart)->GetName();
173 result.clipStart1 = true;
174 result.index2 =
175 static_cast<int>(clips.size()) - 1 -
176 std::distance(clips.rbegin(), pEnd);
177 result.name2 = (*pEnd)->GetName();
178 result.clipStart2 = false;
179 }
180 else if ((*pStart)->GetPlayStartTime() > (*pEnd)->GetPlayEndTime()) {
181 result.nFound = 1;
182 result.time = (*pStart)->GetPlayStartTime();
183 result.index1 =
184 static_cast<int>(clips.size()) - 1 -
185 std::distance(clips.rbegin(), pStart);
186 result.name1 = (*pStart)->GetName();
187 result.clipStart1 = true;
188 }
189 else {
190 result.nFound = 1;
191 result.time = (*pEnd)->GetPlayEndTime();
192 result.index1 =
193 static_cast<int>(clips.size()) - 1 -
194 std::distance(clips.rbegin(), pEnd);
195 result.name1 = (*pEnd)->GetName();
196 result.clipStart1 = false;
197 }
198 }
199 else if (pStart != clips.rend()) {
200 result.nFound = 1;
201 result.time = (*pStart)->GetPlayStartTime();
202 result.index1 =
203 static_cast<int>(clips.size()) - 1 -
204 std::distance(clips.rbegin(), pStart);
205 result.name1 = (*pStart)->GetName();
206 result.clipStart1 = true;
207 }
208
209 return result;
210}
211
214 double time, bool next, std::vector<FoundClipBoundary>& finalResults)
215{
216 auto &tracks = TrackList::Get( project );
217 finalResults.clear();
218
219 bool anyWaveTracksSelected{ tracks.Selected<const WaveTrack>() };
220
221
222 // first search the tracks individually
223
224 std::vector<FoundClipBoundary> results;
225
226 int nTracksSearched = 0;
227 auto all = tracks.Any();
228 auto waveTracks = all.Filter<const WaveTrack>();
229 if (anyWaveTracksSelected)
230 waveTracks = waveTracks + &Track::GetSelected;
231 for (auto waveTrack : waveTracks) {
232 auto result = next ? FindNextClipBoundary(waveTrack, time) :
233 FindPrevClipBoundary(waveTrack, time);
234 if (result.nFound > 0) {
235 result.trackNum =
236 1 + std::distance(all.begin(), all.find(waveTrack));
237 results.push_back(result);
238 }
239
240 nTracksSearched++;
241 }
242
243
244 if (results.size() > 0) {
245 // If any clip boundaries were found
246 // find the clip boundary or boundaries with the min/max time
247 auto compare = [] (const FoundClipBoundary& a, const FoundClipBoundary&b)
248 { return a.time < b.time; };
249
250 auto p = next ? min_element(results.begin(), results.end(), compare ) :
251 max_element(results.begin(), results.end(), compare);
252
253 for ( auto &r : results )
254 if ( r.time == (*p).time )
255 finalResults.push_back( r );
256 }
257
258 return nTracksSearched; // can be used for screen reader messages if required
259}
260
261// for clip boundary commands, create a message for screen readers
263 const std::vector<FoundClipBoundary>& results)
264{
265 TranslatableString message;
266 for (auto& result : results) {
267
268 auto longName = result.ComposeTrackName();
269
271 auto nClips = result.waveTrack->GetNumClips();
272 if (result.nFound < 2) {
273 str = XP(
274 /* i18n-hint:
275 First %s is replaced with the noun "start" or "end"
276 identifying one end of a clip,
277 second string is the name of that clip,
278 first number gives the position of that clip in a sequence
279 of clips,
280 last number counts all clips,
281 and the last string is the name of the track containing the
282 clips.
283 */
284 "%s %s, %d of %d clip %s",
285 "%s %s, %d of %d clips %s",
286 3
287 )(
288 result.clipStart1 ? XO("start") : XO("end"),
289 result.name1,
290 result.index1 + 1,
291 nClips,
292 longName
293 );
294 }
295 else {
296 str = XP(
297 /* i18n-hint:
298 First and third %s are each replaced with the noun "start"
299 or with "end", identifying and end of a clip,
300 second and fourth strings are the names of those clips,
301 first and second numbers give the position of those clips in
302 a sequence of clips,
303 last number counts all clips,
304 and the last string is the name of the track containing the
305 clips.
306 */
307 "%s %s and %s %s, %d and %d of %d clip %s",
308 "%s %s and %s %s, %d and %d of %d clips %s",
309 6
310 )(
311 result.clipStart1 ? XO("start") : XO("end"),
312 result.name1,
313 result.clipStart2 ? XO("start") : XO("end"),
314 result.name2,
315 result.index1 + 1,
316 result.index2 + 1,
317 nClips,
318 longName
319 );
320 }
321
322 if (message.empty())
323 message = str;
324 else
325 message = XO("%s, %s").Format( message, str );
326 }
327
328 return message;
329}
330
332{
333 auto &selectedRegion = ViewInfo::Get( project ).selectedRegion;
334 auto &trackFocus = TrackFocus::Get( project );
335
336 std::vector<FoundClipBoundary> results;
337 FindClipBoundaries(project, next ? selectedRegion.t1() :
338 selectedRegion.t0(), next, results);
339
340 if (results.size() > 0) {
341 // note that if there is more than one result, each has the same time
342 // value.
343 if (next)
344 selectedRegion.setT1(results[0].time);
345 else
346 selectedRegion.setT0(results[0].time);
347
349
350 auto message = ClipBoundaryMessage(results);
351 trackFocus.MessageForScreenReader(message);
352 }
353}
354
355FoundClip FindNextClip(const WaveTrack* wt, double t0, double t1)
356{
357 FoundClip result{};
358 result.waveTrack = wt;
359 const auto clips = wt->SortedIntervalArray();
360
361 t0 = AdjustForFindingStartTimes(clips, t0);
362
363 {
364 auto p = std::find_if(clips.begin(), clips.end(),
365 [&] (const auto& clip) {
366 return clip->GetPlayStartTime() == t0; });
367 if (p != clips.end() && (*p)->GetPlayEndTime() > t1) {
368 result.found = true;
369 result.startTime = (*p)->GetPlayStartTime();
370 result.endTime = (*p)->GetPlayEndTime();
371 result.name = (*p)->GetName();
372 result.index = std::distance(clips.begin(), p);
373 return result;
374 }
375 }
376
377 {
378 auto p = std::find_if(clips.begin(), clips.end(),
379 [&] (const auto& clip) {
380 return clip->GetPlayStartTime() > t0; });
381 if (p != clips.end()) {
382 result.found = true;
383 result.startTime = (*p)->GetPlayStartTime();
384 result.endTime = (*p)->GetPlayEndTime();
385 result.name = (*p)->GetName();
386 result.index = std::distance(clips.begin(), p);
387 return result;
388 }
389 }
390
391 return result;
392}
393
394FoundClip FindPrevClip(const WaveTrack* wt, double t0, double t1)
395{
396 FoundClip result{};
397 result.waveTrack = wt;
398 const auto clips = wt->SortedIntervalArray();
399
400 t0 = AdjustForFindingStartTimes(clips, t0);
401
402 {
403 auto p = std::find_if(clips.begin(), clips.end(),
404 [&] (const auto& clip) {
405 return clip->GetPlayStartTime() == t0; });
406 if (p != clips.end() && (*p)->GetPlayEndTime() < t1) {
407 result.found = true;
408 result.startTime = (*p)->GetPlayStartTime();
409 result.endTime = (*p)->GetPlayEndTime();
410 result.name = (*p)->GetName();
411 result.index = std::distance(clips.begin(), p);
412 return result;
413 }
414 }
415
416 {
417 auto p = std::find_if(clips.rbegin(), clips.rend(),
418 [&] (const auto& clip) {
419 return clip->GetPlayStartTime() < t0; });
420 if (p != clips.rend()) {
421 result.found = true;
422 result.startTime = (*p)->GetPlayStartTime();
423 result.endTime = (*p)->GetPlayEndTime();
424 result.name = (*p)->GetName();
425 result.index =
426 static_cast<int>(clips.size()) - 1 -
427 std::distance(clips.rbegin(), p);
428 return result;
429 }
430 }
431
432 return result;
433}
434
437 double t0, double t1, bool next, std::vector<FoundClip>& finalResults)
438{
439 auto &tracks = TrackList::Get( project );
440 finalResults.clear();
441
442 bool anyWaveTracksSelected{ tracks.Selected<const WaveTrack>() };
443
444 // first search the tracks individually
445
446 std::vector<FoundClip> results;
447
448 int nTracksSearched = 0;
449 auto all = tracks.Any();
450 auto waveTracks = all.Filter<const WaveTrack>();
451 if (anyWaveTracksSelected)
452 waveTracks = waveTracks + &Track::GetSelected;
453 for (auto waveTrack : waveTracks) {
454 auto result = next ? FindNextClip(waveTrack, t0, t1) :
455 FindPrevClip(waveTrack, t0, t1);
456 if (result.found) {
457 result.trackNum =
458 1 + std::distance(all.begin(), all.find(waveTrack));
459 results.push_back(result);
460 }
461 nTracksSearched++;
462 }
463
464
465 if (results.size() > 0) {
466 // if any clips were found,
467 // find the clip or clips with the min/max start time
468 auto compareStart = [] (const FoundClip& a, const FoundClip& b)
469 { return a.startTime < b.startTime; };
470
471 auto pStart = next
472 ? std::min_element(results.begin(), results.end(), compareStart)
473 : std::max_element(results.begin(), results.end(), compareStart);
474
475 std::vector<FoundClip> resultsStartTime;
476 for ( auto &r : results )
477 if ( r.startTime == (*pStart).startTime )
478 resultsStartTime.push_back( r );
479
480 if (resultsStartTime.size() > 1) {
481 // more than one clip with same start time so
482 // find the clip or clips with the min/max end time
483 auto compareEnd = [] (const FoundClip& a, const FoundClip& b)
484 { return a.endTime < b.endTime; };
485
486 auto pEnd = next ? std::min_element(resultsStartTime.begin(),
487 resultsStartTime.end(), compareEnd) :
488 std::max_element(resultsStartTime.begin(),
489 resultsStartTime.end(), compareEnd);
490
491 for ( auto &r : resultsStartTime )
492 if ( r.endTime == (*pEnd).endTime )
493 finalResults.push_back( r );
494 }
495 else {
496 finalResults = resultsStartTime;
497 }
498 }
499
500 return nTracksSearched; // can be used for screen reader messages if required
501}
502
504{
505 auto &selectedRegion = ViewInfo::Get( project ).selectedRegion;
506 auto &trackFocus = TrackFocus::Get( project );
507 auto &viewport = Viewport::Get(project);
508
509 std::vector<FoundClip> results;
510 FindClips(project, selectedRegion.t0(),
511 selectedRegion.t1(), next, results);
512
513 if (results.size() > 0) {
514 // note that if there is more than one result, each has the same start
515 // and end time
516 double t0 = results[0].startTime;
517 double t1 = results[0].endTime;
518 selectedRegion.setTimes(t0, t1);
520 viewport.ScrollIntoView(selectedRegion.t0());
521
522 // create and send message to screen reader
523 TranslatableString message;
524 for (auto& result : results) {
525 auto longName = result.ComposeTrackName();
526 auto nClips = result.waveTrack->GetNumClips();
527 auto str = XP(
528 /* i18n-hint:
529 first string is the name of a clip,
530 first number gives the position of that clip
531 in a sequence of clips,
532 last number counts all clips,
533 last string names a track */
534 "%s, %d of %d clip %s",
535 "%s, %d of %d clips %s",
536 2
537 )(
538 result.name,
539 result.index + 1,
540 nClips,
541 longName
542 );
543
544 if (message.empty())
545 message = str;
546 else
547 message = XO("%s, %s").Format( message, str );
548 }
549 trackFocus.MessageForScreenReader(message);
550 }
551}
552
554(AudacityProject &project, bool next)
555{
556 auto &selectedRegion = ViewInfo::Get( project ).selectedRegion;
557 auto &trackFocus = TrackFocus::Get( project );
558 auto &viewport = Viewport::Get(project);
559
560 std::vector<FoundClipBoundary> results;
561 FindClipBoundaries(project, next ? selectedRegion.t1() :
562 selectedRegion.t0(), next, results);
563
564 if (results.size() > 0) {
565 // note that if there is more than one result, each has the same time
566 // value.
567 double time = results[0].time;
568 selectedRegion.setTimes(time, time);
570 viewport.ScrollIntoView(selectedRegion.t0());
571
572 auto message = ClipBoundaryMessage(results);
573 trackFocus.MessageForScreenReader(message);
574 }
575}
576
577// This function returns the amount moved. Possibly 0.0.
579 bool syncLocked, bool right)
580{
581 auto &trackFocus = TrackFocus::Get(project);
582 auto &viewInfo = ViewInfo::Get(project);
583 auto &selectedRegion = viewInfo.selectedRegion;
584
585 auto track = trackFocus.Get();
586 if (track) {
587 ClipMoveState state;
588
589 auto t0 = selectedRegion.t0();
590
591 std::unique_ptr<TrackShifter> uShifter;
592
593 auto hitTestResult = TrackShifter::HitTestResult::Track;
594 uShifter = MakeTrackShifter::Call(*track, project);
595 if ((hitTestResult = uShifter->HitTest(t0, viewInfo)) ==
597 return 0.0;
598
599 auto pShifter = uShifter.get();
600 auto desiredT0 = viewInfo.OffsetTimeByPixels(t0, (right ? 1 : -1));
601 auto desiredSlideAmount = pShifter->HintOffsetLarger(desiredT0 - t0);
602
603 state.Init(project, pShifter->GetTrack(), hitTestResult, move(uShifter),
604 t0, viewInfo, trackList, syncLocked);
605
606 auto hSlideAmount = state.DoSlideHorizontal(desiredSlideAmount);
607
608 double newT0 = t0 + hSlideAmount;
609 if (hitTestResult != TrackShifter::HitTestResult::Track) {
610 // If necessary, correct for rounding errors. For example,
611 // for a wavetrack, ensure that t0 is still in the clip
612 // which it was within before the move.
613 // (pShifter is still undestroyed in the ClipMoveState.)
614 newT0 = pShifter->AdjustT0(newT0);
615 }
616
617 double diff = selectedRegion.duration();
618 selectedRegion.setTimes(newT0, newT0 + diff);
619
620 return hSlideAmount;
621 };
622 return 0.0;
623}
624
626(AudacityProject &project, bool right, bool keyUp )
627{
628 auto &undoManager = UndoManager::Get( project );
629 auto &viewport = Viewport::Get(project);
630
631 if (keyUp) {
632 undoManager.StopConsolidating();
633 return;
634 }
635
636 auto &trackFocus = TrackFocus::Get( project );
637 auto &viewInfo = ViewInfo::Get( project );
638 auto &selectedRegion = viewInfo.selectedRegion;
639 auto &tracks = TrackList::Get( project );
640 auto isSyncLocked = SyncLockState::Get(project).IsSyncLocked();
641
642 auto amount = DoClipMove(project, tracks, isSyncLocked, right);
643
644 viewport.ScrollIntoView(selectedRegion.t0());
645
646 if (amount != 0.0) {
647 auto message = right? XO("Moved clips to the right") :
648 XO("Moved clips to the left");
649
650 // The following use of the UndoPush flags is so that both a single
651 // keypress (keydown, then keyup), and holding down a key
652 // (multiple keydowns followed by a keyup) result in a single
653 // entry in Audacity's history dialog.
655 .PushState(message, XO("Move audio clips"), UndoPush::CONSOLIDATE);
656 }
657
658 if ( amount == 0.0 )
659 trackFocus.MessageForScreenReader( XO("clip not moved"));
660}
661
662}
663
665namespace {
666
667// exported helper functions
668// none
669
670// Menu handler functions
671
673(const CommandContext &context)
674{
675 auto &project = context.project;
677}
678
680(const CommandContext &context)
681{
682 auto &project = context.project;
684}
685
687{
688 auto &project = context.project;
689 DoSelectClip(project, false);
690}
691
693{
694 auto &project = context.project;
695 DoSelectClip(project, true);
696}
697
699{
701
703}
704
706{
708
710}
711
712// PRL: Clip moving functions -- more than just selection adjustment. Do they
713// really belong in these navigation menus?
714void OnClipLeft(const CommandContext &context)
715{
716 auto &project = context.project;
717 auto evt = context.pEvt;
718 if (evt)
719 DoClipLeftOrRight( project, false, evt->GetEventType() == wxEVT_KEY_UP );
720 else { // called from menu, so simulate keydown and keyup
721 DoClipLeftOrRight( project, false, false );
722 DoClipLeftOrRight( project, false, true );
723 }
724}
725
726void OnClipRight(const CommandContext &context)
727{
728 auto &project = context.project;
729 auto evt = context.pEvt;
730 if (evt)
731 DoClipLeftOrRight( project, true, evt->GetEventType() == wxEVT_KEY_UP );
732 else { // called from menu, so simulate keydown and keyup
733 DoClipLeftOrRight( project, true, false );
734 DoClipLeftOrRight( project, true, true );
735 }
736}
737
738// Menu definitions
739
740using namespace MenuRegistry;
741
742// Register menu items
743
745{
746 static auto menu = std::shared_ptr{
747 Menu( wxT("Clip"), XXO("Audi&o Clips"),
748 Command( wxT("SelPrevClipBoundaryToCursor"),
749 XXO("Pre&vious Clip Boundary to Cursor"),
752 Command( wxT("SelCursorToNextClipBoundary"),
753 XXO("Cursor to Ne&xt Clip Boundary"),
756 Command( wxT("SelPrevClip"), XXO("Previo&us Clip"),
758 Options{ wxT("Alt+,"), XO("Select Previous Clip") } ),
759 Command( wxT("SelNextClip"), XXO("N&ext Clip"), OnSelectNextClip,
761 Options{ wxT("Alt+."), XO("Select Next Clip") } )
762 ) };
763 return menu;
764}
765
767
769{
770 static auto items = std::shared_ptr{
771 Items( wxT("Clip"),
772 Command( wxT("CursPrevClipBoundary"), XXO("Pre&vious Clip Boundary"),
775 Options{}.LongName( XO("Cursor to Prev Clip Boundary") ) ),
776 Command( wxT("CursNextClipBoundary"), XXO("Ne&xt Clip Boundary"),
779 Options{}.LongName( XO("Cursor to Next Clip Boundary") ) )
780 ) };
781 return items;
782}
783
785 { wxT("Transport/Basic/Cursor"),
786 { OrderingHint::Before, wxT("CursProjectStart") } }
787};
788
790{
791 static auto items = std::shared_ptr{
792 Items( wxT("TimeShift"),
793 Command( wxT("ClipLeft"), XXO("Time Shift &Left"), OnClipLeft,
795 Command( wxT("ClipRight"), XXO("Time Shift &Right"), OnClipRight,
797 ) };
798 return items;
799}
800
802 { wxT("Optional/Extra/Part1/Edit"), { OrderingHint::End, {} } }
803};
804
805}
wxT("CloseDown"))
AttachedItem sAttachment1
AttachedItem sAttachment3
AttachedItem sAttachment2
const ReservedCommandFlag & TracksExistFlag()
const ReservedCommandFlag & WaveTracksExistFlag()
const ReservedCommandFlag & TrackPanelHasFocus()
#define str(a)
const TranslatableString name
Definition: Distortion.cpp:76
XO("Cut/Copy/Paste")
XXO("&Cut/Copy/Paste Toolbar")
#define XP(sing, plur, n)
Definition: Internat.h:94
#define _(s)
Definition: Internat.h:73
const auto tracks
const auto project
static Return Call(This &obj, Arguments ...arguments)
Invoke the method – but only after static initialization time.
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:90
CommandContext provides additional information to an 'Apply()' command. It provides the project,...
const wxEvent * pEvt
AudacityProject & project
void PushState(const TranslatableString &desc, const TranslatableString &shortDesc)
void ModifyState(bool bWantsAutoSave)
static ProjectHistory & Get(AudacityProject &project)
Generates classes whose instances register items at construction.
Definition: Registry.h:388
bool IsSyncLocked() const
Definition: SyncLock.cpp:44
static SyncLockState & Get(AudacityProject &project)
Definition: SyncLock.cpp:27
Track * Get()
Definition: TrackFocus.cpp:156
bool GetSelected() const
Selectedness is always the same for all channels of a group.
Definition: Track.cpp:78
A flat linked list of tracks supporting Add, Remove, Clear, and Contains, serialization of the list o...
Definition: Track.h:850
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:314
@ Track
Shift selected track and sister channels only, as a whole.
@ Miss
Don't shift anything.
Holds a msgid for the translation catalog; may also bind format arguments.
static UndoManager & Get(AudacityProject &project)
Definition: UndoManager.cpp:71
NotifyingSelectedRegion selectedRegion
Definition: ViewInfo.h:216
static ViewInfo & Get(AudacityProject &project)
Definition: ViewInfo.cpp:235
static Viewport & Get(AudacityProject &project)
Definition: Viewport.cpp:33
A Track that contains audio waveform data.
Definition: WaveTrack.h:203
IntervalHolders SortedIntervalArray()
Return all WaveClips sorted by clip play start time.
Definition: WaveTrack.cpp:3270
std::vector< IntervalConstHolder > IntervalConstHolders
Definition: WaveTrack.h:212
constexpr auto Items
Definition: MenuRegistry.h:427
constexpr auto Command
Definition: MenuRegistry.h:456
constexpr auto Menu
Items will appear in a main toolbar menu or in a sub-menu.
Definition: MenuRegistry.h:445
std::unique_ptr< detail::IndirectItem< Item > > Indirect(const std::shared_ptr< Item > &ptr)
A convenience function.
Definition: Registry.h:175
WAVE_TRACK_API bool SharesBoundaryWithNextClip(const WaveTrack::Interval &prev, const WaveTrack::Interval &next)
used by commands which interact with clips using the keyboard
int FindClipBoundaries(AudacityProject &project, double time, bool next, std::vector< FoundClipBoundary > &finalResults)
Definition: ClipMenus.cpp:213
FoundClip FindPrevClip(const WaveTrack *wt, double t0, double t1)
Definition: ClipMenus.cpp:394
void DoCursorClipBoundary(AudacityProject &project, bool next)
Definition: ClipMenus.cpp:554
void OnSelectPrevClip(const CommandContext &context)
Definition: ClipMenus.cpp:686
FoundClipBoundary FindNextClipBoundary(const WaveTrack *wt, double time)
Definition: ClipMenus.cpp:96
int FindClips(AudacityProject &project, double t0, double t1, bool next, std::vector< FoundClip > &finalResults)
Definition: ClipMenus.cpp:436
FoundClipBoundary FindPrevClipBoundary(const WaveTrack *wt, double time)
Definition: ClipMenus.cpp:149
void OnClipLeft(const CommandContext &context)
Definition: ClipMenus.cpp:714
double AdjustForFindingStartTimes(const WaveTrack::IntervalConstHolders &clips, double time)
Definition: ClipMenus.cpp:60
void OnSelectCursorToNextClipBoundary(const CommandContext &context)
Definition: ClipMenus.cpp:680
TranslatableString ClipBoundaryMessage(const std::vector< FoundClipBoundary > &results)
Definition: ClipMenus.cpp:262
void DoSelectClipBoundary(AudacityProject &project, bool next)
Definition: ClipMenus.cpp:331
double AdjustForFindingEndTimes(const WaveTrack::IntervalConstHolders &clips, double time)
Definition: ClipMenus.cpp:82
void OnCursorPrevClipBoundary(const CommandContext &context)
Definition: ClipMenus.cpp:698
double DoClipMove(AudacityProject &project, TrackList &trackList, bool syncLocked, bool right)
Definition: ClipMenus.cpp:578
void DoClipLeftOrRight(AudacityProject &project, bool right, bool keyUp)
Definition: ClipMenus.cpp:626
FoundClip FindNextClip(const WaveTrack *wt, double t0, double t1)
Definition: ClipMenus.cpp:355
void DoSelectClip(AudacityProject &project, bool next)
Definition: ClipMenus.cpp:503
void OnCursorNextClipBoundary(const CommandContext &context)
Definition: ClipMenus.cpp:705
void OnSelectNextClip(const CommandContext &context)
Definition: ClipMenus.cpp:692
void OnClipRight(const CommandContext &context)
Definition: ClipMenus.cpp:726
void OnSelectPrevClipBoundaryToCursor(const CommandContext &context)
Definition: ClipMenus.cpp:673
double DoSlideHorizontal(double desiredSlideAmount)
Do sliding of tracks and intervals, maybe adjusting the offset.
void Init(AudacityProject &project, Track &capturedTrack, TrackShifter::HitTestResult hitTestResult, std::unique_ptr< TrackShifter > pHit, double clickTime, const ViewInfo &viewInfo, TrackList &trackList, bool syncLocked)
Will associate a TrackShifter with each track in the list.
Options && LongName(const TranslatableString &value) &&
Definition: MenuRegistry.h:54
Options && WantKeyUp() &&
Definition: MenuRegistry.h:60