Move Diskstream ownership to Track, so that Session no longer holds lists of Diskstre...
[ardour.git] / gtk2_ardour / streamview.cc
index 9b33244fad24401d6245cf9f37b1b1eb908f5159..8fa9ed8d288298dbd7e6365967c80bd7a8aadec5 100644 (file)
+/*
+    Copyright (C) 2001, 2006 Paul Davis
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
 #include <cmath>
 
 #include <gtkmm.h>
 
 #include <gtkmm2ext/gtk_ui.h>
 
-#include <ardour/audioplaylist.h>
-#include <ardour/audioregion.h>
-#include <ardour/audiosource.h>
-#include <ardour/audio_diskstream.h>
-#include <ardour/audio_track.h>
-#include <ardour/playlist_templates.h>
-#include <ardour/source.h>
+#include "ardour/playlist.h"
+#include "ardour/region.h"
+#include "ardour/source.h"
+#include "ardour/track.h"
+#include "ardour/session.h"
 
 #include "streamview.h"
-#include "regionview.h"
-#include "taperegionview.h"
-#include "audio_time_axis.h"
+#include "region_view.h"
+#include "route_time_axis.h"
 #include "canvas-waveview.h"
 #include "canvas-simplerect.h"
 #include "region_selection.h"
 #include "selection.h"
 #include "public_editor.h"
 #include "ardour_ui.h"
-#include "crossfade_view.h"
 #include "rgb_macros.h"
 #include "gui_thread.h"
 #include "utils.h"
-#include "color.h"
 
+using namespace std;
 using namespace ARDOUR;
 using namespace PBD;
 using namespace Editing;
 
-StreamView::StreamView (AudioTimeAxisView& tv)
+StreamView::StreamView (RouteTimeAxisView& tv, ArdourCanvas::Group* group)
        : _trackview (tv)
+       , owns_canvas_group(group == 0)
+       , _background_group (new ArdourCanvas::Group (*_trackview.canvas_background()))
+       , canvas_group(group ? group : new ArdourCanvas::Group(*_trackview.canvas_display()))
+       , _samples_per_unit (_trackview.editor().get_current_zoom ())
+       , rec_updating(false)
+       , rec_active(false)
+       , region_color(_trackview.color())
+       , stream_base_color(0xFFFFFFFF)
+       , _layers (1)
+       , _layer_display (Overlaid)
+       , height(tv.height)
+       , last_rec_data_frame(0)
 {
-       region_color = _trackview.color();
-       crossfades_visible = true;
-
-       if (tv.is_audio_track()) {
-               /* TRACK */
-               //stream_base_color = RGBA_TO_UINT (222,223,218,255);
-               stream_base_color = color_map[cAudioTrackBase];
-       } else {
-               /* BUS */
-               //stream_base_color = RGBA_TO_UINT (230,226,238,255);
-               stream_base_color = color_map[cAudioBusBase];
-       }
-
        /* set_position() will position the group */
 
-       canvas_group = new ArdourCanvas::Group(*_trackview.canvas_display);
-
-       canvas_rect = new ArdourCanvas::SimpleRect (*canvas_group);
+       canvas_rect = new ArdourCanvas::SimpleRect (*_background_group);
        canvas_rect->property_x1() = 0.0;
        canvas_rect->property_y1() = 0.0;
-       canvas_rect->property_x2() = 1000000.0;
-       canvas_rect->property_y2() = (double) tv.height;
-       canvas_rect->property_outline_color_rgba() = color_map[cAudioTrackOutline];
-       canvas_rect->property_outline_what() = (guint32) (0x1|0x2|0x8);  // outline ends and bottom 
-       canvas_rect->property_fill_color_rgba() = stream_base_color;
-
-       canvas_rect->signal_event().connect (bind (mem_fun (_trackview.editor, &PublicEditor::canvas_stream_view_event), canvas_rect, &_trackview));
-
-       _samples_per_unit = _trackview.editor.get_current_zoom();
-       _amplitude_above_axis = 1.0;
-
-       if (_trackview.is_audio_track()) {
-               _trackview.audio_track()->diskstream_changed.connect (mem_fun (*this, &StreamView::diskstream_changed));
-               _trackview.session().TransportStateChange.connect (mem_fun (*this, &StreamView::transport_changed));
-               _trackview.get_diskstream()->record_enable_changed.connect (mem_fun (*this, &StreamView::rec_enable_changed));
-               _trackview.session().RecordStateChanged.connect (mem_fun (*this, &StreamView::sess_rec_enable_changed));
-       } 
+       canvas_rect->property_x2() = _trackview.editor().get_physical_screen_width ();
+       canvas_rect->property_y2() = (double) tv.current_height();
+       canvas_rect->raise(1); // raise above tempo lines
 
-       rec_updating = false;
-       rec_active = false;
-       use_rec_regions = tv.editor.show_waveforms_recording ();
-       last_rec_peak_frame = 0;
+       canvas_rect->property_outline_what() = (guint32) (0x2|0x8);  // outline RHS and bottom
+
+       canvas_rect->signal_event().connect (sigc::bind (
+                       sigc::mem_fun (_trackview.editor(), &PublicEditor::canvas_stream_view_event),
+                       canvas_rect, &_trackview));
 
