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