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