stop a crash during selection
[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
23 #include "pbd/error.h"
24 #include "pbd/stacktrace.h"
25
26 #include "ardour/playlist.h"
27 #include "ardour/rc_configuration.h"
28
29 #include "control_protocol/control_protocol.h"
30
31 #include "audio_region_view.h"
32 #include "debug.h"
33 #include "gui_thread.h"
34 #include "midi_cut_buffer.h"
35 #include "region_gain_line.h"
36 #include "region_view.h"
37 #include "selection.h"
38 #include "selection_templates.h"
39 #include "time_axis_view.h"
40 #include "automation_time_axis.h"
41 #include "public_editor.h"
42 #include "control_point.h"
43
44 #include "pbd/i18n.h"
45
46 using namespace std;
47 using namespace ARDOUR;
48 using namespace PBD;
49
50 struct AudioRangeComparator {
51         bool operator()(AudioRange a, AudioRange b) {
52                 return a.start < b.start;
53         }
54 };
55
56 Selection::Selection (const PublicEditor* e)
57         : tracks (e)
58         , editor (e)
59         , next_time_id (0)
60 {
61         clear ();
62
63         /* we have disambiguate which remove() for the compiler */
64
65         void (Selection::*track_remove)(TimeAxisView*) = &Selection::remove;
66         TimeAxisView::CatchDeletion.connect (*this, MISSING_INVALIDATOR, boost::bind (track_remove, this, _1), gui_context());
67
68         void (Selection::*marker_remove)(ArdourMarker*) = &Selection::remove;
69         ArdourMarker::CatchDeletion.connect (*this, MISSING_INVALIDATOR, boost::bind (marker_remove, this, _1), gui_context());
70
71         void (Selection::*point_remove)(ControlPoint*) = &Selection::remove;
72         ControlPoint::CatchDeletion.connect (*this, MISSING_INVALIDATOR, boost::bind (point_remove, this, _1), gui_context());
73 }
74
75 #if 0
76 Selection&
77 Selection::operator= (const Selection& other)
78 {
79         if (&other != this) {
80                 regions = other.regions;
81                 tracks = other.tracks;
82                 time = other.time;
83                 lines = other.lines;
84                 midi_regions = other.midi_regions;
85                 midi_notes = other.midi_notes;
86         }
87         return *this;
88 }
89 #endif
90
91 bool
92 operator== (const Selection& a, const Selection& b)
93 {
94         return a.regions == b.regions &&
95                 a.tracks == b.tracks &&
96                 a.time == b.time &&
97                 a.lines == b.lines &&
98                 a.playlists == b.playlists &&
99                 a.midi_notes == b.midi_notes &&
100                 a.midi_regions == b.midi_regions;
101 }
102
103 /** Clear everything from the Selection */
104 void
105 Selection::clear ()
106 {
107         clear_tracks ();
108         clear_regions ();
109         clear_points ();
110         clear_lines();
111         clear_time ();
112         clear_playlists ();
113         clear_midi_notes ();
114         clear_midi_regions ();
115         clear_markers ();
116         pending_midi_note_selection.clear();
117 }
118
119 void
120 Selection::clear_objects (bool with_signal)
121 {
122         clear_regions (with_signal);
123         clear_points (with_signal);
124         clear_lines(with_signal);
125         clear_playlists (with_signal);
126         clear_midi_notes (with_signal);
127         clear_midi_regions (with_signal);
128 }
129
130 void
131 Selection::clear_tracks (bool with_signal)
132 {
133         if (!tracks.empty()) {
134                 PresentationInfo::ChangeSuspender cs;
135
136                 for (TrackViewList::iterator x = tracks.begin(); x != tracks.end(); ++x) {
137                         (*x)->set_selected (false);
138                 }
139
140                 tracks.clear ();
141         }
142 }
143
144 void
145 Selection::clear_time (bool with_signal)
146 {
147         time.clear();
148         if (with_signal) {
149                 TimeChanged ();
150         }
151 }
152
153 void
154 Selection::dump_region_layers()
155 {
156         cerr << "region selection layer dump" << endl;
157         for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
158                 cerr << "layer: " << (int)(*i)->region()->layer() << endl;
159         }
160 }
161
162
163 void
164 Selection::clear_regions (bool with_signal)
165 {
166         if (!regions.empty()) {
167                 regions.clear_all ();
168                 if (with_signal) {
169                         RegionsChanged();
170                 }
171         }
172 }
173
174 void
175 Selection::clear_midi_notes (bool with_signal)
176 {
177         if (!midi_notes.empty()) {
178                 for (MidiNoteSelection::iterator x = midi_notes.begin(); x != midi_notes.end(); ++x) {
179                         delete *x;
180                 }
181                 midi_notes.clear ();
182                 if (with_signal) {
183                         MidiNotesChanged ();
184                 }
185         }
186
187         // clear note selections for MRV's that have note selections
188         // this will cause the MRV to be removed from the list
189         for (MidiRegionSelection::iterator i = midi_regions.begin();
190              i != midi_regions.end();) {
191                 MidiRegionSelection::iterator tmp = i;
192                 ++tmp;
193                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
194                 if (mrv) {
195                         mrv->clear_selection();
196                 }
197                 i = tmp;
198         }
199 }
200
201 void
202 Selection::clear_midi_regions (bool with_signal)
203 {
204         if (!midi_regions.empty()) {
205                 midi_regions.clear ();
206                 if (with_signal) {
207                         MidiRegionsChanged ();
208                 }
209         }
210 }
211
212 void
213 Selection::clear_playlists (bool with_signal)
214 {
215         /* Selections own their playlists */
216
217         for (PlaylistSelection::iterator i = playlists.begin(); i != playlists.end(); ++i) {
218                 /* selections own their own regions, which are copies of the "originals". make them go away */
219                 (*i)->drop_regions ();
220                 (*i)->release ();
221         }
222
223         if (!playlists.empty()) {
224                 playlists.clear ();
225                 if (with_signal) {
226                         PlaylistsChanged();
227                 }
228         }
229 }
230
231 void
232 Selection::clear_lines (bool with_signal)
233 {
234         if (!lines.empty()) {
235                 lines.clear ();
236                 if (with_signal) {
237                         LinesChanged();
238                 }
239         }
240 }
241
242 void
243 Selection::clear_markers (bool with_signal)
244 {
245         if (!markers.empty()) {
246                 markers.clear ();
247                 if (with_signal) {
248                         MarkersChanged();
249                 }
250         }
251 }
252
253 void
254 Selection::toggle (boost::shared_ptr<Playlist> pl)
255 {
256         clear_time();  //enforce object/range exclusivity
257         clear_tracks();  //enforce object/track exclusivity
258
259         PlaylistSelection::iterator i;
260
261         if ((i = find (playlists.begin(), playlists.end(), pl)) == playlists.end()) {
262                 pl->use ();
263                 playlists.push_back(pl);
264         } else {
265                 playlists.erase (i);
266         }
267
268         PlaylistsChanged ();
269 }
270
271 void
272 Selection::toggle (const TrackViewList& track_list)
273 {
274         PresentationInfo::ChangeSuspender cs;
275
276         for (TrackViewList::const_iterator i = track_list.begin(); i != track_list.end(); ++i) {
277                 toggle ((*i));
278         }
279 }
280
281 void
282 Selection::toggle (TimeAxisView* track)
283 {
284         TrackSelection::iterator i;
285
286         if ((i = find (tracks.begin(), tracks.end(), track)) == tracks.end()) {
287                 tracks.push_back (track);
288                 track->set_selected (true);
289         } else {
290                 tracks.erase (i);
291                 track->set_selected (false);
292         }
293
294 }
295
296 void
297 Selection::toggle (const MidiNoteSelection& midi_note_list)
298 {
299         clear_time();  //enforce object/range exclusivity
300         clear_tracks();  //enforce object/track exclusivity
301
302         for (MidiNoteSelection::const_iterator i = midi_note_list.begin(); i != midi_note_list.end(); ++i) {
303                 toggle ((*i));
304         }
305 }
306
307 void
308 Selection::toggle (MidiCutBuffer* midi)
309 {
310         MidiNoteSelection::iterator i;
311
312         if ((i = find (midi_notes.begin(), midi_notes.end(), midi)) == midi_notes.end()) {
313                 midi_notes.push_back (midi);
314         } else {
315                 /* remember that we own the MCB */
316                 delete *i;
317                 midi_notes.erase (i);
318         }
319
320         MidiNotesChanged();
321 }
322
323
324 void
325 Selection::toggle (RegionView* r)
326 {
327         clear_time();  //enforce object/range exclusivity
328         clear_tracks();  //enforce object/track exclusivity
329
330         RegionSelection::iterator i;
331
332         if ((i = find (regions.begin(), regions.end(), r)) == regions.end()) {
333                 add (r);
334         } else {
335                 remove (*i);
336         }
337
338         RegionsChanged ();
339 }
340
341 void
342 Selection::toggle (MidiRegionView* mrv)
343 {
344         clear_time();   //enforce object/range exclusivity
345         clear_tracks();  //enforce object/track exclusivity
346
347         MidiRegionSelection::iterator i;
348
349         if ((i = find (midi_regions.begin(), midi_regions.end(), mrv)) == midi_regions.end()) {
350                 add (mrv);
351         } else {
352                 midi_regions.erase (i);
353         }
354
355         MidiRegionsChanged ();
356 }
357
358 void
359 Selection::toggle (vector<RegionView*>& r)
360 {
361         clear_time();  //enforce object/range exclusivity
362         clear_tracks();  //enforce object/track exclusivity
363
364         RegionSelection::iterator i;
365
366         for (vector<RegionView*>::iterator x = r.begin(); x != r.end(); ++x) {
367                 if ((i = find (regions.begin(), regions.end(), (*x))) == regions.end()) {
368                         add ((*x));
369                 } else {
370                         remove (*x);
371                 }
372         }
373
374         RegionsChanged ();
375 }
376
377 long
378 Selection::toggle (framepos_t start, framepos_t end)
379 {
380         clear_objects();  //enforce object/range exclusivity
381
382         AudioRangeComparator cmp;
383
384         /* XXX this implementation is incorrect */
385
386         time.push_back (AudioRange (start, end, ++next_time_id));
387         time.consolidate ();
388         time.sort (cmp);
389
390         TimeChanged ();
391
392         return next_time_id;
393 }
394
395 void
396 Selection::add (boost::shared_ptr<Playlist> pl)
397 {
398         clear_time();  //enforce object/range exclusivity
399         clear_tracks();  //enforce object/track exclusivity
400
401         if (find (playlists.begin(), playlists.end(), pl) == playlists.end()) {
402                 pl->use ();
403                 playlists.push_back(pl);
404                 PlaylistsChanged ();
405         }
406 }
407
408 void
409 Selection::add (const list<boost::shared_ptr<Playlist> >& pllist)
410 {
411         clear_time();  //enforce object/range exclusivity
412         clear_tracks();  //enforce object/track exclusivity
413
414         bool changed = false;
415
416         for (list<boost::shared_ptr<Playlist> >::const_iterator i = pllist.begin(); i != pllist.end(); ++i) {
417                 if (find (playlists.begin(), playlists.end(), (*i)) == playlists.end()) {
418                         (*i)->use ();
419                         playlists.push_back (*i);
420                         changed = true;
421                 }
422         }
423
424         if (changed) {
425                 PlaylistsChanged ();
426         }
427 }
428
429 void
430 Selection::add (TrackViewList const & track_list)
431 {
432         clear_objects();  //enforce object/range exclusivity
433
434         PresentationInfo::ChangeSuspender cs;
435
436         TrackViewList added = tracks.add (track_list);
437
438         if (!added.empty()) {
439                 for (TrackViewList::iterator x = added.begin(); x != added.end(); ++x) {
440                         (*x)->set_selected (true);
441                 }
442         }
443 }
444
445 void
446 Selection::add (TimeAxisView* track)
447 {
448         clear_objects();  //enforce object/range exclusivity
449
450         TrackViewList tr;
451         track->set_selected (true);
452         tr.push_back (track);
453         add (tr);
454 }
455
456 void
457 Selection::add (const MidiNoteSelection& midi_list)
458 {
459         clear_time();  //enforce object/range exclusivity
460         clear_tracks();  //enforce object/track exclusivity
461
462         const MidiNoteSelection::const_iterator b = midi_list.begin();
463         const MidiNoteSelection::const_iterator e = midi_list.end();
464
465         if (!midi_list.empty()) {
466                 midi_notes.insert (midi_notes.end(), b, e);
467                 MidiNotesChanged ();
468         }
469 }
470
471 void
472 Selection::add (MidiCutBuffer* midi)
473 {
474         /* we take ownership of the MCB */
475
476         if (find (midi_notes.begin(), midi_notes.end(), midi) == midi_notes.end()) {
477                 midi_notes.push_back (midi);
478                 MidiNotesChanged ();
479         }
480 }
481
482 void
483 Selection::add (vector<RegionView*>& v)
484 {
485         clear_time();  //enforce object/range exclusivity
486         clear_tracks();  //enforce object/track exclusivity
487
488         /* XXX This method or the add (const RegionSelection&) needs to go
489          */
490
491         bool changed = false;
492
493         for (vector<RegionView*>::iterator i = v.begin(); i != v.end(); ++i) {
494                 if (find (regions.begin(), regions.end(), (*i)) == regions.end()) {
495                         changed = regions.add ((*i));
496                 }
497         }
498
499         if (changed) {
500                 RegionsChanged ();
501         }
502 }
503
504 void
505 Selection::add (const RegionSelection& rs)
506 {
507         clear_time();  //enforce object/range exclusivity
508         clear_tracks();  //enforce object/track exclusivity
509
510         /* XXX This method or the add (const vector<RegionView*>&) needs to go
511          */
512
513         bool changed = false;
514
515         for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
516                 if (find (regions.begin(), regions.end(), (*i)) == regions.end()) {
517                         changed = regions.add ((*i));
518                 }
519         }
520
521         if (changed) {
522                 RegionsChanged ();
523         }
524 }
525
526 void
527 Selection::add (RegionView* r)
528 {
529         clear_time();  //enforce object/range exclusivity
530         clear_tracks();  //enforce object/track exclusivity
531
532         if (find (regions.begin(), regions.end(), r) == regions.end()) {
533                 bool changed = regions.add (r);
534                 if (changed) {
535                         RegionsChanged ();
536                 }
537         }
538 }
539
540 void
541 Selection::add (MidiRegionView* mrv)
542 {
543         DEBUG_TRACE(DEBUG::Selection, string_compose("Selection::add MRV %1\n", mrv));
544
545         clear_time();  //enforce object/range exclusivity
546         clear_tracks();  //enforce object/track exclusivity
547
548         if (find (midi_regions.begin(), midi_regions.end(), mrv) == midi_regions.end()) {
549                 midi_regions.push_back (mrv);
550                 /* XXX should we do this? */
551                 MidiRegionsChanged ();
552         }
553 }
554
555 long
556 Selection::add (framepos_t start, framepos_t end)
557 {
558         clear_objects();  //enforce object/range exclusivity
559
560         AudioRangeComparator cmp;
561
562         /* XXX this implementation is incorrect */
563
564         time.push_back (AudioRange (start, end, ++next_time_id));
565         time.consolidate ();
566         time.sort (cmp);
567
568         TimeChanged ();
569
570         return next_time_id;
571 }
572
573 void
574 Selection::move_time (framecnt_t distance)
575 {
576         if (distance == 0) {
577                 return;
578         }
579
580         for (list<AudioRange>::iterator i = time.begin(); i != time.end(); ++i) {
581                 (*i).start += distance;
582                 (*i).end += distance;
583         }
584
585         TimeChanged ();
586 }
587
588 void
589 Selection::replace (uint32_t sid, framepos_t start, framepos_t end)
590 {
591         clear_objects();  //enforce object/range exclusivity
592
593         for (list<AudioRange>::iterator i = time.begin(); i != time.end(); ++i) {
594                 if ((*i).id == sid) {
595                         time.erase (i);
596                         time.push_back (AudioRange(start,end, sid));
597
598                         /* don't consolidate here */
599
600
601                         AudioRangeComparator cmp;
602                         time.sort (cmp);
603
604                         TimeChanged ();
605                         break;
606                 }
607         }
608 }
609
610 void
611 Selection::add (boost::shared_ptr<Evoral::ControlList> cl)
612 {
613         clear_time();  //enforce object/range exclusivity
614         clear_tracks();  //enforce object/track exclusivity
615
616         boost::shared_ptr<ARDOUR::AutomationList> al
617                 = boost::dynamic_pointer_cast<ARDOUR::AutomationList>(cl);
618         if (!al) {
619                 warning << "Programming error: Selected list is not an ARDOUR::AutomationList" << endmsg;
620                 return;
621         }
622
623         /* The original may change so we must store a copy (not a pointer) here.
624          * e.g AutomationLine rewrites the list with gain mapping.
625          * the downside is that we can't perfom duplicate checks.
626          * This code was changed in response to #6842
627          */
628         lines.push_back (boost::shared_ptr<ARDOUR::AutomationList> (new ARDOUR::AutomationList(*al)));
629         LinesChanged();
630 }
631
632 void
633 Selection::remove (TimeAxisView* track)
634 {
635         list<TimeAxisView*>::iterator i;
636         if ((i = find (tracks.begin(), tracks.end(), track)) != tracks.end()) {
637                 track->set_selected (false);
638                 tracks.erase (i);
639         }
640 }
641
642 void
643 Selection::remove (const TrackViewList& track_list)
644 {
645         PresentationInfo::ChangeSuspender cs;
646
647         for (TrackViewList::const_iterator i = track_list.begin(); i != track_list.end(); ++i) {
648
649                 TrackViewList::iterator x = find (tracks.begin(), tracks.end(), *i);
650
651                 if (x != tracks.end()) {
652                         tracks.erase (x);
653                         (*i)->set_selected (false);
654                 }
655         }
656 }
657
658 void
659 Selection::remove (ControlPoint* p)
660 {
661         PointSelection::iterator i = find (points.begin(), points.end(), p);
662         if (i != points.end ()) {
663                 points.erase (i);
664         }
665 }
666
667 void
668 Selection::remove (const MidiNoteSelection& midi_list)
669 {
670         bool changed = false;
671
672         for (MidiNoteSelection::const_iterator i = midi_list.begin(); i != midi_list.end(); ++i) {
673
674                 MidiNoteSelection::iterator x;
675
676                 if ((x = find (midi_notes.begin(), midi_notes.end(), (*i))) != midi_notes.end()) {
677                         midi_notes.erase (x);
678                         changed = true;
679                 }
680         }
681
682         if (changed) {
683                 MidiNotesChanged();
684         }
685 }
686
687 void
688 Selection::remove (MidiCutBuffer* midi)
689 {
690         MidiNoteSelection::iterator x;
691
692         if ((x = find (midi_notes.begin(), midi_notes.end(), midi)) != midi_notes.end()) {
693                 /* remember that we own the MCB */
694                 delete *x;
695                 midi_notes.erase (x);
696                 MidiNotesChanged ();
697         }
698 }
699
700 void
701 Selection::remove (boost::shared_ptr<Playlist> track)
702 {
703         list<boost::shared_ptr<Playlist> >::iterator i;
704         if ((i = find (playlists.begin(), playlists.end(), track)) != playlists.end()) {
705                 playlists.erase (i);
706                 PlaylistsChanged();
707         }
708 }
709
710 void
711 Selection::remove (const list<boost::shared_ptr<Playlist> >& pllist)
712 {
713         bool changed = false;
714
715         for (list<boost::shared_ptr<Playlist> >::const_iterator i = pllist.begin(); i != pllist.end(); ++i) {
716
717                 list<boost::shared_ptr<Playlist> >::iterator x;
718
719                 if ((x = find (playlists.begin(), playlists.end(), (*i))) != playlists.end()) {
720                         playlists.erase (x);
721                         changed = true;
722                 }
723         }
724
725         if (changed) {
726                 PlaylistsChanged();
727         }
728 }
729
730 void
731 Selection::remove (RegionView* r)
732 {
733         if (regions.remove (r)) {
734                 RegionsChanged ();
735         }
736 }
737
738 void
739 Selection::remove (MidiRegionView* mrv)
740 {
741         DEBUG_TRACE(DEBUG::Selection, string_compose("Selection::remove MRV %1\n", mrv));
742
743         MidiRegionSelection::iterator x;
744
745         if ((x = find (midi_regions.begin(), midi_regions.end(), mrv)) != midi_regions.end()) {
746                 midi_regions.erase (x);
747                 MidiRegionsChanged ();
748         }
749 }
750
751 void
752 Selection::remove (uint32_t selection_id)
753 {
754         if (time.empty()) {
755                 return;
756         }
757
758         for (list<AudioRange>::iterator i = time.begin(); i != time.end(); ++i) {
759                 if ((*i).id == selection_id) {
760                         time.erase (i);
761
762                         TimeChanged ();
763                         break;
764                 }
765         }
766 }
767
768 void
769 Selection::remove (framepos_t /*start*/, framepos_t /*end*/)
770 {
771 }
772
773 void
774 Selection::remove (boost::shared_ptr<ARDOUR::AutomationList> ac)
775 {
776         AutomationSelection::iterator i;
777         if ((i = find (lines.begin(), lines.end(), ac)) != lines.end()) {
778                 lines.erase (i);
779                 LinesChanged();
780         }
781 }
782
783 void
784 Selection::set (TimeAxisView* track)
785 {
786         clear_objects ();  //enforce object/range exclusivity
787
788         PresentationInfo::ChangeSuspender cs;
789
790         if (!tracks.empty()) {
791
792                 if (tracks.size() == 1 && tracks.front() == track) {
793                         /* already single selection: nothing to do */
794                         return;
795                 }
796
797                 for (TrackViewList::iterator x = tracks.begin(); x != tracks.end(); ++x) {
798                         (*x)->set_selected (false);
799                 }
800
801                 tracks.clear ();
802         }
803
804         add (track);
805 }
806
807 void
808 Selection::set (const TrackViewList& track_list)
809 {
810         clear_objects();  //enforce object/range exclusivity
811
812         PresentationInfo::ChangeSuspender cs;
813
814         if (!tracks.empty()) {
815
816                 /* cannot use set<T>::operator== (set<T> const &) here, because
817                  * apparently the ordering used within 2 sets is not
818                  * necessarily the same.
819                  */
820
821                 if (tracks.size() == track_list.size()) {
822                         bool missing = false;
823
824                         for (TrackViewList::const_iterator x = track_list.begin(); x != track_list.end(); ++x) {
825                                 if (find (tracks.begin(), tracks.end(), *x) == tracks.end()) {
826                                         missing = true;
827                                 }
828                         }
829
830                         if (!missing) {
831                                 /* already same selection: nothing to do */
832                                 return;
833                         }
834                 }
835
836                 /* argument is different from existing selection */
837
838                 for (TrackViewList::iterator x = tracks.begin(); x != tracks.end(); ++x) {
839                         (*x)->set_selected (false);
840                 }
841
842                 tracks.clear ();
843         }
844
845         add (track_list);
846 }
847
848 void
849 Selection::set (const MidiNoteSelection& midi_list)
850 {
851         clear_time ();  //enforce region/object exclusivity
852         clear_tracks();  //enforce object/track exclusivity
853         clear_objects ();
854         add (midi_list);
855 }
856
857 void
858 Selection::set (boost::shared_ptr<Playlist> playlist)
859 {
860         clear_time ();  //enforce region/object exclusivity
861         clear_tracks();  //enforce object/track exclusivity
862         clear_objects ();
863         add (playlist);
864 }
865
866 void
867 Selection::set (const list<boost::shared_ptr<Playlist> >& pllist)
868 {
869         clear_time();  //enforce region/object exclusivity
870         clear_objects ();
871         add (pllist);
872 }
873
874 void
875 Selection::set (const RegionSelection& rs)
876 {
877         clear_time();  //enforce region/object exclusivity
878         clear_tracks();  //enforce object/track exclusivity
879         clear_objects();
880         regions = rs;
881         RegionsChanged(); /* EMIT SIGNAL */
882 }
883
884 void
885 Selection::set (MidiRegionView* mrv)
886 {
887         clear_time();  //enforce region/object exclusivity
888         clear_tracks();  //enforce object/track exclusivity
889         clear_objects ();
890         add (mrv);
891 }
892
893 void
894 Selection::set (RegionView* r, bool /*also_clear_tracks*/)
895 {
896         clear_time();  //enforce region/object exclusivity
897         clear_tracks();  //enforce object/track exclusivity
898         clear_objects ();
899         add (r);
900 }
901
902 void
903 Selection::set (vector<RegionView*>& v)
904 {
905         clear_time();  //enforce region/object exclusivity
906         clear_tracks();  //enforce object/track exclusivity
907         clear_objects();
908
909         add (v);
910 }
911
912 /** Set the start and end time of the time selection, without changing
913  *  the list of tracks it applies to.
914  */
915 long
916 Selection::set (framepos_t start, framepos_t end)
917 {
918         clear_objects();  //enforce region/object exclusivity
919         clear_time();
920
921         if ((start == 0 && end == 0) || end < start) {
922                 return 0;
923         }
924
925         if (time.empty()) {
926                 time.push_back (AudioRange (start, end, ++next_time_id));
927         } else {
928                 /* reuse the first entry, and remove all the rest */
929
930                 while (time.size() > 1) {
931                         time.pop_front();
932                 }
933                 time.front().start = start;
934                 time.front().end = end;
935         }
936
937         time.consolidate ();
938
939         TimeChanged ();
940
941         return time.front().id;
942 }
943
944 /** Set the start and end of the range selection.  If more than one range
945  *  is currently selected, the start of the earliest range and the end of the
946  *  latest range are set.  If no range is currently selected, this method
947  *  selects a single range from start to end.
948  *
949  *  @param start New start time.
950  *  @param end New end time.
951  */
952 void
953 Selection::set_preserving_all_ranges (framepos_t start, framepos_t end)
954 {
955         clear_objects();  //enforce region/object exclusivity
956
957         if ((start == 0 && end == 0) || (end < start)) {
958                 return;
959         }
960
961         if (time.empty ()) {
962                 time.push_back (AudioRange (start, end, ++next_time_id));
963         } else {
964                 time.sort (AudioRangeComparator ());
965                 time.front().start = start;
966                 time.back().end = end;
967         }
968
969         time.consolidate ();
970
971         TimeChanged ();
972 }
973
974 void
975 Selection::set (boost::shared_ptr<Evoral::ControlList> ac)
976 {
977         clear_time();  //enforce region/object exclusivity
978         clear_tracks();  //enforce object/track exclusivity
979         clear_objects();
980
981         add (ac);
982 }
983
984 bool
985 Selection::selected (ArdourMarker* m)
986 {
987         return find (markers.begin(), markers.end(), m) != markers.end();
988 }
989
990 bool
991 Selection::selected (TimeAxisView* tv)
992 {
993         return tv->selected ();
994 }
995
996 bool
997 Selection::selected (RegionView* rv)
998 {
999         return find (regions.begin(), regions.end(), rv) != regions.end();
1000 }
1001
1002 bool
1003 Selection::selected (ControlPoint* cp)
1004 {
1005         return find (points.begin(), points.end(), cp) != points.end();
1006 }
1007
1008 bool
1009 Selection::empty (bool internal_selection)
1010 {
1011         bool object_level_empty =  regions.empty () &&
1012                 tracks.empty () &&
1013                 points.empty () &&
1014                 playlists.empty () &&
1015                 lines.empty () &&
1016                 time.empty () &&
1017                 playlists.empty () &&
1018                 markers.empty() &&
1019                 midi_regions.empty()
1020                 ;
1021
1022         if (!internal_selection) {
1023                 return object_level_empty;
1024         }
1025
1026         /* this is intended to really only apply when using a Selection
1027            as a cut buffer.
1028         */
1029
1030         return object_level_empty && midi_notes.empty() && points.empty();
1031 }
1032
1033 void
1034 Selection::toggle (ControlPoint* cp)
1035 {
1036         clear_time();  //enforce region/object exclusivity
1037         clear_tracks();  //enforce object/track exclusivity
1038
1039         cp->set_selected (!cp->selected ());
1040         PointSelection::iterator i = find (points.begin(), points.end(), cp);
1041         if (i == points.end()) {
1042                 points.push_back (cp);
1043         } else {
1044                 points.erase (i);
1045         }
1046
1047         PointsChanged (); /* EMIT SIGNAL */
1048 }
1049
1050 void
1051 Selection::toggle (vector<ControlPoint*> const & cps)
1052 {
1053         clear_time();  //enforce region/object exclusivity
1054         clear_tracks();  //enforce object/track exclusivity
1055
1056         for (vector<ControlPoint*>::const_iterator i = cps.begin(); i != cps.end(); ++i) {
1057                 toggle (*i);
1058         }
1059 }
1060
1061 void
1062 Selection::toggle (list<Selectable*> const & selectables)
1063 {
1064         clear_time();  //enforce region/object exclusivity
1065         clear_tracks();  //enforce object/track exclusivity
1066
1067         RegionView* rv;
1068         ControlPoint* cp;
1069         vector<RegionView*> rvs;
1070         vector<ControlPoint*> cps;
1071
1072         for (std::list<Selectable*>::const_iterator i = selectables.begin(); i != selectables.end(); ++i) {
1073                 if ((rv = dynamic_cast<RegionView*> (*i)) != 0) {
1074                         rvs.push_back (rv);
1075                 } else if ((cp = dynamic_cast<ControlPoint*> (*i)) != 0) {
1076                         cps.push_back (cp);
1077                 } else {
1078                         fatal << _("programming error: ")
1079                               << X_("unknown selectable type passed to Selection::toggle()")
1080                               << endmsg;
1081                         abort(); /*NOTREACHED*/
1082                 }
1083         }
1084
1085         if (!rvs.empty()) {
1086                 toggle (rvs);
1087         }
1088
1089         if (!cps.empty()) {
1090                 toggle (cps);
1091         }
1092 }
1093
1094 void
1095 Selection::set (list<Selectable*> const & selectables)
1096 {
1097         clear_time ();  //enforce region/object exclusivity
1098         clear_tracks();  //enforce object/track exclusivity
1099         clear_objects ();
1100
1101         add (selectables);
1102 }
1103
1104 void
1105 Selection::add (PointSelection const & s)
1106 {
1107         clear_time ();  //enforce region/object exclusivity
1108         clear_tracks();  //enforce object/track exclusivity
1109
1110         for (PointSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
1111                 points.push_back (*i);
1112         }
1113 }
1114
1115 void
1116 Selection::add (list<Selectable*> const & selectables)
1117 {
1118         clear_time ();  //enforce region/object exclusivity
1119         clear_tracks();  //enforce object/track exclusivity
1120
1121         RegionView* rv;
1122         ControlPoint* cp;
1123         vector<RegionView*> rvs;
1124         vector<ControlPoint*> cps;
1125
1126         for (std::list<Selectable*>::const_iterator i = selectables.begin(); i != selectables.end(); ++i) {
1127                 if ((rv = dynamic_cast<RegionView*> (*i)) != 0) {
1128                         rvs.push_back (rv);
1129                 } else if ((cp = dynamic_cast<ControlPoint*> (*i)) != 0) {
1130                         cps.push_back (cp);
1131                 } else {
1132                         fatal << _("programming error: ")
1133                               << X_("unknown selectable type passed to Selection::add()")
1134                               << endmsg;
1135                         abort(); /*NOTREACHED*/
1136                 }
1137         }
1138
1139         if (!rvs.empty()) {
1140                 add (rvs);
1141         }
1142
1143         if (!cps.empty()) {
1144                 add (cps);
1145         }
1146 }
1147
1148 void
1149 Selection::clear_points (bool with_signal)
1150 {
1151         if (!points.empty()) {
1152                 points.clear ();
1153                 if (with_signal) {
1154                         PointsChanged ();
1155                 }
1156         }
1157 }
1158
1159 void
1160 Selection::add (ControlPoint* cp)
1161 {
1162         clear_time ();  //enforce region/object exclusivity
1163         clear_tracks();  //enforce object/track exclusivity
1164
1165         cp->set_selected (true);
1166         points.push_back (cp);
1167         PointsChanged (); /* EMIT SIGNAL */
1168 }
1169
1170 void
1171 Selection::add (vector<ControlPoint*> const & cps)
1172 {
1173         clear_time ();  //enforce region/object exclusivity
1174         clear_tracks();  //enforce object/track exclusivity
1175
1176         for (vector<ControlPoint*>::const_iterator i = cps.begin(); i != cps.end(); ++i) {
1177                 (*i)->set_selected (true);
1178                 points.push_back (*i);
1179         }
1180         PointsChanged (); /* EMIT SIGNAL */
1181 }
1182
1183 void
1184 Selection::set (ControlPoint* cp)
1185 {
1186         clear_time ();  //enforce region/object exclusivity
1187         clear_tracks();  //enforce object/track exclusivity
1188
1189         if (cp->selected () && points.size () == 1) {
1190                 return;
1191         }
1192
1193         for (uint32_t i = 0; i < cp->line().npoints(); ++i) {
1194                 cp->line().nth (i)->set_selected (false);
1195         }
1196
1197         clear_objects ();
1198         add (cp);
1199 }
1200
1201 void
1202 Selection::set (ArdourMarker* m)
1203 {
1204         clear_time ();  //enforce region/object exclusivity
1205         clear_tracks();  //enforce object/track exclusivity
1206         markers.clear ();
1207
1208         add (m);
1209 }
1210
1211 void
1212 Selection::toggle (ArdourMarker* m)
1213 {
1214         MarkerSelection::iterator i;
1215
1216         if ((i = find (markers.begin(), markers.end(), m)) == markers.end()) {
1217                 add (m);
1218         } else {
1219                 remove (m);
1220         }
1221 }
1222
1223 void
1224 Selection::remove (ArdourMarker* m)
1225 {
1226         MarkerSelection::iterator i;
1227
1228         if ((i = find (markers.begin(), markers.end(), m)) != markers.end()) {
1229                 markers.erase (i);
1230                 MarkersChanged();
1231         }
1232 }
1233
1234 void
1235 Selection::add (ArdourMarker* m)
1236 {
1237         clear_time ();  //enforce region/object exclusivity
1238         clear_tracks();  //enforce object/track exclusivity
1239
1240         if (find (markers.begin(), markers.end(), m) == markers.end()) {
1241                 markers.push_back (m);
1242                 MarkersChanged();
1243         }
1244 }
1245
1246 void
1247 Selection::add (const list<ArdourMarker*>& m)
1248 {
1249         clear_time ();  //enforce region/object exclusivity
1250         clear_tracks();  //enforce object/track exclusivity
1251
1252         markers.insert (markers.end(), m.begin(), m.end());
1253         markers.sort ();
1254         markers.unique ();
1255
1256         MarkersChanged ();
1257 }
1258
1259 void
1260 MarkerSelection::range (framepos_t& s, framepos_t& e)
1261 {
1262         s = max_framepos;
1263         e = 0;
1264
1265         for (MarkerSelection::iterator i = begin(); i != end(); ++i) {
1266
1267                 if ((*i)->position() < s) {
1268                         s = (*i)->position();
1269                 }
1270
1271                 if ((*i)->position() > e) {
1272                         e = (*i)->position();
1273                 }
1274         }
1275
1276         s = std::min (s, e);
1277         e = std::max (s, e);
1278 }
1279
1280 XMLNode&
1281 Selection::get_state () const
1282 {
1283         /* XXX: not complete; just sufficient to get track selection state
1284            so that re-opening plugin windows for editor mixer strips works
1285         */
1286
1287         char buf[32];
1288         XMLNode* node = new XMLNode (X_("Selection"));
1289
1290         for (TrackSelection::const_iterator i = tracks.begin(); i != tracks.end(); ++i) {
1291                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
1292                 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (*i);
1293                 if (rtv) {
1294                         XMLNode* t = node->add_child (X_("RouteView"));
1295                         t->add_property (X_("id"), atoi (rtv->route()->id().to_s().c_str()));
1296                 } else if (atv) {
1297                         XMLNode* t = node->add_child (X_("AutomationView"));
1298                         t->add_property (X_("id"), atoi (atv->parent_route()->id().to_s().c_str()));
1299                         t->add_property (X_("parameter"), EventTypeMap::instance().to_symbol (atv->parameter ()));
1300                 }
1301         }
1302
1303         for (RegionSelection::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1304                 XMLNode* r = node->add_child (X_("Region"));
1305                 r->add_property (X_("id"), atoi ((*i)->region ()->id ().to_s ().c_str()));
1306         }
1307
1308         /* midi region views have thir own internal selection. */
1309         list<pair<PBD::ID, std::set<boost::shared_ptr<Evoral::Note<Evoral::Beats> > > > > rid_notes;
1310         editor->get_per_region_note_selection (rid_notes);
1311
1312         list<pair<PBD::ID, std::set<boost::shared_ptr<Evoral::Note<Evoral::Beats> > > > >::iterator rn_it;
1313         for (rn_it = rid_notes.begin(); rn_it != rid_notes.end(); ++rn_it) {
1314                 XMLNode* n = node->add_child (X_("MIDINotes"));
1315                 n->add_property (X_("region-id"), atoi((*rn_it).first.to_s().c_str()));
1316
1317                 for (std::set<boost::shared_ptr<Evoral::Note<Evoral::Beats> > >::iterator i = (*rn_it).second.begin(); i != (*rn_it).second.end(); ++i) {
1318                         XMLNode* nc = n->add_child(X_("note"));
1319
1320                         snprintf(buf, sizeof(buf), "%d", (*i)->id());
1321                         nc->add_property (X_("note-id"), string(buf));
1322                 }
1323         }
1324
1325         for (PointSelection::const_iterator i = points.begin(); i != points.end(); ++i) {
1326                 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (&(*i)->line().trackview);
1327                 if (atv) {
1328
1329                         XMLNode* r = node->add_child (X_("ControlPoint"));
1330                         r->add_property (X_("type"), "track");
1331                         r->add_property (X_("route-id"), atoi (atv->parent_route()->id ().to_s ().c_str()));
1332                         r->add_property (X_("automation-list-id"), atoi ((*i)->line().the_list()->id ().to_s ().c_str()));
1333                         r->add_property (X_("parameter"), EventTypeMap::instance().to_symbol ((*i)->line().the_list()->parameter ()));
1334
1335                         snprintf(buf, sizeof(buf), "%d", (*i)->view_index());
1336                         r->add_property (X_("view-index"), string(buf));
1337                         continue;
1338                 }
1339
1340                 AudioRegionGainLine* argl = dynamic_cast<AudioRegionGainLine*> (&(*i)->line());
1341                 if (argl) {
1342                         XMLNode* r = node->add_child (X_("ControlPoint"));
1343                         r->add_property (X_("type"), "region");
1344                         r->add_property (X_("region-id"), atoi (argl->region_view ().region ()->id ().to_s ().c_str()));
1345                         snprintf(buf, sizeof(buf), "%d", (*i)->view_index());
1346                         r->add_property (X_("view-index"), string(buf));
1347                 }
1348
1349         }
1350
1351         for (TimeSelection::const_iterator i = time.begin(); i != time.end(); ++i) {
1352                 XMLNode* t = node->add_child (X_("AudioRange"));
1353                 snprintf(buf, sizeof(buf), "%" PRId64, (*i).start);
1354                 t->add_property (X_("start"), string(buf));
1355                 snprintf(buf, sizeof(buf), "%" PRId64, (*i).end);
1356                 t->add_property (X_("end"), string(buf));
1357         }
1358
1359         for (MarkerSelection::const_iterator i = markers.begin(); i != markers.end(); ++i) {
1360                 XMLNode* t = node->add_child (X_("Marker"));
1361
1362                 bool is_start;
1363                 Location* loc = editor->find_location_from_marker (*i, is_start);
1364
1365                 t->add_property (X_("id"), atoi (loc->id().to_s().c_str()));
1366                 t->add_property (X_("start"), is_start ? X_("yes") : X_("no"));
1367         }
1368
1369         return *node;
1370 }
1371
1372 int
1373 Selection::set_state (XMLNode const & node, int)
1374 {
1375         if (node.name() != X_("Selection")) {
1376                 return -1;
1377         }
1378
1379         clear_regions ();
1380         clear_midi_notes ();
1381         clear_points ();
1382         clear_time ();
1383         clear_tracks ();
1384         clear_markers ();
1385
1386         XMLNodeList children = node.children ();
1387         for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
1388                 if ((*i)->name() == X_("RouteView")) {
1389
1390                         XMLProperty const * prop_id = (*i)->property (X_("id"));
1391                         assert (prop_id);
1392                         PBD::ID id (prop_id->value ());
1393                         RouteTimeAxisView* rtv = editor->get_route_view_by_route_id (id);
1394                         if (rtv) {
1395                                 add (rtv);
1396                         }
1397
1398                 } else if ((*i)->name() == X_("Region")) {
1399                         XMLProperty const * prop_id = (*i)->property (X_("id"));
1400                         assert (prop_id);
1401                         PBD::ID id (prop_id->value ());
1402
1403                         RegionSelection rs;
1404                         editor->get_regionviews_by_id (id, rs);
1405
1406                         if (!rs.empty ()) {
1407                                 add (rs);
1408                         } else {
1409                                 /*
1410                                   regionviews haven't been constructed - stash the region IDs
1411                                   so we can identify them in Editor::region_view_added ()
1412                                 */
1413                                 regions.pending.push_back (id);
1414                         }
1415
1416                 } else if ((*i)->name() == X_("MIDINotes")) {
1417                         XMLProperty const * prop_region_id = (*i)->property (X_("region-id"));
1418
1419                         assert (prop_region_id);
1420
1421                         PBD::ID const id (prop_region_id->value ());
1422                         RegionSelection rs;
1423
1424                         editor->get_regionviews_by_id (id, rs); // there could be more than one
1425
1426                         std::list<Evoral::event_id_t> notes;
1427                         XMLNodeList children = (*i)->children ();
1428
1429                         for (XMLNodeList::const_iterator ci = children.begin(); ci != children.end(); ++ci) {
1430                                 XMLProperty const * prop_id = (*ci)->property (X_("note-id"));
1431                                 if (prop_id) {
1432                                         Evoral::event_id_t id = atoi(prop_id->value());
1433                                         notes.push_back (id);
1434                                 }
1435                         }
1436
1437                         for (RegionSelection::iterator rsi = rs.begin(); rsi != rs.end(); ++rsi) {
1438                                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (*rsi);
1439                                 if (mrv) {
1440                                         mrv->select_notes(notes);
1441                                 }
1442                         }
1443
1444                         if (rs.empty()) {
1445                                 /* regionviews containing these notes don't yet exist on the canvas.*/
1446                                 pending_midi_note_selection.push_back (make_pair (id, notes));
1447                         }
1448
1449                 } else if  ((*i)->name() == X_("ControlPoint")) {
1450                         XMLProperty const * prop_type = (*i)->property (X_("type"));
1451
1452                         assert(prop_type);
1453
1454                         if (prop_type->value () == "track") {
1455
1456                                 XMLProperty const * prop_route_id = (*i)->property (X_("route-id"));
1457                                 XMLProperty const * prop_alist_id = (*i)->property (X_("automation-list-id"));
1458                                 XMLProperty const * prop_parameter = (*i)->property (X_("parameter"));
1459                                 XMLProperty const * prop_view_index = (*i)->property (X_("view-index"));
1460
1461                                 assert (prop_route_id);
1462                                 assert (prop_alist_id);
1463                                 assert (prop_parameter);
1464                                 assert (prop_view_index);
1465
1466                                 PBD::ID route_id (prop_route_id->value ());
1467                                 RouteTimeAxisView* rtv = editor->get_route_view_by_route_id (route_id);
1468                                 vector <ControlPoint *> cps;
1469
1470                                 if (rtv) {
1471                                         boost::shared_ptr<AutomationTimeAxisView> atv = rtv->automation_child (EventTypeMap::instance().from_symbol (prop_parameter->value ()));
1472                                         if (atv) {
1473                                                 list<boost::shared_ptr<AutomationLine> > lines = atv->lines();
1474                                                 for (list<boost::shared_ptr<AutomationLine> > ::iterator li = lines.begin(); li != lines.end(); ++li) {
1475                                                         if ((*li)->the_list()->id() == prop_alist_id->value()) {
1476                                                                 ControlPoint* cp = (*li)->nth(atol(prop_view_index->value().c_str()));
1477                                                                 if (cp) {
1478                                                                         cps.push_back (cp);
1479                                                                         cp->show();
1480                                                                 }
1481                                                         }
1482                                                 }
1483                                         }
1484                                 }
1485                                 if (!cps.empty()) {
1486                                         add (cps);
1487                                 }
1488                         } else if (prop_type->value () == "region") {
1489                                 XMLProperty const * prop_region_id = (*i)->property (X_("region-id"));
1490                                 XMLProperty const * prop_view_index = (*i)->property (X_("view-index"));
1491
1492                                 if (!prop_region_id || !prop_view_index) {
1493                                         continue;
1494                                 }
1495
1496                                 PBD::ID region_id (prop_region_id->value ());
1497                                 RegionSelection rs;
1498                                 editor->get_regionviews_by_id (region_id, rs);
1499
1500                                 if (!rs.empty ()) {
1501                                         vector <ControlPoint *> cps;
1502                                         for (RegionSelection::iterator rsi = rs.begin(); rsi != rs.end(); ++rsi) {
1503                                                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*rsi);
1504                                                 if (arv) {
1505                                                         boost::shared_ptr<AudioRegionGainLine> gl = arv->get_gain_line ();
1506                                                         ControlPoint* cp = gl->nth(atol(prop_view_index->value().c_str()));
1507                                                         if (cp) {
1508                                                                 cps.push_back (cp);
1509                                                                 cp->show();
1510                                                         }
1511                                                 }
1512                                         }
1513                                         if (!cps.empty()) {
1514                                                 add (cps);
1515                                         }
1516                                 }
1517                         }
1518
1519                 } else if  ((*i)->name() == X_("AudioRange")) {
1520                         XMLProperty const * prop_start = (*i)->property (X_("start"));
1521                         XMLProperty const * prop_end = (*i)->property (X_("end"));
1522
1523                         assert (prop_start);
1524                         assert (prop_end);
1525
1526                         framepos_t s (atol (prop_start->value ().c_str()));
1527                         framepos_t e (atol (prop_end->value ().c_str()));
1528
1529                         set_preserving_all_ranges (s, e);
1530
1531                 } else if ((*i)->name() == X_("AutomationView")) {
1532
1533                         XMLProperty const * prop_id = (*i)->property (X_("id"));
1534                         XMLProperty const * prop_parameter = (*i)->property (X_("parameter"));
1535
1536                         assert (prop_id);
1537                         assert (prop_parameter);
1538
1539                         PBD::ID id (prop_id->value ());
1540                         RouteTimeAxisView* rtv = editor->get_route_view_by_route_id (id);
1541
1542                         if (rtv) {
1543                                 boost::shared_ptr<AutomationTimeAxisView> atv = rtv->automation_child (EventTypeMap::instance().from_symbol (prop_parameter->value ()));
1544
1545                                 /* the automation could be for an entity that was never saved
1546                                    in the session file. Don't freak out if we can't find
1547                                    it.
1548                                 */
1549
1550                                 if (atv) {
1551                                         add (atv.get());
1552                                 }
1553                         }
1554
1555                 } else if ((*i)->name() == X_("Marker")) {
1556
1557                         XMLProperty const * prop_id = (*i)->property (X_("id"));
1558                         XMLProperty const * prop_start = (*i)->property (X_("start"));
1559                         assert (prop_id);
1560                         assert (prop_start);
1561
1562                         PBD::ID id (prop_id->value ());
1563                         ArdourMarker* m = editor->find_marker_from_location_id (id, string_is_affirmative (prop_start->value ()));
1564                         if (m) {
1565                                 add (m);
1566                         }
1567
1568                 }
1569
1570         }
1571
1572         return 0;
1573 }
1574
1575 void
1576 Selection::remove_regions (TimeAxisView* t)
1577 {
1578         RegionSelection::iterator i = regions.begin();
1579         while (i != regions.end ()) {
1580                 RegionSelection::iterator tmp = i;
1581                 ++tmp;
1582
1583                 if (&(*i)->get_time_axis_view() == t) {
1584                         remove (*i);
1585                 }
1586
1587                 i = tmp;
1588         }
1589 }