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