audacia/src/TrackUtilities.cpp

250 lines
7.2 KiB
C++

/**********************************************************************
Audacity: A Digital Audio Editor
TrackUtilities.cpp
Paul Licameli split from TrackMenus.cpp
**********************************************************************/
#include "TrackUtilities.h"
#include "ProjectHistory.h"
#include "ProjectSettings.h"
#include "ProjectWindow.h"
#include "Track.h"
#include "TrackPanelAx.h"
#include "TrackPanel.h"
namespace TrackUtilities {
void DoRemoveTracks( AudacityProject &project )
{
auto &tracks = TrackList::Get( project );
auto &trackPanel = TrackPanel::Get( project );
std::vector<Track*> toRemove;
for (auto track : tracks.Selected())
toRemove.push_back(track);
// Capture the track preceding the first removed track
Track *f{};
if (!toRemove.empty()) {
auto found = tracks.Find(toRemove[0]);
f = *--found;
}
for (auto track : toRemove)
tracks.Remove(track);
if (!f)
// try to use the last track
f = *tracks.Any().rbegin();
if (f) {
// Try to use the first track after the removal
auto found = tracks.FindLeader(f);
auto t = *++found;
if (t)
f = t;
}
// If we actually have something left, then set focus and make sure it's seen
if (f) {
TrackFocus::Get(project).Set(f);
f->EnsureVisible();
}
ProjectHistory::Get( project )
.PushState(XO("Removed audio track(s)"), XO("Remove Track"));
trackPanel.UpdateViewIfNoTracks();
}
void DoTrackMute(AudacityProject &project, Track *t, bool exclusive)
{
const auto &settings = ProjectSettings::Get( project );
auto &tracks = TrackList::Get( project );
// Whatever t is, replace with lead channel
t = *tracks.FindLeader(t);
// "exclusive" mute means mute the chosen track and unmute all others.
if (exclusive) {
for (auto leader : tracks.Leaders<PlayableTrack>()) {
const auto group = TrackList::Channels(leader);
bool chosen = (t == leader);
for (auto channel : group)
channel->SetMute( chosen ),
channel->SetSolo( false );
}
}
else {
// Normal click toggles this track.
auto pt = dynamic_cast<PlayableTrack *>( t );
if (!pt)
return;
bool wasMute = pt->GetMute();
for (auto channel : TrackList::Channels(pt))
channel->SetMute( !wasMute );
if (settings.IsSoloSimple() || settings.IsSoloNone())
{
// We also set a solo indicator if we have just one track / stereo pair playing.
// in a group of more than one playable tracks.
// otherwise clear solo on everything.
auto range = tracks.Leaders<PlayableTrack>();
auto nPlayableTracks = range.size();
auto nPlaying = (range - &PlayableTrack::GetMute).size();
for (auto track : tracks.Any<PlayableTrack>())
// will set both of a stereo pair
track->SetSolo( (nPlaying==1) && (nPlayableTracks > 1 ) && !track->GetMute() );
}
}
ProjectHistory::Get( project ).ModifyState(true);
TrackFocus::Get( project ).UpdateAccessibility();
}
void DoTrackSolo(AudacityProject &project, Track *t, bool exclusive)
{
const auto &settings = ProjectSettings::Get( project );
auto &tracks = TrackList::Get( project );
// Whatever t is, replace with lead channel
t = *tracks.FindLeader(t);
const auto pt = dynamic_cast<PlayableTrack *>( t );
if (!pt)
return;
bool bWasSolo = pt->GetSolo();
bool bSoloMultiple = !settings.IsSoloSimple() ^ exclusive;
// Standard and Simple solo have opposite defaults:
// Standard - Behaves as individual buttons, shift=radio buttons
// Simple - Behaves as radio buttons, shift=individual
// In addition, Simple solo will mute/unmute tracks
// when in standard radio button mode.
if ( bSoloMultiple )
{
for (auto channel : TrackList::Channels(pt))
channel->SetSolo( !bWasSolo );
}
else
{
// Normal click solo this track only, mute everything else.
// OR unmute and unsolo everything.
for (auto leader : tracks.Leaders<PlayableTrack>()) {
const auto group = TrackList::Channels(leader);
bool chosen = (t == leader);
for (auto channel : group) {
if (chosen) {
channel->SetSolo( !bWasSolo );
if( settings.IsSoloSimple() )
channel->SetMute( false );
}
else {
channel->SetSolo( false );
if( settings.IsSoloSimple() )
channel->SetMute( !bWasSolo );
}
}
}
}
ProjectHistory::Get( project ).ModifyState(true);
TrackFocus::Get( project ).UpdateAccessibility();
}
void DoRemoveTrack(AudacityProject &project, Track * toRemove)
{
auto &tracks = TrackList::Get( project );
auto &trackFocus = TrackFocus::Get( project );
auto &window = ProjectWindow::Get( project );
// If it was focused, then NEW focus is the next or, if
// unavailable, the previous track. (The NEW focus is set
// after the track has been removed.)
bool toRemoveWasFocused = trackFocus.Get() == toRemove;
Track* newFocus{};
if (toRemoveWasFocused) {
auto iterNext = tracks.FindLeader(toRemove), iterPrev = iterNext;
newFocus = *++iterNext;
if (!newFocus) {
newFocus = *--iterPrev;
}
}
wxString name = toRemove->GetName();
auto channels = TrackList::Channels(toRemove);
// Be careful to post-increment over positions that get erased!
auto &iter = channels.first;
while (iter != channels.end())
tracks.Remove( * iter++ );
if (toRemoveWasFocused)
trackFocus.Set( newFocus );
ProjectHistory::Get( project ).PushState(
XO("Removed track '%s.'").Format( name ),
XO("Track Remove"));
}
void DoMoveTrack
(AudacityProject &project, Track* target, MoveChoice choice)
{
auto &tracks = TrackList::Get( project );
TranslatableString longDesc, shortDesc;
switch (choice)
{
case OnMoveTopID:
/* i18n-hint: Past tense of 'to move', as in 'moved audio track up'.*/
longDesc = XO("Moved '%s' to Top");
shortDesc = XO("Move Track to Top");
// TODO: write TrackList::Rotate to do this in one step and avoid emitting
// an event for each swap
while (tracks.CanMoveUp(target))
tracks.Move(target, true);
break;
case OnMoveBottomID:
/* i18n-hint: Past tense of 'to move', as in 'moved audio track up'.*/
longDesc = XO("Moved '%s' to Bottom");
shortDesc = XO("Move Track to Bottom");
// TODO: write TrackList::Rotate to do this in one step and avoid emitting
// an event for each swap
while (tracks.CanMoveDown(target))
tracks.Move(target, false);
break;
default:
bool bUp = (OnMoveUpID == choice);
tracks.Move(target, bUp);
longDesc =
/* i18n-hint: Past tense of 'to move', as in 'moved audio track up'.*/
bUp? XO("Moved '%s' Up")
: XO("Moved '%s' Down");
shortDesc =
/* i18n-hint: Past tense of 'to move', as in 'moved audio track up'.*/
bUp? XO("Move Track Up")
: XO("Move Track Down");
}
longDesc.Format(target->GetName());
ProjectHistory::Get( project ).PushState(longDesc, shortDesc);
}
}