add new files for export dialogs
[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         for (list<AudioRegionView*>::iterator i = region_views.begin(); i != region_views.end(); ++i) {
539                 
540                 selected = false;
541                 
542                 for (AudioRegionSelection::iterator ii = regions.begin(); ii != regions.end(); ++ii) {
543                         if (*i == *ii) {
544                                 selected = true;
545                         }
546                 }
547                 
548                 (*i)->set_selected (selected, this);
549         }
550 }
551
552 void
553 StreamView::get_selectables (jack_nframes_t start, jack_nframes_t end, list<Selectable*>& results)
554 {
555         for (list<AudioRegionView*>::iterator i = region_views.begin(); i != region_views.end(); ++i) {
556                 if ((*i)->region.coverage(start, end) != OverlapNone) {
557                         results.push_back (*i);
558                 }
559         }
560 }
561
562 void
563 StreamView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
564 {
565         for (list<AudioRegionView*>::iterator i = region_views.begin(); i != region_views.end(); ++i) {
566                 if (!sel.audio_regions.contains (*i)) {
567                         results.push_back (*i);
568                 }
569         }
570 }
571
572 void
573 StreamView::set_waveform_shape (WaveformShape shape)
574 {
575         for (AudioRegionViewList::iterator i = region_views.begin(); i != region_views.end(); ++i) {
576                 (*i)->set_waveform_shape (shape);
577         }
578 }               
579                 
580 void
581 StreamView::region_layered (AudioRegionView* rv)
582 {
583         rv->get_canvas_group()->lower_to_bottom();
584
585         /* don't ever leave it at the bottom, since then it doesn't
586            get events - the  parent group does instead ...
587         */
588         
589         rv->get_canvas_group()->raise (rv->region.layer() + 1);
590 }
591
592 void
593 StreamView::rec_enable_changed (void *src)
594 {
595         Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &StreamView::setup_rec_box));
596 }
597
598 void
599 StreamView::sess_rec_enable_changed ()
600 {
601         Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &StreamView::setup_rec_box));
602 }
603
604 void
605 StreamView::transport_changed()
606 {
607         Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &StreamView::setup_rec_box));
608 }
609
610 void
611 StreamView::setup_rec_box ()
612 {
613         // cerr << _trackview.name() << " streamview SRB\n";
614
615         if (_trackview.session().transport_rolling()) {
616
617                 // cerr << "\trolling\n";
618
619                 if (!rec_active
620                     && _trackview.session().record_status() == Session::Recording
621                     && _trackview.get_diskstream()->record_enabled()) {
622
623                         if (use_rec_regions && rec_regions.size() == rec_rects.size()) {
624                                 /* add a new region, but don't bother if they set use_rec_regions mid-record */
625
626                                 AudioRegion::SourceList sources;
627
628                                 for (list<sigc::connection>::iterator prc = peak_ready_connections.begin(); prc != peak_ready_connections.end(); ++prc) {
629                                         (*prc).disconnect();
630                                 }
631                                 peak_ready_connections.clear();
632                                         
633                                 for (uint32_t n=0; n < _trackview.get_diskstream()->n_channels(); ++n) {
634                                         Source *src = (Source *) _trackview.get_diskstream()->write_source (n);
635                                         if (src) {
636                                                 sources.push_back (src);
637                                                 peak_ready_connections.push_back (src->PeakRangeReady.connect (bind (mem_fun (*this, &StreamView::rec_peak_range_ready), src))); 
638                                         }
639                                 }
640
641                                 // handle multi
642                                 
643                                 jack_nframes_t start = 0;
644                                 if (rec_regions.size() > 0) {
645                                         start = rec_regions.back()->start() + _trackview.get_diskstream()->get_captured_frames(rec_regions.size()-1);
646                                 }
647                                 
648                                 AudioRegion * region = new AudioRegion(sources, start, 1 , "", 0, (Region::Flag)(Region::DefaultFlags | Region::DoNotSaveState), false);
649                                 region->set_position (_trackview.session().transport_frame(), this);
650                                 rec_regions.push_back (region);
651                                 /* catch it if it goes away */
652                                 region->GoingAway.connect (mem_fun (*this, &StreamView::remove_rec_region));
653
654                                 /* we add the region later */
655                         }
656                         
657                         /* start a new rec box */
658
659                         AudioTrack* at;
660
661                         at = _trackview.audio_track(); /* we know what it is already */
662                         DiskStream& ds = at->disk_stream();
663                         jack_nframes_t frame_pos = ds.current_capture_start ();
664                         gdouble xstart = _trackview.editor.frame_to_pixel (frame_pos);
665                         gdouble xend = xstart;
666                         
667                         ArdourCanvas::SimpleRect * rec_rect = new Gnome::Canvas::SimpleRect (*canvas_group);
668                         rec_rect->property_x1() = xstart;
669                         rec_rect->property_y1() = 1.0;
670                         rec_rect->property_x2() = xend;
671                         rec_rect->property_y2() = (double) _trackview.height - 1;
672                         rec_rect->property_outline_color_rgba() = color_map[cRecordingRectOutline];
673                         rec_rect->property_fill_color_rgba() =  color_map[cRecordingRectFill];
674                         
675                         RecBoxInfo recbox;
676                         recbox.rectangle = rec_rect;
677                         recbox.start = _trackview.session().transport_frame();
678                         recbox.length = 0;
679                         
680                         rec_rects.push_back (recbox);
681                         
682                         screen_update_connection.disconnect();
683                         screen_update_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (mem_fun (*this, &StreamView::update_rec_box));        
684                         rec_updating = true;
685                         rec_active = true;
686
687                 } else if (rec_active &&
688                            (_trackview.session().record_status() != Session::Recording ||
689                             !_trackview.get_diskstream()->record_enabled())) {
690
691                         screen_update_connection.disconnect();
692                         rec_active = false;
693                         rec_updating = false;
694
695                 }
696                 
697         } else {
698
699                 // cerr << "\tNOT rolling, rec_rects = " << rec_rects.size() << " rec_regions = " << rec_regions.size() << endl;
700
701                 if (!rec_rects.empty() || !rec_regions.empty()) {
702
703                         /* disconnect rapid update */
704                         screen_update_connection.disconnect();
705
706                         for (list<sigc::connection>::iterator prc = peak_ready_connections.begin(); prc != peak_ready_connections.end(); ++prc) {
707                                 (*prc).disconnect();
708                         }
709                         peak_ready_connections.clear();
710
711                         rec_updating = false;
712                         rec_active = false;
713                         last_rec_peak_frame = 0;
714                         
715                         /* remove temp regions */
716                         for (list<AudioRegion*>::iterator iter=rec_regions.begin(); iter != rec_regions.end(); )
717                         {
718                                 list<AudioRegion*>::iterator tmp;
719
720                                 tmp = iter;
721                                 ++tmp;
722
723                                 /* this will trigger the remove_region_view */
724                                 delete *iter;
725
726                                 iter = tmp;
727                         }
728                         
729                         rec_regions.clear();
730
731                         // cerr << "\tclear " << rec_rects.size() << " rec rects\n";
732                 
733
734                         /* transport stopped, clear boxes */
735                         for (vector<RecBoxInfo>::iterator iter=rec_rects.begin(); iter != rec_rects.end(); ++iter) {
736                                 RecBoxInfo &rect = (*iter);
737                                 delete rect.rectangle;
738                         }
739                         
740                         rec_rects.clear();
741                         
742                 }
743         }
744 }
745
746
747 void
748 StreamView::update_rec_box ()
749 {
750         /* only update the last box */
751         if (rec_active && rec_rects.size() > 0) {
752                 RecBoxInfo & rect = rec_rects.back();
753                 jack_nframes_t at = _trackview.get_diskstream()->current_capture_end();
754                 
755                 rect.length = at - rect.start;
756
757                 gdouble xstart = _trackview.editor.frame_to_pixel ( rect.start );
758                 gdouble xend = _trackview.editor.frame_to_pixel ( at );
759
760                 rect.rectangle->property_x1() = xstart;
761                 rect.rectangle->property_x2() = xend;
762         }
763 }
764
765 AudioRegionView*
766 StreamView::find_view (const AudioRegion& region)
767 {
768         for (list<AudioRegionView*>::iterator i = region_views.begin(); i != region_views.end(); ++i) {
769
770                 if (&(*i)->region == &region) {
771                         return *i;
772                 }
773         }
774         return 0;
775 }
776         
777 void
778 StreamView::foreach_regionview (sigc::slot<void,AudioRegionView*> slot)
779 {
780         for (list<AudioRegionView*>::iterator i = region_views.begin(); i != region_views.end(); ++i) {
781                 slot (*i);
782         }
783 }
784
785 void
786 StreamView::foreach_crossfadeview (void (CrossfadeView::*pmf)(void))
787 {
788         for (list<CrossfadeView*>::iterator i = crossfade_views.begin(); i != crossfade_views.end(); ++i) {
789                 ((*i)->*pmf) ();
790         }
791 }
792
793 void
794 StreamView::rec_peak_range_ready (jack_nframes_t start, jack_nframes_t cnt, Source * src)
795 {
796         // this is called from the peak building thread
797
798         ENSURE_GUI_THREAD(bind (mem_fun (*this, &StreamView::rec_peak_range_ready), start, cnt, src));
799         
800         if (rec_peak_ready_map.size() == 0 || start+cnt > last_rec_peak_frame) {
801                 last_rec_peak_frame = start + cnt;
802         }
803
804         rec_peak_ready_map[src] = true;
805
806         if (rec_peak_ready_map.size() == _trackview.get_diskstream()->n_channels()) {
807                 this->update_rec_regions ();
808                 rec_peak_ready_map.clear();
809         }
810 }
811
812 void
813 StreamView::update_rec_regions ()
814 {
815         if (use_rec_regions) {
816
817                 uint32_t n = 0;
818
819                 for (list<AudioRegion*>::iterator iter = rec_regions.begin(); iter != rec_regions.end(); n++) {
820
821                         list<AudioRegion*>::iterator tmp;
822
823                         tmp = iter;
824                         ++tmp;
825
826                         if (!canvas_item_visible (rec_rects[n].rectangle)) {
827                                 /* rect already hidden, this region is done */
828                                 iter = tmp;
829                                 continue;
830                         }
831                         
832                         AudioRegion * region = (*iter);
833                         jack_nframes_t origlen = region->length();
834
835                         if (region == rec_regions.back() && rec_active) {
836
837                                 if (last_rec_peak_frame > region->start()) {
838
839                                         jack_nframes_t nlen = last_rec_peak_frame - region->start();
840
841                                         if (nlen != region->length()) {
842
843                                                 region->freeze ();
844                                                 region->set_position (_trackview.get_diskstream()->get_capture_start_frame(n), this);
845                                                 region->set_length (nlen, this);
846                                                 region->thaw ("updated");
847
848                                                 if (origlen == 1) {
849                                                         /* our special initial length */
850                                                         add_region_view_internal (region, false);
851                                                 }
852
853                                                 /* also update rect */
854                                                 ArdourCanvas::SimpleRect * rect = rec_rects[n].rectangle;
855                                                 gdouble xend = _trackview.editor.frame_to_pixel (region->position() + region->length());
856                                                 rect->property_x2() = xend;
857                                         }
858                                 }
859
860                         } else {
861
862                                 jack_nframes_t nlen = _trackview.get_diskstream()->get_captured_frames(n);
863
864                                 if (nlen != region->length()) {
865
866                                         if (region->source(0).length() >= region->start() + nlen) {
867
868                                                 region->freeze ();
869                                                 region->set_position (_trackview.get_diskstream()->get_capture_start_frame(n), this);
870                                                 region->set_length (nlen, this);
871                                                 region->thaw ("updated");
872                                                 
873                                                 if (origlen == 1) {
874                                                         /* our special initial length */
875                                                         add_region_view_internal (region, false);
876                                                 }
877                                                 
878                                                 /* also hide rect */
879                                                 ArdourCanvas::Item * rect = rec_rects[n].rectangle;
880                                                 rect->hide();
881
882                                         }
883                                 }
884                         }
885
886                         iter = tmp;
887                 }
888         }
889 }
890
891 void
892 StreamView::show_all_xfades ()
893 {
894         foreach_crossfadeview (&CrossfadeView::show);
895         crossfades_visible = true;
896 }
897
898 void
899 StreamView::hide_all_xfades ()
900 {
901         foreach_crossfadeview (&CrossfadeView::hide);
902         crossfades_visible = false;
903 }
904
905 void
906 StreamView::hide_xfades_involving (AudioRegionView& rv)
907 {
908         for (list<CrossfadeView *>::iterator i = crossfade_views.begin(); i != crossfade_views.end(); ++i) {
909                 if ((*i)->crossfade.involves (rv.region)) {
910                         (*i)->fake_hide ();
911                 }
912         }
913 }
914
915 void
916 StreamView::reveal_xfades_involving (AudioRegionView& rv)
917 {
918         for (list<CrossfadeView *>::iterator i = crossfade_views.begin(); i != crossfade_views.end(); ++i) {
919                 if ((*i)->crossfade.involves (rv.region) && (*i)->visible()) {
920                         (*i)->show ();
921                 }
922         }
923 }
924
925 void
926 StreamView::color_handler (ColorID id, uint32_t val)
927 {
928         switch (id) {
929         case cAudioTrackBase:
930                 if (_trackview.is_audio_track()) {
931                         canvas_rect->property_fill_color_rgba() = val;
932                 } 
933                 break;
934         case cAudioBusBase:
935                 if (!_trackview.is_audio_track()) {
936                         canvas_rect->property_fill_color_rgba() = val;
937                 }
938                 break;
939         case cAudioTrackOutline:
940                 canvas_rect->property_outline_color_rgba() = val;
941                 break;
942
943         default:
944                 break;
945         }
946 }