Add region gain point selection undo.
[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 "audio_region_view.h"
30 #include "gui_thread.h"
31 #include "midi_cut_buffer.h"
32 #include "region_gain_line.h"
33 #include "region_view.h"
34 #include "selection.h"
35 #include "selection_templates.h"
36 #include "time_axis_view.h"
37 #include "automation_time_axis.h"
38 #include "public_editor.h"
39 #include "control_point.h"
40
41 #include "i18n.h"
42
43 using namespace std;
44 using namespace ARDOUR;
45 using namespace PBD;
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)(ArdourMarker*) = &Selection::remove;
67         ArdourMarker::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         pending_midi_note_selection.clear();
115 }
116
117 void
118 Selection::clear_objects ()
119 {
120         clear_regions ();
121         clear_points ();
122         clear_lines();
123         clear_playlists ();
124         clear_midi_notes ();
125         clear_midi_regions ();
126 }
127
128 void
129 Selection::clear_tracks ()
130 {
131         if (!tracks.empty()) {
132                 for (TrackViewList::iterator x = tracks.begin(); x != tracks.end(); ++x) {
133                         (*x)->set_selected (false);
134                 }
135                 tracks.clear ();
136                 if (!_no_tracks_changed) {
137                         TracksChanged();
138                 }
139         }
140 }
141
142 void
143 Selection::clear_time ()
144 {
145         time.clear();
146
147         TimeChanged ();
148 }
149
150 void
151 Selection::dump_region_layers()
152 {
153         cerr << "region selection layer dump" << endl;
154         for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
155                 cerr << "layer: " << (int)(*i)->region()->layer() << endl;
156         }
157 }
158
159
160 void
161 Selection::clear_regions ()
162 {
163         if (!regions.empty()) {
164                 regions.clear_all ();
165                 RegionsChanged();
166         }
167 }
168
169 void
170 Selection::clear_midi_notes ()
171 {
172         if (!midi_notes.empty()) {
173                 for (MidiNoteSelection::iterator x = midi_notes.begin(); x != midi_notes.end(); ++x) {
174                         delete *x;
175                 }
176                 midi_notes.clear ();
177                 MidiNotesChanged ();
178         }
179
180         /* The note selection is actually stored in MidiRegionView, emit signal to
181            tell them to clear their selection. */
182         ClearMidiNoteSelection();  /* EMIT SIGNAL */
183 }
184
185 void
186 Selection::clear_midi_regions ()
187 {
188         if (!midi_regions.empty()) {
189                 midi_regions.clear ();
190                 MidiRegionsChanged ();
191         }
192 }
193
194 void
195 Selection::clear_playlists ()
196 {
197         /* Selections own their playlists */
198
199         for (PlaylistSelection::iterator i = playlists.begin(); i != playlists.end(); ++i) {
200                 /* selections own their own regions, which are copies of the "originals". make them go away */
201                 (*i)->drop_regions ();
202                 (*i)->release ();
203         }
204
205         if (!playlists.empty()) {
206                 playlists.clear ();
207                 PlaylistsChanged();
208         }
209 }
210
211 void
212 Selection::clear_lines ()
213 {
214         if (!lines.empty()) {
215                 lines.clear ();
216                 LinesChanged();
217         }
218 }
219
220 void
221 Selection::clear_markers ()
222 {
223         if (!markers.empty()) {
224                 markers.clear ();
225                 MarkersChanged();
226         }
227 }
228
229 void
230 Selection::toggle (boost::shared_ptr<Playlist> pl)
231 {
232         clear_time();  //enforce object/range exclusivity
233         clear_tracks();  //enforce object/track exclusivity
234
235         PlaylistSelection::iterator i;
236
237         if ((i = find (playlists.begin(), playlists.end(), pl)) == playlists.end()) {
238                 pl->use ();
239                 playlists.push_back(pl);
240         } else {
241                 playlists.erase (i);
242         }
243
244         PlaylistsChanged ();
245 }
246
247 void
248 Selection::toggle (const TrackViewList& track_list)
249 {
250         for (TrackViewList::const_iterator i = track_list.begin(); i != track_list.end(); ++i) {
251                 toggle ((*i));
252         }
253 }
254
255 void
256 Selection::toggle (TimeAxisView* track)
257 {
258         TrackSelection::iterator i;
259
260         if ((i = find (tracks.begin(), tracks.end(), track)) == tracks.end()) {
261                 track->set_selected (true);
262                 tracks.push_back (track);
263         } else {
264                 track->set_selected (false);
265                 tracks.erase (i);
266         }
267
268         if (!_no_tracks_changed) {
269                 TracksChanged();
270         }
271 }
272
273 void
274 Selection::toggle (const MidiNoteSelection& midi_note_list)
275 {
276         clear_time();  //enforce object/range exclusivity
277         clear_tracks();  //enforce object/track exclusivity
278
279         for (MidiNoteSelection::const_iterator i = midi_note_list.begin(); i != midi_note_list.end(); ++i) {
280                 toggle ((*i));
281         }
282 }
283
284 void
285 Selection::toggle (MidiCutBuffer* midi)
286 {
287         MidiNoteSelection::iterator i;
288
289         if ((i = find (midi_notes.begin(), midi_notes.end(), midi)) == midi_notes.end()) {
290                 midi_notes.push_back (midi);
291         } else {
292                 /* remember that we own the MCB */
293                 delete *i;
294                 midi_notes.erase (i);
295         }
296
297         MidiNotesChanged();
298 }
299
300
301 void
302 Selection::toggle (RegionView* r)
303 {
304         clear_time();  //enforce object/range exclusivity
305         clear_tracks();  //enforce object/track exclusivity
306
307         RegionSelection::iterator i;
308
309         if ((i = find (regions.begin(), regions.end(), r)) == regions.end()) {
310                 add (r);
311         } else {
312                 remove (*i);
313         }
314
315         RegionsChanged ();
316 }
317
318 void
319 Selection::toggle (MidiRegionView* mrv)
320 {
321         clear_time();   //enforce object/range exclusivity
322         clear_tracks();  //enforce object/track exclusivity
323
324         MidiRegionSelection::iterator i;
325
326         if ((i = find (midi_regions.begin(), midi_regions.end(), mrv)) == midi_regions.end()) {
327                 add (mrv);
328         } else {
329                 midi_regions.erase (i);
330         }
331
332         MidiRegionsChanged ();
333 }
334
335 void
336 Selection::toggle (vector<RegionView*>& r)
337 {
338         clear_time();  //enforce object/range exclusivity
339         clear_tracks();  //enforce object/track exclusivity
340
341         RegionSelection::iterator i;
342
343         for (vector<RegionView*>::iterator x = r.begin(); x != r.end(); ++x) {
344                 if ((i = find (regions.begin(), regions.end(), (*x))) == regions.end()) {
345                         add ((*x));
346                 } else {
347                         remove (*x);
348                 }
349         }
350
351         RegionsChanged ();
352 }
353
354 long
355 Selection::toggle (framepos_t start, framepos_t end)
356 {
357         clear_objects();  //enforce object/range exclusivity
358
359         AudioRangeComparator cmp;
360
361         /* XXX this implementation is incorrect */
362
363         time.push_back (AudioRange (start, end, ++next_time_id));
364         time.consolidate ();
365         time.sort (cmp);
366
367         TimeChanged ();
368
369         return next_time_id;
370 }
371
372 void
373 Selection::add (boost::shared_ptr<Playlist> pl)
374 {
375         clear_time();  //enforce object/range exclusivity
376         clear_tracks();  //enforce object/track exclusivity
377
378         if (find (playlists.begin(), playlists.end(), pl) == playlists.end()) {
379                 pl->use ();
380                 playlists.push_back(pl);
381                 PlaylistsChanged ();
382         }
383 }
384
385 void
386 Selection::add (const list<boost::shared_ptr<Playlist> >& pllist)
387 {
388         clear_time();  //enforce object/range exclusivity
389         clear_tracks();  //enforce object/track exclusivity
390
391         bool changed = false;
392
393         for (list<boost::shared_ptr<Playlist> >::const_iterator i = pllist.begin(); i != pllist.end(); ++i) {
394                 if (find (playlists.begin(), playlists.end(), (*i)) == playlists.end()) {
395                         (*i)->use ();
396                         playlists.push_back (*i);
397                         changed = true;
398                 }
399         }
400
401         if (changed) {
402                 PlaylistsChanged ();
403         }
404 }
405
406 void
407 Selection::add (const TrackViewList& track_list)
408 {
409         clear_objects();  //enforce object/range exclusivity
410
411         TrackViewList added = tracks.add (track_list);
412
413         if (!added.empty()) {
414                 for (TrackViewList::iterator x = added.begin(); x != added.end(); ++x) {
415                         (*x)->set_selected (true);
416                 }
417                 if (!_no_tracks_changed) {
418                         TracksChanged ();
419                 }
420         }
421 }
422
423 void
424 Selection::add (TimeAxisView* track)
425 {
426         clear_objects();  //enforce object/range exclusivity
427
428         TrackViewList tr;
429         track->set_selected (true);
430         tr.push_back (track);
431         add (tr);
432 }
433
434 void
435 Selection::add (const MidiNoteSelection& midi_list)
436 {
437         clear_time();  //enforce object/range exclusivity
438         clear_tracks();  //enforce object/track exclusivity
439
440         const MidiNoteSelection::const_iterator b = midi_list.begin();
441         const MidiNoteSelection::const_iterator e = midi_list.end();
442
443         if (!midi_list.empty()) {
444                 midi_notes.insert (midi_notes.end(), b, e);
445                 MidiNotesChanged ();
446         }
447 }
448
449 void
450 Selection::add (MidiCutBuffer* midi)
451 {
452         /* we take ownership of the MCB */
453
454         if (find (midi_notes.begin(), midi_notes.end(), midi) == midi_notes.end()) {
455                 midi_notes.push_back (midi);
456                 MidiNotesChanged ();
457         }
458 }
459
460 void
461 Selection::add (vector<RegionView*>& v)
462 {
463         clear_time();  //enforce object/range exclusivity
464         clear_tracks();  //enforce object/track exclusivity
465
466         /* XXX This method or the add (const RegionSelection&) needs to go
467          */
468
469         bool changed = false;
470
471         for (vector<RegionView*>::iterator i = v.begin(); i != v.end(); ++i) {
472                 if (find (regions.begin(), regions.end(), (*i)) == regions.end()) {
473                         changed = regions.add ((*i));
474                 }
475         }
476
477         if (changed) {
478                 RegionsChanged ();
479         }
480 }
481
482 void
483 Selection::add (const RegionSelection& rs)
484 {
485         clear_time();  //enforce object/range exclusivity
486         clear_tracks();  //enforce object/track exclusivity
487
488         /* XXX This method or the add (const vector<RegionView*>&) needs to go
489          */
490
491         bool changed = false;
492
493         for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
494                 if (find (regions.begin(), regions.end(), (*i)) == regions.end()) {
495                         changed = regions.add ((*i));
496                 }
497         }
498
499         if (changed) {
500                 RegionsChanged ();
501         }
502 }
503
504 void
505 Selection::add (RegionView* r)
506 {
507         clear_time();  //enforce object/range exclusivity
508         clear_tracks();  //enforce object/track exclusivity
509
510         if (find (regions.begin(), regions.end(), r) == regions.end()) {
511                 bool changed = regions.add (r);
512                 if (changed) {
513                         RegionsChanged ();
514                 }
515         }
516 }
517
518 void
519 Selection::add (MidiRegionView* mrv)
520 {
521         clear_time();  //enforce object/range exclusivity
522         clear_tracks();  //enforce object/track exclusivity
523
524         if (find (midi_regions.begin(), midi_regions.end(), mrv) == midi_regions.end()) {
525                 midi_regions.push_back (mrv);
526                 /* XXX should we do this? */
527                 MidiRegionsChanged ();
528         }
529 }
530
531 long
532 Selection::add (framepos_t start, framepos_t end)
533 {
534         clear_objects();  //enforce object/range exclusivity
535
536         AudioRangeComparator cmp;
537
538         /* XXX this implementation is incorrect */
539
540         time.push_back (AudioRange (start, end, ++next_time_id));
541         time.consolidate ();
542         time.sort (cmp);
543
544         TimeChanged ();
545
546         return next_time_id;
547 }
548
549 void
550 Selection::move_time (framecnt_t distance)
551 {
552         if (distance == 0) {
553                 return;
554         }
555
556         for (list<AudioRange>::iterator i = time.begin(); i != time.end(); ++i) {
557                 (*i).start += distance;
558                 (*i).end += distance;
559         }
560
561         TimeChanged ();
562 }
563
564 void
565 Selection::replace (uint32_t sid, framepos_t start, framepos_t end)
566 {
567         clear_objects();  //enforce object/range exclusivity
568
569         for (list<AudioRange>::iterator i = time.begin(); i != time.end(); ++i) {
570                 if ((*i).id == sid) {
571                         time.erase (i);
572                         time.push_back (AudioRange(start,end, sid));
573
574                         /* don't consolidate here */
575
576
577                         AudioRangeComparator cmp;
578                         time.sort (cmp);
579
580                         TimeChanged ();
581                         break;
582                 }
583         }
584 }
585
586 void
587 Selection::add (boost::shared_ptr<Evoral::ControlList> cl)
588 {
589         clear_time();  //enforce object/range exclusivity
590         clear_tracks();  //enforce object/track exclusivity
591
592         boost::shared_ptr<ARDOUR::AutomationList> al
593                 = boost::dynamic_pointer_cast<ARDOUR::AutomationList>(cl);
594         if (!al) {
595                 warning << "Programming error: Selected list is not an ARDOUR::AutomationList" << endmsg;
596                 return;
597         }
598         if (find (lines.begin(), lines.end(), al) == lines.end()) {
599                 lines.push_back (al);
600                 LinesChanged();
601         }
602 }
603
604 void
605 Selection::remove (TimeAxisView* track)
606 {
607         list<TimeAxisView*>::iterator i;
608         if ((i = find (tracks.begin(), tracks.end(), track)) != tracks.end()) {
609                 track->set_selected (false);
610                 tracks.erase (i);
611                 if (!_no_tracks_changed) {
612                         TracksChanged();
613                 }
614         }
615 }
616
617 void
618 Selection::remove (const TrackViewList& track_list)
619 {
620         bool changed = false;
621
622         for (TrackViewList::const_iterator i = track_list.begin(); i != track_list.end(); ++i) {
623
624                 TrackViewList::iterator x = find (tracks.begin(), tracks.end(), *i);
625                 if (x != tracks.end()) {
626                         (*i)->set_selected (false);
627                         tracks.erase (x);
628                         changed = true;
629                 }
630         }
631
632         if (changed) {
633                 if (!_no_tracks_changed) {
634                         TracksChanged();
635                 }
636         }
637 }
638
639 void
640 Selection::remove (ControlPoint* p)
641 {
642         PointSelection::iterator i = find (points.begin(), points.end(), p);
643         if (i != points.end ()) {
644                 points.erase (i);
645         }
646 }
647
648 void
649 Selection::remove (const MidiNoteSelection& midi_list)
650 {
651         bool changed = false;
652
653         for (MidiNoteSelection::const_iterator i = midi_list.begin(); i != midi_list.end(); ++i) {
654
655                 MidiNoteSelection::iterator x;
656
657                 if ((x = find (midi_notes.begin(), midi_notes.end(), (*i))) != midi_notes.end()) {
658                         midi_notes.erase (x);
659                         changed = true;
660                 }
661         }
662
663         if (changed) {
664                 MidiNotesChanged();
665         }
666 }
667
668 void
669 Selection::remove (MidiCutBuffer* midi)
670 {
671         MidiNoteSelection::iterator x;
672
673         if ((x = find (midi_notes.begin(), midi_notes.end(), midi)) != midi_notes.end()) {
674                 /* remember that we own the MCB */
675                 delete *x;
676                 midi_notes.erase (x);
677                 MidiNotesChanged ();
678         }
679 }
680
681 void
682 Selection::remove (boost::shared_ptr<Playlist> track)
683 {
684         list<boost::shared_ptr<Playlist> >::iterator i;
685         if ((i = find (playlists.begin(), playlists.end(), track)) != playlists.end()) {
686                 playlists.erase (i);
687                 PlaylistsChanged();
688         }
689 }
690
691 void
692 Selection::remove (const list<boost::shared_ptr<Playlist> >& pllist)
693 {
694         bool changed = false;
695
696         for (list<boost::shared_ptr<Playlist> >::const_iterator i = pllist.begin(); i != pllist.end(); ++i) {
697
698                 list<boost::shared_ptr<Playlist> >::iterator x;
699
700                 if ((x = find (playlists.begin(), playlists.end(), (*i))) != playlists.end()) {
701                         playlists.erase (x);
702                         changed = true;
703                 }
704         }
705
706         if (changed) {
707                 PlaylistsChanged();
708         }
709 }
710
711 void
712 Selection::remove (RegionView* r)
713 {
714         if (regions.remove (r)) {
715                 RegionsChanged ();
716         }
717 }
718
719 void
720 Selection::remove (MidiRegionView* mrv)
721 {
722         MidiRegionSelection::iterator x;
723
724         if ((x = find (midi_regions.begin(), midi_regions.end(), mrv)) != midi_regions.end()) {
725                 midi_regions.erase (x);
726                 MidiRegionsChanged ();
727         }
728 }
729
730
731 void
732 Selection::remove (uint32_t selection_id)
733 {
734         if (time.empty()) {
735                 return;
736         }
737
738         for (list<AudioRange>::iterator i = time.begin(); i != time.end(); ++i) {
739                 if ((*i).id == selection_id) {
740                         time.erase (i);
741
742                         TimeChanged ();
743                         break;
744                 }
745         }
746 }
747
748 void
749 Selection::remove (framepos_t /*start*/, framepos_t /*end*/)
750 {
751 }
752
753 void
754 Selection::remove (boost::shared_ptr<ARDOUR::AutomationList> ac)
755 {
756         AutomationSelection::iterator i;
757         if ((i = find (lines.begin(), lines.end(), ac)) != lines.end()) {
758                 lines.erase (i);
759                 LinesChanged();
760         }
761 }
762
763 void
764 Selection::set (TimeAxisView* track)
765 {
766         clear_objects();  //enforce object/range exclusivity
767         clear_tracks ();
768         add (track);
769 }
770
771 void
772 Selection::set (const TrackViewList& track_list)
773 {
774         clear_objects();  //enforce object/range exclusivity
775         clear_tracks ();
776         add (track_list);
777 }
778
779 void
780 Selection::set (const MidiNoteSelection& midi_list)
781 {
782         clear_time ();  //enforce region/object exclusivity
783         clear_tracks();  //enforce object/track exclusivity
784         clear_objects ();
785         add (midi_list);
786 }
787
788 void
789 Selection::set (boost::shared_ptr<Playlist> playlist)
790 {
791         clear_time ();  //enforce region/object exclusivity
792         clear_tracks();  //enforce object/track exclusivity
793         clear_objects ();
794         add (playlist);
795 }
796
797 void
798 Selection::set (const list<boost::shared_ptr<Playlist> >& pllist)
799 {
800         clear_time();  //enforce region/object exclusivity
801         clear_objects ();
802         add (pllist);
803 }
804
805 void
806 Selection::set (const RegionSelection& rs)
807 {
808         clear_time();  //enforce region/object exclusivity
809         clear_tracks();  //enforce object/track exclusivity
810         clear_objects();
811         regions = rs;
812         RegionsChanged(); /* EMIT SIGNAL */
813 }
814
815 void
816 Selection::set (MidiRegionView* mrv)
817 {
818         clear_time();  //enforce region/object exclusivity
819         clear_tracks();  //enforce object/track exclusivity
820         clear_objects ();
821         add (mrv);
822 }
823
824 void
825 Selection::set (RegionView* r, bool /*also_clear_tracks*/)
826 {
827         clear_time();  //enforce region/object exclusivity
828         clear_tracks();  //enforce object/track exclusivity
829         clear_objects ();
830         add (r);
831 }
832
833 void
834 Selection::set (vector<RegionView*>& v)
835 {
836         clear_time();  //enforce region/object exclusivity
837         clear_tracks();  //enforce object/track exclusivity
838         clear_objects();
839
840         add (v);
841 }
842
843 /** Set the start and end time of the time selection, without changing
844  *  the list of tracks it applies to.
845  */
846 long
847 Selection::set (framepos_t start, framepos_t end)
848 {
849         clear_objects();  //enforce region/object exclusivity
850         clear_time();
851
852         if ((start == 0 && end == 0) || end < start) {
853                 return 0;
854         }
855
856         if (time.empty()) {
857                 time.push_back (AudioRange (start, end, ++next_time_id));
858         } else {
859                 /* reuse the first entry, and remove all the rest */
860
861                 while (time.size() > 1) {
862                         time.pop_front();
863                 }
864                 time.front().start = start;
865                 time.front().end = end;
866         }
867
868         time.consolidate ();
869
870         TimeChanged ();
871
872         return time.front().id;
873 }
874
875 /** Set the start and end of the range selection.  If more than one range
876  *  is currently selected, the start of the earliest range and the end of the
877  *  latest range are set.  If no range is currently selected, this method
878  *  selects a single range from start to end.
879  *
880  *  @param start New start time.
881  *  @param end New end time.
882  */
883 void
884 Selection::set_preserving_all_ranges (framepos_t start, framepos_t end)
885 {
886         clear_objects();  //enforce region/object exclusivity
887
888         if ((start == 0 && end == 0) || (end < start)) {
889                 return;
890         }
891
892         if (time.empty ()) {
893                 time.push_back (AudioRange (start, end, ++next_time_id));
894         } else {
895                 time.sort (AudioRangeComparator ());
896                 time.front().start = start;
897                 time.back().end = end;
898         }
899
900         time.consolidate ();
901
902         TimeChanged ();
903 }
904
905 void
906 Selection::set (boost::shared_ptr<Evoral::ControlList> ac)
907 {
908         clear_time();  //enforce region/object exclusivity
909         clear_tracks();  //enforce object/track exclusivity
910         clear_objects();
911
912         add (ac);
913 }
914
915 bool
916 Selection::selected (ArdourMarker* m)
917 {
918         return find (markers.begin(), markers.end(), m) != markers.end();
919 }
920
921 bool
922 Selection::selected (TimeAxisView* tv)
923 {
924         return tv->get_selected ();
925 }
926
927 bool
928 Selection::selected (RegionView* rv)
929 {
930         return find (regions.begin(), regions.end(), rv) != regions.end();
931 }
932
933 bool
934 Selection::selected (ControlPoint* cp)
935 {
936         return find (points.begin(), points.end(), cp) != points.end();
937 }
938
939 bool
940 Selection::empty (bool internal_selection)
941 {
942         bool object_level_empty =  regions.empty () &&
943                 tracks.empty () &&
944                 points.empty () &&
945                 playlists.empty () &&
946                 lines.empty () &&
947                 time.empty () &&
948                 playlists.empty () &&
949                 markers.empty() &&
950                 midi_regions.empty()
951                 ;
952
953         if (!internal_selection) {
954                 return object_level_empty;
955         }
956
957         /* this is intended to really only apply when using a Selection
958            as a cut buffer.
959         */
960
961         return object_level_empty && midi_notes.empty() && points.empty();
962 }
963
964 void
965 Selection::toggle (ControlPoint* cp)
966 {
967         clear_time();  //enforce region/object exclusivity
968         clear_tracks();  //enforce object/track exclusivity
969
970         cp->set_selected (!cp->get_selected ());
971         PointSelection::iterator i = find (points.begin(), points.end(), cp);
972         if (i == points.end()) {
973                 points.push_back (cp);
974         } else {
975                 points.erase (i);
976         }
977
978         PointsChanged (); /* EMIT SIGNAL */
979 }
980
981 void
982 Selection::toggle (vector<ControlPoint*> const & cps)
983 {
984         clear_time();  //enforce region/object exclusivity
985         clear_tracks();  //enforce object/track exclusivity
986
987         for (vector<ControlPoint*>::const_iterator i = cps.begin(); i != cps.end(); ++i) {
988                 toggle (*i);
989         }
990 }
991
992 void
993 Selection::toggle (list<Selectable*> const & selectables)
994 {
995         clear_time();  //enforce region/object exclusivity
996         clear_tracks();  //enforce object/track 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                         abort(); /*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_tracks();  //enforce object/track exclusivity
1030         clear_objects ();
1031
1032         add (selectables);
1033 }
1034
1035 void
1036 Selection::add (PointSelection const & s)
1037 {
1038         clear_time ();  //enforce region/object exclusivity
1039         clear_tracks();  //enforce object/track exclusivity
1040
1041         for (PointSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
1042                 points.push_back (*i);
1043         }
1044 }
1045
1046 void
1047 Selection::add (list<Selectable*> const & selectables)
1048 {
1049         clear_time ();  //enforce region/object exclusivity
1050         clear_tracks();  //enforce object/track exclusivity
1051
1052         RegionView* rv;
1053         ControlPoint* cp;
1054         vector<RegionView*> rvs;
1055         vector<ControlPoint*> cps;
1056
1057         for (std::list<Selectable*>::const_iterator i = selectables.begin(); i != selectables.end(); ++i) {
1058                 if ((rv = dynamic_cast<RegionView*> (*i)) != 0) {
1059                         rvs.push_back (rv);
1060                 } else if ((cp = dynamic_cast<ControlPoint*> (*i)) != 0) {
1061                         cps.push_back (cp);
1062                 } else {
1063                         fatal << _("programming error: ")
1064                               << X_("unknown selectable type passed to Selection::add()")
1065                               << endmsg;
1066                         abort(); /*NOTREACHED*/
1067                 }
1068         }
1069
1070         if (!rvs.empty()) {
1071                 add (rvs);
1072         }
1073
1074         if (!cps.empty()) {
1075                 add (cps);
1076         }
1077 }
1078
1079 void
1080 Selection::clear_points ()
1081 {
1082         if (!points.empty()) {
1083                 points.clear ();
1084                 PointsChanged ();
1085         }
1086 }
1087
1088 void
1089 Selection::add (ControlPoint* cp)
1090 {
1091         clear_time ();  //enforce region/object exclusivity
1092         clear_tracks();  //enforce object/track 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         clear_tracks();  //enforce object/track exclusivity
1104
1105         for (vector<ControlPoint*>::const_iterator i = cps.begin(); i != cps.end(); ++i) {
1106                 (*i)->set_selected (true);
1107                 points.push_back (*i);
1108         }
1109         PointsChanged (); /* EMIT SIGNAL */
1110 }
1111
1112 void
1113 Selection::set (ControlPoint* cp)
1114 {
1115         clear_time ();  //enforce region/object exclusivity
1116         clear_tracks();  //enforce object/track exclusivity
1117
1118         if (cp->get_selected () && points.size () == 1) {
1119                 return;
1120         }
1121
1122         for (uint32_t i = 0; i < cp->line().npoints(); ++i) {
1123                 cp->line().nth (i)->set_selected (false);
1124         }
1125
1126         clear_objects ();
1127         add (cp);
1128 }
1129
1130 void
1131 Selection::set (ArdourMarker* m)
1132 {
1133         clear_time ();  //enforce region/object exclusivity
1134         clear_tracks();  //enforce object/track exclusivity
1135         markers.clear ();
1136
1137         add (m);
1138 }
1139
1140 void
1141 Selection::toggle (ArdourMarker* m)
1142 {
1143         MarkerSelection::iterator i;
1144
1145         if ((i = find (markers.begin(), markers.end(), m)) == markers.end()) {
1146                 add (m);
1147         } else {
1148                 remove (m);
1149         }
1150 }
1151
1152 void
1153 Selection::remove (ArdourMarker* m)
1154 {
1155         MarkerSelection::iterator i;
1156
1157         if ((i = find (markers.begin(), markers.end(), m)) != markers.end()) {
1158                 markers.erase (i);
1159                 MarkersChanged();
1160         }
1161 }
1162
1163 void
1164 Selection::add (ArdourMarker* m)
1165 {
1166         clear_time ();  //enforce region/object exclusivity
1167         clear_tracks();  //enforce object/track exclusivity
1168
1169         if (find (markers.begin(), markers.end(), m) == markers.end()) {
1170                 markers.push_back (m);
1171                 MarkersChanged();
1172         }
1173 }
1174
1175 void
1176 Selection::add (const list<ArdourMarker*>& m)
1177 {
1178         clear_time ();  //enforce region/object exclusivity
1179         clear_tracks();  //enforce object/track exclusivity
1180
1181         markers.insert (markers.end(), m.begin(), m.end());
1182         markers.sort ();
1183         markers.unique ();
1184
1185         MarkersChanged ();
1186 }
1187
1188 void
1189 MarkerSelection::range (framepos_t& s, framepos_t& e)
1190 {
1191         s = max_framepos;
1192         e = 0;
1193
1194         for (MarkerSelection::iterator i = begin(); i != end(); ++i) {
1195
1196                 if ((*i)->position() < s) {
1197                         s = (*i)->position();
1198                 }
1199
1200                 if ((*i)->position() > e) {
1201                         e = (*i)->position();
1202                 }
1203         }
1204
1205         s = std::min (s, e);
1206         e = std::max (s, e);
1207 }
1208
1209 XMLNode&
1210 Selection::get_state () const
1211 {
1212         /* XXX: not complete; just sufficient to get track selection state
1213            so that re-opening plugin windows for editor mixer strips works
1214         */
1215
1216         char buf[32];
1217         XMLNode* node = new XMLNode (X_("Selection"));
1218
1219         for (TrackSelection::const_iterator i = tracks.begin(); i != tracks.end(); ++i) {
1220                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
1221                 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (*i);
1222                 if (rtv) {
1223                         XMLNode* t = node->add_child (X_("RouteView"));
1224                         t->add_property (X_("id"), atoi (rtv->route()->id().to_s().c_str()));
1225                 } else if (atv) {
1226                         XMLNode* t = node->add_child (X_("AutomationView"));
1227                         t->add_property (X_("id"), atoi (atv->parent_route()->id().to_s().c_str()));
1228                         t->add_property (X_("parameter"), EventTypeMap::instance().to_symbol (atv->parameter ()));
1229                 }
1230         }
1231
1232         for (RegionSelection::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1233                 XMLNode* r = node->add_child (X_("Region"));
1234                 r->add_property (X_("id"), atoi ((*i)->region ()->id ().to_s ().c_str()));
1235         }
1236
1237         /* midi region views have thir own internal selection. */
1238         XMLNode* n = NULL;
1239         list<pair<PBD::ID, std::set<boost::shared_ptr<Evoral::Note<Evoral::Beats> > > > > rid_notes;
1240         editor->get_per_region_note_selection (rid_notes);
1241         if (!rid_notes.empty()) {
1242                 n = node->add_child (X_("MIDINote"));
1243         }
1244         list<pair<PBD::ID, std::set<boost::shared_ptr<Evoral::Note<Evoral::Beats> > > > >::iterator rn_it;
1245         for (rn_it = rid_notes.begin(); rn_it != rid_notes.end(); ++rn_it) {
1246                 assert(n); // hint for clang static analysis
1247                 n->add_property (X_("region_id"), atoi((*rn_it).first.to_s().c_str()));
1248
1249                 for (std::set<boost::shared_ptr<Evoral::Note<Evoral::Beats> > >::iterator i = (*rn_it).second.begin(); i != (*rn_it).second.end(); ++i) {
1250                         XMLNode* nc = n->add_child(X_("note"));
1251                         snprintf(buf, sizeof(buf), "%d", (*i)->channel());
1252                         nc->add_property(X_("channel"), string(buf));
1253
1254                         snprintf(buf, sizeof(buf), "%f", (*i)->time().to_double());
1255                         nc->add_property(X_("time"), string(buf));
1256
1257                         snprintf(buf, sizeof(buf), "%d", (*i)->note());
1258                         nc->add_property(X_("note"), string(buf));
1259
1260                         snprintf(buf, sizeof(buf), "%f", (*i)->length().to_double());
1261                         nc->add_property(X_("length"), string(buf));
1262
1263                         snprintf(buf, sizeof(buf), "%d", (*i)->velocity());
1264                         nc->add_property(X_("velocity"), string(buf));
1265
1266                         snprintf(buf, sizeof(buf), "%d", (*i)->off_velocity());
1267                         nc->add_property(X_("off-velocity"), string(buf));
1268                 }
1269         }
1270
1271         for (PointSelection::const_iterator i = points.begin(); i != points.end(); ++i) {
1272                 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (&(*i)->line().trackview);
1273                 if (atv) {
1274
1275                         XMLNode* r = node->add_child (X_("ControlPoint"));
1276                         r->add_property (X_("type"), "track");
1277                         r->add_property (X_("route-id"), atoi (atv->parent_route()->id ().to_s ().c_str()));
1278                         r->add_property (X_("automation-list-id"), atoi ((*i)->line().the_list()->id ().to_s ().c_str()));
1279                         r->add_property (X_("parameter"), EventTypeMap::instance().to_symbol ((*i)->line().the_list()->parameter ()));
1280
1281                         snprintf(buf, sizeof(buf), "%d", (*i)->view_index());
1282                         r->add_property (X_("view-index"), string(buf));
1283                         continue;
1284                 }
1285
1286                 AudioRegionGainLine* argl = dynamic_cast<AudioRegionGainLine*> (&(*i)->line());
1287                 if (argl) {
1288                         XMLNode* r = node->add_child (X_("ControlPoint"));
1289                         r->add_property (X_("type"), "region");
1290                         r->add_property (X_("region-id"), atoi (argl->region_view ().region ()->id ().to_s ().c_str()));
1291                         snprintf(buf, sizeof(buf), "%d", (*i)->view_index());
1292                         r->add_property (X_("view-index"), string(buf));
1293                 }
1294
1295         }
1296
1297         for (TimeSelection::const_iterator i = time.begin(); i != time.end(); ++i) {
1298                 XMLNode* t = node->add_child (X_("AudioRange"));
1299                 snprintf(buf, sizeof(buf), "%" PRId64, (*i).start);
1300                 t->add_property (X_("start"), string(buf));
1301                 snprintf(buf, sizeof(buf), "%" PRId64, (*i).end);
1302                 t->add_property (X_("end"), string(buf));
1303         }
1304
1305         for (MarkerSelection::const_iterator i = markers.begin(); i != markers.end(); ++i) {
1306                 XMLNode* t = node->add_child (X_("Marker"));
1307
1308                 bool is_start;
1309                 Location* loc = editor->find_location_from_marker (*i, is_start);
1310
1311                 t->add_property (X_("id"), atoi (loc->id().to_s().c_str()));
1312                 t->add_property (X_("start"), is_start ? X_("yes") : X_("no"));
1313         }
1314
1315         return *node;
1316 }
1317
1318 int
1319 Selection::set_state (XMLNode const & node, int)
1320 {
1321         if (node.name() != X_("Selection")) {
1322                 return -1;
1323         }
1324
1325         clear_regions ();
1326         clear_midi_notes ();
1327         clear_points ();
1328         clear_time ();
1329         clear_tracks ();
1330         clear_markers ();
1331
1332         XMLNodeList children = node.children ();
1333         for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
1334                 if ((*i)->name() == X_("RouteView")) {
1335
1336                         XMLProperty* prop_id = (*i)->property (X_("id"));
1337                         assert (prop_id);
1338                         PBD::ID id (prop_id->value ());
1339                         RouteTimeAxisView* rtv = editor->get_route_view_by_route_id (id);
1340                         if (rtv) {
1341                                 add (rtv);
1342                         }
1343
1344                 } else if ((*i)->name() == X_("Region")) {
1345                         XMLProperty* prop_id = (*i)->property (X_("id"));
1346                         assert (prop_id);
1347                         PBD::ID id (prop_id->value ());
1348
1349                         RegionSelection rs;
1350                         editor->get_regionviews_by_id (id, rs);
1351
1352                         if (!rs.empty ()) {
1353                                 add (rs);
1354                         } else {
1355                                 /*
1356                                   regionviews haven't been constructed - stash the region IDs
1357                                   so we can identify them in Editor::region_view_added ()
1358                                 */
1359                                 regions.pending.push_back (id);
1360                         }
1361
1362                 } else if ((*i)->name() == X_("MIDINote")) {
1363                         XMLProperty* prop_region_id = (*i)->property (X_("region-id"));
1364
1365                         assert (prop_region_id);
1366
1367                         PBD::ID const id (prop_region_id->value ());
1368                         RegionSelection rs;
1369
1370                         editor->get_regionviews_by_id (id, rs); // there could be more than one
1371
1372                         std::list<boost::shared_ptr<Evoral::Note<Evoral::Beats> > > notes;
1373                         XMLNodeList children = (*i)->children ();
1374
1375                         for (XMLNodeList::const_iterator ci = children.begin(); ci != children.end(); ++ci) {
1376                                 XMLProperty* prop_channel = (*ci)->property (X_("channel"));
1377                                 XMLProperty* prop_time = (*ci)->property (X_("time"));
1378                                 XMLProperty* prop_note = (*ci)->property (X_("note"));
1379                                 XMLProperty* prop_length = (*ci)->property (X_("length"));
1380                                 XMLProperty* prop_velocity = (*ci)->property (X_("velocity"));
1381                                 XMLProperty* prop_off_velocity = (*ci)->property (X_("off-velocity"));
1382
1383                                 assert (prop_channel);
1384                                 assert (prop_time);
1385                                 assert (prop_note);
1386                                 assert (prop_length);
1387                                 assert (prop_velocity);
1388                                 assert (prop_off_velocity);
1389
1390                                 uint8_t channel = atoi(prop_channel->value());
1391                                 Evoral::Beats time (atof(prop_time->value()));
1392                                 Evoral::Beats length (atof(prop_length->value()));
1393                                 uint8_t note = atoi(prop_note->value());
1394                                 uint8_t velocity = atoi(prop_velocity->value());
1395                                 uint8_t off_velocity = atoi(prop_off_velocity->value());
1396                                 boost::shared_ptr<Evoral::Note<Evoral::Beats> > the_note
1397                                         (new Evoral::Note<Evoral::Beats>  (channel, time, length, note, velocity));
1398                                 the_note->set_off_velocity (off_velocity);
1399
1400                                 notes.push_back (the_note);
1401                         }
1402
1403                         for (RegionSelection::iterator rsi = rs.begin(); rsi != rs.end(); ++rsi) {
1404                                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (*rsi);
1405                                 if (mrv) {
1406                                         mrv->select_notes(notes);
1407                                 }
1408                         }
1409
1410                         if (rs.empty()) {
1411                                 /* regionviews containing these notes don't yet exist on the canvas.*/
1412                                 pending_midi_note_selection.push_back (make_pair (id, notes));
1413                         }
1414
1415                 } else if  ((*i)->name() == X_("ControlPoint")) {
1416                         XMLProperty* prop_type = (*i)->property (X_("type"));
1417
1418                         assert(prop_type);
1419
1420                         if (prop_type->value () == "track") {
1421
1422                                 XMLProperty* prop_route_id = (*i)->property (X_("route-id"));
1423                                 XMLProperty* prop_alist_id = (*i)->property (X_("automation-list-id"));
1424                                 XMLProperty* prop_parameter = (*i)->property (X_("parameter"));
1425                                 XMLProperty* prop_view_index = (*i)->property (X_("view-index"));
1426
1427                                 assert (prop_type);
1428                                 assert (prop_route_id);
1429                                 assert (prop_alist_id);
1430                                 assert (prop_parameter);
1431                                 assert (prop_view_index);
1432
1433                                 PBD::ID route_id (prop_route_id->value ());
1434                                 RouteTimeAxisView* rtv = editor->get_route_view_by_route_id (route_id);
1435                                 vector <ControlPoint *> cps;
1436
1437                                 if (rtv) {
1438                                         boost::shared_ptr<AutomationTimeAxisView> atv = rtv->automation_child (EventTypeMap::instance().from_symbol (prop_parameter->value ()));
1439                                         if (atv) {
1440                                                 list<boost::shared_ptr<AutomationLine> > lines = atv->lines();
1441                                                 for (list<boost::shared_ptr<AutomationLine> > ::iterator li = lines.begin(); li != lines.end(); ++li) {
1442                                                         if ((*li)->the_list()->id() == prop_alist_id->value()) {
1443                                                                 ControlPoint* cp = (*li)->nth(atol(prop_view_index->value().c_str()));
1444                                                                 if (cp) {
1445                                                                         cps.push_back (cp);
1446                                                                         cp->show();
1447                                                                 }
1448                                                         }
1449                                                 }
1450                                         }
1451                                 }
1452                                 if (!cps.empty()) {
1453                                         add (cps);
1454                                 }
1455                         } else if (prop_type->value () == "region") {
1456                                 XMLProperty* prop_region_id = (*i)->property (X_("region-id"));
1457                                 XMLProperty* prop_view_index = (*i)->property (X_("view-index"));
1458
1459                                 PBD::ID region_id (prop_region_id->value ());
1460                                 RegionSelection rs;
1461                                 editor->get_regionviews_by_id (region_id, rs);
1462
1463                                 if (!rs.empty ()) {
1464                                         vector <ControlPoint *> cps;
1465                                         for (RegionSelection::iterator rsi = rs.begin(); rsi != rs.end(); ++rsi) {
1466                                                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*rsi);
1467                                                 if (arv) {
1468                                                         boost::shared_ptr<AudioRegionGainLine> gl = arv->get_gain_line ();
1469                                                         ControlPoint* cp = gl->nth(atol(prop_view_index->value().c_str()));
1470                                                         if (cp) {
1471                                                                 cps.push_back (cp);
1472                                                                 cp->show();
1473                                                         }
1474                                                 }
1475                                         }
1476                                         if (!cps.empty()) {
1477                                                 add (cps);
1478                                         }
1479                                 }
1480                         }
1481
1482                 } else if  ((*i)->name() == X_("AudioRange")) {
1483                         XMLProperty* prop_start = (*i)->property (X_("start"));
1484                         XMLProperty* prop_end = (*i)->property (X_("end"));
1485
1486                         assert (prop_start);
1487                         assert (prop_end);
1488
1489                         framepos_t s (atol (prop_start->value ().c_str()));
1490                         framepos_t e (atol (prop_end->value ().c_str()));
1491
1492                         set_preserving_all_ranges (s, e);
1493
1494                 } else if ((*i)->name() == X_("AutomationView")) {
1495
1496                         XMLProperty* prop_id = (*i)->property (X_("id"));
1497                         XMLProperty* prop_parameter = (*i)->property (X_("parameter"));
1498
1499                         assert (prop_id);
1500                         assert (prop_parameter);
1501
1502                         PBD::ID id (prop_id->value ());
1503                         RouteTimeAxisView* rtv = editor->get_route_view_by_route_id (id);
1504
1505                         if (rtv) {
1506                                 boost::shared_ptr<AutomationTimeAxisView> atv = rtv->automation_child (EventTypeMap::instance().from_symbol (prop_parameter->value ()));
1507
1508                                 /* the automation could be for an entity that was never saved
1509                                    in the session file. Don't freak out if we can't find
1510                                    it.
1511                                 */
1512
1513                                 if (atv) {
1514                                         add (atv.get());
1515                                 }
1516                         }
1517
1518                 } else if ((*i)->name() == X_("Marker")) {
1519
1520                         XMLProperty* prop_id = (*i)->property (X_("id"));
1521                         XMLProperty* prop_start = (*i)->property (X_("start"));
1522                         assert (prop_id);
1523                         assert (prop_start);
1524
1525                         PBD::ID id (prop_id->value ());
1526                         ArdourMarker* m = editor->find_marker_from_location_id (id, string_is_affirmative (prop_start->value ()));
1527                         if (m) {
1528                                 add (m);
1529                         }
1530
1531                 }
1532
1533         }
1534
1535         return 0;
1536 }
1537
1538 void
1539 Selection::remove_regions (TimeAxisView* t)
1540 {
1541         RegionSelection::iterator i = regions.begin();
1542         while (i != regions.end ()) {
1543                 RegionSelection::iterator tmp = i;
1544                 ++tmp;
1545
1546                 if (&(*i)->get_time_axis_view() == t) {
1547                         remove (*i);
1548                 }
1549
1550                 i = tmp;
1551         }
1552 }
1553
1554 void
1555 Selection::block_tracks_changed (bool yn)
1556 {
1557         _no_tracks_changed = yn;
1558 }