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