remove some cruft with previous commits. remove option link-region-and-track-selecti...
[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         TrackViewList added = tracks.add (track_list);
403
404         if (!added.empty()) {
405                 for (TrackViewList::iterator x = added.begin(); x != added.end(); ++x) {
406                         (*x)->set_selected (true);
407                 }
408                 if (!_no_tracks_changed) {
409                         TracksChanged ();
410                 }
411         }
412 }
413
414 void
415 Selection::add (TimeAxisView* track)
416 {
417         TrackViewList tr;
418         track->set_selected (true);
419         tr.push_back (track);
420         add (tr);
421 }
422
423 void
424 Selection::add (const MidiNoteSelection& midi_list)
425 {
426         clear_time();  //enforce object/range exclusivity
427         clear_tracks();  //enforce object/track exclusivity
428
429         const MidiNoteSelection::const_iterator b = midi_list.begin();
430         const MidiNoteSelection::const_iterator e = midi_list.end();
431
432         if (!midi_list.empty()) {
433                 midi_notes.insert (midi_notes.end(), b, e);
434                 MidiNotesChanged ();
435         }
436 }
437
438 void
439 Selection::add (MidiCutBuffer* midi)
440 {
441         /* we take ownership of the MCB */
442
443         if (find (midi_notes.begin(), midi_notes.end(), midi) == midi_notes.end()) {
444                 midi_notes.push_back (midi);
445                 MidiNotesChanged ();
446         }
447 }
448
449 void
450 Selection::add (vector<RegionView*>& v)
451 {
452         clear_time();  //enforce object/range exclusivity
453         clear_tracks();  //enforce object/track exclusivity
454
455         /* XXX This method or the add (const RegionSelection&) needs to go
456          */
457
458         bool changed = false;
459
460         for (vector<RegionView*>::iterator i = v.begin(); i != v.end(); ++i) {
461                 if (find (regions.begin(), regions.end(), (*i)) == regions.end()) {
462                         changed = regions.add ((*i));
463                 }
464         }
465
466         if (changed) {
467                 RegionsChanged ();
468         }
469 }
470
471 void
472 Selection::add (const RegionSelection& rs)
473 {
474         clear_time();  //enforce object/range exclusivity
475         clear_tracks();  //enforce object/track exclusivity
476
477         /* XXX This method or the add (const vector<RegionView*>&) needs to go
478          */
479
480         bool changed = false;
481
482         for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
483                 if (find (regions.begin(), regions.end(), (*i)) == regions.end()) {
484                         changed = regions.add ((*i));
485                 }
486         }
487
488         if (changed) {
489                 RegionsChanged ();
490         }
491 }
492
493 void
494 Selection::add (RegionView* r)
495 {
496         clear_time();  //enforce object/range exclusivity
497         clear_tracks();  //enforce object/track exclusivity
498
499         if (find (regions.begin(), regions.end(), r) == regions.end()) {
500                 bool changed = regions.add (r);
501         if (changed) {
502             RegionsChanged ();
503         }
504         }
505 }
506
507 void
508 Selection::add (MidiRegionView* mrv)
509 {
510         clear_time();  //enforce object/range exclusivity
511         clear_tracks();  //enforce object/track exclusivity
512
513         if (find (midi_regions.begin(), midi_regions.end(), mrv) == midi_regions.end()) {
514                 midi_regions.push_back (mrv);
515                 /* XXX should we do this? */
516                 MidiRegionsChanged ();
517         }
518 }
519
520 long
521 Selection::add (framepos_t start, framepos_t end)
522 {
523         clear_objects();  //enforce object/range exclusivity
524
525         AudioRangeComparator cmp;
526
527         /* XXX this implementation is incorrect */
528
529         time.push_back (AudioRange (start, end, ++next_time_id));
530         time.consolidate ();
531         time.sort (cmp);
532
533         TimeChanged ();
534
535         return next_time_id;
536 }
537
538 void
539 Selection::move_time (framecnt_t distance)
540 {
541         if (distance == 0) {
542                 return;
543         }
544
545         for (list<AudioRange>::iterator i = time.begin(); i != time.end(); ++i) {
546                 (*i).start += distance;
547                 (*i).end += distance;
548         }
549
550         TimeChanged ();
551 }
552
553 void
554 Selection::replace (uint32_t sid, framepos_t start, framepos_t end)
555 {
556         clear_objects();  //enforce object/range exclusivity
557         
558         for (list<AudioRange>::iterator i = time.begin(); i != time.end(); ++i) {
559                 if ((*i).id == sid) {
560                         time.erase (i);
561                         time.push_back (AudioRange(start,end, sid));
562
563                         /* don't consolidate here */
564
565
566                         AudioRangeComparator cmp;
567                         time.sort (cmp);
568
569                         TimeChanged ();
570                         break;
571                 }
572         }
573 }
574
575 void
576 Selection::add (boost::shared_ptr<Evoral::ControlList> cl)
577 {
578         clear_time();  //enforce object/range exclusivity
579         clear_tracks();  //enforce object/track exclusivity
580
581         boost::shared_ptr<ARDOUR::AutomationList> al
582                 = boost::dynamic_pointer_cast<ARDOUR::AutomationList>(cl);
583         if (!al) {
584                 warning << "Programming error: Selected list is not an ARDOUR::AutomationList" << endmsg;
585                 return;
586         }
587         if (find (lines.begin(), lines.end(), al) == lines.end()) {
588                 lines.push_back (al);
589                 LinesChanged();
590         }
591 }
592
593 void
594 Selection::remove (TimeAxisView* track)
595 {
596         list<TimeAxisView*>::iterator i;
597         if ((i = find (tracks.begin(), tracks.end(), track)) != tracks.end()) {
598                 track->set_selected (false);
599                 tracks.erase (i);
600                 if (!_no_tracks_changed) {
601                         TracksChanged();
602                 }
603         }
604 }
605
606 void
607 Selection::remove (const TrackViewList& track_list)
608 {
609         bool changed = false;
610
611         for (TrackViewList::const_iterator i = track_list.begin(); i != track_list.end(); ++i) {
612
613                 TrackViewList::iterator x = find (tracks.begin(), tracks.end(), *i);
614                 if (x != tracks.end()) {
615                         (*i)->set_selected (false);
616                         tracks.erase (x);
617                         changed = true;
618                 }
619         }
620
621         if (changed) {
622                 if (!_no_tracks_changed) {
623                         TracksChanged();
624                 }
625         }
626 }
627
628 void
629 Selection::remove (ControlPoint* p)
630 {
631         PointSelection::iterator i = find (points.begin(), points.end(), p);
632         if (i != points.end ()) {
633                 points.erase (i);
634         }
635 }
636
637 void
638 Selection::remove (const MidiNoteSelection& midi_list)
639 {
640         bool changed = false;
641
642         for (MidiNoteSelection::const_iterator i = midi_list.begin(); i != midi_list.end(); ++i) {
643
644                 MidiNoteSelection::iterator x;
645
646                 if ((x = find (midi_notes.begin(), midi_notes.end(), (*i))) != midi_notes.end()) {
647                         midi_notes.erase (x);
648                         changed = true;
649                 }
650         }
651
652         if (changed) {
653                 MidiNotesChanged();
654         }
655 }
656
657 void
658 Selection::remove (MidiCutBuffer* midi)
659 {
660         MidiNoteSelection::iterator x;
661
662         if ((x = find (midi_notes.begin(), midi_notes.end(), midi)) != midi_notes.end()) {
663                 /* remember that we own the MCB */
664                 delete *x;
665                 midi_notes.erase (x);
666                 MidiNotesChanged ();
667         }
668 }
669
670 void
671 Selection::remove (boost::shared_ptr<Playlist> track)
672 {
673         list<boost::shared_ptr<Playlist> >::iterator i;
674         if ((i = find (playlists.begin(), playlists.end(), track)) != playlists.end()) {
675                 playlists.erase (i);
676                 PlaylistsChanged();
677         }
678 }
679
680 void
681 Selection::remove (const list<boost::shared_ptr<Playlist> >& pllist)
682 {
683         bool changed = false;
684
685         for (list<boost::shared_ptr<Playlist> >::const_iterator i = pllist.begin(); i != pllist.end(); ++i) {
686
687                 list<boost::shared_ptr<Playlist> >::iterator x;
688
689                 if ((x = find (playlists.begin(), playlists.end(), (*i))) != playlists.end()) {
690                         playlists.erase (x);
691                         changed = true;
692                 }
693         }
694
695         if (changed) {
696                 PlaylistsChanged();
697         }
698 }
699
700 void
701 Selection::remove (RegionView* r)
702 {
703         if (regions.remove (r)) {
704                 RegionsChanged ();
705         }
706 }
707
708 void
709 Selection::remove (MidiRegionView* mrv)
710 {
711         MidiRegionSelection::iterator x;
712
713         if ((x = find (midi_regions.begin(), midi_regions.end(), mrv)) != midi_regions.end()) {
714                 midi_regions.erase (x);
715                 MidiRegionsChanged ();
716         }
717 }
718
719
720 void
721 Selection::remove (uint32_t selection_id)
722 {
723         if (time.empty()) {
724                 return;
725         }
726
727         for (list<AudioRange>::iterator i = time.begin(); i != time.end(); ++i) {
728                 if ((*i).id == selection_id) {
729                         time.erase (i);
730
731                         TimeChanged ();
732                         break;
733                 }
734         }
735 }
736
737 void
738 Selection::remove (framepos_t /*start*/, framepos_t /*end*/)
739 {
740 }
741
742 void
743 Selection::remove (boost::shared_ptr<ARDOUR::AutomationList> ac)
744 {
745         AutomationSelection::iterator i;
746         if ((i = find (lines.begin(), lines.end(), ac)) != lines.end()) {
747                 lines.erase (i);
748                 LinesChanged();
749         }
750 }
751
752 void
753 Selection::set (TimeAxisView* track)
754 {
755         clear_objects();  //enforce object/range exclusivity
756         clear_tracks ();
757         add (track);
758 }
759
760 void
761 Selection::set (const TrackViewList& track_list)
762 {
763         clear_objects();  //enforce object/range exclusivity
764         clear_tracks ();
765         add (track_list);
766 }
767
768 void
769 Selection::set (const MidiNoteSelection& midi_list)
770 {
771         clear_time ();  //enforce region/object exclusivity
772         clear_tracks();  //enforce object/track exclusivity
773         clear_objects ();
774         add (midi_list);
775 }
776
777 void
778 Selection::set (boost::shared_ptr<Playlist> playlist)
779 {
780         clear_time ();  //enforce region/object exclusivity
781         clear_tracks();  //enforce object/track exclusivity
782         clear_objects ();
783         add (playlist);
784 }
785
786 void
787 Selection::set (const list<boost::shared_ptr<Playlist> >& pllist)
788 {
789         clear_time();  //enforce region/object exclusivity
790         clear_objects ();
791         add (pllist);
792 }
793
794 void
795 Selection::set (const RegionSelection& rs)
796 {
797         clear_time();  //enforce region/object exclusivity
798         clear_tracks();  //enforce object/track exclusivity
799         clear_objects();
800         regions = rs;
801         RegionsChanged(); /* EMIT SIGNAL */
802 }
803
804 void
805 Selection::set (MidiRegionView* mrv)
806 {
807         clear_time();  //enforce region/object exclusivity
808         clear_tracks();  //enforce object/track exclusivity
809         clear_objects ();
810         add (mrv);
811 }
812
813 void
814 Selection::set (RegionView* r, bool /*also_clear_tracks*/)
815 {
816         clear_time();  //enforce region/object exclusivity
817         clear_tracks();  //enforce object/track exclusivity
818         clear_objects ();
819         add (r);
820 }
821
822 void
823 Selection::set (vector<RegionView*>& v)
824 {
825         bool had_regions = !regions.empty();
826
827         clear_time();  //enforce region/object exclusivity
828         clear_tracks();  //enforce object/track exclusivity
829         clear_objects();
830
831         add (v);
832 }
833
834 /** Set the start and end time of the time selection, without changing
835  *  the list of tracks it applies to.
836  */
837 long
838 Selection::set (framepos_t start, framepos_t end)
839 {
840         clear_objects();  //enforce region/object exclusivity
841         clear_tracks();  //enforce object/track exclusivity
842         clear_time();
843
844         if ((start == 0 && end == 0) || end < start) {
845                 return 0;
846         }
847
848         if (time.empty()) {
849                 time.push_back (AudioRange (start, end, ++next_time_id));
850         } else {
851                 /* reuse the first entry, and remove all the rest */
852
853                 while (time.size() > 1) {
854                         time.pop_front();
855                 }
856                 time.front().start = start;
857                 time.front().end = end;
858         }
859
860         time.consolidate ();
861
862         TimeChanged ();
863
864         return time.front().id;
865 }
866
867 /** Set the start and end of the range selection.  If more than one range
868  *  is currently selected, the start of the earliest range and the end of the
869  *  latest range are set.  If no range is currently selected, this method
870  *  selects a single range from start to end.
871  *
872  *  @param start New start time.
873  *  @param end New end time.
874  */
875 void
876 Selection::set_preserving_all_ranges (framepos_t start, framepos_t end)
877 {
878         clear_objects();  //enforce region/object exclusivity
879
880         if ((start == 0 && end == 0) || (end < start)) {
881                 return;
882         }
883
884         if (time.empty ()) {
885                 time.push_back (AudioRange (start, end, ++next_time_id));
886         } else {
887                 time.sort (AudioRangeComparator ());
888                 time.front().start = start;
889                 time.back().end = end;
890         }
891
892         time.consolidate ();
893
894         TimeChanged ();
895 }
896
897 void
898 Selection::set (boost::shared_ptr<Evoral::ControlList> ac)
899 {
900         clear_time();  //enforce region/object exclusivity
901         clear_tracks();  //enforce object/track exclusivity
902         clear_objects();
903         
904         add (ac);
905 }
906
907 bool
908 Selection::selected (Marker* m)
909 {
910         return find (markers.begin(), markers.end(), m) != markers.end();
911 }
912
913 bool
914 Selection::selected (TimeAxisView* tv)
915 {
916         return tv->get_selected ();
917 }
918
919 bool
920 Selection::selected (RegionView* rv)
921 {
922         return find (regions.begin(), regions.end(), rv) != regions.end();
923 }
924
925 bool
926 Selection::selected (ControlPoint* cp)
927 {
928         return find (points.begin(), points.end(), cp) != points.end();
929 }
930
931 bool
932 Selection::empty (bool internal_selection)
933 {
934         bool object_level_empty =  regions.empty () &&
935                 tracks.empty () &&
936                 points.empty () &&
937                 playlists.empty () &&
938                 lines.empty () &&
939                 time.empty () &&
940                 playlists.empty () &&
941                 markers.empty() &&
942                 midi_regions.empty()
943                 ;
944
945         if (!internal_selection) {
946                 return object_level_empty;
947         }
948
949         /* this is intended to really only apply when using a Selection
950            as a cut buffer.
951         */
952
953         return object_level_empty && midi_notes.empty();
954 }
955
956 void
957 Selection::toggle (ControlPoint* cp)
958 {
959         clear_time();  //enforce region/object exclusivity
960         clear_tracks();  //enforce object/track exclusivity
961
962         cp->set_selected (!cp->get_selected ());
963         PointSelection::iterator i = find (points.begin(), points.end(), cp);
964         if (i == points.end()) {
965                 points.push_back (cp);
966         } else {
967                 points.erase (i);
968         }
969
970         PointsChanged (); /* EMIT SIGNAL */
971 }
972
973 void
974 Selection::toggle (vector<ControlPoint*> const & cps)
975 {
976         clear_time();  //enforce region/object exclusivity
977         clear_tracks();  //enforce object/track exclusivity
978
979         for (vector<ControlPoint*>::const_iterator i = cps.begin(); i != cps.end(); ++i) {
980                 toggle (*i);
981         }
982 }
983
984 void
985 Selection::toggle (list<Selectable*> const & selectables)
986 {
987         clear_time();  //enforce region/object exclusivity
988         clear_tracks();  //enforce object/track exclusivity
989         
990         RegionView* rv;
991         ControlPoint* cp;
992         vector<RegionView*> rvs;
993         vector<ControlPoint*> cps;
994
995         for (std::list<Selectable*>::const_iterator i = selectables.begin(); i != selectables.end(); ++i) {
996                 if ((rv = dynamic_cast<RegionView*> (*i)) != 0) {
997                         rvs.push_back (rv);
998                 } else if ((cp = dynamic_cast<ControlPoint*> (*i)) != 0) {
999                         cps.push_back (cp);
1000                 } else {
1001                         fatal << _("programming error: ")
1002                               << X_("unknown selectable type passed to Selection::toggle()")
1003                               << endmsg;
1004                         /*NOTREACHED*/
1005                 }
1006         }
1007
1008         if (!rvs.empty()) {
1009                 toggle (rvs);
1010         }
1011
1012         if (!cps.empty()) {
1013                 toggle (cps);
1014         }
1015 }
1016
1017 void
1018 Selection::set (list<Selectable*> const & selectables)
1019 {
1020         clear_time ();  //enforce region/object exclusivity
1021         clear_tracks();  //enforce object/track exclusivity
1022         clear_objects ();
1023
1024         add (selectables);
1025 }
1026
1027 void
1028 Selection::add (PointSelection const & s)
1029 {
1030         clear_time ();  //enforce region/object exclusivity
1031         clear_tracks();  //enforce object/track exclusivity
1032
1033         for (PointSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
1034                 points.push_back (*i);
1035         }
1036 }
1037
1038 void
1039 Selection::add (list<Selectable*> const & selectables)
1040 {
1041         clear_time ();  //enforce region/object exclusivity
1042         clear_tracks();  //enforce object/track exclusivity
1043
1044         RegionView* rv;
1045         ControlPoint* cp;
1046         vector<RegionView*> rvs;
1047         vector<ControlPoint*> cps;
1048
1049         for (std::list<Selectable*>::const_iterator i = selectables.begin(); i != selectables.end(); ++i) {
1050                 if ((rv = dynamic_cast<RegionView*> (*i)) != 0) {
1051                         rvs.push_back (rv);
1052                 } else if ((cp = dynamic_cast<ControlPoint*> (*i)) != 0) {
1053                         cps.push_back (cp);
1054                 } else {
1055                         fatal << _("programming error: ")
1056                               << X_("unknown selectable type passed to Selection::add()")
1057                               << endmsg;
1058                         /*NOTREACHED*/
1059                 }
1060         }
1061
1062         if (!rvs.empty()) {
1063                 add (rvs);
1064         }
1065
1066         if (!cps.empty()) {
1067                 add (cps);
1068         }
1069 }
1070
1071 void
1072 Selection::clear_points ()
1073 {
1074         if (!points.empty()) {
1075                 points.clear ();
1076                 PointsChanged ();
1077         }
1078 }
1079
1080 void
1081 Selection::add (ControlPoint* cp)
1082 {
1083         clear_time ();  //enforce region/object exclusivity
1084         clear_tracks();  //enforce object/track exclusivity
1085
1086         cp->set_selected (true);
1087         points.push_back (cp);
1088         PointsChanged (); /* EMIT SIGNAL */
1089 }
1090
1091 void
1092 Selection::add (vector<ControlPoint*> const & cps)
1093 {
1094         clear_time ();  //enforce region/object exclusivity
1095         clear_tracks();  //enforce object/track exclusivity
1096
1097         for (vector<ControlPoint*>::const_iterator i = cps.begin(); i != cps.end(); ++i) {
1098                 (*i)->set_selected (true);
1099                 points.push_back (*i);
1100         }
1101         PointsChanged (); /* EMIT SIGNAL */
1102 }
1103
1104 void
1105 Selection::set (ControlPoint* cp)
1106 {
1107         clear_time ();  //enforce region/object exclusivity
1108         clear_tracks();  //enforce object/track exclusivity
1109
1110         if (cp->get_selected()) {
1111                 return;
1112         }
1113
1114         for (uint32_t i = 0; i < cp->line().npoints(); ++i) {
1115                 cp->line().nth (i)->set_selected (false);
1116         }
1117
1118         clear_objects ();
1119         add (cp);
1120 }
1121
1122 void
1123 Selection::set (Marker* m)
1124 {
1125         clear_time ();  //enforce region/object exclusivity
1126         clear_tracks();  //enforce object/track exclusivity
1127         markers.clear ();
1128
1129         add (m);
1130 }
1131
1132 void
1133 Selection::toggle (Marker* m)
1134 {
1135         MarkerSelection::iterator i;
1136
1137         if ((i = find (markers.begin(), markers.end(), m)) == markers.end()) {
1138                 add (m);
1139         } else {
1140                 remove (m);
1141         }
1142 }
1143
1144 void
1145 Selection::remove (Marker* m)
1146 {
1147         MarkerSelection::iterator i;
1148
1149         if ((i = find (markers.begin(), markers.end(), m)) != markers.end()) {
1150                 markers.erase (i);
1151                 MarkersChanged();
1152         }
1153 }
1154
1155 void
1156 Selection::add (Marker* m)
1157 {
1158         clear_time ();  //enforce region/object exclusivity
1159         clear_tracks();  //enforce object/track exclusivity
1160
1161         if (find (markers.begin(), markers.end(), m) == markers.end()) {
1162                 markers.push_back (m);
1163                 MarkersChanged();
1164         }
1165 }
1166
1167 void
1168 Selection::add (const list<Marker*>& m)
1169 {
1170         clear_time ();  //enforce region/object exclusivity
1171         clear_tracks();  //enforce object/track exclusivity
1172
1173         markers.insert (markers.end(), m.begin(), m.end());
1174         markers.sort ();
1175         markers.unique ();
1176         
1177         MarkersChanged ();
1178 }
1179
1180 void
1181 MarkerSelection::range (framepos_t& s, framepos_t& e)
1182 {
1183         s = max_framepos;
1184         e = 0;
1185
1186         for (MarkerSelection::iterator i = begin(); i != end(); ++i) {
1187
1188                 if ((*i)->position() < s) {
1189                         s = (*i)->position();
1190                 }
1191
1192                 if ((*i)->position() > e) {
1193                         e = (*i)->position();
1194                 }
1195         }
1196
1197         s = std::min (s, e);
1198         e = std::max (s, e);
1199 }
1200
1201 XMLNode&
1202 Selection::get_state () const
1203 {
1204         /* XXX: not complete; just sufficient to get track selection state
1205            so that re-opening plugin windows for editor mixer strips works
1206         */
1207
1208         XMLNode* node = new XMLNode (X_("Selection"));
1209
1210         for (TrackSelection::const_iterator i = tracks.begin(); i != tracks.end(); ++i) {
1211                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
1212                 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (*i);
1213                 if (rtv) {
1214                         XMLNode* t = node->add_child (X_("RouteView"));
1215                         t->add_property (X_("id"), atoi (rtv->route()->id().to_s().c_str()));
1216                 } else if (atv) {
1217                         XMLNode* t = node->add_child (X_("AutomationView"));
1218                         t->add_property (X_("id"), atoi (atv->parent_route()->id().to_s().c_str()));
1219                         t->add_property (X_("parameter"), EventTypeMap::instance().to_symbol (atv->parameter ()));
1220                 }
1221         }
1222
1223         for (MarkerSelection::const_iterator i = markers.begin(); i != markers.end(); ++i) {
1224                 XMLNode* t = node->add_child (X_("Marker"));
1225
1226                 bool is_start;
1227                 Location* loc = editor->find_location_from_marker (*i, is_start);
1228
1229                 t->add_property (X_("id"), atoi (loc->id().to_s().c_str()));
1230                 t->add_property (X_("start"), is_start ? X_("yes") : X_("no"));
1231         }
1232
1233         return *node;
1234 }
1235
1236 int
1237 Selection::set_state (XMLNode const & node, int)
1238 {
1239         if (node.name() != X_("Selection")) {
1240                 return -1;
1241         }
1242
1243         XMLNodeList children = node.children ();
1244         for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
1245                 if ((*i)->name() == X_("RouteView")) {
1246
1247                         XMLProperty* prop_id = (*i)->property (X_("id"));
1248                         assert (prop_id);
1249                         PBD::ID id (prop_id->value ());
1250                         RouteTimeAxisView* rtv = editor->get_route_view_by_route_id (id);
1251                         if (rtv) {
1252                                 add (rtv);
1253                         }
1254
1255                 } else if ((*i)->name() == X_("AutomationView")) {
1256
1257                         XMLProperty* prop_id = (*i)->property (X_("id"));
1258                         XMLProperty* prop_parameter = (*i)->property (X_("parameter"));
1259
1260                         assert (prop_id);
1261                         assert (prop_parameter);
1262
1263                         PBD::ID id (prop_id->value ());
1264                         RouteTimeAxisView* rtv = editor->get_route_view_by_route_id (id);
1265
1266                         if (rtv) {
1267                                 boost::shared_ptr<AutomationTimeAxisView> atv = rtv->automation_child (EventTypeMap::instance().new_parameter (prop_parameter->value ()));
1268
1269                                 /* the automation could be for an entity that was never saved
1270                                    in the session file. Don't freak out if we can't find
1271                                    it.
1272                                 */
1273
1274                                 if (atv) {
1275                                         add (atv.get());
1276                                 }
1277                         }
1278
1279                 } else if ((*i)->name() == X_("Marker")) {
1280
1281                         XMLProperty* prop_id = (*i)->property (X_("id"));
1282                         XMLProperty* prop_start = (*i)->property (X_("start"));
1283                         assert (prop_id);
1284                         assert (prop_start);
1285
1286                         PBD::ID id (prop_id->value ());
1287                         Marker* m = editor->find_marker_from_location_id (id, string_is_affirmative (prop_start->value ()));
1288                         if (m) {
1289                                 add (m);
1290                         }
1291                         
1292                 }
1293                 
1294         }
1295
1296         return 0;
1297 }
1298
1299 void
1300 Selection::remove_regions (TimeAxisView* t)
1301 {
1302         RegionSelection::iterator i = regions.begin();
1303         while (i != regions.end ()) {
1304                 RegionSelection::iterator tmp = i;
1305                 ++tmp;
1306
1307                 if (&(*i)->get_time_axis_view() == t) {
1308                         remove (*i);
1309                 }
1310
1311                 i = tmp;
1312         }
1313 }
1314
1315 void
1316 Selection::block_tracks_changed (bool yn)
1317 {
1318         _no_tracks_changed = yn;
1319 }