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