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