add begin/end undo/redo signals so that playlist can freeze/thaw itself around potent...
[ardour.git] / libs / ardour / region.cc
1 /*
2     Copyright (C) 2000-2003 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 <iostream>
21 #include <cmath>
22 #include <climits>
23 #include <algorithm>
24 #include <sstream>
25
26
27 #include <glibmm/thread.h>
28 #include "pbd/xml++.h"
29 #include "pbd/stacktrace.h"
30 #include "pbd/enumwriter.h"
31
32 #include "ardour/debug.h"
33 #include "ardour/region.h"
34 #include "ardour/playlist.h"
35 #include "ardour/session.h"
36 #include "ardour/source.h"
37 #include "ardour/tempo.h"
38 #include "ardour/region_factory.h"
39 #include "ardour/filter.h"
40 #include "ardour/profile.h"
41 #include "ardour/utils.h"
42
43 #include "i18n.h"
44
45 using namespace std;
46 using namespace ARDOUR;
47 using namespace PBD;
48
49 namespace ARDOUR { 
50         namespace Properties {
51                 PBD::PropertyDescriptor<bool> muted;
52                 PBD::PropertyDescriptor<bool> opaque;
53                 PBD::PropertyDescriptor<bool> locked;
54                 PBD::PropertyDescriptor<bool> automatic;
55                 PBD::PropertyDescriptor<bool> whole_file;
56                 PBD::PropertyDescriptor<bool> import;
57                 PBD::PropertyDescriptor<bool> external;
58                 PBD::PropertyDescriptor<bool> sync_marked;
59                 PBD::PropertyDescriptor<bool> left_of_split;
60                 PBD::PropertyDescriptor<bool> right_of_split;
61                 PBD::PropertyDescriptor<bool> hidden;
62                 PBD::PropertyDescriptor<bool> position_locked;
63                 PBD::PropertyDescriptor<framepos_t> start;
64                 PBD::PropertyDescriptor<framecnt_t> length;
65                 PBD::PropertyDescriptor<framepos_t> position;
66                 PBD::PropertyDescriptor<framecnt_t> sync_position;
67                 PBD::PropertyDescriptor<layer_t> layer;
68                 PBD::PropertyDescriptor<framepos_t> ancestral_start;
69                 PBD::PropertyDescriptor<framecnt_t> ancestral_length;
70                 PBD::PropertyDescriptor<float> stretch;
71                 PBD::PropertyDescriptor<float> shift;
72         }
73 }
74         
75 PBD::Signal2<void,boost::shared_ptr<ARDOUR::Region>,const PropertyChange&> Region::RegionPropertyChanged;
76
77 void
78 Region::make_property_quarks ()
79 {
80         Properties::muted.id = g_quark_from_static_string (X_("muted"));
81         Properties::opaque.id = g_quark_from_static_string (X_("opaque"));
82         Properties::locked.id = g_quark_from_static_string (X_("locked"));
83         Properties::automatic.id = g_quark_from_static_string (X_("automatic"));
84         Properties::whole_file.id = g_quark_from_static_string (X_("whole-file"));
85         Properties::import.id = g_quark_from_static_string (X_("import"));
86         Properties::external.id = g_quark_from_static_string (X_("external"));
87         Properties::sync_marked.id = g_quark_from_static_string (X_("sync-marked"));
88         Properties::left_of_split.id = g_quark_from_static_string (X_("left-of-split"));
89         Properties::right_of_split.id = g_quark_from_static_string (X_("right-of-split"));
90         Properties::hidden.id = g_quark_from_static_string (X_("hidden"));
91         Properties::position_locked.id = g_quark_from_static_string (X_("position-locked"));
92         Properties::start.id = g_quark_from_static_string (X_("start"));
93         Properties::length.id = g_quark_from_static_string (X_("length"));
94         Properties::position.id = g_quark_from_static_string (X_("position"));
95         Properties::sync_position.id = g_quark_from_static_string (X_("sync-position"));
96         Properties::layer.id = g_quark_from_static_string (X_("layer"));
97         Properties::ancestral_start.id = g_quark_from_static_string (X_("ancestral-start"));
98         Properties::ancestral_length.id = g_quark_from_static_string (X_("ancestral-length"));
99         Properties::stretch.id = g_quark_from_static_string (X_("stretch"));
100         Properties::shift.id = g_quark_from_static_string (X_("shift"));
101 }
102
103 void
104 Region::register_properties ()
105 {
106         _xml_node_name = X_("Region");
107
108         add_property (_muted);
109         add_property (_opaque);
110         add_property (_locked);
111         add_property (_automatic);
112         add_property (_whole_file);
113         add_property (_import);
114         add_property (_external);
115         add_property (_sync_marked);
116         add_property (_left_of_split);
117         add_property (_right_of_split);
118         add_property (_hidden);
119         add_property (_position_locked);
120         add_property (_start);
121         add_property (_length);
122         add_property (_position);
123         add_property (_sync_position);
124         add_property (_layer);
125         add_property (_ancestral_start);
126         add_property (_ancestral_length);
127         add_property (_stretch);
128         add_property (_shift);
129 }
130
131 #define REGION_DEFAULT_STATE(s,l) \
132         _muted (Properties::muted, false)            \
133         , _opaque (Properties::opaque, true) \
134         , _locked (Properties::locked, false) \
135         , _automatic (Properties::automatic, false) \
136         , _whole_file (Properties::whole_file, false) \
137         , _import (Properties::import, false) \
138         , _external (Properties::external, false) \
139         , _sync_marked (Properties::sync_marked, false) \
140         , _left_of_split (Properties::left_of_split, false) \
141         , _right_of_split (Properties::right_of_split, false) \
142         , _hidden (Properties::hidden, false) \
143         , _position_locked (Properties::position_locked, false) \
144         , _start (Properties::start, (s))       \
145         , _length (Properties::length, (l))     \
146         , _position (Properties::position, 0) \
147         , _sync_position (Properties::sync_position, (s)) \
148         , _layer (Properties::layer, 0) \
149         , _ancestral_start (Properties::ancestral_start, (s)) \
150         , _ancestral_length (Properties::ancestral_length, (l)) \
151         , _stretch (Properties::stretch, 1.0) \
152         , _shift (Properties::shift, 1.0)
153
154 #define REGION_COPY_STATE(other) \
155           _muted (other->_muted) \
156         , _opaque (other->_opaque) \
157         , _locked (other->_locked) \
158         , _automatic (other->_automatic) \
159         , _whole_file (other->_whole_file) \
160         , _import (other->_import) \
161         , _external (other->_external) \
162         , _sync_marked (other->_sync_marked) \
163         , _left_of_split (other->_left_of_split) \
164         , _right_of_split (other->_right_of_split) \
165         , _hidden (other->_hidden) \
166         , _position_locked (other->_position_locked) \
167         , _start(other->_start) \
168         , _length(other->_length) \
169         , _position(other->_position) \
170         , _sync_position(other->_sync_position) \
171         , _layer (other->_layer) \
172         , _ancestral_start (other->_ancestral_start) \
173         , _ancestral_length (other->_ancestral_length) \
174         , _stretch (other->_stretch) \
175         , _shift (other->_shift)
176
177 /* derived-from-derived constructor (no sources in constructor) */
178 Region::Region (Session& s, framepos_t start, framecnt_t length, const string& name, DataType type)
179         : SessionObject(s, name)
180         , _type(type)
181         , _no_property_changes (true)
182         , REGION_DEFAULT_STATE(start,length)
183         , _last_length (length)
184         , _last_position (0)
185         , _positional_lock_style(AudioTime)
186         , _first_edit (EditChangesNothing)
187         , _frozen(0)
188         , _read_data_count(0)
189         , _last_layer_op(0)
190         , _pending_explicit_relayer (false)
191 {
192         register_properties ();
193
194         /* no sources at this point */
195 }
196
197 /** Basic Region constructor (many sources) */
198 Region::Region (const SourceList& srcs)
199         : SessionObject(srcs.front()->session(), "toBeRenamed")
200         , _type (srcs.front()->type())
201         , _no_property_changes (true)
202         , REGION_DEFAULT_STATE(0,0)
203         , _last_length (0)
204         , _last_position (0)
205         , _positional_lock_style (_type == DataType::AUDIO ? AudioTime : MusicTime)
206         , _first_edit (EditChangesNothing)
207         , _frozen (0)
208         , _valid_transients(false)
209         , _read_data_count(0)
210         , _last_layer_op (0)
211         , _pending_explicit_relayer (false)
212 {
213         register_properties ();
214
215         _type = srcs.front()->type();
216
217         use_sources (srcs);
218
219         assert(_sources.size() > 0);
220         assert (_type == srcs.front()->type());
221 }
222
223 /** Create a new Region from part of an existing one, starting at one of two places:
224
225     if @param offset_relative is true, then the start within @param other is given by @param offset
226     (i.e. relative to the start of @param other's sources, the start is @param offset + @param other.start()
227
228     if @param offset_relative is false, then the start within the source is given @param offset.
229 */
230 Region::Region (boost::shared_ptr<const Region> other, frameoffset_t offset, bool offset_relative)
231         : SessionObject(other->session(), "toBeRenamed")
232         , _type (other->data_type())
233         , _no_property_changes (true)
234         , REGION_COPY_STATE (other)
235         , _last_length (other->_last_length)
236         , _last_position(other->_last_position) \
237         , _positional_lock_style(other->_positional_lock_style) \
238         , _first_edit (EditChangesNothing)
239         , _frozen (0)
240         , _valid_transients(false)
241         , _read_data_count(0)
242         , _last_layer_op (0)
243         , _pending_explicit_relayer (false)
244
245 {
246         register_properties ();
247
248         /* override state that may have been incorrectly inherited from the other region
249          */
250
251         _position = 0;
252         _locked = false;
253         _whole_file = false;
254         _hidden = false;
255
256         use_sources (other->_sources);
257
258         if (!offset_relative) {
259
260                 /* not sure why we do this, but its a hangover from ardour before
261                    property lists. this would be nice to remove.
262                 */
263
264                 _positional_lock_style = other->_positional_lock_style;
265                 _first_edit = other->_first_edit;
266
267                 if (offset == 0) {
268
269                         _start = 0;
270
271                         /* sync pos is relative to start of file. our start-in-file is now zero,
272                            so set our sync position to whatever the the difference between
273                            _start and _sync_pos was in the other region.
274                            
275                            result is that our new sync pos points to the same point in our source(s)
276                            as the sync in the other region did in its source(s).
277                            
278                            since we start at zero in our source(s), it is not possible to use a sync point that
279                            is before the start. reset it to _start if that was true in the other region.
280                         */
281                         
282                         if (other->sync_marked()) {
283                                 if (other->_start < other->_sync_position) {
284                                         /* sync pos was after the start point of the other region */
285                                         _sync_position = other->_sync_position - other->_start;
286                                 } else {
287                                         /* sync pos was before the start point of the other region. not possible here. */
288                                         _sync_marked = false;
289                                         _sync_position = _start;
290                                 }
291                         } else {
292                                 _sync_marked = false;
293                                 _sync_position = _start;
294                         }
295                 } else {
296                         /* XXX do something else ! */
297                         fatal << string_compose (_("programming error: %1"), X_("Region+offset constructor used with illegal combination of offset+relative"))
298                               << endmsg;
299                         /*NOTREACHED*/
300                 }
301
302         } else {
303
304                 _start = other->_start + offset;
305                 
306                 /* if the other region had a distinct sync point
307                    set, then continue to use it as best we can.
308                    otherwise, reset sync point back to start.
309                 */
310                 
311                 if (other->sync_marked()) {
312                         if (other->_sync_position < _start) {
313                                 _sync_marked = false;
314                                 _sync_position = _start;
315                 } else {
316                                 _sync_position = other->_sync_position;
317                         }
318                 } else {
319                         _sync_marked = false;
320                         _sync_position = _start;
321                 }
322         }
323
324         if (Profile->get_sae()) {
325                 /* reset sync point to start if its ended up
326                    outside region bounds.
327                 */
328
329                 if (_sync_position < _start || _sync_position >= _start + _length) {
330                         _sync_marked = false;
331                         _sync_position = _start;
332                 }
333         }
334
335         assert (_type == other->data_type());
336 }
337
338 /** Create a copy of @param other but with different sources. Used by filters */
339 Region::Region (boost::shared_ptr<const Region> other, const SourceList& srcs)
340         : SessionObject (other->session(), other->name())
341         , _type (srcs.front()->type())
342         , _no_property_changes (true)
343         , REGION_COPY_STATE (other)
344         , _last_length (other->_last_length)
345         , _last_position (other->_last_position)
346         , _positional_lock_style (other->_positional_lock_style)
347         , _first_edit (EditChangesID)
348         , _frozen (0)
349         , _valid_transients (false)
350         , _read_data_count (0)
351         , _last_layer_op (other->_last_layer_op)
352         , _pending_explicit_relayer (false)
353 {
354         register_properties ();
355
356         _locked = false;
357         _position_locked = false;
358
359         other->_first_edit = EditChangesName;
360
361         if (other->_extra_xml) {
362                 _extra_xml = new XMLNode (*other->_extra_xml);
363         } else {
364                 _extra_xml = 0;
365         }
366
367         use_sources (srcs);
368         assert(_sources.size() > 0);
369 }
370
371 /** Simple "copy" constructor */
372 Region::Region (boost::shared_ptr<const Region> other)
373         : SessionObject(other->session(), other->name())
374         , _type(other->data_type())
375         , _no_property_changes (true)
376         , REGION_COPY_STATE (other)
377         , _last_length (other->_last_length)
378         , _last_position (other->_last_position)
379         , _positional_lock_style (other->_positional_lock_style)
380         , _first_edit (EditChangesID)
381         , _frozen(0)
382         , _valid_transients(false)
383         , _read_data_count(0)
384         , _last_layer_op(other->_last_layer_op)
385         , _pending_explicit_relayer (false)
386 {
387         register_properties ();
388
389         _locked = false;
390         _position_locked = false;
391
392         other->_first_edit = EditChangesName;
393
394         if (other->_extra_xml) {
395                 _extra_xml = new XMLNode (*other->_extra_xml);
396         } else {
397                 _extra_xml = 0;
398         }
399
400         use_sources (other->_sources);
401         assert(_sources.size() > 0);
402 }
403
404 Region::~Region ()
405 {
406         DEBUG_TRACE (DEBUG::Destruction, string_compose ("Region %1 destructor @ %2\n", _name, this));
407 }
408
409 void
410 Region::set_playlist (boost::weak_ptr<Playlist> wpl)
411 {
412         _playlist = wpl.lock();
413 }
414
415 bool
416 Region::set_name (const std::string& str)
417 {
418         if (_name != str) {
419                 SessionObject::set_name(str); // EMIT SIGNAL NameChanged()
420                 assert(_name == str);
421                 send_change (Properties::name);
422         }
423
424         return true;
425 }
426
427 void
428 Region::set_length (framecnt_t len, void */*src*/)
429 {
430         //cerr << "Region::set_length() len = " << len << endl;
431         if (locked()) {
432                 return;
433         }
434
435         if (_length != len && len != 0) {
436
437                 /* check that the current _position wouldn't make the new
438                    length impossible.
439                 */
440
441                 if (max_frames - len < _position) {
442                         return;
443                 }
444
445                 if (!verify_length (len)) {
446                         return;
447                 }
448
449
450                 _last_length = _length;
451                 _length = len;
452                 _whole_file = false;
453                 first_edit ();
454                 maybe_uncopy ();
455                 invalidate_transients ();
456
457                 if (!_frozen) {
458                         recompute_at_end ();
459                 }
460
461                 send_change (Properties::length);
462         }
463 }
464
465 void
466 Region::maybe_uncopy ()
467 {
468         /* this does nothing but marked a semantic moment once upon a time */
469 }
470
471 void
472 Region::first_edit ()
473 {
474         boost::shared_ptr<Playlist> pl (playlist());
475
476         if (_first_edit != EditChangesNothing && pl) {
477
478                 _name = _session.new_region_name (_name);
479                 _first_edit = EditChangesNothing;
480
481                 send_change (Properties::name);
482                 RegionFactory::CheckNewRegion (shared_from_this());
483         }
484 }
485
486 bool
487 Region::at_natural_position () const
488 {
489         boost::shared_ptr<Playlist> pl (playlist());
490
491         if (!pl) {
492                 return false;
493         }
494
495         boost::shared_ptr<Region> whole_file_region = get_parent();
496
497         if (whole_file_region) {
498                 if (_position == whole_file_region->position() + _start) {
499                         return true;
500                 }
501         }
502
503         return false;
504 }
505
506 void
507 Region::move_to_natural_position (void *src)
508 {
509         boost::shared_ptr<Playlist> pl (playlist());
510
511         if (!pl) {
512                 return;
513         }
514
515         boost::shared_ptr<Region> whole_file_region = get_parent();
516
517         if (whole_file_region) {
518                 set_position (whole_file_region->position() + _start, src);
519         }
520 }
521
522 void
523 Region::special_set_position (framepos_t pos)
524 {
525         /* this is used when creating a whole file region as
526            a way to store its "natural" or "captured" position.
527         */
528
529         _position = _position;
530         _position = pos;
531 }
532
533 void
534 Region::set_position_lock_style (PositionLockStyle ps)
535 {
536         boost::shared_ptr<Playlist> pl (playlist());
537
538         if (!pl) {
539                 return;
540         }
541
542         _positional_lock_style = ps;
543
544         if (_positional_lock_style == MusicTime) {
545                 _session.tempo_map().bbt_time (_position, _bbt_time);
546         }
547
548 }
549
550 void
551 Region::update_position_after_tempo_map_change ()
552 {
553         boost::shared_ptr<Playlist> pl (playlist());
554
555         if (!pl || _positional_lock_style != MusicTime) {
556                 return;
557         }
558
559         TempoMap& map (_session.tempo_map());
560         framepos_t pos = map.frame_time (_bbt_time);
561         set_position_internal (pos, false);
562 }
563
564 void
565 Region::set_position (framepos_t pos, void* /*src*/)
566 {
567         if (!can_move()) {
568                 return;
569         }
570
571         set_position_internal (pos, true);
572 }
573
574 void
575 Region::set_position_internal (framepos_t pos, bool allow_bbt_recompute)
576 {
577         if (_position != pos) {
578                 _last_position = _position;
579                 _position = pos;
580
581                 /* check that the new _position wouldn't make the current
582                    length impossible - if so, change the length.
583
584                    XXX is this the right thing to do?
585                 */
586
587                 if (max_frames - _length < _position) {
588                         _last_length = _length;
589                         _length = max_frames - _position;
590                 }
591
592                 if (allow_bbt_recompute) {
593                         recompute_position_from_lock_style ();
594                 }
595
596                 invalidate_transients ();
597         }
598
599         /* do this even if the position is the same. this helps out
600            a GUI that has moved its representation already.
601         */
602
603         send_change (Properties::position);
604 }
605
606 void
607 Region::set_position_on_top (framepos_t pos, void* /*src*/)
608 {
609         if (locked()) {
610                 return;
611         }
612
613         if (_position != pos) {
614                 _last_position = _position;
615                 _position = pos;
616         }
617
618         boost::shared_ptr<Playlist> pl (playlist());
619
620         if (pl) {
621                 pl->raise_region_to_top (shared_from_this ());
622         }
623
624         /* do this even if the position is the same. this helps out
625            a GUI that has moved its representation already.
626         */
627
628         send_change (Properties::position);
629 }
630
631 void
632 Region::recompute_position_from_lock_style ()
633 {
634         if (_positional_lock_style == MusicTime) {
635                 _session.tempo_map().bbt_time (_position, _bbt_time);
636         }
637 }
638
639 void
640 Region::nudge_position (frameoffset_t n, void* /*src*/)
641 {
642         if (locked()) {
643                 return;
644         }
645
646         if (n == 0) {
647                 return;
648         }
649
650         _last_position = _position;
651
652         if (n > 0) {
653                 if (_position > max_frames - n) {
654                         _position = max_frames;
655                 } else {
656                         _position += n;
657                 }
658         } else {
659                 if (_position < -n) {
660                         _position = 0;
661                 } else {
662                         _position += n;
663                 }
664         }
665
666         send_change (Properties::position);
667 }
668
669 void
670 Region::set_ancestral_data (framepos_t s, framecnt_t l, float st, float sh)
671 {
672         _ancestral_length = l;
673         _ancestral_start = s;
674         _stretch = st;
675         _shift = sh;
676 }
677
678 void
679 Region::set_start (framepos_t pos, void* /*src*/)
680 {
681         if (locked() || position_locked()) {
682                 return;
683         }
684         /* This just sets the start, nothing else. It effectively shifts
685            the contents of the Region within the overall extent of the Source,
686            without changing the Region's position or length
687         */
688
689         if (_start != pos) {
690
691                 if (!verify_start (pos)) {
692                         return;
693                 }
694
695                 _start = pos;
696                 _whole_file = false;
697                 first_edit ();
698                 invalidate_transients ();
699
700                 send_change (Properties::start);
701         }
702 }
703
704 void
705 Region::trim_start (framepos_t new_position, void */*src*/)
706 {
707         if (locked() || position_locked()) {
708                 return;
709         }
710         framepos_t new_start;
711         frameoffset_t start_shift;
712
713         if (new_position > _position) {
714                 start_shift = new_position - _position;
715         } else {
716                 start_shift = -(_position - new_position);
717         }
718
719         if (start_shift > 0) {
720
721                 if (_start > max_frames - start_shift) {
722                         new_start = max_frames;
723                 } else {
724                         new_start = _start + start_shift;
725                 }
726
727                 if (!verify_start (new_start)) {
728                         return;
729                 }
730
731         } else if (start_shift < 0) {
732
733                 if (_start < -start_shift) {
734                         new_start = 0;
735                 } else {
736                         new_start = _start + start_shift;
737                 }
738         } else {
739                 return;
740         }
741
742         if (new_start == _start) {
743                 return;
744         }
745
746         _start = new_start;
747         _whole_file = false;
748         first_edit ();
749
750         send_change (Properties::start);
751 }
752
753 void
754 Region::trim_front (framepos_t new_position, void *src)
755 {
756         if (locked()) {
757                 return;
758         }
759
760         framepos_t end = last_frame();
761         framepos_t source_zero;
762
763         if (_position > _start) {
764                 source_zero = _position - _start;
765         } else {
766                 source_zero = 0; // its actually negative, but this will work for us
767         }
768
769         if (new_position < end) { /* can't trim it zero or negative length */
770
771                 framecnt_t newlen;
772
773                 /* can't trim it back passed where source position zero is located */
774
775                 new_position = max (new_position, source_zero);
776
777
778                 if (new_position > _position) {
779                         newlen = _length - (new_position - _position);
780                 } else {
781                         newlen = _length + (_position - new_position);
782                 }
783
784                 trim_to_internal (new_position, newlen, src);
785                 if (!_frozen) {
786                         recompute_at_start ();
787                 }
788         }
789 }
790
791 /** @param new_endpoint New region end point, such that, for example,
792  *  a region at 0 of length 10 has an endpoint of 9.
793  */
794
795 void
796 Region::trim_end (framepos_t new_endpoint, void */*src*/)
797 {
798         if (locked()) {
799                 return;
800         }
801
802         if (new_endpoint > _position) {
803                 trim_to_internal (_position, new_endpoint - _position + 1, this);
804                 if (!_frozen) {
805                         recompute_at_end ();
806                 }
807         }
808 }
809
810 void
811 Region::trim_to (framepos_t position, framecnt_t length, void *src)
812 {
813         if (locked()) {
814                 return;
815         }
816
817         trim_to_internal (position, length, src);
818
819         if (!_frozen) {
820                 recompute_at_start ();
821                 recompute_at_end ();
822         }
823 }
824
825 void
826 Region::trim_to_internal (framepos_t position, framecnt_t length, void */*src*/)
827 {
828         frameoffset_t start_shift;
829         framepos_t new_start;
830
831         if (locked()) {
832                 return;
833         }
834
835         if (position > _position) {
836                 start_shift = position - _position;
837         } else {
838                 start_shift = -(_position - position);
839         }
840
841         if (start_shift > 0) {
842
843                 if (_start > max_frames - start_shift) {
844                         new_start = max_frames;
845                 } else {
846                         new_start = _start + start_shift;
847                 }
848
849
850         } else if (start_shift < 0) {
851
852                 if (_start < -start_shift) {
853                         new_start = 0;
854                 } else {
855                         new_start = _start + start_shift;
856                 }
857         } else {
858                 new_start = _start;
859         }
860
861         if (!verify_start_and_length (new_start, length)) {
862                 return;
863         }
864
865         PropertyChange what_changed;
866
867         if (_start != new_start) {
868                 _start = new_start;
869                 what_changed.add (Properties::start);
870         }
871         if (_length != length) {
872                 if (!_frozen) {
873                         _last_length = _length;
874                 }
875                 _length = length;
876                 what_changed.add (Properties::length);
877         }
878         if (_position != position) {
879                 if (!_frozen) {
880                         _last_position = _position;
881                 }
882                 _position = position;
883                 what_changed.add (Properties::position);
884         }
885
886         _whole_file = false;
887
888         PropertyChange start_and_length;
889
890         start_and_length.add (Properties::start);
891         start_and_length.add (Properties::length);
892
893         if (what_changed.contains (start_and_length)) {
894                 first_edit ();
895         }
896
897         if (!what_changed.empty()) {
898                 send_change (what_changed);
899         }
900 }
901
902 void
903 Region::set_hidden (bool yn)
904 {
905         if (hidden() != yn) {
906                 _hidden = yn;
907                 send_change (Properties::hidden);
908         }
909 }
910
911 void
912 Region::set_whole_file (bool yn)
913 {
914         _whole_file = yn;
915         /* no change signal */
916 }
917
918 void
919 Region::set_automatic (bool yn)
920 {
921         _automatic = yn;
922         /* no change signal */
923 }
924
925 void
926 Region::set_muted (bool yn)
927 {
928         if (muted() != yn) {
929                 _muted = yn;
930                 send_change (Properties::muted);
931         }
932 }
933
934 void
935 Region::set_opaque (bool yn)
936 {
937         if (opaque() != yn) {
938                 _opaque = yn;
939                 send_change (Properties::opaque);
940         }
941 }
942
943 void
944 Region::set_locked (bool yn)
945 {
946         if (locked() != yn) {
947                 _locked = yn;
948                 send_change (Properties::locked);
949         }
950 }
951
952 void
953 Region::set_position_locked (bool yn)
954 {
955         if (position_locked() != yn) {
956                 _position_locked = yn;
957                 send_change (Properties::locked);
958         }
959 }
960
961 void
962 Region::set_sync_position (framepos_t absolute_pos)
963 {
964         framepos_t const file_pos = _start + (absolute_pos - _position);
965
966         if (file_pos != _sync_position) {
967                 _sync_marked = true;
968                 _sync_position = file_pos;
969                 if (!_frozen) {
970                         maybe_uncopy ();
971                 }
972                 send_change (Properties::sync_position);
973         }
974 }
975
976 void
977 Region::clear_sync_position ()
978 {
979         if (sync_marked()) {
980                 _sync_marked = false;
981                 if (!_frozen) {
982                         maybe_uncopy ();
983                 }
984                 send_change (Properties::sync_position);
985         }
986 }
987
988 framepos_t
989 Region::sync_offset (int& dir) const
990 {
991         /* returns the sync point relative the first frame of the region */
992
993         if (sync_marked()) {
994                 if (_sync_position > _start) {
995                         dir = 1;
996                         return _sync_position - _start;
997                 } else {
998                         dir = -1;
999                         return _start - _sync_position;
1000                 }
1001         } else {
1002                 dir = 0;
1003                 return 0;
1004         }
1005 }
1006
1007 framepos_t
1008 Region::adjust_to_sync (framepos_t pos) const
1009 {
1010         int sync_dir;
1011         frameoffset_t offset = sync_offset (sync_dir);
1012
1013         // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
1014
1015         if (sync_dir > 0) {
1016                 if (pos > offset) {
1017                         pos -= offset;
1018                 } else {
1019                         pos = 0;
1020                 }
1021         } else {
1022                 if (max_frames - pos > offset) {
1023                         pos += offset;
1024                 }
1025         }
1026
1027         return pos;
1028 }
1029
1030 framepos_t
1031 Region::sync_position() const
1032 {
1033         if (sync_marked()) {
1034                 return _sync_position;
1035         } else {
1036                 return _start;
1037         }
1038 }
1039
1040 void
1041 Region::raise ()
1042 {
1043         boost::shared_ptr<Playlist> pl (playlist());
1044         if (pl) {
1045                 pl->raise_region (shared_from_this ());
1046         }
1047 }
1048
1049 void
1050 Region::lower ()
1051 {
1052         boost::shared_ptr<Playlist> pl (playlist());
1053         if (pl) {
1054                 pl->lower_region (shared_from_this ());
1055         }
1056 }
1057
1058
1059 void
1060 Region::raise_to_top ()
1061 {
1062         boost::shared_ptr<Playlist> pl (playlist());
1063         if (pl) {
1064                 pl->raise_region_to_top (shared_from_this());
1065         }
1066 }
1067
1068 void
1069 Region::lower_to_bottom ()
1070 {
1071         boost::shared_ptr<Playlist> pl (playlist());
1072         if (pl) {
1073                 pl->lower_region_to_bottom (shared_from_this());
1074         }
1075 }
1076
1077 void
1078 Region::set_layer (layer_t l)
1079 {
1080         if (_layer != l) {
1081                 _layer = l;
1082
1083                 send_change (Properties::layer);
1084         }
1085 }
1086
1087 XMLNode&
1088 Region::state (bool /*full_state*/)
1089 {
1090         XMLNode *node = new XMLNode ("Region");
1091         char buf[64];
1092         const char* fe = NULL;
1093
1094         add_properties (*node);
1095
1096         _id.print (buf, sizeof (buf));
1097         node->add_property ("id", buf);
1098         node->add_property ("type", _type.to_string());
1099
1100         switch (_first_edit) {
1101         case EditChangesNothing:
1102                 fe = X_("nothing");
1103                 break;
1104         case EditChangesName:
1105                 fe = X_("name");
1106                 break;
1107         case EditChangesID:
1108                 fe = X_("id");
1109                 break;
1110         default: /* should be unreachable but makes g++ happy */
1111                 fe = X_("nothing");
1112                 break;
1113         }
1114
1115         node->add_property ("first-edit", fe);
1116
1117         /* note: flags are stored by derived classes */
1118
1119         if (_positional_lock_style != AudioTime) {
1120                 node->add_property ("positional-lock-style", enum_2_string (_positional_lock_style));
1121                 stringstream str;
1122                 str << _bbt_time;
1123                 node->add_property ("bbt-position", str.str());
1124         }
1125
1126         return *node;
1127 }
1128
1129 XMLNode&
1130 Region::get_state ()
1131 {
1132         return state (true);
1133 }
1134
1135 int
1136 Region::set_state (const XMLNode& node, int version)
1137 {
1138         PropertyChange what_changed;
1139         return _set_state (node, version, what_changed, true);
1140 }
1141
1142 int
1143 Region::_set_state (const XMLNode& node, int version, PropertyChange& what_changed, bool send)
1144 {
1145         const XMLProperty* prop;
1146
1147         what_changed = set_properties (node);
1148
1149         if ((prop = node.property (X_("id")))) {
1150                 _id = prop->value();
1151         }
1152
1153         if ((prop = node.property ("positional-lock-style")) != 0) {
1154                 _positional_lock_style = PositionLockStyle (string_2_enum (prop->value(), _positional_lock_style));
1155
1156                 if (_positional_lock_style == MusicTime) {
1157                         if ((prop = node.property ("bbt-position")) == 0) {
1158                                 /* missing BBT info, revert to audio time locking */
1159                                 _positional_lock_style = AudioTime;
1160                         } else {
1161                                 if (sscanf (prop->value().c_str(), "%d|%d|%d",
1162                                             &_bbt_time.bars,
1163                                             &_bbt_time.beats,
1164                                             &_bbt_time.ticks) != 3) {
1165                                         _positional_lock_style = AudioTime;
1166                                 }
1167                         }
1168                 }
1169
1170         }
1171
1172         /* fix problems with old sessions corrupted by impossible
1173            values for _stretch or _shift
1174         */
1175         if (_stretch == 0.0f) {
1176                 _stretch = 1.0f;
1177         }
1178         
1179         if (_shift == 0.0f) {
1180                 _shift = 1.0f;
1181         }
1182
1183         const XMLNodeList& nlist = node.children();
1184
1185         for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
1186
1187                 XMLNode *child;
1188
1189                 child = (*niter);
1190
1191                 if (child->name () == "Extra") {
1192                         delete _extra_xml;
1193                         _extra_xml = new XMLNode (*child);
1194                         break;
1195                 }
1196         }
1197
1198         if (send) {
1199                 cerr << _name << ": final change to be sent: ";
1200                 for (PropertyChange::iterator i = what_changed.begin(); i != what_changed.end(); ++i) {
1201                         cerr << g_quark_to_string ((GQuark) *i) << ' ';
1202                 }
1203                 cerr << endl;
1204                 send_change (what_changed);
1205         }
1206
1207         return 0;
1208 }
1209
1210 void
1211 Region::freeze ()
1212 {
1213         _frozen++;
1214         _last_length = _length;
1215         _last_position = _position;
1216 }
1217
1218 void
1219 Region::thaw ()
1220 {
1221         PropertyChange what_changed;
1222
1223         {
1224                 Glib::Mutex::Lock lm (_lock);
1225
1226                 if (_frozen && --_frozen > 0) {
1227                         return;
1228                 }
1229
1230                 if (!_pending_changed.empty()) {
1231                         what_changed = _pending_changed;
1232                         _pending_changed.clear ();
1233                 }
1234         }
1235
1236         if (what_changed.empty()) {
1237                 return;
1238         }
1239
1240         if (what_changed.contains (Properties::length)) {
1241                 if (what_changed.contains (Properties::position)) {
1242                         recompute_at_start ();
1243                 }
1244                 recompute_at_end ();
1245         }
1246
1247         send_change (what_changed);
1248 }
1249
1250 void
1251 Region::send_change (const PropertyChange& what_changed)
1252 {
1253         if (what_changed.empty()) {
1254                 return;
1255         }
1256
1257         {
1258                 Glib::Mutex::Lock lm (_lock);
1259                 if (_frozen) {
1260                         _pending_changed.add (what_changed);
1261                         return;
1262                 }
1263         }
1264
1265         PropertyChanged (what_changed);
1266
1267         if (!_no_property_changes) {
1268                 
1269                 /* Try and send a shared_pointer unless this is part of the constructor.
1270                    If so, do nothing.
1271                 */
1272
1273                 try {
1274                         boost::shared_ptr<Region> rptr = shared_from_this();
1275                         RegionPropertyChanged (rptr, what_changed);
1276
1277                 } catch (...) {
1278                         /* no shared_ptr available, relax; */
1279                 }
1280         }
1281
1282 }
1283
1284 void
1285 Region::set_last_layer_op (uint64_t when)
1286 {
1287         _last_layer_op = when;
1288 }
1289
1290 bool
1291 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1292 {
1293         return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
1294 }
1295
1296 bool
1297 Region::equivalent (boost::shared_ptr<const Region> other) const
1298 {
1299         return _start == other->_start &&
1300                 _position == other->_position &&
1301                 _length == other->_length;
1302 }
1303
1304 bool
1305 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1306 {
1307         return _start == other->_start &&
1308                 _length == other->_length;
1309 }
1310
1311 bool
1312 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1313 {
1314         return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1315 }
1316
1317 void
1318 Region::source_deleted (boost::weak_ptr<Source>)
1319 {
1320         _sources.clear ();
1321
1322         if (!_session.deletion_in_progress()) {
1323                 /* this is a very special case: at least one of the region's
1324                    sources has bee deleted, so invalidate all references to
1325                    ourselves. Do NOT do this during session deletion, because
1326                    then we run the risk that this will actually result
1327                    in this object being deleted (as refcnt goes to zero)
1328                    while emitting DropReferences.
1329                 */
1330
1331                 drop_references ();
1332         }
1333 }
1334
1335 vector<string>
1336 Region::master_source_names ()
1337 {
1338         SourceList::iterator i;
1339
1340         vector<string> names;
1341         for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1342                 names.push_back((*i)->name());
1343         }
1344
1345         return names;
1346 }
1347
1348 void
1349 Region::set_master_sources (const SourceList& srcs)
1350 {
1351         _master_sources = srcs;
1352         assert (_sources.size() == _master_sources.size());
1353 }
1354
1355 bool
1356 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1357 {
1358         if (!other)
1359                 return false;
1360
1361         SourceList::const_iterator i;
1362         SourceList::const_iterator io;
1363
1364         for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1365                 if ((*i)->id() != (*io)->id()) {
1366                         return false;
1367                 }
1368         }
1369
1370         for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1371                 if ((*i)->id() != (*io)->id()) {
1372                         return false;
1373                 }
1374         }
1375
1376         return true;
1377 }
1378
1379 bool
1380 Region::uses_source (boost::shared_ptr<const Source> source) const
1381 {
1382         for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1383                 if (*i == source) {
1384                         return true;
1385                 }
1386         }
1387         return false;
1388 }
1389
1390 sframes_t
1391 Region::source_length(uint32_t n) const
1392 {
1393         return _sources[n]->length(_position - _start);
1394 }
1395
1396 bool
1397 Region::verify_length (framecnt_t len)
1398 {
1399         if (source() && (source()->destructive() || source()->length_mutable())) {
1400                 return true;
1401         }
1402
1403         framecnt_t maxlen = 0;
1404
1405         for (uint32_t n=0; n < _sources.size(); ++n) {
1406                 maxlen = max (maxlen, source_length(n) - _start);
1407         }
1408
1409         len = min (len, maxlen);
1410
1411         return true;
1412 }
1413
1414 bool
1415 Region::verify_start_and_length (framepos_t new_start, framecnt_t& new_length)
1416 {
1417         if (source() && (source()->destructive() || source()->length_mutable())) {
1418                 return true;
1419         }
1420
1421         framecnt_t maxlen = 0;
1422
1423         for (uint32_t n=0; n < _sources.size(); ++n) {
1424                 maxlen = max (maxlen, source_length(n) - new_start);
1425         }
1426
1427         new_length = min (new_length, maxlen);
1428
1429         return true;
1430 }
1431
1432 bool
1433 Region::verify_start (framepos_t pos)
1434 {
1435         if (source() && (source()->destructive() || source()->length_mutable())) {
1436                 return true;
1437         }
1438
1439         for (uint32_t n=0; n < _sources.size(); ++n) {
1440                 if (pos > source_length(n) - _length) {
1441                         return false;
1442                 }
1443         }
1444         return true;
1445 }
1446
1447 bool
1448 Region::verify_start_mutable (framepos_t& new_start)
1449 {
1450         if (source() && (source()->destructive() || source()->length_mutable())) {
1451                 return true;
1452         }
1453
1454         for (uint32_t n=0; n < _sources.size(); ++n) {
1455                 if (new_start > source_length(n) - _length) {
1456                         new_start = source_length(n) - _length;
1457                 }
1458         }
1459         return true;
1460 }
1461
1462 boost::shared_ptr<Region>
1463 Region::get_parent() const
1464 {
1465         boost::shared_ptr<Playlist> pl (playlist());
1466
1467         if (pl) {
1468                 boost::shared_ptr<Region> r;
1469                 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1470
1471                 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1472                         return boost::static_pointer_cast<Region> (r);
1473                 }
1474         }
1475
1476         return boost::shared_ptr<Region>();
1477 }
1478
1479 int
1480 Region::apply (Filter& filter)
1481 {
1482         return filter.run (shared_from_this());
1483 }
1484
1485
1486 void
1487 Region::invalidate_transients ()
1488 {
1489         _valid_transients = false;
1490         _transients.clear ();
1491 }
1492
1493
1494 void
1495 Region::use_sources (SourceList const & s)
1496 {
1497         set<boost::shared_ptr<Source> > unique_srcs;
1498
1499         for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1500                 _sources.push_back (*i);
1501                 (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1502                 unique_srcs.insert (*i);
1503         }
1504
1505         for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1506                 _master_sources.push_back (*i);
1507                 if (unique_srcs.find (*i) == unique_srcs.end()) {
1508                         (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1509                 }
1510         }
1511 }
1512
1513
1514 bool
1515 Region::set_property (const PropertyBase& prop)
1516 {
1517         DEBUG_TRACE (DEBUG::Properties,  string_compose ("region %1 set property %2\n", _name.val(), prop.property_name()));
1518
1519         if (prop == Properties::muted.id) {
1520                 bool val = dynamic_cast<const PropertyTemplate<bool>*>(&prop)->val();
1521                 if (val != _muted) {
1522                         DEBUG_TRACE (DEBUG::Properties, string_compose ("region %1 muted changed from %2 to %3",
1523                                                                         _name.val(), _muted.val(), val));
1524                         _muted = val;
1525                         return true;
1526                 }
1527         } else if (prop == Properties::opaque.id) {
1528                 bool val = dynamic_cast<const PropertyTemplate<bool>*>(&prop)->val();
1529                 if (val != _opaque) {
1530                         DEBUG_TRACE (DEBUG::Properties, string_compose ("region %1 opaque changed from %2 to %3",
1531                                                                         _name.val(), _opaque.val(), val));
1532                         _opaque = val;
1533                         return true;
1534                 }
1535         } else if (prop == Properties::locked.id) {
1536                 bool val = dynamic_cast<const PropertyTemplate<bool>*>(&prop)->val();
1537                 if (val != _locked) {
1538                         DEBUG_TRACE (DEBUG::Properties, string_compose ("region %1 locked changed from %2 to %3",
1539                                                                         _name.val(), _locked.val(), val));
1540                         _locked = val;
1541                         return true;
1542                 }
1543         } else if (prop == Properties::automatic.id) {
1544                 _automatic = dynamic_cast<const PropertyTemplate<bool>*>(&prop)->val();
1545         } else if (prop == Properties::whole_file.id) {
1546                 _whole_file = dynamic_cast<const PropertyTemplate<bool>*>(&prop)->val();
1547         } else if (prop == Properties::import.id) {
1548                 _import = dynamic_cast<const PropertyTemplate<bool>*>(&prop)->val();
1549         } else if (prop == Properties::external.id) {
1550                 _external = dynamic_cast<const PropertyTemplate<bool>*>(&prop)->val();
1551         } else if (prop == Properties::sync_marked.id) {
1552                 _sync_marked = dynamic_cast<const PropertyTemplate<bool>*>(&prop)->val();
1553         } else if (prop == Properties::left_of_split.id) {
1554                 _left_of_split = dynamic_cast<const PropertyTemplate<bool>*>(&prop)->val();
1555         } else if (prop == Properties::right_of_split.id) {
1556                 _right_of_split = dynamic_cast<const PropertyTemplate<bool>*>(&prop)->val();
1557         } else if (prop == Properties::hidden.id) {
1558                 bool val = dynamic_cast<const PropertyTemplate<bool>*>(&prop)->val();
1559                 if (val != _hidden) {
1560                         _hidden = val;
1561                         return true;
1562                 }
1563         } else if (prop == Properties::position_locked.id) {
1564                 _position_locked = dynamic_cast<const PropertyTemplate<bool>*>(&prop)->val();
1565         } else if (prop == Properties::start.id) {
1566                 _start = dynamic_cast<const PropertyTemplate<framepos_t>*>(&prop)->val();
1567         } else if (prop == Properties::length.id) {
1568                 framecnt_t val = dynamic_cast<const PropertyTemplate<framecnt_t>* > (&prop)->val();
1569                 if (val != _length) {
1570                         _length = val;
1571                         return true;
1572                 }
1573         } else if (prop == Properties::position.id) {
1574                 framepos_t val = dynamic_cast<const PropertyTemplate<framepos_t>*>(&prop)->val();
1575                 if (val != _position) {
1576                         _position = val;
1577                         return true;
1578                 }
1579         } else if (prop == Properties::sync_position.id) {
1580                 framepos_t val = dynamic_cast<const PropertyTemplate<framepos_t>*>(&prop)->val();
1581                 if (val != _sync_position) {
1582                         _sync_position = val;
1583                         return true;
1584                 }
1585         } else if (prop == Properties::layer.id) {
1586                 layer_t val = dynamic_cast<const PropertyTemplate<layer_t>*>(&prop)->val();
1587                 if (val != _layer) {
1588                         _layer = val;
1589                         return true;
1590                 }
1591         } else if (prop == Properties::ancestral_start.id) {
1592                 _ancestral_start = dynamic_cast<const PropertyTemplate<framepos_t>*>(&prop)->val();
1593         } else if (prop == Properties::ancestral_length.id) {
1594                 _ancestral_length = dynamic_cast<const PropertyTemplate<framecnt_t>*>(&prop)->val();
1595         } else if (prop == Properties::stretch.id) {
1596                 _stretch = dynamic_cast<const PropertyTemplate<float>*>(&prop)->val();
1597         } else if (prop == Properties::shift.id) {
1598                 _shift = dynamic_cast<const PropertyTemplate<float>*>(&prop)->val();
1599         } else {
1600                 return SessionObject::set_property (prop);
1601         }
1602         
1603         return false;
1604 }