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