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