use new syntax for connecting to backend signals that enforces explicit connection...
[ardour.git] / gtk2_ardour / selection.cc
1 /*
2     Copyright (C) 2002 Paul Davis
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include <algorithm>
21 #include <sigc++/bind.h>
22 #include "pbd/error.h"
23 #include "pbd/stacktrace.h"
24
25 #include "ardour/playlist.h"
26 #include "ardour/rc_configuration.h"
27
28 #include "midi_cut_buffer.h"
29 #include "region_view.h"
30 #include "selection.h"
31 #include "selection_templates.h"
32 #include "time_axis_view.h"
33 #include "automation_time_axis.h"
34 #include "public_editor.h"
35
36 #include "i18n.h"
37
38 using namespace std;
39 using namespace ARDOUR;
40 using namespace PBD;
41
42 struct AudioRangeComparator {
43     bool operator()(AudioRange a, AudioRange b) {
44             return a.start < b.start;
45     }
46 };
47
48 #if 0
49 Selection&
50 Selection::operator= (const Selection& other)
51 {
52         if (&other != this) {
53                 regions = other.regions;
54                 tracks = other.tracks;
55                 time = other.time;
56                 lines = other.lines;
57                 midi_regions = other.midi_regions;
58                 midi_notes = other.midi_notes;
59         }
60         return *this;
61 }
62 #endif
63
64 bool
65 operator== (const Selection& a, const Selection& b)
66 {
67         return a.regions == b.regions &&
68                 a.tracks == b.tracks &&
69                 a.time == b.time &&
70                 a.lines == b.lines &&
71                 a.playlists == b.playlists &&
72                 a.midi_notes == b.midi_notes &&
73                 a.midi_regions == b.midi_regions;
74 }
75
76 /** Clear everything from the Selection */
77 void
78 Selection::clear ()
79 {
80         clear_tracks ();
81         clear_regions ();
82         clear_points ();
83         clear_lines();
84         clear_time ();
85         clear_playlists ();
86         clear_midi_notes ();
87         clear_midi_regions ();
88 }
89
90 void
91 Selection::dump_region_layers()
92 {
93         cerr << "region selection layer dump" << endl;
94         for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
95                 cerr << "layer: " << (int)(*i)->region()->layer() << endl;
96         }
97 }
98
99
100 void
101 Selection::clear_regions ()
102 {
103         if (!regions.empty()) {
104                 regions.clear_all ();
105                 RegionsChanged();
106         }
107 }
108
109 void
110 Selection::clear_tracks ()
111 {
112         if (!tracks.empty()) {
113                 tracks.clear ();
114                 TracksChanged();
115         }
116 }
117
118 void
119 Selection::clear_midi_notes ()
120 {
121         if (!midi_notes.empty()) {
122                 for (MidiNoteSelection::iterator x = midi_notes.begin(); x != midi_notes.end(); ++x) {
123                         delete *x;
124                 }
125                 midi_notes.clear ();
126                 MidiNotesChanged ();
127         }
128 }
129
130 void
131 Selection::clear_midi_regions ()
132 {
133         if (!midi_regions.empty()) {
134                 midi_regions.clear ();
135                 MidiRegionsChanged ();
136         }
137 }
138
139 void
140 Selection::clear_time ()
141 {
142         time.clear();
143
144         TimeChanged ();
145 }
146
147 void
148 Selection::clear_playlists ()
149 {
150         /* Selections own their playlists */
151
152         for (PlaylistSelection::iterator i = playlists.begin(); i != playlists.end(); ++i) {
153                 /* selections own their own regions, which are copies of the "originals". make them go away */
154                 (*i)->drop_regions ();
155                 (*i)->release ();
156         }
157
158         if (!playlists.empty()) {
159                 playlists.clear ();
160                 PlaylistsChanged();
161         }
162 }
163
164 void
165 Selection::clear_lines ()
166 {
167         if (!lines.empty()) {
168                 lines.clear ();
169                 LinesChanged();
170         }
171 }
172
173 void
174 Selection::clear_markers ()
175 {
176         if (!markers.empty()) {
177                 markers.clear ();
178                 MarkersChanged();
179         }
180 }
181
182 void
183 Selection::toggle (boost::shared_ptr<Playlist> pl)
184 {
185         PlaylistSelection::iterator i;
186
187         if ((i = find (playlists.begin(), playlists.end(), pl)) == playlists.end()) {
188                 pl->use ();
189                 playlists.push_back(pl);
190         } else {
191                 playlists.erase (i);
192         }
193
194         PlaylistsChanged ();
195 }
196
197 void
198 Selection::toggle (const TrackViewList& track_list)
199 {
200         for (TrackViewList::const_iterator i = track_list.begin(); i != track_list.end(); ++i) {
201                 toggle ((*i));
202         }
203 }
204
205 void
206 Selection::toggle (TimeAxisView* track)
207 {
208         TrackSelection::iterator i;
209
210         if ((i = find (tracks.begin(), tracks.end(), track)) == tracks.end()) {
211                 void (Selection::*pmf)(TimeAxisView*) = &Selection::remove;
212                 track->GoingAway.connect (*this, boost::bind (pmf, this, track));
213                 tracks.push_back (track);
214         } else {
215                 tracks.erase (i);
216         }
217
218         TracksChanged();
219 }
220
221 void
222 Selection::toggle (const MidiNoteSelection& midi_note_list)
223 {
224         for (MidiNoteSelection::const_iterator i = midi_note_list.begin(); i != midi_note_list.end(); ++i) {
225                 toggle ((*i));
226         }
227 }
228
229 void
230 Selection::toggle (MidiCutBuffer* midi)
231 {
232         MidiNoteSelection::iterator i;
233
234         if ((i = find (midi_notes.begin(), midi_notes.end(), midi)) == midi_notes.end()) {
235                 midi_notes.push_back (midi);
236         } else {
237                 /* remember that we own the MCB */
238                 delete *i;
239                 midi_notes.erase (i);
240         }
241
242         MidiNotesChanged();
243 }
244
245
246 void
247 Selection::toggle (RegionView* r)
248 {
249         RegionSelection::iterator i;
250
251         if ((i = find (regions.begin(), regions.end(), r)) == regions.end()) {
252                 add (r);
253         } else {
254                 remove (*i);
255         }
256
257         RegionsChanged ();
258 }
259
260 void
261 Selection::toggle (MidiRegionView* mrv)
262 {
263         MidiRegionSelection::iterator i;
264
265         if ((i = find (midi_regions.begin(), midi_regions.end(), mrv)) == midi_regions.end()) {
266                 add (mrv);
267         } else {
268                 midi_regions.erase (i);
269         }
270
271         MidiRegionsChanged ();
272 }
273
274 void
275 Selection::toggle (vector<RegionView*>& r)
276 {
277         RegionSelection::iterator i;
278
279         for (vector<RegionView*>::iterator x = r.begin(); x != r.end(); ++x) {
280                 if ((i = find (regions.begin(), regions.end(), (*x))) == regions.end()) {
281                         add ((*x));
282                 } else {
283                         remove (*x);
284                 }
285         }
286
287         RegionsChanged ();
288 }
289
290 long
291 Selection::toggle (nframes_t start, nframes_t end)
292 {
293         AudioRangeComparator cmp;
294
295         /* XXX this implementation is incorrect */
296
297         time.push_back (AudioRange (start, end, next_time_id++));
298         time.consolidate ();
299         time.sort (cmp);
300
301         TimeChanged ();
302
303         return next_time_id - 1;
304 }
305
306 void
307 Selection::add (boost::shared_ptr<Playlist> pl)
308 {
309         if (find (playlists.begin(), playlists.end(), pl) == playlists.end()) {
310                 pl->use ();
311                 playlists.push_back(pl);
312                 PlaylistsChanged ();
313         }
314 }
315
316 void
317 Selection::add (const list<boost::shared_ptr<Playlist> >& pllist)
318 {
319         bool changed = false;
320
321         for (list<boost::shared_ptr<Playlist> >::const_iterator i = pllist.begin(); i != pllist.end(); ++i) {
322                 if (find (playlists.begin(), playlists.end(), (*i)) == playlists.end()) {
323                         (*i)->use ();
324                         playlists.push_back (*i);
325                         changed = true;
326                 }
327         }
328
329         if (changed) {
330                 PlaylistsChanged ();
331         }
332 }
333
334 void
335 Selection::add (const TrackViewList& track_list)
336 {
337         TrackViewList added = tracks.add (track_list);
338
339         for (list<TimeAxisView*>::const_iterator i = added.begin(); i != added.end(); ++i) {
340                 void (Selection::*pmf)(TimeAxisView*) = &Selection::remove;
341                 (*i)->GoingAway.connect (*this, boost::bind (pmf, this, (*i)));
342         }
343
344         if (!added.empty()) {
345                 TracksChanged ();
346         }
347 }
348
349 void
350 Selection::add (TimeAxisView* track)
351 {
352         TrackViewList tr;
353         tr.push_back (track);
354         add (tr);
355 }
356
357 void
358 Selection::add (const MidiNoteSelection& midi_list)
359 {
360         const MidiNoteSelection::const_iterator b = midi_list.begin();
361         const MidiNoteSelection::const_iterator e = midi_list.end();
362
363         if (!midi_list.empty()) {
364                 midi_notes.insert (midi_notes.end(), b, e);
365                 MidiNotesChanged ();
366         }
367 }
368
369 void
370 Selection::add (MidiCutBuffer* midi)
371 {
372         /* we take ownership of the MCB */
373
374         if (find (midi_notes.begin(), midi_notes.end(), midi) == midi_notes.end()) {
375                 midi_notes.push_back (midi);
376                 MidiNotesChanged ();
377         }
378 }
379
380 void
381 Selection::add (vector<RegionView*>& v)
382 {
383         /* XXX This method or the add (const RegionSelection&) needs to go
384          */
385
386         bool changed = false;
387
388         for (vector<RegionView*>::iterator i = v.begin(); i != v.end(); ++i) {
389                 if (find (regions.begin(), regions.end(), (*i)) == regions.end()) {
390                         changed = regions.add ((*i));
391                         if (Config->get_link_region_and_track_selection() && changed) {
392                                 add (&(*i)->get_trackview());
393                         }
394                 }
395         }
396
397         if (changed) {
398                 RegionsChanged ();
399         }
400 }
401
402 void
403 Selection::add (const RegionSelection& rs)
404 {
405         /* XXX This method or the add (const vector<RegionView*>&) needs to go
406          */
407
408         bool changed = false;
409
410         for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
411                 if (find (regions.begin(), regions.end(), (*i)) == regions.end()) {
412                         changed = regions.add ((*i));
413                         if (Config->get_link_region_and_track_selection() && changed) {
414                                 add (&(*i)->get_trackview());
415                         }
416                 }
417         }
418
419         if (changed) {
420                 RegionsChanged ();
421         }
422 }
423
424 void
425 Selection::add (RegionView* r)
426 {
427         if (find (regions.begin(), regions.end(), r) == regions.end()) {
428                 regions.add (r);
429                 if (Config->get_link_region_and_track_selection()) {
430                         add (&r->get_trackview());
431                 }
432                 RegionsChanged ();
433         }
434 }
435
436 void
437 Selection::add (MidiRegionView* mrv)
438 {
439         if (find (midi_regions.begin(), midi_regions.end(), mrv) == midi_regions.end()) {
440                 midi_regions.push_back (mrv);
441                 /* XXX should we do this? */
442 #if 0
443                 if (Config->get_link_region_and_track_selection()) {
444                         add (&mrv->get_trackview());
445                 }
446 #endif
447                 MidiRegionsChanged ();
448         }
449 }
450
451 long
452 Selection::add (nframes_t start, nframes_t end)
453 {
454         AudioRangeComparator cmp;
455
456         /* XXX this implementation is incorrect */
457
458         time.push_back (AudioRange (start, end, next_time_id++));
459         time.consolidate ();
460         time.sort (cmp);
461
462         TimeChanged ();
463
464         return next_time_id - 1;
465 }
466
467 void
468 Selection::replace (uint32_t sid, nframes_t start, nframes_t end)
469 {
470         for (list<AudioRange>::iterator i = time.begin(); i != time.end(); ++i) {
471                 if ((*i).id == sid) {
472                         time.erase (i);
473                         time.push_back (AudioRange(start,end, sid));
474
475                         /* don't consolidate here */
476
477
478                         AudioRangeComparator cmp;
479                         time.sort (cmp);
480
481                         TimeChanged ();
482                         break;
483                 }
484         }
485 }
486
487 void
488 Selection::add (boost::shared_ptr<Evoral::ControlList> cl)
489 {
490         boost::shared_ptr<ARDOUR::AutomationList> al
491                 = boost::dynamic_pointer_cast<ARDOUR::AutomationList>(cl);
492         if (!al) {
493                 warning << "Programming error: Selected list is not an ARDOUR::AutomationList" << endmsg;
494                 return;
495                 return;
496         }
497         if (find (lines.begin(), lines.end(), al) == lines.end()) {
498                 lines.push_back (al);
499                 LinesChanged();
500         }
501 }
502
503 void
504 Selection::remove (TimeAxisView* track)
505 {
506         list<TimeAxisView*>::iterator i;
507         if ((i = find (tracks.begin(), tracks.end(), track)) != tracks.end()) {
508                 tracks.erase (i);
509                 TracksChanged();
510         }
511 }
512
513 void
514 Selection::remove (const TrackViewList& track_list)
515 {
516         bool changed = false;
517
518         for (TrackViewList::const_iterator i = track_list.begin(); i != track_list.end(); ++i) {
519
520                 TrackViewList::iterator x = find (tracks.begin(), tracks.end(), *i);
521                 if (x != tracks.end()) {
522                         tracks.erase (x);
523                         changed = true;
524                 }
525         }
526
527         if (changed) {
528                 TracksChanged();
529         }
530 }
531
532 void
533 Selection::remove (const MidiNoteSelection& midi_list)
534 {
535         bool changed = false;
536
537         for (MidiNoteSelection::const_iterator i = midi_list.begin(); i != midi_list.end(); ++i) {
538
539                 MidiNoteSelection::iterator x;
540
541                 if ((x = find (midi_notes.begin(), midi_notes.end(), (*i))) != midi_notes.end()) {
542                         midi_notes.erase (x);
543                         changed = true;
544                 }
545         }
546
547         if (changed) {
548                 MidiNotesChanged();
549         }
550 }
551
552 void
553 Selection::remove (MidiCutBuffer* midi)
554 {
555         MidiNoteSelection::iterator x;
556
557         if ((x = find (midi_notes.begin(), midi_notes.end(), midi)) != midi_notes.end()) {
558                 /* remember that we own the MCB */
559                 delete *x;
560                 midi_notes.erase (x);
561                 MidiNotesChanged ();
562         }
563 }
564
565 void
566 Selection::remove (boost::shared_ptr<Playlist> track)
567 {
568         list<boost::shared_ptr<Playlist> >::iterator i;
569         if ((i = find (playlists.begin(), playlists.end(), track)) != playlists.end()) {
570                 playlists.erase (i);
571                 PlaylistsChanged();
572         }
573 }
574
575 void
576 Selection::remove (const list<boost::shared_ptr<Playlist> >& pllist)
577 {
578         bool changed = false;
579
580         for (list<boost::shared_ptr<Playlist> >::const_iterator i = pllist.begin(); i != pllist.end(); ++i) {
581
582                 list<boost::shared_ptr<Playlist> >::iterator x;
583
584                 if ((x = find (playlists.begin(), playlists.end(), (*i))) != playlists.end()) {
585                         playlists.erase (x);
586                         changed = true;
587                 }
588         }
589
590         if (changed) {
591                 PlaylistsChanged();
592         }
593 }
594
595 void
596 Selection::remove (RegionView* r)
597 {
598         if (regions.remove (r)) {
599                 RegionsChanged ();
600         }
601
602         if (Config->get_link_region_and_track_selection() && !regions.involves (r->get_trackview())) {
603                 remove (&r->get_trackview());
604         }
605 }
606
607 void
608 Selection::remove (MidiRegionView* mrv)
609 {
610         MidiRegionSelection::iterator x;
611
612         if ((x = find (midi_regions.begin(), midi_regions.end(), mrv)) != midi_regions.end()) {
613                 midi_regions.erase (x);
614                 MidiRegionsChanged ();
615         }
616
617 #if 0
618         /* XXX fix this up ? */
619         if (Config->get_link_region_and_track_selection() && !regions.involves (r->get_trackview())) {
620                 remove (&r->get_trackview());
621         }
622 #endif
623 }
624
625
626 void
627 Selection::remove (uint32_t selection_id)
628 {
629         if (time.empty()) {
630                 return;
631         }
632
633         for (list<AudioRange>::iterator i = time.begin(); i != time.end(); ++i) {
634                 if ((*i).id == selection_id) {
635                         time.erase (i);
636
637                         TimeChanged ();
638                         break;
639                 }
640         }
641 }
642
643 void
644 Selection::remove (nframes_t /*start*/, nframes_t /*end*/)
645 {
646 }
647
648 void
649 Selection::remove (boost::shared_ptr<ARDOUR::AutomationList> ac)
650 {
651         AutomationSelection::iterator i;
652         if ((i = find (lines.begin(), lines.end(), ac)) != lines.end()) {
653                 lines.erase (i);
654                 LinesChanged();
655         }
656 }
657
658 void
659 Selection::set (TimeAxisView* track)
660 {
661         clear_tracks ();
662         add (track);
663 }
664
665 void
666 Selection::set (const TrackViewList& track_list)
667 {
668         clear_tracks ();
669         add (track_list);
670 }
671
672 void
673 Selection::set (const MidiNoteSelection& midi_list)
674 {
675         clear_midi_notes ();
676         add (midi_list);
677 }
678
679 void
680 Selection::set (boost::shared_ptr<Playlist> playlist)
681 {
682         clear_playlists ();
683         add (playlist);
684 }
685
686 void
687 Selection::set (const list<boost::shared_ptr<Playlist> >& pllist)
688 {
689         clear_playlists ();
690         add (pllist);
691 }
692
693 void
694 Selection::set (const RegionSelection& rs)
695 {
696         clear_regions();
697         regions = rs;
698         RegionsChanged(); /* EMIT SIGNAL */
699 }
700
701 void
702 Selection::set (MidiRegionView* mrv)
703 {
704         clear_midi_regions ();
705         add (mrv);
706 }
707
708 void
709 Selection::set (RegionView* r, bool also_clear_tracks)
710 {
711         clear_regions ();
712         if (also_clear_tracks) {
713                 clear_tracks ();
714         }
715         add (r);
716 }
717
718 void
719 Selection::set (vector<RegionView*>& v)
720 {
721         clear_regions ();
722         if (Config->get_link_region_and_track_selection()) {
723                 clear_tracks ();
724                 // make sure to deselect any automation selections
725                 clear_points();
726         }
727         add (v);
728 }
729
730 /** Set the start and end time of the time selection, without changing
731  *  the list of tracks it applies to.
732  */
733 long
734 Selection::set (nframes_t start, nframes_t end)
735 {
736         if ((start == 0 && end == 0) || end < start) {
737                 return 0;
738         }
739
740         if (time.empty()) {
741                 time.push_back (AudioRange (start, end, next_time_id++));
742         } else {
743                 /* reuse the first entry, and remove all the rest */
744
745                 while (time.size() > 1) {
746                         time.pop_front();
747                 }
748                 time.front().start = start;
749                 time.front().end = end;
750         }
751
752         time.consolidate ();
753
754         TimeChanged ();
755
756         return time.front().id;
757 }
758
759 void
760 Selection::set (boost::shared_ptr<Evoral::ControlList> ac)
761 {
762         lines.clear();
763         add (ac);
764 }
765
766 bool
767 Selection::selected (Marker* m)
768 {
769         return find (markers.begin(), markers.end(), m) != markers.end();
770 }
771
772 bool
773 Selection::selected (TimeAxisView* tv)
774 {
775         return find (tracks.begin(), tracks.end(), tv) != tracks.end();
776 }
777
778 bool
779 Selection::selected (RegionView* rv)
780 {
781         return find (regions.begin(), regions.end(), rv) != regions.end();
782 }
783
784 bool
785 Selection::empty (bool internal_selection)
786 {
787         bool object_level_empty =  regions.empty () &&
788                 tracks.empty () &&
789                 points.empty () &&
790                 playlists.empty () &&
791                 lines.empty () &&
792                 time.empty () &&
793                 playlists.empty () &&
794                 markers.empty() &&
795                 midi_regions.empty()
796                 ;
797
798         if (!internal_selection) {
799                 return object_level_empty;
800         }
801
802         /* this is intended to really only apply when using a Selection
803            as a cut buffer.
804         */
805
806         return object_level_empty && midi_notes.empty();
807 }
808
809 void
810 Selection::toggle (const vector<AutomationSelectable*>& autos)
811 {
812         for (vector<AutomationSelectable*>::const_iterator x = autos.begin(); x != autos.end(); ++x) {
813                 if ((*x)->get_selected()) {
814                         points.remove (**x);
815                 } else {
816                         points.push_back (**x);
817                 }
818
819                 delete *x;
820         }
821
822         PointsChanged (); /* EMIT SIGNAL */
823 }
824
825 void
826 Selection::toggle (list<Selectable*>& selectables)
827 {
828         RegionView* rv;
829         AutomationSelectable* as;
830         vector<RegionView*> rvs;
831         vector<AutomationSelectable*> autos;
832
833         for (std::list<Selectable*>::iterator i = selectables.begin(); i != selectables.end(); ++i) {
834                 if ((rv = dynamic_cast<RegionView*> (*i)) != 0) {
835                         rvs.push_back (rv);
836                 } else if ((as = dynamic_cast<AutomationSelectable*> (*i)) != 0) {
837                         autos.push_back (as);
838                 } else {
839                         fatal << _("programming error: ")
840                               << X_("unknown selectable type passed to Selection::toggle()")
841                               << endmsg;
842                         /*NOTREACHED*/
843                 }
844         }
845
846         if (!rvs.empty()) {
847                 toggle (rvs);
848         }
849
850         if (!autos.empty()) {
851                 toggle (autos);
852         }
853 }
854
855 void
856 Selection::set (list<Selectable*>& selectables)
857 {
858         clear_regions();
859         clear_points ();
860         add (selectables);
861 }
862
863
864 void
865 Selection::add (list<Selectable*>& selectables)
866 {
867         RegionView* rv;
868         AutomationSelectable* as;
869         vector<RegionView*> rvs;
870         vector<AutomationSelectable*> autos;
871
872         for (std::list<Selectable*>::iterator i = selectables.begin(); i != selectables.end(); ++i) {
873                 if ((rv = dynamic_cast<RegionView*> (*i)) != 0) {
874                         rvs.push_back (rv);
875                 } else if ((as = dynamic_cast<AutomationSelectable*> (*i)) != 0) {
876                         autos.push_back (as);
877                 } else {
878                         fatal << _("programming error: ")
879                               << X_("unknown selectable type passed to Selection::add()")
880                               << endmsg;
881                         /*NOTREACHED*/
882                 }
883         }
884
885         if (!rvs.empty()) {
886                 add (rvs);
887         }
888
889         if (!autos.empty()) {
890                 add (autos);
891         }
892 }
893
894 void
895 Selection::clear_points ()
896 {
897         if (!points.empty()) {
898                 points.clear ();
899                 PointsChanged ();
900         }
901 }
902
903 void
904 Selection::add (vector<AutomationSelectable*>& autos)
905 {
906         for (vector<AutomationSelectable*>::iterator i = autos.begin(); i != autos.end(); ++i) {
907                 points.push_back (**i);
908         }
909
910         PointsChanged ();
911 }
912
913 void
914 Selection::set (Marker* m)
915 {
916         clear_markers ();
917         add (m);
918 }
919
920 void
921 Selection::toggle (Marker* m)
922 {
923         MarkerSelection::iterator i;
924
925         if ((i = find (markers.begin(), markers.end(), m)) == markers.end()) {
926                 add (m);
927         } else {
928                 remove (m);
929         }
930 }
931
932 void
933 Selection::remove (Marker* m)
934 {
935         MarkerSelection::iterator i;
936
937         if ((i = find (markers.begin(), markers.end(), m)) != markers.end()) {
938                 markers.erase (i);
939                 MarkersChanged();
940         }
941 }
942
943 void
944 Selection::add (Marker* m)
945 {
946         if (find (markers.begin(), markers.end(), m) == markers.end()) {
947
948                 /* disambiguate which remove() for the compiler */
949
950                 void (Selection::*pmf)(Marker*) = &Selection::remove;
951
952                 m->GoingAway.connect (*this, boost::bind (pmf, this, m));
953
954                 markers.push_back (m);
955                 MarkersChanged();
956         }
957 }
958
959 void
960 Selection::add (const list<Marker*>& m)
961 {
962         markers.insert (markers.end(), m.begin(), m.end());
963         MarkersChanged ();
964 }
965
966 void
967 MarkerSelection::range (nframes64_t& s, nframes64_t& e)
968 {
969         s = max_frames;
970         e = 0;
971
972         for (MarkerSelection::iterator i = begin(); i != end(); ++i) {
973
974                 if ((*i)->position() < s) {
975                         s = (*i)->position();
976                 }
977
978                 if ((*i)->position() > e) {
979                         e = (*i)->position();
980                 }
981         }
982
983         s = std::min (s, e);
984         e = std::max (s, e);
985 }