Audacity  3.0.3
ClipMenus.cpp
Go to the documentation of this file.
1 #include "../CommonCommandFlags.h"
2 #include "../ProjectHistory.h"
3 #include "../ProjectSettings.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 // private helper classes and functions
15 namespace {
16 
17 struct FoundTrack {
18  const WaveTrack* waveTrack{};
19  int trackNum{};
20  bool channel{};
21 
23  {
24  auto name = waveTrack->GetName();
25  auto shortName = name == waveTrack->GetDefaultName()
26  /* i18n-hint: compose a name identifying an unnamed track by number */
27  ? XO("Track %d").Format( trackNum )
28  : Verbatim(name);
29  auto longName = shortName;
30  if (channel) {
31  // TODO: more-than-two-channels-message
32  if ( waveTrack->IsLeader() )
33  /* i18n-hint: given the name of a track, specify its left channel */
34  longName = XO("%s left").Format(shortName);
35  else
36  /* i18n-hint: given the name of a track, specify its right channel */
37  longName = XO("%s right").Format(shortName);
38  }
39  return longName;
40  }
41 };
42 
44  bool found{};
45  double startTime{};
46  double endTime{};
47  wxString name{};
48  int index{};
49 };
50 
52  int nFound{}; // 0, 1, or 2
53  double time{};
54  int index1{};
55  wxString name1{};
56  bool clipStart1{};
57  int index2{};
58  wxString name2{};
59  bool clipStart2{};
60 };
61 
63 ( const WaveTrack *first, const WaveTrack *second )
64 {
65  bool sameClips = false;
66 
67  auto& left = first->GetClips();
68  auto& right = second->GetClips();
69 
70  // PRL: should that have been? :
71  // auto left = first->SortedClipArray();
72  // auto right = second->SortedClipArray();
73 
74  if (left.size() == right.size()) {
75  sameClips = true;
76  for (unsigned int i = 0; i < left.size(); i++) {
77  if (left[i]->GetPlayStartTime() != right[i]->GetPlayStartTime() ||
78  left[i]->GetPlayEndTime() != right[i]->GetPlayEndTime()) {
79  sameClips = false;
80  break;
81  }
82  }
83  }
84  return sameClips;
85 }
86 
88  const WaveTrack* wt)
89 {
90  // This is quadratic in the number of channels
91  auto channels = TrackList::Channels(wt);
92  while (!channels.empty()) {
93  auto channel = *channels.first++;
94  for (auto other : channels) {
95  if (!TwoChannelsHaveSameBoundaries(channel, other))
96  return true;
97  }
98  }
99 
100  return false;
101 }
102 
103 // When two clips are immediately next to each other, the GetPlayEndTime() of the
104 // first clip and the GetPlayStartTime() of the second clip may not be exactly equal
105 // due to rounding errors. When searching for the next/prev start time from a
106 // given time, the following function adjusts that given time if necessary to
107 // take this into account. If the given time is the end time of the first of two
108 // clips which are next to each other, then the given time is changed to the
109 // start time of the second clip. This ensures that the correct next/prev start
110 // time is found.
112  const std::vector<const WaveClip*> & clips, double time)
113 {
114  auto q = std::find_if(clips.begin(), clips.end(),
115  [&] (const WaveClip* const& clip) {
116  return clip->GetPlayEndTime() == time; });
117  if (q != clips.end() && q + 1 != clips.end() &&
118  (*q)->SharesBoundaryWithNextClip(*(q+1))) {
119  time = (*(q+1))->GetPlayStartTime();
120  }
121 
122  return time;
123 }
124 
125 // When two clips are immediately next to each other, the GetPlayEndTime() of the
126 // first clip and the GetPlayStartTime() of the second clip may not be exactly equal
127 // due to rounding errors. When searching for the next/prev end time from a
128 // given time, the following function adjusts that given time if necessary to
129 // take this into account. If the given time is the start time of the second of
130 // two clips which are next to each other, then the given time is changed to the
131 // end time of the first clip. This ensures that the correct next/prev end time
132 // is found.
134  const std::vector<const WaveClip*>& clips, double time)
135 {
136  auto q = std::find_if(clips.begin(), clips.end(),
137  [&] (const WaveClip* const& clip) {
138  return clip->GetPlayStartTime() == time; });
139  if (q != clips.end() && q != clips.begin() &&
140  (*(q - 1))->SharesBoundaryWithNextClip(*q)) {
141  time = (*(q-1))->GetPlayEndTime();
142  }
143 
144  return time;
145 }
146 
148 (const WaveTrack* wt, double time)
149 {
150  FoundClipBoundary result{};
151  result.waveTrack = wt;
152  const auto clips = wt->SortedClipArray();
153  double timeStart = AdjustForFindingStartTimes(clips, time);
154  double timeEnd = AdjustForFindingEndTimes(clips, time);
155 
156  auto pStart = std::find_if(clips.begin(), clips.end(),
157  [&] (const WaveClip* const& clip) {
158  return clip->GetPlayStartTime() > timeStart; });
159  auto pEnd = std::find_if(clips.begin(), clips.end(),
160  [&] (const WaveClip* const& clip) {
161  return clip->GetPlayEndTime() > timeEnd; });
162 
163  if (pStart != clips.end() && pEnd != clips.end()) {
164  if ((*pEnd)->SharesBoundaryWithNextClip(*pStart)) {
165  // boundary between two clips which are immediately next to each other.
166  result.nFound = 2;
167  result.time = (*pEnd)->GetPlayEndTime();
168  result.index1 = std::distance(clips.begin(), pEnd);
169  result.name1 = (*pEnd)->GetName();
170  result.clipStart1 = false;
171  result.index2 = std::distance(clips.begin(), pStart);
172  result.name2 = (*pStart)->GetName();
173  result.clipStart2 = true;
174  }
175  else if ((*pStart)->GetPlayStartTime() < (*pEnd)->GetPlayEndTime()) {
176  result.nFound = 1;
177  result.time = (*pStart)->GetPlayStartTime();
178  result.index1 = std::distance(clips.begin(), pStart);
179  result.name1 = (*pStart)->GetName();
180  result.clipStart1 = true;
181  }
182  else {
183  result.nFound = 1;
184  result.time = (*pEnd)->GetPlayEndTime();
185  result.index1 = std::distance(clips.begin(), pEnd);
186  result.name1 = (*pEnd)->GetName();
187  result.clipStart1 = false;
188  }
189  }
190  else if (pEnd != clips.end()) {
191  result.nFound = 1;
192  result.time = (*pEnd)->GetPlayEndTime();
193  result.index1 = std::distance(clips.begin(), pEnd);
194  result.name1 = (*pEnd)->GetName();
195  result.clipStart1 = false;
196  }
197 
198  return result;
199 }
200 
202 {
203  FoundClipBoundary result{};
204  result.waveTrack = wt;
205  const auto clips = wt->SortedClipArray();
206  double timeStart = AdjustForFindingStartTimes(clips, time);
207  double timeEnd = AdjustForFindingEndTimes(clips, time);
208 
209  auto pStart = std::find_if(clips.rbegin(), clips.rend(),
210  [&] (const WaveClip* const& clip) {
211  return clip->GetPlayStartTime() < timeStart; });
212  auto pEnd = std::find_if(clips.rbegin(), clips.rend(),
213  [&] (const WaveClip* const& clip) {
214  return clip->GetPlayEndTime() < timeEnd; });
215 
216  if (pStart != clips.rend() && pEnd != clips.rend()) {
217  if ((*pEnd)->SharesBoundaryWithNextClip(*pStart)) {
218  // boundary between two clips which are immediately next to each other.
219  result.nFound = 2;
220  result.time = (*pStart)->GetPlayStartTime();
221  result.index1 =
222  static_cast<int>(clips.size()) - 1 -
223  std::distance(clips.rbegin(), pStart);
224  result.name1 = (*pStart)->GetName();
225  result.clipStart1 = true;
226  result.index2 =
227  static_cast<int>(clips.size()) - 1 -
228  std::distance(clips.rbegin(), pEnd);
229  result.name2 = (*pEnd)->GetName();
230  result.clipStart2 = false;
231  }
232  else if ((*pStart)->GetPlayStartTime() > (*pEnd)->GetPlayEndTime()) {
233  result.nFound = 1;
234  result.time = (*pStart)->GetPlayStartTime();
235  result.index1 =
236  static_cast<int>(clips.size()) - 1 -
237  std::distance(clips.rbegin(), pStart);
238  result.name1 = (*pStart)->GetName();
239  result.clipStart1 = true;
240  }
241  else {
242  result.nFound = 1;
243  result.time = (*pEnd)->GetPlayEndTime();
244  result.index1 =
245  static_cast<int>(clips.size()) - 1 -
246  std::distance(clips.rbegin(), pEnd);
247  result.name1 = (*pEnd)->GetName();
248  result.clipStart1 = false;
249  }
250  }
251  else if (pStart != clips.rend()) {
252  result.nFound = 1;
253  result.time = (*pStart)->GetPlayStartTime();
254  result.index1 =
255  static_cast<int>(clips.size()) - 1 -
256  std::distance(clips.rbegin(), pStart);
257  result.name1 = (*pStart)->GetName();
258  result.clipStart1 = true;
259  }
260 
261  return result;
262 }
263 
265 (AudacityProject &project,
266  double time, bool next, std::vector<FoundClipBoundary>& finalResults)
267 {
268  auto &tracks = TrackList::Get( project );
269  finalResults.clear();
270 
271  bool anyWaveTracksSelected{ tracks.Selected< const WaveTrack >() };
272 
273 
274  // first search the tracks individually
275 
276  std::vector<FoundClipBoundary> results;
277 
278  int nTracksSearched = 0;
279  auto leaders = tracks.Leaders();
280  auto rangeLeaders = leaders.Filter<const WaveTrack>();
281  if (anyWaveTracksSelected)
282  rangeLeaders = rangeLeaders + &Track::GetSelected;
283  for (auto waveTrack : rangeLeaders) {
284  bool stereoAndDiff = ChannelsHaveDifferentClipBoundaries(waveTrack);
285 
286  auto rangeChan = stereoAndDiff
287  ? TrackList::Channels( waveTrack )
288  : TrackList::SingletonRange(waveTrack);
289 
290  for (auto wt : rangeChan) {
291  auto result = next ? FindNextClipBoundary(wt, time) :
292  FindPrevClipBoundary(wt, time);
293  if (result.nFound > 0) {
294  result.trackNum =
295  1 + std::distance( leaders.begin(), leaders.find( waveTrack ) );
296  result.channel = stereoAndDiff;
297  results.push_back(result);
298  }
299  }
300 
301  nTracksSearched++;
302  }
303 
304 
305  if (results.size() > 0) {
306  // If any clip boundaries were found
307  // find the clip boundary or boundaries with the min/max time
308  auto compare = [] (const FoundClipBoundary& a, const FoundClipBoundary&b)
309  { return a.time < b.time; };
310 
311  auto p = next ? min_element(results.begin(), results.end(), compare ) :
312  max_element(results.begin(), results.end(), compare);
313 
314  for ( auto &r : results )
315  if ( r.time == (*p).time )
316  finalResults.push_back( r );
317  }
318 
319  return nTracksSearched; // can be used for screen reader messages if required
320 }
321 
322 // for clip boundary commands, create a message for screen readers
324  const std::vector<FoundClipBoundary>& results)
325 {
326  TranslatableString message;
327  for (auto& result : results) {
328 
329  auto longName = result.ComposeTrackName();
330 
332  auto nClips = result.waveTrack->GetNumClips();
333  if (result.nFound < 2) {
334  str = XP(
335  /* i18n-hint:
336  First %s is replaced with the noun "start" or "end"
337  identifying one end of a clip,
338  second string is the name of that clip,
339  first number gives the position of that clip in a sequence
340  of clips,
341  last number counts all clips,
342  and the last string is the name of the track containing the
343  clips.
344  */
345  "%s %s, %d of %d clip %s",
346  "%s %s, %d of %d clips %s",
347  3
348  )(
349  result.clipStart1 ? XO("start") : XO("end"),
350  result.name1,
351  result.index1 + 1,
352  nClips,
353  longName
354  );
355  }
356  else {
357  str = XP(
358  /* i18n-hint:
359  First and third %s are each replaced with the noun "start"
360  or with "end", identifying and end of a clip,
361  second and fourth strings are the names of those clips,
362  first and second numbers give the position of those clips in
363  a sequence of clips,
364  last number counts all clips,
365  and the last string is the name of the track containing the
366  clips.
367  */
368  "%s %s and %s %s, %d and %d of %d clip %s",
369  "%s %s and %s %s, %d and %d of %d clips %s",
370  6
371  )(
372  result.clipStart1 ? XO("start") : XO("end"),
373  result.name1,
374  result.clipStart2 ? XO("start") : XO("end"),
375  result.name2,
376  result.index1 + 1,
377  result.index2 + 1,
378  nClips,
379  longName
380  );
381  }
382 
383  if (message.empty())
384  message = str;
385  else
386  message = XO("%s, %s").Format( message, str );
387  }
388 
389  return message;
390 }
391 
392 void DoSelectClipBoundary(AudacityProject &project, bool next)
393 {
394  auto &selectedRegion = ViewInfo::Get( project ).selectedRegion;
395  auto &trackFocus = TrackFocus::Get( project );
396 
397  std::vector<FoundClipBoundary> results;
398  FindClipBoundaries(project, next ? selectedRegion.t1() :
399  selectedRegion.t0(), next, results);
400 
401  if (results.size() > 0) {
402  // note that if there is more than one result, each has the same time
403  // value.
404  if (next)
405  selectedRegion.setT1(results[0].time);
406  else
407  selectedRegion.setT0(results[0].time);
408 
409  ProjectHistory::Get( project ).ModifyState(false);
410 
411  auto message = ClipBoundaryMessage(results);
412  trackFocus.MessageForScreenReader(message);
413  }
414 }
415 
417 (AudacityProject &project, const WaveTrack* wt, double t0, double t1)
418 {
419  (void)project;//Compiler food.
420 
421  FoundClip result{};
422  result.waveTrack = wt;
423  const auto clips = wt->SortedClipArray();
424 
425  t0 = AdjustForFindingStartTimes(clips, t0);
426 
427  {
428  auto p = std::find_if(clips.begin(), clips.end(),
429  [&] (const WaveClip* const& clip) {
430  return clip->GetPlayStartTime() == t0; });
431  if (p != clips.end() && (*p)->GetPlayEndTime() > t1) {
432  result.found = true;
433  result.startTime = (*p)->GetPlayStartTime();
434  result.endTime = (*p)->GetPlayEndTime();
435  result.name = (*p)->GetName();
436  result.index = std::distance(clips.begin(), p);
437  return result;
438  }
439  }
440 
441  {
442  auto p = std::find_if(clips.begin(), clips.end(),
443  [&] (const WaveClip* const& clip) {
444  return clip->GetPlayStartTime() > t0; });
445  if (p != clips.end()) {
446  result.found = true;
447  result.startTime = (*p)->GetPlayStartTime();
448  result.endTime = (*p)->GetPlayEndTime();
449  result.name = (*p)->GetName();
450  result.index = std::distance(clips.begin(), p);
451  return result;
452  }
453  }
454 
455  return result;
456 }
457 
459 (AudacityProject &project, const WaveTrack* wt, double t0, double t1)
460 {
461  (void)project;//Compiler food.
462 
463  FoundClip result{};
464  result.waveTrack = wt;
465  const auto clips = wt->SortedClipArray();
466 
467  t0 = AdjustForFindingStartTimes(clips, t0);
468 
469  {
470  auto p = std::find_if(clips.begin(), clips.end(),
471  [&] (const WaveClip* const& clip) {
472  return clip->GetPlayStartTime() == t0; });
473  if (p != clips.end() && (*p)->GetPlayEndTime() < t1) {
474  result.found = true;
475  result.startTime = (*p)->GetPlayStartTime();
476  result.endTime = (*p)->GetPlayEndTime();
477  result.name = (*p)->GetName();
478  result.index = std::distance(clips.begin(), p);
479  return result;
480  }
481  }
482 
483  {
484  auto p = std::find_if(clips.rbegin(), clips.rend(),
485  [&] (const WaveClip* const& clip) {
486  return clip->GetPlayStartTime() < t0; });
487  if (p != clips.rend()) {
488  result.found = true;
489  result.startTime = (*p)->GetPlayStartTime();
490  result.endTime = (*p)->GetPlayEndTime();
491  result.name = (*p)->GetName();
492  result.index =
493  static_cast<int>(clips.size()) - 1 -
494  std::distance(clips.rbegin(), p);
495  return result;
496  }
497  }
498 
499  return result;
500 }
501 
503 (AudacityProject &project,
504  double t0, double t1, bool next, std::vector<FoundClip>& finalResults)
505 {
506  auto &tracks = TrackList::Get( project );
507  finalResults.clear();
508 
509  bool anyWaveTracksSelected{ tracks.Selected< const WaveTrack >() };
510 
511  // first search the tracks individually
512 
513  std::vector<FoundClip> results;
514 
515  int nTracksSearched = 0;
516  auto leaders = tracks.Leaders();
517  auto rangeLeaders = leaders.Filter<const WaveTrack>();
518  if (anyWaveTracksSelected)
519  rangeLeaders = rangeLeaders + &Track::GetSelected;
520  for (auto waveTrack : rangeLeaders) {
521  bool stereoAndDiff = ChannelsHaveDifferentClipBoundaries(waveTrack);
522 
523  auto rangeChans = stereoAndDiff
524  ? TrackList::Channels( waveTrack )
525  : TrackList::SingletonRange( waveTrack );
526 
527  for ( auto wt : rangeChans ) {
528  auto result = next ? FindNextClip(project, wt, t0, t1) :
529  FindPrevClip(project, wt, t0, t1);
530  if (result.found) {
531  result.trackNum =
532  1 + std::distance( leaders.begin(), leaders.find( waveTrack ) );
533  result.channel = stereoAndDiff;
534  results.push_back(result);
535  }
536  }
537 
538  nTracksSearched++;
539  }
540 
541 
542  if (results.size() > 0) {
543  // if any clips were found,
544  // find the clip or clips with the min/max start time
545  auto compareStart = [] (const FoundClip& a, const FoundClip& b)
546  { return a.startTime < b.startTime; };
547 
548  auto pStart = next
549  ? std::min_element(results.begin(), results.end(), compareStart)
550  : std::max_element(results.begin(), results.end(), compareStart);
551 
552  std::vector<FoundClip> resultsStartTime;
553  for ( auto &r : results )
554  if ( r.startTime == (*pStart).startTime )
555  resultsStartTime.push_back( r );
556 
557  if (resultsStartTime.size() > 1) {
558  // more than one clip with same start time so
559  // find the clip or clips with the min/max end time
560  auto compareEnd = [] (const FoundClip& a, const FoundClip& b)
561  { return a.endTime < b.endTime; };
562 
563  auto pEnd = next ? std::min_element(resultsStartTime.begin(),
564  resultsStartTime.end(), compareEnd) :
565  std::max_element(resultsStartTime.begin(),
566  resultsStartTime.end(), compareEnd);
567 
568  for ( auto &r : resultsStartTime )
569  if ( r.endTime == (*pEnd).endTime )
570  finalResults.push_back( r );
571  }
572  else {
573  finalResults = resultsStartTime;
574  }
575  }
576 
577  return nTracksSearched; // can be used for screen reader messages if required
578 }
579 
580 void DoSelectClip(AudacityProject &project, bool next)
581 {
582  auto &selectedRegion = ViewInfo::Get( project ).selectedRegion;
583  auto &trackFocus = TrackFocus::Get( project );
584  auto &window = ProjectWindow::Get( project );
585 
586  std::vector<FoundClip> results;
587  FindClips(project, selectedRegion.t0(),
588  selectedRegion.t1(), next, results);
589 
590  if (results.size() > 0) {
591  // note that if there is more than one result, each has the same start
592  // and end time
593  double t0 = results[0].startTime;
594  double t1 = results[0].endTime;
595  selectedRegion.setTimes(t0, t1);
596  ProjectHistory::Get( project ).ModifyState(false);
597  window.ScrollIntoView(selectedRegion.t0());
598 
599  // create and send message to screen reader
600  TranslatableString message;
601  for (auto& result : results) {
602  auto longName = result.ComposeTrackName();
603  auto nClips = result.waveTrack->GetNumClips();
604  auto str = XP(
605  /* i18n-hint:
606  first string is the name of a clip,
607  first number gives the position of that clip
608  in a sequence of clips,
609  last number counts all clips,
610  last string names a track */
611  "%s, %d of %d clip %s",
612  "%s, %d of %d clips %s",
613  2
614  )(
615  result.name,
616  result.index + 1,
617  nClips,
618  longName
619  );
620 
621  if (message.empty())
622  message = str;
623  else
624  message = XO("%s, %s").Format( message, str );
625  }
626  trackFocus.MessageForScreenReader(message);
627  }
628 }
629 
631 (AudacityProject &project, bool next)
632 {
633  auto &selectedRegion = ViewInfo::Get( project ).selectedRegion;
634  auto &trackFocus = TrackFocus::Get( project );
635  auto &window = ProjectWindow::Get( project );
636 
637  std::vector<FoundClipBoundary> results;
638  FindClipBoundaries(project, next ? selectedRegion.t1() :
639  selectedRegion.t0(), next, results);
640 
641  if (results.size() > 0) {
642  // note that if there is more than one result, each has the same time
643  // value.
644  double time = results[0].time;
645  selectedRegion.setTimes(time, time);
646  ProjectHistory::Get( project ).ModifyState(false);
647  window.ScrollIntoView(selectedRegion.t0());
648 
649  auto message = ClipBoundaryMessage(results);
650  trackFocus.MessageForScreenReader(message);
651  }
652 }
653 
654 // This function returns the amount moved. Possibly 0.0.
655 double DoClipMove( AudacityProject &project, Track *track,
656  TrackList &trackList, bool syncLocked, bool right )
657 {
658  auto &viewInfo = ViewInfo::Get(project);
659  auto &selectedRegion = viewInfo.selectedRegion;
660 
661  if (track) {
662  ClipMoveState state;
663 
664  auto t0 = selectedRegion.t0();
665 
666  std::unique_ptr<TrackShifter> uShifter;
667 
668  // Find the first channel that has a clip at time t0
669  auto hitTestResult = TrackShifter::HitTestResult::Track;
670  for (auto channel : TrackList::Channels(track) ) {
671  uShifter = MakeTrackShifter::Call( *channel, project );
672  if ( (hitTestResult = uShifter->HitTest( t0, viewInfo )) ==
674  uShifter.reset();
675  else
676  break;
677  }
678 
679  if (!uShifter)
680  return 0.0;
681  auto pShifter = uShifter.get();
682  auto desiredT0 = viewInfo.OffsetTimeByPixels( t0, ( right ? 1 : -1 ) );
683  auto desiredSlideAmount = pShifter->HintOffsetLarger( desiredT0 - t0 );
684 
685  state.Init( project, pShifter->GetTrack(), hitTestResult, std::move( uShifter ),
686  t0, viewInfo, trackList, syncLocked );
687 
688  auto hSlideAmount = state.DoSlideHorizontal( desiredSlideAmount );
689 
690  double newT0 = t0 + hSlideAmount;
691  if (hitTestResult != TrackShifter::HitTestResult::Track) {
692  // If necessary, correct for rounding errors. For example,
693  // for a wavetrack, ensure that t0 is still in the clip
694  // which it was within before the move.
695  // (pShifter is still undestroyed in the ClipMoveState.)
696  newT0 = pShifter->AdjustT0(newT0);
697  }
698 
699  double diff = selectedRegion.duration();
700  selectedRegion.setTimes(newT0, newT0 + diff);
701 
702  return hSlideAmount;
703  };
704  return 0.0;
705 }
706 
708 (AudacityProject &project, bool right, bool keyUp )
709 {
710  auto &undoManager = UndoManager::Get( project );
711  auto &window = ProjectWindow::Get( project );
712 
713  if (keyUp) {
714  undoManager.StopConsolidating();
715  return;
716  }
717 
718  auto &trackFocus = TrackFocus::Get( project );
719  auto &viewInfo = ViewInfo::Get( project );
720  auto &selectedRegion = viewInfo.selectedRegion;
721  const auto &settings = ProjectSettings::Get( project );
722  auto &tracks = TrackList::Get( project );
723  auto isSyncLocked = settings.IsSyncLocked();
724 
725  auto amount = DoClipMove( project, trackFocus.Get(),
726  tracks, isSyncLocked, right );
727 
728  window.ScrollIntoView(selectedRegion.t0());
729 
730  if (amount != 0.0) {
731  auto message = right? XO("Time shifted clips to the right") :
732  XO("Time shifted clips to the left");
733 
734  // The following use of the UndoPush flags is so that both a single
735  // keypress (keydown, then keyup), and holding down a key
736  // (multiple keydowns followed by a keyup) result in a single
737  // entry in Audacity's history dialog.
738  ProjectHistory::Get( project )
739  .PushState(message, XO("Time-Shift"), UndoPush::CONSOLIDATE);
740  }
741 
742  if ( amount == 0.0 )
743  trackFocus.MessageForScreenReader( XO("clip not moved"));
744 }
745 
746 }
747 
749 namespace ClipActions {
750 
751 // exported helper functions
752 // none
753 
754 // Menu handler functions
755 
757 
759 (const CommandContext &context)
760 {
761  auto &project = context.project;
762  DoSelectClipBoundary(project, false);
763 }
764 
766 (const CommandContext &context)
767 {
768  auto &project = context.project;
769  DoSelectClipBoundary(project, true);
770 }
771 
772 void OnSelectPrevClip(const CommandContext &context)
773 {
774  auto &project = context.project;
775  DoSelectClip(project, false);
776 }
777 
778 void OnSelectNextClip(const CommandContext &context)
779 {
780  auto &project = context.project;
781  DoSelectClip(project, true);
782 }
783 
785 {
786  AudacityProject &project = context.project;
787 
788  DoCursorClipBoundary(project, false);
789 }
790 
792 {
793  AudacityProject &project = context.project;
794 
795  DoCursorClipBoundary(project, true);
796 }
797 
798 // PRL: Clip moving functions -- more than just selection adjustment. Do they
799 // really belong in these navigation menus?
800 void OnClipLeft(const CommandContext &context)
801 {
802  auto &project = context.project;
803  auto evt = context.pEvt;
804  if (evt)
805  DoClipLeftOrRight( project, false, evt->GetEventType() == wxEVT_KEY_UP );
806  else { // called from menu, so simulate keydown and keyup
807  DoClipLeftOrRight( project, false, false );
808  DoClipLeftOrRight( project, false, true );
809  }
810 }
811 
812 void OnClipRight(const CommandContext &context)
813 {
814  auto &project = context.project;
815  auto evt = context.pEvt;
816  if (evt)
817  DoClipLeftOrRight( project, true, evt->GetEventType() == wxEVT_KEY_UP );
818  else { // called from menu, so simulate keydown and keyup
819  DoClipLeftOrRight( project, true, false );
820  DoClipLeftOrRight( project, true, true );
821  }
822 }
823 
824 }; // struct Handler
825 
826 } // namespace
827 
829  // Handler is not stateful. Doesn't need a factory registered with
830  // AudacityProject.
831  static ClipActions::Handler instance;
832  return instance;
833 };
834 
835 // Menu definitions
836 
837 #define FN(X) (& ClipActions::Handler :: X)
838 
839 namespace {
840 using namespace MenuTable;
841 
842 // Register menu items
843 
845 {
847 
848  static BaseItemSharedPtr menu {
850  Menu( wxT("Clip"), XXO("Clip B&oundaries"),
851  Command( wxT("SelPrevClipBoundaryToCursor"),
852  XXO("Pre&vious Clip Boundary to Cursor"),
853  FN(OnSelectPrevClipBoundaryToCursor),
855  Command( wxT("SelCursorToNextClipBoundary"),
856  XXO("Cursor to Ne&xt Clip Boundary"),
857  FN(OnSelectCursorToNextClipBoundary),
859  Command( wxT("SelPrevClip"), XXO("Previo&us Clip"),
860  FN(OnSelectPrevClip), WaveTracksExistFlag(),
861  Options{ wxT("Alt+,"), XO("Select Previous Clip") } ),
862  Command( wxT("SelNextClip"), XXO("N&ext Clip"), FN(OnSelectNextClip),
864  Options{ wxT("Alt+."), XO("Select Next Clip") } )
865  ) ) };
866  return menu;
867 }
868 
870  wxT("Select/Basic"),
872 };
873 
875 {
877 
878  static BaseItemSharedPtr items{
880  Items( wxT("Clip"),
881  Command( wxT("CursPrevClipBoundary"), XXO("Pre&vious Clip Boundary"),
882  FN(OnCursorPrevClipBoundary),
884  Options{}.LongName( XO("Cursor to Prev Clip Boundary") ) ),
885  Command( wxT("CursNextClipBoundary"), XXO("Ne&xt Clip Boundary"),
886  FN(OnCursorNextClipBoundary),
888  Options{}.LongName( XO("Cursor to Next Clip Boundary") ) )
889  ) ) };
890  return items;
891 }
892 
894  { wxT("Transport/Basic/Cursor"),
895  { OrderingHint::Before, wxT("CursProjectStart") } },
897 };
898 
900 {
902  static BaseItemSharedPtr items{
904  Items( wxT("TimeShift"),
905  Command( wxT("ClipLeft"), XXO("Time Shift &Left"), FN(OnClipLeft),
906  TracksExistFlag() | TrackPanelHasFocus(), Options{}.WantKeyUp() ),
907  Command( wxT("ClipRight"), XXO("Time Shift &Right"), FN(OnClipRight),
908  TracksExistFlag() | TrackPanelHasFocus(), Options{}.WantKeyUp() )
909  ) ) };
910  return items;
911 }
912 
914  { wxT("Optional/Extra/Part1/Edit"), { OrderingHint::End, {} } },
916 };
917 
918 }
919 
920 #undef FN
anonymous_namespace{ClipMenus.cpp}::FoundClip
Definition: ClipMenus.cpp:43
ProjectHistory::ModifyState
void ModifyState(bool bWantsAutoSave)
Definition: ProjectHistory.cpp:124
TranslatableString
Holds a msgid for the translation catalog; may also bind format arguments.
Definition: TranslatableString.h:32
ViewInfo::Get
static ViewInfo & Get(AudacityProject &project)
Definition: ViewInfo.cpp:241
Registry::OrderingHint::End
@ End
Definition: Registry.h:30
TranslatableString::empty
bool empty() const
Definition: TranslatableString.h:72
ClipActions::Handler::OnCursorPrevClipBoundary
void OnCursorPrevClipBoundary(const CommandContext &context)
Definition: ClipMenus.cpp:784
WaveTrack
A Track that contains audio waveform data.
Definition: WaveTrack.h:69
anonymous_namespace{ClipMenus.cpp}::DoSelectClip
void DoSelectClip(AudacityProject &project, bool next)
Definition: ClipMenus.cpp:580
anonymous_namespace{ClipMenus.cpp}::DoClipMove
double DoClipMove(AudacityProject &project, Track *track, TrackList &trackList, bool syncLocked, bool right)
Definition: ClipMenus.cpp:655
anonymous_namespace{ClipMenus.cpp}::DoSelectClipBoundary
void DoSelectClipBoundary(AudacityProject &project, bool next)
Definition: ClipMenus.cpp:392
anonymous_namespace{ClipMenus.cpp}::FoundClipBoundary::time
double time
Definition: ClipMenus.cpp:53
MenuTable::FinderScope
Definition: CommandManager.h:485
AttachedVirtualFunction::Call
static Return Call(This &obj, Arguments &&...arguments)
Invoke the method – but only after static initialization time.
Definition: AttachedVirtualFunction.h:223
anonymous_namespace{ClipMenus.cpp}::FoundClip::endTime
double endTime
Definition: ClipMenus.cpp:46
TrackShifter::HitTestResult::Track
@ Track
Shift selected track and sister channels only, as a whole.
TrackShifter::HitTest
virtual HitTestResult HitTest(double time, const ViewInfo &viewInfo, HitTestParams *pParams=nullptr)=0
Decide how shift behaves, based on the track that is clicked in.
str
#define str(a)
Definition: DBConnection.cpp:30
CommandContext::pEvt
const wxEvent * pEvt
Definition: CommandContext.h:66
anonymous_namespace{ClipMenus.cpp}::TwoChannelsHaveSameBoundaries
bool TwoChannelsHaveSameBoundaries(const WaveTrack *first, const WaveTrack *second)
Definition: ClipMenus.cpp:63
ClientData::Site::Get
Subclass & Get(const RegisteredFactory &key)
Get reference to an attachment, creating on demand if not present, down-cast it to Subclass.
Definition: ClientData.h:309
anonymous_namespace{ClipMenus.cpp}::FindNextClip
FoundClip FindNextClip(AudacityProject &project, const WaveTrack *wt, double t0, double t1)
Definition: ClipMenus.cpp:417
TrackList::Channels
static auto Channels(TrackType *pTrack) -> TrackIterRange< TrackType >
Definition: Track.h:1484
TrackList
A flat linked list of tracks supporting Add, Remove, Clear, and Contains, serialization of the list o...
Definition: Track.h:1280
WaveTracksExistFlag
const ReservedCommandFlag & WaveTracksExistFlag()
Definition: CommonCommandFlags.cpp:273
ClipMoveState
Definition: TimeShiftHandle.h:205
anonymous_namespace{ClipMenus.cpp}::sAttachment1
AttachedItem sAttachment1
Definition: ClipMenus.cpp:869
Registry::Shared
std::unique_ptr< SharedItem > Shared(const BaseItemSharedPtr &ptr)
Definition: Registry.h:93
anonymous_namespace{ClipMenus.cpp}::FindNextClipBoundary
FoundClipBoundary FindNextClipBoundary(const WaveTrack *wt, double time)
Definition: ClipMenus.cpp:148
XO
#define XO(s)
Definition: Internat.h:31
anonymous_namespace{ClipMenus.cpp}::DoClipLeftOrRight
void DoClipLeftOrRight(AudacityProject &project, bool right, bool keyUp)
Definition: ClipMenus.cpp:708
ProjectSettings::Get
static ProjectSettings & Get(AudacityProject &project)
Definition: ProjectSettings.cpp:44
anonymous_namespace{ClipMenus.cpp}::FindPrevClip
FoundClip FindPrevClip(AudacityProject &project, const WaveTrack *wt, double t0, double t1)
Definition: ClipMenus.cpp:459
anonymous_namespace{ClipMenus.cpp}::FindClipBoundaries
int FindClipBoundaries(AudacityProject &project, double time, bool next, std::vector< FoundClipBoundary > &finalResults)
Definition: ClipMenus.cpp:265
ProjectWindow::Get
static ProjectWindow & Get(AudacityProject &project)
Definition: ProjectWindow.cpp:535
TrackShifter::HitTestResult::Miss
@ Miss
Don't shift anything.
MenuTable::AttachedItem
Definition: CommandManager.h:708
anonymous_namespace{ClipMenus.cpp}::sAttachment3
AttachedItem sAttachment3
Definition: ClipMenus.cpp:913
anonymous_namespace{ClipMenus.cpp}::ExtraTimeShiftItems
BaseItemSharedPtr ExtraTimeShiftItems()
Definition: ClipMenus.cpp:899
ClipActions::Handler::OnClipRight
void OnClipRight(const CommandContext &context)
Definition: ClipMenus.cpp:812
WaveTrack::SortedClipArray
WaveClipPointers SortedClipArray()
Definition: WaveTrack.cpp:2595
ClipActions::Handler::OnSelectCursorToNextClipBoundary
void OnSelectCursorToNextClipBoundary(const CommandContext &context)
Definition: ClipMenus.cpp:766
ClipActions
Namespace for functions for Clip menu.
Definition: ClipMenus.cpp:749
WaveClip
This allows multiple clips to be a part of one WaveTrack.
Definition: WaveClip.h:175
anonymous_namespace{ClipMenus.cpp}::FoundTrack::ComposeTrackName
TranslatableString ComposeTrackName() const
Definition: ClipMenus.cpp:22
FN
#define FN(X)
Definition: ClipMenus.cpp:837
ViewInfo::selectedRegion
NotifyingSelectedRegion selectedRegion
Definition: ViewInfo.h:229
XXO
#define XXO(s)
Definition: Internat.h:44
CommandContext
CommandContext provides additional information to an 'Apply()' command. It provides the project,...
Definition: CommandContext.h:34
anonymous_namespace{ClipMenus.cpp}::AdjustForFindingEndTimes
double AdjustForFindingEndTimes(const std::vector< const WaveClip * > &clips, double time)
Definition: ClipMenus.cpp:133
Registry::BaseItemSharedPtr
std::shared_ptr< BaseItem > BaseItemSharedPtr
Definition: Registry.h:72
anonymous_namespace{ClipMenus.cpp}::AdjustForFindingStartTimes
double AdjustForFindingStartTimes(const std::vector< const WaveClip * > &clips, double time)
Definition: ClipMenus.cpp:111
WaveTrack::GetClips
WaveClipHolders & GetClips()
Definition: WaveTrack.h:371
name
const TranslatableString name
Definition: Distortion.cpp:98
anonymous_namespace{ClipMenus.cpp}::FindClips
int FindClips(AudacityProject &project, double t0, double t1, bool next, std::vector< FoundClip > &finalResults)
Definition: ClipMenus.cpp:503
Registry::OrderingHint::Before
@ Before
Definition: Registry.h:29
ClipActions::Handler::OnSelectPrevClipBoundaryToCursor
void OnSelectPrevClipBoundaryToCursor(const CommandContext &context)
Definition: ClipMenus.cpp:759
MenuTable::Items
std::unique_ptr< MenuItems > Items(const Identifier &internalName, Args &&... args)
Definition: CommandManager.h:600
anonymous_namespace{ClipMenus.cpp}::FoundTrack::waveTrack
const WaveTrack * waveTrack
Definition: ClipMenus.cpp:18
TrackFocus::Get
Track * Get()
Definition: TrackPanelAx.cpp:755
anonymous_namespace{ClipMenus.cpp}::ClipCursorItems
BaseItemSharedPtr ClipCursorItems()
Definition: ClipMenus.cpp:874
ClipMoveState::DoSlideHorizontal
double DoSlideHorizontal(double desiredSlideAmount)
Do sliding of tracks and intervals, maybe adjusting the offset.
Definition: TimeShiftHandle.cpp:411
TracksExistFlag
const ReservedCommandFlag & TracksExistFlag()
Definition: CommonCommandFlags.cpp:173
ViewInfo.h
anonymous_namespace{ClipMenus.cpp}::ClipBoundaryMessage
TranslatableString ClipBoundaryMessage(const std::vector< FoundClipBoundary > &results)
Definition: ClipMenus.cpp:323
anonymous_namespace{ClipMenus.cpp}::ChannelsHaveDifferentClipBoundaries
bool ChannelsHaveDifferentClipBoundaries(const WaveTrack *wt)
Definition: ClipMenus.cpp:87
Track::GetSelected
bool GetSelected() const
Definition: Track.h:431
ClipActions::Handler::OnSelectPrevClip
void OnSelectPrevClip(const CommandContext &context)
Definition: ClipMenus.cpp:772
anonymous_namespace{ClipMenus.cpp}::DoCursorClipBoundary
void DoCursorClipBoundary(AudacityProject &project, bool next)
Definition: ClipMenus.cpp:631
ClipMoveState::Init
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.
Definition: TimeShiftHandle.cpp:281
ProjectHistory::PushState
void PushState(const TranslatableString &desc, const TranslatableString &shortDesc)
Definition: ProjectHistory.cpp:90
TrackPanelHasFocus
const ReservedCommandFlag & TrackPanelHasFocus()
Definition: CommonCommandFlags.cpp:196
anonymous_namespace{ClipMenus.cpp}::FoundClipBoundary
Definition: ClipMenus.cpp:51
TrackList::Get
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:506
Track
Abstract base class for an object holding data associated with points on a time axis.
Definition: Track.h:239
XP
#define XP(sing, plur, n)
Definition: Internat.h:96
UndoManager::Get
static UndoManager & Get(AudacityProject &project)
Definition: UndoManager.cpp:57
MenuTable::Command
std::unique_ptr< CommandItem > Command(const CommandID &name, const TranslatableString &label_in, void(Handler::*pmf)(const CommandContext &), CommandFlag flags, const CommandManager::Options &options={}, CommandHandlerFinder finder=FinderScope::DefaultFinder())
Definition: CommandManager.h:675
ClipActions::Handler
Definition: ClipMenus.cpp:756
AudacityProject
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:92
ClipActions::Handler::OnClipLeft
void OnClipLeft(const CommandContext &context)
Definition: ClipMenus.cpp:800
CommandHandlerObject
wxEvtHandler CommandHandlerObject
Definition: CommandFunctors.h:28
MenuTable::Menu
std::unique_ptr< MenuItem > Menu(const Identifier &internalName, const TranslatableString &title, Args &&... args)
Definition: CommandManager.h:623
Verbatim
TranslatableString Verbatim(wxString str)
Require calls to the one-argument constructor to go through this distinct global function name.
Definition: TranslatableString.h:321
anonymous_namespace{ClipMenus.cpp}::FoundTrack
Definition: ClipMenus.cpp:17
UndoPush::CONSOLIDATE
@ CONSOLIDATE
MenuTable
Definition: CommandManager.h:416
ClipActions::Handler::OnSelectNextClip
void OnSelectNextClip(const CommandContext &context)
Definition: ClipMenus.cpp:778
TrackList::SingletonRange
static auto SingletonRange(TrackType *pTrack) -> TrackIterRange< TrackType >
Definition: Track.h:1439
settings
static Settings & settings()
Definition: TrackInfo.cpp:86
anonymous_namespace{ClipMenus.cpp}::ClipSelectMenu
BaseItemSharedPtr ClipSelectMenu()
Definition: ClipMenus.cpp:844
CommandContext::project
AudacityProject & project
Definition: CommandContext.h:64
anonymous_namespace{Menus.cpp}::Options
std::vector< CommandFlagOptions > & Options()
Definition: Menus.cpp:527
anonymous_namespace{ClipMenus.cpp}::FindPrevClipBoundary
FoundClipBoundary FindPrevClipBoundary(const WaveTrack *wt, double time)
Definition: ClipMenus.cpp:201
ClipActions::Handler::OnCursorNextClipBoundary
void OnCursorNextClipBoundary(const CommandContext &context)
Definition: ClipMenus.cpp:791
anonymous_namespace{ClipMenus.cpp}::sAttachment2
AttachedItem sAttachment2
Definition: ClipMenus.cpp:893
ProjectHistory::Get
static ProjectHistory & Get(AudacityProject &project)
Definition: ProjectHistory.cpp:26
anonymous_namespace{ClipMenus.cpp}::FoundClip::startTime
double startTime
Definition: ClipMenus.cpp:45
findCommandHandler
static CommandHandlerObject & findCommandHandler(AudacityProject &)
Definition: ClipMenus.cpp:828
TrackShifter::HintOffsetLarger
virtual double HintOffsetLarger(double desiredOffset)
Given amount to shift by horizontally, maybe adjust it from zero to suggest minimum distance.
Definition: TimeShiftHandle.cpp:167