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