mirror of
https://github.com/cookiengineer/audacity
synced 2025-05-06 23:02:42 +02:00
180 lines
5.1 KiB
C++
180 lines
5.1 KiB
C++
/**********************************************************************
|
|
|
|
Audacity: A Digital Audio Editor
|
|
|
|
SelectionState.h
|
|
|
|
**********************************************************************/
|
|
|
|
#include "Audacity.h"
|
|
#include "SelectionState.h"
|
|
|
|
#include "ViewInfo.h"
|
|
#include "Track.h"
|
|
#include "Project.h"
|
|
|
|
static const AudacityProject::AttachedObjects::RegisteredFactory key{
|
|
[](AudacityProject &){ return std::make_shared< SelectionState >(); }
|
|
};
|
|
|
|
SelectionState &SelectionState::Get( AudacityProject &project )
|
|
{
|
|
return project.AttachedObjects::Get< SelectionState >( key );
|
|
}
|
|
|
|
const SelectionState &SelectionState::Get( const AudacityProject &project )
|
|
{
|
|
return Get( const_cast< AudacityProject & >( project ) );
|
|
}
|
|
|
|
// 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 )
|
|
{
|
|
//bool wasCorrect = (selected == track.GetSelected());
|
|
|
|
for (auto channel : TrackList::Channels(&track))
|
|
channel->SetSelected(selected);
|
|
|
|
if (updateLastPicked)
|
|
mLastPickedTrack = track.SharedPointer();
|
|
|
|
//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();
|
|
}
|
|
*/
|
|
}
|
|
|
|
void SelectionState::SelectRangeOfTracks
|
|
( TrackList &tracks, Track &rsTrack, Track &reTrack )
|
|
{
|
|
Track *sTrack = &rsTrack, *eTrack = &reTrack;
|
|
// Swap the track pointers if needed
|
|
if (eTrack->GetIndex() < sTrack->GetIndex())
|
|
std::swap(sTrack, eTrack);
|
|
|
|
for (auto track :
|
|
tracks.Any().StartingWith(sTrack).EndingAfter(eTrack))
|
|
SelectTrack(*track, true, false);
|
|
}
|
|
|
|
void SelectionState::SelectNone( TrackList &tracks )
|
|
{
|
|
for (auto t : tracks.Any())
|
|
SelectTrack( *t, false, false );
|
|
}
|
|
|
|
void SelectionState::ChangeSelectionOnShiftClick
|
|
( TrackList &tracks, Track &track )
|
|
{
|
|
// We will either extend from the first or from the last.
|
|
auto pExtendFrom = tracks.Lock(mLastPickedTrack);
|
|
|
|
if( !pExtendFrom ) {
|
|
auto trackRange = tracks.Selected();
|
|
auto pFirst = *trackRange.begin();
|
|
|
|
// If our track is at or after the first, extend from the first.
|
|
if( pFirst && track.GetIndex() >= pFirst->GetIndex() )
|
|
pExtendFrom = pFirst->SharedPointer();
|
|
|
|
// Our track was earlier than the first. Extend from the last.
|
|
if( !pExtendFrom )
|
|
pExtendFrom = Track::SharedPointer( *trackRange.rbegin() );
|
|
}
|
|
|
|
SelectNone( tracks );
|
|
if( pExtendFrom )
|
|
SelectRangeOfTracks( tracks, track, *pExtendFrom );
|
|
else
|
|
SelectTrack( track, true, true );
|
|
mLastPickedTrack = pExtendFrom;
|
|
}
|
|
|
|
void SelectionState::HandleListSelection
|
|
( TrackList &tracks, ViewInfo &viewInfo,
|
|
Track &track, bool shift, bool ctrl, bool syncLocked )
|
|
{
|
|
// AS: If the shift button is being held down, invert
|
|
// the selection on this track.
|
|
if (ctrl)
|
|
SelectTrack( track, !track.GetSelected(), true );
|
|
else {
|
|
if (shift && mLastPickedTrack.lock())
|
|
ChangeSelectionOnShiftClick( tracks, track );
|
|
else {
|
|
SelectNone( tracks );
|
|
SelectTrack( track, true, true );
|
|
SelectTrackLength( viewInfo, track, syncLocked );
|
|
}
|
|
}
|
|
}
|
|
|
|
SelectionStateChanger::SelectionStateChanger
|
|
( SelectionState &state, TrackList &tracks )
|
|
: mpState{ &state }
|
|
, mTracks{ tracks }
|
|
, mInitialLastPickedTrack{ state.mLastPickedTrack }
|
|
{
|
|
// Save initial state of track selections
|
|
mInitialTrackSelection.clear();
|
|
for (const auto track : tracks.Any()) {
|
|
const bool isSelected = track->GetSelected();
|
|
mInitialTrackSelection.push_back(isSelected);
|
|
}
|
|
}
|
|
|
|
SelectionStateChanger::~SelectionStateChanger()
|
|
{
|
|
if ( mpState ) {
|
|
// roll back changes
|
|
mpState->mLastPickedTrack = mInitialLastPickedTrack;
|
|
std::vector<bool>::const_iterator
|
|
it = mInitialTrackSelection.begin(),
|
|
end = mInitialTrackSelection.end();
|
|
|
|
for (auto track : mTracks.Any()) {
|
|
if (it == end)
|
|
break;
|
|
track->SetSelected( *it++ );
|
|
}
|
|
}
|
|
}
|
|
|
|
void SelectionStateChanger::Commit()
|
|
{
|
|
mpState = nullptr;
|
|
}
|