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