windows: don’t popup message box when libjack is not found
[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_skip (bool yn)
380 {
381         if (is_range_marker() && length() > 0) {
382                 if (set_flag_internal (yn, IsSkip)) {
383                         flags_changed (this);
384                         FlagsChanged ();
385                 }
386         }
387 }
388
389 void
390 Location::set_auto_punch (bool yn, void*)
391 {
392         if (is_mark() || _start == _end) {
393                 return;
394         }
395
396         if (set_flag_internal (yn, IsAutoPunch)) {
397                  flags_changed (this); /* EMIT SIGNAL */
398                  FlagsChanged (); /* EMIT SIGNAL */
399         }
400 }
401
402 void
403 Location::set_auto_loop (bool yn, void*)
404 {
405         if (is_mark() || _start == _end) {
406                 return;
407         }
408
409         if (set_flag_internal (yn, IsAutoLoop)) {
410                  flags_changed (this); /* EMIT SIGNAL */
411                  FlagsChanged (); /* EMIT SIGNAL */
412         }
413 }
414
415 bool
416 Location::set_flag_internal (bool yn, Flags flag)
417 {
418         if (yn) {
419                 if (!(_flags & flag)) {
420                         _flags = Flags (_flags | flag);
421                         return true;
422                 }
423         } else {
424                 if (_flags & flag) {
425                         _flags = Flags (_flags & ~flag);
426                         return true;
427                 }
428         }
429         return false;
430 }
431
432 void
433 Location::set_mark (bool yn)
434 {
435         /* This function is private, and so does not emit signals */
436
437         if (_start != _end) {
438                 return;
439         }
440
441         set_flag_internal (yn, IsMark);
442 }
443
444
445 XMLNode&
446 Location::cd_info_node(const string & name, const string & value)
447 {
448         XMLNode* root = new XMLNode("CD-Info");
449
450         root->add_property("name", name);
451         root->add_property("value", value);
452
453         return *root;
454 }
455
456
457 XMLNode&
458 Location::get_state ()
459 {
460         XMLNode *node = new XMLNode ("Location");
461         char buf[64];
462
463         typedef map<string, string>::const_iterator CI;
464
465         for(CI m = cd_info.begin(); m != cd_info.end(); ++m){
466                 node->add_child_nocopy(cd_info_node(m->first, m->second));
467         }
468
469         id().print (buf, sizeof (buf));
470         node->add_property("id", buf);
471         node->add_property ("name", name());
472         snprintf (buf, sizeof (buf), "%" PRId64, start());
473         node->add_property ("start", buf);
474         snprintf (buf, sizeof (buf), "%" PRId64, end());
475         node->add_property ("end", buf);
476         node->add_property ("flags", enum_2_string (_flags));
477         node->add_property ("locked", (_locked ? "yes" : "no"));
478         node->add_property ("position-lock-style", enum_2_string (_position_lock_style));
479
480         if (_scene_change) {
481                 node->add_child_nocopy (_scene_change->get_state());
482         }
483
484         return *node;
485 }
486
487 int
488 Location::set_state (const XMLNode& node, int version)
489 {
490         const XMLProperty *prop;
491
492         XMLNodeList cd_list = node.children();
493         XMLNodeConstIterator cd_iter;
494         XMLNode *cd_node;
495
496         string cd_name;
497         string cd_value;
498
499         if (node.name() != "Location") {
500                 error << _("incorrect XML node passed to Location::set_state") << endmsg;
501                 return -1;
502         }
503
504         if (!set_id (node)) {
505                 warning << _("XML node for Location has no ID information") << endmsg;
506         }
507
508         if ((prop = node.property ("name")) == 0) {
509                 error << _("XML node for Location has no name information") << endmsg;
510                 return -1;
511         }
512
513         set_name (prop->value());
514
515         if ((prop = node.property ("start")) == 0) {
516                 error << _("XML node for Location has no start information") << endmsg;
517                 return -1;
518         }
519
520                 /* can't use set_start() here, because _end
521                    may make the value of _start illegal.
522                 */
523
524         sscanf (prop->value().c_str(), "%" PRId64, &_start);
525
526         if ((prop = node.property ("end")) == 0) {
527                   error << _("XML node for Location has no end information") << endmsg;
528                   return -1;
529         }
530
531         sscanf (prop->value().c_str(), "%" PRId64, &_end);
532
533         if ((prop = node.property ("flags")) == 0) {
534                   error << _("XML node for Location has no flags information") << endmsg;
535                   return -1;
536         }
537
538         _flags = Flags (string_2_enum (prop->value(), _flags));
539
540         if ((prop = node.property ("locked")) != 0) {
541                 _locked = string_is_affirmative (prop->value());
542         } else {
543                 _locked = false;
544         }
545
546         for (cd_iter = cd_list.begin(); cd_iter != cd_list.end(); ++cd_iter) {
547
548                   cd_node = *cd_iter;
549
550                   if (cd_node->name() != "CD-Info") {
551                           continue;
552                   }
553
554                   if ((prop = cd_node->property ("name")) != 0) {
555                           cd_name = prop->value();
556                   } else {
557                           throw failed_constructor ();
558                   }
559
560                   if ((prop = cd_node->property ("value")) != 0) {
561                           cd_value = prop->value();
562                   } else {
563                           throw failed_constructor ();
564                   }
565
566
567                   cd_info[cd_name] = cd_value;
568         }
569
570         if ((prop = node.property ("position-lock-style")) != 0) {
571                 _position_lock_style = PositionLockStyle (string_2_enum (prop->value(), _position_lock_style));
572         }
573
574         XMLNode* scene_child = find_named_node (node, SceneChange::xml_node_name);
575         
576         if (scene_child) {
577                 _scene_change = SceneChange::factory (*scene_child, version);
578         }
579
580         recompute_bbt_from_frames ();
581
582         changed (this); /* EMIT SIGNAL */
583         Changed (); /* EMIT SIGNAL */
584
585         assert (_start >= 0);
586         assert (_end >= 0);
587
588         return 0;
589 }
590
591 void
592 Location::set_position_lock_style (PositionLockStyle ps)
593 {
594         if (_position_lock_style == ps) {
595                 return;
596         }
597
598         _position_lock_style = ps;
599
600         recompute_bbt_from_frames ();
601
602         position_lock_style_changed (this); /* EMIT SIGNAL */
603         PositionLockStyleChanged (); /* EMIT SIGNAL */
604 }
605
606 void
607 Location::recompute_bbt_from_frames ()
608 {
609         if (_position_lock_style != MusicTime) {
610                 return;
611         }
612
613         _session.bbt_time (_start, _bbt_start);
614         _session.bbt_time (_end, _bbt_end);
615 }
616
617 void
618 Location::recompute_frames_from_bbt ()
619 {
620         if (_position_lock_style != MusicTime) {
621                 return;
622         }
623
624         TempoMap& map (_session.tempo_map());
625         set (map.frame_time (_bbt_start), map.frame_time (_bbt_end), false);
626 }
627
628 void
629 Location::lock ()
630 {
631         _locked = true;
632         lock_changed (this);
633         LockChanged ();
634 }
635
636 void
637 Location::unlock ()
638 {
639         _locked = false;
640         lock_changed (this);
641         LockChanged ();
642 }
643
644 void
645 Location::set_scene_change (boost::shared_ptr<SceneChange>  sc)
646 {
647         _scene_change = sc;
648
649         scene_changed (); /* EMIT SIGNAL */
650 }
651
652 /*---------------------------------------------------------------------- */
653
654 Locations::Locations (Session& s)
655         : SessionHandleRef (s)
656 {
657         current_location = 0;
658
659         Location::changed.connect_same_thread (*this, boost::bind (&Locations::location_changed, this, _1));
660         Location::start_changed.connect_same_thread (*this, boost::bind (&Locations::location_changed, this, _1));
661         Location::end_changed.connect_same_thread (*this, boost::bind (&Locations::location_changed, this, _1));
662         Location::flags_changed.connect_same_thread (*this, boost::bind (&Locations::location_changed, this, _1));
663 }
664
665 Locations::~Locations ()
666 {
667         for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
668                 LocationList::iterator tmp = i;
669                 ++tmp;
670                 delete *i;
671                 i = tmp;
672         }
673 }
674
675 int
676 Locations::set_current (Location *loc, bool want_lock)
677 {
678         int ret;
679
680         if (want_lock) {
681                 Glib::Threads::Mutex::Lock lm (lock);
682                 ret = set_current_unlocked (loc);
683         } else {
684                 ret = set_current_unlocked (loc);
685         }
686
687         if (ret == 0) {
688                  current_changed (current_location); /* EMIT SIGNAL */
689         }
690         return ret;
691 }
692
693 int
694 Locations::next_available_name(string& result,string base)
695 {
696         LocationList::iterator i;
697         Location* location;
698         string temp;
699         string::size_type l;
700         int suffix;
701         char buf[32];
702         bool available[SUFFIX_MAX+1];
703
704         result = base;
705         for (int k=1; k<SUFFIX_MAX; k++) {
706                 available[k] = true;
707         }
708         l = base.length();
709         for (i = locations.begin(); i != locations.end(); ++i) {
710                 location =* i;
711                 temp = location->name();
712                 if (l && !temp.find(base,0)) {
713                         suffix = atoi(temp.substr(l,3).c_str());
714                         if (suffix) available[suffix] = false;
715                 }
716         }
717         for (int k=1; k<=SUFFIX_MAX; k++) {
718                 if (available[k]) {
719                         snprintf (buf, 31, "%d", k);
720                         result += buf;
721                         return 1;
722                 }
723         }
724         return 0;
725 }
726
727 int
728 Locations::set_current_unlocked (Location *loc)
729 {
730         if (find (locations.begin(), locations.end(), loc) == locations.end()) {
731                 error << _("Locations: attempt to use unknown location as selected location") << endmsg;
732                 return -1;
733         }
734
735         current_location = loc;
736         return 0;
737 }
738
739 void
740 Locations::clear ()
741 {
742         {
743                 Glib::Threads::Mutex::Lock lm (lock);
744
745                 for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
746
747                         LocationList::iterator tmp = i;
748                         ++tmp;
749
750                         if (!(*i)->is_session_range()) {
751                                 delete *i;
752                                 locations.erase (i);
753                         }
754
755                         i = tmp;
756                 }
757
758                 current_location = 0;
759         }
760
761         changed (OTHER); /* EMIT SIGNAL */
762         current_changed (0); /* EMIT SIGNAL */
763 }
764
765 void
766 Locations::clear_markers ()
767 {
768         {
769                 Glib::Threads::Mutex::Lock lm (lock);
770                 LocationList::iterator tmp;
771
772                 for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
773                         tmp = i;
774                         ++tmp;
775
776                         if ((*i)->is_mark() && !(*i)->is_session_range()) {
777                                 delete *i;
778                                 locations.erase (i);
779                         }
780
781                         i = tmp;
782                 }
783         }
784
785         changed (OTHER); /* EMIT SIGNAL */
786 }
787
788 void
789 Locations::clear_ranges ()
790 {
791         {
792                 Glib::Threads::Mutex::Lock lm (lock);
793                 LocationList::iterator tmp;
794
795                 for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
796
797                         tmp = i;
798                         ++tmp;
799
800                         /* We do not remove these ranges as part of this
801                          * operation
802                          */
803
804                         if ((*i)->is_auto_punch() ||
805                             (*i)->is_auto_loop() ||
806                             (*i)->is_session_range()) {
807                                 i = tmp;
808                                 continue;
809                         }
810
811                         if (!(*i)->is_mark()) {
812                                 delete *i;
813                                 locations.erase (i);
814
815                         }
816
817                         i = tmp;
818                 }
819
820                 current_location = 0;
821         }
822
823         changed (OTHER); /* EMIT SIGNAL */
824         current_changed (0); /* EMIT SIGNAL */
825 }
826
827 void
828 Locations::add (Location *loc, bool make_current)
829 {
830         assert (loc);
831
832         {
833                 Glib::Threads::Mutex::Lock lm (lock);
834                 locations.push_back (loc);
835
836                 if (make_current) {
837                         current_location = loc;
838                 }
839         }
840
841         added (loc); /* EMIT SIGNAL */
842
843         if (make_current) {
844                  current_changed (current_location); /* EMIT SIGNAL */
845         }
846
847         if (loc->is_session_range()) {
848                 Session::StartTimeChanged (0);
849                 Session::EndTimeChanged (1);
850         }
851 }
852
853 void
854 Locations::remove (Location *loc)
855 {
856         bool was_removed = false;
857         bool was_current = false;
858         LocationList::iterator i;
859
860         if (loc->is_session_range()) {
861                 return;
862         }
863
864         {
865                 Glib::Threads::Mutex::Lock lm (lock);
866
867                 for (i = locations.begin(); i != locations.end(); ++i) {
868                         if ((*i) == loc) {
869                                 delete *i;
870                                 locations.erase (i);
871                                 was_removed = true;
872                                 if (current_location == loc) {
873                                         current_location = 0;
874                                         was_current = true;
875                                 }
876                                 break;
877                         }
878                 }
879         }
880
881         if (was_removed) {
882
883                 removed (loc); /* EMIT SIGNAL */
884
885                 if (was_current) {
886                          current_changed (0); /* EMIT SIGNAL */
887                 }
888
889                 changed (REMOVAL); /* EMIT_SIGNAL */
890         }
891 }
892
893 void
894 Locations::location_changed (Location* /*loc*/)
895 {
896         changed (OTHER); /* EMIT SIGNAL */
897 }
898
899 XMLNode&
900 Locations::get_state ()
901 {
902         XMLNode *node = new XMLNode ("Locations");
903         LocationList::iterator iter;
904         Glib::Threads::Mutex::Lock lm (lock);
905
906         for (iter = locations.begin(); iter != locations.end(); ++iter) {
907                 node->add_child_nocopy ((*iter)->get_state ());
908         }
909
910         return *node;
911 }
912
913 int
914 Locations::set_state (const XMLNode& node, int version)
915 {
916         if (node.name() != "Locations") {
917                 error << _("incorrect XML mode passed to Locations::set_state") << endmsg;
918                 return -1;
919         }
920
921         XMLNodeList nlist = node.children();
922
923         /* build up a new locations list in here */
924         LocationList new_locations;
925
926         current_location = 0;
927
928         Location* session_range_location = 0;
929         if (version < 3000) {
930                 session_range_location = new Location (_session, 0, 0, _("session"), Location::IsSessionRange);
931                 new_locations.push_back (session_range_location);
932         }
933
934         {
935                 Glib::Threads::Mutex::Lock lm (lock);
936
937                 XMLNodeConstIterator niter;
938                 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
939
940                         try {
941
942                                 XMLProperty const * prop_id = (*niter)->property ("id");
943                                 assert (prop_id);
944                                 PBD::ID id (prop_id->value ());
945
946                                 LocationList::const_iterator i = locations.begin();
947                                 while (i != locations.end () && (*i)->id() != id) {
948                                         ++i;
949                                 }
950
951                                 Location* loc;
952                                 if (i != locations.end()) {
953                                         /* we can re-use an old Location object */
954                                         loc = *i;
955                                         loc->set_state (**niter, version);
956                                 } else {
957                                         loc = new Location (_session, **niter);
958                                 }
959
960                                 bool add = true;
961
962                                 if (version < 3000) {
963                                         /* look for old-style IsStart / IsEnd properties in this location;
964                                            if they are present, update the session_range_location accordingly
965                                         */
966                                         XMLProperty const * prop = (*niter)->property ("flags");
967                                         if (prop) {
968                                                 string v = prop->value ();
969                                                 while (1) {
970                                                         string::size_type const c = v.find_first_of (',');
971                                                         string const s = v.substr (0, c);
972                                                         if (s == X_("IsStart")) {
973                                                                 session_range_location->set_start (loc->start(), true);
974                                                                 add = false;
975                                                         } else if (s == X_("IsEnd")) {
976                                                                 session_range_location->set_end (loc->start(), true);
977                                                                 add = false;
978                                                         }
979
980                                                         if (c == string::npos) {
981                                                                 break;
982                                                         }
983
984                                                         v = v.substr (c + 1);
985                                                 }
986                                         }
987                                 }
988
989                                 if (add) {
990                                         new_locations.push_back (loc);
991                                 }
992                         }
993
994                         catch (failed_constructor& err) {
995                                 error << _("could not load location from session file - ignored") << endmsg;
996                         }
997                 }
998
999                 locations = new_locations;
1000
1001                 if (locations.size()) {
1002                         current_location = locations.front();
1003                 } else {
1004                         current_location = 0;
1005                 }
1006         }
1007
1008         changed (OTHER); /* EMIT SIGNAL */
1009
1010         return 0;
1011 }
1012
1013
1014 typedef std::pair<framepos_t,Location*> LocationPair;
1015
1016 struct LocationStartEarlierComparison
1017 {
1018     bool operator() (LocationPair a, LocationPair b) {
1019             return a.first < b.first;
1020     }
1021 };
1022
1023 struct LocationStartLaterComparison
1024 {
1025     bool operator() (LocationPair a, LocationPair b) {
1026             return a.first > b.first;
1027     }
1028 };
1029
1030 framepos_t
1031 Locations::first_mark_before (framepos_t frame, bool include_special_ranges)
1032 {
1033         Glib::Threads::Mutex::Lock lm (lock);
1034         vector<LocationPair> locs;
1035         
1036         for (LocationList::iterator i = locations.begin(); i != locations.end(); ++i) {
1037                 locs.push_back (make_pair ((*i)->start(), (*i)));
1038                 if (!(*i)->is_mark()) {
1039                         locs.push_back (make_pair ((*i)->end(), (*i)));
1040                 }
1041         }
1042
1043         LocationStartLaterComparison cmp;
1044         sort (locs.begin(), locs.end(), cmp);
1045
1046         /* locs is sorted in ascending order */
1047
1048         for (vector<LocationPair>::iterator i = locs.begin(); i != locs.end(); ++i) {
1049                 if ((*i).second->is_hidden()) {
1050                         continue;
1051                 }
1052                 if (!include_special_ranges && ((*i).second->is_auto_loop() || (*i).second->is_auto_punch())) {
1053                         continue;
1054                 }
1055                 if ((*i).first < frame) {
1056                         return (*i).first;
1057                 }
1058         }
1059
1060         return -1;
1061 }
1062
1063 Location*
1064 Locations::mark_at (framepos_t pos, framecnt_t slop) const
1065 {
1066         Glib::Threads::Mutex::Lock lm (lock);
1067         Location* closest = 0;
1068         frameoffset_t mindelta = max_framepos;
1069         frameoffset_t delta;
1070
1071         /* locations are not necessarily stored in linear time order so we have
1072          * to iterate across all of them to find the one closest to a give point.
1073          */
1074
1075         for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1076
1077                 if ((*i)->is_mark()) {
1078                         if (pos > (*i)->start()) { 
1079                                 delta = pos - (*i)->start();
1080                         } else {
1081                                 delta = (*i)->start() - pos;
1082                         }
1083                         
1084                         if (slop == 0 && delta == 0) {
1085                                 /* special case: no slop, and direct hit for position */
1086                                 return *i;
1087                         }
1088
1089                         if (delta <= slop) {
1090                                 if (delta < mindelta) {
1091                                         closest = *i;
1092                                         mindelta = delta;
1093                                 }
1094                         }
1095                 }
1096         }
1097
1098         return closest;
1099 }
1100
1101 framepos_t
1102 Locations::first_mark_after (framepos_t frame, bool include_special_ranges)
1103 {
1104         Glib::Threads::Mutex::Lock lm (lock);
1105         vector<LocationPair> locs;
1106
1107         for (LocationList::iterator i = locations.begin(); i != locations.end(); ++i) {
1108                 locs.push_back (make_pair ((*i)->start(), (*i)));
1109                 if (!(*i)->is_mark()) {
1110                         locs.push_back (make_pair ((*i)->end(), (*i)));
1111                 }
1112         }
1113
1114         LocationStartEarlierComparison cmp;
1115         sort (locs.begin(), locs.end(), cmp);
1116         
1117         /* locs is sorted in reverse order */
1118
1119         for (vector<LocationPair>::iterator i = locs.begin(); i != locs.end(); ++i) {
1120                 if ((*i).second->is_hidden()) {
1121                         continue;
1122                 }
1123                 if (!include_special_ranges && ((*i).second->is_auto_loop() || (*i).second->is_auto_punch())) {
1124                         continue;
1125                 }
1126                 if ((*i).first > frame) {
1127                         return (*i).first;
1128                 }
1129         }
1130
1131         return -1;
1132 }
1133
1134 /** Look for the `marks' (either locations which are marks, or start/end points of range markers) either
1135  *  side of a frame.  Note that if frame is exactly on a `mark', that mark will not be considered for returning
1136  *  as before/after.
1137  *  @param frame Frame to look for.
1138  *  @param before Filled in with the position of the last `mark' before `frame' (or max_framepos if none exists)
1139  *  @param after Filled in with the position of the next `mark' after `frame' (or max_framepos if none exists)
1140  */
1141 void
1142 Locations::marks_either_side (framepos_t const frame, framepos_t& before, framepos_t& after) const
1143 {
1144         before = after = max_framepos;
1145
1146         LocationList locs;
1147
1148         {
1149                 Glib::Threads::Mutex::Lock lm (lock);
1150                 locs = locations;
1151         }
1152
1153         /* Get a list of positions; don't store any that are exactly on our requested position */
1154
1155         std::list<framepos_t> positions;
1156
1157         for (LocationList::const_iterator i = locs.begin(); i != locs.end(); ++i) {
1158                 if (((*i)->is_auto_loop() || (*i)->is_auto_punch())) {
1159                         continue;
1160                 }
1161
1162                 if (!(*i)->is_hidden()) {
1163                         if ((*i)->is_mark ()) {
1164                                 if ((*i)->start() != frame) {
1165                                         positions.push_back ((*i)->start ());
1166                                 }
1167                         } else {
1168                                 if ((*i)->start() != frame) {
1169                                         positions.push_back ((*i)->start ());
1170                                 }
1171                                 if ((*i)->end() != frame) {
1172                                         positions.push_back ((*i)->end ());
1173                                 }
1174                         }
1175                 }
1176         }
1177
1178         if (positions.empty ()) {
1179                 return;
1180         }
1181
1182         positions.sort ();
1183
1184         std::list<framepos_t>::iterator i = positions.begin ();
1185         while (i != positions.end () && *i < frame) {
1186                 ++i;
1187         }
1188
1189         if (i == positions.end ()) {
1190                 /* run out of marks */
1191                 before = positions.back ();
1192                 return;
1193         }
1194
1195         after = *i;
1196
1197         if (i == positions.begin ()) {
1198                 /* none before */
1199                 return;
1200         }
1201
1202         --i;
1203         before = *i;
1204 }
1205
1206 Location*
1207 Locations::session_range_location () const
1208 {
1209         for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1210                 if ((*i)->is_session_range()) {
1211                         return const_cast<Location*> (*i);
1212                 }
1213         }
1214         return 0;
1215 }
1216
1217 Location*
1218 Locations::auto_loop_location () const
1219 {
1220         for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1221                 if ((*i)->is_auto_loop()) {
1222                         return const_cast<Location*> (*i);
1223                 }
1224         }
1225         return 0;
1226 }
1227
1228 Location*
1229 Locations::auto_punch_location () const
1230 {
1231         for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1232                 if ((*i)->is_auto_punch()) {
1233                         return const_cast<Location*> (*i);
1234                 }
1235         }
1236        return 0;
1237 }
1238
1239 uint32_t
1240 Locations::num_range_markers () const
1241 {
1242         uint32_t cnt = 0;
1243         Glib::Threads::Mutex::Lock lm (lock);
1244         for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1245                 if ((*i)->is_range_marker()) {
1246                         ++cnt;
1247                 }
1248         }
1249         return cnt;
1250 }
1251
1252 Location *
1253 Locations::get_location_by_id(PBD::ID id)
1254 {
1255     LocationList::iterator it;
1256     for (it  = locations.begin(); it != locations.end(); ++it)
1257         if (id == (*it)->id())
1258             return *it;
1259
1260     return 0;
1261 }
1262
1263 void
1264 Locations::find_all_between (framepos_t start, framepos_t end, LocationList& ll, Location::Flags flags)
1265 {
1266         Glib::Threads::Mutex::Lock lm (lock);
1267
1268         for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1269                 if ((flags == 0 || (*i)->matches (flags)) &&
1270                     ((*i)->start() >= start && (*i)->end() < end)) {
1271                         ll.push_back (*i);
1272                 }
1273         }
1274 }
1275