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