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