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