Audacity 3.2.0
LabelTrackShifter.cpp
Go to the documentation of this file.
1
6#include "LabelTrackView.h"
7#include "../../ui/TimeShiftHandle.h"
8#include "../../../LabelTrack.h"
9#include "ViewInfo.h"
10
11class LabelTrackShifter final : public TrackShifter {
12public:
14 : mpTrack{ track.SharedPointer<LabelTrack>() }
15 , mProject{ project }
16 {
18 mSubscription = mpTrack->Subscribe([this](const LabelTrackEvent &e){
19 switch (e.type) {
20 case LabelTrackEvent::Permutation:
21 return OnLabelPermuted(e);
22 case LabelTrackEvent::Addition:
23 return OnLabelAdded(e);
24 case LabelTrackEvent::Deletion:
25 return OnLabelDeleted(e);
26 default:
27 return;
28 }
29 });
30 }
32 {
33 }
35 Track &GetTrack() const override { return *mpTrack; }
36
37 static inline size_t& GetIndex(ChannelGroupInterval &interval)
38 {
39 return static_cast<LabelTrack::Interval&>(interval).index;
40 }
41
42 static inline size_t GetIndex(const ChannelGroupInterval &interval)
43 {
44 return GetIndex(const_cast<ChannelGroupInterval&>(interval));
45 }
46
48 double time, const ViewInfo &viewInfo, HitTestParams *pParams ) override
49 {
51 auto t0 = viewInfo.selectedRegion.t0();
52 auto t1 = viewInfo.selectedRegion.t1();
53 if ( mpTrack->IsSelected() && time >= t0 && time < t1 )
55
56 // Prefer the box that the mouse hovers over, else the selected one
57 int iLabel = -1;
58 if ( pParams )
59 iLabel =
60 LabelTrackView::OverATextBox(*mpTrack, pParams->xx, pParams->yy);
61 if (iLabel == -1)
63 if (iLabel != -1) {
64 UnfixIntervals([&](const auto &myInterval){
65 return GetIndex( myInterval ) == iLabel;
66 });
67 return result;
68 }
69 else {
70 // If the pick is within the selection, which overlaps some intervals,
71 // then move those intervals only
72 // Else move all labels (preserving the older beahvior of time shift)
73 if ( result == HitTestResult::Selection )
74 SelectInterval({ t0, t1 });
75 if (mMoving.empty())
77 else
78 return result;
79 }
80 }
81
82 void SelectInterval(const ChannelGroupInterval &interval) override
83 {
84 CommonSelectInterval(interval);
85 }
86
87 bool SyncLocks() override { return false; }
88
89 bool MayMigrateTo(Track &otherTrack) override
90 {
91 return CommonMayMigrateTo(otherTrack);
92 }
93
94 /* We need to copy a complete label when detaching it because LabelStruct
95 is stored in a vector in LabelTrack without an extra indirection.
96 So the detached intervals handed back to the caller are unlike those
97 reported by LabelTrack, but carry the extra information. */
100 wxString title;
101 MovingInterval(double start, double end, const LabelStruct &label)
102 : ChannelGroupInterval{ start, end }
103 , region{ label.selectedRegion }
104 , title{ label.title }
105 {}
106 };
107
108 Intervals Detach() override
109 {
110 auto pTrack = mpTrack.get();
111 auto moveLabel = [pTrack](const auto &pInterval) {
112 auto index = GetIndex(*pInterval);
113 auto result = std::make_shared<MovingInterval>(
114 pInterval->Start(), pInterval->End(), *pTrack->GetLabel(index));
115 pTrack->DeleteLabel(index);
116 return result;
117 };
118 Intervals result;
119 std::transform(
120 // Reverse traversal may lessen the shifting-left in the label array
121 // responding to label deletion messages published by DeleteLabel
122 mMoving.rbegin(), mMoving.rend(), std::back_inserter(result),
123 moveLabel);
124 mMoving = Intervals{};
125 return result;
126 }
127
129 const Track &, const Intervals &, double &, double ) override
130 {
131 // Labels have no overlapping constraints, so just...
132 return true;
133 }
134
135 bool Attach(Intervals intervals, double offset) override
136 {
137 auto pTrack = mpTrack.get();
138 std::for_each(intervals.rbegin(), intervals.rend(),
139 [this, pTrack, offset](const auto &pInterval){
140 auto data = static_cast<const MovingInterval&>(*pInterval);
141 if (offset != .0)
142 data.region.move(offset);
143 auto index = pTrack->AddLabel(data.region, data.title);
144 // Recreate the simpler TrackInterval as would be reported by LabelTrack
145 mMoving.emplace_back(pTrack->MakeInterval(index));
146 } );
147 return true;
148 }
149
150 void DoHorizontalOffset(double offset) override
151 {
152 auto &labels = mpTrack->GetLabels();
153 for (auto &pInterval : MovingIntervals()) {
154 auto index = GetIndex(*pInterval);
155 auto labelStruct = labels[index];
156 labelStruct.selectedRegion.move(offset);
157 mpTrack->SetLabel(index, labelStruct);
158 }
159
160 mpTrack->SortLabels(); // causes callback to OnLabelPermuted
161 }
162
163private:
165 {
166 if (e.mpTrack.lock() != mpTrack)
167 return;
168
169 auto former = e.mFormerPosition;
170 auto present = e.mPresentPosition;
171
172 // Avoid signed-unsigned comparison below!
173 if (former < 0 || present < 0) {
174 assert(false);
175 return;
176 }
177
178 auto update = [=](const auto &pInterval){
179 auto &index = GetIndex(*pInterval);
180 if (index == former)
181 index = present;
182 else if (former < index && index <= present)
183 -- index;
184 else if (former > index && index >= present)
185 ++ index;
186 };
187
188 std::for_each(mFixed.begin(), mFixed.end(), update);
189 std::for_each(mMoving.begin(), mMoving.end(), update);
190 }
191
193 {
194 if (e.mpTrack.lock() != mpTrack)
195 return;
196
197 auto present = e.mPresentPosition;
198
199 // Avoid signed-unsigned comparison below!
200 if (present < 0) {
201 assert(false);
202 return;
203 }
204
205 auto update = [=](const auto &pInterval){
206 auto &index = GetIndex(*pInterval);
207 if (index >= present)
208 ++index;
209 };
210
211 std::for_each(mFixed.begin(), mFixed.end(), update);
212 std::for_each(mMoving.begin(), mMoving.end(), update);
213 }
214
216 {
217 if (e.mpTrack.lock() != mpTrack)
218 return;
219
220 auto former = e.mFormerPosition;
221
222 // Avoid signed-unsigned comparison below!
223 if (former < 0) {
224 assert(false);
225 return;
226 }
227
228 auto update = [=](const auto &pInterval){
229 auto &index = GetIndex(*pInterval);
230 if (index > former)
231 --index;
232 else if (index == former)
233 // It should have been deleted first!
234 assert(false);
235 };
236
237 std::for_each(mFixed.begin(), mFixed.end(), update);
238 std::for_each(mMoving.begin(), mMoving.end(), update);
239 }
240
242 const std::shared_ptr<LabelTrack> mpTrack;
244};
245
248 return [](LabelTrack &track, AudacityProject &project) {
249 return std::make_unique<LabelTrackShifter>(track, project);
250 };
251}
DEFINE_ATTACHED_VIRTUAL_OVERRIDE(MakeLabelTrackShifter)
TranslatableString label
Definition: TagsEditor.cpp:165
const auto project
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:90
A start and an end time, and whatever else subclasses associate with them.
Definition: Channel.h:30
A LabelStruct holds information for ONE label in a LabelTrack.
Definition: LabelTrack.h:29
A LabelTrack is a Track that holds labels (LabelStruct).
Definition: LabelTrack.h:87
Track & GetTrack() const override
Label track is always leader; satisfying the post.
static size_t & GetIndex(ChannelGroupInterval &interval)
void DoHorizontalOffset(double offset) override
AudacityProject & mProject
static size_t GetIndex(const ChannelGroupInterval &interval)
bool MayMigrateTo(Track &otherTrack) override
void OnLabelPermuted(const LabelTrackEvent &e)
Intervals Detach() override
Remove all moving intervals from the track, if possible.
void OnLabelAdded(const LabelTrackEvent &e)
~LabelTrackShifter() override
const std::shared_ptr< LabelTrack > mpTrack
LabelTrackShifter(LabelTrack &track, AudacityProject &project)
Observer::Subscription mSubscription
bool SyncLocks() override
Whether unfixing of an interval should propagate to all overlapping intervals in the sync lock group.
bool AdjustFit(const Track &, const Intervals &, double &, double) override
HitTestResult HitTest(double time, const ViewInfo &viewInfo, HitTestParams *pParams) override
Decide how shift behaves, based on the track that is clicked in.
bool Attach(Intervals intervals, double offset) override
Put moving intervals into the track, which may have migrated from another.
void SelectInterval(const ChannelGroupInterval &interval) override
Notifies the shifter that a region is selected, so it may update its fixed and moving intervals.
void OnLabelDeleted(const LabelTrackEvent e)
static LabelTrackView & Get(LabelTrack &)
static int OverATextBox(const LabelTrack &track, int xx, int yy)
int GetNavigationIndex(AudacityProject &project) const
double t1() const
Definition: ViewInfo.h:36
double t0() const
Definition: ViewInfo.h:35
A move-only handle representing a connection to a Publisher.
Definition: Observer.h:70
Defines a selected portion of a project.
Abstract base class for an object holding data associated with points on a time axis.
Definition: Track.h:122
Abstract base class for policies to manipulate a track type for Time Shift.
void CommonSelectInterval(const ChannelGroupInterval &interval)
Intervals mFixed
void InitIntervals()
Derived class constructor can initialize all intervals reported by the track as fixed,...
const Intervals & MovingIntervals() const
Return special intervals of the track that may move.
void UnfixIntervals(std::function< bool(const ChannelGroupInterval &)> pred)
Change intervals satisfying a predicate from fixed to moving.
bool CommonMayMigrateTo(Track &otherTrack)
HitTestResult
Possibilities for HitTest on the clicked track.
@ Selection
Shift chosen intervals of this track; may shift other tracks' intervals.
@ Intervals
Shift intervals only of selected track and sister channels.
@ Track
Shift selected track and sister channels only, as a whole.
Intervals mMoving
std::vector< std::shared_ptr< ChannelGroupInterval > > Intervals
NotifyingSelectedRegion selectedRegion
Definition: ViewInfo.h:215
auto end(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
Definition: PackedArray.h:159
For defining overrides of the method.
const std::weak_ptr< Track > mpTrack
Definition: LabelTrack.h:222
enum LabelTrackEvent::Type type
MovingInterval(double start, double end, const LabelStruct &label)
Optional, more complete information for hit testing.