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