Fix up state save/load of location lock/glue settings.
[ardour.git] / libs / ardour / location.cc
1 /*
2     Copyright (C) 2000 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 <set>
22 #include <cstdio> /* for sprintf */
23 #include <unistd.h>
24 #include <cerrno>
25 #include <ctime>
26 #include <list>
27
28 #include "pbd/stl_delete.h"
29 #include "pbd/xml++.h"
30 #include "pbd/enumwriter.h"
31
32 #include "ardour/location.h"
33 #include "ardour/session.h"
34 #include "ardour/audiofilesource.h"
35 #include "ardour/tempo.h"
36
37 #include "i18n.h"
38
39 #define SUFFIX_MAX 32
40
41 using namespace std;
42 using namespace ARDOUR;
43 using namespace PBD;
44
45 Location::Location (Session& s)
46         : SessionHandleRef (s)
47         , _start (0)
48         , _end (0)
49         , _flags (Flags (0))
50         , _locked (false)
51         , _position_lock_style (AudioTime)
52 {
53
54 }
55
56 Location::Location (Session& s, nframes64_t sample_start, nframes64_t sample_end, const std::string &name, Flags bits)
57         : SessionHandleRef (s)
58         , _name (name)
59         , _start (sample_start)
60         , _end (sample_end)
61         , _flags (bits)
62         , _locked (false)
63         , _position_lock_style (AudioTime)
64 {
65         recompute_bbt_from_frames ();
66 }
67
68 Location::Location (const Location& other)
69         : SessionHandleRef (other._session)
70         , StatefulDestructible()
71         , _name (other._name)
72         , _start (other._start)
73         , _bbt_start (other._bbt_start)
74         , _end (other._end)
75         , _bbt_end (other._bbt_end)
76         , _flags (other._flags)
77         , _position_lock_style (other._position_lock_style)
78 {
79         /* copy is not locked even if original was */
80
81         _locked = false;
82 }
83
84 Location::Location (Session& s, const XMLNode& node)
85         : SessionHandleRef (s)
86 {
87         if (set_state (node, Stateful::loading_state_version)) {
88                 throw failed_constructor ();
89         }
90 }
91
92 Location*
93 Location::operator= (const Location& other)
94 {
95         if (this == &other) {
96                 return this;
97         }
98
99         _name = other._name;
100         _start = other._start;
101         _bbt_start = other._bbt_start;
102         _end = other._end;
103         _bbt_end = other._bbt_end;
104         _flags = other._flags;
105         _position_lock_style = other._position_lock_style;
106
107         /* copy is not locked even if original was */
108
109         _locked = false;
110
111         /* "changed" not emitted on purpose */
112
113         return this;
114 }
115
116 /** Set start position.
117  *  @param s New start.
118  *  @param force true to force setting, even if the given new start is after the current end.
119  *  @param allow_bbt_recompute True to recompute BBT start time from the new given start time.
120  */
121 int
122 Location::set_start (nframes64_t s, bool force, bool allow_bbt_recompute)
123 {
124         if (_locked) {
125                 return -1;
126         }
127
128         if (!force) {
129                 if (((is_auto_punch() || is_auto_loop()) && s >= _end) || (!is_mark() && s > _end)) {
130                         return -1;
131                 }
132         }
133
134         if (is_mark()) {
135                 if (_start != s) {
136                         _start = s;
137                         _end = s;
138                         if (allow_bbt_recompute) {
139                                 recompute_bbt_from_frames ();
140                         }
141                         start_changed (this); /* EMIT SIGNAL */
142                         end_changed (this); /* EMIT SIGNAL */
143                 }
144                 return 0;
145         }
146         
147         if (s != _start) {
148                 _start = s;
149                 if (allow_bbt_recompute) {
150                         recompute_bbt_from_frames ();
151                 }
152                 start_changed (this); /* EMIT SIGNAL */
153                 if (is_session_range ()) {
154                         Session::StartTimeChanged (); /* EMIT SIGNAL */
155                         AudioFileSource::set_header_position_offset (s);
156                 }
157         }
158
159         return 0;
160 }
161
162 /** Set end position.
163  *  @param s New end.
164  *  @param force true to force setting, even if the given new start is after the current end.
165  *  @param allow_bbt_recompute True to recompute BBT end time from the new given end time.
166  */
167 int
168 Location::set_end (nframes64_t e, bool force, bool allow_bbt_recompute)
169 {
170         if (_locked) {
171                 return -1;
172         }
173
174         if (!force) {
175                 if (((is_auto_punch() || is_auto_loop()) && e <= _start) || e < _start) {
176                         return -1;
177                 }
178         }
179         
180         if (is_mark()) {
181                 if (_start != e) {
182                         _start = e;
183                         _end = e;
184                         if (allow_bbt_recompute) {
185                                 recompute_bbt_from_frames ();
186                         }
187                         start_changed (this); /* EMIT SIGNAL */
188                         end_changed (this); /* EMIT SIGNAL */
189                 }
190                 return 0;
191         }
192
193         if (e != _end) {
194                 _end = e;
195                 if (allow_bbt_recompute) {
196                         recompute_bbt_from_frames ();
197                 }
198                 end_changed(this); /* EMIT SIGNAL */
199
200                 if (is_session_range()) {
201                         Session::EndTimeChanged (); /* EMIT SIGNAL */
202                 }
203         }
204
205         return 0;
206 }
207
208 int
209 Location::set (nframes64_t start, nframes64_t end, bool allow_bbt_recompute)
210 {
211         /* check validity */
212         if (((is_auto_punch() || is_auto_loop()) && start >= end) || (!is_mark() && start > end)) {
213                 return -1;
214         }
215
216         /* now we know these values are ok, so force-set them */
217         int const s = set_start (start, true, allow_bbt_recompute);
218         int const e = set_end (end, true, allow_bbt_recompute);
219
220         return (s == 0 && e == 0) ? 0 : -1;
221 }
222
223 int
224 Location::move_to (nframes64_t pos)
225 {
226         if (_locked) {
227                 return -1;
228         }
229
230         if (_start != pos) {
231                 _start = pos;
232                 _end = _start + length();
233                 recompute_bbt_from_frames ();
234
235                 changed (this); /* EMIT SIGNAL */
236         }
237
238         return 0;
239 }
240
241 void
242 Location::set_hidden (bool yn, void *src)
243 {
244         if (set_flag_internal (yn, IsHidden)) {
245                  FlagsChanged (this, src); /* EMIT SIGNAL */
246         }
247 }
248
249 void
250 Location::set_cd (bool yn, void *src)
251 {
252         // XXX this really needs to be session start
253         // but its not available here - leave to GUI
254
255         if (_start == 0) {
256                 error << _("You cannot put a CD marker at this position") << endmsg;
257                 return;
258         }
259
260         if (set_flag_internal (yn, IsCDMarker)) {
261                  FlagsChanged (this, src); /* EMIT SIGNAL */
262         }
263 }
264
265 void
266 Location::set_is_range_marker (bool yn, void *src)
267 {
268        if (set_flag_internal (yn, IsRangeMarker)) {
269                 FlagsChanged (this, src); /* EMIT SIGNAL */
270        }
271 }
272
273 void
274 Location::set_auto_punch (bool yn, void *src)
275 {
276         if (is_mark() || _start == _end) {
277                 return;
278         }
279
280         if (set_flag_internal (yn, IsAutoPunch)) {
281                  FlagsChanged (this, src); /* EMIT SIGNAL */
282         }
283 }
284
285 void
286 Location::set_auto_loop (bool yn, void *src)
287 {
288         if (is_mark() || _start == _end) {
289                 return;
290         }
291
292         if (set_flag_internal (yn, IsAutoLoop)) {
293                  FlagsChanged (this, src); /* EMIT SIGNAL */
294         }
295 }
296
297 bool
298 Location::set_flag_internal (bool yn, Flags flag)
299 {
300         if (yn) {
301                 if (!(_flags & flag)) {
302                         _flags = Flags (_flags | flag);
303                         return true;
304                 }
305         } else {
306                 if (_flags & flag) {
307                         _flags = Flags (_flags & ~flag);
308                         return true;
309                 }
310         }
311         return false;
312 }
313
314 void
315 Location::set_mark (bool yn)
316 {
317         /* This function is private, and so does not emit signals */
318
319         if (_start != _end) {
320                 return;
321         }
322
323         set_flag_internal (yn, IsMark);
324 }
325
326
327 XMLNode&
328 Location::cd_info_node(const string & name, const string & value)
329 {
330         XMLNode* root = new XMLNode("CD-Info");
331
332         root->add_property("name", name);
333         root->add_property("value", value);
334
335         return *root;
336 }
337
338
339 XMLNode&
340 Location::get_state ()
341 {
342         XMLNode *node = new XMLNode ("Location");
343         char buf[64];
344
345         typedef map<string, string>::const_iterator CI;
346
347         for(CI m = cd_info.begin(); m != cd_info.end(); ++m){
348                 node->add_child_nocopy(cd_info_node(m->first, m->second));
349         }
350
351         id().print (buf, sizeof (buf));
352         node->add_property("id", buf);
353         node->add_property ("name", name());
354         snprintf (buf, sizeof (buf), "%" PRId64, start());
355         node->add_property ("start", buf);
356         snprintf (buf, sizeof (buf), "%" PRId64, end());
357         node->add_property ("end", buf);
358         node->add_property ("flags", enum_2_string (_flags));
359         node->add_property ("locked", (_locked ? "yes" : "no"));
360         node->add_property ("position-lock-style", enum_2_string (_position_lock_style));
361
362         return *node;
363 }
364
365 int
366 Location::set_state (const XMLNode& node, int /*version*/)
367 {
368         const XMLProperty *prop;
369
370         XMLNodeList cd_list = node.children();
371         XMLNodeConstIterator cd_iter;
372         XMLNode *cd_node;
373
374         string cd_name;
375         string cd_value;
376
377         if (node.name() != "Location") {
378                 error << _("incorrect XML node passed to Location::set_state") << endmsg;
379                 return -1;
380         }
381
382         if ((prop = node.property ("id")) == 0) {
383                 warning << _("XML node for Location has no ID information") << endmsg;
384         } else {
385                 _id = prop->value ();
386         }
387
388         if ((prop = node.property ("name")) == 0) {
389                 error << _("XML node for Location has no name information") << endmsg;
390                 return -1;
391         }
392
393         set_name (prop->value());
394
395         if ((prop = node.property ("start")) == 0) {
396                 error << _("XML node for Location has no start information") << endmsg;
397                 return -1;
398         }
399
400                 /* can't use set_start() here, because _end
401                    may make the value of _start illegal.
402                 */
403
404         sscanf (prop->value().c_str(), "%" PRId64, &_start);
405
406         if ((prop = node.property ("end")) == 0) {
407                   error << _("XML node for Location has no end information") << endmsg;
408                   return -1;
409         }
410
411         sscanf (prop->value().c_str(), "%" PRId64, &_end);
412
413         if ((prop = node.property ("flags")) == 0) {
414                   error << _("XML node for Location has no flags information") << endmsg;
415                   return -1;
416         }
417
418         _flags = Flags (string_2_enum (prop->value(), _flags));
419
420         if ((prop = node.property ("locked")) != 0) {
421                 _locked = string_is_affirmative (prop->value());
422         } else {
423                 _locked = false;
424         }
425
426         for (cd_iter = cd_list.begin(); cd_iter != cd_list.end(); ++cd_iter) {
427
428                   cd_node = *cd_iter;
429
430                   if (cd_node->name() != "CD-Info") {
431                           continue;
432                   }
433
434                   if ((prop = cd_node->property ("name")) != 0) {
435                           cd_name = prop->value();
436                   } else {
437                           throw failed_constructor ();
438                   }
439
440                   if ((prop = cd_node->property ("value")) != 0) {
441                           cd_value = prop->value();
442                   } else {
443                           throw failed_constructor ();
444                   }
445
446
447                   cd_info[cd_name] = cd_value;
448         }
449
450         if ((prop = node.property ("position-lock-style")) != 0) {
451                 _position_lock_style = PositionLockStyle (string_2_enum (prop->value(), _position_lock_style));
452         }
453
454         recompute_bbt_from_frames ();
455
456         changed (this); /* EMIT SIGNAL */
457
458         return 0;
459 }
460
461 void
462 Location::set_position_lock_style (PositionLockStyle ps)
463 {
464         if (_position_lock_style == ps) {
465                 return;
466         }
467
468         _position_lock_style = ps;
469
470         recompute_bbt_from_frames ();
471
472         PositionLockStyleChanged (this); /* EMIT SIGNAL */
473 }
474
475 void
476 Location::recompute_bbt_from_frames ()
477 {
478         if (_position_lock_style != MusicTime) {
479                 return;
480         }
481         
482         _session.tempo_map().bbt_time (_start, _bbt_start);
483         _session.tempo_map().bbt_time (_end, _bbt_end);
484 }
485
486 void
487 Location::recompute_frames_from_bbt ()
488 {
489         if (_position_lock_style != MusicTime) {
490                 return;
491         }
492
493         TempoMap& map (_session.tempo_map());
494         set (map.frame_time (_bbt_start), map.frame_time (_bbt_end), false);
495 }
496
497 void
498 Location::lock ()
499 {
500         _locked = true;
501         LockChanged (this);
502 }
503
504 void
505 Location::unlock ()
506 {
507         _locked = false;
508         LockChanged (this);
509 }
510
511 /*---------------------------------------------------------------------- */
512
513 Locations::Locations (Session& s)
514         : SessionHandleRef (s)
515 {
516         current_location = 0;
517 }
518
519 Locations::~Locations ()
520 {
521         for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
522                 LocationList::iterator tmp = i;
523                 ++tmp;
524                 delete *i;
525                 i = tmp;
526         }
527 }
528
529 int
530 Locations::set_current (Location *loc, bool want_lock)
531 {
532         int ret;
533
534         if (want_lock) {
535                 Glib::Mutex::Lock lm (lock);
536                 ret = set_current_unlocked (loc);
537         } else {
538                 ret = set_current_unlocked (loc);
539         }
540
541         if (ret == 0) {
542                  current_changed (current_location); /* EMIT SIGNAL */
543         }
544         return ret;
545 }
546
547 int
548 Locations::next_available_name(string& result,string base)
549 {
550         LocationList::iterator i;
551         Location* location;
552         string temp;
553         string::size_type l;
554         int suffix;
555         char buf[32];
556         bool available[SUFFIX_MAX+1];
557
558         result = base;
559         for (int k=1; k<SUFFIX_MAX; k++) {
560                 available[k] = true;
561         }
562         l = base.length();
563         for (i = locations.begin(); i != locations.end(); ++i) {
564                 location =* i;
565                 temp = location->name();
566                 if (l && !temp.find(base,0)) {
567                         suffix = atoi(temp.substr(l,3).c_str());
568                         if (suffix) available[suffix] = false;
569                 }
570         }
571         for (int k=1; k<=SUFFIX_MAX; k++) {
572                 if (available[k]) {
573                         snprintf (buf, 31, "%d", k);
574                         result += buf;
575                         return 1;
576                 }
577         }
578         return 0;
579 }
580
581 int
582 Locations::set_current_unlocked (Location *loc)
583 {
584         if (find (locations.begin(), locations.end(), loc) == locations.end()) {
585                 error << _("Locations: attempt to use unknown location as selected location") << endmsg;
586                 return -1;
587         }
588
589         current_location = loc;
590         return 0;
591 }
592
593 void
594 Locations::clear ()
595 {
596         {
597                 Glib::Mutex::Lock lm (lock);
598
599                 for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
600
601                         LocationList::iterator tmp = i;
602                         ++tmp;
603
604                         if (!(*i)->is_session_range()) {
605                                 locations.erase (i);
606                         }
607
608                         i = tmp;
609                 }
610
611                 current_location = 0;
612         }
613
614         changed (OTHER); /* EMIT SIGNAL */
615         current_changed (0); /* EMIT SIGNAL */
616 }
617
618 void
619 Locations::clear_markers ()
620 {
621         {
622                 Glib::Mutex::Lock lm (lock);
623                 LocationList::iterator tmp;
624
625                 for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
626                         tmp = i;
627                         ++tmp;
628
629                         if ((*i)->is_mark() && !(*i)->is_session_range()) {
630                                 locations.erase (i);
631                         }
632
633                         i = tmp;
634                 }
635         }
636
637         changed (OTHER); /* EMIT SIGNAL */
638 }
639
640 void
641 Locations::clear_ranges ()
642 {
643         {
644                 Glib::Mutex::Lock lm (lock);
645                 LocationList::iterator tmp;
646
647                 for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
648
649                         tmp = i;
650                         ++tmp;
651
652                         if (!(*i)->is_mark()) {
653                                 locations.erase (i);
654
655                         }
656
657                         i = tmp;
658                 }
659
660                 current_location = 0;
661         }
662
663         changed (OTHER); /* EMIT SIGNAL */
664         current_changed (0); /* EMIT SIGNAL */
665 }
666
667 void
668 Locations::add (Location *loc, bool make_current)
669 {
670         assert (loc);
671         
672         {
673                 Glib::Mutex::Lock lm (lock);
674                 locations.push_back (loc);
675
676                 if (make_current) {
677                         current_location = loc;
678                 }
679         }
680
681         added (loc); /* EMIT SIGNAL */
682
683         if (make_current) {
684                  current_changed (current_location); /* EMIT SIGNAL */
685         }
686 }
687
688 void
689 Locations::remove (Location *loc)
690 {
691         bool was_removed = false;
692         bool was_current = false;
693         LocationList::iterator i;
694
695         if (loc->is_session_range()) {
696                 return;
697         }
698
699         {
700                 Glib::Mutex::Lock lm (lock);
701
702                 for (i = locations.begin(); i != locations.end(); ++i) {
703                         if ((*i) == loc) {
704                                 locations.erase (i);
705                                 was_removed = true;
706                                 if (current_location == loc) {
707                                         current_location = 0;
708                                         was_current = true;
709                                 }
710                                 break;
711                         }
712                 }
713         }
714
715         if (was_removed) {
716
717                 removed (loc); /* EMIT SIGNAL */
718
719                 if (was_current) {
720                          current_changed (0); /* EMIT SIGNAL */
721                 }
722
723                 changed (REMOVAL); /* EMIT_SIGNAL */
724         }
725 }
726
727 void
728 Locations::location_changed (Location* /*loc*/)
729 {
730         changed (OTHER); /* EMIT SIGNAL */
731 }
732
733 XMLNode&
734 Locations::get_state ()
735 {
736         XMLNode *node = new XMLNode ("Locations");
737         LocationList::iterator iter;
738         Glib::Mutex::Lock lm (lock);
739
740         for (iter  = locations.begin(); iter != locations.end(); ++iter) {
741                 node->add_child_nocopy ((*iter)->get_state ());
742         }
743
744         return *node;
745 }
746
747 int
748 Locations::set_state (const XMLNode& node, int version)
749 {
750         if (node.name() != "Locations") {
751                 error << _("incorrect XML mode passed to Locations::set_state") << endmsg;
752                 return -1;
753         }
754
755         XMLNodeList nlist = node.children();
756
757         locations.clear ();
758         current_location = 0;
759
760         Location* session_range_location = 0;
761         if (version < 3000) {
762                 session_range_location = new Location (_session, 0, 0, _("session"), Location::IsSessionRange);
763                 locations.push_back (session_range_location);
764         }
765
766         {
767                 Glib::Mutex::Lock lm (lock);
768
769                 XMLNodeConstIterator niter;
770                 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
771
772                         try {
773
774                                 Location *loc = new Location (_session, **niter);
775
776                                 bool add = true;
777
778                                 if (version < 3000) {
779                                         /* look for old-style IsStart / IsEnd properties in this location;
780                                            if they are present, update the session_range_location accordingly
781                                         */
782                                         XMLProperty const * prop = (*niter)->property ("flags");
783                                         if (prop) {
784                                                 string v = prop->value ();
785                                                 while (1) {
786                                                         string::size_type const c = v.find_first_of (',');
787                                                         string const s = v.substr (0, c);
788                                                         if (s == X_("IsStart")) {
789                                                                 session_range_location->set_start (loc->start());
790                                                                 add = false;
791                                                         } else if (s == X_("IsEnd")) {
792                                                                 session_range_location->set_end (loc->start());
793                                                                 add = false;
794                                                         }
795
796                                                         if (c == string::npos) {
797                                                                 break;
798                                                         }
799
800                                                         v = v.substr (c + 1);
801                                                 }
802                                         }
803                                 }
804
805                                 if (add) {
806                                         locations.push_back (loc);
807                                 }
808                         }
809
810                         catch (failed_constructor& err) {
811                                 error << _("could not load location from session file - ignored") << endmsg;
812                         }
813                 }
814
815                 if (locations.size()) {
816                         current_location = locations.front();
817                 } else {
818                         current_location = 0;
819                 }
820         }
821
822         changed (OTHER); /* EMIT SIGNAL */
823
824         return 0;
825 }
826
827 struct LocationStartEarlierComparison
828 {
829     bool operator() (Location *a, Location *b) {
830         return a->start() < b->start();
831     }
832 };
833
834 struct LocationStartLaterComparison
835 {
836     bool operator() (Location *a, Location *b) {
837         return a->start() > b->start();
838     }
839 };
840
841 Location *
842 Locations::first_location_before (nframes64_t frame, bool include_special_ranges)
843 {
844         LocationList locs;
845
846         {
847                 Glib::Mutex::Lock lm (lock);
848                 locs = locations;
849         }
850
851         LocationStartLaterComparison cmp;
852         locs.sort (cmp);
853
854         /* locs is now sorted latest..earliest */
855
856         for (LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
857                 if (!include_special_ranges && ((*i)->is_auto_loop() || (*i)->is_auto_punch())) {
858                         continue;
859                 }
860                 if (!(*i)->is_hidden() && (*i)->start() < frame) {
861                         return (*i);
862                 }
863         }
864
865         return 0;
866 }
867
868 Location *
869 Locations::first_location_after (nframes64_t frame, bool include_special_ranges)
870 {
871         LocationList locs;
872
873         {
874                 Glib::Mutex::Lock lm (lock);
875                 locs = locations;
876         }
877
878         LocationStartEarlierComparison cmp;
879         locs.sort (cmp);
880
881         /* locs is now sorted earliest..latest */
882
883         for (LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
884                 if (!include_special_ranges && ((*i)->is_auto_loop() || (*i)->is_auto_punch())) {
885                         continue;
886                 }
887                 if (!(*i)->is_hidden() && (*i)->start() > frame) {
888                         return (*i);
889                 }
890         }
891
892         return 0;
893 }
894
895 /** Look for the `marks' (either locations which are marks, or start/end points of range markers) either
896  *  side of a frame.  Note that if frame is exactly on a `mark', that mark will not be considered for returning
897  *  as before/after.
898  *  @param frame Frame to look for.
899  *  @param before Filled in with the position of the last `mark' before `frame' (or max_frames if none exists)
900  *  @param after Filled in with the position of the next `mark' after `frame' (or max_frames if none exists)
901  */
902 void
903 Locations::marks_either_side (nframes64_t const frame, nframes64_t& before, nframes64_t& after) const
904 {
905         before = after = max_frames;
906         
907         LocationList locs;
908
909         {
910                 Glib::Mutex::Lock lm (lock);
911                 locs = locations;
912         }
913
914         /* Get a list of positions; don't store any that are exactly on our requested position */
915         
916         std::list<nframes64_t> positions;
917
918         for (LocationList::const_iterator i = locs.begin(); i != locs.end(); ++i) {
919                 if (((*i)->is_auto_loop() || (*i)->is_auto_punch())) {
920                         continue;
921                 }
922
923                 if (!(*i)->is_hidden()) {
924                         if ((*i)->is_mark ()) {
925                                 if ((*i)->start() != frame) {
926                                         positions.push_back ((*i)->start ());
927                                 }
928                         } else {
929                                 if ((*i)->start() != frame) {
930                                         positions.push_back ((*i)->start ());
931                                 }
932                                 if ((*i)->end() != frame) {
933                                         positions.push_back ((*i)->end ());
934                                 }
935                         }
936                 }
937         }
938
939         if (positions.empty ()) {
940                 return;
941         }
942
943         positions.sort ();
944
945         std::list<nframes64_t>::iterator i = positions.begin ();
946         while (i != positions.end () && *i < frame) {
947                 ++i;
948         }
949
950         if (i == positions.end ()) {
951                 /* run out of marks */
952                 before = positions.back ();
953                 return;
954         }
955
956         after = *i;
957
958         if (i == positions.begin ()) {
959                 /* none before */
960                 return;
961         }
962         
963         --i;
964         before = *i;
965 }
966
967 Location*
968 Locations::session_range_location () const
969 {
970         for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
971                 if ((*i)->is_session_range()) {
972                         return const_cast<Location*> (*i);
973                 }
974         }
975         return 0;
976 }
977
978 Location*
979 Locations::auto_loop_location () const
980 {
981         for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
982                 if ((*i)->is_auto_loop()) {
983                         return const_cast<Location*> (*i);
984                 }
985         }
986         return 0;
987 }
988
989 Location*
990 Locations::auto_punch_location () const
991 {
992         for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
993                 if ((*i)->is_auto_punch()) {
994                         return const_cast<Location*> (*i);
995                 }
996         }
997        return 0;
998 }
999
1000 uint32_t
1001 Locations::num_range_markers () const
1002 {
1003         uint32_t cnt = 0;
1004         Glib::Mutex::Lock lm (lock);
1005         for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1006                 if ((*i)->is_range_marker()) {
1007                         ++cnt;
1008                 }
1009         }
1010         return cnt;
1011 }
1012
1013 Location *
1014 Locations::get_location_by_id(PBD::ID id)
1015 {
1016     LocationList::iterator it;
1017     for (it  = locations.begin(); it != locations.end(); ++it)
1018         if (id == (*it)->id())
1019             return *it;
1020
1021     return 0;
1022 }
1023
1024 void
1025 Locations::find_all_between (nframes64_t start, nframes64_t end, LocationList& ll, Location::Flags flags)
1026 {
1027         Glib::Mutex::Lock lm (lock);
1028
1029         for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1030                 if ((flags == 0 || (*i)->matches (flags)) &&
1031                     ((*i)->start() >= start && (*i)->end() < end)) {
1032                         ll.push_back (*i);
1033                 }
1034         }
1035 }