From 4abd38b9a03ccb887ce1dde8abf690a9ea67626a Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Sun, 20 Sep 2020 12:28:04 -0400 Subject: [PATCH] Bug2464 extra: labels should be draggable from one track to another --- src/tracks/labeltrack/ui/LabelTrackView.cpp | 123 +++++++++++++++++++- 1 file changed, 122 insertions(+), 1 deletion(-) diff --git a/src/tracks/labeltrack/ui/LabelTrackView.cpp b/src/tracks/labeltrack/ui/LabelTrackView.cpp index a5ac6d6c5..9af036272 100644 --- a/src/tracks/labeltrack/ui/LabelTrackView.cpp +++ b/src/tracks/labeltrack/ui/LabelTrackView.cpp @@ -2145,11 +2145,19 @@ public: InitIntervals(); mpTrack->Bind( EVT_LABELTRACK_PERMUTED, &LabelTrackShifter::OnLabelPermuted, this ); + mpTrack->Bind( + EVT_LABELTRACK_ADDITION, &LabelTrackShifter::OnLabelAdded, this ); + mpTrack->Bind( + EVT_LABELTRACK_DELETION, &LabelTrackShifter::OnLabelDeleted, this ); } ~LabelTrackShifter() override { mpTrack->Unbind( EVT_LABELTRACK_PERMUTED, &LabelTrackShifter::OnLabelPermuted, this ); + mpTrack->Unbind( + EVT_LABELTRACK_ADDITION, &LabelTrackShifter::OnLabelAdded, this ); + mpTrack->Unbind( + EVT_LABELTRACK_DELETION, &LabelTrackShifter::OnLabelDeleted, this ); } Track &GetTrack() const override { return *mpTrack; } @@ -2207,6 +2215,66 @@ public: bool SyncLocks() override { return false; } + bool MayMigrateTo( Track &otherTrack ) override + { + return CommonMayMigrateTo(otherTrack); + } + + /* We need to copy a complete label when detaching it because LabelStruct + is stored in a vector in LabelTrack without an extra indirection. + So the detached intervals handed back to the caller are unlike those + reported by LabelTrack, but carry the extra information. */ + struct IntervalData final: Track::IntervalData { + SelectedRegion region; + wxString title; + IntervalData(const LabelStruct &label) + : region{label.selectedRegion} + , title{label.title} + {} + }; + + Intervals Detach() override + { + auto pTrack = mpTrack.get(); + auto moveLabel = [pTrack](TrackInterval &interval) -> TrackInterval { + auto &rindex = GetIndex(interval); + auto index = rindex; + rindex = -1; + auto result = TrackInterval{ + interval.Start(), interval.End(), + std::make_unique( *pTrack->GetLabel(index) ) }; + pTrack->DeleteLabel(index); + return result; + }; + Intervals result; + std::transform( + // Reverse traversal may lessen the shifting-left in the label array + mMoving.rbegin(), mMoving.rend(), std::back_inserter(result), + moveLabel ); + mMoving = Intervals{}; + return result; + } + + bool AdjustFit( + const Track &, const Intervals &, double &, double ) override + { + // Labels have no overlapping constraints, so just... + return true; + } + + bool Attach( Intervals intervals ) override + { + auto pTrack = mpTrack.get(); + std::for_each( intervals.rbegin(), intervals.rend(), + [this, pTrack](auto &interval){ + auto pData = static_cast( interval.Extra() ); + auto index = pTrack->AddLabel(pData->region, pData->title); + // Recreate the simpler TrackInterval as would be reported by LabelTrack + mMoving.emplace_back( pTrack->MakeInterval(index) ); + } ); + return true; + } + void DoHorizontalOffset( double offset ) override { auto &labels = mpTrack->GetLabels(); @@ -2217,7 +2285,7 @@ public: mpTrack->SetLabel( index, labelStruct ); } - mpTrack->SortLabels(); + mpTrack->SortLabels(); // causes callback to OnLabelPermuted } private: @@ -2250,6 +2318,59 @@ private: std::for_each(mMoving.begin(), mMoving.end(), update); } + void OnLabelAdded( LabelTrackEvent &e ) + { + e.Skip(); + if ( e.mpTrack.lock() != mpTrack ) + return; + + auto present = e.mPresentPosition; + + // Avoid signed-unsigned comparison below! + if (present < 0) { + wxASSERT(false); + return; + } + + auto update = [=]( TrackInterval &interval ){ + auto pExtra = static_cast(interval.Extra()); + auto &index = pExtra->index; + if ( index >= present ) + ++ index; + }; + + std::for_each(mFixed.begin(), mFixed.end(), update); + std::for_each(mMoving.begin(), mMoving.end(), update); + } + + void OnLabelDeleted( LabelTrackEvent &e ) + { + e.Skip(); + if ( e.mpTrack.lock() != mpTrack ) + return; + + auto former = e.mFormerPosition; + + // Avoid signed-unsigned comparison below! + if (former < 0) { + wxASSERT(false); + return; + } + + auto update = [=]( TrackInterval &interval ){ + auto pExtra = static_cast(interval.Extra()); + auto &index = pExtra->index; + if ( index > former ) + -- index; + else if ( index == former ) + // It should have been deleted first! + wxASSERT( false ); + }; + + std::for_each(mFixed.begin(), mFixed.end(), update); + std::for_each(mMoving.begin(), mMoving.end(), update); + } + std::shared_ptr mpTrack; AudacityProject &mProject; };