minor logic changes to editor 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         for (TrackViewList::const_iterator i = track_list.begin(); i != track_list.end(); ++i) {
646
647                 TrackViewList::iterator x = find (tracks.begin(), tracks.end(), *i);
648                 if (x != tracks.end()) {
649                         (*i)->set_selected (false);
650                         tracks.erase (x);
651                 }
652         }
653 }
654
655 void
656 Selection::remove (ControlPoint* p)
657 {
658         PointSelection::iterator i = find (points.begin(), points.end(), p);
659         if (i != points.end ()) {
660                 points.erase (i);
661         }
662 }
663
664 void
665 Selection::remove (const MidiNoteSelection& midi_list)
666 {
667         bool changed = false;
668
669         for (MidiNoteSelection::const_iterator i = midi_list.begin(); i != midi_list.end(); ++i) {
670
671                 MidiNoteSelection::iterator x;
672
673                 if ((x = find (midi_notes.begin(), midi_notes.end(), (*i))) != midi_notes.end()) {
674                         midi_notes.erase (x);
675                         changed = true;
676                 }
677         }
678
679         if (changed) {
680                 MidiNotesChanged();
681         }
682 }
683
684 void
685 Selection::remove (MidiCutBuffer* midi)
686 {
687         MidiNoteSelection::iterator x;
688
689         if ((x = find (midi_notes.begin(), midi_notes.end(), midi)) != midi_notes.end()) {
690                 /* remember that we own the MCB */
691                 delete *x;
692                 midi_notes.erase (x);
693                 MidiNotesChanged ();
694         }
695 }
696
697 void
698 Selection::remove (boost::shared_ptr<Playlist> track)
699 {
700         list<boost::shared_ptr<Playlist> >::iterator i;
701         if ((i = find (playlists.begin(), playlists.end(), track)) != playlists.end()) {
702                 playlists.erase (i);
703                 PlaylistsChanged();
704         }
705 }
706
707 void
708 Selection::remove (const list<boost::shared_ptr<Playlist> >& pllist)
709 {
710         bool changed = false;
711
712         for (list<boost::shared_ptr<Playlist> >::const_iterator i = pllist.begin(); i != pllist.end(); ++i) {
713
714                 list<boost::shared_ptr<Playlist> >::iterator x;
715
716                 if ((x = find (playlists.begin(), playlists.end(), (*i))) != playlists.end()) {
717                         playlists.erase (x);
718                         changed = true;
719                 }
720         }
721
722         if (changed) {
723                 PlaylistsChanged();
724         }
725 }
726
727 void
728 Selection::remove (RegionView* r)
729 {
730         if (regions.remove (r)) {
731                 RegionsChanged ();
732         }
733 }
734
735 void
736 Selection::remove (MidiRegionView* mrv)
737 {
738         DEBUG_TRACE(DEBUG::Selection, string_compose("Selection::remove MRV %1\n", mrv));
739
740         MidiRegionSelection::iterator x;
741
742         if ((x = find (midi_regions.begin(), midi_regions.end(), mrv)) != midi_regions.end()) {
743                 midi_regions.erase (x);
744                 MidiRegionsChanged ();
745         }
746 }
747
748 void
749 Selection::remove (uint32_t selection_id)
750 {
751         if (time.empty()) {
752                 return;
753         }
754
755         for (list<AudioRange>::iterator i = time.begin(); i != time.end(); ++i) {
756                 if ((*i).id == selection_id) {
757                         time.erase (i);
758
759                         TimeChanged ();
760                         break;
761                 }
762         }
763 }
764
765 void
766 Selection::remove (framepos_t /*start*/, framepos_t /*end*/)
767 {
768 }
769
770 void
771 Selection::remove (boost::shared_ptr<ARDOUR::AutomationList> ac)
772 {
773         AutomationSelection::iterator i;
774         if ((i = find (lines.begin(), lines.end(), ac)) != lines.end()) {
775                 lines.erase (i);
776                 LinesChanged();
777         }
778 }
779
780 void
781 Selection::set (TimeAxisView* track)
782 {
783         clear_objects ();  //enforce object/range exclusivity
784
785         PresentationInfo::ChangeSuspender cs;
786
787         if (!tracks.empty()) {
788
789                 if (tracks.size() == 1 && tracks.front() == track) {
790                         /* already single selection: nothing to do */
791                         return;
792                 }
793
794                 for (TrackViewList::iterator x = tracks.begin(); x != tracks.end(); ++x) {
795                         (*x)->set_selected (false);
796                 }
797
798                 tracks.clear ();
799         }
800
801         add (track);
802 }
803
804 void
805 Selection::set (const TrackViewList& track_list)
806 {
807         clear_objects();  //enforce object/range exclusivity
808
809         PresentationInfo::ChangeSuspender cs;
810
811         if (!tracks.empty()) {
812
813                 /* cannot use set<T>::operator== (set<T> const &) here, because
814                  * apparently the ordering used within 2 sets is not
815                  * necessarily the same.
816                  */
817
818                 if (tracks.size() == track_list.size()) {
819                         bool missing = false;
820
821                         for (TrackViewList::const_iterator x = track_list.begin(); x != track_list.end(); ++x) {
822                                 if (find (tracks.begin(), tracks.end(), *x) == tracks.end()) {
823                                         missing = true;
824                                 }
825                         }
826
827                         if (!missing) {
828                                 /* already same selection: nothing to do */
829                                 return;
830                         }
831                 }
832
833                 /* argument is different from existing selection */
834
835                 for (TrackViewList::iterator x = tracks.begin(); x != tracks.end(); ++x) {
836                         (*x)->set_selected (false);
837                 }
838
839                 tracks.clear ();
840         }
841
842         add (track_list);
843 }
844
845 void
846 Selection::set (const MidiNoteSelection& midi_list)
847 {
848         clear_time ();  //enforce region/object exclusivity
849         clear_tracks();  //enforce object/track exclusivity
850         clear_objects ();
851         add (midi_list);
852 }
853
854 void
855 Selection::set (boost::shared_ptr<Playlist> playlist)
856 {
857         clear_time ();  //enforce region/object exclusivity
858         clear_tracks();  //enforce object/track exclusivity
859         clear_objects ();
860         add (playlist);
861 }
862
863 void
864 Selection::set (const list<boost::shared_ptr<Playlist> >& pllist)
865 {
866         clear_time();  //enforce region/object exclusivity
867         clear_objects ();
868         add (pllist);
869 }
870
871 void
872 Selection::set (const RegionSelection& rs)
873 {
874         clear_time();  //enforce region/object exclusivity
875         clear_tracks();  //enforce object/track exclusivity
876         clear_objects();
877         regions = rs;
878         RegionsChanged(); /* EMIT SIGNAL */
879 }
880
881 void
882 Selection::set (MidiRegionView* mrv)
883 {
884         clear_time();  //enforce region/object exclusivity
885         clear_tracks();  //enforce object/track exclusivity
886         clear_objects ();
887         add (mrv);
888 }
889
890 void
891 Selection::set (RegionView* r, bool /*also_clear_tracks*/)
892 {
893         clear_time();  //enforce region/object exclusivity
894         clear_tracks();  //enforce object/track exclusivity
895         clear_objects ();
896         add (r);
897 }
898
899 void
900 Selection::set (vector<RegionView*>& v)
901 {
902         clear_time();  //enforce region/object exclusivity
903         clear_tracks();  //enforce object/track exclusivity
904         clear_objects();
905
906         add (v);
907 }
908
909 /** Set the start and end time of the time selection, without changing
910  *  the list of tracks it applies to.
911  */
912 long
913 Selection::set (framepos_t start, framepos_t end)
914 {
915         clear_objects();  //enforce region/object exclusivity
916         clear_time();
917
918         if ((start == 0 && end == 0) || end < start) {
919                 return 0;
920         }
921
922         if (time.empty()) {
923                 time.push_back (AudioRange (start, end, ++next_time_id));
924         } else {
925                 /* reuse the first entry, and remove all the rest */
926
927                 while (time.size() > 1) {
928                         time.pop_front();
929                 }
930                 time.front().start = start;
931                 time.front().end = end;
932         }
933
934         time.consolidate ();
935
936         TimeChanged ();
937
938         return time.front().id;
939 }
940
941 /** Set the start and end of the range selection.  If more than one range
942  *  is currently selected, the start of the earliest range and the end of the
943  *  latest range are set.  If no range is currently selected, this method
944  *  selects a single range from start to end.
945  *
946  *  @param start New start time.
947  *  @param end New end time.
948  */
949 void
950 Selection::set_preserving_all_ranges (framepos_t start, framepos_t end)
951 {
952         clear_objects();  //enforce region/object exclusivity
953
954         if ((start == 0 && end == 0) || (end < start)) {
955                 return;
956         }
957
958         if (time.empty ()) {
959                 time.push_back (AudioRange (start, end, ++next_time_id));
960         } else {
961                 time.sort (AudioRangeComparator ());
962                 time.front().start = start;
963                 time.back().end = end;
964         }
965
966         time.consolidate ();
967
968         TimeChanged ();
969 }
970
971 void
972 Selection::set (boost::shared_ptr<Evoral::ControlList> ac)
973 {
974         clear_time();  //enforce region/object exclusivity
975         clear_tracks();  //enforce object/track exclusivity
976         clear_objects();
977
978         add (ac);
979 }
980
981 bool
982 Selection::selected (ArdourMarker* m)
983 {
984         return find (markers.begin(), markers.end(), m) != markers.end();
985 }
986
987 bool
988 Selection::selected (TimeAxisView* tv)
989 {
990         return tv->selected ();
991 }
992
993 bool
994 Selection::selected (RegionView* rv)
995 {
996         return find (regions.begin(), regions.end(), rv) != regions.end();
997 }
998
999 bool
1000 Selection::selected (ControlPoint* cp)
1001 {
1002         return find (points.begin(), points.end(), cp) != points.end();
1003 }
1004
1005 bool
1006 Selection::empty (bool internal_selection)
1007 {
1008         bool object_level_empty =  regions.empty () &&
1009                 tracks.empty () &&
1010                 points.empty () &&
1011                 playlists.empty () &&
1012                 lines.empty () &&
1013                 time.empty () &&
1014                 playlists.empty () &&
1015                 markers.empty() &&
1016                 midi_regions.empty()
1017                 ;
1018
1019         if (!internal_selection) {
1020                 return object_level_empty;
1021         }
1022
1023         /* this is intended to really only apply when using a Selection
1024            as a cut buffer.
1025         */
1026
1027         return object_level_empty && midi_notes.empty() && points.empty();
1028 }
1029
1030 void
1031 Selection::toggle (ControlPoint* cp)
1032 {
1033         clear_time();  //enforce region/object exclusivity
1034         clear_tracks();  //enforce object/track exclusivity
1035
1036         cp->set_selected (!cp->selected ());
1037         PointSelection::iterator i = find (points.begin(), points.end(), cp);
1038         if (i == points.end()) {
1039                 points.push_back (cp);
1040         } else {
1041                 points.erase (i);
1042         }
1043
1044         PointsChanged (); /* EMIT SIGNAL */
1045 }
1046
1047 void
1048 Selection::toggle (vector<ControlPoint*> const & cps)
1049 {
1050         clear_time();  //enforce region/object exclusivity
1051         clear_tracks();  //enforce object/track exclusivity
1052
1053         for (vector<ControlPoint*>::const_iterator i = cps.begin(); i != cps.end(); ++i) {
1054                 toggle (*i);
1055         }
1056 }
1057
1058 void
1059 Selection::toggle (list<Selectable*> const & selectables)
1060 {
1061         clear_time();  //enforce region/object exclusivity
1062         clear_tracks();  //enforce object/track exclusivity
1063
1064         RegionView* rv;
1065         ControlPoint* cp;
1066         vector<RegionView*> rvs;
1067         vector<ControlPoint*> cps;
1068
1069         for (std::list<Selectable*>::const_iterator i = selectables.begin(); i != selectables.end(); ++i) {
1070                 if ((rv = dynamic_cast<RegionView*> (*i)) != 0) {
1071                         rvs.push_back (rv);
1072                 } else if ((cp = dynamic_cast<ControlPoint*> (*i)) != 0) {
1073                         cps.push_back (cp);
1074                 } else {
1075                         fatal << _("programming error: ")
1076                               << X_("unknown selectable type passed to Selection::toggle()")
1077                               << endmsg;
1078                         abort(); /*NOTREACHED*/
1079                 }
1080         }
1081
1082         if (!rvs.empty()) {
1083                 toggle (rvs);
1084         }
1085
1086         if (!cps.empty()) {
1087                 toggle (cps);
1088         }
1089 }
1090
1091 void
1092 Selection::set (list<Selectable*> const & selectables)
1093 {
1094         clear_time ();  //enforce region/object exclusivity
1095         clear_tracks();  //enforce object/track exclusivity
1096         clear_objects ();
1097
1098         add (selectables);
1099 }
1100
1101 void
1102 Selection::add (PointSelection const & s)
1103 {
1104         clear_time ();  //enforce region/object exclusivity
1105         clear_tracks();  //enforce object/track exclusivity
1106
1107         for (PointSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
1108                 points.push_back (*i);
1109         }
1110 }
1111
1112 void
1113 Selection::add (list<Selectable*> const & selectables)
1114 {
1115         clear_time ();  //enforce region/object exclusivity
1116         clear_tracks();  //enforce object/track exclusivity
1117
1118         RegionView* rv;
1119         ControlPoint* cp;
1120         vector<RegionView*> rvs;
1121         vector<ControlPoint*> cps;
1122
1123         for (std::list<Selectable*>::const_iterator i = selectables.begin(); i != selectables.end(); ++i) {
1124                 if ((rv = dynamic_cast<RegionView*> (*i)) != 0) {
1125                         rvs.push_back (rv);
1126                 } else if ((cp = dynamic_cast<ControlPoint*> (*i)) != 0) {
1127                         cps.push_back (cp);
1128                 } else {
1129                         fatal << _("programming error: ")
1130                               << X_("unknown selectable type passed to Selection::add()")
1131                               << endmsg;
1132                         abort(); /*NOTREACHED*/
1133                 }
1134         }
1135
1136         if (!rvs.empty()) {
1137                 add (rvs);
1138         }
1139
1140         if (!cps.empty()) {
1141                 add (cps);
1142         }
1143 }
1144
1145 void
1146 Selection::clear_points (bool with_signal)
1147 {
1148         if (!points.empty()) {
1149                 points.clear ();
1150                 if (with_signal) {
1151                         PointsChanged ();
1152                 }
1153         }
1154 }
1155
1156 void
1157 Selection::add (ControlPoint* cp)
1158 {
1159         clear_time ();  //enforce region/object exclusivity
1160         clear_tracks();  //enforce object/track exclusivity
1161
1162         cp->set_selected (true);
1163         points.push_back (cp);
1164         PointsChanged (); /* EMIT SIGNAL */
1165 }
1166
1167 void
1168 Selection::add (vector<ControlPoint*> const & cps)
1169 {
1170         clear_time ();  //enforce region/object exclusivity
1171         clear_tracks();  //enforce object/track exclusivity
1172
1173         for (vector<ControlPoint*>::const_iterator i = cps.begin(); i != cps.end(); ++i) {
1174                 (*i)->set_selected (true);
1175                 points.push_back (*i);
1176         }
1177         PointsChanged (); /* EMIT SIGNAL */
1178 }
1179
1180 void
1181 Selection::set (ControlPoint* cp)
1182 {
1183         clear_time ();  //enforce region/object exclusivity
1184         clear_tracks();  //enforce object/track exclusivity
1185
1186         if (cp->selected () && points.size () == 1) {
1187                 return;
1188         }
1189
1190         for (uint32_t i = 0; i < cp->line().npoints(); ++i) {
1191                 cp->line().nth (i)->set_selected (false);
1192         }
1193
1194         clear_objects ();
1195         add (cp);
1196 }
1197
1198 void
1199 Selection::set (ArdourMarker* m)
1200 {
1201         clear_time ();  //enforce region/object exclusivity
1202         clear_tracks();  //enforce object/track exclusivity
1203         markers.clear ();
1204
1205         add (m);
1206 }
1207
1208 void
1209 Selection::toggle (ArdourMarker* m)
1210 {
1211         MarkerSelection::iterator i;
1212
1213         if ((i = find (markers.begin(), markers.end(), m)) == markers.end()) {
1214                 add (m);
1215         } else {
1216                 remove (m);
1217         }
1218 }
1219
1220 void
1221 Selection::remove (ArdourMarker* m)
1222 {
1223         MarkerSelection::iterator i;
1224
1225         if ((i = find (markers.begin(), markers.end(), m)) != markers.end()) {
1226                 markers.erase (i);
1227                 MarkersChanged();
1228         }
1229 }
1230
1231 void
1232 Selection::add (ArdourMarker* m)
1233 {
1234         clear_time ();  //enforce region/object exclusivity
1235         clear_tracks();  //enforce object/track exclusivity
1236
1237         if (find (markers.begin(), markers.end(), m) == markers.end()) {
1238                 markers.push_back (m);
1239                 MarkersChanged();
1240         }
1241 }
1242
1243 void
1244 Selection::add (const list<ArdourMarker*>& m)
1245 {
1246         clear_time ();  //enforce region/object exclusivity
1247         clear_tracks();  //enforce object/track exclusivity
1248
1249         markers.insert (markers.end(), m.begin(), m.end());
1250         markers.sort ();
1251         markers.unique ();
1252
1253         MarkersChanged ();
1254 }
1255
1256 void
1257 MarkerSelection::range (framepos_t& s, framepos_t& e)
1258 {
1259         s = max_framepos;
1260         e = 0;
1261
1262         for (MarkerSelection::iterator i = begin(); i != end(); ++i) {
1263
1264                 if ((*i)->position() < s) {
1265                         s = (*i)->position();
1266                 }
1267
1268                 if ((*i)->position() > e) {
1269                         e = (*i)->position();
1270                 }
1271         }
1272
1273         s = std::min (s, e);
1274         e = std::max (s, e);
1275 }
1276
1277 XMLNode&
1278 Selection::get_state () const
1279 {
1280         /* XXX: not complete; just sufficient to get track selection state
1281            so that re-opening plugin windows for editor mixer strips works
1282         */
1283
1284         char buf[32];
1285         XMLNode* node = new XMLNode (X_("Selection"));
1286
1287         for (TrackSelection::const_iterator i = tracks.begin(); i != tracks.end(); ++i) {
1288                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
1289                 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (*i);
1290                 if (rtv) {
1291                         XMLNode* t = node->add_child (X_("RouteView"));
1292                         t->add_property (X_("id"), atoi (rtv->route()->id().to_s().c_str()));
1293                 } else if (atv) {
1294                         XMLNode* t = node->add_child (X_("AutomationView"));
1295                         t->add_property (X_("id"), atoi (atv->parent_route()->id().to_s().c_str()));
1296                         t->add_property (X_("parameter"), EventTypeMap::instance().to_symbol (atv->parameter ()));
1297                 }
1298         }
1299
1300         for (RegionSelection::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1301                 XMLNode* r = node->add_child (X_("Region"));
1302                 r->add_property (X_("id"), atoi ((*i)->region ()->id ().to_s ().c_str()));
1303         }
1304
1305         /* midi region views have thir own internal selection. */
1306         list<pair<PBD::ID, std::set<boost::shared_ptr<Evoral::Note<Evoral::Beats> > > > > rid_notes;
1307         editor->get_per_region_note_selection (rid_notes);
1308
1309         list<pair<PBD::ID, std::set<boost::shared_ptr<Evoral::Note<Evoral::Beats> > > > >::iterator rn_it;
1310         for (rn_it = rid_notes.begin(); rn_it != rid_notes.end(); ++rn_it) {
1311                 XMLNode* n = node->add_child (X_("MIDINotes"));
1312                 n->add_property (X_("region-id"), atoi((*rn_it).first.to_s().c_str()));
1313
1314                 for (std::set<boost::shared_ptr<Evoral::Note<Evoral::Beats> > >::iterator i = (*rn_it).second.begin(); i != (*rn_it).second.end(); ++i) {
1315                         XMLNode* nc = n->add_child(X_("note"));
1316
1317                         snprintf(buf, sizeof(buf), "%d", (*i)->id());
1318                         nc->add_property (X_("note-id"), string(buf));
1319                 }
1320         }
1321
1322         for (PointSelection::const_iterator i = points.begin(); i != points.end(); ++i) {
1323                 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (&(*i)->line().trackview);
1324                 if (atv) {
1325
1326                         XMLNode* r = node->add_child (X_("ControlPoint"));
1327                         r->add_property (X_("type"), "track");
1328                         r->add_property (X_("route-id"), atoi (atv->parent_route()->id ().to_s ().c_str()));
1329                         r->add_property (X_("automation-list-id"), atoi ((*i)->line().the_list()->id ().to_s ().c_str()));
1330                         r->add_property (X_("parameter"), EventTypeMap::instance().to_symbol ((*i)->line().the_list()->parameter ()));
1331
1332                         snprintf(buf, sizeof(buf), "%d", (*i)->view_index());
1333                         r->add_property (X_("view-index"), string(buf));
1334                         continue;
1335                 }
1336
1337                 AudioRegionGainLine* argl = dynamic_cast<AudioRegionGainLine*> (&(*i)->line());
1338                 if (argl) {
1339                         XMLNode* r = node->add_child (X_("ControlPoint"));
1340                         r->add_property (X_("type"), "region");
1341                         r->add_property (X_("region-id"), atoi (argl->region_view ().region ()->id ().to_s ().c_str()));
1342                         snprintf(buf, sizeof(buf), "%d", (*i)->view_index());
1343                         r->add_property (X_("view-index"), string(buf));
1344                 }
1345
1346         }
1347
1348         for (TimeSelection::const_iterator i = time.begin(); i != time.end(); ++i) {
1349                 XMLNode* t = node->add_child (X_("AudioRange"));
1350                 snprintf(buf, sizeof(buf), "%" PRId64, (*i).start);
1351                 t->add_property (X_("start"), string(buf));
1352                 snprintf(buf, sizeof(buf), "%" PRId64, (*i).end);
1353                 t->add_property (X_("end"), string(buf));
1354         }
1355
1356         for (MarkerSelection::const_iterator i = markers.begin(); i != markers.end(); ++i) {
1357                 XMLNode* t = node->add_child (X_("Marker"));
1358
1359                 bool is_start;
1360                 Location* loc = editor->find_location_from_marker (*i, is_start);
1361
1362                 t->add_property (X_("id"), atoi (loc->id().to_s().c_str()));
1363                 t->add_property (X_("start"), is_start ? X_("yes") : X_("no"));
1364         }
1365
1366         return *node;
1367 }
1368
1369 int
1370 Selection::set_state (XMLNode const & node, int)
1371 {
1372         if (node.name() != X_("Selection")) {
1373                 return -1;
1374         }
1375
1376         clear_regions ();
1377         clear_midi_notes ();
1378         clear_points ();
1379         clear_time ();
1380         clear_tracks ();
1381         clear_markers ();
1382
1383         XMLNodeList children = node.children ();
1384         for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
1385                 if ((*i)->name() == X_("RouteView")) {
1386
1387                         XMLProperty const * prop_id = (*i)->property (X_("id"));
1388                         assert (prop_id);
1389                         PBD::ID id (prop_id->value ());
1390                         RouteTimeAxisView* rtv = editor->get_route_view_by_route_id (id);
1391                         if (rtv) {
1392                                 add (rtv);
1393                         }
1394
1395                 } else if ((*i)->name() == X_("Region")) {
1396                         XMLProperty const * prop_id = (*i)->property (X_("id"));
1397                         assert (prop_id);
1398                         PBD::ID id (prop_id->value ());
1399
1400                         RegionSelection rs;
1401                         editor->get_regionviews_by_id (id, rs);
1402
1403                         if (!rs.empty ()) {
1404                                 add (rs);
1405                         } else {
1406                                 /*
1407                                   regionviews haven't been constructed - stash the region IDs
1408                                   so we can identify them in Editor::region_view_added ()
1409                                 */
1410                                 regions.pending.push_back (id);
1411                         }
1412
1413                 } else if ((*i)->name() == X_("MIDINotes")) {
1414                         XMLProperty const * prop_region_id = (*i)->property (X_("region-id"));
1415
1416                         assert (prop_region_id);
1417
1418                         PBD::ID const id (prop_region_id->value ());
1419                         RegionSelection rs;
1420
1421                         editor->get_regionviews_by_id (id, rs); // there could be more than one
1422
1423                         std::list<Evoral::event_id_t> notes;
1424                         XMLNodeList children = (*i)->children ();
1425
1426                         for (XMLNodeList::const_iterator ci = children.begin(); ci != children.end(); ++ci) {
1427                                 XMLProperty const * prop_id = (*ci)->property (X_("note-id"));
1428                                 if (prop_id) {
1429                                         Evoral::event_id_t id = atoi(prop_id->value());
1430                                         notes.push_back (id);
1431                                 }
1432                         }
1433
1434                         for (RegionSelection::iterator rsi = rs.begin(); rsi != rs.end(); ++rsi) {
1435                                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (*rsi);
1436                                 if (mrv) {
1437                                         mrv->select_notes(notes);
1438                                 }
1439                         }
1440
1441                         if (rs.empty()) {
1442                                 /* regionviews containing these notes don't yet exist on the canvas.*/
1443                                 pending_midi_note_selection.push_back (make_pair (id, notes));
1444                         }
1445
1446                 } else if  ((*i)->name() == X_("ControlPoint")) {
1447                         XMLProperty const * prop_type = (*i)->property (X_("type"));
1448
1449                         assert(prop_type);
1450
1451                         if (prop_type->value () == "track") {
1452
1453                                 XMLProperty const * prop_route_id = (*i)->property (X_("route-id"));
1454                                 XMLProperty const * prop_alist_id = (*i)->property (X_("automation-list-id"));
1455                                 XMLProperty const * prop_parameter = (*i)->property (X_("parameter"));
1456                                 XMLProperty const * prop_view_index = (*i)->property (X_("view-index"));
1457
1458                                 assert (prop_route_id);
1459                                 assert (prop_alist_id);
1460                                 assert (prop_parameter);
1461                                 assert (prop_view_index);
1462
1463                                 PBD::ID route_id (prop_route_id->value ());
1464                                 RouteTimeAxisView* rtv = editor->get_route_view_by_route_id (route_id);
1465                                 vector <ControlPoint *> cps;
1466
1467                                 if (rtv) {
1468                                         boost::shared_ptr<AutomationTimeAxisView> atv = rtv->automation_child (EventTypeMap::instance().from_symbol (prop_parameter->value ()));
1469                                         if (atv) {
1470                                                 list<boost::shared_ptr<AutomationLine> > lines = atv->lines();
1471                                                 for (list<boost::shared_ptr<AutomationLine> > ::iterator li = lines.begin(); li != lines.end(); ++li) {
1472                                                         if ((*li)->the_list()->id() == prop_alist_id->value()) {
1473                                                                 ControlPoint* cp = (*li)->nth(atol(prop_view_index->value().c_str()));
1474                                                                 if (cp) {
1475                                                                         cps.push_back (cp);
1476                                                                         cp->show();
1477                                                                 }
1478                                                         }
1479                                                 }
1480                                         }
1481                                 }
1482                                 if (!cps.empty()) {
1483                                         add (cps);
1484                                 }
1485                         } else if (prop_type->value () == "region") {
1486                                 XMLProperty const * prop_region_id = (*i)->property (X_("region-id"));
1487                                 XMLProperty const * prop_view_index = (*i)->property (X_("view-index"));
1488
1489                                 if (!prop_region_id || !prop_view_index) {
1490                                         continue;
1491                                 }
1492
1493                                 PBD::ID region_id (prop_region_id->value ());
1494                                 RegionSelection rs;
1495                                 editor->get_regionviews_by_id (region_id, rs);
1496
1497                                 if (!rs.empty ()) {
1498                                         vector <ControlPoint *> cps;
1499                                         for (RegionSelection::iterator rsi = rs.begin(); rsi != rs.end(); ++rsi) {
1500                                                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*rsi);
1501                                                 if (arv) {
1502                                                         boost::shared_ptr<AudioRegionGainLine> gl = arv->get_gain_line ();
1503                                                         ControlPoint* cp = gl->nth(atol(prop_view_index->value().c_str()));
1504                                                         if (cp) {
1505                                                                 cps.push_back (cp);
1506                                                                 cp->show();
1507                                                         }
1508                                                 }
1509                                         }
1510                                         if (!cps.empty()) {
1511                                                 add (cps);
1512                                         }
1513                                 }
1514                         }
1515
1516                 } else if  ((*i)->name() == X_("AudioRange")) {
1517                         XMLProperty const * prop_start = (*i)->property (X_("start"));
1518                         XMLProperty const * prop_end = (*i)->property (X_("end"));
1519
1520                         assert (prop_start);
1521                         assert (prop_end);
1522
1523                         framepos_t s (atol (prop_start->value ().c_str()));
1524                         framepos_t e (atol (prop_end->value ().c_str()));
1525
1526                         set_preserving_all_ranges (s, e);
1527
1528                 } else if ((*i)->name() == X_("AutomationView")) {
1529
1530                         XMLProperty const * prop_id = (*i)->property (X_("id"));
1531                         XMLProperty const * prop_parameter = (*i)->property (X_("parameter"));
1532
1533                         assert (prop_id);
1534                         assert (prop_parameter);
1535
1536                         PBD::ID id (prop_id->value ());
1537                         RouteTimeAxisView* rtv = editor->get_route_view_by_route_id (id);
1538
1539                         if (rtv) {
1540                                 boost::shared_ptr<AutomationTimeAxisView> atv = rtv->automation_child (EventTypeMap::instance().from_symbol (prop_parameter->value ()));
1541
1542                                 /* the automation could be for an entity that was never saved
1543                                    in the session file. Don't freak out if we can't find
1544                                    it.
1545                                 */
1546
1547                                 if (atv) {
1548                                         add (atv.get());
1549                                 }
1550                         }
1551
1552                 } else if ((*i)->name() == X_("Marker")) {
1553
1554                         XMLProperty const * prop_id = (*i)->property (X_("id"));
1555                         XMLProperty const * prop_start = (*i)->property (X_("start"));
1556                         assert (prop_id);
1557                         assert (prop_start);
1558
1559                         PBD::ID id (prop_id->value ());
1560                         ArdourMarker* m = editor->find_marker_from_location_id (id, string_is_affirmative (prop_start->value ()));
1561                         if (m) {
1562                                 add (m);
1563                         }
1564
1565                 }
1566
1567         }
1568
1569         return 0;
1570 }
1571
1572 void
1573 Selection::remove_regions (TimeAxisView* t)
1574 {
1575         RegionSelection::iterator i = regions.begin();
1576         while (i != regions.end ()) {
1577                 RegionSelection::iterator tmp = i;
1578                 ++tmp;
1579
1580                 if (&(*i)->get_time_axis_view() == t) {
1581                         remove (*i);
1582                 }
1583
1584                 i = tmp;
1585         }
1586 }