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