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