audacia/src/SelectionState.cpp

203 lines
6.0 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
( ViewInfo &viewInfo, Track &track, bool syncLocked )
{
auto trackRange = syncLocked
// If we have a sync-lock group and sync-lock linking is on,
// check the sync-lock group tracks.
? TrackList::SyncLockGroup(&track)
// Otherwise, check for a stereo pair
: TrackList::Channels(&track);
auto minOffset = trackRange.min( &Track::GetOffset );
auto maxEnd = trackRange.max( &Track::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
( Track &track, bool selected, bool updateLastPicked,
MixerBoard *pMixerBoard )
{
bool wasCorrect = (selected == track.GetSelected());
for (auto channel : TrackList::Channels(&track))
channel->SetSelected(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( *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( *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( 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( track, !track.GetSelected(), true, pMixerBoard );
else {
if (shift && mLastPickedTrack.lock())
ChangeSelectionOnShiftClick( tracks, track, pMixerBoard );
else {
SelectNone( tracks, pMixerBoard );
SelectTrack( track, true, true, pMixerBoard );
SelectTrackLength( 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;
}