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