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