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