First stage of options rework.
[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         bool changed = false;
274
275         for (list<TimeAxisView*>::const_iterator i = track_list.begin(); i != track_list.end(); ++i) {
276                 if (find (tracks.begin(), tracks.end(), (*i)) == tracks.end()) {
277                         void (Selection::*pmf)(TimeAxisView*) = &Selection::remove;
278                         (*i)->GoingAway.connect (sigc::bind (mem_fun (*this, pmf), (*i)));
279                         tracks.push_back (*i);
280                         changed = true;
281                 }
282         }
283         
284         if (changed) {
285                 TracksChanged ();
286         }
287 }
288
289 void
290 Selection::add (TimeAxisView* track)
291 {
292         if (find (tracks.begin(), tracks.end(), track) == tracks.end()) {
293                 void (Selection::*pmf)(TimeAxisView*) = &Selection::remove;
294                 track->GoingAway.connect (sigc::bind (mem_fun (*this, pmf), track));
295                 tracks.push_back (track);
296                 TracksChanged();
297         }
298 }
299
300 void
301 Selection::add (vector<RegionView*>& v)
302 {
303         /* XXX This method or the add (const RegionSelection&) needs to go
304          */
305
306         bool changed = false;
307         
308         for (vector<RegionView*>::iterator i = v.begin(); i != v.end(); ++i) {
309                 if (find (regions.begin(), regions.end(), (*i)) == regions.end()) {
310                         changed = regions.add ((*i));
311                         if (Config->get_link_region_and_track_selection() && changed) {
312                                 add (&(*i)->get_trackview());
313                         }
314                 }
315         }
316
317         if (changed) {
318                 RegionsChanged ();
319         }
320 }
321
322 void
323 Selection::add (const RegionSelection& rs)
324 {
325         /* XXX This method or the add (const vector<RegionView*>&) needs to go
326          */
327
328         bool changed = false;
329         
330         for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
331                 if (find (regions.begin(), regions.end(), (*i)) == regions.end()) {
332                         changed = regions.add ((*i));
333                         if (Config->get_link_region_and_track_selection() && changed) {
334                                 add (&(*i)->get_trackview());
335                         }
336                 }
337         }
338         
339         if (changed) {
340                 select_edit_group_regions ();
341                 RegionsChanged ();
342         }
343 }
344
345 void
346 Selection::add (RegionView* r)
347 {
348         if (find (regions.begin(), regions.end(), r) == regions.end()) {
349                 regions.add (r);
350                 if (Config->get_link_region_and_track_selection()) {
351                         add (&r->get_trackview());
352                 }
353                 RegionsChanged ();
354         }
355 }
356
357 long
358 Selection::add (nframes_t start, nframes_t end)
359 {
360         AudioRangeComparator cmp;
361
362         /* XXX this implementation is incorrect */
363
364         time.push_back (AudioRange (start, end, next_time_id++));
365         time.consolidate ();
366         time.sort (cmp);
367         
368         TimeChanged ();
369
370         return next_time_id - 1;
371 }
372
373 void
374 Selection::replace (uint32_t sid, nframes_t start, nframes_t end)
375 {
376         for (list<AudioRange>::iterator i = time.begin(); i != time.end(); ++i) {
377                 if ((*i).id == sid) {
378                         time.erase (i);
379                         time.push_back (AudioRange(start,end, sid));
380
381                         /* don't consolidate here */
382
383
384                         AudioRangeComparator cmp;
385                         time.sort (cmp);
386
387                         TimeChanged ();
388                         break;
389                 }
390         }
391 }
392
393 void
394 Selection::add (boost::shared_ptr<Evoral::ControlList> cl)
395 {
396         boost::shared_ptr<ARDOUR::AutomationList> al
397                 = boost::dynamic_pointer_cast<ARDOUR::AutomationList>(cl);
398         if (!al) {
399                 warning << "Programming error: Selected list is not an ARDOUR::AutomationList" << endmsg;
400                 return;
401                 return;
402         }
403         if (find (lines.begin(), lines.end(), al) == lines.end()) {
404                 lines.push_back (al);
405                 LinesChanged();
406         }
407 }
408
409 void
410 Selection::remove (TimeAxisView* track)
411 {
412         list<TimeAxisView*>::iterator i;
413         if ((i = find (tracks.begin(), tracks.end(), track)) != tracks.end()) {
414                 tracks.erase (i);
415                 TracksChanged();
416         }
417 }
418
419 void
420 Selection::remove (const list<TimeAxisView*>& track_list)
421 {
422         bool changed = false;
423
424         for (list<TimeAxisView*>::const_iterator i = track_list.begin(); i != track_list.end(); ++i) {
425
426                 list<TimeAxisView*>::iterator x;
427
428                 if ((x = find (tracks.begin(), tracks.end(), (*i))) != tracks.end()) {
429                         tracks.erase (x);
430                         changed = true;
431                 }
432         }
433
434         if (changed) {
435                 TracksChanged();
436         }
437 }
438
439 void
440 Selection::remove (boost::shared_ptr<Playlist> track)
441 {
442         list<boost::shared_ptr<Playlist> >::iterator i;
443         if ((i = find (playlists.begin(), playlists.end(), track)) != playlists.end()) {
444                 playlists.erase (i);
445                 PlaylistsChanged();
446         }
447 }
448
449 void
450 Selection::remove (const list<boost::shared_ptr<Playlist> >& pllist)
451 {
452         bool changed = false;
453
454         for (list<boost::shared_ptr<Playlist> >::const_iterator i = pllist.begin(); i != pllist.end(); ++i) {
455
456                 list<boost::shared_ptr<Playlist> >::iterator x;
457
458                 if ((x = find (playlists.begin(), playlists.end(), (*i))) != playlists.end()) {
459                         playlists.erase (x);
460                         changed = true;
461                 }
462         }
463
464         if (changed) {
465                 PlaylistsChanged();
466         }
467 }
468
469 void
470 Selection::remove (RegionView* r)
471 {
472         if (regions.remove (r)) {
473                 RegionsChanged ();
474         }
475
476         if (Config->get_link_region_and_track_selection() && !regions.involves (r->get_trackview())) {
477                 remove (&r->get_trackview());
478         }
479 }
480
481
482 void
483 Selection::remove (uint32_t selection_id)
484 {
485         if (time.empty()) {
486                 return;
487         }
488
489         for (list<AudioRange>::iterator i = time.begin(); i != time.end(); ++i) {
490                 if ((*i).id == selection_id) {
491                         time.erase (i);
492                                                 
493                         TimeChanged ();
494                         break;
495                 }
496         }
497 }
498
499 void
500 Selection::remove (nframes_t start, nframes_t end)
501 {
502 }
503
504 void
505 Selection::remove (boost::shared_ptr<ARDOUR::AutomationList> ac)
506 {
507         AutomationSelection::iterator i;
508         if ((i = find (lines.begin(), lines.end(), ac)) != lines.end()) {
509                 lines.erase (i);
510                 LinesChanged();
511         }
512 }
513
514 void
515 Selection::set (TimeAxisView* track)
516 {
517         clear_tracks ();
518         add (track);
519 }
520
521 void
522 Selection::set (const list<TimeAxisView*>& track_list)
523 {
524         clear_tracks ();
525         add (track_list);
526 }
527
528 void
529 Selection::set (boost::shared_ptr<Playlist> playlist)
530 {
531         clear_playlists ();
532         add (playlist);
533 }
534
535 void
536 Selection::set (const list<boost::shared_ptr<Playlist> >& pllist)
537 {
538         clear_playlists ();
539         add (pllist);
540 }
541
542 void
543 Selection::set (const RegionSelection& rs)
544 {
545         clear_regions();
546         regions = rs;
547         RegionsChanged(); /* EMIT SIGNAL */
548 }
549
550 void
551 Selection::set (RegionView* r, bool also_clear_tracks)
552 {
553         clear_regions ();
554         if (also_clear_tracks) {
555                 clear_tracks ();
556         }
557         add (r);
558 }
559
560 void
561 Selection::set (vector<RegionView*>& v)
562 {
563         clear_regions ();
564         if (Config->get_link_region_and_track_selection()) {
565                 clear_tracks ();
566                 // make sure to deselect any automation selections
567                 clear_points();
568         }
569         add (v);
570 }
571
572 long
573 Selection::set (TimeAxisView* track, nframes_t start, nframes_t end)
574 {
575         if ((start == 0 && end == 0) || end < start) {
576                 return 0;
577         }
578
579         if (time.empty()) {
580                 time.push_back (AudioRange (start, end, next_time_id++));
581         } else {
582                 /* reuse the first entry, and remove all the rest */
583
584                 while (time.size() > 1) {
585                         time.pop_front();
586                 }
587                 time.front().start = start;
588                 time.front().end = end;
589         }
590
591         if (track) {
592                 time.track = track;
593                 time.group = track->edit_group();
594         } else {
595                 time.track = 0;
596                 time.group = 0;
597         }
598
599         time.consolidate ();
600
601         TimeChanged ();
602
603         return time.front().id;
604 }
605
606 void
607 Selection::set (boost::shared_ptr<Evoral::ControlList> ac)
608 {
609         lines.clear();
610         add (ac);
611 }
612
613 bool
614 Selection::selected (Marker* m)
615 {
616         return find (markers.begin(), markers.end(), m) != markers.end();
617 }
618
619 bool
620 Selection::selected (TimeAxisView* tv)
621 {
622         return find (tracks.begin(), tracks.end(), tv) != tracks.end();
623 }
624
625 bool
626 Selection::selected (RegionView* rv)
627 {
628         return find (regions.begin(), regions.end(), rv) != regions.end();
629 }
630
631 bool
632 Selection::empty ()
633 {
634         return regions.empty () &&
635                 tracks.empty () &&
636                 points.empty () && 
637                 playlists.empty () && 
638                 lines.empty () &&
639                 time.empty () &&
640                 playlists.empty () &&
641                 markers.empty()
642                 ;
643 }
644
645 void
646 Selection::toggle (const vector<AutomationSelectable*>& autos)
647 {
648         for (vector<AutomationSelectable*>::const_iterator x = autos.begin(); x != autos.end(); ++x) {
649                 if ((*x)->get_selected()) {
650                         points.remove (**x);
651                 } else {
652                         points.push_back (**x);
653                 }
654
655                 delete *x;
656         }
657
658         PointsChanged (); /* EMIT SIGNAL */
659 }
660
661 void
662 Selection::toggle (list<Selectable*>& selectables)
663 {
664         RegionView* rv;
665         AutomationSelectable* as;
666         vector<RegionView*> rvs;
667         vector<AutomationSelectable*> autos;
668
669         for (std::list<Selectable*>::iterator i = selectables.begin(); i != selectables.end(); ++i) {
670                 if ((rv = dynamic_cast<RegionView*> (*i)) != 0) {
671                         rvs.push_back (rv);
672                 } else if ((as = dynamic_cast<AutomationSelectable*> (*i)) != 0) {
673                         autos.push_back (as);
674                 } else {
675                         fatal << _("programming error: ")
676                               << X_("unknown selectable type passed to Selection::toggle()")
677                               << endmsg;
678                         /*NOTREACHED*/
679                 }
680         }
681
682         if (!rvs.empty()) {
683                 toggle (rvs);
684         } 
685
686         if (!autos.empty()) {
687                 toggle (autos);
688         } 
689 }
690
691 void
692 Selection::set (list<Selectable*>& selectables)
693 {
694         clear_regions();
695         clear_points ();
696         add (selectables);
697 }
698
699
700 void
701 Selection::add (list<Selectable*>& selectables)
702 {
703         RegionView* rv;
704         AutomationSelectable* as;
705         vector<RegionView*> rvs;
706         vector<AutomationSelectable*> autos;
707
708         for (std::list<Selectable*>::iterator i = selectables.begin(); i != selectables.end(); ++i) {
709                 if ((rv = dynamic_cast<RegionView*> (*i)) != 0) {
710                         rvs.push_back (rv);
711                 } else if ((as = dynamic_cast<AutomationSelectable*> (*i)) != 0) {
712                         autos.push_back (as);
713                 } else {
714                         fatal << _("programming error: ")
715                               << X_("unknown selectable type passed to Selection::add()")
716                               << endmsg;
717                         /*NOTREACHED*/
718                 }
719         }
720
721         if (!rvs.empty()) {
722                 add (rvs);
723         } 
724
725         if (!autos.empty()) {
726                 add (autos);
727         } 
728 }
729
730 void
731 Selection::clear_points ()
732 {
733         if (!points.empty()) {
734                 points.clear ();
735                 PointsChanged ();
736         }
737 }
738
739 void
740 Selection::add (vector<AutomationSelectable*>& autos)
741 {
742         for (vector<AutomationSelectable*>::iterator i = autos.begin(); i != autos.end(); ++i) {
743                 points.push_back (**i);
744         }
745
746         PointsChanged ();
747 }
748
749 void
750 Selection::select_edit_group_regions ()
751 {
752         std::set<RegionView*> regions_to_add;
753         
754         for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
755                 vector<RegionView*> e;
756                 editor->get_equivalent_regions (*i, e);
757                 for (vector<RegionView*>::iterator j = e.begin(); j != e.end(); ++j) {
758                         regions_to_add.insert(*j);
759                 }
760         }
761
762         for (std::set<RegionView*>::iterator i = regions_to_add.begin(); i != regions_to_add.end(); ++i) {
763                 add (*i);
764         }
765 }
766
767 void
768 Selection::set (Marker* m)
769 {
770         clear_markers ();
771         add (m);
772 }
773
774 void
775 Selection::toggle (Marker* m)
776 {
777         MarkerSelection::iterator i;
778         
779         if ((i = find (markers.begin(), markers.end(), m)) == markers.end()) {
780                 add (m);
781         } else {
782                 remove (m);
783         }
784 }
785
786 void
787 Selection::remove (Marker* m)
788 {
789         MarkerSelection::iterator i;
790
791         if ((i = find (markers.begin(), markers.end(), m)) != markers.end()) {
792                 markers.erase (i);
793                 MarkersChanged();
794         }
795 }
796
797 void
798 Selection::add (Marker* m)
799 {
800         if (find (markers.begin(), markers.end(), m) == markers.end()) {
801
802                 /* disambiguate which remove() for the compiler */
803                 
804                 void (Selection::*pmf)(Marker*) = &Selection::remove;
805
806                 m->GoingAway.connect (bind (mem_fun (*this, pmf), m));
807
808                 markers.push_back (m);
809                 MarkersChanged();
810         }
811 }
812
813 void
814 Selection::add (const list<Marker*>& m)
815 {
816         markers.insert (markers.end(), m.begin(), m.end());
817         MarkersChanged ();
818 }
819
820 void
821 MarkerSelection::range (nframes64_t& s, nframes64_t& e)
822 {
823         s = max_frames;
824         e = 0;
825
826         for (MarkerSelection::iterator i = begin(); i != end(); ++i) {
827
828                 if ((*i)->position() < s) {
829                         s = (*i)->position();
830                 } 
831
832                 if ((*i)->position() > e) {
833                         e = (*i)->position();
834                 }
835         }
836
837         s = std::min (s, e);
838         e = std::max (s, e);
839 }