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