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