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