-       ColorChanged.connect (mem_fun (*this, &StreamView::color_handler));
+       if (_trackview.is_track()) {
+               _trackview.track()->DiskstreamChanged.connect (*this, invalidator (*this), boost::bind (&StreamView::diskstream_changed, this), gui_context());
+               _trackview.track()->RecordEnableChanged.connect (*this, invalidator (*this), boost::bind (&StreamView::rec_enable_changed, this), gui_context());
+
+               _trackview.session()->TransportStateChange.connect (*this, invalidator (*this), boost::bind (&StreamView::transport_changed, this), gui_context());
+               _trackview.session()->TransportLooped.connect (*this, invalidator (*this), boost::bind (&StreamView::transport_looped, this), gui_context());
+               _trackview.session()->RecordStateChanged.connect (*this, invalidator (*this), boost::bind (&StreamView::sess_rec_enable_changed, this), gui_context());
+       }
+
+       ColorsChanged.connect (sigc::mem_fun (*this, &StreamView::color_handler));
 }
 
 StreamView::~StreamView ()
 {
-       undisplay_diskstream ();
-       delete canvas_group;
+       undisplay_track ();
+
+       delete canvas_rect;
+
+       if (owns_canvas_group) {
+               delete canvas_group;
+       }
 }
 
 void
 StreamView::attach ()
 {
-       if (_trackview.is_audio_track()) {
-               display_diskstream (_trackview.get_diskstream());
+       if (_trackview.is_track()) {
+               display_track (_trackview.track ());
        }
 }
 
 int
 StreamView::set_position (gdouble x, gdouble y)
-
 {
        canvas_group->property_x() = x;
        canvas_group->property_y() = y;
@@ -105,36 +116,30 @@ StreamView::set_position (gdouble x, gdouble y)
 }
 
 int
-StreamView::set_height (gdouble h)
+StreamView::set_height (double h)
 {
        /* limit the values to something sane-ish */
-
        if (h < 10.0 || h > 1000.0) {
                return -1;
        }
 
-       canvas_rect->property_y2() = h;
-
-       for (AudioRegionViewList::iterator i = region_views.begin(); i != region_views.end(); ++i) {
-               (*i)->set_height (h);
+       if (canvas_rect->property_y2() == h) {
+               return 0;
        }
 
-       for (CrossfadeViewList::iterator i = crossfade_views.begin(); i != crossfade_views.end(); ++i) {
-               (*i)->set_height (h);
-       }
+       height = h;
+       canvas_rect->property_y2() = height;
+       update_contents_height ();
 
-       for (vector<RecBoxInfo>::iterator i = rec_rects.begin(); i != rec_rects.end(); ++i) {
-               RecBoxInfo &recbox = (*i);
-               recbox.rectangle->property_y2() = h - 1.0;
-       }
+       HeightChanged ();
 
        return 0;
 }
 
-int 
+int
 StreamView::set_samples_per_unit (gdouble spp)
 {
-       AudioRegionViewList::iterator i;
+       RegionViewList::iterator i;
 
        if (spp < 1.0) {
                return -1;
@@ -146,370 +151,207 @@ StreamView::set_samples_per_unit (gdouble spp)
                (*i)->set_samples_per_unit (spp);
        }
 
-       for (CrossfadeViewList::iterator xi = crossfade_views.begin(); xi != crossfade_views.end(); ++xi) {
-               (*xi)->set_samples_per_unit (spp);
-       }
-
        for (vector<RecBoxInfo>::iterator xi = rec_rects.begin(); xi != rec_rects.end(); ++xi) {
                RecBoxInfo &recbox = (*xi);
-               
-               gdouble xstart = _trackview.editor.frame_to_pixel ( recbox.start );
-               gdouble xend = _trackview.editor.frame_to_pixel ( recbox.start + recbox.length );
+
+               gdouble xstart = _trackview.editor().frame_to_pixel (recbox.start);
+               gdouble xend = _trackview.editor().frame_to_pixel (recbox.start + recbox.length);
 
                recbox.rectangle->property_x1() = xstart;
                recbox.rectangle->property_x2() = xend;
        }
 
-       return 0;
-}
-
-int 
-StreamView::set_amplitude_above_axis (gdouble app)
-
-{
-       AudioRegionViewList::iterator i;
-
-       if (app < 1.0) {
-               return -1;
-       }
-
-       _amplitude_above_axis = app;
-
-       for (i = region_views.begin(); i != region_views.end(); ++i) {
-               (*i)->set_amplitude_above_axis (app);
-       }
+       update_coverage_frames ();
 
        return 0;
 }
 
 void
-StreamView::add_region_view (Region *r)
-{
-       add_region_view_internal (r, true);
-}
-
-void
-StreamView::add_region_view_internal (Region *r, bool wait_for_waves)
+StreamView::add_region_view (boost::weak_ptr<Region> wr)
 {
-       ENSURE_GUI_THREAD (bind (mem_fun (*this, &StreamView::add_region_view), r));
-
-       AudioRegion* region = dynamic_cast<AudioRegion*> (r);
-
-       if (region == 0) {
+       boost::shared_ptr<Region> r (wr.lock());
+       if (!r) {
                return;
        }
 
-       AudioRegionView *region_view;
-       list<AudioRegionView *>::iterator i;
-
-       for (i = region_views.begin(); i != region_views.end(); ++i) {
-               if (&(*i)->region == region) {
-                       
-                       /* great. we already have a AudioRegionView for this Region. use it again.
-                        */
+       add_region_view_internal (r, true);
 
-                       (*i)->set_valid (true);
-                       return;
-               }
+       if (_layer_display == Stacked) {
+               update_contents_height ();
        }
-       
-       switch (_trackview.audio_track()->mode()) {
-       case Normal:
-               region_view = new AudioRegionView (canvas_group, _trackview, *region, 
-                                                  _samples_per_unit, region_color);
-               break;
-       case Destructive:
-               region_view = new TapeAudioRegionView (canvas_group, _trackview, *region, 
-                                                      _samples_per_unit, region_color);
-               break;
-       }
-
-       region_view->init (_amplitude_above_axis, region_color, wait_for_waves);
-       region_views.push_front (region_view);
-       
-       /* follow global waveform setting */
-
-       region_view->set_waveform_visible(_trackview.editor.show_waveforms());
-
-       /* catch regionview going away */
-
-       region->GoingAway.connect (mem_fun (*this, &StreamView::remove_region_view));
-       
-       AudioRegionViewAdded (region_view);
 }
 
 void
-StreamView::remove_region_view (Region *r)
+StreamView::remove_region_view (boost::weak_ptr<Region> weak_r)
 {
-       ENSURE_GUI_THREAD (bind (mem_fun (*this, &StreamView::remove_region_view), r));
+       ENSURE_GUI_THREAD (*this, &StreamView::remove_region_view, weak_r)
 
-       AudioRegion* ar = dynamic_cast<AudioRegion*> (r);
+       boost::shared_ptr<Region> r (weak_r.lock());
 
-       if (ar == 0) {
+       if (!r) {
                return;
        }
 
-       for (list<AudioRegionView *>::iterator i = region_views.begin(); i != region_views.end(); ++i) {
-               if (&((*i)->region) == ar) {
-                       delete *i;
+       for (list<RegionView *>::iterator i = region_views.begin(); i != region_views.end(); ++i) {
+               if (((*i)->region()) == r) {
+                       RegionView* rv = *i;
                        region_views.erase (i);
+                       delete rv;
                        break;
                }
        }
-
-       for (list<CrossfadeView *>::iterator i = crossfade_views.begin(); i != crossfade_views.end();) {
-               list<CrossfadeView*>::iterator tmp;
-               
-               tmp = i;
-               ++tmp;
-               
-               if ((*i)->crossfade.involves (*ar)) {
-                       delete *i;
-                       crossfade_views.erase (i);
-               }
-               
-               i = tmp;
-       }
 }
 
 void
-StreamView::remove_rec_region (Region *r)
+StreamView::undisplay_track ()
 {
-       ENSURE_GUI_THREAD(bind (mem_fun (*this, &StreamView::remove_rec_region), r));
-       
-       if (!Gtkmm2ext::UI::instance()->caller_is_ui_thread()) {
-               fatal << "region deleted from non-GUI thread!" << endmsg;
-               /*NOTREACHED*/
-       } 
-
-       AudioRegion* ar = dynamic_cast<AudioRegion*> (r);
-
-       if (ar == 0) {
-               return;
-       }
-
-       for (list<AudioRegion *>::iterator i = rec_regions.begin(); i != rec_regions.end(); ++i) {
-               if (*i == ar) {
-                       rec_regions.erase (i);
-                       break;
-               }
-       }
-}
-
-void
-StreamView::undisplay_diskstream ()
-{
-       
-       for (AudioRegionViewList::iterator i = region_views.begin(); i != region_views.end(); ++i) {
-               delete *i;
-       }
-
-       for (CrossfadeViewList::iterator i = crossfade_views.begin(); i != crossfade_views.end(); ++i) {
+       for (RegionViewList::iterator i = region_views.begin(); i != region_views.end() ; ) {
+               RegionViewList::iterator next = i;
+               ++next;
                delete *i;
+               i = next;
        }
 
        region_views.clear();
-       crossfade_views.clear ();
 }
 
 void
-StreamView::display_diskstream (AudioDiskstream *ds)
+StreamView::display_track (boost::shared_ptr<Track> tr)
 {
-       playlist_change_connection.disconnect();
-       playlist_changed (ds);
-       playlist_change_connection = ds->PlaylistChanged.connect (bind (mem_fun (*this, &StreamView::playlist_changed), ds));
+       playlist_switched_connection.disconnect();
+       playlist_switched (tr);
+       tr->PlaylistChanged.connect (playlist_switched_connection, invalidator (*this), boost::bind (&StreamView::playlist_switched, this, boost::weak_ptr<Track> (tr)), gui_context());
 }
 
 void
-StreamView::playlist_modified ()
+StreamView::layer_regions()
 {
-       ENSURE_GUI_THREAD (mem_fun (*this, &StreamView::playlist_modified));
-
-       /* if the playlist is modified, make sure xfades are on top and all the regionviews are stacked 
-          correctly.
-       */
-
-       for (AudioRegionViewList::iterator i = region_views.begin(); i != region_views.end(); ++i) {
-               region_layered (*i);
-       }
-
-       for (list<CrossfadeView *>::iterator i = crossfade_views.begin(); i != crossfade_views.end(); ++i) {
-               (*i)->get_canvas_group()->raise_to_top();
-       }
-}
-
-void
-StreamView::playlist_changed (AudioDiskstream *ds)
-{
-       ENSURE_GUI_THREAD (bind (mem_fun (*this, &StreamView::playlist_changed), ds));
-
-       /* disconnect from old playlist */
-
-       for (vector<sigc::connection>::iterator i = playlist_connections.begin(); i != playlist_connections.end(); ++i) {
-               (*i).disconnect();
-       }
-       
-       playlist_connections.clear();
-       undisplay_diskstream ();
-
-       /* draw it */
-
-       redisplay_diskstream ();
-
-       /* catch changes */
-
-       playlist_connections.push_back (ds->playlist()->RegionAdded.connect (mem_fun (*this, &StreamView::add_region_view)));
-       playlist_connections.push_back (ds->playlist()->RegionRemoved.connect (mem_fun (*this, &StreamView::remove_region_view)));
-       playlist_connections.push_back (ds->playlist()->StateChanged.connect (mem_fun (*this, &StreamView::playlist_state_changed)));
-       playlist_connections.push_back (ds->playlist()->Modified.connect (mem_fun (*this, &StreamView::playlist_modified)));
-       AudioPlaylist* apl = dynamic_cast<AudioPlaylist*>(ds->playlist());
-       if (apl)
-               playlist_connections.push_back (apl->NewCrossfade.connect (mem_fun (*this, &StreamView::add_crossfade)));
-}
+       // In one traversal of the region view list:
+       // - Build a list of region views sorted by layer
+       // - Remove invalid views from the actual region view list
+       RegionViewList copy;
+       list<RegionView*>::iterator i, tmp;
+       for (i = region_views.begin(); i != region_views.end(); ) {
+               tmp = i;
+               tmp++;
 
-void
-StreamView::add_crossfade (Crossfade *crossfade)
-{
-       AudioRegionView* lview = 0;
-       AudioRegionView* rview = 0;
+               if (!(*i)->is_valid()) {
+                       delete *i;
+                       region_views.erase (i);
+                       i = tmp;
+                       continue;
+               } else {
+                       (*i)->enable_display(true);
+               }
 
-       ENSURE_GUI_THREAD (bind (mem_fun (*this, &StreamView::add_crossfade), crossfade));
+               if (copy.size() == 0) {
+                       copy.push_front((*i));
+                       i = tmp;
+                       continue;
+               }
 
-       /* first see if we already have a CrossfadeView for this Crossfade */
+               RegionViewList::iterator k = copy.begin();
+               RegionViewList::iterator l = copy.end();
+               l--;
+
+               if ((*i)->region()->layer() <= (*k)->region()->layer()) {
+                       copy.push_front((*i));
+                       i = tmp;
+                       continue;
+               } else if ((*i)->region()->layer() >= (*l)->region()->layer()) {
+                       copy.push_back((*i));
+                       i = tmp;
+                       continue;
+               }
 
-       for (list<CrossfadeView *>::iterator i = crossfade_views.begin(); i != crossfade_views.end(); ++i) {
-               if (&(*i)->crossfade == crossfade) {
-                       if (!crossfades_visible) {
-                               (*i)->hide();
-                       } else {
-                               (*i)->show ();
+               for (RegionViewList::iterator j = copy.begin(); j != copy.end(); ++j) {
+                       if ((*j)->region()->layer() >= (*i)->region()->layer()) {
+                               copy.insert(j, (*i));
+                               break;
                        }
-                       (*i)->set_valid (true);
-                       return;
                }
-       }
-
-       /* create a new one */
 
-       for (list<AudioRegionView *>::iterator i = region_views.begin(); i != region_views.end(); ++i) {
-               if (!lview && &((*i)->region) == &crossfade->out()) {
-                       lview = *i;
-               }
-               if (!rview && &((*i)->region) == &crossfade->in()) {
-                       rview = *i;
-               }
+               i = tmp;
        }
 
-       CrossfadeView *cv = new CrossfadeView (_trackview.canvas_display,
-                                              _trackview,
-                                              *crossfade,
-                                              _samples_per_unit,
-                                              region_color,
-                                              *lview, *rview);
-
-       crossfade->Invalidated.connect (mem_fun (*this, &StreamView::remove_crossfade));
-       crossfade_views.push_back (cv);
-
-       if (!crossfades_visible) {
-               cv->hide ();
+       // Fix canvas layering by raising each to the top in the sorted order.
+       for (RegionViewList::iterator i = copy.begin(); i != copy.end(); ++i) {
+               (*i)->get_canvas_group()->raise_to_top ();
        }
 }
 
 void
-StreamView::remove_crossfade (Crossfade *xfade)
+StreamView::playlist_layered (boost::weak_ptr<Track> wtr)
 {
-       ENSURE_GUI_THREAD (bind (mem_fun (*this, &StreamView::remove_crossfade), xfade));
+       boost::shared_ptr<Track> tr (wtr.lock());
 
-       for (list<CrossfadeView*>::iterator i = crossfade_views.begin(); i != crossfade_views.end(); ++i) {
-               if (&(*i)->crossfade == xfade) {
-                       delete *i;
-                       crossfade_views.erase (i);
-                       break;
-               }
+       if (!tr) {
+               return;
        }
-}
 
-void
-StreamView::playlist_state_changed (Change ignored)
-{
-       ENSURE_GUI_THREAD (bind (mem_fun (*this, &StreamView::playlist_state_changed), ignored));
+       /* update layers count and the y positions and heights of our regions */
+       if (tr->playlist()) {
+               _layers = tr->playlist()->top_layer() + 1;
+       }
 
-       redisplay_diskstream ();
+       if (_layer_display == Stacked) {
+               update_contents_height ();
+               update_coverage_frames ();
+       } else {
+               /* layering has probably been modified. reflect this in the canvas. */
+               layer_regions();
+       } 
 }
 
 void
-StreamView::redisplay_diskstream ()
+StreamView::playlist_switched (boost::weak_ptr<Track> wtr)
 {
-       list<AudioRegionView *>::iterator i, tmp;
-       list<CrossfadeView*>::iterator xi, tmpx;
+       boost::shared_ptr<Track> tr (wtr.lock());
 
-       
-       for (i = region_views.begin(); i != region_views.end(); ++i) {
-               (*i)->set_valid (false);
-       }
-
-       for (xi = crossfade_views.begin(); xi != crossfade_views.end(); ++xi) {
-               (*xi)->set_valid (false);
-               if ((*xi)->visible()) {
-                       (*xi)->show ();
-               }
-       }
-
-       if (_trackview.is_audio_track()) {
-               _trackview.get_diskstream()->playlist()->foreach_region (this, &StreamView::add_region_view);
-               AudioPlaylist* apl = dynamic_cast<AudioPlaylist*>(_trackview.get_diskstream()->playlist());
-               if (apl)
-                       apl->foreach_crossfade (this, &StreamView::add_crossfade);
+       if (!tr) {
+               return;
        }
 
-       for (i = region_views.begin(); i != region_views.end(); ) {
-               tmp = i;
-               tmp++;
+       /* disconnect from old playlist */
 
-               if (!(*i)->is_valid()) {
-                       delete *i;
-                       region_views.erase (i);
-               } 
+       playlist_connections.drop_connections ();
+       undisplay_track ();
 
-               i = tmp;
-       }
+       /* update layers count and the y positions and heights of our regions */
+       _layers = tr->playlist()->top_layer() + 1;
+       update_contents_height ();
+       update_coverage_frames ();
 
-       for (xi = crossfade_views.begin(); xi != crossfade_views.end();) {
-               tmpx = xi;
-               tmpx++;
+       tr->playlist()->set_explicit_relayering (_layer_display == Stacked);
 
-               if (!(*xi)->valid()) {
-                       delete *xi;
-                       crossfade_views.erase (xi);
-               }
+       /* draw it */
 
-               xi = tmpx;
-       }
+       redisplay_track ();
 
-       /* now fix layering */
+       /* catch changes */
 
-       playlist_modified ();
+       tr->playlist()->LayeringChanged.connect (playlist_connections, invalidator (*this), boost::bind (&StreamView::playlist_layered, this, boost::weak_ptr<Track> (tr)), gui_context());
+       tr->playlist()->RegionAdded.connect (playlist_connections, invalidator (*this), ui_bind (&StreamView::add_region_view, this, _1), gui_context());
+       tr->playlist()->RegionRemoved.connect (playlist_connections, invalidator (*this), ui_bind (&StreamView::remove_region_view, this, _1), gui_context());
+       // ds->playlist()->ContentsChanged.connect (playlist_connections, invalidator (*this), boost::bind (&StreamView::redisplay_diskstream, this), gui_context());
 }
 
 void
-StreamView::diskstream_changed (void *src_ignored)
+StreamView::diskstream_changed ()
 {
-       AudioTrack *at;
-
-       if ((at = _trackview.audio_track()) != 0) {
-               AudioDiskstream& ds = at->disk_stream();
-               /* XXX grrr: when will SigC++ allow me to bind references? */
-               Gtkmm2ext::UI::instance()->call_slot (bind (mem_fun (*this, &StreamView::display_diskstream), &ds));
+       boost::shared_ptr<Track> t;
+        
+       if ((t = _trackview.track()) != 0) {
+               Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&StreamView::display_track, this, t));
        } else {
-               Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &StreamView::undisplay_diskstream));
+               Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&StreamView::undisplay_track, this));
        }
 }
 
 void
 StreamView::apply_color (Gdk::Color& color, ColorTarget target)
-
 {
-       list<AudioRegionView *>::iterator i;
+       list<RegionView *>::iterator i;
 
        switch (target) {
        case RegionColor:
@@ -517,467 +359,229 @@ StreamView::apply_color (Gdk::Color& color, ColorTarget target)
                for (i = region_views.begin(); i != region_views.end(); ++i) {
                        (*i)->set_color (region_color);
                }
-               // stream_base_color = RGBA_TO_UINT (color.red/256, color.green/256, color.blue/256, 255);
-               // gnome_canvas_item_set (canvas_rect, "fill_color_rgba", stream_base_color, NULL);
                break;
-               
+
        case StreamBaseColor:
-               // stream_base_color = RGBA_TO_UINT (color.red/256, color.green/256, color.blue/256, 255);
-               // gnome_canvas_item_set (canvas_rect, "fill_color_rgba", stream_base_color, NULL);
+               stream_base_color = RGBA_TO_UINT (
+                       color.get_red_p(), color.get_green_p(), color.get_blue_p(), 255);
+               canvas_rect->property_fill_color_rgba() = stream_base_color;
                break;
        }
 }
 
 void
-StreamView::set_show_waveforms (bool yn)
-{
-       for (list<AudioRegionView *>::iterator i = region_views.begin(); i != region_views.end(); ++i) {
-                       (*i)->set_waveform_visible (yn);
-       }
-}
-
-void
-StreamView::set_selected_regionviews (AudioRegionSelection& regions)
-{
-       bool selected;
-
-       // cerr << _trackview.name() << " (selected = " << regions.size() << ")" << endl;
-       for (list<AudioRegionView*>::iterator i = region_views.begin(); i != region_views.end(); ++i) {
-               
-               selected = false;
-               
-               for (AudioRegionSelection::iterator ii = regions.begin(); ii != regions.end(); ++ii) {
-                       if (*i == *ii) {
-                               selected = true;
-                       }
-               }
-               
-               // cerr << "\tregion " << (*i)->region.name() << " selected = " << selected << endl;
-               (*i)->set_selected (selected);
-       }
-}
-
-void
-StreamView::get_selectables (jack_nframes_t start, jack_nframes_t end, list<Selectable*>& results)
+StreamView::region_layered (RegionView* rv)
 {
-       for (list<AudioRegionView*>::iterator i = region_views.begin(); i != region_views.end(); ++i) {
-               if ((*i)->region.coverage(start, end) != OverlapNone) {
-                       results.push_back (*i);
-               }
-       }
-}
-
-void
-StreamView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
-{
-       for (list<AudioRegionView*>::iterator i = region_views.begin(); i != region_views.end(); ++i) {
-               if (!sel.audio_regions.contains (*i)) {
-                       results.push_back (*i);
-               }
-       }
-}
-
-void
-StreamView::set_waveform_shape (WaveformShape shape)
-{
-       for (AudioRegionViewList::iterator i = region_views.begin(); i != region_views.end(); ++i) {
-               (*i)->set_waveform_shape (shape);
-       }
-}              
-               
-void
-StreamView::region_layered (AudioRegionView* rv)
-{
-       rv->get_canvas_group()->lower_to_bottom();
-
        /* don't ever leave it at the bottom, since then it doesn't
           get events - the  parent group does instead ...
        */
-       
-       rv->get_canvas_group()->raise (rv->region.layer() + 1);
+       rv->get_canvas_group()->raise (rv->region()->layer());
 }
 
 void
-StreamView::rec_enable_changed (void *src)
+StreamView::rec_enable_changed ()
 {
-       Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &StreamView::setup_rec_box));
+       setup_rec_box ();
 }
 
 void
 StreamView::sess_rec_enable_changed ()
 {
-       Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &StreamView::setup_rec_box));
+       setup_rec_box ();
 }
 
 void
 StreamView::transport_changed()
 {
-       Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &StreamView::setup_rec_box));
+       setup_rec_box ();
 }
 
 void
-StreamView::setup_rec_box ()
-{
-       // cerr << _trackview.name() << " streamview SRB\n";
-
-       if (_trackview.session().transport_rolling()) {
-
-               // cerr << "\trolling\n";
-
-               if (!rec_active && 
-                   _trackview.session().record_status() == Session::Recording && 
-                   _trackview.get_diskstream()->record_enabled()) {
-
-                       if (_trackview.audio_track()->mode() == Normal && use_rec_regions && rec_regions.size() == rec_rects.size()) {
-
-                               /* add a new region, but don't bother if they set use_rec_regions mid-record */
-
-                               AudioRegion::SourceList sources;
-
-                               for (list<sigc::connection>::iterator prc = peak_ready_connections.begin(); prc != peak_ready_connections.end(); ++prc) {
-                                       (*prc).disconnect();
-                               }
-                               peak_ready_connections.clear();
-                                       
-                               for (uint32_t n=0; n < _trackview.get_diskstream()->n_channels(); ++n) {
-                                       AudioSource *src = (AudioSource *) _trackview.get_diskstream()->write_source (n);
-                                       if (src) {
-                                               sources.push_back (src);
-                                               peak_ready_connections.push_back (src->PeakRangeReady.connect (bind (mem_fun (*this, &StreamView::rec_peak_range_ready), src))); 
-                                       }
-                               }
-
-                               // handle multi
-                               
-                               jack_nframes_t start = 0;
-                               if (rec_regions.size() > 0) {
-                                       start = rec_regions.back()->start() + _trackview.get_diskstream()->get_captured_frames(rec_regions.size()-1);
-                               }
-                               
-                               AudioRegion * region = new AudioRegion(sources, start, 1 , "", 0, (Region::Flag)(Region::DefaultFlags | Region::DoNotSaveState), false);
-                               region->set_position (_trackview.session().transport_frame(), this);
-                               rec_regions.push_back (region);
-                               /* catch it if it goes away */
-                               region->GoingAway.connect (mem_fun (*this, &StreamView::remove_rec_region));
-
-                               /* we add the region later */
-                       }
-                       
-                       /* start a new rec box */
-
-                       AudioTrack* at;
-
-                       at = _trackview.audio_track(); /* we know what it is already */
-                       AudioDiskstream& ds = at->disk_stream();
-                       jack_nframes_t frame_pos = ds.current_capture_start ();
-                       gdouble xstart = _trackview.editor.frame_to_pixel (frame_pos);
-                       gdouble xend;
-                       uint32_t fill_color;
-
-                       switch (_trackview.audio_track()->mode()) {
-                       case Normal:
-                               xend = xstart;
-                               fill_color = color_map[cRecordingRectFill];
-                               break;
-
-                       case Destructive:
-                               xend = xstart + 2;
-                               fill_color = color_map[cRecordingRectFill];
-                               /* make the recording rect translucent to allow
-                                  the user to see the peak data coming in, etc.
-                               */
-                               fill_color = UINT_RGBA_CHANGE_A (fill_color, 120);
-                               break;
-                       }
-                       
-                       ArdourCanvas::SimpleRect * rec_rect = new Gnome::Canvas::SimpleRect (*canvas_group);
-                       rec_rect->property_x1() = xstart;
-                       rec_rect->property_y1() = 1.0;
-                       rec_rect->property_x2() = xend;
-                       rec_rect->property_y2() = (double) _trackview.height - 1;
-                       rec_rect->property_outline_color_rgba() = color_map[cRecordingRectOutline];
-                       rec_rect->property_fill_color_rgba() = fill_color;
-                       
-                       RecBoxInfo recbox;
-                       recbox.rectangle = rec_rect;
-                       recbox.start = _trackview.session().transport_frame();
-                       recbox.length = 0;
-                       
-                       rec_rects.push_back (recbox);
-                       
-                       screen_update_connection.disconnect();
-                       screen_update_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (mem_fun (*this, &StreamView::update_rec_box));        
-                       rec_updating = true;
-                       rec_active = true;
-
-               } else if (rec_active &&
-                          (_trackview.session().record_status() != Session::Recording ||
-                           !_trackview.get_diskstream()->record_enabled())) {
-
-                       screen_update_connection.disconnect();
-                       rec_active = false;
-                       rec_updating = false;
-
-               }
-               
-       } else {
-
-               // cerr << "\tNOT rolling, rec_rects = " << rec_rects.size() << " rec_regions = " << rec_regions.size() << endl;
-
-               if (!rec_rects.empty() || !rec_regions.empty()) {
-
-                       /* disconnect rapid update */
-                       screen_update_connection.disconnect();
-
-                       for (list<sigc::connection>::iterator prc = peak_ready_connections.begin(); prc != peak_ready_connections.end(); ++prc) {
-                               (*prc).disconnect();
-                       }
-                       peak_ready_connections.clear();
-
-                       rec_updating = false;
-                       rec_active = false;
-                       last_rec_peak_frame = 0;
-                       
-                       /* remove temp regions */
-                       for (list<AudioRegion*>::iterator iter=rec_regions.begin(); iter != rec_regions.end(); )
-                       {
-                               list<AudioRegion*>::iterator tmp;
-
-                               tmp = iter;
-                               ++tmp;
-
-                               /* this will trigger the remove_region_view */
-                               delete *iter;
-
-                               iter = tmp;
-                       }
-                       
-                       rec_regions.clear();
-
-                       // cerr << "\tclear " << rec_rects.size() << " rec rects\n";
-               
-
-                       /* transport stopped, clear boxes */
-                       for (vector<RecBoxInfo>::iterator iter=rec_rects.begin(); iter != rec_rects.end(); ++iter) {
-                               RecBoxInfo &rect = (*iter);
-                               delete rect.rectangle;
-                       }
-                       
-                       rec_rects.clear();
-                       
-               }
-       }
+StreamView::transport_looped()
+{
+       // to force a new rec region
+       rec_active = false;
+       Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&StreamView::setup_rec_box, this));
 }
 
-
 void
 StreamView::update_rec_box ()
 {
        if (rec_active && rec_rects.size() > 0) {
                /* only update the last box */
                RecBoxInfo & rect = rec_rects.back();
-               jack_nframes_t at = _trackview.get_diskstream()->current_capture_end();
+               nframes_t at = _trackview.track()->current_capture_end();
                double xstart;
                double xend;
-               
-               switch (_trackview.audio_track()->mode()) {
+
+               switch (_trackview.track()->mode()) {
+
+               case NonLayered:
                case Normal:
                        rect.length = at - rect.start;
-                       xstart = _trackview.editor.frame_to_pixel (rect.start);
-                       xend = _trackview.editor.frame_to_pixel (at);
+                       xstart = _trackview.editor().frame_to_pixel (rect.start);
+                       xend = _trackview.editor().frame_to_pixel (at);
                        break;
-                       
+
                case Destructive:
                        rect.length = 2;
-                       xstart = _trackview.editor.frame_to_pixel (_trackview.get_diskstream()->current_capture_start());
-                       xend = _trackview.editor.frame_to_pixel (at);
+                       xstart = _trackview.editor().frame_to_pixel (_trackview.track()->current_capture_start());
+                       xend = _trackview.editor().frame_to_pixel (at);
                        break;
                }
-               
+
                rect.rectangle->property_x1() = xstart;
                rect.rectangle->property_x2() = xend;
        }
 }
-       
-AudioRegionView*
-StreamView::find_view (const AudioRegion& region)
+
+RegionView*
+StreamView::find_view (boost::shared_ptr<const Region> region)
 {
-       for (list<AudioRegionView*>::iterator i = region_views.begin(); i != region_views.end(); ++i) {
+       for (list<RegionView*>::iterator i = region_views.begin(); i != region_views.end(); ++i) {
 
-               if (&(*i)->region == &region) {
+               if ((*i)->region() == region) {
                        return *i;
                }
        }
        return 0;
 }
-       
-void
-StreamView::foreach_regionview (sigc::slot<void,AudioRegionView*> slot)
+
+uint32_t
+StreamView::num_selected_regionviews () const
 {
-       for (list<AudioRegionView*>::iterator i = region_views.begin(); i != region_views.end(); ++i) {
-               slot (*i);
+       uint32_t cnt = 0;
+
+       for (list<RegionView*>::const_iterator i = region_views.begin(); i != region_views.end(); ++i) {
+               if ((*i)->get_selected()) {
+                       ++cnt;
+               }
        }
+       return cnt;
 }
 
 void
-StreamView::foreach_crossfadeview (void (CrossfadeView::*pmf)(void))
+StreamView::foreach_regionview (sigc::slot<void,RegionView*> slot)
 {
-       for (list<CrossfadeView*>::iterator i = crossfade_views.begin(); i != crossfade_views.end(); ++i) {
-               ((*i)->*pmf) ();
+       for (list<RegionView*>::iterator i = region_views.begin(); i != region_views.end(); ++i) {
+               slot (*i);
        }
 }
 
 void
-StreamView::rec_peak_range_ready (jack_nframes_t start, jack_nframes_t cnt, Source * src)
+StreamView::foreach_selected_regionview (sigc::slot<void,RegionView*> slot)
 {
-       // this is called from the peak building thread
-
-       ENSURE_GUI_THREAD(bind (mem_fun (*this, &StreamView::rec_peak_range_ready), start, cnt, src));
-       
-       if (rec_peak_ready_map.size() == 0 || start+cnt > last_rec_peak_frame) {
-               last_rec_peak_frame = start + cnt;
-       }
-
-       rec_peak_ready_map[src] = true;
-
-       if (rec_peak_ready_map.size() == _trackview.get_diskstream()->n_channels()) {
-               this->update_rec_regions ();
-               rec_peak_ready_map.clear();
+       for (list<RegionView*>::iterator i = region_views.begin(); i != region_views.end(); ++i) {
+               if ((*i)->get_selected()) {
+                       slot (*i);
+               }
        }
 }
 
 void
-StreamView::update_rec_regions ()
+StreamView::set_selected_regionviews (RegionSelection& regions)
 {
-       if (use_rec_regions) {
-
-               uint32_t n = 0;
-
-               for (list<AudioRegion*>::iterator iter = rec_regions.begin(); iter != rec_regions.end(); n++) {
+       bool selected;
 
-                       list<AudioRegion*>::iterator tmp;
+       for (list<RegionView*>::iterator i = region_views.begin(); i != region_views.end(); ++i) {
 
-                       tmp = iter;
-                       ++tmp;
+               selected = false;
 
-                       if (!canvas_item_visible (rec_rects[n].rectangle)) {
-                               /* rect already hidden, this region is done */
-                               iter = tmp;
-                               continue;
+               for (RegionSelection::iterator ii = regions.begin(); ii != regions.end(); ++ii) {
+                       if (*i == *ii) {
+                               selected = true;
+                               break;
                        }
-                       
-                       AudioRegion * region = (*iter);
-                       jack_nframes_t origlen = region->length();
-
-                       if (region == rec_regions.back() && rec_active) {
-
-                               if (last_rec_peak_frame > region->start()) {
-
-                                       jack_nframes_t nlen = last_rec_peak_frame - region->start();
-
-                                       if (nlen != region->length()) {
-
-                                               region->freeze ();
-                                               region->set_position (_trackview.get_diskstream()->get_capture_start_frame(n), this);
-                                               region->set_length (nlen, this);
-                                               region->thaw ("updated");
-
-                                               if (origlen == 1) {
-                                                       /* our special initial length */
-                                                       add_region_view_internal (region, false);
-                                               }
-
-                                               /* also update rect */
-                                               ArdourCanvas::SimpleRect * rect = rec_rects[n].rectangle;
-                                               gdouble xend = _trackview.editor.frame_to_pixel (region->position() + region->length());
-                                               rect->property_x2() = xend;
-                                       }
-                               }
+               }
 
-                       } else {
+               (*i)->set_selected (selected);
+       }
+}
 
-                               jack_nframes_t nlen = _trackview.get_diskstream()->get_captured_frames(n);
+void
+StreamView::get_selectables (nframes_t start, nframes_t end, double top, double bottom, list<Selectable*>& results)
+{
+       layer_t min_layer = 0;
+       layer_t max_layer = 0;
 
-                               if (nlen != region->length()) {
+       if (_layer_display == Stacked) {
+               double const c = child_height ();
+               min_layer = _layers - ((bottom - _trackview.y_position()) / c);
+               max_layer = _layers - ((top - _trackview.y_position()) / c);
+       }
 
-                                       if (region->source(0).length() >= region->start() + nlen) {
+       for (list<RegionView*>::iterator i = region_views.begin(); i != region_views.end(); ++i) {
 
-                                               region->freeze ();
-                                               region->set_position (_trackview.get_diskstream()->get_capture_start_frame(n), this);
-                                               region->set_length (nlen, this);
-                                               region->thaw ("updated");
-                                               
-                                               if (origlen == 1) {
-                                                       /* our special initial length */
-                                                       add_region_view_internal (region, false);
-                                               }
-                                               
-                                               /* also hide rect */
-                                               ArdourCanvas::Item * rect = rec_rects[n].rectangle;
-                                               rect->hide();
+               bool layer_ok = true;
 
-                                       }
-                               }
-                       }
+               if (_layer_display == Stacked) {
+                       layer_t const l = (*i)->region()->layer ();
+                       layer_ok = (min_layer <= l && l <= max_layer);
+               }
 
-                       iter = tmp;
+               if ((*i)->region()->coverage (start, end) != OverlapNone && layer_ok) {
+                       results.push_back (*i);
                }
        }
 }
 
 void
-StreamView::show_all_xfades ()
+StreamView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
 {
-       foreach_crossfadeview (&CrossfadeView::show);
-       crossfades_visible = true;
+       for (list<RegionView*>::iterator i = region_views.begin(); i != region_views.end(); ++i) {
+               if (!sel.regions.contains (*i)) {
+                       results.push_back (*i);
+               }
+       }
 }
 
-void
-StreamView::hide_all_xfades ()
+/** @return height of a child region view, depending on stacked / overlaid mode */
+double
+StreamView::child_height () const
 {
-       foreach_crossfadeview (&CrossfadeView::hide);
-       crossfades_visible = false;
+       if (_layer_display == Stacked) {
+               return height / _layers;
+       }
+
+       return height;
 }
 
 void
-StreamView::hide_xfades_involving (AudioRegionView& rv)
+StreamView::update_contents_height ()
 {
-       for (list<CrossfadeView *>::iterator i = crossfade_views.begin(); i != crossfade_views.end(); ++i) {
-               if ((*i)->crossfade.involves (rv.region)) {
-                       (*i)->fake_hide ();
+       const double h = child_height ();
+
+       for (RegionViewList::iterator i = region_views.begin(); i != region_views.end(); ++i) {
+               switch (_layer_display) {
+               case Overlaid:
+                       (*i)->set_y (0);
+                       break;
+               case Stacked:
+                       (*i)->set_y (height - ((*i)->region()->layer() + 1) * h);
+                       break;
                }
+
+               (*i)->set_height (h);
+       }
+
+       for (vector<RecBoxInfo>::iterator i = rec_rects.begin(); i != rec_rects.end(); ++i) {
+               i->rectangle->property_y2() = height - 1.0;
        }
 }
 
 void
-StreamView::reveal_xfades_involving (AudioRegionView& rv)
+StreamView::set_layer_display (LayerDisplay d)
 {
-       for (list<CrossfadeView *>::iterator i = crossfade_views.begin(); i != crossfade_views.end(); ++i) {
-               if ((*i)->crossfade.involves (rv.region) && (*i)->visible()) {
-                       (*i)->show ();
-               }
-       }
+       _layer_display = d;
+       update_contents_height ();
+       update_coverage_frames ();
+       _trackview.track()->playlist()->set_explicit_relayering (_layer_display == Stacked);
 }
 
 void
-StreamView::color_handler (ColorID id, uint32_t val)
+StreamView::update_coverage_frames ()
 {
-       switch (id) {
-       case cAudioTrackBase:
-               if (_trackview.is_audio_track()) {
-                       canvas_rect->property_fill_color_rgba() = val;
-               } 
-               break;
-       case cAudioBusBase:
-               if (!_trackview.is_audio_track()) {
-                       canvas_rect->property_fill_color_rgba() = val;
-               }
-               break;
-       case cAudioTrackOutline:
-               canvas_rect->property_outline_color_rgba() = val;
-               break;
-
-       default:
-               break;
+       for (RegionViewList::iterator i = region_views.begin (); i != region_views.end (); ++i) {
+               (*i)->update_coverage_frames (_layer_display);
        }
 }