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