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