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