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 "../TrackPanelAx.h"
5#include "../ProjectWindow.h"
6#include "UndoManager.h"
7#include "WaveClip.h"
8#include "ViewInfo.h"
9#include "WaveTrack.h"
10#include "../commands/CommandContext.h"
11#include "../commands/CommandManager.h"
12#include "../tracks/ui/TimeShiftHandle.h"
13
14#include <cassert>
15
16// private helper classes and functions
17namespace {
18
19struct FoundTrack {
20 const WaveTrack* waveTrack{};
21 int trackNum{};
22
23 wxString ComposeTrackName() const
24 {
25 /* i18n-hint: The %d is replaced by the number of the track.*/
26 auto shortName = wxString::Format(_("Track %d"), trackNum)
27 .Append(" " + waveTrack->GetName());
28 return shortName;
29 }
30};
31
33 bool found{};
34 double startTime{};
35 double endTime{};
36 wxString name{};
37 int index{};
38};
39
41 int nFound{}; // 0, 1, or 2
42 double time{};
43 int index1{};
44 wxString name1{};
45 bool clipStart1{};
46 int index2{};
47 wxString name2{};
48 bool clipStart2{};
49};
50
51// When two clips are immediately next to each other, the GetPlayEndTime() of the
52// first clip and the GetPlayStartTime() of the second clip may not be exactly equal
53// due to rounding errors. When searching for the next/prev start time from a
54// given time, the following function adjusts that given time if necessary to
55// take this into account. If the given time is the end time of the first of two
56// clips which are next to each other, then the given time is changed to the
57// start time of the second clip. This ensures that the correct next/prev start
58// time is found.
60 const std::vector<const WaveClip*> & clips, double time)
61{
62 auto q = std::find_if(clips.begin(), clips.end(),
63 [&] (const WaveClip* const& clip) {
64 return clip->GetPlayEndTime() == time; });
65 if (q != clips.end() && q + 1 != clips.end() &&
66 (*q)->SharesBoundaryWithNextClip(*(q+1))) {
67 time = (*(q+1))->GetPlayStartTime();
68 }
69
70 return time;
71}
72
73// When two clips are immediately next to each other, the GetPlayEndTime() of the
74// first clip and the GetPlayStartTime() of the second clip may not be exactly equal
75// due to rounding errors. When searching for the next/prev end time from a
76// given time, the following function adjusts that given time if necessary to
77// take this into account. If the given time is the start time of the second of
78// two clips which are next to each other, then the given time is changed to the
79// end time of the first clip. This ensures that the correct next/prev end time
80// is found.
82 const std::vector<const WaveClip*>& clips, double time)
83{
84 auto q = std::find_if(clips.begin(), clips.end(),
85 [&] (const WaveClip* const& clip) {
86 return clip->GetPlayStartTime() == time; });
87 if (q != clips.end() && q != clips.begin() &&
88 (*(q - 1))->SharesBoundaryWithNextClip(*q)) {
89 time = (*(q-1))->GetPlayEndTime();
90 }
91
92 return time;
93}
94
96(const WaveTrack* wt, double time)
97{
98 FoundClipBoundary result{};
99 result.waveTrack = wt;
100 const auto clips = wt->SortedClipArray();
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 WaveClip* const& clip) {
106 return clip->GetPlayStartTime() > timeStart; });
107 auto pEnd = std::find_if(clips.begin(), clips.end(),
108 [&] (const WaveClip* const& clip) {
109 return clip->GetPlayEndTime() > timeEnd; });
110
111 if (pStart != clips.end() && pEnd != clips.end()) {
112 if ((*pEnd)->SharesBoundaryWithNextClip(*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->SortedClipArray();
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 WaveClip* const& clip) {
159 return clip->GetPlayStartTime() < timeStart; });
160 auto pEnd = std::find_if(clips.rbegin(), clips.rend(),
161 [&] (const WaveClip* const& clip) {
162 return clip->GetPlayEndTime() < timeEnd; });
163
164 if (pStart != clips.rend() && pEnd != clips.rend()) {
165 if ((*pEnd)->SharesBoundaryWithNextClip(*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 leaders = tracks.Any();
228 auto rangeLeaders = leaders.Filter<const WaveTrack>();
229 if (anyWaveTracksSelected)
230 rangeLeaders = rangeLeaders + &Track::GetSelected;
231 for (auto waveTrack : rangeLeaders) {
232 auto result = next ? FindNextClipBoundary(waveTrack, time) :
233 FindPrevClipBoundary(waveTrack, time);
234 if (result.nFound > 0) {
235 result.trackNum =
236 1 + std::distance(leaders.begin(), leaders.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
356(AudacityProject &project, const WaveTrack* wt, double t0, double t1)
357{
358 (void)project;//Compiler food.
359
360 FoundClip result{};
361 result.waveTrack = wt;
362 const auto clips = wt->SortedClipArray();
363
364 t0 = AdjustForFindingStartTimes(clips, t0);
365
366 {
367 auto p = std::find_if(clips.begin(), clips.end(),
368 [&] (const WaveClip* const& clip) {
369 return clip->GetPlayStartTime() == t0; });
370 if (p != clips.end() && (*p)->GetPlayEndTime() > t1) {
371 result.found = true;
372 result.startTime = (*p)->GetPlayStartTime();
373 result.endTime = (*p)->GetPlayEndTime();
374 result.name = (*p)->GetName();
375 result.index = std::distance(clips.begin(), p);
376 return result;
377 }
378 }
379
380 {
381 auto p = std::find_if(clips.begin(), clips.end(),
382 [&] (const WaveClip* const& clip) {
383 return clip->GetPlayStartTime() > t0; });
384 if (p != clips.end()) {
385 result.found = true;
386 result.startTime = (*p)->GetPlayStartTime();
387 result.endTime = (*p)->GetPlayEndTime();
388 result.name = (*p)->GetName();
389 result.index = std::distance(clips.begin(), p);
390 return result;
391 }
392 }
393
394 return result;
395}
396
398(AudacityProject &project, const WaveTrack* wt, double t0, double t1)
399{
400 (void)project;//Compiler food.
401
402 FoundClip result{};
403 result.waveTrack = wt;
404 const auto clips = wt->SortedClipArray();
405
406 t0 = AdjustForFindingStartTimes(clips, t0);
407
408 {
409 auto p = std::find_if(clips.begin(), clips.end(),
410 [&] (const WaveClip* const& clip) {
411 return clip->GetPlayStartTime() == t0; });
412 if (p != clips.end() && (*p)->GetPlayEndTime() < t1) {
413 result.found = true;
414 result.startTime = (*p)->GetPlayStartTime();
415 result.endTime = (*p)->GetPlayEndTime();
416 result.name = (*p)->GetName();
417 result.index = std::distance(clips.begin(), p);
418 return result;
419 }
420 }
421
422 {
423 auto p = std::find_if(clips.rbegin(), clips.rend(),
424 [&] (const WaveClip* const& clip) {
425 return clip->GetPlayStartTime() < t0; });
426 if (p != clips.rend()) {
427 result.found = true;
428 result.startTime = (*p)->GetPlayStartTime();
429 result.endTime = (*p)->GetPlayEndTime();
430 result.name = (*p)->GetName();
431 result.index =
432 static_cast<int>(clips.size()) - 1 -
433 std::distance(clips.rbegin(), p);
434 return result;
435 }
436 }
437
438 return result;
439}
440
443 double t0, double t1, bool next, std::vector<FoundClip>& finalResults)
444{
445 auto &tracks = TrackList::Get( project );
446 finalResults.clear();
447
448 bool anyWaveTracksSelected{ tracks.Selected<const WaveTrack>() };
449
450 // first search the tracks individually
451
452 std::vector<FoundClip> results;
453
454 int nTracksSearched = 0;
455 auto leaders = tracks.Any();
456 auto rangeLeaders = leaders.Filter<const WaveTrack>();
457 if (anyWaveTracksSelected)
458 rangeLeaders = rangeLeaders + &Track::GetSelected;
459 for (auto waveTrack : rangeLeaders) {
460 auto result = next ? FindNextClip(project, waveTrack, t0, t1) :
461 FindPrevClip(project, waveTrack, t0, t1);
462 if (result.found) {
463 result.trackNum =
464 1 + std::distance(leaders.begin(), leaders.find(waveTrack));
465 results.push_back(result);
466 }
467 nTracksSearched++;
468 }
469
470
471 if (results.size() > 0) {
472 // if any clips were found,
473 // find the clip or clips with the min/max start time
474 auto compareStart = [] (const FoundClip& a, const FoundClip& b)
475 { return a.startTime < b.startTime; };
476
477 auto pStart = next
478 ? std::min_element(results.begin(), results.end(), compareStart)
479 : std::max_element(results.begin(), results.end(), compareStart);
480
481 std::vector<FoundClip> resultsStartTime;
482 for ( auto &r : results )
483 if ( r.startTime == (*pStart).startTime )
484 resultsStartTime.push_back( r );
485
486 if (resultsStartTime.size() > 1) {
487 // more than one clip with same start time so
488 // find the clip or clips with the min/max end time
489 auto compareEnd = [] (const FoundClip& a, const FoundClip& b)
490 { return a.endTime < b.endTime; };
491
492 auto pEnd = next ? std::min_element(resultsStartTime.begin(),
493 resultsStartTime.end(), compareEnd) :
494 std::max_element(resultsStartTime.begin(),
495 resultsStartTime.end(), compareEnd);
496
497 for ( auto &r : resultsStartTime )
498 if ( r.endTime == (*pEnd).endTime )
499 finalResults.push_back( r );
500 }
501 else {
502 finalResults = resultsStartTime;
503 }
504 }
505
506 return nTracksSearched; // can be used for screen reader messages if required
507}
508
510{
511 auto &selectedRegion = ViewInfo::Get( project ).selectedRegion;
512 auto &trackFocus = TrackFocus::Get( project );
513 auto &window = ProjectWindow::Get( project );
514
515 std::vector<FoundClip> results;
516 FindClips(project, selectedRegion.t0(),
517 selectedRegion.t1(), next, results);
518
519 if (results.size() > 0) {
520 // note that if there is more than one result, each has the same start
521 // and end time
522 double t0 = results[0].startTime;
523 double t1 = results[0].endTime;
524 selectedRegion.setTimes(t0, t1);
526 window.ScrollIntoView(selectedRegion.t0());
527
528 // create and send message to screen reader
529 TranslatableString message;
530 for (auto& result : results) {
531 auto longName = result.ComposeTrackName();
532 auto nClips = result.waveTrack->GetNumClips();
533 auto str = XP(
534 /* i18n-hint:
535 first string is the name of a clip,
536 first number gives the position of that clip
537 in a sequence of clips,
538 last number counts all clips,
539 last string names a track */
540 "%s, %d of %d clip %s",
541 "%s, %d of %d clips %s",
542 2
543 )(
544 result.name,
545 result.index + 1,
546 nClips,
547 longName
548 );
549
550 if (message.empty())
551 message = str;
552 else
553 message = XO("%s, %s").Format( message, str );
554 }
555 trackFocus.MessageForScreenReader(message);
556 }
557}
558
560(AudacityProject &project, bool next)
561{
562 auto &selectedRegion = ViewInfo::Get( project ).selectedRegion;
563 auto &trackFocus = TrackFocus::Get( project );
564 auto &window = ProjectWindow::Get( project );
565
566 std::vector<FoundClipBoundary> results;
567 FindClipBoundaries(project, next ? selectedRegion.t1() :
568 selectedRegion.t0(), next, results);
569
570 if (results.size() > 0) {
571 // note that if there is more than one result, each has the same time
572 // value.
573 double time = results[0].time;
574 selectedRegion.setTimes(time, time);
576 window.ScrollIntoView(selectedRegion.t0());
577
578 auto message = ClipBoundaryMessage(results);
579 trackFocus.MessageForScreenReader(message);
580 }
581}
582
583// This function returns the amount moved. Possibly 0.0.
585 bool syncLocked, bool right)
586{
587 auto &trackFocus = TrackFocus::Get(project);
588 auto &viewInfo = ViewInfo::Get(project);
589 auto &selectedRegion = viewInfo.selectedRegion;
590
591 auto track = trackFocus.Get();
592 if (track) {
593 // Focus is always a leader,
594 // satisfying the pre of MakeTrackShifter
595 assert(track->IsLeader());
596 ClipMoveState state;
597
598 auto t0 = selectedRegion.t0();
599
600 std::unique_ptr<TrackShifter> uShifter;
601
602 auto hitTestResult = TrackShifter::HitTestResult::Track;
603 uShifter = MakeTrackShifter::Call(*track, project);
604 if ((hitTestResult = uShifter->HitTest(t0, viewInfo)) ==
606 return 0.0;
607
608 auto pShifter = uShifter.get();
609 auto desiredT0 = viewInfo.OffsetTimeByPixels(t0, (right ? 1 : -1));
610 auto desiredSlideAmount = pShifter->HintOffsetLarger(desiredT0 - t0);
611
612 state.Init(project, pShifter->GetTrack(), hitTestResult, move(uShifter),
613 t0, viewInfo, trackList, syncLocked);
614
615 auto hSlideAmount = state.DoSlideHorizontal(desiredSlideAmount);
616
617 double newT0 = t0 + hSlideAmount;
618 if (hitTestResult != TrackShifter::HitTestResult::Track) {
619 // If necessary, correct for rounding errors. For example,
620 // for a wavetrack, ensure that t0 is still in the clip
621 // which it was within before the move.
622 // (pShifter is still undestroyed in the ClipMoveState.)
623 newT0 = pShifter->AdjustT0(newT0);
624 }
625
626 double diff = selectedRegion.duration();
627 selectedRegion.setTimes(newT0, newT0 + diff);
628
629 return hSlideAmount;
630 };
631 return 0.0;
632}
633
635(AudacityProject &project, bool right, bool keyUp )
636{
637 auto &undoManager = UndoManager::Get( project );
638 auto &window = ProjectWindow::Get( project );
639
640 if (keyUp) {
641 undoManager.StopConsolidating();
642 return;
643 }
644
645 auto &trackFocus = TrackFocus::Get( project );
646 auto &viewInfo = ViewInfo::Get( project );
647 auto &selectedRegion = viewInfo.selectedRegion;
648 auto &tracks = TrackList::Get( project );
649 auto isSyncLocked = SyncLockState::Get(project).IsSyncLocked();
650
651 auto amount = DoClipMove(project, tracks, isSyncLocked, right);
652
653 window.ScrollIntoView(selectedRegion.t0());
654
655 if (amount != 0.0) {
656 auto message = right? XO("Moved clips to the right") :
657 XO("Moved clips to the left");
658
659 // The following use of the UndoPush flags is so that both a single
660 // keypress (keydown, then keyup), and holding down a key
661 // (multiple keydowns followed by a keyup) result in a single
662 // entry in Audacity's history dialog.
664 .PushState(message, XO("Move audio clips"), UndoPush::CONSOLIDATE);
665 }
666
667 if ( amount == 0.0 )
668 trackFocus.MessageForScreenReader( XO("clip not moved"));
669}
670
671}
672
674namespace {
675
676// exported helper functions
677// none
678
679// Menu handler functions
680
682(const CommandContext &context)
683{
684 auto &project = context.project;
686}
687
689(const CommandContext &context)
690{
691 auto &project = context.project;
693}
694
696{
697 auto &project = context.project;
698 DoSelectClip(project, false);
699}
700
702{
703 auto &project = context.project;
704 DoSelectClip(project, true);
705}
706
708{
710
712}
713
715{
717
719}
720
721// PRL: Clip moving functions -- more than just selection adjustment. Do they
722// really belong in these navigation menus?
723void OnClipLeft(const CommandContext &context)
724{
725 auto &project = context.project;
726 auto evt = context.pEvt;
727 if (evt)
728 DoClipLeftOrRight( project, false, evt->GetEventType() == wxEVT_KEY_UP );
729 else { // called from menu, so simulate keydown and keyup
730 DoClipLeftOrRight( project, false, false );
731 DoClipLeftOrRight( project, false, true );
732 }
733}
734
735void OnClipRight(const CommandContext &context)
736{
737 auto &project = context.project;
738 auto evt = context.pEvt;
739 if (evt)
740 DoClipLeftOrRight( project, true, evt->GetEventType() == wxEVT_KEY_UP );
741 else { // called from menu, so simulate keydown and keyup
742 DoClipLeftOrRight( project, true, false );
743 DoClipLeftOrRight( project, true, true );
744 }
745}
746
747// Menu definitions
748
749using namespace MenuTable;
750
751// Register menu items
752
754{
756
757 static BaseItemSharedPtr menu {
758 Menu( wxT("Clip"), XXO("Audi&o Clips"),
759 Command( wxT("SelPrevClipBoundaryToCursor"),
760 XXO("Pre&vious Clip Boundary to Cursor"),
763 Command( wxT("SelCursorToNextClipBoundary"),
764 XXO("Cursor to Ne&xt Clip Boundary"),
767 Command( wxT("SelPrevClip"), XXO("Previo&us Clip"),
769 Options{ wxT("Alt+,"), XO("Select Previous Clip") } ),
770 Command( wxT("SelNextClip"), XXO("N&ext Clip"), OnSelectNextClip,
772 Options{ wxT("Alt+."), XO("Select Next Clip") } )
773 ) };
774 return menu;
775}
776
778 wxT("Select/Basic"),
780};
781
783{
785
786 static BaseItemSharedPtr items{
787 Items( wxT("Clip"),
788 Command( wxT("CursPrevClipBoundary"), XXO("Pre&vious Clip Boundary"),
791 Options{}.LongName( XO("Cursor to Prev Clip Boundary") ) ),
792 Command( wxT("CursNextClipBoundary"), XXO("Ne&xt Clip Boundary"),
795 Options{}.LongName( XO("Cursor to Next Clip Boundary") ) )
796 ) };
797 return items;
798}
799
801 { wxT("Transport/Basic/Cursor"),
802 { OrderingHint::Before, wxT("CursProjectStart") } },
804};
805
807{
809 static BaseItemSharedPtr items{
810 Items( wxT("TimeShift"),
811 Command( wxT("ClipLeft"), XXO("Time Shift &Left"), OnClipLeft,
813 Command( wxT("ClipRight"), XXO("Time Shift &Right"), OnClipRight,
815 ) };
816 return items;
817}
818
820 { wxT("Optional/Extra/Part1/Edit"), { OrderingHint::End, {} } },
822};
823
824}
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)
static ProjectWindow & Get(AudacityProject &project)
bool IsSyncLocked() const
Definition: SyncLock.cpp:43
static SyncLockState & Get(AudacityProject &project)
Definition: SyncLock.cpp:26
Track * Get()
bool GetSelected() const
Selectedness is always the same for all channels of a group.
Definition: Track.cpp:70
A flat linked list of tracks supporting Add, Remove, Clear, and Contains, serialization of the list o...
Definition: Track.h:987
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:354
@ 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:219
static ViewInfo & Get(AudacityProject &project)
Definition: ViewInfo.cpp:235
This allows multiple clips to be a part of one WaveTrack.
Definition: WaveClip.h:103
A Track that contains audio waveform data.
Definition: WaveTrack.h:220
WaveClipPointers SortedClipArray()
Definition: WaveTrack.cpp:3975
constexpr auto Menu
Items will appear in a main toolbar menu or in a sub-menu.
constexpr auto Items
constexpr auto Command
std::unique_ptr< detail::IndirectItem< Item > > Indirect(const std::shared_ptr< Item > &ptr)
A convenience function.
Definition: Registry.h:113
std::shared_ptr< BaseItem > BaseItemSharedPtr
Definition: Registry.h:78
double AdjustForFindingStartTimes(const std::vector< const WaveClip * > &clips, double time)
Definition: ClipMenus.cpp:59
BaseItemSharedPtr ExtraTimeShiftItems()
Definition: ClipMenus.cpp:806
int FindClipBoundaries(AudacityProject &project, double time, bool next, std::vector< FoundClipBoundary > &finalResults)
Definition: ClipMenus.cpp:213
BaseItemSharedPtr ClipSelectMenu()
Definition: ClipMenus.cpp:753
void DoCursorClipBoundary(AudacityProject &project, bool next)
Definition: ClipMenus.cpp:560
void OnSelectPrevClip(const CommandContext &context)
Definition: ClipMenus.cpp:695
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:442
FoundClip FindNextClip(AudacityProject &project, const WaveTrack *wt, double t0, double t1)
Definition: ClipMenus.cpp:356
BaseItemSharedPtr ClipCursorItems()
Definition: ClipMenus.cpp:782
FoundClipBoundary FindPrevClipBoundary(const WaveTrack *wt, double time)
Definition: ClipMenus.cpp:149
void OnClipLeft(const CommandContext &context)
Definition: ClipMenus.cpp:723
void OnSelectCursorToNextClipBoundary(const CommandContext &context)
Definition: ClipMenus.cpp:689
TranslatableString ClipBoundaryMessage(const std::vector< FoundClipBoundary > &results)
Definition: ClipMenus.cpp:262
void DoSelectClipBoundary(AudacityProject &project, bool next)
Definition: ClipMenus.cpp:331
double AdjustForFindingEndTimes(const std::vector< const WaveClip * > &clips, double time)
Definition: ClipMenus.cpp:81
void OnCursorPrevClipBoundary(const CommandContext &context)
Definition: ClipMenus.cpp:707
double DoClipMove(AudacityProject &project, TrackList &trackList, bool syncLocked, bool right)
Definition: ClipMenus.cpp:584
void DoClipLeftOrRight(AudacityProject &project, bool right, bool keyUp)
Definition: ClipMenus.cpp:635
void DoSelectClip(AudacityProject &project, bool next)
Definition: ClipMenus.cpp:509
void OnCursorNextClipBoundary(const CommandContext &context)
Definition: ClipMenus.cpp:714
void OnSelectNextClip(const CommandContext &context)
Definition: ClipMenus.cpp:701
FoundClip FindPrevClip(AudacityProject &project, const WaveTrack *wt, double t0, double t1)
Definition: ClipMenus.cpp:398
void OnClipRight(const CommandContext &context)
Definition: ClipMenus.cpp:735
void OnSelectPrevClipBoundaryToCursor(const CommandContext &context)
Definition: ClipMenus.cpp:682
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 && WantKeyUp() &&
Options && LongName(const TranslatableString &value) &&