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