Merge with 2.0-ongoing R2988
[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 (AutomationList* ac)
393 {
394         if (find (lines.begin(), lines.end(), ac) == lines.end()) {
395                 lines.push_back (ac);
396                 LinesChanged();
397         }
398 }
399
400 void
401 Selection::remove (TimeAxisView* track)
402 {
403         list<TimeAxisView*>::iterator i;
404         if ((i = find (tracks.begin(), tracks.end(), track)) != tracks.end()) {
405                 tracks.erase (i);
406                 TracksChanged();
407         }
408 }
409
410 void
411 Selection::remove (const list<TimeAxisView*>& track_list)
412 {
413         bool changed = false;
414
415         for (list<TimeAxisView*>::const_iterator i = track_list.begin(); i != track_list.end(); ++i) {
416
417                 list<TimeAxisView*>::iterator x;
418
419                 if ((x = find (tracks.begin(), tracks.end(), (*i))) != tracks.end()) {
420                         tracks.erase (x);
421                         changed = true;
422                 }
423         }
424
425         if (changed) {
426                 TracksChanged();
427         }
428 }
429
430 void
431 Selection::remove (boost::shared_ptr<Playlist> track)
432 {
433         list<boost::shared_ptr<Playlist> >::iterator i;
434         if ((i = find (playlists.begin(), playlists.end(), track)) != playlists.end()) {
435                 playlists.erase (i);
436                 PlaylistsChanged();
437         }
438 }
439
440 void
441 Selection::remove (const list<boost::shared_ptr<Playlist> >& pllist)
442 {
443         bool changed = false;
444
445         for (list<boost::shared_ptr<Playlist> >::const_iterator i = pllist.begin(); i != pllist.end(); ++i) {
446
447                 list<boost::shared_ptr<Playlist> >::iterator x;
448
449                 if ((x = find (playlists.begin(), playlists.end(), (*i))) != playlists.end()) {
450                         playlists.erase (x);
451                         changed = true;
452                 }
453         }
454
455         if (changed) {
456                 PlaylistsChanged();
457         }
458 }
459
460 void
461 Selection::remove (RegionView* r)
462 {
463         if (regions.remove (r)) {
464                 RegionsChanged ();
465         }
466
467         if (Config->get_link_region_and_track_selection() && !regions.involves (r->get_trackview())) {
468                 remove (&r->get_trackview());
469         }
470 }
471
472
473 void
474 Selection::remove (uint32_t selection_id)
475 {
476         if (time.empty()) {
477                 return;
478         }
479
480         for (list<AudioRange>::iterator i = time.begin(); i != time.end(); ++i) {
481                 if ((*i).id == selection_id) {
482                         time.erase (i);
483                                                 
484                         TimeChanged ();
485                         break;
486                 }
487         }
488 }
489
490 void
491 Selection::remove (nframes_t start, nframes_t end)
492 {
493 }
494
495 void
496 Selection::remove (AutomationList *ac)
497 {
498         list<AutomationList*>::iterator i;
499         if ((i = find (lines.begin(), lines.end(), ac)) != lines.end()) {
500                 lines.erase (i);
501                 LinesChanged();
502         }
503 }
504
505 void
506 Selection::set (TimeAxisView* track)
507 {
508         clear_tracks ();
509         add (track);
510 }
511
512 void
513 Selection::set (const list<TimeAxisView*>& track_list)
514 {
515         clear_tracks ();
516         add (track_list);
517 }
518
519 void
520 Selection::set (boost::shared_ptr<Playlist> playlist)
521 {
522         clear_playlists ();
523         add (playlist);
524 }
525
526 void
527 Selection::set (const list<boost::shared_ptr<Playlist> >& pllist)
528 {
529         clear_playlists ();
530         add (pllist);
531 }
532
533 void
534 Selection::set (const RegionSelection& rs)
535 {
536         clear_regions();
537         regions = rs;
538         RegionsChanged(); /* EMIT SIGNAL */
539 }
540
541 void
542 Selection::set (RegionView* r, bool also_clear_tracks)
543 {
544         clear_regions ();
545         if (also_clear_tracks) {
546                 clear_tracks ();
547         }
548         add (r);
549 }
550
551 void
552 Selection::set (vector<RegionView*>& v)
553 {
554         clear_regions ();
555         if (Config->get_link_region_and_track_selection()) {
556                 clear_tracks ();
557                 // make sure to deselect any automation selections
558                 clear_points();
559         }
560         add (v);
561 }
562
563 long
564 Selection::set (TimeAxisView* track, nframes_t start, nframes_t end)
565 {
566         if ((start == 0 && end == 0) || end < start) {
567                 return 0;
568         }
569
570         if (time.empty()) {
571                 time.push_back (AudioRange (start, end, next_time_id++));
572         } else {
573                 /* reuse the first entry, and remove all the rest */
574
575                 while (time.size() > 1) {
576                         time.pop_front();
577                 }
578                 time.front().start = start;
579                 time.front().end = end;
580         }
581
582         if (track) {
583                 time.track = track;
584                 time.group = track->edit_group();
585         } else {
586                 time.track = 0;
587                 time.group = 0;
588         }
589
590         time.consolidate ();
591
592         TimeChanged ();
593
594         return time.front().id;
595 }
596
597 void
598 Selection::set (AutomationList *ac)
599 {
600         lines.clear();
601         add (ac);
602 }
603
604 bool
605 Selection::selected (TimeAxisView* tv)
606 {
607         return find (tracks.begin(), tracks.end(), tv) != tracks.end();
608 }
609
610 bool
611 Selection::selected (RegionView* rv)
612 {
613         return find (regions.begin(), regions.end(), rv) != regions.end();
614 }
615
616 bool
617 Selection::empty ()
618 {
619         return regions.empty () &&
620                 tracks.empty () &&
621                 points.empty () && 
622                 playlists.empty () && 
623                 lines.empty () &&
624                 time.empty () &&
625                 playlists.empty () &&
626                 markers.empty()
627                 ;
628 }
629
630 void
631 Selection::toggle (const vector<AutomationSelectable*>& autos)
632 {
633         for (vector<AutomationSelectable*>::const_iterator x = autos.begin(); x != autos.end(); ++x) {
634                 if ((*x)->get_selected()) {
635                         points.remove (**x);
636                 } else {
637                         points.push_back (**x);
638                 }
639
640                 delete *x;
641         }
642
643         PointsChanged (); /* EMIT SIGNAL */
644 }
645
646 void
647 Selection::toggle (list<Selectable*>& selectables)
648 {
649         RegionView* rv;
650         AutomationSelectable* as;
651         vector<RegionView*> rvs;
652         vector<AutomationSelectable*> autos;
653
654         for (std::list<Selectable*>::iterator i = selectables.begin(); i != selectables.end(); ++i) {
655                 if ((rv = dynamic_cast<RegionView*> (*i)) != 0) {
656                         rvs.push_back (rv);
657                 } else if ((as = dynamic_cast<AutomationSelectable*> (*i)) != 0) {
658                         autos.push_back (as);
659                 } else {
660                         fatal << _("programming error: ")
661                               << X_("unknown selectable type passed to Selection::toggle()")
662                               << endmsg;
663                         /*NOTREACHED*/
664                 }
665         }
666
667         if (!rvs.empty()) {
668                 toggle (rvs);
669         } 
670
671         if (!autos.empty()) {
672                 toggle (autos);
673         } 
674 }
675
676 void
677 Selection::set (list<Selectable*>& selectables)
678 {
679         clear_regions();
680         clear_points ();
681         add (selectables);
682 }
683
684
685 void
686 Selection::add (list<Selectable*>& selectables)
687 {
688         RegionView* rv;
689         AutomationSelectable* as;
690         vector<RegionView*> rvs;
691         vector<AutomationSelectable*> autos;
692
693         for (std::list<Selectable*>::iterator i = selectables.begin(); i != selectables.end(); ++i) {
694                 if ((rv = dynamic_cast<RegionView*> (*i)) != 0) {
695                         rvs.push_back (rv);
696                 } else if ((as = dynamic_cast<AutomationSelectable*> (*i)) != 0) {
697                         autos.push_back (as);
698                 } else {
699                         fatal << _("programming error: ")
700                               << X_("unknown selectable type passed to Selection::add()")
701                               << endmsg;
702                         /*NOTREACHED*/
703                 }
704         }
705
706         if (!rvs.empty()) {
707                 add (rvs);
708         } 
709
710         if (!autos.empty()) {
711                 add (autos);
712         } 
713 }
714
715 void
716 Selection::clear_points ()
717 {
718         if (!points.empty()) {
719                 points.clear ();
720                 PointsChanged ();
721         }
722 }
723
724 void
725 Selection::add (vector<AutomationSelectable*>& autos)
726 {
727         for (vector<AutomationSelectable*>::iterator i = autos.begin(); i != autos.end(); ++i) {
728                 points.push_back (**i);
729         }
730
731         PointsChanged ();
732 }
733
734 void
735 Selection::select_edit_group_regions ()
736 {
737         std::set<RegionView*> regions_to_add;
738         
739         for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
740                 vector<RegionView*> e;
741                 editor->get_equivalent_regions (*i, e);
742                 for (vector<RegionView*>::iterator j = e.begin(); j != e.end(); ++j) {
743                         regions_to_add.insert(*j);
744                 }
745         }
746
747         for (std::set<RegionView*>::iterator i = regions_to_add.begin(); i != regions_to_add.end(); ++i) {
748                 add (*i);
749         }
750 }
751
752 void
753 Selection::set (Marker* m)
754 {
755         clear_markers ();
756         add (m);
757 }
758
759 void
760 Selection::toggle (Marker* m)
761 {
762         MarkerSelection::iterator i;
763         
764         if ((i = find (markers.begin(), markers.end(), m)) == markers.end()) {
765                 add (m);
766         } else {
767                 remove (m);
768         }
769 }
770
771 void
772 Selection::remove (Marker* m)
773 {
774         MarkerSelection::iterator i;
775
776         if ((i = find (markers.begin(), markers.end(), m)) != markers.end()) {
777                 markers.erase (i);
778                 MarkersChanged();
779         }
780 }
781
782 void
783 Selection::add (Marker* m)
784 {
785         if (find (markers.begin(), markers.end(), m) == markers.end()) {
786
787                 /* disambiguate which remove() for the compiler */
788                 
789                 void (Selection::*pmf)(Marker*) = &Selection::remove;
790
791                 m->GoingAway.connect (bind (mem_fun (*this, pmf), m));
792
793                 markers.push_back (m);
794                 MarkersChanged();
795         }
796 }