changeset 997:296ccd36f626 tony-2.0-integration

Merge through to branch for Tony 2.0
author Chris Cannam
date Thu, 20 Aug 2015 14:54:21 +0100
parents 788b7623bfca (current diff) 8588b97f1d1c (diff)
children 1a6304c547bf
files
diffstat 64 files changed, 1917 insertions(+), 1402 deletions(-) [+]
line wrap: on
line diff
--- a/configure.ac	Mon Apr 13 13:52:05 2015 +0100
+++ b/configure.ac	Thu Aug 20 14:54:21 2015 +0100
@@ -88,7 +88,7 @@
 SV_MODULE_REQUIRED([rubberband],[rubberband],[rubberband/RubberBandStretcher.h],[rubberband],[rubberband_new])
 
 SV_MODULE_OPTIONAL([liblo],[],[lo/lo.h],[lo],[lo_address_new])
-SV_MODULE_OPTIONAL([portaudio_2_0],[portaudio-2.0 >= 19],[portaudio.h],[portaudio],[Pa_IsFormatSupported])
+SV_MODULE_OPTIONAL([portaudio],[portaudio-2.0 >= 19],[portaudio.h],[portaudio],[Pa_IsFormatSupported])
 SV_MODULE_OPTIONAL([JACK],[jack >= 0.100],[jack/jack.h],[jack],[jack_client_open])
 SV_MODULE_OPTIONAL([libpulse],[libpulse >= 0.9],[pulse/pulseaudio.h],[pulse],[pa_stream_new])
 SV_MODULE_OPTIONAL([lrdf],[lrdf >= 0.2],[lrdf.h],[lrdf],[lrdf_init])
--- a/layer/Colour3DPlotLayer.cpp	Mon Apr 13 13:52:05 2015 +0100
+++ b/layer/Colour3DPlotLayer.cpp	Thu Aug 20 14:54:21 2015 +0100
@@ -54,6 +54,7 @@
     m_normalizeVisibleArea(false),
     m_normalizeHybrid(false),
     m_invertVertical(false),
+    m_rectified(false),
     m_opaque(false),
     m_smooth(false),
     m_peakResolution(256),
@@ -162,6 +163,7 @@
     list.push_back("Gain");
     list.push_back("Bin Scale");
     list.push_back("Invert Vertical Scale");
+    list.push_back("Show Rectified");
     list.push_back("Opaque");
     list.push_back("Smooth");
     return list;
@@ -175,6 +177,7 @@
     if (name == "Normalize Columns") return tr("Normalize Columns");
     if (name == "Normalize Visible Area") return tr("Normalize Visible Area");
     if (name == "Invert Vertical Scale") return tr("Invert Vertical Scale");
+    if (name == "Show Rectified") return tr("Half-Wave Rectify");
     if (name == "Gain") return tr("Gain");
     if (name == "Opaque") return tr("Always Opaque");
     if (name == "Smooth") return tr("Smooth");
@@ -188,6 +191,7 @@
     if (name == "Normalize Columns") return "normalise-columns";
     if (name == "Normalize Visible Area") return "normalise";
     if (name == "Invert Vertical Scale") return "invert-vertical";
+    if (name == "Show Rectified") return "derivative";
     if (name == "Opaque") return "opaque";
     if (name == "Smooth") return "smooth";
     return "";
@@ -200,6 +204,7 @@
     if (name == "Normalize Columns") return ToggleProperty;
     if (name == "Normalize Visible Area") return ToggleProperty;
     if (name == "Invert Vertical Scale") return ToggleProperty;
+    if (name == "Show Rectified") return ToggleProperty;
     if (name == "Opaque") return ToggleProperty;
     if (name == "Smooth") return ToggleProperty;
     return ValueProperty;
@@ -211,6 +216,7 @@
     if (name == "Normalize Columns" ||
         name == "Normalize Visible Area" ||
 	name == "Colour Scale" ||
+        name == "Show Rectified" ||
         name == "Gain") return tr("Scale");
     if (name == "Bin Scale" ||
         name == "Invert Vertical Scale") return tr("Bins");
@@ -275,6 +281,13 @@
         *deflt = 0;
 	val = (m_invertVertical ? 1 : 0);
 
+    } else if (name == "Show Rectified") {
+
+        if (min) *min = 0;
+        if (max) *max = 0;
+        if (deflt) *deflt = 0;
+        val = (m_rectified ? 1.0 : 0.0);
+
     } else if (name == "Bin Scale") {
 
 	*min = 0;
@@ -355,6 +368,8 @@
 	setNormalizeVisibleArea(value ? true : false);
     } else if (name == "Invert Vertical Scale") {
 	setInvertVertical(value ? true : false);
+    } else if (name == "Show Rectified") {
+        setShowRectified(value > 0.5);
     } else if (name == "Opaque") {
 	setOpaque(value ? true : false);
     } else if (name == "Smooth") {
@@ -472,6 +487,15 @@
 }
 
 void
+Colour3DPlotLayer::setShowRectified(bool show)
+{
+    if (m_rectified == show) return;
+    m_rectified = show;
+    cacheInvalid();
+    emit layerParametersChanged();
+}
+
+void
 Colour3DPlotLayer::setOpaque(bool n)
 {
     if (m_opaque == n) return;
@@ -506,7 +530,7 @@
 }
 
 void
-Colour3DPlotLayer::setLayerDormant(const View *v, bool dormant)
+Colour3DPlotLayer::setLayerDormant(const LayerGeometryProvider *v, bool dormant)
 {
     if (dormant) {
 
@@ -530,7 +554,7 @@
 }
 
 bool
-Colour3DPlotLayer::isLayerScrollable(const View *v) const
+Colour3DPlotLayer::isLayerScrollable(const LayerGeometryProvider *v) const
 {
     if (m_normalizeVisibleArea) {
         return false;
@@ -589,7 +613,7 @@
 }
 
 bool
-Colour3DPlotLayer::getYScaleValue(const View *, int,
+Colour3DPlotLayer::getYScaleValue(const LayerGeometryProvider *, int,
                                   double &, QString &) const
 {
     return false;//!!!
@@ -645,13 +669,13 @@
 }
 
 double
-Colour3DPlotLayer::getYForBin(View *v, double bin) const
+Colour3DPlotLayer::getYForBin(LayerGeometryProvider *v, double bin) const
 {
     double y = bin;
     if (!m_model) return y;
     double mn = 0, mx = m_model->getHeight();
     getDisplayExtents(mn, mx);
-    double h = v->height();
+    double h = v->getPaintHeight();
     if (m_binScale == LinearBinScale) {
         y = h - (((bin - mn) * h) / (mx - mn));
     } else {
@@ -663,19 +687,19 @@
 }
 
 int
-Colour3DPlotLayer::getIYForBin(View *v, int bin) const
+Colour3DPlotLayer::getIYForBin(LayerGeometryProvider *v, int bin) const
 {
     return int(round(getYForBin(v, bin)));
 }
 
 double
-Colour3DPlotLayer::getBinForY(View *v, double y) const
+Colour3DPlotLayer::getBinForY(LayerGeometryProvider *v, double y) const
 {
     double bin = y;
     if (!m_model) return bin;
     double mn = 0, mx = m_model->getHeight();
     getDisplayExtents(mn, mx);
-    double h = v->height();
+    double h = v->getPaintHeight();
     if (m_binScale == LinearBinScale) {
         bin = mn + ((h - y) * (mx - mn)) / h;
     } else {
@@ -687,13 +711,13 @@
 }
 
 int
-Colour3DPlotLayer::getIBinForY(View *v, int y) const
+Colour3DPlotLayer::getIBinForY(LayerGeometryProvider *v, int y) const
 {
     return int(floor(getBinForY(v, y)));
 }
 
 QString
-Colour3DPlotLayer::getFeatureDescription(View *v, QPoint &pos) const
+Colour3DPlotLayer::getFeatureDescription(LayerGeometryProvider *v, QPoint &pos) const
 {
     if (!m_model) return "";
 
@@ -724,8 +748,8 @@
     if (symin < 0) symin = 0;
     if (symax > sh) symax = sh;
 
- //    double binHeight = double(v->height()) / (symax - symin);
-//    int sy = int((v->height() - y) / binHeight) + symin;
+ //    double binHeight = double(v->getPaintHeight()) / (symax - symin);
+//    int sy = int((v->getPaintHeight() - y) / binHeight) + symin;
 
     int sy = getIBinForY(v, y);
 
@@ -755,14 +779,15 @@
 }
 
 int
-Colour3DPlotLayer::getColourScaleWidth(QPainter &) const
+Colour3DPlotLayer::getColourScaleWidth(QPainter &p) const
 {
-    int cw = 20;
+    // Font is rotated
+    int cw = p.fontMetrics().height();
     return cw;
 }
 
 int
-Colour3DPlotLayer::getVerticalScaleWidth(View *, bool, QPainter &paint) const
+Colour3DPlotLayer::getVerticalScaleWidth(LayerGeometryProvider *, bool, QPainter &paint) const
 {
     if (!m_model) return 0;
 
@@ -784,7 +809,7 @@
 }
 
 void
-Colour3DPlotLayer::paintVerticalScale(View *v, bool, QPainter &paint, QRect rect) const
+Colour3DPlotLayer::paintVerticalScale(LayerGeometryProvider *v, bool, QPainter &paint, QRect rect) const
 {
     if (!m_model) return;
 
@@ -844,7 +869,7 @@
         paint.save();
 
         QFont font = paint.font();
-        font.setPixelSize(10);
+        font.setPixelSize(int(font.pixelSize() * 0.65));
         paint.setFont(font);
 
         int msw = paint.fontMetrics().width(maxstr);
@@ -882,6 +907,8 @@
 
     int py = h;
 
+    int defaultFontHeight = paint.fontMetrics().height();
+    
     for (int i = symin; i <= symax; ++i) {
 
         int y0;
@@ -891,9 +918,9 @@
 
         if (i > symin) {
             if (paint.fontMetrics().height() >= h) {
-                if (h >= 8) {
+                if (h >= defaultFontHeight * 0.8) {
                     QFont tf = paint.font();
-                    tf.setPixelSize(h-2);
+                    tf.setPixelSize(int(h * 0.8));
                     paint.setFont(tf);
                 } else {
                     continue;
@@ -930,7 +957,20 @@
 {
     Profiler profiler("Colour3DPlotLayer::getColumn");
 
+    DenseThreeDimensionalModel::Column prev;
+    if (m_rectified && (col > m_model->getStartFrame())) {
+        prev = m_model->getColumn(col - 1);
+    }
+    
     DenseThreeDimensionalModel::Column values = m_model->getColumn(col);
+
+    if (m_rectified && !prev.empty()) {
+        for (int y = 0; y < values.size(); ++y) {
+            if (values[y] < prev[y]) values[y] = 0;
+            else values[y] -= prev[y];
+        }
+    }
+    
     while (values.size() < m_model->getHeight()) values.push_back(0.f);
     if (!m_normalizeColumns && !m_normalizeHybrid) return values;
 
@@ -1246,7 +1286,7 @@
 }
 
 bool
-Colour3DPlotLayer::shouldPaintDenseIn(const View *v) const
+Colour3DPlotLayer::shouldPaintDenseIn(const LayerGeometryProvider *v) const
 {
     if (!m_model || !v || !(v->getViewManager())) {
         return false;
@@ -1255,7 +1295,7 @@
         v->getViewManager()->getMainModelSampleRate() / m_model->getSampleRate();
     if (m_opaque || 
         m_smooth ||
-        m_model->getHeight() >= v->height() ||
+        m_model->getHeight() >= v->getPaintHeight() ||
         ((m_model->getResolution() * srRatio) / v->getZoomLevel()) < 2) {
         return true;
     }
@@ -1263,7 +1303,7 @@
 }
 
 void
-Colour3DPlotLayer::paint(View *v, QPainter &paint, QRect rect) const
+Colour3DPlotLayer::paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const
 {
 /*
     if (m_model) {
@@ -1278,13 +1318,13 @@
     int completion = 0;
     if (!m_model || !m_model->isOK() || !m_model->isReady(&completion)) {
 	if (completion > 0) {
-	    paint.fillRect(0, 10, v->width() * completion / 100,
+	    paint.fillRect(0, 10, v->getPaintWidth() * completion / 100,
 			   10, QColor(120, 120, 120));
 	}
 	return;
     }
 
-    if (m_normalizeVisibleArea && !m_normalizeColumns) rect = v->rect();
+    if (m_normalizeVisibleArea && !m_normalizeColumns) rect = v->getPaintRect();
 
     sv_frame_t modelStart = m_model->getStartFrame();
     sv_frame_t modelEnd = m_model->getEndFrame();
@@ -1302,7 +1342,7 @@
     int x0 = rect.left();
     int x1 = rect.right() + 1;
 
-    int h = v->height();
+    int h = v->getPaintHeight();
 
     double srRatio =
         v->getViewManager()->getMainModelSampleRate() / m_model->getSampleRate();
@@ -1424,7 +1464,7 @@
 }
 
 void
-Colour3DPlotLayer::paintDense(View *v, QPainter &paint, QRect rect) const
+Colour3DPlotLayer::paintDense(LayerGeometryProvider *v, QPainter &paint, QRect rect) const
 {
     Profiler profiler("Colour3DPlotLayer::paintDense", true);
     if (!m_cache) return;
@@ -1440,7 +1480,7 @@
     int x1 = rect.right() + 1;
 
     const int w = x1 - x0; // const so it can be used as array size below
-    int h = v->height(); // we always paint full height
+    int h = v->getPaintHeight(); // we always paint full height
     int sh = m_model->getHeight();
 
     int symin = m_miny;
@@ -1655,7 +1695,7 @@
 }
 
 bool
-Colour3DPlotLayer::snapToFeatureFrame(View *v, sv_frame_t &frame,
+Colour3DPlotLayer::snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame,
 				      int &resolution,
 				      SnapType snap) const
 {
--- a/layer/Colour3DPlotLayer.h	Mon Apr 13 13:52:05 2015 +0100
+++ b/layer/Colour3DPlotLayer.h	Thu Aug 20 14:54:21 2015 +0100
@@ -49,20 +49,20 @@
         return m_model ? m_model->getZoomConstraint() : 0;
     }
     virtual const Model *getModel() const { return m_model; }
-    virtual void paint(View *v, QPainter &paint, QRect rect) const;
+    virtual void paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const;
 
-    virtual int getVerticalScaleWidth(View *v, bool, QPainter &) const;
-    virtual void paintVerticalScale(View *v, bool, QPainter &paint, QRect rect) const;
+    virtual int getVerticalScaleWidth(LayerGeometryProvider *v, bool, QPainter &) const;
+    virtual void paintVerticalScale(LayerGeometryProvider *v, bool, QPainter &paint, QRect rect) const;
 
-    virtual QString getFeatureDescription(View *v, QPoint &) const;
+    virtual QString getFeatureDescription(LayerGeometryProvider *v, QPoint &) const;
 
-    virtual bool snapToFeatureFrame(View *v, sv_frame_t &frame, 
+    virtual bool snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame, 
 				    int &resolution,
 				    SnapType snap) const;
 
-    virtual void setLayerDormant(const View *v, bool dormant);
+    virtual void setLayerDormant(const LayerGeometryProvider *v, bool dormant);
 
-    virtual bool isLayerScrollable(const View *v) const;
+    virtual bool isLayerScrollable(const LayerGeometryProvider *v) const;
 
     virtual ColourSignificance getLayerColourSignificance() const {
         return ColourHasMeaningfulValue;
@@ -70,7 +70,7 @@
 
     void setModel(const DenseThreeDimensionalModel *model);
 
-    virtual int getCompletion(View *) const { return m_model->getCompletion(); }
+    virtual int getCompletion(LayerGeometryProvider *) const { return m_model->getCompletion(); }
 
     virtual PropertyList getProperties() const;
     virtual PropertyType getPropertyType(const PropertyName &) const;
@@ -139,6 +139,9 @@
     void setInvertVertical(bool i);
     bool getInvertVertical() const;
 
+    void setShowRectified(bool);
+    bool getShowRectified() const { return m_rectified; }
+
     void setOpaque(bool i);
     bool getOpaque() const;
 
@@ -151,7 +154,7 @@
     virtual bool getDisplayExtents(double &min, double &max) const;
     virtual bool setDisplayExtents(double min, double max);
 
-    virtual bool getYScaleValue(const View *, int /* y */,
+    virtual bool getYScaleValue(const LayerGeometryProvider *, int /* y */,
                                 double &/* value */, QString &/* unit */) const;
 
     virtual int getVerticalZoomSteps(int &defaultStep) const;
@@ -187,6 +190,7 @@
     bool        m_normalizeVisibleArea;
     bool        m_normalizeHybrid;
     bool        m_invertVertical;
+    bool        m_rectified;
     bool        m_opaque;
     bool        m_smooth;
     int         m_peakResolution;
@@ -202,12 +206,12 @@
      * and the vertical scale is the usual way up). Bin number may be
      * fractional, to obtain a position part-way through a bin.
      */
-    double getYForBin(View *, double bin) const;
+    double getYForBin(LayerGeometryProvider *, double bin) const;
 
     /**
      * As getYForBin, but rounding to integer values.
      */
-    int getIYForBin(View *, int bin) const;
+    int getIYForBin(LayerGeometryProvider *, int bin) const;
     
     /**
      * Return the bin number, possibly fractional, at the given y
@@ -215,12 +219,12 @@
      * at which the bins "start" (i.e. the bottom of the visible bin,
      * if the vertical scale is the usual way up).
      */
-    double getBinForY(View *, double y) const;
+    double getBinForY(LayerGeometryProvider *, double y) const;
 
     /**
      * As getBinForY, but rounding to integer values.
      */
-    int getIBinForY(View *, int y) const;
+    int getIBinForY(LayerGeometryProvider *, int y) const;
     
     DenseThreeDimensionalModel::Column getColumn(int col) const;
 
@@ -229,11 +233,11 @@
      * are so small you can't see their borders. False for big,
      * translucent cells.
      */
-    bool shouldPaintDenseIn(const View *) const; 
+    bool shouldPaintDenseIn(const LayerGeometryProvider *) const; 
 
     int getColourScaleWidth(QPainter &) const;
     void fillCache(int firstBin, int lastBin) const;
-    void paintDense(View *v, QPainter &paint, QRect rect) const;
+    void paintDense(LayerGeometryProvider *v, QPainter &paint, QRect rect) const;
 };
 
 #endif
--- a/layer/ColourScaleLayer.h	Mon Apr 13 13:52:05 2015 +0100
+++ b/layer/ColourScaleLayer.h	Thu Aug 20 14:54:21 2015 +0100
@@ -19,11 +19,13 @@
 #include <QString>
 #include <QColor>
 
+class LayerGeometryProvider;
+
 class ColourScaleLayer
 {
 public:
     virtual QString getScaleUnits() const = 0;
-    virtual QColor getColourForValue(View *v, double value) const = 0;
+    virtual QColor getColourForValue(LayerGeometryProvider *v, double value) const = 0;
 };
 
 #endif
--- a/layer/FlexiNoteLayer.cpp	Mon Apr 13 13:52:05 2015 +0100
+++ b/layer/FlexiNoteLayer.cpp	Thu Aug 20 14:54:21 2015 +0100
@@ -204,7 +204,7 @@
 }
 
 bool
-FlexiNoteLayer::isLayerScrollable(const View *v) const
+FlexiNoteLayer::isLayerScrollable(const LayerGeometryProvider *v) const
 {
     QPoint discard;
     return !v->shouldIlluminateLocalFeatures(this, discard);
@@ -405,7 +405,7 @@
 }
 
 FlexiNoteModel::PointList
-FlexiNoteLayer::getLocalPoints(View *v, int x) const
+FlexiNoteLayer::getLocalPoints(LayerGeometryProvider *v, int x) const
 {
     if (!m_model) return FlexiNoteModel::PointList();
 
@@ -448,7 +448,7 @@
 }
 
 bool
-FlexiNoteLayer::getPointToDrag(View *v, int x, int y, FlexiNoteModel::Point &p) const
+FlexiNoteLayer::getPointToDrag(LayerGeometryProvider *v, int x, int y, FlexiNoteModel::Point &p) const
 {
     if (!m_model) return false;
 
@@ -476,7 +476,7 @@
 }
 
 bool
-FlexiNoteLayer::getNoteToEdit(View *v, int x, int y, FlexiNoteModel::Point &p) const
+FlexiNoteLayer::getNoteToEdit(LayerGeometryProvider *v, int x, int y, FlexiNoteModel::Point &p) const
 {
     // GF: find the note that is closest to the cursor
     if (!m_model) return false;
@@ -505,7 +505,7 @@
 }
 
 QString
-FlexiNoteLayer::getFeatureDescription(View *v, QPoint &pos) const
+FlexiNoteLayer::getFeatureDescription(LayerGeometryProvider *v, QPoint &pos) const
 {
     int x = pos.x();
 
@@ -593,7 +593,7 @@
 }
 
 bool
-FlexiNoteLayer::snapToFeatureFrame(View *v, sv_frame_t &frame,
+FlexiNoteLayer::snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame,
                                    int &resolution,
                                    SnapType snap) const
 {
@@ -673,7 +673,7 @@
 }
 
 void
-FlexiNoteLayer::getScaleExtents(View *v, double &min, double &max, bool &log) const
+FlexiNoteLayer::getScaleExtents(LayerGeometryProvider *v, double &min, double &max, bool &log) const
 {
     min = 0.0;
     max = 0.0;
@@ -730,11 +730,11 @@
 }
 
 int
-FlexiNoteLayer::getYForValue(View *v, double val) const
+FlexiNoteLayer::getYForValue(LayerGeometryProvider *v, double val) const
 {
     double min = 0.0, max = 0.0;
     bool logarithmic = false;
-    int h = v->height();
+    int h = v->getPaintHeight();
 
     getScaleExtents(v, min, max, logarithmic);
 
@@ -765,11 +765,11 @@
 }
 
 double
-FlexiNoteLayer::getValueForY(View *v, int y) const
+FlexiNoteLayer::getValueForY(LayerGeometryProvider *v, int y) const
 {
     double min = 0.0, max = 0.0;
     bool logarithmic = false;
-    int h = v->height();
+    int h = v->getPaintHeight();
 
     getScaleExtents(v, min, max, logarithmic);
 
@@ -794,7 +794,7 @@
 }
 
 void
-FlexiNoteLayer::paint(View *v, QPainter &paint, QRect rect) const
+FlexiNoteLayer::paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const
 {
     if (!m_model || !m_model->isOK()) return;
 
@@ -860,8 +860,8 @@
                 !FlexiNoteModel::Point::Comparator()(illuminatePoint, p) &&
                 !FlexiNoteModel::Point::Comparator()(p, illuminatePoint)) {
 
-                paint.drawLine(x, -1, x, v->height() + 1);
-                paint.drawLine(x+w, -1, x+w, v->height() + 1);
+                paint.drawLine(x, -1, x, v->getPaintHeight() + 1);
+                paint.drawLine(x+w, -1, x+w, v->getPaintHeight() + 1);
         
                 paint.setPen(v->getForeground());
                 // paint.setBrush(v->getForeground());
@@ -904,7 +904,7 @@
 }
 
 int
-FlexiNoteLayer::getVerticalScaleWidth(View *v, bool, QPainter &paint) const
+FlexiNoteLayer::getVerticalScaleWidth(LayerGeometryProvider *v, bool, QPainter &paint) const
 {
     if (!m_model || shouldAutoAlign()) {
         return 0;
@@ -918,7 +918,7 @@
 }
 
 void
-FlexiNoteLayer::paintVerticalScale(View *v, bool, QPainter &paint, QRect) const
+FlexiNoteLayer::paintVerticalScale(LayerGeometryProvider *v, bool, QPainter &paint, QRect) const
 {
     if (!m_model || m_model->getPoints().empty()) return;
 
@@ -927,7 +927,7 @@
     bool logarithmic;
 
     int w = getVerticalScaleWidth(v, false, paint);
-    int h = v->height();
+    int h = v->getPaintHeight();
 
     getScaleExtents(v, min, max, logarithmic);
 
@@ -956,7 +956,7 @@
 }
 
 void
-FlexiNoteLayer::drawStart(View *v, QMouseEvent *e)
+FlexiNoteLayer::drawStart(LayerGeometryProvider *v, QMouseEvent *e)
 {
 //    SVDEBUG << "FlexiNoteLayer::drawStart(" << e->x() << "," << e->y() << ")" << endl;
 
@@ -980,7 +980,7 @@
 }
 
 void
-FlexiNoteLayer::drawDrag(View *v, QMouseEvent *e)
+FlexiNoteLayer::drawDrag(LayerGeometryProvider *v, QMouseEvent *e)
 {
 //    SVDEBUG << "FlexiNoteLayer::drawDrag(" << e->x() << "," << e->y() << ")" << endl;
 
@@ -1009,7 +1009,7 @@
 }
 
 void
-FlexiNoteLayer::drawEnd(View *, QMouseEvent *)
+FlexiNoteLayer::drawEnd(LayerGeometryProvider *, QMouseEvent *)
 {
 //    SVDEBUG << "FlexiNoteLayer::drawEnd(" << e->x() << "," << e->y() << ")" << endl;
     if (!m_model || !m_editing) return;
@@ -1019,7 +1019,7 @@
 }
 
 void
-FlexiNoteLayer::eraseStart(View *v, QMouseEvent *e)
+FlexiNoteLayer::eraseStart(LayerGeometryProvider *v, QMouseEvent *e)
 {
     if (!m_model) return;
 
@@ -1034,12 +1034,12 @@
 }
 
 void
-FlexiNoteLayer::eraseDrag(View *, QMouseEvent *)
+FlexiNoteLayer::eraseDrag(LayerGeometryProvider *, QMouseEvent *)
 {
 }
 
 void
-FlexiNoteLayer::eraseEnd(View *v, QMouseEvent *e)
+FlexiNoteLayer::eraseEnd(LayerGeometryProvider *v, QMouseEvent *e)
 {
     if (!m_model || !m_editing) return;
 
@@ -1059,7 +1059,7 @@
 }
 
 void
-FlexiNoteLayer::editStart(View *v, QMouseEvent *e)
+FlexiNoteLayer::editStart(LayerGeometryProvider *v, QMouseEvent *e)
 {
 //    SVDEBUG << "FlexiNoteLayer::editStart(" << e->x() << "," << e->y() << ")" << endl;
     std::cerr << "FlexiNoteLayer::editStart(" << e->x() << "," << e->y() << ")" << std::endl;
@@ -1110,7 +1110,7 @@
 }
 
 void
-FlexiNoteLayer::editDrag(View *v, QMouseEvent *e)
+FlexiNoteLayer::editDrag(LayerGeometryProvider *v, QMouseEvent *e)
 {
 //    SVDEBUG << "FlexiNoteLayer::editDrag(" << e->x() << "," << e->y() << ")" << endl;
     std::cerr << "FlexiNoteLayer::editDrag(" << e->x() << "," << e->y() << ")" << std::endl;
@@ -1190,7 +1190,7 @@
 }
 
 void
-FlexiNoteLayer::editEnd(View *v, QMouseEvent *e)
+FlexiNoteLayer::editEnd(LayerGeometryProvider *v, QMouseEvent *e)
 {
 //    SVDEBUG << "FlexiNoteLayer::editEnd(" << e->x() << "," << e->y() << ")" << endl;
     std::cerr << "FlexiNoteLayer::editEnd(" << e->x() << "," << e->y() << ")" << std::endl;
@@ -1229,7 +1229,7 @@
 }
 
 void
-FlexiNoteLayer::splitStart(View *v, QMouseEvent *e)
+FlexiNoteLayer::splitStart(LayerGeometryProvider *v, QMouseEvent *e)
 {
     // GF: note splitting starts (!! remove printing soon)
     std::cerr << "splitStart (n.b. editStart will be called later, if the user drags the mouse)" << std::endl;
@@ -1252,7 +1252,7 @@
 }
 
 void
-FlexiNoteLayer::splitEnd(View *v, QMouseEvent *e)
+FlexiNoteLayer::splitEnd(LayerGeometryProvider *v, QMouseEvent *e)
 {
     // GF: note splitting ends. (!! remove printing soon)
     std::cerr << "splitEnd" << std::endl;
@@ -1271,13 +1271,13 @@
 }
 
 void
-FlexiNoteLayer::splitNotesAt(View *v, sv_frame_t frame)
+FlexiNoteLayer::splitNotesAt(LayerGeometryProvider *v, sv_frame_t frame)
 {
     splitNotesAt(v, frame, 0);
 }
 
 void
-FlexiNoteLayer::splitNotesAt(View *v, sv_frame_t frame, QMouseEvent *e)
+FlexiNoteLayer::splitNotesAt(LayerGeometryProvider *v, sv_frame_t frame, QMouseEvent *e)
 {
     FlexiNoteModel::PointList onPoints = m_model->getPoints(frame);
     if (onPoints.empty()) return;
@@ -1317,7 +1317,7 @@
 }
 
 void
-FlexiNoteLayer::addNote(View *v, QMouseEvent *e)
+FlexiNoteLayer::addNote(LayerGeometryProvider *v, QMouseEvent *e)
 {
     std::cerr << "addNote" << std::endl;
     if (!m_model) return;
@@ -1356,14 +1356,14 @@
 }
 
 SparseTimeValueModel *
-FlexiNoteLayer::getAssociatedPitchModel(View *v) const
+FlexiNoteLayer::getAssociatedPitchModel(LayerGeometryProvider *v) const
 {
     // Better than we used to do, but still not very satisfactory
 
 //    cerr << "FlexiNoteLayer::getAssociatedPitchModel()" << endl;
 
-    for (int i = 0; i < v->getLayerCount(); ++i) {
-        Layer *layer = v->getLayer(i);
+    for (int i = 0; i < v->getView()->getLayerCount(); ++i) {
+        Layer *layer = v->getView()->getLayer(i);
         if (layer &&
             layer->getLayerPresentationName() != "candidate") {
 //            cerr << "FlexiNoteLayer::getAssociatedPitchModel: looks like our layer is " << layer << endl;
@@ -1381,7 +1381,7 @@
 }
 
 void
-FlexiNoteLayer::snapSelectedNotesToPitchTrack(View *v, Selection s)
+FlexiNoteLayer::snapSelectedNotesToPitchTrack(LayerGeometryProvider *v, Selection s)
 {
     if (!m_model) return;
 
@@ -1419,7 +1419,7 @@
 }
 
 void
-FlexiNoteLayer::mergeNotes(View *v, Selection s, bool inclusive)
+FlexiNoteLayer::mergeNotes(LayerGeometryProvider *v, Selection s, bool inclusive)
 {
     FlexiNoteModel::PointList points =
         m_model->getPoints(s.getStartFrame(), s.getEndFrame());
@@ -1462,8 +1462,7 @@
 }
 
 bool
-FlexiNoteLayer::updateNoteValueFromPitchCurve(View *v,
-                                              FlexiNoteModel::Point &note) const
+FlexiNoteLayer::updateNoteValueFromPitchCurve(LayerGeometryProvider *v, FlexiNoteModel::Point &note) const
 {
     SparseTimeValueModel *model = getAssociatedPitchModel(v);
     if (!model) return false;
@@ -1507,13 +1506,13 @@
 }
 
 void 
-FlexiNoteLayer::mouseMoveEvent(View *v, QMouseEvent *e)
+FlexiNoteLayer::mouseMoveEvent(LayerGeometryProvider *v, QMouseEvent *e)
 {
     // GF: context sensitive cursors
-    // v->setCursor(Qt::ArrowCursor);
+    // v->getView()->setCursor(Qt::ArrowCursor);
     FlexiNoteModel::Point note(0);
     if (!getNoteToEdit(v, e->x(), e->y(), note)) { 
-        // v->setCursor(Qt::UpArrowCursor);
+        // v->getView()->setCursor(Qt::UpArrowCursor);
         return; 
     }
 
@@ -1524,28 +1523,28 @@
                              closeToTop, closeToBottom);
     
     if (closeToLeft) {
-        v->setCursor(Qt::SizeHorCursor);
+        v->getView()->setCursor(Qt::SizeHorCursor);
         m_editMode = LeftBoundary;
         cerr << "edit mode -> LeftBoundary" << endl;
     } else if (closeToRight) {
-        v->setCursor(Qt::SizeHorCursor);
+        v->getView()->setCursor(Qt::SizeHorCursor);
         m_editMode = RightBoundary;
         cerr << "edit mode -> RightBoundary" << endl;
     } else if (closeToTop) {
-        v->setCursor(Qt::CrossCursor);
+        v->getView()->setCursor(Qt::CrossCursor);
         m_editMode = DragNote;
         cerr << "edit mode -> DragNote" << endl;
     } else if (closeToBottom) {
-        v->setCursor(Qt::UpArrowCursor);
+        v->getView()->setCursor(Qt::UpArrowCursor);
         m_editMode = SplitNote;
         cerr << "edit mode -> SplitNote" << endl;
     } else {
-        v->setCursor(Qt::ArrowCursor);
+        v->getView()->setCursor(Qt::ArrowCursor);
     }
 }
 
 void
-FlexiNoteLayer::getRelativeMousePosition(View *v, FlexiNoteModel::Point &note, int x, int y, bool &closeToLeft, bool &closeToRight, bool &closeToTop, bool &closeToBottom) const
+FlexiNoteLayer::getRelativeMousePosition(LayerGeometryProvider *v, FlexiNoteModel::Point &note, int x, int y, bool &closeToLeft, bool &closeToRight, bool &closeToTop, bool &closeToBottom) const
 {
     // GF: TODO: consoloidate the tolerance values
     if (!m_model) return;
@@ -1574,7 +1573,7 @@
 
 
 bool
-FlexiNoteLayer::editOpen(View *v, QMouseEvent *e)
+FlexiNoteLayer::editOpen(LayerGeometryProvider *v, QMouseEvent *e)
 {
     std::cerr << "Opening note editor dialog" << std::endl;
     if (!m_model) return false;
@@ -1728,7 +1727,7 @@
 }
 
 void
-FlexiNoteLayer::copy(View *v, Selection s, Clipboard &to)
+FlexiNoteLayer::copy(LayerGeometryProvider *v, Selection s, Clipboard &to)
 {
     if (!m_model) return;
 
@@ -1746,7 +1745,7 @@
 }
 
 bool
-FlexiNoteLayer::paste(View *v, const Clipboard &from, sv_frame_t /*frameOffset */, bool /* interactive */)
+FlexiNoteLayer::paste(LayerGeometryProvider *v, const Clipboard &from, sv_frame_t /*frameOffset */, bool /* interactive */)
 {
     if (!m_model) return false;
 
@@ -1757,7 +1756,7 @@
     if (clipboardHasDifferentAlignment(v, from)) {
 
         QMessageBox::StandardButton button =
-            QMessageBox::question(v, tr("Re-align pasted items?"),
+            QMessageBox::question(v->getView(), tr("Re-align pasted items?"),
                                   tr("The items you are pasting came from a layer with different source material from this one.  Do you want to re-align them in time, to match the source material for this layer?"),
                                   QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
                                   QMessageBox::Yes);
@@ -1894,7 +1893,7 @@
 }
 
 void
-FlexiNoteLayer::setVerticalRangeToNoteRange(View *v)
+FlexiNoteLayer::setVerticalRangeToNoteRange(LayerGeometryProvider *v)
 {
     double minf = std::numeric_limits<double>::max();
     double maxf = 0;
@@ -1910,7 +1909,7 @@
     std::cerr << "min frequency:" << minf << ", max frequency: " << maxf << std::endl;
     
     if (hasNotes) {
-        v->getLayer(1)->setDisplayExtents(minf*0.66,maxf*1.5); 
+        v->getView()->getLayer(1)->setDisplayExtents(minf*0.66,maxf*1.5); 
         // MM: this is a hack because we rely on 
         // * this layer being automatically aligned to layer 1
         // * layer one is a log frequency layer.
--- a/layer/FlexiNoteLayer.h	Mon Apr 13 13:52:05 2015 +0100
+++ b/layer/FlexiNoteLayer.h	Thu Aug 20 14:54:21 2015 +0100
@@ -38,50 +38,50 @@
 public:
     FlexiNoteLayer();
 
-    virtual void paint(View *v, QPainter &paint, QRect rect) const;
+    virtual void paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const;
 
-    virtual int getVerticalScaleWidth(View *v, bool, QPainter &) const;
-    virtual void paintVerticalScale(View *v, bool, QPainter &paint, QRect rect) const;
+    virtual int getVerticalScaleWidth(LayerGeometryProvider *v, bool, QPainter &) const;
+    virtual void paintVerticalScale(LayerGeometryProvider *v, bool, QPainter &paint, QRect rect) const;
 
-    virtual QString getFeatureDescription(View *v, QPoint &) const;
+    virtual QString getFeatureDescription(LayerGeometryProvider *v, QPoint &) const;
 
-    virtual bool snapToFeatureFrame(View *v, sv_frame_t &frame,
+    virtual bool snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame,
                     int &resolution,
                     SnapType snap) const;
 
-    virtual void drawStart(View *v, QMouseEvent *);
-    virtual void drawDrag(View *v, QMouseEvent *);
-    virtual void drawEnd(View *v, QMouseEvent *);
+    virtual void drawStart(LayerGeometryProvider *v, QMouseEvent *);
+    virtual void drawDrag(LayerGeometryProvider *v, QMouseEvent *);
+    virtual void drawEnd(LayerGeometryProvider *v, QMouseEvent *);
 
-    virtual void eraseStart(View *v, QMouseEvent *);
-    virtual void eraseDrag(View *v, QMouseEvent *);
-    virtual void eraseEnd(View *v, QMouseEvent *);
+    virtual void eraseStart(LayerGeometryProvider *v, QMouseEvent *);
+    virtual void eraseDrag(LayerGeometryProvider *v, QMouseEvent *);
+    virtual void eraseEnd(LayerGeometryProvider *v, QMouseEvent *);
 
-    virtual void editStart(View *v, QMouseEvent *);
-    virtual void editDrag(View *v, QMouseEvent *);
-    virtual void editEnd(View *v, QMouseEvent *);
+    virtual void editStart(LayerGeometryProvider *v, QMouseEvent *);
+    virtual void editDrag(LayerGeometryProvider *v, QMouseEvent *);
+    virtual void editEnd(LayerGeometryProvider *v, QMouseEvent *);
 
-    virtual void splitStart(View *v, QMouseEvent *);
-    virtual void splitEnd(View *v, QMouseEvent *);
+    virtual void splitStart(LayerGeometryProvider *v, QMouseEvent *);
+    virtual void splitEnd(LayerGeometryProvider *v, QMouseEvent *);
     
-    virtual void addNote(View *v, QMouseEvent *e);
+    virtual void addNote(LayerGeometryProvider *v, QMouseEvent *e);
 
-    virtual void mouseMoveEvent(View *v, QMouseEvent *);
+    virtual void mouseMoveEvent(LayerGeometryProvider *v, QMouseEvent *);
 
-    virtual bool editOpen(View *v, QMouseEvent *);
+    virtual bool editOpen(LayerGeometryProvider *v, QMouseEvent *);
 
     virtual void moveSelection(Selection s, sv_frame_t newStartFrame);
     virtual void resizeSelection(Selection s, Selection newSize);
     virtual void deleteSelection(Selection s);
     virtual void deleteSelectionInclusive(Selection s);
 
-    virtual void copy(View *v, Selection s, Clipboard &to);
-    virtual bool paste(View *v, const Clipboard &from, sv_frame_t frameOffset,
+    virtual void copy(LayerGeometryProvider *v, Selection s, Clipboard &to);
+    virtual bool paste(LayerGeometryProvider *v, const Clipboard &from, sv_frame_t frameOffset,
                        bool interactive);
 
-    void splitNotesAt(View *v, sv_frame_t frame);
-    void snapSelectedNotesToPitchTrack(View *v, Selection s);
-    void mergeNotes(View *v, Selection s, bool inclusive);
+    void splitNotesAt(LayerGeometryProvider *v, sv_frame_t frame);
+    void snapSelectedNotesToPitchTrack(LayerGeometryProvider *v, Selection s);
+    void mergeNotes(LayerGeometryProvider *v, Selection s, bool inclusive);
 
     virtual const Model *getModel() const { return m_model; }
     void setModel(FlexiNoteModel *model);
@@ -116,11 +116,11 @@
     void setVerticalScale(VerticalScale scale);
     VerticalScale getVerticalScale() const { return m_verticalScale; }
 
-    virtual bool isLayerScrollable(const View *v) const;
+    virtual bool isLayerScrollable(const LayerGeometryProvider *v) const;
 
     virtual bool isLayerEditable() const { return true; }
 
-    virtual int getCompletion(View *) const { return m_model->getCompletion(); }
+    virtual int getCompletion(LayerGeometryProvider *) const { return m_model->getCompletion(); }
 
     virtual bool getValueExtents(double &min, double &max,
                                  bool &log, QString &unit) const;
@@ -156,11 +156,11 @@
 
     void setProperties(const QXmlAttributes &attributes);
     
-    void setVerticalRangeToNoteRange(View *v);
+    void setVerticalRangeToNoteRange(LayerGeometryProvider *v);
 
     /// VerticalScaleLayer methods
-    virtual int getYForValue(View *v, double value) const;
-    virtual double getValueForY(View *v, int y) const;
+    virtual int getYForValue(LayerGeometryProvider *v, double value) const;
+    virtual double getValueForY(LayerGeometryProvider *v, int y) const;
     virtual QString getScaleUnits() const;
 
 signals:
@@ -168,23 +168,19 @@
     void materialiseReAnalysis();
     
 protected:
-    void getScaleExtents(View *, double &min, double &max, bool &log) const;
+    void getScaleExtents(LayerGeometryProvider *, double &min, double &max, bool &log) const;
     bool shouldConvertMIDIToHz() const;
 
     virtual int getDefaultColourHint(bool dark, bool &impose);
 
-    FlexiNoteModel::PointList getLocalPoints(View *v, int) const;
+    FlexiNoteModel::PointList getLocalPoints(LayerGeometryProvider *v, int) const;
 
-    bool getPointToDrag(View *v, int x, int y, FlexiNoteModel::Point &) const;
-    bool getNoteToEdit(View *v, int x, int y, FlexiNoteModel::Point &) const;
-    void getRelativeMousePosition(View *v, FlexiNoteModel::Point &note,
-                                  int x, int y,
-                                  bool &closeToLeft, bool &closeToRight,
-                                  bool &closeToTop, bool &closeToBottom) const;
-    SparseTimeValueModel *getAssociatedPitchModel(View *v) const;
-    bool updateNoteValueFromPitchCurve(View *v,
-                                       FlexiNoteModel::Point &note) const;
-    void splitNotesAt(View *v, sv_frame_t frame, QMouseEvent *e);
+    bool getPointToDrag(LayerGeometryProvider *v, int x, int y, FlexiNoteModel::Point &) const;
+    bool getNoteToEdit(LayerGeometryProvider *v, int x, int y, FlexiNoteModel::Point &) const;
+    void getRelativeMousePosition(LayerGeometryProvider *v, FlexiNoteModel::Point &note, int x, int y, bool &closeToLeft, bool &closeToRight, bool &closeToTop, bool &closeToBottom) const;
+    SparseTimeValueModel *getAssociatedPitchModel(LayerGeometryProvider *v) const;
+    bool updateNoteValueFromPitchCurve(LayerGeometryProvider *v, FlexiNoteModel::Point &note) const;
+    void splitNotesAt(LayerGeometryProvider *v, sv_frame_t frame, QMouseEvent *e);
 
     FlexiNoteModel *m_model;
     bool m_editing;
--- a/layer/ImageLayer.cpp	Mon Apr 13 13:52:05 2015 +0100
+++ b/layer/ImageLayer.cpp	Thu Aug 20 14:54:21 2015 +0100
@@ -116,14 +116,14 @@
 }
 
 bool
-ImageLayer::isLayerScrollable(const View *) const
+ImageLayer::isLayerScrollable(const LayerGeometryProvider *) const
 {
     return true;
 }
 
 
 ImageModel::PointList
-ImageLayer::getLocalPoints(View *v, int x, int ) const
+ImageLayer::getLocalPoints(LayerGeometryProvider *v, int x, int ) const
 {
     if (!m_model) return ImageModel::PointList();
 
@@ -169,7 +169,7 @@
 }
 
 QString
-ImageLayer::getFeatureDescription(View *v, QPoint &pos) const
+ImageLayer::getFeatureDescription(LayerGeometryProvider *v, QPoint &pos) const
 {
     int x = pos.x();
 
@@ -208,7 +208,7 @@
 //!!! too much overlap with TimeValueLayer/TimeInstantLayer/TextLayer
 
 bool
-ImageLayer::snapToFeatureFrame(View *v, sv_frame_t &frame,
+ImageLayer::snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame,
 			      int &resolution,
 			      SnapType snap) const
 {
@@ -280,7 +280,7 @@
 }
 
 void
-ImageLayer::paint(View *v, QPainter &paint, QRect rect) const
+ImageLayer::paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const
 {
     if (!m_model || !m_model->isOK()) return;
 
@@ -290,7 +290,7 @@
 //    Profiler profiler("ImageLayer::paint", true);
 
 //    int x0 = rect.left(), x1 = rect.right();
-    int x0 = 0, x1 = v->width();
+    int x0 = 0, x1 = v->getPaintWidth();
 
     sv_frame_t frame0 = v->getFrameForX(x0);
     sv_frame_t frame1 = v->getFrameForX(x1);
@@ -299,7 +299,7 @@
     if (points.empty()) return;
 
     paint.save();
-    paint.setClipRect(rect.x(), 0, rect.width(), v->height());
+    paint.setClipRect(rect.x(), 0, rect.width(), v->getPaintHeight());
 
     QColor penColour;
     penColour = v->getForeground();
@@ -338,7 +338,7 @@
 }
 
 void
-ImageLayer::drawImage(View *v, QPainter &paint, const ImageModel::Point &p,
+ImageLayer::drawImage(LayerGeometryProvider *v, QPainter &paint, const ImageModel::Point &p,
                       int x, int nx) const
 {
     QString label = p.label;
@@ -358,12 +358,12 @@
     int bottomMargin = 10;
     int spacing = 5;
 
-    if (v->height() < 100) {
+    if (v->getPaintHeight() < 100) {
         topMargin = 5;
         bottomMargin = 5;
     }
 
-    int maxBoxHeight = v->height() - topMargin - bottomMargin;
+    int maxBoxHeight = v->getPaintHeight() - topMargin - bottomMargin;
 
     int availableWidth = nx - x - 3;
     if (availableWidth < 20) availableWidth = 20;
@@ -372,7 +372,7 @@
 
     if (label != "") {
 
-        int likelyHeight = v->height() / 4;
+        int likelyHeight = v->getPaintHeight() / 4;
 
         int likelyWidth = // available height times image aspect
             ((maxBoxHeight - likelyHeight) * imageSize.width())
@@ -435,10 +435,10 @@
         division += paint.fontMetrics().height();
     }                
 
-    bottomMargin = v->height() - topMargin - boxHeight;
-    if (bottomMargin > topMargin + v->height()/7) {
-        topMargin += v->height()/8;
-        bottomMargin -= v->height()/8;
+    bottomMargin = v->getPaintHeight() - topMargin - boxHeight;
+    if (bottomMargin > topMargin + v->getPaintHeight()/7) {
+        topMargin += v->getPaintHeight()/8;
+        bottomMargin -= v->getPaintHeight()/8;
     }
 
     paint.drawRect(x - 1,
@@ -480,7 +480,7 @@
 }
 
 void
-ImageLayer::setLayerDormant(const View *v, bool dormant)
+ImageLayer::setLayerDormant(const LayerGeometryProvider *v, bool dormant)
 {
     if (dormant) {
         // Delete the images named in the view's scaled map from the
@@ -517,7 +517,7 @@
 }
 
 QImage 
-ImageLayer::getImage(View *v, QString name, QSize maxSize) const
+ImageLayer::getImage(LayerGeometryProvider *v, QString name, QSize maxSize) const
 {
 //    SVDEBUG << "ImageLayer::getImage(" << v << ", " << name << ", ("
 //              << maxSize.width() << "x" << maxSize.height() << "))" << endl;
@@ -554,7 +554,7 @@
 }
 
 void
-ImageLayer::drawStart(View *v, QMouseEvent *e)
+ImageLayer::drawStart(LayerGeometryProvider *v, QMouseEvent *e)
 {
 //    SVDEBUG << "ImageLayer::drawStart(" << e->x() << "," << e->y() << ")" << endl;
 
@@ -578,7 +578,7 @@
 }
 
 void
-ImageLayer::drawDrag(View *v, QMouseEvent *e)
+ImageLayer::drawDrag(LayerGeometryProvider *v, QMouseEvent *e)
 {
 //    SVDEBUG << "ImageLayer::drawDrag(" << e->x() << "," << e->y() << ")" << endl;
 
@@ -594,7 +594,7 @@
 }
 
 void
-ImageLayer::drawEnd(View *, QMouseEvent *)
+ImageLayer::drawEnd(LayerGeometryProvider *, QMouseEvent *)
 {
 //    SVDEBUG << "ImageLayer::drawEnd(" << e->x() << "," << e->y() << ")" << endl;
     if (!m_model || !m_editing) return;
@@ -638,7 +638,7 @@
 }
 
 void
-ImageLayer::editStart(View *v, QMouseEvent *e)
+ImageLayer::editStart(LayerGeometryProvider *v, QMouseEvent *e)
 {
 //    SVDEBUG << "ImageLayer::editStart(" << e->x() << "," << e->y() << ")" << endl;
 
@@ -660,7 +660,7 @@
 }
 
 void
-ImageLayer::editDrag(View *v, QMouseEvent *e)
+ImageLayer::editDrag(LayerGeometryProvider *v, QMouseEvent *e)
 {
     if (!m_model || !m_editing) return;
 
@@ -680,7 +680,7 @@
 }
 
 void
-ImageLayer::editEnd(View *, QMouseEvent *)
+ImageLayer::editEnd(LayerGeometryProvider *, QMouseEvent *)
 {
 //    SVDEBUG << "ImageLayer::editEnd(" << e->x() << "," << e->y() << ")" << endl;
     if (!m_model || !m_editing) return;
@@ -694,7 +694,7 @@
 }
 
 bool
-ImageLayer::editOpen(View *v, QMouseEvent *e)
+ImageLayer::editOpen(LayerGeometryProvider *v, QMouseEvent *e)
 {
     if (!m_model) return false;
 
@@ -801,7 +801,7 @@
 }
 
 void
-ImageLayer::copy(View *v, Selection s, Clipboard &to)
+ImageLayer::copy(LayerGeometryProvider *v, Selection s, Clipboard &to)
 {
     if (!m_model) return;
 
@@ -819,7 +819,7 @@
 }
 
 bool
-ImageLayer::paste(View *v, const Clipboard &from, sv_frame_t /* frameOffset */, bool /* interactive */)
+ImageLayer::paste(LayerGeometryProvider *v, const Clipboard &from, sv_frame_t /* frameOffset */, bool /* interactive */)
 {
     if (!m_model) return false;
 
@@ -830,7 +830,7 @@
     if (clipboardHasDifferentAlignment(v, from)) {
 
         QMessageBox::StandardButton button =
-            QMessageBox::question(v, tr("Re-align pasted items?"),
+            QMessageBox::question(v->getView(), tr("Re-align pasted items?"),
                                   tr("The items you are pasting came from a layer with different source material from this one.  Do you want to re-align them in time, to match the source material for this layer?"),
                                   QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
                                   QMessageBox::Yes);
--- a/layer/ImageLayer.h	Mon Apr 13 13:52:05 2015 +0100
+++ b/layer/ImageLayer.h	Thu Aug 20 14:54:21 2015 +0100
@@ -38,31 +38,31 @@
     ImageLayer();
     virtual ~ImageLayer();
 
-    virtual void paint(View *v, QPainter &paint, QRect rect) const;
+    virtual void paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const;
 
-    virtual QString getFeatureDescription(View *v, QPoint &) const;
+    virtual QString getFeatureDescription(LayerGeometryProvider *v, QPoint &) const;
 
-    virtual bool snapToFeatureFrame(View *v, sv_frame_t &frame,
+    virtual bool snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame,
 				    int &resolution,
 				    SnapType snap) const;
 
-    virtual void drawStart(View *v, QMouseEvent *);
-    virtual void drawDrag(View *v, QMouseEvent *);
-    virtual void drawEnd(View *v, QMouseEvent *);
+    virtual void drawStart(LayerGeometryProvider *v, QMouseEvent *);
+    virtual void drawDrag(LayerGeometryProvider *v, QMouseEvent *);
+    virtual void drawEnd(LayerGeometryProvider *v, QMouseEvent *);
 
-    virtual void editStart(View *v, QMouseEvent *);
-    virtual void editDrag(View *v, QMouseEvent *);
-    virtual void editEnd(View *v, QMouseEvent *);
+    virtual void editStart(LayerGeometryProvider *v, QMouseEvent *);
+    virtual void editDrag(LayerGeometryProvider *v, QMouseEvent *);
+    virtual void editEnd(LayerGeometryProvider *v, QMouseEvent *);
 
     virtual void moveSelection(Selection s, sv_frame_t newStartFrame);
     virtual void resizeSelection(Selection s, Selection newSize);
     virtual void deleteSelection(Selection s);
 
-    virtual void copy(View *v, Selection s, Clipboard &to);
-    virtual bool paste(View *v, const Clipboard &from, sv_frame_t frameOffset,
+    virtual void copy(LayerGeometryProvider *v, Selection s, Clipboard &to);
+    virtual bool paste(LayerGeometryProvider *v, const Clipboard &from, sv_frame_t frameOffset,
                        bool interactive);
 
-    virtual bool editOpen(View *, QMouseEvent *); // on double-click
+    virtual bool editOpen(LayerGeometryProvider *, QMouseEvent *); // on double-click
 
     virtual const Model *getModel() const { return m_model; }
     void setModel(ImageModel *model);
@@ -80,11 +80,11 @@
         return ColourAbsent;
     }
 
-    virtual bool isLayerScrollable(const View *v) const;
+    virtual bool isLayerScrollable(const LayerGeometryProvider *v) const;
 
     virtual bool isLayerEditable() const { return true; }
 
-    virtual int getCompletion(View *) const { return m_model->getCompletion(); }
+    virtual int getCompletion(LayerGeometryProvider *) const { return m_model->getCompletion(); }
 
     virtual bool getValueExtents(double &min, double &max,
                                  bool &logarithmic, QString &unit) const;
@@ -92,9 +92,9 @@
     virtual void toXml(QTextStream &stream, QString indent = "",
                        QString extraAttributes = "") const;
 
-    virtual int getVerticalScaleWidth(View *, bool, QPainter &) const { return 0; }
+    virtual int getVerticalScaleWidth(LayerGeometryProvider *, bool, QPainter &) const { return 0; }
 
-    virtual void setLayerDormant(const View *v, bool dormant);
+    virtual void setLayerDormant(const LayerGeometryProvider *v, bool dormant);
 
     void setProperties(const QXmlAttributes &attributes);
 
@@ -105,18 +105,18 @@
     void fileSourceReady();
 
 protected:
-    ImageModel::PointList getLocalPoints(View *v, int x, int y) const;
+    ImageModel::PointList getLocalPoints(LayerGeometryProvider *v, int x, int y) const;
 
     bool getImageOriginalSize(QString name, QSize &size) const;
-    QImage getImage(View *v, QString name, QSize maxSize) const;
+    QImage getImage(LayerGeometryProvider *v, QString name, QSize maxSize) const;
 
-    void drawImage(View *v, QPainter &paint, const ImageModel::Point &p,
+    void drawImage(LayerGeometryProvider *v, QPainter &paint, const ImageModel::Point &p,
                    int x, int nx) const;
 
     //!!! how to reap no-longer-used images?
 
     typedef std::map<QString, QImage> ImageMap;
-    typedef std::map<const View *, ImageMap> ViewImageMap;
+    typedef std::map<const LayerGeometryProvider *, ImageMap> ViewImageMap;
     typedef std::map<QString, FileSource *> FileSourceMap;
 
     static ImageMap m_images;
--- a/layer/Layer.cpp	Mon Apr 13 13:52:05 2015 +0100
+++ b/layer/Layer.cpp	Thu Aug 20 14:54:21 2015 +0100
@@ -115,7 +115,7 @@
 }
 
 void
-Layer::setLayerDormant(const View *v, bool dormant)
+Layer::setLayerDormant(const LayerGeometryProvider *v, bool dormant)
 {
     const void *vv = (const void *)v;
     QMutexLocker locker(&m_dormancyMutex);
@@ -123,7 +123,7 @@
 }
 
 bool
-Layer::isLayerDormant(const View *v) const
+Layer::isLayerDormant(const LayerGeometryProvider *v) const
 {
     const void *vv = (const void *)v;
     QMutexLocker locker(&m_dormancyMutex);
@@ -132,14 +132,14 @@
 }
 
 void
-Layer::showLayer(View *view, bool show)
+Layer::showLayer(LayerGeometryProvider *view, bool show)
 {
     setLayerDormant(view, !show);
     emit layerParametersChanged();
 }
 
 bool
-Layer::getXScaleValue(const View *v, int x, double &value, QString &unit) const
+Layer::getXScaleValue(const LayerGeometryProvider *v, int x, double &value, QString &unit) const
 {
     if (!hasTimeXAxis()) return false;
 
@@ -152,7 +152,7 @@
 }
 
 bool
-Layer::getYScaleDifference(const View *v, int y0, int y1,
+Layer::getYScaleDifference(const LayerGeometryProvider *v, int y0, int y1,
                            double &diff, QString &unit) const
 {
     double v0, v1;
@@ -166,31 +166,31 @@
 }
 
 sv_frame_t
-Layer::alignToReference(View *v, sv_frame_t frame) const
+Layer::alignToReference(LayerGeometryProvider *v, sv_frame_t frame) const
 {
     const Model *m = getModel();
     SVDEBUG << "Layer::alignToReference(" << frame << "): model = " << m << ", alignment reference = " << (m ? m->getAlignmentReference() : 0) << endl;
     if (m && m->getAlignmentReference()) {
         return m->alignToReference(frame);
     } else {
-        return v->alignToReference(frame);
+        return v->getView()->alignToReference(frame);
     }
 }
 
 sv_frame_t
-Layer::alignFromReference(View *v, sv_frame_t frame) const
+Layer::alignFromReference(LayerGeometryProvider *v, sv_frame_t frame) const
 {
     const Model *m = getModel();
     SVDEBUG << "Layer::alignFromReference(" << frame << "): model = " << m << ", alignment reference = " << (m ? m->getAlignmentReference() : 0) << endl;
     if (m && m->getAlignmentReference()) {
         return m->alignFromReference(frame);
     } else {
-        return v->alignFromReference(frame);
+        return v->getView()->alignFromReference(frame);
     }
 }
 
 bool
-Layer::clipboardHasDifferentAlignment(View *v, const Clipboard &clip) const
+Layer::clipboardHasDifferentAlignment(LayerGeometryProvider *v, const Clipboard &clip) const
 {
     // Notes on pasting to an aligned layer:
     // 
@@ -371,7 +371,7 @@
 }
 
 void
-Layer::measureStart(View *v, QMouseEvent *e)
+Layer::measureStart(LayerGeometryProvider *v, QMouseEvent *e)
 {
     setMeasureRectFromPixrect(v, m_draggingRect,
                               QRect(e->x(), e->y(), 0, 0));
@@ -379,7 +379,7 @@
 }
 
 void
-Layer::measureDrag(View *v, QMouseEvent *e)
+Layer::measureDrag(LayerGeometryProvider *v, QMouseEvent *e)
 {
     if (!m_haveDraggingRect) return;
 
@@ -391,7 +391,7 @@
 }
 
 void
-Layer::measureEnd(View *v, QMouseEvent *e)
+Layer::measureEnd(LayerGeometryProvider *v, QMouseEvent *e)
 {
     if (!m_haveDraggingRect) return;
     measureDrag(v, e);
@@ -405,7 +405,7 @@
 }
 
 void
-Layer::measureDoubleClick(View *, QMouseEvent *)
+Layer::measureDoubleClick(LayerGeometryProvider *, QMouseEvent *)
 {
     // nothing, in the base class
 }
@@ -425,7 +425,7 @@
 }
 
 void
-Layer::paintMeasurementRects(View *v, QPainter &paint,
+Layer::paintMeasurementRects(LayerGeometryProvider *v, QPainter &paint,
                              bool showFocus, QPoint focusPoint) const
 {
     updateMeasurePixrects(v);
@@ -457,7 +457,7 @@
 }
 
 bool
-Layer::nearestMeasurementRectChanged(View *v, QPoint prev, QPoint now) const
+Layer::nearestMeasurementRectChanged(LayerGeometryProvider *v, QPoint prev, QPoint now) const
 {
     updateMeasurePixrects(v);
     
@@ -468,7 +468,7 @@
 }
 
 void
-Layer::updateMeasurePixrects(View *v) const
+Layer::updateMeasurePixrects(LayerGeometryProvider *v) const
 {
     sv_frame_t sf = v->getStartFrame();
     sv_frame_t ef = v->getEndFrame();
@@ -507,26 +507,26 @@
 }
 
 void
-Layer::updateMeasureRectYCoords(View *v, const MeasureRect &r) const
+Layer::updateMeasureRectYCoords(LayerGeometryProvider *v, const MeasureRect &r) const
 {
-    int y0 = int(lrint(r.startY * v->height()));
-    int y1 = int(lrint(r.endY * v->height()));
+    int y0 = int(lrint(r.startY * v->getPaintHeight()));
+    int y1 = int(lrint(r.endY * v->getPaintHeight()));
     r.pixrect = QRect(r.pixrect.x(), y0, r.pixrect.width(), y1 - y0);
 }
 
 void
-Layer::setMeasureRectYCoord(View *v, MeasureRect &r, bool start, int y) const
+Layer::setMeasureRectYCoord(LayerGeometryProvider *v, MeasureRect &r, bool start, int y) const
 {
     if (start) {
-        r.startY = double(y) / double(v->height());
+        r.startY = double(y) / double(v->getPaintHeight());
         r.endY = r.startY;
     } else {
-        r.endY = double(y) / double(v->height());
+        r.endY = double(y) / double(v->getPaintHeight());
     }
 }
 
 void
-Layer::setMeasureRectFromPixrect(View *v, MeasureRect &r, QRect pixrect) const
+Layer::setMeasureRectFromPixrect(LayerGeometryProvider *v, MeasureRect &r, QRect pixrect) const
 {
     r.pixrect = pixrect;
     r.haveFrames = hasTimeXAxis();
@@ -566,13 +566,13 @@
 }
 
 void
-Layer::paintMeasurementRect(View *v, QPainter &paint,
+Layer::paintMeasurementRect(LayerGeometryProvider *v, QPainter &paint,
                             const MeasureRect &r, bool focus) const
 {
     if (r.haveFrames) {
         
         int x0 = -1;
-        int x1 = v->width() + 1;
+        int x1 = v->getPaintWidth() + 1;
         
         if (r.startFrame >= v->getStartFrame()) {
             x0 = v->getXForFrame(r.startFrame);
--- a/layer/Layer.h	Mon Apr 13 13:52:05 2015 +0100
+++ b/layer/Layer.h	Thu Aug 20 14:54:21 2015 +0100
@@ -39,6 +39,7 @@
 class Model;
 class QPainter;
 class View;
+class LayerGeometryProvider;
 class QMouseEvent;
 class Clipboard;
 class RangeMapper;
@@ -62,7 +63,7 @@
     Model *getModel() {
 	return const_cast<Model *>(const_cast<const Layer *>(this)->getModel());
     }
-
+    
     /**
      * Return a zoom constraint object defining the supported zoom
      * levels for this layer.  If this returns zero, the layer will
@@ -83,12 +84,13 @@
     /**
      * Paint the given rectangle of this layer onto the given view
      * using the given painter, superimposing it on top of any
-     * existing material in that view.  The view is provided here
-     * because it is possible for one layer to exist in more than one
-     * view, so the dimensions of the view may vary from one paint
-     * call to another (without any view having been resized).
+     * existing material in that view.  The LayerGeometryProvider (an
+     * interface implemented by View) is provided here because it is
+     * possible for one layer to exist in more than one view, so the
+     * dimensions of the view may vary from one paint call to another
+     * (without any view having been resized).
      */
-    virtual void paint(View *, QPainter &, QRect) const = 0;   
+    virtual void paint(LayerGeometryProvider *, QPainter &, QRect) const = 0;   
 
     /**
      * Enable or disable synchronous painting.  If synchronous
@@ -128,25 +130,25 @@
     virtual QString getLayerPresentationName() const;
     virtual QPixmap getLayerPresentationPixmap(QSize) const { return QPixmap(); }
 
-    virtual int getVerticalScaleWidth(View *, bool detailed,
+    virtual int getVerticalScaleWidth(LayerGeometryProvider *, bool detailed,
                                       QPainter &) const = 0;
 
-    virtual void paintVerticalScale(View *, bool /* detailed */,
+    virtual void paintVerticalScale(LayerGeometryProvider *, bool /* detailed */,
                                     QPainter &, QRect) const { }
 
-    virtual bool getCrosshairExtents(View *, QPainter &, QPoint /* cursorPos */,
+    virtual bool getCrosshairExtents(LayerGeometryProvider *, QPainter &, QPoint /* cursorPos */,
                                      std::vector<QRect> &) const {
         return false;
     }
-    virtual void paintCrosshairs(View *, QPainter &, QPoint) const { }
+    virtual void paintCrosshairs(LayerGeometryProvider *, QPainter &, QPoint) const { }
 
-    virtual void paintMeasurementRects(View *, QPainter &,
+    virtual void paintMeasurementRects(LayerGeometryProvider *, QPainter &,
                                        bool showFocus, QPoint focusPoint) const;
 
-    virtual bool nearestMeasurementRectChanged(View *, QPoint prev,
+    virtual bool nearestMeasurementRectChanged(LayerGeometryProvider *, QPoint prev,
                                                QPoint now) const;
 
-    virtual QString getFeatureDescription(View *, QPoint &) const {
+    virtual QString getFeatureDescription(LayerGeometryProvider *, QPoint &) const {
 	return "";
     }
 
@@ -180,7 +182,7 @@
      * (and leave frame unmodified).  If returning true, also return
      * the resolution of the model in this layer in sample frames.
      */
-    virtual bool snapToFeatureFrame(View * /* v */,
+    virtual bool snapToFeatureFrame(LayerGeometryProvider * /* v */,
 				    sv_frame_t & /* frame */,
 				    int &resolution,
 				    SnapType /* snap */) const {
@@ -204,7 +206,7 @@
      * (and leave frame unmodified).  If returning true, also return
      * the resolution of the model in this layer in sample frames.
      */
-    virtual bool snapToSimilarFeature(View * /* v */,
+    virtual bool snapToSimilarFeature(LayerGeometryProvider * /* v */,
                                       sv_frame_t & /* source frame */,
                                       int &resolution,
                                       SnapType /* snap */) const {
@@ -217,30 +219,30 @@
     // Layer needs to get actual mouse events, I guess.  Draw mode is
     // probably the easier.
 
-    virtual void drawStart(View *, QMouseEvent *) { }
-    virtual void drawDrag(View *, QMouseEvent *) { }
-    virtual void drawEnd(View *, QMouseEvent *) { }
+    virtual void drawStart(LayerGeometryProvider *, QMouseEvent *) { }
+    virtual void drawDrag(LayerGeometryProvider *, QMouseEvent *) { }
+    virtual void drawEnd(LayerGeometryProvider *, QMouseEvent *) { }
 
-    virtual void eraseStart(View *, QMouseEvent *) { }
-    virtual void eraseDrag(View *, QMouseEvent *) { }
-    virtual void eraseEnd(View *, QMouseEvent *) { }
+    virtual void eraseStart(LayerGeometryProvider *, QMouseEvent *) { }
+    virtual void eraseDrag(LayerGeometryProvider *, QMouseEvent *) { }
+    virtual void eraseEnd(LayerGeometryProvider *, QMouseEvent *) { }
 
-    virtual void editStart(View *, QMouseEvent *) { }
-    virtual void editDrag(View *, QMouseEvent *) { }
-    virtual void editEnd(View *, QMouseEvent *) { }
+    virtual void editStart(LayerGeometryProvider *, QMouseEvent *) { }
+    virtual void editDrag(LayerGeometryProvider *, QMouseEvent *) { }
+    virtual void editEnd(LayerGeometryProvider *, QMouseEvent *) { }
 
-    virtual void splitStart(View *, QMouseEvent *) { }
-    virtual void splitEnd(View *, QMouseEvent *) { }
-    virtual void addNote(View *, QMouseEvent *) { };
+    virtual void splitStart(LayerGeometryProvider *, QMouseEvent *) { }
+    virtual void splitEnd(LayerGeometryProvider *, QMouseEvent *) { }
+    virtual void addNote(LayerGeometryProvider *, QMouseEvent *) { };
 
     // Measurement rectangle (or equivalent).  Unlike draw and edit,
     // the base Layer class can provide working implementations of
     // these for most situations.
     //
-    virtual void measureStart(View *, QMouseEvent *);
-    virtual void measureDrag(View *, QMouseEvent *);
-    virtual void measureEnd(View *, QMouseEvent *);
-    virtual void measureDoubleClick(View *, QMouseEvent *);
+    virtual void measureStart(LayerGeometryProvider *, QMouseEvent *);
+    virtual void measureDrag(LayerGeometryProvider *, QMouseEvent *);
+    virtual void measureEnd(LayerGeometryProvider *, QMouseEvent *);
+    virtual void measureDoubleClick(LayerGeometryProvider *, QMouseEvent *);
 
     virtual bool haveCurrentMeasureRect() const {
         return m_haveCurrentMeasureRect;
@@ -252,13 +254,13 @@
      * double-click).  If there is no item or editing is not
      * supported, return false.
      */
-    virtual bool editOpen(View *, QMouseEvent *) { return false; }
+    virtual bool editOpen(LayerGeometryProvider *, QMouseEvent *) { return false; }
 
     virtual void moveSelection(Selection, sv_frame_t /* newStartFrame */) { }
     virtual void resizeSelection(Selection, Selection /* newSize */) { }
     virtual void deleteSelection(Selection) { }
 
-    virtual void copy(View *, Selection, Clipboard & /* to */) { }
+    virtual void copy(LayerGeometryProvider *, Selection, Clipboard & /* to */) { }
 
     /**
      * Paste from the given clipboard onto the layer at the given
@@ -267,7 +269,7 @@
      * return false if the user cancelled the paste operation.  This
      * function should return true if a paste actually occurred.
      */
-    virtual bool paste(View *,
+    virtual bool paste(LayerGeometryProvider *,
                        const Clipboard & /* from */,
                        sv_frame_t /* frameOffset */,
                        bool /* interactive */) { return false; }
@@ -289,7 +291,7 @@
      * scrolling better if it is known that individual views can be
      * scrolled safely in this way.
      */
-    virtual bool isLayerScrollable(const View *) const { return true; }
+    virtual bool isLayerScrollable(const LayerGeometryProvider *) const { return true; }
 
     /**
      * This should return true if the layer completely obscures any
@@ -344,14 +346,14 @@
      * isReady(int *) call.  The view may choose to show a progress
      * meter if it finds that this returns < 100 at any given moment.
      */
-    virtual int getCompletion(View *) const { return 100; }
+    virtual int getCompletion(LayerGeometryProvider *) const { return 100; }
 
     /**
      * Return an error string if any errors have occurred while
      * loading or processing data for the given view.  Return the
      * empty string if no error has occurred.
      */
-    virtual QString getError(View *) const { return ""; }
+    virtual QString getError(LayerGeometryProvider *) const { return ""; }
 
     virtual void setObjectName(const QString &name);
 
@@ -400,13 +402,13 @@
      * A layer class that overrides this function must also call this
      * class's implementation.
      */
-    virtual void setLayerDormant(const View *v, bool dormant);
+    virtual void setLayerDormant(const LayerGeometryProvider *v, bool dormant);
 
     /**
      * Return whether the layer is dormant (i.e. hidden) in the given
      * view.
      */
-    virtual bool isLayerDormant(const View *v) const;
+    virtual bool isLayerDormant(const LayerGeometryProvider *v) const;
 
     virtual PlayParameters *getPlayParameters();
 
@@ -457,14 +459,14 @@
      * measurement tool.  The default implementation works correctly
      * if the layer hasTimeXAxis().
      */
-    virtual bool getXScaleValue(const View *v, int x,
+    virtual bool getXScaleValue(const LayerGeometryProvider *v, int x,
                                 double &value, QString &unit) const;
 
     /** 
      * Return the value and unit at the given y coordinate in the
      * given view.
      */
-    virtual bool getYScaleValue(const View *, int /* y */,
+    virtual bool getYScaleValue(const LayerGeometryProvider *, int /* y */,
                                 double &/* value */, QString &/* unit */) const {
         return false;
     }
@@ -475,7 +477,7 @@
      * The default implementation just calls getYScaleValue twice and
      * returns the difference, with the same unit.
      */
-    virtual bool getYScaleDifference(const View *v, int y0, int y1,
+    virtual bool getYScaleDifference(const LayerGeometryProvider *v, int y0, int y1,
                                      double &diff, QString &unit) const;
         
     /**
@@ -518,8 +520,15 @@
      */
     virtual RangeMapper *getNewVerticalZoomRangeMapper() const { return 0; }
 
+    /**
+     * Return true if this layer type can function without a model
+     * being set. If false (the default), the layer will not be loaded
+     * from a session if its model cannot be found.
+     */
+    virtual bool canExistWithoutModel() const { return false; }
+
 public slots:
-    void showLayer(View *, bool show);
+    void showLayer(LayerGeometryProvider *, bool show);
 
 signals:
     void modelChanged();
@@ -538,9 +547,9 @@
 protected:
     void connectSignals(const Model *);
 
-    virtual sv_frame_t alignToReference(View *v, sv_frame_t frame) const;
-    virtual sv_frame_t alignFromReference(View *v, sv_frame_t frame) const;
-    bool clipboardHasDifferentAlignment(View *v, const Clipboard &clip) const;
+    virtual sv_frame_t alignToReference(LayerGeometryProvider *v, sv_frame_t frame) const;
+    virtual sv_frame_t alignFromReference(LayerGeometryProvider *v, sv_frame_t frame) const;
+    bool clipboardHasDifferentAlignment(LayerGeometryProvider *v, const Clipboard &clip) const;
 
     struct MeasureRect {
 
@@ -605,16 +614,16 @@
     // Note that pixrects are only correct for a single view.
     // So we should update them at the start of the paint procedure
     // (painting is single threaded) and only use them after that.
-    void updateMeasurePixrects(View *v) const;
+    void updateMeasurePixrects(LayerGeometryProvider *v) const;
 
-    virtual void updateMeasureRectYCoords(View *v, const MeasureRect &r) const;
-    virtual void setMeasureRectYCoord(View *v, MeasureRect &r, bool start, int y) const;
-    virtual void setMeasureRectFromPixrect(View *v, MeasureRect &r, QRect pixrect) const;
+    virtual void updateMeasureRectYCoords(LayerGeometryProvider *v, const MeasureRect &r) const;
+    virtual void setMeasureRectYCoord(LayerGeometryProvider *v, MeasureRect &r, bool start, int y) const;
+    virtual void setMeasureRectFromPixrect(LayerGeometryProvider *v, MeasureRect &r, QRect pixrect) const;
 
     // This assumes updateMeasurementPixrects has been called
     MeasureRectSet::const_iterator findFocusedMeasureRect(QPoint) const;
 
-    void paintMeasurementRect(View *v, QPainter &paint,
+    void paintMeasurementRect(LayerGeometryProvider *v, QPainter &paint,
                               const MeasureRect &r, bool focus) const;
 
     QString m_presentationName;
--- a/layer/LayerFactory.cpp	Mon Apr 13 13:52:05 2015 +0100
+++ b/layer/LayerFactory.cpp	Thu Aug 20 14:54:21 2015 +0100
@@ -198,7 +198,10 @@
     LayerTypeSet types;
     types.insert(TimeInstants);
     types.insert(TimeValues);
-    types.insert(FlexiNotes);
+    // Because this is strictly a UI function -- list the layer types
+    // to show in a menu -- it should not contain FlexiNotes; the
+    // layer isn't meaningfully editable in SV
+//    types.insert(FlexiNotes);
     types.insert(Notes);
     types.insert(Regions);
     types.insert(Text);
--- a/layer/LayerFactory.h	Mon Apr 13 13:52:05 2015 +0100
+++ b/layer/LayerFactory.h	Thu Aug 20 14:54:21 2015 +0100
@@ -57,6 +57,11 @@
 
     typedef std::set<LayerType> LayerTypeSet;
     LayerTypeSet getValidLayerTypes(Model *model);
+
+    /**
+     * Return the set of layer types that an end user should be
+     * allowed to create, empty, for subsequent editing.
+     */
     LayerTypeSet getValidEmptyLayerTypes();
 
     LayerType getLayerType(const Layer *);
--- a/layer/LinearColourScale.cpp	Mon Apr 13 13:52:05 2015 +0100
+++ b/layer/LinearColourScale.cpp	Thu Aug 20 14:54:21 2015 +0100
@@ -20,24 +20,24 @@
 
 #include <cmath>
 
-#include "view/View.h"
+#include "view/LayerGeometryProvider.h"
 
 int
-LinearColourScale::getWidth(View *,
+LinearColourScale::getWidth(LayerGeometryProvider *,
 			    QPainter &paint)
 {
     return paint.fontMetrics().width("-000.00") + 15;
 }
 
 void
-LinearColourScale::paintVertical(View *v,
+LinearColourScale::paintVertical(LayerGeometryProvider *v,
 				 const ColourScaleLayer *layer,
 				 QPainter &paint,
 				 int /* x0 */,
 				 double min,
 				 double max)
 {
-    int h = v->height();
+    int h = v->getPaintHeight();
 
     int n = 10;
 
--- a/layer/LinearColourScale.h	Mon Apr 13 13:52:05 2015 +0100
+++ b/layer/LinearColourScale.h	Thu Aug 20 14:54:21 2015 +0100
@@ -19,16 +19,16 @@
 #include <QRect>
 
 class QPainter;
-class View;
+class LayerGeometryProvider;
 class ColourScaleLayer;
 
 class LinearColourScale
 {
 public:
-    int getWidth(View *v, QPainter &paint);
+    int getWidth(LayerGeometryProvider *v, QPainter &paint);
 
     void paintVertical
-    (View *v, const ColourScaleLayer *layer, QPainter &paint, int x0,
+    (LayerGeometryProvider *v, const ColourScaleLayer *layer, QPainter &paint, int x0,
      double minf, double maxf);
 };
 
--- a/layer/LinearNumericalScale.cpp	Mon Apr 13 13:52:05 2015 +0100
+++ b/layer/LinearNumericalScale.cpp	Thu Aug 20 14:54:21 2015 +0100
@@ -23,14 +23,14 @@
 #include "view/View.h"
 
 int
-LinearNumericalScale::getWidth(View *,
+LinearNumericalScale::getWidth(LayerGeometryProvider *,
 			       QPainter &paint)
 {
     return paint.fontMetrics().width("-000.00") + 10;
 }
 
 void
-LinearNumericalScale::paintVertical(View *v,
+LinearNumericalScale::paintVertical(LayerGeometryProvider *v,
 				    const VerticalScaleLayer *layer,
 				    QPainter &paint,
 				    int x0,
@@ -69,7 +69,7 @@
         double dispval = val;
 
 	if (i == n-1 &&
-	    v->height() < paint.fontMetrics().height() * (n*2)) {
+	    v->getPaintHeight() < paint.fontMetrics().height() * (n*2)) {
 	    if (layer->getScaleUnits() != "") drawText = false;
 	}
 	dispval = int(rint(val / round) * round);
--- a/layer/LinearNumericalScale.h	Mon Apr 13 13:52:05 2015 +0100
+++ b/layer/LinearNumericalScale.h	Thu Aug 20 14:54:21 2015 +0100
@@ -19,17 +19,17 @@
 #include <QRect>
 
 class QPainter;
-class View;
+class LayerGeometryProvider;
 class VerticalScaleLayer;
 
 class LinearNumericalScale
 {
 public:
-    int getWidth(View *v, QPainter &paint);
+    int getWidth(LayerGeometryProvider *v, QPainter &paint);
 
     void paintVertical
-    (View *v, const VerticalScaleLayer *layer, QPainter &paint, int x0,
-     double minf, double maxf);
+    (LayerGeometryProvider *v, const VerticalScaleLayer *layer,
+     QPainter &paint, int x0, double minf, double maxf);
 };
 
 #endif
--- a/layer/LogColourScale.cpp	Mon Apr 13 13:52:05 2015 +0100
+++ b/layer/LogColourScale.cpp	Thu Aug 20 14:54:21 2015 +0100
@@ -25,21 +25,21 @@
 #include "view/View.h"
 
 int
-LogColourScale::getWidth(View *,
+LogColourScale::getWidth(LayerGeometryProvider *,
 			    QPainter &paint)
 {
     return paint.fontMetrics().width("-000.00") + 15;
 }
 
 void
-LogColourScale::paintVertical(View *v,
+LogColourScale::paintVertical(LayerGeometryProvider *v,
 			      const ColourScaleLayer *layer,
 			      QPainter &paint,
 			      int /* x0 */,
 			      double minlog,
 			      double maxlog)
 {
-    int h = v->height();
+    int h = v->getPaintHeight();
 
     int n = 10;
 
--- a/layer/LogColourScale.h	Mon Apr 13 13:52:05 2015 +0100
+++ b/layer/LogColourScale.h	Thu Aug 20 14:54:21 2015 +0100
@@ -19,16 +19,16 @@
 #include <QRect>
 
 class QPainter;
-class View;
+class LayerGeometryProvider;
 class ColourScaleLayer;
 
 class LogColourScale
 {
 public:
-    int getWidth(View *v, QPainter &paint);
+    int getWidth(LayerGeometryProvider *v, QPainter &paint);
 
     void paintVertical
-    (View *v, const ColourScaleLayer *layer, QPainter &paint, int x0,
+    (LayerGeometryProvider *v, const ColourScaleLayer *layer, QPainter &paint, int x0,
      double minf, double maxf);
 };
 
--- a/layer/LogNumericalScale.cpp	Mon Apr 13 13:52:05 2015 +0100
+++ b/layer/LogNumericalScale.cpp	Thu Aug 20 14:54:21 2015 +0100
@@ -27,14 +27,14 @@
 //#define DEBUG_TIME_VALUE_LAYER 1
 
 int
-LogNumericalScale::getWidth(View *,
+LogNumericalScale::getWidth(LayerGeometryProvider *,
 			    QPainter &paint)
 {
     return paint.fontMetrics().width("-000.00") + 10;
 }
 
 void
-LogNumericalScale::paintVertical(View *v,
+LogNumericalScale::paintVertical(LayerGeometryProvider *v,
 				 const VerticalScaleLayer *layer,
 				 QPainter &paint,
 				 int x0,
@@ -79,7 +79,7 @@
         bool drawText = true;
 
 	if (i == n-1 &&
-	    v->height() < paint.fontMetrics().height() * (n*2)) {
+	    v->getPaintHeight() < paint.fontMetrics().height() * (n*2)) {
 	    if (layer->getScaleUnits() != "") drawText = false;
 	}
 
--- a/layer/LogNumericalScale.h	Mon Apr 13 13:52:05 2015 +0100
+++ b/layer/LogNumericalScale.h	Thu Aug 20 14:54:21 2015 +0100
@@ -19,16 +19,16 @@
 #include <QRect>
 
 class QPainter;
-class View;
+class LayerGeometryProvider;
 class VerticalScaleLayer;
 
 class LogNumericalScale
 {
 public:
-    int getWidth(View *v, QPainter &paint);
+    int getWidth(LayerGeometryProvider *v, QPainter &paint);
 
     void paintVertical
-    (View *v, const VerticalScaleLayer *layer, QPainter &paint, int x0,
+    (LayerGeometryProvider *v, const VerticalScaleLayer *layer, QPainter &paint, int x0,
      double minlog, double maxlog);
 };
 
--- a/layer/NoteLayer.cpp	Mon Apr 13 13:52:05 2015 +0100
+++ b/layer/NoteLayer.cpp	Thu Aug 20 14:54:21 2015 +0100
@@ -191,7 +191,7 @@
 }
 
 bool
-NoteLayer::isLayerScrollable(const View *v) const
+NoteLayer::isLayerScrollable(const LayerGeometryProvider *v) const
 {
     QPoint discard;
     return !v->shouldIlluminateLocalFeatures(this, discard);
@@ -389,7 +389,7 @@
 }
 
 NoteModel::PointList
-NoteLayer::getLocalPoints(View *v, int x) const
+NoteLayer::getLocalPoints(LayerGeometryProvider *v, int x) const
 {
     if (!m_model) return NoteModel::PointList();
 
@@ -432,7 +432,7 @@
 }
 
 bool
-NoteLayer::getPointToDrag(View *v, int x, int y, NoteModel::Point &p) const
+NoteLayer::getPointToDrag(LayerGeometryProvider *v, int x, int y, NoteModel::Point &p) const
 {
     if (!m_model) return false;
 
@@ -460,7 +460,7 @@
 }
 
 QString
-NoteLayer::getFeatureDescription(View *v, QPoint &pos) const
+NoteLayer::getFeatureDescription(LayerGeometryProvider *v, QPoint &pos) const
 {
     int x = pos.x();
 
@@ -547,7 +547,7 @@
 }
 
 bool
-NoteLayer::snapToFeatureFrame(View *v, sv_frame_t &frame,
+NoteLayer::snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame,
 			      int &resolution,
 			      SnapType snap) const
 {
@@ -619,7 +619,7 @@
 }
 
 void
-NoteLayer::getScaleExtents(View *v, double &min, double &max, bool &log) const
+NoteLayer::getScaleExtents(LayerGeometryProvider *v, double &min, double &max, bool &log) const
 {
     min = 0.0;
     max = 0.0;
@@ -677,11 +677,11 @@
 }
 
 int
-NoteLayer::getYForValue(View *v, double val) const
+NoteLayer::getYForValue(LayerGeometryProvider *v, double val) const
 {
     double min = 0.0, max = 0.0;
     bool logarithmic = false;
-    int h = v->height();
+    int h = v->getPaintHeight();
 
     getScaleExtents(v, min, max, logarithmic);
 
@@ -712,11 +712,11 @@
 }
 
 double
-NoteLayer::getValueForY(View *v, int y) const
+NoteLayer::getValueForY(LayerGeometryProvider *v, int y) const
 {
     double min = 0.0, max = 0.0;
     bool logarithmic = false;
-    int h = v->height();
+    int h = v->getPaintHeight();
 
     getScaleExtents(v, min, max, logarithmic);
 
@@ -741,7 +741,7 @@
 }
 
 void
-NoteLayer::paint(View *v, QPainter &paint, QRect rect) const
+NoteLayer::paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const
 {
     if (!m_model || !m_model->isOK()) return;
 
@@ -830,7 +830,7 @@
 }
 
 int
-NoteLayer::getVerticalScaleWidth(View *v, bool, QPainter &paint) const
+NoteLayer::getVerticalScaleWidth(LayerGeometryProvider *v, bool, QPainter &paint) const
 {
     if (!m_model || shouldAutoAlign()) {
         return 0;
@@ -844,7 +844,7 @@
 }
 
 void
-NoteLayer::paintVerticalScale(View *v, bool, QPainter &paint, QRect) const
+NoteLayer::paintVerticalScale(LayerGeometryProvider *v, bool, QPainter &paint, QRect) const
 {
     if (!m_model || m_model->getPoints().empty()) return;
 
@@ -853,7 +853,7 @@
     bool logarithmic;
 
     int w = getVerticalScaleWidth(v, false, paint);
-    int h = v->height();
+    int h = v->getPaintHeight();
 
     getScaleExtents(v, min, max, logarithmic);
 
@@ -882,7 +882,7 @@
 }
 
 void
-NoteLayer::drawStart(View *v, QMouseEvent *e)
+NoteLayer::drawStart(LayerGeometryProvider *v, QMouseEvent *e)
 {
 //    SVDEBUG << "NoteLayer::drawStart(" << e->x() << "," << e->y() << ")" << endl;
 
@@ -906,7 +906,7 @@
 }
 
 void
-NoteLayer::drawDrag(View *v, QMouseEvent *e)
+NoteLayer::drawDrag(LayerGeometryProvider *v, QMouseEvent *e)
 {
 //    SVDEBUG << "NoteLayer::drawDrag(" << e->x() << "," << e->y() << ")" << endl;
 
@@ -935,7 +935,7 @@
 }
 
 void
-NoteLayer::drawEnd(View *, QMouseEvent *)
+NoteLayer::drawEnd(LayerGeometryProvider *, QMouseEvent *)
 {
 //    SVDEBUG << "NoteLayer::drawEnd(" << e->x() << "," << e->y() << ")" << endl;
     if (!m_model || !m_editing) return;
@@ -945,7 +945,7 @@
 }
 
 void
-NoteLayer::eraseStart(View *v, QMouseEvent *e)
+NoteLayer::eraseStart(LayerGeometryProvider *v, QMouseEvent *e)
 {
     if (!m_model) return;
 
@@ -960,12 +960,12 @@
 }
 
 void
-NoteLayer::eraseDrag(View *, QMouseEvent *)
+NoteLayer::eraseDrag(LayerGeometryProvider *, QMouseEvent *)
 {
 }
 
 void
-NoteLayer::eraseEnd(View *v, QMouseEvent *e)
+NoteLayer::eraseEnd(LayerGeometryProvider *v, QMouseEvent *e)
 {
     if (!m_model || !m_editing) return;
 
@@ -985,7 +985,7 @@
 }
 
 void
-NoteLayer::editStart(View *v, QMouseEvent *e)
+NoteLayer::editStart(LayerGeometryProvider *v, QMouseEvent *e)
 {
 //    SVDEBUG << "NoteLayer::editStart(" << e->x() << "," << e->y() << ")" << endl;
 
@@ -1008,7 +1008,7 @@
 }
 
 void
-NoteLayer::editDrag(View *v, QMouseEvent *e)
+NoteLayer::editDrag(LayerGeometryProvider *v, QMouseEvent *e)
 {
 //    SVDEBUG << "NoteLayer::editDrag(" << e->x() << "," << e->y() << ")" << endl;
 
@@ -1037,7 +1037,7 @@
 }
 
 void
-NoteLayer::editEnd(View *, QMouseEvent *)
+NoteLayer::editEnd(LayerGeometryProvider *, QMouseEvent *)
 {
 //    SVDEBUG << "NoteLayer::editEnd(" << e->x() << "," << e->y() << ")" << endl;
     if (!m_model || !m_editing) return;
@@ -1065,7 +1065,7 @@
 }
 
 bool
-NoteLayer::editOpen(View *v, QMouseEvent *e)
+NoteLayer::editOpen(LayerGeometryProvider *v, QMouseEvent *e)
 {
     if (!m_model) return false;
 
@@ -1193,7 +1193,7 @@
 }    
 
 void
-NoteLayer::copy(View *v, Selection s, Clipboard &to)
+NoteLayer::copy(LayerGeometryProvider *v, Selection s, Clipboard &to)
 {
     if (!m_model) return;
 
@@ -1211,7 +1211,7 @@
 }
 
 bool
-NoteLayer::paste(View *v, const Clipboard &from, sv_frame_t /* frameOffset */, bool /* interactive */)
+NoteLayer::paste(LayerGeometryProvider *v, const Clipboard &from, sv_frame_t /* frameOffset */, bool /* interactive */)
 {
     if (!m_model) return false;
 
@@ -1222,7 +1222,7 @@
     if (clipboardHasDifferentAlignment(v, from)) {
 
         QMessageBox::StandardButton button =
-            QMessageBox::question(v, tr("Re-align pasted items?"),
+            QMessageBox::question(v->getView(), tr("Re-align pasted items?"),
                                   tr("The items you are pasting came from a layer with different source material from this one.  Do you want to re-align them in time, to match the source material for this layer?"),
                                   QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
                                   QMessageBox::Yes);
--- a/layer/NoteLayer.h	Mon Apr 13 13:52:05 2015 +0100
+++ b/layer/NoteLayer.h	Thu Aug 20 14:54:21 2015 +0100
@@ -35,37 +35,37 @@
 public:
     NoteLayer();
 
-    virtual void paint(View *v, QPainter &paint, QRect rect) const;
+    virtual void paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const;
 
-    virtual int getVerticalScaleWidth(View *v, bool, QPainter &) const;
-    virtual void paintVerticalScale(View *v, bool, QPainter &paint, QRect rect) const;
+    virtual int getVerticalScaleWidth(LayerGeometryProvider *v, bool, QPainter &) const;
+    virtual void paintVerticalScale(LayerGeometryProvider *v, bool, QPainter &paint, QRect rect) const;
 
-    virtual QString getFeatureDescription(View *v, QPoint &) const;
+    virtual QString getFeatureDescription(LayerGeometryProvider *v, QPoint &) const;
 
-    virtual bool snapToFeatureFrame(View *v, sv_frame_t &frame,
+    virtual bool snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame,
 				    int &resolution,
 				    SnapType snap) const;
 
-    virtual void drawStart(View *v, QMouseEvent *);
-    virtual void drawDrag(View *v, QMouseEvent *);
-    virtual void drawEnd(View *v, QMouseEvent *);
+    virtual void drawStart(LayerGeometryProvider *v, QMouseEvent *);
+    virtual void drawDrag(LayerGeometryProvider *v, QMouseEvent *);
+    virtual void drawEnd(LayerGeometryProvider *v, QMouseEvent *);
 
-    virtual void eraseStart(View *v, QMouseEvent *);
-    virtual void eraseDrag(View *v, QMouseEvent *);
-    virtual void eraseEnd(View *v, QMouseEvent *);
+    virtual void eraseStart(LayerGeometryProvider *v, QMouseEvent *);
+    virtual void eraseDrag(LayerGeometryProvider *v, QMouseEvent *);
+    virtual void eraseEnd(LayerGeometryProvider *v, QMouseEvent *);
 
-    virtual void editStart(View *v, QMouseEvent *);
-    virtual void editDrag(View *v, QMouseEvent *);
-    virtual void editEnd(View *v, QMouseEvent *);
+    virtual void editStart(LayerGeometryProvider *v, QMouseEvent *);
+    virtual void editDrag(LayerGeometryProvider *v, QMouseEvent *);
+    virtual void editEnd(LayerGeometryProvider *v, QMouseEvent *);
 
-    virtual bool editOpen(View *v, QMouseEvent *);
+    virtual bool editOpen(LayerGeometryProvider *v, QMouseEvent *);
 
     virtual void moveSelection(Selection s, sv_frame_t newStartFrame);
     virtual void resizeSelection(Selection s, Selection newSize);
     virtual void deleteSelection(Selection s);
 
-    virtual void copy(View *v, Selection s, Clipboard &to);
-    virtual bool paste(View *v, const Clipboard &from, sv_frame_t frameOffset,
+    virtual void copy(LayerGeometryProvider *v, Selection s, Clipboard &to);
+    virtual bool paste(LayerGeometryProvider *v, const Clipboard &from, sv_frame_t frameOffset,
                        bool interactive);
 
     virtual const Model *getModel() const { return m_model; }
@@ -91,11 +91,11 @@
     void setVerticalScale(VerticalScale scale);
     VerticalScale getVerticalScale() const { return m_verticalScale; }
 
-    virtual bool isLayerScrollable(const View *v) const;
+    virtual bool isLayerScrollable(const LayerGeometryProvider *v) const;
 
     virtual bool isLayerEditable() const { return true; }
 
-    virtual int getCompletion(View *) const { return m_model->getCompletion(); }
+    virtual int getCompletion(LayerGeometryProvider *) const { return m_model->getCompletion(); }
 
     virtual bool getValueExtents(double &min, double &max,
                                  bool &log, QString &unit) const;
@@ -132,19 +132,19 @@
     void setProperties(const QXmlAttributes &attributes);
 
     /// VerticalScaleLayer methods
-    virtual int getYForValue(View *v, double value) const;
-    virtual double getValueForY(View *v, int y) const;
+    virtual int getYForValue(LayerGeometryProvider *v, double value) const;
+    virtual double getValueForY(LayerGeometryProvider *v, int y) const;
     virtual QString getScaleUnits() const;
 
 protected:
-    void getScaleExtents(View *, double &min, double &max, bool &log) const;
+    void getScaleExtents(LayerGeometryProvider *, double &min, double &max, bool &log) const;
     bool shouldConvertMIDIToHz() const;
 
     virtual int getDefaultColourHint(bool dark, bool &impose);
 
-    NoteModel::PointList getLocalPoints(View *v, int) const;
+    NoteModel::PointList getLocalPoints(LayerGeometryProvider *v, int) const;
 
-    bool getPointToDrag(View *v, int x, int y, NoteModel::Point &) const;
+    bool getPointToDrag(LayerGeometryProvider *v, int x, int y, NoteModel::Point &) const;
 
     NoteModel *m_model;
     bool m_editing;
--- a/layer/PianoScale.cpp	Mon Apr 13 13:52:05 2015 +0100
+++ b/layer/PianoScale.cpp	Thu Aug 20 14:54:21 2015 +0100
@@ -21,10 +21,10 @@
 
 #include "base/Pitch.h"
 
-#include "view/View.h"
+#include "view/LayerGeometryProvider.h"
 
 void
-PianoScale::paintPianoVertical(View *v,
+PianoScale::paintPianoVertical(LayerGeometryProvider *v,
 			       QPainter &paint,
 			       QRect r,
 			       double minf,
--- a/layer/PianoScale.h	Mon Apr 13 13:52:05 2015 +0100
+++ b/layer/PianoScale.h	Thu Aug 20 14:54:21 2015 +0100
@@ -19,13 +19,13 @@
 #include <QRect>
 
 class QPainter;
-class View;
+class LayerGeometryProvider;
 
 class PianoScale
 {
 public:
     void paintPianoVertical
-    (View *v, QPainter &paint, QRect rect, double minf, double maxf);
+    (LayerGeometryProvider *v, QPainter &paint, QRect rect, double minf, double maxf);
 };
 
 #endif
--- a/layer/RegionLayer.cpp	Mon Apr 13 13:52:05 2015 +0100
+++ b/layer/RegionLayer.cpp	Thu Aug 20 14:54:21 2015 +0100
@@ -244,7 +244,7 @@
 }
 
 bool
-RegionLayer::isLayerScrollable(const View *v) const
+RegionLayer::isLayerScrollable(const LayerGeometryProvider *v) const
 {
     QPoint discard;
     return !v->shouldIlluminateLocalFeatures(this, discard);
@@ -302,11 +302,11 @@
 }
 
 RegionModel::PointList
-RegionLayer::getLocalPoints(View *v, int x) const
+RegionLayer::getLocalPoints(LayerGeometryProvider *v, int x) const
 {
     if (!m_model) return RegionModel::PointList();
 
-    long frame = v->getFrameForX(x);
+    sv_frame_t frame = v->getFrameForX(x);
 
     RegionModel::PointList onPoints =
 	m_model->getPoints(frame);
@@ -345,11 +345,11 @@
 }
 
 bool
-RegionLayer::getPointToDrag(View *v, int x, int y, RegionModel::Point &p) const
+RegionLayer::getPointToDrag(LayerGeometryProvider *v, int x, int y, RegionModel::Point &p) const
 {
     if (!m_model) return false;
 
-    long frame = v->getFrameForX(x);
+    sv_frame_t frame = v->getFrameForX(x);
 
     RegionModel::PointList onPoints = m_model->getPoints(frame);
     if (onPoints.empty()) return false;
@@ -383,7 +383,7 @@
 }
 
 QString
-RegionLayer::getFeatureDescription(View *v, QPoint &pos) const
+RegionLayer::getFeatureDescription(LayerGeometryProvider *v, QPoint &pos) const
 {
     int x = pos.x();
 
@@ -453,7 +453,7 @@
 }
 
 bool
-RegionLayer::snapToFeatureFrame(View *v, sv_frame_t &frame,
+RegionLayer::snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame,
                                 int &resolution,
                                 SnapType snap) const
 {
@@ -536,7 +536,7 @@
 }
 
 bool
-RegionLayer::snapToSimilarFeature(View *v, sv_frame_t &frame,
+RegionLayer::snapToSimilarFeature(LayerGeometryProvider *v, sv_frame_t &frame,
                                   int &resolution,
                                   SnapType snap) const
 {
@@ -624,7 +624,7 @@
 }
 
 void
-RegionLayer::getScaleExtents(View *v, double &min, double &max, bool &log) const
+RegionLayer::getScaleExtents(LayerGeometryProvider *v, double &min, double &max, bool &log) const
 {
     min = 0.0;
     max = 0.0;
@@ -676,9 +676,9 @@
 }
 
 int
-RegionLayer::spacingIndexToY(View *v, int i) const
+RegionLayer::spacingIndexToY(LayerGeometryProvider *v, int i) const
 {
-    int h = v->height();
+    int h = v->getPaintHeight();
     int n = int(m_spacingMap.size());
     // this maps from i (spacing of the value from the spacing
     // map) and n (number of region types) to y
@@ -687,10 +687,10 @@
 }
 
 double
-RegionLayer::yToSpacingIndex(View *v, int y) const
+RegionLayer::yToSpacingIndex(LayerGeometryProvider *v, int y) const
 {
     // we return an inexact result here (double rather than int)
-    int h = v->height();
+    int h = v->getPaintHeight();
     int n = int(m_spacingMap.size());
     // from y = h - ((h * i) / n) + (h / (2 * n)) as above (vh taking place of i)
     double vh = double(2*h*n - h - 2*n*y) / double(2*h);
@@ -698,11 +698,11 @@
 }
 
 int
-RegionLayer::getYForValue(View *v, double val) const
+RegionLayer::getYForValue(LayerGeometryProvider *v, double val) const
 {
     double min = 0.0, max = 0.0;
     bool logarithmic = false;
-    int h = v->height();
+    int h = v->getPaintHeight();
 
     if (m_verticalScale == EqualSpaced) {
 
@@ -733,17 +733,17 @@
 }
 
 double
-RegionLayer::getValueForY(View *v, int y) const
+RegionLayer::getValueForY(LayerGeometryProvider *v, int y) const
 {
     return getValueForY(v, y, -1);
 }
 
 double
-RegionLayer::getValueForY(View *v, int y, int avoid) const
+RegionLayer::getValueForY(LayerGeometryProvider *v, int y, int avoid) const
 {
     double min = 0.0, max = 0.0;
     bool logarithmic = false;
-    int h = v->height();
+    int h = v->getPaintHeight();
 
     if (m_verticalScale == EqualSpaced) {
 
@@ -836,7 +836,7 @@
 }
 
 QColor
-RegionLayer::getColourForValue(View *v, double val) const
+RegionLayer::getColourForValue(LayerGeometryProvider *v, double val) const
 {
     double min, max;
     bool log;
@@ -866,7 +866,7 @@
 }
 
 void
-RegionLayer::paint(View *v, QPainter &paint, QRect rect) const
+RegionLayer::paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const
 {
     if (!m_model || !m_model->isOK()) return;
 
@@ -942,7 +942,7 @@
 	if (w < 1) w = 1;
 
 	if (m_plotStyle == PlotSegmentation) {
-            paint.setPen(getForegroundQColor(v));
+            paint.setPen(getForegroundQColor(v->getView()));
             paint.setBrush(getColourForValue(v, p.value));
         } else {
             paint.setPen(getBaseQColor());
@@ -958,15 +958,15 @@
                 RegionModel::Point::Comparator()(illuminatePoint, p) ||
                 RegionModel::Point::Comparator()(p, illuminatePoint)) {
 
-                paint.setPen(QPen(getForegroundQColor(v), 1));
-                paint.drawLine(x, 0, x, v->height());
+                paint.setPen(QPen(getForegroundQColor(v->getView()), 1));
+                paint.drawLine(x, 0, x, v->getPaintHeight());
                 paint.setPen(Qt::NoPen);
 
             } else {
-                paint.setPen(QPen(getForegroundQColor(v), 2));
+                paint.setPen(QPen(getForegroundQColor(v->getView()), 2));
             }
 
-	    paint.drawRect(x, -1, ex - x, v->height() + 2);
+	    paint.drawRect(x, -1, ex - x, v->getPaintHeight() + 2);
 
 	} else {
 
@@ -1040,7 +1040,7 @@
                 labelX = x + 5;
                 labelY = v->getTextLabelHeight(this, paint);
                 if (labelX < nextLabelMinX) {
-                    if (lastLabelY < v->height()/2) {
+                    if (lastLabelY < v->getPaintHeight()/2) {
                         labelY = lastLabelY + fontHeight;
                     }
                 }
@@ -1056,7 +1056,7 @@
 }
 
 int
-RegionLayer::getVerticalScaleWidth(View *v, bool, QPainter &paint) const
+RegionLayer::getVerticalScaleWidth(LayerGeometryProvider *v, bool, QPainter &paint) const
 {
     if (!m_model || 
         m_verticalScale == AutoAlignScale || 
@@ -1078,7 +1078,7 @@
 }
 
 void
-RegionLayer::paintVerticalScale(View *v, bool, QPainter &paint, QRect) const
+RegionLayer::paintVerticalScale(LayerGeometryProvider *v, bool, QPainter &paint, QRect) const
 {
     if (!m_model || m_model->getPoints().empty()) return;
 
@@ -1121,11 +1121,11 @@
 }
 
 void
-RegionLayer::drawStart(View *v, QMouseEvent *e)
+RegionLayer::drawStart(LayerGeometryProvider *v, QMouseEvent *e)
 {
     if (!m_model) return;
 
-    long frame = v->getFrameForX(e->x());
+    sv_frame_t frame = v->getFrameForX(e->x());
     if (frame < 0) frame = 0;
     frame = frame / m_model->getResolution() * m_model->getResolution();
 
@@ -1145,7 +1145,7 @@
 }
 
 void
-RegionLayer::drawDrag(View *v, QMouseEvent *e)
+RegionLayer::drawDrag(LayerGeometryProvider *v, QMouseEvent *e)
 {
     if (!m_model || !m_editing) return;
 
@@ -1175,7 +1175,7 @@
 }
 
 void
-RegionLayer::drawEnd(View *, QMouseEvent *)
+RegionLayer::drawEnd(LayerGeometryProvider *, QMouseEvent *)
 {
     if (!m_model || !m_editing) return;
     finish(m_editingCommand);
@@ -1186,7 +1186,7 @@
 }
 
 void
-RegionLayer::eraseStart(View *v, QMouseEvent *e)
+RegionLayer::eraseStart(LayerGeometryProvider *v, QMouseEvent *e)
 {
     if (!m_model) return;
 
@@ -1202,12 +1202,12 @@
 }
 
 void
-RegionLayer::eraseDrag(View *, QMouseEvent *)
+RegionLayer::eraseDrag(LayerGeometryProvider *, QMouseEvent *)
 {
 }
 
 void
-RegionLayer::eraseEnd(View *v, QMouseEvent *e)
+RegionLayer::eraseEnd(LayerGeometryProvider *v, QMouseEvent *e)
 {
     if (!m_model || !m_editing) return;
 
@@ -1229,7 +1229,7 @@
 }
 
 void
-RegionLayer::editStart(View *v, QMouseEvent *e)
+RegionLayer::editStart(LayerGeometryProvider *v, QMouseEvent *e)
 {
     if (!m_model) return;
 
@@ -1254,7 +1254,7 @@
 }
 
 void
-RegionLayer::editDrag(View *v, QMouseEvent *e)
+RegionLayer::editDrag(LayerGeometryProvider *v, QMouseEvent *e)
 {
     if (!m_model || !m_editing) return;
 
@@ -1263,7 +1263,7 @@
     int newx = m_dragPointX + xdist;
     int newy = m_dragPointY + ydist;
 
-    long frame = v->getFrameForX(newx);
+    sv_frame_t frame = v->getFrameForX(newx);
     if (frame < 0) frame = 0;
     frame = frame / m_model->getResolution() * m_model->getResolution();
 
@@ -1289,7 +1289,7 @@
 }
 
 void
-RegionLayer::editEnd(View *, QMouseEvent *)
+RegionLayer::editEnd(LayerGeometryProvider *, QMouseEvent *)
 {
     if (!m_model || !m_editing) return;
 
@@ -1317,7 +1317,7 @@
 }
 
 bool
-RegionLayer::editOpen(View *v, QMouseEvent *e)
+RegionLayer::editOpen(LayerGeometryProvider *v, QMouseEvent *e)
 {
     if (!m_model) return false;
 
@@ -1447,7 +1447,7 @@
 }    
 
 void
-RegionLayer::copy(View *v, Selection s, Clipboard &to)
+RegionLayer::copy(LayerGeometryProvider *v, Selection s, Clipboard &to)
 {
     if (!m_model) return;
 
@@ -1465,7 +1465,7 @@
 }
 
 bool
-RegionLayer::paste(View *v, const Clipboard &from, sv_frame_t /* frameOffset */, bool /* interactive */)
+RegionLayer::paste(LayerGeometryProvider *v, const Clipboard &from, sv_frame_t /* frameOffset */, bool /* interactive */)
 {
     if (!m_model) return false;
 
@@ -1476,7 +1476,7 @@
     if (clipboardHasDifferentAlignment(v, from)) {
 
         QMessageBox::StandardButton button =
-            QMessageBox::question(v, tr("Re-align pasted items?"),
+            QMessageBox::question(v->getView(), tr("Re-align pasted items?"),
                                   tr("The items you are pasting came from a layer with different source material from this one.  Do you want to re-align them in time, to match the source material for this layer?"),
                                   QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
                                   QMessageBox::Yes);
--- a/layer/RegionLayer.h	Mon Apr 13 13:52:05 2015 +0100
+++ b/layer/RegionLayer.h	Thu Aug 20 14:54:21 2015 +0100
@@ -39,41 +39,41 @@
 public:
     RegionLayer();
 
-    virtual void paint(View *v, QPainter &paint, QRect rect) const;
+    virtual void paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const;
 
-    virtual int getVerticalScaleWidth(View *v, bool, QPainter &) const;
-    virtual void paintVerticalScale(View *v, bool, QPainter &paint, QRect rect) const;
+    virtual int getVerticalScaleWidth(LayerGeometryProvider *v, bool, QPainter &) const;
+    virtual void paintVerticalScale(LayerGeometryProvider *v, bool, QPainter &paint, QRect rect) const;
 
-    virtual QString getFeatureDescription(View *v, QPoint &) const;
+    virtual QString getFeatureDescription(LayerGeometryProvider *v, QPoint &) const;
     virtual QString getLabelPreceding(sv_frame_t) const;
 
-    virtual bool snapToFeatureFrame(View *v, sv_frame_t &frame,
+    virtual bool snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame,
 				    int &resolution,
 				    SnapType snap) const;
-    virtual bool snapToSimilarFeature(View *v, sv_frame_t &frame,
+    virtual bool snapToSimilarFeature(LayerGeometryProvider *v, sv_frame_t &frame,
                                       int &resolution,
                                       SnapType snap) const;
 
-    virtual void drawStart(View *v, QMouseEvent *);
-    virtual void drawDrag(View *v, QMouseEvent *);
-    virtual void drawEnd(View *v, QMouseEvent *);
+    virtual void drawStart(LayerGeometryProvider *v, QMouseEvent *);
+    virtual void drawDrag(LayerGeometryProvider *v, QMouseEvent *);
+    virtual void drawEnd(LayerGeometryProvider *v, QMouseEvent *);
 
-    virtual void eraseStart(View *v, QMouseEvent *);
-    virtual void eraseDrag(View *v, QMouseEvent *);
-    virtual void eraseEnd(View *v, QMouseEvent *);
+    virtual void eraseStart(LayerGeometryProvider *v, QMouseEvent *);
+    virtual void eraseDrag(LayerGeometryProvider *v, QMouseEvent *);
+    virtual void eraseEnd(LayerGeometryProvider *v, QMouseEvent *);
 
-    virtual void editStart(View *v, QMouseEvent *);
-    virtual void editDrag(View *v, QMouseEvent *);
-    virtual void editEnd(View *v, QMouseEvent *);
+    virtual void editStart(LayerGeometryProvider *v, QMouseEvent *);
+    virtual void editDrag(LayerGeometryProvider *v, QMouseEvent *);
+    virtual void editEnd(LayerGeometryProvider *v, QMouseEvent *);
 
-    virtual bool editOpen(View *v, QMouseEvent *);
+    virtual bool editOpen(LayerGeometryProvider *v, QMouseEvent *);
 
     virtual void moveSelection(Selection s, sv_frame_t newStartFrame);
     virtual void resizeSelection(Selection s, Selection newSize);
     virtual void deleteSelection(Selection s);
 
-    virtual void copy(View *v, Selection s, Clipboard &to);
-    virtual bool paste(View *v, const Clipboard &from, sv_frame_t frameOffset,
+    virtual void copy(LayerGeometryProvider *v, Selection s, Clipboard &to);
+    virtual bool paste(LayerGeometryProvider *v, const Clipboard &from, sv_frame_t frameOffset,
                        bool interactive);
 
     virtual const Model *getModel() const { return m_model; }
@@ -110,11 +110,11 @@
     void setPlotStyle(PlotStyle style);
     PlotStyle getPlotStyle() const { return m_plotStyle; }
 
-    virtual bool isLayerScrollable(const View *v) const;
+    virtual bool isLayerScrollable(const LayerGeometryProvider *v) const;
 
     virtual bool isLayerEditable() const { return true; }
 
-    virtual int getCompletion(View *) const { return m_model->getCompletion(); }
+    virtual int getCompletion(LayerGeometryProvider *) const { return m_model->getCompletion(); }
 
     virtual bool getValueExtents(double &min, double &max,
                                  bool &log, QString &unit) const;
@@ -127,23 +127,23 @@
     void setProperties(const QXmlAttributes &attributes);
 
     /// VerticalScaleLayer and ColourScaleLayer methods
-    int getYForValue(View *v, double value) const;
-    double getValueForY(View *v, int y) const;
+    int getYForValue(LayerGeometryProvider *v, double value) const;
+    double getValueForY(LayerGeometryProvider *v, int y) const;
     virtual QString getScaleUnits() const;
-    QColor getColourForValue(View *v, double value) const;
+    QColor getColourForValue(LayerGeometryProvider *v, double value) const;
 
 protected slots:
     void recalcSpacing();
 
 protected:
-    double getValueForY(View *v, int y, int avoid) const;
-    void getScaleExtents(View *, double &min, double &max, bool &log) const;
+    double getValueForY(LayerGeometryProvider *v, int y, int avoid) const;
+    void getScaleExtents(LayerGeometryProvider *, double &min, double &max, bool &log) const;
 
     virtual int getDefaultColourHint(bool dark, bool &impose);
 
-    RegionModel::PointList getLocalPoints(View *v, int x) const;
+    RegionModel::PointList getLocalPoints(LayerGeometryProvider *v, int x) const;
 
-    bool getPointToDrag(View *v, int x, int y, RegionModel::Point &) const;
+    bool getPointToDrag(LayerGeometryProvider *v, int x, int y, RegionModel::Point &) const;
 
     RegionModel *m_model;
     bool m_editing;
@@ -166,8 +166,8 @@
     // region value -> number of regions with this value
     SpacingMap m_distributionMap;
 
-    int spacingIndexToY(View *v, int i) const;
-    double yToSpacingIndex(View *v, int y) const;
+    int spacingIndexToY(LayerGeometryProvider *v, int i) const;
+    double yToSpacingIndex(LayerGeometryProvider *v, int y) const;
 
     void finish(RegionModel::EditCommand *command) {
         Command *c = command->finish();
--- a/layer/SingleColourLayer.cpp	Mon Apr 13 13:52:05 2015 +0100
+++ b/layer/SingleColourLayer.cpp	Thu Aug 20 14:54:21 2015 +0100
@@ -137,7 +137,7 @@
 }
 
 void
-SingleColourLayer::setDefaultColourFor(View *v)
+SingleColourLayer::setDefaultColourFor(LayerGeometryProvider *v)
 {
 #ifdef DEBUG_COLOUR_SELECTION
     SVDEBUG << "SingleColourLayer::setDefaultColourFor: m_colourExplicitlySet = " << m_colourExplicitlySet << ", m_defaultColourSet " << m_defaultColourSet << endl;
@@ -244,19 +244,19 @@
 }
 
 QColor
-SingleColourLayer::getBackgroundQColor(View *v) const
+SingleColourLayer::getBackgroundQColor(LayerGeometryProvider *v) const
 {
     return v->getBackground();
 }
 
 QColor
-SingleColourLayer::getForegroundQColor(View *v) const
+SingleColourLayer::getForegroundQColor(LayerGeometryProvider *v) const
 {
     return v->getForeground();
 }
 
 std::vector<QColor>
-SingleColourLayer::getPartialShades(View *v) const
+SingleColourLayer::getPartialShades(LayerGeometryProvider *v) const
 {
     std::vector<QColor> s;
     QColor base = getBaseQColor();
--- a/layer/SingleColourLayer.h	Mon Apr 13 13:52:05 2015 +0100
+++ b/layer/SingleColourLayer.h	Thu Aug 20 14:54:21 2015 +0100
@@ -71,16 +71,16 @@
 
     virtual void setProperties(const QXmlAttributes &attributes);
 
-    virtual void setDefaultColourFor(View *v);
+    virtual void setDefaultColourFor(LayerGeometryProvider *v);
 
 protected:
     SingleColourLayer();
     virtual ~SingleColourLayer();
 
     virtual QColor getBaseQColor() const;
-    virtual QColor getBackgroundQColor(View *v) const;
-    virtual QColor getForegroundQColor(View *v) const;
-    std::vector<QColor> getPartialShades(View *v) const;
+    virtual QColor getBackgroundQColor(LayerGeometryProvider *v) const;
+    virtual QColor getForegroundQColor(LayerGeometryProvider *v) const;
+    std::vector<QColor> getPartialShades(LayerGeometryProvider *v) const;
 
     virtual void flagBaseColourChanged() { }
     virtual int getDefaultColourHint(bool /* darkBackground */,
--- a/layer/SliceLayer.cpp	Mon Apr 13 13:52:05 2015 +0100
+++ b/layer/SliceLayer.cpp	Thu Aug 20 14:54:21 2015 +0100
@@ -92,14 +92,14 @@
 }
 
 QString
-SliceLayer::getFeatureDescription(View *v, QPoint &p) const
+SliceLayer::getFeatureDescription(LayerGeometryProvider *v, QPoint &p) const
 {
     int minbin, maxbin, range;
     return getFeatureDescriptionAux(v, p, true, minbin, maxbin, range);
 }
 
 QString
-SliceLayer::getFeatureDescriptionAux(View *v, QPoint &p,
+SliceLayer::getFeatureDescriptionAux(LayerGeometryProvider *v, QPoint &p,
                                      bool includeBinDescription,
                                      int &minbin, int &maxbin, int &range) const
 {
@@ -108,7 +108,7 @@
     if (!m_sliceableModel) return "";
 
     int xorigin = m_xorigins[v];
-    int w = v->width() - xorigin - 1;
+    int w = v->getPaintWidth() - xorigin - 1;
     
     int mh = m_sliceableModel->getHeight();
     minbin = getBinForX(p.x() - xorigin, mh, w);
@@ -226,7 +226,7 @@
 }
 
 double
-SliceLayer::getYForValue(double value, const View *v, double &norm) const
+SliceLayer::getYForValue(double value, const LayerGeometryProvider *v, double &norm) const
 {
     norm = 0.0;
 
@@ -276,7 +276,7 @@
 }
 
 double
-SliceLayer::getValueForY(double y, const View *v) const
+SliceLayer::getValueForY(double y, const LayerGeometryProvider *v) const
 {
     double value = 0.0;
 
@@ -313,7 +313,7 @@
 }
 
 void
-SliceLayer::paint(View *v, QPainter &paint, QRect rect) const
+SliceLayer::paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const
 {
     if (!m_sliceableModel || !m_sliceableModel->isOK() ||
         !m_sliceableModel->isReady()) return;
@@ -334,11 +334,11 @@
     paint.setPen(getBaseQColor());
 
     int xorigin = getVerticalScaleWidth(v, true, paint) + 1;
-    int w = v->width() - xorigin - 1;
+    int w = v->getPaintWidth() - xorigin - 1;
 
     m_xorigins[v] = xorigin; // for use in getFeatureDescription
     
-    int yorigin = v->height() - 20 - paint.fontMetrics().height() - 7;
+    int yorigin = v->getPaintHeight() - 20 - paint.fontMetrics().height() - 7;
     int h = yorigin - paint.fontMetrics().height() - 8;
 
     m_yorigins[v] = yorigin; // for getYForValue etc
@@ -501,7 +501,7 @@
 }
 
 int
-SliceLayer::getVerticalScaleWidth(View *, bool, QPainter &paint) const
+SliceLayer::getVerticalScaleWidth(LayerGeometryProvider *, bool, QPainter &paint) const
 {
     if (m_energyScale == LinearScale || m_energyScale == AbsoluteScale) {
 	return std::max(paint.fontMetrics().width("0.0") + 13,
@@ -513,7 +513,7 @@
 }
 
 void
-SliceLayer::paintVerticalScale(View *v, bool, QPainter &paint, QRect rect) const
+SliceLayer::paintVerticalScale(LayerGeometryProvider *v, bool, QPainter &paint, QRect rect) const
 {
     double thresh = m_threshold;
     if (m_energyScale != LinearScale && m_energyScale != AbsoluteScale) {
@@ -523,7 +523,7 @@
 //    int h = (rect.height() * 3) / 4;
 //    int y = (rect.height() / 2) - (h / 2);
     
-    int yorigin = v->height() - 20 - paint.fontMetrics().height() - 6;
+    int yorigin = v->getPaintHeight() - 20 - paint.fontMetrics().height() - 6;
     int h = yorigin - paint.fontMetrics().height() - 8;
     if (h < 0) return;
 
--- a/layer/SliceLayer.h	Mon Apr 13 13:52:05 2015 +0100
+++ b/layer/SliceLayer.h	Thu Aug 20 14:54:21 2015 +0100
@@ -38,12 +38,12 @@
 
     void setSliceableModel(const Model *model);    
 
-    virtual void paint(View *v, QPainter &paint, QRect rect) const;
+    virtual void paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const;
 
-    virtual QString getFeatureDescription(View *v, QPoint &) const;
+    virtual QString getFeatureDescription(LayerGeometryProvider *v, QPoint &) const;
 
-    virtual int getVerticalScaleWidth(View *v, bool, QPainter &) const;
-    virtual void paintVerticalScale(View *v, bool, QPainter &paint, QRect rect) const;
+    virtual int getVerticalScaleWidth(LayerGeometryProvider *v, bool, QPainter &) const;
+    virtual void paintVerticalScale(LayerGeometryProvider *v, bool, QPainter &paint, QRect rect) const;
 
     virtual ColourSignificance getLayerColourSignificance() const {
         return ColourAndBackgroundSignificant;
@@ -67,7 +67,7 @@
 
     virtual bool hasTimeXAxis() const { return false; }
 
-    virtual bool isLayerScrollable(const View *) const { return false; }
+    virtual bool isLayerScrollable(const LayerGeometryProvider *) const { return false; }
 
     enum EnergyScale { LinearScale, MeterScale, dBScale, AbsoluteScale };
 
@@ -112,10 +112,10 @@
     virtual double getXForBin(int bin, int totalBins, double w) const;
     virtual int getBinForX(double x, int totalBins, double w) const;
 
-    virtual double getYForValue(double value, const View *v, double &norm) const;
-    virtual double getValueForY(double y, const View *v) const;
+    virtual double getYForValue(double value, const LayerGeometryProvider *v, double &norm) const;
+    virtual double getValueForY(double y, const LayerGeometryProvider *v) const;
     
-    virtual QString getFeatureDescriptionAux(View *v, QPoint &,
+    virtual QString getFeatureDescriptionAux(LayerGeometryProvider *v, QPoint &,
                                              bool includeBinDescription,
                                              int &minbin, int &maxbin,
                                              int &range) const;
@@ -141,9 +141,9 @@
     float                             m_initialThreshold;
     float                             m_gain;
     mutable std::vector<int>          m_scalePoints;
-    mutable std::map<const View *, int> m_xorigins;
-    mutable std::map<const View *, int> m_yorigins;
-    mutable std::map<const View *, int> m_heights;
+    mutable std::map<const LayerGeometryProvider *, int> m_xorigins;
+    mutable std::map<const LayerGeometryProvider *, int> m_yorigins;
+    mutable std::map<const LayerGeometryProvider *, int> m_heights;
     mutable sv_frame_t                m_currentf0;
     mutable sv_frame_t                m_currentf1;
     mutable std::vector<float>        m_values;
--- a/layer/SpectrogramLayer.cpp	Mon Apr 13 13:52:05 2015 +0100
+++ b/layer/SpectrogramLayer.cpp	Thu Aug 20 14:54:21 2015 +0100
@@ -73,15 +73,11 @@
     m_colourMap(0),
     m_frequencyScale(LinearFrequencyScale),
     m_binDisplay(AllBins),
-    m_normalizeColumns(false),
-    m_normalizeVisibleArea(false),
-    m_normalizeHybrid(false),
+    m_normalization(NoNormalization),
     m_lastEmittedZoomStep(-1),
     m_synchronous(false),
     m_haveDetailedScale(false),
     m_lastPaintBlockWidth(0),
-    m_updateTimer(0),
-    m_candidateFillStartFrame(0),
     m_exiting(false),
     m_sliceableModel(0)
 {
@@ -107,7 +103,7 @@
 	setFrequencyScale(LogFrequencyScale);
 	setColourScale(LinearColourScale);
 	setBinDisplay(PeakFrequencies);
-	setNormalizeColumns(true);
+        setNormalization(NormalizeColumns);
     }
 
     Preferences *prefs = Preferences::getInstance();
@@ -120,9 +116,6 @@
 
 SpectrogramLayer::~SpectrogramLayer()
 {
-    delete m_updateTimer;
-    m_updateTimer = 0;
-    
     invalidateFFTModels();
 }
 
@@ -155,8 +148,7 @@
     list.push_back("Colour Scale");
     list.push_back("Window Size");
     list.push_back("Window Increment");
-    list.push_back("Normalize Columns");
-    list.push_back("Normalize Visible Area");
+    list.push_back("Normalization");
     list.push_back("Bin Display");
     list.push_back("Threshold");
     list.push_back("Gain");
@@ -175,8 +167,7 @@
     if (name == "Colour Scale") return tr("Colour Scale");
     if (name == "Window Size") return tr("Window Size");
     if (name == "Window Increment") return tr("Window Overlap");
-    if (name == "Normalize Columns") return tr("Normalize Columns");
-    if (name == "Normalize Visible Area") return tr("Normalize Visible Area");
+    if (name == "Normalization") return tr("Normalization");
     if (name == "Bin Display") return tr("Bin Display");
     if (name == "Threshold") return tr("Threshold");
     if (name == "Gain") return tr("Gain");
@@ -189,10 +180,8 @@
 }
 
 QString
-SpectrogramLayer::getPropertyIconName(const PropertyName &name) const
+SpectrogramLayer::getPropertyIconName(const PropertyName &) const
 {
-    if (name == "Normalize Columns") return "normalise-columns";
-    if (name == "Normalize Visible Area") return "normalise";
     return "";
 }
 
@@ -201,8 +190,6 @@
 {
     if (name == "Gain") return RangeProperty;
     if (name == "Colour Rotation") return RangeProperty;
-    if (name == "Normalize Columns") return ToggleProperty;
-    if (name == "Normalize Visible Area") return ToggleProperty;
     if (name == "Threshold") return RangeProperty;
     if (name == "Zero Padding") return ToggleProperty;
     return ValueProperty;
@@ -219,8 +206,7 @@
     if (name == "Colour" ||
 	name == "Threshold" ||
 	name == "Colour Rotation") return tr("Colour");
-    if (name == "Normalize Columns" ||
-        name == "Normalize Visible Area" ||
+    if (name == "Normalization" ||
         name == "Gain" ||
 	name == "Colour Scale") return tr("Scale");
     return QString();
@@ -365,15 +351,12 @@
         *deflt = int(AllBins);
 	val = (int)m_binDisplay;
 
-    } else if (name == "Normalize Columns") {
+    } else if (name == "Normalization") {
 	
-        *deflt = 0;
-	val = (m_normalizeColumns ? 1 : 0);
-
-    } else if (name == "Normalize Visible Area") {
-	
-        *deflt = 0;
-	val = (m_normalizeVisibleArea ? 1 : 0);
+        *min = 0;
+        *max = 3;
+        *deflt = int(NoNormalization);
+        val = (int)m_normalization;
 
     } else {
 	val = Layer::getPropertyRangeAndValue(name, min, max, deflt);
@@ -399,6 +382,9 @@
 	case 4: return tr("Phase");
 	}
     }
+    if (name == "Normalization") {
+        return ""; // icon only
+    }
     if (name == "Window Size") {
 	return QString("%1").arg(32 << value);
     }
@@ -465,6 +451,22 @@
     return tr("<unknown>");
 }
 
+QString
+SpectrogramLayer::getPropertyValueIconName(const PropertyName &name,
+                                           int value) const
+{
+    if (name == "Normalization") {
+        switch(value) {
+        default:
+        case 0: return "normalise-none";
+        case 1: return "normalise-columns";
+        case 2: return "normalise";
+        case 3: return "normalise-hybrid";
+        }
+    }
+    return "";
+}
+
 RangeMapper *
 SpectrogramLayer::getNewPropertyRangeMapper(const PropertyName &name) const
 {
@@ -555,10 +557,14 @@
 	case 1: setBinDisplay(PeakBins); break;
 	case 2: setBinDisplay(PeakFrequencies); break;
 	}
-    } else if (name == "Normalize Columns") {
-	setNormalizeColumns(value ? true : false);
-    } else if (name == "Normalize Visible Area") {
-	setNormalizeVisibleArea(value ? true : false);
+    } else if (name == "Normalization") {
+        switch (value) {
+        default:
+        case 0: setNormalization(NoNormalization); break;
+        case 1: setNormalization(NormalizeColumns); break;
+        case 2: setNormalization(NormalizeVisibleArea); break;
+        case 3: setNormalization(NormalizeHybrid); break;
+        }
     }
 }
 
@@ -578,10 +584,10 @@
          i != m_imageCaches.end(); ++i) {
 
         //!!! when are views removed from the map? on setLayerDormant?
-        const View *v = i->first;
+        const LayerGeometryProvider *v = i->first;
 
 #ifdef DEBUG_SPECTROGRAM_REPAINT
-        SVDEBUG << "SpectrogramLayer::invalidateImageCaches(" 
+        cerr << "SpectrogramLayer::invalidateImageCaches(" 
                   << startFrame << ", " << endFrame << "): view range is "
                   << v->getStartFrame() << ", " << v->getEndFrame()
                   << endl;
@@ -601,11 +607,11 @@
             }
             int x = v->getXForFrame(startFrame);
 #ifdef DEBUG_SPECTROGRAM_REPAINT
-            SVDEBUG << "clipping from 0 to " << x-1 << endl;
+            cerr << "clipping from 0 to " << x-1 << endl;
 #endif
             if (x > 1) {
                 i->second.validArea &=
-                    QRect(0, 0, x-1, v->height());
+                    QRect(0, 0, x-1, v->getPaintHeight());
             } else {
                 i->second.validArea = QRect();
             }
@@ -618,12 +624,12 @@
             }
             int x = v->getXForFrame(endFrame);
 #ifdef DEBUG_SPECTROGRAM_REPAINT
-            SVDEBUG << "clipping from " << x+1 << " to " << v->width()
+            cerr << "clipping from " << x+1 << " to " << v->getPaintWidth()
                       << endl;
 #endif
-            if (x < v->width()) {
+            if (x < v->getPaintWidth()) {
                 i->second.validArea &=
-                    QRect(x+1, 0, v->width()-(x+1), v->height());
+                    QRect(x+1, 0, v->getPaintWidth()-(x+1), v->getPaintHeight());
             } else {
                 i->second.validArea = QRect();
             }
@@ -934,69 +940,30 @@
 }
 
 void
-SpectrogramLayer::setNormalizeColumns(bool n)
+SpectrogramLayer::setNormalization(Normalization n)
 {
-    if (m_normalizeColumns == n) return;
+    if (m_normalization == n) return;
 
     invalidateImageCaches();
     invalidateMagnitudes();
-    m_normalizeColumns = n;
+    m_normalization = n;
 
     emit layerParametersChanged();
 }
 
-bool
-SpectrogramLayer::getNormalizeColumns() const
+SpectrogramLayer::Normalization
+SpectrogramLayer::getNormalization() const
 {
-    return m_normalizeColumns;
+    return m_normalization;
 }
 
 void
-SpectrogramLayer::setNormalizeHybrid(bool n)
-{
-    if (m_normalizeHybrid == n) return;
-
-    invalidateImageCaches();
-    invalidateMagnitudes();
-    m_normalizeHybrid = n;
-
-    emit layerParametersChanged();
-}
-
-bool
-SpectrogramLayer::getNormalizeHybrid() const
-{
-    return m_normalizeHybrid;
-}
-
-void
-SpectrogramLayer::setNormalizeVisibleArea(bool n)
-{
-    SVDEBUG << "SpectrogramLayer::setNormalizeVisibleArea(" << n
-              << ") (from " << m_normalizeVisibleArea << ")" << endl;
-
-    if (m_normalizeVisibleArea == n) return;
-
-    invalidateImageCaches();
-    invalidateMagnitudes();
-    m_normalizeVisibleArea = n;
-
-    emit layerParametersChanged();
-}
-
-bool
-SpectrogramLayer::getNormalizeVisibleArea() const
-{
-    return m_normalizeVisibleArea;
-}
-
-void
-SpectrogramLayer::setLayerDormant(const View *v, bool dormant)
+SpectrogramLayer::setLayerDormant(const LayerGeometryProvider *v, bool dormant)
 {
     if (dormant) {
 
 #ifdef DEBUG_SPECTROGRAM_REPAINT
-        SVDEBUG << "SpectrogramLayer::setLayerDormant(" << dormant << ")"
+        cerr << "SpectrogramLayer::setLayerDormant(" << dormant << ")"
                   << endl;
 #endif
 
@@ -1006,17 +973,20 @@
 
         Layer::setLayerDormant(v, true);
 
+        const View *view = v->getView();
+        
 	invalidateImageCaches();
-        m_imageCaches.erase(v);
-
-        if (m_fftModels.find(v) != m_fftModels.end()) {
-
-            if (m_sliceableModel == m_fftModels[v].first) {
+
+        m_imageCaches.erase(view);
+
+        if (m_fftModels.find(view) != m_fftModels.end()) {
+
+            if (m_sliceableModel == m_fftModels[view]) {
                 bool replaced = false;
                 for (ViewFFTMap::iterator i = m_fftModels.begin();
                      i != m_fftModels.end(); ++i) {
-                    if (i->second.first != m_sliceableModel) {
-                        emit sliceableModelReplaced(m_sliceableModel, i->second.first);
+                    if (i->second != m_sliceableModel) {
+                        emit sliceableModelReplaced(m_sliceableModel, i->second);
                         replaced = true;
                         break;
                     }
@@ -1024,11 +994,11 @@
                 if (!replaced) emit sliceableModelReplaced(m_sliceableModel, 0);
             }
 
-            delete m_fftModels[v].first;
-            m_fftModels.erase(v);
-
-            delete m_peakCaches[v];
-            m_peakCaches.erase(v);
+            delete m_fftModels[view];
+            m_fftModels.erase(view);
+
+            delete m_peakCaches[view];
+            m_peakCaches.erase(view);
         }
 	
     } else {
@@ -1041,7 +1011,7 @@
 SpectrogramLayer::cacheInvalid()
 {
 #ifdef DEBUG_SPECTROGRAM_REPAINT
-    SVDEBUG << "SpectrogramLayer::cacheInvalid()" << endl;
+    cerr << "SpectrogramLayer::cacheInvalid()" << endl;
 #endif
 
     invalidateImageCaches();
@@ -1052,81 +1022,13 @@
 SpectrogramLayer::cacheInvalid(sv_frame_t from, sv_frame_t to)
 {
 #ifdef DEBUG_SPECTROGRAM_REPAINT
-    SVDEBUG << "SpectrogramLayer::cacheInvalid(" << from << ", " << to << ")" << endl;
+    cerr << "SpectrogramLayer::cacheInvalid(" << from << ", " << to << ")" << endl;
 #endif
 
     invalidateImageCaches(from, to);
     invalidateMagnitudes();
 }
 
-void
-SpectrogramLayer::fillTimerTimedOut()
-{
-    if (!m_model) return;
-
-    bool allDone = true;
-
-#ifdef DEBUG_SPECTROGRAM_REPAINT
-    SVDEBUG << "SpectrogramLayer::fillTimerTimedOut: have " << m_fftModels.size() << " FFT models associated with views" << endl;
-#endif
-
-    for (ViewFFTMap::iterator i = m_fftModels.begin();
-         i != m_fftModels.end(); ++i) {
-
-        const FFTModel *model = i->second.first;
-        sv_frame_t lastFill = i->second.second;
-
-        if (model) {
-
-            sv_frame_t fill = model->getFillExtent();
-
-#ifdef DEBUG_SPECTROGRAM_REPAINT
-            SVDEBUG << "SpectrogramLayer::fillTimerTimedOut: extent for " << model << ": " << fill << ", last " << lastFill << ", total " << m_model->getEndFrame() << endl;
-#endif
-
-            if (fill >= lastFill) {
-                if (fill >= m_model->getEndFrame() && lastFill > 0) {
-#ifdef DEBUG_SPECTROGRAM_REPAINT
-                    cerr << "complete!" << endl;
-#endif
-                    invalidateImageCaches();
-                    i->second.second = -1;
-                    emit modelChanged();
-
-                } else if (fill > lastFill) {
-#ifdef DEBUG_SPECTROGRAM_REPAINT
-                    cerr << "SpectrogramLayer: emitting modelChanged("
-                              << lastFill << "," << fill << ")" << endl;
-#endif
-                    invalidateImageCaches(lastFill, fill);
-                    i->second.second = fill;
-                    emit modelChangedWithin(lastFill, fill);
-                }
-            } else {
-#ifdef DEBUG_SPECTROGRAM_REPAINT
-                cerr << "SpectrogramLayer: going backwards, emitting modelChanged("
-                          << m_model->getStartFrame() << "," << m_model->getEndFrame() << ")" << endl;
-#endif
-                invalidateImageCaches();
-                i->second.second = fill;
-                emit modelChangedWithin(m_model->getStartFrame(), m_model->getEndFrame());
-            }
-
-            if (i->second.second >= 0) {
-                allDone = false;
-            }
-        }
-    }
-
-    if (allDone) {
-#ifdef DEBUG_SPECTROGRAM_REPAINT
-        cerr << "SpectrogramLayer: all complete!" << endl;
-#endif
-        delete m_updateTimer;
-        m_updateTimer = 0;
-    }
-}
-
 bool
 SpectrogramLayer::hasLightBackground() const 
 {
@@ -1181,17 +1083,17 @@
 }
 
 unsigned char
-SpectrogramLayer::getDisplayValue(View *v, double input) const
+SpectrogramLayer::getDisplayValue(LayerGeometryProvider *v, double input) const
 {
     int value;
 
     double min = 0.0;
     double max = 1.0;
 
-    if (m_normalizeVisibleArea) {
+    if (m_normalization == NormalizeVisibleArea) {
         min = m_viewMags[v].getMin();
         max = m_viewMags[v].getMax();
-    } else if (!m_normalizeColumns) {
+    } else if (m_normalization != NormalizeColumns) {
         if (m_colourScale == LinearColourScale //||
 //            m_colourScale == MeterColourScale) {
             ) {
@@ -1294,11 +1196,11 @@
 }
 
 bool
-SpectrogramLayer::getYBinRange(View *v, int y, double &q0, double &q1) const
+SpectrogramLayer::getYBinRange(LayerGeometryProvider *v, int y, double &q0, double &q1) const
 {
     Profiler profiler("SpectrogramLayer::getYBinRange");
     
-    int h = v->height();
+    int h = v->getPaintHeight();
     if (y < 0 || y >= h) return false;
 
     sv_samplerate_t sr = m_model->getSampleRate();
@@ -1320,11 +1222,11 @@
 }
 
 bool
-SpectrogramLayer::getSmoothedYBinRange(View *v, int y, double &q0, double &q1) const
+SpectrogramLayer::getSmoothedYBinRange(LayerGeometryProvider *v, int y, double &q0, double &q1) const
 {
     Profiler profiler("SpectrogramLayer::getSmoothedYBinRange");
 
-    int h = v->height();
+    int h = v->getPaintHeight();
     if (y < 0 || y >= h) return false;
 
     sv_samplerate_t sr = m_model->getSampleRate();
@@ -1346,7 +1248,7 @@
 }
     
 bool
-SpectrogramLayer::getXBinRange(View *v, int x, double &s0, double &s1) const
+SpectrogramLayer::getXBinRange(LayerGeometryProvider *v, int x, double &s0, double &s1) const
 {
     sv_frame_t modelStart = m_model->getStartFrame();
     sv_frame_t modelEnd = m_model->getEndFrame();
@@ -1370,7 +1272,7 @@
 }
  
 bool
-SpectrogramLayer::getXBinSourceRange(View *v, int x, RealTime &min, RealTime &max) const
+SpectrogramLayer::getXBinSourceRange(LayerGeometryProvider *v, int x, RealTime &min, RealTime &max) const
 {
     double s0 = 0, s1 = 0;
     if (!getXBinRange(v, x, s0, s1)) return false;
@@ -1389,7 +1291,7 @@
 }
 
 bool
-SpectrogramLayer::getYBinSourceRange(View *v, int y, double &freqMin, double &freqMax)
+SpectrogramLayer::getYBinSourceRange(LayerGeometryProvider *v, int y, double &freqMin, double &freqMax)
 const
 {
     double q0 = 0, q1 = 0;
@@ -1408,7 +1310,7 @@
 }
 
 bool
-SpectrogramLayer::getAdjustedYBinSourceRange(View *v, int x, int y,
+SpectrogramLayer::getAdjustedYBinSourceRange(LayerGeometryProvider *v, int x, int y,
 					     double &freqMin, double &freqMax,
 					     double &adjFreqMin, double &adjFreqMax)
 const
@@ -1475,7 +1377,7 @@
 }
     
 bool
-SpectrogramLayer::getXYBinSourceRange(View *v, int x, int y,
+SpectrogramLayer::getXYBinSourceRange(LayerGeometryProvider *v, int x, int y,
 				      double &min, double &max,
 				      double &phaseMin, double &phaseMax) const
 {
@@ -1544,7 +1446,7 @@
 }
    
 int
-SpectrogramLayer::getZeroPadLevel(const View *v) const
+SpectrogramLayer::getZeroPadLevel(const LayerGeometryProvider *v) const
 {
     //!!! tidy all this stuff
 
@@ -1574,7 +1476,7 @@
     }
 
     double perPixel =
-        double(v->height()) /
+        double(v->getPaintHeight()) /
         double((maxbin - minbin) / (m_zeroPadLevel + 1));
 
     if (perPixel > 2.8) {
@@ -1587,52 +1489,51 @@
 }
 
 int
-SpectrogramLayer::getFFTSize(const View *v) const
+SpectrogramLayer::getFFTSize(const LayerGeometryProvider *v) const
 {
     return m_fftSize * (getZeroPadLevel(v) + 1);
 }
 	
 FFTModel *
-SpectrogramLayer::getFFTModel(const View *v) const
+SpectrogramLayer::getFFTModel(const LayerGeometryProvider *v) const
 {
     if (!m_model) return 0;
 
     int fftSize = getFFTSize(v);
 
-    if (m_fftModels.find(v) != m_fftModels.end()) {
-        if (m_fftModels[v].first == 0) {
+    const View *view = v->getView();
+    
+    if (m_fftModels.find(view) != m_fftModels.end()) {
+        if (m_fftModels[view] == 0) {
 #ifdef DEBUG_SPECTROGRAM_REPAINT
-            SVDEBUG << "SpectrogramLayer::getFFTModel(" << v << "): Found null model" << endl;
+            cerr << "SpectrogramLayer::getFFTModel(" << v << "): Found null model" << endl;
 #endif
             return 0;
         }
-        if (m_fftModels[v].first->getHeight() != fftSize / 2 + 1) {
+        if (m_fftModels[view]->getHeight() != fftSize / 2 + 1) {
 #ifdef DEBUG_SPECTROGRAM_REPAINT
-            SVDEBUG << "SpectrogramLayer::getFFTModel(" << v << "): Found a model with the wrong height (" << m_fftModels[v].first->getHeight() << ", wanted " << (fftSize / 2 + 1) << ")" << endl;
+            cerr << "SpectrogramLayer::getFFTModel(" << v << "): Found a model with the wrong height (" << m_fftModels[view].first->getHeight() << ", wanted " << (fftSize / 2 + 1) << ")" << endl;
 #endif
-            delete m_fftModels[v].first;
-            m_fftModels.erase(v);
-            delete m_peakCaches[v];
-            m_peakCaches.erase(v);
+            delete m_fftModels[view];
+            m_fftModels.erase(view);
+            delete m_peakCaches[view];
+            m_peakCaches.erase(view);
         } else {
 #ifdef DEBUG_SPECTROGRAM_REPAINT
-            SVDEBUG << "SpectrogramLayer::getFFTModel(" << v << "): Found a good model of height " << m_fftModels[v].first->getHeight() << endl;
+            cerr << "SpectrogramLayer::getFFTModel(" << v << "): Found a good model of height " << m_fftModels[view]->getHeight() << endl;
 #endif
-            return m_fftModels[v].first;
+            return m_fftModels[view];
         }
     }
 
-    if (m_fftModels.find(v) == m_fftModels.end()) {
+    if (m_fftModels.find(view) == m_fftModels.end()) {
 
         FFTModel *model = new FFTModel(m_model,
                                        m_channel,
                                        m_windowType,
                                        m_windowSize,
                                        getWindowIncrement(),
-                                       fftSize,
-                                       true, // polar
-                                       StorageAdviser::SpeedCritical,
-                                       m_candidateFillStartFrame);
+                                       fftSize);
 
         if (!model->isOK()) {
             QMessageBox::critical
@@ -1640,7 +1541,7 @@
                  tr("Failed to create the FFT model for this spectrogram.\n"
                     "There may be insufficient memory or disc space to continue."));
             delete model;
-            m_fftModels[v] = FFTFillPair(0, 0);
+            m_fftModels[view] = 0;
             return 0;
         }
 
@@ -1652,29 +1553,22 @@
             m_sliceableModel = model;
         }
 
-        m_fftModels[v] = FFTFillPair(model, 0);
-
-        model->resume();
-        
-        delete m_updateTimer;
-        m_updateTimer = new QTimer((SpectrogramLayer *)this);
-        connect(m_updateTimer, SIGNAL(timeout()),
-                this, SLOT(fillTimerTimedOut()));
-        m_updateTimer->start(200);
+        m_fftModels[view] = model;
     }
 
-    return m_fftModels[v].first;
+    return m_fftModels[view];
 }
 
 Dense3DModelPeakCache *
-SpectrogramLayer::getPeakCache(const View *v) const
+SpectrogramLayer::getPeakCache(const LayerGeometryProvider *v) const
 {
-    if (!m_peakCaches[v]) {
+    const View *view = v->getView();
+    if (!m_peakCaches[view]) {
         FFTModel *f = getFFTModel(v);
         if (!f) return 0;
-        m_peakCaches[v] = new Dense3DModelPeakCache(f, 8);
+        m_peakCaches[view] = new Dense3DModelPeakCache(f, 8);
     }
-    return m_peakCaches[v];
+    return m_peakCaches[view];
 }
 
 const Model *
@@ -1682,7 +1576,7 @@
 {
     if (m_sliceableModel) return m_sliceableModel;
     if (m_fftModels.empty()) return 0;
-    m_sliceableModel = m_fftModels.begin()->second.first;
+    m_sliceableModel = m_fftModels.begin()->second;
     return m_sliceableModel;
 }
 
@@ -1691,7 +1585,7 @@
 {
     for (ViewFFTMap::iterator i = m_fftModels.begin();
          i != m_fftModels.end(); ++i) {
-        delete i->second.first;
+        delete i->second;
     }
     for (PeakCacheMap::iterator i = m_peakCaches.begin();
          i != m_peakCaches.end(); ++i) {
@@ -1719,11 +1613,11 @@
 }
 
 bool
-SpectrogramLayer::updateViewMagnitudes(View *v) const
+SpectrogramLayer::updateViewMagnitudes(LayerGeometryProvider *v) const
 {
     MagnitudeRange mag;
 
-    int x0 = 0, x1 = v->width();
+    int x0 = 0, x1 = v->getPaintWidth();
     double s00 = 0, s01 = 0, s10 = 0, s11 = 0;
     
     if (!getXBinRange(v, x0, s00, s01)) {
@@ -1750,7 +1644,7 @@
     }
 
 #ifdef DEBUG_SPECTROGRAM_REPAINT
-    SVDEBUG << "SpectrogramLayer::updateViewMagnitudes returning from cols "
+    cerr << "SpectrogramLayer::updateViewMagnitudes returning from cols "
               << s0 << " -> " << s1 << " inclusive" << endl;
 #endif
 
@@ -1767,7 +1661,7 @@
 }
 
 void
-SpectrogramLayer::paint(View *v, QPainter &paint, QRect rect) const
+SpectrogramLayer::paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const
 {
     // What a lovely, old-fashioned function this is.
     // It's practically FORTRAN 77 in its clarity and linearity.
@@ -1775,14 +1669,12 @@
     Profiler profiler("SpectrogramLayer::paint", false);
 
 #ifdef DEBUG_SPECTROGRAM_REPAINT
-    SVDEBUG << "SpectrogramLayer::paint(): m_model is " << m_model << ", zoom level is " << v->getZoomLevel() << ", m_updateTimer " << m_updateTimer << endl;
+    cerr << "SpectrogramLayer::paint(): m_model is " << m_model << ", zoom level is " << v->getZoomLevel() << endl;
     
     cerr << "rect is " << rect.x() << "," << rect.y() << " " << rect.width() << "x" << rect.height() << endl;
 #endif
 
     sv_frame_t startFrame = v->getStartFrame();
-    if (startFrame < 0) m_candidateFillStartFrame = 0;
-    else m_candidateFillStartFrame = startFrame;
 
     if (!m_model || !m_model->isOK() || !m_model->isReady()) {
 	return;
@@ -1807,23 +1699,21 @@
 	return;
     }
 */
-    ImageCache &cache = m_imageCaches[v];
+
+    const View *view = v->getView();
+    
+    ImageCache &cache = m_imageCaches[view];
 
 #ifdef DEBUG_SPECTROGRAM_REPAINT
-    SVDEBUG << "SpectrogramLayer::paint(): image cache valid area " << cache.
+    cerr << "SpectrogramLayer::paint(): image cache valid area " << cache.
 
 validArea.x() << ", " << cache.validArea.y() << ", " << cache.validArea.width() << "x" << cache.validArea.height() << endl;
 #endif
 
-#ifdef DEBUG_SPECTROGRAM_REPAINT
-    bool stillCacheing = (m_updateTimer != 0);
-    SVDEBUG << "SpectrogramLayer::paint(): Still cacheing = " << stillCacheing << endl;
-#endif
-
     int zoomLevel = v->getZoomLevel();
 
     int x0 = 0;
-    int x1 = v->width();
+    int x1 = v->getPaintWidth();
 
     bool recreateWholeImageCache = true;
 
@@ -1840,8 +1730,8 @@
         int ch = cache.image.height();
         
 	if (int(cache.zoomLevel) == zoomLevel &&
-	    cw == v->width() &&
-	    ch == v->height()) {
+	    cw == v->getPaintWidth() &&
+	    ch == v->getPaintHeight()) {
 
 	    if (v->getXForFrame(cache.startFrame) ==
 		v->getXForFrame(startFrame) &&
@@ -1947,13 +1837,13 @@
                 cerr << "(cache zoomLevel " << cache.zoomLevel
                           << " != " << zoomLevel << ")" << endl;
             }
-            if (cw != v->width()) {
+            if (cw != v->getPaintWidth()) {
                 cerr << "(cache width " << cw
-                          << " != " << v->width();
+                          << " != " << v->getPaintWidth();
             }
-            if (ch != v->height()) {
+            if (ch != v->getPaintHeight()) {
                 cerr << "(cache height " << ch
-                          << " != " << v->height();
+                          << " != " << v->getPaintHeight();
             }
 #endif
             cache.validArea = QRect();
@@ -1965,7 +1855,7 @@
 #ifdef DEBUG_SPECTROGRAM_REPAINT
         cerr << "SpectrogramLayer: magnitude range changed to [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "]" << endl;
 #endif
-        if (m_normalizeVisibleArea) {
+        if (m_normalization == NormalizeVisibleArea) {
             cache.validArea = QRect();
             recreateWholeImageCache = true;
         }
@@ -1977,7 +1867,7 @@
 
     if (recreateWholeImageCache) {
         x0 = 0;
-        x1 = v->width();
+        x1 = v->getPaintWidth();
     }
 
     struct timeval tv;
@@ -2021,7 +1911,7 @@
     // is coherent without having to worry about vertical matching of
     // required and valid areas as well as horizontal.
 
-    int h = v->height();
+    int h = v->getPaintHeight();
 
     if (cache.validArea.width() > 0) {
 
@@ -2174,10 +2064,6 @@
         SVDEBUG << "*** NOTE: w == 0" << endl;
     }
 
-#ifdef DEBUG_SPECTROGRAM_REPAINT
-    int pixels = 0;
-#endif
-
     Profiler outerprof("SpectrogramLayer::paint: all cols");
 
     // The draw buffer contains a fragment at either our pixel
@@ -2328,15 +2214,15 @@
 
     if (recreateWholeImageCache) {
 #ifdef DEBUG_SPECTROGRAM_REPAINT
-        SVDEBUG << "Recreating image cache: width = " << v->width()
+        cerr << "Recreating image cache: width = " << v->getPaintWidth()
                   << ", height = " << h << endl;
 #endif
-	cache.image = QImage(v->width(), h, QImage::Format_ARGB32_Premultiplied);
+	cache.image = QImage(v->getPaintWidth(), h, QImage::Format_ARGB32_Premultiplied);
     }
 
     if (w > 0) {
 #ifdef DEBUG_SPECTROGRAM_REPAINT
-        SVDEBUG << "Painting " << w << "x" << h
+        cerr << "Painting " << w << "x" << h
                   << " from draw buffer at " << 0 << "," << 0
                   << " to " << w << "x" << h << " on cache at "
                   << x0 << "," << 0 << endl;
@@ -2348,7 +2234,7 @@
             int scaledLeft = v->getXForFrame(leftBoundaryFrame);
             int scaledRight = v->getXForFrame(rightBoundaryFrame);
 #ifdef DEBUG_SPECTROGRAM_REPAINT
-            SVDEBUG << "Rescaling image from " << bufwid
+            cerr << "Rescaling image from " << bufwid
                  << "x" << h << " to "
                  << scaledRight-scaledLeft << "x" << h << endl;
 #endif
@@ -2363,7 +2249,7 @@
             int scaledLeftCrop = v->getXForFrame(leftCropFrame);
             int scaledRightCrop = v->getXForFrame(rightCropFrame);
 #ifdef DEBUG_SPECTROGRAM_REPAINT
-            SVDEBUG << "Drawing image region of width " << scaledRightCrop - scaledLeftCrop << " to "
+            cerr << "Drawing image region of width " << scaledRightCrop - scaledLeftCrop << " to "
                  << scaledLeftCrop << " from " << scaledLeftCrop - scaledLeft << endl;
 #endif
             cachePainter.drawImage
@@ -2384,7 +2270,7 @@
     QRect pr = rect & cache.validArea;
 
 #ifdef DEBUG_SPECTROGRAM_REPAINT
-    SVDEBUG << "Painting " << pr.width() << "x" << pr.height()
+    cerr << "Painting " << pr.width() << "x" << pr.height()
               << " from cache at " << pr.x() << "," << pr.y()
               << " to window" << endl;
 #endif
@@ -2400,27 +2286,27 @@
 
     if (!m_synchronous) {
 
-        if (!m_normalizeVisibleArea || !overallMagChanged) {
+        if ((m_normalization != NormalizeVisibleArea) || !overallMagChanged) {
     
             if (cache.validArea.x() > 0) {
 #ifdef DEBUG_SPECTROGRAM_REPAINT
-                SVDEBUG << "SpectrogramLayer::paint() updating left (0, "
+                cerr << "SpectrogramLayer::paint() updating left (0, "
                           << cache.validArea.x() << ")" << endl;
 #endif
-                v->update(0, 0, cache.validArea.x(), h);
+                v->getView()->update(0, 0, cache.validArea.x(), h);
             }
             
             if (cache.validArea.x() + cache.validArea.width() <
                 cache.image.width()) {
 #ifdef DEBUG_SPECTROGRAM_REPAINT
-                SVDEBUG << "SpectrogramLayer::paint() updating right ("
+                cerr << "SpectrogramLayer::paint() updating right ("
                           << cache.validArea.x() + cache.validArea.width()
                           << ", "
                           << cache.image.width() - (cache.validArea.x() +
                                                      cache.validArea.width())
                           << ")" << endl;
 #endif
-                v->update(cache.validArea.x() + cache.validArea.width(),
+                v->getView()->update(cache.validArea.x() + cache.validArea.width(),
                           0,
                           cache.image.width() - (cache.validArea.x() +
                                                   cache.validArea.width()),
@@ -2430,14 +2316,14 @@
             // overallMagChanged
             cerr << "\noverallMagChanged - updating all\n" << endl;
             cache.validArea = QRect();
-            v->update();
+            v->getView()->update();
         }
     }
 
     illuminateLocalFeatures(v, paint);
 
 #ifdef DEBUG_SPECTROGRAM_REPAINT
-    SVDEBUG << "SpectrogramLayer::paint() returning" << endl;
+    cerr << "SpectrogramLayer::paint() returning" << endl;
 #endif
 
     if (!m_synchronous) {
@@ -2445,12 +2331,10 @@
         (void)gettimeofday(&tv, 0);
         m_lastPaintTime = RealTime::fromTimeval(tv) - mainPaintStart;
     }
-
-//!!!    if (fftSuspended) fft->resume();
 }
 
 bool
-SpectrogramLayer::paintDrawBufferPeakFrequencies(View *v,
+SpectrogramLayer::paintDrawBufferPeakFrequencies(LayerGeometryProvider *v,
                                                  int w,
                                                  int h,
                                                  const vector<int> &binforx,
@@ -2514,9 +2398,9 @@
                                                     minbin, maxbin - 1);
                 if (m_colourScale == PhaseColourScale) {
                     fft->getPhasesAt(sx, values, minbin, maxbin - minbin + 1);
-                } else if (m_normalizeColumns) {
+                } else if (m_normalization == NormalizeColumns) {
                     fft->getNormalizedMagnitudesAt(sx, values, minbin, maxbin - minbin + 1);
-                } else if (m_normalizeHybrid) {
+                } else if (m_normalization == NormalizeHybrid) {
                     fft->getNormalizedMagnitudesAt(sx, values, minbin, maxbin - minbin + 1);
                     double max = fft->getMaximumMagnitudeAt(sx);
                     if (max > 0.f) {
@@ -2542,7 +2426,7 @@
                 double value = values[bin - minbin];
 
                 if (m_colourScale != PhaseColourScale) {
-                    if (!m_normalizeColumns && !m_normalizeHybrid) {
+                    if (m_normalization != NormalizeColumns) {
                         value /= (m_fftSize/2.0);
                     }
                     mag.sample(float(value));
@@ -2578,7 +2462,7 @@
 }
 
 bool
-SpectrogramLayer::paintDrawBuffer(View *v,
+SpectrogramLayer::paintDrawBuffer(LayerGeometryProvider *v,
                                   int w,
                                   int h,
                                   const vector<int> &binforx,
@@ -2677,29 +2561,30 @@
             if (sx != psx) {
                 if (fft) {
 #ifdef DEBUG_SPECTROGRAM_REPAINT
-                    SVDEBUG << "Retrieving column " << sx << " from fft directly" << endl;
+                    cerr << "Retrieving column " << sx << " from fft directly" << endl;
 #endif
                     if (m_colourScale == PhaseColourScale) {
                         fft->getPhasesAt(sx, autoarray, minbin, maxbin - minbin + 1);
-                    } else if (m_normalizeColumns) {
+                    } else if (m_normalization == NormalizeColumns) {
                         fft->getNormalizedMagnitudesAt(sx, autoarray, minbin, maxbin - minbin + 1);
-                    } else if (m_normalizeHybrid) {
+                    } else if (m_normalization == NormalizeHybrid) {
                         fft->getNormalizedMagnitudesAt(sx, autoarray, minbin, maxbin - minbin + 1);
-                        double max = fft->getMaximumMagnitudeAt(sx);
+                        float max = fft->getMaximumMagnitudeAt(sx);
+                        float scale = log10f(max + 1.f);
+                        cout << "sx = " << sx << ", max = " << max << ", log10(max) = " << log10(max) << ", scale = " << scale << endl;
                         for (int i = minbin; i <= maxbin; ++i) {
-                            if (max > 0.0) {
-                                autoarray[i - minbin] = float(autoarray[i - minbin] * log10(max));
-                            }
+                            autoarray[i - minbin] *= scale;
                         }
                     } else {
                         fft->getMagnitudesAt(sx, autoarray, minbin, maxbin - minbin + 1);
                     }
                 } else {
 #ifdef DEBUG_SPECTROGRAM_REPAINT
-                    SVDEBUG << "Retrieving column " << sx << " from peaks cache" << endl;
+                    cerr << "Retrieving column " << sx << " from peaks cache" << endl;
 #endif
                     c = sourceModel->getColumn(sx);
-                    if (m_normalizeColumns || m_normalizeHybrid) {
+                    if (m_normalization == NormalizeColumns ||
+                        m_normalization == NormalizeHybrid) {
                         for (int y = 0; y < h; ++y) {
                             if (c[y] > columnMax) columnMax = c[y];
                         }
@@ -2742,7 +2627,8 @@
                     value = prop * v0 + (1.0 - prop) * v1;
 
                     if (m_colourScale != PhaseColourScale) {
-                        if (!m_normalizeColumns) {
+                        if (m_normalization != NormalizeColumns &&
+                            m_normalization != NormalizeHybrid) {
                             value /= (m_fftSize/2.0);
                         }
                         mag.sample(float(value));
@@ -2767,7 +2653,8 @@
                         }
 
                         if (m_colourScale != PhaseColourScale) {
-                            if (!m_normalizeColumns) {
+                            if (m_normalization != NormalizeColumns &&
+                                m_normalization != NormalizeHybrid) {
                                 value /= (m_fftSize/2.0);
                             }
                             mag.sample(float(value));
@@ -2801,11 +2688,12 @@
             double peak = peaks[y];
             
             if (m_colourScale != PhaseColourScale &&
-                (m_normalizeColumns || m_normalizeHybrid) && 
+                (m_normalization == NormalizeColumns ||
+                 m_normalization == NormalizeHybrid) &&
                 columnMax > 0.f) {
                 peak /= columnMax;
-                if (m_normalizeHybrid) {
-                    peak *= log10(columnMax);
+                if (m_normalization == NormalizeHybrid) {
+                    peak *= log10(columnMax + 1.f);
                 }
             }
             
@@ -2819,7 +2707,7 @@
 }
 
 void
-SpectrogramLayer::illuminateLocalFeatures(View *v, QPainter &paint) const
+SpectrogramLayer::illuminateLocalFeatures(LayerGeometryProvider *v, QPainter &paint) const
 {
     Profiler profiler("SpectrogramLayer::illuminateLocalFeatures");
 
@@ -2858,7 +2746,7 @@
 }
 
 double
-SpectrogramLayer::getYForFrequency(const View *v, double frequency) const
+SpectrogramLayer::getYForFrequency(const LayerGeometryProvider *v, double frequency) const
 {
     return v->getYForFrequency(frequency,
 			       getEffectiveMinFrequency(),
@@ -2867,7 +2755,7 @@
 }
 
 double
-SpectrogramLayer::getFrequencyForY(const View *v, int y) const
+SpectrogramLayer::getFrequencyForY(const LayerGeometryProvider *v, int y) const
 {
     return v->getFrequencyForY(y,
 			       getEffectiveMinFrequency(),
@@ -2876,23 +2764,25 @@
 }
 
 int
-SpectrogramLayer::getCompletion(View *v) const
+SpectrogramLayer::getCompletion(LayerGeometryProvider *v) const
 {
-    if (m_updateTimer == 0) return 100;
-    if (m_fftModels.find(v) == m_fftModels.end()) return 100;
-
-    int completion = m_fftModels[v].first->getCompletion();
+    const View *view = v->getView();
+    
+    if (m_fftModels.find(view) == m_fftModels.end()) return 100;
+
+    int completion = m_fftModels[view]->getCompletion();
 #ifdef DEBUG_SPECTROGRAM_REPAINT
-    SVDEBUG << "SpectrogramLayer::getCompletion: completion = " << completion << endl;
+    cerr << "SpectrogramLayer::getCompletion: completion = " << completion << endl;
 #endif
     return completion;
 }
 
 QString
-SpectrogramLayer::getError(View *v) const
+SpectrogramLayer::getError(LayerGeometryProvider *v) const
 {
-    if (m_fftModels.find(v) == m_fftModels.end()) return "";
-    return m_fftModels[v].first->getError();
+    const View *view = v->getView();
+    if (m_fftModels.find(view) == m_fftModels.end()) return "";
+    return m_fftModels[view]->getError();
 }
 
 bool
@@ -2953,7 +2843,7 @@
 }
 
 bool
-SpectrogramLayer::getYScaleValue(const View *v, int y,
+SpectrogramLayer::getYScaleValue(const LayerGeometryProvider *v, int y,
                                  double &value, QString &unit) const
 {
     value = getFrequencyForY(v, y);
@@ -2962,7 +2852,7 @@
 }
 
 bool
-SpectrogramLayer::snapToFeatureFrame(View *,
+SpectrogramLayer::snapToFeatureFrame(LayerGeometryProvider *,
                                      sv_frame_t &frame,
 				     int &resolution,
 				     SnapType snap) const
@@ -2985,12 +2875,13 @@
 } 
 
 void
-SpectrogramLayer::measureDoubleClick(View *v, QMouseEvent *e)
+SpectrogramLayer::measureDoubleClick(LayerGeometryProvider *v, QMouseEvent *e)
 {
-    ImageCache &cache = m_imageCaches[v];
+    const View *view = v->getView();
+    ImageCache &cache = m_imageCaches[view];
 
     cerr << "cache width: " << cache.image.width() << ", height: "
-              << cache.image.height() << endl;
+         << cache.image.height() << endl;
 
     QImage image = cache.image;
 
@@ -3005,11 +2896,11 @@
 }
 
 bool
-SpectrogramLayer::getCrosshairExtents(View *v, QPainter &paint,
+SpectrogramLayer::getCrosshairExtents(LayerGeometryProvider *v, QPainter &paint,
                                       QPoint cursorPos,
                                       std::vector<QRect> &extents) const
 {
-    QRect vertical(cursorPos.x() - 12, 0, 12, v->height());
+    QRect vertical(cursorPos.x() - 12, 0, 12, v->getPaintHeight());
     extents.push_back(vertical);
 
     QRect horizontal(0, cursorPos.y(), cursorPos.x(), 1);
@@ -3028,14 +2919,14 @@
     extents.push_back(pitch);
 
     QRect rt(cursorPos.x(),
-             v->height() - paint.fontMetrics().height() - 2,
+             v->getPaintHeight() - paint.fontMetrics().height() - 2,
              paint.fontMetrics().width("1234.567 s"),
              paint.fontMetrics().height());
     extents.push_back(rt);
 
     int w(paint.fontMetrics().width("1234567890") + 2);
     QRect frame(cursorPos.x() - w - 2,
-                v->height() - paint.fontMetrics().height() - 2,
+                v->getPaintHeight() - paint.fontMetrics().height() - 2,
                 w,
                 paint.fontMetrics().height());
     extents.push_back(frame);
@@ -3044,7 +2935,7 @@
 }
 
 void
-SpectrogramLayer::paintCrosshairs(View *v, QPainter &paint,
+SpectrogramLayer::paintCrosshairs(LayerGeometryProvider *v, QPainter &paint,
                                   QPoint cursorPos) const
 {
     paint.save();
@@ -3059,7 +2950,7 @@
     paint.setPen(m_crosshairColour);
 
     paint.drawLine(0, cursorPos.y(), cursorPos.x() - 1, cursorPos.y());
-    paint.drawLine(cursorPos.x(), 0, cursorPos.x(), v->height());
+    paint.drawLine(cursorPos.x(), 0, cursorPos.x(), v->getPaintHeight());
     
     double fundamental = getFrequencyForY(v, cursorPos.y());
 
@@ -3084,12 +2975,12 @@
     QString frameLabel = QString("%1").arg(frame);
     v->drawVisibleText(paint,
                        cursorPos.x() - paint.fontMetrics().width(frameLabel) - 2,
-                       v->height() - 2,
+                       v->getPaintHeight() - 2,
                        frameLabel,
                        View::OutlinedText);
     v->drawVisibleText(paint,
                        cursorPos.x() + 2,
-                       v->height() - 2,
+                       v->getPaintHeight() - 2,
                        rtLabel,
                        View::OutlinedText);
 
@@ -3098,7 +2989,7 @@
     while (harmonic < 100) {
 
         int hy = int(lrint(getYForFrequency(v, fundamental * harmonic)));
-        if (hy < 0 || hy > v->height()) break;
+        if (hy < 0 || hy > v->getPaintHeight()) break;
         
         int len = 7;
 
@@ -3122,7 +3013,7 @@
 }
 
 QString
-SpectrogramLayer::getFeatureDescription(View *v, QPoint &pos) const
+SpectrogramLayer::getFeatureDescription(LayerGeometryProvider *v, QPoint &pos) const
 {
     int x = pos.x();
     int y = pos.y();
@@ -3244,7 +3135,7 @@
 }
 
 int
-SpectrogramLayer::getVerticalScaleWidth(View *, bool detailed, QPainter &paint) const
+SpectrogramLayer::getVerticalScaleWidth(LayerGeometryProvider *, bool detailed, QPainter &paint) const
 {
     if (!m_model || !m_model->isOK()) return 0;
 
@@ -3265,7 +3156,7 @@
 }
 
 void
-SpectrogramLayer::paintVerticalScale(View *v, bool detailed, QPainter &paint, QRect rect) const
+SpectrogramLayer::paintVerticalScale(LayerGeometryProvider *v, bool detailed, QPainter &paint, QRect rect) const
 {
     if (!m_model || !m_model->isOK()) {
 	return;
@@ -3379,10 +3270,10 @@
 
     int bin = -1;
 
-    for (int y = 0; y < v->height(); ++y) {
+    for (int y = 0; y < v->getPaintHeight(); ++y) {
 
 	double q0, q1;
-	if (!getYBinRange(v, v->height() - y, q0, q1)) continue;
+	if (!getYBinRange(v, v->getPaintHeight() - y, q0, q1)) continue;
 
 	int vy;
 
@@ -3596,7 +3487,7 @@
 }
 
 void
-SpectrogramLayer::updateMeasureRectYCoords(View *v, const MeasureRect &r) const
+SpectrogramLayer::updateMeasureRectYCoords(LayerGeometryProvider *v, const MeasureRect &r) const
 {
     int y0 = 0;
     if (r.startY > 0.0) y0 = int(getYForFrequency(v, r.startY));
@@ -3610,7 +3501,7 @@
 }
 
 void
-SpectrogramLayer::setMeasureRectYCoord(View *v, MeasureRect &r, bool start, int y) const
+SpectrogramLayer::setMeasureRectYCoord(LayerGeometryProvider *v, MeasureRect &r, bool start, int y) const
 {
     if (start) {
         r.startY = getFrequencyForY(v, y);
@@ -3657,9 +3548,9 @@
     s += QString("normalizeColumns=\"%1\" "
                  "normalizeVisibleArea=\"%2\" "
                  "normalizeHybrid=\"%3\" ")
-	.arg(m_normalizeColumns ? "true" : "false")
-        .arg(m_normalizeVisibleArea ? "true" : "false")
-        .arg(m_normalizeHybrid ? "true" : "false");
+	.arg(m_normalization == NormalizeColumns ? "true" : "false")
+        .arg(m_normalization == NormalizeVisibleArea ? "true" : "false")
+        .arg(m_normalization == NormalizeHybrid ? "true" : "false");
 
     Layer::toXml(stream, indent, extraAttributes + " " + s);
 }
@@ -3727,14 +3618,20 @@
 
     bool normalizeColumns =
 	(attributes.value("normalizeColumns").trimmed() == "true");
-    setNormalizeColumns(normalizeColumns);
+    if (normalizeColumns) {
+        setNormalization(NormalizeColumns);
+    }
 
     bool normalizeVisibleArea =
 	(attributes.value("normalizeVisibleArea").trimmed() == "true");
-    setNormalizeVisibleArea(normalizeVisibleArea);
+    if (normalizeVisibleArea) {
+        setNormalization(NormalizeVisibleArea);
+    }
 
     bool normalizeHybrid =
 	(attributes.value("normalizeHybrid").trimmed() == "true");
-    setNormalizeHybrid(normalizeHybrid);
+    if (normalizeHybrid) {
+        setNormalization(NormalizeHybrid);
+    }
 }
     
--- a/layer/SpectrogramLayer.h	Mon Apr 13 13:52:05 2015 +0100
+++ b/layer/SpectrogramLayer.h	Thu Aug 20 14:54:21 2015 +0100
@@ -57,23 +57,23 @@
 
     virtual const ZoomConstraint *getZoomConstraint() const { return this; }
     virtual const Model *getModel() const { return m_model; }
-    virtual void paint(View *v, QPainter &paint, QRect rect) const;
+    virtual void paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const;
     virtual void setSynchronousPainting(bool synchronous);
 
-    virtual int getVerticalScaleWidth(View *v, bool detailed, QPainter &) const;
-    virtual void paintVerticalScale(View *v, bool detailed, QPainter &paint, QRect rect) const;
+    virtual int getVerticalScaleWidth(LayerGeometryProvider *v, bool detailed, QPainter &) const;
+    virtual void paintVerticalScale(LayerGeometryProvider *v, bool detailed, QPainter &paint, QRect rect) const;
 
-    virtual bool getCrosshairExtents(View *, QPainter &, QPoint cursorPos,
+    virtual bool getCrosshairExtents(LayerGeometryProvider *, QPainter &, QPoint cursorPos,
                                      std::vector<QRect> &extents) const;
-    virtual void paintCrosshairs(View *, QPainter &, QPoint) const;
+    virtual void paintCrosshairs(LayerGeometryProvider *, QPainter &, QPoint) const;
 
-    virtual QString getFeatureDescription(View *v, QPoint &) const;
+    virtual QString getFeatureDescription(LayerGeometryProvider *v, QPoint &) const;
 
-    virtual bool snapToFeatureFrame(View *v, sv_frame_t &frame,
+    virtual bool snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame,
 				    int &resolution,
 				    SnapType snap) const;
 
-    virtual void measureDoubleClick(View *, QMouseEvent *);
+    virtual void measureDoubleClick(LayerGeometryProvider *, QMouseEvent *);
 
     virtual bool hasLightBackground() const;
 
@@ -88,6 +88,8 @@
                                          int *min, int *max, int *deflt) const;
     virtual QString getPropertyValueLabel(const PropertyName &,
 					  int value) const;
+    virtual QString getPropertyValueIconName(const PropertyName &,
+                                             int value) const;
     virtual RangeMapper *getNewPropertyRangeMapper(const PropertyName &) const;
     virtual void setProperty(const PropertyName &, int value);
 
@@ -170,27 +172,24 @@
      */
     void setBinDisplay(BinDisplay);
     BinDisplay getBinDisplay() const;
- 
-    /**
-     * Normalize each column to its maximum value, independent of its
-     * neighbours.
-     */
-    void setNormalizeColumns(bool n);
-    bool getNormalizeColumns() const;
+
+    enum Normalization {
+        NoNormalization,
+        NormalizeColumns,
+        NormalizeVisibleArea,
+        NormalizeHybrid
+    };
 
     /**
-     * Normalize each value against the maximum in the visible region.
+     * Specify the normalization mode for bin values.
      */
-    void setNormalizeVisibleArea(bool n);
-    bool getNormalizeVisibleArea() const;
+    void setNormalization(Normalization);
+    Normalization getNormalization() const;
 
     /**
-     * Normalize each column to its maximum value, and then scale by
-     * the log of the (absolute) maximum value.
+     * Specify the colour map. See ColourMapper for the colour map
+     * values.
      */
-    void setNormalizeHybrid(bool n);
-    bool getNormalizeHybrid() const;
-    
     void setColourMap(int map);
     int getColourMap() const;
 
@@ -210,11 +209,11 @@
         return ColourHasMeaningfulValue;
     }
 
-    double getYForFrequency(const View *v, double frequency) const;
-    double getFrequencyForY(const View *v, int y) const;
+    double getYForFrequency(const LayerGeometryProvider *v, double frequency) const;
+    double getFrequencyForY(const LayerGeometryProvider *v, int y) const;
 
-    virtual int getCompletion(View *v) const;
-    virtual QString getError(View *v) const;
+    virtual int getCompletion(LayerGeometryProvider *v) const;
+    virtual QString getError(LayerGeometryProvider *v) const;
 
     virtual bool getValueExtents(double &min, double &max,
                                  bool &logarithmic, QString &unit) const;
@@ -223,16 +222,16 @@
 
     virtual bool setDisplayExtents(double min, double max);
 
-    virtual bool getYScaleValue(const View *, int, double &, QString &) const;
+    virtual bool getYScaleValue(const LayerGeometryProvider *, int, double &, QString &) const;
 
     virtual void toXml(QTextStream &stream, QString indent = "",
                        QString extraAttributes = "") const;
 
     void setProperties(const QXmlAttributes &attributes);
 
-    virtual void setLayerDormant(const View *v, bool dormant);
+    virtual void setLayerDormant(const LayerGeometryProvider *v, bool dormant);
 
-    virtual bool isLayerScrollable(const View *) const { return false; }
+    virtual bool isLayerScrollable(const LayerGeometryProvider *) const { return false; }
 
     virtual int getVerticalZoomSteps(int &defaultStep) const;
     virtual int getCurrentVerticalZoomStep() const;
@@ -247,8 +246,6 @@
     
     void preferenceChanged(PropertyContainer::PropertyName name);
 
-    void fillTimerTimedOut();
-
 protected:
     const DenseTimeValueModel *m_model; // I do not own this
 
@@ -272,9 +269,7 @@
     QColor              m_crosshairColour;
     FrequencyScale      m_frequencyScale;
     BinDisplay          m_binDisplay;
-    bool                m_normalizeColumns;
-    bool                m_normalizeVisibleArea;
-    bool                m_normalizeHybrid;
+    Normalization       m_normalization;
     int                 m_lastEmittedZoomStep;
     bool                m_synchronous;
 
@@ -326,19 +321,16 @@
      */
     mutable QImage m_drawBuffer;
 
-    mutable QTimer *m_updateTimer;
-
-    mutable sv_frame_t m_candidateFillStartFrame;
     bool m_exiting;
 
     void initialisePalette();
     void rotatePalette(int distance);
 
-    unsigned char getDisplayValue(View *v, double input) const;
+    unsigned char getDisplayValue(LayerGeometryProvider *v, double input) const;
 
     int getColourScaleWidth(QPainter &) const;
 
-    void illuminateLocalFeatures(View *v, QPainter &painter) const;
+    void illuminateLocalFeatures(LayerGeometryProvider *v, QPainter &painter) const;
 
     double getEffectiveMinFrequency() const;
     double getEffectiveMaxFrequency() const;
@@ -349,16 +341,16 @@
     // position, if the spectrogram has oversampling smoothing.  Use
     // getSmoothedYBinRange to obtain that.
 
-    bool getXBinRange(View *v, int x, double &windowMin, double &windowMax) const;
-    bool getYBinRange(View *v, int y, double &freqBinMin, double &freqBinMax) const;
-    bool getSmoothedYBinRange(View *v, int y, double &freqBinMin, double &freqBinMax) const;
+    bool getXBinRange(LayerGeometryProvider *v, int x, double &windowMin, double &windowMax) const;
+    bool getYBinRange(LayerGeometryProvider *v, int y, double &freqBinMin, double &freqBinMax) const;
+    bool getSmoothedYBinRange(LayerGeometryProvider *v, int y, double &freqBinMin, double &freqBinMax) const;
 
-    bool getYBinSourceRange(View *v, int y, double &freqMin, double &freqMax) const;
-    bool getAdjustedYBinSourceRange(View *v, int x, int y,
+    bool getYBinSourceRange(LayerGeometryProvider *v, int y, double &freqMin, double &freqMax) const;
+    bool getAdjustedYBinSourceRange(LayerGeometryProvider *v, int x, int y,
 				    double &freqMin, double &freqMax,
 				    double &adjFreqMin, double &adjFreqMax) const;
-    bool getXBinSourceRange(View *v, int x, RealTime &timeMin, RealTime &timeMax) const;
-    bool getXYBinSourceRange(View *v, int x, int y, double &min, double &max,
+    bool getXBinSourceRange(LayerGeometryProvider *v, int x, RealTime &timeMin, RealTime &timeMax) const;
+    bool getXYBinSourceRange(LayerGeometryProvider *v, int x, int y, double &min, double &max,
 			     double &phaseMin, double &phaseMax) const;
 
     int getWindowIncrement() const {
@@ -367,14 +359,13 @@
         else return m_windowSize / (1 << (m_windowHopLevel - 1));
     }
 
-    int getZeroPadLevel(const View *v) const;
-    int getFFTSize(const View *v) const;
-    FFTModel *getFFTModel(const View *v) const;
-    Dense3DModelPeakCache *getPeakCache(const View *v) const;
+    int getZeroPadLevel(const LayerGeometryProvider *v) const;
+    int getFFTSize(const LayerGeometryProvider *v) const;
+    FFTModel *getFFTModel(const LayerGeometryProvider *v) const;
+    Dense3DModelPeakCache *getPeakCache(const LayerGeometryProvider *v) const;
     void invalidateFFTModels();
 
-    typedef std::pair<FFTModel *, sv_frame_t> FFTFillPair; // model, last fill
-    typedef std::map<const View *, FFTFillPair> ViewFFTMap;
+    typedef std::map<const View *, FFTModel *> ViewFFTMap;
     typedef std::map<const View *, Dense3DModelPeakCache *> PeakCacheMap;
     mutable ViewFFTMap m_fftModels;
     mutable PeakCacheMap m_peakCaches;
@@ -422,18 +413,18 @@
         float m_max;
     };
 
-    typedef std::map<const View *, MagnitudeRange> ViewMagMap;
+    typedef std::map<const LayerGeometryProvider *, MagnitudeRange> ViewMagMap;
     mutable ViewMagMap m_viewMags;
     mutable std::vector<MagnitudeRange> m_columnMags;
     void invalidateMagnitudes();
-    bool updateViewMagnitudes(View *v) const;
-    bool paintDrawBuffer(View *v, int w, int h,
+    bool updateViewMagnitudes(LayerGeometryProvider *v) const;
+    bool paintDrawBuffer(LayerGeometryProvider *v, int w, int h,
                          const std::vector<int> &binforx,
                          const std::vector<double> &binfory,
                          bool usePeaksCache,
                          MagnitudeRange &overallMag,
                          bool &overallMagChanged) const;
-    bool paintDrawBufferPeakFrequencies(View *v, int w, int h,
+    bool paintDrawBufferPeakFrequencies(LayerGeometryProvider *v, int w, int h,
                                         const std::vector<int> &binforx,
                                         int minbin,
                                         int maxbin,
@@ -443,8 +434,8 @@
                                         MagnitudeRange &overallMag,
                                         bool &overallMagChanged) const;
 
-    virtual void updateMeasureRectYCoords(View *v, const MeasureRect &r) const;
-    virtual void setMeasureRectYCoord(View *v, MeasureRect &r, bool start, int y) const;
+    virtual void updateMeasureRectYCoords(LayerGeometryProvider *v, const MeasureRect &r) const;
+    virtual void setMeasureRectYCoord(LayerGeometryProvider *v, MeasureRect &r, bool start, int y) const;
 };
 
 #endif
--- a/layer/SpectrumLayer.cpp	Mon Apr 13 13:52:05 2015 +0100
+++ b/layer/SpectrumLayer.cpp	Thu Aug 20 14:54:21 2015 +0100
@@ -112,11 +112,7 @@
                                     m_windowType,
                                     m_windowSize,
                                     getWindowIncrement(),
-                                    m_windowSize,
-                                    false,
-                                    StorageAdviser::Criteria
-                                    (StorageAdviser::SpeedCritical |
-                                     StorageAdviser::FrequentLookupLikely));
+                                    m_windowSize);
 
     setSliceableModel(newFFT);
 
@@ -125,8 +121,6 @@
         m_biasCurve.push_back(1.f / (float(m_windowSize)/2.f));
     }
 
-    newFFT->resume();
-
     m_newFFTNeeded = false;
 }
 
@@ -386,18 +380,18 @@
 }
 
 bool
-SpectrumLayer::getXScaleValue(const View *v, int x, 
+SpectrumLayer::getXScaleValue(const LayerGeometryProvider *v, int x, 
                               double &value, QString &unit) const
 {
     if (m_xorigins.find(v) == m_xorigins.end()) return false;
     int xorigin = m_xorigins.find(v)->second;
-    value = getFrequencyForX(x - xorigin, v->width() - xorigin - 1);
+    value = getFrequencyForX(x - xorigin, v->getPaintWidth() - xorigin - 1);
     unit = "Hz";
     return true;
 }
 
 bool
-SpectrumLayer::getYScaleValue(const View *v, int y,
+SpectrumLayer::getYScaleValue(const LayerGeometryProvider *v, int y,
                               double &value, QString &unit) const
 {
     value = getValueForY(y, v);
@@ -419,7 +413,7 @@
 }
 
 bool
-SpectrumLayer::getYScaleDifference(const View *v, int y0, int y1,
+SpectrumLayer::getYScaleDifference(const LayerGeometryProvider *v, int y0, int y1,
                                    double &diff, QString &unit) const
 {
     bool rv = SliceLayer::getYScaleDifference(v, y0, y1, diff, unit);
@@ -429,14 +423,14 @@
 
 
 bool
-SpectrumLayer::getCrosshairExtents(View *v, QPainter &paint,
+SpectrumLayer::getCrosshairExtents(LayerGeometryProvider *v, QPainter &paint,
                                    QPoint cursorPos,
                                    std::vector<QRect> &extents) const
 {
-    QRect vertical(cursorPos.x(), cursorPos.y(), 1, v->height() - cursorPos.y());
+    QRect vertical(cursorPos.x(), cursorPos.y(), 1, v->getPaintHeight() - cursorPos.y());
     extents.push_back(vertical);
 
-    QRect horizontal(0, cursorPos.y(), v->width(), 12);
+    QRect horizontal(0, cursorPos.y(), v->getPaintWidth(), 12);
     extents.push_back(horizontal);
 
     int hoffset = 2;
@@ -455,14 +449,14 @@
     extents.push_back(log);
 
     QRect freq(cursorPos.x(),
-               v->height() - paint.fontMetrics().height() - hoffset,
+               v->getPaintHeight() - paint.fontMetrics().height() - hoffset,
                paint.fontMetrics().width("123456 Hz") + 2,
                paint.fontMetrics().height());
     extents.push_back(freq);
 
     int w(paint.fontMetrics().width("C#10+50c") + 2);
     QRect pitch(cursorPos.x() - w,
-                v->height() - paint.fontMetrics().height() - hoffset,
+                v->getPaintHeight() - paint.fontMetrics().height() - hoffset,
                 w,
                 paint.fontMetrics().height());
     extents.push_back(pitch);
@@ -471,7 +465,7 @@
 }
 
 void
-SpectrumLayer::paintCrosshairs(View *v, QPainter &paint,
+SpectrumLayer::paintCrosshairs(LayerGeometryProvider *v, QPainter &paint,
                                QPoint cursorPos) const
 {
     if (!m_sliceableModel) return;
@@ -487,10 +481,10 @@
     paint.setPen(mapper.getContrastingColour());
 
     int xorigin = m_xorigins[v];
-    int w = v->width() - xorigin - 1;
+    int w = v->getPaintWidth() - xorigin - 1;
     
-    paint.drawLine(xorigin, cursorPos.y(), v->width(), cursorPos.y());
-    paint.drawLine(cursorPos.x(), cursorPos.y(), cursorPos.x(), v->height());
+    paint.drawLine(xorigin, cursorPos.y(), v->getPaintWidth(), cursorPos.y());
+    paint.drawLine(cursorPos.x(), cursorPos.y(), cursorPos.x(), v->getPaintHeight());
     
     double fundamental = getFrequencyForX(cursorPos.x() - xorigin, w);
 
@@ -499,7 +493,7 @@
 
     v->drawVisibleText(paint,
                        cursorPos.x() + 2,
-                       v->height() - 2 - hoffset,
+                       v->getPaintHeight() - 2 - hoffset,
                        QString("%1 Hz").arg(fundamental),
                        View::OutlinedText);
 
@@ -507,7 +501,7 @@
         QString pitchLabel = Pitch::getPitchLabelForFrequency(fundamental);
         v->drawVisibleText(paint,
                            cursorPos.x() - paint.fontMetrics().width(pitchLabel) - 2,
-                           v->height() - 2 - hoffset,
+                           v->getPaintHeight() - 2 - hoffset,
                            pitchLabel,
                            View::OutlinedText);
     }
@@ -537,7 +531,7 @@
         int hx = int(lrint(getXForFrequency(fundamental * harmonic, w)));
         hx += xorigin;
 
-        if (hx < xorigin || hx > v->width()) break;
+        if (hx < xorigin || hx > v->getPaintWidth()) break;
         
         int len = 7;
 
@@ -561,7 +555,7 @@
 }
 
 QString
-SpectrumLayer::getFeatureDescription(View *v, QPoint &p) const
+SpectrumLayer::getFeatureDescription(LayerGeometryProvider *v, QPoint &p) const
 {
     if (!m_sliceableModel) return "";
 
@@ -650,7 +644,7 @@
 }
 
 void
-SpectrumLayer::paint(View *v, QPainter &paint, QRect rect) const
+SpectrumLayer::paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const
 {
     if (!m_originModel || !m_originModel->isOK() ||
         !m_originModel->isReady()) {
@@ -669,7 +663,7 @@
     double thresh = (pow(10, -6) / m_gain) * (m_windowSize / 2.0); // -60dB adj
 
     int xorigin = getVerticalScaleWidth(v, false, paint) + 1;
-    int w = v->width() - xorigin - 1;
+    int w = v->getPaintWidth() - xorigin - 1;
 
     int pkh = 0;
 //!!!    if (m_binScale == LogBins) {
@@ -729,7 +723,7 @@
             (void)getYForValue(values[bin], v, norm); // don't need return value, need norm
 
             paint.setPen(mapper.map(norm));
-            paint.drawLine(xorigin + x, 0, xorigin + x, v->height() - pkh - 1);
+            paint.drawLine(xorigin + x, 0, xorigin + x, v->getPaintHeight() - pkh - 1);
         }
 
         paint.restore();
@@ -749,7 +743,7 @@
 //    if (m_binScale == LogBins) {
 
 //        int pkh = 10;
-        int h = v->height();
+        int h = v->getPaintHeight();
 
         // piano keyboard
         //!!! should be in a new paintHorizontalScale()?
--- a/layer/SpectrumLayer.h	Mon Apr 13 13:52:05 2015 +0100
+++ b/layer/SpectrumLayer.h	Thu Aug 20 14:54:21 2015 +0100
@@ -39,13 +39,13 @@
     void setModel(DenseTimeValueModel *model);
     virtual const Model *getModel() const { return m_originModel; }
 
-    virtual bool getCrosshairExtents(View *, QPainter &, QPoint cursorPos,
+    virtual bool getCrosshairExtents(LayerGeometryProvider *, QPainter &, QPoint cursorPos,
                                      std::vector<QRect> &extents) const;
-    virtual void paintCrosshairs(View *, QPainter &, QPoint) const;
+    virtual void paintCrosshairs(LayerGeometryProvider *, QPainter &, QPoint) const;
 
-    virtual QString getFeatureDescription(View *v, QPoint &) const;
+    virtual QString getFeatureDescription(LayerGeometryProvider *v, QPoint &) const;
 
-    virtual void paint(View *v, QPainter &paint, QRect rect) const;
+    virtual void paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const;
 
     virtual VerticalPosition getPreferredFrameCountPosition() const {
 	return PositionTop;
@@ -67,16 +67,16 @@
     virtual bool getValueExtents(double &min, double &max,
                                  bool &logarithmic, QString &unit) const;
 
-    virtual bool getXScaleValue(const View *v, int x,
+    virtual bool getXScaleValue(const LayerGeometryProvider *v, int x,
                                 double &value, QString &unit) const;
 
-    virtual bool getYScaleValue(const View *, int y,
+    virtual bool getYScaleValue(const LayerGeometryProvider *, int y,
                                 double &value, QString &unit) const;
 
-    virtual bool getYScaleDifference(const View *, int y0, int y1,
+    virtual bool getYScaleDifference(const LayerGeometryProvider *, int y0, int y1,
                                      double &diff, QString &unit) const;
 
-    virtual bool isLayerScrollable(const View *) const { return false; }
+    virtual bool isLayerScrollable(const LayerGeometryProvider *) const { return false; }
 
     void setChannel(int);
     int getChannel() const { return m_channel; }
@@ -93,7 +93,7 @@
     void setShowPeaks(bool);
     bool getShowPeaks() const { return m_showPeaks; }
 
-    virtual int getVerticalScaleWidth(View *, bool, QPainter &) const { return 0; }
+    virtual int getVerticalScaleWidth(LayerGeometryProvider *, bool, QPainter &) const { return 0; }
 
     virtual void toXml(QTextStream &stream, QString indent = "",
                        QString extraAttributes = "") const;
--- a/layer/TextLayer.cpp	Mon Apr 13 13:52:05 2015 +0100
+++ b/layer/TextLayer.cpp	Thu Aug 20 14:54:21 2015 +0100
@@ -102,7 +102,7 @@
 }
 
 bool
-TextLayer::isLayerScrollable(const View *v) const
+TextLayer::isLayerScrollable(const LayerGeometryProvider *v) const
 {
     QPoint discard;
     return !v->shouldIlluminateLocalFeatures(this, discard);
@@ -110,12 +110,12 @@
 
 
 TextModel::PointList
-TextLayer::getLocalPoints(View *v, int x, int y) const
+TextLayer::getLocalPoints(LayerGeometryProvider *v, int x, int y) const
 {
     if (!m_model) return TextModel::PointList();
 
     sv_frame_t frame0 = v->getFrameForX(-150);
-    sv_frame_t frame1 = v->getFrameForX(v->width() + 150);
+    sv_frame_t frame1 = v->getFrameForX(v->getPaintWidth() + 150);
     
     TextModel::PointList points(m_model->getPoints(frame0, frame1));
 
@@ -139,9 +139,9 @@
 	    (QRect(0, 0, 150, 200),
 	     Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, label);
 
-	if (py + rect.height() > v->height()) {
-	    if (rect.height() > v->height()) py = 0;
-	    else py = v->height() - rect.height() - 1;
+	if (py + rect.height() > v->getPaintHeight()) {
+	    if (rect.height() > v->getPaintHeight()) py = 0;
+	    else py = v->getPaintHeight() - rect.height() - 1;
 	}
 
 	if (x >= px && x < px + rect.width() &&
@@ -154,7 +154,7 @@
 }
 
 bool
-TextLayer::getPointToDrag(View *v, int x, int y, TextModel::Point &p) const
+TextLayer::getPointToDrag(LayerGeometryProvider *v, int x, int y, TextModel::Point &p) const
 {
     if (!m_model) return false;
 
@@ -182,7 +182,7 @@
 }
 
 QString
-TextLayer::getFeatureDescription(View *v, QPoint &pos) const
+TextLayer::getFeatureDescription(LayerGeometryProvider *v, QPoint &pos) const
 {
     int x = pos.x();
 
@@ -220,7 +220,7 @@
 //!!! too much overlap with TimeValueLayer/TimeInstantLayer
 
 bool
-TextLayer::snapToFeatureFrame(View *v, sv_frame_t &frame,
+TextLayer::snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame,
 			      int &resolution,
 			      SnapType snap) const
 {
@@ -292,21 +292,21 @@
 }
 
 int
-TextLayer::getYForHeight(View *v, double height) const
+TextLayer::getYForHeight(LayerGeometryProvider *v, double height) const
 {
-    int h = v->height();
+    int h = v->getPaintHeight();
     return h - int(height * h);
 }
 
 double
-TextLayer::getHeightForY(View *v, int y) const
+TextLayer::getHeightForY(LayerGeometryProvider *v, int y) const
 {
-    int h = v->height();
+    int h = v->getPaintHeight();
     return double(h - y) / h;
 }
 
 void
-TextLayer::paint(View *v, QPainter &paint, QRect rect) const
+TextLayer::paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const
 {
     if (!m_model || !m_model->isOK()) return;
 
@@ -347,7 +347,7 @@
     int boxMaxHeight = 200;
 
     paint.save();
-    paint.setClipRect(rect.x(), 0, rect.width() + boxMaxWidth, v->height());
+    paint.setClipRect(rect.x(), 0, rect.width() + boxMaxWidth, v->getPaintHeight());
     
     for (TextModel::PointList::const_iterator i = points.begin();
 	 i != points.end(); ++i) {
@@ -380,9 +380,9 @@
 	QRect textRect = QRect(3, 2, boxRect.width(), boxRect.height());
 	boxRect = QRect(0, 0, boxRect.width() + 6, boxRect.height() + 2);
 
-	if (y + boxRect.height() > v->height()) {
-	    if (boxRect.height() > v->height()) y = 0;
-	    else y = v->height() - boxRect.height() - 1;
+	if (y + boxRect.height() > v->getPaintHeight()) {
+	    if (boxRect.height() > v->getPaintHeight()) y = 0;
+	    else y = v->getPaintHeight() - boxRect.height() - 1;
 	}
 
 	boxRect = QRect(x, y, boxRect.width(), boxRect.height());
@@ -411,7 +411,7 @@
 }
 
 void
-TextLayer::drawStart(View *v, QMouseEvent *e)
+TextLayer::drawStart(LayerGeometryProvider *v, QMouseEvent *e)
 {
 //    SVDEBUG << "TextLayer::drawStart(" << e->x() << "," << e->y() << ")" << endl;
 
@@ -437,7 +437,7 @@
 }
 
 void
-TextLayer::drawDrag(View *v, QMouseEvent *e)
+TextLayer::drawDrag(LayerGeometryProvider *v, QMouseEvent *e)
 {
 //    SVDEBUG << "TextLayer::drawDrag(" << e->x() << "," << e->y() << ")" << endl;
 
@@ -456,13 +456,13 @@
 }
 
 void
-TextLayer::drawEnd(View *v, QMouseEvent *)
+TextLayer::drawEnd(LayerGeometryProvider *v, QMouseEvent *)
 {
 //    SVDEBUG << "TextLayer::drawEnd(" << e->x() << "," << e->y() << ")" << endl;
     if (!m_model || !m_editing) return;
 
     bool ok = false;
-    QString label = QInputDialog::getText(v, tr("Enter label"),
+    QString label = QInputDialog::getText(v->getView(), tr("Enter label"),
 					  tr("Please enter a new label:"),
 					  QLineEdit::Normal, "", &ok);
 
@@ -480,7 +480,7 @@
 }
 
 void
-TextLayer::eraseStart(View *v, QMouseEvent *e)
+TextLayer::eraseStart(LayerGeometryProvider *v, QMouseEvent *e)
 {
     if (!m_model) return;
 
@@ -495,12 +495,12 @@
 }
 
 void
-TextLayer::eraseDrag(View *, QMouseEvent *)
+TextLayer::eraseDrag(LayerGeometryProvider *, QMouseEvent *)
 {
 }
 
 void
-TextLayer::eraseEnd(View *v, QMouseEvent *e)
+TextLayer::eraseEnd(LayerGeometryProvider *v, QMouseEvent *e)
 {
     if (!m_model || !m_editing) return;
 
@@ -521,7 +521,7 @@
 }
 
 void
-TextLayer::editStart(View *v, QMouseEvent *e)
+TextLayer::editStart(LayerGeometryProvider *v, QMouseEvent *e)
 {
 //    SVDEBUG << "TextLayer::editStart(" << e->x() << "," << e->y() << ")" << endl;
 
@@ -543,7 +543,7 @@
 }
 
 void
-TextLayer::editDrag(View *v, QMouseEvent *e)
+TextLayer::editDrag(LayerGeometryProvider *v, QMouseEvent *e)
 {
     if (!m_model || !m_editing) return;
 
@@ -570,7 +570,7 @@
 }
 
 void
-TextLayer::editEnd(View *, QMouseEvent *)
+TextLayer::editEnd(LayerGeometryProvider *, QMouseEvent *)
 {
 //    SVDEBUG << "TextLayer::editEnd(" << e->x() << "," << e->y() << ")" << endl;
     if (!m_model || !m_editing) return;
@@ -598,7 +598,7 @@
 }
 
 bool
-TextLayer::editOpen(View *v, QMouseEvent *e)
+TextLayer::editOpen(LayerGeometryProvider *v, QMouseEvent *e)
 {
     if (!m_model) return false;
 
@@ -608,7 +608,7 @@
     QString label = text.label;
 
     bool ok = false;
-    label = QInputDialog::getText(v, tr("Enter label"),
+    label = QInputDialog::getText(v->getView(), tr("Enter label"),
 				  tr("Please enter a new label:"),
 				  QLineEdit::Normal, label, &ok);
     if (ok && label != text.label) {
@@ -699,7 +699,7 @@
 }
 
 void
-TextLayer::copy(View *v, Selection s, Clipboard &to)
+TextLayer::copy(LayerGeometryProvider *v, Selection s, Clipboard &to)
 {
     if (!m_model) return;
 
@@ -717,7 +717,7 @@
 }
 
 bool
-TextLayer::paste(View *v, const Clipboard &from, sv_frame_t /* frameOffset */, bool /* interactive */)
+TextLayer::paste(LayerGeometryProvider *v, const Clipboard &from, sv_frame_t /* frameOffset */, bool /* interactive */)
 {
     if (!m_model) return false;
 
@@ -728,7 +728,7 @@
     if (clipboardHasDifferentAlignment(v, from)) {
 
         QMessageBox::StandardButton button =
-            QMessageBox::question(v, tr("Re-align pasted items?"),
+            QMessageBox::question(v->getView(), tr("Re-align pasted items?"),
                                   tr("The items you are pasting came from a layer with different source material from this one.  Do you want to re-align them in time, to match the source material for this layer?"),
                                   QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
                                   QMessageBox::Yes);
--- a/layer/TextLayer.h	Mon Apr 13 13:52:05 2015 +0100
+++ b/layer/TextLayer.h	Thu Aug 20 14:54:21 2015 +0100
@@ -32,35 +32,35 @@
 public:
     TextLayer();
 
-    virtual void paint(View *v, QPainter &paint, QRect rect) const;
+    virtual void paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const;
 
-    virtual QString getFeatureDescription(View *v, QPoint &) const;
+    virtual QString getFeatureDescription(LayerGeometryProvider *v, QPoint &) const;
 
-    virtual bool snapToFeatureFrame(View *v, sv_frame_t &frame,
+    virtual bool snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame,
 				    int &resolution,
 				    SnapType snap) const;
 
-    virtual void drawStart(View *v, QMouseEvent *);
-    virtual void drawDrag(View *v, QMouseEvent *);
-    virtual void drawEnd(View *v, QMouseEvent *);
+    virtual void drawStart(LayerGeometryProvider *v, QMouseEvent *);
+    virtual void drawDrag(LayerGeometryProvider *v, QMouseEvent *);
+    virtual void drawEnd(LayerGeometryProvider *v, QMouseEvent *);
 
-    virtual void eraseStart(View *v, QMouseEvent *);
-    virtual void eraseDrag(View *v, QMouseEvent *);
-    virtual void eraseEnd(View *v, QMouseEvent *);
+    virtual void eraseStart(LayerGeometryProvider *v, QMouseEvent *);
+    virtual void eraseDrag(LayerGeometryProvider *v, QMouseEvent *);
+    virtual void eraseEnd(LayerGeometryProvider *v, QMouseEvent *);
 
-    virtual void editStart(View *v, QMouseEvent *);
-    virtual void editDrag(View *v, QMouseEvent *);
-    virtual void editEnd(View *v, QMouseEvent *);
+    virtual void editStart(LayerGeometryProvider *v, QMouseEvent *);
+    virtual void editDrag(LayerGeometryProvider *v, QMouseEvent *);
+    virtual void editEnd(LayerGeometryProvider *v, QMouseEvent *);
 
     virtual void moveSelection(Selection s, sv_frame_t newStartFrame);
     virtual void resizeSelection(Selection s, Selection newSize);
     virtual void deleteSelection(Selection s);
 
-    virtual void copy(View *v, Selection s, Clipboard &to);
-    virtual bool paste(View *v, const Clipboard &from, sv_frame_t frameOffset,
+    virtual void copy(LayerGeometryProvider *v, Selection s, Clipboard &to);
+    virtual bool paste(LayerGeometryProvider *v, const Clipboard &from, sv_frame_t frameOffset,
                        bool interactive);
 
-    virtual bool editOpen(View *, QMouseEvent *); // on double-click
+    virtual bool editOpen(LayerGeometryProvider *, QMouseEvent *); // on double-click
 
     virtual const Model *getModel() const { return m_model; }
     void setModel(TextModel *model);
@@ -74,16 +74,16 @@
 					  int value) const;
     virtual void setProperty(const PropertyName &, int value);
 
-    virtual bool isLayerScrollable(const View *v) const;
+    virtual bool isLayerScrollable(const LayerGeometryProvider *v) const;
 
     virtual bool isLayerEditable() const { return true; }
 
-    virtual int getCompletion(View *) const { return m_model->getCompletion(); }
+    virtual int getCompletion(LayerGeometryProvider *) const { return m_model->getCompletion(); }
 
     virtual bool getValueExtents(double &min, double &max,
                                  bool &logarithmic, QString &unit) const;
 
-    virtual int getVerticalScaleWidth(View *, bool, QPainter &) const { return 0; }
+    virtual int getVerticalScaleWidth(LayerGeometryProvider *, bool, QPainter &) const { return 0; }
 
     virtual void toXml(QTextStream &stream, QString indent = "",
                        QString extraAttributes = "") const;
@@ -91,14 +91,14 @@
     void setProperties(const QXmlAttributes &attributes);
 
 protected:
-    int getYForHeight(View *v, double height) const;
-    double getHeightForY(View *v, int y) const;
+    int getYForHeight(LayerGeometryProvider *v, double height) const;
+    double getHeightForY(LayerGeometryProvider *v, int y) const;
 
     virtual int getDefaultColourHint(bool dark, bool &impose);
 
-    TextModel::PointList getLocalPoints(View *v, int x, int y) const;
+    TextModel::PointList getLocalPoints(LayerGeometryProvider *v, int x, int y) const;
 
-    bool getPointToDrag(View *v, int x, int y, TextModel::Point &) const;
+    bool getPointToDrag(LayerGeometryProvider *v, int x, int y, TextModel::Point &) const;
 
     TextModel *m_model;
     bool m_editing;
--- a/layer/TimeInstantLayer.cpp	Mon Apr 13 13:52:05 2015 +0100
+++ b/layer/TimeInstantLayer.cpp	Thu Aug 20 14:54:21 2015 +0100
@@ -147,14 +147,14 @@
 }
 
 bool
-TimeInstantLayer::isLayerScrollable(const View *v) const
+TimeInstantLayer::isLayerScrollable(const LayerGeometryProvider *v) const
 {
     QPoint discard;
     return !v->shouldIlluminateLocalFeatures(this, discard);
 }
 
 SparseOneDimensionalModel::PointList
-TimeInstantLayer::getLocalPoints(View *v, int x) const
+TimeInstantLayer::getLocalPoints(LayerGeometryProvider *v, int x) const
 {
     // Return a set of points that all have the same frame number, the
     // nearest to the given x coordinate, and that are within a
@@ -162,7 +162,7 @@
 
     if (!m_model) return SparseOneDimensionalModel::PointList();
 
-    long frame = v->getFrameForX(x);
+    sv_frame_t frame = v->getFrameForX(x);
 
     SparseOneDimensionalModel::PointList onPoints =
 	m_model->getPoints(frame);
@@ -213,7 +213,7 @@
 }
 
 QString
-TimeInstantLayer::getFeatureDescription(View *v, QPoint &pos) const
+TimeInstantLayer::getFeatureDescription(LayerGeometryProvider *v, QPoint &pos) const
 {
     int x = pos.x();
 
@@ -229,7 +229,7 @@
 	}
     }
 
-    long useFrame = points.begin()->frame;
+    sv_frame_t useFrame = points.begin()->frame;
 
     RealTime rt = RealTime::frame2RealTime(useFrame, m_model->getSampleRate());
     
@@ -249,7 +249,7 @@
 }
 
 bool
-TimeInstantLayer::snapToFeatureFrame(View *v, sv_frame_t &frame,
+TimeInstantLayer::snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame,
 				     int &resolution,
 				     SnapType snap) const
 {
@@ -321,7 +321,7 @@
 }
 
 void
-TimeInstantLayer::paint(View *v, QPainter &paint, QRect rect) const
+TimeInstantLayer::paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const
 {
     if (!m_model || !m_model->isOK()) return;
 
@@ -329,8 +329,8 @@
 
     int x0 = rect.left(), x1 = rect.right();
 
-    long frame0 = v->getFrameForX(x0);
-    long frame1 = v->getFrameForX(x1);
+    sv_frame_t frame0 = v->getFrameForX(x0);
+    sv_frame_t frame1 = v->getFrameForX(x1);
 
     SparseOneDimensionalModel::PointList points(m_model->getPoints
 						(frame0, frame1));
@@ -367,7 +367,7 @@
 //	      << m_model->getResolution() << " frames" << endl;
 
     QPoint localPos;
-    long illuminateFrame = -1;
+    sv_frame_t illuminateFrame = -1;
 
     if (v->shouldIlluminateLocalFeatures(this, localPos)) {
 	SparseOneDimensionalModel::PointList localPoints =
@@ -403,16 +403,16 @@
 	}
 		
 	if (p.frame == illuminateFrame) {
-	    paint.setPen(getForegroundQColor(v));
+	    paint.setPen(getForegroundQColor(v->getView()));
 	} else {
 	    paint.setPen(brushColour);
 	}
 
 	if (m_plotStyle == PlotInstants) {
 	    if (iw > 1) {
-		paint.drawRect(x, 0, iw - 1, v->height() - 1);
+		paint.drawRect(x, 0, iw - 1, v->getPaintHeight() - 1);
 	    } else {
-		paint.drawLine(x, 0, x, v->height() - 1);
+		paint.drawLine(x, 0, x, v->getPaintHeight() - 1);
 	    }
 	} else {
 
@@ -431,11 +431,11 @@
 	    if (nx >= x) {
 		
 		if (illuminateFrame != p.frame &&
-		    (nx < x + 5 || x >= v->width() - 1)) {
+		    (nx < x + 5 || x >= v->getPaintWidth() - 1)) {
 		    paint.setPen(Qt::NoPen);
 		}
 
-                paint.drawRect(x, -1, nx - x, v->height() + 1);
+                paint.drawRect(x, -1, nx - x, v->getPaintHeight() + 1);
 	    }
 
 	    odd = !odd;
@@ -466,7 +466,7 @@
 }
 
 void
-TimeInstantLayer::drawStart(View *v, QMouseEvent *e)
+TimeInstantLayer::drawStart(LayerGeometryProvider *v, QMouseEvent *e)
 {
 #ifdef DEBUG_TIME_INSTANT_LAYER
     cerr << "TimeInstantLayer::drawStart(" << e->x() << ")" << endl;
@@ -474,7 +474,7 @@
 
     if (!m_model) return;
 
-    long frame = v->getFrameForX(e->x());
+    sv_frame_t frame = v->getFrameForX(e->x());
     if (frame < 0) frame = 0;
     frame = frame / m_model->getResolution() * m_model->getResolution();
 
@@ -489,7 +489,7 @@
 }
 
 void
-TimeInstantLayer::drawDrag(View *v, QMouseEvent *e)
+TimeInstantLayer::drawDrag(LayerGeometryProvider *v, QMouseEvent *e)
 {
 #ifdef DEBUG_TIME_INSTANT_LAYER
     cerr << "TimeInstantLayer::drawDrag(" << e->x() << ")" << endl;
@@ -497,7 +497,7 @@
 
     if (!m_model || !m_editing) return;
 
-    long frame = v->getFrameForX(e->x());
+    sv_frame_t frame = v->getFrameForX(e->x());
     if (frame < 0) frame = 0;
     frame = frame / m_model->getResolution() * m_model->getResolution();
     m_editingCommand->deletePoint(m_editingPoint);
@@ -506,7 +506,7 @@
 }
 
 void
-TimeInstantLayer::drawEnd(View *, QMouseEvent *)
+TimeInstantLayer::drawEnd(LayerGeometryProvider *, QMouseEvent *)
 {
 #ifdef DEBUG_TIME_INSTANT_LAYER
     cerr << "TimeInstantLayer::drawEnd(" << e->x() << ")" << endl;
@@ -523,7 +523,7 @@
 }
 
 void
-TimeInstantLayer::eraseStart(View *v, QMouseEvent *e)
+TimeInstantLayer::eraseStart(LayerGeometryProvider *v, QMouseEvent *e)
 {
     if (!m_model) return;
 
@@ -541,12 +541,12 @@
 }
 
 void
-TimeInstantLayer::eraseDrag(View *, QMouseEvent *)
+TimeInstantLayer::eraseDrag(LayerGeometryProvider *, QMouseEvent *)
 {
 }
 
 void
-TimeInstantLayer::eraseEnd(View *v, QMouseEvent *e)
+TimeInstantLayer::eraseEnd(LayerGeometryProvider *v, QMouseEvent *e)
 {
     if (!m_model || !m_editing) return;
 
@@ -567,7 +567,7 @@
 }
 
 void
-TimeInstantLayer::editStart(View *v, QMouseEvent *e)
+TimeInstantLayer::editStart(LayerGeometryProvider *v, QMouseEvent *e)
 {
 #ifdef DEBUG_TIME_INSTANT_LAYER
     cerr << "TimeInstantLayer::editStart(" << e->x() << ")" << endl;
@@ -589,7 +589,7 @@
 }
 
 void
-TimeInstantLayer::editDrag(View *v, QMouseEvent *e)
+TimeInstantLayer::editDrag(LayerGeometryProvider *v, QMouseEvent *e)
 {
 #ifdef DEBUG_TIME_INSTANT_LAYER
     cerr << "TimeInstantLayer::editDrag(" << e->x() << ")" << endl;
@@ -597,7 +597,7 @@
 
     if (!m_model || !m_editing) return;
 
-    long frame = v->getFrameForX(e->x());
+    sv_frame_t frame = v->getFrameForX(e->x());
     if (frame < 0) frame = 0;
     frame = frame / m_model->getResolution() * m_model->getResolution();
 
@@ -612,7 +612,7 @@
 }
 
 void
-TimeInstantLayer::editEnd(View *, QMouseEvent *)
+TimeInstantLayer::editEnd(LayerGeometryProvider *, QMouseEvent *)
 {
 #ifdef DEBUG_TIME_INSTANT_LAYER
     cerr << "TimeInstantLayer::editEnd(" << e->x() << ")" << endl;
@@ -631,7 +631,7 @@
 }
 
 bool
-TimeInstantLayer::editOpen(View *v, QMouseEvent *e)
+TimeInstantLayer::editOpen(LayerGeometryProvider *v, QMouseEvent *e)
 {
     if (!m_model) return false;
 
@@ -747,7 +747,7 @@
 }
 
 void
-TimeInstantLayer::copy(View *v, Selection s, Clipboard &to)
+TimeInstantLayer::copy(LayerGeometryProvider *v, Selection s, Clipboard &to)
 {
     if (!m_model) return;
 
@@ -765,7 +765,7 @@
 }
 
 bool
-TimeInstantLayer::paste(View *v, const Clipboard &from, sv_frame_t frameOffset, bool)
+TimeInstantLayer::paste(LayerGeometryProvider *v, const Clipboard &from, sv_frame_t frameOffset, bool)
 {
     if (!m_model) return false;
 
@@ -776,7 +776,7 @@
     if (clipboardHasDifferentAlignment(v, from)) {
 
         QMessageBox::StandardButton button =
-            QMessageBox::question(v, tr("Re-align pasted instants?"),
+            QMessageBox::question(v->getView(), tr("Re-align pasted instants?"),
                                   tr("The instants you are pasting came from a layer with different source material from this one.  Do you want to re-align them in time, to match the source material for this layer?"),
                                   QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
                                   QMessageBox::Yes);
--- a/layer/TimeInstantLayer.h	Mon Apr 13 13:52:05 2015 +0100
+++ b/layer/TimeInstantLayer.h	Thu Aug 20 14:54:21 2015 +0100
@@ -33,35 +33,35 @@
     TimeInstantLayer();
     virtual ~TimeInstantLayer();
 
-    virtual void paint(View *v, QPainter &paint, QRect rect) const;
+    virtual void paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const;
 
     virtual QString getLabelPreceding(sv_frame_t) const;
-    virtual QString getFeatureDescription(View *v, QPoint &) const;
+    virtual QString getFeatureDescription(LayerGeometryProvider *v, QPoint &) const;
 
-    virtual bool snapToFeatureFrame(View *v, sv_frame_t &frame,
+    virtual bool snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame,
 				    int &resolution,
 				    SnapType snap) const;
 
-    virtual void drawStart(View *v, QMouseEvent *);
-    virtual void drawDrag(View *v, QMouseEvent *);
-    virtual void drawEnd(View *v, QMouseEvent *);
+    virtual void drawStart(LayerGeometryProvider *v, QMouseEvent *);
+    virtual void drawDrag(LayerGeometryProvider *v, QMouseEvent *);
+    virtual void drawEnd(LayerGeometryProvider *v, QMouseEvent *);
 
-    virtual void eraseStart(View *v, QMouseEvent *);
-    virtual void eraseDrag(View *v, QMouseEvent *);
-    virtual void eraseEnd(View *v, QMouseEvent *);
+    virtual void eraseStart(LayerGeometryProvider *v, QMouseEvent *);
+    virtual void eraseDrag(LayerGeometryProvider *v, QMouseEvent *);
+    virtual void eraseEnd(LayerGeometryProvider *v, QMouseEvent *);
 
-    virtual void editStart(View *v, QMouseEvent *);
-    virtual void editDrag(View *v, QMouseEvent *);
-    virtual void editEnd(View *v, QMouseEvent *);
+    virtual void editStart(LayerGeometryProvider *v, QMouseEvent *);
+    virtual void editDrag(LayerGeometryProvider *v, QMouseEvent *);
+    virtual void editEnd(LayerGeometryProvider *v, QMouseEvent *);
 
-    virtual bool editOpen(View *, QMouseEvent *);
+    virtual bool editOpen(LayerGeometryProvider *, QMouseEvent *);
 
     virtual void moveSelection(Selection s, sv_frame_t newStartFrame);
     virtual void resizeSelection(Selection s, Selection newSize);
     virtual void deleteSelection(Selection s);
 
-    virtual void copy(View *v, Selection s, Clipboard &to);
-    virtual bool paste(View *v, const Clipboard &from, sv_frame_t frameOffset,
+    virtual void copy(LayerGeometryProvider *v, Selection s, Clipboard &to);
+    virtual bool paste(LayerGeometryProvider *v, const Clipboard &from, sv_frame_t frameOffset,
                        bool interactive);
 
     virtual const Model *getModel() const { return m_model; }
@@ -84,11 +84,11 @@
     void setPlotStyle(PlotStyle style);
     PlotStyle getPlotStyle() const { return m_plotStyle; }
 
-    virtual bool isLayerScrollable(const View *v) const;
+    virtual bool isLayerScrollable(const LayerGeometryProvider *v) const;
 
     virtual bool isLayerEditable() const { return true; }
 
-    virtual int getCompletion(View *) const { return m_model->getCompletion(); }
+    virtual int getCompletion(LayerGeometryProvider *) const { return m_model->getCompletion(); }
 
     virtual bool needsTextLabelHeight() const { return m_model->hasTextLabels(); }
 
@@ -109,14 +109,14 @@
         }
     }
 
-    virtual int getVerticalScaleWidth(View *, bool, QPainter &) const { return 0; }
+    virtual int getVerticalScaleWidth(LayerGeometryProvider *, bool, QPainter &) const { return 0; }
 
 protected:
-    SparseOneDimensionalModel::PointList getLocalPoints(View *v, int) const;
+    SparseOneDimensionalModel::PointList getLocalPoints(LayerGeometryProvider *v, int) const;
 
     virtual int getDefaultColourHint(bool dark, bool &impose);
 
-    bool clipboardAlignmentDiffers(View *v, const Clipboard &) const;
+    bool clipboardAlignmentDiffers(LayerGeometryProvider *v, const Clipboard &) const;
 
     SparseOneDimensionalModel *m_model;
     bool m_editing;
--- a/layer/TimeRulerLayer.cpp	Mon Apr 13 13:52:05 2015 +0100
+++ b/layer/TimeRulerLayer.cpp	Thu Aug 20 14:54:21 2015 +0100
@@ -49,7 +49,7 @@
 }
 
 bool
-TimeRulerLayer::snapToFeatureFrame(View *v, sv_frame_t &frame,
+TimeRulerLayer::snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame,
                                    int &resolution, SnapType snap) const
 {
     if (!m_model) {
@@ -88,7 +88,7 @@
         
     case SnapNearest:
     {
-        if (labs(frame - left) > labs(right - frame)) {
+        if (llabs(frame - left) > llabs(right - frame)) {
             frame = right;
         } else {
             frame = left;
@@ -141,7 +141,7 @@
 }
 
 int
-TimeRulerLayer::getMajorTickSpacing(View *v, bool &quarterTicks) const
+TimeRulerLayer::getMajorTickSpacing(LayerGeometryProvider *v, bool &quarterTicks) const
 {
     // return value is in milliseconds
 
@@ -158,7 +158,7 @@
     RealTime rtStart = RealTime::frame2RealTime(startFrame, sampleRate);
     RealTime rtEnd = RealTime::frame2RealTime(endFrame, sampleRate);
 
-    int count = v->width() / minPixelSpacing;
+    int count = v->getPaintWidth() / minPixelSpacing;
     if (count < 1) count = 1;
     RealTime rtGap = (rtEnd - rtStart) / count;
 
@@ -192,7 +192,7 @@
 }
 
 void
-TimeRulerLayer::paint(View *v, QPainter &paint, QRect rect) const
+TimeRulerLayer::paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const
 {
 #ifdef DEBUG_TIME_RULER_LAYER
     SVDEBUG << "TimeRulerLayer::paint (" << rect.x() << "," << rect.y()
@@ -287,11 +287,11 @@
             }
 
             paint.setPen(greyColour);
-            paint.drawLine(x, 0, x, v->height());
+            paint.drawLine(x, 0, x, v->getPaintHeight());
 
             paint.setPen(getBaseQColor());
             paint.drawLine(x, 0, x, 5);
-            paint.drawLine(x, v->height() - 6, x, v->height() - 1);
+            paint.drawLine(x, v->getPaintHeight() - 6, x, v->getPaintHeight() - 1);
 
             int y;
             switch (m_labelHeight) {
@@ -300,16 +300,16 @@
                 y = 6 + metrics.ascent();
                 break;
             case LabelMiddle:
-                y = v->height() / 2 - metrics.height() / 2 + metrics.ascent();
+                y = v->getPaintHeight() / 2 - metrics.height() / 2 + metrics.ascent();
                 break;
             case LabelBottom:
-                y = v->height() - metrics.height() + metrics.ascent() - 6;
+                y = v->getPaintHeight() - metrics.height() + metrics.ascent() - 6;
             }
 
             if (v->getViewManager() && v->getViewManager()->getOverlayMode() !=
                 ViewManager::NoOverlays) {
 
-                if (v->getLayer(0) == this) {
+                if (v->getView()->getLayer(0) == this) {
                     // backmost layer, don't worry about outlining the text
                     paint.drawText(x+2 - tw/2, y, text);
                 } else {
@@ -344,14 +344,14 @@
 	    if (ticks == 10) {
 		if ((i % 2) == 1) {
 		    if (i == 5) {
-			paint.drawLine(x, 0, x, v->height());
+			paint.drawLine(x, 0, x, v->getPaintHeight());
 		    } else sz = 3;
 		} else {
 		    sz = 7;
 		}
 	    }
 	    paint.drawLine(x, 0, x, sz);
-	    paint.drawLine(x, v->height() - sz - 1, x, v->height() - 1);
+	    paint.drawLine(x, v->getPaintHeight() - sz - 1, x, v->getPaintHeight() - 1);
 	}
 
 	ms += incms;
--- a/layer/TimeRulerLayer.h	Mon Apr 13 13:52:05 2015 +0100
+++ b/layer/TimeRulerLayer.h	Thu Aug 20 14:54:21 2015 +0100
@@ -32,7 +32,7 @@
 public:
     TimeRulerLayer();
 
-    virtual void paint(View *v, QPainter &paint, QRect rect) const;
+    virtual void paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const;
 
     void setModel(Model *);
     virtual const Model *getModel() const { return m_model; }
@@ -41,7 +41,7 @@
     void setLabelHeight(LabelHeight h) { m_labelHeight = h; }
     LabelHeight getLabelHeight() const { return m_labelHeight; }
 
-    virtual bool snapToFeatureFrame(View *, sv_frame_t &, int &, SnapType) const;
+    virtual bool snapToFeatureFrame(LayerGeometryProvider *, sv_frame_t &, int &, SnapType) const;
 
     virtual ColourSignificance getLayerColourSignificance() const {
         return ColourIrrelevant;
@@ -53,20 +53,22 @@
 
     virtual QString getLayerPresentationName() const;
 
-    virtual int getVerticalScaleWidth(View *, bool, QPainter &) const { return 0; }
+    virtual int getVerticalScaleWidth(LayerGeometryProvider *, bool, QPainter &) const { return 0; }
 
     virtual void toXml(QTextStream &stream, QString indent = "",
                        QString extraAttributes = "") const;
 
     void setProperties(const QXmlAttributes &attributes);
 
+    virtual bool canExistWithoutModel() const { return true; }
+
 protected:
     Model *m_model;
     LabelHeight m_labelHeight;
 
     virtual int getDefaultColourHint(bool dark, bool &impose);
 
-    int getMajorTickSpacing(View *, bool &quarterTicks) const;
+    int getMajorTickSpacing(LayerGeometryProvider *, bool &quarterTicks) const;
 };
 
 #endif
--- a/layer/TimeValueLayer.cpp	Mon Apr 13 13:52:05 2015 +0100
+++ b/layer/TimeValueLayer.cpp	Thu Aug 20 14:54:21 2015 +0100
@@ -20,6 +20,7 @@
 #include "base/Profiler.h"
 #include "base/LogRange.h"
 #include "base/RangeMapper.h"
+#include "base/Pitch.h"
 #include "ColourDatabase.h"
 #include "view/View.h"
 
@@ -315,7 +316,7 @@
 }
 
 bool
-TimeValueLayer::isLayerScrollable(const View *v) const
+TimeValueLayer::isLayerScrollable(const LayerGeometryProvider *v) const
 {
     // We don't illuminate sections in the line or curve modes, so
     // they're always scrollable
@@ -529,7 +530,7 @@
 }
 
 SparseTimeValueModel::PointList
-TimeValueLayer::getLocalPoints(View *v, int x) const
+TimeValueLayer::getLocalPoints(LayerGeometryProvider *v, int x) const
 {
     if (!m_model) return SparseTimeValueModel::PointList();
 
@@ -586,7 +587,7 @@
 }
 
 QString
-TimeValueLayer::getFeatureDescription(View *v, QPoint &pos) const
+TimeValueLayer::getFeatureDescription(LayerGeometryProvider *v, QPoint &pos) const
 {
     int x = pos.x();
 
@@ -606,20 +607,31 @@
 
     RealTime rt = RealTime::frame2RealTime(useFrame, m_model->getSampleRate());
     
+    QString valueText;
+    float value = points.begin()->value;
+    QString unit = getScaleUnits();
+
+    if (unit == "Hz") {
+        valueText = tr("%1 Hz (%2, %3)")
+            .arg(value)
+            .arg(Pitch::getPitchLabelForFrequency(value))
+            .arg(Pitch::getPitchForFrequency(value));
+    } else if (unit != "") {
+        valueText = tr("%1 %2").arg(value).arg(unit);
+    } else {
+        valueText = tr("%1").arg(value);
+    }
+    
     QString text;
-    QString unit = getScaleUnits();
-    if (unit != "") unit = " " + unit;
 
     if (points.begin()->label == "") {
-	text = QString(tr("Time:\t%1\nValue:\t%2%3\nNo label"))
+	text = QString(tr("Time:\t%1\nValue:\t%2\nNo label"))
 	    .arg(rt.toText(true).c_str())
-	    .arg(points.begin()->value)
-            .arg(unit);
+	    .arg(valueText);
     } else {
-	text = QString(tr("Time:\t%1\nValue:\t%2%3\nLabel:\t%4"))
+	text = QString(tr("Time:\t%1\nValue:\t%2\nLabel:\t%4"))
 	    .arg(rt.toText(true).c_str())
-	    .arg(points.begin()->value)
-            .arg(unit)
+	    .arg(valueText)
 	    .arg(points.begin()->label);
     }
 
@@ -629,7 +641,7 @@
 }
 
 bool
-TimeValueLayer::snapToFeatureFrame(View *v, sv_frame_t &frame,
+TimeValueLayer::snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame,
 				   int &resolution,
 				   SnapType snap) const
 {
@@ -701,7 +713,7 @@
 }
 
 bool
-TimeValueLayer::snapToSimilarFeature(View *v, sv_frame_t &frame,
+TimeValueLayer::snapToSimilarFeature(LayerGeometryProvider *v, sv_frame_t &frame,
                                      int &resolution,
                                      SnapType snap) const
 {
@@ -782,7 +794,7 @@
 }
 
 void
-TimeValueLayer::getScaleExtents(View *v, double &min, double &max, bool &log) const
+TimeValueLayer::getScaleExtents(LayerGeometryProvider *v, double &min, double &max, bool &log) const
 {
     min = 0.0;
     max = 0.0;
@@ -818,11 +830,11 @@
 }
 
 int
-TimeValueLayer::getYForValue(View *v, double val) const
+TimeValueLayer::getYForValue(LayerGeometryProvider *v, double val) const
 {
     double min = 0.0, max = 0.0;
     bool logarithmic = false;
-    int h = v->height();
+    int h = v->getPaintHeight();
 
     getScaleExtents(v, min, max, logarithmic);
 
@@ -839,11 +851,11 @@
 }
 
 double
-TimeValueLayer::getValueForY(View *v, int y) const
+TimeValueLayer::getValueForY(LayerGeometryProvider *v, int y) const
 {
     double min = 0.0, max = 0.0;
     bool logarithmic = false;
-    int h = v->height();
+    int h = v->getPaintHeight();
 
     getScaleExtents(v, min, max, logarithmic);
 
@@ -865,7 +877,7 @@
 }
 
 QColor
-TimeValueLayer::getColourForValue(View *v, double val) const
+TimeValueLayer::getColourForValue(LayerGeometryProvider *v, double val) const
 {
     double min, max;
     bool log;
@@ -896,7 +908,7 @@
 }
 
 void
-TimeValueLayer::paint(View *v, QPainter &paint, QRect rect) const
+TimeValueLayer::paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const
 {
     if (!m_model || !m_model->isOK()) return;
 
@@ -931,8 +943,8 @@
     double max = m_model->getValueMaximum();
     if (max == min) max = min + 1.0;
 
-    int origin = int(nearbyint(v->height() -
-			       (-min * v->height()) / (max - min)));
+    int origin = int(nearbyint(v->getPaintHeight() -
+			       (-min * v->getPaintHeight()) / (max - min)));
 
     QPoint localPos;
     sv_frame_t illuminateFrame = -1;
@@ -966,7 +978,7 @@
         textY = v->getTextLabelHeight(this, paint);
     } else {
         int originY = getYForValue(v, 0.f);
-        if (originY > 0 && originY < v->height()) {
+        if (originY > 0 && originY < v->getPaintHeight()) {
             paint.save();
             paint.setPen(getPartialShades(v)[1]);
             paint.drawLine(x0, originY, x1, originY);
@@ -1171,12 +1183,12 @@
             if (!illuminate) {
                 if (!m_drawSegmentDivisions ||
                     nx < x + 5 ||
-                    x >= v->width() - 1) {
+                    x >= v->getPaintWidth() - 1) {
                     paint.setPen(Qt::NoPen);
                 }
 	    }
 
-	    paint.drawRect(x, -1, nx - x, v->height() + 1);
+	    paint.drawRect(x, -1, nx - x, v->getPaintHeight() + 1);
 	}
 
         if (v->shouldShowFeatureLabels()) {
@@ -1219,7 +1231,7 @@
 	paint.drawPath(path);
     } else if ((m_plotStyle == PlotCurve || m_plotStyle == PlotLines)
                && !path.isEmpty()) {
-	paint.setRenderHint(QPainter::Antialiasing, pointCount <= v->width());
+	paint.setRenderHint(QPainter::Antialiasing, pointCount <= v->getPaintWidth());
 	paint.drawPath(path);
     }
 
@@ -1230,7 +1242,7 @@
 }
 
 int
-TimeValueLayer::getVerticalScaleWidth(View *v, bool, QPainter &paint) const
+TimeValueLayer::getVerticalScaleWidth(LayerGeometryProvider *v, bool, QPainter &paint) const
 {
     if (!m_model || shouldAutoAlign()) {
         return 0;
@@ -1250,7 +1262,7 @@
 }
 
 void
-TimeValueLayer::paintVerticalScale(View *v, bool, QPainter &paint, QRect) const
+TimeValueLayer::paintVerticalScale(LayerGeometryProvider *v, bool, QPainter &paint, QRect) const
 {
     if (!m_model || m_model->getPoints().empty()) return;
 
@@ -1259,7 +1271,7 @@
     bool logarithmic;
 
     int w = getVerticalScaleWidth(v, false, paint);
-    int h = v->height();
+    int h = v->getPaintHeight();
 
     if (m_plotStyle == PlotSegmentation) {
 
@@ -1302,7 +1314,7 @@
 }
 
 void
-TimeValueLayer::drawStart(View *v, QMouseEvent *e)
+TimeValueLayer::drawStart(LayerGeometryProvider *v, QMouseEvent *e)
 {
 #ifdef DEBUG_TIME_VALUE_LAYER
     cerr << "TimeValueLayer::drawStart(" << e->x() << "," << e->y() << ")" << endl;
@@ -1352,7 +1364,7 @@
 }
 
 void
-TimeValueLayer::drawDrag(View *v, QMouseEvent *e)
+TimeValueLayer::drawDrag(LayerGeometryProvider *v, QMouseEvent *e)
 {
 #ifdef DEBUG_TIME_VALUE_LAYER
     cerr << "TimeValueLayer::drawDrag(" << e->x() << "," << e->y() << ")" << endl;
@@ -1414,7 +1426,7 @@
 }
 
 void
-TimeValueLayer::drawEnd(View *, QMouseEvent *)
+TimeValueLayer::drawEnd(LayerGeometryProvider *, QMouseEvent *)
 {
 #ifdef DEBUG_TIME_VALUE_LAYER
     cerr << "TimeValueLayer::drawEnd" << endl;
@@ -1426,7 +1438,7 @@
 }
 
 void
-TimeValueLayer::eraseStart(View *v, QMouseEvent *e)
+TimeValueLayer::eraseStart(LayerGeometryProvider *v, QMouseEvent *e)
 {
     if (!m_model) return;
 
@@ -1444,12 +1456,12 @@
 }
 
 void
-TimeValueLayer::eraseDrag(View *, QMouseEvent *)
+TimeValueLayer::eraseDrag(LayerGeometryProvider *, QMouseEvent *)
 {
 }
 
 void
-TimeValueLayer::eraseEnd(View *v, QMouseEvent *e)
+TimeValueLayer::eraseEnd(LayerGeometryProvider *v, QMouseEvent *e)
 {
     if (!m_model || !m_editing) return;
 
@@ -1471,7 +1483,7 @@
 }
 
 void
-TimeValueLayer::editStart(View *v, QMouseEvent *e)
+TimeValueLayer::editStart(LayerGeometryProvider *v, QMouseEvent *e)
 {
 #ifdef DEBUG_TIME_VALUE_LAYER
     cerr << "TimeValueLayer::editStart(" << e->x() << "," << e->y() << ")" << endl;
@@ -1494,7 +1506,7 @@
 }
 
 void
-TimeValueLayer::editDrag(View *v, QMouseEvent *e)
+TimeValueLayer::editDrag(LayerGeometryProvider *v, QMouseEvent *e)
 {
 #ifdef DEBUG_TIME_VALUE_LAYER
     cerr << "TimeValueLayer::editDrag(" << e->x() << "," << e->y() << ")" << endl;
@@ -1520,7 +1532,7 @@
 }
 
 void
-TimeValueLayer::editEnd(View *, QMouseEvent *)
+TimeValueLayer::editEnd(LayerGeometryProvider *, QMouseEvent *)
 {
 #ifdef DEBUG_TIME_VALUE_LAYER
     cerr << "TimeValueLayer::editEnd" << endl;
@@ -1550,7 +1562,7 @@
 }
 
 bool
-TimeValueLayer::editOpen(View *v, QMouseEvent *e)
+TimeValueLayer::editOpen(LayerGeometryProvider *v, QMouseEvent *e)
 {
     if (!m_model) return false;
 
@@ -1673,7 +1685,7 @@
 }    
 
 void
-TimeValueLayer::copy(View *v, Selection s, Clipboard &to)
+TimeValueLayer::copy(LayerGeometryProvider *v, Selection s, Clipboard &to)
 {
     if (!m_model) return;
 
@@ -1691,7 +1703,7 @@
 }
 
 bool
-TimeValueLayer::paste(View *v, const Clipboard &from, sv_frame_t /* frameOffset */,
+TimeValueLayer::paste(LayerGeometryProvider *v, const Clipboard &from, sv_frame_t /* frameOffset */,
                       bool interactive)
 {
     if (!m_model) return false;
@@ -1703,7 +1715,7 @@
     if (clipboardHasDifferentAlignment(v, from)) {
 
         QMessageBox::StandardButton button =
-            QMessageBox::question(v, tr("Re-align pasted items?"),
+            QMessageBox::question(v->getView(), tr("Re-align pasted items?"),
                                   tr("The items you are pasting came from a layer with different source material from this one.  Do you want to re-align them in time, to match the source material for this layer?"),
                                   QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
                                   QMessageBox::Yes);
--- a/layer/TimeValueLayer.h	Mon Apr 13 13:52:05 2015 +0100
+++ b/layer/TimeValueLayer.h	Thu Aug 20 14:54:21 2015 +0100
@@ -37,41 +37,41 @@
 public:
     TimeValueLayer();
 
-    virtual void paint(View *v, QPainter &paint, QRect rect) const;
+    virtual void paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const;
 
-    virtual int getVerticalScaleWidth(View *v, bool, QPainter &) const;
-    virtual void paintVerticalScale(View *v, bool, QPainter &paint, QRect rect) const;
+    virtual int getVerticalScaleWidth(LayerGeometryProvider *v, bool, QPainter &) const;
+    virtual void paintVerticalScale(LayerGeometryProvider *v, bool, QPainter &paint, QRect rect) const;
 
-    virtual QString getFeatureDescription(View *v, QPoint &) const;
+    virtual QString getFeatureDescription(LayerGeometryProvider *v, QPoint &) const;
     virtual QString getLabelPreceding(sv_frame_t) const;
 
-    virtual bool snapToFeatureFrame(View *v, sv_frame_t &frame,
+    virtual bool snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame,
 				    int &resolution,
 				    SnapType snap) const;
-    virtual bool snapToSimilarFeature(View *v, sv_frame_t &frame,
+    virtual bool snapToSimilarFeature(LayerGeometryProvider *v, sv_frame_t &frame,
                                       int &resolution,
                                       SnapType snap) const;
 
-    virtual void drawStart(View *v, QMouseEvent *);
-    virtual void drawDrag(View *v, QMouseEvent *);
-    virtual void drawEnd(View *v, QMouseEvent *);
+    virtual void drawStart(LayerGeometryProvider *v, QMouseEvent *);
+    virtual void drawDrag(LayerGeometryProvider *v, QMouseEvent *);
+    virtual void drawEnd(LayerGeometryProvider *v, QMouseEvent *);
 
-    virtual void eraseStart(View *v, QMouseEvent *);
-    virtual void eraseDrag(View *v, QMouseEvent *);
-    virtual void eraseEnd(View *v, QMouseEvent *);
+    virtual void eraseStart(LayerGeometryProvider *v, QMouseEvent *);
+    virtual void eraseDrag(LayerGeometryProvider *v, QMouseEvent *);
+    virtual void eraseEnd(LayerGeometryProvider *v, QMouseEvent *);
 
-    virtual void editStart(View *v, QMouseEvent *);
-    virtual void editDrag(View *v, QMouseEvent *);
-    virtual void editEnd(View *v, QMouseEvent *);
+    virtual void editStart(LayerGeometryProvider *v, QMouseEvent *);
+    virtual void editDrag(LayerGeometryProvider *v, QMouseEvent *);
+    virtual void editEnd(LayerGeometryProvider *v, QMouseEvent *);
 
-    virtual bool editOpen(View *v, QMouseEvent *);
+    virtual bool editOpen(LayerGeometryProvider *v, QMouseEvent *);
 
     virtual void moveSelection(Selection s, sv_frame_t newStartFrame);
     virtual void resizeSelection(Selection s, Selection newSize);
     virtual void deleteSelection(Selection s);
 
-    virtual void copy(View *v, Selection s, Clipboard &to);
-    virtual bool paste(View *v, const Clipboard &from, sv_frame_t frameOffset,
+    virtual void copy(LayerGeometryProvider *v, Selection s, Clipboard &to);
+    virtual bool paste(LayerGeometryProvider *v, const Clipboard &from, sv_frame_t frameOffset,
                        bool interactive);
 
     virtual const Model *getModel() const { return m_model; }
@@ -120,11 +120,11 @@
     void setShowDerivative(bool);
     bool getShowDerivative() const { return m_derivative; }
 
-    virtual bool isLayerScrollable(const View *v) const;
+    virtual bool isLayerScrollable(const LayerGeometryProvider *v) const;
 
     virtual bool isLayerEditable() const { return true; }
 
-    virtual int getCompletion(View *) const { return m_model->getCompletion(); }
+    virtual int getCompletion(LayerGeometryProvider *) const { return m_model->getCompletion(); }
 
     virtual bool needsTextLabelHeight() const {
         return m_plotStyle == PlotSegmentation && m_model->hasTextLabels();
@@ -155,16 +155,16 @@
     }
 
     /// VerticalScaleLayer and ColourScaleLayer methods
-    virtual int getYForValue(View *, double value) const;
-    virtual double getValueForY(View *, int y) const;
+    virtual int getYForValue(LayerGeometryProvider *, double value) const;
+    virtual double getValueForY(LayerGeometryProvider *, int y) const;
     virtual QString getScaleUnits() const;
-    virtual QColor getColourForValue(View *v, double value) const;
+    virtual QColor getColourForValue(LayerGeometryProvider *v, double value) const;
 
 protected:
-    void getScaleExtents(View *, double &min, double &max, bool &log) const;
+    void getScaleExtents(LayerGeometryProvider *, double &min, double &max, bool &log) const;
     bool shouldAutoAlign() const;
 
-    SparseTimeValueModel::PointList getLocalPoints(View *v, int) const;
+    SparseTimeValueModel::PointList getLocalPoints(LayerGeometryProvider *v, int) const;
 
     virtual int getDefaultColourHint(bool dark, bool &impose);
 
--- a/layer/VerticalScaleLayer.h	Mon Apr 13 13:52:05 2015 +0100
+++ b/layer/VerticalScaleLayer.h	Thu Aug 20 14:54:21 2015 +0100
@@ -19,8 +19,8 @@
 class VerticalScaleLayer
 {
 public:
-    virtual int getYForValue(View *, double value) const = 0;
-    virtual double getValueForY(View *, int y) const = 0;
+    virtual int getYForValue(LayerGeometryProvider *, double value) const = 0;
+    virtual double getValueForY(LayerGeometryProvider *, int y) const = 0;
     virtual QString getScaleUnits() const = 0;
 };
 
--- a/layer/WaveformLayer.cpp	Mon Apr 13 13:52:05 2015 +0100
+++ b/layer/WaveformLayer.cpp	Thu Aug 20 14:54:21 2015 +0100
@@ -326,7 +326,7 @@
 }
 
 int
-WaveformLayer::getCompletion(View *) const
+WaveformLayer::getCompletion(LayerGeometryProvider *) const
 {
     int completion = 100;
     if (!m_model || !m_model->isOK()) return completion;
@@ -399,7 +399,7 @@
 }    
 
 bool
-WaveformLayer::isLayerScrollable(const View *) const
+WaveformLayer::isLayerScrollable(const LayerGeometryProvider *) const
 {
     return !m_autoNormalize;
 }
@@ -408,7 +408,7 @@
                             -5, -3, -2, -1, -0.5, 0 };
 
 bool
-WaveformLayer::getSourceFramesForX(View *v, int x, int modelZoomLevel,
+WaveformLayer::getSourceFramesForX(LayerGeometryProvider *v, int x, int modelZoomLevel,
                                    sv_frame_t &f0, sv_frame_t &f1) const
 {
     sv_frame_t viewFrame = v->getFrameForX(x);
@@ -433,7 +433,7 @@
 }
 
 float
-WaveformLayer::getNormalizeGain(View *v, int channel) const
+WaveformLayer::getNormalizeGain(LayerGeometryProvider *v, int channel) const
 {
     sv_frame_t startFrame = v->getStartFrame();
     sv_frame_t endFrame = v->getEndFrame();
@@ -473,7 +473,7 @@
 }
 
 void
-WaveformLayer::paint(View *v, QPainter &viewPainter, QRect rect) const
+WaveformLayer::paint(LayerGeometryProvider *v, QPainter &viewPainter, QRect rect) const
 {
     if (!m_model || !m_model->isOK()) {
 	return;
@@ -494,8 +494,8 @@
                                      mergingChannels, mixingChannels);
     if (channels == 0) return;
 
-    int w = v->width();
-    int h = v->height();
+    int w = v->getPaintWidth();
+    int h = v->getPaintHeight();
 
     bool ready = m_model->isReady();
     QPainter *paint;
@@ -559,7 +559,7 @@
     y1 = rect.bottom();
 
     if (x0 > 0) --x0;
-    if (x1 < v->width()) ++x1;
+    if (x1 < w) ++x1;
 
     // Our zoom level may differ from that at which the underlying
     // model has its blocks.
@@ -730,14 +730,17 @@
                 cerr << "WaveformLayer::paint: ERROR: i1 " << i1 << " > i0 " << i0 << " plus one (zoom = " << zoomLevel << ", model zoom = " << modelZoomLevel << ")" << endl;
             }
 
-	    if (ranges && i0 < (int)ranges->size()) {
+	    if (ranges && i0 < (sv_frame_t)ranges->size()) {
 
-		range = (*ranges)[i0];
+		range = (*ranges)[size_t(i0)];
 
 		if (i1 > i0 && i1 < (int)ranges->size()) {
-		    range.setMax(std::max(range.max(), (*ranges)[i1].max()));
-		    range.setMin(std::min(range.min(), (*ranges)[i1].min()));
-		    range.setAbsmean((range.absmean() + (*ranges)[i1].absmean()) / 2);
+		    range.setMax(std::max(range.max(),
+                                          (*ranges)[size_t(i1)].max()));
+		    range.setMin(std::min(range.min(),
+                                          (*ranges)[size_t(i1)].min()));
+		    range.setAbsmean((range.absmean()
+                                      + (*ranges)[size_t(i1)].absmean()) / 2);
 		}
 
 	    } else {
@@ -751,30 +754,33 @@
 
 	    if (mergingChannels) {
 
-		if (otherChannelRanges && i0 < (int)otherChannelRanges->size()) {
+		if (otherChannelRanges && i0 < (sv_frame_t)otherChannelRanges->size()) {
 
 		    range.setMax(fabsf(range.max()));
-		    range.setMin(-fabsf((*otherChannelRanges)[i0].max()));
+		    range.setMin(-fabsf((*otherChannelRanges)[size_t(i0)].max()));
 		    range.setAbsmean
                         ((range.absmean() +
-                          (*otherChannelRanges)[i0].absmean()) / 2);
+                          (*otherChannelRanges)[size_t(i0)].absmean()) / 2);
 
-		    if (i1 > i0 && i1 < (int)otherChannelRanges->size()) {
+		    if (i1 > i0 && i1 < (sv_frame_t)otherChannelRanges->size()) {
 			// let's not concern ourselves about the mean
 			range.setMin
                             (std::min
                              (range.min(),
-                              -fabsf((*otherChannelRanges)[i1].max())));
+                              -fabsf((*otherChannelRanges)[size_t(i1)].max())));
 		    }
 		}
 
 	    } else if (mixingChannels) {
 
-		if (otherChannelRanges && i0 < (int)otherChannelRanges->size()) {
+		if (otherChannelRanges && i0 < (sv_frame_t)otherChannelRanges->size()) {
 
-                    range.setMax((range.max() + (*otherChannelRanges)[i0].max()) / 2);
-                    range.setMin((range.min() + (*otherChannelRanges)[i0].min()) / 2);
-                    range.setAbsmean((range.absmean() + (*otherChannelRanges)[i0].absmean()) / 2);
+                    range.setMax((range.max()
+                                  + (*otherChannelRanges)[size_t(i0)].max()) / 2);
+                    range.setMin((range.min()
+                                  + (*otherChannelRanges)[size_t(i0)].min()) / 2);
+                    range.setAbsmean((range.absmean()
+                                      + (*otherChannelRanges)[size_t(i0)].absmean()) / 2);
                 }
             }
 
@@ -939,7 +945,7 @@
 
     if (m_aggressive) {
 
-	if (ready && rect == v->rect()) {
+	if (ready && rect == v->getPaintRect()) {
 	    m_cacheValid = true;
 	    m_cacheZoomLevel = zoomLevel;
 	}
@@ -953,7 +959,7 @@
 }
 
 QString
-WaveformLayer::getFeatureDescription(View *v, QPoint &pos) const
+WaveformLayer::getFeatureDescription(LayerGeometryProvider *v, QPoint &pos) const
 {
     int x = pos.x();
 
@@ -1036,7 +1042,7 @@
 }
 
 int
-WaveformLayer::getYForValue(const View *v, double value, int channel) const
+WaveformLayer::getYForValue(const LayerGeometryProvider *v, double value, int channel) const
 {
     int channels = 0, minChannel = 0, maxChannel = 0;
     bool mergingChannels = false, mixingChannels = false;
@@ -1046,7 +1052,7 @@
     if (channels == 0) return 0;
     if (maxChannel < minChannel || channel < minChannel) return 0;
 
-    int h = v->height();
+    int h = v->getPaintHeight();
     int m = (h / channels) / 2;
 	
     if ((m_scale == dBScale || m_scale == MeterScale) &&
@@ -1079,7 +1085,7 @@
 }
 
 double
-WaveformLayer::getValueForY(const View *v, int y, int &channel) const
+WaveformLayer::getValueForY(const LayerGeometryProvider *v, int y, int &channel) const
 {
     int channels = 0, minChannel = 0, maxChannel = 0;
     bool mergingChannels = false, mixingChannels = false;
@@ -1089,7 +1095,7 @@
     if (channels == 0) return 0;
     if (maxChannel < minChannel) return 0;
 
-    int h = v->height();
+    int h = v->getPaintHeight();
     int m = (h / channels) / 2;
 
     if ((m_scale == dBScale || m_scale == MeterScale) &&
@@ -1125,7 +1131,7 @@
 }
 
 bool
-WaveformLayer::getYScaleValue(const View *v, int y,
+WaveformLayer::getYScaleValue(const LayerGeometryProvider *v, int y,
                               double &value, QString &unit) const
 {
     int channel;
@@ -1151,7 +1157,7 @@
 }
 
 bool
-WaveformLayer::getYScaleDifference(const View *v, int y0, int y1,
+WaveformLayer::getYScaleDifference(const LayerGeometryProvider *v, int y0, int y1,
                                    double &diff, QString &unit) const
 {
     int c0, c1;
@@ -1189,7 +1195,7 @@
 }
 
 int
-WaveformLayer::getVerticalScaleWidth(View *, bool, QPainter &paint) const
+WaveformLayer::getVerticalScaleWidth(LayerGeometryProvider *, bool, QPainter &paint) const
 {
     if (m_scale == LinearScale) {
 	return paint.fontMetrics().width("0.0") + 13;
@@ -1200,7 +1206,7 @@
 }
 
 void
-WaveformLayer::paintVerticalScale(View *v, bool, QPainter &paint, QRect rect) const
+WaveformLayer::paintVerticalScale(LayerGeometryProvider *v, bool, QPainter &paint, QRect rect) const
 {
     if (!m_model || !m_model->isOK()) {
 	return;
--- a/layer/WaveformLayer.h	Mon Apr 13 13:52:05 2015 +0100
+++ b/layer/WaveformLayer.h	Thu Aug 20 14:54:21 2015 +0100
@@ -38,16 +38,16 @@
         return m_model ? m_model->getZoomConstraint() : 0;
     }
     virtual const Model *getModel() const { return m_model; }
-    virtual void paint(View *v, QPainter &paint, QRect rect) const;
+    virtual void paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const;
 
-    virtual QString getFeatureDescription(View *v, QPoint &) const;
+    virtual QString getFeatureDescription(LayerGeometryProvider *v, QPoint &) const;
 
     virtual ColourSignificance getLayerColourSignificance() const {
         return ColourAndBackgroundSignificant;
     }
 
-    virtual int getVerticalScaleWidth(View *v, bool detailed, QPainter &) const;
-    virtual void paintVerticalScale(View *v, bool detailed, QPainter &paint, QRect rect) const;
+    virtual int getVerticalScaleWidth(LayerGeometryProvider *v, bool detailed, QPainter &) const;
+    virtual void paintVerticalScale(LayerGeometryProvider *v, bool detailed, QPainter &paint, QRect rect) const;
 
     void setModel(const RangeSummarisableTimeValueModel *model);
 
@@ -180,17 +180,17 @@
     void setAggressiveCacheing(bool);
     bool getAggressiveCacheing() const { return m_aggressive; }
 
-    virtual bool isLayerScrollable(const View *) const;
+    virtual bool isLayerScrollable(const LayerGeometryProvider *) const;
 
-    virtual int getCompletion(View *) const;
+    virtual int getCompletion(LayerGeometryProvider *) const;
 
     virtual bool getValueExtents(double &min, double &max,
                                  bool &log, QString &unit) const;
 
-    virtual bool getYScaleValue(const View *v, int y,
+    virtual bool getYScaleValue(const LayerGeometryProvider *v, int y,
                                 double &value, QString &unit) const;
     
-    virtual bool getYScaleDifference(const View *v, int y0, int y1,
+    virtual bool getYScaleDifference(const LayerGeometryProvider *v, int y0, int y1,
                                      double &diff, QString &unit) const;
 
     virtual void toXml(QTextStream &stream, QString indent = "",
@@ -202,6 +202,8 @@
     virtual int getCurrentVerticalZoomStep() const;
     virtual void setVerticalZoomStep(int);
 
+    virtual bool canExistWithoutModel() const { return true; }
+
 protected:
     int dBscale(double sample, int m) const;
 
@@ -211,14 +213,14 @@
     int getChannelArrangement(int &min, int &max,
                                  bool &merging, bool &mixing) const;
 
-    int getYForValue(const View *v, double value, int channel) const;
+    int getYForValue(const LayerGeometryProvider *v, double value, int channel) const;
 
-    double getValueForY(const View *v, int y, int &channel) const;
+    double getValueForY(const LayerGeometryProvider *v, int y, int &channel) const;
 
-    bool getSourceFramesForX(View *v, int x, int modelZoomLevel,
+    bool getSourceFramesForX(LayerGeometryProvider *v, int x, int modelZoomLevel,
                              sv_frame_t &f0, sv_frame_t &f1) const;
 
-    float getNormalizeGain(View *v, int channel) const;
+    float getNormalizeGain(LayerGeometryProvider *v, int channel) const;
 
     virtual void flagBaseColourChanged() { m_cacheValid = false; }
 
--- a/svgui.pro	Mon Apr 13 13:52:05 2015 +0100
+++ b/svgui.pro	Thu Aug 20 14:54:21 2015 +0100
@@ -26,7 +26,7 @@
 }
 
 CONFIG += staticlib qt thread warn_on stl rtti exceptions c++11
-QT += network xml gui widgets
+QT += network xml gui widgets svg
 
 TARGET = svgui
 
@@ -93,7 +93,8 @@
            view/Pane.h \
            view/PaneStack.h \
            view/View.h \
-           view/ViewManager.h
+           view/ViewManager.h \
+           view/ViewProxy.h
 SOURCES += view/Overview.cpp \
            view/Pane.cpp \
            view/PaneStack.cpp \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/view/LayerGeometryProvider.h	Thu Aug 20 14:54:21 2015 +0100
@@ -0,0 +1,130 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    Sonic Visualiser
+    An audio file viewer and annotation editor.
+    Centre for Digital Music, Queen Mary, University of London.
+    
+    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.  See the file
+    COPYING included with this distribution for more information.
+*/
+
+#ifndef LAYER_GEOMETRY_PROVIDER_H
+#define LAYER_GEOMETRY_PROVIDER_H
+
+#include "base/BaseTypes.h"
+
+class ViewManager;
+class View;
+class Layer;
+
+class LayerGeometryProvider
+{
+public:
+    /**
+     * Retrieve the first visible sample frame on the widget.
+     * This is a calculated value based on the centre-frame, widget
+     * width and zoom level.  The result may be negative.
+     */
+    virtual sv_frame_t getStartFrame() const = 0;
+
+    /**
+     * Return the centre frame of the visible widget.  This is an
+     * exact value that does not depend on the zoom block size.  Other
+     * frame values (start, end) are calculated from this based on the
+     * zoom and other factors.
+     */
+    virtual sv_frame_t getCentreFrame() const = 0;
+
+    /**
+     * Retrieve the last visible sample frame on the widget.
+     * This is a calculated value based on the centre-frame, widget
+     * width and zoom level.
+     */
+    virtual sv_frame_t getEndFrame() const = 0;
+
+    /**
+     * Return the pixel x-coordinate corresponding to a given sample
+     * frame (which may be negative).
+     */
+    virtual int getXForFrame(sv_frame_t frame) const = 0;
+
+    /**
+     * Return the closest frame to the given pixel x-coordinate.
+     */
+    virtual sv_frame_t getFrameForX(int x) const = 0;
+
+    virtual sv_frame_t getModelsStartFrame() const = 0;
+    virtual sv_frame_t getModelsEndFrame() const = 0;
+
+    /**
+     * Return the pixel y-coordinate corresponding to a given
+     * frequency, if the frequency range is as specified.  This does
+     * not imply any policy about layer frequency ranges, but it might
+     * be useful for layers to match theirs up if desired.
+     *
+     * Not thread-safe in logarithmic mode.  Call only from GUI thread.
+     */
+    virtual double getYForFrequency(double frequency, double minFreq, double maxFreq, 
+                                    bool logarithmic) const = 0;
+
+    /**
+     * Return the closest frequency to the given pixel y-coordinate,
+     * if the frequency range is as specified.
+     *
+     * Not thread-safe in logarithmic mode.  Call only from GUI thread.
+     */
+    virtual double getFrequencyForY(int y, double minFreq, double maxFreq,
+			   bool logarithmic) const = 0;
+
+    virtual int getTextLabelHeight(const Layer *layer, QPainter &) const = 0;
+
+    virtual bool getValueExtents(QString unit, double &min, double &max,
+                                 bool &log) const = 0;
+
+    /**
+     * Return the zoom level, i.e. the number of frames per pixel
+     */
+    virtual int getZoomLevel() const = 0;
+
+    /**
+     * To be called from a layer, to obtain the extent of the surface
+     * that the layer is currently painting to. This may be the extent
+     * of the view (if 1x display scaling is in effect) or of a larger
+     * cached pixmap (if greater display scaling is in effect).
+     */
+    virtual QRect getPaintRect() const = 0;
+
+    virtual QSize getPaintSize() const { return getPaintRect().size(); }
+    virtual int getPaintWidth() const { return getPaintRect().width(); }
+    virtual int getPaintHeight() const { return getPaintRect().height(); }
+
+    virtual bool hasLightBackground() const = 0;
+    virtual QColor getForeground() const = 0;
+    virtual QColor getBackground() const = 0;
+
+    virtual ViewManager *getViewManager() const = 0;
+
+    virtual bool shouldIlluminateLocalFeatures(const Layer *, QPoint &) const = 0;
+    virtual bool shouldShowFeatureLabels() const = 0;
+
+    enum TextStyle {
+	BoxedText,
+	OutlinedText,
+        OutlinedItalicText
+    };
+
+    virtual void drawVisibleText(QPainter &p, int x, int y,
+				 QString text, TextStyle style) const = 0;
+
+    virtual void drawMeasurementRect(QPainter &p, const Layer *,
+                                     QRect rect, bool focus) const = 0;
+
+    virtual View *getView() = 0;
+    virtual const View *getView() const = 0;
+};
+
+#endif
--- a/view/Overview.cpp	Mon Apr 13 13:52:05 2015 +0100
+++ b/view/Overview.cpp	Thu Aug 20 14:54:21 2015 +0100
@@ -35,6 +35,10 @@
     m_followZoom = false;
     setPlaybackFollow(PlaybackIgnore);
     m_modelTestTime.start();
+
+    bool light = hasLightBackground();
+    if (light) m_boxColour = Qt::darkGray;
+    else m_boxColour = Qt::lightGray;
 }
 
 void
@@ -159,6 +163,12 @@
 }
 
 void
+Overview::setBoxColour(QColor c)
+{
+    m_boxColour = c;
+}
+
+void
 Overview::paintEvent(QPaintEvent *e)
 {
     // Recalculate zoom in case the size of the widget has changed.
@@ -263,7 +273,7 @@
     
     foreach (QRect vr, rects) {
         paint.setBrush(Qt::NoBrush);
-        paint.setPen(QPen(Qt::gray, 2));
+        paint.setPen(QPen(m_boxColour, 2));
         paint.drawRoundedRect(vr, 4, 4);
     }
 
--- a/view/Overview.h	Mon Apr 13 13:52:05 2015 +0100
+++ b/view/Overview.h	Thu Aug 20 14:54:21 2015 +0100
@@ -49,6 +49,8 @@
     virtual void viewZoomLevelChanged(View *, int, bool);
     virtual void viewManagerPlaybackFrameChanged(sv_frame_t);
 
+    virtual void setBoxColour(QColor);
+    
 protected:
     virtual void paintEvent(QPaintEvent *e);
     virtual void mousePressEvent(QMouseEvent *e);
@@ -67,6 +69,7 @@
     bool m_clickedInRange;
     sv_frame_t m_dragCentreFrame;
     QTime m_modelTestTime;
+    QColor m_boxColour;
     
     typedef std::set<View *> ViewSet;
     ViewSet m_views;
--- a/view/Pane.cpp	Mon Apr 13 13:52:05 2015 +0100
+++ b/view/Pane.cpp	Thu Aug 20 14:54:21 2015 +0100
@@ -388,10 +388,10 @@
 Pane::selectionIsBeingEdited() const
 {
     if (!m_editingSelection.isEmpty()) {
-    if (m_mousePos != m_clickPos &&
-        getFrameForX(m_mousePos.x()) != getFrameForX(m_clickPos.x())) {
-        return true;
-    }
+        if (m_mousePos != m_clickPos &&
+            getFrameForX(m_mousePos.x()) != getFrameForX(m_clickPos.x())) {
+            return true;
+        }
     }
     return false;
 }
@@ -2111,14 +2111,25 @@
         max = snapFrameRight;
     }
 
+    sv_frame_t end = getModelsEndFrame();
+    if (min > end) min = end;
+    if (max > end) max = end;
+
     if (m_manager) {
-        m_manager->setInProgressSelection(Selection(alignToReference(min),
-                                                    alignToReference(max)),
-                                          !m_resizing && !m_ctrlPressed);
+
+        Selection sel(alignToReference(min), alignToReference(max));
+
+        bool exc;
+        bool same = (m_manager->haveInProgressSelection() &&
+                     m_manager->getInProgressSelection(exc) == sel);
+        
+        m_manager->setInProgressSelection(sel, !m_resizing && !m_ctrlPressed);
+
+        if (!same) {
+            edgeScrollMaybe(e->x());
+        }
     }
 
-    edgeScrollMaybe(e->x());
-
     update();
 
     if (min != max) {
@@ -2141,11 +2152,12 @@
         sv_frame_t offset = mouseFrame - getStartFrame();
         sv_frame_t available = getEndFrame() - getStartFrame();
         sv_frame_t move = 0;
-        if (offset >= double(available) * 0.95) {
-            move = sv_frame_t(double(offset - available) * 0.95) + 1;
-        } else if (offset <= double(available) * 0.10) {
-            move = sv_frame_t(double(available) * 0.10 - double(offset)) + 1;
-            move = -move;
+        sv_frame_t rightEdge = available - (available / 20);
+        sv_frame_t leftEdge = (available / 10);
+        if (offset >= rightEdge) {
+            move = offset - rightEdge + 1;
+        } else if (offset <= leftEdge) {
+            move = offset - leftEdge - 1;
         }
         if (move != 0) {
             setCentreFrame(m_centreFrame + move);
--- a/view/View.cpp	Mon Apr 13 13:52:05 2015 +0100
+++ b/view/View.cpp	Thu Aug 20 14:54:21 2015 +0100
@@ -20,6 +20,7 @@
 #include "base/Profiler.h"
 #include "base/Pitch.h"
 #include "base/Preferences.h"
+#include "ViewProxy.h"
 
 #include "layer/TimeRulerLayer.h"
 #include "layer/SingleColourLayer.h"
@@ -37,6 +38,7 @@
 #include <QFont>
 #include <QMessageBox>
 #include <QPushButton>
+#include <QSettings>
 
 #include <iostream>
 #include <cassert>
@@ -44,8 +46,8 @@
 
 #include <unistd.h>
 
-//#define DEBUG_VIEW 1
-//#define DEBUG_VIEW_WIDGET_PAINT 1
+#define DEBUG_VIEW 1
+#define DEBUG_VIEW_WIDGET_PAINT 1
 
 
 View::View(QWidget *w, bool showProgress) :
@@ -59,6 +61,7 @@
     m_playPointerFrame(0),
     m_showProgress(showProgress),
     m_cache(0),
+    m_buffer(0),
     m_cacheCentreFrame(0),
     m_cacheZoomLevel(1024),
     m_selectionCached(false),
@@ -448,9 +451,30 @@
     return m_zoomLevel;
 }
 
+int
+View::effectiveDevicePixelRatio() const
+{
+#ifdef Q_OS_MAC
+    int dpratio = devicePixelRatio();
+    if (dpratio > 1) {
+        QSettings settings;
+        settings.beginGroup("Preferences");
+        if (!settings.value("scaledHiDpi", true).toBool()) {
+            dpratio = 1;
+        }
+        settings.endGroup();
+    }
+    return dpratio;
+#else
+    return 1;
+#endif
+}
+
 void
 View::setZoomLevel(int z)
 {
+    int dpratio = effectiveDevicePixelRatio();
+    if (z < dpratio) return;
     if (z < 1) z = 1;
     if (m_zoomLevel != int(z)) {
 	m_zoomLevel = z;
@@ -1027,7 +1051,7 @@
     f = getAlignedPlaybackFrame();
 
 #ifdef DEBUG_VIEW
-    cerr << " -> aligned frame = " << af << endl;
+    cerr << " -> aligned frame = " << f << endl;
 #endif
 
     movePlayPointer(f);
@@ -1653,11 +1677,27 @@
 void
 View::setPaintFont(QPainter &paint)
 {
+    int scaleFactor = 1;
+    int dpratio = effectiveDevicePixelRatio();
+    if (dpratio > 1) {
+        QPaintDevice *dev = paint.device();
+        if (dynamic_cast<QPixmap *>(dev) || dynamic_cast<QImage *>(dev)) {
+            scaleFactor = dpratio;
+        }
+    }
+
     QFont font(paint.font());
-    font.setPointSize(Preferences::getInstance()->getViewFontSize());
+    font.setPointSize(Preferences::getInstance()->getViewFontSize()
+                      * scaleFactor);
     paint.setFont(font);
 }
 
+QRect
+View::getPaintRect() const
+{
+    return rect();
+}
+
 void
 View::paintEvent(QPaintEvent *e)
 {
@@ -1694,6 +1734,8 @@
 
     QRect nonCacheRect(cacheRect);
 
+    int dpratio = effectiveDevicePixelRatio();
+
     // If not all layers are scrollable, but some of the back layers
     // are, we should store only those in the cache.
 
@@ -1705,25 +1747,34 @@
 
     // If all the non-scrollable layers are non-opaque, then we draw
     // the selection rectangle behind them and cache it.  If any are
-    // opaque, however, we can't cache.
+    // opaque, however, or if our device-pixel ratio is not 1 (so we
+    // need to paint direct to the widget), then we can't cache.
     //
-    if (!selectionCacheable) {
-	selectionCacheable = true;
-	for (LayerList::const_iterator i = nonScrollables.begin();
-	     i != nonScrollables.end(); ++i) {
-	    if ((*i)->isLayerOpaque()) {
-		selectionCacheable = false;
-		break;
-	    }
-	}
-    }
-
-    if (selectionCacheable) {
-	QPoint localPos;
-	bool closeToLeft, closeToRight;
-	if (shouldIlluminateLocalSelection(localPos, closeToLeft, closeToRight)) {
-	    selectionCacheable = false;
-	}
+    if (dpratio == 1) {
+
+        if (!selectionCacheable) {
+            selectionCacheable = true;
+            for (LayerList::const_iterator i = nonScrollables.begin();
+                 i != nonScrollables.end(); ++i) {
+                if ((*i)->isLayerOpaque()) {
+                    selectionCacheable = false;
+                    break;
+                }
+            }
+        }
+
+        if (selectionCacheable) {
+            QPoint localPos;
+            bool closeToLeft, closeToRight;
+            if (shouldIlluminateLocalSelection
+                (localPos, closeToLeft, closeToRight)) {
+                selectionCacheable = false;
+            }
+        }
+
+    } else {
+
+        selectionCacheable = false;
     }
 
 #ifdef DEBUG_VIEW_WIDGET_PAINT
@@ -1741,6 +1792,14 @@
 	m_selectionCached = false;
     }
 
+    QSize scaledCacheSize(scaledSize(size(), dpratio));
+    QRect scaledCacheRect(scaledRect(cacheRect, dpratio));
+
+    if (!m_buffer || scaledCacheSize != m_buffer->size()) {
+        delete m_buffer;
+        m_buffer = new QPixmap(scaledCacheSize);
+    }
+    
     if (!scrollables.empty()) {
 
 #ifdef DEBUG_VIEW_WIDGET_PAINT
@@ -1750,8 +1809,7 @@
 
 	if (!m_cache ||
 	    m_cacheZoomLevel != m_zoomLevel ||
-	    width() != m_cache->width() ||
-	    height() != m_cache->height()) {
+            scaledCacheSize != m_cache->size()) {
 
 	    // cache is not valid
 
@@ -1763,7 +1821,7 @@
 #endif
 	    } else {
 		delete m_cache;
-		m_cache = new QPixmap(width(), height());
+		m_cache = new QPixmap(scaledCacheSize);
 #ifdef DEBUG_VIEW_WIDGET_PAINT
 		cerr << "View(" << this << ")::paintEvent: recreated cache" << endl;
 #endif
@@ -1778,24 +1836,10 @@
 		getXForFrame(m_centreFrame);
 
 	    if (dx > -width() && dx < width()) {
-#ifdef PIXMAP_COPY_TO_SELF
-                // This is not normally defined. Copying a pixmap to
-		// itself doesn't work properly on Windows, Mac, or
-		// X11 with the raster backend (it only works when
-		// moving in one direction and then presumably only by
-		// accident).  It does actually seem to be fine on X11
-		// with the native backend, but we prefer not to use
-		// that anyway
-		paint.begin(m_cache);
-		paint.drawPixmap(dx, 0, *m_cache);
-		paint.end();
-#else
 		static QPixmap *tmpPixmap = 0;
-		if (!tmpPixmap ||
-		    tmpPixmap->width() != width() ||
-		    tmpPixmap->height() != height()) {
+		if (!tmpPixmap || tmpPixmap->size() != scaledCacheSize) {
 		    delete tmpPixmap;
-		    tmpPixmap = new QPixmap(width(), height());
+		    tmpPixmap = new QPixmap(scaledCacheSize);
 		}
 		paint.begin(tmpPixmap);
 		paint.drawPixmap(0, 0, *m_cache);
@@ -1803,7 +1847,6 @@
 		paint.begin(m_cache);
 		paint.drawPixmap(dx, 0, *tmpPixmap);
 		paint.end();
-#endif
 		if (dx < 0) {
 		    cacheRect = QRect(width() + dx, 0, -dx, height());
 		} else {
@@ -1824,8 +1867,8 @@
 #ifdef DEBUG_VIEW_WIDGET_PAINT
 	    cerr << "View(" << this << ")::paintEvent: cache is good" << endl;
 #endif
-	    paint.begin(this);
-	    paint.drawPixmap(cacheRect, *m_cache, cacheRect);
+	    paint.begin(m_buffer);
+	    paint.drawPixmap(scaledCacheRect, *m_cache, scaledCacheRect);
 	    paint.end();
 	    QFrame::paintEvent(e);
 	    paintedCacheRect = true;
@@ -1841,16 +1884,26 @@
 
     // Scrollable (cacheable) items first
 
+    ViewProxy proxy(this, dpratio);
+    
     if (!paintedCacheRect) {
 
-	if (repaintCache) paint.begin(m_cache);
-	else paint.begin(this);
+        QRect rectToPaint;
+
+	if (repaintCache) {
+            paint.begin(m_cache);
+            rectToPaint = scaledCacheRect;
+        } else {
+            paint.begin(m_buffer);
+            rectToPaint = scaledCacheRect;
+        }
+
         setPaintFont(paint);
-	paint.setClipRect(cacheRect);
+	paint.setClipRect(rectToPaint);
 
         paint.setPen(getBackground());
         paint.setBrush(getBackground());
-	paint.drawRect(cacheRect);
+	paint.drawRect(rectToPaint);
 
 	paint.setPen(getForeground());
 	paint.setBrush(Qt::NoBrush);
@@ -1858,7 +1911,10 @@
 	for (LayerList::iterator i = scrollables.begin(); i != scrollables.end(); ++i) {
 	    paint.setRenderHint(QPainter::Antialiasing, false);
 	    paint.save();
-	    (*i)->paint(this, paint, cacheRect);
+#ifdef DEBUG_VIEW_WIDGET_PAINT
+            cerr << "Painting scrollable layer " << *i << " using proxy with repaintCache = " << repaintCache << ", dpratio = " << dpratio << ", rectToPaint = " << rectToPaint.x() << "," << rectToPaint.y() << " " << rectToPaint.width() << "x" << rectToPaint.height() << endl;
+#endif
+            (*i)->paint(&proxy, paint, rectToPaint);
 	    paint.restore();
 	}
 
@@ -1871,8 +1927,9 @@
 
 	if (repaintCache) {
 	    cacheRect |= (e ? e->rect() : rect());
-	    paint.begin(this);
-	    paint.drawPixmap(cacheRect, *m_cache, cacheRect);
+            scaledCacheRect = scaledRect(cacheRect, dpratio);
+	    paint.begin(m_buffer);
+	    paint.drawPixmap(scaledCacheRect, *m_cache, scaledCacheRect);
 	    paint.end();
 	}
     }
@@ -1883,13 +1940,15 @@
 
     nonCacheRect |= cacheRect;
 
-    paint.begin(this);
-    paint.setClipRect(nonCacheRect);
+    QRect scaledNonCacheRect = scaledRect(nonCacheRect, dpratio);
+    
+    paint.begin(m_buffer);
+    paint.setClipRect(scaledNonCacheRect);
     setPaintFont(paint);
     if (scrollables.empty()) {
         paint.setPen(getBackground());
         paint.setBrush(getBackground());
-	paint.drawRect(nonCacheRect);
+	paint.drawRect(scaledNonCacheRect);
     }
 	
     paint.setPen(getForeground());
@@ -1897,10 +1956,18 @@
 	
     for (LayerList::iterator i = nonScrollables.begin(); i != nonScrollables.end(); ++i) {
 //        Profiler profiler2("View::paintEvent non-cacheable");
-	(*i)->paint(this, paint, nonCacheRect);
+#ifdef DEBUG_VIEW_WIDGET_PAINT
+        cerr << "Painting non-scrollable layer " << *i << " without proxy with repaintCache = " << repaintCache << ", dpratio = " << dpratio << ", rectToPaint = " << nonCacheRect.x() << "," << nonCacheRect.y() << " " << nonCacheRect.width() << "x" << nonCacheRect.height() << endl;
+#endif
+	(*i)->paint(&proxy, paint, scaledNonCacheRect);
     }
 	
     paint.end();
+    
+    paint.begin(this);
+    QRect finalPaintRect = e ? e->rect() : rect();
+    paint.drawPixmap(finalPaintRect, *m_buffer, scaledRect(finalPaintRect, dpratio));
+    paint.end();
 
     paint.begin(this);
     setPaintFont(paint);
@@ -1925,7 +1992,7 @@
             showPlayPointer = false;
         }
     }
-
+    
     if (showPlayPointer) {
 
 	paint.begin(this);
@@ -2412,23 +2479,23 @@
 
 	for (LayerList::iterator i = m_layerStack.begin();
              i != m_layerStack.end(); ++i) {
-		if(!((*i)->isLayerDormant(this))){
-
-		    paint.setRenderHint(QPainter::Antialiasing, false);
-
-		    paint.save();
-	            paint.translate(xorigin + x, 0);
-
-	            cerr << "Centre frame now: " << m_centreFrame << " drawing to " << chunk.x() + x + xorigin << ", " << chunk.width() << endl;
-
-	            (*i)->setSynchronousPainting(true);
-
-		    (*i)->paint(this, paint, chunk);
-
-	            (*i)->setSynchronousPainting(false);
-
-		    paint.restore();
-		}
+            if (!((*i)->isLayerDormant(this))){
+
+                paint.setRenderHint(QPainter::Antialiasing, false);
+
+                paint.save();
+                paint.translate(xorigin + x, 0);
+
+                cerr << "Centre frame now: " << m_centreFrame << " drawing to " << chunk.x() + x + xorigin << ", " << chunk.width() << endl;
+
+                (*i)->setSynchronousPainting(true);
+
+                (*i)->paint(this, paint, chunk);
+
+                (*i)->setSynchronousPainting(false);
+
+                paint.restore();
+            }
 	}
     }
 
--- a/view/View.h	Mon Apr 13 13:52:05 2015 +0100
+++ b/view/View.h	Thu Aug 20 14:54:21 2015 +0100
@@ -19,6 +19,8 @@
 #include <QFrame>
 #include <QProgressBar>
 
+#include "LayerGeometryProvider.h"
+
 #include "base/ZoomConstraint.h"
 #include "base/PropertyContainer.h"
 #include "ViewManager.h"
@@ -49,7 +51,8 @@
  */
 
 class View : public QFrame,
-	     public XmlExportable
+	     public XmlExportable,
+             public LayerGeometryProvider
 {
     Q_OBJECT
 
@@ -243,12 +246,6 @@
     virtual QColor getForeground() const;
     virtual QColor getBackground() const;
 
-    enum TextStyle {
-	BoxedText,
-	OutlinedText,
-        OutlinedItalicText
-    };
-
     virtual void drawVisibleText(QPainter &p, int x, int y,
 				 QString text, TextStyle style) const;
 
@@ -315,6 +312,18 @@
     sv_frame_t getModelsStartFrame() const;
     sv_frame_t getModelsEndFrame() const;
 
+    /**
+     * To be called from a layer, to obtain the extent of the surface
+     * that the layer is currently painting to. This may be the extent
+     * of the view (if 1x display scaling is in effect) or of a larger
+     * cached pixmap (if greater display scaling is in effect).
+     */
+    QRect getPaintRect() const;
+
+    QSize getPaintSize() const { return getPaintRect().size(); }
+    int getPaintWidth() const { return getPaintRect().width(); }
+    int getPaintHeight() const { return getPaintRect().height(); }
+
     typedef std::set<Model *> ModelSet;
     ModelSet getModels();
 
@@ -324,6 +333,9 @@
     sv_frame_t alignToReference(sv_frame_t) const;
     sv_frame_t getAlignedPlaybackFrame() const;
 
+    View *getView() { return this; } 
+    const View *getView() const { return this; } 
+    
 signals:
     void propertyContainerAdded(PropertyContainer *pc);
     void propertyContainerRemoved(PropertyContainer *pc);
@@ -377,6 +389,14 @@
     virtual bool shouldLabelSelections() const { return true; }
     virtual bool render(QPainter &paint, int x0, sv_frame_t f0, sv_frame_t f1);
     virtual void setPaintFont(QPainter &paint);
+
+    QSize scaledSize(const QSize &s, int factor) {
+        return QSize(s.width() * factor, s.height() * factor);
+    }
+    QRect scaledRect(const QRect &r, int factor) {
+        return QRect(r.x() * factor, r.y() * factor,
+                     r.width() * factor, r.height() * factor);
+    }
     
     typedef std::vector<Layer *> LayerList;
 
@@ -406,6 +426,8 @@
     void checkProgress(void *object);
     int getProgressBarWidth() const; // if visible
 
+    int effectiveDevicePixelRatio() const;
+
     sv_frame_t          m_centreFrame;
     int                 m_zoomLevel;
     bool                m_followPan;
@@ -417,6 +439,7 @@
     bool                m_showProgress;
 
     QPixmap            *m_cache;
+    QPixmap            *m_buffer;
     sv_frame_t          m_cacheCentreFrame;
     int                 m_cacheZoomLevel;
     bool                m_selectionCached;
--- a/view/ViewManager.cpp	Mon Apr 13 13:52:05 2015 +0100
+++ b/view/ViewManager.cpp	Thu Aug 20 14:54:21 2015 +0100
@@ -166,6 +166,7 @@
 void
 ViewManager::setPlaybackFrame(sv_frame_t f)
 {
+    if (f < 0) f = 0;
     if (m_playbackFrame != f) {
 	m_playbackFrame = f;
 	emit playbackFrameChanged(f);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/view/ViewProxy.h	Thu Aug 20 14:54:21 2015 +0100
@@ -0,0 +1,140 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    Sonic Visualiser
+    An audio file viewer and annotation editor.
+    Centre for Digital Music, Queen Mary, University of London.
+    
+    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.  See the file
+    COPYING included with this distribution for more information.
+*/
+
+#ifndef VIEW_PROXY_H
+#define VIEW_PROXY_H
+
+#include "LayerGeometryProvider.h"
+
+class ViewProxy : public LayerGeometryProvider
+{
+public:
+    ViewProxy(View *view, int scaleFactor) :
+	m_view(view), m_scaleFactor(scaleFactor) { }
+
+    virtual sv_frame_t getStartFrame() const {
+	return m_view->getStartFrame();
+    }
+    virtual sv_frame_t getCentreFrame() const {
+	return m_view->getCentreFrame();
+    }
+    virtual sv_frame_t getEndFrame() const {
+	return m_view->getEndFrame();
+    }
+    virtual int getXForFrame(sv_frame_t frame) const {
+        //!!! not actually correct, if frame lies between view's pixels
+	return m_scaleFactor * m_view->getXForFrame(frame);
+    }
+    virtual sv_frame_t getFrameForX(int x) const {
+        sv_frame_t f0 = m_view->getFrameForX(x / m_scaleFactor);
+        if (m_scaleFactor == 1) return f0;
+        sv_frame_t f1 = m_view->getFrameForX((x / m_scaleFactor) + 1);
+        return f0 + ((f1 - f0) * (x % m_scaleFactor)) / m_scaleFactor;
+    }
+    virtual sv_frame_t getModelsStartFrame() const {
+	return m_view->getModelsStartFrame();
+    }
+    virtual sv_frame_t getModelsEndFrame() const {
+	return m_view->getModelsEndFrame();
+    }
+    virtual double getYForFrequency(double frequency,
+				    double minFreq, double maxFreq, 
+                                    bool logarithmic) const {
+	return m_scaleFactor *
+	    m_view->getYForFrequency(frequency, minFreq, maxFreq, logarithmic);
+    }
+    virtual double getFrequencyForY(int y, double minFreq, double maxFreq,
+				    bool logarithmic) const {
+        double f0 = m_view->getFrequencyForY
+            (y / m_scaleFactor, minFreq, maxFreq, logarithmic);
+        if (m_scaleFactor == 1) return f0;
+        double f1 = m_view->getFrequencyForY
+            ((y / m_scaleFactor) + 1, minFreq, maxFreq, logarithmic);
+        return f0 + ((f1 - f0) * (y % m_scaleFactor)) / m_scaleFactor;
+    }
+    virtual int getTextLabelHeight(const Layer *layer, QPainter &paint) const {
+	return m_scaleFactor * m_view->getTextLabelHeight(layer, paint);
+    }
+    virtual bool getValueExtents(QString unit, double &min, double &max,
+                                 bool &log) const {
+	return m_view->getValueExtents(unit, min, max, log);
+    }
+    virtual int getZoomLevel() const {
+	//!!! aarg, what if it's already 1?
+	int z = m_view->getZoomLevel();
+	cerr << "getZoomLevel: from " << z << " to ";
+	z = z / m_scaleFactor;
+	cerr << z << endl;
+	return z;
+    }
+    virtual QRect getPaintRect() const {
+	QRect r = m_view->getPaintRect();
+	return QRect(r.x() * m_scaleFactor,
+		     r.y() * m_scaleFactor,
+		     r.width() * m_scaleFactor,
+		     r.height() * m_scaleFactor);
+    }
+    virtual QSize getPaintSize() const {
+        return getPaintRect().size();
+    }
+    virtual int getPaintWidth() const { 
+        return getPaintRect().width();
+    }
+    virtual int getPaintHeight() const { 
+        return getPaintRect().height();
+    }
+    virtual bool hasLightBackground() const {
+	return m_view->hasLightBackground();
+    }
+    virtual QColor getForeground() const {
+	return m_view->getForeground();
+    }
+    virtual QColor getBackground() const {
+	return m_view->getBackground();
+    }
+    virtual ViewManager *getViewManager() const {
+	return m_view->getViewManager();
+    }
+	
+    virtual bool shouldIlluminateLocalFeatures(const Layer *layer,
+                                               QPoint &point) const {
+        QPoint p;
+	bool should = m_view->shouldIlluminateLocalFeatures(layer, p);
+        point = QPoint(p.x() * m_scaleFactor, p.y() * m_scaleFactor);
+        return should;
+    }
+
+    virtual bool shouldShowFeatureLabels() const {
+	return m_view->shouldShowFeatureLabels();
+    }
+
+    virtual void drawVisibleText(QPainter &p, int x, int y,
+				 QString text, TextStyle style) const {
+	m_view->drawVisibleText(p, x, y, text, style);
+    }
+
+    virtual void drawMeasurementRect(QPainter &p, const Layer *layer,
+                                     QRect rect, bool focus) const {
+	m_view->drawMeasurementRect(p, layer, rect, focus);
+    }
+
+    virtual View *getView() { return m_view; }
+    virtual const View *getView() const { return m_view; }
+
+private:
+    View *m_view;
+    int m_scaleFactor;
+};
+
+#endif
--- a/widgets/CSVFormatDialog.cpp	Mon Apr 13 13:52:05 2015 +0100
+++ b/widgets/CSVFormatDialog.cpp	Thu Aug 20 14:54:21 2015 +0100
@@ -121,16 +121,37 @@
     layout->addWidget(new QLabel(tr("Timing is specified:")), row, 0);
     
     m_timingTypeCombo = new QComboBox;
-    m_timingTypeCombo->addItem(tr("Explicitly, in seconds"));
-    m_timingTypeCombo->addItem(tr("Explicitly, in milliseconds"));
-    m_timingTypeCombo->addItem(tr("Explicitly, in audio sample frames"));
-    m_timingTypeCombo->addItem(tr("Implicitly: rows are equally spaced in time"));
+
+    m_timingLabels = {
+        { TimingExplicitSeconds, tr("Explicitly, in seconds") },
+        { TimingExplicitMsec, tr("Explicitly, in milliseconds") },
+        { TimingExplicitSamples, tr("Explicitly, in audio sample frames") },
+        { TimingImplicit, tr("Implicitly: rows are equally spaced in time") }
+    };
+
+    for (auto &l: m_timingLabels) {
+        m_timingTypeCombo->addItem(l.second);
+    }
+
     layout->addWidget(m_timingTypeCombo, row++, 1, 1, 2);
+
     connect(m_timingTypeCombo, SIGNAL(activated(int)),
 	    this, SLOT(timingTypeChanged(int)));
-    m_timingTypeCombo->setCurrentIndex
-        (m_format.getTimingType() == CSVFormat::ExplicitTiming ?
-         m_format.getTimeUnits() == CSVFormat::TimeSeconds ? 0 : 2 : 3);
+
+    m_initialTimingOption = TimingImplicit;
+    if (m_format.getTimingType() == CSVFormat::ExplicitTiming) {
+        switch (m_format.getTimeUnits()) {
+        case CSVFormat::TimeSeconds:
+            m_initialTimingOption = TimingExplicitSeconds; break;
+        case CSVFormat::TimeMilliseconds:
+            m_initialTimingOption = TimingExplicitMsec; break;
+        case CSVFormat::TimeAudioFrames:
+            m_initialTimingOption = TimingExplicitSamples; break;
+        case CSVFormat::TimeWindows:
+            m_initialTimingOption = TimingImplicit; break;
+        }
+    }
+    m_timingTypeCombo->setCurrentIndex(int(m_initialTimingOption));
 
     m_sampleRateLabel = new QLabel(tr("Audio sample rate (Hz):"));
     layout->addWidget(m_sampleRateLabel, row, 0);
@@ -189,7 +210,6 @@
     setLayout(layout);
 
     timingTypeChanged(m_timingTypeCombo->currentIndex());
-    updateModelLabel();
 }
 
 CSVFormatDialog::~CSVFormatDialog()
@@ -230,46 +250,72 @@
 }
 
 void
+CSVFormatDialog::applyStartTimePurpose()
+{
+    // First check if we already have any. NB there may be fewer than
+    // m_format.getColumnCount() elements in m_columnPurposeCombos
+    // (because of the fuzzy column behaviour). Note also that the
+    // fuzzy column (which is the one just showing how many more
+    // columns there are) has a different combo with only two items
+    // (ignore or Values)
+    for (int i = 0; i < m_columnPurposeCombos.size(); ++i) {
+        if (i == m_fuzzyColumn) continue;
+        QComboBox *cb = m_columnPurposeCombos[i];
+        if (cb->currentIndex() == int(CSVFormat::ColumnStartTime)) {
+            return;
+        }
+    }
+    // and if not, select one
+    for (int i = 0; i < m_columnPurposeCombos.size(); ++i) {
+        if (i == m_fuzzyColumn) continue;
+        QComboBox *cb = m_columnPurposeCombos[i];
+        if (cb->currentIndex() == int(CSVFormat::ColumnValue)) {
+            cb->setCurrentIndex(int(CSVFormat::ColumnStartTime));
+            return;
+        }
+    }
+}
+
+void
+CSVFormatDialog::removeStartTimePurpose()
+{
+    // NB there may be fewer than m_format.getColumnCount() elements
+    // in m_columnPurposeCombos (because of the fuzzy column
+    // behaviour)
+    for (int i = 0; i < m_columnPurposeCombos.size(); ++i) {
+        if (i == m_fuzzyColumn) continue;
+        QComboBox *cb = m_columnPurposeCombos[i];
+        if (cb->currentIndex() == int(CSVFormat::ColumnStartTime)) {
+            cb->setCurrentIndex(int(CSVFormat::ColumnValue));
+        }
+    }
+}
+
+void
+CSVFormatDialog::updateComboVisibility()
+{
+    bool wantRate = (m_format.getTimingType() == CSVFormat::ImplicitTiming ||
+                     m_format.getTimeUnits() == CSVFormat::TimeAudioFrames);
+    bool wantWindow = (m_format.getTimingType() == CSVFormat::ImplicitTiming);
+    
+    m_sampleRateCombo->setEnabled(wantRate);
+    m_sampleRateLabel->setEnabled(wantRate);
+
+    m_windowSizeCombo->setEnabled(wantWindow);
+    m_windowSizeLabel->setEnabled(wantWindow);
+}
+
+void
 CSVFormatDialog::timingTypeChanged(int type)
 {
-    switch (type) {
-
-    case 0:
-	m_format.setTimingType(CSVFormat::ExplicitTiming);
-	m_format.setTimeUnits(CSVFormat::TimeSeconds);
-	m_sampleRateCombo->setEnabled(false);
-	m_sampleRateLabel->setEnabled(false);
-	m_windowSizeCombo->setEnabled(false);
-	m_windowSizeLabel->setEnabled(false);
-	break;
-
-    case 1:
-	m_format.setTimingType(CSVFormat::ExplicitTiming);
-	m_format.setTimeUnits(CSVFormat::TimeMilliseconds);
-	m_sampleRateCombo->setEnabled(true);
-	m_sampleRateLabel->setEnabled(true);
-	m_windowSizeCombo->setEnabled(false);
-	m_windowSizeLabel->setEnabled(false);
-	break;
-
-    case 2:
-	m_format.setTimingType(CSVFormat::ExplicitTiming);
-	m_format.setTimeUnits(CSVFormat::TimeAudioFrames);
-	m_sampleRateCombo->setEnabled(true);
-	m_sampleRateLabel->setEnabled(true);
-	m_windowSizeCombo->setEnabled(false);
-	m_windowSizeLabel->setEnabled(false);
-	break;
-
-    case 3:
-	m_format.setTimingType(CSVFormat::ImplicitTiming);
-	m_format.setTimeUnits(CSVFormat::TimeWindows);
-	m_sampleRateCombo->setEnabled(true);
-	m_sampleRateLabel->setEnabled(true);
-	m_windowSizeCombo->setEnabled(true);
-	m_windowSizeLabel->setEnabled(true);
-	break;
+    // Update any column purpose combos
+    if (TimingOption(type) == TimingImplicit) {
+        removeStartTimePurpose();
+    } else {
+        applyStartTimePurpose();
     }
+    updateFormatFromDialog();
+    updateComboVisibility();
 }
 
 void
@@ -292,45 +338,31 @@
 CSVFormatDialog::columnPurposeChanged(int p)
 {
     QObject *o = sender();
-
     QComboBox *cb = qobject_cast<QComboBox *>(o);
     if (!cb) return;
 
     CSVFormat::ColumnPurpose purpose = (CSVFormat::ColumnPurpose)p;
 
-    bool haveStartTime = false;
-    bool haveDuration = false;
-    bool havePitch = false;
-    int valueCount = 0;
-
+    bool haveStartTime = false; // so as to update timing type combo appropriately
+    
+    // Ensure the column purpose combos are consistent with one
+    // another, without reference to m_format (which we'll update
+    // separately)
+    
     for (int i = 0; i < m_columnPurposeCombos.size(); ++i) {
 
-        CSVFormat::ColumnPurpose cp = m_format.getColumnPurpose(i);
+        // The fuzzy column combo only has the entries <ignore> or
+        // Values, so it can't affect the timing type and none of this
+        // logic affects it
+        if (i == m_fuzzyColumn) continue;
 
-        bool thisChanged = (cb == m_columnPurposeCombos[i]);
+        QComboBox *thisCombo = m_columnPurposeCombos[i];
         
-        if (thisChanged) {
-
-            cerr << "i == " << i << ", fuzzy == " << m_fuzzyColumn
-                      << ", p == " << p << endl;
-
-            if (i == m_fuzzyColumn) {
-                for (int j = i; j < m_format.getColumnCount(); ++j) {
-                    if (p == 0) { // Ignore
-                        m_format.setColumnPurpose(j, CSVFormat::ColumnUnknown);
-                    } else { // Value
-                        m_format.setColumnPurpose(j, CSVFormat::ColumnValue);
-                        ++valueCount;
-                    }
-                }
-                continue;
-            }
-
-            cp = purpose;
-
-        } else {
-
-            if (i == m_fuzzyColumn) continue;
+        CSVFormat::ColumnPurpose cp = (CSVFormat::ColumnPurpose)
+            (thisCombo->currentIndex());
+        bool thisChanged = (cb == thisCombo);
+        
+        if (!thisChanged) {
 
             // We can only have one ColumnStartTime column, and only
             // one of either ColumnDuration or ColumnEndTime
@@ -353,29 +385,99 @@
                     cp = CSVFormat::ColumnUnknown;
                 }
             }
+
+            if (cp == CSVFormat::ColumnStartTime) {
+                haveStartTime = true;
+            }
+        
+            thisCombo->setCurrentIndex(int(cp));
+
+        } else {
+            if (purpose == CSVFormat::ColumnStartTime) {
+                haveStartTime = true;
+            }
         }
-
-        if (cp == CSVFormat::ColumnStartTime) {
-            haveStartTime = true;
-        }
-        if (cp == CSVFormat::ColumnEndTime ||
-            cp == CSVFormat::ColumnDuration) {
-            haveDuration = true;
-        }
-        if (cp == CSVFormat::ColumnPitch) {
-            havePitch = true;
-        }
-        if (cp == CSVFormat::ColumnValue) {
-            ++valueCount;
-        }
-
-        m_columnPurposeCombos[i]->setCurrentIndex(int(cp));
-        m_format.setColumnPurpose(i, cp);
     }
 
     if (!haveStartTime) {
-        m_timingTypeCombo->setCurrentIndex(2);
-        timingTypeChanged(2);
+        m_timingTypeCombo->setCurrentIndex(int(TimingImplicit));
+    } else if (m_timingTypeCombo->currentIndex() == int(TimingImplicit)) {
+        if (m_initialTimingOption == TimingImplicit) {
+            m_timingTypeCombo->setCurrentIndex(TimingExplicitSeconds);
+        } else {
+            m_timingTypeCombo->setCurrentIndex(m_initialTimingOption);
+        }
+    }
+
+    updateFormatFromDialog();
+    updateComboVisibility();
+}
+
+void
+CSVFormatDialog::updateFormatFromDialog()
+{
+    switch (TimingOption(m_timingTypeCombo->currentIndex())) {
+
+    case TimingExplicitSeconds:
+	m_format.setTimingType(CSVFormat::ExplicitTiming);
+	m_format.setTimeUnits(CSVFormat::TimeSeconds);
+	break;
+
+    case TimingExplicitMsec:
+	m_format.setTimingType(CSVFormat::ExplicitTiming);
+	m_format.setTimeUnits(CSVFormat::TimeMilliseconds);
+	break;
+
+    case TimingExplicitSamples:
+	m_format.setTimingType(CSVFormat::ExplicitTiming);
+	m_format.setTimeUnits(CSVFormat::TimeAudioFrames);
+	break;
+
+    case TimingImplicit:
+	m_format.setTimingType(CSVFormat::ImplicitTiming);
+	m_format.setTimeUnits(CSVFormat::TimeWindows);
+	break;
+    }
+
+    bool haveStartTime = false;
+    bool haveDuration = false;
+    bool havePitch = false;
+    int valueCount = 0;
+
+    for (int i = 0; i < m_columnPurposeCombos.size(); ++i) {
+
+        QComboBox *thisCombo = m_columnPurposeCombos[i];
+        
+        CSVFormat::ColumnPurpose purpose = (CSVFormat::ColumnPurpose)
+            (thisCombo->currentIndex());
+
+        if (i == m_fuzzyColumn) {
+            for (int j = i; j < m_format.getColumnCount(); ++j) {
+                if (purpose == CSVFormat::ColumnUnknown) {
+                    m_format.setColumnPurpose(j, CSVFormat::ColumnUnknown);
+                } else { // Value
+                    m_format.setColumnPurpose(j, CSVFormat::ColumnValue);
+                    ++valueCount;
+                }
+            }
+        } else {
+        
+            if (purpose == CSVFormat::ColumnStartTime) {
+                haveStartTime = true;
+            }
+            if (purpose == CSVFormat::ColumnEndTime ||
+                purpose == CSVFormat::ColumnDuration) {
+                haveDuration = true;
+            }
+            if (purpose == CSVFormat::ColumnPitch) {
+                havePitch = true;
+            }
+            if (purpose == CSVFormat::ColumnValue) {
+                ++valueCount;
+            }
+
+            m_format.setColumnPurpose(i, purpose);
+        }
     }
 
     if (haveStartTime && haveDuration) {
@@ -398,3 +500,4 @@
 }
 
 
+
--- a/widgets/CSVFormatDialog.h	Mon Apr 13 13:52:05 2015 +0100
+++ b/widgets/CSVFormatDialog.h	Thu Aug 20 14:54:21 2015 +0100
@@ -40,11 +40,26 @@
     void sampleRateChanged(QString);
     void windowSizeChanged(QString);
     void columnPurposeChanged(int purpose);
+
+    void updateFormatFromDialog();
     void updateModelLabel();
 
 protected:
     CSVFormat m_format;
     int m_maxDisplayCols;
+
+    enum TimingOption {
+        TimingExplicitSeconds = 0,
+        TimingExplicitMsec,
+        TimingExplicitSamples,
+        TimingImplicit
+    };
+    std::map<TimingOption, QString> m_timingLabels;
+    TimingOption m_initialTimingOption;
+
+    void updateComboVisibility();
+    void applyStartTimePurpose();
+    void removeStartTimePurpose();
     
     QComboBox *m_timingTypeCombo;
     QLabel *m_sampleRateLabel;
--- a/widgets/CommandHistory.cpp	Mon Apr 13 13:52:05 2015 +0100
+++ b/widgets/CommandHistory.cpp	Thu Aug 20 14:54:21 2015 +0100
@@ -26,6 +26,8 @@
 
 #include "base/Command.h"
 
+#include "IconLoader.h"
+
 #include <QRegExp>
 #include <QMenu>
 #include <QToolBar>
@@ -53,12 +55,16 @@
     m_bundleTimer(0),
     m_bundleTimeout(3000)
 {
-    m_undoAction = new QAction(QIcon(":/icons/undo.png"), tr("&Undo"), this);
+    IconLoader loader;
+    QIcon undoIcon(loader.load("undo"));
+    QIcon redoIcon(loader.load("redo"));
+    
+    m_undoAction = new QAction(undoIcon, ("&Undo"), this);
     m_undoAction->setShortcut(tr("Ctrl+Z"));
     m_undoAction->setStatusTip(tr("Undo the last editing operation"));
     connect(m_undoAction, SIGNAL(triggered()), this, SLOT(undo()));
     
-    m_undoMenuAction = new QAction(QIcon(":/icons/undo.png"), tr("&Undo"), this);
+    m_undoMenuAction = new QAction(undoIcon, tr("&Undo"), this);
     connect(m_undoMenuAction, SIGNAL(triggered()), this, SLOT(undo()));
     
     m_undoMenu = new QMenu(tr("&Undo"));
@@ -66,12 +72,12 @@
     connect(m_undoMenu, SIGNAL(triggered(QAction *)),
 	    this, SLOT(undoActivated(QAction*)));
 
-    m_redoAction = new QAction(QIcon(":/icons/redo.png"), tr("Re&do"), this);
+    m_redoAction = new QAction(redoIcon, tr("Re&do"), this);
     m_redoAction->setShortcut(tr("Ctrl+Shift+Z"));
     m_redoAction->setStatusTip(tr("Redo the last operation that was undone"));
     connect(m_redoAction, SIGNAL(triggered()), this, SLOT(redo()));
     
-    m_redoMenuAction = new QAction(QIcon(":/icons/redo.png"), tr("Re&do"), this);
+    m_redoMenuAction = new QAction(redoIcon, tr("Re&do"), this);
     connect(m_redoMenuAction, SIGNAL(triggered()), this, SLOT(redo()));
 
     m_redoMenu = new QMenu(tr("Re&do"));
--- a/widgets/IconLoader.cpp	Mon Apr 13 13:52:05 2015 +0100
+++ b/widgets/IconLoader.cpp	Thu Aug 20 14:54:21 2015 +0100
@@ -19,8 +19,17 @@
 #include <QApplication>
 #include <QPainter>
 #include <QPalette>
+#include <QFile>
+#include <QSvgRenderer>
 
-static const char *autoInvertExceptions[] = {
+#include <vector>
+#include <set>
+
+#include "base/Debug.h"
+
+using namespace std;
+
+static set<QString> autoInvertExceptions {
     // These are the icons that look OK in their default colours, even
     // in a colour scheme with a black background.  (They may also be
     // icons that would look worse if we tried to auto-invert them.)
@@ -29,16 +38,13 @@
     // supply inverted versions -- the loader will load xx_inverse.png
     // in preference to xx.png if a dark background is found.)
     "fileclose",
-    "filenew-22",
     "filenew",
-    "fileopen-22",
     "fileopen",
     "fileopenaudio",
     "fileopensession",
-    "filesave-22",
     "filesave",
-    "filesaveas-22",
     "filesaveas",
+    "filesaveas-sv",
     "help",
     "editcut",
     "editcopy",
@@ -51,42 +57,121 @@
     "zoom"
 };
 
+static vector<int> sizes { 0, 16, 22, 24, 32, 48, 64, 128 };
+
 QIcon
 IconLoader::load(QString name)
 {
-    QPixmap pmap(loadPixmap(name));
-    if (pmap.isNull()) return QIcon();
-    else return QIcon(pmap);
+    QIcon icon;
+    for (int sz: sizes) {
+        QPixmap pmap(loadPixmap(name, sz));
+        if (!pmap.isNull()) icon.addPixmap(pmap);
+    }
+    return icon;
+}
+
+bool
+IconLoader::shouldInvert() const
+{
+    QColor bg = QApplication::palette().window().color();
+    bool darkBackground = (bg.red() + bg.green() + bg.blue() <= 384);
+    return darkBackground;
+}
+
+bool
+IconLoader::shouldAutoInvert(QString name) const
+{
+    if (shouldInvert()) {
+        return (autoInvertExceptions.find(name) == autoInvertExceptions.end());
+    } else {
+        return false;
+    }
 }
 
 QPixmap
-IconLoader::loadPixmap(QString name)
+IconLoader::loadPixmap(QString name, int size)
 {
-    QColor bg = QApplication::palette().window().color();
-    if (bg.red() + bg.green() + bg.blue() > 384) { // light background
-        QPixmap pmap(QString(":icons/%1").arg(name));
-        if (pmap.isNull()) {
-            pmap = QPixmap(QString(":icons/%1.png").arg(name));
-        }
-        return pmap;
+    bool invert = shouldInvert();
+
+    QString scalableName, nonScalableName;
+    QPixmap pmap;
+
+    nonScalableName = makeNonScalableFilename(name, size, invert);
+    pmap = QPixmap(nonScalableName);
+    if (!pmap.isNull()) return pmap;
+
+    if (size > 0) {
+        scalableName = makeScalableFilename(name, invert);
+        pmap = loadScalable(scalableName, size);
+        if (!pmap.isNull()) return pmap;
     }
 
-    QPixmap pmap(QString(":icons/%1").arg(name));
-    if (pmap.isNull()) {
-        pmap = QPixmap(QString(":icons/%1_inverse.png").arg(name));
-        if (pmap.isNull()) {
-            pmap = QPixmap(QString(":icons/%1.png").arg(name));
-        }
-    }
-    if (pmap.isNull()) return pmap;
+    if (invert && shouldAutoInvert(name)) {
 
-    for (int i = 0; i < int(sizeof(autoInvertExceptions)/
-                            sizeof(autoInvertExceptions[0])); ++i) {
-        if (autoInvertExceptions[i] == name) {
-            return pmap;
+        nonScalableName = makeNonScalableFilename(name, size, false);
+        pmap = QPixmap(nonScalableName);
+        if (!pmap.isNull()) return invertPixmap(pmap);
+
+        if (size > 0) {
+            scalableName = makeScalableFilename(name, false);
+            pmap = loadScalable(scalableName, size);
+            if (!pmap.isNull()) return invertPixmap(pmap);
         }
     }
 
+    return QPixmap();
+}
+
+QPixmap
+IconLoader::loadScalable(QString name, int size)
+{
+    if (!QFile(name).exists()) {
+        cerr << "loadScalable: no such file as: \"" << name << "\"" << endl;
+        return QPixmap();
+    }
+    QPixmap pmap(size, size);
+    pmap.fill(Qt::transparent);
+    QSvgRenderer renderer(name);
+    QPainter painter;
+    painter.begin(&pmap);
+    cerr << "calling renderer for " << name << " at size " << size << "..." << endl;
+    renderer.render(&painter);
+    cerr << "renderer completed" << endl;
+    painter.end();
+    return pmap;
+}
+
+QString
+IconLoader::makeNonScalableFilename(QString name, int size, bool invert)
+{
+    if (invert) {
+        if (size == 0) {
+            return QString(":icons/%1_inverse.png").arg(name);
+        } else {
+            return QString(":icons/%1-%2_inverse.png").arg(name).arg(size);
+        }
+    } else {
+        if (size == 0) {
+            return QString(":icons/%1.png").arg(name);
+        } else {
+            return QString(":icons/%1-%2.png").arg(name).arg(size);
+        }
+    }
+}
+
+QString
+IconLoader::makeScalableFilename(QString name, bool invert)
+{
+    if (invert) {
+        return QString(":icons/scalable/%1_inverse.svg").arg(name);
+    } else {
+        return QString(":icons/scalable/%1.svg").arg(name);
+    }
+}
+
+QPixmap
+IconLoader::invertPixmap(QPixmap pmap)
+{
     // No suitable inverted icon found for black background; try to
     // auto-invert the default one
 
--- a/widgets/IconLoader.h	Mon Apr 13 13:52:05 2015 +0100
+++ b/widgets/IconLoader.h	Thu Aug 20 14:54:21 2015 +0100
@@ -25,7 +25,15 @@
     virtual ~IconLoader() { }
 
     QIcon load(QString name);
-    QPixmap loadPixmap(QString name);
+
+private:
+    bool shouldInvert() const;
+    bool shouldAutoInvert(QString) const;
+    QPixmap loadPixmap(QString, int);
+    QPixmap loadScalable(QString, int);
+    QPixmap invertPixmap(QPixmap);
+    QString makeScalableFilename(QString, bool);
+    QString makeNonScalableFilename(QString, int, bool);
 };
 
 #endif
--- a/widgets/LEDButton.cpp	Mon Apr 13 13:52:05 2015 +0100
+++ b/widgets/LEDButton.cpp	Thu Aug 20 14:54:21 2015 +0100
@@ -38,8 +38,6 @@
 
     int dark_factor;
     QColor offcolor;
-    QPixmap *off_map;
-    QPixmap *on_map;
 };
 
 
@@ -51,8 +49,6 @@
     d = new LEDButton::LEDButtonPrivate;
     d->dark_factor = 300;
     d->offcolor = col.dark(300);
-    d->off_map = 0;
-    d->on_map = 0;
     
     setColor(col);
 }
@@ -65,8 +61,6 @@
     d = new LEDButton::LEDButtonPrivate;
     d->dark_factor = 300;
     d->offcolor = col.dark(300);
-    d->off_map = 0;
-    d->on_map = 0;
 
     setColor(col);
 }
@@ -78,16 +72,12 @@
     d = new LEDButton::LEDButtonPrivate;
     d->dark_factor = 300;
     d->offcolor = col.dark(300);
-    d->off_map = 0;
-    d->on_map = 0;
 
     setColor(col);
 }
 
 LEDButton::~LEDButton()
 {
-    delete d->off_map;
-    delete d->on_map;
     delete d;
 }
 
@@ -135,40 +125,7 @@
     if (width < 0) 
 	width = 0;
 
-    QPixmap *tmpMap = 0;
-
-    if (led_state) {
-	if (d->on_map) {
-            if (d->on_map->size() == size()) {
-                paint.begin(this);
-                paint.drawPixmap(0, 0, *d->on_map);
-                paint.end();
-                return;
-            } else {
-                delete d->on_map;
-                d->on_map = 0;
-            }
-	}
-    } else {
-	if (d->off_map) {
-            if (d->off_map->size() == size()) {
-                paint.begin(this);
-                paint.drawPixmap(0, 0, *d->off_map);
-                paint.end();
-                return;
-            } else {
-                delete d->off_map;
-                d->off_map = 0;
-            }
-	}
-    }
-
-    int scale = 1;
-    width *= scale;
-
-    tmpMap = new QPixmap(width, width);
-    tmpMap->fill(palette().background().color());
-    paint.begin(tmpMap);
+    paint.begin(this);
 
     paint.setRenderHint(QPainter::Antialiasing, true);
 
@@ -182,14 +139,14 @@
     paint.setBrush(brush);
 
     // Draws a "flat" LED with the given color:
-    paint.drawEllipse( scale, scale, width - scale*2, width - scale*2 );
+    paint.drawEllipse( 1, 1, width - 2, width - 2 );
 
     // Draw the bright light spot of the LED now, using modified "old"
     // painter routine taken from KDEUI´s LEDButton widget:
 
     // Setting the new width of the pen is essential to avoid "pixelized"
     // shadow like it can be observed with the old LED code
-    pen.setWidth( 2 * scale );
+    pen.setWidth( 2 );
 
     // shrink the light on the LED to a size about 2/3 of the complete LED
     int pos = width/5 + 1;
@@ -217,12 +174,13 @@
 	pos++; light_width--;
     }
 
+    paint.drawPoint(pos, pos);
+
     // Drawing of bright spot finished, now draw a thin border
     // around the LED which resembles a shadow with light coming
     // from the upper left.
 
-//    pen.setWidth( 2 * scale + 1 ); // ### shouldn't this value be smaller for smaller LEDs?
-    pen.setWidth(2 * scale);
+    pen.setWidth(2);
     brush.setStyle(Qt::NoBrush);
     paint.setBrush(brush); // This avoids filling of the ellipse
 
@@ -235,36 +193,13 @@
     for (int arc = 120; arc < 2880; arc += 240) {
 	pen.setColor(color);
 	paint.setPen(pen);
-	int w = width - pen.width()/2 - scale + 1;
+	int w = width - pen.width()/2;
 	paint.drawArc(pen.width()/2 + 1, pen.width()/2 + 1, w - 2, w - 2, angle + arc, 240);
 	paint.drawArc(pen.width()/2 + 1, pen.width()/2 + 1, w - 2, w - 2, angle - arc, 240);
 	color = color.dark(110); //FIXME: this should somehow use the contrast value
     }	// end for ( angle = 720; angle < 6480; angle += 160 )
 
     paint.end();
-    //
-    // painting done
-
-    QPixmap *&dest = led_state ? d->on_map : d->off_map;
-
-    if (scale > 1) {
-
-	QImage i = tmpMap->toImage();
-	width /= scale;
-	delete tmpMap;
-	dest = new QPixmap(QPixmap::fromImage
-			   (i.scaled(width, width, 
-				     Qt::KeepAspectRatio,
-				     Qt::SmoothTransformation)));
-
-    } else {
-
-	dest = tmpMap;
-    }
-
-    paint.begin(this);
-    paint.drawPixmap(0, 0, *dest);
-    paint.end();
 }
 
 bool
@@ -303,10 +238,6 @@
     if(led_color!=col) {
 	led_color = col;
 	d->offcolor = col.dark(d->dark_factor);
-	delete d->on_map;
-	d->on_map = 0;
-	delete d->off_map;
-	d->off_map = 0;
 	update();
     }
 }
--- a/widgets/PropertyBox.cpp	Mon Apr 13 13:52:05 2015 +0100
+++ b/widgets/PropertyBox.cpp	Thu Aug 20 14:54:21 2015 +0100
@@ -63,6 +63,12 @@
     m_mainBox = new QVBoxLayout;
     setLayout(m_mainBox);
 
+#ifdef Q_OS_MAC
+    QMargins mm = m_mainBox->contentsMargins();
+    QMargins mmhalf(mm.left()/2, mm.top()/3, mm.right()/2, mm.bottom()/3);
+    m_mainBox->setContentsMargins(mmhalf);
+#endif
+
 //    m_nameWidget = new QLabel;
 //    m_mainBox->addWidget(m_nameWidget);
 //    m_nameWidget->setText(container->objectName());
@@ -443,7 +449,16 @@
             if (type == PropertyContainer::ValueProperty) {
 
                 for (int i = min; i <= max; ++i) {
-                    cb->addItem(m_container->getPropertyValueLabel(name, i));
+
+                    QString label = m_container->getPropertyValueLabel(name, i);
+                    QString iname = m_container->getPropertyValueIconName(name, i);
+
+                    if (iname != "") {
+                        QIcon icon(IconLoader().load(iname));
+                        cb->addItem(icon, label);
+                    } else {
+                        cb->addItem(label);
+                    }
                 }
 
             } else if (type == PropertyContainer::UnitsProperty) {
@@ -512,8 +527,10 @@
         cb->blockSignals(false);
 
 #ifdef Q_OS_MAC
-	// Crashes on startup without this, for some reason
-	cb->setMinimumSize(QSize(10, 10));
+	// Crashes on startup without this, for some reason; also
+	// prevents combo boxes from getting weirdly squished
+	// vertically
+	cb->setMinimumSize(QSize(10, cb->font().pixelSize() * 2));
 #endif
 
 	break;
--- a/widgets/PropertyStack.cpp	Mon Apr 13 13:52:05 2015 +0100
+++ b/widgets/PropertyStack.cpp	Thu Aug 20 14:54:21 2015 +0100
@@ -50,7 +50,7 @@
     tabBar()->setUsesScrollButtons(true); 
     tabBar()->setIconSize(QSize(16, 16));
 #endif
-    
+
     repopulate();
 
     connect(this, SIGNAL(currentChanged(int)),
--- a/widgets/WindowShapePreview.cpp	Mon Apr 13 13:52:05 2015 +0100
+++ b/widgets/WindowShapePreview.cpp	Thu Aug 20 14:54:21 2015 +0100
@@ -209,7 +209,6 @@
 void
 WindowShapePreview::setWindowType(WindowType type)
 {
-    if (m_windowType == type) return;
     m_windowType = type;
     updateLabels();
 }
--- a/widgets/WindowTypeSelector.cpp	Mon Apr 13 13:52:05 2015 +0100
+++ b/widgets/WindowTypeSelector.cpp	Thu Aug 20 14:54:21 2015 +0100
@@ -78,7 +78,9 @@
 
     connect(m_windowCombo, SIGNAL(currentIndexChanged(int)),
             this, SLOT(windowIndexChanged(int)));
-    windowIndexChanged(index);
+
+    m_windowType = defaultType;
+    m_windowShape->setWindowType(m_windowType);
 }
 
 WindowTypeSelector::~WindowTypeSelector()