mirror of
https://github.com/cookiengineer/audacity
synced 2025-05-13 23:49:03 +02:00
219 lines
6.5 KiB
C++
219 lines
6.5 KiB
C++
/**********************************************************************
|
|
|
|
Audacity: A Digital Audio Editor
|
|
|
|
SelectionState.h
|
|
|
|
**********************************************************************/
|
|
|
|
#include "Audacity.h"
|
|
#include "SelectionState.h"
|
|
#include "MixerBoard.h"
|
|
#include "ViewInfo.h"
|
|
#include "Track.h"
|
|
|
|
// Set selection length to the length of a track -- but if sync-lock is turned
|
|
// on, use the largest possible selection in the sync-lock group.
|
|
// If it's a stereo track, do the same for the stereo channels.
|
|
void SelectionState::SelectTrackLength
|
|
( TrackList &tracks, ViewInfo &viewInfo, Track &track, bool syncLocked )
|
|
{
|
|
SyncLockedTracksIterator it( &tracks );
|
|
Track *t1 = it.StartWith( &track );
|
|
double minOffset = track.GetOffset();
|
|
double maxEnd = track.GetEndTime();
|
|
|
|
// If we have a sync-lock group and sync-lock linking is on,
|
|
// check the sync-lock group tracks.
|
|
if ( syncLocked && t1 != NULL )
|
|
{
|
|
for ( ; t1; t1 = it.Next())
|
|
{
|
|
if (t1->GetOffset() < minOffset)
|
|
minOffset = t1->GetOffset();
|
|
if (t1->GetEndTime() > maxEnd)
|
|
maxEnd = t1->GetEndTime();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Otherwise, check for a stereo pair
|
|
t1 = track.GetLink();
|
|
if (t1)
|
|
{
|
|
if (t1->GetOffset() < minOffset)
|
|
minOffset = t1->GetOffset();
|
|
if (t1->GetEndTime() > maxEnd)
|
|
maxEnd = t1->GetEndTime();
|
|
}
|
|
}
|
|
|
|
// PRL: double click or click on track control.
|
|
// should this select all frequencies too? I think not.
|
|
viewInfo.selectedRegion.setTimes(minOffset, maxEnd);
|
|
}
|
|
|
|
void SelectionState::SelectTrack
|
|
( TrackList &tracks, Track &track, bool selected, bool updateLastPicked,
|
|
MixerBoard *pMixerBoard )
|
|
{
|
|
bool wasCorrect = (selected == track.GetSelected());
|
|
|
|
tracks.Select( &track, selected );
|
|
if (updateLastPicked)
|
|
mLastPickedTrack = Track::Pointer( &track );
|
|
|
|
//The older code below avoids an anchor on an unselected track.
|
|
|
|
/*
|
|
if (selected) {
|
|
// This handles the case of linked tracks, selecting all channels
|
|
mTracks->Select(pTrack, true);
|
|
if (updateLastPicked)
|
|
mLastPickedTrack = Track::Pointer( pTrack );
|
|
}
|
|
else {
|
|
mTracks->Select(pTrack, false);
|
|
if (updateLastPicked && pTrack == mLastPickedTrack.lock().get())
|
|
mLastPickedTrack.reset();
|
|
}
|
|
*/
|
|
|
|
// Update mixer board, but only as needed so it does not flicker.
|
|
if (!wasCorrect) {
|
|
auto pt = dynamic_cast< PlayableTrack* >( &track );
|
|
if (pMixerBoard && pt)
|
|
pMixerBoard->RefreshTrackCluster( pt );
|
|
}
|
|
}
|
|
|
|
void SelectionState::SelectRangeOfTracks
|
|
( TrackList &tracks, Track &rsTrack, Track &reTrack, MixerBoard *pMixerBoard )
|
|
{
|
|
Track *sTrack = &rsTrack, *eTrack = &reTrack;
|
|
// Swap the track pointers if needed
|
|
if (eTrack->GetIndex() < sTrack->GetIndex())
|
|
std::swap(sTrack, eTrack);
|
|
|
|
TrackListIterator iter( &tracks );
|
|
sTrack = iter.StartWith( sTrack );
|
|
do {
|
|
SelectTrack( tracks, *sTrack, true, false, pMixerBoard );
|
|
if ( sTrack == eTrack ) {
|
|
break;
|
|
}
|
|
|
|
sTrack = iter.Next();
|
|
} while ( sTrack );
|
|
}
|
|
|
|
void SelectionState::SelectNone( TrackList &tracks, MixerBoard *pMixerBoard )
|
|
{
|
|
TrackListIterator iter( &tracks );
|
|
Track *track = iter.First();
|
|
while ( track ) {
|
|
SelectTrack( tracks, *track, false, false, pMixerBoard );
|
|
track = iter.Next();
|
|
}
|
|
}
|
|
|
|
void SelectionState::ChangeSelectionOnShiftClick
|
|
( TrackList &tracks, Track &track, MixerBoard *pMixerBoard )
|
|
{
|
|
|
|
// Optional: Track already selected? Nothing to do.
|
|
// If we enable this, Shift-Click behaves like click in this case.
|
|
//if( pTrack->GetSelected() )
|
|
// return;
|
|
|
|
// Find first and last selected track.
|
|
Track* pFirst = nullptr;
|
|
Track* pLast = nullptr;
|
|
// We will either extend from the first or from the last.
|
|
auto pExtendFrom = tracks.Lock(mLastPickedTrack);
|
|
|
|
if( !pExtendFrom ) {
|
|
TrackListIterator iter( &tracks );
|
|
for (Track *t = iter.First(); t; t = iter.Next()) {
|
|
const bool isSelected = t->GetSelected();
|
|
// Record first and last selected.
|
|
if( isSelected ) {
|
|
if( !pFirst )
|
|
pFirst = t;
|
|
pLast = t;
|
|
}
|
|
// If our track is at or after the first, extend from the first.
|
|
if( t == &track )
|
|
pExtendFrom = Track::Pointer( pFirst );
|
|
}
|
|
// Our track was earlier than the first. Extend from the last.
|
|
if( !pExtendFrom )
|
|
pExtendFrom = Track::Pointer( pLast );
|
|
}
|
|
|
|
SelectNone( tracks, pMixerBoard );
|
|
if( pExtendFrom )
|
|
SelectRangeOfTracks( tracks, track, *pExtendFrom, pMixerBoard );
|
|
else
|
|
SelectTrack( tracks, track, true, true, pMixerBoard );
|
|
mLastPickedTrack = pExtendFrom;
|
|
}
|
|
|
|
void SelectionState::HandleListSelection
|
|
( TrackList &tracks, ViewInfo &viewInfo,
|
|
Track &track, bool shift, bool ctrl, bool syncLocked, MixerBoard *pMixerBoard )
|
|
{
|
|
// AS: If the shift button is being held down, invert
|
|
// the selection on this track.
|
|
if (ctrl)
|
|
SelectTrack( tracks, track, !track.GetSelected(), true, pMixerBoard );
|
|
else {
|
|
if (shift && mLastPickedTrack.lock())
|
|
ChangeSelectionOnShiftClick( tracks, track, pMixerBoard );
|
|
else {
|
|
SelectNone( tracks, pMixerBoard );
|
|
SelectTrack( tracks, track, true, true, pMixerBoard );
|
|
SelectTrackLength( tracks, viewInfo, track, syncLocked );
|
|
}
|
|
|
|
if (pMixerBoard)
|
|
pMixerBoard->RefreshTrackClusters();
|
|
}
|
|
}
|
|
|
|
SelectionStateChanger::SelectionStateChanger
|
|
( SelectionState &state, TrackList &tracks )
|
|
: mpState{ &state }
|
|
, mTracks{ tracks }
|
|
, mInitialLastPickedTrack{ state.mLastPickedTrack }
|
|
{
|
|
// Save initial state of track selections
|
|
mInitialTrackSelection.clear();
|
|
TrackListIterator iter( &mTracks );
|
|
for (Track *track = iter.First(); track; track = iter.Next()) {
|
|
const bool isSelected = track->GetSelected();
|
|
mInitialTrackSelection.push_back(isSelected);
|
|
}
|
|
}
|
|
|
|
SelectionStateChanger::~SelectionStateChanger()
|
|
{
|
|
if ( mpState ) {
|
|
// roll back changes
|
|
mpState->mLastPickedTrack = mInitialLastPickedTrack;
|
|
TrackListIterator iter( &mTracks );
|
|
std::vector<bool>::const_iterator
|
|
it = mInitialTrackSelection.begin(),
|
|
end = mInitialTrackSelection.end();
|
|
for (Track *track = iter.First(); track && it != end; track = iter.Next()) {
|
|
// wxASSERT(it != end);
|
|
track->SetSelected( *it++ );
|
|
}
|
|
}
|
|
}
|
|
|
|
void SelectionStateChanger::Commit()
|
|
{
|
|
mpState = nullptr;
|
|
}
|