2 Copyright (C) 2002 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include <sigc++/bind.h>
22 #include "pbd/error.h"
23 #include "pbd/stacktrace.h"
25 #include "ardour/playlist.h"
26 #include "ardour/rc_configuration.h"
28 #include "midi_cut_buffer.h"
29 #include "region_view.h"
30 #include "selection.h"
31 #include "selection_templates.h"
32 #include "time_axis_view.h"
33 #include "automation_time_axis.h"
34 #include "public_editor.h"
39 using namespace ARDOUR;
42 struct AudioRangeComparator {
43 bool operator()(AudioRange a, AudioRange b) {
44 return a.start < b.start;
50 Selection::operator= (const Selection& other)
53 regions = other.regions;
54 tracks = other.tracks;
57 midi_regions = other.midi_regions;
58 midi_notes = other.midi_notes;
65 operator== (const Selection& a, const Selection& b)
67 return a.regions == b.regions &&
68 a.tracks == b.tracks &&
71 a.playlists == b.playlists &&
72 a.midi_notes == b.midi_notes &&
73 a.midi_regions == b.midi_regions;
76 /** Clear everything from the Selection */
87 clear_midi_regions ();
91 Selection::dump_region_layers()
93 cerr << "region selection layer dump" << endl;
94 for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
95 cerr << "layer: " << (int)(*i)->region()->layer() << endl;
101 Selection::clear_regions ()
103 if (!regions.empty()) {
104 regions.clear_all ();
110 Selection::clear_tracks ()
112 if (!tracks.empty()) {
119 Selection::clear_midi_notes ()
121 if (!midi_notes.empty()) {
122 for (MidiNoteSelection::iterator x = midi_notes.begin(); x != midi_notes.end(); ++x) {
131 Selection::clear_midi_regions ()
133 if (!midi_regions.empty()) {
134 midi_regions.clear ();
135 MidiRegionsChanged ();
140 Selection::clear_time ()
148 Selection::clear_playlists ()
150 /* Selections own their playlists */
152 for (PlaylistSelection::iterator i = playlists.begin(); i != playlists.end(); ++i) {
153 /* selections own their own regions, which are copies of the "originals". make them go away */
154 (*i)->drop_regions ();
158 if (!playlists.empty()) {
165 Selection::clear_lines ()
167 if (!lines.empty()) {
174 Selection::clear_markers ()
176 if (!markers.empty()) {
183 Selection::toggle (boost::shared_ptr<Playlist> pl)
185 PlaylistSelection::iterator i;
187 if ((i = find (playlists.begin(), playlists.end(), pl)) == playlists.end()) {
189 playlists.push_back(pl);
198 Selection::toggle (const TrackViewList& track_list)
200 for (TrackViewList::const_iterator i = track_list.begin(); i != track_list.end(); ++i) {
206 Selection::toggle (TimeAxisView* track)
208 TrackSelection::iterator i;
210 if ((i = find (tracks.begin(), tracks.end(), track)) == tracks.end()) {
211 void (Selection::*pmf)(TimeAxisView*) = &Selection::remove;
212 track->GoingAway.connect (*this, boost::bind (pmf, this, track));
213 tracks.push_back (track);
222 Selection::toggle (const MidiNoteSelection& midi_note_list)
224 for (MidiNoteSelection::const_iterator i = midi_note_list.begin(); i != midi_note_list.end(); ++i) {
230 Selection::toggle (MidiCutBuffer* midi)
232 MidiNoteSelection::iterator i;
234 if ((i = find (midi_notes.begin(), midi_notes.end(), midi)) == midi_notes.end()) {
235 midi_notes.push_back (midi);
237 /* remember that we own the MCB */
239 midi_notes.erase (i);
247 Selection::toggle (RegionView* r)
249 RegionSelection::iterator i;
251 if ((i = find (regions.begin(), regions.end(), r)) == regions.end()) {
261 Selection::toggle (MidiRegionView* mrv)
263 MidiRegionSelection::iterator i;
265 if ((i = find (midi_regions.begin(), midi_regions.end(), mrv)) == midi_regions.end()) {
268 midi_regions.erase (i);
271 MidiRegionsChanged ();
275 Selection::toggle (vector<RegionView*>& r)
277 RegionSelection::iterator i;
279 for (vector<RegionView*>::iterator x = r.begin(); x != r.end(); ++x) {
280 if ((i = find (regions.begin(), regions.end(), (*x))) == regions.end()) {
291 Selection::toggle (nframes_t start, nframes_t end)
293 AudioRangeComparator cmp;
295 /* XXX this implementation is incorrect */
297 time.push_back (AudioRange (start, end, next_time_id++));
303 return next_time_id - 1;
307 Selection::add (boost::shared_ptr<Playlist> pl)
309 if (find (playlists.begin(), playlists.end(), pl) == playlists.end()) {
311 playlists.push_back(pl);
317 Selection::add (const list<boost::shared_ptr<Playlist> >& pllist)
319 bool changed = false;
321 for (list<boost::shared_ptr<Playlist> >::const_iterator i = pllist.begin(); i != pllist.end(); ++i) {
322 if (find (playlists.begin(), playlists.end(), (*i)) == playlists.end()) {
324 playlists.push_back (*i);
335 Selection::add (const TrackViewList& track_list)
337 TrackViewList added = tracks.add (track_list);
339 for (list<TimeAxisView*>::const_iterator i = added.begin(); i != added.end(); ++i) {
340 void (Selection::*pmf)(TimeAxisView*) = &Selection::remove;
341 (*i)->GoingAway.connect (*this, boost::bind (pmf, this, (*i)));
344 if (!added.empty()) {
350 Selection::add (TimeAxisView* track)
353 tr.push_back (track);
358 Selection::add (const MidiNoteSelection& midi_list)
360 const MidiNoteSelection::const_iterator b = midi_list.begin();
361 const MidiNoteSelection::const_iterator e = midi_list.end();
363 if (!midi_list.empty()) {
364 midi_notes.insert (midi_notes.end(), b, e);
370 Selection::add (MidiCutBuffer* midi)
372 /* we take ownership of the MCB */
374 if (find (midi_notes.begin(), midi_notes.end(), midi) == midi_notes.end()) {
375 midi_notes.push_back (midi);
381 Selection::add (vector<RegionView*>& v)
383 /* XXX This method or the add (const RegionSelection&) needs to go
386 bool changed = false;
388 for (vector<RegionView*>::iterator i = v.begin(); i != v.end(); ++i) {
389 if (find (regions.begin(), regions.end(), (*i)) == regions.end()) {
390 changed = regions.add ((*i));
391 if (Config->get_link_region_and_track_selection() && changed) {
392 add (&(*i)->get_trackview());
403 Selection::add (const RegionSelection& rs)
405 /* XXX This method or the add (const vector<RegionView*>&) needs to go
408 bool changed = false;
410 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
411 if (find (regions.begin(), regions.end(), (*i)) == regions.end()) {
412 changed = regions.add ((*i));
413 if (Config->get_link_region_and_track_selection() && changed) {
414 add (&(*i)->get_trackview());
425 Selection::add (RegionView* r)
427 if (find (regions.begin(), regions.end(), r) == regions.end()) {
429 if (Config->get_link_region_and_track_selection()) {
430 add (&r->get_trackview());
437 Selection::add (MidiRegionView* mrv)
439 if (find (midi_regions.begin(), midi_regions.end(), mrv) == midi_regions.end()) {
440 midi_regions.push_back (mrv);
441 /* XXX should we do this? */
443 if (Config->get_link_region_and_track_selection()) {
444 add (&mrv->get_trackview());
447 MidiRegionsChanged ();
452 Selection::add (nframes_t start, nframes_t end)
454 AudioRangeComparator cmp;
456 /* XXX this implementation is incorrect */
458 time.push_back (AudioRange (start, end, next_time_id++));
464 return next_time_id - 1;
468 Selection::replace (uint32_t sid, nframes_t start, nframes_t end)
470 for (list<AudioRange>::iterator i = time.begin(); i != time.end(); ++i) {
471 if ((*i).id == sid) {
473 time.push_back (AudioRange(start,end, sid));
475 /* don't consolidate here */
478 AudioRangeComparator cmp;
488 Selection::add (boost::shared_ptr<Evoral::ControlList> cl)
490 boost::shared_ptr<ARDOUR::AutomationList> al
491 = boost::dynamic_pointer_cast<ARDOUR::AutomationList>(cl);
493 warning << "Programming error: Selected list is not an ARDOUR::AutomationList" << endmsg;
497 if (find (lines.begin(), lines.end(), al) == lines.end()) {
498 lines.push_back (al);
504 Selection::remove (TimeAxisView* track)
506 list<TimeAxisView*>::iterator i;
507 if ((i = find (tracks.begin(), tracks.end(), track)) != tracks.end()) {
514 Selection::remove (const TrackViewList& track_list)
516 bool changed = false;
518 for (TrackViewList::const_iterator i = track_list.begin(); i != track_list.end(); ++i) {
520 TrackViewList::iterator x = find (tracks.begin(), tracks.end(), *i);
521 if (x != tracks.end()) {
533 Selection::remove (const MidiNoteSelection& midi_list)
535 bool changed = false;
537 for (MidiNoteSelection::const_iterator i = midi_list.begin(); i != midi_list.end(); ++i) {
539 MidiNoteSelection::iterator x;
541 if ((x = find (midi_notes.begin(), midi_notes.end(), (*i))) != midi_notes.end()) {
542 midi_notes.erase (x);
553 Selection::remove (MidiCutBuffer* midi)
555 MidiNoteSelection::iterator x;
557 if ((x = find (midi_notes.begin(), midi_notes.end(), midi)) != midi_notes.end()) {
558 /* remember that we own the MCB */
560 midi_notes.erase (x);
566 Selection::remove (boost::shared_ptr<Playlist> track)
568 list<boost::shared_ptr<Playlist> >::iterator i;
569 if ((i = find (playlists.begin(), playlists.end(), track)) != playlists.end()) {
576 Selection::remove (const list<boost::shared_ptr<Playlist> >& pllist)
578 bool changed = false;
580 for (list<boost::shared_ptr<Playlist> >::const_iterator i = pllist.begin(); i != pllist.end(); ++i) {
582 list<boost::shared_ptr<Playlist> >::iterator x;
584 if ((x = find (playlists.begin(), playlists.end(), (*i))) != playlists.end()) {
596 Selection::remove (RegionView* r)
598 if (regions.remove (r)) {
602 if (Config->get_link_region_and_track_selection() && !regions.involves (r->get_trackview())) {
603 remove (&r->get_trackview());
608 Selection::remove (MidiRegionView* mrv)
610 MidiRegionSelection::iterator x;
612 if ((x = find (midi_regions.begin(), midi_regions.end(), mrv)) != midi_regions.end()) {
613 midi_regions.erase (x);
614 MidiRegionsChanged ();
618 /* XXX fix this up ? */
619 if (Config->get_link_region_and_track_selection() && !regions.involves (r->get_trackview())) {
620 remove (&r->get_trackview());
627 Selection::remove (uint32_t selection_id)
633 for (list<AudioRange>::iterator i = time.begin(); i != time.end(); ++i) {
634 if ((*i).id == selection_id) {
644 Selection::remove (nframes_t /*start*/, nframes_t /*end*/)
649 Selection::remove (boost::shared_ptr<ARDOUR::AutomationList> ac)
651 AutomationSelection::iterator i;
652 if ((i = find (lines.begin(), lines.end(), ac)) != lines.end()) {
659 Selection::set (TimeAxisView* track)
666 Selection::set (const TrackViewList& track_list)
673 Selection::set (const MidiNoteSelection& midi_list)
680 Selection::set (boost::shared_ptr<Playlist> playlist)
687 Selection::set (const list<boost::shared_ptr<Playlist> >& pllist)
694 Selection::set (const RegionSelection& rs)
698 RegionsChanged(); /* EMIT SIGNAL */
702 Selection::set (MidiRegionView* mrv)
704 clear_midi_regions ();
709 Selection::set (RegionView* r, bool also_clear_tracks)
712 if (also_clear_tracks) {
719 Selection::set (vector<RegionView*>& v)
722 if (Config->get_link_region_and_track_selection()) {
724 // make sure to deselect any automation selections
730 /** Set the start and end time of the time selection, without changing
731 * the list of tracks it applies to.
734 Selection::set (nframes_t start, nframes_t end)
736 if ((start == 0 && end == 0) || end < start) {
741 time.push_back (AudioRange (start, end, next_time_id++));
743 /* reuse the first entry, and remove all the rest */
745 while (time.size() > 1) {
748 time.front().start = start;
749 time.front().end = end;
756 return time.front().id;
760 Selection::set (boost::shared_ptr<Evoral::ControlList> ac)
767 Selection::selected (Marker* m)
769 return find (markers.begin(), markers.end(), m) != markers.end();
773 Selection::selected (TimeAxisView* tv)
775 return find (tracks.begin(), tracks.end(), tv) != tracks.end();
779 Selection::selected (RegionView* rv)
781 return find (regions.begin(), regions.end(), rv) != regions.end();
785 Selection::empty (bool internal_selection)
787 bool object_level_empty = regions.empty () &&
790 playlists.empty () &&
793 playlists.empty () &&
798 if (!internal_selection) {
799 return object_level_empty;
802 /* this is intended to really only apply when using a Selection
806 return object_level_empty && midi_notes.empty();
810 Selection::toggle (const vector<AutomationSelectable*>& autos)
812 for (vector<AutomationSelectable*>::const_iterator x = autos.begin(); x != autos.end(); ++x) {
813 if ((*x)->get_selected()) {
816 points.push_back (**x);
822 PointsChanged (); /* EMIT SIGNAL */
826 Selection::toggle (list<Selectable*>& selectables)
829 AutomationSelectable* as;
830 vector<RegionView*> rvs;
831 vector<AutomationSelectable*> autos;
833 for (std::list<Selectable*>::iterator i = selectables.begin(); i != selectables.end(); ++i) {
834 if ((rv = dynamic_cast<RegionView*> (*i)) != 0) {
836 } else if ((as = dynamic_cast<AutomationSelectable*> (*i)) != 0) {
837 autos.push_back (as);
839 fatal << _("programming error: ")
840 << X_("unknown selectable type passed to Selection::toggle()")
850 if (!autos.empty()) {
856 Selection::set (list<Selectable*>& selectables)
865 Selection::add (list<Selectable*>& selectables)
868 AutomationSelectable* as;
869 vector<RegionView*> rvs;
870 vector<AutomationSelectable*> autos;
872 for (std::list<Selectable*>::iterator i = selectables.begin(); i != selectables.end(); ++i) {
873 if ((rv = dynamic_cast<RegionView*> (*i)) != 0) {
875 } else if ((as = dynamic_cast<AutomationSelectable*> (*i)) != 0) {
876 autos.push_back (as);
878 fatal << _("programming error: ")
879 << X_("unknown selectable type passed to Selection::add()")
889 if (!autos.empty()) {
895 Selection::clear_points ()
897 if (!points.empty()) {
904 Selection::add (vector<AutomationSelectable*>& autos)
906 for (vector<AutomationSelectable*>::iterator i = autos.begin(); i != autos.end(); ++i) {
907 points.push_back (**i);
914 Selection::set (Marker* m)
921 Selection::toggle (Marker* m)
923 MarkerSelection::iterator i;
925 if ((i = find (markers.begin(), markers.end(), m)) == markers.end()) {
933 Selection::remove (Marker* m)
935 MarkerSelection::iterator i;
937 if ((i = find (markers.begin(), markers.end(), m)) != markers.end()) {
944 Selection::add (Marker* m)
946 if (find (markers.begin(), markers.end(), m) == markers.end()) {
948 /* disambiguate which remove() for the compiler */
950 void (Selection::*pmf)(Marker*) = &Selection::remove;
952 m->GoingAway.connect (*this, boost::bind (pmf, this, m));
954 markers.push_back (m);
960 Selection::add (const list<Marker*>& m)
962 markers.insert (markers.end(), m.begin(), m.end());
967 MarkerSelection::range (nframes64_t& s, nframes64_t& e)
972 for (MarkerSelection::iterator i = begin(); i != end(); ++i) {
974 if ((*i)->position() < s) {
975 s = (*i)->position();
978 if ((*i)->position() > e) {
979 e = (*i)->position();