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