change rec box drawing for destructive tracks
[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                         uint32_t fill_color;
670
671                         switch (_trackview.audio_track()->mode()) {
672                         case Normal:
673                                 xend = xstart;
674                                 fill_color = color_map[cRecordingRectFill];
675                                 break;
676
677                         case Destructive:
678                                 xend = xstart + 2;
679                                 fill_color = color_map[cRecordingRectFill];
680                                 /* make the recording rect translucent to allow
681                                    the user to see the peak data coming in, etc.
682                                 */
683                                 fill_color = UINT_RGBA_CHANGE_A (fill_color, 120);
684                                 break;
685                         }
686                         
687                         ArdourCanvas::SimpleRect * rec_rect = new Gnome::Canvas::SimpleRect (*canvas_group);
688                         rec_rect->property_x1() = xstart;
689                         rec_rect->property_y1() = 1.0;
690                         rec_rect->property_x2() = xend;
691                         rec_rect->property_y2() = (double) _trackview.height - 1;
692                         rec_rect->property_outline_color_rgba() = color_map[cRecordingRectOutline];
693                         rec_rect->property_fill_color_rgba() = fill_color;
694                         
695                         RecBoxInfo recbox;
696                         recbox.rectangle = rec_rect;
697                         recbox.start = _trackview.session().transport_frame();
698                         recbox.length = 0;
699                         
700                         rec_rects.push_back (recbox);
701                         
702                         screen_update_connection.disconnect();
703                         screen_update_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (mem_fun (*this, &StreamView::update_rec_box));        
704                         rec_updating = true;
705                         rec_active = true;
706
707                 } else if (rec_active &&
708                            (_trackview.session().record_status() != Session::Recording ||
709                             !_trackview.get_diskstream()->record_enabled())) {
710
711                         screen_update_connection.disconnect();
712                         rec_active = false;
713                         rec_updating = false;
714
715                 }
716                 
717         } else {
718
719                 // cerr << "\tNOT rolling, rec_rects = " << rec_rects.size() << " rec_regions = " << rec_regions.size() << endl;
720
721                 if (!rec_rects.empty() || !rec_regions.empty()) {
722
723                         /* disconnect rapid update */
724                         screen_update_connection.disconnect();
725
726                         for (list<sigc::connection>::iterator prc = peak_ready_connections.begin(); prc != peak_ready_connections.end(); ++prc) {
727                                 (*prc).disconnect();
728                         }
729                         peak_ready_connections.clear();
730
731                         rec_updating = false;
732                         rec_active = false;
733                         last_rec_peak_frame = 0;
734                         
735                         /* remove temp regions */
736                         for (list<AudioRegion*>::iterator iter=rec_regions.begin(); iter != rec_regions.end(); )
737                         {
738                                 list<AudioRegion*>::iterator tmp;
739
740                                 tmp = iter;
741                                 ++tmp;
742
743                                 /* this will trigger the remove_region_view */
744                                 delete *iter;
745
746                                 iter = tmp;
747                         }
748                         
749                         rec_regions.clear();
750
751                         // cerr << "\tclear " << rec_rects.size() << " rec rects\n";
752                 
753
754                         /* transport stopped, clear boxes */
755                         for (vector<RecBoxInfo>::iterator iter=rec_rects.begin(); iter != rec_rects.end(); ++iter) {
756                                 RecBoxInfo &rect = (*iter);
757                                 delete rect.rectangle;
758                         }
759                         
760                         rec_rects.clear();
761                         
762                 }
763         }
764 }
765
766
767 void
768 StreamView::update_rec_box ()
769 {
770         if (rec_active && rec_rects.size() > 0) {
771                 /* only update the last box */
772                 RecBoxInfo & rect = rec_rects.back();
773                 jack_nframes_t at = _trackview.get_diskstream()->current_capture_end();
774                 double xstart;
775                 double xend;
776                 
777                 switch (_trackview.audio_track()->mode()) {
778                 case Normal:
779                         rect.length = at - rect.start;
780                         xstart = _trackview.editor.frame_to_pixel (rect.start);
781                         xend = _trackview.editor.frame_to_pixel (at);
782                         break;
783                         
784                 case Destructive:
785                         rect.length = 2;
786                         xstart = _trackview.editor.frame_to_pixel (_trackview.get_diskstream()->current_capture_start());
787                         xend = _trackview.editor.frame_to_pixel (at);
788                         break;
789                 }
790                 
791                 rect.rectangle->property_x1() = xstart;
792                 rect.rectangle->property_x2() = xend;
793         }
794 }
795         
796 AudioRegionView*
797 StreamView::find_view (const AudioRegion& region)
798 {
799         for (list<AudioRegionView*>::iterator i = region_views.begin(); i != region_views.end(); ++i) {
800
801                 if (&(*i)->region == &region) {
802                         return *i;
803                 }
804         }
805         return 0;
806 }
807         
808 void
809 StreamView::foreach_regionview (sigc::slot<void,AudioRegionView*> slot)
810 {
811         for (list<AudioRegionView*>::iterator i = region_views.begin(); i != region_views.end(); ++i) {
812                 slot (*i);
813         }
814 }
815
816 void
817 StreamView::foreach_crossfadeview (void (CrossfadeView::*pmf)(void))
818 {
819         for (list<CrossfadeView*>::iterator i = crossfade_views.begin(); i != crossfade_views.end(); ++i) {
820                 ((*i)->*pmf) ();
821         }
822 }
823
824 void
825 StreamView::rec_peak_range_ready (jack_nframes_t start, jack_nframes_t cnt, Source * src)
826 {
827         // this is called from the peak building thread
828
829         ENSURE_GUI_THREAD(bind (mem_fun (*this, &StreamView::rec_peak_range_ready), start, cnt, src));
830         
831         if (rec_peak_ready_map.size() == 0 || start+cnt > last_rec_peak_frame) {
832                 last_rec_peak_frame = start + cnt;
833         }
834
835         rec_peak_ready_map[src] = true;
836
837         if (rec_peak_ready_map.size() == _trackview.get_diskstream()->n_channels()) {
838                 this->update_rec_regions ();
839                 rec_peak_ready_map.clear();
840         }
841 }
842
843 void
844 StreamView::update_rec_regions ()
845 {
846         if (use_rec_regions) {
847
848                 uint32_t n = 0;
849
850                 for (list<AudioRegion*>::iterator iter = rec_regions.begin(); iter != rec_regions.end(); n++) {
851
852                         list<AudioRegion*>::iterator tmp;
853
854                         tmp = iter;
855                         ++tmp;
856
857                         if (!canvas_item_visible (rec_rects[n].rectangle)) {
858                                 /* rect already hidden, this region is done */
859                                 iter = tmp;
860                                 continue;
861                         }
862                         
863                         AudioRegion * region = (*iter);
864                         jack_nframes_t origlen = region->length();
865
866                         if (region == rec_regions.back() && rec_active) {
867
868                                 if (last_rec_peak_frame > region->start()) {
869
870                                         jack_nframes_t nlen = last_rec_peak_frame - region->start();
871
872                                         if (nlen != region->length()) {
873
874                                                 region->freeze ();
875                                                 region->set_position (_trackview.get_diskstream()->get_capture_start_frame(n), this);
876                                                 region->set_length (nlen, this);
877                                                 region->thaw ("updated");
878
879                                                 if (origlen == 1) {
880                                                         /* our special initial length */
881                                                         add_region_view_internal (region, false);
882                                                 }
883
884                                                 /* also update rect */
885                                                 ArdourCanvas::SimpleRect * rect = rec_rects[n].rectangle;
886                                                 gdouble xend = _trackview.editor.frame_to_pixel (region->position() + region->length());
887                                                 rect->property_x2() = xend;
888                                         }
889                                 }
890
891                         } else {
892
893                                 jack_nframes_t nlen = _trackview.get_diskstream()->get_captured_frames(n);
894
895                                 if (nlen != region->length()) {
896
897                                         if (region->source(0).length() >= region->start() + nlen) {
898
899                                                 region->freeze ();
900                                                 region->set_position (_trackview.get_diskstream()->get_capture_start_frame(n), this);
901                                                 region->set_length (nlen, this);
902                                                 region->thaw ("updated");
903                                                 
904                                                 if (origlen == 1) {
905                                                         /* our special initial length */
906                                                         add_region_view_internal (region, false);
907                                                 }
908                                                 
909                                                 /* also hide rect */
910                                                 ArdourCanvas::Item * rect = rec_rects[n].rectangle;
911                                                 rect->hide();
912
913                                         }
914                                 }
915                         }
916
917                         iter = tmp;
918                 }
919         }
920 }
921
922 void
923 StreamView::show_all_xfades ()
924 {
925         foreach_crossfadeview (&CrossfadeView::show);
926         crossfades_visible = true;
927 }
928
929 void
930 StreamView::hide_all_xfades ()
931 {
932         foreach_crossfadeview (&CrossfadeView::hide);
933         crossfades_visible = false;
934 }
935
936 void
937 StreamView::hide_xfades_involving (AudioRegionView& rv)
938 {
939         for (list<CrossfadeView *>::iterator i = crossfade_views.begin(); i != crossfade_views.end(); ++i) {
940                 if ((*i)->crossfade.involves (rv.region)) {
941                         (*i)->fake_hide ();
942                 }
943         }
944 }
945
946 void
947 StreamView::reveal_xfades_involving (AudioRegionView& rv)
948 {
949         for (list<CrossfadeView *>::iterator i = crossfade_views.begin(); i != crossfade_views.end(); ++i) {
950                 if ((*i)->crossfade.involves (rv.region) && (*i)->visible()) {
951                         (*i)->show ();
952                 }
953         }
954 }
955
956 void
957 StreamView::color_handler (ColorID id, uint32_t val)
958 {
959         switch (id) {
960         case cAudioTrackBase:
961                 if (_trackview.is_audio_track()) {
962                         canvas_rect->property_fill_color_rgba() = val;
963                 } 
964                 break;
965         case cAudioBusBase:
966                 if (!_trackview.is_audio_track()) {
967                         canvas_rect->property_fill_color_rgba() = val;
968                 }
969                 break;
970         case cAudioTrackOutline:
971                 canvas_rect->property_outline_color_rgba() = val;
972                 break;
973
974         default:
975                 break;
976         }
977 }