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