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