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