the daily dose of const'ness
[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         clear_objects();  //enforce object/range exclusivity
463
464         TrackViewList tr;
465         track->set_selected (true);
466         tr.push_back (track);
467         add (tr);
468 }
469
470 void
471 Selection::add (const MidiNoteSelection& midi_list)
472 {
473         clear_time();  //enforce object/range exclusivity
474         clear_tracks();  //enforce object/track exclusivity
475
476         const MidiNoteSelection::const_iterator b = midi_list.begin();
477         const MidiNoteSelection::const_iterator e = midi_list.end();
478
479         if (!midi_list.empty()) {
480                 midi_notes.insert (midi_notes.end(), b, e);
481                 MidiNotesChanged ();
482         }
483 }
484
485 void
486 Selection::add (MidiCutBuffer* midi)
487 {
488         /* we take ownership of the MCB */
489
490         if (find (midi_notes.begin(), midi_notes.end(), midi) == midi_notes.end()) {
491                 midi_notes.push_back (midi);
492                 MidiNotesChanged ();
493         }
494 }
495
496 void
497 Selection::add (vector<RegionView*>& v)
498 {
499         clear_time();  //enforce object/range exclusivity
500         clear_tracks();  //enforce object/track exclusivity
501
502         /* XXX This method or the add (const RegionSelection&) needs to go
503          */
504
505         bool changed = false;
506
507         for (vector<RegionView*>::iterator i = v.begin(); i != v.end(); ++i) {
508                 if (find (regions.begin(), regions.end(), (*i)) == regions.end()) {
509                         changed = regions.add ((*i));
510                 }
511         }
512
513         if (changed) {
514                 RegionsChanged ();
515         }
516 }
517
518 void
519 Selection::add (const RegionSelection& rs)
520 {
521         clear_time();  //enforce object/range exclusivity
522         clear_tracks();  //enforce object/track exclusivity
523
524         /* XXX This method or the add (const vector<RegionView*>&) needs to go
525          */
526
527         bool changed = false;
528
529         for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
530                 if (find (regions.begin(), regions.end(), (*i)) == regions.end()) {
531                         changed = regions.add ((*i));
532                 }
533         }
534
535         if (changed) {
536                 RegionsChanged ();
537         }
538 }
539
540 void
541 Selection::add (RegionView* r)
542 {
543         clear_time();  //enforce object/range exclusivity
544         clear_tracks();  //enforce object/track exclusivity
545
546         if (find (regions.begin(), regions.end(), r) == regions.end()) {
547                 bool changed = regions.add (r);
548                 if (changed) {
549                         RegionsChanged ();
550                 }
551         }
552 }
553
554 void
555 Selection::add (MidiRegionView* mrv)
556 {
557         DEBUG_TRACE(DEBUG::Selection, string_compose("Selection::add MRV %1\n", mrv));
558
559         clear_time();  //enforce object/range exclusivity
560         clear_tracks();  //enforce object/track exclusivity
561
562         if (find (midi_regions.begin(), midi_regions.end(), mrv) == midi_regions.end()) {
563                 midi_regions.push_back (mrv);
564                 /* XXX should we do this? */
565                 MidiRegionsChanged ();
566         }
567 }
568
569 long
570 Selection::add (framepos_t start, framepos_t end)
571 {
572         clear_objects();  //enforce object/range exclusivity
573
574         AudioRangeComparator cmp;
575
576         /* XXX this implementation is incorrect */
577
578         time.push_back (AudioRange (start, end, ++next_time_id));
579         time.consolidate ();
580         time.sort (cmp);
581
582         TimeChanged ();
583
584         return next_time_id;
585 }
586
587 void
588 Selection::move_time (framecnt_t distance)
589 {
590         if (distance == 0) {
591                 return;
592         }
593
594         for (list<AudioRange>::iterator i = time.begin(); i != time.end(); ++i) {
595                 (*i).start += distance;
596                 (*i).end += distance;
597         }
598
599         TimeChanged ();
600 }
601
602 void
603 Selection::replace (uint32_t sid, framepos_t start, framepos_t end)
604 {
605         clear_objects();  //enforce object/range exclusivity
606
607         for (list<AudioRange>::iterator i = time.begin(); i != time.end(); ++i) {
608                 if ((*i).id == sid) {
609                         time.erase (i);
610                         time.push_back (AudioRange(start,end, sid));
611
612                         /* don't consolidate here */
613
614
615                         AudioRangeComparator cmp;
616                         time.sort (cmp);
617
618                         TimeChanged ();
619                         break;
620                 }
621         }
622 }
623
624 void
625 Selection::add (boost::shared_ptr<Evoral::ControlList> cl)
626 {
627         clear_time();  //enforce object/range exclusivity
628         clear_tracks();  //enforce object/track exclusivity
629
630         boost::shared_ptr<ARDOUR::AutomationList> al
631                 = boost::dynamic_pointer_cast<ARDOUR::AutomationList>(cl);
632         if (!al) {
633                 warning << "Programming error: Selected list is not an ARDOUR::AutomationList" << endmsg;
634                 return;
635         }
636
637         /* The original may change so we must store a copy (not a pointer) here.
638          * e.g AutomationLine rewrites the list with gain mapping.
639          * the downside is that we can't perfom duplicate checks.
640          * This code was changed in response to #6842
641          */
642         lines.push_back (boost::shared_ptr<ARDOUR::AutomationList> (new ARDOUR::AutomationList(*al)));
643         LinesChanged();
644 }
645
646 void
647 Selection::remove (TimeAxisView* track)
648 {
649         list<TimeAxisView*>::iterator i;
650         if ((i = find (tracks.begin(), tracks.end(), track)) != tracks.end()) {
651                 /* erase first, because set_selected() will remove the track
652                    from the selection, invalidating the iterator.
653
654                    In fact, we don't really even need to do the erase, but this is
655                    a hangover of axis view selection being in the GUI.
656                 */
657                 tracks.erase (i);
658                 track->set_selected (false);
659         }
660 }
661
662 void
663 Selection::remove (const TrackViewList& track_list)
664 {
665         PresentationInfo::ChangeSuspender cs;
666
667         for (TrackViewList::const_iterator i = track_list.begin(); i != track_list.end(); ++i) {
668
669                 TrackViewList::iterator x = find (tracks.begin(), tracks.end(), *i);
670
671                 if (x != tracks.end()) {
672                         tracks.erase (x);
673                         (*i)->set_selected (false);
674                 }
675         }
676 }
677
678 void
679 Selection::remove (ControlPoint* p)
680 {
681         PointSelection::iterator i = find (points.begin(), points.end(), p);
682         if (i != points.end ()) {
683                 points.erase (i);
684         }
685 }
686
687 void
688 Selection::remove (const MidiNoteSelection& midi_list)
689 {
690         bool changed = false;
691
692         for (MidiNoteSelection::const_iterator i = midi_list.begin(); i != midi_list.end(); ++i) {
693
694                 MidiNoteSelection::iterator x;
695
696                 if ((x = find (midi_notes.begin(), midi_notes.end(), (*i))) != midi_notes.end()) {
697                         midi_notes.erase (x);
698                         changed = true;
699                 }
700         }
701
702         if (changed) {
703                 MidiNotesChanged();
704         }
705 }
706
707 void
708 Selection::remove (MidiCutBuffer* midi)
709 {
710         MidiNoteSelection::iterator x;
711
712         if ((x = find (midi_notes.begin(), midi_notes.end(), midi)) != midi_notes.end()) {
713                 /* remember that we own the MCB */
714                 delete *x;
715                 midi_notes.erase (x);
716                 MidiNotesChanged ();
717         }
718 }
719
720 void
721 Selection::remove (boost::shared_ptr<Playlist> track)
722 {
723         list<boost::shared_ptr<Playlist> >::iterator i;
724         if ((i = find (playlists.begin(), playlists.end(), track)) != playlists.end()) {
725                 playlists.erase (i);
726                 PlaylistsChanged();
727         }
728 }
729
730 void
731 Selection::remove (const list<boost::shared_ptr<Playlist> >& pllist)
732 {
733         bool changed = false;
734
735         for (list<boost::shared_ptr<Playlist> >::const_iterator i = pllist.begin(); i != pllist.end(); ++i) {
736
737                 list<boost::shared_ptr<Playlist> >::iterator x;
738
739                 if ((x = find (playlists.begin(), playlists.end(), (*i))) != playlists.end()) {
740                         playlists.erase (x);
741                         changed = true;
742                 }
743         }
744
745         if (changed) {
746                 PlaylistsChanged();
747         }
748 }
749
750 void
751 Selection::remove (RegionView* r)
752 {
753         if (regions.remove (r)) {
754                 RegionsChanged ();
755         }
756 }
757
758 void
759 Selection::remove (MidiRegionView* mrv)
760 {
761         DEBUG_TRACE(DEBUG::Selection, string_compose("Selection::remove MRV %1\n", mrv));
762
763         MidiRegionSelection::iterator x;
764
765         if ((x = find (midi_regions.begin(), midi_regions.end(), mrv)) != midi_regions.end()) {
766                 midi_regions.erase (x);
767                 MidiRegionsChanged ();
768         }
769 }
770
771 void
772 Selection::remove (uint32_t selection_id)
773 {
774         if (time.empty()) {
775                 return;
776         }
777
778         for (list<AudioRange>::iterator i = time.begin(); i != time.end(); ++i) {
779                 if ((*i).id == selection_id) {
780                         time.erase (i);
781
782                         TimeChanged ();
783                         break;
784                 }
785         }
786 }
787
788 void
789 Selection::remove (framepos_t /*start*/, framepos_t /*end*/)
790 {
791 }
792
793 void
794 Selection::remove (boost::shared_ptr<ARDOUR::AutomationList> ac)
795 {
796         AutomationSelection::iterator i;
797         if ((i = find (lines.begin(), lines.end(), ac)) != lines.end()) {
798                 lines.erase (i);
799                 LinesChanged();
800         }
801 }
802
803 void
804 Selection::set (TimeAxisView* track)
805 {
806         if (dynamic_cast<VCATimeAxisView*> (track)) {
807                 return;
808         }
809         clear_objects ();  //enforce object/range exclusivity
810
811         PresentationInfo::ChangeSuspender cs;
812
813         if (!tracks.empty()) {
814
815                 if (tracks.size() == 1 && tracks.front() == track) {
816                         /* already single selection: nothing to do */
817                         return;
818                 }
819
820                 for (TrackViewList::iterator x = tracks.begin(); x != tracks.end(); ++x) {
821                         (*x)->set_selected (false);
822                 }
823
824                 tracks.clear ();
825         }
826
827         add (track);
828 }
829
830 void
831 Selection::set (const TrackViewList& track_list)
832 {
833         clear_objects();  //enforce object/range exclusivity
834
835         PresentationInfo::ChangeSuspender cs;
836
837         if (!tracks.empty()) {
838
839                 /* cannot use set<T>::operator== (set<T> const &) here, because
840                  * apparently the ordering used within 2 sets is not
841                  * necessarily the same.
842                  */
843
844                 if (tracks.size() == track_list.size()) {
845                         bool missing = false;
846
847                         for (TrackViewList::const_iterator x = track_list.begin(); x != track_list.end(); ++x) {
848                                 if (dynamic_cast<VCATimeAxisView*> (*x)) {
849                                         continue;
850                                 }
851                                 if (find (tracks.begin(), tracks.end(), *x) == tracks.end()) {
852                                         missing = true;
853                                 }
854                         }
855
856                         if (!missing) {
857                                 /* already same selection: nothing to do */
858                                 return;
859                         }
860                 }
861
862                 /* argument is different from existing selection */
863
864                 for (TrackViewList::iterator x = tracks.begin(); x != tracks.end(); ++x) {
865                         if (dynamic_cast<VCATimeAxisView*> (*x)) {
866                                 continue;
867                         }
868                         (*x)->set_selected (false);
869                 }
870
871                 tracks.clear ();
872         }
873
874         add (track_list);
875 }
876
877 void
878 Selection::set (const MidiNoteSelection& midi_list)
879 {
880         clear_time ();  //enforce region/object exclusivity
881         clear_tracks();  //enforce object/track exclusivity
882         clear_objects ();
883         add (midi_list);
884 }
885
886 void
887 Selection::set (boost::shared_ptr<Playlist> playlist)
888 {
889         clear_time ();  //enforce region/object exclusivity
890         clear_tracks();  //enforce object/track exclusivity
891         clear_objects ();
892         add (playlist);
893 }
894
895 void
896 Selection::set (const list<boost::shared_ptr<Playlist> >& pllist)
897 {
898         clear_time();  //enforce region/object exclusivity
899         clear_objects ();
900         add (pllist);
901 }
902
903 void
904 Selection::set (const RegionSelection& rs)
905 {
906         clear_time();  //enforce region/object exclusivity
907         clear_tracks();  //enforce object/track exclusivity
908         clear_objects();
909         regions = rs;
910         RegionsChanged(); /* EMIT SIGNAL */
911 }
912
913 void
914 Selection::set (MidiRegionView* mrv)
915 {
916         clear_time();  //enforce region/object exclusivity
917         clear_tracks();  //enforce object/track exclusivity
918         clear_objects ();
919         add (mrv);
920 }
921
922 void
923 Selection::set (RegionView* r, bool /*also_clear_tracks*/)
924 {
925         clear_time();  //enforce region/object exclusivity
926         clear_tracks();  //enforce object/track exclusivity
927         clear_objects ();
928         add (r);
929 }
930
931 void
932 Selection::set (vector<RegionView*>& v)
933 {
934         clear_time();  //enforce region/object exclusivity
935         clear_tracks();  //enforce object/track exclusivity
936         clear_objects();
937
938         add (v);
939 }
940
941 /** Set the start and end time of the time selection, without changing
942  *  the list of tracks it applies to.
943  */
944 long
945 Selection::set (framepos_t start, framepos_t end)
946 {
947         clear_objects();  //enforce region/object exclusivity
948         clear_time();
949
950         if ((start == 0 && end == 0) || end < start) {
951                 return 0;
952         }
953
954         if (time.empty()) {
955                 time.push_back (AudioRange (start, end, ++next_time_id));
956         } else {
957                 /* reuse the first entry, and remove all the rest */
958
959                 while (time.size() > 1) {
960                         time.pop_front();
961                 }
962                 time.front().start = start;
963                 time.front().end = end;
964         }
965
966         time.consolidate ();
967
968         TimeChanged ();
969
970         return time.front().id;
971 }
972
973 /** Set the start and end of the range selection.  If more than one range
974  *  is currently selected, the start of the earliest range and the end of the
975  *  latest range are set.  If no range is currently selected, this method
976  *  selects a single range from start to end.
977  *
978  *  @param start New start time.
979  *  @param end New end time.
980  */
981 void
982 Selection::set_preserving_all_ranges (framepos_t start, framepos_t end)
983 {
984         clear_objects();  //enforce region/object exclusivity
985
986         if ((start == 0 && end == 0) || (end < start)) {
987                 return;
988         }
989
990         if (time.empty ()) {
991                 time.push_back (AudioRange (start, end, ++next_time_id));
992         } else {
993                 time.sort (AudioRangeComparator ());
994                 time.front().start = start;
995                 time.back().end = end;
996         }
997
998         time.consolidate ();
999
1000         TimeChanged ();
1001 }
1002
1003 void
1004 Selection::set (boost::shared_ptr<Evoral::ControlList> ac)
1005 {
1006         clear_time();  //enforce region/object exclusivity
1007         clear_tracks();  //enforce object/track exclusivity
1008         clear_objects();
1009
1010         add (ac);
1011 }
1012
1013 bool
1014 Selection::selected (ArdourMarker* m) const
1015 {
1016         return find (markers.begin(), markers.end(), m) != markers.end();
1017 }
1018
1019 bool
1020 Selection::selected (TimeAxisView* tv) const
1021 {
1022         return tv->selected ();
1023 }
1024
1025 bool
1026 Selection::selected (RegionView* rv) const
1027 {
1028         return find (regions.begin(), regions.end(), rv) != regions.end();
1029 }
1030
1031 bool
1032 Selection::selected (ControlPoint* cp) const
1033 {
1034         return find (points.begin(), points.end(), cp) != points.end();
1035 }
1036
1037 bool
1038 Selection::empty (bool internal_selection)
1039 {
1040         bool object_level_empty =  regions.empty () &&
1041                 tracks.empty () &&
1042                 points.empty () &&
1043                 playlists.empty () &&
1044                 lines.empty () &&
1045                 time.empty () &&
1046                 playlists.empty () &&
1047                 markers.empty() &&
1048                 midi_regions.empty()
1049                 ;
1050
1051         if (!internal_selection) {
1052                 return object_level_empty;
1053         }
1054
1055         /* this is intended to really only apply when using a Selection
1056            as a cut buffer.
1057         */
1058
1059         return object_level_empty && midi_notes.empty() && points.empty();
1060 }
1061
1062 void
1063 Selection::toggle (ControlPoint* cp)
1064 {
1065         clear_time();  //enforce region/object exclusivity
1066         clear_tracks();  //enforce object/track exclusivity
1067
1068         cp->set_selected (!cp->selected ());
1069         PointSelection::iterator i = find (points.begin(), points.end(), cp);
1070         if (i == points.end()) {
1071                 points.push_back (cp);
1072         } else {
1073                 points.erase (i);
1074         }
1075
1076         PointsChanged (); /* EMIT SIGNAL */
1077 }
1078
1079 void
1080 Selection::toggle (vector<ControlPoint*> const & cps)
1081 {
1082         clear_time();  //enforce region/object exclusivity
1083         clear_tracks();  //enforce object/track exclusivity
1084
1085         for (vector<ControlPoint*>::const_iterator i = cps.begin(); i != cps.end(); ++i) {
1086                 toggle (*i);
1087         }
1088 }
1089
1090 void
1091 Selection::toggle (list<Selectable*> const & selectables)
1092 {
1093         clear_time();  //enforce region/object exclusivity
1094         clear_tracks();  //enforce object/track exclusivity
1095
1096         RegionView* rv;
1097         ControlPoint* cp;
1098         vector<RegionView*> rvs;
1099         vector<ControlPoint*> cps;
1100
1101         for (std::list<Selectable*>::const_iterator i = selectables.begin(); i != selectables.end(); ++i) {
1102                 if ((rv = dynamic_cast<RegionView*> (*i)) != 0) {
1103                         rvs.push_back (rv);
1104                 } else if ((cp = dynamic_cast<ControlPoint*> (*i)) != 0) {
1105                         cps.push_back (cp);
1106                 } else {
1107                         fatal << _("programming error: ")
1108                               << X_("unknown selectable type passed to Selection::toggle()")
1109                               << endmsg;
1110                         abort(); /*NOTREACHED*/
1111                 }
1112         }
1113
1114         if (!rvs.empty()) {
1115                 toggle (rvs);
1116         }
1117
1118         if (!cps.empty()) {
1119                 toggle (cps);
1120         }
1121 }
1122
1123 void
1124 Selection::set (list<Selectable*> const & selectables)
1125 {
1126         clear_time ();  //enforce region/object exclusivity
1127         clear_tracks();  //enforce object/track exclusivity
1128         clear_objects ();
1129
1130         add (selectables);
1131 }
1132
1133 void
1134 Selection::add (PointSelection const & s)
1135 {
1136         clear_time ();  //enforce region/object exclusivity
1137         clear_tracks();  //enforce object/track exclusivity
1138
1139         for (PointSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
1140                 points.push_back (*i);
1141         }
1142 }
1143
1144 void
1145 Selection::add (list<Selectable*> const & selectables)
1146 {
1147         clear_time ();  //enforce region/object exclusivity
1148         clear_tracks();  //enforce object/track exclusivity
1149
1150         RegionView* rv;
1151         ControlPoint* cp;
1152         vector<RegionView*> rvs;
1153         vector<ControlPoint*> cps;
1154
1155         for (std::list<Selectable*>::const_iterator i = selectables.begin(); i != selectables.end(); ++i) {
1156                 if ((rv = dynamic_cast<RegionView*> (*i)) != 0) {
1157                         rvs.push_back (rv);
1158                 } else if ((cp = dynamic_cast<ControlPoint*> (*i)) != 0) {
1159                         cps.push_back (cp);
1160                 } else {
1161                         fatal << _("programming error: ")
1162                               << X_("unknown selectable type passed to Selection::add()")
1163                               << endmsg;
1164                         abort(); /*NOTREACHED*/
1165                 }
1166         }
1167
1168         if (!rvs.empty()) {
1169                 add (rvs);
1170         }
1171
1172         if (!cps.empty()) {
1173                 add (cps);
1174         }
1175 }
1176
1177 void
1178 Selection::clear_points (bool with_signal)
1179 {
1180         if (!points.empty()) {
1181                 points.clear ();
1182                 if (with_signal) {
1183                         PointsChanged ();
1184                 }
1185         }
1186 }
1187
1188 void
1189 Selection::add (ControlPoint* cp)
1190 {
1191         clear_time ();  //enforce region/object exclusivity
1192         clear_tracks();  //enforce object/track exclusivity
1193
1194         cp->set_selected (true);
1195         points.push_back (cp);
1196         PointsChanged (); /* EMIT SIGNAL */
1197 }
1198
1199 void
1200 Selection::add (vector<ControlPoint*> const & cps)
1201 {
1202         clear_time ();  //enforce region/object exclusivity
1203         clear_tracks();  //enforce object/track exclusivity
1204
1205         for (vector<ControlPoint*>::const_iterator i = cps.begin(); i != cps.end(); ++i) {
1206                 (*i)->set_selected (true);
1207                 points.push_back (*i);
1208         }
1209         PointsChanged (); /* EMIT SIGNAL */
1210 }
1211
1212 void
1213 Selection::set (ControlPoint* cp)
1214 {
1215         clear_time ();  //enforce region/object exclusivity
1216         clear_tracks();  //enforce object/track exclusivity
1217
1218         if (cp->selected () && points.size () == 1) {
1219                 return;
1220         }
1221
1222         for (uint32_t i = 0; i < cp->line().npoints(); ++i) {
1223                 cp->line().nth (i)->set_selected (false);
1224         }
1225
1226         clear_objects ();
1227         add (cp);
1228 }
1229
1230 void
1231 Selection::set (ArdourMarker* m)
1232 {
1233         clear_time ();  //enforce region/object exclusivity
1234         clear_tracks();  //enforce object/track exclusivity
1235         markers.clear ();
1236
1237         add (m);
1238 }
1239
1240 void
1241 Selection::toggle (ArdourMarker* m)
1242 {
1243         MarkerSelection::iterator i;
1244
1245         if ((i = find (markers.begin(), markers.end(), m)) == markers.end()) {
1246                 add (m);
1247         } else {
1248                 remove (m);
1249         }
1250 }
1251
1252 void
1253 Selection::remove (ArdourMarker* m)
1254 {
1255         MarkerSelection::iterator i;
1256
1257         if ((i = find (markers.begin(), markers.end(), m)) != markers.end()) {
1258                 markers.erase (i);
1259                 MarkersChanged();
1260         }
1261 }
1262
1263 void
1264 Selection::add (ArdourMarker* m)
1265 {
1266         clear_time ();  //enforce region/object exclusivity
1267         clear_tracks();  //enforce object/track exclusivity
1268
1269         if (find (markers.begin(), markers.end(), m) == markers.end()) {
1270                 markers.push_back (m);
1271                 MarkersChanged();
1272         }
1273 }
1274
1275 void
1276 Selection::add (const list<ArdourMarker*>& m)
1277 {
1278         clear_time ();  //enforce region/object exclusivity
1279         clear_tracks();  //enforce object/track exclusivity
1280
1281         markers.insert (markers.end(), m.begin(), m.end());
1282         markers.sort ();
1283         markers.unique ();
1284
1285         MarkersChanged ();
1286 }
1287
1288 void
1289 MarkerSelection::range (framepos_t& s, framepos_t& e)
1290 {
1291         s = max_framepos;
1292         e = 0;
1293
1294         for (MarkerSelection::iterator i = begin(); i != end(); ++i) {
1295
1296                 if ((*i)->position() < s) {
1297                         s = (*i)->position();
1298                 }
1299
1300                 if ((*i)->position() > e) {
1301                         e = (*i)->position();
1302                 }
1303         }
1304
1305         s = std::min (s, e);
1306         e = std::max (s, e);
1307 }
1308
1309 XMLNode&
1310 Selection::get_state () const
1311 {
1312         /* XXX: not complete; just sufficient to get track selection state
1313            so that re-opening plugin windows for editor mixer strips works
1314         */
1315
1316         char buf[32];
1317         XMLNode* node = new XMLNode (X_("Selection"));
1318
1319         for (TrackSelection::const_iterator i = tracks.begin(); i != tracks.end(); ++i) {
1320                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
1321                 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (*i);
1322                 if (rtv) {
1323                         XMLNode* t = node->add_child (X_("RouteView"));
1324                         t->add_property (X_("id"), atoi (rtv->route()->id().to_s().c_str()));
1325                 } else if (atv) {
1326                         XMLNode* t = node->add_child (X_("AutomationView"));
1327                         t->add_property (X_("id"), atoi (atv->parent_route()->id().to_s().c_str()));
1328                         t->add_property (X_("parameter"), EventTypeMap::instance().to_symbol (atv->parameter ()));
1329                 }
1330         }
1331
1332         for (RegionSelection::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1333                 XMLNode* r = node->add_child (X_("Region"));
1334                 r->add_property (X_("id"), atoi ((*i)->region ()->id ().to_s ().c_str()));
1335         }
1336
1337         /* midi region views have thir own internal selection. */
1338         list<pair<PBD::ID, std::set<boost::shared_ptr<Evoral::Note<Evoral::Beats> > > > > rid_notes;
1339         editor->get_per_region_note_selection (rid_notes);
1340
1341         list<pair<PBD::ID, std::set<boost::shared_ptr<Evoral::Note<Evoral::Beats> > > > >::iterator rn_it;
1342         for (rn_it = rid_notes.begin(); rn_it != rid_notes.end(); ++rn_it) {
1343                 XMLNode* n = node->add_child (X_("MIDINotes"));
1344                 n->add_property (X_("region-id"), atoi((*rn_it).first.to_s().c_str()));
1345
1346                 for (std::set<boost::shared_ptr<Evoral::Note<Evoral::Beats> > >::iterator i = (*rn_it).second.begin(); i != (*rn_it).second.end(); ++i) {
1347                         XMLNode* nc = n->add_child(X_("note"));
1348
1349                         snprintf(buf, sizeof(buf), "%d", (*i)->id());
1350                         nc->add_property (X_("note-id"), string(buf));
1351                 }
1352         }
1353
1354         for (PointSelection::const_iterator i = points.begin(); i != points.end(); ++i) {
1355                 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (&(*i)->line().trackview);
1356                 if (atv) {
1357
1358                         XMLNode* r = node->add_child (X_("ControlPoint"));
1359                         r->add_property (X_("type"), "track");
1360                         r->add_property (X_("route-id"), atoi (atv->parent_route()->id ().to_s ().c_str()));
1361                         r->add_property (X_("automation-list-id"), atoi ((*i)->line().the_list()->id ().to_s ().c_str()));
1362                         r->add_property (X_("parameter"), EventTypeMap::instance().to_symbol ((*i)->line().the_list()->parameter ()));
1363
1364                         snprintf(buf, sizeof(buf), "%d", (*i)->view_index());
1365                         r->add_property (X_("view-index"), string(buf));
1366                         continue;
1367                 }
1368
1369                 AudioRegionGainLine* argl = dynamic_cast<AudioRegionGainLine*> (&(*i)->line());
1370                 if (argl) {
1371                         XMLNode* r = node->add_child (X_("ControlPoint"));
1372                         r->add_property (X_("type"), "region");
1373                         r->add_property (X_("region-id"), atoi (argl->region_view ().region ()->id ().to_s ().c_str()));
1374                         snprintf(buf, sizeof(buf), "%d", (*i)->view_index());
1375                         r->add_property (X_("view-index"), string(buf));
1376                 }
1377
1378         }
1379
1380         for (TimeSelection::const_iterator i = time.begin(); i != time.end(); ++i) {
1381                 XMLNode* t = node->add_child (X_("AudioRange"));
1382                 snprintf(buf, sizeof(buf), "%" PRId64, (*i).start);
1383                 t->add_property (X_("start"), string(buf));
1384                 snprintf(buf, sizeof(buf), "%" PRId64, (*i).end);
1385                 t->add_property (X_("end"), string(buf));
1386         }
1387
1388         for (MarkerSelection::const_iterator i = markers.begin(); i != markers.end(); ++i) {
1389                 XMLNode* t = node->add_child (X_("Marker"));
1390
1391                 bool is_start;
1392                 Location* loc = editor->find_location_from_marker (*i, is_start);
1393
1394                 t->add_property (X_("id"), atoi (loc->id().to_s().c_str()));
1395                 t->add_property (X_("start"), is_start ? X_("yes") : X_("no"));
1396         }
1397
1398         return *node;
1399 }
1400
1401 int
1402 Selection::set_state (XMLNode const & node, int)
1403 {
1404         if (node.name() != X_("Selection")) {
1405                 return -1;
1406         }
1407
1408         clear_regions ();
1409         clear_midi_notes ();
1410         clear_points ();
1411         clear_time ();
1412         clear_tracks ();
1413         clear_markers ();
1414
1415         XMLNodeList children = node.children ();
1416         for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
1417                 if ((*i)->name() == X_("RouteView")) {
1418
1419                         XMLProperty const * prop_id = (*i)->property (X_("id"));
1420                         assert (prop_id);
1421                         PBD::ID id (prop_id->value ());
1422                         RouteTimeAxisView* rtv = editor->get_route_view_by_route_id (id);
1423                         if (rtv) {
1424                                 add (rtv);
1425                         }
1426
1427                 } else if ((*i)->name() == X_("Region")) {
1428                         XMLProperty const * prop_id = (*i)->property (X_("id"));
1429                         assert (prop_id);
1430                         PBD::ID id (prop_id->value ());
1431
1432                         RegionSelection rs;
1433                         editor->get_regionviews_by_id (id, rs);
1434
1435                         if (!rs.empty ()) {
1436                                 add (rs);
1437                         } else {
1438                                 /*
1439                                   regionviews haven't been constructed - stash the region IDs
1440                                   so we can identify them in Editor::region_view_added ()
1441                                 */
1442                                 regions.pending.push_back (id);
1443                         }
1444
1445                 } else if ((*i)->name() == X_("MIDINotes")) {
1446                         XMLProperty const * prop_region_id = (*i)->property (X_("region-id"));
1447
1448                         assert (prop_region_id);
1449
1450                         PBD::ID const id (prop_region_id->value ());
1451                         RegionSelection rs;
1452
1453                         editor->get_regionviews_by_id (id, rs); // there could be more than one
1454
1455                         std::list<Evoral::event_id_t> notes;
1456                         XMLNodeList children = (*i)->children ();
1457
1458                         for (XMLNodeList::const_iterator ci = children.begin(); ci != children.end(); ++ci) {
1459                                 XMLProperty const * prop_id = (*ci)->property (X_("note-id"));
1460                                 if (prop_id) {
1461                                         Evoral::event_id_t id = atoi(prop_id->value());
1462                                         notes.push_back (id);
1463                                 }
1464                         }
1465
1466                         for (RegionSelection::iterator rsi = rs.begin(); rsi != rs.end(); ++rsi) {
1467                                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (*rsi);
1468                                 if (mrv) {
1469                                         mrv->select_notes(notes);
1470                                 }
1471                         }
1472
1473                         if (rs.empty()) {
1474                                 /* regionviews containing these notes don't yet exist on the canvas.*/
1475                                 pending_midi_note_selection.push_back (make_pair (id, notes));
1476                         }
1477
1478                 } else if  ((*i)->name() == X_("ControlPoint")) {
1479                         XMLProperty const * prop_type = (*i)->property (X_("type"));
1480
1481                         assert(prop_type);
1482
1483                         if (prop_type->value () == "track") {
1484
1485                                 XMLProperty const * prop_route_id = (*i)->property (X_("route-id"));
1486                                 XMLProperty const * prop_alist_id = (*i)->property (X_("automation-list-id"));
1487                                 XMLProperty const * prop_parameter = (*i)->property (X_("parameter"));
1488                                 XMLProperty const * prop_view_index = (*i)->property (X_("view-index"));
1489
1490                                 assert (prop_route_id);
1491                                 assert (prop_alist_id);
1492                                 assert (prop_parameter);
1493                                 assert (prop_view_index);
1494
1495                                 PBD::ID route_id (prop_route_id->value ());
1496                                 RouteTimeAxisView* rtv = editor->get_route_view_by_route_id (route_id);
1497                                 vector <ControlPoint *> cps;
1498
1499                                 if (rtv) {
1500                                         boost::shared_ptr<AutomationTimeAxisView> atv = rtv->automation_child (EventTypeMap::instance().from_symbol (prop_parameter->value ()));
1501                                         if (atv) {
1502                                                 list<boost::shared_ptr<AutomationLine> > lines = atv->lines();
1503                                                 for (list<boost::shared_ptr<AutomationLine> > ::iterator li = lines.begin(); li != lines.end(); ++li) {
1504                                                         if ((*li)->the_list()->id() == prop_alist_id->value()) {
1505                                                                 ControlPoint* cp = (*li)->nth(atol(prop_view_index->value().c_str()));
1506                                                                 if (cp) {
1507                                                                         cps.push_back (cp);
1508                                                                         cp->show();
1509                                                                 }
1510                                                         }
1511                                                 }
1512                                         }
1513                                 }
1514                                 if (!cps.empty()) {
1515                                         add (cps);
1516                                 }
1517                         } else if (prop_type->value () == "region") {
1518                                 XMLProperty const * prop_region_id = (*i)->property (X_("region-id"));
1519                                 XMLProperty const * prop_view_index = (*i)->property (X_("view-index"));
1520
1521                                 if (!prop_region_id || !prop_view_index) {
1522                                         continue;
1523                                 }
1524
1525                                 PBD::ID region_id (prop_region_id->value ());
1526                                 RegionSelection rs;
1527                                 editor->get_regionviews_by_id (region_id, rs);
1528
1529                                 if (!rs.empty ()) {
1530                                         vector <ControlPoint *> cps;
1531                                         for (RegionSelection::iterator rsi = rs.begin(); rsi != rs.end(); ++rsi) {
1532                                                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*rsi);
1533                                                 if (arv) {
1534                                                         boost::shared_ptr<AudioRegionGainLine> gl = arv->get_gain_line ();
1535                                                         ControlPoint* cp = gl->nth(atol(prop_view_index->value().c_str()));
1536                                                         if (cp) {
1537                                                                 cps.push_back (cp);
1538                                                                 cp->show();
1539                                                         }
1540                                                 }
1541                                         }
1542                                         if (!cps.empty()) {
1543                                                 add (cps);
1544                                         }
1545                                 }
1546                         }
1547
1548                 } else if  ((*i)->name() == X_("AudioRange")) {
1549                         XMLProperty const * prop_start = (*i)->property (X_("start"));
1550                         XMLProperty const * prop_end = (*i)->property (X_("end"));
1551
1552                         assert (prop_start);
1553                         assert (prop_end);
1554
1555                         framepos_t s (atol (prop_start->value ().c_str()));
1556                         framepos_t e (atol (prop_end->value ().c_str()));
1557
1558                         set_preserving_all_ranges (s, e);
1559
1560                 } else if ((*i)->name() == X_("AutomationView")) {
1561
1562                         XMLProperty const * prop_id = (*i)->property (X_("id"));
1563                         XMLProperty const * prop_parameter = (*i)->property (X_("parameter"));
1564
1565                         assert (prop_id);
1566                         assert (prop_parameter);
1567
1568                         PBD::ID id (prop_id->value ());
1569                         RouteTimeAxisView* rtv = editor->get_route_view_by_route_id (id);
1570
1571                         if (rtv) {
1572                                 boost::shared_ptr<AutomationTimeAxisView> atv = rtv->automation_child (EventTypeMap::instance().from_symbol (prop_parameter->value ()));
1573
1574                                 /* the automation could be for an entity that was never saved
1575                                    in the session file. Don't freak out if we can't find
1576                                    it.
1577                                 */
1578
1579                                 if (atv) {
1580                                         add (atv.get());
1581                                 }
1582                         }
1583
1584                 } else if ((*i)->name() == X_("Marker")) {
1585
1586                         XMLProperty const * prop_id = (*i)->property (X_("id"));
1587                         XMLProperty const * prop_start = (*i)->property (X_("start"));
1588                         assert (prop_id);
1589                         assert (prop_start);
1590
1591                         PBD::ID id (prop_id->value ());
1592                         ArdourMarker* m = editor->find_marker_from_location_id (id, string_is_affirmative (prop_start->value ()));
1593                         if (m) {
1594                                 add (m);
1595                         }
1596
1597                 }
1598
1599         }
1600
1601         return 0;
1602 }
1603
1604 void
1605 Selection::remove_regions (TimeAxisView* t)
1606 {
1607         RegionSelection::iterator i = regions.begin();
1608         while (i != regions.end ()) {
1609                 RegionSelection::iterator tmp = i;
1610                 ++tmp;
1611
1612                 if (&(*i)->get_time_axis_view() == t) {
1613                         remove (*i);
1614                 }
1615
1616                 i = tmp;
1617         }
1618 }