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