1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
|
/**********************************************************************
Audacity: A Digital Audio Editor
SelectionState.h
**********************************************************************/
#include "SelectionState.h"
#include "ViewInfo.h"
#include "SyncLock.h"
#include "Track.h"
#include "Project.h"
#include <cassert>
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 ) );
}
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.
? SyncLock::Group(track)
// Otherwise, check for one track
: TrackList::SingletonRange(&track);
auto minOffset = trackRange.min(&Track::GetStartTime);
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());
track.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
auto begin = tracks.begin(),
iterS = tracks.Find(sTrack),
iterE = tracks.Find(eTrack);
auto indS = std::distance(begin, iterS),
indE = std::distance(begin, iterE);
if (indE < indS)
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)
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) {
auto begin = tracks.begin(),
iterT = tracks.Find(&track),
iterF = tracks.Find(pFirst);
auto indT = std::distance(begin, iterT),
indF = std::distance(begin, iterF);
if (indT >= indF)
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
const auto range = tracks.Any();
mInitialTrackSelection.clear();
mInitialTrackSelection.reserve(range.size());
for (const auto track : range) {
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) {
if (it == end)
break;
track->SetSelected( *it++ );
}
}
}
void SelectionStateChanger::Commit()
{
mpState = nullptr;
}
|