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