remove almost everything from region editor, make popup menu more useful and accurate...
[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     $Id$
19 */
20
21 #include <iostream>
22 #include <cmath>
23 #include <climits>
24 #include <algorithm>
25
26 #include <sigc++/bind.h>
27 #include <sigc++/class_slot.h>
28
29 #include <glibmm/thread.h>
30 #include <pbd/xml++.h>
31 #include <pbd/stacktrace.h>
32
33 #include <ardour/region.h>
34 #include <ardour/playlist.h>
35 #include <ardour/session.h>
36 #include <ardour/region_factory.h>
37
38 #include "i18n.h"
39
40 using namespace std;
41 using namespace ARDOUR;
42 using namespace PBD;
43
44 Change Region::FadeChanged       = ARDOUR::new_change ();
45 Change Region::SyncOffsetChanged = ARDOUR::new_change ();
46 Change Region::MuteChanged       = ARDOUR::new_change ();
47 Change Region::OpacityChanged    = ARDOUR::new_change ();
48 Change Region::LockChanged       = ARDOUR::new_change ();
49 Change Region::LayerChanged      = ARDOUR::new_change ();
50 Change Region::HiddenChanged     = ARDOUR::new_change ();
51
52 Region::Region (nframes_t start, nframes_t length, const string& name, layer_t layer, Region::Flag flags)
53 {
54         /* basic Region constructor */
55
56         _flags = flags;
57         _playlist = 0;
58         _read_data_count = 0;
59         _frozen = 0;
60         pending_changed = Change (0);
61
62         _name = name;
63         _start = start; 
64         _sync_position = _start;
65         _length = length; 
66         _position = 0; 
67         _layer = layer;
68         _read_data_count = 0;
69         _first_edit = EditChangesNothing;
70         _last_layer_op = 0;
71 }
72
73 Region::Region (boost::shared_ptr<const Region> other, nframes_t offset, nframes_t length, const string& name, layer_t layer, Flag flags)
74 {
75         /* create a new Region from part of an existing one */
76
77         _frozen = 0;
78         pending_changed = Change (0);
79         _playlist = 0;
80         _read_data_count = 0;
81
82         _start = other->_start + offset; 
83         if (other->_sync_position < offset) {
84                 _sync_position = other->_sync_position;
85         } else {
86                 _sync_position = _start;
87         }
88         _length = length; 
89         _name = name;
90         _position = 0; 
91         _layer = layer; 
92         _flags = Flag (flags & ~(Locked|WholeFile|Hidden));
93         _first_edit = EditChangesNothing;
94         _last_layer_op = 0;
95 }
96
97 Region::Region (boost::shared_ptr<const Region> other)
98 {
99         /* Pure copy constructor */
100
101         _frozen = 0;
102         pending_changed = Change (0);
103         _playlist = 0;
104         _read_data_count = 0;
105
106         _first_edit = EditChangesID;
107         other->_first_edit = EditChangesName;
108
109         if (other->_extra_xml) {
110                 _extra_xml = new XMLNode (*other->_extra_xml);
111         } else {
112                 _extra_xml = 0;
113         }
114
115         _start = other->_start;
116         _sync_position = other->_sync_position;
117         _length = other->_length; 
118         _name = other->_name;
119         _position = other->_position; 
120         _layer = other->_layer; 
121         _flags = Flag (other->_flags & ~Locked);
122         _last_layer_op = other->_last_layer_op;
123 }
124
125 Region::Region (const XMLNode& node)
126 {
127         _frozen = 0;
128         pending_changed = Change (0);
129         _playlist = 0;
130         _read_data_count = 0;
131         _start = 0; 
132         _sync_position = _start;
133         _length = 0;
134         _name = X_("error: XML did not reset this");
135         _position = 0; 
136         _layer = 0;
137         _flags = Flag (0);
138         _first_edit = EditChangesNothing;
139
140         if (set_state (node)) {
141                 throw failed_constructor();
142         }
143 }
144
145 Region::~Region ()
146 {
147         /* derived classes must call notify_callbacks() and then emit GoingAway */
148 }
149
150 void
151 Region::set_playlist (Playlist* pl)
152 {
153         _playlist = pl;
154 }
155
156 void
157 Region::set_name (string str)
158 {
159         if (_name != str) {
160                 _name = str; 
161                 send_change (NameChanged);
162         }
163 }
164
165 void
166 Region::set_length (nframes_t len, void *src)
167 {
168         if (_flags & Locked) {
169                 return;
170         }
171
172         if (_length != len && len != 0) {
173
174                 /* check that the current _position wouldn't make the new 
175                    length impossible.
176                 */
177
178                 if (max_frames - len < _position) {
179                         return;
180                 }
181
182                 if (!verify_length (len)) {
183                         return;
184                 }
185                 
186                 _length = len;
187
188                 _flags = Region::Flag (_flags & ~WholeFile);
189
190                 first_edit ();
191                 maybe_uncopy ();
192
193                 if (!_frozen) {
194                         recompute_at_end ();
195                 }
196
197                 send_change (LengthChanged);
198         }
199 }
200
201 void
202 Region::maybe_uncopy ()
203 {
204 }
205
206 void
207 Region::first_edit ()
208 {
209         if (_first_edit != EditChangesNothing && _playlist) {
210
211                 _name = _playlist->session().new_region_name (_name);
212                 _first_edit = EditChangesNothing;
213
214                 send_change (NameChanged);
215                 RegionFactory::CheckNewRegion (shared_from_this());
216         }
217 }
218
219 bool
220 Region::at_natural_position () const
221 {
222         if (!_playlist) {
223                 return false;
224         }
225         
226         boost::shared_ptr<Region> whole_file_region = get_parent();
227
228         if (whole_file_region) {
229                 if (_position == whole_file_region->position() + _start) {
230                         return true;
231                 }
232         }
233
234         return false;
235 }
236
237 void
238 Region::move_to_natural_position (void *src)
239 {
240         if (!_playlist) {
241                 return;
242         }
243         
244         boost::shared_ptr<Region> whole_file_region = get_parent();
245
246         if (whole_file_region) {
247                 set_position (whole_file_region->position() + _start, src);
248         }
249 }
250         
251 void
252 Region::special_set_position (nframes_t pos)
253 {
254         /* this is used when creating a whole file region as 
255            a way to store its "natural" or "captured" position.
256         */
257
258         _position = pos;
259 }
260
261 void
262 Region::set_position (nframes_t pos, void *src)
263 {
264         if (_flags & Locked) {
265                 return;
266         }
267
268         if (_position != pos) {
269                 _position = pos;
270
271                 /* check that the new _position wouldn't make the current
272                    length impossible - if so, change the length. 
273
274                    XXX is this the right thing to do?
275                 */
276
277                 if (max_frames - _length < _position) {
278                         _length = max_frames - _position;
279                 }
280         }
281
282         /* do this even if the position is the same. this helps out
283            a GUI that has moved its representation already.
284         */
285
286         send_change (PositionChanged);
287 }
288
289 void
290 Region::set_position_on_top (nframes_t pos, void *src)
291 {
292         if (_flags & Locked) {
293                 return;
294         }
295
296         if (_position != pos) {
297                 _position = pos;
298         }
299
300         _playlist->raise_region_to_top (shared_from_this ());
301
302         /* do this even if the position is the same. this helps out
303            a GUI that has moved its representation already.
304         */
305         
306         send_change (PositionChanged);
307 }
308
309 void
310 Region::nudge_position (long n, void *src)
311 {
312         if (_flags & Locked) {
313                 return;
314         }
315
316         if (n == 0) {
317                 return;
318         }
319         
320         if (n > 0) {
321                 if (_position > max_frames - n) {
322                         _position = max_frames;
323                 } else {
324                         _position += n;
325                 }
326         } else {
327                 if (_position < (nframes_t) -n) {
328                         _position = 0;
329                 } else {
330                         _position += n;
331                 }
332         }
333
334         send_change (PositionChanged);
335 }
336
337 void
338 Region::set_start (nframes_t pos, void *src)
339 {
340         if (_flags & Locked) {
341                 return;
342         }
343         /* This just sets the start, nothing else. It effectively shifts
344            the contents of the Region within the overall extent of the Source,
345            without changing the Region's position or length
346         */
347
348         if (_start != pos) {
349
350                 if (!verify_start (pos)) {
351                         return;
352                 }
353
354                 _start = pos;
355                 _flags = Region::Flag (_flags & ~WholeFile);
356                 first_edit ();
357
358                 send_change (StartChanged);
359         }
360 }
361
362 void
363 Region::trim_start (nframes_t new_position, void *src)
364 {
365         if (_flags & Locked) {
366                 return;
367         }
368         nframes_t new_start;
369         int32_t start_shift;
370         
371         if (new_position > _position) {
372                 start_shift = new_position - _position;
373         } else {
374                 start_shift = -(_position - new_position);
375         }
376
377         if (start_shift > 0) {
378
379                 if (_start > max_frames - start_shift) {
380                         new_start = max_frames;
381                 } else {
382                         new_start = _start + start_shift;
383                 }
384
385                 if (!verify_start (new_start)) {
386                         return;
387                 }
388
389         } else if (start_shift < 0) {
390
391                 if (_start < (nframes_t) -start_shift) {
392                         new_start = 0;
393                 } else {
394                         new_start = _start + start_shift;
395                 }
396         } else {
397                 return;
398         }
399
400         if (new_start == _start) {
401                 return;
402         }
403         
404         _start = new_start;
405         _flags = Region::Flag (_flags & ~WholeFile);
406         first_edit ();
407
408         send_change (StartChanged);
409 }
410
411 void
412 Region::trim_front (nframes_t new_position, void *src)
413 {
414         if (_flags & Locked) {
415                 return;
416         }
417
418         nframes_t end = last_frame();
419         nframes_t source_zero;
420
421         if (_position > _start) {
422                 source_zero = _position - _start;
423         } else {
424                 source_zero = 0; // its actually negative, but this will work for us
425         }
426
427         if (new_position < end) { /* can't trim it zero or negative length */
428                 
429                 nframes_t newlen;
430
431                 /* can't trim it back passed where source position zero is located */
432                 
433                 new_position = max (new_position, source_zero);
434                 
435                 
436                 if (new_position > _position) {
437                         newlen = _length - (new_position - _position);
438                 } else {
439                         newlen = _length + (_position - new_position);
440                 }
441                 
442                 trim_to_internal (new_position, newlen, src);
443                 if (!_frozen) {
444                         recompute_at_start ();
445                 }
446         }
447 }
448
449 void
450 Region::trim_end (nframes_t new_endpoint, void *src)
451 {
452         if (_flags & Locked) {
453                 return;
454         }
455
456         if (new_endpoint > _position) {
457                 trim_to_internal (_position, new_endpoint - _position, this);
458                 if (!_frozen) {
459                         recompute_at_end ();
460                 }
461         }
462 }
463
464 void
465 Region::trim_to (nframes_t position, nframes_t length, void *src)
466 {
467         if (_flags & Locked) {
468                 return;
469         }
470
471         trim_to_internal (position, length, src);
472
473         if (!_frozen) {
474                 recompute_at_start ();
475                 recompute_at_end ();
476         }
477 }
478
479 void
480 Region::trim_to_internal (nframes_t position, nframes_t length, void *src)
481 {
482         int32_t start_shift;
483         nframes_t new_start;
484
485         if (_flags & Locked) {
486                 return;
487         }
488
489         if (position > _position) {
490                 start_shift = position - _position;
491         } else {
492                 start_shift = -(_position - position);
493         }
494
495         if (start_shift > 0) {
496
497                 if (_start > max_frames - start_shift) {
498                         new_start = max_frames;
499                 } else {
500                         new_start = _start + start_shift;
501                 }
502
503
504         } else if (start_shift < 0) {
505
506                 if (_start < (nframes_t) -start_shift) {
507                         new_start = 0;
508                 } else {
509                         new_start = _start + start_shift;
510                 }
511         } else {
512                 new_start = _start;
513         }
514
515         if (!verify_start_and_length (new_start, length)) {
516                 return;
517         }
518
519         Change what_changed = Change (0);
520
521         if (_start != new_start) {
522                 _start = new_start;
523                 what_changed = Change (what_changed|StartChanged);
524         }
525         if (_length != length) {
526                 _length = length;
527                 what_changed = Change (what_changed|LengthChanged);
528         }
529         if (_position != position) {
530                 _position = position;
531                 what_changed = Change (what_changed|PositionChanged);
532         }
533         
534         _flags = Region::Flag (_flags & ~WholeFile);
535
536         if (what_changed & (StartChanged|LengthChanged)) {
537                 first_edit ();
538         } 
539
540         if (what_changed) {
541                 send_change (what_changed);
542         }
543 }       
544
545 void
546 Region::set_hidden (bool yn)
547 {
548         if (hidden() != yn) {
549
550                 if (yn) {
551                         _flags = Flag (_flags|Hidden);
552                 } else {
553                         _flags = Flag (_flags & ~Hidden);
554                 }
555
556                 send_change (HiddenChanged);
557         }
558 }
559
560 void
561 Region::set_muted (bool yn)
562 {
563         if (muted() != yn) {
564
565                 if (yn) {
566                         _flags = Flag (_flags|Muted);
567                 } else {
568                         _flags = Flag (_flags & ~Muted);
569                 }
570
571                 send_change (MuteChanged);
572         }
573 }
574
575 void
576 Region::set_opaque (bool yn)
577 {
578         if (opaque() != yn) {
579                 if (yn) {
580                         _flags = Flag (_flags|Opaque);
581                 } else {
582                         _flags = Flag (_flags & ~Opaque);
583                 }
584                 send_change (OpacityChanged);
585         }
586 }
587
588 void
589 Region::set_locked (bool yn)
590 {
591         if (locked() != yn) {
592                 if (yn) {
593                         _flags = Flag (_flags|Locked);
594                 } else {
595                         _flags = Flag (_flags & ~Locked);
596                 }
597                 send_change (LockChanged);
598         }
599 }
600
601 void
602 Region::set_sync_position (nframes_t absolute_pos)
603 {
604         nframes_t file_pos;
605
606         file_pos = _start + (absolute_pos - _position);
607
608         if (file_pos != _sync_position) {
609                 
610                 _sync_position = file_pos;
611                 _flags = Flag (_flags|SyncMarked);
612
613                 if (!_frozen) {
614                         maybe_uncopy ();
615                 }
616                 send_change (SyncOffsetChanged);
617         }
618 }
619
620 void
621 Region::clear_sync_position ()
622 {
623         if (_flags & SyncMarked) {
624                 _flags = Flag (_flags & ~SyncMarked);
625
626                 if (!_frozen) {
627                         maybe_uncopy ();
628                 }
629                 send_change (SyncOffsetChanged);
630         }
631 }
632
633 nframes_t
634 Region::sync_offset (int& dir) const
635 {
636         /* returns the sync point relative the first frame of the region */
637
638         if (_flags & SyncMarked) {
639                 if (_sync_position > _start) {
640                         dir = 1;
641                         return _sync_position - _start; 
642                 } else {
643                         dir = -1;
644                         return _start - _sync_position;
645                 }
646         } else {
647                 dir = 0;
648                 return 0;
649         }
650 }
651
652 nframes_t 
653 Region::adjust_to_sync (nframes_t pos)
654 {
655         int sync_dir;
656         nframes_t offset = sync_offset (sync_dir);
657         
658         if (sync_dir > 0) {
659                 if (max_frames - pos > offset) {
660                         pos += offset;
661                 }
662         } else {
663                 if (pos > offset) {
664                         pos -= offset;
665                 } else {
666                         pos = 0;
667                 }
668         }
669
670         return pos;
671 }
672
673 nframes_t
674 Region::sync_position() const
675 {
676         if (_flags & SyncMarked) {
677                 return _sync_position; 
678         } else {
679                 return _start;
680         }
681 }
682
683
684 void
685 Region::raise ()
686 {
687         if (_playlist == 0) {
688                 return;
689         }
690
691         _playlist->raise_region (shared_from_this ());
692 }
693
694 void
695 Region::lower ()
696 {
697         if (_playlist == 0) {
698                 return;
699         }
700
701         _playlist->lower_region (shared_from_this ());
702 }
703
704 void
705 Region::raise_to_top ()
706 {
707
708         if (_playlist == 0) {
709                 return;
710         }
711
712         _playlist->raise_region_to_top (shared_from_this());
713 }
714
715 void
716 Region::lower_to_bottom ()
717 {
718         if (_playlist == 0) {
719                 return;
720         }
721
722         _playlist->lower_region_to_bottom (shared_from_this());
723 }
724
725 void
726 Region::set_layer (layer_t l)
727 {
728         if (_layer != l) {
729                 _layer = l;
730                 
731                 send_change (LayerChanged);
732         }
733 }
734
735 XMLNode&
736 Region::state (bool full_state)
737 {
738         XMLNode *node = new XMLNode ("Region");
739         char buf[64];
740         char* fe;
741
742         _id.print (buf, sizeof (buf));
743         node->add_property ("id", buf);
744         node->add_property ("name", _name);
745         snprintf (buf, sizeof (buf), "%u", _start);
746         node->add_property ("start", buf);
747         snprintf (buf, sizeof (buf), "%u", _length);
748         node->add_property ("length", buf);
749         snprintf (buf, sizeof (buf), "%u", _position);
750         node->add_property ("position", buf);
751         
752         switch (_first_edit) {
753         case EditChangesNothing:
754                 fe = X_("nothing");
755                 break;
756         case EditChangesName:
757                 fe = X_("name");
758                 break;
759         case EditChangesID:
760                 fe = X_("id");
761                 break;
762         }
763
764         node->add_property ("first_edit", fe);
765
766         /* note: flags are stored by derived classes */
767
768         snprintf (buf, sizeof (buf), "%d", (int) _layer);
769         node->add_property ("layer", buf);
770         snprintf (buf, sizeof (buf), "%u", _sync_position);
771         node->add_property ("sync-position", buf);
772
773         return *node;
774 }
775
776 XMLNode&
777 Region::get_state ()
778 {
779         return state (true);
780 }
781
782 int
783 Region::set_live_state (const XMLNode& node, Change& what_changed, bool send)
784 {
785         const XMLNodeList& nlist = node.children();
786         const XMLProperty *prop;
787         nframes_t val;
788
789         /* this is responsible for setting those aspects of Region state 
790            that are mutable after construction.
791         */
792
793         if ((prop = node.property ("name")) == 0) {
794                 error << _("XMLNode describing a Region is incomplete (no name)") << endmsg;
795                 return -1;
796         }
797
798         _name = prop->value();
799
800         if ((prop = node.property ("start")) != 0) {
801                 sscanf (prop->value().c_str(), "%" PRIu32, &val);
802                 if (val != _start) {
803                         what_changed = Change (what_changed|StartChanged);      
804                         _start = val;
805                 }
806         } else {
807                 _start = 0;
808         }
809
810         if ((prop = node.property ("length")) != 0) {
811                 sscanf (prop->value().c_str(), "%" PRIu32, &val);
812                 if (val != _length) {
813                         what_changed = Change (what_changed|LengthChanged);
814                         _length = val;
815                 }
816         } else {
817                 _length = 1;
818         }
819
820         if ((prop = node.property ("position")) != 0) {
821                 sscanf (prop->value().c_str(), "%" PRIu32, &val);
822                 if (val != _position) {
823                         what_changed = Change (what_changed|PositionChanged);
824                         _position = val;
825                 }
826         } else {
827                 _position = 0;
828         }
829
830         if ((prop = node.property ("layer")) != 0) {
831                 layer_t x;
832                 x = (layer_t) atoi (prop->value().c_str());
833                 if (x != _layer) {
834                         what_changed = Change (what_changed|LayerChanged);
835                         _layer = x;
836                 }
837         } else {
838                 _layer = 0;
839         }
840
841         if ((prop = node.property ("sync-position")) != 0) {
842                 sscanf (prop->value().c_str(), "%" PRIu32, &val);
843                 if (val != _sync_position) {
844                         what_changed = Change (what_changed|SyncOffsetChanged);
845                         _sync_position = val;
846                 }
847         } else {
848                 _sync_position = _start;
849         }
850
851         /* XXX FIRST EDIT !!! */
852         
853         /* note: derived classes set flags */
854
855         if (_extra_xml) {
856                 delete _extra_xml;
857                 _extra_xml = 0;
858         }
859
860         for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
861                 
862                 XMLNode *child;
863                 
864                 child = (*niter);
865                 
866                 if (child->name () == "extra") {
867                         _extra_xml = new XMLNode (*child);
868                         break;
869                 }
870         }
871
872         if (send) {
873                 send_change (what_changed);
874         }
875
876         return 0;
877 }
878
879 int
880 Region::set_state (const XMLNode& node)
881 {
882         const XMLProperty *prop;
883         Change what_changed = Change (0);
884
885         /* ID is not allowed to change, ever */
886
887         if ((prop = node.property ("id")) == 0) {
888                 error << _("Session: XMLNode describing a Region is incomplete (no id)") << endmsg;
889                 return -1;
890         }
891
892         _id = prop->value();
893         
894         _first_edit = EditChangesNothing;
895         
896         set_live_state (node, what_changed, true);
897
898         return 0;
899 }
900
901 void
902 Region::freeze ()
903 {
904         _frozen++;
905 }
906
907 void
908 Region::thaw (const string& why)
909 {
910         Change what_changed = Change (0);
911
912         {
913                 Glib::Mutex::Lock lm (lock);
914
915                 if (_frozen && --_frozen > 0) {
916                         return;
917                 }
918
919                 if (pending_changed) {
920                         what_changed = pending_changed;
921                         pending_changed = Change (0);
922                 }
923         }
924
925         if (what_changed == Change (0)) {
926                 return;
927         }
928
929         if (what_changed & LengthChanged) {
930                 if (what_changed & PositionChanged) {
931                         recompute_at_start ();
932                 } 
933                 recompute_at_end ();
934         }
935                 
936         StateChanged (what_changed);
937 }
938
939 void
940 Region::send_change (Change what_changed)
941 {
942         {
943                 Glib::Mutex::Lock lm (lock);
944                 if (_frozen) {
945                         pending_changed = Change (pending_changed|what_changed);
946                         return;
947                 } 
948         }
949
950         StateChanged (what_changed);
951 }
952
953 void
954 Region::set_last_layer_op (uint64_t when)
955 {
956         _last_layer_op = when;
957 }
958
959 bool
960 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
961 {
962         return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
963 }
964
965 bool
966 Region::equivalent (boost::shared_ptr<const Region> other) const
967 {
968         return _start == other->_start &&
969                 _position == other->_position &&
970                 _length == other->_length;
971 }
972
973 bool
974 Region::size_equivalent (boost::shared_ptr<const Region> other) const
975 {
976         return _start == other->_start &&
977                 _length == other->_length;
978 }
979
980 bool
981 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
982 {
983         return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
984 }