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