Upgrade to waf 1.6.7 and autowaf r52.
[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 ((prop = node.property ("id")) == 0) {
423                 warning << _("XML node for Location has no ID information") << endmsg;
424         } else {
425                 _id = prop->value ();
426         }
427
428         if ((prop = node.property ("name")) == 0) {
429                 error << _("XML node for Location has no name information") << endmsg;
430                 return -1;
431         }
432
433         set_name (prop->value());
434
435         if ((prop = node.property ("start")) == 0) {
436                 error << _("XML node for Location has no start information") << endmsg;
437                 return -1;
438         }
439
440                 /* can't use set_start() here, because _end
441                    may make the value of _start illegal.
442                 */
443
444         sscanf (prop->value().c_str(), "%" PRId64, &_start);
445
446         if ((prop = node.property ("end")) == 0) {
447                   error << _("XML node for Location has no end information") << endmsg;
448                   return -1;
449         }
450
451         sscanf (prop->value().c_str(), "%" PRId64, &_end);
452
453         if ((prop = node.property ("flags")) == 0) {
454                   error << _("XML node for Location has no flags information") << endmsg;
455                   return -1;
456         }
457
458         _flags = Flags (string_2_enum (prop->value(), _flags));
459
460         if ((prop = node.property ("locked")) != 0) {
461                 _locked = string_is_affirmative (prop->value());
462         } else {
463                 _locked = false;
464         }
465
466         for (cd_iter = cd_list.begin(); cd_iter != cd_list.end(); ++cd_iter) {
467
468                   cd_node = *cd_iter;
469
470                   if (cd_node->name() != "CD-Info") {
471                           continue;
472                   }
473
474                   if ((prop = cd_node->property ("name")) != 0) {
475                           cd_name = prop->value();
476                   } else {
477                           throw failed_constructor ();
478                   }
479
480                   if ((prop = cd_node->property ("value")) != 0) {
481                           cd_value = prop->value();
482                   } else {
483                           throw failed_constructor ();
484                   }
485
486
487                   cd_info[cd_name] = cd_value;
488         }
489
490         if ((prop = node.property ("position-lock-style")) != 0) {
491                 _position_lock_style = PositionLockStyle (string_2_enum (prop->value(), _position_lock_style));
492         }
493
494         recompute_bbt_from_frames ();
495
496         changed (this); /* EMIT SIGNAL */
497
498         assert (_start >= 0);
499         assert (_end >= 0);
500
501         return 0;
502 }
503
504 void
505 Location::set_position_lock_style (PositionLockStyle ps)
506 {
507         if (_position_lock_style == ps) {
508                 return;
509         }
510
511         _position_lock_style = ps;
512
513         recompute_bbt_from_frames ();
514
515         PositionLockStyleChanged (this); /* EMIT SIGNAL */
516 }
517
518 void
519 Location::recompute_bbt_from_frames ()
520 {
521         if (_position_lock_style != MusicTime) {
522                 return;
523         }
524
525         _session.tempo_map().bbt_time (_start, _bbt_start);
526         _session.tempo_map().bbt_time (_end, _bbt_end);
527 }
528
529 void
530 Location::recompute_frames_from_bbt ()
531 {
532         if (_position_lock_style != MusicTime) {
533                 return;
534         }
535
536         TempoMap& map (_session.tempo_map());
537         set (map.frame_time (_bbt_start), map.frame_time (_bbt_end), false);
538 }
539
540 void
541 Location::lock ()
542 {
543         _locked = true;
544         LockChanged (this);
545 }
546
547 void
548 Location::unlock ()
549 {
550         _locked = false;
551         LockChanged (this);
552 }
553
554 /*---------------------------------------------------------------------- */
555
556 Locations::Locations (Session& s)
557         : SessionHandleRef (s)
558 {
559         current_location = 0;
560 }
561
562 Locations::~Locations ()
563 {
564         for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
565                 LocationList::iterator tmp = i;
566                 ++tmp;
567                 delete *i;
568                 i = tmp;
569         }
570 }
571
572 int
573 Locations::set_current (Location *loc, bool want_lock)
574 {
575         int ret;
576
577         if (want_lock) {
578                 Glib::Mutex::Lock lm (lock);
579                 ret = set_current_unlocked (loc);
580         } else {
581                 ret = set_current_unlocked (loc);
582         }
583
584         if (ret == 0) {
585                  current_changed (current_location); /* EMIT SIGNAL */
586         }
587         return ret;
588 }
589
590 int
591 Locations::next_available_name(string& result,string base)
592 {
593         LocationList::iterator i;
594         Location* location;
595         string temp;
596         string::size_type l;
597         int suffix;
598         char buf[32];
599         bool available[SUFFIX_MAX+1];
600
601         result = base;
602         for (int k=1; k<SUFFIX_MAX; k++) {
603                 available[k] = true;
604         }
605         l = base.length();
606         for (i = locations.begin(); i != locations.end(); ++i) {
607                 location =* i;
608                 temp = location->name();
609                 if (l && !temp.find(base,0)) {
610                         suffix = atoi(temp.substr(l,3).c_str());
611                         if (suffix) available[suffix] = false;
612                 }
613         }
614         for (int k=1; k<=SUFFIX_MAX; k++) {
615                 if (available[k]) {
616                         snprintf (buf, 31, "%d", k);
617                         result += buf;
618                         return 1;
619                 }
620         }
621         return 0;
622 }
623
624 int
625 Locations::set_current_unlocked (Location *loc)
626 {
627         if (find (locations.begin(), locations.end(), loc) == locations.end()) {
628                 error << _("Locations: attempt to use unknown location as selected location") << endmsg;
629                 return -1;
630         }
631
632         current_location = loc;
633         return 0;
634 }
635
636 void
637 Locations::clear ()
638 {
639         {
640                 Glib::Mutex::Lock lm (lock);
641
642                 for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
643
644                         LocationList::iterator tmp = i;
645                         ++tmp;
646
647                         if (!(*i)->is_session_range()) {
648                                 locations.erase (i);
649                         }
650
651                         i = tmp;
652                 }
653
654                 current_location = 0;
655         }
656
657         changed (OTHER); /* EMIT SIGNAL */
658         current_changed (0); /* EMIT SIGNAL */
659 }
660
661 void
662 Locations::clear_markers ()
663 {
664         {
665                 Glib::Mutex::Lock lm (lock);
666                 LocationList::iterator tmp;
667
668                 for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
669                         tmp = i;
670                         ++tmp;
671
672                         if ((*i)->is_mark() && !(*i)->is_session_range()) {
673                                 locations.erase (i);
674                         }
675
676                         i = tmp;
677                 }
678         }
679
680         changed (OTHER); /* EMIT SIGNAL */
681 }
682
683 void
684 Locations::clear_ranges ()
685 {
686         {
687                 Glib::Mutex::Lock lm (lock);
688                 LocationList::iterator tmp;
689
690                 for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
691
692                         tmp = i;
693                         ++tmp;
694
695                         if (!(*i)->is_mark()) {
696                                 locations.erase (i);
697
698                         }
699
700                         i = tmp;
701                 }
702
703                 current_location = 0;
704         }
705
706         changed (OTHER); /* EMIT SIGNAL */
707         current_changed (0); /* EMIT SIGNAL */
708 }
709
710 void
711 Locations::add (Location *loc, bool make_current)
712 {
713         assert (loc);
714
715         {
716                 Glib::Mutex::Lock lm (lock);
717                 locations.push_back (loc);
718
719                 if (make_current) {
720                         current_location = loc;
721                 }
722         }
723
724         added (loc); /* EMIT SIGNAL */
725
726         if (make_current) {
727                  current_changed (current_location); /* EMIT SIGNAL */
728         }
729
730         if (loc->is_session_range()) {
731                 Session::StartTimeChanged (0);
732                 Session::EndTimeChanged (1);
733         }
734 }
735
736 void
737 Locations::remove (Location *loc)
738 {
739         bool was_removed = false;
740         bool was_current = false;
741         LocationList::iterator i;
742
743         if (loc->is_session_range()) {
744                 return;
745         }
746
747         {
748                 Glib::Mutex::Lock lm (lock);
749
750                 for (i = locations.begin(); i != locations.end(); ++i) {
751                         if ((*i) == loc) {
752                                 locations.erase (i);
753                                 was_removed = true;
754                                 if (current_location == loc) {
755                                         current_location = 0;
756                                         was_current = true;
757                                 }
758                                 break;
759                         }
760                 }
761         }
762
763         if (was_removed) {
764
765                 removed (loc); /* EMIT SIGNAL */
766
767                 if (was_current) {
768                          current_changed (0); /* EMIT SIGNAL */
769                 }
770
771                 changed (REMOVAL); /* EMIT_SIGNAL */
772         }
773 }
774
775 void
776 Locations::location_changed (Location* /*loc*/)
777 {
778         changed (OTHER); /* EMIT SIGNAL */
779 }
780
781 XMLNode&
782 Locations::get_state ()
783 {
784         XMLNode *node = new XMLNode ("Locations");
785         LocationList::iterator iter;
786         Glib::Mutex::Lock lm (lock);
787
788         for (iter = locations.begin(); iter != locations.end(); ++iter) {
789                 node->add_child_nocopy ((*iter)->get_state ());
790         }
791
792         return *node;
793 }
794
795 int
796 Locations::set_state (const XMLNode& node, int version)
797 {
798         if (node.name() != "Locations") {
799                 error << _("incorrect XML mode passed to Locations::set_state") << endmsg;
800                 return -1;
801         }
802
803         XMLNodeList nlist = node.children();
804
805         /* build up a new locations list in here */
806         LocationList new_locations;
807
808         current_location = 0;
809
810         Location* session_range_location = 0;
811         if (version < 3000) {
812                 session_range_location = new Location (_session, 0, 0, _("session"), Location::IsSessionRange);
813                 new_locations.push_back (session_range_location);
814         }
815
816         {
817                 Glib::Mutex::Lock lm (lock);
818
819                 XMLNodeConstIterator niter;
820                 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
821
822                         try {
823
824                                 XMLProperty const * prop_id = (*niter)->property ("id");
825                                 assert (prop_id);
826                                 PBD::ID id (prop_id->value ());
827
828                                 LocationList::const_iterator i = locations.begin();
829                                 while (i != locations.end () && (*i)->id() != id) {
830                                         ++i;
831                                 }
832
833                                 Location* loc;
834                                 if (i != locations.end()) {
835                                         /* we can re-use an old Location object */
836                                         loc = *i;
837                                         loc->set_state (**niter, version);
838                                 } else {
839                                         loc = new Location (_session, **niter);
840                                 }
841
842                                 bool add = true;
843
844                                 if (version < 3000) {
845                                         /* look for old-style IsStart / IsEnd properties in this location;
846                                            if they are present, update the session_range_location accordingly
847                                         */
848                                         XMLProperty const * prop = (*niter)->property ("flags");
849                                         if (prop) {
850                                                 string v = prop->value ();
851                                                 while (1) {
852                                                         string::size_type const c = v.find_first_of (',');
853                                                         string const s = v.substr (0, c);
854                                                         if (s == X_("IsStart")) {
855                                                                 session_range_location->set_start (loc->start(), true);
856                                                                 add = false;
857                                                         } else if (s == X_("IsEnd")) {
858                                                                 session_range_location->set_end (loc->start(), true);
859                                                                 add = false;
860                                                         }
861
862                                                         if (c == string::npos) {
863                                                                 break;
864                                                         }
865
866                                                         v = v.substr (c + 1);
867                                                 }
868                                         }
869                                 }
870
871                                 if (add) {
872                                         new_locations.push_back (loc);
873                                 }
874                         }
875
876                         catch (failed_constructor& err) {
877                                 error << _("could not load location from session file - ignored") << endmsg;
878                         }
879                 }
880
881                 locations = new_locations;
882
883                 if (locations.size()) {
884                         current_location = locations.front();
885                 } else {
886                         current_location = 0;
887                 }
888         }
889
890         changed (OTHER); /* EMIT SIGNAL */
891
892         return 0;
893 }
894
895 struct LocationStartEarlierComparison
896 {
897     bool operator() (Location *a, Location *b) {
898         return a->start() < b->start();
899     }
900 };
901
902 struct LocationStartLaterComparison
903 {
904     bool operator() (Location *a, Location *b) {
905         return a->start() > b->start();
906     }
907 };
908
909 Location *
910 Locations::first_location_before (framepos_t frame, bool include_special_ranges)
911 {
912         LocationList locs;
913
914         {
915                 Glib::Mutex::Lock lm (lock);
916                 locs = locations;
917         }
918
919         LocationStartLaterComparison cmp;
920         locs.sort (cmp);
921
922         /* locs is now sorted latest..earliest */
923
924         for (LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
925                 if (!include_special_ranges && ((*i)->is_auto_loop() || (*i)->is_auto_punch())) {
926                         continue;
927                 }
928                 if (!(*i)->is_hidden() && (*i)->start() < frame) {
929                         return (*i);
930                 }
931         }
932
933         return 0;
934 }
935
936 Location *
937 Locations::first_location_after (framepos_t frame, bool include_special_ranges)
938 {
939         LocationList locs;
940
941         {
942                 Glib::Mutex::Lock lm (lock);
943                 locs = locations;
944         }
945
946         LocationStartEarlierComparison cmp;
947         locs.sort (cmp);
948
949         /* locs is now sorted earliest..latest */
950
951         for (LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
952                 if (!include_special_ranges && ((*i)->is_auto_loop() || (*i)->is_auto_punch())) {
953                         continue;
954                 }
955                 if (!(*i)->is_hidden() && (*i)->start() > frame) {
956                         return (*i);
957                 }
958         }
959
960         return 0;
961 }
962
963 /** Look for the `marks' (either locations which are marks, or start/end points of range markers) either
964  *  side of a frame.  Note that if frame is exactly on a `mark', that mark will not be considered for returning
965  *  as before/after.
966  *  @param frame Frame to look for.
967  *  @param before Filled in with the position of the last `mark' before `frame' (or max_framepos if none exists)
968  *  @param after Filled in with the position of the next `mark' after `frame' (or max_framepos if none exists)
969  */
970 void
971 Locations::marks_either_side (framepos_t const frame, framepos_t& before, framepos_t& after) const
972 {
973         before = after = max_framepos;
974
975         LocationList locs;
976
977         {
978                 Glib::Mutex::Lock lm (lock);
979                 locs = locations;
980         }
981
982         /* Get a list of positions; don't store any that are exactly on our requested position */
983
984         std::list<framepos_t> positions;
985
986         for (LocationList::const_iterator i = locs.begin(); i != locs.end(); ++i) {
987                 if (((*i)->is_auto_loop() || (*i)->is_auto_punch())) {
988                         continue;
989                 }
990
991                 if (!(*i)->is_hidden()) {
992                         if ((*i)->is_mark ()) {
993                                 if ((*i)->start() != frame) {
994                                         positions.push_back ((*i)->start ());
995                                 }
996                         } else {
997                                 if ((*i)->start() != frame) {
998                                         positions.push_back ((*i)->start ());
999                                 }
1000                                 if ((*i)->end() != frame) {
1001                                         positions.push_back ((*i)->end ());
1002                                 }
1003                         }
1004                 }
1005         }
1006
1007         if (positions.empty ()) {
1008                 return;
1009         }
1010
1011         positions.sort ();
1012
1013         std::list<framepos_t>::iterator i = positions.begin ();
1014         while (i != positions.end () && *i < frame) {
1015                 ++i;
1016         }
1017
1018         if (i == positions.end ()) {
1019                 /* run out of marks */
1020                 before = positions.back ();
1021                 return;
1022         }
1023
1024         after = *i;
1025
1026         if (i == positions.begin ()) {
1027                 /* none before */
1028                 return;
1029         }
1030
1031         --i;
1032         before = *i;
1033 }
1034
1035 Location*
1036 Locations::session_range_location () const
1037 {
1038         for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1039                 if ((*i)->is_session_range()) {
1040                         return const_cast<Location*> (*i);
1041                 }
1042         }
1043         return 0;
1044 }
1045
1046 Location*
1047 Locations::auto_loop_location () const
1048 {
1049         for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1050                 if ((*i)->is_auto_loop()) {
1051                         return const_cast<Location*> (*i);
1052                 }
1053         }
1054         return 0;
1055 }
1056
1057 Location*
1058 Locations::auto_punch_location () const
1059 {
1060         for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1061                 if ((*i)->is_auto_punch()) {
1062                         return const_cast<Location*> (*i);
1063                 }
1064         }
1065        return 0;
1066 }
1067
1068 uint32_t
1069 Locations::num_range_markers () const
1070 {
1071         uint32_t cnt = 0;
1072         Glib::Mutex::Lock lm (lock);
1073         for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1074                 if ((*i)->is_range_marker()) {
1075                         ++cnt;
1076                 }
1077         }
1078         return cnt;
1079 }
1080
1081 Location *
1082 Locations::get_location_by_id(PBD::ID id)
1083 {
1084     LocationList::iterator it;
1085     for (it  = locations.begin(); it != locations.end(); ++it)
1086         if (id == (*it)->id())
1087             return *it;
1088
1089     return 0;
1090 }
1091
1092 void
1093 Locations::find_all_between (framepos_t start, framepos_t end, LocationList& ll, Location::Flags flags)
1094 {
1095         Glib::Mutex::Lock lm (lock);
1096
1097         for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1098                 if ((flags == 0 || (*i)->matches (flags)) &&
1099                     ((*i)->start() >= start && (*i)->end() < end)) {
1100                         ll.push_back (*i);
1101                 }
1102         }
1103 }