23abfa17c35cbc375529f89e83750d6ed04c10f9
[ardour.git] / gtk2_ardour / selection.cc
1 /*
2     Copyright (C) 2002 Paul Davis
3
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.
8
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.
13
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.
17
18 */
19
20 #include <algorithm>
21 #include <sigc++/bind.h>
22
23 #include "pbd/error.h"
24 #include "pbd/stacktrace.h"
25
26 #include "ardour/playlist.h"
27 #include "ardour/rc_configuration.h"
28
29 #include "control_protocol/control_protocol.h"
30
31 #include "audio_region_view.h"
32 #include "debug.h"
33 #include "gui_thread.h"
34 #include "midi_cut_buffer.h"
35 #include "region_gain_line.h"
36 #include "region_view.h"
37 #include "selection.h"
38 #include "selection_templates.h"
39 #include "time_axis_view.h"
40 #include "automation_time_axis.h"
41 #include "public_editor.h"
42 #include "control_point.h"
43 #include "vca_time_axis.h"
44
45 #include "pbd/i18n.h"
46
47 using namespace std;
48 using namespace ARDOUR;
49 using namespace PBD;
50
51 struct AudioRangeComparator {
52         bool operator()(AudioRange a, AudioRange b) {
53                 return a.start < b.start;
54         }
55 };
56
57 Selection::Selection (const PublicEditor* e)
58         : tracks (e)
59         , editor (e)
60         , next_time_id (0)
61 {
62         clear ();
63
64         /* we have disambiguate which remove() for the compiler */
65
66         void (Selection::*track_remove)(TimeAxisView*) = &Selection::remove;
67         TimeAxisView::CatchDeletion.connect (*this, MISSING_INVALIDATOR, boost::bind (track_remove, this, _1), gui_context());
68
69         void (Selection::*marker_remove)(ArdourMarker*) = &Selection::remove;
70         ArdourMarker::CatchDeletion.connect (*this, MISSING_INVALIDATOR, boost::bind (marker_remove, this, _1), gui_context());
71
72         void (Selection::*point_remove)(ControlPoint*) = &Selection::remove;
73         ControlPoint::CatchDeletion.connect (*this, MISSING_INVALIDATOR, boost::bind (point_remove, this, _1), gui_context());
74 }
75
76 #if 0
77 Selection&
78 Selection::operator= (const Selection& other)
79 {
80         if (&other != this) {
81                 regions = other.regions;
82                 tracks = other.tracks;
83                 time = other.time;
84                 lines = other.lines;
85                 midi_regions = other.midi_regions;
86                 midi_notes = other.midi_notes;
87         }
88         return *this;
89 }
90 #endif
91
92 bool
93 operator== (const Selection& a, const Selection& b)
94 {
95         return a.regions == b.regions &&
96                 a.tracks == b.tracks &&
97                 a.time == b.time &&
98                 a.lines == b.lines &&
99                 a.playlists == b.playlists &&
100                 a.midi_notes == b.midi_notes &&
101                 a.midi_regions == b.midi_regions;
102 }
103
104 /** Clear everything from the Selection */
105 void
106 Selection::clear ()
107 {
108         clear_tracks ();
109         clear_regions ();
110         clear_points ();
111         clear_lines();
112         clear_time ();
113         clear_playlists ();
114         clear_midi_notes ();
115         clear_midi_regions ();
116         clear_markers ();
117         pending_midi_note_selection.clear();
118 }
119
120 void
121 Selection::clear_objects (bool with_signal)
122 {
123         clear_regions (with_signal);
124         clear_points (with_signal);
125         clear_lines(with_signal);
126         clear_playlists (with_signal);
127         clear_midi_notes (with_signal);
128         clear_midi_regions (with_signal);
129 }
130
131 void
132 Selection::clear_tracks (bool with_signal)
133 {
134         if (!tracks.empty()) {
135                 PresentationInfo::ChangeSuspender cs;
136
137                 for (TrackViewList::iterator x = tracks.begin(); x != tracks.end(); ++x) {
138                         (*x)->set_selected (false);
139                 }
140
141                 tracks.clear ();
142         }
143 }
144
145 void
146 Selection::clear_time (bool with_signal)
147 {
148         time.clear();
149         if (with_signal) {
150                 TimeChanged ();
151         }
152 }
153
154 void
155 Selection::dump_region_layers()
156 {
157         cerr << "region selection layer dump" << endl;
158         for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
159                 cerr << "layer: " << (int)(*i)->region()->layer() << endl;
160         }
161 }
162
163
164 void
165 Selection::clear_regions (bool with_signal)
166 {
167         if (!regions.empty()) {
168                 regions.clear_all ();
169                 if (with_signal) {
170                         RegionsChanged();
171                 }
172         }
173 }
174
175 void
176 Selection::clear_midi_notes (bool with_signal)
177 {
178         if (!midi_notes.empty()) {
179                 for (MidiNoteSelection::iterator x = midi_notes.begin(); x != midi_notes.end(); ++x) {
180                         delete *x;
181                 }
182                 midi_notes.clear ();
183                 if (with_signal) {
184                         MidiNotesChanged ();
185                 }
186         }
187
188         // clear note selections for MRV's that have note selections
189         // this will cause the MRV to be removed from the list
190         for (MidiRegionSelection::iterator i = midi_regions.begin();
191              i != midi_regions.end();) {
192                 MidiRegionSelection::iterator tmp = i;
193                 ++tmp;
194                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
195                 if (mrv) {
196                         mrv->clear_selection();
197                 }
198                 i = tmp;
199         }
200 }
201
202 void
203 Selection::clear_midi_regions (bool with_signal)
204 {
205         if (!midi_regions.empty()) {
206                 midi_regions.clear ();
207                 if (with_signal) {
208                         MidiRegionsChanged ();
209                 }
210         }
211 }
212
213 void
214 Selection::clear_playlists (bool with_signal)
215 {
216         /* Selections own their playlists */
217
218         for (PlaylistSelection::iterator i = playlists.begin(); i != playlists.end(); ++i) {
219                 /* selections own their own regions, which are copies of the "originals". make them go away */
220                 (*i)->drop_regions ();
221                 (*i)->release ();
222         }
223
224         if (!playlists.empty()) {
225                 playlists.clear ();
226                 if (with_signal) {
227                         PlaylistsChanged();
228                 }
229         }
230 }
231
232 void
233 Selection::clear_lines (bool with_signal)
234 {
235         if (!lines.empty()) {
236                 lines.clear ();
237                 if (with_signal) {
238                         LinesChanged();
239                 }
240         }
241 }
242
243 void
244 Selection::clear_markers (bool with_signal)
245 {
246         if (!markers.empty()) {
247                 markers.clear ();
248                 if (with_signal) {
249                         MarkersChanged();
250                 }
251         }
252 }
253
254 void
255 Selection::toggle (boost::shared_ptr<Playlist> pl)
256 {
257         clear_time();  //enforce object/range exclusivity
258         clear_tracks();  //enforce object/track exclusivity
259
260         PlaylistSelection::iterator i;
261
262         if ((i = find (playlists.begin(), playlists.end(), pl)) == playlists.end()) {
263                 pl->use ();
264                 playlists.push_back(pl);
265         } else {
266                 playlists.erase (i);
267         }
268
269         PlaylistsChanged ();
270 }
271
272 void
273 Selection::toggle (const TrackViewList& track_list)
274 {
275         PresentationInfo::ChangeSuspender cs;
276
277         for (TrackViewList::const_iterator i = track_list.begin(); i != track_list.end(); ++i) {
278                 if (dynamic_cast<VCATimeAxisView*> (*i)) {
279                         continue;
280                 }
281                 toggle ((*i));
282         }
283 }
284
285 void
286 Selection::toggle (TimeAxisView* track)
287 {
288         if (dynamic_cast<VCATimeAxisView*> (track)) {
289                 return;
290         }
291
292         TrackSelection::iterator i;
293
294         if ((i = find (tracks.begin(), tracks.end(), track)) == tracks.end()) {
295                 tracks.push_back (track);
296                 track->set_selected (true);
297         } else {
298                 tracks.erase (i);
299                 track->set_selected (false);
300         }
301
302 }
303
304 void
305 Selection::toggle (const MidiNoteSelection& midi_note_list)
306 {
307         clear_time();  //enforce object/range exclusivity
308         clear_tracks();  //enforce object/track exclusivity
309
310         for (MidiNoteSelection::const_iterator i = midi_note_list.begin(); i != midi_note_list.end(); ++i) {
311                 toggle ((*i));
312         }
313 }
314
315 void
316 Selection::toggle (MidiCutBuffer* midi)
317 {
318         MidiNoteSelection::iterator i;
319
320         if ((i = find (midi_notes.begin(), midi_notes.end(), midi)) == midi_notes.end()) {
321                 midi_notes.push_back (midi);
322         } else {
323                 /* remember that we own the MCB */
324                 delete *i;
325                 midi_notes.erase (i);
326         }
327
328         MidiNotesChanged();
329 }
330
331
332 void
333 Selection::toggle (RegionView* r)
334 {
335         clear_time();  //enforce object/range exclusivity
336         clear_tracks();  //enforce object/track exclusivity
337
338         RegionSelection::iterator i;
339
340         if ((i = find (regions.begin(), regions.end(), r)) == regions.end()) {
341                 add (r);
342         } else {
343                 remove (*i);
344         }
345
346         RegionsChanged ();
347 }
348
349 void
350 Selection::toggle (MidiRegionView* mrv)
351 {
352         clear_time();   //enforce object/range exclusivity
353         clear_tracks();  //enforce object/track exclusivity
354
355         MidiRegionSelection::iterator i;
356
357         if ((i = find (midi_regions.begin(), midi_regions.end(), mrv)) == midi_regions.end()) {
358                 add (mrv);
359         } else {
360                 midi_regions.erase (i);
361         }
362
363         MidiRegionsChanged ();
364 }
365
366 void
367 Selection::toggle (vector<RegionView*>& r)
368 {
369         clear_time();  //enforce object/range exclusivity
370         clear_tracks();  //enforce object/track exclusivity
371
372         RegionSelection::iterator i;
373
374         for (vector<RegionView*>::iterator x = r.begin(); x != r.end(); ++x) {
375                 if ((i = find (regions.begin(), regions.end(), (*x))) == regions.end()) {
376                         add ((*x));
377                 } else {
378                         remove (*x);
379                 }
380         }
381
382         RegionsChanged ();
383 }
384
385 long
386 Selection::toggle (framepos_t start, framepos_t end)
387 {
388         clear_objects();  //enforce object/range exclusivity
389
390         AudioRangeComparator cmp;
391
392         /* XXX this implementation is incorrect */
393
394         time.push_back (AudioRange (start, end, ++next_time_id));
395         time.consolidate ();
396         time.sort (cmp);
397
398         TimeChanged ();
399
400         return next_time_id;
401 }
402
403 void
404 Selection::add (boost::shared_ptr<Playlist> pl)
405 {
406         clear_time();  //enforce object/range exclusivity
407         clear_tracks();  //enforce object/track exclusivity
408
409         if (find (playlists.begin(), playlists.end(), pl) == playlists.end()) {
410                 pl->use ();
411                 playlists.push_back(pl);
412                 PlaylistsChanged ();
413         }
414 }
415
416 void
417 Selection::add (const list<boost::shared_ptr<Playlist> >& pllist)
418 {
419         clear_time();  //enforce object/range exclusivity
420         clear_tracks();  //enforce object/track exclusivity
421
422         bool changed = false;
423
424         for (list<boost::shared_ptr<Playlist> >::const_iterator i = pllist.begin(); i != pllist.end(); ++i) {
425                 if (find (playlists.begin(), playlists.end(), (*i)) == playlists.end()) {
426                         (*i)->use ();
427                         playlists.push_back (*i);
428                         changed = true;
429                 }
430         }
431
432         if (changed) {
433                 PlaylistsChanged ();
434         }
435 }
436
437 void
438 Selection::add (TrackViewList const & track_list)
439 {
440         clear_objects();  //enforce object/range exclusivity
441
442         PresentationInfo::ChangeSuspender cs;
443
444         TrackViewList added = tracks.add (track_list);
445
446         if (!added.empty()) {
447                 for (TrackViewList::iterator x = added.begin(); x != added.end(); ++x) {
448                         if (dynamic_cast<VCATimeAxisView*> (*x)) {
449                                 continue;
450                         }
451                         (*x)->set_selected (true);
452                 }
453         }
454 }
455
456 void
457 Selection::add (TimeAxisView* track)
458 {
459         if (dynamic_cast<VCATimeAxisView*> (track)) {
460                 return;
461         }
462
463         TrackViewList tr;
464         tr.push_back (track);
465         add (tr);
466 }
467
468 void
469 Selection::add (const MidiNoteSelection& midi_list)
470 {
471         clear_time();  //enforce object/range exclusivity
472         clear_tracks();  //enforce object/track exclusivity
473
474         const MidiNoteSelection::const_iterator b = midi_list.begin();
475         const MidiNoteSelection::const_iterator e = midi_list.end();
476
477         if (!midi_list.empty()) {
478                 midi_notes.insert (midi_notes.end(), b, e);
479                 MidiNotesChanged ();
480         }
481 }
482
483 void
484 Selection::add (MidiCutBuffer* midi)
485 {
486         /* we take ownership of the MCB */
487
488         if (find (midi_notes.begin(), midi_notes.end(), midi) == midi_notes.end()) {
489                 midi_notes.push_back (midi);
490                 MidiNotesChanged ();
491         }
492 }
493
494 void
495 Selection::add (vector<RegionView*>& v)
496 {
497         clear_time();  //enforce object/range exclusivity
498         clear_tracks();  //enforce object/track exclusivity
499
500         /* XXX This method or the add (const RegionSelection&) needs to go
501          */
502
503         bool changed = false;
504
505         for (vector<RegionView*>::iterator i = v.begin(); i != v.end(); ++i) {
506                 if (find (regions.begin(), regions.end(), (*i)) == regions.end()) {
507                         changed = regions.add ((*i));
508                 }
509         }
510
511         if (changed) {
512                 RegionsChanged ();
513         }
514 }
515
516 void
517 Selection::add (const RegionSelection& rs)
518 {
519         clear_time();  //enforce object/range exclusivity
520         clear_tracks();  //enforce object/track exclusivity
521
522         /* XXX This method or the add (const vector<RegionView*>&) needs to go
523          */
524
525         bool changed = false;
526
527         for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
528                 if (find (regions.begin(), regions.end(), (*i)) == regions.end()) {
529                         changed = regions.add ((*i));
530                 }
531         }
532
533         if (changed) {
534                 RegionsChanged ();
535         }
536 }
537
538 void
539 Selection::add (RegionView* r)
540 {
541         clear_time();  //enforce object/range exclusivity
542         clear_tracks();  //enforce object/track exclusivity
543
544         if (find (regions.begin(), regions.end(), r) == regions.end()) {
545                 bool changed = regions.add (r);
546                 if (changed) {
547                         RegionsChanged ();
548                 }
549         }
550 }
551
552 void
553 Selection::add (MidiRegionView* mrv)
554 {
555         DEBUG_TRACE(DEBUG::Selection, string_compose("Selection::add MRV %1\n", mrv));
556
557         clear_time();  //enforce object/range exclusivity
558         clear_tracks();  //enforce object/track exclusivity
559
560         if (find (midi_regions.begin(), midi_regions.end(), mrv) == midi_regions.end()) {
561                 midi_regions.push_back (mrv);
562                 /* XXX should we do this? */
563                 MidiRegionsChanged ();
564         }
565 }
566
567 long
568 Selection::add (framepos_t start, framepos_t end)
569 {
570         clear_objects();  //enforce object/range exclusivity
571
572         AudioRangeComparator cmp;
573
574         /* XXX this implementation is incorrect */
575
576         time.push_back (AudioRange (start, end, ++next_time_id));
577         time.consolidate ();
578         time.sort (cmp);
579
580         TimeChanged ();
581
582         return next_time_id;
583 }
584
585 void
586 Selection::move_time (framecnt_t distance)
587 {
588         if (distance == 0) {
589                 return;
590         }
591
592         for (list<AudioRange>::iterator i = time.begin(); i != time.end(); ++i) {
593                 (*i).start += distance;
594                 (*i).end += distance;
595         }
596
597         TimeChanged ();
598 }
599
600 void
601 Selection::replace (uint32_t sid, framepos_t start, framepos_t end)
602 {
603         clear_objects();  //enforce object/range exclusivity
604
605         for (list<AudioRange>::iterator i = time.begin(); i != time.end(); ++i) {
606                 if ((*i).id == sid) {
607                         time.erase (i);
608                         time.push_back (AudioRange(start,end, sid));
609
610                         /* don't consolidate here */
611
612
613                         AudioRangeComparator cmp;
614                         time.sort (cmp);
615
616                         TimeChanged ();
617                         break;
618                 }
619         }
620 }
621
622 void
623 Selection::add (boost::shared_ptr<Evoral::ControlList> cl)
624 {
625         clear_time();  //enforce object/range exclusivity
626         clear_tracks();  //enforce object/track exclusivity
627
628         boost::shared_ptr<ARDOUR::AutomationList> al
629                 = boost::dynamic_pointer_cast<ARDOUR::AutomationList>(cl);
630         if (!al) {
631                 warning << "Programming error: Selected list is not an ARDOUR::AutomationList" << endmsg;
632                 return;
633         }
634
635         /* The original may change so we must store a copy (not a pointer) here.
636          * e.g AutomationLine rewrites the list with gain mapping.
637          * the downside is that we can't perfom duplicate checks.
638          * This code was changed in response to #6842
639          */
640         lines.push_back (boost::shared_ptr<ARDOUR::AutomationList> (new ARDOUR::AutomationList(*al)));
641         LinesChanged();
642 }
643
644 void
645 Selection::remove (TimeAxisView* track)
646 {
647         list<TimeAxisView*>::iterator i;
648         if ((i = find (tracks.begin(), tracks.end(), track)) != tracks.end()) {
649                 /* erase first, because set_selected() will remove the track
650                    from the selection, invalidating the iterator.
651
652                    In fact, we don't really even need to do the erase, but this is
653                    a hangover of axis view selection being in the GUI.
654                 */
655                 tracks.erase (i);
656                 track->set_selected (false);
657         }
658 }
659
660 void
661 Selection::remove (const TrackViewList& track_list)
662 {
663         PresentationInfo::ChangeSuspender cs;
664
665         for (TrackViewList::const_iterator i = track_list.begin(); i != track_list.end(); ++i) {
666
667                 TrackViewList::iterator x = find (tracks.begin(), tracks.end(), *i);
668
669                 if (x != tracks.end()) {
670                         tracks.erase (x);
671                         (*i)->set_selected (false);
672                 }
673         }
674 }
675
676 void
677 Selection::remove (ControlPoint* p)
678 {
679         PointSelection::iterator i = find (points.begin(), points.end(), p);
680         if (i != points.end ()) {
681                 points.erase (i);
682         }
683 }
684
685 void
686 Selection::remove (const MidiNoteSelection& midi_list)
687 {
688         bool changed = false;
689
690         for (MidiNoteSelection::const_iterator i = midi_list.begin(); i != midi_list.end(); ++i) {
691
692                 MidiNoteSelection::iterator x;
693
694                 if ((x = find (midi_notes.begin(), midi_notes.end(), (*i))) != midi_notes.end()) {
695                         midi_notes.erase (x);
696                         changed = true;
697                 }
698         }
699
700         if (changed) {
701                 MidiNotesChanged();
702         }
703 }
704
705 void
706 Selection::remove (MidiCutBuffer* midi)
707 {
708         MidiNoteSelection::iterator x;
709
710         if ((x = find (midi_notes.begin(), midi_notes.end(), midi)) != midi_notes.end()) {
711                 /* remember that we own the MCB */
712                 delete *x;
713                 midi_notes.erase (x);
714                 MidiNotesChanged ();
715         }
716 }
717
718 void
719 Selection::remove (boost::shared_ptr<Playlist> track)
720 {
721         list<boost::shared_ptr<Playlist> >::iterator i;
722         if ((i = find (playlists.begin(), playlists.end(), track)) != playlists.end()) {
723                 playlists.erase (i);
724                 PlaylistsChanged();
725         }
726 }
727
728 void
729 Selection::remove (const list<boost::shared_ptr<Playlist> >& pllist)
730 {
731         bool changed = false;
732
733         for (list<boost::shared_ptr<Playlist> >::const_iterator i = pllist.begin(); i != pllist.end(); ++i) {
734
735                 list<boost::shared_ptr<Playlist> >::iterator x;
736
737                 if ((x = find (playlists.begin(), playlists.end(), (*i))) != playlists.end()) {
738                         playlists.erase (x);
739                         changed = true;
740                 }
741         }
742
743         if (changed) {
744                 PlaylistsChanged();
745         }
746 }
747
748 void
749 Selection::remove (RegionView* r)
750 {
751         if (regions.remove (r)) {
752                 RegionsChanged ();
753         }
754 }
755
756 void
757 Selection::remove (MidiRegionView* mrv)
758 {
759         DEBUG_TRACE(DEBUG::Selection, string_compose("Selection::remove MRV %1\n", mrv));
760
761         MidiRegionSelection::iterator x;
762
763         if ((x = find (midi_regions.begin(), midi_regions.end(), mrv)) != midi_regions.end()) {
764                 midi_regions.erase (x);
765                 MidiRegionsChanged ();
766         }
767 }
768
769 void
770 Selection::remove (uint32_t selection_id)
771 {
772         if (time.empty()) {
773                 return;
774         }
775
776         for (list<AudioRange>::iterator i = time.begin(); i != time.end(); ++i) {
777                 if ((*i).id == selection_id) {
778                         time.erase (i);
779
780                         TimeChanged ();
781                         break;
782                 }
783         }
784 }
785
786 void
787 Selection::remove (framepos_t /*start*/, framepos_t /*end*/)
788 {
789 }
790
791 void
792 Selection::remove (boost::shared_ptr<ARDOUR::AutomationList> ac)
793 {
794         AutomationSelection::iterator i;
795         if ((i = find (lines.begin(), lines.end(), ac)) != lines.end()) {
796                 lines.erase (i);
797                 LinesChanged();
798         }
799 }
800
801 void
802 Selection::set (TimeAxisView* track)
803 {
804         if (dynamic_cast<VCATimeAxisView*> (track)) {
805                 return;
806         }
807         clear_objects ();  //enforce object/range exclusivity
808
809         PresentationInfo::ChangeSuspender cs;
810
811         if (!tracks.empty()) {
812
813                 if (tracks.size() == 1 && tracks.front() == track) {
814                         /* already single selection: nothing to do */
815                         return;
816                 }
817
818                 for (TrackViewList::iterator x = tracks.begin(); x != tracks.end(); ++x) {
819                         (*x)->set_selected (false);
820                 }
821
822                 tracks.clear ();
823         }
824
825         add (track);
826 }
827
828 void
829 Selection::set (const TrackViewList& track_list)
830 {
831         clear_objects();  //enforce object/range exclusivity
832
833         PresentationInfo::ChangeSuspender cs;
834
835         if (!tracks.empty()) {
836
837                 /* cannot use set<T>::operator== (set<T> const &) here, because
838                  * apparently the ordering used within 2 sets is not
839                  * necessarily the same.
840                  */
841
842                 if (tracks.size() == track_list.size()) {
843                         bool missing = false;
844
845                         for (TrackViewList::const_iterator x = track_list.begin(); x != track_list.end(); ++x) {
846                                 if (dynamic_cast<VCATimeAxisView*> (*x)) {
847                                         continue;
848                                 }
849                                 if (find (tracks.begin(), tracks.end(), *x) == tracks.end()) {
850                                         missing = true;
851                                 }
852                         }
853
854                         if (!missing) {
855                                 /* already same selection: nothing to do */
856                                 return;
857                         }
858                 }
859
860                 /* argument is different from existing selection */
861
862                 for (TrackViewList::iterator x = tracks.begin(); x != tracks.end(); ++x) {
863                         if (dynamic_cast<VCATimeAxisView*> (*x)) {
864                                 continue;
865                         }
866                         (*x)->set_selected (false);
867                 }
868
869                 tracks.clear ();
870         }
871
872         add (track_list);
873 }
874
875 void
876 Selection::set (const MidiNoteSelection& midi_list)
877 {
878         clear_time ();  //enforce region/object exclusivity
879         clear_tracks();  //enforce object/track exclusivity
880         clear_objects ();
881         add (midi_list);
882 }
883
884 void
885 Selection::set (boost::shared_ptr<Playlist> playlist)
886 {
887         clear_time ();  //enforce region/object exclusivity
888         clear_tracks();  //enforce object/track exclusivity
889         clear_objects ();
890         add (playlist);
891 }
892
893 void
894 Selection::set (const list<boost::shared_ptr<Playlist> >& pllist)
895 {
896         clear_time();  //enforce region/object exclusivity
897         clear_objects ();
898         add (pllist);
899 }
900
901 void
902 Selection::set (const RegionSelection& rs)
903 {
904         clear_time();  //enforce region/object exclusivity
905         clear_tracks();  //enforce object/track exclusivity
906         clear_objects();
907         regions = rs;
908         RegionsChanged(); /* EMIT SIGNAL */
909 }
910
911 void
912 Selection::set (MidiRegionView* mrv)
913 {
914         clear_time();  //enforce region/object exclusivity
915         clear_tracks();  //enforce object/track exclusivity
916         clear_objects ();
917         add (mrv);
918 }
919
920 void
921 Selection::set (RegionView* r, bool /*also_clear_tracks*/)
922 {
923         clear_time();  //enforce region/object exclusivity
924         clear_tracks();  //enforce object/track exclusivity
925         clear_objects ();
926         add (r);
927 }
928
929 void
930 Selection::set (vector<RegionView*>& v)
931 {
932         clear_time();  //enforce region/object exclusivity
933         clear_tracks();  //enforce object/track exclusivity
934         clear_objects();
935
936         add (v);
937 }
938
939 /** Set the start and end time of the time selection, without changing
940  *  the list of tracks it applies to.
941  */
942 long
943 Selection::set (framepos_t start, framepos_t end)
944 {
945         clear_objects();  //enforce region/object exclusivity
946         clear_time();
947
948         if ((start == 0 && end == 0) || end < start) {
949                 return 0;
950         }
951
952         if (time.empty()) {
953                 time.push_back (AudioRange (start, end, ++next_time_id));
954         } else {
955                 /* reuse the first entry, and remove all the rest */
956
957                 while (time.size() > 1) {
958                         time.pop_front();
959                 }
960                 time.front().start = start;
961                 time.front().end = end;
962         }
963
964         time.consolidate ();
965
966         TimeChanged ();
967
968         return time.front().id;
969 }
970
971 /** Set the start and end of the range selection.  If more than one range
972  *  is currently selected, the start of the earliest range and the end of the
973  *  latest range are set.  If no range is currently selected, this method
974  *  selects a single range from start to end.
975  *
976  *  @param start New start time.
977  *  @param end New end time.
978  */
979 void
980 Selection::set_preserving_all_ranges (framepos_t start, framepos_t end)
981 {
982         clear_objects();  //enforce region/object exclusivity
983
984         if ((start == 0 && end == 0) || (end < start)) {
985                 return;
986         }
987
988         if (time.empty ()) {
989                 time.push_back (AudioRange (start, end, ++next_time_id));
990         } else {
991                 time.sort (AudioRangeComparator ());
992                 time.front().start = start;
993                 time.back().end = end;
994         }
995
996         time.consolidate ();
997
998         TimeChanged ();
999 }
1000
1001 void
1002 Selection::set (boost::shared_ptr<Evoral::ControlList> ac)
1003 {
1004         clear_time();  //enforce region/object exclusivity
1005         clear_tracks();  //enforce object/track exclusivity
1006         clear_objects();
1007
1008         add (ac);
1009 }
1010
1011 bool
1012 Selection::selected (ArdourMarker* m) const
1013 {
1014         return find (markers.begin(), markers.end(), m) != markers.end();
1015 }
1016
1017 bool
1018 Selection::selected (TimeAxisView* tv) const
1019 {
1020         return tv->selected ();
1021 }
1022
1023 bool
1024 Selection::selected (RegionView* rv) const
1025 {
1026         return find (regions.begin(), regions.end(), rv) != regions.end();
1027 }
1028
1029 bool
1030 Selection::selected (ControlPoint* cp) const
1031 {
1032         return find (points.begin(), points.end(), cp) != points.end();
1033 }
1034
1035 bool
1036 Selection::empty (bool internal_selection)
1037 {
1038         bool object_level_empty =  regions.empty () &&
1039                 tracks.empty () &&
1040                 points.empty () &&
1041                 playlists.empty () &&
1042                 lines.empty () &&
1043                 time.empty () &&
1044                 playlists.empty () &&
1045                 markers.empty() &&
1046                 midi_regions.empty()
1047                 ;
1048
1049         if (!internal_selection) {
1050                 return object_level_empty;
1051         }
1052
1053         /* this is intended to really only apply when using a Selection
1054            as a cut buffer.
1055         */
1056
1057         return object_level_empty && midi_notes.empty() && points.empty();
1058 }
1059
1060 void
1061 Selection::toggle (ControlPoint* cp)
1062 {
1063         clear_time();  //enforce region/object exclusivity
1064         clear_tracks();  //enforce object/track exclusivity
1065
1066         cp->set_selected (!cp->selected ());
1067         PointSelection::iterator i = find (points.begin(), points.end(), cp);
1068         if (i == points.end()) {
1069                 points.push_back (cp);
1070         } else {
1071                 points.erase (i);
1072         }
1073
1074         PointsChanged (); /* EMIT SIGNAL */
1075 }
1076
1077 void
1078 Selection::toggle (vector<ControlPoint*> const & cps)
1079 {
1080         clear_time();  //enforce region/object exclusivity
1081         clear_tracks();  //enforce object/track exclusivity
1082
1083         for (vector<ControlPoint*>::const_iterator i = cps.begin(); i != cps.end(); ++i) {
1084                 toggle (*i);
1085         }
1086 }
1087
1088 void
1089 Selection::toggle (list<Selectable*> const & selectables)
1090 {
1091         clear_time();  //enforce region/object exclusivity
1092         clear_tracks();  //enforce object/track exclusivity
1093
1094         RegionView* rv;
1095         ControlPoint* cp;
1096         vector<RegionView*> rvs;
1097         vector<ControlPoint*> cps;
1098
1099         for (std::list<Selectable*>::const_iterator i = selectables.begin(); i != selectables.end(); ++i) {
1100                 if ((rv = dynamic_cast<RegionView*> (*i)) != 0) {
1101                         rvs.push_back (rv);
1102                 } else if ((cp = dynamic_cast<ControlPoint*> (*i)) != 0) {
1103                         cps.push_back (cp);
1104                 } else {
1105                         fatal << _("programming error: ")
1106                               << X_("unknown selectable type passed to Selection::toggle()")
1107                               << endmsg;
1108                         abort(); /*NOTREACHED*/
1109                 }
1110         }
1111
1112         if (!rvs.empty()) {
1113                 toggle (rvs);
1114         }
1115
1116         if (!cps.empty()) {
1117                 toggle (cps);
1118         }
1119 }
1120
1121 void
1122 Selection::set (list<Selectable*> const & selectables)
1123 {
1124         clear_time ();  //enforce region/object exclusivity
1125         clear_tracks();  //enforce object/track exclusivity
1126         clear_objects ();
1127
1128         add (selectables);
1129 }
1130
1131 void
1132 Selection::add (PointSelection const & s)
1133 {
1134         clear_time ();  //enforce region/object exclusivity
1135         clear_tracks();  //enforce object/track exclusivity
1136
1137         for (PointSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
1138                 points.push_back (*i);
1139         }
1140 }
1141
1142 void
1143 Selection::add (list<Selectable*> const & selectables)
1144 {
1145         clear_time ();  //enforce region/object exclusivity
1146         clear_tracks();  //enforce object/track exclusivity
1147
1148         RegionView* rv;
1149         ControlPoint* cp;
1150         vector<RegionView*> rvs;
1151         vector<ControlPoint*> cps;
1152
1153         for (std::list<Selectable*>::const_iterator i = selectables.begin(); i != selectables.end(); ++i) {
1154                 if ((rv = dynamic_cast<RegionView*> (*i)) != 0) {
1155                         rvs.push_back (rv);
1156                 } else if ((cp = dynamic_cast<ControlPoint*> (*i)) != 0) {
1157                         cps.push_back (cp);
1158                 } else {
1159                         fatal << _("programming error: ")
1160                               << X_("unknown selectable type passed to Selection::add()")
1161                               << endmsg;
1162                         abort(); /*NOTREACHED*/
1163                 }
1164         }
1165
1166         if (!rvs.empty()) {
1167                 add (rvs);
1168         }
1169
1170         if (!cps.empty()) {
1171                 add (cps);
1172         }
1173 }
1174
1175 void
1176 Selection::clear_points (bool with_signal)
1177 {
1178         if (!points.empty()) {
1179                 points.clear ();
1180                 if (with_signal) {
1181                         PointsChanged ();
1182                 }
1183         }
1184 }
1185
1186 void
1187 Selection::add (ControlPoint* cp)
1188 {
1189         clear_time ();  //enforce region/object exclusivity
1190         clear_tracks();  //enforce object/track exclusivity
1191
1192         cp->set_selected (true);
1193         points.push_back (cp);
1194         PointsChanged (); /* EMIT SIGNAL */
1195 }
1196
1197 void
1198 Selection::add (vector<ControlPoint*> const & cps)
1199 {
1200         clear_time ();  //enforce region/object exclusivity
1201         clear_tracks();  //enforce object/track exclusivity
1202
1203         for (vector<ControlPoint*>::const_iterator i = cps.begin(); i != cps.end(); ++i) {
1204                 (*i)->set_selected (true);
1205                 points.push_back (*i);
1206         }
1207         PointsChanged (); /* EMIT SIGNAL */
1208 }
1209
1210 void
1211 Selection::set (ControlPoint* cp)
1212 {
1213         clear_time ();  //enforce region/object exclusivity
1214         clear_tracks();  //enforce object/track exclusivity
1215
1216         if (cp->selected () && points.size () == 1) {
1217                 return;
1218         }
1219
1220         for (uint32_t i = 0; i < cp->line().npoints(); ++i) {
1221                 cp->line().nth (i)->set_selected (false);
1222         }
1223
1224         clear_objects ();
1225         add (cp);
1226 }
1227
1228 void
1229 Selection::set (ArdourMarker* m)
1230 {
1231         clear_time ();  //enforce region/object exclusivity
1232         clear_tracks();  //enforce object/track exclusivity
1233         markers.clear ();
1234
1235         add (m);
1236 }
1237
1238 void
1239 Selection::toggle (ArdourMarker* m)
1240 {
1241         MarkerSelection::iterator i;
1242
1243         if ((i = find (markers.begin(), markers.end(), m)) == markers.end()) {
1244                 add (m);
1245         } else {
1246                 remove (m);
1247         }
1248 }
1249
1250 void
1251 Selection::remove (ArdourMarker* m)
1252 {
1253         MarkerSelection::iterator i;
1254
1255         if ((i = find (markers.begin(), markers.end(), m)) != markers.end()) {
1256                 markers.erase (i);
1257                 MarkersChanged();
1258         }
1259 }
1260
1261 void
1262 Selection::add (ArdourMarker* m)
1263 {
1264         clear_time ();  //enforce region/object exclusivity
1265         clear_tracks();  //enforce object/track exclusivity
1266
1267         if (find (markers.begin(), markers.end(), m) == markers.end()) {
1268                 markers.push_back (m);
1269                 MarkersChanged();
1270         }
1271 }
1272
1273 void
1274 Selection::add (const list<ArdourMarker*>& m)
1275 {
1276         clear_time ();  //enforce region/object exclusivity
1277         clear_tracks();  //enforce object/track exclusivity
1278
1279         markers.insert (markers.end(), m.begin(), m.end());
1280         markers.sort ();
1281         markers.unique ();
1282
1283         MarkersChanged ();
1284 }
1285
1286 void
1287 MarkerSelection::range (framepos_t& s, framepos_t& e)
1288 {
1289         s = max_framepos;
1290         e = 0;
1291
1292         for (MarkerSelection::iterator i = begin(); i != end(); ++i) {
1293
1294                 if ((*i)->position() < s) {
1295                         s = (*i)->position();
1296                 }
1297
1298                 if ((*i)->position() > e) {
1299                         e = (*i)->position();
1300                 }
1301         }
1302
1303         s = std::min (s, e);
1304         e = std::max (s, e);
1305 }
1306
1307 XMLNode&
1308 Selection::get_state () const
1309 {
1310         /* XXX: not complete; just sufficient to get track selection state
1311            so that re-opening plugin windows for editor mixer strips works
1312         */
1313
1314         char buf[32];
1315         XMLNode* node = new XMLNode (X_("Selection"));
1316
1317         for (TrackSelection::const_iterator i = tracks.begin(); i != tracks.end(); ++i) {
1318                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
1319                 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (*i);
1320                 if (rtv) {
1321                         XMLNode* t = node->add_child (X_("RouteView"));
1322                         t->add_property (X_("id"), atoi (rtv->route()->id().to_s().c_str()));
1323                 } else if (atv) {
1324                         XMLNode* t = node->add_child (X_("AutomationView"));
1325                         t->add_property (X_("id"), atoi (atv->parent_route()->id().to_s().c_str()));
1326                         t->add_property (X_("parameter"), EventTypeMap::instance().to_symbol (atv->parameter ()));
1327                 }
1328         }
1329
1330         for (RegionSelection::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1331                 XMLNode* r = node->add_child (X_("Region"));
1332                 r->add_property (X_("id"), atoi ((*i)->region ()->id ().to_s ().c_str()));
1333         }
1334
1335         /* midi region views have thir own internal selection. */
1336         list<pair<PBD::ID, std::set<boost::shared_ptr<Evoral::Note<Evoral::Beats> > > > > rid_notes;
1337         editor->get_per_region_note_selection (rid_notes);
1338
1339         list<pair<PBD::ID, std::set<boost::shared_ptr<Evoral::Note<Evoral::Beats> > > > >::iterator rn_it;
1340         for (rn_it = rid_notes.begin(); rn_it != rid_notes.end(); ++rn_it) {
1341                 XMLNode* n = node->add_child (X_("MIDINotes"));
1342                 n->add_property (X_("region-id"), atoi((*rn_it).first.to_s().c_str()));
1343
1344                 for (std::set<boost::shared_ptr<Evoral::Note<Evoral::Beats> > >::iterator i = (*rn_it).second.begin(); i != (*rn_it).second.end(); ++i) {
1345                         XMLNode* nc = n->add_child(X_("note"));
1346
1347                         snprintf(buf, sizeof(buf), "%d", (*i)->id());
1348                         nc->add_property (X_("note-id"), string(buf));
1349                 }
1350         }
1351
1352         for (PointSelection::const_iterator i = points.begin(); i != points.end(); ++i) {
1353                 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (&(*i)->line().trackview);
1354                 if (atv) {
1355
1356                         XMLNode* r = node->add_child (X_("ControlPoint"));
1357                         r->add_property (X_("type"), "track");
1358                         r->add_property (X_("route-id"), atoi (atv->parent_route()->id ().to_s ().c_str()));
1359                         r->add_property (X_("automation-list-id"), atoi ((*i)->line().the_list()->id ().to_s ().c_str()));
1360                         r->add_property (X_("parameter"), EventTypeMap::instance().to_symbol ((*i)->line().the_list()->parameter ()));
1361
1362                         snprintf(buf, sizeof(buf), "%d", (*i)->view_index());
1363                         r->add_property (X_("view-index"), string(buf));
1364                         continue;
1365                 }
1366
1367                 AudioRegionGainLine* argl = dynamic_cast<AudioRegionGainLine*> (&(*i)->line());
1368                 if (argl) {
1369                         XMLNode* r = node->add_child (X_("ControlPoint"));
1370                         r->add_property (X_("type"), "region");
1371                         r->add_property (X_("region-id"), atoi (argl->region_view ().region ()->id ().to_s ().c_str()));
1372                         snprintf(buf, sizeof(buf), "%d", (*i)->view_index());
1373                         r->add_property (X_("view-index"), string(buf));
1374                 }
1375
1376         }
1377
1378         for (TimeSelection::const_iterator i = time.begin(); i != time.end(); ++i) {
1379                 XMLNode* t = node->add_child (X_("AudioRange"));
1380                 snprintf(buf, sizeof(buf), "%" PRId64, (*i).start);
1381                 t->add_property (X_("start"), string(buf));
1382                 snprintf(buf, sizeof(buf), "%" PRId64, (*i).end);
1383                 t->add_property (X_("end"), string(buf));
1384         }
1385
1386         for (MarkerSelection::const_iterator i = markers.begin(); i != markers.end(); ++i) {
1387                 XMLNode* t = node->add_child (X_("Marker"));
1388
1389                 bool is_start;
1390                 Location* loc = editor->find_location_from_marker (*i, is_start);
1391
1392                 t->add_property (X_("id"), atoi (loc->id().to_s().c_str()));
1393                 t->add_property (X_("start"), is_start ? X_("yes") : X_("no"));
1394         }
1395
1396         return *node;
1397 }
1398
1399 int
1400 Selection::set_state (XMLNode const & node, int)
1401 {
1402         if (node.name() != X_("Selection")) {
1403                 return -1;
1404         }
1405
1406         clear_regions ();
1407         clear_midi_notes ();
1408         clear_points ();
1409         clear_time ();
1410         clear_tracks ();
1411         clear_markers ();
1412
1413         XMLNodeList children = node.children ();
1414         for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
1415                 if ((*i)->name() == X_("RouteView")) {
1416
1417                         XMLProperty const * prop_id = (*i)->property (X_("id"));
1418                         assert (prop_id);
1419                         PBD::ID id (prop_id->value ());
1420                         RouteTimeAxisView* rtv = editor->get_route_view_by_route_id (id);
1421                         if (rtv) {
1422                                 add (rtv);
1423                         }
1424
1425                 } else if ((*i)->name() == X_("Region")) {
1426                         XMLProperty const * prop_id = (*i)->property (X_("id"));
1427                         assert (prop_id);
1428                         PBD::ID id (prop_id->value ());
1429
1430                         RegionSelection rs;
1431                         editor->get_regionviews_by_id (id, rs);
1432
1433                         if (!rs.empty ()) {
1434                                 add (rs);
1435                         } else {
1436                                 /*
1437                                   regionviews haven't been constructed - stash the region IDs
1438                                   so we can identify them in Editor::region_view_added ()
1439                                 */
1440                                 regions.pending.push_back (id);
1441                         }
1442
1443                 } else if ((*i)->name() == X_("MIDINotes")) {
1444                         XMLProperty const * prop_region_id = (*i)->property (X_("region-id"));
1445
1446                         assert (prop_region_id);
1447
1448                         PBD::ID const id (prop_region_id->value ());
1449                         RegionSelection rs;
1450
1451                         editor->get_regionviews_by_id (id, rs); // there could be more than one
1452
1453                         std::list<Evoral::event_id_t> notes;
1454                         XMLNodeList children = (*i)->children ();
1455
1456                         for (XMLNodeList::const_iterator ci = children.begin(); ci != children.end(); ++ci) {
1457                                 XMLProperty const * prop_id = (*ci)->property (X_("note-id"));
1458                                 if (prop_id) {
1459                                         Evoral::event_id_t id = atoi(prop_id->value());
1460                                         notes.push_back (id);
1461                                 }
1462                         }
1463
1464                         for (RegionSelection::iterator rsi = rs.begin(); rsi != rs.end(); ++rsi) {
1465                                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (*rsi);
1466                                 if (mrv) {
1467                                         mrv->select_notes(notes);
1468                                 }
1469                         }
1470
1471                         if (rs.empty()) {
1472                                 /* regionviews containing these notes don't yet exist on the canvas.*/
1473                                 pending_midi_note_selection.push_back (make_pair (id, notes));
1474                         }
1475
1476                 } else if  ((*i)->name() == X_("ControlPoint")) {
1477                         XMLProperty const * prop_type = (*i)->property (X_("type"));
1478
1479                         assert(prop_type);
1480
1481                         if (prop_type->value () == "track") {
1482
1483                                 XMLProperty const * prop_route_id = (*i)->property (X_("route-id"));
1484                                 XMLProperty const * prop_alist_id = (*i)->property (X_("automation-list-id"));
1485                                 XMLProperty const * prop_parameter = (*i)->property (X_("parameter"));
1486                                 XMLProperty const * prop_view_index = (*i)->property (X_("view-index"));
1487
1488                                 assert (prop_route_id);
1489                                 assert (prop_alist_id);
1490                                 assert (prop_parameter);
1491                                 assert (prop_view_index);
1492
1493                                 PBD::ID route_id (prop_route_id->value ());
1494                                 RouteTimeAxisView* rtv = editor->get_route_view_by_route_id (route_id);
1495                                 vector <ControlPoint *> cps;
1496
1497                                 if (rtv) {
1498                                         boost::shared_ptr<AutomationTimeAxisView> atv = rtv->automation_child (EventTypeMap::instance().from_symbol (prop_parameter->value ()));
1499                                         if (atv) {
1500                                                 list<boost::shared_ptr<AutomationLine> > lines = atv->lines();
1501                                                 for (list<boost::shared_ptr<AutomationLine> > ::iterator li = lines.begin(); li != lines.end(); ++li) {
1502                                                         if ((*li)->the_list()->id() == prop_alist_id->value()) {
1503                                                                 ControlPoint* cp = (*li)->nth(atol(prop_view_index->value().c_str()));
1504                                                                 if (cp) {
1505                                                                         cps.push_back (cp);
1506                                                                         cp->show();
1507                                                                 }
1508                                                         }
1509                                                 }
1510                                         }
1511                                 }
1512                                 if (!cps.empty()) {
1513                                         add (cps);
1514                                 }
1515                         } else if (prop_type->value () == "region") {
1516                                 XMLProperty const * prop_region_id = (*i)->property (X_("region-id"));
1517                                 XMLProperty const * prop_view_index = (*i)->property (X_("view-index"));
1518
1519                                 if (!prop_region_id || !prop_view_index) {
1520                                         continue;
1521                                 }
1522
1523                                 PBD::ID region_id (prop_region_id->value ());
1524                                 RegionSelection rs;
1525                                 editor->get_regionviews_by_id (region_id, rs);
1526
1527                                 if (!rs.empty ()) {
1528                                         vector <ControlPoint *> cps;
1529                                         for (RegionSelection::iterator rsi = rs.begin(); rsi != rs.end(); ++rsi) {
1530                                                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*rsi);
1531                                                 if (arv) {
1532                                                         boost::shared_ptr<AudioRegionGainLine> gl = arv->get_gain_line ();
1533                                                         ControlPoint* cp = gl->nth(atol(prop_view_index->value().c_str()));
1534                                                         if (cp) {
1535                                                                 cps.push_back (cp);
1536                                                                 cp->show();
1537                                                         }
1538                                                 }
1539                                         }
1540                                         if (!cps.empty()) {
1541                                                 add (cps);
1542                                         }
1543                                 }
1544                         }
1545
1546                 } else if  ((*i)->name() == X_("AudioRange")) {
1547                         XMLProperty const * prop_start = (*i)->property (X_("start"));
1548                         XMLProperty const * prop_end = (*i)->property (X_("end"));
1549
1550                         assert (prop_start);
1551                         assert (prop_end);
1552
1553                         framepos_t s (atol (prop_start->value ().c_str()));
1554                         framepos_t e (atol (prop_end->value ().c_str()));
1555
1556                         set_preserving_all_ranges (s, e);
1557
1558                 } else if ((*i)->name() == X_("AutomationView")) {
1559
1560                         XMLProperty const * prop_id = (*i)->property (X_("id"));
1561                         XMLProperty const * prop_parameter = (*i)->property (X_("parameter"));
1562
1563                         assert (prop_id);
1564                         assert (prop_parameter);
1565
1566                         PBD::ID id (prop_id->value ());
1567                         RouteTimeAxisView* rtv = editor->get_route_view_by_route_id (id);
1568
1569                         if (rtv) {
1570                                 boost::shared_ptr<AutomationTimeAxisView> atv = rtv->automation_child (EventTypeMap::instance().from_symbol (prop_parameter->value ()));
1571
1572                                 /* the automation could be for an entity that was never saved
1573                                    in the session file. Don't freak out if we can't find
1574                                    it.
1575                                 */
1576
1577                                 if (atv) {
1578                                         add (atv.get());
1579                                 }
1580                         }
1581
1582                 } else if ((*i)->name() == X_("Marker")) {
1583
1584                         XMLProperty const * prop_id = (*i)->property (X_("id"));
1585                         XMLProperty const * prop_start = (*i)->property (X_("start"));
1586                         assert (prop_id);
1587                         assert (prop_start);
1588
1589                         PBD::ID id (prop_id->value ());
1590                         ArdourMarker* m = editor->find_marker_from_location_id (id, string_is_affirmative (prop_start->value ()));
1591                         if (m) {
1592                                 add (m);
1593                         }
1594
1595                 }
1596
1597         }
1598
1599         return 0;
1600 }
1601
1602 void
1603 Selection::remove_regions (TimeAxisView* t)
1604 {
1605         RegionSelection::iterator i = regions.begin();
1606         while (i != regions.end ()) {
1607                 RegionSelection::iterator tmp = i;
1608                 ++tmp;
1609
1610                 if (&(*i)->get_time_axis_view() == t) {
1611                         remove (*i);
1612                 }
1613
1614                 i = tmp;
1615         }
1616 }