13
0
livetrax/gtk2_ardour/selection.cc
nick_m a795892492 Separate selection operations into their own temporary history mechanism.
The user can now replay *all* earlier selection operations until the next
session undo/redo command, or the completion of a new operation.
Nothing relating to selection ops is stored, and selection operation history
is begun on first idle.

Selection operation history is fundamentally different from the history of
operations which act on a selection in terms of both their viewport and the
amount of information required to replay them.
WRT undo, the user of a selection op doesn't care about the viewport state
at the beginning of an op, but rather that at the end of the previous one.
2015-01-02 08:01:12 -05:00

1376 lines
29 KiB
C++

/*
Copyright (C) 2002 Paul Davis
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <algorithm>
#include <sigc++/bind.h>
#include "pbd/error.h"
#include "pbd/stacktrace.h"
#include "ardour/playlist.h"
#include "ardour/rc_configuration.h"
#include "gui_thread.h"
#include "midi_cut_buffer.h"
#include "region_view.h"
#include "selection.h"
#include "selection_templates.h"
#include "time_axis_view.h"
#include "automation_time_axis.h"
#include "public_editor.h"
#include "control_point.h"
#include "i18n.h"
using namespace std;
using namespace ARDOUR;
using namespace PBD;
struct AudioRangeComparator {
bool operator()(AudioRange a, AudioRange b) {
return a.start < b.start;
}
};
Selection::Selection (const PublicEditor* e)
: tracks (e)
, editor (e)
, next_time_id (0)
, _no_tracks_changed (false)
{
clear ();
/* we have disambiguate which remove() for the compiler */
void (Selection::*track_remove)(TimeAxisView*) = &Selection::remove;
TimeAxisView::CatchDeletion.connect (*this, MISSING_INVALIDATOR, boost::bind (track_remove, this, _1), gui_context());
void (Selection::*marker_remove)(Marker*) = &Selection::remove;
Marker::CatchDeletion.connect (*this, MISSING_INVALIDATOR, boost::bind (marker_remove, this, _1), gui_context());
void (Selection::*point_remove)(ControlPoint*) = &Selection::remove;
ControlPoint::CatchDeletion.connect (*this, MISSING_INVALIDATOR, boost::bind (point_remove, this, _1), gui_context());
}
#if 0
Selection&
Selection::operator= (const Selection& other)
{
if (&other != this) {
regions = other.regions;
tracks = other.tracks;
time = other.time;
lines = other.lines;
midi_regions = other.midi_regions;
midi_notes = other.midi_notes;
}
return *this;
}
#endif
bool
operator== (const Selection& a, const Selection& b)
{
return a.regions == b.regions &&
a.tracks == b.tracks &&
a.time == b.time &&
a.lines == b.lines &&
a.playlists == b.playlists &&
a.midi_notes == b.midi_notes &&
a.midi_regions == b.midi_regions;
}
/** Clear everything from the Selection */
void
Selection::clear ()
{
clear_tracks ();
clear_regions ();
clear_points ();
clear_lines();
clear_time ();
clear_playlists ();
clear_midi_notes ();
clear_midi_regions ();
clear_markers ();
}
void
Selection::clear_objects ()
{
clear_regions ();
clear_points ();
clear_lines();
clear_playlists ();
clear_midi_notes ();
clear_midi_regions ();
}
void
Selection::clear_tracks ()
{
if (!tracks.empty()) {
for (TrackViewList::iterator x = tracks.begin(); x != tracks.end(); ++x) {
(*x)->set_selected (false);
}
tracks.clear ();
if (!_no_tracks_changed) {
TracksChanged();
}
}
}
void
Selection::clear_time ()
{
time.clear();
TimeChanged ();
}
void
Selection::dump_region_layers()
{
cerr << "region selection layer dump" << endl;
for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
cerr << "layer: " << (int)(*i)->region()->layer() << endl;
}
}
void
Selection::clear_regions ()
{
if (!regions.empty()) {
regions.clear_all ();
RegionsChanged();
}
}
void
Selection::clear_midi_notes ()
{
if (!midi_notes.empty()) {
for (MidiNoteSelection::iterator x = midi_notes.begin(); x != midi_notes.end(); ++x) {
delete *x;
}
midi_notes.clear ();
MidiNotesChanged ();
}
/* The note selection is actually stored in MidiRegionView, emit signal to
tell them to clear their selection. */
ClearMidiNoteSelection(); /* EMIT SIGNAL */
}
void
Selection::clear_midi_regions ()
{
if (!midi_regions.empty()) {
midi_regions.clear ();
MidiRegionsChanged ();
}
}
void
Selection::clear_playlists ()
{
/* Selections own their playlists */
for (PlaylistSelection::iterator i = playlists.begin(); i != playlists.end(); ++i) {
/* selections own their own regions, which are copies of the "originals". make them go away */
(*i)->drop_regions ();
(*i)->release ();
}
if (!playlists.empty()) {
playlists.clear ();
PlaylistsChanged();
}
}
void
Selection::clear_lines ()
{
if (!lines.empty()) {
lines.clear ();
LinesChanged();
}
}
void
Selection::clear_markers ()
{
if (!markers.empty()) {
markers.clear ();
MarkersChanged();
}
}
void
Selection::toggle (boost::shared_ptr<Playlist> pl)
{
clear_time(); //enforce object/range exclusivity
clear_tracks(); //enforce object/track exclusivity
PlaylistSelection::iterator i;
if ((i = find (playlists.begin(), playlists.end(), pl)) == playlists.end()) {
pl->use ();
playlists.push_back(pl);
} else {
playlists.erase (i);
}
PlaylistsChanged ();
}
void
Selection::toggle (const TrackViewList& track_list)
{
for (TrackViewList::const_iterator i = track_list.begin(); i != track_list.end(); ++i) {
toggle ((*i));
}
}
void
Selection::toggle (TimeAxisView* track)
{
TrackSelection::iterator i;
if ((i = find (tracks.begin(), tracks.end(), track)) == tracks.end()) {
track->set_selected (true);
tracks.push_back (track);
} else {
track->set_selected (false);
tracks.erase (i);
}
if (!_no_tracks_changed) {
TracksChanged();
}
}
void
Selection::toggle (const MidiNoteSelection& midi_note_list)
{
clear_time(); //enforce object/range exclusivity
clear_tracks(); //enforce object/track exclusivity
for (MidiNoteSelection::const_iterator i = midi_note_list.begin(); i != midi_note_list.end(); ++i) {
toggle ((*i));
}
}
void
Selection::toggle (MidiCutBuffer* midi)
{
MidiNoteSelection::iterator i;
if ((i = find (midi_notes.begin(), midi_notes.end(), midi)) == midi_notes.end()) {
midi_notes.push_back (midi);
} else {
/* remember that we own the MCB */
delete *i;
midi_notes.erase (i);
}
MidiNotesChanged();
}
void
Selection::toggle (RegionView* r)
{
clear_time(); //enforce object/range exclusivity
clear_tracks(); //enforce object/track exclusivity
RegionSelection::iterator i;
if ((i = find (regions.begin(), regions.end(), r)) == regions.end()) {
add (r);
} else {
remove (*i);
}
RegionsChanged ();
}
void
Selection::toggle (MidiRegionView* mrv)
{
clear_time(); //enforce object/range exclusivity
clear_tracks(); //enforce object/track exclusivity
MidiRegionSelection::iterator i;
if ((i = find (midi_regions.begin(), midi_regions.end(), mrv)) == midi_regions.end()) {
add (mrv);
} else {
midi_regions.erase (i);
}
MidiRegionsChanged ();
}
void
Selection::toggle (vector<RegionView*>& r)
{
clear_time(); //enforce object/range exclusivity
clear_tracks(); //enforce object/track exclusivity
RegionSelection::iterator i;
for (vector<RegionView*>::iterator x = r.begin(); x != r.end(); ++x) {
if ((i = find (regions.begin(), regions.end(), (*x))) == regions.end()) {
add ((*x));
} else {
remove (*x);
}
}
RegionsChanged ();
}
long
Selection::toggle (framepos_t start, framepos_t end)
{
clear_objects(); //enforce object/range exclusivity
AudioRangeComparator cmp;
/* XXX this implementation is incorrect */
time.push_back (AudioRange (start, end, ++next_time_id));
time.consolidate ();
time.sort (cmp);
TimeChanged ();
return next_time_id;
}
void
Selection::add (boost::shared_ptr<Playlist> pl)
{
clear_time(); //enforce object/range exclusivity
clear_tracks(); //enforce object/track exclusivity
if (find (playlists.begin(), playlists.end(), pl) == playlists.end()) {
pl->use ();
playlists.push_back(pl);
PlaylistsChanged ();
}
}
void
Selection::add (const list<boost::shared_ptr<Playlist> >& pllist)
{
clear_time(); //enforce object/range exclusivity
clear_tracks(); //enforce object/track exclusivity
bool changed = false;
for (list<boost::shared_ptr<Playlist> >::const_iterator i = pllist.begin(); i != pllist.end(); ++i) {
if (find (playlists.begin(), playlists.end(), (*i)) == playlists.end()) {
(*i)->use ();
playlists.push_back (*i);
changed = true;
}
}
if (changed) {
PlaylistsChanged ();
}
}
void
Selection::add (const TrackViewList& track_list)
{
clear_objects(); //enforce object/range exclusivity
TrackViewList added = tracks.add (track_list);
if (!added.empty()) {
for (TrackViewList::iterator x = added.begin(); x != added.end(); ++x) {
(*x)->set_selected (true);
}
if (!_no_tracks_changed) {
TracksChanged ();
}
}
}
void
Selection::add (TimeAxisView* track)
{
clear_objects(); //enforce object/range exclusivity
TrackViewList tr;
track->set_selected (true);
tr.push_back (track);
add (tr);
}
void
Selection::add (const MidiNoteSelection& midi_list)
{
clear_time(); //enforce object/range exclusivity
clear_tracks(); //enforce object/track exclusivity
const MidiNoteSelection::const_iterator b = midi_list.begin();
const MidiNoteSelection::const_iterator e = midi_list.end();
if (!midi_list.empty()) {
midi_notes.insert (midi_notes.end(), b, e);
MidiNotesChanged ();
}
}
void
Selection::add (MidiCutBuffer* midi)
{
/* we take ownership of the MCB */
if (find (midi_notes.begin(), midi_notes.end(), midi) == midi_notes.end()) {
midi_notes.push_back (midi);
MidiNotesChanged ();
}
}
void
Selection::add (vector<RegionView*>& v)
{
clear_time(); //enforce object/range exclusivity
clear_tracks(); //enforce object/track exclusivity
/* XXX This method or the add (const RegionSelection&) needs to go
*/
bool changed = false;
for (vector<RegionView*>::iterator i = v.begin(); i != v.end(); ++i) {
if (find (regions.begin(), regions.end(), (*i)) == regions.end()) {
changed = regions.add ((*i));
}
}
if (changed) {
RegionsChanged ();
}
}
void
Selection::add (const RegionSelection& rs)
{
clear_time(); //enforce object/range exclusivity
clear_tracks(); //enforce object/track exclusivity
/* XXX This method or the add (const vector<RegionView*>&) needs to go
*/
bool changed = false;
for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
if (find (regions.begin(), regions.end(), (*i)) == regions.end()) {
changed = regions.add ((*i));
}
}
if (changed) {
RegionsChanged ();
}
}
void
Selection::add (RegionView* r)
{
clear_time(); //enforce object/range exclusivity
clear_tracks(); //enforce object/track exclusivity
if (find (regions.begin(), regions.end(), r) == regions.end()) {
bool changed = regions.add (r);
if (changed) {
RegionsChanged ();
}
}
}
void
Selection::add (MidiRegionView* mrv)
{
clear_time(); //enforce object/range exclusivity
clear_tracks(); //enforce object/track exclusivity
if (find (midi_regions.begin(), midi_regions.end(), mrv) == midi_regions.end()) {
midi_regions.push_back (mrv);
/* XXX should we do this? */
MidiRegionsChanged ();
}
}
long
Selection::add (framepos_t start, framepos_t end)
{
clear_objects(); //enforce object/range exclusivity
AudioRangeComparator cmp;
/* XXX this implementation is incorrect */
time.push_back (AudioRange (start, end, ++next_time_id));
time.consolidate ();
time.sort (cmp);
TimeChanged ();
return next_time_id;
}
void
Selection::move_time (framecnt_t distance)
{
if (distance == 0) {
return;
}
for (list<AudioRange>::iterator i = time.begin(); i != time.end(); ++i) {
(*i).start += distance;
(*i).end += distance;
}
TimeChanged ();
}
void
Selection::replace (uint32_t sid, framepos_t start, framepos_t end)
{
clear_objects(); //enforce object/range exclusivity
for (list<AudioRange>::iterator i = time.begin(); i != time.end(); ++i) {
if ((*i).id == sid) {
time.erase (i);
time.push_back (AudioRange(start,end, sid));
/* don't consolidate here */
AudioRangeComparator cmp;
time.sort (cmp);
TimeChanged ();
break;
}
}
}
void
Selection::add (boost::shared_ptr<Evoral::ControlList> cl)
{
clear_time(); //enforce object/range exclusivity
clear_tracks(); //enforce object/track exclusivity
boost::shared_ptr<ARDOUR::AutomationList> al
= boost::dynamic_pointer_cast<ARDOUR::AutomationList>(cl);
if (!al) {
warning << "Programming error: Selected list is not an ARDOUR::AutomationList" << endmsg;
return;
}
if (find (lines.begin(), lines.end(), al) == lines.end()) {
lines.push_back (al);
LinesChanged();
}
}
void
Selection::remove (TimeAxisView* track)
{
list<TimeAxisView*>::iterator i;
if ((i = find (tracks.begin(), tracks.end(), track)) != tracks.end()) {
track->set_selected (false);
tracks.erase (i);
if (!_no_tracks_changed) {
TracksChanged();
}
}
}
void
Selection::remove (const TrackViewList& track_list)
{
bool changed = false;
for (TrackViewList::const_iterator i = track_list.begin(); i != track_list.end(); ++i) {
TrackViewList::iterator x = find (tracks.begin(), tracks.end(), *i);
if (x != tracks.end()) {
(*i)->set_selected (false);
tracks.erase (x);
changed = true;
}
}
if (changed) {
if (!_no_tracks_changed) {
TracksChanged();
}
}
}
void
Selection::remove (ControlPoint* p)
{
PointSelection::iterator i = find (points.begin(), points.end(), p);
if (i != points.end ()) {
points.erase (i);
}
}
void
Selection::remove (const MidiNoteSelection& midi_list)
{
bool changed = false;
for (MidiNoteSelection::const_iterator i = midi_list.begin(); i != midi_list.end(); ++i) {
MidiNoteSelection::iterator x;
if ((x = find (midi_notes.begin(), midi_notes.end(), (*i))) != midi_notes.end()) {
midi_notes.erase (x);
changed = true;
}
}
if (changed) {
MidiNotesChanged();
}
}
void
Selection::remove (MidiCutBuffer* midi)
{
MidiNoteSelection::iterator x;
if ((x = find (midi_notes.begin(), midi_notes.end(), midi)) != midi_notes.end()) {
/* remember that we own the MCB */
delete *x;
midi_notes.erase (x);
MidiNotesChanged ();
}
}
void
Selection::remove (boost::shared_ptr<Playlist> track)
{
list<boost::shared_ptr<Playlist> >::iterator i;
if ((i = find (playlists.begin(), playlists.end(), track)) != playlists.end()) {
playlists.erase (i);
PlaylistsChanged();
}
}
void
Selection::remove (const list<boost::shared_ptr<Playlist> >& pllist)
{
bool changed = false;
for (list<boost::shared_ptr<Playlist> >::const_iterator i = pllist.begin(); i != pllist.end(); ++i) {
list<boost::shared_ptr<Playlist> >::iterator x;
if ((x = find (playlists.begin(), playlists.end(), (*i))) != playlists.end()) {
playlists.erase (x);
changed = true;
}
}
if (changed) {
PlaylistsChanged();
}
}
void
Selection::remove (RegionView* r)
{
if (regions.remove (r)) {
RegionsChanged ();
}
}
void
Selection::remove (MidiRegionView* mrv)
{
MidiRegionSelection::iterator x;
if ((x = find (midi_regions.begin(), midi_regions.end(), mrv)) != midi_regions.end()) {
midi_regions.erase (x);
MidiRegionsChanged ();
}
}
void
Selection::remove (uint32_t selection_id)
{
if (time.empty()) {
return;
}
for (list<AudioRange>::iterator i = time.begin(); i != time.end(); ++i) {
if ((*i).id == selection_id) {
time.erase (i);
TimeChanged ();
break;
}
}
}
void
Selection::remove (framepos_t /*start*/, framepos_t /*end*/)
{
}
void
Selection::remove (boost::shared_ptr<ARDOUR::AutomationList> ac)
{
AutomationSelection::iterator i;
if ((i = find (lines.begin(), lines.end(), ac)) != lines.end()) {
lines.erase (i);
LinesChanged();
}
}
void
Selection::set (TimeAxisView* track)
{
clear_objects(); //enforce object/range exclusivity
clear_tracks ();
add (track);
}
void
Selection::set (const TrackViewList& track_list)
{
clear_objects(); //enforce object/range exclusivity
clear_tracks ();
add (track_list);
}
void
Selection::set (const MidiNoteSelection& midi_list)
{
clear_time (); //enforce region/object exclusivity
clear_tracks(); //enforce object/track exclusivity
clear_objects ();
add (midi_list);
}
void
Selection::set (boost::shared_ptr<Playlist> playlist)
{
clear_time (); //enforce region/object exclusivity
clear_tracks(); //enforce object/track exclusivity
clear_objects ();
add (playlist);
}
void
Selection::set (const list<boost::shared_ptr<Playlist> >& pllist)
{
clear_time(); //enforce region/object exclusivity
clear_objects ();
add (pllist);
}
void
Selection::set (const RegionSelection& rs)
{
clear_time(); //enforce region/object exclusivity
clear_tracks(); //enforce object/track exclusivity
clear_objects();
regions = rs;
RegionsChanged(); /* EMIT SIGNAL */
}
void
Selection::set (MidiRegionView* mrv)
{
clear_time(); //enforce region/object exclusivity
clear_tracks(); //enforce object/track exclusivity
clear_objects ();
add (mrv);
}
void
Selection::set (RegionView* r, bool /*also_clear_tracks*/)
{
clear_time(); //enforce region/object exclusivity
clear_tracks(); //enforce object/track exclusivity
clear_objects ();
add (r);
}
void
Selection::set (vector<RegionView*>& v)
{
clear_time(); //enforce region/object exclusivity
clear_tracks(); //enforce object/track exclusivity
clear_objects();
add (v);
}
/** Set the start and end time of the time selection, without changing
* the list of tracks it applies to.
*/
long
Selection::set (framepos_t start, framepos_t end)
{
clear_objects(); //enforce region/object exclusivity
clear_time();
if ((start == 0 && end == 0) || end < start) {
return 0;
}
if (time.empty()) {
time.push_back (AudioRange (start, end, ++next_time_id));
} else {
/* reuse the first entry, and remove all the rest */
while (time.size() > 1) {
time.pop_front();
}
time.front().start = start;
time.front().end = end;
}
time.consolidate ();
TimeChanged ();
return time.front().id;
}
/** Set the start and end of the range selection. If more than one range
* is currently selected, the start of the earliest range and the end of the
* latest range are set. If no range is currently selected, this method
* selects a single range from start to end.
*
* @param start New start time.
* @param end New end time.
*/
void
Selection::set_preserving_all_ranges (framepos_t start, framepos_t end)
{
clear_objects(); //enforce region/object exclusivity
if ((start == 0 && end == 0) || (end < start)) {
return;
}
if (time.empty ()) {
time.push_back (AudioRange (start, end, ++next_time_id));
} else {
time.sort (AudioRangeComparator ());
time.front().start = start;
time.back().end = end;
}
time.consolidate ();
TimeChanged ();
}
void
Selection::set (boost::shared_ptr<Evoral::ControlList> ac)
{
clear_time(); //enforce region/object exclusivity
clear_tracks(); //enforce object/track exclusivity
clear_objects();
add (ac);
}
bool
Selection::selected (Marker* m)
{
return find (markers.begin(), markers.end(), m) != markers.end();
}
bool
Selection::selected (TimeAxisView* tv)
{
return tv->get_selected ();
}
bool
Selection::selected (RegionView* rv)
{
return find (regions.begin(), regions.end(), rv) != regions.end();
}
bool
Selection::selected (ControlPoint* cp)
{
return find (points.begin(), points.end(), cp) != points.end();
}
bool
Selection::empty (bool internal_selection)
{
bool object_level_empty = regions.empty () &&
tracks.empty () &&
points.empty () &&
playlists.empty () &&
lines.empty () &&
time.empty () &&
playlists.empty () &&
markers.empty() &&
midi_regions.empty()
;
if (!internal_selection) {
return object_level_empty;
}
/* this is intended to really only apply when using a Selection
as a cut buffer.
*/
return object_level_empty && midi_notes.empty() && points.empty();
}
void
Selection::toggle (ControlPoint* cp)
{
clear_time(); //enforce region/object exclusivity
clear_tracks(); //enforce object/track exclusivity
cp->set_selected (!cp->get_selected ());
PointSelection::iterator i = find (points.begin(), points.end(), cp);
if (i == points.end()) {
points.push_back (cp);
} else {
points.erase (i);
}
PointsChanged (); /* EMIT SIGNAL */
}
void
Selection::toggle (vector<ControlPoint*> const & cps)
{
clear_time(); //enforce region/object exclusivity
clear_tracks(); //enforce object/track exclusivity
for (vector<ControlPoint*>::const_iterator i = cps.begin(); i != cps.end(); ++i) {
toggle (*i);
}
}
void
Selection::toggle (list<Selectable*> const & selectables)
{
clear_time(); //enforce region/object exclusivity
clear_tracks(); //enforce object/track exclusivity
RegionView* rv;
ControlPoint* cp;
vector<RegionView*> rvs;
vector<ControlPoint*> cps;
for (std::list<Selectable*>::const_iterator i = selectables.begin(); i != selectables.end(); ++i) {
if ((rv = dynamic_cast<RegionView*> (*i)) != 0) {
rvs.push_back (rv);
} else if ((cp = dynamic_cast<ControlPoint*> (*i)) != 0) {
cps.push_back (cp);
} else {
fatal << _("programming error: ")
<< X_("unknown selectable type passed to Selection::toggle()")
<< endmsg;
abort(); /*NOTREACHED*/
}
}
if (!rvs.empty()) {
toggle (rvs);
}
if (!cps.empty()) {
toggle (cps);
}
}
void
Selection::set (list<Selectable*> const & selectables)
{
clear_time (); //enforce region/object exclusivity
clear_tracks(); //enforce object/track exclusivity
clear_objects ();
add (selectables);
}
void
Selection::add (PointSelection const & s)
{
clear_time (); //enforce region/object exclusivity
clear_tracks(); //enforce object/track exclusivity
for (PointSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
points.push_back (*i);
}
}
void
Selection::add (list<Selectable*> const & selectables)
{
clear_time (); //enforce region/object exclusivity
clear_tracks(); //enforce object/track exclusivity
RegionView* rv;
ControlPoint* cp;
vector<RegionView*> rvs;
vector<ControlPoint*> cps;
for (std::list<Selectable*>::const_iterator i = selectables.begin(); i != selectables.end(); ++i) {
if ((rv = dynamic_cast<RegionView*> (*i)) != 0) {
rvs.push_back (rv);
} else if ((cp = dynamic_cast<ControlPoint*> (*i)) != 0) {
cps.push_back (cp);
} else {
fatal << _("programming error: ")
<< X_("unknown selectable type passed to Selection::add()")
<< endmsg;
abort(); /*NOTREACHED*/
}
}
if (!rvs.empty()) {
add (rvs);
}
if (!cps.empty()) {
add (cps);
}
}
void
Selection::clear_points ()
{
if (!points.empty()) {
points.clear ();
PointsChanged ();
}
}
void
Selection::add (ControlPoint* cp)
{
clear_time (); //enforce region/object exclusivity
clear_tracks(); //enforce object/track exclusivity
cp->set_selected (true);
points.push_back (cp);
PointsChanged (); /* EMIT SIGNAL */
}
void
Selection::add (vector<ControlPoint*> const & cps)
{
clear_time (); //enforce region/object exclusivity
clear_tracks(); //enforce object/track exclusivity
for (vector<ControlPoint*>::const_iterator i = cps.begin(); i != cps.end(); ++i) {
(*i)->set_selected (true);
points.push_back (*i);
}
PointsChanged (); /* EMIT SIGNAL */
}
void
Selection::set (ControlPoint* cp)
{
clear_time (); //enforce region/object exclusivity
clear_tracks(); //enforce object/track exclusivity
if (cp->get_selected()) {
return;
}
for (uint32_t i = 0; i < cp->line().npoints(); ++i) {
cp->line().nth (i)->set_selected (false);
}
clear_objects ();
add (cp);
}
void
Selection::set (Marker* m)
{
clear_time (); //enforce region/object exclusivity
clear_tracks(); //enforce object/track exclusivity
markers.clear ();
add (m);
}
void
Selection::toggle (Marker* m)
{
MarkerSelection::iterator i;
if ((i = find (markers.begin(), markers.end(), m)) == markers.end()) {
add (m);
} else {
remove (m);
}
}
void
Selection::remove (Marker* m)
{
MarkerSelection::iterator i;
if ((i = find (markers.begin(), markers.end(), m)) != markers.end()) {
markers.erase (i);
MarkersChanged();
}
}
void
Selection::add (Marker* m)
{
clear_time (); //enforce region/object exclusivity
clear_tracks(); //enforce object/track exclusivity
if (find (markers.begin(), markers.end(), m) == markers.end()) {
markers.push_back (m);
MarkersChanged();
}
}
void
Selection::add (const list<Marker*>& m)
{
clear_time (); //enforce region/object exclusivity
clear_tracks(); //enforce object/track exclusivity
markers.insert (markers.end(), m.begin(), m.end());
markers.sort ();
markers.unique ();
MarkersChanged ();
}
void
MarkerSelection::range (framepos_t& s, framepos_t& e)
{
s = max_framepos;
e = 0;
for (MarkerSelection::iterator i = begin(); i != end(); ++i) {
if ((*i)->position() < s) {
s = (*i)->position();
}
if ((*i)->position() > e) {
e = (*i)->position();
}
}
s = std::min (s, e);
e = std::max (s, e);
}
XMLNode&
Selection::get_state () const
{
/* XXX: not complete; just sufficient to get track selection state
so that re-opening plugin windows for editor mixer strips works
*/
char buf[32];
XMLNode* node = new XMLNode (X_("Selection"));
for (TrackSelection::const_iterator i = tracks.begin(); i != tracks.end(); ++i) {
RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (*i);
if (rtv) {
XMLNode* t = node->add_child (X_("RouteView"));
t->add_property (X_("id"), atoi (rtv->route()->id().to_s().c_str()));
} else if (atv) {
XMLNode* t = node->add_child (X_("AutomationView"));
t->add_property (X_("id"), atoi (atv->parent_route()->id().to_s().c_str()));
t->add_property (X_("parameter"), EventTypeMap::instance().to_symbol (atv->parameter ()));
}
}
for (RegionSelection::const_iterator i = regions.begin(); i != regions.end(); ++i) {
XMLNode* r = node->add_child (X_("Region"));
r->add_property (X_("id"), atoi ((*i)->region ()->id ().to_s ().c_str()));
}
for (TimeSelection::const_iterator i = time.begin(); i != time.end(); ++i) {
XMLNode* t = node->add_child (X_("AudioRange"));
snprintf(buf, sizeof(buf), "%" PRId64, (*i).start);
t->add_property (X_("start"), string(buf));
snprintf(buf, sizeof(buf), "%" PRId64, (*i).end);
t->add_property (X_("end"), string(buf));
}
for (MarkerSelection::const_iterator i = markers.begin(); i != markers.end(); ++i) {
XMLNode* t = node->add_child (X_("Marker"));
bool is_start;
Location* loc = editor->find_location_from_marker (*i, is_start);
t->add_property (X_("id"), atoi (loc->id().to_s().c_str()));
t->add_property (X_("start"), is_start ? X_("yes") : X_("no"));
}
return *node;
}
int
Selection::set_state (XMLNode const & node, int)
{
if (node.name() != X_("Selection")) {
return -1;
}
clear_regions ();
clear_points ();
clear_time ();
clear_tracks ();
clear_markers ();
XMLNodeList children = node.children ();
for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
if ((*i)->name() == X_("RouteView")) {
XMLProperty* prop_id = (*i)->property (X_("id"));
assert (prop_id);
PBD::ID id (prop_id->value ());
RouteTimeAxisView* rtv = editor->get_route_view_by_route_id (id);
if (rtv) {
add (rtv);
}
} else if ((*i)->name() == X_("Region")) {
XMLProperty* prop_id = (*i)->property (X_("id"));
assert (prop_id);
PBD::ID id (prop_id->value ());
RegionSelection rs;
editor->get_regionviews_by_id (id, rs);
if (!rs.empty ()) {
add (rs);
} else {
/*
regionviews are being constructed - stash the region IDs
so we can identify them in Editor::region_view_added ()
*/
regions.pending.push_back (id);
}
} else if ((*i)->name() == X_("AudioRange")) {
XMLProperty* prop_start = (*i)->property (X_("start"));
XMLProperty* prop_end = (*i)->property (X_("end"));
assert (prop_start);
assert (prop_end);
framepos_t s (atol (prop_start->value ().c_str()));
framepos_t e (atol (prop_end->value ().c_str()));
set_preserving_all_ranges (s, e);
} else if ((*i)->name() == X_("AutomationView")) {
XMLProperty* prop_id = (*i)->property (X_("id"));
XMLProperty* prop_parameter = (*i)->property (X_("parameter"));
assert (prop_id);
assert (prop_parameter);
PBD::ID id (prop_id->value ());
RouteTimeAxisView* rtv = editor->get_route_view_by_route_id (id);
if (rtv) {
boost::shared_ptr<AutomationTimeAxisView> atv = rtv->automation_child (EventTypeMap::instance().from_symbol (prop_parameter->value ()));
/* the automation could be for an entity that was never saved
in the session file. Don't freak out if we can't find
it.
*/
if (atv) {
add (atv.get());
}
}
} else if ((*i)->name() == X_("Marker")) {
XMLProperty* prop_id = (*i)->property (X_("id"));
XMLProperty* prop_start = (*i)->property (X_("start"));
assert (prop_id);
assert (prop_start);
PBD::ID id (prop_id->value ());
Marker* m = editor->find_marker_from_location_id (id, string_is_affirmative (prop_start->value ()));
if (m) {
add (m);
}
}
}
return 0;
}
void
Selection::remove_regions (TimeAxisView* t)
{
RegionSelection::iterator i = regions.begin();
while (i != regions.end ()) {
RegionSelection::iterator tmp = i;
++tmp;
if (&(*i)->get_time_axis_view() == t) {
remove (*i);
}
i = tmp;
}
}
void
Selection::block_tracks_changed (bool yn)
{
_no_tracks_changed = yn;
}