Audacity  3.0.3
TimeShiftHandle.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3 Audacity: A Digital Audio Editor
4 
5 TimeShiftHandle.cpp
6 
7 Paul Licameli split from TrackPanel.cpp
8 
9 **********************************************************************/
10 
11 
12 #include "TimeShiftHandle.h"
13 
14 #include "TrackView.h"
15 #include "../../AColor.h"
16 #include "../../HitTestResult.h"
17 #include "../../ProjectAudioIO.h"
18 #include "../../ProjectHistory.h"
19 #include "../../ProjectSettings.h"
20 #include "../../RefreshCode.h"
21 #include "../../Snap.h"
22 #include "../../Track.h"
23 #include "../../TrackArtist.h"
24 #include "../../TrackPanelDrawingContext.h"
25 #include "../../TrackPanelMouseEvent.h"
26 #include "../../UndoManager.h"
27 #include "../../ViewInfo.h"
28 #include "../../../images/Cursors.h"
29 
31 ( const std::shared_ptr<Track> &pTrack, bool gripHit )
32  : mGripHit{ gripHit }
33 {
34  mClipMoveState.mCapturedTrack = pTrack;
35 }
36 
38 {
39 #ifdef EXPERIMENTAL_TRACK_PANEL_HIGHLIGHTING
41 #endif
42 }
43 
45 (const AudacityProject *WXUNUSED(pProject), bool unsafe)
46 {
47  static auto disabledCursor =
48  ::MakeCursor(wxCURSOR_NO_ENTRY, DisabledCursorXpm, 16, 16);
49  static auto slideCursor =
50  MakeCursor(wxCURSOR_SIZEWE, TimeCursorXpm, 16, 16);
51  // TODO: Should it say "track or clip" ? Non-wave tracks can move, or clips in a wave track.
52  // TODO: mention effects of shift (move all clips of selected wave track) and ctrl (move vertically only) ?
53  // -- but not all of that is available in multi tool.
54  auto message = XO("Click and drag to move a track in time");
55 
56  return {
57  message,
58  (unsafe
59  ? &*disabledCursor
60  : &*slideCursor)
61  };
62 }
63 
65 (std::weak_ptr<TimeShiftHandle> &holder,
66  const std::shared_ptr<Track> &pTrack, bool gripHit)
67 {
68  auto result = std::make_shared<TimeShiftHandle>( pTrack, gripHit );
69  result = AssignUIHandlePtr(holder, result);
70  return result;
71 }
72 
74 (std::weak_ptr<TimeShiftHandle> &holder,
75  const wxMouseState &state, const wxRect &rect,
76  const std::shared_ptr<Track> &pTrack)
77 {
81 
82  // Perhaps we should delegate this to TrackArtist as only TrackArtist
83  // knows what the real sizes are??
84 
85  // The drag Handle width includes border, width and a little extra margin.
86  const int adjustedDragHandleWidth = 14;
87  // The hotspot for the cursor isn't at its centre. Adjust for this.
88  const int hotspotOffset = 5;
89 
90  // We are doing an approximate test here - is the mouse in the right or left border?
91  if (!(state.m_x + hotspotOffset < rect.x + adjustedDragHandleWidth ||
92  state.m_x + hotspotOffset >= rect.x + rect.width - adjustedDragHandleWidth))
93  return {};
94 
95  return HitAnywhere( holder, pTrack, true );
96 }
97 
99 {
100 }
101 
103 {
104  if ( !shifters.empty() ) {
105  for ( auto &pair : shifters )
106  pair.second->DoHorizontalOffset( offset );
107  }
108  else {
109  for (auto channel : TrackList::Channels( mCapturedTrack.get() ))
110  channel->Offset( offset );
111  }
112 }
113 
114 TrackShifter::TrackShifter() = default;
115 
116 TrackShifter::~TrackShifter() = default;
117 
119  std::function< bool( const TrackInterval& ) > pred )
120 {
121  for ( auto iter = mFixed.begin(); iter != mFixed.end(); ) {
122  if ( pred( *iter) ) {
123  mMoving.push_back( std::move( *iter ) );
124  iter = mFixed.erase( iter );
125  mAllFixed = false;
126  }
127  else
128  ++iter;
129  }
130 }
131 
133 {
134  std::move( mFixed.begin(), mFixed.end(), std::back_inserter(mMoving) );
135  mFixed = Intervals{};
136  mAllFixed = false;
137 }
138 
140 {
141  UnfixAll();
142 }
143 
145 {
146  UnfixIntervals( [&](auto &myInterval){
147  return !(interval.End() < myInterval.Start() ||
148  myInterval.End() < interval.Start());
149  });
150 }
151 
152 double TrackShifter::HintOffsetLarger(double desiredOffset)
153 {
154  return desiredOffset;
155 }
156 
157 double TrackShifter::QuantizeOffset(double desiredOffset)
158 {
159  return desiredOffset;
160 }
161 
162 double TrackShifter::AdjustOffsetSmaller(double desiredOffset)
163 {
164  return desiredOffset;
165 }
166 
168 {
169  return false;
170 }
171 
173 {
174  auto &track = GetTrack();
175 
176  // Both tracks need to be owned to decide this
177  auto pMyList = track.GetOwner().get();
178  auto pOtherList = otherTrack.GetOwner().get();
179  if (pMyList && pOtherList) {
180 
181  // Can migrate to another track of the same kind...
182  if ( otherTrack.SameKindAs( track ) ) {
183 
184  // ... with the same number of channels ...
185  auto myChannels = TrackList::Channels( &track );
186  auto otherChannels = TrackList::Channels( &otherTrack );
187  if (myChannels.size() == otherChannels.size()) {
188 
189  // ... and where this track and the other have corresponding
190  // positions
191  return myChannels.size() == 1 ||
192  std::distance(myChannels.first, pMyList->Find(&track)) ==
193  std::distance(otherChannels.first, pOtherList->Find(&otherTrack));
194 
195  }
196 
197  }
198 
199  }
200  return false;
201 }
202 
204 {
205  return {};
206 }
207 
209  const Track &, const Intervals&, double &, double)
210 {
211  return false;
212 }
213 
215 {
216  return true;
217 }
218 
220 {
221  return true;
222 }
223 
224 void TrackShifter::DoHorizontalOffset( double offset )
225 {
226  if (!AllFixed())
227  GetTrack().Offset( offset );
228 }
229 
230 double TrackShifter::AdjustT0(double t0) const
231 {
232  return t0;
233 }
234 
236 {
237  mMoving.clear();
239 }
240 
242  : mpTrack{ track.SharedPointer() }
243 {
244  InitIntervals();
245 }
246 
248 
250  double, const ViewInfo&, HitTestParams* ) -> HitTestResult
251 {
252  return HitTestResult::Track;
253 }
254 
256 {
257  return false;
258 }
259 
261  return [](Track &track, AudacityProject&) {
262  return std::make_unique<CoarseTrackShifter>(track);
263  };
264 }
266 
268  AudacityProject &project,
269  Track &capturedTrack,
270  TrackShifter::HitTestResult hitTestResult,
271  std::unique_ptr<TrackShifter> pHit,
272  double clickTime,
273  const ViewInfo &viewInfo,
274  TrackList &trackList, bool syncLocked )
275 {
276  shifters.clear();
277 
278  auto &state = *this;
279  state.mCapturedTrack = capturedTrack.SharedPointer();
280 
281  switch (hitTestResult) {
283  wxASSERT(false);
284  pHit.reset();
285  break;
287  pHit.reset();
288  break;
290  break;
292  state.movingSelection = true;
293  break;
294  default:
295  break;
296  }
297 
298  if (!pHit)
299  return;
300 
301  state.shifters[&capturedTrack] = std::move( pHit );
302 
303  // Collect TrackShifters for the rest of the tracks
304  for ( auto track : trackList.Any() ) {
305  auto &pShifter = state.shifters[track];
306  if (!pShifter)
307  pShifter = MakeTrackShifter::Call( *track, project );
308  }
309 
310  if ( state.movingSelection ) {
311  // All selected tracks may move some intervals
312  const TrackInterval interval{
313  viewInfo.selectedRegion.t0(),
314  viewInfo.selectedRegion.t1()
315  };
316  for ( const auto &pair : state.shifters ) {
317  auto &shifter = *pair.second;
318  auto &track = shifter.GetTrack();
319  if (&track == &capturedTrack)
320  // Don't change the choice of intervals made by HitTest
321  continue;
322  if ( track.IsSelected() )
323  shifter.SelectInterval( interval );
324  }
325  }
326  else {
327  // Move intervals only of the chosen channel group
328  for ( auto channel : TrackList::Channels( &capturedTrack ) ) {
329  auto &shifter = *state.shifters[channel];
330  if ( channel != &capturedTrack )
331  shifter.SelectInterval(TrackInterval{clickTime, clickTime});
332  }
333  }
334 
335  // Sync lock propagation of unfixing of intervals
336  if ( syncLocked ) {
337  bool change = true;
338  while( change ) {
339  change = false;
340 
341  // Iterate over all unfixed intervals in all tracks
342  // that do propagation and are in sync lock groups ...
343  for ( auto &pair : state.shifters ) {
344  auto &shifter = *pair.second.get();
345  if (!shifter.SyncLocks())
346  continue;
347  auto &track = shifter.GetTrack();
348  auto group = TrackList::SyncLockGroup(&track);
349  if ( group.size() <= 1 )
350  continue;
351 
352  auto &intervals = shifter.MovingIntervals();
353  for (auto &interval : intervals) {
354 
355  // ...and tell all other tracks in the sync lock group
356  // to select that interval...
357  for ( auto pTrack2 : group ) {
358  if (pTrack2 == &track)
359  continue;
360 
361  auto &shifter2 = *shifters[pTrack2];
362  auto size = shifter2.MovingIntervals().size();
363  shifter2.SelectInterval( interval );
364  change = change ||
365  (shifter2.SyncLocks() &&
366  size != shifter2.MovingIntervals().size());
367  }
368 
369  }
370  }
371 
372  // ... and repeat if any other interval became unfixed in a
373  // shifter that propagates
374  }
375  }
376 }
377 
379 {
380  auto pTrack = mCapturedTrack.get();
381  if ( pTrack ) {
382  auto iter = shifters.find( pTrack );
383  if ( iter != shifters.end() ) {
384  auto &pShifter = iter->second;
385  if ( pShifter ) {
386  auto &intervals = pShifter->MovingIntervals();
387  if ( !intervals.empty() )
388  return &intervals[0];
389  }
390  }
391  }
392  return nullptr;
393 }
394 
395 double ClipMoveState::DoSlideHorizontal( double desiredSlideAmount )
396 {
397  auto &state = *this;
398  auto &capturedTrack = *state.mCapturedTrack;
399 
400  // Given a signed slide distance, move clips, but subject to constraint of
401  // non-overlapping with other clips, so the distance may be adjusted toward
402  // zero.
403  if ( !state.shifters.empty() ) {
404  double initialAllowed = 0;
405  do { // loop to compute allowed, does not actually move anything yet
406  initialAllowed = desiredSlideAmount;
407 
408  for (auto &pair : shifters) {
409  auto newAmount = pair.second->AdjustOffsetSmaller( desiredSlideAmount );
410  if ( desiredSlideAmount != newAmount ) {
411  if ( newAmount * desiredSlideAmount < 0 ||
412  fabs(newAmount) > fabs(desiredSlideAmount) ) {
413  wxASSERT( false ); // AdjustOffsetSmaller didn't honor postcondition!
414  newAmount = 0; // Be sure the loop progresses to termination!
415  }
416  desiredSlideAmount = newAmount;
417  state.snapLeft = state.snapRight = -1; // see bug 1067
418  }
419  if (newAmount == 0)
420  break;
421  }
422  } while ( desiredSlideAmount != initialAllowed );
423  }
424 
425  // Whether moving intervals or a whole track,
426  // finally, here is where clips are moved
427  if ( desiredSlideAmount != 0.0 )
428  state.DoHorizontalOffset( desiredSlideAmount );
429 
430  return (state.hSlideAmount = desiredSlideAmount);
431 }
432 
433 namespace {
435  const TrackList &tracks, const ClipMoveState::ShifterMap &shifters )
436 {
437  // Compare with the other function FindCandidates in Snap
438  // Make the snap manager more selective than it would be if just constructed
439  // from the track list
440  SnapPointArray candidates;
441  for ( const auto &pair : shifters ) {
442  auto &shifter = pair.second;
443  auto &track = shifter->GetTrack();
444  for (const auto &interval : shifter->FixedIntervals() ) {
445  candidates.emplace_back( interval.Start(), &track );
446  if ( interval.Start() != interval.End() )
447  candidates.emplace_back( interval.End(), &track );
448  }
449  }
450  return candidates;
451 }
452 }
453 
455 (const TrackPanelMouseEvent &evt, AudacityProject *pProject)
456 {
457  using namespace RefreshCode;
458  const bool unsafe = ProjectAudioIO::Get( *pProject ).IsAudioActive();
459  if ( unsafe )
460  return Cancelled;
461 
462  const wxMouseEvent &event = evt.event;
463  const wxRect &rect = evt.rect;
464  auto &viewInfo = ViewInfo::Get( *pProject );
465 
466  const auto pView = std::static_pointer_cast<TrackView>(evt.pCell);
467  const auto pTrack = pView ? pView->FindTrack().get() : nullptr;
468  if (!pTrack)
469  return RefreshCode::Cancelled;
470 
471  auto &trackList = TrackList::Get( *pProject );
472 
474  mDidSlideVertically = false;
475 
476  const bool multiToolModeActive =
478 
479  const double clickTime =
480  viewInfo.PositionToTime(event.m_x, rect.x);
481 
482  auto pShifter = MakeTrackShifter::Call( *pTrack, *pProject );
483 
484  auto hitTestResult = TrackShifter::HitTestResult::Track;
485  if (!event.ShiftDown()) {
487  rect, event.m_x, event.m_y
488  };
489  hitTestResult = pShifter->HitTest( clickTime, viewInfo, &params );
490  switch( hitTestResult ) {
492  return Cancelled;
493  default:
494  break;
495  }
496  }
497  else {
498  // just do shifting of one whole track
499  }
500 
501  mClipMoveState.Init( *pProject, *pTrack,
502  hitTestResult,
503  std::move( pShifter ),
504  clickTime,
505 
506  viewInfo, trackList,
507  ProjectSettings::Get( *pProject ).IsSyncLocked() );
508 
509  mSlideUpDownOnly = event.CmdDown() && !multiToolModeActive;
510  mRect = rect;
511  mClipMoveState.mMouseClickX = event.m_x;
512  mSnapManager =
513  std::make_shared<SnapManager>(*trackList.GetOwner(),
514  FindCandidates( trackList, mClipMoveState.shifters ),
515  viewInfo,
516  true, // don't snap to time
520  auto pInterval = mClipMoveState.CapturedInterval();
521  mSnapPreferRightEdge = pInterval &&
522  (fabs(clickTime - pInterval->End()) <
523  fabs(clickTime - pInterval->Start()));
524 
525  return RefreshNone;
526 }
527 
528 namespace {
530  const ViewInfo &viewInfo, wxCoord xx, const wxMouseEvent &event,
531  SnapManager *pSnapManager,
532  bool slideUpDownOnly, bool snapPreferRightEdge,
533  ClipMoveState &state,
534  Track &track )
535  {
536  auto &capturedTrack = *state.mCapturedTrack;
537  if (slideUpDownOnly)
538  return 0.0;
539  else {
540  double desiredSlideAmount =
541  viewInfo.PositionToTime(event.m_x) -
542  viewInfo.PositionToTime(state.mMouseClickX);
543  double clipLeft = 0, clipRight = 0;
544 
545  if (!state.shifters.empty())
546  desiredSlideAmount =
547  state.shifters[ &track ]->QuantizeOffset( desiredSlideAmount );
548 
549  // Adjust desiredSlideAmount using SnapManager
550  if (pSnapManager) {
551  auto pInterval = state.CapturedInterval();
552  if (pInterval) {
553  clipLeft = pInterval->Start() + desiredSlideAmount;
554  clipRight = pInterval->End() + desiredSlideAmount;
555  }
556  else {
557  clipLeft = capturedTrack.GetStartTime() + desiredSlideAmount;
558  clipRight = capturedTrack.GetEndTime() + desiredSlideAmount;
559  }
560 
561  auto results =
562  pSnapManager->Snap(&capturedTrack, clipLeft, false);
563  auto newClipLeft = results.outTime;
564  results =
565  pSnapManager->Snap(&capturedTrack, clipRight, false);
566  auto newClipRight = results.outTime;
567 
568  // Only one of them is allowed to snap
569  if (newClipLeft != clipLeft && newClipRight != clipRight) {
570  // Un-snap the un-preferred edge
571  if (snapPreferRightEdge)
572  newClipLeft = clipLeft;
573  else
574  newClipRight = clipRight;
575  }
576 
577  // Take whichever one snapped (if any) and compute the NEW desiredSlideAmount
578  state.snapLeft = -1;
579  state.snapRight = -1;
580  if (newClipLeft != clipLeft) {
581  const double difference = (newClipLeft - clipLeft);
582  desiredSlideAmount += difference;
583  state.snapLeft =
584  viewInfo.TimeToPosition(newClipLeft, xx);
585  }
586  else if (newClipRight != clipRight) {
587  const double difference = (newClipRight - clipRight);
588  desiredSlideAmount += difference;
589  state.snapRight =
590  viewInfo.TimeToPosition(newClipRight, xx);
591  }
592  }
593  return desiredSlideAmount;
594  }
595  }
596 
597  using Correspondence = std::unordered_map< Track*, Track* >;
598 
600  Correspondence &correspondence,
601  TrackList &trackList, Track &capturedTrack, Track &track,
602  ClipMoveState &state)
603  {
604  // Accumulate new pairs for the correspondence, and merge them
605  // into the given correspondence only on success
606  Correspondence newPairs;
607 
608  auto sameType = [&]( auto pTrack ){
609  return capturedTrack.SameKindAs( *pTrack );
610  };
611  if (!sameType(&track))
612  return false;
613 
614  // All tracks of the same kind as the captured track
615  auto range = trackList.Any() + sameType;
616 
617  // Find how far this track would shift down among those (signed)
618  const auto myPosition =
619  std::distance( range.first, trackList.Find( &capturedTrack ) );
620  const auto otherPosition =
621  std::distance( range.first, trackList.Find( &track ) );
622  auto diff = otherPosition - myPosition;
623 
624  // Point to destination track
625  auto iter = range.first.advance( diff > 0 ? diff : 0 );
626 
627  for (auto pTrack : range) {
628  auto &pShifter = state.shifters[pTrack];
629  if ( !pShifter->MovingIntervals().empty() ) {
630  // One of the interesting tracks
631 
632  auto pOther = *iter;
633  if ( diff < 0 || !pOther )
634  // No corresponding track
635  return false;
636 
637  if ( !pShifter->MayMigrateTo(*pOther) )
638  // Rejected for other reason
639  return false;
640 
641  if ( correspondence.count(pTrack) )
642  // Don't overwrite the given correspondence
643  return false;
644 
645  newPairs[ pTrack ] = pOther;
646  }
647 
648  if ( diff < 0 )
649  ++diff; // Still consuming initial tracks
650  else
651  ++iter; // Safe to increment TrackIter even at end of range
652  }
653 
654  // Success
655  if (correspondence.empty())
656  correspondence.swap(newPairs);
657  else
658  std::copy( newPairs.begin(), newPairs.end(),
659  std::inserter( correspondence, correspondence.end() ) );
660  return true;
661  }
662 
664  std::unordered_map<Track*, TrackShifter::Intervals>;
665 
666  bool CheckFit(
667  ClipMoveState &state, const Correspondence &correspondence,
668  const DetachedIntervals &intervals,
669  double tolerance, double &desiredSlideAmount )
670  {
671  bool ok = true;
672  double firstTolerance = tolerance;
673 
674  // The desiredSlideAmount may change and the tolerance may get used up.
675  for ( unsigned iPass = 0; iPass < 2 && ok; ++iPass ) {
676  for ( auto &pair : state.shifters ) {
677  auto *pSrcTrack = pair.first;
678  auto iter = correspondence.find( pSrcTrack );
679  if ( iter != correspondence.end() )
680  if( auto *pOtherTrack = iter->second )
681  if ( !(ok = pair.second->AdjustFit(
682  *pOtherTrack, intervals.at(pSrcTrack),
683  desiredSlideAmount /*in,out*/, tolerance)) )
684  break;
685  }
686 
687  // If it fits ok, desiredSlideAmount could have been updated to get
688  // the clip to fit.
689  // Check again, in the new position, this time with zero tolerance.
690  if (firstTolerance == 0)
691  break;
692  else
693  tolerance = 0.0;
694  }
695 
696  return ok;
697  }
698 
699  [[noreturn]] void MigrationFailure() {
700  // Tracks may be in an inconsistent state; throw to the application
701  // handler which restores consistency from undo history
703  XO("Could not shift between tracks")};
704  }
705 
708  : state( clipMoveState )
709  {
710  // Pluck the moving clips out of their tracks
711  for (auto &pair : state.shifters)
712  detached[pair.first] = pair.second->Detach();
713  }
714 
715  void Reinsert(
716  std::unordered_map< Track*, Track* > *pCorrespondence )
717  {
718  for (auto &pair : detached) {
719  auto pTrack = pair.first;
720  if (pCorrespondence && pCorrespondence->count(pTrack))
721  pTrack = (*pCorrespondence)[pTrack];
722  auto &pShifter = state.shifters[pTrack];
723  if (!pShifter->Attach( std::move( pair.second ) ))
725  }
726  }
727 
730  };
731 }
732 
734 ( ViewInfo &viewInfo, wxCoord xx,
735  ClipMoveState &state, TrackList &trackList,
736  Track &dstTrack, double &desiredSlideAmount )
737 {
738  Correspondence correspondence;
739 
740  // See if captured track corresponds to another
741  auto &capturedTrack = *state.mCapturedTrack;
742  if (!FindCorrespondence(
743  correspondence, trackList, capturedTrack, dstTrack, state ))
744  return false;
745 
746  // Try to extend the correpondence
747  auto tryExtend = [&](bool forward){
748  auto begin = trackList.begin(), end = trackList.end();
749  auto pCaptured = trackList.Find( &capturedTrack );
750  auto pDst = trackList.Find( &dstTrack );
751  // Scan for more correspondences
752  while ( true ) {
753  // Remember that TrackIter wraps circularly to the end iterator when
754  // decrementing it
755 
756  // First move to a track with moving intervals and
757  // without a correspondent
758  do
759  forward ? ++pCaptured : --pCaptured;
760  while ( pCaptured != end &&
761  ( correspondence.count(*pCaptured) || state.shifters[*pCaptured]->MovingIntervals().empty() ) );
762  if ( pCaptured == end )
763  break;
764 
765  // Change the choice of possible correspondent track too
766  do
767  forward ? ++pDst : --pDst;
768  while ( pDst != end && correspondence.count(*pDst) );
769  if ( pDst == end )
770  break;
771 
772  // Make correspondence if we can
773  if (!FindCorrespondence(
774  correspondence, trackList, **pCaptured, **pDst, state ))
775  break;
776  }
777  };
778  // Try extension, backward first, then forward
779  // (anticipating the case of dragging a label that is under a clip)
780  tryExtend(false);
781  tryExtend(true);
782 
783  // Having passed that test, remove clips temporarily from their
784  // tracks, so moving clips don't interfere with each other
785  // when we call CanInsertClip()
786  TemporaryClipRemover remover{ state };
787 
788  // Now check that the move is possible
789  double slide = desiredSlideAmount; // remember amount requested.
790  // The test for tolerance will need review with FishEye!
791  // The tolerance is supposed to be the time for one pixel,
792  // i.e. one pixel tolerance at current zoom.
793  double tolerance =
794  viewInfo.PositionToTime(xx + 1) - viewInfo.PositionToTime(xx);
795  bool ok = CheckFit( state, correspondence, remover.detached,
796  tolerance, desiredSlideAmount /*in,out*/ );
797 
798  if (!ok) {
799  // Failure, even with using tolerance.
800  remover.Reinsert( nullptr );
801  return false;
802  }
803 
804  // Make the offset permanent; start from a "clean slate"
805  state.mMouseClickX = xx;
806  remover.Reinsert( &correspondence );
807  return true;
808 }
809 
811 (const TrackPanelMouseEvent &evt, AudacityProject *pProject)
812 {
813  using namespace RefreshCode;
814  const bool unsafe = ProjectAudioIO::Get( *pProject ).IsAudioActive();
815  if (unsafe) {
816  this->Cancel(pProject);
817  return RefreshAll | Cancelled;
818  }
819 
820  const wxMouseEvent &event = evt.event;
821  auto &viewInfo = ViewInfo::Get( *pProject );
822 
823  TrackView *trackView = dynamic_cast<TrackView*>(evt.pCell.get());
824  Track *track = trackView ? trackView->FindTrack().get() : nullptr;
825 
826  // Uncommenting this permits drag to continue to work even over the controls area
827  /*
828  track = static_cast<CommonTrackPanelCell*>(evt.pCell)->FindTrack().get();
829  */
830 
831  if (!track) {
832  // Allow sliding if the pointer is not over any track, but only if x is
833  // within the bounds of the tracks area.
834  if (event.m_x >= mRect.GetX() &&
835  event.m_x < mRect.GetX() + mRect.GetWidth())
836  track = mClipMoveState.mCapturedTrack.get();
837  }
838 
839  // May need a shared_ptr to reassign mCapturedTrack below
840  auto pTrack = Track::SharedPointer( track );
841  if (!pTrack)
843 
844 
845  auto &trackList = TrackList::Get( *pProject );
846 
847  // GM: slide now implementing snap-to
848  // samples functionality based on sample rate.
849 
850  // Start by undoing the current slide amount; everything
851  // happens relative to the original horizontal position of
852  // each clip...
854 
856  // Slide the selection, too
857  viewInfo.selectedRegion.move( -mClipMoveState.hSlideAmount );
858  }
860 
861  double desiredSlideAmount =
862  FindDesiredSlideAmount( viewInfo, mRect.x, event, mSnapManager.get(),
864  *pTrack );
865 
866  // Scroll during vertical drag.
867  // If the mouse is over a track that isn't the captured track,
868  // decide which tracks the captured clips should go to.
869  // EnsureVisible(pTrack); //vvv Gale says this has problems on Linux, per bug 393 thread. Revert for 2.0.2.
870  bool slidVertically = (
872  /* && !mCapturedClipIsSelection*/
873  && DoSlideVertical( viewInfo, event.m_x, mClipMoveState,
874  trackList, *pTrack, desiredSlideAmount ) );
875  if (slidVertically)
876  {
878  mDidSlideVertically = true;
879  }
880 
881  if (desiredSlideAmount == 0.0)
882  return RefreshAll;
883 
884  // Note that mouse dragging doesn't use TrackShifter::HintOffsetLarger()
885 
886  mClipMoveState.DoSlideHorizontal( desiredSlideAmount );
887 
889  // Slide the selection, too
890  viewInfo.selectedRegion.move( mClipMoveState.hSlideAmount );
891  }
892 
893  if (slidVertically) {
894  // NEW origin
896  }
897 
898  return RefreshAll;
899 }
900 
902 (const TrackPanelMouseState &, AudacityProject *pProject)
903 {
904  // After all that, it still may be unsafe to drag.
905  // Even if so, make an informative cursor change from default to "banned."
906  const bool unsafe = ProjectAudioIO::Get( *pProject ).IsAudioActive();
907  return HitPreview(pProject, unsafe);
908 }
909 
911 (const TrackPanelMouseEvent &, AudacityProject *pProject,
912  wxWindow *)
913 {
914  using namespace RefreshCode;
915  const bool unsafe = ProjectAudioIO::Get( *pProject ).IsAudioActive();
916  if (unsafe)
917  return this->Cancel(pProject);
918 
919  Result result = RefreshNone;
920 
921  // Do not draw yellow lines
922  if ( mClipMoveState.snapLeft != -1 || mClipMoveState.snapRight != -1) {
924  result |= RefreshAll;
925  }
926 
928  return result;
929 
930  for ( auto &pair : mClipMoveState.shifters )
931  if ( !pair.second->FinishMigration() )
933 
934  TranslatableString msg;
935  bool consolidate;
936  if (mDidSlideVertically) {
937  msg = XO("Moved clips to another track");
938  consolidate = false;
939  }
940  else {
941  msg = ( mClipMoveState.hSlideAmount > 0
942  ? XO("Time shifted tracks/clips right %.02f seconds")
943  : XO("Time shifted tracks/clips left %.02f seconds")
944  )
945  .Format( fabs( mClipMoveState.hSlideAmount ) );
946  consolidate = true;
947  }
948  ProjectHistory::Get( *pProject ).PushState(msg, XO("Time-Shift"),
949  consolidate ? (UndoPush::CONSOLIDATE) : (UndoPush::NONE));
950 
951  return result | FixScrollbars;
952 }
953 
955 {
956  ProjectHistory::Get( *pProject ).RollbackState();
958 }
959 
961  TrackPanelDrawingContext &context,
962  const wxRect &rect, unsigned iPass )
963 {
964  if ( iPass == TrackArtist::PassSnapping ) {
965  auto &dc = context.dc;
966  // Draw snap guidelines if we have any
967  if ( mSnapManager ) {
968  mSnapManager->Draw(
970  }
971  }
972 }
973 
976  const wxRect &rect, const wxRect &panelRect, unsigned iPass )
977 {
978  if ( iPass == TrackArtist::PassSnapping )
979  return MaximizeHeight( rect, panelRect );
980  else
981  return rect;
982 }
TrackPanelMouseEvent::pCell
std::shared_ptr< TrackPanelCell > pCell
Definition: TrackPanelMouseEvent.h:61
TimeShiftHandle::TimeShiftHandle
TimeShiftHandle(const TimeShiftHandle &)=delete
anonymous_namespace{TimeShiftHandle.cpp}::TemporaryClipRemover
Definition: TimeShiftHandle.cpp:706
SnapManager::Snap
SnapResults Snap(Track *currentTrack, double t, bool rightEdge)
Definition: Snap.cpp:269
TranslatableString
Holds a msgid for the translation catalog; may also bind format arguments.
Definition: TranslatableString.h:32
TrackShifter::QuantizeOffset
virtual double QuantizeOffset(double desiredOffset)
Given amount to shift by horizontally, do any preferred rounding, before placement constraint checks.
Definition: TimeShiftHandle.cpp:157
RefreshCode::FixScrollbars
@ FixScrollbars
Definition: RefreshCode.h:27
ViewInfo::Get
static ViewInfo & Get(AudacityProject &project)
Definition: ViewInfo.cpp:156
TimeShiftHandle.h
TrackShifter::UnfixAll
void UnfixAll()
Change all intervals from fixed to moving.
Definition: TimeShiftHandle.cpp:132
TrackShifter::mFixed
Intervals mFixed
Definition: TimeShiftHandle.h:172
anonymous_namespace{TimeShiftHandle.cpp}::TemporaryClipRemover::state
ClipMoveState & state
Definition: TimeShiftHandle.cpp:728
TrackPanelMouseEvent::rect
const wxRect & rect
Definition: TrackPanelMouseEvent.h:59
TrackShifter::MayMigrateTo
virtual bool MayMigrateTo(Track &otherTrack)
Whether intervals may migrate to the other track, not yet checking all placement constraints *‍/.
Definition: TimeShiftHandle.cpp:167
RefreshCode::RefreshAll
@ RefreshAll
Definition: RefreshCode.h:26
TimeShiftHandle::Drag
Result Drag(const TrackPanelMouseEvent &event, AudacityProject *pProject) override
Definition: TimeShiftHandle.cpp:811
RefreshCode::RefreshNone
@ RefreshNone
Definition: RefreshCode.h:21
AttachedVirtualFunction::Call
static Return Call(This &obj, Arguments &&...arguments)
Invoke the method – but only after static initialization time.
Definition: AttachedVirtualFunction.h:229
TrackShifter::HitTestResult::Track
@ Track
Shift selected track and sister channels only, as a whole.
ClipMoveState::snapLeft
wxInt64 snapLeft
Definition: TimeShiftHandle.h:240
AttachedVirtualFunction
Class template generates single-dispatch, open method registry tables.
Definition: AttachedVirtualFunction.h:161
TrackView.h
ClipMoveState::DoHorizontalOffset
void DoHorizontalOffset(double offset)
Offset tracks or intervals horizontally, without adjusting the offset.
Definition: TimeShiftHandle.cpp:102
Track::GetIntervals
virtual ConstIntervals GetIntervals() const
Report times on the track where important intervals begin and end, for UI to snap to.
Definition: Track.cpp:1232
CommonTrackPanelCell::FindTrack
std::shared_ptr< Track > FindTrack()
Definition: CommonTrackPanelCell.h:44
TrackPanelDrawingContext
Definition: TrackPanelDrawingContext.h:22
TrackList::Channels
static auto Channels(TrackType *pTrack) -> TrackIterRange< TrackType >
Definition: Track.h:1467
Format
Abstract base class used in importing a file.
TrackList
A flat linked list of tracks supporting Add, Remove, Clear, and Contains, serialization of the list o...
Definition: Track.h:1263
TrackArtist::PassSnapping
@ PassSnapping
Definition: TrackArtist.h:70
ConstTrackInterval::End
double End() const
Definition: Track.h:206
MakeCursor
std::unique_ptr< wxCursor > MakeCursor(int WXUNUSED(CursorId), const char *const pXpm[36], int HotX, int HotY)
Definition: TrackPanel.cpp:177
ViewInfo
Definition: ViewInfo.h:172
ClipMoveState
Definition: TimeShiftHandle.h:202
ClipMoveState::snapRight
wxInt64 snapRight
Definition: TimeShiftHandle.h:240
Track::SharedPointer
std::shared_ptr< Subclass > SharedPointer()
Definition: Track.h:279
registerMakeTrackShifter
static MakeTrackShifter registerMakeTrackShifter
Definition: TimeShiftHandle.cpp:265
TrackShifter::mAllFixed
bool mAllFixed
Definition: TimeShiftHandle.h:176
anonymous_namespace{TimeShiftHandle.cpp}::DetachedIntervals
std::unordered_map< Track *, TrackShifter::Intervals > DetachedIntervals
Definition: TimeShiftHandle.cpp:664
TimeShiftHandle::mSnapPreferRightEdge
bool mSnapPreferRightEdge
Definition: TimeShiftHandle.h:322
RefreshCode::Cancelled
@ Cancelled
Definition: RefreshCode.h:23
UndoPush::NONE
@ NONE
XO
#define XO(s)
Definition: Internat.h:31
ProgressResult::Cancelled
@ Cancelled
anonymous_namespace{TimeShiftHandle.cpp}::TemporaryClipRemover::Reinsert
void Reinsert(std::unordered_map< Track *, Track * > *pCorrespondence)
Definition: TimeShiftHandle.cpp:715
Track::GetOwner
std::shared_ptr< TrackList > GetOwner() const
Definition: Track.h:367
ProjectSettings::Get
static ProjectSettings & Get(AudacityProject &project)
Definition: ProjectSettings.cpp:40
TrackList::Find
auto Find(Track *pTrack) -> TrackIter< TrackType >
Turn a pointer into a TrackIter (constant time); get end iterator if this does not own the track.
Definition: Track.h:1316
TimeShiftHandle::DrawingArea
wxRect DrawingArea(TrackPanelDrawingContext &, const wxRect &rect, const wxRect &panelRect, unsigned iPass) override
Definition: TimeShiftHandle.cpp:974
TrackPanelDrawingContext::dc
wxDC & dc
Definition: TrackPanelDrawingContext.h:23
TimeShiftHandle::HitTest
static UIHandlePtr HitTest(std::weak_ptr< TimeShiftHandle > &holder, const wxMouseState &state, const wxRect &rect, const std::shared_ptr< Track > &pTrack)
Definition: TimeShiftHandle.cpp:74
TimeShiftHandle::mSlideUpDownOnly
bool mSlideUpDownOnly
Definition: TimeShiftHandle.h:320
CoarseTrackShifter::CoarseTrackShifter
CoarseTrackShifter(Track &track)
Definition: TimeShiftHandle.cpp:241
TrackShifter::HitTestResult::Miss
@ Miss
Don't shift anything.
TrackInterval
A start and an end time, and mutative access to optional extra information.
Definition: Track.h:218
ProjectSettings::GetTool
int GetTool() const
Definition: ProjectSettings.h:91
ProjectAudioIO::Get
static ProjectAudioIO & Get(AudacityProject &project)
Definition: ProjectAudioIO.cpp:23
TimeShiftHandle::mSnapManager
std::shared_ptr< SnapManager > mSnapManager
Definition: TimeShiftHandle.h:328
ZoomInfo::TimeToPosition
wxInt64 TimeToPosition(double time, wxInt64 origin=0, bool ignoreFisheye=false) const
STM: Converts a project time to screen x position.
Definition: ZoomInfo.cpp:53
SnapResults::outTime
double outTime
Definition: Snap.h:47
ProjectAudioIO::IsAudioActive
bool IsAudioActive() const
Definition: ProjectAudioIO.cpp:52
TimeShiftHandle::mClipMoveState
ClipMoveState mClipMoveState
Definition: TimeShiftHandle.h:330
NotifyingSelectedRegion::t1
double t1() const
Definition: ViewInfo.h:46
TrackShifter::CommonMayMigrateTo
bool CommonMayMigrateTo(Track &otherTrack)
Definition: TimeShiftHandle.cpp:172
anonymous_namespace{TimeShiftHandle.cpp}::TemporaryClipRemover::detached
DetachedIntervals detached
Definition: TimeShiftHandle.cpp:729
TrackList::end
iterator end()
Definition: Track.h:1308
ClipMoveState::mCapturedTrack
std::shared_ptr< Track > mCapturedTrack
Definition: TimeShiftHandle.h:235
TimeShiftHandle::Enter
void Enter(bool forward, AudacityProject *) override
Definition: TimeShiftHandle.cpp:37
ProjectSettings::IsSyncLocked
bool IsSyncLocked() const
Definition: ProjectSettings.cpp:192
TrackShifter::HitTestResult::Selection
@ Selection
Shfit chosen intervals of this track; may shift other tracks' intervals.
TrackShifter::HitTestResult
HitTestResult
Possibilities for HitTest on the clicked track.
Definition: TimeShiftHandle.h:42
TrackShifter::~TrackShifter
virtual ~TrackShifter()=0
TrackShifter::TrackShifter
TrackShifter()
ViewInfo::selectedRegion
NotifyingSelectedRegion selectedRegion
Definition: ViewInfo.h:199
UIHandle::mChangeHighlight
Result mChangeHighlight
Definition: UIHandle.h:133
ToolCodes::multiTool
@ multiTool
Definition: ProjectSettings.h:40
TrackShifter::mMoving
Intervals mMoving
Definition: TimeShiftHandle.h:173
TrackShifter::FinishMigration
virtual bool FinishMigration()
When dragging is done, do (once) the final steps of migration (which may be expensive)
Definition: TimeShiftHandle.cpp:219
Track::Offset
void Offset(double t)
Definition: Track.h:427
TimeShiftHandle::mDidSlideVertically
bool mDidSlideVertically
Definition: TimeShiftHandle.h:319
ClipMoveState::shifters
ShifterMap shifters
Definition: TimeShiftHandle.h:239
kPixelTolerance
const int kPixelTolerance
Definition: Snap.h:28
TrackList::begin
iterator begin()
Definition: Track.h:1307
ClipMoveState::hSlideAmount
double hSlideAmount
Definition: TimeShiftHandle.h:238
CoarseTrackShifter::~CoarseTrackShifter
~CoarseTrackShifter() override
TrackShifter::Attach
virtual bool Attach(Intervals intervals)
Put moving intervals into the track, which may have migrated from another.
Definition: TimeShiftHandle.cpp:214
Track::SameKindAs
bool SameKindAs(const Track &track) const
Definition: Track.h:480
anonymous_namespace{TimeShiftHandle.cpp}::Correspondence
std::unordered_map< Track *, Track * > Correspondence
Definition: TimeShiftHandle.cpp:597
UIHandle::Result
unsigned Result
Definition: UIHandle.h:37
ClipMoveState::CapturedInterval
const TrackInterval * CapturedInterval() const
Return pointer to the first fixed interval of the captured track, if there is one.
Definition: TimeShiftHandle.cpp:378
TimeShiftHandle::HitPreview
static HitTestPreview HitPreview(const AudacityProject *pProject, bool unsafe)
Definition: TimeShiftHandle.cpp:45
TimeShiftHandle::Release
Result Release(const TrackPanelMouseEvent &event, AudacityProject *pProject, wxWindow *pParent) override
Definition: TimeShiftHandle.cpp:911
TrackShifter::Detach
virtual Intervals Detach()
Remove all moving intervals from the track, if possible.
Definition: TimeShiftHandle.cpp:203
TrackShifter::AllFixed
bool AllFixed() const
Definition: TimeShiftHandle.h:168
TimeShiftHandle::Preview
HitTestPreview Preview(const TrackPanelMouseState &state, AudacityProject *pProject) override
Definition: TimeShiftHandle.cpp:902
UIHandlePtr
std::shared_ptr< UIHandle > UIHandlePtr
Definition: CellularPanel.h:28
HitTestPreview
Definition: HitTestResult.h:20
TrackShifter::InitIntervals
void InitIntervals()
Derived class constructor can initialize all intervals reported by the track as fixed,...
Definition: TimeShiftHandle.cpp:235
anonymous_namespace{TimeShiftHandle.cpp}::FindDesiredSlideAmount
double FindDesiredSlideAmount(const ViewInfo &viewInfo, wxCoord xx, const wxMouseEvent &event, SnapManager *pSnapManager, bool slideUpDownOnly, bool snapPreferRightEdge, ClipMoveState &state, Track &track)
Definition: TimeShiftHandle.cpp:529
TrackPanelDrawable::MaximizeHeight
static wxRect MaximizeHeight(const wxRect &rect, const wxRect &panelRect)
Definition: TrackPanelDrawable.h:52
ClipMoveState::movingSelection
bool movingSelection
Definition: TimeShiftHandle.h:237
ClipMoveState::DoSlideHorizontal
double DoSlideHorizontal(double desiredSlideAmount)
Do sliding of tracks and intervals, maybe adjusting the offset.
Definition: TimeShiftHandle.cpp:395
TimeShiftHandle::DoSlideVertical
static bool DoSlideVertical(ViewInfo &viewInfo, wxCoord xx, ClipMoveState &state, TrackList &trackList, Track &dstTrack, double &desiredSlideAmount)
Definition: TimeShiftHandle.cpp:734
RefreshCode::RefreshCell
@ RefreshCell
Definition: RefreshCode.h:24
ClipMoveState::ShifterMap
std::unordered_map< Track *, std::unique_ptr< TrackShifter > > ShifterMap
Definition: TimeShiftHandle.h:211
AttachedVirtualFunction::Implementation
static Function Implementation()
A function returning a std::function, which you must define so that the program links.
Definition: TimeShiftHandle.cpp:260
TrackShifter::AdjustT0
virtual double AdjustT0(double t0) const
Definition: TimeShiftHandle.cpp:230
TimeShiftHandle::mRect
wxRect mRect
Definition: TimeShiftHandle.h:317
ClipMoveState::mMouseClickX
int mMouseClickX
Definition: TimeShiftHandle.h:242
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:267
ProjectHistory::PushState
void PushState(const TranslatableString &desc, const TranslatableString &shortDesc)
Definition: ProjectHistory.cpp:90
TrackShifter::AdjustFit
virtual bool AdjustFit(const Track &otherTrack, const Intervals &intervals, double &desiredOffset, double tolerance)
Test whether intervals can fit into another track, maybe adjusting the offset slightly.
Definition: TimeShiftHandle.cpp:208
TrackList::Get
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:495
Track
Abstract base class for an object holding data associated with points on a time axis.
Definition: Track.h:238
TrackShifter::HitTestResult::Intervals
@ Intervals
Shift intervals only of selected track and sister channels.
TrackView
Definition: TrackView.h:24
TrackShifter::GetTrack
virtual Track & GetTrack() const =0
There is always an associated track.
TimeShiftHandle::Cancel
Result Cancel(AudacityProject *pProject) override
Definition: TimeShiftHandle.cpp:954
ProjectHistory::RollbackState
void RollbackState()
Definition: ProjectHistory.cpp:117
AudacityProject
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:112
anonymous_namespace{TimeShiftHandle.cpp}::MigrationFailure
void MigrationFailure()
Definition: TimeShiftHandle.cpp:699
TrackPanelMouseEvent
Definition: TrackPanelMouseEvent.h:46
SnapManager
Definition: Snap.h:56
anonymous_namespace{TimeShiftHandle.cpp}::FindCandidates
SnapPointArray FindCandidates(const TrackList &tracks, const ClipMoveState::ShifterMap &shifters)
Definition: TimeShiftHandle.cpp:434
CoarseTrackShifter::SyncLocks
bool SyncLocks() override
Returns false.
Definition: TimeShiftHandle.cpp:255
TrackPanelMouseState
Definition: TrackPanelMouseEvent.h:28
anonymous_namespace{TimeShiftHandle.cpp}::FindCorrespondence
bool FindCorrespondence(Correspondence &correspondence, TrackList &trackList, Track &capturedTrack, Track &track, ClipMoveState &state)
Definition: TimeShiftHandle.cpp:599
ExceptionType::Internal
@ Internal
Indicates internal failure from Audacity.
UndoPush::CONSOLIDATE
@ CONSOLIDATE
TimeShiftHandle::Draw
void Draw(TrackPanelDrawingContext &context, const wxRect &rect, unsigned iPass) override
Definition: TimeShiftHandle.cpp:960
TrackShifter::DoHorizontalOffset
virtual void DoHorizontalOffset(double offset)
Definition: TimeShiftHandle.cpp:224
CoarseTrackShifter::HitTest
HitTestResult HitTest(double, const ViewInfo &, HitTestParams *) override
Decide how shift behaves, based on the track that is clicked in.
Definition: TimeShiftHandle.cpp:249
anonymous_namespace{TimeShiftHandle.cpp}::CheckFit
bool CheckFit(ClipMoveState &state, const Correspondence &correspondence, const DetachedIntervals &intervals, double tolerance, double &desiredSlideAmount)
Definition: TimeShiftHandle.cpp:666
NotifyingSelectedRegion::t0
double t0() const
Definition: ViewInfo.h:45
params
EffectDistortion::Params params
Definition: Distortion.cpp:99
ConstTrackInterval::Start
double Start() const
Definition: Track.h:205
TrackList::Any
auto Any() -> TrackIterRange< TrackType >
Definition: Track.h:1354
AssignUIHandlePtr
std::shared_ptr< Subclass > AssignUIHandlePtr(std::weak_ptr< Subclass > &holder, const std::shared_ptr< Subclass > &pNew)
Definition: UIHandle.h:145
TimeShiftHandle::~TimeShiftHandle
virtual ~TimeShiftHandle()
Definition: TimeShiftHandle.cpp:98
TrackShifter::UnfixIntervals
void UnfixIntervals(std::function< bool(const TrackInterval &) > pred)
Change intervals satisfying a predicate from fixed to moving.
Definition: TimeShiftHandle.cpp:118
TimeShiftHandle::Click
Result Click(const TrackPanelMouseEvent &event, AudacityProject *pProject) override
Definition: TimeShiftHandle.cpp:455
TrackShifter::AdjustOffsetSmaller
virtual double AdjustOffsetSmaller(double desiredOffset)
Given amount to shift by horizontally, maybe adjust it toward zero to meet placement constraints.
Definition: TimeShiftHandle.cpp:162
TrackList::SyncLockGroup
static TrackIterRange< Track > SyncLockGroup(Track *pTrack)
Definition: Track.cpp:639
RefreshCode
Namespace containing an enum 'what to do on a refresh?'.
Definition: RefreshCode.h:16
ClipMoveState::clear
void clear()
Definition: TimeShiftHandle.h:244
anonymous_namespace{TimeShiftHandle.cpp}::TemporaryClipRemover::TemporaryClipRemover
TemporaryClipRemover(ClipMoveState &clipMoveState)
Definition: TimeShiftHandle.cpp:707
SimpleMessageBoxException
A MessageBoxException that shows a given, unvarying string.
Definition: AudacityException.h:91
TimeShiftHandle::HitAnywhere
static UIHandlePtr HitAnywhere(std::weak_ptr< TimeShiftHandle > &holder, const std::shared_ptr< Track > &pTrack, bool gripHit)
Definition: TimeShiftHandle.cpp:65
TrackShifter::CommonSelectInterval
void CommonSelectInterval(const TrackInterval &interval)
Definition: TimeShiftHandle.cpp:144
TrackShifter::Intervals
std::vector< TrackInterval > Intervals
Definition: TimeShiftHandle.h:67
TrackPanelMouseEvent::event
wxMouseEvent & event
Definition: TrackPanelMouseEvent.h:58
SnapPointArray
std::vector< SnapPoint > SnapPointArray
Definition: Snap.h:43
AttachedVirtualFunction::Function
std::function< Return(Object &, Arguments...) > Function
This member name is declared in this class template and redeclared in each override.
Definition: AttachedVirtualFunction.h:167
TrackShifter::SelectInterval
virtual void SelectInterval(const TrackInterval &interval)
Notifies the shifter that a region is selected, so it may update its fixed and moving intervals.
Definition: TimeShiftHandle.cpp:139
ProjectHistory::Get
static ProjectHistory & Get(AudacityProject &project)
Definition: ProjectHistory.cpp:26
ZoomInfo::PositionToTime
double PositionToTime(wxInt64 position, wxInt64 origin=0, bool ignoreFisheye=false) const
Definition: ZoomInfo.cpp:43
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:152
TrackShifter::HitTestParams
Optional, more complete information for hit testing.
Definition: TimeShiftHandle.h:50