remove leftover cruft from link-region-and-track removal
[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         clear_time();  //enforce region/object exclusivity
826         clear_tracks();  //enforce object/track exclusivity
827         clear_objects();
828
829         add (v);
830 }
831
832 /** Set the start and end time of the time selection, without changing
833  *  the list of tracks it applies to.
834  */
835 long
836 Selection::set (framepos_t start, framepos_t end)
837 {
838         clear_objects();  //enforce region/object exclusivity
839         clear_tracks();  //enforce object/track exclusivity
840         clear_time();
841
842         if ((start == 0 && end == 0) || end < start) {
843                 return 0;
844         }
845
846         if (time.empty()) {
847                 time.push_back (AudioRange (start, end, ++next_time_id));
848         } else {
849                 /* reuse the first entry, and remove all the rest */
850
851                 while (time.size() > 1) {
852                         time.pop_front();
853                 }
854                 time.front().start = start;
855                 time.front().end = end;
856         }
857
858         time.consolidate ();
859
860         TimeChanged ();
861
862         return time.front().id;
863 }
864
865 /** Set the start and end of the range selection.  If more than one range
866  *  is currently selected, the start of the earliest range and the end of the
867  *  latest range are set.  If no range is currently selected, this method
868  *  selects a single range from start to end.
869  *
870  *  @param start New start time.
871  *  @param end New end time.
872  */
873 void
874 Selection::set_preserving_all_ranges (framepos_t start, framepos_t end)
875 {
876         clear_objects();  //enforce region/object exclusivity
877
878         if ((start == 0 && end == 0) || (end < start)) {
879                 return;
880         }
881
882         if (time.empty ()) {
883                 time.push_back (AudioRange (start, end, ++next_time_id));
884         } else {
885                 time.sort (AudioRangeComparator ());
886                 time.front().start = start;
887                 time.back().end = end;
888         }
889
890         time.consolidate ();
891
892         TimeChanged ();
893 }
894
895 void
896 Selection::set (boost::shared_ptr<Evoral::ControlList> ac)
897 {
898         clear_time();  //enforce region/object exclusivity
899         clear_tracks();  //enforce object/track exclusivity
900         clear_objects();
901         
902         add (ac);
903 }
904
905 bool
906 Selection::selected (Marker* m)
907 {
908         return find (markers.begin(), markers.end(), m) != markers.end();
909 }
910
911 bool
912 Selection::selected (TimeAxisView* tv)
913 {
914         return tv->get_selected ();
915 }
916
917 bool
918 Selection::selected (RegionView* rv)
919 {
920         return find (regions.begin(), regions.end(), rv) != regions.end();
921 }
922
923 bool
924 Selection::selected (ControlPoint* cp)
925 {
926         return find (points.begin(), points.end(), cp) != points.end();
927 }
928
929 bool
930 Selection::empty (bool internal_selection)
931 {
932         bool object_level_empty =  regions.empty () &&
933                 tracks.empty () &&
934                 points.empty () &&
935                 playlists.empty () &&
936                 lines.empty () &&
937                 time.empty () &&
938                 playlists.empty () &&
939                 markers.empty() &&
940                 midi_regions.empty()
941                 ;
942
943         if (!internal_selection) {
944                 return object_level_empty;
945         }
946
947         /* this is intended to really only apply when using a Selection
948            as a cut buffer.
949         */
950
951         return object_level_empty && midi_notes.empty();
952 }
953
954 void
955 Selection::toggle (ControlPoint* cp)
956 {
957         clear_time();  //enforce region/object exclusivity
958         clear_tracks();  //enforce object/track exclusivity
959
960         cp->set_selected (!cp->get_selected ());
961         PointSelection::iterator i = find (points.begin(), points.end(), cp);
962         if (i == points.end()) {
963                 points.push_back (cp);
964         } else {
965                 points.erase (i);
966         }
967
968         PointsChanged (); /* EMIT SIGNAL */
969 }
970
971 void
972 Selection::toggle (vector<ControlPoint*> const & cps)
973 {
974         clear_time();  //enforce region/object exclusivity
975         clear_tracks();  //enforce object/track exclusivity
976
977         for (vector<ControlPoint*>::const_iterator i = cps.begin(); i != cps.end(); ++i) {
978                 toggle (*i);
979         }
980 }
981
982 void
983 Selection::toggle (list<Selectable*> const & selectables)
984 {
985         clear_time();  //enforce region/object exclusivity
986         clear_tracks();  //enforce object/track exclusivity
987         
988         RegionView* rv;
989         ControlPoint* cp;
990         vector<RegionView*> rvs;
991         vector<ControlPoint*> cps;
992
993         for (std::list<Selectable*>::const_iterator i = selectables.begin(); i != selectables.end(); ++i) {
994                 if ((rv = dynamic_cast<RegionView*> (*i)) != 0) {
995                         rvs.push_back (rv);
996                 } else if ((cp = dynamic_cast<ControlPoint*> (*i)) != 0) {
997                         cps.push_back (cp);
998                 } else {
999                         fatal << _("programming error: ")
1000                               << X_("unknown selectable type passed to Selection::toggle()")
1001                               << endmsg;
1002                         /*NOTREACHED*/
1003                 }
1004         }
1005
1006         if (!rvs.empty()) {
1007                 toggle (rvs);
1008         }
1009
1010         if (!cps.empty()) {
1011                 toggle (cps);
1012         }
1013 }
1014
1015 void
1016 Selection::set (list<Selectable*> const & selectables)
1017 {
1018         clear_time ();  //enforce region/object exclusivity
1019         clear_tracks();  //enforce object/track exclusivity
1020         clear_objects ();
1021
1022         add (selectables);
1023 }
1024
1025 void
1026 Selection::add (PointSelection const & s)
1027 {
1028         clear_time ();  //enforce region/object exclusivity
1029         clear_tracks();  //enforce object/track exclusivity
1030
1031         for (PointSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
1032                 points.push_back (*i);
1033         }
1034 }
1035
1036 void
1037 Selection::add (list<Selectable*> const & selectables)
1038 {
1039         clear_time ();  //enforce region/object exclusivity
1040         clear_tracks();  //enforce object/track exclusivity
1041
1042         RegionView* rv;
1043         ControlPoint* cp;
1044         vector<RegionView*> rvs;
1045         vector<ControlPoint*> cps;
1046
1047         for (std::list<Selectable*>::const_iterator i = selectables.begin(); i != selectables.end(); ++i) {
1048                 if ((rv = dynamic_cast<RegionView*> (*i)) != 0) {
1049                         rvs.push_back (rv);
1050                 } else if ((cp = dynamic_cast<ControlPoint*> (*i)) != 0) {
1051                         cps.push_back (cp);
1052                 } else {
1053                         fatal << _("programming error: ")
1054                               << X_("unknown selectable type passed to Selection::add()")
1055                               << endmsg;
1056                         /*NOTREACHED*/
1057                 }
1058         }
1059
1060         if (!rvs.empty()) {
1061                 add (rvs);
1062         }
1063
1064         if (!cps.empty()) {
1065                 add (cps);
1066         }
1067 }
1068
1069 void
1070 Selection::clear_points ()
1071 {
1072         if (!points.empty()) {
1073                 points.clear ();
1074                 PointsChanged ();
1075         }
1076 }
1077
1078 void
1079 Selection::add (ControlPoint* cp)
1080 {
1081         clear_time ();  //enforce region/object exclusivity
1082         clear_tracks();  //enforce object/track exclusivity
1083
1084         cp->set_selected (true);
1085         points.push_back (cp);
1086         PointsChanged (); /* EMIT SIGNAL */
1087 }
1088
1089 void
1090 Selection::add (vector<ControlPoint*> const & cps)
1091 {
1092         clear_time ();  //enforce region/object exclusivity
1093         clear_tracks();  //enforce object/track exclusivity
1094
1095         for (vector<ControlPoint*>::const_iterator i = cps.begin(); i != cps.end(); ++i) {
1096                 (*i)->set_selected (true);
1097                 points.push_back (*i);
1098         }
1099         PointsChanged (); /* EMIT SIGNAL */
1100 }
1101
1102 void
1103 Selection::set (ControlPoint* cp)
1104 {
1105         clear_time ();  //enforce region/object exclusivity
1106         clear_tracks();  //enforce object/track exclusivity
1107
1108         if (cp->get_selected()) {
1109                 return;
1110         }
1111
1112         for (uint32_t i = 0; i < cp->line().npoints(); ++i) {
1113                 cp->line().nth (i)->set_selected (false);
1114         }
1115
1116         clear_objects ();
1117         add (cp);
1118 }
1119
1120 void
1121 Selection::set (Marker* m)
1122 {
1123         clear_time ();  //enforce region/object exclusivity
1124         clear_tracks();  //enforce object/track exclusivity
1125         markers.clear ();
1126
1127         add (m);
1128 }
1129
1130 void
1131 Selection::toggle (Marker* m)
1132 {
1133         MarkerSelection::iterator i;
1134
1135         if ((i = find (markers.begin(), markers.end(), m)) == markers.end()) {
1136                 add (m);
1137         } else {
1138                 remove (m);
1139         }
1140 }
1141
1142 void
1143 Selection::remove (Marker* m)
1144 {
1145         MarkerSelection::iterator i;
1146
1147         if ((i = find (markers.begin(), markers.end(), m)) != markers.end()) {
1148                 markers.erase (i);
1149                 MarkersChanged();
1150         }
1151 }
1152
1153 void
1154 Selection::add (Marker* m)
1155 {
1156         clear_time ();  //enforce region/object exclusivity
1157         clear_tracks();  //enforce object/track exclusivity
1158
1159         if (find (markers.begin(), markers.end(), m) == markers.end()) {
1160                 markers.push_back (m);
1161                 MarkersChanged();
1162         }
1163 }
1164
1165 void
1166 Selection::add (const list<Marker*>& m)
1167 {
1168         clear_time ();  //enforce region/object exclusivity
1169         clear_tracks();  //enforce object/track exclusivity
1170
1171         markers.insert (markers.end(), m.begin(), m.end());
1172         markers.sort ();
1173         markers.unique ();
1174         
1175         MarkersChanged ();
1176 }
1177
1178 void
1179 MarkerSelection::range (framepos_t& s, framepos_t& e)
1180 {
1181         s = max_framepos;
1182         e = 0;
1183
1184         for (MarkerSelection::iterator i = begin(); i != end(); ++i) {
1185
1186                 if ((*i)->position() < s) {
1187                         s = (*i)->position();
1188                 }
1189
1190                 if ((*i)->position() > e) {
1191                         e = (*i)->position();
1192                 }
1193         }
1194
1195         s = std::min (s, e);
1196         e = std::max (s, e);
1197 }
1198
1199 XMLNode&
1200 Selection::get_state () const
1201 {
1202         /* XXX: not complete; just sufficient to get track selection state
1203            so that re-opening plugin windows for editor mixer strips works
1204         */
1205
1206         XMLNode* node = new XMLNode (X_("Selection"));
1207
1208         for (TrackSelection::const_iterator i = tracks.begin(); i != tracks.end(); ++i) {
1209                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
1210                 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (*i);
1211                 if (rtv) {
1212                         XMLNode* t = node->add_child (X_("RouteView"));
1213                         t->add_property (X_("id"), atoi (rtv->route()->id().to_s().c_str()));
1214                 } else if (atv) {
1215                         XMLNode* t = node->add_child (X_("AutomationView"));
1216                         t->add_property (X_("id"), atoi (atv->parent_route()->id().to_s().c_str()));
1217                         t->add_property (X_("parameter"), EventTypeMap::instance().to_symbol (atv->parameter ()));
1218                 }
1219         }
1220
1221         for (MarkerSelection::const_iterator i = markers.begin(); i != markers.end(); ++i) {
1222                 XMLNode* t = node->add_child (X_("Marker"));
1223
1224                 bool is_start;
1225                 Location* loc = editor->find_location_from_marker (*i, is_start);
1226
1227                 t->add_property (X_("id"), atoi (loc->id().to_s().c_str()));
1228                 t->add_property (X_("start"), is_start ? X_("yes") : X_("no"));
1229         }
1230
1231         return *node;
1232 }
1233
1234 int
1235 Selection::set_state (XMLNode const & node, int)
1236 {
1237         if (node.name() != X_("Selection")) {
1238                 return -1;
1239         }
1240
1241         XMLNodeList children = node.children ();
1242         for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
1243                 if ((*i)->name() == X_("RouteView")) {
1244
1245                         XMLProperty* prop_id = (*i)->property (X_("id"));
1246                         assert (prop_id);
1247                         PBD::ID id (prop_id->value ());
1248                         RouteTimeAxisView* rtv = editor->get_route_view_by_route_id (id);
1249                         if (rtv) {
1250                                 add (rtv);
1251                         }
1252
1253                 } else if ((*i)->name() == X_("AutomationView")) {
1254
1255                         XMLProperty* prop_id = (*i)->property (X_("id"));
1256                         XMLProperty* prop_parameter = (*i)->property (X_("parameter"));
1257
1258                         assert (prop_id);
1259                         assert (prop_parameter);
1260
1261                         PBD::ID id (prop_id->value ());
1262                         RouteTimeAxisView* rtv = editor->get_route_view_by_route_id (id);
1263
1264                         if (rtv) {
1265                                 boost::shared_ptr<AutomationTimeAxisView> atv = rtv->automation_child (EventTypeMap::instance().new_parameter (prop_parameter->value ()));
1266
1267                                 /* the automation could be for an entity that was never saved
1268                                    in the session file. Don't freak out if we can't find
1269                                    it.
1270                                 */
1271
1272                                 if (atv) {
1273                                         add (atv.get());
1274                                 }
1275                         }
1276
1277                 } else if ((*i)->name() == X_("Marker")) {
1278
1279                         XMLProperty* prop_id = (*i)->property (X_("id"));
1280                         XMLProperty* prop_start = (*i)->property (X_("start"));
1281                         assert (prop_id);
1282                         assert (prop_start);
1283
1284                         PBD::ID id (prop_id->value ());
1285                         Marker* m = editor->find_marker_from_location_id (id, string_is_affirmative (prop_start->value ()));
1286                         if (m) {
1287                                 add (m);
1288                         }
1289                         
1290                 }
1291                 
1292         }
1293
1294         return 0;
1295 }
1296
1297 void
1298 Selection::remove_regions (TimeAxisView* t)
1299 {
1300         RegionSelection::iterator i = regions.begin();
1301         while (i != regions.end ()) {
1302                 RegionSelection::iterator tmp = i;
1303                 ++tmp;
1304
1305                 if (&(*i)->get_time_axis_view() == t) {
1306                         remove (*i);
1307                 }
1308
1309                 i = tmp;
1310         }
1311 }
1312
1313 void
1314 Selection::block_tracks_changed (bool yn)
1315 {
1316         _no_tracks_changed = yn;
1317 }