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