rework Stateful::set_state() patch to avoid default version argument
[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 <sigc++/bind.h>
27
28 #include "pbd/stl_delete.h"
29 #include "pbd/xml++.h"
30 #include "pbd/enumwriter.h"
31
32 #include "ardour/location.h"
33 #include "ardour/session.h"
34 #include "ardour/audiofilesource.h"
35
36 #include "i18n.h"
37
38 #define SUFFIX_MAX 32
39
40 using namespace std;
41 using namespace ARDOUR;
42 using namespace sigc;
43 using namespace PBD;
44
45 Location::Location (const Location& other)
46         : StatefulDestructible(),
47           _name (other._name),
48           _start (other._start),
49           _end (other._end),
50           _flags (other._flags)
51 {
52         /* start and end flags can never be copied, because there can only ever be one of each */
53
54         _flags = Flags (_flags & ~IsStart);
55         _flags = Flags (_flags & ~IsEnd);
56
57         /* copy is not locked even if original was */
58
59         _locked = false;
60 }
61
62 Location::Location (const XMLNode& node)
63 {
64         if (set_state (node, Stateful::loading_state_version)) {
65                 throw failed_constructor ();
66         }
67 }
68
69 Location*
70 Location::operator= (const Location& other)
71 {
72         if (this == &other) {
73                 return this;
74         }
75
76         _name = other._name;
77         _start = other._start;
78         _end = other._end;
79         _flags = other._flags;
80
81         /* copy is not locked even if original was */
82
83         _locked = false;
84
85         /* "changed" not emitted on purpose */
86
87         return this;
88 }
89
90 int
91 Location::set_start (nframes64_t s)
92 {
93         if (_locked) {
94                 return -1;
95         }
96
97         if (is_mark()) {
98                 if (_start != s) {
99
100                         _start = s;
101                         _end = s;
102
103                         start_changed(this); /* EMIT SIGNAL */
104                         end_changed(this); /* EMIT SIGNAL */
105
106                         if ( is_start() ) {
107
108                                 Session::StartTimeChanged (); /* EMIT SIGNAL */
109                                 AudioFileSource::set_header_position_offset ( s );
110                         }
111
112                         if ( is_end() ) {
113                                 Session::EndTimeChanged (); /* EMIT SIGNAL */
114                         }
115                 }
116                 return 0;
117         }
118
119         if (((is_auto_punch() || is_auto_loop()) && s >= _end) || s > _end) {
120                 return -1;
121         }
122
123         if (s != _start) {
124                 _start = s;
125                 start_changed(this); /* EMIT SIGNAL */
126         }
127
128         return 0;
129 }
130
131 int
132 Location::set_end (nframes64_t e)
133 {
134         if (_locked) {
135                 return -1;
136         }
137
138         if (is_mark()) {
139                 if (_start != e) {
140                         _start = e;
141                         _end = e;
142                         start_changed(this); /* EMIT SIGNAL */
143                         end_changed(this); /* EMIT SIGNAL */
144
145                         if ( is_start() ) {
146                                 Session::StartTimeChanged (); /* EMIT SIGNAL */
147                         }
148
149                         if ( is_end() ) {
150                                 Session::EndTimeChanged (); /* EMIT SIGNAL */
151                         }
152
153                 }
154                 return 0;
155         }
156
157         if (((is_auto_punch() || is_auto_loop()) && e <= _start) || e < _start) {
158                 return -1;
159         }
160
161         if (e != _end) {
162                 _end = e;
163                  end_changed(this); /* EMIT SIGNAL */
164         }
165         return 0;
166 }
167
168 int
169 Location::set (nframes64_t start, nframes64_t end)
170 {
171         if (_locked) {
172                 return -1;
173         }
174
175         if (is_mark() && start != end) {
176                 return -1;
177         } else if (((is_auto_punch() || is_auto_loop()) && start >= end) || (start > end)) {
178                 return -1;
179         }
180
181         if (_start != start) {
182                 _start = start;
183                 start_changed(this); /* EMIT SIGNAL */
184         }
185
186         if (_end != end) {
187                 _end = end;
188                 end_changed(this); /* EMIT SIGNAL */
189         }
190         return 0;
191 }
192
193 int
194 Location::move_to (nframes64_t pos)
195 {
196         if (_locked) {
197                 return -1;
198         }
199
200         if (_start != pos) {
201                 _start = pos;
202                 _end = _start + length();
203
204                 changed (this); /* EMIT SIGNAL */
205         }
206
207         return 0;
208 }
209
210 void
211 Location::set_hidden (bool yn, void *src)
212 {
213         if (set_flag_internal (yn, IsHidden)) {
214                  FlagsChanged (this, src); /* EMIT SIGNAL */
215         }
216 }
217
218 void
219 Location::set_cd (bool yn, void *src)
220 {
221         // XXX this really needs to be session start
222         // but its not available here - leave to GUI
223
224         if (_start == 0) {
225                 error << _("You cannot put a CD marker at this position") << endmsg;
226                 return;
227         }
228
229         if (set_flag_internal (yn, IsCDMarker)) {
230                  FlagsChanged (this, src); /* EMIT SIGNAL */
231         }
232 }
233
234 void
235 Location::set_is_end (bool yn, void *src)
236 {
237         if (set_flag_internal (yn, IsEnd)) {
238                  FlagsChanged (this, src); /* EMIT SIGNAL */
239         }
240 }
241
242 void
243 Location::set_is_start (bool yn, void *src)
244 {
245         if (set_flag_internal (yn, IsStart)) {
246                  FlagsChanged (this, src); /* EMIT SIGNAL */
247         }
248 }
249
250 void
251 Location::set_is_range_marker (bool yn, void *src)
252 {
253        if (set_flag_internal (yn, IsRangeMarker)) {
254                 FlagsChanged (this, src); /* EMIT SIGNAL */
255        }
256 }
257
258 void
259 Location::set_auto_punch (bool yn, void *src)
260 {
261         if (is_mark() || _start == _end) {
262                 return;
263         }
264
265         if (set_flag_internal (yn, IsAutoPunch)) {
266                  FlagsChanged (this, src); /* EMIT SIGNAL */
267         }
268 }
269
270 void
271 Location::set_auto_loop (bool yn, void *src)
272 {
273         if (is_mark() || _start == _end) {
274                 return;
275         }
276
277         if (set_flag_internal (yn, IsAutoLoop)) {
278                  FlagsChanged (this, src); /* EMIT SIGNAL */
279         }
280 }
281
282 bool
283 Location::set_flag_internal (bool yn, Flags flag)
284 {
285         if (yn) {
286                 if (!(_flags & flag)) {
287                         _flags = Flags (_flags | flag);
288                         return true;
289                 }
290         } else {
291                 if (_flags & flag) {
292                         _flags = Flags (_flags & ~flag);
293                         return true;
294                 }
295         }
296         return false;
297 }
298
299 void
300 Location::set_mark (bool yn)
301 {
302         /* This function is private, and so does not emit signals */
303
304         if (_start != _end) {
305                 return;
306         }
307
308         set_flag_internal (yn, IsMark);
309 }
310
311
312 XMLNode&
313 Location::cd_info_node(const string & name, const string & value)
314 {
315         XMLNode* root = new XMLNode("CD-Info");
316
317         root->add_property("name", name);
318         root->add_property("value", value);
319
320         return *root;
321 }
322
323
324 XMLNode&
325 Location::get_state (void)
326 {
327         XMLNode *node = new XMLNode ("Location");
328         char buf[64];
329
330         typedef map<string, string>::const_iterator CI;
331
332         for(CI m = cd_info.begin(); m != cd_info.end(); ++m){
333                 node->add_child_nocopy(cd_info_node(m->first, m->second));
334         }
335
336         id().print (buf, sizeof (buf));
337         node->add_property("id", buf);
338         node->add_property ("name", name());
339         snprintf (buf, sizeof (buf), "%" PRId64, start());
340         node->add_property ("start", buf);
341         snprintf (buf, sizeof (buf), "%" PRId64, end());
342         node->add_property ("end", buf);
343         node->add_property ("flags", enum_2_string (_flags));
344         node->add_property ("locked", (_locked ? "yes" : "no"));
345
346         return *node;
347 }
348
349 int
350 Location::set_state (const XMLNode& node, int version)
351 {
352         const XMLProperty *prop;
353
354         XMLNodeList cd_list = node.children();
355         XMLNodeConstIterator cd_iter;
356         XMLNode *cd_node;
357
358         string cd_name;
359         string cd_value;
360
361         if (node.name() != "Location") {
362                 error << _("incorrect XML node passed to Location::set_state") << endmsg;
363                 return -1;
364         }
365
366         if ((prop = node.property ("id")) == 0) {
367                 warning << _("XML node for Location has no ID information") << endmsg;
368         } else {
369                 _id = prop->value ();
370         }
371
372         if ((prop = node.property ("name")) == 0) {
373                 error << _("XML node for Location has no name information") << endmsg;
374                 return -1;
375         }
376
377         set_name (prop->value());
378
379         if ((prop = node.property ("start")) == 0) {
380                 error << _("XML node for Location has no start information") << endmsg;
381                 return -1;
382         }
383
384                 /* can't use set_start() here, because _end
385                    may make the value of _start illegal.
386                 */
387
388         sscanf (prop->value().c_str(), "%" PRId64, &_start);
389
390         if ((prop = node.property ("end")) == 0) {
391                   error << _("XML node for Location has no end information") << endmsg;
392                   return -1;
393         }
394
395         sscanf (prop->value().c_str(), "%" PRId64, &_end);
396
397         if ((prop = node.property ("flags")) == 0) {
398                   error << _("XML node for Location has no flags information") << endmsg;
399                   return -1;
400         }
401
402         _flags = Flags (string_2_enum (prop->value(), _flags));
403
404         if ((prop = node.property ("locked")) != 0) {
405                 _locked = string_is_affirmative (prop->value());
406         } else {
407                 _locked = false;
408         }
409
410         for (cd_iter = cd_list.begin(); cd_iter != cd_list.end(); ++cd_iter) {
411
412                   cd_node = *cd_iter;
413
414                   if (cd_node->name() != "CD-Info") {
415                     continue;
416                   }
417
418                   if ((prop = cd_node->property ("name")) != 0) {
419                     cd_name = prop->value();
420                   } else {
421                     throw failed_constructor ();
422                   }
423
424                   if ((prop = cd_node->property ("value")) != 0) {
425                     cd_value = prop->value();
426                   } else {
427                     throw failed_constructor ();
428                   }
429
430
431                   cd_info[cd_name] = cd_value;
432
433         }
434
435         changed(this); /* EMIT SIGNAL */
436
437         return 0;
438 }
439
440 /*---------------------------------------------------------------------- */
441
442 Locations::Locations ()
443
444 {
445         current_location = 0;
446 }
447
448 Locations::~Locations ()
449 {
450         for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
451                 LocationList::iterator tmp = i;
452                 ++tmp;
453                 delete *i;
454                 i = tmp;
455         }
456 }
457
458 int
459 Locations::set_current (Location *loc, bool want_lock)
460
461 {
462         int ret;
463
464         if (want_lock) {
465                 Glib::Mutex::Lock lm (lock);
466                 ret = set_current_unlocked (loc);
467         } else {
468                 ret = set_current_unlocked (loc);
469         }
470
471         if (ret == 0) {
472                  current_changed (current_location); /* EMIT SIGNAL */
473         }
474         return ret;
475 }
476
477 int
478 Locations::next_available_name(string& result,string base)
479 {
480         LocationList::iterator i;
481         Location* location;
482         string temp;
483         string::size_type l;
484         int suffix;
485         char buf[32];
486         bool available[SUFFIX_MAX+1];
487
488         result = base;
489         for (int k=1; k<SUFFIX_MAX; k++) {
490                 available[k] = true;
491         }
492         l = base.length();
493         for (i = locations.begin(); i != locations.end(); ++i) {
494                 location =* i;
495                 temp = location->name();
496                 if (l && !temp.find(base,0)) {
497                         suffix = atoi(temp.substr(l,3).c_str());
498                         if (suffix) available[suffix] = false;
499                 }
500         }
501         for (int k=1; k<=SUFFIX_MAX; k++) {
502                 if (available[k]) {
503                         snprintf (buf, 31, "%d", k);
504                         result += buf;
505                         return 1;
506                 }
507         }
508         return 0;
509 }
510
511 int
512 Locations::set_current_unlocked (Location *loc)
513 {
514         if (find (locations.begin(), locations.end(), loc) == locations.end()) {
515                 error << _("Locations: attempt to use unknown location as selected location") << endmsg;
516                 return -1;
517         }
518
519         current_location = loc;
520         return 0;
521 }
522
523 void
524 Locations::clear ()
525 {
526         {
527                 Glib::Mutex::Lock lm (lock);
528
529                 for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
530
531                         LocationList::iterator tmp = i;
532                         ++tmp;
533
534                         if (!(*i)->is_end() && !(*i)->is_start()) {
535                                 locations.erase (i);
536                         }
537
538                         i = tmp;
539                 }
540
541                 current_location = 0;
542         }
543
544         changed (); /* EMIT SIGNAL */
545         current_changed (0); /* EMIT SIGNAL */
546 }
547
548 void
549 Locations::clear_markers ()
550 {
551         {
552                 Glib::Mutex::Lock lm (lock);
553                 LocationList::iterator tmp;
554
555                 for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
556                         tmp = i;
557                         ++tmp;
558
559                         if ((*i)->is_mark() && !(*i)->is_end() && !(*i)->is_start()) {
560                                 locations.erase (i);
561                         }
562
563                         i = tmp;
564                 }
565         }
566
567         changed (); /* EMIT SIGNAL */
568 }
569
570 void
571 Locations::clear_ranges ()
572 {
573         {
574                 Glib::Mutex::Lock lm (lock);
575                 LocationList::iterator tmp;
576
577                 for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
578
579                         tmp = i;
580                         ++tmp;
581
582                         if (!(*i)->is_mark()) {
583                                 locations.erase (i);
584
585                         }
586
587                         i = tmp;
588                 }
589
590                 current_location = 0;
591         }
592
593         changed (); /* EMIT SIGNAL */
594         current_changed (0); /* EMIT SIGNAL */
595 }
596
597 void
598 Locations::add (Location *loc, bool make_current)
599 {
600         {
601                 Glib::Mutex::Lock lm (lock);
602                 locations.push_back (loc);
603
604                 if (make_current) {
605                         current_location = loc;
606                 }
607         }
608
609         added (loc); /* EMIT SIGNAL */
610
611         if (make_current) {
612                  current_changed (current_location); /* EMIT SIGNAL */
613         }
614 }
615
616 void
617 Locations::remove (Location *loc)
618
619 {
620         bool was_removed = false;
621         bool was_current = false;
622         LocationList::iterator i;
623
624         if (loc->is_end() || loc->is_start()) {
625                 return;
626         }
627
628         {
629                 Glib::Mutex::Lock lm (lock);
630
631                 for (i = locations.begin(); i != locations.end(); ++i) {
632                         if ((*i) == loc) {
633                                 locations.erase (i);
634                                 was_removed = true;
635                                 if (current_location == loc) {
636                                         current_location = 0;
637                                         was_current = true;
638                                 }
639                                 break;
640                         }
641                 }
642         }
643
644         if (was_removed) {
645
646                 removed (loc); /* EMIT SIGNAL */
647
648                 if (was_current) {
649                          current_changed (0); /* EMIT SIGNAL */
650                 }
651
652                 changed (); /* EMIT_SIGNAL */
653         }
654 }
655
656 void
657 Locations::location_changed (Location* /*loc*/)
658 {
659         changed (); /* EMIT SIGNAL */
660 }
661
662 XMLNode&
663 Locations::get_state ()
664 {
665         XMLNode *node = new XMLNode ("Locations");
666         LocationList::iterator iter;
667         Glib::Mutex::Lock lm (lock);
668
669         for (iter  = locations.begin(); iter != locations.end(); ++iter) {
670                 node->add_child_nocopy ((*iter)->get_state ());
671         }
672
673         return *node;
674 }
675
676 int
677 Locations::set_state (const XMLNode& node, int version)
678 {
679         XMLNodeList nlist;
680         XMLNodeConstIterator niter;
681
682         if (node.name() != "Locations") {
683                 error << _("incorrect XML mode passed to Locations::set_state") << endmsg;
684                 return -1;
685         }
686
687         nlist = node.children();
688
689         locations.clear ();
690         current_location = 0;
691
692         {
693                 Glib::Mutex::Lock lm (lock);
694
695                 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
696
697                         try {
698
699                                 Location *loc = new Location (**niter);
700                                 locations.push_back (loc);
701                         }
702
703                         catch (failed_constructor& err) {
704                                 error << _("could not load location from session file - ignored") << endmsg;
705                         }
706                 }
707
708                 if (locations.size()) {
709
710                         current_location = locations.front();
711                 } else {
712                         current_location = 0;
713                 }
714         }
715
716         changed (); /* EMIT SIGNAL */
717
718         return 0;
719 }
720
721 struct LocationStartEarlierComparison
722 {
723     bool operator() (Location *a, Location *b) {
724         return a->start() < b->start();
725     }
726 };
727
728 struct LocationStartLaterComparison
729 {
730     bool operator() (Location *a, Location *b) {
731         return a->start() > b->start();
732     }
733 };
734
735 Location *
736 Locations::first_location_before (nframes64_t frame, bool include_special_ranges)
737 {
738         LocationList locs;
739
740         {
741                 Glib::Mutex::Lock lm (lock);
742                 locs = locations;
743         }
744
745         LocationStartLaterComparison cmp;
746         locs.sort (cmp);
747
748         /* locs is now sorted latest..earliest */
749
750         for (LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
751                 if (!include_special_ranges && ((*i)->is_auto_loop() || (*i)->is_auto_punch())) {
752                         continue;
753                 }
754                 if (!(*i)->is_hidden() && (*i)->start() < frame) {
755                         return (*i);
756                 }
757         }
758
759         return 0;
760 }
761
762 Location *
763 Locations::first_location_after (nframes64_t frame, bool include_special_ranges)
764 {
765         LocationList locs;
766
767         {
768                 Glib::Mutex::Lock lm (lock);
769                 locs = locations;
770         }
771
772         LocationStartEarlierComparison cmp;
773         locs.sort (cmp);
774
775         /* locs is now sorted earliest..latest */
776
777         for (LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
778                 if (!include_special_ranges && ((*i)->is_auto_loop() || (*i)->is_auto_punch())) {
779                         continue;
780                 }
781                 if (!(*i)->is_hidden() && (*i)->start() > frame) {
782                         return (*i);
783                 }
784         }
785
786         return 0;
787 }
788
789 nframes64_t
790 Locations::first_mark_before (nframes64_t frame, bool include_special_ranges)
791 {
792         LocationList locs;
793
794         {
795         Glib::Mutex::Lock lm (lock);
796                 locs = locations;
797         }
798
799         LocationStartLaterComparison cmp;
800         locs.sort (cmp);
801
802         /* locs is now sorted latest..earliest */
803
804         for (LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
805                 if (!include_special_ranges && ((*i)->is_auto_loop() || (*i)->is_auto_punch())) {
806                         continue;
807                 }
808                 if (!(*i)->is_hidden()) {
809                         if ((*i)->is_mark()) {
810                                 /* MARK: start == end */
811                                 if ((*i)->start() < frame) {
812                                         return (*i)->start();
813                                 }
814                         } else {
815                                 /* RANGE: start != end, compare start and end */
816                                 if ((*i)->end() < frame) {
817                                         return (*i)->end();
818                                 }
819                                 if ((*i)->start () < frame) {
820                                         return (*i)->start();
821                                 }
822                         }
823                 }
824         }
825
826         return 0;
827 }
828
829 nframes64_t
830 Locations::first_mark_after (nframes64_t frame, bool include_special_ranges)
831 {
832         LocationList locs;
833
834         {
835         Glib::Mutex::Lock lm (lock);
836                 locs = locations;
837         }
838
839         LocationStartEarlierComparison cmp;
840         locs.sort (cmp);
841
842         /* locs is now sorted earliest..latest */
843
844         for (LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
845                 if (!include_special_ranges && ((*i)->is_auto_loop() || (*i)->is_auto_punch())) {
846                         continue;
847                 }
848                 if (!(*i)->is_hidden()) {
849                         if ((*i)->is_mark()) {
850                                 /* MARK, start == end so just compare start */
851                                 if ((*i)->start() > frame) {
852                                         return (*i)->start();
853                                 }
854                         } else {
855                                 /* RANGE, start != end, compare start and end */
856                                 if ((*i)->start() > frame ) {
857                                         return (*i)->start ();
858                                 }
859                                 if ((*i)->end() > frame) {
860                                         return (*i)->end ();
861                                 }
862                         }
863                 }
864         }
865
866         return max_frames;
867 }
868
869 Location*
870 Locations::end_location () const
871 {
872         for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
873                 if ((*i)->is_end()) {
874                         return const_cast<Location*> (*i);
875                 }
876         }
877         return 0;
878 }
879
880 Location*
881 Locations::start_location () const
882 {
883         for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
884                 if ((*i)->is_start()) {
885                         return const_cast<Location*> (*i);
886                 }
887         }
888         return 0;
889 }
890
891 Location*
892 Locations::auto_loop_location () const
893 {
894         for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
895                 if ((*i)->is_auto_loop()) {
896                         return const_cast<Location*> (*i);
897                 }
898         }
899         return 0;
900 }
901
902 Location*
903 Locations::auto_punch_location () const
904 {
905         for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
906                 if ((*i)->is_auto_punch()) {
907                         return const_cast<Location*> (*i);
908                 }
909         }
910        return 0;
911 }
912
913 uint32_t
914 Locations::num_range_markers () const
915 {
916         uint32_t cnt = 0;
917         Glib::Mutex::Lock lm (lock);
918         for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
919                 if ((*i)->is_range_marker()) {
920                         ++cnt;
921                 }
922         }
923         return cnt;
924 }
925
926 Location *
927 Locations::get_location_by_id(PBD::ID id)
928 {
929     LocationList::iterator it;
930     for (it  = locations.begin(); it != locations.end(); it++)
931         if (id == (*it)->id())
932             return *it;
933
934     return 0;
935 }
936
937 void
938 Locations::find_all_between (nframes64_t start, nframes64_t end, LocationList& ll, Location::Flags flags)
939 {
940         Glib::Mutex::Lock lm (lock);
941
942         for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
943                 if ((flags == 0 || (*i)->matches (flags)) &&
944                     ((*i)->start() >= start && (*i)->end() < end)) {
945                         ll.push_back (*i);
946                 }
947         }
948 }