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 }
34 Track &GetTrack() const override { return *mpTrack; }
35
36 static inline size_t& GetIndex(ChannelGroupInterval &interval)
37 {
38 return static_cast<LabelTrack::Interval&>(interval).index;
39 }
40
41 static inline size_t GetIndex(const ChannelGroupInterval &interval)
42 {
43 return GetIndex(const_cast<ChannelGroupInterval&>(interval));
44 }
45
47 double time, const ViewInfo &viewInfo, HitTestParams *pParams ) override
48 {
50 auto t0 = viewInfo.selectedRegion.t0();
51 auto t1 = viewInfo.selectedRegion.t1();
52 if ( mpTrack->IsSelected() && time >= t0 && time < t1 )
54
55 // Prefer the box that the mouse hovers over, else the selected one
56 int iLabel = -1;
57 if ( pParams )
58 iLabel =
59 LabelTrackView::OverATextBox(*mpTrack, pParams->xx, pParams->yy);
60 if (iLabel == -1)
62 if (iLabel != -1) {
63 UnfixIntervals([&](const auto &myInterval){
64 return GetIndex( myInterval ) == iLabel;
65 });
66 return result;
67 }
68 else {
69 // If the pick is within the selection, which overlaps some intervals,
70 // then move those intervals only
71 // Else move all labels (preserving the older beahvior of time shift)
72 if ( result == HitTestResult::Selection )
73 SelectInterval({ t0, t1 });
74 if (mMoving.empty())
76 else
77 return result;
78 }
79 }
80
81 void SelectInterval(TimeInterval interval) override
82 {
83 CommonSelectInterval(interval);
84 }
85
86 bool SyncLocks() override { return false; }
87
88 bool MayMigrateTo(Track &otherTrack) override
89 {
90 return CommonMayMigrateTo(otherTrack);
91 }
92
93 /* We need to copy a complete label when detaching it because LabelStruct
94 is stored in a vector in LabelTrack without an extra indirection.
95 So the detached intervals handed back to the caller are unlike those
96 reported by LabelTrack, but carry the extra information. */
99 wxString title;
100 MovingInterval(double start, double end, const LabelStruct &label)
101 : start{ start }, end{ end }
102 , region{ label.selectedRegion }
103 , title{ label.title }
104 {}
105 double Start() const override { return start; }
106 double End() const override { return end; }
107 const double start, end;
108 };
109
110 Intervals Detach() override
111 {
112 auto pTrack = mpTrack.get();
113 auto moveLabel = [pTrack](const auto &pInterval) {
114 auto index = GetIndex(*pInterval);
115 auto result = std::make_shared<MovingInterval>(
116 pInterval->Start(), pInterval->End(), *pTrack->GetLabel(index));
117 pTrack->DeleteLabel(index);
118 return result;
119 };
120 Intervals result;
121 std::transform(
122 // Reverse traversal may lessen the shifting-left in the label array
123 // responding to label deletion messages published by DeleteLabel
124 mMoving.rbegin(), mMoving.rend(), std::back_inserter(result),
125 moveLabel);
126 mMoving = Intervals{};
127 return result;
128 }
129
131 const Track &, const Intervals &, double &, double ) override
132 {
133 // Labels have no overlapping constraints, so just...
134 return true;
135 }
136
137 bool Attach(Intervals intervals, double offset) override
138 {
139 auto pTrack = mpTrack.get();
140 std::for_each(intervals.rbegin(), intervals.rend(),
141 [this, pTrack, offset](const auto &pInterval){
142 auto data = static_cast<const MovingInterval&>(*pInterval);
143 if (offset != .0)
144 data.region.move(offset);
145 auto index = pTrack->AddLabel(data.region, data.title);
146 // Recreate the simpler TrackInterval as would be reported by LabelTrack
147 mMoving.emplace_back(pTrack->MakeInterval(index));
148 } );
149 return true;
150 }
151
152 void DoHorizontalOffset(double offset) override
153 {
154 auto &labels = mpTrack->GetLabels();
155 for (auto &pInterval : MovingIntervals()) {
156 auto index = GetIndex(*pInterval);
157 auto labelStruct = labels[index];
158 labelStruct.selectedRegion.move(offset);
159 mpTrack->SetLabel(index, labelStruct);
160 }
161
162 mpTrack->SortLabels(); // causes callback to OnLabelPermuted
163 }
164
165private:
167 {
168 if (e.mpTrack.lock() != mpTrack)
169 return;
170
171 auto former = e.mFormerPosition;
172 auto present = e.mPresentPosition;
173
174 // Avoid signed-unsigned comparison below!
175 if (former < 0 || present < 0) {
176 assert(false);
177 return;
178 }
179
180 auto update = [=](const auto &pInterval){
181 auto &index = GetIndex(*pInterval);
182 if (index == former)
183 index = present;
184 else if (former < index && index <= present)
185 -- index;
186 else if (former > index && index >= present)
187 ++ index;
188 };
189
190 std::for_each(mFixed.begin(), mFixed.end(), update);
191 std::for_each(mMoving.begin(), mMoving.end(), update);
192 }
193
195 {
196 if (e.mpTrack.lock() != mpTrack)
197 return;
198
199 auto present = e.mPresentPosition;
200
201 // Avoid signed-unsigned comparison below!
202 if (present < 0) {
203 assert(false);
204 return;
205 }
206
207 auto update = [=](const auto &pInterval){
208 auto &index = GetIndex(*pInterval);
209 if (index >= present)
210 ++index;
211 };
212
213 std::for_each(mFixed.begin(), mFixed.end(), update);
214 std::for_each(mMoving.begin(), mMoving.end(), update);
215 }
216
218 {
219 if (e.mpTrack.lock() != mpTrack)
220 return;
221
222 auto former = e.mFormerPosition;
223
224 // Avoid signed-unsigned comparison below!
225 if (former < 0) {
226 assert(false);
227 return;
228 }
229
230 auto update = [=](const auto &pInterval){
231 auto &index = GetIndex(*pInterval);
232 if (index > former)
233 --index;
234 else if (index == former)
235 // It should have been deleted first!
236 assert(false);
237 };
238
239 std::for_each(mFixed.begin(), mFixed.end(), update);
240 std::for_each(mMoving.begin(), mMoving.end(), update);
241 }
242
244 const std::shared_ptr<LabelTrack> mpTrack;
246};
247
250 return [](LabelTrack &track, AudacityProject &project) {
251 return std::make_unique<LabelTrackShifter>(track, project);
252 };
253}
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:29
A LabelStruct holds information for ONE label in a LabelTrack.
Definition: LabelTrack.h:40
A LabelTrack is a Track that holds labels (LabelStruct).
Definition: LabelTrack.h:98
Track & GetTrack() const override
There is always an associated track.
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)
void SelectInterval(TimeInterval interval) override
Notifies the shifter that a region is selected, so it may update its fixed and moving intervals.
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 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:110
Abstract base class for policies to manipulate a track type for Time Shift.
Intervals mFixed
void CommonSelectInterval(TimeInterval interval)
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:216
For defining overrides of the method.
const std::weak_ptr< Track > mpTrack
Definition: LabelTrack.h:241
enum LabelTrackEvent::Type type
MovingInterval(double start, double end, const LabelStruct &label)
Optional, more complete information for hit testing.
A simple time interval.