first part of MIDI cut/copy/paste ; fix for input/output_streams of an IOProcessor...
[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 "region_view.h"
29 #include "selection.h"
30 #include "selection_templates.h"
31 #include "time_axis_view.h"
32 #include "automation_time_axis.h"
33 #include "public_editor.h"
34
35 #include "i18n.h"
36
37 using namespace std;
38 using namespace ARDOUR;
39 using namespace PBD;
40 using namespace sigc;
41
42 struct AudioRangeComparator {
43     bool operator()(AudioRange a, AudioRange b) {
44             return a.start < b.start;
45     }
46 };
47
48 Selection&
49 Selection::operator= (const Selection& other)
50 {
51         if (&other != this) {
52                 regions = other.regions;
53                 tracks = other.tracks;
54                 time = other.time;
55                 lines = other.lines;
56                 midi = other.midi;
57         }
58         return *this;
59 }
60
61 bool
62 operator== (const Selection& a, const Selection& b)
63 {
64         return a.regions == b.regions &&
65                 a.tracks == b.tracks &&
66                 a.time.track == b.time.track &&
67                 a.time.group == b.time.group && 
68                 a.time == b.time &&
69                 a.lines == b.lines &&
70                 a.playlists == b.playlists &&
71                 a.midi == b.midi;
72 }
73
74 /** Clear everything from the Selection */
75 void
76 Selection::clear ()
77 {
78         clear_tracks ();
79         clear_regions ();
80         clear_points ();
81         clear_lines();
82         clear_time ();
83         clear_playlists ();
84         clear_midi ();
85 }
86
87 void
88 Selection::dump_region_layers()
89 {
90         cerr << "region selection layer dump" << endl;
91         for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
92                 cerr << "layer: " << (int)(*i)->region()->layer() << endl;
93         }
94 }
95
96
97 void
98 Selection::clear_regions ()
99 {
100         if (!regions.empty()) {
101                 regions.clear_all ();
102                 RegionsChanged();
103         }
104 }
105
106 void
107 Selection::clear_tracks ()
108 {
109         if (!tracks.empty()) {
110                 tracks.clear ();
111                 TracksChanged();
112         }
113 }
114
115 void
116 Selection::clear_midi ()
117 {
118         if (!midi.empty()) {
119                 midi.clear ();
120                 MidiChanged ();
121         }
122 }
123
124 void
125 Selection::clear_time ()
126 {
127         time.track = 0;
128         time.group = 0;
129         time.clear();
130
131         TimeChanged ();
132 }
133
134 void
135 Selection::clear_playlists ()
136 {
137         /* Selections own their playlists */
138
139         for (PlaylistSelection::iterator i = playlists.begin(); i != playlists.end(); ++i) {
140                 /* selections own their own regions, which are copies of the "originals". make them go away */
141                 (*i)->drop_regions ();
142                 (*i)->release ();
143         }
144
145         if (!playlists.empty()) {
146                 playlists.clear ();
147                 PlaylistsChanged();
148         }
149 }
150
151 void
152 Selection::clear_lines ()
153 {
154         if (!lines.empty()) {
155                 lines.clear ();
156                 LinesChanged();
157         }
158 }
159
160 void
161 Selection::clear_markers ()
162 {
163         if (!markers.empty()) {
164                 markers.clear ();
165                 MarkersChanged();
166         }
167 }
168
169 void
170 Selection::toggle (boost::shared_ptr<Playlist> pl)
171 {
172         PlaylistSelection::iterator i;
173
174         if ((i = find (playlists.begin(), playlists.end(), pl)) == playlists.end()) {
175                 pl->use ();
176                 playlists.push_back(pl);
177         } else {
178                 playlists.erase (i);
179         }
180
181         PlaylistsChanged ();
182 }
183
184 void
185 Selection::toggle (const list<TimeAxisView*>& track_list)
186 {
187         for (list<TimeAxisView*>::const_iterator i = track_list.begin(); i != track_list.end(); ++i) {
188                 toggle ( (*i) );
189         }
190 }
191
192 void
193 Selection::toggle (TimeAxisView* track)
194 {
195         TrackSelection::iterator i;
196         
197         if ((i = find (tracks.begin(), tracks.end(), track)) == tracks.end()) {
198                 void (Selection::*pmf)(TimeAxisView*) = &Selection::remove;
199                 track->GoingAway.connect (sigc::bind (mem_fun (*this, pmf), track));
200                 tracks.push_back (track);
201         } else {
202                 tracks.erase (i);
203         }
204
205         TracksChanged();
206 }
207
208 void
209 Selection::toggle (RegionView* r)
210 {
211         RegionSelection::iterator i;
212
213         if ((i = find (regions.begin(), regions.end(), r)) == regions.end()) {
214                 add (r);
215         } else {
216                 remove (*i);
217         }
218
219         RegionsChanged ();
220 }
221
222 void
223 Selection::toggle (MidiRegionView* mrv)
224 {
225         MidiSelection::iterator i;
226
227         if ((i = find (midi.begin(), midi.end(), mrv)) == midi.end()) {
228                 add (mrv);
229         } else {
230                 midi.erase (i);
231         }
232
233         MidiChanged ();
234 }
235
236 void
237 Selection::toggle (vector<RegionView*>& r)
238 {
239         RegionSelection::iterator i;
240
241         for (vector<RegionView*>::iterator x = r.begin(); x != r.end(); ++x) {
242                 if ((i = find (regions.begin(), regions.end(), (*x))) == regions.end()) {
243                         add ((*x));
244                 } else {
245                         remove (*x);
246                 }
247         }
248
249         RegionsChanged ();
250 }
251
252 long
253 Selection::toggle (nframes_t start, nframes_t end)
254 {
255         AudioRangeComparator cmp;
256
257         /* XXX this implementation is incorrect */
258
259         time.push_back (AudioRange (start, end, next_time_id++));
260         time.consolidate ();
261         time.sort (cmp);
262         
263         TimeChanged ();
264
265         return next_time_id - 1;
266 }
267
268 void
269 Selection::add (boost::shared_ptr<Playlist> pl)
270 {
271         if (find (playlists.begin(), playlists.end(), pl) == playlists.end()) {
272                 pl->use ();
273                 playlists.push_back(pl);
274                 PlaylistsChanged ();
275         }
276 }
277
278 void
279 Selection::add (const list<boost::shared_ptr<Playlist> >& pllist)
280 {
281         bool changed = false;
282
283         for (list<boost::shared_ptr<Playlist> >::const_iterator i = pllist.begin(); i != pllist.end(); ++i) {
284                 if (find (playlists.begin(), playlists.end(), (*i)) == playlists.end()) {
285                         (*i)->use ();
286                         playlists.push_back (*i);
287                         changed = true;
288                 }
289         }
290         
291         if (changed) {
292                 PlaylistsChanged ();
293         }
294 }
295
296 void
297 Selection::add (const list<TimeAxisView*>& track_list)
298 {
299         list<TimeAxisView*> added = tracks.add (track_list);
300
301         for (list<TimeAxisView*>::const_iterator i = added.begin(); i != added.end(); ++i) {
302                 void (Selection::*pmf)(TimeAxisView*) = &Selection::remove;
303                 (*i)->GoingAway.connect (sigc::bind (mem_fun (*this, pmf), (*i)));
304         }
305         
306         if (!added.empty()) {
307                 TracksChanged ();
308         }
309 }
310
311 void
312 Selection::add (TimeAxisView* track)
313 {
314         if (find (tracks.begin(), tracks.end(), track) == tracks.end()) {
315                 void (Selection::*pmf)(TimeAxisView*) = &Selection::remove;
316                 track->GoingAway.connect (sigc::bind (mem_fun (*this, pmf), track));
317                 tracks.push_back (track);
318                 TracksChanged();
319         }
320 }
321
322 void
323 Selection::add (vector<RegionView*>& v)
324 {
325         /* XXX This method or the add (const RegionSelection&) needs to go
326          */
327
328         bool changed = false;
329         
330         for (vector<RegionView*>::iterator i = v.begin(); i != v.end(); ++i) {
331                 if (find (regions.begin(), regions.end(), (*i)) == regions.end()) {
332                         changed = regions.add ((*i));
333                         if (Config->get_link_region_and_track_selection() && changed) {
334                                 add (&(*i)->get_trackview());
335                         }
336                 }
337         }
338
339         if (changed) {
340                 RegionsChanged ();
341         }
342 }
343
344 void
345 Selection::add (const RegionSelection& rs)
346 {
347         /* XXX This method or the add (const vector<RegionView*>&) needs to go
348          */
349
350         bool changed = false;
351         
352         for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
353                 if (find (regions.begin(), regions.end(), (*i)) == regions.end()) {
354                         changed = regions.add ((*i));
355                         if (Config->get_link_region_and_track_selection() && changed) {
356                                 add (&(*i)->get_trackview());
357                         }
358                 }
359         }
360         
361         if (changed) {
362                 RegionsChanged ();
363         }
364 }
365
366 void
367 Selection::add (RegionView* r)
368 {
369         if (find (regions.begin(), regions.end(), r) == regions.end()) {
370                 regions.add (r);
371                 if (Config->get_link_region_and_track_selection()) {
372                         add (&r->get_trackview());
373                 }
374                 RegionsChanged ();
375         }
376 }
377
378 void
379 Selection::add (MidiRegionView* mrv)
380 {
381         if (find (midi.begin(), midi.end(), mrv) == midi.end()) {
382                 midi.push_back (mrv);
383                 /* XXX should we do this? */
384 #if 0
385                 if (Config->get_link_region_and_track_selection()) {
386                         add (&mrv->get_trackview());
387                 }
388 #endif
389                 MidiChanged ();
390         }
391 }
392
393 long
394 Selection::add (nframes_t start, nframes_t end)
395 {
396         AudioRangeComparator cmp;
397
398         /* XXX this implementation is incorrect */
399
400         time.push_back (AudioRange (start, end, next_time_id++));
401         time.consolidate ();
402         time.sort (cmp);
403         
404         TimeChanged ();
405
406         return next_time_id - 1;
407 }
408
409 void
410 Selection::replace (uint32_t sid, nframes_t start, nframes_t end)
411 {
412         for (list<AudioRange>::iterator i = time.begin(); i != time.end(); ++i) {
413                 if ((*i).id == sid) {
414                         time.erase (i);
415                         time.push_back (AudioRange(start,end, sid));
416
417                         /* don't consolidate here */
418
419
420                         AudioRangeComparator cmp;
421                         time.sort (cmp);
422
423                         TimeChanged ();
424                         break;
425                 }
426         }
427 }
428
429 void
430 Selection::add (boost::shared_ptr<Evoral::ControlList> cl)
431 {
432         boost::shared_ptr<ARDOUR::AutomationList> al
433                 = boost::dynamic_pointer_cast<ARDOUR::AutomationList>(cl);
434         if (!al) {
435                 warning << "Programming error: Selected list is not an ARDOUR::AutomationList" << endmsg;
436                 return;
437                 return;
438         }
439         if (find (lines.begin(), lines.end(), al) == lines.end()) {
440                 lines.push_back (al);
441                 LinesChanged();
442         }
443 }
444
445 void
446 Selection::remove (TimeAxisView* track)
447 {
448         list<TimeAxisView*>::iterator i;
449         if ((i = find (tracks.begin(), tracks.end(), track)) != tracks.end()) {
450                 tracks.erase (i);
451                 TracksChanged();
452         }
453 }
454
455 void
456 Selection::remove (const list<TimeAxisView*>& track_list)
457 {
458         bool changed = false;
459
460         for (list<TimeAxisView*>::const_iterator i = track_list.begin(); i != track_list.end(); ++i) {
461
462                 list<TimeAxisView*>::iterator x;
463
464                 if ((x = find (tracks.begin(), tracks.end(), (*i))) != tracks.end()) {
465                         tracks.erase (x);
466                         changed = true;
467                 }
468         }
469
470         if (changed) {
471                 TracksChanged();
472         }
473 }
474
475 void
476 Selection::remove (boost::shared_ptr<Playlist> track)
477 {
478         list<boost::shared_ptr<Playlist> >::iterator i;
479         if ((i = find (playlists.begin(), playlists.end(), track)) != playlists.end()) {
480                 playlists.erase (i);
481                 PlaylistsChanged();
482         }
483 }
484
485 void
486 Selection::remove (const list<boost::shared_ptr<Playlist> >& pllist)
487 {
488         bool changed = false;
489
490         for (list<boost::shared_ptr<Playlist> >::const_iterator i = pllist.begin(); i != pllist.end(); ++i) {
491
492                 list<boost::shared_ptr<Playlist> >::iterator x;
493
494                 if ((x = find (playlists.begin(), playlists.end(), (*i))) != playlists.end()) {
495                         playlists.erase (x);
496                         changed = true;
497                 }
498         }
499
500         if (changed) {
501                 PlaylistsChanged();
502         }
503 }
504
505 void
506 Selection::remove (RegionView* r)
507 {
508         if (regions.remove (r)) {
509                 RegionsChanged ();
510         }
511
512         if (Config->get_link_region_and_track_selection() && !regions.involves (r->get_trackview())) {
513                 remove (&r->get_trackview());
514         }
515 }
516
517 void
518 Selection::remove (MidiRegionView* mrv)
519 {
520         MidiSelection::iterator x;
521
522         if ((x = find (midi.begin(), midi.end(), mrv)) != midi.end()) {
523                 midi.erase (x);
524                 MidiChanged ();
525         }
526
527 #if 0
528         /* XXX fix this up ? */
529         if (Config->get_link_region_and_track_selection() && !regions.involves (r->get_trackview())) {
530                 remove (&r->get_trackview());
531         }
532 #endif
533 }
534
535
536 void
537 Selection::remove (uint32_t selection_id)
538 {
539         if (time.empty()) {
540                 return;
541         }
542
543         for (list<AudioRange>::iterator i = time.begin(); i != time.end(); ++i) {
544                 if ((*i).id == selection_id) {
545                         time.erase (i);
546                                                 
547                         TimeChanged ();
548                         break;
549                 }
550         }
551 }
552
553 void
554 Selection::remove (nframes_t /*start*/, nframes_t /*end*/)
555 {
556 }
557
558 void
559 Selection::remove (boost::shared_ptr<ARDOUR::AutomationList> ac)
560 {
561         AutomationSelection::iterator i;
562         if ((i = find (lines.begin(), lines.end(), ac)) != lines.end()) {
563                 lines.erase (i);
564                 LinesChanged();
565         }
566 }
567
568 void
569 Selection::set (TimeAxisView* track)
570 {
571         clear_tracks ();
572         add (track);
573 }
574
575 void
576 Selection::set (const list<TimeAxisView*>& track_list)
577 {
578         clear_tracks ();
579         add (track_list);
580 }
581
582 void
583 Selection::set (boost::shared_ptr<Playlist> playlist)
584 {
585         clear_playlists ();
586         add (playlist);
587 }
588
589 void
590 Selection::set (const list<boost::shared_ptr<Playlist> >& pllist)
591 {
592         clear_playlists ();
593         add (pllist);
594 }
595
596 void
597 Selection::set (const RegionSelection& rs)
598 {
599         clear_regions();
600         regions = rs;
601         RegionsChanged(); /* EMIT SIGNAL */
602 }
603
604 void
605 Selection::set (MidiRegionView* mrv) 
606 {
607         clear_midi ();
608         add (mrv);
609 }
610
611 void
612 Selection::set (RegionView* r, bool also_clear_tracks)
613 {
614         clear_regions ();
615         if (also_clear_tracks) {
616                 clear_tracks ();
617         }
618         add (r);
619 }
620
621 void
622 Selection::set (vector<RegionView*>& v)
623 {
624         clear_regions ();
625         if (Config->get_link_region_and_track_selection()) {
626                 clear_tracks ();
627                 // make sure to deselect any automation selections
628                 clear_points();
629         }
630         add (v);
631 }
632
633 long
634 Selection::set (TimeAxisView* track, nframes_t start, nframes_t end)
635 {
636         if ((start == 0 && end == 0) || end < start) {
637                 return 0;
638         }
639
640         if (time.empty()) {
641                 time.push_back (AudioRange (start, end, next_time_id++));
642         } else {
643                 /* reuse the first entry, and remove all the rest */
644
645                 while (time.size() > 1) {
646                         time.pop_front();
647                 }
648                 time.front().start = start;
649                 time.front().end = end;
650         }
651
652         if (track) {
653                 time.track = track;
654                 time.group = track->route_group();
655         } else {
656                 time.track = 0;
657                 time.group = 0;
658         }
659
660         time.consolidate ();
661
662         TimeChanged ();
663
664         return time.front().id;
665 }
666
667 void
668 Selection::set (boost::shared_ptr<Evoral::ControlList> ac)
669 {
670         lines.clear();
671         add (ac);
672 }
673
674 bool
675 Selection::selected (Marker* m)
676 {
677         return find (markers.begin(), markers.end(), m) != markers.end();
678 }
679
680 bool
681 Selection::selected (TimeAxisView* tv)
682 {
683         return find (tracks.begin(), tracks.end(), tv) != tracks.end();
684 }
685
686 bool
687 Selection::selected (RegionView* rv)
688 {
689         return find (regions.begin(), regions.end(), rv) != regions.end();
690 }
691
692 bool
693 Selection::empty ()
694 {
695         return regions.empty () &&
696                 tracks.empty () &&
697                 points.empty () && 
698                 playlists.empty () && 
699                 lines.empty () &&
700                 time.empty () &&
701                 playlists.empty () &&
702                 markers.empty()
703                 ;
704 }
705
706 void
707 Selection::toggle (const vector<AutomationSelectable*>& autos)
708 {
709         for (vector<AutomationSelectable*>::const_iterator x = autos.begin(); x != autos.end(); ++x) {
710                 if ((*x)->get_selected()) {
711                         points.remove (**x);
712                 } else {
713                         points.push_back (**x);
714                 }
715
716                 delete *x;
717         }
718
719         PointsChanged (); /* EMIT SIGNAL */
720 }
721
722 void
723 Selection::toggle (list<Selectable*>& selectables)
724 {
725         RegionView* rv;
726         AutomationSelectable* as;
727         vector<RegionView*> rvs;
728         vector<AutomationSelectable*> autos;
729
730         for (std::list<Selectable*>::iterator i = selectables.begin(); i != selectables.end(); ++i) {
731                 if ((rv = dynamic_cast<RegionView*> (*i)) != 0) {
732                         rvs.push_back (rv);
733                 } else if ((as = dynamic_cast<AutomationSelectable*> (*i)) != 0) {
734                         autos.push_back (as);
735                 } else {
736                         fatal << _("programming error: ")
737                               << X_("unknown selectable type passed to Selection::toggle()")
738                               << endmsg;
739                         /*NOTREACHED*/
740                 }
741         }
742
743         if (!rvs.empty()) {
744                 toggle (rvs);
745         } 
746
747         if (!autos.empty()) {
748                 toggle (autos);
749         } 
750 }
751
752 void
753 Selection::set (list<Selectable*>& selectables)
754 {
755         clear_regions();
756         clear_points ();
757         add (selectables);
758 }
759
760
761 void
762 Selection::add (list<Selectable*>& selectables)
763 {
764         RegionView* rv;
765         AutomationSelectable* as;
766         vector<RegionView*> rvs;
767         vector<AutomationSelectable*> autos;
768
769         for (std::list<Selectable*>::iterator i = selectables.begin(); i != selectables.end(); ++i) {
770                 if ((rv = dynamic_cast<RegionView*> (*i)) != 0) {
771                         rvs.push_back (rv);
772                 } else if ((as = dynamic_cast<AutomationSelectable*> (*i)) != 0) {
773                         autos.push_back (as);
774                 } else {
775                         fatal << _("programming error: ")
776                               << X_("unknown selectable type passed to Selection::add()")
777                               << endmsg;
778                         /*NOTREACHED*/
779                 }
780         }
781
782         if (!rvs.empty()) {
783                 add (rvs);
784         } 
785
786         if (!autos.empty()) {
787                 add (autos);
788         } 
789 }
790
791 void
792 Selection::clear_points ()
793 {
794         if (!points.empty()) {
795                 points.clear ();
796                 PointsChanged ();
797         }
798 }
799
800 void
801 Selection::add (vector<AutomationSelectable*>& autos)
802 {
803         for (vector<AutomationSelectable*>::iterator i = autos.begin(); i != autos.end(); ++i) {
804                 points.push_back (**i);
805         }
806
807         PointsChanged ();
808 }
809
810 void
811 Selection::set (Marker* m)
812 {
813         clear_markers ();
814         add (m);
815 }
816
817 void
818 Selection::toggle (Marker* m)
819 {
820         MarkerSelection::iterator i;
821         
822         if ((i = find (markers.begin(), markers.end(), m)) == markers.end()) {
823                 add (m);
824         } else {
825                 remove (m);
826         }
827 }
828
829 void
830 Selection::remove (Marker* m)
831 {
832         MarkerSelection::iterator i;
833
834         if ((i = find (markers.begin(), markers.end(), m)) != markers.end()) {
835                 markers.erase (i);
836                 MarkersChanged();
837         }
838 }
839
840 void
841 Selection::add (Marker* m)
842 {
843         if (find (markers.begin(), markers.end(), m) == markers.end()) {
844
845                 /* disambiguate which remove() for the compiler */
846                 
847                 void (Selection::*pmf)(Marker*) = &Selection::remove;
848
849                 m->GoingAway.connect (bind (mem_fun (*this, pmf), m));
850
851                 markers.push_back (m);
852                 MarkersChanged();
853         }
854 }
855
856 void
857 Selection::add (const list<Marker*>& m)
858 {
859         markers.insert (markers.end(), m.begin(), m.end());
860         MarkersChanged ();
861 }
862
863 void
864 MarkerSelection::range (nframes64_t& s, nframes64_t& e)
865 {
866         s = max_frames;
867         e = 0;
868
869         for (MarkerSelection::iterator i = begin(); i != end(); ++i) {
870
871                 if ((*i)->position() < s) {
872                         s = (*i)->position();
873                 } 
874
875                 if ((*i)->position() > e) {
876                         e = (*i)->position();
877                 }
878         }
879
880         s = std::min (s, e);
881         e = std::max (s, e);
882 }