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