Audacity 3.2.0
TrackUtilities.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 TrackUtilities.cpp
6
7 Paul Licameli split from TrackMenus.cpp
8
9 **********************************************************************/
10
11#include "TrackUtilities.h"
12
13#include "PlayableTrack.h"
14#include "ProjectHistory.h"
15#include "TrackFocus.h"
16#include "TrackPanel.h"
17#include "Viewport.h"
18
19namespace TrackUtilities {
20
22{
24 auto &trackPanel = TrackPanel::Get(project);
25
26 auto range = tracks.Selected();
27 using Iter = decltype(range.begin());
28
29 // Capture the leader track preceding the first removed track
30 std::optional<Iter> focus;
31 if (!range.empty()) {
32 auto iter = tracks.Find(*range.begin());
33 // TrackIter allows decrement even of begin iterators
34 focus.emplace(--iter);
35 }
36
37 while (!range.empty())
38 // range iterates over leaders only
39 tracks.Remove(**range.first++);
40
41 if (!(focus.has_value() && **focus))
42 // try to use the last leader track
43 focus.emplace(tracks.end().advance(-1));
44 assert(focus);
45 Track *f = **focus;
46 // Try to use the first track after the removal
47 // TrackIter allows increment even of end iterators
48 if (const auto nextF = * ++ *focus)
49 f = nextF;
50
51 // If we actually have something left, then set focus and make sure it's seen
52 if (f) {
55 }
56
58 .PushState(XO("Removed audio track(s)"), XO("Remove Track"));
59
60 trackPanel.UpdateViewIfNoTracks();
61}
62
63void DoTrackMute(AudacityProject &project, Track *t, bool exclusive)
64{
65 auto &tracks = TrackList::Get( project );
66
67 // Whatever t is, replace with lead channel
68 t = *tracks.Find(t);
69
70 // "exclusive" mute means mute the chosen track and unmute all others.
71 if (exclusive) {
72 for (auto leader : tracks.Any<PlayableTrack>()) {
73 bool chosen = (t == leader);
74 leader->SetMute(chosen);
75 leader->SetSolo(false);
76 }
77 }
78 else {
79 // Normal click toggles this track.
80 auto pt = dynamic_cast<PlayableTrack *>( t );
81 if (!pt)
82 return;
83
84 bool wasMute = pt->GetMute();
85 pt->SetMute(!wasMute);
86
87 if (auto value = TracksBehaviorsSolo.ReadEnum();
88 value == SoloBehaviorSimple || value == SoloBehaviorNone)
89 {
90 // We also set a solo indicator if we have just one track / stereo pair playing.
91 // in a group of more than one playable tracks.
92 // otherwise clear solo on everything.
93
94 auto range = tracks.Any<PlayableTrack>();
95 auto nPlayableTracks = range.size();
96 auto nPlaying = (range - &PlayableTrack::GetMute).size();
97 for (auto track : range)
98 track->SetSolo((nPlaying == 1) &&
99 (nPlayableTracks > 1) && !track->GetMute());
100 }
101 }
103
104 TrackFocus::Get( project ).UpdateAccessibility();
105}
106
107void DoTrackSolo(AudacityProject &project, Track *t, bool exclusive)
108{
109 auto &tracks = TrackList::Get( project );
110
111 // Whatever t is, replace with lead channel
112 t = *tracks.Find(t);
113
114 const auto pt = dynamic_cast<PlayableTrack *>( t );
115 if (!pt)
116 return;
117 bool bWasSolo = pt->GetSolo();
118
119 bool simple = (TracksBehaviorsSolo.ReadEnum() == SoloBehaviorSimple);
120 bool bSoloMultiple = !simple ^ exclusive;
121
122 // Standard and Simple solo have opposite defaults:
123 // Standard - Behaves as individual buttons, shift=radio buttons
124 // Simple - Behaves as radio buttons, shift=individual
125 // In addition, Simple solo will mute/unmute tracks
126 // when in standard radio button mode.
127 if (bSoloMultiple)
128 pt->SetSolo(!bWasSolo);
129 else {
130 // Normal click solo this track only, mute everything else.
131 // OR unmute and unsolo everything.
132 for (auto leader : tracks.Any<PlayableTrack>()) {
133 bool chosen = (t == leader);
134 if (chosen) {
135 leader->SetSolo(!bWasSolo);
136 if (simple)
137 leader->SetMute(false);
138 }
139 else {
140 leader->SetSolo(false);
141 if (simple)
142 leader->SetMute(!bWasSolo);
143 }
144 }
145 }
147
148 TrackFocus::Get( project ).UpdateAccessibility();
149}
150
152{
153 if (!toRemove)
154 return;
155
157 auto &trackFocus = TrackFocus::Get(project);
158
159 const auto iter = tracks.Find(toRemove);
160
161 // If it was focused, then NEW focus is the next or, if
162 // unavailable, the previous track. (The NEW focus is set
163 // after the track has been removed.)
164 bool toRemoveWasFocused = trackFocus.Get() == *iter;
165 std::optional<decltype(iter)> newFocus{};
166 if (toRemoveWasFocused) {
167 auto iterNext = iter,
168 iterPrev = iter;
169 newFocus.emplace(++iterNext);
170 if (!**newFocus)
171 newFocus.emplace(--iterPrev);
172 }
173
174 wxString name = toRemove->GetName();
175
176 // By construction of iter
177 assert((*iter)->IsLeader());
178 tracks.Remove(**iter);
179
180 if (toRemoveWasFocused)
181 trackFocus.Set(**newFocus);
182
184 XO("Removed track '%s'.").Format(name),
185 XO("Track Remove"));
186}
187
189(AudacityProject &project, Track* target, MoveChoice choice)
190{
191 auto &tracks = TrackList::Get( project );
192
193 TranslatableString longDesc, shortDesc;
194
195 switch (choice)
196 {
197 case OnMoveTopID:
198 /* i18n-hint: Past tense of 'to move', as in 'moved audio track up'.*/
199 longDesc = XO("Moved '%s' to Top");
200 shortDesc = XO("Move Track to Top");
201
202 // TODO: write TrackList::Rotate to do this in one step and avoid emitting
203 // an event for each swap
204 while (tracks.CanMoveUp(target))
205 tracks.Move(target, true);
206
207 break;
208 case OnMoveBottomID:
209 /* i18n-hint: Past tense of 'to move', as in 'moved audio track up'.*/
210 longDesc = XO("Moved '%s' to Bottom");
211 shortDesc = XO("Move Track to Bottom");
212
213 // TODO: write TrackList::Rotate to do this in one step and avoid emitting
214 // an event for each swap
215 while (tracks.CanMoveDown(target))
216 tracks.Move(target, false);
217
218 break;
219 default:
220 bool bUp = (OnMoveUpID == choice);
221
222 tracks.Move(target, bUp);
223 longDesc =
224 /* i18n-hint: Past tense of 'to move', as in 'moved audio track up'.*/
225 bUp? XO("Moved '%s' Up")
226 : XO("Moved '%s' Down");
227 shortDesc =
228 /* i18n-hint: Past tense of 'to move', as in 'moved audio track up'.*/
229 bUp? XO("Move Track Up")
230 : XO("Move Track Down");
231
232 }
233
234 longDesc.Format(target->GetName());
235
236 ProjectHistory::Get( project ).PushState(longDesc, shortDesc);
237}
238
239}
const TranslatableString name
Definition: Distortion.cpp:76
XO("Cut/Copy/Paste")
EnumSetting< SoloBehavior > TracksBehaviorsSolo
Extends Track with notions of mute and solo setting.
@ SoloBehaviorSimple
Definition: PlayableTrack.h:70
@ SoloBehaviorNone
Definition: PlayableTrack.h:72
const auto tracks
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
size_t size() const
How many attachment pointers are in the Site.
Definition: ClientData.h:259
Abstract base class used in importing a file.
AudioTrack subclass that can also be audibly replayed by the program.
Definition: PlayableTrack.h:40
bool GetSolo() const
Definition: PlayableTrack.h:48
bool GetMute() const
Definition: PlayableTrack.h:47
void PushState(const TranslatableString &desc, const TranslatableString &shortDesc)
void ModifyState(bool bWantsAutoSave)
static ProjectHistory & Get(AudacityProject &project)
Track * Get()
Definition: TrackFocus.cpp:156
Abstract base class for an object holding data associated with points on a time axis.
Definition: Track.h:122
const wxString & GetName() const
Name is always the same for all channels of a group.
Definition: Track.cpp:56
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:347
static TrackPanel & Get(AudacityProject &project)
Definition: TrackPanel.cpp:233
Holds a msgid for the translation catalog; may also bind format arguments.
TranslatableString & Format(Args &&...args) &
Capture variadic format arguments (by copy) when there is no plural.
void ShowTrack(const Track &track)
Definition: Viewport.cpp:478
static Viewport & Get(AudacityProject &project)
Definition: Viewport.cpp:32
void DoTrackMute(AudacityProject &project, Track *t, bool exclusive)
void DoMoveTrack(AudacityProject &project, Track *target, MoveChoice choice)
Move a track up, down, to top or to bottom.
void DoTrackSolo(AudacityProject &project, Track *t, bool exclusive)
void DoRemoveTrack(AudacityProject &project, Track *toRemove)
void DoRemoveTracks(AudacityProject &project)