fix track controls height allocation to be font sensitive; fix core dump related...
[ardour.git] / gtk2_ardour / streamview.cc
1 #include <cmath>
2
3 #include <gtkmm.h>
4
5 #include <gtkmm2ext/gtk_ui.h>
6
7 #include <ardour/audioplaylist.h>
8 #include <ardour/audioregion.h>
9 #include <ardour/diskstream.h>
10 #include <ardour/audio_track.h>
11 #include <ardour/playlist_templates.h>
12 #include <ardour/source.h>
13
14 #include "streamview.h"
15 #include "regionview.h"
16 #include "audio_time_axis.h"
17 #include "canvas-waveview.h"
18 #include "canvas-simplerect.h"
19 #include "region_selection.h"
20 #include "selection.h"
21 #include "public_editor.h"
22 #include "ardour_ui.h"
23 #include "crossfade_view.h"
24 #include "rgb_macros.h"
25 #include "gui_thread.h"
26 #include "utils.h"
27
28 using namespace ARDOUR;
29 using namespace Editing;
30
31 StreamView::StreamView (AudioTimeAxisView& tv)
32         : _trackview (tv)
33 {
34         region_color = _trackview.color();
35         crossfades_visible = true;
36
37         if (tv.is_audio_track()) {
38                 /* TRACK */
39                 //stream_base_color = RGBA_TO_UINT (222,223,218,255);
40                 stream_base_color = color_map[cAudioTrackBase];
41         } else {
42                 /* BUS */
43                 //stream_base_color = RGBA_TO_UINT (230,226,238,255);
44                 stream_base_color = color_map[cAudioBusBase];
45         }
46
47         /* set_position() will position the group */
48
49         //GTK2FIX -- how to get the group? is the canvas display really a group?
50         //canvas_group = gnome_canvas_item_new (GNOME_CANVAS_GROUP(_trackview.canvas_display),
51         //                          gnome_canvas_group_get_type (),
52         //                          NULL);
53         canvas_group = new ArdourCanvas::Group(*_trackview.canvas_display);
54
55         //canvas_rect = gnome_canvas_item_new (GNOME_CANVAS_GROUP(canvas_group),
56         //                         gnome_canvas_simplerect_get_type(),
57         //                         "x1", 0.0,
58         //                         "y1", 0.0,
59         //                         "x2", 1000000.0,
60         //                         "y2", (double) tv.height,
61         //                         "outline_color_rgba", color_map[cAudioTrackOutline],
62         //                         /* outline ends and bottom */
63         //                         "outline_what", (guint32) (0x1|0x2|0x8),
64         //                         "fill_color_rgba", stream_base_color,
65         //      ]                  NULL);
66         canvas_rect = new ArdourCanvas::SimpleRect (*canvas_group);
67         canvas_rect->property_x1() = 0.0;
68         canvas_rect->property_y1() = 0.0;
69         canvas_rect->property_x2() = 1000000.0;
70         canvas_rect->property_y2() = (double) tv.height;
71         canvas_rect->property_outline_color_rgba() = color_map[cAudioTrackOutline];
72         /* outline ends and bottom */
73         canvas_rect->property_outline_what() = (guint32) (0x1|0x2|0x8);
74         canvas_rect->property_fill_color_rgba() = stream_base_color;
75
76         canvas_rect->signal_event().connect (bind (mem_fun (_trackview.editor, &PublicEditor::canvas_stream_view_event), canvas_rect, &_trackview));
77
78         _samples_per_unit = _trackview.editor.get_current_zoom();
79         _amplitude_above_axis = 1.0;
80
81         if (_trackview.is_audio_track()) {
82                 _trackview.audio_track()->diskstream_changed.connect (mem_fun (*this, &StreamView::diskstream_changed));
83                 _trackview.session().TransportStateChange.connect (mem_fun (*this, &StreamView::transport_changed));
84                 _trackview.get_diskstream()->record_enable_changed.connect (mem_fun (*this, &StreamView::rec_enable_changed));
85                 _trackview.session().RecordEnabled.connect (mem_fun (*this, &StreamView::sess_rec_enable_changed));
86                 _trackview.session().RecordDisabled.connect (mem_fun (*this, &StreamView::sess_rec_enable_changed));
87         } 
88
89         rec_updating = false;
90         rec_active = false;
91         use_rec_regions = tv.editor.show_waveforms_recording ();
92         last_rec_peak_frame = 0;
93 }
94
95 StreamView::~StreamView ()
96 {
97         undisplay_diskstream ();
98         delete canvas_group;
99 }
100
101 void
102 StreamView::attach ()
103 {
104         if (_trackview.is_audio_track()) {
105                 display_diskstream (_trackview.get_diskstream());
106         }
107 }
108
109 int
110 StreamView::set_position (gdouble x, gdouble y)
111
112 {
113         canvas_group->property_x() = x;
114         canvas_group->property_y() = y;
115         return 0;
116 }
117
118 int
119 StreamView::set_height (gdouble h)
120 {
121         /* limit the values to something sane-ish */
122
123         if (h < 10.0 || h > 1000.0) {
124                 return -1;
125         }
126
127         canvas_rect->property_y2() = h;
128
129         for (AudioRegionViewList::iterator i = region_views.begin(); i != region_views.end(); ++i) {
130                 (*i)->set_height (h);
131         }
132
133         for (CrossfadeViewList::iterator i = crossfade_views.begin(); i != crossfade_views.end(); ++i) {
134                 (*i)->set_height (h);
135         }
136
137         for (vector<RecBoxInfo>::iterator i = rec_rects.begin(); i != rec_rects.end(); ++i) {
138                 RecBoxInfo &recbox = (*i);
139                 recbox.rectangle->property_y2() = h - 1.0;
140         }
141
142         return 0;
143 }
144
145 int 
146 StreamView::set_samples_per_unit (gdouble spp)
147 {
148         AudioRegionViewList::iterator i;
149
150         if (spp < 1.0) {
151                 return -1;
152         }
153
154         _samples_per_unit = spp;
155
156         for (i = region_views.begin(); i != region_views.end(); ++i) {
157                 (*i)->set_samples_per_unit (spp);
158         }
159
160         for (CrossfadeViewList::iterator xi = crossfade_views.begin(); xi != crossfade_views.end(); ++xi) {
161                 (*xi)->set_samples_per_unit (spp);
162         }
163
164         for (vector<RecBoxInfo>::iterator xi = rec_rects.begin(); xi != rec_rects.end(); ++xi) {
165                 RecBoxInfo &recbox = (*xi);
166                 
167                 gdouble xstart = _trackview.editor.frame_to_pixel ( recbox.start );
168                 gdouble xend = _trackview.editor.frame_to_pixel ( recbox.start + recbox.length );
169
170                 recbox.rectangle->property_x1() = xstart;
171                 recbox.rectangle->property_x2() = xend;
172         }
173
174         return 0;
175 }
176
177 int 
178 StreamView::set_amplitude_above_axis (gdouble app)
179
180 {
181         AudioRegionViewList::iterator i;
182
183         if (app < 1.0) {
184                 return -1;
185         }
186
187         _amplitude_above_axis = app;
188
189         for (i = region_views.begin(); i != region_views.end(); ++i) {
190                 (*i)->set_amplitude_above_axis (app);
191         }
192
193         return 0;
194 }
195
196 void
197 StreamView::add_region_view (Region *r)
198 {
199         add_region_view_internal (r, true);
200 }
201
202 void
203 StreamView::add_region_view_internal (Region *r, bool wait_for_waves)
204 {
205         ENSURE_GUI_THREAD (bind (mem_fun (*this, &StreamView::add_region_view), r));
206
207         AudioRegion* region = dynamic_cast<AudioRegion*> (r);
208
209         if (region == 0) {
210                 return;
211         }
212
213         AudioRegionView *region_view;
214         list<AudioRegionView *>::iterator i;
215
216         for (i = region_views.begin(); i != region_views.end(); ++i) {
217                 if (&(*i)->region == region) {
218                         
219                         /* great. we already have a AudioRegionView for this Region. use it again.
220                          */
221
222                         (*i)->set_valid (true);
223                         return;
224                 }
225         }
226
227         region_view = new AudioRegionView (canvas_group,
228                                            _trackview,
229                                            *region,
230                                            _samples_per_unit,
231                                            _amplitude_above_axis, 
232                                            region_color, 
233                                            wait_for_waves);
234
235         region_views.push_front (region_view);
236         
237         /* follow global waveform setting */
238
239         region_view->set_waveform_visible(_trackview.editor.show_waveforms());
240
241         /* catch regionview going away */
242
243         region->GoingAway.connect (mem_fun (*this, &StreamView::remove_region_view));
244         
245         AudioRegionViewAdded (region_view);
246 }
247
248 void
249 StreamView::remove_region_view (Region *r)
250 {
251         ENSURE_GUI_THREAD (bind (mem_fun (*this, &StreamView::remove_region_view), r));
252
253         AudioRegion* ar = dynamic_cast<AudioRegion*> (r);
254
255         if (ar == 0) {
256                 return;
257         }
258
259         for (list<AudioRegionView *>::iterator i = region_views.begin(); i != region_views.end(); ++i) {
260                 if (&((*i)->region) == ar) {
261                         delete *i;
262                         region_views.erase (i);
263                         break;
264                 }
265         }
266
267         for (list<CrossfadeView *>::iterator i = crossfade_views.begin(); i != crossfade_views.end();) {
268                 list<CrossfadeView*>::iterator tmp;
269                 
270                 tmp = i;
271                 ++tmp;
272                 
273                 if ((*i)->crossfade.involves (*ar)) {
274                         delete *i;
275                         crossfade_views.erase (i);
276                 }
277                 
278                 i = tmp;
279         }
280 }
281
282 void
283 StreamView::remove_rec_region (Region *r)
284 {
285         ENSURE_GUI_THREAD(bind (mem_fun (*this, &StreamView::remove_rec_region), r));
286         
287         if (!Gtkmm2ext::UI::instance()->caller_is_gui_thread()) {
288                 fatal << "region deleted from non-GUI thread!" << endmsg;
289                 /*NOTREACHED*/
290         } 
291
292         AudioRegion* ar = dynamic_cast<AudioRegion*> (r);
293
294         if (ar == 0) {
295                 return;
296         }
297
298         for (list<AudioRegion *>::iterator i = rec_regions.begin(); i != rec_regions.end(); ++i) {
299                 if (*i == ar) {
300                         rec_regions.erase (i);
301                         break;
302                 }
303         }
304 }
305
306 void
307 StreamView::undisplay_diskstream ()
308 {
309         for (AudioRegionViewList::iterator i = region_views.begin(); i != region_views.end(); ++i) {
310                 delete *i;
311         }
312
313         for (CrossfadeViewList::iterator i = crossfade_views.begin(); i != crossfade_views.end(); ++i) {
314                 delete *i;
315         }
316
317         region_views.clear();
318         crossfade_views.clear ();
319 }
320
321 void
322 StreamView::display_diskstream (DiskStream *ds)
323 {
324         playlist_change_connection.disconnect();
325         playlist_changed (ds);
326         playlist_change_connection = ds->PlaylistChanged.connect (bind (mem_fun (*this, &StreamView::playlist_changed), ds));
327 }
328
329 void
330 StreamView::playlist_modified ()
331 {
332         ENSURE_GUI_THREAD (mem_fun (*this, &StreamView::playlist_modified));
333
334         /* if the playlist is modified, make sure xfades are on top and all the regionviews are stacked 
335            correctly.
336         */
337
338         for (AudioRegionViewList::iterator i = region_views.begin(); i != region_views.end(); ++i) {
339                 region_layered (*i);
340         }
341
342         for (list<CrossfadeView *>::iterator i = crossfade_views.begin(); i != crossfade_views.end(); ++i) {
343                 (*i)->get_canvas_group()->raise_to_top();
344         }
345 }
346
347 void
348 StreamView::playlist_changed (DiskStream *ds)
349 {
350         ENSURE_GUI_THREAD (bind (mem_fun (*this, &StreamView::playlist_changed), ds));
351
352         /* disconnect from old playlist */
353
354         for (vector<sigc::connection>::iterator i = playlist_connections.begin(); i != playlist_connections.end(); ++i) {
355                 (*i).disconnect();
356         }
357         
358         playlist_connections.clear();
359         undisplay_diskstream ();
360
361         /* draw it */
362
363         redisplay_diskstream ();
364
365         /* catch changes */
366
367         playlist_connections.push_back (ds->playlist()->RegionAdded.connect (mem_fun (*this, &StreamView::add_region_view)));
368         playlist_connections.push_back (ds->playlist()->RegionRemoved.connect (mem_fun (*this, &StreamView::remove_region_view)));
369         playlist_connections.push_back (ds->playlist()->StateChanged.connect (mem_fun (*this, &StreamView::playlist_state_changed)));
370         playlist_connections.push_back (ds->playlist()->Modified.connect (mem_fun (*this, &StreamView::playlist_modified)));
371         playlist_connections.push_back (ds->playlist()->NewCrossfade.connect (mem_fun (*this, &StreamView::add_crossfade)));
372 }
373
374 void
375 StreamView::add_crossfade (Crossfade *crossfade)
376 {
377         AudioRegionView* lview = 0;
378         AudioRegionView* rview = 0;
379
380         ENSURE_GUI_THREAD (bind (mem_fun (*this, &StreamView::add_crossfade), crossfade));
381
382         /* first see if we already have a CrossfadeView for this Crossfade */
383
384         for (list<CrossfadeView *>::iterator i = crossfade_views.begin(); i != crossfade_views.end(); ++i) {
385                 if (&(*i)->crossfade == crossfade) {
386                         if (!crossfades_visible) {
387                                 (*i)->hide();
388                         } else {
389                                 (*i)->show ();
390                         }
391                         (*i)->set_valid (true);
392                         return;
393                 }
394         }
395
396         /* create a new one */
397
398         for (list<AudioRegionView *>::iterator i = region_views.begin(); i != region_views.end(); ++i) {
399                 if (!lview && &((*i)->region) == &crossfade->out()) {
400                         lview = *i;
401                 }
402                 if (!rview && &((*i)->region) == &crossfade->in()) {
403                         rview = *i;
404                 }
405         }
406
407         CrossfadeView *cv = new CrossfadeView (_trackview.canvas_display,
408                                                _trackview,
409                                                *crossfade,
410                                                _samples_per_unit,
411                                                region_color,
412                                                *lview, *rview);
413
414         crossfade->Invalidated.connect (mem_fun (*this, &StreamView::remove_crossfade));
415         crossfade_views.push_back (cv);
416
417         if (!crossfades_visible) {
418                 cv->hide ();
419         }
420 }
421
422 void
423 StreamView::remove_crossfade (Crossfade *xfade)
424 {
425         ENSURE_GUI_THREAD (bind (mem_fun (*this, &StreamView::remove_crossfade), xfade));
426
427         for (list<CrossfadeView*>::iterator i = crossfade_views.begin(); i != crossfade_views.end(); ++i) {
428                 if (&(*i)->crossfade == xfade) {
429                         delete *i;
430                         crossfade_views.erase (i);
431                         break;
432                 }
433         }
434 }
435
436 void
437 StreamView::playlist_state_changed (Change ignored)
438 {
439         ENSURE_GUI_THREAD (bind (mem_fun (*this, &StreamView::playlist_state_changed), ignored));
440
441         redisplay_diskstream ();
442 }
443
444 void
445 StreamView::redisplay_diskstream ()
446 {
447         list<AudioRegionView *>::iterator i, tmp;
448         list<CrossfadeView*>::iterator xi, tmpx;
449
450         for (i = region_views.begin(); i != region_views.end(); ++i) {
451                 (*i)->set_valid (false);
452         }
453
454         for (xi = crossfade_views.begin(); xi != crossfade_views.end(); ++xi) {
455                 (*xi)->set_valid (false);
456                 if ((*xi)->visible()) {
457                         (*xi)->show ();
458                 }
459         }
460
461         if (_trackview.is_audio_track()) {
462                 _trackview.get_diskstream()->playlist()->foreach_region (this, &StreamView::add_region_view);
463                 _trackview.get_diskstream()->playlist()->foreach_crossfade (this, &StreamView::add_crossfade);
464         }
465
466         for (i = region_views.begin(); i != region_views.end(); ) {
467                 tmp = i;
468                 tmp++;
469
470                 if (!(*i)->is_valid()) {
471                         delete *i;
472                         region_views.erase (i);
473                 } 
474
475                 i = tmp;
476         }
477
478         for (xi = crossfade_views.begin(); xi != crossfade_views.end();) {
479                 tmpx = xi;
480                 tmpx++;
481
482                 if (!(*xi)->valid()) {
483                         delete *xi;
484                         crossfade_views.erase (xi);
485                 }
486
487                 xi = tmpx;
488         }
489
490         /* now fix layering */
491
492         playlist_modified ();
493 }
494
495 void
496 StreamView::diskstream_changed (void *src_ignored)
497 {
498         AudioTrack *at;
499
500         if ((at = _trackview.audio_track()) != 0) {
501                 DiskStream& ds = at->disk_stream();
502                 /* XXX grrr: when will SigC++ allow me to bind references? */
503                 Gtkmm2ext::UI::instance()->call_slot (bind (mem_fun (*this, &StreamView::display_diskstream), &ds));
504         } else {
505                 Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &StreamView::undisplay_diskstream));
506         }
507 }
508
509 void
510 StreamView::apply_color (Gdk::Color& color, ColorTarget target)
511
512 {
513         list<AudioRegionView *>::iterator i;
514
515         switch (target) {
516         case RegionColor:
517                 region_color = color;
518                 for (i = region_views.begin(); i != region_views.end(); ++i) {
519                         (*i)->set_color (region_color);
520                 }
521                 // stream_base_color = RGBA_TO_UINT (color.red/256, color.green/256, color.blue/256, 255);
522                 // gnome_canvas_item_set (canvas_rect, "fill_color_rgba", stream_base_color, NULL);
523                 break;
524                 
525         case StreamBaseColor:
526                 // stream_base_color = RGBA_TO_UINT (color.red/256, color.green/256, color.blue/256, 255);
527                 // gnome_canvas_item_set (canvas_rect, "fill_color_rgba", stream_base_color, NULL);
528                 break;
529         }
530 }
531
532 void
533 StreamView::set_show_waveforms (bool yn)
534 {
535         for (list<AudioRegionView *>::iterator i = region_views.begin(); i != region_views.end(); ++i) {
536                         (*i)->set_waveform_visible (yn);
537         }
538 }
539
540 void
541 StreamView::set_selected_regionviews (AudioRegionSelection& regions)
542 {
543         bool selected;
544
545         for (list<AudioRegionView*>::iterator i = region_views.begin(); i != region_views.end(); ++i) {
546                 
547                 selected = false;
548                 
549                 for (AudioRegionSelection::iterator ii = regions.begin(); ii != regions.end(); ++ii) {
550                         if (*i == *ii) {
551                                 selected = true;
552                         }
553                 }
554                 
555                 (*i)->set_selected (selected, this);
556         }
557 }
558
559 void
560 StreamView::get_selectables (jack_nframes_t start, jack_nframes_t end, list<Selectable*>& results)
561 {
562         for (list<AudioRegionView*>::iterator i = region_views.begin(); i != region_views.end(); ++i) {
563                 if ((*i)->region.coverage(start, end) != OverlapNone) {
564                         results.push_back (*i);
565                 }
566         }
567 }
568
569 void
570 StreamView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
571 {
572         for (list<AudioRegionView*>::iterator i = region_views.begin(); i != region_views.end(); ++i) {
573                 if (!sel.audio_regions.contains (*i)) {
574                         results.push_back (*i);
575                 }
576         }
577 }
578
579 void
580 StreamView::set_waveform_shape (WaveformShape shape)
581 {
582         for (AudioRegionViewList::iterator i = region_views.begin(); i != region_views.end(); ++i) {
583                 (*i)->set_waveform_shape (shape);
584         }
585 }               
586                 
587 void
588 StreamView::region_layered (AudioRegionView* rv)
589 {
590         rv->get_canvas_group()->lower_to_bottom();
591
592         /* don't ever leave it at the bottom, since then it doesn't
593            get events - the  parent group does instead ...
594         */
595         
596         rv->get_canvas_group()->raise (rv->region.layer() + 1);
597 }
598
599 void
600 StreamView::rec_enable_changed (void *src)
601 {
602         Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &StreamView::setup_rec_box));
603 }
604
605 void
606 StreamView::sess_rec_enable_changed ()
607 {
608         Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &StreamView::setup_rec_box));
609 }
610
611 void
612 StreamView::transport_changed()
613 {
614         Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &StreamView::setup_rec_box));
615 }
616
617 void
618 StreamView::setup_rec_box ()
619 {
620         // cerr << _trackview.name() << " streamview SRB\n";
621
622         if (_trackview.session().transport_rolling()) {
623
624                 // cerr << "\trolling\n";
625
626                 if (!rec_active
627                     && _trackview.session().record_status() == Session::Recording
628                     && _trackview.get_diskstream()->record_enabled()) {
629
630                         if (use_rec_regions && rec_regions.size() == rec_rects.size()) {
631                                 /* add a new region, but don't bother if they set use_rec_regions mid-record */
632
633                                 AudioRegion::SourceList sources;
634
635                                 for (list<sigc::connection>::iterator prc = peak_ready_connections.begin(); prc != peak_ready_connections.end(); ++prc) {
636                                         (*prc).disconnect();
637                                 }
638                                 peak_ready_connections.clear();
639                                         
640                                 for (uint32_t n=0; n < _trackview.get_diskstream()->n_channels(); ++n) {
641                                         Source *src = (Source *) _trackview.get_diskstream()->write_source (n);
642                                         if (src) {
643                                                 sources.push_back (src);
644                                                 peak_ready_connections.push_back (src->PeakRangeReady.connect (bind (mem_fun (*this, &StreamView::rec_peak_range_ready), src))); 
645                                         }
646                                 }
647
648                                 // handle multi
649                                 
650                                 jack_nframes_t start = 0;
651                                 if (rec_regions.size() > 0) {
652                                         start = rec_regions.back()->start() + _trackview.get_diskstream()->get_captured_frames(rec_regions.size()-1);
653                                 }
654                                 
655                                 AudioRegion * region = new AudioRegion(sources, start, 1 , "", 0, (Region::Flag)(Region::DefaultFlags | Region::DoNotSaveState), false);
656                                 region->set_position (_trackview.session().transport_frame(), this);
657                                 rec_regions.push_back (region);
658                                 /* catch it if it goes away */
659                                 region->GoingAway.connect (mem_fun (*this, &StreamView::remove_rec_region));
660
661                                 /* we add the region later */
662                         }
663                         
664                         /* start a new rec box */
665
666                         AudioTrack* at;
667
668                         at = _trackview.audio_track(); /* we know what it is already */
669                         DiskStream& ds = at->disk_stream();
670                         jack_nframes_t frame_pos = ds.current_capture_start ();
671                         gdouble xstart = _trackview.editor.frame_to_pixel (frame_pos);
672                         gdouble xend = xstart;
673                         
674                         ArdourCanvas::SimpleRect * rec_rect = new Gnome::Canvas::SimpleRect (*canvas_group);
675                         rec_rect->property_x1() = xstart;
676                         rec_rect->property_y1() = 1.0;
677                         rec_rect->property_x2() = xend;
678                         rec_rect->property_y2() = (double) _trackview.height - 1;
679                         rec_rect->property_outline_color_rgba() = color_map[cRecordingRectOutline];
680                         rec_rect->property_fill_color_rgba() =  color_map[cRecordingRectFill];
681                         
682                         RecBoxInfo recbox;
683                         recbox.rectangle = rec_rect;
684                         recbox.start = _trackview.session().transport_frame();
685                         recbox.length = 0;
686                         
687                         rec_rects.push_back (recbox);
688                         
689                         screen_update_connection.disconnect();
690                         screen_update_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (mem_fun (*this, &StreamView::update_rec_box));        
691                         rec_updating = true;
692                         rec_active = true;
693
694                 } else if (rec_active &&
695                            (_trackview.session().record_status() != Session::Recording ||
696                             !_trackview.get_diskstream()->record_enabled())) {
697
698                         screen_update_connection.disconnect();
699                         rec_active = false;
700                         rec_updating = false;
701
702                 }
703                 
704         } else {
705
706                 // cerr << "\tNOT rolling, rec_rects = " << rec_rects.size() << " rec_regions = " << rec_regions.size() << endl;
707
708                 if (!rec_rects.empty() || !rec_regions.empty()) {
709
710                         /* disconnect rapid update */
711                         screen_update_connection.disconnect();
712
713                         for (list<sigc::connection>::iterator prc = peak_ready_connections.begin(); prc != peak_ready_connections.end(); ++prc) {
714                                 (*prc).disconnect();
715                         }
716                         peak_ready_connections.clear();
717
718                         rec_updating = false;
719                         rec_active = false;
720                         last_rec_peak_frame = 0;
721                         
722                         /* remove temp regions */
723                         for (list<AudioRegion*>::iterator iter=rec_regions.begin(); iter != rec_regions.end(); )
724                         {
725                                 list<AudioRegion*>::iterator tmp;
726
727                                 tmp = iter;
728                                 ++tmp;
729
730                                 /* this will trigger the remove_region_view */
731                                 delete *iter;
732
733                                 iter = tmp;
734                         }
735                         
736                         rec_regions.clear();
737
738                         // cerr << "\tclear " << rec_rects.size() << " rec rects\n";
739                 
740
741                         /* transport stopped, clear boxes */
742                         for (vector<RecBoxInfo>::iterator iter=rec_rects.begin(); iter != rec_rects.end(); ++iter) {
743                                 RecBoxInfo &rect = (*iter);
744                                 delete rect.rectangle;
745                         }
746                         
747                         rec_rects.clear();
748                         
749                 }
750         }
751 }
752
753
754 void
755 StreamView::update_rec_box ()
756 {
757         /* only update the last box */
758         if (rec_active && rec_rects.size() > 0) {
759                 RecBoxInfo & rect = rec_rects.back();
760                 jack_nframes_t at = _trackview.get_diskstream()->current_capture_end();
761                 
762                 rect.length = at - rect.start;
763
764                 gdouble xstart = _trackview.editor.frame_to_pixel ( rect.start );
765                 gdouble xend = _trackview.editor.frame_to_pixel ( at );
766
767                 rect.rectangle->property_x1() = xstart;
768                 rect.rectangle->property_x2() = xend;
769         }
770 }
771
772 AudioRegionView*
773 StreamView::find_view (const AudioRegion& region)
774 {
775         for (list<AudioRegionView*>::iterator i = region_views.begin(); i != region_views.end(); ++i) {
776
777                 if (&(*i)->region == &region) {
778                         return *i;
779                 }
780         }
781         return 0;
782 }
783         
784 void
785 StreamView::foreach_regionview (sigc::slot<void,AudioRegionView*> slot)
786 {
787         for (list<AudioRegionView*>::iterator i = region_views.begin(); i != region_views.end(); ++i) {
788                 slot (*i);
789         }
790 }
791
792 void
793 StreamView::foreach_crossfadeview (void (CrossfadeView::*pmf)(void))
794 {
795         for (list<CrossfadeView*>::iterator i = crossfade_views.begin(); i != crossfade_views.end(); ++i) {
796                 ((*i)->*pmf) ();
797         }
798 }
799
800 void
801 StreamView::rec_peak_range_ready (jack_nframes_t start, jack_nframes_t cnt, Source * src)
802 {
803         // this is called from the peak building thread
804
805         ENSURE_GUI_THREAD(bind (mem_fun (*this, &StreamView::rec_peak_range_ready), start, cnt, src));
806         
807         if (rec_peak_ready_map.size() == 0 || start+cnt > last_rec_peak_frame) {
808                 last_rec_peak_frame = start + cnt;
809         }
810
811         rec_peak_ready_map[src] = true;
812
813         if (rec_peak_ready_map.size() == _trackview.get_diskstream()->n_channels()) {
814                 this->update_rec_regions ();
815                 rec_peak_ready_map.clear();
816         }
817 }
818
819 void
820 StreamView::update_rec_regions ()
821 {
822         if (use_rec_regions) {
823
824                 uint32_t n = 0;
825
826                 for (list<AudioRegion*>::iterator iter = rec_regions.begin(); iter != rec_regions.end(); n++) {
827
828                         list<AudioRegion*>::iterator tmp;
829
830                         tmp = iter;
831                         ++tmp;
832
833                         if (!canvas_item_visible (rec_rects[n].rectangle)) {
834                                 /* rect already hidden, this region is done */
835                                 iter = tmp;
836                                 continue;
837                         }
838                         
839                         AudioRegion * region = (*iter);
840                         jack_nframes_t origlen = region->length();
841
842                         if (region == rec_regions.back() && rec_active) {
843
844                                 if (last_rec_peak_frame > region->start()) {
845
846                                         jack_nframes_t nlen = last_rec_peak_frame - region->start();
847
848                                         if (nlen != region->length()) {
849
850                                                 region->freeze ();
851                                                 region->set_position (_trackview.get_diskstream()->get_capture_start_frame(n), this);
852                                                 region->set_length (nlen, this);
853                                                 region->thaw ("updated");
854
855                                                 if (origlen == 1) {
856                                                         /* our special initial length */
857                                                         add_region_view_internal (region, false);
858                                                 }
859
860                                                 /* also update rect */
861                                                 ArdourCanvas::SimpleRect * rect = rec_rects[n].rectangle;
862                                                 gdouble xend = _trackview.editor.frame_to_pixel (region->position() + region->length());
863                                                 rect->property_x2() = xend;
864                                         }
865                                 }
866
867                         } else {
868
869                                 jack_nframes_t nlen = _trackview.get_diskstream()->get_captured_frames(n);
870
871                                 if (nlen != region->length()) {
872
873                                         if (region->source(0).length() >= region->start() + nlen) {
874
875                                                 region->freeze ();
876                                                 region->set_position (_trackview.get_diskstream()->get_capture_start_frame(n), this);
877                                                 region->set_length (nlen, this);
878                                                 region->thaw ("updated");
879                                                 
880                                                 if (origlen == 1) {
881                                                         /* our special initial length */
882                                                         add_region_view_internal (region, false);
883                                                 }
884                                                 
885                                                 /* also hide rect */
886                                                 ArdourCanvas::Item * rect = rec_rects[n].rectangle;
887                                                 rect->hide();
888
889                                         }
890                                 }
891                         }
892
893                         iter = tmp;
894                 }
895         }
896 }
897
898 void
899 StreamView::show_all_xfades ()
900 {
901         foreach_crossfadeview (&CrossfadeView::show);
902         crossfades_visible = true;
903 }
904
905 void
906 StreamView::hide_all_xfades ()
907 {
908         foreach_crossfadeview (&CrossfadeView::hide);
909         crossfades_visible = false;
910 }
911
912 void
913 StreamView::hide_xfades_involving (AudioRegionView& rv)
914 {
915         for (list<CrossfadeView *>::iterator i = crossfade_views.begin(); i != crossfade_views.end(); ++i) {
916                 if ((*i)->crossfade.involves (rv.region)) {
917                         (*i)->fake_hide ();
918                 }
919         }
920 }
921
922 void
923 StreamView::reveal_xfades_involving (AudioRegionView& rv)
924 {
925         for (list<CrossfadeView *>::iterator i = crossfade_views.begin(); i != crossfade_views.end(); ++i) {
926                 if ((*i)->crossfade.involves (rv.region) && (*i)->visible()) {
927                         (*i)->show ();
928                 }
929         }
930 }