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