mirror of
https://github.com/cookiengineer/audacity
synced 2025-07-31 16:09:28 +02:00
Slim AudacityProject some...
... move some specialized operations with few uses into menus/
This commit is contained in:
commit
6fe0c6dd45
@ -118,6 +118,7 @@ void DoSelectSomething( AudacityProject &project );
|
||||
}
|
||||
|
||||
namespace ViewActions {
|
||||
double GetZoomOfToFit( const AudacityProject &project );
|
||||
void DoZoomFit( AudacityProject &project );
|
||||
void DoZoomFitV( AudacityProject &project );
|
||||
}
|
||||
@ -139,6 +140,12 @@ namespace TrackActions {
|
||||
};
|
||||
/// Move a track up, down, to top or to bottom.
|
||||
void DoMoveTrack( AudacityProject &project, Track* target, MoveChoice choice );
|
||||
// "exclusive" mute means mute the chosen track and unmute all others.
|
||||
void DoTrackMute( AudacityProject &project, Track *pTrack, bool exclusive );
|
||||
// Type of solo (standard or simple) follows the set preference, unless
|
||||
// exclusive == true, which causes the opposite behavior.
|
||||
void DoTrackSolo( AudacityProject &project, Track *pTrack, bool exclusive );
|
||||
void DoRemoveTrack( AudacityProject &project, Track * toRemove );
|
||||
void DoRemoveTracks( AudacityProject & );
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
**********************************************************************/
|
||||
|
||||
#include "Audacity.h"
|
||||
#include "Menus.h"
|
||||
#include "MixerBoard.h"
|
||||
|
||||
#include <cfloat>
|
||||
@ -744,7 +745,8 @@ void MixerTrackCluster::OnSlider_Pan(wxCommandEvent& WXUNUSED(event))
|
||||
|
||||
void MixerTrackCluster::OnButton_Mute(wxCommandEvent& WXUNUSED(event))
|
||||
{
|
||||
mProject->HandleTrackMute(mTrack.get(), mToggleButton_Mute->WasShiftDown());
|
||||
TrackActions::DoTrackMute(
|
||||
*mProject, mTrack.get(), mToggleButton_Mute->WasShiftDown());
|
||||
mToggleButton_Mute->SetAlternateIdx(mTrack->GetSolo() ? 1 : 0);
|
||||
|
||||
// Update the TrackPanel correspondingly.
|
||||
@ -759,7 +761,8 @@ void MixerTrackCluster::OnButton_Mute(wxCommandEvent& WXUNUSED(event))
|
||||
|
||||
void MixerTrackCluster::OnButton_Solo(wxCommandEvent& WXUNUSED(event))
|
||||
{
|
||||
mProject->HandleTrackSolo(mTrack.get(), mToggleButton_Solo->WasShiftDown());
|
||||
TrackActions::DoTrackSolo(
|
||||
*mProject, mTrack.get(), mToggleButton_Solo->WasShiftDown());
|
||||
bool bIsSolo = mTrack->GetSolo();
|
||||
mToggleButton_Mute->SetAlternateIdx(bIsSolo ? 1 : 0);
|
||||
|
||||
|
442
src/Project.cpp
442
src/Project.cpp
@ -2951,7 +2951,7 @@ AudacityProject *AudacityProject::OpenProject(
|
||||
pProject->OpenFile( fileNameArg, addtohistory );
|
||||
pNewProject = nullptr;
|
||||
if( pProject && pProject->mIsRecovered )
|
||||
pProject->Zoom( pProject->GetZoomOfToFit() );
|
||||
pProject->Zoom( ViewActions::GetZoomOfToFit( *pProject ) );
|
||||
|
||||
return pProject;
|
||||
}
|
||||
@ -4739,26 +4739,6 @@ void AudacityProject::ClearClipboard()
|
||||
}
|
||||
}
|
||||
|
||||
void AudacityProject::Clear()
|
||||
{
|
||||
for (auto n : GetTracks()->Any()) {
|
||||
if (n->GetSelected() || n->IsSyncLockSelected()) {
|
||||
n->Clear(mViewInfo.selectedRegion.t0(), mViewInfo.selectedRegion.t1());
|
||||
}
|
||||
}
|
||||
|
||||
double seconds = mViewInfo.selectedRegion.duration();
|
||||
|
||||
mViewInfo.selectedRegion.collapseToT0();
|
||||
|
||||
PushState(wxString::Format(_("Deleted %.2f seconds at t=%.2f"),
|
||||
seconds,
|
||||
mViewInfo.selectedRegion.t0()),
|
||||
_("Delete"));
|
||||
|
||||
RedrawProject();
|
||||
}
|
||||
|
||||
// Utility function called by other zoom methods
|
||||
void AudacityProject::Zoom(double level)
|
||||
{
|
||||
@ -5009,155 +4989,6 @@ void AudacityProject::OnTimer(wxTimerEvent& WXUNUSED(event))
|
||||
RestartTimer();
|
||||
}
|
||||
|
||||
//get regions selected by selected labels
|
||||
//removes unnecessary regions, overlapping regions are merged
|
||||
//regions memory need to be deleted by the caller
|
||||
void AudacityProject::GetRegionsByLabel( Regions ®ions )
|
||||
{
|
||||
//determine labeled regions
|
||||
for (auto lt : GetTracks()->Selected< LabelTrack >()) {
|
||||
for (int i = 0; i < lt->GetNumLabels(); i++)
|
||||
{
|
||||
const LabelStruct *ls = lt->GetLabel(i);
|
||||
if (ls->selectedRegion.t0() >= mViewInfo.selectedRegion.t0() &&
|
||||
ls->selectedRegion.t1() <= mViewInfo.selectedRegion.t1())
|
||||
regions.push_back(Region(ls->getT0(), ls->getT1()));
|
||||
}
|
||||
}
|
||||
|
||||
//anything to do ?
|
||||
if( regions.size() == 0 )
|
||||
return;
|
||||
|
||||
//sort and remove unnecessary regions
|
||||
std::sort(regions.begin(), regions.end());
|
||||
unsigned int selected = 1;
|
||||
while( selected < regions.size() )
|
||||
{
|
||||
const Region &cur = regions.at( selected );
|
||||
Region &last = regions.at( selected - 1 );
|
||||
if( cur.start < last.end )
|
||||
{
|
||||
if( cur.end > last.end )
|
||||
last.end = cur.end;
|
||||
regions.erase( regions.begin() + selected );
|
||||
}
|
||||
else
|
||||
selected++;
|
||||
}
|
||||
}
|
||||
|
||||
//Executes the edit function on all selected wave tracks with
|
||||
//regions specified by selected labels
|
||||
//If No tracks selected, function is applied on all tracks
|
||||
//If the function replaces the selection with audio of a different length,
|
||||
// bSyncLockedTracks should be set true to perform the same action on sync-lock selected
|
||||
// tracks.
|
||||
void AudacityProject::EditByLabel( EditFunction action,
|
||||
bool bSyncLockedTracks )
|
||||
{
|
||||
Regions regions;
|
||||
|
||||
GetRegionsByLabel( regions );
|
||||
if( regions.size() == 0 )
|
||||
return;
|
||||
|
||||
// if at least one wave track is selected
|
||||
// apply only on the selected track
|
||||
const bool allTracks = (GetTracks()->Selected< WaveTrack >()).empty();
|
||||
|
||||
//Apply action on wavetracks starting from
|
||||
//labeled regions in the end. This is to correctly perform
|
||||
//actions like 'Delete' which collapse the track area.
|
||||
for (auto wt : GetTracks()->Any<WaveTrack>())
|
||||
{
|
||||
if (allTracks || wt->GetSelected() || (bSyncLockedTracks && wt->IsSyncLockSelected()))
|
||||
{
|
||||
for (int i = (int)regions.size() - 1; i >= 0; i--) {
|
||||
const Region ®ion = regions.at(i);
|
||||
(wt->*action)(region.start, region.end);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Executes the edit function on all selected wave tracks with
|
||||
//regions specified by selected labels
|
||||
//If No tracks selected, function is applied on all tracks
|
||||
//Functions copy the edited regions to clipboard, possibly in multiple tracks
|
||||
//This probably should not be called if *action() changes the timeline, because
|
||||
// the copy needs to happen by track, and the timeline change by group.
|
||||
void AudacityProject::EditClipboardByLabel( EditDestFunction action )
|
||||
{
|
||||
Regions regions;
|
||||
|
||||
GetRegionsByLabel( regions );
|
||||
if( regions.size() == 0 )
|
||||
return;
|
||||
|
||||
// if at least one wave track is selected
|
||||
// apply only on the selected track
|
||||
const bool allTracks = (GetTracks()->Selected< WaveTrack >()).empty();
|
||||
|
||||
ClearClipboard();
|
||||
|
||||
auto pNewClipboard = TrackList::Create();
|
||||
auto &newClipboard = *pNewClipboard;
|
||||
|
||||
//Apply action on wavetracks starting from
|
||||
//labeled regions in the end. This is to correctly perform
|
||||
//actions like 'Cut' which collapse the track area.
|
||||
|
||||
for(auto wt :
|
||||
GetTracks()->Any<WaveTrack>()
|
||||
+ (allTracks ? &Track::Any : &Track::IsSelected)
|
||||
) {
|
||||
// This track accumulates the needed clips, right to left:
|
||||
Track::Holder merged;
|
||||
for( int i = (int)regions.size() - 1; i >= 0; i-- )
|
||||
{
|
||||
const Region ®ion = regions.at(i);
|
||||
auto dest = ( wt->*action )( region.start, region.end );
|
||||
if( dest )
|
||||
{
|
||||
Track::FinishCopy( wt, dest.get() );
|
||||
if( !merged )
|
||||
merged = std::move(dest);
|
||||
else
|
||||
{
|
||||
// Paste to the beginning; unless this is the first region,
|
||||
// offset the track to account for time between the regions
|
||||
if (i < (int)regions.size() - 1)
|
||||
merged->Offset(
|
||||
regions.at(i + 1).start - region.end);
|
||||
|
||||
// dest may have a placeholder clip at the end that is
|
||||
// removed when pasting, which is okay because we proceed
|
||||
// right to left. Any placeholder already in merged is kept.
|
||||
// Only the rightmost placeholder is important in the final
|
||||
// result.
|
||||
merged->Paste( 0.0 , dest.get() );
|
||||
}
|
||||
}
|
||||
else // nothing copied but there is a 'region', so the 'region' must be a 'point label' so offset
|
||||
if (i < (int)regions.size() - 1)
|
||||
if (merged)
|
||||
merged->Offset(
|
||||
regions.at(i + 1).start - region.end);
|
||||
}
|
||||
if( merged )
|
||||
newClipboard.Add( std::move(merged) );
|
||||
}
|
||||
|
||||
// Survived possibility of exceptions. Commit changes to the clipboard now.
|
||||
newClipboard.Swap(*msClipboard);
|
||||
wxTheApp->AddPendingEvent( wxCommandEvent{ EVT_CLIPBOARD_CHANGE } );
|
||||
|
||||
msClipT0 = regions.front().start;
|
||||
msClipT1 = regions.back().end;
|
||||
}
|
||||
|
||||
|
||||
// TrackPanel callback method
|
||||
void AudacityProject::TP_DisplayStatusMessage(const wxString &msg)
|
||||
{
|
||||
@ -5481,176 +5312,6 @@ void AudacityProject::SetSyncLock(bool flag)
|
||||
}
|
||||
}
|
||||
|
||||
void AudacityProject::DoTrackMute(Track *t, bool exclusive)
|
||||
{
|
||||
HandleTrackMute(t, exclusive);
|
||||
|
||||
mTrackPanel->UpdateAccessibility();
|
||||
mTrackPanel->Refresh(false);
|
||||
}
|
||||
|
||||
void AudacityProject::DoTrackSolo(Track *t, bool exclusive)
|
||||
{
|
||||
HandleTrackSolo(t, exclusive);
|
||||
|
||||
mTrackPanel->UpdateAccessibility();
|
||||
mTrackPanel->Refresh(false);
|
||||
}
|
||||
|
||||
void AudacityProject::SetTrackGain(WaveTrack * wt, LWSlider * slider)
|
||||
{
|
||||
wxASSERT(wt);
|
||||
float newValue = slider->Get();
|
||||
|
||||
for (auto channel : TrackList::Channels(wt))
|
||||
channel->SetGain(newValue);
|
||||
|
||||
PushState(_("Adjusted gain"), _("Gain"), UndoPush::CONSOLIDATE);
|
||||
|
||||
GetTrackPanel()->RefreshTrack(wt);
|
||||
}
|
||||
|
||||
void AudacityProject::SetTrackPan(WaveTrack * wt, LWSlider * slider)
|
||||
{
|
||||
wxASSERT(wt);
|
||||
float newValue = slider->Get();
|
||||
|
||||
for (auto channel : TrackList::Channels(wt))
|
||||
channel->SetPan(newValue);
|
||||
|
||||
PushState(_("Adjusted Pan"), _("Pan"), UndoPush::CONSOLIDATE);
|
||||
|
||||
GetTrackPanel()->RefreshTrack(wt);
|
||||
}
|
||||
|
||||
/// Removes the specified track. Called from HandleClosing.
|
||||
void AudacityProject::RemoveTrack(Track * toRemove)
|
||||
{
|
||||
// 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 = mTrackPanel->GetFocusedTrack() == toRemove;
|
||||
Track* newFocus{};
|
||||
if (toRemoveWasFocused) {
|
||||
auto iterNext = mTracks->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())
|
||||
mTracks->Remove( * iter++ );
|
||||
|
||||
if (toRemoveWasFocused)
|
||||
mTrackPanel->SetFocusedTrack(newFocus);
|
||||
|
||||
PushState(
|
||||
wxString::Format(_("Removed track '%s.'"),
|
||||
name),
|
||||
_("Track Remove"));
|
||||
|
||||
TP_RedrawScrollbars();
|
||||
HandleResize();
|
||||
GetTrackPanel()->Refresh(false);
|
||||
}
|
||||
|
||||
void AudacityProject::HandleTrackMute(Track *t, const bool exclusive)
|
||||
{
|
||||
// Whatever t is, replace with lead channel
|
||||
t = *GetTracks()->FindLeader(t);
|
||||
|
||||
// "exclusive" mute means mute the chosen track and unmute all others.
|
||||
if (exclusive) {
|
||||
for (auto leader : GetTracks()->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 (IsSoloSimple() || 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 = GetTracks()->Leaders<PlayableTrack>();
|
||||
auto nPlayableTracks = range.size();
|
||||
auto nPlaying = (range - &PlayableTrack::GetMute).size();
|
||||
|
||||
for (auto track : GetTracks()->Any<PlayableTrack>())
|
||||
// will set both of a stereo pair
|
||||
track->SetSolo( (nPlaying==1) && (nPlayableTracks > 1 ) && !track->GetMute() );
|
||||
}
|
||||
}
|
||||
ModifyState(true);
|
||||
}
|
||||
|
||||
// Type of solo (standard or simple) follows the set preference, unless
|
||||
// alternate == true, which causes the opposite behavior.
|
||||
void AudacityProject::HandleTrackSolo(Track *t, const bool alternate)
|
||||
{
|
||||
// Whatever t is, replace with lead channel
|
||||
t = *GetTracks()->FindLeader(t);
|
||||
|
||||
const auto pt = dynamic_cast<PlayableTrack *>( t );
|
||||
if (!pt)
|
||||
return;
|
||||
bool bWasSolo = pt->GetSolo();
|
||||
|
||||
bool bSoloMultiple = !IsSoloSimple() ^ alternate;
|
||||
|
||||
// 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 : GetTracks()->Leaders<PlayableTrack>()) {
|
||||
const auto group = TrackList::Channels(leader);
|
||||
bool chosen = (t == leader);
|
||||
for (auto channel : group) {
|
||||
if (chosen) {
|
||||
channel->SetSolo( !bWasSolo );
|
||||
if( IsSoloSimple() )
|
||||
channel->SetMute( false );
|
||||
}
|
||||
else {
|
||||
channel->SetSolo( false );
|
||||
if( IsSoloSimple() )
|
||||
channel->SetMute( !bWasSolo );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ModifyState(true);
|
||||
}
|
||||
|
||||
// Keyboard capture
|
||||
|
||||
// static
|
||||
@ -5848,107 +5509,6 @@ int AudacityProject::GetEstimatedRecordingMinsLeftOnDisk(long lCaptureChannels)
|
||||
}
|
||||
|
||||
|
||||
double AudacityProject::GetZoomOfToFit(){
|
||||
const double end = mTracks->GetEndTime();
|
||||
const double start = mViewInfo.bScrollBeyondZero
|
||||
? std::min(mTracks->GetStartTime(), 0.0)
|
||||
: 0;
|
||||
const double len = end - start;
|
||||
|
||||
if (len <= 0.0)
|
||||
return mViewInfo.GetZoom();
|
||||
|
||||
int w;
|
||||
mTrackPanel->GetTracksUsableArea(&w, NULL);
|
||||
w -= 10;
|
||||
return w/len;
|
||||
}
|
||||
|
||||
double AudacityProject::GetZoomOfSelection(){
|
||||
const double lowerBound =
|
||||
std::max(mViewInfo.selectedRegion.t0(), ScrollingLowerBoundTime());
|
||||
const double denom =
|
||||
mViewInfo.selectedRegion.t1() - lowerBound;
|
||||
if (denom <= 0.0)
|
||||
return mViewInfo.GetZoom();
|
||||
|
||||
// LL: The "-1" is just a hack to get around an issue where zooming to
|
||||
// selection doesn't actually get the entire selected region within the
|
||||
// visible area. This causes a problem with scrolling at end of playback
|
||||
// where the selected region may be scrolled off the left of the screen.
|
||||
// I know this isn't right, but until the real rounding or 1-off issue is
|
||||
// found, this will have to work.
|
||||
// PRL: Did I fix this? I am not sure, so I leave the hack in place.
|
||||
// Fixes might have resulted from commits
|
||||
// 1b8f44d0537d987c59653b11ed75a842b48896ea and
|
||||
// e7c7bb84a966c3b3cc4b3a9717d5f247f25e7296
|
||||
int width;
|
||||
mTrackPanel->GetTracksUsableArea(&width, NULL);
|
||||
return (width - 1) / denom;
|
||||
}
|
||||
|
||||
double AudacityProject::GetZoomOfPreset( int preset ){
|
||||
|
||||
// Sets a limit on how far we will zoom out as a factor over zoom to fit.
|
||||
const double maxZoomOutFactor = 4.0;
|
||||
// Sets how many pixels we allow for one uint, such as seconds.
|
||||
const double pixelsPerUnit = 5.0;
|
||||
|
||||
double result = 1.0;
|
||||
double zoomToFit = GetZoomOfToFit();
|
||||
switch( preset ){
|
||||
default:
|
||||
case WaveTrack::kZoomDefault:
|
||||
result = ZoomInfo::GetDefaultZoom();
|
||||
break;
|
||||
case WaveTrack::kZoomToFit:
|
||||
result = zoomToFit;
|
||||
break;
|
||||
case WaveTrack::kZoomToSelection:
|
||||
result = GetZoomOfSelection();
|
||||
break;
|
||||
case WaveTrack::kZoomMinutes:
|
||||
result = pixelsPerUnit * 1.0/60;
|
||||
break;
|
||||
case WaveTrack::kZoomSeconds:
|
||||
result = pixelsPerUnit * 1.0;
|
||||
break;
|
||||
case WaveTrack::kZoom5ths:
|
||||
result = pixelsPerUnit * 5.0;
|
||||
break;
|
||||
case WaveTrack::kZoom10ths:
|
||||
result = pixelsPerUnit * 10.0;
|
||||
break;
|
||||
case WaveTrack::kZoom20ths:
|
||||
result = pixelsPerUnit * 20.0;
|
||||
break;
|
||||
case WaveTrack::kZoom50ths:
|
||||
result = pixelsPerUnit * 50.0;
|
||||
break;
|
||||
case WaveTrack::kZoom100ths:
|
||||
result = pixelsPerUnit * 100.0;
|
||||
break;
|
||||
case WaveTrack::kZoom500ths:
|
||||
result = pixelsPerUnit * 500.0;
|
||||
break;
|
||||
case WaveTrack::kZoomMilliSeconds:
|
||||
result = pixelsPerUnit * 1000.0;
|
||||
break;
|
||||
case WaveTrack::kZoomSamples:
|
||||
result = 44100.0;
|
||||
break;
|
||||
case WaveTrack::kZoom4To1:
|
||||
result = 44100.0 * 4;
|
||||
break;
|
||||
case WaveTrack::kMaxZoom:
|
||||
result = ZoomInfo::GetMaxZoom();
|
||||
break;
|
||||
};
|
||||
if( result < (zoomToFit/maxZoomOutFactor) )
|
||||
result = zoomToFit / maxZoomOutFactor;
|
||||
return result;
|
||||
}
|
||||
|
||||
AudacityProject::PlaybackScroller::PlaybackScroller(AudacityProject *project)
|
||||
: mProject(project)
|
||||
{
|
||||
|
@ -93,8 +93,6 @@ class MixerBoardFrame;
|
||||
struct AudioIOStartStreamOptions;
|
||||
struct UndoState;
|
||||
|
||||
class Regions;
|
||||
|
||||
class LWSlider;
|
||||
class UndoManager;
|
||||
enum class UndoPush : unsigned char;
|
||||
@ -318,8 +316,6 @@ private:
|
||||
bool DoSave(bool fromSaveAs, bool bWantSaveCopy, bool bLossless = false);
|
||||
public:
|
||||
|
||||
void Clear();// clears a selection
|
||||
|
||||
const wxString &GetFileName() { return mFileName; }
|
||||
bool GetDirty() { return mDirty; }
|
||||
void SetProjectTitle( int number =-1);
|
||||
@ -426,29 +422,9 @@ public:
|
||||
void SkipEnd(bool shift);
|
||||
|
||||
|
||||
typedef void (WaveTrack::* EditFunction)(double, double);
|
||||
typedef std::unique_ptr<Track> (WaveTrack::* EditDestFunction)(double, double);
|
||||
|
||||
void EditByLabel(EditFunction action, bool bSyncLockedTracks);
|
||||
void EditClipboardByLabel(EditDestFunction action );
|
||||
|
||||
bool IsSyncLocked();
|
||||
void SetSyncLock(bool flag);
|
||||
|
||||
void DoTrackMute(Track *pTrack, bool exclusive);
|
||||
void DoTrackSolo(Track *pTrack, bool exclusive);
|
||||
void SetTrackGain(WaveTrack * track, LWSlider * slider);
|
||||
void SetTrackPan(WaveTrack * track, LWSlider * slider);
|
||||
|
||||
void RemoveTrack(Track * toRemove);
|
||||
|
||||
// "exclusive" mute means mute the chosen track and unmute all others.
|
||||
void HandleTrackMute(Track *t, const bool exclusive);
|
||||
|
||||
// Type of solo (standard or simple) follows the set preference, unless
|
||||
// alternate == true, which causes the opposite behavior.
|
||||
void HandleTrackSolo(Track *t, const bool alternate);
|
||||
|
||||
// Snap To
|
||||
|
||||
void SetSnapTo(int snap);
|
||||
@ -603,17 +579,10 @@ public:
|
||||
|
||||
void PopState(const UndoState &state);
|
||||
|
||||
void GetRegionsByLabel( Regions ®ions );
|
||||
|
||||
void AutoSave();
|
||||
void DeleteCurrentAutoSaveFile();
|
||||
|
||||
public:
|
||||
double GetZoomOfToFit();
|
||||
double GetZoomOfSelection();
|
||||
|
||||
double GetZoomOfPreset(int preset );
|
||||
|
||||
bool IsSoloSimple() const { return mSoloPref == wxT("Simple"); }
|
||||
bool IsSoloNone() const { return mSoloPref == wxT("None"); }
|
||||
|
||||
|
@ -87,9 +87,9 @@ bool ZoomInfo::ZoomOutAvailable() const
|
||||
return zoom > gMinZoom;
|
||||
}
|
||||
|
||||
double ZoomInfo::GetZoom( ){ return zoom;};
|
||||
double ZoomInfo::GetMaxZoom( ){ return gMaxZoom;};
|
||||
double ZoomInfo::GetMinZoom( ){ return gMinZoom;};
|
||||
double ZoomInfo::GetZoom( ) const { return zoom;};
|
||||
double ZoomInfo::GetMaxZoom( ) { return gMaxZoom;};
|
||||
double ZoomInfo::GetMinZoom( ) { return gMinZoom;};
|
||||
|
||||
void ZoomInfo::SetZoom(double pixelsPerSecond)
|
||||
{
|
||||
|
@ -85,12 +85,14 @@ public:
|
||||
static double GetDefaultZoom()
|
||||
{ return 44100.0 / 512.0; }
|
||||
|
||||
// There is NO GetZoom()!
|
||||
// Use TimeToPosition and PositionToTime and OffsetTimeByPixels!
|
||||
|
||||
// Limits zoom to certain bounds
|
||||
void SetZoom(double pixelsPerSecond);
|
||||
double GetZoom();
|
||||
|
||||
// This function should not be used to convert positions to times and back
|
||||
// Use TimeToPosition and PositionToTime and OffsetTimeByPixels instead
|
||||
double GetZoom() const;
|
||||
|
||||
static double GetMaxZoom( );
|
||||
static double GetMinZoom( );
|
||||
|
||||
|
@ -53,7 +53,7 @@ struct Region
|
||||
}
|
||||
};
|
||||
|
||||
class Regions : public std::vector < Region > {};
|
||||
using Regions = std::vector < Region >;
|
||||
|
||||
class Envelope;
|
||||
|
||||
|
@ -364,9 +364,28 @@ void OnCut(const CommandContext &context)
|
||||
void OnDelete(const CommandContext &context)
|
||||
{
|
||||
auto &project = context.project;
|
||||
project.Clear();
|
||||
auto &tracks = *project.GetTracks();
|
||||
auto &selectedRegion = project.GetViewInfo().selectedRegion;
|
||||
|
||||
for (auto n : tracks.Any()) {
|
||||
if (n->GetSelected() || n->IsSyncLockSelected()) {
|
||||
n->Clear(selectedRegion.t0(), selectedRegion.t1());
|
||||
}
|
||||
}
|
||||
|
||||
double seconds = selectedRegion.duration();
|
||||
|
||||
selectedRegion.collapseToT0();
|
||||
|
||||
project.PushState(wxString::Format(_("Deleted %.2f seconds at t=%.2f"),
|
||||
seconds,
|
||||
selectedRegion.t0()),
|
||||
_("Delete"));
|
||||
|
||||
project.RedrawProject();
|
||||
}
|
||||
|
||||
|
||||
void OnCopy(const CommandContext &context)
|
||||
{
|
||||
auto &project = context.project;
|
||||
|
@ -1,3 +1,4 @@
|
||||
#include "../AudacityApp.h" // for EVT_CLIPBOARD_CHANGE
|
||||
#include "../AudioIO.h"
|
||||
#include "../LabelTrack.h"
|
||||
#include "../Menus.h"
|
||||
@ -80,6 +81,165 @@ int DoAddLabel(
|
||||
return index;
|
||||
}
|
||||
|
||||
//get regions selected by selected labels
|
||||
//removes unnecessary regions, overlapping regions are merged
|
||||
void GetRegionsByLabel(
|
||||
const TrackList &tracks, const SelectedRegion &selectedRegion,
|
||||
Regions ®ions )
|
||||
{
|
||||
//determine labeled regions
|
||||
for (auto lt : tracks.Selected< const LabelTrack >()) {
|
||||
for (int i = 0; i < lt->GetNumLabels(); i++)
|
||||
{
|
||||
const LabelStruct *ls = lt->GetLabel(i);
|
||||
if (ls->selectedRegion.t0() >= selectedRegion.t0() &&
|
||||
ls->selectedRegion.t1() <= selectedRegion.t1())
|
||||
regions.push_back(Region(ls->getT0(), ls->getT1()));
|
||||
}
|
||||
}
|
||||
|
||||
//anything to do ?
|
||||
if( regions.size() == 0 )
|
||||
return;
|
||||
|
||||
//sort and remove unnecessary regions
|
||||
std::sort(regions.begin(), regions.end());
|
||||
unsigned int selected = 1;
|
||||
while( selected < regions.size() )
|
||||
{
|
||||
const Region &cur = regions.at( selected );
|
||||
Region &last = regions.at( selected - 1 );
|
||||
if( cur.start < last.end )
|
||||
{
|
||||
if( cur.end > last.end )
|
||||
last.end = cur.end;
|
||||
regions.erase( regions.begin() + selected );
|
||||
}
|
||||
else
|
||||
++selected;
|
||||
}
|
||||
}
|
||||
|
||||
using EditFunction = void (WaveTrack::*)(double, double);
|
||||
|
||||
//Executes the edit function on all selected wave tracks with
|
||||
//regions specified by selected labels
|
||||
//If No tracks selected, function is applied on all tracks
|
||||
//If the function replaces the selection with audio of a different length,
|
||||
// bSyncLockedTracks should be set true to perform the same action on sync-lock
|
||||
// selected tracks.
|
||||
void EditByLabel(
|
||||
TrackList &tracks, const SelectedRegion &selectedRegion,
|
||||
EditFunction action, bool bSyncLockedTracks )
|
||||
{
|
||||
Regions regions;
|
||||
|
||||
GetRegionsByLabel( tracks, selectedRegion, regions );
|
||||
if( regions.size() == 0 )
|
||||
return;
|
||||
|
||||
// if at least one wave track is selected
|
||||
// apply only on the selected track
|
||||
const bool allTracks = (tracks.Selected< WaveTrack >()).empty();
|
||||
|
||||
//Apply action on wavetracks starting from
|
||||
//labeled regions in the end. This is to correctly perform
|
||||
//actions like 'Delete' which collapse the track area.
|
||||
for (auto wt : tracks.Any<WaveTrack>())
|
||||
{
|
||||
if (allTracks || wt->GetSelected() ||
|
||||
(bSyncLockedTracks && wt->IsSyncLockSelected()))
|
||||
{
|
||||
for (int i = (int)regions.size() - 1; i >= 0; i--) {
|
||||
const Region ®ion = regions.at(i);
|
||||
(wt->*action)(region.start, region.end);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
using EditDestFunction = std::unique_ptr<Track> (WaveTrack::*)(double, double);
|
||||
|
||||
//Executes the edit function on all selected wave tracks with
|
||||
//regions specified by selected labels
|
||||
//If No tracks selected, function is applied on all tracks
|
||||
//Functions copy the edited regions to clipboard, possibly in multiple tracks
|
||||
//This probably should not be called if *action() changes the timeline, because
|
||||
// the copy needs to happen by track, and the timeline change by group.
|
||||
void EditClipboardByLabel(
|
||||
TrackList &tracks, const SelectedRegion &selectedRegion,
|
||||
EditDestFunction action )
|
||||
{
|
||||
Regions regions;
|
||||
|
||||
GetRegionsByLabel( tracks, selectedRegion, regions );
|
||||
if( regions.size() == 0 )
|
||||
return;
|
||||
|
||||
// if at least one wave track is selected
|
||||
// apply only on the selected track
|
||||
const bool allTracks = (tracks.Selected< WaveTrack >()).empty();
|
||||
|
||||
AudacityProject::ClearClipboard();
|
||||
|
||||
auto pNewClipboard = TrackList::Create();
|
||||
auto &newClipboard = *pNewClipboard;
|
||||
|
||||
//Apply action on wavetracks starting from
|
||||
//labeled regions in the end. This is to correctly perform
|
||||
//actions like 'Cut' which collapse the track area.
|
||||
|
||||
for(auto wt :
|
||||
tracks.Any<WaveTrack>()
|
||||
+ (allTracks ? &Track::Any : &Track::IsSelected)
|
||||
) {
|
||||
// This track accumulates the needed clips, right to left:
|
||||
Track::Holder merged;
|
||||
for( int i = (int)regions.size() - 1; i >= 0; i-- )
|
||||
{
|
||||
const Region ®ion = regions.at(i);
|
||||
auto dest = ( wt->*action )( region.start, region.end );
|
||||
if( dest )
|
||||
{
|
||||
Track::FinishCopy( wt, dest.get() );
|
||||
if( !merged )
|
||||
merged = std::move(dest);
|
||||
else
|
||||
{
|
||||
// Paste to the beginning; unless this is the first region,
|
||||
// offset the track to account for time between the regions
|
||||
if (i < (int)regions.size() - 1)
|
||||
merged->Offset(
|
||||
regions.at(i + 1).start - region.end);
|
||||
|
||||
// dest may have a placeholder clip at the end that is
|
||||
// removed when pasting, which is okay because we proceed
|
||||
// right to left. Any placeholder already in merged is kept.
|
||||
// Only the rightmost placeholder is important in the final
|
||||
// result.
|
||||
merged->Paste( 0.0 , dest.get() );
|
||||
}
|
||||
}
|
||||
else
|
||||
// nothing copied but there is a 'region', so the 'region' must
|
||||
// be a 'point label' so offset
|
||||
if (i < (int)regions.size() - 1)
|
||||
if (merged)
|
||||
merged->Offset(
|
||||
regions.at(i + 1).start - region.end);
|
||||
}
|
||||
if( merged )
|
||||
newClipboard.Add( std::move(merged) );
|
||||
}
|
||||
|
||||
// Survived possibility of exceptions. Commit changes to the clipboard now.
|
||||
newClipboard.Swap(*AudacityProject::msClipboard);
|
||||
wxTheApp->AddPendingEvent( wxCommandEvent{ EVT_CLIPBOARD_CHANGE } );
|
||||
|
||||
AudacityProject::msClipT0 = regions.front().start;
|
||||
AudacityProject::msClipT1 = regions.back().end;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace LabelEditActions {
|
||||
@ -196,6 +356,7 @@ void OnToggleTypeToCreateLabel(const CommandContext &WXUNUSED(context) )
|
||||
void OnCutLabels(const CommandContext &context)
|
||||
{
|
||||
auto &project = context.project;
|
||||
auto &tracks = *project.GetTracks();
|
||||
auto &selectedRegion = project.GetViewInfo().selectedRegion;
|
||||
|
||||
if( selectedRegion.isPoint() )
|
||||
@ -203,12 +364,13 @@ void OnCutLabels(const CommandContext &context)
|
||||
|
||||
// Because of grouping the copy may need to operate on different tracks than
|
||||
// the clear, so we do these actions separately.
|
||||
project.EditClipboardByLabel( &WaveTrack::CopyNonconst );
|
||||
EditClipboardByLabel( tracks, selectedRegion, &WaveTrack::CopyNonconst );
|
||||
|
||||
if( gPrefs->Read( wxT( "/GUI/EnableCutLines" ), ( long )0 ) )
|
||||
project.EditByLabel( &WaveTrack::ClearAndAddCutLine, true );
|
||||
EditByLabel(
|
||||
tracks, selectedRegion, &WaveTrack::ClearAndAddCutLine, true );
|
||||
else
|
||||
project.EditByLabel( &WaveTrack::Clear, true );
|
||||
EditByLabel( tracks, selectedRegion, &WaveTrack::Clear, true );
|
||||
|
||||
AudacityProject::msClipProject = &project;
|
||||
|
||||
@ -227,12 +389,13 @@ void OnCutLabels(const CommandContext &context)
|
||||
void OnDeleteLabels(const CommandContext &context)
|
||||
{
|
||||
auto &project = context.project;
|
||||
auto &tracks = *project.GetTracks();
|
||||
auto &selectedRegion = project.GetViewInfo().selectedRegion;
|
||||
|
||||
if( selectedRegion.isPoint() )
|
||||
return;
|
||||
|
||||
project.EditByLabel( &WaveTrack::Clear, true );
|
||||
EditByLabel( tracks, selectedRegion, &WaveTrack::Clear, true );
|
||||
|
||||
selectedRegion.collapseToT0();
|
||||
|
||||
@ -248,12 +411,13 @@ void OnDeleteLabels(const CommandContext &context)
|
||||
void OnSplitCutLabels(const CommandContext &context)
|
||||
{
|
||||
auto &project = context.project;
|
||||
auto &tracks = *project.GetTracks();
|
||||
auto &selectedRegion = project.GetViewInfo().selectedRegion;
|
||||
|
||||
if( selectedRegion.isPoint() )
|
||||
return;
|
||||
|
||||
project.EditClipboardByLabel( &WaveTrack::SplitCut );
|
||||
EditClipboardByLabel( tracks, selectedRegion, &WaveTrack::SplitCut );
|
||||
|
||||
AudacityProject::msClipProject = &project;
|
||||
|
||||
@ -270,12 +434,13 @@ void OnSplitCutLabels(const CommandContext &context)
|
||||
void OnSplitDeleteLabels(const CommandContext &context)
|
||||
{
|
||||
auto &project = context.project;
|
||||
auto &tracks = *project.GetTracks();
|
||||
auto &selectedRegion = project.GetViewInfo().selectedRegion;
|
||||
|
||||
if( selectedRegion.isPoint() )
|
||||
return;
|
||||
|
||||
project.EditByLabel( &WaveTrack::SplitDelete, false );
|
||||
EditByLabel( tracks, selectedRegion, &WaveTrack::SplitDelete, false );
|
||||
|
||||
project.PushState(
|
||||
/* i18n-hint: (verb) Audacity has just done a special kind of DELETE on
|
||||
@ -292,12 +457,13 @@ void OnSilenceLabels(const CommandContext &context)
|
||||
{
|
||||
auto &project = context.project;
|
||||
auto trackPanel = project.GetTrackPanel();
|
||||
auto &tracks = *project.GetTracks();
|
||||
auto &selectedRegion = project.GetViewInfo().selectedRegion;
|
||||
|
||||
if( selectedRegion.isPoint() )
|
||||
return;
|
||||
|
||||
project.EditByLabel( &WaveTrack::Silence, false );
|
||||
EditByLabel( tracks, selectedRegion, &WaveTrack::Silence, false );
|
||||
|
||||
project.PushState(
|
||||
/* i18n-hint: (verb)*/
|
||||
@ -312,12 +478,13 @@ void OnCopyLabels(const CommandContext &context)
|
||||
{
|
||||
auto &project = context.project;
|
||||
auto trackPanel = project.GetTrackPanel();
|
||||
auto &tracks = *project.GetTracks();
|
||||
auto &selectedRegion = project.GetViewInfo().selectedRegion;
|
||||
|
||||
if( selectedRegion.isPoint() )
|
||||
return;
|
||||
|
||||
project.EditClipboardByLabel( &WaveTrack::CopyNonconst );
|
||||
EditClipboardByLabel( tracks, selectedRegion, &WaveTrack::CopyNonconst );
|
||||
|
||||
AudacityProject::msClipProject = &project;
|
||||
|
||||
@ -331,8 +498,10 @@ void OnCopyLabels(const CommandContext &context)
|
||||
void OnSplitLabels(const CommandContext &context)
|
||||
{
|
||||
auto &project = context.project;
|
||||
auto &tracks = *project.GetTracks();
|
||||
auto &selectedRegion = project.GetViewInfo().selectedRegion;
|
||||
|
||||
project.EditByLabel( &WaveTrack::Split, false );
|
||||
EditByLabel( tracks, selectedRegion, &WaveTrack::Split, false );
|
||||
|
||||
project.PushState(
|
||||
/* i18n-hint: (verb) past tense. Audacity has just split the labeled
|
||||
@ -347,12 +516,13 @@ void OnSplitLabels(const CommandContext &context)
|
||||
void OnJoinLabels(const CommandContext &context)
|
||||
{
|
||||
auto &project = context.project;
|
||||
auto &tracks = *project.GetTracks();
|
||||
auto &selectedRegion = project.GetViewInfo().selectedRegion;
|
||||
|
||||
if( selectedRegion.isPoint() )
|
||||
return;
|
||||
|
||||
project.EditByLabel( &WaveTrack::Join, false );
|
||||
EditByLabel( tracks, selectedRegion, &WaveTrack::Join, false );
|
||||
|
||||
project.PushState(
|
||||
/* i18n-hint: (verb) Audacity has just joined the labeled audio (points or
|
||||
@ -367,12 +537,13 @@ void OnJoinLabels(const CommandContext &context)
|
||||
void OnDisjoinLabels(const CommandContext &context)
|
||||
{
|
||||
auto &project = context.project;
|
||||
auto &tracks = *project.GetTracks();
|
||||
auto &selectedRegion = project.GetViewInfo().selectedRegion;
|
||||
|
||||
if( selectedRegion.isPoint() )
|
||||
return;
|
||||
|
||||
project.EditByLabel( &WaveTrack::Disjoin, false );
|
||||
EditByLabel( tracks, selectedRegion, &WaveTrack::Disjoin, false );
|
||||
|
||||
project.PushState(
|
||||
/* i18n-hint: (verb) Audacity has just detached the labeled audio regions.
|
||||
|
@ -503,6 +503,32 @@ void DoSortTracks( AudacityProject &project, int flags )
|
||||
pTracks->Permute(arr);
|
||||
}
|
||||
|
||||
void SetTrackGain(AudacityProject &project, WaveTrack * wt, LWSlider * slider)
|
||||
{
|
||||
wxASSERT(wt);
|
||||
float newValue = slider->Get();
|
||||
|
||||
for (auto channel : TrackList::Channels(wt))
|
||||
channel->SetGain(newValue);
|
||||
|
||||
project.PushState(_("Adjusted gain"), _("Gain"), UndoPush::CONSOLIDATE);
|
||||
|
||||
project.GetTrackPanel()->RefreshTrack(wt);
|
||||
}
|
||||
|
||||
void SetTrackPan(AudacityProject &project, WaveTrack * wt, LWSlider * slider)
|
||||
{
|
||||
wxASSERT(wt);
|
||||
float newValue = slider->Get();
|
||||
|
||||
for (auto channel : TrackList::Channels(wt))
|
||||
channel->SetPan(newValue);
|
||||
|
||||
project.PushState(_("Adjusted Pan"), _("Pan"), UndoPush::CONSOLIDATE);
|
||||
|
||||
project.GetTrackPanel()->RefreshTrack(wt);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace TrackActions {
|
||||
@ -549,6 +575,146 @@ void DoRemoveTracks( AudacityProject &project )
|
||||
trackPanel->Refresh(false);
|
||||
}
|
||||
|
||||
void DoTrackMute(AudacityProject &project, Track *t, bool exclusive)
|
||||
{
|
||||
auto &tracks = *project.GetTracks();
|
||||
auto &trackPanel = *project.GetTrackPanel();
|
||||
|
||||
// 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 (project.IsSoloSimple() || project.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() );
|
||||
}
|
||||
}
|
||||
project.ModifyState(true);
|
||||
|
||||
trackPanel.UpdateAccessibility();
|
||||
trackPanel.Refresh(false);
|
||||
}
|
||||
|
||||
void DoTrackSolo(AudacityProject &project, Track *t, bool exclusive)
|
||||
{
|
||||
auto &tracks = *project.GetTracks();
|
||||
auto &trackPanel = *project.GetTrackPanel();
|
||||
|
||||
// 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 = !project.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( project.IsSoloSimple() )
|
||||
channel->SetMute( false );
|
||||
}
|
||||
else {
|
||||
channel->SetSolo( false );
|
||||
if( project.IsSoloSimple() )
|
||||
channel->SetMute( !bWasSolo );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
project.ModifyState(true);
|
||||
|
||||
trackPanel.UpdateAccessibility();
|
||||
trackPanel.Refresh(false);
|
||||
}
|
||||
|
||||
void DoRemoveTrack(AudacityProject &project, Track * toRemove)
|
||||
{
|
||||
auto &tracks = *project.GetTracks();
|
||||
auto &trackPanel = *project.GetTrackPanel();
|
||||
|
||||
// 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 = trackPanel.GetFocusedTrack() == 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)
|
||||
trackPanel.SetFocusedTrack(newFocus);
|
||||
|
||||
project.PushState(
|
||||
wxString::Format(_("Removed track '%s.'"),
|
||||
name),
|
||||
_("Track Remove"));
|
||||
|
||||
project.FixScrollbars();
|
||||
project.HandleResize();
|
||||
trackPanel.Refresh(false);
|
||||
}
|
||||
|
||||
void DoMoveTrack
|
||||
(AudacityProject &project, Track* target, MoveChoice choice)
|
||||
{
|
||||
@ -1084,7 +1250,7 @@ void OnTrackPan(const CommandContext &context)
|
||||
if (track) track->TypeSwitch( [&](WaveTrack *wt) {
|
||||
LWSlider *slider = trackPanel->PanSlider(wt);
|
||||
if (slider->ShowDialog())
|
||||
project.SetTrackPan(wt, slider);
|
||||
SetTrackPan(project, wt, slider);
|
||||
});
|
||||
}
|
||||
|
||||
@ -1097,7 +1263,7 @@ void OnTrackPanLeft(const CommandContext &context)
|
||||
if (track) track->TypeSwitch( [&](WaveTrack *wt) {
|
||||
LWSlider *slider = trackPanel->PanSlider(wt);
|
||||
slider->Decrease(1);
|
||||
project.SetTrackPan(wt, slider);
|
||||
SetTrackPan(project, wt, slider);
|
||||
});
|
||||
}
|
||||
|
||||
@ -1110,7 +1276,7 @@ void OnTrackPanRight(const CommandContext &context)
|
||||
if (track) track->TypeSwitch( [&](WaveTrack *wt) {
|
||||
LWSlider *slider = trackPanel->PanSlider(wt);
|
||||
slider->Increase(1);
|
||||
project.SetTrackPan(wt, slider);
|
||||
SetTrackPan(project, wt, slider);
|
||||
});
|
||||
}
|
||||
|
||||
@ -1124,7 +1290,7 @@ void OnTrackGain(const CommandContext &context)
|
||||
if (track) track->TypeSwitch( [&](WaveTrack *wt) {
|
||||
LWSlider *slider = trackPanel->GainSlider(wt);
|
||||
if (slider->ShowDialog())
|
||||
project.SetTrackGain(wt, slider);
|
||||
SetTrackGain(project, wt, slider);
|
||||
});
|
||||
}
|
||||
|
||||
@ -1137,7 +1303,7 @@ void OnTrackGainInc(const CommandContext &context)
|
||||
if (track) track->TypeSwitch( [&](WaveTrack *wt) {
|
||||
LWSlider *slider = trackPanel->GainSlider(wt);
|
||||
slider->Increase(1);
|
||||
project.SetTrackGain(wt, slider);
|
||||
SetTrackGain(project, wt, slider);
|
||||
});
|
||||
}
|
||||
|
||||
@ -1150,7 +1316,7 @@ void OnTrackGainDec(const CommandContext &context)
|
||||
if (track) track->TypeSwitch( [&](WaveTrack *wt) {
|
||||
LWSlider *slider = trackPanel->GainSlider(wt);
|
||||
slider->Decrease(1);
|
||||
project.SetTrackGain(wt, slider);
|
||||
SetTrackGain(project, wt, slider);
|
||||
});
|
||||
}
|
||||
|
||||
@ -1169,7 +1335,7 @@ void OnTrackMute(const CommandContext &context)
|
||||
|
||||
const auto track = trackPanel->GetFocusedTrack();
|
||||
if (track) track->TypeSwitch( [&](PlayableTrack *t) {
|
||||
project.DoTrackMute(t, false);
|
||||
DoTrackMute(project, t, false);
|
||||
});
|
||||
}
|
||||
|
||||
@ -1180,7 +1346,7 @@ void OnTrackSolo(const CommandContext &context)
|
||||
|
||||
const auto track = trackPanel->GetFocusedTrack();
|
||||
if (track) track->TypeSwitch( [&](PlayableTrack *t) {
|
||||
project.DoTrackSolo(t, false);
|
||||
DoTrackSolo(project, t, false);
|
||||
});
|
||||
}
|
||||
|
||||
@ -1203,7 +1369,7 @@ void OnTrackClose(const CommandContext &context)
|
||||
return;
|
||||
}
|
||||
|
||||
project.RemoveTrack(t);
|
||||
DoRemoveTrack(project, t);
|
||||
|
||||
trackPanel->UpdateViewIfNoTracks();
|
||||
trackPanel->Refresh(false);
|
||||
|
@ -14,12 +14,125 @@
|
||||
|
||||
// private helper classes and functions
|
||||
namespace {
|
||||
|
||||
double GetZoomOfSelection( const AudacityProject &project )
|
||||
{
|
||||
const auto &viewInfo = project.GetViewInfo();
|
||||
const auto &trackPanel = *project.GetTrackPanel();
|
||||
|
||||
const double lowerBound =
|
||||
std::max(viewInfo.selectedRegion.t0(),
|
||||
project.ScrollingLowerBoundTime());
|
||||
const double denom =
|
||||
viewInfo.selectedRegion.t1() - lowerBound;
|
||||
if (denom <= 0.0)
|
||||
return viewInfo.GetZoom();
|
||||
|
||||
// LL: The "-1" is just a hack to get around an issue where zooming to
|
||||
// selection doesn't actually get the entire selected region within the
|
||||
// visible area. This causes a problem with scrolling at end of playback
|
||||
// where the selected region may be scrolled off the left of the screen.
|
||||
// I know this isn't right, but until the real rounding or 1-off issue is
|
||||
// found, this will have to work.
|
||||
// PRL: Did I fix this? I am not sure, so I leave the hack in place.
|
||||
// Fixes might have resulted from commits
|
||||
// 1b8f44d0537d987c59653b11ed75a842b48896ea and
|
||||
// e7c7bb84a966c3b3cc4b3a9717d5f247f25e7296
|
||||
int width;
|
||||
trackPanel.GetTracksUsableArea(&width, NULL);
|
||||
return (width - 1) / denom;
|
||||
}
|
||||
|
||||
double GetZoomOfPreset( const AudacityProject &project, int preset )
|
||||
{
|
||||
|
||||
// Sets a limit on how far we will zoom out as a factor over zoom to fit.
|
||||
const double maxZoomOutFactor = 4.0;
|
||||
// Sets how many pixels we allow for one uint, such as seconds.
|
||||
const double pixelsPerUnit = 5.0;
|
||||
|
||||
double result = 1.0;
|
||||
double zoomToFit = ViewActions::GetZoomOfToFit( project );
|
||||
switch( preset ){
|
||||
default:
|
||||
case WaveTrack::kZoomDefault:
|
||||
result = ZoomInfo::GetDefaultZoom();
|
||||
break;
|
||||
case WaveTrack::kZoomToFit:
|
||||
result = zoomToFit;
|
||||
break;
|
||||
case WaveTrack::kZoomToSelection:
|
||||
result = GetZoomOfSelection( project );
|
||||
break;
|
||||
case WaveTrack::kZoomMinutes:
|
||||
result = pixelsPerUnit * 1.0/60;
|
||||
break;
|
||||
case WaveTrack::kZoomSeconds:
|
||||
result = pixelsPerUnit * 1.0;
|
||||
break;
|
||||
case WaveTrack::kZoom5ths:
|
||||
result = pixelsPerUnit * 5.0;
|
||||
break;
|
||||
case WaveTrack::kZoom10ths:
|
||||
result = pixelsPerUnit * 10.0;
|
||||
break;
|
||||
case WaveTrack::kZoom20ths:
|
||||
result = pixelsPerUnit * 20.0;
|
||||
break;
|
||||
case WaveTrack::kZoom50ths:
|
||||
result = pixelsPerUnit * 50.0;
|
||||
break;
|
||||
case WaveTrack::kZoom100ths:
|
||||
result = pixelsPerUnit * 100.0;
|
||||
break;
|
||||
case WaveTrack::kZoom500ths:
|
||||
result = pixelsPerUnit * 500.0;
|
||||
break;
|
||||
case WaveTrack::kZoomMilliSeconds:
|
||||
result = pixelsPerUnit * 1000.0;
|
||||
break;
|
||||
case WaveTrack::kZoomSamples:
|
||||
result = 44100.0;
|
||||
break;
|
||||
case WaveTrack::kZoom4To1:
|
||||
result = 44100.0 * 4;
|
||||
break;
|
||||
case WaveTrack::kMaxZoom:
|
||||
result = ZoomInfo::GetMaxZoom();
|
||||
break;
|
||||
};
|
||||
if( result < (zoomToFit/maxZoomOutFactor) )
|
||||
result = zoomToFit / maxZoomOutFactor;
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace ViewActions {
|
||||
|
||||
// exported helper functions
|
||||
|
||||
double GetZoomOfToFit( const AudacityProject &project )
|
||||
{
|
||||
const auto &tracks = *project.GetTracks();
|
||||
const auto &viewInfo = project.GetViewInfo();
|
||||
const auto &trackPanel = *project.GetTrackPanel();
|
||||
|
||||
const double end = tracks.GetEndTime();
|
||||
const double start = viewInfo.bScrollBeyondZero
|
||||
? std::min( tracks.GetStartTime(), 0.0)
|
||||
: 0;
|
||||
const double len = end - start;
|
||||
|
||||
if (len <= 0.0)
|
||||
return viewInfo.GetZoom();
|
||||
|
||||
int w;
|
||||
trackPanel.GetTracksUsableArea(&w, NULL);
|
||||
w -= 10;
|
||||
return w/len;
|
||||
}
|
||||
|
||||
void DoZoomFit(AudacityProject &project)
|
||||
{
|
||||
auto &viewInfo = project.GetViewInfo();
|
||||
@ -29,7 +142,7 @@ void DoZoomFit(AudacityProject &project)
|
||||
? std::min(tracks->GetStartTime(), 0.0)
|
||||
: 0;
|
||||
|
||||
project.Zoom( project.GetZoomOfToFit() );
|
||||
project.Zoom( GetZoomOfToFit( project ) );
|
||||
project.TP_ScrollWindow(start);
|
||||
}
|
||||
|
||||
@ -91,7 +204,7 @@ void OnZoomSel(const CommandContext &context)
|
||||
auto &project = context.project;
|
||||
auto &selectedRegion = project.GetViewInfo().selectedRegion;
|
||||
|
||||
project.Zoom( project.GetZoomOfSelection() );
|
||||
project.Zoom( GetZoomOfSelection( project ) );
|
||||
project.TP_ScrollWindow(selectedRegion.t0());
|
||||
}
|
||||
|
||||
@ -105,8 +218,8 @@ void OnZoomToggle(const CommandContext &context)
|
||||
// const double origWidth = GetScreenEndTime() - origLeft;
|
||||
|
||||
// Choose the zoom that is most different to the current zoom.
|
||||
double Zoom1 = project.GetZoomOfPreset( TracksPrefs::Zoom1Choice() );
|
||||
double Zoom2 = project.GetZoomOfPreset( TracksPrefs::Zoom2Choice() );
|
||||
double Zoom1 = GetZoomOfPreset( project, TracksPrefs::Zoom1Choice() );
|
||||
double Zoom2 = GetZoomOfPreset( project, TracksPrefs::Zoom2Choice() );
|
||||
double Z = viewInfo.GetZoom();// Current Zoom.
|
||||
double ChosenZoom =
|
||||
fabs(log(Zoom1 / Z)) > fabs(log( Z / Zoom2)) ? Zoom1:Zoom2;
|
||||
|
@ -13,6 +13,7 @@ Paul Licameli split from TrackPanel.cpp
|
||||
|
||||
#include "../../../commands/CommandManager.h"
|
||||
#include "../../../HitTestResult.h"
|
||||
#include "../../../Menus.h"
|
||||
#include "../../../Project.h"
|
||||
#include "../../../RefreshCode.h"
|
||||
#include "../../../Track.h"
|
||||
@ -33,7 +34,7 @@ UIHandle::Result MuteButtonHandle::CommitChanges
|
||||
{
|
||||
auto pTrack = mpTrack.lock();
|
||||
if ( dynamic_cast< PlayableTrack* >( pTrack.get() ) )
|
||||
pProject->DoTrackMute(pTrack.get(), event.ShiftDown());
|
||||
TrackActions::DoTrackMute(*pProject, pTrack.get(), event.ShiftDown());
|
||||
|
||||
return RefreshCode::RefreshNone;
|
||||
}
|
||||
@ -89,7 +90,7 @@ UIHandle::Result SoloButtonHandle::CommitChanges
|
||||
{
|
||||
auto pTrack = mpTrack.lock();
|
||||
if ( dynamic_cast< PlayableTrack* >( pTrack.get() ) )
|
||||
pProject->DoTrackSolo(pTrack.get(), event.ShiftDown());
|
||||
TrackActions::DoTrackSolo(*pProject, pTrack.get(), event.ShiftDown());
|
||||
|
||||
return RefreshCode::RefreshNone;
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ Paul Licameli split from TrackPanel.cpp
|
||||
#include "TrackButtonHandles.h"
|
||||
|
||||
#include "../../HitTestResult.h"
|
||||
#include "../../Menus.h"
|
||||
#include "../../Project.h"
|
||||
#include "../../RefreshCode.h"
|
||||
#include "../../Track.h"
|
||||
@ -95,7 +96,7 @@ UIHandle::Result CloseButtonHandle::CommitChanges
|
||||
pProject->StopIfPaused();
|
||||
if (!pProject->IsAudioActive()) {
|
||||
// This pushes an undo item:
|
||||
pProject->RemoveTrack(pTrack.get());
|
||||
TrackActions::DoRemoveTrack(*pProject, pTrack.get());
|
||||
// Redraw all tracks when any one of them closes
|
||||
// (Could we invent a return code that draws only those at or below
|
||||
// the affected track?)
|
||||
|
Loading…
x
Reference in New Issue
Block a user