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