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