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