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