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