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