Factor out sequencing related things into an independant new library: "evoral".
[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 (TimeAxisView* tv)
613 {
614         return find (tracks.begin(), tracks.end(), tv) != tracks.end();
615 }
616
617 bool
618 Selection::selected (RegionView* rv)
619 {
620         return find (regions.begin(), regions.end(), rv) != regions.end();
621 }
622
623 bool
624 Selection::empty ()
625 {
626         return regions.empty () &&
627                 tracks.empty () &&
628                 points.empty () && 
629                 playlists.empty () && 
630                 lines.empty () &&
631                 time.empty () &&
632                 playlists.empty () &&
633                 markers.empty()
634                 ;
635 }
636
637 void
638 Selection::toggle (const vector<AutomationSelectable*>& autos)
639 {
640         for (vector<AutomationSelectable*>::const_iterator x = autos.begin(); x != autos.end(); ++x) {
641                 if ((*x)->get_selected()) {
642                         points.remove (**x);
643                 } else {
644                         points.push_back (**x);
645                 }
646
647                 delete *x;
648         }
649
650         PointsChanged (); /* EMIT SIGNAL */
651 }
652
653 void
654 Selection::toggle (list<Selectable*>& selectables)
655 {
656         RegionView* rv;
657         AutomationSelectable* as;
658         vector<RegionView*> rvs;
659         vector<AutomationSelectable*> autos;
660
661         for (std::list<Selectable*>::iterator i = selectables.begin(); i != selectables.end(); ++i) {
662                 if ((rv = dynamic_cast<RegionView*> (*i)) != 0) {
663                         rvs.push_back (rv);
664                 } else if ((as = dynamic_cast<AutomationSelectable*> (*i)) != 0) {
665                         autos.push_back (as);
666                 } else {
667                         fatal << _("programming error: ")
668                               << X_("unknown selectable type passed to Selection::toggle()")
669                               << endmsg;
670                         /*NOTREACHED*/
671                 }
672         }
673
674         if (!rvs.empty()) {
675                 toggle (rvs);
676         } 
677
678         if (!autos.empty()) {
679                 toggle (autos);
680         } 
681 }
682
683 void
684 Selection::set (list<Selectable*>& selectables)
685 {
686         clear_regions();
687         clear_points ();
688         add (selectables);
689 }
690
691
692 void
693 Selection::add (list<Selectable*>& selectables)
694 {
695         RegionView* rv;
696         AutomationSelectable* as;
697         vector<RegionView*> rvs;
698         vector<AutomationSelectable*> autos;
699
700         for (std::list<Selectable*>::iterator i = selectables.begin(); i != selectables.end(); ++i) {
701                 if ((rv = dynamic_cast<RegionView*> (*i)) != 0) {
702                         rvs.push_back (rv);
703                 } else if ((as = dynamic_cast<AutomationSelectable*> (*i)) != 0) {
704                         autos.push_back (as);
705                 } else {
706                         fatal << _("programming error: ")
707                               << X_("unknown selectable type passed to Selection::add()")
708                               << endmsg;
709                         /*NOTREACHED*/
710                 }
711         }
712
713         if (!rvs.empty()) {
714                 add (rvs);
715         } 
716
717         if (!autos.empty()) {
718                 add (autos);
719         } 
720 }
721
722 void
723 Selection::clear_points ()
724 {
725         if (!points.empty()) {
726                 points.clear ();
727                 PointsChanged ();
728         }
729 }
730
731 void
732 Selection::add (vector<AutomationSelectable*>& autos)
733 {
734         for (vector<AutomationSelectable*>::iterator i = autos.begin(); i != autos.end(); ++i) {
735                 points.push_back (**i);
736         }
737
738         PointsChanged ();
739 }
740
741 void
742 Selection::select_edit_group_regions ()
743 {
744         std::set<RegionView*> regions_to_add;
745         
746         for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
747                 vector<RegionView*> e;
748                 editor->get_equivalent_regions (*i, e);
749                 for (vector<RegionView*>::iterator j = e.begin(); j != e.end(); ++j) {
750                         regions_to_add.insert(*j);
751                 }
752         }
753
754         for (std::set<RegionView*>::iterator i = regions_to_add.begin(); i != regions_to_add.end(); ++i) {
755                 add (*i);
756         }
757 }
758
759 void
760 Selection::set (Marker* m)
761 {
762         clear_markers ();
763         add (m);
764 }
765
766 void
767 Selection::toggle (Marker* m)
768 {
769         MarkerSelection::iterator i;
770         
771         if ((i = find (markers.begin(), markers.end(), m)) == markers.end()) {
772                 add (m);
773         } else {
774                 remove (m);
775         }
776 }
777
778 void
779 Selection::remove (Marker* m)
780 {
781         MarkerSelection::iterator i;
782
783         if ((i = find (markers.begin(), markers.end(), m)) != markers.end()) {
784                 markers.erase (i);
785                 MarkersChanged();
786         }
787 }
788
789 void
790 Selection::add (Marker* m)
791 {
792         if (find (markers.begin(), markers.end(), m) == markers.end()) {
793
794                 /* disambiguate which remove() for the compiler */
795                 
796                 void (Selection::*pmf)(Marker*) = &Selection::remove;
797
798                 m->GoingAway.connect (bind (mem_fun (*this, pmf), m));
799
800                 markers.push_back (m);
801                 MarkersChanged();
802         }
803 }